240 lines
8.1 KiB
Python
240 lines
8.1 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, org_filter
|
|
|
|
router = APIRouter(prefix="/api/calendar", tags=["calendar"])
|
|
|
|
|
|
PLATFORM_ICONS = {
|
|
"zhihu": "知",
|
|
"wechat": "微",
|
|
"xiaohongshu": "红"
|
|
}
|
|
|
|
def enrich_entry(entry, db):
|
|
d = entry.to_dict()
|
|
d["platform_icon"] = PLATFORM_ICONS.get(entry.platform, "📝")
|
|
d["topic_status"] = None
|
|
if entry.topic_id:
|
|
topic = db.query(Topic).filter(Topic.id == entry.topic_id).first()
|
|
if topic:
|
|
d["topic_status"] = topic.status
|
|
return d
|
|
|
|
@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)
|
|
base = db.query(ContentCalendar).filter(
|
|
ContentCalendar.planned_date >= start,
|
|
ContentCalendar.planned_date <= end
|
|
)
|
|
of = org_filter(current_user, Topic)
|
|
if of is not True:
|
|
base = base.outerjoin(Topic, ContentCalendar.topic_id == Topic.id).filter(
|
|
(ContentCalendar.topic_id.is_(None)) | (Topic.org_id == current_user.org_id)
|
|
)
|
|
entries = base.order_by(ContentCalendar.planned_date).all()
|
|
return [enrich_entry(e, db) for e in entries]
|
|
|
|
|
|
@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)
|
|
of = org_filter(current_user, Topic)
|
|
if of is not True:
|
|
query = query.outerjoin(Topic, ContentCalendar.topic_id == Topic.id).filter(
|
|
(ContentCalendar.topic_id.is_(None)) | (Topic.org_id == current_user.org_id)
|
|
)
|
|
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="选题不存在")
|
|
if current_user.role != "admin" and topic.org_id != current_user.org_id:
|
|
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="日历条目不存在")
|
|
|
|
if entry.topic_id:
|
|
topic = db.query(Topic).filter(Topic.id == entry.topic_id).first()
|
|
if topic and current_user.role != "admin" and topic.org_id != current_user.org_id:
|
|
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="日历条目不存在")
|
|
if entry.topic_id:
|
|
topic = db.query(Topic).filter(Topic.id == entry.topic_id).first()
|
|
if topic and current_user.role != "admin" and topic.org_id != current_user.org_id:
|
|
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="选题不存在")
|
|
if current_user.role != "admin" and topic.org_id != current_user.org_id:
|
|
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)
|
|
q = db.query(ContentCalendar).filter(
|
|
ContentCalendar.planned_date >= start,
|
|
ContentCalendar.planned_date <= end
|
|
)
|
|
of = org_filter(current_user, Topic)
|
|
if of is not True:
|
|
q = q.outerjoin(Topic, ContentCalendar.topic_id == Topic.id).filter(
|
|
(ContentCalendar.topic_id.is_(None)) | (Topic.org_id == current_user.org_id)
|
|
)
|
|
entries = q.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="选题不存在")
|
|
if current_user.role != "admin" and topic.org_id != current_user.org_id:
|
|
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 |