From 9c67878ac780ce5e7b4336c66171bb784a55052a Mon Sep 17 00:00:00 2001 From: chenyuan Date: Tue, 11 Aug 2026 23:46:19 +0800 Subject: [PATCH 1/4] 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; + } +} From 0aac26f13b9af5be1bd175c2ec884b5c67ee4fb7 Mon Sep 17 00:00:00 2001 From: chenyuan Date: Wed, 12 Aug 2026 01:01:31 +0800 Subject: [PATCH 2/4] feat: validate value proposition canvas --- .../stu/StudentTrainingAnswerController.java | 17 ++++++ ...uePositioningStepTwoValidationRequest.java | 40 +++++++++++++ ...luePositioningStepTwoValidationResult.java | 11 ++++ ...ProductValuePositioningStepTwoService.java | 9 +++ ...uctValuePositioningStepTwoServiceImpl.java | 41 +++++++++++++ ...aluePositioningStepTwoServiceImplTest.java | 57 +++++++++++++++++++ 6 files changed, 175 insertions(+) create mode 100644 src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductValuePositioningStepTwoValidationRequest.java create mode 100644 src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductValuePositioningStepTwoValidationResult.java create mode 100644 src/main/java/com/sztzjy/linkCommerce/service/ProductValuePositioningStepTwoService.java create mode 100644 src/main/java/com/sztzjy/linkCommerce/service/impl/ProductValuePositioningStepTwoServiceImpl.java create mode 100644 src/test/java/com/sztzjy/linkCommerce/service/impl/ProductValuePositioningStepTwoServiceImplTest.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 045620f..0318a80 100644 --- a/src/main/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerController.java +++ b/src/main/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerController.java @@ -39,6 +39,8 @@ import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorStepFourValidationResu 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.entity.dto.ProductValuePositioningStepTwoValidationRequest; +import com.sztzjy.linkCommerce.entity.dto.ProductValuePositioningStepTwoValidationResult; import com.sztzjy.linkCommerce.service.NewProductSurveyStepOneService; import com.sztzjy.linkCommerce.service.MarketOpportunitySelectionStepOneService; import com.sztzjy.linkCommerce.service.MarketOpportunitySelectionStepTwoService; @@ -56,6 +58,7 @@ 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.ProductValuePositioningStepTwoService; import com.sztzjy.linkCommerce.service.StudentTrainingAnswerService; import com.sztzjy.linkCommerce.util.ResultEntity; import io.swagger.annotations.Api; @@ -123,6 +126,9 @@ public class StudentTrainingAnswerController { @Autowired private ProductValuePositioningStepOneService productValuePositioningStepOneService; + @Autowired + private ProductValuePositioningStepTwoService productValuePositioningStepTwoService; + @GetMapping("/{taskKey}") @ApiOperation("查询当前学生指定实训任务答案") public ResultEntity detail(@PathVariable String taskKey, @@ -338,6 +344,17 @@ public class StudentTrainingAnswerController { } } + @PostMapping("/product-value-positioning/step-2/validate") + @ApiOperation("产品价值主张及定位第二步校验") + public ResultEntity validateProductValuePositioningStepTwo( + @RequestBody ProductValuePositioningStepTwoValidationRequest answer, HttpServletRequest request) { + try { + return new ResultEntity<>(HttpStatus.OK, "校验完成", productValuePositioningStepTwoService.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/ProductValuePositioningStepTwoValidationRequest.java b/src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductValuePositioningStepTwoValidationRequest.java new file mode 100644 index 0000000..f4014ee --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductValuePositioningStepTwoValidationRequest.java @@ -0,0 +1,40 @@ +package com.sztzjy.linkCommerce.entity.dto; + +public class ProductValuePositioningStepTwoValidationRequest { + private CustomerProfile customerProfile; + private ValueMap valueMap; + private String canvasUrl; + + public CustomerProfile getCustomerProfile() { return customerProfile; } + public void setCustomerProfile(CustomerProfile customerProfile) { this.customerProfile = customerProfile; } + public ValueMap getValueMap() { return valueMap; } + public void setValueMap(ValueMap valueMap) { this.valueMap = valueMap; } + public String getCanvasUrl() { return canvasUrl; } + public void setCanvasUrl(String canvasUrl) { this.canvasUrl = canvasUrl; } + + public static class CustomerProfile { + private String jobs; + private String pains; + private String gains; + + public String getJobs() { return jobs; } + public void setJobs(String jobs) { this.jobs = jobs; } + public String getPains() { return pains; } + public void setPains(String pains) { this.pains = pains; } + public String getGains() { return gains; } + public void setGains(String gains) { this.gains = gains; } + } + + public static class ValueMap { + private String products; + private String painRelievers; + private String gainCreators; + + public String getProducts() { return products; } + public void setProducts(String products) { this.products = products; } + public String getPainRelievers() { return painRelievers; } + public void setPainRelievers(String painRelievers) { this.painRelievers = painRelievers; } + public String getGainCreators() { return gainCreators; } + public void setGainCreators(String gainCreators) { this.gainCreators = gainCreators; } + } +} diff --git a/src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductValuePositioningStepTwoValidationResult.java b/src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductValuePositioningStepTwoValidationResult.java new file mode 100644 index 0000000..37e7c91 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductValuePositioningStepTwoValidationResult.java @@ -0,0 +1,11 @@ +package com.sztzjy.linkCommerce.entity.dto; + +public class ProductValuePositioningStepTwoValidationResult { + 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/ProductValuePositioningStepTwoService.java b/src/main/java/com/sztzjy/linkCommerce/service/ProductValuePositioningStepTwoService.java new file mode 100644 index 0000000..e5192ca --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/service/ProductValuePositioningStepTwoService.java @@ -0,0 +1,9 @@ +package com.sztzjy.linkCommerce.service; + +import com.sztzjy.linkCommerce.config.security.JwtUser; +import com.sztzjy.linkCommerce.entity.dto.ProductValuePositioningStepTwoValidationRequest; +import com.sztzjy.linkCommerce.entity.dto.ProductValuePositioningStepTwoValidationResult; + +public interface ProductValuePositioningStepTwoService { + ProductValuePositioningStepTwoValidationResult validate(ProductValuePositioningStepTwoValidationRequest request, JwtUser user); +} diff --git a/src/main/java/com/sztzjy/linkCommerce/service/impl/ProductValuePositioningStepTwoServiceImpl.java b/src/main/java/com/sztzjy/linkCommerce/service/impl/ProductValuePositioningStepTwoServiceImpl.java new file mode 100644 index 0000000..c015deb --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/service/impl/ProductValuePositioningStepTwoServiceImpl.java @@ -0,0 +1,41 @@ +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.ProductValuePositioningStepTwoValidationRequest; +import com.sztzjy.linkCommerce.entity.dto.ProductValuePositioningStepTwoValidationResult; +import com.sztzjy.linkCommerce.service.ProductValuePositioningStepTwoService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; + +@Service +public class ProductValuePositioningStepTwoServiceImpl implements ProductValuePositioningStepTwoService { + @Override + public ProductValuePositioningStepTwoValidationResult validate(ProductValuePositioningStepTwoValidationRequest request, JwtUser user) { + requireStudent(user); + ProductValuePositioningStepTwoValidationRequest.CustomerProfile profile = request == null ? null : request.getCustomerProfile(); + ProductValuePositioningStepTwoValidationRequest.ValueMap valueMap = request == null ? null : request.getValueMap(); + if (profile == null || valueMap == null + || StringUtils.isBlank(profile.getJobs()) || StringUtils.isBlank(profile.getPains()) || StringUtils.isBlank(profile.getGains()) + || StringUtils.isBlank(valueMap.getProducts()) || StringUtils.isBlank(valueMap.getPainRelievers()) || StringUtils.isBlank(valueMap.getGainCreators()) + || StringUtils.isBlank(request.getCanvasUrl())) { + throw badRequest("请完整填写用户档案、产品价值地图并上传产品价值主张画布"); + } + ProductValuePositioningStepTwoValidationResult result = new ProductValuePositioningStepTwoValidationResult(); + 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/ProductValuePositioningStepTwoServiceImplTest.java b/src/test/java/com/sztzjy/linkCommerce/service/impl/ProductValuePositioningStepTwoServiceImplTest.java new file mode 100644 index 0000000..056d795 --- /dev/null +++ b/src/test/java/com/sztzjy/linkCommerce/service/impl/ProductValuePositioningStepTwoServiceImplTest.java @@ -0,0 +1,57 @@ +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.ProductValuePositioningStepTwoValidationRequest; +import com.sztzjy.linkCommerce.entity.dto.ProductValuePositioningStepTwoValidationResult; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class ProductValuePositioningStepTwoServiceImplTest { + private final ProductValuePositioningStepTwoServiceImpl service = new ProductValuePositioningStepTwoServiceImpl(); + + @Test + void acceptsCompleteValuePropositionCanvas() { + ProductValuePositioningStepTwoValidationResult result = service.validate(request(), student()); + assertTrue(result.isValid()); + } + + @Test + void rejectsBlankPainReliever() { + ProductValuePositioningStepTwoValidationRequest request = request(); + request.getValueMap().setPainRelievers(" "); + assertThrows(ServiceException.class, () -> service.validate(request, student())); + } + + @Test + void rejectsMissingCanvas() { + ProductValuePositioningStepTwoValidationRequest request = request(); + request.setCanvasUrl(""); + assertThrows(ServiceException.class, () -> service.validate(request, student())); + } + + private ProductValuePositioningStepTwoValidationRequest request() { + ProductValuePositioningStepTwoValidationRequest.CustomerProfile profile = new ProductValuePositioningStepTwoValidationRequest.CustomerProfile(); + profile.setJobs("完成高质量睡眠"); + profile.setPains("入睡困难"); + profile.setGains("温和唤醒"); + ProductValuePositioningStepTwoValidationRequest.ValueMap valueMap = new ProductValuePositioningStepTwoValidationRequest.ValueMap(); + valueMap.setProducts("智能助眠灯"); + valueMap.setPainRelievers("节律光缓解入睡困难"); + valueMap.setGainCreators("睡眠报告创造额外收益"); + ProductValuePositioningStepTwoValidationRequest request = new ProductValuePositioningStepTwoValidationRequest(); + request.setCustomerProfile(profile); + request.setValueMap(valueMap); + request.setCanvasUrl("/uploads/value-canvas.png"); + return request; + } + + private JwtUser student() { + JwtUser user = new JwtUser(); + user.setUserId("student-id"); + user.setRoleId(4); + return user; + } +} From 5b7602bca3a3e617343cd6dd6eb506d707851f4b Mon Sep 17 00:00:00 2001 From: chenyuan Date: Wed, 12 Aug 2026 08:08:27 +0800 Subject: [PATCH 3/4] feat: validate value positioning statements --- .../stu/StudentTrainingAnswerController.java | 17 ++++++ ...PositioningStepThreeValidationRequest.java | 32 +++++++++++ ...ePositioningStepThreeValidationResult.java | 11 ++++ ...oductValuePositioningStepThreeService.java | 9 ++++ ...tValuePositioningStepThreeServiceImpl.java | 42 +++++++++++++++ ...uePositioningStepThreeServiceImplTest.java | 54 +++++++++++++++++++ 6 files changed, 165 insertions(+) create mode 100644 src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductValuePositioningStepThreeValidationRequest.java create mode 100644 src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductValuePositioningStepThreeValidationResult.java create mode 100644 src/main/java/com/sztzjy/linkCommerce/service/ProductValuePositioningStepThreeService.java create mode 100644 src/main/java/com/sztzjy/linkCommerce/service/impl/ProductValuePositioningStepThreeServiceImpl.java create mode 100644 src/test/java/com/sztzjy/linkCommerce/service/impl/ProductValuePositioningStepThreeServiceImplTest.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 0318a80..0702a84 100644 --- a/src/main/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerController.java +++ b/src/main/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerController.java @@ -41,6 +41,8 @@ import com.sztzjy.linkCommerce.entity.dto.ProductValuePositioningStepOneValidati import com.sztzjy.linkCommerce.entity.dto.ProductValuePositioningStepOneValidationResult; import com.sztzjy.linkCommerce.entity.dto.ProductValuePositioningStepTwoValidationRequest; import com.sztzjy.linkCommerce.entity.dto.ProductValuePositioningStepTwoValidationResult; +import com.sztzjy.linkCommerce.entity.dto.ProductValuePositioningStepThreeValidationRequest; +import com.sztzjy.linkCommerce.entity.dto.ProductValuePositioningStepThreeValidationResult; import com.sztzjy.linkCommerce.service.NewProductSurveyStepOneService; import com.sztzjy.linkCommerce.service.MarketOpportunitySelectionStepOneService; import com.sztzjy.linkCommerce.service.MarketOpportunitySelectionStepTwoService; @@ -59,6 +61,7 @@ import com.sztzjy.linkCommerce.service.ConsumerBehaviorStepThreeService; import com.sztzjy.linkCommerce.service.ConsumerBehaviorStepFourService; import com.sztzjy.linkCommerce.service.ProductValuePositioningStepOneService; import com.sztzjy.linkCommerce.service.ProductValuePositioningStepTwoService; +import com.sztzjy.linkCommerce.service.ProductValuePositioningStepThreeService; import com.sztzjy.linkCommerce.service.StudentTrainingAnswerService; import com.sztzjy.linkCommerce.util.ResultEntity; import io.swagger.annotations.Api; @@ -129,6 +132,9 @@ public class StudentTrainingAnswerController { @Autowired private ProductValuePositioningStepTwoService productValuePositioningStepTwoService; + @Autowired + private ProductValuePositioningStepThreeService productValuePositioningStepThreeService; + @GetMapping("/{taskKey}") @ApiOperation("查询当前学生指定实训任务答案") public ResultEntity detail(@PathVariable String taskKey, @@ -355,6 +361,17 @@ public class StudentTrainingAnswerController { } } + @PostMapping("/product-value-positioning/step-3/validate") + @ApiOperation("产品价值主张及定位第三步校验") + public ResultEntity validateProductValuePositioningStepThree( + @RequestBody ProductValuePositioningStepThreeValidationRequest answer, HttpServletRequest request) { + try { + return new ResultEntity<>(HttpStatus.OK, "校验完成", productValuePositioningStepThreeService.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/ProductValuePositioningStepThreeValidationRequest.java b/src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductValuePositioningStepThreeValidationRequest.java new file mode 100644 index 0000000..221cebb --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductValuePositioningStepThreeValidationRequest.java @@ -0,0 +1,32 @@ +package com.sztzjy.linkCommerce.entity.dto; + +import java.util.List; + +public class ProductValuePositioningStepThreeValidationRequest { + private List statements; + + public List getStatements() { return statements; } + public void setStatements(List statements) { this.statements = statements; } + + public static class PositioningStatement { + private Integer id; + private String targetUser; + private String productName; + private String productCategory; + private String coreValue; + private String differentiationReason; + + public Integer getId() { return id; } + public void setId(Integer id) { this.id = id; } + public String getTargetUser() { return targetUser; } + public void setTargetUser(String targetUser) { this.targetUser = targetUser; } + public String getProductName() { return productName; } + public void setProductName(String productName) { this.productName = productName; } + public String getProductCategory() { return productCategory; } + public void setProductCategory(String productCategory) { this.productCategory = productCategory; } + public String getCoreValue() { return coreValue; } + public void setCoreValue(String coreValue) { this.coreValue = coreValue; } + public String getDifferentiationReason() { return differentiationReason; } + public void setDifferentiationReason(String differentiationReason) { this.differentiationReason = differentiationReason; } + } +} diff --git a/src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductValuePositioningStepThreeValidationResult.java b/src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductValuePositioningStepThreeValidationResult.java new file mode 100644 index 0000000..6fdad78 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductValuePositioningStepThreeValidationResult.java @@ -0,0 +1,11 @@ +package com.sztzjy.linkCommerce.entity.dto; + +public class ProductValuePositioningStepThreeValidationResult { + 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/ProductValuePositioningStepThreeService.java b/src/main/java/com/sztzjy/linkCommerce/service/ProductValuePositioningStepThreeService.java new file mode 100644 index 0000000..647f018 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/service/ProductValuePositioningStepThreeService.java @@ -0,0 +1,9 @@ +package com.sztzjy.linkCommerce.service; + +import com.sztzjy.linkCommerce.config.security.JwtUser; +import com.sztzjy.linkCommerce.entity.dto.ProductValuePositioningStepThreeValidationRequest; +import com.sztzjy.linkCommerce.entity.dto.ProductValuePositioningStepThreeValidationResult; + +public interface ProductValuePositioningStepThreeService { + ProductValuePositioningStepThreeValidationResult validate(ProductValuePositioningStepThreeValidationRequest request, JwtUser user); +} diff --git a/src/main/java/com/sztzjy/linkCommerce/service/impl/ProductValuePositioningStepThreeServiceImpl.java b/src/main/java/com/sztzjy/linkCommerce/service/impl/ProductValuePositioningStepThreeServiceImpl.java new file mode 100644 index 0000000..b7adf7c --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/service/impl/ProductValuePositioningStepThreeServiceImpl.java @@ -0,0 +1,42 @@ +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.ProductValuePositioningStepThreeValidationRequest; +import com.sztzjy.linkCommerce.entity.dto.ProductValuePositioningStepThreeValidationResult; +import com.sztzjy.linkCommerce.service.ProductValuePositioningStepThreeService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class ProductValuePositioningStepThreeServiceImpl implements ProductValuePositioningStepThreeService { + @Override + public ProductValuePositioningStepThreeValidationResult validate(ProductValuePositioningStepThreeValidationRequest request, JwtUser user) { + requireStudent(user); + List statements = request == null ? null : request.getStatements(); + if (statements == null || statements.size() != 2) throw badRequest("请填写两个产品价值定位陈述版本"); + for (int index = 0; index < statements.size(); index++) { + ProductValuePositioningStepThreeValidationRequest.PositioningStatement statement = statements.get(index); + if (statement == null || statement.getId() == null || statement.getId() != index + 1 + || StringUtils.isBlank(statement.getTargetUser()) || StringUtils.isBlank(statement.getProductName()) + || StringUtils.isBlank(statement.getProductCategory()) || StringUtils.isBlank(statement.getCoreValue()) + || StringUtils.isBlank(statement.getDifferentiationReason())) { + throw badRequest("请完整填写两个产品价值定位陈述版本"); + } + } + ProductValuePositioningStepThreeValidationResult result = new ProductValuePositioningStepThreeValidationResult(); + 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/ProductValuePositioningStepThreeServiceImplTest.java b/src/test/java/com/sztzjy/linkCommerce/service/impl/ProductValuePositioningStepThreeServiceImplTest.java new file mode 100644 index 0000000..168d7d1 --- /dev/null +++ b/src/test/java/com/sztzjy/linkCommerce/service/impl/ProductValuePositioningStepThreeServiceImplTest.java @@ -0,0 +1,54 @@ +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.ProductValuePositioningStepThreeValidationRequest; +import com.sztzjy.linkCommerce.entity.dto.ProductValuePositioningStepThreeValidationResult; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class ProductValuePositioningStepThreeServiceImplTest { + private final ProductValuePositioningStepThreeServiceImpl service = new ProductValuePositioningStepThreeServiceImpl(); + + @Test + void acceptsTwoCompleteStatements() { + ProductValuePositioningStepThreeValidationResult result = service.validate(request(), student()); + assertTrue(result.isValid()); + } + + @Test + void rejectsAnIncompleteStatement() { + ProductValuePositioningStepThreeValidationRequest request = request(); + request.getStatements().get(1).setCoreValue(" "); + assertThrows(ServiceException.class, () -> service.validate(request, student())); + } + + private ProductValuePositioningStepThreeValidationRequest request() { + List statements = new ArrayList<>(); + for (int index = 1; index <= 2; index++) { + ProductValuePositioningStepThreeValidationRequest.PositioningStatement statement = new ProductValuePositioningStepThreeValidationRequest.PositioningStatement(); + statement.setId(index); + statement.setTargetUser("目标用户" + index); + statement.setProductName("产品名称" + index); + statement.setProductCategory("产品类别" + index); + statement.setCoreValue("核心价值" + index); + statement.setDifferentiationReason("差异化理由" + index); + statements.add(statement); + } + ProductValuePositioningStepThreeValidationRequest request = new ProductValuePositioningStepThreeValidationRequest(); + request.setStatements(statements); + return request; + } + + private JwtUser student() { + JwtUser user = new JwtUser(); + user.setUserId("student-id"); + user.setRoleId(4); + return user; + } +} From f51133090fe674a33c7f9b2768b74e1c6b58d6dd Mon Sep 17 00:00:00 2001 From: chenyuan Date: Wed, 12 Aug 2026 09:16:33 +0800 Subject: [PATCH 4/4] feat: validate product positioning mrd --- .../stu/StudentTrainingAnswerController.java | 17 ++++++ ...ePositioningStepFourValidationRequest.java | 41 +++++++++++++ ...uePositioningStepFourValidationResult.java | 11 ++++ ...roductValuePositioningStepFourService.java | 9 +++ ...ctValuePositioningStepFourServiceImpl.java | 47 ++++++++++++++ ...luePositioningStepFourServiceImplTest.java | 61 +++++++++++++++++++ 6 files changed, 186 insertions(+) create mode 100644 src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductValuePositioningStepFourValidationRequest.java create mode 100644 src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductValuePositioningStepFourValidationResult.java create mode 100644 src/main/java/com/sztzjy/linkCommerce/service/ProductValuePositioningStepFourService.java create mode 100644 src/main/java/com/sztzjy/linkCommerce/service/impl/ProductValuePositioningStepFourServiceImpl.java create mode 100644 src/test/java/com/sztzjy/linkCommerce/service/impl/ProductValuePositioningStepFourServiceImplTest.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 0702a84..6cceeb4 100644 --- a/src/main/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerController.java +++ b/src/main/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerController.java @@ -43,6 +43,8 @@ import com.sztzjy.linkCommerce.entity.dto.ProductValuePositioningStepTwoValidati import com.sztzjy.linkCommerce.entity.dto.ProductValuePositioningStepTwoValidationResult; import com.sztzjy.linkCommerce.entity.dto.ProductValuePositioningStepThreeValidationRequest; import com.sztzjy.linkCommerce.entity.dto.ProductValuePositioningStepThreeValidationResult; +import com.sztzjy.linkCommerce.entity.dto.ProductValuePositioningStepFourValidationRequest; +import com.sztzjy.linkCommerce.entity.dto.ProductValuePositioningStepFourValidationResult; import com.sztzjy.linkCommerce.service.NewProductSurveyStepOneService; import com.sztzjy.linkCommerce.service.MarketOpportunitySelectionStepOneService; import com.sztzjy.linkCommerce.service.MarketOpportunitySelectionStepTwoService; @@ -62,6 +64,7 @@ import com.sztzjy.linkCommerce.service.ConsumerBehaviorStepFourService; import com.sztzjy.linkCommerce.service.ProductValuePositioningStepOneService; import com.sztzjy.linkCommerce.service.ProductValuePositioningStepTwoService; import com.sztzjy.linkCommerce.service.ProductValuePositioningStepThreeService; +import com.sztzjy.linkCommerce.service.ProductValuePositioningStepFourService; import com.sztzjy.linkCommerce.service.StudentTrainingAnswerService; import com.sztzjy.linkCommerce.util.ResultEntity; import io.swagger.annotations.Api; @@ -135,6 +138,9 @@ public class StudentTrainingAnswerController { @Autowired private ProductValuePositioningStepThreeService productValuePositioningStepThreeService; + @Autowired + private ProductValuePositioningStepFourService productValuePositioningStepFourService; + @GetMapping("/{taskKey}") @ApiOperation("查询当前学生指定实训任务答案") public ResultEntity detail(@PathVariable String taskKey, @@ -372,6 +378,17 @@ public class StudentTrainingAnswerController { } } + @PostMapping("/product-value-positioning/step-4/validate") + @ApiOperation("产品价值主张及定位第四步校验") + public ResultEntity validateProductValuePositioningStepFour( + @RequestBody ProductValuePositioningStepFourValidationRequest answer, HttpServletRequest request) { + try { + return new ResultEntity<>(HttpStatus.OK, "校验完成", productValuePositioningStepFourService.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/ProductValuePositioningStepFourValidationRequest.java b/src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductValuePositioningStepFourValidationRequest.java new file mode 100644 index 0000000..67556bb --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductValuePositioningStepFourValidationRequest.java @@ -0,0 +1,41 @@ +package com.sztzjy.linkCommerce.entity.dto; + +import java.util.List; + +public class ProductValuePositioningStepFourValidationRequest { + private List marketInsights; + private List marketOverview; + private List userAnalysis; + private List competitorAnalysis; + private List productRequirements; + private String productPositioning; + + public List getMarketInsights() { return marketInsights; } + public void setMarketInsights(List marketInsights) { this.marketInsights = marketInsights; } + public List getMarketOverview() { return marketOverview; } + public void setMarketOverview(List marketOverview) { this.marketOverview = marketOverview; } + public List getUserAnalysis() { return userAnalysis; } + public void setUserAnalysis(List userAnalysis) { this.userAnalysis = userAnalysis; } + public List getCompetitorAnalysis() { return competitorAnalysis; } + public void setCompetitorAnalysis(List competitorAnalysis) { this.competitorAnalysis = competitorAnalysis; } + public List getProductRequirements() { return productRequirements; } + public void setProductRequirements(List productRequirements) { this.productRequirements = productRequirements; } + public String getProductPositioning() { return productPositioning; } + public void setProductPositioning(String productPositioning) { this.productPositioning = productPositioning; } + + public static class MrdRow { + private Integer id; + private String module; + private String title; + private String content; + + public Integer getId() { return id; } + public void setId(Integer id) { this.id = id; } + public String getModule() { return module; } + public void setModule(String module) { this.module = module; } + public String getTitle() { return title; } + public void setTitle(String title) { this.title = title; } + public String getContent() { return content; } + public void setContent(String content) { this.content = content; } + } +} diff --git a/src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductValuePositioningStepFourValidationResult.java b/src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductValuePositioningStepFourValidationResult.java new file mode 100644 index 0000000..7079821 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/entity/dto/ProductValuePositioningStepFourValidationResult.java @@ -0,0 +1,11 @@ +package com.sztzjy.linkCommerce.entity.dto; + +public class ProductValuePositioningStepFourValidationResult { + 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/ProductValuePositioningStepFourService.java b/src/main/java/com/sztzjy/linkCommerce/service/ProductValuePositioningStepFourService.java new file mode 100644 index 0000000..962a0fb --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/service/ProductValuePositioningStepFourService.java @@ -0,0 +1,9 @@ +package com.sztzjy.linkCommerce.service; + +import com.sztzjy.linkCommerce.config.security.JwtUser; +import com.sztzjy.linkCommerce.entity.dto.ProductValuePositioningStepFourValidationRequest; +import com.sztzjy.linkCommerce.entity.dto.ProductValuePositioningStepFourValidationResult; + +public interface ProductValuePositioningStepFourService { + ProductValuePositioningStepFourValidationResult validate(ProductValuePositioningStepFourValidationRequest request, JwtUser user); +} diff --git a/src/main/java/com/sztzjy/linkCommerce/service/impl/ProductValuePositioningStepFourServiceImpl.java b/src/main/java/com/sztzjy/linkCommerce/service/impl/ProductValuePositioningStepFourServiceImpl.java new file mode 100644 index 0000000..b39d426 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/service/impl/ProductValuePositioningStepFourServiceImpl.java @@ -0,0 +1,47 @@ +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.ProductValuePositioningStepFourValidationRequest; +import com.sztzjy.linkCommerce.entity.dto.ProductValuePositioningStepFourValidationResult; +import com.sztzjy.linkCommerce.service.ProductValuePositioningStepFourService; +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 ProductValuePositioningStepFourServiceImpl implements ProductValuePositioningStepFourService { + private static final List REQUIRED_COUNTS = Arrays.asList(3, 2, 3, 2, 5); + + @Override + public ProductValuePositioningStepFourValidationResult validate(ProductValuePositioningStepFourValidationRequest request, JwtUser user) { + requireStudent(user); + if (request == null || StringUtils.isBlank(request.getProductPositioning())) throw badRequest("请完整填写 MRD 的五组分析内容和产品定位"); + List> sections = Arrays.asList( + request.getMarketInsights(), request.getMarketOverview(), request.getUserAnalysis(), request.getCompetitorAnalysis(), request.getProductRequirements()); + for (int sectionIndex = 0; sectionIndex < sections.size(); sectionIndex++) { + List rows = sections.get(sectionIndex); + if (rows == null || rows.size() != REQUIRED_COUNTS.get(sectionIndex)) throw badRequest("请按 MRD 固定结构完整填写内容"); + for (int index = 0; index < rows.size(); index++) { + ProductValuePositioningStepFourValidationRequest.MrdRow row = rows.get(index); + if (row == null || row.getId() == null || row.getId() != index + 1 || StringUtils.isBlank(row.getTitle()) || StringUtils.isBlank(row.getContent())) { + throw badRequest("请按 MRD 固定结构完整填写内容"); + } + } + } + ProductValuePositioningStepFourValidationResult result = new ProductValuePositioningStepFourValidationResult(); + result.setValid(true); + result.setMessage("MRD 文档填写完整。"); + 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/ProductValuePositioningStepFourServiceImplTest.java b/src/test/java/com/sztzjy/linkCommerce/service/impl/ProductValuePositioningStepFourServiceImplTest.java new file mode 100644 index 0000000..36e4fce --- /dev/null +++ b/src/test/java/com/sztzjy/linkCommerce/service/impl/ProductValuePositioningStepFourServiceImplTest.java @@ -0,0 +1,61 @@ +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.ProductValuePositioningStepFourValidationRequest; +import com.sztzjy.linkCommerce.entity.dto.ProductValuePositioningStepFourValidationResult; +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 ProductValuePositioningStepFourServiceImplTest { + private final ProductValuePositioningStepFourServiceImpl service = new ProductValuePositioningStepFourServiceImpl(); + + @Test + void acceptsCompleteMrd() { + ProductValuePositioningStepFourValidationResult result = service.validate(request(), student()); + assertTrue(result.isValid()); + } + + @Test + void rejectsIncompleteMrdRow() { + ProductValuePositioningStepFourValidationRequest request = request(); + request.getUserAnalysis().get(2).setContent(" "); + assertThrows(ServiceException.class, () -> service.validate(request, student())); + } + + private ProductValuePositioningStepFourValidationRequest request() { + ProductValuePositioningStepFourValidationRequest request = new ProductValuePositioningStepFourValidationRequest(); + request.setMarketInsights(rows(3)); + request.setMarketOverview(rows(2)); + request.setUserAnalysis(rows(3)); + request.setCompetitorAnalysis(rows(2)); + request.setProductRequirements(rows(5)); + request.setProductPositioning("面向目标用户的差异化产品定位"); + return request; + } + + private List rows(int count) { + List rows = new ArrayList<>(); + for (int index = 1; index <= count; index++) { + ProductValuePositioningStepFourValidationRequest.MrdRow row = new ProductValuePositioningStepFourValidationRequest.MrdRow(); + row.setId(index); + row.setTitle("标题" + index); + row.setContent("内容" + index); + rows.add(row); + } + return rows; + } + + private JwtUser student() { + JwtUser user = new JwtUser(); + user.setUserId("student-id"); + user.setRoleId(4); + return user; + } +}