139 lines
5.6 KiB
Python
139 lines
5.6 KiB
Python
"""审核相关 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()
|