Files
Neo-ZQYY/apps/backend/app/schemas/xcx_board.py
Neo 6f8f12314f feat: 累积功能变更 — 聊天集成、租户管理、小程序更新、ETL 增强、迁移脚本
包含多个会话的累积代码变更:
- backend: AI 聊天服务、触发器调度、认证增强、WebSocket、调度器最小间隔
- admin-web: ETL 状态页、任务管理、调度配置、登录优化
- miniprogram: 看板页面、聊天集成、UI 组件、导航更新
- etl: DWS 新任务(finance_area_daily/board_cache)、连接器增强
- tenant-admin: 项目初始化
- db: 19 个迁移脚本(etl_feiqiu 11 + zqyy_app 8)
- packages/shared: 枚举和工具函数更新
- tools: 数据库工具、报表生成、健康检查
- docs: PRD/架构/部署/合约文档更新

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 00:03:48 +08:00

477 lines
13 KiB
Python
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.
# AI_CHANGELOG
# - 2026-03-20 | Prompt: R3 项目类型筛选接口重建 | SkillFilterEnum 和 ProjectFilterEnum
# 枚举值从 all/chinese/snooker/mahjong/karaoke 改为 ALL/BILLIARD/SNOOKER/MAHJONG/KTV
# 与 dws.cfg_area_category.category_code 一致,消除前后端映射层。
# - 2026-03-27 | Prompt: board-finance-integration T2.4 | AreaFilterEnum 从 7 项重建为 9 项
# (新增 vip/snooker/ktv移除 teamBuilding与区域筛选对照表一致。
"""三看板接口 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 技能筛选(值与 dws.cfg_area_category.category_code 一致)。"""
# CHANGE 2026-03-20 | R3 修复:枚举值从 chinese/snooker 等前端自定义值
# 改为数据库 category_codeBILLIARD/SNOOKER/MAHJONG/KTV消除映射层。
ALL = "ALL"
BILLIARD = "BILLIARD"
SNOOKER = "SNOOKER"
MAHJONG = "MAHJONG"
KTV = "KTV"
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 项目筛选(值与 dws.cfg_area_category.category_code 一致)。"""
# CHANGE 2026-03-20 | R3 修复:枚举值从 chinese/snooker 等前端自定义值
# 改为数据库 category_codeBILLIARD/SNOOKER/MAHJONG/KTV消除映射层。
ALL = "ALL"
BILLIARD = "BILLIARD"
SNOOKER = "SNOOKER"
MAHJONG = "MAHJONG"
KTV = "KTV"
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 区域筛选。"""
# CHANGE 2026-03-27 | board-finance-integration T2.4 | 枚举从 7 项重建为 9 项,
# 与区域筛选对照表一致all/hall/hallA-C/vip/snooker/mahjong/ktv
all = "all"
hall = "hall"
hallA = "hallA"
hallB = "hallB"
hallC = "hallC"
vip = "vip"
snooker = "snooker"
mahjong = "mahjong"
ktv = "ktv"
# ---------------------------------------------------------------------------
# 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]
total: int
page: int
page_size: int
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 | None = None
cash_out: float | None = None
cash_balance: float | None = None
balance_rate: float | None = None
# 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
# 全类别会员卡余额合计环比
all_card_balance_compare: str | None = None
all_card_balance_down: bool | None = None
all_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
desc: str | None = None
amount: float
compare: str | None = None
class ChannelItem(CamelModel):
label: str
desc: str | None = None
amount: float
compare: str | None = None
class RevenuePanel(CamelModel):
structure_rows: list[RevenueStructureRow]
price_items: list[RevenueItem]
total_occurrence: float
total_occurrence_compare: str | None = None
total_occurrence_down: bool | None = None
total_occurrence_flat: bool | None = None
discount_items: list[RevenueItem]
# CHANGE 2026-03-28 | board-finance-phase2 bugfix | 优惠总计供前端展示
discount_total: float = 0.0
discount_total_compare: str | None = None
discount_total_down: bool | None = None
discount_total_flat: bool | None = None
confirmed_total: float
confirmed_total_compare: str | None = None
confirmed_total_down: bool | None = None
confirmed_total_flat: bool | None = None
channel_items: list[ChannelItem]
class CashflowItem(CamelModel):
label: str
desc: str | None = None
amount: float
compare: str | None = None
down: bool | None = None
class CashflowPanel(CamelModel):
consume_items: list[CashflowItem] # 3 项
recharge_items: list[CashflowItem] # 1 项
total: float
total_compare: str | None = None
total_down: bool | None = None
total_flat: bool | None = None
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 | None # area≠all 时为 null
expense: ExpensePanel | None # area≠all 时为 null
coach_analysis: CoachAnalysisPanel