ai-engineering-init 1.14.0 → 1.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -101,6 +101,120 @@ feat(order): 新增订单导出功能
101
101
  Closes #123
102
102
  ```
103
103
 
104
+ ## 数据类型规范
105
+
106
+ ### 布尔语义字段必须使用 Boolean
107
+
108
+ ```java
109
+ // ❌ 错误
110
+ private Integer ifNarrow;
111
+ private Integer isEnabled;
112
+
113
+ // ✅ 正确
114
+ private Boolean narrow; // getter 自动生成 isNarrow()
115
+ private Boolean enabled; // getter 自动生成 isEnabled()
116
+ ```
117
+
118
+ **规则**:
119
+ - 语义为"是/否"的字段,类型必须为 `Boolean`
120
+ - 字段名不加 `if`/`is`/`has` 前缀(JavaBean 规范中 `Boolean` 的 getter 自动生成 `isXxx()`)
121
+ - 数据库字段使用 `TINYINT(1)` 或 `BIT(1)`
122
+
123
+ ### 枚举字段必须提供明确约束
124
+
125
+ ```java
126
+ // ❌ 错误:调用方无法知道合法值
127
+ @ApiModelProperty(value = "操作类型")
128
+ private Integer tradeType;
129
+
130
+ // ✅ 方案一:VO/DTO 层直接用枚举(推荐)
131
+ @ApiModelProperty(value = "操作类型")
132
+ private AccTradeTypeEnum tradeType;
133
+
134
+ // ✅ 方案二:保留 Integer 但标注合法值
135
+ @ApiModelProperty(value = "操作类型:1-充值 2-消费 3-退款", allowableValues = "1,2,3")
136
+ private Integer tradeType;
137
+ ```
138
+
139
+ ### 金额字段禁止使用浮点类型
140
+
141
+ ```java
142
+ // ❌ 错误
143
+ private Double amount;
144
+ private Float price;
145
+
146
+ // ✅ 正确:Entity/Service 层用 Long(分),VO 层展示用 BigDecimal(元)
147
+ private Long amountFen;
148
+ private BigDecimal amountYuan;
149
+ ```
150
+
151
+ ### 原始类型 vs 包装类型
152
+
153
+ | 场景 | 用原始类型 | 用包装类型 |
154
+ |------|----------|----------|
155
+ | Entity / VO / DTO 字段 | — | ✅ 统一用包装类型 |
156
+ | 方法参数(不允许 null) | ✅ `int count` | — |
157
+ | 方法参数(允许 null) | — | ✅ `Integer count` |
158
+ | 局部变量 | ✅ `int i = 0` | — |
159
+
160
+ ## Optional 使用规范
161
+
162
+ ```java
163
+ // ❌ 错误:of() 不接受 null,value 为 null 直接 NPE
164
+ Optional.of(value).orElse(defaultValue);
165
+
166
+ // ✅ 正确:ofNullable() 安全处理 null
167
+ Optional.ofNullable(value).orElse(defaultValue);
168
+
169
+ // ❌ 禁止:Optional 作为方法参数或类字段
170
+ public void process(Optional<String> name) { ... }
171
+ private Optional<String> name;
172
+
173
+ // ✅ 允许:Optional 作为方法返回值、链式处理
174
+ public Optional<Entity> findById(Long id) { ... }
175
+ Optional.ofNullable(entity).map(Entity::getConfig).orElse(DEFAULT_VALUE);
176
+ ```
177
+
178
+ ## @Transactional 规范
179
+
180
+ ```java
181
+ // ❌ 错误:默认只回滚 RuntimeException
182
+ @Transactional
183
+ public void createOrder() { ... }
184
+
185
+ // ✅ 正确:显式指定回滚异常
186
+ @Transactional(rollbackFor = Exception.class)
187
+ public void createOrder() { ... }
188
+ ```
189
+
190
+ - 所有 `@Transactional` 必须显式写 `rollbackFor = Exception.class`
191
+ - 只读查询不加 `@Transactional`(或用 `readOnly = true`)
192
+ - 事务方法不要 try-catch 吞掉异常,否则事务不回滚
193
+
194
+ ## TODO 管理规范
195
+
196
+ ```java
197
+ // ❌ 错误:无负责人、无日期、无跟踪
198
+ // TODO 修改一下
199
+
200
+ // ✅ 正确:完整 TODO 格式
201
+ // TODO(@陈沈杰, 2026-03-20, #TASK-1234): 移动端 AppId 赋值逻辑待产品确认
202
+ ```
203
+
204
+ - 每个 TODO 必须有对应的任务号
205
+ - 超过 2 个迭代未处理的 TODO 必须清理
206
+ - 不用的代码直接删除,不要注释保留
207
+
208
+ ## 代码格式化规范
209
+
210
+ | 项目 | 规范 |
211
+ |------|------|
212
+ | 缩进 | 4 个空格(不用 Tab) |
213
+ | 行宽 | 120 字符 |
214
+ | 大括号 | K&R 风格(同行开始) |
215
+ | 空行 | 方法间 1 个空行,逻辑块间 1 个空行 |
216
+ | import | 分组排序:java → jakarta → org → net → com,组间空行 |
217
+
104
218
  ## 代码示例
105
219
 
106
220
  ### 统一响应格式
@@ -161,3 +275,8 @@ public enum OrderStatusEnum {
161
275
  | Git 提交信息写"fix bug" | 写清楚修了什么:`fix(order): 修复金额计算精度丢失` |
162
276
  | Boolean 变量名:`flag` | 有意义的名字:`isActive`, `hasPermission` |
163
277
  | 缩写命名:`usr`, `mgr` | 完整命名:`user`, `manager` |
278
+ | `Optional.of(可能null值)` | `Optional.ofNullable(value)` |
279
+ | `@Transactional` 无 rollbackFor | `@Transactional(rollbackFor = Exception.class)` |
280
+ | TODO 无负责人和日期 | `// TODO(@负责人, 日期, #任务号): 描述` |
281
+ | 布尔字段用 `Integer` | 用 `Boolean` 类型 |
282
+ | 枚举字段无合法值说明 | `@ApiModelProperty` 标注合法值或直接用枚举类型 |
@@ -394,9 +394,186 @@ public class OrderService {
394
394
  }
395
395
  ```
396
396
 
397
- ## 通用代码规范
397
+ ## 数据类型规范
398
+
399
+ ### 布尔字段命名
400
+
401
+ ```java
402
+ // ❌ 错误:前缀冗余、类型错误
403
+ private Integer ifNarrow;
404
+ private Integer isEnabled;
405
+
406
+ // ✅ 正确:Boolean 类型,无前缀
407
+ private Boolean narrow; // getter → isNarrow()
408
+ private Boolean enabled; // getter → isEnabled()
409
+ ```
410
+
411
+ ### 枚举字段标注
412
+
413
+ ```java
414
+ // ❌ 错误:只靠注释 @see
415
+ /** @see AccTradeTypeEnum */
416
+ private Integer tradeType;
417
+
418
+ // ✅ 方案一:VO/DTO 用枚举类型(配合 @JsonValue)
419
+ private AccTradeTypeEnum tradeType;
420
+
421
+ // ✅ 方案二:@ApiModelProperty 标注合法值
422
+ @ApiModelProperty(value = "操作类型:1-充值 2-消费 3-退款", allowableValues = "1,2,3")
423
+ private Integer tradeType;
424
+ ```
425
+
426
+ ## MyBatis-Plus 安全规范
427
+
428
+ ### selectOne 必须有唯一保障
429
+
430
+ ```java
431
+ // ❌ 危险:多条记录时抛 TooManyResultsException
432
+ Entity entity = mapper.selectOne(wrapper);
433
+
434
+ // ✅ 方案一:LIMIT 1
435
+ Entity entity = mapper.selectOne(wrapper.last("LIMIT 1"));
436
+
437
+ // ✅ 方案二:selectList 取第一条
438
+ List<Entity> list = mapper.selectList(wrapper);
439
+ Entity entity = CollUtil.isNotEmpty(list) ? list.get(0) : null;
440
+
441
+ // ✅ 方案三:确保有唯一索引(注释说明)
442
+ // 唯一索引:UNIQUE KEY (order_no, del_flag)
443
+ Entity entity = mapper.selectOne(wrapper);
444
+ ```
445
+
446
+ ### 存在性判断用 EXISTS,禁用 selectCount
447
+
448
+ ```java
449
+ // ❌ 低效:100万行表 ~200ms
450
+ Long count = mapper.selectCount(wrapper);
451
+ if (count > 0) { ... }
452
+
453
+ // ✅ 高效:~2ms(MyBatis-Plus 3.5.4+)
454
+ boolean exists = mapper.exists(wrapper);
455
+
456
+ // ✅ 或 selectList + LIMIT 1
457
+ boolean exists = CollUtil.isNotEmpty(mapper.selectList(wrapper.last("LIMIT 1")));
458
+ ```
459
+
460
+ ### Wrapper 嵌套不超过 2 层
461
+
462
+ ```java
463
+ // ❌ 过于复杂的 Wrapper
464
+ wrapper.and(w -> w.eq(A::getType, type)
465
+ .or(q -> q.eq(A::getType, 0))
466
+ .or(!PersonTypeEnum.LABOUR.getKey().equals(type),
467
+ q -> q.eq(A::getType, PSN_TYPE_SHARED)));
468
+
469
+ // ✅ 复杂查询写到 XML 中
470
+ List<Entity> list = mapper.selectByTypeCondition(type, isLabour);
471
+ ```
472
+
473
+ ### 禁止 SELECT *
474
+
475
+ ```xml
476
+ <!-- ❌ 禁止 -->
477
+ <select id="selectAll">SELECT * FROM t_order WHERE del_flag = 2</select>
478
+
479
+ <!-- ✅ 明确列出字段 -->
480
+ <select id="selectAll">SELECT id, order_no, amount, status, crtime FROM t_order WHERE del_flag = 2</select>
481
+ ```
482
+
483
+ ## Redis 使用规范
484
+
485
+ ### 禁止 KEYS 命令
398
486
 
399
- 无论使用哪种项目架构,以下规范都是通用的:
487
+ ```java
488
+ // ❌ 严禁:KEYS 阻塞 Redis
489
+ Set<Object> keys = keysByPattern(pattern);
490
+ Set<Object> keys = redisTemplate.keys(pattern);
491
+
492
+ // ✅ 使用 Redisson deleteByPattern(内部 SCAN + UNLINK)
493
+ RedissonClient redisson = SpringUtil.getBean(RedissonClient.class);
494
+ redisson.getKeys().deleteByPattern(keyPattern);
495
+ ```
496
+
497
+ ## Optional 使用规范
498
+
499
+ ```java
500
+ // ❌ 错误:of() 不接受 null
501
+ Optional.of(value).orElse(defaultValue);
502
+
503
+ // ✅ 正确:ofNullable()
504
+ Optional.ofNullable(value).orElse(defaultValue);
505
+
506
+ // ✅ 链式安全转换
507
+ Optional.ofNullable(model.getReserveRate())
508
+ .map(BigDecimal::new)
509
+ .orElse(BigDecimal.ZERO);
510
+
511
+ // ❌ 禁止作为方法参数或类字段
512
+ // ✅ 允许作为方法返回值
513
+ ```
514
+
515
+ ## @Transactional 规范
516
+
517
+ ```java
518
+ // ❌ 默认只回滚 RuntimeException
519
+ @Transactional
520
+ public void createOrder() { ... }
521
+
522
+ // ✅ 显式指定 rollbackFor
523
+ @Transactional(rollbackFor = Exception.class)
524
+ public void createOrder() { ... }
525
+ ```
526
+
527
+ - 事务方法不要 try-catch 吞掉异常
528
+ - 只读查询不加 `@Transactional`
529
+
530
+ ## 业务逻辑分层规范
531
+
532
+ ```java
533
+ // ❌ 错误:业务判断混在数据操作中
534
+ public void processOrder(Long orderId) {
535
+ OrderInfo order = orderMapper.selectById(orderId);
536
+ if (order.getStatus() == 1 && order.getPayTime() != null
537
+ && ChronoUnit.HOURS.between(order.getPayTime(), LocalDateTime.now()) < 24) {
538
+ order.setStatus(2);
539
+ orderMapper.updateById(order);
540
+ accWalletService.deduct(order.getCustId(), order.getAmount());
541
+ }
542
+ }
543
+
544
+ // ✅ 正确:分层清晰
545
+ public void processOrder(Long orderId) {
546
+ OrderInfo order = orderMapper.selectById(orderId);
547
+ if (ObjectUtil.isNull(order)) {
548
+ throw new LeException(I18n.getMessage("order_not_found"));
549
+ }
550
+ checkCanProcess(order); // 业务校验(独立方法)
551
+ order.markAsProcessed(); // 状态变更(Entity 方法封装)
552
+ orderMapper.updateById(order);
553
+ afterOrderProcessed(order); // 后续动作(独立方法)
554
+ }
555
+ ```
556
+
557
+ | 层 | 职责 | 不应做的 |
558
+ |----|------|---------|
559
+ | Controller | 参数接收、格式转换 | 不含业务判断 |
560
+ | Business | 业务编排、跨 Service 协调 | 不直接操作 Mapper |
561
+ | Service | 单表 CRUD、单表事务 | 不含跨表业务逻辑 |
562
+ | Mapper | SQL 映射 | 不含业务逻辑 |
563
+
564
+ ## TODO 管理规范
565
+
566
+ ```java
567
+ // ❌ 错误
568
+ // TODO 修改一下
569
+
570
+ // ✅ 正确
571
+ // TODO(@陈沈杰, 2026-03-20, #TASK-1234): 移动端 AppId 赋值逻辑待产品确认
572
+ ```
573
+
574
+ - 不用的代码直接删除,不要注释保留
575
+
576
+ ## 通用代码规范
400
577
 
401
578
  1. **禁止使用 `SELECT *`**:明确指定字段
402
579
  2. **使用参数化查询**:`#{}` 而非 `${}`
@@ -101,6 +101,120 @@ feat(order): 新增订单导出功能
101
101
  Closes #123
102
102
  ```
103
103
 
104
+ ## 数据类型规范
105
+
106
+ ### 布尔语义字段必须使用 Boolean
107
+
108
+ ```java
109
+ // ❌ 错误
110
+ private Integer ifNarrow;
111
+ private Integer isEnabled;
112
+
113
+ // ✅ 正确
114
+ private Boolean narrow; // getter 自动生成 isNarrow()
115
+ private Boolean enabled; // getter 自动生成 isEnabled()
116
+ ```
117
+
118
+ **规则**:
119
+ - 语义为"是/否"的字段,类型必须为 `Boolean`
120
+ - 字段名不加 `if`/`is`/`has` 前缀(JavaBean 规范中 `Boolean` 的 getter 自动生成 `isXxx()`)
121
+ - 数据库字段使用 `TINYINT(1)` 或 `BIT(1)`
122
+
123
+ ### 枚举字段必须提供明确约束
124
+
125
+ ```java
126
+ // ❌ 错误:调用方无法知道合法值
127
+ @ApiModelProperty(value = "操作类型")
128
+ private Integer tradeType;
129
+
130
+ // ✅ 方案一:VO/DTO 层直接用枚举(推荐)
131
+ @ApiModelProperty(value = "操作类型")
132
+ private AccTradeTypeEnum tradeType;
133
+
134
+ // ✅ 方案二:保留 Integer 但标注合法值
135
+ @ApiModelProperty(value = "操作类型:1-充值 2-消费 3-退款", allowableValues = "1,2,3")
136
+ private Integer tradeType;
137
+ ```
138
+
139
+ ### 金额字段禁止使用浮点类型
140
+
141
+ ```java
142
+ // ❌ 错误
143
+ private Double amount;
144
+ private Float price;
145
+
146
+ // ✅ 正确:Entity/Service 层用 Long(分),VO 层展示用 BigDecimal(元)
147
+ private Long amountFen;
148
+ private BigDecimal amountYuan;
149
+ ```
150
+
151
+ ### 原始类型 vs 包装类型
152
+
153
+ | 场景 | 用原始类型 | 用包装类型 |
154
+ |------|----------|----------|
155
+ | Entity / VO / DTO 字段 | — | ✅ 统一用包装类型 |
156
+ | 方法参数(不允许 null) | ✅ `int count` | — |
157
+ | 方法参数(允许 null) | — | ✅ `Integer count` |
158
+ | 局部变量 | ✅ `int i = 0` | — |
159
+
160
+ ## Optional 使用规范
161
+
162
+ ```java
163
+ // ❌ 错误:of() 不接受 null,value 为 null 直接 NPE
164
+ Optional.of(value).orElse(defaultValue);
165
+
166
+ // ✅ 正确:ofNullable() 安全处理 null
167
+ Optional.ofNullable(value).orElse(defaultValue);
168
+
169
+ // ❌ 禁止:Optional 作为方法参数或类字段
170
+ public void process(Optional<String> name) { ... }
171
+ private Optional<String> name;
172
+
173
+ // ✅ 允许:Optional 作为方法返回值、链式处理
174
+ public Optional<Entity> findById(Long id) { ... }
175
+ Optional.ofNullable(entity).map(Entity::getConfig).orElse(DEFAULT_VALUE);
176
+ ```
177
+
178
+ ## @Transactional 规范
179
+
180
+ ```java
181
+ // ❌ 错误:默认只回滚 RuntimeException
182
+ @Transactional
183
+ public void createOrder() { ... }
184
+
185
+ // ✅ 正确:显式指定回滚异常
186
+ @Transactional(rollbackFor = Exception.class)
187
+ public void createOrder() { ... }
188
+ ```
189
+
190
+ - 所有 `@Transactional` 必须显式写 `rollbackFor = Exception.class`
191
+ - 只读查询不加 `@Transactional`(或用 `readOnly = true`)
192
+ - 事务方法不要 try-catch 吞掉异常,否则事务不回滚
193
+
194
+ ## TODO 管理规范
195
+
196
+ ```java
197
+ // ❌ 错误:无负责人、无日期、无跟踪
198
+ // TODO 修改一下
199
+
200
+ // ✅ 正确:完整 TODO 格式
201
+ // TODO(@陈沈杰, 2026-03-20, #TASK-1234): 移动端 AppId 赋值逻辑待产品确认
202
+ ```
203
+
204
+ - 每个 TODO 必须有对应的任务号
205
+ - 超过 2 个迭代未处理的 TODO 必须清理
206
+ - 不用的代码直接删除,不要注释保留
207
+
208
+ ## 代码格式化规范
209
+
210
+ | 项目 | 规范 |
211
+ |------|------|
212
+ | 缩进 | 4 个空格(不用 Tab) |
213
+ | 行宽 | 120 字符 |
214
+ | 大括号 | K&R 风格(同行开始) |
215
+ | 空行 | 方法间 1 个空行,逻辑块间 1 个空行 |
216
+ | import | 分组排序:java → jakarta → org → net → com,组间空行 |
217
+
104
218
  ## 代码示例
105
219
 
106
220
  ### 统一响应格式
@@ -161,3 +275,8 @@ public enum OrderStatusEnum {
161
275
  | Git 提交信息写"fix bug" | 写清楚修了什么:`fix(order): 修复金额计算精度丢失` |
162
276
  | Boolean 变量名:`flag` | 有意义的名字:`isActive`, `hasPermission` |
163
277
  | 缩写命名:`usr`, `mgr` | 完整命名:`user`, `manager` |
278
+ | `Optional.of(可能null值)` | `Optional.ofNullable(value)` |
279
+ | `@Transactional` 无 rollbackFor | `@Transactional(rollbackFor = Exception.class)` |
280
+ | TODO 无负责人和日期 | `// TODO(@负责人, 日期, #任务号): 描述` |
281
+ | 布尔字段用 `Integer` | 用 `Boolean` 类型 |
282
+ | 枚举字段无合法值说明 | `@ApiModelProperty` 标注合法值或直接用枚举类型 |
@@ -394,9 +394,186 @@ public class OrderService {
394
394
  }
