主要更新: - 更新代理商端文档,明确项目由品牌方分配流程 - 新增Brief配置详情页(已配置)设计稿 - 完善工作台紧急待办中品牌新任务功能 - 整理Pencil设计文件中代理商端页面顺序 - 新增后端FastAPI框架及核心API - 新增前端Next.js页面和组件库 - 添加.gitignore排除构建和缓存文件 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
105 lines
3.2 KiB
Python
105 lines
3.2 KiB
Python
"""
|
|
特例审批模型
|
|
"""
|
|
from typing import TYPE_CHECKING, Optional
|
|
from datetime import datetime
|
|
from sqlalchemy import String, Text, Boolean, ForeignKey, DateTime, Enum as SQLEnum
|
|
from app.models.types import JSONType
|
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
import enum
|
|
|
|
from app.models.base import Base, TimestampMixin
|
|
|
|
if TYPE_CHECKING:
|
|
from app.models.tenant import Tenant
|
|
|
|
|
|
class RiskTargetType(str, enum.Enum):
|
|
"""特例目标类型"""
|
|
INFLUENCER = "influencer"
|
|
ORDER = "order"
|
|
CONTENT = "content"
|
|
|
|
|
|
class RiskExceptionStatus(str, enum.Enum):
|
|
"""特例审批状态"""
|
|
PENDING = "pending"
|
|
APPROVED = "approved"
|
|
REJECTED = "rejected"
|
|
EXPIRED = "expired"
|
|
REVOKED = "revoked"
|
|
|
|
|
|
class RiskException(Base, TimestampMixin):
|
|
"""特例审批表"""
|
|
__tablename__ = "risk_exceptions"
|
|
|
|
id: Mapped[str] = mapped_column(String(64), primary_key=True)
|
|
tenant_id: Mapped[str] = mapped_column(
|
|
String(64),
|
|
ForeignKey("tenants.id", ondelete="CASCADE"),
|
|
nullable=False,
|
|
index=True,
|
|
)
|
|
|
|
# 申请信息
|
|
applicant_id: Mapped[str] = mapped_column(String(64), nullable=False, index=True)
|
|
apply_time: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True),
|
|
nullable=False,
|
|
)
|
|
|
|
# 目标信息
|
|
target_type: Mapped[RiskTargetType] = mapped_column(
|
|
SQLEnum(RiskTargetType, name="risk_target_type_enum"),
|
|
nullable=False,
|
|
)
|
|
target_id: Mapped[str] = mapped_column(String(64), nullable=False, index=True)
|
|
risk_rule_id: Mapped[str] = mapped_column(String(64), nullable=False)
|
|
|
|
# 状态
|
|
status: Mapped[RiskExceptionStatus] = mapped_column(
|
|
SQLEnum(RiskExceptionStatus, name="risk_exception_status_enum"),
|
|
default=RiskExceptionStatus.PENDING,
|
|
nullable=False,
|
|
index=True,
|
|
)
|
|
|
|
# 有效期
|
|
valid_start_time: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True),
|
|
nullable=False,
|
|
)
|
|
valid_end_time: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True),
|
|
nullable=False,
|
|
)
|
|
|
|
# 申请原因
|
|
reason_category: Mapped[str] = mapped_column(String(100), nullable=False)
|
|
justification: Mapped[str] = mapped_column(Text, nullable=False)
|
|
attachment_url: Mapped[Optional[str]] = mapped_column(String(2048), nullable=True)
|
|
|
|
# 审批信息
|
|
current_approver_id: Mapped[Optional[str]] = mapped_column(String(64), nullable=True)
|
|
|
|
# 审批流转日志 (JSON 数组)
|
|
# [{"approver_id": "...", "action": "approve/reject", "comment": "...", "timestamp": "..."}]
|
|
approval_chain_log: Mapped[list] = mapped_column(JSONType, default=list, nullable=False)
|
|
|
|
# 驳回信息
|
|
auto_rejected: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
|
rejection_reason: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
|
|
|
|
# 最近状态变更时间
|
|
last_status_at: Mapped[Optional[datetime]] = mapped_column(
|
|
DateTime(timezone=True),
|
|
nullable=True,
|
|
)
|
|
|
|
# 关联
|
|
tenant: Mapped["Tenant"] = relationship("Tenant", back_populates="risk_exceptions")
|
|
|
|
def __repr__(self) -> str:
|
|
return f"<RiskException(id={self.id}, status={self.status})>"
|