Răsfoiți Sursa

docs(dev-rule): 更新开发规范文档以匹配项目实际实现

- 同步代码风格指南与实际项目实现(如 Jakarta 包、异常基类等)
- 补充项目关键实现约定(拦截器、权限组、统一响应封装等)
- 更新包结构说明与目录用途注释
- 明确测试依赖缺失现状并建议添加 spring-boot-starter-test
- 修正自定义异常继承关系为 RuntimeException
- 补充 MyBatis 实体字段注解与自动填充实现细节
- 完善 TokenService 行为说明(包括续期逻辑)
- 更新 API 设计规范中关于 AuthInterceptor 和 CORS 的描述
- 添加当前 pom.xml 依赖版本信息并指出重复声明问题
- 增加安全与部署建议(如证书管理、日志输出等)
mcbaiyun 2 luni în urmă
părinte
comite
695b2e9c9a

+ 122 - 72
docs/DevRule/01-代码风格指南.md

@@ -1,5 +1,7 @@
 # 01-代码风格指南
 
+以下文档在原有代码风格约定基础上,已同步并补充当前项目实际实现的约定(例如:使用 Jakarta 包、异常基类为 RuntimeException、拦截器 token 优先级等)。请以此文档为团队规范。
+
 ## 1. 命名规范
 
 ### 1.1 包名
@@ -33,14 +35,6 @@
 
 ### 2.2 花括号
 - 类和方法的花括号在声明行的末尾。
-- 示例:
-  ```java
-  public class Example {
-      public void method() {
-          // code
-      }
-  }
-  ```
 
 ### 2.3 导入语句
 - 导入语句按以下顺序分组:
@@ -57,83 +51,38 @@
 - 方法参数之间用逗号加空格分隔,如 `method(a, b, c)`。
 - 控制语句关键字后加空格,如 `if (condition)`。
 
-## 3. 注释规范
+## 3. 注释与架构相关约定
 
-### 3.1 类注释
+### 3.1 使用的包(Jakarta)
+- 本项目使用 Jakarta 规范包(例如 `jakarta.servlet.*`, `jakarta.validation.*`),而非旧的 `javax.*` 命名空间。
+
+### 3.2 类注释
 - 使用Javadoc注释 `/** */`。
 - 示例:
   ```java
   /**
    * 性别枚举,数据库存储为整数:1=男, 2=女
    */
-  public enum Gender {
-  ```
-
-### 3.2 方法注释
-- 公共方法使用Javadoc注释。
-- 示例:
-  ```java
-  /**
-   * 获取用户OpenID
-   */
-  public R<?> getOpenid(GetOpenidRequest req) {
-  ```
-
-### 3.3 字段注释
-- 使用 `/** */` 注释字段,描述字段含义。
-- 示例:
-  ```java
-  /** 用户名;用户名 */
-  @Schema(description = "用户名(可选)")
-  private String username;
-  ```
-
-### 3.4 代码内注释
-- 使用 `//` 进行单行注释,解释复杂逻辑。
-- 示例:
-  ```java
-  // role is required now
-  if (req.getRole() == null) {
+  public enum Gender { ... }
   ```
 
-## 4. 其他约定
+### 3.3 方法注释
+- 公共方法使用Javadoc注释并描述入参、返回值与异常。
 
-### 4.1 访问修饰符
-- 字段使用 `private`。
-- 方法根据需要使用 `public`、`private` 或 `protected`。
+### 3.4 字段注释
+- 使用 `/** */` 注释字段,描述字段含义并可配合 `@Schema` 等注解提供文档。
 
-### 4.2 构造函数
-- 使用 `@Autowired` 注解进行依赖注入。
-- 示例:
-  ```java
-  @Autowired
-  public WebMvcConfig(AuthInterceptor authInterceptor) {
-      this.authInterceptor = authInterceptor;
-  }
-  ```
+### 3.5 Lombok 与简化样板代码
+- 项目使用 Lombok(例如 `@Getter`, `@Setter`, `@RequiredArgsConstructor` 等)以减少样板代码。
 
-### 4.3 异常处理
-- 自定义异常继承 `Exception`。
-- 示例:
-  ```java
-  public class CustomException extends Exception {
-      public CustomException(String message) {
-          super(message);
-      }
-  }
-  ```
+## 4. 关键实现约定(来自代码库)
 
-### 4.4 常量定义
-- 常量使用 `public static final` 修饰。
-- 示例:
-  ```java
-  public static final long TOKEN_TTL_HOURS = 72L;
-  ```
+以下约定是从当前代码实现中抽取的,文档应与代码保持一致。
 
-### 4.5 枚举定义
-- 使用 `@EnumValue` 注解指定数据库存储值。
-- 提供 `getCode()`、`getDescription()` 方法。
-- 示例:
+### 4.1 枚举约定
+- 枚举使用 `@EnumValue`(MyBatis-Plus)标记用于数据库存储的值。
+- 枚举应提供 `getCode()`、`getDescription()` 方法;通常还包含 `fromCode(int)` 的静态方法用于反序列化,以及 `toString()` 返回描述字符串。
+- 示例(摘自 `work.baiyun.chronicdiseaseapp.enums.Gender`):
   ```java
   public enum Gender {
       MALE(1, "男"),
@@ -143,6 +92,107 @@
       private final int code;
       private final String description;
 
-      // constructor and methods
+      public int getCode() { return code; }
+      public String getDescription() { return description; }
+
+      public static Gender fromCode(int code) { ... }
   }
   ```
+
+### 4.2 分页对象
+- 项目使用 `com.github.pagehelper.PageInfo` 来计算总数,分页返回封装类为 `work.baiyun.chronicdiseaseapp.common.Page<T>`,并使用 Lombok 的 `@Getter`/`@Setter`。
+
+### 4.3 异常基类与全局处理
+- 自定义业务异常 `CustomException` 继承自 `RuntimeException`(运行时异常),由全局异常处理器 `CustomExceptionHandler` 处理。
+- 全局异常处理器使用 `@RestControllerAdvice`,并处理如下异常类型:
+  - `BindException`(参数绑定错误)
+  - `ConstraintViolationException`(参数校验错误)
+  - `CustomException`(业务异常)
+  - `Exception`(未捕获的通用异常)
+- 处理器将错误信息统一返回项目内定义的响应封装 `work.baiyun.chronicdiseaseapp.common.R`,并使用 `work.baiyun.chronicdiseaseapp.enums.ExceptionResultCode` 中的 code/msg。
+
+### 4.4 拦截器(AuthInterceptor)行为约定
+- 拦截器优先解析标准 Authorization 头:
+  - `Authorization: Bearer <token>` (优先,且忽略大小写的 Bearer 前缀)
+  - 若 `Authorization` 未提供 token,则回退到 `X-Token` header 或 `token` header
+  - 若无 token,返回 401(HttpStatus.UNAUTHORIZED)并拒绝请求
+- 拦截器允许 CORS 预检(OPTIONS)请求直接通过。
+- 验证通过后,将 `currentUserId`(用户 id)和 `currentUserRole`(权限组,`PermissionGroup`)放入 `HttpServletRequest` 的 attribute 中以供 Controller 使用。
+
+示例摘录(来自 `work.baiyun.chronicdiseaseapp.config.AuthInterceptor`)中的行为:
+```java
+// 优先处理 Authorization: Bearer <token>
+String authHeader = request.getHeader("Authorization");
+String xToken = request.getHeader("X-Token");
+String tokenHeader = request.getHeader("token");
+// 解析逻辑:优先 Authorization(支持 Bearer),回退 X-Token,再回退 token
+```
+
+### 4.5 WebMvc 配置
+- `WebMvcConfig` 使用构造函数注入 `AuthInterceptor`(带 `@Autowired` 构造器),并在 `addInterceptors` 中注册拦截器。
+- 默认拦截所有路径 `/**`,但排除登录/Swagger/OpenAPI 相关路径,示例排除路径列表:`/`, `/get_openid`, `/v3/api-docs/**`, `/swagger-ui/**`, `/doc.html`, `/webjars/**`, `/favicon.ico` 等。
+
+### 4.6 主启动类约定
+- 主类 `SpringAPP` 使用 `@EnableKnife4j` 启用 Knife4j(API 文档增强),并在 `@SpringBootApplication` 中排除 `ThymeleafAutoConfiguration`(项目未使用 Thymeleaf)。
+
+### 4.6 PermissionGroup(权限组)
+- 项目定义了 `work.baiyun.chronicdiseaseapp.enums.PermissionGroup`,用于表示用户权限组并用于访问控制。
+- 枚举值与代码实现如下:
+  - `SYS_ADMIN(1, "管理员")`
+  - `DOCTOR(2, "医生")`
+  - `PATIENT(3, "患者")`
+  - `PATIENT_FAMILY(4, "患者家属")`
+- 每个枚举包含 `@EnumValue` 标记的 `code`(用于数据库存储)和 `description`,并提供 `getCode()`、`getDescription()`、`fromCode(int)` 方法。
+
+### 4.7 统一响应封装(`R`)
+- 项目通过 `work.baiyun.chronicdiseaseapp.common.R<T>` 作为统一响应体。核心字段:
+  - `Integer code`:响应码
+  - `String message`:提示消息
+  - `T data`:响应主体数据
+- 常用静态构造方法(实现来自代码):
+  - `R.success(Integer code, String message)` 返回无数据的成功响应
+  - `R.success(Integer code, String message, T data)` 返回带数据的成功响应
+  - `R.fail(Integer code, String message)` 返回无数据的失败响应
+  - `R.fail(Integer code, String message, T data)` 返回带数据的失败响应
+
+### 4.8 TokenService(令牌服务)
+- `work.baiyun.chronicdiseaseapp.service.TokenService` 是令牌管理的接口,重要方法:
+  - `String createToken(Long userId)`:为用户生成并保存新的 token,返回 token 字符串(通常在登录或刷新时调用)。
+  - `AuthPrincipal validateToken(String token)`:校验 token 并返回 `AuthPrincipal`(包含 `userId` 与 `PermissionGroup`);若 token 无效或过期返回 `null`。实现中会处理接近过期时的续期逻辑(例如小于 24 小时时延长有效期)。
+  - `void revokeToken(String token)`:撤销 token(例如登出时使用)。
+
+## 5. 异常与日志记录约定
+- 对未捕获异常进行日志记录(使用 SLF4J 的 Logger),并在响应中返回通用错误码与信息;针对参数校验返回精确的校验错误消息列表。
+
+## 6. 其他约定与建议
+- 构造器注入优先于字段注入。
+- 在控制器/服务中尽量使用 `AuthPrincipal`/`currentUserId` 作为当前请求用户的来源,而不是重新解析 token。
+- 如果文档中的旧条目(如“自定义异常继承 Exception”)与上面实际实现冲突,以代码为准并及时修正文档。
+
+---
+
+文档已根据仓库中实际实现同步关键点,若需要我可以:
+- 将其它规则(例如导入分组、更多示例)补充完整;
+- 或者生成一份 PR 描述,列出文档修改与代码不一致的具体代码位置。
+
+## 7. 快速示例(可复制到 Controller)
+下面的片段示例展示如何在 Controller 中读取由 `AuthInterceptor` 放入的当前用户信息,并返回基于 `R` 的响应:
+
+```java
+import jakarta.servlet.http.HttpServletRequest;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+import work.baiyun.chronicdiseaseapp.common.R;
+
+@RestController
+public class ExampleController {
+  @GetMapping("/me")
+  public R<Long> getCurrentUser(HttpServletRequest request) {
+    Long currentUserId = (Long) request.getAttribute("currentUserId");
+    if (currentUserId == null) {
+      return R.fail(401, "未登录");
+    }
+    return R.success(200, "ok", currentUserId);
+  }
+}
+```

+ 67 - 33
docs/DevRule/02-项目结构规范.md

@@ -13,68 +13,87 @@ ChronicDiseaseApp/
 │   ├── main/                        # 主源代码
 │   │   ├── java/                    # Java源代码
 │   │   │   └── work/baiyun/chronicdiseaseapp/  # 主包
-│   │   │       ├── SpringAPP.java   # Spring Boot主启动类
+│   │   │       ├── SpringAPP.java   # Spring Boot 主启动类(@EnableKnife4j)
 │   │   │       ├── common/          # 通用类
-│   │   │       │   ├── Page.java    # 分页类
+│   │   │       │   ├── Page.java    # 分页类(PageHelper 封装)
 │   │   │       │   └── R.java       # 统一响应类
 │   │   │       ├── config/          # 配置类
 │   │   │       │   ├── AuthInterceptor.java      # 认证拦截器
-│   │   │       │   ├── CorsConfig.java           # CORS配置
-│   │   │       │   ├── Knife4jConfig.java        # Knife4j配置
-│   │   │       │   ├── MybatisPlusConfig.java    # MyBatis-Plus配置
-│   │   │       │   ├── RestTemplateConfig.java   # RestTemplate配置
-│   │   │       │   ├── WebMvcConfig.java         # Web MVC配置
+│   │   │       │   ├── CorsConfig.java           # CORS 配置
+│   │   │       │   ├── Knife4jConfig.java        # Knife4j 配置
+│   │   │       │   ├── MybatisPlusConfig.java    # MyBatis-Plus 配置
+│   │   │       │   ├── RestTemplateConfig.java   # RestTemplate 配置
+│   │   │       │   ├── WebMvcConfig.java         # Web MVC 配置(拦截器注册)
 │   │   │       │   └── WeChatProperties.java     # 微信配置属性
 │   │   │       ├── controller/     # 控制器层
 │   │   │       │   ├── GeoController.java        # 地理位置控制器
-│   │   │       │   └── WeChatController.java     # 微信控制器
+│   │   │       │   ├── WeChatController.java     # 微信控制器
+│   │   │       │   ├── BloodGlucoseDataController.java  # 血糖数据控制器
+│   │   │       │   ├── BloodPressureDataController.java # 血压数据控制器
+│   │   │       │   ├── HeartRateDataController.java     # 心率数据控制器
+│   │   │       │   └── PhysicalDataController.java      # 体征数据控制器
 │   │   │       ├── enums/          # 枚举类
 │   │   │       │   ├── ExceptionResultCode.java  # 异常结果码枚举
 │   │   │       │   ├── FailResultCode.java       # 失败结果码枚举
 │   │   │       │   ├── Gender.java               # 性别枚举
 │   │   │       │   ├── PermissionGroup.java      # 权限组枚举
-│   │   │       │   └── SuccessResultCode.java    # 成功结果码枚举
+│   │   │       │   ├── SuccessResultCode.java    # 成功结果码枚举
+│   │   │       │   └── BloodGlucoseType.java     # 血糖类型枚举
 │   │   │       ├── exception/     # 异常处理
-│   │   │       │   ├── CustomException.java      # 自定义异常
-│   │   │       │   └── CustomExceptionHandler.java # 异常处理器
-│   │   │       ├── handler/       # 处理器
-│   │   │       │   ├── CustomMetaObjectHandler.java  # MyBatis元对象处理
-│   │   │       │   ├── GenderTypeHandler.java        # 性别类型处理
-│   │   │       │   └── PermissionGroupTypeHandler.java # 权限组类型处理
-│   │   │       ├── mapper/        # 数据访问层
+│   │   │       │   ├── CustomException.java      # 自定义异常(RuntimeException)
+│   │   │       │   └── CustomExceptionHandler.java # 全局异常处理器
+│   │   │       ├── handler/       # MyBatis 处理器
+│   │   │       │   ├── CustomMetaObjectHandler.java  # 元对象处理
+│   │   │       │   ├── GenderTypeHandler.java        # 性别类型处理
+│   │   │       │   └── PermissionGroupTypeHandler.java # 权限组类型处理
+│   │   │       ├── mapper/        # 数据访问层(Mapper 接口)
 │   │   │       │   ├── SysUserMapper.java       # 系统用户Mapper
 │   │   │       │   ├── UserInfoMapper.java      # 用户信息Mapper
