fix(cloud): 优化预约单过期处理和微信支付回调逻辑
- 添加费标准详情字段并实现相关查询逻辑 - 优化预约单过期处理逻辑,增加缓冲期和精确的时间判断 - 完善微信支付回调处理,增加参数校验和幂等性检查 - 添加支付成功通知功能
This commit is contained in:
parent
a0fef83bf3
commit
90b805d0f4
@ -1,6 +1,7 @@
|
|||||||
package com.evotech.hd.common.core.entity.cloud;
|
package com.evotech.hd.common.core.entity.cloud;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.IdType;
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
import com.evotech.hd.common.core.entity.BaseEntity;
|
import com.evotech.hd.common.core.entity.BaseEntity;
|
||||||
@ -96,6 +97,9 @@ public class OrderSwapBattery extends BaseEntity implements Serializable {
|
|||||||
|
|
||||||
@Schema(description = "费用标准")
|
@Schema(description = "费用标准")
|
||||||
private String feeStandardJson;
|
private String feeStandardJson;
|
||||||
|
@Schema(description = "费用标准详情")
|
||||||
|
@TableField(exist = false)
|
||||||
|
private BatteryStationHdFeeStandard feeStandardDetail;
|
||||||
|
|
||||||
@Schema(description = "上次租赁电池时车辆里程")
|
@Schema(description = "上次租赁电池时车辆里程")
|
||||||
private BigDecimal lastRentBatCarOdo;
|
private BigDecimal lastRentBatCarOdo;
|
||||||
|
|||||||
@ -53,6 +53,8 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
public class OrderSwapBatteryServiceImpl implements OrderSwapBatteryService {
|
public class OrderSwapBatteryServiceImpl implements OrderSwapBatteryService {
|
||||||
@ -87,6 +89,8 @@ public class OrderSwapBatteryServiceImpl implements OrderSwapBatteryService {
|
|||||||
private BatteryStationDao batteryStationDao;
|
private BatteryStationDao batteryStationDao;
|
||||||
@Resource
|
@Resource
|
||||||
private GZHTemplateMessageService gzhTemplateMessageService;
|
private GZHTemplateMessageService gzhTemplateMessageService;
|
||||||
|
@Resource
|
||||||
|
private BatteryStationHdFeeStandardDao batteryStationHdFeeStandardDao;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -247,8 +251,17 @@ public class OrderSwapBatteryServiceImpl implements OrderSwapBatteryService {
|
|||||||
if (page.getRecords().isEmpty()) {
|
if (page.getRecords().isEmpty()) {
|
||||||
return new Result<List<OrderSwapBattery>>().error(CodeMsg.DATABASE_RESULT_NULL);
|
return new Result<List<OrderSwapBattery>>().error(CodeMsg.DATABASE_RESULT_NULL);
|
||||||
}
|
}
|
||||||
return new Result<List<OrderSwapBattery>>().success(page);
|
page.getRecords().forEach(osb -> {
|
||||||
}
|
if (StringUtils.hasText(osb.getFeeStandardJson())){
|
||||||
|
String feeStandardJson = osb.getFeeStandardJson();
|
||||||
|
BatteryStationHdFeeStandard bean = JSONUtil.toBean(feeStandardJson, BatteryStationHdFeeStandard.class);
|
||||||
|
osb.setFeeStandardDetail(bean);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Result<List<OrderSwapBattery>>().success(page);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Result<List<OrderSwapBattery>> listByCompany(PageListSwapOrderRequest plsor) {
|
public Result<List<OrderSwapBattery>> listByCompany(PageListSwapOrderRequest plsor) {
|
||||||
|
|||||||
@ -86,28 +86,59 @@ public class TradeServiceImpl implements TradeService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public Result<String> wechatPayNotifyHandle(TradeDetail tradeDetail) {
|
public Result<String> wechatPayNotifyHandle(TradeDetail tradeDetail) {
|
||||||
// 1. 添加交易
|
try {
|
||||||
boolean b = tradeDetailDao.exists(new QueryWrapper<TradeDetail>()
|
// 1. 参数校验
|
||||||
.eq("out_trade_no", tradeDetail.getOutTradeNo())
|
if (tradeDetail == null || !StringUtils.hasText(tradeDetail.getOutTradeNo())) {
|
||||||
.eq("transaction_id", tradeDetail.getTransactionId())
|
log.error("微信支付回调参数异常: {}", JSONUtil.toJsonStr(tradeDetail));
|
||||||
.eq("pay_result", tradeDetail.getPayResult()));
|
return new Result<String>().error("参数异常");
|
||||||
if (b) {
|
}
|
||||||
log.info("\r\n===>>>已处理过此回调:" + tradeDetail);
|
|
||||||
return new Result<String>().error("重复消息!");
|
// 2. 幂等性检查
|
||||||
|
boolean isProcessed = tradeDetailDao.exists(new QueryWrapper<TradeDetail>()
|
||||||
|
.eq("out_trade_no", tradeDetail.getOutTradeNo())
|
||||||
|
.eq("transaction_id", tradeDetail.getTransactionId())
|
||||||
|
.eq("pay_result", tradeDetail.getPayResult()));
|
||||||
|
|
||||||
|
if (isProcessed) {
|
||||||
|
log.info("重复的微信支付回调, outTradeNo: {}", tradeDetail.getOutTradeNo());
|
||||||
|
return new Result<String>().error("重复消息");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 记录交易
|
||||||
|
log.info("开始处理微信支付回调, outTradeNo: {}, 交易信息: {}",
|
||||||
|
tradeDetail.getOutTradeNo(),
|
||||||
|
JSONUtil.toJsonStr(tradeDetail));
|
||||||
|
|
||||||
|
add(tradeDetail);
|
||||||
|
|
||||||
|
// 4. 业务处理
|
||||||
|
payNotifyHandleService.payNotifyBussinessHandle(tradeDetail);
|
||||||
|
|
||||||
|
// 5. 发送支付成功通知
|
||||||
|
if (StringUtils.hasText(tradeDetail.getOrderNo())) {
|
||||||
|
OrderSwapBattery order = orderSwapBatteryDao.selectOne(
|
||||||
|
new LambdaQueryWrapper<OrderSwapBattery>()
|
||||||
|
.eq(OrderSwapBattery::getOrderNo, tradeDetail.getOrderNo())
|
||||||
|
);
|
||||||
|
|
||||||
|
if (order != null) {
|
||||||
|
gzhTemplateMessageService.orderMessageSend2(order.getPkId(), 3);
|
||||||
|
log.info("支付成功通知已发送, orderNo: {}", tradeDetail.getOrderNo());
|
||||||
|
} else {
|
||||||
|
log.warn("未找到对应的订单, orderNo: {}", tradeDetail.getOrderNo());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Result<String>().success("OK");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("处理微信支付回调异常, outTradeNo: {}, 异常信息: {}",
|
||||||
|
tradeDetail.getOutTradeNo(),
|
||||||
|
e.getMessage(),
|
||||||
|
e);
|
||||||
|
throw new RuntimeException("处理支付回调失败", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加交易
|
|
||||||
log.info("请求时间{} 是否存在交易{} 添加交易>>>>>{}" ,new Date() ,b, JSONUtil.toJsonStr(tradeDetail));
|
|
||||||
add(tradeDetail);
|
|
||||||
// 2. 业务类型处理
|
|
||||||
payNotifyHandleService.payNotifyBussinessHandle(tradeDetail);
|
|
||||||
|
|
||||||
return new Result<String>().success("OK");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package com.evotech.hd.cloud.task;
|
|||||||
import cn.hutool.core.collection.CollectionUtil;
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
import cn.hutool.core.date.DateUnit;
|
import cn.hutool.core.date.DateUnit;
|
||||||
import cn.hutool.core.date.DateUtil;
|
import cn.hutool.core.date.DateUtil;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
import com.evotech.hd.cloud.dao.OrderSwapBatteryPreDao;
|
import com.evotech.hd.cloud.dao.OrderSwapBatteryPreDao;
|
||||||
import com.evotech.hd.cloud.service.OrderSwapBatteryService;
|
import com.evotech.hd.cloud.service.OrderSwapBatteryService;
|
||||||
@ -16,6 +17,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.springframework.data.redis.core.RedisTemplate;
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
@ -32,37 +34,62 @@ public class OrderSwapBatteryTask {
|
|||||||
@Resource
|
@Resource
|
||||||
private RedisUtil redisUtil;
|
private RedisUtil redisUtil;
|
||||||
|
|
||||||
@Scheduled(cron = "0 0 * * * ?") // 每小时执行一次
|
// @Scheduled(cron = "0 0 * * * ?") // 每小时执行一次
|
||||||
|
@Scheduled(cron = "0 */2 * * * ?")
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void orderSwapBatteryExpired() {
|
public void orderSwapBatteryExpired() {
|
||||||
log.info("\r\n===>>> 开始查找预约单更新预约状态..");
|
log.info("\r\n===>>> 开始查找预约单更新预约状态..");
|
||||||
|
|
||||||
// 查询条件,排除已过期和无效状态
|
// 查询条件,只查询未处理的预约单
|
||||||
QueryWrapper<OrderSwapBatteryPre> queryWrapper = new QueryWrapper<>();
|
LambdaQueryWrapper<OrderSwapBatteryPre> queryWrapper = new LambdaQueryWrapper<>();
|
||||||
queryWrapper
|
queryWrapper.eq( OrderSwapBatteryPre::getStatus,1)
|
||||||
.ne("status", 4)//无效
|
.le( OrderSwapBatteryPre::getReservationTime, new Date())
|
||||||
.ne("status", 3) //过期
|
.select(OrderSwapBatteryPre::getPkId,
|
||||||
.ne("status", 2); //过期
|
OrderSwapBatteryPre::getSourceId,
|
||||||
|
OrderSwapBatteryPre::getReservationTime,
|
||||||
|
OrderSwapBatteryPre::getSwapDuration,
|
||||||
|
OrderSwapBatteryPre::getStatus)
|
||||||
|
.orderByDesc(OrderSwapBatteryPre::getReservationTime)
|
||||||
|
.last("limit 1000");
|
||||||
List<OrderSwapBatteryPre> orderSwapBatteryPreList = orderSwapBatteryPreDao.selectList(queryWrapper);
|
List<OrderSwapBatteryPre> orderSwapBatteryPreList = orderSwapBatteryPreDao.selectList(queryWrapper);
|
||||||
|
|
||||||
// 获取当前时间
|
|
||||||
Date currentTime = new Date();
|
Date currentTime = new Date();
|
||||||
int expiredCount = 0; // 记录过期的预约单数量
|
int expiredCount = 0;
|
||||||
|
|
||||||
for (OrderSwapBatteryPre order : orderSwapBatteryPreList) {
|
for (OrderSwapBatteryPre order : orderSwapBatteryPreList) {
|
||||||
// 检查预约时间是否已过期
|
try {
|
||||||
if (order.getReservationTime() != null && order.getReservationTime().before(currentTime)) {
|
// 1. 解析预约时间段
|
||||||
// 更新状态为过期
|
String[] timeRange = order.getSwapDuration().split("-");
|
||||||
order.setStatus(4); // 设置为过期状态
|
if (timeRange.length != 2) {
|
||||||
try {
|
log.error("预约时间段格式错误,订单ID: {}", order.getPkId());
|
||||||
orderSwapBatteryPreDao.updateById(order);
|
continue;
|
||||||
//发送公众号过期提醒
|
|
||||||
//templateMessageService.orderMessageSend(order, 4);
|
|
||||||
expiredCount++;
|
|
||||||
log.info("预约单已过期,订单ID: {}", order.getSourceId()); // 记录过期的订单ID
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("更新预约单状态失败,预约人:{},订单ID: {}, 错误信息: {}", order.getUname(), order.getPkId(), e.getMessage());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 2. 计算预约结束时间
|
||||||
|
Date reservationDate = order.getReservationTime();
|
||||||
|
String endTimeStr = timeRange[1].trim();
|
||||||
|
Date endTime = DateUtil.parse(DateUtil.format(reservationDate, "yyyy-MM-dd") + " " + endTimeStr,
|
||||||
|
"yyyy-MM-dd HH:mm");
|
||||||
|
|
||||||
|
// 3. 添加30分钟缓冲期
|
||||||
|
Date expireTime = DateUtil.offsetMinute(endTime, 30);
|
||||||
|
|
||||||
|
// 4. 判断是否过期
|
||||||
|
if (currentTime.after(expireTime)) {
|
||||||
|
order.setStatus(4); // 设置为过期状态
|
||||||
|
orderSwapBatteryPreDao.updateById(order);
|
||||||
|
|
||||||
|
// 发送公众号过期提醒
|
||||||
|
// templateMessageService.preOrderMessageSend(order);
|
||||||
|
|
||||||
|
expiredCount++;
|
||||||
|
log.info("预约单已过期,订单ID: {}, 预约时间: {}, 过期时间: {}",
|
||||||
|
order.getSourceId(),
|
||||||
|
DateUtil.format(order.getReservationTime(), "yyyy-MM-dd HH:mm:ss"),
|
||||||
|
DateUtil.format(expireTime, "yyyy-MM-dd HH:mm:ss"));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("处理预约单过期失败,预约人:{},订单ID: {}, 错误信息: {}",
|
||||||
|
order.getUname(), order.getPkId(), e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -123,5 +123,5 @@ hbyt:
|
|||||||
order_swap_end_template_id: wGll5W3NSHXkHjcEprjUbd1shigGOqywGX7A0BVnzUs
|
order_swap_end_template_id: wGll5W3NSHXkHjcEprjUbd1shigGOqywGX7A0BVnzUs
|
||||||
order_to_pay_template_id: IE0_RGuN5ufU4GFrCgM9JeUfvlWfbneEuUMDsn0RBQs
|
order_to_pay_template_id: IE0_RGuN5ufU4GFrCgM9JeUfvlWfbneEuUMDsn0RBQs
|
||||||
order_end_template_id: C5-AGh67aFXChQONqSwxaRrJgFrn_Z25VTlTk0jHSUY
|
order_end_template_id: C5-AGh67aFXChQONqSwxaRrJgFrn_Z25VTlTk0jHSUY
|
||||||
wallet_recharge_template_id: 8JXsn_VkI0S0YZSbT2EKmV_zOxyudo4IsBLeU7V-SFk
|
wallet_recharge_template_id: 8JXsn_VkI0S0YZSbT2EKmV_zOxyudo4IsBLeU7V-SFk
|
||||||
|
order_to_pay_template_id2: K6cdaBOkxGJpah0vsz5rq5UBID04mk-eEWp2XM0KyA0
|
||||||
Loading…
Reference in New Issue
Block a user