本文件为 AI Agent 代码生成提供强制规则约束,定义生成边界与规范。 所有规则均为强制性,Agent 生成代码时必须严格遵守。违反规则的代码不可提交。
| 技术 | 版本 | 禁止变更 |
|---|---|---|
| Java | 21 | 禁止使用 Java 22+ 特性 |
| Spring Boot | 4.0.3 | 禁止降级或升级 |
| MyBatis-Plus | 3.5.16 | 使用 mybatis-plus-spring-boot4-starter |
| Sa-Token | 1.45.0 | 使用 sa-token-spring-boot3-starter |
| Hutool | 5.8.40 | 禁止使用 hutool 6.x API |
| FastJson2 | 2.0.53 | 禁止使用 fastjson (v1) |
| Lombok | 1.18.36 | -- |
| Jakarta EE | Jakarta (非 Javax) | Spring Boot 4 使用 jakarta 命名空间 |
haha-admin --> haha-service --> haha-mapper --> haha-entity
haha-miniapp --> haha-service --> haha-mapper --> haha-entity
|
haha-common <---------------------------+(所有模块均可依赖)
haha-sdk(独立模块,不依赖其他内部模块)
禁止:
| 应用 | 模块 | 端口 | Context-Path | 用途 |
|---|---|---|---|---|
| AdminApplication | haha-admin | 7070 | /admin | 运营平台后端 |
| MiniappApplication | haha-miniapp | 7077 | /api | 小程序后端 |
两个应用共享 haha-common / haha-entity / haha-mapper / haha-service,共享 @ComponentScan(basePackages = {"com.haha"}) 和 @MapperScan("com.haha.mapper")。
位置: haha-entity/src/main/java/com/haha/entity/
必须遵守:
package com.haha.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
@TableName("t_xxx") // 表名必须 t_ 前缀
public class Xxx implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.ASSIGN_ID) // 雪花算法
@JsonSerialize(using = ToStringSerializer.class) // Long -> String 防精度丢失
private Long id;
// 日期字段必须加格式注解
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
private LocalDateTime createTime;
// 非数据库字段必须标记
@TableField(exist = false)
private String statusLabel;
// 金额字段使用 BigDecimal,禁止使用 double/float
// private BigDecimal amount;
}
规则:
id 字段必须添加 @JsonSerialize(using = ToStringSerializer.class),禁止遗漏@JsonSerialize(using = ToStringSerializer.class)@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")BigDecimal,禁止使用 double 或 floatSerializable 接口@TableField(exist = false) 标注create_time),Java 字段用驼峰(createTime),MyBatis-Plus 自动映射位置: haha-entity/src/main/java/com/haha/entity/dto/ 或 haha-admin/src/main/java/com/haha/admin/dto/
package com.haha.entity.dto;
import lombok.Data;
import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class XxxCreateDTO {
// 使用 Jakarta Validation 注解
// @NotNull(message = "xxx不能为空")
// @NotEmpty(message = "xxx不能为空")
// @Size(max = 50, message = "xxx长度不能超过50")
private String name;
}
规则:
XxxCreateDTO、XxxUpdateDTO、XxxQueryDTOBasePageQueryDTO(来自 haha-common)并调用 validate() 方法校验分页参数jakarta.validation.constraints.*),禁止使用 javax.validation.*jakarta.validation-api 依赖位置: haha-common/src/main/java/com/haha/common/vo/
规则:
Result<T>(Result.success() / Result.error())PageResult<T>,通过 PageResult.of(IPage<T>) 转换@Data 注解,实现 Serializable位置: haha-mapper/src/main/java/com/haha/mapper/
package com.haha.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.haha.entity.Xxx;
import org.apache.ibatis.annotations.Param;
public interface XxxMapper extends BaseMapper<Xxx> {
// 简单查询:使用注解式 SQL
// @Select("SELECT ... FROM t_xxx WHERE ...")
// List<Xxx> findByCondition(@Param("field") String field);
// 复杂查询:使用 XML 映射文件
// 对应文件:haha-mapper/src/main/resources/mapper/XxxMapper.xml
}
规则:
BaseMapper<Entity>@Select / @Update / @Insert 注解,动态 SQL 用 <script> 标签haha-mapper/src/main/resources/mapper/XxxMapper.xmlresultMap,禁止使用 resultType 自动映射(保证字段可控)@Param 注解位置: haha-service/src/main/java/com/haha/service/
package com.haha.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.haha.entity.Xxx;
public interface XxxService extends IService<Xxx> {
/**
* 方法说明
*
* @param param 参数说明
* @return 返回说明
*/
Xxx getByField(String param);
}
规则:
IService<Entity>Map<String, Object>(已有代码除外,新代码禁止)位置: haha-service/src/main/java/com/haha/service/impl/
package com.haha.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.haha.service.XxxService;
import com.haha.mapper.XxxMapper;
import com.haha.entity.Xxx;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Slf4j
@Service
@RequiredArgsConstructor
public class XxxServiceImpl extends ServiceImpl<XxxMapper, Xxx> implements XxxService {
// 依赖注入:构造器注入(通过 @RequiredArgsConstructor)
private final OtherMapper otherMapper;
// 循环依赖时使用 @Autowired @Lazy
// @Autowired
// @Lazy
// private CircularService circularService;
@Override
@Transactional(rollbackFor = Exception.class)
public Xxx getByField(String param) {
log.info("查询xxx: param={}", param);
return lambdaQuery().eq(Xxx::getField, param).one();
}
}
规则:
ServiceImpl<Mapper, Entity>@Slf4j + @Service + @RequiredArgsConstructorfinal 字段 + @RequiredArgsConstructor)@Autowired @Lazy@Transactional(rollbackFor = Exception.class)lambdaQuery() / lambdaUpdate()page < 1 时设为 1,pageSize > 100 时设为 10位置:
haha-admin/src/main/java/com/haha/admin/controller/小程序:haha-miniapp/src/main/java/com/haha/miniapp/controller/
package com.haha.admin.controller;
import com.haha.admin.annotation.RequirePermission;
import com.haha.common.annotation.Log;
import com.haha.common.enums.OperationType;
import com.haha.common.vo.PageResult;
import com.haha.common.vo.Result;
import com.haha.entity.Xxx;
import com.haha.service.XxxService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
@Slf4j
@RestController
@RequestMapping("/xxx")
@RequiredArgsConstructor
public class XxxController {
private final XxxService xxxService;
@RequirePermission("xxx:read")
@GetMapping("/list")
public Result<PageResult<Xxx>> list(XxxQueryDTO queryDTO) {
queryDTO.validate();
// ... 分页查询
return Result.success("查询成功", PageResult.of(page));
}
@RequirePermission("xxx:read")
@GetMapping("/{id}")
public Result<Xxx> getById(@PathVariable Long id) {
Xxx xxx = xxxService.getDetailById(id);
if (xxx == null) {
return Result.error(404, "xxx不存在");
}
return Result.success("查询成功", xxx);
}
@RequirePermission("xxx:create")
@Log(module = "xxx管理", operation = OperationType.CREATE, summary = "创建xxx")
@PostMapping
public Result<Void> create(@RequestBody XxxCreateDTO dto) {
// ...
return Result.success("创建成功", null);
}
@RequirePermission("xxx:update")
@Log(module = "xxx管理", operation = OperationType.UPDATE, summary = "更新xxx")
@PutMapping("/{id}")
public Result<Void> update(@PathVariable Long id, @RequestBody XxxUpdateDTO dto) {
// ...
return Result.success("更新成功", null);
}
@RequirePermission("xxx:delete")
@Log(module = "xxx管理", operation = OperationType.DELETE, summary = "删除xxx")
@DeleteMapping("/{id}")
public Result<Void> delete(@PathVariable Long id) {
// ...
return Result.success("删除成功", null);
}
}
规则:
@Slf4j + @RestController + @RequestMapping + @RequiredArgsConstructor@RequirePermission 权限注解@Log 操作日志注解Result<T>,禁止直接返回实体或 MapResult<PageResult<T>>Result.error(404, "xxx不存在")@RequestMapping 路径使用小写 kebab-case(如 /timed-discount),禁止驼峰@RequestParam 或 DTO 接收,路径参数用 @PathVariable,请求体用 @RequestBody位置: haha-common/src/main/java/com/haha/common/enums/
package com.haha.common.enums;
import lombok.Getter;
@Getter
public enum XxxStatus {
ACTIVE(1, "活跃"),
INACTIVE(0, "未激活");
private final int code; // 数据库存储值
private final String description; // 中文描述
XxxStatus(int code, String description) {
this.code = code;
this.description = description;
}
public static XxxStatus fromCode(Integer code) {
if (code == null) return null;
for (XxxStatus s : values()) {
if (s.code == code) return s;
}
return null;
}
public static String getDescription(Integer code) {
XxxStatus s = fromCode(code);
return s != null ? s.description : "未知";
}
}
规则:
code 和 description 两个字段,禁止添加 status 等冗余字段code 类型为 String(如 DeviceDoorStatus),数字枚举的 code 类型为 int(如 OrderStatus)fromCode() 静态方法code 值,禁止存储 description 或数字序号位置: haha-common/src/main/java/com/haha/common/constant/
package com.haha.common.constant;
/**
* xxx相关常量
*/
public final class XxxConstants {
private XxxConstants() {} // 禁止实例化
public static final int STATUS_ACTIVE = 1;
public static final String PAY_STATUS_PAID = "PAID";
}
规则:
public final class,私有构造int,字符串状态码用 String| 操作 | HTTP Method | 路径 | 示例 |
|---|---|---|---|
| 分页列表 | GET | /xxx/list | /order/list |
| 详情 | GET | /xxx/{id} | /order/{id} |
| 统计 | GET | /xxx/statistics | /order/statistics |
| 创建 | POST | /xxx | /order |
| 更新 | PUT | /xxx/{id} | /order/{id} |
| 删除 | DELETE | /xxx/{id} | /order/{id} |
| 特殊操作 | POST | /xxx/{id}/action | /order/{id}/refund |
@RequirePermission("module:operation")read / create / update / delete / other:read,写操作用对应 operation@RequirePermission(用户通过 accessToken 鉴权)@Log(module = "模块名", operation = OperationType.XXX, summary = "操作描述")CREATE / UPDATE / DELETE / OTHER@Log{
"code": 200,
"message": "操作成功",
"data": { ... }
}
| 场景 | code | message |
|---|---|---|
| 成功 | 200 | "操作成功" 或具体描述 |
| 参数错误 | 400 | 具体字段错误描述 |
| 未登录 | 401 | "未登录" / "token已过期" 等 |
| 无权限 | 403 | "无访问权限" |
| 资源不存在 | 404 | "xxx不存在" |
| 业务异常 | 自定义 | BusinessException 消息 |
| 系统异常 | 500 | "系统繁忙,请稍后重试" |
禁止:
GlobalExceptionHandler 捕获code=200 但 data 中包含错误信息// 使用 ResponseEnum 枚举
throw new BusinessException(ResponseEnum.ORDER_NOT_FOUND);
// 自定义消息
throw new BusinessException(404, "订单不存在");
// 仅消息(默认500)
throw new BusinessException("操作失败");
t_,如 t_order、t_device、t_door_recordt_timed_discount_activityt_{主实体}_{关联实体},如 t_activity_device、t_coupon_shop@TableId(type = IdType.ASSIGN_ID)IdType.ASSIGN_IDBIGINTIdType.AUTO)create_time、pay_status、order_no)createTime、payStatus、orderNo)map-underscore-to-camel-case: true 自动映射BigDecimal 类型,数据库 DECIMAL(10,2)INT,字符串状态用 VARCHAR(如 pay_status 存 "PAID")DATETIME,对应 Java LocalDateTimehaha-mapper/src/main/resources/mapper/XxxMapper.xmlresultMap,显式映射所有列t_ 前缀<if> / <where> / <set> 标签<sql> 标签复用@TableLogic 注解的实体,BaseMapper 自带方法自动处理AND deleted = 0JavaScript Number.MAX_SAFE_INTEGER = 9007199254740991(16位),雪花 ID 为 19 位数字,超出安全范围导致精度丢失。
强制规则:
id 字段(Long 类型)必须添加 @JsonSerialize(using = ToStringSerializer.class)Long 类型外键字段(userId, orderId, shopId, templateId 等)同样必须添加JacksonConfig 中的 Long->String 模块)替代字段级注解前端接收 ID 必须为字符串格式
// 正确
@TableId(type = IdType.ASSIGN_ID)
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
// 错误 - 缺少 @JsonSerialize
@TableId(type = IdType.ASSIGN_ID)
private Long id;
yyyy-MM-dd HH:mm:ssAsia/Shanghai@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")JavaTimeModule,禁用 WRITE_DATES_AS_TIMESTAMPS处理图片 URL 时必须进行容错判断:
private String normalizeImageUrl(String picUrl) {
if (picUrl == null || picUrl.isEmpty()) {
return picUrl;
}
if (picUrl.startsWith("http://") || picUrl.startsWith("https://")) {
return picUrl;
}
return commonConfig.getImageDomainPrefix() + picUrl;
}
// 正确 - 构造器注入(推荐)
@Slf4j
@Service
@RequiredArgsConstructor
public class XxxServiceImpl {
private final XxxMapper xxxMapper;
private final OtherService otherService;
}
仅在出现循环依赖时使用 @Autowired @Lazy:
// 仅循环依赖时允许
@Autowired
@Lazy
private PaymentService paymentService;
禁止:无循环依赖时使用 @Autowired,应一律使用构造器注入。
Spring Boot 4.0.3 要求 @Configuration 必须显式指定 proxyBeanMethods = false:
// 正确
@Configuration(proxyBeanMethods = false)
public class XxxConfig {
// ...
}
// 错误 - 缺少 proxyBeanMethods = false
@Configuration
public class XxxConfig {
// ...
}
order.paidAmount 取值,禁止使用 order.totalAmountpaidAmount 为空时回退到 totalAmount,确保向前兼容paidAmount = totalAmount - discountAmounttotalAmount:订单总金额(原价)discountAmount:优惠金额(营销活动优惠 + 优惠券优惠)paidAmount:实付总金额handleOrderCallback)中执行discountAmount 和 paidAmountDeviceDoorStatus 枚举仅含 code(String)和 description(String),禁止冗余字段door_status 字段存储枚举 code 值(如 OPENED、CLOSED、ERROR、ANOTHER)DeviceDoorStatus.convertToDbValue() 写入数据库DeviceDoorStatus.fromDbValue() 从数据库读取device.alert.enabled)、冷却期、巡检间隔、信号阈值@JsonSerialize(using = ToStringSerializer.class)@NotNull / @NotEmpty 等 Validation 注解时haha-common/pom.xml 中显式添加 jakarta.validation-api 依赖import jakarta.validation cannot be resolved@Configuration 不指定 proxyBeanMethods = false 会导致 CGLIB 增强失败@Configuration 类添加 (proxyBeanMethods = false)AND deleted = 0 条件BaseMapper 自带方法(selectById, selectList 等)自动处理@Autowired @Lazy,禁止使用 @RequiredArgsConstructor 注入循环依赖@Async 方法不在调用方事务上下文中执行@Transactionaldouble / float 进行金额计算BigDecimal,指定 RoundingMode.HALF_UPamount.divide(divisor, 2, RoundingMode.HALF_UP)`),PowerShell 会将反引号解释为转义字符System.out.println() 或日志输出时避免反引号haha-mapper/src/main/resources/mapper/XxxMapper.xml@JsonSerialize(using = ToStringSerializer.class)@JsonFormat@RequirePermission@Log 注解docs/database/ 目录t_ 前缀BIGINT,雪花算法DECIMAL(10,2)DATETIME@JsonSerialize 和 @JsonFormat 注解不可遗漏deleted 列和 @TableLogic 注解