结构调整

This commit is contained in:
2026-07-14 16:36:24 +08:00
parent 585e9d8e15
commit 57f354cf9f
254 changed files with 84114 additions and 44 deletions
+699
View File
@@ -0,0 +1,699 @@
# 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 中添加依赖