"""审核相关 activity。 这里的函数都运行在 worker 侧,可以安全地做数据库 I/O。 workflow 本身只负责调用这些 activity,不直接写数据库。 """ from sqlalchemy import select from temporalio import activity from app.domain.enums import OrderStatus, ReviewDecision, ReviewTaskStatus, StepStatus, WorkflowStepName from app.infra.db.models.review_task import ReviewTaskORM from app.infra.db.models.workflow_step import WorkflowStepORM from app.infra.db.session import get_session_factory from app.workers.activities.tryon_activities import jsonable, load_order_and_run, utc_now from app.workers.workflows.types import ( ReviewResolutionActivityInput, ReviewWaitActivityInput, WorkflowFailureActivityInput, ) @activity.defn async def mark_waiting_for_review_activity(payload: ReviewWaitActivityInput) -> None: """把 workflow 标记为等待人工审核。 这一步会做三件事: 1. 新增一条 review 类型的 workflow_step,状态是 waiting 2. 新增一条 review_task,供 API 查询待审核列表 3. 把订单和 workflow_run 都切到 waiting_review """ async with get_session_factory()() as session: order, workflow_run = await load_order_and_run(session, payload.order_id, payload.workflow_run_id) review_step = WorkflowStepORM( workflow_run_id=payload.workflow_run_id, step_name=WorkflowStepName.REVIEW, step_status=StepStatus.WAITING, input_json=jsonable(payload), started_at=utc_now(), ) session.add(review_step) session.add( ReviewTaskORM( order_id=payload.order_id, status=ReviewTaskStatus.PENDING, selected_asset_id=payload.candidate_asset_ids[0] if payload.candidate_asset_ids else None, comment=payload.comment, ) ) order.status = OrderStatus.WAITING_REVIEW workflow_run.status = OrderStatus.WAITING_REVIEW workflow_run.current_step = WorkflowStepName.REVIEW await session.commit() @activity.defn async def complete_review_wait_activity(payload: ReviewResolutionActivityInput) -> None: """收口当前这次 waiting_review。 这里的职责不是决定后续怎么跑,而是把“等待审核”这个数据库状态结束掉: - approve / rerun -> review step 记为 succeeded - reject -> review step 记为 failed """ async with get_session_factory()() as session: order, workflow_run = await load_order_and_run(session, payload.order_id, payload.workflow_run_id) step_result = await session.execute( select(WorkflowStepORM) .where( WorkflowStepORM.workflow_run_id == payload.workflow_run_id, WorkflowStepORM.step_name == WorkflowStepName.REVIEW, WorkflowStepORM.step_status == StepStatus.WAITING, ) .order_by(WorkflowStepORM.started_at.desc(), WorkflowStepORM.id.desc()) ) review_step = step_result.scalars().first() if review_step is not None: # 只处理仍处于 waiting 的那条 review_step, # 避免重复 signal 把历史 review 记录覆盖掉。 review_step.step_status = ( StepStatus.FAILED if payload.decision == ReviewDecision.REJECT else StepStatus.SUCCEEDED ) review_step.output_json = jsonable(payload) review_step.error_message = payload.comment if payload.decision == ReviewDecision.REJECT else None review_step.ended_at = utc_now() if payload.decision == ReviewDecision.REJECT: order.status = OrderStatus.FAILED workflow_run.status = OrderStatus.FAILED else: order.status = OrderStatus.RUNNING workflow_run.status = OrderStatus.RUNNING workflow_run.current_step = WorkflowStepName.REVIEW await session.commit() @activity.defn async def mark_workflow_failed_activity(payload: WorkflowFailureActivityInput) -> None: """把订单和 workflow_run 持久化为失败。 这个 activity 是 workflow 的“兜底收尾器”: 当任意步骤抛异常时,workflow 调它把数据库状态补完整。 """ async with get_session_factory()() as session: order, workflow_run = await load_order_and_run(session, payload.order_id, payload.workflow_run_id) step_result = await session.execute( select(WorkflowStepORM) .where( WorkflowStepORM.workflow_run_id == payload.workflow_run_id, WorkflowStepORM.step_name == payload.current_step, ) .order_by(WorkflowStepORM.started_at.desc(), WorkflowStepORM.id.desc()) ) workflow_step = step_result.scalars().first() if workflow_step is None: workflow_step = WorkflowStepORM( workflow_run_id=payload.workflow_run_id, step_name=payload.current_step, step_status=StepStatus.FAILED, input_json=jsonable(payload), started_at=utc_now(), ) session.add(workflow_step) workflow_step.step_status = StepStatus.FAILED workflow_step.error_message = payload.message workflow_step.output_json = jsonable({"message": payload.message, "status": payload.status.value}) workflow_step.ended_at = workflow_step.ended_at or utc_now() order.status = payload.status workflow_run.status = payload.status workflow_run.current_step = payload.current_step await session.commit()