做药房管理系统这件事,老实说一开始并不复杂,但做完回头看,水挺深的。我最初只是帮朋友家的小药房整理采购台账,Excel里几千条记录,品类多、批次杂、供应商报价还不统一,每次月底对账都像破案现场。后来干脆动了手,用Python加Vue搭了一套药品采购管理系统,从采购申请、供应商比价、入库验收到应付账款,全部推进线上闭环。这篇文章就详细讲讲这套系统的设计思路、核心模块、前后端实现以及我在开发过程中踩过的坑,给同样想做类似系统的朋友一个完整参考。
1. 项目脉络:药房采购场景的真实痛点与系统边界
1.1 买药这个事,为什么特别需要一套系统
药房的采购和普通商品采购有个本质区别:药品是强监管商品,牵扯效期、批号、GSP记录、随货同行单,供应链里任何一个环节脱节,轻则库存积压,重则资质出问题。你会发现,药房采购绝不只是"下单收货"这么简单,它其实是一条完整的链路:
- 缺货发现:销售端得出哪些药快卖完了,哪些效期不足,哪些近效期需要促销出清。
- 需求汇总:店长或采购员按供应商维度、品类维度汇总采购需求。
- 供应商比价:同一品规的药品可能有两三家供应商,价格、账期、配送时效都得比。
- 审批层级:超过一定金额的采购单需要上级审批,小额单可以快速通过。
- 采购入库:到货之后要按批号核对品名、规格、数量、效期、注册证号。
- 对账付款:月结供应商的账单要核对送货单、入库单,确认无误后走付款流程。
这些环节靠Excel和微信群也能跑起来,但一旦门店多了、品类过千、供应商过几十家,人工处理就会开始漏批次、漏效期、漏资质材料。我做的这套系统,核心就是把上面这条链路搬到线上:谁在什么时间发起采购,选了哪家供应商,批号效期是什么,验收人是谁入库的,账期什么时候到,全部留痕。
1.2 系统面向的角色和功能边界
很多人在设计管理系统时有个通病,就是功能越做越多,最后把系统做成一个大杂烩。我自己规划的时候,一开始就想清楚了边界:这套系统只解决"采购"这件事,不做全链路进销存,不做收银台,不做会员营销。核心角色就三四个,功能条数控制在合理范围:
- 操作员:发起采购申请、查看缺货提醒、录入收货验收信息。
- 采购员:汇总采购需求、选择供应商、维护采购价格、提交审批。
- 店长/负责人:审批采购单、查看采购统计报表、维护供应商资质。
- 系统管理员:维护用户权限、配置基础数据、管理药品字典和供应商档案。
它不是ERP,不是医院HIS系统的采购模块,而是独立给中小连锁药房或单店使用的轻量级专用系统。这个定位非常重要,因为它决定了技术选型和功能复杂度,也决定了开发周期能控制在个人可以完成的范围。
1.3 功能需求整理:从需求池到功能清单
我习惯把需求拆成几个池子:必须有的、最好有的、先不做的。必须有的部分我梳理成下面这些功能点:
- 药品档案管理:药品名称(通用名和商品名分开存)、规格、剂型、生产厂家、批准文号、包装单位、默认供应商。
- 供应商档案:供应商名称、联系人、电话、证照有效期(GSP证、营业执照)、对账周期、默认账期。
- 采购申请:门店/部门、申请人、申请明细(药品、数量、期望到货日期)、备注。
- 采购单生成:由采购申请合并生成采购单,分摊到供应商,形成正式采购订单。
- 审批流:按单笔金额和月度累计金额设阈值,超过阈值转店长审批。
- 收货验收:到货后扫码或按单号调出采购单,逐条录入实收数量、生产批号、效期,不合格品可拒收。
- 采购入库:验收无误后生成入库记录,同步更新库存表。
- 退货处理:针对效期差、破损、不合格药品,走退货流程并更新应付账款。
- 应付账款:按供应商维度统计应付、已付、未付金额,支持按账期查询逾期单。
- 统计报表:采购金额按月/按品类/按供应商排行,缺货率、到货及时率等指标。
"最好有"的部分,我加了采购价格历史曲线(同一个品规在供应商处的价格波动)、近效期药品提醒、库存上下限自动补货建议。先把"必须有"的做扎实,后面的功能才有意义。
2. 架构拆解:为什么选Python+Vue这套组合
2.1 后端选型逻辑:FastAPI、Flask还是Django
Python做后端最常见的选择是Flask、Django和FastAPI。做这个项目时,我最终选了FastAPI,理由挺明确的:
- Django太重了,自带Admin后台和ORM,但我的业务需要自定义审批流和复杂查询,Django的约定式设计反而让我觉得绕。
- Flask轻量但全手动,异步支持弱;如果后续要接扫码枪、做WebSocket实时提醒,Flask要额外配很多东西。
- FastAPI原生异步,自带OpenAPI文档,接口调试可以直接在浏览器里看文档页面测,开发效率高不少。
后端的技术栈如下:
- Python 3.10+,FastAPI作为Web框架。
- SQLAlchemy 2.x作为ORM,底层连PostgreSQL或MySQL都行。我开发环境用SQLite,生产切到MySQL。如果你只做小规模单店,SQLite完全够用,部署时直接一个文件就带走。
- 数据校验用Pydantic v2,表单提交和API请求都通过模型类做校验。
- 认证用JWT token,配合依赖注入实现角色权限校验。
- 定时任务用APScheduler,跑每日库存快照、近效期提醒、供应商证照到期提醒。
为什么慢查询和复杂报表要直接写SQL而不是全交给ORM?因为采购统计这种多表关联、按时间窗口滚动的查询,ORM表达起来又长又难维护。我一般业务增删改查用ORM,报表统计用SQLAlchemy的text()写原生SQL,兼顾效率和可读性。
2.2 前端选型逻辑:Vue 3 + Element Plus的实际体验
前端这块,我选了Vue 3的组合式API加Element Plus组件库。Vite作为构建工具,axios做请求,Pinia管理全局状态,Vue Router做页面路由。
为什么不用React?不是React不好,而是Vue的模板语法和组件库生态更适合快速搭建中后台管理系统。Element Plus的表单组件、表格组件、弹窗组件都很成熟,做药房采购这种以表单和列表为主的系统,开发效率提升非常明显。而且Vue的响应式机制对嵌套表格、动态表单这种场景处理起来很顺手。
我在项目里用到的Element Plus组件大概有这些:
- el-table:采购单明细展示、供应商列表、入库记录列表,支持多选、排序、列固定。
- el-form:采购申请表单、验收录入表单、供应商信息表单,配合rules做必填校验。
- el-tree:药品分类树,点击分类联动筛选药品列表。
- el-dialog:新建、编辑、审批等业务操作的弹出层。
- el-timeline:展示采购单的流转历史(申请→审批→下单→到货→入库)。
- el-tag:标记采购单状态、效期预警等级。
前端这边我踩过的坑主要是权限路由的动态生成。如果直接在前端路由表里写死角色对应页面,加角色或者调权限时,前端代码要跟着改。后来我把菜单和权限全部做成后端接口下发,前端登录时拉取权限标识,再用router.addRoute动态注册路由,这样后端配置一变,前端不用发布新版本就能生效。
2.3 前后端分离下的部署结构
这个项目我采用的是经典前后端分离结构:前端项目用nginx托管静态文件,后端FastAPI用uvicorn跑在独立端口,通过反向代理统一入口访问API。数据库单独跑在云数据库或内网主机上。
前端构建流程: npm install -> npm run build -> 产物放到nginx的html目录下我实际部署时用的是Docker Compose。一条命令拉起后端容器、前端容器、数据库容器,升级时只需要替换镜像、刷新容器,不用手动装依赖和改配置。
3. 后端核心模块:从采购申请到入库结算的闭环
3.1 数据模型设计:表结构怎么规划才不留坑
数据库设计是这套系统的地基,坑如果埋在表结构里,后面改起来就是大工程。我按业务域拆成几个模块:基础档案、采购业务、库存关联、财务对账。
药品字典表(drugs)的核心字段我这么设计:
- id,通用名(generic_name),商品名(brand_name),规格(spec),剂型(dosage_form),生产厂家(manufacturer),批准文号(approval_number)。
- 包装单位(package_unit),最小销售单位(min_unit),换算比例(conversion_ratio),比如一件=20盒,一盒=10袋。
- 默认供应商(default_supplier_id),默认采购价(reference_price),最低库存(min_stock),最高库存(max_stock)。
- 是否处方药(is_rx),是否管制药品(is_controlled),是否停用(is_disabled)。
这里有一个非常关键的细节:同一种药品,不同厂家、不同规格、不同批准文号,必须拆成独立记录。例如"阿莫西林胶囊 0.25g24粒"和"阿莫西林胶囊 0.25g48粒",虽然通用名相同,但规格不同,在系统中就是两条独立的药品档案。采购、入库、销售、盘点都必须精确到具体品规,否则效期管理和批次追踪会乱掉。
采购业务这块,我设计了"主单-明细"的两层结构:
- purchase_orders(采购主单):单号(单号规则自动生成)、供应商id、采购员、状态(草稿/待审批/已审批/已下单/部分到货/已完成/已取消)、审批记录、备注。
- purchase_order_items(采购明细):采购单id、药品id、申请数量、实际下单数量、单价、金额、税率、到货数量、验收状态。
这样设计的好处是:一张采购单可以批量包含几十种药品,每种药品的到货和验收进度独立跟踪。到货时按明细行录入批号和效期,而不是整单一把梭。
库存与批次关联表(stock_batches):
- id、药品id、供应商id、批号(batch_no)、生产日期(produce_date)、有效期至(expiry_date)、剩余数量、入库时间。
为什么要单独维护批次表?因为药品是按批次管理的,同一盒药可能是不同批次的。效期预警、召回、追溯全部依赖批次数据。采购入库时录入的批号效期,后续销售出库通过先进先出原则扣减批次,近效期药品能在每个月末自动统计出来。
财务对账这块,我单独建了supplier_statements表:供应商id、账单期(年月份)、应付金额、已付金额、未付金额、付款截止日期、状态。每次采购入库后自动累加该供应商的应付金额,付款登记后扣减已付金额。
3.2 采购单流转状态机:防止流程乱套的关键设计
采购单最容易出的问题就是状态混乱,比如东西都收货了,系统还显示"待审批",或者单子已经取消了,验收员还能往里录入数量。所以我在设计状态字段时做了一个有限状态机,所有状态流转只有固定的几条路径,不存在自由跳转。
状态路径如下:
- 草稿(DRAFT)→ 已提交(SUBMITTED)→ 已审批(APPROVED)→ 已下单(ORDERED)→ 部分到货(PARTIAL_RECEIVED)→ 已完成(COMPLETED)
- 任意"非完成"状态都可以作废(CANCELED),但作废操作要记录操作人和原因。
为什么需要部分到货这个状态?现实中供应商分批送货是常态。这周送一半,下周再补一半,系统必须支持这种部分入库能力。每个明细行的实收数量独立累加,全部明细的累计实收数量等于下单数量时,主单自动转成已完成状态。
代码实现上,我专门写了一个状态流转校验函数,每次更新状态前先检查当前状态能否合法跳到目标状态,不允许的就直接抛异常并返回错误信息。
3.3 采购价格管理:比价和调价怎么自动化
药房的采购成本直接影响毛利率,所以价格管理很重要。同一个药品,供应商A报价低但账期只有15天,供应商B报价高但给了30天账期还月结,采购员做决策时必须能直观对比。
我在药品档案里维护参考采购价,在采购明细行里记录"本次采购单价",同时在price_history表里存价格变更记录,包含药品id、供应商id、变更前价格、变更后价格、生效时间、操作人。每次新建采购单时,系统自动带出该药品最近一次向该供应商采购的价格作为默认价,采购员可以手动调整并备注原因。
比价功能没有做推送式的"系统自动推荐最优供应商",而是做成"采购对比视图":同一个药品,列出所有供应商最近三次采购的报价、平均价、账期、到货时间。采购员看这张表做决定,比我直接替人决策要靠谱。药品采购不是只拼单价,还要看供应商的现货能力、物流速度和售后服务,这些维度系统没法全自动判断。
3.4 效期管理:从入库那一刻就要算清楚
效期是药品管理系统里最容易出人命的模块。我设计的效期管理逻辑如下:
- 入库时强制录入有效期至日期,不允许为空。
- 每天定时任务扫描批次表,计算剩余天数,按三档生成预警:大于180天为正常,90-180天为关注,30-90天为预警,小于30天为紧急。
- 近效期药品列表在首页仪表盘展示,点击可以穿透到具体批次的库存位置和数量。
- 采购申请时,系统自动检查在途订单和现有库存量,如果某种药已经有足够库存且效期良好,系统会提示"已有近效期库存未消化完,请谨慎补货"。
这个"谨慎补货"的提示非常实用。之前有一次某感冒药在效期还有8个月时补了一大单,结果三个月都没卖完,最后过期损失了不少。从那之后,效期和补货的联动逻辑就写进系统里了。
4. Vue前端页面:做给操作员用的交互细节
4.1 采购工作台:一屏看清今天要干什么
采购工作台是这套系统里操作员每天打开的第一个页面。设计它的核心思路是"把最高频的操作放在最显眼的位置,把需要处理的事情用红点数标出来"。
工作台由几个区域组成:
- 顶部统计卡:今日待处理采购申请数、待审批单据数、今日到货待验收数、近效期预警数。
- 中间快捷操作区:新增采购申请、新增采购单、到货验收、退货登记,四个大按钮直接进入对应页面。
- 下方待办列表:三个tab分别是"待我审批""待我验收""我发起的采购",每个tab里的条目直接显示单号和当前状态。
这个页面的开发难度不高,但交互细节很重要。表格里的状态用不同颜色的el-tag直观区分,点击单号可以弹出详情页查看整单流转记录,不用跳转页面。操作员不用翻来翻去找功能入口,效率提升非常明显。
4.2 采购单创建流程:从选药品到生成单号
采购单创建页面是整个系统里最核心的表单页,交互设计上我特别注意了下面几点:
第一步,选供应商。选好供应商之后,药品列表会自动过滤成只显示该供应商能供货的品规。避免采购员选错供应商、下了订单对方根本没货的情况。
第二步,选药品和数量。药品选择用弹窗搜索,支持按通用名、商品名、规格模糊查询。选中药品后填写采购数量,系统自动带出上次采购价和参考价,并且显示最近三次采购价格趋势,采购员可以判断这次报价是否合理。
第三步,填写审批说明。如果采购单金额超过阈值,需要填写采购理由,比如"近效期药品消化完毕,需要补充库存""季节性需求增加"等。审批人看到理由后更容易快速决策。
第四步,提交。提交后系统自动生成采购单号,规则是PO + 年月日 + 四位流水号,例如PO202405240013。审批事宜自动推送给对应审批人。
表单提交前的校验有两个坑值得特别提一下:一是数量必须大于0但不能超过申请的补货上限,二是同一张采购单里不能重复出现同一个品规。重复提交同一个药品会让供应商那边以为是两种药,造成重复发货。我在前端做了去重校验,同时后端接口也做了同样的幂等校验,双保险。
4.3 收货验收界面:扫码录入批号效期的交互优化
到货验收是操作员每天实际要干的重活,这个页面的设计重点不是花哨,而是减少重复输入。一张单可能包含几十种药品,每种药都要录入实收数量、批号、效期。如果全靠手输,工作量非常大,还容易录错。
我做了三个优化:
- 实收数量默认等于下单数量,如果分批到货可以手动改成对应数量。
- 批号和效期录入支持连续扫码,扫码枪扫一次药品批号自动填入一条记录,扫完批号自动跳到效期输入框。效率比手输快很多。
- 效期输入用日期选择器并限制最小日期为今天,不允许录入过去的日期(特殊情况除外)。
验收录入的每一行数据都与采购明细行关联,后端在保存时还会校验累计已验收入库数量不能超过下单数量。如果供应商送多了且超量的货没有事先沟通,系统直接阻止入库,需要人工创建额外采购单或退货处理。
4.4 报表页面:用数据说话,辅助决策
报表页面分几个tab:采购金额趋势、品类占比、供应商交付质量、效期预警统计、应付账款余额。
采购金额趋势用折线图展示最近12个月每个月的采购总额,可以按供应商维度拆,也可以按品类维度拆。品类占比做成环形图,让店长一眼看清哪种药花费占比最高,采购结构是否合理。
供应商交付质量这个报表我觉得最有意思。它统计每个供应商在过去6个月内订单总数、按时到货率、平均到货时长、拒收率、退货率。按时到货率这个指标非常实用,很多供应商报价低但总是晚一两天,表面上看采购价便宜了,实际上门店缺货损失和紧急补货成本更高。
图表库我用了ECharts,配合Vue封装成组件,数据由后端接口返回聚合后的JSON,前端直接渲染。大文件报表分页和懒加载都要做,不能一次把几万条明细一次性拉到前端。
5. 权限设计与审批流:让采购流程不失控
5.1 基于角色的权限控制:操作员能看到什么不能做什么
权限模型我采用RBAC(Role-Based Access Control),角色-权限-用户的经典三层结构。系统预置了四个角色,每个角色的权限矩阵如下:
| 模块 | 操作员 | 采购员 | 店长/审批人 | 系统管理员 |
|---|---|---|---|---|
| 药品档案查询 | 可查看 | 可查看/维护参考价 | 可查看 | 可维护 |
| 采购申请 | 可发起 | 可发起/汇总 | 可审批 | 全部权限 |
| 采购单 | 可查看 | 可创建/提交 | 可审批/查看 | 全部权限 |
| 收货验收 | 可录入 | 可录入 | 可查看 | 可查看 |
| 退货管理 | 可发起 | 可处理 | 可审批 | 全部权限 |
| 供应商档案 | 可查看 | 可维护 | 可审批 | 全部权限 |
| 对账付款 | 不可见 | 可录入 | 可审批 | 全部权限 |
| 权限管理 | 不可见 | 不可见 | 不可见 | 全部权限 |
| 系统设置 | 不可见 | 不可见 | 不可见 | 全部权限 |
实际编码时,后端每个接口都加了依赖注入校验,前端只是隐藏入口。接口层校验是安全底线,前端隐藏菜单只是用户体验层面的处理,绝对不能依赖。举个例子,如果有人猜到接口地址直接调用API,前端不展示权限管理菜单,但接口层没有校验的话就会被越权操作,这个风险在涉及钱款的系统里绝对不能有。
5.2 审批条件配置:按金额和品类自动分流
审批流的规则我做成可配置的,不改代码就能调整阈值。配置表里维护了两类规则:
- 按单笔金额:采购单总金额大于5000元,必须店长审批;大于20000元,必须上一级审批。
- 按品类特殊管控:某些高风险品类,比如含特殊成分的药品,无论金额多少都必须审批。
审批操作本身很简单,就是通过或驳回。但通过和驳回都要求填写意见,这样流程记录才完整。每个审批动作都会写进审批历史表,在每个节点记录操作人、操作时间、审批意见、审批结果。
这里要强调一下"职责分离":采购员不能审批自己创建的采购单。系统在提交审批时会检查发起人和审批人是否相同,相同就直接报错。这种控制虽然看起来死板,但能有效防止采购员绕过监督,是财务合规的基本要求。
5.3 审批超时提醒:避免单据卡住没人处理
实际使用中,采购单最怕的就是审批人出差了或者忙起来忘了看,单子就卡在"待审批"状态好几天,门店断货了还不知道怎么回事。我给审批流程加了一个超时提醒机制:
- 审批单超过24小时未处理,每天早上定时任务给审批人推送待办提醒。
- 超过48小时未处理,升级提醒给审批人的上级。
- 超过72小时未处理,系统自动标注"超时未处理"并进入报表异常清单。
这个机制上线后被店长狠狠夸了一回。之前有一次月底结账时发现一周前的采购单还在待审批状态,门店某个关键药品已经断货两天。有了超时提醒后,这种事情基本不再发生。
6. 部署实测与踩坑记录
6.1 并发扣库存问题:采购入库和销售出库同时操作怎么办
我最早写库存更新逻辑时,用的是典型的"先查后改"模式:查出当前库存数量,在Python代码里做加减法,再写回数据库。单用户测试没问题,但多用户同时操作时就出过岔子。当时我在测试环境里模拟了两个操作员同时对一个药品做入库操作,结果库存数量对不上,一个入库单把另一个入库单的更新覆盖了。
这个问题的本质是并发更新丢失。我后来用两种方式解决:
- 数据库行级锁:更新库存前使用SELECT ... FOR UPDATE锁定对应库存行,事务提交后再释放。
- 原子更新:直接用UPDATE stock_batches SET quantity = quantity + 数量 WHERE id = ...,让数据库自己保证加减操作的原子性,不需要先查出旧值再改。
实际操作中我倾向于后者,代码简单、性能好,也不用担心锁等待。对于需要"扣减库存前检查库存是否充足"的场景,我在UPDATE语句里加了一个数量条件:
UPDATE stock_batches SET quantity = quantity - :out_qty WHERE id = :batch_id AND quantity >= :out_qty如果影响行数为0,说明库存不足或批次不存在,事务回滚,再向前端返回"库存不足"的错误。这种方式避免了先SELECT再UPDATE之间被人插队的窗口期问题。
6.2 采购单号生成策略:并发下不能依赖数据库自增
采购单号我用的是"PO + 年月日 + 四位流水号"格式。最早实现时直接查数据库当天最大的流水号加一,结果并发提交时两张单子生成了同一个单号,直接导致数据错乱。后面改成了两种方案配合:
- 方案一是用数据库序列或者独立的单号表,每次生成时对单号表加锁取号。
- 方案二(推荐,我最终用的方案)是使用Redis的INCR命令做流水号自增,一天一个key,过期时间设为48小时。
生成逻辑伪代码: daily_key = f"po_seq_{purchase_date}" seq = redis.incr(daily_key) po_no = f"PO{purchase_date}{seq:04d}"这样既保证并发安全,也不会因为数据库锁拖慢请求性能。前提是代码里要处理Redis不可用时降级到数据库序列取号,毕竟系统不能因为Redis挂掉就不干活了。
6.3 前端路由切换时表单数据丢失
这个坑是在做采购申请页面时发现的。操作员辛辛苦苦填了大半张采购申请单,不小心点了一下侧边栏菜单,跳到了另一个页面,再回来时表单内容全部清空。虽然业务上看采购申请是允许分次填写的,但用户体验非常糟糕。
我做的处理有两个层面:
- 表单离开时弹确认框,提示"当前页面存在未保存的表单数据,确认离开?"如果选择取消,则阻止路由跳转。
- 临时保存草稿功能:每5秒把表单数据自动缓存到浏览器的localStorage里,下次进入页面时优先恢复缓存,提交成功或手动清空后删除缓存。
第二个方案更实用,因为操作员不一定会看弹窗提示,直接恢复现场才是真正的无感体验。后来我还把这个自动保存的配置做成了一个开关,给不同门店按需开启。
6.4 供应商证照到期预警:不能只靠人脑记
药品采购涉及供应商的资质证照审查,GSP证照、营业执照、药品经营许可证都有有效期。证照过期还继续采购,这是合规层面的问题,审查时会被一票否决。
我在系统里加了一个供应商证照管理模块,每个供应商可以录入多张证照,每张证照记录证照类型、证照编号、有效期至、附件照片。每天早上定时任务扫描到期情况,形成预警清单:
- 证照即将在90天内到期:等级为预警,推送给采购员。
- 证照已过期:等级为严重,推送给店长,同时系统在创建采购单时直接阻断该供应商,不允许新建任何采购订单。
这个设计的价值在于:把关口从"人盯"变成"系统盯",只要证照信息录入无误,到期提醒就不会漏。采购部门再也不用每个月手动翻证照复印件了。
7. 运行时性能优化与存量数据迁移
7.1 列表查询分页与索引设计
系统上线运行一段时间后,药品字典、采购单历史、批次记录的数量涨得很快。最早没建索引的时候,翻页查询一些大表经常要等两秒多,操作员以为系统卡死了。
我做了两件事来优化:
第一,建索引。给所有外键字段和查询条件字段单独建普通索引或联合索引。比如purchase_orders表的supplier_id、status、created_at三个字段联合建索引,stock_batches表的drug_id、expiry_date两个字段联合建索引。查询条件上常用到的字段,索引带来的性能提升非常明显。
第二,分页改游标或深度翻页优化。普通的分页LIMIT offset在大数据量时越翻页越慢,我改成了基于条件的偏移方式,例如WHERE id > last_id ORDER BY id LIMIT 20。对前端来说交互基本没变化,但后端查询效率提升不少。
7.2 老药房Excel数据怎么导入系统
实际落地时还有一个现实问题:药房原来的Excel数据几千条,怎么快速迁到新系统里?我写了一个批量导入工具,支持药品字典、供应商档案、库存期初数三个维度的Excel导入。每张表有对应的模板,模板里的列名和系统字段一一对应,导入前在代码里做合法性校验。
导入的细节难点在于去重:Excel里的药品名可能写得不规范,比如"阿莫西林 0.25x24"和"阿莫西林胶囊0.25g*24粒"其实是同一个品规。我导入前先做规范化处理,统一替换剂型和规格的常用写法,然后再匹配系统已有数据。匹配不上的标记为"待人工确认",导入后生成一份差异报告,采购员逐条确认后再入库。
期初库存这块最容易出错的是批次数据。Excel里通常只有总数没有分批次明细,我建立期初批次的方式是:默认把期初库存全部记为一个批次,批次号用"INIT"前缀加导入日期,效期统一设为药品剩余有效期。严格说这不是最准确的处理方式,但作为上线初期的过渡方案,能快速把系统跑起来,后续通过日常盘点逐步修正批次数据。
7.3 定时任务与消息通知:什么时候提醒才不会打扰人
系统的定时任务分两个时间点执行:每天晚上23点跑库存快照和效期扫描,每天早上8点跑证照到期提醒和审批超时提醒。提醒方式一开始只做了站内通知,后来加了企业微信机器人推送,采购员在群里就能看到新增的待办。
时间点的选择很重要。每晚23点跑库存快照,是因为这个时间点门店基本结束营业,不会有业务事务写入,快照数据一致性最好。早上8点推送提醒,是让采购员上班打开手机第一眼就看到今天的待办,不会错过审批和证照处理。
8. 这套系统后续还能怎么扩展
系统上线跑稳之后,我开始想它的边界还能往哪延伸。目前有几个方向我觉得很有价值,也正在逐步落地:
- 采购预测模块:基于过去12个月的销售数据和库存周转天数,给每个品规算出一个建议采购量,生成补货建议清单。采购员可以直接把建议清单转成采购申请,省掉大量人工统计工作。
- 电子随货同行单管理:供应商送货时附带随货同行单,目前是拍照上传,后续想做成扫码识别和OCR自动提取批号效期,减少录入工作量。
- 供应商协同门户:给供应商开一个受限账号,他们可以登录系统查看自己名下的采购单、送货记录和对账信息。减少采购员反复微信传达状态的时间损耗。
- 移动端审批:目前店长必须在电脑端审批,后续想做一个简单的移动端页面,让他在地铁上、出差路上点几个按钮就能完成审批动作。
每个方向都是基于实际使用中暴露出来的需求,不是拍脑袋加的。做系统最忌一上来就堆功能,先把核心链路跑扎实,再根据数据反馈做迭代,踩坑率会小很多。
回到文章开头说的那句话,药房采购管理系统听起来是个挺垂直的业务系统,但真正做完你会发现,它其实要处理的是供应链管理、库存管理、财务对账、权限合规这些通用问题在药品这个特殊行业里的组合。开发周期花了不少,实际投入的精力远超预期,但系统跑起来之后,每天节省的人工对账时间、库存积压减少的损失、效期预警避免的过期浪费,都是看得见摸得着的回报。如果看到这里的你也有类似的想法,我的建议是:先把业务流程画清楚,再动手写代码,这才是做管理系统最重要的一步。