毕业设计实战:基于Spring Boot的中药实验管理系统全栈开发

29 阅读12分钟

一、项目背景:中医药实验教学数字化的创新需求

在中医药教育现代化进程中,传统实验教学管理面临三大核心痛点实验资源管理低效(药材、器材信息记录混乱,使用情况难以追踪)、实验流程不规范(预约、指导、评分等环节缺乏统一管理)、教学数据分散(实验记录、评分数据、学习成果分散存储)。据2023年中医药教育信息化调研显示,国内中医药院校实验管理系统数字化覆盖率不足40%,大量珍贵的实验数据未能得到有效整合和分析。

为应对这一挑战,基于Spring Boot的中药实验管理系统应运而生。系统以"资源管理规范化、实验流程标准化、教学数据一体化"为核心目标,采用B/S架构构建符合中医药实验特点的管理平台,整合实验教学、在线学习、实验预约、器材管理等功能模块,建立"管理员统筹-教师指导-学生参与-实验员协助"的四级应用模式,推动中药实验管理从"手工记录、分散管理"向"数字化、系统化、智能化"转型。

二、技术架构:中药实验管理系统的全栈技术选型

项目基于"专业性、稳定性、安全性"三大原则,选用成熟的Java Web技术栈,确保系统在实验管理、器材追踪等核心业务上的准确性和可靠性:

技术模块具体工具/技术核心作用
后端框架Spring Boot 2.x快速构建RESTful API,提供稳健的业务逻辑处理
数据库MySQL 8.0安全存储实验数据、用户信息、器材记录等
前端技术JSP + Bootstrap + JavaScript构建专业化的中医药实验管理界面
架构模式B/S(Browser/Server)支持多角色协同工作,无需客户端安装
服务器Tomcat 9.0部署Web应用,保障教学期间的稳定运行

三、项目全流程:6步完成中药实验管理系统开发

3.1 第一步:需求分析——明确系统核心价值

针对传统中药实验管理的"资源管理低效、流程不规范、数据分散"痛点,系统聚焦"资源数字化、流程标准化、数据一体化",明确四级角色的核心需求:

3.1.1 功能性需求

  1. 四级角色权限体系

    • 管理员:个人中心、学生管理、教师管理、实验员管理、系统配置、数据统计;
    • 教师:实验教学发布、在线学习管理、实验信息维护、预约审核、实验指导、实验评分;
    • 学生:实验学习、在线学习、实验预约、实验参与、成绩查询;
    • 实验员:器材信息管理、实验指导协助、实验环境维护。
  2. 核心业务功能

    • 实验全流程管理:从实验发布、预约、指导到评分的完整闭环;
    • 教学资源管理:实验教学内容、视频资料、学习材料的统一管理;
    • 器材生命周期管理:器材入库、使用记录、维护保养的全流程跟踪;
    • 学习社区互动:自由讨论、经验分享、问题解答的交流平台。

3.1.2 非功能性需求

  • 系统专业性:符合中医药实验教学的特点和规范;
  • 数据准确性:实验数据、评分记录准确无误;
  • 安全性要求:实验数据安全,权限控制严格;
  • 易用性:界面符合教学使用习惯,操作流程清晰。

3.2 第二步:系统设计——构建整体架构

系统采用分层架构模式,确保业务逻辑清晰、符合中医药实验教学规范:

3.2.1 系统总体架构

  1. 表现层

    • 基于JSP动态生成不同角色的操作界面;
    • Bootstrap提供统一的UI组件和响应式布局。
  2. 业务逻辑层

    • 用户管理服务:四级角色权限管理和身份验证;
    • 实验管理服务:实验流程控制、状态流转、数据记录;
    • 资源管理服务:教学资源、器材信息的CRUD操作;
    • 社区服务:发帖回帖、内容审核、消息通知。
  3. 数据持久层

    • MyBatis实现数据库操作;
    • 事务管理确保数据一致性。

3.2.2 核心数据库设计

系统设计15张核心数据表,关键表结构如下:

表名核心字段作用
学生表id、学号、密码、学生姓名、班级、专业存储学生基本信息
教师表id、工号、密码、教师姓名、电话、邮箱存储教师信息
实验员表id、账号、密码、姓名、电话、邮箱存储实验员信息
实验教学表id、实验名称、实验类型、教学视频、教学内容管理实验教学内容
实验信息表id、实验名称、实验类型、实验地点、实验时间存储实验安排信息
实验预约表id、实验名称、预约内容、预约时间、学号记录实验预约信息

3.3 第三步:后端核心功能实现——Spring Boot架构

基于Spring Boot框架实现符合中医药实验特点的核心业务逻辑:

3.3.1 实验管理功能实现

@RestController
@RequestMapping("/api/experiment")
public class ExperimentController {
    
    @Autowired
    private ExperimentService experimentService;
    
    /**
     * 教师发布实验信息
     */
    @PostMapping("/publish")
    public ResponseEntity<?> publishExperiment(@RequestBody ExperimentDTO experimentDTO,
                                              @RequestHeader("teacherId") Long teacherId) {
        try {
            // 参数验证
            if (StringUtils.isEmpty(experimentDTO.getShiyanmingcheng()) || 
                StringUtils.isEmpty(experimentDTO.getShiyanleixing()) ||
                experimentDTO.getShiyanshijian() == null) {
                return ResponseEntity.badRequest().body("实验名称、类型和时间不能为空");
            }
            
            // 获取教师信息
            Teacher teacher = experimentService.getTeacherById(teacherId);
            if (teacher == null) {
                return ResponseEntity.badRequest().body("教师信息不存在");
            }
            
            ExperimentInfo experiment = new ExperimentInfo();
            experiment.setShiyanmingcheng(experimentDTO.getShiyanmingcheng());
            experiment.setShiyanleixing(experimentDTO.getShiyanleixing());
            experiment.setTupian(experimentDTO.getTupian());
            experiment.setShiyandidian(experimentDTO.getShiyandidian());
            experiment.setShiyanshijian(experimentDTO.getShiyanshijian());
            experiment.setGonghao(teacher.getGonghao());
            experiment.setJiaoshixingming(teacher.getJiaoshixingming());
            experiment.setShiyanneirong(experimentDTO.getShiyanneirong());
            experiment.setStatus("可预约"); // 初始状态
            experiment.setAddtime(new Date());
            
            ExperimentInfo result = experimentService.publishExperiment(experiment);
            return ResponseEntity.ok("实验信息发布成功,实验ID:" + result.getId());
        } catch (Exception e) {
            return ResponseEntity.internalServerError()
                .body("发布实验信息失败:" + e.getMessage());
        }
    }
    
    /**
     * 学生预约实验
     */
    @PostMapping("/reserve")
    public ResponseEntity<?> reserveExperiment(@RequestBody ReserveDTO reserveDTO,
                                              @RequestHeader("studentId") Long studentId) {
        try {
            // 参数验证
            if (StringUtils.isEmpty(reserveDTO.getShiyanmingcheng()) || 
                StringUtils.isEmpty(reserveDTO.getYuyueneirong())) {
                return ResponseEntity.badRequest().body("实验名称和预约内容不能为空");
            }
            
            // 获取学生信息
            Student student = experimentService.getStudentById(studentId);
            if (student == null) {
                return ResponseEntity.badRequest().body("学生信息不存在");
            }
            
            // 检查实验是否存在且可预约
            ExperimentInfo experiment = experimentService.getExperimentByName(reserveDTO.getShiyanmingcheng());
            if (experiment == null || !"可预约".equals(experiment.getStatus())) {
                return ResponseEntity.badRequest().body("实验不存在或不可预约");
            }
            
            // 检查是否已预约
            if (experimentService.hasReserved(studentId, reserveDTO.getShiyanmingcheng())) {
                return ResponseEntity.badRequest().body("您已预约该实验");
            }
            
            ExperimentReserve reserve = new ExperimentReserve();
            reserve.setShiyanmingcheng(reserveDTO.getShiyanmingcheng());
            reserve.setShiyanleixing(experiment.getShiyanleixing());
            reserve.setGonghao(experiment.getGonghao());
            reserve.setJiaoshixingming(experiment.getJiaoshixingming());
            reserve.setYuyueneirong(reserveDTO.getYuyueneirong());
            reserve.setYuyueshijian(new Date());
            reserve.setXuehao(student.getXuehao());
            reserve.setXueshengxingming(student.getXueshengxingming());
            reserve.setBanji(student.getBanji());
            reserve.setZhuanye(student.getZhuanye());
            reserve.setSfsh("否"); // 初始状态:未审核
            reserve.setAddtime(new Date());
            
            ExperimentReserve result = experimentService.reserveExperiment(reserve);
            return ResponseEntity.ok("实验预约成功,等待教师审核");
        } catch (Exception e) {
            return ResponseEntity.internalServerError()
                .body("预约实验失败:" + e.getMessage());
        }
    }
    
