Files

29 KiB
Raw Permalink Blame History

2026-05-26 会话记忆

本周聚焦

  • 只关注 tiny-erp,不处理其他项目
  • 只关注 md(主数据)im(库存) 两个模块
  • 不再关注 om(销售)、mc(采购)、fin(财务)、home、system 等其他模块

模块路径确认

包路径: com.witsoft.erp 项目路径: /home/yangxuan/Projects/IdeaProjects/tiny-erp

md 模块(主数据)

module/md/ — 已完整实现

  • category — 物料分类
  • customer — 客户
  • factory — 工厂
  • material — 物料
  • supplier — 供应商
  • warehouse — 仓库

im 模块(库存)

module/im/ — 已完整实现

  • inbound — 入库单(含采购入库、生产入库)
  • outbound — 出库单
  • stock — 库存查询、库存流水、呆滞分析
  • stocktake — 库存盘点(含快速盘点)

原记忆清理

旧记忆(memory/2026-05-23.md)中关于 bd 模块的命名实际上是 md(代码实际使用的是 md,不是 bd)。 ome/mc/fin/home/system 模块依然存在但不处理。

注释规范(已写入 MEMORY.md

  1. 所有公共接口和类必须包含 Javadoc
  2. 注释语言:中文描述业务背景,技术术语保留英文(如 NPE)
  3. 禁止生成 "Gets the value of X" 这类无意义的 getter/setter 注释
  4. 复杂算法必须在代码块上方解释核心逻辑
  5. 禁止使用 HTML 标签

开发注意事项(已写入 MEMORY.md)

  1. 复用优先:逻辑雷同时提取公共方法复用
  2. 业务表仅存物料 id:物料相关字段仅存 material_id,其他数据通过关联查询填充
  3. 填充在 Service 层:关联数据填充逻辑统一在 Service 层处理
  4. 首个版本直接改 DDL:脚本直接修改建表语句,不需要写 ALTER TABLE
  5. 字典优先用 DictService:字典取值优先使用 DictService 公共方法

库存业务表字段清理

背景: 业务表只存物料 ID,物料编码/名称/规格/单位通过关联查询填充。

DDL 改动(50-inventory.sql):

  • im_stock — 移除 material_code, material_name, spec, unit
  • im_stock_transaction — 移除 material_code, material_name
  • im_inbound_item — 移除 material_code, material_name, spec, unit
  • im_outbound_item — 移除 material_code, material_name, spec, unit

实体类: Stock, StockTransaction, InboundItem, OutboundItem — 移除对应字段

请求 DTO InboundItemSaveReqDTO, OutboundItemSaveReqDTO, StocktakeItemSaveReqDTO, QuickStocktakeItemDTO — 只保留 materialId

接口改动:

  • StockTransactionService.recordTransaction() — 移除 materialCode/materialName 参数
  • MaterialService — 新增 searchMaterialIdsByKeyword() 方法

Service 改动:

  • StockTransactionServiceImpl — keyword 搜索改为两步(先查物料表匹配 ID);DTO 填充从 materialInfoMap 读取
  • StockServiceImpl — 同上
  • InboundServiceImpl — saveItems 不 set 冗余字段;recordTransaction 去参;history 从 material 填充
  • OutboundServiceImpl — 同上
  • MaterialServiceImpl — 实现 searchMaterialIdsByKeyword()

编译验证: mvn compile -q

Git 提交约定

  • commit 跳过 application.ymlgit add 时用 git add . -- ':!application.yml' 或手动排除
  • commit 后不要 push — 只做本地提交,不推送远程

2026-05-26 会话记忆

物料信息填充公共服务 (MaterialFillService)

核心设计决策

  1. 实体设计原则

    • InboundItemOutboundItem 只存储 materialId
    • 不存储物料编码、名称、规格、单位等字段
    • 防止物料信息变更后历史数据不一致
  2. 批量查询模式

    List<Long> materialIds = items.stream().map(Item::getMaterialId).collect(Collectors.toList());
    Map<Long, MaterialInfoDTO> materialInfoMap = materialFillService.getMaterialInfoMap(materialIds);
    
    • 避免 N+1 查询问题
    • 一次查询所有物料信息,性能更优
  3. Service 层填充

    • 所有物料信息填充逻辑在 Service 层处理
    • Controller 只负责返回 DTO
    • 不放在 Mapper 或 Controller 层

修改的 Service 类(共 6 个)

Service 方法 用途
InboundServiceImpl executeInbound(), cancel() 入库/冲销时记录流水
OutboundServiceImpl executeOutbound(), cancel() 出库/冲销时记录流水
FinAccountsAutoServiceImpl createApFromInbound(), cancelApFromInbound() 采购应付联动
FinAccountsAutoServiceImpl createArFromOutbound(), cancelArFromOutbound() 销售应收联动
HomeServiceImpl getLatestNews() 首页动态显示
DeliveryServiceImpl confirmOutbound() 发货确认

物料类型字典

  • Dict Type: material_type
  • 值:P-原材料,SA-半成品,FG-成品
  • SQL 脚本:scripts/postgresql/22-material-type-dict.sql

Git 状态

  • 所有修改已 commit13+ commits
  • 禁止 push - 用户明确要求
  • 分支:master

经验教训

  1. 修改实体类后,所有引用该实类的地方都要检查
  2. 编译错误可能分散在多个模块(im, fin, home, om
  3. 使用 mvn clean compile 确保完整编译,避免 IDE 缓存误导

2026-05-26 会话记忆

本周聚焦

  • 只关注 tiny-erp,不处理其他项目
  • 只关注 md(主数据)im(库存) 两个模块
  • 不再关注 om(销售)、mc(采购)、fin(财务)、home、system 等其他模块

模块路径确认

包路径: com.witsoft.erp 项目路径: /home/yangxuan/Projects/IdeaProjects/tiny-erp

md 模块(主数据)

module/md/ — 已完整实现

  • category — 物料分类
  • customer — 客户
  • factory — 工厂
  • material — 物料
  • supplier — 供应商
  • warehouse — 仓库

im 模块(库存)

module/im/ — 已完整实现

  • inbound — 入库单(含采购入库、生产入库)
  • outbound — 出库单
  • stock — 库存查询、库存流水、呆滞分析
  • stocktake — 库存盘点(含快速盘点)

原记忆清理

旧记忆(memory/2026-05-23.md)中关于 bd 模块的命名实际上是 md(代码实际使用的是 md,不是 bd)。 ome/mc/fin/home/system 模块依然存在但不处理。

注释规范(已写入 MEMORY.md

  1. 所有公共接口和类必须包含 Javadoc
  2. 注释语言:中文描述业务背景,技术术语保留英文(如 NPE)
  3. 禁止生成 "Gets the value of X" 这类无意义的 getter/setter 注释
  4. 复杂算法必须在代码块上方解释核心逻辑
  5. 禁止使用 HTML 标签

开发注意事项(已写入 MEMORY.md)

  1. 复用优先:逻辑雷同时提取公共方法复用
  2. 业务表仅存物料 id:物料相关字段仅存 material_id,其他数据通过关联查询填充
  3. 填充在 Service 层:关联数据填充逻辑统一在 Service 层处理
  4. 首个版本直接改 DDL:脚本直接修改建表语句,不需要写 ALTER TABLE
  5. 字典优先用 DictService:字典取值优先使用 DictService 公共方法

Git 提交约定

  • commit 跳过 application.ymlgit add . -- ':!application.yml' 排除,防止泄露配置
  • commit 后不要 push — 只做本地 commit,不推送远程仓库

库存业务表字段清理

背景: 业务表只存物料 ID,物料编码/名称/规格/单位通过关联查询填充。

DDL 改动(50-inventory.sql):

  • im_stock — 移除 material_code, material_name, spec, unit
  • im_stock_transaction — 移除 material_code, material_name
  • im_inbound_item — 移除 material_code, material_name, spec, unit
  • im_outbound_item — 移除 material_code, material_name, spec, unit

实体类: Stock, StockTransaction, InboundItem, OutboundItem — 移除对应字段

请求 DTO InboundItemSaveReqDTO, OutboundItemSaveReqDTO, StocktakeItemSaveReqDTO, QuickStocktakeItemDTO — 只保留 materialId

接口改动:

  • StockTransactionService.recordTransaction() — 移除 materialCode/materialName 参数
  • MaterialService — 新增 searchMaterialIdsByKeyword() 方法

Service 改动:

  • StockTransactionServiceImpl — keyword 搜索改为两步(先查物料表匹配 ID);DTO 填充从 materialInfoMap 读取
  • StockServiceImpl — 同上
  • InboundServiceImpl — saveItems 不 set 冗余字段;recordTransaction 去参;history 从 material 填充
  • OutboundServiceImpl — 同上
  • MaterialServiceImpl — 实现 searchMaterialIdsByKeyword()

编译验证: mvn compile -q

物料信息填充公共服务 (MaterialFillService)

核心设计决策

  1. 实体设计原则

    • InboundItemOutboundItem 只存储 materialId
    • 不存储物料编码、名称、规格、单位等字段
    • 防止物料信息变更后历史数据不一致
  2. 批量查询模式

    List<Long> materialIds = items.stream().map(Item::getMaterialId).collect(Collectors.toList());
    Map<Long, MaterialInfoDTO> materialInfoMap = materialFillService.getMaterialInfoMap(materialIds);
    
    • 避免 N+1 查询问题
    • 一次查询所有物料信息,性能更优
  3. Service 层填充

    • 所有物料信息填充逻辑在 Service 层处理
    • Controller 只负责返回 DTO
    • 不放在 Mapper 或 Controller 层

修改的 Service 类(共 6 个)

Service 方法 用途
InboundServiceImpl executeInbound(), cancel() 入库/冲销时记录流水
OutboundServiceImpl executeOutbound(), cancel() 出库/冲销时记录流水
FinAccountsAutoServiceImpl createApFromInbound(), cancelApFromInbound() 采购应付联动
FinAccountsAutoServiceImpl createArFromOutbound(), cancelArFromOutbound() 销售应收联动
HomeServiceImpl getLatestNews() 首页动态显示
DeliveryServiceImpl confirmOutbound() 发货确认

物料类型字典

  • Dict Type: material_type
  • 值:P-原材料,SA-半成品,FG-成品
  • SQL 脚本:scripts/postgresql/22-material-type-dict.sql

Git 状态

  • 所有修改已 commit13+ commits
  • 禁止 push - 用户明确要求
  • 分支:master

物料选择器(Material Selector

新增物料选择器功能,用于入库/出库操作时弹窗选物料。

接口

接口 位置 数据源 仓库参数
POST /im/inbound/material-selector InboundController md_material 不需要
POST /im/outbound/material-selector OutboundController im_stock JOIN md_material 需要 warehouseId

请求参数(MaterialSelectorReqDTO → md.material.dto

  • categoryId — 0=常用物料,>0=按分类筛选
  • keyword — 物料编码/名称/规格关键字
  • warehouseId — 出库场景需要
  • 继承 PageQuerypageNum, pageSize

常用物料 TOP10 逻辑

  • 口径: 近 30 天出入库明细条目数最多的 10 个物料
  • 入库: im_inbound_item GROUP BY material_id ORDER BY COUNT(*) DESC LIMIT 10
  • 出库: im_outbound_item 同上
  • SQL 放 Mapper XML(方便后续扩展)

分类筛选逻辑

  • 入库: MyBatis Plus LambdaQueryWrapper 查 md_material 表,按 category_id + keyword 分页
  • 出库: XML 联表 im_stock JOIN md_material,按 category_id + keyword + warehouseId 分页,只查库存 > 0 的数据

响应

  • 复用 StockPageDTOim.stock.dto
  • 物料编码/名称/规格/单位/分类等从 MaterialFillService 填充

新增文件(7个)

文件 路径
MaterialSelectorReqDTO md.material.dto
MaterialSelectorService md.material.service
MaterialSelectorServiceImpl md.material.service.impl
InboundItemMapper.xml resources/mapper/im/
OutboundItemMapper.xml resources/mapper/im/
StockMapper.xml resources/mapper/im/

修改文件(5个)

文件 改动
InboundItemMapper.java + selectRecentMaterialIds()
OutboundItemMapper.java + selectRecentMaterialIds()
StockMapper.java + selectStockPage() / countStockPage()
InboundController.java + /material-selector 端点
OutboundController.java + /material-selector 端点

历史单据创建人名称

改动

  • InboundHistoryItemDTO — 新增 creatorName 字段
  • OutboundHistoryItemDTO — 新增 creatorName 字段
  • InboundServiceImpl.listHistory() — 收集 creators → 批量查 sys_user(username→nickname) → 填充
  • OutboundServiceImpl.listHistory() — 同上

逻辑

  1. 从单据列表收集所有 creator(用户名)
  2. userService.lambdaQuery().in(User::getUsername, creators).list()
  3. Map<String, String>username → nickname
  4. 逐行 dto.setCreatorName()
  5. 一次 SQL,不 N+1

2026-05-26 会话记忆

本周聚焦

  • 只关注 tiny-erp,不处理其他项目
  • 只关注 md(主数据)im(库存) 两个模块
  • 不再关注 om(销售)、mc(采购)、fin(财务)、home、system 等其他模块

模块路径确认

包路径: com.witsoft.erp 项目路径: /home/yangxuan/Projects/IdeaProjects/tiny-erp

md 模块(主数据)

module/md/ — 已完整实现

  • category — 物料分类
  • customer — 客户
  • factory — 工厂
  • material — 物料
  • supplier — 供应商
  • warehouse — 仓库

im 模块(库存)

module/im/ — 已完整实现

  • inbound — 入库单(含采购入库、生产入库)
  • outbound — 出库单
  • stock — 库存查询、库存流水、呆滞分析
  • stocktake — 库存盘点(含快速盘点)

原记忆清理

旧记忆(memory/2026-05-23.md)中关于 bd 模块的命名实际上是 md(代码实际使用的是 md,不是 bd)。 ome/mc/fin/home/system 模块依然存在但不处理。

注释规范(已写入 MEMORY.md

  1. 所有公共接口和类必须包含 Javadoc
  2. 注释语言:中文描述业务背景,技术术语保留英文(如 NPE)
  3. 禁止生成 "Gets the value of X" 这类无意义的 getter/setter 注释
  4. 复杂算法必须在代码块上方解释核心逻辑
  5. 禁止使用 HTML 标签

开发注意事项(已写入 MEMORY.md)

  1. 复用优先:逻辑雷同时提取公共方法复用
  2. 业务表仅存物料 id:物料相关字段仅存 material_id,其他数据通过关联查询填充
  3. 填充在 Service 层:关联数据填充逻辑统一在 Service 层处理
  4. 首个版本直接改 DDL:脚本直接修改建表语句,不需要写 ALTER TABLE
  5. 字典优先用 DictService:字典取值优先使用 DictService 公共方法

Git 提交约定

  • commit 跳过 application.ymlgit add . -- ':!application.yml' 排除,防止泄露配置
  • commit 后不要 push — 只做本地 commit,不推送远程仓库

库存业务表字段清理

背景: 业务表只存物料 ID,物料编码/名称/规格/单位通过关联查询填充。

DDL 改动(50-inventory.sql):

  • im_stock — 移除 material_code, material_name, spec, unit
  • im_stock_transaction — 移除 material_code, material_name
  • im_inbound_item — 移除 material_code, material_name, spec, unit
  • im_outbound_item — 移除 material_code, material_name, spec, unit

实体类: Stock, StockTransaction, InboundItem, OutboundItem — 移除对应字段

请求 DTO InboundItemSaveReqDTO, OutboundItemSaveReqDTO, StocktakeItemSaveReqDTO, QuickStocktakeItemDTO — 只保留 materialId

接口改动:

  • StockTransactionService.recordTransaction() — 移除 materialCode/materialName 参数
  • MaterialService — 新增 searchMaterialIdsByKeyword() 方法

Service 改动:

  • StockTransactionServiceImpl — keyword 搜索改为两步(先查物料表匹配 ID);DTO 填充从 materialInfoMap 读取
  • StockServiceImpl — 同上
  • InboundServiceImpl — saveItems 不 set 冗余字段;recordTransaction 去参;history 从 material 填充
  • OutboundServiceImpl — 同上
  • MaterialServiceImpl — 实现 searchMaterialIdsByKeyword()

编译验证: mvn compile -q

物料信息填充公共服务 (MaterialFillService)

核心设计决策

  1. 实体设计原则

    • InboundItemOutboundItem 只存储 materialId
    • 不存储物料编码、名称、规格、单位等字段
    • 防止物料信息变更后历史数据不一致
  2. 批量查询模式

    List<Long> materialIds = items.stream().map(Item::getMaterialId).collect(Collectors.toList());
    Map<Long, MaterialInfoDTO> materialInfoMap = materialFillService.getMaterialInfoMap(materialIds);
    
    • 避免 N+1 查询问题
    • 一次查询所有物料信息,性能更优
  3. Service 层填充

    • 所有物料信息填充逻辑在 Service 层处理
    • Controller 只负责返回 DTO
    • 不放在 Mapper 或 Controller 层

修改的 Service 类(共 6 个)

Service 方法 用途
InboundServiceImpl executeInbound(), cancel() 入库/冲销时记录流水
OutboundServiceImpl executeOutbound(), cancel() 出库/冲销时记录流水
FinAccountsAutoServiceImpl createApFromInbound(), cancelApFromInbound() 采购应付联动
FinAccountsAutoServiceImpl createArFromOutbound(), cancelArFromOutbound() 销售应收联动
HomeServiceImpl getLatestNews() 首页动态显示
DeliveryServiceImpl confirmOutbound() 发货确认

物料类型字典

  • Dict Type: material_type
  • 值:P-原材料,SA-半成品,FG-成品
  • SQL 脚本:scripts/postgresql/22-material-type-dict.sql

Git 状态

  • 所有修改已 commit13+ commits
  • 禁止 push - 用户明确要求
  • 分支:master

物料选择器(Material Selector

新增物料选择器功能,用于入库/出库操作时弹窗选物料。

接口

接口 位置 数据源 仓库参数
POST /im/inbound/material-selector InboundController md_material 不需要
POST /im/outbound/material-selector OutboundController im_stock JOIN md_material 需要 warehouseId

请求参数(MaterialSelectorReqDTO → md.material.dto

  • categoryId — 0=常用物料,>0=按分类筛选
  • keyword — 物料编码/名称/规格关键字
  • warehouseId — 出库场景需要
  • 继承 PageQuerypageNum, pageSize

常用物料 TOP10 逻辑

  • 口径: 近 30 天出入库明细条目数最多的 10 个物料
  • 入库: im_inbound_item GROUP BY material_id ORDER BY COUNT(*) DESC LIMIT 10
  • 出库: im_outbound_item 同上
  • SQL 放 Mapper XML(方便后续扩展)

分类筛选逻辑

  • 入库: MyBatis Plus LambdaQueryWrapper 查 md_material 表,按 category_id + keyword 分页
  • 出库: XML 联表 im_stock JOIN md_material,按 category_id + keyword + warehouseId 分页,只查库存 > 0 的数据

响应

  • 复用 StockPageDTOim.stock.dto
  • 物料编码/名称/规格/单位/分类等从 MaterialFillService 填充

新增文件(7个)

文件 路径
MaterialSelectorReqDTO md.material.dto
MaterialSelectorService md.material.service
MaterialSelectorServiceImpl md.material.service.impl
InboundItemMapper.xml resources/mapper/im/
OutboundItemMapper.xml resources/mapper/im/
StockMapper.xml resources/mapper/im/

修改文件(5个)

文件 改动
InboundItemMapper.java + selectRecentMaterialIds()
OutboundItemMapper.java + selectRecentMaterialIds()
StockMapper.java + selectStockPage() / countStockPage()
InboundController.java + /material-selector 端点
OutboundController.java + /material-selector 端点

历史单据创建人名称

改动

  • InboundHistoryItemDTO — 新增 creatorName 字段
  • OutboundHistoryItemDTO — 新增 creatorName 字段
  • InboundServiceImpl.listHistory() — 收集 creators → 批量查 sys_user(username→nickname) → 填充
  • OutboundServiceImpl.listHistory() — 同上

逻辑

  1. 从单据列表收集所有 creator(用户名)
  2. userService.lambdaQuery().in(User::getUsername, creators).list()
  3. Map<String, String>username → nickname
  4. 逐行 dto.setCreatorName()
  5. 一次 SQL,不 N+1

实体名称字段清理(SupplierName/CustomerName/WarehouseName

背景

单据实体(Inbound、Outbound、PurchaseOrder、SalesOrder、Delivery)及其 Item 实体中,之前还存储了供应商名称、客户名称、仓库名称等冗余字段。本次清理全部移除,只存 ID,显示名称通过 master-data 查询填充。

涉及实体

  • Inbound — 移除 supplierName, warehouseName
  • Outbound — 移除 customerName, warehouseName
  • PurchaseOrder — 移除 supplierName
  • PurchaseOrderItem — 移除了 materialCode, materialName, spec, unit(之前已完成)
  • SalesOrder — 移除 customerName
  • SalesOrderItem — 移除了物料冗余字段(之前已完成)
  • Delivery — 移除 customerName
  • DeliveryItem — 移除了物料冗余字段(之前已完成)

DTO 改动

  • InboundSaveReqDTO / PurchaseInboundSaveReqDTO / ProductionInboundSaveReqDTO — 只保留 supplierId/warehouseId,移除 supplierName/warehouseName
  • OutboundSaveReqDTO / SalesOutboundSaveReqDTO / MaterialOutboundSaveReqDTO — 只保留 customerId/warehouseId,移除 customerName/warehouseName
  • InboundHistoryItemDTO / OutboundHistoryItemDTO保留 display name 字段(响应 DTO 需要显示),通过 master-data lookup 填充

不做改动

  • Ar.java / Ap.java — 保留 customerName / supplierName 作为财务历史快照
  • InboundHistoryItemDTO / OutboundHistoryItemDTO — 保留 name 字段供前端展示

主要 Service 改动

文件 改动内容
InboundServiceImpl.java 注入 WarehouseMapper;移除 entity setSupplierName/setWarehouseNamehistory DTO 名通过 supplierMapper + warehouseMapper 批量 lookupautoInboundFromOrder 用 supplierId 取代 supplierNameupdatePurchaseOrderAfterInbound 用 supplierId 查询
OutboundServiceImpl.java 注入 WarehouseMapper;移除 entity setCustomerNamehistory DTO 名通过 customerMapper + warehouseMapper 批量 lookup
FinAccountsAutoServiceImpl.java createApFromInbound/cancelApFromInbound 使用 supplierMapper 查询 supplierName 填充 ApcreateArFromOutbound/cancelArFromOutbound 使用 customerMapper 查询 customerName
PurchaseOrderServiceImpl.java setSupplierName(reqDTO.getSupplierName())setSupplierId(reqDTO.getSupplierId())
SalesOrderServiceImpl.java setCustomerName(reqDTO.getCustomerName())setCustomerId(reqDTO.getCustomerId())
DeliveryServiceImpl.java setCustomerName(order.getCustomerName())setCustomerId(order.getCustomerId())item 用 materialFillService 填充物料信息

控制器查询条件

  • PurchaseOrderController / SalesOrderController / DeliveryController — 查询条件从名称改为 ID

编译验证

  • mvn clean compile — 零错误

集成测试(InboundOutboundIntegrationTest

文件路径

src/test/java/com/witsoft/erp/im/InboundOutboundIntegrationTest.java

设计

  • 使用 TestRestTemplatewebEnvironment = RANDOM_PORT
  • @TestMethodOrder(MethodOrderer.OrderAnnotation.class) 按顺序执行
  • 复用已有的 Result<T> 泛型返回包装类

测试覆盖(11 个用例)

序号 端点 说明
1 POST /erp/system/user/login 登录获取 Bearer token
2 POST /erp/im/inbound/production 生产入库(2 行物料)
3 POST /erp/im/inbound/purchase 采购入库(含单价)
4 POST /erp/im/inbound/history-list 查询入库历史
5 POST /erp/im/outbound/material 领料出库
6 POST /erp/im/outbound/sales 销售出库(含单价)
7 POST /erp/im/outbound/history-list 查询出库历史
8 POST /erp/im/inbound/auto-from-order 自动入库(基于采购订单)
9 POST /erp/im/stock/transaction/page 查询库存流水
10 DELETE /erp/im/inbound/{id} 撤销入库单
11 DELETE /erp/im/outbound/{id} 撤销出库单

注意事项

  • application.ymlserver.servlet.context-path: /erp,所有路径须带 /erp 前缀
  • 测试需要准备基础数据:物料 ID=3/4、供应商 ID=1、客户 ID=1、仓库 ID=1、采购订单 ID=1
  • 当前 authToken 未通过 TestRestTemplate 的 basic auth,走的是 token-auth 模式
  • 登录密码写的是 ***,需要按实际数据库密码修改 TEST_PASSWORD 常量

Controller 集成测试(TestRestTemplate

现有测试文件

测试类 路径 用例数 覆盖 Controller
InboundOutboundIntegrationTest im/InboundOutboundIntegrationTest.java 11 Inbound + Outbound(合并)
StockControllerTest im/stock/StockControllerTest.java 17 StockController
StocktakeControllerTest im/stocktake/StocktakeControllerTest.java 17 StocktakeController
InboundControllerTest im/inbound/InboundControllerTest.java 21 InboundController
OutboundControllerTest im/outbound/OutboundControllerTest.java 21 OutboundController

测试设计模式(所有测试统一)

  1. TestRestTemplate + RANDOM_PORT — 自动分配端口,避免冲突
  2. @TestMethodOrder(OrderAnnotation.class) — 按 Order 顺序执行
  3. CONTEXT_PATH = "/erp" — 手动拼接,因为自动 context-path 处理不可靠
  4. assumeToken() 守卫 — 登录失败后通过 Assumptions.assumeTrue(authToken != null) 跳过后续测试,避免 403
  5. 密码可配置System.getProperty("test.password", "admin123"),通过 -Dtest.password=xxx 覆盖
  6. url() 辅助方法"http://localhost:" + port + CONTEXT_PATH + path

InboundControllerTest21 用例)

序号 端点 验证
1 POST /system/user/login 登录获取 Bearer token
2-3 POST /im/inbound/material-selector 物料选择器(分类筛选)
4-6 POST /im/inbound 新增 + 明细为空/类型为空校验边界
7-8 GET /im/inbound/{id} 详情 / 不存在返回 null
9 GET /im/inbound/{id}/items 明细列表
10 PUT /im/inbound 修改
11-12 PUT /im/inbound/{id}/cancel 撤销 / 重复撤销 400
13-14 DELETE /im/inbound/{id} 删除
15-16 POST /im/inbound/page 分页(全查/仓库筛选)
17 POST /im/inbound/history-list 历史明细列表
18 POST /im/inbound/production 生产入库
19-20 POST /im/inbound/purchase 采购入库 / 供应商为空 400
21 POST /im/inbound/auto-from-order 自动入库-订单不存在 400

OutboundControllerTest21 用例)

序号 端点 验证
1 POST /system/user/login 登录
2-3 POST /im/outbound/material-selector 物料选择器
4-6 POST /im/outbound 新增 + 明细为空/客户为空校验
7-8 GET /im/outbound/{id} 详情 / 不存在
9 GET /im/outbound/{id}/items 明细
10 PUT /im/outbound 修改
11-12 PUT /im/outbound/{id}/cancel 撤销 / 重复撤销 400
13-14 DELETE /im/outbound/{id} 删除
15-16 POST /im/outbound/page 分页(全查/客户筛选)
17 POST /im/outbound/history-list 历史明细
18-19 POST /im/outbound/material 领料出库 / 明细为空 400
20-21 POST /im/outbound/sales 销售出库 / 客户为空 400

StockControllerTest17 用例)

覆盖 StockController 全部 4 个端点:

序号 端点 验证
1 POST /system/user/login 登录
2-6 POST /im/stock/page 空参数、仓库筛选、关键字、无匹配关键字、自定义分页
7-11 POST /im/stock/list 空、仓库、关键字、预警筛选、无匹配关键字
12-13 GET /im/stock/{materialId} 存在/不存在
14-16 PUT /im/stock/safe-qty 正常设置、物料不存在 400、参数校验
17 POST /im/stock/page DTO 字段完整性验证

StocktakeControllerTest17 用例)

覆盖 StocktakeController 全部 10 个端点:

序号 端点 验证
1 POST /system/user/login 登录
2-3 POST /im/stocktake 新增(草稿)/ 明细为空 400
4-5 GET /im/stocktake/{id} 详情 / 不存在
6 GET /im/stocktake/{id}/items 明细(含物料信息)
7 PUT /im/stocktake 修改
8-9 PUT /im/stocktake/{id}/audit 审核 / 重复审核 400
10-11 PUT /im/stocktake/{id}/cancel 作废
12 DELETE /im/stocktake/{id} 删除
13 POST /im/stocktake/quick-save 快速盘点(创建即审核)
14-15 POST /im/stocktake/page 分页(筛选/空 body
16-17 POST /im/stocktake/history-list 历史明细(空条件)

已知问题

  • InboundController.getPage() 有两个 @RequestBody — 测试用 Map 代理
  • OutboundController.getPage() 同理
  • StocktakeController.getPage() 同理
  • 密码默认 admin123,通过 -Dtest.password=xxx 配置
  • IntelliJ 解析 junit-platform-launcher:1.8.2 失败,已在 pom.xml 中添加依赖