feat: batch update - gift card breakdown spec, backend APIs, miniprogram pages, ETL finance recharge, docs & migrations

This commit is contained in:
Neo
2026-03-20 01:43:48 +08:00
parent 075caf067f
commit 79f9a0e1da
437 changed files with 118603 additions and 976 deletions

View File

@@ -0,0 +1,433 @@
"""三看板接口 Pydantic SchemaBOARD-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 响应 SchemaTask 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 响应 SchemaTask 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 响应 SchemaTask 2.4
# ---------------------------------------------------------------------------
class OverviewPanel(CamelModel):
"""经营一览8 项核心指标 + 各 3 个环比字段Optionalcompare=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