    /**
     * 教师审核实验预约
     */
    @PostMapping("/audit-reserve/{reserveId}")
    public ResponseEntity<?> auditReserve(@PathVariable Long reserveId,
                                         @RequestBody AuditDTO auditDTO) {
        try {
            // 验证预约记录是否存在
            ExperimentReserve reserve = experimentService.getReserveById(reserveId);
            if (reserve == null) {
                return ResponseEntity.badRequest().body("预约记录不存在");
            }
            
            // 更新审核状态
            reserve.setSfsh(auditDTO.getSfsh()); // "是"通过,"否"拒绝
            reserve.setShhf(auditDTO.getShhf()); // 审核回复
            experimentService.updateReserve(reserve);
            
            // 如果审核通过,更新实验状态
            if ("是".equals(auditDTO.getSfsh())) {
                experimentService.updateExperimentStatus(reserve.getShiyanmingcheng(), "已预约");
            }
            
            return ResponseEntity.ok("实验预约审核完成");
        } catch (Exception e) {
            return ResponseEntity.internalServerError()
                .body("审核实验预约失败:" + e.getMessage());
        }
    }
}

@Service
@Transactional
public class ExperimentServiceImpl implements ExperimentService {
    
    @Autowired
    private ExperimentInfoMapper experimentMapper;
    
    @Autowired
    private ExperimentReserveMapper reserveMapper;
    
    @Autowired
    private TeacherMapper teacherMapper;
    
    @Autowired
    private StudentMapper studentMapper;
    
    @Override
    public ExperimentInfo publishExperiment(ExperimentInfo experiment) {
        experimentMapper.insert(experiment);
        return experiment;
    }
    
    @Override
    public ExperimentReserve reserveExperiment(ExperimentReserve reserve) {
        reserveMapper.insert(reserve);
        return reserve;
    }
    
    @Override
    public boolean hasReserved(Long studentId, String experimentName) {
        Student student = studentMapper.selectById(studentId);
        LambdaQueryWrapper<ExperimentReserve> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ExperimentReserve::getXuehao, student.getXuehao())
               .eq(ExperimentReserve::getShiyanmingcheng, experimentName);
        return reserveMapper.selectCount(wrapper) > 0;
    }
}

3.3.2 实验指导与评分功能实现

@RestController
@RequestMapping("/api/guidance")
public class GuidanceController {
    
    @Autowired
    private GuidanceService guidanceService;
    
