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); } }