feat: 业务层 services + 领域层 domain

- 领域层 domain/rules.py:RBAC 判定规则纯函数(role/permission/scope),接口层守卫改调领域规则
- 业务层 services/task_service.py:统一任务状态机(grab/bid/deliver/win/review),修复 deliver 无状态预检缺陷
- rbac_opc 抢单/投标/交付改走 TaskService
This commit is contained in:
2026-08-23 23:56:39 +08:00
parent 980a2db6d9
commit d754d6ca4e
6 changed files with 139 additions and 41 deletions
+2
View File
@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
"""业务层:业务逻辑编排与用例。只依赖领域层与基础设施层,不依赖接口层。"""
+64
View File
@@ -0,0 +1,64 @@
# -*- 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 模式可抢,抢后置 in_progress。"""
task = await self.db.tasks.get(task_id)
if task is None or task["status"] != "published" or task["mode"] != "grab":
raise HTTPException(status_code=400, detail="任务不可抢单")
return await self.db.tasks.set_status(task_id, "in_progress")
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 → wintask → 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 → completedreject → in_progress。"""
target = "completed" if accept else "in_progress"
return await self.db.tasks.set_status(task_id, target)