    /**
     * 教师进行实验指导
     */
    @PostMapping("/provide")
    public ResponseEntity<?> provideGuidance(@RequestBody GuidanceDTO guidanceDTO,
                                            @RequestHeader("teacherId") Long teacherId) {
        try {
            // 参数验证
            if (StringUtils.isEmpty(guidanceDTO.getShiyanmingcheng()) || 
                StringUtils.isEmpty(guidanceDTO.getZhidaoneirong())) {
                return ResponseEntity.badRequest().body("实验名称和指导内容不能为空");
            }
            
            // 获取教师信息
            Teacher teacher = guidanceService.getTeacherById(teacherId);
            if (teacher == null) {
                return ResponseEntity.badRequest().body("教师信息不存在");
            }
            
            // 检查实验预约是否存在且已审核通过
            ExperimentReserve reserve = guidanceService.getApprovedReserve(
                guidanceDTO.getShiyanmingcheng(), guidanceDTO.getXuehao());
            if (reserve == null) {
                return ResponseEntity.badRequest().body("未找到对应的已审核通过的实验预约");
            }
            
            ExperimentGuidance guidance = new ExperimentGuidance();
            guidance.setShiyanmingcheng(guidanceDTO.getShiyanmingcheng());
            guidance.setShiyanleixing(reserve.getShiyanleixing());
            guidance.setGonghao(teacher.getGonghao());
            guidance.setJiaoshixingming(teacher.getJiaoshixingming());
            guidance.setZhidaoneirong(guidanceDTO.getZhidaoneirong());
            guidance.setShiyanriqi(new Date());
            guidance.setXuehao(reserve.getXuehao());
            guidance.setXueshengxingming(reserve.getXueshengxingming());
            guidance.setBanji(reserve.getBanji());
            guidance.setZhuanye(reserve.getZhuanye());
            guidance.setAddtime(new Date());
            
            ExperimentGuidance result = guidanceService.provideGuidance(guidance);
            return ResponseEntity.ok("实验指导记录添加成功");
        } catch (Exception e) {
            return ResponseEntity.internalServerError()
                .body("添加实验指导失败:" + e.getMessage());
        }
    }
    
    /**
     * 教师进行实验评分
     */
    @PostMapping("/score")
    public ResponseEntity<?> scoreExperiment(@RequestBody ScoreDTO scoreDTO,
                                            @RequestHeader("teacherId") Long teacherId) {
        try {
            // 参数验证
            if (StringUtils.isEmpty(scoreDTO.getShiyanmingcheng()) || 
                StringUtils.isEmpty(scoreDTO.getPingfen())) {
                return ResponseEntity.badRequest().body("实验名称和评分不能为空");
            }
            
            // 获取教师信息
            Teacher teacher = guidanceService.getTeacherById(teacherId);
            if (teacher == null) {
                return ResponseEntity.badRequest().body("教师信息不存在");
            }
            
            // 检查实验是否已完成
            ExperimentReserve reserve = guidanceService.getCompletedReserve(
                scoreDTO.getShiyanmingcheng(), scoreDTO.getXuehao());
            if (reserve == null) {
                return ResponseEntity.badRequest().body("未找到对应的已完成实验记录");
            }
            
            ExperimentScore score = new ExperimentScore();
            score.setShiyanmingcheng(scoreDTO.getShiyanmingcheng());
            score.setShiyanleixing(reserve.getShiyanleixing());
            score.setXuehao(reserve.getXuehao());
            score.setXueshengxingming(reserve.getXueshengxingming());
            score.setBanji(reserve.getBanji());
            score.setZhuanye(reserve.getZhuanye());
            score.setPingfen(scoreDTO.getPingfen());
            score.setDianping(scoreDTO.getDianping());
            score.setRiqi(new Date());
            score.setGonghao(teacher.getGonghao());
            score.setJiaoshixingming(teacher.getJiaoshixingming());
            score.setAddtime(new Date());
            
            ExperimentScore result = guidanceService.scoreExperiment(score);
            return ResponseEntity.ok("实验评分成功");
        } catch (Exception e) {
            return ResponseEntity.internalServerError()
                .body("实验评分失败:" + e.getMessage());
        }
    }
}

3.3.3 器材管理功能实现

@RestController
@RequestMapping("/api/equipment")
public class EquipmentController {
    
    @Autowired
    private EquipmentService equipmentService;
    
