Hospital Backend Management System Using Spring Boot and Vue

Modern healthcare institutions require efficient, secure, and scalable systems to manage complex administrative workflows. This system addresses critical hospital operations—including patient records, prescriptions, ward assignments, doctor scheduling, medication inventory, and announcements—through a robust full-stack architecture built with Spring Boot on the backend and Vue.js on the frontend.

Technical Stack

  • Back end: Java 8, Spring Boot 2.x
  • Frontend: Vue.js (with Element UI)
  • Database: MySQL 5.7
  • Build Tools: Maven 3.3.9, Tomcat 7
  • IDE Support: IntelliJ IDEA, Eclipse, or MyEclipse
  • Browser: Google Chrome

Default admin credentials: admin / admin. Admin panel URL: http://localhost:8080/[project-name]/admin/dist/index.html

Core Implementation Highlights

The backend leverages Spring Boot’s auto-configuration and embedded server capabilities to streamline development. Key features include RESTful API design, JWT-based session management, and integration with MyBatis-Plus for efficient database operations. The system uses MySQL 5.7 for ACID-compliant data storage, ensuring reliability and referential integrity across entities like patients, doctors, prescriptions, and wards.

File uploads are handled via a dedicated controller that stores assets in the static/upload directory and supports optional persistence during IDE restarts. Role-based access control ensures non-admin users can only interact with their own data (e.g., forum posts).

File Upload Controller (Refactored Logic)

package com.controller;

import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.http.ResponseEntity;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.apache.commons.io.FileUtils;
import org.springframework.util.ResourceUtils;
import com.utils.R;
import java.io.File;
import java.util.Date;

@RestController
@RequestMapping("/api/file")
public class FileStorageController {

    @PostMapping("/upload")
    public R handleFileUpload(@RequestParam("file") MultipartFile file) {
        if (file.isEmpty()) {
            return R.error("File is empty");
        }

        try {
            String ext = getFileExtension(file.getOriginalFilename());
            String filename = System.currentTimeMillis() + "." + ext;
            
            File uploadDir = new File(ResourceUtils.getURL("classpath:static").getPath(), "upload");
            if (!uploadDir.exists()) uploadDir.mkdirs();
            
            File dest = new File(uploadDir, filename);
            file.transferTo(dest);

            // Optional: Sync to source folder during development (uncomment if needed)
            // FileUtils.copyFile(dest, new File("your-project-path/src/main/resources/static/upload/" + filename));

            return R.ok().put("filename", filename);
        } catch (Exception e) {
            return R.error("Upload failed: " + e.getMessage());
        }
    }

    @GetMapping("/download/{filename}")
    public ResponseEntity<byte[]> downloadFile(@PathVariable String filename) {
        try {
            File uploadDir = new File(ResourceUtils.getURL("classpath:static").getPath(), "upload");
            File file = new File(uploadDir, filename);
            
            if (!file.exists()) {
                return ResponseEntity.notFound().build();
            }

            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            headers.setContentDispositionFormData("attachment", filename);
            
            return new ResponseEntity<>(FileUtils.readFileToByteArray(file), headers, HttpStatus.OK);
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }

    private String getFileExtension(String name) {
        return name.substring(name.lastIndexOf(".") + 1);
    }
}

Forum Module with Hierarchical Data Support

package com.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.service.ForumService;
import com.entity.ForumEntity;
import com.utils.PageUtils;
import com.utils.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.*;

@RestController
@RequestMapping("/api/forum")
public class ForumApiController {

    @Autowired
    private ForumService forumService;

    @GetMapping("/list")
    public R listForums(@RequestParam Map<String, Object> params, HttpServletRequest req) {
        Long userId = (Long) req.getSession().getAttribute("userId");
        String role = (String) req.getSession().getAttribute("role");

        QueryWrapper<ForumEntity> query = buildQuery(params);
        if (!"管理员".equals(role)) {
            query.eq("user_id", userId);
        }

        PageUtils page = forumService.queryPage(params, query);
        return R.ok().put("data", page);
    }

    @PostMapping
    public R createPost(@RequestBody ForumEntity post, HttpServletRequest req) {
        post.setId(System.currentTimeMillis() + new Random().nextInt(1000));
        post.setUserId((Long) req.getSession().getAttribute("userId"));
        forumService.save(post);
        return R.ok();
    }

    @GetMapping("/tree/{id}")
    public R getThreadWithReplies(@PathVariable Long id) {
        ForumEntity root = forumService.getById(id);
        if (root != null) {
            loadRepliesRecursively(root);
        }
        return R.ok().put("data", root);
    }

    private void loadRepliesRecursively(ForumEntity node) {
        List<ForumEntity> replies = forumService.list(
            new QueryWrapper<ForumEntity>().eq("parent_id", node.getId())
        );
        if (!replies.isEmpty()) {
            node.setChilds(replies);
            replies.forEach(this::loadRepliesRecursively);
        }
    }

    // Helper method to construct dynamic queries from request parameters
    private QueryWrapper<ForumEntity> buildQuery(Map<String, Object> params) {
        QueryWrapper<ForumEntity> qw = new QueryWrapper<>();
        params.forEach((k, v) -> {
            if (v != null && !"".equals(v)) {
                qw.like(k, v.toString());
            }
        });
        return qw;
    }
}

Testing Strategy

The system underwent both black-box testing (validating functional requirements from an end-user perspective) and white-box testing (verifying internal logic paths, especially in data validation and role-based access controls). Test cases prioritized high-risk modules—such as appointment scheduling and prescription management—following the Pareto principle to maximize defect detection efficiency.

System Advantages

  • Modular Design: Spring Boot enables clear separation of concerns, facilitating future enhancements.
  • Data Integrity: MySQL 5.7 ensures transactional consistency and supports efficient querying via indexed relationships.
  • User Experience: Vue-based interface provides responsive interactions with real-time feedback.
  • Security: Session-based authentication restricts unauthorized access to sensitive operations.
  • Maintainability: Standardized REST APIs and layered architecture simplify debugging and updates.

Tags: Spring Boot Vue.js MySQL REST API mybatis-plus

Posted on Sun, 24 May 2026 18:12:47 +0000 by Jimmy_uk