# -*- coding: utf-8 -*- """业务层 · 任务状态机(抢单 / 投标 / 交付 / 中标 / 验收)。 业务层只处理业务逻辑;不感知 HTTP 之外的框架细节。依赖基础设施层 Repository(经 Database 门面),不反向依赖接口层。 """ from __future__ import annotations from fastapi import HTTPException from ..infrastructure.repositories import Database class TaskService: """统一任务状态流转:grab/bid/deliver/win/review。""" def __init__(self, db: Database): self.db = db async def grab(self, task_id: str, actor: dict) -> dict: """抢单(兼容旧入口):published + grab → claimed,记录接单流水。""" return await self.claim_by_id(task_id, actor, source="grab") async def claim(self, task_code: str, actor: dict, source: str = "scan") -> dict: """扫码/按短码接单:解析 task_code 后领单。""" task = await self.db.tasks.get_by_code(task_code) if task is None: raise HTTPException(status_code=404, detail="任务不存在") return await self.claim_by_id(task["id"], actor, source=source) async def claim_by_id(self, task_id: str, actor: dict, source: str) -> dict: """published + grab 模式 → claimed(claimed_by/claimed_at + TaskClaim 流水)。""" task = await self.db.tasks.get(task_id) if task is None: raise HTTPException(status_code=404, detail="任务不存在") if task["status"] != "published": raise HTTPException(status_code=400, detail="任务不可接单") if task["mode"] != "grab": raise HTTPException(status_code=400, detail="任务不支持扫码接单") updated = await self.db.tasks.claim(task_id, actor["id"]) await self.db.task_claims.create( task_id, actor["id"], actor.get("nickname") or actor.get("username", ""), source, ) return updated async def start_doing(self, task_id: str, actor: dict) -> dict: """claimed → doing。""" task = await self.db.tasks.get(task_id) if task is None: raise HTTPException(status_code=404, detail="任务不存在") if task["status"] != "claimed": raise HTTPException(status_code=400, detail="任务未接单,无法开始") return await self.db.tasks.set_doing(task_id) async def complete(self, task_id: str, actor: dict) -> dict: """doing → completed。""" task = await self.db.tasks.get(task_id) if task is None: raise HTTPException(status_code=404, detail="任务不存在") if task["status"] != "doing": raise HTTPException(status_code=400, detail="任务未在进行中,无法完成") return await self.db.tasks.set_status(task_id, "completed") async def bid(self, task_id: str, actor: dict, quote: int, plan: str) -> dict: """投标:仅 published + bid 模式可投。""" task = await self.db.tasks.get(task_id) if task is None or task["status"] != "published" or task["mode"] != "bid": raise HTTPException(status_code=400, detail="任务不可投标") return await self.db.bids.create( task_id, actor["id"], actor.get("nickname") or actor["username"], quote, plan, ) async def deliver(self, task_id: str, actor: dict) -> dict: """交付:任务存在且处于进行中(in_progress)才可交付 → delivered。 补齐原实现「无任何状态预检」的缺陷,防随意交付。 """ task = await self.db.tasks.get(task_id) if task is None: raise HTTPException(status_code=404, detail="Task not found") if task["status"] != "in_progress": raise HTTPException(status_code=400, detail="任务未在进行中,无法交付") return await self.db.tasks.set_status(task_id, "delivered") async def enterprise_submit(self, task_id: str) -> dict: """企业提交审核:pending 任务 → published。""" return await self.db.tasks.set_status(task_id, "published") async def win_bid(self, task_id: str, bid_id: str) -> dict: """企业评标中标:bid → win,task → in_progress(双状态联动)。""" bid = await self.db.bids.get(bid_id) if bid is None or bid["task_id"] != task_id: raise HTTPException(status_code=404, detail="竞标不存在") await self.db.bids.set_status(bid_id, "win") return await self.db.tasks.set_status(task_id, "in_progress") async def review(self, task_id: str, accept: bool) -> dict: """企业验收:accept → completed;reject → in_progress。""" target = "completed" if accept else "in_progress" return await self.db.tasks.set_status(task_id, target)