feat 退款订单定时同步任务

This commit is contained in:
xxm1995
2024-03-12 17:57:15 +08:00
parent b1de4e83f8
commit 197382b1f8
14 changed files with 360 additions and 103 deletions

185
README.md
View File

@@ -213,3 +213,188 @@ QQ扫码加入QQ交流群
## 🍷License
Apache License Version 2.0
# 🚀Dromara成员项目
<style>
.member-project {
display: flex;
flex-wrap: wrap;
}
.member-project a {
padding: 10px;
}
.member-project a img {
height: 40px;
}
</style>
<div class="member-project">
<a href="https://gitee.com/dromara/TLog" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/tlog.png" alt="TLog" title="一个轻量级的分布式日志标记追踪神器10分钟即可接入自动对日志打标签完成微服务的链路追踪">
</a>
<a href="https://gitee.com/dromara/liteFlow" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/liteflow.png" alt="liteFlow" title="轻量,快速,稳定,可编排的组件式流程引擎">
</a>
<a href="https://hutool.cn/" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/hutool.jpg" alt="hutool" title="小而全的Java工具类库使Java拥有函数式语言般的优雅让Java语言也可以“甜甜的”。">
</a>
<a href="https://sa-token.cc/" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/sa-token.png" alt="sa-token" title="一个轻量级 java 权限认证框架,让鉴权变得简单、优雅!">
</a>
<a href="https://gitee.com/dromara/hmily" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/hmily.png" alt="hmily" title="高性能一站式分布式事务解决方案。">
</a>
<a href="https://gitee.com/dromara/Raincat" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/raincat.png" alt="Raincat" title="强一致性分布式事务解决方案。">
</a>
<a href="https://gitee.com/dromara/myth" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/myth.png" alt="myth" title="可靠消息分布式事务解决方案。">
</a>
<a href="https://cubic.jiagoujishu.com/" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/cubic.png" alt="cubic" title="一站式问题定位平台以agent的方式无侵入接入应用完整集成arthas功能模块致力于应用级监控帮助开发人员快速定位问题">
</a>
<a href="https://maxkey.top/" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/maxkey.png" alt="maxkey" title="业界领先的身份管理和认证产品">
</a>
<a href="http://forest.dtflyx.com/" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/forest-logo.png" alt="forest" title="Forest能够帮助您使用更简单的方式编写Java的HTTP客户端">
</a>
<a href="https://jpom.top/" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/jpom.png" alt="jpom" title="一款简而轻的低侵入式在线构建、自动部署、日常运维、项目监控软件">
</a>
<a href="https://su.usthe.com/" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/sureness.png" alt="sureness" title="面向 REST API 的高性能认证鉴权框架">
</a>
<a href="https://easy-es.cn/" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/easy-es2.png" alt="easy-es" title="傻瓜级ElasticSearch搜索引擎ORM框架">
</a>
<a href="https://gitee.com/dromara/northstar" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/northstar_logo.png" alt="northstar" title="Northstar盈富量化交易平台">
</a>
<a href="https://hertzbeat.com/" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/hertzbeat-brand.svg" alt="hertzbeat" title="易用友好的云监控系统">
</a>
<a href="https://dromara.gitee.io/fast-request/" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/fast-request.png" alt="fast-request" title="Idea 版 Postman为简化调试API而生">
</a>
<a href="https://www.jeesuite.com/" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/mendmix.png" alt="mendmix" title="开源分布式云原生架构一站式解决方案">
</a>
<a href="https://gitee.com/dromara/koalas-rpc" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/koalas-rpc2.png" alt="koalas-rpc" title="企业生产级百亿日PV高可用可拓展的RPC框架。">
</a>
<a href="https://async.sizegang.cn/" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/gobrs-async.png" alt="gobrs-async" title="配置极简功能强大的异步任务动态编排框架">
</a>
<a href="https://dynamictp.cn/" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/dynamic-tp.png" alt="dynamic-tp" title="基于配置中心的轻量级动态可监控线程池">
</a>
<a href="https://www.x-easypdf.cn" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/x-easypdf.png" alt="x-easypdf" title="一个用搭积木的方式构建pdf的框架基于pdfbox">
</a>
<a href="http://dromara.gitee.io/image-combiner" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/image-combiner.png" alt="image-combiner" title="一个专门用于图片合成的工具,没有很复杂的功能,简单实用,却不失强大">
</a>
<a href="https://www.herodotus.cn/" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/dante-cloud2.png" alt="dante-cloud" title="Dante-Cloud 是一款企业级微服务架构和服务能力开发平台。">
</a>
<a href="http://www.mtruning.club" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/go-view.png" alt="go-view" title="低代码数据可视化开发平台">
</a>
<a href="https://tangyh.top/" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/lamp-cloud.png" alt="lamp-cloud" title="微服务中后台快速开发平台,支持租户(SaaS)模式、非租户模式">
</a>
<a href="https://www.redisfront.com/" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/redis-front.png" alt="redis-front" title="RedisFront 是一款开源免费的跨平台 Redis 桌面客户端工具, 支持单机模式, 集群模式, 哨兵模式以及 SSH 隧道连接, 可轻松管理Redis缓存数据.">
</a>
<a href="https://www.yuque.com/u34495/mivcfg" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/electron-egg.png" alt="electron-egg" title="一个入门简单、跨平台、企业级桌面软件开发框架">
</a>
<a href="https://gitee.com/dromara/open-capacity-platform" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/open-capacity-platform.jpg" alt="open-capacity-platform" title="简称ocp是基于Spring Cloud的企业级微服务框架(用户权限管理,配置中心管理,应用管理,....)">
</a>
<a href="http://easy-trans.fhs-opensource.top/" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/easy_trans.png" alt="Easy-Trans" title="Easy-Trans 一个注解搞定数据翻译,减少30%SQL代码量">
</a>
<a href="https://gitee.com/dromara/neutrino-proxy" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/neutrino-proxy.svg" alt="neutrino-proxy" title="一款基于 Netty 的、开源的内网穿透神器。">
</a>
<a href="https://chatgpt.cn.obiscr.com/" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/chatgpt.png" alt="chatgpt" title="一个支持在 JetBrains 系列 IDE 上运行的 ChatGPT 的插件。">
</a>
<a href="https://gitee.com/dromara/zyplayer-doc" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/zyplayer-doc.png" alt="zyplayer-doc" title="zyplayer-doc是一款适合团队和个人使用的WIKI文档管理工具同时还包含数据库文档、Api接口文档。">
</a>
<a href="https://gitee.com/dromara/payment-spring-boot" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/payment-spring-boot.png" alt="payment-spring-boot" title="最全最好用的微信支付V3 Spring Boot 组件。">
</a>
<a href="https://www.j2eefast.com/" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/j2eefast.png" alt="j2eefast" title="J2eeFAST 是一个致力于中小企业 Java EE 企业级快速开发平台,我们永久开源!">
</a>
<a href="https://gitee.com/dromara/data-compare" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/dataCompare.png" alt="data-compare" title="数据库比对工具hive 表数据比对mysql、Doris 数据比对实现自动化配置进行数据比对避免频繁写sql 进行处理,低代码(Low-Code) 平台">
</a>
<a href="https://gitee.com/dromara/open-giteye-api" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/open-giteye-api.svg" alt="open-giteye-api" title="giteye.net 是专为开源作者设计的数据图表服务工具类站点,提供了包括 Star 趋势图、贡献者列表、Gitee指数等数据图表服务。">
</a>
<a href="https://gitee.com/dromara/RuoYi-Vue-Plus" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/RuoYi-Vue-Plus.png" alt="RuoYi-Vue-Plus" title="后台管理系统 重写 RuoYi-Vue 所有功能 集成 Sa-Token + Mybatis-Plus + Jackson + Xxl-Job + SpringDoc + Hutool + OSS 定期同步">
</a>
<a href="https://gitee.com/dromara/RuoYi-Cloud-Plus" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/RuoYi-Cloud-Plus.png" alt="RuoYi-Cloud-Plus" title="微服务管理系统 重写RuoYi-Cloud所有功能 整合 SpringCloudAlibaba Dubbo3.0 Sa-Token Mybatis-Plus MQ OSS ES Xxl-Job Docker 全方位升级 定期同步">
</a>
<a href="https://gitee.com/dromara/stream-query" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/stream-query.png" alt="stream-query" title="允许完全摆脱 Mapper 的 mybatis-plus 体验!封装 stream 和 lambda 操作进行数据返回处理。">
</a>
<a href="https://wind.kim/" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/sms4j.png" alt="sms4j" title="短信聚合工具,让发送短信变的更简单。">
</a>
<a href="https://cloudeon.top/" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/cloudeon.png" alt="cloudeon" title="简化kubernetes上大数据集群的运维管理">
</a>
<a href="https://github.com/dromara/hodor" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/hodor.png" alt="hodor" title="Hodor是一个专注于任务编排和高可用性的分布式任务调度系统。">
</a>
<a href="http://nsrule.com/" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/test-hub.png" alt="test-hub" title="流程编排,插件驱动,测试无限可能">
</a>
<a href="https://gitee.com/dromara/disjob" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/disjob-2.png" alt="disjob" title="Disjob是一个分布式的任务调度框架">
</a>
<a href="https://gitee.com/dromara/binlog4j" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/Binlog4j.png" alt="binlog4j" title="轻量级 Mysql Binlog 客户端, 提供宕机续读, 高可用集群等特性">
</a>
<a href="https://gitee.com/dromara/yft-design" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/yft-design.png" alt="yft-design" title="基于 Canvas 的开源版 创客贴 支持导出jsonsvg, image文件。">
</a>
<a href="https://x-file-storage.xuyanwu.cn" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/logo3.svg" alt="x-file-storage" title="">
</a>
<a href="https://wemq.nicholasld.cn" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/WeMQ.png" alt="WeMQAQ" title="开源、高性能、安全、功能强大的物联网调试和管理解决方案。">
</a>
<a href="https://www.yuque.com/may-fly/mayfly-go" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/mayfly-go.png" alt="Mayfly-Go" title="web版 linux、数据库、redis、mongo统一管理操作平台。">
</a>
<a href="https://akali.yomahub.com" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/Akali.png" alt="Akali" title="Akali(阿卡丽),轻量级本地化热点检测/降级框架10秒钟即可接入使用大流量下的神器">
</a>
<a href="https://dbswitch.gitee.io/docs-site/#/" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/dbswitch.png" alt="dbswitch" title="异构数据库迁移同步工具dbswitch提供源端数据库向目的端数据的全量与增量迁移同步功能">
</a>
<a href="https://gitee.com/dromara/easyAi" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/easyAi.png" alt="easyAi" title="java傻瓜ai框架无需任何算法知识通过简单的api调用就可以实现常用的图像内物体的识别定位等图像ai服务及自然语言分类处理服务。面向java开发程序员不依赖任何第三方库第三方接口独立包。">
</a>
<a href="https://gitee.com/dromara/tianai-captcha" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/tianai-captcha.png" alt="tianai-captcha" title="可能是java界最好的开源行为验证码[滑块验证码、点选验证码、行为验证码、旋转验证码, 滑动验证码]">
</a>
<a href="https://dromara.org/zh/projects/" target="_blank">
<img src="https://x-file-storage.xuyanwu.cn/assets/link/dromara.png" alt="dromara" title="让每一位开源爱好者,体会到开源的快乐。">
</a>
</div>

