feat: 累积功能变更 — 聊天集成、租户管理、小程序更新、ETL 增强、迁移脚本
包含多个会话的累积代码变更: - 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>
This commit is contained in:
@@ -255,15 +255,42 @@ Flow 列表:
|
||||
### 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`
|
||||
@@ -422,7 +449,15 @@ MVP 全链路验证端点,从 `test."xcx-test"` 表读取数据。
|
||||
查询参数:
|
||||
- `month`:月份(格式 `YYYY-MM`,默认当月)
|
||||
|
||||
响应包含:`currentTier`/`nextTier`/`upgradeHoursNeeded`/`upgradeBonus`(收入档位)、`thisMonthRecords`(DateGroup 分组)、`lastMonthIncome`、`incomeItems`(含 desc)、`newCustomers`/`regularCustomers`。
|
||||
响应包含:
|
||||
- `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)。返回指定月份的服务记录明细,按日期分组,支持分页。
|
||||
@@ -613,11 +648,768 @@ SSE 事件类型:
|
||||
|
||||
替代原 `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 状态码 | 含义 |
|
||||
|
||||
Reference in New Issue
Block a user