electripper/electripper-system/src/main/java/com/ruoyi/system/task/EtTask.java
2024-06-07 21:31:39 +08:00

365 lines
20 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.ruoyi.system.task;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.ServiceConstants;
import com.ruoyi.common.core.domain.entity.AsUser;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.system.domain.*;
import com.ruoyi.system.mapper.AsDeviceMapper;
import com.ruoyi.system.mapper.AsUserMapper;
import com.ruoyi.system.mapper.EtOrderMapper;
import com.ruoyi.system.mapper.SysUserMapper;
import com.ruoyi.system.service.*;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.model.Refund;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
/**
* 定时任务调度测试
*
* @author ruoyi
*/
@Slf4j
@Component("etTask")
public class EtTask {
@Resource
private EtOrderMapper etOrderMapper;
@Autowired
private IEtOperatingAreaService etOperatingAreaService;
@Resource
private SysUserMapper userMapper;
@Autowired
private IEtDividendDetailService dividendDetailService;
@Autowired
private ScheduledExecutorService scheduledExecutorService;
@Autowired
private IEtOrderService etOrderService;
@Resource
private AsDeviceMapper asDeviceMapper;
@Autowired
private IWxPayService wxPayService;
@Autowired
private IEtFeeRuleService etFeeRuleService;
@Autowired
private IEtRefundService etRefundService;
@Autowired
private RedisCache redisCache;
@Resource
private AsUserMapper asUserMapper;
@Autowired
private CallbackService callbackService;
/**
* 1.启动时判断是否有未取消预约的订单
* 2.判断已完成的订单未退还押金的
* 3.启动时判断是否分账
*/
@Transactional
@PostConstruct
public void init() {
log.info("=========================启动业务处理=========================");
log.info("=========================开始=========================");
/** 1.启动时判断是否有未取消预约的订单*/
uncancelledAppointmentHandle();
/** 2.判断已完成的订单未退还押金的(根据et_refund表中的refund_result结果判断是否已经退款) */
/** ①找出所有已完成的订单 status=4 type = 1 r.refund_result IS NULL
* ②根据用户查询最后一次押金充值记录
*/
List<EtOrder> orders = etOrderMapper.selectUserListFinishOrder();
for(EtOrder order:orders){
EtFeeRule rule = etFeeRuleService.selectEtFeeRuleByRuleId(order.getRuleId());
if(ObjectUtil.isNull(rule)){
throw new ServiceException("骑行订单:【"+order.getOrderNo()+"】未找到该套餐【"+order.getRuleId()+"");
}
EtOperatingArea area = etOperatingAreaService.selectEtOperatingAreaByAreaId(order.getAreaId());
AsUser asUser = asUserMapper.selectUserById(order.getUserId());
Integer autoRefundDeposit = rule.getAutoRefundDeposit();
// 根据用户查询最后一次押金充值订单
EtOrder etOrder = new EtOrder();
etOrder.setUserId(order.getUserId());
etOrder.setPaid(ServiceConstants.ORDER_PAY_STATUS_PAID);
etOrder.setType(ServiceConstants.ORDER_TYPE_DEPOSIT);
etOrder.setStatus(ServiceConstants.ORDER_STATUS_ORDER_END);
List<EtOrder> etOrders = etOrderMapper.selectEtOrderList(etOrder);
if (etOrders.size() > 0 || ObjectUtil.isNotNull(etOrders) ) {
Optional<EtOrder> latestOrderOptional = etOrders.stream()
.max(Comparator.comparing(EtOrder::getPayTime));
if (latestOrderOptional.isPresent()) {
EtOrder lastOrder = latestOrderOptional.get();
log.info("【系统启动】用户【{}】最后一次押金充值订单 : " + JSON.toJSONString(lastOrder),asUser.getUserId());
if(lastOrder.getTotalFee().compareTo(new BigDecimal(area.getDeposit()))!=0){
log.info("【系统启动】最后一次押金充值记录 金额与押金不一致,订单押金:【{}】,区域押金:【{}】",lastOrder.getTotalFee(),area.getDeposit());
}else{
// 根据最新的订单号,查询是否有退款记录
EtRefund etRefund = etRefundService.selectEtRefundByOrderNo(lastOrder.getOrderNo());
// 没有退款记录,发起退款
if(ObjectUtil.isNull(etRefund)){
// 根据订单支付时间 autoRefundDeposit个小时后退押金
String reason = autoRefundDeposit + "个小时后自动退押金";
Date payTime = order.getPayTime();
Date refundDepositTime = DateUtils.getTimeAfterXHours(payTime, autoRefundDeposit);
Date nowDate = DateUtils.getNowDate();
if (nowDate.after(refundDepositTime)) {
log.info("【系统启动】用户【{}】押金充值订单【{}】已过期,开始自动退押金",asUser.getUserId(),lastOrder.getOrderNo());
refundDeposit(asUser, lastOrder, reason);
}else{
int timeDifferenceInMinutes = DateUtils.timeDifferenceInMinutes(payTime, nowDate);
int i = autoRefundDeposit * 60;
int delay = i - timeDifferenceInMinutes;
log.info("【系统启动】用户【{}】押金充值订单【{}】未过期,【{}】分钟后退押金",asUser.getUserId(),lastOrder.getOrderNo(),delay);
scheduledExecutorService.schedule(() -> {
refundDeposit(asUser, lastOrder, reason);
}, delay, TimeUnit.MINUTES);
}
}else{
// 有退款记录,判断是否成功
if(!Constants.SUCCESS2.equals(etRefund.getRefundResult())){
log.info("【系统启动】押金退款未成功回调,退款单号:【{}】",etRefund.getRefundNo());
// 根据退款单号查询退款信息
Refund refund = wxPayService.queryByOutRefundNo(area.getAreaId(),etRefund.getRefundNo());
if(ObjectUtil.isNotNull(refund) && Constants.SUCCESS2.equals(refund.getStatus().name())){
// 更新退款记录
etRefund.setRefundResult(Constants.SUCCESS2);
etRefund.setUpdateTime(new Date());
etRefundService.updateEtRefund(etRefund);
log.info("【系统启动】更新押金退款回调成功,退款单号:【{}】",refund.getOutRefundNo());
}
}
}
}
}
}
}
/** 3.启动时判断是否分账(根据订单号查询分账明细表是否有记录来判断是否分账) */
/** ①找出所有已完成的骑行订单 status=4 type = 1 r.refund_result IS NULL
* ②根据订单号查询分账明细表是否有记录
* 有记录则已经分账过
* 没值代表还未分账
* 判断是否已过分账时间
* 未过,计算出多少小时后分账
* 已过,直接分账(记录分账明细表)
*/
// 查询所有待分账的订单
List<EtOrder> needDividendOrders = etOrderMapper.selectNeedDividendOrder();
for(EtOrder order: needDividendOrders){
log.info("【系统启动】待分账订单:【{}】",order.getOrderNo());
EtOperatingArea area = etOperatingAreaService.selectEtOperatingAreaByAreaId(order.getAreaId());
if(dividendDetailService.isDividendComputedByOrderNo(order.getOrderNo())){
log.info("订单【{}】已经分账",order.getOrderNo());
break;
}
log.info("【系统启动】骑行订单【{}】未分账,开始分账",order.getOrderNo());
Date payTime = order.getPayTime();
Date dividendTime = DateUtils.getTimeAfterXHours(payTime, 24);//分账时间
Date nowDate = DateUtils.getNowDate();
if (nowDate.after(dividendTime)) {
log.info("【系统启动】骑行订单【{}】已过分账时间,开始分账",order.getOrderNo());
// 请求分账处理
Transaction transaction = wxPayService.queryOrderByOutTradeNo(order.getOrderNo());
if (callbackService.dividendHandle(transaction.getTransactionId(), order, area)) break;
}else{
int timeDifferenceInHours = DateUtils.timeDifferenceInHours(payTime, nowDate);
int delay = 24 - timeDifferenceInHours;
log.info("【系统启动】骑行订单【{}】未过分账时间,【{}】小时后开始分账",order.getOrderNo(),delay);
// 24小时后发起分账
scheduledExecutorService.schedule(() -> {
// 请求分账处理
Transaction transaction = wxPayService.queryOrderByOutTradeNo(order.getOrderNo());
if (callbackService.dividendHandle(transaction.getTransactionId(), order, area)) return;
}, delay , TimeUnit.HOURS);
}
}
log.info("=========================结束=========================");
}
private void refundDeposit(AsUser asUser, EtOrder lastOrder, String reason) {
Refund refund = wxPayService.refund(lastOrder, reason, lastOrder.getTotalFee());
lastOrder.setReason(reason);
EtRefund refund1= etOrderService.createRefund(lastOrder, lastOrder.getTotalFee(), null, null, null, null, refund,ServiceConstants.REFUND_TYPE_DEPOSIT);
if(etRefundService.insertEtRefund(refund1)>0){
log.info("【自动退款】保存退款对象成功");
// 新增资金流水记录
callbackService.capitalFlowRecords(lastOrder,ServiceConstants.FLOW_TYPE_DISBURSE,ServiceConstants.ORDER_TYPE_DEPOSIT_REFUND);
// 更新用户信息,清除缓存
asUser.setBalance(BigDecimal.ZERO);
int updateUser = asUserMapper.updateUser(asUser);
if(updateUser>0){
Collection<String> keys = SpringUtils.getBean(RedisCache.class).keys(CacheConstants.LOGIN_TOKEN_KEY + "*");
redisCache.deleteObject(keys);
log.info("【系统启动】退还押金,更新用户余额成功!");
}
log.info("=================【系统启动】退还押金定时任务结束!!!==================");
}else{
throw new ServiceException("【系统启动】保存退款对象失败");
}
}
private void uncancelledAppointmentHandle() {
List<EtOrder> orders= etOrderMapper.selectAppointmentUnfinished();
log.info("预约未完成的订单 = " + JSON.toJSONString(orders));
for (EtOrder order:orders) {
EtOperatingArea area = etOperatingAreaService.selectEtOperatingAreaByAreaId(order.getAreaId());
AsDevice asDevice = asDeviceMapper.selectAsDeviceBySn(order.getSn());
Date appointmentEndTime = DateUtils.getTimeAfterXMinutes(order.getAppointmentStartTime(), area.getTimeoutMinutes());//预约结束时间
int timeDifferenceInSeconds = DateUtils.timeDifferenceInSeconds(appointmentEndTime, order.getAppointmentStartTime());//(超时时间-开始时间)的秒数
int differenceInSeconds = DateUtils.timeDifferenceInSeconds(new Date(), order.getAppointmentStartTime());//(当前时间-开始时间)的秒数
int delay = timeDifferenceInSeconds - differenceInSeconds;
log.info("【定时取消预约】延迟:【{}】秒", delay);
//定时取消预约
scheduledExecutorService.schedule(() -> {
log.error("【车辆超时预约】系统自动取消");
EtOrder order1 = etOrderService.selectEtOrderByOrderNo(order.getOrderNo());
log.info("【定时取消预约】重新获取订单信息:{}",JSON.toJSON(order1));
if(order1.getPaid().equals(ServiceConstants.ORDER_PAY_STATUS_PAID)){//已支付订单,跳过
log.error("【车辆超时预约】订单已支付,跳过");
return;
}
log.error("【车辆超时预约】订单未支付,系统自动处理");
//未支付 订单更新最后预约时间,并结束订单,做超出预约时间标记
order.setStatus(ServiceConstants.ORDER_STATUS_CANCEL_APPOINTMENT);
order.setAppointmentEndTime(new Date());
order.setAppointmentTimeout("1");
//计算预约费
BigDecimal appointmentServiceFee = area.getAppointmentServiceFee();
BigDecimal fee = appointmentServiceFee.multiply(new BigDecimal(area.getTimeoutMinutes()).divide(new BigDecimal(10)));
order.setAppointmentFee(fee);
order.setTotalFee(fee);
int update = etOrderService.updateEtOrder(order);
if(update==0){
throw new ServiceException("【车辆超时预约】:更新订单状态失败");
}
// 改变车辆状态
asDevice.setStatus(ServiceConstants.VEHICLE_STATUS_NORMAL);
asDevice.setLockStatus(ServiceConstants.LOCK_STATUS_OPEN);
int device = asDeviceMapper.updateAsDevice(asDevice);
if(device==0){
log.error("【车辆超时预约】更新车辆状态失败");
throw new ServiceException("【车辆超时预约】更新车辆状态失败");
}
}, delay, TimeUnit.SECONDS);
}
}
/**
* 每天凌晨0点5分执行计算分账结果
* cron: 0 5 0 * * ?
*/
public void computeDividend()
{
log.info("每天凌晨0点5分执行计算分账结果");
// 获取昨天的订单,2024-05-26 00:00:00 -- 2024-05-26 23:59:59
// 获取昨天日期格式: yyyy-MM-dd
LocalDate yesterday = LocalDate.now().minusDays(1);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String formattedYesterday = yesterday.format(formatter);
log.info("获取昨天日期 = " + formattedYesterday);
//判断该日期是否已经计算过分账结果
if(dividendDetailService.isDividendComputed(LocalDate.now().format(formatter))){
log.info("该日期已经计算过分账结果");
return;
}
String startDateStr = formattedYesterday + " "+ Constants.DATE_FORMAT_START_PEREND;
String endDateStr = formattedYesterday + " " +Constants.DATE_FORMAT_END_PEREND;
EtOrder order = new EtOrder();
order.setStartTime(startDateStr);
order.setEndTime(endDateStr);
order.setPaid(ServiceConstants.ORDER_PAY_STATUS_PAID);
order.setStatus(ServiceConstants.ORDER_STATUS_ORDER_END);
order.setType(ServiceConstants.ORDER_TYPE_RIDING);
List<EtOrder> orderListByDate = etOrderMapper.selectEtOrderList(order);
for(EtOrder order1:orderListByDate){
EtDividendDetail etDividendDetail = new EtDividendDetail();
EtOperatingArea area = etOperatingAreaService.selectEtOperatingAreaByAreaId(order1.getAreaId());
SysUser sysUser = new SysUser();
sysUser.setUserType("03");
sysUser.setAreaId(area.getAreaId());
List<SysUser> sysUsers = userMapper.selectUserList(sysUser);
for(SysUser user : sysUsers){
etDividendDetail.setAreaId(area.getAreaId());
etDividendDetail.setPartnerId(user.getUserId());
etDividendDetail.setOrderNo(order1.getOrderNo());
etDividendDetail.setTotalAmount(order1.getTotalFee());
etDividendDetail.setCreateTime(DateUtils.getNowDate());
etDividendDetail.setDividendProportion(user.getDividendProportion());
String dividendItem = user.getDividendItem();
// todo 分账金额是骑行费,还是调度费,看分账项目 分账项目1-骑行费(骑行费+预约费2-调度费(调度费+管理费)
BigDecimal dividendAmount = BigDecimal.ZERO;
if(dividendItem.contains("1")){
dividendAmount.add(order1.getRidingFee().add(order1.getAppointmentFee()));//1-骑行费(骑行费+预约费)
}else if(dividendItem.contains("2")){
dividendAmount.add(order1.getManageFee().add(order1.getManageFee()));//2-调度费(调度费+管理费)
}
BigDecimal divide = new BigDecimal(user.getDividendProportion()).divide(new BigDecimal(100), 2, BigDecimal.ROUND_HALF_UP);
etDividendDetail.setDividendAmount(dividendAmount.multiply(divide));
etDividendDetail.setDividendItem(dividendItem);
log.info("保存分账明细 === " + JSON.toJSONString(etDividendDetail));
int i = dividendDetailService.insertEtDividendDetail(etDividendDetail);
if(i==0){
throw new ServiceException("保存分账明细失败");
}
}
int totalDividendProportion = IntStream.of(sysUsers.stream()
.mapToInt(SysUser::getDividendProportion)
.toArray())
.sum();
//算运营商自己的分账
etDividendDetail.setAreaId(area.getAreaId());
etDividendDetail.setPartnerId(0L);
etDividendDetail.setOrderNo(order1.getOrderNo());
etDividendDetail.setTotalAmount(order1.getTotalFee());
etDividendDetail.setCreateTime(DateUtils.getNowDate());
etDividendDetail.setDividendAmount(order1.getTotalFee().multiply(new BigDecimal(100-totalDividendProportion).divide(new BigDecimal(100),2, BigDecimal.ROUND_HALF_UP)));
etDividendDetail.setDividendProportion(100-totalDividendProportion);
etDividendDetail.setDividendItem("运营商");
int i = dividendDetailService.insertEtDividendDetail(etDividendDetail);
if(i==0){
throw new ServiceException("保存分账明细失败");
}
}
}
}