Files
yu-zhi-ran/platform/backend/app/api/publishing.py
T
Yuzhiran Dev 9c37c9a574 feat: Phase 4 多租户隔离 + 四阶段升级测试 + CSS 统一化
Phase 4: org_id 注入 JWT/API 过滤/组织管理 CRUD/前端组织列
测试: tests/test_phase_upgrades.py 97项全覆盖
CSS: theme-modern.css 共享 mobile-card-list/status-dot/search-bar 等模式
修复: initial_data.py LLM配置 NOT NULL 约束, TopicResponse 含 org_id
2026-05-17 06:56:53 +08:00

118 lines
3.9 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
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)}"
)
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 = '已发布'
topic.updated_at = datetime.now()
topic.published_at = datetime.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))