merge: new product survey steps three and four
commit
66cd95b13e
@ -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<String> }>` 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<NewProductSurveyStepThreeValidationResult> 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
|
||||
<th>{{ step2Form.productName || '所选产品' }}分析</th>
|
||||
<tr v-for="row in step3Rows" :key="row.dimension">
|
||||
<td>{{ row.dimension }}<small>{{ row.definition }}</small></td>
|
||||
<td><ol><li v-for="point in row.referencePoints" :key="point">{{ point }}</li></ol></td>
|
||||
<td><textarea v-model="row.analysis" placeholder="每行填写一条要点,至少两条" /></td>
|
||||
</tr>
|
||||
```
|
||||
|
||||
Define `STEP_THREE_REFERENCE_ROWS` with the exact three dimensions, definitions, and AI 学习机 reference points from the approved spec. Add helpers: `splitStepThreePoints(value)`, `countStepThreePoints(value)`, `buildStepThreeValidationPayload()`, and a normalizer that converts old `product1`/`product2`/`product3` records into the new `analysis` text. Update local draft, answer restoration, reset, export outcome, and `buildAnswerPayload` to store the new points-array representation.
|
||||
|
||||
- [ ] **Step 5: Gate step-three navigation and show count guidance**
|
||||
|
||||
```js
|
||||
if (currentStep.value === 3) {
|
||||
await checkNewProductSurveyStepThree(buildStepThreeValidationPayload());
|
||||
await saveAnswer('IN_PROGRESS', true);
|
||||
advanceStep();
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
Display “已填写 N / 至少 2 条” per row and leave the student on step three when the API or local validation rejects the data. Preserve the first- and second-step validation flows.
|
||||
|
||||
- [ ] **Step 6: Run frontend contract test and production build**
|
||||
|
||||
Run: `node tests/new-product-survey-step-one.static.test.cjs`
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
Run: `node 'E:\workspace\dianshang\e-commerce-internet\node_modules\vite\bin\vite.js' build --config '.\vite.config.js'`
|
||||
|
||||
Expected: exit code 0, aside from existing repository warnings.
|
||||
|
||||
- [ ] **Step 7: Commit frontend changes**
|
||||
|
||||
```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: analyze new product survey step three"
|
||||
```
|
||||
|
||||
### Task 3: 完整验证与本地服务
|
||||
|
||||
**Files:**
|
||||
- Modify: no source files expected.
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: feature commits in both step-three worktrees.
|
||||
- Produces: test, build, and runtime evidence for the student page and backend API.
|
||||
|
||||
- [ ] **Step 1: Run full backend tests**
|
||||
|
||||
Run: `mvn -B test`
|
||||
|
||||
Expected: all tests pass with zero failures.
|
||||
|
||||
- [ ] **Step 2: Run all standalone frontend tests**
|
||||
|
||||
Run:
|
||||
|
||||
```powershell
|
||||
$tests = Get-ChildItem tests -File | Where-Object { $_.Name -like '*.test.cjs' -or $_.Name -like '*.test.mjs' }
|
||||
foreach ($test in $tests) { node $test.FullName }
|
||||
```
|
||||
|
||||
Expected: every test file exits 0.
|
||||
|
||||
- [ ] **Step 3: Start or refresh local services only on approval**
|
||||
|
||||
Run the frontend from its worktree using Vite on port 147. Build the backend JAR with `mvn -B package -DskipTests` and start it on port 7548 only if the user asks to start the backend; otherwise leave it stopped as requested previously.
|
||||
|
||||
- [ ] **Step 4: Report evidence and keep branches isolated**
|
||||
|
||||
Report test/build outcomes and commit hashes. Do not merge or stop/restart services unless the user explicitly chooses an integration or runtime action.
|
||||
Loading…
Reference in New Issue