pay: PayNotifyJob 增加多租户的支持

This commit is contained in:
YunaiV
2022-11-24 23:56:13 +08:00
parent 0247fd5c69
commit 1cd9085c59
13 changed files with 144 additions and 94 deletions

View File

@ -0,0 +1,34 @@
package cn.iocoder.yudao.module.pay.api.notify.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
/**
* 支付单的通知 Request DTO
*
* @author 芋道源码
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class PayOrderNotifyReqDTO {
/**
* 商户订单编号
*/
@NotEmpty(message = "商户订单号不能为空")
private String merchantOrderId;
/**
* 支付订单编号
*/
@NotNull(message = "支付订单编号不能为空")
private Long payOrderId;
}

View File

@ -0,0 +1,42 @@
package cn.iocoder.yudao.module.pay.api.notify.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
/**
* 退款单的通知 Request DTO
*
* @author 芋道源码
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class PayRefundNotifyReqDTO {
/**
* 商户退款单编号
*/
@NotEmpty(message = "商户退款单编号不能为空")
private String merchantOrderId;
/**
* 支付退款编号
*/
@NotNull(message = "支付退款编号不能为空")
private Long payRefundId;
/**
* 退款状态
*
* (成功,失败) TODO 芋艿:枚举
*/
@NotNull(message = "退款状态不能为空")
private Integer status;
}

View File

@ -0,0 +1,4 @@
/**
* 占位符,无特殊作用
*/
package cn.iocoder.yudao.module.pay.api.notify;

View File

