Files
Neo-ZQYY/apps/backend/app/schemas/tenant_excel.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

135 lines
5.6 KiB
Python
Raw Permalink 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.
# -*- 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="确认时间")