Commit 1cd9085c5938bdad7312a80bdadb3d41dc8afe2d

Authored by YunaiV
1 parent 0247fd5c

pay: PayNotifyJob 增加多租户的支持

Showing 12 changed files with 123 additions and 73 deletions
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/util/TenantUtils.java
... ... @@ -2,6 +2,10 @@ package cn.iocoder.yudao.framework.tenant.core.util;
2 2  
3 3 import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
4 4  
  5 +import java.util.Map;
  6 +
  7 +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID;
  8 +
5 9 /**
6 10 * 多租户 Util
7 11 *
... ... @@ -32,4 +36,16 @@ public class TenantUtils {
32 36 }
33 37 }
34 38  
  39 + /**
  40 + * 将多租户编号,添加到 header 中
  41 + *
  42 + * @param headers HTTP 请求 headers
  43 + */
  44 + public static void addTenantHeader(Map<String, String> headers) {
  45 + Long tenantId = TenantContextHolder.getTenantId();
  46 + if (tenantId != null) {
  47 + headers.put(HEADER_TENANT_ID, tenantId.toString());
  48 + }
  49 + }
  50 +
35 51 }
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java
... ... @@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 6 import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
7 7 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
  8 +import cn.iocoder.yudao.module.pay.api.notify.dto.PayOrderNotifyReqDTO;
8 9 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO;
9 10 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderGetCreateInfoRespVO;
10 11 import cn.iocoder.yudao.module.trade.controller.app.order.vo.TradeOrderPageReqVO;
... ... @@ -33,7 +34,7 @@ public class AppTradeOrderController {
33 34 @GetMapping("/get-create-info")
34 35 @ApiOperation("基于商品,确认创建订单")
35 36 @PreAuthenticated
36   - public CommonResult<AppTradeOrderGetCreateInfoRespVO> getTradeOrderCreateInfo(AppTradeOrderCreateReqVO createReqVO) {
  37 + public CommonResult<AppTradeOrderGetCreateInfoRespVO> getOrderCreateInfo(AppTradeOrderCreateReqVO createReqVO) {
37 38 // return success(tradeOrderService.getOrderConfirmCreateInfo(UserSecurityContextHolder.getUserId(), skuId, quantity, couponCardId));
38 39 return null;
39 40 }
... ... @@ -41,8 +42,8 @@ public class AppTradeOrderController {
41 42 @PostMapping("/create")
42 43 @ApiOperation("创建订单")
43 44 @PreAuthenticated
44   - public CommonResult<Long> createTradeOrder(@RequestBody AppTradeOrderCreateReqVO createReqVO,
45   - HttpServletRequest servletRequest) {
  45 + public CommonResult<Long> createOrder(@RequestBody AppTradeOrderCreateReqVO createReqVO,
  46 + HttpServletRequest servletRequest) {
46 47 // 获取登录用户、用户 IP 地址
47 48 Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
48 49 String clientIp = ServletUtil.getClientIP(servletRequest);
... ... @@ -51,6 +52,12 @@ public class AppTradeOrderController {
51 52 return CommonResult.success(orderId);
52 53 }
53 54  
  55 + @PostMapping("/update-paid")
  56 + @ApiOperation(value = "更新订单为已支付", notes = "由 pay-module 支付服务,进行回调,可见 PayNotifyJob")
  57 + public CommonResult<Boolean> updateOrderPaid(@RequestBody PayOrderNotifyReqDTO notifyReqDTO) {
  58 + return null;
  59 + }
  60 +
54 61 @GetMapping("/get")
55 62 @ApiOperation("获得交易订单")
56 63 @ApiImplicitParam(name = "tradeOrderId", value = "交易订单编号", required = true, dataTypeClass = Long.class)
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/notify/vo/PayNotifyOrderReqVO.java renamed to yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/notify/dto/PayOrderNotifyReqDTO.java
1   -package cn.iocoder.yudao.module.pay.service.notify.vo;
  1 +package cn.iocoder.yudao.module.pay.api.notify.dto;
2 2  
3   -import io.swagger.annotations.ApiModel;
4   -import io.swagger.annotations.ApiModelProperty;
5 3 import lombok.AllArgsConstructor;
6 4 import lombok.Builder;
7 5 import lombok.Data;
... ... @@ -10,18 +8,26 @@ import lombok.NoArgsConstructor;
10 8 import javax.validation.constraints.NotEmpty;
11 9 import javax.validation.constraints.NotNull;
12 10  
13   -@ApiModel(value = "支付单的通知 Request VO", description = "业务方接入支付回调时,使用该 VO 对象")
  11 +/**
  12 + * 支付单的通知 Request DTO
  13 + *
  14 + * @author 芋道源码
  15 + */
14 16 @Data
15 17 @Builder
16 18 @NoArgsConstructor
17 19 @AllArgsConstructor
18   -public class PayNotifyOrderReqVO {
  20 +public class PayOrderNotifyReqDTO {
19 21  
20   - @ApiModelProperty(value = "商户订单编号", required = true, example = "10")
  22 + /**
  23 + * 商户订单编号
  24 + */
21 25 @NotEmpty(message = "商户订单号不能为空")
22 26 private String merchantOrderId;
23 27  
24   - @ApiModelProperty(value = "支付订单编号", required = true, example = "20")
  28 + /**
  29 + * 支付订单编号
  30 + */
25 31 @NotNull(message = "支付订单编号不能为空")
26 32 private Long payOrderId;
27 33  
... ...
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/notify/dto/PayRefundNotifyReqDTO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pay.api.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.NotEmpty;
  9 +import javax.validation.constraints.NotNull;
  10 +
  11 +/**
  12 + * 退款单的通知 Request DTO
  13 + *
  14 + * @author 芋道源码
  15 + */
  16 +@Data
  17 +@Builder
  18 +@NoArgsConstructor
  19 +@AllArgsConstructor
  20 +public class PayRefundNotifyReqDTO {
  21 +
  22 + /**
  23 + * 商户退款单编号
  24 + */
  25 + @NotEmpty(message = "商户退款单编号不能为空")
  26 + private String merchantOrderId;
  27 +
  28 + /**
  29 + * 支付退款编号
  30 + */
  31 + @NotNull(message = "支付退款编号不能为空")
  32 + private Long payRefundId;
  33 +
  34 + /**
  35 + * 退款状态
  36 + *
  37 + * (成功,失败) TODO 芋艿:枚举
  38 + */
  39 + @NotNull(message = "退款状态不能为空")
  40 + private Integer status;
  41 +
  42 +}
... ...
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/notify/package-info.java 0 → 100644
  1 +/**
  2 + * 占位符,无特殊作用
  3 + */
  4 +package cn.iocoder.yudao.module.pay.api.notify;
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/job/notify/PayNotifyJob.java
... ... @@ -15,8 +15,8 @@ import javax.annotation.Resource;
15 15 * @author 芋道源码
16 16 */
17 17 @Component
  18 +@TenantJob // 多租户
18 19 @Slf4j
19   -@TenantJob
20 20 public class PayNotifyJob implements JobHandler {
21 21  
22 22 @Resource
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/package-info.java
... ... @@ -2,8 +2,8 @@
2 2 * pay 模块,我们放支付业务,提供业务的支付能力。
3 3 * 例如说:商户、应用、支付、退款等等
4 4 *
5   - * 1. Controller URL:以 /member/ 开头,避免和其它 Module 冲突
6   - * 2. DataObject 表名:以 member_ 开头,方便在数据库中区分
  5 + * 1. Controller URL:以 /pay/ 开头,避免和其它 Module 冲突
  6 + * 2. DataObject 表名:以 pay_ 开头,方便在数据库中区分
7 7 *
8 8 * 注意,由于 Pay 模块和 Trade 模块,容易重名,所以类名都加载 Pay 的前缀~
9 9 */
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/notify/PayNotifyServiceImpl.java
... ... @@ -2,7 +2,13 @@ package cn.iocoder.yudao.module.pay.service.notify;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.exceptions.ExceptionUtil;
  5 +import cn.hutool.http.HttpResponse;
5 6 import cn.hutool.http.HttpUtil;
  7 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  8 +import cn.iocoder.yudao.framework.common.util.date.DateUtils;
  9 +import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
  10 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
  11 +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
6 12 import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyLogDO;
7 13 import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyTaskDO;
8 14 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
... ... @@ -13,11 +19,8 @@ import cn.iocoder.yudao.module.pay.dal.redis.notify.PayNotifyLockRedisDAO;
13 19 import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyStatusEnum;
14 20 import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum;
15 21 import cn.iocoder.yudao.module.pay.service.notify.dto.PayNotifyTaskCreateReqDTO;
16   -import cn.iocoder.yudao.module.pay.service.notify.vo.PayNotifyOrderReqVO;
17   -import cn.iocoder.yudao.module.pay.service.notify.vo.PayRefundOrderReqVO;
18   -import cn.iocoder.yudao.framework.common.pojo.CommonResult;
19   -import cn.iocoder.yudao.framework.common.util.date.DateUtils;
20   -import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
  22 +import cn.iocoder.yudao.module.pay.api.notify.dto.PayOrderNotifyReqDTO;
  23 +import cn.iocoder.yudao.module.pay.api.notify.dto.PayRefundNotifyReqDTO;
21 24 import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
22 25 import cn.iocoder.yudao.module.pay.service.refund.PayRefundService;
23 26 import lombok.extern.slf4j.Slf4j;
... ... @@ -30,7 +33,9 @@ import org.springframework.transaction.annotation.Transactional;
30 33 import javax.annotation.Resource;
31 34 import javax.validation.Valid;
32 35 import java.time.LocalDateTime;
  36 +import java.util.HashMap;
33 37 import java.util.List;
  38 +import java.util.Map;
34 39 import java.util.Objects;
35 40 import java.util.concurrent.CountDownLatch;
36 41 import java.util.concurrent.TimeUnit;
... ... @@ -164,8 +169,9 @@ public class PayNotifyServiceImpl implements PayNotifyService {
164 169 // 校验,当前任务是否已经被通知过
165 170 // 虽然已经通过分布式加锁,但是可能同时满足通知的条件,然后都去获得锁。此时,第一个执行完后,第二个还是能拿到锁,然后会再执行一次。
166 171 PayNotifyTaskDO dbTask = payNotifyTaskCoreMapper.selectById(task.getId());
167   - if (DateUtils.afterNow(dbTask.getNextNotifyTime())) {
168   - log.info("[executeNotify][dbTask({}) 任务被忽略,原因是未到达下次通知时间,可能是因为并发执行了]", JsonUtils.toJsonString(dbTask));
  172 + if (LocalDateTimeUtils.afterNow(dbTask.getNextNotifyTime())) {
  173 + log.info("[executeNotifySync][dbTask({}) 任务被忽略,原因是未到达下次通知时间,可能是因为并发执行了]",
  174 + JsonUtils.toJsonString(dbTask));
169 175 return;
170 176 }
171 177  
... ... @@ -185,11 +191,12 @@ public class PayNotifyServiceImpl implements PayNotifyService {
185 191 invokeException = e;
186 192 }
187 193  
188   - // 处理
189   - Integer newStatus = this.processNotifyResult(task, invokeResult, invokeException);
  194 + // 处理结果
  195 + Integer newStatus = processNotifyResult(task, invokeResult, invokeException);
190 196  
191 197 // 记录 PayNotifyLog 日志
192   - String response = invokeException != null ? ExceptionUtil.getRootCauseMessage(invokeException) : JsonUtils.toJsonString(invokeResult);
  198 + String response = invokeException != null ? ExceptionUtil.getRootCauseMessage(invokeException) :
  199 + JsonUtils.toJsonString(invokeResult);
193 200 payNotifyLogCoreMapper.insert(PayNotifyLogDO.builder().taskId(task.getId())
194 201 .notifyTimes(task.getNotifyTimes() + 1).status(newStatus).response(response).build());
195 202 }
... ... @@ -201,22 +208,28 @@ public class PayNotifyServiceImpl implements PayNotifyService {
201 208 * @return HTTP 响应
202 209 */
203 210 private CommonResult<?> executeNotifyInvoke(PayNotifyTaskDO task) {
204   - // 拼接参数
  211 + // 拼接 body 参数
205 212 Object request;
206 213 if (Objects.equals(task.getType(), PayNotifyTypeEnum.ORDER.getType())) {
207   - request = PayNotifyOrderReqVO.builder().merchantOrderId(task.getMerchantOrderId())
  214 + request = PayOrderNotifyReqDTO.builder().merchantOrderId(task.getMerchantOrderId())
208 215 .payOrderId(task.getDataId()).build();
209 216 } else if (Objects.equals(task.getType(), PayNotifyTypeEnum.REFUND.getType())) {
210   - request = PayRefundOrderReqVO.builder().merchantOrderId(task.getMerchantOrderId())
  217 + request = PayRefundNotifyReqDTO.builder().merchantOrderId(task.getMerchantOrderId())
211 218 .payRefundId(task.getDataId()).build();
212 219 } else {
213 220 throw new RuntimeException("未知的通知任务类型:" + JsonUtils.toJsonString(task));
214 221 }
215   - // 请求地址
216   - String response = HttpUtil.post(task.getNotifyUrl(), JsonUtils.toJsonString(request),
217   - (int) NOTIFY_TIMEOUT_MILLIS);
218   - // 解析结果
219   - return JsonUtils.parseObject(response, CommonResult.class);
  222 + // 拼接 header 参数
  223 + Map<String, String> headers = new HashMap<>();
  224 + TenantUtils.addTenantHeader(headers);
  225 +
  226 + // 发起请求
  227 + try (HttpResponse response = HttpUtil.createPost(task.getNotifyUrl())
  228 + .body(JsonUtils.toJsonString(request)).addHeaders(headers)
  229 + .timeout((int) NOTIFY_TIMEOUT_MILLIS).execute()) {
  230 + // 解析结果
  231 + return JsonUtils.parseObject(response.body(), CommonResult.class);
  232 + }
220 233 }
221 234  
222 235 /**
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/notify/vo/PayRefundOrderReqVO.java deleted
1   -package cn.iocoder.yudao.module.pay.service.notify.vo;
2   -
3   -import io.swagger.annotations.ApiModel;
4   -import io.swagger.annotations.ApiModelProperty;
5   -import lombok.AllArgsConstructor;
6   -import lombok.Builder;
7   -import lombok.Data;
8   -import lombok.NoArgsConstructor;
9   -
10   -import javax.validation.constraints.NotEmpty;
11   -import javax.validation.constraints.NotNull;
12   -
13   -@ApiModel(value = "退款单的通知 Request VO", description = "业务方接入退款回调时,使用该 VO 对象")
14   -@Data
15   -@Builder
16   -@NoArgsConstructor
17   -@AllArgsConstructor
18   -public class PayRefundOrderReqVO {
19   -
20   - @ApiModelProperty(value = "商户退款单编号", required = true, example = "10")
21   - @NotEmpty(message = "商户退款单编号不能为空")
22   - private String merchantOrderId;
23   -
24   - @ApiModelProperty(value = "支付退款编号", required = true, example = "20")
25   - @NotNull(message = "支付退款编号不能为空")
26   - private Long payRefundId;
27   -
28   - @ApiModelProperty(value = "退款状态(成功,失败)", required = true, example = "10")
29   - private Integer status;
30   -
31   -}
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/notify/vo/package-info.java deleted
1   -/**
2   - * 这里的 VO 包有点特殊,是提供给接入支付模块的业务,提供回调接口时,可以直接使用 VO
3   - *
4   - * 例如说,支付单的回调,使用 TODO 芋艿:想下怎么优化下
5   - */
6   -package cn.iocoder.yudao.module.pay.service.notify.vo;
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/package-info.java deleted
1   -package cn.iocoder.yudao.module.pay.service;
yudao-server/src/main/java/cn/iocoder/yudao/module/shop/controller/app/AppShopOrderController.java
1 1 package cn.iocoder.yudao.module.shop.controller.app;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4   -import cn.iocoder.yudao.module.pay.service.notify.vo.PayNotifyOrderReqVO;
5   -import cn.iocoder.yudao.module.pay.service.notify.vo.PayRefundOrderReqVO;
  4 +import cn.iocoder.yudao.module.pay.api.notify.dto.PayOrderNotifyReqDTO;
  5 +import cn.iocoder.yudao.module.pay.api.notify.dto.PayRefundNotifyReqDTO;
6 6 import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
7 7 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
8 8 import cn.iocoder.yudao.module.pay.util.PaySeqUtils;
... ... @@ -58,14 +58,14 @@ public class AppShopOrderController {
58 58  
59 59 @PostMapping("/pay-notify")
60 60 @ApiOperation("支付回调")
61   - public CommonResult<Boolean> payNotify(@RequestBody @Valid PayNotifyOrderReqVO reqVO) {
  61 + public CommonResult<Boolean> payNotify(@RequestBody @Valid PayOrderNotifyReqDTO reqVO) {
62 62 log.info("[payNotify][回调成功]");
63 63 return success(true);
64 64 }
65 65  
66 66 @PostMapping("/refund-notify")
67 67 @ApiOperation("退款回调")
68   - public CommonResult<Boolean> refundNotify(@RequestBody @Valid PayRefundOrderReqVO reqVO) {
  68 + public CommonResult<Boolean> refundNotify(@RequestBody @Valid PayRefundNotifyReqDTO reqVO) {
69 69 log.info("[refundNotify][回调成功]");
70 70 return success(true);
71 71 }
... ...