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 ede28e8b
authored
Jun 29, 2023
by
张晓
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
批次排班数据+dispath_order 分解到order两个表
1 parent
6275f5cc
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
35 changed files
with
1988 additions
and
0 deletions
project-pre-dispatch/fake-compose/docker-compose.yml
project-pre-dispatch/pom.xml
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/Application.java
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/WebConfig.java
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/common/DateUtil.java
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/dao/DispatchBatchRepository.java
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/dao/DispatchEngineerRepository.java
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/dao/DispatchOrderRepository.java
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/dao/EngineerInfoRepository.java
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/dao/OrderAppointmentRepository.java
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/dao/OrderRequestRepository.java
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/entity/DispatchBatch.java
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/entity/DispatchEngineer.java
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/entity/DispatchOrder.java
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/entity/EngineerInfo.java
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/entity/OrderAppointment.java
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/entity/OrderRequest.java
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/interceptor/RequestInterceptor.java
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/opta/domain/Customer.java
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/opta/domain/Depot.java
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/opta/domain/Location.java
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/opta/domain/Status.java
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/opta/domain/Vehicle.java
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/opta/domain/VehicleRoutingSolution.java
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/opta/domain/geo/DistanceCalculator.java
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/opta/domain/geo/GeoDistanceCalculator.java
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/opta/solver/VehicleRoutingConstraintProvider.java
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/scheduler/Scheduler.java
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/service/BatchService.java
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/service/PrepareService.java
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/service/impl/BatchServiceImpl.java
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/service/impl/PrepareServiceImpl.java
project-pre-dispatch/src/main/resources/application.yaml
project-pre-dispatch/src/main/resources/logback.xml
project-pre-dispatch/src/test/java/com/dituhui/pea/pre/BatchServiceTest.java
project-pre-dispatch/fake-compose/docker-compose.yml
0 → 100644
View file @
ede28e8
version
:
"
3"
services
:
nacos
:
image
:
nacos/nacos-server:v2.2.3-slim
container_name
:
nacos-standalone-local
environment
:
-
PREFER_HOST_MODE=hostname
-
MODE=standalone
-
NACOS_AUTH_IDENTITY_KEY=serverIdentity
-
NACOS_AUTH_IDENTITY_VALUE=security
-
NACOS_AUTH_TOKEN=SecretKey012345678901234567890123456789012345678901234567890123456789
volumes
:
-
./nacos-logs/:/home/nacos/logs
ports
:
-
"
8848:8848"
-
"
9848:9848"
seata-server
:
image
:
seataio/seata-server:1.6.1
hostname
:
seata-server
restart
:
always
container_name
:
seata-standalone-local
ports
:
-
"
8091:8091"
environment
:
-
SEATA_PORT=8091
-
STORE_MODE=file
# redis:
# image: redis:6.2-alpine
# hostname: redis
# restart: always
# container_name: redis-standalone-local
# ports:
# - "6379:6379"
# command: redis-server --save 20 1 --loglevel warning --requirepass 123456
project-pre-dispatch/pom.xml
0 → 100644
View file @
ede28e8
<?xml version="1.0" encoding="UTF-8"?>
<project
xmlns=
"http://maven.apache.org/POM/4.0.0"
xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
>
<parent>
<groupId>
com.alibaba.cloud
</groupId>
<artifactId>
spring-cloud-alibaba-parent
</artifactId>
<version>
${revision}
</version>
<relativePath>
../pom.xml
</relativePath>
</parent>
<modelVersion>
4.0.0
</modelVersion>
<artifactId>
project-pre-dispatch
</artifactId>
<name>
Pea PreDispatch
</name>
<properties>
<druid.version>
1.1.10
</druid.version>
<version.org.optaplanner>
9.38.0.Final
</version.org.optaplanner>
<mysql.version>
8.0.28
</mysql.version>
</properties>
<dependencies>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-web
</artifactId>
</dependency>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-validation
</artifactId>
</dependency>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-jdbc
</artifactId>
</dependency>
<dependency>
<groupId>
com.alibaba
</groupId>
<artifactId>
druid-spring-boot-starter
</artifactId>
<version>
${druid.version}
</version>
</dependency>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-data-jpa
</artifactId>
</dependency>
<dependency>
<groupId>
mysql
</groupId>
<artifactId>
mysql-connector-java
</artifactId>
<version>
${mysql.version}
</version>
</dependency>
<dependency>
<groupId>
com.alibaba.cloud
</groupId>
<artifactId>
spring-cloud-starter-alibaba-seata
</artifactId>
</dependency>
<dependency>
<groupId>
com.alibaba.cloud
</groupId>
<artifactId>
spring-cloud-starter-alibaba-nacos-discovery
</artifactId>
</dependency>
<dependency>
<groupId>
com.alibaba.cloud
</groupId>
<artifactId>
spring-cloud-starter-alibaba-nacos-config
</artifactId>
</dependency>
<dependency>
<groupId>
com.alibaba.cloud
</groupId>
<artifactId>
project-interface
</artifactId>
<version>
${revision}
</version>
</dependency>
<dependency>
<groupId>
org.optaplanner
</groupId>
<artifactId>
optaplanner-core
</artifactId>
<version>
${version.org.optaplanner}
</version>
</dependency>
<dependency>
<groupId>
org.gavaghan
</groupId>
<artifactId>
geodesy
</artifactId>
<version>
1.1.3
</version>
</dependency>
<dependency>
<groupId>
commons-dbutils
</groupId>
<artifactId>
commons-dbutils
</artifactId>
<version>
1.7
</version>
</dependency>
<dependency>
<groupId>
cn.hutool
</groupId>
<artifactId>
hutool-all
</artifactId>
<version>
5.7.5
</version>
</dependency>
<dependency>
<groupId>
ch.qos.logback
</groupId>
<artifactId>
logback-classic
</artifactId>
<scope>
runtime
</scope>
</dependency>
<dependency>
<groupId>
org.projectlombok
</groupId>
<artifactId>
lombok
</artifactId>
<scope>
provided
</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>
commons-io
</groupId>
<artifactId>
commons-io
</artifactId>
<version>
2.13.0
</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
<groupId>
org.apache.commons
</groupId>
<artifactId>
commons-lang3
</artifactId>
<version>
3.12.0
</version>
</dependency>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-test
</artifactId>
<scope>
test
</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-maven-plugin
</artifactId>
</plugin>
<plugin>
<groupId>
org.apache.maven.plugins
</groupId>
<artifactId>
maven-compiler-plugin
</artifactId>
<configuration>
<source>
10
</source>
<target>
10
</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/Application.java
0 → 100644
View file @
ede28e8
package
com
.
dituhui
.
pea
.
pre
;
import
org.springframework.boot.SpringApplication
;
import
org.springframework.boot.autoconfigure.SpringBootApplication
;
import
org.springframework.scheduling.annotation.EnableScheduling
;
/**
* @author zhangx
*/
@SpringBootApplication
@EnableScheduling
public
class
Application
{
public
static
void
main
(
String
[]
args
)
{
SpringApplication
.
run
(
Application
.
class
,
args
);
}
}
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/WebConfig.java
0 → 100644
View file @
ede28e8
package
com
.
dituhui
.
pea
.
pre
;
import
com.dituhui.pea.pre.interceptor.RequestInterceptor
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.context.annotation.Configuration
;
import
org.springframework.data.jpa.repository.config.EnableJpaRepositories
;
import
org.springframework.web.servlet.config.annotation.InterceptorRegistry
;
import
org.springframework.web.servlet.config.annotation.WebMvcConfigurer
;
@EnableJpaRepositories
@Configuration
public
class
WebConfig
implements
WebMvcConfigurer
{
@Autowired
private
RequestInterceptor
requestInterceptor
;
@Override
public
void
addInterceptors
(
InterceptorRegistry
registry
)
{
registry
.
addInterceptor
(
requestInterceptor
).
addPathPatterns
(
"/**"
);
}
}
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/common/DateUtil.java
0 → 100644
View file @
ede28e8
package
com
.
dituhui
.
pea
.
pre
.
common
;
import
java.time.Instant
;
import
java.time.LocalDateTime
;
import
java.time.ZoneId
;
import
java.time.ZonedDateTime
;
import
java.util.Date
;
public
class
DateUtil
{
/**
* LocalDateTime转毫秒时间戳
*
* @param localDateTime LocalDateTime
* @return 时间戳
*/
public
static
Long
localDateTimeToTimestamp
(
LocalDateTime
localDateTime
)
{
try
{
ZoneId
zoneId
=
ZoneId
.
systemDefault
();
Instant
instant
=
localDateTime
.
atZone
(
zoneId
).
toInstant
();
return
instant
.
toEpochMilli
();
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
return
null
;
}
/**
* 时间戳转LocalDateTime
*
* @param timestamp 时间戳
* @return LocalDateTime
*/
public
static
LocalDateTime
timestampToLocalDateTime
(
long
timestamp
)
{
try
{
Instant
instant
=
Instant
.
ofEpochMilli
(
timestamp
);
ZoneId
zone
=
ZoneId
.
systemDefault
();
return
LocalDateTime
.
ofInstant
(
instant
,
zone
);
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
return
null
;
}
/**
* Date转LocalDateTime
*
* @param date Date
* @return LocalDateTime
*/
public
static
LocalDateTime
dateToLocalDateTime
(
Date
date
)
{
try
{
Instant
instant
=
date
.
toInstant
();
ZoneId
zoneId
=
ZoneId
.
systemDefault
();
return
instant
.
atZone
(
zoneId
).
toLocalDateTime
();
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
return
null
;
}
/**
* LocalDateTime转Date
*
* @param localDateTime LocalDateTime
* @return Date
*/
public
static
Date
localDateTimeToDate
(
LocalDateTime
localDateTime
)
{
try
{
ZoneId
zoneId
=
ZoneId
.
systemDefault
();
ZonedDateTime
zdt
=
localDateTime
.
atZone
(
zoneId
);
return
Date
.
from
(
zdt
.
toInstant
());
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
return
null
;
}
}
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/dao/DispatchBatchRepository.java
0 → 100644
View file @
ede28e8
package
com
.
dituhui
.
pea
.
pre
.
dao
;
import
com.dituhui.pea.pre.entity.DispatchBatch
;
import
org.springframework.data.jpa.repository.Query
;
import
org.springframework.data.repository.CrudRepository
;
import
org.springframework.stereotype.Component
;
import
org.springframework.stereotype.Repository
;
import
org.springframework.stereotype.Service
;
import
java.util.List
;
import
java.util.Optional
;
@Repository
public
interface
DispatchBatchRepository
extends
CrudRepository
<
DispatchBatch
,
Long
>
{
List
<
DispatchBatch
>
findByGroupId
(
String
groupId
);
Optional
<
DispatchBatch
>
findByGroupIdAndBatchDate
(
String
groupId
,
String
batchDay
);
@Query
(
value
=
"from DispatchBatch where groupId = ?1 and batchNo=?2 "
)
List
<
DispatchBatch
>
findLatestGroup
(
String
groupId
,
String
batchNo
);
}
\ No newline at end of file
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/dao/DispatchEngineerRepository.java
0 → 100644
View file @
ede28e8
package
com
.
dituhui
.
pea
.
pre
.
dao
;
import
com.dituhui.pea.pre.entity.DispatchEngineer
;
import
org.springframework.data.repository.CrudRepository
;
import
java.util.List
;
public
interface
DispatchEngineerRepository
extends
CrudRepository
<
DispatchEngineer
,
Long
>
{
List
<
DispatchEngineer
>
findByGroupId
(
String
groupId
);
List
<
DispatchEngineer
>
findByGroupIdAndBatchNo
(
String
groupId
,
String
batchNo
);
}
\ No newline at end of file
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/dao/DispatchOrderRepository.java
0 → 100644
View file @
ede28e8
package
com
.
dituhui
.
pea
.
pre
.
dao
;
import
com.dituhui.pea.pre.entity.DispatchOrder
;
import
org.springframework.data.jpa.repository.Query
;
import
org.springframework.data.repository.CrudRepository
;
import
java.util.List
;
import
java.util.Optional
;
public
interface
DispatchOrderRepository
extends
CrudRepository
<
DispatchOrder
,
Long
>
{
List
<
DispatchOrder
>
findByGroupIdAndBatchNo
(
String
groupId
,
String
batchNo
);
List
<
DispatchOrder
>
findByGroupIdAndBatchNoAndEngineerCodeNot
(
String
groupId
,
String
batchNo
,
String
code
);
@Query
(
"from DispatchOrder where groupId=?1 and batchNo=?2 and engineerCode is not null and engineerCode!='' "
)
List
<
DispatchOrder
>
findAssigned
(
String
groupId
,
String
batchNo
);
Optional
<
DispatchOrder
>
findByGroupIdAndBatchNoAndOrderId
(
String
groupId
,
String
batchNo
,
String
orderId
);
}
\ No newline at end of file
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/dao/EngineerInfoRepository.java
0 → 100644
View file @
ede28e8
package
com
.
dituhui
.
pea
.
pre
.
dao
;
import
com.dituhui.pea.pre.entity.EngineerInfo
;
import
org.springframework.data.repository.CrudRepository
;
import
java.util.List
;
import
java.util.Optional
;
public
interface
EngineerInfoRepository
extends
CrudRepository
<
EngineerInfo
,
Long
>
{
List
<
EngineerInfo
>
findByGroupId
(
String
groupId
);
Optional
<
EngineerInfo
>
findByEngineerCode
(
String
engineerCode
);
}
\ No newline at end of file
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/dao/OrderAppointmentRepository.java
0 → 100644
View file @
ede28e8
package
com
.
dituhui
.
pea
.
pre
.
dao
;
import
com.dituhui.pea.pre.entity.OrderAppointment
;
import
org.springframework.data.repository.CrudRepository
;
import
java.util.Optional
;
public
interface
OrderAppointmentRepository
extends
CrudRepository
<
OrderAppointment
,
Long
>
{
Optional
<
OrderAppointment
>
findByOrderId
(
String
orderId
);
}
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/dao/OrderRequestRepository.java
0 → 100644
View file @
ede28e8
package
com
.
dituhui
.
pea
.
pre
.
dao
;
import
com.dituhui.pea.pre.entity.OrderRequest
;
import
org.springframework.data.repository.CrudRepository
;
import
java.util.Optional
;
public
interface
OrderRequestRepository
extends
CrudRepository
<
OrderRequest
,
Long
>
{
Optional
<
OrderRequest
>
findByOrderId
(
String
orderId
);
}
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/entity/DispatchBatch.java
0 → 100644
View file @
ede28e8
package
com
.
dituhui
.
pea
.
pre
.
entity
;
import
com.fasterxml.jackson.annotation.JsonFormat
;
import
lombok.Data
;
import
javax.persistence.*
;
import
java.io.Serializable
;
import
java.time.LocalDateTime
;
/**
* 排班批次总表
*/
@Entity
@Data
@Table
(
name
=
"dispatch_batch"
)
public
class
DispatchBatch
implements
Serializable
{
private
static
final
long
serialVersionUID
=
1L
;
@Id
@Column
(
name
=
"id"
,
nullable
=
false
)
@GeneratedValue
(
strategy
=
GenerationType
.
IDENTITY
)
private
Long
id
;
@Column
(
name
=
"group_id"
)
private
String
groupId
;
/**
* 批次号
*/
@Column
(
name
=
"batch_no"
)
private
String
batchNo
;
/**
* 跑批日期
*/
@Column
(
name
=
"batch_date"
)
private
String
batchDate
;
/**
* 技术员数量
*/
@Column
(
name
=
"engineer_num"
)
private
Integer
engineerNum
;
/**
* 服务单数量
*/
@Column
(
name
=
"order_num"
)
private
Integer
orderNum
;
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
)
@Column
(
name
=
"start_time"
)
private
LocalDateTime
startTime
;
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
)
@Column
(
name
=
"end_time"
)
private
LocalDateTime
endTime
;
/**
* RUNNING,DONE
*/
@Column
(
name
=
"status"
)
private
String
status
;
@Column
(
name
=
"memo"
)
private
String
memo
;
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
)
@Column
(
name
=
"update_time"
)
private
LocalDateTime
updateTime
;
@Column
(
name
=
"ext"
)
private
String
ext
;
public
DispatchBatch
()
{
}
public
DispatchBatch
(
String
groupId
,
String
batchNo
,
String
batchDate
,
Integer
engineerNum
,
Integer
orderNum
)
{
this
.
groupId
=
groupId
;
this
.
batchNo
=
batchNo
;
this
.
batchDate
=
batchDate
;
this
.
engineerNum
=
engineerNum
;
this
.
orderNum
=
orderNum
;
}
}
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/entity/DispatchEngineer.java
0 → 100644
View file @
ede28e8
package
com
.
dituhui
.
pea
.
pre
.
entity
;
import
com.fasterxml.jackson.annotation.JsonFormat
;
import
lombok.Data
;
import
javax.persistence.*
;
import
java.io.Serializable
;
import
java.time.LocalDateTime
;
@Data
@Entity
@Table
(
name
=
"dispatch_engineer"
)
public
class
DispatchEngineer
implements
Serializable
{
@Id
@GeneratedValue
(
strategy
=
GenerationType
.
IDENTITY
)
private
Integer
id
;
@Column
(
name
=
"group_id"
)
private
String
groupId
;
@Column
(
name
=
"batch_no"
)
private
String
batchNo
;
@Column
(
name
=
"engineer_code"
)
private
String
engineerCode
;
@Column
(
name
=
"engineer_name"
)
private
String
engineerName
;
@Column
(
name
=
"x"
)
private
String
X
;
@Column
(
name
=
"y"
)
private
String
Y
;
@Column
(
name
=
"max_num"
)
private
Integer
maxNum
;
@Column
(
name
=
"max_minute"
)
private
Integer
maxMinute
;
@Column
(
name
=
"max_distance"
)
private
Integer
maxDistance
;
private
String
ext
=
""
;
private
String
memo
;
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
)
@Column
(
name
=
"update_time"
)
private
LocalDateTime
updateTime
;
}
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/entity/DispatchOrder.java
0 → 100644
View file @
ede28e8
package
com
.
dituhui
.
pea
.
pre
.
entity
;
import
com.fasterxml.jackson.annotation.JsonFormat
;
import
javax.persistence.*
;
import
lombok.Data
;
import
java.io.Serializable
;
import
java.time.LocalDateTime
;
import
java.util.Date
;
@Data
@Entity
@Table
(
name
=
"dispatch_order"
)
public
class
DispatchOrder
implements
Serializable
{
@Id
@GeneratedValue
(
strategy
=
GenerationType
.
IDENTITY
)
private
Integer
id
;
@Column
(
name
=
"group_id"
)
private
String
groupId
;
@Column
(
name
=
"batch_no"
)
private
String
batchNo
;
@Column
(
name
=
"order_id"
)
private
String
orderId
;
@Column
(
name
=
"x"
)
private
String
X
;
@Column
(
name
=
"y"
)
private
String
Y
;
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
,
timezone
=
"GMT+8"
)
@Column
(
name
=
"expect_time_begin"
)
private
Date
expectTimeBegin
;
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
,
timezone
=
"GMT+8"
)
@Column
(
name
=
"expect_time_end"
)
private
Date
expectTimeEnd
;
private
String
tags
;
private
Integer
priority
;
private
String
skills
;
@Column
(
name
=
"take_time"
)
private
Integer
takeTime
;
@Column
(
name
=
"engineer_code"
)
private
String
engineerCode
;
private
Integer
seq
;
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
)
@Column
(
name
=
"time_begin"
)
private
LocalDateTime
timeBegin
;
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
)
@Column
(
name
=
"time_end"
)
private
LocalDateTime
timeEnd
;
private
String
status
;
private
String
ext
;
private
String
memo
;
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
)
@Column
(
name
=
"update_time"
)
private
LocalDateTime
updateTime
;
}
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/entity/EngineerInfo.java
0 → 100644
View file @
ede28e8
package
com
.
dituhui
.
pea
.
pre
.
entity
;
import
com.fasterxml.jackson.annotation.JsonFormat
;
import
lombok.Data
;
import
javax.persistence.*
;
import
java.io.Serializable
;
import
java.time.LocalDateTime
;
@Data
@Entity
@Table
(
name
=
"engineer_info"
)
public
class
EngineerInfo
implements
Serializable
{
@Id
@GeneratedValue
(
strategy
=
GenerationType
.
IDENTITY
)
private
Integer
id
;
@Column
(
name
=
"engineer_code"
)
private
String
engineerCode
;
private
String
name
;
@Column
(
name
=
"group_id"
)
private
String
groupId
;
@Column
(
name
=
"cosmos_id"
)
private
String
cosmosId
;
private
String
gender
;
private
String
birth
;
private
String
phone
;
private
String
address
;
private
Integer
kind
;
private
String
grade
;
private
String
credentials
;
private
Integer
vehicle
;
@Column
(
name
=
"vehicle_no"
)
private
String
vehicleNo
;
@Column
(
name
=
"bean_status"
)
private
Integer
beanStatus
;
private
String
tags
;
private
String
memo
;
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
)
@Column
(
name
=
"create_time"
)
private
LocalDateTime
createTime
;
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
)
@Column
(
name
=
"update_time"
)
private
LocalDateTime
updateTime
;
}
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/entity/OrderAppointment.java
0 → 100644
View file @
ede28e8
package
com
.
dituhui
.
pea
.
pre
.
entity
;
import
com.fasterxml.jackson.annotation.JsonFormat
;
import
lombok.Data
;
import
javax.persistence.*
;
import
java.io.Serializable
;
import
java.time.LocalDateTime
;
@Data
@Entity
@Table
(
name
=
"order_appointment"
)
public
class
OrderAppointment
implements
Serializable
{
@Id
@GeneratedValue
(
strategy
=
GenerationType
.
IDENTITY
)
private
Integer
id
;
@Column
(
name
=
"order_id"
)
private
String
orderId
;
@Column
(
name
=
"suborder_id"
)
private
String
suborderId
;
@Column
(
name
=
"main_sub"
)
private
Integer
mainSub
;
@Column
(
name
=
"engineer_code"
)
private
String
engineerCode
;
@Column
(
name
=
"engineer_name"
)
private
String
engineerName
;
@Column
(
name
=
"engineer_phone"
)
private
String
engineerPhone
;
@Column
(
name
=
"engineer_age"
)
private
Integer
engineerAge
;
@Column
(
name
=
"is_workshop"
)
private
Integer
isWorkshop
;
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
,
timezone
=
"GMT+8"
)
@Column
(
name
=
"expect_start_time"
)
private
LocalDateTime
expectStartTime
;
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
,
timezone
=
"GMT+8"
)
@Column
(
name
=
"expect_end_time"
)
private
LocalDateTime
expectEndTime
;
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
,
timezone
=
"GMT+8"
)
@Column
(
name
=
"actual_time"
)
private
LocalDateTime
actualTime
;
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
,
timezone
=
"GMT+8"
)
@Column
(
name
=
"actual_start_time"
)
private
LocalDateTime
actualStartTime
;
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
,
timezone
=
"GMT+8"
)
@Column
(
name
=
"actual_end_time"
)
private
LocalDateTime
actualEndTime
;
@Column
(
name
=
"pre_status"
)
private
String
preStatus
;
private
String
status
;
private
String
memo
;
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
,
timezone
=
"GMT+8"
)
@Column
(
name
=
"create_time"
)
private
LocalDateTime
createTime
;
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
,
timezone
=
"GMT+8"
)
@Column
(
name
=
"update_time"
)
private
LocalDateTime
updateTime
;
}
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/entity/OrderRequest.java
0 → 100644
View file @
ede28e8
package
com
.
dituhui
.
pea
.
pre
.
entity
;
import
com.fasterxml.jackson.annotation.JsonFormat
;
import
lombok.Data
;
import
javax.persistence.*
;
import
java.io.Serializable
;
import
java.time.LocalDateTime
;
@Data
@Entity
@Table
(
name
=
"order_request"
)
public
class
OrderRequest
implements
Serializable
{
@Id
@GeneratedValue
(
strategy
=
GenerationType
.
IDENTITY
)
private
String
id
;
@Column
(
name
=
"order_id"
)
private
String
orderId
;
private
String
name
;
private
String
phone
;
private
String
address
;
@Column
(
name
=
"x"
)
private
String
X
;
@Column
(
name
=
"y"
)
private
String
Y
;
private
String
province
;
private
String
city
;
private
String
county
;
@Column
(
name
=
"category_id"
)
private
String
categoryId
;
private
String
brand
;
private
String
type
;
private
String
skill
;
@Column
(
name
=
"apply_note"
)
private
String
applyNote
;
@Column
(
name
=
"fault_describe"
)
private
String
faultDescribe
;
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
,
timezone
=
"GMT+8"
)
@Column
(
name
=
"expect_time_begin"
)
private
LocalDateTime
expectTimeBegin
;
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
,
timezone
=
"GMT+8"
)
@Column
(
name
=
"expect_time_end"
)
private
LocalDateTime
expectTimeEnd
;
@Column
(
name
=
"expect_time_desc"
)
private
String
expectTimeDesc
;
private
String
source
;
@Column
(
name
=
"area_id"
)
private
String
areaId
;
@Column
(
name
=
"order_priority"
)
private
String
orderPriority
;
@Column
(
name
=
"order_tags"
)
private
String
orderTags
;
private
Integer
priority
;
private
String
tags
;
private
String
status
;
@Column
(
name
=
"appointment_status"
)
private
String
appointmentStatus
;
@Column
(
name
=
"appointment_method"
)
private
String
appointmentMethod
;
@Column
(
name
=
"org_cluster_id"
)
private
String
orgClusterId
;
@Column
(
name
=
"org_cluster_name"
)
private
String
orgClusterName
;
@Column
(
name
=
"org_branch_id"
)
private
String
orgBranchId
;
@Column
(
name
=
"org_branch_name"
)
private
String
orgBranchName
;
@Column
(
name
=
"org_group_id"
)
private
String
orgGroupId
;
@Column
(
name
=
"org_group_name"
)
private
String
orgGroupName
;
@Column
(
name
=
"org_team_id"
)
private
String
orgTeamId
;
@Column
(
name
=
"org_team_name"
)
private
String
orgTeamName
;
private
String
description
;
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
,
timezone
=
"GMT+8"
)
@Column
(
name
=
"create_time"
)
private
LocalDateTime
createTime
;
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
,
timezone
=
"GMT+8"
)
@Column
(
name
=
"update_time"
)
private
LocalDateTime
updateTime
;
}
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/interceptor/RequestInterceptor.java
0 → 100644
View file @
ede28e8
package
com
.
dituhui
.
pea
.
pre
.
interceptor
;
import
com.fasterxml.jackson.databind.ObjectMapper
;
import
lombok.extern.slf4j.Slf4j
;
import
org.aspectj.lang.JoinPoint
;
import
org.aspectj.lang.ProceedingJoinPoint
;
import
org.aspectj.lang.annotation.*
;
import
org.springframework.stereotype.Component
;
import
org.springframework.web.context.request.RequestContextHolder
;
import
org.springframework.web.context.request.ServletRequestAttributes
;
import
org.springframework.web.servlet.HandlerInterceptor
;
import
javax.servlet.http.HttpServletRequest
;
@Slf4j
@Aspect
@Component
public
class
RequestInterceptor
implements
HandlerInterceptor
{
/**
* 以 controller 包下定义的所有请求为切入点
*/
@Pointcut
(
value
=
"execution(public * com.dituhui.pea.order.controller..*.*(..))"
)
public
void
reqOpenAPILog
()
{
}
/**
* 在切点之前织入
*
* @param joinPoint
* @throws Throwable
*/
@Before
(
"reqOpenAPILog()"
)
public
void
doBefore
(
JoinPoint
joinPoint
)
throws
Throwable
{
// 开始打印请求日志
ServletRequestAttributes
attributes
=
(
ServletRequestAttributes
)
RequestContextHolder
.
getRequestAttributes
();
HttpServletRequest
request
=
attributes
.
getRequest
();
// 打印请求 url
log
.
info
(
"Request URL : {}"
,
request
.
getRequestURL
().
toString
());
// 打印 Http method
log
.
info
(
"HTTP Method : {}"
,
request
.
getMethod
());
// 打印调用 controller 的全路径以及执行方法
log
.
info
(
"Class Method : {}.{}"
,
joinPoint
.
getSignature
().
getDeclaringTypeName
(),
joinPoint
.
getSignature
().
getName
());
// 打印请求的 IP
log
.
info
(
"Request IP : {}"
,
request
.
getRemoteAddr
());
// 打印请求入参
log
.
info
(
"Request Args : {}"
,
new
ObjectMapper
().
writeValueAsString
(
joinPoint
.
getArgs
()));
}
/**
* 在切点之后织入
*
* @throws Throwable
*/
@After
(
"reqOpenAPILog()"
)
public
void
doAfter
()
throws
Throwable
{
// TODO
}
/**
* 环绕
*
* @param proceedingJoinPoint
* @return
* @throws Throwable
*/
@Around
(
"reqOpenAPILog()"
)
public
Object
doAround
(
ProceedingJoinPoint
proceedingJoinPoint
)
throws
Throwable
{
log
.
info
(
"========================================== Start =========================================="
);
long
startTime
=
System
.
currentTimeMillis
();
Object
result
=
proceedingJoinPoint
.
proceed
();
// 打印出参
// log.info("Response Args : {}", result);
log
.
info
(
"Response Args : {}"
,
new
ObjectMapper
().
writeValueAsString
(
result
));
// 执行耗时
log
.
info
(
"Time-Consuming : {} ms"
,
System
.
currentTimeMillis
()
-
startTime
);
log
.
info
(
"=========================================== End ==========================================="
);
return
result
;
}
}
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/opta/domain/Customer.java
0 → 100644
View file @
ede28e8
package
com
.
dituhui
.
pea
.
pre
.
opta
.
domain
;
public
class
Customer
{
private
String
id
;
private
Location
location
;
// 耗时
private
int
duration
;
private
String
skill
;
public
Customer
()
{
}
public
Customer
(
String
id
,
Location
location
,
int
duration
,
String
skill
)
{
this
.
id
=
id
;
this
.
location
=
location
;
this
.
duration
=
duration
;
this
.
skill
=
skill
;
}
public
String
getId
()
{
return
id
;
}
public
void
setId
(
String
id
)
{
this
.
id
=
id
;
}
public
Location
getLocation
()
{
return
location
;
}
public
void
setLocation
(
Location
location
)
{
this
.
location
=
location
;
}
public
int
getDuration
()
{
return
duration
;
}
public
void
setDuration
(
int
duration
)
{
this
.
duration
=
duration
;
}
public
String
getSkill
()
{
return
skill
;
}
public
void
setSkill
(
String
skill
)
{
this
.
skill
=
skill
;
}
// ************************************************************************
// Complex methods
// ************************************************************************
@Override
public
String
toString
()
{
return
"Customer{"
+
"id='"
+
id
+
'\''
+
", location="
+
location
+
", duration="
+
duration
+
", skill='"
+
skill
+
'\''
+
'}'
;
}
}
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/opta/domain/Depot.java
0 → 100644
View file @
ede28e8
package
com
.
dituhui
.
pea
.
pre
.
opta
.
domain
;
// 起点
public
class
Depot
{
private
final
String
id
;
// 类型 分站
private
final
String
type
;
private
final
Location
location
;
public
Depot
(
String
id
,
String
type
,
Location
location
)
{
this
.
id
=
id
;
this
.
type
=
type
;
this
.
location
=
location
;
}
public
String
getId
()
{
return
id
;
}
public
String
getType
()
{
return
type
;
}
public
Location
getLocation
()
{
return
location
;
}
}
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/opta/domain/Location.java
0 → 100644
View file @
ede28e8
package
com
.
dituhui
.
pea
.
pre
.
opta
.
domain
;
import
com.fasterxml.jackson.annotation.JsonFormat
;
import
com.fasterxml.jackson.annotation.JsonIgnoreProperties
;
import
java.util.Map
;
@JsonFormat
(
shape
=
JsonFormat
.
Shape
.
ARRAY
)
@JsonIgnoreProperties
({
"id"
})
public
class
Location
{
private
final
String
id
;
// 类型 engineer order
private
final
String
type
;
private
final
double
latitude
;
private
final
double
longitude
;
private
Map
<
Location
,
Long
>
distanceMap
;
public
Location
(
String
id
,
String
type
,
double
longitude
,
double
latitude
)
{
this
.
id
=
id
;
this
.
type
=
type
;
this
.
longitude
=
longitude
;
this
.
latitude
=
latitude
;
}
@Override
public
String
toString
()
{
return
"Location{"
+
"id='"
+
id
+
'\''
+
", type='"
+
type
+
'\''
+
", latitude="
+
latitude
+
", longitude="
+
longitude
+
'}'
;
}
public
String
getId
()
{
return
id
;
}
public
String
getType
()
{
return
type
;
}
public
double
getLatitude
()
{
return
latitude
;
}
public
double
getLongitude
()
{
return
longitude
;
}
/**
* Set the distance map. Distances are in meters.
*
* @param distanceMap a map containing distances from here to other locations
*/
public
void
setDistanceMap
(
Map
<
Location
,
Long
>
distanceMap
)
{
this
.
distanceMap
=
distanceMap
;
}
/**
* Distance to the given location in meters.
*
* @param location other location
* @return distance in meters
*/
public
long
getDistanceTo
(
Location
location
)
{
return
distanceMap
.
get
(
location
);
}
// ************************************************************************
// Complex methods
// ************************************************************************
/**
* The angle relative to the direction EAST.
*
* @param location never null
* @return in Cartesian coordinates
*/
public
double
getAngle
(
Location
location
)
{
// Euclidean distance (Pythagorean theorem) - not correct when the surface is a sphere
double
latitudeDifference
=
location
.
latitude
-
latitude
;
double
longitudeDifference
=
location
.
longitude
-
longitude
;
return
Math
.
atan2
(
latitudeDifference
,
longitudeDifference
);
}
}
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/opta/domain/Status.java
0 → 100644
View file @
ede28e8
package
com
.
dituhui
.
pea
.
pre
.
opta
.
domain
;
import
org.optaplanner.core.api.solver.SolverStatus
;
class
Status
{
public
final
VehicleRoutingSolution
solution
;
public
final
String
scoreExplanation
;
public
final
boolean
isSolving
;
Status
(
VehicleRoutingSolution
solution
,
String
scoreExplanation
,
SolverStatus
solverStatus
)
{
this
.
solution
=
solution
;
this
.
scoreExplanation
=
scoreExplanation
;
this
.
isSolving
=
solverStatus
!=
SolverStatus
.
NOT_SOLVING
;
}
}
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/opta/domain/Vehicle.java
0 → 100644
View file @
ede28e8
package
com
.
dituhui
.
pea
.
pre
.
opta
.
domain
;
import
org.optaplanner.core.api.domain.entity.PlanningEntity
;
import
org.optaplanner.core.api.domain.lookup.PlanningId
;
import
org.optaplanner.core.api.domain.variable.PlanningListVariable
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.Set
;
@PlanningEntity
public
class
Vehicle
{
@PlanningId
private
String
id
;
private
int
maxMinute
;
// 单位是米,这里要注意
private
int
maxDistanceMeter
;
private
Depot
depot
;
private
Set
<
String
>
skillSet
;
@PlanningListVariable
private
List
<
Customer
>
customerList
;
public
Vehicle
()
{
}
public
Vehicle
(
String
id
,
int
maxMinute
,
int
maxDistanceMeter
,
Depot
depot
,
Set
<
String
>
skillSet
)
{
this
.
id
=
id
;
this
.
maxMinute
=
maxMinute
;
this
.
maxDistanceMeter
=
maxDistanceMeter
;
this
.
depot
=
depot
;
this
.
skillSet
=
skillSet
;
this
.
customerList
=
new
ArrayList
<>();
}
public
String
getId
()
{
return
id
;
}
public
void
setId
(
String
id
)
{
this
.
id
=
id
;
}
public
int
getMaxMinute
()
{
return
maxMinute
;
}
public
void
setMaxMinute
(
int
maxMinute
)
{
this
.
maxMinute
=
maxMinute
;
}
public
int
getMaxDistanceMeter
()
{
return
maxDistanceMeter
;
}
public
void
setMaxDistanceMeter
(
int
maxDistanceMeter
)
{
this
.
maxDistanceMeter
=
maxDistanceMeter
;
}
public
Depot
getDepot
()
{
return
depot
;
}
public
void
setDepot
(
Depot
depot
)
{
this
.
depot
=
depot
;
}
public
List
<
Customer
>
getCustomerList
()
{
return
customerList
;
}
public
void
setCustomerList
(
List
<
Customer
>
customerList
)
{
this
.
customerList
=
customerList
;
}
public
Set
<
String
>
getSkillSet
()
{
return
skillSet
;
}
public
void
setSkillSet
(
Set
<
String
>
skillSet
)
{
this
.
skillSet
=
skillSet
;
}
// ************************************************************************
// Complex methods
// ************************************************************************
/**
* @return route of the vehicle
*/
public
List
<
Location
>
getRoute
()
{
if
(
customerList
.
isEmpty
())
{
return
Collections
.
emptyList
();
}
List
<
Location
>
route
=
new
ArrayList
<
Location
>();
route
.
add
(
depot
.
getLocation
());
for
(
Customer
customer
:
customerList
)
{
route
.
add
(
customer
.
getLocation
());
}
// route.add(depot.getLocation());
return
route
;
}
private
int
getTotalDuration
()
{
int
total
=
0
;
for
(
Customer
customer
:
customerList
)
{
total
+=
customer
.
getDuration
();
}
return
total
;
}
public
long
getTotalDurationMinutes
()
{
// 服务耗时
int
durationMinute
=
getTotalDuration
();
// 路程耗时
// 驾车每秒种平均距离 (高德导航历史数据计算得出) avg_rate = 7.65
int
distance
=
getTotalDistanceMeters
();
double
avg_rate
=
7.65
;
long
routeMinute
=
Math
.
round
(
distance
/
avg_rate
/
60
);
return
durationMinute
+
routeMinute
;
}
public
int
getTotalDistanceMeters
()
{
if
(
customerList
.
isEmpty
())
{
return
0
;
}
int
total
=
0
;
Location
previousLocation
=
depot
.
getLocation
();
for
(
Customer
customer
:
customerList
)
{
total
+=
previousLocation
.
getDistanceTo
(
customer
.
getLocation
());
previousLocation
=
customer
.
getLocation
();
}
total
+=
previousLocation
.
getDistanceTo
(
depot
.
getLocation
());
return
total
;
}
@Override
public
String
toString
()
{
return
"Vehicle{"
+
"id='"
+
id
+
'\''
+
", maxMinute="
+
maxMinute
+
", maxDistanceMeter="
+
maxDistanceMeter
+
", depot="
+
depot
+
", skills="
+
skillSet
+
", customerList="
+
customerList
+
'}'
;
}
}
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/opta/domain/VehicleRoutingSolution.java
0 → 100644
View file @
ede28e8
package
com
.
dituhui
.
pea
.
pre
.
opta
.
domain
;
import
org.optaplanner.core.api.domain.solution.PlanningEntityCollectionProperty
;
import
org.optaplanner.core.api.domain.solution.PlanningScore
;
import
org.optaplanner.core.api.domain.solution.PlanningSolution
;
import
org.optaplanner.core.api.domain.solution.ProblemFactCollectionProperty
;
import
org.optaplanner.core.api.domain.valuerange.ValueRangeProvider
;
import
org.optaplanner.core.api.score.buildin.hardsoftlong.HardSoftLongScore
;
import
java.util.ArrayList
;
import
java.util.List
;
@PlanningSolution
public
class
VehicleRoutingSolution
{
private
String
name
;
@ProblemFactCollectionProperty
private
List
<
Location
>
locationList
;
@ProblemFactCollectionProperty
private
List
<
Depot
>
depotList
;
@PlanningEntityCollectionProperty
private
List
<
Vehicle
>
vehicleList
;
@ProblemFactCollectionProperty
@ValueRangeProvider
private
List
<
Customer
>
customerList
;
@PlanningScore
private
HardSoftLongScore
score
;
public
VehicleRoutingSolution
()
{
}
public
VehicleRoutingSolution
(
String
name
,
List
<
Depot
>
depotList
,
List
<
Vehicle
>
vehicleList
,
List
<
Customer
>
customerList
,
List
<
Location
>
locationList
)
{
this
.
name
=
name
;
this
.
depotList
=
depotList
;
this
.
vehicleList
=
vehicleList
;
this
.
customerList
=
customerList
;
this
.
locationList
=
locationList
;
}
public
String
getName
()
{
return
name
;
}
public
void
setName
(
String
name
)
{
this
.
name
=
name
;
}
public
List
<
Location
>
getLocationList
()
{
return
locationList
;
}
public
void
setLocationList
(
List
<
Location
>
locationList
)
{
this
.
locationList
=
locationList
;
}
public
List
<
Depot
>
getDepotList
()
{
return
depotList
;
}
public
void
setDepotList
(
List
<
Depot
>
depotList
)
{
this
.
depotList
=
depotList
;
}
public
List
<
Vehicle
>
getVehicleList
()
{
return
vehicleList
;
}
public
void
setVehicleList
(
List
<
Vehicle
>
vehicleList
)
{
this
.
vehicleList
=
vehicleList
;
}
public
List
<
Customer
>
getCustomerList
()
{
return
customerList
;
}
public
void
setCustomerList
(
List
<
Customer
>
customerList
)
{
this
.
customerList
=
customerList
;
}
public
HardSoftLongScore
getScore
()
{
return
score
;
}
public
void
setScore
(
HardSoftLongScore
score
)
{
this
.
score
=
score
;
}
// ************************************************************************
// Complex methods
// ************************************************************************
}
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/opta/domain/geo/DistanceCalculator.java
0 → 100644
View file @
ede28e8
package
com
.
dituhui
.
pea
.
pre
.
opta
.
domain
.
geo
;
import
com.dituhui.pea.pre.opta.domain.Location
;
import
org.springframework.stereotype.Component
;
import
java.util.Collection
;
import
java.util.Map
;
import
java.util.function.Function
;
import
java.util.stream.Collectors
;
@Component
public
interface
DistanceCalculator
{
/**
* Calculate the distance between {@code from} and {@code to} in meters.
*
* @param from starting location
* @param to target location
* @return distance in meters
*/
long
calculateDistance
(
Location
from
,
Location
to
);
/**
* Bulk calculation of distance.
* Typically much more scalable than {@link #calculateDistance(Location, Location)} iteratively.
*
* @param fromLocations never null
* @param toLocations never null
* @return never null
*/
default
Map
<
Location
,
Map
<
Location
,
Long
>>
calculateBulkDistance
(
Collection
<
Location
>
fromLocations
,
Collection
<
Location
>
toLocations
)
{
return
fromLocations
.
stream
().
collect
(
Collectors
.
toMap
(
Function
.
identity
(),
from
->
toLocations
.
stream
().
collect
(
Collectors
.
toMap
(
Function
.
identity
(),
to
->
calculateDistance
(
from
,
to
)
))
));
}
/**
* Calculate distance matrix for the given list of locations and assign distance maps accordingly.
*
* @param locationList
*/
default
void
initDistanceMaps
(
Collection
<
Location
>
locationList
)
{
Map
<
Location
,
Map
<
Location
,
Long
>>
distanceMatrix
=
calculateBulkDistance
(
locationList
,
locationList
);
locationList
.
forEach
(
location
->
location
.
setDistanceMap
(
distanceMatrix
.
get
(
location
)));
}
}
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/opta/domain/geo/GeoDistanceCalculator.java
0 → 100644
View file @
ede28e8
package
com
.
dituhui
.
pea
.
pre
.
opta
.
domain
.
geo
;
import
com.dituhui.pea.pre.opta.domain.Location
;
import
org.gavaghan.geodesy.Ellipsoid
;
import
org.gavaghan.geodesy.GeodeticCalculator
;
import
org.gavaghan.geodesy.GeodeticCurve
;
import
org.gavaghan.geodesy.GlobalCoordinates
;
import
org.springframework.stereotype.Component
;
@Component
public
class
GeoDistanceCalculator
implements
DistanceCalculator
{
@Override
public
long
calculateDistance
(
Location
from
,
Location
to
)
{
if
(
from
.
equals
(
to
))
{
return
0L
;
}
GlobalCoordinates
source
=
new
GlobalCoordinates
(
from
.
getLatitude
(),
from
.
getLongitude
());
GlobalCoordinates
target
=
new
GlobalCoordinates
(
to
.
getLatitude
(),
to
.
getLongitude
());
GeodeticCurve
geoCurve
=
new
GeodeticCalculator
().
calculateGeodeticCurve
(
Ellipsoid
.
WGS84
,
source
,
target
);
long
distance
=
Math
.
round
(
geoCurve
.
getEllipsoidalDistance
());
// todo *1.4倍 约等于实际路线距离
distance
=
Math
.
round
(
distance
*
1.4
);
return
distance
;
}
}
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/opta/solver/VehicleRoutingConstraintProvider.java
0 → 100644
View file @
ede28e8
package
com
.
dituhui
.
pea
.
pre
.
opta
.
solver
;
import
com.dituhui.pea.pre.opta.domain.Vehicle
;
import
org.optaplanner.core.api.score.buildin.hardsoftlong.HardSoftLongScore
;
import
org.optaplanner.core.api.score.stream.Constraint
;
import
org.optaplanner.core.api.score.stream.ConstraintFactory
;
import
org.optaplanner.core.api.score.stream.ConstraintProvider
;
import
org.springframework.stereotype.Service
;
import
java.util.Set
;
@Service
public
class
VehicleRoutingConstraintProvider
implements
ConstraintProvider
{
@Override
public
Constraint
[]
defineConstraints
(
ConstraintFactory
factory
)
{
return
new
Constraint
[]{
// skillMatchHard(factory),
mixCountHard
(
factory
),
maxTimeCapacityHard
(
factory
),
maxDistanceHard
(
factory
),
totalDistance
(
factory
),
avgCustomers
(
factory
),
};
}
//
protected
Constraint
skillMatchHard
(
ConstraintFactory
factory
)
{
return
factory
.
forEach
(
Vehicle
.
class
).
filter
(
vehicle
->
vehicle
.
getCustomerList
().
size
()
>
0
)
.
penalizeLong
(
HardSoftLongScore
.
ofUninitialized
(
0
,
100
,
0
),
vehicle
->
{
Set
<
String
>
skillSet
=
vehicle
.
getSkillSet
();
final
int
[]
missCount
=
{
0
};
vehicle
.
getCustomerList
().
forEach
(
customer
->
{
if
(!
skillSet
.
contains
(
customer
.
getSkill
()))
{
missCount
[
0
]
=
missCount
[
0
]
+
1
;
}
});
return
missCount
[
0
];
})
.
asConstraint
(
"技能匹配"
);
}
// 最少n单
public
Constraint
mixCountHard
(
ConstraintFactory
factory
)
{
return
factory
.
forEach
(
Vehicle
.
class
).
filter
(
vehicle
->
vehicle
.
getCustomerList
().
size
()
<=
2
)
.
penalizeLong
(
HardSoftLongScore
.
ofUninitialized
(
0
,
100
,
0
),
vehicle
->
{
// int score = 15 - vehicle.getCustomerList().size();
return
1
;
})
.
asConstraint
(
"最小单数约束(3单)"
);
}
// 总时长限制
protected
Constraint
maxTimeCapacityHard
(
ConstraintFactory
factory
)
{
return
factory
.
forEach
(
Vehicle
.
class
)
.
filter
(
vehicle
->
{
long
span
=
vehicle
.
getTotalDurationMinutes
()
-
vehicle
.
getMaxMinute
();
return
span
>
0
;
})
.
penalizeLong
(
HardSoftLongScore
.
ofUninitialized
(
0
,
20
,
0
),
vehicle
->
{
long
span
=
vehicle
.
getTotalDurationMinutes
()
-
vehicle
.
getMaxMinute
();
return
span
/
60
;
})
.
asConstraint
(
"工作时长限制"
);
}
// 总里程限制
protected
Constraint
maxDistanceHard
(
ConstraintFactory
factory
)
{
return
factory
.
forEach
(
Vehicle
.
class
).
filter
(
vehicle
->
{
int
total
=
vehicle
.
getTotalDistanceMeters
();
int
max
=
vehicle
.
getMaxDistanceMeter
();
int
diff
=
total
-
max
;
// Log.infof("DistanceHard filter vehicle:%s, customlist.size:%d , total-distance:%d, max-meter:%d diff:%s",
// vehicle.getId(), vehicle.getCustomerList().size(), total, max, diff);
return
diff
>
0
;
})
.
penalizeLong
(
HardSoftLongScore
.
ofUninitialized
(
0
,
20
,
0
),
vehicle
->
{
int
total
=
vehicle
.
getTotalDistanceMeters
();
int
max
=
vehicle
.
getMaxDistanceMeter
();
int
diff
=
total
-
max
;
// Log.infof("DistanceHard vehicle:%s, customlist.size:%d , total-distance:%d, max-meter:%d diff:%s",
// vehicle.getId(), vehicle.getCustomerList().size(), total, max, diff);
return
diff
/
1000
;
})
.
asConstraint
(
"最大里程约束"
);
}
/*
protected Constraint vehicleDistanceCapacity(ConstraintFactory factory) {
return factory.forEach(Vehicle.class)
.filter(vehicle -> {
int span = Math.abs(vehicle.getTotalDistanceMeters() - vehicle.getMaxDistance());
double rate = Math.round(span / vehicle.getMaxDistance());
return rate > 0.2;
})
.penalizeLong(HardSoftLongScore.ONE_HARD,
vehicle -> {
int span = Math.abs(vehicle.getTotalDistanceMeters() - vehicle.getMaxDistance());
double rate = Math.round(span / vehicle.getMaxDistance());
return span;
})
.asConstraint("vehicleDistanceCapacity");
}*/
// ************************************************************************
// Soft constraints
// ************************************************************************
// 路径最短为最优
protected
Constraint
totalDistance
(
ConstraintFactory
factory
)
{
return
factory
.
forEach
(
Vehicle
.
class
)
.
penalizeLong
(
HardSoftLongScore
.
ofUninitialized
(
0
,
1000
,
10
),
vehicle
->
vehicle
.
getTotalDistanceMeters
()/
1000
)
.
asConstraint
(
"里程最短最优"
);
}
// 车辆间货物均衡为最优级
protected
Constraint
avgCustomers
(
ConstraintFactory
factory
)
{
return
factory
.
forEach
(
Vehicle
.
class
)
.
penalizeLong
(
HardSoftLongScore
.
ofUninitialized
(
0
,
100
,
0
),
vehicle
->
Math
.
abs
(
9
-
vehicle
.
getCustomerList
().
size
()))
.
asConstraint
(
"车辆间货物均衡最优"
);
}
}
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/scheduler/Scheduler.java
0 → 100644
View file @
ede28e8
package
com
.
dituhui
.
pea
.
pre
.
scheduler
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.scheduling.annotation.Scheduled
;
import
org.springframework.stereotype.Component
;
@Slf4j
@Component
public
class
Scheduler
{
@Scheduled
(
fixedRate
=
1000
*
10
)
public
void
RunLog
(){
log
.
info
(
"RunLog"
);
}
@Scheduled
(
cron
=
"${pre-dispatch.cron.expr}"
)
public
void
dispatchRun
(){
log
.
info
(
"dispatchRun"
);
}
}
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/service/BatchService.java
0 → 100644
View file @
ede28e8
package
com
.
dituhui
.
pea
.
pre
.
service
;
import
java.sql.SQLException
;
/**
* @author zhangx
*
* 批次排班数据准备
*
*/
public
interface
BatchService
{
// 检查指定日期的小组是否有在运行的批次任务,有则返回,没有则创建后返回批次码
String
buildBatchNo
(
String
groupId
,
String
day
)
throws
SQLException
;
}
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/service/PrepareService.java
0 → 100644
View file @
ede28e8
package
com
.
dituhui
.
pea
.
pre
.
service
;
import
com.dituhui.pea.pre.opta.domain.VehicleRoutingSolution
;
import
java.sql.SQLException
;
/**
* @author zhangx
* <p>
* 排班算法数据准备
* 排班结果解析到dispatch_order(更新补充技术员工号、上门时间) ,order_appointment、order_request
*/
public
interface
PrepareService
{
/*
* 按小组、批次号组装问题对象
* 调用optaplaner计算输出结果
* */
VehicleRoutingSolution
prepareAndSolveSolution
(
String
groupId
,
String
batchNo
);
/*
* 将计算结果回写到dispatch_order表(更新补充技术员工号、上门时间)
* */
void
saveSolutionToDispatch
(
String
groupId
,
String
batchNo
,
VehicleRoutingSolution
solution
)
throws
SQLException
;
/*
* 将dispath_order 中的计算结果,回写到 order_request, order_appointment
* order_appointment(新增、更新)
* order_request(主要更新状态)
* */
void
extractDispatchToOrder
(
String
groupId
,
String
batchNo
)
throws
SQLException
;
}
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/service/impl/BatchServiceImpl.java
0 → 100644
View file @
ede28e8
package
com
.
dituhui
.
pea
.
pre
.
service
.
impl
;
import
com.dituhui.pea.pre.dao.DispatchBatchRepository
;
import
com.dituhui.pea.pre.entity.DispatchBatch
;
import
com.dituhui.pea.pre.service.BatchService
;
import
lombok.extern.slf4j.Slf4j
;
import
org.apache.commons.dbutils.QueryRunner
;
import
org.apache.commons.dbutils.handlers.ScalarHandler
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.stereotype.Service
;
import
org.springframework.transaction.annotation.Transactional
;
import
javax.sql.DataSource
;
import
java.sql.Connection
;
import
java.sql.SQLException
;
import
java.time.LocalDate
;
import
java.time.LocalDateTime
;
import
java.time.format.DateTimeFormatter
;
import
java.util.Optional
;
/**
* @author zhangx
*/
@Slf4j
@Service
public
class
BatchServiceImpl
implements
BatchService
{
@Autowired
DispatchBatchRepository
batchRepository
;
private
QueryRunner
queryRunner
;
public
BatchServiceImpl
(
DataSource
dataSource
)
{
this
.
queryRunner
=
new
QueryRunner
(
dataSource
);
}
// 生成最新批次日期
private
String
calcBatchDay
()
{
// 定义日期时间格式
DateTimeFormatter
formatter
=
DateTimeFormatter
.
ofPattern
(
"yyyy-MM-dd"
);
String
result
=
LocalDate
.
now
().
format
(
formatter
);
return
result
;
}
// 生成最新批次号
private
String
calcBatchNo
()
{
// 定义日期时间格式
DateTimeFormatter
formatter
=
DateTimeFormatter
.
ofPattern
(
"yyyyMMdd-HHmm"
);
// 将当前时间转换为字符串
String
result
=
LocalDateTime
.
now
().
format
(
formatter
);
return
result
;
}
// 查询当前小组内已配置有中心点的技术员数量
private
int
queryEnginerCount
(
String
groupId
)
{
int
result
=
0
;
String
sql
=
"select count(1) from engineer_info a left join engineer_business b on a.engineer_code=b.engineer_code "
+
" where group_id=? and b.x is not null and b.x !='' "
;
ScalarHandler
<
Long
>
scal
=
new
ScalarHandler
<>();
try
{
result
=
Math
.
toIntExact
(
queryRunner
.
query
(
sql
,
scal
,
groupId
));
}
catch
(
SQLException
e
)
{
throw
new
RuntimeException
(
e
);
}
return
result
;
}
// 查询待指派工单数量
private
int
queryOrderCount
(
String
groupId
,
String
batchDay
)
{
int
result
=
0
;
String
sql
=
"select count(1) from order_request where org_group_id=? "
+
" and status='OPEN' and appointment_status='NOT_ASSIGNED' and appointment_method like 'AUTO%' "
+
" and expect_time_begin between ? and ? "
+
" order by create_time asc "
;
ScalarHandler
<
Long
>
scal
=
new
ScalarHandler
<>();
try
{
long
size
=
queryRunner
.
query
(
sql
,
scal
,
groupId
,
batchDay
+
" 00:00:00"
,
batchDay
+
" 23:59:59"
);
result
=
Math
.
toIntExact
(
size
);
}
catch
(
SQLException
e
)
{
throw
new
RuntimeException
(
e
);
}
return
result
;
}
// 检查给定小组、日期是否有在运行的批次任务,没则返回,没有则创建
@Transactional
@Override
public
String
buildBatchNo
(
String
groupId
,
String
day
)
throws
SQLException
{
log
.
info
(
"准备批次数据, groupId:{}, day:{}"
,
groupId
,
day
);
String
batchNo
=
""
;
String
batchDay
=
""
;
Optional
<
DispatchBatch
>
optional
=
batchRepository
.
findByGroupIdAndBatchDate
(
groupId
,
day
);
if
(!
optional
.
isPresent
())
{
batchNo
=
calcBatchNo
();
batchDay
=
calcBatchDay
();
// 执行数据库操作
String
sqlInsert
=
"INSERT INTO `dispatch_batch` ( `group_id`, `batch_no`, `batch_date`, `engineer_num`, `order_num`, `start_time`, `end_time`, `status`) "
+
" VALUES(?, ?, ?, ?, ?, ?, ?, ?)"
;
queryRunner
.
execute
(
sqlInsert
,
groupId
,
batchNo
,
batchDay
,
0
,
0
,
LocalDateTime
.
now
(),
null
,
"RUNNING"
);
log
.
info
(
"生成新批次, groupId:{}, day:{}"
,
groupId
,
day
);
}
else
{
batchNo
=
optional
.
get
().
getBatchNo
();
batchDay
=
optional
.
get
().
getBatchDate
();
}
// int engCount = queryEnginerCount(groupId);
// int orderCount = queryOrderCount(groupId, batchDay);
log
.
info
(
"清理原批次数据, groupId:{}, day:{}"
,
groupId
,
day
);
queryRunner
.
execute
(
"delete from dispatch_engineer where group_id=? and batch_no=?"
,
groupId
,
batchNo
);
queryRunner
.
execute
(
"delete from dispatch_order where group_id=? and batch_no=?"
,
groupId
,
batchNo
);
log
.
info
(
"写入新批次技术员、工单数据, groupId:{}, day:{}"
,
groupId
,
day
);
String
sqlEngineer
=
"INSERT INTO dispatch_engineer (group_id, batch_no, engineer_code, engineer_name, x, y, max_num, max_minute, max_distance)\n"
+
"select a.group_id, ? , a.engineer_code, a.name , b.x, b.y , max_num, max_minute, max_distance from \n"
+
" engineer_info a left join engineer_business b \n"
+
" on a.engineer_code=b.engineer_code \n"
+
" where a.group_id=? and b.x is not null and b.x !=''\n"
+
" order by a.engineer_code asc"
;
int
engCount
=
queryRunner
.
execute
(
sqlEngineer
,
batchNo
,
groupId
);
String
sqlOrder
=
"INSERT INTO dispatch_order (group_id, batch_no, order_id , x, y, expect_time_begin, expect_time_end, tags, priority , skills , take_time )\n"
+
" select a.org_group_id, ? , a.order_id, a.x, a.y , \n"
+
" a.expect_time_begin, a.expect_time_end, a.tags, a.priority , concat(a.brand, a.type, a.skill) skills , b.take_time \n"
+
" from order_request a left join product_category b on (a.brand=b.brand and a.type=b.type and a.skill=b.skill )\n"
+
" where a.org_group_id=? and status='OPEN' and appointment_status='NOT_ASSIGNED' and appointment_method like 'AUTO%' \n"
+
" and expect_time_begin between ? and ? \n"
+
" order by a.expect_time_begin asc "
;
int
orderCount
=
queryRunner
.
execute
(
sqlOrder
,
batchNo
,
groupId
,
batchDay
+
" 00:00:00"
,
batchDay
+
" 23:59:59"
);
queryRunner
.
execute
(
"update dispatch_batch set engineer_num=? , order_num=? where group_id=? and batch_no=?"
,
engCount
,
orderCount
,
groupId
,
batchNo
);
log
.
info
(
"准备批次数据完成, groupId:{}, day:{}"
,
groupId
,
day
);
return
batchNo
;
}
}
project-pre-dispatch/src/main/java/com/dituhui/pea/pre/service/impl/PrepareServiceImpl.java
0 → 100644
View file @
ede28e8
This diff is collapsed.
Click to expand it.
project-pre-dispatch/src/main/resources/application.yaml
0 → 100644
View file @
ede28e8
server
:
port
:
8012
pre-dispatch
:
cron
:
expr
:
0 */5 8-18 * * ?
spring
:
application
:
name
:
project-pre-dispatch
jackson
:
default-property-inclusion
:
NON_NULL
# time-zone: GMT+8
date-format
:
yyyy-MM-dd HH:mm:ss
cloud
:
nacos
:
discovery
:
server-addr
:
127.0.0.1:8848
group
:
project
config
:
server-addr
:
127.0.0.1:8848
group
:
project
file-extension
:
yaml
import-check
:
# no config file
enabled
:
false
config
:
import
:
-
optional:nacos:project-order.yaml
# - optional:nacos:datasource-config.yaml
datasource
:
driver-class-name
:
com.mysql.cj.jdbc.Driver
url
:
jdbc:mysql://127.0.0.1:3306/saas_aftersale_test?serverTimezone=UTC
username
:
root
password
:
12345678
type
:
com.alibaba.druid.pool.DruidDataSource
redis
:
database
:
0
host
:
127.0.0.1
port
:
6379
password
:
123456
jedis
:
pool
:
max-active
:
32
min-idle
:
0
max-idle
:
8
max-wait
:
-1
jpa
:
show-sql
:
true
hibernate
:
ddl-auto
:
none
seata
:
application-id
:
${spring.application.name}
tx-service-group
:
${spring.application.name}-group
service
:
vgroup-mapping
:
project-order-group
:
default
grouplist
:
default
:
127.0.0.1:8091
project-pre-dispatch/src/main/resources/logback.xml
0 → 100644
View file @
ede28e8
<configuration>
<appender
name=
"CONSOLE"
class=
"ch.qos.logback.core.ConsoleAppender"
>
<encoder>
<pattern>
%d{yyyy-MM-dd'T'HH:mm:ss.SSSZ} %level [%thread] %logger{15} : %msg%n
</pattern>
</encoder>
</appender>
<root
level=
"INFO"
>
<appender-ref
ref=
"CONSOLE"
/>
</root>
</configuration>
project-pre-dispatch/src/test/java/com/dituhui/pea/pre/BatchServiceTest.java
0 → 100644
View file @
ede28e8
package
com
.
dituhui
.
pea
.
pre
;
import
com.dituhui.pea.pre.service.BatchService
;
import
com.dituhui.pea.pre.service.impl.BatchServiceImpl
;
import
lombok.extern.slf4j.Slf4j
;
import
org.junit.jupiter.api.Test
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.boot.test.context.SpringBootTest
;
import
java.sql.SQLException
;
@Slf4j
@SpringBootTest
public
class
BatchServiceTest
{
@Autowired
BatchService
batchService
;
String
groupId
=
"gsuzhou"
;
String
day
=
"2023-06-16"
;
@Test
public
void
test1
()
{
log
.
info
(
"init"
);
try
{
batchService
.
buildBatchNo
(
groupId
,
day
);
}
catch
(
SQLException
e
)
{
log
.
info
(
"error %s"
,
e
);
throw
new
RuntimeException
(
e
);
}
log
.
info
(
"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