# Report Preview and Legacy API Removal Implementation Plan

> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

**Goal:** Remove the unused public HTML preview API and deprecated generic report creation endpoint while preserving typed report creation, status query, PDF generation, and PDF download.

**Architecture:** Treat this as an API contract cleanup: tests first lock the desired public contract, contracts/application/controller are then simplified to remove `previewHtmlUrl` and preview methods, and the PDF generation pipeline keeps using internal HTML renderers. The runtime controller should expose only typed creation, detail query, and PDF download endpoints.

**Tech Stack:** Java 17, Spring Boot MVC, Maven reactor, JUnit 5, AssertJ, MockMvc, Jackson records/DTOs.

---

## Working Directory

Run all commands from this worktree unless a step says otherwise:

```text
/Users/exiao/Codes/dcjxb.microservice/.worktrees/report-api-cleanup
```

Baseline already verified before writing this plan:

```bash
mvn -pl ability-center-runtime -am -Dtest=ExamSprintReportControllerWebMvcTest,ExamSprintReportControllerTest test
mvn -pl abilities/exam-sprint/application -am -Dtest=ExamSprintReportApplicationServiceTest test
```

Both commands passed on `2026-04-27`.

Do not create a git commit unless the user explicitly asks for one.

---

### Task 1: Update Runtime Controller Tests for Removed Public Contract

**Files:**
- Modify: `ability-center-runtime/src/test/java/cn/yunzhixue/ability/center/examsprint/adapter/http/ExamSprintReportControllerWebMvcTest.java`
- Modify: `ability-center-runtime/src/test/java/cn/yunzhixue/ability/center/examsprint/adapter/http/ExamSprintReportControllerTest.java`

**Step 1: Write the failing controller-level tests**

In `ExamSprintReportControllerWebMvcTest.java`:

1. Remove the import of `ExamSprintReportApplicationService.ReportHtmlPreviewContent`.
2. Update `new CreateExamSprintReportWithUrlResponse(...)` calls so they pass only these fields:

```java
new CreateExamSprintReportWithUrlResponse(
        "report-sync-001",
        ExamSprintReportType.OUTLOOK,
        ExamSprintReportGenerationStatus.SUCCESS,
        Instant.parse("2026-01-01T00:00:00Z"),
        Instant.parse("2026-01-01T00:01:00Z"),
        Instant.parse("2026-01-02T00:00:00Z"),
        "https://dcjxb-cdntest.yunzhixue.cn/exam-assault-report/exam-sprint-outlook-report-report-sync-001.pdf")
```

3. Update the achievement sync fixture the same way, keeping only the `downloadUrl` as the final argument.
4. Update `new ExamSprintReportDetailResponse(...)` calls so they pass only these fields:

```java
new ExamSprintReportDetailResponse(
        "report-001",
        ExamSprintReportType.OUTLOOK,
        ExamSprintReportGenerationStatus.SUCCESS,
        Instant.parse("2026-01-01T00:00:00Z"),
        Instant.parse("2026-01-01T00:05:00Z"),
        Instant.parse("2026-01-02T00:00:00Z"),
        "https://dcjxb-cdntest.yunzhixue.cn/exam-assault-report/exam-sprint-outlook-report-report-001.pdf",
        null)
```

5. Remove assertions that check `$.data.previewHtmlUrl`.
6. Replace the old `oldGenericCreateReportEndpointIsRemoved` test with a test that proves removed endpoints are no longer exposed:

```java
@Test
void removedReportEndpointsAreNotExposed() throws Exception {
    mockMvc.perform(post("/api/exam-sprint/reports")
                    .contentType(MediaType.APPLICATION_JSON)
                    .content("{\"reportType\":\"OUTLOOK\",\"payload\":{}}"))
            .andExpect(status().isNotFound());

    mockMvc.perform(get("/api/exam-sprint/reports/{reportId}/preview/html", "report-001"))
            .andExpect(status().isNotFound());

    verifyNoInteractions(applicationService);
}
```

7. Delete the `previewHtmlReturnsTextHtmlForSucceededReport` test.
8. Remove any stubbing of `applicationService.previewReportHtml(...)`.

In `ExamSprintReportControllerTest.java`:

