签入版本
This commit is contained in:
@@ -1,135 +1,47 @@
|
||||
# 12.1 API 响应规范
|
||||
|
||||
:::tip 概述
|
||||
所有接口返回统一的 JSON 结构,前端可据此判断请求结果。成功响应 `code=0`,失败响应 `code=1`(或 401/403/404/422)。
|
||||
:::tip 必须遵守
|
||||
所有接口一律返回统一 JSON 信封,对外码值只有 0 / 1 / 401 三值语义。业务代码禁止自造响应形态。
|
||||
:::
|
||||
|
||||
## 统一格式
|
||||
|
||||
```typescript
|
||||
interface ApiResponse {
|
||||
code: number; // 状态码:0=成功,其他=失败
|
||||
ok: boolean; // 是否成功
|
||||
msg: string; // 提示信息
|
||||
data: any; // 响应数据
|
||||
}
|
||||
```
|
||||
|
||||
## 成功响应
|
||||
## 信封
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"ok": true,
|
||||
"msg": "操作成功",
|
||||
"data": {
|
||||
"id": 1,
|
||||
"username": "admin",
|
||||
"createTime": "2026-01-01 00:00:00"
|
||||
}
|
||||
}
|
||||
{ "code": 0, "ok": true, "msg": "操作成功", "data": {} }
|
||||
```
|
||||
|
||||
## 分页响应
|
||||
| 码值 | 语义 | 前端行为 |
|
||||
|---|---|---|
|
||||
| 0 | 成功 | 取 data |
|
||||
| 1 | 失败 | 弹 msg |
|
||||
| 401 | 未认证 | 跳登录(或刷新令牌) |
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"ok": true,
|
||||
"msg": "操作成功",
|
||||
"data": {
|
||||
"records": [
|
||||
{ "id": 1, "username": "admin" }
|
||||
],
|
||||
"total": 100,
|
||||
"size": 20,
|
||||
"current": 1,
|
||||
"pages": 5
|
||||
}
|
||||
}
|
||||
```
|
||||
内部细分码(400/403/429/1xxx)只用于服务端日志与文档,对外统一收敛(401 除外)。
|
||||
|
||||
## 失败响应
|
||||
## 构造规约
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 1,
|
||||
"ok": false,
|
||||
"msg": "用户名已存在",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
| 场景 | 写法 |
|
||||
|---|---|
|
||||
| 成功带数据 | `response.Ok(data)` |
|
||||
| 成功自定义文案 | `response.OkMsg(msg, data)` |
|
||||
| 分页 | `dto.NewPageData(...)` 经信封返回 |
|
||||
| 业务失败 | `return nil, consts.CodeXxx` / `apperr.Newf(...)` |
|
||||
| 禁止 | handler 手写 JSON、自定义 code 集、裸写 HTTP 状态 |
|
||||
|
||||
## 字段说明
|
||||
## 禁则
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `code` | number | 状态码,0=成功 |
|
||||
| `ok` | boolean | 是否成功 |
|
||||
| `msg` | string | 提示信息 |
|
||||
| `data` | any | 响应数据,失败时通常为 null |
|
||||
| `data.records` | array | 分页数据列表 |
|
||||
| `data.total` | number | 总记录数 |
|
||||
| `data.size` | number | 每页条数 |
|
||||
| `data.current` | number | 当前页码 |
|
||||
| `data.pages` | number | 总页数 |
|
||||
- 禁止返回 HTML 错误页(NoRoute/panic 已保证 JSON 兜底)
|
||||
- 禁止 data 内嵌套第二套 code/msg
|
||||
- 禁止吞错后返回"成功"空数据
|
||||
- 禁止在业务层直接 `response.Write`(信封由绑定层与中间件统一负责)
|
||||
|
||||
## 驼峰自动转换
|
||||
## 检查点
|
||||
|
||||
响应数据自动从 snake_case 转为 camelCase:
|
||||
- 新接口响应经信封且 code 语义正确
|
||||
- 前端只按 0/1/401 三分支处理
|
||||
- 错误 msg 是面向用户的中文提示(不含技术栈细节)
|
||||
|
||||
| 数据库字段 | 响应字段 |
|
||||
|-----------|---------|
|
||||
| `create_time` | `createTime` |
|
||||
| `user_name` | `userName` |
|
||||
| `is_delete` | `isDelete` |
|
||||
## 相关
|
||||
|
||||
可通过 `config/api.php` 的 `camel_snake_convert` 配置关闭。
|
||||
|
||||
## 后端构造方式
|
||||
|
||||
```php
|
||||
// Controller 中
|
||||
return $this->success($data, '操作成功'); // 成功
|
||||
return $this->success(['id' => $id], '添加成功'); // 成功(带数据)
|
||||
return $this->fail('用户名已存在'); // 失败
|
||||
return $this->_page($result); // 分页
|
||||
```
|
||||
|
||||
```php
|
||||
// Result 类直接调用
|
||||
Result::success($data, $msg);
|
||||
Result::page($records, $total, $current, $size);
|
||||
Result::fail($msg, $code);
|
||||
Result::unauthorized($msg); // 401
|
||||
Result::forbidden($msg); // 403
|
||||
Result::notFound($msg); // 404
|
||||
Result::validateError($errors); // 422 参数验证失败
|
||||
```
|
||||
|
||||
### validateError 示例
|
||||
|
||||
`Result::validateError()` 用于参数验证失败场景,HTTP 状态码为 422,`data` 字段返回具体错误明细,便于前端逐字段提示:
|
||||
|
||||
```php
|
||||
// 直接传字符串
|
||||
return Result::validateError('用户名不能为空');
|
||||
|
||||
// 传数组,包含多个字段错误
|
||||
return Result::validateError([
|
||||
'username' => '用户名不能为空',
|
||||
'email' => '邮箱格式不正确',
|
||||
]);
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 422,
|
||||
"ok": false,
|
||||
"msg": "参数验证失败",
|
||||
"data": {
|
||||
"username": "用户名不能为空",
|
||||
"email": "邮箱格式不正确"
|
||||
}
|
||||
}
|
||||
```
|
||||
- [8.1 统一响应格式](/zh/api-spec/response.html)
|
||||
- [8.2 错误码对照表](/zh/api-spec/error-code.html)
|
||||
|
||||
@@ -1,120 +1,64 @@
|
||||
# 12.6 后端编码规范
|
||||
# 12.8 后端编码规范
|
||||
|
||||
:::tip 概述
|
||||
本节定义后端 PHP 代码的编写规范,包括 Model、Logic、Controller、Validate 各层的命名和结构规范,确保团队代码风格统一。
|
||||
:::tip 建议遵守
|
||||
在数据访问铁律与模块规约之上,后端编码的通用风格约定:命名、注释、错误、并发、依赖方向。
|
||||
:::
|
||||
|
||||
## Model 层规范
|
||||
## 命名
|
||||
|
||||
```php
|
||||
// 继承 BaseModel
|
||||
class Example extends BaseModel
|
||||
{
|
||||
/**
|
||||
* 对应数据库表名(不含前缀)
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'example';
|
||||
}
|
||||
| 对象 | 规约 | 示例 |
|
||||
|---|---|---|
|
||||
| 模块文件 | 小写下划线,与表名对应 | user.go、dict_item.go |
|
||||
| 控制器 | C + 模块名 Pascal | CUser |
|
||||
| 服务 | s + 模块名 | sUser |
|
||||
| 仓储 | 模块名 + Repo | UserRepo |
|
||||
| 声明变量 | 模块名 + Spec | userSpec |
|
||||
| 常量 | 语义化大驼峰 | CodeRecordNotFound |
|
||||
| 缓存键 | consts 键函数生成 | CacheKeyCaptcha(uuid) |
|
||||
|
||||
## 注释
|
||||
|
||||
- 包注释一句说明模块职责;文件头不加横幅
|
||||
- 导出函数注释以函数名开头,说明行为/约束/降级
|
||||
- 反直觉处(顺序依赖、红线原因)必须注释"为什么"
|
||||
- 注释直述现状,不写改造过程与来源对比
|
||||
|
||||
## 错误处理
|
||||
|
||||
- 业务失败用哨兵/apperr,**原样上抛不 wrap**
|
||||
- 不吞错只记日志;日志与返回二选一时优先返回
|
||||
- panic 仅限编程错误(如 ToData 类型不匹配)——运行时输入不 panic
|
||||
|
||||
## 并发
|
||||
|
||||
- service 单例方法必须并发安全(无请求态字段)
|
||||
- ctx 贯穿一切(身份/事务/追踪),不用全局变量传请求态
|
||||
- 缓存读写经 utility/cache(自带降级),不直连 go-redis
|
||||
|
||||
## 依赖方向
|
||||
|
||||
```text
|
||||
handler → service → repository → db
|
||||
utility 被任意层引用;utility 不 import internal
|
||||
task → service(单向);service 不反向依赖 task
|
||||
```
|
||||
|
||||
## Logic 层规范
|
||||
禁止反向与横向环依赖;跨模块协作经同包直调或显式接口。
|
||||
|
||||
```php
|
||||
// 系统共享模块继承 BaseLogic
|
||||
class ExampleLogic extends BaseLogic
|
||||
{
|
||||
/**
|
||||
* 关联的模型类
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected string $modelClass = Example::class;
|
||||
## 性能习惯
|
||||
|
||||
/**
|
||||
* LIKE 模糊匹配字段
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected array $pageLikeFields = ['name'];
|
||||
- 查询指定列优先(大表 SELECT * 慎用)
|
||||
- 循环内不做 N+1 查询(批量 IN)
|
||||
- 大结果集导出走分批,不一次载入
|
||||
- 热点读经缓存(键走 consts 函数)
|
||||
|
||||
/**
|
||||
* 精确匹配字段
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected array $pageEqFields = ['status'];
|
||||
## 提交前
|
||||
|
||||
/**
|
||||
* 新增前处理
|
||||
*
|
||||
* @param array $data 待新增的数据
|
||||
* @return array 处理后的数据
|
||||
*/
|
||||
protected function beforeAdd(array $data): array
|
||||
{
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
// 租户模块继承 BaseTenantLogic
|
||||
class UserLogic extends BaseTenantLogic { ... }
|
||||
```bash
|
||||
make check # fmt + vet + test-short + check-config
|
||||
```
|
||||
|
||||
## Controller 层规范
|
||||
## 相关
|
||||
|
||||
```php
|
||||
class ExampleController extends BaseController
|
||||
{
|
||||
protected ExampleLogic $logic;
|
||||
|
||||
protected function initialize(): void
|
||||
{
|
||||
$this->logic = new ExampleLogic();
|
||||
}
|
||||
|
||||
// 使用注解
|
||||
#[Log('模块-操作', Log::TYPE_ADD)]
|
||||
#[Permission('sys:example:add', '添加')]
|
||||
public function add(): Json
|
||||
{
|
||||
return parent::_add($this->logic, $this->getJsonBody());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Validate 层规范
|
||||
|
||||
```php
|
||||
class ExampleValidate extends Validate
|
||||
{
|
||||
protected $rule = [
|
||||
'name' => 'require',
|
||||
'categoryId' => 'require', // ✅ 驼峰命名
|
||||
// 'category_id' => 'require', // ❌ 禁止下划线
|
||||
];
|
||||
|
||||
protected $message = [
|
||||
'name.require' => '名称不能为空',
|
||||
'categoryId.require' => '分类不能为空',
|
||||
];
|
||||
|
||||
protected $scene = [
|
||||
'add' => ['name', 'categoryId'],
|
||||
'update' => ['name'],
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
:::warning 字段名必须使用驼峰
|
||||
Validate 中的字段名必须使用 camelCase(驼峰),与前端传参一致。
|
||||
因为校验发生在 `BaseLogic::beforeAdd` 之后、`array_camel_to_snake` 转换之前,
|
||||
此时数据键名还是驼峰形式。使用下划线会导致校验规则匹配不到字段。
|
||||
:::
|
||||
|
||||
## 注解规范
|
||||
|
||||
- 所有写操作(POST/PUT/DELETE)必须标注 `#[Log]`
|
||||
- 所有需要权限控制的接口必须标注 `#[Permission]`
|
||||
- 权限码格式:`sys:{模块名}:{操作}`
|
||||
- [12.4 模块开发规约](/zh/standards/module-code.html)
|
||||
- [12.3 数据访问铁律](/zh/standards/data-access.html)
|
||||
|
||||
@@ -1,80 +1,48 @@
|
||||
# 12.5 代码审查清单
|
||||
# 12.7 代码审查清单
|
||||
|
||||
:::tip 概述
|
||||
代码审查(Code Review)是保证代码质量的重要环节。以下清单涵盖后端和前端的审查要点。
|
||||
:::tip 建议遵守
|
||||
审查按清单走:安全红线 → 契约影响 → 分层纪律 → 规约符合度。前四项任一不过即阻塞。
|
||||
:::
|
||||
|
||||
## 后端审查清单
|
||||
## 阻塞项(必须过)
|
||||
|
||||
### Model 层
|
||||
- [ ] **数据访问**:无裸 `*gorm.DB`;删除走软删;Raw 有场景注释([12.3](/zh/standards/data-access.html))
|
||||
- [ ] **契约稳定**:dto json 标签名未意外变更;RouteSpec 路径/权限码变更已同步前端与菜单
|
||||
- [ ] **错误处理**:业务错误原样上抛(无 wrap)、无吞错、无 handler 写响应
|
||||
- [ ] **安全**:新接口有权限码声明;敏感信息(密码/Token)不入日志;无硬编码密钥
|
||||
- [ ] **配置**:新增配置键同步 .env.example(check-config 可过)
|
||||
|
||||
- [ ] 继承 `BaseModel`
|
||||
- [ ] 表名 `$name` 配置正确
|
||||
- [ ] 关联关系定义正确(如有)
|
||||
- [ ] 无硬编码的数据库前缀
|
||||
## 规约项
|
||||
|
||||
### Logic 层
|
||||
- [ ] 模块四件套齐全、命名符合 [12.4](/zh/standards/module-code.html)
|
||||
- [ ] handler 只有委托;逻辑在 service;差异进 ModuleSpec 声明/钩子
|
||||
- [ ] 钩子无 HTTP/文件 IO、无 append
|
||||
- [ ] 事务用 WithTrans 且回调内用入参 ctx
|
||||
- [ ] 分页符合 [12.2](/zh/standards/pagination.html);响应信封符合 [12.1](/zh/standards/api-response.html)
|
||||
- [ ] 字典/枚举显示走 SerializeMaps 或字典接口,无硬编码文案映射
|
||||
- [ ] 注释按当前功能直述(无改造过程表述)
|
||||
|
||||
- [ ] 继承正确的基类(`BaseLogic` 或 `BaseTenantLogic`)
|
||||
- [ ] `$modelClass` 配置正确
|
||||
- [ ] 查询条件配置合理(`pageLikeFields` / `pageEqFields`)
|
||||
- [ ] 排序配置合理(`pageOrderBy`)
|
||||
- [ ] 唯一性校验配置(`uniqueFields`)
|
||||
- [ ] 枚举映射配置(`serializeMaps`)
|
||||
- [ ] 文件字段配置(`fileFields` / `fileSaveDir`)
|
||||
- [ ] 钩子方法中无 SQL 注入风险
|
||||
- [ ] 事务使用正确(`startTrans` / `commit` / `rollback`)
|
||||
## 前端项
|
||||
|
||||
### Controller 层
|
||||
- [ ] api 模块 URL 不带 /api 前缀,命名与后端动作对应
|
||||
- [ ] 按钮权限用 v-perm/v-perms,码值与菜单节点一致
|
||||
- [ ] 表单字段与 dto json 名一致(全量提交语义)
|
||||
- [ ] 未绕过 http 封装直接用 axios
|
||||
|
||||
- [ ] 继承 `BaseController`
|
||||
- [ ] `initialize()` 中实例化 Logic
|
||||
- [ ] 所有接口标注 `#[Permission]` 注解
|
||||
- [ ] 所有写操作标注 `#[Log]` 注解
|
||||
- [ ] 权限码格式正确(`sys:模块名:操作`)
|
||||
- [ ] 异常处理完善(try-catch)
|
||||
- [ ] 参数校验充分
|
||||
## 测试与工程
|
||||
|
||||
### 安全
|
||||
- [ ] 新逻辑有配套测试(或说明为何不测)
|
||||
- [ ] `make check` / 前端 type+lint 本地通过
|
||||
- [ ] 生成器产物与模板改动同提交
|
||||
- [ ] 提交信息符合 [12.5](/zh/standards/git.html)
|
||||
|
||||
- [ ] 无 SQL 注入风险(使用 ORM 参数绑定)
|
||||
- [ ] 无 XSS 风险(输出转义)
|
||||
- [ ] 敏感数据不暴露(password、salt)
|
||||
- [ ] 文件上传有类型和大小限制
|
||||
- [ ] 权限校验覆盖所有接口
|
||||
## 审查沟通原则
|
||||
|
||||
## 前端审查清单
|
||||
- 挑刺对代码不对人;给出理由与替代方案
|
||||
- 优先级:正确性 > 安全 > 契约 > 可读性 > 风格
|
||||
- 争议收敛到规约文档——这次讨论的结论应能写进本章某页
|
||||
|
||||
### API 模块
|
||||
## 相关
|
||||
|
||||
- [ ] 函数命名规范(`getXxxPage` / `xxxAdd` / `xxxUpdate` / `xxxDelete`)
|
||||
- [ ] HTTP 方法正确(GET/POST/PUT/DELETE)
|
||||
- [ ] 参数传递方式正确(params / data)
|
||||
- [ ] 错误处理完善
|
||||
|
||||
### 页面组件
|
||||
|
||||
- [ ] 使用 `<script setup lang="ts">` 语法
|
||||
- [ ] Props 有类型定义
|
||||
- [ ] 表单有验证规则
|
||||
- [ ] 删除操作有确认弹窗
|
||||
- [ ] 加载状态处理(loading)
|
||||
- [ ] 空数据处理
|
||||
|
||||
### 权限
|
||||
|
||||
- [ ] 按钮级权限使用 `v-perm` 指令
|
||||
- [ ] 权限码与后端一致
|
||||
|
||||
### 性能
|
||||
|
||||
- [ ] 列表数据分页加载
|
||||
- [ ] 图片懒加载
|
||||
- [ ] 组件按需引入
|
||||
|
||||
## Git 提交审查
|
||||
|
||||
- [ ] commit message 格式正确
|
||||
- [ ] 一个 commit 只做一件事
|
||||
- [ ] 无调试代码残留(console.log、dump)
|
||||
- [ ] 无敏感信息泄露(密码、密钥)
|
||||
- [12.8 后端编码规范](/zh/standards/backend-code.html)
|
||||
- [12.9 前端编码规范](/zh/standards/frontend-code.html)
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
# 12.3 数据访问铁律
|
||||
|
||||
:::tip 必须遵守
|
||||
数据访问的四条红线:**禁止裸用 `*gorm.DB`、一切删除走软删除、Raw 受限使用、事务统一 WithTrans**。违反任何一条都会破坏租户隔离或审计完整性。
|
||||
:::
|
||||
|
||||
## 铁律一:禁止裸用 *gorm.DB
|
||||
|
||||
```go
|
||||
// 正确
|
||||
repository.NewUserRepo().Ctx(ctx).Where(...).Find(...)
|
||||
|
||||
// 禁止
|
||||
db.DB.Where(...) // 全局句柄直用
|
||||
gorm.Open(...) // 业务层自建连接
|
||||
```
|
||||
|
||||
`Ctx(ctx)` 是唯一常规入口——软删过滤与租户隔离在此注入,绕过即裸奔。
|
||||
|
||||
## 铁律二:一切删除走软删除
|
||||
|
||||
```go
|
||||
repo.SoftDelete(ctx, "id", id) // UPDATE del_flag=1
|
||||
repo.SoftDeleteByID(ctx, id)
|
||||
```
|
||||
|
||||
物理 DELETE 被全局钩子拦截。确需物理清理(定时任务清超期日志):
|
||||
|
||||
```go
|
||||
ctx = hooks.WithPhysicalDelete(ctx) // 显式放行,作用域仅此链路
|
||||
```
|
||||
|
||||
## 铁律三:Raw 仅限明确场景
|
||||
|
||||
`Raw()` 不注入任何 Scope(已删数据可见、跨租户可见),仅限:
|
||||
|
||||
- 导入(批量落库)
|
||||
- 字典管理(平台级维护)
|
||||
- 登录校验(公开路由无租户身份)
|
||||
|
||||
使用处必须注释说明场景正当性,且**条件完整性由使用者自行保证**。
|
||||
|
||||
## 铁律四:事务统一 WithTrans
|
||||
|
||||
```go
|
||||
db.WithTrans(ctx, func(ctx context.Context) error {
|
||||
// 回调内一律用入参 ctx(携带事务连接)
|
||||
// 返回非 nil 错误整体回滚
|
||||
})
|
||||
```
|
||||
|
||||
跨模块写操作必须同事务;钩子在事务内同样生效(审计盖章一致)。
|
||||
|
||||
## 常见违反与后果
|
||||
|
||||
| 违反 | 后果 |
|
||||
|---|---|
|
||||
| 裸 DB 查询 | 查到已删数据、跨租户数据泄露 |
|
||||
| 业务代码物理删除 | 审计链断裂,无法追责 |
|
||||
| Raw 当常规用 | 同裸 DB,且逐渐扩散 |
|
||||
| 事务用外层 ctx | 事务内查询落回连接池,回滚不生效 |
|
||||
|
||||
## 审查抓手
|
||||
|
||||
PR 审查 grep 三板斧:`gorm.DB`(层内裸用)、`\.Delete(`(物理删除)、`Raw(`(受限入口)。
|
||||
|
||||
## 相关
|
||||
|
||||
- [3.4 数据访问层(repository)](/zh/guide/repository.html)
|
||||
- [4.8 横切机制](/zh/architecture/cross-cutting.html)
|
||||
- [12.6 数据删除规范](/zh/standards/deletion.html)
|
||||
@@ -1,156 +1,54 @@
|
||||
# 12.4 数据删除规范
|
||||
# 12.6 数据删除规范
|
||||
|
||||
## 软删除约定
|
||||
|
||||
:::tip 核心原则
|
||||
所有业务数据统一使用软删除机制,通过 `is_delete` 字段标记记录状态,不物理删除数据。
|
||||
:::tip 建议遵守
|
||||
删除的默认形态是软删除;物理删除是受控例外。删除前做引用守卫,删除后可追溯。
|
||||
:::
|
||||
|
||||
| is_delete 值 | 含义 |
|
||||
|--------------|------|
|
||||
| `0` | 正常记录(默认值) |
|
||||
| `1` | 已删除记录 |
|
||||
## 默认:软删除
|
||||
|
||||
所有查询方法自动过滤 `is_delete = 0`,业务层无需关心已删除数据。
|
||||
```go
|
||||
// repository 标准入口
|
||||
repo.SoftDelete(ctx, "id", id)
|
||||
repo.SoftDeleteByID(ctx, id)
|
||||
|
||||
## 单条删除
|
||||
|
||||
### URL 格式
|
||||
|
||||
```text
|
||||
DELETE /api/{module}/delete/{id}
|
||||
// 批量:基座 BatchDelete(逐条软删)
|
||||
spec.BatchDelete(ctx, ids)
|
||||
```
|
||||
|
||||
### Controller 示例
|
||||
软删后对查询不可见(Ctx 注入 del_flag=0),数据保留可审计、可恢复(运维按需 UPDATE 回 0)。
|
||||
|
||||
```php
|
||||
#[Log('案例演示-删除记录', Log::TYPE_DELETE, '删除案例演示ID:{id}')]
|
||||
#[Permission('sys:example:delete', '删除案例演示')]
|
||||
public function delete(int $id): Json
|
||||
{
|
||||
return parent::_remove($this->logic, $id);
|
||||
}
|
||||
## 例外:物理删除
|
||||
|
||||
仅限**数据本身有保留期语义**的场景(超期日志、临时文件索引):
|
||||
|
||||
```go
|
||||
ctx = hooks.WithPhysicalDelete(ctx) // 显式放行
|
||||
```
|
||||
|
||||
### 内部流程
|
||||
放行要收敛在最小函数作用域,注释说明依据(如"超过 job.logRetentionDays")。
|
||||
|
||||
```text
|
||||
Controller::delete($id)
|
||||
│
|
||||
▼
|
||||
BaseController::_remove($logic, $id)
|
||||
│
|
||||
▼
|
||||
BaseLogic::delete($id)
|
||||
│
|
||||
├─ beforeDelete($id) 钩子(抛异常可拦截)
|
||||
│
|
||||
├─ $model->find($id)
|
||||
│ └─ 不存在 → return false
|
||||
│
|
||||
├─ $model->save(['is_delete' => 1])
|
||||
│
|
||||
└─ afterDelete($id) 钩子
|
||||
```
|
||||
## 删除守卫
|
||||
|
||||
## 批量删除
|
||||
删除被引用数据前做存在性检查,冲突时上抛守卫错误:
|
||||
|
||||
### 请求格式
|
||||
| 错误码 | 场景 |
|
||||
|---|---|
|
||||
| CodeHasChildren | 树形表存在下级(部门/菜单/分类) |
|
||||
| CodeInUse | 关键记录仍被引用(用户在角色中等) |
|
||||
|
||||
```text
|
||||
DELETE /api/{module}/batchDelete
|
||||
Content-Type: application/json
|
||||
守卫检查用 `ExistsByCond`(软删/租户已过滤)。
|
||||
|
||||
{ "ids": [1, 2, 3] }
|
||||
```
|
||||
## 前端交互
|
||||
|
||||
### Controller 示例
|
||||
- 危险删除(批量、不可逆)必须二次确认
|
||||
- 删除失败展示后端 msg(守卫原因)
|
||||
- 不做"假删除"(前端移除行但不调接口)
|
||||
|
||||
```php
|
||||
#[Log('案例演示-批量删除', Log::TYPE_DELETE)]
|
||||
#[Permission('sys:example:delete', '删除案例演示')]
|
||||
public function batchDelete(): Json
|
||||
{
|
||||
$ids = $this->getJsonBody()['ids'] ?? [];
|
||||
return parent::_batchRemove($this->logic, $ids);
|
||||
}
|
||||
```
|
||||
## 审计要求
|
||||
|
||||
### 内部流程
|
||||
删除操作经 RouteSpec Log/LogType=delete 留操作日志——谁删了什么可追溯。物理删除场景本身不落业务表,但任务日志留有执行记录。
|
||||
|
||||
```text
|
||||
Controller::batchDelete()
|
||||
│
|
||||
▼
|
||||
BaseController::_batchRemove($logic, $ids)
|
||||
│
|
||||
├─ ids 为空 → 返回失败 "请选择要删除的数据"
|
||||
│
|
||||
▼
|
||||
BaseLogic::batchDelete($ids)
|
||||
│
|
||||
├─ beforeBatchDelete($ids) 钩子(整体校验)
|
||||
│
|
||||
├─ 逐条 beforeDelete($id) 校验
|
||||
│
|
||||
├─ Model::batchSoftDelete($ids)
|
||||
│ └─ UPDATE SET is_delete = 1 WHERE id IN (...)
|
||||
│
|
||||
└─ afterBatchDelete($ids) 钩子
|
||||
```
|
||||
## 相关
|
||||
|
||||
## 删除前校验(beforeDelete 钩子)
|
||||
|
||||
在 Logic 中重写 `beforeDelete` 方法,抛异常可拦截删除:
|
||||
|
||||
```php
|
||||
class UserLogic extends BaseTenantLogic
|
||||
{
|
||||
protected function beforeDelete(int $id): void
|
||||
{
|
||||
// 检查是否有子级
|
||||
$children = Dept::where('parent_id', $id)->where('is_delete', 0)->count();
|
||||
if ($children > 0) {
|
||||
throw new \Exception('存在子部门,不可删除');
|
||||
}
|
||||
|
||||
// 检查是否被引用
|
||||
$userCount = User::where('dept_id', $id)->where('is_delete', 0)->count();
|
||||
if ($userCount > 0) {
|
||||
throw new \Exception('该部门下有用户,不可删除');
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 何时可物理删除
|
||||
|
||||
| 场景 | 方式 | 说明 |
|
||||
|------|------|------|
|
||||
| 日志数据清理 | 定时任务 | CleanLogTask 清理 N 天前的日志 |
|
||||
| 临时数据 | 定时任务 | 清理过期的临时文件 |
|
||||
| 其它 | 审批 | 需经过管理员审批 |
|
||||
|
||||
## 前端删除规范
|
||||
|
||||
### 单条删除
|
||||
|
||||
```vue
|
||||
<el-popconfirm title="确认删除该记录?" @confirm="handleDelete(row.id)">
|
||||
<template #reference>
|
||||
<el-button type="danger" size="small">删除</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
```
|
||||
|
||||
### 批量删除
|
||||
|
||||
```vue
|
||||
<el-button
|
||||
type="danger"
|
||||
:disabled="!selectedIds.length"
|
||||
@click="handleBatchDelete"
|
||||
>
|
||||
批量删除 ({{ selectedIds.length }})
|
||||
</el-button>
|
||||
```
|
||||
- [12.3 数据访问铁律](/zh/standards/data-access.html)
|
||||
- [4.8 横切机制](/zh/architecture/cross-cutting.html)
|
||||
|
||||
@@ -1,142 +1,64 @@
|
||||
# 12.7 前端编码规范
|
||||
# 12.9 前端编码规范
|
||||
|
||||
:::tip 概述
|
||||
前端编码规范涵盖 Vue 组件、TypeScript、API 模块、样式和代码风格等方面。
|
||||
:::tip 建议遵守
|
||||
前端通用风格约定:分层隔离、命名一致、组件复用、类型渐进。配合 `pnpm type:check` 与 `pnpm lint:eslint` 门禁。
|
||||
:::
|
||||
|
||||
## Vue 组件规范
|
||||
## 分层纪律
|
||||
|
||||
```vue
|
||||
<!-- 推荐的组件结构 -->
|
||||
<template>
|
||||
<!-- 模板内容 -->
|
||||
</template>
|
||||
| 层 | 禁止 |
|
||||
|---|---|
|
||||
| views 页面 | 直接 axios、写信封判断、放公共组件 |
|
||||
| api 模块 | UI 逻辑、持 Token、catch 兜底(拦截器已做) |
|
||||
| components 公共组件 | 耦合具体业务模块 |
|
||||
| store | 存可从接口推导的数据 |
|
||||
| utils | import api/views/store(环依赖) |
|
||||
|
||||
<script setup lang="ts">
|
||||
// 使用 <script setup lang="ts"> 语法
|
||||
import { ref, onMounted } from 'vue';
|
||||
## 命名
|
||||
|
||||
// Props 定义
|
||||
interface Props {
|
||||
id: number;
|
||||
name?: string;
|
||||
}
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
name: '',
|
||||
});
|
||||
| 对象 | 规约 | 示例 |
|
||||
|---|---|---|
|
||||
| api 文件 | 域/模块.ts | system/user.ts |
|
||||
| api 函数 | 模块+动作 | userPage、userAdd |
|
||||
| 页面目录 | 域/模块/ | views/system/user/ |
|
||||
| 组件 | PascalCase | UserTable.vue |
|
||||
| hooks | use-*.ts | use-async.ts |
|
||||
| store | camelCase 模块 | asyncRoute.ts |
|
||||
| 权限码 | 与后端逐字一致 | sys:user:add |
|
||||
|
||||
// 响应式数据
|
||||
const loading = ref(false);
|
||||
const tableData = ref<any[]>([]);
|
||||
## 组件实践
|
||||
|
||||
// 方法
|
||||
const fetchData = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await getExamplePage({ pageNo: 1, pageSize: 20 });
|
||||
tableData.value = res.records;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
- 公共组件优先(Table/Page/Form/Select/Upload...),不重复造
|
||||
- 页面私有组件放 views 下 components/
|
||||
- props 显式声明 + 类型(vue-types/propTypes)
|
||||
- 事件命名 `update:xxx` / `change`,不滥用全局事件总线
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => fetchData());
|
||||
</script>
|
||||
## 状态与数据
|
||||
|
||||
<style scoped>
|
||||
/* 组件样式 */
|
||||
</style>
|
||||
```
|
||||
- 本地状态 ref/reactive;跨页面才进 Pinia
|
||||
- storage 键走 mutation-types 常量,不散裸字符串
|
||||
- 字典数据走字典接口,不硬编码 options
|
||||
|
||||
## TypeScript 规范
|
||||
## 样式
|
||||
|
||||
```typescript
|
||||
// 接口定义
|
||||
interface User {
|
||||
id: number;
|
||||
username: string;
|
||||
realname: string;
|
||||
status: number;
|
||||
}
|
||||
- 颜色/间距用变量,不硬编码色值
|
||||
- 组件样式 scoped;全局样式进 styles/ 统一管理
|
||||
- 避免 `!important`;层级用变量
|
||||
|
||||
// 枚举定义
|
||||
enum UserStatus {
|
||||
ENABLE = 1,
|
||||
DISABLE = 2,
|
||||
}
|
||||
## 类型
|
||||
|
||||
// API 响应类型
|
||||
interface PageResult<T> {
|
||||
records: T[];
|
||||
total: number;
|
||||
size: number;
|
||||
current: number;
|
||||
pages: number;
|
||||
}
|
||||
- 渐进式补类型:api 泛型 → props → 页面
|
||||
- 枚举用 as const + 类型导出
|
||||
- 临时 any 需注释原因,不扩散
|
||||
|
||||
// Props 类型
|
||||
interface TableProps {
|
||||
data: any[];
|
||||
loading?: boolean;
|
||||
columns: Column[];
|
||||
}
|
||||
```
|
||||
|
||||
## API 模块规范
|
||||
|
||||
```typescript
|
||||
// ui/src/api/system/user.ts
|
||||
import { http } from '@/utils/http/axios';
|
||||
|
||||
// 查询用 GET + params
|
||||
export function getUserPage(params?: Record<string, any>) {
|
||||
return http.request({ url: '/user/page', method: 'GET', params });
|
||||
}
|
||||
|
||||
// 新增用 POST + data
|
||||
export function userAdd(data: any) {
|
||||
return http.request({ url: '/user/add', method: 'POST', data });
|
||||
}
|
||||
|
||||
// 修改用 PUT + data
|
||||
export function userUpdate(data: any) {
|
||||
return http.request({ url: '/user/update', method: 'PUT', data });
|
||||
}
|
||||
|
||||
// 删除用 DELETE
|
||||
export function userDelete(id: number) {
|
||||
return http.request({ url: '/user/delete/' + id, method: 'DELETE' });
|
||||
}
|
||||
```
|
||||
|
||||
## 命名规范
|
||||
|
||||
| 类型 | 规范 | 示例 |
|
||||
|------|------|------|
|
||||
| 组件文件 | PascalCase | `BasicTable.vue` |
|
||||
| API 文件 | camelCase | `user.ts` |
|
||||
| API 函数 | camelCase | `getUserPage` |
|
||||
| Store 文件 | camelCase | `user.ts` |
|
||||
| 常量 | UPPER_SNAKE_CASE | `ACCESS_TOKEN` |
|
||||
| CSS 类 | kebab-case | `page-container` |
|
||||
|
||||
## 代码风格
|
||||
## 提交前
|
||||
|
||||
```bash
|
||||
# ESLint 检查
|
||||
pnpm type:check
|
||||
pnpm lint:eslint
|
||||
|
||||
# Prettier 格式化
|
||||
pnpm lint:prettier
|
||||
|
||||
# Stylelint 检查
|
||||
pnpm lint:stylelint
|
||||
```
|
||||
|
||||
:::warning 禁止事项
|
||||
- 不要使用 `any` 类型(除非确实无法确定类型)
|
||||
- 不要使用 `console.log`(生产环境)
|
||||
- 不要在模板中写复杂逻辑(提取为 computed 或方法)
|
||||
- 不要在 `setup` 外使用 Composition API
|
||||
:::
|
||||
## 相关
|
||||
|
||||
- [9.14 API 模块编写规范](/zh/frontend/api-module.html)
|
||||
- [12.7 代码审查清单](/zh/standards/code-review.html)
|
||||
|
||||
@@ -1,117 +1,62 @@
|
||||
# 12.3 Git 提交规范
|
||||
# 12.5 Git 提交规范
|
||||
|
||||
:::tip 概述
|
||||
统一的 Git 提交规范有助于代码审查、版本管理和自动化工具集成。
|
||||
:::tip 必须遵守
|
||||
提交信息采用约定式格式:`类型(范围): 摘要`。一个提交一个主题,提交前本地过 `make check`。
|
||||
:::
|
||||
|
||||
## Commit Message 格式
|
||||
## 格式
|
||||
|
||||
```html
|
||||
<type>(<scope>): <subject>
|
||||
```text
|
||||
<类型>(<范围>): <摘要,祈使句,不带句号>
|
||||
|
||||
<body>
|
||||
<可选正文:动机与取舍>
|
||||
<可选脚注:关联需求/破坏性说明>
|
||||
```
|
||||
|
||||
## Type 类型
|
||||
示例:
|
||||
|
||||
| type | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| `feat` | 新功能 | `feat(user): 新增用户导入导出功能` |
|
||||
| `fix` | 修复 Bug | `fix(login): 修复验证码过期后仍可使用的问题` |
|
||||
| `docs` | 文档 | `docs: 更新 API 接口文档` |
|
||||
| `style` | 代码格式(不影响功能) | `style: 统一代码缩进` |
|
||||
| `refactor` | 重构 | `refactor(logic): 优化分页查询性能` |
|
||||
| `perf` | 性能优化 | `perf(dict): 增加字典缓存减少数据库查询` |
|
||||
| `test` | 测试 | `test: 添加用户模块单元测试` |
|
||||
| `chore` | 构建/工具 | `chore: 更新 Composer 依赖` |
|
||||
|
||||
## Scope 范围
|
||||
|
||||
| scope | 说明 |
|
||||
|-------|------|
|
||||
| `user` | 用户模块 |
|
||||
| `role` | 角色模块 |
|
||||
| `menu` | 菜单模块 |
|
||||
| `dict` | 字典模块 |
|
||||
| `job` | 定时任务 |
|
||||
| `auth` | 认证授权 |
|
||||
| `upload` | 文件上传 |
|
||||
| `generator` | 代码生成器 |
|
||||
| `config` | 配置 |
|
||||
| `deploy` | 部署 |
|
||||
|
||||
## 示例
|
||||
|
||||
```bash
|
||||
# -----------------------------------------------------------------------------
|
||||
# 新功能(feat)
|
||||
# -----------------------------------------------------------------------------
|
||||
# -m → 指定提交信息(单行简短描述)
|
||||
git commit -m "feat(article): 新增文章管理模块"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 修复 Bug(fix)
|
||||
# -----------------------------------------------------------------------------
|
||||
git commit -m "fix(login): 修复密码错误时未记录登录日志的问题"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 文档更新(docs)
|
||||
# -----------------------------------------------------------------------------
|
||||
git commit -m "docs: 补充多数据库部署文档"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 重构(refactor)
|
||||
# -----------------------------------------------------------------------------
|
||||
git commit -m "refactor(logic): 统一删除前校验逻辑"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 性能优化(perf)
|
||||
# -----------------------------------------------------------------------------
|
||||
git commit -m "perf(dict): 增加请求级缓存减少重复查询"
|
||||
```text
|
||||
feat(user): 用户导入增加手机号唯一校验
|
||||
fix(dict): 字典项变更未删除缓存导致显示名滞后
|
||||
refactor(repo): 基座 SoftDelete 条件参数成对传入
|
||||
docs(quickstart): 补充 Windows make.bat 说明
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
## 类型
|
||||
|
||||
:::warning 禁止事项
|
||||
- 不要提交调试代码(`console.log`、`dump`、`var_dump`)
|
||||
- 不要提交敏感信息(密码、密钥、Token)
|
||||
- 不要提交 IDE 配置文件(`.idea/`、`.vscode/`)
|
||||
- 一个 commit 只做一件事
|
||||
:::
|
||||
| 类型 | 含义 |
|
||||
|---|---|
|
||||
| feat | 新功能 |
|
||||
| fix | 缺陷修复 |
|
||||
| refactor | 重构(不改行为) |
|
||||
| perf | 性能 |
|
||||
| docs | 文档 |
|
||||
| test | 测试 |
|
||||
| build / ci | 构建与流水线 |
|
||||
| chore | 杂项 |
|
||||
|
||||
## .gitignore
|
||||
## 范围
|
||||
|
||||
```gitignore
|
||||
# =============================================================================
|
||||
# .gitignore — Git 版本控制忽略规则
|
||||
# =============================================================================
|
||||
# 匹配的文件不会被 git track(不会提交到仓库)
|
||||
# =============================================================================
|
||||
按模块/域命名:user、role、dict、job、upload、router、generator、frontend、deploy...
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 运行时目录(ThinkPHP 缓存、日志、编译模板 — 自动生成,无需版本控制)
|
||||
# -----------------------------------------------------------------------------
|
||||
runtime/
|
||||
vendor/
|
||||
## 纪律
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 环境配置(包含数据库密码等敏感信息 — 绝不能提交)
|
||||
# -----------------------------------------------------------------------------
|
||||
.env
|
||||
- **一个提交一个主题**:功能与格式化提交分离(禁大杂烩 diff)
|
||||
- **提交前**:`make check`(后端改动)/ `pnpm type:check + lint`(前端改动)
|
||||
- **不提交**:log/、upload/ 运行产物、.env、IDE 配置
|
||||
- **生成器产物**:与模板改动同一提交(保持可追溯)
|
||||
- **破坏性变更**(API 契约调整):提交正文显式标注并同步前端
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# IDE 配置文件(每个开发者的 IDE 设置不同 — 不应互相影响)
|
||||
# -----------------------------------------------------------------------------
|
||||
.idea/
|
||||
.vscode/
|
||||
## 分支建议
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 临时上传文件(用户上传的临时文件 — 不属于代码)
|
||||
# -----------------------------------------------------------------------------
|
||||
uploads/temp/
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 前端依赖(node_modules 体积庞大 — 通过 pnpm install 重新安装即可)
|
||||
# -----------------------------------------------------------------------------
|
||||
ui/node_modules/
|
||||
```text
|
||||
main 生产就绪
|
||||
develop 集成分支
|
||||
feat/* 功能
|
||||
fix/* 修复
|
||||
```
|
||||
|
||||
## 相关
|
||||
|
||||
- [11.11 CI/CD 与质量门禁](/zh/deploy/ci-cd.html)
|
||||
- [12.7 代码审查清单](/zh/standards/code-review.html)
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
# 12.4 模块开发规约
|
||||
|
||||
:::tip 必须遵守
|
||||
一个模块固定四个文件(dto / repository / service / handler),对外 JSON 契约(json 标签、字段名、路径、权限码、binding 标签)以表结构与前端约定为准,保持稳定。本页是规约全文要点。
|
||||
:::
|
||||
|
||||
## dto — internal/model/dto/{module}.go
|
||||
|
||||
- 结构体按职责命名(UserItem、UserPageReq…),json 标签即对外 API 字段名
|
||||
- 每字段补 `form:"同json名"` 标签;binding/msg 与 dc 注释按校验需求书写
|
||||
- 时间字段统一 `*LocalTime`;文件字段 `*multipart.FileHeader` + UnmarshalBody
|
||||
- 分页公共字段内嵌 PageReq;批量删除用 `Ids []int64`
|
||||
- 类型别名:`type XxxListRes = []*XxxItem`、`type XxxPageRes = PageData[XxxItem]`
|
||||
- 路由声明在 router.go 该控制器的 RouteSpec 表(字段 Method/Path/Summary/Permission/Log/LogType/Public),新增接口补一条,写法与既有条目逐字一致
|
||||
|
||||
## repository — internal/repository/{module}.go
|
||||
|
||||
```go
|
||||
type XxxRepo struct{ Base }
|
||||
func NewXxxRepo() *XxxRepo { return &XxxRepo{Base: NewBase("gin_xxx")} }
|
||||
func (r *XxxRepo) Ctx(ctx context.Context) *gorm.DB { return r.Base.Ctx(ctx) }
|
||||
```
|
||||
|
||||
表名 = gin_ + 模块名;关联表同样建仓储。铁律见 [12.3 数据访问铁律](/zh/standards/data-access.html)。
|
||||
|
||||
## service — internal/service/{module}.go
|
||||
|
||||
- 三段式:`type sXxx struct{}` + init 注册 + `var Xxx *sXxx`
|
||||
- 标准七方法委托 ModuleSpec;复杂逻辑(导入导出、登录、分配菜单)手写方法
|
||||
- 跨模块同包直调(无需 import);dict 相关模块 init 调 SetDictLookup
|
||||
- 数据权限需要的模块声明 `DataScope: ApplyDataScope("dept_id")`
|
||||
- 事务:`db.WithTrans(ctx, func(ctx context.Context) error { ... })`
|
||||
- 业务错误:`consts.CodeXxx` 哨兵或 `apperr.Newf(consts.CodeXxx.Code(), "fmt", a...)`
|
||||
- 钩子禁 HTTP/文件 IO、禁 append;错误原样上抛
|
||||
|
||||
## handler — internal/handler/{module}.go
|
||||
|
||||
- 极薄委托:`type CXxx struct{}` + 与 dto 一一对应的方法
|
||||
- 签名固定 `func (c *CXxx) Method(ctx context.Context, req *dto.XxxReq) (*dto.XxxRes, error)`
|
||||
- 非标准方法(导入/导出/重置密码/分配菜单等)保留为 handler 方法并注释入参/返回/副作用
|
||||
|
||||
## 通用禁则
|
||||
|
||||
- 注释一律按当前功能直述
|
||||
- 文件头不加版权横幅,仅包注释一句说明模块职责
|
||||
- binding/msg 标签逐字保持(本项目 validator 读取该标签)
|
||||
- 新增配置键必须同步 .env.example
|
||||
|
||||
## 相关
|
||||
|
||||
- [第 3 章 模块开发实战](/zh/guide/overview.html)
|
||||
- [3.11 完整代码清单](/zh/guide/code-list.html)
|
||||
@@ -1,65 +1,41 @@
|
||||
# 12.2 分页查询规范
|
||||
|
||||
:::tip 概述
|
||||
所有分页接口遵循统一的参数命名和返回结构,确保前后端一致性。
|
||||
:::tip 必须遵守
|
||||
列表查询一律用分页契约:请求内嵌 PageReq,响应为 PageData 五字段。禁止自造分页参数名或响应结构。
|
||||
:::
|
||||
|
||||
## 请求参数
|
||||
## 契约
|
||||
|
||||
| 参数 | 类型 | 默认值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| `pageNo` | number | 1 | 页码(最小为 1) |
|
||||
| `pageSize` | number | 20 | 每页条数(1~100) |
|
||||
| `orderField` | string | - | 排序字段(camelCase,如 `createTime`) |
|
||||
| `orderType` | string | `desc` | 排序方向(`asc` / `desc`) |
|
||||
| `fields` | string | - | 返回字段(逗号分隔,如 `id,username,status`) |
|
||||
| 其他参数 | - | - | 按 Logic 中 pageLikeFields / pageEqFields 自动匹配 |
|
||||
|
||||
## 请求示例
|
||||
|
||||
```bash
|
||||
# 基本分页
|
||||
GET /api/example/page?pageNo=1&pageSize=10
|
||||
|
||||
# 带模糊搜索
|
||||
GET /api/example/page?pageNo=1&pageSize=10&name=测试
|
||||
|
||||
# 带精确过滤
|
||||
GET /api/example/page?pageNo=1&pageSize=10&status=1&type=2
|
||||
|
||||
# 自定义排序
|
||||
GET /api/example/page?pageNo=1&pageSize=10&orderField=sort&orderType=asc
|
||||
|
||||
# 指定返回字段
|
||||
GET /api/example/page?pageNo=1&pageSize=10&fields=id,name,status
|
||||
```text
|
||||
请求:pageNo(默认 1)/ pageSize(默认 10,上限 500)
|
||||
响应:records / total / size / current / pages
|
||||
```
|
||||
|
||||
## 返回结构
|
||||
## 服务端规约
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"ok": true,
|
||||
"msg": "操作成功",
|
||||
"data": {
|
||||
"records": [
|
||||
{ "id": 1, "name": "测试", "status": 1, "statusText": "启用" }
|
||||
],
|
||||
"total": 100,
|
||||
"size": 10,
|
||||
"current": 1,
|
||||
"pages": 10
|
||||
}
|
||||
}
|
||||
```
|
||||
- 请求结构内嵌 `dto.PageReq`,不手写 pageNo/pageSize 字段
|
||||
- 分页经 ModuleSpec.Page 委托(自动兜底与组壳)
|
||||
- records 空时输出 `[]`(NewPageData 保证)
|
||||
- 深翻页由 pageSize 上限兜底;特殊大导出走 Export 而非超大分页
|
||||
|
||||
## 参数转换
|
||||
## 前端规约
|
||||
|
||||
前端使用 camelCase,后端自动转换为 snake_case。排除项(pageNo、pageSize、orderField、orderType、fields)不参与转换。详见 [横切机制 - 驼峰/下划线转换](/zh/architecture/cross-cutting.html)。
|
||||
- 分页组件绑定 pageNo/pageSize,变更即重查
|
||||
- 筛选变化回到第 1 页
|
||||
- 判空按 `records.length`(不判 null)
|
||||
|
||||
## 排序字段
|
||||
## List(全量)的边界
|
||||
|
||||
| 前端传参 | 后端处理 | SQL |
|
||||
|---------|---------|-----|
|
||||
| `orderField=createTime&orderType=desc` | 转为 `create_time` | `ORDER BY create_time DESC` |
|
||||
| 不传 | 使用 Logic 默认排序 | 按 `pageOrderBy` 配置 |
|
||||
下拉数据源等小数据量用 List + limit 上限;**任何面向页面表格的数据必须走 Page**。导出用 Export(服务端流式写出),不以"大 pageSize 分页"替代。
|
||||
|
||||
## 反例
|
||||
|
||||
| 反例 | 问题 |
|
||||
|---|---|
|
||||
| 自造参数 offset/limit/size 混用 | 与基座、生成器、前端组件不兼容 |
|
||||
| 响应只回数组不回 total | 前端无法渲染分页条 |
|
||||
| pageSize 不设上限 | 一次拉爆内存 |
|
||||
|
||||
## 相关
|
||||
|
||||
- [8.3 分页查询规范](/zh/api-spec/pagination.html)
|
||||
|
||||
Reference in New Issue
Block a user