Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
Y
yd-product
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
xingmin
yd-product
Commits
4ab00f6c
Commit
4ab00f6c
authored
Apr 13, 2026
by
zhangxingmin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
push
parent
48abef2d
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
266 additions
and
201 deletions
+266
-201
yd-product-api/src/main/java/com/yd/product/api/service/impl/ApiAnnouncementCommissionRatioServiceImpl.java
+137
-130
yd-product-api/src/main/java/com/yd/product/api/service/impl/ApiExpectedCommissionRatioServiceImpl.java
+129
-71
No files found.
yd-product-api/src/main/java/com/yd/product/api/service/impl/ApiAnnouncementCommissionRatioServiceImpl.java
View file @
4ab00f6c
...
@@ -25,6 +25,7 @@ import org.springframework.beans.factory.annotation.Autowired;
...
@@ -25,6 +25,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.time.LocalDateTime
;
import
java.util.*
;
import
java.util.*
;
import
java.util.stream.Collectors
;
import
java.util.stream.Collectors
;
...
@@ -88,7 +89,7 @@ public class ApiAnnouncementCommissionRatioServiceImpl implements ApiAnnouncemen
...
@@ -88,7 +89,7 @@ public class ApiAnnouncementCommissionRatioServiceImpl implements ApiAnnouncemen
.
stream
()
.
stream
()
.
map
(
dto
->
{
.
map
(
dto
->
{
AnnouncementCommissionRatio
ratio
=
new
AnnouncementCommissionRatio
();
AnnouncementCommissionRatio
ratio
=
new
AnnouncementCommissionRatio
();
BeanUtils
.
copyProperties
(
dto
,
ratio
);
BeanUtils
.
copyProperties
(
dto
,
ratio
);
ratio
.
setAnnouncementSpeciesBizId
(
request
.
getAnnouncementSpeciesBizId
());
ratio
.
setAnnouncementSpeciesBizId
(
request
.
getAnnouncementSpeciesBizId
());
ratio
.
setAnnouncementCommissionRatioBizId
(
RandomStringGenerator
.
generateBizId16
(
CommonEnum
.
UID_TYPE_ANNOUNCEMENT_COMMISSION_RATIO
.
getCode
()));
ratio
.
setAnnouncementCommissionRatioBizId
(
RandomStringGenerator
.
generateBizId16
(
CommonEnum
.
UID_TYPE_ANNOUNCEMENT_COMMISSION_RATIO
.
getCode
()));
return
ratio
;
return
ratio
;
...
@@ -110,7 +111,7 @@ public class ApiAnnouncementCommissionRatioServiceImpl implements ApiAnnouncemen
...
@@ -110,7 +111,7 @@ public class ApiAnnouncementCommissionRatioServiceImpl implements ApiAnnouncemen
//入参校验,佣金年限校验不同条件下的唯一性 TODO
//入参校验,佣金年限校验不同条件下的唯一性 TODO
//新增
//新增
AnnouncementCommissionRatio
ratio
=
new
AnnouncementCommissionRatio
();
AnnouncementCommissionRatio
ratio
=
new
AnnouncementCommissionRatio
();
BeanUtils
.
copyProperties
(
request
,
ratio
);
BeanUtils
.
copyProperties
(
request
,
ratio
);
ratio
.
setAnnouncementCommissionRatioBizId
(
RandomStringGenerator
.
generateBizId16
(
CommonEnum
.
UID_TYPE_ANNOUNCEMENT_COMMISSION_RATIO
.
getCode
()));
ratio
.
setAnnouncementCommissionRatioBizId
(
RandomStringGenerator
.
generateBizId16
(
CommonEnum
.
UID_TYPE_ANNOUNCEMENT_COMMISSION_RATIO
.
getCode
()));
iAnnouncementCommissionRatioService
.
saveOrUpdate
(
ratio
);
iAnnouncementCommissionRatioService
.
saveOrUpdate
(
ratio
);
return
Result
.
success
();
return
Result
.
success
();
...
@@ -130,7 +131,7 @@ public class ApiAnnouncementCommissionRatioServiceImpl implements ApiAnnouncemen
...
@@ -130,7 +131,7 @@ public class ApiAnnouncementCommissionRatioServiceImpl implements ApiAnnouncemen
apiAnnouncementSpeciesService
.
checkAnnouncementSpeciesIsExist
(
request
.
getAnnouncementSpeciesBizId
());
apiAnnouncementSpeciesService
.
checkAnnouncementSpeciesIsExist
(
request
.
getAnnouncementSpeciesBizId
());
//入参校验,佣金年限校验不同条件下的唯一性 TODO
//入参校验,佣金年限校验不同条件下的唯一性 TODO
//新增
//新增
BeanUtils
.
copyProperties
(
request
,
ratio
);
BeanUtils
.
copyProperties
(
request
,
ratio
);
iAnnouncementCommissionRatioService
.
saveOrUpdate
(
ratio
);
iAnnouncementCommissionRatioService
.
saveOrUpdate
(
ratio
);
return
Result
.
success
();
return
Result
.
success
();
}
}
...
@@ -162,7 +163,7 @@ public class ApiAnnouncementCommissionRatioServiceImpl implements ApiAnnouncemen
...
@@ -162,7 +163,7 @@ public class ApiAnnouncementCommissionRatioServiceImpl implements ApiAnnouncemen
Result
<
AnnouncementCommissionRatio
>
result
=
checkAnnouncementCommissionRatioIsExist
(
announcementCommissionRatioBizId
);
Result
<
AnnouncementCommissionRatio
>
result
=
checkAnnouncementCommissionRatioIsExist
(
announcementCommissionRatioBizId
);
AnnouncementCommissionRatio
announcementCommissionRatio
=
result
.
getData
();
AnnouncementCommissionRatio
announcementCommissionRatio
=
result
.
getData
();
ApiAnnouncementCommissionRatioDetailResponse
response
=
new
ApiAnnouncementCommissionRatioDetailResponse
();
ApiAnnouncementCommissionRatioDetailResponse
response
=
new
ApiAnnouncementCommissionRatioDetailResponse
();
BeanUtils
.
copyProperties
(
announcementCommissionRatio
,
response
);
BeanUtils
.
copyProperties
(
announcementCommissionRatio
,
response
);
return
Result
.
success
(
response
);
return
Result
.
success
(
response
);
}
}
...
@@ -204,7 +205,7 @@ public class ApiAnnouncementCommissionRatioServiceImpl implements ApiAnnouncemen
...
@@ -204,7 +205,7 @@ public class ApiAnnouncementCommissionRatioServiceImpl implements ApiAnnouncemen
@Override
@Override
public
Result
<
Boolean
>
isData
(
String
productLaunchBizId
)
{
public
Result
<
Boolean
>
isData
(
String
productLaunchBizId
)
{
List
<
AnnouncementSpecies
>
speciesList
=
iAnnouncementSpeciesService
.
queryList
(
productLaunchBizId
);
List
<
AnnouncementSpecies
>
speciesList
=
iAnnouncementSpeciesService
.
queryList
(
productLaunchBizId
);
if
(
CollectionUtils
.
isEmpty
(
speciesList
)){
if
(
CollectionUtils
.
isEmpty
(
speciesList
))
{
return
Result
.
success
(
false
);
return
Result
.
success
(
false
);
}
}
List
<
String
>
announcementSpeciesBizIdList
=
speciesList
.
stream
().
map
(
AnnouncementSpecies:
:
getAnnouncementSpeciesBizId
).
collect
(
Collectors
.
toList
());
List
<
String
>
announcementSpeciesBizIdList
=
speciesList
.
stream
().
map
(
AnnouncementSpecies:
:
getAnnouncementSpeciesBizId
).
collect
(
Collectors
.
toList
());
...
@@ -231,28 +232,35 @@ public class ApiAnnouncementCommissionRatioServiceImpl implements ApiAnnouncemen
...
@@ -231,28 +232,35 @@ public class ApiAnnouncementCommissionRatioServiceImpl implements ApiAnnouncemen
}
}
/**
/**
* 校验入参-公告佣比率规格明细列表
* 校验入参-公告佣比率规格明细列表
(年限区间和有效时间区间均不能重叠)
* @param ratioBatchSaveDtoList
* @param ratioBatchSaveDtoList
* @return
* @return
*/
*/
public
Result
checkBatchSaveRequestPram
(
List
<
ApiAnnouncementCommissionRatioBatchSaveDto
>
ratioBatchSaveDtoList
)
{
public
Result
checkBatchSaveRequestPram
(
List
<
ApiAnnouncementCommissionRatioBatchSaveDto
>
ratioBatchSaveDtoList
)
{
// 1. 验证并准备数据(增加有效时间字段)
// 1. 验证并准备数据
List
<
DtoWithParsedData
>
preparedDataList
=
prepareData
(
ratioBatchSaveDtoList
);
List
<
DtoWithParsedData
>
preparedDataList
=
prepareData
(
ratioBatchSaveDtoList
);
// 2. 检查所有数据对的年限重叠情况(考虑scope交集)
// 2. 检查年限重叠(原有逻辑,保持不变)
List
<
OverlapError
>
errors
=
checkAllDataPairs
(
preparedDataList
);
List
<
OverlapError
>
yearErrors
=
checkYearOverlap
(
preparedDataList
);
// 3. 检查有效时间重叠(新增逻辑,使用排除 effectiveStart/end 的分组,但仍考虑 scope 交集)
List
<
OverlapError
>
effectiveErrors
=
checkEffectiveTimeOverlap
(
preparedDataList
);
// 3. 如果有错误,返回错误信息
// 4. 合并错误
if
(!
errors
.
isEmpty
())
{
List
<
OverlapError
>
allErrors
=
new
ArrayList
<>();
String
errorMsg
=
buildErrorMessage
(
errors
,
ratioBatchSaveDtoList
);
allErrors
.
addAll
(
yearErrors
);
throw
new
BusinessException
(
1004
,
errorMsg
);
allErrors
.
addAll
(
effectiveErrors
);
// 5. 如果有错误,返回错误信息
if
(!
allErrors
.
isEmpty
())
{
String
errorMsg
=
buildErrorMessage
(
allErrors
,
ratioBatchSaveDtoList
);
throw
new
BusinessException
(
1004
,
errorMsg
);
}
}
return
Result
.
success
();
return
Result
.
success
();
}
}
/**
/**
* 准备数据:解析年限
并将scope转换为集合
* 准备数据:解析年限
、存储有效时间、解析scope,并校验基本合法性
* @param dtoList
* @param dtoList
* @return
* @return
*/
*/
...
@@ -267,63 +275,96 @@ public class ApiAnnouncementCommissionRatioServiceImpl implements ApiAnnouncemen
...
@@ -267,63 +275,96 @@ public class ApiAnnouncementCommissionRatioServiceImpl implements ApiAnnouncemen
Integer
endYear
=
ProductCommonUtils
.
parseYearToInt
(
dto
.
getEndPeriod
());
Integer
endYear
=
ProductCommonUtils
.
parseYearToInt
(
dto
.
getEndPeriod
());
if
(
startYear
>
endYear
)
{
if
(
startYear
>
endYear
)
{
throw
new
BusinessException
(
"第"
+
(
i
+
1
)
+
"条数据的起始年限["
+
startYear
+
"]不能大于结束年限["
+
endYear
+
"]"
);
throw
new
BusinessException
(
"第"
+
(
i
+
1
)
+
"条数据的起始年限["
+
startYear
+
"]不能大于结束年限["
+
endYear
+
"]"
);
}
// 校验有效时间起止顺序(新增)
if
(
dto
.
getEffectiveStart
().
isAfter
(
dto
.
getEffectiveEnd
()))
{
throw
new
BusinessException
(
"第"
+
(
i
+
1
)
+
"条数据的有效开始时间不能晚于有效结束时间"
);
}
}
// 解析scope为集合,去重
// 解析scope为集合,去重
Set
<
String
>
scopeSet
=
ProductCommonUtils
.
parseScopeToSet
(
dto
.
getScope
());
Set
<
String
>
scopeSet
=
ProductCommonUtils
.
parseScopeToSet
(
dto
.
getScope
());
preparedList
.
add
(
new
DtoWithParsedData
(
i
,
dto
,
startYear
,
endYear
,
scopeSet
));
preparedList
.
add
(
new
DtoWithParsedData
(
i
,
dto
,
startYear
,
endYear
,
scopeSet
,
dto
.
getEffectiveStart
(),
dto
.
getEffectiveEnd
()));
}
}
return
preparedList
;
return
preparedList
;
}
}
/**
/**
* 检查
所有数据对的年限重叠情况
* 检查
年限重叠(原有逻辑,完全不变)
*/
*/
private
List
<
OverlapError
>
check
AllDataPairs
(
List
<
DtoWithParsedData
>
preparedDataList
)
{
private
List
<
OverlapError
>
check
YearOverlap
(
List
<
DtoWithParsedData
>
preparedDataList
)
{
List
<
OverlapError
>
errors
=
new
ArrayList
<>();
List
<
OverlapError
>
errors
=
new
ArrayList
<>();
int
n
=
preparedDataList
.
size
();
int
n
=
preparedDataList
.
size
();
// 使用双重循环检查所有数据对
for
(
int
i
=
0
;
i
<
n
-
1
;
i
++)
{
for
(
int
i
=
0
;
i
<
n
-
1
;
i
++)
{
DtoWithParsedData
data1
=
preparedDataList
.
get
(
i
);
DtoWithParsedData
data1
=
preparedDataList
.
get
(
i
);
for
(
int
j
=
i
+
1
;
j
<
n
;
j
++)
{
for
(
int
j
=
i
+
1
;
j
<
n
;
j
++)
{
DtoWithParsedData
data2
=
preparedDataList
.
get
(
j
);
DtoWithParsedData
data2
=
preparedDataList
.
get
(
j
);
// 使用原有的分组条件(包含 effectiveStart/end 和 scope 交集)
// 检查是否属于同一分组条件(考虑scope交集)
if
(
isSameGroupForYear
(
data1
,
data2
))
{
if
(
isSameGroup
(
data1
,
data2
))
{
if
(
ProductCommonUtils
.
isYearRangeOverlap
(
data1
.
startYear
,
data1
.
endYear
,
// 检查年限是否重叠
data2
.
startYear
,
data2
.
endYear
))
{
if
(
ProductCommonUtils
.
isYearRangeOverlap
(
data1
.
startYear
,
data1
.
endYear
,
data2
.
startYear
,
data2
.
endYear
))
{
if
(!
isErrorAlreadyExists
(
errors
,
data1
.
originalIndex
,
data2
.
originalIndex
,
"YEAR"
))
{
// 避免重复添加相同的错误
errors
.
add
(
new
OverlapError
(
data1
.
originalIndex
,
data2
.
originalIndex
,
"YEAR"
));
if
(!
isErrorAlreadyExists
(
errors
,
data1
.
originalIndex
,
data2
.
originalIndex
))
{
errors
.
add
(
new
OverlapError
(
data1
.
originalIndex
,
data2
.
originalIndex
));
}
}
}
}
}
}
}
}
}
}
return
errors
;
}
/**
* 检查有效时间重叠(新增逻辑,使用排除 effectiveStart/end 的分组,但仍考虑 scope 交集)
*/
private
List
<
OverlapError
>
checkEffectiveTimeOverlap
(
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
);
// 使用排除 effectiveStart/end 的分组条件,但仍然检查 scope 交集
if
(
isSameGroupForEffectiveTime
(
data1
,
data2
))
{
if
(
isTimeRangeOverlap
(
data1
.
effectiveStart
,
data1
.
effectiveEnd
,
data2
.
effectiveStart
,
data2
.
effectiveEnd
))
{
if
(!
isErrorAlreadyExists
(
errors
,
data1
.
originalIndex
,
data2
.
originalIndex
,
"EFFECTIVE"
))
{
errors
.
add
(
new
OverlapError
(
data1
.
originalIndex
,
data2
.
originalIndex
,
"EFFECTIVE"
));
}
}
}
}
}
return
errors
;
return
errors
;
}
}
/**
/**
* 判断两个
数据是否属于同一分组条件
* 判断两个
有效时间区间是否重叠
*/
*/
private
boolean
isSameGroup
(
DtoWithParsedData
data1
,
DtoWithParsedData
data2
)
{
private
boolean
isTimeRangeOverlap
(
LocalDateTime
start1
,
LocalDateTime
end1
,
// 1. 检查除scope外的其他条件是否相同
LocalDateTime
start2
,
LocalDateTime
end2
)
{
return
!(
end1
.
isBefore
(
start2
)
||
end2
.
isBefore
(
start1
));
}
/**
* 原有的分组条件(包含 effectiveStart/end 和 scope 交集),用于年限重叠校验,保持不变
*/
private
boolean
isSameGroupForYear
(
DtoWithParsedData
data1
,
DtoWithParsedData
data2
)
{
// 1. 检查除scope外的其他条件是否相同(包含 effectiveStart/end)
if
(!
isSameGroupExceptScope
(
data1
.
dto
,
data2
.
dto
))
{
if
(!
isSameGroupExceptScope
(
data1
.
dto
,
data2
.
dto
))
{
return
false
;
return
false
;
}
}
// 2. 检查scope是否有交集
// 2. 检查scope是否有交集
return
ProductCommonUtils
.
hasScopeIntersection
(
data1
.
scopeSet
,
data2
.
scopeSet
);
return
ProductCommonUtils
.
hasScopeIntersection
(
data1
.
scopeSet
,
data2
.
scopeSet
);
}
}
/**
/**
*
检查除scope外的其他条件是否相同
*
原有的 isSameGroupExceptScope 方法,完全不变
*/
*/
private
boolean
isSameGroupExceptScope
(
ApiAnnouncementCommissionRatioBatchSaveDto
dto1
,
private
boolean
isSameGroupExceptScope
(
ApiAnnouncementCommissionRatioBatchSaveDto
dto1
,
ApiAnnouncementCommissionRatioBatchSaveDto
dto2
)
{
ApiAnnouncementCommissionRatioBatchSaveDto
dto2
)
{
...
@@ -335,7 +376,29 @@ public class ApiAnnouncementCommissionRatioServiceImpl implements ApiAnnouncemen
...
@@ -335,7 +376,29 @@ public class ApiAnnouncementCommissionRatioServiceImpl implements ApiAnnouncemen
}
}
/**
/**
* 校验基础字段
* 新增:用于有效时间重叠校验的分组条件(排除 effectiveStart/end,但仍检查 scope 交集)
*/
private
boolean
isSameGroupForEffectiveTime
(
DtoWithParsedData
data1
,
DtoWithParsedData
data2
)
{
// 1. 检查排除 effectiveStart/end 后的其他条件是否相同
if
(!
isSameGroupExceptTimeAndScope
(
data1
.
dto
,
data2
.
dto
))
{
return
false
;
}
// 2. 检查scope是否有交集
return
ProductCommonUtils
.
hasScopeIntersection
(
data1
.
scopeSet
,
data2
.
scopeSet
);
}
/**
* 检查排除 effectiveStart/end 和 scope 后的条件是否相同(仅业务分组字段)
*/
private
boolean
isSameGroupExceptTimeAndScope
(
ApiAnnouncementCommissionRatioBatchSaveDto
dto1
,
ApiAnnouncementCommissionRatioBatchSaveDto
dto2
)
{
return
Objects
.
equals
(
dto1
.
getExpenseName
(),
dto2
.
getExpenseName
())
&&
Objects
.
equals
(
dto1
.
getIsExchangeRate
(),
dto2
.
getIsExchangeRate
())
&&
Objects
.
equals
(
dto1
.
getCurrency
(),
dto2
.
getCurrency
());
}
/**
* 校验基础字段(保留,未使用)
*/
*/
private
boolean
validateBasicFields
(
ApiAnnouncementCommissionRatioBatchSaveDto
dto
)
{
private
boolean
validateBasicFields
(
ApiAnnouncementCommissionRatioBatchSaveDto
dto
)
{
return
dto
.
getExpenseName
()
!=
null
&&
!
dto
.
getExpenseName
().
trim
().
isEmpty
()
return
dto
.
getExpenseName
()
!=
null
&&
!
dto
.
getExpenseName
().
trim
().
isEmpty
()
...
@@ -348,52 +411,21 @@ public class ApiAnnouncementCommissionRatioServiceImpl implements ApiAnnouncemen
...
@@ -348,52 +411,21 @@ public class ApiAnnouncementCommissionRatioServiceImpl implements ApiAnnouncemen
}
}
/**
/**
* 检查错误是否已存在
* 检查错误是否已存在
(区分类型)
*/
*/
private
boolean
isErrorAlreadyExists
(
List
<
OverlapError
>
errors
,
int
index1
,
int
index2
)
{
private
boolean
isErrorAlreadyExists
(
List
<
OverlapError
>
errors
,
int
index1
,
int
index2
,
String
type
)
{
for
(
OverlapError
error
:
errors
)
{
for
(
OverlapError
error
:
errors
)
{
if
((
error
.
index1
==
index1
&&
error
.
index2
==
index2
)
||
if
(((
error
.
index1
==
index1
&&
error
.
index2
==
index2
)
||
(
error
.
index1
==
index2
&&
error
.
index2
==
index1
))
{
(
error
.
index1
==
index2
&&
error
.
index2
==
index1
))
&&
error
.
type
.
equals
(
type
))
{
return
true
;
return
true
;
}
}
}
}
return
false
;
return
false
;
}
}
// /**
// * 构建错误消息
// */
// private String buildErrorMessage(List<OverlapError> errors,
// List<ApiAnnouncementCommissionRatioBatchSaveDto> originalList) {
// if (errors.isEmpty()) {
// return "未发现重叠数据";
// }
//
// StringBuilder sb = new StringBuilder();
//
// Set<Integer> set = new HashSet<>();
// if (!CollectionUtils.isEmpty(errors)) {
// errors.stream().forEach(dto -> {
// set.add(dto.getIndex1());
// set.add(dto.getIndex2());
// });
// }
//
// sb.append("发现佣金年限区间重叠的数据,请检查以下行数:\n\n");
//
// // 将行数(索引+1,因为用户从1开始计数)排序并格式化为"第X行"
// String rowNumbers = set.stream()
// .sorted()
// .map(index -> "第" + (index + 1) + "行")
// .collect(Collectors.joining(",")); // 用中文逗号分隔
//
// sb.append(rowNumbers);
//
// return sb.toString();
// }
/**
/**
* 构建错误消息
* 构建错误消息
(区分年限重叠和有效时间重叠)
*/
*/
private
String
buildErrorMessage
(
List
<
OverlapError
>
errors
,
private
String
buildErrorMessage
(
List
<
OverlapError
>
errors
,
List
<
ApiAnnouncementCommissionRatioBatchSaveDto
>
originalList
)
{
List
<
ApiAnnouncementCommissionRatioBatchSaveDto
>
originalList
)
{
...
@@ -401,72 +433,45 @@ public class ApiAnnouncementCommissionRatioServiceImpl implements ApiAnnouncemen
...
@@ -401,72 +433,45 @@ public class ApiAnnouncementCommissionRatioServiceImpl implements ApiAnnouncemen
return
"未发现重叠数据"
;
return
"未发现重叠数据"
;
}
}
StringBuilder
sb
=
new
StringBuilder
();
// 按重叠类型分组
Map
<
String
,
Map
<
Integer
,
List
<
Integer
>>>
typeGroupMap
=
new
LinkedHashMap
<>();
// Set<Integer> set = new HashSet<>();
// if (!CollectionUtils.isEmpty(errors)) {
// errors.stream().forEach(dto -> {
// set.add(dto.getIndex1());
// set.add(dto.getIndex2());
// });
// }
sb
.
append
(
"发现佣金年限区间重叠的数据,请检查以下数据:\n\n"
);
// sb.append("发现佣金年限区间重叠的数据,请检查以下行数:\n\n");
// sb.append(String.join("",set))
// // 按数据索引分组,方便查看每条数据的所有重叠关系
Map
<
Integer
,
List
<
Integer
>>
overlapMap
=
new
TreeMap
<>();
for
(
OverlapError
error
:
errors
)
{
for
(
OverlapError
error
:
errors
)
{
overlapMap
.
computeIfAbsent
(
error
.
index1
,
k
->
new
ArrayList
<>()).
add
(
error
.
index2
);
typeGroupMap
.
computeIfAbsent
(
error
.
type
,
k
->
new
TreeMap
<>())
overlapMap
.
computeIfAbsent
(
error
.
index2
,
k
->
new
ArrayList
<>()).
add
(
error
.
index1
);
.
computeIfAbsent
(
error
.
index1
,
k
->
new
ArrayList
<>()).
add
(
error
.
index2
);
typeGroupMap
.
get
(
error
.
type
)
.
computeIfAbsent
(
error
.
index2
,
k
->
new
ArrayList
<>()).
add
(
error
.
index1
);
}
}
// 构建错误信息
StringBuilder
sb
=
new
StringBuilder
();
for
(
Map
.
Entry
<
String
,
Map
<
Integer
,
List
<
Integer
>>>
typeEntry
:
typeGroupMap
.
entrySet
())
{
String
overlapType
=
"YEAR"
.
equals
(
typeEntry
.
getKey
())
?
"佣金年限区间"
:
"有效时间区间"
;
sb
.
append
(
"发现"
).
append
(
overlapType
).
append
(
"重叠的数据:\n"
);
Map
<
Integer
,
List
<
Integer
>>
overlapMap
=
typeEntry
.
getValue
();
for
(
Map
.
Entry
<
Integer
,
List
<
Integer
>>
entry
:
overlapMap
.
entrySet
())
{
for
(
Map
.
Entry
<
Integer
,
List
<
Integer
>>
entry
:
overlapMap
.
entrySet
())
{
int
dataIndex
=
entry
.
getKey
();
int
dataIndex
=
entry
.
getKey
();
List
<
Integer
>
overlapIndices
=
entry
.
getValue
();
List
<
Integer
>
overlapIndices
=
entry
.
getValue
();
ApiAnnouncementCommissionRatioBatchSaveDto
dto
=
originalList
.
get
(
dataIndex
);
ApiAnnouncementCommissionRatioBatchSaveDto
dto
=
originalList
.
get
(
dataIndex
);
if
(
"YEAR"
.
equals
(
typeEntry
.
getKey
()))
{
sb
.
append
(
"第"
).
append
(
dataIndex
+
1
).
append
(
"行数据年限区间"
)
sb
.
append
(
"第"
).
append
(
dataIndex
+
1
).
append
(
"行数据年限区间 "
)
// .append(" - 费用名称: ").append(dto.getExpenseName()).append("\n")
.
append
(
dto
.
getStartPeriod
()).
append
(
"~"
).
append
(
dto
.
getEndPeriod
())
// .append(" - 年限区间: ").append(dto.getStartPeriod()).append(" 至 ").append(dto.getEndPeriod()).append("\n")
.
append
(
" 与以下数据年限区间重叠:"
);
// .append(" - 适用范围: ").append(dto.getScope()).append("\n")
}
else
{
// .append(" - 有效时间: ").append(dto.getEffectiveStart()).append(" 至 ").append(dto.getEffectiveEnd()).append("\n")
sb
.
append
(
"第"
).
append
(
dataIndex
+
1
).
append
(
"行数据有效时间 "
)
// .append(" - 汇率影响: ").append(dto.getIsExchangeRate()).append("\n")
.
append
(
dto
.
getEffectiveStart
()).
append
(
"~"
).
append
(
dto
.
getEffectiveEnd
())
// .append(" - 结算币种: ").append(dto.getCurrency()).append("\n")
.
append
(
" 与以下数据有效时间重叠:"
);
.
append
(
"与以下数据年限区间有重叠:"
);
}
for
(
int
overlapIndex
:
overlapIndices
)
{
for
(
int
overlapIndex
:
overlapIndices
)
{
ApiAnnouncementCommissionRatioBatchSaveDto
overlapDto
=
originalList
.
get
(
overlapIndex
);
sb
.
append
(
" 第"
).
append
(
overlapIndex
+
1
).
append
(
"行"
);
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
(
"\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
();
return
sb
.
toString
();
}
}
/**
/**
* 内部类:包含解析后数据的DTO
* 内部类:包含解析后数据的DTO
(增加有效时间字段)
*/
*/
private
static
class
DtoWithParsedData
{
private
static
class
DtoWithParsedData
{
final
int
originalIndex
;
// 在原始列表中的索引
final
int
originalIndex
;
// 在原始列表中的索引
...
@@ -474,31 +479,34 @@ public class ApiAnnouncementCommissionRatioServiceImpl implements ApiAnnouncemen
...
@@ -474,31 +479,34 @@ public class ApiAnnouncementCommissionRatioServiceImpl implements ApiAnnouncemen
final
int
startYear
;
// 解析后的起始年份
final
int
startYear
;
// 解析后的起始年份
final
int
endYear
;
// 解析后的结束年份
final
int
endYear
;
// 解析后的结束年份
final
Set
<
String
>
scopeSet
;
// 解析后的scope集合
final
Set
<
String
>
scopeSet
;
// 解析后的scope集合
final
LocalDateTime
effectiveStart
;
// 有效开始时间
final
LocalDateTime
effectiveEnd
;
// 有效结束时间
public
DtoWithParsedData
(
int
originalIndex
,
ApiAnnouncementCommissionRatioBatchSaveDto
dto
,
public
DtoWithParsedData
(
int
originalIndex
,
ApiAnnouncementCommissionRatioBatchSaveDto
dto
,
int
startYear
,
int
endYear
,
Set
<
String
>
scopeSet
)
{
int
startYear
,
int
endYear
,
Set
<
String
>
scopeSet
,
LocalDateTime
effectiveStart
,
LocalDateTime
effectiveEnd
)
{
this
.
originalIndex
=
originalIndex
;
this
.
originalIndex
=
originalIndex
;
this
.
dto
=
dto
;
this
.
dto
=
dto
;
this
.
startYear
=
startYear
;
this
.
startYear
=
startYear
;
this
.
endYear
=
endYear
;
this
.
endYear
=
endYear
;
this
.
scopeSet
=
scopeSet
;
this
.
scopeSet
=
scopeSet
;
this
.
effectiveStart
=
effectiveStart
;
this
.
effectiveEnd
=
effectiveEnd
;
}
}
}
}
/**
/**
* 内部类:重叠错误
* 内部类:重叠错误
(增加类型字段)
*/
*/
private
static
class
OverlapError
{
private
static
class
OverlapError
{
private
final
int
index1
;
private
final
int
index1
;
private
final
int
index2
;
private
final
int
index2
;
private
final
String
type
;
// "YEAR" 或 "EFFECTIVE"
public
OverlapError
(
int
index1
,
int
index2
)
{
public
OverlapError
(
int
index1
,
int
index2
,
String
type
)
{
this
.
index1
=
index1
;
this
.
index1
=
index1
;
this
.
index2
=
index2
;
this
.
index2
=
index2
;
this
.
type
=
type
;
}
}
public
int
getIndex1
()
{
return
index1
;
}
public
int
getIndex2
()
{
return
index2
;
}
}
}
}
}
\ No newline at end of file
yd-product-api/src/main/java/com/yd/product/api/service/impl/ApiExpectedCommissionRatioServiceImpl.java
View file @
4ab00f6c
...
@@ -10,14 +10,12 @@ import com.yd.product.api.service.ApiExpectedCommissionRatioService;
...
@@ -10,14 +10,12 @@ import com.yd.product.api.service.ApiExpectedCommissionRatioService;
import
com.yd.product.api.service.ApiExpectedSpeciesService
;
import
com.yd.product.api.service.ApiExpectedSpeciesService
;
import
com.yd.product.api.utils.ProductCommonUtils
;
import
com.yd.product.api.utils.ProductCommonUtils
;
import
com.yd.product.feign.dto.ApiExpectedCommissionRatioBatchSaveDto
;
import
com.yd.product.feign.dto.ApiExpectedCommissionRatioBatchSaveDto
;
import
com.yd.product.feign.dto.ApiExpectedCommissionRatioBatchSaveDto
;
import
com.yd.product.feign.request.expectedcommissionratio.ApiExpectedCommissionRatioAddRequest
;
import
com.yd.product.feign.request.expectedcommissionratio.ApiExpectedCommissionRatioAddRequest
;
import
com.yd.product.feign.request.expectedcommissionratio.ApiExpectedCommissionRatioBatchSaveRequest
;
import
com.yd.product.feign.request.expectedcommissionratio.ApiExpectedCommissionRatioBatchSaveRequest
;
import
com.yd.product.feign.request.expectedcommissionratio.ApiExpectedCommissionRatioEditRequest
;
import
com.yd.product.feign.request.expectedcommissionratio.ApiExpectedCommissionRatioEditRequest
;
import
com.yd.product.feign.request.expectedcommissionratio.ApiExpectedCommissionRatioPageRequest
;
import
com.yd.product.feign.request.expectedcommissionratio.ApiExpectedCommissionRatioPageRequest
;
import
com.yd.product.feign.response.expectedcommissionratio.ApiExpectedCommissionRatioDetailResponse
;
import
com.yd.product.feign.response.expectedcommissionratio.ApiExpectedCommissionRatioDetailResponse
;
import
com.yd.product.feign.response.expectedcommissionratio.ApiExpectedCommissionRatioPageResponse
;
import
com.yd.product.feign.response.expectedcommissionratio.ApiExpectedCommissionRatioPageResponse
;
import
com.yd.product.service.model.AnnouncementCommissionRatio
;
import
com.yd.product.service.model.ExpectedCommissionRatio
;
import
com.yd.product.service.model.ExpectedCommissionRatio
;
import
com.yd.product.service.service.IExpectedCommissionRatioService
;
import
com.yd.product.service.service.IExpectedCommissionRatioService
;
import
lombok.extern.slf4j.Slf4j
;
import
lombok.extern.slf4j.Slf4j
;
...
@@ -26,6 +24,7 @@ import org.springframework.beans.factory.annotation.Autowired;
...
@@ -26,6 +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.time.LocalDateTime
;
import
java.util.*
;
import
java.util.*
;
import
java.util.stream.Collectors
;
import
java.util.stream.Collectors
;
...
@@ -70,7 +69,7 @@ public class ApiExpectedCommissionRatioServiceImpl implements ApiExpectedCommiss
...
@@ -70,7 +69,7 @@ public class ApiExpectedCommissionRatioServiceImpl implements ApiExpectedCommiss
.
stream
()
.
stream
()
.
map
(
dto
->
{
.
map
(
dto
->
{
ExpectedCommissionRatio
ratio
=
new
ExpectedCommissionRatio
();
ExpectedCommissionRatio
ratio
=
new
ExpectedCommissionRatio
();
BeanUtils
.
copyProperties
(
dto
,
ratio
);
BeanUtils
.
copyProperties
(
dto
,
ratio
);
ratio
.
setExpectedSpeciesBizId
(
request
.
getExpectedSpeciesBizId
());
ratio
.
setExpectedSpeciesBizId
(
request
.
getExpectedSpeciesBizId
());
ratio
.
setExpectedCommissionRatioBizId
(
RandomStringGenerator
.
generateBizId16
(
CommonEnum
.
UID_TYPE_EXPECTED_COMMISSION_RATIO
.
getCode
()));
ratio
.
setExpectedCommissionRatioBizId
(
RandomStringGenerator
.
generateBizId16
(
CommonEnum
.
UID_TYPE_EXPECTED_COMMISSION_RATIO
.
getCode
()));
return
ratio
;
return
ratio
;
...
@@ -92,7 +91,7 @@ public class ApiExpectedCommissionRatioServiceImpl implements ApiExpectedCommiss
...
@@ -92,7 +91,7 @@ public class ApiExpectedCommissionRatioServiceImpl implements ApiExpectedCommiss
//入参校验,佣金年限校验不同条件下的唯一性 TODO
//入参校验,佣金年限校验不同条件下的唯一性 TODO
//新增
//新增
ExpectedCommissionRatio
ratio
=
new
ExpectedCommissionRatio
();
ExpectedCommissionRatio
ratio
=
new
ExpectedCommissionRatio
();
BeanUtils
.
copyProperties
(
request
,
ratio
);
BeanUtils
.
copyProperties
(
request
,
ratio
);
ratio
.
setExpectedCommissionRatioBizId
(
RandomStringGenerator
.
generateBizId16
(
CommonEnum
.
UID_TYPE_EXPECTED_COMMISSION_RATIO
.
getCode
()));
ratio
.
setExpectedCommissionRatioBizId
(
RandomStringGenerator
.
generateBizId16
(
CommonEnum
.
UID_TYPE_EXPECTED_COMMISSION_RATIO
.
getCode
()));
iExpectedCommissionRatioService
.
saveOrUpdate
(
ratio
);
iExpectedCommissionRatioService
.
saveOrUpdate
(
ratio
);
return
Result
.
success
();
return
Result
.
success
();
...
@@ -112,7 +111,7 @@ public class ApiExpectedCommissionRatioServiceImpl implements ApiExpectedCommiss
...
@@ -112,7 +111,7 @@ public class ApiExpectedCommissionRatioServiceImpl implements ApiExpectedCommiss
apiExpectedSpeciesService
.
checkExpectedSpeciesIsExist
(
request
.
getExpectedSpeciesBizId
());
apiExpectedSpeciesService
.
checkExpectedSpeciesIsExist
(
request
.
getExpectedSpeciesBizId
());
//入参校验,佣金年限校验不同条件下的唯一性 TODO
//入参校验,佣金年限校验不同条件下的唯一性 TODO
//编辑
//编辑
BeanUtils
.
copyProperties
(
request
,
ratio
);
BeanUtils
.
copyProperties
(
request
,
ratio
);
iExpectedCommissionRatioService
.
saveOrUpdate
(
ratio
);
iExpectedCommissionRatioService
.
saveOrUpdate
(
ratio
);
return
Result
.
success
();
return
Result
.
success
();
}
}
...
@@ -144,7 +143,7 @@ public class ApiExpectedCommissionRatioServiceImpl implements ApiExpectedCommiss
...
@@ -144,7 +143,7 @@ public class ApiExpectedCommissionRatioServiceImpl implements ApiExpectedCommiss
Result
<
ExpectedCommissionRatio
>
result
=
checkExpectedCommissionRatioIsExist
(
expectedSpeciesBizId
);
Result
<
ExpectedCommissionRatio
>
result
=
checkExpectedCommissionRatioIsExist
(
expectedSpeciesBizId
);
ExpectedCommissionRatio
ratio
=
result
.
getData
();
ExpectedCommissionRatio
ratio
=
result
.
getData
();
ApiExpectedCommissionRatioDetailResponse
response
=
new
ApiExpectedCommissionRatioDetailResponse
();
ApiExpectedCommissionRatioDetailResponse
response
=
new
ApiExpectedCommissionRatioDetailResponse
();
BeanUtils
.
copyProperties
(
ratio
,
response
);
BeanUtils
.
copyProperties
(
ratio
,
response
);
return
Result
.
success
(
response
);
return
Result
.
success
(
response
);
}
}
...
@@ -179,33 +178,40 @@ public class ApiExpectedCommissionRatioServiceImpl implements ApiExpectedCommiss
...
@@ -179,33 +178,40 @@ public class ApiExpectedCommissionRatioServiceImpl implements ApiExpectedCommiss
}
}
/**
/**
* 校验入参-来佣比率规格明细列表
* 校验入参-来佣比率规格明细列表
(年限区间和有效时间区间均不能重叠)
* @param ratioBatchSaveDtoList
* @param ratioBatchSaveDtoList
* @return
* @return
*/
*/
public
Result
checkBatchSaveRequestPram
(
List
<
ApiExpectedCommissionRatioBatchSaveDto
>
ratioBatchSaveDtoList
)
{
public
Result
checkBatchSaveRequestPram
(
List
<
ApiExpectedCommissionRatioBatchSaveDto
>
ratioBatchSaveDtoList
)
{
// 1. 验证并准备数据(增加有效时间字段)
List
<
DtoWithParsedData
>
preparedDataList
=
prepareData
(
ratioBatchSaveDtoList
);
// 2. 检查所有数据对的年限重叠情况(原有逻辑,保持不变)
List
<
OverlapError
>
yearErrors
=
checkYearOverlap
(
preparedDataList
);
//
1. 验证并准备数据
//
3. 检查所有数据对的有效时间重叠情况(新增逻辑,使用排除时间字段的业务分组)
List
<
ApiExpectedCommissionRatioServiceImpl
.
DtoWithParsedData
>
preparedDataList
=
prepareData
(
ratioBatchSaveDto
List
);
List
<
OverlapError
>
effectiveErrors
=
checkEffectiveTimeOverlap
(
preparedData
List
);
// 2. 检查所有数据对的年限重叠情况
// 4. 合并错误
List
<
ApiExpectedCommissionRatioServiceImpl
.
OverlapError
>
errors
=
checkAllDataPairs
(
preparedDataList
);
List
<
OverlapError
>
allErrors
=
new
ArrayList
<>();
allErrors
.
addAll
(
yearErrors
);
allErrors
.
addAll
(
effectiveErrors
);
//
3
. 如果有错误,返回错误信息
//
5
. 如果有错误,返回错误信息
if
(!
e
rrors
.
isEmpty
())
{
if
(!
allE
rrors
.
isEmpty
())
{
String
errorMsg
=
buildErrorMessage
(
e
rrors
,
ratioBatchSaveDtoList
);
String
errorMsg
=
buildErrorMessage
(
allE
rrors
,
ratioBatchSaveDtoList
);
throw
new
BusinessException
(
1004
,
errorMsg
);
throw
new
BusinessException
(
1004
,
errorMsg
);
}
}
return
Result
.
success
();
return
Result
.
success
();
}
}
/**
/**
* 准备数据:解析年限
并转换为集合
* 准备数据:解析年限
、存储有效时间,并校验基本合法性
* @param dtoList
* @param dtoList
* @return
* @return
*/
*/
private
List
<
ApiExpectedCommissionRatioServiceImpl
.
DtoWithParsedData
>
prepareData
(
List
<
ApiExpectedCommissionRatioBatchSaveDto
>
dtoList
)
{
private
List
<
DtoWithParsedData
>
prepareData
(
List
<
ApiExpectedCommissionRatioBatchSaveDto
>
dtoList
)
{
List
<
ApiExpectedCommissionRatioServiceImpl
.
DtoWithParsedData
>
preparedList
=
new
ArrayList
<>();
List
<
DtoWithParsedData
>
preparedList
=
new
ArrayList
<>();
for
(
int
i
=
0
;
i
<
dtoList
.
size
();
i
++)
{
for
(
int
i
=
0
;
i
<
dtoList
.
size
();
i
++)
{
ApiExpectedCommissionRatioBatchSaveDto
dto
=
dtoList
.
get
(
i
);
ApiExpectedCommissionRatioBatchSaveDto
dto
=
dtoList
.
get
(
i
);
...
@@ -218,56 +224,85 @@ public class ApiExpectedCommissionRatioServiceImpl implements ApiExpectedCommiss
...
@@ -218,56 +224,85 @@ public class ApiExpectedCommissionRatioServiceImpl implements ApiExpectedCommiss
throw
new
BusinessException
(
"第"
+
(
i
+
1
)
+
"条数据的起始年限["
+
startYear
+
"]不能大于结束年限["
+
endYear
+
"]"
);
throw
new
BusinessException
(
"第"
+
(
i
+
1
)
+
"条数据的起始年限["
+
startYear
+
"]不能大于结束年限["
+
endYear
+
"]"
);
}
}
preparedList
.
add
(
new
ApiExpectedCommissionRatioServiceImpl
.
DtoWithParsedData
(
i
,
dto
,
startYear
,
endYear
));
// 校验有效时间起止顺序(新增)
if
(
dto
.
getEffectiveStart
().
isAfter
(
dto
.
getEffectiveEnd
()))
{
throw
new
BusinessException
(
"第"
+
(
i
+
1
)
+
"条数据的有效开始时间不能晚于有效结束时间"
);
}
preparedList
.
add
(
new
DtoWithParsedData
(
i
,
dto
,
startYear
,
endYear
,
dto
.
getEffectiveStart
(),
dto
.
getEffectiveEnd
()));
}
}
return
preparedList
;
return
preparedList
;
}
}
/**
/**
* 检查
所有数据对的年限重叠情况
* 检查
年限重叠(原有逻辑,完全不变)
*/
*/
private
List
<
ApiExpectedCommissionRatioServiceImpl
.
OverlapError
>
checkAllDataPairs
(
List
<
ApiExpectedCommissionRatioServiceImpl
.
DtoWithParsedData
>
preparedDataList
)
{
private
List
<
OverlapError
>
checkYearOverlap
(
List
<
DtoWithParsedData
>
preparedDataList
)
{
List
<
ApiExpectedCommissionRatioServiceImpl
.
OverlapError
>
errors
=
new
ArrayList
<>();
List
<
OverlapError
>
errors
=
new
ArrayList
<>();
int
n
=
preparedDataList
.
size
();
int
n
=
preparedDataList
.
size
();
// 使用双重循环检查所有数据对
for
(
int
i
=
0
;
i
<
n
-
1
;
i
++)
{
for
(
int
i
=
0
;
i
<
n
-
1
;
i
++)
{
ApiExpectedCommissionRatioServiceImpl
.
DtoWithParsedData
data1
=
preparedDataList
.
get
(
i
);
DtoWithParsedData
data1
=
preparedDataList
.
get
(
i
);
for
(
int
j
=
i
+
1
;
j
<
n
;
j
++)
{
for
(
int
j
=
i
+
1
;
j
<
n
;
j
++)
{
ApiExpectedCommissionRatioServiceImpl
.
DtoWithParsedData
data2
=
preparedDataList
.
get
(
j
);
DtoWithParsedData
data2
=
preparedDataList
.
get
(
j
);
// 使用原有的分组条件(包含 effectiveStart/end)
// 检查是否属于同一分组条件
if
(
isSameGroupForYear
(
data1
,
data2
))
{
if
(
isSameGroup
(
data1
,
data2
))
{
if
(
ProductCommonUtils
.
isYearRangeOverlap
(
data1
.
startYear
,
data1
.
endYear
,
// 检查年限是否重叠
data2
.
startYear
,
data2
.
endYear
))
{
if
(
ProductCommonUtils
.
isYearRangeOverlap
(
data1
.
startYear
,
data1
.
endYear
,
data2
.
startYear
,
data2
.
endYear
))
{
if
(!
isErrorAlreadyExists
(
errors
,
data1
.
originalIndex
,
data2
.
originalIndex
,
"YEAR"
))
{
// 避免重复添加相同的错误
errors
.
add
(
new
OverlapError
(
data1
.
originalIndex
,
data2
.
originalIndex
,
"YEAR"
));
if
(!
isErrorAlreadyExists
(
errors
,
data1
.
originalIndex
,
data2
.
originalIndex
))
{
errors
.
add
(
new
ApiExpectedCommissionRatioServiceImpl
.
OverlapError
(
data1
.
originalIndex
,
data2
.
originalIndex
));
}
}
}
}
}
}
}
}
}
}
return
errors
;
}
/**
* 检查有效时间重叠(新增逻辑,使用排除时间字段的业务分组)
*/
private
List
<
OverlapError
>
checkEffectiveTimeOverlap
(
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
);
// 使用排除时间字段的分组条件(expenseName, isExchangeRate, currency, reconciliationCompany)
if
(
isSameGroupForEffectiveTime
(
data1
.
dto
,
data2
.
dto
))
{
if
(
isTimeRangeOverlap
(
data1
.
effectiveStart
,
data1
.
effectiveEnd
,
data2
.
effectiveStart
,
data2
.
effectiveEnd
))
{
if
(!
isErrorAlreadyExists
(
errors
,
data1
.
originalIndex
,
data2
.
originalIndex
,
"EFFECTIVE"
))
{
errors
.
add
(
new
OverlapError
(
data1
.
originalIndex
,
data2
.
originalIndex
,
"EFFECTIVE"
));
}
}
}
}
}
return
errors
;
return
errors
;
}
}
/**
/**
* 判断两个
数据是否属于同一分组条件
* 判断两个
有效时间区间是否重叠
*/
*/
private
boolean
isSameGroup
(
ApiExpectedCommissionRatioServiceImpl
.
DtoWithParsedData
data1
,
ApiExpectedCommissionRatioServiceImpl
.
DtoWithParsedData
data2
)
{
private
boolean
isTimeRangeOverlap
(
LocalDateTime
start1
,
LocalDateTime
end1
,
// 1. 检查条件是否相同
LocalDateTime
start2
,
LocalDateTime
end2
)
{
if
(!
isSameGroupExcept
(
data1
.
dto
,
data2
.
dto
))
{
return
!(
end1
.
isBefore
(
start2
)
||
end2
.
isBefore
(
start1
));
return
false
;
}
}
return
true
;
/**
* 原有的分组条件(包含有效开始/结束时间),用于年限重叠校验,保持不变
*/
private
boolean
isSameGroupForYear
(
DtoWithParsedData
data1
,
DtoWithParsedData
data2
)
{
return
isSameGroupExcept
(
data1
.
dto
,
data2
.
dto
);
}
}
/**
/**
*
检查条件是否相同
*
原有的 isSameGroupExcept 方法,完全不变
*/
*/
private
boolean
isSameGroupExcept
(
ApiExpectedCommissionRatioBatchSaveDto
dto1
,
private
boolean
isSameGroupExcept
(
ApiExpectedCommissionRatioBatchSaveDto
dto1
,
ApiExpectedCommissionRatioBatchSaveDto
dto2
)
{
ApiExpectedCommissionRatioBatchSaveDto
dto2
)
{
...
@@ -280,12 +315,24 @@ public class ApiExpectedCommissionRatioServiceImpl implements ApiExpectedCommiss
...
@@ -280,12 +315,24 @@ public class ApiExpectedCommissionRatioServiceImpl implements ApiExpectedCommiss
}
}
/**
/**
*
检查错误是否已存在
*
新增:用于有效时间重叠校验的分组条件(排除 effectiveStart/end)
*/
*/
private
boolean
isErrorAlreadyExists
(
List
<
ApiExpectedCommissionRatioServiceImpl
.
OverlapError
>
errors
,
int
index1
,
int
index2
)
{
private
boolean
isSameGroupForEffectiveTime
(
ApiExpectedCommissionRatioBatchSaveDto
dto1
,
for
(
ApiExpectedCommissionRatioServiceImpl
.
OverlapError
error
:
errors
)
{
ApiExpectedCommissionRatioBatchSaveDto
dto2
)
{
if
((
error
.
index1
==
index1
&&
error
.
index2
==
index2
)
||
return
Objects
.
equals
(
dto1
.
getExpenseName
(),
dto2
.
getExpenseName
())
(
error
.
index1
==
index2
&&
error
.
index2
==
index1
))
{
&&
Objects
.
equals
(
dto1
.
getIsExchangeRate
(),
dto2
.
getIsExchangeRate
())
&&
Objects
.
equals
(
dto1
.
getCurrency
(),
dto2
.
getCurrency
())
&&
Objects
.
equals
(
dto1
.
getReconciliationCompany
(),
dto2
.
getReconciliationCompany
());
}
/**
* 检查错误是否已存在(区分类型)
*/
private
boolean
isErrorAlreadyExists
(
List
<
OverlapError
>
errors
,
int
index1
,
int
index2
,
String
type
)
{
for
(
OverlapError
error
:
errors
)
{
if
(((
error
.
index1
==
index1
&&
error
.
index2
==
index2
)
||
(
error
.
index1
==
index2
&&
error
.
index2
==
index1
))
&&
error
.
type
.
equals
(
type
))
{
return
true
;
return
true
;
}
}
}
}
...
@@ -293,76 +340,87 @@ public class ApiExpectedCommissionRatioServiceImpl implements ApiExpectedCommiss
...
@@ -293,76 +340,87 @@ public class ApiExpectedCommissionRatioServiceImpl implements ApiExpectedCommiss
}
}
/**
/**
* 构建错误消息
* 构建错误消息
(区分年限重叠和有效时间重叠)
*/
*/
private
String
buildErrorMessage
(
List
<
ApiExpectedCommissionRatioServiceImpl
.
OverlapError
>
errors
,
private
String
buildErrorMessage
(
List
<
OverlapError
>
errors
,
List
<
ApiExpectedCommissionRatioBatchSaveDto
>
originalList
)
{
List
<
ApiExpectedCommissionRatioBatchSaveDto
>
originalList
)
{
if
(
errors
.
isEmpty
())
{
if
(
errors
.
isEmpty
())
{
return
"未发现重叠数据"
;
return
"未发现重叠数据"
;
}
}
StringBuilder
sb
=
new
StringBuilder
();
// 按重叠类型分组
Map
<
String
,
Map
<
Integer
,
List
<
Integer
>>>
typeGroupMap
=
new
LinkedHashMap
<>();
sb
.
append
(
"发现佣金年限区间重叠的数据,请检查以下数据:\n\n"
);
for
(
OverlapError
error
:
errors
)
{
Map
<
Integer
,
List
<
Integer
>>
overlapMap
=
new
TreeMap
<>();
typeGroupMap
.
computeIfAbsent
(
error
.
type
,
k
->
new
TreeMap
<>())
.
computeIfAbsent
(
error
.
index1
,
k
->
new
ArrayList
<>()).
add
(
error
.
index2
);
for
(
ApiExpectedCommissionRatioServiceImpl
.
OverlapError
error
:
errors
)
{
typeGroupMap
.
get
(
error
.
type
)
overlapMap
.
computeIfAbsent
(
error
.
index1
,
k
->
new
ArrayList
<>()).
add
(
error
.
index2
);
.
computeIfAbsent
(
error
.
index2
,
k
->
new
ArrayList
<>()).
add
(
error
.
index1
);
overlapMap
.
computeIfAbsent
(
error
.
index2
,
k
->
new
ArrayList
<>()).
add
(
error
.
index1
);
}
}
// 构建错误信息
StringBuilder
sb
=
new
StringBuilder
();
for
(
Map
.
Entry
<
String
,
Map
<
Integer
,
List
<
Integer
>>>
typeEntry
:
typeGroupMap
.
entrySet
())
{
String
overlapType
=
"YEAR"
.
equals
(
typeEntry
.
getKey
())
?
"佣金年限区间"
:
"有效时间区间"
;
sb
.
append
(
"发现"
).
append
(
overlapType
).
append
(
"重叠的数据:\n"
);
Map
<
Integer
,
List
<
Integer
>>
overlapMap
=
typeEntry
.
getValue
();
for
(
Map
.
Entry
<
Integer
,
List
<
Integer
>>
entry
:
overlapMap
.
entrySet
())
{
for
(
Map
.
Entry
<
Integer
,
List
<
Integer
>>
entry
:
overlapMap
.
entrySet
())
{
int
dataIndex
=
entry
.
getKey
();
int
dataIndex
=
entry
.
getKey
();
List
<
Integer
>
overlapIndices
=
entry
.
getValue
();
List
<
Integer
>
overlapIndices
=
entry
.
getValue
();
ApiExpectedCommissionRatioBatchSaveDto
dto
=
originalList
.
get
(
dataIndex
);
ApiExpectedCommissionRatioBatchSaveDto
dto
=
originalList
.
get
(
dataIndex
);
if
(
"YEAR"
.
equals
(
typeEntry
.
getKey
()))
{
sb
.
append
(
"第"
).
append
(
dataIndex
+
1
).
append
(
"行数据年限区间"
)
sb
.
append
(
"第"
).
append
(
dataIndex
+
1
).
append
(
"行数据年限区间 "
)
.
append
(
"与以下数据年限区间有重叠:"
);
.
append
(
dto
.
getStartPeriod
()).
append
(
"~"
).
append
(
dto
.
getEndPeriod
())
.
append
(
" 与以下数据年限区间重叠:"
);
}
else
{
sb
.
append
(
"第"
).
append
(
dataIndex
+
1
).
append
(
"行数据有效时间 "
)
.
append
(
dto
.
getEffectiveStart
()).
append
(
"~"
).
append
(
dto
.
getEffectiveEnd
())
.
append
(
" 与以下数据有效时间重叠:"
);
}
for
(
int
overlapIndex
:
overlapIndices
)
{
for
(
int
overlapIndex
:
overlapIndices
)
{
ApiExpectedCommissionRatioBatchSaveDto
overlapDto
=
originalList
.
get
(
overlapIndex
);
sb
.
append
(
" 第"
).
append
(
overlapIndex
+
1
).
append
(
"行"
);
sb
.
append
(
" 第"
).
append
(
overlapIndex
+
1
).
append
(
"行"
);
}
}
sb
.
append
(
"\n"
);
sb
.
append
(
"\n"
);
}
}
sb
.
append
(
"\n"
);
}
return
sb
.
toString
();
return
sb
.
toString
();
}
}
/**
/**
* 内部类:包含解析后数据的DTO
* 内部类:包含解析后数据的DTO
(增加有效时间字段)
*/
*/
private
static
class
DtoWithParsedData
{
private
static
class
DtoWithParsedData
{
final
int
originalIndex
;
// 在原始列表中的索引
final
int
originalIndex
;
// 在原始列表中的索引
final
ApiExpectedCommissionRatioBatchSaveDto
dto
;
// 原始DTO
final
ApiExpectedCommissionRatioBatchSaveDto
dto
;
// 原始DTO
final
int
startYear
;
// 解析后的起始年份
final
int
startYear
;
// 解析后的起始年份
final
int
endYear
;
// 解析后的结束年份
final
int
endYear
;
// 解析后的结束年份
final
LocalDateTime
effectiveStart
;
// 有效开始时间
final
LocalDateTime
effectiveEnd
;
// 有效结束时间
public
DtoWithParsedData
(
int
originalIndex
,
ApiExpectedCommissionRatioBatchSaveDto
dto
,
public
DtoWithParsedData
(
int
originalIndex
,
ApiExpectedCommissionRatioBatchSaveDto
dto
,
int
startYear
,
int
endYear
)
{
int
startYear
,
int
endYear
,
LocalDateTime
effectiveStart
,
LocalDateTime
effectiveEnd
)
{
this
.
originalIndex
=
originalIndex
;
this
.
originalIndex
=
originalIndex
;
this
.
dto
=
dto
;
this
.
dto
=
dto
;
this
.
startYear
=
startYear
;
this
.
startYear
=
startYear
;
this
.
endYear
=
endYear
;
this
.
endYear
=
endYear
;
this
.
effectiveStart
=
effectiveStart
;
this
.
effectiveEnd
=
effectiveEnd
;
}
}
}
}
/**
/**
* 内部类:重叠错误
* 内部类:重叠错误
(增加类型字段)
*/
*/
private
static
class
OverlapError
{
private
static
class
OverlapError
{
private
final
int
index1
;
private
final
int
index1
;
private
final
int
index2
;
private
final
int
index2
;
private
final
String
type
;
// "YEAR" 或 "EFFECTIVE"
public
OverlapError
(
int
index1
,
int
index2
)
{
public
OverlapError
(
int
index1
,
int
index2
,
String
type
)
{
this
.
index1
=
index1
;
this
.
index1
=
index1
;
this
.
index2
=
index2
;
this
.
index2
=
index2
;
this
.
type
=
type
;
}
}
public
int
getIndex1
()
{
return
index1
;
}
public
int
getIndex2
()
{
return
index2
;
}
}
}
/**
/**
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment