96 lines
3.4 KiB
Python
96 lines
3.4 KiB
Python
|
|
"""
|
||
|
|
年度收视目标 API — 增改查
|
||
|
|
|
||
|
|
业务规则(.clinerules 5.2 只增不改):
|
||
|
|
- yearly_targets.year 有 UNIQUE 约束
|
||
|
|
- INSERT 时 year 冲突 → 直接报错,阻止静默覆盖
|
||
|
|
- 禁止 ON CONFLICT UPDATE
|
||
|
|
"""
|
||
|
|
|
||
|
|
from fastapi import APIRouter, Depends, HTTPException, status
|
||
|
|
from sqlmodel import Session, select
|
||
|
|
|
||
|
|
from app.core.deps import get_current_user, require_role
|
||
|
|
from app.db.session import get_session
|
||
|
|
from app.models.user import User, UserRole
|
||
|
|
from app.schemas.yearly_target import YearlyTargetCreate, YearlyTargetResponse, YearlyTargetUpdate
|
||
|
|
|
||
|
|
router = APIRouter(prefix="/api/yearly_targets", tags=["年度目标"])
|
||
|
|
|
||
|
|
|
||
|
|
@router.get("", response_model=list[YearlyTargetResponse])
|
||
|
|
def list_targets(
|
||
|
|
session: Session = Depends(get_session),
|
||
|
|
current_user: User = Depends(require_role(UserRole.zhipianren, UserRole.zebian)),
|
||
|
|
):
|
||
|
|
"""获取所有年度目标(仅管理员/责编)。"""
|
||
|
|
statement = select(YearlyTarget).order_by(YearlyTarget.year.desc())
|
||
|
|
targets = session.exec(statement).all()
|
||
|
|
return targets
|
||
|
|
|
||
|
|
|
||
|
|
@router.get("/{target_id}", response_model=YearlyTargetResponse)
|
||
|
|
def get_target(
|
||
|
|
target_id: int,
|
||
|
|
session: Session = Depends(get_session),
|
||
|
|
current_user: User = Depends(require_role(UserRole.zhipianren, UserRole.zebian)),
|
||
|
|
):
|
||
|
|
"""根据 ID 获取单个年度目标(仅管理员/责编)。"""
|
||
|
|
target = session.get(YearlyTarget, target_id)
|
||
|
|
if target is None:
|
||
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="目标不存在")
|
||
|
|
return target
|
||
|
|
|
||
|
|
|
||
|
|
@router.post("", response_model=YearlyTargetResponse, status_code=status.HTTP_201_CREATED)
|
||
|
|
def create_target(
|
||
|
|
body: YearlyTargetCreate,
|
||
|
|
session: Session = Depends(get_session),
|
||
|
|
current_user: User = Depends(require_role(UserRole.zhipianren, UserRole.zebian)),
|
||
|
|
):
|
||
|
|
"""新增年度目标(仅管理员/责编)。
|
||
|
|
|
||
|
|
注意:year 有 UNIQUE 约束,重复年份会直接报错。
|
||
|
|
这是预期行为——历史年份目标不可覆盖。
|
||
|
|
"""
|
||
|
|
# 先查是否已存在(让报错更友好,而不是数据库层直接崩溃)
|
||
|
|
existing = session.exec(select(YearlyTarget).where(YearlyTarget.year == body.year)).first()
|
||
|
|
if existing:
|
||
|
|
raise HTTPException(
|
||
|
|
status_code=status.HTTP_409_CONFLICT,
|
||
|
|
detail=f"{body.year} 年目标已存在,历史年份不可覆盖。请新增其他年份。",
|
||
|
|
)
|
||
|
|
|
||
|
|
target = YearlyTarget.model_validate(body.model_dump())
|
||
|
|
session.add(target)
|
||
|
|
session.commit()
|
||
|
|
session.refresh(target)
|
||
|
|
return target
|
||
|
|
|
||
|
|
|
||
|
|
@router.patch("/{target_id}", response_model=YearlyTargetResponse)
|
||
|
|
def update_target(
|
||
|
|
target_id: int,
|
||
|
|
body: YearlyTargetUpdate,
|
||
|
|
session: Session = Depends(get_session),
|
||
|
|
current_user: User = Depends(require_role(UserRole.zhipianren, UserRole.zebian)),
|
||
|
|
):
|
||
|
|
"""更新年度目标(仅管理员/责编)。
|
||
|
|
|
||
|
|
注意:此接口仅用于修正错字等小修,year 字段不可修改。
|
||
|
|
"""
|
||
|
|
target = session.get(YearlyTarget, target_id)
|
||
|
|
if target is None:
|
||
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="目标不存在")
|
||
|
|
|
||
|
|
update_data = body.model_dump(exclude_unset=True)
|
||
|
|
# 禁止修改 year 字段
|
||
|
|
update_data.pop("year", None)
|
||
|
|
|
||
|
|
for field, value in update_data.items():
|
||
|
|
setattr(target, field, value)
|
||
|
|
|
||
|
|
session.add(target)
|
||
|
|
session.commit()
|
||
|
|
session.refresh(target)
|
||
|
|
return target
|