395
395
  ```
396
396
 
397
- ## 通用代码规范
397
+ ## 数据类型规范
398
+
399
+ ### 布尔字段命名
400
+
401
+ ```java
402
+ // ❌ 错误:前缀冗余、类型错误
403
+ private Integer ifNarrow;
404
+ private Integer isEnabled;
405
+
406
+ // ✅ 正确:Boolean 类型,无前缀
407
+ private Boolean narrow; // getter → isNarrow()
408
+ private Boolean enabled; // getter → isEnabled()
409
+ ```
410
+
411
+ ### 枚举字段标注
412
+
413
+ ```java
414
+ // ❌ 错误:只靠注释 @see
415
+ /** @see AccTradeTypeEnum */
416
+ private Integer tradeType;
417
+
418
+ // ✅ 方案一:VO/DTO 用枚举类型(配合 @JsonValue)
419
+ private AccTradeTypeEnum tradeType;
420
+
421
+ // ✅ 方案二:@ApiModelProperty 标注合法值
422
+ @ApiModelProperty(value = "操作类型:1-充值 2-消费 3-退款", allowableValues = "1,2,3")
423
+ private Integer tradeType;
424
+ ```
425
+
426
+ ## MyBatis-Plus 安全规范
427
+
428
+ ### selectOne 必须有唯一保障
429
+
430
+ ```java
431
+ // ❌ 危险:多条记录时抛 TooManyResultsException
432
+ Entity entity = mapper.selectOne(wrapper);
433
+
434
+ // ✅ 方案一:LIMIT 1
435
+ Entity entity = mapper.selectOne(wrapper.last("LIMIT 1"));
436
+
437
+ // ✅ 方案二:selectList 取第一条
438
+ List<Entity> list = mapper.selectList(wrapper);
439
+ Entity entity = CollUtil.isNotEmpty(list) ? list.get(0) : null;
440
+
441
+ // ✅ 方案三:确保有唯一索引(注释说明)
442
+ // 唯一索引:UNIQUE KEY (order_no, del_flag)
443
+ Entity entity = mapper.selectOne(wrapper);
444
+ ```
445
+
446
+ ### 存在性判断用 EXISTS,禁用 selectCount
447
+
448
+ ```java
449
+ // ❌ 低效:100万行表 ~200ms
450
+ Long count = mapper.selectCount(wrapper);
451
+ if (count > 0) { ... }
452
+
453
+ // ✅ 高效:~2ms(MyBatis-Plus 3.5.4+)
454
+ boolean exists = mapper.exists(wrapper);
455
+
456
+ // ✅ 或 selectList + LIMIT 1
457
+ boolean exists = CollUtil.isNotEmpty(mapper.selectList(wrapper.last("LIMIT 1")));
458
+ ```
459
+
460
+ ### Wrapper 嵌套不超过 2 层
461
+
462
+ ```java
463
+ // ❌ 过于复杂的 Wrapper
464
+ wrapper.and(w -> w.eq(A::getType, type)
465
+ .or(q -> q.eq(A::getType, 0))
466
+ .or(!PersonTypeEnum.LABOUR.getKey().equals(type),
467
+ q -> q.eq(A::getType, PSN_TYPE_SHARED)));
468
+
469
+ // ✅ 复杂查询写到 XML 中
470
+ List<Entity> list = mapper.selectByTypeCondition(type, isLabour);
471
+ ```
472
+
473
+ ### 禁止 SELECT *
474
+
475
+ ```xml
476
+ <!-- ❌ 禁止 -->
477
+ <select id="selectAll">SELECT * FROM t_order WHERE del_flag = 2</select>
478
+
479
+ <!-- ✅ 明确列出字段 -->
480
+ <select id="selectAll">SELECT id, order_no, amount, status, crtime FROM t_order WHERE del_flag = 2</select>
481
+ ```
482
+
483
+ ## Redis 使用规范
484
+
485
+ ### 禁止 KEYS 命令
398
486
 
399
- 无论使用哪种项目架构,以下规范都是通用的:
487
+ ```java
488
+ // ❌ 严禁:KEYS 阻塞 Redis
489
+ Set<Object> keys = keysByPattern(pattern);
490
+ Set<Object> keys = redisTemplate.keys(pattern);
491
+
492
+ // ✅ 使用 Redisson deleteByPattern(内部 SCAN + UNLINK)
493
+ RedissonClient redisson = SpringUtil.getBean(RedissonClient.class);
494
+ redisson.getKeys().deleteByPattern(keyPattern);
495
+ ```
496
+
497
+ ## Optional 使用规范
498
+
499
+ ```java
500
+ // ❌ 错误:of() 不接受 null
501
+ Optional.of(value).orElse(defaultValue);
502
+
503
+ // ✅ 正确:ofNullable()
504
+ Optional.ofNullable(value).orElse(defaultValue);
505
+
506
+ // ✅ 链式安全转换
507
+ Optional.ofNullable(model.getReserveRate())
508
+ .map(BigDecimal::new)
509
+ .orElse(BigDecimal.ZERO);
510
+
511
+ // ❌ 禁止作为方法参数或类字段
512
+ // ✅ 允许作为方法返回值
513
+ ```
514
+
515
+ ## @Transactional 规范
516
+
517
+ ```java
518
+ // ❌ 默认只回滚 RuntimeException
519
+ @Transactional
520
+ public void createOrder() { ... }
521
+
522
+ // ✅ 显式指定 rollbackFor
523
+ @Transactional(rollbackFor = Exception.class)
524
+ public void createOrder() { ... }
525
+ ```
526
+
527
+ - 事务方法不要 try-catch 吞掉异常
528
+ - 只读查询不加 `@Transactional`
529
+
530
+ ## 业务逻辑分层规范
531
+
532
+ ```java
533
+ // ❌ 错误:业务判断混在数据操作中
534
+ public void processOrder(Long orderId) {
535
+ OrderInfo order = orderMapper.selectById(orderId);
536
+ if (order.getStatus() == 1 && order.getPayTime() != null
537
+ && ChronoUnit.HOURS.between(order.getPayTime(), LocalDateTime.now()) < 24) {
538
+ order.setStatus(2);
539
+ orderMapper.updateById(order);
540
+ accWalletService.deduct(order.getCustId(), order.getAmount());
541
+ }
542
+ }
543
+
544
+ // ✅ 正确:分层清晰
545
+ public void processOrder(Long orderId) {
546
+ OrderInfo order = orderMapper.selectById(orderId);
547
+ if (ObjectUtil.isNull(order)) {
548
+ throw new LeException(I18n.getMessage("order_not_found"));
549
+ }
550
+ checkCanProcess(order); // 业务校验(独立方法)
551
+ order.markAsProcessed(); // 状态变更(Entity 方法封装)
552
+ orderMapper.updateById(order);
553
+ afterOrderProcessed(order); // 后续动作(独立方法)
554
+ }
555
+ ```
556
+
557
+ | 层 | 职责 | 不应做的 |
558
+ |----|------|---------|
559
+ | Controller | 参数接收、格式转换 | 不含业务判断 |
560
+ | Business | 业务编排、跨 Service 协调 | 不直接操作 Mapper |
561
+ | Service | 单表 CRUD、单表事务 | 不含跨表业务逻辑 |
562
+ | Mapper | SQL 映射 | 不含业务逻辑 |
563
+
564
+ ## TODO 管理规范
565
+
566
+ ```java
567
+ // ❌ 错误
568
+ // TODO 修改一下
569
+
570
+ // ✅ 正确
571
+ // TODO(@陈沈杰, 2026-03-20, #TASK-1234): 移动端 AppId 赋值逻辑待产品确认
572
+ ```
573
+
574
+ - 不用的代码直接删除,不要注释保留
575
+
576
+ ## 通用代码规范
400
577
 
401
578
  1. **禁止使用 `SELECT *`**:明确指定字段
402
579
  2. **使用参数化查询**:`#{}` 而非 `${}`
