From eada1aef10206bb4108610931c1ec2c2951b0f43 Mon Sep 17 00:00:00 2001 From: chenyuan Date: Fri, 7 Aug 2026 16:13:17 +0800 Subject: [PATCH] feat: validate product development process step four --- .../stu/StudentTrainingAnswerController.java | 17 ++++++ ...pmentProcessStepFourValidationRequest.java | 14 +++++ ...opmentProcessStepFourValidationResult.java | 10 ++++ ...ductDevelopmentProcessStepFourService.java | 9 +++ ...DevelopmentProcessStepFourServiceImpl.java | 52 +++++++++++++++++ .../StudentTrainingAnswerControllerTest.java | 20 +++++++ ...lopmentProcessStepFourServiceImplTest.java | 58 +++++++++++++++++++ 7 files changed, 180 insertions(+) create mode 100644 src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductDevelopmentProcessStepFourValidationRequest.java create mode 100644 src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductDevelopmentProcessStepFourValidationResult.java create mode 100644 src/main/java/com/sztzjy/linkCommerce/service/ProductDevelopmentProcessStepFourService.java create mode 100644 src/main/java/com/sztzjy/linkCommerce/service/impl/ProductDevelopmentProcessStepFourServiceImpl.java create mode 100644 src/test/java/com/sztzjy/linkCommerce/service/impl/ProductDevelopmentProcessStepFourServiceImplTest.java diff --git a/src/main/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerController.java b/src/main/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerController.java index 1061e88..d480c3c 100644 --- a/src/main/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerController.java +++ b/src/main/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerController.java @@ -26,6 +26,8 @@ import com.sztzjy.linkCommerce.entity.dto.ProductDevelopmentProcessStepTwoValida import com.sztzjy.linkCommerce.entity.dto.ProductDevelopmentProcessStepTwoValidationResult; import com.sztzjy.linkCommerce.entity.dto.ProductDevelopmentProcessStepThreeValidationRequest; import com.sztzjy.linkCommerce.entity.dto.ProductDevelopmentProcessStepThreeValidationResult; +import com.sztzjy.linkCommerce.entity.dto.ProductDevelopmentProcessStepFourValidationRequest; +import com.sztzjy.linkCommerce.entity.dto.ProductDevelopmentProcessStepFourValidationResult; import com.sztzjy.linkCommerce.service.NewProductSurveyStepOneService; import com.sztzjy.linkCommerce.service.NewProductSurveyStepTwoService; import com.sztzjy.linkCommerce.service.NewProductSurveyStepThreeService; @@ -37,6 +39,7 @@ import com.sztzjy.linkCommerce.service.ProductDevelopmentFactorsStepFourService; import com.sztzjy.linkCommerce.service.ProductDevelopmentProcessStepOneService; import com.sztzjy.linkCommerce.service.ProductDevelopmentProcessStepTwoService; import com.sztzjy.linkCommerce.service.ProductDevelopmentProcessStepThreeService; +import com.sztzjy.linkCommerce.service.ProductDevelopmentProcessStepFourService; import com.sztzjy.linkCommerce.service.StudentTrainingAnswerService; import com.sztzjy.linkCommerce.util.ResultEntity; import io.swagger.annotations.Api; @@ -85,6 +88,9 @@ public class StudentTrainingAnswerController { @Autowired private ProductDevelopmentProcessStepThreeService productDevelopmentProcessStepThreeService; + @Autowired + private ProductDevelopmentProcessStepFourService productDevelopmentProcessStepFourService; + @GetMapping("/{taskKey}") @ApiOperation("查询当前学生指定实训任务答案") public ResultEntity detail(@PathVariable String taskKey, @@ -234,6 +240,17 @@ public class StudentTrainingAnswerController { } } + @PostMapping("/product-development-process/step-4/validate") + @ApiOperation("产品开发主要流程第四步校验") + public ResultEntity validateProductDevelopmentProcessStepFour( + @RequestBody ProductDevelopmentProcessStepFourValidationRequest answer, HttpServletRequest request) { + try { + return new ResultEntity<>(HttpStatus.OK, "校验通过", productDevelopmentProcessStepFourService.validate(answer, TokenProvider.getJWTUser(request))); + } catch (ServiceException e) { + return new ResultEntity<>(e.getCode(), e.getMessage()); + } + } + @DeleteMapping("/{taskKey}") @ApiOperation("删除当前学生指定实训任务答案") public ResultEntity delete(@PathVariable String taskKey, diff --git a/src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductDevelopmentProcessStepFourValidationRequest.java b/src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductDevelopmentProcessStepFourValidationRequest.java new file mode 100644 index 0000000..cfc4334 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductDevelopmentProcessStepFourValidationRequest.java @@ -0,0 +1,14 @@ +package com.sztzjy.linkCommerce.entity.dto; + +public class ProductDevelopmentProcessStepFourValidationRequest { + private String parallelWorkAnalysis; + private String aiScenarioOne; + private String aiScenarioTwo; + + public String getParallelWorkAnalysis() { return parallelWorkAnalysis; } + public void setParallelWorkAnalysis(String parallelWorkAnalysis) { this.parallelWorkAnalysis = parallelWorkAnalysis; } + public String getAiScenarioOne() { return aiScenarioOne; } + public void setAiScenarioOne(String aiScenarioOne) { this.aiScenarioOne = aiScenarioOne; } + public String getAiScenarioTwo() { return aiScenarioTwo; } + public void setAiScenarioTwo(String aiScenarioTwo) { this.aiScenarioTwo = aiScenarioTwo; } +} diff --git a/src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductDevelopmentProcessStepFourValidationResult.java b/src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductDevelopmentProcessStepFourValidationResult.java new file mode 100644 index 0000000..7e9c351 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductDevelopmentProcessStepFourValidationResult.java @@ -0,0 +1,10 @@ +package com.sztzjy.linkCommerce.entity.dto; + +public class ProductDevelopmentProcessStepFourValidationResult { + private boolean valid; + private String message; + public boolean isValid() { return valid; } + public void setValid(boolean valid) { this.valid = valid; } + public String getMessage() { return message; } + public void setMessage(String message) { this.message = message; } +} diff --git a/src/main/java/com/sztzjy/linkCommerce/service/ProductDevelopmentProcessStepFourService.java b/src/main/java/com/sztzjy/linkCommerce/service/ProductDevelopmentProcessStepFourService.java new file mode 100644 index 0000000..ee1a0c3 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/service/ProductDevelopmentProcessStepFourService.java @@ -0,0 +1,9 @@ +package com.sztzjy.linkCommerce.service; + +import com.sztzjy.linkCommerce.config.security.JwtUser; +import com.sztzjy.linkCommerce.entity.dto.ProductDevelopmentProcessStepFourValidationRequest; +import com.sztzjy.linkCommerce.entity.dto.ProductDevelopmentProcessStepFourValidationResult; + +public interface ProductDevelopmentProcessStepFourService { + ProductDevelopmentProcessStepFourValidationResult validate(ProductDevelopmentProcessStepFourValidationRequest request, JwtUser user); +} diff --git a/src/main/java/com/sztzjy/linkCommerce/service/impl/ProductDevelopmentProcessStepFourServiceImpl.java b/src/main/java/com/sztzjy/linkCommerce/service/impl/ProductDevelopmentProcessStepFourServiceImpl.java new file mode 100644 index 0000000..c77c865 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/service/impl/ProductDevelopmentProcessStepFourServiceImpl.java @@ -0,0 +1,52 @@ +package com.sztzjy.linkCommerce.service.impl; + +import com.sztzjy.linkCommerce.config.exception.handler.ServiceException; +import com.sztzjy.linkCommerce.config.security.JwtUser; +import com.sztzjy.linkCommerce.entity.StudentTrainingAnswer; +import com.sztzjy.linkCommerce.entity.dto.ProductDevelopmentProcessStepFourValidationRequest; +import com.sztzjy.linkCommerce.entity.dto.ProductDevelopmentProcessStepFourValidationResult; +import com.sztzjy.linkCommerce.service.ProductDevelopmentProcessStepFourService; +import com.sztzjy.linkCommerce.service.StudentTrainingAnswerService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; + +@Service +public class ProductDevelopmentProcessStepFourServiceImpl implements ProductDevelopmentProcessStepFourService { + private static final String TASK_KEY = "product-development-process"; + private final StudentTrainingAnswerService studentTrainingAnswerService; + + @Autowired + public ProductDevelopmentProcessStepFourServiceImpl(StudentTrainingAnswerService studentTrainingAnswerService) { + this.studentTrainingAnswerService = studentTrainingAnswerService; + } + + @Override + public ProductDevelopmentProcessStepFourValidationResult validate(ProductDevelopmentProcessStepFourValidationRequest request, JwtUser user) { + requireStudent(user); + requireStepThreeCompleted(user); + if (StringUtils.isBlank(request == null ? null : request.getParallelWorkAnalysis())) { + throw new ServiceException(HttpStatus.BAD_REQUEST, "请填写可提前介入并同步完成的电商运营工作"); + } + if (StringUtils.isBlank(request.getAiScenarioOne()) || StringUtils.isBlank(request.getAiScenarioTwo())) { + throw new ServiceException(HttpStatus.BAD_REQUEST, "请至少填写 2 个 AI 工具提效应用场景"); + } + ProductDevelopmentProcessStepFourValidationResult result = new ProductDevelopmentProcessStepFourValidationResult(); + result.setValid(true); + result.setMessage("校验通过"); + return result; + } + + private void requireStudent(JwtUser user) { + if (user == null || StringUtils.isBlank(user.getUserId())) throw new ServiceException(HttpStatus.UNAUTHORIZED, "请先登录后再校验"); + if (user.getRoleId() != 4) throw new ServiceException(HttpStatus.FORBIDDEN, "仅学生可进行实训校验"); + } + + private void requireStepThreeCompleted(JwtUser user) { + StudentTrainingAnswer answer = studentTrainingAnswerService.get(TASK_KEY, null, user); + if (answer == null || StringUtils.isBlank(answer.getStep3Answer())) { + throw new ServiceException(HttpStatus.BAD_REQUEST, "请先上传并保存第三步甘特图"); + } + } +} diff --git a/src/test/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerControllerTest.java b/src/test/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerControllerTest.java index 0055d44..517732e 100644 --- a/src/test/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerControllerTest.java +++ b/src/test/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerControllerTest.java @@ -23,6 +23,8 @@ import com.sztzjy.linkCommerce.entity.dto.ProductDevelopmentProcessStepTwoValida import com.sztzjy.linkCommerce.entity.dto.ProductDevelopmentProcessStepTwoValidationResult; import com.sztzjy.linkCommerce.entity.dto.ProductDevelopmentProcessStepThreeValidationRequest; import com.sztzjy.linkCommerce.entity.dto.ProductDevelopmentProcessStepThreeValidationResult; +import com.sztzjy.linkCommerce.entity.dto.ProductDevelopmentProcessStepFourValidationRequest; +import com.sztzjy.linkCommerce.entity.dto.ProductDevelopmentProcessStepFourValidationResult; import com.sztzjy.linkCommerce.service.NewProductSurveyStepOneService; import com.sztzjy.linkCommerce.service.NewProductSurveyStepTwoService; import com.sztzjy.linkCommerce.service.NewProductSurveyStepThreeService; @@ -33,6 +35,7 @@ import com.sztzjy.linkCommerce.service.ProductDevelopmentFactorsStepThreeService import com.sztzjy.linkCommerce.service.ProductDevelopmentProcessStepOneService; import com.sztzjy.linkCommerce.service.ProductDevelopmentProcessStepTwoService; import com.sztzjy.linkCommerce.service.ProductDevelopmentProcessStepThreeService; +import com.sztzjy.linkCommerce.service.ProductDevelopmentProcessStepFourService; import com.sztzjy.linkCommerce.util.ResultEntity; import org.junit.jupiter.api.Test; import org.springframework.http.HttpStatus; @@ -215,6 +218,23 @@ class StudentTrainingAnswerControllerTest { verify(service).validate(any(ProductDevelopmentProcessStepThreeValidationRequest.class), any(JwtUser.class)); } + @Test + void validateProductDevelopmentProcessStepFourUsesTheAuthenticatedStudentAndReturnsTheValidationResult() { + StudentTrainingAnswerController controller = new StudentTrainingAnswerController(); + ProductDevelopmentProcessStepFourService service = mock(ProductDevelopmentProcessStepFourService.class); + ReflectionTestUtils.setField(controller, "productDevelopmentProcessStepFourService", service); + ProductDevelopmentProcessStepFourValidationResult validation = new ProductDevelopmentProcessStepFourValidationResult(); + validation.setValid(true); + when(service.validate(any(ProductDevelopmentProcessStepFourValidationRequest.class), any(JwtUser.class))).thenReturn(validation); + + ResultEntity result = controller + .validateProductDevelopmentProcessStepFour(new ProductDevelopmentProcessStepFourValidationRequest(), authenticatedStudentRequest()); + + assertEquals(HttpStatus.OK, result.getStatusCode()); + assertEquals(validation, result.getBody().getData()); + verify(service).validate(any(ProductDevelopmentProcessStepFourValidationRequest.class), any(JwtUser.class)); + } + private HttpServletRequest authenticatedStudentRequest() { JwtUser user = new JwtUser(); user.setUserId("stu-1"); diff --git a/src/test/java/com/sztzjy/linkCommerce/service/impl/ProductDevelopmentProcessStepFourServiceImplTest.java b/src/test/java/com/sztzjy/linkCommerce/service/impl/ProductDevelopmentProcessStepFourServiceImplTest.java new file mode 100644 index 0000000..af871fc --- /dev/null +++ b/src/test/java/com/sztzjy/linkCommerce/service/impl/ProductDevelopmentProcessStepFourServiceImplTest.java @@ -0,0 +1,58 @@ +package com.sztzjy.linkCommerce.service.impl; + +import com.sztzjy.linkCommerce.config.exception.handler.ServiceException; +import com.sztzjy.linkCommerce.config.security.JwtUser; +import com.sztzjy.linkCommerce.entity.StudentTrainingAnswer; +import com.sztzjy.linkCommerce.entity.dto.ProductDevelopmentProcessStepFourValidationRequest; +import com.sztzjy.linkCommerce.entity.dto.ProductDevelopmentProcessStepFourValidationResult; +import com.sztzjy.linkCommerce.service.StudentTrainingAnswerService; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +class ProductDevelopmentProcessStepFourServiceImplTest { + @Test + void validateAcceptsCompletedReflectionAfterGanttChartIsSaved() { + ProductDevelopmentProcessStepFourServiceImpl service = new ProductDevelopmentProcessStepFourServiceImpl(answerServiceWithStepThree()); + + ProductDevelopmentProcessStepFourValidationResult result = service.validate(completedRequest(), student()); + + assertTrue(result.isValid()); + } + + @Test + void validateRejectsMissingFieldsOrMissingGanttChart() { + ProductDevelopmentProcessStepFourServiceImpl service = new ProductDevelopmentProcessStepFourServiceImpl(answerServiceWithStepThree()); + ProductDevelopmentProcessStepFourValidationRequest missingScenario = completedRequest(); + missingScenario.setAiScenarioTwo(""); + assertThrows(ServiceException.class, () -> service.validate(missingScenario, student())); + + StudentTrainingAnswerService emptyAnswerService = mock(StudentTrainingAnswerService.class); + when(emptyAnswerService.get(anyString(), any(), any(JwtUser.class))).thenReturn(new StudentTrainingAnswer()); + ProductDevelopmentProcessStepFourServiceImpl noGanttService = new ProductDevelopmentProcessStepFourServiceImpl(emptyAnswerService); + assertThrows(ServiceException.class, () -> noGanttService.validate(completedRequest(), student())); + } + + private StudentTrainingAnswerService answerServiceWithStepThree() { + StudentTrainingAnswerService answerService = mock(StudentTrainingAnswerService.class); + StudentTrainingAnswer answer = new StudentTrainingAnswer(); + answer.setStep3Answer("{\"ganttImageUrl\":\"/profile/upload/gantt.png\"}"); + when(answerService.get(anyString(), any(), any(JwtUser.class))).thenReturn(answer); + return answerService; + } + + private ProductDevelopmentProcessStepFourValidationRequest completedRequest() { + ProductDevelopmentProcessStepFourValidationRequest request = new ProductDevelopmentProcessStepFourValidationRequest(); + request.setParallelWorkAnalysis("在产品定义阶段提前准备内容种草素材和预热蓄水计划。"); + request.setAiScenarioOne("使用AI归纳竞品评论与用户需求,辅助产出PRD初稿。"); + request.setAiScenarioTwo("使用AI生成详情页文案和主图脚本,提高运营素材生产效率。"); + return request; + } + + private JwtUser student() { JwtUser user = new JwtUser(); user.setUserId("stu-1"); user.setRoleId(4); return user; } +}