5caf7bc52e
- 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/平台对比)
120 lines
4.0 KiB
Python
120 lines
4.0 KiB
Python
#!/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))
|