699 lines
29 KiB
Markdown
699 lines
29 KiB
Markdown
# 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 状态
|
||
|
||
- 所有修改已 commit(13+ 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 状态
|
||
|
||
- 所有修改已 commit(13+ 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 状态
|
||
|
||
- 所有修改已 commit(13+ 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/setWarehouseName;history DTO 名通过 supplierMapper + warehouseMapper 批量 lookup;autoInboundFromOrder 用 supplierId 取代 supplierName;updatePurchaseOrderAfterInbound 用 supplierId 查询 |
|
||
| **OutboundServiceImpl.java** | 注入 WarehouseMapper;移除 entity setCustomerName;history 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`
|
||
|
||
### InboundControllerTest(21 用例)
|
||
|
||
| 序号 | 端点 | 验证 |
|
||
|------|------|------|
|
||
| 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 |
|
||
|
||
### OutboundControllerTest(21 用例)
|
||
|
||
| 序号 | 端点 | 验证 |
|
||
|------|------|------|
|
||
| 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 |
|
||
|
||
### StockControllerTest(17 用例)
|
||
|
||
覆盖 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 字段完整性验证 |
|
||
|
||
### StocktakeControllerTest(17 用例)
|
||
|
||
覆盖 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 中添加依赖 |