feat: add async worker queues and retry controls
This commit is contained in:
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user