Commit 47ba5b7b445636e43c8810ff6ff7964d76644361

Authored by zhijiantianya@gmail.com
1 parent 35ceef37

by gateway: 支付渠道的代码优化

Showing 18 changed files with 360 additions and 418 deletions
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClientConfig.java
1 1 package cn.iocoder.yudao.framework.pay.core.client;
2 2  
  3 +import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
3 4 import com.fasterxml.jackson.annotation.JsonTypeInfo;
4 5  
5 6 import javax.validation.ConstraintViolation;
... ... @@ -20,23 +21,10 @@ import java.util.Set;
20 21 public interface PayClientConfig {
21 22  
22 23 /**
23   - * 配置验证参数是
24   - *
25   - * @param validator 校验对象
26   - * @return 配置好的验证参数
27   - */
28   - Set<ConstraintViolation<PayClientConfig>> verifyParam(Validator validator);
29   -
30   - // TODO @aquan:貌似抽象一个 validation group 就好了!
31   - /**
32 24 * 参数校验
33 25 *
34 26 * @param validator 校验对象
35 27 */
36   - default void validate(Validator validator) {
37   - Set<ConstraintViolation<PayClientConfig>> violations = verifyParam(validator);
38   - if (!violations.isEmpty()) {
39   - throw new ConstraintViolationException(violations);
40   - }
41   - }
  28 + void validate(Validator validator);
  29 +
42 30 }
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPayClientConfig.java
1 1 package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
2 2  
  3 +import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
