Commit 73e1158836c53ab7155ac329939b4f100e42dcf3

Authored by zhijiantianya@gmail.com
1 parent 00e1c30f

by gateway:

1. notify 部分单元测试
Showing 14 changed files with 238 additions and 66 deletions
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/notify/PayNotifyTaskDO.java
@@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; @@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO;
7 import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyStatusEnum; 7 import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyStatusEnum;
8 import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum; 8 import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum;
9 import com.baomidou.mybatisplus.annotation.KeySequence; 9 import com.baomidou.mybatisplus.annotation.KeySequence;
  10 +import com.baomidou.mybatisplus.annotation.TableId;
10 import com.baomidou.mybatisplus.annotation.TableName; 11 import com.baomidou.mybatisplus.annotation.TableName;
11 import lombok.Data; 12 import lombok.Data;
12 import lombok.EqualsAndHashCode; 13 import lombok.EqualsAndHashCode;
@@ -40,6 +41,7 @@ public class PayNotifyTaskDO extends TenantBaseDO { @@ -40,6 +41,7 @@ public class PayNotifyTaskDO extends TenantBaseDO {
40 /** 41 /**
41 * 编号,自增 42 * 编号,自增
42 */ 43 */
  44 + @TableId
43 private Long id; 45 private Long id;
44 /** 46 /**
45 * 应用编号 47 * 应用编号
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/redis/RedisKeyConstants.java
@@ -11,7 +11,7 @@ import org.redisson.api.RLock; @@ -11,7 +11,7 @@ import org.redisson.api.RLock;
11 public interface RedisKeyConstants { 11 public interface RedisKeyConstants {
12 12
13 RedisKeyDefine PAY_NOTIFY_LOCK = new RedisKeyDefine("通知任务的分布式锁", 13 RedisKeyDefine PAY_NOTIFY_LOCK = new RedisKeyDefine("通知任务的分布式锁",
14 - "pay_notify:lock:", // 参数来自 DefaultLockKeyBuilder 类 14 + "pay_notify:lock:%d", // 参数来自 DefaultLockKeyBuilder 类
15 RedisKeyDefine.KeyTypeEnum.HASH, RLock.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC); // Redisson 的 Lock 锁,使用 Hash 数据结构 15 RedisKeyDefine.KeyTypeEnum.HASH, RLock.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC); // Redisson 的 Lock 锁,使用 Hash 数据结构
16 16
17 /** 17 /**
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/notify/PayNotifyService.java
@@ -4,9 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -4,9 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskPageReqVO; 4 import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskPageReqVO;
5 import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyLogDO; 5 import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyLogDO;
6 import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyTaskDO; 6 import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyTaskDO;
7 -import cn.iocoder.yudao.module.pay.service.notify.dto.PayNotifyTaskCreateReqDTO;  
8 7
9 -import javax.validation.Valid;  
10 import java.util.List; 8 import java.util.List;
11 9
12 /** 10 /**
@@ -19,9 +17,10 @@ public interface PayNotifyService { @@ -19,9 +17,10 @@ public interface PayNotifyService {
19 /** 17 /**
20 * 创建回调通知任务 18 * 创建回调通知任务
21 * 19 *
22 - * @param reqDTO 任务信息 20 + * @param type 类型
  21 + * @param dataId 数据编号
23 */ 22 */
24 - void createPayNotifyTask(@Valid PayNotifyTaskCreateReqDTO reqDTO); 23 + void createPayNotifyTask(Integer type, Long dataId);
25 24
26 /** 25 /**
27 * 执行回调通知 26 * 执行回调通知
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/notify/PayNotifyServiceImpl.java
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.pay.service.notify; @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.pay.service.notify;
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.exceptions.ExceptionUtil; 4 import cn.hutool.core.exceptions.ExceptionUtil;
5 import cn.hutool.core.util.ObjectUtil; 5 import cn.hutool.core.util.ObjectUtil;
  6 +import cn.hutool.extra.spring.SpringUtil;
6 import cn.hutool.http.HttpResponse; 7 import cn.hutool.http.HttpResponse;
7 import cn.hutool.http.HttpUtil; 8 import cn.hutool.http.HttpUtil;
8 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 9 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
@@ -22,7 +23,6 @@ import cn.iocoder.yudao.module.pay.dal.mysql.notify.PayNotifyTaskMapper; @@ -22,7 +23,6 @@ import cn.iocoder.yudao.module.pay.dal.mysql.notify.PayNotifyTaskMapper;
22 import cn.iocoder.yudao.module.pay.dal.redis.notify.PayNotifyLockRedisDAO; 23 import cn.iocoder.yudao.module.pay.dal.redis.notify.PayNotifyLockRedisDAO;
23 import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyStatusEnum; 24 import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyStatusEnum;
24 import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum; 25 import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum;
25 -import cn.iocoder.yudao.module.pay.service.notify.dto.PayNotifyTaskCreateReqDTO;  
26 import cn.iocoder.yudao.module.pay.service.order.PayOrderService; 26 import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
27 import cn.iocoder.yudao.module.pay.service.refund.PayRefundService; 27 import cn.iocoder.yudao.module.pay.service.refund.PayRefundService;
28 import lombok.extern.slf4j.Slf4j; 28 import lombok.extern.slf4j.Slf4j;
@@ -84,15 +84,10 @@ public class PayNotifyServiceImpl implements PayNotifyService { @@ -84,15 +84,10 @@ public class PayNotifyServiceImpl implements PayNotifyService {
84 @Resource 84 @Resource
85 private PayNotifyLockRedisDAO notifyLockCoreRedisDAO; 85 private PayNotifyLockRedisDAO notifyLockCoreRedisDAO;
86 86
87 - @Resource  
88 - @Lazy // 循环依赖(自己依赖自己),避免报错  
89 - private PayNotifyServiceImpl self;  
90 -  
91 @Override 87 @Override
92 @Transactional(rollbackFor = Exception.class) 88 @Transactional(rollbackFor = Exception.class)
93 - public void createPayNotifyTask(PayNotifyTaskCreateReqDTO reqDTO) {  
94 - PayNotifyTaskDO task = new PayNotifyTaskDO();  
95 - task.setType(reqDTO.getType()).setDataId(reqDTO.getDataId()); 89 + public void createPayNotifyTask(Integer type, Long dataId) {
  90 + PayNotifyTaskDO task = new PayNotifyTaskDO().setType(type).setDataId(dataId);
96 task.setStatus(PayNotifyStatusEnum.WAITING.getStatus()).setNextNotifyTime(LocalDateTime.now()) 91 task.setStatus(PayNotifyStatusEnum.WAITING.getStatus()).setNextNotifyTime(LocalDateTime.now())
97 .setNotifyTimes(0).setMaxNotifyTimes(PayNotifyTaskDO.NOTIFY_FREQUENCY.length + 1); 92 .setNotifyTimes(0).setMaxNotifyTimes(PayNotifyTaskDO.NOTIFY_FREQUENCY.length + 1);
98 // 补充 appId + notifyUrl 字段 93 // 补充 appId + notifyUrl 字段
@@ -178,7 +173,7 @@ public class PayNotifyServiceImpl implements PayNotifyService { @@ -178,7 +173,7 @@ public class PayNotifyServiceImpl implements PayNotifyService {
178 } 173 }
179 174
180 // 执行通知 175 // 执行通知
181 - self.executeNotify0(dbTask); 176 + getSelf().executeNotify0(dbTask);
182 }); 177 });
183 } 178 }
184 179
@@ -285,4 +280,13 @@ public class PayNotifyServiceImpl implements PayNotifyService { @@ -285,4 +280,13 @@ public class PayNotifyServiceImpl implements PayNotifyService {
285 return notifyLogMapper.selectListByTaskId(taskId); 280 return notifyLogMapper.selectListByTaskId(taskId);
286 } 281 }
287 282
  283 + /**
  284 + * 获得自身的代理对象,解决 AOP 生效问题
  285 + *
  286 + * @return 自己
  287 + */
  288 + private PayNotifyServiceImpl getSelf() {
  289 + return SpringUtil.getBean(getClass());
  290 + }
  291 +
288 } 292 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/notify/dto/PayNotifyTaskCreateReqDTO.java deleted
1 -package cn.iocoder.yudao.module.pay.service.notify.dto;  
2 -  
3 -import lombok.AllArgsConstructor;  
4 -import lombok.Builder;  
5 -import lombok.Data;  
6 -import lombok.NoArgsConstructor;  
7 -  
8 -import javax.validation.constraints.NotNull;  
9 -  
10 -/**  
11 - * 支付通知创建 DTO  
12 - *  
13 - * @author 芋道源码  
14 - */  
15 -@Data  
16 -@Builder  
17 -@NoArgsConstructor  
18 -@AllArgsConstructor  
19 -public class PayNotifyTaskCreateReqDTO {  
20 -  
21 - /**  
22 - * 类型  
23 - */  
24 - @NotNull(message = "类型不能为空")  
25 - private Integer type;  
26 - /**  
27 - * 数据编号  
28 - */  
29 - @NotNull(message = "数据编号不能为空")  
30 - private Long dataId;  
31 -  
32 -}  
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/notify/vo/PayNotifyOrderReqVO.java deleted
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/notify/vo/PayRefundOrderReqVO.java deleted
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java
@@ -31,7 +31,6 @@ import cn.iocoder.yudao.module.pay.framework.pay.config.PayProperties; @@ -31,7 +31,6 @@ import cn.iocoder.yudao.module.pay.framework.pay.config.PayProperties;
31 import cn.iocoder.yudao.module.pay.service.app.PayAppService; 31 import cn.iocoder.yudao.module.pay.service.app.PayAppService;
32 import cn.iocoder.yudao.module.pay.service.channel.PayChannelService; 32 import cn.iocoder.yudao.module.pay.service.channel.PayChannelService;
33 import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService; 33 import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService;
34 -import cn.iocoder.yudao.module.pay.service.notify.dto.PayNotifyTaskCreateReqDTO;  
35 import cn.iocoder.yudao.module.pay.util.MoneyUtils; 34 import cn.iocoder.yudao.module.pay.util.MoneyUtils;
36 import com.google.common.annotations.VisibleForTesting; 35 import com.google.common.annotations.VisibleForTesting;
37 import lombok.extern.slf4j.Slf4j; 36 import lombok.extern.slf4j.Slf4j;
@@ -282,8 +281,8 @@ public class PayOrderServiceImpl implements PayOrderService { @@ -282,8 +281,8 @@ public class PayOrderServiceImpl implements PayOrderService {
282 } 281 }
283 282
284 // 3. 插入支付通知记录 283 // 3. 插入支付通知记录
285 - notifyService.createPayNotifyTask(PayNotifyTaskCreateReqDTO.builder()  
286 - .type(PayNotifyTypeEnum.ORDER.getType()).dataId(orderExtension.getOrderId()).build()); 284 + notifyService.createPayNotifyTask(PayNotifyTypeEnum.ORDER.getType(),
  285 + orderExtension.getOrderId());
287 } 286 }
288 287
289 /** 288 /**
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceImpl.java
@@ -26,7 +26,6 @@ import cn.iocoder.yudao.module.pay.framework.pay.config.PayProperties; @@ -26,7 +26,6 @@ import cn.iocoder.yudao.module.pay.framework.pay.config.PayProperties;
26 import cn.iocoder.yudao.module.pay.service.app.PayAppService; 26 import cn.iocoder.yudao.module.pay.service.app.PayAppService;
27 import cn.iocoder.yudao.module.pay.service.channel.PayChannelService; 27 import cn.iocoder.yudao.module.pay.service.channel.PayChannelService;
28 import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService; 28 import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService;
29 -import cn.iocoder.yudao.module.pay.service.notify.dto.PayNotifyTaskCreateReqDTO;  
30 import cn.iocoder.yudao.module.pay.service.order.PayOrderService; 29 import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
31 import lombok.extern.slf4j.Slf4j; 30 import lombok.extern.slf4j.Slf4j;
32 import org.springframework.stereotype.Service; 31 import org.springframework.stereotype.Service;
@@ -242,8 +241,8 @@ public class PayRefundServiceImpl implements PayRefundService { @@ -242,8 +241,8 @@ public class PayRefundServiceImpl implements PayRefundService {
242 orderService.updateOrderRefundPrice(refund.getOrderId(), refund.getRefundPrice()); 241 orderService.updateOrderRefundPrice(refund.getOrderId(), refund.getRefundPrice());
243 242
244 // 3. 插入退款通知记录 243 // 3. 插入退款通知记录
245 - notifyService.createPayNotifyTask(PayNotifyTaskCreateReqDTO.builder()  
246 - .type(PayNotifyTypeEnum.REFUND.getType()).dataId(refund.getId()).build()); 244 + notifyService.createPayNotifyTask(PayNotifyTypeEnum.REFUND.getType(),
  245 + refund.getId());
247 } 246 }
248 247
249 private void notifyRefundFailure(PayChannelDO channel, PayRefundRespDTO notify) { 248 private void notifyRefundFailure(PayChannelDO channel, PayRefundRespDTO notify) {
@@ -273,8 +272,8 @@ public class PayRefundServiceImpl implements PayRefundService { @@ -273,8 +272,8 @@ public class PayRefundServiceImpl implements PayRefundService {
273 log.info("[notifyRefundFailure][退款订单({}) 更新为退款失败]", refund.getId()); 272 log.info("[notifyRefundFailure][退款订单({}) 更新为退款失败]", refund.getId());
274 273
275 // 2. 插入退款通知记录 274 // 2. 插入退款通知记录
276 - notifyService.createPayNotifyTask(PayNotifyTaskCreateReqDTO.builder()  
277 - .type(PayNotifyTypeEnum.REFUND.getType()).dataId(refund.getId()).build()); 275 + notifyService.createPayNotifyTask(PayNotifyTypeEnum.REFUND.getType(),
  276 + refund.getId());
278 } 277 }
279 278
280 @Override 279 @Override
yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/notify/PayNotifyServiceTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pay.service.notify;
  2 +
  3 +import cn.hutool.extra.spring.SpringUtil;
  4 +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
  5 +import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyTaskDO;
  6 +import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
  7 +import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO;
  8 +import cn.iocoder.yudao.module.pay.dal.mysql.notify.PayNotifyLogMapper;
  9 +import cn.iocoder.yudao.module.pay.dal.mysql.notify.PayNotifyTaskMapper;
  10 +import cn.iocoder.yudao.module.pay.dal.redis.notify.PayNotifyLockRedisDAO;
  11 +import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyStatusEnum;
  12 +import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum;
  13 +import cn.iocoder.yudao.module.pay.framework.job.config.PayJobConfiguration;
  14 +import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
  15 +import cn.iocoder.yudao.module.pay.service.refund.PayRefundService;
  16 +import cn.iocoder.yudao.module.pay.service.refund.PayRefundServiceImpl;
  17 +import org.junit.jupiter.api.Test;
  18 +import org.mockito.MockedStatic;
  19 +import org.redisson.api.RLock;
  20 +import org.redisson.api.RedissonClient;
  21 +import org.springframework.boot.test.mock.mockito.MockBean;
  22 +import org.springframework.context.annotation.Import;
  23 +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
  24 +
  25 +import javax.annotation.Resource;
  26 +
  27 +import java.time.Duration;
  28 +
  29 +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.addTime;
  30 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
  31 +import static org.assertj.core.api.Assertions.assertThat;
  32 +import static org.junit.jupiter.api.Assertions.*;
  33 +import static org.mockito.ArgumentMatchers.anyString;
  34 +import static org.mockito.ArgumentMatchers.eq;
  35 +import static org.mockito.Mockito.*;
  36 +
  37 +/**
  38 + * {@link PayRefundServiceImpl} 的单元测试类
  39 + *
  40 + * @author 芋艿
  41 + */
  42 +@Import({PayJobConfiguration.class, PayNotifyServiceImpl.class, PayNotifyLockRedisDAO.class})
  43 +public class PayNotifyServiceTest extends BaseDbUnitTest {
  44 +
  45 + @Resource
  46 + private PayNotifyServiceImpl notifyService;
  47 +
  48 + @MockBean
  49 + private PayOrderService orderService;
  50 + @MockBean
  51 + private PayRefundService refundService;
  52 +
  53 + @Resource
  54 + private PayNotifyTaskMapper notifyTaskMapper;
  55 + @Resource
  56 + private PayNotifyLogMapper notifyLogMapper;
  57 +
  58 + @MockBean
  59 + private RedissonClient redissonClient;
  60 +
  61 + @Test
  62 + public void testCreatePayNotifyTask_order() {
  63 + PayNotifyServiceImpl payNotifyService = mock(PayNotifyServiceImpl.class);
  64 + try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
  65 + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PayNotifyServiceImpl.class)))
  66 + .thenReturn(payNotifyService);
  67 +
  68 + // 准备参数
  69 + Integer type = PayNotifyTypeEnum.ORDER.getType();
  70 + Long dataId = 1L;
  71 + // mock 方法(order)
  72 + PayOrderDO order = randomPojo(PayOrderDO.class);
  73 + when(orderService.getOrder(eq(1L))).thenReturn(order);
  74 + // mock 方法(lock)
  75 + mockLock(null); // null 的原因,是咱没办法拿到 taskId 新增
  76 +
  77 + // 调用
  78 + notifyService.createPayNotifyTask(type, dataId);
  79 + // 断言,task
  80 + PayNotifyTaskDO dbTask = notifyTaskMapper.selectOne(null);
  81 + assertNotNull(dbTask.getNextNotifyTime());
  82 + assertThat(dbTask)
  83 + .extracting("type", "dataId", "status", "notifyTimes", "maxNotifyTimes",
  84 + "appId", "merchantOrderId", "notifyUrl")
  85 + .containsExactly(type, dataId, PayNotifyStatusEnum.WAITING.getStatus(), 0, 9,
  86 + order.getAppId(), order.getMerchantOrderId(), order.getNotifyUrl());
  87 + // 断言,调用
  88 + verify(payNotifyService).executeNotify0(eq(dbTask));
  89 + }
  90 + }
  91 +
  92 + @Test
  93 + public void testCreatePayNotifyTask_refund() {
  94 + PayNotifyServiceImpl payNotifyService = mock(PayNotifyServiceImpl.class);
  95 + try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
  96 + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PayNotifyServiceImpl.class)))
  97 + .thenReturn(payNotifyService);
  98 +
  99 + // 准备参数
  100 + Integer type = PayNotifyTypeEnum.REFUND.getType();
  101 + Long dataId = 1L;
  102 + // mock 方法(refund)
  103 + PayRefundDO refund = randomPojo(PayRefundDO.class);
  104 + when(refundService.getRefund(eq(1L))).thenReturn(refund);
  105 + // mock 方法(lock)
  106 + mockLock(null); // null 的原因,是咱没办法拿到 taskId 新增
  107 +
  108 + // 调用
  109 + notifyService.createPayNotifyTask(type, dataId);
  110 + // 断言,task
  111 + PayNotifyTaskDO dbTask = notifyTaskMapper.selectOne(null);
  112 + assertNotNull(dbTask.getNextNotifyTime());
  113 + assertThat(dbTask)
  114 + .extracting("type", "dataId", "status", "notifyTimes", "maxNotifyTimes",
  115 + "appId", "merchantOrderId", "notifyUrl")
  116 + .containsExactly(type, dataId, PayNotifyStatusEnum.WAITING.getStatus(), 0, 9,
  117 + refund.getAppId(), refund.getMerchantOrderId(), refund.getNotifyUrl());
  118 + // 断言,调用
  119 + verify(payNotifyService).executeNotify0(eq(dbTask));
  120 + }
  121 + }
  122 +
  123 + @Test
  124 + public void testExecuteNotify() throws InterruptedException {
  125 + // mock 数据(notify)
  126 + PayNotifyTaskDO dbTask01 = randomPojo(PayNotifyTaskDO.class,
  127 + o -> o.setStatus(PayNotifyStatusEnum.WAITING.getStatus())
  128 + .setNextNotifyTime(addTime(Duration.ofMinutes(-1))));
  129 + notifyTaskMapper.insert(dbTask01);
  130 + PayNotifyTaskDO dbTask02 = randomPojo(PayNotifyTaskDO.class,
  131 + o -> o.setStatus(PayNotifyStatusEnum.REQUEST_SUCCESS.getStatus())
  132 + .setNextNotifyTime(addTime(Duration.ofMinutes(-1))));
  133 + notifyTaskMapper.insert(dbTask02);
  134 + PayNotifyTaskDO dbTask03 = randomPojo(PayNotifyTaskDO.class,
  135 + o -> o.setStatus(PayNotifyStatusEnum.REQUEST_FAILURE.getStatus())
  136 + .setNextNotifyTime(addTime(Duration.ofMinutes(-1))));
  137 + notifyTaskMapper.insert(dbTask03);
  138 + PayNotifyTaskDO dbTask04 = randomPojo(PayNotifyTaskDO.class, // 不满足状态
  139 + o -> o.setStatus(PayNotifyStatusEnum.FAILURE.getStatus())
  140 + .setNextNotifyTime(addTime(Duration.ofMinutes(-1))));
  141 + notifyTaskMapper.insert(dbTask04);
  142 + PayNotifyTaskDO dbTask05 = randomPojo(PayNotifyTaskDO.class, // 不满足状态
  143 + o -> o.setStatus(PayNotifyStatusEnum.SUCCESS.getStatus())
  144 + .setNextNotifyTime(addTime(Duration.ofMinutes(-1))));
  145 + notifyTaskMapper.insert(dbTask05);
  146 + PayNotifyTaskDO dbTask06 = randomPojo(PayNotifyTaskDO.class, // 不满足时间
  147 + o -> o.setStatus(PayNotifyStatusEnum.SUCCESS.getStatus())
  148 + .setNextNotifyTime(addTime(Duration.ofMinutes(1))));
  149 + notifyTaskMapper.insert(dbTask06);
  150 + // mock 方法(lock)
  151 + mockLock(dbTask01.getId());
  152 + mockLock(dbTask02.getId());
  153 + mockLock(dbTask03.getId());
  154 +
  155 + // 调用
  156 + int count = notifyService.executeNotify();
  157 + // 断言,数量
  158 + assertEquals(count, 3);
  159 + }
  160 +
  161 + @Test // 由于 HttpUtil 不好 mock,所以只测试异常的情况
  162 + public void testExecuteNotify0_exception() {
  163 + // mock 数据(task)
  164 + PayNotifyTaskDO task = randomPojo(PayNotifyTaskDO.class, o -> o.setType(-1)
  165 + .setNotifyTimes(0).setMaxNotifyTimes(9));
  166 + notifyTaskMapper.insert(task);
  167 +
  168 + // 调用
  169 + notifyService.executeNotify0(task);
  170 +
  171 + }
  172 +
  173 + private void mockLock(Long id) {
  174 + RLock lock = mock(RLock.class);
  175 + if (id == null) {
  176 + when(redissonClient.getLock(anyString()))
  177 + .thenReturn(lock);
  178 + } else {
  179 + when(redissonClient.getLock(eq("pay_notify:lock:" + id)))
  180 + .thenReturn(lock);
  181 + }
  182 + }
  183 +
  184 +}
yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceTest.java
@@ -27,7 +27,6 @@ import cn.iocoder.yudao.module.pay.framework.pay.config.PayProperties; @@ -27,7 +27,6 @@ import cn.iocoder.yudao.module.pay.framework.pay.config.PayProperties;
27 import cn.iocoder.yudao.module.pay.service.app.PayAppService; 27 import cn.iocoder.yudao.module.pay.service.app.PayAppService;
28 import cn.iocoder.yudao.module.pay.service.channel.PayChannelService; 28 import cn.iocoder.yudao.module.pay.service.channel.PayChannelService;
29 import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService; 29 import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService;
30 -import cn.iocoder.yudao.module.pay.service.notify.dto.PayNotifyTaskCreateReqDTO;  
31 import org.junit.jupiter.api.BeforeEach; 30 import org.junit.jupiter.api.BeforeEach;
32 import org.junit.jupiter.api.Test; 31 import org.junit.jupiter.api.Test;
33 import org.mockito.MockedStatic; 32 import org.mockito.MockedStatic;
@@ -622,7 +621,7 @@ public class PayOrderServiceTest extends BaseDbAndRedisUnitTest { @@ -622,7 +621,7 @@ public class PayOrderServiceTest extends BaseDbAndRedisUnitTest {
622 // 断言 PayOrderDO :数据未更新,因为它是 SUCCESS 621 // 断言 PayOrderDO :数据未更新,因为它是 SUCCESS
623 assertPojoEquals(order, orderMapper.selectOne(null)); 622 assertPojoEquals(order, orderMapper.selectOne(null));
624 // 断言,调用 623 // 断言,调用
625 - verify(notifyService, never()).createPayNotifyTask(any(PayNotifyTaskCreateReqDTO.class)); 624 + verify(notifyService, never()).createPayNotifyTask(anyInt(), anyLong());
626 } 625 }
627 626
628 @Test 627 @Test
@@ -661,11 +660,8 @@ public class PayOrderServiceTest extends BaseDbAndRedisUnitTest { @@ -661,11 +660,8 @@ public class PayOrderServiceTest extends BaseDbAndRedisUnitTest {
661 assertPojoEquals(order, orderMapper.selectOne(null), 660 assertPojoEquals(order, orderMapper.selectOne(null),
662 "updateTime", "updater"); 661 "updateTime", "updater");
663 // 断言,调用 662 // 断言,调用
664 - verify(notifyService).createPayNotifyTask(argThat(reqDTO -> {  
665 - assertEquals(reqDTO.getType(), PayNotifyTypeEnum.ORDER.getType());  
666 - assertEquals(reqDTO.getDataId(), orderExtension.getOrderId());  
667 - return true;  
668 - })); 663 + verify(notifyService).createPayNotifyTask(eq(PayNotifyTypeEnum.ORDER.getType()),
  664 + eq(orderExtension.getOrderId()));
669 } 665 }
670 666
671 @Test 667 @Test
yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceTest.java
@@ -25,7 +25,6 @@ import cn.iocoder.yudao.module.pay.framework.pay.config.PayProperties; @@ -25,7 +25,6 @@ import cn.iocoder.yudao.module.pay.framework.pay.config.PayProperties;
25 import cn.iocoder.yudao.module.pay.service.app.PayAppService; 25 import cn.iocoder.yudao.module.pay.service.app.PayAppService;
26 import cn.iocoder.yudao.module.pay.service.channel.PayChannelService; 26 import cn.iocoder.yudao.module.pay.service.channel.PayChannelService;
27 import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService; 27 import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService;
28 -import cn.iocoder.yudao.module.pay.service.notify.dto.PayNotifyTaskCreateReqDTO;  
29 import cn.iocoder.yudao.module.pay.service.order.PayOrderService; 28 import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
30 import org.junit.jupiter.api.BeforeEach; 29 import org.junit.jupiter.api.BeforeEach;
31 import org.junit.jupiter.api.Test; 30 import org.junit.jupiter.api.Test;
@@ -552,8 +551,8 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest { @@ -552,8 +551,8 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest {
552 "updateTime", "updater"); 551 "updateTime", "updater");
553 // 断言,调用 552 // 断言,调用
554 verify(orderService).updateOrderRefundPrice(eq(100L), eq(23)); 553 verify(orderService).updateOrderRefundPrice(eq(100L), eq(23));
555 - verify(notifyService).createPayNotifyTask(eq(PayNotifyTaskCreateReqDTO.builder()  
556 - .type(PayNotifyTypeEnum.REFUND.getType()).dataId(refund.getId()).build())); 554 + verify(notifyService).createPayNotifyTask(eq(PayNotifyTypeEnum.REFUND.getType()),
  555 + eq(refund.getId()));
557 } 556 }
558 557
559 @Test 558 @Test
@@ -624,8 +623,8 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest { @@ -624,8 +623,8 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest {
624 assertPojoEquals(refund, refundMapper.selectById(refund.getId()), 623 assertPojoEquals(refund, refundMapper.selectById(refund.getId()),
625 "updateTime", "updater"); 624 "updateTime", "updater");
626 // 断言,调用 625 // 断言,调用
627 - verify(notifyService).createPayNotifyTask(eq(PayNotifyTaskCreateReqDTO.builder()  
628 - .type(PayNotifyTypeEnum.REFUND.getType()).dataId(refund.getId()).build())); 626 + verify(notifyService).createPayNotifyTask(eq(PayNotifyTypeEnum.REFUND.getType()),
  627 + eq(refund.getId()));
629 } 628 }
630 629
631 @Test 630 @Test
yudao-module-pay/yudao-module-pay-biz/src/test/resources/sql/clean.sql
@@ -3,3 +3,4 @@ DELETE FROM pay_channel; @@ -3,3 +3,4 @@ DELETE FROM pay_channel;
3 DELETE FROM pay_order; 3 DELETE FROM pay_order;
4 DELETE FROM pay_order_extension; 4 DELETE FROM pay_order_extension;
5 DELETE FROM pay_refund; 5 DELETE FROM pay_refund;
  6 +DELETE FROM pay_notify_task;
6 \ No newline at end of file 7 \ No newline at end of file
yudao-module-pay/yudao-module-pay-biz/src/test/resources/sql/create_tables.sql
@@ -109,3 +109,24 @@ CREATE TABLE IF NOT EXISTS `pay_refund` ( @@ -109,3 +109,24 @@ CREATE TABLE IF NOT EXISTS `pay_refund` (
109 `deleted` bit(1) NOT NULL DEFAULT FALSE, 109 `deleted` bit(1) NOT NULL DEFAULT FALSE,
110 PRIMARY KEY ("id") 110 PRIMARY KEY ("id")
111 ) COMMENT = '退款订单'; 111 ) COMMENT = '退款订单';
  112 +
  113 +CREATE TABLE IF NOT EXISTS `pay_notify_task` (
  114 + "id" number NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  115 + `app_id` bigint(20) NOT NULL,
  116 + `type` tinyint(4) NOT NULL,
  117 + `data_id` bigint(20) NOT NULL,
  118 + `merchant_order_id` varchar(64) NOT NULL,
  119 + `status` tinyint(4) NOT NULL,
  120 + `next_notify_time` datetime(0) NULL DEFAULT NULL,
  121 + `last_execute_time` datetime(0) NULL DEFAULT NULL,
  122 + `notify_times` int NOT NULL,
  123 + `max_notify_times` int NOT NULL,
  124 + `notify_url` varchar(1024) NOT NULL,
  125 + `creator` varchar(64) NULL DEFAULT '',
  126 + `create_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
  127 + `updater` varchar(64) NULL DEFAULT '',
  128 + `update_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  129 + `deleted` bit(1) NOT NULL DEFAULT FALSE,
  130 + `tenant_id` bigint(20) NOT NULL DEFAULT 0,
  131 + PRIMARY KEY ("id")
  132 +) COMMENT = '支付通知';