sync: 同步 sql-platform-pro 最新代码 — 权限体系安全修复 v0.8.1
- P0: 实例访问控制全线接入权限组 (829测试) - P1: permissions字段激活 + 操作级权限检查 (843测试) - 前端: 删除数据权限页面, 权限组合并为唯一入口 - 新增: CodeMirror编辑器 + 查询模板 + 慢查询分析 - 新增: MongoDB管理页面 + Dashboard增强 - 修复: Pydantic V2迁移 + 前端TS错误修复
This commit is contained in:
1 parent
6fcd9465cf
commit
4c9427e546
105 files changed
+23680
-471
No files matched your search
@@ -0,0 +1,244 @@
|
||||
"""审批流程管理 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)
|
||||
Reference in new issue
Block a user