Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
H
HYH.APSJ
Project
Project
Details
Activity
Releases
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
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
佟礼
HYH.APSJ
Commits
6430785e
Commit
6430785e
authored
Mar 12, 2026
by
DESKTOP-VKRD9QF\Administration
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' of
http://39.100.78.207:1213/tongli/hyh.apsj
parents
247fcbd2
ff8e1628
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
735 additions
and
39 deletions
+735
-39
OrderSortRule.java
src/main/java/com/aps/entity/Algorithm/OrderSortRule.java
+1
-0
Order.java
src/main/java/com/aps/entity/basic/Order.java
+7
-0
GeneticDecoder.java
src/main/java/com/aps/service/Algorithm/GeneticDecoder.java
+170
-8
MaterialRequirementService.java
...com/aps/service/Algorithm/MaterialRequirementService.java
+3
-0
OrderSortService.java
...main/java/com/aps/service/Algorithm/OrderSortService.java
+483
-19
PlanResultService.java
src/main/java/com/aps/service/plan/PlanResultService.java
+10
-0
OrderSortServiceTest.java
src/test/java/com/aps/demo/OrderSortServiceTest.java
+60
-11
PlanResultServiceTest.java
src/test/java/com/aps/demo/PlanResultServiceTest.java
+1
-1
No files found.
src/main/java/com/aps/entity/Algorithm/OrderSortRule.java
View file @
6430785e
...
@@ -7,6 +7,7 @@ import java.util.List;
...
@@ -7,6 +7,7 @@ import java.util.List;
public
class
OrderSortRule
{
public
class
OrderSortRule
{
private
boolean
enabled
;
private
boolean
enabled
;
private
List
<
SortCondition
>
conditions
;
private
List
<
SortCondition
>
conditions
;
//private boolean minimizeChangeover=false;
@Data
@Data
public
static
class
SortCondition
{
public
static
class
SortCondition
{
...
...
src/main/java/com/aps/entity/basic/Order.java
View file @
6430785e
...
@@ -176,4 +176,11 @@ public class Order {
...
@@ -176,4 +176,11 @@ public class Order {
* 物料需求
* 物料需求
*/
*/
private
List
<
OrderMaterialRequirement
>
materialRequirementList
;
private
List
<
OrderMaterialRequirement
>
materialRequirementList
;
/**
* 换线成本
*/
private
double
changeoverCost
;
private
int
changeoverPriority
;
}
}
\ No newline at end of file
src/main/java/com/aps/service/Algorithm/GeneticDecoder.java
View file @
6430785e
...
@@ -479,19 +479,19 @@ if(finishedOrder==null||finishedOrder.size()==0)
...
@@ -479,19 +479,19 @@ if(finishedOrder==null||finishedOrder.size()==0)
}
}
int
setupTime
=
0
;
int
setupTime
=
0
;
CopyOnWriteArrayList
<
GAScheduleResult
>
machineTasks
=
null
;
CopyOnWriteArrayList
<
GAScheduleResult
>
machineTasks
1
=
chromosome
.
getResult
().
stream
()
CopyOnWriteArrayList
<
GAScheduleResult
>
machineTasks
=
chromosome
.
getResult
().
stream
()
.
filter
(
t
->
t
.
getMachineId
()
==
machine
.
getId
())
.
filter
(
t
->
t
.
getMachineId
()
==
machine
.
getId
())
.
sorted
(
Comparator
.
comparingInt
(
GAScheduleResult:
:
getStartTime
))
.
sorted
(
Comparator
.
comparingInt
(
GAScheduleResult:
:
getStartTime
))
.
collect
(
Collectors
.
toCollection
(
CopyOnWriteArrayList:
:
new
));
.
collect
(
Collectors
.
toCollection
(
CopyOnWriteArrayList:
:
new
));
GAScheduleResult
lastGeneOnMachine
=
null
;
GAScheduleResult
lastGeneOnMachine
=
null
;
if
(
machineTasks
1
!=
null
&&
machineTasks1
.
size
()>
0
)
if
(
machineTasks
!=
null
&&
machineTasks
.
size
()>
0
)
{
{
lastGeneOnMachine
=
machineTasks
1
.
get
(
machineTasks1
.
size
()-
1
);
lastGeneOnMachine
=
machineTasks
.
get
(
machineTasks
.
size
()-
1
);
}
}
if
(
lastGeneOnMachine
!=
null
)
if
(
lastGeneOnMachine
!=
null
&&
_globalParam
.
is_smoothChangeOver
()
)
{
{
earliestStartTime
=
Math
.
max
(
earliestStartTime
,
lastGeneOnMachine
.
getEndTime
());
earliestStartTime
=
Math
.
max
(
earliestStartTime
,
lastGeneOnMachine
.
getEndTime
());
}
}
...
@@ -533,16 +533,16 @@ if(finishedOrder==null||finishedOrder.size()==0)
...
@@ -533,16 +533,16 @@ if(finishedOrder==null||finishedOrder.size()==0)
}
}
machineTasks
1
=
chromosome
.
getResult
().
stream
()
machineTasks
=
chromosome
.
getResult
().
stream
()
.
filter
(
t
->
t
.
getMachineId
()
==
machine
.
getId
())
.
filter
(
t
->
t
.
getMachineId
()
==
machine
.
getId
())
.
sorted
(
Comparator
.
comparingInt
(
GAScheduleResult:
:
getStartTime
))
.
sorted
(
Comparator
.
comparingInt
(
GAScheduleResult:
:
getStartTime
))
.
collect
(
Collectors
.
toCollection
(
CopyOnWriteArrayList:
:
new
));
.
collect
(
Collectors
.
toCollection
(
CopyOnWriteArrayList:
:
new
));
if
(
machineTasks
1
!=
null
&&
machineTasks1
.
size
()>
0
)
if
(
machineTasks
!=
null
&&
machineTasks
.
size
()>
0
&&
_globalParam
.
is_smoothChangeOver
()
)
{
{
lastGeneOnMachine
=
machineTasks
1
.
get
(
machineTasks1
.
size
()-
1
);
lastGeneOnMachine
=
machineTasks
.
get
(
machineTasks
.
size
()-
1
);
}
}
if
(
_globalParam
.
is_smoothChangeOver
())
{
if
(
_globalParam
.
is_smoothChangeOver
())
{
...
@@ -702,6 +702,7 @@ if(finishedOrder==null||finishedOrder.size()==0)
...
@@ -702,6 +702,7 @@ if(finishedOrder==null||finishedOrder.size()==0)
}
}
}
}
}
}
//重叠
List
<
OperationDependency
>
SSOperations
=
currentOp
.
getPrevEntryIds
().
stream
()
List
<
OperationDependency
>
SSOperations
=
currentOp
.
getPrevEntryIds
().
stream
()
.
filter
(
t
->
t
.
getDependencyType
()
==
DependencyType
.
StartToStart
)
.
filter
(
t
->
t
.
getDependencyType
()
==
DependencyType
.
StartToStart
)
.
collect
(
Collectors
.
toList
());
//重叠
.
collect
(
Collectors
.
toList
());
//重叠
...
@@ -726,6 +727,18 @@ if(finishedOrder==null||finishedOrder.size()==0)
...
@@ -726,6 +727,18 @@ if(finishedOrder==null||finishedOrder.size()==0)
.
orElse
(
0
);
.
orElse
(
0
);
prevtime
=
calculateNextOperationStartTime
(
newScheduleResult
,
newScheduleResultDetails
,
0
,
processTime
);
prevtime
=
calculateNextOperationStartTime
(
newScheduleResult
,
newScheduleResultDetails
,
0
,
processTime
);
// 下一工序结束时间需晚于上一工序结束时间一个产品加工时间
// 下一工序结束时间需晚于上一工序结束时间一个产品加工时间
// int ssStartTimeWithCalendar = calculateSSStartTimeWithCalendar(
// newScheduleResult,
// newScheduleResultDetails,
// currentOp,
// machine,
// processTime,
// prevtime);
//
// prevtime = Math.max(prevtime, ssStartTimeWithCalendar);
double
nextopplanend
=
prevperationEndTime
+
processTime
;
double
nextopplanend
=
prevperationEndTime
+
processTime
;
List
<
ScheduleResultDetail
>
geneDetails1
=
machineCalculator
.
checkMachineStartTime
(
machine
,
(
int
)
(
processTime
*
currentOp
.
getQuantity
()),
List
<
ScheduleResultDetail
>
geneDetails1
=
machineCalculator
.
checkMachineStartTime
(
machine
,
(
int
)
(
processTime
*
currentOp
.
getQuantity
()),
...
@@ -1054,6 +1067,155 @@ if(MaterialRequirements==null||MaterialRequirements.size()==0)
...
@@ -1054,6 +1067,155 @@ if(MaterialRequirements==null||MaterialRequirements.size()==0)
// 单设备时使用原逻辑
// 单设备时使用原逻辑
return
(
int
)
calculateWithSingleMachine
(
details
,
leadCount
,
time1
);
return
(
int
)
calculateWithSingleMachine
(
details
,
leadCount
,
time1
);
}
}
/**
* 计算基于日历重叠约束的开始时间
* 整合了产能计算和连续性检查
* @param prevScheduleResults 前一工序的调度结果
* @param prevDetails 前一工序的详细调度信息
* @param currentOp 当前工序
* @param machine 当前工序的目标机器
* @param processTime 当前工序的处理时间
* @param prevtime 基础前序时间
* @return 修正后的开始时间
*/
private
int
calculateSSStartTimeWithCalendar
(
List
<
GAScheduleResult
>
prevScheduleResults
,
List
<
ScheduleResultDetail
>
prevDetails
,
Entry
currentOp
,
Machine
machine
,
double
processTime
,
int
prevtime
)
{
if
(
prevScheduleResults
==
null
||
prevScheduleResults
.
isEmpty
())
{
return
prevtime
;
}
//前工序生产数量和
double
qty
=
prevScheduleResults
.
stream
()
.
mapToDouble
(
GAScheduleResult:
:
getQuantity
)
.
sum
();
if
(
qty
<=
0
)
{
return
prevtime
;
}
double
prevTimePerUnit
=
prevScheduleResults
.
stream
()
.
mapToDouble
(
r
->
r
.
getOneTime
())
.
sum
()
/
qty
;
double
currentTimePerUnit
=
processTime
;
int
leadCount
=
(
int
)
calculateLeadProductionItems
(
qty
,
prevTimePerUnit
,
currentTimePerUnit
);
int
productionTimeForLeadCount
=
calculateProductionTimeWithCalendar
(
prevDetails
,
leadCount
,
machine
,
currentOp
);
int
prevEndTime
=
prevScheduleResults
.
stream
()
.
mapToInt
(
GAScheduleResult:
:
getEndTime
)
.
max
()
.
orElse
(
0
);
int
minEndTimeForContinuity
=
prevEndTime
-
(
int
)
((
qty
-
leadCount
)
*
prevTimePerUnit
);
int
adjustedStartTime
=
Math
.
max
(
productionTimeForLeadCount
,
minEndTimeForContinuity
);
adjustedStartTime
=
Math
.
max
(
adjustedStartTime
,
prevtime
);
int
totalProcessingTime
=
(
int
)
(
currentTimePerUnit
*
qty
);
int
continuityAdjustedTime
=
ensureContinuousProduction
(
machine
,
adjustedStartTime
,
totalProcessingTime
,
currentOp
);
return
Math
.
max
(
adjustedStartTime
,
continuityAdjustedTime
);
}
/**
* 确保下一工序能够连续生产(不中断)
* 检查并调整开始时间以保证生产连续性
*/
private
int
ensureContinuousProduction
(
Machine
machine
,
int
proposedStartTime
,
int
processingTime
,
Entry
currentOp
)
{
if
(
machine
==
null
||
processingTime
<=
0
)
{
return
proposedStartTime
;
}
LocalDateTime
proposedStart
=
baseTime
.
plusSeconds
(
proposedStartTime
);
LocalDateTime
proposedEnd
=
proposedStart
.
plusSeconds
(
processingTime
);
CopyOnWriteArrayList
<
TimeSegment
>
availableSegments
=
machineCalculator
.
getMachineAvailableTime
(
machine
,
proposedStartTime
,
processingTime
,
null
,
currentOp
.
getIsInterrupt
()
!=
1
);
if
(
availableSegments
==
null
||
availableSegments
.
isEmpty
())
{
return
proposedStartTime
;
}
for
(
TimeSegment
segment
:
availableSegments
)
{
LocalDateTime
segStart
=
segment
.
getStart
();
LocalDateTime
segEnd
=
segment
.
getEnd
();
if
(!
segStart
.
isAfter
(
proposedStart
)
&&
!
segEnd
.
isBefore
(
proposedEnd
))
{
return
proposedStartTime
;
}
if
(
segEnd
.
isAfter
(
proposedStart
)
&&
segEnd
.
isBefore
(
proposedEnd
))
{
int
newStartTime
=
(
int
)
ChronoUnit
.
SECONDS
.
between
(
baseTime
,
segEnd
);
return
ensureContinuousProduction
(
machine
,
newStartTime
,
processingTime
,
currentOp
);
}
if
(
segStart
.
isAfter
(
proposedStart
)
&&
segStart
.
isBefore
(
proposedEnd
))
{
int
newStartTime
=
(
int
)
ChronoUnit
.
SECONDS
.
between
(
baseTime
,
segStart
);
return
newStartTime
;
}
}
TimeSegment
firstSegment
=
availableSegments
.
get
(
0
);
int
segStart
=
(
int
)
ChronoUnit
.
SECONDS
.
between
(
baseTime
,
firstSegment
.
getStart
());
if
(
segStart
>
proposedStartTime
)
{
return
segStart
;
}
return
proposedStartTime
;
}
/**
* 根据日历计算生产指定数量所需时间(考虑效率系数)
*/
private
int
calculateProductionTimeWithCalendar
(
List
<
ScheduleResultDetail
>
details
,
int
leadCount
,
Machine
machine
,
Entry
currentOp
)
{
if
(
details
==
null
||
details
.
isEmpty
()
||
leadCount
<=
0
)
{
return
0
;
}
Set
<
Integer
>
timePoints
=
new
TreeSet
<>();
for
(
ScheduleResultDetail
detail
:
details
)
{
timePoints
.
add
(
detail
.
getStartTime
());
timePoints
.
add
(
detail
.
getEndTime
());
}
List
<
Integer
>
sortedTimes
=
new
ArrayList
<>(
timePoints
);
double
accumulatedQty
=
0.0
;
for
(
int
i
=
0
;
i
<
sortedTimes
.
size
()
-
1
;
i
++)
{
int
startTime
=
sortedTimes
.
get
(
i
);
int
endTime
=
sortedTimes
.
get
(
i
+
1
);
int
duration
=
endTime
-
startTime
;
if
(
duration
<=
0
)
continue
;
double
efficiency
=
details
.
stream
()
.
filter
(
d
->
d
.
getStartTime
()
<=
startTime
&&
d
.
getEndTime
()
>=
endTime
)
.
mapToDouble
(
d
->
d
.
getProcessingTime
()
>
0
?
1.0
/
d
.
getOneTime
()
:
0
)
.
sum
();
double
segmentQty
=
efficiency
*
duration
;
if
(
accumulatedQty
+
segmentQty
>=
leadCount
)
{
double
remainingQty
=
leadCount
-
accumulatedQty
;
int
requiredTime
=
(
int
)
Math
.
ceil
(
remainingQty
/
efficiency
);
return
startTime
+
requiredTime
;
}
accumulatedQty
+=
segmentQty
;
}
return
details
.
stream
()
.
mapToInt
(
ScheduleResultDetail:
:
getEndTime
)
.
max
()
.
orElse
(
0
);
}
/**
/**
* 多设备并行时计算满足提前量的最早时间
* 多设备并行时计算满足提前量的最早时间
*/
*/
...
...
src/main/java/com/aps/service/Algorithm/MaterialRequirementService.java
View file @
6430785e
...
@@ -1617,6 +1617,9 @@ if(headers1==null)
...
@@ -1617,6 +1617,9 @@ if(headers1==null)
redisUtils
.
del
(
sceneId
+
routingDiscreteParamCacheKey
);
redisUtils
.
del
(
sceneId
+
routingDiscreteParamCacheKey
);
GlobalCacheUtil
.
remove
(
sceneId
+
routingDiscreteParamCacheKey
);
GlobalCacheUtil
.
remove
(
sceneId
+
routingDiscreteParamCacheKey
);
redisUtils
.
del
(
sceneId
+
materialsCacheKey
);
GlobalCacheUtil
.
remove
(
sceneId
+
materialsCacheKey
);
}
}
...
...
src/main/java/com/aps/service/Algorithm/OrderSortService.java
View file @
6430785e
package
com
.
aps
.
service
.
Algorithm
;
package
com
.
aps
.
service
.
Algorithm
;
import
com.aps.common.util.ProductionDeepCopyUtil
;
import
com.aps.entity.Algorithm.OrderSortRule
;
import
com.aps.entity.Algorithm.OrderSortRule
;
import
com.aps.entity.basic.Order
;
import
com.aps.entity.basic.Order
;
import
lombok.extern.slf4j.Slf4j
;
import
lombok.extern.slf4j.Slf4j
;
...
@@ -14,6 +15,7 @@ import java.time.OffsetDateTime;
...
@@ -14,6 +15,7 @@ import java.time.OffsetDateTime;
import
java.util.*
;
import
java.util.*
;
import
java.util.function.Function
;
import
java.util.function.Function
;
import
java.util.stream.Collectors
;
import
java.util.stream.Collectors
;
import
java.util.stream.IntStream
;
/**
/**
* 增强版订单排序服务(最终版:严格匹配条件数格式)
* 增强版订单排序服务(最终版:严格匹配条件数格式)
...
@@ -90,6 +92,457 @@ public class OrderSortService {
...
@@ -90,6 +92,457 @@ public class OrderSortService {
orders
.
addAll
(
sortedOrders
);
orders
.
addAll
(
sortedOrders
);
}
}
/**
* 计算两个订单之间的换线成本
*/
private
double
calculateChangeoverCost
(
Order
order1
,
Order
order2
)
{
if
(
order1
==
null
||
order2
==
null
)
{
return
Double
.
MAX_VALUE
;
}
double
cost
=
0.0
;
// 1. 工艺路线不同,增加换线成本
if
(!
Objects
.
equals
(
order1
.
getRoutingId
(),
order2
.
getRoutingId
()))
{
cost
+=
100.0
;
}
// 2. 物料系列不同,增加换线成本
if
(!
Objects
.
equals
(
order1
.
getSerie
(),
order2
.
getSerie
()))
{
cost
+=
50.0
;
}
// 3. 物料代码不同,增加换线成本
if
(!
Objects
.
equals
(
order1
.
getMaterialCode
(),
order2
.
getMaterialCode
()))
{
cost
+=
25.0
;
}
return
cost
;
}
/**
* 基于最小化换线成本的排序
*/
private
List
<
Order
>
sortByMinimizeChangeover
(
List
<
Order
>
orders
)
{
if
(
CollectionUtils
.
isEmpty
(
orders
))
{
return
new
ArrayList
<>();
}
// 定义阈值:当订单数量小于等于这个值时,遍历所有起始点
int
threshold
=
10
;
if
(
orders
.
size
()
<=
threshold
)
{
// 订单数量较少,遍历所有起始点
return
sortByMinimizeChangeoverWithAllStarts
(
orders
);
}
else
{
// 订单数量较多,使用局部搜索
return
sortByMinimizeChangeoverWithLocalSearch
(
orders
);
}
}
/**
* 基于遍历所有起始点的最小化换线排序
*/
private
List
<
Order
>
sortByMinimizeChangeoverWithAllStarts
(
List
<
Order
>
orders
)
{
if
(
CollectionUtils
.
isEmpty
(
orders
))
{
return
new
ArrayList
<>();
}
List
<
Map
.
Entry
<
List
<
Order
>,
Double
>>
results
=
IntStream
.
range
(
0
,
orders
.
size
())
.
parallel
()
.
mapToObj
(
i
->
{
List
<
Order
>
sortedOrders
=
generateInitialSolution
(
ProductionDeepCopyUtil
.
deepCopyList
(
orders
)
,
i
);
// 计算总换线成本
double
totalCost
=
calculateTotalChangeoverCost
(
sortedOrders
);
return
new
AbstractMap
.
SimpleEntry
<>(
sortedOrders
,
totalCost
);
})
.
collect
(
Collectors
.
toList
());
List
<
Order
>
bestSortedOrders
=
null
;
double
minTotalCost
=
Double
.
MAX_VALUE
;
for
(
Map
.
Entry
<
List
<
Order
>,
Double
>
entry
:
results
)
{
if
(
entry
.
getValue
()
<
minTotalCost
)
{
minTotalCost
=
entry
.
getValue
();
bestSortedOrders
=
entry
.
getKey
();
}
}
return
bestSortedOrders
;
}
/**
* 基于局部搜索的最小化换线排序
*/
private
List
<
Order
>
sortByMinimizeChangeoverWithLocalSearch
(
List
<
Order
>
orders
)
{
if
(
CollectionUtils
.
isEmpty
(
orders
))
{
return
new
ArrayList
<>();
}
// 1. 生成多个初始解,选择最优的一个
// 尝试多个随机起始点
Set
<
Integer
>
selectedIndices
=
new
HashSet
<>();
Random
random
=
new
Random
();
int
maxAttempts
=
Math
.
min
(
5
,
orders
.
size
());
while
(
selectedIndices
.
size
()
<
maxAttempts
)
{
int
randomIndex
=
random
.
nextInt
(
orders
.
size
());
selectedIndices
.
add
(
randomIndex
);
}
List
<
Map
.
Entry
<
List
<
Order
>,
Double
>>
initialSolutions
=
selectedIndices
.
parallelStream
()
.
map
(
index
->
{
List
<
Order
>
initialSolution
=
generateInitialSolution
(
ProductionDeepCopyUtil
.
deepCopyList
(
orders
),
index
);
double
initialCost
=
calculateTotalChangeoverCost
(
initialSolution
);
return
new
AbstractMap
.
SimpleEntry
<>(
initialSolution
,
initialCost
);
})
.
collect
(
Collectors
.
toList
());
// 选择最优初始解
List
<
Order
>
bestInitialSolution
=
null
;
double
bestInitialCost
=
Double
.
MAX_VALUE
;
for
(
Map
.
Entry
<
List
<
Order
>,
Double
>
entry
:
initialSolutions
)
{
if
(
entry
.
getValue
()
<
bestInitialCost
)
{
bestInitialCost
=
entry
.
getValue
();
bestInitialSolution
=
entry
.
getKey
();
}
}
List
<
Order
>
currentSolution
=
bestInitialSolution
!=
null
?
bestInitialSolution
:
generateInitialSolution
(
orders
,
0
);
double
currentCost
=
bestInitialCost
;
// 2. 局部搜索(允许所有相邻订单交换)
boolean
improved
;
int
maxIterations
=
50
;
int
iteration
=
0
;
// do {
// improved = false;
// List<Order> bestNeighbor = new ArrayList<>(currentSolution);
// double bestNeighborCost = currentCost;
//
// // 遍历所有相邻订单对,尝试交换
// for (int i = 0; i < currentSolution.size() - 1; i++) {
// // 交换相邻订单
// List<Order> neighbor = new ArrayList<>(currentSolution);
// Collections.swap(neighbor, i, i + 1);
//
//
// double neighborCost = calculateTotalChangeoverCost2(neighbor);
//
// // 如果交换后成本更低,更新最佳邻居
// if (neighborCost < bestNeighborCost) {
// bestNeighbor = neighbor;
// bestNeighborCost = neighborCost;
// improved = true;
// }
// }
//
// // 如果找到更优解,更新当前解
// if (improved) {
// currentSolution = bestNeighbor;
// // assignChangeoverPriority(currentSolution);
// currentCost = bestNeighborCost;
// }
//
// iteration++;
// } while (improved && iteration < maxIterations);
do
{
improved
=
false
;
List
<
Order
>
bestNeighbor
=
new
ArrayList
<>(
currentSolution
);
double
bestNeighborCost
=
currentCost
;
// 并行处理邻域搜索
List
<
Order
>
finalCurrentSolution
=
currentSolution
;
List
<
Map
.
Entry
<
List
<
Order
>,
Double
>>
neighbors
=
IntStream
.
range
(
0
,
currentSolution
.
size
()
-
1
)
.
parallel
()
.
mapToObj
(
i
->
{
// 交换相邻订单
List
<
Order
>
neighbor
=
new
ArrayList
<>(
finalCurrentSolution
);
Collections
.
swap
(
neighbor
,
i
,
i
+
1
);
double
neighborCost
=
calculateTotalChangeoverCost2
(
neighbor
);
return
new
AbstractMap
.
SimpleEntry
<>(
neighbor
,
neighborCost
);
})
.
collect
(
Collectors
.
toList
());
// 找到最佳邻居
for
(
Map
.
Entry
<
List
<
Order
>,
Double
>
entry
:
neighbors
)
{
if
(
entry
.
getValue
()
<
bestNeighborCost
)
{
bestNeighbor
=
entry
.
getKey
();
bestNeighborCost
=
entry
.
getValue
();
improved
=
true
;
}
}
// 如果找到更优解,更新当前解
if
(
improved
)
{
currentSolution
=
bestNeighbor
;
currentCost
=
bestNeighborCost
;
}
iteration
++;
}
while
(
improved
&&
iteration
<
maxIterations
);
return
currentSolution
;
}
/**
* 计算订单序列的总换线成本
*/
/**
* 计算订单序列的总换线成本(考虑ChangeoverPriority分组)
*/
private
double
calculateTotalChangeoverCost2
(
List
<
Order
>
orders
)
{
if
(
CollectionUtils
.
isEmpty
(
orders
)
||
orders
.
size
()
<=
1
)
{
return
0.0
;
}
double
totalCost
=
0.0
;
// 首先为订单分配ChangeoverPriority
assignChangeoverPriority
(
orders
);
totalCost
=
calculateTotalChangeoverCost
(
orders
);
return
totalCost
;
}
/**
* 生成初始解(使用贪心算法)
*/
private
List
<
Order
>
generateInitialSolution
(
List
<
Order
>
orders
,
int
i
)
{
if
(
CollectionUtils
.
isEmpty
(
orders
))
{
return
new
ArrayList
<>();
}
List
<
Order
>
sortedOrders
=
new
ArrayList
<>();
List
<
Order
>
remainingOrders
=
new
ArrayList
<>(
orders
);
// 选择第一个订单作为起始点
Order
currentOrder
=
remainingOrders
.
remove
(
i
);
currentOrder
.
setChangeoverPriority
(
1
);
sortedOrders
.
add
(
currentOrder
);
int
priority
=
2
;
// 每次选择与当前最后一个订单换线成本最低的订单
while
(!
remainingOrders
.
isEmpty
())
{
double
minCost
=
Double
.
MAX_VALUE
;
List
<
Order
>
bestOrders
=
new
ArrayList
<>();
// 找到换线成本最低的所有订单
for
(
Order
order
:
remainingOrders
)
{
double
cost
=
calculateChangeoverCost
(
currentOrder
,
order
);
if
(
cost
<
minCost
)
{
minCost
=
cost
;
order
.
setChangeoverCost
(
cost
);
if
(
minCost
==
0
){
order
.
setChangeoverPriority
(
currentOrder
.
getChangeoverPriority
());
}
else
{
order
.
setChangeoverPriority
(
priority
);
}
bestOrders
.
clear
();
bestOrders
.
add
(
order
);
}
else
if
(
cost
==
minCost
)
{
order
.
setChangeoverCost
(
cost
);
if
(
minCost
==
0
){
order
.
setChangeoverPriority
(
currentOrder
.
getChangeoverPriority
());
}
else
{
order
.
setChangeoverPriority
(
priority
);
}
bestOrders
.
add
(
order
);
}
}
// 如果有多个成本相同的订单,将它们作为一组添加
if
(!
bestOrders
.
isEmpty
())
{
sortedOrders
.
addAll
(
bestOrders
);
remainingOrders
.
removeAll
(
bestOrders
);
// 更新当前订单为最后添加的订单
currentOrder
=
bestOrders
.
get
(
bestOrders
.
size
()
-
1
);
if
(
minCost
!=
0
)
{
priority
++;
}
}
else
{
// 以防万一,添加剩余的第一个订单
Order
nextOrder
=
remainingOrders
.
remove
(
0
);
sortedOrders
.
add
(
nextOrder
);
currentOrder
=
nextOrder
;
if
(
minCost
!=
0
)
{
priority
++;
}
}
}
return
sortedOrders
;
}
/**
* 为排序后的订单分配ChangeoverPriority
*/
private
void
assignChangeoverPriority
(
List
<
Order
>
orders
)
{
if
(
CollectionUtils
.
isEmpty
(
orders
))
{
return
;
}
int
priority
=
1
;
orders
.
get
(
0
).
setChangeoverPriority
(
priority
);
for
(
int
i
=
1
;
i
<
orders
.
size
();
i
++)
{
double
cost
=
calculateChangeoverCost
(
orders
.
get
(
i
-
1
),
orders
.
get
(
i
));
if
(
cost
>
0
)
{
//成本为0 ,则在同一级
priority
++;
orders
.
get
(
i
).
setChangeoverCost
(
cost
);
}
else
{
orders
.
get
(
i
).
setChangeoverCost
(
orders
.
get
(
i
-
1
).
getChangeoverCost
());
}
orders
.
get
(
i
).
setChangeoverPriority
(
priority
);
}
}
/**
* 计算订单序列的总换线成本
*/
private
double
calculateTotalChangeoverCost
(
List
<
Order
>
orders
)
{
if
(
CollectionUtils
.
isEmpty
(
orders
)
||
orders
.
size
()
<=
1
)
{
return
0.0
;
}
double
totalCost
=
0.0
;
// 001>002 2 cost 75
// 001>003 2 cost 75
//有一点问题,相同ChangeoverPriority 直接换型应该是0,002>003, 然而对象中存的是,
// 001>002,001>003 ,所以相同ChangeoverPriority 不加成本了
// 只计算不同优先级组之间的换线成本
for
(
int
i
=
0
;
i
<
orders
.
size
()
-
1
;
i
++)
{
Order
currentOrder
=
orders
.
get
(
i
);
Order
nextOrder
=
orders
.
get
(
i
+
1
);
// 只有当两个订单的ChangeoverPriority不同时,才计算换线成本
if
(
currentOrder
.
getChangeoverPriority
()
!=
nextOrder
.
getChangeoverPriority
())
{
totalCost
+=
orders
.
get
(
i
+
1
).
getChangeoverCost
();
}
}
return
totalCost
;
}
/**
* 处理排序条件,支持在任意位置应用最小化换线策略
*/
private
void
processConditionsWithMinimizeChangeover
(
List
<
Order
>
orders
,
List
<
OrderSortRule
.
SortCondition
>
conditions
,
int
conditionIndex
,
Map
<
Integer
,
List
<
Integer
>>
priorityPaths
)
{
// 递归终止:处理完所有条件后停止
if
(
conditionIndex
>=
conditions
.
size
()
||
CollectionUtils
.
isEmpty
(
orders
))
{
return
;
}
OrderSortRule
.
SortCondition
currentCondition
=
conditions
.
get
(
conditionIndex
);
// 检查是否是最小化换线策略
if
(
"minimize_changeover"
.
equals
(
currentCondition
.
getFieldName
()))
{
// 应用最小化换线策略
List
<
Order
>
optimizedOrders
=
sortByMinimizeChangeover
(
orders
);
// 为最小化换线后的订单分配层级序号
// 相同成本的订单分配相同的序号
for
(
Order
order
:
optimizedOrders
)
{
priorityPaths
.
get
(
order
.
getId
()).
add
(
order
.
getChangeoverPriority
());
}
Map
<
Integer
,
List
<
Order
>>
priorityGroups
=
new
HashMap
<>();
for
(
Order
order
:
optimizedOrders
)
{
int
priority
=
order
.
getChangeoverPriority
();
priorityGroups
.
computeIfAbsent
(
priority
,
k
->
new
ArrayList
<>()).
add
(
order
);
}
// 按优先级分组顺序处理
List
<
Integer
>
sortedPriorities
=
priorityGroups
.
keySet
().
stream
()
.
sorted
()
.
collect
(
Collectors
.
toList
());
for
(
Integer
priority
:
sortedPriorities
)
{
List
<
Order
>
groupOrders
=
priorityGroups
.
get
(
priority
);
// 递归处理下一个条件
processConditionsWithMinimizeChangeover
(
groupOrders
,
conditions
,
conditionIndex
+
1
,
priorityPaths
);
}
// 递归处理下一个条件
// processConditionsWithMinimizeChangeover(optimizedOrders, conditions, conditionIndex + 1, priorityPaths);
}
else
{
// 处理普通排序条件
String
fieldName
=
currentCondition
.
getFieldName
();
Function
<
Order
,
?>
keyExtractor
=
getFieldExtractor
(
fieldName
);
if
(
keyExtractor
==
null
)
{
log
.
warn
(
"跳过无效排序字段:{}"
,
currentCondition
.
getFieldName
());
processConditionsWithMinimizeChangeover
(
orders
,
conditions
,
conditionIndex
+
1
,
priorityPaths
);
return
;
}
// 1. 按当前条件分组 处理null键
Map
<
Object
,
List
<
Order
>>
groups
=
new
HashMap
<>();
orders
.
forEach
(
order
->
{
Object
key
=
keyExtractor
.
apply
(
order
);
groups
.
computeIfAbsent
(
key
,
k
->
new
ArrayList
<>()).
add
(
order
);
});
// 2. 对分组键排序(关键:按条件配置的方向排序)
List
<
Object
>
sortedKeys
=
getSortedKeys
(
groups
,
currentCondition
);
// 3. 为每个分组分配层级序号(从1开始)
for
(
int
groupIndex
=
0
;
groupIndex
<
sortedKeys
.
size
();
groupIndex
++)
{
Object
key
=
sortedKeys
.
get
(
groupIndex
);
List
<
Order
>
groupOrders
=
groups
.
get
(
key
);
// 分配当前层级序号(1、2、3...)
int
levelNumber
=
groupIndex
+
1
;
groupOrders
.
forEach
(
order
->
priorityPaths
.
get
(
order
.
getId
()).
add
(
levelNumber
));
// 递归处理下一级条件
processConditionsWithMinimizeChangeover
(
groupOrders
,
conditions
,
conditionIndex
+
1
,
priorityPaths
);
}
}
}
private
List
<
Order
>
sortWithConfiguredStrategy
(
List
<
Order
>
orders
,
OrderSortRule
rule
)
{
// if (rule.getConditions().size()==1&&rule.getConditions().get(0).getFieldName().equals("minimize_changeover")) {
// return sortByMinimizeChangeover(orders);
// }
List
<
Order
>
currentOrders
=
new
ArrayList
<>(
orders
);
List
<
OrderSortRule
.
SortCondition
>
conditions
=
rule
.
getConditions
().
stream
()
.
sorted
(
Comparator
.
comparingInt
(
OrderSortRule
.
SortCondition
::
getSequence
))
.
collect
(
Collectors
.
toList
());
// 为每个订单初始化优先级路径
Map
<
Integer
,
List
<
Integer
>>
priorityPaths
=
new
HashMap
<>();
currentOrders
.
forEach
(
order
->
priorityPaths
.
put
(
order
.
getId
(),
new
ArrayList
<>()));
// 处理排序条件
processConditionsWithMinimizeChangeover
(
currentOrders
,
conditions
,
0
,
priorityPaths
);
// 将路径转换为最终格式
convertPriorityPathsToNumeric
(
currentOrders
,
priorityPaths
);
// 按优先级升序排序(数值越小越优先)
return
currentOrders
.
stream
()
.
sorted
(
Comparator
.
comparingDouble
(
Order:
:
getActualPriority
))
.
collect
(
Collectors
.
toList
());
}
/**
/**
* 根据数据库配置的规则对订单排序
* 根据数据库配置的规则对订单排序
...
@@ -136,14 +589,24 @@ public class OrderSortService {
...
@@ -136,14 +589,24 @@ public class OrderSortService {
return
new
ArrayList
<>(
orders
);
return
new
ArrayList
<>(
orders
);
}
}
boolean
hasMinimizeChangeover
=
rule
.
getConditions
().
stream
()
.
anyMatch
(
condition
->
"minimize_changeover"
.
equals
(
condition
.
getFieldName
()));
if
(
hasMinimizeChangeover
)
{
// 仅使用最小化换线策略
return
sortWithConfiguredStrategy
(
orders
,
rule
);
}
else
{
// 按条件顺序排序(sequence越小越先执行)
// 按条件顺序排序(sequence越小越先执行)
List
<
OrderSortRule
.
SortCondition
>
sortedConditions
=
rule
.
getConditions
().
stream
()
List
<
OrderSortRule
.
SortCondition
>
sortedConditions
=
rule
.
getConditions
().
stream
()
.
sorted
(
Comparator
.
comparingInt
(
OrderSortRule
.
SortCondition
::
getSequence
))
.
sorted
(
Comparator
.
comparingInt
(
OrderSortRule
.
SortCondition
::
getSequence
))
.
collect
(
Collectors
.
toList
());
.
collect
(
Collectors
.
toList
());
// 为每个订单初始化优先级路径
// 为每个订单初始化优先级路径
Map
<
Ord
er
,
List
<
Integer
>>
priorityPaths
=
new
HashMap
<>();
Map
<
Integ
er
,
List
<
Integer
>>
priorityPaths
=
new
HashMap
<>();
orders
.
forEach
(
order
->
priorityPaths
.
put
(
order
,
new
ArrayList
<>()));
orders
.
forEach
(
order
->
priorityPaths
.
put
(
order
.
getId
()
,
new
ArrayList
<>()));
// 递归分配层级化优先级(核心逻辑)
// 递归分配层级化优先级(核心逻辑)
assignHierarchicalPriority
(
orders
,
sortedConditions
,
0
,
priorityPaths
);
assignHierarchicalPriority
(
orders
,
sortedConditions
,
0
,
priorityPaths
);
...
@@ -156,6 +619,7 @@ public class OrderSortService {
...
@@ -156,6 +619,7 @@ public class OrderSortService {
.
sorted
(
Comparator
.
comparingDouble
(
Order:
:
getActualPriority
))
.
sorted
(
Comparator
.
comparingDouble
(
Order:
:
getActualPriority
))
.
collect
(
Collectors
.
toList
());
.
collect
(
Collectors
.
toList
());
}
}
}
/**
/**
* 递归分配层级化优先级路径
* 递归分配层级化优先级路径
...
@@ -163,7 +627,7 @@ public class OrderSortService {
...
@@ -163,7 +627,7 @@ public class OrderSortService {
private
void
assignHierarchicalPriority
(
List
<
Order
>
orders
,
private
void
assignHierarchicalPriority
(
List
<
Order
>
orders
,
List
<
OrderSortRule
.
SortCondition
>
conditions
,
List
<
OrderSortRule
.
SortCondition
>
conditions
,
int
conditionIndex
,
int
conditionIndex
,
Map
<
Ord
er
,
List
<
Integer
>>
priorityPaths
)
{
Map
<
Integ
er
,
List
<
Integer
>>
priorityPaths
)
{
// 递归终止:处理完所有条件后停止,不再添加额外序号
// 递归终止:处理完所有条件后停止,不再添加额外序号
if
(
conditionIndex
>=
conditions
.
size
()
||
CollectionUtils
.
isEmpty
(
orders
))
{
if
(
conditionIndex
>=
conditions
.
size
()
||
CollectionUtils
.
isEmpty
(
orders
))
{
return
;
return
;
...
@@ -196,7 +660,7 @@ public class OrderSortService {
...
@@ -196,7 +660,7 @@ public class OrderSortService {
// 分配当前层级序号(1、2、3...)
// 分配当前层级序号(1、2、3...)
int
levelNumber
=
groupIndex
+
1
;
int
levelNumber
=
groupIndex
+
1
;
groupOrders
.
forEach
(
order
->
priorityPaths
.
get
(
order
).
add
(
levelNumber
));
groupOrders
.
forEach
(
order
->
priorityPaths
.
get
(
order
.
getId
()
).
add
(
levelNumber
));
// 递归处理下一级条件
// 递归处理下一级条件
assignHierarchicalPriority
(
groupOrders
,
conditions
,
conditionIndex
+
1
,
priorityPaths
);
assignHierarchicalPriority
(
groupOrders
,
conditions
,
conditionIndex
+
1
,
priorityPaths
);
...
@@ -208,9 +672,9 @@ public class OrderSortService {
...
@@ -208,9 +672,9 @@ public class OrderSortService {
* 3个条件 → [1,1,1] → 1.11
* 3个条件 → [1,1,1] → 1.11
* 4个条件 → [1,1,1,1] → 1.111
* 4个条件 → [1,1,1,1] → 1.111
*/
*/
private
void
convertPriorityPathsToNumeric
(
List
<
Order
>
orders
,
Map
<
Ord
er
,
List
<
Integer
>>
priorityPaths
)
{
private
void
convertPriorityPathsToNumeric
(
List
<
Order
>
orders
,
Map
<
Integ
er
,
List
<
Integer
>>
priorityPaths
)
{
for
(
Order
order
:
orders
)
{
for
(
Order
order
:
orders
)
{
List
<
Integer
>
path
=
priorityPaths
.
get
(
order
);
List
<
Integer
>
path
=
priorityPaths
.
get
(
order
.
getId
()
);
if
(
CollectionUtils
.
isEmpty
(
path
))
{
if
(
CollectionUtils
.
isEmpty
(
path
))
{
order
.
setActualPriority
(
0.0
);
order
.
setActualPriority
(
0.0
);
continue
;
continue
;
...
...
src/main/java/com/aps/service/plan/PlanResultService.java
View file @
6430785e
...
@@ -1983,6 +1983,16 @@ private GlobalParam InitGlobalParam()
...
@@ -1983,6 +1983,16 @@ private GlobalParam InitGlobalParam()
conditions
.
add
(
condition
);
conditions
.
add
(
condition
);
files
.
add
(
"orderCode"
);
files
.
add
(
"orderCode"
);
i
++;
i
++;
}
else
if
(
strategy
.
getName
().
equals
(
"minimize_tool_changeovers"
))
{
// 最小化换线策略需要特殊处理,这里只做标记
// 实际排序逻辑将在orderSortService中处理
OrderSortRule
.
SortCondition
condition
=
new
OrderSortRule
.
SortCondition
();
condition
.
setSequence
(
i
);
condition
.
setFieldName
(
"minimize_changeover"
);
condition
.
setReverse
(
false
);
conditions
.
add
(
condition
);
files
.
add
(
"minimize_changeover"
);
i
++;
}
}
}
}
...
...
src/test/java/com/aps/demo/OrderSortServiceTest.java
View file @
6430785e
...
@@ -13,9 +13,7 @@ import java.time.LocalDate;
...
@@ -13,9 +13,7 @@ import java.time.LocalDate;
import
java.time.LocalDateTime
;
import
java.time.LocalDateTime
;
import
java.time.OffsetDateTime
;
import
java.time.OffsetDateTime
;
import
java.time.format.DateTimeFormatter
;
import
java.time.format.DateTimeFormatter
;
import
java.util.ArrayList
;
import
java.util.*
;
import
java.util.Comparator
;
import
java.util.List
;
import
java.util.stream.Collectors
;
import
java.util.stream.Collectors
;
/**
/**
...
@@ -45,18 +43,68 @@ class OrderSortServiceTest {
...
@@ -45,18 +43,68 @@ class OrderSortServiceTest {
// 创建多条件排序规则:先按dueDate,再按priority
// 创建多条件排序规则:先按dueDate,再按priority
OrderSortRule
rule
=
createMultiConditionRule
();
OrderSortRule
rule
=
createMultiConditionRule
();
List
<
Order
>
orders
=
new
ArrayList
<>();
Order
order
=
new
Order
();
order
.
setId
(
1
);
order
.
setOrderId
(
"001"
);
order
.
setRoutingId
(
1
);
order
.
setMaterialCode
(
"M001"
);
order
.
setSerie
(
"S001"
);
orders
.
add
(
order
);
Order
order2
=
new
Order
();
order2
.
setId
(
2
);
order2
.
setOrderId
(
"002"
);
order2
.
setRoutingId
(
1
);
order2
.
setMaterialCode
(
"M002"
);
order2
.
setSerie
(
"S001"
);
orders
.
add
(
order2
);
Order
order3
=
new
Order
();
order3
.
setId
(
3
);
order3
.
setOrderId
(
"003"
);
order3
.
setRoutingId
(
1
);
order3
.
setMaterialCode
(
"M002"
);
order3
.
setSerie
(
"S001"
);
orders
.
add
(
order3
);
Order
order4
=
new
Order
();
order4
.
setId
(
4
);
order4
.
setOrderId
(
"004"
);
order4
.
setRoutingId
(
2
);
order4
.
setMaterialCode
(
"M001"
);
order4
.
setSerie
(
"S002"
);
orders
.
add
(
order4
);
Order
order5
=
new
Order
();
order5
.
setId
(
5
);
order5
.
setOrderId
(
"005"
);
order5
.
setRoutingId
(
2
);
order5
.
setMaterialCode
(
"M002"
);
order5
.
setSerie
(
"S003"
);
orders
.
add
(
order5
);
Order
order6
=
new
Order
();
order6
.
setId
(
6
);
order6
.
setOrderId
(
"006"
);
order6
.
setRoutingId
(
2
);
order6
.
setMaterialCode
(
"M002"
);
order6
.
setSerie
(
"S003"
);
orders
.
add
(
order6
);
// 执行测试
// 执行测试
orderSortService
.
assignPriority
(
testOrders
,
rule
);
orderSortService
.
assignPriorityValues
(
orders
,
rule
);
// 按照优先级排序
// 按照优先级排序
testO
rders
.
sort
(
o
rders
.
sort
(
Comparator
.
comparing
(
Order:
:
getActualPriority
).
reversed
()
Comparator
.
comparing
(
Order:
:
getActualPriority
).
reversed
()
);
);
// 验证多级优先级分配
// 验证多级优先级分配
printOrderPriorities
(
testO
rders
);
// 调试输出
printOrderPriorities
(
o
rders
);
// 调试输出
}
}
...
@@ -102,16 +150,17 @@ class OrderSortServiceTest {
...
@@ -102,16 +150,17 @@ class OrderSortServiceTest {
// 条件2:按到期日
// 条件2:按到期日
OrderSortRule
.
SortCondition
condition2
=
new
OrderSortRule
.
SortCondition
();
OrderSortRule
.
SortCondition
condition2
=
new
OrderSortRule
.
SortCondition
();
condition2
.
setSequence
(
2
);
condition2
.
setSequence
(
2
);
condition2
.
setFieldName
(
"
dueDate
"
);
condition2
.
setFieldName
(
"
minimize_changeover
"
);
condition2
.
setReverse
(
false
);
// 早到期在前
condition2
.
setReverse
(
false
);
// 早到期在前
conditions
.
add
(
condition2
);
conditions
.
add
(
condition2
);
// 条件3:按优先级
// 条件3:按优先级
OrderSortRule
.
SortCondition
condition3
=
new
OrderSortRule
.
SortCondition
();
OrderSortRule
.
SortCondition
condition3
=
new
OrderSortRule
.
SortCondition
();
condition3
.
setSequence
(
3
);
condition3
.
setSequence
(
3
);
condition3
.
setFieldName
(
"
priority
"
);
condition3
.
setFieldName
(
"
materialCode
"
);
condition3
.
setReverse
(
true
);
// 高优先级在前
condition3
.
setReverse
(
true
);
// 高优先级在前
conditions
.
add
(
condition3
);
// conditions.add(condition3);
rule
.
setConditions
(
conditions
);
rule
.
setConditions
(
conditions
);
return
rule
;
return
rule
;
...
...
src/test/java/com/aps/demo/PlanResultServiceTest.java
View file @
6430785e
...
@@ -42,7 +42,7 @@ public class PlanResultServiceTest {
...
@@ -42,7 +42,7 @@ public class PlanResultServiceTest {
// planResultService.execute2("5475E00B844847ACB6DC20227967BA2F");
// planResultService.execute2("5475E00B844847ACB6DC20227967BA2F");
// planResultService.execute2("00E0C5D3E4AD4F36B56C39395906618D");
// planResultService.execute2("00E0C5D3E4AD4F36B56C39395906618D");
planResultService
.
execute2
(
"
31F0FF4DFAD844BD9C72EDEEF3430A1F
"
);
planResultService
.
execute2
(
"
92BB773E1E2447C99D8176C991D5C9D2
"
);
// planResultService.execute2("265F24B6DF3C40E4B17D193B0CC8AAF2");
// planResultService.execute2("265F24B6DF3C40E4B17D193B0CC8AAF2");
// LocalDateTime t= LocalDateTime.of(2026, 02, 14, 1, 25, 52);
// LocalDateTime t= LocalDateTime.of(2026, 02, 14, 1, 25, 52);
// List<Integer> opids=new ArrayList<>();//BCA6FA43FFA444D3952CF8F6E1EA291B
// List<Integer> opids=new ArrayList<>();//BCA6FA43FFA444D3952CF8F6E1EA291B
...
...
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