1. 项目背景与核心需求
医院药品管理系统是医疗机构日常运营中不可或缺的核心业务系统之一。传统的药品管理往往依赖手工记录和纸质单据,存在效率低下、易出错、追溯困难等问题。随着医疗信息化的发展,构建一套高效、安全、可追溯的药品管理系统成为医院现代化管理的刚需。
这个项目采用前后端分离架构,前端使用Vue.js框架构建用户界面,后端采用Node.js+ThinkPHP混合技术栈实现业务逻辑。这种技术选型主要基于以下考虑:
-
药品数据实时性要求高:Node.js的非阻塞I/O特性能够高效处理大量并发请求,确保药房、药库、临床科室之间的数据同步实时可靠。
-
复杂业务逻辑需求:ThinkPHP成熟的MVC架构和丰富的类库支持,能够很好地处理药品采购、入库、调拨、盘点等复杂业务流程。
-
现代化交互体验:Vue.js的组件化开发模式特别适合构建药品管理这类具有大量表单、表格交互的复杂后台系统。
2. 系统架构设计
2.1 技术栈选型分析
前端技术栈:
- Vue 2.x:核心框架
- Element UI:UI组件库
- Axios:HTTP客户端
- Vuex:状态管理
- Vue Router:路由管理
后端技术栈:
- Node.js:基础运行环境
- ThinkPHP 6.0:主要业务框架
- MySQL:主数据库
- Redis:缓存和会话管理
提示:选择ThinkPHP而非纯Node.js方案,主要考虑到医院信息系统对事务完整性的高要求。ThinkPHP的数据库事务机制更为成熟稳定。
2.2 系统模块划分
系统主要分为六大核心模块:
-
基础数据管理
- 药品字典维护
- 供应商管理
- 科室管理
- 用户权限管理
-
药品库存管理
- 入库管理
- 出库管理
- 库存调拨
- 库存盘点
- 效期管理
-
药品采购管理
- 采购计划
- 采购订单
- 采购退货
-
药品调剂管理
- 处方审核
- 配药管理
- 发药管理
-
统计分析报表
- 库存报表
- 收支报表
- 效期报表
- 用药分析
-
系统管理
- 参数配置
- 操作日志
- 数据备份
3. 核心功能实现细节
3.1 药品唯一标识设计
为实现药品全流程追溯,我们设计了复合型药品唯一标识码:
code复制规格ID(4位)+药品ID(6位)+批次号(8位)+效期(6位)
这种编码方案的优势在于:
- 前10位可快速定位到具体药品
- 完整20位码可精确定位到特定批次的药品
- 效期信息内置便于自动化管理
javascript复制// 药品编码生成函数示例
function generateDrugCode(specId, drugId, batchNo, expiryDate) {
const specPart = specId.padStart(4, '0');
const drugPart = drugId.padStart(6, '0');
const batchPart = batchNo.padStart(8, '0');
const datePart = expiryDate.replace(/-/g, '');
return `${specPart}${drugPart}${batchPart}${datePart}`;
}
3.2 库存实时同步方案
为解决多终端库存数据一致性问题,我们设计了基于WebSocket的实时同步机制:
- 任何库存变动操作都会触发后端事件
- 事件通过WebSocket广播给所有在线客户端
- 客户端收到通知后主动请求最新数据
- 前端使用Vuex管理库存状态,确保视图及时更新
javascript复制// WebSocket服务端示例
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', (ws) => {
ws.on('message', (message) => {
// 处理客户端消息
});
// 库存变更时广播通知
stockChangeEmitter.on('change', (data) => {
wss.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify({
type: 'stock_update',
data: data
}));
}
});
});
});
3.3 药品效期预警实现
系统实现了三级效期预警机制:
- 临期预警:有效期剩余30天,黄色警示
- 近效期:有效期剩余7天,橙色警示
- 已过期:红色警示并自动锁定
后端使用ThinkPHP的定时任务模块每天凌晨执行效期检查:
php复制// 效期检查任务示例
class ExpiryCheckTask {
public function run() {
$today = date('Y-m-d');
$warningDate = date('Y-m-d', strtotime('+30 days'));
$criticalDate = date('Y-m-d', strtotime('+7 days'));
// 更新临期药品状态
Db::name('drug_stock')
->where('expiry_date', '<=', $warningDate)
->where('expiry_date', '>', $criticalDate)
->update(['warning_level' => 1]);
// 更新近效期药品状态
Db::name('drug_stock')
->where('expiry_date', '<=', $criticalDate)
->where('expiry_date', '>', $today)
->update(['warning_level' => 2]);
// 锁定过期药品
Db::name('drug_stock')
->where('expiry_date', '<', $today)
->update(['is_locked' => 1]);
}
}
4. 关键技术难点与解决方案
4.1 高并发库存扣减问题
在门诊发药高峰期,系统可能面临大量并发库存扣减请求。我们采用以下方案确保数据一致性:
- 数据库层面:使用SELECT...FOR UPDATE悲观锁
- 应用层面:实现Redis分布式锁
- 事务管理:整个扣减操作封装在数据库事务中
php复制// 库存扣减示例
public function reduceStock($drugCode, $quantity) {
Db::startTrans();
try {
// 获取分布式锁
$lockKey = "stock_lock_{$drugCode}";
if (!Redis::setnx($lockKey, 1)) {
throw new Exception('系统繁忙,请稍后再试');
}
Redis::expire($lockKey, 5);
// 查询并锁定库存记录
$stock = Db::name('drug_stock')
->where('drug_code', $drugCode)
->lock(true)
->find();
if (!$stock || $stock['quantity'] < $quantity) {
throw new Exception('库存不足');
}
// 扣减库存
Db::name('drug_stock')
->where('drug_code', $drugCode)
->dec('quantity', $quantity)
->update();
// 记录库存变动日志
$this->addStockLog($drugCode, -$quantity);
Db::commit();
return true;
} catch (Exception $e) {
Db::rollback();
throw $e;
} finally {
Redis::del($lockKey);
}
}
4.2 药品批次追溯实现
为满足GSP规范要求,系统实现了完整的药品批次追溯功能:
- 正向追溯:从入库批次→库存→出库→患者
- 反向追溯:从患者→药品→入库批次→供应商
数据库设计上,我们建立了以下关键表关系:
- 药品入库表(drug_in)
- 药品库存表(drug_stock)
- 药品出库表(drug_out)
- 处方明细表(prescription_detail)
javascript复制// 批次追溯前端组件示例
Vue.component('batch-trace', {
props: ['drugCode'],
data() {
return {
traceResult: null,
loading: false
}
},
methods: {
async trace() {
this.loading = true;
try {
const res = await axios.get('/drug/trace', {
params: { drugCode: this.drugCode }
});
this.traceResult = res.data;
} finally {
this.loading = false;
}
}
},
template: `
<div>
<el-button @click="trace" :loading="loading">追溯批次</el-button>
<el-table :data="traceResult" v-if="traceResult">
<el-table-column prop="type" label="类型"></el-table-column>
<el-table-column prop="batchNo" label="批次号"></el-table-column>
<el-table-column prop="quantity" label="数量"></el-table-column>
<el-table-column prop="date" label="日期"></el-table-column>
<el-table-column prop="operator" label="操作人"></el-table-column>
</el-table>
</div>
`
});
5. 系统安全与权限设计
5.1 RBAC权限模型实现
系统采用基于角色的访问控制(RBAC)模型:
- 用户-角色-权限三级结构
- 权限细化到按钮级别
- 前端路由动态生成
后端权限验证中间件示例:
php复制class AuthMiddleware {
public function handle($request, Closure $next, $permission) {
$user = Session::get('user');
if (!$user || !$user->hasPermission($permission)) {
if ($request->isAjax()) {
return json(['code' => 403, 'msg' => '无权访问']);
} else {
return redirect('/error/403');
}
}
return $next($request);
}
}
5.2 数据安全措施
- 敏感数据加密:药品价格、患者信息等敏感字段采用AES加密存储
- 操作日志审计:记录所有关键数据变更操作
- 数据定期备份:每日全量备份+binlog增量备份
- HTTPS传输:全站启用HTTPS加密传输
php复制// 数据加密示例
class DrugService {
const CIPHER = 'AES-256-CBC';
const KEY = 'your-32-byte-key';
const IV = 'your-16-byte-iv';
public static function encrypt($data) {
return openssl_encrypt(
json_encode($data),
self::CIPHER,
self::KEY,
0,
self::IV
);
}
public static function decrypt($encrypted) {
$decrypted = openssl_decrypt(
$encrypted,
self::CIPHER,
self::KEY,
0,
self::IV
);
return json_decode($decrypted, true);
}
}
6. 系统部署与性能优化
6.1 生产环境部署方案
推荐部署架构:
- 前端:Nginx静态资源服务
- 后端:Node.js + ThinkPHP双服务
- 数据库:MySQL主从复制
- 缓存:Redis集群
- 文件存储:分布式文件系统或对象存储
bash复制# 使用PM2管理Node服务
pm2 start server.js -i max --name "drug-api"
# ThinkPHP部署建议配置
location /api/ {
index index.php;
try_files $uri $uri/ /api/index.php$is_args$args;
location ~ \.php$ {
fastcgi_pass php-fpm:9000;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
6.2 性能优化实践
-
前端优化:
- 路由懒加载
- 组件异步加载
- 静态资源CDN加速
-
后端优化:
- 热点数据Redis缓存
- 数据库查询优化
- 接口响应压缩
-
数据库优化:
- 合理设计索引
- 查询语句优化
- 分表分库策略
javascript复制// 前端路由懒加载示例
const routes = [
{
path: '/stock',
component: () => import('./views/StockManagement.vue'),
meta: { requiresAuth: true }
},
{
path: '/purchase',
component: () => import('./views/PurchaseManagement.vue'),
meta: { requiresAuth: true }
}
];
7. 项目总结与经验分享
在实际开发过程中,我们积累了一些有价值的经验:
-
药品字典标准化:建议对接国家药品编码标准,避免后续对接其他系统时的映射问题。
-
批次管理要严格:在实际运行中发现,即使是同一药品不同批次的效期可能差异很大,必须严格区分管理。
-
库存快照设计:关键业务操作(如出库)时应记录当时的库存快照,便于后续审计追踪。
-
接口版本控制:医疗系统迭代周期长,要做好API版本管理,确保兼容性。
-
压力测试要充足:门诊高峰期系统压力很大,需要提前做好充分的压力测试。
这个项目成功上线后,显著提升了医院的药品管理效率,实现了:
- 库存准确率从92%提升到99.9%
- 药品调配时间缩短60%
- 过期药品发生率降低85%
- 全流程可追溯率达到100%