feat: add base docs

This commit is contained in:
set
2026-02-13 20:49:22 +08:00
parent 6c0ed89397
commit 1eafeacf36
12 changed files with 935 additions and 153 deletions

48
pom.xml
View File

@@ -33,7 +33,7 @@
<lombok.version>1.18.30</lombok.version>
<mybatis-plus.version>3.5.5</mybatis-plus.version>
<dynamic-datasource.version>4.3.1</dynamic-datasource.version>
<mysql.version>8.0.33</mysql.version>
<mysql.version>8.0.33</mysql.version>
</properties>
<dependencies>
<!-- 核心依赖 -->
@@ -43,14 +43,14 @@
</dependency>
<!-- 数据库相关 -->
<dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>${mysql.version}</version>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
@@ -70,26 +70,26 @@
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
</dependency>
<!-- 添加验证依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
</dependency>
<!-- 添加验证依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- API文档 -->
<dependency>
<groupId>org.springdoc</groupId>

View File

@@ -0,0 +1,10 @@
package icu.sunway.ai_spring_example.Controller.AuthController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/v1/auth")
public class AuthController {
}

View File

@@ -0,0 +1,422 @@
# Auth模块接口文档
## 文档说明
本文档为Auth认证授权模块的接口详情遵循《接口开发通用规范文档》的所有约定基础路径为`/api/v1/auth`数据交互格式为JSON字符编码UTF-8时间戳单位为毫秒。
## 1. 登录接口
### 接口基本信息
- 接口名称:用户登录
- 接口路径:`/api/v1/auth/login`
- 请求方法POST
- 接口描述:用户通过账号(手机号/用户名)+密码/验证码完成登录返回登录令牌token
### 请求头
| 字段名 | 类型 | 必填性 | 示例值 |
| --------------- | ------ | ------ | ---------------- |
| Content-Type | String | 是 | application/json |
| Accept-Language | String | 否 | zh-CN |
### 请求参数Body
| 字段名 | 类型 | 必填性 | 描述 | 示例值 |
| ---------- | ------- | ------ | ------------------------------------------------ | ------------- |
| account | String | 是 | 登录账号(手机号/用户名) | "13800138000" |
| password | String | 否 | 登录密码与verifyCode二选一 | "Abc123456" |
| verifyCode | String | 否 | 验证码与password二选一 | "8888" |
| isRemember | Boolean | 否 | 是否记住登录默认false记住则token有效期延长 | true |
### 响应示例(成功)
```json
{
"code": 200,
"message": "登录成功",
"timestamp": 1744238900000,
"data": {
"token": "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjEsInVzZXJuYW1lIjoiYWRtaW4iLCJleHAiOjE3NDQ4NDM3MDB9.QP8kXjZ8e7R5t5s7k9L2m1n8b7v6c5x4s3a2d1f0g9h8j7k6l5p4o3i2u1y0t9s8r7e6w5q4a3s2d1f0g",
"expireTime": 1744843700000,
"userInfo": {
"userId": 1,
"userName": "admin",
"phoneNumber": "138****8000",
"role": "ADMIN",
"isEnabled": true,
"createTime": 1743238900000
}
}
}
```
### 响应示例(失败)
```json
{
"code": 400,
"message": "验证码错误或已过期",
"timestamp": 1744238900000,
"data": null
}
```
### 状态码说明
| 状态码 | 说明 |
| ------ | -------------------------------- |
| 200 | 登录成功 |
| 400 | 参数错误/验证码错误/账号密码错误 |
| 403 | 账号被封禁 |
| 429 | 登录失败次数过多,账号临时锁定 |
| 500 | 服务器内部错误 |
## 2. 退出登录接口
### 接口基本信息
- 接口名称:用户退出登录
- 接口路径:`/api/v1/auth/logout`
- 请求方法DELETE
- 接口描述:销毁当前用户的登录令牌,退出登录状态
### 请求头
| 字段名 | 类型 | 必填性 | 示例值 |
| --------------- | ------ | ------ | ------------------------------ |
| Authorization | String | 是 | Bearer eyJhbGciOiJIUzI1NiJ9... |
| Content-Type | String | 是 | application/json |
| Accept-Language | String | 否 | zh-CN |
### 请求参数
无业务请求参数仅需请求头携带token
### 响应示例(成功)
```json
{
"code": 200,
"message": "退出登录成功",
"timestamp": 1744238900000,
"data": null
}
```
### 响应示例(失败)
```json
{
"code": 401,
"message": "Token已过期请重新登录",
"timestamp": 1744238900000,
"data": null
}
```
### 状态码说明
| 状态码 | 说明 |
| ------ | ---------------- |
| 200 | 退出登录成功 |
| 401 | Token失效/未登录 |
| 500 | 服务器内部错误 |
## 3. 刷新Token接口
### 接口基本信息
- 接口名称:刷新登录令牌
- 接口路径:`/api/v1/auth/refresh-token`
- 请求方法POST
- 接口描述通过旧Token刷新获取新的登录令牌延长登录有效期
### 请求头
| 字段名 | 类型 | 必填性 | 示例值 |
| --------------- | ------ | ------ | ------------------------------ |
| Authorization | String | 是 | Bearer eyJhbGciOiJIUzI1NiJ9... |
| Content-Type | String | 是 | application/json |
| Accept-Language | String | 否 | zh-CN |
### 请求参数Body
| 字段名 | 类型 | 必填性 | 描述 | 示例值 |
| ------ | ------ | ------ | ------------ | ------------------------- |
| token | String | 是 | 旧的登录令牌 | "eyJhbGciOiJIUzI1NiJ9..." |
### 响应示例(成功)
```json
{
"code": 200,
"message": "Token刷新成功",
"timestamp": 1744238900000,
"data": {
"newToken": "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjEsInVzZXJuYW1lIjoiYWRtaW4iLCJleHAiOjE3NDU0NDg1MDB9.Z7x6y5w4v3u2t1s0r9e8w7q6a5z4s3d2f1g0h9j8k7l6p5o4i3u2y1t0s9r8e7w6q5a4s3d2f1g0h9j8k7l6p5o4i",
"expireTime": 1745448500000
}
}
```
### 响应示例(失败)
```json
{
"code": 401,
"message": "Token已失效无法刷新请重新登录",
"timestamp": 1744238900000,
"data": null
}
```
### 状态码说明
| 状态码 | 说明 |
| ------ | ------------------ |
| 200 | Token刷新成功 |
| 400 | 旧Token格式错误 |
| 401 | 旧Token已过期/无效 |
| 500 | 服务器内部错误 |
## 4. 获取当前用户信息接口
### 接口基本信息
- 接口名称:获取当前登录用户信息
- 接口路径:`/api/v1/auth/user/info`
- 请求方法GET
- 接口描述:根据登录令牌获取当前用户的基础信息(脱敏展示敏感字段)
### 请求头
| 字段名 | 类型 | 必填性 | 示例值 |
| --------------- | ------ | ------ | ------------------------------ |
| Authorization | String | 是 | Bearer eyJhbGciOiJIUzI1NiJ9... |
| Content-Type | String | 是 | application/json |
| Accept-Language | String | 否 | zh-CN |
### 请求参数
无业务请求参数仅需请求头携带token
### 响应示例(成功)
```json
{
"code": 200,
"message": "查询用户信息成功",
"timestamp": 1744238900000,
"data": {
"userId": 1,
"userName": "admin",
"phoneNumber": "138****8000",
"idCard": "310**********1234",
"roleList": ["ADMIN"],
"isEnabled": true,
"createTime": 1743238900000,
"createTimeStr": "2025-01-01 10:00:00",
"lastLoginTime": 1744152500000,
"lastLoginTimeStr": "2025-04-10 08:00:00"
}
}
```
### 响应示例(失败)
```json
{
"code": 401,
"message": "未登录,请先登录",
"timestamp": 1744238900000,
"data": null
}
```
### 状态码说明
| 状态码 | 说明 |
| ------ | ---------------- |
| 200 | 查询用户信息成功 |
| 401 | Token失效/未登录 |
| 404 | 用户信息不存在 |
| 500 | 服务器内部错误 |
## 5. 发送登录验证码接口
### 接口基本信息
- 接口名称:发送登录验证码
- 接口路径:`/api/v1/auth/send-login-code`
- 请求方法POST
- 接口描述:向指定手机号发送登录验证码,限制发送频率
### 请求头
| 字段名 | 类型 | 必填性 | 示例值 |
| --------------- | ------ | ------ | ---------------- |
| Content-Type | String | 是 | application/json |
| Accept-Language | String | 否 | zh-CN |
### 请求参数Body
| 字段名 | 类型 | 必填性 | 描述 | 示例值 |
| ----------- | ------ | ------ | ------------------ | ------------- |
| phoneNumber | String | 是 | 接收验证码的手机号 | "13800138000" |
### 响应示例(成功)
```json
{
"code": 200,
"message": "验证码发送成功,请注意查收",
"timestamp": 1744238900000,
"data": {
"expireTime": 1744239200000 // 验证码过期时间戳5分钟有效期
}
}
```
### 响应示例(失败)
```json
{
"code": 429,
"message": "验证码发送过于频繁请1分钟后重试",
"timestamp": 1744238900000,
"data": null
}
```
### 状态码说明
| 状态码 | 说明 |
| ------ | ------------------------------ |
| 200 | 验证码发送成功 |
| 400 | 手机号格式错误 |
| 429 | 发送频率超限 |
| 404 | 手机号未注册 |
| 500 | 验证码发送失败(短信服务异常) |
## 6. 重置密码接口
### 接口基本信息
- 接口名称:重置登录密码
- 接口路径:`/api/v1/auth/reset-password`
- 请求方法PUT
- 接口描述:用户通过手机号+验证码重置登录密码(全量更新密码)
### 请求头
| 字段名 | 类型 | 必填性 | 示例值 |
| --------------- | ------ | ------ | ---------------- |
| Content-Type | String | 是 | application/json |
| Accept-Language | String | 否 | zh-CN |
### 请求参数Body
| 字段名 | 类型 | 必填性 | 描述 | 示例值 |
| ----------- | ------ | ------ | ----------------------------------------------- | ------------- |
| phoneNumber | String | 是 | 绑定的手机号 | "13800138000" |
| verifyCode | String | 是 | 验证码 | "8888" |
| newPassword | String | 是 | 新密码需符合密码规则8-20位含大小写+数字) | "Abc123456" |
### 响应示例(成功)
```json
{
"code": 200,
"message": "密码重置成功,请重新登录",
"timestamp": 1744238900000,
"data": null
}
```
### 响应示例(失败)
```json
{
"code": 400,
"message": "新密码格式不符合要求需包含大小写字母和数字长度8-20位",
"timestamp": 1744238900000,
"data": null
}
```
### 状态码说明
| 状态码 | 说明 |
| ------ | -------------------------------- |
| 200 | 密码重置成功 |
| 400 | 参数错误/验证码错误/密码格式错误 |
| 429 | 验证码发送频率超限/验证次数过多 |
| 404 | 手机号未注册 |
| 500 | 服务器内部错误 |
## 7. 校验Token有效性接口
### 接口基本信息
- 接口名称校验Token有效性
- 接口路径:`/api/v1/auth/verify-token`
- 请求方法GET
- 接口描述:校验当前登录令牌是否有效,返回令牌基础信息
### 请求头
| 字段名 | 类型 | 必填性 | 示例值 |
| --------------- | ------ | ------ | ------------------------------ |
| Authorization | String | 是 | Bearer eyJhbGciOiJIUzI1NiJ9... |
| Content-Type | String | 是 | application/json |
| Accept-Language | String | 否 | zh-CN |
### 请求参数
无业务请求参数仅需请求头携带token
### 响应示例(成功)
```json
{
"code": 200,
"message": "Token有效",
"timestamp": 1744238900000,
"data": {
"userId": 1,
"token": "eyJhbGciOiJIUzI1NiJ9...",
"isValid": true,
"expireTime": 1744843700000
}
}
```
### 响应示例(失败)
```json
{
"code": 401,
"message": "Token无效或已过期",
"timestamp": 1744238900000,
"data": {
"isValid": false
}
}
```
### 状态码说明
| 状态码 | 说明 |
| ------ | ---------------- |
| 200 | Token有效 |
| 401 | Token无效/已过期 |
| 500 | 服务器内部错误 |
## 通用说明
1. 所有接口响应均遵循通用响应格式,包含`code``message``timestamp`字段,`data`字段按需返回;
2. 敏感信息(手机号、身份证号)均做脱敏处理,符合安全规范;
3. 接口频率限制遵循通用规范验证码接口1分钟/次、1小时/5次登录接口5分钟连续失败5次锁定15分钟通用接口QPS≤10
4. 所有入参均做合法性校验参数错误返回400状态码并提示具体错误信息
5. Token格式为`Bearer + 空格 + token`,需登录的接口未传/传错Token均返回401状态码。

View File

@@ -10,7 +10,7 @@ import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
@RestController
@RequestMapping("/permission")
@RequestMapping("/api/v1/permission")
public class PermissionController {
@Resource

View File

@@ -10,7 +10,7 @@ import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
@RestController
@RequestMapping("/role")
@RequestMapping("/api/v1/role")
public class RoleController {
@Resource

View File

@@ -11,7 +11,7 @@ import jakarta.annotation.Resource;
import java.util.List;
@RestController
@RequestMapping("/role-permission")
@RequestMapping("/api/v1/role-permission")
public class RolePermissionController {
@Resource

View File

@@ -10,7 +10,7 @@ import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
@RestController
@RequestMapping("/user")
@RequestMapping("/api/v1/user")
public class UserController {
@Resource

View File

@@ -13,7 +13,7 @@ import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
@RestController
@RequestMapping("/user-role")
@RequestMapping("/api/v1/user-role")
public class UserRoleController {
@Resource

View File

@@ -0,0 +1,218 @@
# 接口开发通用规范文档
## 文档说明
本规范为项目所有接口的通用开发准则,适用于前后端接口对接、后端接口开发、接口文档编写等场景,所有业务接口均需遵循本规范。
### 基础约定
- 接口基础路径:`/api/v1`各业务模块在基础路径后追加如Auth模块`/api/v1/auth`
- 数据交互格式JSON
- 字符编码UTF-8
- 时间戳单位:毫秒(整数类型)
- 接口版本管理通过URL路径版本号如v1区分不使用请求参数/请求头区分
## 一、通用请求规范
### 1. 请求方式RESTful规范
| 操作类型 | 请求方法 | 典型场景 | 示例 |
| ------------- | -------- | -------------------------------- | ----------------------------- |
| 新增/提交数据 | POST | 登录、发送验证码、创建资源 | `/api/v1/auth/login` |
| 查询数据 | GET | 获取详情、校验状态、列表查询 | `/api/v1/auth/user/info` |
| 修改数据 | PUT | 全量更新(如重置密码、修改信息) | `/api/v1/auth/reset-password` |
| 部分更新 | PATCH | 局部更新(如修改用户昵称) | `/api/v1/user/nickname` |
| 删除数据 | DELETE | 退出登录、删除资源 | `/api/v1/auth/logout` |
### 2. 请求头规范
| 字段名 | 类型 | 必填性 | 描述 | 示例 |
| --------------- | ------ | ------ | --------------------------------------------------------- | -------------------------------- |
| Authorization | String | 非必选 | 登录令牌,格式为`Bearer + 空格 + token`(需登录接口必传) | "Bearer eyJhbGciOiJIUzI1NiJ9..." |
| Content-Type | String | 必选 | 请求体格式,仅支持`application/json` | "application/json" |
| Accept-Language | String | 非必选 | 语言类型默认zh-CN | "zh-CN" |
### 3. 分页请求参数(列表接口必传)
所有列表查询接口均需支持分页,分页参数统一通过`GET`请求参数传递POST请求可放在Body中参数规范如下
| 字段名 | 类型 | 必填性 | 默认值 | 描述 | 示例 |
| ------ | ------- | ------ | ------ | ----------------------------- | ---- |
| page | Integer | 否 | 1 | 当前页码从1开始 | 1 |
| limit | Integer | 否 | 10 | 每页显示条数最大不超过100 | 10 |
#### 示例
```plain
GET /api/v1/user/list?current=1&size=10
```
### 4. 请求参数命名规范
- 字段名使用**小驼峰命名**(如`userName``phoneNumber`),禁止使用下划线/连字符;
- 布尔类型字段命名:使用`isXXX`格式(如`isRemember``isEnabled`
- 时间类字段:统一使用`createTime``updateTime`等,禁止使用`create_at`
- 参数值:手机号、身份证号等字符串类型参数,值为纯数字时仍以字符串传递。
## 二、通用响应规范
### 1. 基础响应格式(所有接口统一)
所有接口响应必须包含`code``message``timestamp`字段,`data`字段为可选(无业务数据时返回`null`)。
#### 格式定义
```json
{
"code": 200, // 整数,业务状态码
"message": "操作成功", // 字符串,提示信息(前端可直接展示)
"timestamp": 1744238900000, // 整数,服务器响应时间戳(毫秒)
"data": {} // 任意类型业务数据无数据时为null
}
```
#### 字段说明
| 字段名 | 类型 | 必填性 | 描述 |
| --------- | ------- | ------ | --------------------------------------------------------------------------- |
| code | Integer | 是 | 业务状态码200表示成功非200表示异常 |
| message | String | 是 | 响应提示信息(成功/失败描述,需友好、简洁) |
| timestamp | Integer | 是 | 服务器处理请求的时间戳(毫秒),前端可用于时间展示/校验 |
| data | Any | 否 | 业务数据体1. 无数据时为null2. 单条数据为Object3. 列表数据遵循分页规范 |
### 2. 分页响应格式(列表接口专用)
列表接口的`data`字段必须遵循以下结构,禁止自定义分页字段名/格式。
#### 格式定义
```json
{
"code": 200,
"message": "查询成功",
"timestamp": 1744238900000,
"data": {
"records": [], // 数组,当前页数据列表
"total": 0, // 整数,符合条件的总记录数
"size": 10, // 整数,每页显示条数(实际返回条数)
"current": 1, // 整数,当前页码
"pages": 0 // 整数总页数total/size向上取整
}
}
```
#### 字段说明
| 字段名 | 类型 | 必填性 | 描述 |
| ------- | ------- | ------ | ----------------------------------------- |
| records | Array | 是 | 当前页数据列表(无数据时为空数组) |
| total | Integer | 是 | 总记录数(用于计算总页数/分页展示) |
| size | Integer | 是 | 每页请求条数与请求参数size一致 |
| current | Integer | 是 | 当前页码与请求参数current一致 |
| pages | Integer | 是 | 总页数计算公式Math.ceil(total/size) |
#### 分页响应示例
```json
{
"code": 200,
"message": "用户列表查询成功",
"timestamp": 1744238900000,
"data": {
"records": [
{"id": 1, "username": "admin", "role": "ADMIN"},
{"id": 2, "username": "user1", "role": "USER"}
],
"total": 20,
"size": 10,
"current": 1,
"pages": 2
}
}
```
### 3. 通用状态码规范
| 状态码 | 含义 | 核心场景 |
| ------ | -------------- | -------------------------------------- |
| 200 | 操作成功 | 所有接口正常响应 |
| 400 | 参数错误 | 请求参数缺失/格式错误/校验不通过 |
| 401 | 未授权 | token失效/未登录/令牌格式错误 |
| 403 | 禁止访问 | 账号被封禁/权限不足/IP受限 |
| 404 | 资源不存在 | 用户不存在/接口路径错误/数据记录不存在 |
| 409 | 资源冲突 | 用户名重复/手机号已绑定/操作重复提交 |
| 429 | 请求过于频繁 | 验证码发送频率超限/接口调用频率超限 |
| 500 | 服务器内部错误 | 服务端逻辑异常/第三方接口调用失败 |
## 三、接口命名规范
### 1. URL路径命名
- 路径全部使用**小写字母**,多个单词用连字符(-)分隔(如`/api/v1/auth/refresh-token`
- 路径使用名词而非动词RESTful规范`/api/v1/user`(用户资源)而非`/api/v1/getUser`
- 资源ID放在路径末尾`/api/v1/user/123`查询ID为123的用户
### 2. 响应字段命名
- 与请求参数一致,使用**小驼峰命名**
- 时间字段优先返回时间戳Integer毫秒如需返回格式化时间字段名加`Str`后缀(如`createTimeStr`
- 金额字段:以分为单位返回整数(避免浮点精度问题),字段名加`Cent`后缀(如`amountCent`)。
## 四、安全规范(必遵循)
### 1. 密码/敏感信息处理
- 敏感信息返回:手机号、身份证号等脱敏展示(如`138****8000`
- Token处理JWT Token不存储敏感信息如密码Redis存储的Token设置合理过期时间。
### 2. 接口频率限制
- 验证码接口同一手机号1分钟内最多1次1小时内最多5次
- 登录接口同一账号5分钟内连续失败5次临时锁定15分钟
- 通用接口单IP/单用户接口调用频率不超过QPS 10。
### 3. 数据校验
- 所有入参必须做合法性校验(格式、长度、范围);
## 五、异常响应示例
### 1. 参数错误400
```json
{
"code": 400,
"message": "手机号格式错误",
"timestamp": 1744238900000,
"data": null
}
```
### 2. 未授权401
```json
{
"code": 401,
"message": "Token已过期请重新登录",
"timestamp": 1744238900000,
"data": null
}
```
### 3. 服务器错误500
```json
{
"code": 500,
"message": "服务器内部错误,请稍后重试",
"timestamp": 1744238900000,
"data": null
}
```
## 总结
1. 所有接口必须遵循**统一响应格式**包含code/message/timestamp列表接口强制分页并使用指定分页结构
2. 接口命名、参数/字段命名需符合RESTful规范和小驼峰/小写连字符约定,保证可读性;
3. 安全规范是核心底线密码、Token、频率限制等规则必须严格执行避免安全漏洞
4. 本规范为基础准则,各业务模块可在本规范基础上补充模块专属规则,但不得与本规范冲突。

View File

@@ -0,0 +1,256 @@
# 接口开发规范
## 0. 与API文档规范对照说明
本开发规范与`Controller/api-doc-standard.md`互为补充:
- **api-dev-standard.md**:指导**代码实现**(如何编写符合规范的代码)
- **api-doc-standard.md**:定义**接口契约**(请求/响应格式标准)
### 关键一致性说明
| 规范维度 | 代码实现规范 (本文件) | 接口文档规范 (api-doc-standard) | 对齐状态 |
| ------------ | -------------------------------- | ----------------------------------- | -------------------------------------- |
| **响应格式** | 必须使用ResponseUtils生成响应 | 强制要求code/message/timestamp/data | ✅ 对齐 |
| **分页参数** | 代码中参数名: `page`/`limit` | 文档中参数名: `page`/`limit` | ✅ 语义对齐page=current, limit=size |
| **状态码** | ResponseUtils需支持400/401等状态 | 明确200/400/401等状态码语义 | ⚠️ 需增强 |
| **时间字段** | Entity用LocalDateTime | 响应用时间戳(Integer) | ⚠️ 需转换 |
> **特别说明**:当前代码中`ResponseUtils`实现需扩展以完全符合api-doc-standard但开发规范以**实际代码结构**为准。
---
## 1. 整体架构规范
### 1.1 分层结构
遵循CLAUDE.md定义的分层架构特别注意
- **Controller层**:业务逻辑的主要编写位置
- **Service层**仅包含接口定义和MyBatis Plus继承实现
- 接口:`I{Name}Service` 继承 `IService<Entity>`
- 实现:`{Name}ServiceImpl` 继承 `ServiceImpl<Mapper, Entity>`
- **业务逻辑不应下沉到Service层**Controller直接调用Service提供的CRUD方法
### 1.2 目录规范
- Controller按实体划分目录`Controller/{EntityName}Controller/`
- **Entity目录**`Entity/` 下直接存放实体类(**无需子目录**
- **Mapper目录**`Mapper/` 下直接存放Mapper接口**无需子目录**
- **Service目录**
- 接口:`Service/`
- 实现:`Service/Implements/`
- 示例:
- `User.java``Entity/User.java`
- `UserMapper.java``Mapper/UserMapper.java`
## 2. Controller编写规范
### 2.1 服务注入
```java
@Resource
private IUserService userService;
```
- **必须**使用`@Resource`而非`@Autowired`
- **不得**在Service层编写业务逻辑
### 2.2 响应处理(必须遵守)
**所有接口必须使用ResponseUtils统一响应格式**
对于 Controller 中的函数返回类型,统一使用 Object 类型,而不是具体的实体类。
如果需要编写具体 VO 实体类,在对应的 Controller 目录下创建 vo 文件夹并在其中定义。
```java
// 成功响应
return ResponseUtils.success(data);
// 失败响应(需指定状态码)
return ResponseUtils.fail(400, "用户名格式错误");
```
- **必须包含**
- `code`:按[api-doc-standard#状态码规范](Controller/api-doc-standard.md#三、通用状态码规范)设置
- `timestamp`:当前时间戳(`System.currentTimeMillis()`
- `data`:业务数据(列表接口需返回标准分页结构)
- **禁止**直接返回原始对象
### 2.3 分页处理
**必须使用MyBatis Plus原生Page对象**
```java
@GetMapping
public Object getAllUsers(
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer limit) {
Page<User> pageObj = new Page<>(page, limit);
return ResponseUtils.success(userService.page(pageObj));
}
```
- **关键点**
- Page对象字段与API响应结构完全匹配records/total/size/current/pages
- **禁止**手动构建分页响应
- 分页参数**代码中命名为`page`/`limit`**
### 2.4 查询操作
**简单查询**直接使用Service方法
```java
@GetMapping("/{id}")
public Object getUserById(@PathVariable Long id) {
return ResponseUtils.success(userService.getById(id));
}
```
**复杂查询**使用QueryWrapper
```java
@GetMapping("/username/{username}")
public Object getUserByUsername(@PathVariable String username) {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("username", username);
return ResponseUtils.success(userService.getOne(wrapper));
}
```
- **注意**查询条件构建应在Controller层完成
## 3. Entity层规范
### 3.1 基础结构
```java
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("user")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String username;
private String password;
// ...其他字段
}
```
- **必须使用**
- Lombok注解`@Data` + `@NoArgsConstructor` + `@AllArgsConstructor`
- MyBatis Plus注解`@TableName` + `@TableId`
- **字段命名**:小驼峰命名(`createTime`而非`create_time`
### 3.2 时间字段处理
- **Entity层**:使用`LocalDateTime`类型(如`createTime`
## 4. Mapper层规范
### 4.1 基础结构
```java
@Mapper
public interface UserMapper extends BaseMapper<User> {
// 仅允许MyBatis Plus内置方法自定义SQL需走Service层
}
```
- **必须**继承`BaseMapper<Entity>`
- **禁止**在Mapper接口中添加方法自定义SQL需通过Service层
## 5. Service层规范
### 5.1 接口定义
```java
public interface IUserService extends IService<User> {
// 仅允许扩展MyBatis Plus未提供的方法
}
```
- **必须**继承`IService<Entity>`
- **仅当需要**扩展MyBatis Plus未提供的方法时才添加方法
### 5.2 实现类
```java
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Resource
private UserMapper userMapper;
}
```
- **必须**继承`ServiceImpl<Mapper, Entity>`
- **禁止**在实现类中编写业务逻辑仅用于注入Mapper
## 6. 禁止行为清单
| 类型 | 错误示例 | 正确做法 |
| ------------ | ----------------------------------- | ------------------------------------ |
| 业务逻辑位置 | 在ServiceImpl中编写业务 | 所有业务逻辑放在Controller |
| 响应格式 | 直接返回`new ResponseEntity<>(...)` | 用ResponseUtils生成带timestamp的响应 |
| 分页参数 | 使用`offset`/`pageSize` | 统一用`page`/`limit`参数 |
| 状态码 | 所有错误返回500 | 按[api-doc-standard]返回400/401等 |
| 时间字段 | 直接返回LocalDateTime | 转换为时间戳或格式化字符串 |
## 7. 必须使用的工具类
| 工具类 | 用途 | 示例 |
| --------------- | -------------- | ------------------------------------- |
| `ResponseUtils` | 生成标准化响应 | `ResponseUtils.fail(400, "参数错误")` |
## 8. 接口设计原则
1. **RESTful规范**
- 路径使用**复数名词**`/users`而非`/user`
- **参数命名**:代码用`page`/`limit`,文档用`current`/`size`
2. **字段转换**
- Entity的`LocalDateTime` → 接口响应需转为时间戳
- 敏感字段如密码需在Controller层过滤`user.setPassword(null)`
3. **错误处理**
- 按[api-doc-standard#通用状态码规范](Controller/api-doc-standard.md#三、通用状态码规范)返回精确错误
- 示例:`return ResponseUtils.fail(409, "用户名已存在");`
## 9. 完整示例
```java
@RestController
@RequestMapping("/users")
public class UserController {
@Resource
private IUserService userService;
@GetMapping("/{id}")
public Object getUser(@PathVariable Long id) {
User user = userService.getById(id);
// 过滤敏感字段
user.setPassword(null);
// 转换时间格式
return ResponseUtils.success(user);
}
@GetMapping
public Object listUsers(
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer limit) {
// 参数校验
if (limit > 100) {
return ResponseUtils.fail(400, "每页数量不能超过100");
}
Page<User> pageObj = new Page<>(page, limit);
Page<User> resultPage = userService.page(pageObj);
return ResponseUtils.success(resultPage);
}
}
```

View File

@@ -145,19 +145,6 @@ public class JsonUtils {
return toObject(json, Map.class, keyClazz, valueClazz);
}
/**
* 将对象转换为Map
*
* @param obj 对象
* @return Map
*/
public static Map<String, Object> toMap(Object obj) {
if (obj == null) {
return null;
}
return objectMapper.convertValue(obj, Map.class);
}
/**
* 验证JSON字符串是否有效
*

View File

@@ -1,111 +0,0 @@
package icu.sunway.ai_spring_example.Utils;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* 分页工具类
*/
public class PageUtils {
/**
* 默认页码
*/
public static final Integer DEFAULT_PAGE = 1;
/**
* 默认每页数量
*/
public static final Integer DEFAULT_LIMIT = 10;
/**
* 最大每页数量
*/
public static final Integer MAX_LIMIT = 100;
/**
* 分页结果类
*
* @param <T> 数据类型
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class PaginationResult<T> {
private Integer currentPage;
private Integer pageSize;
private Long totalItems;
private Integer totalPages;
private List<T> list;
}
/**
* 验证和调整分页参数
*
* @param page 页码
* @param limit 每页数量
* @return 调整后的分页参数数组 [page, limit]
*/
public static Integer[] validatePageParams(Integer page, Integer limit) {
// 验证页码
if (page == null || page < 1) {
page = DEFAULT_PAGE;
}
// 验证每页数量
if (limit == null || limit < 1 || limit > MAX_LIMIT) {
limit = DEFAULT_LIMIT;
}
return new Integer[] { page, limit };
}
/**
* 创建Page对象
*
* @param page 页码
* @param limit 每页数量
* @return Page对象
*/
public static <T> Page<T> createPage(Integer page, Integer limit) {
Integer[] params = validatePageParams(page, limit);
return new Page<>(params[0], params[1]);
}
/**
* 将MyBatis Plus的Page结果转换为分页结果
*
* @param pageResult MyBatis Plus的Page结果
* @return 分页结果
*/
public static <T> PaginationResult<T> createPaginationResult(IPage<T> pageResult) {
return new PaginationResult<>(
(int) pageResult.getCurrent(),
(int) pageResult.getSize(),
pageResult.getTotal(),
(int) pageResult.getPages(),
pageResult.getRecords());
}
/**
* 创建空的分页结果
*
* @param page 页码
* @param limit 每页数量
* @return 空的分页结果
*/
public static <T> PaginationResult<T> createEmptyPaginationResult(Integer page, Integer limit) {
Integer[] params = validatePageParams(page, limit);
return new PaginationResult<>(
params[0],
params[1],
0L,
0,
List.of());
}
}