Commit 7de5cbdd by 刘鑫

feat(申请加单): 申请加单除排除一家多单和需要配件逻辑

1 parent d856f1fb
......@@ -82,6 +82,11 @@
<artifactId>geodesy</artifactId>
<version>1.1.3</version>
</dependency>
<dependency>
<groupId>org.locationtech.spatial4j</groupId>
<artifactId>spatial4j</artifactId>
<version>0.8</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
......
package com.dituhui.pea.order.common;
import org.locationtech.spatial4j.context.SpatialContext;
import org.locationtech.spatial4j.distance.DistanceUtils;
import org.locationtech.spatial4j.shape.Rectangle;
/**
* 计算外接正方形坐标 以及距离
*/
public final class Stapial4jUtil {
private static final SpatialContext SPATIAL_CONTEXT = SpatialContext.GEO;
/**
* 利用开源库计算外接正方形坐标
*
* @param distance 距离,单位km
* @param userLng 当前经度
* @param userLat 当前纬度
* @return
*/
public static Rectangle getRectangle(double distance, double userLng, double userLat) {
return SPATIAL_CONTEXT.getDistCalc()
.calcBoxByDistFromPt(SPATIAL_CONTEXT.makePoint(userLng, userLat),
distance * DistanceUtils.KM_TO_DEG, SPATIAL_CONTEXT, null);
}
/***
* 球面中,两点间的距离(第三方库方法)
*
* @param longitude 经度1
* @param latitude 纬度1
* @param userLng 经度2
* @param userLat 纬度2
* @return 返回距离,单位km
*/
public static double getDistance(double longitude, double latitude, double userLng, double userLat) {
return SPATIAL_CONTEXT.calcDistance(SPATIAL_CONTEXT.makePoint(userLng, userLat),
SPATIAL_CONTEXT.makePoint(longitude, latitude)) * DistanceUtils.DEG_TO_KM;
}
/**
* 工具类私有化构造方法, 不允许外部调用
*/
private Stapial4jUtil() {
}
}
......@@ -23,7 +23,10 @@ import com.dituhui.pea.order.service.OrderCreateService;
import com.dituhui.pea.order.service.OrderInfoService;
import com.dituhui.pea.order.service.PeaOuterAPIService;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
......@@ -245,10 +248,24 @@ public class PeaApiController {
* @apiNote 技术员发起申请,根据技术员当前位置和空闲时间段,返回附近的n张尚未分配,且不需要备件的工单,技术员逐条顺序联系客户确认
*/
@PostMapping("/order/increase/query")
public Result<Order> orderIncrease(@Validated @RequestBody EngineerOrderParam requestParam) {
public Result<OrderResult> orderIncrease(@Validated @RequestBody EngineerOrderParam requestParam) {
return Result.success(peaOuterAPIService.orderIncreaseQuery(requestParam.getEngineerCode(), requestParam.getLocation(),
requestParam.getIdleDuration()));
List<Order> orders = peaOuterAPIService.orderIncreaseQuery(requestParam.getEngineerCode(), requestParam.getLocation(),
requestParam.getIdleDuration());
return Result.success(new OrderResult(orders));
}
/**
* 申请加单返回值
*/
@Setter
@Getter
@AllArgsConstructor
class OrderResult {
/**
* 待处理工单列表
*/
private List<Order> orders;
}
/**
......
package com.dituhui.pea.order.dao;
import com.dituhui.pea.order.entity.EngineerSkillGroupEntity;
import com.dituhui.pea.order.entity.SkillGroupEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.util.List;
......@@ -20,4 +22,14 @@ public interface EngineerSkillGroupDao extends JpaRepository<EngineerSkillGroupE
List<EngineerSkillGroupEntity> findBySkillGroupCode(String skillGroupCode);
List<EngineerSkillGroupEntity> findBySkillGroupCodeAndStatus(String skillGroupCode, boolean status);
/**
* 工程师绑定的技能组
*
* @param engineerCode 工程师代码
* @return 工程师技能组信息
*/
@Query(value = "SELECT esg.* FROM engineer_skill_group esg LEFT JOIN skill_group sg ON sg.skill_group_code = esg.skill_group_code \n" +
" WHERE esg.engineer_code = :engineerCode", nativeQuery = true)
List<EngineerSkillGroupEntity> getEngineerSkillGroup(String engineerCode);
}
......@@ -7,6 +7,7 @@ import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
public interface OrderInfoDao extends JpaRepository<OrderInfoEntity, Long>, JpaSpecificationExecutor<OrderInfoEntity> {
......@@ -50,4 +51,30 @@ public interface OrderInfoDao extends JpaRepository<OrderInfoEntity, Long>, JpaS
*/
@Query(value = "SELECT * FROM order_info oi WHERE (engineer_code = :engineerCode OR engineer_code_sub =:engineerCode) AND dt = :targetDate", nativeQuery = true)
List<OrderInfoEntity> getEngineerDateOrder(String engineerCode, String targetDate);
/**
* 获取矩形范围内的工单
*
* @param maxLongitude 矩形中最大的经度
* @param minLongitude 矩形中最小的经度
* @param maxLatitude 矩形中最大的经度
* @param minLatitude 矩形中最小的经度
* @return 矩形范围内的工单
*/
@Query(value = "SELECT * FROM order_info oi WHERE CONVERT(oi.x, decimal(18,8)) < :maxLongitude AND CONVERT(oi.x, decimal(18,8)) >= :minLongitude " +
" AND CONVERT(oi.y, decimal(18,8)) < :maxLatitude AND CONVERT(oi.y, decimal(18,8)) >= :minLatitude", nativeQuery = true)
List<OrderInfoEntity> getRectangleOrder(Double maxLongitude, Double minLongitude, Double maxLatitude, Double minLatitude);
/**
* 查询指定时间后满足技能要求 且未分配的工单信息
*
* @param skillGroupCode 技能组信息
* @param dateTime 时间
* @return 满足条件的工单信息
*/
@Query(value = "SELECT oi.* FROM skill_info si LEFT JOIN order_info oi ON oi.brand = si.brand AND oi.type = si.type AND oi.skill = si.skill " +
" WHERE si.skill_group_code = :skillGroupCode AND oi.expect_time_begin >= :dateTime " +
" AND (appointment_status = 'INIT' AND order_status != 'CANCELED') ", nativeQuery = true)
List<OrderInfoEntity> getSkillGroupOrder(String skillGroupCode, LocalDateTime dateTime);
}
......@@ -2,7 +2,12 @@ package com.dituhui.pea.order.entity;
import lombok.Data;
import javax.persistence.*;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import java.time.LocalDateTime;
@Data
......@@ -23,7 +28,7 @@ public class SkillGroupEntity {
@Column(length = 50, nullable = false)
private String description;
@Column(length = 20, nullable = false)
@Column(name = "category", length = 20, nullable = false)
private String category;
@Column(length = 100, nullable = false)
......
......@@ -33,7 +33,7 @@ public interface PeaOuterAPIService {
* @param idleDuration 空闲时长 至少空闲多久以上,单位:分钟
* @return 不需要的备件且符合条件的多个未分配工单
*/
Order orderIncreaseQuery(String engineerCode, Location location, Integer idleDuration);
List<Order> orderIncreaseQuery(String engineerCode, Location location, Integer idleDuration);
OrderDTO.AppointResult engineerCode(String engineerCode, LocalDate localDate);
......
package com.dituhui.pea.order.service.impl;
import com.dituhui.pea.common.BusinessException;
import com.dituhui.pea.order.common.Stapial4jUtil;
import com.dituhui.pea.order.common.jackson.DateUtil;
import com.dituhui.pea.order.dao.EngineerSkillGroupDao;
import com.dituhui.pea.order.dao.OrderInfoDao;
import com.dituhui.pea.order.dao.OrgGroupDao;
import com.dituhui.pea.order.dao.SkillGroupDao;
import com.dituhui.pea.order.dto.param.EstimateDTO;
import com.dituhui.pea.order.dto.param.Location;
import com.dituhui.pea.order.dto.param.Order;
import com.dituhui.pea.order.dto.param.OrderDTO;
import com.dituhui.pea.order.entity.EngineerSkillGroupEntity;
import com.dituhui.pea.order.entity.OrderInfoEntity;
import com.dituhui.pea.order.entity.OrgGroupEntity;
import com.dituhui.pea.order.enums.OrderFlowEnum;
import com.dituhui.pea.order.entity.SkillGroupEntity;
import com.dituhui.pea.order.service.PeaOuterAPIService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.locationtech.spatial4j.shape.Rectangle;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
/**
......@@ -34,6 +46,7 @@ public class PeaOuterAPIServiceImpl implements PeaOuterAPIService {
*/
private final OrgGroupDao orgGroupDao;
private final OrderInfoDao orderInfoDao;
private final EngineerSkillGroupDao engineerSkillGroupDao;
@Override
public EstimateDTO.VisitResult estimateVisitService(String brand, String productType, String serviceType, Location clientLocation) {
......@@ -60,29 +73,82 @@ public class PeaOuterAPIServiceImpl implements PeaOuterAPIService {
@Override
public Order orderIncreaseQuery(String engineerCode, Location location, Integer idleDuration) {
OrderInfoEntity sss = orderInfoDao.findTopBySkillAndAppointmentStatus("标准安装", OrderFlowEnum.INIT.name());
Order order = new Order();
order.setOrderId(sss.getOrderId());
order.setName(sss.getName());
order.setPhone(sss.getPhone());
order.setBrand(sss.getBrand());
order.setProductType(sss.getType());
order.setFaultDescribe(sss.getFaultDescribe());
order.setExpectTimeBegin(DateUtil.toDate(sss.getExpectTimeBegin()));
order.setExpectTimeEnd(DateUtil.toDate(sss.getExpectTimeEnd()));
order.setApplyNote(sss.getApplyNote());
public List<Order> orderIncreaseQuery(String engineerCode, Location location, Integer idleDuration) {
//查询工程师所有技能组
List<EngineerSkillGroupEntity> skillGroups = engineerSkillGroupDao.getEngineerSkillGroup(engineerCode);
if (CollectionUtils.isEmpty(skillGroups)) {
throw new BusinessException("当前工程师未配置技能信息");
}
LocalDateTime targetTime = LocalDateTime.now();
Set<OrderInfoEntity> orderList = new HashSet<>();
//查询符合条件(技能、)的工单
for (EngineerSkillGroupEntity skillGroup : skillGroups) {
final String groupCode = skillGroup.getSkillGroupCode();
List<OrderInfoEntity> skillGroupOrder = orderInfoDao.getSkillGroupOrder(groupCode, targetTime);
orderList.addAll(skillGroupOrder);
}
//TODO 排除一家多台、需要配件工单
//每分钟0.3167 KM, 每小时19KM
//查询 地址范围内工单(过滤掉所需耗时大于空闲时长的工单), 范围根据空闲时长计算
double speed = 0.3167;
double distance = speed * idleDuration;
List<OrderInfoEntity> infoEntities = nearBySearch(distance, location.getLongitude(), location.getLatitude(), orderList);
infoEntities = Optional.ofNullable(infoEntities).orElse(Collections.emptyList())
.stream()
//过滤掉所需耗时大于空闲时长的工单
.filter(order -> order.getTakeTime() <= idleDuration)
.collect(Collectors.toList());
Location orderLocation = new Location();
orderLocation.setAddress(sss.getAddress());
orderLocation.setLongitude(Double.parseDouble(sss.getX()));
orderLocation.setLatitude(Double.parseDouble(sss.getY()));
order.setLocation(orderLocation);
return infoEntities.stream().map(sss -> {
Order order = new Order();
order.setOrderId(sss.getOrderId());
order.setName(sss.getName());
order.setPhone(sss.getPhone());
order.setBrand(sss.getBrand());
order.setProductType(sss.getType());
order.setServiceType(sss.getSkill());
order.setTakeTime(sss.getTakeTime());
order.setFaultDescribe(sss.getFaultDescribe());
order.setExpectTimeBegin(DateUtil.toDate(sss.getExpectTimeBegin()));
order.setExpectTimeEnd(DateUtil.toDate(sss.getExpectTimeEnd()));
order.setApplyNote(sss.getApplyNote());
Location orderLocation = new Location();
orderLocation.setAddress(sss.getAddress());
orderLocation.setLongitude(Double.parseDouble(sss.getX()));
orderLocation.setLatitude(Double.parseDouble(sss.getY()));
order.setLocation(orderLocation);
return order;
}).collect(Collectors.toList());
}
return order;
public List<OrderInfoEntity> nearBySearch(double distance, double userLng, double userLat, Set<OrderInfoEntity> orderList) {
//1 获取外切正方形最大最小经纬度
Rectangle rectangle = Stapial4jUtil.getRectangle(distance, userLng, userLat);
BigDecimal maxX = BigDecimal.valueOf(rectangle.getMaxX());
BigDecimal minX = BigDecimal.valueOf(rectangle.getMinX());
BigDecimal minY = BigDecimal.valueOf(rectangle.getMinY());
BigDecimal maxY = BigDecimal.valueOf(rectangle.getMaxY());
//2.获取位置在正方形内的所有工单
List<OrderInfoEntity> rectangleOrder = new ArrayList<>(orderList);
rectangleOrder = rectangleOrder.stream().filter(order -> {
//过滤满足距离要求的工单
BigDecimal longitude = new BigDecimal(order.getX());
BigDecimal latitude = new BigDecimal(order.getY());
return (longitude.compareTo(maxX) < 0 && longitude.compareTo(minX) >= 0) && (latitude.compareTo(maxY) < 0 && latitude.compareTo(minY) >= 0);
}
)
//3.剔除半径超过指定距离的多余工单
.filter(a -> Stapial4jUtil.getDistance(Double.parseDouble(a.getX()), Double.parseDouble(a.getY()),
userLng, userLat) <= distance)
.collect(Collectors.toList());
return rectangleOrder;
}
@Override
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!