Просмотр исходного кода

feat(临考突击报告): 补充同步生成前置耗时日志

金逸霄 1 неделя назад
Родитель
Сommit
0d28e3e416

+ 51 - 3
abilities/exam-sprint/application/src/main/java/cn/yunzhixue/ability/center/examsprint/application/report/DefaultExamSprintReportApplicationService.java

@@ -100,19 +100,33 @@ public class DefaultExamSprintReportApplicationService implements ExamSprintRepo
 
     @Override
     public CreateExamSprintReportWithUrlResponse createOutlookReportSync(JsonNode payload) {
-        logReceivedPayload(ReportType.OUTLOOK, "sync", payload);
+        SyncPreparationTiming timing = startSyncPreparation(ReportType.OUTLOOK, payload);
         validateOutlookPayload(payload);
+        timing.markValidationCompleted();
         JsonNode normalizedPayload = normalizeOutlookPayload(payload);
-        return submitReportGenerationSync(ReportType.OUTLOOK, new UnmodeledReportContent(ReportType.OUTLOOK, normalizedPayload));
+        ReportContent content = new UnmodeledReportContent(ReportType.OUTLOOK, normalizedPayload);
+        timing.markContentMappingCompleted();
+        timing.logCompleted();
+        return submitReportGenerationSync(ReportType.OUTLOOK, content);
     }
 
     @Override
     public CreateExamSprintReportWithUrlResponse createAchievementReportSync(JsonNode payload) {
-        logReceivedPayload(ReportType.ACHIEVEMENT, "sync", payload);
+        SyncPreparationTiming timing = startSyncPreparation(ReportType.ACHIEVEMENT, payload);
         AchievementReportContent content = validateAchievementPayload(payload);
+        timing.markValidationCompleted();
+        timing.markContentMappingCompleted();
+        timing.logCompleted();
         return submitReportGenerationSync(ReportType.ACHIEVEMENT, content);
     }
 
+    private SyncPreparationTiming startSyncPreparation(ReportType reportType, JsonNode payload) {
+        SyncPreparationTiming timing = new SyncPreparationTiming(reportType);
+        logReceivedPayload(reportType, "sync", payload);
+        timing.markPayloadSummaryCompleted();
+        return timing;
+    }
+
     private void logReceivedPayload(ReportType reportType, String mode, JsonNode payload) {
         log.info(
                 "exam_sprint_report_payload_received reportType={} mode={} payload={}",
@@ -296,6 +310,40 @@ public class DefaultExamSprintReportApplicationService implements ExamSprintRepo
                 downloadUrl);
     }
 
+    private final class SyncPreparationTiming {
+        private final ReportType reportType;
+        private final long startedNanos = System.nanoTime();
+        private long payloadSummaryDurationMs;
+        private long validationDurationMs;
+        private long contentMappingDurationMs;
+
+        private SyncPreparationTiming(ReportType reportType) {
+            this.reportType = reportType;
+        }
+
+        private void markPayloadSummaryCompleted() {
+            payloadSummaryDurationMs = elapsedMillis(startedNanos);
+        }
+
+        private void markValidationCompleted() {
+            validationDurationMs = elapsedMillis(startedNanos) - payloadSummaryDurationMs;
+        }
+
+        private void markContentMappingCompleted() {
+            contentMappingDurationMs = elapsedMillis(startedNanos) - payloadSummaryDurationMs - validationDurationMs;
+        }
+
+        private void logCompleted() {
+            log.info(
+                    "exam_sprint_report_sync_stage_completed reportType={} stage=prepare_content durationMs={} payloadSummaryDurationMs={} validationDurationMs={} contentMappingDurationMs={}",
+                    reportType,
+                    elapsedMillis(startedNanos),
+                    payloadSummaryDurationMs,
+                    validationDurationMs,
+                    contentMappingDurationMs);
+        }
+    }
+
     @Override
     public ExamSprintReportDetailResponse getReport(String reportId) {
         Instant now = clock.instant();

+ 34 - 1
abilities/exam-sprint/application/src/test/java/cn/yunzhixue/ability/center/examsprint/application/report/ExamSprintReportApplicationServiceTest.java

@@ -619,6 +619,28 @@ class ExamSprintReportApplicationServiceTest {
         assertThat(storage.generatedKeys).containsExactly(expectedFileName);
     }
 
+    /** 覆盖同步展望报告观测场景,应记录 payload 日志后到 pipeline 前的前置阶段耗时,避免误判端到端耗时。 */
+    @Test
+    void createOutlookReportSyncLogsPreparationStageDuration(CapturedOutput output) {
+        DefaultExamSprintReportApplicationService service = service(
+                new TestRepository(),
+                reportId -> {
+                    throw new IllegalStateException("sync create must not dispatch async generation");
+                },
+                new TestStorage());
+
+        service.createOutlookReportSync(validOutlookPayload());
+
+        assertThat(output.getAll())
+                .contains("exam_sprint_report_sync_stage_completed")
+                .contains("reportType=OUTLOOK")
+                .contains("stage=prepare_content")
+                .contains("durationMs=")
+                .contains("payloadSummaryDurationMs=")
+                .contains("validationDurationMs=")
+                .contains("contentMappingDurationMs=");
+    }
+
     /** 覆盖同步展望报告下载地址生成失败场景,当存储层抛错时,应转换为下载不可用且日志不泄露异常消息。 */
     @Test
     void createOutlookReportSyncConvertsDownloadUrlGenerationFailureWithoutSensitiveLog(CapturedOutput output) {
@@ -1015,6 +1037,16 @@ class ExamSprintReportApplicationServiceTest {
             TestStorage storage,
             List<ExamSprintReportRenderer> renderers,
             ExamSprintReportPdfGenerator pdfGenerator) {
+        return service(repository, dispatcher, storage, renderers, pdfGenerator, properties());
+    }
+
+    private DefaultExamSprintReportApplicationService service(
+            TestRepository repository,
+            ExamSprintReportGenerationDispatcher dispatcher,
+            TestStorage storage,
+            List<ExamSprintReportRenderer> renderers,
+            ExamSprintReportPdfGenerator pdfGenerator,
+            ExamSprintReportProperties properties) {
         return new DefaultExamSprintReportApplicationService(
                 repository,
                 dispatcher,
@@ -1025,7 +1057,7 @@ class ExamSprintReportApplicationServiceTest {
                         storage,
                         FIXED_CLOCK),
                 storage,
-                properties(),
+                properties,
                 FIXED_CLOCK,
                 OBJECT_MAPPER,
                 VALIDATOR);
@@ -1422,4 +1454,5 @@ class ExamSprintReportApplicationServiceTest {
             throw new IllegalStateException("renderer exploded");
         }
     }
+
 }