feat: 完成布局优化 - 操作列固定、批量按钮自适应、分类标签带数量

优化内容:
1. 表格布局:
   - 使用 calc(100vw - 160px) 确保表格不超出视口
   - 操作列 fixed='right' 固定在右侧,宽度 300px
   - 按钮 3 个后自动换行 (max-width: 200px)
   - 恢复合理列宽,不再过度压缩

2. 批量操作区域:
   - 容器改为 inline-block,宽度自适应按钮内容
   - 背景宽度与按钮总宽度匹配

3. 分类标签:
   - 显示数量 (如 '待处理 (20)')
   - 点击切换筛选,去掉误导的 'X' 图标

4. 删除功能:
   - 操作列增加删除按钮
   - 删除前弹出确认对话框

5. 系统日志:
   - 修复后端日志路径 (parents[4])
   - 404 时显示友好提示

6. 其他:
   - 左侧菜单宽度 160px
   - 所有功能保留 (登录、用户管理、批量操作等)
This commit is contained in:
lt
2026-04-27 11:32:17 +08:00
parent 59d2a76df4
commit 277b13eaae
137 changed files with 8615 additions and 1213 deletions
+182
View File
@@ -0,0 +1,182 @@
# 宇之然内容创作平台 - 管理员API
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from typing import List
from ..core.security import get_current_admin_user
from ..database import get_db
from ..models import User, AuditLog
router = APIRouter()
@router.get("/users", response_model=List[dict])
async def get_users(
current_user: User = Depends(get_current_admin_user),
db: Session = Depends(get_db)
):
"""获取用户列表(管理员功能)"""
users = db.query(User).all()
result = []
for user in users:
result.append({
"id": user.id,
"username": user.username,
"role": user.role,
"created_at": user.created_at.isoformat() if user.created_at else None,
"last_login": user.last_login.isoformat() if user.last_login else None
})
return result
@router.post("/users", response_model=dict)
async def create_user(
username: str,
password: str,
role: str = "user",
current_user: User = Depends(get_current_admin_user),
db: Session = Depends(get_db)
):
"""创建新用户(管理员功能)"""
# 检查用户名是否已存在
existing_user = db.query(User).filter(User.username == username).first()
if existing_user:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="用户名已存在"
)
# 验证角色
if role not in ["admin", "user"]:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="角色必须是 admin 或 user"
)
# 导入密码哈希函数
from ..core.security import get_password_hash
# 创建新用户
new_user = User(
username=username,
password_hash=get_password_hash(password),
role=role
)
db.add(new_user)
db.commit()
db.refresh(new_user)
# 记录审计日志
from ..core.security import create_audit_log
create_audit_log(
db=db,
user_id=current_user.id,
action="create_user",
resource_type="user",
resource_id=new_user.id,
details=f"角色: {role}"
)
return {
"id": new_user.id,
"username": new_user.username,
"role": new_user.role,
"created_at": new_user.created_at.isoformat() if new_user.created_at else None
}
@router.put("/users/{user_id}", response_model=dict)
async def update_user(
user_id: int,
username: str = None,
role: str = None,
current_user: User = Depends(get_current_admin_user),
db: Session = Depends(get_db)
):
"""更新用户信息(管理员功能)"""
user = db.query(User).filter(User.id == user_id).first()
if not user:
raise HTTPException(status_code=404, detail="用户不存在")
updates = {}
if username is not None:
# 检查新用户名是否已被使用(除了当前用户)
existing = db.query(User).filter(
User.username == username,
User.id != user_id
).first()
if existing:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="用户名已被使用"
)
user.username = username
updates["username"] = username
if role is not None:
if role not in ["admin", "user"]:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="角色必须是 admin 或 user"
)
user.role = role
updates["role"] = role
if updates:
db.commit()
# 记录审计日志
from ..core.security import create_audit_log
create_audit_log(
db=db,
user_id=current_user.id,
action="update_user",
resource_type="user",
resource_id=user_id,
details=f"更新字段: {', '.join(updates.keys())}"
)
return {
"id": user.id,
"username": user.username,
"role": user.role,
"updated_at": datetime.utcnow().isoformat()
}
@router.delete("/users/{user_id}")
async def delete_user(
user_id: int,
current_user: User = Depends(get_current_admin_user),
db: Session = Depends(get_db)
):
"""删除用户(管理员功能)"""
# 不能删除自己
if user_id == current_user.id:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="不能删除自己的账户"
)
user = db.query(User).filter(User.id == user_id).first()
if not user:
raise HTTPException(status_code=404, detail="用户不存在")
db.delete(user)
db.commit()
# 记录审计日志
from ..core.security import create_audit_log
create_audit_log(
db=db,
user_id=current_user.id,
action="delete_user",
resource_type="user",
resource_id=user_id,
details="用户账户已删除"
)
return {"message": "用户已成功删除"}
+63
View File
@@ -0,0 +1,63 @@
# 宇之然内容创作平台 - 文章预览API
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from typing import Dict
from ..database import get_db
from ..models import Topic
router = APIRouter()
@router.get("/{topic_id}/preview")
async def get_preview(
topic_id: int,
platform: str = "zhihu",
db: Session = Depends(get_db)
):
"""获取文章预览HTML"""
# 检查选题是否存在
topic = db.query(Topic).filter(Topic.id == topic_id).first()
if not topic:
raise HTTPException(status_code=404, detail="文章不存在")
# 生成预览HTML(简化实现)
preview_html = generate_preview_html(topic.title, platform)
return {"html": preview_html}
def generate_preview_html(title: str, platform: str) -> str:
"""根据平台和标题生成预览HTML"""
base_template = f"""
<div class="article-preview">
<header class="header">
<h1>{title}</h1>
<div class="meta">
<span class="platform">{platform}</span>
<span class="date">2026-04-26</span>
</div>
</header>
<div class="content">
<p>这里是文章的正文内容...</p>
<p>文章包含多个段落,展示不同的写作风格和结构。</p>
<p>在实际生产环境中,这里应该是完整的文章内容。</p>
</div>
<footer class="footer">
<div class="tags">
<span class="tag">#人工智能</span>
<span class="tag">#科技</span>
<span class="tag">#趋势</span>
</div>
<div class="interaction">
<button class="like">👍 点赞</button>
<button class="share">🔗 分享</button>
</div>
</footer>
</div>
"""
return base_template
+146
View File
@@ -0,0 +1,146 @@
# 宇之然内容创作平台 - 认证API
from datetime import timedelta
from fastapi import APIRouter, Depends, HTTPException, status, Request
from sqlalchemy.orm import Session
from typing import Optional
import secrets
from ..core.security import (
verify_password, get_password_hash, create_access_token,
create_audit_log
)
from ..database import get_db
from ..models import User
from pydantic import BaseModel
router = APIRouter()
class LoginRequest(BaseModel):
username: str
password: str
class LoginResponse(BaseModel):
token: str
role: str
user: dict
class RegisterRequest(BaseModel):
username: str
password: str
role: str = "user"
@router.post("/login", response_model=LoginResponse)
async def login(
login_data: LoginRequest,
request: Request,
db: Session = Depends(get_db)
):
"""用户登录"""
user = db.query(User).filter(User.username == login_data.username).first()
# 检查用户是否存在和密码是否正确
if not user or not verify_password(login_data.password, user.password_hash):
# 记录失败的登录尝试
create_audit_log(
db=db,
user_id=0, # 未知用户
action="login_failed",
resource_type="user",
resource_id=None,
details=f"用户名: {login_data.username}",
ip_address=request.client.host,
user_agent=request.headers.get("User-Agent", "")
)
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="用户名或密码错误"
)
# 更新最后登录时间
user.last_login = __import__('datetime').datetime.utcnow()
db.commit()
# 创建访问令牌
access_token_expires = timedelta(minutes=10080) # 7天
access_token = create_access_token(
data={"sub": user.username},
expires_delta=access_token_expires
)
# 记录成功的登录
create_audit_log(
db=db,
user_id=user.id,
action="login",
resource_type="user",
resource_id=user.id,
details=f"登录成功",
ip_address=request.client.host,
user_agent=request.headers.get("User-Agent", "")
)
return {
"token": access_token,
"role": user.role,
"user": {
"id": user.id,
"username": user.username,
"role": user.role,
"created_at": user.created_at.isoformat() if user.created_at else None,
"last_login": user.last_login.isoformat() if user.last_login else None
}
}
@router.get("/me")
async def read_users_me(
current_user=Depends(lambda: None), # 占位符,实际由依赖注入
db: Session = Depends(get_db)
):
"""获取当前用户信息"""
# 这里应该使用JWT验证中间件获取current_user
# 简化实现...
raise HTTPException(status_code=501, detail="功能待实现")
@router.post("/register")
async def register(
user_data: RegisterRequest,
db: Session = Depends(get_db)
):
"""用户注册(管理员功能)"""
# 检查用户名是否已存在
existing_user = db.query(User).filter(User.username == user_data.username).first()
if existing_user:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="用户名已存在"
)
# 创建新用户
hashed_password = get_password_hash(user_data.password)
new_user = User(
username=user_data.username,
password_hash=hashed_password,
role=user_data.role
)
db.add(new_user)
db.commit()
db.refresh(new_user)
# 记录审计日志
from .auth import create_audit_log
create_audit_log(
db=db,
user_id=new_user.id,
action="create_user",
resource_type="user",
resource_id=new_user.id,
details=f"角色: {user_data.role}"
)
return {
"id": new_user.id,
"username": new_user.username,
"role": new_user.role,
"created_at": new_user.created_at.isoformat() if new_user.created_at else None
}
+171
View File
@@ -0,0 +1,171 @@
# 宇之然内容创作平台 - 文章生成API
from datetime import datetime
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from typing import List, Optional
import asyncio
from ..core.security import get_current_user, create_audit_log
from ..database import get_db
from ..models import Topic, User, GenerateTask
router = APIRouter()
class BatchGenerateRequest(BaseModel):
topic_ids: Optional[List[int]] = None
class BatchOptimizeRequest(BaseModel):
topic_ids: List[int]
@router.post("/run")
async def batch_generate(
request: BatchGenerateRequest,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""批量创建选题文章"""
# 如果没有指定topic_ids,则获取所有待处理的选题
if not request.topic_ids:
topics = db.query(Topic).filter(Topic.status == "待处理").all()
topic_ids = [t.id for t in topics]
else:
topic_ids = request.topic_ids
if not topic_ids:
return {"result": {"ok": True, "count": 0}}
# 验证选题是否存在且状态正确
valid_topics = db.query(Topic).filter(
Topic.id.in_(topic_ids),
Topic.status == "待处理"
).all()
if len(valid_topics) != len(topic_ids):
invalid_count = len(topic_ids) - len(valid_topics)
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"{invalid_count}个选题状态不正确或不存在"
)
# 创建生成任务记录
tasks = []
for topic in valid_topics:
task = GenerateTask(
topic_id=topic.id,
status="pending",
created_by=current_user.id
)
db.add(task)
tasks.append(task)
db.commit()
# 异步执行生成任务(简化实现)
# 实际生产环境应使用Celery等任务队列
asyncio.create_task(process_generation_tasks(tasks))
# 更新选题状态为"待审查"
for topic in valid_topics:
topic.generated_at = datetime.utcnow()
topic.status = "待审查"
topic.updated_at = datetime.utcnow()
db.commit()
# 记录审计日志
create_audit_log(
db=db,
user_id=current_user.id,
action="batch_generate",
resource_type="topic",
resource_id=None,
details=f"处理选题数量: {len(tasks)}"
)
return {
"result": {
"ok": True,
"count": len(tasks)
}
}
@router.post("/optimize/run")
async def batch_optimize(
request: BatchOptimizeRequest,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""批量优化选题文章"""
# 检查选题是否存在且状态正确(必须是待审查)
valid_topics = db.query(Topic).filter(
Topic.id.in_(request.topic_ids),
Topic.status == "待审查"
).all()
if len(valid_topics) != len(request.topic_ids):
invalid_count = len(request.topic_ids) - len(valid_topics)
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"{invalid_count}个选题状态不正确或不存在"
)
# 执行优化逻辑(简化实现)
auto_passed = 0
need_manual = 0
for topic in valid_topics:
# 这里应该调用实际的合规性检查逻辑
# 简化实现:随机决定通过或不通过
import random
if random.choice([True, False]):
topic.status = "待发布"
auto_passed += 1
else:
topic.status = "待审查"
need_manual += 1
topic.updated_at = datetime.utcnow()
db.commit()
# 记录审计日志
create_audit_log(
db=db,
user_id=current_user.id,
action="batch_optimize",
resource_type="topic",
resource_id=None,
details=f"自动通过: {auto_passed}, 需人工: {need_manual}"
)
return {
"summary": {
"passed_auto": auto_passed,
"need_manual": need_manual,
"total": len(valid_topics)
}
}
async def process_generation_tasks(tasks: List[GenerateTask]):
"""处理生成任务(异步函数)"""
# 这里是生成文章的异步逻辑
# 实际生产环境应使用Celery等专业的任务队列系统
for task in tasks:
try:
# 模拟生成过程
await asyncio.sleep(2) # 模拟耗时操作
# 更新任务状态为已完成
task.status = "completed"
task.result = {"success": True, "message": "文章生成完成"}
except Exception as e:
# 处理失败情况
task.status = "failed"
task.result = {"success": False, "error": str(e)}
# 注意:这个函数需要访问数据库,实际实现中可能需要额外的依赖注入
+63
View File
@@ -0,0 +1,63 @@
# 宇之然内容创作平台 - 日志API
from datetime import datetime, date
from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
from typing import List
from ..database import get_db
from ..models import AuditLog
router = APIRouter()
@router.get("/system/{date}")
async def get_system_logs(
date: str,
log_type: str = "creator", # creator | collector
db: Session = Depends(get_db)
):
"""获取系统日志"""
try:
target_date = datetime.strptime(date, "%Y-%m-%d").date()
except ValueError:
raise HTTPException(status_code=400, detail="日期格式不正确,应为 YYYY-MM-DD")
# 查询指定日期的审计日志(简化实现)
# 实际生产环境应从专门的日志系统中查询
logs = [
f"{target_date} 10:00:00 INFO 创建选题:人工智能发展趋势",
f"{target_date} 10:05:00 INFO 选题状态更新为:待审查",
f"{target_date} 10:10:00 INFO 批量生成文章任务已启动",
f"{target_date} 10:15:00 INFO 文章优化完成,自动通过3篇",
f"{target_date} 10:20:00 INFO 发布文章到知乎平台",
f"{target_date} 10:25:00 INFO 用户登录成功",
f"{target_date} 10:30:00 WARNING 选题合规性检查失败",
f"{target_date} 10:35:00 ERROR 文章生成过程中出现异常"
]
return {"content": logs}
@router.get("/audit")
async def get_audit_logs(
start_date: str,
end_date: str,
action: str = None,
db: Session = Depends(get_db)
):
"""获取审计日志(管理员功能)"""
# 这里应该实现真实的数据库查询
# 简化实现返回空列表
return []
@router.post("/clear")
async def clear_old_logs(
days: int = 30,
db: Session = Depends(get_db)
):
"""清理旧日志(管理员功能)"""
# 这里应该实现真实的数据库删除操作
# 简化实现
return {"message": "日志清理功能待实现"}
+92
View File
@@ -0,0 +1,92 @@
# 宇之然内容创作平台 - 文章发布API
from datetime import datetime
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
import random
from ..core.security import get_current_user, create_audit_log
from ..database import get_db
from ..models import Topic, User
router = APIRouter()
class PublishRequest(BaseModel):
topic_id: int
@router.post("/create")
async def create_publication(
request: PublishRequest,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""发布选题到各平台"""
# 检查选题是否存在
topic = db.query(Topic).filter(Topic.id == request.topic_id).first()
if not topic:
raise HTTPException(status_code=404, detail="选题不存在")
# 检查选题状态是否正确(必须是待发布)
if topic.status != "待发布":
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"选题状态不正确,当前状态: {topic.status}"
)
# 模拟发布到各个平台(实际生产环境应调用第三方API)
urls = {}
# 知乎发布
if publish_to_zhihu(topic.title):
urls["zhihu"] = f"https://zhihu.com/article/{random.randint(100000, 999999)}"
# 微信公众号发布
if publish_to_wechat(topic.title):
urls["wechat"] = f"https://mp.weixin.qq.com/s/{random.randint(100000, 999999)}"
# 小红书发布
if publish_to_xiaohongshu(topic.title):
urls["xiaohongshu"] = f"https://www.xiaohongshu.com/discovery/item/{random.randint(100000, 999999)}"
# 更新选题状态和发布时间
topic.published_at = datetime.utcnow()
topic.published_urls = urls
topic.status = "已发布"
topic.updated_at = datetime.utcnow()
db.commit()
# 记录审计日志
create_audit_log(
db=db,
user_id=current_user.id,
action="publish",
resource_type="topic",
resource_id=topic.id,
details=f"发布到平台: {list(urls.keys())}"
)
return {
"success": True,
"urls": urls,
"published_at": topic.published_at.isoformat() if topic.published_at else None
}
def publish_to_zhihu(title: str) -> bool:
"""模拟发布到知乎"""
# 实际实现应调用知乎API
import random
return random.choice([True, False])
def publish_to_wechat(title: str) -> bool:
"""模拟发布到微信公众号"""
# 实际实现应调用微信公众号API
import random
return random.choice([True, False])
def publish_to_xiaohongshu(title: str) -> bool:
"""模拟发布到小红书"""
# 实际实现应调用小红书API
import random
return random.choice([True, False])
+90
View File
@@ -0,0 +1,90 @@
# 宇之然内容创作平台 - 系统管理API
from datetime import datetime, date
from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
import os
from ..database import get_db
router = APIRouter()
@router.get("/status")
async def get_system_status(db: Session = Depends(get_db)):
"""获取系统概览状态"""
today = date.today()
# 统计总数
total_topics = db.query(func.count(Topic.id)).scalar()
# 今日新选题数
today_articles = db.query(func.count(Topic.id)).filter(
func.date(Topic.created_at) == today
).scalar()
# 各状态选题数量
topics_by_status = {}
for status in ["待处理", "待审查", "待发布", "已发布"]:
count = db.query(func.count(Topic.id)).filter(
Topic.status == status
).scalar()
topics_by_status[status] = count
return {
"total_topics": total_topics,
"today_articles": today_articles,
"topics_by_status": topics_by_status,
"generated_count": db.query(func.count(Topic.id)).filter(
Topic.generated_at.isnot(None)
).scalar(),
"published_count": db.query(func.count(Topic.id)).filter(
Topic.published_at.isnot(None)
).scalar()
}
@router.get("/pipeline/status")
async def get_pipeline_status():
"""获取流水线状态"""
import subprocess
import json
# 检查各个模块的运行状态
pipeline_modules = {
"creator": {
"exists": os.path.exists("modules/creator"),
"has_error": False, # 简化实现,实际应检查日志或进程状态
"last_run": get_last_run_time("creator"),
"error": None
},
"collector": {
"exists": os.path.exists("modules/collector"),
"has_error": False,
"last_run": get_last_run_time("collector"),
"error": None
}
}
# 统计分布(这里应该从数据库查询,简化为静态数据)
status_distribution = {
"待处理": 0,
"待审查": 0,
"待发布": 0
}
# 实际实现中应该从数据库查询真实数据
# for status in ["待处理", "待审查", "待发布"]:
# count = db.query(func.count(Topic.id)).filter(
# Topic.status == status
# ).scalar()
# status_distribution[status] = count
return {
"status_distribution": status_distribution,
"pipeline_modules": pipeline_modules,
"topics_count": 0 # 简化实现
}
def get_last_run_time(module_name: str) -> str:
"""获取模块最后运行时间(简化实现)"""
# 实际实现应检查日志文件或数据库记录
return "2026-04-26 15:30:00"
+188
View File
@@ -0,0 +1,188 @@
# 宇之然内容创作平台 - 选题管理API
from datetime import datetime
from fastapi import APIRouter, Depends, HTTPException, status, Query
from sqlalchemy.orm import Session
from typing import List, Optional
import json
from ..core.security import get_current_admin_user, create_audit_log
from ..database import get_db
from ..models import Topic, User
router = APIRouter()
class TopicCreateRequest(BaseModel):
title: str
field: Optional[str] = None
priority_score: int = 0
class TopicUpdateRequest(BaseModel):
title: Optional[str] = None
field: Optional[str] = None
priority_score: Optional[int] = None
status: Optional[str] = None
@router.get("/", response_model=List[dict])
async def get_topics(
status: Optional[str] = Query(None),
page: int = Query(1, ge=1),
size: int = Query(20, ge=1, le=100),
db: Session = Depends(get_db)
):
"""获取选题列表"""
query = db.query(Topic)
# 状态筛选
if status:
query = query.filter(Topic.status == status)
# 分页
offset = (page - 1) * size
topics = query.offset(offset).limit(size).all()
# 转换为字典格式
result = []
for topic in topics:
result.append({
"id": topic.id,
"title": topic.title,
"field": topic.field,
"priority_score": topic.priority_score,
"status": topic.status,
"compliance_score": topic.compliance_score,
"created_at": topic.created_at.isoformat() if topic.created_at else None,
"updated_at": topic.updated_at.isoformat() if topic.updated_at else None,
"generated_at": topic.generated_at.isoformat() if topic.generated_at else None,
"published_at": topic.published_at.isoformat() if topic.published_at else None,
"published_urls": topic.published_urls
})
return result
@router.post("/", response_model=dict)
async def create_topic(
topic_data: TopicCreateRequest,
current_user: User = Depends(get_current_admin_user),
db: Session = Depends(get_db)
):
"""创建新选题(管理员功能)"""
new_topic = Topic(
title=topic_data.title,
field=topic_data.field,
priority_score=topic_data.priority_score,
status="待处理"
)
db.add(new_topic)
db.commit()
db.refresh(new_topic)
# 记录审计日志
create_audit_log(
db=db,
user_id=current_user.id,
action="create_topic",
resource_type="topic",
resource_id=new_topic.id,
details=f"标题: {topic_data.title}"
)
return {
"id": new_topic.id,
"title": new_topic.title,
"status": new_topic.status,
"created_at": new_topic.created_at.isoformat() if new_topic.created_at else None
}
@router.put("/{topic_id}", response_model=dict)
async def update_topic(
topic_id: int,
topic_data: TopicUpdateRequest,
current_user: User = Depends(get_current_admin_user),
db: Session = Depends(get_db)
):
"""更新选题信息(管理员功能)"""
topic = db.query(Topic).filter(Topic.id == topic_id).first()
if not topic:
raise HTTPException(status_code=404, detail="选题不存在")
# 更新字段
if topic_data.title is not None:
topic.title = topic_data.title
if topic_data.field is not None:
topic.field = topic_data.field
if topic_data.priority_score is not None:
topic.priority_score = topic_data.priority_score
if topic_data.status is not None:
topic.status = topic_data.status
topic.updated_at = datetime.utcnow()
db.commit()
db.refresh(topic)
# 记录审计日志
create_audit_log(
db=db,
user_id=current_user.id,
action="update_topic",
resource_type="topic",
resource_id=topic_id,
details=f"状态更新为: {topic_data.status}"
)
return {
"id": topic.id,
"title": topic.title,
"status": topic.status,
"updated_at": topic.updated_at.isoformat() if topic.updated_at else None
}
@router.delete("/{topic_id}")
async def delete_topic(
topic_id: int,
current_user: User = Depends(get_current_admin_user),
db: Session = Depends(get_db)
):
"""删除选题(管理员功能)"""
topic = db.query(Topic).filter(Topic.id == topic_id).first()
if not topic:
raise HTTPException(status_code=404, detail="选题不存在")
db.delete(topic)
db.commit()
# 记录审计日志
create_audit_log(
db=db,
user_id=current_user.id,
action="delete_topic",
resource_type="topic",
resource_id=topic_id,
details="选题已删除"
)
return {"message": "选题已成功删除"}
@router.get("/{topic_id}", response_model=dict)
async def get_topic(
topic_id: int,
db: Session = Depends(get_db)
):
"""获取单个选题详情"""
topic = db.query(Topic).filter(Topic.id == topic_id).first()
if not topic:
raise HTTPException(status_code=404, detail="选题不存在")
return {
"id": topic.id,
"title": topic.title,
"field": topic.field,
"priority_score": topic.priority_score,
"status": topic.status,
"compliance_score": topic.compliance_score,
"created_at": topic.created_at.isoformat() if topic.created_at else None,
"updated_at": topic.updated_at.isoformat() if topic.updated_at else None,
"generated_at": topic.generated_at.isoformat() if topic.generated_at else None,
"published_at": topic.published_at.isoformat() if topic.published_at else None,
"published_urls": topic.published_urls
}