资讯详情

资讯详情

超市进销存系统设计:用例图驱动与库存事务一致性实现

简介这份资源面向超市运营管理者、信息系统学习者及课程设计开发者提供一套覆盖商品进货、销售管理、库存统计与订单管理全流程的进销存系统实现并附系统用例图帮助理解员工、顾客、供应商等参与者与各功能模块的交互关系适合作为毕业设计、课程实践或小型超市信息化改造的参考。压缩包共103个文件约2.03MB以aspx页面、cs后台代码、jpg与gif界面截图为主辅以css样式、bmp与png图片、mdl模型文件及mdf、ldf数据库文件构成可运行的Web系统与配套素材。资源已有4380人学习下载读者可从中获得进货录入与采购订单生成、销售出库与发票处理、库存阈值预警与周转分析、订单状态追踪等模块的完整实现思路并借助用例图快速梳理系统功能边界与业务流程为开发与优化提供直观依据。1. 从一张用例图说起超市进销存到底要解决什么问题很多同学做超市进销存管理系统第一步就扎进数据库建表结果做到一半发现角色权限对不上、业务流程走不通返工成本极高。我一般建议先把用例图画出来——这张图不是交差用的装饰而是把「谁在什么场景下对哪些商品做了什么操作」这件事钉死的契约。超市进销存的核心矛盾其实很朴素商品从进货入库、货架销售到库存预警中间涉及采购员、收银员、库管员、店长四类角色每类角色能碰的数据边界必须清晰。这套方案面向的正是要交毕设或课程设计的同学以及需要给小型超市搭一套能跑起来的进销存系统的从业者用例图先行、模块后置、验收兜底三步走完就能交付。2. 需求梳理与用例建模四类角色、六条主用例怎么定2.1 角色划分与权限边界超市进销存最容易翻车的地方不是代码写不出来而是角色权限设计得太粗。我见过不少方案把「管理员」一个角色包打天下结果收银员能改进货价、库管员能删销售记录答辩时导师一问就露馅。合理的做法是按最小职责原则拆成四类角色每类角色只保留完成本职工作的用例。角色核心职责可操作用例数据可见范围采购员供应商管理、进货下单供应商维护、进货单录入、进货查询仅进货与供应商数据库管员入库验收、库存盘点入库确认、库存调整、库存预警查看库存与商品基础信息收银员前台销售、退货销售开单、退货处理、商品查询仅商品售价与库存数量店长全局监控、报表销售统计、库存报表、角色管理全部数据只读加审批这张表看着简单但它直接决定了后面数据库表的外键约束和接口的鉴权逻辑。采购员看不到销售毛利、收银员改不了进货单价这些边界在用例图里用「包含」和「扩展」关系标清楚写代码时就不会出现越权接口。2.2 六条主用例的流转关系用例图的价值在于把用例之间的依赖画明白。超市进销存的主干用例通常有六条供应商维护、进货单管理、入库确认、销售开单、退货处理、库存预警。它们不是并列的而是有先后依赖。进货单管理「包含」供应商选择入库确认「扩展」进货单只有审核通过的进货单才能入库销售开单「依赖」库存扣减退货处理「扩展」销售单。用 PlantUML 描述这层关系比手画更不容易漏startuml left to right direction actor 采购员 as P actor 库管员 as K actor 收银员 as C actor 店长 as M rectangle 超市进销存系统 { usecase 供应商维护 as UC1 usecase 进货单管理 as UC2 usecase 入库确认 as UC3 usecase 销售开单 as UC4 usecase 退货处理 as UC5 usecase 库存预警 as UC6 usecase 销售统计报表 as UC7 } P -- UC1 P -- UC2 UC2 .. UC1 : include K -- UC3 UC3 .. UC2 : extend C -- UC4 C -- UC5 UC5 .. UC4 : extend K -- UC6 M -- UC7 M -- UC6 enduml这段用例图描述里include表示进货单必然用到供应商数据extend表示入库和退货是在特定条件下才触发的扩展流程。画图时把箭头方向搞反是常见错误——扩展用例指向基础用例不是反过来。用例图定稿后每个用例对应一个后端接口组前端按角色渲染菜单权限校验在网关层统一拦截这套映射关系就是后面模块设计的骨架。2.3 从用例到功能清单的映射用例图画完不能就扔了要逐条翻译成功能清单和验收点。我的习惯是给每个用例补三列触发条件、前置数据、成功判定。比如「入库确认」的触发条件是进货单状态为「已审核」前置数据是进货明细和实际到货数量成功判定是库存表对应商品数量增加且生成入库流水。这三列写清楚开发时不会漏掉状态机答辩时也能直接对着讲。功能清单按模块归并后大致是基础数据模块商品、供应商、分类、进货模块进货单、审核、入库、销售模块开单、退货、小票、库存模块实时库存、盘点、预警、报表模块日销、库存周转、系统模块角色、用户、日志。六个模块对应六组接口每组接口的入参出参在用例的「前置数据」和「成功判定」里已经埋好了后面写 Controller 基本是填空题。3. 总体架构与数据库设计三层结构撑起六个模块3.1 技术选型与分层职责这套系统不需要微服务单体分层架构足够硬上微服务反而是给自己挖坑。我一般选 Spring Boot 做后端、Vue 或原生 HTML 加 Thymeleaf 做前端、MySQL 存数据三层结构划分清楚Controller 层只做参数校验和权限拦截Service 层承载业务状态流转Mapper 层管数据读写。分层的关键不是目录好看而是让「入库确认」这种跨表操作只出现在 Service 层Controller 里绝不出现两条以上的数据库写操作。选型理由说透一点超市进销存的并发量不高单体应用部署简单、调试直观毕设场景下导师要看的是业务闭环而不是分布式组件堆砌。数据库选 MySQL 是因为事务支持成熟进货入库和销售扣库存都依赖事务保证一致性这一点比换任何花哨框架都重要。3.2 核心数据表与字段设计数据库设计直接决定后面代码好不好写。超市进销存的表不用多但几张核心表的字段和约束必须一次到位。-- 商品表库存数量冗余在此避免每次查库存都聚合流水 CREATE TABLE product ( id BIGINT PRIMARY KEY AUTO_INCREMENT, barcode VARCHAR(32) UNIQUE NOT NULL COMMENT 条形码收银扫码用, name VARCHAR(64) NOT NULL, category_id BIGINT NOT NULL, purchase_price DECIMAL(10,2) NOT NULL COMMENT 进货价仅采购和店长可见, sale_price DECIMAL(10,2) NOT NULL, stock INT NOT NULL DEFAULT 0 COMMENT 当前库存入库加、销售减, warn_threshold INT NOT NULL DEFAULT 10 COMMENT 预警阈值, status TINYINT NOT NULL DEFAULT 1 COMMENT 1上架 0下架 ); -- 进货单主表状态机驱动草稿→已审核→已入库 CREATE TABLE purchase_order ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) UNIQUE NOT NULL, supplier_id BIGINT NOT NULL, operator_id BIGINT NOT NULL, total_amount DECIMAL(12,2) NOT NULL, status TINYINT NOT NULL DEFAULT 0 COMMENT 0草稿 1已审核 2已入库 3已作废, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ); -- 库存流水表所有库存变动留痕盘点对账靠它 CREATE TABLE stock_flow ( id BIGINT PRIMARY KEY AUTO_INCREMENT, product_id BIGINT NOT NULL, change_type TINYINT NOT NULL COMMENT 1入库 2销售 3退货 4盘点调整, change_qty INT NOT NULL COMMENT 正数增加负数减少, ref_order_no VARCHAR(32) COMMENT 关联单号, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP );这三张表的设计要点在于商品表冗余stock字段是为了收银时快速判断库存但每次变动必须同步写stock_flow否则对账时就是一笔糊涂账。进货单用status字段做状态机0 到 1 到 2 的单向流转在 Service 层用条件更新控制UPDATE ... WHERE status 0这种写法能天然防并发重复审核。stock_flow的change_qty用正负号区分增减比拆成两张表更省事盘点时SUM(change_qty)和商品表stock对不上就说明有漏写流水的 bug。3.3 库存扣减的事务边界销售开单是最容易出问题的地方。收银员点「结算」的瞬间要做三件事写销售单、扣商品库存、写库存流水。这三步必须在一个事务里任何一步失败全部回滚。我见过有方案先扣库存再写单结果写单失败库存白扣了只能手工补这就是事务边界没划对。Service public class SaleService { Transactional(rollbackFor Exception.class) public void createSale(SaleDTO dto) { // 1. 乐观锁扣库存stock 不足则影响行数为 0直接抛异常 int affected productMapper.deductStock(dto.getProductId(), dto.getQty()); if (affected 0) { throw new BizException(库存不足商品ID dto.getProductId()); } // 2. 写销售单 saleOrderMapper.insert(buildOrder(dto)); // 3. 写库存流水change_qty 为负 stockFlowMapper.insert(buildFlow(dto, -dto.getQty())); } }deductStock对应的 SQL 是UPDATE product SET stock stock - #{qty} WHERE id #{id} AND stock #{qty}把库存判断和扣减合成一条原子语句避免「先查后改」的竞态。Transactional的rollbackFor显式指定 Exception是因为默认只回滚运行时异常业务异常如果继承自 Exception 而不加这个参数事务不会回滚这是血泪经验。参数上qty必须做正整数校验负数传进来会变成加库存收银端要拦一道。4. 核心模块实现进货、销售、库存三条主线怎么落地4.1 进货入库的状态流转进货模块的难点不在增删改查而在状态流转的严谨性。一张进货单从草稿到入库中间隔着审核和实际到货核对任何一步跳过都会导致库存和账面对不上。我的做法是把状态机写死在 Service 层每个流转方法只允许从特定前置状态进入。public void auditPurchase(Long orderId, Long auditorId) { // 只有草稿状态可审核条件更新防重复提交 int rows purchaseOrderMapper.updateStatus(orderId, 0, 1, auditorId); if (rows 0) { throw new BizException(单据状态已变更请刷新后重试); } } public void confirmInbound(Long orderId, ListInboundItem items) { PurchaseOrder order purchaseOrderMapper.selectById(orderId); if (order.getStatus() ! 1) { throw new BizException(仅已审核单据可入库); } for (InboundItem item : items) { // 实际到货数量可能少于订货数量按实收入库 productMapper.addStock(item.getProductId(), item.getRealQty()); stockFlowMapper.insert(buildInboundFlow(order, item)); } purchaseOrderMapper.updateStatus(orderId, 1, 2, null); }updateStatus的 SQL 带上前置状态做条件UPDATE purchase_order SET status #{to} WHERE id #{id} AND status #{from}。这样即使两个库管员同时点审核也只有一个人成功另一个人收到「状态已变更」的提示。入库时按realQty而不是订货数量加库存是因为实际到货短少是常态按订货量入库会让库存虚高。这套状态流转在用例图里对应「入库确认扩展进货单管理」那条关系代码和设计图能对上答辩时讲起来有据可依。4.2 销售开单与退货的对称处理销售和退货是一对镜像操作设计时最好让它们共用一套库存变动逻辑只是方向相反。销售时库存减、流水记负数退货时库存加、流水记正数并且要校验原销售单存在且未全额退过。Transactional(rollbackFor Exception.class) public void returnGoods(String saleOrderNo, Long productId, int qty) { SaleOrder order saleOrderMapper.selectByNo(saleOrderNo); if (order null) throw new BizException(原销售单不存在); // 已退数量不能超过原销售量 int returned returnMapper.sumReturnedQty(saleOrderNo, productId); if (returned qty order.getQty()) { throw new BizException(退货数量超出原销售量); } productMapper.addStock(productId, qty); stockFlowMapper.insert(buildReturnFlow(saleOrderNo, productId, qty)); returnMapper.insert(buildReturnRecord(saleOrderNo, productId, qty)); }退货的坑在于「部分退货」和「重复退货」。sumReturnedQty先汇总该单该商品已退数量加上本次不超过原销量才放行这个校验必须在事务内做否则并发退货会超退。库存加回用addStock而不是直接 set保证和入库走同一条累加路径。退货流水关联原销售单号报表里能追溯每一笔退货的来源店长查账时不会抓瞎。4.3 库存预警与盘点调整库存预警不是定时任务扫全表那么简单商品多了以后全表扫描很慢。我的做法是在库存变动时顺带判断入库和销售扣减后如果stock warn_threshold就往预警表插一条记录店长端只查预警表。这样预警是事件驱动而不是轮询实时性更好。盘点调整则是另一条路径库管员实盘数量和系统数量不一致时差额通过change_type 4的流水修正商品表stock直接改成实盘数。这里要注意盘点流水必须记录调整前后的值否则事后无法解释库存为什么突变。预警阈值warn_threshold按商品设置而不是全局统一矿泉水和大件家电的安全库存显然不是一个量级这个字段放在商品表里比放配置表更合理。5. 避坑与常见问题五个最容易翻车的地方5.1 库存数量和流水对不上现象是商品表stock和stock_flow汇总值不一致盘点时差额越积越大。原因通常是某条库存变动路径漏写了流水比如直接UPDATE product SET stock ?而没走统一的变动方法。解决办法是把所有库存变动收敛到一个StockService.changeStock(productId, qty, type, refNo)方法里商品表和流水表在同一个事务内更新任何模块都不允许绕过它直接改库存。5.2 并发审核导致重复入库现象是同一张进货单被入库两次库存翻倍。原因是审核和入库接口没有做状态条件更新两个请求同时读到「已审核」状态都放行了。解决办法是UPDATE ... WHERE status #{from}条件更新加影响行数判断影响行数为 0 就抛异常。这个模式在审核、入库、作废三个动作上都要用不能只防一处。5.3 收银端看到进货价现象是收银员登录后商品列表里带出了进货价字段属于越权泄露。原因是后端接口返回了整个商品实体前端只是没显示。解决办法是接口按角色返回不同 DTO收银端 DTO 里根本不含purchase_price字段而不是靠前端隐藏。权限控制要在数据出口做不能指望前端自觉。5.4 退货后库存加错商品现象是退 A 商品库存加到了 B 商品上。原因是退货时只传了销售单号和数量商品 ID 从销售明细里取的时候取错了行。解决办法是退货接口强制要求传productId并且校验该商品确实在原销售单明细里双重确认后再加库存。销售单明细表的主键要能唯一定位到「某单某商品」不能只靠行号。5.5 报表统计把作废单算进去现象是日销售报表金额比实际收款多。原因是统计 SQL 没过滤status把作废和退货单也加进去了。解决办法是报表查询统一带状态条件销售统计只算有效单退货单独列一列冲减不要混在一个SUM里。报表 SQL 最好单独维护不要和业务查询复用同一个 Mapper 方法避免改一处影响另一处。6. 验收与答辩怎么证明这套系统真的能跑验收这件事我一般分三层走功能验收、数据验收、异常验收。功能验收对着用例图逐条点六个主用例每个至少走一遍正常流程数据验收在操作前后各查一次数据库重点看stock和stock_flow汇总是否一致异常验收专门制造边界情况比如库存为 1 时卖 2 件、重复审核同一张单、退货数量超原销量看系统是否按预期拦截。答辩时导师最爱问的三个问题提前准备好答案能省很多事。第一个是「库存并发怎么保证」答条件更新加事务现场打开deductStock的 SQL 给他看WHERE stock #{qty}。第二个是「角色权限怎么实现」答网关拦截加 DTO 裁剪演示收银员账号调商品接口返回的 JSON 里没有进货价。第三个是「用例图和代码怎么对应」答每个用例对应一组接口现场从用例图点到 Controller 方法这条链路能走通说明设计不是画着玩的。验收清单可以做成一张表答辩前自己先过一遍验收项操作步骤预期结果判定方式进货入库闭环建单→审核→入库库存增加、流水生成查库对比库存不足拦截库存 1 卖 2提示库存不足、不生成单看返回与库重复审核防护两窗口同时审核仅一个成功看状态字段退货超量拦截退 3 件原买 2 件提示超量看返回权限数据裁剪收银员查商品无进货价字段看 JSON这张表建议答辩前自己跑两遍第二遍换个顺序跑因为有些 bug 只在特定操作顺序下才暴露。我吃过一次亏单独测每个功能都正常连着操作「入库→销售→退货→再入库」时库存对不上原因是退货流水关联单号写错了字段单测根本发现不了。从那以后我每次交付前都强制走一遍「全链路串行操作加库存对账」不管功能看起来多简单。希望这套结构和验收思路能帮到你用例图先画、状态机写死、库存变动收敛到一个入口这三条守住超市进销存这类系统基本不会出大问题。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →