news 2026/10/20 5:50:23

超市进销存系统用例图设计:从角色权限到表结构与代码落地

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
超市进销存系统用例图设计:从角色权限到表结构与代码落地

简介:面向超市运营与管理系统学习者,这份资源提供完整的进销存系统源码与设计文档。系统覆盖商品进货、销售管理、库存统计、订单追踪等核心模块,配套用例图清晰展示员工、顾客、供应商与系统间的交互关系,并定义管理员、收银员、采购员等角色权限,便于理解业务流程与权限设计。压缩包为rar格式,共103个文件,其中18个aspx页面负责界面展示,24个C#源文件实现业务逻辑,全套CSS样式控制前端样式,大量jpg/gif为界面截图与操作演示,另有mdf/ldf数据库文件及mdl设计模型,从页面到数据层完整覆盖,整体仅2.03MB,结构紧凑。已有4379人学习下载,适合用作课程设计、毕业设计参考或中小型超市信息化改造的起步模板。通过源码和用例图,读者可快速掌握进销存系统的分层实现思路,并直接复用进货、销售、库存预警等关键功能代码。

1. 超市进销存管理系统:先说你为什么要关心用例图

看到「超市进销存管理系统(附用例图)」这个标题,大多数人的第一反应是:进销存不就是加个库存、记个流水吗,用例图不过是毕业设计凑页数的东西。但真正做过超市后台系统的人会告诉你,进销存是典型的「看着简单、做起来全是坑」的业务。收银台扫一秒条码,背后要扣库存、记流水、算毛利、同步会员价;晚上盘点时账面和实物对不上,又说不清是漏单还是退货没入库。而用例图这个看似静态的图,恰恰是梳理这些脏活累活的最好起点——它把「谁、在什么场景下、对系统做什么」这件事提前讲清楚,避免开发到一半才发现采购、店长、收银员对「入库」的理解完全不一样。这篇文章不聊理论,直接带你从用例图出发,把超市进销存系统的角色、用例、表结构和关键代码一步步拆开,让你能照着落地一套能跑、能扛并发、报表对得上的系统。

2. 进销存系统的核心用例拆解:从收银台到库存账本

2.1 三个核心角色与他们的权限边界

超市进销存系统的用例图,第一个要确定的不是画几个圈,而是角色。我见过很多项目把角色画成「管理员」「员工」两个大圈,结果权限全都揉在一起,最后收银员能改进货价,店长看不到毛利,整个系统成了摆设。常见做法是先按业务动作拆角色:收银员、采购员/仓管员、店长/经理,必要时再加一个系统管理员。每个角色对应一组用例,角色之间可以有继承关系,但权限边界必须清晰。

收银员的核心用例是「销售结算」「退货处理」「查看当日小票」。这里要注意,收银员不应该有「改商品价格」的权限,最多只能申请折扣,折扣幅度超过阈值要店长授权。采购员或仓管员负责「商品入库」「库存盘点」「报损出库」,他们要能录入进货单,但看不到销售毛利这类经营数据。店长拥有「查看销售报表」「查看库存报表」「设置促销价」「管理员工账号」等用例,属于高层级角色,可以继承收银员和采购员的查看权限,但不建议继承操作权限——否则店长误操作退货单,追责时很麻烦。

系统管理员在超市场景里一般不直接参与业务,只负责初始化商品分类、维护系统参数、备份数据。画用例图时,系统管理员不要跟店长合并,因为「维护系统」和「经营超市」是两个不同维度的事。用例图的价值就是把这些边界提前画出来,让数据库设计和接口权限设计都有据可依。很多团队翻车,就是跳过了这一步,直接在代码里写 if (user.role == "admin"),最后角色一多,代码全是分支判断,改都改不动。

2.2 用例图怎么画:把「补货」拆成看得懂的流程

纸上画用例图时,很多人会把「进货」「补货」「入库」混成一个用例,这是最常见的错误。对超市业务来说,「采购下单」和「到货入库」是两件事,执行人完全不同:采购员负责下单,仓管员负责收货。如果你画成同一个用例,数据库里就少了一张采购单表,后期想查「这批货是哪个供应商发的、谁验收的」就全得靠翻纸质单据。

我一般建议用用例描述表来辅助画图,而不是直接画圈。以「商品入库」为例,用例名称写「商品入库」,主执行者是仓管员,前置条件是「存在已审核的采购单」,后置条件是「库存数量增加且生成入库流水」。基本流程是:仓管员扫描到货商品条码 → 系统匹配采购单 → 录入实收数量 → 确认入库 → 系统更新库存并记录操作人。异常流程包括:到货数量与采购单不一致、商品条码无法识别、采购单尚未审核。把这些写清楚,再画用例图,每个圈的边界自然就清晰了。

具体画图时,用 UML 用例图的三个要素:角色(小人)、用例(椭圆)、关系(连线)。角色之间用泛化关系,用例之间用 include/extend 关系。比如「销售结算」一定会 include「验证会员信息」;「退货处理」会 extend「重新入库」,因为退货后的商品可能重新上架也可能报损。把这些关系标出来,开发时拆接口就有了依据。下面用一个简化的用例清单表展示超市进销存的核心用例,方便你对照自己的图。

角色核心用例关键前置条件关键后置条件
收银员销售结算商品已上架、库存充足库存扣减、生成销售流水
收银员退货处理存在原销售小票库存回补或报损
采购员采购下单供应商信息完整生成待审核采购单
仓管员商品入库采购单已审核库存增加、生成入库流水
仓管员库存盘点盘点期间停止该区域销售生成盘盈盘亏单
店长查看经营报表当日已结账展示毛利、销量、库存周转

用例图画到这一步基本就能用了。要注意的是,用例图不是流程图,不需要画箭头表示先后顺序,它只表达「谁能用系统做什么」。如果你把箭头画成流程,说明你还没分清用例和活动图。画完图之后,最好找业务方(比如超市店长)走一遍用例描述表,确认「入库」流程里到底是先扫条码还是先选采购单,这些细节直接决定页面表单的字段顺序,也决定数据库表的外键设计。

3. 用用例图驱动数据库设计:把图上的圈变成三张表

3.1 从「商品管理」用例到 goods 表

用例图画完,数据库设计就不是拍脑袋了。每个核心用例背后必然对应一张或几张表。以「商品管理」为例,它包含新增商品、修改价格、上下架、设置条码等子功能,落到数据库就是一张 goods 商品表。这张表设计的核心不是字段多,而是把「商品」和「SKU」分清——超市里一包方便面和一个整箱方便面,扫码价格不同,库存单位也不同,如果只存在一张表里,促销和盘点都会乱。

常见的做法是设计两张表:spu(标准产品单元)和 sku(库存单位)。小超市可以不拆这么细,但至少要在 goods 表里加上 barcode(条码)、sale_price(售价)、cost_price(成本价)、stock(库存)、status(上下架状态)这些字段。注意成本价和售价必须分开,因为毛利计算依赖成本价,促销折扣依赖售价。很多开发者图省事只存一个 price,后期要算毛利就只能用售价代替,导致报表里的毛利虚高,店长一看就不认账。

还有条码字段,建议加唯一索引。超市商品条码分为 EAN-13 和店内码,同一种商品可能有多个条码(比如促销装和普通装)。如果不用唯一索引,收银台扫到重复条码时会弹商品选择框,严重影响收银效率。这里给一个简化的 goods 表建表语句,spu/sku 拆分在小型系统里可选,但条码唯一索引一定要加。

CREATE TABLE goods ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '内部ID', spu_code VARCHAR(32) NOT NULL COMMENT '商品编码(同款商品共用)', sku_code VARCHAR(64) NOT NULL COMMENT 'SKU编码(每个规格唯一)', barcode VARCHAR(20) NOT NULL COMMENT '条码(EAN-13或店内码)', name VARCHAR(128) NOT NULL COMMENT '商品名', category_id BIGINT NOT NULL COMMENT '分类ID', sale_price DECIMAL(10,2) NOT NULL COMMENT '售价', cost_price DECIMAL(10,2) NOT NULL COMMENT '成本价', stock INT NOT NULL DEFAULT 0 COMMENT '当前库存', status TINYINT NOT NULL DEFAULT 1 COMMENT '1上架 0下架', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_barcode (barcode), KEY idx_category (category_id) ) COMMENT='商品表';

