From 9c67878ac780ce5e7b4336c66171bb784a55052a Mon Sep 17 00:00:00 2001 From: chenyuan Date: Tue, 11 Aug 2026 23:46:19 +0800 Subject: [PATCH] feat: validate product value positioning step one --- .../stu/StudentTrainingAnswerController.java | 17 +++++ ...uePositioningStepOneValidationRequest.java | 31 +++++++++ ...luePositioningStepOneValidationResult.java | 11 ++++ ...ProductValuePositioningStepOneService.java | 9 +++ ...uctValuePositioningStepOneServiceImpl.java | 51 +++++++++++++++ ...aluePositioningStepOneServiceImplTest.java | 64 +++++++++++++++++++ 6 files changed, 183 insertions(+) create mode 100644 src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductValuePositioningStepOneValidationRequest.java create mode 100644 src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductValuePositioningStepOneValidationResult.java create mode 100644 src/main/java/com/sztzjy/linkCommerce/service/ProductValuePositioningStepOneService.java create mode 100644 src/main/java/com/sztzjy/linkCommerce/service/impl/ProductValuePositioningStepOneServiceImpl.java create mode 100644 src/test/java/com/sztzjy/linkCommerce/service/impl/ProductValuePositioningStepOneServiceImplTest.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 ea3673c..045620f 100644 --- a/src/main/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerController.java +++ b/src/main/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerController.java @@ -37,6 +37,8 @@ import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorStepThreeValidationRes import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorStepFourValidationRequest; import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorStepFourValidationResult; import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorWordPreviewResult; +import com.sztzjy.linkCommerce.entity.dto.ProductValuePositioningStepOneValidationRequest; +import com.sztzjy.linkCommerce.entity.dto.ProductValuePositioningStepOneValidationResult; import com.sztzjy.linkCommerce.service.NewProductSurveyStepOneService; import com.sztzjy.linkCommerce.service.MarketOpportunitySelectionStepOneService; import com.sztzjy.linkCommerce.service.MarketOpportunitySelectionStepTwoService; @@ -53,6 +55,7 @@ import com.sztzjy.linkCommerce.service.ConsumerBehaviorStepOneService; import com.sztzjy.linkCommerce.service.ConsumerBehaviorStepTwoService; import com.sztzjy.linkCommerce.service.ConsumerBehaviorStepThreeService; import com.sztzjy.linkCommerce.service.ConsumerBehaviorStepFourService; +import com.sztzjy.linkCommerce.service.ProductValuePositioningStepOneService; import com.sztzjy.linkCommerce.service.StudentTrainingAnswerService; import com.sztzjy.linkCommerce.util.ResultEntity; import io.swagger.annotations.Api; @@ -117,6 +120,9 @@ public class StudentTrainingAnswerController { @Autowired private ConsumerBehaviorStepFourService consumerBehaviorStepFourService; + @Autowired + private ProductValuePositioningStepOneService productValuePositioningStepOneService; + @GetMapping("/{taskKey}") @ApiOperation("查询当前学生指定实训任务答案") public ResultEntity detail(@PathVariable String taskKey, @@ -321,6 +327,17 @@ public class StudentTrainingAnswerController { } } + @PostMapping("/product-value-positioning/step-1/validate") + @ApiOperation("产品价值主张及定位第一步校验") + public ResultEntity validateProductValuePositioningStepOne( + @RequestBody ProductValuePositioningStepOneValidationRequest answer, HttpServletRequest request) { + try { + return new ResultEntity<>(HttpStatus.OK, "校验完成", productValuePositioningStepOneService.validate(answer, TokenProvider.getJWTUser(request))); + } catch (ServiceException e) { + return new ResultEntity<>(e.getCode(), e.getMessage()); + } + } + @PostMapping(value = "/consumer-behavior/step-3/word-preview", consumes = "multipart/form-data") @ApiOperation("消费者需求与行为第三步Word预览") public ResultEntity previewConsumerBehaviorStepThreeWord( diff --git a/src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductValuePositioningStepOneValidationRequest.java b/src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductValuePositioningStepOneValidationRequest.java new file mode 100644 index 0000000..696bc5f --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductValuePositioningStepOneValidationRequest.java @@ -0,0 +1,31 @@ +package com.sztzjy.linkCommerce.entity.dto; + +import java.util.List; + +public class ProductValuePositioningStepOneValidationRequest { + private List rows; + + public List getRows() { + return rows; + } + + public void setRows(List rows) { + this.rows = rows; + } + + public static class CorePainRow { + private Integer id; + private String type; + private String description; + private String scene; + + public Integer getId() { return id; } + public void setId(Integer id) { this.id = id; } + public String getType() { return type; } + public void setType(String type) { this.type = type; } + public String getDescription() { return description; } + public void setDescription(String description) { this.description = description; } + public String getScene() { return scene; } + public void setScene(String scene) { this.scene = scene; } + } +} diff --git a/src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductValuePositioningStepOneValidationResult.java b/src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductValuePositioningStepOneValidationResult.java new file mode 100644 index 0000000..7c795ad --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductValuePositioningStepOneValidationResult.java @@ -0,0 +1,11 @@ +package com.sztzjy.linkCommerce.entity.dto; + +public class ProductValuePositioningStepOneValidationResult { + 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/ProductValuePositioningStepOneService.java b/src/main/java/com/sztzjy/linkCommerce/service/ProductValuePositioningStepOneService.java new file mode 100644 index 0000000..557bd20 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/service/ProductValuePositioningStepOneService.java @@ -0,0 +1,9 @@ +package com.sztzjy.linkCommerce.service; + +import com.sztzjy.linkCommerce.config.security.JwtUser; +import com.sztzjy.linkCommerce.entity.dto.ProductValuePositioningStepOneValidationRequest; +import com.sztzjy.linkCommerce.entity.dto.ProductValuePositioningStepOneValidationResult; + +public interface ProductValuePositioningStepOneService { + ProductValuePositioningStepOneValidationResult validate(ProductValuePositioningStepOneValidationRequest request, JwtUser user); +} diff --git a/src/main/java/com/sztzjy/linkCommerce/service/impl/ProductValuePositioningStepOneServiceImpl.java b/src/main/java/com/sztzjy/linkCommerce/service/impl/ProductValuePositioningStepOneServiceImpl.java new file mode 100644 index 0000000..196ebf5 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/service/impl/ProductValuePositioningStepOneServiceImpl.java @@ -0,0 +1,51 @@ +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.dto.ProductValuePositioningStepOneValidationRequest; +import com.sztzjy.linkCommerce.entity.dto.ProductValuePositioningStepOneValidationResult; +import com.sztzjy.linkCommerce.service.ProductValuePositioningStepOneService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.List; + +@Service +public class ProductValuePositioningStepOneServiceImpl implements ProductValuePositioningStepOneService { + private static final List TYPES = Arrays.asList("核心痛点1", "核心痛点2", "核心痛点3", "弱需求", "伪需求"); + + @Override + public ProductValuePositioningStepOneValidationResult validate(ProductValuePositioningStepOneValidationRequest request, JwtUser user) { + requireStudent(user); + List rows = request == null ? null : request.getRows(); + if (rows == null || rows.size() != TYPES.size()) { + throw badRequest("请完成 3 个核心痛点、弱需求和伪需求的分析"); + } + for (int index = 0; index < TYPES.size(); index++) { + ProductValuePositioningStepOneValidationRequest.CorePainRow row = rows.get(index); + if (row == null || row.getId() == null || row.getId() != index + 1 || !TYPES.get(index).equals(row.getType()) + || StringUtils.isBlank(row.getDescription()) || StringUtils.isBlank(row.getScene())) { + throw badRequest("请按固定分类完整填写需求描述和场景描述"); + } + } + ProductValuePositioningStepOneValidationResult result = new ProductValuePositioningStepOneValidationResult(); + 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 ServiceException badRequest(String message) { + return new ServiceException(HttpStatus.BAD_REQUEST, message); + } +} diff --git a/src/test/java/com/sztzjy/linkCommerce/service/impl/ProductValuePositioningStepOneServiceImplTest.java b/src/test/java/com/sztzjy/linkCommerce/service/impl/ProductValuePositioningStepOneServiceImplTest.java new file mode 100644 index 0000000..77b0b8a --- /dev/null +++ b/src/test/java/com/sztzjy/linkCommerce/service/impl/ProductValuePositioningStepOneServiceImplTest.java @@ -0,0 +1,64 @@ +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.dto.ProductValuePositioningStepOneValidationRequest; +import com.sztzjy.linkCommerce.entity.dto.ProductValuePositioningStepOneValidationResult; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class ProductValuePositioningStepOneServiceImplTest { + private final ProductValuePositioningStepOneServiceImpl service = new ProductValuePositioningStepOneServiceImpl(); + + @Test + void acceptsCompleteFixedPainCategories() { + ProductValuePositioningStepOneValidationResult result = service.validate(request(), student()); + + assertTrue(result.isValid()); + } + + @Test + void rejectsChangedCategoryOrder() { + ProductValuePositioningStepOneValidationRequest request = request(); + request.getRows().get(0).setType("弱需求"); + + assertThrows(ServiceException.class, () -> service.validate(request, student())); + } + + @Test + void rejectsBlankScene() { + ProductValuePositioningStepOneValidationRequest request = request(); + request.getRows().get(2).setScene(" "); + + assertThrows(ServiceException.class, () -> service.validate(request, student())); + } + + private ProductValuePositioningStepOneValidationRequest request() { + List types = Arrays.asList("核心痛点1", "核心痛点2", "核心痛点3", "弱需求", "伪需求"); + List rows = new ArrayList<>(); + for (int index = 0; index < types.size(); index++) { + ProductValuePositioningStepOneValidationRequest.CorePainRow row = new ProductValuePositioningStepOneValidationRequest.CorePainRow(); + row.setId(index + 1); + row.setType(types.get(index)); + row.setDescription("需求描述" + (index + 1)); + row.setScene("场景描述" + (index + 1)); + rows.add(row); + } + ProductValuePositioningStepOneValidationRequest request = new ProductValuePositioningStepOneValidationRequest(); + request.setRows(rows); + return request; + } + + private JwtUser student() { + JwtUser user = new JwtUser(); + user.setUserId("student-id"); + user.setRoleId(4); + return user; + } +}