13 KiB
产品开发关键因素第 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。
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。服务实现的唯一维度集合为:
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: 提交服务变更
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、响应对象不变,且服务获得了认证学生。
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 步模式新增:
@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: 提交路由变更
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"。
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;真实模式调用:
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: 提交前端变更
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。