Bläddra i källkod

fix(临考突击报告): 优化报告页面背景和页脚字号

金逸霄 1 dag sedan
förälder
incheckning
9f7da29f4f

+ 1 - 1
abilities/exam-sprint/infrastructure/src/main/java/cn/yunzhixue/ability/center/examsprint/infrastructure/report/pdf/DefaultPlaywrightPdfWorker.java

@@ -227,7 +227,7 @@ final class DefaultPlaywrightPdfWorker implements PlaywrightPdfWorker {
                         display: table;
                         table-layout: fixed;
                         font-family: 'MiSans VF', MiSans, ReportFont, sans-serif;
-                        font-size: 9px;
+                        font-size: 12px;
                         line-height: 1.45;
                         color: #68768a;
                     }

+ 2 - 2
abilities/exam-sprint/infrastructure/src/main/resources/templates/achievement-exam-sprint-report-template.html

@@ -12,7 +12,7 @@
         body {
             margin: 0;
             padding: 0;
-            background: #f5f7fa;
+            background: #fff;
             color: #263241;
             font-family: 'MiSans VF', MiSans, ReportFont, sans-serif;
             font-size: 14px;
@@ -23,7 +23,7 @@
             max-width: 1200px;
             margin: 0 auto;
             background: #fff;
-            border: 1px solid #e7edf5;
+            border: 1px solid transparent;
             border-radius: 14px;
             padding: 32px;
         }

+ 2 - 2
abilities/exam-sprint/infrastructure/src/main/resources/templates/outlook-exam-sprint-report-template.html

