做过Java课程设计或者毕业设计的同学应该都明白,选一个“看起来有业务深度、技术栈又不至于太吓人”的题目是最关键的。基于Spring Boot的眼科医院管理系统,就是这类题目里很典型的一种:它看起来是医院信息系统的子集,业务上有挂号、门诊、药品、收费、病历档案这一串闭环流程,技术上又能把Spring Boot、数据库设计、权限控制、事务处理这些核心点全部覆盖到。和市面上常见的图书管理、学生管理系统相比,这个题目的业务建模更有嚼头,答辩时能讲的东西也更多。项目交付物一般包括源码、数据库脚本和一份完整的万字设计文档,刚好对应课设和毕设的全部验收材料。
我接触过好几个以这个题目为蓝本完成的课设和毕设项目,也踩过不少坑。这篇博文不打算写成教程式的说明书,而是从整体设计、数据库建模、核心功能实现到常见问题排查,把这类项目从立项到交付的关键点一次讲透。如果你正在找Spring Boot练手项目、准备课程设计或者做毕业设计,这篇文章应该能帮你少走很多弯路。
1. 项目整体设计与架构思考
1.1 为什么选Spring Boot:技术选型的一场“减负”战
先回答一个最基础的问题:医院管理系统这种CRUD为主的项目,为什么大家都愿意用Spring Boot?答案其实很简单,它把传统SSM(Spring + SpringMVC + MyBatis)里最烦人的那些配置全部自动化了。传统SSM搭一个项目,要手动配置DispatcherServlet、扫描包、事务管理器、MyBatis工厂,光配置文件就能写两三页;Spring Boot用starter依赖和自动装配机制,一个Application启动类加上少量配置就能跑起来,整个上手成本低了一大截。
单就这个眼科医院系统而言,Spring Boot最实在的优势有三个。第一,内嵌Tomcat,打包以后就是一个可直接运行的jar/war包,部署演示的时候不用再单独装容器,对验收答辩非常友好。第二,生态成熟,像MyBatis Plus这种国产增强框架与Spring Boot的集成几乎是无缝的,单表CRUD不用手写SQL,逻辑删除、分页插件、自动填充都是内置功能,开发效率明显提升。第三,社区资料极多,遇到版本兼容、配置报错之类的问题,基本一搜就有答案,完全不用担心卡在环境上。
这里也要泼一盆冷水:别因为题目叫“眼科医院管理系统”就想着把微服务、分布式事务、Redis缓存、消息队列全部堆上去。课设和毕设的场景撑不起这么重的架构,强行引入只会增加部署难度和出错的概率。技术选型讲究的是匹配业务复杂度,Spring Boot + MySQL + MyBatis Plus这套组合对这个项目来说已经绰绰有余,既能让评委看到你有工程化的意识,又不会把自己拖进无底洞。
1.2 从眼科就诊流程反推功能模块划分
做这类系统最忌讳一上来就写代码,先把业务流程走一遍再拆模块,会轻松很多。眼科医院的就诊流程和其他专科医院大同小异:患者建档注册,然后选择科室和医生进行预约挂号;到了预约时间,医生接诊,查看患者的基本信息和历史病历,做视力、眼压等眼科专项检查;诊断完成后,医生可能会开药或者建议做进一步检查,患者到收费处缴费;药房确认费用后发药,就诊记录归档;后续还有复诊随访和线上咨询。
把这个流程走完,功能模块基本就浮出水面了:系统管理(用户、角色、权限)、科室与医生信息管理、预约挂号管理、患者建档与眼健康档案管理、门诊接诊与病历管理、药品库存管理、处方与收费管理、在线咨询与健康科普。这里有一个容易忽略的点:标题里除了“眼科医院管理系统”,还有“眼科健康管理与咨询系统”这个变体,它本质上和前者是同一套系统,只是侧重点不一样。
所以我们在模块设计时完全可以把在线咨询、健康科普文章、眼健康档案这些偏“健康管理”的功能也包含进去。这样既覆盖了医院内部的管理流程,又响应了标题里的“健康管理与咨询”,验收的时候能讲的东西瞬间多了一倍。尤其是眼健康档案,它记录患者每次就诊的左眼视力、右眼视力、眼压等数据,复诊时医生可以看到历史变化曲线,这是眼科系统区别于普通医院系统的一个重要亮点。
我的习惯是先画一张简单的业务流程图,把“建档→挂号→接诊→检查→开药→收费→取药→归档→随访”用箭头串起来,再对着流程图去列模块清单。业务闭环在演示时非常重要,因为评委大概率会要求你现场走一遍流程,如果能从登录开始一路演示到收费、处方打印,这个项目的完整度就基本站住了。
1.3 分层设计、统一返回与全局异常处理,这三件事为什么能加分
Spring Boot项目的经典分层一般是Controller、Service、Mapper三层,再配合实体类会拆成DTO(数据传输对象)、VO(视图对象)、Entity(数据库实体)。很多同学做课设时图省事,Service层直接return Map,Controller里塞一堆业务判断,结果代码全堆在一个类里,改起来痛苦不说,答辩时也说不出所以然。分层这件事,一方面是为了职责清晰,Controller只负责接收参数和返回结果,Service只负责业务逻辑,Mapper只负责与数据库打交道;另一方面,它让我在答辩时能够逐层讲解,从URL到数据库的走向一句话就能说清楚,评委一听就明白你是有工程意识的。
为了统一前后端的交互格式,我会在项目里定义一个Result 返回类,包含code、message、data三个字段,所有Controller方法都返回这个对象。这样前端不管做小程序、Vue页面还是Thymeleaf模板,解析逻辑都是统一的。
public class Result<T> { private Integer code; private String message; private T data; public static <T> Result<T> ok(T data) { Result<T> r = new Result<>(); r.setCode(200); r.setMessage("操作成功"); r.setData(data); return r; } public static <T> Result<T> error(String message) { Result<T> r = new Result<>(); r.setCode(500); r.setMessage(message); return r; } }另外一定要加全局异常处理器,用@RestControllerAdvice统一捕获业务异常和系统异常。不然数据库连不上、参数校验失败这些情况会把500错误页直接丢给用户,既不友好也显得很不专业。捕获异常后统一返回Result.error(),同时把异常信息记录到日志里,定位问题也方便。这几个设计在代码量上只多了一点点,但带来的工程化观感提升非常大,属于投入产出比很高的事情。
2. 数据库设计与核心业务建模
2.1 十张核心表的设计思路与字段说明
这个系统的数据库设计是整个项目的地基,表结构设计得清不清楚,直接决定后面写代码顺不顺。我按最常见的交付标准整理了十张核心表,这几个表基本可以覆盖一条完整的就诊业务线。
第一张是用户表sys_user,字段包括主键id、username、password、role(ADMIN/DOCTOR/PATIENT/STAFF)、real_name、phone、status。password不能存明文,至少要用BCrypt加盐哈希。为什么用户要和医生、患者分开建表?因为一个系统的登录主体不止一种,医生和患者还带有大量医疗属性字段,全塞进用户表会非常臃肿,分开后各自扩展也方便。
第二张是科室表department,字段是dept_id、dept_name、location、description;第三张是医生表doctor,包括doctor_id、user_id(外键关联sys_user)、dept_id、title(职称)、specialty、schedule(排班说明)。医生表冗余dept_id是为了查询医生列表时不用连表找科室,这是很常见的冗余设计思路。第四张是患者表patient,包括patient_id、user_id、name、gender、birth_date、phone、id_card、allergy_history、medical_history。
眼科特色要放在一个独立的眼健康记录表patient_eye_record里,字段包括eye_record_id、patient_id、visit_time、left_vision、right_vision、left_iop、right_iop、remark。这里记录的是每次就诊时的视力值和眼压值,复诊时医生能直接拉出历史数据做对比,是后续做眼健康档案功能的数据基础。
然后是预约表appointment,字段包括appointment_id、patient_id、doctor_id、dept_id、appt_date、time_slot(上午/下午)、status(0待就诊/1已完成/2已取消)、create_time、deleted。这里有一个关键设计:预约表同时冗余patient_id、doctor_id、dept_id,好处是查询个人预约记录或医生排班时不需要频繁连表,性能更好,代码也更简单。
门诊记录表medical_record存主诉、诊断、医嘱等信息,字段包括record_id、patient_id、doctor_id、appointment_id、chief_complaint、diagnosis、suggestions、create_time。处方表prescription和处方明细表prescription_item负责药品处方,明细表里除了药品ID,还会冗余drug_name和price快照,因为药品信息以后可能调整,但历史处方必须保持原样。
药品表drug包括drug_id、drug_name、specification、unit、price、stock、warning_stock、manufacturer、status;收费表charge包括charge_id、record_id、patient_id、total_amount、status、pay_type、pay_time。在线咨询表consultation和健康科普表article用来支撑健康管理与咨询功能,咨询问题、医生回复、状态几个字段就够用。这一整套表设计下来,业务闭环基本就通了。
2.2 表关系与就诊状态流转,先把业务闭环跑通
数据库表之间的关系并不复杂,梳理清楚反而能成为答辩时的一张王牌。department与doctor是1:N,一个科室可以有多名医生;doctor与appointment是1:N,一个医生有多条预约;patient与appointment是1:N,一个患者可以有多条预约记录。appointment与medical_record是1:1,一次预约对应一次就诊,这样病历记录能够追溯到具体的挂号时段。medical_record与prescription是一对一,一条就诊记录产生一张处方;prescription与prescription_item是1:N,一张处方里有多条药品明细;drug与prescription_item是1:N,一种药品会出现在不同的处方明细里。
除了表关系,还要把业务状态流转画清楚。预约状态从0待就诊流转到1已完成或2已取消,这是整个系统的第一个状态闭环。就诊完成后生成病历和处方,处方生成的同时扣减药品库存并生成待缴费的收费单;收费单从0待缴费流转到1已缴费,药房看到已缴费状态才允许发药。整个过程串起来就是“挂号成功→就诊完成→处方生成→库存扣减→缴费完成”,任何一个环节断掉,后面的流程都无法继续。设计表的时候把status字段和每个状态对应的更新条件提前定好,后面写接口逻辑就会非常顺。
这里我特别想强调一下“状态”字段的用法。很多初学者会在代码里用业务逻辑去“猜”当前状态,比如先select查一下再判断能不能cancel,但更稳妥的做法是直接在UPDATE语句里把status作为条件。比如取消预约时用update appointment set status = 2 where appointment_id = ? and status = 0,通过影响行数来判断这次操作是否有效,从根源上避免并发问题。
2.3 索引与分页:课设阶段如何做到“够用且能讲”
数据库设计这部分,只要谈到优化,绕不开索引和分页。课设阶段不需要把索引设计搞得很复杂,但至少要为高频查询加上必要的索引,并且在文档里写清楚“为什么加这些索引”,这往往是答辩时比较加分的点。
按照业务高频查询来梳理,预约表上至少有四个查询场景:按患者查预约记录、按医生和日期查某天的排班、按状态查待就诊列表、按日期范围做统计。所以我会在appointment表上加(patient_id, appt_date)、(doctor_id, appt_date)两个组合索引。组合索引的好处是查询条件同时包含这两个字段时可以快速定位,比两个单列索引更高效。medical_record表按patient_id + create_time建组合索引,因为患者病历查询几乎总是按时间降序排列的。drug表按drug_name加普通索引,方便药品的模糊搜索和库存检索。
分页方面,MyBatis Plus提供了现成的分页插件,配置一个PaginationInnerInterceptor就能实现物理分页。使用的时候只需要:
IPage<Appointment> page = new Page<>(current, size); appointmentMapper.selectPage(page, wrapper);补充一点个人体会:有的同学喜欢把所有字段都加上索引,觉得这样查询都快。实际上索引维护是有开销的,每次插入、更新都要同步更新索引树,索引越多写入越慢。课设阶段只要把查询频率最高的两三个索引做好就足够了,能说出这个“权衡”的道理,比堆一堆索引更体现理解深度。
3. 核心功能模块实现与关键代码解析
3.1 工程初始化与配置:pom、数据源、连接池参数逐项拆解
工程结构方面,我建议在IDEA里直接通过Spring Initializr创建项目,这里有一个容易踩坑的点:Spring Boot 3.x版本要求JDK17以上,很多同学还在用JDK8,直接选3.x会出现编译错误。如果本机是JDK8,项目就选Spring Boot 2.7系列,这是目前课设和毕设最稳妥的选择。
pom.xml里核心依赖并不需要太多。spring-boot-starter-web提供Web能力,mybatis-plus-boot-starter负责数据库操作增强,mysql-connector-j提供MySQL驱动,lombok减少样板代码,hutool可以顺手处理一些日期、加密、Excel导出的杂活。需要注意MyBatis Plus的版本要和Spring Boot版本兼容,比如Boot 2.7对应的MyBatis Plus可以用3.5.3系列,Boot 3.x则要选MyBatis Plus的3.5.4及以上版本,否则启动时容易报类冲突。
application.yml是整个项目的配置中心,数据源配置这里重点讲一下:
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/eye_hospital?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 123456 hikari: minimum-idle: 5 maximum-pool-size: 15 connection-timeout: 30000 idle-timeout: 600000这段配置里每个参数都有讲究。driver-class-name用的是com.mysql.cj.jdbc.Driver,这是MySQL 8推荐的驱动类,如果沿用旧的com.mysql.jdbc.Driver会直接启动失败。URL里必须带上useUnicode=true和characterEncoding=utf8,避免中文乱码;serverTimezone=Asia/Shanghai解决MySQL 8连接时区报错问题。连接池用的是HikariCP,它性能好且配置简单,minimum-idle是空闲连接数,maximum-pool-size是最大连接数,connection-timeout是获取连接超时时间。课设并发不会太高,这几个参数用默认值也够,但如果你能解释清楚每个参数的含义,会让评委觉得你真的用过连接池,而不是只会点“下一步”。
MyBatis Plus的配置同样在application.yml里完成:map-underscore-to-camel-case开启下划线转驼峰,这样数据库字段user_id可以直接映射到userId;逻辑删除配置logic-delete-field: deleted,删除操作就自动变成update删除标记,预约记录和病历这类需要追溯历史的数据建议用逻辑删除而不是物理删除。把这些配置写好之后,项目的基础就算立住了。
3.2 登录认证方案:选Session还是JWT,怎么落地
登录认证几乎是所有管理系统的必考环节。方案无非两种:传统Session和现在前后端分离场景下更常见的JWT。课设项目如果做的是Thymeleaf服务端渲染,用Session加拦截器最简单,逻辑清晰也够用;如果计划做Vue或者微信小程序这种完全分离的前后端,那JWT会更合适,因为token可以放在本地存储里,每次请求带上Authorization头即可。
我个人的建议是:如果不怕多加一点代码,直接上JWT。它既能体现你对前后端分离的理解,又能避免Session在多端登录场景下的一些麻烦,而且JWT本身并不难。登录接口的核心逻辑是:先查用户,校验BCrypt密码,密码正确就用jjwt生成一个带过期时间的token返回给前端。
public Result<String> login(LoginDTO dto) { User user = userMapper.selectOne(Wrappers.<User>lambdaQuery() .eq(User::getUsername, dto.getUsername())); if (user == null || !BCrypt.checkpw(dto.getPassword(), user.getPassword())) { return Result.error("用户名或密码错误"); } Map<String, Object> claims = new HashMap<>(); claims.put("userId", user.getId()); claims.put("role", user.getRole()); String token = Jwts.builder() .setClaims(claims) .setExpiration(new Date(System.currentTimeMillis() + 3600_000L)) .signWith(Keys.hmacShaKeyFor(SECRET.getBytes(StandardCharsets.UTF_8))) .compact(); return Result.ok(token); }登录成功后,再写一个拦截器统一校验token。preHandle方法里从请求头取出Authorization,解析token,如果解析失败直接返回401。这样除了登录接口和静态资源,其他接口都必须带合法token才能访问,权限控制的第一层就完成了。角色权限可以再进一步细化:ADMIN可以访问所有管理接口,DOCTOR只能查自己的患者和病历,PATIENT只能看自己的记录和预约。拦截器里拿到role字段后做一次简单的判断即可。
需要注意的是,密码加密一定要做。如果直接用明文存数据库,一旦数据库泄露,所有用户信息都暴露了;哪怕只是课设,也应该用BCryptPasswordEncoder这类带盐的哈希算法。这项安全素养在答辩时往往会被评委提问,准备一下不会吃亏。
3.3 预约挂号与眼健康档案:号源防重与特色数据怎么处理
预约挂号是整个系统里最有业务含金量的功能,因为这里有一个非常典型的并发问题:同一时段同一医生的号源可能会被多个患者同时抢,如果处理不当,就会出现“超卖”现象。常见的错误做法是先select查询该时段是否已满,再insert预约记录,但两个请求同时查到“有空位”时,后插入的请求就会产生冲突。
正确的思路是数据库级兜底加应用级校验双保险。首先在appointment表的(doctor_id, appt_date, time_slot)上建唯一索引,从数据库层面保证同一医生同一日期同一时段只有一条有效预约记录;同时,在Service层插入时捕获DuplicateKeyException并转换为业务提示。
还有一个很实用的细节:用条件更新的方式防止并发。先插入待确认状态的预约记录,或者直接用一个带status条件的UPDATE来更新号源表。比如在号源表里这样操作:
int rows = appointmentSlotMapper.reduceStock( doctorId, apptDate, timeSlot, 1); if (rows == 0) { throw new BusinessException("该时段已被预约,请选择其他时间"); }这里reduceStock的本质是update ... set remaining = remaining - 1 where remaining > 0,用影响行数判断是否扣减成功。如果返回值是0,说明库存已经被其他请求抢走了,事务回滚,下一个患者只能换时段。这个“条件更新+影响行数判断”的做法在处理并发扣减时非常实用,在代码评审和答辩中是很大的亮点。
眼健康档案模块是这个系统区别于“通用医院管理系统”的特色功能。我在设计时会单独建patient_eye_record表,医生每次接诊时把患者的左右眼视力、眼压填入,保存在档案里;患者端查询眼健康档案时,可以按时间列出所有检查记录。这个功能虽然代码量不大,但因为和眼科业务强相关,很容易成为文档里的亮点章节,也能支撑“健康管理”这个标题核心。
3.4 药品库存与收费结算:一个事务解决的一致性问题
药品入库和收费结算看似两个独立模块,实际在业务上必须绑定。医生开出处方后,系统要扣减药品库存,同时生成一张待缴费的收费单,这两个动作必须一起成功或一起失败。比如扣了库存但收费单没生成,库存就会莫名其妙少一盒;反过来收费单生成了但没扣库存,患者能买到已经超卖的药。解决办法就是加事务。
我在Service层写一个创建处方的方法,方法上标注@Transactional(rollbackFor = Exception.class),方法内部依次完成三件事:遍历处方明细校验并扣减库存、保存处方主表和明细表、计算总金额并生成收费单。事务的意义在于,任何一个环节抛出异常,前面已经执行的数据库操作全部回滚。
@Transactional(rollbackFor = Exception.class) public void createPrescription(PrescriptionDTO dto) { BigDecimal total = BigDecimal.ZERO; for (PrescriptionItemDTO item : dto.getItems()) { Drug drug = drugMapper.selectById(item.getDrugId()); if (drug == null || drug.getStock() < item.getQuantity()) { throw new BusinessException("药品库存不足:" + item.getDrugName()); } int rows = drugMapper.reduceStock(item.getDrugId(), item.getQuantity()); if (rows == 0) { throw new BusinessException("药品库存不足,请刷新库存"); } // 保存处方明细,计算小计金额累加到total } // 保存处方主表、生成收费单 }这里有一个新手常踩的坑:事务方法内部捕获了异常却只记录日志不抛出,导致事务管理器认为方法正常结束,回滚不会发生。所以事务方法内部一定不能吞异常,要么不捕获,要么捕获后抛出RuntimeException,否则数据一致性完全没保障。
收费模块另一个需要注意的点是金额精度。药品价格和收费金额用double会出精度问题,0.1+0.2可能会变成0.30000000000000004。数据库字段要用decimal,Java侧用BigDecimal,这样在涉及钱的项目里才算稳妥。这门课的内容虽然不难,但在银行、医院这类金融医疗场景里是基本常识,值得专门写进文档。
4. 常见问题排查与实操避坑实录
4.1 环境与版本:Spring Boot版本、MySQL驱动、Maven构建常见故障
这份内容几乎每个做Spring Boot项目的同学都会遇到,直接列一个速查表,方便对应排查:
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 启动报Failed to configure a DataSource | 没配数据源或驱动缺失 | 检查application.yml中datasource配置,确认mysql驱动依赖已引入 |
| 报ClassNotFound: com.mysql.jdbc.Driver | MySQL 8驱动类名变了 | 驱动类改为com.mysql.cj.jdbc.Driver |
| 连接数据库报时区错误 | URL缺少serverTimezone参数 | URL加上serverTimezone=Asia/Shanghai |
| 启动报Invalid bound statement | Mapper XML路径不对 | 检查mybatis-plus配置的mapper-locations是否匹配 |
| Maven打包后jar无法运行 | 缺少spring-boot-maven-plugin | 在pom中加入该插件,并执行repackage |
| JDK8环境下Boot 3.x项目编译失败 | Boot 3.x要求JDK17 | 换用Spring Boot 2.7系列,或升级JDK版本 |
这里面最典型的坑是“Spring Boot版本太高”。很多同学下载项目模板时直接选了当前最新的3.x版本,本地JDK还是8,一编译就报错。我的建议是课设阶段不要追新,选一个长期维护的稳定版本即可,比如Spring Boot 2.7.18,搭配JDK8和JDK11都没问题。等环境稳定了,再去了解新特性也不迟。
数据库连接池另一个常见问题是“连接耗尽了”。表现为页面请求卡住很久然后在日志里看到Connection is not available, request timed out,这通常是连接池最大连接数设置太小,或者有连接泄露——比如代码中手动获取了Connection没有归还。排查时优先看代码里有没有忘了关闭连接的地方,其次再调大maximum-pool-size。HikariCP默认的10个连接对课设场景通常足够了,出现耗尽基本都是代码问题。
4.2 日常开发:日期格式、驼峰映射、Thymeleaf热更新与跨域问题
前面的版本问题解决后,开发期还会遇到几个非常琐碎但高频的坑。第一个是日期格式问题。前端表单提交的日期字符串比如“2024-01-15”,后端DTO里的LocalDate字段如果不加注解,会直接报反序列化失败。参数接收时在LocalDate字段上加@DateTimeFormat(pattern = "yyyy-MM-dd")可以解决;JSON返回给前端时,配合全局Jackson配置或者字段上的@JsonFormat,把LocalDateTime输出成“yyyy-MM-dd HH:mm:ss”这种可读格式。
第二个是MyBatis Plus的驼峰映射问题。如果数据库字段是user_id,实体字段是userId,正常情况下mapUnderscoreToCamelCase开启后能自动映射。但如果你用了resultMap且没有显式配置column属性,有时也会出现映射不到导致字段为null的情况。建议统一使用MyBatis Plus的LambdaQueryWrapper和selectById这种内置方法,基本不会遇到这类问题。
第三个是Thymeleaf热更新。如果前端用了Thymeleaf模板,每次改HTML还要重启项目非常影响效率。在application.yml里加一行spring.thymeleaf.cache: false,再配合idea的Build project automatically设置,修改模板后刷新页面就能看到变化。需要注意生产环境一定要把模板缓存打开,否则每次渲染都读磁盘,性能会差很多。
第四个是跨域问题。如果前端和后端分开部署,比如前端在8081端口跑Node服务,后端在8080端口跑Spring Boot,前端请求必然触发CORS跨域。方案是写一个WebMvcConfigurer配置类,设置允许的域名、请求头和请求方法。不过课设通常只有一个端口,偶尔遇到跨域也别慌张,就是一层配置的事。
4.3 交付与答辩:数据库脚本、万字文档和演示顺序怎么准备
项目交付时,源码、数据库和文档三者缺一不可。数据库这部分的交付一定不只是放一张建表SQL。我习惯在项目根目录建sql文件夹,里面放两个脚本:schema.sql负责建库建表,data.sql负责插入初始化数据,包括几个测试科室、医生账号、患者账号和一些药品数据。这样验收老师拿到项目后导入数据库就能直接跑起来,第一印象会好很多。
文档结构参考以下目录基本能写满万字:项目背景与意义、国内外研究现状、需求分析(功能需求+非功能需求)、系统设计(架构设计+模块设计)、数据库设计(ER图+表结构说明)、详细设计(核心模块流程+核心代码说明)、系统测试(测试用例+测试结果)、部署说明、总结与展望。画ER图的时候不要只画出表名,一定要把每个字段和主外键关系标清楚,这是数据库设计章节最核心的内容。
答辩演示的顺序建议是:先登录,展示不同角色的不同界面;然后以患者身份新建档案和预约挂号;再切换成医生身份,看到预约列表后开始接诊,录入主诉和诊断,原地开处方;再到药品管理模块看库存有没有扣减;最后到收费模块完成缴费。这个顺序其实就是业务闭环,让评委顺着真实的工作流看下来,几乎不需要额外解释。评委问“数据库在哪些地方用了事务”,你直接把药品扣减和收费生成的例子抛出来;问“权限怎么控制的”,就把JWT拦截器讲一遍。把这两个问题准备充分,答辩基本就稳了。
最后分享一个我在这类项目上体会最深的地方:医院管理系统不是一个只靠技术就能堆起来的项目,业务闭环要顺,数据关系要能自洽。哪怕代码写得再花哨,到演示时挂号、就诊、收费这些环节连不通,也一样会被扣分。反过来,只要把业务主流程跑通、数据库关系讲清楚,再配上一两个像眼健康档案这样有领域特色的模块,答辩的时候基本就能稳稳占住主动权。
另外一个小建议:一定要在项目里保留好完整的SQL脚本和初始化数据,不要只放空表结构。因为评委和老师拿到项目后第一件事通常就是导入数据库、跑起来,这一步顺不顺决定第一印象。如果你正准备基于Spring Boot做管理系统类的课设或毕设,眼科医院这个题目的业务张力足够大,做出来的东西也够撑起一份万字文档,值得认真投入。