
简介这是一套面向Java开发者与计算机专业学生的医院管理系统完整源码采用Java技术栈实现挂号、门诊、药房、住院等核心医疗业务模块适合作为课程设计、毕业设计或企业级项目练手的参考方案。压缩包共1236个文件约8.91MB以791个java源文件为主体辅以106个xml配置、84个js与37个html前端页面另有sql脚本、yml配置、dockerfile及properties等部署与数据文件结构完整、层次清晰。系统涵盖在线预约挂号、电子病历、处方开具、药品库存与过期预警、入院登记与费用统计等功能并涉及权限控制、RESTful接口设计及单元与压力测试思路。目前已有681人学习下载读者可借此理解医疗行业业务逻辑掌握Spring Boot、MyBatis等主流框架的落地方式并参考其数据库表结构与模块划分快速搭建自己的信息化管理项目。1. 从一份 Java 医院管理系统源码说起挂号、门诊、药房、住院到底怎么串起来很多做 Java 课程设计或者想练手企业级项目的同学都会在搜索框里敲下「医院管理系统 源码 数据库」这几个词。但真正拿到一份能跑的源码之后问题往往不是「代码看不懂」而是「业务串不起来」——挂号完了数据去哪了、门诊开的处方怎么流到药房、住院费用又是怎么和医嘱对上的这些才是卡住大多数人的地方。这份基于 Java 开发的医院管理系统源码覆盖了挂号、门诊、药房、住院四条核心业务线配套数据库脚本属于典型的 Spring Boot MyBatis 分层架构适合想完整跑通一套医疗业务闭环的开发者。它解决的不是「教你写 CRUD」而是让你看到一张挂号单从生成到结算中间到底经过了哪些表、哪些状态、哪些接口。如果你正在找一份能直接部署、能改、能讲清楚业务链路的 Java 项目这份资源值得往下看。2. 环境搭建与数据库初始化从 SQL 脚本到能登录的第一屏拿到源码压缩包之后别急着打开 IDE 就点运行。我见过太多人卡在「启动报错」这一步最后发现是数据库没建对或者配置文件里的连接串没改。这一章把环境搭建和数据库初始化拆开讲按顺序走基本能一次点亮。2.1 技术栈确认与依赖版本对齐从项目正文给出的信息来看这套系统后端以 Java 为主常见做法是 Spring Boot 做 Web 层MyBatis 或 MyBatis-Plus 做数据访问前端页面用的是 Bootstrap AdminLTE 这套后台模板配置文件里出现了 nginx.conf、redis.conf、broker.conf、logstash.conf说明它不只是个单机 Demo而是预留了反向代理、缓存、消息队列和日志采集的接入点。另外出现了 io.seata.server.coordinator.AbstractCore 这个类路径意味着分布式事务组件 Seata 也在依赖里虽然课程设计场景下不一定真的启用但依赖冲突往往就出在这里。我一般会先做三件事确认 JDK 版本、确认 Maven 依赖能拉下来、确认数据库版本和脚本匹配。JDK 建议用 8 或 11Spring Boot 2.x 对这两个版本兼容性最好如果源码里 pom.xml 写的是 Spring Boot 3.x那就必须上 JDK 17。数据库方面MySQL 5.7 和 8.0 在语法上有差异尤其是utf8mb4字符集和DATETIME默认值这块脚本报错大概率是版本不对。# 查看当前 JDK 版本确认和 pom.xml 里的 java.version 一致 java -version # 查看 Maven 版本建议 3.6 以上 mvn -v # 登录 MySQL 确认版本 mysql -uroot -p -e SELECT VERSION();这三条命令看起来简单但能帮你排除掉一半的「玄学启动失败」。版本对不上后面所有步骤都是白费。2.2 数据库脚本导入与表结构核对数据库脚本一般放在sql/或者db/目录下文件名可能是hospital.sql、init.sql之类。导入之前先看一眼脚本头部有没有CREATE DATABASE语句如果没有需要自己先建库。-- 创建数据库字符集必须用 utf8mb4否则中文姓名和地址会乱码 CREATE DATABASE hospital_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; -- 切换到这个库 USE hospital_db; -- 导入脚本在命令行执行不在 SQL 客户端里 -- mysql -uroot -p hospital_db hospital.sql导入完成之后别急着启动项目先核对几张核心表是否存在。挂号业务通常涉及patient患者、doctor医生、department科室、schedule排班、registration挂号记录门诊涉及medical_record病历、prescription处方药房涉及drug药品、drug_stock库存住院涉及admission入院登记、ward病房、bed床位、inpatient_order医嘱。-- 核对核心表是否都建好了 SHOW TABLES LIKE %patient%; SHOW TABLES LIKE %registration%; SHOW TABLES LIKE %prescription%; SHOW TABLES LIKE %drug%; SHOW TABLES LIKE %admission%; -- 查看挂号表结构重点关注状态字段和索引 DESC registration;如果发现某张表缺失大概率是脚本执行到一半报错中断了往上翻日志找第一个报错的位置。常见原因是外键约束顺序不对或者某个字段用了当前 MySQL 版本不支持的语法。2.3 配置文件修改与首次启动配置文件通常在src/main/resources/下核心是application.yml或application.properties。需要改的地方就三处数据库连接、Redis 连接、端口号。# application.yml 关键配置片段 spring: datasource: url: jdbc:mysql://localhost:3306/hospital_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 你的密码 driver-class-name: com.mysql.cj.jdbc.Driver redis: host: localhost port: 6379 # 如果本地没装 Redis先把相关依赖注释掉或者改成 spring.redis.enabledfalse server: port: 8080这里有个坑要注意serverTimezone必须显式指定否则 MySQL 8.0 驱动会报时区错误。另外如果源码里集成了 Seataapplication.yml里可能还有seata.tx-service-group之类的配置本地跑不通的话先把 Seata 相关配置注释掉用不到分布式事务的场景不需要它。启动命令用 Maven 就行# 在项目根目录执行跳过测试可以加快启动 mvn spring-boot:run -DskipTests # 或者先打包再运行 mvn clean package -DskipTests java -jar target/hospital-*.jar看到控制台输出Started Application in x.x seconds就算起来了。浏览器访问http://localhost:8080默认账号密码一般在README或者数据库的user表里常见的是admin/123456或admin/admin。3. 挂号与门诊模块排班、并发锁和处方流转的实现细节环境跑通之后真正值得花时间看的是业务模块。挂号和门诊是医院系统里数据流转最密集的两块也是面试和答辩时最容易被追问的地方。这一章把这两个模块拆开讲清楚排班怎么建、挂号怎么防重复、处方怎么从医生端流到药房端。3.1 排班表设计与挂号并发控制排班是挂号的源头。没有排班患者就看不到哪个医生哪天出诊。排班表的设计通常包含doctor_id、department_id、work_date、time_slot上午/下午、total_quota总号源、remaining_quota剩余号源这几个字段。-- 排班表典型结构 CREATE TABLE schedule ( id BIGINT PRIMARY KEY AUTO_INCREMENT, doctor_id BIGINT NOT NULL, department_id BIGINT NOT NULL, work_date DATE NOT NULL, time_slot TINYINT COMMENT 1上午 2下午, total_quota INT DEFAULT 0, remaining_quota INT DEFAULT 0, version INT DEFAULT 0 COMMENT 乐观锁版本号, UNIQUE KEY uk_doctor_date_slot (doctor_id, work_date, time_slot) );注意UNIQUE KEY这一行它保证了同一个医生同一天同一时段只有一条排班记录避免重复插入。version字段是给乐观锁用的挂号扣减号源时靠它防止超卖。挂号的核心逻辑是「扣减剩余号源 生成挂号记录」这两步必须在同一个事务里。常见做法是用乐观锁// 挂号服务核心逻辑简化版 Transactional(rollbackFor Exception.class) public RegistrationVO register(Long scheduleId, Long patientId) { // 1. 查询排班 Schedule schedule scheduleMapper.selectById(scheduleId); if (schedule.getRemainingQuota() 0) { throw new BusinessException(该时段号源已挂完); } // 2. 乐观锁扣减号源version 不匹配则更新失败 int affected scheduleMapper.decreaseQuota(scheduleId, schedule.getVersion()); if (affected 0) { throw new BusinessException(号源已被抢走请重新选择); } // 3. 生成挂号记录 Registration reg new Registration(); reg.setPatientId(patientId); reg.setScheduleId(scheduleId); reg.setStatus(RegistrationStatus.WAITING); // 待就诊 reg.setCreateTime(new Date()); registrationMapper.insert(reg); return convertToVO(reg); }对应的 Mapper XML 里decreaseQuota的 SQL 要带上version条件update iddecreaseQuota UPDATE schedule SET remaining_quota remaining_quota - 1, version version 1 WHERE id #{scheduleId} AND version #{version} AND remaining_quota 0 /update这段代码的逻辑是先查再改改的时候比对版本号。如果两个人同时挂号只有一个人能更新成功另一个会拿到affected 0然后提示重新选择。这就是乐观锁防超卖的标准做法。参数version是关键没有它两个线程同时读到remaining_quota 1就会都扣成功变成 -1。3.2 门诊接诊与处方状态机挂号完成之后患者状态是「待就诊」。医生在门诊模块看到这个队列点击接诊状态变成「就诊中」然后写病历、开处方。处方开完之后状态是「待缴费」缴费之后变成「待发药」药房发药之后变成「已发药」。这是一条典型的状态机。// 处方状态枚举 public enum PrescriptionStatus { CREATED(0, 已创建), PAID(1, 已缴费), DISPENSED(2, 已发药), CANCELLED(3, 已取消); private final int code; private final String desc; // 构造方法和 getter 省略 }处方主表和明细表是分开的。主表存处方头信息患者、医生、开方时间、总金额、状态明细表存具体药品药品 ID、数量、用法用量。CREATE TABLE prescription ( id BIGINT PRIMARY KEY AUTO_INCREMENT, prescription_no VARCHAR(32) UNIQUE NOT NULL, patient_id BIGINT NOT NULL, doctor_id BIGINT NOT NULL, registration_id BIGINT, total_amount DECIMAL(10,2) DEFAULT 0.00, status TINYINT DEFAULT 0, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE prescription_item ( id BIGINT PRIMARY KEY AUTO_INCREMENT, prescription_id BIGINT NOT NULL, drug_id BIGINT NOT NULL, quantity INT NOT NULL, usage_desc VARCHAR(200), unit_price DECIMAL(10,2), KEY idx_prescription (prescription_id) );状态流转的接口要加权限校验只有医生能开方只有收费员能确认缴费只有药师能发药。这套权限控制在 Spring Security 或者 Shiro 里配置角色和接口的映射关系一般在sys_role、sys_permission这几张表里。3.3 药房扣库存与处方核销药房模块的核心动作是「根据处方扣库存」。这里有个容易翻车的地方处方里的药品可能库存不足或者药品已过期。所以发药之前要先校验。// 发药逻辑简化版 Transactional(rollbackFor Exception.class) public void dispense(Long prescriptionId) { Prescription pres prescriptionMapper.selectById(prescriptionId); if (pres.getStatus() ! PrescriptionStatus.PAID.getCode()) { throw new BusinessException(处方未缴费不能发药); } ListPrescriptionItem items itemMapper.selectByPrescriptionId(prescriptionId); for (PrescriptionItem item : items) { // 校验库存 DrugStock stock stockMapper.selectByDrugId(item.getDrugId()); if (stock.getQuantity() item.getQuantity()) { throw new BusinessException(药品库存不足 item.getDrugId()); } // 扣减库存 int affected stockMapper.decreaseStock(item.getDrugId(), item.getQuantity()); if (affected 0) { throw new BusinessException(库存扣减失败请重试); } } // 更新处方状态为已发药 pres.setStatus(PrescriptionStatus.DISPENSED.getCode()); prescriptionMapper.updateById(pres); }decreaseStock的 SQL 同样要带条件update iddecreaseStock UPDATE drug_stock SET quantity quantity - #{quantity} WHERE drug_id #{drugId} AND quantity #{quantity} /updateAND quantity #{quantity}这个条件保证了不会扣成负数。如果affected 0说明库存不够事务回滚整张处方都发不出去。这就是「要么全发要么不发」的原子性。4. 住院管理与权限控制床位分配、医嘱跟踪和角色隔离住院模块比门诊复杂因为它是一个持续过程涉及入院登记、床位分配、医嘱下达、费用累计、出院结算多个环节。权限控制在这块尤其重要护士能看床位但不能开医嘱医生能开医嘱但不能改费用管理员什么都能看但不能碰具体诊疗数据。4.1 入院登记与床位状态同步入院登记的第一步是选床位。床位表通常有status字段0空闲、1占用、2维修。患者入院时床位从空闲变占用同时生成一条入院记录。CREATE TABLE bed ( id BIGINT PRIMARY KEY AUTO_INCREMENT, ward_id BIGINT NOT NULL, bed_no VARCHAR(10) NOT NULL, status TINYINT DEFAULT 0 COMMENT 0空闲 1占用 2维修, current_patient_id BIGINT DEFAULT NULL, UNIQUE KEY uk_ward_bed (ward_id, bed_no) ); CREATE TABLE admission ( id BIGINT PRIMARY KEY AUTO_INCREMENT, admission_no VARCHAR(32) UNIQUE NOT NULL, patient_id BIGINT NOT NULL, bed_id BIGINT NOT NULL, doctor_id BIGINT NOT NULL, admission_time DATETIME NOT NULL, discharge_time DATETIME DEFAULT NULL, status TINYINT DEFAULT 0 COMMENT 0在院 1已出院, total_cost DECIMAL(12,2) DEFAULT 0.00 );分配床位的逻辑要加锁否则两个护士同时给两个患者分配同一张床就会冲突。常见做法是用SELECT ... FOR UPDATE锁住床位行Transactional(rollbackFor Exception.class) public void admit(Long patientId, Long bedId, Long doctorId) { // 悲观锁锁定床位行 Bed bed bedMapper.selectByIdForUpdate(bedId); if (bed.getStatus() ! 0) { throw new BusinessException(床位已被占用); } // 更新床位状态 bed.setStatus(1); bed.setCurrentPatientId(patientId); bedMapper.updateById(bed); // 生成入院记录 Admission admission new Admission(); admission.setAdmissionNo(generateNo(AD)); admission.setPatientId(patientId); admission.setBedId(bedId); admission.setDoctorId(doctorId); admission.setAdmissionTime(new Date()); admission.setStatus(0); admissionMapper.insert(admission); }对应的 Mapper 方法select idselectByIdForUpdate resultTypeBed SELECT * FROM bed WHERE id #{id} FOR UPDATE /selectFOR UPDATE会在事务期间锁住这一行其他事务读这行时会等待直到当前事务提交或回滚。这就是悲观锁适合冲突概率高的场景。代价是并发性能下降但床位分配这种低频操作完全扛得住。4.2 医嘱下达与费用累计医嘱是住院期间的核心数据。医生每天查房后下达医嘱护士执行费用自动累计到入院记录里。医嘱表通常包含admission_id、doctor_id、order_type长期/临时、content、status待执行/已执行/已取消。CREATE TABLE inpatient_order ( id BIGINT PRIMARY KEY AUTO_INCREMENT, admission_id BIGINT NOT NULL, doctor_id BIGINT NOT NULL, order_type TINYINT COMMENT 1长期 2临时, content VARCHAR(500) NOT NULL, status TINYINT DEFAULT 0 COMMENT 0待执行 1已执行 2已取消, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_admission (admission_id) );费用累计的做法有两种一种是每次医嘱执行时实时累加另一种是出院时统一计算。实时累加体验更好但要注意事务一致性。Transactional(rollbackFor Exception.class) public void executeOrder(Long orderId) { InpatientOrder order orderMapper.selectById(orderId); if (order.getStatus() ! 0) { throw new BusinessException(医嘱状态不允许执行); } // 更新医嘱状态 order.setStatus(1); orderMapper.updateById(order); // 累加费用到入院记录 BigDecimal cost calculateOrderCost(order); admissionMapper.addCost(order.getAdmissionId(), cost); }update idaddCost UPDATE admission SET total_cost total_cost #{cost} WHERE id #{admissionId} /update这里的关键是total_cost用DECIMAL而不是FLOAT金额计算不能用浮点数否则会出现0.1 0.2 0.30000000000000004这种问题。4.3 基于角色的权限隔离实现医院系统的权限模型是典型的 RBAC基于角色的访问控制。用户表、角色表、权限表三张主表加两张关联表。核心逻辑是用户关联角色角色关联权限接口拦截时检查当前用户是否拥有对应权限。CREATE TABLE sys_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) UNIQUE NOT NULL, password VARCHAR(100) NOT NULL, real_name VARCHAR(50), status TINYINT DEFAULT 1 ); CREATE TABLE sys_role ( id BIGINT PRIMARY KEY AUTO_INCREMENT, role_code VARCHAR(50) UNIQUE NOT NULL, role_name VARCHAR(50) NOT NULL ); CREATE TABLE sys_user_role ( user_id BIGINT NOT NULL, role_id BIGINT NOT NULL, PRIMARY KEY (user_id, role_id) ); CREATE TABLE sys_permission ( id BIGINT PRIMARY KEY AUTO_INCREMENT, perm_code VARCHAR(100) UNIQUE NOT NULL, perm_name VARCHAR(100), url_pattern VARCHAR(200) ); CREATE TABLE sys_role_permission ( role_id BIGINT NOT NULL, permission_id BIGINT NOT NULL, PRIMARY KEY (role_id, permission_id) );在 Spring Security 里可以通过自定义UserDetailsService加载用户的角色和权限然后在配置类里用antMatchers做 URL 级拦截。Configuration EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() // 挂号接口挂号员和 admin 可访问 .antMatchers(/registration/**).hasAnyRole(REGISTRAR, ADMIN) // 门诊接口医生和 admin 可访问 .antMatchers(/outpatient/**).hasAnyRole(DOCTOR, ADMIN) // 药房接口药师和 admin 可访问 .antMatchers(/pharmacy/**).hasAnyRole(PHARMACIST, ADMIN) // 住院接口医生、护士、admin 可访问 .antMatchers(/inpatient/**).hasAnyRole(DOCTOR, NURSE, ADMIN) .anyRequest().authenticated() .and() .formLogin().loginPage(/login).permitAll() .and() .logout().permitAll(); } }这段配置的意思是不同角色只能访问自己业务范围内的接口。挂号员看不到药房库存药师看不到门诊病历护士不能开医嘱。这就是「最小权限原则」的落地。5. 避坑与常见问题排查从启动报错到业务数据不一致这一章是我自己踩过的坑也是读者最可能遇到的。每条按「现象 → 原因 → 解决」写不绕弯子。5.1 启动报错Seata 相关类找不到或连接超时现象项目启动时控制台报io.seata.server.coordinator.AbstractCore相关异常或者卡在Seata初始化阶段不动最后超时退出。原因源码里引入了 Seata 依赖但本地没有启动 Seata Server或者application.yml里的seata.tx-service-group配置和 Server 端不一致。课程设计场景下分布式事务基本用不到这个依赖属于「带了但没启用」的状态。解决在application.yml里把 Seata 相关配置注释掉或者在启动类上排除 Seata 自动配置。如果 pom.xml 里有seata-spring-boot-starter可以暂时把依赖注释掉重新mvn clean package。更稳妥的做法是保留依赖但设置seata.enabledfalse。seata: enabled: false # 其他配置全部注释5.2 数据库连接失败时区错误或字符集不匹配现象启动时报The server time zone value xxx is unrecognized或者中文姓名显示成???。原因MySQL 8.0 驱动要求显式指定serverTimezone不指定就报错。字符集问题则是建库时用了utf8而不是utf8mb4导致 emoji 或者某些生僻字存不进去。解决连接串加上serverTimezoneAsia/Shanghai建库语句用DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci。已经建好的库可以改ALTER DATABASE hospital_db CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;表级别也要改ALTER TABLE patient CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;5.3 挂号超卖剩余号源变成负数现象压测或者多人同时挂号时remaining_quota出现负数或者同一时段挂出去的人数超过total_quota。原因扣减号源的 SQL 没有加AND remaining_quota 0条件或者没有用乐观锁/悲观锁两个线程同时读到remaining_quota 1然后都扣成功。解决扣减 SQL 必须带AND remaining_quota 0并且用version字段做乐观锁。如果并发量确实高可以改用 Redis 预扣减但课程设计场景下乐观锁足够了。5.4 处方发药后库存对不上现象药房发药成功但库存表里的数量没有变化或者变化了但和处方明细对不上。原因发药逻辑没有放在同一个事务里或者扣库存的 SQL 没有带AND quantity #{quantity}条件导致扣成负数后事务没有回滚。解决发药方法加Transactional(rollbackFor Exception.class)扣库存 SQL 带条件判断affected 0时主动抛异常触发回滚。另外检查一下drug_stock表是不是有多个仓库的记录发药时可能扣错了仓库。5.5 权限配置不生效所有接口都能匿名访问现象配置了 Spring Security但未登录状态下访问/registration/list居然能返回数据。原因WebSecurityConfigurerAdapter的configure(HttpSecurity)方法里antMatchers的顺序写错了或者anyRequest().authenticated()被前面的permitAll()覆盖了。Spring Security 的匹配规则是从上到下一旦匹配到就不再往下走。解决把最具体的规则放在最前面anyRequest().authenticated()放在最后。另外检查一下是不是在configure(WebSecurity)里忽略了太多路径。// 正确顺序具体规则在前通用规则在后 http.authorizeRequests() .antMatchers(/login, /css/**, /js/**).permitAll() .antMatchers(/registration/**).hasAnyRole(REGISTRAR, ADMIN) .antMatchers(/outpatient/**).hasAnyRole(DOCTOR, ADMIN) .anyRequest().authenticated();6. 进阶技巧用 MyBatis-Plus 代码生成器快速扩展新模块这套系统跑通之后你可能会想加新功能比如「体检管理」或者「疫苗预约」。如果一个个手写 Entity、Mapper、Service、Controller效率太低。MyBatis-Plus 的代码生成器可以一键生成整套 CRUD 代码改改就能用。6.1 配置代码生成器在pom.xml里加上 MyBatis-Plus 生成器依赖dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-generator/artifactId version3.5.1/version /dependency dependency groupIdorg.apache.velocity/groupId artifactIdvelocity-engine-core/artifactId version2.3/version /dependency然后写一个生成器类public class CodeGenerator { public static void main(String[] args) { // 数据源配置 DataSourceConfig dsc new DataSourceConfig(); dsc.setUrl(jdbc:mysql://localhost:3306/hospital_db?serverTimezoneAsia/Shanghai); dsc.setUsername(root); dsc.setPassword(你的密码); dsc.setDriverName(com.mysql.cj.jdbc.Driver); // 全局配置 GlobalConfig gc new GlobalConfig(); gc.setOutputDir(System.getProperty(user.dir) /src/main/java); gc.setAuthor(yourname); gc.setOpen(false); gc.setFileOverride(true); // 覆盖已有文件谨慎使用 gc.setServiceName(%sService); // 去掉 Service 前面的 I 前缀 // 包配置 PackageConfig pc new PackageConfig(); pc.setParent(com.hospital); pc.setModuleName(checkup); // 新模块名 pc.setEntity(entity); pc.setMapper(mapper); pc.setService(service); pc.setController(controller); // 策略配置 StrategyConfig strategy new StrategyConfig(); strategy.setInclude(checkup_record, checkup_item); // 要生成的表名 strategy.setNaming(NamingStrategy.underline_to_camel); strategy.setColumnNaming(NamingStrategy.underline_to_camel); strategy.setEntityLombokModel(true); strategy.setRestControllerStyle(true); // 执行生成 new AutoGenerator() .setDataSource(dsc) .setGlobalConfig(gc) .setPackageInfo(pc) .setStrategy(strategy) .execute(); } }运行这个main方法checkup_record和checkup_item两张表对应的 Entity、Mapper、Service、Controller 就全生成好了。生成的代码是标准 CRUD分页查询、条件查询都有改改业务逻辑就能用。6.2 生成后的调整要点生成的代码不能直接上线有几个地方要改。第一Controller 里的权限注解要补上比如PreAuthorize(hasRole(ADMIN))。第二Service 里的业务校验要加比如新增体检记录时校验患者是否存在。第三Mapper XML 里的 SQL 要检查一下尤其是关联查询生成器默认只生成单表操作。// 生成后的 Controller 示例需要手动补权限注解 RestController RequestMapping(/checkup/record) public class CheckupRecordController { Autowired private CheckupRecordService checkupRecordService; PreAuthorize(hasAnyRole(DOCTOR, ADMIN)) GetMapping(/page) public ResultPageCheckupRecord page(PageParam param) { return Result.ok(checkupRecordService.page(param)); } PreAuthorize(hasRole(ADMIN)) PostMapping(/save) public ResultVoid save(RequestBody CheckupRecord record) { checkupRecordService.save(record); return Result.ok(); } }6.3 验证新模块是否接入成功生成并调整完之后重启项目访问http://localhost:8080/checkup/record/page如果返回分页数据哪怕是空的说明模块接入成功。然后用不同角色登录验证权限是否生效医生能看列表但不能新增管理员既能看也能增。验证项预期结果检查方式接口可访问返回 JSON 数据浏览器或 Postman 直接访问权限拦截无权限角色返回 403用普通用户 token 访问管理员接口分页正常返回 total 和 records传 pageNum 和 pageSize 参数新增生效数据库多一条记录调 save 接口后查库这套代码生成器的用法我从第一次接触到现在每次开新模块都强制走一遍。生成完先跑通再改业务比从零手写快至少三倍。希望帮到你。本文还有配套的精品资源点击获取