Files
yu-zhi-ran/platform/backend/app/api/publishing.py
T
Yuzhiran Dev 5caf7bc52e feat: Wave 2 - API org isolation + factory.html + insights.html
- Add org_id auto-population to articles.py, publishing.py, metrics.py
- Add org_id filtering to search_rankings.py geo endpoints
- Add org_id to calendar.py create/update/delete endpoints
- Add org_id to tasks.py create endpoints
- Create factory.html content pipeline page (3-tab: 待创作/进行中/待审查)
- Create insights.html analytics page (4-tab: 概览/搜索排名/GEO/平台对比)
2026-06-17 16:13:48 +08:00

120 lines
4.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""发布管理 API"""
from fastapi import APIRouter, HTTPException, Depends, Request
from pydantic import BaseModel
from datetime import datetime, timezone, timedelta
from typing import Optional, List
from ..database import get_db
from ..models import Topic, PublishRecord, User
from sqlalchemy.orm import Session
from .auth import get_current_admin, org_filter
from ..core.audit_logger import audit_log
router = APIRouter(prefix="/api/publishing", tags=["publishing"])
PLATFORM_LABELS = {
"zhihu": "知乎",
"wechat": "微信公众号",
"xiaohongshu": "小红书"
}
class MultiPublishRequest(BaseModel):
topic_id: str
platforms: List[str] = ["zhihu", "wechat", "xiaohongshu"]
class PublishResult(BaseModel):
platform: str
platform_label: str
status: str
error_msg: Optional[str] = None
class MultiPublishResponse(BaseModel):
ok: bool
topic_id: str
results: List[PublishResult]
message: str
@router.post("/create", response_model=MultiPublishResponse)
async def create_publish_record(
req: MultiPublishRequest,
request: Request,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_admin)
):
"""多平台发布选题(管理员)"""
try:
q = db.query(Topic).filter(Topic.id == req.topic_id)
of = org_filter(current_user, Topic)
if of is not True:
q = q.filter(of)
topic = q.first()
if not topic:
raise HTTPException(status_code=404, detail=f"选题 {req.topic_id} 不存在")
if topic.status not in ('ready', '待发布'):
raise HTTPException(status_code=400, detail=f"选题 {req.topic_id} 状态不是待发布(当前: {topic.status}")
if not req.platforms:
raise HTTPException(status_code=400, detail="至少选择一个发布平台")
operator = current_user.username
results = []
for platform in req.platforms:
try:
record = PublishRecord(
topic_id=req.topic_id,
platform=platform,
action='publish',
status='success',
operator=operator,
description=f"选题 {req.topic_id} 发布到 {PLATFORM_LABELS.get(platform, platform)}",
org_id=topic.org_id
)
db.add(record)
results.append(PublishResult(
platform=platform,
platform_label=PLATFORM_LABELS.get(platform, platform),
status='success'
))
except Exception as e:
results.append(PublishResult(
platform=platform,
platform_label=PLATFORM_LABELS.get(platform, platform),
status='failed',
error_msg=str(e)
))
topic.status = '已发布'
_now = datetime.now(timezone(timedelta(hours=8)))
topic.updated_at = _now
topic.published_at = _now.date()
db.commit()
db.expire_all()
db.refresh(topic)
audit_log(
action="publish",
user=current_user,
resource_type="topic",
resource_id=req.topic_id,
details={"operator": operator, "platforms": req.platforms, "results": [r.model_dump() for r in results]},
ip_address=request.client.host if request.client else None,
user_agent=request.headers.get("user-agent", ""),
db=db
)
success_count = sum(1 for r in results if r.status == 'success')
return MultiPublishResponse(
ok=success_count > 0,
topic_id=req.topic_id,
results=results,
message=f"选题 {req.topic_id} 发布完成({success_count}/{len(results)} 平台成功)"
)
except HTTPException:
raise
except Exception as e:
db.rollback()
raise HTTPException(status_code=500, detail=str(e))