重构插件异步状态与业务用例

This commit is contained in:
张家训
2026-07-30 22:06:04 +08:00
parent 6b855a42b8
commit 09fbd84837
72 changed files with 3723 additions and 904 deletions
@@ -0,0 +1,108 @@
# Shared Result UI Phase 2 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:** Remove duplicated export-history and empty-result UI code from Scanner and MyBatis SQL Export while preserving their behavior.
**Architecture:** Add a small immutable adapter model between plugin-specific history rows and a reusable JavaFX history pane. Keep file-opening side effects behind injected callbacks so the shared pane only renders data and emits commands. Add a reusable empty-state component for later plugin migrations.
**Tech Stack:** Java 17, JavaFX, JUnit 5, Maven.
## Global Constraints
- Work only in `C:\tmp\workutils-plugin-ui-refactor` on branch `codex/plugin-ui-refactor`.
- Do not add raw `new Thread`.
- Shared UI components must not read files or launch external programs.
- Plugin-specific persistence formats must remain unchanged.
- Write and observe a failing test before each production behavior.
---
### Task 1: Immutable history presentation model
**Files:**
- Create: `src/main/java/com/example/workutils/system/ui/history/HistoryItem.java`
- Test: `src/test/java/com/example/workutils/system/ui/history/HistoryItemTest.java`
**Interfaces:**
- Produces: `HistoryItem(String index, String query, String filePath, String count, String timestamp)`.
- Produces: `fileDisplay(int maxLength)` for bounded single-line display.
- [ ] Write tests proving short paths remain unchanged, long paths are ellipsized while retaining the filename, and constructor values cannot be null.
- [ ] Run `.\mvnw.cmd -q -Dtest=HistoryItemTest test` and observe compilation failure because `HistoryItem` is absent.
- [ ] Implement the immutable record and its display method.
- [ ] Run the focused test and confirm it passes.
### Task 2: Reusable empty state
**Files:**
- Create: `src/main/java/com/example/workutils/system/ui/EmptyStatePane.java`
- Modify: `src/main/resources/css/light-theme.css`
- Test: `src/test/java/com/example/workutils/system/ui/EmptyStateContentTest.java`
**Interfaces:**
- Produces: `EmptyStatePane.Content(String title, String description, String actionLabel)`.
- Produces: `EmptyStatePane(Content content, Runnable action, int fontSize)`.
- [ ] Write pure content-validation tests proving title is required and a blank action label is allowed only when no action is rendered.
- [ ] Run the focused test and observe failure because the component is absent.
- [ ] Implement the content model, pane, optional action button, and theme classes.
- [ ] Run the focused test and confirm it passes.
### Task 3: Reusable export history pane
**Files:**
- Create: `src/main/java/com/example/workutils/system/ui/history/HistoryFileCommands.java`
- Create: `src/main/java/com/example/workutils/system/ui/history/ExportHistoryPane.java`
- Test: `src/test/java/com/example/workutils/system/ui/history/HistoryFileCommandsTest.java`
**Interfaces:**
- Consumes: `ObservableList<HistoryItem>`.
- Consumes: `HistoryFileCommands` callbacks for open file, open editor, open folder, and copy path.
- Produces: `ExportHistoryPane(String countColumnTitle, ObservableList<HistoryItem> items, HistoryFileCommands commands, int fontSize)`.
- [ ] Write tests proving `HistoryFileCommands` rejects missing callbacks and routes each command to the supplied consumer.
- [ ] Run the focused test and observe failure because the type is absent.
- [ ] Implement callback validation and the shared table, action cell, tooltip, context menu, and empty-state placeholder.
- [ ] Run the focused test and confirm it passes.
### Task 4: Migrate Scanner
**Files:**
- Modify: `src/main/java/com/example/workutils/biz/scanner/ScannerWindow.java`
- Test: `src/test/java/com/example/workutils/biz/scanner/ScannerHistoryMappingTest.java`
**Interfaces:**
- Consumes: `HistoryItem`, `ExportHistoryPane`, `HistoryFileCommands`.
- Produces: package-visible `toHistoryItem(HistoryRow row)` for deterministic presentation mapping.
- [ ] Write a mapping test for index, query, path, match count, and timestamp.
- [ ] Run the focused test and observe failure because the mapping method is absent.
- [ ] Replace duplicated table/action-cell construction with `ExportHistoryPane`.
- [ ] Run scanner tests and confirm they pass.
### Task 5: Migrate MyBatis SQL Export
**Files:**
- Modify: `src/main/java/com/example/workutils/biz/mybatisexport/MyBatisExportWindow.java`
- Test: `src/test/java/com/example/workutils/biz/mybatisexport/MyBatisExportHistoryMappingTest.java`
**Interfaces:**
- Consumes: `HistoryItem`, `ExportHistoryPane`, `HistoryFileCommands`.
- Produces: package-visible `toHistoryItem(HistoryRow row)`.
- [ ] Write a mapping test for index, query, path, export count, and timestamp.
- [ ] Run the focused test and observe failure because the mapping method is absent.
- [ ] Replace duplicated table/action-cell construction with `ExportHistoryPane`.
- [ ] Run export tests and confirm they pass.
### Task 6: Verification
**Files:**
- Modify only files required by failures found during verification.
- [ ] Run `.\mvnw.cmd -q test`.
- [ ] Run `.\mvnw.cmd -q -DskipTests compile`.
- [ ] Run `git diff --check`.
- [ ] Confirm Scanner and MyBatis SQL Export no longer declare `buildActionCell`.
- [ ] Inspect both pages without triggering scan, export, file-open, or delete side effects.