feat: add async worker queues and retry controls

This commit is contained in:
wren
2026-04-29 11:48:09 +08:00
parent e738398eb6
commit f3b83c9979
16 changed files with 1316 additions and 96 deletions
@@ -21,8 +21,10 @@ from fastapi_modules.fastapi_leaudit.domian.vo.auditVo import (
AuditRunErrorVO,
AuditRunVO,
)
from fastapi_modules.fastapi_leaudit.leaudit_bridge.fileSourceResolver import FileSourceResolver
from fastapi_modules.fastapi_leaudit.leaudit_bridge.tasks import dispatch_leaudit_task
from fastapi_modules.fastapi_leaudit.leaudit_bridge.tasks import (
dispatch_leaudit_task,
resolve_worker_queue,
)
from fastapi_modules.fastapi_leaudit.models import (
LeauditAuditRun,
LeauditDocument,
@@ -31,20 +33,67 @@ from fastapi_modules.fastapi_leaudit.models import (
from fastapi_modules.fastapi_leaudit.services import IAuditService
def _normalize_speed(speed: str | None) -> str:
"""Normalize front-end speed selection to urgent/normal."""
normalized = (speed or "").strip().lower()
if normalized in {"urgent", "high", "fast", "emergency", "紧急"}:
return "urgent"
return "normal"
class AuditServiceImpl(IAuditService):
"""评查服务实现。"""
async def Run(self, DocumentId: int, RuleType: str | None = None, Force: bool = False) -> AuditRunVO:
async def Run(
self,
DocumentId: int,
RuleType: str | None = None,
Force: bool = False,
Speed: str = "normal",
) -> AuditRunVO:
"""触发文档评查。
当前阶段同步触发 bridge 执行链,后续再切换为 Celery 异步分发
当前阶段只负责创建 run 并投递 worker,不在 HTTP 请求内同步执行
"""
async with GetAsyncSession() as session:
logger.info(f"触发评查: documentId={DocumentId}, ruleType={RuleType}")
normalizedSpeed = _normalize_speed(Speed)
document = await session.get(LeauditDocument, DocumentId)
if not document:
raise LeauditException(StatusCodeEnum.HTTP_404_NOT_FOUND, "评查文档不存在")
if not Force:
activeRunResult = await session.execute(
select(LeauditAuditRun)
.where(
LeauditAuditRun.documentId == DocumentId,
LeauditAuditRun.status.in_(("queued", "running", "retrying")),
)
.order_by(LeauditAuditRun.Id.desc())
.limit(1)
)
activeRun = activeRunResult.scalar_one_or_none()
if activeRun:
return AuditRunVO(
runId=activeRun.Id,
documentId=activeRun.documentId,
runNo=activeRun.runNo,
documentFileId=activeRun.documentFileId,
status=activeRun.status,
phase=activeRun.phase,
resultStatus=activeRun.resultStatus,
ruleSetId=activeRun.ruleSetId,
ruleVersionId=activeRun.ruleVersionId,
ruleTypeId=activeRun.ruleTypeId,
rescueApplied=activeRun.rescueApplied or False,
totalScore=float(activeRun.totalScore) if activeRun.totalScore else None,
passedCount=activeRun.passedCount,
failedCount=activeRun.failedCount,
skippedCount=activeRun.skippedCount,
startedAt=activeRun.startedAt,
finishedAt=activeRun.finishedAt,
)
fileResult = await session.execute(
select(LeauditDocumentFile)
.where(
@@ -91,49 +140,48 @@ class AuditServiceImpl(IAuditService):
if not binding or not binding["rule_set_id"] or not binding["rule_version_id"]:
raise LeauditException(StatusCodeEnum.HTTP_400_BAD_REQUEST, "当前文档类型未绑定可用规则版本")
triggerSource = f"{'retry' if Force else 'upload'}:{normalizedSpeed}"
run = LeauditAuditRun(
documentId=DocumentId,
documentFileId=documentFile.Id,
runNo=int(latestRunNo) + 1,
triggerSource="manual" if not Force else "retry",
status="pending",
triggerSource=triggerSource,
status="queued",
phase="dispatch",
ruleSetId=int(binding["rule_set_id"]),
ruleVersionId=int(binding["rule_version_id"]),
ruleTypeId=binding["rule_type_id"],
ruleSourceOssUrl=binding["rule_source_oss_url"],
ruleSourceSha256=binding["rule_source_sha256"],
startedAt=datetime.now(),
)
session.add(run)
await session.flush()
document.currentRunId = run.Id
document.processingStatus = "running"
document.processingStatus = "queued"
await session.commit()
await session.refresh(run)
try:
Resolver = FileSourceResolver()
Payload = await Resolver.ResolvePayload(documentFile)
taskId = dispatch_leaudit_task(
run_id=run.Id,
queue_name=resolve_worker_queue(triggerSource),
rules_path=RuleType,
)
except Exception as Error:
run.status = "failed"
run.phase = "dispatch"
run.finishedAt = datetime.now()
document.processingStatus = "failed"
await session.commit()
raise LeauditException(
StatusCodeEnum.HTTP_500_INTERNAL_SERVER_ERROR,
f"读取评查文件失败: {Error}",
f"投递评查任务失败: {Error}",
) from Error
dispatch_leaudit_task(
document_id=DocumentId,
file_content=Payload.fileContent,
filename=Payload.fileName,
upload_info={
"run_id": run.Id,
"rule_version_id": run.ruleVersionId,
"rule_source_oss_url": run.ruleSourceOssUrl,
"source_type": Payload.sourceType,
"source_path": Payload.sourcePath,
},
rules_path=RuleType,
)
run.taskId = taskId
await session.commit()
await session.refresh(run)
return AuditRunVO(