Commit b54f7e9256b7efb4bfb0d5c6f950ad59be5ed6d1
1 parent
721adfbf
by gateway:
1. 完善部分 order 单元测试 2. 增加支付订单的 no 生成逻辑
Showing
12 changed files
with
397 additions
and
92 deletions
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/config/YudaoPayAutoConfiguration.java
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/redis/RedisKeyConstants.java
| ... | ... | @@ -14,4 +14,12 @@ public interface RedisKeyConstants { |
| 14 | 14 | "pay_notify:lock:", // 参数来自 DefaultLockKeyBuilder 类 |
| 15 | 15 | RedisKeyDefine.KeyTypeEnum.HASH, RLock.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC); // Redisson 的 Lock 锁,使用 Hash 数据结构 |
| 16 | 16 | |
| 17 | + /** | |
| 18 | + * 支付序号的缓存 | |
| 19 | + * | |
| 20 | + * KEY 格式:pay_no:{prefix} | |
| 21 | + * VALUE 数据格式:编号自增 | |
| 22 | + */ | |
| 23 | + String PAY_NO = "pay_no"; | |
| 24 | + | |
| 17 | 25 | } | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/redis/no/PayNoRedisDAO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.pay.dal.redis.no; | |
| 2 | + | |
| 3 | +import cn.hutool.core.date.DatePattern;import cn.hutool.core.date.DateUtil;import org.springframework.data.redis.core.StringRedisTemplate; | |
| 4 | +import org.springframework.stereotype.Repository; | |
| 5 | + | |
| 6 | +import javax.annotation.Resource;import java.time.LocalDateTime; | |
| 7 | + | |
| 8 | +/** | |
| 9 | + * 支付序号的 Redis DAO | |
| 10 | + * | |
| 11 | + * @author 芋道源码 | |
| 12 | + */ | |
| 13 | +@Repository | |
| 14 | +public class PayNoRedisDAO { | |
| 15 | + | |
| 16 | + @Resource | |
| 17 | + private StringRedisTemplate stringRedisTemplate; | |
| 18 | + | |
| 19 | + /** | |
| 20 | + * 生成序号 | |
| 21 | + * | |
| 22 | + * @param prefix 前缀 | |
| 23 | + * @return 序号 | |
| 24 | + */ | |
| 25 | + public String generate(String prefix) { | |
| 26 | + String noPrefix = prefix + DateUtil.format(LocalDateTime.now(), DatePattern.PURE_DATETIME_PATTERN); | |
| 27 | + Long no = stringRedisTemplate.opsForValue().increment(noPrefix); | |
| 28 | + return noPrefix + no; | |
| 29 | + } | |
| 30 | + | |
| 31 | +} | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/config/PayConfiguration.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.pay.framework.pay.config; | |
| 2 | + | |
| 3 | +import org.springframework.boot.context.properties.EnableConfigurationProperties; | |
| 4 | +import org.springframework.context.annotation.Configuration; | |
| 5 | + | |
| 6 | +@Configuration(proxyBeanMethods = false) | |
| 7 | +@EnableConfigurationProperties(PayProperties.class) | |
| 8 | +public class PayConfiguration { | |
| 9 | +} | ... | ... |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/config/PayProperties.java renamed to yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/config/PayProperties.java
| 1 | -package cn.iocoder.yudao.framework.pay.config; | |
| 1 | +package cn.iocoder.yudao.module.pay.framework.pay.config; | |
| 2 | 2 | |
| 3 | 3 | import lombok.Data; |
| 4 | 4 | import org.hibernate.validator.constraints.URL; |
| ... | ... | @@ -12,6 +12,9 @@ import javax.validation.constraints.NotEmpty; |
| 12 | 12 | @Data |
| 13 | 13 | public class PayProperties { |
| 14 | 14 | |
| 15 | + private static final String ORDER_NO_PREFIX = "P"; | |
| 16 | + private static final String REFUND_NO_PREFIX = "R"; | |
| 17 | + | |
| 15 | 18 | /** |
| 16 | 19 | * 支付回调地址 |
| 17 | 20 | * |
| ... | ... | @@ -34,4 +37,16 @@ public class PayProperties { |
| 34 | 37 | @URL(message = "支付回调地址的格式必须是 URL") |
| 35 | 38 | private String refundNotifyUrl; |
| 36 | 39 | |
| 40 | + /** | |
| 41 | + * 支付订单 no 的前缀 | |
| 42 | + */ | |
| 43 | + @NotEmpty(message = "支付订单 no 的前缀不能为空") | |
| 44 | + private String orderNoPrefix = ORDER_NO_PREFIX; | |
| 45 | + | |
| 46 | + /** | |
| 47 | + * 退款订单 no 的前缀 | |
| 48 | + */ | |
| 49 | + @NotEmpty(message = "退款订单 no 的前缀不能为空") | |
| 50 | + private String refundNoPrefix = REFUND_NO_PREFIX; | |
| 51 | + | |
| 37 | 52 | } | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/core/package-info.java
0 → 100644
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java
| 1 | 1 | package cn.iocoder.yudao.module.pay.service.order; |
| 2 | 2 | |
| 3 | -import cn.hutool.core.date.DateUtil; | |
| 4 | 3 | import cn.hutool.core.lang.Pair; |
| 5 | 4 | import cn.hutool.core.util.ObjectUtil; |
| 6 | -import cn.hutool.core.util.RandomUtil; | |
| 7 | 5 | import cn.hutool.core.util.StrUtil; |
| 8 | 6 | import cn.hutool.extra.spring.SpringUtil; |
| 9 | 7 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 10 | 8 | import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; |
| 11 | -import cn.iocoder.yudao.framework.pay.config.PayProperties; | |
| 12 | 9 | import cn.iocoder.yudao.framework.pay.core.client.PayClient; |
| 13 | 10 | import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; |
| 14 | 11 | import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; |
| ... | ... | @@ -27,8 +24,10 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; |
| 27 | 24 | import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO; |
| 28 | 25 | import cn.iocoder.yudao.module.pay.dal.mysql.order.PayOrderExtensionMapper; |
| 29 | 26 | import cn.iocoder.yudao.module.pay.dal.mysql.order.PayOrderMapper; |
| 27 | +import cn.iocoder.yudao.module.pay.dal.redis.no.PayNoRedisDAO; | |
| 30 | 28 | import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum; |
| 31 | 29 | import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; |
| 30 | +import cn.iocoder.yudao.module.pay.framework.pay.config.PayProperties; | |
| 32 | 31 | import cn.iocoder.yudao.module.pay.service.app.PayAppService; |
| 33 | 32 | import cn.iocoder.yudao.module.pay.service.channel.PayChannelService; |
| 34 | 33 | import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService; |
| ... | ... | @@ -40,7 +39,6 @@ import org.springframework.transaction.annotation.Transactional; |
| 40 | 39 | import org.springframework.validation.annotation.Validated; |
| 41 | 40 | |
| 42 | 41 | import javax.annotation.Resource; |
| 43 | -import java.time.LocalDateTime; | |
| 44 | 42 | import java.util.List; |
| 45 | 43 | import java.util.Objects; |
| 46 | 44 | |
| ... | ... | @@ -68,6 +66,8 @@ public class PayOrderServiceImpl implements PayOrderService { |
| 68 | 66 | private PayOrderMapper orderMapper; |
| 69 | 67 | @Resource |
| 70 | 68 | private PayOrderExtensionMapper orderExtensionMapper; |
| 69 | + @Resource | |
| 70 | + private PayNoRedisDAO noRedisDAO; | |
| 71 | 71 | |
| 72 | 72 | @Resource |
| 73 | 73 | private PayAppService appService; |
| ... | ... | @@ -136,8 +136,9 @@ public class PayOrderServiceImpl implements PayOrderService { |
| 136 | 136 | PayClient client = payClientFactory.getPayClient(channel.getId()); |
| 137 | 137 | |
| 138 | 138 | // 2. 插入 PayOrderExtensionDO |
| 139 | + String no = noRedisDAO.generate(payProperties.getOrderNoPrefix()); | |
| 139 | 140 | PayOrderExtensionDO orderExtension = PayOrderConvert.INSTANCE.convert(reqVO, userIp) |
| 140 | - .setOrderId(order.getId()).setNo(generateOrderExtensionNo()) | |
| 141 | + .setOrderId(order.getId()).setNo(no) | |
| 141 | 142 | .setChannelId(channel.getId()).setChannelCode(channel.getCode()) |
| 142 | 143 | .setStatus(PayOrderStatusEnum.WAITING.getStatus()); |
| 143 | 144 | orderExtensionMapper.insert(orderExtension); |
| ... | ... | @@ -155,7 +156,7 @@ public class PayOrderServiceImpl implements PayOrderService { |
| 155 | 156 | |
| 156 | 157 | // 4. 如果调用直接支付成功,则直接更新支付单状态为成功。例如说:付款码支付,免密支付时,就直接验证支付成功 |
| 157 | 158 | if (unifiedOrderResp != null) { |
| 158 | - getSelf().notifyPayOrder(channel, unifiedOrderResp); | |
| 159 | + getSelf().notifyOrder(channel, unifiedOrderResp); | |
| 159 | 160 | // 如有渠道错误码,则抛出业务异常,提示用户 |
| 160 | 161 | if (StrUtil.isNotEmpty(unifiedOrderResp.getChannelErrorCode())) { |
| 161 | 162 | throw exception(ORDER_SUBMIT_CHANNEL_ERROR, unifiedOrderResp.getChannelErrorCode(), |
| ... | ... | @@ -204,31 +205,12 @@ public class PayOrderServiceImpl implements PayOrderService { |
| 204 | 205 | return payProperties.getOrderNotifyUrl() + "/" + channel.getId(); |
| 205 | 206 | } |
| 206 | 207 | |
| 207 | - private String generateOrderExtensionNo() { | |
| 208 | -// wx | |
| 209 | -// 2014 | |
| 210 | -// 10 | |
| 211 | -// 27 | |
| 212 | -// 20 | |
| 213 | -// 09 | |
| 214 | -// 39 | |
| 215 | -// 5522657 | |
| 216 | -// a690389285100 | |
| 217 | - // 目前的算法 | |
| 218 | - // 时间序列,年月日时分秒 14 位 | |
| 219 | - // 纯随机,6 位 TODO 芋艿:此处估计是会有问题的,后续在调整 | |
| 220 | - return DateUtil.format(LocalDateTime.now(), "yyyyMMddHHmmss") + // 时间序列 | |
| 221 | - RandomUtil.randomInt(100000, 999999) // 随机。为什么是这个范围,因为偷懒 | |
| 222 | - ; | |
| 223 | - } | |
| 224 | - | |
| 225 | 208 | @Override |
| 226 | - @Transactional(rollbackFor = Exception.class) | |
| 227 | 209 | public void notifyOrder(Long channelId, PayOrderRespDTO notify) { |
| 228 | 210 | // 校验支付渠道是否有效 |
| 229 | 211 | PayChannelDO channel = channelService.validPayChannel(channelId); |
| 230 | 212 | // 更新支付订单为已支付 |
| 231 | - TenantUtils.execute(channel.getTenantId(), () -> getSelf().notifyPayOrder(channel, notify)); | |
| 213 | + TenantUtils.execute(channel.getTenantId(), () -> getSelf().notifyOrder(channel, notify)); | |
| 232 | 214 | } |
| 233 | 215 | |
| 234 | 216 | /** |
| ... | ... | @@ -238,7 +220,7 @@ public class PayOrderServiceImpl implements PayOrderService { |
| 238 | 220 | * @param notify 通知 |
| 239 | 221 | */ |
| 240 | 222 | @Transactional(rollbackFor = Exception.class) // 注意,如果是方法内调用该方法,需要通过 getSelf().notifyPayOrder(channel, notify) 调用,否则事务不生效 |
| 241 | - public void notifyPayOrder(PayChannelDO channel, PayOrderRespDTO notify) { | |
| 223 | + public void notifyOrder(PayChannelDO channel, PayOrderRespDTO notify) { | |
| 242 | 224 | // 情况一:支付成功的回调 |
| 243 | 225 | if (PayOrderStatusRespEnum.isSuccess(notify.getStatus())) { |
| 244 | 226 | notifyOrderSuccess(channel, notify); |
| ... | ... | @@ -254,16 +236,16 @@ public class PayOrderServiceImpl implements PayOrderService { |
| 254 | 236 | |
| 255 | 237 | private void notifyOrderSuccess(PayChannelDO channel, PayOrderRespDTO notify) { |
| 256 | 238 | // 1. 更新 PayOrderExtensionDO 支付成功 |
| 257 | - PayOrderExtensionDO orderExtension = updateOrderExtensionSuccess(notify); | |
| 239 | + PayOrderExtensionDO orderExtension = updateOrderSuccess(notify); | |
| 258 | 240 | // 2. 更新 PayOrderDO 支付成功 |
| 259 | - Pair<Boolean, PayOrderDO> order = updateOrderExtensionSuccess(channel, orderExtension, notify); | |
| 260 | - if (order.getKey()) { // 如果之前已经成功回调,则直接返回,不用重复记录支付通知记录;例如说:支付平台重复回调 | |
| 241 | + Boolean paid = updateOrderSuccess(channel, orderExtension, notify); | |
| 242 | + if (paid) { // 如果之前已经成功回调,则直接返回,不用重复记录支付通知记录;例如说:支付平台重复回调 | |
| 261 | 243 | return; |
| 262 | 244 | } |
| 263 | 245 | |
| 264 | 246 | // 3. 插入支付通知记录 |
| 265 | 247 | notifyService.createPayNotifyTask(PayNotifyTaskCreateReqDTO.builder() |
| 266 | - .type(PayNotifyTypeEnum.ORDER.getType()).dataId(order.getValue().getId()).build()); | |
| 248 | + .type(PayNotifyTypeEnum.ORDER.getType()).dataId(orderExtension.getOrderId()).build()); | |
| 267 | 249 | } |
| 268 | 250 | |
| 269 | 251 | /** |
| ... | ... | @@ -272,7 +254,7 @@ public class PayOrderServiceImpl implements PayOrderService { |
| 272 | 254 | * @param notify 通知 |
| 273 | 255 | * @return PayOrderExtensionDO 对象 |
| 274 | 256 | */ |
| 275 | - private PayOrderExtensionDO updateOrderExtensionSuccess(PayOrderRespDTO notify) { | |
| 257 | + private PayOrderExtensionDO updateOrderSuccess(PayOrderRespDTO notify) { | |
| 276 | 258 | // 1. 查询 PayOrderExtensionDO |
| 277 | 259 | PayOrderExtensionDO orderExtension = orderExtensionMapper.selectByNo(notify.getOutTradeNo()); |
| 278 | 260 | if (orderExtension == null) { |
| ... | ... | @@ -302,11 +284,10 @@ public class PayOrderServiceImpl implements PayOrderService { |
| 302 | 284 | * @param channel 支付渠道 |
| 303 | 285 | * @param orderExtension 支付拓展单 |
| 304 | 286 | * @param notify 通知回调 |
| 305 | - * @return key:是否之前已经成功回调 | |
| 306 | - * value:PayOrderDO 对象 | |
| 287 | + * @return 是否之前已经成功回调 | |
| 307 | 288 | */ |
| 308 | - private Pair<Boolean, PayOrderDO> updateOrderExtensionSuccess(PayChannelDO channel, PayOrderExtensionDO orderExtension, | |
| 309 | - PayOrderRespDTO notify) { | |
| 289 | + private Boolean updateOrderSuccess(PayChannelDO channel, PayOrderExtensionDO orderExtension, | |
| 290 | + PayOrderRespDTO notify) { | |
| 310 | 291 | // 1. 判断 PayOrderDO 是否处于待支付 |
| 311 | 292 | PayOrderDO order = orderMapper.selectById(orderExtension.getOrderId()); |
| 312 | 293 | if (order == null) { |
| ... | ... | @@ -315,7 +296,7 @@ public class PayOrderServiceImpl implements PayOrderService { |
| 315 | 296 | if (PayOrderStatusEnum.isSuccess(order.getStatus()) // 如果已经是成功,直接返回,不用重复更新 |
| 316 | 297 | && Objects.equals(order.getExtensionId(), orderExtension.getId())) { |
| 317 | 298 | log.info("[updateOrderExtensionSuccess][支付订单({}) 已经是已支付,无需更新]", order.getId()); |
| 318 | - return Pair.of(true, order); | |
| 299 | + return true; | |
| 319 | 300 | } |
| 320 | 301 | if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状态,必须是待支付 |
| 321 | 302 | throw exception(ORDER_STATUS_IS_NOT_WAITING); |
| ... | ... | @@ -333,7 +314,7 @@ public class PayOrderServiceImpl implements PayOrderService { |
| 333 | 314 | throw exception(ORDER_STATUS_IS_NOT_WAITING); |
| 334 | 315 | } |
| 335 | 316 | log.info("[updateOrderExtensionSuccess][支付订单({}) 更新为已支付]", order.getId()); |
| 336 | - return Pair.of(false, order); | |
| 317 | + return false; | |
| 337 | 318 | } |
| 338 | 319 | |
| 339 | 320 | private void notifyOrderClosed(PayChannelDO channel, PayOrderRespDTO notify) { | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceImpl.java
| ... | ... | @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.pay.service.refund; |
| 3 | 3 | import cn.hutool.core.date.DateUtil; |
| 4 | 4 | import cn.hutool.core.util.RandomUtil; |
| 5 | 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | -import cn.iocoder.yudao.framework.pay.config.PayProperties; | |
| 7 | 6 | import cn.iocoder.yudao.framework.pay.core.client.PayClient; |
| 8 | 7 | import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; |
| 9 | 8 | import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; |
| ... | ... | @@ -24,6 +23,7 @@ import cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants; |
| 24 | 23 | import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum; |
| 25 | 24 | import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; |
| 26 | 25 | import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum; |
| 26 | +import cn.iocoder.yudao.module.pay.framework.pay.config.PayProperties; | |
| 27 | 27 | import cn.iocoder.yudao.module.pay.service.app.PayAppService; |
| 28 | 28 | import cn.iocoder.yudao.module.pay.service.channel.PayChannelService; |
| 29 | 29 | import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService; | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceTest.java
| 1 | 1 | package cn.iocoder.yudao.module.pay.service.order; |
| 2 | 2 | |
| 3 | +import cn.hutool.extra.spring.SpringUtil; | |
| 3 | 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | -import cn.iocoder.yudao.framework.pay.config.PayProperties; | |
| 5 | 5 | import cn.iocoder.yudao.framework.pay.core.client.PayClient; |
| 6 | 6 | import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; |
| 7 | -import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; | |
| 7 | +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; | |
| 8 | 8 | import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; |
| 9 | -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | |
| 9 | +import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; | |
| 10 | +import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum; | |
| 11 | +import cn.iocoder.yudao.framework.test.core.ut.BaseDbAndRedisUnitTest; | |
| 10 | 12 | import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; |
| 11 | 13 | import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderExportReqVO; |
| 12 | 14 | import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderPageReqVO; |
| 13 | 15 | import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderSubmitReqVO; |
| 16 | +import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderSubmitRespVO; | |
| 14 | 17 | import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO; |
| 15 | 18 | import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO; |
| 16 | 19 | import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; |
| 17 | 20 | import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO; |
| 18 | 21 | import cn.iocoder.yudao.module.pay.dal.mysql.order.PayOrderExtensionMapper; |
| 19 | 22 | import cn.iocoder.yudao.module.pay.dal.mysql.order.PayOrderMapper; |
| 23 | +import cn.iocoder.yudao.module.pay.dal.redis.no.PayNoRedisDAO; | |
| 24 | +import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum; | |
| 20 | 25 | import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; |
| 26 | +import cn.iocoder.yudao.module.pay.framework.pay.config.PayProperties; | |
| 21 | 27 | import cn.iocoder.yudao.module.pay.service.app.PayAppService; |
| 22 | 28 | import cn.iocoder.yudao.module.pay.service.channel.PayChannelService; |
| 23 | 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; | |
| 24 | 32 | import org.junit.jupiter.api.Test; |
| 33 | +import org.mockito.ArgumentMatcher; | |
| 34 | +import org.mockito.MockedStatic; | |
| 25 | 35 | import org.springframework.boot.test.mock.mockito.MockBean; |
| 26 | 36 | import org.springframework.context.annotation.Import; |
| 27 | 37 | |
| ... | ... | @@ -30,6 +40,7 @@ import java.time.Duration; |
| 30 | 40 | import java.util.List; |
| 31 | 41 | |
| 32 | 42 | import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; |
| 43 | +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; | |
| 33 | 44 | import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; |
| 34 | 45 | import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; |
| 35 | 46 | import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; |
| ... | ... | @@ -37,19 +48,18 @@ import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; |
| 37 | 48 | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; |
| 38 | 49 | import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*; |
| 39 | 50 | import static org.assertj.core.api.Assertions.assertThat; |
| 40 | -import static org.junit.jupiter.api.Assertions.*; | |
| 41 | -import static org.mockito.ArgumentMatchers.any; | |
| 42 | -import static org.mockito.ArgumentMatchers.eq; | |
| 43 | -import static org.mockito.Mockito.mock; | |
| 44 | -import static org.mockito.Mockito.when; | |
| 51 | +import static org.junit.jupiter.api.Assertions.assertEquals; | |
| 52 | +import static org.junit.jupiter.api.Assertions.assertNotNull; | |
| 53 | +import static org.mockito.ArgumentMatchers.*; | |
| 54 | +import static org.mockito.Mockito.*; | |
| 45 | 55 | |
| 46 | 56 | /** |
| 47 | 57 | * {@link PayOrderServiceImpl} 的单元测试类 |
| 48 | 58 | * |
| 49 | 59 | * @author 芋艿 |
| 50 | 60 | */ |
| 51 | -@Import({PayOrderServiceImpl.class}) | |
| 52 | -public class PayOrderServiceTest extends BaseDbUnitTest { | |
| 61 | +@Import({PayOrderServiceImpl.class, PayNoRedisDAO.class}) | |
| 62 | +public class PayOrderServiceTest extends BaseDbAndRedisUnitTest { | |
| 53 | 63 | |
| 54 | 64 | @Resource |
| 55 | 65 | private PayOrderServiceImpl orderService; |
| ... | ... | @@ -70,6 +80,11 @@ public class PayOrderServiceTest extends BaseDbUnitTest { |
| 70 | 80 | @MockBean |
| 71 | 81 | private PayNotifyService notifyService; |
| 72 | 82 | |
| 83 | + @BeforeEach | |
| 84 | + public void setUp() { | |
| 85 | + when(properties.getOrderNotifyUrl()).thenReturn("http://127.0.0.1"); | |
| 86 | + } | |
| 87 | + | |
| 73 | 88 | @Test |
| 74 | 89 | public void testGetOrderPage() { |
| 75 | 90 | // mock 数据 |
| ... | ... | @@ -237,7 +252,7 @@ public class PayOrderServiceTest extends BaseDbUnitTest { |
| 237 | 252 | public void testSubmitOrder_channelNotFound() { |
| 238 | 253 | // mock 数据(order) |
| 239 | 254 | PayOrderDO order = randomPojo(PayOrderDO.class, o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus()) |
| 240 | - .setAppId(1L)); | |
| 255 | + .setAppId(1L).setExpireTime(addTime(Duration.ofDays(1)))); | |
| 241 | 256 | orderMapper.insert(order); |
| 242 | 257 | // 准备参数 |
| 243 | 258 | PayOrderSubmitReqVO reqVO = randomPojo(PayOrderSubmitReqVO.class, o -> o.setId(order.getId()) |
| ... | ... | @@ -257,54 +272,295 @@ public class PayOrderServiceTest extends BaseDbUnitTest { |
| 257 | 272 | |
| 258 | 273 | @Test // 调用 unifiedOrder 接口,返回存在渠道错误 |
| 259 | 274 | public void testSubmitOrder_channelError() { |
| 260 | - // mock 数据(order) | |
| 261 | - PayOrderDO order = randomPojo(PayOrderDO.class, o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus()) | |
| 262 | - .setAppId(1L)); | |
| 263 | - orderMapper.insert(order); | |
| 275 | + PayOrderServiceImpl payOrderServiceImpl = mock(PayOrderServiceImpl.class); | |
| 276 | + try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) { | |
| 277 | + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PayOrderServiceImpl.class))) | |
| 278 | + .thenReturn(payOrderServiceImpl); | |
| 279 | + | |
| 280 | + // mock 数据(order) | |
| 281 | + PayOrderDO order = randomPojo(PayOrderDO.class, o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus()) | |
| 282 | + .setAppId(1L).setExpireTime(addTime(Duration.ofDays(1)))); | |
| 283 | + orderMapper.insert(order); | |
| 284 | + // 准备参数 | |
| 285 | + PayOrderSubmitReqVO reqVO = randomPojo(PayOrderSubmitReqVO.class, o -> o.setId(order.getId()) | |
| 286 | + .setChannelCode(PayChannelEnum.ALIPAY_APP.getCode())); | |
| 287 | + String userIp = randomString(); | |
| 288 | + // mock 方法(app) | |
| 289 | + PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L)); | |
| 290 | + when(appService.validPayApp(eq(1L))).thenReturn(app); | |
| 291 | + // mock 方法(channel) | |
| 292 | + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L) | |
| 293 | + .setCode(PayChannelEnum.ALIPAY_APP.getCode())); | |
| 294 | + when(channelService.validPayChannel(eq(1L), eq(PayChannelEnum.ALIPAY_APP.getCode()))) | |
| 295 | + .thenReturn(channel); | |
| 296 | + // mock 方法(client) | |
| 297 | + PayClient client = mock(PayClient.class); | |
| 298 | + when(payClientFactory.getPayClient(eq(10L))).thenReturn(client); | |
| 299 | + // mock 方法() | |
| 300 | + PayOrderRespDTO unifiedOrderResp = randomPojo(PayOrderRespDTO.class, o -> | |
| 301 | + o.setChannelErrorCode("001").setChannelErrorMsg("模拟异常")); | |
| 302 | + when(client.unifiedOrder(argThat(payOrderUnifiedReqDTO -> { | |
| 303 | + assertNotNull(payOrderUnifiedReqDTO.getOutTradeNo()); | |
| 304 | + assertThat(payOrderUnifiedReqDTO) | |
| 305 | + .extracting("subject", "body", "notifyUrl", "returnUrl", "price", "expireTime") | |
| 306 | + .containsExactly(order.getSubject(), order.getBody(), "http://127.0.0.1/10", | |
| 307 | + reqVO.getReturnUrl(), order.getPrice(), order.getExpireTime()); | |
| 308 | + return true; | |
| 309 | + }))).thenReturn(unifiedOrderResp); | |
| 310 | + | |
| 311 | + // 调用,并断言异常 | |
| 312 | + assertServiceException(() -> orderService.submitOrder(reqVO, userIp), | |
| 313 | + ORDER_SUBMIT_CHANNEL_ERROR, "001", "模拟异常"); | |
| 314 | + // 断言,数据记录(PayOrderExtensionDO) | |
| 315 | + PayOrderExtensionDO orderExtension = orderExtensionMapper.selectOne(null); | |
| 316 | + assertNotNull(orderExtension); | |
| 317 | + assertThat(orderExtension).extracting("no", "orderId").isNotNull(); | |
| 318 | + assertThat(orderExtension) | |
| 319 | + .extracting("channelId", "channelCode","userIp" ,"status", "channelExtras", | |
| 320 | + "channelErrorCode", "channelErrorMsg", "channelNotifyData") | |
| 321 | + .containsExactly(10L, PayChannelEnum.ALIPAY_APP.getCode(), userIp, | |
| 322 | + PayOrderStatusEnum.WAITING.getStatus(), reqVO.getChannelExtras(), | |
| 323 | + null, null, null); | |
| 324 | + } | |
| 325 | + } | |
| 326 | + | |
| 327 | + @Test | |
| 328 | + public void testSubmitOrder_success() { | |
| 329 | + PayOrderServiceImpl payOrderServiceImpl = mock(PayOrderServiceImpl.class); | |
| 330 | + try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) { | |
| 331 | + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PayOrderServiceImpl.class))) | |
| 332 | + .thenReturn(payOrderServiceImpl); | |
| 333 | + | |
| 334 | + // mock 数据(order) | |
| 335 | + PayOrderDO order = randomPojo(PayOrderDO.class, o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus()) | |
| 336 | + .setAppId(1L).setExpireTime(addTime(Duration.ofDays(1)))); | |
| 337 | + orderMapper.insert(order); | |
| 338 | + // 准备参数 | |
| 339 | + PayOrderSubmitReqVO reqVO = randomPojo(PayOrderSubmitReqVO.class, o -> o.setId(order.getId()) | |
| 340 | + .setChannelCode(PayChannelEnum.ALIPAY_APP.getCode())); | |
| 341 | + String userIp = randomString(); | |
| 342 | + // mock 方法(app) | |
| 343 | + PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L)); | |
| 344 | + when(appService.validPayApp(eq(1L))).thenReturn(app); | |
| 345 | + // mock 方法(channel) | |
| 346 | + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L) | |
| 347 | + .setCode(PayChannelEnum.ALIPAY_APP.getCode())); | |
| 348 | + when(channelService.validPayChannel(eq(1L), eq(PayChannelEnum.ALIPAY_APP.getCode()))) | |
| 349 | + .thenReturn(channel); | |
| 350 | + // mock 方法(client) | |
| 351 | + PayClient client = mock(PayClient.class); | |
| 352 | + when(payClientFactory.getPayClient(eq(10L))).thenReturn(client); | |
| 353 | + // mock 方法() | |
| 354 | + PayOrderRespDTO unifiedOrderResp = randomPojo(PayOrderRespDTO.class, o -> o.setChannelErrorCode(null).setChannelErrorMsg(null) | |
| 355 | + .setDisplayMode(PayOrderDisplayModeEnum.URL.getMode()).setDisplayContent("tudou")); | |
| 356 | + when(client.unifiedOrder(argThat(payOrderUnifiedReqDTO -> { | |
| 357 | + assertNotNull(payOrderUnifiedReqDTO.getOutTradeNo()); | |
| 358 | + assertThat(payOrderUnifiedReqDTO) | |
| 359 | + .extracting("subject", "body", "notifyUrl", "returnUrl", "price", "expireTime") | |
| 360 | + .containsExactly(order.getSubject(), order.getBody(), "http://127.0.0.1/10", | |
| 361 | + reqVO.getReturnUrl(), order.getPrice(), order.getExpireTime()); | |
| 362 | + return true; | |
| 363 | + }))).thenReturn(unifiedOrderResp); | |
| 364 | + | |
| 365 | + // 调用 | |
| 366 | + PayOrderSubmitRespVO result = orderService.submitOrder(reqVO, userIp); | |
| 367 | + // 断言,数据记录(PayOrderExtensionDO) | |
| 368 | + PayOrderExtensionDO orderExtension = orderExtensionMapper.selectOne(null); | |
| 369 | + assertNotNull(orderExtension); | |
| 370 | + assertThat(orderExtension).extracting("no", "orderId").isNotNull(); | |
| 371 | + assertThat(orderExtension) | |
| 372 | + .extracting("channelId", "channelCode","userIp" ,"status", "channelExtras", | |
| 373 | + "channelErrorCode", "channelErrorMsg", "channelNotifyData") | |
| 374 | + .containsExactly(10L, PayChannelEnum.ALIPAY_APP.getCode(), userIp, | |
| 375 | + PayOrderStatusEnum.WAITING.getStatus(), reqVO.getChannelExtras(), | |
| 376 | + null, null, null); | |
| 377 | + // 断言,返回(PayOrderSubmitRespVO) | |
| 378 | + assertThat(result) | |
| 379 | + .extracting("status", "displayMode", "displayContent") | |
| 380 | + .containsExactly(PayOrderStatusEnum.WAITING.getStatus(), PayOrderDisplayModeEnum.URL.getMode(), "tudou"); | |
| 381 | + // 断言,调用 | |
| 382 | + verify(payOrderServiceImpl).notifyOrder(same(channel), same(unifiedOrderResp)); | |
| 383 | + } | |
| 384 | + } | |
| 385 | + | |
| 386 | + @Test | |
| 387 | + public void testNotifyOrder_channelId() { | |
| 388 | + PayOrderServiceImpl payOrderServiceImpl = mock(PayOrderServiceImpl.class); | |
| 389 | + try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) { | |
| 390 | + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PayOrderServiceImpl.class))) | |
| 391 | + .thenReturn(payOrderServiceImpl); | |
| 392 | + // 准备参数 | |
| 393 | + Long channelId = 10L; | |
| 394 | + PayOrderRespDTO notify = randomPojo(PayOrderRespDTO.class); | |
| 395 | + // mock 方法(channel) | |
| 396 | + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L)); | |
| 397 | + when(channelService.validPayChannel(eq(10L))).thenReturn(channel); | |
| 398 | + | |
| 399 | + // 调用 | |
| 400 | + orderService.notifyOrder(channelId, notify); | |
| 401 | + // 断言 | |
| 402 | + verify(payOrderServiceImpl).notifyOrder(same(channel), same(notify)); | |
| 403 | + } | |
| 404 | + } | |
| 405 | + | |
| 406 | + @Test | |
| 407 | + public void testNotifyOrderSuccess_orderExtension_notFound() { | |
| 264 | 408 | // 准备参数 |
| 265 | - PayOrderSubmitReqVO reqVO = randomPojo(PayOrderSubmitReqVO.class, o -> o.setId(order.getId()) | |
| 266 | - .setChannelCode(PayChannelEnum.ALIPAY_APP.getCode())); | |
| 267 | - String userIp = randomString(); | |
| 268 | - // mock 方法(app) | |
| 269 | - PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L)); | |
| 270 | - when(appService.validPayApp(eq(1L))).thenReturn(app); | |
| 271 | - // mock 方法(channel) | |
| 272 | - PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L) | |
| 273 | - .setCode(PayChannelEnum.ALIPAY_APP.getCode())); | |
| 274 | - when(channelService.validPayChannel(eq(1L), eq(PayChannelEnum.ALIPAY_APP.getCode()))) | |
| 275 | - .thenReturn(channel); | |
| 276 | - // mock 方法(client) | |
| 277 | - PayClient client = mock(PayClient.class); | |
| 278 | - when(payClientFactory.getPayClient(eq(10L))).thenReturn(client); | |
| 279 | - when(client.unifiedOrder(any(PayOrderUnifiedReqDTO.class))).thenThrow(new NullPointerException()); | |
| 409 | + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L)); | |
| 410 | + PayOrderRespDTO notify = randomPojo(PayOrderRespDTO.class, | |
| 411 | + o -> o.setStatus(PayOrderStatusRespEnum.SUCCESS.getStatus())); | |
| 412 | + | |
| 413 | + // 调用,并断言异常 | |
| 414 | + assertServiceException(() -> orderService.notifyOrder(channel, notify), | |
| 415 | + ORDER_EXTENSION_NOT_FOUND); | |
| 416 | + } | |
| 417 | + | |
| 418 | + @Test | |
| 419 | + public void testNotifyOrderSuccess_orderExtension_closed() { | |
| 420 | + // mock 数据(PayOrderExtensionDO) | |
| 421 | + PayOrderExtensionDO orderExtension = randomPojo(PayOrderExtensionDO.class, | |
| 422 | + o -> o.setStatus(PayOrderStatusEnum.CLOSED.getStatus()) | |
| 423 | + .setNo("P110")); | |
| 424 | + orderExtensionMapper.insert(orderExtension); | |
| 425 | + // 准备参数 | |
| 426 | + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L)); | |
| 427 | + PayOrderRespDTO notify = randomPojo(PayOrderRespDTO.class, | |
| 428 | + o -> o.setStatus(PayOrderStatusRespEnum.SUCCESS.getStatus()) | |
| 429 | + .setOutTradeNo("P110")); | |
| 280 | 430 | |
| 281 | 431 | // 调用,并断言异常 |
| 282 | - assertThrows(NullPointerException.class, () -> orderService.submitOrder(reqVO, userIp)); | |
| 283 | - // 断言,数据记录(PayOrderExtensionDO) | |
| 284 | - PayOrderExtensionDO orderExtension = orderExtensionMapper.selectOne(null); | |
| 285 | - assertNotNull(orderExtension); | |
| 286 | - assertThat(orderExtension).extracting("no", "orderId").isNotNull(); | |
| 287 | - assertThat(orderExtension) | |
| 288 | - .extracting("channelId", "channelCode","userIp" ,"status", "channelExtras", | |
| 289 | - "channelErrorCode", "channelErrorMsg", "channelNotifyData") | |
| 290 | - .containsExactly(10L, PayChannelEnum.ALIPAY_APP.getCode(), userIp, | |
| 291 | - PayOrderStatusEnum.WAITING.getStatus(), reqVO.getChannelExtras(), | |
| 292 | - null, null, null); | |
| 432 | + assertServiceException(() -> orderService.notifyOrder(channel, notify), | |
| 433 | + ORDER_EXTENSION_STATUS_IS_NOT_WAITING); | |
| 293 | 434 | } |
| 294 | 435 | |
| 295 | - @Test // 【成功】支付结果为等待中 | |
| 296 | - public void testSubmitOrder_waitingResult() { | |
| 436 | + @Test | |
| 437 | + public void testNotifyOrderSuccess_order_notFound() { | |
| 438 | + // mock 数据(PayOrderExtensionDO) | |
| 439 | + PayOrderExtensionDO orderExtension = randomPojo(PayOrderExtensionDO.class, | |
| 440 | + o -> o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()) | |
| 441 | + .setNo("P110")); | |
| 442 | + orderExtensionMapper.insert(orderExtension); | |
| 443 | + // 准备参数 | |
| 444 | + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L)); | |
| 445 | + PayOrderRespDTO notify = randomPojo(PayOrderRespDTO.class, | |
| 446 | + o -> o.setStatus(PayOrderStatusRespEnum.SUCCESS.getStatus()) | |
| 447 | + .setOutTradeNo("P110")); | |
| 297 | 448 | |
| 449 | + // 调用,并断言异常 | |
| 450 | + assertServiceException(() -> orderService.notifyOrder(channel, notify), | |
| 451 | + ORDER_NOT_FOUND); | |
| 452 | + // 断言 PayOrderExtensionDO :数据更新被回滚 | |
| 453 | + assertPojoEquals(orderExtension, orderExtensionMapper.selectOne(null)); | |
| 298 | 454 | } |
| 299 | 455 | |
| 300 | - @Test // 【成功】支付结果为已完成 | |
| 301 | - public void testSubmitOrder_successResult() { | |
| 456 | + @Test | |
| 457 | + public void testNotifyOrderSuccess_order_closed() { | |
| 458 | + testNotifyOrderSuccess_order_closedOrRefund(PayOrderStatusEnum.CLOSED.getStatus()); | |
| 459 | + } | |
| 302 | 460 | |
| 461 | + @Test | |
| 462 | + public void testNotifyOrderSuccess_order_refund() { | |
| 463 | + testNotifyOrderSuccess_order_closedOrRefund(PayOrderStatusEnum.REFUND.getStatus()); | |
| 303 | 464 | } |
| 304 | 465 | |
| 305 | - @Test // 【成功】支付结果为已关闭 | |
| 306 | - public void testSubmitOrder_closedResult() { | |
| 466 | + private void testNotifyOrderSuccess_order_closedOrRefund(Integer status) { | |
| 467 | + // mock 数据(PayOrderDO) | |
| 468 | + PayOrderDO order = randomPojo(PayOrderDO.class, o -> o.setStatus(status)); | |
| 469 | + orderMapper.insert(order); | |
| 470 | + // mock 数据(PayOrderExtensionDO) | |
| 471 | + PayOrderExtensionDO orderExtension = randomPojo(PayOrderExtensionDO.class, | |
| 472 | + o -> o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()) | |
| 473 | + .setNo("P110") | |
| 474 | + .setOrderId(order.getId())); | |
| 475 | + orderExtensionMapper.insert(orderExtension); | |
| 476 | + // 准备参数 | |
| 477 | + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L)); | |
| 478 | + PayOrderRespDTO notify = randomPojo(PayOrderRespDTO.class, | |
| 479 | + o -> o.setStatus(PayOrderStatusRespEnum.SUCCESS.getStatus()) | |
| 480 | + .setOutTradeNo("P110")); | |
| 481 | + | |
| 482 | + // 调用,并断言异常 | |
| 483 | + assertServiceException(() -> orderService.notifyOrder(channel, notify), | |
| 484 | + ORDER_STATUS_IS_NOT_WAITING); | |
| 485 | + // 断言 PayOrderExtensionDO :数据未更新,因为它是 SUCCESS | |
| 486 | + assertPojoEquals(orderExtension, orderExtensionMapper.selectOne(null)); | |
| 487 | + } | |
| 307 | 488 | |
| 489 | + @Test | |
| 490 | + public void testNotifyOrderSuccess_order_paid() { | |
| 491 | + // mock 数据(PayOrderDO) | |
| 492 | + PayOrderDO order = randomPojo(PayOrderDO.class, | |
| 493 | + o -> o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus())); | |
| 494 | + orderMapper.insert(order); | |
| 495 | + // mock 数据(PayOrderExtensionDO) | |
| 496 | + PayOrderExtensionDO orderExtension = randomPojo(PayOrderExtensionDO.class, | |
| 497 | + o -> o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()) | |
| 498 | + .setNo("P110") | |
| 499 | + .setOrderId(order.getId())); | |
| 500 | + orderExtensionMapper.insert(orderExtension); | |
| 501 | + // 重要:需要将 order 的 extensionId 更新下 | |
| 502 | + order.setExtensionId(orderExtension.getId()); | |
| 503 | + orderMapper.updateById(order); | |
| 504 | + // 准备参数 | |
| 505 | + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L)); | |
| 506 | + PayOrderRespDTO notify = randomPojo(PayOrderRespDTO.class, | |
| 507 | + o -> o.setStatus(PayOrderStatusRespEnum.SUCCESS.getStatus()) | |
| 508 | + .setOutTradeNo("P110")); | |
| 509 | + | |
| 510 | + // 调用,并断言异常 | |
| 511 | + orderService.notifyOrder(channel, notify); | |
| 512 | + // 断言 PayOrderExtensionDO :数据未更新,因为它是 SUCCESS | |
| 513 | + assertPojoEquals(orderExtension, orderExtensionMapper.selectOne(null)); | |
| 514 | + // 断言 PayOrderDO :数据未更新,因为它是 SUCCESS | |
| 515 | + assertPojoEquals(order, orderMapper.selectOne(null)); | |
| 516 | + // 断言,调用 | |
| 517 | + verify(notifyService, never()).createPayNotifyTask(any(PayNotifyTaskCreateReqDTO.class)); | |
| 518 | + } | |
| 519 | + | |
| 520 | + @Test | |
| 521 | + public void testNotifyOrderSuccess_order_waiting() { | |
| 522 | + // mock 数据(PayOrderDO) | |
| 523 | + PayOrderDO order = randomPojo(PayOrderDO.class, | |
| 524 | + o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus()) | |
| 525 | + .setPrice(10)); | |
| 526 | + orderMapper.insert(order); | |
| 527 | + // mock 数据(PayOrderExtensionDO) | |
| 528 | + PayOrderExtensionDO orderExtension = randomPojo(PayOrderExtensionDO.class, | |
| 529 | + o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus()) | |
| 530 | + .setNo("P110") | |
| 531 | + .setOrderId(order.getId())); | |
| 532 | + orderExtensionMapper.insert(orderExtension); | |
| 533 | + // 准备参数 | |
| 534 | + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L) | |
| 535 | + .setFeeRate(0.1D)); | |
| 536 | + PayOrderRespDTO notify = randomPojo(PayOrderRespDTO.class, | |
| 537 | + o -> o.setStatus(PayOrderStatusRespEnum.SUCCESS.getStatus()) | |
| 538 | + .setOutTradeNo("P110")); | |
| 539 | + | |
| 540 | + // 调用,并断言异常 | |
| 541 | + orderService.notifyOrder(channel, notify); | |
| 542 | + // 断言 PayOrderExtensionDO :数据未更新,因为它是 SUCCESS | |
| 543 | + orderExtension.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()) | |
| 544 | + .setChannelNotifyData(toJsonString(notify)); | |
| 545 | + assertPojoEquals(orderExtension, orderExtensionMapper.selectOne(null), | |
| 546 | + "updateTime", "updater"); | |
| 547 | + // 断言 PayOrderDO :数据未更新,因为它是 SUCCESS | |
| 548 | + order.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()) | |
| 549 | + .setChannelId(10L).setChannelCode(channel.getCode()) | |
| 550 | + .setSuccessTime(notify.getSuccessTime()).setExtensionId(orderExtension.getId()).setNo(orderExtension.getNo()) | |
| 551 | + .setChannelOrderNo(notify.getChannelOrderNo()).setChannelUserId(notify.getChannelUserId()) | |
| 552 | + .setChannelFeeRate(0.1D).setChannelFeePrice(1); | |
| 553 | + assertPojoEquals(order, orderMapper.selectOne(null), | |
| 554 | + "updateTime", "updater"); | |
| 555 | + // 断言,调用 | |
| 556 | + verify(notifyService).createPayNotifyTask(argThat(new ArgumentMatcher<PayNotifyTaskCreateReqDTO>() { | |
| 557 | + @Override | |
| 558 | + public boolean matches(PayNotifyTaskCreateReqDTO reqDTO) { | |
| 559 | + assertEquals(reqDTO.getType(), PayNotifyTypeEnum.ORDER.getType()); | |
| 560 | + assertEquals(reqDTO.getDataId(), orderExtension.getOrderId()); | |
| 561 | + return true; | |
| 562 | + } | |
| 563 | + })); | |
| 308 | 564 | } |
| 309 | 565 | |
| 310 | 566 | } | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceTest.java
| 1 | 1 | package cn.iocoder.yudao.module.pay.service.refund; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | -import cn.iocoder.yudao.framework.pay.config.PayProperties; | |
| 5 | 4 | import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; |
| 6 | 5 | import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; |
| 6 | +import cn.iocoder.yudao.framework.test.core.ut.BaseDbAndRedisUnitTest; | |
| 7 | 7 | import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; |
| 8 | 8 | import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundExportReqVO; |
| 9 | 9 | import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundPageReqVO; |
| ... | ... | @@ -11,6 +11,7 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; |
| 11 | 11 | import cn.iocoder.yudao.module.pay.dal.mysql.refund.PayRefundMapper; |
| 12 | 12 | import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; |
| 13 | 13 | import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum; |
| 14 | +import cn.iocoder.yudao.module.pay.framework.pay.config.PayProperties; | |
| 14 | 15 | import cn.iocoder.yudao.module.pay.service.app.PayAppService; |
| 15 | 16 | import cn.iocoder.yudao.module.pay.service.channel.PayChannelService; |
| 16 | 17 | import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService; |
| ... | ... | @@ -29,7 +30,7 @@ import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; |
| 29 | 30 | import static org.junit.jupiter.api.Assertions.assertEquals; |
| 30 | 31 | |
| 31 | 32 | @Import(PayRefundServiceImpl.class) |
| 32 | -public class PayRefundServiceTest extends BaseDbUnitTest { | |
| 33 | +public class PayRefundServiceTest extends BaseDbAndRedisUnitTest { | |
| 33 | 34 | |
| 34 | 35 | @Resource |
| 35 | 36 | private PayRefundServiceImpl refundService; | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/test/resources/sql/clean.sql
yudao-module-pay/yudao-module-pay-biz/src/test/resources/sql/create_tables.sql
| ... | ... | @@ -44,7 +44,7 @@ CREATE TABLE IF NOT EXISTS `pay_order` ( |
| 44 | 44 | `channel_fee_price` bigint(20) DEFAULT 0, |
| 45 | 45 | `status` tinyint(4) NOT NULL, |
| 46 | 46 | `user_ip` varchar(50) NOT NULL, |
| 47 | - `expire_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, | |
| 47 | + `expire_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, | |
| 48 | 48 | `success_time` datetime(0) DEFAULT CURRENT_TIMESTAMP, |
| 49 | 49 | `notify_time` datetime(0) DEFAULT CURRENT_TIMESTAMP, |
| 50 | 50 | `extension_id` bigint(20) DEFAULT NULL, |
| ... | ... | @@ -71,7 +71,7 @@ CREATE TABLE IF NOT EXISTS `pay_order_extension` ( |
| 71 | 71 | `channel_extras` varchar(1024) NULL DEFAULT NULL, |
| 72 | 72 | `channel_error_code` varchar(64) NULL, |
| 73 | 73 | `channel_error_msg` varchar(64) NULL, |
| 74 | - `channel_notify_data` varchar(64) NULL, | |
| 74 | + `channel_notify_data` varchar(1024) NULL, | |
| 75 | 75 | `creator` varchar(64) NULL DEFAULT '', |
| 76 | 76 | `create_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, |
| 77 | 77 | `updater` varchar(64) NULL DEFAULT '', | ... | ... |