接手“基于SpringBoot+Vue的社区医院管理系统”这类项目时,很多人会先入为主地觉得:无非就是 Java 后端加点页面,把增删改查写完就交差。但真把这个系统从零搭起来,你会发现要理顺的远不止 CRUD,挂号、收费、药房库存和多角色权限之间的业务闭环才是最大的工作量。这篇文章我想用实际开发中的完整视角,把整个社区医院管理系统的设计与实现拆开讲一遍,包括后端 SpringBoot + MyBatis + MySQL 怎么组织、前端 Vue 怎么对接、数据库怎么设计、上线前要避开哪些坑。无论你是拿它做毕业设计,还是想通过一个完整项目理解 JavaWeb 开发,这篇内容都能给你一套可以直接落地的思路。
1. 项目整体设计与业务拆解
1.1 先想清楚“管什么”,再谈“怎么实现”
社区医院和大型三甲医院的信息系统不同,它的业务体量相对小,但流程不能缺。我从实际需求里整理出三条主线:患者线(建档、挂号、就诊、收费)、药品线(入库、库存、发药、退药)、管理线(员工账号、角色权限、数据统计)。把这三条主线画成流程图之后,再去划分后端模块就非常清晰,不会今天加一个接口明天推倒重来。
有人问我:社区医院系统是不是只用一张表存订单就行?这是把问题想简单了。挂号后医生要开处方,处方里有多种药品;收费后药房要减库存;如果患者退费,还要联动退回库存。每一步都涉及多个表的写入和状态流转,所以数据库设计阶段就必须考虑事务边界。如果一开始就把表结构拍脑袋定下来,后面写业务代码时一定会不断改表,改表又意味着改实体类、改 Mapper、改接口,返工成本非常高。
1.2 为什么选前后端分离,而不是 JSP 模板
如果只是做一个课程设计,用 JSP + Servlet 也能跑。但 SpringBoot + Vue 的结构更适合“接口复用”和“多端扩展”。后端只负责 RESTful API,前端通过 axios 调用,将来如果要加移动端或者小程序,后端完全可以继续复用。前后端分离还带来一个好处:开发时不用把整个 Tomcat 跑起来才能看页面,前端起 Vite 开发服务器就能独立调样式和交互,效率高很多。
开发这个系统时,我的分工是:先定好接口文档,前端同学可以把 Mock 数据挂在真接口地址上并行开发,后端只需按文档提供数据格式。等到联调阶段,问题主要集中在字段命名不一致和状态码约定不统一,提前用统一返回结构能省掉大量扯皮。所以你在做项目时,不要急着写代码,先把“返回结构长什么样、哪些接口需要登录、权限码怎么命名”这几件事定下来,后面会顺利得多。
1.3 功能模块与角色权限矩阵
社区医院系统常见的角色有管理员、医生、收费员、药房药师。每个角色看到的菜单和操作按钮都不一样。管理员负责账号和药品基础数据,医生可以挂号、开处方但不能改药品价格,收费员能收费不能发药,药房能确认发药但不能改处方。这就需要一个可扩展的 RBAC 权限模型。
我采用的权限分三级:用户关联角色,角色关联菜单,菜单里保存按钮级标识,比如user:add、drug:update。前端在路由守卫里根据当前用户的权限过滤菜单,后端在每个需要权限的 Controller 接口上做二次校验。这套方案不复杂,但对毕业设计和工作中的小项目都完全够用,截图和答辩也好讲。关键是不要做成“一个管理员账号走天下”,那样项目看起来非常单薄,也体现不出权限设计的价值。
2. 技术栈选型与环境搭建
2.1 后端:SpringBoot 版本不能乱选
后端我建议用 SpringBoot 2.7.18 + JDK 8 + MyBatis 2.3.2。不是不能用 SpringBoot 3 + JDK 17,而是 3.x 对部分老教程中的配置、依赖坐标都有变化,很多同学照抄 2.x 的写法会踩“springboot版本太高”的坑。比如 javax.servlet 改成 jakarta.servlet、Spring Security 6 配置方式变了,一套代码纠缠下来反而消耗精力。如果你的目标是快速把系统跑起来,2.7.18 是最稳的组合。
pom.xml 里不需要把依赖堆满,核心就是spring-boot-starter-web、mybatis-spring-boot-starter、mysql-connector-j、lombok,再加一个jjwt做登录令牌。下面是最小可用配置:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.18</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.3.2</version> </dependency> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies>JDK 8 对应 mysql-connector-j 8.0.x,SpringBoot 2.7 的依赖管理已经会自动匹配。不要手写一个 5.1.47 的老驱动去连 MySQL 8,否则会出现时区错误和连接失败。
2.2 前端:Vue3 + Vite + Element Plus
前端选用 Vue3 + Vite 4 + Element Plus + Pinia。社区医院管理系统的界面不追求花哨,Element Plus 的表格、表单、弹窗组件足够覆盖挂号、开方、发药这些场景,而且组件库的中文文档非常友好。相比 Vue2 的 Webpack 工程,Vite 的启动速度对调试期帮助很大。
不过 Vue3 对 Node 版本有要求,建议 Node 16.20 以上。如果你电脑上 Node 版本太老,npm install会报 ERESOLVE 错误,最简单的处理是把 Node 升级到 16.20 或 18 LTS。项目初期,Vite 配置代理转发后端接口,前端代码里尽量不写死http://localhost:8080,避免部署时到处改地址。前端的工程化和环境管理一开始就要做好,否则后面切换环境时会漏改,导致用户手机上登录不了。
2.3 数据库初始化与连接配置
数据库我统一用 MySQL 8.0,字符集指定 utf8mb4。连接配置有几个容易踩的细节:首先要加useSSL=false,否则 MySQL 8 默认会尝试 SSL 握手;其次要加serverTimezone=Asia/Shanghai,不然 JDBC 连接时会因为时区问题报错;最后allowPublicKeyRetrieval=true也不能少,尤其在本地用 root 账号连接时很常见。
server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/community_hospital?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true username: root password: 123456 jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: Asia/Shanghai mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.hospital.entity configuration: map-underscore-to-camel-case: truemap-underscore-to-camel-case建议开启,数据库字段patient_name就能自动映射到实体类里的patientName,减少大量手写 resultMap。多对一复杂查询再单独写 resultMap 即可。如果你用的是 MySQL 5.7,连接驱动和 url 参数会稍有差异,但 utf8mb4 和时区这两个配置依然是通用的。
3. 数据库设计:从门诊业务到库存流水
3.1 核心表设计思路
社区医院系统的表我分为四组:系统权限组(sys_user、sys_role、sys_menu、sys_user_role)、患者问诊组(patient、registration、prescription、prescription_item)、药品库存组(drug_info、drug_stock、drug_stock_log)、收费结算组(payment_order、payment_item)。这个分组不是拍脑袋想出来的,而是对应前面提到的三条业务主线,每个表都能找到它所属的业务环节,开发时定位问题也快。
在设计“挂号—开方—收费—发药”这个核心链路时,要特别注意状态字段。比如挂号表有 status:0 待就诊、1 就诊中、2 已完成、3 已取消;收费单有 status:0 待支付、1 已支付、2 已退费。状态字段用 tinyint 比字符串省空间,但代码里必须定义常量类统一管理,比如RegistrationStatusEnum,不要散落一地的if (status == 1)。
3.2 几张核心表的建表参考
以患者表和处方明细表为例,下面这些字段是这个系统里最常见的结构:
CREATE TABLE patient ( id BIGINT PRIMARY KEY AUTO_INCREMENT, patient_no VARCHAR(32) NOT NULL COMMENT '病历号', patient_name VARCHAR(64) NOT NULL, gender TINYINT NOT NULL COMMENT '1男 2女', phone VARCHAR(20), id_card VARCHAR(18), create_time DATETIME DEFAULT CURRENT_TIMESTAMP, del_flag TINYINT DEFAULT 0, UNIQUE KEY uk_patient_no (patient_no) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='患者表'; CREATE TABLE prescription_item ( id BIGINT PRIMARY KEY AUTO_INCREMENT, prescription_id BIGINT NOT NULL, drug_id BIGINT NOT NULL, drug_name VARCHAR(64) NOT NULL, price DECIMAL(10,2) NOT NULL COMMENT '开单时单价', quantity INT NOT NULL, amount DECIMAL(10,2) NOT NULL COMMENT '数量*单价', status TINYINT DEFAULT 0 COMMENT '0未发药 1已发药', KEY idx_prescription_id (prescription_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='处方明细表';处方明细为什么要冗余drug_name和price字段?因为药品价格可能调整,开单之后这个处方必须保留当时的快照,不能用户来缴费时发现金额变了。实际项目里这种“业务快照”非常常见,理解了这一点,就明白不是所有字段都要严格遵循三范式。
3.3 数据库设计中的避坑点
第一,金额字段一定用 DECIMAL,不要用 DOUBLE 或 FLOAT。二进制浮点数在计算金额时存在精度问题,收费、退费场景下一分钱都不能差。第二,不要大量使用物理外键。比如 prescription_item 的 drug_id 没必要建 FOREIGN KEY,否则删除药品、导入数据时会遇到大量约束冲突。只需要在常用查询列建普通索引。第三,用户密码不要明文存,至少用 BCrypt 加密。在演示环境里有人觉得无所谓,但真正部署到社区医院后,数据安全是必须考虑的底线问题。
索引方面,我重点在registration.patient_id、prescription.patient_id、prescription_item.drug_id上加了普通索引。不要给所有列都加索引,增删改会变慢;也不要一张表只有主键索引,时间一长查询日志会把数据库拖垮。创建索引时还要考虑 YourSQL 的区分度,性别这种字段区分度太低,加了索引反而浪费空间。
4. 后端核心模块实现
4.1 统一返回结果和全局异常处理
所有接口必须返回统一结构,前端才能用同一个逻辑处理。我定义Result<T>,里面放 code、message、data。code 为 200 表示成功,其他码对应业务异常。这样比 HTTP 状态码 + 乱七八糟的 body 更好排查问题。
@Data public class Result<T> { private Integer code; private String message; private T data; public static <T> Result<T> success(T data) { Result<T> r = new Result<>(); r.setCode(200); r.setMessage("操作成功"); r.setData(data); return r; } public static <T> Result<T> error(Integer code, String message) { Result<T> r = new Result<>(); r.setCode(code); r.setMessage(message); return r; } }全局异常处理用@RestControllerAdvice。业务异常统一抛ServiceException,由切面捕获后转成 Result;未知异常打日志后返回“系统繁忙”的通用提示。这一步相当重要,否则数据库字段过长、空指针等异常直接堆给前端,接口联调效率极低。日志一定要用 Slf4j,别用 System.out.println,否则线上排查问题根本没有可用线索。
4.2 登录认证与权限控制
这个系统的登录流程是:前端提交用户名和密码,后端校验通过后签发 JWT,前端把 token 存到 localStorage 或 Pinia 中,后续请求通过Authorization头携带。后端写一个拦截器解析 token,并从数据库读取用户权限。如果没有 Redis,初版可以直接把权限列表放进 JWT 的 claims 里,但要注意 JWT 体积不能太大,不然每个请求都带着很长的 header。
我在实践中没有引入 Spring Security,而是用拦截器 + 自定义注解完成权限校验。Spring Security 功能强大但学习成本高,对于社区医院这个体量的项目,自己写的拦截器反而更容易掌控。核心原理不复杂:登录成功后把 userId、userName、roleId 放进 token;拦截器先从 header 取到 token,解析成功再放行,失败返回 401。需要接口级权限时,加上@RequirePermission("drug:update")注解,在拦截器里检查当前角色是否拥有该权限标识即可。
4.3 MyBatis 映射与动态 SQL 的实战写法
MyBatis 在项目里最大的价值是动态 SQL,不要所有查询都写死。患者列表通常要支持按姓名、手机号、病历号筛选,用<where>和<if>拼接条件非常方便。
<select id="selectPatientList" resultType="com.example.hospital.entity.Patient"> SELECT id, patient_no, patient_name, gender, phone, create_time FROM patient <where> <if test="patientName != null and patientName != ''"> AND patient_name LIKE CONCAT('%', #{patientName}, '%') </if> <if test="phone != null and phone != ''"> AND phone = #{phone} </if> AND del_flag = 0 </where> ORDER BY id DESC </select>动态 SQL 里最容易犯的错是“单个字符比较”。比如<if test="status == '1'.toString()">在 OGNL 里实际会进行字符与字符串比较,导致条件不生效。正确写法是status == '1',让 OGNL 把它当字符处理,或者直接写成status == 1配合数字类型。我见过很多新手在这个细节上卡半天,页面明明传了状态参数,查询结果却不过滤。
关于 MyBatis 批量操作,实际开发中处方明细插入、收费明细插入几乎天天用到。批量插入的 SQL 可以用<foreach>拼接 values,但注意一次不要插入太多条,否则 SQL 过长会超出 MySQL max_allowed_packet。我一般把批量大小控制在 200 条以内,分批插入,用事务保证一致性。
4.4 挂号、收费、发药事务怎么保证一致
假设收费成功后药房才能发药,如果发药时库存不足,整个流程必须回滚?实际场景分两种:收费是独立动作,发药是独立动作,它们之间用状态字段驱动,不必在一个事务里。真正必须在一个事务里的是“收费单主表 + 收费明细 + 更新挂号状态”,这三步要一起成功或失败。
以发药为例,用 MyBatis 更新库存时要防止超卖。不要先查出库存再在内存里减,然后 update 回去,这样并发时会超卖。正确做法是直接在 SQL 里加条件:
<update id="deductStock"> UPDATE drug_stock SET stock = stock - #{quantity} WHERE drug_id = #{drugId} AND stock >= #{quantity} </update>如果影响行数为 0,说明库存不足,直接抛出异常。在@Transactional方法里抛异常,前面已经执行的更新会自动回滚。这是非常典型的并发控制技巧,尤其适合社区医院高峰期多人同时挂号发药的场景。
4.5 MyBatis 缓存踩过的坑
MyBatis 一级缓存是 SqlSession 级别的,Spring 集成后,一次事务中多个相同查询可能命中一级缓存。看起来性能提升了,但如果你在同一个事务里先查询药品,再由其他服务直接修改数据库,第二次查询拿到的还是旧数据。处理这种问题的思路是:报表统计类查询不要和写操作放在同一个长事务里,必要的时候用@Transactional(propagation = Propagation.REQUIRES_NEW)隔离。
二级缓存我默认关闭,除非是字典表这类极少变动的数据。开启二级缓存后,如果缓存实体没有正确实现序列化,或者没有设置缓存刷新策略,就会出现一堆幽灵数据,调试成本远高于收益。日常开发中,性能瓶颈往往不在缓存,而在 SQL 是否走了索引、是否产生了 N+1 查询,先把这些基础问题排查干净,再考虑上缓存。
5. 前端页面与接口对接
5.1 Vue3 项目结构和动态路由
前端项目结构不需要太复杂,但目录要清晰。我的推荐目录是:
src/ api/ # 按模块拆分的接口请求 assets/ components/ layout/ # 后台布局框架 router/ # 静态路由 + 动态路由 store/ # Pinia views/ system/ # 用户、角色、菜单 outpatient/ # 挂号、接诊、处方 pharmacy/ # 药品库存、发药 payment/ # 收费管理 dashboard/ # 首页统计 utils/request.js动态路由是实现权限菜单的关键:用户登录后,后端返回该角色可以访问的菜单树和按钮权限,前端用router.addRoute把动态路由注入。这样普通用户在地址栏手动输入/system/user也不会看到用户管理页面,因为路由根本不存在。具体实现时,先定义一个静态路由只有/login和根路由,其他页面全部动态添加,这样权限也集中在接口返回的菜单数据里,前后端逻辑都清晰。
5.2 Axios 封装与请求拦截
axios 封装我放在utils/request.js里,统一 baseURL、超时时间、请求拦截和响应拦截。请求拦截器里从 Pinia 取 token 并加到 header;响应拦截器里如果 code 为 401,就清空登录状态并跳转登录页。
service.interceptors.response.use( (response) => { const res = response.data if (res.code === 200) return res if (res.code === 401) { router.push('/login') } ElMessage.error(res.message || '请求失败') return Promise.reject(new Error(res.message)) }, (error) => { ElMessage.error(error.message || '网络异常') return Promise.reject(error) } )一定要在拦截器里对res.code === 200判断,而不是每次调用接口都写if (res.data.code === 200)。不然代码里会堆满重复判断,而且容易漏判。另一个细节是文件导出接口,如果后端返回的是二进制流,响应拦截器就不能统一走 JSON 解析,需要在请求时单独设置responseType: 'blob',这一点容易被忽视。
5.3 挂号页面的实现思路
挂号页面是这个系统的门面。用户输入手机号后先查患者,如果不存在则跳转到建档;建档完成后再选择科室、医生,提交挂号。前端表单校验用 Element Plus 的 rules,手机号格式、身份证格式都可以在前端先挡一道,减少后端无效请求。具体页面组件可以拆成PatientSearch.vue和RegistrationForm.vue,一个负责查患者,一个负责挂号,职责单一,后续维护也方便。
提交挂号时,后端接口接收patientId, doctorId, deptId, registrationFee等参数。挂号成功后返回挂号单号,前端可以打印一张简单的小票。这个小功能在实际社区医院里非常实用,也是答辩时能加分的亮点。打印功能可以用浏览器的window.print()结合一段独立打印样式区实现,不必引入复杂插件。要注意的是打印区域外的元素要隐藏,不然打印出来会带上侧边栏和顶部菜单。
5.4 前端打包和部署常见坑
开发模式用 Vite 代理没有问题,但打包后如果直接把 dist 丢进 Nginx,可能会出现两个典型问题:第一个是使用 history 路由模式时刷新页面 404,需要在 Nginx 里配置 try_files 回退到 index.html;第二个是接口地址还指向 localhost,前端页面打开后所有接口全部失败,打包前一定要通过环境变量区分VITE_API_BASE_URL。
还有一个容易被忽略的问题:Element Plus 按需引入时,表格、表单、消息组件如果没自动导入,页面会“看起来没样式”。我建议初稿使用时直接把完整组件库引入,等系统稳定后再考虑按需引入,避免样式问题干扰业务调试。如果前端打包后布局异常,优先检查 index.html 里的<div id="app">是否被样式覆盖,以及路由懒加载的组件路径是否正确,很多布局问题都是加载不到组件导致的。
6. 实操流程串联:从挂号到发药的完整数据流转
6.1 准备基础数据
要跑通整个流程,先要准备管理员账号、科室数据、医生账号和药品字典。管理员在“系统管理-用户管理”里创建医生账号并分配角色;在“药品管理”里录入药品名称、规格、价格和初始库存。这个过程同时也是在测试权限功能,非管理员账号不能进入系统管理菜单。
我建议建一个init.sql初始化脚本,把管理员账号、默认角色、默认科室和几种常用药品都写进去。不要每次都在页面上手工点,效率太低,而且演示的时候一旦数据被改乱了,重新初始化只需要执行一条 SQL,前端后端都不用动。初始化脚本里要包含sys_user、sys_role、sys_user_role、sys_menu等基础数据,这样系统第一次启动就能直接登录。
6.2 核心流程操作与接口调用
我把操作拆成四步,每一步都关注表和状态的变化:
- 挂号:收费员选择或新建患者,提交
/api/registration,生成挂号记录,状态为“待就诊”。 - 接诊开方:医生在我的接诊列表里选择患者,进入开方页面,添加多个药品,提交
/api/prescription。此时只生成处方和明细,不扣库存。 - 收费:收费员根据挂号单或处方号查询待支付订单,确认金额后点击收费,调用
/api/payment/pay。后端在同一个事务里生成收费单、更新挂号状态为“已缴费”。 - 发药:药房药师看到已缴费未发药的处方,点击发药,调用
/api/pharmacy/dispense。后端循环处方明细扣减库存,并把明细状态更新为“已发药”。
这个流程每步的身份都要对上:发起请求的 token 对应账号的角色,决定能否调用这个接口。如果你发现页面操作“没反应”,第一步不是看前端代码,而是看浏览器 Network 里接口返回的 code 和 message,判断是权限拒绝、参数错误还是后端抛异常。我在联调时经常遇到前端报 500,结果一查日志是参数类型转换失败,这种问题在接口文档里写清楚字段类型就能避免。
6.3 库存回滚与退费逻辑
实际中退费也是重要环节。患者缴费后还没发药,这时要退费,正确的顺序是:先作废收费单、更新挂号状态为“已取消”,再释放待发药处方;如果已经发药,还要先做退药回补库存,再做退费。这个顺序不能颠倒,否则会出现“钱退了,库存却少了”的严重问题。我把退费接口用@Transactional包裹,并且加上状态校验,只有“已缴费未发药”的订单允许退费。
退费操作还有一个容易被忽略的问题:不同支付方式的退款方式不同,如果是现金,直接退现金;如果是扫码支付,还要考虑原路退回。社区医院管理系统一般先做成“现金退费”或者“人工确认退款”,因为真正对接微信支付、支付宝支付需要商户号、证书、回调验签等一系列配置。如果你只是做课程设计,把退费状态流转做对就行,支付通道对接可以当成扩展功能写在文档里。
7. 常见问题与排查技巧实录
7.1 高频报错速查表
| 现象 | 原因 | 处理方式 |
|---|---|---|
Invalid bound statement (not found) | Mapper 接口和 XML namespace 不匹配,或 mapper-locations 没扫到 XML | 检查 namespace 是接口全限定名,在 application.yml 配置classpath:mapper/*.xml |
The server time zone value ... | JDBC 连接 MySQL 时区未指定 | URL 加serverTimezone=Asia/Shanghai |
Public Key Retrieval is not allowed | MySQL 8 使用 caching_sha2_password 认证 | URL 加allowPublicKeyRetrieval=true |
| 前端请求跨域 | 开发环境端口不同或部署域名不同 | 开发环境配置 Vite proxy,生产环境 Nginx 反向代理 |
| history 路由刷新 404 | 前端路由走 history 模式,Nginx 没回退 | Nginxtry_files $uri $uri/ /index.html; |
| MyBatis 单个字符比较不生效 | OGNL 字符/字符串类型判断有歧义 | 把字段转字符串比较或用数字状态 |
| 页面能显示但布局异常 | Element Plus 组件样式未正常引入或按需引入遗漏 | 先全量引入组件库,确认功能后优化 |
| 批量插入 SQL 过长 | foreach 拼接的 values 太多 | 分批插入,建议每次 100~200 条 |
这张表覆盖了我开发中最常见的几个问题。其实大部分启动报错都集中在配置层面,只要先把 SpringBoot 和 MyBatis 的基础连接跑通,后面业务的报错反而更好定位。排查问题时要学会看堆栈第一行,不要整个异常信息全贴到搜索引擎,第一行经常包含真正的错误原因。
7.2 三个容易忽略的实战经验
第一,不要一上来就写业务代码。先把后端工程启动起来,确认 MySQL 连接、MyBatis 扫描、前端登录打通这三个基础链路没问题,再往后加功能。否则后面几十个接口时报错,你根本分不清是配置问题还是业务问题。我见过一个同学花了两周写完全部代码,结果启动时发现 Mapper 扫描路径配错,所有接口都不通,最后开始排查时心态已经崩了。
第二,日志一定要打到位。在 Service 层方法入口打印入参,在事务方法出口打印结果,尤其是挂号、收费、发药这类写操作。有一次我排查“库存居然变成负数”的问题,就是通过日志发现同一张处方被两个请求重复提交,后来在发药接口里加了一个幂等校验:同一个处方明细状态为已发药时,直接返回“该药品已发药,请勿重复操作”。日志不仅是给自己看的,也是给后期的维护者看的,所以输出格式尽量包含方法名、入参和耗时。
第三,在演示和答辩的时候,不要把系统吹得太满。社区医院管理系统能完整跑通“患者建档、挂号、开方、收费、发药、退费”这条主链路,已经是一个相当完整的作品。中大型医院项目里的 HIS、LIS、PACS 等专业系统,不是一个小团队在短时间内能做出来的。把核心业务做深做稳,比堆十多个半成品模块更有说服力。
最后说一个实在的建议:如果你准备拿这个项目作为毕业设计或者求职项目,一定要能自己讲清楚每一个表字段和每一个接口的用途。面试官大概率会沿着“登录鉴权怎么实现、库存怎么防止超卖、退款怎么保证一致性”这几个方向追问。按照我上面分享的设计思路和踩坑经验,把这些点理顺,这个项目会是一份很有分量的实践经历。