feat: 后端 CRUD 完成(episodes + yearly_targets + users 管理 API)

This commit is contained in:
simonkoson
2026-05-15 12:17:46 +08:00
parent fab15505bd
commit 3dc1a1cf3c
10 changed files with 497 additions and 4 deletions
+65
View File
@@ -0,0 +1,65 @@
"""
节目期次模型 — SQLModel
业务规则(.clinerules 5.3 软引用 + 快照模式):
- editor_id 可空(离职编导)
- editor_name_snapshot 必填(历史导入也走快照)
- 查询历史作品:先 editor_id,回退 editor_name_snapshot 文本匹配
"""
from datetime import date, datetime
from sqlalchemy import Column
from sqlalchemy import DateTime as SADateTime
from sqlalchemy import Text
from sqlalchemy import Boolean as SABoolean
from sqlalchemy import Integer as SAInteger
from sqlalchemy import Numeric as SANumeric
from sqlalchemy.sql import func as sa_func
from sqlmodel import Field, SQLModel
class Episode(SQLModel, table=True):
__tablename__ = "episodes"
id: int | None = Field(default=None, primary_key=True)
episode_number: int = Field(unique=True, index=True)
program_name: str = Field(max_length=200)
air_date: date
# 编导(软引用 + 快照双字段)
# 在职编导:editor_id 和 editor_name_snapshot 都填
# 离职编导:editor_id 留空,editor_name_snapshot 填姓名
editor_id: int | None = Field(default=None, foreign_key="users.id")
editor_name_snapshot: str = Field(max_length=100)
# 收视数据
# 注意:.clinerules 5.1 颜色只对 audience_share 上色
# audience_rating 只展示数字,不参与颜色判定
audience_share: float | None = Field(default=None)
audience_rating: float | None = Field(default=None)
# 重播相关
is_rerun: bool = Field(default=False)
original_episode_id: int | None = Field(default=None, foreign_key="episodes.id")
is_time_sensitive: bool = Field(default=False)
# 外拍信息
is_outdoor_shoot: bool = Field(default=False)
outdoor_contact_person: str | None = Field(default=None, max_length=100)
outdoor_contact_info: str | None = Field(default=None, max_length=200)
# 线索
lead_source: str | None = Field(default=None, max_length=300)
# 责编备注
notes: str | None = Field(default=None)
created_at: datetime | None = Field(
default=None,
sa_column=Column(SADateTime(timezone=True), nullable=False, server_default=sa_func.now()),
)
updated_at: datetime | None = Field(
default=None,
sa_column=Column(SADateTime(timezone=True), nullable=False, server_default=sa_func.now()),
)
+34
View File
@@ -0,0 +1,34 @@
"""
年度收视目标模型 — SQLModel
业务规则(.clinerules 5.2 年度目标只增不改):
- yearly_targets.year 有 UNIQUE 约束,数据库层已保证
- 代码端 INSERT 时只许 ON CONFLICT DO NOTHING,禁止 ON CONFLICT UPDATE
- 历史年份目标行绝不可被覆盖或删除
- 前端判颜色时按播出年份取对应年的目标,不是当前年份
"""
from datetime import date, datetime
from sqlalchemy import Column
from sqlalchemy import DateTime as SADateTime
from sqlalchemy import Integer as SAInteger
from sqlalchemy import Numeric as SANumeric
from sqlalchemy.sql import func as sa_func
from sqlmodel import Field, SQLModel
class YearlyTarget(SQLModel, table=True):
__tablename__ = "yearly_targets"
id: int | None = Field(default=None, primary_key=True)
year: int = Field(unique=True, index=True)
base_target: float # 基础目标
stretch_target: float # 摸高目标
issued_date: date | None = Field(default=None)
notes: str | None = Field(default=None)
created_at: datetime | None = Field(
default=None,
sa_column=Column(SADateTime(timezone=True), nullable=False, server_default=sa_func.now()),
)