9.2 KiB
Dynamic Training Steps 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: Teachers can manage arbitrary task steps; students can fill, save, and submit every added custom step without losing specialised built-in forms.
Architecture: TrainingTask.steps becomes a stable object array while retaining JSON text storage. The backend keeps legacy answers and adds a JSON map for custom-step answers. One reusable student workspace and launcher makes custom steps reachable from every task route.
Tech Stack: Vue 3, Element Plus, Spring Boot 2.7, MyBatis, JUnit 5, Mockito.
Global Constraints
- Built-in task count remains fixed; step count is dynamic.
- Preserve legacy string-array steps and step1 through step4 answers.
- Each new step has a stable ID and
kind: "custom"; a task must retain at least one step. - Built-in specialised forms follow step IDs, never array positions.
Task 1: Dynamic step model in the backend
Files:
- Create:
src/main/java/com/sztzjy/linkCommerce/entity/TrainingTaskStep.java - Modify:
src/main/java/com/sztzjy/linkCommerce/service/impl/TrainingTaskServiceImpl.java - Modify:
src/test/java/com/sztzjy/linkCommerce/service/impl/TrainingTaskServiceImplTest.java
Interfaces:
-
Consumes: legacy arrays such as
["调研","分析"]and object arrays such as[{"id":"custom-1","name":"补充分析","kind":"custom"}]. -
Produces: ordered
{id,name,kind}JSON arrays with no four-step padding or truncation. -
Step 1: Write the failing test
request.setSteps("[{\"id\":\"builtin-1\",\"name\":\"调研\",\"kind\":\"builtin\"},{\"id\":\"custom-1\",\"name\":\"补充分析\",\"kind\":\"custom\"},{\"id\":\"custom-2\",\"name\":\"成果复盘\",\"kind\":\"custom\"}]");
TrainingTask saved = service.update("task-id", request);
assertThat(saved.getSteps()).contains("custom-1", "custom-2", "补充分析", "成果复盘");
- Step 2: Verify the test fails
Run: mvn -Dtest=TrainingTaskServiceImplTest test
Expected: FAIL because normalizeFixedSteps limits entries to the built-in count.
- Step 3: Implement the normalizer
private String normalizeSteps(String value, TrainingTask builtInTask) {
List<TrainingTaskStep> steps = parseSteps(value, builtInTask);
if (steps.isEmpty()) throw new IllegalArgumentException("至少保留一个步骤");
return JSON.toJSONString(steps);
}
parseSteps must give historical string values deterministic legacy-<position> IDs, preserve valid IDs and kind, generate IDs for blank new IDs, and replace every create/update/import/class-save call to normalizeFixedSteps.
- Step 4: Verify and commit
Run: mvn -Dtest=TrainingTaskServiceImplTest test
Expected: PASS.
git add src/main/java/com/sztzjy/linkCommerce/service/impl/TrainingTaskServiceImpl.java src/test/java/com/sztzjy/linkCommerce/service/impl/TrainingTaskServiceImplTest.java
git commit -m "feat: support dynamic training task steps"
Task 2: Dynamic student-answer persistence
Files:
- Modify:
src/main/java/com/sztzjy/linkCommerce/entity/StudentTrainingAnswer.java - Modify:
src/main/java/com/sztzjy/linkCommerce/service/impl/StudentTrainingAnswerServiceImpl.java - Modify:
src/test/java/com/sztzjy/linkCommerce/service/impl/StudentTrainingAnswerServiceImplTest.java
Interfaces:
-
Consumes:
dynamicStepAnswersJSON keyed by stable step ID. -
Produces: the same JSON map from the current student-answer API;
currentStepcan exceed four. -
Step 1: Write the failing test
request.setCurrentStep(6);
request.setDynamicStepAnswers("{\"custom-1\":{\"content\":\"补充调研结论\",\"submitted\":true}}");
StudentTrainingAnswer saved = service.save("new-product-survey", request, student());
assertEquals(6, saved.getCurrentStep());
assertThat(saved.getDynamicStepAnswers()).contains("custom-1", "补充调研结论");
- Step 2: Verify the test fails
Run: mvn -Dtest=StudentTrainingAnswerServiceImplTest test
Expected: FAIL because the model lacks dynamicStepAnswers and caps the current step at four.
- Step 3: Implement model and migration
Add dynamicStepAnswers to the entity, add dynamic_step_answers longtext NULL via the table-ensure migration, preserve it in buildRecord, clear it for RESET, validate JSON-object values, and remove the fixed currentStep upper bound. Keep the four legacy answer fields unchanged.
- Step 4: Verify and commit
Run: mvn -Dtest=StudentTrainingAnswerServiceImplTest test
Expected: PASS.
git add src/main/java/com/sztzjy/linkCommerce/entity/StudentTrainingAnswer.java src/main/java/com/sztzjy/linkCommerce/service/impl/StudentTrainingAnswerServiceImpl.java src/test/java/com/sztzjy/linkCommerce/service/impl/StudentTrainingAnswerServiceImplTest.java
git commit -m "feat: persist dynamic student step answers"
Task 3: Dynamic teacher and management editors
Files:
- Modify:
../e-commerce-internet/src/views/teacherEnd/trainingTask/index.vue - Modify:
../e-commerce-internet/src/views/schoolAdmin/trainingTask/index.vue
Interfaces:
-
Consumes: legacy step strings or
{id,name,kind}objects. -
Produces: non-empty object-array JSON in the existing save payload.
-
Step 1: Create the failing browser check
Open each editor; add a fifth row, rename it, delete a different row, and assert the final remaining row cannot be deleted.
- Step 2: Verify current behavior fails
Expected: four static inputs with no add/delete controls, and step five is discarded on save.
- Step 3: Implement reusable row behavior in both editors
function createStep(name = "", kind = "custom") {
return { id: crypto.randomUUID(), name, kind }
}
function addStep() { taskForm.steps.push(createStep()) }
function removeStep(index) { if (taskForm.steps.length > 1) taskForm.steps.splice(index, 1) }
Bind each input to taskForm.steps[index].name, show a delete control per row, add + 新增步骤, serialize valid rows as objects, and remove copy claiming a fixed step count.
- Step 4: Verify and commit
Use the browser to save and reopen a five-step task in both editors; assert ordering and IDs persist.
git -C ../e-commerce-internet add src/views/teacherEnd/trainingTask/index.vue src/views/schoolAdmin/trainingTask/index.vue
git -C ../e-commerce-internet commit -m "feat: edit training task steps dynamically"
Task 4: Student custom-step workspace
Files:
- Create:
../e-commerce-internet/src/views/training/DynamicTrainingStepPage.vue - Create:
../e-commerce-internet/src/views/components/DynamicTrainingStepLauncher.vue - Modify:
../e-commerce-internet/src/router/index.js - Modify:
../e-commerce-internet/src/layout/components/AppMain.vue - Modify:
../e-commerce-internet/src/api/studentTrainingAnswer.js
Interfaces:
-
Consumes: route params
{taskKey, stepId}, custom step objects, anddynamicStepAnswers. -
Produces:
dynamicStepAnswers[stepId] = {content, attachment, savedAt, submitted}through the existing student-answer endpoint. -
Step 1: Create the failing browser flow
With a custom step configured, assert its launcher is visible on the student task, its route has text and attachment controls, and a saved value survives reload.
- Step 2: Verify current behavior fails
Expected: no custom-step launcher, route, or dynamic answer payload.
- Step 3: Implement the shared workspace
function buildPayload(action) {
return {
dynamicStepAnswers: JSON.stringify({
...answers.value,
[route.params.stepId]: { content: form.content, attachment: form.attachment, submitted: action === "SUBMIT", savedAt: new Date().toISOString() }
}),
currentStep: selectedStepIndex.value,
saveAction: action
}
}
The route must reject missing or built-in step IDs. The workspace must use the existing student upload endpoint, offer separate save and submit actions, and restore prior text and attachment metadata. The global launcher lists custom steps once for every student task route, leaving built-in specialised pages unchanged.
- Step 4: Verify and commit
Create a custom teacher step, then as a student save text and a document, reload, and assert both remain. Confirm an existing built-in task page remains usable.
git -C ../e-commerce-internet add src/views/training/DynamicTrainingStepPage.vue src/views/components/DynamicTrainingStepLauncher.vue src/router/index.js src/layout/components/AppMain.vue src/api/studentTrainingAnswer.js
git -C ../e-commerce-internet commit -m "feat: add student custom training step workspace"
Task 5: Regression verification
Files:
-
Modify only failed regression tests from Tasks 1 and 2.
-
Step 1: Run backend suite
Run: mvn test
Expected: all tests pass.
- Step 2: Build frontend
Run: npm run build:prod
Expected: production build completes without Vue compilation errors.
- Step 3: Verify in browser
Verify add, rename, and delete in both editors; student custom-step save/reload/submit; and the original specialised forms on two existing tasks.