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>
This commit is contained in:
134
apps/backend/app/schemas/tenant_excel.py
Normal file
134
apps/backend/app/schemas/tenant_excel.py
Normal file
@@ -0,0 +1,134 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
租户管理后台 — Excel 上传 Pydantic Schema。
|
||||
|
||||
覆盖:4 种模板行数据模型、校验结果、冲突 diff、确认请求、上传记录。
|
||||
|
||||
需求: 5.2, 7.2, 8.4
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from app.schemas.base import CamelModel
|
||||
|
||||
|
||||
# ── 4 种模板行数据模型 ────────────────────────────────────
|
||||
|
||||
|
||||
class ExpenseRow(CamelModel):
|
||||
"""财务支出行数据。"""
|
||||
row_index: int = Field(..., description="行号(从 1 开始)")
|
||||
expense_month: str = Field(..., description="月份 YYYY-MM")
|
||||
category: str = Field(..., description="支出类别(8 值枚举)")
|
||||
amount: float = Field(..., description="金额(> 0,精度 2 位小数)")
|
||||
remark: str | None = Field(None, description="备注(可选,最长 500 字符)")
|
||||
|
||||
|
||||
class PlatformIncomeRow(CamelModel):
|
||||
"""团购收入行数据。"""
|
||||
row_index: int = Field(..., description="行号")
|
||||
income_month: str = Field(..., description="月份 YYYY-MM")
|
||||
platform_name: str = Field(..., description="平台名称")
|
||||
amount: float = Field(..., description="收入金额(> 0)")
|
||||
remark: str | None = Field(None, description="备注(可选,最长 500 字符)")
|
||||
|
||||
|
||||
class SalaryAdjRow(CamelModel):
|
||||
"""助教奖罚行数据。"""
|
||||
row_index: int = Field(..., description="行号")
|
||||
salary_month: str = Field(..., description="月份 YYYY-MM")
|
||||
assistant_name: str = Field(..., description="助教姓名")
|
||||
assistant_number: str = Field(..., description="助教编号")
|
||||
adjustment_type: str = Field(..., description="类型(扣款/奖金)")
|
||||
amount: float = Field(..., description="金额(> 0)")
|
||||
reason: str = Field(..., description="原因(非空,最长 200 字符)")
|
||||
assistant_id: int | None = Field(None, description="匹配到的助教 ID")
|
||||
|
||||
|
||||
class RechargeCommissionRow(CamelModel):
|
||||
"""充值业绩归属行数据。"""
|
||||
row_index: int = Field(..., description="行号")
|
||||
recharge_date: str = Field(..., description="充值日期 YYYY-MM-DD")
|
||||
member_name: str = Field(..., description="会员名称")
|
||||
recharge_amount: float = Field(..., description="充值金额(> 0)")
|
||||
assigned_assistant: str = Field(..., description="归属助教")
|
||||
reward_amount: float = Field(..., description="奖励金额(≥ 0)")
|
||||
assistant_id: int | None = Field(None, description="匹配到的助教 ID")
|
||||
|
||||
|
||||
# ── 校验错误/警告 ─────────────────────────────────────────
|
||||
|
||||
|
||||
class ValidationError(CamelModel):
|
||||
"""单行校验错误。"""
|
||||
row_index: int = Field(..., description="行号")
|
||||
column: str = Field(..., description="列名")
|
||||
message: str = Field(..., description="错误描述")
|
||||
|
||||
|
||||
class ValidationWarning(CamelModel):
|
||||
"""单行校验警告(如人员匹配失败)。"""
|
||||
row_index: int = Field(..., description="行号")
|
||||
column: str = Field(..., description="列名")
|
||||
message: str = Field(..., description="警告描述")
|
||||
|
||||
|
||||
class ValidationResult(CamelModel):
|
||||
"""校验结果。"""
|
||||
errors: list[ValidationError] = Field(default_factory=list, description="错误列表")
|
||||
warnings: list[ValidationWarning] = Field(default_factory=list, description="警告列表")
|
||||
passed_rows: list[dict] = Field(default_factory=list, description="通过校验的行数据")
|
||||
upload_id: int | None = Field(None, description="上传批次 ID(校验全部通过时创建)")
|
||||
|
||||
|
||||
# ── 冲突 diff ─────────────────────────────────────────────
|
||||
|
||||
|
||||
class FieldDiff(CamelModel):
|
||||
"""单字段差异。"""
|
||||
field: str = Field(..., description="字段名")
|
||||
old_value: str | None = Field(None, description="旧值")
|
||||
new_value: str | None = Field(None, description="新值")
|
||||
|
||||
|
||||
class ConflictDiff(CamelModel):
|
||||
"""冲突行 diff。"""
|
||||
row_index: int = Field(..., description="行号")
|
||||
field_diffs: list[FieldDiff] = Field(default_factory=list, description="逐字段差异")
|
||||
|
||||
|
||||
# ── 确认请求 ──────────────────────────────────────────────
|
||||
|
||||
|
||||
class Resolution(CamelModel):
|
||||
"""单行冲突解决方案。"""
|
||||
row_index: int = Field(..., description="行号")
|
||||
action: Literal["replace", "keep"] = Field(..., description="操作:replace=替换/keep=保留")
|
||||
|
||||
|
||||
class ConfirmRequest(CamelModel):
|
||||
"""确认写入请求。"""
|
||||
upload_id: int = Field(..., description="上传批次 ID")
|
||||
resolutions: list[Resolution] = Field(default_factory=list, description="冲突解决方案列表")
|
||||
|
||||
|
||||
# ── 上传记录 ──────────────────────────────────────────────
|
||||
|
||||
|
||||
class UploadLogItem(CamelModel):
|
||||
"""上传记录列表项。"""
|
||||
id: int
|
||||
site_id: int
|
||||
upload_type: str = Field(..., description="模板类型")
|
||||
file_name: str = Field(..., description="原始文件名")
|
||||
uploaded_by: int = Field(..., description="上传人 ID")
|
||||
row_count: int = Field(0, description="数据行数")
|
||||
conflict_count: int = Field(0, description="冲突行数")
|
||||
resolved_count: int = Field(0, description="已解决冲突数")
|
||||
status: str = Field(..., description="状态:pending/confirmed/failed")
|
||||
created_at: str | None = Field(None, description="上传时间")
|
||||
confirmed_at: str | None = Field(None, description="确认时间")
|
||||
Reference in New Issue
Block a user