feat(tasks): 阶段A后端核心 —— 多模式任务字段 + 接单资格判定 + 各模式限额守卫 + 公开广场/详情can_accept

- Task 增扩展列:publisher_role/visibility/c_visible/park_public/assign_type/assign_park_id/assign_opc_id/park_id/park_released/bid_quota/win_quota/eligibility(JSON)/settle_mode(默认operator_escrow)
- TaskRepository._to_dict/create/update 同步新字段;迁移 0020_task_multi_mode
- TaskService.can_accept(task,user):仅被指派/园区专属/指派园区未发单 + eligibility(证书/地域/性别/工时/领域/专长/案例) 判定,返回 ok/reasons
- 抢单(grab)与投标(bid)接入 can_accept 守卫;bid 加报名上限与重复报名校验
- /opc/tasks 公开广场改为 published+c_visible;/opc/tasks/{id} 详情同;登录后附带 can_accept/accept_reasons(经 optional_current_user)
This commit is contained in:
Pine
2026-08-27 19:05:18 +08:00
parent 11598abcc1
commit 076f96af74
5 changed files with 198 additions and 14 deletions
+22 -5
View File
@@ -9,7 +9,7 @@ from __future__ import annotations
from fastapi import APIRouter, Depends, HTTPException, Request
from pydantic import BaseModel
from ..dependencies import get_db
from ..dependencies import get_db, optional_current_user
from ..schemas.opc import BidRequest, ProfileUpdate, FinanceRecordCreate, TaskClaimRequest, TaskAssignRequest, TaskRecommendRequest, TaskSelectRecommendRequest
from ...rbac import require_roles, write_audit
from ...infrastructure.repositories import Database, new_id, utcnow_iso
@@ -58,10 +58,20 @@ async def opc_agent(
@router.get("/tasks", summary="任务广场(已发布平台任务,公开可见)")
async def opc_task_square(
db: Database = Depends(get_db),
user: dict | None = Depends(optional_current_user),
):
"""任务广场公开可见:返回已发布(published)任务,按展示优先级降序。查看无需登录。"""
items = await db.tasks.list_published()
"""任务广场公开可见:返回「待接单 published + C端可见(c_visible)」任务,按展示优先级降序。
查看无需登录;登录后对每个任务标注 can_accept / accept_reasons(接单资格判定)。"""
from ...services.task_service import TaskService
svc = TaskService(db)
items = [t for t in await db.tasks.list_published() if t.get("status") == "published" and t.get("c_visible")]
items.sort(key=lambda t: -(t.get("display_priority") or 0))
if user:
for it in items:
acc = await svc.can_accept(it, user)
it["can_accept"] = acc["ok"]
it["accept_reasons"] = acc["reasons"]
return {"items": items}
@@ -69,11 +79,18 @@ async def opc_task_square(
async def opc_task_detail(
task_id: str,
db: Database = Depends(get_db),
user: dict | None = Depends(optional_current_user),
):
"""任务详情,公开可见:仅返回已发布任务;否则 404。"""
"""任务详情,公开可见:仅返回已发布 + C端可见任务;否则 404。登录后附 can_accept。"""
from ...services.task_service import TaskService
item = await db.tasks.get(task_id)
if item is None or item.get("status") != "published":
if item is None or item.get("status") != "published" or not item.get("c_visible"):
raise HTTPException(status_code=404, detail="任务不存在")
if user:
acc = await TaskService(db).can_accept(item, user)
item["can_accept"] = acc["ok"]
item["accept_reasons"] = acc["reasons"]
return item