"""Temporal 工作流服务层。 这一层位于 API 和 Temporal 之间,负责: 1. 选择该启动哪个 workflow 2. 发送 signal 3. 查询已持久化的 workflow 状态 """ from datetime import timedelta from fastapi import HTTPException, status from sqlalchemy import select from sqlalchemy.orm import selectinload from app.api.schemas.workflow import WorkflowStatusResponse, WorkflowStepRead from app.domain.enums import ServiceMode from app.infra.db.models.workflow_run import WorkflowRunORM from app.infra.temporal.client import get_temporal_client from app.infra.temporal.task_queues import IMAGE_PIPELINE_CONTROL_TASK_QUEUE from app.workers.workflows.low_end_pipeline import LowEndPipelineWorkflow from app.workers.workflows.mid_end_pipeline import MidEndPipelineWorkflow from app.workers.workflows.types import PipelineWorkflowInput, ReviewSignalPayload class WorkflowService: """Temporal 编排服务。""" @staticmethod def workflow_type_for_mode(service_mode: ServiceMode) -> str: """根据服务模式返回对应的 workflow 类型名。""" if service_mode == ServiceMode.AUTO_BASIC: return LowEndPipelineWorkflow.__name__ return MidEndPipelineWorkflow.__name__ async def start_workflow(self, workflow_input: PipelineWorkflowInput) -> None: """为订单启动对应的 Temporal workflow。 这里做的只是“发起执行”: 真正的流水线顺序仍然在 workflow 类里定义。 """ client = await get_temporal_client() workflow_id = f"order-{workflow_input.order_id}" workflow_callable = ( LowEndPipelineWorkflow.run if workflow_input.service_mode == ServiceMode.AUTO_BASIC else MidEndPipelineWorkflow.run ) await client.start_workflow( workflow_callable, workflow_input, # workflow_id 固定为 order-{order_id},方便 API 后续按订单回查。 id=workflow_id, task_queue=IMAGE_PIPELINE_CONTROL_TASK_QUEUE, run_timeout=timedelta(minutes=30), task_timeout=timedelta(seconds=30), ) async def signal_review(self, workflow_id: str, payload: ReviewSignalPayload) -> None: """向运行中的 workflow 发送审核 signal。""" client = await get_temporal_client() handle = client.get_workflow_handle(workflow_id=workflow_id) # "submit_review" 对应 workflow 里用 @workflow.signal 标记的方法名。 await handle.signal("submit_review", payload) async def get_workflow_status(self, session, order_id: int) -> WorkflowStatusResponse: """返回订单对应的已持久化 workflow 状态。 这里查的是我们自己数据库里的状态镜像,不是直接去 Temporal history 现查。 这么做更适合业务 API 对外暴露。 """ result = await session.execute( select(WorkflowRunORM) .where(WorkflowRunORM.order_id == order_id) .options(selectinload(WorkflowRunORM.steps)) ) workflow_run = result.scalar_one_or_none() if workflow_run is None: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Workflow not found") return WorkflowStatusResponse( order_id=workflow_run.order_id, workflow_id=workflow_run.workflow_id, workflow_type=workflow_run.workflow_type, workflow_status=workflow_run.status, current_step=workflow_run.current_step, steps=[WorkflowStepRead.model_validate(step) for step in workflow_run.steps], created_at=workflow_run.created_at, updated_at=workflow_run.updated_at, )