Files

699 lines
29 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.yml** — `git add` 时用 `git add . -- ':!application.yml'` 或手动排除
- **commit 后不要 push** — 只做本地提交,不推送远程
# 2026-05-26 会话记忆
## 物料信息填充公共服务 (MaterialFillService)
### 核心设计决策
1. **实体设计原则**
- `InboundItem``OutboundItem` 只存储 `materialId`
- 不存储物料编码、名称、规格、单位等字段
- 防止物料信息变更后历史数据不一致
2. **批量查询模式**
```java
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.yml** — `git 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. **实体设计原则**
- `InboundItem` 和 `OutboundItem` 只存储 `materialId`
- 不存储物料编码、名称、规格、单位等字段
- 防止物料信息变更后历史数据不一致
2. **批量查询模式**
```java
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` — 出库场景需要
- 继承 `PageQuery`pageNum, 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 的数据
### 响应
- 复用 `StockPageDTO`im.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.yml** — `git 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. **实体设计原则**
- `InboundItem` 和 `OutboundItem` 只存储 `materialId`
- 不存储物料编码、名称、规格、单位等字段
- 防止物料信息变更后历史数据不一致
2. **批量查询模式**
```java
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` — 出库场景需要
- 继承 `PageQuery`pageNum, 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 的数据
### 响应
- 复用 `StockPageDTO`im.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 填充 Ap`createArFromOutbound`/`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`
### 设计
- 使用 `TestRestTemplate``webEnvironment = 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.yml` 中 `server.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 中添加依赖