19 KiB
19 KiB
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→ItemInfodmp_md_xxx→Xxx
3. 实体类规范
-
基础字段 (业务必需):
字段说明 数据库字段 Java 字段 物料编码 item_codeitemCode物料名称 item_nameitemName规格型号 item_specitemSpec物料类型 item_typeitemType物料分类 item_categorys_codeitemCategorysCode物料属性 propertiesproperties主单位 unit_codeunitCode辅单位 assist_unit_codeassistUnitCode物料描述 item_descitemDesc供应商 vendor_codevendorCode默认仓库 good_warehousegoodWarehouse批次控制 batch_controlbatchControl(默认 0/关)领料属性 picking_propertypickingProperty(默认 AD/按单领料)状态 statusstatus -
审计字段 (继承
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 | 配置项不是业务实体 |
📋 数据字典核心功能
- 单个字典类型查询:
Map<String, String> getDictionaryMap(String typeCode) - 批量字典类型查询:
Map<String, Map<String, String>> getDictionaryMaps(List<String> typeCodes) - 输出格式:
Map<String, String>(en_code → full_name) - 重复处理: 同一 typeCode 下 en_code 重复时取第一条 (按 sort_code + id 排序)
- 多租户支持: 自动获取 ecid 过滤
- 软删除过滤: 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