Files

19 KiB
Raw Permalink Blame History

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_infoItemInfo
    • dmp_md_xxxXxx

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):

    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:

@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:

/**
 * @Description : 物料信息管理
 * @ModifyBrief :
 * @Author : yangxuan
 * @Date : 2026/8/4
 * @Version : 1.0
 */

规范:

  • @Author 固定为 yangxuan
  • @Date 精确到日 (2026/8/4)
  • @ModifyBrief 留空,后续修改时补充

2. 方法注释

/**
 * @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())

示例:

@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 调用日志规范

正确示例:

@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. 字段注释

单行注释即可:

/** 物料编码 */
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 静态引用

错误:

return ResponseModel.success(result);  // ❌ success 不是静态方法

正确:

return ResponseModel.succeed(result);  // ✅ 使用静态方法 succeed

问题 2 详解:分页 XML / 详情 MP

分页查询 (原生 XML):

// 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):

// ItemServiceImpl.java
ItemInfo item = itemMapper.selectById(id);

问题 4 详解:Feign 调用日志

正确示例:

@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:

@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):

@Override
public PageDomain<ItemVO> queryPageList(ItemQueryDTO dto) {
    // 直接使用 dto.getEcid(),不再自己获取
    List<ItemVO> list = itemMapper.queryPageList(dto);
    // ...
}

问题 7 详解:使用 PageDomain

Service 手动分页:

@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:

<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 裸类型

错误:

public ResponseModel<Map<String, Object>> insertItem(ItemFormDTO dto) {  // ❌ 泛型参数
    // ...
}

正确 (与项目其他 Feign 接口一致):

public ResponseModel insertItem(ItemFormDTO dto) {  // ✅ 裸类型
    // ...
}

参考项目其他 Feign 接口:

// 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 替代方案

错误:

log.debug("入参:{}", JsonUtils.toJson(dto));  // ❌ JsonUtils.toJson() 方法不存在

正确 (使用 fastjson):

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)

  • 创建 apis/smdm/ItemApi.java (Feign 接口)
  • 创建 smd/entity/ItemInfo.java
  • 创建 smd/mapper/ItemMapper.java
  • 创建 smd/mapper/ItemMapper.xml
  • 创建 smd/service/ItemService.java
  • 创建 smd/service/impl/ItemServiceImpl.java
  • 创建 smd/controller/ItemController.java
  • 创建 smd/dto/ItemQueryDTO.java
  • 创建 smd/dto/ItemFormDTO.java
  • 创建 smd/vo/ItemVO.java
  • 修正 15 个编译错误
  • Maven 编译验证通过
  • 数据字典模块开发 (5 个文件 + 2 个枚举类)
  • 字典填充逻辑实现
  • 单元测试通过
  • 集成测试

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