Files
Neo-ZQYY/apps/backend/docs/API-REFERENCE.md
Neo 80bda9b991 chore(audit): 2026-04-20 历史批次预审 + 文档同步 + .gitignore 修正
- 新增 docs/audit/changes/2026-04-20__historical-batch-pre-audit.md
  157 文件分批盘点审计(7 条主线 + 10 项高/中风险 + 2 份迁移 SQL DDL 清单)
- 补追 docs/audit/changes/2026-04-15__meituan-settle-core-sync.md
  原审计产物因 .gitignore 屏蔽长期未入仓,本次一并追回
- 刷新 docs/audit/audit_dashboard.md(33 条审计记录)
- .gitignore 白名单放行 docs/audit/changes/*.md 与 audit_dashboard.md
  同时屏蔽 changes/changes/ 嵌套误产物目录
- 新增 docs/specs/audit-gap-recovery/tasks.md
  扫描嵌套目录发现 96 份 D 类孤本(从未入过 git history),
  生成独立 PRD 供单开任务清理与补追
- 文档同步(高风险项):
  - apps/backend/docs/API-REFERENCE.md (+69)
  - apps/miniprogram/README.md (+50)
  - apps/etl/connectors/feiqiu/docs/architecture/data_flow.md (+52/-2)
  - apps/etl/connectors/feiqiu/docs/architecture/system_overview.md (+5/-3)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 06:32:58 +08:00

43 KiB
Raw Blame History

API 参考手册

后端 API 基于 FastAPI 构建,所有端点均以 /api/ 为前缀。
在线文档:启动后访问 http://localhost:8000/docsSwagger UI/redocReDoc

全局响应格式RNS1.0

所有 JSON 成功响应HTTP 2xx自动包装为统一格式

{ "code": 0, "data": { ... } }

异常响应格式:

{ "code": 401, "message": "未认证" }

规则:

  • code=0 表示成功,data 为业务数据
  • code≠0 表示错误,code 为 HTTP 状态码,message 为错误描述
  • SSE 流式端点(text/event-stream)和非 JSON 响应不包装,直接透传
  • 所有小程序 API 响应字段名为 camelCaseuserIdstoreName

1. 管理后台认证 /api/auth

POST /api/auth/login

管理后台用户名密码登录。

请求体:

{ "username": "admin", "password": "..." }

响应:

{ "access_token": "...", "refresh_token": "...", "token_type": "bearer" }

POST /api/auth/refresh

刷新访问令牌。

请求体:

{ "refresh_token": "..." }

2. 小程序认证 /api/xcx-auth

小程序用户的完整生命周期:微信登录 → 提交申请 → 管理员审批 → 正式使用。

POST /api/xcx-auth/login

微信登录。用 wx.login() 获取的 code 换取 JWT。

请求体:

{ "code": "微信临时登录凭证" }

响应:

{
  "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_idroles

POST /api/xcx-auth/apply

提交入驻申请。需受限令牌或完整令牌。

请求体:

{
  "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

查询当前用户状态和申请记录。需受限令牌或完整令牌。

响应:

{
  "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

切换当前门店,返回新的令牌对。需完整令牌。

请求体:

{ "site_id": 2 }

POST /api/xcx-auth/refresh

刷新令牌。

请求体:

{ "refresh_token": "..." }

POST /api/xcx-auth/dev-login

开发模式 mock 登录(仅 WX_DEV_MODE=true 时注册)。

请求体:

{ "openid": "模拟openid", "status": "approved" }

说明:

  • status 可选,为空时保留已有用户当前状态,新用户默认 new
  • 仅开发/测试环境可用

3. 任务配置 /api/tasks

所有端点需 JWT 认证。

GET /api/tasks/registry

按业务域分组的 ETL 任务列表。

响应示例:

{
  "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 子进程。

请求体:TaskConfigSchemaflow、tasks、window 等)

响应:

{ "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 保护

请求体:

{ "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

所有端点需 JWTapproved 状态)。

GET /api/xcx/tasks

获取当前助教的活跃任务列表。

响应:TaskListItem[]

POST /api/xcx/tasks/{id}/pin

置顶任务。

POST /api/xcx/tasks/{id}/unpin

取消置顶。

POST /api/xcx/tasks/{id}/abandon

放弃任务(需填写原因)。

请求体:

{ "reason": "放弃原因" }

POST /api/xcx/tasks/{id}/restore

取消放弃,恢复为活跃状态。

GET /api/xcx/tasks/{id}

获取任务详情TASK-2。返回单个任务的完整信息含服务记录、备注、AI 分析、维客线索。

响应包含 customerId 字段,供前端跳转客户详情/对话页面使用。


14. 小程序绩效 /api/xcx/performance

所有端点需 JWTapproved 状态)。

GET /api/xcx/performance

绩效概览PERF-1。返回指定月份的收入档位、DateGroup 分组记录、新客/常客列表。

查询参数:

  • month:月份(格式 YYYY-MM,默认当月)

响应包含:

  • currentTier:当前档位助教到手单价(basicRate = 客户价 - base_deductionincentiveRate = 客户价 × (1 - bonus_deduction_ratio)
  • nextTier:下一档到手单价(同上公式,使用下一档抽成参数)
  • upgradeHoursNeeded距下一档所需小时数next_tier.min_hours - total_hours
  • upgradeBonus:升档后因抽成降低的节省额
  • incomeItems:始终 3 项基础课收入、激励课收入、Top3 销冠奖),金额为 0 时仍显示
  • thisMonthRecordsDateGroup 分组)、lastMonthIncomenewCustomers/regularCustomers

档位数据来源:cfg_performance_tier 配置表(通过 fdw_queries.get_performance_tiers() 查询)。

GET /api/xcx/performance/records

绩效明细PERF-2。返回指定月份的服务记录明细按日期分组支持分页。

查询参数:

  • year:年份(必填)
  • month:月份(1-12,必填)
  • page:页码(默认 1
  • page_size:每页条数(默认 20最大 100
  • coach_id:目标助教 ID可选管理者视角

权限分流(运行时通过 get_user_permissions() 实时检查,非 require_permission 中间件):

  • 不带 coach_id(查自己):需 view_tasks 权限,assistant_id 由当前登录用户绑定决定
  • coach_id(查他人):需 view_board_coach 权限(manager/head_coach/staffassistant_id 使用传入值;同 site 约束由 user.site_id 隐式保证
  • 缺少对应权限统一返回 HTTP 403 权限不足

对应服务层变更:performance_service.get_records() 新增 assistant_id_override 参数。

响应字段新增:

  • dateGroups[].records[].isScattered:散客标记(member_id ≤ 0 时为 true),前端据此将客户姓名置灰

15. 小程序备注 /api/xcx/notes

所有端点需 JWTapproved 状态)。

POST /api/xcx/notes

创建备注(含星星评分,可选关联任务)。

请求体:

{
  "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

所有端点需 JWTapproved 状态)。

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[]

[
  { "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≠allrecharge 板块为 nullcompare=0 时响应不含环比字段。

权限:view_board_finance

响应:FinanceBoardResponse(含 overview/recharge/revenue/cashflow/expense/coachAnalysis


17A. 小程序助教详情 /api/xcx/coaches

所有端点需 JWTapproved 状态)+ view_board_coach 权限。

GET /api/xcx/coaches/{coach_id}/banner

助教 banner 轻量信息。仅返回 id / name / level / storeName,用于 coach-service-records 等只需 banner 数据的页面首屏快速加载。

/{coach_id} 详情快一个数量级。

权限:view_board_coach

响应:CoachBannerResponse

{ "id": 123, "name": "张三", "level": "金牌", "storeName": "朗朗桌球(总店)" }

GET /api/xcx/coaches/{coach_id}

助教详情COACH-1。返回助教基础信息、绩效、收入、档位、任务分组、TOP 客户、近期服务、历史月份、备注。

权限:view_board_coach2026-03-27 权限改造 W4助教详情跟助教看板走

响应:CoachDetailResponse

字段说明:

  • storeName:助教所在门店名(跟随被查看助教所在门店,供小程序 banner 展示)
  • performancePerformanceMetrics,与任务页 PerformanceSummary 同源(来自 monthly_summary 实时值)。字段从 6 扩展到 25
    • 核心字段:totalHourstotalIncometotalCustomersmonthLabeltierNodesbasicHoursbonusHourscurrentTier(数组下标 0-basednextTierHourstierCompletedbonusMoneyincomeTrendincomeTrendDirprevMonthcurrentTierLabel
    • 详情专属扩展:customerBalancetasksCompleted
    • 兼容旧字段(前端渐进适配):monthlyHoursmonthlySalary
  • taskStatsCoachTaskStats,当月任务完成统计(按 task_type 分类计数,数据源 coach_tasks 表)
    • callbackfollow_up_visit 完成数
    • recallhigh_priority_recall + priority_recall 完成数
  • topCustomers[].isScattered:散客标识(member_id ≤ 0),前端据此将客户姓名置灰
  • serviceRecords[].isScattered:同上,作用于近期服务记录

17B. 小程序客户详情 /api/xcx/customers

所有端点需 JWTapproved 状态)+ view_board_customer 权限。

GET /api/xcx/customers/{customer_id}

客户详情CUST-1。返回客户基础信息、Banner 概览、AI 洞察、助教任务、心动助教、维客线索、消费记录、备注。

权限:view_board_customer2026-03-27 权限改造 W4客户详情跟客户看板走

响应:CustomerDetailResponse

字段说明(本次新增):

  • consumptionRecords[].foodDetail:自定义食品类目名称(string | null)。为空时前端降级展示「食品酒水」
  • notes[].creatorName:备注创建者姓名
  • notes[].creatorRole:备注创建者角色

18. 小程序 CHAT /api/xcx/chat

所有端点需 JWTapproved 状态)。替代原 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(对话 IDtitle(对话标题)、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

每条消息包含:idroleuser/assistant)、contentcreatedAtreferenceCard(可选,含 type/title/summary/data 键值对)。

POST /api/xcx/chat/{chat_id}/messages

发送消息并获取同步 AI 回复CHAT-3。chatId 归属验证:不属于当前用户返回 403。

请求体:

{ "content": "消息内容" }

响应:SendMessageResponse(含 userMessageaiReply,各含 id/content/createdAt

AI 失败降级:用户消息仍保存,aiReply.content 返回错误提示HTTP 200。

POST /api/xcx/chat/stream

SSE 流式对话端点CHAT-4。chatId 归属验证在流开始前完成。

请求体:

{ "chatId": 1, "content": "消息内容" }

响应:text/event-stream(不经过 ResponseWrapper 包装)

SSE 事件类型:

  • event: messagedata: {"token": "文本片段"}
  • event: donedata: {"messageId": 123, "createdAt": "ISO8601"}
  • event: errordata: {"message": "错误描述"}

15. 维客线索 /api/member-retention-clue

替代原 member-birthday 端点,提供维客线索管理能力。


19. 租户管理后台认证 /api/tenant/auth

租户管理员独立认证体系,与小程序认证完全隔离。使用用户名+密码登录JWT aud=tenant-admin

POST /api/tenant/auth/login

租户管理员登录。

请求体:

{ "username": "admin01", "password": "..." }

响应:

{ "access_token": "...", "refresh_token": "...", "token_type": "bearer" }

错误码:

  • 401用户名或密码错误统一消息不区分
  • 403账号已被禁用is_active=false

POST /api/tenant/auth/refresh

刷新租户管理员令牌。验证 aud=tenant-admin + type=refresh

请求体:

{ "refresh_token": "..." }

响应:同登录响应格式。

错误码:

  • 401无效的刷新令牌 / 令牌类型不匹配

20. 租户用户审核与管理 /api/tenant

所有端点需租户管理员 JWTaud=tenant-admin),数据自动按 managed_site_ids 隔离。

GET /api/tenant/my-sites

当前管理员管辖的店铺列表(用于前端筛选下拉)。

响应:

[
  { "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:角色 codecoach → 查 dwd.dim_assistant,其他 → 查 dwd.dim_staff,必填)
  • site_id:店铺 site_idsite_code 二选一)
  • site_code:店铺编号(与 site_id 二选一)

响应:StaffCandidate[](含 id/identityLabel/name/mobile/entryTime/source

说明:

  • coach 角色的 identityLabeldws.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 NULLauth.users.phone

GET /api/tenant/applications/{id}/match-suggestions

关联匹配建议。通过 site_code_mapping 解析 site_id,在 v_dim_assistantscd2_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_rolesuser_assistant_bindinguser_applications.status='approved'

请求体:

{ "role": "coach", "assistantId": 123, "staffId": null }

错误码:

  • 409该申请已被处理非 pending 状态)

POST /api/tenant/applications/{id}/reject

审核拒绝。

请求体:

{ "reason": "拒绝原因" }

错误码:

  • 409该申请已被处理

GET /api/tenant/users

已通过审核用户列表,支持角色筛选、关键词搜索和分页。

查询参数:

  • role:按角色筛选(可选)
  • keyword:关键词搜索(姓名/手机号,可选)
  • page:页码(默认 1
  • page_size:每页条数(默认 20最大 100

响应:{ items, total, page, pageSize }

PATCH /api/tenant/users/{id}

编辑用户角色/门店/状态。

请求体(部分更新):

{ "role": "staff", "siteId": 2, "status": "disabled" }

错误码:

  • 403目标门店不在管辖范围内
  • 404用户不存在

PUT /api/tenant/users/{id}/binding

更新用户助教/员工绑定。

请求体:

{ "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

  • fileExcel 文件(.xlsx/.xls
  • upload_type:模板类型
  • site_id:门店 ID

响应:

{
  "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

确认写入。单事务写入目标表,失败回滚整批。

请求体:

{
  "uploadId": 1,
  "resolutions": [
    { "rowIndex": 3, "action": "replace" },
    { "rowIndex": 5, "action": "keep" }
  ]
}

响应:

{ "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_membernickname 模糊匹配 OR mobile 精确匹配,scd2_is_current=1)。手机号脱敏返回。

查询参数:

  • keyword:搜索关键词(必填,最少 1 字符)
  • site_id:指定门店 ID 筛选(可选)

响应:

{
  "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。

请求体:

{ "category": "消费习惯", "summary": "每周消费 2-3 次", "detail": "偏好周末下午时段" }

错误码:

  • 404线索不存在或不在管辖范围
  • 422无效的线索大类 / 摘要为空或超长

DELETE /api/tenant/clues/{id}

物理删除线索。

错误码:

  • 404线索不存在

PATCH /api/tenant/clues/{id}/visibility

切换线索 is_hidden 状态。

请求体:

{ "isHidden": true }

23. 租户店铺管理员 CRUD /api/tenant/site-admins

所有端点需租户管理员 JWTaud=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_admintenant_id 继承当前租户管理员。

请求体:

{
  "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

重置店铺管理员密码。

请求体:

{ "newPassword": "..." }
  • 404管理员不存在

24. 管理端租户管理员 CRUD /api/admin

所有端点需管理后台 JWTadmin_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_idbiz.tenants 选择,managed_site_idsbiz.sites 选择。

请求体:

{
  "username": "admin01",
  "password": "...",
  "displayName": "管理员01",
  "tenantId": 1,
  "managedSiteIds": [1, 2, 3]
}

响应201

{ "id": 1, "createdAt": "2026-03-20T10:00:00" }

错误码:

  • 409用户名已存在

PATCH /api/admin/tenant-admins/{id}

编辑租户管理员username / display_name / managed_site_ids / is_active

请求体(部分更新):

{ "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

重置租户管理员密码。

请求体:

{ "newPassword": "..." }

错误码:

  • 404租户管理员不存在

WebSocket /ws/logs/{execution_id}

实时日志推送。连接后自动接收指定执行的日志流。


25. 注册体系管理 /api/adminadmin_registry

管理「连接器 → 租户 → 店铺」三级注册体系。所有端点需管理后台 JWT。

GET /api/admin/tenants

所有活跃租户列表(含连接器名称)。

响应:

[
  {
    "id": 1,
    "tenant_id": 12345,
    "tenant_name": "朗朗桌球",
    "connector_name": "飞球",
    "is_active": true
  }
]

GET /api/admin/tenants/{tenant_id}/sites

指定租户下所有活跃店铺。

路径参数:

  • tenant_id:租户 IDbiz.tenants.id

响应:

[
  {
    "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:店铺 IDbiz.sites.id

请求体:

{ "new_code": "LLQ002" }

响应:

{
  "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:店铺 IDbiz.sites.id

响应:

[
  {
    "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_sitescd2_is_current=1),对比 biz.sites,新增/更新店铺信息。

响应:

{ "inserted": 3, "updated": 1 }

26. AI 监控后台 /api/admin/ai

所有端点需管理后台 JWTDepends(require_admin)。P15 新增13 个端点。

GET /api/admin/ai/dashboard

AI 运行总览。返回今日统计、7 天趋势、App 分布、Token 预算、告警列表、App 健康状态。

查询参数:

  • site_id:门店筛选(可选)

GET /api/admin/ai/trigger-jobs

调度任务分页列表 + 今日去重统计。

查询参数:

  • event_typestatussite_iddate_fromdate_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_jobis_forced=true),异步执行。

GET /api/admin/ai/run-logs

调用记录分页列表。

查询参数:

  • app_typestatustrigger_typesite_iddate_fromdate_to:筛选(均可选)
  • pagepage_size:分页

GET /api/admin/ai/run-logs/{log_id}

调用记录详情(含完整 prompt/response/error不脱敏

POST /api/admin/ai/cache/invalidate

批量缓存失效。

请求体:

{ "site_id": 123, "app_type": "app3_clue", "member_id": 456 }

site_id 必填,app_typemember_id 可选。

GET /api/admin/ai/budget

Token 预算使用情况(日/月已用量、上限、百分比)。

POST /api/admin/ai/batch-run

创建批量执行请求,返回预估(不立即执行)。

请求体:

{ "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 分钟)。

请求体:

{ "batch_id": "uuid" }

错误码:

  • 400batch_id 无效或已过期
  • 409batch_id 已确认

GET /api/admin/ai/alerts

告警列表(ai_run_logs WHERE status IN ('failed','timeout','circuit_open'))。

查询参数:

  • alert_status:告警状态筛选(可选)
  • site_id:门店筛选(可选)
  • pagepage_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

返回有日志数据的日期列表。

响应:

{ "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,可选)
  • methodHTTP 方法筛选(GET/POST/PUT/DELETE,可选)
  • path_contains:路径关键词模糊匹配(可选)
  • status_codeHTTP 状态码精确匹配(可选)
  • 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_idtrace_typetimestampmethodpathstatus_codetotal_duration_msuser_idsite_iddb_query_countdb_total_mserrorspansspan 数组,每个含 span_type/module/function/description_zh/duration_ms/params/result_summary/extra)。

错误码:

  • 404指定 request_id 不存在

POST /api/admin/dev-trace/cleanup

按日期范围手动清理日志。

请求体:

{ "start_date": "2026-03-20", "end_date": "2026-03-21" }

响应:

{ "deleted_dates": ["2026-03-20", "2026-03-21"], "deleted_count": 2 }

GET /api/admin/dev-trace/settings

返回当前 trace 设置。

响应:

{
  "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

更新运行时设置(不需重启,即时生效)。

请求体(部分更新):

{ "enabled": false, "retention_days": 14, "log_sql": false }

响应:更新后的完整设置对象(同 GET 响应格式)。

GET /api/admin/dev-trace/coverage

返回最近一次覆盖率扫描结果。

响应:

{
  "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

对每个库执行诊断 SQLpg_stat_activity(连接池)、pg_database_size()(大小)、慢查询统计。连接失败时返回 status: "disconnected",其余字段为 null。即使所有库都连接失败仍返回 HTTP 200。

响应:

[
  {
    "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_jobsAI 事件链,最近 100 条)→ source="ai"
  • public.scheduled_tasksETL 调度)→ source="etl"

某数据源查询失败时记录日志,返回其他数据源数据。

响应:

[
  {
    "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_expressioninterval_seconds

仅 merge 请求中非 null 的字段到 trigger_config JSONB不覆盖其他已有字段。更新后重新计算 next_run_at

请求体(部分更新,至少提供一个字段):

{ "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 服务器内部错误