haha
This commit is contained in:
643
API文档.md
Normal file
643
API文档.md
Normal file
@@ -0,0 +1,643 @@
|
||||
# BOSS 直聘自动化平台 - API 接口文档
|
||||
|
||||
**Base URL**: `http://{服务器IP}:9000`
|
||||
|
||||
**认证方式**: Cookie(`auth_token`),登录成功后自动通过 `Set-Cookie` 设置,后续请求自动携带。
|
||||
|
||||
**请求格式**: 支持 `application/json` 和 `multipart/form-data`
|
||||
|
||||
**响应规范**: 所有接口的 JSON 响应体均包含 **`code`** 字段,与 HTTP 状态码一致。成功时如 `code: 200`、`code: 201`;错误时如 `code: 401`、`code: 404`。原返回数组的接口会包装为 `{"code": 状态码, "data": 原数组}`。
|
||||
|
||||
---
|
||||
|
||||
## 一、健康检查
|
||||
|
||||
### GET /health
|
||||
|
||||
**说明**: 检查服务器是否正常运行,无需登录。
|
||||
|
||||
**状态码**:
|
||||
|
||||
| 状态码 | 说明 |
|
||||
|--------|------|
|
||||
| 200 | 成功,返回服务状态与在线 Worker 数 |
|
||||
| 500 | 服务器内部错误(少见) |
|
||||
|
||||
**请求参数**: 无
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"workers_online": 2
|
||||
}
|
||||
```
|
||||
|
||||
| 返回字段 | 类型 | 说明 |
|
||||
|---------|------|------|
|
||||
| status | string | 服务状态,固定 `"ok"` |
|
||||
| workers_online | int | 当前在线 Worker 数量 |
|
||||
|
||||
---
|
||||
|
||||
## 二、认证
|
||||
|
||||
### POST /api/auth/login
|
||||
|
||||
**说明**: 管理员登录,无需认证。登录成功后通过 `Set-Cookie` 返回 `auth_token`。
|
||||
|
||||
**状态码**:
|
||||
|
||||
| 状态码 | 说明 |
|
||||
|--------|------|
|
||||
| 200 | 成功,返回 token 并设置 Cookie |
|
||||
| 401 | 用户名或密码错误 |
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| username | string | 是 | 用户名 |
|
||||
| password | string | 是 | 密码 |
|
||||
|
||||
**请求示例**:
|
||||
```json
|
||||
{
|
||||
"username": "admin",
|
||||
"password": "boss_dp_admin"
|
||||
}
|
||||
```
|
||||
|
||||
**成功响应** (200):
|
||||
```json
|
||||
{
|
||||
"token": "a1b2c3d4e5f6..."
|
||||
}
|
||||
```
|
||||
|
||||
**失败响应** (401):
|
||||
```json
|
||||
{
|
||||
"detail": "用户名或密码错误"
|
||||
}
|
||||
```
|
||||
|
||||
| 返回字段 | 类型 | 说明 |
|
||||
|---------|------|------|
|
||||
| token | string | 登录 token(同时通过 Cookie 设置) |
|
||||
|
||||
**响应 Header**:
|
||||
```
|
||||
Set-Cookie: auth_token=a1b2c3d4e5f6...; HttpOnly; Max-Age=31536000; SameSite=Lax
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、Worker 管理
|
||||
|
||||
> 以下接口均需登录(携带 `auth_token` Cookie)
|
||||
|
||||
### GET /api/workers
|
||||
|
||||
**说明**: 获取所有已注册 Worker 列表。
|
||||
|
||||
**状态码**:
|
||||
|
||||
| 状态码 | 说明 |
|
||||
|--------|------|
|
||||
| 200 | 成功,返回 Worker 数组 |
|
||||
| 401 | 未登录或 token 失效 |
|
||||
|
||||
**请求参数**: 无
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"worker_id": "worker-1",
|
||||
"worker_name": "本机",
|
||||
"browsers": [
|
||||
{
|
||||
"id": "abd63190eea84dc49429962f7bb330a4",
|
||||
"name": "第一个",
|
||||
"remark": ""
|
||||
}
|
||||
],
|
||||
"online": true,
|
||||
"current_task_id": null
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
| 返回字段 | 类型 | 说明 |
|
||||
|---------|------|------|
|
||||
| worker_id | string | Worker 唯一标识 |
|
||||
| worker_name | string | Worker 名称(电脑名) |
|
||||
| browsers | array | 该电脑上的比特浏览器环境列表 |
|
||||
| browsers[].id | string | 浏览器窗口 ID |
|
||||
| browsers[].name | string | 浏览器窗口名称(环境名) |
|
||||
| browsers[].remark | string | 备注 |
|
||||
| online | boolean | 是否在线 |
|
||||
| current_task_id | string/null | 当前正在执行的任务 ID |
|
||||
|
||||
---
|
||||
|
||||
### GET /api/workers/{worker_id}
|
||||
|
||||
**说明**: 获取指定 Worker 的详细信息。
|
||||
|
||||
**状态码**:
|
||||
|
||||
| 状态码 | 说明 |
|
||||
|--------|------|
|
||||
| 200 | 成功,返回单个 Worker 对象 |
|
||||
| 401 | 未登录或 token 失效 |
|
||||
| 404 | Worker 不存在 |
|
||||
|
||||
**路径参数**:
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| worker_id | string | 是 | Worker ID |
|
||||
|
||||
**成功响应** (200): 同上单个 Worker 对象
|
||||
|
||||
**失败响应** (404):
|
||||
```json
|
||||
{
|
||||
"detail": "Worker worker-99 不存在"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、账号管理
|
||||
|
||||
> 以下接口均需登录(携带 `auth_token` Cookie)
|
||||
|
||||
### POST /api/accounts
|
||||
|
||||
**说明**: 添加账号(将浏览器环境名称绑定到指定电脑)。若已存在相同绑定则直接返回已有记录。
|
||||
|
||||
**状态码**:
|
||||
|
||||
| 状态码 | 说明 |
|
||||
|--------|------|
|
||||
| 201 | 成功创建或返回已有记录 |
|
||||
| 400 | 请求参数错误(如缺少 browser_name、worker_id) |
|
||||
| 401 | 未登录或 token 失效 |
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| browser_name | string | 是 | 浏览器环境名称 |
|
||||
| worker_id | string | 是 | 目标电脑的 Worker ID |
|
||||
|
||||
**请求示例**:
|
||||
```json
|
||||
{
|
||||
"browser_name": "第一个",
|
||||
"worker_id": "worker-1"
|
||||
}
|
||||
```
|
||||
|
||||
**成功响应** (201):
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"worker_id": "worker-1",
|
||||
"browser_id": "name:第一个",
|
||||
"browser_name": "第一个",
|
||||
"boss_username": "",
|
||||
"is_logged_in": false,
|
||||
"current_task_id": null,
|
||||
"current_task_status": null,
|
||||
"checked_at": null,
|
||||
"created_at": "2026-02-14T16:00:00",
|
||||
"updated_at": "2026-02-14T16:00:00",
|
||||
"worker_name": "本机",
|
||||
"worker_online": true
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### GET /api/accounts
|
||||
|
||||
**说明**: 查询所有账号列表,包含电脑名称、在线状态、任务执行状态。
|
||||
|
||||
**状态码**:
|
||||
|
||||
| 状态码 | 说明 |
|
||||
|--------|------|
|
||||
| 200 | 成功,返回账号数组 |
|
||||
| 401 | 未登录或 token 失效 |
|
||||
|
||||
**查询参数**:
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| worker_id | string | 否 | 按 Worker ID 过滤 |
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"worker_id": "worker-1",
|
||||
"browser_id": "abd63190eea84dc49429962f7bb330a4",
|
||||
"browser_name": "第一个",
|
||||
"boss_username": "某用户",
|
||||
"is_logged_in": true,
|
||||
"current_task_id": "3f113d90bb32",
|
||||
"current_task_status": "success",
|
||||
"checked_at": "2026-02-14T16:05:00",
|
||||
"created_at": "2026-02-14T16:00:00",
|
||||
"updated_at": "2026-02-14T16:05:00",
|
||||
"worker_name": "本机",
|
||||
"worker_online": true
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
| 返回字段 | 类型 | 说明 |
|
||||
|---------|------|------|
|
||||
| id | int | 账号记录 ID |
|
||||
| worker_id | string | 绑定的 Worker ID |
|
||||
| browser_id | string | 比特浏览器窗口 ID |
|
||||
| browser_name | string | 浏览器环境名称 |
|
||||
| boss_username | string | BOSS 直聘用户名(检测后填充) |
|
||||
| is_logged_in | boolean | 是否已登录 BOSS |
|
||||
| current_task_id | string/null | 当前关联的任务 ID |
|
||||
| current_task_status | string/null | 当前任务状态:`pending` / `dispatched` / `running` / `success` / `failed` |
|
||||
| checked_at | string/null | 最近一次检测时间(ISO 格式) |
|
||||
| created_at | string | 创建时间 |
|
||||
| updated_at | string | 更新时间 |
|
||||
| worker_name | string | 电脑名称(运行时补充) |
|
||||
| worker_online | boolean | 电脑是否在线(运行时补充) |
|
||||
|
||||
---
|
||||
|
||||
### GET /api/accounts/{id}
|
||||
|
||||
**说明**: 查询单个账号详情。
|
||||
|
||||
**状态码**:
|
||||
|
||||
| 状态码 | 说明 |
|
||||
|--------|------|
|
||||
| 200 | 成功,返回单个账号对象 |
|
||||
| 401 | 未登录或 token 失效 |
|
||||
| 404 | 账号不存在 |
|
||||
|
||||
**路径参数**:
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| id | int | 是 | 账号记录 ID |
|
||||
|
||||
**成功响应** (200): 同上单个账号对象
|
||||
|
||||
**失败响应** (404):
|
||||
```json
|
||||
{
|
||||
"detail": "账号不存在"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### DELETE /api/accounts/{id}
|
||||
|
||||
**说明**: 删除指定账号。
|
||||
|
||||
**状态码**:
|
||||
|
||||
| 状态码 | 说明 |
|
||||
|--------|------|
|
||||
| 200 | 成功删除 |
|
||||
| 401 | 未登录或 token 失效 |
|
||||
| 404 | 账号不存在 |
|
||||
|
||||
**路径参数**:
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| id | int | 是 | 账号记录 ID |
|
||||
|
||||
**成功响应** (200):
|
||||
```json
|
||||
{
|
||||
"message": "账号已删除"
|
||||
}
|
||||
```
|
||||
|
||||
**失败响应** (404):
|
||||
```json
|
||||
{
|
||||
"detail": "账号不存在"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、任务管理
|
||||
|
||||
> 以下接口均需登录(携带 `auth_token` Cookie)
|
||||
>
|
||||
> **统一任务入口**:所有任务(检查登录、招聘等)都通过 `POST /api/tasks` 提交,通过 `task_type` 字段区分任务类型。
|
||||
|
||||
### POST /api/tasks
|
||||
|
||||
**说明**: 提交新任务。通过 `task_type` 指定任务类型,系统自动路由到目标 Worker 执行。
|
||||
|
||||
**状态码**:
|
||||
|
||||
| 状态码 | 说明 |
|
||||
|--------|------|
|
||||
| 201 | 成功,任务已创建并派发 |
|
||||
| 400 | 未指定 worker_id 或 account_name |
|
||||
| 401 | 未登录或 token 失效 |
|
||||
| 404 | 未找到拥有该浏览器环境的在线 Worker |
|
||||
| 503 | Worker 不在线 / WebSocket 连接不存在 / 派发失败 |
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| task_type | string | 是 | 任务类型:`check_login`(检查登录)、`boss_recruit`(招聘) |
|
||||
| worker_id | string | 否 | 目标 Worker ID(与 account_name 二选一) |
|
||||
| account_name | string | 否 | 浏览器环境名称(系统自动查找对应 Worker) |
|
||||
| params | object | 否 | 任务附加参数,默认 `{}` |
|
||||
|
||||
**路由规则**: `worker_id` 优先;若未指定则通过 `account_name` 自动查找对应的在线 Worker。
|
||||
|
||||
**请求示例 — 检查登录**:
|
||||
```json
|
||||
{
|
||||
"task_type": "check_login",
|
||||
"account_name": "第一个"
|
||||
}
|
||||
```
|
||||
|
||||
**请求示例 — 招聘任务**:
|
||||
```json
|
||||
{
|
||||
"task_type": "boss_recruit",
|
||||
"worker_id": "worker-1",
|
||||
"params": {
|
||||
"keyword": "Python开发",
|
||||
"count": 10
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**成功响应** (201):
|
||||
```json
|
||||
{
|
||||
"task_id": "3f113d90bb32",
|
||||
"task_type": "check_login",
|
||||
"status": "dispatched",
|
||||
"worker_id": "worker-1",
|
||||
"account_name": "第一个",
|
||||
"params": {},
|
||||
"progress": null,
|
||||
"result": null,
|
||||
"error": null,
|
||||
"created_at": 1739520000.123,
|
||||
"updated_at": 1739520000.456
|
||||
}
|
||||
```
|
||||
|
||||
**失败响应**:
|
||||
|
||||
| 状态码 | 说明 |
|
||||
|--------|------|
|
||||
| 400 | 未指定 worker_id 或 account_name |
|
||||
| 401 | 未登录或 token 失效 |
|
||||
| 404 | 未找到拥有该浏览器环境的在线 Worker |
|
||||
| 503 | Worker 不在线 / WebSocket 连接不存在 / 派发失败 |
|
||||
|
||||
---
|
||||
|
||||
### GET /api/tasks
|
||||
|
||||
**说明**: 查询任务列表(内存中的任务)。
|
||||
|
||||
**状态码**:
|
||||
|
||||
| 状态码 | 说明 |
|
||||
|--------|------|
|
||||
| 200 | 成功,返回任务数组 |
|
||||
| 401 | 未登录或 token 失效 |
|
||||
|
||||
**查询参数**:
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| worker_id | string | 否 | 按 Worker ID 过滤 |
|
||||
| status | string | 否 | 按状态过滤:`pending` / `dispatched` / `running` / `success` / `failed` / `cancelled` |
|
||||
| limit | int | 否 | 返回数量限制,默认 50 |
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"task_id": "3f113d90bb32",
|
||||
"task_type": "check_login",
|
||||
"status": "success",
|
||||
"worker_id": "worker-1",
|
||||
"account_name": "第一个",
|
||||
"params": {},
|
||||
"progress": "检测完成: 第一个 → 未登录 ()",
|
||||
"result": {
|
||||
"browser_id": "abd63190eea84dc49429962f7bb330a4",
|
||||
"browser_name": "第一个",
|
||||
"boss_username": "",
|
||||
"is_logged_in": false
|
||||
},
|
||||
"error": null,
|
||||
"created_at": 1739520000.123,
|
||||
"updated_at": 1739520030.789
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
| 返回字段 | 类型 | 说明 |
|
||||
|---------|------|------|
|
||||
| task_id | string | 任务唯一 ID(12 位十六进制) |
|
||||
| task_type | string | 任务类型 |
|
||||
| status | string | 任务状态:`pending` → `dispatched` → `running` → `success`/`failed` |
|
||||
| worker_id | string/null | 执行任务的 Worker ID |
|
||||
| account_name | string/null | 关联的浏览器环境名称 |
|
||||
| params | object | 任务参数 |
|
||||
| progress | string/null | 执行进度描述 |
|
||||
| result | object/null | 任务结果(成功时返回) |
|
||||
| error | string/null | 错误信息(失败时返回) |
|
||||
| created_at | float | 创建时间(Unix 时间戳) |
|
||||
| updated_at | float | 最后更新时间(Unix 时间戳) |
|
||||
|
||||
---
|
||||
|
||||
### GET /api/tasks/{task_id}
|
||||
|
||||
**说明**: 查询指定任务的状态和结果。
|
||||
|
||||
**状态码**:
|
||||
|
||||
| 状态码 | 说明 |
|
||||
|--------|------|
|
||||
| 200 | 成功,返回单个任务对象 |
|
||||
| 401 | 未登录或 token 失效 |
|
||||
| 404 | 任务不存在 |
|
||||
|
||||
**路径参数**:
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| task_id | string | 是 | 任务 ID |
|
||||
|
||||
**成功响应** (200): 同上单个任务对象
|
||||
|
||||
**失败响应** (404):
|
||||
```json
|
||||
{
|
||||
"detail": "任务 xxx 不存在"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、任务类型说明
|
||||
|
||||
| task_type | 说明 | 必要参数 | 返回 result |
|
||||
|-----------|------|---------|-------------|
|
||||
| `check_login` | 检测浏览器环境中 BOSS 账号是否已登录 | `account_name`(环境名) | `{ "browser_id", "browser_name", "boss_username", "is_logged_in" }` |
|
||||
| `boss_recruit` | 执行 BOSS 直聘自动招聘流程 | `worker_id` + `params` | 根据任务定义返回 |
|
||||
|
||||
---
|
||||
|
||||
## 七、任务状态流转
|
||||
|
||||
```
|
||||
pending → dispatched → running → success
|
||||
→ failed
|
||||
→ cancelled
|
||||
```
|
||||
|
||||
| 状态 | 说明 |
|
||||
|------|------|
|
||||
| pending | 任务已创建,等待派发 |
|
||||
| dispatched | 已通过 WebSocket 发送给 Worker |
|
||||
| running | Worker 正在执行中 |
|
||||
| success | 执行成功 |
|
||||
| failed | 执行失败 |
|
||||
| cancelled | 已取消 |
|
||||
|
||||
---
|
||||
|
||||
## 八、联系记录(数据展示)
|
||||
|
||||
> 以下接口均需登录(携带 `auth_token` Cookie)
|
||||
|
||||
### GET /api/contacts/query(按电话/微信号查询)
|
||||
|
||||
**说明**:根据已获取的电话号码或微信号查询联系记录,用于数据展示。对 `contact` 字段做模糊匹配。
|
||||
|
||||
**状态码**:
|
||||
|
||||
| 状态码 | 说明 |
|
||||
|--------|------|
|
||||
| 200 | 成功,返回分页结果 |
|
||||
| 400 | 未提供 contact 参数 |
|
||||
| 401 | 未登录或 token 失效 |
|
||||
|
||||
**查询参数**:
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| contact | string | 是 | 电话号码或微信号(支持部分匹配) |
|
||||
| page | int | 否 | 页码,默认 1 |
|
||||
| page_size | int | 否 | 每页条数,默认 20 |
|
||||
|
||||
**请求示例**:
|
||||
```
|
||||
GET /api/contacts/query?contact=13800138000
|
||||
GET /api/contacts/query?contact=wxid_abc&page=1&page_size=10
|
||||
```
|
||||
|
||||
**成功响应** (200):
|
||||
```json
|
||||
{
|
||||
"total": 2,
|
||||
"page": 1,
|
||||
"page_size": 20,
|
||||
"contact_keyword": "13800138000",
|
||||
"results": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "张三",
|
||||
"position": "Python开发",
|
||||
"contact": "13800138000",
|
||||
"reply_status": "已回复",
|
||||
"wechat_exchanged": true,
|
||||
"account_id": 1,
|
||||
"worker_id": "worker-1",
|
||||
"notes": "",
|
||||
"contacted_at": "2026-02-24T10:00:00",
|
||||
"created_at": "2026-02-24T10:00:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
| 返回字段 | 类型 | 说明 |
|
||||
|---------|------|------|
|
||||
| total | int | 匹配总数 |
|
||||
| page | int | 当前页 |
|
||||
| page_size | int | 每页条数 |
|
||||
| contact_keyword | string | 本次查询使用的关键词 |
|
||||
| results | array | 联系记录列表(字段同联系记录模型) |
|
||||
|
||||
**失败响应** (400):未提供 contact 参数时
|
||||
```json
|
||||
{
|
||||
"detail": "请提供 contact 参数(电话号码或微信号)"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### GET /api/contacts
|
||||
|
||||
**说明**:分页查询联系记录,支持按姓名、岗位、联系方式关键词搜索,以及按回复状态、是否交换微信筛选。
|
||||
|
||||
**状态码**:
|
||||
|
||||
| 状态码 | 说明 |
|
||||
|--------|------|
|
||||
| 200 | 成功,返回 total、page、page_size、results |
|
||||
| 401 | 未登录或 token 失效 |
|
||||
|
||||
**查询参数**:`search`(关键词)、`reply_status`、`wechat_exchanged`、`page`、`page_size`。返回格式同上(含 `total`、`page`、`page_size`、`results`)。
|
||||
|
||||
---
|
||||
|
||||
## 九、通用错误格式
|
||||
|
||||
所有接口的错误响应统一为:
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": "错误描述信息"
|
||||
}
|
||||
```
|
||||
|
||||
| 状态码 | 说明 |
|
||||
|--------|------|
|
||||
| 400 | 请求参数错误 |
|
||||
| 401 | 未登录或 token 失效 |
|
||||
| 403 | 无权限 |
|
||||
| 404 | 资源不存在 |
|
||||
| 503 | Worker 不在线或服务不可用 |
|
||||
@@ -13,9 +13,10 @@ def custom_exception_handler(exc, context):
|
||||
# 确保使用异常自带的 HTTP 状态码(如 401 登录失效、403 无权限)
|
||||
if isinstance(exc, APIException) and getattr(exc, "status_code", None):
|
||||
response.status_code = exc.status_code
|
||||
# 统一格式:{"detail": "..."}
|
||||
# 统一格式:{"detail": "...", "code": 状态码}
|
||||
if isinstance(response.data, list):
|
||||
response.data = {"detail": response.data[0] if response.data else str(exc)}
|
||||
elif isinstance(response.data, dict) and "detail" not in response.data:
|
||||
response.data = {"detail": str(response.data)}
|
||||
response.data["code"] = response.status_code
|
||||
return response
|
||||
|
||||
@@ -2,6 +2,34 @@
|
||||
"""
|
||||
自定义中间件。
|
||||
"""
|
||||
import json
|
||||
|
||||
|
||||
class ResponseCodeMiddleware:
|
||||
"""为所有 JSON 响应 body 统一注入 code 字段(与 HTTP 状态码一致)。"""
|
||||
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request):
|
||||
response = self.get_response(request)
|
||||
if "application/json" not in response.get("Content-Type", ""):
|
||||
return response
|
||||
try:
|
||||
content = response.content.decode(response.charset or "utf-8")
|
||||
data = json.loads(content)
|
||||
code = response.status_code
|
||||
if isinstance(data, dict):
|
||||
if "code" not in data:
|
||||
data["code"] = code
|
||||
elif isinstance(data, list):
|
||||
data = {"code": code, "data": data}
|
||||
else:
|
||||
data = {"code": code, "data": data}
|
||||
response.content = json.dumps(data, ensure_ascii=False).encode(response.charset or "utf-8")
|
||||
except (ValueError, TypeError, UnicodeDecodeError):
|
||||
pass
|
||||
return response
|
||||
|
||||
|
||||
class CorsMiddleware:
|
||||
|
||||
@@ -25,6 +25,7 @@ INSTALLED_APPS = [
|
||||
MIDDLEWARE = [
|
||||
"django.middleware.common.CommonMiddleware",
|
||||
"server.middleware.CorsMiddleware",
|
||||
"server.middleware.ResponseCodeMiddleware",
|
||||
]
|
||||
|
||||
# ─── URL / ASGI ───
|
||||
|
||||
Reference in New Issue
Block a user