1. 项目背景与行业痛点
作为一名经历过多次宠物医院就诊的铲屎官,我深刻体会过传统宠物医疗服务的种种不便。去年我家猫咪突发尿闭,在急诊室排队两小时才见到医生,期间手写病历被护士弄混,药品库存状态全靠人工核对,这种低效体验促使我决定用技术手段改善这个行业。
宠物医疗行业正面临三大核心痛点:
- 信息孤岛严重:病例记录、药品库存、设备状态分散在不同纸质档案和Excel表中,一次简单的疫苗预约需要前台反复打电话确认
- 业务流程割裂:从预约挂号到复诊跟进缺乏系统衔接,医生无法实时查看宠物历史用药记录
- 资源调配低效:美容师排班与设备使用情况不透明,经常出现客户等待时间过长的情况
2. 技术选型与架构设计
2.1 为什么选择Spring Boot
经过对比主流技术方案,最终选择Spring Boot作为基础框架,主要基于以下考量:
- 快速迭代:宠物医院通常没有专职IT团队,需要开箱即用的解决方案。Spring Boot的自动配置特性让部署时间缩短60%
- 微服务友好:采用模块化设计(如图1),未来可拆分医疗服务、电商、CRM等独立服务。实测在4核8G服务器上单个模块启动仅需3.2秒
- 生态完善:整合Spring Security做RBAC权限控制时,配置代码比传统SSM框架减少70%
技术栈全景图:
- 前端:Thymeleaf + Bootstrap 5.2
- 后端:Spring Boot 2.7 + MyBatis-Plus 3.5
- 数据库:MySQL 8.0(配置了主从复制)
- 中间件:Redis 6.2(缓存问诊记录)
2.2 数据库设计精要
针对宠物医疗业务特点,数据库设计着重解决三个问题:
2.2.1 病例关联模型
sql复制CREATE TABLE `pet_case` (
`case_id` BIGINT PRIMARY KEY AUTO_INCREMENT,
`pet_id` BIGINT NOT NULL COMMENT '关联宠物ID',
`symptoms` TEXT NOT NULL COMMENT 'JSON格式存储症状标签',
`diagnosis` VARCHAR(500) DEFAULT NULL,
`prescription` JSON DEFAULT NULL COMMENT '药品及用法用量',
`follow_up` DATE DEFAULT NULL COMMENT '复诊提醒日期',
FULLTEXT INDEX `idx_symptoms` (`symptoms`) -- 支持症状关键词搜索
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
2.2.2 库存事务处理
采用双重校验机制防止超卖:
- 下单时执行
SELECT...FOR UPDATE锁定库存记录 - 支付成功后通过MQ异步扣减库存
- 每日凌晨执行库存校对任务
2.2.3 设备生命周期管理
设计状态机模式跟踪设备流转:
code复制新购 -> 入库 -> 领用 -> 维修 -> 报废
↑______↓
3. 核心功能实现细节
3.1 智能预约调度算法
美容师预约模块采用时间片轮转算法,关键实现逻辑:
java复制public List<TimeSlot> generateTimeSlots(LocalDate date, Integer staffId) {
// 获取已预约时段(Redis缓存优化)
Set<LocalDateTime> bookedSlots = redisTemplate.opsForSet()
.members("appointment:" + staffId + ":" + date);
// 生成可选时段(每30分钟一个间隔)
return Stream.iterate(date.atTime(9, 0), t -> t.plusMinutes(30))
.limit(16) // 9:00-17:00
.filter(t -> !bookedSlots.contains(t))
.map(t -> new TimeSlot(t, t.plusMinutes(30)))
.collect(Collectors.toList());
}
3.2 问诊即时通讯方案
在线问诊采用混合通信模式:
- 轻量级咨询:WebSocket实时对话(消息体<5KB)
- 复杂病例:自动生成临时会话室链接
- 图片传输:先压缩至800×600分辨率再上传OSS
消息存储采用冷热分离策略:
- 7天内对话存Redis
- 历史记录转存MySQL并建立全文索引
3.3 库存动态预警系统
在商品入库时触发库存计算:
java复制@Transactional
public void processStockIn(StockInDTO dto) {
// 1. 更新库存表
inventoryMapper.updateStock(dto.getGoodsId(), dto.getAmount());
// 2. 检查警戒线
Inventory inventory = inventoryMapper.selectById(dto.getGoodsId());
if(inventory.getStock() < inventory.getThreshold()) {
alertService.sendStockAlert(
inventory.getGoodsName(),
inventory.getStock()
);
}
// 3. 记录操作日志(AOP实现)
}
4. 部署优化实践
4.1 性能调优参数
在application.yml中配置的关键参数:
yaml复制server:
tomcat:
max-threads: 200 # 根据压测结果调整
min-spare-threads: 20
spring:
datasource:
hikari:
maximum-pool-size: 30 # 建议为CPU核心数*2 + 磁盘数
connection-timeout: 30000
redis:
lettuce:
pool:
max-active: 50 # 问诊高峰期需要扩容
4.2 高可用方案
采用双活部署架构:
- 阿里云华北+华东双Region部署
- 通过Global Traffic Manager实现DNS级故障转移
- 数据库使用GTID主从同步(延迟<500ms)
5. 踩坑实录与解决方案
5.1 病例图片存储优化
初期直接存储原图导致:
- 数据库体积每周增长1.2GB
- 列表查询延迟高达800ms
解决方案:
- 图片转存OSS,数据库只保留URL
- 采用Thumbnailator生成缩略图
- 建立单独的照片服务微服务
5.2 预约冲突问题
曾出现同一时段被重复预约的BUG,原因是:
- 前端本地时间与服务端不一致
- 并发请求未加锁
最终方案:
- 采用服务器时间作为基准
- 对预约操作添加分布式锁(Redisson实现)
- 增加预约结果短信确认
6. 项目演进方向
当前系统已在3家宠物医院试运行,日均处理:
- 问诊记录120+条
- 商品订单80+笔
- 设备调度30+次
下一步重点优化:
- 接入AI辅助诊断(正在训练宠物症状识别模型)
- 开发微信小程序端(预计Q3上线)
- 实现跨院病例共享(需解决数据权限问题)
这套系统开发过程中最深的体会是:医疗信息化项目必须坚持"业务驱动技术"原则。比如我们最初设计的复杂权限体系,在实际运营中发现反而增加了操作复杂度,后来简化为基于场景的权限模板,培训时间缩短了40%。技术人容易陷入完美主义陷阱,但真正的价值在于解决实际问题。