refactor(work-service,print-worker): 优化代码结构与错误处理

1. 提取评分prompt构建逻辑减少重复代码
2. 移除冗余的参考答案图片循环逻辑
3. 重构打印服务的错误回调逻辑,统一失败处理流程
4. 新增打印服务的任务取消处理逻辑
This commit is contained in:
glz
2026-07-08 17:07:41 +08:00
parent 195792ec66
commit de9a0c2978
3 changed files with 45 additions and 37 deletions

View File

@ -47,10 +47,11 @@ public class AutoDotCodeConsumer(
using var scope = scopeFactory.CreateScope(); using var scope = scopeFactory.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<ISqlSugarClient>(); var dbContext = scope.ServiceProvider.GetRequiredService<ISqlSugarClient>();
JournalPagePrintDto? request = null;
try try
{ {
var request = JsonSerializer.Deserialize<JournalPagePrintDto>(message, new JsonSerializerOptions request = JsonSerializer.Deserialize<JournalPagePrintDto>(message, new JsonSerializerOptions
{ {
PropertyNameCaseInsensitive = true PropertyNameCaseInsensitive = true
}); });
@ -72,6 +73,7 @@ public class AutoDotCodeConsumer(
if (string.IsNullOrWhiteSpace(journalPdfKey)) if (string.IsNullOrWhiteSpace(journalPdfKey))
{ {
logger.LogError("书籍上传 PDF 文件地址为空JournalId: {JournalId}", request.JournalId); logger.LogError("书籍上传 PDF 文件地址为空JournalId: {JournalId}", request.JournalId);
await TryCallbackCodeFailAsync(request, cancellationToken);
return; return;
} }
@ -79,6 +81,7 @@ public class AutoDotCodeConsumer(
if (pdfStream == null) if (pdfStream == null)
{ {
logger.LogError("获取书籍上传 PDF 文件失败OSS Key: {OssKey}", journalPdfKey); logger.LogError("获取书籍上传 PDF 文件失败OSS Key: {OssKey}", journalPdfKey);
await TryCallbackCodeFailAsync(request, cancellationToken);
return; return;
} }
@ -93,6 +96,7 @@ public class AutoDotCodeConsumer(
if (dotFile == null) if (dotFile == null)
{ {
logger.LogError("配置打印数据错误点阵文件不存在DotId: {DotId}", dotId); logger.LogError("配置打印数据错误点阵文件不存在DotId: {DotId}", dotId);
await TryCallbackCodeFailAsync(request, cancellationToken);
return; return;
} }
@ -101,7 +105,7 @@ public class AutoDotCodeConsumer(
if (!File.Exists(exePath)) if (!File.Exists(exePath))
{ {
logger.LogError("PrintTool.exe 不存在,路径:{ExePath}", exePath); logger.LogError("PrintTool.exe 不存在,路径:{ExePath}", exePath);
await ExecuteUpdateJournalStatusAsync(BuildFailResponse(request), cancellationToken); await TryCallbackCodeFailAsync(request, cancellationToken);
return; return;
} }
@ -109,7 +113,7 @@ public class AutoDotCodeConsumer(
if (!File.Exists(xmlPath)) if (!File.Exists(xmlPath))
{ {
logger.LogError("铺码授权文件不存在,路径:{XmlPath}", xmlPath); logger.LogError("铺码授权文件不存在,路径:{XmlPath}", xmlPath);
await ExecuteUpdateJournalStatusAsync(BuildFailResponse(request), cancellationToken); await TryCallbackCodeFailAsync(request, cancellationToken);
return; return;
} }
@ -126,7 +130,7 @@ public class AutoDotCodeConsumer(
if (dotFileDetailList.Count < pageNumMax) if (dotFileDetailList.Count < pageNumMax)
{ {
logger.LogError("点阵页码余量不足DotId: {DotId}, Need: {Need}, Available: {Available}", dotId, pageNumMax, dotFileDetailList.Count); logger.LogError("点阵页码余量不足DotId: {DotId}, Need: {Need}, Available: {Available}", dotId, pageNumMax, dotFileDetailList.Count);
await ExecuteUpdateJournalStatusAsync(BuildFailResponse(request), cancellationToken); await TryCallbackCodeFailAsync(request, cancellationToken);
return; return;
} }
@ -138,7 +142,7 @@ public class AutoDotCodeConsumer(
if (printResult.Timeout || printResult.ExitCode != 0) if (printResult.Timeout || printResult.ExitCode != 0)
{ {
logger.LogError("执行 PrintTool.exe 失败,退出码:{ExitCode},错误信息:{ErrorMessage}", printResult.ExitCode, printResult.Error); logger.LogError("执行 PrintTool.exe 失败,退出码:{ExitCode},错误信息:{ErrorMessage}", printResult.ExitCode, printResult.Error);
await ExecuteUpdateJournalStatusAsync(BuildFailResponse(request, dotFileDetailPageName), cancellationToken); await TryCallbackCodeFailAsync(request, cancellationToken, dotFileDetailPageName);
return; return;
} }
@ -150,8 +154,7 @@ public class AutoDotCodeConsumer(
if (!ValidatePrintOutput(printResult.Output, dotFileDetailPageName, dPrint)) if (!ValidatePrintOutput(printResult.Output, dotFileDetailPageName, dPrint))
{ {
statusModel.Status = JournalStatusEnum.CodeFail; await TryCallbackCodeFailAsync(request, cancellationToken, dotFileDetailPageName);
await ExecuteUpdateJournalStatusAsync(statusModel, cancellationToken);
return; return;
} }
@ -192,10 +195,18 @@ public class AutoDotCodeConsumer(
logger.LogError("回调接口修改书籍状态失败JournalId: {JournalId},接口返回消息:{Message}", statusModel.JournalId, callbackResponse.Message); logger.LogError("回调接口修改书籍状态失败JournalId: {JournalId},接口返回消息:{Message}", statusModel.JournalId, callbackResponse.Message);
} }
} }
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
{
logger.LogWarning("自动铺码处理已取消JournalId: {JournalId}", request?.JournalId);
throw;
}
catch (Exception ex) catch (Exception ex)
{ {
logger.LogError(ex, "自动铺码处理失败"); logger.LogError(ex, "自动铺码处理失败");
throw; if (request != null)
{
await TryCallbackCodeFailAsync(request, CancellationToken.None);
}
} }
} }
@ -298,6 +309,26 @@ public class AutoDotCodeConsumer(
return new CallbackUpdateJournalStatusResponse(); return new CallbackUpdateJournalStatusResponse();
} }
private async Task TryCallbackCodeFailAsync(JournalPagePrintDto request, CancellationToken cancellationToken, string[]? pageNo = null)
{
try
{
var callbackResponse = await ExecuteUpdateJournalStatusAsync(BuildFailResponse(request, pageNo), cancellationToken);
if (!callbackResponse.IsSuccess)
{
logger.LogError("回调接口修改书籍为铺码失败状态失败JournalId: {JournalId},接口返回消息:{Message}", request.JournalId, callbackResponse.Message);
}
}
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
{
throw;
}
catch (Exception callbackEx)
{
logger.LogError(callbackEx, "铺码失败后请求回调接口失败JournalId: {JournalId}", request.JournalId);
}
}
private static JournalPagePrintDto BuildFailResponse(JournalPagePrintDto request, string[]? pageNo = null) private static JournalPagePrintDto BuildFailResponse(JournalPagePrintDto request, string[]? pageNo = null)
{ {
return new JournalPagePrintDto return new JournalPagePrintDto

View File

@ -233,13 +233,14 @@ public class JournalTaskReceiveConsumer(
{ {
throw new InvalidOperationException("AI聊天服务配置不完整请检查 AiChat 配置节点"); throw new InvalidOperationException("AI聊天服务配置不完整请检查 AiChat 配置节点");
} }
var answerMapPrompt = BuildScorePrompt(scoreUnit, referenceAnswerMap);
logger.LogInformation(answerMapPrompt);
var content = new List<object> var content = new List<object>
{ {
new new
{ {
type = "text", type = "text",
text = BuildScorePrompt(scoreUnit, referenceAnswerMap) text = answerMapPrompt
} }
}; };
@ -251,7 +252,7 @@ public class JournalTaskReceiveConsumer(
content.Add(new content.Add(new
{ {
type = "text", type = "text",
text = $"以下为任务 {context.Task.Id} 的学生作答图片,共 {answerImages.Count} 张。" text = $"以下学生作答图片,共 {answerImages.Count} 张。"
}); });
foreach (var answerImage in answerImages) foreach (var answerImage in answerImages)
@ -269,31 +270,6 @@ public class JournalTaskReceiveConsumer(
throw new InvalidOperationException($"评分单元 {scoreUnit.GroupId} 缺少答案图片"); throw new InvalidOperationException($"评分单元 {scoreUnit.GroupId} 缺少答案图片");
} }
foreach (var context in scoreUnit.Questions)
{
referenceAnswerMap.TryGetValue(context.Task.Id, out var referenceAnswers);
var referenceAnswerImages = await BuildReferenceAnswerImageContentsAsync(context.Task.Id, referenceAnswers ?? [], cancellationToken);
if (referenceAnswerImages.Count == 0)
{
continue;
}
content.Add(new
{
type = "text",
text = $"以下为任务 {context.Task.Id} 的参考答案图片,共 {referenceAnswerImages.Count} 张。参考答案不是必有评分时以题目Prompt和学生答案为主。"
});
foreach (var referenceAnswerImage in referenceAnswerImages)
{
content.Add(new
{
type = "image_url",
image_url = new { url = referenceAnswerImage.DataUrl }
});
}
}
var requestBody = new var requestBody = new
{ {
model, model,
@ -403,6 +379,7 @@ public class JournalTaskReceiveConsumer(
prompt.AppendLine(" \"Completion\": 100,"); prompt.AppendLine(" \"Completion\": 100,");
prompt.AppendLine(" \"Result\": \"50字内的中文评语\""); prompt.AppendLine(" \"Result\": \"50字内的中文评语\"");
prompt.AppendLine("}"); prompt.AppendLine("}");
return prompt.ToString(); return prompt.ToString();
} }

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>