|
|
|
|
@ -0,0 +1,256 @@
|
|
|
|
|
# 产品开发关键因素第 2 步 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:** 为学生端产品开发关键因素的第 2 步提供技术与合规两行对比表、服务端四项非空校验,以及仅保存并进入第 3 步的流程。
|
|
|
|
|
|
|
|
|
|
**Architecture:** 前端将第 1 步和第 2 步的固定行数据分开维护,分别写入已有答案记录的 `step1Answer` 与 `step2Answer`;第 2 步校验成功后以 `SAVE` 写入 `currentStep: 3`。后端参照第 1 步新增独立 DTO、校验服务和控制器路由,保留已有答案服务的字段合并行为,不新增表或改动教师端。
|
|
|
|
|
|
|
|
|
|
**Tech Stack:** Vue 3 Composition API、Element Plus、Vite、Spring Boot、JUnit 5、Mockito、Maven。
|
|
|
|
|
|
|
|
|
|
## Global Constraints
|
|
|
|
|
|
|
|
|
|
- 仅修改学生端 `product-development-factors` 第 2 步和其校验接口;不改教师端、管理员端、数据库结构、材料下载或第 3 步业务内容。
|
|
|
|
|
- 第 2 步表格只能出现“技术应用”“合规与认证”两行及原型的四列表头,且不显示产品名称输入框。
|
|
|
|
|
- 四个分析值去除首尾空白后必须非空;服务端拒绝缺失、重复或不在白名单内的维度。
|
|
|
|
|
- 推进使用 `saveAction: "SAVE"` 和 `currentStep: 3`,不得提交整项任务或触发成绩结算。
|
|
|
|
|
- 仅提交本计划列出的文件,保留主工作区既有未跟踪文件。
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 文件结构
|
|
|
|
|
|
|
|
|
|
### 前端仓库 `E:/workspace/dianshang/e-commerce-internet`
|
|
|
|
|
|
|
|
|
|
- 修改:`src/views/foundation/product-development-factors.vue` — 同屏步骤状态分离、第 2 步表格、答案恢复与保存推进。
|
|
|
|
|
- 修改:`src/api/studentTrainingAnswer.js` — 第 2 步真实/Demo 校验封装。
|
|
|
|
|
- 修改:`tests/product-development-factors-step-one.static.test.cjs` — 扩展为覆盖第 1、2 步共同页面的静态契约。
|
|
|
|
|
|
|
|
|
|
### 后端仓库 `E:/workspace/dianshang/link_commerce`
|
|
|
|
|
|
|
|
|
|
- 新建:`entity/dto/ProductDevelopmentFactorsStepTwoValidationRequest.java` — 两行分析请求。
|
|
|
|
|
- 新建:`entity/dto/ProductDevelopmentFactorsStepTwoValidationResult.java` — 校验响应。
|
|
|
|
|
- 新建:`service/ProductDevelopmentFactorsStepTwoService.java` 与 `service/impl/ProductDevelopmentFactorsStepTwoServiceImpl.java` — 学生身份和二行四项文本校验。
|
|
|
|
|
- 修改:`controller/stu/StudentTrainingAnswerController.java` — 第 2 步校验路由。
|
|
|
|
|
- 新建:`service/impl/ProductDevelopmentFactorsStepTwoServiceImplTest.java` — 服务校验行为。
|
|
|
|
|
- 修改:`controller/stu/StudentTrainingAnswerControllerTest.java` — 控制器认证传递。
|
|
|
|
|
|
|
|
|
|
## Task 1: 实现第 2 步后端校验服务
|
|
|
|
|
|
|
|
|
|
**Files:**
|
|
|
|
|
|
|
|
|
|
- Create: `link_commerce/src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductDevelopmentFactorsStepTwoValidationRequest.java`
|
|
|
|
|
- Create: `link_commerce/src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductDevelopmentFactorsStepTwoValidationResult.java`
|
|
|
|
|
- Create: `link_commerce/src/main/java/com/sztzjy/linkCommerce/service/ProductDevelopmentFactorsStepTwoService.java`
|
|
|
|
|
- Create: `link_commerce/src/main/java/com/sztzjy/linkCommerce/service/impl/ProductDevelopmentFactorsStepTwoServiceImpl.java`
|
|
|
|
|
- Create: `link_commerce/src/test/java/com/sztzjy/linkCommerce/service/impl/ProductDevelopmentFactorsStepTwoServiceImplTest.java`
|
|
|
|
|
|
|
|
|
|
**Interfaces:**
|
|
|
|
|
|
|
|
|
|
- Consumes: `JwtUser`、`ServiceException`、`StringUtils` 和第 1 步服务的校验模式。
|
|
|
|
|
- Produces: `ProductDevelopmentFactorsStepTwoService#validate(ProductDevelopmentFactorsStepTwoValidationRequest request, JwtUser user)`,成功返回 `valid=true`、`message="校验通过"`。
|
|
|
|
|
|
|
|
|
|
- [ ] **Step 1: 写服务失败测试**
|
|
|
|
|
|
|
|
|
|
测试完整的“技术应用”“合规与认证”两行通过;将任一成功或失败文本设为空、替换成重复维度或未知维度、使用教师用户时均断言 `ServiceException`。
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
ProductDevelopmentFactorsStepTwoValidationResult result = service.validate(validRequest(), student());
|
|
|
|
|
assertTrue(result.isValid());
|
|
|
|
|
assertThrows(ServiceException.class, () -> service.validate(requestWithBlankAnalysis(), student()));
|
|
|
|
|
assertThrows(ServiceException.class, () -> service.validate(validRequest(), teacher()));
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
- [ ] **Step 2: 运行失败测试**
|
|
|
|
|
|
|
|
|
|
Run: `mvn -B '-Dtest=ProductDevelopmentFactorsStepTwoServiceImplTest' test`
|
|
|
|
|
|
|
|
|
|
Expected: FAIL,因为第 2 步 DTO 和服务尚未存在。
|
|
|
|
|
|
|
|
|
|
- [ ] **Step 3: 写最小服务实现**
|
|
|
|
|
|
|
|
|
|
请求 DTO 定义 `List<Row> factors`,内嵌 `Row` 提供 `dimension`、`successAnalysis`、`failedAnalysis` getter/setter;结果 DTO 提供 `boolean valid`、`String message` getter/setter。服务实现的唯一维度集合为:
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
private static final Set<String> DIMENSIONS = new LinkedHashSet<>(Arrays.asList(
|
|
|
|
|
"技术应用", "合规与认证"));
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`validate` 先拒绝未登录和非 `roleId == 4` 的用户;随后要求恰好两行、每个维度属于集合且只出现一次、每行成功/失败文本均 `StringUtils.isNotBlank`,最后确认提交集合与 `DIMENSIONS` 完全相等。请求内容错误使用 `HttpStatus.BAD_REQUEST`,登录/权限错误分别使用 `UNAUTHORIZED`/`FORBIDDEN`。
|
|
|
|
|
|
|
|
|
|
- [ ] **Step 4: 运行服务测试**
|
|
|
|
|
|
|
|
|
|
Run: `mvn -B '-Dtest=ProductDevelopmentFactorsStepTwoServiceImplTest' test`
|
|
|
|
|
|
|
|
|
|
Expected: PASS,所有新增服务行为通过。
|
|
|
|
|
|
|
|
|
|
- [ ] **Step 5: 提交服务变更**
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
git add src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductDevelopmentFactorsStepTwoValidationRequest.java src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductDevelopmentFactorsStepTwoValidationResult.java src/main/java/com/sztzjy/linkCommerce/service/ProductDevelopmentFactorsStepTwoService.java src/main/java/com/sztzjy/linkCommerce/service/impl/ProductDevelopmentFactorsStepTwoServiceImpl.java src/test/java/com/sztzjy/linkCommerce/service/impl/ProductDevelopmentFactorsStepTwoServiceImplTest.java
|
|
|
|
|
git commit -m "feat: validate product factors step two"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Task 2: 暴露第 2 步校验路由
|
|
|
|
|
|
|
|
|
|
**Files:**
|
|
|
|
|
|
|
|
|
|
- Modify: `link_commerce/src/main/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerController.java`
|
|
|
|
|
- Modify: `link_commerce/src/test/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerControllerTest.java`
|
|
|
|
|
|
|
|
|
|
**Interfaces:**
|
|
|
|
|
|
|
|
|
|
- Consumes: Task 1 的 `ProductDevelopmentFactorsStepTwoService#validate`。
|
|
|
|
|
- Produces: `POST /api/student-training-answers/product-development-factors/step-2/validate`。
|
|
|
|
|
|
|
|
|
|
- [ ] **Step 1: 写控制器失败测试**
|
|
|
|
|
|
|
|
|
|
mock 第 2 步服务,注入控制器,调用 `validateProductDevelopmentFactorsStepTwo(answer, authenticatedStudentRequest())`,断言 HTTP 200、响应对象不变,且服务获得了认证学生。
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
when(service.validate(any(ProductDevelopmentFactorsStepTwoValidationRequest.class), any(JwtUser.class))).thenReturn(validation);
|
|
|
|
|
assertEquals(HttpStatus.OK, result.getStatusCode());
|
|
|
|
|
verify(service).validate(any(ProductDevelopmentFactorsStepTwoValidationRequest.class), any(JwtUser.class));
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
- [ ] **Step 2: 运行控制器测试确认失败**
|
|
|
|
|
|
|
|
|
|
Run: `mvn -B '-Dtest=StudentTrainingAnswerControllerTest' test`
|
|
|
|
|
|
|
|
|
|
Expected: FAIL,因为控制器还没有第 2 步方法。
|
|
|
|
|
|
|
|
|
|
- [ ] **Step 3: 新增路由**
|
|
|
|
|
|
|
|
|
|
添加 DTO/服务 import 与 `@Autowired ProductDevelopmentFactorsStepTwoService productDevelopmentFactorsStepTwoService`,并按第 1 步模式新增:
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
@PostMapping("/product-development-factors/step-2/validate")
|
|
|
|
|
public ResultEntity<ProductDevelopmentFactorsStepTwoValidationResult> validateProductDevelopmentFactorsStepTwo(
|
|
|
|
|
@RequestBody ProductDevelopmentFactorsStepTwoValidationRequest answer, HttpServletRequest request) {
|
|
|
|
|
try {
|
|
|
|
|
return new ResultEntity<>(HttpStatus.OK, "校验通过",
|
|
|
|
|
productDevelopmentFactorsStepTwoService.validate(answer, TokenProvider.getJWTUser(request)));
|
|
|
|
|
} catch (ServiceException e) {
|
|
|
|
|
return new ResultEntity<>(e.getCode(), e.getMessage());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
- [ ] **Step 4: 运行聚焦后端测试**
|
|
|
|
|
|
|
|
|
|
Run: `mvn -B '-Dtest=ProductDevelopmentFactorsStepOneServiceImplTest,ProductDevelopmentFactorsStepTwoServiceImplTest,StudentTrainingAnswerControllerTest' test`
|
|
|
|
|
|
|
|
|
|
Expected: PASS,第 1 步回归、新服务和两条控制器路由均通过。
|
|
|
|
|
|
|
|
|
|
- [ ] **Step 5: 提交路由变更**
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
git add src/main/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerController.java src/test/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerControllerTest.java
|
|
|
|
|
git commit -m "feat: expose product factors step two validation"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Task 3: 实现学生端第 2 步页面与保存流程
|
|
|
|
|
|
|
|
|
|
**Files:**
|
|
|
|
|
|
|
|
|
|
- Modify: `e-commerce-internet/src/api/studentTrainingAnswer.js`
|
|
|
|
|
- Modify: `e-commerce-internet/src/views/foundation/product-development-factors.vue`
|
|
|
|
|
- Modify: `e-commerce-internet/tests/product-development-factors-step-one.static.test.cjs`
|
|
|
|
|
|
|
|
|
|
**Interfaces:**
|
|
|
|
|
|
|
|
|
|
- Consumes: Task 2 路由与已有 `saveStudentTrainingAnswer`。
|
|
|
|
|
- Produces: `checkProductDevelopmentFactorsStepTwo(payload)`;保存 `{ step2Answer, currentStep: 3, submitted: false, saveAction: "SAVE" }` 并进入索引 2。
|
|
|
|
|
|
|
|
|
|
- [ ] **Step 1: 写前端静态契约失败测试**
|
|
|
|
|
|
|
|
|
|
扩展测试以断言 `STEP_TWO_FACTOR_ROWS`、两个中文维度、`checkProductDevelopmentFactorsStepTwo`、第 2 步路由、`v-else-if="currentStep === 1"`、`step2Answer: JSON.stringify(buildStepTwoOutcome())`、`buildStepTwoAnswerPayload("SAVE", 3)` 都存在;断言源码不出现 `buildStepTwoAnswerPayload("SUBMIT"`。
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
assert.match(page, /const STEP_TWO_FACTOR_ROWS = \[/);
|
|
|
|
|
assert.match(page, /await checkProductDevelopmentFactorsStepTwo\(buildStepTwoValidationPayload\(\)\)/);
|
|
|
|
|
assert.match(api, /product-development-factors\/step-2\/validate/);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
- [ ] **Step 2: 运行静态测试确认失败**
|
|
|
|
|
|
|
|
|
|
Run: `node tests/product-development-factors-step-one.static.test.cjs`
|
|
|
|
|
|
|
|
|
|
Expected: FAIL,因为第 2 步常量、API 和保存动作未实现。
|
|
|
|
|
|
|
|
|
|
- [ ] **Step 3: 添加 API 与 Demo 校验**
|
|
|
|
|
|
|
|
|
|
在 API 文件新增 `checkProductDevelopmentFactorsStepTwo(payload)`。Demo 模式只接受两行固定维度且四项分析非空,失败时拒绝 Promise;真实模式调用:
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
return request({
|
|
|
|
|
url: "/api/student-training-answers/product-development-factors/step-2/validate",
|
|
|
|
|
method: "post",
|
|
|
|
|
data: payload,
|
|
|
|
|
headers: { repeatSubmit: false },
|
|
|
|
|
});
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
- [ ] **Step 4: 分离两步数据并渲染第 2 步**
|
|
|
|
|
|
|
|
|
|
将现有 `factorRows` 重命名为 `stepOneRows`,新增 `STEP_TWO_FACTOR_ROWS` 与 `stepTwoRows`,并为两步分别提供 `create...Rows`、`build...Outcome`、`build...ValidationPayload`。模板保留第 1 步 `v-if="currentStep === 0"`,新增 `v-else-if="currentStep === 1"` 的原型表格和“保存并进入第 3 步”按钮,最后 `v-else` 才保留第 3 步待配置占位。
|
|
|
|
|
|
|
|
|
|
`loadSavedAnswer()` 分别读取 `answer.step1Answer` 与 `answer.step2Answer` 并按维度恢复;本地草稿保存两个步骤的内容和当前步骤,读取时兼容旧第 1 步草稿。第 2 步保存函数先校验,再调用 `saveStudentTrainingAnswer(TASK_KEY, buildStepTwoAnswerPayload("SAVE", 3))`,步骤配置不少于三步时设置 `currentStep.value = 2`,否则显示已保存但未配置第 3 步。重置时清空两步行数据、将当前步骤设为 0,并保持既有 `RESET` 行为。
|
|
|
|
|
|
|
|
|
|
- [ ] **Step 5: 运行前端契约测试**
|
|
|
|
|
|
|
|
|
|
Run: `node tests/product-development-factors-step-one.static.test.cjs`
|
|
|
|
|
|
|
|
|
|
Expected: PASS,两个步骤结构、校验和无 `SUBMIT` 约束均满足。
|
|
|
|
|
|
|
|
|
|
- [ ] **Step 6: 构建生产前端**
|
|
|
|
|
|
|
|
|
|
Run: `npm run build:prod`
|
|
|
|
|
|
|
|
|
|
Expected: PASS,Vite 生产构建成功。
|
|
|
|
|
|
|
|
|
|
- [ ] **Step 7: 提交前端变更**
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
git add src/api/studentTrainingAnswer.js src/views/foundation/product-development-factors.vue tests/product-development-factors-step-one.static.test.cjs
|
|
|
|
|
git commit -m "feat: add product factors step two analysis"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Task 4: 合并前回归验证
|
|
|
|
|
|
|
|
|
|
**Files:**
|
|
|
|
|
|
|
|
|
|
- Modify: 无;仅验证 Task 1–3 的提交。
|
|
|
|
|
|
|
|
|
|
**Interfaces:**
|
|
|
|
|
|
|
|
|
|
- Consumes: 前端第 1、2 步保存/恢复逻辑和后端两条校验路由。
|
|
|
|
|
- Produces: 可供用户选择集成方式的干净分支,不启动本地后端、不推送或发布。
|
|
|
|
|
|
|
|
|
|
- [ ] **Step 1: 运行后端完整测试**
|
|
|
|
|
|
|
|
|
|
Run: `mvn -B test`
|
|
|
|
|
|
|
|
|
|
Expected: PASS,所有 Maven 测试通过。
|
|
|
|
|
|
|
|
|
|
- [ ] **Step 2: 运行前端契约与生产构建**
|
|
|
|
|
|
|
|
|
|
Run: `node tests/product-development-factors-step-one.static.test.cjs; npm run build:prod`
|
|
|
|
|
|
|
|
|
|
Expected: PASS,契约和构建均通过。
|
|
|
|
|
|
|
|
|
|
- [ ] **Step 3: 检查分支状态**
|
|
|
|
|
|
|
|
|
|
Run: `git status --short`(两个 worktree 分别执行)
|
|
|
|
|
|
|
|
|
|
Expected: 没有本功能未提交改动;不处理主工作区无关未跟踪文件。
|
|
|
|
|
|
|
|
|
|
- [ ] **Step 4: 报告提交与验证结果**
|
|
|
|
|
|
|
|
|
|
报告前后端提交号、测试结果和“保存并进入第 3 步”的用户可见行为。除非用户明确要求,不合并、推送、发布或启动本地后端。
|
|
|
|
|
|
|
|
|
|
## 自检结果
|
|
|
|
|
|
|
|
|
|
- 规格覆盖:Task 1–2 负责后端身份和四项文本校验,Task 3 负责两步页面分离、恢复、Demo/真实 API 与保存推进,Task 4 负责完整回归。
|
|
|
|
|
- 占位扫描:计划没有未定义的后续实现项;第 3 步仅为现有导航承接所需的待配置状态,不添加业务。
|
|
|
|
|
- 类型一致性:前后端请求字段统一为 `factors[].dimension/successAnalysis/failedAnalysis`;第 2 步路由统一为 `/product-development-factors/step-2/validate`。
|