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
This commit is contained in:
@@ -3,78 +3,112 @@
|
||||
from fastapi import APIRouter, HTTPException, Depends, Request
|
||||
from pydantic import BaseModel
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
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
|
||||
from .auth import get_current_admin, org_filter
|
||||
from ..core.audit_logger import audit_log
|
||||
|
||||
router = APIRouter(prefix="/api/publishing", tags=["publishing"])
|
||||
|
||||
class PublishRequest(BaseModel):
|
||||
topic_id: str
|
||||
PLATFORM_LABELS = {
|
||||
"zhihu": "知乎",
|
||||
"wechat": "微信公众号",
|
||||
"xiaohongshu": "小红书"
|
||||
}
|
||||
|
||||
class PublishResponse(BaseModel):
|
||||
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=PublishResponse)
|
||||
@router.post("/create", response_model=MultiPublishResponse)
|
||||
async def create_publish_record(
|
||||
req: PublishRequest,
|
||||
req: MultiPublishRequest,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_admin)
|
||||
):
|
||||
"""标记选题为已发布,并创建发布记录(管理员)"""
|
||||
"""多平台发布选题(管理员)"""
|
||||
try:
|
||||
# 查找选题
|
||||
topic = db.query(Topic).filter(Topic.id == req.topic_id).first()
|
||||
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() # 设置发布时间为今天
|
||||
|
||||
# 创建发布记录
|
||||
operator = current_user.username
|
||||
record = PublishRecord(
|
||||
topic_id=req.topic_id,
|
||||
platform='all',
|
||||
action='publish',
|
||||
status='success',
|
||||
operator=operator,
|
||||
description=f"选题 {req.topic_id} 已发布"
|
||||
)
|
||||
db.add(record)
|
||||
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, "status": "success"},
|
||||
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
|
||||
)
|
||||
|
||||
return PublishResponse(
|
||||
ok=True,
|
||||
success_count = sum(1 for r in results if r.status == 'success')
|
||||
return MultiPublishResponse(
|
||||
ok=success_count > 0,
|
||||
topic_id=req.topic_id,
|
||||
message=f"选题 {req.topic_id} 已成功发布"
|
||||
results=results,
|
||||
message=f"选题 {req.topic_id} 发布完成({success_count}/{len(results)} 平台成功)"
|
||||
)
|
||||
except HTTPException:
|
||||
raise
|
||||
|
||||
Reference in New Issue
Block a user