# 评查点分组管理 API 文档 v3 > **版本**: v3 > **路由前缀**: `/api/v3/evaluation-point-groups` > **数据库表**: `evaluation_point_groups` > **认证方式**: JWT Bearer Token --- ## 📋 目录 1. [数据模型](#数据模型) 2. [查询接口](#查询接口) 3. [创建接口](#创建接口) 4. [更新接口](#更新接口) 5. [删除接口](#删除接口) 6. [批量操作接口](#批量操作接口) 7. [错误响应](#错误响应) 8. [使用示例](#使用示例) --- ## 数据模型 ### 数据库表结构 (`evaluation_point_groups`) | 字段名 | 类型 | 约束 | 默认值 | 说明 | |--------|------|------|--------|------| | `id` | INTEGER | PRIMARY KEY | auto_increment | 分组ID(自增主键) | | `pid` | INTEGER | NULLABLE | NULL | 父分组ID(NULL/0 表示一级分组) | | `code` | VARCHAR(50) | NOT NULL | - | 分组编码(唯一标识) | | `name` | VARCHAR(100) | NOT NULL | - | 分组名称 | | `description` | TEXT | NULLABLE | NULL | 分组描述 | | `is_enabled` | BOOLEAN | NOT NULL | true | 启用状态 | | `created_at` | TIMESTAMPTZ | NOT NULL | now() | 创建时间 | | `updated_at` | TIMESTAMPTZ | NOT NULL | now() | 更新时间 | ### Pydantic 数据模型 #### `EvaluationPointGroupBase` - 基础模型 ```python class EvaluationPointGroupBase(BaseModel): name: str = Field(..., min_length=1, max_length=100, description="分组名称") code: str = Field(..., min_length=1, max_length=50, pattern=r"^[a-zA-Z0-9_-]+$", description="分组编码") pid: Optional[int] = Field(None, description="父分组ID (NULL/0表示一级分组)") description: Optional[str] = Field(None, description="分组描述") is_enabled: bool = Field(True, description="启用状态") ``` #### `EvaluationPointGroupCreate` - 创建请求模型 ```python class EvaluationPointGroupCreate(EvaluationPointGroupBase): pass ``` #### `EvaluationPointGroupUpdate` - 更新请求模型 ```python class EvaluationPointGroupUpdate(BaseModel): name: Optional[str] = Field(None, min_length=1, max_length=100) code: Optional[str] = Field(None, min_length=1, max_length=50, pattern=r"^[a-zA-Z0-9_-]+$") pid: Optional[int] = None description: Optional[str] = None is_enabled: Optional[bool] = None ``` #### `EvaluationPointGroupResponse` - 响应模型 ```python class EvaluationPointGroupResponse(EvaluationPointGroupBase): id: int created_at: datetime updated_at: datetime rule_count: Optional[int] = Field(None, description="评查点数量(子分组查询时返回)") children: Optional[List['EvaluationPointGroupResponse']] = Field(None, description="子分组列表") class Config: from_attributes = True ``` #### `EvaluationPointGroupListResponse` - 列表响应模型 ```python class EvaluationPointGroupListResponse(BaseModel): data: List[EvaluationPointGroupResponse] total: int page: int page_size: int ``` --- ## 查询接口 ### 1. 获取一级分组列表 **接口**: `GET /api/v3/evaluation-point-groups` **功能**: 获取所有一级分组(pid = 0 或 NULL) **请求参数** (Query): | 参数名 | 类型 | 必填 | 默认值 | 说明 | |--------|------|------|--------|------| | `page` | int | 否 | 1 | 页码 | | `page_size` | int | 否 | 20 | 每页数量(最大100) | | `name` | str | 否 | - | 分组名称(模糊搜索) | | `code` | str | 否 | - | 分组编码(模糊搜索) | | `is_enabled` | bool | 否 | - | 启用状态筛选 | **响应示例**: ```json { "data": [ { "id": 1, "pid": null, "name": "合同基本要素", "code": "contract-basic", "description": "合同基本信息检查", "is_enabled": true, "created_at": "2024-01-01T10:00:00Z", "updated_at": "2024-01-01T10:00:00Z", "rule_count": null, "children": null } ], "total": 10, "page": 1, "page_size": 20 } ``` **SQL 等价**: ```sql SELECT * FROM evaluation_point_groups WHERE (pid = 0 OR pid IS NULL) AND name ILIKE '%合同%' -- 可选 AND code ILIKE '%basic%' -- 可选 AND is_enabled = true -- 可选 ORDER BY created_at DESC LIMIT 20 OFFSET 0; ``` --- ### 2. 获取所有分组(树形结构) **接口**: `GET /api/v3/evaluation-point-groups/all` **功能**: 获取所有分组并构建父子树形结构 **请求参数** (Query): | 参数名 | 类型 | 必填 | 默认值 | 说明 | |--------|------|------|--------|------| | `include_disabled` | bool | 否 | false | 是否包含禁用的分组 | | `with_rule_count` | bool | 否 | true | 是否返回评查点数量 | **响应示例**: ```json { "data": [ { "id": 1, "pid": null, "name": "合同基本要素", "code": "contract-basic", "description": "合同基本信息检查", "is_enabled": true, "created_at": "2024-01-01T10:00:00Z", "updated_at": "2024-01-01T10:00:00Z", "rule_count": 15, "children": [ { "id": 2, "pid": 1, "name": "合同主体信息", "code": "contract-subject", "description": "检查合同主体信息", "is_enabled": true, "created_at": "2024-01-01T11:00:00Z", "updated_at": "2024-01-01T11:00:00Z", "rule_count": 5, "children": null } ] } ], "total": 25, "page": 1, "page_size": 1000 } ``` **处理逻辑**: 1. 查询所有分组(可选过滤禁用状态) 2. 筛选一级分组(pid = NULL 或 0) 3. 为每个一级分组查找子分组(pid = parent.id) 4. 如果 `with_rule_count=true`,查询每个分组的评查点数量 --- ### 3. 获取单个分组详情 **接口**: `GET /api/v3/evaluation-point-groups/{id}` **功能**: 根据 ID 获取分组详情 **路径参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | `id` | int | 是 | 分组ID | **查询参数** (Query): | 参数名 | 类型 | 必填 | 默认值 | 说明 | |--------|------|------|--------|------| | `with_rule_count` | bool | 否 | true | 是否返回评查点数量 | **响应示例**: ```json { "id": 1, "pid": null, "name": "合同基本要素", "code": "contract-basic", "description": "合同基本信息检查", "is_enabled": true, "created_at": "2024-01-01T10:00:00Z", "updated_at": "2024-01-01T10:00:00Z", "rule_count": 15, "children": null } ``` **错误响应** (404): ```json { "detail": "评查点分组不存在" } ``` --- ### 4. 获取子分组列表 **接口**: `GET /api/v3/evaluation-point-groups/{parent_id}/children` **功能**: 获取指定父分组下的所有子分组,并附带评查点数量 **路径参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | `parent_id` | int | 是 | 父分组ID | **查询参数** (Query): | 参数名 | 类型 | 必填 | 默认值 | 说明 | |--------|------|------|--------|------| | `page` | int | 否 | 1 | 页码 | | `page_size` | int | 否 | 20 | 每页数量 | | `is_enabled` | bool | 否 | - | 启用状态筛选 | **响应示例**: ```json { "data": [ { "id": 2, "pid": 1, "name": "合同主体信息", "code": "contract-subject", "description": "检查合同主体信息", "is_enabled": true, "created_at": "2024-01-01T11:00:00Z", "updated_at": "2024-01-01T11:00:00Z", "rule_count": 5, "children": null } ], "total": 5, "page": 1, "page_size": 20 } ``` **SQL 等价**: ```sql -- 查询子分组 SELECT * FROM evaluation_point_groups WHERE pid = 1 AND is_enabled = true -- 可选 ORDER BY created_at DESC LIMIT 20 OFFSET 0; -- 查询每个子分组的评查点数量 SELECT COUNT(*) FROM evaluation_points WHERE evaluation_point_groups_id = 2; ``` --- ## 创建接口 ### 5. 创建评查点分组 **接口**: `POST /api/v3/evaluation-point-groups` **功能**: 创建新的评查点分组(一级或二级) **请求头**: ``` Authorization: Bearer Content-Type: application/json ``` **请求体**: ```json { "name": "合同主体信息", "code": "contract-subject", "pid": 1, "description": "检查合同主体信息的完整性", "is_enabled": true } ``` **字段说明**: - `name`: 必填,1-100字符 - `code`: 必填,1-50字符,只能包含字母、数字、连字符和下划线 - `pid`: 可选,NULL/0 表示一级分组,其他值为父分组ID - `description`: 可选,分组描述 - `is_enabled`: 可选,默认 true **字段验证规则**: 1. `name` 和 `code` 不能为空 2. `code` 必须唯一(数据库约束) 3. `code` 格式:`^[a-zA-Z0-9_-]+$` 4. 如果 `pid` 不为 NULL/0,必须引用存在的分组ID **响应示例** (201 Created): ```json { "id": 3, "pid": 1, "name": "合同主体信息", "code": "contract-subject", "description": "检查合同主体信息的完整性", "is_enabled": true, "created_at": "2024-01-15T14:30:00Z", "updated_at": "2024-01-15T14:30:00Z", "rule_count": 0, "children": null } ``` **错误响应** (400): ```json { "detail": "分组编码已存在" } ``` **错误响应** (404): ```json { "detail": "父分组不存在" } ``` --- ## 更新接口 ### 6. 更新评查点分组 **接口**: `PUT /api/v3/evaluation-point-groups/{id}` **功能**: 更新指定分组的信息 **路径参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | `id` | int | 是 | 分组ID | **请求头**: ``` Authorization: Bearer Content-Type: application/json ``` **请求体** (Partial Update): ```json { "name": "合同主体信息(更新)", "code": "contract-subject-v2", "description": "更新后的描述", "is_enabled": false } ``` **字段说明**: - 所有字段均为可选 - 只更新提供的字段 - `updated_at` 自动更新为当前时间 **响应示例** (200 OK): ```json { "id": 3, "pid": 1, "name": "合同主体信息(更新)", "code": "contract-subject-v2", "description": "更新后的描述", "is_enabled": false, "created_at": "2024-01-15T14:30:00Z", "updated_at": "2024-01-15T16:00:00Z", "rule_count": 5, "children": null } ``` **错误响应** (404): ```json { "detail": "评查点分组不存在" } ``` **错误响应** (400): ```json { "detail": "分组编码已被其他分组使用" } ``` --- ## 删除接口 ### 7. 删除评查点分组 **接口**: `DELETE /api/v3/evaluation-point-groups/{id}` **功能**: 删除指定分组(级联删除子分组和评查点) **路径参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | `id` | int | 是 | 分组ID | **请求头**: ``` Authorization: Bearer ``` **删除逻辑**: 1. 查询分组信息,判断是一级还是二级分组 2. 如果是一级分组: - 查询所有子分组 - 删除每个子分组的评查点(`evaluation_points` 表) - 删除所有子分组 3. 删除当前分组的评查点 4. 删除当前分组 **响应示例** (200 OK): ```json { "success": true, "message": "评查点分组删除成功", "deleted_groups": 3, "deleted_points": 15 } ``` **错误响应** (404): ```json { "detail": "评查点分组不存在" } ``` **SQL 执行顺序** (伪代码): ```sql -- 1. 查询分组信息 SELECT * FROM evaluation_point_groups WHERE id = ?; -- 2. 如果是一级分组,查询所有子分组 SELECT * FROM evaluation_point_groups WHERE pid = ?; -- 3. 删除所有子分组的评查点 DELETE FROM evaluation_points WHERE evaluation_point_groups_id IN ( SELECT id FROM evaluation_point_groups WHERE pid = ? ); -- 4. 删除所有子分组 DELETE FROM evaluation_point_groups WHERE pid = ?; -- 5. 删除当前分组的评查点 DELETE FROM evaluation_points WHERE evaluation_point_groups_id = ?; -- 6. 删除当前分组 DELETE FROM evaluation_point_groups WHERE id = ?; ``` --- ## 批量操作接口 ### 8. 批量更新启用状态 **接口**: `PATCH /api/v3/evaluation-point-groups/batch/status` **功能**: 批量更新多个分组的启用状态 **请求头**: ``` Authorization: Bearer Content-Type: application/json ``` **请求体**: ```json { "ids": [1, 2, 3], "is_enabled": false } ``` **响应示例** (200 OK): ```json { "success": true, "updated_count": 3, "message": "批量更新成功" } ``` --- ### 9. 批量删除分组 **接口**: `DELETE /api/v3/evaluation-point-groups/batch` **功能**: 批量删除多个分组(级联删除) **请求头**: ``` Authorization: Bearer Content-Type: application/json ``` **请求体**: ```json { "ids": [1, 2, 3] } ``` **响应示例** (200 OK): ```json { "success": true, "deleted_groups": 5, "deleted_points": 25, "message": "批量删除成功" } ``` --- ## 错误响应 ### 标准错误响应格式 ```json { "detail": "错误描述信息" } ``` ### 常见错误码 | HTTP 状态码 | 错误场景 | 示例 | |------------|---------|------| | 400 | 请求参数验证失败 | `{"detail": "分组名称不能为空"}` | | 401 | 未授权(JWT无效) | `{"detail": "未授权访问"}` | | 404 | 资源不存在 | `{"detail": "评查点分组不存在"}` | | 409 | 资源冲突 | `{"detail": "分组编码已存在"}` | | 422 | 数据验证失败 | `{"detail": [{"loc": ["body", "code"], "msg": "格式不正确"}]}` | | 500 | 服务器内部错误 | `{"detail": "服务器错误,请稍后重试"}` | --- ## 使用示例 ### 示例 1: 获取一级分组(带分页和筛选) **请求**: ```http GET /api/v3/evaluation-point-groups?page=1&page_size=10&name=合同&is_enabled=true Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... ``` **响应**: ```json { "data": [ { "id": 1, "pid": null, "name": "合同基本要素", "code": "contract-basic", "description": "合同基本信息检查", "is_enabled": true, "created_at": "2024-01-01T10:00:00Z", "updated_at": "2024-01-01T10:00:00Z", "rule_count": null, "children": null } ], "total": 1, "page": 1, "page_size": 10 } ``` --- ### 示例 2: 创建二级分组 **请求**: ```http POST /api/v3/evaluation-point-groups Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... Content-Type: application/json { "name": "合同主体信息", "code": "contract-subject", "pid": 1, "description": "检查合同主体信息的完整性", "is_enabled": true } ``` **响应**: ```json { "id": 3, "pid": 1, "name": "合同主体信息", "code": "contract-subject", "description": "检查合同主体信息的完整性", "is_enabled": true, "created_at": "2024-01-15T14:30:00Z", "updated_at": "2024-01-15T14:30:00Z", "rule_count": 0, "children": null } ``` --- ### 示例 3: 获取树形结构 **请求**: ```http GET /api/v3/evaluation-point-groups/all?include_disabled=false&with_rule_count=true Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... ``` **响应**: ```json { "data": [ { "id": 1, "pid": null, "name": "合同基本要素", "code": "contract-basic", "description": "合同基本信息检查", "is_enabled": true, "created_at": "2024-01-01T10:00:00Z", "updated_at": "2024-01-01T10:00:00Z", "rule_count": 15, "children": [ { "id": 2, "pid": 1, "name": "合同主体信息", "code": "contract-subject", "description": "检查合同主体信息", "is_enabled": true, "created_at": "2024-01-01T11:00:00Z", "updated_at": "2024-01-01T11:00:00Z", "rule_count": 5, "children": null } ] } ], "total": 25, "page": 1, "page_size": 1000 } ``` --- ### 示例 4: 批量更新状态 **请求**: ```http PATCH /api/v3/evaluation-point-groups/batch/status Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... Content-Type: application/json { "ids": [1, 2, 3], "is_enabled": false } ``` **响应**: ```json { "success": true, "updated_count": 3, "message": "批量更新成功" } ``` --- ### 示例 5: 删除分组(级联删除) **请求**: ```http DELETE /api/v3/evaluation-point-groups/1 Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... ``` **响应**: ```json { "success": true, "message": "评查点分组删除成功", "deleted_groups": 3, "deleted_points": 15 } ``` --- ## 附录 ### 性能优化建议 1. **查询优化**: - 为 `pid`、`code`、`is_enabled` 字段创建索引 - 使用分页避免一次性加载大量数据 - 树形结构查询时,使用递归查询或批量查询减少数据库往返 2. **缓存策略**: - 对不常变化的分组列表进行缓存(Redis) - 缓存键格式: `eval_groups:all`, `eval_groups:{id}` - 创建/更新/删除操作时清除相关缓存 3. **批量操作**: - 使用批量查询减少数据库连接开销 - 使用事务确保批量操作的原子性 ### 数据库索引建议 ```sql -- 为常用查询字段创建索引 CREATE INDEX idx_evaluation_point_groups_pid ON evaluation_point_groups(pid); CREATE INDEX idx_evaluation_point_groups_code ON evaluation_point_groups(code); CREATE INDEX idx_evaluation_point_groups_is_enabled ON evaluation_point_groups(is_enabled); CREATE INDEX idx_evaluation_point_groups_created_at ON evaluation_point_groups(created_at DESC); -- 为外键创建索引(提升关联查询性能) CREATE INDEX idx_evaluation_points_group_id ON evaluation_points(evaluation_point_groups_id); CREATE INDEX idx_evaluation_points_parent_group_id ON evaluation_points(evaluation_point_groups_pid); ``` ### 与 PostgREST 前端实现的对比 | 功能 | PostgREST 前端实现 | FastAPI 后端实现 | |------|-------------------|-----------------| | 认证方式 | 前端传递 JWT | 后端验证 JWT | | 分页 | 前端手动实现 | 后端自动分页 | | 树形结构 | 前端构建 | 后端构建(可选) | | 评查点数量 | 前端并发查询 | 后端一次性返回 | | 级联删除 | 前端多次调用 | 后端事务处理 | | 数据验证 | 前端验证 | 前后端双重验证 | | 错误处理 | 前端解析错误 | 后端统一错误格式 | --- **文档版本**: v1.0 **最后更新**: 2025-01-21 **维护者**: DocAuditAI Team