13 KiB
13 KiB
后端架构文档 — FastAPI 服务层
更新日期:2026-03-19(RNS1.3 三看板接口 + CONFIG-1 技能类型) 位置:
apps/backend/框架:FastAPI + psycopg2(同步连接池) 数据库:zqyy_app(业务库)+etl_feiqiu(ETL 库,直连只读访问)
1. 模块交互总览
graph TB
subgraph "微信小程序"
MP[miniprogram pages]
end
subgraph "管理后台"
ADMIN[admin-web]
end
subgraph "FastAPI 后端"
subgraph "路由层 (routers/)"
R_AUTH[xcx_auth]
R_TASKS[xcx_tasks]
R_NOTES[xcx_notes]
R_PERF[xcx_performance]
R_CUST[xcx_customers<br/>CUST-1, CUST-2]
R_COACH[xcx_coaches<br/>COACH-1]
R_CHAT[xcx_ai_chat]
R_CACHE[xcx_ai_cache]
R_ADMIN[admin_*]
end
subgraph "服务层 (services/)"
S_TM[task_manager<br/>任务 CRUD + 列表扩展]
S_PS[performance_service<br/>绩效概览 + 明细]
S_NS[note_service<br/>备注 CRUD + 评分]
S_CS[customer_service<br/>客户详情 + 服务记录]
S_CO[coach_service<br/>助教详情]
S_FDW[fdw_queries<br/>FDW 查询集中封装]
S_TG[task_generator<br/>任务自动生成]
S_TE[task_expiry<br/>过期检测]
S_RD[recall_detector<br/>召回完成检测]
S_WX[wechat<br/>微信登录/Token]
end
subgraph "中间件 + 认证"
MW[ResponseWrapperMiddleware]
AUTH[JWT + require_approved]
end
end
subgraph "数据库"
BIZ[(zqyy_app<br/>biz / auth / public)]
ETL[(etl_feiqiu<br/>直连 app.v_*)]
end
MP --> R_AUTH & R_TASKS & R_NOTES & R_PERF & R_CUST & R_COACH & R_CHAT
ADMIN --> R_ADMIN
R_TASKS --> S_TM
R_PERF --> S_PS
R_NOTES --> S_NS
R_CUST --> S_CS
R_COACH --> S_CO
R_CHAT --> S_TM
S_TM --> S_FDW
S_PS --> S_FDW
S_CS --> S_FDW
S_CO --> S_FDW
S_TM --> BIZ
S_PS --> BIZ
S_NS --> BIZ
S_CS --> BIZ
S_CO --> BIZ
S_FDW --> ETL
style S_FDW fill:#9f9,stroke:#333
style S_PS fill:#9f9,stroke:#333
style R_PERF fill:#9f9,stroke:#333
style S_CS fill:#9f9,stroke:#333
style S_CO fill:#9f9,stroke:#333
style R_CUST fill:#9f9,stroke:#333
style R_COACH fill:#9f9,stroke:#333
2. 路由模块清单
| 路由文件 | 前缀 | 说明 |
|---|---|---|
xcx_auth.py |
/api/xcx/auth |
微信登录、Token 刷新、入驻申请 |
xcx_tasks.py |
/api/xcx/tasks |
任务列表(TASK-1)、详情(TASK-2)、pin/unpin、abandon/restore |
xcx_notes.py |
/api/xcx/notes |
备注 CRUD(含 score 评分) |
xcx_performance.py |
/api/xcx/performance |
绩效概览(PERF-1)、明细(PERF-2) |
xcx_customers.py |
/api/xcx/customers |
客户详情(CUST-1)、客户服务记录(CUST-2)(RNS1.2 新增) |
xcx_coaches.py |
/api/xcx/coaches |
助教详情(COACH-1)(RNS1.2 新增) |
xcx_board.py |
/api/xcx/board |
助教看板(BOARD-1)、客户看板(BOARD-2)、财务看板(BOARD-3)(RNS1.3 新增) |
xcx_config.py |
/api/xcx/config |
技能类型配置(CONFIG-1)(RNS1.3 新增) |
xcx_ai_chat.py |
/api/xcx/ai |
AI 对话(SSE 流式) |
xcx_ai_cache.py |
/api/xcx/ai-cache |
AI 缓存查询 |
admin_applications.py |
/api/admin/applications |
入驻审核 |
auth.py |
/api/auth |
管理后台登录 |
member_retention_clue.py |
/api/member-retention-clue |
维客线索 |
ops_panel.py |
/api/ops |
运维面板 |
business_day.py |
/api/business-day |
营业日配置 |
3. 服务层模块清单
| 服务文件 | 职责 | 数据源 |
|---|---|---|
task_manager.py |
任务 CRUD、get_task_list_v2()、get_task_detail() |
biz.coach_tasks + FDW |
performance_service.py |
get_overview()、get_records(),绩效汇总与明细 |
FDW |
note_service.py |
备注创建(含 score)、AI 占位、回访触发 | biz.notes |
customer_service.py |
客户详情(CUST-1)、客户服务记录(CUST-2)(RNS1.2 新增) | biz.coach_tasks + biz.ai_cache + biz.notes + public.member_retention_clue + FDW |
coach_service.py |
助教详情(COACH-1):绩效/收入/任务分组/TOP客户/历史月份(RNS1.2 新增) | biz.coach_tasks + biz.notes + FDW |
fdw_queries.py |
ETL RLS 视图查询集中封装,直连 ETL 库 + SET LOCAL app.current_site_id 门店隔离 |
app.v_* (ETL 直连) |
task_generator.py |
定时任务:基于 WBI/NCI/RS 指数自动生成助教任务 | biz + FDW |
task_expiry.py |
定时任务:检测过期任务并标记 inactive | biz.coach_tasks |
recall_detector.py |
事件驱动:ETL 数据更新后检测召回完成 | biz + FDW |
note_reclassifier.py |
事件驱动:召回完成后回溯重分类备注 | biz.notes |
wechat.py |
微信 code2session、Token 管理 | 外部 API |
role.py |
角色权限查询 | auth.* |
scheduler.py |
触发器调度引擎 | biz.trigger_jobs |
board_service.py |
三看板编排:get_coach_board()、get_customer_board()、get_finance_board(),含日期范围/环比/排序/分页/降级(RNS1.3 新增) |
FDW + biz.coach_tasks |
application.py |
入驻申请处理 | auth.applications |
4. FDW 查询封装(fdw_queries.py)
所有跨库查询集中在此模块,确保 DWD-DOC 强制规则统一实施。
⚠️ 架构变更(2026-03-18):不再使用 zqyy_app 的 fdw_etl.* foreign table,改为通过 get_etl_readonly_connection(site_id) 直连 ETL 库查询 app.v_* RLS 视图。原因:postgres_fdw 不传递自定义 GUC 参数到远端连接,导致 RLS 视图的 current_setting('app.current_site_id') 在远端未设置而报错。
所有函数通过 _fdw_context() 上下文管理器:创建 ETL 直连 → SET LOCAL app.current_site_id → 执行查询 → 关闭连接。
| 函数 | 用途 | DWD-DOC 规则 |
|---|---|---|
get_member_info() |
批量查询会员姓名/手机号 | DQ-6:JOIN v_dim_member |
get_member_balance() |
批量查询储值卡余额 | DQ-7:JOIN v_dim_member_card_account |
get_last_visit_days() |
批量查询距上次到店天数 | 废单排除:is_delete = 0 |
get_salary_calc() |
助教绩效/档位/收入 | 收入用 gross_salary;费用用 base_income + bonus_income |
get_service_records() |
服务记录明细(分页) | 收入用 ledger_amount + DQ-6 + 废单排除 |
get_service_records_for_task() |
特定客户服务记录 | 同上 |
get_consumption_60d() |
客户近 60 天消费金额(RNS1.2 新增) | 收入用 ledger_amount(items_sum)+ 废单排除 |
get_relation_index() |
客户与助教关系指数列表(RNS1.2 新增) | 来源 v_dws_member_assistant_relation_index |
get_consumption_records() |
客户消费记录嵌套查询(RNS1.2 新增) | items_sum + DWD-DOC 规则 2 + 废单排除 |
get_total_service_count() |
客户累计服务总次数(RNS1.2 新增) | 废单排除:is_delete = 0 |
get_coach_60d_stats() |
助教对客户近 60 天统计(RNS1.2 新增) | 废单排除:is_delete = 0 |
get_assistant_info() |
助教基本信息(RNS1.2 新增) | 来源 v_dim_assistant |
get_salary_calc_multi_months() |
批量多月绩效数据(RNS1.2 新增) | 来源 v_dws_assistant_salary_calc |
get_monthly_customer_count() |
各月不重复客户数(RNS1.2 新增) | 废单排除:is_delete = 0 |
get_coach_top_customers() |
助教 TOP 客户(RNS1.2 新增) | DQ-6 + DQ-7 + items_sum + 废单排除 |
get_customer_service_records() |
客户按月服务记录(RNS1.2 新增) | DQ-6 + items_sum + 废单排除 |
get_all_assistants() |
BOARD-1:按技能筛选助教列表(RNS1.3 新增) | v_dim_assistant |
get_salary_calc_batch() |
BOARD-1:批量查询当期/上期绩效(RNS1.3 新增) | items_sum + assistant_pd/cx_money |
get_top_customers_for_coaches() |
BOARD-1:按亲密度 Top3 客户 + 四级 emoji(RNS1.3 新增) | DQ-6 + v_dws_member_assistant_relation_index |
get_coach_sv_data() |
BOARD-1:助教客源储值数据(RNS1.3 新增) | v_dws_assistant_monthly_summary |
get_customer_board_recall() |
BOARD-2:召回维度(WBI 降序)(RNS1.3 新增) | v_dws_member_winback_index + DQ-6 |
get_customer_board_potential() |
BOARD-2:潜力维度(SPI 降序)(RNS1.3 新增) | v_dws_member_spending_power_index |
get_customer_board_balance() |
BOARD-2:余额维度(RNS1.3 新增) | v_dim_member_card_account + DQ-7 |
get_customer_board_recharge() |
BOARD-2:充值维度(RNS1.3 新增) | v_dwd_recharge_order |
get_customer_board_recent() |
BOARD-2:最近到店维度(RNS1.3 新增) | v_dws_member_visit_detail |
get_customer_board_spend60() |
BOARD-2:60 天消费维度(RNS1.3 新增) | items_sum 口径 |
get_customer_board_freq60() |
BOARD-2:60 天频次维度 + weeklyVisits(RNS1.3 新增) | v_dws_member_consumption_summary |
get_customer_board_loyal() |
BOARD-2:忠诚度维度(RS 降序)(RNS1.3 新增) | v_dws_member_assistant_relation_index |
get_customer_assistants() |
BOARD-2:批量查询客户关联助教(RNS1.3 新增) | 含亲密度 + 当前跟进置顶 |
get_finance_overview() |
BOARD-3:经营一览 8 项指标(RNS1.3 新增) | v_dws_finance_daily_summary + items_sum |
get_finance_recharge() |
BOARD-3:预收资产(储值卡 + 赠送卡矩阵)(RNS1.3 新增) | v_dws_finance_recharge_summary |
get_finance_revenue() |
BOARD-3:应计收入结构(RNS1.3 新增) | v_dws_finance_income_structure + discount_detail |
get_finance_cashflow() |
BOARD-3:现金流入(RNS1.3 新增) | v_dws_finance_daily_summary + 互斥规则 7 |
get_finance_expense() |
BOARD-3:现金流出 4 子分组(RNS1.3 新增) | v_dws_finance_expense_summary + platform_settlement |
get_finance_coach_analysis() |
BOARD-3:助教分析(basic + incentive)(RNS1.3 新增) | v_dws_assistant_salary_calc |
get_skill_types() |
CONFIG-1:技能类型配置(RNS1.3 新增) | ETL cfg 表 |
列名映射(design.md 理想名 → 实际 FDW 视图列名)
RLS 视图直接暴露 DWD/DWS 原始列名,后端代码在 SQL 中使用 AS 别名转换。
v_dwd_assistant_service_log(基于 dwd.dwd_assistant_service_log,非 _ex 表):
| 代码语义 | 实际列名 | 说明 |
|---|---|---|
| id | assistant_service_id |
服务记录主键 |
| assistant_id (WHERE) | site_assistant_id |
与 salary_calc.assistant_id 同源 |
| member_id | tenant_member_id |
会员 ID |
| is_trash = false | is_delete = 0 |
整数类型,0=正常 |
| settle_time | create_time |
结算时间 |
| start_time | start_use_time |
开始使用时间 |
| end_time | last_use_time |
最后使用时间 |
| service_hours | income_seconds / 3600.0 |
折算工时(计算字段) |
| service_hours_raw | real_use_seconds / 3600.0 |
原始工时(计算字段) |
| income (items_sum) | ledger_amount |
收入金额 |
| course_type | skill_name |
课程类型 |
| table_name | site_table_id |
仅有台桌 ID,无名称 |
v_dws_assistant_salary_calc:
| 代码语义 | 实际列名 | 说明 |
|---|---|---|
| calc_month | salary_month |
date 类型,存储为 YYYY-MM-01 |
| coach_level | assistant_level_name |
档位名称 |
| tier_index | tier_id |
档位 ID |
| basic_hours | base_hours |
基础课时 |
| total_hours | effective_hours |
有效总工时 |
| total_income | gross_salary |
总收入 |
| basic_rate | base_course_price |
基础课单价 |
| incentive_rate | bonus_course_price |
激励课单价 |
| bonus_money | sprint_bonus |
冲刺奖金 |
| assistant_pd_money_total | base_income |
基础课总收入 |
| assistant_cx_money_total | bonus_income |
激励课总收入 |
注意:
tier_nodes、total_customers、next_tier_*、tier_completed在视图中不存在,后端使用默认值或从其他数据源推算。
5. 数据流向
小程序请求
→ JWT 认证 (require_approved)
→ 路由层 (routers/)
→ 服务层 (services/)
→ 业务库 (zqyy_app) 直连
→ ETL 库 (etl_feiqiu) 直连只读(app.v_* RLS 视图)
→ 响应包装 (ResponseWrapperMiddleware)
→ { code: 0, data: ..., message: "ok" }
6. 关键设计决策
- ETL 直连:所有 ETL 查询封装在
fdw_queries.py,通过_fdw_context()直连 ETL 库查询app.v_*RLS 视图(不使用 FDW foreign table,因 postgres_fdw 不传递自定义 GUC) - 优雅降级:扩展字段(lastVisitDays/balance/aiSuggestion)查询失败返回 null,不影响核心响应
- camelCase 转换:所有小程序端响应通过
CamelModel自动转换为 camelCase - 门店隔离:业务库通过
site_id参数过滤,ETL 查询通过SET LOCAL app.current_site_id+ RLS 视图隔离