    /**
     * 添加器材信息
     */
    @PostMapping("/add")
    public ResponseEntity<?> addEquipment(@RequestBody EquipmentDTO equipmentDTO) {
        try {
            // 参数验证
            if (StringUtils.isEmpty(equipmentDTO.getQicaimingcheng()) || 
                StringUtils.isEmpty(equipmentDTO.getQicaileixing()) ||
                equipmentDTO.getShuliang() == null) {
                return ResponseEntity.badRequest().body("器材名称、类型和数量不能为空");
            }
            
            EquipmentInfo equipment = new EquipmentInfo();
            equipment.setQicaimingcheng(equipmentDTO.getQicaimingcheng());
            equipment.setQicaileixing(equipmentDTO.getQicaileixing());
            equipment.setTupian(equipmentDTO.getTupian());
            equipment.setShuliang(equipmentDTO.getShuliang());
            equipment.setDengjiriqi(new Date());
            equipment.setZhuyishixiang(equipmentDTO.getZhuyishixiang());
            equipment.setQicaiyongfa(equipmentDTO.getQicaiyongfa());
            equipment.setStatus("可用"); // 初始状态
            equipment.setAddtime(new Date());
            
            EquipmentInfo result = equipmentService.addEquipment(equipment);
            return ResponseEntity.ok("器材信息添加成功,器材ID:" + result.getId());
        } catch (Exception e) {
            return ResponseEntity.internalServerError()
                .body("添加器材信息失败:" + e.getMessage());
        }
    }
    
    /**
     * 器材借用申请
     */
    @PostMapping("/borrow")
    public ResponseEntity<?> borrowEquipment(@RequestBody BorrowDTO borrowDTO,
                                            @RequestHeader("userId") Long userId,
                                            @RequestHeader("userType") String userType) {
        try {
            // 参数验证
            if (StringUtils.isEmpty(borrowDTO.getQicaimingcheng()) || 
                borrowDTO.getBorrowCount() == null) {
                return ResponseEntity.badRequest().body("器材名称和借用数量不能为空");
            }
            
            // 检查器材是否存在且可用
            EquipmentInfo equipment = equipmentService.getEquipmentByName(borrowDTO.getQicaimingcheng());
            if (equipment == null || !"可用".equals(equipment.getStatus())) {
                return ResponseEntity.badRequest().body("器材不存在或不可用");
            }
            
            if (equipment.getShuliang() < borrowDTO.getBorrowCount()) {
                return ResponseEntity.badRequest().body("借用数量超过库存数量");
            }
            
            // 根据用户类型获取用户信息
            UserInfo user = equipmentService.getUserInfo(userId, userType);
            if (user == null) {
                return ResponseEntity.badRequest().body("用户信息不存在");
            }
            
            EquipmentBorrow borrow = new EquipmentBorrow();
            borrow.setQicaimingcheng(borrowDTO.getQicaimingcheng());
            borrow.setQicaileixing(equipment.getQicaileixing());
            borrow.setBorrowCount(borrowDTO.getBorrowCount());
            borrow.setBorrowPurpose(borrowDTO.getBorrowPurpose());
            borrow.setBorrowTime(new Date());
            borrow.setExpectedReturnTime(borrowDTO.getExpectedReturnTime());
            borrow.setUserId(userId);
            borrow.setUserName(user.getUserName());
            borrow.setUserType(userType);
            borrow.setStatus("待审核");
            borrow.setAddtime(new Date());
            
            EquipmentBorrow result = equipmentService.borrowEquipment(borrow);
            
            // 更新器材状态为待审核
            equipment.setStatus("待审核");
            equipmentService.updateEquipment(equipment);
            
            return ResponseEntity.ok("器材借用申请提交成功,等待审核");
        } catch (Exception e) {
            return ResponseEntity.internalServerError()
                .body("器材借用申请失败:" + e.getMessage());
        }
    }
}

3.4 第四步:前端界面实现——中医药实验系统风格设计

基于JSP + Bootstrap构建符合中医药特色的实验管理系统界面:

