diff --git a/docs/superpowers/plans/2026-08-07-new-product-survey-step4.md b/docs/superpowers/plans/2026-08-07-new-product-survey-step4.md new file mode 100644 index 0000000..dcff84c --- /dev/null +++ b/docs/superpowers/plans/2026-08-07-new-product-survey-step4.md @@ -0,0 +1,232 @@ +# New Product Survey Step Four 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:** Deliver the student-only fourth step that collects three AI application-value analyses and blocks final task submission until all are filled. + +**Architecture:** Keep the existing `StudentTrainingAnswer.step4Answer` JSON persistence and add a stateless student-only validation endpoint. The Vue page retains a local draft and calls the validation endpoint only on final submission; the demo client enforces the same non-empty rule locally. + +**Tech Stack:** Vue 3 Composition API, Element Plus, Vite, Spring Boot, Java 8, JUnit 5, Mockito. + +## Global Constraints + +- Only change the student `new-product-survey` page and its supporting student validation API; do not change teacher, admin, table schema, prior steps, or material download behavior. +- Persist `{ coreValue, tangibleValue, extendedValue }` in the existing `step4Answer` field. +- A value is valid only when `trim()` leaves non-empty text; validation does not score analysis quality. +- Saving a draft remains allowed with empty fields; final `SUBMIT` is guarded by validation. +- Keep legacy fourth-step JSON restoration compatible. +- Do not start the backend automatically; the user starts it manually. + +--- + +### Task 1: Add server-side fourth-step validation + +**Files:** +- Create: `E:/workspace/dianshang/link_commerce/.worktrees/new-product-survey-step3/src/main/java/com/sztzjy/linkCommerce/entity/dto/NewProductSurveyStepFourValidationRequest.java` +- Create: `E:/workspace/dianshang/link_commerce/.worktrees/new-product-survey-step3/src/main/java/com/sztzjy/linkCommerce/entity/dto/NewProductSurveyStepFourValidationResult.java` +- Create: `E:/workspace/dianshang/link_commerce/.worktrees/new-product-survey-step3/src/main/java/com/sztzjy/linkCommerce/service/NewProductSurveyStepFourService.java` +- Create: `E:/workspace/dianshang/link_commerce/.worktrees/new-product-survey-step3/src/main/java/com/sztzjy/linkCommerce/service/impl/NewProductSurveyStepFourServiceImpl.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/NewProductSurveyStepFourServiceImplTest.java` +- Test: `E:/workspace/dianshang/link_commerce/.worktrees/new-product-survey-step3/src/test/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerControllerTest.java` + +**Interfaces:** +- Consumes: authenticated `JwtUser` and `NewProductSurveyStepFourValidationRequest { String coreValue, String tangibleValue, String extendedValue }`. +- Produces: `NewProductSurveyStepFourValidationResult { boolean valid, String message }` from `POST /api/student-training-answers/new-product-survey/step-4/validate`. + +- [ ] **Step 1: Write the failing service tests** + +```java +@Test +void acceptsStudentWithAllThreeNonBlankValues() { + NewProductSurveyStepFourValidationRequest request = request("核心分析", "有形分析", "延伸分析"); + assertTrue(service.validate(request, student()).isValid()); +} + +@ParameterizedTest +@NullAndEmptySource +@ValueSource(strings = {" ", "\t"}) +void rejectsBlankCoreValue(String value) { + assertThrows(ServiceException.class, () -> service.validate(request(value, "有形", "延伸"), student())); +} + +@Test +void rejectsNonStudent() { + assertThrows(ServiceException.class, () -> service.validate(request("核心", "有形", "延伸"), teacher())); +} +``` + +- [ ] **Step 2: Run the service test to verify it fails** + +Run: `mvn -B '-Dtest=NewProductSurveyStepFourServiceImplTest' test` + +Expected: FAIL because fourth-step request and service types do not exist. + +- [ ] **Step 3: Implement DTOs and service** + +```java +public interface NewProductSurveyStepFourService { + NewProductSurveyStepFourValidationResult validate(NewProductSurveyStepFourValidationRequest request, JwtUser user); +} + +private void requireNonBlank(String value, String message) { + if (StringUtils.isBlank(value)) { + throw new ServiceException(HttpStatus.BAD_REQUEST, message); + } +} +``` + +Require the existing student role and user id, then require `coreValue`, `tangibleValue`, and `extendedValue` after trimming. Return `valid=true` and `校验通过` on success. + +- [ ] **Step 4: Run the service test to verify it passes** + +Run: `mvn -B '-Dtest=NewProductSurveyStepFourServiceImplTest' test` + +Expected: PASS with nonblank, blank, and authorization behavior covered. + +- [ ] **Step 5: Write the failing controller test** + +```java +@Test +void validateStepFourUsesAuthenticatedStudentAndReturnsValidationResult() { + NewProductSurveyStepFourValidationResult result = new NewProductSurveyStepFourValidationResult(true, "校验通过"); + when(newProductSurveyStepFourService.validate(any(), any())).thenReturn(result); + ResultEntity response = controller + .validateNewProductSurveyStepFour(request("核心", "有形", "延伸"), authenticatedRequest()); + assertEquals(HttpStatus.OK, response.getCode()); + assertSame(result, response.getData()); +} +``` + +- [ ] **Step 6: Run the controller test to verify it fails** + +Run: `mvn -B '-Dtest=StudentTrainingAnswerControllerTest' test` + +Expected: FAIL because `validateNewProductSurveyStepFour` does not exist. + +- [ ] **Step 7: Add the controller endpoint** + +```java +@PostMapping("/new-product-survey/step-4/validate") +public ResultEntity validateNewProductSurveyStepFour( + @RequestBody NewProductSurveyStepFourValidationRequest answer, HttpServletRequest request) { + try { + return new ResultEntity<>(HttpStatus.OK, "校验通过", + newProductSurveyStepFourService.validate(answer, TokenProvider.getJWTUser(request))); + } catch (ServiceException e) { + return new ResultEntity<>(e.getCode(), e.getMessage()); + } +} +``` + +- [ ] **Step 8: Run focused server tests** + +Run: `mvn -B '-Dtest=NewProductSurveyStepFourServiceImplTest,StudentTrainingAnswerControllerTest' test` + +Expected: PASS with fourth-step service behavior and endpoint delegation covered. + +- [ ] **Step 9: Commit server validation** + +```bash +git add src/main/java/com/sztzjy/linkCommerce/entity/dto/NewProductSurveyStepFourValidationRequest.java src/main/java/com/sztzjy/linkCommerce/entity/dto/NewProductSurveyStepFourValidationResult.java src/main/java/com/sztzjy/linkCommerce/service/NewProductSurveyStepFourService.java src/main/java/com/sztzjy/linkCommerce/service/impl/NewProductSurveyStepFourServiceImpl.java src/main/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerController.java src/test/java/com/sztzjy/linkCommerce/service/impl/NewProductSurveyStepFourServiceImplTest.java src/test/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerControllerTest.java +git commit -m "feat: validate new product survey step four" +``` + +### Task 2: Update the fourth-step student UI and submission flow + +**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: `checkNewProductSurveyStepFour(payload)` returning `{ code: 200, data: { valid: true, message: "校验通过" } }`. +- Produces: guarded `submitTask()` that saves `step4Answer` only after successful validation. + +- [ ] **Step 1: Add failing static source assertions** + +```js +assert.match(page, /AI技术是否实现产品功能突破?是否形成差异化卖点?/, "step four must show the core-layer prototype question"); +assert.match(page, /AI技术是否影响产品形态?如产品外形包装、电商主图设计、参数配置、价格带定位。/, "step four must show the tangible-layer prototype question"); +assert.match(page, /AI技术如何通过服务升级、内容更新提升用户粘性,推动付费服务?/, "step four must show the extended-layer prototype question"); +assert.match(page, /checkNewProductSurveyStepFour\(step4Form\.value\)/, "step four must validate before submission"); +assert.match(api, /new-product-survey\/step-4\/validate/, "the API must expose the step-four validation route"); +``` + +- [ ] **Step 2: Run the static test to verify it fails** + +Run: `& 'E:\Program Files\nodejs\node.exe' tests\new-product-survey-step-one.static.test.cjs` + +Expected: FAIL because the fourth-step question copy and validation call are absent. + +- [ ] **Step 3: Implement client validation and prototype-aligned form** + +```js +function validateNewProductSurveyStepFourDemo(form) { + const value = form || {}; + for (const [field, label] of [["coreValue", "核心层"], ["tangibleValue", "有形层"], ["extendedValue", "延伸层"]]) { + if (!String(value[field] || "").trim()) throw new Error(`请填写${label}AI应用价值分析`); + } + return { valid: true, message: "校验通过" }; +} + +async function submitTask() { + checking.value = true; + try { + await checkNewProductSurveyStepFour(step4Form.value); + await saveAnswer("SUBMITTED"); + } catch (error) { + proxy?.$modal?.msgWarning(error?.message || "请完成三层次AI应用价值分析"); + } finally { + checking.value = false; + } +} +``` + +Replace the existing generic field labels with the three prototype questions, keep their bindings as `coreValue`, `tangibleValue`, and `extendedValue`, and preserve draft saving and restoration. + +- [ ] **Step 4: Run the static test to verify it passes** + +Run: `& 'E:\Program Files\nodejs\node.exe' tests\new-product-survey-step-one.static.test.cjs` + +Expected: PASS with prior step contracts still intact. + +- [ ] **Step 5: Commit student UI and client validation** + +```bash +git add src/api/studentTrainingAnswer.js src/views/foundation/new-product-survey.vue tests/new-product-survey-step-one.static.test.cjs +git commit -m "feat: add new product survey step four" +``` + +### Task 3: Run full verification + +**Files:** +- Modify: none. + +**Interfaces:** +- Consumes: both committed fourth-step implementations. +- Produces: verification evidence before integration; no application process is started. + +- [ ] **Step 1: Run complete backend tests** + +Run: `mvn -B test` + +Expected: Maven reports `BUILD SUCCESS` and zero failures. + +- [ ] **Step 2: Run the frontend source contract** + +Run: `& 'E:\Program Files\nodejs\node.exe' tests\new-product-survey-step-one.static.test.cjs` + +Expected: `new product survey step-one contract passed`. + +- [ ] **Step 3: Run the production frontend build** + +Run: `& 'E:\Program Files\nodejs\node.exe' 'E:\workspace\dianshang\e-commerce-internet\node_modules\vite\bin\vite.js' build` + +Expected: Vite exits with code 0. Existing `::v-deep`, unresolved `Navbar.vue` top1 asset, and large-chunk warnings may remain. + +- [ ] **Step 4: Inspect repository state** + +Run: `git status --short; git log --oneline --max-count=5` + +Expected: only the intended fourth-step commits are present; do not start the backend.