|
|
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<String> KANO_TYPES = Arrays.asList("基本型", "期望型", "兴奋型", "无差异型", "反向型");
|
|
|
|
|
|
@Override
|
|
|
public FeatureConversionStepTwoValidationResult validate(FeatureConversionStepTwoValidationRequest request, JwtUser user) {
|
|
|
requireStudent(user);
|
|
|
List<String> sourceDefinitions = request == null ? null : request.getSourceDefinitions();
|
|
|
List<FeatureConversionStepTwoValidationRequest.RiceScoreRow> 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);
|
|
|
}
|
|
|
}
|