feat: AI Worker 平台 MVP v1.0.0 - 多租户/项目管理/AI Worker/任务编排/HITL审核/成本治理

This commit is contained in:
2026-08-12 13:31:02 +08:00
parent 7caa52e64f
commit 05a97f30be
+114
View File
@@ -0,0 +1,114 @@
"""Project service: CRUD + lifecycle management."""
from sqlalchemy.orm import Session
from typing import List, Optional
from app.models.project import Project
from app.models.task import Task
from app.models.cost import CostLog
from app.models.artifact import Artifact
from app.models.review import Review
from app.schemas.project import ProjectCreate, ProjectUpdate, ProjectStats
class ProjectService:
@staticmethod
def list_projects(
db: Session,
tenant_id: int,
status: Optional[str] = None,
skip: int = 0,
limit: int = 100,
) -> List[Project]:
q = db.query(Project).filter(Project.tenant_id == tenant_id)
if status:
q = q.filter(Project.status == status)
return q.order_by(Project.created_at.desc()).offset(skip).limit(limit).all()
@staticmethod
def get_project(db: Session, tenant_id: int, project_id: int) -> Optional[Project]:
return (
db.query(Project)
.filter(Project.id == project_id, Project.tenant_id == tenant_id)
.first()
)
@staticmethod
def create_project(
db: Session, tenant_id: int, owner_id: int, req: ProjectCreate
) -> Project:
project = Project(
tenant_id=tenant_id,
owner_id=owner_id,
name=req.name,
description=req.description,
start_date=req.start_date,
end_date=req.end_date,
budget_limit_cents=req.budget_limit_cents,
acceptance_criteria=req.acceptance_criteria,
tags=req.tags,
status="draft",
)
db.add(project)
db.commit()
db.refresh(project)
return project
@staticmethod
def update_project(
db: Session, tenant_id: int, project_id: int, req: ProjectUpdate
) -> Project:
project = ProjectService.get_project(db, tenant_id, project_id)
if not project:
raise ValueError(f"Project #{project_id} not found")
for field, value in req.model_dump(exclude_unset=True).items():
setattr(project, field, value)
db.commit()
db.refresh(project)
return project
@staticmethod
def delete_project(db: Session, tenant_id: int, project_id: int) -> bool:
project = ProjectService.get_project(db, tenant_id, project_id)
if not project:
return False
db.delete(project)
db.commit()
return True
@staticmethod
def get_stats(db: Session, tenant_id: int, project_id: int) -> ProjectStats:
tasks = (
db.query(Task)
.filter(Task.project_id == project_id, Task.tenant_id == tenant_id)
.all()
)
cost_logs = (
db.query(CostLog)
.filter(CostLog.project_id == project_id, CostLog.tenant_id == tenant_id)
.all()
)
artifacts = (
db.query(Artifact)
.filter(Artifact.project_id == project_id, Artifact.tenant_id == tenant_id)
.count()
)
pending_reviews = (
db.query(Review)
.filter(
Review.project_id == project_id,
Review.tenant_id == tenant_id,
Review.status == "pending",
)
.count()
)
total_cost = sum(c.cost_cents for c in cost_logs)
return ProjectStats(
total_tasks=len(tasks),
pending_tasks=sum(1 for t in tasks if t.status == "pending"),
in_progress_tasks=sum(1 for t in tasks if t.status == "in_progress"),
review_tasks=sum(1 for t in tasks if t.status == "review"),
done_tasks=sum(1 for t in tasks if t.status == "done"),
total_cost_cents=total_cost,
total_artifacts=artifacts,
pending_reviews=pending_reviews,
)