"""数据库模型 —— 所有表定义""" from sqlalchemy import ( Column, Integer, String, Float, Date, DateTime, Text, ForeignKey, Enum as SAEnum, JSON ) from sqlalchemy.orm import relationship from sqlalchemy.sql import func from database import Base import enum # ──────────────────────────── 枚举定义 ──────────────────────────── class ProjectType(str, enum.Enum): CLIENT_FORMAL = "客户正式项目" CLIENT_TEST = "客户测试项目" INTERNAL_ORIGINAL = "内部原创项目" INTERNAL_TEST = "内部测试项目" class ProjectStatus(str, enum.Enum): IN_PROGRESS = "制作中" COMPLETED = "已完成" class PhaseGroup(str, enum.Enum): PRE = "前期" PRODUCTION = "制作" POST = "后期" class UserRole(str, enum.Enum): MEMBER = "成员" LEADER = "组长" SUPERVISOR = "主管" OWNER = "Owner" class WorkType(str, enum.Enum): PRODUCTION = "制作" TEST = "测试" PLAN = "方案" class ContentType(str, enum.Enum): ANIMATION = "内容制作" DESIGN = "设定策划" EDITING = "剪辑后期" OTHER = "其他" class SubmitTo(str, enum.Enum): LEADER = "组长" PRODUCER = "制片" INTERNAL = "内部" EXTERNAL = "外部" class SubscriptionPeriod(str, enum.Enum): MONTHLY = "月" YEARLY = "年" class CostAllocationType(str, enum.Enum): PROJECT = "指定项目" TEAM = "内容组整体" MANUAL = "手动分摊" class OutsourceType(str, enum.Enum): ANIMATION = "动画" EDITING = "剪辑" FULL_EPISODE = "整集" # ──────────────────────────── 用户 ──────────────────────────── class User(Base): __tablename__ = "users" id = Column(Integer, primary_key=True, index=True) username = Column(String(50), unique=True, nullable=False, index=True) password_hash = Column(String(255), nullable=False) name = Column(String(50), nullable=False) phase_group = Column(SAEnum(PhaseGroup), nullable=False) role = Column(SAEnum(UserRole), nullable=False, default=UserRole.MEMBER) monthly_salary = Column(Float, nullable=False, default=0) is_active = Column(Integer, nullable=False, default=1) created_at = Column(DateTime, server_default=func.now()) # 关系 submissions = relationship("Submission", back_populates="user") led_projects = relationship("Project", back_populates="leader") @property def daily_cost(self): """日成本 = 月薪 ÷ 22""" from config import WORKING_DAYS_PER_MONTH return round(self.monthly_salary / WORKING_DAYS_PER_MONTH, 2) if self.monthly_salary else 0 # ──────────────────────────── 项目 ──────────────────────────── class Project(Base): __tablename__ = "projects" id = Column(Integer, primary_key=True, index=True) name = Column(String(100), nullable=False) project_type = Column(SAEnum(ProjectType), nullable=False) status = Column(SAEnum(ProjectStatus), nullable=False, default=ProjectStatus.IN_PROGRESS) leader_id = Column(Integer, ForeignKey("users.id"), nullable=True) current_phase = Column(SAEnum(PhaseGroup), nullable=False, default=PhaseGroup.PRE) episode_duration_minutes = Column(Float, nullable=False) episode_count = Column(Integer, nullable=False) estimated_completion_date = Column(Date, nullable=True) actual_completion_date = Column(Date, nullable=True) contract_amount = Column(Float, nullable=True) # 仅客户正式项目 created_at = Column(DateTime, server_default=func.now()) # 关系 leader = relationship("User", back_populates="led_projects") submissions = relationship("Submission", back_populates="project") outsource_costs = relationship("OutsourceCost", back_populates="project") ai_tool_allocations = relationship("AIToolCostAllocation", back_populates="project") @property def target_total_seconds(self): """目标总秒数 = 单集时长(分) × 60 × 集数""" return int(self.episode_duration_minutes * 60 * self.episode_count) # ──────────────────────────── 内容提交 ──────────────────────────── class Submission(Base): __tablename__ = "submissions" id = Column(Integer, primary_key=True, index=True) user_id = Column(Integer, ForeignKey("users.id"), nullable=False) project_id = Column(Integer, ForeignKey("projects.id"), nullable=False) project_phase = Column(SAEnum(PhaseGroup), nullable=False) work_type = Column(SAEnum(WorkType), nullable=False) content_type = Column(SAEnum(ContentType), nullable=False) duration_minutes = Column(Float, nullable=True, default=0) duration_seconds = Column(Float, nullable=True, default=0) total_seconds = Column(Float, nullable=False, default=0) # 系统自动计算 hours_spent = Column(Float, nullable=True) # 可选:投入时长(小时) submit_to = Column(SAEnum(SubmitTo), nullable=False) description = Column(Text, nullable=True) submit_date = Column(Date, nullable=False) created_at = Column(DateTime, server_default=func.now()) # 关系 user = relationship("User", back_populates="submissions") project = relationship("Project", back_populates="submissions") history = relationship("SubmissionHistory", back_populates="submission") # ──────────────────────────── AI 工具成本 ──────────────────────────── class AIToolCost(Base): __tablename__ = "ai_tool_costs" id = Column(Integer, primary_key=True, index=True) tool_name = Column(String(100), nullable=False) subscription_period = Column(SAEnum(SubscriptionPeriod), nullable=False) amount = Column(Float, nullable=False) allocation_type = Column(SAEnum(CostAllocationType), nullable=False) project_id = Column(Integer, ForeignKey("projects.id"), nullable=True) # 指定项目时 recorded_by = Column(Integer, ForeignKey("users.id"), nullable=False) record_date = Column(Date, nullable=False) created_at = Column(DateTime, server_default=func.now()) # 关系 allocations = relationship("AIToolCostAllocation", back_populates="ai_tool_cost") class AIToolCostAllocation(Base): """AI 工具成本手动分摊明细""" __tablename__ = "ai_tool_cost_allocations" id = Column(Integer, primary_key=True, index=True) ai_tool_cost_id = Column(Integer, ForeignKey("ai_tool_costs.id"), nullable=False) project_id = Column(Integer, ForeignKey("projects.id"), nullable=False) percentage = Column(Float, nullable=False) # 0-100 ai_tool_cost = relationship("AIToolCost", back_populates="allocations") project = relationship("Project", back_populates="ai_tool_allocations") # ──────────────────────────── 外包成本 ──────────────────────────── class OutsourceCost(Base): __tablename__ = "outsource_costs" id = Column(Integer, primary_key=True, index=True) project_id = Column(Integer, ForeignKey("projects.id"), nullable=False) outsource_type = Column(SAEnum(OutsourceType), nullable=False) episode_start = Column(Integer, nullable=True) episode_end = Column(Integer, nullable=True) amount = Column(Float, nullable=False) recorded_by = Column(Integer, ForeignKey("users.id"), nullable=False) record_date = Column(Date, nullable=False) created_at = Column(DateTime, server_default=func.now()) project = relationship("Project", back_populates="outsource_costs") # ──────────────────────────── 人力成本手动调整 ──────────────────────────── class CostOverride(Base): """管理员手动修改某人某天的成本分摊""" __tablename__ = "cost_overrides" id = Column(Integer, primary_key=True, index=True) user_id = Column(Integer, ForeignKey("users.id"), nullable=False) date = Column(Date, nullable=False) project_id = Column(Integer, ForeignKey("projects.id"), nullable=False) override_amount = Column(Float, nullable=False) adjusted_by = Column(Integer, ForeignKey("users.id"), nullable=False) reason = Column(Text, nullable=True) created_at = Column(DateTime, server_default=func.now()) # ──────────────────────────── 提交历史版本 ──────────────────────────── class SubmissionHistory(Base): """内容提交的修改历史""" __tablename__ = "submission_history" id = Column(Integer, primary_key=True, index=True) submission_id = Column(Integer, ForeignKey("submissions.id"), nullable=False) changed_by = Column(Integer, ForeignKey("users.id"), nullable=False) change_reason = Column(Text, nullable=False) old_data = Column(JSON, nullable=False) new_data = Column(JSON, nullable=False) created_at = Column(DateTime, server_default=func.now()) submission = relationship("Submission", back_populates="history")