# 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 workshop); @PostMapping("/create") ResponseModel insertWorkshop(@RequestBody Map 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 */ ``` **规范**: - 公共接口方法必须添加方法注释 - 私有方法可选添加 - `@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> 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 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` | `ItemController.java`, `ItemServiceImpl.java` | | 10 | **Map 类型转换警告** | 使用 `Convert.convert(Map.class, dto)` 或 `@SuppressWarnings` | `ItemController.java`, `ItemServiceImpl.java` | | 11 | **XML 使用 `` + `` 片段** | 公共列定义和查询条件使用 SQL 片段复用 | `ItemMapper.xml` | | 12 | **ItemApi ResponseModel 泛型** | Feign 接口返回 `ResponseModel` | `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 ``` **详情查询** (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 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> 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 queryPageList(ItemQueryDTO dto) { // 直接使用 dto.getEcid(),不再自己获取 List list = itemMapper.queryPageList(dto); // ... } ``` --- ### 问题 7 详解:使用 PageDomain **Service 手动分页**: ```java @Override public PageDomain 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 list = itemMapper.queryPageList(dto); // 构建 PageDomain PageDomain page = new PageDomain<>(pageNo, pageSize, total); page.setList(list); return page; } ``` --- ### 问题 11 详解:XML SQL 片段 **ItemMapper.xml**: ```xml 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 AND t.ecid = #{dto.ecid} AND t.item_name LIKE CONCAT('%', #{dto.itemName}, '%') AND t.item_type = #{dto.itemType} AND t.status = #{dto.status} ``` --- ### 问题 13 详解:ResponseModel 裸类型 **错误**: ```java public ResponseModel> insertItem(ItemFormDTO dto) { // ❌ 泛型参数 // ... } ``` **正确** (与项目其他 Feign 接口一致): ```java public ResponseModel insertItem(ItemFormDTO dto) { // ✅ 裸类型 // ... } ``` **参考项目其他 Feign 接口**: ```java // WorkshopApi.java ResponseModel queryWorkshopPageList(@RequestBody Map workshop); ResponseModel insertWorkshop(@RequestBody Map 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 getDictionaryMap(String typeCode)` 2. **批量字典类型查询**: `Map> getDictionaryMaps(List typeCodes)` 3. **输出格式**: `Map` (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> 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`