From 47ba5b7b445636e43c8810ff6ff7964d76644361 Mon Sep 17 00:00:00 2001 From: "zhijiantianya@gmail.com" Date: Wed, 12 Jul 2023 20:34:43 +0800 Subject: [PATCH] =?UTF-8?q?by=20gateway:=20=E6=94=AF=E4=BB=98=E6=B8=A0?= =?UTF-8?q?=E9=81=93=E7=9A=84=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pay/core/client/PayClientConfig.java | 18 +- .../impl/alipay/AlipayPayClientConfig.java | 6 +- .../client/impl/weixin/WxPayClientConfig.java | 8 +- .../impl/alipay/AlipayQrPayClientTest.java | 4 +- .../controller/admin/app/vo/PayAppBaseVO.java | 3 + .../admin/channel/PayChannelController.java | 43 +-- .../admin/channel/vo/PayChannelBaseVO.java | 15 +- .../channel/vo/PayChannelCreateReqVO.java | 5 + .../admin/channel/vo/PayChannelPageReqVO.java | 42 --- .../admin/channel/vo/PayChannelRespVO.java | 8 +- .../channel/vo/PayChannelUpdateReqVO.java | 1 + .../dal/mysql/channel/PayChannelMapper.java | 45 +-- .../pay/service/app/PayAppServiceImpl.java | 9 +- .../service/channel/PayChannelService.java | 21 +- .../channel/PayChannelServiceImpl.java | 147 +++++---- .../pay/service/app/PayAppServiceTest.java | 122 +++++++- .../channel/PayChannelServiceTest.java | 282 ++++++------------ yudao-ui-admin/src/api/pay/channel.js | 9 - 18 files changed, 365 insertions(+), 423 deletions(-) delete mode 100644 yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/channel/vo/PayChannelPageReqVO.java diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClientConfig.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClientConfig.java index bf709e497..5a3843034 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClientConfig.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClientConfig.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.framework.pay.core.client; +import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; import com.fasterxml.jackson.annotation.JsonTypeInfo; import javax.validation.ConstraintViolation; @@ -19,24 +20,11 @@ import java.util.Set; // 2. 反序列化到内存对象时,通过 @class 属性,可以创建出正确的类型 public interface PayClientConfig { - /** - * 配置验证参数是 - * - * @param validator 校验对象 - * @return 配置好的验证参数 - */ - Set> verifyParam(Validator validator); - - // TODO @aquan:貌似抽象一个 validation group 就好了! /** * 参数校验 * * @param validator 校验对象 */ - default void validate(Validator validator) { - Set> violations = verifyParam(validator); - if (!violations.isEmpty()) { - throw new ConstraintViolationException(violations); - } - } + void validate(Validator validator); + } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPayClientConfig.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPayClientConfig.java index 5b3b69b9f..066ff0122 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPayClientConfig.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPayClientConfig.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay; +import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig; import lombok.Data; @@ -100,9 +101,8 @@ public class AlipayPayClientConfig implements PayClientConfig { } @Override - public Set> verifyParam(Validator validator) { - // TODO 芋艿:参数校验 - return validator.validate(this, + public void validate(Validator validator) { + ValidationUtils.validate(validator, this, MODE_PUBLIC_KEY.equals(this.getMode()) ? ModePublicKey.class : ModeCertificate.class); } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxPayClientConfig.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxPayClientConfig.java index e109a6cf7..d5740bf76 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxPayClientConfig.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxPayClientConfig.java @@ -1,15 +1,14 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.weixin; import cn.hutool.core.io.IoUtil; +import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig; import lombok.Data; -import javax.validation.ConstraintViolation; import javax.validation.Validator; import javax.validation.constraints.NotBlank; import java.io.FileInputStream; import java.io.FileNotFoundException; -import java.util.Set; /** * 微信支付的 PayClientConfig 实现类 @@ -100,8 +99,9 @@ public class WxPayClientConfig implements PayClientConfig { } @Override - public Set> verifyParam(Validator validator) { - return validator.validate(this, this.getApiVersion().equals(API_VERSION_V2) ? V2.class : V3.class); + public void validate(Validator validator) { + ValidationUtils.validate(validator, this, + API_VERSION_V2.equals(this.getApiVersion()) ? V2.class : V3.class); } public static void main(String[] args) throws FileNotFoundException { diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClientTest.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClientTest.java index 78939aa83..45cabc7fd 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClientTest.java +++ b/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; public class AlipayQrPayClientTest extends BaseMockitoUnitTest { + private static final String SERVER_URL_SANDBOX = "https://openapi.alipaydev.com/gateway.do"; + private final AlipayPayClientConfig config = new AlipayPayClientConfig() .setAppId("2021000118634035") - .setServerUrl(AlipayPayClientConfig.SERVER_URL_SANDBOX) + .setServerUrl(SERVER_URL_SANDBOX) .setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT) // TODO @tina:key 可以随机就好,简洁一点哈。 .setPrivateKey("MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCHsEV1cDupwJ" + diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/app/vo/PayAppBaseVO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/app/vo/PayAppBaseVO.java index d2ff4f321..bf366119c 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/app/vo/PayAppBaseVO.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/app/vo/PayAppBaseVO.java @@ -1,4 +1,6 @@ package cn.iocoder.yudao.module.pay.controller.admin.app.vo; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; import io.swagger.v3.oas.annotations.media.Schema; import lombok.*; import org.hibernate.validator.constraints.URL; @@ -18,6 +20,7 @@ public class PayAppBaseVO { @Schema(description = "开启状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") @NotNull(message = "开启状态不能为空") + @InEnum(CommonStatusEnum.class) private Integer status; @Schema(description = "备注", example = "我是一个测试应用") diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/channel/PayChannelController.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/channel/PayChannelController.java index cec33bf1a..37927a799 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/channel/PayChannelController.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/channel/PayChannelController.java @@ -1,9 +1,7 @@ package cn.iocoder.yudao.module.pay.controller.admin.channel; import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelCreateReqVO; -import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelPageReqVO; import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelRespVO; import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelUpdateReqVO; import cn.iocoder.yudao.module.pay.convert.channel.PayChannelConvert; @@ -11,7 +9,6 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO; import cn.iocoder.yudao.module.pay.service.channel.PayChannelService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; @@ -59,39 +56,19 @@ public class PayChannelController { } @GetMapping("/get") - @Operation(summary = "获得支付渠道 ") + @Operation(summary = "获得支付渠道") @Parameter(name = "id", description = "编号", required = true, example = "1024") @PreAuthorize("@ss.hasPermission('pay:channel:query')") - public CommonResult getChannel(@RequestParam("id") Long id) { - PayChannelDO channel = channelService.getChannel(id); - return success(PayChannelConvert.INSTANCE.convert(channel)); - } - - @GetMapping("/page") - @Operation(summary = "获得支付渠道分页") - @PreAuthorize("@ss.hasPermission('pay:channel:query')") - public CommonResult> getChannelPage(@Valid PayChannelPageReqVO pageVO) { - PageResult pageResult = channelService.getChannelPage(pageVO); - return success(PayChannelConvert.INSTANCE.convertPage(pageResult)); - } - - // TODO 芋艿:需要 review 下实现 - @GetMapping("/get-channel") - @Operation(summary = "根据条件查询微信支付渠道") - @Parameters({ - @Parameter(name = "appId", description = "应用编号", required = true, example = "1"), - @Parameter(name = "code", description = "支付渠道编码", required = true, example = "wx_pub") - }) - @PreAuthorize("@ss.hasPermission('pay:channel:query')") - public CommonResult getChannel(@RequestParam Long appId, @RequestParam String code) { - // 獲取渠道 - PayChannelDO channel = channelService.getChannelByConditions(appId, code); - if (channel == null) { - return success(new PayChannelRespVO()); + public CommonResult getChannel(@RequestParam(value = "id", required = false) Long id, + @RequestParam(value = "appId", required = false) Long appId, + @RequestParam(value = "code", required = false) String code) { + PayChannelDO channel = null; + if (id != null) { + channel = channelService.getChannel(id); + } else if (appId != null && code != null) { + channel = channelService.getChannelByAppIdAndCode(appId, code); } - // 拼凑数据 - PayChannelRespVO respVo = PayChannelConvert.INSTANCE.convert(channel); - return success(respVo); + return success(PayChannelConvert.INSTANCE.convert(channel)); } @GetMapping("/get-enable-code-list") diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/channel/vo/PayChannelBaseVO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/channel/vo/PayChannelBaseVO.java index 48e98a3be..1416c9790 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/channel/vo/PayChannelBaseVO.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/channel/vo/PayChannelBaseVO.java @@ -1,4 +1,6 @@ package cn.iocoder.yudao.module.pay.controller.admin.channel.vo; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; import io.swagger.v3.oas.annotations.media.Schema; import lombok.*; import javax.validation.constraints.*; @@ -10,22 +12,19 @@ import javax.validation.constraints.*; @Data public class PayChannelBaseVO { - @Schema(description = "渠道编码", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "渠道编码不能为空") - private String code; - - @Schema(description = "开启状态", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "开启状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @NotNull(message = "开启状态不能为空") + @InEnum(CommonStatusEnum.class) private Integer status; - @Schema(description = "备注") + @Schema(description = "备注", example = "我是小备注") private String remark; - @Schema(description = "渠道费率,单位:百分比", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "渠道费率,单位:百分比", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") @NotNull(message = "渠道费率,单位:百分比不能为空") private Double feeRate; - @Schema(description = "应用编号", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "应用编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") @NotNull(message = "应用编号不能为空") private Long appId; diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/channel/vo/PayChannelCreateReqVO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/channel/vo/PayChannelCreateReqVO.java index d43ebbd3c..08073827a 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/channel/vo/PayChannelCreateReqVO.java +++ b/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; import lombok.ToString; import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; @Schema(description = "管理后台 - 支付渠道 创建 Request VO") @Data @@ -13,6 +14,10 @@ import javax.validation.constraints.NotBlank; @ToString(callSuper = true) public class PayChannelCreateReqVO extends PayChannelBaseVO { + @Schema(description = "渠道编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "alipay_pc") + @NotNull(message = "渠道编码不能为空") + private String code; + @Schema(description = "渠道配置的 json 字符串") @NotBlank(message = "渠道配置不能为空") private String config; diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/channel/vo/PayChannelPageReqVO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/channel/vo/PayChannelPageReqVO.java deleted file mode 100644 index 675730128..000000000 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/channel/vo/PayChannelPageReqVO.java +++ /dev/null @@ -1,42 +0,0 @@ -package cn.iocoder.yudao.module.pay.controller.admin.channel.vo; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - 支付渠道 分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class PayChannelPageReqVO extends PageParam { - - @Schema(description = "渠道编码") - private String code; - - @Schema(description = "开启状态") - private Integer status; - - @Schema(description = "备注") - private String remark; - - @Schema(description = "渠道费率,单位:百分比") - private Double feeRate; - - @Schema(description = "应用编号") - private Long appId; - - @Schema(description = "支付渠道配置") - private String config; - - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @Schema(description = "创建时间") - private LocalDateTime[] createTime; - -} diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/channel/vo/PayChannelRespVO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/channel/vo/PayChannelRespVO.java index 0d63aa25f..dafd29ec9 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/channel/vo/PayChannelRespVO.java +++ b/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; @ToString(callSuper = true) public class PayChannelRespVO extends PayChannelBaseVO { - @Schema(description = "商户编号", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "商户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private Long id; - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private LocalDateTime createTime; + @Schema(description = "渠道编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "alipay_pc") + private String code; + @Schema(description = "配置", requiredMode = Schema.RequiredMode.REQUIRED) private String config; + } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/channel/vo/PayChannelUpdateReqVO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/channel/vo/PayChannelUpdateReqVO.java index 357151ea2..39bf83eb0 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/channel/vo/PayChannelUpdateReqVO.java +++ b/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 { @Schema(description = "渠道配置的json字符串") @NotBlank(message = "渠道配置不能为空") private String config; + } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/channel/PayChannelMapper.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/channel/PayChannelMapper.java index 2f7fd86fd..acc28eeb6 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/channel/PayChannelMapper.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/channel/PayChannelMapper.java @@ -1,14 +1,13 @@ package cn.iocoder.yudao.module.pay.dal.mysql.channel; -import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX; -import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelPageReqVO; import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; +import java.time.LocalDateTime; import java.util.Collection; import java.util.List; @@ -19,41 +18,8 @@ public interface PayChannelMapper extends BaseMapperX { return selectOne(PayChannelDO::getAppId, appId, PayChannelDO::getCode, code); } - default PageResult selectPage(PayChannelPageReqVO reqVO) { - return selectPage(reqVO, new QueryWrapperX() - .eqIfPresent("code", reqVO.getCode()) - .eqIfPresent("status", reqVO.getStatus()) - .eqIfPresent("remark", reqVO.getRemark()) - .eqIfPresent("fee_rate", reqVO.getFeeRate()) - .eqIfPresent("app_id", reqVO.getAppId()) - .betweenIfPresent("create_time", reqVO.getCreateTime()) - .orderByDesc("id")); - } - - /** - * 根据条件获取渠道 - * - * @param appId 应用编号 - * @param code 渠道编码 - * @return 数量 - */ - default PayChannelDO selectOne(Long appId, String code) { - return this.selectOne((new QueryWrapper().lambda() - .eq(PayChannelDO::getAppId, appId) - .eq(PayChannelDO::getCode, code) - )); - } - - // TODO @aquan:select 命名 - /** - * 根据支付应用ID集合获得支付渠道列表 - * - * @param appIds 应用编号集合 - * @return 支付渠道列表 - */ - default List getChannelListByAppIds(Collection appIds){ - return this.selectList(new QueryWrapper().lambda() - .in(PayChannelDO::getAppId, appIds)); + default List selectListByAppIds(Collection appIds){ + return selectList(PayChannelDO::getAppId, appIds); } default List selectListByAppId(Long appId, Integer status) { @@ -62,4 +28,7 @@ public interface PayChannelMapper extends BaseMapperX { .eq(PayChannelDO::getStatus, status)); } + @Select("SELECT COUNT(*) FROM pay_channel WHERE update_time > #{maxUpdateTime}") + Long selectCountByUpdateTimeGt(LocalDateTime maxUpdateTime); + } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/app/PayAppServiceImpl.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/app/PayAppServiceImpl.java index c644edbfd..48e9c3c97 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/app/PayAppServiceImpl.java +++ b/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 { // 校验商户存在 validateAppExists(id); // 更新状态 - PayAppDO app = new PayAppDO(); - app.setId(id); - app.setStatus(status); - appMapper.updateById(app); + appMapper.updateById(new PayAppDO().setId(id).setStatus(status)); } @Override @@ -116,11 +113,11 @@ public class PayAppServiceImpl implements PayAppService { PayAppDO app = appMapper.selectById(id); // 校验是否存在 if (app == null) { - throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_APP_NOT_FOUND); + throw exception(ErrorCodeConstants.PAY_APP_NOT_FOUND); } // 校验是否禁用 if (CommonStatusEnum.DISABLE.getStatus().equals(app.getStatus())) { - throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_APP_IS_DISABLE); + throw exception(ErrorCodeConstants.PAY_APP_IS_DISABLE); } return app; } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelService.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelService.java index 3c634b8a9..1ec50e989 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelService.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelService.java @@ -1,9 +1,7 @@ package cn.iocoder.yudao.module.pay.service.channel; import cn.iocoder.yudao.framework.common.exception.ServiceException; -import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelCreateReqVO; -import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelPageReqVO; import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelUpdateReqVO; import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO; @@ -18,11 +16,6 @@ import java.util.List; */ public interface PayChannelService { - /** - * 初始化支付客户端 - */ - void initLocalCache(); - /** * 创建支付渠道 * @@ -54,15 +47,7 @@ public interface PayChannelService { PayChannelDO getChannel(Long id); /** - * 获得支付渠道分页 - * - * @param pageReqVO 分页查询 - * @return 支付渠道分页 - */ - PageResult getChannelPage(PayChannelPageReqVO pageReqVO); - - /** - * 根据支付应用ID集合获得支付渠道列表 + * 根据支付应用 ID 集合,获得支付渠道列表 * * @param appIds 应用编号集合 * @return 支付渠道列表 @@ -72,11 +57,11 @@ public interface PayChannelService { /** * 根据条件获取渠道 * - * @param appid 应用编号 + * @param appId 应用编号 * @param code 渠道编码 * @return 数量 */ - PayChannelDO getChannelByConditions(Long appid, String code); + PayChannelDO getChannelByAppIdAndCode(Long appId, String code); /** * 支付渠道的合法性 diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceImpl.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceImpl.java index de3d51ba7..69adddc4d 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceImpl.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceImpl.java @@ -1,30 +1,35 @@ package cn.iocoder.yudao.module.pay.service.channel; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ObjectUtil; -import cn.hutool.json.JSONUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; -import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig; import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelCreateReqVO; -import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelPageReqVO; import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelUpdateReqVO; import cn.iocoder.yudao.module.pay.convert.channel.PayChannelConvert; import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO; import cn.iocoder.yudao.module.pay.dal.mysql.channel.PayChannelMapper; import cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants; +import lombok.Getter; +import lombok.Setter; import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import javax.annotation.PostConstruct; import javax.annotation.Resource; import javax.validation.Validator; +import java.time.LocalDateTime; import java.util.Collection; import java.util.List; +import java.util.concurrent.TimeUnit; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 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 @Validated public class PayChannelServiceImpl implements PayChannelService { + @Getter // 为了方便测试,这里提供 getter 方法 + @Setter + private volatile List channelCache; + @Resource private PayClientFactory payClientFactory; @@ -52,7 +61,6 @@ public class PayChannelServiceImpl implements PayChannelService { /** * 初始化 {@link #payClientFactory} 缓存 */ - @Override @PostConstruct public void initLocalCache() { // 注意:忽略自动多租户,因为要全局初始化缓存 @@ -64,49 +72,101 @@ public class PayChannelServiceImpl implements PayChannelService { // 第二步:构建缓存:创建或更新支付 Client channels.forEach(payChannel -> payClientFactory.createOrUpdatePayClient(payChannel.getId(), payChannel.getCode(), payChannel.getConfig())); + this.channelCache = channels; }); } + /** + * 通过定时任务轮询,刷新缓存 + * + * 目的:多节点部署时,通过轮询”通知“所有节点,进行刷新 + */ + @Scheduled(initialDelay = 60, fixedRate = 60, timeUnit = TimeUnit.SECONDS) + public void refreshLocalCache() { + // 情况一:如果缓存里没有数据,则直接刷新缓存 + if (CollUtil.isEmpty(channelCache)) { + initLocalCache(); + return; + } + + // 情况二,如果缓存里数据,则通过 updateTime 判断是否有数据变更,有变更则刷新缓存 + LocalDateTime maxTime = CollectionUtils.getMaxValue(channelCache, PayChannelDO::getUpdateTime); + if (channelMapper.selectCountByUpdateTimeGt(maxTime) > 0) { + initLocalCache(); + } + } + @Override public Long createChannel(PayChannelCreateReqVO reqVO) { // 断言是否有重复的 - PayChannelDO channelDO = this.getChannelByConditions(reqVO.getAppId(), reqVO.getCode()); - if (ObjectUtil.isNotNull(channelDO)) { + PayChannelDO dbChannel = getChannelByAppIdAndCode(reqVO.getAppId(), reqVO.getCode()); + if (dbChannel != null) { throw exception(CHANNEL_EXIST_SAME_CHANNEL_ERROR); } // 新增渠道 - PayChannelDO channel = PayChannelConvert.INSTANCE.convert(reqVO); - settingConfigAndCheckParam(channel, reqVO.getConfig()); + PayChannelDO channel = PayChannelConvert.INSTANCE.convert(reqVO) + .setConfig(parseConfig(reqVO.getCode(), reqVO.getConfig())); channelMapper.insert(channel); - // TODO 芋艿:缺少刷新本地缓存的机制 + + // 刷新缓存 + refreshLocalCache(); return channel.getId(); } @Override public void updateChannel(PayChannelUpdateReqVO updateReqVO) { // 校验存在 - this.validateChannelExists(updateReqVO.getId()); + PayChannelDO dbChannel = validateChannelExists(updateReqVO.getId()); + // 更新 - PayChannelDO channel = PayChannelConvert.INSTANCE.convert(updateReqVO); - settingConfigAndCheckParam(channel, updateReqVO.getConfig()); + PayChannelDO channel = PayChannelConvert.INSTANCE.convert(updateReqVO) + .setConfig(parseConfig(dbChannel.getCode(), updateReqVO.getConfig())); channelMapper.updateById(channel); - // TODO 芋艿:缺少刷新本地缓存的机制 + + // 刷新缓存 + refreshLocalCache(); + } + + /** + * 解析并校验配置 + * + * @param code 渠道编码 + * @param configStr 配置 + * @return 支付配置 + */ + private PayClientConfig parseConfig(String code, String configStr) { + // 解析配置 + Class payClass = PayChannelEnum.getByCode(code).getConfigClass(); + if (ObjectUtil.isNull(payClass)) { + throw exception(CHANNEL_NOT_EXISTS); + } + PayClientConfig config = JsonUtils.parseObject2(configStr, payClass); + Assert.notNull(config); + + // 验证参数 + config.validate(validator); + return config; } @Override public void deleteChannel(Long id) { // 校验存在 - this.validateChannelExists(id); + validateChannelExists(id); + // 删除 channelMapper.deleteById(id); - // TODO 芋艿:缺少刷新本地缓存的机制 + + // 刷新缓存 + refreshLocalCache(); } - private void validateChannelExists(Long id) { - if (channelMapper.selectById(id) == null) { + private PayChannelDO validateChannelExists(Long id) { + PayChannelDO channel = channelMapper.selectById(id); + if (channel == null) { throw exception(CHANNEL_NOT_EXISTS); } + return channel; } @Override @@ -114,45 +174,20 @@ public class PayChannelServiceImpl implements PayChannelService { return channelMapper.selectById(id); } - @Override - public PageResult getChannelPage(PayChannelPageReqVO pageReqVO) { - return channelMapper.selectPage(pageReqVO); - } - @Override public List getChannelListByAppIds(Collection appIds) { - return channelMapper.getChannelListByAppIds(appIds); + return channelMapper.selectListByAppIds(appIds); } @Override - public PayChannelDO getChannelByConditions(Long appid, String code) { - return this.channelMapper.selectOne(appid, code); - } - - /** - * 设置渠道配置以及参数校验 - * - * @param channel 渠道 - * @param configStr 配置 - */ - private void settingConfigAndCheckParam(PayChannelDO channel, String configStr) { - // 得到这个渠道是微信的还是支付宝的 - Class payClass = PayChannelEnum.getByCode(channel.getCode()).getConfigClass(); - if (ObjectUtil.isNull(payClass)) { - throw exception(CHANNEL_NOT_EXISTS); - } - // TODO @芋艿:不要使用 hutool 的 json 工具,用项目的 - PayClientConfig config = JSONUtil.toBean(configStr, payClass); - - // 验证参数 - config.validate(validator); - channel.setConfig(config); + public PayChannelDO getChannelByAppIdAndCode(Long appId, String code) { + return channelMapper.selectByAppIdAndCode(appId, code); } @Override public PayChannelDO validPayChannel(Long id) { PayChannelDO channel = channelMapper.selectById(id); - this.validPayChannel(channel); + validPayChannel(channel); return channel; } @@ -163,18 +198,18 @@ public class PayChannelServiceImpl implements PayChannelService { return channel; } + private void validPayChannel(PayChannelDO channel) { + if (channel == null) { + throw exception(ErrorCodeConstants.PAY_CHANNEL_NOT_FOUND); + } + if (CommonStatusEnum.DISABLE.getStatus().equals(channel.getStatus())) { + throw exception(ErrorCodeConstants.PAY_CHANNEL_IS_DISABLE); + } + } + @Override public List getEnableChannelList(Long appId) { return channelMapper.selectListByAppId(appId, CommonStatusEnum.ENABLE.getStatus()); } - private void validPayChannel(PayChannelDO channel) { - if (channel == null) { - throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_CHANNEL_NOT_FOUND); - } - if (CommonStatusEnum.DISABLE.getStatus().equals(channel.getStatus())) { - throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_CHANNEL_IS_DISABLE); - } - } - } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/app/PayAppServiceTest.java b/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/app/PayAppServiceTest.java index df785d128..81536aba3 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/app/PayAppServiceTest.java +++ b/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; import javax.annotation.Resource; import java.time.LocalDateTime; +import java.util.Collections; +import java.util.Map; import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; 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 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.PAY_APP_NOT_FOUND; +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*; +import static java.util.Collections.singleton; import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; /** * {@link PayAppServiceImpl} 的单元测试 @@ -67,8 +72,7 @@ public class PayAppServiceTest extends BaseDbUnitTest { @Test public void testUpdateApp_success() { // mock 数据 - PayAppDO dbApp = randomPojo(PayAppDO.class, o -> - o.setStatus(CommonStatusEnum.DISABLE.getStatus())); + PayAppDO dbApp = randomPojo(PayAppDO.class); appMapper.insert(dbApp);// @Sql: 先插入出一条存在的数据 // 准备参数 PayAppUpdateReqVO reqVO = randomPojo(PayAppUpdateReqVO.class, o -> { @@ -94,10 +98,26 @@ public class PayAppServiceTest extends BaseDbUnitTest { } @Test - public void testDeleteApp_success() { + public void testUpdateAppStatus() { // mock 数据 PayAppDO dbApp = randomPojo(PayAppDO.class, o -> - o.setStatus((RandomUtil.randomEle(CommonStatusEnum.values()).getStatus()))); + o.setStatus(CommonStatusEnum.DISABLE.getStatus())); + appMapper.insert(dbApp);// @Sql: 先插入出一条存在的数据 + + // 准备参数 + Long id = dbApp.getId(); + Integer status = CommonStatusEnum.ENABLE.getStatus(); + // 调用 + appService.updateAppStatus(id, status); + // 断言 + PayAppDO app = appMapper.selectById(id); // 获取最新的 + assertEquals(status, app.getStatus()); + } + + @Test + public void testDeleteApp_success() { + // mock 数据 + PayAppDO dbApp = randomPojo(PayAppDO.class); appMapper.insert(dbApp);// @Sql: 先插入出一条存在的数据 // 准备参数 Long id = dbApp.getId(); @@ -117,6 +137,65 @@ public class PayAppServiceTest extends BaseDbUnitTest { assertServiceException(() -> appService.deleteApp(id), PAY_APP_NOT_FOUND); } + @Test + public void testDeleteApp_existOrder() { + // mock 数据 + PayAppDO dbApp = randomPojo(PayAppDO.class); + appMapper.insert(dbApp);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbApp.getId(); + // mock 订单有订单 + when(orderService.getOrderCountByAppId(eq(id))).thenReturn(10L); + + // 调用, 并断言异常 + assertServiceException(() -> appService.deleteApp(id), PAY_APP_EXIST_ORDER_CANT_DELETE); + } + + @Test + public void testDeleteApp_existRefund() { + // mock 数据 + PayAppDO dbApp = randomPojo(PayAppDO.class); + appMapper.insert(dbApp);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbApp.getId(); + // mock 订单有订单 + when(refundService.getRefundCountByAppId(eq(id))).thenReturn(10L); + + // 调用, 并断言异常 + assertServiceException(() -> appService.deleteApp(id), PAY_APP_EXIST_REFUND_CANT_DELETE); + } + + @Test + public void testApp() { + // mock 数据 + PayAppDO dbApp = randomPojo(PayAppDO.class); + appMapper.insert(dbApp);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbApp.getId(); + + // 调用 + PayAppDO app = appService.getApp(id); + // 校验数据一致 + assertPojoEquals(app, dbApp); + } + + @Test + public void testAppMap() { + // mock 数据 + PayAppDO dbApp01 = randomPojo(PayAppDO.class); + appMapper.insert(dbApp01);// @Sql: 先插入出一条存在的数据 + PayAppDO dbApp02 = randomPojo(PayAppDO.class); + appMapper.insert(dbApp02);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbApp01.getId(); + + // 调用 + Map appMap = appService.getAppMap(singleton(id)); + // 校验数据一致 + assertEquals(1, appMap.size()); + assertPojoEquals(dbApp01, appMap.get(id)); + } + @Test public void testGetAppPage() { // mock 数据 @@ -147,4 +226,37 @@ public class PayAppServiceTest extends BaseDbUnitTest { assertPojoEquals(dbApp, pageResult.getList().get(0)); } + @Test + public void testValidPayApp_success() { + // mock 数据 + PayAppDO dbApp = randomPojo(PayAppDO.class, + o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); + appMapper.insert(dbApp);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbApp.getId(); + + // 调用 + PayAppDO app = appService.validPayApp(id); + // 校验数据一致 + assertPojoEquals(app, dbApp); + } + + @Test + public void testValidPayApp_notFound() { + assertServiceException(() -> appService.validPayApp(randomLongId()), PAY_APP_NOT_FOUND); + } + + @Test + public void testValidPayApp_disable() { + // mock 数据 + PayAppDO dbApp = randomPojo(PayAppDO.class, + o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())); + appMapper.insert(dbApp);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbApp.getId(); + + // 调用,并断言异常 + assertServiceException(() -> appService.validPayApp(id), PAY_APP_IS_DISABLE); + } + } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceTest.java b/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceTest.java index cfc2ad8f6..0a5d33495 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceTest.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceTest.java @@ -1,32 +1,32 @@ package cn.iocoder.yudao.module.pay.service.channel; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig; import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WxPayClientConfig; import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelCreateReqVO; -import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelPageReqVO; import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelUpdateReqVO; import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO; import cn.iocoder.yudao.module.pay.dal.mysql.channel.PayChannelMapper; import com.alibaba.fastjson.JSON; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import javax.annotation.Resource; import javax.validation.Validator; -import java.time.LocalDateTime; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import java.util.Collections; +import java.util.List; + import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.CHANNEL_EXIST_SAME_CHANNEL_ERROR; import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.CHANNEL_NOT_EXISTS; import static org.junit.jupiter.api.Assertions.*; @@ -46,109 +46,62 @@ public class PayChannelServiceTest extends BaseDbUnitTest { @MockBean private Validator validator; + @BeforeEach + public void setUp() { + channelService.setChannelCache(null); + } + @Test - public void testCreateWechatVersion2Channel_success() { + public void testCreateChannel_success() { // 准备参数 - WxPayClientConfig v2Config = getV2Config(); + WxPayClientConfig config = randomWxPayClientConfig(); PayChannelCreateReqVO reqVO = randomPojo(PayChannelCreateReqVO.class, o -> { + o.setStatus(randomCommonStatus()); o.setCode(PayChannelEnum.WX_PUB.getCode()); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - o.setConfig(JSON.toJSONString(v2Config)); + o.setConfig(JsonUtils.toJsonString(config)); }); // 调用 Long channelId = channelService.createChannel(reqVO); - // 断言 - assertNotNull(channelId); // 校验记录的属性是否正确 PayChannelDO channel = channelMapper.selectById(channelId); assertPojoEquals(reqVO, channel, "config"); - // 关于config 对象应该拿出来重新对比 - assertPojoEquals(v2Config, channel.getConfig()); + assertPojoEquals(config, channel.getConfig()); + // 校验缓存 + assertEquals(1, channelService.getChannelCache().size()); + assertEquals(channel, channelService.getChannelCache().get(0)); } @Test - public void testCreateWechatVersion3Channel_success() { + public void testCreateChannel_exists() { + // mock 数据 + PayChannelDO dbChannel = randomPojo(PayChannelDO.class, + o -> o.setConfig(randomWxPayClientConfig())); + channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据 // 准备参数 - WxPayClientConfig v3Config = getV3Config(); PayChannelCreateReqVO reqVO = randomPojo(PayChannelCreateReqVO.class, o -> { - o.setCode(PayChannelEnum.WX_PUB.getCode()); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - o.setConfig(JSON.toJSONString(v3Config)); + o.setAppId(dbChannel.getAppId()); + o.setCode(dbChannel.getCode()); }); - // 调用 - Long channelId = channelService.createChannel(reqVO); - // 断言 - assertNotNull(channelId); - // 校验记录的属性是否正确 - PayChannelDO channel = channelMapper.selectById(channelId); - assertPojoEquals(reqVO, channel, "config"); - // 关于config 对象应该拿出来重新对比 - assertPojoEquals(v3Config, channel.getConfig()); - } - - @Test - public void testCreateAliPayPublicKeyChannel_success() { - // 准备参数 - - AlipayPayClientConfig payClientConfig = getPublicKeyConfig(); - PayChannelCreateReqVO reqVO = randomPojo(PayChannelCreateReqVO.class, o -> { - o.setCode(PayChannelEnum.ALIPAY_APP.getCode()); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - o.setConfig(JSON.toJSONString(payClientConfig)); - }); - - // 调用 - Long channelId = channelService.createChannel(reqVO); - // 断言 - assertNotNull(channelId); - // 校验记录的属性是否正确 - PayChannelDO channel = channelMapper.selectById(channelId); - assertPojoEquals(reqVO, channel, "config"); - // 关于config 对象应该拿出来重新对比 - assertPojoEquals(payClientConfig, channel.getConfig()); - } - - @Test - public void testCreateAliPayCertificateChannel_success() { - // 准备参数 - - AlipayPayClientConfig payClientConfig = getCertificateConfig(); - PayChannelCreateReqVO reqVO = randomPojo(PayChannelCreateReqVO.class, o -> { - o.setCode(PayChannelEnum.ALIPAY_APP.getCode()); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - o.setConfig(JSON.toJSONString(payClientConfig)); - }); - - // 调用 - Long channelId = channelService.createChannel(reqVO); - // 断言 - assertNotNull(channelId); - // 校验记录的属性是否正确 - PayChannelDO channel = channelMapper.selectById(channelId); - assertPojoEquals(reqVO, channel, "config"); - // 关于config 对象应该拿出来重新对比 - assertPojoEquals(payClientConfig, channel.getConfig()); + // 调用, 并断言异常 + assertServiceException(() -> channelService.createChannel(reqVO), CHANNEL_EXIST_SAME_CHANNEL_ERROR); } @Test public void testUpdateChannel_success() { // mock 数据 - AlipayPayClientConfig payClientConfig = getCertificateConfig(); PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> { o.setCode(PayChannelEnum.ALIPAY_APP.getCode()); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - o.setConfig(payClientConfig); + o.setConfig(randomAlipayPayClientConfig()); }); channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据 // 准备参数 - AlipayPayClientConfig payClientPublicKeyConfig = getPublicKeyConfig(); + AlipayPayClientConfig config = randomAlipayPayClientConfig(); PayChannelUpdateReqVO reqVO = randomPojo(PayChannelUpdateReqVO.class, o -> { - o.setCode(dbChannel.getCode()); - o.setStatus(dbChannel.getStatus()); - o.setConfig(JSON.toJSONString(payClientPublicKeyConfig)); o.setId(dbChannel.getId()); // 设置更新的 ID + o.setStatus(randomCommonStatus()); + o.setConfig(JsonUtils.toJsonString(config)); }); // 调用 @@ -156,15 +109,17 @@ public class PayChannelServiceTest extends BaseDbUnitTest { // 校验是否更新正确 PayChannelDO channel = channelMapper.selectById(reqVO.getId()); // 获取最新的 assertPojoEquals(reqVO, channel, "config"); - assertPojoEquals(payClientPublicKeyConfig, channel.getConfig()); + assertPojoEquals(config, channel.getConfig()); + // 校验缓存 + assertEquals(1, channelService.getChannelCache().size()); + assertEquals(channel, channelService.getChannelCache().get(0)); } @Test public void testUpdateChannel_notExists() { // 准备参数 - AlipayPayClientConfig payClientPublicKeyConfig = getPublicKeyConfig(); + AlipayPayClientConfig payClientPublicKeyConfig = randomAlipayPayClientConfig(); PayChannelUpdateReqVO reqVO = randomPojo(PayChannelUpdateReqVO.class, o -> { - o.setCode(PayChannelEnum.ALIPAY_APP.getCode()); o.setStatus(CommonStatusEnum.ENABLE.getStatus()); o.setConfig(JSON.toJSONString(payClientPublicKeyConfig)); }); @@ -176,11 +131,9 @@ public class PayChannelServiceTest extends BaseDbUnitTest { @Test public void testDeleteChannel_success() { // mock 数据 - AlipayPayClientConfig payClientConfig = getCertificateConfig(); PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> { o.setCode(PayChannelEnum.ALIPAY_APP.getCode()); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - o.setConfig(payClientConfig); + o.setConfig(randomAlipayPayClientConfig()); }); channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据 // 准备参数 @@ -190,6 +143,8 @@ public class PayChannelServiceTest extends BaseDbUnitTest { channelService.deleteChannel(id); // 校验数据不存在了 assertNull(channelMapper.selectById(id)); + // 校验缓存 + assertEquals(0, channelService.getChannelCache().size()); } @Test @@ -201,119 +156,80 @@ public class PayChannelServiceTest extends BaseDbUnitTest { assertServiceException(() -> channelService.deleteChannel(id), CHANNEL_NOT_EXISTS); } - @Test // TODO 请修改 null 为需要的值 - public void testGetChannelPage() { + @Test + public void testGetChannel() { // mock 数据 - AlipayPayClientConfig payClientConfig = getCertificateConfig(); - PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> { // 等会查询到 + PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> { o.setCode(PayChannelEnum.ALIPAY_APP.getCode()); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - o.setRemark("灿灿子的支付渠道"); - o.setFeeRate(0.03); - o.setAppId(1L); - o.setConfig(payClientConfig); - o.setCreateTime(buildTime(2021,11,20)); + o.setConfig(randomAlipayPayClientConfig()); }); - channelMapper.insert(dbChannel); - // 执行拷贝的时候会出现异常,所以在插入后要重置为null 后续在写入新的 - dbChannel.setConfig(null); - // 测试 code 不匹配 - channelMapper.insert(cloneIgnoreId(dbChannel, o -> { - o.setConfig(payClientConfig); - o.setCode(PayChannelEnum.WX_PUB.getCode()); - })); - // 测试 status 不匹配 - channelMapper.insert(cloneIgnoreId(dbChannel, o -> { - o.setConfig(payClientConfig); - o.setStatus(CommonStatusEnum.DISABLE.getStatus()); - })); - // 测试 remark 不匹配 - channelMapper.insert(cloneIgnoreId(dbChannel, o ->{ - o.setConfig(payClientConfig); - o.setRemark("敏敏子的渠道"); - })); - // 测试 feeRate 不匹配 - channelMapper.insert(cloneIgnoreId(dbChannel, o -> { - o.setConfig(payClientConfig); - o.setFeeRate(1.23); - })); - // 测试 appId 不匹配 - channelMapper.insert(cloneIgnoreId(dbChannel, o -> { - o.setConfig(payClientConfig); - o.setAppId(2L); - })); - // 测试 createTime 不匹配 - channelMapper.insert(cloneIgnoreId(dbChannel, o -> { - o.setConfig(payClientConfig); - o.setCreateTime(buildTime(2021, 10, 20)); - })); + channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据 // 准备参数 - PayChannelPageReqVO reqVO = new PayChannelPageReqVO(); - reqVO.setCode(PayChannelEnum.ALIPAY_APP.getCode()); - reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); - reqVO.setRemark("灿灿子的支付渠道"); - reqVO.setFeeRate(0.03); - reqVO.setAppId(1L); - reqVO.setConfig(JSON.toJSONString(payClientConfig)); - reqVO.setCreateTime((new LocalDateTime[]{buildTime(2021,11,19),buildTime(2021,11,21)})); + Long id = dbChannel.getId(); // 调用 - PageResult pageResult = channelService.getChannelPage(reqVO); + PayChannelDO channel = channelService.getChannel(id); + // 校验是否更新正确 + assertPojoEquals(dbChannel, channel); + } + + @Test + public void testGetChannelListByAppIds() { + // mock 数据 + PayChannelDO dbChannel01 = randomPojo(PayChannelDO.class, o -> { + o.setCode(PayChannelEnum.ALIPAY_APP.getCode()); + o.setConfig(randomAlipayPayClientConfig()); + }); + channelMapper.insert(dbChannel01);// @Sql: 先插入出一条存在的数据 + PayChannelDO dbChannel02 = randomPojo(PayChannelDO.class, o -> { + o.setCode(PayChannelEnum.WX_PUB.getCode()); + o.setConfig(randomWxPayClientConfig()); + }); + channelMapper.insert(dbChannel02);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long appId = dbChannel01.getAppId(); + + // 调用 + List channels = channelService.getChannelListByAppIds(Collections.singleton(appId)); + // 校验是否更新正确 + assertEquals(1, channels.size()); + assertPojoEquals(dbChannel01, channels.get(0)); + } + + @Test + public void testGetChannelByAppIdAndCode() { + // mock 数据 + PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> { + o.setCode(PayChannelEnum.ALIPAY_APP.getCode()); + o.setConfig(randomAlipayPayClientConfig()); + }); + channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long appId = dbChannel.getAppId(); + String code = dbChannel.getCode();; + + // 调用 + PayChannelDO channel = channelService.getChannelByAppIdAndCode(appId, code); // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbChannel, pageResult.getList().get(0), "config"); - assertPojoEquals(payClientConfig, pageResult.getList().get(0).getConfig()); - + assertPojoEquals(channel, dbChannel); } - public WxPayClientConfig getV2Config() { + public WxPayClientConfig randomWxPayClientConfig() { return new WxPayClientConfig() - .setAppId("APP00001") - .setMchId("MCH00001") + .setAppId(randomString()) + .setMchId(randomString()) .setApiVersion(WxPayClientConfig.API_VERSION_V2) - .setMchKey("dsa1d5s6a1d6sa16d1sa56d15a61das6") - .setApiV3Key("") - .setPrivateCertContent("") - .setPrivateKeyContent(""); + .setMchKey(randomString()); } - public WxPayClientConfig getV3Config() { - return new WxPayClientConfig() - .setAppId("APP00001") - .setMchId("MCH00001") - .setApiVersion(WxPayClientConfig.API_VERSION_V3) - .setMchKey("") - .setApiV3Key("sdadasdsadadsa") - .setPrivateKeyContent("dsa445das415d15asd16ad156as") - .setPrivateCertContent("dsadasd45asd4s5a"); - - } - - public AlipayPayClientConfig getPublicKeyConfig() { + public AlipayPayClientConfig randomAlipayPayClientConfig() { return new AlipayPayClientConfig() - .setServerUrl(ALIPAY_SERVER_URL) - .setAppId("APP00001") + .setServerUrl(randomURL()) + .setAppId(randomString()) .setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT) .setMode(AlipayPayClientConfig.MODE_PUBLIC_KEY) - .setPrivateKey("13131321312") - .setAlipayPublicKey("13321321321") - .setAppCertContent("") - .setAlipayPublicCertContent("") - .setRootCertContent(""); - } - - public AlipayPayClientConfig getCertificateConfig() { - return new AlipayPayClientConfig() - .setServerUrl(ALIPAY_SERVER_URL) - .setAppId("APP00001") - .setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT) - .setMode(AlipayPayClientConfig.MODE_CERTIFICATE) - .setPrivateKey("") - .setAlipayPublicKey("") - .setAppCertContent("13321321321sda") - .setAlipayPublicCertContent("13321321321aqeqw") - .setRootCertContent("13321321321dsad"); + .setPrivateKey(randomString()) + .setAlipayPublicKey(randomString()); } } diff --git a/yudao-ui-admin/src/api/pay/channel.js b/yudao-ui-admin/src/api/pay/channel.js index 68e203807..727fde092 100644 --- a/yudao-ui-admin/src/api/pay/channel.js +++ b/yudao-ui-admin/src/api/pay/channel.js @@ -27,15 +27,6 @@ export function deleteChannel(id) { }) } -// 获得支付渠道分页 -export function getChannelPage(query) { - return request({ - url: '/pay/channel/page', - method: 'get', - params: query - }) -} - // 获得支付渠道 export function getChannel(appId,code) { return request({