feat(tasks): 阶段A剩余 —— 园区端/大屏任务 + 运营平台资金托管结算
- 大屏 /api/tasks 改为仅展示「当前园区可接」:本园专属(park_id)∪公有(park_public),状态published/claimed/doing
- 园区端 /api/park/tasks(本园发布+指派本园列表) /tasks/{id}/release(园区内发单→park_released+可见性) /tasks/{id}/assign(园区内指派→锁定被指派人)
- 运营托管结算(operator):/admin/escrows(台账) /admin/tasks/{id}/escrow(托管建档/充值,佣金5%) /admin/tasks/{id}/release(验收结算,划佣金+余额代付OPC),全部挂运营平台
This commit is contained in:
@@ -11,7 +11,7 @@ from fastapi import APIRouter, Depends, File, 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, ContentUpdateRequest, 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, ContentReviewRequest, 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
|
||||
@@ -20,6 +20,61 @@ from ...services import training_admin_bridge as training
|
||||
router = APIRouter(prefix="/admin", tags=["admin-op"])
|
||||
|
||||
|
||||
class EscrowDepositRequest(BaseModel):
|
||||
amount: int = 0
|
||||
|
||||
|
||||
@router.get("/escrows", summary="运营结算台账(资金托管)")
|
||||
async def operator_escrows(
|
||||
status: str | None = None,
|
||||
db: Database = Depends(get_db),
|
||||
_u: dict = Depends(require_roles("operator")),
|
||||
):
|
||||
"""运营平台资金托管台账:全部任务托管、佣金、状态(deposited/frozen/released/refunded)。"""
|
||||
return {"items": await db.escrows.list(status=status)}
|
||||
|
||||
|
||||
@router.post("/tasks/{task_id}/escrow", summary="任务托管建档/充值(运营平台托管)")
|
||||
async def operator_escrow_deposit(
|
||||
task_id: str,
|
||||
req: EscrowDepositRequest,
|
||||
request: Request,
|
||||
db: Database = Depends(get_db),
|
||||
_u: dict = Depends(require_roles("operator")),
|
||||
):
|
||||
"""发标方向运营平台托管账户建档/充值;已托管返回现有。金额默认取任务预算。"""
|
||||
from ...services.settlement_service import COMMISSION_RATE
|
||||
|
||||
task = await db.tasks.get(task_id)
|
||||
if task is None:
|
||||
raise HTTPException(status_code=404, detail="任务不存在")
|
||||
for e in await db.escrows.list():
|
||||
if e["task_id"] == task_id:
|
||||
return e
|
||||
amount = req.amount or max(task.get("budget_min", 0), task.get("budget_max", 0))
|
||||
commission = int(amount * COMMISSION_RATE)
|
||||
esc = await db.escrows.create(task_id, task["title"], amount, commission)
|
||||
await write_audit(db, action="escrow.deposit", resource="escrow", resource_id=esc["id"],
|
||||
detail=f"amount={amount}", user=_u, request=request)
|
||||
return esc
|
||||
|
||||
|
||||
@router.post("/tasks/{task_id}/release", summary="运营结算(验收通过,划佣金+代付 OPC)")
|
||||
async def operator_escrow_release(
|
||||
task_id: str,
|
||||
request: Request,
|
||||
db: Database = Depends(get_db),
|
||||
_u: dict = Depends(require_roles("operator")),
|
||||
):
|
||||
"""验收通过:任务须 completed;托管结算 released(平台佣金留存,余额代付 OPC)。"""
|
||||
from ...services.settlement_service import SettlementService
|
||||
|
||||
released = await SettlementService(db).release_escrow(task_id)
|
||||
await write_audit(db, action="escrow.release", resource="escrow", resource_id=released["id"],
|
||||
detail=f"amount={released.get('amount')}", user=_u, request=request)
|
||||
return released
|
||||
|
||||
|
||||
@router.get("/tasks", summary="任务列表")
|
||||
async def list_tasks(
|
||||
status: str | None = None,
|
||||
@@ -261,6 +316,30 @@ async def set_content_status(
|
||||
return item
|
||||
|
||||
|
||||
@router.post("/content/{content_id}/review", summary="资讯审核(通过/下架)")
|
||||
async def review_content(
|
||||
content_id: str,
|
||||
req: ContentReviewRequest,
|
||||
request: Request,
|
||||
db: Database = Depends(get_db),
|
||||
actor: dict = Depends(require_permission("action:content.manage")),
|
||||
):
|
||||
"""approve → 已发布 + 公开(C 端可见);offline → 已下线。"""
|
||||
if req.decision == "approve":
|
||||
item = await db.content.approve(content_id)
|
||||
action = "content.approve"
|
||||
elif req.decision == "offline":
|
||||
item = await db.content.set_status(content_id, "offline")
|
||||
action = "content.offline"
|
||||
else:
|
||||
raise HTTPException(status_code=400, detail="无效审核操作")
|
||||
if item is None:
|
||||
raise HTTPException(status_code=404, detail="Content not found")
|
||||
await write_audit(db, action=action, resource="content", resource_id=content_id,
|
||||
detail=item.get("title", ""), user=actor, request=request)
|
||||
return item
|
||||
|
||||
|
||||
@router.put("/content/{content_id}", summary="编辑内容")
|
||||
async def update_content(
|
||||
content_id: str,
|
||||
|
||||
Reference in New Issue
Block a user