# -*- coding: utf-8 -*- """ 小程序任务路由 —— 任务列表、任务详情、置顶、放弃、取消放弃。 端点清单: - GET /api/xcx/tasks — 获取任务列表 + 绩效概览(TASK-1) - GET /api/xcx/tasks/by-member/{member_id} — 按会员查询最高优先级 active 任务详情 - GET /api/xcx/tasks/{task_id} — 获取任务详情完整版(TASK-2) - POST /api/xcx/tasks/{id}/pin — 置顶任务 - POST /api/xcx/tasks/{id}/unpin — 取消置顶 - POST /api/xcx/tasks/{id}/abandon — 放弃任务 - POST /api/xcx/tasks/{id}/restore — 恢复任务 所有端点均需 JWT(approved 状态)。 回访任务通过提交备注自动完成(note_service),不提供手动完成接口。 """ from __future__ import annotations from fastapi import APIRouter, Depends, Query from app.auth.dependencies import CurrentUser from app.middleware.permission import require_approved, require_permission from app.schemas.xcx_tasks import ( AbandonRequest, TaskDetailResponse, TaskListResponse, ) from app.services import task_manager from app.trace.decorators import trace_service router = APIRouter(prefix="/api/xcx/tasks", tags=["小程序任务"]) @router.get("", response_model=TaskListResponse) @trace_service("获取任务列表", "Get task list") async def get_tasks( status: str = Query("pending", pattern="^(pending|completed|abandoned)$"), page: int = Query(1, ge=1), page_size: int = Query(20, ge=1, le=200), # CHANGE 2026-03-27 | 权限改造 W4:统一权限保护 user: CurrentUser = Depends(require_permission("view_tasks")), ): """获取任务列表 + 绩效概览。""" return await task_manager.get_task_list_v2( user.user_id, user.site_id, status, page, page_size ) @router.get("/by-member/{member_id}", response_model=TaskDetailResponse) @trace_service("按会员查询任务详情", "Get task detail by member") async def get_task_by_member( member_id: int, user: CurrentUser = Depends(require_permission("view_tasks")), ): """按 member_id 查询当前助教的最高优先级 active 任务详情。""" return await task_manager.get_task_by_member( member_id, user.user_id, user.site_id ) @router.get("/{task_id}", response_model=TaskDetailResponse) @trace_service("获取任务详情", "Get task detail") async def get_task_detail( task_id: int, user: CurrentUser = Depends(require_permission("view_tasks")), ): """获取任务详情完整版。""" return await task_manager.get_task_detail( task_id, user.user_id, user.site_id ) @router.post("/{task_id}/pin") @trace_service("置顶任务", "Pin task") async def pin_task( task_id: int, user: CurrentUser = Depends(require_permission("view_tasks")), ): """置顶任务。""" result = await task_manager.pin_task(task_id, user.user_id, user.site_id) return {"is_pinned": result["is_pinned"]} @router.post("/{task_id}/unpin") @trace_service("取消置顶", "Unpin task") async def unpin_task( task_id: int, user: CurrentUser = Depends(require_permission("view_tasks")), ): """取消置顶。""" result = await task_manager.unpin_task(task_id, user.user_id, user.site_id) return {"is_pinned": result["is_pinned"]} @router.post("/{task_id}/abandon") @trace_service("放弃任务", "Abandon task") async def abandon_task( task_id: int, body: AbandonRequest, user: CurrentUser = Depends(require_permission("view_tasks")), ): """放弃任务(需填写原因)。""" return await task_manager.abandon_task( task_id, user.user_id, user.site_id, body.reason ) @router.post("/{task_id}/restore") @trace_service("恢复任务", "Restore task") async def restore_task( task_id: int, user: CurrentUser = Depends(require_permission("view_tasks")), ): """取消放弃,恢复为活跃状态。""" return await task_manager.cancel_abandon(task_id, user.user_id, user.site_id)