From 6462dd13d2e88c1fe8c220adc965500de82be465 Mon Sep 17 00:00:00 2001 From: chenyuan Date: Fri, 7 Aug 2026 18:51:19 +0800 Subject: [PATCH] feat: validate market opportunity selection step two --- .../stu/StudentTrainingAnswerController.java | 17 ++++++++ ...nitySelectionStepTwoValidationRequest.java | 17 ++++++++ ...unitySelectionStepTwoValidationResult.java | 10 +++++ ...ketOpportunitySelectionStepTwoService.java | 9 ++++ ...pportunitySelectionStepTwoServiceImpl.java | 42 +++++++++++++++++++ .../StudentTrainingAnswerControllerTest.java | 15 +++++++ ...tunitySelectionStepTwoServiceImplTest.java | 40 ++++++++++++++++++ 7 files changed, 150 insertions(+) create mode 100644 src/main/java/com/sztzjy/linkCommerce/entity/dto/MarketOpportunitySelectionStepTwoValidationRequest.java create mode 100644 src/main/java/com/sztzjy/linkCommerce/entity/dto/MarketOpportunitySelectionStepTwoValidationResult.java create mode 100644 src/main/java/com/sztzjy/linkCommerce/service/MarketOpportunitySelectionStepTwoService.java create mode 100644 src/main/java/com/sztzjy/linkCommerce/service/impl/MarketOpportunitySelectionStepTwoServiceImpl.java create mode 100644 src/test/java/com/sztzjy/linkCommerce/service/impl/MarketOpportunitySelectionStepTwoServiceImplTest.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 a67505a..d23a3f3 100644 --- a/src/main/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerController.java +++ b/src/main/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerController.java @@ -8,6 +8,8 @@ import com.sztzjy.linkCommerce.entity.dto.NewProductSurveyStepOneCheckRequest; import com.sztzjy.linkCommerce.entity.dto.NewProductSurveyStepOneCheckResult; import com.sztzjy.linkCommerce.entity.dto.MarketOpportunitySelectionStepOneCheckRequest; import com.sztzjy.linkCommerce.entity.dto.MarketOpportunitySelectionStepOneCheckResult; +import com.sztzjy.linkCommerce.entity.dto.MarketOpportunitySelectionStepTwoValidationRequest; +import com.sztzjy.linkCommerce.entity.dto.MarketOpportunitySelectionStepTwoValidationResult; import com.sztzjy.linkCommerce.entity.dto.NewProductSurveyStepTwoValidationRequest; import com.sztzjy.linkCommerce.entity.dto.NewProductSurveyStepTwoValidationResult; import com.sztzjy.linkCommerce.entity.dto.NewProductSurveyStepThreeValidationRequest; @@ -24,6 +26,7 @@ import com.sztzjy.linkCommerce.entity.dto.ProductDevelopmentFactorsStepFourValid import com.sztzjy.linkCommerce.entity.dto.ProductDevelopmentFactorsStepFourValidationResult; import com.sztzjy.linkCommerce.service.NewProductSurveyStepOneService; import com.sztzjy.linkCommerce.service.MarketOpportunitySelectionStepOneService; +import com.sztzjy.linkCommerce.service.MarketOpportunitySelectionStepTwoService; import com.sztzjy.linkCommerce.service.NewProductSurveyStepTwoService; import com.sztzjy.linkCommerce.service.NewProductSurveyStepThreeService; import com.sztzjy.linkCommerce.service.NewProductSurveyStepFourService; @@ -54,6 +57,9 @@ public class StudentTrainingAnswerController { @Autowired private MarketOpportunitySelectionStepOneService marketOpportunitySelectionStepOneService; + @Autowired + private MarketOpportunitySelectionStepTwoService marketOpportunitySelectionStepTwoService; + @Autowired private NewProductSurveyStepTwoService newProductSurveyStepTwoService; @@ -122,6 +128,17 @@ public class StudentTrainingAnswerController { } } + @PostMapping("/market-opportunity-selection/step-2/validate") + @ApiOperation("市场机会识别与选品第二步校验") + public ResultEntity validateMarketOpportunitySelectionStepTwo( + @RequestBody MarketOpportunitySelectionStepTwoValidationRequest answer, HttpServletRequest request) { + try { + return new ResultEntity<>(HttpStatus.OK, "校验通过", marketOpportunitySelectionStepTwoService.validate(answer, TokenProvider.getJWTUser(request))); + } catch (ServiceException e) { + return new ResultEntity<>(e.getCode(), e.getMessage()); + } + } + @PostMapping("/new-product-survey/step-2/validate") @ApiOperation("新产品调查与分析第二步校验") public ResultEntity validateNewProductSurveyStepTwo( diff --git a/src/main/java/com/sztzjy/linkCommerce/entity/dto/MarketOpportunitySelectionStepTwoValidationRequest.java b/src/main/java/com/sztzjy/linkCommerce/entity/dto/MarketOpportunitySelectionStepTwoValidationRequest.java new file mode 100644 index 0000000..8d4c8aa --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/entity/dto/MarketOpportunitySelectionStepTwoValidationRequest.java @@ -0,0 +1,17 @@ +package com.sztzjy.linkCommerce.entity.dto; + +import java.util.List; + +public class MarketOpportunitySelectionStepTwoValidationRequest { + private List weights; + public List getWeights() { return weights; } + public void setWeights(List weights) { this.weights = weights; } + public static class Weight { + private String id; + private Integer weight; + public String getId() { return id; } + public void setId(String id) { this.id = id; } + public Integer getWeight() { return weight; } + public void setWeight(Integer weight) { this.weight = weight; } + } +} diff --git a/src/main/java/com/sztzjy/linkCommerce/entity/dto/MarketOpportunitySelectionStepTwoValidationResult.java b/src/main/java/com/sztzjy/linkCommerce/entity/dto/MarketOpportunitySelectionStepTwoValidationResult.java new file mode 100644 index 0000000..17003b4 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/entity/dto/MarketOpportunitySelectionStepTwoValidationResult.java @@ -0,0 +1,10 @@ +package com.sztzjy.linkCommerce.entity.dto; + +public class MarketOpportunitySelectionStepTwoValidationResult { + 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/MarketOpportunitySelectionStepTwoService.java b/src/main/java/com/sztzjy/linkCommerce/service/MarketOpportunitySelectionStepTwoService.java new file mode 100644 index 0000000..2156d23 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/service/MarketOpportunitySelectionStepTwoService.java @@ -0,0 +1,9 @@ +package com.sztzjy.linkCommerce.service; + +import com.sztzjy.linkCommerce.config.security.JwtUser; +import com.sztzjy.linkCommerce.entity.dto.MarketOpportunitySelectionStepTwoValidationRequest; +import com.sztzjy.linkCommerce.entity.dto.MarketOpportunitySelectionStepTwoValidationResult; + +public interface MarketOpportunitySelectionStepTwoService { + MarketOpportunitySelectionStepTwoValidationResult validate(MarketOpportunitySelectionStepTwoValidationRequest request, JwtUser user); +} diff --git a/src/main/java/com/sztzjy/linkCommerce/service/impl/MarketOpportunitySelectionStepTwoServiceImpl.java b/src/main/java/com/sztzjy/linkCommerce/service/impl/MarketOpportunitySelectionStepTwoServiceImpl.java new file mode 100644 index 0000000..f4ad38e --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/service/impl/MarketOpportunitySelectionStepTwoServiceImpl.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.StudentTrainingAnswer; +import com.sztzjy.linkCommerce.entity.dto.MarketOpportunitySelectionStepTwoValidationRequest; +import com.sztzjy.linkCommerce.entity.dto.MarketOpportunitySelectionStepTwoValidationResult; +import com.sztzjy.linkCommerce.service.MarketOpportunitySelectionStepTwoService; +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; + +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.Set; + +@Service +public class MarketOpportunitySelectionStepTwoServiceImpl implements MarketOpportunitySelectionStepTwoService { + private static final String TASK_KEY = "market-opportunity-selection"; + private static final Set INDICATOR_IDS = new LinkedHashSet<>(Arrays.asList("search-count", "transaction-amount", "transaction-growth", "category-transaction-growth", "demand-supply-ratio", "online-products", "online-merchants", "organic-traffic-ratio")); + @Autowired private StudentTrainingAnswerService studentTrainingAnswerService; + + @Override + public MarketOpportunitySelectionStepTwoValidationResult validate(MarketOpportunitySelectionStepTwoValidationRequest request, JwtUser user) { + if (user == null || StringUtils.isBlank(user.getUserId())) throw new ServiceException(HttpStatus.UNAUTHORIZED, "请先登录后再校验"); + if (user.getRoleId() != 4) throw new ServiceException(HttpStatus.FORBIDDEN, "仅学生可进行实训校验"); + StudentTrainingAnswer savedAnswer = studentTrainingAnswerService.get(TASK_KEY, null, user); + if (savedAnswer == null || StringUtils.isBlank(savedAnswer.getStep1Answer())) throw new ServiceException(HttpStatus.BAD_REQUEST, "请先完成并保存第 1 步指标分类"); + if (request == null || request.getWeights() == null || request.getWeights().size() != INDICATOR_IDS.size()) throw new ServiceException(HttpStatus.BAD_REQUEST, "请为全部二级指标填写权重"); + Set submittedIds = new LinkedHashSet<>(); int totalWeight = 0; + for (MarketOpportunitySelectionStepTwoValidationRequest.Weight item : request.getWeights()) { + String id = item == null ? "" : StringUtils.trimToEmpty(item.getId()); Integer weight = item == null ? null : item.getWeight(); + if (!INDICATOR_IDS.contains(id) || !submittedIds.add(id) || weight == null || weight < 1 || weight > 100) throw new ServiceException(HttpStatus.BAD_REQUEST, "二级指标权重需为 1 至 100 的整数"); + totalWeight += weight; + } + if (!submittedIds.equals(INDICATOR_IDS)) throw new ServiceException(HttpStatus.BAD_REQUEST, "提交的二级指标不完整"); + if (totalWeight != 100) throw new ServiceException(HttpStatus.BAD_REQUEST, "二级指标权重合计必须为 100%"); + MarketOpportunitySelectionStepTwoValidationResult result = new MarketOpportunitySelectionStepTwoValidationResult(); result.setValid(true); result.setMessage("校验通过"); return result; + } +} 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 8aea745..501edd4 100644 --- a/src/test/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerControllerTest.java +++ b/src/test/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerControllerTest.java @@ -7,6 +7,8 @@ import com.sztzjy.linkCommerce.entity.dto.NewProductSurveyStepOneCheckRequest; import com.sztzjy.linkCommerce.entity.dto.NewProductSurveyStepOneCheckResult; import com.sztzjy.linkCommerce.entity.dto.MarketOpportunitySelectionStepOneCheckRequest; import com.sztzjy.linkCommerce.entity.dto.MarketOpportunitySelectionStepOneCheckResult; +import com.sztzjy.linkCommerce.entity.dto.MarketOpportunitySelectionStepTwoValidationRequest; +import com.sztzjy.linkCommerce.entity.dto.MarketOpportunitySelectionStepTwoValidationResult; import com.sztzjy.linkCommerce.entity.dto.NewProductSurveyStepTwoValidationRequest; import com.sztzjy.linkCommerce.entity.dto.NewProductSurveyStepTwoValidationResult; import com.sztzjy.linkCommerce.entity.dto.NewProductSurveyStepThreeValidationRequest; @@ -21,6 +23,7 @@ import com.sztzjy.linkCommerce.entity.dto.ProductDevelopmentFactorsStepThreeVali import com.sztzjy.linkCommerce.entity.dto.ProductDevelopmentFactorsStepThreeValidationResult; import com.sztzjy.linkCommerce.service.NewProductSurveyStepOneService; import com.sztzjy.linkCommerce.service.MarketOpportunitySelectionStepOneService; +import com.sztzjy.linkCommerce.service.MarketOpportunitySelectionStepTwoService; import com.sztzjy.linkCommerce.service.NewProductSurveyStepTwoService; import com.sztzjy.linkCommerce.service.NewProductSurveyStepThreeService; import com.sztzjy.linkCommerce.service.NewProductSurveyStepFourService; @@ -72,6 +75,18 @@ class StudentTrainingAnswerControllerTest { verify(service).check(any(MarketOpportunitySelectionStepOneCheckRequest.class), any(JwtUser.class)); } + @Test + void validateMarketOpportunitySelectionStepTwoUsesTheAuthenticatedStudentAndReturnsTheValidationResult() { + StudentTrainingAnswerController controller = new StudentTrainingAnswerController(); + MarketOpportunitySelectionStepTwoService service = mock(MarketOpportunitySelectionStepTwoService.class); + ReflectionTestUtils.setField(controller, "marketOpportunitySelectionStepTwoService", service); + MarketOpportunitySelectionStepTwoValidationResult validation = new MarketOpportunitySelectionStepTwoValidationResult(); validation.setValid(true); + when(service.validate(any(MarketOpportunitySelectionStepTwoValidationRequest.class), any(JwtUser.class))).thenReturn(validation); + ResultEntity result = controller.validateMarketOpportunitySelectionStepTwo(new MarketOpportunitySelectionStepTwoValidationRequest(), authenticatedStudentRequest()); + assertEquals(HttpStatus.OK, result.getStatusCode()); assertEquals(validation, result.getBody().getData()); + verify(service).validate(any(MarketOpportunitySelectionStepTwoValidationRequest.class), any(JwtUser.class)); + } + @Test void validateStepTwoUsesTheAuthenticatedStudentAndReturnsTheValidationResult() { StudentTrainingAnswerController controller = new StudentTrainingAnswerController(); diff --git a/src/test/java/com/sztzjy/linkCommerce/service/impl/MarketOpportunitySelectionStepTwoServiceImplTest.java b/src/test/java/com/sztzjy/linkCommerce/service/impl/MarketOpportunitySelectionStepTwoServiceImplTest.java new file mode 100644 index 0000000..665321b --- /dev/null +++ b/src/test/java/com/sztzjy/linkCommerce/service/impl/MarketOpportunitySelectionStepTwoServiceImplTest.java @@ -0,0 +1,40 @@ +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.MarketOpportunitySelectionStepTwoValidationRequest; +import com.sztzjy.linkCommerce.entity.dto.MarketOpportunitySelectionStepTwoValidationResult; +import com.sztzjy.linkCommerce.service.StudentTrainingAnswerService; +import org.junit.jupiter.api.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.ArrayList; +import java.util.Arrays; + +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 MarketOpportunitySelectionStepTwoServiceImplTest { + @Test + void validateAcceptsAllWeightsThatTotalOneHundredAfterStepOneIsSaved() { + MarketOpportunitySelectionStepTwoValidationResult result = serviceWithStepOne().validate(completedRequest(), student()); + assertTrue(result.isValid()); + } + + @Test + void validateRejectsIncorrectTotalAndMissingStepOne() { + MarketOpportunitySelectionStepTwoValidationRequest invalid = completedRequest(); invalid.getWeights().get(0).setWeight(21); + assertThrows(ServiceException.class, () -> serviceWithStepOne().validate(invalid, student())); + MarketOpportunitySelectionStepTwoServiceImpl service = new MarketOpportunitySelectionStepTwoServiceImpl(); StudentTrainingAnswerService answers = mock(StudentTrainingAnswerService.class); ReflectionTestUtils.setField(service, "studentTrainingAnswerService", answers); when(answers.get(anyString(), any(), any(JwtUser.class))).thenReturn(null); + assertThrows(ServiceException.class, () -> service.validate(completedRequest(), student())); + } + + private MarketOpportunitySelectionStepTwoServiceImpl serviceWithStepOne() { MarketOpportunitySelectionStepTwoServiceImpl service = new MarketOpportunitySelectionStepTwoServiceImpl(); StudentTrainingAnswerService answers = mock(StudentTrainingAnswerService.class); StudentTrainingAnswer answer = new StudentTrainingAnswer(); answer.setStep1Answer("{\"answers\":{}}"); when(answers.get(anyString(), any(), any(JwtUser.class))).thenReturn(answer); ReflectionTestUtils.setField(service, "studentTrainingAnswerService", answers); return service; } + private MarketOpportunitySelectionStepTwoValidationRequest completedRequest() { MarketOpportunitySelectionStepTwoValidationRequest request = new MarketOpportunitySelectionStepTwoValidationRequest(); ArrayList weights = new ArrayList<>(); int[] values = {20, 15, 15, 15, 10, 10, 10, 5}; Arrays.asList("search-count", "transaction-amount", "transaction-growth", "category-transaction-growth", "demand-supply-ratio", "online-products", "online-merchants", "organic-traffic-ratio").forEach(id -> { MarketOpportunitySelectionStepTwoValidationRequest.Weight item = new MarketOpportunitySelectionStepTwoValidationRequest.Weight(); item.setId(id); item.setWeight(values[weights.size()]); weights.add(item); }); request.setWeights(weights); return request; } + private JwtUser student() { JwtUser user = new JwtUser(); user.setUserId("stu-1"); user.setRoleId(4); return user; } +}