From 766dde4d7393420d75f05b9060d2b05e3ce207ae Mon Sep 17 00:00:00 2001 From: chenyuan Date: Tue, 11 Aug 2026 23:05:12 +0800 Subject: [PATCH] feat: validate feature conversion step one --- .../stu/StudentTrainingAnswerController.java | 17 ++++++ ...ureConversionStepOneValidationRequest.java | 34 +++++++++++ ...tureConversionStepOneValidationResult.java | 11 ++++ .../FeatureConversionStepOneService.java | 9 +++ .../FeatureConversionStepOneServiceImpl.java | 51 ++++++++++++++++ ...atureConversionStepOneServiceImplTest.java | 60 +++++++++++++++++++ 6 files changed, 182 insertions(+) create mode 100644 src/main/java/com/sztzjy/linkCommerce/entity/dto/FeatureConversionStepOneValidationRequest.java create mode 100644 src/main/java/com/sztzjy/linkCommerce/entity/dto/FeatureConversionStepOneValidationResult.java create mode 100644 src/main/java/com/sztzjy/linkCommerce/service/FeatureConversionStepOneService.java create mode 100644 src/main/java/com/sztzjy/linkCommerce/service/impl/FeatureConversionStepOneServiceImpl.java create mode 100644 src/test/java/com/sztzjy/linkCommerce/service/impl/FeatureConversionStepOneServiceImplTest.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..88d0ea5 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.FeatureConversionStepOneValidationRequest; +import com.sztzjy.linkCommerce.entity.dto.FeatureConversionStepOneValidationResult; 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.FeatureConversionStepOneService; 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 FeatureConversionStepOneService featureConversionStepOneService; + @GetMapping("/{taskKey}") @ApiOperation("查询当前学生指定实训任务答案") public ResultEntity detail(@PathVariable String taskKey, @@ -321,6 +327,17 @@ public class StudentTrainingAnswerController { } } + @PostMapping("/feature-conversion/step-1/validate") + @ApiOperation("需求转化为功能第一步校验") + public ResultEntity validateFeatureConversionStepOne( + @RequestBody FeatureConversionStepOneValidationRequest answer, HttpServletRequest request) { + try { + return new ResultEntity<>(HttpStatus.OK, "校验完成", featureConversionStepOneService.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/FeatureConversionStepOneValidationRequest.java b/src/main/java/com/sztzjy/linkCommerce/entity/dto/FeatureConversionStepOneValidationRequest.java new file mode 100644 index 0000000..fe1e597 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/entity/dto/FeatureConversionStepOneValidationRequest.java @@ -0,0 +1,34 @@ +package com.sztzjy.linkCommerce.entity.dto; + +import java.util.List; + +public class FeatureConversionStepOneValidationRequest { + private List rows; + + public List getRows() { + return rows; + } + + public void setRows(List rows) { + this.rows = rows; + } + + public static class ConversionRow { + private String voice; + private String insight; + private String definition; + private String description; + private String acceptance; + + public String getVoice() { return voice; } + public void setVoice(String voice) { this.voice = voice; } + public String getInsight() { return insight; } + public void setInsight(String insight) { this.insight = insight; } + public String getDefinition() { return definition; } + public void setDefinition(String definition) { this.definition = definition; } + public String getDescription() { return description; } + public void setDescription(String description) { this.description = description; } + public String getAcceptance() { return acceptance; } + public void setAcceptance(String acceptance) { this.acceptance = acceptance; } + } +} diff --git a/src/main/java/com/sztzjy/linkCommerce/entity/dto/FeatureConversionStepOneValidationResult.java b/src/main/java/com/sztzjy/linkCommerce/entity/dto/FeatureConversionStepOneValidationResult.java new file mode 100644 index 0000000..fec6c5f --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/entity/dto/FeatureConversionStepOneValidationResult.java @@ -0,0 +1,11 @@ +package com.sztzjy.linkCommerce.entity.dto; + +public class FeatureConversionStepOneValidationResult { + 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/FeatureConversionStepOneService.java b/src/main/java/com/sztzjy/linkCommerce/service/FeatureConversionStepOneService.java new file mode 100644 index 0000000..0c1d9a9 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/service/FeatureConversionStepOneService.java @@ -0,0 +1,9 @@ +package com.sztzjy.linkCommerce.service; + +import com.sztzjy.linkCommerce.config.security.JwtUser; +import com.sztzjy.linkCommerce.entity.dto.FeatureConversionStepOneValidationRequest; +import com.sztzjy.linkCommerce.entity.dto.FeatureConversionStepOneValidationResult; + +public interface FeatureConversionStepOneService { + FeatureConversionStepOneValidationResult validate(FeatureConversionStepOneValidationRequest request, JwtUser user); +} diff --git a/src/main/java/com/sztzjy/linkCommerce/service/impl/FeatureConversionStepOneServiceImpl.java b/src/main/java/com/sztzjy/linkCommerce/service/impl/FeatureConversionStepOneServiceImpl.java new file mode 100644 index 0000000..9087b2f --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/service/impl/FeatureConversionStepOneServiceImpl.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.FeatureConversionStepOneValidationRequest; +import com.sztzjy.linkCommerce.entity.dto.FeatureConversionStepOneValidationResult; +import com.sztzjy.linkCommerce.service.FeatureConversionStepOneService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class FeatureConversionStepOneServiceImpl implements FeatureConversionStepOneService { + private static final int MIN_ROWS = 8; + private static final int MAX_ROWS = 12; + + @Override + public FeatureConversionStepOneValidationResult validate(FeatureConversionStepOneValidationRequest request, JwtUser user) { + requireStudent(user); + List rows = request == null ? null : request.getRows(); + if (rows == null || rows.size() < MIN_ROWS || rows.size() > MAX_ROWS || rows.stream().anyMatch(row -> row == null)) { + throw badRequest("请填写 8 至 12 行需求转化信息"); + } + if (rows.stream().anyMatch(row -> StringUtils.isBlank(row.getVoice()) + || StringUtils.isBlank(row.getInsight()) + || StringUtils.isBlank(row.getDefinition()) + || StringUtils.isBlank(row.getDescription()) + || StringUtils.isBlank(row.getAcceptance()))) { + throw badRequest("请完整填写每行的用户原声、场景洞察、功能定义、功能描述和验收标准"); + } + FeatureConversionStepOneValidationResult result = new FeatureConversionStepOneValidationResult(); + 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/FeatureConversionStepOneServiceImplTest.java b/src/test/java/com/sztzjy/linkCommerce/service/impl/FeatureConversionStepOneServiceImplTest.java new file mode 100644 index 0000000..f9da981 --- /dev/null +++ b/src/test/java/com/sztzjy/linkCommerce/service/impl/FeatureConversionStepOneServiceImplTest.java @@ -0,0 +1,60 @@ +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.FeatureConversionStepOneValidationRequest; +import com.sztzjy.linkCommerce.entity.dto.FeatureConversionStepOneValidationResult; +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 FeatureConversionStepOneServiceImplTest { + private final FeatureConversionStepOneServiceImpl service = new FeatureConversionStepOneServiceImpl(); + + @Test + void acceptsEightCompleteRows() { + FeatureConversionStepOneValidationResult result = service.validate(request(8), student()); + + assertTrue(result.isValid()); + } + + @Test + void rejectsTooFewRows() { + assertThrows(ServiceException.class, () -> service.validate(request(7), student())); + } + + @Test + void rejectsIncompleteRow() { + FeatureConversionStepOneValidationRequest request = request(8); + request.getRows().get(0).setAcceptance(" "); + + assertThrows(ServiceException.class, () -> service.validate(request, student())); + } + + private FeatureConversionStepOneValidationRequest request(int count) { + FeatureConversionStepOneValidationRequest request = new FeatureConversionStepOneValidationRequest(); + List rows = new ArrayList<>(); + for (int index = 0; index < count; index++) { + FeatureConversionStepOneValidationRequest.ConversionRow row = new FeatureConversionStepOneValidationRequest.ConversionRow(); + row.setVoice("用户原声"); + row.setInsight("场景洞察"); + row.setDefinition("功能定义"); + row.setDescription("功能描述"); + row.setAcceptance("验收标准"); + rows.add(row); + } + request.setRows(rows); + return request; + } + + private JwtUser student() { + JwtUser user = new JwtUser(); + user.setUserId("stu-1"); + user.setRoleId(4); + return user; + } +}