3 4 import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
4 5 import lombok.Data;
5 6  
... ... @@ -100,9 +101,8 @@ public class AlipayPayClientConfig implements PayClientConfig {
100 101 }
101 102  
102 103 @Override
103   - public Set<ConstraintViolation<PayClientConfig>> verifyParam(Validator validator) {
104   - // TODO 芋艿:参数校验
105   - return validator.validate(this,
  104 + public void validate(Validator validator) {
  105 + ValidationUtils.validate(validator, this,
106 106 MODE_PUBLIC_KEY.equals(this.getMode()) ? ModePublicKey.class : ModeCertificate.class);
107 107 }
108 108  
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxPayClientConfig.java
1 1 package cn.iocoder.yudao.framework.pay.core.client.impl.weixin;
2 2  
3 3 import cn.hutool.core.io.IoUtil;
  4 +import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
4 5 import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
5 6 import lombok.Data;
6 7  
7   -import javax.validation.ConstraintViolation;
8 8 import javax.validation.Validator;
9 9 import javax.validation.constraints.NotBlank;
10 10 import java.io.FileInputStream;
11 11 import java.io.FileNotFoundException;
12   -import java.util.Set;
13 12  
14 13 /**
15 14 * 微信支付的 PayClientConfig 实现类
... ... @@ -100,8 +99,9 @@ public class WxPayClientConfig implements PayClientConfig {
100 99 }
101 100  
102 101 @Override
103   - public Set<ConstraintViolation<PayClientConfig>> verifyParam(Validator validator) {
104   - return validator.validate(this, this.getApiVersion().equals(API_VERSION_V2) ? V2.class : V3.class);
  102 + public void validate(Validator validator) {
  103 + ValidationUtils.validate(validator, this,
  104 + API_VERSION_V2.equals(this.getApiVersion()) ? V2.class : V3.class);
105 105 }
106 106  
107 107 public static void main(String[] args) throws FileNotFoundException {
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClientTest.java
... ... @@ -22,9 +22,11 @@ import static org.mockito.Mockito.when;
22 22  
23 23 public class AlipayQrPayClientTest extends BaseMockitoUnitTest {
24 24  
  25 + private static final String SERVER_URL_SANDBOX = "https://openapi.alipaydev.com/gateway.do";
  26 +
25 27 private final AlipayPayClientConfig config = new AlipayPayClientConfig()
26 28 .setAppId("2021000118634035")
27   - .setServerUrl(AlipayPayClientConfig.SERVER_URL_SANDBOX)
  29 + .setServerUrl(SERVER_URL_SANDBOX)
28 30 .setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT)
29 31 // TODO @tina:key 可以随机就好,简洁一点哈。
30 32 .setPrivateKey("MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCHsEV1cDupwJ" +
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/app/vo/PayAppBaseVO.java
1 1 package cn.iocoder.yudao.module.pay.controller.admin.app.vo;
  2 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  3 +import cn.iocoder.yudao.framework.common.validation.InEnum;
2 4 import io.swagger.v3.oas.annotations.media.Schema;
3 5 import lombok.*;
4 6 import org.hibernate.validator.constraints.URL;
... ... @@ -18,6 +20,7 @@ public class PayAppBaseVO {
18 20  
19 21 @Schema(description = "开启状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
20 22 @NotNull(message = "开启状态不能为空")
  23 + @InEnum(CommonStatusEnum.class)
21 24 private Integer status;
22 25  
23 26 @Schema(description = "备注", example = "我是一个测试应用")
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/channel/PayChannelController.java
1 1 package cn.iocoder.yudao.module.pay.controller.admin.channel;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4   -import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 4 import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelCreateReqVO;
6   -import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelPageReqVO;
7 5 import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelRespVO;
8 6 import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelUpdateReqVO;
9 7 import cn.iocoder.yudao.module.pay.convert.channel.PayChannelConvert;
... ... @@ -11,7 +9,6 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO;
11 9 import cn.iocoder.yudao.module.pay.service.channel.PayChannelService;
12 10 import io.swagger.v3.oas.annotations.Operation;
13 11 import io.swagger.v3.oas.annotations.Parameter;
14   -import io.swagger.v3.oas.annotations.Parameters;
15 12 import io.swagger.v3.oas.annotations.tags.Tag;
16 13 import org.springframework.security.access.prepost.PreAuthorize;
17 14 import org.springframework.validation.annotation.Validated;
... ... @@ -59,39 +56,19 @@ public class PayChannelController {
59 56 }
60 57  
61 58 @GetMapping("/get")
62   - @Operation(summary = "获得支付渠道 ")
  59 + @Operation(summary = "获得支付渠道")
63 60 @Parameter(name = "id", description = "编号", required = true, example = "1024")
64 61 @PreAuthorize("@ss.hasPermission('pay:channel:query')")
65   - public CommonResult<PayChannelRespVO> getChannel(@RequestParam("id") Long id) {
66   - PayChannelDO channel = channelService.getChannel(id);
67   - return success(PayChannelConvert.INSTANCE.convert(channel));
68   - }
69   -
70   - @GetMapping("/page")
71   - @Operation(summary = "获得支付渠道分页")
72   - @PreAuthorize("@ss.hasPermission('pay:channel:query')")
73   - public CommonResult<PageResult<PayChannelRespVO>> getChannelPage(@Valid PayChannelPageReqVO pageVO) {
74   - PageResult<PayChannelDO> pageResult = channelService.getChannelPage(pageVO);
75   - return success(PayChannelConvert.INSTANCE.convertPage(pageResult));
76   - }
77   -
78   - // TODO 芋艿:需要 review 下实现
79   - @GetMapping("/get-channel")
80   - @Operation(summary = "根据条件查询微信支付渠道")
81   - @Parameters({
82   - @Parameter(name = "appId", description = "应用编号", required = true, example = "1"),
83   - @Parameter(name = "code", description = "支付渠道编码", required = true, example = "wx_pub")
84   - })
85   - @PreAuthorize("@ss.hasPermission('pay:channel:query')")
86   - public CommonResult<PayChannelRespVO> getChannel(@RequestParam Long appId, @RequestParam String code) {
87   - // 獲取渠道
88   - PayChannelDO channel = channelService.getChannelByConditions(appId, code);
89   - if (channel == null) {
90   - return success(new PayChannelRespVO());
  62 + public CommonResult<PayChannelRespVO> getChannel(@RequestParam(value = "id", required = false) Long id,
  63 + @RequestParam(value = "appId", required = false) Long appId,
  64 + @RequestParam(value = "code", required = false) String code) {
  65 + PayChannelDO channel = null;
  66 + if (id != null) {
  67 + channel = channelService.getChannel(id);
  68 + } else if (appId != null && code != null) {
  69 + channel = channelService.getChannelByAppIdAndCode(appId, code);
91 70 }
92   - // 拼凑数据
93   - PayChannelRespVO respVo = PayChannelConvert.INSTANCE.convert(channel);
94   - return success(respVo);
  71 + return success(PayChannelConvert.INSTANCE.convert(channel));
95 72 }
96 73  
97 74 @GetMapping("/get-enable-code-list")
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/channel/vo/PayChannelBaseVO.java
1 1 package cn.iocoder.yudao.module.pay.controller.admin.channel.vo;
  2 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  3 +import cn.iocoder.yudao.framework.common.validation.InEnum;
2 4 import io.swagger.v3.oas.annotations.media.Schema;
3 5 import lombok.*;
4 6 import javax.validation.constraints.*;
... ... @@ -10,22 +12,19 @@ import javax.validation.constraints.*;
10 12 @Data
11 13 public class PayChannelBaseVO {
12 14  
13   - @Schema(description = "渠道编码", requiredMode = Schema.RequiredMode.REQUIRED)
14   - @NotNull(message = "渠道编码不能为空")
15   - private String code;
16   -
17   - @Schema(description = "开启状态", requiredMode = Schema.RequiredMode.REQUIRED)
  15 + @Schema(description = "开启状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
18 16 @NotNull(message = "开启状态不能为空")
  17 + @InEnum(CommonStatusEnum.class)
19 18 private Integer status;
20 19  
21   - @Schema(description = "备注")
  20 + @Schema(description = "备注", example = "我是小备注")
22 21 private String remark;
23 22  
24   - @Schema(description = "渠道费率,单位:百分比", requiredMode = Schema.RequiredMode.REQUIRED)
  23 + @Schema(description = "渠道费率,单位:百分比", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
25 24 @NotNull(message = "渠道费率,单位:百分比不能为空")
26 25 private Double feeRate;
27 26  
28   - @Schema(description = "应用编号", requiredMode = Schema.RequiredMode.REQUIRED)
  27 + @Schema(description = "应用编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
29 28 @NotNull(message = "应用编号不能为空")
30 29 private Long appId;
31 30  
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/channel/vo/PayChannelCreateReqVO.java
... ... @@ -6,6 +6,7 @@ import lombok.EqualsAndHashCode;
6 6 import lombok.ToString;
7 7  
8 8 import javax.validation.constraints.NotBlank;
  9 +import javax.validation.constraints.NotNull;
9 10  
10 11 @Schema(description = "管理后台 - 支付渠道 创建 Request VO")
11 12 @Data
... ... @@ -13,6 +14,10 @@ import javax.validation.constraints.NotBlank;
13 14 @ToString(callSuper = true)
14 15 public class PayChannelCreateReqVO extends PayChannelBaseVO {
15 16  
  17 + @Schema(description = "渠道编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "alipay_pc")
  18 + @NotNull(message = "渠道编码不能为空")
  19 + private String code;
  20 +
16 21 @Schema(description = "渠道配置的 json 字符串")
17 22 @NotBlank(message = "渠道配置不能为空")
18 23 private String config;
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/channel/vo/PayChannelPageReqVO.java deleted
1   -package cn.iocoder.yudao.module.pay.controller.admin.channel.vo;
2   -
3   -import cn.iocoder.yudao.framework.common.pojo.PageParam;
4   -import io.swagger.v3.oas.annotations.media.Schema;
5   -import lombok.Data;
6   -import lombok.EqualsAndHashCode;
7   -import lombok.ToString;
8   -import org.springframework.format.annotation.DateTimeFormat;
9   -
10   -import java.time.LocalDateTime;
11   -
12   -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
13   -
14   -@Schema(description = "管理后台 - 支付渠道 分页 Request VO")
15   -@Data
16   -@EqualsAndHashCode(callSuper = true)
17   -@ToString(callSuper = true)
18   -public class PayChannelPageReqVO extends PageParam {
19   -
20   - @Schema(description = "渠道编码")
21   - private String code;
22   -
23   - @Schema(description = "开启状态")
24   - private Integer status;
25   -
26   - @Schema(description = "备注")
27   - private String remark;
28   -
29   - @Schema(description = "渠道费率,单位:百分比")
30   - private Double feeRate;
31   -
32   - @Schema(description = "应用编号")
33   - private Long appId;
34   -
35   - @Schema(description = "支付渠道配置")
36   - private String config;
37   -
38   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
39   - @Schema(description = "创建时间")
40   - private LocalDateTime[] createTime;
41   -
42   -}
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/channel/vo/PayChannelRespVO.java
... ... @@ -10,12 +10,16 @@ import java.time.LocalDateTime;
10 10 @ToString(callSuper = true)
11 11 public class PayChannelRespVO extends PayChannelBaseVO {
12 12  
13   - @Schema(description = "商户编号", requiredMode = Schema.RequiredMode.REQUIRED)
  13 + @Schema(description = "商户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
14 14 private Long id;
15 15  
16   - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  16 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
17 17 private LocalDateTime createTime;
18 18  
  19 + @Schema(description = "渠道编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "alipay_pc")
  20 + private String code;
  21 +
19 22 @Schema(description = "配置", requiredMode = Schema.RequiredMode.REQUIRED)
20 23 private String config;
  24 +
21 25 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/channel/vo/PayChannelUpdateReqVO.java
... ... @@ -16,4 +16,5 @@ public class PayChannelUpdateReqVO extends PayChannelBaseVO {
16 16 @Schema(description = "渠道配置的json字符串")
17 17 @NotBlank(message = "渠道配置不能为空")
18 18 private String config;
  19 +
19 20 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/channel/PayChannelMapper.java
1 1 package cn.iocoder.yudao.module.pay.dal.mysql.channel;
2 2  
3   -import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 3 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 4 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6   -import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
7   -import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelPageReqVO;
8 5 import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO;
9 6 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
10 7 import org.apache.ibatis.annotations.Mapper;
  8 +import org.apache.ibatis.annotations.Select;
11 9  
  10 +import java.time.LocalDateTime;
12 11 import java.util.Collection;
13 12 import java.util.List;
14 13  
... ... @@ -19,41 +18,8 @@ public interface PayChannelMapper extends BaseMapperX&lt;PayChannelDO&gt; {
19 18 return selectOne(PayChannelDO::getAppId, appId, PayChannelDO::getCode, code);
20 19 }
21 20  
22   - default PageResult<PayChannelDO> selectPage(PayChannelPageReqVO reqVO) {
23   - return selectPage(reqVO, new QueryWrapperX<PayChannelDO>()
24   - .eqIfPresent("code", reqVO.getCode())
25   - .eqIfPresent("status", reqVO.getStatus())
26   - .eqIfPresent("remark", reqVO.getRemark())
27   - .eqIfPresent("fee_rate", reqVO.getFeeRate())
28   - .eqIfPresent("app_id", reqVO.getAppId())
29   - .betweenIfPresent("create_time", reqVO.getCreateTime())
30   - .orderByDesc("id"));
31   - }
32   -
33   - /**
34   - * 根据条件获取渠道
35   - *
36   - * @param appId 应用编号
37   - * @param code 渠道编码
38   - * @return 数量
39   - */
40   - default PayChannelDO selectOne(Long appId, String code) {
41   - return this.selectOne((new QueryWrapper<PayChannelDO>().lambda()
42   - .eq(PayChannelDO::getAppId, appId)
43   - .eq(PayChannelDO::getCode, code)
44   - ));
45   - }
46   -
47   - // TODO @aquan:select 命名
48   - /**
49   - * 根据支付应用ID集合获得支付渠道列表
50   - *
51   - * @param appIds 应用编号集合
52   - * @return 支付渠道列表
53   - */
54   - default List<PayChannelDO> getChannelListByAppIds(Collection<Long> appIds){
55   - return this.selectList(new QueryWrapper<PayChannelDO>().lambda()
56   - .in(PayChannelDO::getAppId, appIds));
  21 + default List<PayChannelDO> selectListByAppIds(Collection<Long> appIds){
  22 + return selectList(PayChannelDO::getAppId, appIds);
57 23 }
58 24  
59 25 default List<PayChannelDO> selectListByAppId(Long appId, Integer status) {
... ... @@ -62,4 +28,7 @@ public interface PayChannelMapper extends BaseMapperX&lt;PayChannelDO&gt; {
62 28 .eq(PayChannelDO::getStatus, status));
63 29 }
64 30  
  31 + @Select("SELECT COUNT(*) FROM pay_channel WHERE update_time > #{maxUpdateTime}")
  32 + Long selectCountByUpdateTimeGt(LocalDateTime maxUpdateTime);
  33 +
65 34 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/app/PayAppServiceImpl.java
... ... @@ -68,10 +68,7 @@ public class PayAppServiceImpl implements PayAppService {
68 68 // 校验商户存在
69 69 validateAppExists(id);
70 70 // 更新状态
71   - PayAppDO app = new PayAppDO();
72   - app.setId(id);
73   - app.setStatus(status);
74   - appMapper.updateById(app);
  71 + appMapper.updateById(new PayAppDO().setId(id).setStatus(status));
75 72 }
76 73  
77 74 @Override
... ... @@ -116,11 +113,11 @@ public class PayAppServiceImpl implements PayAppService {
116 113 PayAppDO app = appMapper.selectById(id);
117 114 // 校验是否存在
118 115 if (app == null) {
119   - throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_APP_NOT_FOUND);
  116 + throw exception(ErrorCodeConstants.PAY_APP_NOT_FOUND);
120 117 }
121 118 // 校验是否禁用
122 119 if (CommonStatusEnum.DISABLE.getStatus().equals(app.getStatus())) {
123   - throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_APP_IS_DISABLE);
  120 + throw exception(ErrorCodeConstants.PAY_APP_IS_DISABLE);
124 121 }
125 122 return app;
126 123 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelService.java
1 1 package cn.iocoder.yudao.module.pay.service.channel;
2 2  
3 3 import cn.iocoder.yudao.framework.common.exception.ServiceException;
4   -import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 4 import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelCreateReqVO;
6   -import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelPageReqVO;
7 5 import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelUpdateReqVO;
8 6 import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO;
9 7  
... ... @@ -19,11 +17,6 @@ import java.util.List;
19 17 public interface PayChannelService {
20 18  
21 19 /**
22   - * 初始化支付客户端
23   - */
24   - void initLocalCache();
25   -
26   - /**
27 20 * 创建支付渠道
28 21 *
29 22 * @param createReqVO 创建信息
... ... @@ -54,15 +47,7 @@ public interface PayChannelService {
54 47 PayChannelDO getChannel(Long id);
55 48  
56 49 /**
57   - * 获得支付渠道分页
58   - *
59   - * @param pageReqVO 分页查询
60   - * @return 支付渠道分页
61   - */
62   - PageResult<PayChannelDO> getChannelPage(PayChannelPageReqVO pageReqVO);
63   -
64   - /**
65   - * 根据支付应用ID集合获得支付渠道列表
  50 + * 根据支付应用 ID 集合,获得支付渠道列表
66 51 *
67 52 * @param appIds 应用编号集合
68 53 * @return 支付渠道列表
... ... @@ -72,11 +57,11 @@ public interface PayChannelService {
72 57 /**
73 58 * 根据条件获取渠道
74 59 *
75   - * @param appid 应用编号
  60 + * @param appId 应用编号
76 61 * @param code 渠道编码
77 62 * @return 数量
78 63 */
79   - PayChannelDO getChannelByConditions(Long appid, String code);
  64 + PayChannelDO getChannelByAppIdAndCode(Long appId, String code);
80 65  
81 66 /**
82 67 * 支付渠道的合法性
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceImpl.java
1 1 package cn.iocoder.yudao.module.pay.service.channel;
2 2  
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.lang.Assert;
3 5 import cn.hutool.core.util.ObjectUtil;
4   -import cn.hutool.json.JSONUtil;
5 6 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
6   -import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
7   -import cn.iocoder.yudao.framework.common.pojo.PageResult;
  7 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
  8 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
8 9 import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
9 10 import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
10 11 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
11 12 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
12 13 import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelCreateReqVO;
13   -import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelPageReqVO;
14 14 import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelUpdateReqVO;
15 15 import cn.iocoder.yudao.module.pay.convert.channel.PayChannelConvert;
16 16 import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO;
17 17 import cn.iocoder.yudao.module.pay.dal.mysql.channel.PayChannelMapper;
18 18 import cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants;
  19 +import lombok.Getter;
  20 +import lombok.Setter;
19 21 import lombok.extern.slf4j.Slf4j;
  22 +import org.springframework.scheduling.annotation.Scheduled;
20 23 import org.springframework.stereotype.Service;
21 24 import org.springframework.validation.annotation.Validated;
22 25  
23 26 import javax.annotation.PostConstruct;
24 27 import javax.annotation.Resource;
25 28 import javax.validation.Validator;
  29 +import java.time.LocalDateTime;
26 30 import java.util.Collection;
27 31 import java.util.List;
  32 +import java.util.concurrent.TimeUnit;
28 33  
29 34 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
30 35 import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.CHANNEL_EXIST_SAME_CHANNEL_ERROR;
... ... @@ -40,6 +45,10 @@ import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.CHANNEL_NOT_E
40 45 @Validated
41 46 public class PayChannelServiceImpl implements PayChannelService {
42 47  
  48 + @Getter // 为了方便测试,这里提供 getter 方法
  49 + @Setter
  50 + private volatile List<PayChannelDO> channelCache;
  51 +
43 52 @Resource
44 53 private PayClientFactory payClientFactory;
45 54  
... ... @@ -52,7 +61,6 @@ public class PayChannelServiceImpl implements PayChannelService {
52 61 /**
53 62 * 初始化 {@link #payClientFactory} 缓存
54 63 */
55   - @Override
56 64 @PostConstruct
57 65 public void initLocalCache() {
58 66 // 注意:忽略自动多租户,因为要全局初始化缓存
... ... @@ -64,49 +72,101 @@ public class PayChannelServiceImpl implements PayChannelService {
64 72 // 第二步:构建缓存:创建或更新支付 Client
65 73 channels.forEach(payChannel -> payClientFactory.createOrUpdatePayClient(payChannel.getId(),
66 74 payChannel.getCode(), payChannel.getConfig()));
  75 + this.channelCache = channels;
67 76 });
68 77 }
69 78  
  79 + /**
  80 + * 通过定时任务轮询,刷新缓存
  81 + *
  82 + * 目的:多节点部署时,通过轮询”通知“所有节点,进行刷新
  83 + */
  84 + @Scheduled(initialDelay = 60, fixedRate = 60, timeUnit = TimeUnit.SECONDS)
  85 + public void refreshLocalCache() {
  86 + // 情况一:如果缓存里没有数据,则直接刷新缓存
  87 + if (CollUtil.isEmpty(channelCache)) {
  88 + initLocalCache();
  89 + return;
  90 + }
  91 +
  92 + // 情况二,如果缓存里数据,则通过 updateTime 判断是否有数据变更,有变更则刷新缓存
  93 + LocalDateTime maxTime = CollectionUtils.getMaxValue(channelCache, PayChannelDO::getUpdateTime);
  94 + if (channelMapper.selectCountByUpdateTimeGt(maxTime) > 0) {
  95 + initLocalCache();
  96 + }
  97 + }
  98 +
70 99 @Override
71 100 public Long createChannel(PayChannelCreateReqVO reqVO) {
72 101 // 断言是否有重复的
73   - PayChannelDO channelDO = this.getChannelByConditions(reqVO.getAppId(), reqVO.getCode());
74   - if (ObjectUtil.isNotNull(channelDO)) {
  102 + PayChannelDO dbChannel = getChannelByAppIdAndCode(reqVO.getAppId(), reqVO.getCode());
  103 + if (dbChannel != null) {
75 104 throw exception(CHANNEL_EXIST_SAME_CHANNEL_ERROR);
76 105 }
77 106  
78 107 // 新增渠道
79   - PayChannelDO channel = PayChannelConvert.INSTANCE.convert(reqVO);
80   - settingConfigAndCheckParam(channel, reqVO.getConfig());
  108 + PayChannelDO channel = PayChannelConvert.INSTANCE.convert(reqVO)
  109 + .setConfig(parseConfig(reqVO.getCode(), reqVO.getConfig()));
81 110 channelMapper.insert(channel);
82   - // TODO 芋艿:缺少刷新本地缓存的机制
  111 +
  112 + // 刷新缓存
  113 + refreshLocalCache();
83 114 return channel.getId();
84 115 }
85 116  
86 117 @Override
87 118 public void updateChannel(PayChannelUpdateReqVO updateReqVO) {
88 119 // 校验存在
89   - this.validateChannelExists(updateReqVO.getId());
  120 + PayChannelDO dbChannel = validateChannelExists(updateReqVO.getId());
  121 +
90 122 // 更新
91   - PayChannelDO channel = PayChannelConvert.INSTANCE.convert(updateReqVO);
92   - settingConfigAndCheckParam(channel, updateReqVO.getConfig());
  123 + PayChannelDO channel = PayChannelConvert.INSTANCE.convert(updateReqVO)
  124 + .setConfig(parseConfig(dbChannel.getCode(), updateReqVO.getConfig()));
93 125 channelMapper.updateById(channel);
94   - // TODO 芋艿:缺少刷新本地缓存的机制
  126 +
  127 + // 刷新缓存
  128 + refreshLocalCache();
  129 + }
  130 +
  131 + /**
  132 + * 解析并校验配置
  133 + *
  134 + * @param code 渠道编码
  135 + * @param configStr 配置
  136 + * @return 支付配置
  137 + */
  138 + private PayClientConfig parseConfig(String code, String configStr) {
  139 + // 解析配置
  140 + Class<? extends PayClientConfig> payClass = PayChannelEnum.getByCode(code).getConfigClass();
  141 + if (ObjectUtil.isNull(payClass)) {
  142 + throw exception(CHANNEL_NOT_EXISTS);
  143 + }
  144 + PayClientConfig config = JsonUtils.parseObject2(configStr, payClass);
  145 + Assert.notNull(config);
  146 +
  147 + // 验证参数
  148 + config.validate(validator);
  149 + return config;
95 150 }
96 151  
97 152 @Override
98 153 public void deleteChannel(Long id) {
99 154 // 校验存在
100   - this.validateChannelExists(id);
  155 + validateChannelExists(id);
  156 +
101 157 // 删除
102 158 channelMapper.deleteById(id);
103   - // TODO 芋艿:缺少刷新本地缓存的机制
  159 +
  160 + // 刷新缓存
  161 + refreshLocalCache();
104 162 }
105 163  
106   - private void validateChannelExists(Long id) {
107   - if (channelMapper.selectById(id) == null) {
  164 + private PayChannelDO validateChannelExists(Long id) {
  165 + PayChannelDO channel = channelMapper.selectById(id);
  166 + if (channel == null) {
108 167 throw exception(CHANNEL_NOT_EXISTS);
109 168 }
  169 + return channel;
110 170 }
111 171  
112 172 @Override
... ... @@ -115,44 +175,19 @@ public class PayChannelServiceImpl implements PayChannelService {
115 175 }
116 176  
117 177 @Override
118   - public PageResult<PayChannelDO> getChannelPage(PayChannelPageReqVO pageReqVO) {
119   - return channelMapper.selectPage(pageReqVO);
120   - }
121   -
122   - @Override
123 178 public List<PayChannelDO> getChannelListByAppIds(Collection<Long> appIds) {
124   - return channelMapper.getChannelListByAppIds(appIds);
  179 + return channelMapper.selectListByAppIds(appIds);
125 180 }
126 181  
127 182 @Override
128   - public PayChannelDO getChannelByConditions(Long appid, String code) {
129   - return this.channelMapper.selectOne(appid, code);
130   - }
131   -
132   - /**
133   - * 设置渠道配置以及参数校验
134   - *
135   - * @param channel 渠道
136   - * @param configStr 配置
137   - */
138   - private void settingConfigAndCheckParam(PayChannelDO channel, String configStr) {
139   - // 得到这个渠道是微信的还是支付宝的
140   - Class<? extends PayClientConfig> payClass = PayChannelEnum.getByCode(channel.getCode()).getConfigClass();
141   - if (ObjectUtil.isNull(payClass)) {
142   - throw exception(CHANNEL_NOT_EXISTS);
143   - }
144   - // TODO @芋艿:不要使用 hutool 的 json 工具,用项目的
145   - PayClientConfig config = JSONUtil.toBean(configStr, payClass);
146   -
147   - // 验证参数
148   - config.validate(validator);
149   - channel.setConfig(config);
  183 + public PayChannelDO getChannelByAppIdAndCode(Long appId, String code) {
  184 + return channelMapper.selectByAppIdAndCode(appId, code);
150 185 }
151 186  
152 187 @Override
153 188 public PayChannelDO validPayChannel(Long id) {
154 189 PayChannelDO channel = channelMapper.selectById(id);
155   - this.validPayChannel(channel);
  190 + validPayChannel(channel);
156 191 return channel;
157 192 }
158 193  
... ... @@ -163,18 +198,18 @@ public class PayChannelServiceImpl implements PayChannelService {
163 198 return channel;
164 199 }
165 200  
166   - @Override
167   - public List<PayChannelDO> getEnableChannelList(Long appId) {
168   - return channelMapper.selectListByAppId(appId, CommonStatusEnum.ENABLE.getStatus());
169   - }
170   -
171 201 private void validPayChannel(PayChannelDO channel) {
172 202 if (channel == null) {
173   - throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_CHANNEL_NOT_FOUND);
  203 + throw exception(ErrorCodeConstants.PAY_CHANNEL_NOT_FOUND);
174 204 }
175 205 if (CommonStatusEnum.DISABLE.getStatus().equals(channel.getStatus())) {
176   - throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_CHANNEL_IS_DISABLE);
  206 + throw exception(ErrorCodeConstants.PAY_CHANNEL_IS_DISABLE);
177 207 }
178 208 }
179 209  
  210 + @Override
  211 + public List<PayChannelDO> getEnableChannelList(Long appId) {
  212 + return channelMapper.selectListByAppId(appId, CommonStatusEnum.ENABLE.getStatus());
  213 + }
  214 +
180 215 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/app/PayAppServiceTest.java
... ... @@ -19,6 +19,8 @@ import org.springframework.context.annotation.Import;
19 19  
20 20 import javax.annotation.Resource;
21 21 import java.time.LocalDateTime;
  22 +import java.util.Collections;
  23 +import java.util.Map;
22 24  
23 25 import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
24 26 import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;
... ... @@ -26,8 +28,11 @@ import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgn
26 28 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
27 29 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
28 30 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
29   -import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.PAY_APP_NOT_FOUND;
  31 +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
  32 +import static java.util.Collections.singleton;
30 33 import static org.junit.jupiter.api.Assertions.*;
  34 +import static org.mockito.ArgumentMatchers.eq;
  35 +import static org.mockito.Mockito.when;
31 36  
32 37 /**
33 38 * {@link PayAppServiceImpl} 的单元测试
... ... @@ -67,8 +72,7 @@ public class PayAppServiceTest extends BaseDbUnitTest {
67 72 @Test
68 73 public void testUpdateApp_success() {
69 74 // mock 数据
70   - PayAppDO dbApp = randomPojo(PayAppDO.class, o ->
71   - o.setStatus(CommonStatusEnum.DISABLE.getStatus()));
  75 + PayAppDO dbApp = randomPojo(PayAppDO.class);
72 76 appMapper.insert(dbApp);// @Sql: 先插入出一条存在的数据
73 77 // 准备参数
74 78 PayAppUpdateReqVO reqVO = randomPojo(PayAppUpdateReqVO.class, o -> {
... ... @@ -94,10 +98,26 @@ public class PayAppServiceTest extends BaseDbUnitTest {
94 98 }
95 99  
96 100 @Test
97   - public void testDeleteApp_success() {
  101 + public void testUpdateAppStatus() {
98 102 // mock 数据
99 103 PayAppDO dbApp = randomPojo(PayAppDO.class, o ->
100   - o.setStatus((RandomUtil.randomEle(CommonStatusEnum.values()).getStatus())));
  104 + o.setStatus(CommonStatusEnum.DISABLE.getStatus()));
  105 + appMapper.insert(dbApp);// @Sql: 先插入出一条存在的数据
  106 +
  107 + // 准备参数
  108 + Long id = dbApp.getId();
  109 + Integer status = CommonStatusEnum.ENABLE.getStatus();
  110 + // 调用
  111 + appService.updateAppStatus(id, status);
  112 + // 断言
  113 + PayAppDO app = appMapper.selectById(id); // 获取最新的
  114 + assertEquals(status, app.getStatus());
  115 + }
  116 +
  117 + @Test
  118 + public void testDeleteApp_success() {
  119 + // mock 数据
  120 + PayAppDO dbApp = randomPojo(PayAppDO.class);
101 121 appMapper.insert(dbApp);// @Sql: 先插入出一条存在的数据
102 122 // 准备参数
103 123 Long id = dbApp.getId();
... ... @@ -118,6 +138,65 @@ public class PayAppServiceTest extends BaseDbUnitTest {
118 138 }
119 139  
120 140 @Test
  141 + public void testDeleteApp_existOrder() {
  142 + // mock 数据
  143 + PayAppDO dbApp = randomPojo(PayAppDO.class);
  144 + appMapper.insert(dbApp);// @Sql: 先插入出一条存在的数据
  145 + // 准备参数
  146 + Long id = dbApp.getId();
  147 + // mock 订单有订单
  148 + when(orderService.getOrderCountByAppId(eq(id))).thenReturn(10L);
  149 +
  150 + // 调用, 并断言异常
  151 + assertServiceException(() -> appService.deleteApp(id), PAY_APP_EXIST_ORDER_CANT_DELETE);
  152 + }
  153 +
  154 + @Test
  155 + public void testDeleteApp_existRefund() {
  156 + // mock 数据
  157 + PayAppDO dbApp = randomPojo(PayAppDO.class);
  158 + appMapper.insert(dbApp);// @Sql: 先插入出一条存在的数据
  159 + // 准备参数
  160 + Long id = dbApp.getId();
  161 + // mock 订单有订单
  162 + when(refundService.getRefundCountByAppId(eq(id))).thenReturn(10L);
  163 +
  164 + // 调用, 并断言异常
  165 + assertServiceException(() -> appService.deleteApp(id), PAY_APP_EXIST_REFUND_CANT_DELETE);
  166 + }
  167 +
  168 + @Test
  169 + public void testApp() {
  170 + // mock 数据
  171 + PayAppDO dbApp = randomPojo(PayAppDO.class);
  172 + appMapper.insert(dbApp);// @Sql: 先插入出一条存在的数据
  173 + // 准备参数
  174 + Long id = dbApp.getId();
  175 +
  176 + // 调用
  177 + PayAppDO app = appService.getApp(id);
  178 + // 校验数据一致
  179 + assertPojoEquals(app, dbApp);
  180 + }
  181 +
  182 + @Test
  183 + public void testAppMap() {
  184 + // mock 数据
  185 + PayAppDO dbApp01 = randomPojo(PayAppDO.class);
  186 + appMapper.insert(dbApp01);// @Sql: 先插入出一条存在的数据
  187 + PayAppDO dbApp02 = randomPojo(PayAppDO.class);
  188 + appMapper.insert(dbApp02);// @Sql: 先插入出一条存在的数据
  189 + // 准备参数
  190 + Long id = dbApp01.getId();
  191 +
  192 + // 调用
  193 + Map<Long, PayAppDO> appMap = appService.getAppMap(singleton(id));
  194 + // 校验数据一致
  195 + assertEquals(1, appMap.size());
  196 + assertPojoEquals(dbApp01, appMap.get(id));
  197 + }
  198 +
  199 + @Test
121 200 public void testGetAppPage() {
122 201 // mock 数据
123 202 PayAppDO dbApp = randomPojo(PayAppDO.class, o -> { // 等会查询到
... ... @@ -147,4 +226,37 @@ public class PayAppServiceTest extends BaseDbUnitTest {
147 226 assertPojoEquals(dbApp, pageResult.getList().get(0));
148 227 }
149 228  
  229 + @Test
  230 + public void testValidPayApp_success() {
  231 + // mock 数据
  232 + PayAppDO dbApp = randomPojo(PayAppDO.class,
  233 + o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()));
  234 + appMapper.insert(dbApp);// @Sql: 先插入出一条存在的数据
  235 + // 准备参数
  236 + Long id = dbApp.getId();
  237 +
  238 + // 调用
  239 + PayAppDO app = appService.validPayApp(id);
  240 + // 校验数据一致
  241 + assertPojoEquals(app, dbApp);
  242 + }
  243 +
  244 + @Test
  245 + public void testValidPayApp_notFound() {
  246 + assertServiceException(() -> appService.validPayApp(randomLongId()), PAY_APP_NOT_FOUND);
  247 + }
  248 +
  249 + @Test
  250 + public void testValidPayApp_disable() {
  251 + // mock 数据
  252 + PayAppDO dbApp = randomPojo(PayAppDO.class,
  253 + o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()));
  254 + appMapper.insert(dbApp);// @Sql: 先插入出一条存在的数据
  255 + // 准备参数
  256 + Long id = dbApp.getId();
  257 +
  258 + // 调用,并断言异常
  259 + assertServiceException(() -> appService.validPayApp(id), PAY_APP_IS_DISABLE);
  260 + }
  261 +
150 262 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceTest.java
1 1 package cn.iocoder.yudao.module.pay.service.channel;
2 2  
3 3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4   -import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
5 5 import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
6 6 import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig;
7 7 import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WxPayClientConfig;
8 8 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
9 9 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
10 10 import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelCreateReqVO;
11   -import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelPageReqVO;
12 11 import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelUpdateReqVO;
13 12 import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO;
14 13 import cn.iocoder.yudao.module.pay.dal.mysql.channel.PayChannelMapper;
15 14 import com.alibaba.fastjson.JSON;
  15 +import org.junit.jupiter.api.BeforeEach;
16 16 import org.junit.jupiter.api.Test;
17 17 import org.springframework.boot.test.mock.mockito.MockBean;
18 18 import org.springframework.context.annotation.Import;
19 19  
20 20 import javax.annotation.Resource;
21 21 import javax.validation.Validator;
22   -import java.time.LocalDateTime;
23 22  
24   -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;
25   -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
  23 +import java.util.Collections;
  24 +import java.util.List;
  25 +
26 26 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
27 27 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
28   -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
29   -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
  28 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
  29 +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.CHANNEL_EXIST_SAME_CHANNEL_ERROR;
30 30 import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.CHANNEL_NOT_EXISTS;
31 31 import static org.junit.jupiter.api.Assertions.*;
32 32  
... ... @@ -46,109 +46,62 @@ public class PayChannelServiceTest extends BaseDbUnitTest {
46 46 @MockBean
47 47 private Validator validator;
48 48  
49   - @Test
50   - public void testCreateWechatVersion2Channel_success() {
51   - // 准备参数
52   - WxPayClientConfig v2Config = getV2Config();
53   - PayChannelCreateReqVO reqVO = randomPojo(PayChannelCreateReqVO.class, o -> {
54   - o.setCode(PayChannelEnum.WX_PUB.getCode());
55   - o.setStatus(CommonStatusEnum.ENABLE.getStatus());
56   - o.setConfig(JSON.toJSONString(v2Config));
57   - });
58   -
59   - // 调用
60   - Long channelId = channelService.createChannel(reqVO);
61   - // 断言
62   - assertNotNull(channelId);
63   - // 校验记录的属性是否正确
64   - PayChannelDO channel = channelMapper.selectById(channelId);
65   - assertPojoEquals(reqVO, channel, "config");
66   - // 关于config 对象应该拿出来重新对比
67   - assertPojoEquals(v2Config, channel.getConfig());
  49 + @BeforeEach
  50 + public void setUp() {
  51 + channelService.setChannelCache(null);
68 52 }
69 53  
70 54 @Test
71   - public void testCreateWechatVersion3Channel_success() {
  55 + public void testCreateChannel_success() {
72 56 // 准备参数
73   - WxPayClientConfig v3Config = getV3Config();
  57 + WxPayClientConfig config = randomWxPayClientConfig();
74 58 PayChannelCreateReqVO reqVO = randomPojo(PayChannelCreateReqVO.class, o -> {
  59 + o.setStatus(randomCommonStatus());
75 60 o.setCode(PayChannelEnum.WX_PUB.getCode());
76   - o.setStatus(CommonStatusEnum.ENABLE.getStatus());
77   - o.setConfig(JSON.toJSONString(v3Config));
78   - });
79   -
80   - // 调用
81   - Long channelId = channelService.createChannel(reqVO);
82   - // 断言
83   - assertNotNull(channelId);
84   - // 校验记录的属性是否正确
85   - PayChannelDO channel = channelMapper.selectById(channelId);
86   - assertPojoEquals(reqVO, channel, "config");
87   - // 关于config 对象应该拿出来重新对比
88   - assertPojoEquals(v3Config, channel.getConfig());
89   - }
90   -
91   - @Test
92   - public void testCreateAliPayPublicKeyChannel_success() {
93   - // 准备参数
94   -
95   - AlipayPayClientConfig payClientConfig = getPublicKeyConfig();
96   - PayChannelCreateReqVO reqVO = randomPojo(PayChannelCreateReqVO.class, o -> {
97   - o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
98   - o.setStatus(CommonStatusEnum.ENABLE.getStatus());
99   - o.setConfig(JSON.toJSONString(payClientConfig));
  61 + o.setConfig(JsonUtils.toJsonString(config));
100 62 });
101 63  
102 64 // 调用
103 65 Long channelId = channelService.createChannel(reqVO);
104   - // 断言
105   - assertNotNull(channelId);
106 66 // 校验记录的属性是否正确
107 67 PayChannelDO channel = channelMapper.selectById(channelId);
108 68 assertPojoEquals(reqVO, channel, "config");
109   - // 关于config 对象应该拿出来重新对比
110   - assertPojoEquals(payClientConfig, channel.getConfig());
  69 + assertPojoEquals(config, channel.getConfig());
  70 + // 校验缓存
  71 + assertEquals(1, channelService.getChannelCache().size());
  72 + assertEquals(channel, channelService.getChannelCache().get(0));
111 73 }
112 74  
113 75 @Test
114   - public void testCreateAliPayCertificateChannel_success() {
  76 + public void testCreateChannel_exists() {
  77 + // mock 数据
  78 + PayChannelDO dbChannel = randomPojo(PayChannelDO.class,
  79 + o -> o.setConfig(randomWxPayClientConfig()));
  80 + channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据
115 81 // 准备参数
116   -
117   - AlipayPayClientConfig payClientConfig = getCertificateConfig();
118 82 PayChannelCreateReqVO reqVO = randomPojo(PayChannelCreateReqVO.class, o -> {
119   - o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
120   - o.setStatus(CommonStatusEnum.ENABLE.getStatus());
121   - o.setConfig(JSON.toJSONString(payClientConfig));
  83 + o.setAppId(dbChannel.getAppId());
  84 + o.setCode(dbChannel.getCode());
122 85 });
123 86  
124   - // 调用
125   - Long channelId = channelService.createChannel(reqVO);
126   - // 断言
127   - assertNotNull(channelId);
128   - // 校验记录的属性是否正确
129   - PayChannelDO channel = channelMapper.selectById(channelId);
130   - assertPojoEquals(reqVO, channel, "config");
131   - // 关于config 对象应该拿出来重新对比
132   - assertPojoEquals(payClientConfig, channel.getConfig());
  87 + // 调用, 并断言异常
  88 + assertServiceException(() -> channelService.createChannel(reqVO), CHANNEL_EXIST_SAME_CHANNEL_ERROR);
133 89 }
134 90  
135 91 @Test
136 92 public void testUpdateChannel_success() {
137 93 // mock 数据
138   - AlipayPayClientConfig payClientConfig = getCertificateConfig();
139 94 PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> {
140 95 o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
141   - o.setStatus(CommonStatusEnum.ENABLE.getStatus());
142   - o.setConfig(payClientConfig);
  96 + o.setConfig(randomAlipayPayClientConfig());
143 97 });
144 98 channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据
145 99 // 准备参数
146   - AlipayPayClientConfig payClientPublicKeyConfig = getPublicKeyConfig();
  100 + AlipayPayClientConfig config = randomAlipayPayClientConfig();
147 101 PayChannelUpdateReqVO reqVO = randomPojo(PayChannelUpdateReqVO.class, o -> {
148   - o.setCode(dbChannel.getCode());
149   - o.setStatus(dbChannel.getStatus());
150   - o.setConfig(JSON.toJSONString(payClientPublicKeyConfig));
151 102 o.setId(dbChannel.getId()); // 设置更新的 ID
  103 + o.setStatus(randomCommonStatus());
  104 + o.setConfig(JsonUtils.toJsonString(config));
152 105 });
153 106  
154 107 // 调用
... ... @@ -156,15 +109,17 @@ public class PayChannelServiceTest extends BaseDbUnitTest {
156 109 // 校验是否更新正确
157 110 PayChannelDO channel = channelMapper.selectById(reqVO.getId()); // 获取最新的
158 111 assertPojoEquals(reqVO, channel, "config");
159   - assertPojoEquals(payClientPublicKeyConfig, channel.getConfig());
  112 + assertPojoEquals(config, channel.getConfig());
  113 + // 校验缓存
  114 + assertEquals(1, channelService.getChannelCache().size());
  115 + assertEquals(channel, channelService.getChannelCache().get(0));
160 116 }
161 117  
162 118 @Test
163 119 public void testUpdateChannel_notExists() {
164 120 // 准备参数
165   - AlipayPayClientConfig payClientPublicKeyConfig = getPublicKeyConfig();
  121 + AlipayPayClientConfig payClientPublicKeyConfig = randomAlipayPayClientConfig();
166 122 PayChannelUpdateReqVO reqVO = randomPojo(PayChannelUpdateReqVO.class, o -> {
167   - o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
168 123 o.setStatus(CommonStatusEnum.ENABLE.getStatus());
169 124 o.setConfig(JSON.toJSONString(payClientPublicKeyConfig));
170 125 });
... ... @@ -176,11 +131,9 @@ public class PayChannelServiceTest extends BaseDbUnitTest {
176 131 @Test
177 132 public void testDeleteChannel_success() {
178 133 // mock 数据
179   - AlipayPayClientConfig payClientConfig = getCertificateConfig();
180 134 PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> {
181 135 o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
182   - o.setStatus(CommonStatusEnum.ENABLE.getStatus());
183   - o.setConfig(payClientConfig);
  136 + o.setConfig(randomAlipayPayClientConfig());
184 137 });
185 138 channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据
186 139 // 准备参数
... ... @@ -190,6 +143,8 @@ public class PayChannelServiceTest extends BaseDbUnitTest {
190 143 channelService.deleteChannel(id);
191 144 // 校验数据不存在了
192 145 assertNull(channelMapper.selectById(id));
  146 + // 校验缓存
  147 + assertEquals(0, channelService.getChannelCache().size());
193 148 }
194 149  
195 150 @Test
... ... @@ -201,119 +156,80 @@ public class PayChannelServiceTest extends BaseDbUnitTest {
201 156 assertServiceException(() -> channelService.deleteChannel(id), CHANNEL_NOT_EXISTS);
202 157 }
203 158  
204   - @Test // TODO 请修改 null 为需要的值
205   - public void testGetChannelPage() {
  159 + @Test
  160 + public void testGetChannel() {
206 161 // mock 数据
207   - AlipayPayClientConfig payClientConfig = getCertificateConfig();
208   - PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> { // 等会查询到
  162 + PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> {
209 163 o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
210   - o.setStatus(CommonStatusEnum.ENABLE.getStatus());
211   - o.setRemark("灿灿子的支付渠道");
212   - o.setFeeRate(0.03);
213   - o.setAppId(1L);
214   - o.setConfig(payClientConfig);
215   - o.setCreateTime(buildTime(2021,11,20));
  164 + o.setConfig(randomAlipayPayClientConfig());
216 165 });
217   - channelMapper.insert(dbChannel);
218   - // 执行拷贝的时候会出现异常,所以在插入后要重置为null 后续在写入新的
219   - dbChannel.setConfig(null);
220   - // 测试 code 不匹配
221   - channelMapper.insert(cloneIgnoreId(dbChannel, o -> {
222   - o.setConfig(payClientConfig);
223   - o.setCode(PayChannelEnum.WX_PUB.getCode());
224   - }));
225   - // 测试 status 不匹配
226   - channelMapper.insert(cloneIgnoreId(dbChannel, o -> {
227   - o.setConfig(payClientConfig);
228   - o.setStatus(CommonStatusEnum.DISABLE.getStatus());
229   - }));
230   - // 测试 remark 不匹配
231   - channelMapper.insert(cloneIgnoreId(dbChannel, o ->{
232   - o.setConfig(payClientConfig);
233   - o.setRemark("敏敏子的渠道");
234   - }));
235   - // 测试 feeRate 不匹配
236   - channelMapper.insert(cloneIgnoreId(dbChannel, o -> {
237   - o.setConfig(payClientConfig);
238   - o.setFeeRate(1.23);
239   - }));
240   - // 测试 appId 不匹配
241   - channelMapper.insert(cloneIgnoreId(dbChannel, o -> {
242   - o.setConfig(payClientConfig);
243   - o.setAppId(2L);
244   - }));
245   - // 测试 createTime 不匹配
246   - channelMapper.insert(cloneIgnoreId(dbChannel, o -> {
247   - o.setConfig(payClientConfig);
248   - o.setCreateTime(buildTime(2021, 10, 20));
249   - }));
  166 + channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据
250 167 // 准备参数
251   - PayChannelPageReqVO reqVO = new PayChannelPageReqVO();
252   - reqVO.setCode(PayChannelEnum.ALIPAY_APP.getCode());
253   - reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
254   - reqVO.setRemark("灿灿子的支付渠道");
255   - reqVO.setFeeRate(0.03);
256   - reqVO.setAppId(1L);
257   - reqVO.setConfig(JSON.toJSONString(payClientConfig));
258   - reqVO.setCreateTime((new LocalDateTime[]{buildTime(2021,11,19),buildTime(2021,11,21)}));
  168 + Long id = dbChannel.getId();
259 169  
260 170 // 调用
261   - PageResult<PayChannelDO> pageResult = channelService.getChannelPage(reqVO);
262   - // 断言
263   - assertEquals(1, pageResult.getTotal());
264   - assertEquals(1, pageResult.getList().size());
265   - assertPojoEquals(dbChannel, pageResult.getList().get(0), "config");
266   - assertPojoEquals(payClientConfig, pageResult.getList().get(0).getConfig());
267   -
  171 + PayChannelDO channel = channelService.getChannel(id);
  172 + // 校验是否更新正确
  173 + assertPojoEquals(dbChannel, channel);
268 174 }
269 175  
270   - public WxPayClientConfig getV2Config() {
271   - return new WxPayClientConfig()
272   - .setAppId("APP00001")
273   - .setMchId("MCH00001")
274   - .setApiVersion(WxPayClientConfig.API_VERSION_V2)
275   - .setMchKey("dsa1d5s6a1d6sa16d1sa56d15a61das6")
276   - .setApiV3Key("")
277   - .setPrivateCertContent("")
278   - .setPrivateKeyContent("");
  176 + @Test
  177 + public void testGetChannelListByAppIds() {
  178 + // mock 数据
  179 + PayChannelDO dbChannel01 = randomPojo(PayChannelDO.class, o -> {
  180 + o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
  181 + o.setConfig(randomAlipayPayClientConfig());
  182 + });
  183 + channelMapper.insert(dbChannel01);// @Sql: 先插入出一条存在的数据
  184 + PayChannelDO dbChannel02 = randomPojo(PayChannelDO.class, o -> {
  185 + o.setCode(PayChannelEnum.WX_PUB.getCode());
  186 + o.setConfig(randomWxPayClientConfig());
  187 + });
  188 + channelMapper.insert(dbChannel02);// @Sql: 先插入出一条存在的数据
  189 + // 准备参数
  190 + Long appId = dbChannel01.getAppId();
  191 +
  192 + // 调用
  193 + List<PayChannelDO> channels = channelService.getChannelListByAppIds(Collections.singleton(appId));
  194 + // 校验是否更新正确
  195 + assertEquals(1, channels.size());
  196 + assertPojoEquals(dbChannel01, channels.get(0));
279 197 }
280 198  
281   - public WxPayClientConfig getV3Config() {
282   - return new WxPayClientConfig()
283   - .setAppId("APP00001")
284   - .setMchId("MCH00001")
285   - .setApiVersion(WxPayClientConfig.API_VERSION_V3)
286   - .setMchKey("")
287   - .setApiV3Key("sdadasdsadadsa")
288   - .setPrivateKeyContent("dsa445das415d15asd16ad156as")
289   - .setPrivateCertContent("dsadasd45asd4s5a");
  199 + @Test
  200 + public void testGetChannelByAppIdAndCode() {
  201 + // mock 数据
  202 + PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> {
  203 + o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
  204 + o.setConfig(randomAlipayPayClientConfig());
  205 + });
  206 + channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据
  207 + // 准备参数
  208 + Long appId = dbChannel.getAppId();
  209 + String code = dbChannel.getCode();;
290 210  
  211 + // 调用
  212 + PayChannelDO channel = channelService.getChannelByAppIdAndCode(appId, code);
  213 + // 断言
  214 + assertPojoEquals(channel, dbChannel);
291 215 }
292 216  
293   - public AlipayPayClientConfig getPublicKeyConfig() {
294   - return new AlipayPayClientConfig()
295   - .setServerUrl(ALIPAY_SERVER_URL)
296   - .setAppId("APP00001")
297   - .setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT)
298   - .setMode(AlipayPayClientConfig.MODE_PUBLIC_KEY)
299   - .setPrivateKey("13131321312")
300   - .setAlipayPublicKey("13321321321")
301   - .setAppCertContent("")
302   - .setAlipayPublicCertContent("")
303   - .setRootCertContent("");
  217 + public WxPayClientConfig randomWxPayClientConfig() {
  218 + return new WxPayClientConfig()
  219 + .setAppId(randomString())
  220 + .setMchId(randomString())
  221 + .setApiVersion(WxPayClientConfig.API_VERSION_V2)
  222 + .setMchKey(randomString());
304 223 }
305 224  
306   - public AlipayPayClientConfig getCertificateConfig() {
  225 + public AlipayPayClientConfig randomAlipayPayClientConfig() {
307 226 return new AlipayPayClientConfig()
308   - .setServerUrl(ALIPAY_SERVER_URL)
309   - .setAppId("APP00001")
  227 + .setServerUrl(randomURL())
  228 + .setAppId(randomString())
310 229 .setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT)
311   - .setMode(AlipayPayClientConfig.MODE_CERTIFICATE)
312   - .setPrivateKey("")
313   - .setAlipayPublicKey("")
314   - .setAppCertContent("13321321321sda")
315   - .setAlipayPublicCertContent("13321321321aqeqw")
316   - .setRootCertContent("13321321321dsad");
  230 + .setMode(AlipayPayClientConfig.MODE_PUBLIC_KEY)
  231 + .setPrivateKey(randomString())
  232 + .setAlipayPublicKey(randomString());
317 233 }
318 234  
319 235 }
... ...
yudao-ui-admin/src/api/pay/channel.js
... ... @@ -27,15 +27,6 @@ export function deleteChannel(id) {
27 27 })
28 28 }
29 29  
30   -// 获得支付渠道分页
31   -export function getChannelPage(query) {
32   - return request({
33   - url: '/pay/channel/page',
34   - method: 'get',
35   - params: query
36   - })
37   -}
38   -
39 30 // 获得支付渠道
40 31 export function getChannel(appId,code) {
41 32 return request({
... ...