ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

社区医院管理系统实战:SpringBoot+Vue+MyBatis+MySQL全栈开发与优化

社区医院管理系统实战:SpringBoot+Vue+MyBatis+MySQL全栈开发与优化 1. 项目整体设计与技术选型拆解1.1 为什么是SpringBootVueMyBatisMySQL这个组合一套社区医院管理系统从立项到上线前后花了大概半年。说它是“企业级”并不是说用了多复杂的中间件。恰恰相反SpringBootVueMyBatisMySQL这套组合能扛住社区医院每天上千次的挂号、缴费、取药操作已经足够实用。项目覆盖患者建档、分诊挂号、门诊医生站、收费、药房管理、住院登记等完整流程也是不少同学毕业设计或者入行医疗信息化的首选样例。先说后端选型。SpringBoot是目前Java生态里启动成本最低、生态最成熟的框架。社区医院这类场景的特点是业务流程复杂、技术并发量不大但对数据准确性要求极高。用SpringBoot能快速把接口拆出来配合强大的starter体系集成MyBatis、拦截器、分页插件、定时任务都很顺手。如果换成微服务架构反而会因为服务拆分、注册中心、链路追踪这些额外组件增加部署成本对一家社区医院的信息科来说并不友好。前端选Vue核心原因是组件化开发和渐进式引入。项目管理端、医生工作台、药房窗口、收费窗口都有各自的交互逻辑如果用传统JSP加jQuery强撸页面一多维护成本就上来了。Vue的响应式数据绑定让表单、弹窗、动态表格开发效率明显提升。配合Vue Router做页面路由Vuex或Pinia做状态管理整个前端工程可以按模块拆分成独立的目录。MyBatis在这个项目里的地位比很多框架选择更关键。医疗系统里有大量动态查询条件比如按医生、按日期区间、按科室、按病情关键字复合筛选挂号记录。MyBatis的where、if动态SQL能让这类查询写得很优雅而且SQL是手写的运行效率完全可控。相比之下JPA在复杂统计和报表场景下要么得写JPQL要么容易生成低效SQL反而不如直接手写。MySQL则是没有什么悬念的选择。社区医院的数据量级单库单表就能扛住MySQL 8.0的窗口函数、公共表表达式对统计报表支持很完善。加上MySQL安装、维护、备份的生态资料极多招人也容易是性价比最高的数据存储方案。这套组合选下来技术上没有炫技但每一层的选型都踩在“够用、稳定、好维护”这个点上。1.2 核心业务模块与角色权限设计社区医院管理系统和我之前做过的通用后台管理系统有一个明显差别它的业务模块是强耦合的。患者挂了号才能看诊医生开了处方才能去药房拿药收费完成才能做检查检验住院登记之后才有床位分配。模块之间是一条完整的业务链所以设计时一定是先梳理流程、再拆分模块。这个系统里我拆出了八个核心模块模块核心职责关联业务患者管理建档、信息修改、历史就诊记录查询所有业务的基础数据分诊挂号号源生成、挂号、退号、排队叫号关联门诊、收费门诊医生站接诊、病历书写、开具处方关联药房、收费收费管理挂号费、药费、检查费结算关联挂号、处方、检查药房管理库存维护、发药、退药、盘点关联处方、收费住院管理入院登记、床位分配、医嘱管理关联病区、收费统计报表门诊量、收入、科室绩效统计汇总各业务数据系统管理用户、角色、菜单、字典管理全局支撑角色权限设计上我直接采用RBAC模型也就是“用户-角色-权限”三层结构。社区医院麻雀虽小但在权限上必须严谨。比如收费员只能查看收费界面不能打开药品库存编辑医生可以写病历、开处方但不能改药品单价。用药房的登录账号不小心进了门诊医生站这在真实的医疗环境里是绝对不能发生的。后端我用SpringBoot拦截器加自定义注解做接口权限校验每个接口在Controller层标注需要的角色码拦截器统一校验当前登录用户的角色权限。前端则根据登录返回的角色列表动态生成可访问的菜单路由没权限的页面根本不渲染。这两层权限校验各管一段后端是保障数据安全的底线前端更多是提升操作体验。做完之后我才意识到权限设计这种事宁可一开始多花两天建模也不要上线后补丁式打补丁。2. 数据库设计从表结构到索引的实战经验2.1 核心表结构设计数据库是整个系统的地基表结构没设计好后面所有功能都会跟着别扭。我按业务链拆成了几个主题域基础数据患者、医生、科室、药品、业务数据挂号、处方、收费、住院、发药、系统数据用户、角色、菜单、字典。患者表是最基础的一张表字段上除了姓名、性别、身份证号、手机号这些常规信息还加了medical_record_no作为院内病历号这个号在挂号、处方、收费里都要频繁使用。身份证号设了唯一索引防止同一个人反复建档。体检里身份证号是天然的业务主键用它在数据库层面做防重最可靠。挂号表是整个系统的核心表之一字段包括patient_id、doctor_id、dept_id、visit_date、time_period上午、下午、晚间、registration_fee、status已挂号、已就诊、已退号。这里有个容易踩坑的点挂号费、药品金额、检查费这些涉及钱的字段一律用DECIMAL(10,2)不要用FLOAT和DOUBLE。浮点数在累计求和时会出现精度误差比如0.1加0.2变成0.30000000000000004在收费这种场景里属于重大事故。处方表和处方明细表是典型的一对多关系。处方主表存医生ID、患者ID、开单时间、总金额处方明细表存每一条药品的药品ID、药品名、单价、数量、用法用量。设计时我特意没有在明细表里只存drug_id而是冗余了drug_name和unit_price字段。为什么因为药品价格会调整药品名称可能被修改如果全部关联实时查药品表历史处方单子在打印时就可能显示现在的价格这在医疗纠纷中是巨大的隐患。冗余字段保存的是开单时刻的快照这个设计思路在报表和历史追溯场景里很管用。药品表、药房库存表、药品出入库流水表又是另一组关键设计。药品表维护基础信息比如通用名、商品名、规格、生产企业、零售价库存表记录了当前所在药房的实时库存出入库流水表则记录每一次入库、出库、盘点、报损。任何一次库存变动都写流水保证每一盒药品都能追溯到来源。数据表还加了del_flag逻辑删除字段所有删除操作都做成软删除避免误删后无法恢复。2.2 索引设计不用物理外键但必须有对应索引很多刚入行的同学在画ER图的时候都习惯加物理外键比如订单表里加一个FOREIGN KEY指向患者表。在这个项目里我明确建议正式环境不用物理外键改用逻辑关联加索引。物理外键的痛点在于插入和删除时要额外检查关联表的完整性在业务高峰期会产生很多不必要的锁竞争。比如挂号表和号源表如果存在物理外键患者集中挂号时数据库每次都要去检查号源ID是否存在一旦碰到大事务非常容易死锁。社区医院虽然没有电商那么大的并发但上午开诊时段集中挂号也经常有几十个请求同时进来没必要让数据库在这种地方浪费性能。不用物理外键不代表不建索引。相反我在所有关联字段上都手动建了索引。挂号表的patient_id、doctor_id、visit_date建了联合索引(doctor_id, visit_date)因为系统里最常见的查询是“某个医生某一天有多少号源、看了多少患者”。处方明细表里的prescription_id建了索引查询某一单处方时不会全表扫描。药品库存表对drug_id建了唯一索引防止同一种药品在同一个库存表里出现两条记录。这里说一个实际优化的例子。上线跑了一周后发现统计报表模块里“按科室查询最近一个月的门诊量”这个接口总是慢慢查询日志显示要扫二十多万行。EXPLAIN一看表里为了排序使用了文件排序没有合适的联合索引。后来我在挂号表上加了(dept_id, visit_date, status)联合索引查询效率直接从1.8秒降到了80毫秒。所以说索引不是建得越多越好而是要根据实际查询场景去匹配。最忌讳的是每个字段都建索引写入时索引维护成本反而拖垮性能。2.3 金额、时间和状态字段的规范数据库设计里最琐碎但也最影响开发效率的就是各种字段的规范统一。这个项目里我定了三条规矩写死在开发文档里金额全部用DECIMAL(10,2)、时间统一用DATETIME、状态字段全部用TINYINT并配上状态字典表。时间字段为什么要特别强调因为我在联调阶段就被MySQL的时区问题坑过一次。后端服务器和数据库服务器的系统时区不一致导致插入的create_time和实际时间差了8个小时。后来在连接串上强制指定了serverTimezoneAsia/Shanghai再把所有时间字段的默认值统一设为CURRENT_TIMESTAMP这个问题才算彻底根治。后来写任何表的建表语句我都会顺手把create_time和update_time加上并给update_time配上ON UPDATE CURRENT_TIMESTAMP这样更新时间根本不用Java代码里面手动维护。状态字段用TINYINT是为了稳定。比如挂号状态我约定0表示已挂号、1表示已就诊、2表示已退号、3表示爽约这些映射关系统一维护在系统的字典表里前端通过接口读取字典项渲染成中文标签。有些人喜欢直接存pending、finished这样的字符串看着直观但存字符串的查询效率和存储空间都不如数字类型而且字符串一旦拼写不统一数据就乱了。数字状态码加字典表既能保证性能又能保证显示可配置。3. 后端实现SpringBoot分层与MyBatis实战记录3.1 项目分层和统一响应体后端我采用经典的四层结构Controller层负责接收和校验参数Service层处理业务逻辑Mapper层操作数据库Entity/DTO/VO三个数据模型各司其职。很多同学做项目时喜欢把Entity直接返回给前端这是一个容易被忽视的问题。数据库实体可能包含密码、内部备注、逻辑删除标记这些敏感或多余字段直接暴露给前端既不安全也不专业。DTO是接口的入参模型VO是接口的出参模型。比如挂号接口前端传过来的是一个包含patientId、doctorId、visitDate、timePeriod的DTO后台处理完返回的是一个包含挂号单号、医生姓名、科室名称、排队序号的VO。这样Service层在做对象转换时顺便把关联表查询到的医生姓名、科室名称填充进去前端拿到的是“可以直接展示的数据”而不是需要前端自己再去拼接的裸数据。统一响应体也是后端项目中容易被忽略的设计。所有接口的返回格式都统一为{code: 200, message: success, data: {...}}这种结构前端axios拦截器统一判断code非200时直接弹出错误提示。这样做的最大好处是前端不用在每个接口调用处都写一遍错误处理逻辑。最开始时我偷懒部分接口直接返回了一个Map前端联调时发现格式不统一被迫写了大量兼容代码后来花了半天时间全部重构到统一响应体立刻清爽了。前端所有接口方法写起来都是同一个套路代码量减少约三分之一。3.2 MyBatis动态SQL与事务管理MyBatis在这个项目里最出彩的地方是动态SQL。以挂号记录查询为例现实中用户很少只按一个条件筛选更多时候是“时间区间医生科室状态”组合查询。用MyBatis的where加if标签可以只写一个SQL就支持七八种筛选组合代码可读性还很高。select idlistRegistration resultTypecom.hms.vo.RegistrationVO SELECT r.id, r.registration_no, p.name AS patient_name, r.visit_date, r.time_period, r.status, d.dept_name FROM registration r LEFT JOIN patient p ON r.patient_id p.id LEFT JOIN sys_dept d ON r.dept_id d.id where if testdeptId ! null AND r.dept_id #{deptId} /if if testdoctorId ! null AND r.doctor_id #{doctorId} /if if teststartDate ! null and endDate ! null AND r.visit_date BETWEEN #{startDate} AND #{endDate} /if if teststatus ! null AND r.status #{status} /if /where ORDER BY r.visit_date DESC, r.id DESC /select事务管理是这个项目里另一个必须重点看的设计。挂号、开处方、扣库存这类操作必须保证原子性。比如患者挂号的同时号源余量要减一同时还要生成一条收费流水如果其中任何一步失败整个操作都不能生效。我就在这里踩过坑最初挂号成功了但生成流水的时候因为参数传递错误抛了异常导致号源减了但费用没记上患者拿着挂号单去缴费时收费员一脸懵。解决办法是在Service层的方法上加Transactional注解把挂号、更新号源、生成流水三个操作放进同一个事务。除此之外我在ServiceImpl里还特别注意了代理方法失效的问题Transactional只对通过Spring代理调用外部方法生效如果同一个类里面的A方法调用了内部B方法B上的Transactional不会生效。所以我把事务性的操作都拆到独立的Service类里避免内部自调用导致事务失效。3.3 MyBatis缓存与TypeHandler实际应用MyBatis的缓存机制是很多人在面试里背得滚瓜烂熟、但在实际项目里又经常搞混的点。一级缓存默认开启作用范围是同一个SqlSession也就是一次请求中多次执行完全相同查询会命中缓存。二级缓存是跨SqlSession的但如果你用的是SpringBoot集成的方式默认情况下二级缓存往往没有真正开启或者需要手动设置。我在这个系统里做了一个决定管理端和医生工作台这类要求数据实时性高的模块保持默认关闭二级缓存。为什么因为社区的挂号、库存、收费都是强实时业务如果缓存了某条挂号数据另一个收费窗口立刻取消了它旧缓存里的数据还在就非常尴尬。MyBatis的二级缓存本身也不是分布式缓存对集群部署并不友好。与其小心翼翼地配置刷新时机不如把缓存用在刀刃上比如字典数据、科室列表这样几乎不变的数据直接在Service层用本地缓存框架来管更可控。TypeHandler在MyBatis里算是一个进阶话题但在医疗项目里它处理状态字段特别好用。比如挂号状态是TINYINT我不想在Java代码里到处写if (status 0)这种魔法数字就可以定义一个StatusTypeHandler在Java对象中使用枚举类型存数据库时自动转换成数字读出来后自动转换成枚举。配置方式也简单在application.yml里指定type-handlers-package然后在枚举字段上加上EnumTypeHandler注解即可。前几次写的时候有点绕但用顺了之后业务代码里再没有一个裸数字状态读代码的体验大幅提升。4. 前端实现Vue工程搭建与前后端联调4.1 环境配置与工程目录前端我基于Vue 2 Vue CLI搭建为什么不选Vue 3这里不是保守主要考虑到社区医院已经有现成的Vue 2生态组件比如老牌的Element UI对表格、表单、弹窗这种后台管理场景非常成熟。如果换成Vue 3加Element Plus思路差不多但团队上手有个过渡期。项目从零开始的时候我建议先跑通主流程再根据团队情况决定要不要升级。环境配置上Node.js版本建议用14或16Vue CLI 5.x对Node版本有一定要求。第一次创建项目时我直接用了vue create命令行选择Manually select features勾选了Router和Vuex。这里有个实际提醒国内网络环境下npm install非常慢甚至经常报错ERR_SOCKET_TIMEOUT我自己是在项目根目录创建.npmrc文件并配置了淘宝镜像源才顺利装完依赖。镜像配置后安装依赖速度能从五六分钟缩短到几十秒。工程目录我是这样组织的src/api按模块存放请求方法src/router存放路由表src/store放Vuex状态src/views放页面组件。src/api这个目录很多人会忽略觉得一个页面里直接axios.get也方便但系统功能一多之后就明白了页面里散落着大量请求代码后端接口一旦变动改起来极其痛苦。统一封装以后每个接口都在api目录里有一个方法函数名就是接口含义改动只需要维护一个文件。4.2 登录态管理与角色动态路由前端路由这块我踩过最多的坑是权限路由。直接写死的静态路由很简单但问题是所有角色都能看到全部菜单收费员也能点进药房管理页面。后来我用动态路由方案登录成功后后端返回当前用户的角色列表和可访问的菜单编码前端根据菜单编码动态生成路由表用router.addRoutes挂载到Vue Router上。router.beforeEach(async (to, from, next) { const token getToken() if (!token) { if (to.path /login) { next() } else { next(/login) } return } const hasRoutes store.getters.hasRoutes if (!hasRoutes) { const userInfo await store.dispatch(user/getInfo) const accessRoutes await store.dispatch(permission/generateRoutes, userInfo.roles) router.addRoutes(accessRoutes) next({ ...to, replace: true }) } else { next() } })这里有一个反复出现的坑刷新页面时Vuex里的数据全部清空动态路由也会丢失。如果不做处理刷新后就会出现白屏或者404。我当时的处理方案是上面这段代码的路由守卫逻辑刷新后进入beforeEach发现store里没有已经生成路由的标记就重新请求用户信息、重新生成路由再通过next({ ...to, replace: true })重新进入目标路由这样就能保证刷新后页面仍然正常。路由参数这个点也提一下。从挂号列表页点击某一条记录跳转到详情页时我会用this.$router.push({ name: RegistrationDetail, query: { id: row.id } })传递患者ID。也有同学习惯用动态路径参数/registration/:id两者都行。用query的优势是参数在URL里可见刷新也不会丢用params的路径方式则更美观但要注意如果配置了params参数跳转时漏传会导致页面异常。我在这个项目里统一用query传ID简单直接问题最少。4.3 打包部署与SpringBoot集成前端开发时通过proxy代理解决跨域问题。在vue.config.js里配置devServer.proxy把/api开头的请求转发到http://localhost:8080SpringBoot服务上。但上线部署时不能再依赖node服务我采用了两种方案各有适用场景。第一种是最省事的前端npm run build生成静态文件复制到SpringBoot项目的src/main/resources/static目录下再通过SpringBoot直接访问。这种方式把前端页面当成静态资源托管只占用一个端口适合一台服务器搞定所有服务的场景。但要注意如果使用Vue Router的history模式直接访问http://ip/registration这样的路径时后端没有对应的Controller会返回404。我的解决方法是使用hash模式URL上带着#无论如何刷新后端始终加载index.html虽然URL不太好看但稳定性优先。第二种是经典的前后端分离部署前端静态文件放Nginx后端SpringBoot跑在某个Tomcat端口通过Nginx反向代理/api请求到后端。这种方案的好处是前端页面和后端服务可以独立升级负载能力更强。但对社区医院这种内部系统来说Nginx本身也是一个额外的运维节点需要有人会维护配置。所以我最终的推荐是如果图省心用第一种如果后续有公网访问或者多前端入口再上Nginx。5. 核心业务场景的落地细节5.1 挂号号源控制与并发处理社区医院上午八点到十点是挂号高峰虽然没有电商那种万人抢购的场面但几十个患者窗口同时挂号还是存在的。号源控制是整个系统里对数据一致性要求最高的场景之一一个号源既不能被两个患者同时挂上又不能因为并发高就拒绝服务。最直观的思路是在挂号逻辑里先查询号源剩余数判断大于零就执行减一操作。但这里有一个经典并发问题两个请求同时读到剩余数为1都判断可以挂号然后都执行减一最终号源变成负数两个患者都拿到了同一个号。这个问题叫超卖。我在这个系统里用了两种手段兜底。第一种是数据库乐观锁号源表设计时加一个version字段更新语句带上WHERE version #{version}如果更新影响行数为0说明有其他人改过了重新读取再重试。第二种是数据库层面的唯一约束把doctor_id visit_date time_period visit_no设计成唯一索引哪怕是极端并发下数据库也会强制只允许一条记录插入成功。这两层保护组合起来号源数据基本上是铁板一块。5.2 药品处方扣减库存的实现逻辑药房发药是另一个关键场景。医生开了处方患者缴费后到药房窗口取药药房管理员点击发药后台要做两件事扣除库存、生成出库流水。这里的难点在于药品库存和处方明细必须保持强一致不可能出现处方已开但药房库存不足的情况。我的实现逻辑是发药时先查询处方明细逐条判断对应药品库存是否充足如果某条不满足整个发药操作要全部回滚并且提示药房人员哪一味药库存不足。代码上使用SELECT ... FOR UPDATE对库存行加锁保证库存判断和扣减操作之间不会有其他请求插入。当时考虑过不加锁、直接UPDATE drug_stock SET stock stock - #{num} WHERE drug_id #{id} AND stock #{num}这种原子操作判断影响行数是否为1如果不为1再整体回滚也是一种方案。但我为了打印清晰的库存不足提示采用了先锁定再判断的方式逻辑更直白。还有一个容易被忽略的细节处方状态流转。发药完成后处方状态要更新为“已发药”收费记录里对应的处方状态也要同步更新。这些操作都在同一个事务里完成任何一个环节失败药品库存都不会扣减避免出现患者交了钱却拿不到药或者在系统里药已发出但库存没减的尴尬状态。5.3 统计报表中的SQL优化思路社区医院也要定期汇报门诊量、收入、科室绩效。系统里我做了三个核心报表接口按日门诊量统计、按科室收入统计、按医生接诊量排名。这三个接口的本质都是对业务表做聚合查询。按科室收入统计的SQL大概长这样SELECT d.dept_name, COUNT(DISTINCT r.id) AS visit_count, SUM(s.total_amount) AS total_income FROM registration r LEFT JOIN sys_dept d ON r.dept_id d.id LEFT JOIN settlement s ON r.id s.registration_id WHERE r.visit_date BETWEEN #{startDate} AND #{endDate} GROUP BY d.id, d.dept_name ORDER BY total_income DESC刚开始这个接口在导出月度报表时非常慢排查后发现问题出在settlement表按registration_id关联时没有索引。后来补建设了registration_id索引情况好了很多。另一个优化点是避免在WHERE条件中对索引字段做函数运算。比如把WHERE MONTH(r.visit_date) 6改成WHERE r.visit_date 2025-06-01 AND r.visit_date 2025-07-01后者才能用上联合索引。这里我也分享一个报表优化的通用原则报表查询往往需要跨表关联和聚合计算但这类查询如果每次都实时扫大表性能一定越来越差。比较好的做法是每天晚上定时任务把前一天的数据汇总到统计表中报表查询只读取汇总结果速度会有质的提升。我在系统二期就加入了这种汇总表机制后台的月度统计接口从两秒多降到一百毫秒以内患者和领导都觉得系统“变快了”。6. 常见问题与排查记录6.1 MySQL 8.0连接报SSL错误与安装配置新装MySQL 8.0后SpringBoot启动时大概率会遇到SSL connection error或Public Key Retrieval is not allowed这样的报错。原因是MySQL 8.0默认开启了SSL和caching_sha2_password认证插件而较老的JDBC驱动或默认连接参数没有正确适配。我在application.yml里的处理方式是spring: datasource: url: jdbc:mysql://localhost:3306/community_hospital?useUnicodetruecharacterEncodingutf8useSSLfalseallowPublicKeyRetrievaltrueserverTimezoneAsia/Shanghai username: root password: your_password driver-class-name: com.mysql.cj.jdbc.DriveruseSSLfalse表示放弃SSL连接内网环境没有必要为了合规而增加加密开销allowPublicKeyRetrievaltrue允许客户端从服务端获取公钥是配合caching_sha2_password认证方式的。如果用的是8.0之后的版本而且安全要求高也可以在服务器端创建使用mysql_native_password插件的用户但要注意新版本对旧插件支持在收紧不能盲目照抄老教程。另外serverTimezone一定要加否则时间字段容易差8小时。6.2 SpringBoot版本太高导致的依赖冲突这个项目的初期我直接用当时最新的SpringBoot版本搭建骨架结果MyBatis启动时的包路径报错找了一圈才发现是新版本用了jakarta.*命名空间而老版本的MyBatis依赖还在找javax.*的类。这个问题的本质是SpringBoot 3.x完成了从Java EE到Jakarta EE的包迁移很多老第三方库没有及时适配。对于社区医院管理系统这种偏业务、偏稳定的项目我明确建议用SpringBoot 2.7.x系列配合匹配的MyBatis Spring Boot Starter版本。2.7.x是SpringBoot 2.x的最后一个稳定分支既保留了javax命名空间又能兼容JDK 8到17稳定性经过了长时间验证。没有必要盲目追新框架版本的选择要服务于业务稳定性。如果你已经用了高版本排查方向就是检查所有第三方依赖的包名是javax还是jakarta逐一替换相关依赖的版本。6.3 Vue打包后刷新404与静态资源路径问题Vue项目打包后放进SpringBoot里经常会遇到两种情况一是刷新页面后404二是CSS和JS加载路径不对导致白屏。404的问题在上面已经提到使用hash模式即可解决。资源路径问题则需要在vue.config.js里设置publicPath。module.exports { publicPath: process.env.NODE_ENV production ? ./ : /, outputDir: dist, assetsDir: static }设置成相对路径以后打包生成的index.html里引用的JS、CSS地址就不会以/开头而是./static/js/xxx.js这样不管部署在什么子路径下资源都能正常加载。这个配置排查起来很迷惑页面有时候是白屏打开控制台才看到一堆Failed to load resource。如果遇到优先检查这两个配置。6.4 高频问题速查表问题现象常见原因解决办法MySQL连接被拒绝端口未开或密码错误检查3306端口用Navicat客户端验证账号MyBatis执行SQL不打印日志级别未配置配置mybatis.configuration.log-implorg.apache.ibatis.logging.stdout.StdOutImpl前端npm install超时网络下载慢配镜像源重试安装请求跨域前后端端口不一致开发环境配置Vue proxy生产环境Nginx或后端静态托管时分秒丢失JSON序列化时间格式问题配置Jackson时间格式或加JsonFormat数据库连接数耗尽连接池配置太小调整HikariCP最大连接数排查大事务阻塞排查这些问题时我的经验是先看日志再看数据库最后怀疑前端。很多看起来是前后端联调的问题实际是后端事务没提交或异常被吞了。如果你发现接口返回正常但数据没变先看Service代码里有没有真正把异常抛出来再看有没有加Transactional和异常回滚配置。用日志把SQL打出来问题基本能定位个八九不离十。整套做下来我个人最深的体会是社区医院管理系统这种项目真正决定成败的不是用了多落伍或多先进的技术而是数据链路是否完整、权限边界是否清晰、状态流转是否闭环。“企业级”三个字在我的理解里不是中间件全家桶的代名词而是每一笔挂号、每一张处方、每一次发药都能正确落库任何一个环节出了故障都能快速定位。最后分享一个从这次开发里沉淀下来的操作习惯建表时就把金额类型、时间默认值、逻辑删除标志、版本号字段全部统一好后面少说能省掉八成以上的返工。这套系统后续如果要扩展我会优先做微信预约挂号和云胶片影像浏览把院内系统延伸到患者端让社区医院的数字化服务更完整一些。
返回列表