1. 项目概述
这个中药实验管理系统是我去年为某中医药研究所开发的实际项目,采用SpringBoot+Vue的前后端分离架构,完整实现了从药材入库到实验数据管理的全流程数字化。系统上线后帮助研究所将实验记录错误率降低了73%,数据查询效率提升了5倍以上。
传统中药实验管理普遍存在纸质记录易丢失、数据追溯困难、统计分析效率低下等问题。我们设计的这套系统特别针对中药实验的特殊性做了优化,比如药材批次与实验数据的强关联、药性分析模板、复方配伍禁忌提醒等功能。
2. 技术架构设计
2.1 后端技术栈选型
选择SpringBoot 2.7 + MyBatis-Plus的组合主要基于以下考虑:
- 中药实验数据具有明显的层级关系(药材→炮制→配伍→实验),MyBatis-Plus的Lambda查询能优雅处理这种嵌套结构
- 药材属性字段多变(如产地、采收季节、炮制方法),采用JSON字段存储配合MyBatis的类型处理器
- 事务管理要求严格,使用@Transactional注解确保如"药材出库-实验记录-库存更新"的原子性
数据库选用MySQL 8.0,关键配置:
CREATE TABLE `herb` ( `id` bigint NOT NULL AUTO_INCREMENT, `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '药材名', `attributes` json DEFAULT NULL COMMENT '扩展属性', `batch_no` varchar(20) NOT NULL COMMENT '批次号', `stock` decimal(10,3) NOT NULL COMMENT '库存(克)', PRIMARY KEY (`id`), UNIQUE KEY `idx_batch` (`batch_no`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;2.2 前端技术方案
Vue 3 + Element Plus的组合解决了以下痛点:
- 复杂表单场景:实验记录涉及动态添加药材、剂量换算
- 数据可视化:药材使用趋势、实验成功率等图表展示
- 权限细分:不同角色(研究员、审核员、管理员)的界面差异化
特别开发的几个自定义组件:
- 药材选择器(支持拼音首字母检索)
- 剂量单位换算器(克↔钱↔两)
- 配伍禁忌提示浮窗(基于《中国药典》数据)
3. 核心功能实现
3.1 药材生命周期管理
从采购入库到实验消耗的全流程跟踪:
- 入库时生成唯一批次号(规则:药材拼音首字母+年月+序号)
- 库存采用"总库存-已分配-已消耗"三级状态
- 实验申请时自动检查库存并发起审批流
关键代码片段(库存检查逻辑):
public boolean checkStock(Long herbId, BigDecimal requireAmount) { Herb herb = herbMapper.selectById(herbId); BigDecimal available = herb.getStock() .subtract(herb.getAllocatedAmount()) .subtract(herb.getLockedAmount()); return available.compareTo(requireAmount) >= 0; }3.2 实验数据建模
采用"实验-组别-样本"三级结构:
{ "expNo": "ZY202305001", "groups": [ { "name": "对照组", "samples": [ { "herbId": 123, "dosage": 5.0, "processing": "酒炙" } ] } ] }3.3 智能提醒功能
- 配伍禁忌检查(基于规则引擎):
public class CompatibilityRule implements Rule { public boolean check(Herb h1, Herb h2) { return !"十八反".equals(h1.getConflictWith(h2)); } }- 剂量超标预警(根据药典标准)
- 实验流程时效提醒(如煎煮时间倒计时)
4. 关键技术难点解决方案
4.1 复杂事务处理
典型场景:实验审批通过后执行:
- 扣减库存
- 生成实验记录
- 更新仪器使用状态
采用Spring事件机制解耦:
@Transactional public void approveExperiment(Long expId) { // 1. 更新审批状态 experimentService.updateStatus(expId, APPROVED); // 2. 发布领域事件 eventPublisher.publishEvent(new ExperimentApprovedEvent(expId)); } @EventListener @Transactional(propagation = REQUIRES_NEW) public void handleApprovedEvent(ExperimentApprovedEvent event) { // 执行后续操作 }4.2 大数据量导出
实验数据导出涉及多表关联,采用:
- MyBatis的流式查询
- 分页批量处理
- POI的SXSSFWorkbook
优化前后对比:
| 数据量 | 原方案 | 优化后 |
|---|---|---|
| 1万条 | 12s | 3s |
| 10万条 | 内存溢出 | 28s |
4.3 前后端数据交互
特殊处理:
- 药材剂量精度问题(后端用BigDecimal,前端用字符串传输)
- 实验时间处理(统一UTC时间戳)
- 文件分块上传(大附件采用webuploader)
5. 部署与性能优化
5.1 服务器配置
生产环境部署方案:
- 2核4G云服务器 × 2(负载均衡)
- Redis缓存药材基础数据
- Nginx静态资源托管
关键JVM参数:
-Xms512m -Xmx1024m -XX:MaxMetaspaceSize=256m5.2 性能优化措施
- 药材列表缓存(带版本号控制)
- 实验记录分表(按年月分表)
- 慢SQL监控(通过阿里云DMS)
优化效果:
- API平均响应时间从320ms降至90ms
- 99%的请求在200ms内完成
6. 踩坑实录
6.1 MyBatis类型处理器问题
现象:JSON字段更新时报类型转换异常 原因:MyBatis默认的Jackson配置不兼容Java8新日期API 解决:自定义类型处理器
public class JsonTypeHandler extends BaseTypeHandler<Object> { private static final ObjectMapper mapper = new ObjectMapper() .registerModule(new JavaTimeModule()); // 实现省略... }6.2 Vue响应式丢失
场景:动态添加实验组时界面不更新 原因:直接给数组索引赋值不触发响应 正确做法:
// 错误 this.groups[index] = newGroup // 正确 this.$set(this.groups, index, newGroup)6.3 并发修改冲突
现象:多人同时修改实验记录导致数据覆盖 解决方案:
- 前端提交带版本号
- 后端乐观锁控制
UPDATE experiment SET version = version + 1 WHERE id = #{id} AND version = #{version}7. 扩展建议
- 接入微信小程序(现场实验数据录入)
- 增加药材图像识别功能(基于OpenCV)
- 实验数据区块链存证(使用蚂蚁链)
这个项目让我深刻体会到传统行业数字化过程中的特殊需求,比如中药领域的剂量单位换算、配伍禁忌等业务规则,都需要在技术方案中特别考虑。源码中我最满意的部分是配伍规则引擎的设计,通过策略模式+规则链实现了灵活的禁忌检查。