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 b829ba5..62eb9fd 100644 --- a/src/main/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerController.java +++ b/src/main/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerController.java @@ -398,6 +398,12 @@ public class StudentTrainingAnswerController { private com.sztzjy.linkCommerce.service.ConsumerScenarioStepThreeService consumerScenarioStepThreeService; @Autowired private com.sztzjy.linkCommerce.service.ConsumerScenarioStepFourService consumerScenarioStepFourService; + @Autowired + private com.sztzjy.linkCommerce.service.FeatureConversionStepOneService featureConversionStepOneService; + @Autowired + private com.sztzjy.linkCommerce.service.FeatureConversionStepTwoService featureConversionStepTwoService; + @Autowired + private com.sztzjy.linkCommerce.service.FeatureConversionStepThreeService featureConversionStepThreeService; @Autowired private SupplierEvaluationStepTwoService supplierEvaluationStepTwoService; @@ -1188,6 +1194,15 @@ public class StudentTrainingAnswerController { @ApiOperation("消费场景解构第四步校验") public ResultEntity validateConsumerScenarioStepFour(@RequestBody com.sztzjy.linkCommerce.entity.dto.ConsumerScenarioStepFourValidationRequest answer, HttpServletRequest request) { try { return new ResultEntity<>(HttpStatus.OK, "校验完成", consumerScenarioStepFourService.validate(answer, TokenProvider.getJWTUser(request))); } catch (ServiceException e) { return new ResultEntity<>(e.getCode(), e.getMessage()); } } + @PostMapping("/feature-conversion/step-1/validate") + @ApiOperation("需求转化为功能第一步校验") + public ResultEntity validateFeatureConversionStepOne(@RequestBody com.sztzjy.linkCommerce.entity.dto.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("/feature-conversion/step-2/validate") + @ApiOperation("需求转化为功能第二步校验") + public ResultEntity validateFeatureConversionStepTwo(@RequestBody com.sztzjy.linkCommerce.entity.dto.FeatureConversionStepTwoValidationRequest answer, HttpServletRequest request) { try { return new ResultEntity<>(HttpStatus.OK, "校验完成", featureConversionStepTwoService.validate(answer, TokenProvider.getJWTUser(request))); } catch (ServiceException e) { return new ResultEntity<>(e.getCode(), e.getMessage()); } } + @PostMapping("/feature-conversion/step-3/validate") + @ApiOperation("需求转化为功能第三步校验") + public ResultEntity validateFeatureConversionStepThree(@RequestBody com.sztzjy.linkCommerce.entity.dto.FeatureConversionStepThreeValidationRequest answer, HttpServletRequest request) { try { return new ResultEntity<>(HttpStatus.OK, "校验完成", featureConversionStepThreeService.validate(answer, TokenProvider.getJWTUser(request))); } catch (ServiceException e) { return new ResultEntity<>(e.getCode(), e.getMessage()); } } @DeleteMapping("/{taskKey}") @ApiOperation("删除当前学生指定实训任务答案") public ResultEntity delete(@PathVariable String taskKey, diff --git a/src/main/java/com/sztzjy/linkCommerce/entity/dto/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/entity/dto/FeatureConversionStepThreeValidationRequest.java b/src/main/java/com/sztzjy/linkCommerce/entity/dto/FeatureConversionStepThreeValidationRequest.java new file mode 100644 index 0000000..e8380f5 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/entity/dto/FeatureConversionStepThreeValidationRequest.java @@ -0,0 +1,37 @@ +package com.sztzjy.linkCommerce.entity.dto; + +import java.util.List; + +public class FeatureConversionStepThreeValidationRequest { + private List scoreRows; + private List rows; + + public List getScoreRows() { + return scoreRows; + } + + public void setScoreRows(List scoreRows) { + this.scoreRows = scoreRows; + } + + public List getRows() { + return rows; + } + + public void setRows(List rows) { + this.rows = rows; + } + + public static class PriorityRow { + private String name; + private String priority; + private String reason; + + public String getName() { return name; } + public void setName(String name) { this.name = name; } + public String getPriority() { return priority; } + public void setPriority(String priority) { this.priority = priority; } + public String getReason() { return reason; } + public void setReason(String reason) { this.reason = reason; } + } +} diff --git a/src/main/java/com/sztzjy/linkCommerce/entity/dto/FeatureConversionStepThreeValidationResult.java b/src/main/java/com/sztzjy/linkCommerce/entity/dto/FeatureConversionStepThreeValidationResult.java new file mode 100644 index 0000000..13f5208 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/entity/dto/FeatureConversionStepThreeValidationResult.java @@ -0,0 +1,11 @@ +package com.sztzjy.linkCommerce.entity.dto; + +public class FeatureConversionStepThreeValidationResult { + 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/entity/dto/FeatureConversionStepTwoValidationRequest.java b/src/main/java/com/sztzjy/linkCommerce/entity/dto/FeatureConversionStepTwoValidationRequest.java new file mode 100644 index 0000000..726e7dd --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/entity/dto/FeatureConversionStepTwoValidationRequest.java @@ -0,0 +1,52 @@ +package com.sztzjy.linkCommerce.entity.dto; + +import java.util.List; + +public class FeatureConversionStepTwoValidationRequest { + private List sourceDefinitions; + private List rows; + + public List getSourceDefinitions() { + return sourceDefinitions; + } + + public void setSourceDefinitions(List sourceDefinitions) { + this.sourceDefinitions = sourceDefinitions; + } + + public List getRows() { + return rows; + } + + public void setRows(List rows) { + this.rows = rows; + } + + public static class RiceScoreRow { + private String requirementId; + private String name; + private String kano; + private Double reach; + private Double impact; + private Double confidence; + private Double effort; + private String riceScore; + + public String getRequirementId() { return requirementId; } + public void setRequirementId(String requirementId) { this.requirementId = requirementId; } + public String getName() { return name; } + public void setName(String name) { this.name = name; } + public String getKano() { return kano; } + public void setKano(String kano) { this.kano = kano; } + public Double getReach() { return reach; } + public void setReach(Double reach) { this.reach = reach; } + public Double getImpact() { return impact; } + public void setImpact(Double impact) { this.impact = impact; } + public Double getConfidence() { return confidence; } + public void setConfidence(Double confidence) { this.confidence = confidence; } + public Double getEffort() { return effort; } + public void setEffort(Double effort) { this.effort = effort; } + public String getRiceScore() { return riceScore; } + public void setRiceScore(String riceScore) { this.riceScore = riceScore; } + } +} diff --git a/src/main/java/com/sztzjy/linkCommerce/entity/dto/FeatureConversionStepTwoValidationResult.java b/src/main/java/com/sztzjy/linkCommerce/entity/dto/FeatureConversionStepTwoValidationResult.java new file mode 100644 index 0000000..25acca7 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/entity/dto/FeatureConversionStepTwoValidationResult.java @@ -0,0 +1,11 @@ +package com.sztzjy.linkCommerce.entity.dto; + +public class FeatureConversionStepTwoValidationResult { + 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/FeatureConversionStepThreeService.java b/src/main/java/com/sztzjy/linkCommerce/service/FeatureConversionStepThreeService.java new file mode 100644 index 0000000..b6817ae --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/service/FeatureConversionStepThreeService.java @@ -0,0 +1,9 @@ +package com.sztzjy.linkCommerce.service; + +import com.sztzjy.linkCommerce.config.security.JwtUser; +import com.sztzjy.linkCommerce.entity.dto.FeatureConversionStepThreeValidationRequest; +import com.sztzjy.linkCommerce.entity.dto.FeatureConversionStepThreeValidationResult; + +public interface FeatureConversionStepThreeService { + FeatureConversionStepThreeValidationResult validate(FeatureConversionStepThreeValidationRequest request, JwtUser user); +} diff --git a/src/main/java/com/sztzjy/linkCommerce/service/FeatureConversionStepTwoService.java b/src/main/java/com/sztzjy/linkCommerce/service/FeatureConversionStepTwoService.java new file mode 100644 index 0000000..422ccfb --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/service/FeatureConversionStepTwoService.java @@ -0,0 +1,9 @@ +package com.sztzjy.linkCommerce.service; + +import com.sztzjy.linkCommerce.config.security.JwtUser; +import com.sztzjy.linkCommerce.entity.dto.FeatureConversionStepTwoValidationRequest; +import com.sztzjy.linkCommerce.entity.dto.FeatureConversionStepTwoValidationResult; + +public interface FeatureConversionStepTwoService { + FeatureConversionStepTwoValidationResult validate(FeatureConversionStepTwoValidationRequest 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/main/java/com/sztzjy/linkCommerce/service/impl/FeatureConversionStepThreeServiceImpl.java b/src/main/java/com/sztzjy/linkCommerce/service/impl/FeatureConversionStepThreeServiceImpl.java new file mode 100644 index 0000000..dc1d054 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/service/impl/FeatureConversionStepThreeServiceImpl.java @@ -0,0 +1,58 @@ +package com.sztzjy.linkCommerce.service.impl; + +import com.sztzjy.linkCommerce.config.exception.handler.ServiceException; +import com.sztzjy.linkCommerce.config.security.JwtUser; +import com.sztzjy.linkCommerce.entity.dto.FeatureConversionStepThreeValidationRequest; +import com.sztzjy.linkCommerce.entity.dto.FeatureConversionStepThreeValidationResult; +import com.sztzjy.linkCommerce.entity.dto.FeatureConversionStepTwoValidationRequest; +import com.sztzjy.linkCommerce.service.FeatureConversionStepThreeService; +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 FeatureConversionStepThreeServiceImpl implements FeatureConversionStepThreeService { + private static final int MIN_ROWS = 8; + private static final int MAX_ROWS = 12; + private static final List PRIORITIES = Arrays.asList("P0", "P1", "P2", "P3"); + + @Override + public FeatureConversionStepThreeValidationResult validate(FeatureConversionStepThreeValidationRequest request, JwtUser user) { + requireStudent(user); + List scoreRows = request == null ? null : request.getScoreRows(); + List rows = request == null ? null : request.getRows(); + if (scoreRows == null || rows == null || scoreRows.size() < MIN_ROWS || scoreRows.size() > MAX_ROWS || scoreRows.size() != rows.size()) { + throw badRequest("功能优先级表必须与第二步 RICE 评分表保持一致,且为 8 至 12 行"); + } + for (int index = 0; index < rows.size(); index++) { + FeatureConversionStepTwoValidationRequest.RiceScoreRow scoreRow = scoreRows.get(index); + FeatureConversionStepThreeValidationRequest.PriorityRow row = rows.get(index); + if (scoreRow == null || row == null || StringUtils.isBlank(scoreRow.getName()) || !scoreRow.getName().equals(row.getName())) { + throw badRequest("功能名称和行数必须与第二步 RICE 评分表保持一致"); + } + if (!PRIORITIES.contains(row.getPriority()) || StringUtils.isBlank(row.getReason())) { + throw badRequest("请为每项功能选择 P0-P3 优先级并填写划分理由"); + } + } + FeatureConversionStepThreeValidationResult result = new FeatureConversionStepThreeValidationResult(); + 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/main/java/com/sztzjy/linkCommerce/service/impl/FeatureConversionStepTwoServiceImpl.java b/src/main/java/com/sztzjy/linkCommerce/service/impl/FeatureConversionStepTwoServiceImpl.java new file mode 100644 index 0000000..eb64922 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/service/impl/FeatureConversionStepTwoServiceImpl.java @@ -0,0 +1,82 @@ +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.FeatureConversionStepTwoValidationRequest; +import com.sztzjy.linkCommerce.entity.dto.FeatureConversionStepTwoValidationResult; +import com.sztzjy.linkCommerce.service.FeatureConversionStepTwoService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Arrays; +import java.util.List; + +@Service +public class FeatureConversionStepTwoServiceImpl implements FeatureConversionStepTwoService { + private static final int MIN_ROWS = 8; + private static final int MAX_ROWS = 12; + private static final List KANO_TYPES = Arrays.asList("基本型", "期望型", "兴奋型", "无差异型", "反向型"); + + @Override + public FeatureConversionStepTwoValidationResult validate(FeatureConversionStepTwoValidationRequest request, JwtUser user) { + requireStudent(user); + List sourceDefinitions = request == null ? null : request.getSourceDefinitions(); + List rows = request == null ? null : request.getRows(); + if (sourceDefinitions == null || rows == null || rows.size() < MIN_ROWS || rows.size() > MAX_ROWS || sourceDefinitions.size() != rows.size()) { + throw badRequest("RICE 评分行数必须与第一步需求转化表一致,且为 8 至 12 行"); + } + for (int index = 0; index < rows.size(); index++) { + String expectedId = String.format("F%02d", index + 1); + String expectedName = sourceDefinitions.get(index); + FeatureConversionStepTwoValidationRequest.RiceScoreRow row = rows.get(index); + if (row == null || StringUtils.isBlank(expectedName) || !expectedId.equals(row.getRequirementId()) || !expectedName.equals(row.getName())) { + throw badRequest("需求 ID 与功能名称必须与第一步的功能定义保持一致"); + } + if (!KANO_TYPES.contains(row.getKano()) || !isScore(row.getReach()) || !isScore(row.getImpact()) + || !isConfidence(row.getConfidence()) || row.getEffort() == null || row.getEffort() <= 0 || !isRiceScoreValid(row)) { + throw badRequest("请完整填写 KANO、Reach、Impact、Confidence、Effort,并确认 RICE 得分正确"); + } + } + FeatureConversionStepTwoValidationResult result = new FeatureConversionStepTwoValidationResult(); + result.setValid(true); + result.setMessage("RICE 评分填写完整。"); + return result; + } + + private boolean isScore(Double value) { + return value != null && value >= 1 && value <= 5 && value == Math.rint(value); + } + + private boolean isConfidence(Double value) { + return value != null && value >= 1 && value <= 100 && value == Math.rint(value); + } + + private boolean isRiceScoreValid(FeatureConversionStepTwoValidationRequest.RiceScoreRow row) { + if (StringUtils.isBlank(row.getRiceScore())) return false; + try { + BigDecimal expected = BigDecimal.valueOf(row.getReach()) + .multiply(BigDecimal.valueOf(row.getImpact())) + .multiply(BigDecimal.valueOf(row.getConfidence()).movePointLeft(2)) + .divide(BigDecimal.valueOf(row.getEffort()), 2, RoundingMode.HALF_UP); + return expected.compareTo(new BigDecimal(row.getRiceScore())) == 0; + } catch (NumberFormatException error) { + return false; + } + } + + 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; + } +} diff --git a/src/test/java/com/sztzjy/linkCommerce/service/impl/FeatureConversionStepThreeServiceImplTest.java b/src/test/java/com/sztzjy/linkCommerce/service/impl/FeatureConversionStepThreeServiceImplTest.java new file mode 100644 index 0000000..08ad2fb --- /dev/null +++ b/src/test/java/com/sztzjy/linkCommerce/service/impl/FeatureConversionStepThreeServiceImplTest.java @@ -0,0 +1,69 @@ +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.FeatureConversionStepThreeValidationRequest; +import com.sztzjy.linkCommerce.entity.dto.FeatureConversionStepThreeValidationResult; +import com.sztzjy.linkCommerce.entity.dto.FeatureConversionStepTwoValidationRequest; +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 FeatureConversionStepThreeServiceImplTest { + private final FeatureConversionStepThreeServiceImpl service = new FeatureConversionStepThreeServiceImpl(); + + @Test + void acceptsPriorityRowsSynchronizedWithRiceRows() { + FeatureConversionStepThreeValidationResult result = service.validate(request(), student()); + + assertTrue(result.isValid()); + } + + @Test + void rejectsPriorityNameNotSynchronizedWithRiceRows() { + FeatureConversionStepThreeValidationRequest request = request(); + request.getRows().get(0).setName("被篡改的功能名称"); + + assertThrows(ServiceException.class, () -> service.validate(request, student())); + } + + @Test + void rejectsBlankPriorityReason() { + FeatureConversionStepThreeValidationRequest request = request(); + request.getRows().get(0).setReason(" "); + + assertThrows(ServiceException.class, () -> service.validate(request, student())); + } + + private FeatureConversionStepThreeValidationRequest request() { + FeatureConversionStepThreeValidationRequest request = new FeatureConversionStepThreeValidationRequest(); + List scoreRows = new ArrayList<>(); + List rows = new ArrayList<>(); + for (int index = 0; index < 8; index++) { + String name = "功能定义" + (index + 1); + FeatureConversionStepTwoValidationRequest.RiceScoreRow scoreRow = new FeatureConversionStepTwoValidationRequest.RiceScoreRow(); + scoreRow.setName(name); + scoreRows.add(scoreRow); + + FeatureConversionStepThreeValidationRequest.PriorityRow row = new FeatureConversionStepThreeValidationRequest.PriorityRow(); + row.setName(name); + row.setPriority(index == 0 ? "P0" : "P1"); + row.setReason("结合 RICE 评分确定开发优先级"); + rows.add(row); + } + request.setScoreRows(scoreRows); + request.setRows(rows); + return request; + } + + private JwtUser student() { + JwtUser user = new JwtUser(); + user.setUserId("student-id"); + user.setRoleId(4); + return user; + } +} diff --git a/src/test/java/com/sztzjy/linkCommerce/service/impl/FeatureConversionStepTwoServiceImplTest.java b/src/test/java/com/sztzjy/linkCommerce/service/impl/FeatureConversionStepTwoServiceImplTest.java new file mode 100644 index 0000000..93b219c --- /dev/null +++ b/src/test/java/com/sztzjy/linkCommerce/service/impl/FeatureConversionStepTwoServiceImplTest.java @@ -0,0 +1,70 @@ +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.FeatureConversionStepTwoValidationRequest; +import com.sztzjy.linkCommerce.entity.dto.FeatureConversionStepTwoValidationResult; +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 FeatureConversionStepTwoServiceImplTest { + private final FeatureConversionStepTwoServiceImpl service = new FeatureConversionStepTwoServiceImpl(); + + @Test + void acceptsSynchronizedRiceRows() { + FeatureConversionStepTwoValidationResult result = service.validate(request(), student()); + + assertTrue(result.isValid()); + } + + @Test + void rejectsNameNotSynchronizedWithStepOne() { + FeatureConversionStepTwoValidationRequest request = request(); + request.getRows().get(0).setName("手动修改的功能"); + + assertThrows(ServiceException.class, () -> service.validate(request, student())); + } + + @Test + void rejectsInvalidRiceScore() { + FeatureConversionStepTwoValidationRequest request = request(); + request.getRows().get(0).setRiceScore("15.99"); + + assertThrows(ServiceException.class, () -> service.validate(request, student())); + } + + private FeatureConversionStepTwoValidationRequest request() { + FeatureConversionStepTwoValidationRequest request = new FeatureConversionStepTwoValidationRequest(); + List definitions = new ArrayList<>(); + List rows = new ArrayList<>(); + for (int index = 0; index < 8; index++) { + String definition = "功能定义" + (index + 1); + definitions.add(definition); + FeatureConversionStepTwoValidationRequest.RiceScoreRow row = new FeatureConversionStepTwoValidationRequest.RiceScoreRow(); + row.setRequirementId(String.format("F%02d", index + 1)); + row.setName(definition); + row.setKano("基本型"); + row.setReach(5D); + row.setImpact(4D); + row.setConfidence(80D); + row.setEffort(1D); + row.setRiceScore("16.00"); + rows.add(row); + } + request.setSourceDefinitions(definitions); + request.setRows(rows); + return request; + } + + private JwtUser student() { + JwtUser user = new JwtUser(); + user.setUserId("stu-1"); + user.setRoleId(4); + return user; + } +}