Files
s2f/backend/app/models/exception_item.py
T
freedakgmail 33b4c734aa feat: 完善前后端核心功能模块
后端:
- 新增认证(auth)、任务(tasks)、映射(mappings)、对账(reconciliation)、异常(exceptions)、导出(exports) API
- 新增核心模块: database, security, permissions, tenant, exceptions, error_handlers
- 新增数据模型: user, company, reconciliation_task, field_mapping, uploaded_file 等
- 新增服务层: ai_recognizer, file_parser, file_storage, mapping, reconciliation 等
- 添加数据库迁移脚本

前端:
- 新增登录页面和仪表盘页面
- 新增任务列表、任务详情、字段映射页面
- 新增异常处理页面和规则设置页面
- 新增 API 代理路由 /api/[...path]
- 新增 UI 组件库 (button, card, dialog, input, table 等)
- 新增 auth 组件 (ProtectedRoute, PermissionGate)
- 新增 layout 组件 (Header, Sidebar)
- 新增 mapping 组件 (FieldMappingTable, AISuggestionPanel)
- 新增 API 客户端和 hooks (useAsync, useToast, usePermission 等)
- 新增状态管理 (auth-store, company-store, ui-store)
- 集成 Tailwind CSS 和 shadcn/ui 组件库

其他:
- 添加 Alembic 数据库迁移配置
- 添加初始化示例数据脚本
- 更新项目文档
2026-07-07 09:04:47 +08:00

98 lines
4.1 KiB
Python

"""
异常项模型
存储对账过程中检测到的异常
"""
from datetime import datetime
from enum import Enum
from typing import Optional
from sqlalchemy import ForeignKey, Integer, String, Text, DateTime, JSON, Float
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.models.base import BaseModel
class ExceptionType(str, Enum):
"""异常类型"""
# 第一阶段:工资表内部异常
MISSING_EMPLOYEE = "MISSING_EMPLOYEE" # 员工缺失(三个表之一缺失该员工)
AMOUNT_MISMATCH = "AMOUNT_MISMATCH" # 金额不匹配
ZERO_AMOUNT = "ZERO_AMOUNT" # 金额为零
NEGATIVE_AMOUNT = "NEGATIVE_AMOUNT" # 负数金额
UNUSUAL_AMOUNT = "UNUSUAL_AMOUNT" # 金额异常(过大或过小)
DUPLICATE_EMPLOYEE = "DUPLICATE_EMPLOYEE" # 重复员工
# 第二阶段:银行实发对账(预留)
BANK_MISMATCH = "BANK_MISMATCH" # 银行实发与工资表不匹配
BANK_MISSING = "BANK_MISSING" # 银行记录缺失
BANK_EXTRA = "BANK_EXTRA" # 银行多余记录
class ExceptionStatus(str, Enum):
"""异常状态"""
PENDING = "PENDING" # 待处理
CONFIRMED = "CONFIRMED" # 已确认
IGNORED = "IGNORED" # 已忽略
RESOLVED = "RESOLVED" # 已解决
class ExceptionSeverity(str, Enum):
"""异常严重程度"""
LOW = "LOW" # 低
MEDIUM = "MEDIUM" # 中
HIGH = "HIGH" # 高
CRITICAL = "CRITICAL" # 严重
class ExceptionItem(BaseModel):
"""
异常项模型
记录对账过程中检测到的各类异常
"""
__tablename__ = "exception_items"
company_id: Mapped[int] = mapped_column(Integer, ForeignKey("companies.id"), nullable=False, index=True)
task_id: Mapped[int] = mapped_column(Integer, ForeignKey("reconciliation_tasks.id"), nullable=False, index=True)
# 异常基本信息
exception_type: Mapped[str] = mapped_column(String(50), nullable=False, index=True, comment="异常类型")
severity: Mapped[str] = mapped_column(String(20), nullable=False, default=ExceptionSeverity.MEDIUM.value, comment="严重程度")
status: Mapped[str] = mapped_column(String(20), nullable=False, default=ExceptionStatus.PENDING.value, index=True, comment="状态")
# 关联的员工信息
employee_id: Mapped[Optional[str]] = mapped_column(String(100), nullable=True, index=True, comment="员工ID")
employee_name: Mapped[Optional[str]] = mapped_column(String(100), nullable=True, comment="员工姓名")
# 异常详情
description: Mapped[str] = mapped_column(Text, nullable=False, comment="异常描述")
detail: Mapped[Optional[dict]] = mapped_column(JSON, nullable=True, comment="详细信息")
# 涉及的金额
salary_amount: Mapped[Optional[float]] = mapped_column(Float, nullable=True, comment="工资表金额")
social_security_amount: Mapped[Optional[float]] = mapped_column(Float, nullable=True, comment="社保表金额")
tax_amount: Mapped[Optional[float]] = mapped_column(Float, nullable=True, comment="个税表金额")
bank_amount: Mapped[Optional[float]] = mapped_column(Float, nullable=True, comment="银行实发金额")
# 差异金额
difference_amount: Mapped[Optional[float]] = mapped_column(Float, nullable=True, comment="差异金额")
# 建议处理方式
suggested_action: Mapped[Optional[str]] = mapped_column(String(200), nullable=True, comment="建议处理方式")
ai_suggestion: Mapped[Optional[str]] = mapped_column(Text, nullable=True, comment="AI 分析建议")
# 处理信息
handled_by: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("users.id"), nullable=True)
handled_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
handling_note: Mapped[Optional[str]] = mapped_column(Text, nullable=True, comment="处理备注")
# 关系
company = relationship("Company", backref="exceptions")
task = relationship("ReconciliationTask", backref="exceptions")
handler = relationship("User", foreign_keys=[handled_by])
def __repr__(self) -> str:
return f"<ExceptionItem(id={self.id}, type='{self.exception_type}', status='{self.status}')>"