Commit c89accbf060d4828601be1fba35a3594ef078ab2
1 parent
2df46a7c
完成支付通知的逻辑
Showing
4 changed files
with
102 additions
and
23 deletions
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/notify/PayNotifyLogDO.java
| ... | ... | @@ -3,9 +3,7 @@ package cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.notify; |
| 3 | 3 | import cn.iocoder.yudao.coreservice.modules.pay.enums.notify.PayNotifyStatusEnum; |
| 4 | 4 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| 5 | 5 | import com.baomidou.mybatisplus.annotation.TableName; |
| 6 | -import lombok.Data; | |
| 7 | -import lombok.EqualsAndHashCode; | |
| 8 | -import lombok.experimental.Accessors; | |
| 6 | +import lombok.*; | |
| 9 | 7 | |
| 10 | 8 | /** |
| 11 | 9 | * 商户支付、退款等的通知 Log |
| ... | ... | @@ -16,7 +14,9 @@ import lombok.experimental.Accessors; |
| 16 | 14 | @TableName("pay_notify_log") |
| 17 | 15 | @Data |
| 18 | 16 | @EqualsAndHashCode(callSuper = true) |
| 19 | -@Accessors(chain = true) | |
| 17 | +@Builder | |
| 18 | +@NoArgsConstructor | |
| 19 | +@AllArgsConstructor | |
| 20 | 20 | public class PayNotifyLogDO extends BaseDO { |
| 21 | 21 | |
| 22 | 22 | /** |
| ... | ... | @@ -24,13 +24,15 @@ public class PayNotifyLogDO extends BaseDO { |
| 24 | 24 | */ |
| 25 | 25 | private Long id; |
| 26 | 26 | /** |
| 27 | - * 通知编号 | |
| 27 | + * 通知任务编号 | |
| 28 | 28 | * |
| 29 | 29 | * 关联 {@link PayNotifyTaskDO#getId()} |
| 30 | 30 | */ |
| 31 | - private Long notifyId; | |
| 31 | + private Long taskId; | |
| 32 | 32 | /** |
| 33 | - * 当前通知次数 | |
| 33 | + * 第几次被通知 | |
| 34 | + * | |
| 35 | + * 对应到 {@link PayNotifyTaskDO#getNotifyTimes()} | |
| 34 | 36 | */ |
| 35 | 37 | private Integer notifyTimes; |
| 36 | 38 | /** | ... | ... |
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/mysql/notify/PayNotifyLogCoreMapper.java
0 → 100644
| 1 | +package cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.notify; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.notify.PayNotifyLogDO; | |
| 4 | +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | |
| 5 | +import org.apache.ibatis.annotations.Mapper; | |
| 6 | + | |
| 7 | +@Mapper | |
| 8 | +public interface PayNotifyLogCoreMapper extends BaseMapperX<PayNotifyLogDO> { | |
| 9 | +} | ... | ... |
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/notify/impl/PayNotifyCoreServiceImpl.java
| ... | ... | @@ -2,8 +2,10 @@ package cn.iocoder.yudao.coreservice.modules.pay.service.notify.impl; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.hutool.http.HttpUtil; |
| 5 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.notify.PayNotifyLogDO; | |
| 5 | 6 | import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.notify.PayNotifyTaskDO; |
| 6 | 7 | import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderDO; |
| 8 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.notify.PayNotifyLogCoreMapper; | |
| 7 | 9 | import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.notify.PayNotifyTaskCoreMapper; |
| 8 | 10 | import cn.iocoder.yudao.coreservice.modules.pay.dal.redis.notify.PayNotifyLockCoreRedisDAO; |
| 9 | 11 | import cn.iocoder.yudao.coreservice.modules.pay.enums.notify.PayNotifyStatusEnum; |
| ... | ... | @@ -25,12 +27,14 @@ import org.springframework.transaction.annotation.Transactional; |
| 25 | 27 | |
| 26 | 28 | import javax.annotation.Resource; |
| 27 | 29 | import javax.validation.Valid; |
| 30 | +import java.util.Calendar; | |
| 28 | 31 | import java.util.Date; |
| 29 | 32 | import java.util.List; |
| 30 | 33 | import java.util.Objects; |
| 31 | 34 | import java.util.concurrent.CountDownLatch; |
| 32 | 35 | import java.util.concurrent.TimeUnit; |
| 33 | 36 | |
| 37 | +import static cn.hutool.core.exceptions.ExceptionUtil.getRootCauseMessage; | |
| 34 | 38 | import static cn.iocoder.yudao.framework.common.util.date.DateUtils.SECOND_MILLIS; |
| 35 | 39 | import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; |
| 36 | 40 | |
| ... | ... | @@ -59,6 +63,8 @@ public class PayNotifyCoreServiceImpl implements PayNotifyCoreService { |
| 59 | 63 | |
| 60 | 64 | @Resource |
| 61 | 65 | private PayNotifyTaskCoreMapper payNotifyTaskCoreMapper; |
| 66 | + @Resource | |
| 67 | + private PayNotifyLogCoreMapper payNotifyLogCoreMapper; | |
| 62 | 68 | |
| 63 | 69 | @Resource |
| 64 | 70 | private ThreadPoolTaskExecutor threadPoolTaskExecutor; // TODO 芋艿:未来提供独立的线程池 |
| ... | ... | @@ -105,7 +111,7 @@ public class PayNotifyCoreServiceImpl implements PayNotifyCoreService { |
| 105 | 111 | CountDownLatch latch = new CountDownLatch(tasks.size()); |
| 106 | 112 | tasks.forEach(task -> threadPoolTaskExecutor.execute(() -> { |
| 107 | 113 | try { |
| 108 | - executeNotify(task); | |
| 114 | + executeNotifySync(task); | |
| 109 | 115 | } finally { |
| 110 | 116 | latch.countDown(); |
| 111 | 117 | } |
| ... | ... | @@ -141,7 +147,7 @@ public class PayNotifyCoreServiceImpl implements PayNotifyCoreService { |
| 141 | 147 | */ |
| 142 | 148 | @Async |
| 143 | 149 | public void executeNotifyAsync(PayNotifyTaskDO task) { |
| 144 | - self.executeNotify(task); // 使用 self,避免事务不发起 | |
| 150 | + self.executeNotifySync(task); // 使用 self,避免事务不发起 | |
| 145 | 151 | } |
| 146 | 152 | |
| 147 | 153 | /** |
| ... | ... | @@ -149,7 +155,7 @@ public class PayNotifyCoreServiceImpl implements PayNotifyCoreService { |
| 149 | 155 | * |
| 150 | 156 | * @param task 通知任务 |
| 151 | 157 | */ |
| 152 | - public void executeNotify(PayNotifyTaskDO task) { | |
| 158 | + public void executeNotifySync(PayNotifyTaskDO task) { | |
| 153 | 159 | // 分布式锁,避免并发问题 |
| 154 | 160 | payNotifyLockCoreRedisDAO.lock(task.getId(), NOTIFY_TIMEOUT_MILLIS, () -> { |
| 155 | 161 | // 校验,当前任务是否已经被通知过 |
| ... | ... | @@ -161,12 +167,12 @@ public class PayNotifyCoreServiceImpl implements PayNotifyCoreService { |
| 161 | 167 | } |
| 162 | 168 | |
| 163 | 169 | // 执行通知 |
| 164 | - executeNotify0(dbTask); | |
| 170 | + executeNotify(dbTask); | |
| 165 | 171 | }); |
| 166 | 172 | } |
| 167 | 173 | |
| 168 | 174 | @Transactional |
| 169 | - public void executeNotify0(PayNotifyTaskDO task) { | |
| 175 | + public void executeNotify(PayNotifyTaskDO task) { | |
| 170 | 176 | // 发起回调 |
| 171 | 177 | CommonResult<?> invokeResult = null; |
| 172 | 178 | Throwable invokeException = null; |
| ... | ... | @@ -176,19 +182,13 @@ public class PayNotifyCoreServiceImpl implements PayNotifyCoreService { |
| 176 | 182 | invokeException = e; |
| 177 | 183 | } |
| 178 | 184 | |
| 179 | - // 设置通用的更新 PayNotifyTaskDO 的字段 | |
| 180 | - PayNotifyTaskDO updateTask = new PayNotifyTaskDO() | |
| 181 | - .setId(task.getId()) | |
| 182 | - .setLastExecuteTime(new Date()) | |
| 183 | - .setNotifyTimes(task.getNotifyTimes() + 1); | |
| 184 | - | |
| 185 | - // 情况一:调用成功 | |
| 186 | - | |
| 187 | - // 情况二:调用失败 | |
| 188 | - | |
| 189 | - // 调用三:调用异常 | |
| 185 | + // 处理 | |
| 186 | + Integer newStatus = this.processNotifyResult(task, invokeResult, invokeException); | |
| 190 | 187 | |
| 191 | 188 | // 记录 PayNotifyLog 日志 |
| 189 | + String response = invokeException != null ? getRootCauseMessage(invokeException) : toJsonString(invokeResult); | |
| 190 | + payNotifyLogCoreMapper.insert(PayNotifyLogDO.builder().taskId(task.getId()) | |
| 191 | + .notifyTimes(task.getNotifyTimes() + 1).status(newStatus).response(response).build()); | |
| 192 | 192 | } |
| 193 | 193 | |
| 194 | 194 | /** |
| ... | ... | @@ -216,4 +216,41 @@ public class PayNotifyCoreServiceImpl implements PayNotifyCoreService { |
| 216 | 216 | return JsonUtils.parseObject(response, CommonResult.class); |
| 217 | 217 | } |
| 218 | 218 | |
| 219 | + /** | |
| 220 | + * 处理并更新通知结果 | |
| 221 | + * | |
| 222 | + * @param task 通知任务 | |
| 223 | + * @param invokeResult 通知结果 | |
| 224 | + * @param invokeException 通知异常 | |
| 225 | + * @return 最终任务的状态 | |
| 226 | + */ | |
| 227 | + private Integer processNotifyResult(PayNotifyTaskDO task, CommonResult<?> invokeResult, Throwable invokeException) { | |
| 228 | + // 设置通用的更新 PayNotifyTaskDO 的字段 | |
| 229 | + PayNotifyTaskDO updateTask = new PayNotifyTaskDO() | |
| 230 | + .setId(task.getId()) | |
| 231 | + .setLastExecuteTime(new Date()) | |
| 232 | + .setNotifyTimes(task.getNotifyTimes() + 1); | |
| 233 | + | |
| 234 | + // 情况一:调用成功 | |
| 235 | + if (invokeResult != null && invokeResult.isSuccess()) { | |
| 236 | + updateTask.setStatus(PayNotifyStatusEnum.SUCCESS.getStatus()); | |
| 237 | + return updateTask.getStatus(); | |
| 238 | + } | |
| 239 | + // 情况二:调用失败、调用异常 | |
| 240 | + // 2.1 超过最大回调次数 | |
| 241 | + if (updateTask.getNotifyTimes() >= PayNotifyTaskDO.NOTIFY_FREQUENCY.length) { | |
| 242 | + updateTask.setStatus(PayNotifyStatusEnum.FAILURE.getStatus()); | |
| 243 | + return updateTask.getStatus(); | |
| 244 | + } | |
| 245 | + // 2.2 未超过最大回调次数 | |
| 246 | + updateTask.setNextNotifyTime(DateUtils.addDate(Calendar.SECOND, PayNotifyTaskDO.NOTIFY_FREQUENCY[updateTask.getNotifyTimes()])); | |
| 247 | + updateTask.setStatus(invokeException != null ? PayNotifyStatusEnum.REQUEST_FAILURE.getStatus() | |
| 248 | + : PayNotifyStatusEnum.REQUEST_SUCCESS.getStatus()); | |
| 249 | + return updateTask.getStatus(); | |
| 250 | + } | |
| 251 | + | |
| 252 | + private void processNotifySuccess(PayNotifyTaskDO task, PayNotifyTaskDO updateTask) { | |
| 253 | + payNotifyTaskCoreMapper.updateById(updateTask); | |
| 254 | + } | |
| 255 | + | |
| 219 | 256 | } | ... | ... |
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java
| ... | ... | @@ -89,4 +89,35 @@ public class DateUtils { |
| 89 | 89 | return date.getTime() >= System.currentTimeMillis(); |
| 90 | 90 | } |
| 91 | 91 | |
| 92 | + /** | |
| 93 | + * 计算当期时间相差的日期 | |
| 94 | + * | |
| 95 | + * @param field 日历字段.<br/>eg:Calendar.MONTH,Calendar.DAY_OF_MONTH,<br/>Calendar.HOUR_OF_DAY等. | |
| 96 | + * @param amount 相差的数值 | |
| 97 | + * @return 计算后的日志 | |
| 98 | + */ | |
| 99 | + public static Date addDate(int field, int amount) { | |
| 100 | + return addDate(null, field, amount); | |
| 101 | + } | |
| 102 | + | |
| 103 | + /** | |
| 104 | + * 计算当期时间相差的日期 | |
| 105 | + * | |
| 106 | + * @param date 设置时间 | |
| 107 | + * @param field 日历字段 例如说,{@link Calendar#DAY_OF_MONTH} 等 | |
| 108 | + * @param amount 相差的数值 | |
| 109 | + * @return 计算后的日志 | |
| 110 | + */ | |
| 111 | + public static Date addDate(Date date, int field, int amount) { | |
| 112 | + if (amount == 0) { | |
| 113 | + return date; | |
| 114 | + } | |
| 115 | + Calendar c = Calendar.getInstance(); | |
| 116 | + if (date != null) { | |
| 117 | + c.setTime(date); | |
| 118 | + } | |
| 119 | + c.add(field, amount); | |
| 120 | + return c.getTime(); | |
| 121 | + } | |
| 122 | + | |
| 92 | 123 | } | ... | ... |