From f3001d31648cf9b35a74b0d412110aa78bb69c1b Mon Sep 17 00:00:00 2001 From: chenyuan Date: Fri, 7 Aug 2026 14:56:58 +0800 Subject: [PATCH 1/6] docs: specify product development factors step three --- ...roduct-development-factors-step3-design.md | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 docs/superpowers/specs/2026-08-07-product-development-factors-step3-design.md diff --git a/docs/superpowers/specs/2026-08-07-product-development-factors-step3-design.md b/docs/superpowers/specs/2026-08-07-product-development-factors-step3-design.md new file mode 100644 index 0000000..5ea4918 --- /dev/null +++ b/docs/superpowers/specs/2026-08-07-product-development-factors-step3-design.md @@ -0,0 +1,52 @@ +# 产品开发关键因素第 3 步设计 + +## 目标 + +为学生端“产品开发关键因素”实训补齐第 3 步“商业化及市场反馈”分析页。学生对成功产品与失败产品分别完成三项固定维度分析,保存后进入第 4 步,但不提交整个任务。 + +## 范围与约束 + +- 仅修改学生端页面及其学生答案校验接口;不调整教师端、任务配置端或其他模块。 +- 延续现有 `product-development-factors` 单页多步骤页面、答案存储和本地草稿机制。 +- 不新增数据库表或字段:第 3 步内容持久化到现有答案记录的 `step3Answer`,进度写入 `currentStep`。 +- 成功保存第 3 步后,保存动作固定为 `SAVE`、`submitted: false`、`currentStep: 4`;不得调用任务提交动作。 + +## 学生页面 + +当 `currentStep === 2` 时展示标题“商业化及市场反馈角度分析,填写下表:”,表头为“维度 / 评估项 / 成功产品 / 失败产品”。 + +固定三行及评估项: + +| 维度 | 评估项 | +| --- | --- | +| 供应链与成本控制 | 产品成本是否可控?是否支持规模化?是否具备备选供应链?关键元器件(如芯片、传感器)有≥2家可替换供应商 | +| 商业模式 | 盈利模式是否清晰?硬件销售毛利率≥50%,或有订阅服务(如云存储、AI 功能包)。是否构建“数据 - 反馈 - 迭代”闭环?产品使用数据可回传,驱动功能优化与新版本开发 | +| 市场反馈 | 是否处于需求上升期?功能、设计、服务、品牌是否有差异化壁垒?用户是否高留存、高互动、低退货? | + +每行的成功产品与失败产品列各提供多行文本输入。六个输入均为必填;保存按钮为“保存并进入第 4 步”,清空按钮沿用现有行为。页面恢复已保存答案和本地草稿;导出内容与清空逻辑覆盖前三步。 + +若任务配置少于四步,保存仍成功,提示“已保存,当前任务尚未配置第 4 步”,并留在当前页;否则切换到第 4 步占位页。 + +## 前端数据流 + +- 定义 `STEP_THREE_FACTOR_ROWS` 和 `stepThreeRows`,结构与前两步相同:`dimension`、`criteria`、`successAnalysis`、`failedAnalysis`。 +- 在 `buildDraft` 中新增 `stepThree`;在加载答案与草稿时恢复第 3 步。 +- 仅第 3 步保存时组装 `{ factors: [{ dimension, successAnalysis, failedAnalysis }] }` 发往校验接口;校验通过后,保存 `step3Answer`。 +- 演示模式使用与正式接口相同的固定维度和六项非空规则,保证离线体验一致。 + +## 后端校验接口 + +新增 `POST /api/student-training-answers/product-development-factors/step-3/validate`。 + +- 仅允许已登录、`roleId == 4` 的学生调用;未登录返回 401,非学生返回 403。 +- 请求体为 `factors` 数组,每项含 `dimension`、`successAnalysis`、`failedAnalysis`。 +- 必须且只能提交一次下列三个维度:供应链与成本控制、商业模式、市场反馈。 +- 每个维度的成功产品与失败产品分析去除空白后都必须有内容。 +- 合法请求返回 `{ valid: true, message: "校验通过" }`;缺项、重复、伪造维度或空内容返回明确的 400 错误信息。 + +## 测试 + +- 后端服务测试覆盖:完整合法数据、空内容、伪造或重复维度、非学生身份。 +- 控制器测试覆盖:接口把认证学生与请求参数传给第 3 步校验服务并返回结果。 +- 前端静态契约测试覆盖:三项固定维度、`step3Answer` 持久化、校验 API、六项非空演示校验,以及只保存进入第 4 步、不提交任务的行为。 +- 最终运行后端 `mvn -B test` 和前端静态契约测试、生产构建。 From 493e7ded86a6cf1ea711cc557e009ed63e1376e0 Mon Sep 17 00:00:00 2001 From: chenyuan Date: Fri, 7 Aug 2026 14:59:41 +0800 Subject: [PATCH 2/6] docs: plan product development factors step three --- ...08-07-product-development-factors-step3.md | 221 ++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100644 docs/superpowers/plans/2026-08-07-product-development-factors-step3.md diff --git a/docs/superpowers/plans/2026-08-07-product-development-factors-step3.md b/docs/superpowers/plans/2026-08-07-product-development-factors-step3.md new file mode 100644 index 0000000..e3dcc05 --- /dev/null +++ b/docs/superpowers/plans/2026-08-07-product-development-factors-step3.md @@ -0,0 +1,221 @@ +# 产品开发关键因素第 3 步 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:** 为学生端产品开发关键因素实训实现第 3 步商业化及市场反馈分析、服务端校验与只保存进入第 4 步的流程。 + +**Architecture:** 前端在现有 `product-development-factors.vue` 中增加固定三行的第 3 步表格,并把数据独立写入 `step3Answer`。后端按前两步的分层模式新增 DTO、服务及控制器入口,严格验证学生身份、三项固定维度及六个非空分析字段。 + +**Tech Stack:** Vue 3 Composition API、Element Plus、现有 request API;Spring Boot、JUnit 5、Mockito、Maven。 + +## Global Constraints + +- 仅修改学生端页面及学生答案校验接口;不调整教师端或任务配置。 +- 不新增数据库表或字段,使用既有答案记录的 `step3Answer` 与 `currentStep`。 +- 第 3 步保存固定使用 `SAVE`、`submitted: false`、`currentStep: 4`,不得提交任务。 +- 保存前必须通过正式或演示模式的一致校验:三个固定维度、六个分析字段全部非空。 + +--- + +### Task 1: 第 3 步后端校验服务 + +**Files:** +- Create: `src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductDevelopmentFactorsStepThreeValidationRequest.java` +- Create: `src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductDevelopmentFactorsStepThreeValidationResult.java` +- Create: `src/main/java/com/sztzjy/linkCommerce/service/ProductDevelopmentFactorsStepThreeService.java` +- Create: `src/main/java/com/sztzjy/linkCommerce/service/impl/ProductDevelopmentFactorsStepThreeServiceImpl.java` +- Test: `src/test/java/com/sztzjy/linkCommerce/service/impl/ProductDevelopmentFactorsStepThreeServiceImplTest.java` + +**Interfaces:** +- Consumes: `JwtUser`、`ServiceException`、Spring HTTP status conventions。 +- Produces: `ProductDevelopmentFactorsStepThreeService#validate(ProductDevelopmentFactorsStepThreeValidationRequest, JwtUser)`,返回 `{ valid, message }`。 + +- [ ] **Step 1: 写失败服务测试** + +```java +@Test void acceptsCompleteStudentAnalyses() { + assertTrue(new ProductDevelopmentFactorsStepThreeServiceImpl().validate(valid(), student()).isValid()); +} + +@Test void rejectsBlankOrForgedAnalyses() { + ProductDevelopmentFactorsStepThreeValidationRequest blank = valid(); + blank.getFactors().get(2).setFailedAnalysis(" "); + assertThrows(ServiceException.class, () -> service.validate(blank, student())); + ProductDevelopmentFactorsStepThreeValidationRequest forged = valid(); + forged.getFactors().get(2).setDimension("伪造维度"); + assertThrows(ServiceException.class, () -> service.validate(forged, student())); +} +``` + +- [ ] **Step 2: 运行测试确认失败** + +Run: `mvn -B -Dtest=ProductDevelopmentFactorsStepThreeServiceImplTest test` + +Expected: FAIL,因为 DTO 和服务尚不存在。 + +- [ ] **Step 3: 实现最小服务** + +```java +private static final Set DIMENSIONS = new LinkedHashSet<>(Arrays.asList( + "供应链与成本控制", "商业模式", "市场反馈")); + +if (user == null || StringUtils.isBlank(user.getUserId())) throw new ServiceException(HttpStatus.UNAUTHORIZED, "请先登录后再提交"); +if (user.getRoleId() != 4) throw new ServiceException(HttpStatus.FORBIDDEN, "仅学生可提交实训答案"); +``` + +校验 `factors` 数量为 3、每个维度只出现一次且属于 `DIMENSIONS`,并要求每个 `successAnalysis`、`failedAnalysis` 均非空;合法时返回 `valid=true`、`message="校验通过"`。 + +- [ ] **Step 4: 运行服务测试确认通过** + +Run: `mvn -B -Dtest=ProductDevelopmentFactorsStepThreeServiceImplTest test` + +Expected: PASS,覆盖合法、空值、伪造维度和非学生身份。 + +- [ ] **Step 5: 提交后端服务** + +```bash +git add src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductDevelopmentFactorsStepThreeValidationRequest.java src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductDevelopmentFactorsStepThreeValidationResult.java src/main/java/com/sztzjy/linkCommerce/service/ProductDevelopmentFactorsStepThreeService.java src/main/java/com/sztzjy/linkCommerce/service/impl/ProductDevelopmentFactorsStepThreeServiceImpl.java src/test/java/com/sztzjy/linkCommerce/service/impl/ProductDevelopmentFactorsStepThreeServiceImplTest.java +git commit -m "feat: validate product factors step three" +``` + +### Task 2: 第 3 步校验控制器入口 + +**Files:** +- Modify: `src/main/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerController.java` +- Modify: `src/test/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerControllerTest.java` + +**Interfaces:** +- Consumes: Task 1 的 `ProductDevelopmentFactorsStepThreeService`。 +- Produces: `POST /api/student-training-answers/product-development-factors/step-3/validate`。 + +- [ ] **Step 1: 写失败控制器测试** + +```java +@Test void validateProductDevelopmentFactorsStepThreeUsesTheAuthenticatedStudentAndReturnsTheValidationResult() { + ProductDevelopmentFactorsStepThreeService service = mock(ProductDevelopmentFactorsStepThreeService.class); + ReflectionTestUtils.setField(controller, "productDevelopmentFactorsStepThreeService", service); + when(service.validate(any(ProductDevelopmentFactorsStepThreeValidationRequest.class), any(JwtUser.class))).thenReturn(validation); + ResultEntity result = controller.validateProductDevelopmentFactorsStepThree(new ProductDevelopmentFactorsStepThreeValidationRequest(), authenticatedStudentRequest()); + assertEquals(HttpStatus.OK, result.getStatusCode()); +} +``` + +- [ ] **Step 2: 运行测试确认失败** + +Run: `mvn -B -Dtest=StudentTrainingAnswerControllerTest test` + +Expected: FAIL,因为第 3 步服务注入字段和控制器方法尚不存在。 + +- [ ] **Step 3: 增加路由与转发** + +```java +@PostMapping("/product-development-factors/step-3/validate") +public ResultEntity validateProductDevelopmentFactorsStepThree( + @RequestBody ProductDevelopmentFactorsStepThreeValidationRequest request, + HttpServletRequest servletRequest) { + return ResultEntity.ok(productDevelopmentFactorsStepThreeService.validate(request, getJwtUser(servletRequest))); +} +``` + +- [ ] **Step 4: 运行控制器测试确认通过** + +Run: `mvn -B -Dtest=StudentTrainingAnswerControllerTest,ProductDevelopmentFactorsStepThreeServiceImplTest test` + +Expected: PASS。 + +- [ ] **Step 5: 提交控制器** + +```bash +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 three validation" +``` + +### Task 3: 第 3 步学生页面、草稿和 API + +**Files:** +- Modify: `src/views/foundation/product-development-factors.vue` +- Modify: `src/api/studentTrainingAnswer.js` +- Modify: `tests/product-development-factors-step-one.static.test.cjs` + +**Interfaces:** +- Consumes: Task 2 的正式校验 URL、既有 `saveStudentTrainingAnswer`、`getStudentTrainingAnswer`。 +- Produces: 第 3 步固定三行分析页面;`step3Answer`、`currentStep: 4`、`checkProductDevelopmentFactorsStepThree(payload)`。 + +- [ ] **Step 1: 先扩展前端静态契约测试** + +```js +assert.match(pageSource, /STEP_THREE_FACTOR_ROWS/); +assert.match(pageSource, /供应链与成本控制/); +assert.match(pageSource, /商业模式/); +assert.match(pageSource, /市场反馈/); +assert.match(pageSource, /step3Answer/); +assert.match(pageSource, /saveAndGoToStepFour/); +assert.match(apiSource, /product-development-factors\/step-3\/validate/); +``` + +- [ ] **Step 2: 运行静态测试确认失败** + +Run: `node tests/product-development-factors-step-one.static.test.cjs` + +Expected: FAIL,因为第 3 步常量、保存函数和 API 尚不存在。 + +- [ ] **Step 3: 实现前端最小改动** + +在 `currentStep === 2` 增加表格;建立 `STEP_THREE_FACTOR_ROWS`、`createStepThreeRows`、`stepThreeRows`,并扩展草稿、答案恢复、导出与清空。 + +```js +function buildStepThreeAnswerPayload(saveAction = "SAVE", persistedStep = 4) { + return { + step3Answer: JSON.stringify(buildStepThreeOutcome()), + currentStep: persistedStep, + submitted: false, + saveAction, + }; +} +``` + +实现正式 API 和演示模式的一致校验;`saveAndGoToStepFour` 成功后清理草稿并在第 4 步存在时将 `currentStep` 设为 `3`,否则显示已保存但未配置第 4 步的提示。 + +- [ ] **Step 4: 运行前端测试与生产构建** + +Run: `node tests/product-development-factors-step-one.static.test.cjs; npm run build:prod` + +Expected: 静态契约测试与生产构建均 PASS。 + +- [ ] **Step 5: 提交前端改动** + +```bash +git add src/views/foundation/product-development-factors.vue src/api/studentTrainingAnswer.js tests/product-development-factors-step-one.static.test.cjs +git commit -m "feat: add product factors step three analysis" +``` + +### Task 4: 全量回归与交付检查 + +**Files:** +- Verify only: 前后端工作树的已提交变更。 + +**Interfaces:** +- Consumes: Tasks 1–3。 +- Produces: 经过完整回归验证的前后端功能分支。 + +- [ ] **Step 1: 运行后端全量测试** + +Run: `mvn -B test` + +Expected: BUILD SUCCESS,所有测试通过。 + +- [ ] **Step 2: 运行前端完整验证** + +Run: `node tests/product-development-factors-step-one.static.test.cjs; npm run build:prod` + +Expected: 静态契约测试和生产构建成功。 + +- [ ] **Step 3: 审查工作树** + +Run: `git status --short; git log --oneline -3` + +Expected: 仅本功能的预期提交;不纳入主工作区已有未跟踪文件。 + +- [ ] **Step 4: 汇报并等待集成指令** + +报告功能、提交和验证结果,等待用户明确指示合并、推送或发布。 From 331f35797a32c13fbf0bb58458a451954f4e3748 Mon Sep 17 00:00:00 2001 From: chenyuan Date: Fri, 7 Aug 2026 15:07:10 +0800 Subject: [PATCH 3/6] feat: add product factors step three analysis --- src/api/studentTrainingAnswer.js | 24 ++++++++++++ .../product-development-factors.vue | 38 ++++++++++++++++++- ...velopment-factors-step-one.static.test.cjs | 11 ++++++ 3 files changed, 71 insertions(+), 2 deletions(-) diff --git a/src/api/studentTrainingAnswer.js b/src/api/studentTrainingAnswer.js index 28bbcf4..17c12a4 100644 --- a/src/api/studentTrainingAnswer.js +++ b/src/api/studentTrainingAnswer.js @@ -251,3 +251,27 @@ export function checkProductDevelopmentFactorsStepTwo(payload) { if (isStudentDemo()) { try { return Promise.resolve({ code: 200, data: validateProductDevelopmentFactorsStepTwoDemo(payload) }); } catch (error) { return Promise.reject(error); } } return request({ url: "/api/student-training-answers/product-development-factors/step-2/validate", method: "post", data: payload, headers: { repeatSubmit: false } }); } + +const productDevelopmentFactorsStepThreeDimensions = ["供应链与成本控制", "商业模式", "市场反馈"]; + +function validateProductDevelopmentFactorsStepThreeDemo(payload) { + const factors = Array.isArray(payload?.factors) ? payload.factors : []; + const dimensions = factors.map((factor) => String(factor?.dimension || "").trim()); + if (factors.length !== productDevelopmentFactorsStepThreeDimensions.length + || new Set(dimensions).size !== productDevelopmentFactorsStepThreeDimensions.length + || productDevelopmentFactorsStepThreeDimensions.some((dimension) => !dimensions.includes(dimension))) { + throw new Error("请完成供应链与成本控制、商业模式和市场反馈分析"); + } + const incomplete = factors.find((factor) => !String(factor?.successAnalysis || "").trim() + || !String(factor?.failedAnalysis || "").trim()); + if (incomplete) throw new Error(`请完成${incomplete.dimension || "该项"}的成功产品和失败产品分析`); + return { valid: true, message: "校验通过" }; +} + +export function checkProductDevelopmentFactorsStepThree(payload) { + if (isStudentDemo()) { + try { return Promise.resolve({ code: 200, data: validateProductDevelopmentFactorsStepThreeDemo(payload) }); } + catch (error) { return Promise.reject(error); } + } + return request({ url: "/api/student-training-answers/product-development-factors/step-3/validate", method: "post", data: payload, headers: { repeatSubmit: false } }); +} diff --git a/src/views/foundation/product-development-factors.vue b/src/views/foundation/product-development-factors.vue index c219197..66d37ec 100644 --- a/src/views/foundation/product-development-factors.vue +++ b/src/views/foundation/product-development-factors.vue @@ -104,6 +104,14 @@
+