建表逻辑里最关键的是把 barcode 设为唯一键,这是从「收银员扫码结算」这个用例反推出来的。同时把sale_price和cost_price都定义为DECIMAL(10,2),避免用 float 导致金额精度丢失。接下来你会看到,商品表只是第一张地基表,真正容易出问题的是库存和流水表,它们都跟具体用例一一对应。

3.2 从「销售结算」用例到订单与流水表

「销售结算」是超市系统最高频的用例,它对应的表设计直接决定系统能不能撑住晚高峰。很多初学者只建一张 orders 表,把商品明细用逗号拼成一个字符串塞进去,这在演示项目里能跑,但一到真实场景就废了——退货时查不到单品,报表算不出品类销量,库存扣减也没法追溯。正确做法是拆成两张表:sales_order(销售主表)和 sales_order_item(销售明细表),再加一张 payment_log(支付流水表)。

销售主表记录一次结算的整体信息:订单号、收银员 ID、会员 ID(可空)、应收金额、实收金额、折扣金额、支付方式、结算时间。销售明细表记录每件商品:商品 ID、条码、商品名快照、单价、数量、小计金额。这里的重点是把商品名和单价做成快照字段,而不是靠 join goods 表去实时查。因为商品今天可能改价、明天可能下架,如果报表里要还原三个月前的订单,join 出来的价格会变成现在的新价格,账就对不上了。

payment_log 表则记录每一笔支付流水,包括支付单号、订单号、支付金额、支付渠道、支付状态。为什么要单独一张表?因为实际收银时常出现「扫码枪扫了商品,但顾客说等等,我要加一袋盐」,这时订单还没确认,不能写流水;等结算完成,再插入一条支付流水。如果把这逻辑塞进 orders 表,开发时就会被迫把「未支付订单」也写进主表,导致查询性能变差。下表是三张表的分工对照。

表对应用例中的动作关键记录内容常见错误
sales_order销售结算(主流程)订单号、实收金额、收银员漏掉订单号,直接用自增ID
sales_order_item销售结算(明细分录)商品快照、单价、数量不存快照,只存商品ID
payment_log支付流水支付单号、渠道、状态把支付状态直接塞订单表

订单号建议用「日期 + 收银机号 + 流水号」生成,比如20250720123001,不要用自增 ID 直接当订单号,否则顾客拿小票来退货时,收银员根本没法口述订单号。此外,涉及到金额的字段全部用 DECIMAL,这是进销存系统中最不值钱但最要命的教训——用 float 存金额,累计几十万笔后报表就会莫名多出几分钱,财务查起来想砸电脑。

3.3 从「库存盘点」用例到库存变动记录

库存盘点用例是超市系统里最能体现「用例图驱动设计」价值的地方。如果只画了「库存盘点」一个圈而不拆子用例,你很容易只建一张 inventory 表记录当前库存,盘点时直接 UPDATE 库存字段。这样做表面能跑,但一旦出现盘亏(账面多、实物少),你就永远不知道这批货是丢了、被盗了、还是上次入库时数量录错了。可靠的做法是增加库存变动流水表 stock_flow,每次入库、出库、报损、盘点调整都写一条流水,当前库存只是流水的汇总结果。

stock_flow 表字段包括:变动类型(入库/销售出库/退货入库/报损/盘点调整)、商品 ID、变动数量(正负)、关联单据号(对应采购单/销售单/盘点单)、操作人、备注。每次操作都在一个数据库事务里同时更新 goods.stock 和插入 stock_flow。盘点时,系统先冻结当前账面库存,生成盘点单,仓管员录入实物数量,系统自动算出差额,并以「盘点调整」类型生成流水。这样做的最大好处是:任何库存变化都可回溯,月底对账时直接按流水逐笔核对。

CREATE TABLE stock_flow ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '流水ID', goods_id BIGINT NOT NULL COMMENT '商品ID', change_type TINYINT NOT NULL COMMENT '1入库 2销售出库 3退货入库 4报损 5盘点调整', change_qty INT NOT NULL COMMENT '变动数量(正负)', ref_no VARCHAR(64) NOT NULL COMMENT '关联单据号', operator_id BIGINT NOT NULL COMMENT '操作人ID', remark VARCHAR(255) DEFAULT NULL COMMENT '备注', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, KEY idx_goods_time (goods_id, created_at) ) COMMENT='库存变动流水表';

