feat: expand EasyCode software factory workflows

This commit is contained in:
王鹏
2026-07-15 12:48:50 +08:00
parent fcfa4374d7
commit 79dea897bc
1108 changed files with 163774 additions and 21593 deletions

View File

@@ -0,0 +1,276 @@
# Qing Business Action Runtime 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 runnable Qing backend endpoints and transactional DSL execution for portal business-action buttons.
**Architecture:** Keep actions table-owned, render their absolute blueprint paths in the owning controller, and execute validated rules/effects in the owning MyBatis-Plus service. Resolve the authenticated portal user in the controller and collect unresolved request fields in the Vue template.
**Tech Stack:** Java 8, Spring Boot 2.5, MyBatis-Plus, JdbcTemplate, Velocity, Vue 2, Element UI, JUnit 4.
---
### Task 1: Add Failing Qing Template Tests
**Files:**
- Modify: `ruoyi-generator/src/test/java/com/ruoyi/generator/util/QingTemplateSupportTest.java`
- Modify: `ruoyi-generator/src/test/java/com/ruoyi/generator/service/GenProjectServiceImplTest.java`
- [ ] **Step 1: Add a rendered backend-action test**
Create a project with a `borrow_book` action owned by `demo_product`, render the Qing controller, service interface, and service implementation, and assert:
```java
assertTrue(controller.contains("@PostMapping(\"/library/borrow/borrow\")"));
assertTrue(controller.contains("params.put(\"__currentUserId\", currentUser.getUserId())"));
assertTrue(service.contains("int borrow_book(Map<String, Object> params)"));
assertTrue(implementation.contains("@Transactional(rollbackFor = Exception.class)"));
assertTrue(implementation.contains("resolveBusinessValue"));
assertTrue(implementation.contains("\"${current_user.id}\""));
```
- [ ] **Step 2: Add a rendered frontend parameter-collection test**
Assert the rendered Vue page contains:
```java
assertTrue(page.contains("collectBusinessActionPayload"));
assertTrue(page.contains("this.$prompt"));
assertTrue(page.contains("missingFields"));
assertTrue(page.contains("action.requestFields"));
```
- [ ] **Step 3: Add stale-template fallback tests**
Provide template ID `9201` records whose controller/service/serviceImpl contents do not include business-action markers. Assert preview generation replaces them with bundled templates containing:
```java
assertTrue(content.contains("tableBusinessActions"));
assertTrue(content.contains("executeBusinessAction"));
```
- [ ] **Step 4: Run tests and verify RED**
Run:
```powershell
mvn -pl ruoyi-generator -Dtest=QingTemplateSupportTest,GenProjectServiceImplTest test
```
Expected: failures because Qing backend action rendering, frontend field collection, and service fallbacks do not exist.
### Task 2: Generate Backend Business Actions
**Files:**
- Modify: `ruoyi-generator/src/main/resources/qing/controller.java.vm`
- Modify: `ruoyi-generator/src/main/resources/qing/service.java.vm`
- Modify: `ruoyi-generator/src/main/resources/qing/serviceImpl.java.vm`
- [ ] **Step 1: Give CRUD methods complete route mappings**
Remove the controller-level `@RequestMapping`. Prefix CRUD mappings directly:
```java
@PostMapping("/${moduleName}/${businessName}")
@GetMapping("/${moduleName}/${businessName}/page")
@GetMapping("/${moduleName}/${businessName}/{${pkColumn.javaField}}")
```
This leaves business actions free to use their absolute blueprint paths.
- [ ] **Step 2: Render controller action endpoints**
For each `$tableBusinessActions` item, generate the matching Spring mapping. For POST:
```java
@PostMapping("${action.path}")
public Result<Integer> ${action.code}(@RequestBody(required = false) Map<String, Object> params,
HttpServletRequest request)
{
Map<String, Object> actionParams = params == null
? new LinkedHashMap<String, Object>() : new LinkedHashMap<String, Object>(params);
PortalUser currentUser = tokenStore.getPortalUser(resolveToken(request));
if (currentUser == null) {
return Result.error("请先登录");
}
actionParams.put("__currentUserId", currentUser.getUserId());
try {
return Result.success(${className}Service.${action.code}(actionParams));
} catch (RuntimeException e) {
return Result.error(e.getMessage());
}
}
```
Render equivalent GET, PUT, and DELETE mappings.
- [ ] **Step 3: Render service interface methods**
For each table action:
```java
int ${action.code}(Map<String, Object> params);
```
- [ ] **Step 4: Render transactional service implementations**
Generate one method per action:
```java
@Transactional(rollbackFor = Exception.class)
public int ${action.code}(Map<String, Object> params)
```
Validate `requestFields`, execute `ruleChecks`, and execute `effects`. Reuse deterministic helpers for:
```text
EXISTS, NOT_EXISTS, FIELD_EQUALS, FIELD_NOT_EQUALS, FIELD_IN, NUMBER_GTE
INSERT_ROW, UPDATE_FIELD, UPDATE_FIELDS, SET_STATUS, INCREASE_NUMBER, DECREASE_NUMBER
```
For effects needing conditions, fall back to `action.requestFields` when `conditionFields` is empty.
- [ ] **Step 5: Resolve safe placeholders**
Implement:
```java
if ("${now}".equals(valueExpression)) return new Date();
if ("${current_user.id}".equals(valueExpression)) return params.get("__currentUserId");
if (valueExpression.startsWith("${param.") && valueExpression.endsWith("}")) {
String field = valueExpression.substring(8, valueExpression.length() - 1);
requireBusinessParam(params, field);
return params.get(field);
}
```
- [ ] **Step 6: Run targeted tests**
Run:
```powershell
mvn -pl ruoyi-generator -Dtest=QingTemplateSupportTest test
```
Expected: backend rendering assertions pass.
### Task 3: Collect Missing Frontend Parameters
**Files:**
- Modify: `ruoyi-generator/src/main/resources/qing/index.vue.vm`
- [ ] **Step 1: Keep action metadata**
Continue serializing `requestFields` in `businessActionMap`.
- [ ] **Step 2: Collect unresolved fields before request**
Replace synchronous payload construction with:
```javascript
collectBusinessActionPayload(action, row) {
const payload = this.buildBusinessActionPayload(action, row)
const missingFields = (action.requestFields || []).filter(field => payload[field] === undefined)
return missingFields.reduce((promise, field) => {
return promise.then(() => this.$prompt(
"请输入" + this.formatBusinessFieldLabel(field),
action.name || "业务操作",
{ inputPlaceholder: this.businessFieldPlaceholder(field), confirmButtonText: "确定", cancelButtonText: "取消" }
).then(({ value }) => {
payload[field] = value
}))
}, Promise.resolve()).then(() => payload)
}
```
- [ ] **Step 3: Execute only after collection**
Call `collectBusinessActionPayload(action, row).then(payload => ...)`; cancellation sends no request.
- [ ] **Step 4: Surface request failures**
Add a request catch that displays the backend response message or “操作失败”.
- [ ] **Step 5: Run targeted tests**
Run:
```powershell
mvn -pl ruoyi-generator -Dtest=QingTemplateSupportTest test
```
Expected: frontend parameter-collection assertions pass.
### Task 4: Make Existing Installed Templates Upgrade Safely
**Files:**
- Modify: `ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenProjectServiceImpl.java`
- Modify: `sql/qing_templates.sql`
- Modify: `sql/db.sql`
- [ ] **Step 1: Add bundled service constants**
Add constants for `service.java.vm`, `serviceImpl.java.vm`, and their Qing classpath resources.
- [ ] **Step 2: Add stale-template detection**
For template ID `9201`, replace installed service templates when they lack markers:
```java
service.java.vm -> "$tableBusinessActions"
serviceImpl.java.vm -> "resolveBusinessValue" and "@Transactional"
controller.java.vm -> "executeBusinessAction"
```
- [ ] **Step 3: Synchronize SQL seeds**
Replace the backend controller/service/serviceImpl and frontend-index template records in `sql/qing_templates.sql`, then copy the same records into the runnable-template section of `sql/db.sql`.
- [ ] **Step 4: Run fallback and SQL consistency tests**
Run:
```powershell
mvn -pl ruoyi-generator -Dtest=GenProjectServiceImplTest,QingTemplateSupportTest test
```
Expected: all targeted tests pass.
### Task 5: Generate and Verify the Running Project
**Files:**
- Generated preview workspace only; no source file is manually edited.
- [ ] **Step 1: Run generator tests**
```powershell
mvn -pl ruoyi-generator -Dtest=QingTemplateSupportTest,GenProjectServiceImplTest test
```
- [ ] **Step 2: Run the broader generator suite**
```powershell
mvn -pl ruoyi-generator test
```
- [ ] **Step 3: Rebuild EasyCode**
```powershell
npm run build
```
Run from `easycode-web`.
- [ ] **Step 4: Restart project 105 run preview**
Use the existing `/front/project/105/run-preview` API, wait until the new backend/frontend URLs are ready, and open `/books/2`.
- [ ] **Step 5: Verify borrow and reserve**
Log in as the seeded portal user, click each action, enter the prompted date, and confirm:
- neither request returns 404;
- successful actions create the corresponding record;
- the book availability count changes only for borrowing;
- the UI reports backend validation messages when an action is invalid.