Files
jsowell-charger-web/jsowell-quartz/src/main/java/com/jsowell/quartz/task/JsowellTask.java
jsowell 1d0acb72c4 update
2026-05-27 17:29:26 +08:00

553 lines
22 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package com.jsowell.quartz.task;
import com.google.common.collect.Lists;
import com.jsowell.adapay.dto.WithdrawDTO;
import com.jsowell.adapay.service.AdapayService;
import com.jsowell.common.constant.CacheConstants;
import com.jsowell.common.constant.Constants;
import com.jsowell.common.core.redis.RedisCache;
import com.jsowell.common.enums.SoftwareProtocolEnum;
import com.jsowell.common.enums.thirdparty.ThirdPlatformTypeEnum;
import com.jsowell.common.enums.ykc.PileChannelEntity;
import com.jsowell.common.util.DateUtils;
import com.jsowell.common.util.StringUtils;
import com.jsowell.common.util.spring.SpringUtils;
import com.jsowell.pile.domain.*;
import com.jsowell.pile.domain.ykcCommond.ProofreadTimeCommand;
import com.jsowell.pile.domain.ykcCommond.PublishPileBillingTemplateCommand;
import com.jsowell.pile.domain.ykcCommond.StartChargingCommand;
import com.jsowell.pile.service.*;
import com.jsowell.pile.vo.base.StationInfoVO;
import com.jsowell.pile.vo.web.BillingTemplateVO;
import com.jsowell.quartz.service.AdapayUnsplitRecordHandleService;
import com.jsowell.thirdparty.amap.service.AMapService;
import com.jsowell.thirdparty.common.NotificationDTO;
import com.jsowell.thirdparty.common.NotificationService;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.ss.usermodel.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
@Component("jsowellTask")
public class JsowellTask {
private final Logger log = LoggerFactory.getLogger(JsowellTask.class);
@Autowired
private OrderBasicInfoService orderBasicInfoService;
@Autowired
private PileBasicInfoService pileBasicInfoService;
@Autowired
private PileBillingTemplateService pileBillingTemplateService;
@Autowired
private YKCPushCommandService ykcPushCommandService;
@Autowired
private PileMerchantInfoService pileMerchantInfoService;
@Autowired
private PileStationInfoService pileStationInfoService;
@Autowired
private RedisCache redisCache;
@Autowired
private NotificationService notificationService;
@Autowired
private AMapService aMapService;
@Autowired
private AdapayService adapayService;
@Autowired
private SettleOrderReportService settleOrderReportService;
@Autowired
private ThirdPartyStationRelationService thirdPartyStationRelationService;
// @Autowired
// private OrderUnsplitRecordService orderUnsplitRecordService;
@Autowired
private AdapayUnsplitRecordHandleService adapayUnsplitRecordHandleService;
private static final long YKC_DAILY_TIMECHECK_INTERVAL_MILLIS = 200L;
/**
* 设置挡板, PRE环境不执行
*/
public void setBarrier() {
String env = SpringUtils.getActiveProfile();
if (StringUtils.equalsIgnoreCase(env, "pre")) {
log.debug("PRE环境不执行");
// return;
}
}
/**
* 关闭15分钟未支付的订单
* close15MinutesOfUnpaidOrders
*/
public void close15MinutesOfUnpaidOrders() {
String env = SpringUtils.getActiveProfile();
if (StringUtils.equalsIgnoreCase(env, "pre")) {
log.debug("PRE环境不执行");
return;
}
// log.info("关闭15分钟未支付的订单");
orderBasicInfoService.close15MinutesOfUnpaidOrders();
}
/**
* 关闭启动失败的订单
* 订单支付成功在15分钟内未启动
*/
public void closeStartFailedOrder() {
String env = SpringUtils.getActiveProfile();
if (StringUtils.equalsIgnoreCase(env, "pre")) {
log.debug("PRE环境不执行");
// return;
}
// 查询出最近2天支付成功并且订单状态为未启动的订单
String startTime = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, DateUtils.addDays(new Date(), -2));
String endTime = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, new Date());
orderBasicInfoService.closeStartFailedOrder(startTime, endTime);
}
/**
* 查询预约充电的订单并启动
*/
public void appointmentOrderStart() {
String env = SpringUtils.getActiveProfile();
if (StringUtils.equalsIgnoreCase(env, "pre")) {
log.debug("PRE环境不执行");
return;
}
// 查询出 已支付 设置预约充电 未启动 的订单
LocalDateTime now = LocalDateTime.now();
List<OrderBasicInfo> list = orderBasicInfoService.getReservedOrder(now);
if (CollectionUtils.isEmpty(list)) {
return;
}
log.info("待启动充电订单:{}", list);
for (OrderBasicInfo orderInfo : list) {
// 下发充电桩设置指令
String pileSn = orderInfo.getPileSn();
// 发送启动充电指令前,再次下发计费模板
BillingTemplateVO billingTemplateVO = pileBillingTemplateService.selectBillingTemplateDetailByPileSn(pileSn);
if (billingTemplateVO != null) {
PublishPileBillingTemplateCommand command = PublishPileBillingTemplateCommand.builder()
.billingTemplateVO(billingTemplateVO)
.pileSn(pileSn)
.build();
ykcPushCommandService.pushPublishPileBillingTemplate(command);
}
// 发送启动指令
String connectorCode = orderInfo.getConnectorCode();
String transactionCode = orderInfo.getTransactionCode();
BigDecimal payAmount = orderInfo.getPayAmount();
if (StringUtils.isEmpty(pileSn) || StringUtils.isEmpty(connectorCode)) {
log.warn("appointmentOrderStart-远程启动充电, 充电桩编号和枪口号不能为空");
return;
}
log.info("appointmentOrderStart 远程启动充电, 桩号:{}, 枪口号:{}", pileSn, connectorCode);
StartChargingCommand startChargingCommand = StartChargingCommand.builder()
.pileSn(pileSn)
.connectorCode(connectorCode)
.transactionCode(transactionCode)
.chargeAmount(payAmount)
.build();
ykcPushCommandService.pushStartChargingCommand(startChargingCommand);
}
}
/**
* 云快充1.6每日自动对时
* jsowellTask.dailyProofreadTimeForYkcV160()
*/
public void dailyProofreadTimeForYkcV160() {
this.dailyProofreadTimeForYkcV160(YKC_DAILY_TIMECHECK_INTERVAL_MILLIS);
}
/**
* 云快充1.6每日自动对时
* jsowellTask.dailyProofreadTimeForYkcV160(200)
*/
public void dailyProofreadTimeForYkcV160(Long intervalMillis) {
String env = SpringUtils.getActiveProfile();
if (StringUtils.equalsIgnoreCase(env, "pre")) {
log.debug("PRE环境不执行");
return;
}
long sendIntervalMillis = intervalMillis == null || intervalMillis < 0 ? YKC_DAILY_TIMECHECK_INTERVAL_MILLIS : intervalMillis;
List<String> connectedPileSnList = PileChannelEntity.getPileSnListSnapshot().stream()
.filter(StringUtils::isNotBlank)
.sorted()
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(connectedPileSnList)) {
log.info("云快充1.6每日自动对时跳过: 当前无在线长连接");
return;
}
int candidateCount = connectedPileSnList.size();
int sentCount = 0;
int skippedCount = 0;
int failedCount = 0;
log.info("云快充1.6每日自动对时开始, candidateCount:{}, intervalMillis:{}", candidateCount, sendIntervalMillis);
for (String pileSn : connectedPileSnList) {
PileBasicInfo pileBasicInfo = pileBasicInfoService.selectPileBasicInfoBySN(pileSn);
if (pileBasicInfo == null) {
skippedCount++;
log.warn("云快充1.6每日自动对时跳过, pileSn:{}, reason: pile_basic_info_not_found", pileSn);
continue;
}
if (!StringUtils.equals(SoftwareProtocolEnum.YUN_KUAI_CHONGV160.getValue(), pileBasicInfo.getSoftwareProtocol())) {
skippedCount++;
continue;
}
try {
ProofreadTimeCommand command = ProofreadTimeCommand.builder().pileSn(pileSn).build();
ykcPushCommandService.pushProofreadTimeCommand(command);
sentCount++;
} catch (Exception e) {
failedCount++;
log.error("云快充1.6每日自动对时失败, pileSn:{}", pileSn, e);
}
if (sendIntervalMillis > 0) {
try {
Thread.sleep(sendIntervalMillis);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.warn("云快充1.6每日自动对时被中断, sentCount:{}, skippedCount:{}, failedCount:{}", sentCount, skippedCount, failedCount);
return;
}
}
}
log.info("云快充1.6每日自动对时结束, candidateCount:{}, sentCount:{}, skippedCount:{}, failedCount:{}, intervalMillis:{}",
candidateCount, sentCount, skippedCount, failedCount, sendIntervalMillis);
}
/**
* 计算站点订单报表
* jsowellTask.calculateTheSiteOrdersReport()
*/
public void calculateTheSiteOrdersReport() {
String env = SpringUtils.getActiveProfile();
if (StringUtils.equalsIgnoreCase(env, "pre")) {
log.debug("PRE环境不执行");
return;
}
// 查询出所有站点
PileStationInfo pileStationInfo = new PileStationInfo();
pileStationInfo.setDelFlag(Constants.ZERO);
// 查询未删除的站点列表
List<PileStationInfo> list = pileStationInfoService.selectPileStationInfoList(pileStationInfo);
if (CollectionUtils.isEmpty(list)) {
return;
}
LocalDate yesterday = LocalDate.now().plusDays(-1);
// 计算每个站点前一天的报表
for (PileStationInfo stationInfo : list) {
try {
settleOrderReportService.generateDailyOrderReports(stationInfo.getId() + "", yesterday.toString());
} catch (Exception e) {
log.error("计算站点订单报表 发生异常 stationId:{}", stationInfo.getId(), e);
}
}
}
/**
* 站点的枪口数据推送到高德
* jsowellTask.pushToAMap()
*/
public void pushToAMap() {
String env = SpringUtils.getActiveProfile();
if (StringUtils.equalsIgnoreCase(env, "pre")) {
log.debug("PRE环境不执行");
return;
}
Set<String> stationIds = redisCache.getCacheSet(CacheConstants.PUSH_STATION_CONNECTOR);
if (CollectionUtils.isEmpty(stationIds)) {
return;
}
log.info("推送到高德的stationId:{}", stationIds);
for (String stationId : stationIds) {
try {
aMapService.pushChargingDeviceDynamics(stationId);
} catch (Exception e) {
log.error("推送到高德error", e);
}
}
// 删除缓存
redisCache.deleteObject(CacheConstants.PUSH_STATION_CONNECTOR);
}
/**
* 贵州省平台推送充电站实时功率 15分钟执行一次
*/
public void pushStationRealTimePowerInfo() {
String env = SpringUtils.getActiveProfile();
if (StringUtils.equalsIgnoreCase(env, "pre")) {
log.debug("PRE环境不执行");
return;
}
List<String> thirdPartyTypeList = Lists.newArrayList(
ThirdPlatformTypeEnum.GUI_ZHOU_PLATFORM.getTypeCode(),
ThirdPlatformTypeEnum.SI_CHUAN_PLATFORM.getTypeCode(),
ThirdPlatformTypeEnum.JI_LIN_PLATFORM.getTypeCode()
);
for (String thirdPartyType : thirdPartyTypeList) {
List<StationInfoVO> stationInfoVOS = thirdPartyStationRelationService.selectStationList(thirdPartyType);
if (CollectionUtils.isEmpty(stationInfoVOS)) {
continue;
}
List<String> stationIdList = stationInfoVOS.stream()
.map(StationInfoVO::getStationId)
.collect(Collectors.toList());
for (String stationId : stationIdList) {
try {
NotificationDTO dto = new NotificationDTO();
dto.setStationId(stationId);
dto.setPlatformType(thirdPartyType);
notificationService.notificationStationPowerInfo(dto);
} catch (Exception e) {
log.error("平台类型:{}站点ID{},推送充电站实时功率失败", thirdPartyType, stationId, e);
}
}
}
}
/**
* 推送统计信息 24小时执行一次
*/
public void pushStatisticsInfo() {
String env = SpringUtils.getActiveProfile();
if (StringUtils.equalsIgnoreCase(env, "pre")) {
log.debug("PRE环境不执行");
return;
}
List<String> thirdPartyTypeList = Lists.newArrayList(
ThirdPlatformTypeEnum.GUI_ZHOU_PLATFORM.getTypeCode(),
ThirdPlatformTypeEnum.SI_CHUAN_PLATFORM.getTypeCode(),
ThirdPlatformTypeEnum.JI_LIN_PLATFORM.getTypeCode()
);
for (String thirdPartyType : thirdPartyTypeList) {
List<StationInfoVO> stationInfoVOS = thirdPartyStationRelationService.selectStationList(thirdPartyType);
if (CollectionUtils.isEmpty(stationInfoVOS)) {
continue;
}
List<String> stationIdList = stationInfoVOS.stream()
.map(StationInfoVO::getStationId)
.collect(Collectors.toList());
for (String stationId : stationIdList) {
try {
NotificationDTO dto = new NotificationDTO();
dto.setStationId(stationId);
dto.setPlatformType(thirdPartyType);
notificationService.notificationOperationStatsInfo(dto);
} catch (Exception e) {
log.error("平台类型:{}站点ID{},推送统计信息失败", thirdPartyType, stationId, e);
}
}
}
}
/**
* 定时任务, 订单分账
* jsowellTask.processOrderSplitting()
*/
public void processOrderSplitting() {
String env = SpringUtils.getActiveProfile();
if (StringUtils.equalsIgnoreCase(env, "pre")) {
log.debug("PRE环境不执行");
return;
}
// 查询运营商列表
List<PileMerchantInfo> pileMerchantInfos = pileMerchantInfoService.selectPileMerchantInfoList(null);
if (CollectionUtils.isEmpty(pileMerchantInfos)) {
log.info("定时任务,处理订单分账, 未查询到运营商列表,直接返回");
return;
}
// 获取日期
LocalDate yesterday = LocalDate.now().plusDays(-1);
// 设置挡板8月1号之后的订单按照实际进行分账
// LocalDateTime now = LocalDateTime.now();
// LocalDateTime dateTime = LocalDateTime.of(2023, 8, 2, 0, 0, 0);
// if (now.isBefore(dateTime)) {
// log.info("当前时间:{}早于:{}, 不进行分账处理", DateUtils.formatDateTime(now), DateUtils.formatDateTime(dateTime));
// return;
// }
// 调生成运营商日报方法
pileMerchantInfos.parallelStream().forEach(merchant -> {
try {
orderBasicInfoService.orderSplittingOperations(merchant.getId() + "", yesterday.toString());
} catch (Exception e) {
log.error("生成运营商日报异常, merchantId:{}", merchant.getId(), e);
}
});
}
/**
* 生成运营商日报表
* jsowellTask.generateMerchantBill()
*
*
*/
public void generateMerchantBill() {
String env = SpringUtils.getActiveProfile();
if (StringUtils.equalsIgnoreCase(env, "pre")) {
log.debug("PRE环境不执行");
return;
}
// 查询运营商列表
List<PileMerchantInfo> pileMerchantInfos = pileMerchantInfoService.selectPileMerchantInfoList(null);
if (CollectionUtils.isEmpty(pileMerchantInfos)) {
log.info("定时任务,处理订单分账, 未查询到运营商列表,直接返回");
return;
}
// 获取日期
LocalDate yesterday = LocalDate.now().plusDays(-1);
// 调生成运营商日报方法
pileMerchantInfos.parallelStream().forEach(merchant -> {
try {
orderBasicInfoService.generateMerchantBill(merchant.getId() + "", yesterday.toString());
} catch (Exception e) {
log.error("生成运营商日报异常, merchantId:{}", merchant.getId(), e);
}
});
}
/**
* 定时任务,自动提现
* jsowellTask.automaticPayouts()
*/
public void automaticPayouts() {
String env = SpringUtils.getActiveProfile();
if (StringUtils.equalsIgnoreCase(env, "pre")) {
log.debug("PRE环境不执行");
return;
}
// 查询开启自动提现运营商列表
// List<PileMerchantInfo> pileMerchantInfos = pileMerchantInfoService.selectPileMerchantInfoList(null);
List<PileMerchantInfo> pileMerchantInfos = pileMerchantInfoService.selectAutoWithdrawalMerchantInfoList();
if (CollectionUtils.isEmpty(pileMerchantInfos)) {
log.info("定时任务,自动提现, 未查询到运营商列表,直接返回");
return;
}
// 调提现方法
pileMerchantInfos.parallelStream().forEach(merchant -> {
try {
WithdrawDTO dto = new WithdrawDTO();
dto.setMerchantId(merchant.getId() + "");
dto.setFeeAmt("0");
adapayService.drawCash(dto);
} catch (Exception e) {
log.error("生成运营商日报异常, merchantId:{}", merchant.getId(), e);
}
});
}
/**
* 处理未分帐订单
* jsowellTask.processUnSettledOrder()
*/
public void processUnSettledOrder() {
adapayUnsplitRecordHandleService.processUnSettledOrder();
}
/**
* 处理adapay_unsplit_record待分账数据统一分账到memberId=0
* 依赖queryList()请先完成settle_amount/due_refund_amount等字段补齐
* jsowellTask.processUnsplitRecordToDefaultMember()
*/
public void processUnsplitRecordToDefaultMember() {
adapayUnsplitRecordHandleService.processUnsplitRecordToDefaultMember();
}
/**
* 处理adapay_unsplit_record待分账数据统一分账到memberId=0
* jsowellTask.processUnsplitRecordToDefaultMember(wechatAppId, pageSize)
*/
public void processUnsplitRecordToDefaultMember(String wechatAppId, Integer pageSize) {
adapayUnsplitRecordHandleService.processUnsplitRecordToDefaultMember(wechatAppId, pageSize);
}
/**
* 从Excel导入adapay_unsplit_record并补齐缺失字段
* 默认文件路径doc/万车充小程序-未分账明细.xlsx
* jsowellTask.importAdapayUnsplitRecordAndCompleteFields()
*/
/**
* 从默认路径导入未分账明细并补齐缺失字段(无参入口)
* 默认读取 doc/万车充小程序-未分账明细.xlsx相对于工作目录
* jsowellTask.importAdapayUnsplitRecordAndCompleteFields()
*/
public void importAdapayUnsplitRecordAndCompleteFields() {
adapayUnsplitRecordHandleService.importAdapayUnsplitRecordAndCompleteFields();
}
/**
* 补齐 adapay_unsplit_record 表中缺失字段(独立入口,可单独作为定时任务调用)
* 以指定时间范围内的未分账记录为目标,补齐 orderCode、退款金额、结算金额、桩类型
* jsowellTask.completeAdapayUnsplitRecordFields(startTime, endTime)
* 示例jsowellTask.completeAdapayUnsplitRecordFields('2024-01-01 00:00:00', '2025-12-31 23:59:59')
*/
public void completeAdapayUnsplitRecordFields(String startTime, String endTime) {
adapayUnsplitRecordHandleService.completeAdapayUnsplitRecordFields(startTime, endTime);
}
/**
* 从Excel导入adapay_unsplit_record并补齐缺失字段
* 流程:
* 1. 校验文件路径(相对路径自动拼接工作目录转为绝对路径)
* 2. 解析Excel逐行转换为 AdapayUnsplitRecord
* 以 paymentId 为业务唯一键,已存在则按主键更新,不存在则新增
* 3. 以导入数据的支付时间范围为条件,分页查询已入库记录,补齐 orderCode/退款金额/结算金额/桩类型等缺失字段
* jsowellTask.importAdapayUnsplitRecordAndCompleteFields(文件路径)
*/
public void importAdapayUnsplitRecordAndCompleteFields(String filePath) {
adapayUnsplitRecordHandleService.importAdapayUnsplitRecordAndCompleteFields(filePath);
}
public void updateOrderReview() {
LocalDate yesterday = DateUtils.getYesterday();
LocalDateTime start = yesterday.atStartOfDay();
LocalDateTime end = yesterday.atTime(23, 59, 59);
orderBasicInfoService.updateOrderReviewFlagTemp(start, end, null, null);
}
}