1. Remove `jsonPath("$.data.previewHtmlUrl").isNotEmpty()` assertions.
2. Remove extraction of `previewHtmlUri`.
3. Remove `mockMvc.perform(get(previewHtmlUri))` calls and HTML-content assertions.
4. Keep the PDF download assertions for both report types.

**Step 2: Run controller tests to verify they fail before implementation**

Run:

```bash
mvn -pl ability-center-runtime -am -Dtest=ExamSprintReportControllerWebMvcTest,ExamSprintReportControllerTest test
```

Expected: FAIL. Accept either compile failures from the still-old DTO constructor shapes, or test failures because the preview endpoint still exists / `previewHtmlUrl` still appears.

**Step 3: Checkpoint status**

Run:

```bash
git status --short
```

Expected: only the two runtime test files and this plan file should be modified so far.

Do not commit.

---

### Task 2: Update Application Service Tests for Removed `previewHtmlUrl` and Preview Service Method

**Files:**
- Modify: `abilities/exam-sprint/application/src/test/java/cn/yunzhixue/ability/center/examsprint/application/report/ExamSprintReportApplicationServiceTest.java`

**Step 1: Write failing application-contract tests**

Add imports near the other `java.*` imports:

```java
import cn.yunzhixue.ability.center.examsprint.contracts.report.CreateExamSprintReportWithUrlResponse;
import cn.yunzhixue.ability.center.examsprint.contracts.report.ExamSprintReportDetailResponse;

import java.lang.reflect.Method;
import java.lang.reflect.RecordComponent;
import java.util.Arrays;
```

Add this test near the top of `ExamSprintReportApplicationServiceTest`:

```java
@Test
void publicReportContractsDoNotExposeHtmlPreview() {
    assertThat(recordComponentNames(CreateExamSprintReportWithUrlResponse.class))
            .contains("downloadUrl")
            .doesNotContain("previewHtmlUrl");
    assertThat(recordComponentNames(ExamSprintReportDetailResponse.class))
            .contains("downloadUrl")
            .doesNotContain("previewHtmlUrl");
    assertThat(Arrays.stream(ExamSprintReportApplicationService.class.getDeclaredMethods())
            .map(Method::getName))
            .doesNotContain("previewReportHtml");
}
```

Add this helper near the other private helpers:

```java
private List<String> recordComponentNames(Class<? extends Record> recordType) {
    return Arrays.stream(recordType.getRecordComponents())
            .map(RecordComponent::getName)
            .toList();
}
```

Update existing application service tests:

1. In `createOutlookReportSyncGeneratesUploadAndReturnsDownloadUrl`, delete the `response.previewHtmlUrl()` assertion.
2. In `createAchievementReportSyncGeneratesUploadAndReturnsDownloadUrl`, delete the `response.previewHtmlUrl()` assertion.
3. Rename `getReportReturnsDownloadAndPreviewUrlsForSuccessfulReport` to `getReportReturnsDownloadUrlForSuccessfulReport`.
4. In that renamed test, delete the `response.previewHtmlUrl()` assertion.
5. Delete `previewReportHtmlRendersSavedPayloadForSuccessfulReport`.
6. Delete `previewReportHtmlRejectsPendingReport`.

**Step 2: Run application tests to verify they fail before implementation**

Run:

```bash
mvn -pl abilities/exam-sprint/application -am -Dtest=ExamSprintReportApplicationServiceTest test
```

Expected: FAIL because `previewHtmlUrl` is still a record component and `previewReportHtml` is still on the application service interface.

**Step 3: Checkpoint status**

Run:

```bash
git status --short
```

Expected: runtime tests, application test, and this plan file are modified.

Do not commit.

---

### Task 3: Remove Preview Fields from Contract Records

**Files:**
- Modify: `abilities/exam-sprint/contracts/src/main/java/cn/yunzhixue/ability/center/examsprint/contracts/report/CreateExamSprintReportWithUrlResponse.java`
- Modify: `abilities/exam-sprint/contracts/src/main/java/cn/yunzhixue/ability/center/examsprint/contracts/report/ExamSprintReportDetailResponse.java`

**Step 1: Implement the contract change**

Change `CreateExamSprintReportWithUrlResponse` to:

```java
public record CreateExamSprintReportWithUrlResponse(
        String reportId,
        ExamSprintReportType reportType,
        ExamSprintReportGenerationStatus generationStatus,
        Instant createdAt,
        Instant updatedAt,
        Instant expiresAt,
        String downloadUrl) {
}
```

Change `ExamSprintReportDetailResponse` to:

```java
public record ExamSprintReportDetailResponse(
        String reportId,
        ExamSprintReportType reportType,
        ExamSprintReportGenerationStatus generationStatus,
        Instant createdAt,
        Instant updatedAt,
        Instant expiresAt,
        String downloadUrl,
        String failureReason) {
}
```

**Step 2: Run targeted compile/tests to expose remaining call sites**

Run:

```bash
mvn -pl abilities/exam-sprint/application,ability-center-runtime -am -DskipTests compile test-compile
```

Expected: FAIL with constructor or method references that still include `previewHtmlUrl` or preview service methods.

**Step 3: Checkpoint status**

Run:

```bash
git status --short
```

Do not commit.

---

### Task 4: Remove Preview Method from Application Service and Implementation

**Files:**
- Modify: `abilities/exam-sprint/application/src/main/java/cn/yunzhixue/ability/center/examsprint/application/report/ExamSprintReportApplicationService.java`
- Modify: `abilities/exam-sprint/application/src/main/java/cn/yunzhixue/ability/center/examsprint/application/report/DefaultExamSprintReportApplicationService.java`
- Modify as needed: `abilities/exam-sprint/application/src/test/java/cn/yunzhixue/ability/center/examsprint/application/report/ExamSprintReportApplicationServiceTest.java`

**Step 1: Remove preview from the service interface**

In `ExamSprintReportApplicationService.java`:

1. Delete `ReportHtmlPreviewContent previewReportHtml(String reportId);`.
2. Delete the nested `ReportHtmlPreviewContent` record.
3. Keep `ReportDownloadContent`.

Final shape should be:

```java
public interface ExamSprintReportApplicationService {

    CreateExamSprintReportResponse createOutlookReport(JsonNode payload);

    CreateExamSprintReportResponse createAchievementReport(JsonNode payload);

    CreateExamSprintReportWithUrlResponse createOutlookReportSync(JsonNode payload);

    CreateExamSprintReportWithUrlResponse createAchievementReportSync(JsonNode payload);

    ExamSprintReportDetailResponse getReport(String reportId);

    ReportDownloadContent downloadReport(String reportId);

    record ReportDownloadContent(String fileName, byte[] bytes, String contentType) {
    }
}
```

**Step 2: Remove preview implementation**

In `DefaultExamSprintReportApplicationService.java`:

1. Remove the import of `ExamSprintReportRenderer` if it is no longer needed.
2. Remove the `private final List<ExamSprintReportRenderer> renderers;` field.
3. Remove the `List<ExamSprintReportRenderer> renderers` constructor parameter.
4. Remove `this.renderers = List.copyOf(renderers);`.
5. In `submitReportGenerationSync`, remove `previewHtmlUrl` construction and return `CreateExamSprintReportWithUrlResponse` with `downloadUrl` as the final argument.
6. In `getReport`, remove the `previewHtmlUrl` local variable, remove its assignment, and return `ExamSprintReportDetailResponse` with `failureReason` immediately after `downloadUrl`.
7. Delete the entire `previewReportHtml(String reportId)` method.
8. Keep `List` import if still used for another type; otherwise remove it.

**Step 3: Update test service factory constructor call**

In `ExamSprintReportApplicationServiceTest.java`, update `new DefaultExamSprintReportApplicationService(...)` to remove the final `List.of(new PreviewTestRenderer())` argument. Keep `PreviewTestRenderer` if it is still used by `ExamSprintReportGenerationPipeline` in the test helper.

Expected constructor call shape:

```java
return new DefaultExamSprintReportApplicationService(
        repository,
        dispatcher,
        new ExamSprintReportGenerationPipeline(
                repository,
                renderers,
                pdfGenerator,
                storage,
                FIXED_CLOCK),
        storage,
        properties(),
        FIXED_CLOCK,
        OBJECT_MAPPER,
        VALIDATOR);
```