3.4.1 核心界面设计

  1. 首页:系统概览、实验通知、快速入口;
  2. 实验中心:实验列表、分类筛选、详情查看;
  3. 预约管理:实验预约、预约状态、历史记录;
  4. 教学资源:实验教学、在线学习、资料下载;
  5. 器材管理:器材列表、借用申请、使用记录;
  6. 成绩中心:实验评分、成绩查询、统计分析;
  7. 交流社区:自由讨论、经验分享、问题求助。 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

3.4.2 设计亮点

  • 中医药风格:采用传统中国风元素,体现中医药文化特色;
  • 专业布局:界面布局符合实验教学流程,操作路径清晰;
  • 数据可视化:使用图表展示实验数据统计和分析结果;
  • 响应式设计:支持不同设备访问,确保使用体验一致性。

3.5 第五步:系统测试——确保系统稳定可靠

通过全面的测试确保系统功能完整性和数据准确性:

3.5.1 功能测试

测试场景测试用例预期结果实际结果
实验发布教师发布新实验发布成功,学生可见发布成功,信息完整
实验预约学生预约实验预约成功,状态待审核预约成功,流程正确
预约审核教师审核预约审核通过/拒绝,状态更新审核功能正常
实验指导教师进行实验指导指导记录成功添加记录完整,信息准确
实验评分教师进行实验评分评分成功,学生可查评分准确,显示正常

3.5.2 性能测试

  • 并发测试:模拟实验选课高峰期大量学生同时操作,系统响应正常;
  • 数据准确性:实验数据、评分记录准确无误;
  • 稳定性测试:长时间运行无内存泄漏,系统稳定可靠。

3.6 第六步:问题排查与优化——提升系统性能

开发过程中遇到的核心问题及解决方案:

  1. 问题:实验预约并发控制
    解决方案:使用数据库事务和乐观锁机制防止超预约。

  2. 问题:器材库存同步
    解决方案:使用数据库事务确保库存更新的原子性。

  3. 问题:实验流程状态管理
    解决方案:设计完整的状态流转机制,确保流程规范性。

  4. 问题:数据统计性能
    解决方案:使用数据库视图和定时任务生成统计报表。

四、毕业设计复盘:中药实验管理系统开发实践总结

4.1 开发过程中的技术挑战

  1. 复杂业务流程:实验预约、审核、指导、评分的完整流程管理;
  2. 多角色权限控制:四级角色权限体系的细粒度控制;
  3. 数据一致性:实验状态、器材库存等数据的强一致性保障;
  4. 系统专业性:确保系统符合中医药实验教学的特点和规范。

4.2 给后续开发者的建议

  1. 深入理解业务:充分研究中医药实验教学的具体流程和需求;
  2. 注重数据模型设计:设计符合实验教学特点的数据结构;
  3. 强化权限管理:建立严格的多角色权限控制体系;
  4. 优化用户体验:简化操作流程,提供清晰的状态提示;
  5. 完善系统文档:编写详细的技术文档和用户操作手册。

五、项目资源与发展展望

5.1 项目核心资源

本项目提供完整的开发资料:

  • 后端源码:完整的Spring Boot项目,包含实验管理相关业务逻辑;
  • 前端页面:JSP页面和静态资源文件;
  • 数据库脚本:建表语句和示例数据;
  • 部署文档:详细的系统部署教程;
  • API文档:接口说明和使用示例。

5.2 系统扩展方向

  1. 移动端应用:开发微信小程序,支持移动端实验预约和查询;
  2. 智能推荐:基于学生兴趣和能力的智能实验推荐;
  3. 虚拟实验:集成虚拟仿真实验,扩展实验教学形式;
  4. 大数据分析:构建实验教学数据分析平台,为教学改进提供支持;
  5. 物联网集成:集成智能实验设备,实现实验数据的自动采集。

如果本文对您的Spring Boot学习、教育管理系统相关毕业设计有帮助,欢迎点赞 + 收藏 + 关注,后续会分享更多教育信息化项目实战案例!