Quellcode durchsuchen

创建项目后端代码规则文件

skyline vor 1 Monat
Ursprung
Commit
fd5ae0abf4
1 geänderte Dateien mit 723 neuen und 0 gelöschten Zeilen
  1. 723 0
      AGENTS.md

+ 723 - 0
AGENTS.md

@@ -0,0 +1,723 @@
+# RULE.md - 哈哈零售系统智能体代码生成规则
+
+> 本文件为 AI Agent 代码生成提供强制规则约束,定义生成边界与规范。
+> 所有规则均为强制性,Agent 生成代码时必须严格遵守。违反规则的代码不可提交。
+
+---
+
+## 1. 项目边界
+
+### 1.1 技术栈版本锁定
+
+| 技术 | 版本 | 禁止变更 |
+|------|------|----------|
+| 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 命名空间 |
+
+### 1.2 模块依赖方向(严格单向)
+
+```
+haha-admin  -->  haha-service  -->  haha-mapper  -->  haha-entity
+haha-miniapp -->  haha-service  -->  haha-mapper  -->  haha-entity
+                                        |
+haha-common <---------------------------+(所有模块均可依赖)
+haha-sdk(独立模块,不依赖其他内部模块)
+```
+
+**禁止**:
+- haha-mapper 依赖 haha-service(反向依赖)
+- haha-entity 依赖 haha-mapper 或 haha-service(反向依赖)
+- haha-common 依赖任何其他内部模块
+- haha-admin 与 haha-miniapp 互相依赖
+
+### 1.3 双应用架构
+
+| 应用 | 模块 | 端口 | 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")`。
+
+---
+
+## 2. 代码生成规则
+
+### 2.1 实体类(Entity)
+
+**位置**: `haha-entity/src/main/java/com/haha/entity/`
+
+**必须遵守**:
+
+```java
+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)`,禁止遗漏
+- 所有 Long 类型外键字段(如 userId, orderId, shopId)也必须添加 `@JsonSerialize(using = ToStringSerializer.class)`
+- 日期字段必须添加 `@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")`
+- 金额字段必须使用 `BigDecimal`,禁止使用 `double` 或 `float`
+- 实现 `Serializable` 接口
+- 非 DB 字段必须用 `@TableField(exist = false)` 标注
+- 数据库字段命名用下划线(`create_time`),Java 字段用驼峰(`createTime`),MyBatis-Plus 自动映射
+
+### 2.2 DTO 类
+
+**位置**: `haha-entity/src/main/java/com/haha/entity/dto/` 或 `haha-admin/src/main/java/com/haha/admin/dto/`
+
+```java
+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;
+}
+```
+
+**规则**:
+- DTO 按操作分:`XxxCreateDTO`、`XxxUpdateDTO`、`XxxQueryDTO`
+- 查询 DTO 继承 `BasePageQueryDTO`(来自 haha-common)并调用 `validate()` 方法校验分页参数
+- 使用 Jakarta Validation 注解(`jakarta.validation.constraints.*`),禁止使用 `javax.validation.*`
+- haha-common 模块使用 Validation 注解时,必须在 pom.xml 中显式添加 `jakarta.validation-api` 依赖
+
+### 2.3 VO 类
+
+**位置**: `haha-common/src/main/java/com/haha/common/vo/`
+
+**规则**:
+- 统一返回使用 `Result<T>`(`Result.success()` / `Result.error()`)
+- 分页返回使用 `PageResult<T>`,通过 `PageResult.of(IPage<T>)` 转换
+- VO 类用 `@Data` 注解,实现 `Serializable`
+
+### 2.4 Mapper 接口
+
+**位置**: `haha-mapper/src/main/java/com/haha/mapper/`
+
+```java
+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>`
+- 简单 SQL 用 `@Select` / `@Update` / `@Insert` 注解,动态 SQL 用 `<script>` 标签
+- 复杂 SQL(多表关联、动态条件>3个)必须使用 XML 映射文件
+- XML 映射文件位置:`haha-mapper/src/main/resources/mapper/XxxMapper.xml`
+- XML 中必须定义 `resultMap`,禁止使用 `resultType` 自动映射(保证字段可控)
+- 查询方法参数使用 `@Param` 注解
+
+### 2.5 Service 接口
+
+**位置**: `haha-service/src/main/java/com/haha/service/`
+
+```java
+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>`
+- 接口方法必须添加 Javadoc 注释
+- 返回值类型优先使用实体类或 VO,禁止返回 `Map<String, Object>`(已有代码除外,新代码禁止)
+
+### 2.6 Service 实现
+
+**位置**: `haha-service/src/main/java/com/haha/service/impl/`
+
+```java
+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` + `@RequiredArgsConstructor`
+- 依赖注入优先使用构造器注入(`final` 字段 + `@RequiredArgsConstructor`)
+- 仅在循环依赖时才允许使用 `@Autowired @Lazy`
+- 事务方法必须添加 `@Transactional(rollbackFor = Exception.class)`
+- 查询优先使用 MyBatis-Plus Lambda 链式:`lambdaQuery()` / `lambdaUpdate()`
+- 分页参数校验:`page < 1` 时设为 1,`pageSize > 100` 时设为 10
+
+### 2.7 Controller 层
+
+**位置**:
+- 运营平台:`haha-admin/src/main/java/com/haha/admin/controller/`
+- 小程序:`haha-miniapp/src/main/java/com/haha/miniapp/controller/`
+
+```java
+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>`,禁止直接返回实体或 Map
+- 分页统一返回 `Result<PageResult<T>>`
+- 资源不存在返回 `Result.error(404, "xxx不存在")`
+- `@RequestMapping` 路径使用小写 kebab-case(如 `/timed-discount`),禁止驼峰
+- 请求参数:查询用 `@RequestParam` 或 DTO 接收,路径参数用 `@PathVariable`,请求体用 `@RequestBody`
+
+### 2.8 枚举类
+
+**位置**: `haha-common/src/main/java/com/haha/common/enums/`
+
+```java
+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` 或数字序号
+
+### 2.9 常量类
+
+**位置**: `haha-common/src/main/java/com/haha/common/constant/`
+
+```java
+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`
+- 常量类不与枚举重复定义相同的状态码
+
+---
+
+## 3. API 生成规则
+
+### 3.1 RESTful 设计
+
+| 操作 | 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` |
+
+### 3.2 权限注解
+
+- 格式:`@RequirePermission("module:operation")`
+- operation 标准值:`read` / `create` / `update` / `delete` / `other`
+- 读操作用 `:read`,写操作用对应 operation
+- 小程序端 Controller 不需要 `@RequirePermission`(用户通过 accessToken 鉴权)
+
+### 3.3 操作日志注解
+
+- 写操作必须添加:`@Log(module = "模块名", operation = OperationType.XXX, summary = "操作描述")`
+- OperationType 标准值:`CREATE` / `UPDATE` / `DELETE` / `OTHER`
+- 读操作不加 `@Log`
+
+### 3.4 统一返回格式
+
+```json
+{
+  "code": 200,
+  "message": "操作成功",
+  "data": { ... }
+}
+```
+
+| 场景 | code | message |
+|------|------|---------|
+| 成功 | 200 | "操作成功" 或具体描述 |
+| 参数错误 | 400 | 具体字段错误描述 |
+| 未登录 | 401 | "未登录" / "token已过期" 等 |
+| 无权限 | 403 | "无访问权限" |
+| 资源不存在 | 404 | "xxx不存在" |
+| 业务异常 | 自定义 | BusinessException 消息 |
+| 系统异常 | 500 | "系统繁忙,请稍后重试" |
+
+**禁止**:
+- 禁止在 Controller 中抛出异常后不做处理,统一由 `GlobalExceptionHandler` 捕获
+- 禁止返回 `code=200` 但 data 中包含错误信息
+
+### 3.5 业务异常抛出
+
+```java
+// 使用 ResponseEnum 枚举
+throw new BusinessException(ResponseEnum.ORDER_NOT_FOUND);
+
+// 自定义消息
+throw new BusinessException(404, "订单不存在");
+
+// 仅消息(默认500)
+throw new BusinessException("操作失败");
+```
+
+---
+
+## 4. 数据库规则
+
+### 4.1 表命名
+
+- 前缀 `t_`,如 `t_order`、`t_device`、`t_door_record`
+- 多词用下划线分隔,如 `t_timed_discount_activity`
+- 关联表命名:`t_{主实体}_{关联实体}`,如 `t_activity_device`、`t_coupon_shop`
+
+### 4.2 ID 策略
+
+- 全局使用雪花算法:`@TableId(type = IdType.ASSIGN_ID)`
+- MyBatis-Plus 全局配置已设 `IdType.ASSIGN_ID`
+- 数据库列类型为 `BIGINT`
+- 禁止使用自增 ID(`IdType.AUTO`)
+
+### 4.3 字段命名
+
+- 数据库列:下划线命名(`create_time`、`pay_status`、`order_no`)
+- Java 字段:驼峰命名(`createTime`、`payStatus`、`orderNo`)
+- MyBatis-Plus 已开启 `map-underscore-to-camel-case: true` 自动映射
+- 金额字段:`BigDecimal` 类型,数据库 `DECIMAL(10,2)`
+- 状态字段:数字状态用 `INT`,字符串状态用 `VARCHAR`(如 pay_status 存 "PAID")
+- 时间字段:`DATETIME`,对应 Java `LocalDateTime`
+
+### 4.4 XML 映射文件
+
+- 位置:`haha-mapper/src/main/resources/mapper/XxxMapper.xml`
+- 必须定义 `resultMap`,显式映射所有列
+- SQL 中引用表名必须使用 `t_` 前缀
+- 动态条件使用 `<if>` / `<where>` / `<set>` 标签
+- SQL 片段使用 `<sql>` 标签复用
+
+### 4.5 逻辑删除
+
+- MyBatis-Plus 逻辑删除条件**不会自动添加**到手写 SQL 中
+- 使用 `@TableLogic` 注解的实体,`BaseMapper` 自带方法自动处理
+- 手写 SQL(注解或 XML)必须手动添加逻辑删除条件:`AND deleted = 0`
+
+---
+
+## 5. 序列化与精度规则
+
+### 5.1 雪花 ID 精度(最高优先级)
+
+JavaScript `Number.MAX_SAFE_INTEGER` = 9007199254740991(16位),雪花 ID 为 19 位数字,超出安全范围导致精度丢失。
+
+**强制规则**:
+- 所有实体的 `id` 字段(`Long` 类型)必须添加 `@JsonSerialize(using = ToStringSerializer.class)`
+- 所有 `Long` 类型外键字段(userId, orderId, shopId, templateId 等)同样必须添加
+- 禁止依赖全局 Jackson 配置(`JacksonConfig` 中的 Long->String 模块)替代字段级注解
+- 前端接收 ID 必须为字符串格式
+
+```java
+// 正确
+@TableId(type = IdType.ASSIGN_ID)
+@JsonSerialize(using = ToStringSerializer.class)
+private Long id;
+
+// 错误 - 缺少 @JsonSerialize
+@TableId(type = IdType.ASSIGN_ID)
+private Long id;
+```
+
+### 5.2 日期序列化
+
+- 日期格式统一:`yyyy-MM-dd HH:mm:ss`
+- 时区统一:`Asia/Shanghai`
+- 实体字段必须加:`@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")`
+- Jackson 全局配置已注册 `JavaTimeModule`,禁用 `WRITE_DATES_AS_TIMESTAMPS`
+
+### 5.3 图片 URL 容错
+
+处理图片 URL 时必须进行容错判断:
+
+```java
+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;
+}
+```
+
+---
+
+## 6. 依赖注入规则
+
+### 6.1 构造器注入优先
+
+```java
+// 正确 - 构造器注入(推荐)
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class XxxServiceImpl {
+    private final XxxMapper xxxMapper;
+    private final OtherService otherService;
+}
+```
+
+### 6.2 循环依赖处理
+
+仅在出现循环依赖时使用 `@Autowired @Lazy`:
+
+```java
+// 仅循环依赖时允许
+@Autowired
+@Lazy
+private PaymentService paymentService;
+```
+
+**禁止**:无循环依赖时使用 `@Autowired`,应一律使用构造器注入。
+
+### 6.3 @Configuration 规则
+
+Spring Boot 4.0.3 要求 `@Configuration` 必须显式指定 `proxyBeanMethods = false`:
+
+```java
+// 正确
+@Configuration(proxyBeanMethods = false)
+public class XxxConfig {
+    // ...
+}
+
+// 错误 - 缺少 proxyBeanMethods = false
+@Configuration
+public class XxxConfig {
+    // ...
+}
+```
+
+---
+
+## 7. 业务规则约束
+
+### 7.1 订单金额取值
+
+- **实付金额必须从 `order.paidAmount` 取值**,禁止使用 `order.totalAmount`
+- `paidAmount` 为空时回退到 `totalAmount`,确保向前兼容
+- 三字段关系:`paidAmount = totalAmount - discountAmount`
+- `totalAmount`:订单总金额(原价)
+- `discountAmount`:优惠金额(营销活动优惠 + 优惠券优惠)
+- `paidAmount`:实付总金额
+
+### 7.2 优惠券优先级
+
+- 多张可用优惠券时,按**到期时间升序**排序,最先到期的优先使用
+- 适用条件检查:门店匹配、商品匹配、最低消费金额
+- 优惠券自动扣减在订单回调(`handleOrderCallback`)中执行
+- 扣减后更新 `discountAmount` 和 `paidAmount`
+
+### 7.3 设备门状态枚举
+
+- `DeviceDoorStatus` 枚举仅含 `code`(String)和 `description`(String),禁止冗余字段
+- 数据库 `door_status` 字段存储枚举 `code` 值(如 `OPENED`、`CLOSED`、`ERROR`、`ANOTHER`)
+- 禁止存储数字、JSON 对象或非规范字符串
+- 使用 `DeviceDoorStatus.convertToDbValue()` 写入数据库
+- 使用 `DeviceDoorStatus.fromDbValue()` 从数据库读取
+
+### 7.4 设备告警
+
+- 采用回调实时预警 + 定时巡检兜底的混合方案
+- 告警通知通过企业微信群机器人 Webhook
+- 配置项:开关(`device.alert.enabled`)、冷却期、巡检间隔、信号阈值
+- Redis 存储告警冷却状态,防止重复告警
+
+### 7.5 订单售后时效
+
+- 退款申请有效期:支付完成后 7 天内
+- 前后端均需校验 7 天时效限制
+- 超期订单前端应给出明确提示
+
+### 7.6 微信支付分集成
+
+- 扫码开门流程强制检查支付分开通状态
+- 订单创建后初始化支付分服务订单
+- 订单回调后完结支付分订单并扣费
+- 支付分状态:CREATED -> DOING -> USER_PAYING -> DONE
+
+---
+
+## 8. 常见陷阱(禁止违反)
+
+### 8.1 雪花 ID 精度丢失
+
+- 前端 JavaScript 无法安全表示 19 位数字
+- **必须**在每个 Long 类型 ID 字段添加 `@JsonSerialize(using = ToStringSerializer.class)`
+- 已有全局 Jackson Long->String 配置作为兜底,但**禁止依赖全局配置**替代字段注解
+
+### 8.2 jakarta.validation 依赖
+
+- haha-common 模块使用 `@NotNull` / `@NotEmpty` 等 Validation 注解时
+- **必须**在 `haha-common/pom.xml` 中显式添加 `jakarta.validation-api` 依赖
+- 否则编译报错:`import jakarta.validation cannot be resolved`
+
+### 8.3 @Configuration 的 proxyBeanMethods
+
+- Spring Boot 4.0.3 中,`@Configuration` 不指定 `proxyBeanMethods = false` 会导致 CGLIB 增强失败
+- **必须**所有 `@Configuration` 类添加 `(proxyBeanMethods = false)`
+
+### 8.4 MyBatis-Plus 逻辑删除
+
+- 逻辑删除条件**不会自动添加**到手写 SQL(注解或 XML)中
+- 手写 SQL 查询**必须手动添加** `AND deleted = 0` 条件
+- `BaseMapper` 自带方法(selectById, selectList 等)自动处理
+
+### 8.5 循环依赖
+
+- Service 间循环引用必须使用 `@Autowired @Lazy`,禁止使用 `@RequiredArgsConstructor` 注入循环依赖
+- 新增 Service 依赖时优先检查是否形成循环
+
+### 8.6 @Transactional 与 @Async
+
+- `@Async` 方法不在调用方事务上下文中执行
+- 异步方法内部如需事务,必须单独添加 `@Transactional`
+- 异步方法异常不应影响主流程,必须 try-catch 并记录日志
+
+### 8.7 金额计算
+
+- 禁止使用 `double` / `float` 进行金额计算
+- 必须使用 `BigDecimal`,指定 `RoundingMode.HALF_UP`
+- 除法必须指定 scale:`amount.divide(divisor, 2, RoundingMode.HALF_UP)`
+
+### 8.8 PowerShell 反引号
+
+- 在 Windows PowerShell 环境下生成 Java 代码时
+- 禁止在字符串中使用反引号(`` ` ``),PowerShell 会将反引号解释为转义字符
+- 使用 `System.out.println()` 或日志输出时避免反引号
+
+---
+
+## 9. 代码模板速查
+
+### 9.1 新增业务模块检查清单
+
+1. Entity 类(haha-entity)-> DTO 类(haha-entity/dto)-> Mapper 接口(haha-mapper)
+2. 如需 XML 映射 -> `haha-mapper/src/main/resources/mapper/XxxMapper.xml`
+3. Service 接口(haha-service)-> Service 实现(haha-service/impl)
+4. Controller(haha-admin/controller 或 haha-miniapp/controller)
+5. 常量类(haha-common/constant)-> 枚举类(haha-common/enums)
+6. 确认所有 Long ID 字段添加 `@JsonSerialize(using = ToStringSerializer.class)`
+7. 确认日期字段添加 `@JsonFormat`
+8. 运营端 Controller 添加 `@RequirePermission`
+9. 写操作添加 `@Log` 注解
+
+### 9.2 数据库变更检查清单
+
+1. SQL 迁移脚本放在 `docs/database/` 目录
+2. 表名 `t_` 前缀
+3. ID 列 `BIGINT`,雪花算法
+4. 金额列 `DECIMAL(10,2)`
+5. 时间列 `DATETIME`
+6. Entity 类同步更新
+7. `@JsonSerialize` 和 `@JsonFormat` 注解不可遗漏
+8. 如使用逻辑删除,添加 `deleted` 列和 `@TableLogic` 注解