@@ -11,7 +11,7 @@
 
         body {
             margin: 0;
-            background-color: #f5f7fa;
+            background-color: #fff;
             padding: 0;
             color: #333;
             font-family: 'MiSans VF', MiSans, ReportFont, sans-serif;
@@ -25,7 +25,7 @@
             background: #fff;
             padding: 32px;
             border-radius: 12px;
-            border: 1px solid #e7edf5;
+            border: 1px solid transparent;
         }
 
         h1.report-title {

+ 61 - 0
abilities/exam-sprint/infrastructure/src/test/java/cn/yunzhixue/ability/center/examsprint/infrastructure/report/pdf/PlaywrightExamSprintReportPdfGeneratorTest.java

@@ -32,6 +32,7 @@ import java.util.List;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.assertj.core.api.Assertions.within;
 
 @TestInstance(TestInstance.Lifecycle.PER_CLASS)
 class PlaywrightExamSprintReportPdfGeneratorTest {
@@ -488,6 +489,62 @@ class PlaywrightExamSprintReportPdfGeneratorTest {
         }
     }
 
+    @Test
+    void generateUsesHeaderFontSizeForBusinessFooterAndPageNumbers() throws Exception {
+        byte[] pdfBytes = pdfGenerator.generate("""
+                <html>
+                <head>
+                    <meta charset=\"UTF-8\"/>
+                    <style>
+                        @page { size: A4; margin: 0; }
+                        body { margin: 0; font-family: MiSans, ReportFont, sans-serif; font-size: 14px; }
+                        .report-container { padding: 32px; }
+                        .report-header { display: table; width: 100%; table-layout: fixed; border-bottom: 3px solid #111; margin-bottom: 28px; padding-bottom: 10px; }
+                        .header-logo, .header-main, .header-generated-at { display: table-cell; vertical-align: top; }
+                        .header-logo { width: 180px; }
+                        .header-main { text-align: center; color: #68768a; }
+                        .header-report-type, .header-student-name { font-size: 13px; line-height: 1.5; }
+                        .header-generated-at { width: 260px; color: #68768a; font-size: 12px; line-height: 1.5; text-align: right; white-space: nowrap; }
+                        .page { height: 1122px; page-break-after: always; }
+                    </style>
+                </head>
+                <body>
+                    <div class=\"report-container\">
+                        <header class=\"report-header\">
+                            <div class=\"header-logo\"></div>
+                            <div class=\"header-main\">
+                                <div class=\"header-report-type\">学习成果报告</div>
+                                <div class=\"header-student-name\">分页测试学生</div>
+                            </div>
+                            <div class=\"header-generated-at\">2026-05-13 10:08:54</div>
+                        </header>
+                        <main>
+                            <section class=\"page\">第一页正文内容</section>
+                            <section class=\"page\">第二页正文内容</section>
+                        </main>
+                    </div>
+                    <div class=\"report-footer-business\">
+                        <div class=\"report-footer-business-line\">张三&nbsp;&nbsp;&nbsp;&nbsp;Tel:138987484</div>
+                        <div class=\"report-footer-business-line\">浙江省杭州市</div>
+                    </div>
+                </body>
+                </html>
+                """);
+
+        assertPdfHeader(pdfBytes);
+        List<TextLine> pageTwoLines = textLines(pdfBytes, 2);
+        TextLine headerTimeLine = findLineContaining(pageTwoLines, "2026-05-1310:08:54");
+        TextLine businessLine = findLineContaining(pageTwoLines, "张三Tel:138987484");
+        TextLine pageNumberLine = findLineContaining(pageTwoLines, "2/");
+
+        assertThat(businessLine.height())
+                .as("business footer font height should match header generated-at font height")
+                .isCloseTo(headerTimeLine.height(), within(0.6f));
+        assertThat(pageNumberLine.height())
+                .as("page number font height should match header generated-at font height")
+                .isCloseTo(headerTimeLine.height(), within(0.6f));
+    }
+
     private void assertPdfHeader(byte[] pdfBytes) {
         assertThat(pdfBytes).isNotEmpty();
         assertThat(new String(pdfBytes, 0, 4, StandardCharsets.ISO_8859_1)).isEqualTo("%PDF");
@@ -577,6 +634,10 @@ class PlaywrightExamSprintReportPdfGeneratorTest {
     }
 
     private record TextLine(String text, float x, float y, float bottomY) {
+
+        private float height() {
+            return bottomY - y;
+        }
     }
 
     private AchievementReportContent sampleAchievementContent() {

+ 17 - 0
abilities/exam-sprint/infrastructure/src/test/java/cn/yunzhixue/ability/center/examsprint/infrastructure/report/rendering/achievement/AchievementExamSprintReportTemplateCompatibilityTest.java

@@ -21,6 +21,23 @@ class AchievementExamSprintReportTemplateCompatibilityTest {
                 .doesNotContain("font-family: MiSans, ReportFont, sans-serif");
     }
 
+    @Test
+    void reportContainerBorderIsTransparentToAvoidPageHeaderSeam() throws Exception {
+        String normalizedTemplate = normalizeWhitespace(loadTemplate());
+
+        assertThat(normalizedTemplate)
+                .containsPattern("\\.report-container\\s*\\{[^}]*border\\s*:\\s*1px\\s+solid\\s+transparent\\s*;[^}]*}");
+    }
+
+    @Test
+    void bodyBackgroundIsWhiteForReportPages() throws Exception {
+        String normalizedTemplate = normalizeWhitespace(loadTemplate());
+
+        assertThat(normalizedTemplate)
+                .containsPattern("body\\s*\\{[^}]*background\\s*:\\s*#fff\\s*;[^}]*}")
+                .doesNotContain("background: #f5f7fa");
+    }
+
     private String loadTemplate() throws Exception {
         ClassPathResource resource = new ClassPathResource("templates/achievement-exam-sprint-report-template.html");
         try (InputStream inputStream = resource.getInputStream()) {

+ 19 - 2
abilities/exam-sprint/infrastructure/src/test/java/cn/yunzhixue/ability/center/examsprint/infrastructure/report/rendering/outlook/OutlookExamSprintReportTemplateCompatibilityTest.java

@@ -18,7 +18,7 @@ class OutlookExamSprintReportTemplateCompatibilityTest {
 
         assertThat(normalizedTemplate)
                 .contains("body")
-                .contains("background-color: #f5f7fa")
+                .contains("background-color: #fff")
                 .contains("padding: 0")
                 .contains("color: #333")
                 .contains("font-family: " + REPORT_FONT_STACK)
@@ -33,7 +33,7 @@ class OutlookExamSprintReportTemplateCompatibilityTest {
                 .contains("background: #fff")
                 .contains("padding: 32px")
                 .contains("border-radius: 12px")
-                .contains("border: 1px solid #e7edf5")
+                .contains("border: 1px solid transparent")
                 .doesNotContainPattern("(?i)(^|[\\s{;])box-shadow\\s*:")
                 .contains("h1.report-title")
                 .contains("font-size: 28px")
@@ -147,6 +147,23 @@ class OutlookExamSprintReportTemplateCompatibilityTest {
                 .doesNotContain("font-family: MiSans, ReportFont, sans-serif");
     }
 
+    @Test
+    void reportContainerBorderIsTransparentToAvoidPageHeaderSeam() throws Exception {
+        String normalizedTemplate = normalizeWhitespace(loadTemplate());
+
+        assertThat(normalizedTemplate)
+                .containsPattern("\\.report-container\\s*\\{[^}]*border\\s*:\\s*1px\\s+solid\\s+transparent\\s*;[^}]*}");
+    }
+
+    @Test
+    void bodyBackgroundIsWhiteForReportPages() throws Exception {
+        String normalizedTemplate = normalizeWhitespace(loadTemplate());
+
+        assertThat(normalizedTemplate)
+                .containsPattern("body\\s*\\{[^}]*background-color\\s*:\\s*#fff\\s*;[^}]*}")
+                .doesNotContain("background-color: #f5f7fa");
+    }
+
     private String loadTemplate() throws Exception {
         ClassPathResource resource = new ClassPathResource("templates/outlook-exam-sprint-report-template.html");
         try (InputStream inputStream = resource.getInputStream()) {