这张表的change_qty用正负表示方向:入库为正,销售出库为负,盘点调整可能是正也可能是负。ref_no关联到具体单据,让每一笔变动都能找到原单据。设计时要注意「当前库存」不能直接拿来当唯一事实,因为并发场景下直接更新一个字段很容易丢更新;流水表加 goods_id 和 created_at 联合索引,按月归档可以避免单表数据膨胀。到这里,用例图上的「销售结算」「商品入库」「库存盘点」都变成了具体的表,接下来就可以写代码了。

4. 按用例图落地系统:最小可运行的技术选型与关键代码

4.1 技术栈与目录结构

如果你是自己学习或给某社区超市做内部系统,技术栈不必追新。我常用的组合是:Spring Boot + MyBatis-Plus + MySQL + Redis(仅做缓存和分布式锁)。前端用一个简单的管理后台框架即可,甚至可以用服务端渲染页面。超市进销存的瓶颈不在技术炫技,而在事务一致性和并发控制,所以我建议把核心代码放在后端,前端保持轻量。

目录结构按用例图来分包,而不是按「controller/service/dao」三层硬分。常见的做法是:

src/main/java/com/example/supermarket/ ├── module/ │ ├── goods/ # 商品管理用例 │ ├── purchase/ # 采购用例 │ ├── stock/ # 入库/盘点/报损用例 │ ├── sale/ # 销售结算/退货用例 │ ├── report/ # 报表查看用例 │ └── auth/ # 登录/权限 ├── common/ # 统一结果、异常、工具类 └── config/ # 数据源、Redis、事务配置

按用例模块分包的好处是:改采购逻辑时不会误碰销售代码,新成员接手也能从包名直接看出系统有哪些业务。注意这里没有把「角色管理」单独拉出来,因为超市系统的账号权限比较简单,用一个 auth 模块就够了。目录结构定了以后,下面两个关键代码段是几乎每个进销存系统都跑不掉的:登录权限拦截和库存扣减事务。

4.2 登录与权限:用例图里的「登录」圈怎么变成代码

用例图里每个角色都要执行「登录」用例,但这个用例如果只做用户名密码校验,后面所有接口都要重复写权限判断。我在实际项目中会用一个拦截器 + 注解的方式,把用例图中的角色边界直接映射到代码。首先定义一个枚举表示角色:

public enum Role { CASHIER(1, "收银员"), PURCHASER(2, "采购员"), WAREHOUSE(3, "仓管员"), MANAGER(4, "店长"), ADMIN(5, "系统管理员"); public final int level; public final String desc; Role(int level, String desc) { this.level = level; this.desc = desc; } }

然后定义一个@RequireRole注解,标注在 Controller 方法上。比如「销售结算」接口只允许收银员和店长访问,「采购下单」接口只允许采购员访问。拦截器里判断当前登录用户的角色 level 是否大于等于注解要求的 level。为什么用 level 而不是用角色名精确匹配?因为店长要继承收银员和采购员的查看权限,用 level 表示大小关系,代码写起来最省事。

@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RequireRole { Role value() default Role.CASHIER; }

拦截器的核心逻辑如下:从 ThreadLocal 取出当前登录用户(登录成功时放入),再拿到注解的角色要求,比较 level。注意这里有个坑:店长能操作收银功能,但收银员不能操作店长的报表功能,所以判断方向不能反。很多翻车现场就是比较符号写反,导致店长被 403,收银员却能访问报表。建议在拦截器里加上日志输出,方便排查。

public class RoleInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { if (handler instanceof HandlerMethod) { HandlerMethod hm = (HandlerMethod) handler; RequireRole requireRole = hm.getMethodAnnotation(RequireRole.class); if (requireRole == null) { return true; } LoginUser user = UserContext.get(); if (user == null) { throw new BizException("未登录"); } if (user.getRole().level < requireRole.value().level) { throw new BizException("无权访问"); } } return true; } }

