electripper/electripper-system/src/main/java/com/ruoyi/system/task/EtTask.java
2025-01-21 23:51:50 +08:00

960 lines
49 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 cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
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.CommonUtil;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SendAliSmsUtil;
import com.ruoyi.common.utils.SendSmsVo;
import com.ruoyi.common.utils.http.HttpUtils;
import com.ruoyi.common.utils.map.GeoUtils;
import com.ruoyi.common.utils.map.GpsCoordinateUtils;
import com.ruoyi.common.utils.onenet.CreateDeviceVo;
import com.ruoyi.common.utils.onenet.DeviceInfo;
import com.ruoyi.common.utils.onenet.LogEntry;
import com.ruoyi.common.utils.onenet.Token;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.system.domain.*;
import com.ruoyi.system.domain.vo.AsDeviceVO;
import com.ruoyi.system.domain.vo.SelfReconciliationVO;
import com.ruoyi.system.mapper.*;
import com.ruoyi.system.service.*;
import com.wechat.pay.java.service.refund.model.Refund;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
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 IEtRefundService etRefundService;
@Autowired
private RedisCache redisCache;
@Resource
private AsUserMapper asUserMapper;
@Resource
private EtLocationLogMapper etLocationLogMapper;
@Autowired
private IAsDeviceService deviceService;
@Autowired
private IEtModelService etModelService;
@Resource
private EtModelMapper etModelMapper;
@Resource
private EtCouponClaimLogMapper etCouponClaimLogMapper;
@Resource
private EtCapitalFlowMapper etCapitalFlowMapper;
@Resource
private EtReconciliationMapper etReconciliationMapper;
@Autowired
private EtChannelService smEtChannelService;
@Resource
private EtModelRuleMapper etModelRuleMapper;
@Value(value = "${iot.deviceUrl}")
private String deviceUrl;
@Value(value = "${iot.productId}")
private String productId;
@Value("${aliyun.accessKeyId}")
private String accessKeyId;
@Value("${aliyun.accessKeySecret}")
private String accessKeySecret;
@Value("${aliyun.signName}")
private String signName;
@Value("${aliyun.templateCode}")
private String templateCode;
/**
* 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();
log.info("已完成的订单未退还押金的的订单 = " + JSON.toJSONString(orders));
for(EtOrder order:orders){
// EtFeeRule rule = etFeeRuleService.selectEtFeeRuleByRuleIdIncludeDelete(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 = order.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{
// 根据最新的订单号,查询是否有退款记录
List<EtRefund> etRefunds = etRefundService.selectEtRefundByOrderNo(lastOrder.getOrderNo());
if(etRefunds.size() == 1){
EtRefund etRefund = etRefunds.get(0);
// 没有退款记录,发起退款
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(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) {
String outRefundNo = IdUtils.getOrderNo("ref");
lastOrder.setReason(reason);
EtRefund refund1= etOrderService.createRefund(lastOrder, lastOrder.getTotalFee(), null, null, null, null, outRefundNo,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.APP_LOGIN_TOKEN_KEY + "*");
// redisCache.deleteObject(keys);
log.info("【系统启动】退还押金,更新用户余额成功!");
}
wxPayService.refund(lastOrder, reason, lastOrder.getTotalFee(),outRefundNo);
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);
order.setPayFee(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("保存分账明细失败");
}
}
}
/**
* 开始骑行未结束的订单1分钟算一次距离
* cron: 0 5 0 * * ?
*/
public void computeDistance(){
log.info("-------------------【定时任务】计算订单距离开始-------------------");
EtOrder order = new EtOrder();
order.setType("1");
order.setStatus(ServiceConstants.ORDER_STATUS_RIDING);
List<EtOrder> orders = etOrderService.selectEtOrderList(order);
for(EtOrder etOrder:orders){
String endTime = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, DateUtils.getNowDate());
String startTime = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, etOrder.getUnlockTime());
String tripRouteStr = deviceService.trajectory(etOrder.getSn(), startTime, endTime);
if(StrUtil.isNotBlank(tripRouteStr)){
double[][] doubles = GeoUtils.parseJsonTrack(tripRouteStr);
double v = GeoUtils.calculateTotalDistance(doubles);
EtOrder etOrder1 = new EtOrder();
etOrder1.setOrderId(etOrder.getOrderId());
etOrder1.setDistance((int)Math.round(v));
int updateEtOrder = etOrderService.updateEtOrder(etOrder1);
if(updateEtOrder>0){
log.info("【定时任务】计算订单距离成功【orderNo="+etOrder.getOrderNo()+"");
}
}
}
}
/**
* 一个星期删除一次onenet心跳日志
* cron: 0 5 0 * * ?
*
* DELETE FROM et_location_log
* WHERE create_time < NOW() - INTERVAL 7 DAY;
*/
public void deleteLocationLog(){
log.info("-------------------【定时任务】删除onenet心跳日志-------------------");
etLocationLogMapper.deleteLocationLogByCreateTime();
}
/**
* 车辆与订单状态同步
* 1. 如果有正在骑行中的订单,车辆的状态是待骑行的,改成临时锁车,不发命令
* 2. 如果车辆状态是骑行中或临时锁车,查询订单没有订单,则修改车辆状态为待骑行
*
*/
public void stausSynchronization(){
log.info("-------------------【定时任务】车辆与订单状态同步-------------------");
// 当前有骑行中的订单
List<EtOrder> orders = etOrderService.getCurrentOrderList();
for (EtOrder order:orders) {
AsDevice device = asDeviceMapper.selectAsDeviceBySn(order.getSn());
if(ObjectUtil.isNotNull(device) && device.getStatus().equals(ServiceConstants.VEHICLE_STATUS_NORMAL)){
AsDevice device1 = new AsDevice();
device1.setSn(device.getSn());
device1.setStatus(ServiceConstants.VEHICLE_STATUS_TEMPORARILY_LOCK);
device1.setLockStatus(ServiceConstants.LOCK_STATUS_CLOSE);
int i = asDeviceMapper.updateAsDeviceBySn(device1);
if(i>0){
log.info("【定时任务】车辆状态修改为临时锁车【sn="+device.getSn()+"");
}
}
}
// 2. 如果车辆状态是骑行中或临时锁车,查询当前没有订单,则修改车辆状态为待骑行
QueryWrapper<AsDevice> wrapper = new QueryWrapper<>();
wrapper.in("status", "3","4"); // 设备状态正常
// 查询所有设备
List<AsDevice> allDevices = asDeviceMapper.selectList(wrapper);
for(AsDevice device:allDevices){
if(ObjectUtil.isNotNull(etOrderService.getCurrentOrder2(device.getSn()))){
continue;
}else{
AsDevice device1 = new AsDevice();
device1.setSn(device.getSn());
device1.setStatus(ServiceConstants.VEHICLE_STATUS_NORMAL);
device1.setLockStatus(ServiceConstants.LOCK_STATUS_CLOSE);
int i = asDeviceMapper.updateAsDeviceBySn(device1);
if(i>0){
log.info("【定时任务】车辆状态修改为待骑行【sn="+device.getSn()+"");
}
}
}
}
// 写一个定时,如果车辆是骑行中,没有现在骑行中的订单,则关闭车辆
/**
* 自动押金抵扣
* 写一个定时任务处理所有的 7天前待支付的订单用押金抵扣如果已经退押金的直接改成结束订单
* 1. 查询所有待支付的订单根据还车时间7天前的订单
* 2. 如果订单金额是0直接结束订单修改订单状态为已支付
* 3. 查询用户是否还有未退款的押金,如果有,则进行押金抵扣,如果没有,则结束订单
*/
public void autoDeduction(){
log.info("-------------------【定时任务】自动押金抵扣-------------------");
/** 1. 查询所有待支付的订单根据还车时间7天前的订单 */
List<EtOrder> orders = etOrderMapper.selectToBePaidEtOrderList();
if(ObjectUtil.isNotNull(orders) && orders.size()>0){
for(EtOrder order:orders){
if(order.getTotalFee().compareTo(BigDecimal.ZERO) == 0){
// 结束订单,修改订单状态为已支付
updateOrderPaid(order);
}else{
etOrderService.deduction(order);
}
}
}
}
/**
* 押金抵扣不成功的修复
* 1. 找出所有押金抵扣不成功的订单
* 2. 将状态改成已结束
*
*/
public void deductionErrorOrder(){
log.info("-------------------【定时任务】押金抵扣不成功的修复---开始----------------");
List<EtOrder> orders = etOrderMapper.deductionErrorOrderList();
for (EtOrder order:orders) {
EtOrder order1 = new EtOrder();
order1.setOrderId(order.getOrderId());
order1.setStatus(ServiceConstants.ORDER_STATUS_ORDER_END);
order1.setMark("押金抵扣修复,订单已结束");
etOrderMapper.updateEtOrder(order1);
}
log.info("-------------------【定时任务】押金抵扣不成功的修复---结束----------------");
}
/** 更新订单为已支付*/
private void updateOrderPaid(EtOrder order) {
EtOrder order1 = new EtOrder();
order1.setOrderId(order.getOrderId());
order1.setPaid(ServiceConstants.ORDER_PAY_STATUS_PAID);
order1.setPayTime(DateUtils.getNowDate());
order1.setStatus(ServiceConstants.ORDER_STATUS_ORDER_END);
order1.setPayType(ServiceConstants.PAY_TYPE_YJ);
order1.setMark("超过7天系统自动押金抵扣");
order1.setDepositDeduction(ServiceConstants.IS_DEPOSIT_DEDUCTION);
int updateEtOrder = etOrderMapper.updateEtOrder(order1);
if(updateEtOrder == 0){
throw new ServiceException("押金抵扣失败,更新骑行订单失败");
}
}
/** 如果还有未退款的押金,如果有,则进行押金抵扣 */
private void autoDeductionHandle(EtOrder order){
// select * from et_order o
// where o.status ='4' and o.paid = '1' and o.type = 1 and o.is_test = '0'
// GROUP BY o.user_id
}
/**
* 更新设备的定位和电压 10秒一次
* cron: 0 10 0 * * ?
*/
public void updateLocation10(){
log.info("-------------------【定时任务10秒一次】更新设备的定位和电压-----开始--------------");
// 记录开始时间
long startTime = System.nanoTime();
Collection<String> keys = redisCache.keys(CacheConstants.CACHE_DEVICE_KEY + "*");
// log.info("redis缓存中的数据" + JSON.toJSONString(keys));
for(String key:keys){
String msg = redisCache.getCacheObject(key);
// log.info("redis缓存中的数据{}", msg);
LogEntry logEntry = JSONObject.parseObject(msg, LogEntry.class);
// log.info("logEntry转换后的对象: logEntry---【{}】" , JSON.toJSONString(logEntry));
LogEntry.LocationValue value = logEntry.getValue();
AsDevice device = asDeviceMapper.selectAsDeviceByMac(logEntry.getDevName());
if(ObjectUtil.isNotNull(device) && !isRepeatMsg(msg,logEntry.getDevName())){
if(ServiceConstants.LOCK_STATUS_OPEN.equals(device.getLockStatus())){
updateLocationHandle(msg, logEntry, value, device);
}
}
}
// 计算执行时间(以毫秒为单位)
long duration = (System.nanoTime() - startTime) / 1_000_000;
// log.info("-------------------【定时任务10秒一次】更新设备的定位和电压----结束---------------{} 毫秒", duration);
}
private boolean isRepeatMsg(String msg,String mac){
// 获取最后一条消息
String lastMsg = etLocationLogMapper.getLastMsg(mac);
if(ObjectUtil.isNotNull(lastMsg) && msg.equals(lastMsg)){
return true;
}else{
return false;
}
}
/**
* 更新设备的定位和电压 5分钟一次
* cron: 0 20 0 * * ?
*/
public void updateLocation300(){
log.info("-------------------【定时任务5分钟一次】更新设备的定位和电压-----开始--------------");
// 记录开始时间
long startTime = System.nanoTime();
Collection<String> keys = redisCache.keys(CacheConstants.CACHE_DEVICE_KEY + "*");
// log.info("redis缓存中的数据{}", JSON.toJSONString(keys));
for(String key:keys){
String msg = redisCache.getCacheObject(key);
// log.info("redis缓存中的数据{}", msg);
LogEntry logEntry = JSONObject.parseObject(msg, LogEntry.class);
// log.info("logEntry转换后的对象: logEntry---【{}】" , JSON.toJSONString(logEntry));
LogEntry.LocationValue value = logEntry.getValue();
AsDevice device = asDeviceMapper.selectAsDeviceByMac(logEntry.getDevName());
if(ObjectUtil.isNotNull(device) && ServiceConstants.LOCK_STATUS_CLOSE.equals(device.getLockStatus())){
updateLocationHandle(msg, logEntry, value, device);
}
}
// 计算执行时间(以毫秒为单位)
long duration = (System.nanoTime() - startTime) / 1_000_000;
// log.info("-------------------【定时任务5分钟一次】更新设备的定位和电压----结束---------------{} 毫秒", duration);
}
private void updateLocationHandle(String msg, LogEntry logEntry, LogEntry.LocationValue value, AsDevice device) {
// 坐标转换 WGS84 转 GCJ02
double[] doubles = coordinateConvert(value);
BigDecimal lon = BigDecimal.valueOf(doubles[1]).setScale(8, RoundingMode.HALF_UP);
BigDecimal lat = BigDecimal.valueOf(doubles[0]).setScale(8, RoundingMode.HALF_UP);
asynchronousSaveLog(msg, logEntry.getAt(), logEntry.getDevName(), lon, lat, device);
AsDevice updateDevice = new AsDevice();
updateDevice.setDeviceId(device.getDeviceId());
BigDecimal voltage = new BigDecimal(value.getBat());
if(voltage.compareTo(new BigDecimal(100)) > 0){
voltage = voltage.divide(new BigDecimal(10));
}
updateDevice.setVoltage(voltage.toString());//电压
if(ObjectUtil.isNotNull(device.getModelId())){
EtModel model = etModelService.selectEtModelByModelId(device.getModelId());
if(ObjectUtil.isNotNull(model)){
Integer remainingMileage = 0;
if(StrUtil.isNotBlank(device.getVoltage())){
remainingMileage = CommonUtil.getRemainingMileage(device.getVoltage(), model.getFullVoltage(), model.getLowVoltage(), model.getFullEndurance());
}
Integer electricQuantity = CommonUtil.getElectricQuantity(device.getVoltage(), model.getFullVoltage(), model.getLowVoltage());//电量百分百
updateDevice.setRemainingMileage(remainingMileage);
updateDevice.setRemainingPower(electricQuantity.toString());
}
}
updateDevice.setLastTime(DateUtils.getNowDate());
updateDevice.setSignalStrength(value.getCsq());
updateDevice.setQuality(value.getQ());
if(BigDecimal.ZERO.compareTo(lon) != 0 && BigDecimal.ZERO.compareTo(lat) != 0){
updateDevice.setLatitude(lat.toString());
updateDevice.setLongitude(lon.toString());
updateDevice.setLastLocationTime(new Date(logEntry.getAt()));
updateDevice.setGps("1");
// 信号强度
updateDevice.setSatellites(value.getS());
}else{
updateDevice.setGps("0");
updateDevice.setSatellites(0);
}
int i = deviceService.updateLocation(updateDevice);
if(i>0){
log.info("===============更新设备信息成功===========>{}", logEntry.getDevName());
}
}
/**
* 异步保存定位
*/
private void asynchronousSaveLog(String msg, long at,String mac,BigDecimal lon,BigDecimal lat,AsDevice device){
//异步保存定位
scheduledExecutorService.schedule(() -> {
EtLocationLog etLocationLog = new EtLocationLog();
etLocationLog.setOnenetMsg(msg);
etLocationLog.setCreateTime(DateUtils.getNowDate());
etLocationLog.setLongitude(lon.toString());
etLocationLog.setLatitude(lat.toString());
etLocationLog.setMac(mac);
etLocationLog.setAt(new Date(at));
etLocationLog.setStatus(device.getStatus());
etLocationLog.setLockStatus(device.getLockStatus());
etLocationLogMapper.insertEtLocationLog(etLocationLog);
}, 0, TimeUnit.SECONDS);
}
/** 坐标转换 */
@NotNull
private double[] coordinateConvert(LogEntry.LocationValue value) {
BigDecimal lon = new BigDecimal(value.getLon());
BigDecimal lat = new BigDecimal(value.getLat());
if(lon.compareTo(new BigDecimal(1000)) < 0 ){
return GpsCoordinateUtils.calWGS84toGCJ02(lat.doubleValue(), lon.doubleValue());
}
// log.info("WGS84经纬度未计算" + lon + "---" + lat);
// 除以100
lon = lon.divide(new BigDecimal(100), 10, RoundingMode.HALF_UP);
lat = lat.divide(new BigDecimal(100), 10, RoundingMode.HALF_UP);
// log.info("WGS84经纬度除以100后" + lon + "---" + lat);
// 取出lon中后面的小数点
String[] lonStr = getDecimalPart(lon);
String[] latStr = getDecimalPart(lat);
// log.info("WGS84经纬度截取小数点" + lonStr[0] + "---" + lonStr[1] + "---"+ latStr[0]+"---"+ latStr[1]);
// 再将结果乘以5/3
String lon2 = "0."+ lonStr[1];
String lat2 = "0."+ latStr[1];
BigDecimal lons = new BigDecimal(lon2).multiply(new BigDecimal(5).divide(new BigDecimal(3), 8, RoundingMode.HALF_UP));
BigDecimal lats = new BigDecimal(lat2).multiply(new BigDecimal(5).divide(new BigDecimal(3), 8, RoundingMode.HALF_UP));
BigDecimal lo = new BigDecimal(lonStr[0]).add(lons);
BigDecimal la = new BigDecimal(latStr[0]).add(lats);
// log.info("WGS84经纬度计算后" + lo + "---" + la);
lo = lo.setScale(8, RoundingMode.HALF_UP);
la = la.setScale(8, RoundingMode.HALF_UP);
// log.info("WGS84经纬度保留8为小数" + lo + "---" + la);
double[] doubles = GpsCoordinateUtils.calWGS84toGCJ02(la.doubleValue(), lo.doubleValue());
return doubles;
}
private static String[] getDecimalPart(BigDecimal number) {
// 将BigDecimal转换为字符串
String numberStr = number.toPlainString();
// 找到小数点的位置
int indexOfDecimal = numberStr.indexOf(".");
// 初始化结果数组
String[] parts = new String[2];
// 如果有小数点
if (indexOfDecimal >= 0) {
parts[0] = numberStr.substring(0, indexOfDecimal); // 整数部分
parts[1] = numberStr.substring(indexOfDecimal + 1); // 小数部分
} else {
// 如果没有小数点,整数部分为整个字符串,小数部分为空
parts[0] = numberStr;
parts[1] = "";
}
return parts;
}
/**
* 判断优惠券是否过期
*
*/
public void couponIsExpires(){
log.info("-------------------【定时任务】判断优惠券是否过期---开始----------------");
EtCouponUserLog etCouponUserLog = new EtCouponUserLog();
etCouponUserLog.setStatus(ServiceConstants.COUPON_STATUS_UNUSED);
List<EtCouponUserLog> couponUserLogs = etCouponClaimLogMapper.selectEtCouponClaimLogList(etCouponUserLog);
// 获取当前时间
Date currentTime = new Date();
for(EtCouponUserLog couponLog :couponUserLogs){
// 获取优惠券的过期时间
Date expirationTime = couponLog.getExpirationTime();
// 判断优惠券是否过期
if (expirationTime != null && expirationTime.before(currentTime)) {
// 如果过期,更新状态为已过期
couponLog.setStatus(ServiceConstants.COUPON_STATUS_EXPIRED);
// 更新到数据库
int result = etCouponClaimLogMapper.updateEtCouponClaimLog(couponLog);
if (result > 0) {
log.info("优惠券ID: {} 已过期,状态已更新为:已过期", couponLog.getCouponId());
} else {
log.warn("优惠券ID: {} 更新状态失败", couponLog.getCouponId());
}
}
}
log.info("-------------------【定时任务】判断优惠券是否过期---结束----------------");
}
/**
* 更新设备的在线状态
*
*/
public void updateDeviceOnlineStatus(){
log.info("-------------------【定时任务】更新设备的在线状态---开始----------------");
List<AsDeviceVO> deviceVOS = asDeviceMapper.selectAllDevice();
for(AsDeviceVO deviceVO : deviceVOS){
CreateDeviceVo createDeviceVo = new CreateDeviceVo();
createDeviceVo.setDevice_name(deviceVO.getMac());
createDeviceVo.setProduct_id(productId);
try {
String param = "device_name=" + deviceVO.getMac() + "&product_id=" + productId;
String sendUrl = deviceUrl+"/detail" + "?"+param;
String result = HttpUtils.sendGetWithToken(sendUrl,null, Token.getToken());
log.info("【定时--更新设备的在线状态】===>IOT请求调用结果:【{}】",result);
DeviceInfo deviceInfo = JSON.parseObject(result, DeviceInfo.class);
int onlineStatus = Integer.parseInt(deviceVO.getOnlineStatus());
if(deviceInfo.getCode() != 0){
log.info("【定时--更新设备的在线状态】===>IOT请求调用失败");
continue;
}
int onenetStatus = deviceInfo.getData().getStatus();
if(onenetStatus != onlineStatus ){
AsDevice device = new AsDevice();
device.setMac(deviceVO.getMac());
if(onenetStatus!=2){
device.setOnlineStatus(onenetStatus+"");
}
int i = asDeviceMapper.updateAsDeviceByMac(device);
log.info("【定时--更新设备的在线状态】===>更新设备状态结果:【{}】",i);
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
}
}
}
/**
* 平台对账
* */
@SneakyThrows
public void selfReconciliation(String timeStart, String timeEnd, String areaId) {
long aLong = 0;
if(StrUtil.isNotBlank(areaId)){
aLong = Long.parseLong(areaId);
}
if(StrUtil.isBlank(timeStart) || StrUtil.isBlank(timeEnd)){
// 默认取昨天的时间
LocalDate yesterday = LocalDate.now().minusDays(1);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
timeStart = yesterday.format(formatter);
timeEnd = yesterday.format(formatter);
}
List<ChannelVO> channelVOS = smEtChannelService.selectSmChannelList(new ChannelQuery());
for (ChannelVO channel:channelVOS) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
int limit = DateUtils.differentDaysByMillisecond(timeStart, timeEnd) + 1;
Calendar calendar = Calendar.getInstance();
calendar.setTime(dateFormat.parse(timeEnd));
for (int i = 0; i < limit; i++) {
String formattedDate = dateFormat.format(calendar.getTime());
String startDateStr = formattedDate + " " + Constants.DATE_FORMAT_START_PEREND;
String endDateStr = formattedDate + " " + Constants.DATE_FORMAT_END_PEREND;
/** 总收入*/
SelfReconciliationVO selfReconciliationVO = buildSelfReconciliation(startDateStr,endDateStr, formattedDate,aLong,channel.getChannelId());
// 保存
saveRecon(formattedDate, selfReconciliationVO,channel.getChannelId());
calendar.add(Calendar.DATE, -1);
}
}
}
private SelfReconciliationVO buildSelfReconciliation(String startDateStr, String endDateStr, String formattedDate, Long aLong, Long channelId) {
SelfReconciliationVO selfReconciliationVO = new SelfReconciliationVO();
selfReconciliationVO.setDay(formattedDate);
BigDecimal totalFlowAmount = defaultIfNull(etOrderMapper.getTotalPaidFee(startDateStr, endDateStr, aLong,channelId),BigDecimal.ZERO);// 骑行订单收入 29835.51
BigDecimal totalAmount = defaultIfNull(etCapitalFlowMapper.getOrderPaidAmount(startDateStr, endDateStr, aLong,channelId),BigDecimal.ZERO);// 骑行订单收入 4712.51
BigDecimal deductionAmount = defaultIfNull(etOrderMapper.getDepositDeductionAmount(startDateStr, endDateStr, aLong,channelId),BigDecimal.ZERO);// 押金抵扣金额 538
BigDecimal depositAmount = defaultIfNull(etOrderMapper.getDepositAmount(startDateStr, endDateStr, aLong,channelId),BigDecimal.ZERO);// 押金收入 25123
BigDecimal handlingFee = etCapitalFlowMapper.getHandlingFee2(startDateStr, endDateStr, null, aLong,channelId);//手续费,扣除掉退款部分的
BigDecimal platformServiceFee = etCapitalFlowMapper.getServiceFee2(startDateStr, endDateStr, null,aLong,channelId);//平台服务费 ,扣除掉退款部分的
selfReconciliationVO.setOrderPaid(totalAmount);
selfReconciliationVO.setTotalFlowAmount(totalFlowAmount);
selfReconciliationVO.setDeductionAmount(deductionAmount);
selfReconciliationVO.setDepositPaid(depositAmount);
selfReconciliationVO.setHandlingCharge(handlingFee);
selfReconciliationVO.setPlatformServiceFee(platformServiceFee);
/** 总支出*/
BigDecimal orderRefund = defaultIfNull(etOrderMapper.getRefundFee2(startDateStr, endDateStr, null, aLong,channelId), BigDecimal.ZERO);//订单退款
BigDecimal depositRefundFee = defaultIfNull(etOrderMapper.getDepositRefundFee(startDateStr, endDateStr, null, aLong,channelId), BigDecimal.ZERO);//押金退款 24795 25386
// depositChange 等于depositAmount减去deductionAmount减去depositRefundFee
selfReconciliationVO.setDepositChange(depositAmount.subtract(deductionAmount).subtract(depositRefundFee));
// 统计所有用户今日账变 进账
BigDecimal userReceipts = defaultIfNull(etCapitalFlowMapper.getAllUserReceipts(startDateStr, endDateStr, aLong,channelId),BigDecimal.ZERO);
selfReconciliationVO.setOrderRefund(orderRefund);
selfReconciliationVO.setDepositRefund(depositRefundFee);
BigDecimal depositBalance = depositAmount.subtract(depositRefundFee).subtract(deductionAmount);
selfReconciliationVO.setDepositBalance(depositBalance);
selfReconciliationVO.setUserReceipts(userReceipts);
// 结算金额等于totalFlowAmount减去orderRefund减去depositRefundFee
selfReconciliationVO.setSettlementAmount(totalFlowAmount.subtract(orderRefund).subtract(depositRefundFee));
return selfReconciliationVO;
}
private void saveRecon(String formattedDate,SelfReconciliationVO selfReconciliationVO,Long channelId) {
EtReconciliation etReconciliation = new EtReconciliation();
etReconciliation.setDay(formattedDate);
etReconciliation.setTotalAmount(selfReconciliationVO.getTotalFlowAmount());
etReconciliation.setHandlingCharge(selfReconciliationVO.getHandlingCharge());
etReconciliation.setPlatformServiceFee(selfReconciliationVO.getPlatformServiceFee());
etReconciliation.setDepositPaid(selfReconciliationVO.getDepositPaid());
etReconciliation.setOrderPaid(selfReconciliationVO.getOrderPaid());
etReconciliation.setOrderRefund(selfReconciliationVO.getOrderRefund());
etReconciliation.setOrderSurplus(selfReconciliationVO.getOrderPaid().subtract(selfReconciliationVO.getOrderRefund()).add(selfReconciliationVO.getDeductionAmount()));
etReconciliation.setDepositSurplus(selfReconciliationVO.getDepositChange());
etReconciliation.setDepositRefund(selfReconciliationVO.getDepositRefund());
etReconciliation.setDeductionAmount(selfReconciliationVO.getDeductionAmount());
etReconciliation.setUserReceipts(selfReconciliationVO.getUserReceipts());
etReconciliation.setSettlementAmount(selfReconciliationVO.getSettlementAmount());
etReconciliation.setCreateTime(DateUtils.getNowDate());
etReconciliation.setPayChannel(channelId);
int i1 = etReconciliationMapper.insertEtReconciliation(etReconciliation);
log.info("【平台对账】保存对账数据结果:【{}】",i1);
}
private BigDecimal defaultIfNull(BigDecimal value, BigDecimal defaultValue) {
return value != null ? value : defaultValue;
}
/**
* 如果发现车型没有关联收费模式则发短信提示
* */
@SneakyThrows
public void sendMsgtips() {
List<EtModel> etModels = etModelMapper.selectNotRuleModelList();
for (EtModel etModel :etModels){
QueryWrapper<EtModelRule> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("model_id", etModel.getModelId()); // 设备状态正常
Integer integer = etModelRuleMapper.selectCount(queryWrapper);
if(integer == 0){
JSONObject jsonObject = new JSONObject();
jsonObject.put("name",etModel.getModelId());
SendSmsVo sendSmsVo = new SendSmsVo();
sendSmsVo.setMobile("18650502300");
sendSmsVo.setTemplateCode(templateCode);
sendSmsVo.setParam(jsonObject.toJSONString());
sendSmsVo.setSignName(signName);
SendSmsResponse response = SendAliSmsUtil.sendVerifyCode(accessKeyId,accessKeySecret,sendSmsVo);
log.info("【发送短信】发送短信结果:【{}】",response.getMessage());
}
}
}
}