diff --git a/docs/superpowers/plans/2026-08-07-new-product-survey-step3.md b/docs/superpowers/plans/2026-08-07-new-product-survey-step3.md new file mode 100644 index 0000000..348d85c --- /dev/null +++ b/docs/superpowers/plans/2026-08-07-new-product-survey-step3.md @@ -0,0 +1,213 @@ +# 新产品调查与分析第三步 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:** 将新产品调查与分析第三步改为单产品三层次分析表,并在进入第四步前校验每层至少两条要点。 + +**Architecture:** 前端将旧的三产品对比行转换成固定三层分析行,展示 AI 学习机参考并将第二步的产品名称作为分析列标题。学生输入以换行文本编辑、保存为 `rows[].points` 数组;后端提供无状态的完整度校验接口,答案本体继续由既有学生实训答案服务保存到 `step3Answer`。 + +**Tech Stack:** Vue 3 Composition API、Element Plus、Vite、RuoYi request 封装、Spring Boot、JUnit 5、Mockito。 + +## Global Constraints + +- 仅调整学生端 `new-product-survey` 的第三步与对应学生校验接口;不调整教师端、管理端、前两步或第四步。 +- 保留当前任务配置的 `materialUrl` 下载入口和固定“下载案例资料”文案。 +- `step3Answer` 必须继续使用已有学生答案表保存,不新增表。 +- 核心层、有形层、延伸层每层至少两条非空、换行分隔的要点。 +- 演示模式执行同一套本地校验,不调用新增后端接口。 + +--- + +### Task 1: 后端第三步完整度校验 + +**Files:** +- Create: `E:/workspace/dianshang/link_commerce/.worktrees/new-product-survey-step3/src/main/java/com/sztzjy/linkCommerce/entity/dto/NewProductSurveyStepThreeValidationRequest.java` +- Create: `E:/workspace/dianshang/link_commerce/.worktrees/new-product-survey-step3/src/main/java/com/sztzjy/linkCommerce/entity/dto/NewProductSurveyStepThreeValidationResult.java` +- Create: `E:/workspace/dianshang/link_commerce/.worktrees/new-product-survey-step3/src/main/java/com/sztzjy/linkCommerce/service/NewProductSurveyStepThreeService.java` +- Create: `E:/workspace/dianshang/link_commerce/.worktrees/new-product-survey-step3/src/main/java/com/sztzjy/linkCommerce/service/impl/NewProductSurveyStepThreeServiceImpl.java` +- Modify: `E:/workspace/dianshang/link_commerce/.worktrees/new-product-survey-step3/src/main/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerController.java` +- Test: `E:/workspace/dianshang/link_commerce/.worktrees/new-product-survey-step3/src/test/java/com/sztzjy/linkCommerce/service/impl/NewProductSurveyStepThreeServiceImplTest.java` +- Test: `E:/workspace/dianshang/link_commerce/.worktrees/new-product-survey-step3/src/test/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerControllerTest.java` + +**Interfaces:** +- Consumes: `POST /api/student-training-answers/new-product-survey/step-3/validate` with `rows: Array<{ dimension: String, points: List }>` and `JwtUser`. +- Produces: `NewProductSurveyStepThreeService.validate(NewProductSurveyStepThreeValidationRequest, JwtUser)` that returns `{ valid: true, message: "校验通过" }` or a 4xx `ServiceException`. + +- [ ] **Step 1: Write the failing service tests** + +```java +@Test +void validatesThreeDimensionsWithTwoPointsEach() { + NewProductSurveyStepThreeValidationResult result = service.validate(completeRequest(), student()); + assertTrue(result.isValid()); +} + +@Test +void rejectsAOnePointRowDuplicateDimensionAndNonStudent() { + assertThrows(ServiceException.class, () -> service.validate(request(row("核心层", "仅一条"), tangible(), extended()), student())); + assertThrows(ServiceException.class, () -> service.validate(request(core(), core(), extended()), student())); + assertThrows(ServiceException.class, () -> service.validate(completeRequest(), teacher())); +} +``` + +- [ ] **Step 2: Run the service test to verify it fails** + +Run: `mvn -B '-Dtest=NewProductSurveyStepThreeServiceImplTest' test` + +Expected: FAIL because the step-three request, result, and service do not exist. + +- [ ] **Step 3: Implement the DTOs and service** + +```java +public interface NewProductSurveyStepThreeService { + NewProductSurveyStepThreeValidationResult validate(NewProductSurveyStepThreeValidationRequest request, JwtUser user); +} + +// Require roleId == 4; exactly one 核心层, 有形层, 延伸层 row; +// trim and discard blank points; each expected row needs at least two remaining points. +``` + +- [ ] **Step 4: Add the controller endpoint and controller test** + +```java +@PostMapping("/new-product-survey/step-3/validate") +public ResultEntity validateNewProductSurveyStepThree( + @RequestBody NewProductSurveyStepThreeValidationRequest answer, HttpServletRequest request) { + return new ResultEntity<>(HttpStatus.OK, "校验通过", newProductSurveyStepThreeService.validate(answer, TokenProvider.getJWTUser(request))); +} +``` + +Mock the service only at the controller boundary; assert the real controller returns HTTP 200 with the validation result for an authenticated student request. + +- [ ] **Step 5: Run focused backend tests** + +Run: `mvn -B '-Dtest=NewProductSurveyStepThreeServiceImplTest,StudentTrainingAnswerControllerTest' test` + +Expected: PASS with zero failures. + +- [ ] **Step 6: Commit backend changes** + +```bash +git add src/main/java src/test/java +git commit -m "feat: validate new product survey step three" +``` + +### Task 2: 学生端单产品分析表与持久化 + +**Files:** +- Modify: `E:/workspace/dianshang/e-commerce-internet/.worktrees/new-product-survey-step3/src/api/studentTrainingAnswer.js` +- Modify: `E:/workspace/dianshang/e-commerce-internet/.worktrees/new-product-survey-step3/src/views/foundation/new-product-survey.vue` +- Modify: `E:/workspace/dianshang/e-commerce-internet/.worktrees/new-product-survey-step3/tests/new-product-survey-step-one.static.test.cjs` + +**Interfaces:** +- Consumes: `checkNewProductSurveyStepThree({ rows })` and second-step `productName`. +- Produces: `step3Answer` JSON `{ rows: Array<{ dimension: String, points: String[] }> }`; advances to step four only after the third-step validation call resolves. + +- [ ] **Step 1: Write failing frontend contract assertions** + +```js +assert.match(page, /const STEP_THREE_REFERENCE_ROWS = \[/, "step three must retain the fixed AI learning-device reference"); +assert.match(page, /step2Form\.productName \|\| '所选产品'/, "the analysis header must reflect the selected product"); +assert.match(page, /countStepThreePoints\(row\.analysis\)/, "each row must show its entered-point count"); +assert.match(page, /checkNewProductSurveyStepThree\(buildStepThreeValidationPayload\(\)\)/, "step three must validate before advancing"); +assert.match(api, /new-product-survey\/step-3\/validate/, "the API must expose the step-three validation route"); +``` + +- [ ] **Step 2: Run the frontend contract test to verify it fails** + +Run: `node tests/new-product-survey-step-one.static.test.cjs` + +Expected: FAIL because the page still uses `createStep3Rows()` as a three-product comparison table and the third-step API does not exist. + +- [ ] **Step 3: Add the API client and demonstration-mode validator** + +```js +export function checkNewProductSurveyStepThree(payload) { + if (isStudentDemo()) return Promise.resolve({ code: 200, data: validateNewProductSurveyStepThreeDemo(payload) }); + return request({ + url: '/api/student-training-answers/new-product-survey/step-3/validate', + method: 'post', + data: payload, + headers: { repeatSubmit: false }, + }); +} +``` + +The demo validator must require the same three dimensions and at least two trimmed points per row, rejecting invalid input with `Error`. + +- [ ] **Step 4: Replace the comparison table with the prototype-aligned table** + +```vue +{{ step2Form.productName || '所选产品' }}分析 + + {{ row.dimension }}{{ row.definition }} +
  1. {{ point }}
+