这段代码里用 level 比较,隐含的逻辑是「高等级角色可以访问低等级角色的接口」。但注意,这并不完全等价于角色继承,因为用例图里店长虽然能查看报表,可有些极端操作(比如删除流水)不应该开放给任何角色。所以我在@RequireRole之外还加了一个@AdminOnly注解用于系统初始化和数据修复接口,避免所有权限都靠 level 一路放通。

4.3 进货入库与销售出库:事务与库存扣减的临界点

库存扣减是进销存系统最核心的代码,也是并发问题最多的地方。假设收银台同时扫了两件同一商品,两个请求同时执行UPDATE goods SET stock = stock - 1 WHERE id = ?,如果没有锁,数据库的行锁会让两个更新串行执行,结果一般没问题。但如果你先SELECT stock再在 Java 里减 1 再UPDATE,就会出现著名的丢失更新——两个请求都读到 10,都算成 9,然后分别写回 9,库存从 10 变成了 9,少扣了 1 件。

所以销售出库的正确姿势是使用原子更新,或者加悲观锁。最常见的写法是UPDATE goods SET stock = stock - #{qty} WHERE id = #{goodsId} AND stock >= #{qty},用受影响行数判断是否扣减成功。这个 SQL 把判断和扣减放在一条语句里,数据库行锁保证同一时刻只有一个人能更新成功,配合事务实现原子性。下面这段代码是销售结算时扣减库存的最小实现:

@Transactional(rollbackFor = Exception.class) public void settleSale(SaleCommand cmd) { // 1. 校验并扣减库存,使用原子更新防止超卖 for (SaleItem item : cmd.getItems()) { int updated = goodsMapper.deductStock(item.getGoodsId(), item.getQty()); if (updated == 0) { throw new BizException("商品库存不足: " + item.getGoodsId()); } } // 2. 保存销售主表和明细表,快照商品价格 SaleOrder order = buildOrder(cmd); saleOrderMapper.insert(order); for (SaleItem item : cmd.getItems()) { saleOrderItemMapper.insert(buildItem(order.getId(), item)); } // 3. 记录库存变动流水 for (SaleItem item : cmd.getItems()) { stockFlowMapper.insert(buildFlow(item.getGoodsId(), 2, -item.getQty(), order.getOrderNo())); } }

这里每一步都不能乱。先扣库存,再写订单,最后写流水,顺序不能反——因为如果先写订单再扣库存失败,订单已经落库,回滚时会带着订单一起回滚,倒是没问题;但要防止的是,扣库存成功后写订单失败抛出异常,事务回滚会把库存扣减也回滚,所以整个方法必须加上@Transactional。deductStock的 SQL 长这样:

UPDATE goods SET stock = stock - #{qty} WHERE id = #{goodsId} AND stock >= #{qty}

注:stock - #{qty}如果 qty 为负,等于加库存,所以调用方必须保证 qty 为正整数。deductStock返回受影响行数,如果库存不足则返回 0,代码里抛出异常触发回滚。这里没加悲观锁SELECT ... FOR UPDATE,因为原子更新在大多数场景下已经足够。但如果要支持「先锁定库存、稍后结算」的复杂场景(比如扫码枪扫完商品但顾客还没付款),就需要引入 Redis 分布式锁或预占库存表,那是另一个进阶话题。

5. 进销存系统避坑指南:库存负数、并发超卖、报表对不上

5.1 库存变成负数:掩码与事务隔离级别

现象:某商品库存余额变成了 -3,但销售明细里所有订单数量都正常,找不出哪一笔扣错了。原因通常是代码里用了「先查再改」的更新方式,并且事务隔离级别是 Read Committed,两个事务同时读到库存 5,各自减 3 后都写回 2,实际库存变成 2,库存明细却显示共卖出 6,账面应为 -1。解决方法是把扣减 SQL 改成原子更新,并且给 goods 表的 stock 字段加上约束。很多数据库允许给字段加非负约束,但如果历史数据已经有负数,加约束会失败,所以更通用的做法是在应用层把deductStock的 SQL 写死AND stock >= #{qty}。

另外,事务隔离级别要确保是 READ COMMITTED 或更高。如果用的是 MySQL 默认的 REPEATABLE READ,快照读会读到旧数据,但原子更新走的是当前读,所以问题不大。真正要小心的是在事务里先 SELECT 再 UPDATE,这种写法在任何隔离级别下都可能丢更新,必须改成 UPDATE 影响行数判断。建议在开发环境直接开启 MyBatis 的 SQL 日志,打印出完整 SQL,排查时能看到是不是走了SELECT加UPDATE。

5.2 并发销售导致超卖:乐观锁与悲观锁怎么选

现象:促销活动开始时,100 件库存卖了 350 单,数据库里库存变成 -250。原因就是节点收到大量请求,每个请求都先 SELECT 库存,判断 >0 后执行扣减,但扣减没有条件,最后全部写成功。解决方法是前面提到的原子更新加stock >= #{qty}条件。这个方案本质是乐观锁——用「受影响行数=0」来判断冲突,不额外加锁,性能最好。但如果你的场景是「先下单、15 分钟内支付」的电商式订单,库存锁定时间长,乐观锁会导致支付时才发现库存被抢没了,用户体验不好。

超市收银场景属于即时扣减,用乐观锁完全够。而如果你在开发盘点模块,需要先冻结库存,防止盘点过程中有销售单改库存,那就得用悲观锁SELECT ... FOR UPDATE锁住商品行,等盘点结束再释放。我给出的建议是:进销存系统 90% 的库存变化都用原子更新;只有涉及多步骤业务(采购审批、盘点冻结)时才引入悲观锁和状态机。不要一上来就套 Redis 分布式锁,超市系统规模通常不至于打到单商品每秒几千次的并发,把数据库事务用好才是正路。

5.3 报表金额对不上:舍入规则和流水幂等

现象:店长看到「今日销售额」是 15678.89,但财务收银系统合计是 15678.90,差一分钱。原因通常是代码里用了 float 或 double 计算金额,累计多次结算后产生二进制浮点误差。解决方法是统一用BigDecimal,并且定义四舍五入规则。.setScale(2, RoundingMode.HALF_UP)是常见选择,但要注意不同业务字段的舍入规则可能不同:单品小计按数量乘单价,订单合计按小计累加,优惠分摊到每个商品时可能需要HALF_DOWN才能让合计不差一分钱。

这个问题我在某项目里排查了一下午,最后发现是订单合计先按每件商品折扣后金额相加,折扣金额本身却只保留两位小数,累加时误差被放大。建议所有金额计算放到一个MoneyUtil工具类里,禁止在业务代码里直接写加减乘除。流水幂等是另一个隐蔽问题:同一条销售单因为接口重试被插入两次,报表金额翻倍。解决方法是给 sales_order 加上唯一订单号索引,插入前先查订单号是否存在,或者直接用数据库唯一键拦截重复插入。

5.4 用例图画完但开发不认:颗粒度问题

现象:用例图画了 20 个圈,开发说太细,照着写代码反而被束缚;业务方说看不懂,圈和线太抽象。问题出在颗粒度:一张好的用例图不适合覆盖所有异常分支,而是聚焦「角色能做什么」。比如「销售结算」是一个用例,「退货处理」是一个用例,但「验证会员信息」就不要单独画成一个用例,它应该作为「销售结算」的 include 关系。画图时多问一句:这个用例对角色来说是不是一个完整的业务目标?如果是,就保留;如果只是中间步骤,就降级为关系。

另外,用例图不是需求文档本身。图上每个用例都要配套用例描述表(前置条件、主流程、异常流),开发照着描述表才能写出可测的代码。如果你画完图不写描述表,那图就是摆设,开发自然不认。我在实际项目中会把用例描述表放在和代码同一个仓库的 docs 目录下,版本跟着代码走,避免业务方改需求后文档和代码对不上。

6. 让用例图真正驱动开发:一张核对表和一个验收技巧

前面五章把用例图、表结构和关键代码串起来了,但最后你还是要回答一个问题:怎么验证这套系统真的按用例图实现了?我常用的方法是一张「用例-代码-表」核对表。把用例图上的每个用例列出来,标注对应的 Controller 接口、Service 方法、数据库表,以及测试用例编号。比如「商品入库」用例,对应 PurchaseInController、StockInService、purchase_order 和 stock_flow 表,测试用例要覆盖正常入库、数量不符、采购单未审核三种情况。这张表不仅是开发的自查工具,也是后来接手的人理解系统的地图。

验收时还有一个特别实用的技巧:站在业务方角度模拟一次完整的超市早班流程。早上收银员登录,扫描 20 件商品,其中一件参与促销,一件是会员价,结算后看库存是否减少;然后仓管员把到货 10 箱水入进系统,其中一箱外箱破损,做报损;店长在后台看当日毛利,把毛利额、销售流水合计、库存变动流水合计三方对平。只要这个流程走通,系统的基本盘就稳了。对不平时优先查流水,不要一上来就怀疑数据库坏了。

我自己的血泪经验是:进销存系统最大的坑往往不在技术,而在你对「超市业务」理解不够。用例图就是逼着你把业务先想明白的后悔药——画图时理清角色边界,建表时看清单据关系,写代码时守好事务底线。如果你现在正要动手做这个系统,我建议你先别急着敲代码,照着第 2 章的用例清单,找一家熟悉的小超市问清楚「收货时如果采购单数量对不上怎么办」「盘点时收银台还在卖货怎么办」,把这些答案写进用例描述表再开工。这套流程我给几个朋友团队用过,基本能把后期的返工量砍掉一大半。希望今天的这篇笔记能帮到你——哪怕只让画用例图时少走一个弯路,也值得了。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/20 5:50:22

生成式AI如何从工具变成精神秩序:反馈回路与边界设防

最近有位朋友跟我说了一句让我愣住的话&#xff1a;“现在我写什么东西之前&#xff0c;都会先问一遍AI&#xff0c;看到它的回答之后&#xff0c;心里才踏实。”他说得云淡风轻&#xff0c;但这句话我琢磨了很久。因为我自己也发现&#xff0c;生成式AI的使用方式正在悄悄变味…

作者头像 李华
网站建设 2026/10/20 5:49:18

PPT自动化生成实战:内容与样式分离的演示文稿工程化方案

1. 项目缘起与核心定位第一次看到“ppt-master”这个命名&#xff0c;我脑子里蹦出来的不是某个具体工具&#xff0c;而是一类需求&#xff1a;把做PPT这件事从“手工活”变成“可复用的工程”。过去几年我帮不少团队做过汇报材料&#xff0c;也见过太多人把大量时间耗在调字体…

作者头像 李华
网站建设 2026/10/20 5:48:47

artcraft 项目实战:素材管线与模板化生成的设计与实现

1. 从“artcraft”这个名字说起&#xff1a;它到底想解决什么问题第一次看到“artcraft”这个标题&#xff0c;我脑子里蹦出来的第一反应是&#xff1a;这大概率是一个把“艺术创作”和“手工实现”揉在一起的项目。art 是艺术&#xff0c;craft 是手艺、工艺&#xff0c;合在一…

作者头像 李华
网站建设 2026/10/20 5:48:35

从DOOM到自动化测试:AI Agent决策模型的迁移实践

“Jev”是我给我做的一个内部工具起的代号。它最早的任务是在 DOOM 里学会怎么从危机四伏的 3D 迷宫里存活下来&#xff0c;后来我却把它那套决策框架原封不动地搬到了自动化测试里&#xff0c;让它决定每次代码提交之后到底该跑哪些测试、跑多久、跑挂了该先救谁。整个迁移过程…

作者头像 李华
网站建设 2026/10/20 5:48:28

医疗管理系统CS+WinForm实战:架构、性能优化与踩坑指南

简介&#xff1a;这是一套基于C/S架构的医疗设备管理系统&#xff0c;采用WinForm开发&#xff0c;面向医院信息化建设者、.NET开发学习者以及需完成课设或毕设的学生。系统实现了设备信息管理、合同管理、配件管理、维修管理、信息统计和打印六大功能模块&#xff0c;内置管理…

作者头像 李华
网站建设 2026/10/20 5:48:28

用Python打造演示文稿生成工具:内容与样式分离的PPTX自动化实践

1. 为什么我要自己动手做一个演示文稿生成工具先说说我做这个项目的起因。日常工作里&#xff0c;演示文稿这件事几乎躲不开——项目汇报、方案评审、内部培训、客户提案&#xff0c;一年下来少说也要产出几十份。最开始我也是老老实实打开软件&#xff0c;一页一页地排版、调字…

作者头像 李华