1. 项目概述:构建一个Node.js+Vue.js网上药店管理系统
去年我接手了一个医药电商平台的重构项目,核心需求是将传统的药品销售流程数字化。这个基于Node.js和Vue.js的网上药店管理系统,不仅需要满足常规电商功能,还要处理药品特有的合规性要求。系统上线后日均订单量突破3000单,处方药审核通过率达到98%,下面分享我的完整实现方案。
药品电商系统与普通电商有显著差异:首先,药品分类必须符合GSP规范;其次,处方药销售需要对接医疗机构审核系统;最后,所有交易记录必须保存至少5年备查。这些特殊要求直接影响我们的技术选型和架构设计。
2. 技术架构设计
2.1 全栈技术选型
前端架构:
- Vue 3 + TypeScript:获得更好的类型检查和代码提示
- Pinia状态管理:比Vuex更简洁的API设计
- Element Plus组件库:专为医疗行业优化过的表单验证组件
- ECharts:用于展示销售数据统计报表
后端架构:
- Node.js 18 LTS:长期支持版本更稳定
- NestJS框架:企业级应用所需的依赖注入、模块化支持
- TypeORM:支持MySQL和MongoDB双数据库操作
- Redis:高频访问的药品库存数据缓存
数据库设计:
sql复制-- 药品核心表结构示例
CREATE TABLE `drugs` (
`id` varchar(20) PRIMARY KEY COMMENT '国药准字编号',
`name` varchar(100) NOT NULL COMMENT '通用名',
`spec` varchar(50) NOT NULL COMMENT '规格',
`category_id` int NOT NULL COMMENT 'GSP分类ID',
`is_rx` tinyint(1) DEFAULT 0 COMMENT '是否处方药',
`price` decimal(10,2) UNSIGNED NOT NULL,
`stock` int UNSIGNED DEFAULT 0 COMMENT '库存',
`usage` text COMMENT '用法用量',
`contraindication` text COMMENT '禁忌症'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
2.2 系统模块划分
-
用户中心模块:
- 实名认证(对接公安系统API)
- 用药人管理(保存过敏史等医疗信息)
- 处方上传功能(支持拍照识别)
-
药品模块:
- 三级分类体系(按GSP规范)
- 药品详情页特殊字段:
- 不良反应
- 药物相互作用
- 孕妇及哺乳期妇女用药
-
处方审核模块:
- 对接互联网医院API
- 电子签名存证
- 审核状态实时推送
3. 核心功能实现细节
3.1 药品搜索系统优化
普通电商的搜索逻辑不适用于药品搜索,我们做了以下特殊处理:
javascript复制// 药品搜索特殊逻辑
class DrugSearchService {
async search(keyword) {
// 1. 优先匹配批准文号
if (/^[A-Z]\d{8}$/.test(keyword)) {
return this.searchByApprovalNumber(keyword);
}
// 2. 通用名拼音首字母搜索
if (/^[a-z]+$/.test(keyword)) {
return this.searchByPinyin(keyword);
}
// 3. 常规模糊搜索
return this.fuzzySearch(keyword);
}
// 药品搜索建议组件
getSearchSuggestions(input) {
return [
{ type: 'history', items: [...] },
{ type: 'hot', items: ['布洛芬', '连花清瘟'] },
{ type: 'category', items: [...] }
];
}
}
3.2 购物车特殊处理
药品购物车需要处理以下特殊情况:
- 处方药必须关联有效处方
- 某些药品不能合并购买(如抗生素)
- 需显示医保支付比例
vue复制<!-- 购物车组件片段 -->
<template>
<div v-for="item in cartItems" :key="item.drugId">
<div v-if="item.isRx" class="rx-tag">
处方药
<span v-if="!item.prescriptionId" @click="uploadPrescription">
上传处方
</span>
</div>
<div class="insurance" v-if="item.insuranceRatio">
医保报销{{ item.insuranceRatio }}%
</div>
</div>
</template>
<script setup>
const checkDrugConflict = (cartItems, newItem) => {
// 检查药物相互作用
const conflictDrugs = ['抗生素A', '抗生素B'];
return cartItems.some(item =>
conflictDrugs.includes(item.drugId) &&
conflictDrugs.includes(newItem.drugId)
);
};
</script>
4. 支付与安全合规方案
4.1 特殊支付流程设计
药品电商的支付流程有额外步骤:
- 处方药需先审核后支付
- 支持医保个人账户支付
- 必须保存完整的支付凭证
mermaid复制sequenceDiagram
participant 用户
participant 前端
participant 后端
participant 支付网关
participant 医保系统
用户->>前端: 提交订单
前端->>后端: 创建订单(含药品类型)
后端-->>前端: 返回订单详情
alt 处方药
后端->>医疗系统: 提交处方审核
医疗系统-->>后端: 审核结果
后端-->>前端: 通知审核状态
end
前端->>后端: 发起支付
后端->>医保系统: 校验医保支付资格
医保系统-->>后端: 返回可报销金额
后端->>支付网关: 生成支付订单
支付网关-->>前端: 调起支付界面
用户->>支付网关: 完成支付
支付网关->>后端: 支付结果通知
后端->>数据库: 更新订单状态
后端-->>前端: 支付结果
4.2 数据安全措施
-
敏感信息加密:
- 使用SM4算法加密患者医疗信息
- 数据库字段级加密(如过敏史)
-
审计日志:
typescript复制// 药品数据变更审计日志
@Entity()
export class DrugAuditLog {
@PrimaryGeneratedColumn()
id: number;
@Column()
drugId: string;
@Column('json')
oldValue: Record<string, any>;
@Column('json')
newValue: Record<string, any>;
@Column()
operator: string;
@CreateDateColumn()
createdAt: Date;
}
- 合规性检查:
javascript复制// 每日合规性检查任务
class ComplianceCheck {
async checkPrescriptionDrugs() {
const orders = await Order.find({
hasRx: true,
prescriptionVerified: false
});
if (orders.length > 0) {
await this.alertComplianceTeam(orders);
}
}
}
5. 部署与性能优化
5.1 医疗级系统部署方案
-
服务器架构:
- 采用双活机房部署,满足等保三级要求
- 数据库主从分离+读写分离
- 关键服务(如支付)独立部署
-
缓存策略:
javascript复制// 药品详情缓存方案
async getDrugDetail(id) {
const cacheKey = `drug:${id}`;
let data = await redis.get(cacheKey);
if (!data) {
data = await drugRepository.findOne({
where: { id },
cache: 3600 // TypeORM内置缓存
});
await redis.setex(cacheKey, 300, JSON.stringify(data));
}
return data;
}
5.2 前端性能优化技巧
- 按需加载医疗组件:
javascript复制// 动态加载处方上传组件
const PrescriptionUpload = () => import(
/* webpackChunkName: "prescription" */
'@/components/PrescriptionUpload.vue'
);
- 图片优化:
- 药品图片使用WebP格式
- 实施懒加载策略
html复制<img
v-lazy="drug.imageUrl"
alt="药品图片"
loading="lazy"
:data-srcset="`${drug.imageUrl}?width=400 400w, ${drug.imageUrl}?width=800 800w`"
>
- 关键CSS内联:
html复制<style>
.drug-card, .rx-badge {
/* 保证首屏关键样式快速加载 */
}
</style>
6. 测试与合规验证
6.1 医药行业特殊测试用例
-
处方药流程测试:
- 未上传处方时禁止加入购物车
- 处方审核不通过时禁止支付
- 处方过期后订单自动取消
-
药品信息准确性测试:
javascript复制describe('药品信息校验', () => {
it('处方药必须包含不良反应说明', async () => {
const rxDrug = await Drug.findOne({ isRx: true });
expect(rxDrug.adverseReactions).not.toBeNull();
});
it('国药准字格式验证', () => {
const zjNumber = 'H20010001';
expect(/^[A-Z]\d{8}$/.test(zjNumber)).toBeTruthy();
});
});
6.2 灰度发布策略
由于医药系统的特殊性,我们采用:
-
分区域发布:
- 先在医疗资源丰富的一线城市上线
- 逐步向二三线城市扩展
-
用户分群发布:
javascript复制// AB测试新功能
function shouldEnableNewFeature(user) {
return user.tags.includes('medical_professional') ||
Math.random() < 0.2; // 20%普通用户
}
- 紧急回滚机制:
- 准备完整的回滚脚本
- 数据库变更使用可逆迁移
7. 医疗合规要点
在开发医药电商系统时,需要特别注意:
-
《药品网络销售监督管理办法》要求:
- 处方药销售必须有执业药师审核
- 首页不得直接展示处方药
- 必须保存处方、审核记录等至少5年
-
医保对接注意事项:
- 使用官方SDK接入医保支付
- 医保目录药品需特殊标注
- 医保结算数据单独存储
-
数据存储规范:
sql复制-- 医疗数据存储特殊要求
CREATE TABLE `prescriptions` (
`id` bigint PRIMARY KEY AUTO_INCREMENT,
`patient_id` bigint NOT NULL,
`doctor_license` varchar(50) COMMENT '医师执业证书编号',
`hospital_code` varchar(20) COMMENT '医疗机构代码',
`images` json COMMENT '处方照片',
`diagnosis` varchar(255) COMMENT '诊断结果',
`is_valid` tinyint(1) DEFAULT 0,
`expire_at` datetime NOT NULL,
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
INDEX `idx_patient` (`patient_id`),
INDEX `idx_doctor` (`doctor_license`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='电子处方';
开发医疗类电商系统最大的挑战不是技术实现,而是如何在满足复杂合规要求的同时提供流畅的用户体验。我们在项目中总结出几个关键点:1) 药品数据必须来自权威来源;2) 处方审核流程既要严谨又要高效;3) 所有操作都要留有完整审计日志。这些经验也让我们后续开发其他医疗系统时少走了很多弯路。