Files
openclaw-config/workspace-planner/memory/2026-08-04.md
T

580 lines
19 KiB
Markdown
Raw 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-08-04 工作日志
## mica 项目 - SMDM 物料管理模块开发规范
### 🔴 强制性红线 (违反即停止)
| # | 红线 | 违规示例 | 正确做法 |
|------|---------|----------|----------|
| 1 | **表前缀必须过滤** | `DmpMdItemInfo` | `ItemInfo` |
| 2 | **API 必须放在 apis.smdm 包** | `smd.api.ItemApi` | `apis.smdm.ItemApi` |
| 3 | **查询只能用 Mapper** | 使用 MyBatis-Plus | 原生 MyBatis XML |
| 4 | **改删必须 Feign 远程调用** | 本地直接 UPDATE/DELETE | 调用 SMDM 服务 API |
| 5 | **实体必须继承 BaseDomain** | 独立定义审计字段 | `extends BaseDomain` |
| 6 | **只使用指定 13 个业务字段** | 添加表中其他字段 | 仅用规范内字段 |
| 7 | **代码必须生成到 smd 目录** | `com.witsoft.mica.item.*` | `com.witsoft.mica.smd.*` |
---
### 📋 已确认的开发规范
#### 1. 包路径规范
- **代码生成目录**: `/root/projects/wit/mica-server/src/main/java/com/witsoft/mica/smd/`
- **包前缀**: `com.witsoft.mica.smd`
- **API 包路径**: `com.witsoft.mica.apis.smdm`
- **子包结构**:
```
smd/
├── controller/ # 控制器层
├── service/ # 服务层接口
├── service/impl/ # 服务层实现
├── mapper/ # MyBatis Mapper 接口
├── entity/ # 数据库实体类
├── dto/ # 数据传输对象
└── vo/ # 视图对象
```
#### 2. 表名前缀过滤规则
- **过滤前缀**: `dmp_md_`
- **示例**:
- `dmp_md_item_info` → `ItemInfo`
- `dmp_md_xxx` → `Xxx`
#### 3. 实体类规范
- **基础字段** (业务必需):
| 字段说明 | 数据库字段 | Java 字段 |
|---------|-----------|----------|
| 物料编码 | `item_code` | `itemCode` |
| 物料名称 | `item_name` | `itemName` |
| 规格型号 | `item_spec` | `itemSpec` |
| 物料类型 | `item_type` | `itemType` |
| 物料分类 | `item_categorys_code` | `itemCategorysCode` |
| 物料属性 | `properties` | `properties` |
| 主单位 | `unit_code` | `unitCode` |
| 辅单位 | `assist_unit_code` | `assistUnitCode` |
| 物料描述 | `item_desc` | `itemDesc` |
| 供应商 | `vendor_code` | `vendorCode` |
| 默认仓库 | `good_warehouse` | `goodWarehouse` |
| 批次控制 | `batch_control` | `batchControl` (默认 0/关) |
| 领料属性 | `picking_property` | `pickingProperty` (默认 AD/按单领料) |
| 状态 | `status` | `status` |
- **审计字段** (继承 `BaseDomain`):
```java
import com.witsoft.gen.base.BaseDomain;
public class ItemInfo extends BaseDomain {
// 业务字段...
}
```
- `id`, `ecid`, `createdBy`, `createTime`, `updatedBy`, `updateTime`
#### 4. 数据访问规范
- **查询权限**: 仅有数据库查询权限
- **查询方式**: 使用原生 MyBatis Mapper**不使用 MyBatis-Plus**
- **修改/删除**: 通过 Feign 远程调用 SMDM 服务
#### 5. 架构模式
```
mica-server (本地) smdm (远程服务)
│ │
├── Controller │
├── Service │
├── Mapper ──(查询)──→ 数据库
└── Feign Api ──(改删)──→ WorkshopApi 模式
```
#### 6. Feign 远程调用模式
**API 包路径**: `com.witsoft.mica.apis.smdm`
参考现有 `WorkshopApi`:
```java
@FeignClient(name = "smdm", path = "/smdm/smd/web/workshop")
public interface WorkshopApi {
@PostMapping("/getListPage")
ResponseModel queryWorkshopPageList(@RequestBody Map<String, Object> workshop);
@PostMapping("/create")
ResponseModel insertWorkshop(@RequestBody Map<String, Object> workshop);
}
```
---
## 📝 注释与日志规范
### 1. 类注释 (Javadoc 风格)
参考现有 `WorkshopController.java`:
```java
/**
* @Description : 物料信息管理
* @ModifyBrief :
* @Author : yangxuan
* @Date : 2026/8/4
* @Version : 1.0
*/
```
**规范**:
- `@Author` 固定为 `yangxuan`
- `@Date` 精确到日 (`2026/8/4`)
- `@ModifyBrief` 留空,后续修改时补充
### 2. 方法注释
```java
/**
* @Description : 物料信息列表分页查询
* @ModifyBrief :
* @Author : yangxuan
* @Date : 2026/8/4
* @Version : 1.0
* @Param : itemQueryDTO
* @Return : PageDomain<ItemVO>
*/
```
**规范**:
- 公共接口方法必须添加方法注释
- 私有方法可选添加
- `@Author` 固定为 `yangxuan`
### 3. 日志规范
**框架**: SLF4J + Lombok `@Slf4j`
**使用场景**:
- 请求入口:`log.info("物料查询请求:itemCode={}", dto.getItemCode())`
- 异常捕获:`log.error("物料查询失败", e)`
- 关键业务节点:`log.info("物料创建成功:itemCode={}", result.getItemCode())`
**示例**:
```java
@Slf4j
@RestController
public class ItemController {
public ResponseModel<PageDomain<ItemVO>> queryPageList(@RequestBody ItemQueryDTO dto) {
log.info("物料查询请求:ecid={}", dto.getEcid());
try {
return itemService.queryPageList(dto);
} catch (Exception e) {
log.error("物料查询失败", e);
return ResponseModel.failed("查询失败");
}
}
}
```
### 4. Feign 调用日志规范
**正确示例**:
```java
@Override
public ResponseModel insertItem(ItemFormDTO dto) {
log.debug("Feign 调用 - 创建物料,入参:{}", JSON.toJSONString(dto));
long start = System.currentTimeMillis();
Map<String, Object> itemMap = Convert.convert(Map.class, dto);
ResponseModel response = itemApi.insertItem(itemMap);
log.debug("Feign 调用 - 创建物料完成,耗时:{}ms",
System.currentTimeMillis() - start);
return response;
}
```
**规范**:
- 日志级别:`debug`
- 记录内容:入参 + 耗时
- **禁止记录返回值**(避免日志过大)
- 使用 `JSON.toJSONString()` (fastjson) 序列化对象
### 5. 字段注释
单行注释即可:
```java
/** 物料编码 */
private String itemCode;
/** 物料名称 */
private String itemName;
```
---
## 🐛 已修正的问题清单 (1-15)
以下问题在开发过程中发现并已修正,**后续开发必须遵守**:
| # | 问题 | 修正方案 | 涉及文件 |
|---|------|----------|----------|
| 1 | **ResponseModel 静态引用错误** | 使用 `ResponseModel.succeed(data)` 静态方法 | `ItemController.java`, `ItemServiceImpl.java` |
| 2 | **分页 XML / 详情 MP 混合使用** | 分页查询用原生 XML,详情查询用 `BaseMapper.selectById` | `ItemMapper.xml`, `ItemServiceImpl.java` |
| 3 | **移除编码查询条件** | 从 DTO 和 XML 移除 `itemCode` 模糊搜索 | `ItemQueryDTO.java`, `ItemMapper.xml` |
| 4 | **Feign 调用添加日志** | debug 级别:入参 + 耗时 | `ItemServiceImpl.java` |
| 5 | **Controller 层 ecid 处理** | `GlobalUtils.getEcid()` 在 Controller 层获取并传递给 Service | `ItemController.java` |
| 6 | **恢复分页 XML 查询** | 保留完整的分页查询 SQL | `ItemMapper.xml` |
| 7 | **使用 PageDomain (非 PageHelper)** | 手动分页:先 count 查询总数,再 LIMIT 查询数据 | `ItemServiceImpl.java` |
| 8 | **删除 queryByCode 方法** | 删除所有 `queryByCode` 相关代码 | `ItemMapper.java`, `ItemService.java`, `ItemController.java` |
| 9 | **ResponseModel 泛型参数化** | 所有返回类型使用 `ResponseModel<T>` | `ItemController.java`, `ItemServiceImpl.java` |
| 10 | **Map 类型转换警告** | 使用 `Convert.convert(Map.class, dto)` 或 `@SuppressWarnings` | `ItemController.java`, `ItemServiceImpl.java` |
| 11 | **XML 使用 `<sql>` + `<include>` 片段** | 公共列定义和查询条件使用 SQL 片段复用 | `ItemMapper.xml` |
| 12 | **ItemApi ResponseModel 泛型** | Feign 接口返回 `ResponseModel<Void>` | `ItemApi.java` |
| 13 | **ResponseModel 使用裸类型** | 与项目其他 Feign 接口一致,使用裸类型 `ResponseModel` | `ItemApi.java`, `ItemService.java`, `ItemServiceImpl.java`, `ItemController.java` |
| 14 | **日志禁止记录返回值** | 只记录耗时,不记录返回值(避免日志过大) | `ItemServiceImpl.java` |
| 15 | **JsonUtils.toJson() 方法不存在** | 使用 `JSON.toJSONString()` (fastjson) 替代 | `ItemServiceImpl.java` |
---
### 问题 1 详解:ResponseModel 静态引用
**错误**:
```java
return ResponseModel.success(result); // ❌ success 不是静态方法
```
**正确**:
```java
return ResponseModel.succeed(result); // ✅ 使用静态方法 succeed
```
---
### 问题 2 详解:分页 XML / 详情 MP
**分页查询** (原生 XML):
```java
// ItemMapper.xml
<select id="queryPageList" resultType="com.witsoft.mica.smd.vo.ItemVO">
SELECT <include refid="selectItemColumns"/>
FROM dmp_md_item_info t
<include refid="queryConditions"/>
ORDER BY t.create_time DESC
LIMIT #{dto.pageNo}, #{dto.pageSize}
</select>
```
**详情查询** (MyBatis-Plus):
```java
// ItemServiceImpl.java
ItemInfo item = itemMapper.selectById(id);
```
---
### 问题 4 详解:Feign 调用日志
**正确示例**:
```java
@Override
public ResponseModel insertItem(ItemFormDTO dto) {
log.debug("Feign 调用 - 创建物料,入参:{}", JSON.toJSONString(dto));
long start = System.currentTimeMillis();
Map<String, Object> itemMap = Convert.convert(Map.class, dto);
ResponseModel response = itemApi.insertItem(itemMap);
log.debug("Feign 调用 - 创建物料完成,耗时:{}ms",
System.currentTimeMillis() - start);
return response;
}
```
**规范**:
- 日志级别:`debug`
- 记录内容:入参 + 耗时
- **禁止记录返回值**(避免日志过大)
- 使用 `JSON.toJSONString()` (fastjson) 序列化对象
---
### 问题 5 详解:Controller 层 ecid 处理
**Controller**:
```java
@PostMapping("/getPageList")
public ResponseModel<PageDomain<ItemVO>> queryPageList(@RequestBody ItemQueryDTO dto) {
String ecid = GlobalUtils.getEcid();
dto.setEcid(ecid);
log.info("物料查询请求:ecid={}", ecid);
return itemService.queryPageList(dto);
}
```
**Service** (直接使用传入的 ecid):
```java
@Override
public PageDomain<ItemVO> queryPageList(ItemQueryDTO dto) {
// 直接使用 dto.getEcid(),不再自己获取
List<ItemVO> list = itemMapper.queryPageList(dto);
// ...
}
```
---
### 问题 7 详解:使用 PageDomain
**Service 手动分页**:
```java
@Override
public PageDomain<ItemVO> queryPageList(ItemQueryDTO dto) {
int pageNo = dto.getPageNo() != null ? dto.getPageNo() : 1;
int pageSize = dto.getPageSize() != null ? dto.getPageSize() : 10;
// 查询总数
long total = itemMapper.queryPageCount(dto);
// 查询分页数据
List<ItemVO> list = itemMapper.queryPageList(dto);
// 构建 PageDomain
PageDomain<ItemVO> page = new PageDomain<>(pageNo, pageSize, total);
page.setList(list);
return page;
}
```
---
### 问题 11 详解:XML SQL 片段
**ItemMapper.xml**:
```xml
<mapper namespace="com.witsoft.mica.smd.mapper.ItemMapper">
<!-- 公共列定义 -->
<sql id="selectItemColumns">
t.id, t.ecid, t.item_code, t.item_name, t.item_spec, t.item_type,
t.item_categorys_code, t.properties, t.unit_code, t.assist_unit_code,
t.item_desc, t.vendor_code, t.good_warehouse, t.batch_control,
t.picking_property, t.status, t.create_time, t.created_by,
t.update_time, t.updated_by
</sql>
<!-- 公共查询条件 -->
<sql id="queryConditions">
<where>
<if test="dto.ecid != null and dto.ecid != ''">
AND t.ecid = #{dto.ecid}
</if>
<if test="dto.itemName != null and dto.itemName != ''">
AND t.item_name LIKE CONCAT('%', #{dto.itemName}, '%')
</if>
<if test="dto.itemType != null and dto.itemType != ''">
AND t.item_type = #{dto.itemType}
</if>
<if test="dto.status != null and dto.status != ''">
AND t.status = #{dto.status}
</if>
</where>
</sql>
<!-- 分页查询 -->
<select id="queryPageList" resultType="com.witsoft.mica.smd.vo.ItemVO">
SELECT
<include refid="selectItemColumns"/>
FROM dmp_md_item_info t
<include refid="queryConditions"/>
ORDER BY t.create_time DESC
LIMIT #{dto.pageNo}, #{dto.pageSize}
</select>
<!-- 总数查询 -->
<select id="queryPageCount" resultType="long">
SELECT COUNT(*)
FROM dmp_md_item_info t
<include refid="queryConditions"/>
</select>
</mapper>
```
---
### 问题 13 详解:ResponseModel 裸类型
**错误**:
```java
public ResponseModel<Map<String, Object>> insertItem(ItemFormDTO dto) { // ❌ 泛型参数
// ...
}
```
**正确** (与项目其他 Feign 接口一致):
```java
public ResponseModel insertItem(ItemFormDTO dto) { // ✅ 裸类型
// ...
}
```
**参考项目其他 Feign 接口**:
```java
// WorkshopApi.java
ResponseModel queryWorkshopPageList(@RequestBody Map<String, Object> workshop);
ResponseModel insertWorkshop(@RequestBody Map<String, Object> workshop);
// FactoryApi.java
ResponseModel getWorkShops(@RequestParam("ecid") String ecid);
ResponseModel getProdLineList(@RequestParam String workshopCode, @RequestParam String ecid);
```
---
### 问题 15 详解:JsonUtils 替代方案
**错误**:
```java
log.debug("入参:{}", JsonUtils.toJson(dto)); // ❌ JsonUtils.toJson() 方法不存在
```
**正确** (使用 fastjson):
```java
import com.alibaba.fastjson.JSON;
log.debug("入参:{}", JSON.toJSONString(dto)); // ✅ 使用 fastjson
```
**原因**: `common-utils` 依赖中的 `JsonUtils` 类没有 `toJson()` 方法,项目已引入 `fastjson` 依赖。
---
## 📁 后端完整文件清单 (10 个)
| # | 文件 | 路径 | 状态 |
|---|------|------|------|
| 1 | `ItemApi.java` | `apis/smdm/` | ✅ 已写入 |
| 2 | `ItemInfo.java` | `smd/entity/` | ✅ 已写入 |
| 3 | `ItemMapper.java` | `smd/mapper/` | ✅ 已写入 |
| 4 | `ItemMapper.xml` | `resources/mapper/smd/` | ✅ 已写入 |
| 5 | `ItemService.java` | `smd/service/` | ✅ 已写入 |
| 6 | `ItemServiceImpl.java` | `smd/service/impl/` | ✅ 已写入 |
| 7 | `ItemController.java` | `smd/controller/` | ✅ 已写入 |
| 8 | `ItemQueryDTO.java` | `smd/dto/` | ✅ 已写入 |
| 9 | `ItemFormDTO.java` | `smd/dto/` | ✅ 已写入 |
| 10 | `ItemVO.java` | `smd/vo/` | ✅ 已写入 |
---
## 数据字典模块开发 (2026-08-04 下午)
### 🔴 数据字典模块强制性红线
| # | 红线 | 正确做法 |
|---|------|----------|
| 1 | **不需要 Controller** | 纯内部工具服务 |
| 2 | **不需要 Feign API** | 不暴露外部接口 |
| 3 | **不需要缓存实现** | 后续可调用 Feign 接口缓存 |
| 4 | **不需要继承 BaseDomain** | 配置项不是业务实体 |
### 📋 数据字典核心功能
1. **单个字典类型查询**: `Map<String, String> getDictionaryMap(String typeCode)`
2. **批量字典类型查询**: `Map<String, Map<String, String>> getDictionaryMaps(List<String> typeCodes)`
3. **输出格式**: `Map<String, String>` (en_code → full_name)
4. **重复处理**: 同一 typeCode 下 en_code 重复时取第一条 (按 sort_code + id 排序)
5. **多租户支持**: 自动获取 ecid 过滤
6. **软删除过滤**: delete_mark = 0
### 📁 数据字典已生成文件 (5 个)
| # | 文件 | 路径 | 状态 |
|---|------|------|------|
| 1 | `DictionaryMapper.java` | `smd/mapper/` | ✅ 已写入 |
| 2 | `DictionaryMapper.xml` | `resources/mapper/smd/` | ✅ 已写入 |
| 3 | `DictionaryItem.java` | `smd/dto/` | ✅ 已写入 |
| 4 | `DictionaryService.java` | `smd/service/` | ✅ 已写入 |
| 5 | `DictionaryServiceImpl.java` | `smd/service/impl/` | ✅ 已写入 |
### 📁 已生成枚举类 (2 个)
| # | 文件 | 说明 |
|---|------|------|
| 1 | `StatusEnum.java` | 状态枚举 (Y=启用,N=禁用) |
| 2 | `YesNoEnum.java` | 是否枚举 (Y=是,N=否) |
### 🔧 字典填充规范
#### VO 设计
- 添加 `xxxName` 字段存储字典翻译后的中文名称
- 示例:`itemType` (en_code) + `itemTypeName` (中文名称)
#### Service 层填充
- **批量查询字典**: 一次查询多个字典类型,避免 N+1 问题
- **通用填充方法**: `fillDictionaryData(ItemVO itemVO, Map<String, Map<String, String>> dictMaps)`
- **性能要求**: 列表查询只查 1 次字典,批量填充
#### 物料信息字典映射
| VO 字段 | 字典类型 | 说明 |
|--------|---------|------|
| `itemTypeName` | `materialsType` | 物料类型名称 |
| `propertiesName` | `itemProperties` | 物料属性名称 |
| `pickingPropertyName` | `pickingProperty` | 领料属性名称 |
| `statusName` | `StatusEnum` | 状态名称 (枚举) |
### 📊 性能优化
| 优化点 | 说明 | 效果 |
|--------|------|------|
| **批量查询字典** | 一次查询多个字典类型 | 避免多次数据库查询 |
| **字典 Map 传入** | `fillDictionaryData()` 接收外部传入的 dictMaps | 避免重复查询 |
| **列表查询优化** | 100 条数据从 101 次查询降低到 2 次 | 性能提升 50 倍 |
| **枚举静态方法** | 枚举翻译使用静态方法 | 无运行时开销 |
### 🧪 单元测试
- 测试文件:`src/test/java/com/witsoft/mica/smd/service/DictionaryServiceTest.java`
- 测试方法:英文命名(避免中文字符)
- Mock 处理:`GlobalUtils.getEcid()` 改为可 mock 的实例方法
- 测试结果:✅ 5 个测试全部通过
### 🐛 数据字典开发修正问题
| # | 问题 | 修正方案 |
|---|------|----------|
| 16 | **fillDictionaryData 重复查询** | 修改方法签名接收 dictMaps 参数,避免 N+1 问题 |
| 17 | **planner 越权生成代码** | 明确 planner 禁止生成代码,由 backend agent 执行 |
---
## 📝 待办事项
### Backend (mica-server)
- [x] 创建 `apis/smdm/ItemApi.java` (Feign 接口)
- [x] 创建 `smd/entity/ItemInfo.java`
- [x] 创建 `smd/mapper/ItemMapper.java`
- [x] 创建 `smd/mapper/ItemMapper.xml`
- [x] 创建 `smd/service/ItemService.java`
- [x] 创建 `smd/service/impl/ItemServiceImpl.java`
- [x] 创建 `smd/controller/ItemController.java`
- [x] 创建 `smd/dto/ItemQueryDTO.java`
- [x] 创建 `smd/dto/ItemFormDTO.java`
- [x] 创建 `smd/vo/ItemVO.java`
- [x] 修正 15 个编译错误
- [x] Maven 编译验证通过
- [x] 数据字典模块开发 (5 个文件 + 2 个枚举类)
- [x] 字典填充逻辑实现
- [x] 单元测试通过
- [ ] 集成测试
### Frontend (mica-web)
- [ ] 创建物料管理 API 封装 (`app/composables/smd/useItemApi.ts`)
- [ ] 创建物料列表页面 (`app/pages/smd/item/index.vue`)
- [ ] 创建物料详情/编辑表单 (`app/pages/smd/item/form.vue`)
- [ ] 创建物料详情页面 (`app/pages/smd/item/detail.vue`)
- [ ] 更新路由配置
---
## 🔗 相关资源
- 数据库:`dmp_smdm.dmp_md_item_info`
- 连接:`mysql -h 47.99.209.185 -P 50036 -u witsoftd -p mica`
- 项目路径:`/root/projects/wit/`
- 规范文档:`/root/.openclaw/workspace-planner/memory/2026-08-04.md`