Files
opsxsql/backend/app/api/v1/endpoints/workflow.py
T
logan 4c9427e546 sync: 同步 sql-platform-pro 最新代码 — 权限体系安全修复 v0.8.1
- P0: 实例访问控制全线接入权限组 (829测试)
- P1: permissions字段激活 + 操作级权限检查 (843测试)
- 前端: 删除数据权限页面, 权限组合并为唯一入口
- 新增: CodeMirror编辑器 + 查询模板 + 慢查询分析
- 新增: MongoDB管理页面 + Dashboard增强
- 修复: Pydantic V2迁移 + 前端TS错误修复
2026-05-23 14:41:06 +08:00

245 lines
7.7 KiB
Python

"""审批流程管理 API。"""
from typing import List, Optional
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload
from app.core.database import get_db
from app.core.security import require_role
from app.models.user import User
from app.models.workflow import WorkflowDefinition, WorkflowStep
from app.schemas.workflow import (
WorkflowCreate, WorkflowUpdate, WorkflowResponse,
WorkflowStepCreate,
)
router = APIRouter(prefix="/workflows", tags=["审批流程"])
def _serialize_step(step: WorkflowStep) -> dict:
return {
"id": step.id,
"workflow_id": step.workflow_id,
"step_number": step.step_number,
"name": step.name,
"step_type": step.step_type,
"approver_role": step.approver_role,
"approver_ids": step.approver_ids,
"is_required": step.is_required,
"timeout_hours": step.timeout_hours,
"auto_approve_on_timeout": step.auto_approve_on_timeout,
}
def _serialize_workflow(wf: WorkflowDefinition) -> dict:
return {
"id": wf.id,
"name": wf.name,
"description": wf.description,
"is_default": wf.is_default,
"is_active": wf.is_active,
"created_at": wf.created_at,
"steps": [_serialize_step(s) for s in wf.steps],
}
@router.get("", response_model=List[WorkflowResponse])
async def list_workflows(
db: AsyncSession = Depends(get_db),
current_user: User = Depends(require_role("admin", "dba")),
):
result = await db.execute(
select(WorkflowDefinition)
.where(WorkflowDefinition.is_active == True)
.options(selectinload(WorkflowDefinition.steps))
)
workflows = result.scalars().unique().all()
return [_serialize_workflow(wf) for wf in workflows]
@router.post("", response_model=WorkflowResponse, status_code=201)
async def create_workflow(
data: WorkflowCreate,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(require_role("admin")),
):
# 如果设为默认,取消其他默认
if data.is_default:
from sqlalchemy import update as sa_update
await db.execute(
sa_update(WorkflowDefinition)
.where(WorkflowDefinition.is_default == True)
.values(is_default=False)
)
workflow = WorkflowDefinition(
name=data.name,
description=data.description,
is_default=data.is_default,
)
db.add(workflow)
await db.flush()
for step_data in data.steps:
step = WorkflowStep(
workflow_id=workflow.id,
step_number=step_data.step_number,
name=step_data.name,
step_type=step_data.step_type,
approver_role=step_data.approver_role,
approver_ids=step_data.approver_ids,
is_required=step_data.is_required,
timeout_hours=step_data.timeout_hours,
auto_approve_on_timeout=step_data.auto_approve_on_timeout,
)
db.add(step)
await db.commit()
# Reload with steps
result = await db.execute(
select(WorkflowDefinition)
.where(WorkflowDefinition.id == workflow.id)
.options(selectinload(WorkflowDefinition.steps))
)
workflow = result.scalar_one()
return _serialize_workflow(workflow)
@router.get("/{workflow_id}", response_model=WorkflowResponse)
async def get_workflow(
workflow_id: int,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(require_role("admin", "dba")),
):
result = await db.execute(
select(WorkflowDefinition)
.where(WorkflowDefinition.id == workflow_id)
.options(selectinload(WorkflowDefinition.steps))
)
workflow = result.scalar_one_or_none()
if not workflow:
raise HTTPException(status_code=404, detail="审批流程不存在")
return _serialize_workflow(workflow)
@router.put("/{workflow_id}", response_model=WorkflowResponse)
async def update_workflow(
workflow_id: int,
data: WorkflowUpdate,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(require_role("admin")),
):
result = await db.execute(
select(WorkflowDefinition)
.where(WorkflowDefinition.id == workflow_id)
.options(selectinload(WorkflowDefinition.steps))
)
workflow = result.scalar_one_or_none()
if not workflow:
raise HTTPException(status_code=404, detail="审批流程不存在")
if data.name is not None:
workflow.name = data.name
if data.description is not None:
workflow.description = data.description
if data.is_active is not None:
workflow.is_active = data.is_active
await db.commit()
await db.refresh(workflow)
return _serialize_workflow(workflow)
@router.delete("/{workflow_id}")
async def delete_workflow(
workflow_id: int,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(require_role("admin")),
):
result = await db.execute(
select(WorkflowDefinition).where(WorkflowDefinition.id == workflow_id)
)
workflow = result.scalar_one_or_none()
if not workflow:
raise HTTPException(status_code=404, detail="审批流程不存在")
workflow.is_active = False
await db.commit()
return {"message": "审批流程已删除"}
@router.post("/{workflow_id}/set-default")
async def set_default_workflow(
workflow_id: int,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(require_role("admin")),
):
"""设为默认审批流程。"""
# 取消所有默认
result = await db.execute(select(WorkflowDefinition))
all_workflows = result.scalars().all()
for wf in all_workflows:
wf.is_default = False
# 设置指定流程为默认
result = await db.execute(
select(WorkflowDefinition).where(WorkflowDefinition.id == workflow_id)
)
workflow = result.scalar_one_or_none()
if not workflow:
raise HTTPException(status_code=404, detail="审批流程不存在")
workflow.is_default = True
await db.commit()
return {"message": "审批流程 '{}' 已设为默认".format(workflow.name)}
@router.post("/{workflow_id}/clone", response_model=WorkflowResponse)
async def clone_workflow(
workflow_id: int,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(require_role("admin")),
):
"""克隆审批流程。"""
result = await db.execute(
select(WorkflowDefinition)
.where(WorkflowDefinition.id == workflow_id)
.options(selectinload(WorkflowDefinition.steps))
)
source = result.scalar_one_or_none()
if not source:
raise HTTPException(status_code=404, detail="审批流程不存在")
new_workflow = WorkflowDefinition(
name="{} (副本)".format(source.name),
description=source.description,
is_default=False,
)
db.add(new_workflow)
await db.flush()
for step in source.steps:
new_step = WorkflowStep(
workflow_id=new_workflow.id,
step_number=step.step_number,
name=step.name,
step_type=step.step_type,
approver_role=step.approver_role,
approver_ids=step.approver_ids,
is_required=step.is_required,
timeout_hours=step.timeout_hours,
auto_approve_on_timeout=step.auto_approve_on_timeout,
)
db.add(new_step)
await db.commit()
# Reload with steps
result = await db.execute(
select(WorkflowDefinition)
.where(WorkflowDefinition.id == new_workflow.id)
.options(selectinload(WorkflowDefinition.steps))
)
new_workflow = result.scalar_one()
return _serialize_workflow(new_workflow)