# Report Business Footer Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Add optional `BusinessInfo` support to both exam sprint reports, show concrete report type text in repeated headers, and render business contact info on the left side of repeated footers with page numbers on the right. **Architecture:** Extend report payload contracts with a shared optional `BusinessInfo` record. Renderers own business-data formatting by emitting `.report-footer-business` markup, while `DefaultPlaywrightPdfWorker` extracts that markup into Chromium's native footer template next to the page-number placeholders. Existing native page header/footer margin handling remains unchanged. **Tech Stack:** Java records, Jackson JSON binding, Bean Validation annotations, HTML templates, Playwright Java PDF header/footer templates, JUnit 5, AssertJ, PDFBox. --- ## File Map - Create `abilities/exam-sprint/contracts/src/main/java/cn/yunzhixue/ability/center/examsprint/contracts/report/BusinessInfo.java`: shared optional payload object. - Modify `AchievementExamSprintReportPayload.java`: add `BusinessInfo businessInfo` field. - Modify `OutlookExamSprintReportPayload.java`: add `BusinessInfo businessInfo` field. - Modify `AchievementReportContent.java`: add nested optional domain `BusinessInfo` and compact constructor normalization. - Modify `AchievementReportContentMapper.java`: map payload business info to domain content. - Modify achievement/outlook HTML templates: replace fixed header type placeholder and add optional footer business placeholder. - Modify achievement/outlook renderers: provide report type placeholder and footer business markup. - Modify `DefaultPlaywrightPdfWorker.java`: extract optional footer business block and place it left of right-aligned page numbers. - Modify tests under application and infrastructure to cover new behavior. ## Task 1: Contract and Domain BusinessInfo Flow **Files:** - Create: `abilities/exam-sprint/contracts/src/main/java/cn/yunzhixue/ability/center/examsprint/contracts/report/BusinessInfo.java` - Modify: `abilities/exam-sprint/contracts/src/main/java/cn/yunzhixue/ability/center/examsprint/contracts/report/AchievementExamSprintReportPayload.java` - Modify: `abilities/exam-sprint/contracts/src/main/java/cn/yunzhixue/ability/center/examsprint/contracts/report/OutlookExamSprintReportPayload.java` - Modify: `abilities/exam-sprint/domain/src/main/java/cn/yunzhixue/ability/center/examsprint/domain/report/AchievementReportContent.java` - Modify: `abilities/exam-sprint/application/src/main/java/cn/yunzhixue/ability/center/examsprint/application/report/AchievementReportContentMapper.java` - Test: `abilities/exam-sprint/application/src/test/java/cn/yunzhixue/ability/center/examsprint/application/report/AchievementReportContentMapperTest.java` - [ ] **Step 1: Write failing mapper test** Add a test that parses an achievement payload with `BusinessInfo`, maps it, and asserts `businessInfo().name()`, `phone()`, and `address()` are populated. - [ ] **Step 2: Run mapper test to verify RED** Run: `mvn -pl abilities/exam-sprint/application -Dtest=AchievementReportContentMapperTest test` Expected: compilation failure or assertion failure because `BusinessInfo` is not modeled yet. - [ ] **Step 3: Add contracts and domain fields** Create `BusinessInfo` contract record and add optional fields to both payload records. Add `AchievementReportContent.BusinessInfo` and a nullable `businessInfo` component. - [ ] **Step 4: Map achievement business info** Update `AchievementReportContentMapper.toDomainContent` to convert non-null payload business info to domain business info. - [ ] **Step 5: Run mapper test to verify GREEN** Run: `mvn -pl abilities/exam-sprint/application -Dtest=AchievementReportContentMapperTest test` Expected: PASS. ## Task 2: Renderer Header Type and Footer Business Markup **Files:** - Modify: `abilities/exam-sprint/infrastructure/src/main/resources/templates/achievement-exam-sprint-report-template.html` - Modify: `abilities/exam-sprint/infrastructure/src/main/resources/templates/outlook-exam-sprint-report-template.html` - Modify: `abilities/exam-sprint/infrastructure/src/main/java/cn/yunzhixue/ability/center/examsprint/infrastructure/report/rendering/achievement/ClasspathAchievementExamSprintReportRenderer.java` - Modify: `abilities/exam-sprint/infrastructure/src/main/java/cn/yunzhixue/ability/center/examsprint/infrastructure/report/rendering/outlook/ClasspathOutlookExamSprintReportRenderer.java` - Test: achievement/outlook renderer tests. - [ ] **Step 1: Write failing renderer tests** Assert achievement header contains `学习成果报告`; outlook header contains `潜力展望报告`; business info renders in `.report-footer-business` when present and does not render when missing. - [ ] **Step 2: Run renderer tests to verify RED** Run: `mvn -pl abilities/exam-sprint/infrastructure -Dtest=ClasspathAchievementExamSprintReportRendererTest,ClasspathOutlookExamSprintReportRendererTest test` Expected: FAIL because templates still contain fixed header text and no footer business markup. - [ ] **Step 3: Implement renderer placeholders** Replace fixed header report type text with `{{headerReportType}}`. Add `{{reportFooterBusiness}}` before ``. Render escaped optional business markup with first line name/phone and second line address. - [ ] **Step 4: Run renderer tests to verify GREEN** Run: `mvn -pl abilities/exam-sprint/infrastructure -Dtest=ClasspathAchievementExamSprintReportRendererTest,ClasspathOutlookExamSprintReportRendererTest test` Expected: PASS. ## Task 3: PDF Footer Layout Extraction **Files:** - Modify: `abilities/exam-sprint/infrastructure/src/main/java/cn/yunzhixue/ability/center/examsprint/infrastructure/report/pdf/DefaultPlaywrightPdfWorker.java` - Test: `abilities/exam-sprint/infrastructure/src/test/java/cn/yunzhixue/ability/center/examsprint/infrastructure/report/pdf/PlaywrightExamSprintReportPdfGeneratorTest.java` - [ ] **Step 1: Write failing PDF footer test** Generate multi-page HTML containing `.report-footer-business`, then use PDFBox text positions to assert business text is on the left half of the page and `2 / total` is on the right half. - [ ] **Step 2: Run PDF footer test to verify RED** Run: `mvn -pl abilities/exam-sprint/infrastructure -Dtest=PlaywrightExamSprintReportPdfGeneratorTest#generatePlacesBusinessInfoLeftAndPageNumbersRightInFooter test` Expected: FAIL because footer template currently only renders centered page numbers. - [ ] **Step 3: Implement footer extraction** Add a `.report-footer-business` regex extractor, remove that source block from print body HTML, and build a footer template with left business block and right page-number block. - [ ] **Step 4: Run PDF footer test to verify GREEN** Run: `mvn -pl abilities/exam-sprint/infrastructure -Dtest=PlaywrightExamSprintReportPdfGeneratorTest#generatePlacesBusinessInfoLeftAndPageNumbersRightInFooter test` Expected: PASS. ## Task 4: Full Verification **Files:** - No additional production files. - [ ] **Step 1: Run contract/application relevant tests** Run: `mvn -pl abilities/exam-sprint/application test` Expected: PASS. - [ ] **Step 2: Run infrastructure tests** Run: `mvn -pl abilities/exam-sprint/infrastructure test` Expected: PASS. - [ ] **Step 3: Review diff** Run: `git diff -- abilities/exam-sprint docs/superpowers/specs/2026-05-13-report-business-footer-design.md docs/superpowers/plans/2026-05-13-report-business-footer.md` Expected: only intended contract, mapper, renderer, template, PDF worker, test, and documentation changes.