feat(content): 资讯增删改 + C端资讯中心(类别)端点

- ContentRepository:+update(部分字段)/get/delete
- schemas.operator:+ContentUpdateRequest(全可选)
- rbac_operator:+PUT/DELETE /admin/content/{id}(action:content.manage+审计)
- rbac_opc:+GET /opc/content?type=policy|news|skill|dynamic(仅published)、/opc/content/{id}(详情)
- 类别字典 policy/news/skill/dynamic

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Pine
2026-08-27 14:05:38 +08:00
parent 5840a5cb4c
commit 873a70a095
4 changed files with 90 additions and 1 deletions
+22
View File
@@ -87,6 +87,28 @@ async def opc_policy(
return {"items": await db.content.list(ctype="policy", status="published")}
@router.get("/content", summary="资讯中心(按类别,已发布)")
async def opc_content(
type: str = "news",
db: Database = Depends(get_db),
_u: dict = Depends(require_roles("opc_member")),
):
"""C 端资讯:type ∈ policy(政策)/news(资讯)/skill(技能)/dynamic(动态),仅 published。"""
return {"items": await db.content.list(ctype=type, status="published")}
@router.get("/content/{content_id}", summary="资讯详情")
async def opc_content_detail(
content_id: str,
db: Database = Depends(get_db),
_u: dict = Depends(require_roles("opc_member")),
):
item = await db.content.get(content_id)
if item is None or item.get("status") != "published":
raise HTTPException(status_code=404, detail="内容不存在")
return item
@router.get("/finance", summary="财务流水")
async def opc_finance(
db: Database = Depends(get_db),
+32 -1
View File
@@ -11,7 +11,7 @@ from fastapi import APIRouter, Depends, HTTPException, Request, Response
from pydantic import BaseModel
from ..dependencies import get_db
from ..schemas.operator import TaskCreateRequest, TaskUpdateRequest, TaskStatusRequest, TaskAssignRequest, TaskRecommendRequest, TaskSelectRecommendRequest, ProviderCreateRequest, ProviderUpdateRequest, ContentCreateRequest, ContentStatusRequest, ConfigUpdateRequest, ComputePingResponse, ComputeBalanceRequest, ComputeProvisionRequest, ComputeProvisionResponse, CourseCreateRequest, CourseStatusRequest, CourseUpdateRequest, ActivityCreateRequest, ActivityStatusRequest, ActivityUpdateRequest, BookingUpdateRequest, TestCreateRequest, TestStatusRequest
from ..schemas.operator import TaskCreateRequest, TaskUpdateRequest, TaskStatusRequest, TaskAssignRequest, TaskRecommendRequest, TaskSelectRecommendRequest, ProviderCreateRequest, ProviderUpdateRequest, ContentCreateRequest, ContentStatusRequest, ContentUpdateRequest, ConfigUpdateRequest, ComputePingResponse, ComputeBalanceRequest, ComputeProvisionRequest, ComputeProvisionResponse, CourseCreateRequest, CourseStatusRequest, CourseUpdateRequest, ActivityCreateRequest, ActivityStatusRequest, ActivityUpdateRequest, BookingUpdateRequest, TestCreateRequest, TestStatusRequest
from ...rbac import require_permission, require_roles, write_audit
from ...infrastructure.repositories import Database
from ...services import compute_client
@@ -231,6 +231,37 @@ async def set_content_status(
return item
@router.put("/content/{content_id}", summary="编辑内容")
async def update_content(
content_id: str,
req: ContentUpdateRequest,
request: Request,
db: Database = Depends(get_db),
actor: dict = Depends(require_permission("action:content.manage")),
):
item = await db.content.update(content_id, req.model_dump(exclude_unset=True, exclude_none=True))
if item is None:
raise HTTPException(status_code=404, detail="Content not found")
await write_audit(db, action="content.update", resource="content", resource_id=content_id,
detail=item.get("title", ""), user=actor, request=request)
return item
@router.delete("/content/{content_id}", summary="删除内容")
async def delete_content(
content_id: str,
request: Request,
db: Database = Depends(get_db),
actor: dict = Depends(require_permission("action:content.manage")),
):
ok = await db.content.delete(content_id)
if not ok:
raise HTTPException(status_code=404, detail="Content not found")
await write_audit(db, action="content.delete", resource="content", resource_id=content_id,
detail=content_id, user=actor, request=request)
return {"ok": True}
# ── 系统配置 ──────────────────────────────────────────────────────────────
@router.get("/config", summary="系统配置列表")
async def list_config(
+9
View File
@@ -94,6 +94,15 @@ class ContentStatusRequest(BaseModel):
status: str # published/offline/draft
class ContentUpdateRequest(BaseModel):
"""内容更新(字段全可选,仅更新传入项)。"""
type: str | None = None # policy/news/skill/dynamic
title: str | None = None
summary: str | None = None
body: str | None = None
status: str | None = None
class ConfigUpdateRequest(BaseModel):
value: str