You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
dianshang-qianduan/docs/superpowers/plans/2026-08-07-product-developm...

16 KiB

产品开发关键因素第 1 步 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 步提供原型对应的三维对比表、服务端非空校验,以及仅保存并进入第 2 步的流程。

Architecture: 前端把第 1 步维度收敛为固定常量,以 step1Answer 持久化三行成功/失败分析;点击推进时先调用专用校验接口,再使用已有学生答案保存接口保存 SAVE 草稿和第 2 步进度。后端新增轻量 DTO、校验服务和控制器路由复用已有 JWT 学生身份检查与 StudentTrainingAnswer 持久化机制,不新建表。

Tech Stack: Vue 3 Composition API、Element Plus、Vite、Spring Boot、JUnit 5、Mockito、Maven。

Global Constraints

  • 只修改学生端 product-development-factors 的第 1 步和其服务端校验;不修改教师端、管理员端、数据库结构、材料下载或第 2 步的业务内容。
  • 页面仅保留原型的四列表头与三个固定维度,不显示成功/失败产品名称输入项。
  • 六个分析输入去除首尾空白后必须非空,服务端必须拒绝缺失、重复、额外或伪造维度。
  • 第 1 步推进只能使用 saveAction: "SAVE",不得触发 SUBMIT 或成绩结算。
  • 使用已有隔离 worktree仅提交本计划列出的文件避免纳入主工作区已有的无关未跟踪文件。

文件结构

前端仓库 E:/workspace/dianshang/e-commerce-internet

  • 修改:src/views/foundation/product-development-factors.vue — 固定第 1 步表格、恢复/保存进度和“保存并进入第 2 步”交互。
  • 修改:src/api/studentTrainingAnswer.js — 增加 Demo 与真实环境共用的第 1 步校验 API 封装。
  • 新建:tests/product-development-factors-step-one.static.test.cjs — 对页面结构、保存行为与 API 路由做静态契约测试。
  • 新建:src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductDevelopmentFactorsStepOneValidationRequest.java — 接收固定维度及两列分析文本。
  • 新建:src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductDevelopmentFactorsStepOneValidationResult.java — 返回 validmessage
  • 新建:src/main/java/com/sztzjy/linkCommerce/service/ProductDevelopmentFactorsStepOneService.java — 声明校验入口。
  • 新建:src/main/java/com/sztzjy/linkCommerce/service/impl/ProductDevelopmentFactorsStepOneServiceImpl.java — 学生身份、三维度和六项非空校验。
  • 修改:src/main/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerController.java — 注入服务并暴露校验路由。
  • 新建:src/test/java/com/sztzjy/linkCommerce/service/impl/ProductDevelopmentFactorsStepOneServiceImplTest.java — 服务层行为测试。
  • 修改:src/test/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerControllerTest.java — 控制器传递认证学生和返回校验结果的测试。

Task 1: 后端第 1 步校验契约

Files:

  • Create: link_commerce/src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductDevelopmentFactorsStepOneValidationRequest.java
  • Create: link_commerce/src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductDevelopmentFactorsStepOneValidationResult.java
  • Create: link_commerce/src/main/java/com/sztzjy/linkCommerce/service/ProductDevelopmentFactorsStepOneService.java
  • Create: link_commerce/src/main/java/com/sztzjy/linkCommerce/service/impl/ProductDevelopmentFactorsStepOneServiceImpl.java
  • Create: link_commerce/src/test/java/com/sztzjy/linkCommerce/service/impl/ProductDevelopmentFactorsStepOneServiceImplTest.java

Interfaces:

  • Consumes: JwtUserServiceExceptionStringUtilsHttpStatus,以及 NewProductSurveyStepThreeServiceImpl 的角色校验风格。

  • Produces: ProductDevelopmentFactorsStepOneService#validate(ProductDevelopmentFactorsStepOneValidationRequest request, JwtUser user),供控制器调用;成功时返回 { valid: true, message: "校验通过" }

  • Step 1: 写服务层失败测试

ProductDevelopmentFactorsStepOneServiceImplTest 建立三个测试:完整的三个固定维度均有成功/失败文本时返回有效;任一分析为空、维度重复或维度不在白名单时抛出 ServiceException;角色不是 4 时抛出 ServiceException

ProductDevelopmentFactorsStepOneValidationResult result = service.validate(validRequest(), student());
assertTrue(result.isValid());
assertThrows(ServiceException.class, () -> service.validate(requestWithBlankFailure(), student()));
assertThrows(ServiceException.class, () -> service.validate(validRequest(), teacher()));
  • Step 2: 运行测试确认失败

Run: mvn -B '-Dtest=ProductDevelopmentFactorsStepOneServiceImplTest' test

Expected: FAIL因为校验 DTO、服务接口及实现尚不存在。

  • Step 3: 添加请求、结果和服务实现

请求 DTO 定义 List<Row> factorsRow 定义 String dimensionString successAnalysisString failedAnalysis 及标准 getter/setter。实现类固定使用以下维度集合

private static final Set<String> DIMENSIONS = new LinkedHashSet<>(Arrays.asList(
        "用户需求真实性", "产品体验闭环", "生态与兼容"));

validate 必须先验证登录用户存在且 roleId == 4,再验证 factors 数量恰为 3、每个维度属于 DIMENSIONS 且仅出现一次、successAnalysisfailedAnalysis 都满足 StringUtils.isNotBlank,最后验证提交维度集合与 DIMENSIONS 完全一致。所有请求内容问题用 new ServiceException(HttpStatus.BAD_REQUEST, message) 拒绝;身份问题分别用 UNAUTHORIZEDFORBIDDEN

  • Step 4: 运行服务测试确认通过

Run: mvn -B '-Dtest=ProductDevelopmentFactorsStepOneServiceImplTest' test

Expected: PASS三个测试全部通过。

  • Step 5: 提交后端服务层变更
git add src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductDevelopmentFactorsStepOneValidationRequest.java src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductDevelopmentFactorsStepOneValidationResult.java src/main/java/com/sztzjy/linkCommerce/service/ProductDevelopmentFactorsStepOneService.java src/main/java/com/sztzjy/linkCommerce/service/impl/ProductDevelopmentFactorsStepOneServiceImpl.java src/test/java/com/sztzjy/linkCommerce/service/impl/ProductDevelopmentFactorsStepOneServiceImplTest.java
git commit -m "feat: validate product factors step one"

Task 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 的 ProductDevelopmentFactorsStepOneService#validate(request, user)

  • Produces: POST /api/student-training-answers/product-development-factors/step-1/validate,请求体为 { "factors": [{ "dimension": "...", "successAnalysis": "...", "failedAnalysis": "..." }] }

  • Step 1: 写控制器失败测试

StudentTrainingAnswerControllerTest 添加一个测试mock ProductDevelopmentFactorsStepOneService,通过 ReflectionTestUtils 注入控制器,调用 validateProductDevelopmentFactorsStepOne,断言状态为 HttpStatus.OK、返回体是 mock 的结果,并验证 service.validate(any(ProductDevelopmentFactorsStepOneValidationRequest.class), any(JwtUser.class)) 被调用。

when(service.validate(any(ProductDevelopmentFactorsStepOneValidationRequest.class), any(JwtUser.class))).thenReturn(validation);
ResultEntity<ProductDevelopmentFactorsStepOneValidationResult> result = controller
        .validateProductDevelopmentFactorsStepOne(answer, authenticatedStudentRequest());
assertEquals(HttpStatus.OK, result.getStatusCode());
verify(service).validate(any(ProductDevelopmentFactorsStepOneValidationRequest.class), any(JwtUser.class));
  • Step 2: 运行控制器测试确认失败

Run: mvn -B '-Dtest=StudentTrainingAnswerControllerTest' test

Expected: FAIL因为控制器尚未注入服务或不存在该方法。

  • Step 3: 添加控制器依赖与路由

在控制器添加 DTO 和服务 import、@Autowired ProductDevelopmentFactorsStepOneService productDevelopmentFactorsStepOneService,并添加以下方法。方法沿用已有四个新产品调查校验接口的 try/catch ServiceException 响应方式。

@PostMapping("/product-development-factors/step-1/validate")
@ApiOperation("产品开发关键因素第一步校验")
public ResultEntity<ProductDevelopmentFactorsStepOneValidationResult> validateProductDevelopmentFactorsStepOne(
        @RequestBody ProductDevelopmentFactorsStepOneValidationRequest answer, HttpServletRequest request) {
    try {
        JwtUser user = TokenProvider.getJWTUser(request);
        return new ResultEntity<>(HttpStatus.OK, "校验通过", productDevelopmentFactorsStepOneService.validate(answer, user));
    } catch (ServiceException e) {
        return new ResultEntity<>(e.getCode(), e.getMessage());
    }
}
  • Step 4: 运行控制器和服务测试确认通过

Run: mvn -B '-Dtest=ProductDevelopmentFactorsStepOneServiceImplTest,StudentTrainingAnswerControllerTest' test

Expected: PASS新增路由通过认证用户调用服务已有控制器测试保持通过。

  • 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 one validation"

Task 3: 前端 API 与页面第 1 步交互

Files:

  • Modify: e-commerce-internet/src/api/studentTrainingAnswer.js
  • Modify: e-commerce-internet/src/views/foundation/product-development-factors.vue
  • Create: e-commerce-internet/tests/product-development-factors-step-one.static.test.cjs

Interfaces:

  • Consumes: Task 2 的 POST /api/student-training-answers/product-development-factors/step-1/validate,以及现有 saveStudentTrainingAnswer(taskKey, data)

  • Produces: checkProductDevelopmentFactorsStepOne(payload);页面第 1 步调用其校验并保存 { step1Answer, currentStep: 2, submitted: false, saveAction: "SAVE" } 后切到索引 1

  • Step 1: 写前端静态契约失败测试

新建 tests/product-development-factors-step-one.static.test.cjs,读取 API 和 Vue 源码,断言固定三维度常量与三条中文评估项存在,旧的 successProduct/failedProduct 输入绑定不存在,存在 checkProductDevelopmentFactorsStepOne 和真实路由字符串,推进方法先校验后 saveStudentTrainingAnswer,并且保存动作字符串为 "SAVE" 而非 "SUBMIT"

assert.match(view, /const STEP_ONE_FACTOR_ROWS = \[/);
assert.doesNotMatch(view, /v-model="form\.successProduct"/);
assert.match(api, /product-development-factors\/step-1\/validate/);
assert.match(view, /await checkProductDevelopmentFactorsStepOne\(buildStepOneValidationPayload\(\)\)/);
  • Step 2: 运行静态测试确认失败

Run: node tests/product-development-factors-step-one.static.test.cjs

Expected: FAIL因为页面尚未声明固定三行常量或调用新校验 API。

  • Step 3: 实现 API 封装和 Demo 校验

studentTrainingAnswer.js 增加 checkProductDevelopmentFactorsStepOne(payload)。Demo 模式以相同固定维度、恰好三行和每行两列非空规则校验,失败时 Promise.reject(new Error(message));真实模式发起:

return request({
  url: "/api/student-training-answers/product-development-factors/step-1/validate",
  method: "post",
  data: payload,
  headers: { repeatSubmit: false },
});
  • Step 4: 重构第 1 步页面

product-development-factors.vueSTEP_ONE_FACTOR_ROWS 取代八行工厂,且只包含原型指定的三行和评估项。移除 form.product-entry.product-field.input-single 及模板中的产品名称输入区域;buildOutcome() 仅保存 title 与三行 factors

将表格和第 1 步操作包在 v-if="currentStep === 0" 中,非第 1 步渲染不复用第 1 步表格的轻量占位块。新增 buildStepOneValidationPayload(),其返回值仅含 { factors: factorRows.value.map(({ dimension, successAnalysis, failedAnalysis }) => ({ dimension, successAnalysis, failedAnalysis })) }。新增 saveAndGoToStepTwo():先等待校验、再调用 saveStudentTrainingAnswer(TASK_KEY, buildAnswerPayload("SAVE", 2))、清理本地草稿;当 trainingSteps.value.length >= 2 时设置 currentStep.value = 1,否则提示任务未配置下一步。替换原“提交任务”按钮为“保存并进入第 2 步”,不再保留第 1 步的 SUBMIT 路径。

loadSavedAnswer() 在恢复 step1Answer 后,将服务端 answer.currentStep 的 1 基步骤值转换成合法 0 基索引;历史 JSON 的产品名称字段不读取。resetTraining() 重建三行数据、保存 RESET,并复位 currentStep.value = 0

  • Step 5: 运行前端静态测试确认通过

Run: node tests/product-development-factors-step-one.static.test.cjs

Expected: PASS页面和 API 合同全部满足。

  • Step 6: 构建前端

Run: npm run build

Expected: PASSVite 生产构建完成且没有 Vue 编译错误。

  • 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 one analysis"

Task 4: 跨端回归验证与交付准备

Files:

  • Modify: 无;只验证 Task 13 已提交的文件。

Interfaces:

  • Consumes: 新增前端校验封装、后端校验路由和已有答案保存接口。

  • 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

Expected: PASS静态契约与生产构建均完成。

  • Step 3: 检查提交范围

Run: git status --short(分别在两个 worktree 执行)

Expected: 无本功能未提交改动;不暂存或删除主工作区既有未跟踪文件。

  • Step 4: 记录验证结果并等待集成指令

报告前后端提交号、测试结果与用户可见的“保存并进入第 2 步”行为。除非用户明确要求,不合并、不推送、不发布,也不启动本地后端。

自检结果

  • 规格覆盖Task 12 实现身份与六项非空的服务端校验Task 3 实现原型三行页面、Demo 校验、持久化和仅保存推进Task 4 覆盖跨端验证与不发布约束。
  • 占位扫描:计划没有未定义的后续实现项;第 1 步以外的页面仅在已有步骤导航中显示明确的待配置占位,未扩展第 2 步业务。
  • 类型一致性:前端 factors[].dimension/successAnalysis/failedAnalysis 与后端请求 DTO 的 Row 字段一致;控制器路由与 API 封装字符串一致。