-│   │   │       │   └── UserTokenMapper.java     # 用户令牌Mapper
+│   │   │       │   ├── UserTokenMapper.java     # 用户令牌Mapper
+│   │   │       │   ├── BloodGlucoseDataMapper.java # 血糖数据Mapper
+│   │   │       │   ├── BloodPressureDataMapper.java# 血压数据Mapper
+│   │   │       │   ├── HeartRateDataMapper.java     # 心率数据Mapper
+│   │   │       │   └── PhysicalDataMapper.java      # 体征数据Mapper
 │   │   │       ├── model/        # 数据模型
 │   │   │       │   ├── bo/       # 业务对象
-│   │   │       │   ├── po/       # 持久化对象
+│   │   │       │   ├── po/       # 持久化对象(实体)
 │   │   │       │   │   ├── BaseEntity.java      # 基础实体
 │   │   │       │   │   ├── UserInfo.java        # 用户信息实体
-│   │   │       │   │   └── UserToken.java       # 用户令牌实体
-│   │   │       │   └── vo/       # 视图对象
+│   │   │       │   │   ├── UserToken.java       # 用户令牌实体
+│   │   │       │   │   ├── BloodGlucoseData.java # 血糖实体
+│   │   │       │   │   ├── BloodPressureData.java# 血压实体
+│   │   │       │   │   ├── HeartRateData.java    # 心率实体
+│   │   │       │   │   └── PhysicalData.java     # 体征实体
+│   │   │       │   └── vo/       # 视图/请求响应对象
 │   │   │       │       ├── GetOpenidRequest.java     # 获取OpenID请求
 │   │   │       │       ├── QueryUserRequest.java     # 查询用户请求
 │   │   │       │       ├── QueryUserResponse.java    # 查询用户响应
-│   │   │       │       └── UpdateUserInfoRequest.java # 更新用户信息请求
-│   │   │       ├── service/      # 服务层
+│   │   │       │       ├── UpdateUserInfoRequest.java # 更新用户信息请求
+│   │   │       │       ├── AuthPrincipal.java         # 鉴权主体(userId, role)
+│   │   │       │       └── 各类数据请求/响应 VO(如 BloodPressureDataResponse 等)
+│   │   │       ├── service/      # 服务接口
 │   │   │       │   ├── TokenService.java       # 令牌服务接口
 │   │   │       │   ├── UserService.java        # 用户服务接口
 │   │   │       │   ├── WeChatService.java      # 微信服务接口
 │   │   │       │   └── impl/     # 服务实现
 │   │   │       │       ├── TokenServiceImpl.java   # 令牌服务实现
 │   │   │       │       ├── UserServiceImpl.java    # 用户服务实现
-│   │   │       │       └── ...                     # 其他服务实现
+│   │   │       │       ├── BloodGlucoseDataServiceImpl.java # 血糖服务实现
+│   │   │       │       ├── BloodPressureDataServiceImpl.java# 血压服务实现
+│   │   │       │       ├── HeartRateDataServiceImpl.java    # 心率服务实现
+│   │   │       │       └── PhysicalDataServiceImpl.java     # 体征服务实现
 │   │   │       └── util/         # 工具类
-│   │   │           └── TokenUtil.java           # 令牌工具类
+│   │   │           ├── TokenUtil.java           # 令牌工具类
+│   │   │           └── SecurityUtils.java       # 安全相关辅助方法
 │   │   └── resources/            # 资源文件
-│   │       ├── application.yml   # 应用配置文件
-│   │       ├── logback-spring.xml # 日志配置文件
-│   │       └── cert/             # 证书文件
-│   │           ├── fullchain.pem  # 证书链
-│   │           └── privkey.pem    # 私钥
+│   │       ├── application.yml   # 应用配置
+│   │       ├── logback-spring.xml # 日志配置
+│   │       └── cert/             # 证书
+│   │           ├── fullchain.pem  # 证书链(仓库副本 — 生产请使用安全注入)
+│   │           └── privkey.pem    # 私钥(不要在公共仓库中保存)
 │   └── test/                     # 测试代码
-│       └── java/                 # 测试Java代码(前为空)
-├── target/                        # 编译输出目录
+│       └── java/                 # 测试Java代码(前为空)
+├── target/                        # 编译输出目录(忽略提交)
 │   ├── classes/                   # 编译后的类文件
 │   ├── generated-sources/         # 生成的源代码
 │   ├── generated-test-sources/    # 生成的测试源代码
@@ -82,8 +101,8 @@ ChronicDiseaseApp/
 ├── docs/                          # 文档目录
 │   ├── ReadME.md                  # 项目说明文档
 │   └── DevRule/                   # 开发规范目录
-├── logs/                          # 日志目录
-└── cert/                          # 证书目录(根目录副本)
+├── logs/                          # 运行时日志目录
+└── cert/                          # 仓库证书副本(生产环境请使用密钥管理
 ```
 
 ## 包结构说明
@@ -166,3 +185,18 @@ Maven编译后的输出目录,包含编译后的类文件、生成的源代码
 
 - **logs/**: 运行时日志文件存放目录
 - **cert/**: 证书文件存放目录(根目录副本,与resources下的cert目录内容相同)
+
+## 关于 `target`、`logs` 与 `cert`
+
+- `target/`:为 Maven 构建输出目录,应在 `.gitignore` 中忽略,不提交到版本控制。
+- `logs/`:运行时日志目录,通常为部署所在机器的本地目录;如需要在容器或云环境下运行,请将日志重定向到标准输出或使用集中化日志(例如 ELK、Azure Monitor 等)。
+- `cert/`:仓库中存在证书副本(resources 下有 `cert/`),生产密钥和证书应通过安全密钥管理器(例如 Vault、Azure KeyVault)或 CI/CD 的机密存储注入,避免在公共或共享仓库中保存私钥。
+
+## 测试约定
+
+- `src/test/java/`:存放单元测试和集成测试代码。当前目录为空,建议引入基础单元测试(示例:服务层的 `TokenService` / `UserService` 单元测试)并在 CI 中运行。
+
+## 版本控制建议
+
+- 将 `target/`、`logs/`、以及 IDE 特有的配置目录(例如 `.idea/`)加入 `.gitignore`。
+- 对于敏感证书/私钥,使用外部机密管理或将其放在部署时注入的位置,不直接放在仓库中。

+ 10 - 9
docs/DevRule/03-API设计规范.md

@@ -70,13 +70,13 @@
 
 ## 认证与授权
 1. **认证方式**:
-   - 优先使用标准Authorization header:`Authorization: Bearer <token>`
+   - 优先使用标准 Authorization header:`Authorization: Bearer <token>`(AuthInterceptor 优先解析该方式)
    - 向后兼容支持:`X-Token` 或 `token` header
-   - 支持POST body中的`token`字段
+   - 注意:`AuthInterceptor` 当前实现通过请求头解析 token(Authorization、X-Token、token),并不直接读取请求 body。若需要从 body 中提取 token,请在 Controller 层明确定义并处理。
 
 2. **拦截器配置**:
-   - `AuthInterceptor`负责Token校验
-   - 排除路径:`/`、`/get_openid`、Swagger相关路径
+   - `AuthInterceptor` 负责 Token 校验并在校验通过后把 `currentUserId`/`currentUserRole` 放入 request attribute
+   - 排除路径:`/`、`/get_openid`、`/v3/api-docs/**`, `/swagger-ui/**`, `/doc.html`, `/webjars/**`, `/favicon.ico` 等(参见 `WebMvcConfig` 的 `excludePathPatterns`)
 
 3. **Token管理**:
    - 使用`TokenService`生成和校验Token
@@ -110,15 +110,16 @@
 
 ## 跨域配置
 1. **CORS配置**:`CorsConfig`
-   - 允许路径:`/api/**`
-   - 允许源:`http://localhost:3000`
+   - 实际实现:`CorsConfig.addCorsMappings` 当前对所有路径 `/**` 开放跨域(本地开发场景),并允许源 `http://localhost:3000`。
    - 允许方法:GET, POST, PUT, DELETE, OPTIONS
+   - 允许头:`*`(允许所有请求头)并暴露头 `X-Custom-Header`, `X-Token`, `Authorization` 给前端
    - 允许凭证:true
 
 ## API文档
-1. **文档工具**:使用Knife4j(基于Swagger)
-   - 配置在`Knife4jConfig`中
-   - 访问路径:`/doc.html`
+1. **文档工具**:使用 Knife4j / springdoc-openapi(基于 OpenAPI 3)
+   - `Knife4jConfig` 中配置了 `OpenAPI` 的基本信息(title、description、version)并通过 `GroupedOpenApi` 扫描包 `work.baiyun.chronicdiseaseapp`。
+   - 默认本地服务器地址配置为 `http://localhost:8080`,版本号在 `OpenAPI` info 中设置为 `v1`。
+   - 访问路径:`/doc.html`(Knife4j UI)或 springdoc 的 `/v3/api-docs`。
 
 2. **字段描述**:
    - 使用`@Schema`注解描述请求/响应字段

+ 26 - 0
docs/DevRule/04-测试规范.md

@@ -14,6 +14,32 @@
 - **集成测试**:使用Spring Boot Test进行集成测试。
 - **Mock框架**:使用Mockito进行依赖注入的模拟。
 
+### 当前项目情况
+- 当前 `pom.xml` 中 **未** 包含常用的测试依赖(如 `spring-boot-starter-test`/`junit-jupiter`/`mockito`)。因此仓库内 `src/test/java` 目录目前为空。
+- 建议在 `pom.xml` 中添加以下依赖以开启测试能力:
+
+```xml
+<!-- Spring Boot Test(包含 JUnit 5、AssertJ、Mockito 等) -->
+<dependency>
+	<groupId>org.springframework.boot</groupId>
+	<artifactId>spring-boot-starter-test</artifactId>
+	<scope>test</scope>
+</dependency>
+
+<!-- 可选:单独声明 JUnit Jupiter(若不使用 starter) -->
+<dependency>
+	<groupId>org.junit.jupiter</groupId>
+	<artifactId>junit-jupiter</artifactId>
+	<version>5.9.2</version>
+	<scope>test</scope>
+</dependency>
+
+<!-- 可选:Jacoco 用于覆盖率报告(在 CI 中使用插件方式更常见) -->
+<!-- 在 pom.xml 中添加 jacoco-maven-plugin 的 build 插件配置 -->
+```
+
+添加这些依赖后,建议在本地或 CI 中运行 `mvn test` 并生成 coverage 报告(如使用 JaCoCo)。
+
 ## 测试结构
 1. **测试类命名**:测试类以被测类名 + "Test" 命名,例如 `UserServiceTest`。
 2. **测试方法命名**:使用描述性名称,遵循 `should_When_Then` 或 `test_方法名_场景` 格式。

+ 11 - 0
docs/DevRule/05-依赖管理规范.md

@@ -62,6 +62,17 @@
 ### 4. API 文档
 - Knife4j:使用 4.4.0 Jakarta EE 兼容版本,提供 API 文档生成。
 
+### 当前项目实际依赖(摘自 `pom.xml`)
+- Spring Boot 版本:`3.0.13`
+- MyBatis-Plus:`3.5.3`
+- PageHelper:`1.4.2`
+- Lombok:`1.18.24`(scope=provided)
+- Hutool:`5.8.22`
+- MySQL Connector:`5.1.49`
+- Knife4j(Jakarta):`knife4j-openapi3-jakarta-spring-boot-starter:4.4.0`
+
+注意:当前 `pom.xml` 中存在重复声明 `spring-boot-starter-validation`(出现了两次),建议去重以保持清晰。
+
 ## 依赖更新策略
 
 ### 1. 定期审查

+ 5 - 5
docs/DevRule/06-日志和错误处理规范.md

@@ -60,13 +60,13 @@
 
 ### 自定义异常
 
-- 继承 `Exception` 类,实现 `CustomException`
+- 自定义业务异常 `CustomException` 在代码中继承自 `RuntimeException`(运行时异常),全局异常处理器会捕获并转换为统一 `R.fail()` 响应
 - 构造函数接受消息字符串:
   ```java
-  public class CustomException extends Exception {
-      public CustomException(String message) {
-          super(message);
-      }
+  public class CustomException extends RuntimeException {
+    public CustomException(String message) {
+      super(message);
+    }
   }
   ```
 

+ 11 - 0
docs/DevRule/07-数据库规范.md

@@ -31,17 +31,28 @@
   - `update_time`:更新时间,`DATETIME`。
 - 公共字段通过 `@TableField` 注解指定字段名和填充策略。
 
+注:在代码中 `BaseEntity` 的字段命名与注解如下(与上面对应):
+- `@TableId(value = "id", type = IdType.ASSIGN_ID)` 标记 `id` 字段,并使用雪花算法生成ID;返回给前端时对 Long 类型使用 `ToStringSerializer` 序列化。
+- `@TableField(value = "version") @Version` 用于乐观锁。
+- `@TableField(value = "create_user", fill = FieldFill.INSERT)` 表示插入时自动填充 `createUser`。
+- `@TableField(value = "create_time", fill = FieldFill.INSERT)` 对应 `createTime` 字段,类型为 `LocalDateTime`。
+- `@TableField(value = "update_user", fill = FieldFill.INSERT)` 与 `updateUser`,插入时默认填充;更新时 `update_time` 标注为 `FieldFill.INSERT_UPDATE`。
+
 ## 自动填充规范
 - 使用 `CustomMetaObjectHandler` 实现自动填充。
 - 插入时自动填充:`create_time`、`update_time`、`create_user`、`update_user`。
 - 更新时自动填充:`update_time`、`update_user`。
 - 默认创建者和更新者 ID 为 1(后续可从安全上下文中获取)。
 
+实现说明:`CustomMetaObjectHandler` 在 `insertFill`/`updateFill` 方法中使用 `strictInsertFill` / `strictUpdateFill` 来填充时间和用户字段;当 `createUser` / `updateUser` 为空时,默认写入常量 `currentUserInfoId = 1L`。
+
 ## 乐观锁规范
 - 使用 `version` 字段实现乐观锁。
 - 在实体类中使用 `@Version` 注解。
 - 配置 `OptimisticLockerInnerInterceptor` 插件。
 
+实现说明:`MybatisPlusConfig` 注册了 `OptimisticLockerInnerInterceptor` 到 `MybatisPlusInterceptor` 中以启用乐观锁支持。
+
 ## 枚举处理规范
 - 枚举类使用 `@EnumValue` 注解标记存储值。
 - 自定义 `TypeHandler` 处理枚举与数据库的映射。

+ 5 - 0
docs/DevRule/08-安全规范.md

@@ -15,6 +15,11 @@
   - 兼容:`X-Token` 或 `token` HTTP头
 - **认证拦截**:所有API请求(除登录和文档相关路径外)均通过`AuthInterceptor`进行Token验证。
 
+实现说明:
+- `TokenServiceImpl.createToken(Long userId)` 使用 `TokenUtil.generateToken()` 生成 token,并将 `expireTime` 设置为当前时间加 `TokenUtil.TOKEN_TTL_HOURS`(代码中默认为 72 小时),若用户已有记录则更新 token 与过期时间。
+- `TokenServiceImpl.validateToken(String token)` 从 `t_user_token` 表中根据 token 查询记录,若过期则返回 null;若剩余有效期小于 `TokenUtil.REFRESH_THRESHOLD_HOURS`(24 小时),会将过期时间延期 `TOKEN_TTL_HOURS` 并保存。
+- `revokeToken` 实现通过将 token 与 expireTime 置空来撤销 token。
+
 ### 授权机制
 - **角色权限**:基于用户角色进行权限控制,支持以下角色:
   - 系统管理员(SYS_ADMIN)