80 KiB
小程序接口契约文档
创建时间:2026-03-18 状态:联调前草案,后端开发时以此为契约基准 基础路径:
/api/xcx
〇、约定
- 所有接口需认证(
Authorization: Bearer <token>),除 login/dev-login 外 - 响应统一 JSON,成功
{ code: 0, data: ... },失败{ code: number, message: string } - 分页统一
{ items: T[], total: number, page: number, pageSize: number } - 金额单位:元(number,保留 2 位小数)
- 时间格式:ISO 8601(
2026-03-18T10:30:00) - 排序参数:
sort=field:asc|desc
一、认证模块(已实现 ✅)
AUTH-1: 微信登录
POST /api/xcx/login
Body: { code: string }
Response: { access_token, refresh_token, user: { user_id, status, nickname } }
AUTH-2: 开发模式登录
POST /api/xcx/dev-login
Body: { openid: string }
Response: 同 AUTH-1
⚠️ 生产环境必须禁用此接口(REQ-3)
AUTH-3: 查询当前用户
GET /api/xcx/me
Response: {
user_id: number,
status: 'new' | 'pending' | 'approved' | 'rejected' | 'disabled',
nickname: string,
role: string, // 新增(REQ-4)
store_name: string, // 新增(REQ-4)
coach_level?: string, // 新增(REQ-4),仅助教角色
avatar?: string // 新增(REQ-4)
}
AUTH-4: 刷新 Token
POST /api/xcx/refresh
Body: { refresh_token: string }
Response: { access_token, refresh_token }
AUTH-5: 提交入驻申请
POST /api/xcx/apply
Body: { name: string, phone: string, store_id: string }
Response: { user_id: number, status: 'pending' }
二、任务模块
TASK-1: 任务列表
数据源:
zqyy_app.coach_tasks(任务)、etl_feiqiu.dws.*(绩效聚合,via FDW) 金额口径:收入金额使用items_sum(DWD-DOC 强制规则 1)
GET /api/xcx/tasks?status={pending|completed|abandoned}&page=1&pageSize=20
响应结构
interface TaskListResponse {
items: TaskItem[]
total: number
page: number
pageSize: number
// 绩效概览(附带在任务列表响应中,避免额外请求)
performance: PerformanceSummary
}
子类型定义
/** 任务项 */
interface TaskItem {
id: string
customerName: string
customerAvatar: string
taskType: 'callback' | 'priority_recall' | 'relationship' | 'high_priority'
taskTypeLabel: string
deadline: string // ISO 8601
heartScore: number // 0-10
hobbies: string[]
isPinned: boolean
hasNote: boolean
status: 'pending' | 'completed' | 'abandoned'
// ── 可选扩展字段(需求 5.7.2) ──
lastVisitDays?: number // 距上次到店天数
balance?: number // 客户余额(元)
aiSuggestion?: string // AI 建议摘要
}
/** 绩效概览(扩展版,需求 5.7.1)
* 从原 4 字段扩展为 15+ 字段,供任务列表页 Banner 进度条组件使用 */
interface PerformanceSummary {
// ── 原有字段 ──
totalHours: number // 本月总工时(折算后)
totalIncome: number // 本月总收入
totalCustomers: number // 本月服务客户数
monthLabel: string // 月份标签,如 '3月'
// ── 扩展字段 ──
tierNodes: number[] // 档位节点数组,如 [0, 100, 130, 160, 190, 220]
basicHours: number // 基础课时
bonusHours: number // 激励课时
currentTier: number // 当前档位索引(0-based,对应 tierNodes)
nextTierHours: number // 下一档位工时阈值
tierCompleted: boolean // 是否已达最高档
bonusMoney: number // 升档奖金(元)
incomeTrend: string // 收入趋势描述,如 '↓368'
incomeTrendDir: 'up' | 'down' // 趋势方向
prevMonth: string // 上月标签,如 '1月'
currentTierLabel: string // 当前档位名称,如 '初级'
}
字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
performance.tierNodes |
number[] | 档位节点数组,前端用于绘制进度条刻度 |
performance.basicHours |
number | 基础课工时 |
performance.bonusHours |
number | 激励课工时 |
performance.currentTier |
number | 当前所在档位索引(0=未入档,1=初级...) |
performance.nextTierHours |
number | 下一档位的工时阈值 |
performance.tierCompleted |
boolean | 是否已达最高档位 |
performance.bonusMoney |
number | 达到下一档位可获得的奖金 |
performance.incomeTrend |
string | 与上月对比的收入变化,含方向符号 |
performance.incomeTrendDir |
string | 'up' 或 'down',前端用于控制颜色 |
performance.prevMonth |
string | 上月标签,用于对比展示 |
items[].lastVisitDays |
number? | 距上次到店天数,可选 |
items[].balance |
number? | 客户余额,可选 |
items[].aiSuggestion |
string? | AI 建议摘要,可选 |
TASK-2: 任务详情
GET /api/xcx/tasks/{taskId}
Response: {
// 基础信息(同 TASK-1 item)
id, customer_name, customer_avatar, task_type, task_type_label,
deadline, heart_score, hobbies, is_pinned, has_note, status,
// 扩展信息
last_visit_date?: string,
last_spend_amount?: number,
days_absent?: number,
spend_trend?: 'up' | 'down' | 'flat',
churn_risk?: 'high' | 'medium' | 'low',
callback_reason?: string,
preferences?: string[],
consumption_habits?: string,
social_preference?: string,
// 维客线索
retention_clues: Array<{
tag: string,
tag_color: 'primary' | 'success' | 'purple' | 'error',
emoji: string,
text: string,
source: string,
desc?: string
}>,
// 话术参考
talking_points: string[],
// 近期服务记录摘要
service_summary: { total_hours: number, total_income: number, avg_income: number },
service_records: Array<{
table: string,
type: string,
type_class: 'basic' | 'vip' | 'tip' | 'recharge' | 'incentive',
record_type?: 'course' | 'recharge',
duration: number,
duration_raw?: number,
income: number,
is_estimate?: boolean,
drinks: string,
date: string
}>,
// AI 分析
ai_analysis: { summary: string, suggestions: string[] },
// 备注(最近 N 条)
notes: Array<{
id: string,
content: string,
tag_type: 'customer' | 'coach' | 'system',
tag_label: string,
created_at: string,
ai_score?: number,
manual_score?: number
}>
}
TASK-3: 放弃任务
POST /api/xcx/tasks/{taskId}/abandon
Body: { reason: string }
Response: { status: 'abandoned' }
TASK-4: 取消放弃
POST /api/xcx/tasks/{taskId}/restore
Response: { status: 'pending' }
三、备注模块
NOTE-1: 备注列表
GET /api/xcx/notes?page=1&pageSize=20
Response: {
items: Array<{
id: string,
content: string,
tag_type: 'customer' | 'coach' | 'system',
tag_label: string,
created_at: string,
ai_score?: number,
manual_score?: number
}>,
total, page, pageSize
}
NOTE-2: 新增备注
POST /api/xcx/notes
Body: { content: string, task_id?: string, customer_id?: string }
Response: { id: string, created_at: string }
NOTE-3: 删除备注
DELETE /api/xcx/notes/{noteId}
Response: { success: true }
四、绩效模块
PERF-1: 绩效概览
数据源:
etl_feiqiu.dws.*(绩效/收入聚合,via FDW)、zqyy_app.coaches(助教信息) 金额口径:所有收入金额使用items_sum(DWD-DOC 强制规则 1);助教费用使用assistant_pd_money+assistant_cx_money拆分(DWD-DOC 强制规则 2)
GET /api/xcx/performance?year={year}&month={month}
响应结构
interface PerformanceOverviewResponse {
// ── Banner 数据 ──
coachName: string // 助教姓名
coachRole: string // 角色,如 '助教'
storeName: string // 门店名
monthlyIncome: string // 本月收入(格式化),如 '¥6,206'
lastMonthIncome: string // 上月收入(格式化),如 '¥16,880'
// ── 收入档位 ──
currentTier: {
basicRate: number // 当前档基础课时费率(元/h)
incentiveRate: number // 当前档激励课时费率(元/h)
}
nextTier: {
basicRate: number // 下一档基础课时费率
incentiveRate: number // 下一档激励课时费率
}
upgradeHoursNeeded: number // 距升档所需工时
upgradeBonus: number // 升档奖金(元)
// ── 本月业绩明细(incomeItems) ──
incomeItems: IncomeItem[]
monthlyTotal: string // 本月收入合计(格式化),如 '¥6,950.5'
// ── 本月服务记录(thisMonthRecords) ──
// 按日期分组的 DateGroup 结构
thisMonthRecords: DateGroup[]
// ── 新客列表(newCustomers) ──
newCustomers: Array<{
name: string
avatarChar: string // 姓氏首字
avatarColor: string // 头像渐变色
lastService: string // 最后服务日期,如 '2月7日'
count: number // 服务次数
}>
// ── 常客列表(regularCustomers) ──
regularCustomers: Array<{
name: string
avatarChar: string
avatarColor: string
hours: number // 总工时
income: string // 总收入(格式化),如 '¥960'
count: number // 服务次数
}>
}
子类型定义
/** 业绩明细项 */
interface IncomeItem {
icon: string // emoji 图标,如 '🎱'、'⭐'、'💰'、'🏆'
label: string // 标签,如 '基础课'、'激励课'、'充值激励'、'TOP3 销冠奖'
desc: string // 费率×工时描述,如 '80元/h × 75h'
value: string // 金额(格式化),如 '¥6,000';或文字如 '继续努力'
}
/** 按日期分组的服务记录 */
interface DateGroup {
date: string // 日期标签,如 '2月7日'
totalHours: string // 当日总工时,如 '4.0h'
totalIncome: string // 当日总收入,如 '¥350'
records: Array<{
customerName: string
avatarChar: string // 姓氏首字
avatarColor: string // 头像渐变色
timeRange: string // 时间段,如 '20:00-22:00'
hours: string // 工时,如 '2.0h'
courseType: string // 课程类型,如 '基础课'、'包厢课'
courseTypeClass: string // 样式类:tag-basic / tag-vip / tag-tip
location: string // 台桌/包厢,如 '3号台'、'VIP1号房'
income: string // 收入(格式化),如 '¥160'
}>
}
字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
lastMonthIncome |
string | 上月收入,用于 Banner 对比展示 |
currentTier / nextTier |
object | 当前/下一档位的基础课和激励课费率 |
upgradeHoursNeeded |
number | 距升档还需多少工时 |
upgradeBonus |
number | 升档后可获得的奖金 |
incomeItems[].desc |
string | 费率×工时的拆分描述,如 "80元/h × 75h" |
newCustomers[].lastService |
string | 最后服务日期 |
newCustomers[].count |
number | 服务次数 |
regularCustomers[].hours |
number | 总工时 |
regularCustomers[].income |
string | 总收入(items_sum 口径) |
PERF-2: 绩效明细(按月)
GET /api/xcx/performance/records?year={year}&month={month}&page=1&pageSize=20
Response: {
// 月度汇总
summary: {
total_count: number,
total_hours: number,
total_hours_raw: number,
total_income: number
},
// 按日期分组
date_groups: Array<{
date: string, // "3月18日"
total_hours: number,
total_income: number,
records: Array<{
id: string,
customer_name: string,
time_range: string, // "20:00-22:00"
hours: number,
hours_raw?: number,
course_type: string,
course_type_class: string,
location: string,
income: number
}>
}>,
has_more: boolean
}
五、客户模块(已实现 ✅)
CUST-1: 客户详情
客户详情页完整数据,包含 Banner 概览、AI 洞察、关联助教任务、最亲密助教、消费记录(嵌套结构)和备注列表。 数据源:
biz.members(会员基础信息)、fdw_etl.dwd.dim_member(手机号/昵称,DQ-6)、fdw_etl.dwd.dim_member_card_account(会员卡,DQ-7)、fdw_etl.dwd.dwd_settlement_head(消费记录)、fdw_etl.dws.*(聚合指标)、biz.ai_cache(AI 洞察)、biz.tasks(助教任务)、biz.customer_notes(备注) DWD-DOC 强制规则:金额字段使用items_sum口径(规则 1);助教费用使用assistant_pd_money+assistant_cx_money拆分(规则 2);会员信息通过member_idJOINdim_member获取(规则 DQ-6),禁止直接使用settlement_head.member_phone;会员卡信息通过member_idJOINdim_member_card_account获取(规则 DQ-7)
GET /api/xcx/customers/{customerId}
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
customerId |
string (path) | 是 | 客户唯一 ID |
响应结构(TypeScript 类型定义)
/** 顶层响应 */
interface CustomerDetailResponse {
// ─── 基础信息 ───
/** 客户唯一 ID */
id: string
/** 客户姓名(⚠️ 通过 member_id JOIN dim_member.nickname 获取,DQ-6) */
name: string
/** 脱敏手机号,如 "139****5678"(⚠️ 通过 member_id JOIN dim_member.mobile 获取,DQ-6) */
phone: string
/** 完整手机号(点击查看时用)(⚠️ 同上,DQ-6) */
phoneFull: string
/** 头像 URL */
avatar: string
/** 会员等级(⚠️ 通过 member_id JOIN dim_member_card_account 获取,DQ-7) */
memberLevel: string
/** 关系指数,如 "0.85" */
relationIndex: string
/** 客户标签列表 */
tags: string[]
// ─── Banner 概览字段 ───
/** 客户余额(元),⚠️ 使用 `items_sum` 口径 */
balance: number
/** 近 60 天消费金额(元),⚠️ 使用 `items_sum` 口径 */
consumption60d: number
/** 理想到店间隔(天) */
idealInterval: number
/** 距上次到店天数 */
daysSinceVisit: number
// ─── AI 洞察模块 ───
/** AI 分析洞察,数据源:biz.ai_cache(cache_type=app4_analysis) */
aiInsight: AiInsight
// ─── 关联助教任务列表 ───
/** 关联助教任务 */
coachTasks: CoachTask[]
// ─── 最亲密助教 ───
/** 最亲密助教列表 */
favoriteCoaches: FavoriteCoach[]
// ─── 维客线索 ───
/** 维客线索列表(同 TASK-2 格式) */
retentionClues: RetentionClue[]
// ─── 消费记录(嵌套结构) ───
/** 消费记录列表,⚠️ 所有金额使用 `items_sum` 口径 */
consumptionRecords: ConsumptionRecord[]
// ─── 备注列表 ───
/** 客户备注 */
notes: CustomerNote[]
}
// ─── AI 洞察 ───
interface AiInsight {
/** AI 分析摘要文本 */
summary: string
/** 策略建议列表 */
strategies: Array<{
/** 标签颜色(CSS 类或色值) */
color: string
/** 策略建议文本 */
text: string
}>
}
// ─── 关联助教任务 ───
interface CoachTask {
/** 助教姓名 */
name: string
/** 助教等级:star / senior / middle / junior */
level: string
/** 等级对应颜色(前端样式用) */
levelColor: string
/** 任务类型,如 "回访"、"召回" */
taskType: string
/** 任务类型对应颜色 */
taskColor: string
/** 背景样式类 */
bgClass: string
/** 任务状态,如 "进行中"、"已完成" */
status: string
/** 最后服务日期,如 "2026-03-01" */
lastService: string
/**
* 指标列表(近 60 天统计)
* 含:近60天次数 / 总时长 / 次均时长
*/
metrics: MetricItem[]
}
// ─── 最亲密助教 ───
interface FavoriteCoach {
/** 亲密度 emoji,如 "💖"、"💛" */
emoji: string
/** 助教姓名 */
name: string
/** 关系指数,如 "0.92" */
relationIndex: string
/** 关系指数对应颜色 */
indexColor: string
/** 背景样式类 */
bgClass: string
/**
* 统计指标列表
* 含:基础课时(assistant_pd_money 对应)/ 激励课时(assistant_cx_money 对应)/ 上课次数 / 充值金额
* ⚠️ 课时费用使用 assistant_pd_money + assistant_cx_money 拆分(DWD-DOC 规则 2)
*/
stats: MetricItem[]
}
// ─── 通用指标项 ───
interface MetricItem {
/** 指标标签,如 "近60天次数"、"基础课时" */
label: string
/** 指标值,如 "12次"、"45.5h"、"¥3,200" */
value: string
/** 可选颜色标记(高亮/警告等) */
color?: string
}
// ─── 维客线索(同 TASK-2 格式) ───
interface RetentionClue {
/** 线索类型 */
type: string
/** 线索描述 */
text: string
}
// ─── 消费记录(嵌套结构) ───
interface ConsumptionRecord {
/** 记录唯一 ID */
id: string
/** 消费类型:table(台桌消费)/ shop(酒水/商品消费)/ recharge(充值) */
type: 'table' | 'shop' | 'recharge'
/** 消费日期,如 "2026-03-01" */
date: string
/** 台桌名称(type=table 时有值) */
tableName?: string
/** 开始时间,如 "14:00"(type=table 时有值) */
startTime?: string
/** 结束时间,如 "16:30"(type=table 时有值) */
endTime?: string
/** 时长(分钟)(type=table 时有值) */
duration?: number
/**
* 台费金额(元),⚠️ 使用 `items_sum` 口径中的 `table_charge_money`
* type=table 时有值
*/
tableFee?: number
/** 台费原价(元,优惠前),type=table 时有值 */
tableOrigPrice?: number
/**
* 助教服务列表
* ⚠️ fee 使用 assistant_pd_money(基础/陪打)+ assistant_cx_money(激励/超休)拆分(DWD-DOC 规则 2)
*/
coaches: CoachServiceItem[]
/** 酒水/商品金额(元),⚠️ 使用 `items_sum` 口径中的 `goods_money` */
foodAmount?: number
/** 酒水/商品原价(元,优惠前) */
foodOrigPrice?: number
/**
* 消费总金额(元)
* ⚠️ 使用 `items_sum` 口径 = table_charge_money + goods_money + assistant_pd_money + assistant_cx_money + electricity_money
*/
totalAmount: number
/** 消费总原价(元,优惠前) */
totalOrigPrice: number
/** 支付方式,如 "储值卡"、"微信支付"、"现金" */
payMethod: string
/** 充值金额(元),type=recharge 时有值 */
rechargeAmount?: number
}
/** 助教服务明细(嵌套在消费记录中) */
interface CoachServiceItem {
/** 助教姓名 */
name: string
/** 助教等级:star / senior / middle / junior */
level: string
/** 等级对应颜色 */
levelColor: string
/** 课程类型,如 "基础课"、"激励课" */
courseType: string
/** 服务时长(小时) */
hours: number
/**
* 折算工时(小时),可选
* 激励课按系数折算后的工时
*/
perfHours?: number
/**
* 服务费用(元)
* ⚠️ 基础课 = assistant_pd_money,激励课 = assistant_cx_money(DWD-DOC 规则 2)
*/
fee: number
}
// ─── 客户备注 ───
interface CustomerNote {
/** 备注唯一 ID */
id: string
/** 标签文本,如 "跟进"、"重要" */
tagLabel: string
/** 创建时间,ISO 8601 格式 */
createdAt: string
/** 备注内容 */
content: string
}
字段说明表
基础信息字段(8 个)
| 字段 | 类型 | 说明 | 数据源 |
|---|---|---|---|
id |
string | 客户唯一 ID | biz.members.id |
name |
string | 客户姓名 | ⚠️ member_id JOIN fdw_etl.dwd.dim_member.nickname(scd2_is_current=1),DQ-6 |
phone |
string | 脱敏手机号,如 "139****5678" |
⚠️ member_id JOIN fdw_etl.dwd.dim_member.mobile(scd2_is_current=1),DQ-6,后端脱敏 |
phoneFull |
string | 完整手机号(点击查看时用) | 同上,不脱敏 |
avatar |
string | 头像 URL | biz.members.avatar |
memberLevel |
string | 会员等级 | ⚠️ member_id JOIN fdw_etl.dwd.dim_member_card_account(tenant_member_id=member_id,scd2_is_current=1),DQ-7 |
relationIndex |
string | 关系指数,如 "0.85" |
后端根据服务频率/消费金额/亲密度综合计算 |
tags |
string[] |
客户标签列表 | biz.customer_tags |
Banner 概览字段(4 个)
| 字段 | 类型 | 说明 | 数据源 |
|---|---|---|---|
balance |
number | 客户余额(元),⚠️ items_sum 口径 |
fdw_etl.dws.* 聚合,储值卡余额 |
consumption60d |
number | 近 60 天消费金额(元),⚠️ items_sum 口径 |
fdw_etl.dwd.dwd_settlement_head,按 settle_time 过滤近 60 天,SUM(items_sum) |
idealInterval |
number | 理想到店间隔(天) | 后端根据历史到店频率计算 |
daysSinceVisit |
number | 距上次到店天数 | fdw_etl.dwd.dwd_settlement_head,MAX(settle_time) 与当前日期差值 |
aiInsight 模块(2 个字段)
| 字段 | 类型 | 说明 | 数据源 |
|---|---|---|---|
summary |
string | AI 分析摘要文本 | biz.ai_cache,cache_type='app4_analysis',target_id=customerId |
strategies |
Array<{ color, text }> |
策略建议列表,color 为标签颜色,text 为建议文本 |
同上,JSON 解析 cache_value |
coachTasks 模块(每项 9 个字段)
| 字段 | 类型 | 说明 | 数据源 |
|---|---|---|---|
name |
string | 助教姓名 | biz.users.nickname |
level |
string | 助教等级:star/senior/middle/junior |
fdw_etl.v_dws_assistant_salary_calc.coach_level |
levelColor |
string | 等级对应颜色 | 后端根据 level 映射 |
taskType |
string | 任务类型,如 "回访"、"召回" |
biz.tasks.task_type |
taskColor |
string | 任务类型对应颜色 | 后端根据 taskType 映射 |
bgClass |
string | 背景样式类 | 后端根据 level 映射 |
status |
string | 任务状态,如 "进行中"、"已完成" |
biz.tasks.status |
lastService |
string | 最后服务日期 | fdw_etl.dwd.dwd_settlement_head,该助教对该客户的 MAX(settle_time) |
metrics |
MetricItem[] |
近 60 天指标:次数/总时长/次均时长 | fdw_etl.dwd.dwd_settlement_head + dwd_assistant_service_log_ex,按助教+客户聚合近 60 天 |
favoriteCoaches 模块(每项 6 个字段)
| 字段 | 类型 | 说明 | 数据源 |
|---|---|---|---|
emoji |
string | 亲密度 emoji(💖 = 高亲密度 ≥ 0.7,💛 = 中亲密度 < 0.7) | 后端根据 relationIndex 映射 |
name |
string | 助教姓名 | biz.users.nickname |
relationIndex |
string | 关系指数,如 "0.92" |
后端根据服务频率/消费金额综合计算 |
indexColor |
string | 关系指数对应颜色 | 后端根据 relationIndex 阈值映射 |
bgClass |
string | 背景样式类 | 后端根据排名映射 |
stats |
MetricItem[] |
统计指标:基础课时(assistant_pd_money)/ 激励课时(assistant_cx_money)/ 上课次数 / 充值金额 |
fdw_etl.dwd.dwd_settlement_head + dwd_assistant_service_log_ex,⚠️ 课时费用使用 assistant_pd_money + assistant_cx_money 拆分(DWD-DOC 规则 2) |
consumptionRecords 模块(每项 16 个字段)
| 字段 | 类型 | 说明 | 数据源 |
|---|---|---|---|
id |
string | 记录唯一 ID | fdw_etl.dwd.dwd_settlement_head.id |
type |
enum | 消费类型:table/shop/recharge |
后端根据 settle_type 映射(IN (1,3) 为正向交易) |
date |
string | 消费日期 | dwd_settlement_head.settle_time,格式化为日期 |
tableName |
string? | 台桌名称 | dwd_settlement_head.table_name |
startTime |
string? | 开始时间 | dwd_settlement_head.start_time,格式化为 "HH:mm" |
endTime |
string? | 结束时间 | dwd_settlement_head.end_time,格式化为 "HH:mm" |
duration |
number? | 时长(分钟) | 后端根据 startTime/endTime 计算 |
tableFee |
number? | 台费金额(元),⚠️ table_charge_money |
dwd_settlement_head.table_charge_money |
tableOrigPrice |
number? | 台费原价(元,优惠前) | dwd_settlement_head.table_charge_money + 对应折扣 |
coaches |
CoachServiceItem[] |
助教服务列表 | fdw_etl.dwd.dwd_assistant_service_log_ex,关联 settlement_id |
foodAmount |
number? | 酒水/商品金额(元),⚠️ goods_money |
dwd_settlement_head.goods_money |
foodOrigPrice |
number? | 酒水/商品原价(元,优惠前) | dwd_settlement_head.goods_money + 对应折扣 |
totalAmount |
number | 消费总金额(元),⚠️ items_sum 口径 |
dwd_settlement_head.items_sum |
totalOrigPrice |
number | 消费总原价(元,优惠前) | items_sum + adjust_amount |
payMethod |
string | 支付方式 | 后端根据 pay_type / balance_amount 等字段映射 |
rechargeAmount |
number? | 充值金额(元),type=recharge 时有值 |
biz.recharge_records.amount |
coaches 子数组(CoachServiceItem,每项 7 个字段)
| 字段 | 类型 | 说明 | 数据源 |
|---|---|---|---|
name |
string | 助教姓名 | biz.users.nickname |
level |
string | 助教等级 | fdw_etl.v_dws_assistant_salary_calc.coach_level |
levelColor |
string | 等级对应颜色 | 后端根据 level 映射 |
courseType |
string | 课程类型:"基础课" / "激励课" |
后端根据 dwd_assistant_service_log_ex 服务类型映射 |
hours |
number | 服务时长(小时) | dwd_assistant_service_log_ex.service_hours |
perfHours |
number? | 折算工时(小时),激励课按系数折算 | dwd_assistant_service_log_ex,后端按激励系数计算 |
fee |
number | 服务费用(元),⚠️ 基础课 = assistant_pd_money,激励课 = assistant_cx_money(DWD-DOC 规则 2) |
dwd_settlement_head.assistant_pd_money / assistant_cx_money |
notes 模块(每项 4 个字段)
| 字段 | 类型 | 说明 | 数据源 |
|---|---|---|---|
id |
string | 备注唯一 ID | biz.customer_notes.id |
tagLabel |
string | 标签文本,如 "跟进"、"重要" |
biz.customer_notes.tag |
createdAt |
string | 创建时间,ISO 8601 格式 | biz.customer_notes.created_at |
content |
string | 备注内容 | biz.customer_notes.content |
业务规则
| 规则 | 说明 |
|---|---|
items_sum 口径(DWD-DOC 规则 1) |
所有金额字段(balance/consumption60d/tableFee/foodAmount/totalAmount 等)使用 items_sum = table_charge_money + goods_money + assistant_pd_money + assistant_cx_money + electricity_money,禁止使用 consume_money |
| 助教费用拆分(DWD-DOC 规则 2) | coaches[].fee 中基础课 = assistant_pd_money(陪打),激励课 = assistant_cx_money(超休);favoriteCoaches[].stats 中基础课时/激励课时同理。禁止使用 service_fee |
| 会员信息 JOIN(DWD-DOC 规则 DQ-6) | name/phone/phoneFull 必须通过 member_id LEFT JOIN dwd.dim_member(取 scd2_is_current=1)获取,禁止直接使用 settlement_head.member_phone/member_name(自 2025-12 起全为 NULL) |
| 会员卡 JOIN(DWD-DOC 规则 DQ-7) | memberLevel 必须通过 member_id LEFT JOIN dwd.dim_member_card_account(tenant_member_id=member_id,scd2_is_current=1)获取,禁止直接使用 member_card_type_name(自 2025-07-21 起全为 NULL) |
settle_type 过滤 |
消费记录取正向交易 settle_type IN (1, 3),本表无 is_delete 字段 |
| 废单排除 | 使用 dwd_assistant_service_log_ex.is_trash 排除废单,dwd_assistant_trash_event 已废弃 |
type 枚举映射 |
table(台桌消费)/ shop(酒水/商品消费)/ recharge(充值),后端根据 settle_type 和业务逻辑映射 |
| 亲密度 emoji | 💖 = 高亲密度(关系指数 ≥ 0.7),💛 = 中亲密度(关系指数 < 0.7) |
数据源映射表
| 模块 | 主数据源 | 辅助数据源 |
|---|---|---|
| 基础信息 | biz.members |
fdw_etl.dwd.dim_member(DQ-6)、fdw_etl.dwd.dim_member_card_account(DQ-7) |
| Banner 概览 | fdw_etl.dwd.dwd_settlement_head |
fdw_etl.dws.*(余额聚合) |
| AI 洞察 | biz.ai_cache(cache_type='app4_analysis') |
— |
| 关联助教任务 | biz.tasks |
biz.users、fdw_etl.dwd.dwd_settlement_head、dwd_assistant_service_log_ex |
| 最亲密助教 | fdw_etl.dwd.dwd_assistant_service_log_ex |
biz.users、fdw_etl.dwd.dwd_settlement_head |
| 消费记录 | fdw_etl.dwd.dwd_settlement_head |
fdw_etl.dwd.dwd_assistant_service_log_ex、biz.recharge_records |
| 备注 | biz.customer_notes |
— |
CUST-2: 客户服务记录
GET /api/xcx/customers/{customerId}/records?year={year}&month={month}&table={tableId}
Response: {
customer_name: string,
customer_phone: string,
relation_index: string,
tables: Array<{ id: string, name: string }>,
records: Array<{
id: string,
date: string,
time_range: string,
table: string,
type: string,
type_class: string,
duration: number,
duration_raw?: number,
income: number,
drinks: string
}>,
has_more: boolean
}
客户模块错误码(CUST-1 / CUST-2 共用)
| HTTP 状态码 | 场景 | 响应示例 |
|---|---|---|
| 403 | 用户未通过审核(require_approved() 拦截) |
{ "code": 403, "message": "用户未通过审核,无法访问此资源" } |
| 404 | 客户不存在(customerId 无对应记录) |
{ "code": 404, "message": "客户不存在" } |
| 422 | 请求参数校验失败(year/month/page/pageSize 不合法) | Pydantic HTTPValidationError |
CUST-1 扩展模块(aiInsight/coachTasks/favoriteCoaches/consumptionRecords)查询失败时不返回错误码,而是对该模块返回空默认值(空数组或空对象),整体响应仍为 HTTP 200。
六、看板模块
BOARD-1: 助教看板
助教排行看板,支持排序×技能×时间三重筛选,4 种维度卡片(定档业绩/工资/客源储值/任务)。 数据源:
fdw_etl.v_dws_assistant_salary_calc(工资/定档)、fdw_etl.v_dws_recharge_summary(储值)、biz.tasks(任务) DWD-DOC 强制规则:金额字段使用items_sum口径(规则 1);助教费用使用assistant_pd_money+assistant_cx_money拆分(规则 2)
GET /api/xcx/board/coaches?sort={sort}&skill={skill}&time={time}
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
sort |
enum | 是 | 排序维度,6 种枚举:perf_desc(定档业绩最高)/ perf_asc(定档业绩最低)/ salary_desc(工资最高)/ salary_asc(工资最低)/ sv_desc(客源储值最高)/ task_desc(任务完成最多) |
skill |
enum | 是 | 技能筛选,5 种枚举:all(不限)/ chinese(🎱 中式/追分)/ snooker(斯诺克)/ mahjong(🀄 麻将/棋牌)/ karaoke(🎤 团建/K歌) |
time |
enum | 是 | 时间范围,6 种枚举:month(本月)/ quarter(本季度)/ last_month(上月)/ last_3m(前3个月不含本月)/ last_quarter(上季度)/ last_6m(最近6个月不含本月) |
交叉约束
| 约束 | 说明 |
|---|---|
time=last_6m + sort=sv_desc |
⛔ 不兼容,后端返回 HTTP 400 { code: 400, message: "最近6个月不支持客源储值排序" } |
原因:储值数据跨 6 个月聚合开销过大,且业务意义有限。前端
TIME_OPTIONS中已在last_6m选项文案标注"不支持客源储值最高"。
响应结构(TypeScript 类型定义)
/** 顶层响应 */
interface BoardCoachesResponse {
items: CoachBoardItem[]
}
/** 助教卡片 — 基础字段 + 4 维度专属字段 */
interface CoachBoardItem {
// ─── 基础字段(所有维度共享) ───
/** 助教 ID */
id: string
/** 助教姓名 */
name: string
/** 姓名首字(用于头像占位符) */
initial: string
/** 头像渐变色标识(blue/green/pink/amber/violet/cyan 等) */
avatarGradient: string
/** 等级英文 key:star / senior / middle / junior */
level: string
/**
* 技能标签列表
* - text: 技能 emoji 或文字(如 '🎱'、'斯'、'🀄'、'🎤')
* - cls: 前端样式类(如 'skill--chinese'、'skill--snooker')
*/
skills: Array<{ text: string; cls: string }>
/**
* Top 客户列表(含亲密度 emoji 前缀)
* 示例:['💖 王先生', '💖 李女士', '💛 赵总']
* 💖 = 高亲密度,💛 = 中亲密度
*/
topCustomers: string[]
// ─── perf 维度专属(sort=perf_desc / perf_asc 时展示) ───
/** 当期定档工时(小时) */
perfHours: number
/** 上期定档工时(小时),无上期数据时不返回 */
perfHoursBefore?: number
/** 距升档差距描述,如 '距升档 13.8h';已达标时不返回 */
perfGap?: string
/** 是否已达标当前档位 */
perfReached: boolean
// ─── salary 维度专属(sort=salary_desc / salary_asc 时展示) ───
/** 预估工资总额(元) */
salary: number
/** 工资维度-定档工时(小时) */
salaryPerfHours: number
/** 工资维度-上期定档工时(小时),无上期数据时不返回 */
salaryPerfBefore?: number
// ─── sv 维度专属(sort=sv_desc 时展示) ───
/** 客源储值总额(元) */
svAmount: number
/** 储值客户数 */
svCustomerCount: number
/** 储值消耗额(元) */
svConsume: number
// ─── task 维度专属(sort=task_desc 时展示) ───
/** 召回任务完成数 */
taskRecall: number
/** 回访任务完成数 */
taskCallback: number
}
字段说明表
基础字段(7 个,所有维度共享)
| 字段 | 类型 | 说明 | 数据源 |
|---|---|---|---|
id |
string | 助教唯一 ID | biz.users.id |
name |
string | 助教姓名 | biz.users.nickname |
initial |
string | 姓名首字,用于头像占位符渲染 | 后端从 name 提取 |
avatarGradient |
string | 头像渐变色标识(blue/green/pink/amber/violet/cyan) |
后端根据 ID 哈希分配 |
level |
string | 等级英文 key:star/senior/middle/junior |
fdw_etl.v_dws_assistant_salary_calc.coach_level |
skills |
Array<{ text: string, cls: string }> |
技能标签,text 为 emoji 或文字,cls 为前端样式类 |
biz.coach_skills 或配置表 |
topCustomers |
string[] |
Top 客户列表,含亲密度 emoji 前缀(💖/💛) | 后端按亲密度排序取 Top 3,拼接 emoji + 姓名 |
perf 维度专属字段(4 个)
| 字段 | 类型 | 说明 | 数据源 |
|---|---|---|---|
perfHours |
number | 当期定档工时(小时) | fdw_etl.v_dws_assistant_salary_calc,按 time 参数聚合 |
perfHoursBefore |
number? | 上期定档工时(小时),无上期数据时不返回 | 同上,取上一周期 |
perfGap |
string? | 距升档差距描述,如 '距升档 13.8h';已达标时不返回 |
后端根据档位阈值计算 |
perfReached |
boolean | 是否已达标当前档位 | 后端根据档位阈值判断 |
salary 维度专属字段(3 个)
| 字段 | 类型 | 说明 | 数据源 |
|---|---|---|---|
salary |
number | 预估工资总额(元),⚠️ 使用 items_sum 口径 |
fdw_etl.v_dws_assistant_salary_calc |
salaryPerfHours |
number | 工资维度-定档工时(小时) | 同上 |
salaryPerfBefore |
number? | 工资维度-上期定档工时(小时),无上期数据时不返回 | 同上,取上一周期 |
sv 维度专属字段(3 个)
| 字段 | 类型 | 说明 | 数据源 |
|---|---|---|---|
svAmount |
number | 客源储值总额(元) | fdw_etl.v_dws_recharge_summary,按助教关联客户聚合 |
svCustomerCount |
number | 储值客户数 | 同上,COUNT(DISTINCT customer_id) |
svConsume |
number | 储值消耗额(元) | 同上,消耗金额聚合 |
task 维度专属字段(2 个)
| 字段 | 类型 | 说明 | 数据源 |
|---|---|---|---|
taskRecall |
number | 召回任务完成数 | biz.tasks,task_type='priority_recall' AND status='completed' |
taskCallback |
number | 回访任务完成数 | biz.tasks,task_type='callback' AND status='completed' |
维度与排序的关系
前端根据 sort 参数决定展示哪组维度字段(卡片模板切换):
| sort 值 | 维度类型 | 展示字段 |
|---|---|---|
perf_desc / perf_asc |
perf | perfHours / perfHoursBefore / perfGap / perfReached |
salary_desc / salary_asc |
salary | salary / salaryPerfHours / salaryPerfBefore |
sv_desc |
sv | svAmount / svCustomerCount / svConsume |
task_desc |
task | taskRecall / taskCallback |
后端始终返回所有维度字段(避免多次请求),前端根据
sort选择性渲染对应卡片模板。
业务规则
| 规则 | 说明 |
|---|---|
time=last_6m + sort=sv_desc → HTTP 400 |
储值数据跨 6 个月聚合开销过大,后端拒绝此组合 |
items_sum 口径(DWD-DOC 规则 1) |
salary 等金额字段使用 items_sum 口径,禁止使用 consume_money |
| 助教费用拆分(DWD-DOC 规则 2) | 工资计算中基础课 = assistant_pd_money,激励课 = assistant_cx_money |
levelClass 前端计算 |
后端仅返回 level(英文 key),前端通过 LEVEL_CLASS 映射生成样式类 |
perfGap 后端计算 |
后端根据档位阈值计算差距描述字符串,已达标时不返回该字段 |
topCustomers emoji 规则 |
💖 = 高亲密度(关系指数 ≥ 0.7),💛 = 中亲密度(关系指数 < 0.7) |
BOARD-2: 客户看板
客户排行看板,支持 8 个维度查看前 100 名客户,每个维度展示不同的专属字段卡片。 数据源:
fdw_etl.v_dws_customer_*系列视图(召回/潜力/余额/充值/到店/消费/频率/专一度)、biz.tasks(任务关联)、fdw_etl.dwd.dim_member(会员信息) DWD-DOC 强制规则:金额字段使用items_sum口径(规则 1);会员信息通过member_idJOINdim_member获取(规则 DQ-6/DQ-7)
GET /api/xcx/board/customers?dimension={dim}&project={proj}&page=1&pageSize=20
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
dimension |
enum | 是 | 维度,8 种枚举:recall(最应召回)/ potential(最大消费潜力)/ balance(最高余额)/ recharge(最近充值)/ recent(最近到店)/ spend60(最高消费近60天)/ freq60(最频繁近60天)/ loyal(最专一近60天) |
project |
enum | 是 | 项目筛选,5 种枚举:all(全部)/ chinese(🎱 中式/追分)/ snooker(斯诺克)/ mahjong(🀄 麻将/棋牌)/ karaoke(🎤 团建/K歌) |
page |
number | 否 | 页码,默认 1 |
pageSize |
number | 否 | 每页条数,默认 20 |
响应结构(TypeScript 类型定义)
/** 顶层响应 */
interface BoardCustomersResponse {
items: CustomerBoardItem[]
total: number
page: number
pageSize: number
}
// ─── 助教信息(嵌套在客户卡片中) ───
interface AssistantInfo {
/** 助教姓名 */
name: string
/**
* 前端样式类
* - 'assistant--assignee':当前跟进助教
* - 'assistant--abandoned':已放弃的助教
* - 'assistant--normal':普通关联助教
*/
cls: string
/** 亲密度分数 0-10,供 heart-icon 组件渲染 */
heartScore: number
/** 角标文字,如 '跟'(跟进中)/ '弃'(已放弃) */
badge?: string
/** 角标样式类,如 'assistant-badge--follow' / 'assistant-badge--drop' */
badgeCls?: string
}
/** 客户卡片 — 基础字段 + 8 维度专属字段 */
interface CustomerBoardItem {
// ─── 基础字段(所有维度共享,5 个) ───
/** 客户唯一 ID */
id: string
/** 客户姓名 */
name: string
/** 姓名首字(用于头像占位符) */
initial: string
/** 头像样式类(avatar--amber / avatar--pink / avatar--blue 等) */
avatarCls: string
/**
* 关联助教列表
* 按亲密度降序排列,含跟进状态角标
*/
assistants: AssistantInfo[]
// ─── recall 维度专属(dimension=recall 时展示) ───
/** 理想到店间隔(天) */
idealDays?: number
/** 已过天数(距上次到店) */
elapsedDays?: number
/** 超期天数(elapsedDays - idealDays,> 0 表示超期) */
overdueDays?: number
/** 近 30 天到店次数 */
visits30d?: number
/** 客户余额(格式化字符串,如 '¥2,680') */
balance?: string
/** 召回指数(0-10 评分,如 '9.2') */
recallIndex?: string
// ─── potential 维度专属(dimension=potential 时展示) ───
/**
* 潜力标签列表
* - text: 标签文字(如 '高频'、'高客单'、'高余额'、'低频')
* - theme: 标签主题色('primary' / 'warning' / 'success' / 'gray')
*/
potentialTags?: Array<{ text: string; theme: string }>
/** 近 30 天消费(格式化字符串,如 '¥4,200') */
spend30d?: string
/** 平均到店频率(如 '6.2次') */
avgVisits?: string
/** 平均客单价(格式化字符串,如 '¥680') */
avgSpend?: string
// ─── balance 维度专属(dimension=balance 时展示) ───
// balance 字段复用上方 recall 维度的 balance(格式化字符串)
/** 最后到店时间(如 '3天前'、'12天前') */
lastVisit?: string
/** 月均消费(格式化字符串,如 '¥3,500') */
monthlyConsume?: string
/** 可用月数(如 '约0.8个月'、'约4.6个月') */
availableMonths?: string
// ─── recharge 维度专属(dimension=recharge 时展示) ───
/** 最近充值日期(如 '2月15日') */
lastRecharge?: string
/** 充值金额(格式化字符串,如 '¥5,000') */
rechargeAmount?: string
/** 近 60 天充值次数(如 '2次') */
recharges60d?: string
/** 当前余额(格式化字符串,如 '¥2,680') */
currentBalance?: string
// ─── recent 维度专属(dimension=recent 时展示) ───
/** 距上次到店天数 */
daysAgo?: number
/** 到店频率(如 '6.2次/月') */
visitFreq?: string
// idealDays 复用 recall 维度字段
// visits30d 复用 recall 维度字段
// avgSpend 复用 potential 维度字段
// ─── spend60 维度专属(dimension=spend60 时展示) ───
/** 近 60 天消费(格式化字符串,如 '¥8,400') */
spend60d?: string
/** 近 60 天到店次数(如 '12') */
visits60d?: string
/** 是否高消费标记(true 时前端显示高消费标签) */
highSpendTag?: boolean
// avgSpend 复用 potential 维度字段
// ─── freq60 维度专属(dimension=freq60 时展示) ───
// visits60d 复用 spend60 维度字段
/** 平均到店间隔(天,如 '5.0天') */
avgInterval?: string
/**
* 最近 8 周到店柱状图数据
* - val: 该周到店次数
* - pct: 百分比高度(0-100,相对于最大值)
* 数组长度固定为 8,从最早一周到最近一周排列
*/
weeklyVisits?: Array<{ val: number; pct: number }>
// spend60d 复用 spend60 维度字段
// ─── loyal 维度专属(dimension=loyal 时展示) ───
/** 专一度指数(0-100,如 '92') */
intimacy?: string
/** Top 1 助教姓名 */
topCoachName?: string
/** Top 1 助教亲密度(heart-icon 分数,0-10) */
topCoachHeart?: number
/** Top 1 助教关系指数(如 '9.2') */
topCoachScore?: string
/** 主助教姓名(占比最高的助教) */
coachName?: string
/** 主助教服务占比(如 '78%') */
coachRatio?: string
/**
* 助教明细列表(按服务占比降序)
* 展示每位助教的服务统计和关系指数
*/
coachDetails?: Array<{
/** 助教姓名 */
name: string
/** 前端样式类(同 AssistantInfo.cls) */
cls: string
/** 亲密度分数 0-10 */
heartScore: number
/** 角标文字('跟' / '弃'),无角标时不返回 */
badge?: string
/** 平均服务时长(如 '2.3h') */
avgDuration: string
/** 服务次数(如 '14') */
serviceCount: string
/** 该助教关联消费金额(格式化字符串,如 '¥4,200') */
coachSpend: string
/** 关系指数(0-10) */
relationIdx: number
}>
}
字段说明表
基础字段(5 个,所有维度共享)
| 字段 | 类型 | 说明 | 数据源 |
|---|---|---|---|
id |
string | 客户唯一 ID | fdw_etl.dwd.dim_member.member_id |
name |
string | 客户姓名 | fdw_etl.dwd.dim_member.nickname(通过 member_id JOIN,DQ-6) |
initial |
string | 姓名首字,用于头像占位符渲染 | 后端从 name 提取 |
avatarCls |
string | 头像样式类(avatar--amber/avatar--pink/avatar--blue 等) |
后端根据 ID 哈希分配 |
assistants |
AssistantInfo[] |
关联助教列表,含亲密度和跟进状态 | biz.tasks + fdw_etl.dws 亲密度计算 |
recall 维度专属字段(6 个)
| 字段 | 类型 | 说明 | 数据源 |
|---|---|---|---|
idealDays |
number | 理想到店间隔(天),基于历史到店频率计算 | fdw_etl.v_dws_customer_recall |
elapsedDays |
number | 距上次到店已过天数 | 同上,CURRENT_DATE - last_visit_date |
overdueDays |
number | 超期天数(elapsedDays - idealDays) |
后端计算 |
visits30d |
number | 近 30 天到店次数 | fdw_etl.v_dws_customer_recall |
balance |
string | 客户余额(格式化),⚠️ 使用 items_sum 口径 |
fdw_etl.dwd.dim_member_card_account |
recallIndex |
string | 召回指数(0-10),综合超期天数、余额、频率加权 | 后端算法计算 |
potential 维度专属字段(4 个)
| 字段 | 类型 | 说明 | 数据源 |
|---|---|---|---|
potentialTags |
Array<{ text, theme }> |
潜力标签(高频/高客单/高余额/低频等),theme 取值 primary/warning/success/gray |
后端根据消费特征生成 |
spend30d |
string | 近 30 天消费(格式化),⚠️ 使用 items_sum 口径 |
fdw_etl.v_dws_customer_potential |
avgVisits |
string | 平均到店频率(如 '6.2次') | 同上 |
avgSpend |
string | 平均客单价(格式化),⚠️ 使用 items_sum 口径 |
同上,spend / visits |
balance 维度专属字段(4 个)
| 字段 | 类型 | 说明 | 数据源 |
|---|---|---|---|
balance |
string | 客户余额(格式化),与 recall 维度共用字段 | fdw_etl.dwd.dim_member_card_account |
lastVisit |
string | 最后到店时间描述(如 '3天前') | fdw_etl.v_dws_customer_balance |
monthlyConsume |
string | 月均消费(格式化),⚠️ 使用 items_sum 口径 |
同上 |
availableMonths |
string | 可用月数(balance / monthlyConsume,如 '约0.8个月') |
后端计算 |
recharge 维度专属字段(4 个)
| 字段 | 类型 | 说明 | 数据源 |
|---|---|---|---|
lastRecharge |
string | 最近充值日期(如 '2月15日') | fdw_etl.v_dws_customer_recharge |
rechargeAmount |
string | 最近充值金额(格式化) | 同上 |
recharges60d |
string | 近 60 天充值次数(如 '2次') | 同上 |
currentBalance |
string | 当前余额(格式化) | fdw_etl.dwd.dim_member_card_account |
recent 维度专属字段(5 个)
| 字段 | 类型 | 说明 | 数据源 |
|---|---|---|---|
daysAgo |
number | 距上次到店天数 | fdw_etl.v_dws_customer_recent |
visitFreq |
string | 到店频率(如 '6.2次/月') | 同上 |
idealDays |
number | 理想到店间隔(天),与 recall 维度共用字段 | fdw_etl.v_dws_customer_recall |
visits30d |
number | 近 30 天到店次数,与 recall 维度共用字段 | 同上 |
avgSpend |
string | 平均客单价,与 potential 维度共用字段 | fdw_etl.v_dws_customer_potential |
spend60 维度专属字段(4 个)
| 字段 | 类型 | 说明 | 数据源 |
|---|---|---|---|
spend60d |
string | 近 60 天消费(格式化),⚠️ 使用 items_sum 口径 |
fdw_etl.v_dws_customer_spend60 |
visits60d |
string | 近 60 天到店次数 | 同上 |
highSpendTag |
boolean | 是否高消费标记(true 时前端显示高消费标签) |
后端根据阈值判断 |
avgSpend |
string | 平均客单价,与 potential 维度共用字段 | fdw_etl.v_dws_customer_potential |
freq60 维度专属字段(4 个)
| 字段 | 类型 | 说明 | 数据源 |
|---|---|---|---|
visits60d |
string | 近 60 天到店次数,与 spend60 维度共用字段 | fdw_etl.v_dws_customer_spend60 |
avgInterval |
string | 平均到店间隔(如 '5.0天') | fdw_etl.v_dws_customer_freq60 |
weeklyVisits |
Array<{ val, pct }> |
最近 8 周到店柱状图,val 为次数,pct 为百分比高度(0-100) |
同上,按周聚合 |
spend60d |
string | 近 60 天消费,与 spend60 维度共用字段 | fdw_etl.v_dws_customer_spend60 |
loyal 维度专属字段(7 个)
| 字段 | 类型 | 说明 | 数据源 |
|---|---|---|---|
intimacy |
string | 专一度指数(0-100) | fdw_etl.v_dws_customer_loyal |
topCoachName |
string | Top 1 助教姓名 | 同上,按服务占比排序取第一 |
topCoachHeart |
number | Top 1 助教亲密度(0-10,heart-icon 用) | 同上 |
topCoachScore |
string | Top 1 助教关系指数(如 '9.2') | 同上 |
coachName |
string | 主助教姓名(占比最高) | 同上 |
coachRatio |
string | 主助教服务占比(如 '78%') | 同上 |
coachDetails |
Array<{ name, cls, heartScore, badge?, avgDuration, serviceCount, coachSpend, relationIdx }> |
助教明细列表,按服务占比降序 | 同上,逐助教聚合 |
维度与展示字段的关系
前端根据 dimension 参数决定展示哪组维度字段(卡片模板切换):
| dimension 值 | 维度类型 | 展示字段 |
|---|---|---|
recall |
召回 | idealDays / elapsedDays / overdueDays / visits30d / balance / recallIndex |
potential |
潜力 | potentialTags / spend30d / avgVisits / avgSpend |
balance |
余额 | balance / lastVisit / monthlyConsume / availableMonths |
recharge |
充值 | lastRecharge / rechargeAmount / recharges60d / currentBalance |
recent |
到店 | daysAgo / visitFreq / idealDays / visits30d / avgSpend |
spend60 |
消费60天 | spend60d / visits60d / highSpendTag / avgSpend |
freq60 |
频率60天 | visits60d / avgInterval / weeklyVisits / spend60d |
loyal |
专一度 | intimacy / topCoachName / topCoachHeart / topCoachScore / coachName / coachRatio / coachDetails |
后端按
dimension参数仅返回对应维度的专属字段(减少传输量),前端根据dimension选择性渲染对应卡片模板。
业务规则
| 规则 | 说明 |
|---|---|
items_sum 口径(DWD-DOC 规则 1) |
balance、spend30d、avgSpend、monthlyConsume、spend60d、coachSpend 等金额字段使用 items_sum 口径,禁止使用 consume_money |
| 会员信息 JOIN(DWD-DOC 规则 DQ-6) | 客户姓名通过 member_id JOIN dim_member(nickname,scd2_is_current=1),禁止直接使用 member_phone/member_name |
| 会员卡余额 JOIN(DWD-DOC 规则 DQ-7) | 余额通过 member_id JOIN dim_member_card_account(scd2_is_current=1),禁止直接使用 member_card_type_name |
assistants 排序规则 |
按亲密度(heartScore)降序排列;当前跟进助教(cls='assistant--assignee')置顶 |
weeklyVisits 固定 8 周 |
数组长度固定为 8,从最早一周到最近一周排列;pct 为相对于 8 周最大值的百分比 |
coachDetails 排序规则 |
按服务占比降序排列;badge 仅在跟进('跟')或放弃('弃')状态时返回 |
| 分页上限 | 单次最多返回 100 条(pageSize 上限 100),超出截断 |
数据源映射
| 维度 | FDW 视图 | 说明 |
|---|---|---|
| recall | fdw_etl.v_dws_customer_recall |
召回指数、理想间隔、超期天数 |
| potential | fdw_etl.v_dws_customer_potential |
潜力标签、消费频率、客单价 |
| balance | fdw_etl.v_dws_customer_balance |
余额、月均消费、可用月数 |
| recharge | fdw_etl.v_dws_customer_recharge |
充值记录、充值频率 |
| recent | fdw_etl.v_dws_customer_recent |
到店天数、到店频率 |
| spend60 | fdw_etl.v_dws_customer_spend60 |
60 天消费、到店次数 |
| freq60 | fdw_etl.v_dws_customer_freq60 |
到店间隔、周柱状图 |
| loyal | fdw_etl.v_dws_customer_loyal |
专一度、助教明细 |
| 会员基础 | fdw_etl.dwd.dim_member |
姓名、手机号(DQ-6) |
| 会员卡 | fdw_etl.dwd.dim_member_card_account |
余额(DQ-7) |
| 助教关联 | biz.tasks + biz.users |
跟进状态、角标 |
BOARD-3: 财务看板
⚠️ 全项目最复杂的单个接口,6 个独立板块嵌套结构。 数据源:
fdw_etl.v_dws_finance_*系列视图 +fdw_etl.v_dws_assistant_salary_calcDWD-DOC 强制规则:所有金额字段使用items_sum口径(规则 1);助教费用使用assistant_pd_money+assistant_cx_money拆分(规则 2)
GET /api/xcx/board/finance?time={time}&area={area}&compare={0|1}
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
time |
enum | 是 | 时间范围,8 种枚举:month(本月)/ lastMonth(上月)/ week(本周)/ lastWeek(上周)/ quarter3(前3个月不含本月)/ quarter(本季度)/ lastQuarter(上季度)/ half6(最近6个月不含本月) |
area |
enum | 是 | 区域筛选,7 种枚举:all(全部区域)/ hall(大厅)/ hallA(A区)/ hallB(B区)/ hallC(C区)/ mahjong(麻将房)/ teamBuilding(团建房) |
compare |
0 | 1 |
否 | 是否返回环比数据,默认 0。compare=0 时所有 xxxCompare / isDown / isFlat 字段不返回(减少查询开销) |
响应结构(TypeScript 类型定义)
/** 顶层响应 */
interface BoardFinanceResponse {
overview: OverviewSection
recharge: RechargeSection | null // area ≠ all 时为 null(储值卡数据不按区域拆分)
revenue: RevenueSection
cashflow: CashflowSection
expense: ExpenseSection
coachAnalysis: CoachAnalysisSection
}
// ─── 环比字段通用模式 ───
// 每个核心指标附带 3 个环比字段:
// xxxCompare: string — 环比百分比,如 "12.5%"、"持平"
// isDown: boolean — 是否下降(true = 负向变化)
// isFlat: boolean — 是否持平(true 时 compare 显示 "持平")
// compare=0 时这 3 个字段不返回
// ─── 1. 经营一览 overview ───
interface OverviewSection {
/** 发生额/正价 — 所有消费项目按标价计算的总金额 */
occurrence: string // 格式化金额,如 "¥823,456"
occurrenceCompare?: string
occurrenceDown?: boolean
occurrenceFlat?: boolean
/** 总优惠 — 会员折扣 + 赠送卡抵扣 + 团购差价等 */
discount: string // 负值,如 "-¥113,336"
discountCompare?: string
discountDown?: boolean
discountFlat?: boolean
/** 折扣率 — discount / occurrence */
discountRate: string // 百分比,如 "13.8%"
discountRateCompare?: string
discountRateDown?: boolean
discountRateFlat?: boolean
/** 成交/确认收入 — occurrence - discount */
confirmedRevenue: string
confirmedCompare?: string
confirmedDown?: boolean
confirmedFlat?: boolean
/** 实收/现金流入 — 实际到账现金(消费直接支付 + 储值充值) */
cashIn: string
cashInCompare?: string
cashInDown?: boolean
cashInFlat?: boolean
/** 现金支出 — 人工 + 房租 + 水电 + 进货等 */
cashOut: string
cashOutCompare?: string
cashOutDown?: boolean
cashOutFlat?: boolean
/** 现金结余 — cashIn - cashOut */
cashBalance: string
cashBalanceCompare?: string
cashBalanceDown?: boolean
cashBalanceFlat?: boolean
/** 结余率 — cashBalance / cashIn */
balanceRate: string // 百分比,如 "32.4%"
balanceRateCompare?: string
balanceRateDown?: boolean
balanceRateFlat?: boolean
}
// ─── 2. 预收资产 recharge ───
// ⚠️ area ≠ all 时整个板块返回 null(储值卡数据不按区域拆分)
interface RechargeSection {
/** 储值卡充值实收 */
actualIncome: string
actualCompare?: string
actualDown?: boolean
actualFlat?: boolean
/** 首充 */
firstCharge: string
firstChargeCompare?: string
firstChargeDown?: boolean
firstChargeFlat?: boolean
/** 续费 */
renewCharge: string
renewChargeCompare?: string
renewChargeDown?: boolean
renewChargeFlat?: boolean
/** 消耗 — 会员使用储值卡消费的金额 */
consumed: string
consumedCompare?: string
consumedDown?: boolean
consumedFlat?: boolean
/** 储值卡总余额 */
cardBalance: string
cardBalanceCompare?: string
cardBalanceDown?: boolean
cardBalanceFlat?: boolean
/**
* 赠送卡 3×4 矩阵
* 行:新增 / 消费 / 余额
* 列:合计 / 酒水卡 / 台费卡 / 抵用券
* 每个单元格含值和环比字段
*/
giftRows: GiftRow[]
/** 全类别会员卡余额合计 — 储值卡 + 赠送卡(酒水卡 + 台费卡 + 抵用券) */
allCardBalance: string
allCardBalanceCompare?: string
allCardBalanceDown?: boolean
allCardBalanceFlat?: boolean
}
interface GiftRow {
label: string // "新增" | "消费" | "余额"
total: string // 合计金额
totalCompare?: string
wine: string // 酒水卡
wineCompare?: string
table: string // 台费卡
tableCompare?: string
coupon: string // 抵用券
couponCompare?: string
}
// ─── 3. 应计收入确认 revenue ───
interface RevenueSection {
/**
* 收入结构表 — 9 行含子行标记
* 主行:开台与包厢 / 助教(基础课) / 助教(激励课) / 食品酒水
* 子行(isSub=true):A区 / B区 / C区 / 团建区 / 麻将区(属于"开台与包厢"的子行)
*
* ⚠️ DWD-DOC 规则 2:助教行金额来自 assistant_pd_money(基础课/陪打)和 assistant_cx_money(激励课/超休)
*/
structureRows: RevenueStructureRow[]
/** 正价明细 — 4 项 */
priceItems: RevenueDetailItem[]
/** 发生额合计 */
totalOccurrence: string
totalOccurrenceCompare?: string
/** 优惠明细 — 4 项 */
discountItems: RevenueDetailItem[]
/** 确认收入合计 — occurrence - discount */
confirmedTotal: string
confirmedTotalCompare?: string
confirmedTotalDown?: boolean
confirmedTotalFlat?: boolean
/** 渠道明细 — 3 项(储值卡结算冲销 / 现金线上支付 / 团购核销确认收入) */
channelItems: RevenueDetailItem[]
}
interface RevenueStructureRow {
id: string // 行标识,如 "table" / "area-a" / "coach-basic" / "food"
name: string // 行名称,如 "开台与包厢" / "A区" / "助教" / "食品酒水"
desc?: string // 行描述,如 "基础课" / "激励课"
amount: string // 发生额
discount: string // 优惠金额(无优惠时为 "-")
booked: string // 入账金额
bookedCompare?: string // 入账环比
isSub?: boolean // 是否为子行(缩进展示)
}
interface RevenueDetailItem {
name: string // 项目名称
desc?: string // 补充说明,如 "台桌卡+酒水卡+抵用券"
value: string // 金额
compare?: string // 环比
}
// ─── 4. 现金流入 cashflow ───
interface CashflowSection {
/** 消费收款 — 3 项(纸币现金 / 线上收款 / 团购平台) */
consumeItems: CashflowItem[]
/** 充值收款 — 1 项(会员充值到账) */
rechargeItems: CashflowItem[]
/** 合计 */
total: string
totalCompare?: string
totalDown?: boolean
totalFlat?: boolean
}
interface CashflowItem {
name: string // 项目名称
desc: string // 补充说明,如 "柜台现金收款"
value: string // 金额
compare?: string // 环比
isDown?: boolean // 是否下降
}
// ─── 5. 现金流出 expense ───
interface ExpenseSection {
/** 经营支出 — 3 项(食品饮料 / 耗材 / 报销) */
operationItems: ExpenseItem[]
/** 固定支出 — 4 项(房租 / 水电 / 物业 / 人员工资) */
fixedItems: ExpenseItem[]
/**
* 助教分成 — 4 项(基础课分成 / 激励课分成 / 充值提成 / 额外奖金)
* ⚠️ DWD-DOC 规则 2:基础课分成来自 assistant_pd_money,激励课分成来自 assistant_cx_money
*/
coachItems: ExpenseItem[]
/** 平台服务费 — 3 项(汇来米 / 美团 / 抖音) */
platformItems: ExpenseItem[]
/** 合计 */
total: string
totalCompare?: string
totalDown?: boolean
totalFlat?: boolean
}
interface ExpenseItem {
name: string // 项目名称
value: string // 金额
compare?: string // 环比,如 "4.5%" 或 "持平"
isDown?: boolean // 是否下降
isFlat?: boolean // 是否持平
}
// ─── 6. 助教分析 coachAnalysis ───
interface CoachAnalysisSection {
/** 基础课(陪打)— assistant_pd_money */
basic: CoachAnalysisTable
/** 激励课(超休)— assistant_cx_money */
incentive: CoachAnalysisTable
}
interface CoachAnalysisTable {
/** 汇总行 */
totalPay: string // 总课时费
totalPayCompare?: string
totalPayDown?: boolean
totalShare: string // 总分成
totalShareCompare?: string
totalShareDown?: boolean
avgHourly: string // 平均时薪,如 "¥25/h"
avgHourlyCompare?: string
avgHourlyFlat?: boolean
/** 按等级分行 — 初级 / 中级 / 高级 / 星级 */
rows: CoachAnalysisRow[]
}
interface CoachAnalysisRow {
level: string // "初级" | "中级" | "高级" | "星级"
pay: string // 课时费
payCompare?: string
payDown?: boolean
share: string // 分成
shareCompare?: string
shareDown?: boolean
hourly: string // 时薪,如 "¥20/h"
hourlyCompare?: string // 如 "持平"
hourlyFlat?: boolean // 是否持平
}
业务规则
| 规则 | 说明 |
|---|---|
area ≠ all 时 recharge = null |
储值卡数据不按区域拆分,非全部区域时预收资产板块不返回 |
items_sum 口径(DWD-DOC 规则 1) |
所有金额字段使用 items_sum = table_charge_money + goods_money + assistant_pd_money + assistant_cx_money + electricity_money,禁止使用 consume_money |
| 助教费用拆分(DWD-DOC 规则 2) | 基础课 = assistant_pd_money(陪打),激励课 = assistant_cx_money(超休),禁止使用 service_fee |
| 支付渠道恒等式(DWD-DOC 规则 3) | balance_amount = recharge_card_amount + gift_card_amount,三者不可重复计算 |
| 折扣互斥(DWD-DOC 规则 6) | discount_manual(大客户优惠)与 discount_other 互斥,两者之和 = adjust_amount |
| 现金流互斥(DWD-DOC 规则 7) | platform_settlement_amount 和 groupbuy_pay_amount 互斥 |
| 环比计算 | 后端根据 time 参数计算当期和上期日期范围,分别查询后计算百分比;compare=0 时不计算 |
数据源映射
| 板块 | FDW 视图 | 说明 |
|---|---|---|
| overview | fdw_etl.v_dws_finance_overview |
8 指标 + 8 环比 |
| recharge | fdw_etl.v_dws_finance_recharge |
储值卡 + 赠送卡矩阵 |
| revenue | fdw_etl.v_dws_finance_revenue |
收入结构表 + 明细 |
| cashflow | fdw_etl.v_dws_finance_cashflow |
消费收款 + 充值收款 |
| expense | fdw_etl.v_dws_finance_expense |
4 子分组 |
| coachAnalysis | fdw_etl.v_dws_assistant_salary_calc |
按等级分行聚合 |
字段说明表
overview 经营一览(8 指标)
| 字段 | 类型 | 说明 | 环比字段 |
|---|---|---|---|
occurrence |
string | 发生额/正价 | occurrenceCompare + occurrenceDown + occurrenceFlat |
discount |
string | 总优惠(负值) | discountCompare + discountDown + discountFlat |
discountRate |
string | 折扣率(百分比) | discountRateCompare + discountRateDown + discountRateFlat |
confirmedRevenue |
string | 成交/确认收入 | confirmedCompare + confirmedDown + confirmedFlat |
cashIn |
string | 实收/现金流入 | cashInCompare + cashInDown + cashInFlat |
cashOut |
string | 现金支出 | cashOutCompare + cashOutDown + cashOutFlat |
cashBalance |
string | 现金结余 | cashBalanceCompare + cashBalanceDown + cashBalanceFlat |
balanceRate |
string | 结余率(百分比) | balanceRateCompare + balanceRateDown + balanceRateFlat |
recharge 预收资产
| 字段 | 类型 | 说明 |
|---|---|---|
actualIncome |
string | 储值卡充值实收 |
firstCharge |
string | 首充 |
renewCharge |
string | 续费 |
consumed |
string | 消耗 |
cardBalance |
string | 储值卡总余额 |
giftRows |
GiftRow[] | 赠送卡 3×4 矩阵(新增/消费/余额 × 合计/酒水卡/台费卡/抵用券) |
allCardBalance |
string | 全类别会员卡余额合计 |
revenue 应计收入确认
| 字段 | 类型 | 说明 |
|---|---|---|
structureRows |
RevenueStructureRow[] | 收入结构表(9 行含子行) |
priceItems |
RevenueDetailItem[] | 正价明细(4 项:开台消费/酒水商品/包厢费用/助教服务) |
totalOccurrence |
string | 发生额合计 |
discountItems |
RevenueDetailItem[] | 优惠明细(4 项:团购优惠/手动调整+大客户优惠/赠送卡抵扣/其他优惠) |
confirmedTotal |
string | 确认收入合计 |
channelItems |
RevenueDetailItem[] | 渠道明细(3 项:储值卡结算冲销/现金线上支付/团购核销确认收入) |
coachAnalysis 助教分析
| 子表 | 数据源字段 | 说明 |
|---|---|---|
basic |
assistant_pd_money |
基础课(陪打)按初级/中级/高级/星级分行 |
incentive |
assistant_cx_money |
激励课(超休)按初级/中级/高级/星级分行 |
七、助教模块(已实现 ✅)
COACH-1: 助教详情
数据源:
zqyy_app.coaches(基本信息)、etl_feiqiu.dws.*(绩效/收入,via FDW)、zqyy_app.coach_tasks(任务)、zqyy_app.coach_notes(备注) 金额口径:所有消费金额使用items_sum(DWD-DOC 强制规则 1);助教费用使用assistant_pd_money+assistant_cx_money拆分(DWD-DOC 强制规则 2) 会员信息:通过member_idJOINdim_member获取(DWD-DOC 强制规则 DQ-6),禁止直接使用member_phone
GET /api/xcx/coaches/{coachId}
响应结构
interface CoachDetailResponse {
// ── 基本信息 ──
id: string
name: string
avatar: string
level: string // 初级/中级/高级/星级
skills: string[] // 技能标签,如 ['中🎱', '🎯斯诺克']
workYears: number // 工龄(年)
customerCount: number // 客户数
hireDate: string // 入职日期,如 '2023-03-15'
// ── 绩效指标(performance) ──
performance: {
monthlyHours: number // 本月定档工时
monthlySalary: number // 本月工资(预估)
customerBalance: number // 客源储值余额合计
tasksCompleted: number // 本月任务完成数
perfCurrent: number // 当前绩效值
perfTarget: number // 绩效目标值
}
// ── 收入明细(income) ──
// thisMonth / lastMonth 各含 4 项收入分类
income: {
thisMonth: IncomeItem[]
lastMonth: IncomeItem[]
}
// ── 档位节点(tierNodes) ──
// 供前端绩效进度条组件使用,如 [0, 100, 130, 160, 190, 220]
tierNodes: number[]
// ── 任务分组 ──
visibleTasks: TaskItem[] // 可见任务
hiddenTasks: TaskItem[] // 隐藏任务
abandonedTasks: AbandonedTask[] // 已放弃任务
// ── Top 客户(topCustomers) ──
topCustomers: TopCustomer[]
// ── 近期服务记录(serviceRecords) ──
serviceRecords: ServiceRecord[]
// ── 历史月份统计(historyMonths) ──
// 最近 5+ 个月的汇总数据
historyMonths: HistoryMonth[]
// ── 备注(notes) ──
notes: NoteItem[]
}
子类型定义
/** 收入分项 */
interface IncomeItem {
label: string // 如 '基础课时费'、'激励课时费'、'充值提成'、'酒水提成'
amount: string // 格式化金额,如 '¥3,500'
color: string // 主题色:primary / success / warning / purple
}
/** 任务项(visibleTasks / hiddenTasks) */
interface TaskItem {
typeLabel: string // 任务类型标签,如 '高优先召回'、'客户回访'
typeClass: string // 样式类:high-priority / priority / callback / relationship
customerName: string // 客户姓名
customerId?: string // 客户 ID(用于跳转 customer-detail)
noteCount: number // 备注数量
pinned: boolean // 是否置顶
notes?: Array<{ // 备注列表(可选)
pinned?: boolean // 是否置顶备注
text: string // 备注内容
date: string // 日期,如 '2026-02-06'
}>
}
/** 已放弃任务 */
interface AbandonedTask {
customerName: string
reason: string // 放弃原因,如 '客户拒绝'、'超时未响应'
}
/** Top 客户(扩展版,含关系指数和余额) */
interface TopCustomer {
id: string
name: string
initial: string // 姓氏首字,如 '王'
avatarGradient: string // 头像渐变色:pink / amber / green / blue / violet / teal
heartEmoji: string // 关系 emoji(P6 AC3 四级映射):💖 / 🧡 / 💛 / 💙
relationScore: string // 关系指数(0-10),如 '9.5'
scoreColor: string // 分数颜色:#FF6B6B / #FF8C00 / #FFA726 / #5B9BD5
serviceCount: number // 服务次数
balance: string // 余额(格式化),如 '¥8,600'
consume: string // 消费总额(格式化,items_sum 口径),如 '¥12,800'
}
/** 近期服务记录 */
interface ServiceRecord {
customerId?: string // 客户 ID(用于跳转 customer-detail)
customerName: string
initial: string
avatarGradient: string
type: string // 课程类型:'基础课' / '激励课'
typeClass: string // 样式类:basic / incentive
table: string // 台桌名,如 'A12号台'
duration: string // 时长,如 '2.5h'
income: string // 收入(格式化),如 '¥200'
date: string // 日期时间,如 '2026-02-07 21:30'
perfHours?: string // 折算工时(可选),如 '2h'
}
/** 历史月份统计 */
interface HistoryMonth {
month: string // 月份标签:'本月' / '上月' / '4月' 等
estimated: boolean // 是否为预估数据
customers: string // 客户数(格式化),如 '22人'
hours: string // 工时(格式化),如 '87.5h'
salary: string // 工资(格式化),如 '¥6,950'
callbackDone: number // 回访任务完成数
recallDone: number // 召回任务完成数
}
/** 备注项 */
interface NoteItem {
id: string
content: string // 备注内容
timestamp: string // ISO 时间戳,用于排序
aiScore?: number // AI 应用 6 评分(1-10,展示用,星数 = aiScore ÷ 2)
manualScore?: number // 用户手动评分(1-5 星,输入用)
customerName: string // 关联客户/操作人
tagLabel: string // 标签
createdAt: string // 格式化时间,如 '2026-03-05 14:30'
}
字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
performance.monthlyHours |
number | 本月定档工时(折算后) |
performance.monthlySalary |
number | 本月工资预估(含基础+激励+提成) |
performance.customerBalance |
number | 该助教所有客户的储值余额合计 |
performance.tasksCompleted |
number | 本月已完成任务数 |
performance.perfCurrent |
number | 当前绩效进度值 |
performance.perfTarget |
number | 绩效目标值 |
tierNodes |
number[] | 档位节点数组,如 [0, 100, 130, 160, 190, 220],前端用于绘制进度条刻度 |
income.thisMonth / lastMonth |
IncomeItem[] | 各含 4 项:基础课时费 / 激励课时费 / 充值提成 / 酒水提成 |
topCustomers[].consume |
string | 消费总额,使用 items_sum 口径 |
serviceRecords[].perfHours |
string? | 折算工时,仅激励课有值 |
serviceRecords[].customerId |
string? | 客户 ID,用于从服务记录跳转到客户详情 |
visibleTasks[].customerId |
string? | 客户 ID,用于从任务项跳转到客户详情 |
historyMonths |
HistoryMonth[] | 最近 5+ 个月,第一条为本月(estimated: true) |
助教模块错误码(COACH-1)
| HTTP 状态码 | 场景 | 响应示例 |
|---|---|---|
| 403 | 用户未通过审核(require_approved() 拦截) |
{ "code": 403, "message": "用户未通过审核,无法访问此资源" } |
| 404 | 助教不存在(coachId 无对应记录) |
{ "code": 404, "message": "助教不存在" } |
| 422 | 请求参数校验失败(coachId 类型不合法) | Pydantic HTTPValidationError |
COACH-1 扩展模块(income/topCustomers/serviceRecords/historyMonths/notes)查询失败时不返回错误码,而是对该模块返回空默认值,整体响应仍为 HTTP 200。
八、对话模块
数据源:
zqyy_app.chat_sessions(对话会话)、zqyy_app.chat_messages(消息) 时间字段统一使用createdAt(camelCase),替代前端timestamp和旧契约created_at
CHAT-1: 对话历史列表
GET /api/xcx/chat/history?page=1&pageSize=20
响应结构
interface ChatHistoryResponse {
items: Array<{
id: string // 对话 ID
title: string // 对话标题(新增,需求 5.8.2)
customerName?: string // 关联客户姓名(可选)
lastMessage: string // 最后一条消息摘要
timestamp: string // 最后消息时间(ISO 8601)
unreadCount: number // 未读消息数
}>
total: number
page: number
pageSize: number
}
CHAT-2: 对话消息
支持
customerId查询参数(需求 5.8.5):后端根据customerId自动查找或创建对话,返回对应的chatId和消息列表
GET /api/xcx/chat/{chatId}/messages?page=1&pageSize=50
GET /api/xcx/chat/messages?customerId={customerId}&page=1&pageSize=50
响应结构
interface ChatMessagesResponse {
chatId: string // 对话 ID(customerId 入口时由后端返回)
items: Array<{
id: string
role: 'user' | 'assistant'
content: string
createdAt: string // 统一时间字段名(需求 5.8.1),ISO 8601
// 引用卡片(可选,需求 5.8.3)
referenceCard?: {
type: 'customer' | 'record' // 引用类型
title: string // 卡片标题,如 '张伟 — 消费概览'
summary: string // 摘要文字
data: Record<string, string> // 键值对详情,如 { '近30天消费': '¥2,380', '到店次数': '8次' }
}
}>
total: number
page: number
pageSize: number
}
CHAT-3: 发送消息
POST /api/xcx/chat/{chatId}/messages
Body: { content: string }
响应结构
interface SendMessageResponse {
userMessage: {
id: string
content: string
createdAt: string
}
aiReply: {
id: string
content: string
createdAt: string
}
}
CHAT-4: SSE 流式端点(需求 5.8.4)
用于 AI 流式回复,前端通过 SSE 接收逐 token 输出
POST /api/xcx/chat/stream
Content-Type: application/json
Body: { chatId: string, content: string }
Response: text/event-stream
SSE 事件格式
event: message
data: {"token": "根据"}
event: message
data: {"token": "数据分析"}
event: done
data: {"messageId": "msg-xxx", "createdAt": "2026-03-05T14:30:00+08:00"}
event: message— 逐 token 输出,data.token为文本片段event: done— 流结束,data.messageId为完整消息 IDevent: error— 错误,data.message为错误描述
注意:SSE 端点的响应不经过 ResponseWrapperMiddleware 包装(
text/event-stream自动跳过)
字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
CHAT-1.items[].title |
string | 对话标题,新增字段 |
CHAT-2.items[].createdAt |
string | 统一时间字段名,替代 created_at 和 timestamp |
CHAT-2.items[].referenceCard |
object? | 引用卡片,从其他页面跳转时附带的上下文信息 |
CHAT-2 customerId 参数 |
query | 支持通过客户 ID 查找/创建对话 |
CHAT-4 |
SSE | 流式端点,text/event-stream 格式,不经过响应包装 |
九、配置模块
CONFIG-1: 技能类型列表(REQ-1)
GET /api/xcx/config/skill-types
Response: {
skills: Array<{ value: string, text: string, icon?: string }>
}
附:接口与页面映射
| 页面 | 依赖接口 | 优先级 |
|---|---|---|
| login | AUTH-1, AUTH-2 | ✅ 已实现 |
| app.ts | AUTH-3, AUTH-4 | ✅ 已实现 |
| apply | AUTH-5 | ✅ 已实现 |
| reviewing | AUTH-3 | ✅ 已实现 |
| no-permission | AUTH-3 | ✅ 已实现 |
| task-list | TASK-1 | P0 |
| task-detail | TASK-2, TASK-3, TASK-4, NOTE-2, NOTE-3 | P0 |
| notes | NOTE-1, NOTE-3 | P1 |
| performance | PERF-1 | P1 |
| performance-records | PERF-2 | P1 |
| customer-detail | CUST-1 | ✅ 后端已实现 |
| customer-service-records | CUST-2 | ✅ 后端已实现 |
| coach-detail | COACH-1 | ✅ 后端已实现 |
| board-coach | BOARD-1, CONFIG-1 | P2 |
| board-customer | BOARD-2 | P2 |
| board-finance | BOARD-3 | P2 |
| chat-history | CHAT-1 | P2 |
| chat | CHAT-2, CHAT-3 | P2 |
| my-profile | AUTH-3 | ✅ 已实现(读 globalData) |