package com.tievd.jyz.Timer;
|
|
import cn.hutool.core.bean.BeanUtil;
|
import cn.hutool.core.util.RandomUtil;
|
import com.alibaba.fastjson.JSON;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
|
import com.tievd.jyz.cache.AlgorithmCache;
|
import com.tievd.jyz.constants.SystemConstant;
|
import com.tievd.jyz.entity.*;
|
import com.tievd.jyz.entity.vo.ClientVo;
|
import com.tievd.jyz.entity.vo.OilStatisVo;
|
import com.tievd.jyz.service.*;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.stereotype.Component;
|
import org.springframework.util.CollectionUtils;
|
|
import java.sql.Timestamp;
|
import java.time.LocalDate;
|
import java.time.LocalDateTime;
|
import java.time.temporal.ChronoField;
|
import java.time.temporal.ChronoUnit;
|
import java.util.*;
|
import java.util.function.Function;
|
import java.util.stream.Collectors;
|
|
|
@Slf4j
|
@Component
|
@EnableScheduling
|
public class SystemSchedule {
|
|
@Autowired
|
ICameraService cameraService;
|
|
@Autowired
|
IOilStaticService oilStaticService;
|
|
@Autowired
|
IOilRecordService oilRecordService;
|
|
@Autowired
|
IOiloutRecordService oiloutRecordService;
|
|
@Autowired
|
IOiloutEventService oiloutEventService;
|
|
@Autowired
|
IClientConfigService clientConfigService;
|
|
/**
|
* 统计车辆加油停靠等数据
|
*/
|
@Scheduled(cron = "${jyz.timer.oil-statis:0 0 * * * ?}")
|
public void statisOil() {
|
log.info("----------------开始统计车辆信息数据-----------------");
|
LambdaQueryWrapper<OilRecord> wrapper = new LambdaQueryWrapper();
|
// wrapper.ge(OilRecord::getStartTime, statisTime);
|
wrapper.isNotNull(OilRecord::getStandard)
|
.isNotNull(OilRecord::getBehavior)
|
.isNotNull(OilRecord::getStartTime);
|
wrapper.orderByDesc(OilRecord::getStartTime);
|
List<OilRecord> recordList = oilRecordService.list(wrapper);
|
//数据统计
|
Map<String, OilStatisVo> statisMap = new HashMap<>();
|
|
//客户类型
|
Map<String, List<OilRecord>> recordMap = new HashMap<>();
|
|
for (OilRecord oilRecord : recordList) {
|
String statisKey = oilRecord.getLicenseNum() + "_" + oilRecord.getOrgCode();
|
statisMap.putIfAbsent(statisKey, new OilStatisVo(oilRecord));
|
OilStatisVo statisVo = statisMap.get(statisKey);
|
//出现次数
|
statisVo.setAppearCount(statisVo.getAppearCount() + 1);
|
//加油次数
|
if (SystemConstant.BEHAVIOR_TYPE_OIL == oilRecord.getBehavior() + 0 ) {
|
statisVo.setOilCount(statisVo.getOilCount() + 1);
|
|
recordMap.putIfAbsent(statisKey, new ArrayList<>());
|
recordMap.get(statisKey).add(oilRecord);
|
}
|
//加油量
|
int oilVolume = oilRecord.getOilVolume() == null ? 0 : oilRecord.getOilVolume();
|
statisVo.setOilSum(statisVo.getOilSum() + oilVolume);
|
//异常次数
|
if (SystemConstant.STANDARD_ERROR == oilRecord.getStandard() + 0 ) {
|
statisVo.setEventCount(statisVo.getEventCount() + 1);
|
}
|
//停留时间
|
int spandTime = oilRecord.getSpandTime() == null ? 0 : oilRecord.getSpandTime();
|
statisVo.setStayTime(statisVo.getStayTime() + spandTime);
|
|
//加油位
|
if (StringUtils.isNotBlank(oilRecord.getOilPosition())) {
|
Map<String, Integer> positionMap = statisVo.getPosition();
|
String position = oilRecord.getOilPosition();
|
positionMap.put(position, positionMap.getOrDefault(position, 0) + 1);
|
}
|
|
//高峰时段
|
Map<Integer, Integer> phraseMap = statisVo.getPhrase();
|
int timePhrase = getTimePhrase(oilRecord.getStartTime());
|
phraseMap.put(timePhrase, phraseMap.getOrDefault(timePhrase, 0) + 1);
|
|
}
|
|
List<OilStatis> saveList = new ArrayList<>();
|
|
//客户规则
|
List<ClientVo> clientVos = clientConfigService.groupList();
|
|
//计算汇总的数据
|
for (Map.Entry<String, OilStatisVo> statis : statisMap.entrySet()) {
|
OilStatisVo statisVo = statis.getValue();
|
String key = statis.getKey();
|
//客户类型
|
for (ClientVo clientVo : clientVos) {
|
if (!recordMap.containsKey(key) || CollectionUtils.isEmpty(clientVo.getClientConfigs())) {
|
continue;
|
}
|
ClientTypeInfer<OilRecord> clientInfer = new ClientTypeInfer<>(
|
clientVo, recordMap.get(key), r -> LocalDate.parse(r.getRecordDay()));
|
if (clientInfer.infer() != null) {
|
statisVo.setClientName(clientVo.getClientName());
|
statisVo.setClientId(clientVo.getId());
|
}
|
}
|
//加油位置
|
if (statisVo.getPosition().size() != 0) {
|
Map.Entry<String, Integer> pos = Collections.max(statisVo.getPosition().entrySet(), Comparator.comparingInt(Map.Entry::getValue));
|
statisVo.setOilPosition(pos.getKey());
|
}
|
//高峰时段
|
Map.Entry<Integer, Integer> phrase = Collections.max(statisVo.getPhrase().entrySet(), Comparator.comparingInt(Map.Entry::getValue));
|
statisVo.setHigherPhrase(phrase.getKey().byteValue());
|
OilStatis oilStatis = new OilStatis();
|
BeanUtils.copyProperties(statisVo, oilStatis);
|
saveList.add(oilStatis);
|
}
|
oilStaticService.saveOrUpdateBatch(saveList);
|
log.info("----------------车辆信息数据统计成功-----------------");
|
}
|
|
private int getTimePhrase(Date date) {
|
int hour = date.getHours();
|
if (hour >= 7 && hour < 10) {
|
return SystemConstant.MORNING_PHRASE;
|
} else if (hour >= 17 && hour < 19) {
|
return SystemConstant.EVENING_PHRASE;
|
} else if (hour >= 0 && hour < 7) {
|
return SystemConstant.MIDNIGHT_PHRASE;
|
} else {
|
return SystemConstant.COMMON_PHRASE;
|
}
|
|
}
|
|
|
/**
|
* 每小时定时完结卸油记录及事件
|
* 默认补齐卸油时长: 2h
|
* 默认图片: 该次记录事件的最后一张
|
*/
|
@Scheduled(cron = "${jyz.timer.oilout-complete:0 30 * * * ?}")
|
public void oilOutComplet() {
|
log.info("---------开始处理未正常完结的卸油流程----------");
|
LambdaQueryWrapper<OiloutRecord> wrapper = new LambdaQueryWrapper<OiloutRecord>();
|
wrapper.isNull(OiloutRecord::getEndTime)
|
.le(OiloutRecord::getStartTime, LocalDateTime.now().minusHours(2));
|
List<OiloutRecord> records = oiloutRecordService.list(wrapper);
|
if (CollectionUtils.isEmpty(records)) {
|
return;
|
}
|
log.info("---------检测到存在未正常完结的卸油流程----------", JSON.toJSONString(records));
|
int hour = 2;
|
for (OiloutRecord record : records) {
|
Timestamp startTime = record.getStartTime();
|
LocalDateTime endTime = startTime.toLocalDateTime().plusHours(hour);
|
record.setEndTime(Timestamp.valueOf(endTime));
|
record.setSpandTime(hour * 60);
|
eventComplet(record);
|
}
|
oiloutRecordService.updateBatchById(records);
|
log.info("---------已完善未正常完结的卸油流程----------");
|
}
|
|
/**
|
* 补充未入库的事件
|
* @param record
|
*/
|
private void eventComplet(OiloutRecord record) {
|
LambdaQueryWrapper<OiloutEvent> wrapper = new LambdaQueryWrapper<OiloutEvent>();
|
wrapper.eq(OiloutEvent::getRecordId, record.getId())
|
.orderByAsc(OiloutEvent::getAlgorithmCode);
|
List<OiloutEvent> eventList = oiloutEventService.list();
|
if (CollectionUtils.isEmpty(eventList)) {
|
return;
|
}
|
log.info("---------开始处理未正常完结的卸油卸油事件----------");
|
List<String> completed = eventList.stream().map(OiloutEvent::getAlgorithmCode).collect(Collectors.toList());
|
//获取未入库事件的算法code
|
List<String> unComplete = new ArrayList<>();
|
unComplete.addAll(AlgorithmCache.oiloutAlgMap().keySet());
|
unComplete.removeAll(completed);
|
//待补充事件采用最后一个事件的基础信息
|
OiloutEvent lastEvent = eventList.get(eventList.size() - 1);
|
List<OiloutEvent> unCompleteEvent = new ArrayList<>();
|
for (String algCode : unComplete) {
|
OiloutEvent event = new OiloutEvent();
|
SysAlgorithmItem algorithm = AlgorithmCache.getAlgorithmMap(algCode);
|
BeanUtil.copyProperties(lastEvent, event);
|
event.setId(null)
|
.setAlgorithmCode(algCode)
|
.setAlgorithmName(algorithm.getAlgorithmName())
|
.setEventPhrase(algorithm.getPhrase())
|
.setImgUid(RandomUtil.randomNumbers(10))
|
.setEventType(SystemConstant.OILOUT_EVETN_TYPE_ERROR.byteValue())
|
.setCreateTime(new Date());
|
unCompleteEvent.add(event);
|
}
|
oiloutEventService.saveBatch(unCompleteEvent);
|
log.info("---------已完善未正常完结的卸油卸油事件----------");
|
}
|
|
|
public static void main(String[] args) {
|
new SystemSchedule().test();
|
}
|
|
void test(){
|
List<OilRecord> records = new ArrayList<>();
|
records.add(new OilRecord().setRecordDay("2023-08-21"));
|
records.add(new OilRecord().setRecordDay("2023-05-15"));
|
records.add(new OilRecord().setRecordDay("2023-07-21"));
|
records.add(new OilRecord().setRecordDay("2023-06-21"));
|
|
ClientVo clientVo = new ClientVo();
|
clientVo.setClientName("哈哈哈哈");
|
List<ClientConfig> configs = new ArrayList<>();
|
configs.add(new ClientConfig().setTimeValue(3)
|
.setTimeUnit("months")
|
.setCountType((byte) 2)
|
.setCountRef((byte) 1)
|
.setCountNum(0));
|
clientVo.setClientConfigs(configs);
|
ClientTypeInfer<OilRecord> clientInfer = new ClientTypeInfer<>(
|
clientVo,
|
records,
|
r -> LocalDate.parse(r.getRecordDay()));
|
clientInfer.infer();
|
}
|
|
/**
|
* 客户类型推断
|
* @param <T> record
|
*/
|
class ClientTypeInfer<T> {
|
|
ClientVo rules;
|
|
List<T> dataList;
|
|
Function<T, LocalDate> timeVal;
|
|
public ClientTypeInfer(ClientVo rules, List<T> dataList, Function<T, LocalDate> timeVal){
|
this.rules = rules;
|
this.dataList = dataList;
|
this.timeVal = timeVal;
|
}
|
|
/**
|
* 获得客户类型
|
* @return
|
*/
|
public String infer() {
|
try {
|
if (CollectionUtils.isEmpty(rules.getClientConfigs())) {
|
return null;
|
}
|
for (ClientConfig rule : rules.getClientConfigs()) {
|
if (rule == null || rule.getTimeValue() == null || StringUtils.isBlank(rule.getTimeUnit())) {
|
return null;
|
}
|
ChronoUnit unit = ChronoUnit.valueOf(rule.getTimeUnit().toUpperCase());
|
LocalDate timeLimit = LocalDate.now().minus(rule.getTimeValue(), unit);
|
switch(unit) {
|
case MONTHS: timeLimit = timeLimit.with(ChronoField.DAY_OF_MONTH, 1); break;
|
case YEARS: timeLimit = timeLimit.with(ChronoField.DAY_OF_MONTH, 1); break;
|
}
|
LocalDate finalTimeLimit = timeLimit;
|
List<T> filterData = dataList.stream().filter(t -> !timeVal.apply(t).isBefore(finalTimeLimit)).collect(Collectors.toList());
|
|
// 根据规则类型选择判断逻辑
|
Byte ruleType = rule.getRuleType();
|
if (ruleType == null) {
|
return null;
|
}
|
if (ruleType == 1) {
|
if (rule.getCountType() == null || rule.getCountRef() == null || rule.getCountNum() == null) {
|
return null;
|
}
|
// 加油频次(原有逻辑)
|
int limit = rule.getCountNum();
|
int ref = rule.getCountRef();
|
if (rule.getCountType() == 1) {
|
int sum = filterData.size();
|
if (Integer.compare(sum, limit) != ref) {
|
return null;
|
}
|
} else if((rule.getCountType() == 2)) {
|
Map<Integer, Integer> monthCount = new HashMap<>();
|
int nowMonth = LocalDate.now().getMonthValue();
|
for (int month = timeLimit.getMonthValue(); month != nowMonth ; month++) {
|
monthCount.put(month, 0);
|
}
|
for (T tmp : filterData) {
|
int month = timeVal.apply(tmp).getMonthValue();
|
if (monthCount.containsKey(month)) {
|
monthCount.put(month, monthCount.get(month) + 1);
|
}
|
}
|
Collection<Integer> countNums = monthCount.values();
|
if (countNums.stream().anyMatch(n -> Integer.compare(n, limit) != ref)) {
|
return null;
|
}
|
} else {
|
return null;
|
}
|
} else if (ruleType == 2) {
|
// 加油趋势(新逻辑)
|
if (rule.getCountTrend() == null || rule.getCountNum() == null || rule.getHistoryMonths() == null || rule.getRecentMonths() == null) {
|
return null;
|
}
|
if (!checkTrend(rule, filterData)) {
|
return null;
|
}
|
} else {
|
return null;
|
}
|
}
|
} catch (Exception e) {
|
e.printStackTrace();
|
return null;
|
}
|
return rules.getClientName();
|
}
|
|
/**
|
* 趋势判断(使用配置的月数)
|
* @param rule 规则
|
* @param dataList 数据列表
|
* @return
|
*/
|
private boolean checkTrend(ClientConfig rule, List<T> dataList) {
|
if (dataList == null || dataList.isEmpty()) {
|
return false;
|
}
|
|
Byte countTrend = rule.getCountTrend();
|
Integer countNum = rule.getCountNum();
|
Integer historyMonths = rule.getHistoryMonths();
|
Integer recentMonths = rule.getRecentMonths();
|
if (countTrend == null || countNum == null || historyMonths == null || recentMonths == null) {
|
return false;
|
}
|
if (historyMonths <= 0 || recentMonths <= 0 || countNum <= 0) {
|
return false;
|
}
|
|
LocalDate now = LocalDate.now();
|
LocalDate currentMonth = now.withDayOfMonth(1);
|
LocalDate recentStart = currentMonth.minusMonths(recentMonths - 1L);
|
LocalDate historyStart = recentStart.minusMonths(historyMonths);
|
LocalDate historyEnd = recentStart;
|
|
Map<LocalDate, Integer> recentMonthCount = new LinkedHashMap<>();
|
for (int i = 0; i < recentMonths; i++) {
|
LocalDate month = recentStart.plusMonths(i);
|
recentMonthCount.put(month, 0);
|
}
|
|
Map<LocalDate, Integer> historyMonthCount = new LinkedHashMap<>();
|
for (int i = 0; i < historyMonths; i++) {
|
LocalDate month = historyStart.plusMonths(i);
|
historyMonthCount.put(month, 0);
|
}
|
|
for (T tmp : dataList) {
|
LocalDate month = timeVal.apply(tmp).withDayOfMonth(1);
|
if (recentMonthCount.containsKey(month)) {
|
recentMonthCount.put(month, recentMonthCount.get(month) + 1);
|
}
|
if (historyMonthCount.containsKey(month) && !month.isBefore(historyStart) && month.isBefore(historyEnd)) {
|
historyMonthCount.put(month, historyMonthCount.get(month) + 1);
|
}
|
}
|
|
if (countTrend == 1) {
|
boolean historyStable = historyMonthCount.values().stream().allMatch(n -> n >= countNum);
|
boolean recentStable = recentMonthCount.values().stream().allMatch(n -> n >= countNum);
|
return historyStable && recentStable;
|
} else if (countTrend == 2) {
|
boolean historyStable = historyMonthCount.values().stream().allMatch(n -> n >= countNum);
|
boolean recentDecrease = recentMonthCount.values().stream().allMatch(n -> n < countNum);
|
return historyStable && recentDecrease;
|
}
|
|
return false;
|
}
|
|
}
|
|
}
|