|
|
|
|
@ -0,0 +1,214 @@
|
|
|
|
|
# 新产品调查与分析第二步 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:** 让学生完成新产品调查与分析的第二步,并在提交前由后端验证平台、场景、关键词、产品名和 1–5 张产品图片。
|
|
|
|
|
|
|
|
|
|
**Architecture:** 将 `new-product-survey.vue` 中旧的三行平台表格替换为单个调查表单,并将其 JSON 继续保存在现有 `StudentTrainingAnswer.step2Answer`。图片先沿用 `/common/upload` 上传,答案中只保存 `{ name, url }`;新增一个无状态的第二步校验服务与控制器入口,前端通过校验后才推进到第三步。
|
|
|
|
|
|
|
|
|
|
**Tech Stack:** Vue 3 Composition API、Element Plus、Vite、现有 RuoYi request 封装、Spring Boot、JUnit 5、Mockito。
|
|
|
|
|
|
|
|
|
|
## Global Constraints
|
|
|
|
|
|
|
|
|
|
- 仅调整学生端 `new-product-survey` 与对应学生答案接口;不改教师端、管理端或其余步骤。
|
|
|
|
|
- 案例资料按钮继续读取当前任务的 `materialUrl` 并下载,显示固定文案“下载案例资料”。
|
|
|
|
|
- `step2Answer` 必须兼容既有答案存储与本地草稿,不新建图片表。
|
|
|
|
|
- 第二步提交必须要求 1 至 5 张图片;选择“其他场景”时必须填写自定义场景。
|
|
|
|
|
- 演示模式只做本地校验与会话保存,不调用上传或新增后端校验接口。
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### Task 1: 后端第二步提交校验
|
|
|
|
|
|
|
|
|
|
**Files:**
|
|
|
|
|
- Create: `E:/workspace/dianshang/link_commerce/.worktrees/new-product-survey-step1/src/main/java/com/sztzjy/linkCommerce/entity/dto/NewProductSurveyStepTwoValidationRequest.java`
|
|
|
|
|
- Create: `E:/workspace/dianshang/link_commerce/.worktrees/new-product-survey-step1/src/main/java/com/sztzjy/linkCommerce/entity/dto/NewProductSurveyStepTwoValidationResult.java`
|
|
|
|
|
- Create: `E:/workspace/dianshang/link_commerce/.worktrees/new-product-survey-step1/src/main/java/com/sztzjy/linkCommerce/service/NewProductSurveyStepTwoService.java`
|
|
|
|
|
- Create: `E:/workspace/dianshang/link_commerce/.worktrees/new-product-survey-step1/src/main/java/com/sztzjy/linkCommerce/service/impl/NewProductSurveyStepTwoServiceImpl.java`
|
|
|
|
|
- Modify: `E:/workspace/dianshang/link_commerce/.worktrees/new-product-survey-step1/src/main/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerController.java`
|
|
|
|
|
- Test: `E:/workspace/dianshang/link_commerce/.worktrees/new-product-survey-step1/src/test/java/com/sztzjy/linkCommerce/service/impl/NewProductSurveyStepTwoServiceImplTest.java`
|
|
|
|
|
- Test: `E:/workspace/dianshang/link_commerce/.worktrees/new-product-survey-step1/src/test/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerControllerTest.java`
|
|
|
|
|
|
|
|
|
|
**Interfaces:**
|
|
|
|
|
- Consumes: `JwtUser` and `POST /api/student-training-answers/new-product-survey/step-2/validate` request JSON.
|
|
|
|
|
- Produces: `NewProductSurveyStepTwoService.validate(NewProductSurveyStepTwoValidationRequest, JwtUser)` returning `{ valid: true, message: "校验通过" }` or a 4xx `ServiceException`.
|
|
|
|
|
|
|
|
|
|
- [ ] **Step 1: Write failing service tests**
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
@Test
|
|
|
|
|
void validatesACompleteStudentSubmission() {
|
|
|
|
|
NewProductSurveyStepTwoValidationResult result = service.validate(request("京东", "AI儿童玩具", "", twoImages()), student());
|
|
|
|
|
assertTrue(result.isValid());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void rejectsOtherSceneWithoutCustomSceneAndMoreThanFiveImages() {
|
|
|
|
|
assertThrows(ServiceException.class, () -> service.validate(request("京东", "其他场景", "", oneImage()), student()));
|
|
|
|
|
assertThrows(ServiceException.class, () -> service.validate(request("京东", "AI儿童玩具", "", sixImages()), student()));
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
- [ ] **Step 2: Run the service test to verify it fails**
|
|
|
|
|
|
|
|
|
|
Run: `mvn -B '-Dtest=NewProductSurveyStepTwoServiceImplTest' test`
|
|
|
|
|
|
|
|
|
|
Expected: FAIL because the step-two DTO and service do not exist.
|
|
|
|
|
|
|
|
|
|
- [ ] **Step 3: Implement the DTO, service, and validation rules**
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
public interface NewProductSurveyStepTwoService {
|
|
|
|
|
NewProductSurveyStepTwoValidationResult validate(NewProductSurveyStepTwoValidationRequest request, JwtUser user);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// validate roleId == 4; platform in 京东/天猫/抖音电商; scene is nonblank;
|
|
|
|
|
// customScene required only for 其他场景; keyword/productName nonblank;
|
|
|
|
|
// imageUrls contains 1..5 nonblank URLs. Throw ServiceException(BAD_REQUEST, message) on failure.
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
- [ ] **Step 4: Expose the controller endpoint and its test**
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
@PostMapping("/new-product-survey/step-2/validate")
|
|
|
|
|
public ResultEntity<NewProductSurveyStepTwoValidationResult> validateNewProductSurveyStepTwo(
|
|
|
|
|
@RequestBody NewProductSurveyStepTwoValidationRequest answer, HttpServletRequest request) {
|
|
|
|
|
return new ResultEntity<>(HttpStatus.OK, "校验通过", newProductSurveyStepTwoService.validate(answer, TokenProvider.getJWTUser(request)));
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Mock the new service in `StudentTrainingAnswerControllerTest` and assert the controller passes the request and JWT user through, returning HTTP 200.
|
|
|
|
|
|
|
|
|
|
- [ ] **Step 5: Run focused backend tests**
|
|
|
|
|
|
|
|
|
|
Run: `mvn -B '-Dtest=NewProductSurveyStepTwoServiceImplTest,StudentTrainingAnswerControllerTest' test`
|
|
|
|
|
|
|
|
|
|
Expected: PASS with zero failures.
|
|
|
|
|
|
|
|
|
|
- [ ] **Step 6: Commit the backend implementation**
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
git add src/main/java src/test/java
|
|
|
|
|
git commit -m "feat: validate new product survey step two"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Task 2: 前端第二步表单、上传与答案回填
|
|
|
|
|
|
|
|
|
|
**Files:**
|
|
|
|
|
- Modify: `E:/workspace/dianshang/e-commerce-internet/.worktrees/new-product-survey-step1/src/api/studentTrainingAnswer.js`
|
|
|
|
|
- Modify: `E:/workspace/dianshang/e-commerce-internet/.worktrees/new-product-survey-step1/src/views/foundation/new-product-survey.vue`
|
|
|
|
|
- Modify: `E:/workspace/dianshang/e-commerce-internet/.worktrees/new-product-survey-step1/tests/new-product-survey-step-one.static.test.cjs`
|
|
|
|
|
|
|
|
|
|
**Interfaces:**
|
|
|
|
|
- Consumes: `uploadStudentTrainingFile(FormData)`, `checkNewProductSurveyStepTwo(form)` and the backend validation response.
|
|
|
|
|
- Produces: `step2Answer` JSON `{ platform, scene, customScene, keyword, productName, images: Array<{name, url}> }` and only advances the wizard after validation succeeds.
|
|
|
|
|
|
|
|
|
|
- [ ] **Step 1: Write failing frontend static assertions**
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
assert.match(source, /AI智能家居[\s\S]*AI美妆辅助[\s\S]*其他场景/);
|
|
|
|
|
assert.match(source, /v-if="step2Form\.scene === '其他场景'"/);
|
|
|
|
|
assert.match(source, /MAX_STEP_TWO_IMAGES = 5/);
|
|
|
|
|
assert.match(source, /checkNewProductSurveyStepTwo/);
|
|
|
|
|
assert.match(source, /step2Answer: JSON\.stringify\(step2Form\.value\)/);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
- [ ] **Step 2: Run the static test to verify it fails**
|
|
|
|
|
|
|
|
|
|
Run: `node tests/new-product-survey-step-one.static.test.cjs`
|
|
|
|
|
|
|
|
|
|
Expected: FAIL because the page still renders `step2Rows` and has no step-two validation client.
|
|
|
|
|
|
|
|
|
|
- [ ] **Step 3: Add the API client and local demo validator**
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
export function checkNewProductSurveyStepTwo(form) {
|
|
|
|
|
if (isStudentDemo()) return Promise.resolve({ code: 200, data: validateStepTwoDemo(form) });
|
|
|
|
|
return request({
|
|
|
|
|
url: "/api/student-training-answers/new-product-survey/step-2/validate",
|
|
|
|
|
method: "post",
|
|
|
|
|
data: { ...form, imageUrls: form.images.map((image) => image.url) },
|
|
|
|
|
headers: { repeatSubmit: false },
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`validateStepTwoDemo` must apply the same required-field, other-scene, and 1–5 image count rules and reject invalid data with an `Error`.
|
|
|
|
|
|
|
|
|
|
- [ ] **Step 4: Replace the old table with the second-step form**
|
|
|
|
|
|
|
|
|
|
```vue
|
|
|
|
|
<el-radio-group v-model="step2Form.platform">
|
|
|
|
|
<el-radio v-for="option in PLATFORM_OPTIONS" :key="option.value" :label="option.value">{{ option.label }}</el-radio>
|
|
|
|
|
</el-radio-group>
|
|
|
|
|
<input v-if="step2Form.scene === '其他场景'" v-model.trim="step2Form.customScene" />
|
|
|
|
|
<input type="file" accept="image/*" multiple @change="uploadStepTwoImages" />
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Use constants for the three platform options and six scene options. Implement `uploadStepTwoImages`, `removeStepTwoImage`, and `previewStepTwoImage`: reject non-images and selections that would exceed five files; upload files with `FormData`; store only name and resolved URL; show thumbnails in a dialog-capable preview. Replace all `step2Rows` watch, reset, draft, save, outcome, and restoration paths with `step2Form`, while retaining safe fallback behavior for old row-array answers.
|
|
|
|
|
|
|
|
|
|
- [ ] **Step 5: Gate next-step navigation on step-two validation**
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
if (currentStep.value === 2) {
|
|
|
|
|
await checkNewProductSurveyStepTwo(step2Form.value);
|
|
|
|
|
await saveAnswer("IN_PROGRESS", true);
|
|
|
|
|
advanceStep();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Show a human-readable validation/upload error and leave the user on step two on failure. Keep step-one grading and steps three/four navigation behavior unchanged.
|
|
|
|
|
|
|
|
|
|
- [ ] **Step 6: Run frontend static 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: build exits with code 0; retain only the repository’s pre-existing warnings.
|
|
|
|
|
|
|
|
|
|
- [ ] **Step 7: Commit the frontend implementation**
|
|
|
|
|
|
|
|
|
|
```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: collect new product survey step two"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Task 3: 全量验证与运行中的服务检查
|
|
|
|
|
|
|
|
|
|
**Files:**
|
|
|
|
|
- Modify: no source files expected.
|
|
|
|
|
|
|
|
|
|
**Interfaces:**
|
|
|
|
|
- Consumes: committed frontend and backend worktree changes.
|
|
|
|
|
- Produces: evidence that the new APIs compile and the updated student page is available on the already-started local services.
|
|
|
|
|
|
|
|
|
|
- [ ] **Step 1: Run the complete backend suite**
|
|
|
|
|
|
|
|
|
|
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: Verify the running services**
|
|
|
|
|
|
|
|
|
|
Run: `Invoke-WebRequest http://127.0.0.1:147/index.html -UseBasicParsing` and `Invoke-WebRequest http://127.0.0.1:7548/doc.html -UseBasicParsing`.
|
|
|
|
|
|
|
|
|
|
Expected: both return HTTP 200. If the backend process predates the new commit, rebuild the backend JAR with `mvn -B package -DskipTests` and restart only the project JAR on port 7548.
|
|
|
|
|
|
|
|
|
|
- [ ] **Step 4: Report verification and commits**
|
|
|
|
|
|
|
|
|
|
Provide test/build outcomes, local URLs, and the two feature commit hashes. Do not merge the isolated worktree branches without an explicit user instruction.
|