Files
aiagent/backend/app/api/teams.py
2026-06-18 07:47:03 +08:00

366 lines
11 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Team API — 虚拟团队管理 + 项目执行
"""
from fastapi import APIRouter, Depends, Query, HTTPException
from fastapi.responses import StreamingResponse
from sqlalchemy.orm import Session
from pydantic import BaseModel, Field
from typing import List, Optional, Dict, Any
import json
import logging
from app.core.database import get_db
from app.api.auth import get_current_user
from app.models.user import User
from app.services.team_service import TeamService, get_preset_roles
from app.services.team_orchestrator import TeamOrchestrator
logger = logging.getLogger(__name__)
router = APIRouter(
prefix="/api/v1/teams",
tags=["teams"],
responses={
401: {"description": "未授权"},
404: {"description": "资源不存在"},
400: {"description": "请求参数错误"},
},
)
# ──────────────────────────── Schemas ────────────────────────────
class TeamCreate(BaseModel):
name: str
description: str = ""
workspace_id: Optional[str] = None
config: Optional[Dict[str, Any]] = None
class TeamUpdate(BaseModel):
name: Optional[str] = None
description: Optional[str] = None
config: Optional[Dict[str, Any]] = None
status: Optional[str] = None
class MemberAdd(BaseModel):
agent_id: str
role: str
position: int = 0
is_lead: bool = False
class ProjectExecute(BaseModel):
project_description: str = Field(..., min_length=1, description="项目描述")
auto_approve_files: bool = Field(default=True, description="是否自动批准文件写入(跳过确认弹窗)")
# ──────────────────────────── Endpoints ────────────────────────────
@router.get("/preset-roles")
def preset_roles():
"""获取预置角色定义PM/设计师/开发/测试/DevOps"""
return {"roles": get_preset_roles()}
@router.post("")
def create_team(
body: TeamCreate,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""创建团队。"""
svc = TeamService(db)
team = svc.create_team(
name=body.name,
user_id=current_user.id,
description=body.description,
workspace_id=body.workspace_id,
config=body.config,
)
return {"data": team.to_dict()}
@router.post("/template/software-company")
def create_software_company_template(
workspace_id: Optional[str] = Query(None),
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""一键创建「软件公司虚拟团队」模板。
自动创建 5 个角色 AgentPM/设计师/开发/测试/DevOps并组建团队。
若用户已有同名 Agent 则复用。
"""
svc = TeamService(db)
result = svc.create_software_company_template(
user_id=current_user.id,
workspace_id=workspace_id,
)
return {"data": result}
@router.post("/template/education-training")
def create_education_training_template(
workspace_id: Optional[str] = Query(None),
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""一键创建「教育培训团队」模板。
自动创建 4 个角色 Agent课程设计/主讲讲师/作业助教/教务管理)并组建团队。
若用户已有同名 Agent 则复用。
"""
svc = TeamService(db)
result = svc.create_education_training_template(
user_id=current_user.id,
workspace_id=workspace_id,
)
return {"data": result}
@router.post("/template/platform-engineering")
def create_platform_engineering_template(
workspace_id: Optional[str] = Query(None),
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""一键创建「天工平台工程团队」模板。
自动创建 5 个角色 Agent全栈开发/前端体验/平台运维/质量保障/产品负责人)并组建团队。
专用于天工智能体平台的自我维护与迭代升级。
若用户已有同名 Agent 则复用。
"""
svc = TeamService(db)
result = svc.create_platform_engineering_template(
user_id=current_user.id,
workspace_id=workspace_id,
)
return {"data": result}
@router.post("/template/tech-documentation")
def create_tech_doc_template(
workspace_id: Optional[str] = Query(None),
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""一键创建「技术文档团队」模板。
自动创建 5 个角色 Agent文档架构师/技术写手/API文档专员/翻译审校/发布管理)并组建团队。
专用于软件项目的技术文档体系建设。
若用户已有同名 Agent 则复用。
"""
svc = TeamService(db)
result = svc.create_tech_doc_template(
user_id=current_user.id,
workspace_id=workspace_id,
)
return {"data": result}
@router.post("/template/health-management")
def create_health_management_template(
workspace_id: Optional[str] = Query(None),
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""一键创建「健康管理团队」模板。"""
svc = TeamService(db)
result = svc.create_health_management_template(
user_id=current_user.id, workspace_id=workspace_id,
)
return {"data": result}
@router.post("/template/medical-consultation")
def create_medical_consultation_template(
workspace_id: Optional[str] = Query(None),
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""一键创建「医疗咨询团队」模板。"""
svc = TeamService(db)
result = svc.create_medical_consultation_template(
user_id=current_user.id, workspace_id=workspace_id,
)
return {"data": result}
@router.post("/template/user-simulation-test")
def create_user_simulation_test_template(
workspace_id: Optional[str] = Query(None),
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""一键创建「系统应用测试团队」模板。
自动创建 5 个角色 Agent测试规划师/功能测试员/体验审核员/边界探索员/性能评估员)并组建团队。
若用户已有同名 Agent 则复用。
"""
svc = TeamService(db)
result = svc.create_user_simulation_test_template(
user_id=current_user.id, workspace_id=workspace_id,
)
return {"data": result}
@router.get("")
def list_teams(
workspace_id: Optional[str] = Query(None),
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""列出当前用户/工作区的所有团队。"""
svc = TeamService(db)
teams = svc.list_teams(
user_id=current_user.id,
workspace_id=workspace_id,
)
return {"data": [t.to_dict() for t in teams]}
@router.get("/{team_id}")
def get_team(
team_id: str,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""获取团队详情(含成员列表)。"""
svc = TeamService(db)
result = svc.get_team_with_members(team_id)
if not result:
raise HTTPException(status_code=404, detail="团队不存在")
return {"data": result}
@router.put("/{team_id}")
def update_team(
team_id: str,
body: TeamUpdate,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""更新团队信息。"""
svc = TeamService(db)
team = svc.update_team(team_id, **body.model_dump(exclude_none=True))
if not team:
raise HTTPException(status_code=404, detail="团队不存在")
return {"data": team.to_dict()}
@router.delete("/{team_id}")
def delete_team(
team_id: str,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""删除团队及其所有成员。"""
svc = TeamService(db)
ok = svc.delete_team(team_id)
if not ok:
raise HTTPException(status_code=404, detail="团队不存在")
return {"message": "团队已删除"}
@router.post("/{team_id}/members")
def add_member(
team_id: str,
body: MemberAdd,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""为团队添加/替换角色成员。"""
svc = TeamService(db)
member = svc.add_member(
team_id=team_id,
agent_id=body.agent_id,
role=body.role,
position=body.position,
is_lead=body.is_lead,
)
if not member:
raise HTTPException(status_code=404, detail="团队不存在")
return {"data": member.to_dict()}
@router.delete("/{team_id}/members/{member_id}")
def remove_member(
team_id: str,
member_id: str,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""从团队移除成员。"""
svc = TeamService(db)
ok = svc.remove_member(team_id, member_id)
if not ok:
raise HTTPException(status_code=404, detail="成员不存在")
return {"message": "成员已移除"}
@router.get("/{team_id}/members")
def list_members(
team_id: str,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""列出团队成员。"""
svc = TeamService(db)
members = svc.get_members(team_id)
return {"data": [m.to_dict() for m in members]}
@router.post("/{team_id}/execute")
async def execute_project(
team_id: str,
body: ProjectExecute,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""执行团队项目(非流式)。
PM 先分解计划各角色按阶段顺序执行QA 最终审查。
"""
orch = TeamOrchestrator(db, team_id, current_user.id)
try:
result = await orch.execute(body.project_description, auto_approve_files=body.auto_approve_files)
return {"data": result}
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
except Exception as e:
logger.error("团队执行失败: %s", e, exc_info=True)
raise HTTPException(status_code=500, detail=f"团队执行失败: {e}")
@router.post("/{team_id}/execute/stream")
async def execute_project_stream(
team_id: str,
body: ProjectExecute,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""执行团队项目SSE 流式)。"""
async def event_generator():
orch = TeamOrchestrator(db, team_id, current_user.id)
try:
async for event in orch.execute_stream(body.project_description, auto_approve_files=body.auto_approve_files):
yield f"data: {json.dumps(event, ensure_ascii=False)}\n\n"
except ValueError as e:
yield f"data: {json.dumps({'type': 'error', 'content': str(e)}, ensure_ascii=False)}\n\n"
except Exception as e:
logger.error("团队流式执行失败: %s", e, exc_info=True)
yield f"data: {json.dumps({'type': 'error', 'content': str(e)}, ensure_ascii=False)}\n\n"
return StreamingResponse(
event_generator(),
media_type="text/event-stream",
headers={
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"X-Accel-Buffering": "no",
},
)