View File

@@ -37,4 +37,12 @@ public class PayOrderManager extends BaseManager<PayOrderMapper, PayOrder> {
QueryWrapper<PayOrder> generator = QueryGenerator.generator(query);
return page(mpPage, generator);
}
/**
* 强制更新 (忽略版本号)
*/
public void updateForceById(PayOrder payOrder) {
payOrder.setVersion(null);
this.updateById(payOrder);
}
}

View File

@@ -56,4 +56,17 @@ public class PayOrderService {
}
payOrderManager.updateById(payOrder);
}
/**
* 使用强制更新
*/
public void updateForceById(PayOrder payOrder){
// 如果是异步支付且支付订单完成, 需要删除订单超时任务记录
if (payOrder.isAsyncPay() && ORDER_FINISH.contains(payOrder.getStatus())){
expiredTimeService.cancelExpiredTime(payOrder.getId());
}
payOrder.setVersion(null);
payOrderManager.updateForceById(payOrder);
}
}

View File

@@ -4,6 +4,7 @@ import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.common.mybatisplus.impl.BaseManager;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.bootx.platform.common.query.generator.QueryGenerator;
import cn.bootx.platform.daxpay.code.RefundStatusEnum;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundOrder;
import cn.bootx.platform.daxpay.service.param.order.RefundOrderQuery;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@@ -12,6 +13,8 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
/**
@@ -46,4 +49,15 @@ public class RefundOrderManager extends BaseManager<RefundOrderMapper, RefundOrd
public boolean existsByRefundNo(String refundNo){
return this.existedByField(RefundOrder::getRefundNo,refundNo);
}
/**
* 查询退款中的支付订单
*/
public List<RefundOrder> findAllByProgress() {
LocalDateTime now = LocalDateTime.now();
return lambdaQuery()
.le(RefundOrder::getRefundTime,now)
.eq(RefundOrder::getStatus, RefundStatusEnum.PROGRESS)
.list();
}
}

