简介:这份资源面向超市经营者、门店管理人员及信息系统初学者,提供一套覆盖商品进货、销售管理、库存统计与订单管理全流程的进销存管理方案,并附系统用例图,帮助理解员工、顾客、供应商等参与者与系统之间的交互关系,适合课程设计、毕业项目参考或小型超市信息化落地。压缩包共103个文件,约2.03MB,以aspx页面、cs后台代码、jpg与gif界面截图为主,辅以css样式、bmp图片及mdf、ldf数据库文件,构成可运行的Web系统雏形。已有4380人学习下载,说明其在同类教学与实战资源中具备一定参考价值。读者可从中获取进货录入与采购订单生成、销售出库与发票处理、库存阈值预警与周转分析、订单状态追踪等模块的实现思路,并借助用例图梳理功能边界与业务流程,为二次开发或系统设计提供直观参照。
1. 超市进销存系统配用例图:一份能直接拆开复用的需求建模底稿
很多做毕业设计或接私活的开发者,拿到“超市进销存”这个题目第一反应是打开 IDE 建表写 CRUD,结果做到一半发现采购、销售、库存三块业务互相打架,退货单和盘点单的字段对不上,最后推倒重来。问题不在代码,在于需求阶段没把角色和用例边界划清楚。这份资源把超市进销存管理系统和配套用例图打包在一起,核心价值是给你一套已经梳理过的业务边界:谁在用系统、每个角色能触发哪些动作、哪些动作之间存在包含与扩展关系。它适合三类人——课程设计要交需求文档的学生、接中小型零售管理系统的独立开发者、以及想补一补 UML 建模基本功的后端。用例图不是画着好看的,它决定了你后面建几张表、写几个接口、权限怎么切。
2. 用例图先立住:角色划分与三类关系的落地读法
2.1 从超市真实岗位反推参与者
拿到用例图第一件事不是看图形,是看参与者(Actor)有几个。超市进销存场景里,合格的参与者划分通常落在四个角色上:收银员、库管员、采购员、店长(或系统管理员)。这份资源的用例图基本按这个骨架展开,我拆的时候重点核对了每个角色背后的操作权限是否闭合。
收银员只碰销售侧:扫码开单、挂单、退货、查商品价格。库管员管库存侧:入库登记、出库登记、库存盘点、库存预警查看。采购员管进货侧:供应商维护、采购单创建、到货验收。店长是全局视角:商品档案维护、价格调整、报表查看、用户与权限管理。
这里有个容易被忽略的点:退货这个用例到底挂给谁。很多同学把退货画在库管员下面,实际业务里退货是收银台发起的,库管只负责退货商品回库。用例图里如果退货同时连了收银员和库管员,说明作者考虑过这条链路,你落地时就要在退货单上同时记录“操作收银员”和“回库确认人”两个字段。反过来,如果图里退货只连了一个角色,你就要自己补上另一端的确认逻辑,否则库存对不上账。
我一般会拿一张纸,把每个参与者横向列开,纵向写他能触发的全部用例,画成矩阵。矩阵里出现空格的地方,就是权限控制的边界;出现一个用例被两个角色共享的地方,就是需要做角色继承或权限复用的地方。这一步做完,后面建 user、role、permission 三张表基本不用改。
2.2 包含、扩展、泛化:三种关系对应的代码结构
用例图里最容易被画错也最容易被读错的是关系线。这份资源里出现了包含(include)、扩展(extend)和泛化(generalization)三类关系,我逐个说它们落到代码里长什么样。
包含关系表示“每次执行 A 都必然执行 B”。典型例子是“创建销售单”包含“计算应收金额”和“扣减库存”。落到代码里,这不是三个独立接口,而是一个销售单创建方法内部依次调用计价服务和库存服务。看到包含线,你就知道这两个动作应该放在同一个事务里,不能拆成前端两次请求。
扩展关系表示“B 只在特定条件下才发生”。典型例子是“创建销售单”扩展出“应用会员折扣”。扩展点是有条件的,代码里对应的是策略模式或者 if 分支,而不是无条件调用。很多实现把折扣逻辑硬编码进主流程,结果非会员单也走一遍折扣计算,这就是没读懂扩展关系的语义。
泛化关系在用例图里表现为一个用例指向另一个用例的三角箭头,表示特殊与一般。比如“支付”是父用例,“现金支付”“扫码支付”“银行卡支付”是子用例。落到代码里就是支付接口加多个实现类,前端根据支付方式下拉框选择走哪个实现。如果你在用例图里看到支付被拆成了三个平级的用例而没有泛化线,说明建模的人没做抽象,你实现时最好自己补一个支付策略接口,否则后面加一种支付方式就要改三处。
提示:读用例图时先数关系线再数用例框。关系线数量往往比用例数量更能反映作者对业务的理解深度,一个只有用例框没有关系线的图,基本等于没画。
2.3 用例描述表:把图翻译成字段和接口
光有图形没法直接写代码,中间缺一层用例描述。这份资源如果只给了图,你需要自己补一张用例描述表,我按经验给一个可直接套用的模板,以“创建采购单”为例:
| 项目 | 内容 |
|---|---|
| 用例名称 | 创建采购单 |
| 参与者 | 采购员 |
| 前置条件 | 采购员已登录且拥有采购权限;供应商档案已存在 |
| 主流程 | 选择供应商 → 添加商品行(商品、数量、进价)→ 系统计算总金额 → 提交生成采购单 |
| 扩展流程 | 商品不存在时提示先维护商品档案;数量为 0 时阻止提交 |
| 后置条件 | 采购单状态为“待审核”,库存不变 |
| 涉及数据 | 采购单主表、采购单明细表、供应商表、商品表 |
这张表的价值在于,主流程的每一步几乎对应一个接口参数或一次数据库写入,扩展流程对应校验逻辑和错误码,后置条件对应事务提交后的状态字段。你把四个核心用例(销售开单、采购入库、库存盘点、退货)各填一张,数据库表结构基本就浮出来了。我见过太多人跳过这一步直接建表,结果表建完发现少了一个“单据状态”字段,回头改迁移脚本改到崩溃。
3. 从用例到表结构:进销存三张核心单据的建模顺序
3.1 先建商品与库存,再建单据
建表顺序错了,外键就挂不上。正确的顺序是:商品档案 → 库存 → 单据。商品表是全局主数据,库存表是商品在当前仓库的存量快照,单据表才引用前两者。
商品表至少要有商品编码、名称、条码、分类、规格、单位、进价、售价、状态。条码要加唯一索引,因为收银扫码靠它定位商品。库存表的核心是商品 ID 加仓库 ID 加当前数量,再补一个安全库存下限用于预警。这里有个血泪经验:库存数量不要用“查出来再加减再写回”的方式更新,并发下必超卖,正确做法是用一条带条件的 UPDATE:
-- 扣减库存:只有当前库存足够时才扣,影响行数为 0 说明库存不足 UPDATE stock SET quantity = quantity - #{num} WHERE product_id = #{productId} AND warehouse_id = #{warehouseId} AND quantity >= #{num};这条语句执行后检查受影响行数,等于 0 就直接抛“库存不足”业务异常,整个销售单事务回滚。参数num是本次出库数量,productId和warehouseId定位到具体库存记录。不要先 SELECT 再判断再 UPDATE,那中间的时间窗口足够另一个请求把库存扣光。
单据表分主表和明细表。销售单主表存单号、收银员、会员、应收金额、实收金额、支付方式、状态、创建时间;明细表存单据 ID、商品 ID、数量、单价、小计。采购单结构对称,只是把收银员换成采购员、售价换成进价。退货单建议单独建表而不是用负数销售单,因为退货需要记录原单号和回库确认状态,混在销售单里字段会越来越脏。
3.2 单据状态机:别用一个 status 字段糊弄
进销存系统里最容易被低估的是单据状态。采购单从创建到入库,中间要经过待审核、已审核、部分到货、已入库、已取消几个状态;销售单有已开单、已支付、已退货。如果你只用一个 status 字段存字符串,后面加状态就要改代码里的所有判断。
我一般会单独建一张状态流转表或者在代码里用枚举加状态机。以采购单为例,合法流转是:待审核 → 已审核 → 部分到货 → 已入库,任意未入库状态可以转已取消。代码里用一个 Map 维护“当前状态 → 允许的下一状态集合”,每次变更前校验:
# 采购单状态流转校验,非法流转直接拒绝 TRANSITIONS = { "PENDING": {"APPROVED", "CANCELLED"}, "APPROVED": {"PARTIAL", "RECEIVED", "CANCELLED"}, "PARTIAL": {"RECEIVED"}, "RECEIVED": set(), "CANCELLED": set(), } def change_status(order, target): if target not in TRANSITIONS[order.status]: raise ValueError(f"非法状态流转: {order.status} -> {target}") order.status = targetTRANSITIONS字典就是状态机的全部规则,change_status在每次状态变更前做一次白名单校验。这样做的好处是,后面业务方说“已审核的单子也要能取消”,你只改字典里一个集合,不用满项目搜if status ==。参数order是采购单实体,target是目标状态字符串,异常信息里带上原状态和目标状态,排查时一眼能看出是哪条流转被拒了。
3.3 盘点单与库存调整的差异处理
盘点是最容易写错的一块。很多实现把盘点结果直接 UPDATE 库存数量,这是错的。盘点应该生成一张盘点单,记录账面数量、实盘数量、差异数量,审核通过后才生成一条库存调整记录去改库存。这样做的原因是可追溯:月底对账发现库存少了,你能查到是哪次盘点调的、谁调的、差异多少。
盘点单表结构建议:盘点单号、仓库、盘点人、盘点时间、状态;明细表:商品、账面数量、实盘数量、差异。审核时遍历明细,差异不为 0 的生成库存流水,流水表记录变更前数量、变更后数量、变更类型(盘点调整)、关联单据号。库存流水这张表平时看着多余,出问题的时候就是黑匣子,后悔药全靠它。
4. 权限与并发:用例图没画但上线必踩的两件事
4.1 按参与者做接口级权限,别只做菜单隐藏
用例图告诉你谁能做什么,但前端隐藏菜单不等于后端安全。收银员看不到“采购管理”菜单,不代表他不能直接调采购接口。合格的权限设计是接口级校验:每个接口标注所需权限码,网关或拦截器统一校验。
权限模型用经典的用户-角色-权限三层。用户表、角色表、权限表,加两张关联表。权限码按“模块:动作”命名,比如sale:create、stock:check、purchase:approve。拦截器从登录态里取出用户权限集合,比对接口注解上的权限码:
// 接口级权限注解,value 为权限码 @RequirePermission("purchase:approve") @PostMapping("/purchase/{id}/approve") public Result approve(@PathVariable Long id) { purchaseService.approve(id); return Result.ok(); }拦截器解析@RequirePermission的 value,判断当前用户权限集合是否包含该码,不包含返回 403。参数id是采购单主键,approve内部还要再校验单据当前状态是否为待审核,权限和状态是两道独立的闸。这里常见的翻车是只做了角色判断没做权限码,结果店长和采购员共用一个角色,采购员莫名其妙能审批自己的单子。角色可以粗,权限码必须细。
4.2 销售开单的并发扣库存与幂等
收银高峰期同一商品被多个收银台同时卖出是常态。前面 3.1 的条件 UPDATE 解决了超卖,但还有一个问题:网络抖动导致收银员重复点击提交,同一单被创建两次,库存扣两次。解决办法是幂等键。
前端每次打开开单页生成一个 UUID 作为请求幂等键,随创建请求一起提交。后端在销售单表上给幂等键加唯一索引,插入时如果冲突就返回已存在的单据,不再重复扣库存。代码上就是捕获唯一键冲突异常后查一次原单返回:
def create_sale(payload, idempotent_key): try: order = sale_repo.insert(payload, idempotent_key) except DuplicateKeyError: # 幂等键已存在,说明是重复提交,直接返回原单 return sale_repo.find_by_idempotent_key(idempotent_key) stock_service.deduct(order) return orderidempotent_key由前端生成并保证同一开单会话内不变,insert和deduct必须在同一事务里,否则可能出现单据插入成功但库存没扣的中间态。这个细节用例图里不会画,但它是收银场景能不能用的分水岭。
5. 避坑排查:用例图落地时最常见的五个翻车点
现象一:按用例图建完表,发现退货单没有字段记录原销售单。原因是用例图里“退货”和“销售”被画成两个独立用例,没有关联线,建模时没体现退货必须引用原单。解决办法是退货单主表加origin_order_id字段,退货明细按原单明细逐行勾选,退款金额不能超过原单实收。补这个字段越早越好,等数据跑起来再加迁移脚本,历史退货单只能填空值。
现象二:库管员能看到采购单但点不了审核,报 403。原因是权限码配到了角色上,但库管员角色没继承采购相关权限,而用例图里“到货验收”这个用例同时连了库管员和采购员。解决办法是检查用例图里共享用例的角色,在权限表里给两个角色都挂上对应权限码,或者抽一个“仓储操作”公共角色让两者继承。别用给库管员临时加管理员角色来糊弄,后面权限审计一团糟。
现象三:盘点后库存数量对了,但库存流水里查不到调整记录。原因是盘点审核逻辑直接 UPDATE 了库存表,漏了写流水。解决办法是把“改库存”和“写流水”封装成一个库存服务方法,任何地方改库存都必须走这个方法,禁止直接操作 stock 表。代码评审时把直接 UPDATE stock 的写法列为红线。
现象四:销售单金额和明细合计对不上,差几分钱。原因是单价用浮点数存储,多行累加产生精度误差。解决办法是金额字段统一用 DECIMAL(10,2) 或整数分存储,计算时用 BigDecimal 或整数运算,最后再格式化。这个坑在用例图阶段完全看不出来,但收银对账时天天被投诉。
现象五:采购单已入库,但库存没增加。原因是用例图里“到货验收”和“入库登记”被画成两个用例,实现时只做了验收改状态,忘了入库加库存。解决办法是回到用例描述表,确认“入库登记”的后置条件写的是“库存增加”,然后在验收通过的回调里显式调用入库服务。凡是后置条件涉及数据变更的用例,实现完必须对着表逐条验证。
6. 把用例图变成验收清单:一套可复用的自检流程
用例图画完不是终点,它最好的用法是变成验收清单。我的习惯是,每完成一个模块,就把对应用例的参与者、主流程、扩展流程、后置条件逐条拉出来,当成测试用例跑一遍。比如销售模块,收银员登录后开单、挂单、恢复挂单、退货,每条主流程走通,每条扩展流程(库存不足、商品下架、会员折扣失效)都手动触发一次,后置条件核对库存流水和单据状态。这套流程走下来,能挡掉八成上线后的低级问题。
再往深一层,用例图还能反推接口文档。每个用例对应一组接口,主流程的每一步对应请求参数,扩展流程对应错误码,后置条件对应响应里的状态字段。我一般会维护一张对照表:
| 用例 | 接口 | 关键参数 | 主要错误码 |
|---|---|---|---|
| 销售开单 | POST /sale | 商品行、支付方式、幂等键 | 库存不足、商品下架 |
| 采购入库 | POST /purchase/receive | 采购单号、实收数量 | 状态非法、数量超订单 |
| 库存盘点 | POST /stock/check | 仓库、盘点明细 | 商品不存在、重复盘点 |
这张表填满,前后端联调基本不用来回扯皮,测试也能照着表写用例。我吃过一次亏,早期接一个零售项目,用例图只画了主流程,扩展流程全靠口头沟通,结果退货时会员积分回退逻辑漏了,上线一周被财务追着对账。从那以后我每次拿到用例图,都强制先把扩展流程和后置条件补成清单,再动一行代码。希望这份资源和这套读法,能帮你少走一遍我走过的弯路。
本文还有配套的精品资源,点击获取