Files
Neo-ZQYY/docs/miniprogram-dev/API-contract.md

2023 lines
80 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 小程序接口契约文档
> 创建时间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
```
#### 响应结构
```typescript
interface TaskListResponse {
items: TaskItem[]
total: number
page: number
pageSize: number
// 绩效概览(附带在任务列表响应中,避免额外请求)
performance: PerformanceSummary
}
```
#### 子类型定义
```typescript
/** 任务项 */
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}
```
#### 响应结构
```typescript
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 // 服务次数
}>
}
```
#### 子类型定义
```typescript
/** 业绩明细项 */
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_id` JOIN `dim_member` 获取(规则 DQ-6禁止直接使用 `settlement_head.member_phone`;会员卡信息通过 `member_id` JOIN `dim_member_card_account` 获取(规则 DQ-7
```
GET /api/xcx/customers/{customerId}
```
#### 请求参数
| 参数 | 类型 | 必填 | 说明 |
|------|------|:----:|------|
| `customerId` | string (path) | 是 | 客户唯一 ID |
#### 响应结构TypeScript 类型定义)
```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_cachecache_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_moneyDWD-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` |
| 会员信息 JOINDWD-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 |
| 会员卡 JOINDWD-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 类型定义)
```typescript
/** 顶层响应 */
interface BoardCoachesResponse {
items: CoachBoardItem[]
}
/** 助教卡片 — 基础字段 + 4 维度专属字段 */
interface CoachBoardItem {
// ─── 基础字段(所有维度共享) ───
/** 助教 ID */
id: string
/** 助教姓名 */
name: string
/** 姓名首字(用于头像占位符) */
initial: string
/** 头像渐变色标识blue/green/pink/amber/violet/cyan 等) */
avatarGradient: string
/** 等级英文 keystar / 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_id` JOIN `dim_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 类型定义)
```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` JOINDQ-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-10heart-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` |
| 会员信息 JOINDWD-DOC 规则 DQ-6 | 客户姓名通过 `member_id` JOIN `dim_member``nickname``scd2_is_current=1`),禁止直接使用 `member_phone`/`member_name` |
| 会员卡余额 JOINDWD-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_calc`
> DWD-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 类型定义)
```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=trueA区 / 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_id` JOIN `dim_member` 获取DWD-DOC 强制规则 DQ-6禁止直接使用 `member_phone`
```
GET /api/xcx/coaches/{coachId}
```
#### 响应结构
```typescript
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[]
}
```
#### 子类型定义
```typescript
/** 收入分项 */
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 // 关系 emojiP6 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
```
#### 响应结构
```typescript
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
```
#### 响应结构
```typescript
interface ChatMessagesResponse {
chatId: string // 对话 IDcustomerId 入口时由后端返回)
items: Array<{
id: string
role: 'user' | 'assistant'
content: string
createdAt: string // 统一时间字段名(需求 5.8.1ISO 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 }
```
#### 响应结构
```typescript
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` 为完整消息 ID
- `event: 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 |