Commit 25d10fa9 by 刘鑫

feat(技能组容量): 技能组容量全天、半天返回

1 parent 7280845c
......@@ -3,23 +3,45 @@ package com.dituhui.pea.order.controller;
import com.dituhui.pea.common.Result;
import com.dituhui.pea.order.common.jackson.DateUtil;
import com.dituhui.pea.order.common.jackson.JsonUtil;
import com.dituhui.pea.order.dto.param.*;
import com.dituhui.pea.order.dao.TableCodeCheckDao;
import com.dituhui.pea.order.dto.param.BaseDistance;
import com.dituhui.pea.order.dto.param.BaseDistanceParam;
import com.dituhui.pea.order.dto.param.CapacityQueryDTO;
import com.dituhui.pea.order.dto.param.EngineerCalendarResultDTO;
import com.dituhui.pea.order.dto.param.EngineerOrderParam;
import com.dituhui.pea.order.dto.param.EstimateDTO;
import com.dituhui.pea.order.dto.param.Order;
import com.dituhui.pea.order.dto.param.OrderConfirmParam;
import com.dituhui.pea.order.dto.param.OrderConfirmResult;
import com.dituhui.pea.order.dto.param.OrderDTO;
import com.dituhui.pea.order.entity.TypeCodeCheckTableEntity;
import com.dituhui.pea.order.service.CapacityQueryService;
import com.dituhui.pea.order.service.EngineerCalendarService;
import com.dituhui.pea.order.service.OrderCreateService;
import com.dituhui.pea.order.service.PeaOuterAPIService;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.stream.Collectors;
/**
* PEA 对外接口
......@@ -32,8 +54,9 @@ public class PeaApiController {
private final EngineerCalendarService engineerCalendarService;
private final PeaOuterAPIService peaOuterAPIService;
@Autowired
private OrderCreateService orderCreateService;
private final CapacityQueryService capacityQueryService;
private final OrderCreateService orderCreateService;
private final TableCodeCheckDao tableCodeCheckDao;
private static final String capacity = "{\"groupId\": \"G100038\", \"params\": {\"beginDate\": \"2023-07-21\", \"endDate\": \"2023-07-27\", \"services\": [{\"brand\": \"博世\", \"productType\": \"多门冰箱\", \"serviceType\": \"商场样机安装\"}], \"location\": {\"addressId\":\"21231231\", \"latitude\": 120.608463, \"longitude\": 31.318442, \"name\": \"江苏省苏州市姑苏区蒋庙前\", \"address\": \"江苏省苏州市姑苏区解放大街123号\"}}, \"takeTime\": 720, \"datas\": [{\"date\": \"2023-07-21\", \"segments\": [{\"maxDuration\": 90, \"name\": \"全天\", \"beginTime\": \"2023-07-21 08:00:00\", \"endTime\": \"2023-07-21 17:59:59\", \"status\": 1, \"remain\": 3500}, {\"maxDuration\": 45, \"name\": \"上午\", \"beginTime\": \"2023-07-21 08:00:00\", \"endTime\": \"2023-07-21 11:59:59\", \"status\": 1, \"remain\": 2500}, {\"maxDuration\": 60, \"name\": \"下午\", \"beginTime\": \"2023-07-21 13:00:00\", \"endTime\": \"2023-07-21 17:59:59\", \"status\": 0, \"remain\": 1000}, {\"maxDuration\": 60, \"name\": \"时间段\", \"beginTime\": \"2023-07-21 13:00:00\", \"endTime\": \"2023-07-21 15:00:00\", \"status\": 1, \"remain\": 480}]}]}";
......@@ -83,23 +106,34 @@ public class PeaApiController {
* @return 容量列表
*/
@PostMapping("/capacity/query")
public Result<CapacityQueryDTO.Result> capacityQuery(@Validated @RequestBody CapacityQueryDTO.Request reqDTO) throws IOException {
public Result<CapacityQueryDTO.Result> capacityQuery(@Validated @RequestBody CapacityQueryDTO.Request reqDTO) {
//查询日期起止参数限制为一月
ArrayList<String> strings = new ArrayList<>();
strings.add(half_capacity);
strings.add(day_capacity);
strings.add(half_capacity2);
strings.add(date_capacity);
Random random = new Random();
int i = random.nextInt(4);
CapacityQueryDTO.Result result = JsonUtil.parse(strings.get(i), CapacityQueryDTO.Result.class).get();
result.setParams(reqDTO);
return Result.success(result);
LocalDate startDate = reqDTO.getBeginDate().toInstant().atZone(ZoneId.of("+8")).toLocalDate();
LocalDate endDate = reqDTO.getEndDate().toInstant().atZone(ZoneId.of("+8")).toLocalDate();
List<CapacityQueryDTO.Service> services = reqDTO.getServices();
List<CapacityQueryDTO.Service> convertService = services.stream()
.map(source -> {
TypeCodeCheckTableEntity brand = tableCodeCheckDao.findByTypeAndCode("BRAND", source.getBrand());
TypeCodeCheckTableEntity type = tableCodeCheckDao.findByTypeAndCode("TYPE", source.getProductType());
TypeCodeCheckTableEntity skill = tableCodeCheckDao.findByTypeAndCode("SKILL", source.getServiceType());
CapacityQueryDTO.Service service = new CapacityQueryDTO.Service();
service.setBrand(fixBrand(brand.getName()));
service.setProductType(type.getName());
service.setServiceType(skill.getName());
return service;
}).collect(Collectors.toList());
return capacityQueryService.matchCapacityData(convertService, reqDTO.getLocation(), startDate, endDate);
}
private String fixBrand(String brand) {
if (!brand.equals("嘉格纳")) {
return "博世/西门子以及其他品牌";
} else {
return brand;
}
}
/**
* 4.2 改派到人容量看板查询
......@@ -117,7 +151,7 @@ public class PeaApiController {
@RequestParam("beginDate") Date beginDate,
@DateTimeFormat(pattern = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
Date endDate) throws IOException {
Date endDate) throws IOException {
ArrayList<String> strings = new ArrayList<>();
strings.add(half_capacity);
......
......@@ -136,7 +136,7 @@ public class CapacityQueryDTO {
/**
* 最大可用时长, 单位: 分钟
*/
private int maxDuration;
private long maxDuration;
/**
* 容量名称 全天/上午/下午/时间段
......@@ -165,7 +165,7 @@ public class CapacityQueryDTO {
/**
* 剩余容量
*/
private int remain;
private long remain;
}
......
......@@ -92,7 +92,31 @@ public class CalcEngineerCapacityScheduler {
List<OrderInfoEntity> orders = orderInfoDao.findByDtAndEngineerCode(DateUtils.localDateFromStr(date), engineerCode);
// 根据capacity_engineer_calendar和order_info,来确定当天剩下的最大连续时间区块;
EngineerBusinessEntity businessEntity = engineerBusinessDao.getByEngineerCode(engineerCode);
//TODO 就算单小时时间段工程师技能已用容量存储至时间切片工程师时间表内
// 就算单小时时间段工程师技能已用容量存储至时间切片工程师时间表内
extracted(date, engineerCode, orders, ss);
int used = orders.stream().map(e -> {
if (ss.contains(e.getOrderStatus())) {
return 0;
} else {
return e.getTakeTime();
}
}).mapToInt(Integer::intValue).sum();
long cnt = orders.stream()
.filter(e -> !ss.contains(e.getOrderStatus()))
.count();
long max = getMaxRemainBlock(date, engineerCode, orders, businessEntity);
log.info("正在处理: 日期[{}]技术员[{}]容量相关信息 ==> used:{}, orderCnt:{}, maxDuration:{}", date, engineerCode, used, cnt, max);
statEntity.setOrderCount((int) cnt);
statEntity.setCapUsed(used);
statEntity.setCapLeft(statEntity.getCapTotal() - used);
statEntity.setMaxDuration((int) max);
statEntity.setUpdateTime(LocalDateTime.now());
capacityEngineerStatDao.save(statEntity);
}
private void extracted(String date, String engineerCode, List<OrderInfoEntity> orders, Set<String> ss) {
//查询时间片容量
List<CapacityEngineerSliceUsedEntity> engineerTimeSlice = engineerSliceUsedCapacityDao.findByWorkdayAndEngineerCode(date, engineerCode);
for (CapacityEngineerSliceUsedEntity sliceCap : engineerTimeSlice) {
......@@ -127,29 +151,10 @@ public class CalcEngineerCapacityScheduler {
}
sliceCap.setCapLeft(lengthOfLeave);
//TODO 更新时间片统计
}
int used = orders.stream().map(e -> {
if (ss.contains(e.getOrderStatus())) {
return 0;
} else {
return e.getTakeTime();
}
}).mapToInt(Integer::intValue).sum();
long cnt = orders.stream()
.filter(e -> !ss.contains(e.getOrderStatus()))
.count();
long max = getMaxRemainBlock(date, engineerCode, orders, businessEntity);
log.info("正在处理: 日期[{}]技术员[{}]容量相关信息 ==> used:{}, orderCnt:{}, maxDuration:{}", date, engineerCode, used, cnt, max);
statEntity.setOrderCount((int) cnt);
statEntity.setCapUsed(used);
statEntity.setCapLeft(statEntity.getCapTotal() - used);
statEntity.setMaxDuration((int) max);
statEntity.setUpdateTime(LocalDateTime.now());
capacityEngineerStatDao.save(statEntity);
engineerSliceUsedCapacityDao.saveAll(engineerTimeSlice);
}
private long getMaxRemainBlock(String date, String engineerCode, List<OrderInfoEntity> orders, EngineerBusinessEntity businessEntity) {
// 根据capacity_engineer_calendar和order_info,来确定当天剩下的最大连续时间区块;
LocalDateTime startTime = DateUtils.localDateTimeFromStr(String.format("%s %s:00", date, businessEntity.getWorkOn()));
......
......@@ -20,7 +20,7 @@ public interface CapacityQueryService {
* @param endDate 结束日期
* @return 满足对应技能工作队的容量(三种类型与分站绑定)
*/
Result<?> matchCapacityData(List<CapacityQueryDTO.Service> services, Location location, LocalDate beginDate, LocalDate endDate);
Result<CapacityQueryDTO.Result> matchCapacityData(List<CapacityQueryDTO.Service> services, Location location, LocalDate beginDate, LocalDate endDate);
/**
* 查询单个工程师指定日期的容量状态
......
......@@ -6,6 +6,8 @@ import com.dituhui.pea.order.common.CapacityUtils;
import com.dituhui.pea.order.common.DateUtils;
import com.dituhui.pea.order.common.EngineerUtils;
import com.dituhui.pea.order.common.SaasUtils;
import com.dituhui.pea.order.common.jackson.DateTimeUtil;
import com.dituhui.pea.order.common.jackson.DateUtil;
import com.dituhui.pea.order.dao.CapacityEngineerStatDao;
import com.dituhui.pea.order.dao.EngineerInfoDao;
import com.dituhui.pea.order.dao.EngineerSkillDao;
......@@ -24,6 +26,7 @@ import com.dituhui.pea.order.entity.EngineerSkillEntity;
import com.dituhui.pea.order.entity.MapBlockInfoEntity;
import com.dituhui.pea.order.entity.OrgTeamEntity;
import com.dituhui.pea.order.entity.SkillInfoEntity;
import com.dituhui.pea.order.entity.TimeSliceEntity;
import com.dituhui.pea.order.feign.ISaaSRemoteService;
import com.dituhui.pea.order.service.CapacityQueryService;
import com.dituhui.pea.pojo.fendan.FendanDTO;
......@@ -36,10 +39,13 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.OptionalLong;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
......@@ -75,8 +81,9 @@ public class CapacityQueryServiceImpl implements CapacityQueryService {
private EngineerSliceUsedCapacityDao engineerSliceUsedCapacityDao;
@Override
public Result<?> matchCapacityData(List<CapacityQueryDTO.Service> services, Location location, LocalDate beginDate, LocalDate endDate) {
//TODO 转换品牌、产品类型、 技能代码为对应汉字
public Result<CapacityQueryDTO.Result> matchCapacityData(List<CapacityQueryDTO.Service> services, Location location, LocalDate beginDate, LocalDate endDate) {
//转换品牌、产品类型、 技能代码为对应汉字
//1:根据经纬度分单获取面
FendanDTO fendanDTO = new FendanDTO();
final String addressLongLat = location.getLongitude() + "," + location.getLatitude();
......@@ -88,8 +95,7 @@ public class CapacityQueryServiceImpl implements CapacityQueryService {
//2:根据查询出区划匹配分站信息
List<String> blockIds = blockInfoList.stream().map(SaasUtils.BlockInfo::getBlockId).distinct().collect(Collectors.toList());
// List<MapBlockInfoEntity> mapBlockInfoEntities = mapBlockInfoDao.findByBlockIdIn(blockIds);
List<MapBlockInfoEntity> mapBlockInfoEntities = new ArrayList<>();
List<MapBlockInfoEntity> mapBlockInfoEntities = mapBlockInfoDao.findByAreaIdsIn(blockIds);
if (CollectionUtils.isEmpty(mapBlockInfoEntities)) {
return Result.failed(StatusCodeEnum.FENDAN_TEAM_UNMATCHED);
}
......@@ -114,19 +120,95 @@ public class CapacityQueryServiceImpl implements CapacityQueryService {
allFulfillEngineer.addAll(engineerInfoEntities);
}
log.info("[matchCapacityData]【符合技能要求的工程师总数为:{} 个】", allFulfillEngineer.size());
//计算所有查询技能的所需耗时 (querySkillGroup)
int totalTakeTime = querySkillGroup.stream()
final int totalTakeTime = querySkillGroup.stream()
.mapToInt(SkillInfoEntity::getTakeTime)
.sum();
//TODO 查询单个工程师日期范围内的技能容量信息 后加和汇总
//查询单个工程师日期范围内的技能容量信息 后加和汇总
List<TimeSliceEntity> halfDayTypeTimeSlice = timeSliceDao.findByType("HALF_DAY");
List<TimeSliceEntity> allDayTypeTimeSlice = timeSliceDao.findByType("ALL_DAY");
LocalDate currentDate = beginDate;
//定义返回值
CapacityQueryDTO.Result result = new CapacityQueryDTO.Result();
result.setGroupId(groupId);
result.setTakeTime(totalTakeTime);
List<CapacityQueryDTO.Data> datas = new ArrayList<>();
while (!currentDate.isAfter(endDate)) {
List<CapacityEngineerSliceUsedEntity> allEngineerTimeSlice = new ArrayList<>();
for (EngineerInfoEntity engineerInfo : allFulfillEngineer) {
List<CapacityEngineerSliceUsedEntity> engineerTimeSlice = engineerSliceUsedCapacityDao
.findByWorkdayAndEngineerCode(DateTimeUtil.formatDate(currentDate), engineerInfo.getEngineerCode());
allEngineerTimeSlice.addAll(engineerTimeSlice);
}
//计算所有工程师半天类型容量
List<CapacityQueryDTO.Segment> engineerHalfDay = getEngineerTypeDay(halfDayTypeTimeSlice,
allEngineerTimeSlice, currentDate, totalTakeTime);
//计算所有工程师全天天类型容量
List<CapacityQueryDTO.Segment> engineerAllDay = getEngineerTypeDay(allDayTypeTimeSlice,
allEngineerTimeSlice, currentDate, totalTakeTime);
//TODO 时间段
CapacityQueryDTO.Data data = new CapacityQueryDTO.Data();
data.setDate(DateUtil.toDate(currentDate));
List<CapacityQueryDTO.Segment> objects = new ArrayList<>();
objects.addAll(engineerHalfDay);
objects.addAll(engineerAllDay);
data.setSegments(objects);
datas.add(data);
currentDate = currentDate.plusDays(1);
}
result.setDatas(datas);
return Result.success(result);
}
return null;
private List<CapacityQueryDTO.Segment> getEngineerTypeDay(List<TimeSliceEntity> typeTimeSlice,
List<CapacityEngineerSliceUsedEntity> engineerTimeSlice,
LocalDate date, int totalTakeTime) {
ArrayList<CapacityQueryDTO.Segment> segments = new ArrayList<>();
//半天容量
for (TimeSliceEntity targetTimeSlice : typeTimeSlice) {
final LocalTime targetStartTime = LocalTime.parse(targetTimeSlice.getStart(), DateUtil.TIME_FORMATTER);
final LocalTime targetEndTime = LocalTime.parse(targetTimeSlice.getEnd(), DateUtil.TIME_FORMATTER);
CapacityQueryDTO.Segment segment = caculateTargetTimeSlice(totalTakeTime, targetTimeSlice, engineerTimeSlice, targetStartTime, targetEndTime, date);
segments.add(segment);
}
return segments;
}
private List<?> getEngineerOneDay(String engineerCode, String date) {
List<CapacityEngineerSliceUsedEntity> engineerTimeSlice = engineerSliceUsedCapacityDao.findByWorkdayAndEngineerCode(date, engineerCode);
return Collections.emptyList();
private static CapacityQueryDTO.Segment caculateTargetTimeSlice(int totalTakeTime, TimeSliceEntity targetTimeSlice,
List<CapacityEngineerSliceUsedEntity> engineerTimeSlice, LocalTime targetStartTime,
LocalTime targetEndTime, LocalDate targetDate) {
List<CapacityEngineerSliceUsedEntity> collect = engineerTimeSlice.stream().filter(t -> {
TimeSliceEntity timeSlice = t.getTimmeSlice();
LocalTime sliceStartHour = LocalTime.parse(timeSlice.getStart(), DateUtil.TIME_FORMATTER);
LocalTime sliceEndHour = LocalTime.parse(timeSlice.getEnd(), DateUtil.TIME_FORMATTER);
return (targetStartTime.isAfter(sliceStartHour) && targetEndTime.isBefore(sliceEndHour)) ||
(targetStartTime.equals(sliceStartHour) || targetEndTime.equals(sliceEndHour));
}).collect(Collectors.toList());
CapacityQueryDTO.Segment segment = new CapacityQueryDTO.Segment();
segment.setName(targetTimeSlice.getName());
segment.setEndTime(DateUtil.toDate(LocalDateTime.of(targetDate, targetEndTime)));
segment.setBeginTime(DateUtil.toDate(LocalDateTime.of(targetDate, targetStartTime)));
if (!CollectionUtils.isEmpty(collect)) {
OptionalLong optionalLong = collect.stream().mapToLong(t -> {
Long maxDuration = t.getMaxDuration();
return Objects.isNull(maxDuration) ? 0L : maxDuration;
}).max();
long maxDuration = optionalLong.isEmpty() ? 0 : optionalLong.getAsLong();
segment.setMaxDuration(maxDuration);
long remain = collect.stream().mapToLong(CapacityEngineerSliceUsedEntity::getCapLeft).sum();
segment.setRemain(remain);
segment.setStatus(totalTakeTime <= maxDuration ? 1 : 0);
} else {
segment.setMaxDuration(0);
segment.setRemain(0);
segment.setStatus(0);
}
return segment;
}
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!