View File

@@ -42,7 +42,7 @@ public class PayCallbackService {
public void payCallback() {
CallbackLocal callbackInfo = PaymentContextLocal.get().getCallbackInfo();
// 加锁
LockInfo lock = lockTemplate.lock("callback:payment:" + callbackInfo.getOrderId());
LockInfo lock = lockTemplate.lock("callback:payment:" + callbackInfo.getOrderId(),10000, 200);
if (Objects.isNull(lock)){
callbackInfo.setCallbackStatus(PayCallbackStatusEnum.IGNORE).setMsg("回调正在处理中,忽略本次回调请求");
log.warn("订单号: {} 回调正在处理中,忽略本次回调请求", callbackInfo.getOrderId());

View File

@@ -39,7 +39,7 @@ public class RefundCallbackService {
CallbackLocal callbackInfo = PaymentContextLocal.get().getCallbackInfo();
// 加锁
LockInfo lock = lockTemplate.lock("callback:refund:" + callbackInfo.getOrderId());
LockInfo lock = lockTemplate.lock("callback:refund:" + callbackInfo.getOrderId(),10000, 200);
if (Objects.isNull(lock)){
callbackInfo.setCallbackStatus(PayCallbackStatusEnum.IGNORE).setMsg("回调正在处理中,忽略本次回调请求");
log.warn("订单号: {} 回调正在处理中,忽略本次回调请求", callbackInfo.getOrderId());

View File

@@ -63,7 +63,7 @@ public class PayCloseService {
payOrder = payOrderQueryService.findByBusinessNo(param.getBusinessNo())
.orElseThrow(() -> new PayFailureException("未查询到支付订单"));
}
LockInfo lock = lockTemplate.lock("payment:close:" + payOrder.getId());
LockInfo lock = lockTemplate.lock("payment:close:" + payOrder.getId(),10000, 50);
if (Objects.isNull(lock)){
throw new RepetitiveOperationException("支付订单已在关闭中,请勿重复发起");
}

View File

@@ -189,7 +189,7 @@ public class ClientNoticeService {
private void run(Long taskId){
LocalDateTime now = LocalDateTime.now();
// 开启分布式锁
LockInfo lock = lockTemplate.lock(KEY + ":" + taskId,2000, 50);
LockInfo lock = lockTemplate.lock(KEY + ":" + taskId,10000, 200);
if (Objects.isNull(lock)){
throw new RepetitiveOperationException("支付同步处理中,请勿重复操作");
}

View File

@@ -1,54 +0,0 @@
package cn.bootx.platform.daxpay.service.core.payment.pay.task;
import cn.bootx.platform.common.core.exception.RepetitiveOperationException;
import cn.bootx.platform.daxpay.param.pay.PaySyncParam;
import cn.bootx.platform.daxpay.service.core.payment.pay.dao.PayExpiredTimeRepository;
import cn.bootx.platform.daxpay.service.core.payment.sync.service.PaySyncService;
import com.baomidou.lock.LockInfo;
import com.baomidou.lock.LockTemplate;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Objects;
import java.util.Set;
/**
* 待支付订单的状态同步, 先不进行启用
* @author xxm
* @since 2024/1/5
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class PayWaitOrderSyncTask {
private final PayExpiredTimeRepository repository;
private final PaySyncService paySyncService;
private final LockTemplate lockTemplate;
public void task(){
log.debug("开始同步支付订单状态");
// 从超时订单列表中获取到未超时的订单号
Set<String> keys = repository.getNormalKeysBy30Day();
for (String key : keys) {
LockInfo lock = lockTemplate.lock("payment:sync:" + key,10000,200);
if (Objects.isNull(lock)){
throw new RepetitiveOperationException("支付同步处理中,请勿重复操作");
}
try {
Long paymentId = Long.parseLong(key);
PaySyncParam paySyncParam = new PaySyncParam();
paySyncParam.setPaymentId(paymentId);
// 执行网关同步, 网关同步时会对支付的进行状态的处理
paySyncService.sync(paySyncParam);
} catch (Exception e) {
log.error("同步支付订单异常", e);
} finally {
lockTemplate.releaseLock(lock);
}
}
}
}

View File

@@ -17,16 +17,15 @@ import cn.bootx.platform.daxpay.service.core.record.repair.entity.PayRepairRecor
import cn.bootx.platform.daxpay.service.core.record.repair.service.PayRepairRecordService;
import cn.bootx.platform.daxpay.service.func.AbsPayRepairStrategy;
import cn.hutool.core.util.IdUtil;
import com.baomidou.lock.LockInfo;
import com.baomidou.lock.LockTemplate;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -48,11 +47,20 @@ public class PayRepairService {
private final PayRepairRecordService recordService;
private final LockTemplate lockTemplate;
/**
* 修复支付单
*/
@Transactional(rollbackFor = Exception.class)
public PayRepairResult repair(PayOrder order, PayRepairWayEnum repairType){
// 添加分布式锁
LockInfo lock = lockTemplate.lock("repair:pay:" + order.getId(), 10000, 200);
if (Objects.isNull(lock)){
log.warn("当前支付定单正在修复中: {}", order.getId());
return new PayRepairResult();
}
// 1. 获取支付单管理的通道支付订单
Map<String, PayChannelOrder> channelOrderMap = channelOrderManager.findAllByPaymentId(order.getId())
.stream()

View File

@@ -23,6 +23,8 @@ import cn.bootx.platform.daxpay.service.core.record.repair.entity.PayRepairRecor
import cn.bootx.platform.daxpay.service.core.record.repair.service.PayRepairRecordService;
import cn.bootx.platform.daxpay.service.func.AbsRefundRepairStrategy;
import cn.hutool.core.util.IdUtil;
import com.baomidou.lock.LockInfo;
import com.baomidou.lock.LockTemplate;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -56,54 +58,67 @@ public class RefundRepairService {
private final PayRepairRecordService recordService;
private final LockTemplate lockTemplate;
/**
* 修复退款单
*/
@Transactional(rollbackFor = Exception.class)
public RefundRepairResult repair(RefundOrder refundOrder, RefundRepairWayEnum repairType){
// 获取关联支付单
PayOrder payOrder = payOrderQueryService.findById(refundOrder.getPaymentId())
.orElseThrow(() -> new RuntimeException("支付单不存在"));
// 关联支付通道支付单
Map<String, PayChannelOrder> payChannelOrderMap = payChannelOrderManager.findAllByPaymentId(refundOrder.getPaymentId())
.stream()
.collect(Collectors.toMap(PayChannelOrder::getChannel, Function.identity(), CollectorsFunction::retainLatest));
// 异步通道退款单
Map<String, RefundChannelOrder> refundChannelOrderMap = refundChannelOrderManager.findAllByRefundId(refundOrder.getId())
.stream()
.collect(Collectors.toMap(RefundChannelOrder::getChannel, Function.identity(), CollectorsFunction::retainLatest));
// 2 初始化修复参数
List<String> channels = new ArrayList<>(payChannelOrderMap.keySet());
List<AbsRefundRepairStrategy> repairStrategies = RefundRepairStrategyFactory.createAsyncLast(channels);
for (AbsRefundRepairStrategy repairStrategy : repairStrategies) {
PayChannelOrder payChannelOrder = payChannelOrderMap.get(repairStrategy.getChannel().getCode());
RefundChannelOrder refundChannelOrder = refundChannelOrderMap.get(repairStrategy.getChannel().getCode());
repairStrategy.initRepairParam(refundOrder, refundChannelOrder, payOrder, payChannelOrder);
// 添加分布式锁
LockInfo lock = lockTemplate.lock("repair:refund:" + refundOrder.getId(), 10000, 200);
if (Objects.isNull(lock)){
log.warn("当前退款单正在修复中: {}", refundOrder.getId());
return new RefundRepairResult();
}
try {
// 获取关联支付单
PayOrder payOrder = payOrderQueryService.findById(refundOrder.getPaymentId())
.orElseThrow(() -> new RuntimeException("支付单不存在"));
// 关联支付通道支付单
Map<String, PayChannelOrder> payChannelOrderMap = payChannelOrderManager.findAllByPaymentId(refundOrder.getPaymentId())
.stream()
.collect(Collectors.toMap(PayChannelOrder::getChannel, Function.identity(), CollectorsFunction::retainLatest));
// 异步通道退款单
Map<String, RefundChannelOrder> refundChannelOrderMap = refundChannelOrderManager.findAllByRefundId(refundOrder.getId())
.stream()
.collect(Collectors.toMap(RefundChannelOrder::getChannel, Function.identity(), CollectorsFunction::retainLatest));
// 根据不同的类型执行对应的修复逻辑
RefundRepairResult repairResult = new RefundRepairResult();
if (Objects.requireNonNull(repairType) == RefundRepairWayEnum.REFUND_SUCCESS) {
repairResult = this.success(refundOrder,payOrder,repairStrategies);
} else if (repairType == RefundRepairWayEnum.REFUND_FAIL) {
repairResult = this.close(refundOrder,payOrder,repairStrategies);
} else {
log.error("走到了理论上讲不会走到的分支");
// 2 初始化修复参数
List<String> channels = new ArrayList<>(payChannelOrderMap.keySet());
List<AbsRefundRepairStrategy> repairStrategies = RefundRepairStrategyFactory.createAsyncLast(channels);
for (AbsRefundRepairStrategy repairStrategy : repairStrategies) {
PayChannelOrder payChannelOrder = payChannelOrderMap.get(repairStrategy.getChannel()
.getCode());
RefundChannelOrder refundChannelOrder = refundChannelOrderMap.get(repairStrategy.getChannel()
.getCode());
repairStrategy.initRepairParam(refundOrder, refundChannelOrder, payOrder, payChannelOrder);
}
// 根据不同的类型执行对应的修复逻辑
RefundRepairResult repairResult = new RefundRepairResult();
if (Objects.requireNonNull(repairType) == RefundRepairWayEnum.REFUND_SUCCESS) {
repairResult = this.success(refundOrder, payOrder, repairStrategies);
} else if (repairType == RefundRepairWayEnum.REFUND_FAIL) {
repairResult = this.close(refundOrder, payOrder, repairStrategies);
} else {
log.error("走到了理论上讲不会走到的分支");
}
// 设置修复ID并保存修复记录
repairResult.setRepairNo(IdUtil.getSnowflakeNextIdStr());
// 支付修复记录
PayRepairRecord payRepairRecord = this.payRepairRecord(payOrder, repairType, repairResult);
// 退款修复记录
PayRepairRecord refundRepairRecord = this.refundRepairRecord(refundOrder, repairType, repairResult);
// 发送通知
clientNoticeService.registerRefundNotice(refundOrder, null, new ArrayList<>(refundChannelOrderMap.values()));
recordService.saveAllRecord(Arrays.asList(payRepairRecord, refundRepairRecord));
return repairResult;
} finally {
lockTemplate.releaseLock(lock);
}
// 设置修复ID并保存修复记录
repairResult.setRepairNo(IdUtil.getSnowflakeNextIdStr());
// 支付修复记录
PayRepairRecord payRepairRecord = this.payRepairRecord(payOrder, repairType, repairResult);
// 退款修复记录
PayRepairRecord refundRepairRecord = this.refundRepairRecord(refundOrder, repairType, repairResult);
// 发送通知
clientNoticeService.registerRefundNotice(refundOrder, null, new ArrayList<>(refundChannelOrderMap.values()));
recordService.saveAllRecord(Arrays.asList(payRepairRecord, refundRepairRecord));
return repairResult;
}
/**

View File

@@ -88,7 +88,7 @@ public class PaySyncService {
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public SyncResult syncPayOrder(PayOrder payOrder) {
// 加锁
LockInfo lock = lockTemplate.lock("sync:payment" + payOrder.getId(),10000,200);
LockInfo lock = lockTemplate.lock("sync:pay" + payOrder.getId(),10000,200);
if (Objects.isNull(lock)){
throw new RepetitiveOperationException("支付同步处理中,请勿重复操作");
}

View File

@@ -0,0 +1,27 @@
package cn.bootx.platform.daxpay.service.task;
import cn.bootx.platform.daxpay.service.task.service.RefundSyncTaskService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.stereotype.Component;
/**
* 退款定时同步任务 一分钟一次, 查询退款中的订单进行同步
* @author xxm
* @since 2024/3/12
*/
@Slf4j
@Component
@DisallowConcurrentExecution
@PersistJobDataAfterExecution
@RequiredArgsConstructor
public class RefundSyncTask implements Job {
private final RefundSyncTaskService refundSyncTaskService;
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
refundSyncTaskService.syncTask();
}
}

View File

@@ -0,0 +1,41 @@
package cn.bootx.platform.daxpay.service.task.service;
import cn.bootx.platform.daxpay.service.core.order.refund.dao.RefundOrderManager;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundOrder;
import cn.bootx.platform.daxpay.service.core.payment.sync.service.RefundSyncService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 定时
* @author xxm
* @since 2024/3/12
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class RefundSyncTaskService {
private final RefundSyncService refundSyncService;
private final RefundOrderManager refundOrderManager;
/**
* 同步任务
*/
public void syncTask(){
// 查询退款中的退款订单
List<RefundOrder> list = refundOrderManager.findAllByProgress();
for (RefundOrder refundOrder : list) {
try {
// 调用同步方法
refundSyncService.syncRefundOrder(refundOrder);
} catch (Exception e) {
log.warn("退款执行同步失败, ID: {}",refundOrder.getId());
}
}
}
}