@ -15,8 +15,8 @@ import javax.annotation.Resource;
* @author 芋道源码
*/
@Component
@TenantJob // 多租户
@Slf4j
@TenantJob
public class PayNotifyJob implements JobHandler {
@Resource

View File

@ -2,8 +2,8 @@
* pay 模块,我们放支付业务,提供业务的支付能力。
* 例如说:商户、应用、支付、退款等等
*
* 1. Controller URL以 /member/ 开头,避免和其它 Module 冲突
* 2. DataObject 表名:以 member_ 开头,方便在数据库中区分
* 1. Controller URL以 /pay/ 开头,避免和其它 Module 冲突
* 2. DataObject 表名:以 pay_ 开头,方便在数据库中区分
*
* 注意,由于 Pay 模块和 Trade 模块,容易重名,所以类名都加载 Pay 的前缀~
*/

View File

@ -2,7 +2,13 @@ package cn.iocoder.yudao.module.pay.service.notify;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyLogDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyTaskDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
@ -13,11 +19,8 @@ import cn.iocoder.yudao.module.pay.dal.redis.notify.PayNotifyLockRedisDAO;
import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyStatusEnum;
import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum;
import cn.iocoder.yudao.module.pay.service.notify.dto.PayNotifyTaskCreateReqDTO;
import cn.iocoder.yudao.module.pay.service.notify.vo.PayNotifyOrderReqVO;
import cn.iocoder.yudao.module.pay.service.notify.vo.PayRefundOrderReqVO;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.module.pay.api.notify.dto.PayOrderNotifyReqDTO;
import cn.iocoder.yudao.module.pay.api.notify.dto.PayRefundNotifyReqDTO;
import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
import cn.iocoder.yudao.module.pay.service.refund.PayRefundService;
import lombok.extern.slf4j.Slf4j;
@ -30,7 +33,9 @@ import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@ -164,8 +169,9 @@ public class PayNotifyServiceImpl implements PayNotifyService {
// 校验,当前任务是否已经被通知过
// 虽然已经通过分布式加锁,但是可能同时满足通知的条件,然后都去获得锁。此时,第一个执行完后,第二个还是能拿到锁,然后会再执行一次。
PayNotifyTaskDO dbTask = payNotifyTaskCoreMapper.selectById(task.getId());
if (DateUtils.afterNow(dbTask.getNextNotifyTime())) {
log.info("[executeNotify][dbTask({}) 任务被忽略,原因是未到达下次通知时间,可能是因为并发执行了]", JsonUtils.toJsonString(dbTask));
if (LocalDateTimeUtils.afterNow(dbTask.getNextNotifyTime())) {
log.info("[executeNotifySync][dbTask({}) 任务被忽略,原因是未到达下次通知时间,可能是因为并发执行了]",
JsonUtils.toJsonString(dbTask));
return;
}
@ -185,11 +191,12 @@ public class PayNotifyServiceImpl implements PayNotifyService {
invokeException = e;
}
// 处理
Integer newStatus = this.processNotifyResult(task, invokeResult, invokeException);
// 处理结果
Integer newStatus = processNotifyResult(task, invokeResult, invokeException);
// 记录 PayNotifyLog 日志
String response = invokeException != null ? ExceptionUtil.getRootCauseMessage(invokeException) : JsonUtils.toJsonString(invokeResult);
String response = invokeException != null ? ExceptionUtil.getRootCauseMessage(invokeException) :
JsonUtils.toJsonString(invokeResult);
payNotifyLogCoreMapper.insert(PayNotifyLogDO.builder().taskId(task.getId())
.notifyTimes(task.getNotifyTimes() + 1).status(newStatus).response(response).build());
}
@ -201,22 +208,28 @@ public class PayNotifyServiceImpl implements PayNotifyService {
* @return HTTP 响应
*/
private CommonResult<?> executeNotifyInvoke(PayNotifyTaskDO task) {
// 拼接参数
// 拼接 body 参数
Object request;
if (Objects.equals(task.getType(), PayNotifyTypeEnum.ORDER.getType())) {
request = PayNotifyOrderReqVO.builder().merchantOrderId(task.getMerchantOrderId())
request = PayOrderNotifyReqDTO.builder().merchantOrderId(task.getMerchantOrderId())
.payOrderId(task.getDataId()).build();
} else if (Objects.equals(task.getType(), PayNotifyTypeEnum.REFUND.getType())) {
request = PayRefundOrderReqVO.builder().merchantOrderId(task.getMerchantOrderId())
request = PayRefundNotifyReqDTO.builder().merchantOrderId(task.getMerchantOrderId())
.payRefundId(task.getDataId()).build();
} else {
throw new RuntimeException("未知的通知任务类型:" + JsonUtils.toJsonString(task));
}
// 请求地址
String response = HttpUtil.post(task.getNotifyUrl(), JsonUtils.toJsonString(request),
(int) NOTIFY_TIMEOUT_MILLIS);
// 解析结果
return JsonUtils.parseObject(response, CommonResult.class);
// 拼接 header 参数
Map<String, String> headers = new HashMap<>();
TenantUtils.addTenantHeader(headers);
// 发起请求
try (HttpResponse response = HttpUtil.createPost(task.getNotifyUrl())
.body(JsonUtils.toJsonString(request)).addHeaders(headers)
.timeout((int) NOTIFY_TIMEOUT_MILLIS).execute()) {
// 解析结果
return JsonUtils.parseObject(response.body(), CommonResult.class);
}
}
/**

View File

@ -1,28 +0,0 @@
package cn.iocoder.yudao.module.pay.service.notify.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@ApiModel(value = "支付单的通知 Request VO", description = "业务方接入支付回调时,使用该 VO 对象")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class PayNotifyOrderReqVO {
@ApiModelProperty(value = "商户订单编号", required = true, example = "10")
@NotEmpty(message = "商户订单号不能为空")
private String merchantOrderId;
@ApiModelProperty(value = "支付订单编号", required = true, example = "20")
@NotNull(message = "支付订单编号不能为空")
private Long payOrderId;
}

View File

@ -1,31 +0,0 @@
package cn.iocoder.yudao.module.pay.service.notify.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@ApiModel(value = "退款单的通知 Request VO", description = "业务方接入退款回调时,使用该 VO 对象")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class PayRefundOrderReqVO {
@ApiModelProperty(value = "商户退款单编号", required = true, example = "10")
@NotEmpty(message = "商户退款单编号不能为空")
private String merchantOrderId;
@ApiModelProperty(value = "支付退款编号", required = true, example = "20")
@NotNull(message = "支付退款编号不能为空")
private Long payRefundId;
@ApiModelProperty(value = "退款状态(成功,失败)", required = true, example = "10")
private Integer status;
}

View File

@ -1,6 +0,0 @@
/**
* 这里的 VO 包有点特殊,是提供给接入支付模块的业务,提供回调接口时,可以直接使用 VO
*
* 例如说,支付单的回调,使用 TODO 芋艿:想下怎么优化下
*/
package cn.iocoder.yudao.module.pay.service.notify.vo;

View File

@ -1 +0,0 @@
package cn.iocoder.yudao.module.pay.service;