Files
yidaima/RuoYi-Vue/docs/superpowers/plans/2026-07-06-admin-home-dashboard-metrics.md

429 lines
15 KiB
Markdown
Raw Blame History

# Admin Home Dashboard Metrics 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:** Generate an admin home dashboard with metric cards, semantic icons, and chart blocks from `AppBlueprintDesign.adminHomeDashboard`, falling back to the main business table when needed.
**Architecture:** Add dashboard DTOs to the app blueprint, a focused `AdminHomeDashboardComposer` that converts blueprint/fallback intent into existing `business-blocks-v1` chart block instances, and wire it into admin page initialization. Reuse `BusinessBlockRegistryService`, `ChartDatasetValidator`, existing chart business block definitions, and existing Velocity chart generation templates.
**Tech Stack:** Java 8, JUnit 4, Mockito, Fastjson2, Jackson, RuoYi generator services, Velocity templates, Vue 2/Element UI.
---
## File Structure
- Create `ruoyi-generator/src/main/java/com/ruoyi/generator/domain/front/dto/AdminHomeDashboardDesign.java`: top-level dashboard blueprint model.
- Create `ruoyi-generator/src/main/java/com/ruoyi/generator/domain/front/dto/AdminHomeMetricDesign.java`: metric card blueprint model.
- Create `ruoyi-generator/src/main/java/com/ruoyi/generator/domain/front/dto/AdminHomeChartDesign.java`: chart blueprint model.
- Modify `ruoyi-generator/src/main/java/com/ruoyi/generator/domain/front/dto/AppBlueprintDesign.java`: add `adminHomeDashboard`.
- Create `ruoyi-generator/src/main/java/com/ruoyi/generator/service/front/AdminHomeDashboardComposer.java`: compose dashboard layout from blueprint and table metadata.
- Modify `ruoyi-generator/src/main/java/com/ruoyi/generator/service/front/FrontendPageDesignService.java`: call composer for `admin_home`.
- Modify `ruoyi-generator/src/main/java/com/ruoyi/generator/service/front/ChartDatasetValidator.java`: allow spans `3` and `8`.
- Modify `ruoyi-generator/src/main/java/com/ruoyi/generator/service/front/AiGenerateServiceImpl.java`: prompt AI to return `adminHomeDashboard`.
- Modify `ruoyi-generator/src/main/resources/business-blocks/page/business-block-page.vue.vm`: add grid classes for `span=3` and `span=8`.
- Modify `ruoyi-generator/src/main/resources/business-blocks/chart/templates/frontend/AdminChartBlock.vue.vm`: render metric blocks as polished indicator cards with icon/theme/unit support.
- Test `ruoyi-generator/src/test/java/com/ruoyi/generator/service/front/AdminHomeDashboardComposerTest.java`.
- Modify tests in `ChartDatasetValidatorTest`, `FrontendPageDesignServiceTest`, `AiGenerateServiceImplTest`, `QingTemplateSupportTest`, and `BusinessBlockGenerationServiceTest`.
## Task 1: Allow Dashboard Grid Spans
**Files:**
- Modify: `ruoyi-generator/src/main/java/com/ruoyi/generator/service/front/ChartDatasetValidator.java`
- Modify: `ruoyi-generator/src/test/java/com/ruoyi/generator/service/front/ChartDatasetValidatorTest.java`
- Modify: `ruoyi-generator/src/main/resources/business-blocks/page/business-block-page.vue.vm`
- Modify: `ruoyi-generator/src/test/java/com/ruoyi/generator/util/QingTemplateSupportTest.java`
- [ ] **Step 1: Write failing validator test**
Add this test to `ChartDatasetValidatorTest`:
```java
@Test
public void chartValidationAcceptsDashboardSpans()
{
BusinessBlockInstance metric = validLineChartInstance();
metric.setSpan(Integer.valueOf(3));
validator.validate(lineChartDefinition(), metric, chartTables());
BusinessBlockInstance wide = validLineChartInstance();
wide.setSpan(Integer.valueOf(8));
validator.validate(lineChartDefinition(), wide, chartTables());
}
```
- [ ] **Step 2: Run failing validator test**
Run:
```bash
mvn -pl ruoyi-generator -Dtest=ChartDatasetValidatorTest#chartValidationAcceptsDashboardSpans test
```
Expected: FAIL because span `3` is rejected.
- [ ] **Step 3: Implement span support**
Change `ChartDatasetValidator.SPANS` to:
```java
private static final Set<Integer> SPANS =
new HashSet<Integer>(Arrays.asList(Integer.valueOf(12), Integer.valueOf(8),
Integer.valueOf(6), Integer.valueOf(4), Integer.valueOf(3)));
```
Update the span error message to mention `12<31><32>?<3F><>?<3F><>?<3F><>?`.
- [ ] **Step 4: Add template test and implementation**
Add assertions to `QingTemplateSupportTest.businessBlockPageTemplateUsesLocalizedPolishedEmptyState()`:
```java
assertTrue(content.contains("business-block-page__item--span-3"));
assertTrue(content.contains("business-block-page__item--span-8"));
```
Run:
```bash
mvn -pl ruoyi-generator -Dtest=QingTemplateSupportTest#businessBlockPageTemplateUsesLocalizedPolishedEmptyState test
```
Expected: FAIL until `business-block-page.vue.vm` contains:
```css
.business-block-page__item--span-8 { grid-column: span 8; }
.business-block-page__item--span-3 { grid-column: span 3; }
```
- [ ] **Step 5: Verify task**
Run:
```bash
mvn -pl ruoyi-generator -Dtest=ChartDatasetValidatorTest,QingTemplateSupportTest test
```
Expected: PASS for touched tests.
## Task 2: Add Dashboard Blueprint DTOs and AI Prompt
**Files:**
- Create: `ruoyi-generator/src/main/java/com/ruoyi/generator/domain/front/dto/AdminHomeDashboardDesign.java`
- Create: `ruoyi-generator/src/main/java/com/ruoyi/generator/domain/front/dto/AdminHomeMetricDesign.java`
- Create: `ruoyi-generator/src/main/java/com/ruoyi/generator/domain/front/dto/AdminHomeChartDesign.java`
- Modify: `ruoyi-generator/src/main/java/com/ruoyi/generator/domain/front/dto/AppBlueprintDesign.java`
- Modify: `ruoyi-generator/src/main/java/com/ruoyi/generator/service/front/AiGenerateServiceImpl.java`
- Modify: `ruoyi-generator/src/test/java/com/ruoyi/generator/service/front/AiGenerateServiceImplTest.java`
- [ ] **Step 1: Write failing AI blueprint test**
Extend `generateAppBlueprintNormalizesAndPersistsStaticMenus()`:
```java
assertTrue(promptCaptor.getValue().contains("adminHomeDashboard"));
assertEquals("运营总览", blueprint.getAdminHomeDashboard().getTitle());
assertEquals("order_count", blueprint.getAdminHomeDashboard().getMetrics().get(0).getCode());
assertEquals("sales_trend", blueprint.getAdminHomeDashboard().getCharts().get(0).getCode());
assertTrue(projectCaptor.getValue().getAppBlueprint().contains("adminHomeDashboard"));
```
Extend `appBlueprintResponse()` with a valid `adminHomeDashboard` object that contains one metric and one chart.
- [ ] **Step 2: Run failing AI test**
Run:
```bash
mvn -pl ruoyi-generator -Dtest=AiGenerateServiceImplTest#generateAppBlueprintNormalizesAndPersistsStaticMenus test
```
Expected: FAIL because `AppBlueprintDesign` has no dashboard property or the prompt does not mention it.
- [ ] **Step 3: Add DTOs**
Use these public fields through getters/setters:
```java
public class AdminHomeDashboardDesign {
private String title;
private String description;
private List<AdminHomeMetricDesign> metrics = new ArrayList<AdminHomeMetricDesign>();
private List<AdminHomeChartDesign> charts = new ArrayList<AdminHomeChartDesign>();
}
```
```java
public class AdminHomeMetricDesign {
private String code;
private String title;
private String tableName;
private String aggregate;
private String field;
private List<ChartFilterConfig> filters = new ArrayList<ChartFilterConfig>();
private ChartDateRangeConfig dateRange;
private String semantic;
private String unit;
private String prefix;
private String suffix;
private Integer precision;
}
```
```java
public class AdminHomeChartDesign {
private String code;
private String title;
private String type;
private String tableName;
private ChartDimensionConfig dimension;
private List<ChartMetricConfig> metrics = new ArrayList<ChartMetricConfig>();
private List<ChartFilterConfig> filters = new ArrayList<ChartFilterConfig>();
private ChartDateRangeConfig dateRange;
private ChartSortConfig sort;
private Integer limit;
private String semantic;
}
```
Add `private AdminHomeDashboardDesign adminHomeDashboard;` to `AppBlueprintDesign`.
- [ ] **Step 4: Update prompt**
In `buildAppBlueprintPrompt`, include `adminHomeDashboard` in the JSON shape and add constraints:
```java
prompt.append("Also return adminHomeDashboard for the generated backend home page. ");
prompt.append("It must include 4 business metrics when possible and at least 2 charts: one trend line/bar chart and one distribution pie/bar chart. ");
prompt.append("Use semantic values only for dashboard icons: money, order, user, task, warning, inventory, content, default. Do not output Element UI icon class names.\\n");
```
- [ ] **Step 5: Verify task**
Run:
```bash
mvn -pl ruoyi-generator -Dtest=AiGenerateServiceImplTest#generateAppBlueprintNormalizesAndPersistsStaticMenus test
```
Expected: PASS.
## Task 3: Compose Dashboard Layout From Blueprint
**Files:**
- Create: `ruoyi-generator/src/main/java/com/ruoyi/generator/service/front/AdminHomeDashboardComposer.java`
- Create: `ruoyi-generator/src/test/java/com/ruoyi/generator/service/front/AdminHomeDashboardComposerTest.java`
- [ ] **Step 1: Write failing composer test for complete blueprint**
Test should create an `admin_home` design, an app blueprint with 4 metrics and 2 charts, and a `shop_order` table with id, amount, status, and create time columns. Assert that:
```java
assertTrue(composer.compose(homeDesign, blueprint, tables(), null));
assertTrue(homeDesign.getLayoutJson().contains("\"dashboard\""));
assertTrue(homeDesign.getLayoutJson().contains("\"blockCode\":\"admin_metric_chart\""));
assertTrue(homeDesign.getLayoutJson().contains("\"span\":3"));
assertTrue(homeDesign.getLayoutJson().contains("\"blockCode\":\"admin_line_chart\""));
assertTrue(homeDesign.getLayoutJson().contains("\"span\":8"));
assertTrue(homeDesign.getLayoutJson().contains("\"icon\":\"el-icon-s-order\""));
```
- [ ] **Step 2: Run failing composer test**
Run:
```bash
mvn -pl ruoyi-generator -Dtest=AdminHomeDashboardComposerTest#composeUsesBlueprintMetricsAndCharts test
```
Expected: FAIL because the class does not exist.
- [ ] **Step 3: Implement composer core**
Implement:
```java
public boolean compose(FrontProjectPageDesign homeDesign, AppBlueprintDesign blueprint,
Collection<FrontProjectTable> sourceTables, BusinessLoopPlan loopPlan)
```
Key helpers:
- `metricBlock(AdminHomeMetricDesign metric, int index)`
- `chartBlock(AdminHomeChartDesign chart, int index)`
- `validate(BusinessBlockInstance instance, List<GenTable> genTables)`
- `display(String title, String semantic, AdminHomeMetricDesign metric)`
- `iconFor(String semantic)` and `themeFor(String semantic)`
- `safeCode(String value, String fallback)`
Use block codes `admin_metric_chart`, `admin_line_chart`, `admin_bar_chart`, `admin_pie_chart` and IDs with `auto_admin_metric_` or `auto_admin_chart_` prefixes.
- [ ] **Step 4: Add failing fallback test**
Test no dashboard blueprint and a single `shop_order` table. Assert composer still produces total records, today added, pending, abnormal, trend, and distribution blocks.
- [ ] **Step 5: Implement fallback**
Select the main table, find primary key/id field, date field, status field, and amount field. Build:
- `total_count`
- `today_created`
- `pending_count`
- `abnormal_count`
- `created_trend`
- `status_distribution`
- [ ] **Step 6: Verify task**
Run:
```bash
mvn -pl ruoyi-generator -Dtest=AdminHomeDashboardComposerTest test
```
Expected: PASS.
## Task 4: Wire Composer Into Admin Page Initialization
**Files:**
- Modify: `ruoyi-generator/src/main/java/com/ruoyi/generator/service/front/FrontendPageDesignService.java`
- Modify: `ruoyi-generator/src/test/java/com/ruoyi/generator/service/front/FrontendPageDesignServiceTest.java`
- [ ] **Step 1: Write failing service test**
Add a test that initializes admin page designs with `projectWithAdminDashboard()` and `orderTable()`. Assert:
```java
FrontendPageDesignInitResponse response = service.initializePageDesigns(7L, 10L, "admin");
assertEquals("admin_home", response.getDesigns().get(0).getPageCode());
assertTrue(response.getDesigns().get(0).getLayoutJson().contains("admin_metric_chart"));
assertTrue(response.getDesigns().get(0).getLayoutJson().contains("admin_line_chart"));
verify(pageDesignMapper, times(2)).insertFrontProjectPageDesign(any(FrontProjectPageDesign.class));
verify(pageDesignMapper).updateFrontProjectPageDesign(any(FrontProjectPageDesign.class));
```
- [ ] **Step 2: Run failing service test**
Run:
```bash
mvn -pl ruoyi-generator -Dtest=FrontendPageDesignServiceTest#initializeAdminHomeComposesDashboardFromBlueprint test
```
Expected: FAIL because the service does not call the composer.
- [ ] **Step 3: Wire composer**
Add:
```java
@Autowired
private AdminHomeDashboardComposer adminHomeDashboardComposer;
```
Add a lazy fallback method like existing `homeBusinessBlockComposer()`. In `initializeAdminPageDesigns`, after `getOrCreateHomeDesign`, call:
```java
if (adminHomeDashboardComposer().compose(homeDesign, blueprint, frontTables(tablesByName), loopPlan))
{
pageDesignMapper.updateFrontProjectPageDesign(homeDesign);
}
```
- [ ] **Step 4: Verify task**
Run:
```bash
mvn -pl ruoyi-generator -Dtest=FrontendPageDesignServiceTest#initializeAdminHomeComposesDashboardFromBlueprint test
```
Expected: PASS.
## Task 5: Render Metric Cards With Icons and Theme
**Files:**
- Modify: `ruoyi-generator/src/main/resources/business-blocks/chart/templates/frontend/AdminChartBlock.vue.vm`
- Modify: `ruoyi-generator/src/test/java/com/ruoyi/generator/service/BusinessBlockGenerationServiceTest.java`
- [ ] **Step 1: Write failing template generation test**
Extend the existing chart component generation test or add one using an `admin_metric_chart` instance with:
```json
"display":{"title":"Order Count","semantic":"order","icon":"el-icon-s-order","theme":"green","unit":"<22><>?}
```
Assert generated component contains:
```java
assertTrue(component.contains("admin-chart-block--metric"));
assertTrue(component.contains("display.icon"));
assertTrue(component.contains("metricUnit"));
assertTrue(component.contains("themeClass"));
```
- [ ] **Step 2: Run failing generation test**
Run:
```bash
mvn -pl ruoyi-generator -Dtest=BusinessBlockGenerationServiceTest test
```
Expected: FAIL until the template includes metric card markup/helpers.
- [ ] **Step 3: Update template**
For `chartType === "metric"`, render a dedicated metric card with:
- icon square: `<i :class="display.icon || 'el-icon-data-line'"></i>`
- title and optional subtitle
- formatted value with `prefix`, `suffix`, `unit`, and `precision`
- `themeClass` computed from `display.theme`
- [ ] **Step 4: Verify task**
Run:
```bash
mvn -pl ruoyi-generator -Dtest=BusinessBlockGenerationServiceTest test
```
Expected: PASS.
## Task 6: Final Verification
**Files:**
- All touched files.
- [ ] **Step 1: Run focused tests**
Run:
```bash
mvn -pl ruoyi-generator -Dtest=AdminHomeDashboardComposerTest,FrontendPageDesignServiceTest,ChartDatasetValidatorTest,BusinessBlockGenerationServiceTest,QingTemplateSupportTest,AiGenerateServiceImplTest test
```
Expected: PASS.
- [ ] **Step 2: Run compile**
Run:
```bash
mvn -pl ruoyi-generator -am -DskipTests compile
```
Expected: PASS.
- [ ] **Step 3: Review diffs**
Run:
```bash
git diff -- RuoYi-Vue/ruoyi-generator RuoYi-Vue/docs/superpowers/plans/2026-07-06-admin-home-dashboard-metrics.md
```
Expected: Only dashboard implementation, tests, templates, and plan changes.