Skip to content
Toggle navigation
Projects
Groups
Snippets
Help
yangxiujun
/
paidan_demo
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Settings
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit 47ca589f
authored
Jul 07, 2023
by
张晓
Browse files
Options
Browse Files
Download
Plain Diff
初步打通
2 parents
0a9dac80
683fb475
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
384 additions
and
184 deletions
project-dispatch/src/main/java/com/dituhui/pea/dispatch/DispatchServiceApplication.java
project-dispatch/src/main/java/com/dituhui/pea/dispatch/pojo/Customer.java
project-dispatch/src/main/java/com/dituhui/pea/dispatch/scheduler/BatchScheduler.java
project-dispatch/src/main/java/com/dituhui/pea/dispatch/service/impl/BatchServiceImpl.java
project-dispatch/src/main/java/com/dituhui/pea/dispatch/service/impl/ExtractServiceImpl.java
project-dispatch/src/main/java/com/dituhui/pea/dispatch/service/impl/SolveServiceImpl.java
project-dispatch/src/main/resources/application-dev.yaml
project-dispatch/src/main/resources/application.yaml
project-dispatch/src/test/java/com/dituhui/pea/dispatch/SolveServiceTest.java
project-dispatch/src/main/java/com/dituhui/pea/dispatch/DispatchServiceApplication.java
View file @
47ca589
...
@@ -18,11 +18,13 @@ package com.dituhui.pea.dispatch;
...
@@ -18,11 +18,13 @@ package com.dituhui.pea.dispatch;
import
org.springframework.boot.SpringApplication
;
import
org.springframework.boot.SpringApplication
;
import
org.springframework.boot.autoconfigure.SpringBootApplication
;
import
org.springframework.boot.autoconfigure.SpringBootApplication
;
import
org.springframework.scheduling.annotation.EnableScheduling
;
/**
/**
* @author gpzhang
* @author gpzhang
*/
*/
@SpringBootApplication
@SpringBootApplication
@EnableScheduling
public
class
DispatchServiceApplication
{
public
class
DispatchServiceApplication
{
public
static
void
main
(
String
[]
args
)
{
public
static
void
main
(
String
[]
args
)
{
...
...
project-dispatch/src/main/java/com/dituhui/pea/dispatch/pojo/Customer.java
View file @
47ca589
...
@@ -11,162 +11,173 @@ import lombok.Setter;
...
@@ -11,162 +11,173 @@ import lombok.Setter;
/**
/**
* 订单
* 订单
*
* @author gpzhang
*
*
* @author gpzhang
*/
*/
@Setter
@Setter
@Getter
@Getter
@PlanningEntity
@PlanningEntity
public
class
Customer
{
public
class
Customer
{
private
long
id
;
private
long
id
;
private
String
code
;
private
String
code
;
@JsonIgnore
@JsonIgnore
private
Location
location
;
private
Location
location
;
// 时间窗 分钟
// 时间窗 分钟
private
int
startTime
;
private
int
startTime
;
private
int
endTime
;
private
int
endTime
;
private
int
serviceDuration
;
private
int
serviceDuration
;
// 需要技能
// 需要技能
private
String
requiredSkill
;
private
String
requiredSkill
;
// Shadow variables
// Shadow variables
@JsonIgnore
@JsonIgnore
private
Technician
technician
;
private
Technician
technician
;
@JsonIgnore
@JsonIgnore
private
Customer
previousCustomer
;
private
Customer
previousCustomer
;
@JsonIgnore
@JsonIgnore
private
Customer
nextCustomer
;
private
Customer
nextCustomer
;
// 到达时间
// 到达时间
private
Integer
arrivalTime
;
private
Integer
arrivalTime
;
// 离开时间
// 离开时间
// private Integer departureTime;
// private Integer departureTime;
public
Customer
()
{
public
Customer
()
{
}
}
public
Customer
(
long
id
,
String
code
,
Location
location
,
int
startTime
,
int
endTime
,
String
requiredSkill
,
public
Customer
(
long
id
,
String
code
,
Location
location
,
int
startTime
,
int
endTime
,
String
requiredSkill
,
int
serviceDuration
)
{
int
serviceDuration
)
{
this
.
id
=
id
;
this
.
id
=
id
;
this
.
code
=
code
;
this
.
code
=
code
;
this
.
location
=
location
;
this
.
location
=
location
;
this
.
startTime
=
startTime
;
this
.
startTime
=
startTime
;
this
.
endTime
=
endTime
;
this
.
endTime
=
endTime
;
this
.
requiredSkill
=
requiredSkill
;
this
.
requiredSkill
=
requiredSkill
;
this
.
serviceDuration
=
serviceDuration
;
this
.
serviceDuration
=
serviceDuration
;
}
}
@InverseRelationShadowVariable
(
sourceVariableName
=
"customerList"
)
@InverseRelationShadowVariable
(
sourceVariableName
=
"customerList"
)
public
Technician
getTechnician
()
{
public
Technician
getTechnician
()
{
return
technician
;
return
technician
;
}
}
@org
.
optaplanner
.
core
.
api
.
domain
.
variable
.
PreviousElementShadowVariable
(
sourceVariableName
=
"customerList"
)
@org
.
optaplanner
.
core
.
api
.
domain
.
variable
.
PreviousElementShadowVariable
(
sourceVariableName
=
"customerList"
)
public
Customer
getPreviousCustomer
()
{
public
Customer
getPreviousCustomer
()
{
return
previousCustomer
;
return
previousCustomer
;
}
}
@org
.
optaplanner
.
core
.
api
.
domain
.
variable
.
NextElementShadowVariable
(
sourceVariableName
=
"customerList"
)
@org
.
optaplanner
.
core
.
api
.
domain
.
variable
.
NextElementShadowVariable
(
sourceVariableName
=
"customerList"
)
public
Customer
getNextCustomer
()
{
public
Customer
getNextCustomer
()
{
return
nextCustomer
;
return
nextCustomer
;
}
}
@ShadowVariable
(
variableListenerClass
=
com
.
dituhui
.
pea
.
dispatch
.
shadowVariable
.
ArrivalTimeUpdatingVariableListener
.
class
,
sourceVariableName
=
"technician"
)
@ShadowVariable
(
variableListenerClass
=
com
.
dituhui
.
pea
.
dispatch
.
shadowVariable
.
ArrivalTimeUpdatingVariableListener
.
class
,
sourceVariableName
=
"technician"
)
@ShadowVariable
(
variableListenerClass
=
com
.
dituhui
.
pea
.
dispatch
.
shadowVariable
.
ArrivalTimeUpdatingVariableListener
.
class
,
sourceVariableName
=
"previousCustomer"
)
@ShadowVariable
(
variableListenerClass
=
com
.
dituhui
.
pea
.
dispatch
.
shadowVariable
.
ArrivalTimeUpdatingVariableListener
.
class
,
sourceVariableName
=
"previousCustomer"
)
public
Integer
getArrivalTime
()
{
public
Integer
getArrivalTime
()
{
return
arrivalTime
;
return
arrivalTime
;
}
}
// ************************************************************************
// ************************************************************************
// Complex methods
// Complex methods
// ************************************************************************
// ************************************************************************
public
Integer
getDepartureTime
()
{
public
Integer
getDepartureTime
()
{
if
(
arrivalTime
==
null
)
{
if
(
arrivalTime
==
null
)
{
return
null
;
return
null
;
}
}
return
Math
.
max
(
arrivalTime
,
startTime
)
+
serviceDuration
;
return
Math
.
max
(
arrivalTime
,
startTime
)
+
serviceDuration
;
}
}
public
boolean
isArrivalBeforeStartTime
()
{
public
boolean
isArrivalBeforeStartTime
()
{
return
arrivalTime
!=
null
&&
arrivalTime
<
startTime
;
return
arrivalTime
!=
null
&&
arrivalTime
<
startTime
;
}
}
public
boolean
isArrivalAfterEndTime
()
{
public
boolean
isArrivalAfterEndTime
()
{
return
arrivalTime
!=
null
&&
endTime
<
arrivalTime
;
return
arrivalTime
!=
null
&&
endTime
<
arrivalTime
;
}
}
/**
/**
* 计算2个订单之间时间窗gap 注意:有交集返回0,其他返回相离时间
* 计算2个订单之间时间窗gap 注意:有交集返回0,其他返回相离时间
*
*
* @param other
* @param other
* @return
* @return
*/
*/
public
Integer
getTimeWindowGapTo
(
Customer
other
)
{
public
Integer
getTimeWindowGapTo
(
Customer
other
)
{
// dueTime doesn't account for serviceDuration
// dueTime doesn't account for serviceDuration
int
latestDepartureTime
=
endTime
+
serviceDuration
;
int
latestDepartureTime
=
endTime
+
serviceDuration
;
int
otherLatestDepartureTime
=
other
.
getEndTime
()
+
other
.
getServiceDuration
();
int
otherLatestDepartureTime
=
other
.
getEndTime
()
+
other
.
getServiceDuration
();
if
(
latestDepartureTime
<
other
.
getStartTime
())
{
if
(
latestDepartureTime
<
other
.
getStartTime
())
{
return
other
.
getStartTime
()
-
latestDepartureTime
;
return
other
.
getStartTime
()
-
latestDepartureTime
;
}
}
if
(
otherLatestDepartureTime
<
startTime
)
{
if
(
otherLatestDepartureTime
<
startTime
)
{
return
startTime
-
otherLatestDepartureTime
;
return
startTime
-
otherLatestDepartureTime
;
}
}
return
0
;
return
0
;
}
}
/**
/**
* 与前一个订单或者出发地depot的距离
* 与前一个订单或者出发地depot的距离
*
*
* @return
* @return
*/
*/
public
long
getDistanceFromPreviousStandstill
()
{
public
long
getDistanceFromPreviousStandstill
()
{
if
(
technician
==
null
)
{
if
(
technician
==
null
)
{
throw
new
IllegalStateException
(
throw
new
IllegalStateException
(
"This method must not be called when the shadow variables are not initialized yet."
);
"This method must not be called when the shadow variables are not initialized yet."
);
}
}
if
(
previousCustomer
==
null
)
{
if
(
previousCustomer
==
null
)
{
return
technician
.
getDepot
().
getLocation
().
getDistanceTo
(
location
);
return
technician
.
getDepot
().
getLocation
().
getDistanceTo
(
location
);
}
}
return
previousCustomer
.
getLocation
().
getDistanceTo
(
location
);
return
previousCustomer
.
getLocation
().
getDistanceTo
(
location
);
}
}
/**
/**
* 与前一个订单或者出发地depot的路程时间
* 与前一个订单或者出发地depot的路程时间
*
*
* @return
* @return
*/
*/
public
int
getPathTimeFromPreviousStandstill
()
{
public
int
getPathTimeFromPreviousStandstill
()
{
if
(
technician
==
null
)
{
if
(
technician
==
null
)
{
throw
new
IllegalStateException
(
throw
new
IllegalStateException
(
"This method must not be called when the shadow variables are not initialized yet."
);
"This method must not be called when the shadow variables are not initialized yet."
);
}
}
if
(
previousCustomer
==
null
)
{
if
(
previousCustomer
==
null
)
{
return
technician
.
getDepot
().
getLocation
().
getPathTimeTo
(
location
);
return
technician
.
getDepot
().
getLocation
().
getPathTimeTo
(
location
);
}
}
return
previousCustomer
.
getLocation
().
getPathTimeTo
(
location
);
return
previousCustomer
.
getLocation
().
getPathTimeTo
(
location
);
}
}
@Override
@Override
public
int
hashCode
()
{
public
int
hashCode
()
{
return
Long
.
valueOf
(
this
.
id
).
hashCode
();
return
Long
.
valueOf
(
this
.
id
).
hashCode
();
}
}
@Override
@Override
public
boolean
equals
(
Object
obj
)
{
public
boolean
equals
(
Object
obj
)
{
if
(
obj
==
null
)
if
(
obj
==
null
)
return
false
;
return
false
;
if
(!(
obj
instanceof
Customer
))
if
(!(
obj
instanceof
Customer
))
return
false
;
return
false
;
if
(
obj
==
this
)
if
(
obj
==
this
)
return
true
;
return
true
;
return
this
.
id
==
((
Customer
)
obj
).
getId
();
return
this
.
id
==
((
Customer
)
obj
).
getId
();
}
}
@Override
@Override
public
String
toString
()
{
public
String
toString
()
{
return
"Customer{"
+
"id="
+
id
+
'}'
;
return
"Customer{"
+
}
"id="
+
id
+
", code='"
+
code
+
'\''
+
", location="
+
location
+
", startTime="
+
startTime
+
", endTime="
+
endTime
+
", serviceDuration="
+
serviceDuration
+
", requiredSkill='"
+
requiredSkill
+
'\''
+
", technician="
+
technician
.
getCode
()
+
", previousCustomer="
+
((
previousCustomer
!=
null
)
?
previousCustomer
.
getCode
()
:
"null"
)
+
", nextCustomer="
+
((
nextCustomer
!=
null
)
?
nextCustomer
.
getCode
()
:
"null"
)
+
", arrivalTime="
+
arrivalTime
+
", departureTime="
+
((
getDepartureTime
()
!=
null
)?
getDepartureTime
()
:
0
)
+
'}'
;
}
}
}
project-dispatch/src/main/java/com/dituhui/pea/dispatch/scheduler/BatchScheduler.java
0 → 100644
View file @
47ca589
package
com
.
dituhui
.
pea
.
dispatch
.
scheduler
;
import
com.dituhui.pea.dispatch.constraint.DispatchConstraintProvider
;
import
com.dituhui.pea.dispatch.pojo.Customer
;
import
com.dituhui.pea.dispatch.pojo.DispatchSolution
;
import
com.dituhui.pea.dispatch.pojo.Technician
;
import
com.dituhui.pea.dispatch.service.BatchService
;
import
com.dituhui.pea.dispatch.service.ExtractService
;
import
com.dituhui.pea.dispatch.service.SolveService
;
import
lombok.extern.slf4j.Slf4j
;
import
org.optaplanner.core.api.solver.Solver
;
import
org.optaplanner.core.api.solver.SolverFactory
;
import
org.optaplanner.core.api.solver.SolverJob
;
import
org.optaplanner.core.api.solver.SolverManager
;
import
org.optaplanner.core.config.solver.SolverConfig
;
import
org.optaplanner.core.config.solver.SolverManagerConfig
;
import
org.optaplanner.persistence.jackson.impl.domain.solution.JacksonSolutionFileIO
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.stereotype.Component
;
import
org.springframework.scheduling.annotation.Scheduled
;
import
java.io.File
;
import
java.sql.SQLException
;
import
java.time.Duration
;
import
java.time.LocalDate
;
import
java.time.LocalDateTime
;
import
java.time.format.DateTimeFormatter
;
import
java.util.Arrays
;
import
java.util.UUID
;
@Slf4j
@Component
public
class
BatchScheduler
{
String
groupId
=
"gsuzhou"
;
int
nextDays
=
3
;
@Autowired
BatchService
batchService
;
@Autowired
SolveService
solveService
;
@Autowired
ExtractService
extractService
;
private
Solver
<
DispatchSolution
>
solver
;
public
BatchScheduler
()
{
SolverConfig
solverConfig
=
new
SolverConfig
().
withSolutionClass
(
DispatchSolution
.
class
);
solverConfig
.
withEntityClassList
(
Arrays
.
asList
(
Technician
.
class
,
Customer
.
class
));
// 这里不能漏掉,否则约束不生效
solverConfig
.
withConstraintProviderClass
(
DispatchConstraintProvider
.
class
);
solverConfig
.
withTerminationSpentLimit
(
Duration
.
ofSeconds
(
20
));
SolverFactory
<
DispatchSolution
>
solverFactory
=
SolverFactory
.
create
(
solverConfig
);
solver
=
solverFactory
.
buildSolver
();
}
/*
* 异步执行任务开始
* */
@Scheduled
(
cron
=
"${dispatch.cron.expr}"
)
public
void
dispatchRun
()
{
log
.
info
(
"dispatchRun group:{}"
,
groupId
);
try
{
for
(
int
i
=
0
;
i
<=
2
;
i
++)
{
String
currDay
=
LocalDate
.
now
().
plusDays
(
i
).
format
(
DateTimeFormatter
.
ISO_LOCAL_DATE
);
log
.
info
(
"dispatchRun begin----- group:{}, day:{}"
,
groupId
,
currDay
);
String
batchNo
=
batchService
.
buildBatchNo
(
groupId
,
currDay
);
UUID
problemId
=
solveService
.
generateProblemId
(
groupId
,
batchNo
);
log
.
info
(
"dispatchRun group:{}, day:{}, batch:{}, problemId:{}"
,
groupId
,
currDay
,
batchNo
,
problemId
);
DispatchSolution
problem
=
solveService
.
prepareSolution
(
groupId
,
batchNo
);
if
(
problem
.
getCustomerList
().
size
()
<=
0
)
{
log
.
info
(
"dispatchRun no order , group:{}, day:{}, batch:{}, problemId:{}, order-size:{}"
,
groupId
,
currDay
,
batchNo
,
problemId
,
problem
.
getCustomerList
().
size
());
continue
;
}
log
.
info
(
"dispatchRun prepare done, group:{}, day:{}, batch:{}, problemId:{}"
,
groupId
,
currDay
,
batchNo
,
problemId
);
DispatchSolution
solution
=
solver
.
solve
(
problem
);
log
.
info
(
"dispatchRun run done, group:{}, day:{}, batch:{}, problemId:{}, score:{}"
,
groupId
,
currDay
,
batchNo
,
problemId
,
solution
.
getScore
().
toShortString
());
this
.
extractService
.
saveAndExtractSolution
(
solution
);
log
.
info
(
"dispatchRun done ------ group:{}, day:{}"
,
groupId
,
currDay
);
JacksonSolutionFileIO
<
DispatchSolution
>
exporter
=
new
JacksonSolutionFileIO
<
DispatchSolution
>(
DispatchSolution
.
class
);
// Set the output file.
exporter
.
write
(
solution
,
new
File
(
"dispatchSolution_%s_%s.json"
.
format
(
groupId
,
currDay
)));
Thread
.
sleep
(
1000
*
5
);
}
}
catch
(
SQLException
e
)
{
log
.
info
(
"error %s"
,
e
);
throw
new
RuntimeException
(
e
);
}
catch
(
InterruptedException
e
)
{
log
.
info
(
"error %s"
,
e
);
throw
new
RuntimeException
(
e
);
}
log
.
info
(
"done"
);
}
// @Scheduled(fixedRate = 1000*10)
public
void
RunLog
()
{
log
.
info
(
"RunLog"
);
}
}
project-dispatch/src/main/java/com/dituhui/pea/dispatch/service/impl/BatchServiceImpl.java
View file @
47ca589
...
@@ -54,7 +54,6 @@ public class BatchServiceImpl implements BatchService {
...
@@ -54,7 +54,6 @@ public class BatchServiceImpl implements BatchService {
// 检查给定小组、日期是否有在运行的批次任务,没则返回,没有则创建
// 检查给定小组、日期是否有在运行的批次任务,没则返回,没有则创建
@Transactional
@Transactional
@Override
@Override
public
String
buildBatchNo
(
String
groupId
,
String
day
)
{
public
String
buildBatchNo
(
String
groupId
,
String
day
)
{
...
...
project-dispatch/src/main/java/com/dituhui/pea/dispatch/service/impl/ExtractServiceImpl.java
View file @
47ca589
package
com
.
dituhui
.
pea
.
dispatch
.
service
.
impl
;
package
com
.
dituhui
.
pea
.
dispatch
.
service
.
impl
;
import
cn.hutool.core.date.DateField
;
import
cn.hutool.core.date.DateTime
;
import
cn.hutool.core.date.DateTime
;
import
cn.hutool.core.date.DateUtil
;
import
cn.hutool.core.date.DateUtil
;
import
com.dituhui.pea.dispatch.dao.*
;
import
com.dituhui.pea.dispatch.dao.*
;
...
@@ -38,8 +39,6 @@ import java.util.stream.Stream;
...
@@ -38,8 +39,6 @@ import java.util.stream.Stream;
public
class
ExtractServiceImpl
implements
ExtractService
{
public
class
ExtractServiceImpl
implements
ExtractService
{
@Autowired
@Autowired
EngineerInfoRepository
engineerInfoRepo
;
EngineerInfoRepository
engineerInfoRepo
;
...
@@ -58,10 +57,10 @@ public class ExtractServiceImpl implements ExtractService {
...
@@ -58,10 +57,10 @@ public class ExtractServiceImpl implements ExtractService {
private
JdbcTemplate
jdbcTemplate
;
private
JdbcTemplate
jdbcTemplate
;
/*
/*
* 将计算结果回写到dispatch2个表、以及order两个表
* 将计算结果回写到dispatch2个表、以及order两个表
* */
* */
@Override
@Override
public
void
saveAndExtractSolution
(
DispatchSolution
solution
)
throws
RuntimeException
{
public
void
saveAndExtractSolution
(
DispatchSolution
solution
)
throws
RuntimeException
{
String
groupId
=
solution
.
getGroupId
();
String
groupId
=
solution
.
getGroupId
();
String
batchNo
=
solution
.
getBatchNo
();
String
batchNo
=
solution
.
getBatchNo
();
log
.
info
(
"算法结果回写包装方法, groupId:{}, batchNo:{}"
,
groupId
,
batchNo
);
log
.
info
(
"算法结果回写包装方法, groupId:{}, batchNo:{}"
,
groupId
,
batchNo
);
...
@@ -69,6 +68,7 @@ public class ExtractServiceImpl implements ExtractService {
...
@@ -69,6 +68,7 @@ public class ExtractServiceImpl implements ExtractService {
try
{
try
{
this
.
extractDispatchToOrder
(
solution
.
getGroupId
(),
solution
.
getBatchNo
());
this
.
extractDispatchToOrder
(
solution
.
getGroupId
(),
solution
.
getBatchNo
());
}
catch
(
SQLException
e
)
{
}
catch
(
SQLException
e
)
{
log
.
error
(
"算法结果回写包装方法异常, groupId:{}, batchNo:{}"
,
groupId
,
batchNo
,
e
);
throw
new
RuntimeException
(
e
);
throw
new
RuntimeException
(
e
);
}
}
}
}
...
@@ -76,6 +76,7 @@ public class ExtractServiceImpl implements ExtractService {
...
@@ -76,6 +76,7 @@ public class ExtractServiceImpl implements ExtractService {
/**
/**
* 将计算结果回写到dispatch_order表(更新补充技术员工号、上门时间)
* 将计算结果回写到dispatch_order表(更新补充技术员工号、上门时间)
*/
*/
@Transactional
@Override
@Override
public
void
saveSolutionToDispatch
(
String
groupId
,
String
batchNo
,
DispatchSolution
solution
)
throws
RuntimeException
{
public
void
saveSolutionToDispatch
(
String
groupId
,
String
batchNo
,
DispatchSolution
solution
)
throws
RuntimeException
{
log
.
info
(
"算法结果回写dispatch, groupId:{}, batchNo:{}"
,
groupId
,
batchNo
);
log
.
info
(
"算法结果回写dispatch, groupId:{}, batchNo:{}"
,
groupId
,
batchNo
);
...
@@ -90,14 +91,14 @@ public class ExtractServiceImpl implements ExtractService {
...
@@ -90,14 +91,14 @@ public class ExtractServiceImpl implements ExtractService {
log
.
info
(
"算法结果回写dispatch, step2-开始回写, groupId:{}, batchNo:{}"
,
groupId
,
batchNo
);
log
.
info
(
"算法结果回写dispatch, step2-开始回写, groupId:{}, batchNo:{}"
,
groupId
,
batchNo
);
// 保存当前批次指派结果
// 保存当前批次指派结果
solution
.
getTechnicianList
().
forEach
(
vehicle
->
{
solution
.
getTechnicianList
().
forEach
(
technician
->
{
log
.
info
(
"算法结果回写dispatch, step2.1-按技术员逐个回写, groupId:{}, batchNo:{},
employ
: {}, max-minute:{}, customlist.size:{}"
,
log
.
info
(
"算法结果回写dispatch, step2.1-按技术员逐个回写, groupId:{}, batchNo:{},
technician
: {}, max-minute:{}, customlist.size:{}"
,
groupId
,
batchNo
,
vehicle
.
getId
(),
vehicle
.
getMaxMinute
(),
vehicle
.
getCustomerList
().
size
());
groupId
,
batchNo
,
technician
.
getCode
(),
technician
.
getMaxMinute
(),
technician
.
getCustomerList
().
size
());
AtomicInteger
seq
=
new
AtomicInteger
();
AtomicInteger
seq
=
new
AtomicInteger
();
final
Date
[]
expectBegin
=
{
null
};
vehicle
.
getCustomerList
().
forEach
(
customer
->
{
technician
.
getCustomerList
().
forEach
(
customer
->
{
int
idx
=
seq
.
getAndIncrement
();
int
idx
=
seq
.
getAndIncrement
();
// 统计按8:00开始 +take_time + 20分钟路程向后累积
// 统计按8:00开始 +take_time + 20分钟路程向后累积
...
@@ -106,25 +107,22 @@ public class ExtractServiceImpl implements ExtractService {
...
@@ -106,25 +107,22 @@ public class ExtractServiceImpl implements ExtractService {
DispatchOrder
dOrder
=
optional
.
get
();
DispatchOrder
dOrder
=
optional
.
get
();
if
(
expectBegin
[
0
]
==
null
)
{
expectBegin
[
0
]
=
dOrder
.
getExpectTimeBegin
();
}
LocalDateTime
localExpectBegin
=
LocalDateTime
.
ofInstant
(
expectBegin
[
0
].
toInstant
(),
ZoneId
.
systemDefault
());
// 时间相加操作
// 时间相加操作
LocalDateTime
localEndTime
=
localExpectBegin
.
plusMinutes
(
dOrder
.
getTakeTime
());
// LocalDateTime localExpectBegin = LocalDateTime.ofInstant(expectBegin[0].toInstant(), ZoneId.systemDefault());
Date
end
=
Date
.
from
(
localEndTime
.
atZone
(
ZoneId
.
systemDefault
()).
toInstant
());
// LocalDateTime localEndTime = localExpectBegin.plusMinutes(dOrder.getTakeTime());
// Date end = Date.from(localEndTime.atZone(ZoneId.systemDefault()).toInstant());
log
.
info
(
"算法结果回写dispatch, step3-逐个客户处理, groupId:{}, batchNo:{}, employ: {}, customer:{}, service-duration:{} "
,
log
.
info
(
"算法结果回写dispatch, step3-逐个客户处理, groupId:{}, batchNo:{}, employ: {}, customer:{}, service-duration:{} "
,
groupId
,
batchNo
,
vehicle
.
getId
(),
customer
.
getId
(),
customer
.
getServiceDuration
());
groupId
,
batchNo
,
technician
.
getCode
(),
customer
.
getCode
(),
customer
.
getServiceDuration
());
log
.
info
(
customer
.
toString
());
Date
arriveTime
=
DateUtil
.
beginOfDay
(
dOrder
.
getExpectTimeBegin
()).
offset
(
DateField
.
MINUTE
,
customer
.
getArrivalTime
());
Date
leaveTime
=
DateUtil
.
beginOfDay
(
arriveTime
).
offset
(
DateField
.
MINUTE
,
customer
.
getDepartureTime
());
Object
[]
param
=
{
vehicle
.
getId
(),
idx
,
expectBegin
[
0
],
end
,
groupId
,
batchNo
,
customer
.
getId
()};
Object
[]
param
=
{
technician
.
getCode
(),
idx
,
arriveTime
,
leaveTime
,
groupId
,
batchNo
,
customer
.
getCode
()};
jdbcTemplate
.
update
(
sql
,
param
);
jdbcTemplate
.
update
(
sql
,
param
);
// 再追加20分钟路程时间做为下一次时间的开始
expectBegin
[
0
]
=
Date
.
from
(
localEndTime
.
plusMinutes
(
20
).
atZone
(
ZoneId
.
systemDefault
()).
toInstant
());
}
}
});
});
...
@@ -192,13 +190,14 @@ public class ExtractServiceImpl implements ExtractService {
...
@@ -192,13 +190,14 @@ public class ExtractServiceImpl implements ExtractService {
jdbcTemplate
.
update
(
"update order_request set appointment_status ='ASSIGNED' where order_id =? and appointment_status='NOT_ASSIGNED'"
,
orderId
);
jdbcTemplate
.
update
(
"update order_request set appointment_status ='ASSIGNED' where order_id =? and appointment_status='NOT_ASSIGNED'"
,
orderId
);
}
}
// 会有多次上门情况( pre_status='
dispatch
' and status in ('NOT_ASSIGNED', 'ASSIGNED') ,直接更新,其它情况新增)
// 会有多次上门情况( pre_status='
PRE
' and status in ('NOT_ASSIGNED', 'ASSIGNED') ,直接更新,其它情况新增)
Optional
<
OrderAppointment
>
appointmentOpt
=
orderAppointmentRepo
.
findByOrderId
(
orderId
);
Optional
<
OrderAppointment
>
appointmentOpt
=
orderAppointmentRepo
.
findByOrderId
(
orderId
);
if
(
appointmentOpt
.
isPresent
()
&&
"
dispatch
"
.
equals
(
appointmentOpt
.
get
().
getPreStatus
())
&&
if
(
appointmentOpt
.
isPresent
()
&&
"
PRE
"
.
equals
(
appointmentOpt
.
get
().
getPreStatus
())
&&
(
"NOT_ASSIGNED"
.
equals
(
appointmentOpt
.
get
().
getStatus
())
||
"ASSIGNED"
.
equals
(
appointmentOpt
.
get
().
getStatus
())))
{
(
"NOT_ASSIGNED"
.
equals
(
appointmentOpt
.
get
().
getStatus
())
||
"ASSIGNED"
.
equals
(
appointmentOpt
.
get
().
getStatus
())))
{
OrderAppointment
appointment
=
appointmentOpt
.
get
();
OrderAppointment
appointment
=
appointmentOpt
.
get
();
appointment
.
setStatus
(
"ASSIGNED"
);
appointment
.
setStatus
(
"ASSIGNED"
);
appointment
.
setPreStatus
(
"PRE"
);
appointment
.
setEngineerCode
(
engCode
);
appointment
.
setEngineerCode
(
engCode
);
appointment
.
setEngineerName
(
engName
);
appointment
.
setEngineerName
(
engName
);
appointment
.
setEngineerPhone
(
phone
);
appointment
.
setEngineerPhone
(
phone
);
...
@@ -222,7 +221,7 @@ public class ExtractServiceImpl implements ExtractService {
...
@@ -222,7 +221,7 @@ public class ExtractServiceImpl implements ExtractService {
appointment
.
setIsWorkshop
(
0
);
appointment
.
setIsWorkshop
(
0
);
appointment
.
setExpectStartTime
(
dispatchOrder
.
getTimeBegin
());
appointment
.
setExpectStartTime
(
dispatchOrder
.
getTimeBegin
());
appointment
.
setExpectEndTime
(
dispatchOrder
.
getTimeEnd
());
appointment
.
setExpectEndTime
(
dispatchOrder
.
getTimeEnd
());
appointment
.
setPreStatus
(
"
dispatch
"
);
appointment
.
setPreStatus
(
"
PRE
"
);
appointment
.
setStatus
(
"ASSIGNED"
);
appointment
.
setStatus
(
"ASSIGNED"
);
appointment
.
setMemo
(
""
);
appointment
.
setMemo
(
""
);
appointment
.
setCreateTime
(
LocalDateTime
.
now
());
appointment
.
setCreateTime
(
LocalDateTime
.
now
());
...
...
project-dispatch/src/main/java/com/dituhui/pea/dispatch/service/impl/SolveServiceImpl.java
View file @
47ca589
...
@@ -14,10 +14,14 @@ import lombok.extern.slf4j.Slf4j;
...
@@ -14,10 +14,14 @@ import lombok.extern.slf4j.Slf4j;
import
org.optaplanner.core.api.solver.Solver
;
import
org.optaplanner.core.api.solver.Solver
;
import
org.optaplanner.core.api.solver.SolverFactory
;
import
org.optaplanner.core.api.solver.SolverFactory
;
import
org.optaplanner.core.config.solver.SolverConfig
;
import
org.optaplanner.core.config.solver.SolverConfig
;
import
org.optaplanner.persistence.jackson.impl.domain.solution.JacksonSolutionFileIO
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.core.io.ClassPathResource
;
import
org.springframework.jdbc.core.JdbcTemplate
;
import
org.springframework.jdbc.core.JdbcTemplate
;
import
org.springframework.stereotype.Service
;
import
org.springframework.stereotype.Service
;
import
java.io.File
;
import
java.time.Duration
;
import
java.time.Duration
;
import
java.time.LocalDateTime
;
import
java.time.LocalDateTime
;
import
java.util.*
;
import
java.util.*
;
...
@@ -80,7 +84,6 @@ public class SolveServiceImpl implements SolveService {
...
@@ -80,7 +84,6 @@ public class SolveServiceImpl implements SolveService {
oneDepot
=
new
Depot
(
oneGroup
.
getId
(),
oneGroup
.
getGroupId
(),
deptLocation
,
60
*
8
,
60
*
18
);
oneDepot
=
new
Depot
(
oneGroup
.
getId
(),
oneGroup
.
getGroupId
(),
deptLocation
,
60
*
8
,
60
*
18
);
// customerlist
// customerlist
ArrayList
<
Customer
>
customerList
=
new
ArrayList
<>();
ArrayList
<
Customer
>
customerList
=
new
ArrayList
<>();
dispatchOrderRepo
.
findByGroupIdAndBatchNo
(
groupId
,
batchNo
).
forEach
(
order
->
{
dispatchOrderRepo
.
findByGroupIdAndBatchNo
(
groupId
,
batchNo
).
forEach
(
order
->
{
...
@@ -117,13 +120,13 @@ public class SolveServiceImpl implements SolveService {
...
@@ -117,13 +120,13 @@ public class SolveServiceImpl implements SolveService {
// 距离偏好map
// 距离偏好map
Map
<
String
,
Long
>
preferedLoctionDistanceMap
=
new
HashMap
<
String
,
Long
>();
Map
<
String
,
Long
>
preferedLoctionDistanceMap
=
new
HashMap
<
String
,
Long
>();
customerList
.
forEach
(
customer
->
{
customerList
.
forEach
(
customer
->
{
long
distance
=
distanceCalculator
.
calculateDistance
(
location
,
customer
.
getLocation
());
long
distance
=
distanceCalculator
.
calculateDistance
(
location
,
customer
.
getLocation
());
preferedLoctionDistanceMap
.
put
(
engineer
.
getEngineer
Code
(),
distance
);
preferedLoctionDistanceMap
.
put
(
customer
.
get
Code
(),
distance
);
});
});
Technician
vehicle
=
new
Technician
(
engineer
.
getId
(),
engineer
.
getEngineerCode
(),
Technician
vehicle
=
new
Technician
(
engineer
.
getId
(),
engineer
.
getEngineerCode
(),
engineer
.
getMaxNum
(),
engineer
.
getMaxMinute
(),
engineer
.
getMaxDistance
()
*
1000
,
depot
,
engineer
.
getMaxNum
(),
engineer
.
getMaxMinute
(),
engineer
.
getMaxDistance
()
*
1000
,
depot
,
60
*
8
,
60
*
18
,
Set
.
copyOf
(
skillList
),
preferedLoctionDistanceMap
);
60
*
8
,
60
*
18
,
Set
.
copyOf
(
skillList
),
preferedLoctionDistanceMap
);
technicianList
.
add
(
vehicle
);
technicianList
.
add
(
vehicle
);
});
});
...
@@ -164,6 +167,12 @@ public class SolveServiceImpl implements SolveService {
...
@@ -164,6 +167,12 @@ public class SolveServiceImpl implements SolveService {
DispatchSolution
solution
=
solver
.
solve
(
problem
);
DispatchSolution
solution
=
solver
.
solve
(
problem
);
log
.
info
(
"调用引擎处理-结束, groupId:{}, batchNo:{}, score:{}"
,
groupId
,
batchNo
,
solution
.
getScore
());
log
.
info
(
"调用引擎处理-结束, groupId:{}, batchNo:{}, score:{}"
,
groupId
,
batchNo
,
solution
.
getScore
());
JacksonSolutionFileIO
<
DispatchSolution
>
exporter
=
new
JacksonSolutionFileIO
<
DispatchSolution
>(
DispatchSolution
.
class
);
// Set the output file.
exporter
.
write
(
solution
,
new
File
(
"dispatchSolution.json"
));
return
solution
;
return
solution
;
}
}
...
...
project-dispatch/src/main/resources/application-dev.yaml
View file @
47ca589
server
:
server
:
port
:
8011
port
:
8011
dispatch
:
cron
:
expr
:
0 45 8-18 * * ?
# expr: 0 */10 8-18 * * ?
spring
:
spring
:
application
:
application
:
name
:
project-dispatch
name
:
project-dispatch
jackson
:
default-property-inclusion
:
NON_NULL
# time-zone: GMT+8
date-format
:
yyyy-MM-dd HH:mm:ss
cloud
:
cloud
:
nacos
:
nacos
:
discovery
:
discovery
:
...
@@ -18,7 +27,7 @@ spring:
...
@@ -18,7 +27,7 @@ spring:
enabled
:
false
enabled
:
false
datasource
:
datasource
:
driver-class-name
:
com.mysql.cj.jdbc.Driver
driver-class-name
:
com.mysql.cj.jdbc.Driver
url
:
jdbc:mysql://127.0.0.1:3306/saas_aftersale_test?serverTimezone=
UTC
url
:
jdbc:mysql://127.0.0.1:3306/saas_aftersale_test?serverTimezone=
Asia/Shanghai
username
:
root
username
:
root
password
:
12345678
password
:
12345678
type
:
com.alibaba.druid.pool.DruidDataSource
type
:
com.alibaba.druid.pool.DruidDataSource
...
...
project-dispatch/src/main/resources/application.yaml
View file @
47ca589
server
:
server
:
port
:
8011
port
:
8011
dispatch
:
cron
:
expr
:
0 */30 8-18 * * ?
spring
:
spring
:
application
:
application
:
name
:
project-dispatch
name
:
project-dispatch
jackson
:
default-property-inclusion
:
NON_NULL
# time-zone: GMT+8
date-format
:
yyyy-MM-dd HH:mm:ss
cloud
:
cloud
:
nacos
:
nacos
:
discovery
:
discovery
:
...
@@ -18,7 +26,7 @@ spring:
...
@@ -18,7 +26,7 @@ spring:
enabled
:
false
enabled
:
false
datasource
:
datasource
:
driver-class-name
:
com.mysql.cj.jdbc.Driver
driver-class-name
:
com.mysql.cj.jdbc.Driver
url
:
jdbc:mysql://10.10.0.116:3306/saas_aftersale_test?serverTimezone=
UTC
url
:
jdbc:mysql://10.10.0.116:3306/saas_aftersale_test?serverTimezone=
Asia/Shanghai
username
:
root
username
:
root
password
:
123456
password
:
123456
type
:
com.alibaba.druid.pool.DruidDataSource
type
:
com.alibaba.druid.pool.DruidDataSource
...
...
project-dispatch/src/test/java/com/dituhui/pea/dispatch/SolveServiceTest.java
View file @
47ca589
package
com
.
dituhui
.
pea
.
dispatch
;
package
com
.
dituhui
.
pea
.
dispatch
;
import
com.dituhui.pea.dispatch.constraint.DispatchConstraintProvider
;
import
com.dituhui.pea.dispatch.pojo.Customer
;
import
com.dituhui.pea.dispatch.pojo.DispatchSolution
;
import
com.dituhui.pea.dispatch.pojo.DispatchSolution
;
import
com.dituhui.pea.dispatch.pojo.Technician
;
import
com.dituhui.pea.dispatch.service.ExtractService
;
import
com.dituhui.pea.dispatch.service.SolveService
;
import
com.dituhui.pea.dispatch.service.SolveService
;
import
lombok.extern.slf4j.Slf4j
;
import
lombok.extern.slf4j.Slf4j
;
import
org.junit.jupiter.api.Test
;
import
org.junit.jupiter.api.Test
;
import
org.optaplanner.core.api.solver.SolverManager
;
import
org.optaplanner.core.config.solver.SolverConfig
;
import
org.optaplanner.core.config.solver.SolverManagerConfig
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.boot.test.context.SpringBootTest
;
import
org.springframework.boot.test.context.SpringBootTest
;
import
java.time.Duration
;
import
java.util.Arrays
;
import
java.util.UUID
;
import
static
java
.
lang
.
Thread
.
sleep
;
@Slf4j
@Slf4j
@SpringBootTest
@SpringBootTest
class
SolveServiceTest
{
class
SolveServiceTest
{
...
@@ -15,10 +28,26 @@ class SolveServiceTest {
...
@@ -15,10 +28,26 @@ class SolveServiceTest {
@Autowired
@Autowired
SolveService
solveService
;
SolveService
solveService
;
@Autowired
ExtractService
extractService
;
String
groupId
=
"gsuzhou"
;
String
groupId
=
"gsuzhou"
;
String
batchNo
=
"20230705-1500"
;
String
batchNo
=
"20230705-1500"
;
//@Test
private
SolverManager
<
DispatchSolution
,
UUID
>
solverManager
;
public
SolveServiceTest
()
{
SolverConfig
solverConfig
=
new
SolverConfig
().
withSolutionClass
(
DispatchSolution
.
class
);
solverConfig
.
withEntityClassList
(
Arrays
.
asList
(
Technician
.
class
,
Customer
.
class
));
// 这里不能漏掉,否则约束不生效
solverConfig
.
withConstraintProviderClass
(
DispatchConstraintProvider
.
class
);
solverConfig
.
withTerminationSpentLimit
(
Duration
.
ofSeconds
(
10
));
solverManager
=
SolverManager
.
create
(
solverConfig
,
new
SolverManagerConfig
());
}
@Test
public
void
test1
()
{
public
void
test1
()
{
log
.
info
(
"init"
);
log
.
info
(
"init"
);
...
@@ -29,4 +58,21 @@ class SolveServiceTest {
...
@@ -29,4 +58,21 @@ class SolveServiceTest {
log
.
info
(
"done"
);
log
.
info
(
"done"
);
}
}
@Test
public
void
testAsync
()
throws
InterruptedException
{
log
.
info
(
"testAsync init"
);
UUID
problemId
=
solveService
.
generateProblemId
(
groupId
,
batchNo
);
log
.
info
(
"testAsync problemId:{}"
,
problemId
);
DispatchSolution
problem
=
solveService
.
prepareSolution
(
groupId
,
batchNo
);
solverManager
.
solveAndListen
(
problemId
,
id
->
problem
,
this
.
extractService
::
saveAndExtractSolution
);
sleep
(
10
*
60
*
1000
);
log
.
info
(
"testAsync done"
);
}
}
}
Write
Preview
Markdown
is supported
Attach a file
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 post a comment