Commit 7f382e6e by zhangxingmin

push

parent 29d6a4cd
...@@ -8,6 +8,8 @@ import com.yd.common.result.Result; ...@@ -8,6 +8,8 @@ import com.yd.common.result.Result;
import com.yd.common.utils.RandomStringGenerator; import com.yd.common.utils.RandomStringGenerator;
import com.yd.product.api.service.ApiAnnouncementCommissionRatioService; import com.yd.product.api.service.ApiAnnouncementCommissionRatioService;
import com.yd.product.api.service.ApiAnnouncementSpeciesService; import com.yd.product.api.service.ApiAnnouncementSpeciesService;
import com.yd.product.api.utils.ProductCommonUtils;
import com.yd.product.feign.dto.ApiAnnouncementCommissionRatioBatchSaveDto;
import com.yd.product.feign.request.announcementcommissionratio.ApiAnnouncementCommissionRatioBatchSaveRequest; import com.yd.product.feign.request.announcementcommissionratio.ApiAnnouncementCommissionRatioBatchSaveRequest;
import com.yd.product.feign.request.announcementcommissionratio.ApiAnnouncementCommissionRatioEditRequest; import com.yd.product.feign.request.announcementcommissionratio.ApiAnnouncementCommissionRatioEditRequest;
import com.yd.product.feign.request.announcementcommissionratio.ApiAnnouncementCommissionRatioPageRequest; import com.yd.product.feign.request.announcementcommissionratio.ApiAnnouncementCommissionRatioPageRequest;
...@@ -22,9 +24,7 @@ import org.springframework.beans.factory.annotation.Autowired; ...@@ -22,9 +24,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import java.util.ArrayList; import java.util.*;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Slf4j @Slf4j
...@@ -173,4 +173,255 @@ public class ApiAnnouncementCommissionRatioServiceImpl implements ApiAnnouncemen ...@@ -173,4 +173,255 @@ public class ApiAnnouncementCommissionRatioServiceImpl implements ApiAnnouncemen
return Result.success(announcementCommissionRatio); return Result.success(announcementCommissionRatio);
} }
// /**
// * 校验入参-公告佣比率规格明细列表
// * @param ratioBatchSaveDtoList
// * @return
// */
// public Result checkBatchSaveRequestPram(List<ApiAnnouncementCommissionRatioBatchSaveDto> ratioBatchSaveDtoList) {
// //校验ratioBatchSaveDtoList入参对象里面费用名称、有效开始时间、有效结束时间、适用范围、是否受汇率影响、结算币种都一样的数据情况下,判断佣金年限(起)- 佣金年限(止)区间是否有重叠情况,然后抛出异常提示哪几条数据有重叠情况。比如都一样的数据情况下,两条数据的判断佣金年限(起)- 佣金年限(止)区间一个是1-3、3-5年也算重叠,只有1-3、4-7不算重叠)
// return null;
// }
/**
* 校验入参-公告佣比率规格明细列表
* @param ratioBatchSaveDtoList
* @return
*/
public Result checkBatchSaveRequestPram(List<ApiAnnouncementCommissionRatioBatchSaveDto> ratioBatchSaveDtoList) {
if (ratioBatchSaveDtoList == null || ratioBatchSaveDtoList.isEmpty()) {
return Result.success("数据为空,无需校验");
}
try {
// 1. 验证并准备数据
List<DtoWithParsedData> preparedDataList = prepareData(ratioBatchSaveDtoList);
if (preparedDataList == null) {
return Result.fail("数据准备失败,请检查输入数据");
}
// 2. 检查所有数据对的年限重叠情况(考虑scope交集)
List<OverlapError> errors = checkAllDataPairs(preparedDataList);
// 3. 如果有错误,返回错误信息
if (!errors.isEmpty()) {
String errorMsg = buildErrorMessage(errors, ratioBatchSaveDtoList);
return Result.fail(errorMsg);
}
return Result.success("校验通过,无重叠数据");
} catch (Exception e) {
return Result.fail("数据校验异常:" + e.getMessage());
}
}
/**
* 准备数据:解析年限并将scope转换为集合
* @param dtoList
* @return
*/
private List<DtoWithParsedData> prepareData(List<ApiAnnouncementCommissionRatioBatchSaveDto> dtoList) {
List<DtoWithParsedData> preparedList = new ArrayList<>();
for (int i = 0; i < dtoList.size(); i++) {
ApiAnnouncementCommissionRatioBatchSaveDto dto = dtoList.get(i);
// 解析年限
Integer startYear = ProductCommonUtils.parseYearToInt(dto.getStartPeriod());
Integer endYear = ProductCommonUtils.parseYearToInt(dto.getEndPeriod());
if (startYear > endYear) {
throw new BusinessException("第" + (i+1) + "条数据的起始年限[" + startYear + "]不能大于结束年限[" + endYear + "]");
}
// 解析scope为集合,去重
Set<String> scopeSet = ProductCommonUtils.parseScopeToSet(dto.getScope());
preparedList.add(new DtoWithParsedData(i, dto, startYear, endYear, scopeSet));
}
return preparedList;
}
/**
* 检查所有数据对的年限重叠情况
*/
private List<OverlapError> checkAllDataPairs(List<DtoWithParsedData> preparedDataList) {
List<OverlapError> errors = new ArrayList<>();
int n = preparedDataList.size();
// 使用双重循环检查所有数据对
for (int i = 0; i < n - 1; i++) {
DtoWithParsedData data1 = preparedDataList.get(i);
for (int j = i + 1; j < n; j++) {
DtoWithParsedData data2 = preparedDataList.get(j);
// 检查是否属于同一分组条件(考虑scope交集)
if (isSameGroup(data1, data2)) {
// 检查年限是否重叠
if (ProductCommonUtils.isYearRangeOverlap(data1.startYear, data1.endYear, data2.startYear, data2.endYear)) {
// 避免重复添加相同的错误
if (!isErrorAlreadyExists(errors, data1.originalIndex, data2.originalIndex)) {
errors.add(new OverlapError(data1.originalIndex, data2.originalIndex));
}
}
}
}
}
return errors;
}
/**
* 判断两个数据是否属于同一分组条件
*/
private boolean isSameGroup(DtoWithParsedData data1, DtoWithParsedData data2) {
// 1. 检查除scope外的其他条件是否相同
if (!isSameGroupExceptScope(data1.dto, data2.dto)) {
return false;
}
// 2. 检查scope是否有交集
return ProductCommonUtils.hasScopeIntersection(data1.scopeSet, data2.scopeSet);
}
/**
* 检查除scope外的其他条件是否相同
*/
private boolean isSameGroupExceptScope(ApiAnnouncementCommissionRatioBatchSaveDto dto1,
ApiAnnouncementCommissionRatioBatchSaveDto dto2) {
return Objects.equals(dto1.getExpenseName(), dto2.getExpenseName())
&& Objects.equals(dto1.getEffectiveStart(), dto2.getEffectiveStart())
&& Objects.equals(dto1.getEffectiveEnd(), dto2.getEffectiveEnd())
&& Objects.equals(dto1.getIsExchangeRate(), dto2.getIsExchangeRate())
&& Objects.equals(dto1.getCurrency(), dto2.getCurrency());
}
/**
* 校验基础字段
*/
private boolean validateBasicFields(ApiAnnouncementCommissionRatioBatchSaveDto dto) {
return dto.getExpenseName() != null && !dto.getExpenseName().trim().isEmpty()
&& dto.getEffectiveStart() != null
&& dto.getEffectiveEnd() != null
&& dto.getEffectiveStart().isBefore(dto.getEffectiveEnd())
&& dto.getScope() != null && !dto.getScope().trim().isEmpty()
&& dto.getIsExchangeRate() != null && !dto.getIsExchangeRate().trim().isEmpty()
&& dto.getCurrency() != null && !dto.getCurrency().trim().isEmpty();
}
/**
* 检查错误是否已存在
*/
private boolean isErrorAlreadyExists(List<OverlapError> errors, int index1, int index2) {
for (OverlapError error : errors) {
if ((error.index1 == index1 && error.index2 == index2) ||
(error.index1 == index2 && error.index2 == index1)) {
return true;
}
}
return false;
}
/**
* 构建错误消息
*/
private String buildErrorMessage(List<OverlapError> errors,
List<ApiAnnouncementCommissionRatioBatchSaveDto> originalList) {
if (errors.isEmpty()) {
return "未发现重叠数据";
}
StringBuilder sb = new StringBuilder();
sb.append("发现佣金年限区间重叠的数据,请检查以下数据(只要适用范围有交集就视为相同分组条件):\n\n");
// 按数据索引分组,方便查看每条数据的所有重叠关系
Map<Integer, List<Integer>> overlapMap = new TreeMap<>();
for (OverlapError error : errors) {
overlapMap.computeIfAbsent(error.index1, k -> new ArrayList<>()).add(error.index2);
overlapMap.computeIfAbsent(error.index2, k -> new ArrayList<>()).add(error.index1);
}
// 构建错误信息
for (Map.Entry<Integer, List<Integer>> entry : overlapMap.entrySet()) {
int dataIndex = entry.getKey();
List<Integer> overlapIndices = entry.getValue();
ApiAnnouncementCommissionRatioBatchSaveDto dto = originalList.get(dataIndex);
sb.append("第").append(dataIndex + 1).append("条数据:\n")
.append(" - 费用名称: ").append(dto.getExpenseName()).append("\n")
.append(" - 年限区间: ").append(dto.getStartPeriod()).append(" 至 ").append(dto.getEndPeriod()).append("\n")
.append(" - 适用范围: ").append(dto.getScope()).append("\n")
.append(" - 有效时间: ").append(dto.getEffectiveStart()).append(" 至 ").append(dto.getEffectiveEnd()).append("\n")
.append(" - 汇率影响: ").append(dto.getIsExchangeRate()).append("\n")
.append(" - 结算币种: ").append(dto.getCurrency()).append("\n")
.append(" 与以下数据重叠:\n");
for (int overlapIndex : overlapIndices) {
ApiAnnouncementCommissionRatioBatchSaveDto overlapDto = originalList.get(overlapIndex);
sb.append(" - 第").append(overlapIndex + 1).append("条: ")
.append(overlapDto.getExpenseName())
.append(" [").append(overlapDto.getStartPeriod())
.append("-").append(overlapDto.getEndPeriod()).append("]")
.append(" 适用范围:").append(overlapDto.getScope()).append("\n");
}
sb.append("\n");
}
// 添加通用分组条件说明
sb.append("重叠判断规则说明:\n");
sb.append("1. 以下条件完全相同的数据才会被比较:\n");
sb.append(" - 费用名称\n");
sb.append(" - 有效开始时间\n");
sb.append(" - 有效结束时间\n");
sb.append(" - 是否受汇率影响\n");
sb.append(" - 结算币种\n");
sb.append("2. 适用范围只要有一个相同的值(交集)就视为相同分组条件\n");
sb.append("3. 年限区间重叠判断规则:\n");
sb.append(" - 1-3 和 3-5: 算重叠(因为有共同的3)\n");
sb.append(" - 1-3 和 4-7: 不算重叠\n");
sb.append(" - 1-5 和 2-4: 算重叠\n");
return sb.toString();
}
/**
* 内部类:包含解析后数据的DTO
*/
private static class DtoWithParsedData {
final int originalIndex; // 在原始列表中的索引
final ApiAnnouncementCommissionRatioBatchSaveDto dto; // 原始DTO
final int startYear; // 解析后的起始年份
final int endYear; // 解析后的结束年份
final Set<String> scopeSet; // 解析后的scope集合
public DtoWithParsedData(int originalIndex, ApiAnnouncementCommissionRatioBatchSaveDto dto,
int startYear, int endYear, Set<String> scopeSet) {
this.originalIndex = originalIndex;
this.dto = dto;
this.startYear = startYear;
this.endYear = endYear;
this.scopeSet = scopeSet;
}
}
/**
* 内部类:重叠错误
*/
private static class OverlapError {
private final int index1;
private final int index2;
public OverlapError(int index1, int index2) {
this.index1 = index1;
this.index2 = index2;
}
public int getIndex1() { return index1; }
public int getIndex2() { return index2; }
}
} }
package com.yd.product.api.utils;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
public class ProductCommonUtils {
/**
* 解析年限字符串为整数
* @param yearStr
* @return
*/
public static Integer parseYearToInt(String yearStr) {
if (yearStr == null || yearStr.trim().isEmpty()) {
return null;
}
try {
// 提取数字部分,支持"1"、"1年"、"第1年"等格式
String numericPart = yearStr.replaceAll("[^0-9]", "").trim();
if (numericPart.isEmpty()) {
return null;
}
return Integer.parseInt(numericPart);
} catch (NumberFormatException e) {
return null;
}
}
/**
* 检查年限区间是否重叠
* 根据需求:1-3和3-5算重叠,1-3和4-7不算重叠
* @param start1
* @param end1
* @param start2
* @param end2
* @return
*/
public static boolean isYearRangeOverlap(int start1, int end1, int start2, int end2) {
// 两个区间重叠的条件:区间1的结束年份 >= 区间2的开始年份
// 例如:1-3和3-5重叠(3>=3),1-3和4-7不重叠(3<4)
return Math.max(start1, start2) <= Math.min(end1, end2);
}
/**
* 将scope字符串解析为集合
* @param scope
* @return
*/
public static Set<String> parseScopeToSet(String scope) {
if (scope == null || scope.trim().isEmpty()) {
return new HashSet<>();
}
return Arrays.stream(scope.split(";"))
.map(String::trim)
.filter(s -> !s.isEmpty())
.collect(Collectors.toSet());
}
/**
* 检查两个scope集合是否有交集
*/
public static boolean hasScopeIntersection(Set<String> scopeSet1, Set<String> scopeSet2) {
// 特殊情况处理
if (scopeSet1.isEmpty() || scopeSet2.isEmpty()) {
return false;
}
// 如果任一集合包含"全部",则视为有交集
if (scopeSet1.contains("全部") || scopeSet2.contains("全部")) {
return true;
}
// 检查是否有共同元素
for (String item : scopeSet1) {
if (scopeSet2.contains(item)) {
return true;
}
}
return false;
}
}
package com.yd.product.feign.dto; package com.yd.product.feign.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data; import lombok.Data;
import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotBlank;
...@@ -43,17 +44,19 @@ public class ApiAnnouncementCommissionRatioBatchSaveDto { ...@@ -43,17 +44,19 @@ public class ApiAnnouncementCommissionRatioBatchSaveDto {
/** /**
* 有效开始时间 * 有效开始时间
*/ */
@JsonFormat(pattern = "yyyy-MM-dd")
@NotNull(message = "有效开始时间不能为空") @NotNull(message = "有效开始时间不能为空")
private LocalDateTime effectiveStart; private LocalDateTime effectiveStart;
/** /**
* 有效结束时间 * 有效结束时间
*/ */
@JsonFormat(pattern = "yyyy-MM-dd")
@NotNull(message = "有效结束时间不能为空") @NotNull(message = "有效结束时间不能为空")
private LocalDateTime effectiveEnd; private LocalDateTime effectiveEnd;
/** /**
* 适用范围(经纪人/分销员/加盟商/签单员/转介人/全部,字典) * 适用范围(经纪人/分销员/加盟商/签单员/转介人/全部,字典,多选多个用分号分隔
*/ */
@NotBlank(message = "适用范围不能为空") @NotBlank(message = "适用范围不能为空")
private String scope; private String scope;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment