Files
yu-zhi-ran/platform/backend/app/api/calendar.py
T
Yuzhiran Dev 233e23016c feat: 内容数据迁移至数据库,合规审查全链路打通
- 文章 HTML 存储从文件系统迁移至 articles 表,删除 releases 目录
- 合规审查从 DB 读取 HTML,审查结果写回 DB,通过后自动推进至待发布
- 新增 todayCount 筛选按钮,与系统概览统计数据一致
- 全屏预览修复:提升 z-index 超过侧边栏,添加退出全屏/关闭按钮
- 统一 '优化' → '审查' 命名,消除前后端术语不一致
- 调度器创作完成后自动触发审查(生成 → 审查 → 待发布)
- 清理旧备份/调试文件、过期大纲和研究笔记
2026-05-13 17:33:56 +08:00

191 lines
5.9 KiB
Python

from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from typing import List, Optional
from datetime import datetime, date
from calendar import monthrange
from ..database import get_db
from ..models import ContentCalendar, Topic
from ..schemas import (
ContentCalendarCreate, ContentCalendarUpdate, ContentCalendarResponse,
ContentCalendarBase
)
from .auth import get_current_user
router = APIRouter(prefix="/api/calendar", tags=["calendar"])
@router.get("", response_model=List[ContentCalendarResponse])
def get_calendar(
year: Optional[int] = Query(None),
month: Optional[int] = Query(None),
db: Session = Depends(get_db),
current_user=Depends(get_current_user)
):
today = date.today()
year = year or today.year
month = month or today.month
start = date(year, month, 1)
last_day = monthrange(year, month)[1]
end = date(year, month, last_day)
return db.query(ContentCalendar).filter(
ContentCalendar.planned_date >= start,
ContentCalendar.planned_date <= end
).order_by(ContentCalendar.planned_date).all()
@router.get("/entries", response_model=List[ContentCalendarResponse])
def list_entries(
start_date: Optional[date] = None,
end_date: Optional[date] = None,
status: Optional[str] = None,
platform: Optional[str] = None,
limit: int = Query(50, le=200),
db: Session = Depends(get_db),
current_user=Depends(get_current_user)
):
query = db.query(ContentCalendar)
if start_date:
query = query.filter(ContentCalendar.planned_date >= start_date)
if end_date:
query = query.filter(ContentCalendar.planned_date <= end_date)
if status:
query = query.filter(ContentCalendar.status == status)
if platform:
query = query.filter(ContentCalendar.platform == platform)
return query.order_by(ContentCalendar.planned_date.desc()).limit(limit).all()
@router.post("/entries", response_model=ContentCalendarResponse)
def create_entry(
data: ContentCalendarCreate,
db: Session = Depends(get_db),
current_user=Depends(get_current_user)
):
if data.topic_id:
topic = db.query(Topic).filter(Topic.id == data.topic_id).first()
if not topic:
raise HTTPException(status_code=404, detail="选题不存在")
entry = ContentCalendar(**data.model_dump())
db.add(entry)
db.commit()
db.refresh(entry)
if data.topic_id and data.title:
if not topic.title or topic.title != data.title:
topic.title = data.title
db.commit()
return entry
@router.put("/entries/{entry_id}", response_model=ContentCalendarResponse)
def update_entry(
entry_id: int,
data: ContentCalendarUpdate,
db: Session = Depends(get_db),
current_user=Depends(get_current_user)
):
entry = db.query(ContentCalendar).filter(ContentCalendar.id == entry_id).first()
if not entry:
raise HTTPException(status_code=404, detail="日历条目不存在")
for k, v in data.model_dump(exclude_unset=True).items():
setattr(entry, k, v)
db.commit()
db.refresh(entry)
if entry.topic_id:
topic = db.query(Topic).filter(Topic.id == entry.topic_id).first()
if topic and entry.status == "published" and not entry.published_date:
entry.published_date = date.today()
if topic.status == "pending" or topic.status == "ready":
topic.status = "published"
topic.published_at = date.today()
topic.updated_at = datetime.now()
db.commit()
return entry
@router.delete("/entries/{entry_id}")
def delete_entry(
entry_id: int,
db: Session = Depends(get_db),
current_user=Depends(get_current_user)
):
entry = db.query(ContentCalendar).filter(ContentCalendar.id == entry_id).first()
if not entry:
raise HTTPException(status_code=404, detail="日历条目不存在")
db.delete(entry)
db.commit()
return {"ok": True}
@router.post("/entries/bind-topic")
def bind_topic_to_entry(
entry_id: int,
topic_id: str,
db: Session = Depends(get_db),
current_user=Depends(get_current_user)
):
entry = db.query(ContentCalendar).filter(ContentCalendar.id == entry_id).first()
if not entry:
raise HTTPException(status_code=404, detail="日历条目不存在")
topic = db.query(Topic).filter(Topic.id == topic_id).first()
if not topic:
raise HTTPException(status_code=404, detail="选题不存在")
entry.topic_id = topic_id
entry.title = topic.title
if topic.field_name:
entry.field_id = topic.field_id
db.commit()
db.refresh(entry)
return entry
@router.get("/stats")
def calendar_stats(
year: int = Query(...),
month: int = Query(...),
db: Session = Depends(get_db),
current_user=Depends(get_current_user)
):
start = date(year, month, 1)
last_day = monthrange(year, month)[1]
end = date(year, month, last_day)
entries = db.query(ContentCalendar).filter(
ContentCalendar.planned_date >= start,
ContentCalendar.planned_date <= end
).all()
stats = {"total": len(entries), "planned": 0, "published": 0, "delayed": 0, "cancelled": 0}
for e in entries:
if e.status in stats:
stats[e.status] += 1
return stats
@router.post("/entries/from-topic")
def create_from_topic(
topic_id: str,
planned_date: date,
platform: str = Query(...),
db: Session = Depends(get_db),
current_user=Depends(get_current_user)
):
topic = db.query(Topic).filter(Topic.id == topic_id).first()
if not topic:
raise HTTPException(status_code=404, detail="选题不存在")
entry = ContentCalendar(
topic_id=topic_id,
field_id=topic.field_id,
title=topic.title,
planned_date=planned_date,
platform=platform,
created_by=current_user.username
)
db.add(entry)
db.commit()
db.refresh(entry)
return entry