From 068a9422f8077db4f422b652d43fe560bc34a6ff Mon Sep 17 00:00:00 2001 From: huangzhuang_3rd Date: Wed, 12 Aug 2026 13:31:04 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20AI=20Worker=20=E5=B9=B3=E5=8F=B0=20MVP?= =?UTF-8?q?=20v1.0.0=20-=20=E5=A4=9A=E7=A7=9F=E6=88=B7/=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E7=AE=A1=E7=90=86/AI=20Worker/=E4=BB=BB=E5=8A=A1=E7=BC=96?= =?UTF-8?q?=E6=8E=92/HITL=E5=AE=A1=E6=A0=B8/=E6=88=90=E6=9C=AC=E6=B2=BB?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/services/task_service.py | 116 +++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 backend/app/services/task_service.py diff --git a/backend/app/services/task_service.py b/backend/app/services/task_service.py new file mode 100644 index 0000000..09f9706 --- /dev/null +++ b/backend/app/services/task_service.py @@ -0,0 +1,116 @@ +"""Task service: CRUD + assignment + status management.""" +from sqlalchemy.orm import Session +from typing import List, Optional +from datetime import datetime +from app.models.task import Task +from app.schemas.task import TaskCreate, TaskUpdate, TaskAssign + + +class TaskService: + @staticmethod + def list_tasks( + db: Session, + tenant_id: int, + project_id: Optional[int] = None, + status: Optional[str] = None, + assignee_id: Optional[int] = None, + skip: int = 0, + limit: int = 100, + ) -> List[Task]: + q = db.query(Task).filter(Task.tenant_id == tenant_id) + if project_id: + q = q.filter(Task.project_id == project_id) + if status: + q = q.filter(Task.status == status) + if assignee_id: + q = q.filter(Task.assignee_id == assignee_id) + return q.order_by(Task.created_at.desc()).offset(skip).limit(limit).all() + + @staticmethod + def get_task(db: Session, tenant_id: int, task_id: int) -> Optional[Task]: + return ( + db.query(Task) + .filter(Task.id == task_id, Task.tenant_id == tenant_id) + .first() + ) + + @staticmethod + def create_task( + db: Session, tenant_id: int, project_id: int, req: TaskCreate + ) -> Task: + task = Task( + tenant_id=tenant_id, + project_id=project_id, + title=req.title, + description=req.description, + status="pending", + priority=req.priority, + task_type=req.task_type, + input_data=req.input_data, + requires_review=req.requires_review, + assignee_id=req.assignee_id, + worker_id=req.worker_id, + due_date=req.due_date, + depends_on=req.depends_on, + ) + db.add(task) + db.commit() + db.refresh(task) + return task + + @staticmethod + def update_task( + db: Session, tenant_id: int, task_id: int, req: TaskUpdate + ) -> Task: + task = TaskService.get_task(db, tenant_id, task_id) + if not task: + raise ValueError(f"Task #{task_id} not found") + for field, value in req.model_dump(exclude_unset=True).items(): + setattr(task, field, value) + db.commit() + db.refresh(task) + return task + + @staticmethod + def delete_task(db: Session, tenant_id: int, task_id: int) -> bool: + task = TaskService.get_task(db, tenant_id, task_id) + if not task: + return False + db.delete(task) + db.commit() + return True + + @staticmethod + def assign_task( + db: Session, tenant_id: int, task_id: int, req: TaskAssign + ) -> Task: + task = TaskService.get_task(db, tenant_id, task_id) + if not task: + raise ValueError(f"Task #{task_id} not found") + if req.assignee_id is not None: + task.assignee_id = req.assignee_id + if req.worker_id is not None: + task.worker_id = req.worker_id + if task.status == "pending": + task.status = "assigned" + db.commit() + db.refresh(task) + return task + + @staticmethod + def mark_status( + db: Session, tenant_id: int, task_id: int, status: str, output: str = "" + ) -> Task: + task = TaskService.get_task(db, tenant_id, task_id) + if not task: + raise ValueError(f"Task #{task_id} not found") + task.status = status + if output: + task.output_data = output + if status == "in_progress": + task.started_at = datetime.utcnow() + elif status in ("done", "review", "rejected", "cancelled"): + task.completed_at = datetime.utcnow() + db.commit() + db.refresh(task) + return task