diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/job/notify/PayNotifyJob.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/job/notify/PayNotifyJob.java new file mode 100644 index 000000000..58685f1b5 --- /dev/null +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/job/notify/PayNotifyJob.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.adminserver.modules.pay.job.notify; + +import cn.iocoder.yudao.coreservice.modules.pay.service.notify.PayNotifyCoreService; +import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + +/** + * 支付通知 Job + * 通过不断扫描待通知的 PayNotifyTaskDO 记录,回调业务线的回调接口 + * + * @author 芋道源码 + */ +@Component +@Slf4j +public class PayNotifyJob implements JobHandler { + + @Resource + private PayNotifyCoreService payNotifyCoreService; + + @Override + public String execute(String param) throws Exception { + int notifyCount = payNotifyCoreService.executeNotify(); + return String.format("执行支付通知 %s 个", notifyCount); + } + +} diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/job/package-info.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/job/package-info.java new file mode 100644 index 000000000..3db569789 --- /dev/null +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/job/package-info.java @@ -0,0 +1 @@ +package cn.iocoder.yudao.adminserver.modules.pay.job; diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/enums/SysErrorCodeConstants.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/enums/SysErrorCodeConstants.java index 8a606e062..0fad26be8 100644 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/enums/SysErrorCodeConstants.java +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/enums/SysErrorCodeConstants.java @@ -1,7 +1,6 @@ package cn.iocoder.yudao.adminserver.modules.system.enums; import cn.iocoder.yudao.framework.common.exception.ErrorCode; -import javafx.beans.binding.MapExpression; /** * System 错误码枚举类 diff --git a/yudao-admin-server/src/main/resources/application-local.yaml b/yudao-admin-server/src/main/resources/application-local.yaml index 2d36b7157..4f45d9476 100644 --- a/yudao-admin-server/src/main/resources/application-local.yaml +++ b/yudao-admin-server/src/main/resources/application-local.yaml @@ -166,6 +166,9 @@ yudao: exclude-urls: # 如下两个 url,仅仅是为了演示,去掉配置也没关系 - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求 - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 + pay: + pay-notify-url: http://niubi.natapp1.cc/api/pay/order/notify + refund-notify-url: http://niubi.natapp1.cc/api/pay/refund/notify demo: false # 关闭演示模式 justauth: diff --git a/yudao-core-service/pom.xml b/yudao-core-service/pom.xml index 278c33fc7..da3e9bece 100644 --- a/yudao-core-service/pom.xml +++ b/yudao-core-service/pom.xml @@ -77,6 +77,12 @@ yudao-spring-boot-starter-mq + + + cn.iocoder.boot + yudao-spring-boot-starter-protection + + cn.iocoder.boot diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/notify/PayNotifyLogDO.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/notify/PayNotifyLogDO.java index 460cb1063..80ed5b017 100644 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/notify/PayNotifyLogDO.java +++ b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/notify/PayNotifyLogDO.java @@ -34,15 +34,11 @@ public class PayNotifyLogDO extends BaseDO { */ private Integer notifyTimes; /** - * 请求参数 - */ - private String request; - /** - * 响应结果 + * HTTP 响应结果 */ private String response; /** - * 状态 + * 支付通知状态 * * 外键 {@link PayNotifyStatusEnum} */ diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/mysql/notify/PayNotifyTaskCoreMapper.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/mysql/notify/PayNotifyTaskCoreMapper.java index e3d70b7b2..276c6710b 100644 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/mysql/notify/PayNotifyTaskCoreMapper.java +++ b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/mysql/notify/PayNotifyTaskCoreMapper.java @@ -1,10 +1,30 @@ package cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.notify; -import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayAppDO; import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.notify.PayNotifyTaskDO; +import cn.iocoder.yudao.coreservice.modules.pay.enums.notify.PayNotifyStatusEnum; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import org.apache.ibatis.annotations.Mapper; +import java.util.Date; +import java.util.List; + @Mapper public interface PayNotifyTaskCoreMapper extends BaseMapperX { + + /** + * 获得需要通知的 PayNotifyTaskDO 记录。需要满足如下条件: + * + * 1. status 非成功 + * 2. nextNotifyTime 小于当前时间 + * + * @return PayTransactionNotifyTaskDO 数组 + */ + default List selectListByNotify() { + return selectList(new QueryWrapper() + .in("status", PayNotifyStatusEnum.WAITING.getStatus(), PayNotifyStatusEnum.REQUEST_SUCCESS.getStatus(), + PayNotifyStatusEnum.REQUEST_FAILURE.getStatus()) + .le("next_notify_time", new Date())); + } + } diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/redis/PayRedisKeyCoreConstants.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/redis/PayRedisKeyCoreConstants.java new file mode 100644 index 000000000..99384ec12 --- /dev/null +++ b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/redis/PayRedisKeyCoreConstants.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.coreservice.modules.pay.dal.redis; + +import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine; +import org.redisson.api.RLock; + +import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.HASH; + +/** + * Lock4j Redis Key 枚举类 + * + * @author 芋道源码 + */ +public interface PayRedisKeyCoreConstants { + + RedisKeyDefine PAY_NOTIFY_LOCK = new RedisKeyDefine("通知任务的分布式锁", + "pay_notify:lock:", // 参数来自 DefaultLockKeyBuilder 类 + HASH, RLock.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC); // Redisson 的 Lock 锁,使用 Hash 数据结构 + +} diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/redis/notify/PayNotifyLockCoreRedisDAO.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/redis/notify/PayNotifyLockCoreRedisDAO.java new file mode 100644 index 000000000..5ce6b44b3 --- /dev/null +++ b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/redis/notify/PayNotifyLockCoreRedisDAO.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.coreservice.modules.pay.dal.redis.notify; + +import org.redisson.api.RLock; +import org.redisson.api.RedissonClient; +import org.springframework.stereotype.Repository; + +import javax.annotation.Resource; +import java.util.concurrent.TimeUnit; + +import static cn.iocoder.yudao.coreservice.modules.pay.dal.redis.PayRedisKeyCoreConstants.PAY_NOTIFY_LOCK; + +/** + * 支付通知的锁 Redis DAO + * + * @author 芋道源码 + */ +@Repository +public class PayNotifyLockCoreRedisDAO { + + @Resource + private RedissonClient redissonClient; + + public void lock(Long id, Long timeoutMillis, Runnable runnable) { + String lockKey = formatKey(id); + RLock lock = redissonClient.getLock(lockKey); + try { + lock.lock(timeoutMillis, TimeUnit.MILLISECONDS); + // 执行逻辑 + runnable.run(); + } finally { + lock.unlock(); + } + } + + private static String formatKey(Long id) { + return String.format(PAY_NOTIFY_LOCK.getKeyTemplate(), id); + } + +} diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/notify/PayNotifyCoreService.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/notify/PayNotifyCoreService.java index 2bad6985e..468375709 100644 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/notify/PayNotifyCoreService.java +++ b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/notify/PayNotifyCoreService.java @@ -22,7 +22,8 @@ public interface PayNotifyCoreService { * 执行支付通知 * * 注意,该方法提供给定时任务调用。目前是 yudao-admin-server 进行调用 + * @return 通知数量 */ - void executeNotify(); + int executeNotify() throws InterruptedException; } diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/notify/impl/PayNotifyCoreServiceImpl.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/notify/impl/PayNotifyCoreServiceImpl.java index d57cbfc36..77a500a37 100644 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/notify/impl/PayNotifyCoreServiceImpl.java +++ b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/notify/impl/PayNotifyCoreServiceImpl.java @@ -1,22 +1,38 @@ package cn.iocoder.yudao.coreservice.modules.pay.service.notify.impl; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.http.HttpUtil; import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.notify.PayNotifyTaskDO; import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderDO; import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.notify.PayNotifyTaskCoreMapper; +import cn.iocoder.yudao.coreservice.modules.pay.dal.redis.notify.PayNotifyLockCoreRedisDAO; import cn.iocoder.yudao.coreservice.modules.pay.enums.notify.PayNotifyStatusEnum; import cn.iocoder.yudao.coreservice.modules.pay.enums.notify.PayNotifyTypeEnum; import cn.iocoder.yudao.coreservice.modules.pay.service.notify.PayNotifyCoreService; import cn.iocoder.yudao.coreservice.modules.pay.service.notify.dto.PayNotifyTaskCreateReqDTO; +import cn.iocoder.yudao.coreservice.modules.pay.service.notify.vo.PayNotifyOrderReqVO; +import cn.iocoder.yudao.coreservice.modules.pay.service.notify.vo.PayRefundOrderReqVO; import cn.iocoder.yudao.coreservice.modules.pay.service.order.PayOrderCoreService; +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 lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Lazy; +import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import javax.validation.Valid; import java.util.Date; +import java.util.List; import java.util.Objects; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.SECOND_MILLIS; +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; /** * 支付通知 Core Service 实现类 @@ -28,6 +44,15 @@ import java.util.Objects; @Slf4j public class PayNotifyCoreServiceImpl implements PayNotifyCoreService { + /** + * 通知超时时间,单位:秒 + */ + public static final int NOTIFY_TIMEOUT = 120; + /** + * {@link #NOTIFY_TIMEOUT} 的毫秒 + */ + public static final long NOTIFY_TIMEOUT_MILLIS = 120 * SECOND_MILLIS; + @Resource @Lazy // 循环依赖,避免报错 private PayOrderCoreService payOrderCoreService; @@ -38,6 +63,13 @@ public class PayNotifyCoreServiceImpl implements PayNotifyCoreService { @Resource private ThreadPoolTaskExecutor threadPoolTaskExecutor; // TODO 芋艿:未来提供独立的线程池 + @Resource + private PayNotifyLockCoreRedisDAO payNotifyLockCoreRedisDAO; + + @Resource + @Lazy // 循环依赖(自己依赖自己),避免报错 + private PayNotifyCoreServiceImpl self; + @Override public void createPayNotifyTask(PayNotifyTaskCreateReqDTO reqDTO) { PayNotifyTaskDO task = new PayNotifyTaskDO(); @@ -56,11 +88,132 @@ public class PayNotifyCoreServiceImpl implements PayNotifyCoreService { // 执行插入 payNotifyTaskCoreMapper.insert(task); + + // 异步直接发起任务。虽然会有定时任务扫描,但是会导致延迟 + self.executeNotifyAsync(task); } @Override - public void executeNotify() { + public int executeNotify() throws InterruptedException { + // 获得需要通知的任务 + List tasks = payNotifyTaskCoreMapper.selectListByNotify(); + if (CollUtil.isEmpty(tasks)) { + return 0; + } + // 遍历,逐个通知 + CountDownLatch latch = new CountDownLatch(tasks.size()); + tasks.forEach(task -> threadPoolTaskExecutor.execute(() -> { + try { + executeNotify(task); + } finally { + latch.countDown(); + } + })); + // 等待完成 + this.awaitExecuteNotify(latch); + // 返回执行完成的任务数(成功 + 失败) + return tasks.size(); + } + + /** + * 等待全部支付通知的完成 + * 每 1 秒会打印一次剩余任务数量 + * + * @param latch Latch + * @throws InterruptedException 如果被打断 + */ + private void awaitExecuteNotify(CountDownLatch latch) throws InterruptedException { + long size = latch.getCount(); + for (int i = 0; i < NOTIFY_TIMEOUT; i++) { + if (latch.await(1L, TimeUnit.SECONDS)) { + return; + } + log.info("[awaitExecuteNotify][任务处理中, 总任务数({}) 剩余任务数({})]", size, latch.getCount()); + } + log.error("[awaitExecuteNotify][任务未处理完,总任务数({}) 剩余任务数({})]", size, latch.getCount()); + } + + /** + * 异步执行单个支付通知 + * + * @param task 通知任务 + */ + @Async + public void executeNotifyAsync(PayNotifyTaskDO task) { + self.executeNotify(task); // 使用 self,避免事务不发起 + } + + /** + * 同步执行单个支付通知 + * + * @param task 通知任务 + */ + public void executeNotify(PayNotifyTaskDO task) { + // 分布式锁,避免并发问题 + payNotifyLockCoreRedisDAO.lock(task.getId(), NOTIFY_TIMEOUT_MILLIS, () -> { + // 校验,当前任务是否已经被通知过 + // 虽然已经通过分布式加锁,但是可能同时满足通知的条件,然后都去获得锁。此时,第一个执行完后,第二个还是能拿到锁,然后会再执行一次。 + PayNotifyTaskDO dbTask = payNotifyTaskCoreMapper.selectById(task.getId()); + if (DateUtils.afterNow(dbTask.getNextNotifyTime())) { + log.info("[executeNotify][dbTask({}) 任务被忽略,原因是未到达下次通知时间,可能是因为并发执行了]", toJsonString(dbTask)); + return; + } + + // 执行通知 + executeNotify0(dbTask); + }); + } + + @Transactional + public void executeNotify0(PayNotifyTaskDO task) { + // 发起回调 + CommonResult invokeResult = null; + Throwable invokeException = null; + try { + invokeResult = executeNotifyInvoke(task); + } catch (Throwable e) { + invokeException = e; + } + + // 设置通用的更新 PayNotifyTaskDO 的字段 + PayNotifyTaskDO updateTask = new PayNotifyTaskDO() + .setId(task.getId()) + .setLastExecuteTime(new Date()) + .setNotifyTimes(task.getNotifyTimes() + 1); + + // 情况一:调用成功 + + // 情况二:调用失败 + + // 调用三:调用异常 + + // 记录 PayNotifyLog 日志 + } + + /** + * 执行单个支付任务的 HTTP 调用 + * + * @param task 通知任务 + * @return HTTP 响应 + */ + private CommonResult executeNotifyInvoke(PayNotifyTaskDO task) { + // 拼接参数 + Object request; + if (Objects.equals(task.getType(), PayNotifyTypeEnum.ORDER.getType())) { + request = PayNotifyOrderReqVO.builder().merchantOrderId(task.getMerchantOrderId()) + .payOrderId(task.getDataId()).build(); + } else if (Objects.equals(task.getType(), PayNotifyTypeEnum.REFUND.getType())) { + request = PayRefundOrderReqVO.builder().merchantOrderId(task.getMerchantOrderId()) + .payRefundId(task.getDataId()).build(); + } else { + throw new RuntimeException("未知的通知任务类型:" + toJsonString(task)); + } + // 请求地址 + String response = HttpUtil.post(task.getNotifyUrl(), toJsonString(request), + (int) NOTIFY_TIMEOUT_MILLIS); + // 解析结果 + return JsonUtils.parseObject(response, CommonResult.class); } } diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/notify/vo/PayNotifyOrderReqVO.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/notify/vo/PayNotifyOrderReqVO.java new file mode 100644 index 000000000..94b1fec56 --- /dev/null +++ b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/notify/vo/PayNotifyOrderReqVO.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.coreservice.modules.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; + +} diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/notify/vo/PayRefundOrderReqVO.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/notify/vo/PayRefundOrderReqVO.java new file mode 100644 index 000000000..705800892 --- /dev/null +++ b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/notify/vo/PayRefundOrderReqVO.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.coreservice.modules.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; + +} diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/notify/vo/package-info.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/notify/vo/package-info.java new file mode 100644 index 000000000..78667d3ce --- /dev/null +++ b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/notify/vo/package-info.java @@ -0,0 +1,6 @@ +/** + * 这里的 VO 包有点特殊,是提供给接入支付模块的业务,提供回调接口时,可以直接使用 VO + * + * 例如说,支付单的回调,使用 + */ +package cn.iocoder.yudao.coreservice.modules.pay.service.notify.vo; diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java index 783747087..b90673e90 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java @@ -6,6 +6,8 @@ import java.util.Date; /** * 时间工具类 + * + * @author 芋道源码 */ public class DateUtils { @@ -14,6 +16,11 @@ public class DateUtils { */ public static final String TIME_ZONE_DEFAULT = "GMT+8"; + /** + * 秒转换成毫秒 + */ + public static final long SECOND_MILLIS = 1000; + public static final String FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND = "yyyy-MM-dd HH:mm:ss"; public static Date addTime(Duration duration) { @@ -74,4 +81,12 @@ public class DateUtils { return a.compareTo(b) > 0 ? a : b; } + public static boolean beforeNow(Date date) { + return date.getTime() < System.currentTimeMillis(); + } + + public static boolean afterNow(Date date) { + return date.getTime() >= System.currentTimeMillis(); + } + } diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/shop/controller/ShopOrderController.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/shop/controller/ShopOrderController.java index fccc69d3e..4767c074d 100644 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/shop/controller/ShopOrderController.java +++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/shop/controller/ShopOrderController.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.userserver.modules.shop.controller; +import cn.iocoder.yudao.coreservice.modules.pay.service.notify.vo.PayNotifyOrderReqVO; import cn.iocoder.yudao.coreservice.modules.pay.service.order.PayOrderCoreService; import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayOrderCreateReqDTO; import cn.iocoder.yudao.framework.common.pojo.CommonResult; @@ -10,10 +11,12 @@ import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; +import javax.validation.Valid; import java.time.Duration; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; @@ -52,4 +55,11 @@ public class ShopOrderController { .payOrderId(payOrderId).build()); } + @PostMapping("/pay-notify") + @ApiOperation("支付回调") + public CommonResult payNotify(@RequestBody @Valid PayNotifyOrderReqVO reqVO) { + log.info("[payNotify][回调成功]"); + return success(true); + } + }