包含多个会话的累积代码变更: - backend: AI 聊天服务、触发器调度、认证增强、WebSocket、调度器最小间隔 - admin-web: ETL 状态页、任务管理、调度配置、登录优化 - miniprogram: 看板页面、聊天集成、UI 组件、导航更新 - etl: DWS 新任务(finance_area_daily/board_cache)、连接器增强 - tenant-admin: 项目初始化 - db: 19 个迁移脚本(etl_feiqiu 11 + zqyy_app 8) - packages/shared: 枚举和工具函数更新 - tools: 数据库工具、报表生成、健康检查 - docs: PRD/架构/部署/合约文档更新 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1426 lines
40 KiB
Markdown
1426 lines
40 KiB
Markdown
# API 参考手册
|
||
|
||
后端 API 基于 FastAPI 构建,所有端点均以 `/api/` 为前缀。
|
||
在线文档:启动后访问 `http://localhost:8000/docs`(Swagger UI)或 `/redoc`(ReDoc)。
|
||
|
||
## 全局响应格式(RNS1.0)
|
||
|
||
所有 JSON 成功响应(HTTP 2xx)自动包装为统一格式:
|
||
|
||
```json
|
||
{ "code": 0, "data": { ... } }
|
||
```
|
||
|
||
异常响应格式:
|
||
|
||
```json
|
||
{ "code": 401, "message": "未认证" }
|
||
```
|
||
|
||
规则:
|
||
- `code=0` 表示成功,`data` 为业务数据
|
||
- `code≠0` 表示错误,`code` 为 HTTP 状态码,`message` 为错误描述
|
||
- SSE 流式端点(`text/event-stream`)和非 JSON 响应不包装,直接透传
|
||
- 所有小程序 API 响应字段名为 camelCase(如 `userId`、`storeName`)
|
||
|
||
---
|
||
|
||
## 1. 管理后台认证 `/api/auth`
|
||
|
||
### POST `/api/auth/login`
|
||
管理后台用户名密码登录。
|
||
|
||
请求体:
|
||
```json
|
||
{ "username": "admin", "password": "..." }
|
||
```
|
||
响应:
|
||
```json
|
||
{ "access_token": "...", "refresh_token": "...", "token_type": "bearer" }
|
||
```
|
||
|
||
### POST `/api/auth/refresh`
|
||
刷新访问令牌。
|
||
|
||
请求体:
|
||
```json
|
||
{ "refresh_token": "..." }
|
||
```
|
||
|
||
---
|
||
|
||
## 2. 小程序认证 `/api/xcx-auth`
|
||
|
||
小程序用户的完整生命周期:微信登录 → 提交申请 → 管理员审批 → 正式使用。
|
||
|
||
### POST `/api/xcx-auth/login`
|
||
微信登录。用 `wx.login()` 获取的 code 换取 JWT。
|
||
|
||
请求体:
|
||
```json
|
||
{ "code": "微信临时登录凭证" }
|
||
```
|
||
响应:
|
||
```json
|
||
{
|
||
"access_token": "...",
|
||
"refresh_token": "...",
|
||
"token_type": "bearer",
|
||
"user_status": "pending | approved | rejected | disabled",
|
||
"user_id": 1
|
||
}
|
||
```
|
||
说明:
|
||
- 首次登录自动创建 `auth.users` 记录(status=new),前端引导至申请页
|
||
- new/pending/rejected 用户获得受限令牌(`limited=True`),仅可访问申请相关端点
|
||
- approved 用户获得完整令牌,包含 `site_id` 和 `roles`
|
||
|
||
### POST `/api/xcx-auth/apply`
|
||
提交入驻申请。需受限令牌或完整令牌。
|
||
|
||
请求体:
|
||
```json
|
||
{
|
||
"site_code": "AB123",
|
||
"applied_role_text": "助教",
|
||
"phone": "13800138000",
|
||
"employee_number": "E001",
|
||
"nickname": "张三"
|
||
}
|
||
```
|
||
说明:
|
||
- `site_code` 格式:2 字母 + 3 数字(如 `AB123`),映射到 `auth.site_code_mapping`
|
||
- 后端自动进行人员匹配(`matching.py`),在 ETL 库中查找助教/员工记录
|
||
|
||
### GET `/api/xcx-auth/status`
|
||
查询当前用户状态和申请记录。需受限令牌或完整令牌。
|
||
|
||
响应:
|
||
```json
|
||
{
|
||
"user_id": 1,
|
||
"status": "approved",
|
||
"nickname": "张三",
|
||
"applications": [
|
||
{
|
||
"id": 1,
|
||
"site_code": "AB123",
|
||
"applied_role_text": "助教",
|
||
"status": "approved",
|
||
"review_note": null,
|
||
"created_at": "2026-02-25T10:00:00",
|
||
"reviewed_at": "2026-02-25T11:00:00"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### GET `/api/xcx-auth/sites`
|
||
获取当前用户关联的门店列表。需完整令牌。
|
||
|
||
### POST `/api/xcx-auth/switch-site`
|
||
切换当前门店,返回新的令牌对。需完整令牌。
|
||
|
||
请求体:
|
||
```json
|
||
{ "site_id": 2 }
|
||
```
|
||
|
||
### POST `/api/xcx-auth/refresh`
|
||
刷新令牌。
|
||
|
||
请求体:
|
||
```json
|
||
{ "refresh_token": "..." }
|
||
```
|
||
|
||
### POST `/api/xcx-auth/dev-login`
|
||
开发模式 mock 登录(仅 `WX_DEV_MODE=true` 时注册)。
|
||
|
||
请求体:
|
||
```json
|
||
{ "openid": "模拟openid", "status": "approved" }
|
||
```
|
||
说明:
|
||
- `status` 可选,为空时保留已有用户当前状态,新用户默认 `new`
|
||
- 仅开发/测试环境可用
|
||
|
||
---
|
||
|
||
## 3. 任务配置 `/api/tasks`
|
||
|
||
所有端点需 JWT 认证。
|
||
|
||
### GET `/api/tasks/registry`
|
||
按业务域分组的 ETL 任务列表。
|
||
|
||
响应示例:
|
||
```json
|
||
{
|
||
"groups": {
|
||
"会员": [
|
||
{
|
||
"code": "DWD_LOAD_FROM_ODS",
|
||
"name": "ODS → DWD 加载",
|
||
"domain": "会员",
|
||
"layer": "DWD",
|
||
"requires_window": true,
|
||
"is_ods": false,
|
||
"is_dimension": false,
|
||
"default_enabled": true,
|
||
"is_common": true
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
### GET `/api/tasks/dwd-tables`
|
||
按业务域分组的 DWD 表定义。
|
||
|
||
### GET `/api/tasks/flows`
|
||
返回 7 种 Flow 定义和 4 种处理模式。
|
||
|
||
Flow 列表:
|
||
| ID | 名称 | 层级 |
|
||
|----|------|------|
|
||
| `api_ods` | API → ODS | ODS |
|
||
| `api_ods_dwd` | API → ODS → DWD | ODS, DWD |
|
||
| `api_full` | API → ODS → DWD → DWS → INDEX | ODS, DWD, DWS, INDEX |
|
||
| `ods_dwd` | ODS → DWD | DWD |
|
||
| `dwd_dws` | DWD → DWS汇总 | DWS |
|
||
| `dwd_dws_index` | DWD → DWS → INDEX | DWS, INDEX |
|
||
| `dwd_index` | DWD → DWS指数 | INDEX |
|
||
|
||
处理模式:
|
||
| ID | 名称 | 说明 |
|
||
|----|------|------|
|
||
| `increment_only` | 仅增量处理 | 只处理新增和变更的数据 |
|
||
| `verify_only` | 仅校验修复 | 校验现有数据并修复不一致 |
|
||
| `increment_verify` | 增量 + 校验修复 | 先增量处理,再校验并修复 |
|
||
| `full_window` | 全窗口处理 | 用 API 返回数据的实际时间范围处理全部层 |
|
||
|
||
### POST `/api/tasks/validate`
|
||
验证任务配置并返回 CLI 命令预览。`store_id` 从 JWT 自动注入。
|
||
|
||
### GET `/api/tasks/sync-check`
|
||
对比后端硬编码任务列表与 ETL 真实注册表,返回差异。
|
||
|
||
---
|
||
|
||
## 4. 任务执行 `/api/execution`
|
||
|
||
所有端点需 JWT 认证,`site_id` 从 JWT 提取。
|
||
|
||
### POST `/api/execution/run`
|
||
直接执行任务(不经过队列)。异步启动 ETL CLI 子进程。
|
||
|
||
请求体:`TaskConfigSchema`(flow、tasks、window 等)
|
||
|
||
响应:
|
||
```json
|
||
{ "execution_id": "uuid", "message": "任务已提交执行" }
|
||
```
|
||
|
||
### GET `/api/execution/queue`
|
||
获取当前门店的待执行队列。
|
||
|
||
### POST `/api/execution/queue`
|
||
将任务配置添加到执行队列。
|
||
|
||
### PUT `/api/execution/queue/reorder`
|
||
调整队列中任务的执行顺序。
|
||
|
||
### DELETE `/api/execution/queue/{task_id}`
|
||
从队列中删除待执行任务(仅 pending 状态)。
|
||
|
||
### POST `/api/execution/{execution_id}/cancel`
|
||
取消正在执行的任务。
|
||
|
||
### GET `/api/execution/history`
|
||
执行历史记录(按 `started_at` 降序,默认 50 条,最多 200 条)。
|
||
|
||
### GET `/api/execution/{execution_id}/logs`
|
||
获取指定执行的完整日志。优先从内存缓冲区读取(执行中),否则从数据库读取(已完成)。
|
||
|
||
---
|
||
|
||
## 5. 调度管理 `/api/schedules`
|
||
|
||
所有端点需 JWT 认证。
|
||
|
||
### GET `/api/schedules`
|
||
列出当前门店的所有调度任务。
|
||
|
||
### POST `/api/schedules`
|
||
创建调度任务,自动计算 `next_run_at`。
|
||
|
||
请求体新增字段(P16):
|
||
- `min_run_interval_value`:最小运行间隔数值(int,默认 0 表示无限制)
|
||
- `min_run_interval_unit`:间隔单位(`minutes`/`hours`/`days`,默认 `minutes`)
|
||
|
||
### PUT `/api/schedules/{schedule_id}`
|
||
更新调度任务(部分更新,仅更新请求中提供的字段)。
|
||
|
||
请求体新增可选字段(P16):
|
||
- `min_run_interval_value`:最小运行间隔数值
|
||
- `min_run_interval_unit`:间隔单位
|
||
|
||
### DELETE `/api/schedules/{schedule_id}`
|
||
删除调度任务。
|
||
|
||
### PATCH `/api/schedules/{schedule_id}/toggle`
|
||
切换启用/禁用状态。禁用时 `next_run_at` 置 NULL;启用时重新计算。
|
||
|
||
### POST `/api/schedules/{schedule_id}/run`
|
||
手动触发执行调度任务。
|
||
|
||
查询参数:
|
||
- `force`:是否强制执行(bool,默认 `false`)
|
||
|
||
行为:
|
||
- `force=false`:检查并发状态和最小间隔,不满足条件返回 409
|
||
- `force=true`:绕过所有检查,直接入队执行
|
||
|
||
错误码:
|
||
- 409:任务正在执行中(`last_status='running'`)
|
||
- 409:最小运行间隔未到(提示距下次可执行的剩余时间)
|
||
|
||
列表响应新增字段(P16):
|
||
- `min_run_interval_value`:最小运行间隔数值(0 表示无限制)
|
||
- `min_run_interval_unit`:间隔单位
|
||
- `last_success_at`:最后一次成功执行时间(TIMESTAMPTZ,可为 null)
|
||
|
||
---
|
||
|
||
## 6. 数据库查看器 `/api/db`
|
||
|
||
所有端点需 JWT 认证。使用 ETL 只读连接 + RLS 门店隔离。
|
||
|
||
### GET `/api/db/schemas`
|
||
返回 ETL 数据库中的 Schema 列表。
|
||
|
||
### GET `/api/db/schemas/{name}/tables`
|
||
返回指定 Schema 下所有表的名称和行数统计。
|
||
|
||
### GET `/api/db/tables/{schema}/{table}/columns`
|
||
返回指定表的列定义(列名、数据类型、是否可空、默认值)。
|
||
|
||
### POST `/api/db/query`
|
||
只读 SQL 执行。
|
||
|
||
安全措施:
|
||
- 拦截写操作关键词(INSERT / UPDATE / DELETE / DROP / TRUNCATE)
|
||
- 返回行数上限 1000 行
|
||
- 查询超时 30 秒
|
||
- 连接级 `read_only` 保护
|
||
|
||
请求体:
|
||
```json
|
||
{ "sql": "SELECT * FROM dwd.member_info LIMIT 10" }
|
||
```
|
||
|
||
---
|
||
|
||
## 7. ETL 状态 `/api/etl-status`
|
||
|
||
### GET `/api/etl-status/cursors`
|
||
返回各 ODS 表的最新数据游标(查询 `meta.etl_cursor`)。
|
||
|
||
### GET `/api/etl-status/recent-runs`
|
||
返回最近 50 条任务执行记录。
|
||
|
||
---
|
||
|
||
## 8. 环境配置 `/api/env-config`
|
||
|
||
### GET `/api/env-config`
|
||
读取根 `.env` 文件内容(敏感值脱敏显示)。
|
||
|
||
### PUT `/api/env-config`
|
||
更新 `.env` 文件中的配置项。
|
||
|
||
---
|
||
|
||
## 9. 运维面板 `/api/ops`
|
||
|
||
### GET `/api/ops/system`
|
||
服务器系统资源概况(CPU、内存、磁盘、启动时间)。
|
||
|
||
### GET `/api/ops/services`
|
||
所有环境(test/prod)的服务运行状态(PID、端口、内存、CPU、运行时长)。
|
||
|
||
### POST `/api/ops/services/{env}/start`
|
||
启动指定环境的后端服务。
|
||
|
||
### POST `/api/ops/services/{env}/stop`
|
||
停止指定环境的后端服务。
|
||
|
||
### POST `/api/ops/services/{env}/restart`
|
||
重启指定环境的后端服务。
|
||
|
||
### GET `/api/ops/git`
|
||
所有环境的 Git 状态(分支、最新提交、是否有本地修改)。
|
||
|
||
### POST `/api/ops/git/{env}/pull`
|
||
对指定环境执行 `git pull --ff-only`。
|
||
|
||
### POST `/api/ops/git/{env}/sync-deps`
|
||
对指定环境执行 `uv sync --all-packages`。
|
||
|
||
### GET `/api/ops/env-file/{env}`
|
||
读取指定环境的 `.env` 文件(敏感值脱敏)。
|
||
|
||
---
|
||
|
||
## 10. 其他端点
|
||
|
||
### GET `/health`
|
||
健康检查。返回 `{"status": "ok"}`。
|
||
|
||
### GET `/api/xcx-test`
|
||
MVP 全链路验证端点,从 `test."xcx-test"` 表读取数据。
|
||
|
||
### GET/POST `/api/wx-callback`
|
||
微信消息推送回调。GET 用于签名验证,POST 用于接收消息。
|
||
|
||
---
|
||
|
||
## 11. 管理端申请审核 `/api/admin/applications`
|
||
|
||
### GET `/api/admin/applications`
|
||
获取待审核申请列表。需管理后台 JWT。
|
||
|
||
### POST `/api/admin/applications/{id}/approve`
|
||
批准申请。
|
||
|
||
### POST `/api/admin/applications/{id}/reject`
|
||
拒绝申请。
|
||
|
||
---
|
||
|
||
## 12. 营业日配置 `/api/business-day`
|
||
|
||
### GET `/api/business-day/config`
|
||
获取营业日分割点配置(`BUSINESS_DAY_START_HOUR`)。
|
||
|
||
---
|
||
|
||
## 13. 小程序任务 `/api/xcx/tasks`
|
||
|
||
所有端点需 JWT(approved 状态)。
|
||
|
||
### GET `/api/xcx/tasks`
|
||
获取当前助教的活跃任务列表。
|
||
|
||
响应:`TaskListItem[]`
|
||
|
||
### POST `/api/xcx/tasks/{id}/pin`
|
||
置顶任务。
|
||
|
||
### POST `/api/xcx/tasks/{id}/unpin`
|
||
取消置顶。
|
||
|
||
### POST `/api/xcx/tasks/{id}/abandon`
|
||
放弃任务(需填写原因)。
|
||
|
||
请求体:
|
||
```json
|
||
{ "reason": "放弃原因" }
|
||
```
|
||
|
||
### POST `/api/xcx/tasks/{id}/restore`
|
||
取消放弃,恢复为活跃状态。
|
||
|
||
### GET `/api/xcx/tasks/{id}`
|
||
获取任务详情(TASK-2)。返回单个任务的完整信息,含服务记录、备注、AI 分析、维客线索。
|
||
|
||
响应包含 `customerId` 字段,供前端跳转客户详情/对话页面使用。
|
||
|
||
---
|
||
|
||
## 14. 小程序绩效 `/api/xcx/performance`
|
||
|
||
所有端点需 JWT(approved 状态)。
|
||
|
||
### GET `/api/xcx/performance`
|
||
绩效概览(PERF-1)。返回指定月份的收入档位、DateGroup 分组记录、新客/常客列表。
|
||
|
||
查询参数:
|
||
- `month`:月份(格式 `YYYY-MM`,默认当月)
|
||
|
||
响应包含:
|
||
- `currentTier`:当前档位助教到手单价(`basicRate` = 客户价 - base_deduction,`incentiveRate` = 客户价 × (1 - bonus_deduction_ratio))
|
||
- `nextTier`:下一档到手单价(同上公式,使用下一档抽成参数)
|
||
- `upgradeHoursNeeded`:距下一档所需小时数(next_tier.min_hours - total_hours)
|
||
- `upgradeBonus`:升档后因抽成降低的节省额
|
||
- `incomeItems`:始终 3 项(基础课收入、激励课收入、Top3 销冠奖),金额为 0 时仍显示
|
||
- `thisMonthRecords`(DateGroup 分组)、`lastMonthIncome`、`newCustomers`/`regularCustomers`
|
||
|
||
档位数据来源:`cfg_performance_tier` 配置表(通过 `fdw_queries.get_performance_tiers()` 查询)。
|
||
|
||
### GET `/api/xcx/performance/records`
|
||
绩效明细(PERF-2)。返回指定月份的服务记录明细,按日期分组,支持分页。
|
||
|
||
查询参数:
|
||
- `month`:月份(格式 `YYYY-MM`,默认当月)
|
||
- `page`:页码(默认 1)
|
||
- `page_size`:每页条数(默认 20)
|
||
|
||
---
|
||
|
||
## 15. 小程序备注 `/api/xcx/notes`
|
||
|
||
所有端点需 JWT(approved 状态)。
|
||
|
||
### POST `/api/xcx/notes`
|
||
创建备注(含星星评分,可选关联任务)。
|
||
|
||
请求体:
|
||
```json
|
||
{
|
||
"target_type": "member",
|
||
"target_id": 1,
|
||
"content": "备注内容",
|
||
"task_id": null,
|
||
"rating_service_willingness": 4,
|
||
"rating_revisit_likelihood": 3
|
||
}
|
||
```
|
||
|
||
### GET `/api/xcx/notes`
|
||
查询某目标的备注列表(按创建时间倒序)。
|
||
|
||
查询参数:
|
||
- `target_type`:目标类型(默认 `member`)
|
||
- `target_id`:目标 ID(必填)
|
||
|
||
### DELETE `/api/xcx/notes/{id}`
|
||
删除备注(验证归属后硬删除)。
|
||
|
||
---
|
||
|
||
## 16. 小程序配置 `/api/xcx/config`
|
||
|
||
所有端点需 JWT(approved 状态)。
|
||
|
||
### GET `/api/xcx/config/skill-types`
|
||
项目类型筛选器配置(CONFIG-1)。返回前端筛选器选项列表。
|
||
|
||
数据源:`app.v_cfg_area_category`(基于 `dws.cfg_area_category` 去重到 category 级别,排除 SPECIAL/OTHER,按 sort_order 排序)。
|
||
|
||
响应头部自动插入"不限"选项(key=ALL),不存储在数据库中。
|
||
|
||
响应:`SkillTypeItem[]`
|
||
```json
|
||
[
|
||
{ "key": "ALL", "label": "不限", "emoji": "🔍", "cls": "" },
|
||
{ "key": "BILLIARD", "label": "🎱 中式/追分", "emoji": "🎱", "cls": "" },
|
||
{ "key": "SNOOKER", "label": "斯诺克", "emoji": "斯", "cls": "" },
|
||
{ "key": "MAHJONG", "label": "🀄 麻将/棋牌", "emoji": "🀄", "cls": "" },
|
||
{ "key": "KTV", "label": "🎤 团建/K歌", "emoji": "🎤", "cls": "" }
|
||
]
|
||
```
|
||
|
||
降级行为:查询失败时返回空数组 `[]`。
|
||
|
||
---
|
||
|
||
## 17. 小程序三看板 `/api/xcx/board`
|
||
|
||
所有端点需 JWT + 对应权限。
|
||
|
||
### GET `/api/xcx/board/coaches`
|
||
助教看板(BOARD-1)。返回助教列表,支持排序×技能×时间三重筛选。
|
||
|
||
查询参数:
|
||
- `sort`:排序维度(`perf_desc`/`perf_asc`/`salary_desc`/`salary_asc`/`sv_desc`/`task_desc`,默认 `perf_desc`)
|
||
- `skill`:技能筛选(`ALL`/`BILLIARD`/`SNOOKER`/`MAHJONG`/`KTV`,默认 `ALL`)
|
||
- `time`:时间范围(`month`/`quarter`/`last_month`/`last_3m`/`last_quarter`/`last_6m`,默认 `month`)
|
||
|
||
约束:`time=last_6m` + `sort=sv_desc` 互斥,返回 400。
|
||
|
||
权限:`view_board_coach`
|
||
|
||
响应:`CoachBoardResponse`(含 `items` + `dimType`)
|
||
|
||
### GET `/api/xcx/board/customers`
|
||
客户看板(BOARD-2)。返回客户列表,支持维度×项目筛选 + 分页。
|
||
|
||
查询参数:
|
||
- `dimension`:客户维度(`recall`/`potential`/`balance`/`recharge`/`recent`/`spend60`/`freq60`/`loyal`,默认 `recall`)
|
||
- `project`:项目筛选(`ALL`/`BILLIARD`/`SNOOKER`/`MAHJONG`/`KTV`,默认 `ALL`)
|
||
- `page`:页码(默认 1)
|
||
- `page_size`:每页条数(默认 20,最大 100)
|
||
|
||
权限:`view_board_customer`
|
||
|
||
响应:`CustomerBoardResponse`(含 `items`/`total`/`page`/`pageSize`)
|
||
|
||
### GET `/api/xcx/board/finance`
|
||
财务看板(BOARD-3)。返回 6 大板块(经营一览/预收资产/营收结构/现金流/支出/助教分析)。
|
||
|
||
查询参数:
|
||
- `time`:时间范围(`month`/`lastMonth`/`week`/`lastWeek`/`quarter3`/`quarter`/`lastQuarter`/`half6`,默认 `month`)
|
||
- `area`:区域筛选(`all`/`hall`/`hallA`/`hallB`/`hallC`/`mahjong`/`teamBuilding`,默认 `all`)
|
||
- `compare`:环比开关(0=关闭/1=开启,默认 0)
|
||
|
||
约束:`area≠all` 时 `recharge` 板块为 null;`compare=0` 时响应不含环比字段。
|
||
|
||
权限:`view_board_finance`
|
||
|
||
响应:`FinanceBoardResponse`(含 `overview`/`recharge`/`revenue`/`cashflow`/`expense`/`coachAnalysis`)
|
||
|
||
---
|
||
|
||
## 18. 小程序 CHAT `/api/xcx/chat`
|
||
|
||
所有端点需 JWT(approved 状态)。替代原 `xcx_ai_chat`(`/api/ai/*`),统一迁移到 `/api/xcx/chat/*` 路径。
|
||
|
||
### GET `/api/xcx/chat/history`
|
||
对话历史列表(CHAT-1)。返回当前用户的对话列表,按最后消息时间倒序。
|
||
|
||
查询参数:
|
||
- `page`:页码(默认 1)
|
||
- `page_size`:每页条数(默认 20,最大 100)
|
||
|
||
响应:`ChatHistoryResponse`(含 `items`/`total`/`page`/`pageSize`)
|
||
|
||
每条对话包含:`id`(对话 ID)、`title`(对话标题)、`customerName`(关联客户姓名,可选)、`lastMessage`(最后消息摘要)、`timestamp`(最后消息时间)、`unreadCount`(未读数)。
|
||
|
||
### GET `/api/xcx/chat/messages`
|
||
通过上下文查询消息(CHAT-2b)。根据 `contextType` + `contextId` 自动查找或创建对话。
|
||
|
||
查询参数:
|
||
- `contextType`:上下文类型(`task`/`customer`/`coach`/`general`,必填)
|
||
- `contextId`:上下文 ID(必填)
|
||
- `page`:页码(默认 1)
|
||
- `page_size`:每页条数(默认 50,最大 100)
|
||
|
||
对话复用规则:`task` 始终复用(无时限);`customer`/`coach` ≤ 3 天复用、> 3 天新建;`general` 始终新建。
|
||
|
||
响应:`ChatMessagesResponse`(含 `chatId`/`items`/`total`/`page`/`pageSize`)
|
||
|
||
### GET `/api/xcx/chat/{chat_id}/messages`
|
||
通过 chatId 查询消息(CHAT-2a)。消息按 `created_at` 正序。
|
||
|
||
路径参数:
|
||
- `chat_id`:对话 ID
|
||
|
||
查询参数:
|
||
- `page`:页码(默认 1)
|
||
- `page_size`:每页条数(默认 50,最大 100)
|
||
|
||
响应:`ChatMessagesResponse`
|
||
|
||
每条消息包含:`id`、`role`(`user`/`assistant`)、`content`、`createdAt`、`referenceCard`(可选,含 `type`/`title`/`summary`/`data` 键值对)。
|
||
|
||
### POST `/api/xcx/chat/{chat_id}/messages`
|
||
发送消息并获取同步 AI 回复(CHAT-3)。chatId 归属验证:不属于当前用户返回 403。
|
||
|
||
请求体:
|
||
```json
|
||
{ "content": "消息内容" }
|
||
```
|
||
|
||
响应:`SendMessageResponse`(含 `userMessage` 和 `aiReply`,各含 `id`/`content`/`createdAt`)
|
||
|
||
AI 失败降级:用户消息仍保存,`aiReply.content` 返回错误提示,HTTP 200。
|
||
|
||
### POST `/api/xcx/chat/stream`
|
||
SSE 流式对话端点(CHAT-4)。chatId 归属验证在流开始前完成。
|
||
|
||
请求体:
|
||
```json
|
||
{ "chatId": 1, "content": "消息内容" }
|
||
```
|
||
|
||
响应:`text/event-stream`(不经过 ResponseWrapper 包装)
|
||
|
||
SSE 事件类型:
|
||
- `event: message` — `data: {"token": "文本片段"}`
|
||
- `event: done` — `data: {"messageId": 123, "createdAt": "ISO8601"}`
|
||
- `event: error` — `data: {"message": "错误描述"}`
|
||
|
||
---
|
||
|
||
## 15. 维客线索 `/api/member-retention-clue`
|
||
|
||
替代原 `member-birthday` 端点,提供维客线索管理能力。
|
||
|
||
---
|
||
|
||
## 19. 租户管理后台认证 `/api/tenant/auth`
|
||
|
||
租户管理员独立认证体系,与小程序认证完全隔离。使用用户名+密码登录,JWT `aud=tenant-admin`。
|
||
|
||
### POST `/api/tenant/auth/login`
|
||
租户管理员登录。
|
||
|
||
请求体:
|
||
```json
|
||
{ "username": "admin01", "password": "..." }
|
||
```
|
||
响应:
|
||
```json
|
||
{ "access_token": "...", "refresh_token": "...", "token_type": "bearer" }
|
||
```
|
||
错误码:
|
||
- 401:用户名或密码错误(统一消息,不区分)
|
||
- 403:账号已被禁用(`is_active=false`)
|
||
|
||
### POST `/api/tenant/auth/refresh`
|
||
刷新租户管理员令牌。验证 `aud=tenant-admin` + `type=refresh`。
|
||
|
||
请求体:
|
||
```json
|
||
{ "refresh_token": "..." }
|
||
```
|
||
响应:同登录响应格式。
|
||
|
||
错误码:
|
||
- 401:无效的刷新令牌 / 令牌类型不匹配
|
||
|
||
---
|
||
|
||
## 20. 租户用户审核与管理 `/api/tenant`
|
||
|
||
所有端点需租户管理员 JWT(`aud=tenant-admin`),数据自动按 `managed_site_ids` 隔离。
|
||
|
||
### GET `/api/tenant/my-sites`
|
||
当前管理员管辖的店铺列表(用于前端筛选下拉)。
|
||
|
||
响应:
|
||
```json
|
||
[
|
||
{ "siteId": 1, "siteName": "XX球房", "siteCode": "LLQ001" }
|
||
]
|
||
```
|
||
|
||
### GET `/api/tenant/roles`
|
||
小程序可用角色列表(从 `auth.roles` 动态读取,排除 `tenant_admin`/`site_admin` 管理类角色)。
|
||
|
||
响应:`RoleItem[]`(含 `id`/`code`/`name`/`description`)
|
||
|
||
### GET `/api/tenant/site-staff`
|
||
按角色 + 门店查询人员候选列表。直连 ETL 库底层表,手动 `site_id` 过滤(FDW 视图 RLS 跨库不生效)。
|
||
|
||
查询参数:
|
||
- `role`:角色 code(`coach` → 查 `dwd.dim_assistant`,其他 → 查 `dwd.dim_staff`,必填)
|
||
- `site_id`:店铺 site_id(与 `site_code` 二选一)
|
||
- `site_code`:店铺编号(与 `site_id` 二选一)
|
||
|
||
响应:`StaffCandidate[]`(含 `id`/`identityLabel`/`name`/`mobile`/`entryTime`/`source`)
|
||
|
||
说明:
|
||
- coach 角色的 `identityLabel` 从 `dws.cfg_assistant_level_price` 配置表读取中文等级名,回退为数字
|
||
- 非 coach 角色的 `identityLabel` 使用 `dim_staff.job` 字段(职位名称)
|
||
|
||
### GET `/api/tenant/applications`
|
||
申请列表,支持状态筛选、门店筛选和分页。排除 `cancelled` 状态申请。
|
||
|
||
查询参数:
|
||
- `status`:按状态筛选(`pending`/`approved`/`rejected`,可选)
|
||
- `site_id`:按门店筛选(可选)
|
||
- `page`:页码(默认 1)
|
||
- `page_size`:每页条数(默认 20,最大 100)
|
||
|
||
隔离策略:
|
||
- `tenant_admin`:按 `tenant_id` 过滤,覆盖该租户下所有店铺(不受 JWT `managed_site_ids` 限制)
|
||
- `site_admin`:按 `managed_site_ids` 精确过滤
|
||
|
||
响应:`{ items, total, page, pageSize }`
|
||
|
||
说明:`phone` 字段取自 `auth.user_applications.phone`(申请时填写,NOT NULL),非 `auth.users.phone`。
|
||
|
||
### GET `/api/tenant/applications/{id}/match-suggestions`
|
||
关联匹配建议。通过 `site_code_mapping` 解析 `site_id`,在 `v_dim_assistant`(`scd2_is_current=1`)和 `v_dim_staff` + `v_dim_staff_ex` 中按 phone 匹配。
|
||
|
||
响应:`MatchSuggestion[]`(含 `assistantId`/`staffId`/`name`/`number`/`sourceTable`)
|
||
|
||
### POST `/api/tenant/applications/{id}/approve`
|
||
审核通过。事务内多表写入:`users.status='approved'` → `user_site_roles` → `user_assistant_binding` → `user_applications.status='approved'`。
|
||
|
||
请求体:
|
||
```json
|
||
{ "role": "coach", "assistantId": 123, "staffId": null }
|
||
```
|
||
错误码:
|
||
- 409:该申请已被处理(非 pending 状态)
|
||
|
||
### POST `/api/tenant/applications/{id}/reject`
|
||
审核拒绝。
|
||
|
||
请求体:
|
||
```json
|
||
{ "reason": "拒绝原因" }
|
||
```
|
||
错误码:
|
||
- 409:该申请已被处理
|
||
|
||
### GET `/api/tenant/users`
|
||
已通过审核用户列表,支持角色筛选、关键词搜索和分页。
|
||
|
||
查询参数:
|
||
- `role`:按角色筛选(可选)
|
||
- `keyword`:关键词搜索(姓名/手机号,可选)
|
||
- `page`:页码(默认 1)
|
||
- `page_size`:每页条数(默认 20,最大 100)
|
||
|
||
响应:`{ items, total, page, pageSize }`
|
||
|
||
### PATCH `/api/tenant/users/{id}`
|
||
编辑用户角色/门店/状态。
|
||
|
||
请求体(部分更新):
|
||
```json
|
||
{ "role": "staff", "siteId": 2, "status": "disabled" }
|
||
```
|
||
错误码:
|
||
- 403:目标门店不在管辖范围内
|
||
- 404:用户不存在
|
||
|
||
### PUT `/api/tenant/users/{id}/binding`
|
||
更新用户助教/员工绑定。
|
||
|
||
请求体:
|
||
```json
|
||
{ "assistantId": 456, "staffId": null }
|
||
```
|
||
|
||
---
|
||
|
||
## 21. 租户 Excel 上传 `/api/tenant/excel`
|
||
|
||
所有端点需租户管理员 JWT。支持 4 种模板:财务支出(expense)、团购收入(platform_income)、助教奖罚(salary_adj)、充值业绩归属(recharge_commission)。
|
||
|
||
### POST `/api/tenant/excel/upload`
|
||
上传 Excel 文件,执行格式校验 → 人员匹配 → 冲突检测。
|
||
|
||
请求:`multipart/form-data`
|
||
- `file`:Excel 文件(.xlsx/.xls)
|
||
- `upload_type`:模板类型
|
||
- `site_id`:门店 ID
|
||
|
||
响应:
|
||
```json
|
||
{
|
||
"errors": [],
|
||
"warnings": [{ "rowIndex": 2, "column": "助教姓名", "message": "未匹配到助教/员工:张三" }],
|
||
"passedRows": [...],
|
||
"uploadId": 1,
|
||
"conflicts": [{ "rowIndex": 3, "fieldDiffs": [{ "field": "金额", "oldValue": "100.00", "newValue": "200.00" }] }]
|
||
}
|
||
```
|
||
错误码:
|
||
- 400:无效的模板类型 / 无效的 Excel 文件 / 文件内容为空 / 解析失败
|
||
|
||
### POST `/api/tenant/excel/confirm`
|
||
确认写入。单事务写入目标表,失败回滚整批。
|
||
|
||
请求体:
|
||
```json
|
||
{
|
||
"uploadId": 1,
|
||
"resolutions": [
|
||
{ "rowIndex": 3, "action": "replace" },
|
||
{ "rowIndex": 5, "action": "keep" }
|
||
]
|
||
}
|
||
```
|
||
响应:
|
||
```json
|
||
{ "message": "写入成功", "inserted": 10, "updated": 2, "resolved": 3 }
|
||
```
|
||
错误码:
|
||
- 404:上传记录不存在
|
||
- 409:该上传批次已被处理
|
||
|
||
### GET `/api/tenant/excel/logs`
|
||
上传记录列表,分页,按 `managed_site_ids` 隔离。
|
||
|
||
查询参数:
|
||
- `page`:页码(默认 1)
|
||
- `page_size`:每页条数(默认 20,最大 100)
|
||
|
||
响应:`{ items, total, page, pageSize }`
|
||
|
||
### GET `/api/tenant/excel/template/{type}`
|
||
下载空白 Excel 模板文件(含表头和格式说明)。
|
||
|
||
路径参数:
|
||
- `type`:模板类型(`expense`/`platform_income`/`salary_adj`/`recharge_commission`)
|
||
|
||
响应:`application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`
|
||
|
||
---
|
||
|
||
## 22. 租户维客线索管理 `/api/tenant`
|
||
|
||
所有端点需租户管理员 JWT。线索操作先校验 `site_id` 是否在管辖范围内,不在则返回 404(避免泄露线索存在性)。
|
||
|
||
### GET `/api/tenant/customers/search`
|
||
客户搜索。在管辖门店范围内搜索 `v_dim_member`(nickname 模糊匹配 OR mobile 精确匹配,`scd2_is_current=1`)。手机号脱敏返回。
|
||
|
||
查询参数:
|
||
- `keyword`:搜索关键词(必填,最少 1 字符)
|
||
- `site_id`:指定门店 ID 筛选(可选)
|
||
|
||
响应:
|
||
```json
|
||
{
|
||
"items": [
|
||
{ "memberId": 1, "nickname": "张三", "mobileMasked": "138****1234", "siteId": 1, "siteName": "XX球房" }
|
||
]
|
||
}
|
||
```
|
||
|
||
### GET `/api/tenant/customers/{member_id}/clues`
|
||
该客户在管辖门店范围内的全部线索,支持 source 和 is_hidden 筛选。
|
||
|
||
查询参数:
|
||
- `source`:按来源筛选(`manual`/`ai_consumption`/`ai_note`,可选)
|
||
- `is_hidden`:按隐藏状态筛选(可选)
|
||
|
||
响应:`{ items: ClueListItem[] }`
|
||
|
||
### PATCH `/api/tenant/clues/{id}`
|
||
编辑线索 category/summary/detail。
|
||
|
||
请求体:
|
||
```json
|
||
{ "category": "消费习惯", "summary": "每周消费 2-3 次", "detail": "偏好周末下午时段" }
|
||
```
|
||
错误码:
|
||
- 404:线索不存在(或不在管辖范围)
|
||
- 422:无效的线索大类 / 摘要为空或超长
|
||
|
||
### DELETE `/api/tenant/clues/{id}`
|
||
物理删除线索。
|
||
|
||
错误码:
|
||
- 404:线索不存在
|
||
|
||
### PATCH `/api/tenant/clues/{id}/visibility`
|
||
切换线索 is_hidden 状态。
|
||
|
||
请求体:
|
||
```json
|
||
{ "isHidden": true }
|
||
```
|
||
|
||
---
|
||
|
||
## 23. 租户店铺管理员 CRUD `/api/tenant/site-admins`
|
||
|
||
所有端点需租户管理员 JWT(`aud=tenant-admin`),且 `admin_type` 必须为 `tenant_admin`。店铺管理员(`admin_type='site_admin'`)无权访问。
|
||
|
||
### GET `/api/tenant/site-admins`
|
||
店铺管理员列表。
|
||
|
||
响应:`SiteAdmin[]`(含 `id`/`username`/`displayName`/`managedSiteIds`/`isActive`/`createdAt`)
|
||
|
||
### POST `/api/tenant/site-admins`
|
||
创建店铺管理员。`admin_type` 自动设为 `site_admin`,`tenant_id` 继承当前租户管理员。
|
||
|
||
请求体:
|
||
```json
|
||
{
|
||
"username": "LL0001zhangsan",
|
||
"password": "...",
|
||
"displayName": "张三",
|
||
"managedSiteIds": [1]
|
||
}
|
||
```
|
||
|
||
- 409:用户名已存在
|
||
|
||
### PATCH `/api/tenant/site-admins/{id}`
|
||
编辑店铺管理员(displayName / managedSiteIds / isActive)。
|
||
|
||
- 404:管理员不存在或不属于当前租户
|
||
- 422:至少需要提供一个修改字段
|
||
|
||
### DELETE `/api/tenant/site-admins/{id}`
|
||
软删除店铺管理员(`deleted_at` 设为当前时间)。
|
||
|
||
- 404:管理员不存在
|
||
- 409:管理员已被删除
|
||
|
||
### POST `/api/tenant/site-admins/{id}/reset-password`
|
||
重置店铺管理员密码。
|
||
|
||
请求体:
|
||
```json
|
||
{ "newPassword": "..." }
|
||
```
|
||
|
||
- 404:管理员不存在
|
||
|
||
---
|
||
|
||
## 24. 管理端租户管理员 CRUD `/api/admin`
|
||
|
||
所有端点需管理后台 JWT(`admin_users` 表认证)。
|
||
|
||
### GET `/api/admin/tenant-admins`
|
||
管理员列表,支持分页和关键词搜索。
|
||
|
||
查询参数:
|
||
- `page`:页码(默认 1)
|
||
- `page_size`:每页条数(默认 20,最大 100)
|
||
- `keyword`:关键词搜索(用户名/显示名称,可选)
|
||
- `include_inactive`:是否包含已禁用管理员(bool,默认 `false`)
|
||
|
||
响应:`{ items, total, page, pageSize }`
|
||
|
||
每条记录新增 `tenant_name` 字段(来自 `biz.tenants` JOIN)。
|
||
|
||
### POST `/api/admin/tenant-admins`
|
||
创建租户管理员。密码 bcrypt 哈希存储。`tenant_id` 从 `biz.tenants` 选择,`managed_site_ids` 从 `biz.sites` 选择。
|
||
|
||
请求体:
|
||
```json
|
||
{
|
||
"username": "admin01",
|
||
"password": "...",
|
||
"displayName": "管理员01",
|
||
"tenantId": 1,
|
||
"managedSiteIds": [1, 2, 3]
|
||
}
|
||
```
|
||
响应(201):
|
||
```json
|
||
{ "id": 1, "createdAt": "2026-03-20T10:00:00" }
|
||
```
|
||
错误码:
|
||
- 409:用户名已存在
|
||
|
||
### PATCH `/api/admin/tenant-admins/{id}`
|
||
编辑租户管理员(username / display_name / managed_site_ids / is_active)。
|
||
|
||
请求体(部分更新):
|
||
```json
|
||
{ "username": "newname", "displayName": "新名称", "managedSiteIds": [1, 2], "isActive": false }
|
||
```
|
||
错误码:
|
||
- 404:租户管理员不存在
|
||
- 409:用户名已存在(修改 username 时冲突)
|
||
- 422:至少需要提供一个修改字段
|
||
|
||
### DELETE `/api/admin/tenant-admins/{id}`
|
||
软删除租户管理员(`is_active=false`)。
|
||
|
||
错误码:
|
||
- 404:租户管理员不存在
|
||
- 409:管理员已被禁用
|
||
|
||
### POST `/api/admin/tenant-admins/{id}/reset-password`
|
||
重置租户管理员密码。
|
||
|
||
请求体:
|
||
```json
|
||
{ "newPassword": "..." }
|
||
```
|
||
错误码:
|
||
- 404:租户管理员不存在
|
||
|
||
### WebSocket `/ws/logs/{execution_id}`
|
||
实时日志推送。连接后自动接收指定执行的日志流。
|
||
|
||
---
|
||
|
||
## 25. 注册体系管理 `/api/admin`(admin_registry)
|
||
|
||
管理「连接器 → 租户 → 店铺」三级注册体系。所有端点需管理后台 JWT。
|
||
|
||
### GET `/api/admin/tenants`
|
||
所有活跃租户列表(含连接器名称)。
|
||
|
||
响应:
|
||
```json
|
||
[
|
||
{
|
||
"id": 1,
|
||
"tenant_id": 12345,
|
||
"tenant_name": "朗朗桌球",
|
||
"connector_name": "飞球",
|
||
"is_active": true
|
||
}
|
||
]
|
||
```
|
||
|
||
### GET `/api/admin/tenants/{tenant_id}/sites`
|
||
指定租户下所有活跃店铺。
|
||
|
||
路径参数:
|
||
- `tenant_id`:租户 ID(`biz.tenants.id`)
|
||
|
||
响应:
|
||
```json
|
||
[
|
||
{
|
||
"id": 1,
|
||
"site_id": 67890,
|
||
"site_name": "XX球房",
|
||
"site_code": "LLQ001",
|
||
"site_label": "朗朗桌球XX店",
|
||
"is_active": true
|
||
}
|
||
]
|
||
```
|
||
|
||
### PUT `/api/admin/sites/{site_id}/site-code`
|
||
设置/修改店铺简写ID。事务内执行:旧 code 标记退役 → 新 code 插入历史 → 更新 sites.site_code。
|
||
|
||
路径参数:
|
||
- `site_id`:店铺 ID(`biz.sites.id`)
|
||
|
||
请求体:
|
||
```json
|
||
{ "new_code": "LLQ002" }
|
||
```
|
||
|
||
响应:
|
||
```json
|
||
{
|
||
"site_id": 1,
|
||
"old_code": "LLQ001",
|
||
"new_code": "LLQ002",
|
||
"history_cleaned": false
|
||
}
|
||
```
|
||
|
||
校验规则:
|
||
- 格式:6 位字符,3+3 模式,统一大写存储
|
||
- 全局唯一:`biz.sites.site_code` + `biz.site_code_history.site_code` 均不可重复
|
||
|
||
错误码:
|
||
- 400:格式不合法
|
||
- 409:简写ID 已被占用
|
||
|
||
### GET `/api/admin/sites/{site_id}/site-code-history`
|
||
查看店铺简写ID 变更历史。
|
||
|
||
路径参数:
|
||
- `site_id`:店铺 ID(`biz.sites.id`)
|
||
|
||
响应:
|
||
```json
|
||
[
|
||
{
|
||
"id": 1,
|
||
"site_code": "LLQ001",
|
||
"is_current": false,
|
||
"created_at": "2026-03-20T10:00:00",
|
||
"retired_at": "2026-03-22T15:30:00"
|
||
},
|
||
{
|
||
"id": 2,
|
||
"site_code": "LLQ002",
|
||
"is_current": true,
|
||
"created_at": "2026-03-22T15:30:00",
|
||
"retired_at": null
|
||
}
|
||
]
|
||
```
|
||
|
||
### POST `/api/admin/sites/sync`
|
||
手动触发店铺信息同步。通过 FDW 读取 ETL 库 `dwd.dim_site`(`scd2_is_current=1`),对比 `biz.sites`,新增/更新店铺信息。
|
||
|
||
响应:
|
||
```json
|
||
{ "inserted": 3, "updated": 1 }
|
||
```
|
||
|
||
---
|
||
|
||
## 26. AI 监控后台 `/api/admin/ai`
|
||
|
||
所有端点需管理后台 JWT(`Depends(require_admin)`)。P15 新增,13 个端点。
|
||
|
||
### GET `/api/admin/ai/dashboard`
|
||
AI 运行总览。返回今日统计、7 天趋势、App 分布、Token 预算、告警列表、App 健康状态。
|
||
|
||
查询参数:
|
||
- `site_id`:门店筛选(可选)
|
||
|
||
### GET `/api/admin/ai/trigger-jobs`
|
||
调度任务分页列表 + 今日去重统计。
|
||
|
||
查询参数:
|
||
- `event_type`、`status`、`site_id`、`date_from`、`date_to`:筛选(均可选)
|
||
- `page`:页码(默认 1)
|
||
- `page_size`:每页条数(默认 20)
|
||
|
||
### GET `/api/admin/ai/trigger-jobs/{job_id}`
|
||
调度任务详情(含 payload、error_message)。
|
||
|
||
### POST `/api/admin/ai/trigger-jobs/{job_id}/retry`
|
||
手动重跑:创建新 trigger_job(`is_forced=true`),异步执行。
|
||
|
||
### GET `/api/admin/ai/run-logs`
|
||
调用记录分页列表。
|
||
|
||
查询参数:
|
||
- `app_type`、`status`、`trigger_type`、`site_id`、`date_from`、`date_to`:筛选(均可选)
|
||
- `page`、`page_size`:分页
|
||
|
||
### GET `/api/admin/ai/run-logs/{log_id}`
|
||
调用记录详情(含完整 prompt/response/error,不脱敏)。
|
||
|
||
### POST `/api/admin/ai/cache/invalidate`
|
||
批量缓存失效。
|
||
|
||
请求体:
|
||
```json
|
||
{ "site_id": 123, "app_type": "app3_clue", "member_id": 456 }
|
||
```
|
||
`site_id` 必填,`app_type` 和 `member_id` 可选。
|
||
|
||
### GET `/api/admin/ai/budget`
|
||
Token 预算使用情况(日/月已用量、上限、百分比)。
|
||
|
||
### POST `/api/admin/ai/batch-run`
|
||
创建批量执行请求,返回预估(不立即执行)。
|
||
|
||
请求体:
|
||
```json
|
||
{ "app_types": ["app3_clue", "app7_customer_analysis"], "member_ids": [1, 2, 3], "site_id": 123 }
|
||
```
|
||
响应:`{ batch_id, estimated_calls, estimated_tokens }`
|
||
|
||
### POST `/api/admin/ai/batch-run/confirm`
|
||
确认批量执行(`batch_id` 有效期 10 分钟)。
|
||
|
||
请求体:
|
||
```json
|
||
{ "batch_id": "uuid" }
|
||
```
|
||
错误码:
|
||
- 400:batch_id 无效或已过期
|
||
- 409:batch_id 已确认
|
||
|
||
### GET `/api/admin/ai/alerts`
|
||
告警列表(`ai_run_logs` WHERE status IN ('failed','timeout','circuit_open'))。
|
||
|
||
查询参数:
|
||
- `alert_status`:告警状态筛选(可选)
|
||
- `site_id`:门店筛选(可选)
|
||
- `page`、`page_size`:分页
|
||
|
||
### POST `/api/admin/ai/alerts/{log_id}/ack`
|
||
确认告警:`alert_status → acknowledged`。
|
||
|
||
### POST `/api/admin/ai/alerts/{log_id}/ignore`
|
||
忽略告警:`alert_status → ignored`。
|
||
|
||
---
|
||
|
||
## 27. 开发调试全链路日志 `/api/admin/dev-trace`
|
||
|
||
所有端点需管理后台 JWT + admin 角色(`Depends(require_admin)`),非 admin 返回 403。
|
||
|
||
DevTrace 模块提供开发阶段的全链路请求追踪能力,覆盖 HTTP 请求、SSE 流式响应、WebSocket 连接、后台 Job 执行、异常/错误、数据库连接生命周期和中间件层。日志以 JSON Lines 格式写入本地文件系统,仅用于开发调试,不影响生产环境。
|
||
|
||
### GET `/api/admin/dev-trace/dates`
|
||
返回有日志数据的日期列表。
|
||
|
||
响应:
|
||
```json
|
||
{ "dates": ["2026-03-23", "2026-03-22", "2026-03-21"] }
|
||
```
|
||
|
||
### GET `/api/admin/dev-trace/requests`
|
||
按条件分页查询请求列表。
|
||
|
||
查询参数:
|
||
- `date`:日期筛选(格式 `YYYY-MM-DD`,必填)
|
||
- `start_time`:开始时间(`HH:MM`,可选)
|
||
- `end_time`:结束时间(`HH:MM`,可选)
|
||
- `trace_type`:追踪类型筛选(`http`/`sse`/`ws`/`job`,可选)
|
||
- `method`:HTTP 方法筛选(`GET`/`POST`/`PUT`/`DELETE`,可选)
|
||
- `path_contains`:路径关键词模糊匹配(可选)
|
||
- `status_code`:HTTP 状态码精确匹配(可选)
|
||
- `min_duration`:最小耗时(毫秒,可选)
|
||
- `has_error`:是否包含错误(bool,可选)
|
||
- `span_type`:包含指定 span 类型的记录(可选)
|
||
- `page`:页码(默认 1)
|
||
- `page_size`:每页条数(默认 50)
|
||
|
||
响应:`{ items, total, page, pageSize }`
|
||
|
||
### GET `/api/admin/dev-trace/request/{request_id}`
|
||
返回指定 request_id 的完整 trace 记录(含所有 spans)。
|
||
|
||
路径参数:
|
||
- `request_id`:请求 ID
|
||
|
||
响应包含完整字段:`request_id`、`trace_type`、`timestamp`、`method`、`path`、`status_code`、`total_duration_ms`、`user_id`、`site_id`、`db_query_count`、`db_total_ms`、`error`、`spans`(span 数组,每个含 `span_type`/`module`/`function`/`description_zh`/`duration_ms`/`params`/`result_summary`/`extra`)。
|
||
|
||
错误码:
|
||
- 404:指定 request_id 不存在
|
||
|
||
### POST `/api/admin/dev-trace/cleanup`
|
||
按日期范围手动清理日志。
|
||
|
||
请求体:
|
||
```json
|
||
{ "start_date": "2026-03-20", "end_date": "2026-03-21" }
|
||
```
|
||
|
||
响应:
|
||
```json
|
||
{ "deleted_dates": ["2026-03-20", "2026-03-21"], "deleted_count": 2 }
|
||
```
|
||
|
||
### GET `/api/admin/dev-trace/settings`
|
||
返回当前 trace 设置。
|
||
|
||
响应:
|
||
```json
|
||
{
|
||
"enabled": true,
|
||
"log_dir": "export/dev-trace-logs",
|
||
"retention_days": 7,
|
||
"log_sql": true,
|
||
"log_params": true,
|
||
"coverage_scan_interval_minutes": 60
|
||
}
|
||
```
|
||
|
||
### PUT `/api/admin/dev-trace/settings`
|
||
更新运行时设置(不需重启,即时生效)。
|
||
|
||
请求体(部分更新):
|
||
```json
|
||
{ "enabled": false, "retention_days": 14, "log_sql": false }
|
||
```
|
||
|
||
响应:更新后的完整设置对象(同 GET 响应格式)。
|
||
|
||
### GET `/api/admin/dev-trace/coverage`
|
||
返回最近一次覆盖率扫描结果。
|
||
|
||
响应:
|
||
```json
|
||
{
|
||
"scanned_at": "2026-03-23T10:00:00",
|
||
"route": { "total": 30, "covered": 28, "uncovered": ["func_a", "func_b"] },
|
||
"service": { "total": 45, "covered": 40, "uncovered": ["svc_x", "svc_y", "svc_z", "svc_w", "svc_v"] },
|
||
"job": { "total": 4, "covered": 4, "uncovered": [] },
|
||
"sse": { "total": 1, "covered": 1, "uncovered": [] },
|
||
"ws": { "total": 1, "covered": 1, "uncovered": [] }
|
||
}
|
||
```
|
||
|
||
### POST `/api/admin/dev-trace/coverage/scan`
|
||
手动触发覆盖率扫描。
|
||
|
||
响应:扫描完成后的覆盖率结果(同 GET 响应格式)。
|
||
|
||
---
|
||
|
||
## 28. 数据库健康监控 `/api/admin/db-health`
|
||
|
||
所有端点需管理后台 JWT。
|
||
|
||
### GET `/api/admin/db-health`
|
||
返回 4 个数据库的健康状态(etl_feiqiu / test_etl_feiqiu / zqyy_app / test_zqyy_app)。
|
||
|
||
对每个库执行诊断 SQL:`pg_stat_activity`(连接池)、`pg_database_size()`(大小)、慢查询统计。连接失败时返回 `status: "disconnected"`,其余字段为 null。即使所有库都连接失败,仍返回 HTTP 200。
|
||
|
||
响应:
|
||
```json
|
||
[
|
||
{
|
||
"db_name": "zqyy_app",
|
||
"status": "connected",
|
||
"active_connections": 5,
|
||
"idle_connections": 3,
|
||
"db_size_mb": 128.45,
|
||
"slow_query_count": 0
|
||
},
|
||
{
|
||
"db_name": "etl_feiqiu",
|
||
"status": "disconnected",
|
||
"active_connections": null,
|
||
"idle_connections": null,
|
||
"db_size_mb": null,
|
||
"slow_query_count": null
|
||
}
|
||
]
|
||
```
|
||
|
||
---
|
||
|
||
## 29. 触发器统一视图 `/api/admin/triggers`
|
||
|
||
所有端点需管理后台 JWT。
|
||
|
||
### GET `/api/admin/triggers/unified`
|
||
聚合三张表的触发器数据,返回统一格式列表。
|
||
|
||
数据源:
|
||
- `biz.trigger_jobs`(业务触发器)→ `source="biz"`
|
||
- `biz.ai_trigger_jobs`(AI 事件链,最近 100 条)→ `source="ai"`
|
||
- `public.scheduled_tasks`(ETL 调度)→ `source="etl"`
|
||
|
||
某数据源查询失败时记录日志,返回其他数据源数据。
|
||
|
||
响应:
|
||
```json
|
||
[
|
||
{
|
||
"id": 1,
|
||
"name": "daily_sync",
|
||
"source": "biz",
|
||
"trigger_condition": "cron",
|
||
"status": "active",
|
||
"last_run_at": "2026-03-25T03:00:00",
|
||
"next_run_at": "2026-03-26T03:00:00",
|
||
"last_error": null
|
||
}
|
||
]
|
||
```
|
||
|
||
---
|
||
|
||
## 30. 触发器配置编辑 `/api/trigger-jobs`
|
||
|
||
在已有的 `/api/trigger-jobs` 路由模块中新增 PATCH 端点。需管理后台 JWT。
|
||
|
||
### PATCH `/api/trigger-jobs/{id}/config`
|
||
编辑触发器的 `cron_expression` 或 `interval_seconds`。
|
||
|
||
仅 merge 请求中非 null 的字段到 `trigger_config` JSONB,不覆盖其他已有字段。更新后重新计算 `next_run_at`。
|
||
|
||
请求体(部分更新,至少提供一个字段):
|
||
```json
|
||
{ "cron_expression": "0 3 * * 1", "interval_seconds": null }
|
||
```
|
||
|
||
校验规则:
|
||
- 空请求体(两个字段均为 null)→ 422
|
||
- `cron_expression` 格式无效(非 5 字段 / 超范围值)→ 422
|
||
- `interval_seconds < 1` → 422
|
||
- `job_id` 不存在 → 404
|
||
|
||
响应:更新后的完整 `TriggerJobItem`。
|
||
|
||
---
|
||
|
||
## 错误码约定
|
||
|
||
| HTTP 状态码 | 含义 |
|
||
|-------------|------|
|
||
| 200 | 成功 |
|
||
| 201 | 创建成功 |
|
||
| 400 | 请求参数错误 / SQL 执行错误 |
|
||
| 401 | 未认证 / 令牌无效 / 受限令牌 |
|
||
| 404 | 资源不存在 |
|
||
| 408 | 查询超时 |
|
||
| 409 | 状态冲突(如删除非 pending 任务) |
|
||
| 422 | 请求体验证失败 |
|
||
| 500 | 服务器内部错误 |
|