**Step 4: Run application tests**

Run:

```bash
mvn -pl abilities/exam-sprint/application -am -Dtest=ExamSprintReportApplicationServiceTest test
```

Expected: PASS.

**Step 5: Checkpoint status**

Run:

```bash
git status --short
```

Do not commit.

---

### Task 5: Remove Public Controller Endpoints and Update Runtime Tests

**Files:**
- Modify: `ability-center-runtime/src/main/java/cn/yunzhixue/ability/center/examsprint/adapter/http/ExamSprintReportController.java`
- Modify if needed: `ability-center-runtime/src/test/java/cn/yunzhixue/ability/center/examsprint/adapter/http/ExamSprintReportControllerWebMvcTest.java`
- Modify if needed: `ability-center-runtime/src/test/java/cn/yunzhixue/ability/center/examsprint/adapter/http/ExamSprintReportControllerTest.java`

**Step 1: Remove controller endpoint methods**

In `ExamSprintReportController.java`:

1. Remove imports:

```java
import cn.yunzhixue.ability.center.examsprint.application.report.ExamSprintReportApplicationService.ReportHtmlPreviewContent;
import org.springframework.http.HttpStatus;
```

2. Delete `createReportDeprecated()`.
3. Delete `previewReportHtml(String reportId)`.
4. Keep `downloadReport(String reportId)` unchanged.

**Step 2: Fix runtime test compile errors**

If compile errors remain in runtime tests:

1. Remove imports that only supported deleted preview tests.
2. Ensure `CreateExamSprintReportWithUrlResponse` and `ExamSprintReportDetailResponse` constructor calls use the new DTO shapes.
3. Ensure no test references `previewHtmlUrl`.
4. Keep the `removedReportEndpointsAreNotExposed` test from Task 1.

**Step 3: Run runtime tests**

Run:

```bash
mvn -pl ability-center-runtime -am -Dtest=ExamSprintReportControllerWebMvcTest,ExamSprintReportControllerTest test
```

Expected: PASS.

**Step 4: Checkpoint status**

Run:

```bash
git status --short
```

Do not commit.

---

### Task 6: Search for Remaining Preview API References

**Files:**
- Inspect only unless references require cleanup.

**Step 1: Search source and test code for removed public contract names**

Run:

```bash
rg "previewHtmlUrl|previewReportHtml|/preview/html|createReportDeprecated" ability-center-runtime abilities/exam-sprint
```

Expected: no matches in `src/main/java` or active tests. It is acceptable for this implementation plan or historical docs to mention the removed names.

**Step 2: If matches remain in production or active test code, remove them**

Rules:

- Remove public URL construction for `/preview/html`.
- Remove preview endpoint stubs/assertions.
- Do not remove HTML templates, renderer implementations, or PDF pipeline renderer usage.

**Step 3: Run compile for affected modules**

Run:

```bash
mvn -pl abilities/exam-sprint/application,ability-center-runtime -am -DskipTests compile test-compile
```

Expected: PASS.

Do not commit.

---

### Task 7: Final Verification

**Files:**
- No code edits expected.

**Step 1: Run targeted application tests**

Run:

```bash
mvn -pl abilities/exam-sprint/application -am -Dtest=ExamSprintReportApplicationServiceTest test
```

Expected: PASS.

**Step 2: Run targeted runtime tests**

Run:

```bash
mvn -pl ability-center-runtime -am -Dtest=ExamSprintReportControllerWebMvcTest,ExamSprintReportControllerTest test
```

Expected: PASS.

**Step 3: Run broader affected-module verification**

Run:

```bash
mvn -pl abilities/exam-sprint/application,ability-center-runtime -am test
```

Expected: PASS.

**Step 4: Inspect final diff**

Run:

```bash
git status --short
git diff -- ability-center-runtime abilities/exam-sprint docs/plans/2026-04-27-report-preview-and-legacy-api-removal.md
```

Expected:

- Removed preview endpoint and old generic endpoint from the controller.
- Removed `previewHtmlUrl` from response records.
- Removed preview service method and content record.
- Runtime/application tests updated to assert the new contract.
- Internal HTML renderers/templates and PDF generation pipeline remain intact.

Do not commit unless the user explicitly asks.
