This commit is contained in:
shuhongfan
2023-09-04 16:40:17 +08:00
commit cf5ac25c14
8267 changed files with 1305066 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
.idea
target/
*.iml

View File

@@ -0,0 +1,13 @@
FROM openjdk:11-jdk
LABEL maintainer="研究院研发组 <research@itcast.cn>"
# 时区修改为东八区
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
WORKDIR /app
ARG JAR_FILE=target/*.jar
ADD ${JAR_FILE} app.jar
EXPOSE 8080
ENTRYPOINT ["sh","-c","java -Djava.security.egd=file:/dev/./urandom -jar $JAVA_OPTS app.jar"]

View File

@@ -0,0 +1,67 @@
# sl-express-ms-courier使用手册
## 1.说明
**该模块为快递员相关微服务,主要提供根据经纬度查询范围内快递员的服务。**<br />主要功能:
- 条件查询快递员列表(结束取件时间当天快递员有排班)
## 2.使用
### 2.1导入依赖
如需使用快递员相关的服务功能需要引入快递员微服务api
```xml
<dependency>
<groupId>com.sl-express.ms.courier</groupId>
<artifactId>sl-express-ms-courier-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
```
### 2.2feign方法
```java
@FeignClient(name = "sl-express-ms-courier", contextId = "courier", path = "couriers")
@ApiIgnore
public interface CourierFeign {
/**
* 条件查询快递员列表(结束取件时间当天快递员有排班)
* 如果服务范围内无快递员,或满足服务范围的快递员无排班,则返回该网点所有满足排班的快递员
*
* @param agencyId 网点id
* @param longitude 用户地址的经度
* @param latitude 用户地址的纬度
* @param estimatedEndTime 结束取件时间
* @return 快递员id列表
*/
@GetMapping("{agencyId}/{longitude}/{latitude}")
List<Long> queryCourierIdListByCondition(@PathVariable("agencyId") Long agencyId,
@PathVariable("longitude") Double longitude,
@PathVariable("latitude") Double latitude,
@RequestParam("estimatedEndTime") Long estimatedEndTime);
}
```
### 2.3条件查询快递员接口
**接口地址**:`/couriers/{agencyId}/{longitude}/{latitude}`
**请求方式**:`GET`
**请求参数**:
| 参数名称 | 参数说明 | 请求类型 | 是否必须 | 数据类型 |
|------------------|--------|-------|------|----------------|
| agencyId | 网点id | path | true | integer(int64) |
| estimatedEndTime | 结束取件时间 | query | true | integer(int64) |
| latitude | 纬度 | path | true | number(double) |
| longitude | 经度 | path | true | number(double) |
## 3.服务架构
### 3.1条件查询快递员流程图
![条件查询快递员流程图](../../docs/zh-cn/assets/条件查询快递员列表流程图.png)

View File

@@ -0,0 +1,132 @@
<?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.sl-express</groupId>
<artifactId>sl-express-parent</artifactId>
<version>1.4</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.sl-express.ms.courier</groupId>
<artifactId>sl-express-ms-courier-service</artifactId>
<version>1.1-SNAPSHOT</version>
<description>快递员微服务</description>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<sl-express-common.version>1.2-SNAPSHOT</sl-express-common.version>
<sl-express-ms-base-api.version>1.1-SNAPSHOT</sl-express-ms-base-api.version>
<sl-express-ms-service-scope-api.version>1.1-SNAPSHOT</sl-express-ms-service-scope-api.version>
<sl-express-ms-work-api.version>1.1-SNAPSHOT</sl-express-ms-work-api.version>
<sl-express-ms-oms-api.version>1.1-SNAPSHOT</sl-express-ms-oms-api.version>
<sl-express-mq.version>1.1-SNAPSHOT</sl-express-mq.version>
<sl-express-ms-courier-domain.version>1.1-SNAPSHOT</sl-express-ms-courier-domain.version>
</properties>
<dependencies>
<dependency>
<groupId>com.sl-express.common</groupId>
<artifactId>sl-express-common</artifactId>
<version>${sl-express-common.version}</version>
</dependency>
<dependency>
<groupId>com.sl-express.ms.base</groupId>
<artifactId>sl-express-ms-base-api</artifactId>
<version>${sl-express-ms-base-api.version}</version>
</dependency>
<dependency>
<groupId>com.sl-express.ms.service-scope</groupId>
<artifactId>sl-express-ms-service-scope-api</artifactId>
<version>${sl-express-ms-service-scope-api.version}</version>
</dependency>
<dependency>
<groupId>com.sl-express.ms.courier</groupId>
<artifactId>sl-express-ms-courier-domain</artifactId>
<version>${sl-express-ms-courier-domain.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!--AMQP依赖包含RabbitMQ-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>${redisson.version}</version>
</dependency>
<dependency>
<groupId>com.sl-express.mq</groupId>
<artifactId>sl-express-mq</artifactId>
<version>${sl-express-mq.version}</version>
</dependency>
<dependency>
<groupId>com.sl-express.ms.oms</groupId>
<artifactId>sl-express-ms-oms-api</artifactId>
<version>${sl-express-ms-oms-api.version}</version>
</dependency>
<dependency>
<groupId>com.sl-express.ms.work</groupId>
<artifactId>sl-express-ms-work-api</artifactId>
<version>${sl-express-ms-work-api.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
<goal>build-info</goal>
</goals>
</execution>
</executions>
<configuration>
<!--指定主类-->
<mainClass>com.sl.CourierApplication</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,21 @@
package com.sl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.retry.annotation.EnableRetry;
@EnableRetry //开启重试机制
@SpringBootApplication
@Slf4j
@EnableDiscoveryClient
@EnableFeignClients
public class CourierApplication {
public static void main(String[] args) {
SpringApplication.run(CourierApplication.class, args);
}
}

View File

@@ -0,0 +1,39 @@
package com.sl.ms.courier.config;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import lombok.Data;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.SingleServerConfig;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
@Configuration
@EnableConfigurationProperties(RedisProperties.class)
@Data
public class RedissonConfiguration {
@Resource
private RedisProperties redisProperties;
@Bean
public RedissonClient redissonSingle() {
Config config = new Config();
SingleServerConfig serverConfig = config.useSingleServer()
.setAddress("redis://" + redisProperties.getHost() + ":" + redisProperties.getPort());
if (null != (redisProperties.getTimeout())) {
serverConfig.setTimeout(1000 * Convert.toInt(redisProperties.getTimeout().getSeconds()));
}
if (StrUtil.isNotEmpty(redisProperties.getPassword())) {
serverConfig.setPassword(redisProperties.getPassword());
}
return Redisson.create(config);
}
}

View File

@@ -0,0 +1,50 @@
package com.sl.ms.courier.controller;
import com.sl.ms.courier.service.CourierUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
@Slf4j
@RestController
@RequestMapping("/couriers")
@Api(tags = "快递员相关接口")
public class CourierController {
@Resource
private CourierUserService courierUserService;
/**
* 条件查询快递员列表(结束取件时间当天快递员有排班)
* 如果服务范围内无快递员,或满足服务范围的快递员无排班,则返回该网点所有满足排班的快递员
*
* @param agencyId 网点id
* @param longitude 用户地址的经度
* @param latitude 用户地址的纬度
* @param estimatedEndTime 结束取件时间
* @return 快递员id列表
*/
@ApiOperation("根据用户地址的坐标查询为其服务的快递员列表")
@GetMapping("{agencyId}/{longitude}/{latitude}")
@ApiImplicitParams({
@ApiImplicitParam(name = "longitude", value = "经度"),
@ApiImplicitParam(name = "latitude", value = "纬度"),
@ApiImplicitParam(name = "agencyId", value = "网点id"),
@ApiImplicitParam(name = "estimatedEndTime", value = "结束取件时间")
})
public ResponseEntity<List<Long>> queryCourierIdListByCondition(@PathVariable("agencyId") Long agencyId,
@PathVariable("longitude") Double longitude,
@PathVariable("latitude") Double latitude,
@RequestParam("estimatedEndTime") Long estimatedEndTime) {
return ResponseEntity.ok(courierUserService.queryCourierIdListByCondition(agencyId, longitude, latitude, estimatedEndTime));
}
}

View File

@@ -0,0 +1,19 @@
package com.sl.ms.courier.service;
import java.util.List;
public interface CourierUserService {
/**
* 条件查询快递员列表(结束取件时间当天快递员有排班)
* 如果服务范围内无快递员,或满足服务范围的快递员无排班,则返回该网点所有满足排班的快递员
*
* @param agencyId 网点id
* @param longitude 用户地址的经度
* @param latitude 用户地址的纬度
* @param estimatedEndTime 结束取件时间
* @return 快递员id列表
*/
List<Long> queryCourierIdListByCondition(Long agencyId, Double longitude, Double latitude, Long estimatedEndTime);
}

View File

@@ -0,0 +1,90 @@
package com.sl.ms.courier.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import com.sl.ms.base.api.common.WorkSchedulingFeign;
import com.sl.ms.base.domain.base.WorkSchedulingDTO;
import com.sl.ms.base.domain.enums.WorkUserTypeEnum;
import com.sl.ms.courier.service.CourierUserService;
import com.sl.ms.scope.api.ServiceScopeFeign;
import com.sl.ms.scope.dto.ServiceScopeDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Service
@Slf4j
public class CourierUserServiceImpl implements CourierUserService {
@Resource
private WorkSchedulingFeign workSchedulingFeign;
@Resource
private ServiceScopeFeign serviceScopeFeign;
/**
* 条件查询快递员列表(结束取件时间当天快递员有排班)
* 如果服务范围内无快递员,或满足服务范围的快递员无排班,则返回该网点所有满足排班的快递员
*
* @param agencyId 网点id
* @param longitude 用户地址的经度
* @param latitude 用户地址的纬度
* @param estimatedEndTime 结束取件时间
* @return 快递员id列表
*/
@Override
public List<Long> queryCourierIdListByCondition(Long agencyId, Double longitude, Double latitude, Long estimatedEndTime) {
log.info("当前机构id为{}", agencyId);
//1.根据经纬度查询服务范围内的快递员
List<ServiceScopeDTO> serviceScopeDTOS = serviceScopeFeign.queryListByLocation(2, longitude, latitude);
List<WorkSchedulingDTO> workSchedulingDTOS = new ArrayList<>();
List<Long> courierIds = null;
//2.如果服务范围内有快递员,则在其中筛选结束取件时间当天有排班的快递员
if (CollUtil.isNotEmpty(serviceScopeDTOS)) {
List<Long> bids = serviceScopeDTOS.stream().map(ServiceScopeDTO::getBid).collect(Collectors.toList());
log.info("根据经纬度查询到的快递员id有{}", bids);
String bidStr = CollUtil.isEmpty(bids) ? "" : CharSequenceUtil.join(",", bids);
workSchedulingDTOS = workSchedulingFeign.monthSchedule(bidStr, agencyId, WorkUserTypeEnum.COURIER.getCode(), estimatedEndTime);
log.info("满足服务范围、网点的快递员排班:{}", workSchedulingDTOS);
}
//2.1对满足服务范围、网点的快递员筛选排班
if (CollUtil.isNotEmpty(workSchedulingDTOS)) {
courierIds = workSchedulingDTOS.stream()
// 结束取件时间当天是否有排班
.filter(workSchedulingDTO -> workSchedulingDTO.getWorkSchedules().get(0))
.map(WorkSchedulingDTO::getUserId)
.collect(Collectors.toList());
log.info("服务范围、网点、排班均满足的快递员id有{}", courierIds);
}
//3.存在同时满足服务范围、网点、排班的快递员,直接返回
if (CollUtil.isNotEmpty(courierIds)) {
return courierIds;
}
//3.1 如果服务范围内没有快递员,或服务范围内的快递员没有排班,则查询该网点的任一有排班快递员
workSchedulingDTOS = workSchedulingFeign.monthSchedule(null, agencyId, WorkUserTypeEnum.COURIER.getCode(), estimatedEndTime);
log.info("查询该网点所有快递员排班:{}", workSchedulingDTOS);
if (CollUtil.isEmpty(workSchedulingDTOS)) {
return courierIds;
}
//3.2对满足网点的快递员筛选排班
courierIds = workSchedulingDTOS.stream()
// 结束取件时间当天是否有排班
.filter(workSchedulingDTO -> workSchedulingDTO.getWorkSchedules().get(0))
.map(WorkSchedulingDTO::getUserId)
.collect(Collectors.toList());
log.info("只满足网点、排班的快递员id有{}", courierIds);
return courierIds;
}
}

View File

@@ -0,0 +1,7 @@
_ ${spring.application.name} ${application.version}
___ | | ___ __ __ _ __ _ __ ___ ___ ___ Port: ${server.port}
/ __|| | _____ / _ \\ \/ /| '_ \ | '__|/ _ \/ __|/ __| Pid: ${pid} Profile(s): ${AnsiColor.GREEN}${spring.profiles.active}${AnsiColor.DEFAULT}
\__ \| ||_____|| __/ > < | |_) || | | __/\__ \\__ \
|___/|_| \___|/_/\_\| .__/ |_| \___||___/|___/ https://sl-express.itheima.net/
|_|

View File

@@ -0,0 +1,25 @@
server:
port: 18088
tomcat:
uri-encoding: UTF-8
threads:
max: 1000
min-spare: 30
spring:
cloud:
nacos:
username: nacos
password: nacos
server-addr: 192.168.150.101:8848
discovery:
namespace: ecae68ba-7b43-4473-a980-4ddeb6157bdc
ip: 192.168.150.1
config:
namespace: ecae68ba-7b43-4473-a980-4ddeb6157bdc
shared-configs: #共享配置
- data-id: shared-spring-seata.yml
group: SHARED_GROUP
refresh: false
- data-id: shared-spring-rabbitmq.yml
group: SHARED_GROUP
refresh: false

View File

@@ -0,0 +1,24 @@
server:
port: 18088
tomcat:
uri-encoding: UTF-8
threads:
max: 1000
min-spare: 30
spring:
cloud:
nacos:
username: nacos
password: vO5/dZ9,iL
server-addr: nacos-service.yjy-public-slwl-java-prod.svc.cluster.local:8848
discovery:
namespace: 92312ba8-1119-440f-81af-c29618df303b
config:
namespace: 92312ba8-1119-440f-81af-c29618df303b
shared-configs: #共享配置
- data-id: shared-spring-seata.yml
group: SHARED_GROUP
refresh: false
- data-id: shared-spring-rabbitmq.yml
group: SHARED_GROUP
refresh: false

View File

@@ -0,0 +1,24 @@
server:
port: 18088
tomcat:
uri-encoding: UTF-8
threads:
max: 1000
min-spare: 30
spring:
cloud:
nacos:
username: nacos
password: nacos
server-addr: 192.168.150.101:8848
discovery:
namespace: ecae68ba-7b43-4473-a980-4ddeb6157bdc
config:
namespace: ecae68ba-7b43-4473-a980-4ddeb6157bdc
shared-configs: #共享配置
- data-id: shared-spring-seata.yml
group: SHARED_GROUP
refresh: false
- data-id: shared-spring-rabbitmq.yml
group: SHARED_GROUP
refresh: false

View File

@@ -0,0 +1,24 @@
server:
port: 18088
tomcat:
uri-encoding: UTF-8
threads:
max: 1000
min-spare: 30
spring:
cloud:
nacos:
username: nacos
password: nacos
server-addr: nacos-service.yjy-public-slwl-java.svc.cluster.local:8848
discovery:
namespace: 92312ba8-1119-440f-81af-c29618df303b
config:
namespace: 92312ba8-1119-440f-81af-c29618df303b
shared-configs: #共享配置
- data-id: shared-spring-seata.yml
group: SHARED_GROUP
refresh: false
- data-id: shared-spring-rabbitmq.yml
group: SHARED_GROUP
refresh: false

View File

@@ -0,0 +1,23 @@
application:
version: v1.0
logging:
config: classpath:logback-spring.xml
spring:
application:
name: sl-express-ms-courier
profiles:
active: local
mvc:
pathmatch:
#解决异常swagger Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException
#因为Springfox使用的路径匹配是基于AntPathMatcher的而Spring Boot 2.6.X使用的是PathPatternMatcher
matching-strategy: ant_path_matcher
sl:
swagger:
package-path: com.sl.ms.courier.controller
title: 神领物流 - 快递员微服务接口文档
description: 该微服务完成快递员相关的业务。
contact-name: 传智教育·研究院
contact-url: http://www.itcast.cn/
contact-email: yjy@itcast.cn
version: ${application.version}

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--scan: 当此属性设置为true时配置文件如果发生改变将会被重新加载默认值为true。-->
<!--scanPeriod: 设置监测配置文件是否有修改的时间间隔如果没有给出时间单位默认单位是毫秒。当scan为true时此属性生效。默认的时间间隔为1分钟。-->
<!--debug: 当此属性设置为true时将打印出logback内部日志信息实时查看logback运行状态。默认值为false。-->
<configuration debug="false" scan="false" scanPeriod="60 seconds">
<springProperty scope="context" name="appName" source="spring.application.name"/>
<!--文件名-->
<property name="logback.appname" value="${appName}"/>
<!--文件位置-->
<property name="logback.logdir" value="/data/logs"/>
<!-- 定义控制台输出 -->
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} - [%thread] - %-5level - %logger{50} - %msg%n</pattern>
</layout>
</appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUG</level>
</filter>
<File>${logback.logdir}/${logback.appname}/${logback.appname}.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${logback.logdir}/${logback.appname}/${logback.appname}.%d{yyyy-MM-dd}.log.zip</FileNamePattern>
<maxHistory>90</maxHistory>
</rollingPolicy>
<encoder>
<charset>UTF-8</charset>
<pattern>%d [%thread] %-5level %logger{36} %line - %msg%n</pattern>
</encoder>
</appender>
<!--evel:用来设置打印级别大小写无关TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF-->
<!--不能设置为INHERITED或者同义词NULL。默认是DEBUG。-->
<root level="INFO">
<appender-ref ref="stdout"/>
</root>
</configuration>

View File

@@ -0,0 +1,43 @@
package com.sl.ms.courier;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.json.JSONUtil;
import com.sl.ms.courier.service.CourierUserService;
import com.sl.ms.scope.api.ServiceScopeFeign;
import com.sl.ms.scope.dto.ServiceScopeDTO;
import com.sl.transport.common.vo.OrderMsg;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import java.util.List;
@SpringBootTest
@Slf4j
public class CourierTest {
@Resource
private CourierUserService courierUserService;
@Resource
private ServiceScopeFeign serviceScopeFeign;
@Test
void conditionSearch() {
String msg = "{\"orderId\":1561894910655795201,\"taskType\":1,\"agencyId\":996725895563695297,\"estimatedEndTime\":1661223600000,\"longitude\":116.365914,\"latitude\":39.985547,\"created\":1661219739961}";
log.info("接收到订单的消息 >>> msg = {}", msg);
OrderMsg orderMsg = JSONUtil.toBean(msg, OrderMsg.class);
Long agencyId = orderMsg.getAgencyId(); //网点id
// 通过快递员微服务查询 可以为发件人服务的快递员(正常上班、服务范围内)
Double longitude = orderMsg.getLongitude();
Double latitude = orderMsg.getLatitude();
List<Long> courierIds = courierUserService.queryCourierIdListByCondition(agencyId, longitude, latitude, LocalDateTimeUtil.toEpochMilli(orderMsg.getEstimatedEndTime()));
System.out.println(courierIds);
}
@Test
void serviceScopeTest() {
List<ServiceScopeDTO> serviceScopeDTOS = serviceScopeFeign.queryListByLocation(2, 116.365914, 39.985547);
System.out.println(serviceScopeDTOS);
}
}