黑盒测试用例
This commit is contained in:
570
docs/swagger-blackbox-test-cases.md
Normal file
570
docs/swagger-blackbox-test-cases.md
Normal file
@@ -0,0 +1,570 @@
|
||||
# label-backend Swagger 接口黑盒测试用例
|
||||
|
||||
## 1. 文档说明
|
||||
|
||||
- 生成时间:2026-04-14
|
||||
- 适用项目:`label-backend`
|
||||
- 覆盖范围:当前 `controller` 中已开放的全部 Swagger/OpenAPI 接口
|
||||
- 生成依据:
|
||||
- `src/main/java/com/label/controller/**/*.java`
|
||||
- `src/main/java/com/label/service/**/*.java`
|
||||
- `src/main/resources/sql/init.sql`
|
||||
- `src/test/java/com/label/integration/**/*.java`
|
||||
- 说明:
|
||||
- 当前会话内未能直接访问 `http://127.0.0.1:8080/v3/api-docs`,本文档按代码、设计和现有集成测试反推生成。
|
||||
- 文中“预期结果”以黑盒测试应验证的业务契约为主;若当前实现存在契约空洞,用例应保留,用于发现缺陷。
|
||||
|
||||
## 2. 测试前提
|
||||
|
||||
### 2.1 基础环境
|
||||
|
||||
- Base URL:`http://127.0.0.1:8080`
|
||||
- OpenAPI:`GET /v3/api-docs`
|
||||
- Swagger UI:`GET /swagger-ui.html`
|
||||
- 默认返回体:`{ "code": "...", "message": "...", "data": ... }`
|
||||
|
||||
### 2.2 认证前提
|
||||
|
||||
- 若要执行认证、鉴权、越权、Token 失效类用例,建议运行时配置 `auth.enabled=true`。
|
||||
- 若当前运行环境仍为 `auth.enabled=false` 的 mock 模式,则以下用例中所有 `401/403` 校验需要在真实认证模式下执行。
|
||||
|
||||
### 2.3 种子数据建议
|
||||
|
||||
基于 `src/main/resources/sql/init.sql`,至少准备以下账号:
|
||||
|
||||
| 公司 | 用户名 | 密码 | 角色 |
|
||||
|---|---|---|---|
|
||||
| `DEMO` | `admin` | `admin123` | `ADMIN` |
|
||||
| `DEMO` | `reviewer01` | `review123` | `REVIEWER` |
|
||||
| `DEMO` | `annotator01` | `annot123` | `ANNOTATOR` |
|
||||
| `DEMO` | `uploader01` | `upload123` | `UPLOADER` |
|
||||
|
||||
额外建议准备:
|
||||
|
||||
- 第二家公司 `TESTB`
|
||||
- `TESTB` 下至少 1 个 `ADMIN` 账号
|
||||
- `DEMO`、`TESTB` 各自的资料、任务、配置、导出批次、视频任务样本
|
||||
|
||||
### 2.4 通用 Header
|
||||
|
||||
- JSON 接口:`Content-Type: application/json`
|
||||
- 文件上传:`multipart/form-data`
|
||||
- 受保护接口:`Authorization: Bearer <token>`
|
||||
- 视频回调启用密钥时:`X-Callback-Secret: <VIDEO_CALLBACK_SECRET>`
|
||||
|
||||
## 3. 通用黑盒用例
|
||||
|
||||
除 `POST /api/auth/login` 与 `POST /api/video/callback` 外,所有受保护接口均应复用以下通用用例。
|
||||
|
||||
| 用例ID | 适用范围 | 场景 | 操作 | 预期结果 |
|
||||
|---|---|---|---|---|
|
||||
| `G-AUTH-001` | 全部受保护接口 | 缺少 Token | 不传 `Authorization` | HTTP `401`,`code=UNAUTHORIZED` |
|
||||
| `G-AUTH-002` | 全部受保护接口 | Token 格式错误 | 传 `Authorization: BearerX xxx` 或 `Basic xxx` | HTTP `401`,`code=UNAUTHORIZED` |
|
||||
| `G-AUTH-003` | 全部受保护接口 | Token 无效或过期 | 传不存在/已失效 Token | HTTP `401`,`code=UNAUTHORIZED` |
|
||||
| `G-ROLE-001` | 有角色要求的接口 | 角色不足 | 用低权限账号访问高权限接口 | HTTP `403`,`code=FORBIDDEN` |
|
||||
| `G-TENANT-001` | 租户数据相关接口 | 跨租户访问数据 | 用 `TESTB` Token 访问 `DEMO` 数据 | 返回 `404`、空列表或业务拒绝;不能读到 `DEMO` 数据 |
|
||||
| `G-PAGE-001` | 分页接口 | 大页码限制 | 传超大 `pageSize` | 返回成功,`pageSize` 被限制到系统上限,不出现异常 |
|
||||
| `G-ERR-001` | 全部接口 | 非法请求不能打穿到 5xx | 传缺参/错参/非法状态 | 返回可解释的 `4xx` 或失败业务码,不应无意义 `500` |
|
||||
|
||||
## 4. 认证管理
|
||||
|
||||
### 4.1 `POST /api/auth/login`
|
||||
|
||||
| 用例ID | 场景 | 请求 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `AUTH-LOGIN-001` | 正常登录 | `{"companyCode":"DEMO","username":"admin","password":"admin123"}` | HTTP `200`,`code=SUCCESS`,返回 `token/userId/username/role/expiresIn` |
|
||||
| `AUTH-LOGIN-002` | 密码错误 | `password=wrong` | HTTP `401`,`code=USER_NOT_FOUND` |
|
||||
| `AUTH-LOGIN-003` | 公司代码不存在 | `companyCode=NO_SUCH` | HTTP `401`,`code=USER_NOT_FOUND` |
|
||||
| `AUTH-LOGIN-004` | 账号被禁用 | 先将用户禁用,再登录 | HTTP `403`,`code=USER_DISABLED` |
|
||||
| `AUTH-LOGIN-005` | 缺少必填字段 | 缺 `companyCode`、`username` 或 `password` | 返回失败,不能生成有效 Token,不应出现无提示 `500` |
|
||||
|
||||
### 4.2 `POST /api/auth/logout`
|
||||
|
||||
| 用例ID | 场景 | 操作 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `AUTH-LOGOUT-001` | 正常退出 | 用有效 Token 调用退出 | HTTP `200`,`code=SUCCESS` |
|
||||
| `AUTH-LOGOUT-002` | 退出后 Token 立即失效 | 退出后继续访问 `/api/auth/me` | HTTP `401`,`code=UNAUTHORIZED` |
|
||||
| `AUTH-LOGOUT-003` | 无 Token 退出 | 复用 `G-AUTH-001` | HTTP `401` |
|
||||
|
||||
### 4.3 `GET /api/auth/me`
|
||||
|
||||
| 用例ID | 场景 | 操作 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `AUTH-ME-001` | 查询当前用户信息 | 用 `admin` Token 调用 | HTTP `200`,返回 `id/username/realName/role/companyId/companyName` |
|
||||
| `AUTH-ME-002` | 已退出 Token 查询自己 | 先登录再退出,再调 `/me` | HTTP `401`,`code=UNAUTHORIZED` |
|
||||
| `AUTH-ME-003` | 被禁用账号的旧 Token 查询自己 | 先登录,管理员禁用该用户,再调 `/me` | HTTP `401`,`code=UNAUTHORIZED` |
|
||||
|
||||
## 5. 公司管理
|
||||
|
||||
说明:本组接口复用 `G-AUTH-001~003`、`G-ROLE-001`。
|
||||
|
||||
### 5.1 `GET /api/companies`
|
||||
|
||||
| 用例ID | 场景 | 请求 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `COMPANY-LIST-001` | 管理员分页查询公司 | `?page=1&pageSize=20` | HTTP `200`,返回分页结构 |
|
||||
| `COMPANY-LIST-002` | 按状态筛选 | `?status=ACTIVE` | 仅返回对应状态公司 |
|
||||
| `COMPANY-LIST-003` | 非管理员访问 | 用 `REVIEWER` 或 `UPLOADER` 调用 | HTTP `403`,`code=FORBIDDEN` |
|
||||
|
||||
### 5.2 `POST /api/companies`
|
||||
|
||||
| 用例ID | 场景 | 请求 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `COMPANY-CREATE-001` | 创建公司成功 | `{"companyName":"测试公司B","companyCode":"TESTB"}` | HTTP `201`,创建成功,状态默认 `ACTIVE` |
|
||||
| `COMPANY-CREATE-002` | 公司代码重复 | 使用已存在 `companyCode` | HTTP `409`,`code=DUPLICATE_COMPANY_CODE` |
|
||||
| `COMPANY-CREATE-003` | 公司名称重复 | 使用已存在 `companyName` | HTTP `409`,`code=DUPLICATE_COMPANY_NAME` |
|
||||
| `COMPANY-CREATE-004` | 公司名为空 | `companyName` 空或空白 | HTTP `400`,`code=INVALID_COMPANY_FIELD` |
|
||||
| `COMPANY-CREATE-005` | 公司代码为空 | `companyCode` 空或空白 | HTTP `400`,`code=INVALID_COMPANY_FIELD` |
|
||||
|
||||
### 5.3 `PUT /api/companies/{id}`
|
||||
|
||||
| 用例ID | 场景 | 请求 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `COMPANY-UPDATE-001` | 更新公司成功 | 修改 `companyName/companyCode` | HTTP `200`,字段更新成功 |
|
||||
| `COMPANY-UPDATE-002` | 更新不存在公司 | `id` 不存在 | HTTP `404`,`code=NOT_FOUND` |
|
||||
| `COMPANY-UPDATE-003` | 更新为重复代码 | `companyCode` 改成已存在值 | HTTP `409`,`code=DUPLICATE_COMPANY_CODE` |
|
||||
| `COMPANY-UPDATE-004` | 更新为重复名称 | `companyName` 改成已存在值 | HTTP `409`,`code=DUPLICATE_COMPANY_NAME` |
|
||||
|
||||
### 5.4 `PUT /api/companies/{id}/status`
|
||||
|
||||
| 用例ID | 场景 | 请求 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `COMPANY-STATUS-001` | 禁用公司成功 | `{"status":"DISABLED"}` | HTTP `200`,公司状态变为 `DISABLED` |
|
||||
| `COMPANY-STATUS-002` | 恢复公司成功 | `{"status":"ACTIVE"}` | HTTP `200`,公司状态变为 `ACTIVE` |
|
||||
| `COMPANY-STATUS-003` | 非法状态值 | `{"status":"UNKNOWN"}` | HTTP `400`,`code=INVALID_COMPANY_STATUS` |
|
||||
| `COMPANY-STATUS-004` | 公司不存在 | 不存在 `id` | HTTP `404`,`code=NOT_FOUND` |
|
||||
|
||||
### 5.5 `DELETE /api/companies/{id}`
|
||||
|
||||
| 用例ID | 场景 | 操作 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `COMPANY-DELETE-001` | 删除空公司成功 | 删除无用户的公司 | HTTP `200`,删除成功 |
|
||||
| `COMPANY-DELETE-002` | 公司下仍有用户 | 删除已有用户公司 | HTTP `409`,`code=COMPANY_HAS_USERS` |
|
||||
| `COMPANY-DELETE-003` | 删除不存在公司 | 不存在 `id` | HTTP `404`,`code=NOT_FOUND` |
|
||||
|
||||
## 6. 用户管理
|
||||
|
||||
说明:本组接口复用 `G-AUTH-001~003`、`G-ROLE-001`、`G-TENANT-001`。
|
||||
|
||||
### 6.1 `GET /api/users`
|
||||
|
||||
| 用例ID | 场景 | 请求 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `USER-LIST-001` | 管理员查看本公司用户 | `?page=1&pageSize=20` | HTTP `200`,仅返回当前公司用户 |
|
||||
| `USER-LIST-002` | 跨租户隔离 | 用 `TESTB` 管理员查看列表 | 不出现 `DEMO` 用户 |
|
||||
| `USER-LIST-003` | 非管理员访问 | 用 `REVIEWER` 调用 | HTTP `403` |
|
||||
|
||||
### 6.2 `POST /api/users`
|
||||
|
||||
| 用例ID | 场景 | 请求 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `USER-CREATE-001` | 创建用户成功 | `{"username":"qa01","password":"qa123456","realName":"测试员","role":"ANNOTATOR"}` | HTTP `200`,创建成功,状态默认为 `ACTIVE` |
|
||||
| `USER-CREATE-002` | 用户名重复 | 同公司创建同名用户 | HTTP `409`,`code=DUPLICATE_USERNAME` |
|
||||
| `USER-CREATE-003` | 角色非法 | `role=VIEWER` 或其他未支持角色 | HTTP `400`,`code=INVALID_ROLE` |
|
||||
| `USER-CREATE-004` | 跨租户污染校验 | `TESTB` 管理员创建用户后,`DEMO` 不可见 | 创建成功且仅属于当前公司 |
|
||||
|
||||
### 6.3 `PUT /api/users/{id}`
|
||||
|
||||
| 用例ID | 场景 | 请求 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `USER-UPDATE-001` | 更新真实姓名成功 | `{"realName":"新名字"}` | HTTP `200`,真实姓名更新 |
|
||||
| `USER-UPDATE-002` | 更新密码成功 | `{"password":"newpass123"}`,再重新登录 | 新密码可登录,旧密码失效 |
|
||||
| `USER-UPDATE-003` | 更新不存在用户 | 不存在 `id` | HTTP `404`,`code=NOT_FOUND` |
|
||||
| `USER-UPDATE-004` | 更新他租户用户 | 用 `TESTB` 管理员修改 `DEMO` 用户 | HTTP `404` 或不可见 |
|
||||
|
||||
### 6.4 `PUT /api/users/{id}/status`
|
||||
|
||||
| 用例ID | 场景 | 请求 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `USER-STATUS-001` | 禁用用户成功 | `{"status":"DISABLED"}` | HTTP `200`,用户状态更新成功 |
|
||||
| `USER-STATUS-002` | 禁用后旧 Token 失效 | 禁用后用该用户旧 Token 调 `/api/auth/me` | HTTP `401`,`code=UNAUTHORIZED` |
|
||||
| `USER-STATUS-003` | 恢复用户成功 | `{"status":"ACTIVE"}` | HTTP `200` |
|
||||
| `USER-STATUS-004` | 非法状态值 | `{"status":"LOCKED"}` | HTTP `400`,`code=INVALID_STATUS` |
|
||||
|
||||
### 6.5 `PUT /api/users/{id}/role`
|
||||
|
||||
| 用例ID | 场景 | 请求 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `USER-ROLE-001` | 修改角色成功 | `{"role":"REVIEWER"}` | HTTP `200`,用户角色更新成功 |
|
||||
| `USER-ROLE-002` | 角色变更立即生效 | 同一 Token 变更前访问 reviewer 接口 `403`,变更后重试 | 变更后无需重新登录即可访问成功 |
|
||||
| `USER-ROLE-003` | 非法角色值 | `{"role":"VIEWER"}` | HTTP `400`,`code=INVALID_ROLE` |
|
||||
| `USER-ROLE-004` | 修改不存在用户 | 不存在 `id` | HTTP `404`,`code=NOT_FOUND` |
|
||||
|
||||
## 7. 资料管理
|
||||
|
||||
说明:本组接口复用 `G-AUTH-001~003`、`G-ROLE-001`、`G-TENANT-001`。
|
||||
|
||||
### 7.1 `POST /api/source/upload`
|
||||
|
||||
| 用例ID | 场景 | 请求 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `SOURCE-UPLOAD-001` | 上传文本资料成功 | `multipart/form-data`,传 `file` + `dataType=TEXT` | HTTP `201`,返回资料 `id/fileName/dataType/status` |
|
||||
| `SOURCE-UPLOAD-002` | 上传图片资料成功 | `dataType=IMAGE` | HTTP `201` |
|
||||
| `SOURCE-UPLOAD-003` | 上传视频资料成功 | `dataType=VIDEO` | HTTP `201` |
|
||||
| `SOURCE-UPLOAD-004` | 空文件上传 | 文件为空 | HTTP `400`,`code=FILE_EMPTY` |
|
||||
| `SOURCE-UPLOAD-005` | 不支持的资料类型 | `dataType=PDF` | HTTP `400`,`code=INVALID_TYPE` |
|
||||
| `SOURCE-UPLOAD-006` | Swagger 文案兼容性检查 | `dataType=text` 小写 | 应与文档约定一致;若失败需修正文档或实现 |
|
||||
| `SOURCE-UPLOAD-007` | 低权限无权上传 | 用无上传权限账号访问 | HTTP `403` |
|
||||
|
||||
### 7.2 `GET /api/source/list`
|
||||
|
||||
| 用例ID | 场景 | 请求 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `SOURCE-LIST-001` | 上传员查看自己资料 | 用 `uploader01` 调用 | 仅返回自己上传的数据 |
|
||||
| `SOURCE-LIST-002` | 管理员查看公司全部资料 | 用 `admin` 调用 | 返回公司内全部资料 |
|
||||
| `SOURCE-LIST-003` | 按类型筛选 | `?dataType=TEXT` | 仅返回对应类型 |
|
||||
| `SOURCE-LIST-004` | 按状态筛选 | `?status=PENDING` | 仅返回对应状态 |
|
||||
| `SOURCE-LIST-005` | 跨租户隔离 | `TESTB` 调用 | 看不到 `DEMO` 数据 |
|
||||
|
||||
### 7.3 `GET /api/source/{id}`
|
||||
|
||||
| 用例ID | 场景 | 操作 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `SOURCE-DETAIL-001` | 查看资料详情成功 | 查询本公司资料 | HTTP `200`,返回详情及下载地址 |
|
||||
| `SOURCE-DETAIL-002` | 查询不存在资料 | `id` 不存在 | HTTP `404`,`code=NOT_FOUND` |
|
||||
| `SOURCE-DETAIL-003` | 跨租户查看详情 | 用 `TESTB` Token 查 `DEMO` 资料 | HTTP `404` 或不可见 |
|
||||
|
||||
### 7.4 `DELETE /api/source/{id}`
|
||||
|
||||
| 用例ID | 场景 | 操作 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `SOURCE-DELETE-001` | 删除 `PENDING` 资料成功 | 删除未进入流水线资料 | HTTP `200` |
|
||||
| `SOURCE-DELETE-002` | 删除不存在资料 | `id` 不存在 | HTTP `404`,`code=NOT_FOUND` |
|
||||
| `SOURCE-DELETE-003` | 删除已进入流水线资料 | 状态为 `PREPROCESSING/EXTRACTING/QA_REVIEW/APPROVED` | HTTP `409`,`code=SOURCE_IN_PIPELINE` |
|
||||
| `SOURCE-DELETE-004` | 非管理员删除 | 用 `UPLOADER` 删除资料 | HTTP `403` |
|
||||
|
||||
## 8. 任务管理
|
||||
|
||||
说明:本组接口复用 `G-AUTH-001~003`、`G-ROLE-001`、`G-TENANT-001`。
|
||||
|
||||
### 8.1 `GET /api/tasks/pool`
|
||||
|
||||
| 用例ID | 场景 | 请求 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `TASK-POOL-001` | 标注员查看任务池 | 用 `ANNOTATOR` 调用 | 仅返回 `EXTRACTION + UNCLAIMED` 任务 |
|
||||
| `TASK-POOL-002` | Reviewer/Admin 调用任务池 | 用 `REVIEWER` 或 `ADMIN` 调用 | 返回 `SUBMITTED` 待审任务 |
|
||||
| `TASK-POOL-003` | 跨租户隔离 | `TESTB` 调用 | 仅能看到本公司任务 |
|
||||
|
||||
### 8.2 `GET /api/tasks/mine`
|
||||
|
||||
| 用例ID | 场景 | 请求 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `TASK-MINE-001` | 查询我的任务成功 | `?page=1&pageSize=20` | 返回当前用户的 `IN_PROGRESS/SUBMITTED/REJECTED` 任务 |
|
||||
| `TASK-MINE-002` | 按状态过滤 | `?status=REJECTED` | 仅返回对应状态 |
|
||||
| `TASK-MINE-003` | 未领取任务不应出现在 mine | 准备 `UNCLAIMED` 任务 | 列表中不出现该任务 |
|
||||
|
||||
### 8.3 `GET /api/tasks/pending-review`
|
||||
|
||||
| 用例ID | 场景 | 请求 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `TASK-REVIEW-001` | Reviewer 查询待审批任务 | `?taskType=EXTRACTION` | 仅返回 `SUBMITTED` 且符合类型的任务 |
|
||||
| `TASK-REVIEW-002` | 非 Reviewer 访问 | 用 `ANNOTATOR` 调用 | HTTP `403` |
|
||||
| `TASK-REVIEW-003` | 跨租户隔离 | `TESTB` 调用 | 看不到 `DEMO` 待审任务 |
|
||||
|
||||
### 8.4 `GET /api/tasks`
|
||||
|
||||
| 用例ID | 场景 | 请求 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `TASK-LIST-001` | 管理员查询全部任务 | `?status=SUBMITTED&taskType=QA_GENERATION` | 返回过滤后的本公司任务 |
|
||||
| `TASK-LIST-002` | 非管理员访问 | 用 `REVIEWER` 调用 | HTTP `403` |
|
||||
|
||||
### 8.5 `POST /api/tasks`
|
||||
|
||||
| 用例ID | 场景 | 请求 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `TASK-CREATE-001` | 创建提取任务成功 | `{"sourceId":<id>,"taskType":"EXTRACTION"}` | HTTP `200`,返回新任务,状态 `UNCLAIMED` |
|
||||
| `TASK-CREATE-002` | 创建 QA 任务成功 | `{"sourceId":<id>,"taskType":"QA_GENERATION"}` | HTTP `200` |
|
||||
| `TASK-CREATE-003` | 缺少 `sourceId` | 仅传 `taskType` | 应返回明确失败,不应出现裸 `500` |
|
||||
| `TASK-CREATE-004` | 缺少 `taskType` | 仅传 `sourceId` | 应返回明确失败,不应出现裸 `500` |
|
||||
|
||||
### 8.6 `GET /api/tasks/{id}`
|
||||
|
||||
| 用例ID | 场景 | 操作 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `TASK-DETAIL-001` | 查询任务详情成功 | 查询本公司任务 | HTTP `200`,返回任务详情 |
|
||||
| `TASK-DETAIL-002` | 查询不存在任务 | 不存在 `id` | HTTP `404`,`code=NOT_FOUND` |
|
||||
| `TASK-DETAIL-003` | 跨租户查看任务 | 用 `TESTB` 查询 `DEMO` 任务 | HTTP `404` 或不可见 |
|
||||
|
||||
### 8.7 `POST /api/tasks/{id}/claim`
|
||||
|
||||
| 用例ID | 场景 | 操作 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `TASK-CLAIM-001` | 正常领取未领取任务 | `ANNOTATOR` 领取 `UNCLAIMED` 任务 | HTTP `200`,任务状态变为 `IN_PROGRESS` |
|
||||
| `TASK-CLAIM-002` | 并发抢任务 | 10 并发领取同一任务 | 恰好 1 个成功,其余 HTTP `409`,`code=TASK_CLAIMED` |
|
||||
| `TASK-CLAIM-003` | 重复领取已被占用任务 | 第二个用户再领取 | HTTP `409`,`code=TASK_CLAIMED` |
|
||||
|
||||
### 8.8 `POST /api/tasks/{id}/unclaim`
|
||||
|
||||
| 用例ID | 场景 | 操作 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `TASK-UNCLAIM-001` | 放弃进行中任务成功 | 当前领取人放弃 `IN_PROGRESS` 任务 | HTTP `200`,状态回到 `UNCLAIMED` |
|
||||
| `TASK-UNCLAIM-002` | 非法状态放弃 | 对 `SUBMITTED/REJECTED/APPROVED` 任务调用 | HTTP `409`,`code=INVALID_STATE_TRANSITION` |
|
||||
| `TASK-UNCLAIM-003` | 非领取人放弃他人任务 | 用别的标注员调用 | 应被拒绝,不能让他人释放任务 |
|
||||
|
||||
### 8.9 `POST /api/tasks/{id}/reclaim`
|
||||
|
||||
| 用例ID | 场景 | 操作 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `TASK-RECLAIM-001` | 原领取人重领被驳回任务 | 原领取人对 `REJECTED` 任务调用 | HTTP `200`,状态变为 `IN_PROGRESS` |
|
||||
| `TASK-RECLAIM-002` | 非原领取人重领 | 其他用户调用 | HTTP `403`,`code=FORBIDDEN` |
|
||||
| `TASK-RECLAIM-003` | 非驳回状态重领 | 对 `IN_PROGRESS` 任务调用 | HTTP `409`,`code=INVALID_STATE_TRANSITION` |
|
||||
|
||||
### 8.10 `PUT /api/tasks/{id}/reassign`
|
||||
|
||||
| 用例ID | 场景 | 请求 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `TASK-REASSIGN-001` | 管理员强制指派成功 | `{"userId":<targetUserId>}` | HTTP `200`,任务归属变更到目标用户 |
|
||||
| `TASK-REASSIGN-002` | 指派不存在任务 | 不存在 `id` | HTTP `404`,`code=NOT_FOUND` |
|
||||
| `TASK-REASSIGN-003` | 缺少 `userId` | 空请求体或缺字段 | 应返回明确失败,不应裸 `500` |
|
||||
| `TASK-REASSIGN-004` | 指派后状态一致性 | 指派后查询任务 | 任务应处于可执行状态,归属人与时间被更新 |
|
||||
|
||||
## 9. 提取标注
|
||||
|
||||
说明:本组接口复用 `G-AUTH-001~003`、`G-ROLE-001`、`G-TENANT-001`。
|
||||
|
||||
### 9.1 `GET /api/extraction/{taskId}`
|
||||
|
||||
| 用例ID | 场景 | 操作 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `EXT-GET-001` | 获取提取结果成功 | 查询本公司任务 | HTTP `200`,返回 `taskId/sourceType/sourceFilePath/isFinal/resultJson` |
|
||||
| `EXT-GET-002` | 查询不存在任务 | 不存在 `taskId` | HTTP `404`,`code=NOT_FOUND` |
|
||||
| `EXT-GET-003` | 跨租户查询结果 | 用 `TESTB` 查询 `DEMO` 任务 | HTTP `404` 或不可见 |
|
||||
|
||||
### 9.2 `PUT /api/extraction/{taskId}`
|
||||
|
||||
| 用例ID | 场景 | 请求体 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `EXT-PUT-001` | 更新结果成功 | 传合法 JSON 字符串 | HTTP `200` |
|
||||
| `EXT-PUT-002` | 非法 JSON | 传坏 JSON | HTTP `400`,`code=INVALID_JSON` |
|
||||
| `EXT-PUT-003` | 任务不存在 | 不存在 `taskId` | HTTP `404`,`code=NOT_FOUND` |
|
||||
|
||||
### 9.3 `POST /api/extraction/{taskId}/submit`
|
||||
|
||||
| 用例ID | 场景 | 操作 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `EXT-SUBMIT-001` | 提交成功 | `IN_PROGRESS` 任务提交 | HTTP `200`,任务变为 `SUBMITTED` |
|
||||
| `EXT-SUBMIT-002` | 非法状态提交 | `UNCLAIMED/REJECTED/APPROVED` 时提交 | HTTP `409`,`code=INVALID_STATE_TRANSITION` |
|
||||
|
||||
### 9.4 `POST /api/extraction/{taskId}/approve`
|
||||
|
||||
| 用例ID | 场景 | 操作 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `EXT-APPROVE-001` | 审批通过成功 | Reviewer 审批 `SUBMITTED` 任务 | HTTP `200`,原任务 `APPROVED`,`isFinal=true` |
|
||||
| `EXT-APPROVE-002` | 审批通过触发后续链路 | 审批完成后查数据库/接口 | 自动创建 `QA_GENERATION` 任务,`source_data.status=QA_REVIEW`,创建 `training_dataset` |
|
||||
| `EXT-APPROVE-003` | 自审拦截 | 提交人与审批人相同 | HTTP `403`,`code=SELF_REVIEW_FORBIDDEN` |
|
||||
| `EXT-APPROVE-004` | 非法状态审批 | 未提交任务直接审批 | HTTP `409`,`code=INVALID_STATE_TRANSITION` |
|
||||
|
||||
### 9.5 `POST /api/extraction/{taskId}/reject`
|
||||
|
||||
| 用例ID | 场景 | 请求 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `EXT-REJECT-001` | 驳回成功 | `{"reason":"实体识别有误"}` | HTTP `200`,任务变为 `REJECTED` |
|
||||
| `EXT-REJECT-002` | 驳回原因为空 | 空 body 或空 reason | HTTP `400`,`code=REASON_REQUIRED` |
|
||||
| `EXT-REJECT-003` | 自审驳回 | 提交人与驳回人相同 | HTTP `403`,`code=SELF_REVIEW_FORBIDDEN` |
|
||||
| `EXT-REJECT-004` | 驳回后可重领重提 | 驳回后原领取人调 `/reclaim` 再 `/submit` | 可重领,任务恢复到 `SUBMITTED` |
|
||||
|
||||
## 10. 问答生成
|
||||
|
||||
说明:本组接口复用 `G-AUTH-001~003`、`G-ROLE-001`、`G-TENANT-001`。
|
||||
|
||||
### 10.1 `GET /api/qa/{taskId}`
|
||||
|
||||
| 用例ID | 场景 | 操作 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `QA-GET-001` | 获取候选问答对成功 | 查询本公司 QA 任务 | HTTP `200`,返回 `taskId/sourceType/items` |
|
||||
| `QA-GET-002` | 查询不存在任务 | 不存在 `taskId` | HTTP `404`,`code=NOT_FOUND` |
|
||||
| `QA-GET-003` | 跨租户查询 | 用 `TESTB` 查询 `DEMO` 任务 | HTTP `404` 或不可见 |
|
||||
|
||||
### 10.2 `PUT /api/qa/{taskId}`
|
||||
|
||||
| 用例ID | 场景 | 请求体 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `QA-PUT-001` | 更新候选问答对成功 | `{"items":[...]}` | HTTP `200` |
|
||||
| `QA-PUT-002` | 非法 JSON | 传坏 JSON | HTTP `400`,`code=INVALID_JSON` |
|
||||
| `QA-PUT-003` | 缺失 items 字段 | 传空对象 `{}` | 不应报 5xx;若允许则生成空 conversations |
|
||||
|
||||
### 10.3 `POST /api/qa/{taskId}/submit`
|
||||
|
||||
| 用例ID | 场景 | 操作 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `QA-SUBMIT-001` | 提交成功 | `IN_PROGRESS` 任务提交 | HTTP `200`,任务变为 `SUBMITTED` |
|
||||
| `QA-SUBMIT-002` | 非法状态提交 | 对 `UNCLAIMED/REJECTED/APPROVED` 任务提交 | HTTP `409`,`code=INVALID_STATE_TRANSITION` |
|
||||
|
||||
### 10.4 `POST /api/qa/{taskId}/approve`
|
||||
|
||||
| 用例ID | 场景 | 操作 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `QA-APPROVE-001` | QA 审批通过成功 | Reviewer 审批 `SUBMITTED` QA 任务 | HTTP `200` |
|
||||
| `QA-APPROVE-002` | 审批通过完成整条流水线 | 审批完成后查状态 | `training_dataset.status=APPROVED`,任务 `APPROVED`,`source_data.status=APPROVED` |
|
||||
| `QA-APPROVE-003` | 自审拦截 | 提交人与审批人相同 | HTTP `403`,`code=SELF_REVIEW_FORBIDDEN` |
|
||||
|
||||
### 10.5 `POST /api/qa/{taskId}/reject`
|
||||
|
||||
| 用例ID | 场景 | 请求 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `QA-REJECT-001` | 驳回成功 | `{"reason":"问题描述不准确"}` | HTTP `200`,任务变为 `REJECTED` |
|
||||
| `QA-REJECT-002` | 驳回原因为空 | 空 body 或空 reason | HTTP `400`,`code=REASON_REQUIRED` |
|
||||
| `QA-REJECT-003` | 驳回后候选数据删除 | 驳回后检查 `training_dataset` | 候选问答对被删除,`source_data` 维持 `QA_REVIEW` |
|
||||
| `QA-REJECT-004` | 驳回后可重领重提 | 原领取人调 `/reclaim` 再 `/submit` | 可重领,重新提交成功 |
|
||||
|
||||
## 11. 导出与微调
|
||||
|
||||
说明:本组接口复用 `G-AUTH-001~003`、`G-ROLE-001`、`G-TENANT-001`。
|
||||
|
||||
### 11.1 `GET /api/training/samples`
|
||||
|
||||
| 用例ID | 场景 | 请求 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `EXPORT-SAMPLE-001` | 查询已审批样本成功 | `?page=1&pageSize=20` | HTTP `200`,返回 `APPROVED` 样本 |
|
||||
| `EXPORT-SAMPLE-002` | 仅看未导出样本 | `?exported=false` | 只返回 `export_batch_id` 为空的数据 |
|
||||
| `EXPORT-SAMPLE-003` | 按样本类型过滤 | `?sampleType=TEXT` | 仅返回对应类型 |
|
||||
| `EXPORT-SAMPLE-004` | 非管理员访问 | 用 `ANNOTATOR` 调用 | HTTP `403` |
|
||||
|
||||
### 11.2 `POST /api/export/batch`
|
||||
|
||||
| 用例ID | 场景 | 请求 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `EXPORT-BATCH-001` | 创建导出批次成功 | `{"sampleIds":[<approvedId1>,<approvedId2>]}` | HTTP `201`,返回批次信息 |
|
||||
| `EXPORT-BATCH-002` | 样本列表为空 | `{"sampleIds":[]}` | HTTP `400`,`code=EMPTY_SAMPLES` |
|
||||
| `EXPORT-BATCH-003` | 包含未审批样本 | 混入 `PENDING_REVIEW/REJECTED` 样本 | HTTP `400`,`code=INVALID_SAMPLES` |
|
||||
| `EXPORT-BATCH-004` | 包含他租户样本 | 混入其他公司样本 ID | HTTP `400`,`code=INVALID_SAMPLES` |
|
||||
| `EXPORT-BATCH-005` | 批次创建后回写样本导出信息 | 创建成功后查询样本 | `exportBatchId/exportedAt` 已写入 |
|
||||
|
||||
### 11.3 `POST /api/export/{batchId}/finetune`
|
||||
|
||||
| 用例ID | 场景 | 操作 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `FINETUNE-TRIGGER-001` | 首次触发微调成功 | 对 `NOT_STARTED` 批次调用 | HTTP `200`,返回 `glmJobId/finetuneStatus=RUNNING` |
|
||||
| `FINETUNE-TRIGGER-002` | 重复触发微调 | 对已启动批次再次调用 | HTTP `409`,`code=FINETUNE_ALREADY_STARTED` |
|
||||
| `FINETUNE-TRIGGER-003` | 触发不存在批次 | 不存在 `batchId` | HTTP `404`,`code=NOT_FOUND` |
|
||||
|
||||
### 11.4 `GET /api/export/{batchId}/status`
|
||||
|
||||
| 用例ID | 场景 | 操作 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `FINETUNE-STATUS-001` | 未启动批次查询状态 | `glmJobId` 为空批次 | HTTP `200`,返回 `NOT_STARTED/progress=0` |
|
||||
| `FINETUNE-STATUS-002` | 已启动批次查询状态 | 对已提交微调任务批次调用 | HTTP `200`,返回 `batchId/glmJobId/finetuneStatus/progress/errorMessage` |
|
||||
| `FINETUNE-STATUS-003` | 跨租户状态隔离 | 用 `TESTB` 查 `DEMO` 批次 | HTTP `404` 或不可见 |
|
||||
|
||||
### 11.5 `GET /api/export/list`
|
||||
|
||||
| 用例ID | 场景 | 请求 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `EXPORT-LIST-001` | 分页查询导出批次成功 | `?page=1&pageSize=20` | HTTP `200`,仅返回当前公司批次 |
|
||||
| `EXPORT-LIST-002` | 跨租户隔离 | `TESTB` 调用 | 看不到 `DEMO` 批次 |
|
||||
|
||||
## 12. 系统配置
|
||||
|
||||
说明:本组接口复用 `G-AUTH-001~003`、`G-ROLE-001`、`G-TENANT-001`。
|
||||
|
||||
### 12.1 `GET /api/config`
|
||||
|
||||
| 用例ID | 场景 | 操作 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `CONFIG-LIST-001` | 查询合并配置成功 | 管理员查询 | HTTP `200`,返回当前公司可见配置列表 |
|
||||
| `CONFIG-LIST-002` | 公司配置覆盖全局默认 | 先写公司专属 `model_default`,再查询 | 返回 `scope=COMPANY` 且值为公司专属值 |
|
||||
| `CONFIG-LIST-003` | 未配置公司专属时回退全局 | 清除公司专属后查询 | 返回 `scope=GLOBAL` |
|
||||
| `CONFIG-LIST-004` | 跨租户隔离 | `DEMO` 配置不影响 `TESTB` | 另一公司仍看到自己的配置或全局默认 |
|
||||
|
||||
### 12.2 `PUT /api/config/{key}`
|
||||
|
||||
| 用例ID | 场景 | 请求 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `CONFIG-UPSERT-001` | 新增公司专属配置成功 | `{"value":"glm-4-plus","description":"默认模型"}` | HTTP `200`,创建成功 |
|
||||
| `CONFIG-UPSERT-002` | 更新同键配置成功 | 同一个 `key` 连续两次 `PUT` | HTTP `200`,第二次为更新而不是重复插入 |
|
||||
| `CONFIG-UPSERT-003` | 未知配置键 | `PUT /api/config/unknown_key` | HTTP `400`,`code=UNKNOWN_CONFIG_KEY` |
|
||||
| `CONFIG-UPSERT-004` | 空配置值 | `{"value":""}` | HTTP `400`,`code=INVALID_CONFIG_VALUE` |
|
||||
|
||||
## 13. 视频处理
|
||||
|
||||
说明:
|
||||
|
||||
- `POST /api/video/callback` 为公开接口,不复用 `G-AUTH-001~003`
|
||||
- 其余视频接口复用 `G-AUTH-001~003`、`G-ROLE-001`、`G-TENANT-001`
|
||||
|
||||
### 13.1 `POST /api/video/process`
|
||||
|
||||
| 用例ID | 场景 | 请求 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `VIDEO-CREATE-001` | 创建视频处理任务成功 | `{"sourceId":<videoSourceId>,"jobType":"FRAME_EXTRACT","params":"{\"frameInterval\":30}"}` | 创建成功,返回 job,资料状态进入 `PREPROCESSING` |
|
||||
| `VIDEO-CREATE-002` | 使用另一种任务类型成功 | `jobType=VIDEO_TO_TEXT` | 创建成功 |
|
||||
| `VIDEO-CREATE-003` | 缺少必要字段 | 缺 `sourceId` 或 `jobType` | 返回失败,`code=INVALID_PARAMS`,且不能创建 job |
|
||||
| `VIDEO-CREATE-004` | 非法任务类型 | `jobType=UNKNOWN` | HTTP `400`,`code=INVALID_JOB_TYPE` |
|
||||
| `VIDEO-CREATE-005` | 非视频资料创建视频任务 | 对非 `VIDEO` 资料调用 | 应返回明确失败,不应产生错误状态迁移 |
|
||||
| `VIDEO-CREATE-006` | 跨租户创建视频任务 | 用 `TESTB` 处理 `DEMO` 资料 | HTTP `404` 或不可见 |
|
||||
|
||||
### 13.2 `GET /api/video/jobs/{jobId}`
|
||||
|
||||
| 用例ID | 场景 | 操作 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `VIDEO-GET-001` | 查询视频任务成功 | 查询本公司 `jobId` | HTTP `200`,返回任务状态、重试次数、输出路径等 |
|
||||
| `VIDEO-GET-002` | 查询不存在任务 | 不存在 `jobId` | HTTP `404`,`code=NOT_FOUND` |
|
||||
| `VIDEO-GET-003` | 跨租户查询 | 用 `TESTB` 查 `DEMO` 任务 | HTTP `404` 或不可见 |
|
||||
|
||||
### 13.3 `POST /api/video/jobs/{jobId}/reset`
|
||||
|
||||
| 用例ID | 场景 | 操作 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `VIDEO-RESET-001` | 重置失败任务成功 | 对 `FAILED` 任务调用 | HTTP `200`,job 状态变为 `PENDING`,`retryCount=0` |
|
||||
| `VIDEO-RESET-002` | 非失败任务不允许重置 | 对 `PENDING/RETRYING/SUCCESS` 调用 | HTTP `400`,`code=INVALID_TRANSITION` |
|
||||
| `VIDEO-RESET-003` | 跨租户重置 | 用 `TESTB` 调 `DEMO` 任务 | HTTP `404` 或不可见 |
|
||||
|
||||
### 13.4 `POST /api/video/callback`
|
||||
|
||||
| 用例ID | 场景 | 请求 | 预期结果 |
|
||||
|---|---|---|---|
|
||||
| `VIDEO-CALLBACK-001` | SUCCESS 回调成功 | `{"jobId":123,"status":"SUCCESS","outputPath":"processed/frames.zip"}` | HTTP `200`,job 变 `SUCCESS`,`source_data.status=PENDING` |
|
||||
| `VIDEO-CALLBACK-002` | SUCCESS 回调幂等 | 对同一 `jobId` 连续两次发送相同 SUCCESS 回调 | 两次都成功,第二次不重复改状态、不重复产出副作用 |
|
||||
| `VIDEO-CALLBACK-003` | FAILED 回调触发重试 | 对未达重试上限 job 发 `FAILED` | job 变 `RETRYING`,`retryCount+1` |
|
||||
| `VIDEO-CALLBACK-004` | 超过最大重试次数 | `retryCount=maxRetries-1` 时再发 `FAILED` | job 变 `FAILED`,`source_data.status=PENDING` |
|
||||
| `VIDEO-CALLBACK-005` | 回调 job 不存在 | `jobId` 不存在 | 接口不应打穿服务;应安全返回,无脏数据写入 |
|
||||
| `VIDEO-CALLBACK-006` | 启用共享密钥时密钥错误 | 不传或传错 `X-Callback-Secret` | 返回失败,`code=UNAUTHORIZED` |
|
||||
| `VIDEO-CALLBACK-007` | 回调缺少 `jobId/status` | 缺关键字段 | 返回明确失败,不应裸 `500` |
|
||||
|
||||
## 14. 推荐执行顺序
|
||||
|
||||
建议按以下顺序执行,能更快定位问题来源:
|
||||
|
||||
1. 认证管理
|
||||
2. 公司管理
|
||||
3. 用户管理
|
||||
4. 资料管理
|
||||
5. 任务管理
|
||||
6. 提取标注
|
||||
7. 问答生成
|
||||
8. 导出与微调
|
||||
9. 系统配置
|
||||
10. 视频处理
|
||||
|
||||
## 15. 高风险回归点
|
||||
|
||||
每次版本回归至少覆盖以下高风险用例:
|
||||
|
||||
| 优先级 | 用例ID | 说明 |
|
||||
|---|---|---|
|
||||
| P0 | `AUTH-LOGIN-001` | 登录主链路 |
|
||||
| P0 | `AUTH-LOGOUT-002` | 退出后 Token 立即失效 |
|
||||
| P0 | `USER-STATUS-002` | 禁用账号后旧 Token 失效 |
|
||||
| P0 | `SOURCE-DELETE-003` | 资料进入流水线后不可删除 |
|
||||
| P0 | `TASK-CLAIM-002` | 并发抢任务只允许一个成功 |
|
||||
| P0 | `EXT-APPROVE-002` | 提取审批通过自动推进 QA 链路 |
|
||||
| P0 | `QA-APPROVE-002` | QA 审批通过完成整条流水线 |
|
||||
| P0 | `EXPORT-BATCH-003` | 非 APPROVED 样本不可导出 |
|
||||
| P0 | `CONFIG-LIST-004` | 配置跨租户隔离 |
|
||||
| P0 | `VIDEO-CALLBACK-002` | 视频回调幂等 |
|
||||
|
||||
## 16. 后续落地建议
|
||||
|
||||
本文档适合作为三类产物的源:
|
||||
|
||||
- Swagger 手工测试清单
|
||||
- Postman/Apifox/Newman 自动化用例集
|
||||
- `src/test/java/com/label/blackbox` 下的 API 黑盒自动化测试
|
||||
|
||||
若需要继续落地,建议优先把以下用例自动化:
|
||||
|
||||
- 认证与 Token 生命周期
|
||||
- 任务领取并发
|
||||
- 提取/问答审批状态流转
|
||||
- 导出样本校验
|
||||
- 视频回调幂等与重试
|
||||
Reference in New Issue
Block a user