package/CLAUDE.md CHANGED
@@ -94,34 +94,6 @@
94
94
  | 深度分析、仔细思考、全面评估 | `sequential-thinking` |
95
95
  | 最佳实践、官方文档、标准写法 | `context7` |
96
96
  | 打开浏览器、截图、检查元素 | `chrome-devtools` |
97
- | 代码原型、代码审查、算法分析 | `codex` (mcp__codex__codex) |
98
-
99
- ## Core Instruction for CodeX MCP
100
-
101
- 在任何时刻,你必须思考当前过程可以如何与 codex 进行协作,如何调用 Codex 为你提供的 MCP 工具作为你客观全面分析的保障。其中你**务必执行**以下几个步骤:
102
-
103
- **1** 在你对用户需求形成初步分析后,将用户需求、初始思路告知 codex,并要求其完善需求分析和实施计划。
104
-
105
- **2** 在实施具体编码任务前,**必须向 codex 索要代码实现原型(要求 codex 仅给出 unified diff patch,严禁对代码做任何真实修改)**。在获取代码原型后,你**只能以此为逻辑参考,再次对代码修改进行重写**,形成企业生产级别、可读性极高、可维护性极高的代码后,才能实施具体编程修改任务。
106
-
107
- **3** 无论何时,只要完成切实编码行为后,**必须立即使用 codex review 代码改动和对应需求完成程度**。
108
-
109
- **4** codex 只能给出参考,你**必须有自己的思考,甚至需要对 codex 的回答提出质疑**。尽信书则不如无书,你与 codex 的最终使命都是达成统一、全面、精准的意见,所以你们必须不断争辩以找到通向真理的唯一途径。
110
-
111
- ### Codex 工具调用规范
112
-
113
- - **工具名**:`mcp__codex__codex`
114
- - **必选参数**:`PROMPT`(指令)、`cd`(工作目录,必须存在)
115
- - **沙箱策略**:始终使用 `sandbox="read-only"`,严禁 codex 修改真实代码
116
- - **会话管理**:保存每次返回的 `SESSION_ID`,多轮交互时传入以保持上下文
117
- - **输出约定**:要求 codex 仅输出 unified diff patch,不执行实际文件修改
118
-
119
- ```
120
- 标准协作流程:
121
- 需求分析 → codex 完善方案
122
- 编码前 → codex 给出 diff 原型(只读参考)
123
- 编码后 → codex review 改动质量
124
- ```
125
97
 
126
98
  ## Skills 强制评估
127
99