feat: batch update - gift card breakdown spec, backend APIs, miniprogram pages, ETL finance recharge, docs & migrations
This commit is contained in:
433
apps/backend/app/schemas/xcx_board.py
Normal file
433
apps/backend/app/schemas/xcx_board.py
Normal file
@@ -0,0 +1,433 @@
|
||||
"""三看板接口 Pydantic Schema(BOARD-1/2/3 请求参数枚举 + 响应模型)。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from enum import Enum
|
||||
|
||||
from app.schemas.base import CamelModel
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 请求参数枚举(Task 2.1)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class CoachSortEnum(str, Enum):
|
||||
"""BOARD-1 排序维度。"""
|
||||
perf_desc = "perf_desc"
|
||||
perf_asc = "perf_asc"
|
||||
salary_desc = "salary_desc"
|
||||
salary_asc = "salary_asc"
|
||||
sv_desc = "sv_desc"
|
||||
task_desc = "task_desc"
|
||||
|
||||
|
||||
class SkillFilterEnum(str, Enum):
|
||||
"""BOARD-1 技能筛选。"""
|
||||
all = "all"
|
||||
chinese = "chinese"
|
||||
snooker = "snooker"
|
||||
mahjong = "mahjong"
|
||||
karaoke = "karaoke"
|
||||
|
||||
|
||||
class BoardTimeEnum(str, Enum):
|
||||
"""BOARD-1 时间范围。"""
|
||||
month = "month"
|
||||
quarter = "quarter"
|
||||
last_month = "last_month"
|
||||
last_3m = "last_3m"
|
||||
last_quarter = "last_quarter"
|
||||
last_6m = "last_6m"
|
||||
|
||||
|
||||
class CustomerDimensionEnum(str, Enum):
|
||||
"""BOARD-2 客户维度。"""
|
||||
recall = "recall"
|
||||
potential = "potential"
|
||||
balance = "balance"
|
||||
recharge = "recharge"
|
||||
recent = "recent"
|
||||
spend60 = "spend60"
|
||||
freq60 = "freq60"
|
||||
loyal = "loyal"
|
||||
|
||||
|
||||
class ProjectFilterEnum(str, Enum):
|
||||
"""BOARD-2 项目筛选。"""
|
||||
all = "all"
|
||||
chinese = "chinese"
|
||||
snooker = "snooker"
|
||||
mahjong = "mahjong"
|
||||
karaoke = "karaoke"
|
||||
|
||||
|
||||
class FinanceTimeEnum(str, Enum):
|
||||
"""BOARD-3 时间范围。"""
|
||||
month = "month"
|
||||
lastMonth = "lastMonth"
|
||||
week = "week"
|
||||
lastWeek = "lastWeek"
|
||||
quarter3 = "quarter3"
|
||||
quarter = "quarter"
|
||||
lastQuarter = "lastQuarter"
|
||||
half6 = "half6"
|
||||
|
||||
|
||||
class AreaFilterEnum(str, Enum):
|
||||
"""BOARD-3 区域筛选。"""
|
||||
all = "all"
|
||||
hall = "hall"
|
||||
hallA = "hallA"
|
||||
hallB = "hallB"
|
||||
hallC = "hallC"
|
||||
mahjong = "mahjong"
|
||||
teamBuilding = "teamBuilding"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# BOARD-1 响应 Schema(Task 2.2)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class CoachSkillItem(CamelModel):
|
||||
text: str
|
||||
cls: str
|
||||
|
||||
|
||||
class CoachBoardItem(CamelModel):
|
||||
"""助教看板单条记录(扁平结构,包含所有维度字段)。"""
|
||||
|
||||
# 基础字段(所有维度共享)
|
||||
id: int
|
||||
name: str
|
||||
initial: str
|
||||
avatar_gradient: str
|
||||
level: str # star/senior/middle/junior
|
||||
skills: list[CoachSkillItem]
|
||||
top_customers: list[str] # ["💖 王先生", "💛 李女士"]
|
||||
|
||||
# perf 维度
|
||||
perf_hours: float = 0.0
|
||||
perf_hours_before: float | None = None
|
||||
perf_gap: str | None = None # "距升档 13.8h" 或 None
|
||||
perf_reached: bool = False
|
||||
|
||||
# salary 维度
|
||||
salary: float = 0.0
|
||||
salary_perf_hours: float = 0.0
|
||||
salary_perf_before: float | None = None
|
||||
|
||||
# sv 维度
|
||||
sv_amount: float = 0.0
|
||||
sv_customer_count: int = 0
|
||||
sv_consume: float = 0.0
|
||||
|
||||
# task 维度
|
||||
task_recall: int = 0
|
||||
task_callback: int = 0
|
||||
|
||||
|
||||
class CoachBoardResponse(CamelModel):
|
||||
items: list[CoachBoardItem]
|
||||
dim_type: str # perf/salary/sv/task
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# BOARD-2 响应 Schema(Task 2.3)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class CustomerAssistant(CamelModel):
|
||||
name: str
|
||||
cls: str
|
||||
heart_score: float
|
||||
badge: str | None = None
|
||||
badge_cls: str | None = None
|
||||
|
||||
|
||||
class CustomerBoardItemBase(CamelModel):
|
||||
"""客户看板基础字段(所有维度共享)。"""
|
||||
id: int
|
||||
name: str
|
||||
initial: str
|
||||
avatar_cls: str
|
||||
assistants: list[CustomerAssistant]
|
||||
|
||||
|
||||
class RecallItem(CustomerBoardItemBase):
|
||||
ideal_days: int
|
||||
elapsed_days: int
|
||||
overdue_days: int
|
||||
visits_30d: int
|
||||
balance: str
|
||||
recall_index: float
|
||||
|
||||
|
||||
class PotentialTag(CamelModel):
|
||||
text: str
|
||||
theme: str
|
||||
|
||||
|
||||
class PotentialItem(CustomerBoardItemBase):
|
||||
potential_tags: list[PotentialTag]
|
||||
spend_30d: float
|
||||
avg_visits: float
|
||||
avg_spend: float
|
||||
|
||||
|
||||
class BalanceItem(CustomerBoardItemBase):
|
||||
balance: str
|
||||
last_visit: str # "3天前"
|
||||
monthly_consume: float
|
||||
available_months: str # "约0.8个月"
|
||||
|
||||
|
||||
class RechargeItem(CustomerBoardItemBase):
|
||||
last_recharge: str
|
||||
recharge_amount: float
|
||||
recharges_60d: int
|
||||
current_balance: str
|
||||
|
||||
|
||||
class RecentItem(CustomerBoardItemBase):
|
||||
days_ago: int
|
||||
visit_freq: str # "6.2次/月"
|
||||
ideal_days: int
|
||||
visits_30d: int
|
||||
avg_spend: float
|
||||
|
||||
|
||||
class Spend60Item(CustomerBoardItemBase):
|
||||
spend_60d: float
|
||||
visits_60d: int
|
||||
high_spend_tag: bool
|
||||
avg_spend: float
|
||||
|
||||
|
||||
class WeeklyVisit(CamelModel):
|
||||
val: int
|
||||
pct: int # 0-100
|
||||
|
||||
|
||||
class Freq60Item(CustomerBoardItemBase):
|
||||
visits_60d: int
|
||||
avg_interval: str # "5.0天"
|
||||
weekly_visits: list[WeeklyVisit] # 固定长度 8
|
||||
spend_60d: float
|
||||
|
||||
|
||||
class CoachDetail(CamelModel):
|
||||
name: str
|
||||
cls: str
|
||||
heart_score: float
|
||||
badge: str | None = None
|
||||
avg_duration: str
|
||||
service_count: int
|
||||
coach_spend: float
|
||||
relation_idx: float
|
||||
|
||||
|
||||
class LoyalItem(CustomerBoardItemBase):
|
||||
intimacy: float
|
||||
top_coach_name: str
|
||||
top_coach_heart: float
|
||||
top_coach_score: float
|
||||
coach_name: str
|
||||
coach_ratio: str # "78%"
|
||||
coach_details: list[CoachDetail]
|
||||
|
||||
|
||||
class CustomerBoardResponse(CamelModel):
|
||||
items: list[dict] # 实际类型取决于 dimension
|
||||
total: int
|
||||
page: int
|
||||
page_size: int
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# BOARD-3 响应 Schema(Task 2.4)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class OverviewPanel(CamelModel):
|
||||
"""经营一览:8 项核心指标 + 各 3 个环比字段(Optional,compare=0 时为 None)。"""
|
||||
occurrence: float
|
||||
discount: float # 负值
|
||||
discount_rate: float
|
||||
confirmed_revenue: float
|
||||
cash_in: float
|
||||
cash_out: float
|
||||
cash_balance: float
|
||||
balance_rate: float
|
||||
# occurrence 环比
|
||||
occurrence_compare: str | None = None
|
||||
occurrence_down: bool | None = None
|
||||
occurrence_flat: bool | None = None
|
||||
# discount 环比
|
||||
discount_compare: str | None = None
|
||||
discount_down: bool | None = None
|
||||
discount_flat: bool | None = None
|
||||
# discount_rate 环比
|
||||
discount_rate_compare: str | None = None
|
||||
discount_rate_down: bool | None = None
|
||||
discount_rate_flat: bool | None = None
|
||||
# confirmed_revenue 环比
|
||||
confirmed_revenue_compare: str | None = None
|
||||
confirmed_revenue_down: bool | None = None
|
||||
confirmed_revenue_flat: bool | None = None
|
||||
# cash_in 环比
|
||||
cash_in_compare: str | None = None
|
||||
cash_in_down: bool | None = None
|
||||
cash_in_flat: bool | None = None
|
||||
# cash_out 环比
|
||||
cash_out_compare: str | None = None
|
||||
cash_out_down: bool | None = None
|
||||
cash_out_flat: bool | None = None
|
||||
# cash_balance 环比
|
||||
cash_balance_compare: str | None = None
|
||||
cash_balance_down: bool | None = None
|
||||
cash_balance_flat: bool | None = None
|
||||
# balance_rate 环比
|
||||
balance_rate_compare: str | None = None
|
||||
balance_rate_down: bool | None = None
|
||||
balance_rate_flat: bool | None = None
|
||||
|
||||
|
||||
class GiftCell(CamelModel):
|
||||
value: float
|
||||
compare: str | None = None
|
||||
down: bool | None = None
|
||||
flat: bool | None = None
|
||||
|
||||
|
||||
class GiftRow(CamelModel):
|
||||
"""赠送卡矩阵一行:合计 / 酒水卡 / 台费卡 / 抵用券。"""
|
||||
label: str # "新增" / "消费" / "余额"
|
||||
total: GiftCell
|
||||
liquor: GiftCell
|
||||
table_fee: GiftCell
|
||||
voucher: GiftCell
|
||||
|
||||
|
||||
class RechargePanel(CamelModel):
|
||||
"""预收资产板块:储值卡 5 指标 + 赠送卡 3×4 矩阵 + 全卡余额。"""
|
||||
actual_income: float
|
||||
first_charge: float
|
||||
renew_charge: float
|
||||
consumed: float
|
||||
card_balance: float
|
||||
gift_rows: list[GiftRow] # 3 行
|
||||
all_card_balance: float
|
||||
# 储值卡各项环比字段
|
||||
actual_income_compare: str | None = None
|
||||
actual_income_down: bool | None = None
|
||||
actual_income_flat: bool | None = None
|
||||
first_charge_compare: str | None = None
|
||||
first_charge_down: bool | None = None
|
||||
first_charge_flat: bool | None = None
|
||||
renew_charge_compare: str | None = None
|
||||
renew_charge_down: bool | None = None
|
||||
renew_charge_flat: bool | None = None
|
||||
consumed_compare: str | None = None
|
||||
consumed_down: bool | None = None
|
||||
consumed_flat: bool | None = None
|
||||
card_balance_compare: str | None = None
|
||||
card_balance_down: bool | None = None
|
||||
card_balance_flat: bool | None = None
|
||||
|
||||
|
||||
class RevenueStructureRow(CamelModel):
|
||||
id: str
|
||||
name: str
|
||||
desc: str | None = None
|
||||
is_sub: bool = False
|
||||
amount: float
|
||||
discount: float
|
||||
booked: float
|
||||
booked_compare: str | None = None
|
||||
|
||||
|
||||
class RevenueItem(CamelModel):
|
||||
label: str
|
||||
amount: float
|
||||
|
||||
|
||||
class ChannelItem(CamelModel):
|
||||
label: str
|
||||
amount: float
|
||||
|
||||
|
||||
class RevenuePanel(CamelModel):
|
||||
structure_rows: list[RevenueStructureRow]
|
||||
price_items: list[RevenueItem] # 4 项
|
||||
total_occurrence: float
|
||||
discount_items: list[RevenueItem] # 4 项
|
||||
confirmed_total: float
|
||||
channel_items: list[ChannelItem] # 3 项
|
||||
|
||||
|
||||
class CashflowItem(CamelModel):
|
||||
label: str
|
||||
amount: float
|
||||
|
||||
|
||||
class CashflowPanel(CamelModel):
|
||||
consume_items: list[CashflowItem] # 3 项
|
||||
recharge_items: list[CashflowItem] # 1 项
|
||||
total: float
|
||||
|
||||
|
||||
class ExpenseItem(CamelModel):
|
||||
label: str
|
||||
amount: float
|
||||
compare: str | None = None
|
||||
down: bool | None = None
|
||||
flat: bool | None = None
|
||||
|
||||
|
||||
class ExpensePanel(CamelModel):
|
||||
operation_items: list[ExpenseItem] # 3 项
|
||||
fixed_items: list[ExpenseItem] # 4 项
|
||||
coach_items: list[ExpenseItem] # 4 项
|
||||
platform_items: list[ExpenseItem] # 3 项
|
||||
total: float
|
||||
total_compare: str | None = None
|
||||
total_down: bool | None = None
|
||||
total_flat: bool | None = None
|
||||
|
||||
|
||||
class CoachAnalysisRow(CamelModel):
|
||||
level: str
|
||||
pay: float
|
||||
share: float
|
||||
hourly: float
|
||||
pay_compare: str | None = None
|
||||
pay_down: bool | None = None
|
||||
share_compare: str | None = None
|
||||
share_down: bool | None = None
|
||||
hourly_compare: str | None = None
|
||||
hourly_flat: bool | None = None
|
||||
|
||||
|
||||
class CoachAnalysisTable(CamelModel):
|
||||
total_pay: float
|
||||
total_share: float
|
||||
avg_hourly: float
|
||||
total_pay_compare: str | None = None
|
||||
total_pay_down: bool | None = None
|
||||
total_share_compare: str | None = None
|
||||
total_share_down: bool | None = None
|
||||
avg_hourly_compare: str | None = None
|
||||
avg_hourly_flat: bool | None = None
|
||||
rows: list[CoachAnalysisRow] # 4 行:初级/中级/高级/星级
|
||||
|
||||
|
||||
class CoachAnalysisPanel(CamelModel):
|
||||
basic: CoachAnalysisTable # 基础课/陪打
|
||||
incentive: CoachAnalysisTable # 激励课/超休
|
||||
|
||||
|
||||
class FinanceBoardResponse(CamelModel):
|
||||
overview: OverviewPanel
|
||||
recharge: RechargePanel | None # area≠all 时为 null
|
||||
revenue: RevenuePanel
|
||||
cashflow: CashflowPanel
|
||||
expense: ExpensePanel
|
||||
coach_analysis: CoachAnalysisPanel
|
||||
Reference in New Issue
Block a user