You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

83 lines
4.2 KiB
Java

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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