diff --git a/sql/pay-app-menu.sql b/sql/pay-app-menu.sql new file mode 100644 index 000000000..2b6f9cf77 --- /dev/null +++ b/sql/pay-app-menu.sql @@ -0,0 +1,64 @@ +-- 菜单 SQL +INSERT INTO `sys_menu`( + `name`, `permission`, `menu_type`, `sort`, `parent_id`, + `path`, `icon`, `component`, `status` +) +VALUES ( + '支付应用信息管理', '', 2, 0, 1117, + 'app', '', 'pay/app/index', 0 + ); + +-- 按钮父菜单ID +SELECT @parentId := LAST_INSERT_ID(); + +-- 按钮 SQL +INSERT INTO `sys_menu`( + `name`, `permission`, `menu_type`, `sort`, `parent_id`, + `path`, `icon`, `component`, `status` +) +VALUES ( + '支付应用信息查询', 'pay:app:query', 3, 1, @parentId, + '', '', '', 0 + ); +INSERT INTO `sys_menu`( + `name`, `permission`, `menu_type`, `sort`, `parent_id`, + `path`, `icon`, `component`, `status` +) +VALUES ( + '支付应用信息创建', 'pay:app:create', 3, 2, @parentId, + '', '', '', 0 + ); +INSERT INTO `sys_menu`( + `name`, `permission`, `menu_type`, `sort`, `parent_id`, + `path`, `icon`, `component`, `status` +) +VALUES ( + '支付应用信息更新', 'pay:app:update', 3, 3, @parentId, + '', '', '', 0 + ); +INSERT INTO `sys_menu`( + `name`, `permission`, `menu_type`, `sort`, `parent_id`, + `path`, `icon`, `component`, `status` +) +VALUES ( + '支付应用信息删除', 'pay:app:delete', 3, 4, @parentId, + '', '', '', 0 + ); +INSERT INTO `sys_menu`( + `name`, `permission`, `menu_type`, `sort`, `parent_id`, + `path`, `icon`, `component`, `status` +) +VALUES ( + '支付应用信息导出', 'pay:app:export', 3, 5, @parentId, + '', '', '', 0 + ); + + +INSERT INTO `sys_menu` ( + `name`, `permission`, `menu_type`, `sort`, `parent_id`, + `path`, `icon`, `component`, `status`, `creator`, `create_time`, + `updater`, `update_time`, `deleted` +) VALUES ( + '秘钥解析', 'pay:channel:parsing', 3, 6, 1129, '', '', '', 0, '1', + '2021-11-08 15:15:47', '1', '2021-11-08 15:15:47', b'0' + ); diff --git a/sql/pay-dict.sql b/sql/pay-dict.sql new file mode 100644 index 000000000..3b769568c --- /dev/null +++ b/sql/pay-dict.sql @@ -0,0 +1,24 @@ + +-- 商户状态 status +INSERT INTO `sys_dict_type` ( `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES ( '商户状态', 'pay_merchant_status', 0, '商户的启用于停用状态', '1', '2021-11-03 11:29:04', '1', '2021-11-03 11:29:04', b'0'); + +INSERT INTO `sys_dict_data` ( `sort`, `label`, `value`, `dict_type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES ( 1, '启用', '0', 'pay_merchant_status', 0, '商户启用', '1', '2021-11-03 11:30:52', '1', '2021-11-03 11:31:15', b'0'); +INSERT INTO `sys_dict_data` ( `sort`, `label`, `value`, `dict_type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES ( 2, '停用', '1', 'pay_merchant_status', 0, '商户停用', '1', '2021-11-03 11:31:05', '1', '2021-11-03 11:31:05', b'0'); + +-- 支付应用状态 status +INSERT INTO `sys_dict_type` ( `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES ('支付应用状态', 'pay_app_status', 0, '支付应用的启停状态', '1', '2021-11-06 19:41:50', '1', '2021-11-06 19:41:50', b'0'); + +INSERT INTO `sys_dict_data` ( `sort`, `label`, `value`, `dict_type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES ( 1, '开启', '0', 'pay_app_status', 0, NULL, '1', '2021-11-06 19:42:10', '1', '2021-11-06 19:42:10', b'0'); +INSERT INTO `sys_dict_data` ( `sort`, `label`, `value`, `dict_type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES ( 2, '关闭', '1', 'pay_app_status', 0, NULL, '1', '2021-11-06 19:42:17', '1', '2021-11-06 19:42:17', b'0'); + +-- 支付渠道状态 status +INSERT INTO `sys_dict_type` ( `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES ( '支付渠道状态', 'pay_channel_status', 0, '支付渠道的启停状态', '1', '2021-11-08 16:47:21', '1', '2021-11-08 16:47:21', b'0'); + +INSERT INTO `sys_dict_data` ( `sort`, `label`, `value`, `dict_type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES ( 1, '开启', '0', 'pay_channel_status', 0, '开启', '1', '2021-11-08 16:47:45', '1', '2021-11-08 16:47:45', b'0'); +INSERT INTO `sys_dict_data` ( `sort`, `label`, `value`, `dict_type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES ( 2, '关闭', '1', 'pay_channel_status', 0, '关闭', '1', '2021-11-08 16:47:52', '1', '2021-11-08 16:47:52', b'0'); + +-- 微信渠道版本控制 +INSERT INTO `sys_dict_type` (`name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES ('支付渠道微信版本', 'pay_channel_wechat_version', 0, '支付渠道微信版本', '1', '2021-11-08 17:00:26', '1', '2021-11-08 17:00:26', b'0'); + +INSERT INTO `sys_dict_data` (`sort`, `label`, `value`, `dict_type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, 'v2', 'v2', 'pay_channel_wechat_version', 0, 'v2版本', '1', '2021-11-08 17:00:58', '1', '2021-11-08 17:00:58', b'0'); +INSERT INTO `sys_dict_data` (`sort`, `label`, `value`, `dict_type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2, 'v3', 'v3', 'pay_channel_wechat_version', 0, 'v3版本', '1', '2021-11-08 17:01:07', '1', '2021-11-08 17:01:07', b'0'); diff --git a/sql/pay-merchant-menu.sql b/sql/pay-merchant-menu.sql index cc4a4e761..65b4ceb9b 100644 --- a/sql/pay-merchant-menu.sql +++ b/sql/pay-merchant-menu.sql @@ -1,9 +1,4 @@ -- 支付模块-商户中心-菜单SQL --- 菜单 SQL -INSERT INTO `sys_menu` ( - `name`, `permission`,`menu_type`,`sort`, `parent_id`, `path`, `icon`, `component`, - `status`, `creator`,`create_time`, `updater`, `update_time`, `deleted` -) VALUES ('支付管理', '', 1, 4,0, '/pay','pay', NULL, 0, '1', '2021-11-03 10:35:04', '1', '2021-11-03 10:35:04', b'0'); INSERT INTO `sys_menu`( `name`, `permission`, `menu_type`, `sort`, `parent_id`, diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/app/PayAppController.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/app/PayAppController.java new file mode 100644 index 000000000..cf3df2aa4 --- /dev/null +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/app/PayAppController.java @@ -0,0 +1,175 @@ +package cn.iocoder.yudao.adminserver.modules.pay.controller.app; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.adminserver.modules.pay.controller.app.vo.*; +import cn.iocoder.yudao.adminserver.modules.pay.convert.app.PayAppConvert; +import cn.iocoder.yudao.adminserver.modules.pay.service.app.PayAppService; +import cn.iocoder.yudao.adminserver.modules.pay.service.channel.PayChannelService; +import cn.iocoder.yudao.adminserver.modules.pay.service.merchant.PayMerchantService; +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayAppDO; +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO; +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayMerchantDO; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import javax.validation.Valid; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; + +/** + * 支付应用信息 controller 组件 + * + * @author aquan + */ +@Slf4j +@Api(tags = "支付应用信息") +@RestController +@RequestMapping("/pay/app") +@Validated +public class PayAppController { + + @Resource + private PayAppService appService; + + @Resource + private PayChannelService channelService; + + + @Resource + private PayMerchantService merchantService; + + @PostMapping("/create") + @ApiOperation("创建支付应用信息") + @PreAuthorize("@ss.hasPermission('pay:app:create')") + public CommonResult<Long> createApp(@Valid @RequestBody PayAppCreateReqVO createReqVO) { + return success(appService.createApp(createReqVO)); + } + + @PutMapping("/update") + @ApiOperation("更新支付应用信息") + @PreAuthorize("@ss.hasPermission('pay:app:update')") + public CommonResult<Boolean> updateApp(@Valid @RequestBody PayAppUpdateReqVO updateReqVO) { + appService.updateApp(updateReqVO); + return success(true); + } + + @PutMapping("/update-status") + @ApiOperation("更新支付应用状态") + @PreAuthorize("@ss.hasPermission('pay:app:update')") + public CommonResult<Boolean> updateAppStatus(@Valid @RequestBody PayAppUpdateStatusReqVO updateReqVO) { + appService.updateAppStatus(updateReqVO.getId(), updateReqVO.getStatus()); + return success(true); + } + + @DeleteMapping("/delete") + @ApiOperation("删除支付应用信息") + @ApiImplicitParam(name = "id", value = "编号", required = true) + @PreAuthorize("@ss.hasPermission('pay:app:delete')") + public CommonResult<Boolean> deleteApp(@RequestParam("id") Long id) { + appService.deleteApp(id); + return success(true); + } + + @GetMapping("/get") + @ApiOperation("获得支付应用信息") + @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class) + @PreAuthorize("@ss.hasPermission('pay:app:query')") + public CommonResult<PayAppRespVO> getApp(@RequestParam("id") Long id) { + PayAppDO app = appService.getApp(id); + return success(PayAppConvert.INSTANCE.convert(app)); + } + + @GetMapping("/list") + @ApiOperation("获得支付应用信息列表") + @ApiImplicitParam(name = "ids", value = "编号列表", required = true, example = "1024,2048", dataTypeClass = List.class) + @PreAuthorize("@ss.hasPermission('pay:app:query')") + public CommonResult<List<PayAppRespVO>> getAppList(@RequestParam("ids") Collection<Long> ids) { + List<PayAppDO> list = appService.getAppList(ids); + return success(PayAppConvert.INSTANCE.convertList(list)); + } + + @GetMapping("/page") + @ApiOperation("获得支付应用信息分页") + @PreAuthorize("@ss.hasPermission('pay:app:query')") + public CommonResult<PageResult<PayAppPageItemRespVO>> getAppPage(@Valid PayAppPageReqVO pageVO) { + // 得到应用分页列表 + PageResult<PayAppDO> pageResult = appService.getAppPage(pageVO); + if (CollUtil.isEmpty(pageResult.getList())) { + return success(new PageResult<>(pageResult.getTotal())); + } + + // 得到所有的应用编号,查出所有的通道 + Collection<Long> payAppIds = CollectionUtils.convertList(pageResult.getList(), PayAppDO::getId); + List<PayChannelDO> channels = channelService.getSimpleChannels(payAppIds); + + // 得到所有的商户信息 + Collection<Long> merchantIds = CollectionUtils.convertList(pageResult.getList(), PayAppDO::getMerchantId); + Map<Long, PayMerchantDO> deptMap = merchantService.getMerchantMap(merchantIds); + + // 利用反射将通道数据复制到返回的数据结构中去 + List<PayAppPageItemRespVO> appList = new ArrayList<>(pageResult.getList().size()); + pageResult.getList().forEach(app -> { + // 写入应用信息的数据 + PayAppPageItemRespVO respVO = PayAppConvert.INSTANCE.pageConvert(app); + // 写入商户的数据 + respVO.setPayMerchant(PayAppConvert.INSTANCE.convert(deptMap.get(app.getMerchantId()))); + // 写入支付渠道信息的数据 + PayAppPageItemRespVO.PayChannel payChannel = new PayAppPageItemRespVO.PayChannel(); + channels.forEach(c -> { + if (c.getAppId().equals(app.getId())) { + // 获取 set 方法 + String methodName = StrUtil.toCamelCase("set_" + c.getCode()); + try { + // 根据 set 方法将值写入 + payChannel.getClass().getMethod(methodName, Integer.class) + .invoke(payChannel, CommonStatusEnum.ENABLE.getStatus()); + } catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { + log.error("[getAppPage]调用方法[{}]设置参数[{}]异常", c.getCode(), methodName); + } + } + }); + respVO.setPayChannel(payChannel); + appList.add(respVO); + }); + + return success(new PageResult<>(appList, pageResult.getTotal())); + } + + @GetMapping("/export-excel") + @ApiOperation("导出支付应用信息 Excel") + @PreAuthorize("@ss.hasPermission('pay:app:export')") + @OperateLog(type = EXPORT) + public void exportAppExcel(@Valid PayAppExportReqVO exportReqVO, + HttpServletResponse response) throws IOException { + List<PayAppDO> list = appService.getAppList(exportReqVO); + // 导出 Excel + List<PayAppExcelVO> datas = PayAppConvert.INSTANCE.convertList02(list); + ExcelUtils.write(response, "支付应用信息.xls", "数据", PayAppExcelVO.class, datas); + } + + + +} diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/app/vo/PayAppBaseVO.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/app/vo/PayAppBaseVO.java new file mode 100644 index 000000000..6ffe565ec --- /dev/null +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/app/vo/PayAppBaseVO.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.adminserver.modules.pay.controller.app.vo; + +import lombok.*; +import io.swagger.annotations.*; +import javax.validation.constraints.*; + +/** +* 支付应用信息 Base VO,提供给添加、修改、详细的子 VO 使用 +* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 +*/ +@Data +public class PayAppBaseVO { + + @ApiModelProperty(value = "应用名", required = true) + @NotNull(message = "应用名不能为空") + private String name; + + @ApiModelProperty(value = "开启状态", required = true) + @NotNull(message = "开启状态不能为空") + private Integer status; + + @ApiModelProperty(value = "备注") + private String remark; + + @ApiModelProperty(value = "支付结果的回调地址", required = true) + @NotNull(message = "支付结果的回调地址不能为空") + private String payNotifyUrl; + + @ApiModelProperty(value = "退款结果的回调地址", required = true) + @NotNull(message = "退款结果的回调地址不能为空") + private String refundNotifyUrl; + + @ApiModelProperty(value = "商户编号", required = true) + @NotNull(message = "商户编号不能为空") + private Long merchantId; + +} diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/app/vo/PayAppCreateReqVO.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/app/vo/PayAppCreateReqVO.java new file mode 100644 index 000000000..3b0e69b99 --- /dev/null +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/app/vo/PayAppCreateReqVO.java @@ -0,0 +1,14 @@ +package cn.iocoder.yudao.adminserver.modules.pay.controller.app.vo; + +import lombok.*; +import java.util.*; +import io.swagger.annotations.*; +import javax.validation.constraints.*; + +@ApiModel("支付应用信息创建 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class PayAppCreateReqVO extends PayAppBaseVO { + +} diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/app/vo/PayAppExcelVO.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/app/vo/PayAppExcelVO.java new file mode 100644 index 000000000..f81b53bd0 --- /dev/null +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/app/vo/PayAppExcelVO.java @@ -0,0 +1,41 @@ +package cn.iocoder.yudao.adminserver.modules.pay.controller.app.vo; + +import lombok.*; +import java.util.*; +import io.swagger.annotations.*; + +import com.alibaba.excel.annotation.ExcelProperty; + +/** + * 支付应用信息 Excel VO + * + * @author 芋艿 + */ +@Data +public class PayAppExcelVO { + + @ExcelProperty("应用编号") + private Long id; + + @ExcelProperty("应用名") + private String name; + + @ExcelProperty("开启状态") + private Integer status; + + @ExcelProperty("备注") + private String remark; + + @ExcelProperty("支付结果的回调地址") + private String payNotifyUrl; + + @ExcelProperty("退款结果的回调地址") + private String refundNotifyUrl; + + @ExcelProperty("商户编号") + private Long merchantId; + + @ExcelProperty("创建时间") + private Date createTime; + +} diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/app/vo/PayAppExportReqVO.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/app/vo/PayAppExportReqVO.java new file mode 100644 index 000000000..e2c68480d --- /dev/null +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/app/vo/PayAppExportReqVO.java @@ -0,0 +1,41 @@ +package cn.iocoder.yudao.adminserver.modules.pay.controller.app.vo; + +import lombok.*; +import java.util.*; +import io.swagger.annotations.*; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import org.springframework.format.annotation.DateTimeFormat; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@ApiModel(value = "支付应用信息 Excel 导出 Request VO", description = "参数和 PayAppPageReqVO 是一致的") +@Data +public class PayAppExportReqVO { + + @ApiModelProperty(value = "应用名") + private String name; + + @ApiModelProperty(value = "开启状态") + private Integer status; + + @ApiModelProperty(value = "备注") + private String remark; + + @ApiModelProperty(value = "支付结果的回调地址") + private String payNotifyUrl; + + @ApiModelProperty(value = "退款结果的回调地址") + private String refundNotifyUrl; + + @ApiModelProperty(value = "商户编号") + private Long merchantId; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @ApiModelProperty(value = "开始创建时间") + private Date beginCreateTime; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @ApiModelProperty(value = "结束创建时间") + private Date endCreateTime; + +} diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/app/vo/PayAppPageItemRespVO.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/app/vo/PayAppPageItemRespVO.java new file mode 100644 index 000000000..0261090e0 --- /dev/null +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/app/vo/PayAppPageItemRespVO.java @@ -0,0 +1,75 @@ +package cn.iocoder.yudao.adminserver.modules.pay.controller.app.vo; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.util.Date; + +@ApiModel(value = "支付应用信息分页查询 Response VO", description = "相比于支付信息,还会多出应用渠道的开关信息") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class PayAppPageItemRespVO extends PayAppBaseVO { + + @ApiModelProperty(value = "应用编号", required = true) + private Long id; + + @ApiModelProperty(value = "创建时间", required = true) + private Date createTime; + + /** + * 所属商户 + */ + private PayMerchant payMerchant; + + @ApiModel("商户") + @Data + public static class PayMerchant { + + @ApiModelProperty(value = "商户编号", required = true, example = "1") + private Long id; + + @ApiModelProperty(value = "商户名称", required = true, example = "研发部") + private String name; + + } + + /** + * 支付渠道 + */ + private PayChannel payChannel; + + /** + * 支付渠道开通情况 + * 1默认为未开通当前支付渠道,0为已开通支付渠道 + */ + @Data + @ApiModel("支付渠道") + public static class PayChannel { + + @ApiModelProperty(value = "微信 JSAPI 支付", required = true, example = "1") + private Integer wxPub = CommonStatusEnum.DISABLE.getStatus(); + + @ApiModelProperty(value = "微信小程序支付", required = true, example = "1") + private Integer wxLite = CommonStatusEnum.DISABLE.getStatus(); + + @ApiModelProperty(value = "微信 App 支付", required = true, example = "1") + private Integer wxApp = CommonStatusEnum.DISABLE.getStatus(); + + @ApiModelProperty(value = "支付宝 PC 网站支付", required = true, example = "1") + private Integer alipayPc = CommonStatusEnum.DISABLE.getStatus(); + + @ApiModelProperty(value = "支付宝 Wap 网站支付", required = true, example = "1") + private Integer alipayWap = CommonStatusEnum.DISABLE.getStatus(); + + @ApiModelProperty(value = "支付宝App 支付", required = true, example = "1") + private Integer alipayApp = CommonStatusEnum.DISABLE.getStatus(); + + @ApiModelProperty(value = "支付宝扫码支付", required = true, example = "1") + private Integer alipayQr = CommonStatusEnum.DISABLE.getStatus(); + } +} diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/app/vo/PayAppPageReqVO.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/app/vo/PayAppPageReqVO.java new file mode 100644 index 000000000..62156e993 --- /dev/null +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/app/vo/PayAppPageReqVO.java @@ -0,0 +1,43 @@ +package cn.iocoder.yudao.adminserver.modules.pay.controller.app.vo; + +import lombok.*; +import java.util.*; +import io.swagger.annotations.*; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import org.springframework.format.annotation.DateTimeFormat; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@ApiModel("支付应用信息分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class PayAppPageReqVO extends PageParam { + + @ApiModelProperty(value = "应用名") + private String name; + + @ApiModelProperty(value = "开启状态") + private Integer status; + + @ApiModelProperty(value = "备注") + private String remark; + + @ApiModelProperty(value = "支付结果的回调地址") + private String payNotifyUrl; + + @ApiModelProperty(value = "退款结果的回调地址") + private String refundNotifyUrl; + + @ApiModelProperty(value = "商户名称") + private String merchantName; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @ApiModelProperty(value = "开始创建时间") + private Date beginCreateTime; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @ApiModelProperty(value = "结束创建时间") + private Date endCreateTime; + +} diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/app/vo/PayAppRespVO.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/app/vo/PayAppRespVO.java new file mode 100644 index 000000000..565491f92 --- /dev/null +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/app/vo/PayAppRespVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.adminserver.modules.pay.controller.app.vo; + +import lombok.*; +import java.util.*; +import io.swagger.annotations.*; + +@ApiModel("支付应用信息 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class PayAppRespVO extends PayAppBaseVO { + + @ApiModelProperty(value = "应用编号", required = true) + private Long id; + + @ApiModelProperty(value = "创建时间", required = true) + private Date createTime; + +} diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/app/vo/PayAppUpdateReqVO.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/app/vo/PayAppUpdateReqVO.java new file mode 100644 index 000000000..426366fc1 --- /dev/null +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/app/vo/PayAppUpdateReqVO.java @@ -0,0 +1,18 @@ +package cn.iocoder.yudao.adminserver.modules.pay.controller.app.vo; + +import lombok.*; +import java.util.*; +import io.swagger.annotations.*; +import javax.validation.constraints.*; + +@ApiModel("支付应用信息更新 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class PayAppUpdateReqVO extends PayAppBaseVO { + + @ApiModelProperty(value = "应用编号", required = true) + @NotNull(message = "应用编号不能为空") + private Long id; + +} diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/app/vo/PayAppUpdateStatusReqVO.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/app/vo/PayAppUpdateStatusReqVO.java new file mode 100644 index 000000000..28a3218ae --- /dev/null +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/app/vo/PayAppUpdateStatusReqVO.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.adminserver.modules.pay.controller.app.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotNull; + +@ApiModel("应用更新状态 Request VO") +@Data +public class PayAppUpdateStatusReqVO { + + @ApiModelProperty(value = "商户编号", required = true, example = "1024") + @NotNull(message = "商户编号不能为空") + private Long id; + + @ApiModelProperty(value = "状态", required = true, example = "1", notes = "见 SysCommonStatusEnum 枚举") + @NotNull(message = "状态不能为空") + private Integer status; + +} diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/PayChannelController.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/PayChannelController.java new file mode 100644 index 000000000..b44dfa9a9 --- /dev/null +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/PayChannelController.java @@ -0,0 +1,190 @@ +package cn.iocoder.yudao.adminserver.modules.pay.controller.channel; + +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig; +import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPubPayClient; +import org.springframework.util.Assert; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; + +import org.springframework.validation.annotation.Validated; +import org.springframework.security.access.prepost.PreAuthorize; + +import io.swagger.annotations.*; + +import javax.validation.*; +import javax.servlet.http.*; +import java.util.*; +import java.io.IOException; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; + +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; + +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*; + +import cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo.*; +import cn.iocoder.yudao.adminserver.modules.pay.convert.channel.PayChannelConvert; +import cn.iocoder.yudao.adminserver.modules.pay.service.channel.PayChannelService; +import org.springframework.web.multipart.MultipartFile; + +@Api(tags = "支付渠道") +@RestController +@RequestMapping("/pay/channel") +@Validated +public class PayChannelController { + + @Resource + private PayChannelService channelService; + + // todo 芋艿 这几个生成的方法是没用到的 您看要不删除了把? -----start + @PostMapping("/create") + @ApiOperation("创建支付渠道 ") + @PreAuthorize("@ss.hasPermission('pay:channel:create')") + public CommonResult<Long> createChannel(@Valid @RequestBody PayChannelCreateReqVO createReqVO) { + return success(channelService.createChannel(createReqVO)); + } + + @PutMapping("/update") + @ApiOperation("更新支付渠道 ") + @PreAuthorize("@ss.hasPermission('pay:channel:update')") + public CommonResult<Boolean> updateChannel(@Valid @RequestBody PayChannelUpdateReqVO updateReqVO) { + channelService.updateChannel(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @ApiOperation("删除支付渠道 ") + @ApiImplicitParam(name = "id", value = "编号", required = true) + @PreAuthorize("@ss.hasPermission('pay:channel:delete')") + public CommonResult<Boolean> deleteChannel(@RequestParam("id") Long id) { + channelService.deleteChannel(id); + return success(true); + } + + @GetMapping("/get") + @ApiOperation("获得支付渠道 ") + @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class) + @PreAuthorize("@ss.hasPermission('pay:channel:query')") + public CommonResult<PayChannelRespVO> getChannel(@RequestParam("id") Long id) { + PayChannelDO channel = channelService.getChannel(id); + return success(PayChannelConvert.INSTANCE.convert(channel)); + } + + @GetMapping("/list") + @ApiOperation("获得支付渠道列表") + @ApiImplicitParam(name = "ids", value = "编号列表", + required = true, example = "1024,2048", dataTypeClass = List.class) + @PreAuthorize("@ss.hasPermission('pay:channel:query')") + public CommonResult<List<PayChannelRespVO>> getChannelList(@RequestParam("ids") Collection<Long> ids) { + List<PayChannelDO> list = channelService.getChannelList(ids); + return success(PayChannelConvert.INSTANCE.convertList(list)); + } + + @GetMapping("/page") + @ApiOperation("获得支付渠道分页") + @PreAuthorize("@ss.hasPermission('pay:channel:query')") + public CommonResult<PageResult<PayChannelRespVO>> getChannelPage(@Valid PayChannelPageReqVO pageVO) { + PageResult<PayChannelDO> pageResult = channelService.getChannelPage(pageVO); + return success(PayChannelConvert.INSTANCE.convertPage(pageResult)); + } + + @GetMapping("/export-excel") + @ApiOperation("导出支付渠道Excel") + @PreAuthorize("@ss.hasPermission('pay:channel:export')") + @OperateLog(type = EXPORT) + public void exportChannelExcel(@Valid PayChannelExportReqVO exportReqVO, + HttpServletResponse response) throws IOException { + List<PayChannelDO> list = channelService.getChannelList(exportReqVO); + // 导出 Excel + List<PayChannelExcelVO> datas = PayChannelConvert.INSTANCE.convertList02(list); + ExcelUtils.write(response, "支付渠道.xls", "数据", PayChannelExcelVO.class, datas); + } + + // todo 芋艿 这几个生成的方法是没用到的 您看要不删除了把? -----end + + @PostMapping("/parsing-pem") + @ApiOperation("解析pem证书转换为字符串") + @PreAuthorize("@ss.hasPermission('pay:channel:parsing')") + @ApiImplicitParam(name = "file", value = "pem文件", required = true, dataTypeClass = MultipartFile.class) + public CommonResult<String> parsingPemFile(@RequestParam("file") MultipartFile file) { + return success(channelService.parsingPemFile(file)); + } + + @PostMapping("/create-wechat") + @ApiOperation("创建支付渠道 ") + @PreAuthorize("@ss.hasPermission('pay:channel:create')") + public CommonResult<Long> createWechatChannel(@Valid @RequestBody PayWechatChannelCreateReqVO reqVO) { + // 针对于 V2 或者 V3 版本的参数校验 + this.paramAdvanceCheck(reqVO.getWeChatConfig().getApiVersion(),reqVO.getWeChatConfig().getMchKey(), + reqVO.getWeChatConfig().getPrivateKeyContent(),reqVO.getWeChatConfig().getPrivateCertContent()); + + return success(channelService.createWechatChannel(reqVO)); + } + + @GetMapping("/get-wechat") + @ApiOperation("根据条件查询微信支付渠道") + @ApiImplicitParams({ + @ApiImplicitParam(name = "merchantId", value = "商户编号", + required = true, example = "1", dataTypeClass = Long.class), + @ApiImplicitParam(name = "appId", value = "应用编号", + required = true, example = "1", dataTypeClass = Long.class), + @ApiImplicitParam(name = "code", value = "支付渠道编码", + required = true, example = "wx_pub", dataTypeClass = String.class) + }) + @PreAuthorize("@ss.hasPermission('pay:channel:query')") + public CommonResult<PayWeChatChannelRespVO> getWeChatChannel( + @RequestParam Long merchantId, @RequestParam Long appId, @RequestParam String code) { + + // 獲取渠道 + PayChannelDO channel = channelService.getChannelByConditions(merchantId, appId, code); + if (channel == null) { + return success(new PayWeChatChannelRespVO()); + } + + // 拼凑数据 + PayWeChatChannelRespVO respVo = PayChannelConvert.INSTANCE.convert2(channel); + WXPayClientConfig config = (WXPayClientConfig) channel.getConfig(); + respVo.setWeChatConfig(PayChannelConvert.INSTANCE.configConvert(config)); + return success(respVo); + } + + @PutMapping("/update-wechat") + @ApiOperation("更新微信支付渠道 ") + @PreAuthorize("@ss.hasPermission('pay:channel:update')") + public CommonResult<Boolean> updateWechatChannel(@Valid @RequestBody PayWechatChannelUpdateReqVO updateReqVO) { + + // 针对于 V2 或者 V3 版本的参数校验 + this.paramAdvanceCheck(updateReqVO.getWeChatConfig().getApiVersion(),updateReqVO.getWeChatConfig().getMchKey(), + updateReqVO.getWeChatConfig().getPrivateKeyContent(),updateReqVO.getWeChatConfig().getPrivateCertContent()); + + channelService.updateWechatChannel(updateReqVO); + return success(true); + } + + /** + * 预检测微信秘钥参数 + * @param version 版本 + * @param mchKey v2版本秘钥 + * @param privateKeyContent v3版本apiclient_key + * @param privateCertContent v3版本中apiclient_cert + */ + private void paramAdvanceCheck(String version, String mchKey, String privateKeyContent, String privateCertContent) { + // 针对于 V2 或者 V3 版本的参数校验 + if (version.equals(WXPayClientConfig.API_VERSION_V2)) { + Assert.notNull(mchKey, "v2版本中商户密钥不可为空"); + } + if (version.equals(WXPayClientConfig.API_VERSION_V3)) { + Assert.notNull(privateKeyContent, "v3版本apiclient_key.pem不可为空"); + Assert.notNull(privateCertContent, "v3版本中apiclient_cert.pem不可为空"); + } + } + +} diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/vo/PayChannelBaseVO.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/vo/PayChannelBaseVO.java new file mode 100644 index 000000000..42a98b972 --- /dev/null +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/vo/PayChannelBaseVO.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo; + +import lombok.*; +import java.util.*; +import io.swagger.annotations.*; +import javax.validation.constraints.*; + +/** +* 支付渠道 + Base VO,提供给添加、修改、详细的子 VO 使用 +* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 +*/ +@Data +public class PayChannelBaseVO { + + @ApiModelProperty(value = "渠道编码", required = true) + @NotNull(message = "渠道编码不能为空") + private String code; + + @ApiModelProperty(value = "开启状态", required = true) + @NotNull(message = "开启状态不能为空") + private Integer status; + + @ApiModelProperty(value = "备注") + private String remark; + + @ApiModelProperty(value = "渠道费率,单位:百分比", required = true) + @NotNull(message = "渠道费率,单位:百分比不能为空") + private Double feeRate; + + @ApiModelProperty(value = "商户编号", required = true) + @NotNull(message = "商户编号不能为空") + private Long merchantId; + + @ApiModelProperty(value = "应用编号", required = true) + @NotNull(message = "应用编号不能为空") + private Long appId; + +} diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/vo/PayChannelCreateReqVO.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/vo/PayChannelCreateReqVO.java new file mode 100644 index 000000000..f19b0384c --- /dev/null +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/vo/PayChannelCreateReqVO.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo; + +import io.swagger.annotations.ApiModel; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@ApiModel("支付渠道 创建 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class PayChannelCreateReqVO extends PayChannelBaseVO { + + + +} diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/vo/PayChannelExcelVO.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/vo/PayChannelExcelVO.java new file mode 100644 index 000000000..a4618780a --- /dev/null +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/vo/PayChannelExcelVO.java @@ -0,0 +1,50 @@ +package cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo; + +import lombok.*; +import java.util.*; +import io.swagger.annotations.*; + +import com.alibaba.excel.annotation.ExcelProperty; + +/** + * 支付渠道 + Excel VO + * + * @author 芋艿 + */ +@Data +public class PayChannelExcelVO { + + @ExcelProperty("商户编号") + private Long id; + + @ExcelProperty("渠道编码") + private String code; + + @ExcelProperty("开启状态") + private Integer status; + + @ExcelProperty("备注") + private String remark; + + @ExcelProperty("渠道费率,单位:百分比") + private Double feeRate; + + @ExcelProperty("商户编号") + private Long merchantId; + + @ExcelProperty("应用编号") + private Long appId; + + /** + * todo @芋艿 mapStruct 存在转换问题 + * java: Can't map property "PayClientConfig payChannelDO.config" to "String payChannelExcelVO.config". + * Consider to declare/implement a mapping method: "String map(PayClientConfig value)". + */ + /// @ExcelProperty("支付渠道配置") + /// private String config; + + @ExcelProperty("创建时间") + private Date createTime; + +} diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/vo/PayChannelExportReqVO.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/vo/PayChannelExportReqVO.java new file mode 100644 index 000000000..29e3acdaf --- /dev/null +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/vo/PayChannelExportReqVO.java @@ -0,0 +1,44 @@ +package cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo; + +import lombok.*; +import java.util.*; +import io.swagger.annotations.*; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import org.springframework.format.annotation.DateTimeFormat; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@ApiModel(value = "支付渠道 Excel 导出 Request VO", description = "参数和 PayChannelPageReqVO 是一致的") +@Data +public class PayChannelExportReqVO { + + @ApiModelProperty(value = "渠道编码") + private String code; + + @ApiModelProperty(value = "开启状态") + private Integer status; + + @ApiModelProperty(value = "备注") + private String remark; + + @ApiModelProperty(value = "渠道费率,单位:百分比") + private Double feeRate; + + @ApiModelProperty(value = "商户编号") + private Long merchantId; + + @ApiModelProperty(value = "应用编号") + private Long appId; + + @ApiModelProperty(value = "支付渠道配置") + private String config; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @ApiModelProperty(value = "开始创建时间") + private Date beginCreateTime; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @ApiModelProperty(value = "结束创建时间") + private Date endCreateTime; + +} diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/vo/PayChannelPageReqVO.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/vo/PayChannelPageReqVO.java new file mode 100644 index 000000000..83851f944 --- /dev/null +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/vo/PayChannelPageReqVO.java @@ -0,0 +1,46 @@ +package cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo; + +import lombok.*; +import java.util.*; +import io.swagger.annotations.*; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import org.springframework.format.annotation.DateTimeFormat; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@ApiModel("支付渠道 分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class PayChannelPageReqVO extends PageParam { + + @ApiModelProperty(value = "渠道编码") + private String code; + + @ApiModelProperty(value = "开启状态") + private Integer status; + + @ApiModelProperty(value = "备注") + private String remark; + + @ApiModelProperty(value = "渠道费率,单位:百分比") + private Double feeRate; + + @ApiModelProperty(value = "商户编号") + private Long merchantId; + + @ApiModelProperty(value = "应用编号") + private Long appId; + + @ApiModelProperty(value = "支付渠道配置") + private String config; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @ApiModelProperty(value = "开始创建时间") + private Date beginCreateTime; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @ApiModelProperty(value = "结束创建时间") + private Date endCreateTime; + +} diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/vo/PayChannelRespVO.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/vo/PayChannelRespVO.java new file mode 100644 index 000000000..b44625b24 --- /dev/null +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/vo/PayChannelRespVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo; + +import lombok.*; +import java.util.*; +import io.swagger.annotations.*; + +@ApiModel("支付渠道 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class PayChannelRespVO extends PayChannelBaseVO { + + @ApiModelProperty(value = "商户编号", required = true) + private Long id; + + @ApiModelProperty(value = "创建时间", required = true) + private Date createTime; + +} diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/vo/PayChannelUpdateReqVO.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/vo/PayChannelUpdateReqVO.java new file mode 100644 index 000000000..658ed8ba3 --- /dev/null +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/vo/PayChannelUpdateReqVO.java @@ -0,0 +1,18 @@ +package cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo; + +import lombok.*; +import java.util.*; +import io.swagger.annotations.*; +import javax.validation.constraints.*; + +@ApiModel("支付渠道 更新 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class PayChannelUpdateReqVO extends PayChannelBaseVO { + + @ApiModelProperty(value = "商户编号", required = true) + @NotNull(message = "商户编号不能为空") + private Long id; + +} diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/vo/PayWeChatChannelRespVO.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/vo/PayWeChatChannelRespVO.java new file mode 100644 index 000000000..eafa044fd --- /dev/null +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/vo/PayWeChatChannelRespVO.java @@ -0,0 +1,65 @@ +package cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import javax.validation.Valid; +import java.util.Date; + +@ApiModel("支付微信渠道 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class PayWeChatChannelRespVO extends PayChannelBaseVO { + + @ApiModelProperty(value = "商户编号", required = true) + private Long id; + + @ApiModelProperty(value = "创建时间", required = true) + private Date createTime; + + /** + * 微信配置类 + */ + @Valid + private WeChatConfig weChatConfig; + + /** + * 微信配置类 + */ + @Data + @ApiModel("微信配置类") + public static class WeChatConfig { + + @ApiModelProperty(value = "公众号或者小程序的 appid", required = true, example = "wx041349c6f39b261b") + private String appId; + + + @ApiModelProperty(value = "商户号", required = true, example = "1545083881") + private String mchId; + + @ApiModelProperty(value = "API 版本", required = true, example = "v2") + private String apiVersion; + + // ========== V2 版本的参数 ========== + + @ApiModelProperty(value = "商户密钥", required = true, example = "0alL64UDQdaCwiKZ73ib7ypaIjMns06p") + private String mchKey; + + /// todo @aquan 暂不支持 .p12上传 后期优化 + /// apiclient_cert.p12 证书文件的绝对路径或者以 classpath: 开头的类路径. 对应的字符串 + /// private String keyContent; + + // ========== V3 版本的参数 ========== + + @ApiModelProperty(value = "apiclient_key.pem 证书对应的字符串", required = true, example = "-----BEGIN PRIVATE KEY-----") + private String privateKeyContent; + + @ApiModelProperty(value = "apiclient_cert.pem 证书对应的字符串", required = true, example = "-----BEGIN CERTIFICATE-----") + private String privateCertContent; + } + +} diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/vo/PayWechatChannelCreateReqVO.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/vo/PayWechatChannelCreateReqVO.java new file mode 100644 index 000000000..36bfd579d --- /dev/null +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/vo/PayWechatChannelCreateReqVO.java @@ -0,0 +1,68 @@ +package cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo; + +import com.fasterxml.jackson.annotation.JsonClassDescription; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import javax.validation.Valid; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * 支付渠道微信创建Request VO + * @author aquan + */ +@ApiModel("支付渠道微信创建Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class PayWechatChannelCreateReqVO extends PayChannelBaseVO { + + /** + * 微信配置类 + */ + @Valid + private WeChatConfig weChatConfig; + + /** + * 微信配置类 + */ + @Data + @ApiModel("微信配置类") + public static class WeChatConfig { + + @NotBlank(message = "公众号或者小程序的 appid不能为空") + @ApiModelProperty(value = "公众号或者小程序的 appid", required = true, example = "wx041349c6f39b261b") + private String appId; + + + @NotBlank(message = "商户号不能为空") + @ApiModelProperty(value = "商户号", required = true, example = "1545083881") + private String mchId; + + @NotNull(message = "API 版本不能为空") + @ApiModelProperty(value = "API 版本", required = true, example = "v2") + private String apiVersion; + + // ========== V2 版本的参数 ========== + + @ApiModelProperty(value = "商户密钥", required = true, example = "0alL64UDQdaCwiKZ73ib7ypaIjMns06p") + private String mchKey; + + /// todo @aquan 暂不支持 .p12上传 后期优化 + /// apiclient_cert.p12 证书文件的绝对路径或者以 classpath: 开头的类路径. 对应的字符串 + /// private String keyContent; + + // ========== V3 版本的参数 ========== + + @ApiModelProperty(value = "apiclient_key.pem 证书对应的字符串", required = true, example = "-----BEGIN PRIVATE KEY-----") + private String privateKeyContent; + + @ApiModelProperty(value = "apiclient_cert.pem 证书对应的字符串", required = true, example = "-----BEGIN CERTIFICATE-----") + private String privateCertContent; + } + +} diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/vo/PayWechatChannelUpdateReqVO.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/vo/PayWechatChannelUpdateReqVO.java new file mode 100644 index 000000000..60c814308 --- /dev/null +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/vo/PayWechatChannelUpdateReqVO.java @@ -0,0 +1,66 @@ +package cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import javax.validation.Valid; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +@ApiModel("支付渠道 更新 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class PayWechatChannelUpdateReqVO extends PayChannelBaseVO { + + @ApiModelProperty(value = "商户编号", required = true) + @NotNull(message = "商户编号不能为空") + private Long id; + + /** + * 微信配置类 + */ + @Valid + private PayWechatChannelCreateReqVO.WeChatConfig weChatConfig; + + /** + * 微信配置类 + */ + @Data + @ApiModel("微信配置类") + public static class WeChatConfig { + + @NotBlank(message = "公众号或者小程序的 appid不能为空") + @ApiModelProperty(value = "公众号或者小程序的 appid", required = true, example = "wx041349c6f39b261b") + private String appId; + + + @NotBlank(message = "商户号不能为空") + @ApiModelProperty(value = "商户号", required = true, example = "1545083881") + private String mchId; + + @NotNull(message = "API 版本不能为空") + @ApiModelProperty(value = "API 版本", required = true, example = "v2") + private String apiVersion; + + // ========== V2 版本的参数 ========== + + @ApiModelProperty(value = "商户密钥", required = true, example = "0alL64UDQdaCwiKZ73ib7ypaIjMns06p") + private String mchKey; + + /// todo @aquan 暂不支持 .p12上传 后期优化 + /// apiclient_cert.p12 证书文件的绝对路径或者以 classpath: 开头的类路径. 对应的字符串 + /// private String keyContent; + + // ========== V3 版本的参数 ========== + + @ApiModelProperty(value = "apiclient_key.pem 证书对应的字符串", required = true, example = "-----BEGIN PRIVATE KEY-----") + private String privateKeyContent; + + @ApiModelProperty(value = "apiclient_cert.pem 证书对应的字符串", required = true, example = "-----BEGIN CERTIFICATE-----") + private String privateCertContent; + } +} diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/merchant/PayMerchantController.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/merchant/PayMerchantController.java index 6ec9af43a..ceb9cb2c7 100644 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/merchant/PayMerchantController.java +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/merchant/PayMerchantController.java @@ -3,7 +3,6 @@ package cn.iocoder.yudao.adminserver.modules.pay.controller.merchant; import cn.iocoder.yudao.adminserver.modules.pay.controller.merchant.vo.*; import cn.iocoder.yudao.adminserver.modules.pay.convert.merchant.PayMerchantConvert; import cn.iocoder.yudao.adminserver.modules.pay.service.merchant.PayMerchantService; -import cn.iocoder.yudao.adminserver.modules.system.controller.user.vo.user.SysUserUpdateStatusReqVO; import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayMerchantDO; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -76,6 +75,15 @@ public class PayMerchantController { return success(PayMerchantConvert.INSTANCE.convert(merchant)); } + @GetMapping("/list-name") + @ApiOperation("根据商户名称获得支付商户信息列表") + @ApiImplicitParam(name = "name", value = "商户名称", required = true, example = "芋道", dataTypeClass = Long.class) + @PreAuthorize("@ss.hasPermission('pay:merchant:query')") + public CommonResult<List<PayMerchantRespVO>> getMerchantListByName(@RequestParam("name") String name) { + List<PayMerchantDO> merchantListDO = merchantService.getMerchantListByNameLimit(name); + return success(PayMerchantConvert.INSTANCE.convertList(merchantListDO)); + } + @GetMapping("/list") @ApiOperation("获得支付商户信息列表") @ApiImplicitParam(name = "ids", value = "编号列表", required = true, example = "1024,2048", dataTypeClass = List.class) diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/merchant/vo/PayMerchantBaseVO.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/merchant/vo/PayMerchantBaseVO.java index 8bc863792..ed7b69a3f 100644 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/merchant/vo/PayMerchantBaseVO.java +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/merchant/vo/PayMerchantBaseVO.java @@ -12,10 +12,6 @@ import javax.validation.constraints.NotNull; @Data public class PayMerchantBaseVO { - // TODO @aquan:no 应该不允许修改。啊哈哈,我的原型没画对 - @ApiModelProperty(value = "商户号") - private String no; - @ApiModelProperty(value = "商户全称", required = true) @NotNull(message = "商户全称不能为空") private String name; diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/convert/app/PayAppConvert.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/convert/app/PayAppConvert.java new file mode 100644 index 000000000..15da37cbf --- /dev/null +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/convert/app/PayAppConvert.java @@ -0,0 +1,41 @@ +package cn.iocoder.yudao.adminserver.modules.pay.convert.app; + +import java.util.*; + +import cn.iocoder.yudao.adminserver.modules.system.controller.user.vo.user.SysUserPageItemRespVO; +import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.dept.SysDeptDO; +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayAppDO; +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayMerchantDO; +import cn.iocoder.yudao.framework.common.pojo.PageResult; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; +import cn.iocoder.yudao.adminserver.modules.pay.controller.app.vo.*; + +/** + * 支付应用信息 Convert + * + * @author 芋艿 + */ +@Mapper +public interface PayAppConvert { + + PayAppConvert INSTANCE = Mappers.getMapper(PayAppConvert.class); + + PayAppPageItemRespVO pageConvert (PayAppDO bean); + + PayAppPageItemRespVO.PayMerchant convert(PayMerchantDO bean); + + PayAppDO convert(PayAppCreateReqVO bean); + + PayAppDO convert(PayAppUpdateReqVO bean); + + PayAppRespVO convert(PayAppDO bean); + + List<PayAppRespVO> convertList(List<PayAppDO> list); + + PageResult<PayAppRespVO> convertPage(PageResult<PayAppDO> page); + + List<PayAppExcelVO> convertList02(List<PayAppDO> list); + +} diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/convert/channel/PayChannelConvert.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/convert/channel/PayChannelConvert.java new file mode 100644 index 000000000..05cb4d5e4 --- /dev/null +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/convert/channel/PayChannelConvert.java @@ -0,0 +1,51 @@ +package cn.iocoder.yudao.adminserver.modules.pay.convert.channel; + +import java.util.*; + +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO; +import cn.iocoder.yudao.framework.common.pojo.PageResult; + +import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; +import cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo.*; + +/** + * 支付渠道 + Convert + * + * @author 芋艿 + */ +@Mapper +public interface PayChannelConvert { + + PayChannelConvert INSTANCE = Mappers.getMapper(PayChannelConvert.class); + + @Mapping(target = "config",ignore = true) + PayChannelDO convert(PayWechatChannelCreateReqVO bean); + + @Mapping(target = "config",ignore = true) + PayChannelDO convert(PayWechatChannelUpdateReqVO bean); + + PayChannelDO convert(PayChannelCreateReqVO bean); + + PayChannelDO convert(PayChannelUpdateReqVO bean); + + PayChannelRespVO convert(PayChannelDO bean); + + List<PayChannelRespVO> convertList(List<PayChannelDO> list); + + PageResult<PayChannelRespVO> convertPage(PageResult<PayChannelDO> page); + + List<PayChannelExcelVO> convertList02(List<PayChannelDO> list); + + WXPayClientConfig configConvert(PayWechatChannelCreateReqVO.WeChatConfig bean); + + WXPayClientConfig configConvert(PayWechatChannelUpdateReqVO.WeChatConfig bean); + + @Mapping(target = "weChatConfig",ignore = true) + PayWeChatChannelRespVO convert2(PayChannelDO bean); + + PayWeChatChannelRespVO.WeChatConfig configConvert(WXPayClientConfig bean); +} diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/dal/mysql/app/PayAppMapper.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/dal/mysql/app/PayAppMapper.java new file mode 100644 index 000000000..200ccf296 --- /dev/null +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/dal/mysql/app/PayAppMapper.java @@ -0,0 +1,44 @@ +package cn.iocoder.yudao.adminserver.modules.pay.dal.mysql.app; + +import java.util.*; + +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayAppDO; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import org.apache.ibatis.annotations.Mapper; +import cn.iocoder.yudao.adminserver.modules.pay.controller.app.vo.*; + +/** + * 支付应用信息 Mapper + * + * @author 芋艿 + */ +@Mapper +public interface PayAppMapper extends BaseMapperX<PayAppDO> { + + default PageResult<PayAppDO> selectPage(PayAppPageReqVO reqVO,Collection<Long> merchantIds) { + return selectPage(reqVO, new QueryWrapperX<PayAppDO>() + .likeIfPresent("name", reqVO.getName()) + .eqIfPresent("status", reqVO.getStatus()) + .eqIfPresent("remark", reqVO.getRemark()) + .eqIfPresent("pay_notify_url", reqVO.getPayNotifyUrl()) + .eqIfPresent("refund_notify_url", reqVO.getRefundNotifyUrl()) + .inIfPresent("merchant_id", merchantIds) + .betweenIfPresent("create_time", reqVO.getBeginCreateTime(), reqVO.getEndCreateTime()) + .orderByDesc("id")); + } + + default List<PayAppDO> selectList(PayAppExportReqVO reqVO) { + return selectList(new QueryWrapperX<PayAppDO>() + .likeIfPresent("name", reqVO.getName()) + .eqIfPresent("status", reqVO.getStatus()) + .eqIfPresent("remark", reqVO.getRemark()) + .eqIfPresent("pay_notify_url", reqVO.getPayNotifyUrl()) + .eqIfPresent("refund_notify_url", reqVO.getRefundNotifyUrl()) + .eqIfPresent("merchant_id", reqVO.getMerchantId()) + .betweenIfPresent("create_time", reqVO.getBeginCreateTime(), reqVO.getEndCreateTime()) + .orderByDesc("id") ); + } + +} diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/dal/mysql/channel/PayChannelMapper.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/dal/mysql/channel/PayChannelMapper.java new file mode 100644 index 000000000..2134f08d3 --- /dev/null +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/dal/mysql/channel/PayChannelMapper.java @@ -0,0 +1,47 @@ +package cn.iocoder.yudao.adminserver.modules.pay.dal.mysql.channel; + +import java.util.*; + +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import org.apache.ibatis.annotations.Mapper; +import cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo.*; + +/** + * 支付渠道 + Mapper + * + * @author 芋艿 + */ +@Mapper +public interface PayChannelMapper extends BaseMapperX<PayChannelDO> { + + default PageResult<PayChannelDO> selectPage(PayChannelPageReqVO reqVO) { + return selectPage(reqVO, new QueryWrapperX<PayChannelDO>() + .eqIfPresent("code", reqVO.getCode()) + .eqIfPresent("status", reqVO.getStatus()) + .eqIfPresent("remark", reqVO.getRemark()) + .eqIfPresent("fee_rate", reqVO.getFeeRate()) + .eqIfPresent("merchant_id", reqVO.getMerchantId()) + .eqIfPresent("app_id", reqVO.getAppId()) + .eqIfPresent("config", reqVO.getConfig()) + .betweenIfPresent("create_time", reqVO.getBeginCreateTime(), reqVO.getEndCreateTime()) + .orderByDesc("id") ); + } + + default List<PayChannelDO> selectList(PayChannelExportReqVO reqVO) { + return selectList(new QueryWrapperX<PayChannelDO>() + .eqIfPresent("code", reqVO.getCode()) + .eqIfPresent("status", reqVO.getStatus()) + .eqIfPresent("remark", reqVO.getRemark()) + .eqIfPresent("fee_rate", reqVO.getFeeRate()) + .eqIfPresent("merchant_id", reqVO.getMerchantId()) + .eqIfPresent("app_id", reqVO.getAppId()) + .eqIfPresent("config", reqVO.getConfig()) + .betweenIfPresent("create_time", reqVO.getBeginCreateTime(), reqVO.getEndCreateTime()) + .orderByDesc("id") ); + } + +} diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/service/app/PayAppService.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/service/app/PayAppService.java new file mode 100644 index 000000000..8baa6516d --- /dev/null +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/service/app/PayAppService.java @@ -0,0 +1,81 @@ +package cn.iocoder.yudao.adminserver.modules.pay.service.app; + +import java.util.*; +import javax.validation.*; + +import cn.iocoder.yudao.adminserver.modules.pay.controller.app.vo.*; +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayAppDO; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import org.springframework.web.multipart.MultipartFile; + +/** + * 支付应用信息 Service 接口 + * + * @author 芋艿 + */ +public interface PayAppService { + + /** + * 创建支付应用信息 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createApp(@Valid PayAppCreateReqVO createReqVO); + + /** + * 更新支付应用信息 + * + * @param updateReqVO 更新信息 + */ + void updateApp(@Valid PayAppUpdateReqVO updateReqVO); + + /** + * 删除支付应用信息 + * + * @param id 编号 + */ + void deleteApp(Long id); + + /** + * 获得支付应用信息 + * + * @param id 编号 + * @return 支付应用信息 + */ + PayAppDO getApp(Long id); + + /** + * 获得支付应用信息列表 + * + * @param ids 编号 + * @return 支付应用信息列表 + */ + List<PayAppDO> getAppList(Collection<Long> ids); + + /** + * 获得支付应用信息分页 + * + * @param pageReqVO 分页查询 + * @return 支付应用信息分页 + */ + PageResult<PayAppDO> getAppPage(PayAppPageReqVO pageReqVO); + + /** + * 获得支付应用信息列表, 用于 Excel 导出 + * + * @param exportReqVO 查询条件 + * @return 支付应用信息列表 + */ + List<PayAppDO> getAppList(PayAppExportReqVO exportReqVO); + + /** + * 修改应用信息状态 + * + * @param id 应用编号 + * @param status 状态{@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum} + */ + void updateAppStatus(Long id, Integer status); + + +} diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/service/app/impl/PayAppServiceImpl.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/service/app/impl/PayAppServiceImpl.java new file mode 100644 index 000000000..499abe76e --- /dev/null +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/service/app/impl/PayAppServiceImpl.java @@ -0,0 +1,140 @@ +package cn.iocoder.yudao.adminserver.modules.pay.service.app.impl; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.adminserver.modules.pay.service.merchant.PayMerchantService; +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayAppDO; +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayMerchantDO; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import com.google.common.annotations.VisibleForTesting; +import org.springframework.stereotype.Service; +import javax.annotation.Resource; +import org.springframework.validation.annotation.Validated; + +import java.util.*; +import cn.iocoder.yudao.adminserver.modules.pay.controller.app.vo.*; +import cn.iocoder.yudao.framework.common.pojo.PageResult; + +import cn.iocoder.yudao.adminserver.modules.pay.convert.app.PayAppConvert; +import cn.iocoder.yudao.adminserver.modules.pay.dal.mysql.app.PayAppMapper; +import cn.iocoder.yudao.adminserver.modules.pay.service.app.PayAppService; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants.*; + +/** + * 支付应用信息 Service 实现类 + * + * @author aquan + */ +@Service +@Validated +public class PayAppServiceImpl implements PayAppService { + + @Resource + private PayAppMapper appMapper; + + /** + * 商户 service 组件 + */ + @Resource + private PayMerchantService merchantService; + + @Override + public Long createApp(PayAppCreateReqVO createReqVO) { + // 插入 + PayAppDO app = PayAppConvert.INSTANCE.convert(createReqVO); + appMapper.insert(app); + // 返回 + return app.getId(); + } + + @Override + public void updateApp(PayAppUpdateReqVO updateReqVO) { + // 校验存在 + this.validateAppExists(updateReqVO.getId()); + // 更新 + PayAppDO updateObj = PayAppConvert.INSTANCE.convert(updateReqVO); + appMapper.updateById(updateObj); + } + + @Override + public void deleteApp(Long id) { + // 校验存在 + this.validateAppExists(id); + // 删除 + appMapper.deleteById(id); + } + + private void validateAppExists(Long id) { + if (appMapper.selectById(id) == null) { + throw exception(APP_NOT_EXISTS); + } + } + + @Override + public PayAppDO getApp(Long id) { + return appMapper.selectById(id); + } + + @Override + public List<PayAppDO> getAppList(Collection<Long> ids) { + return appMapper.selectBatchIds(ids); + } + + @Override + public PageResult<PayAppDO> getAppPage(PayAppPageReqVO pageReqVO) { + return appMapper.selectPage(pageReqVO,this.getMerchantCondition(pageReqVO.getMerchantName())); + } + + @Override + public List<PayAppDO> getAppList(PayAppExportReqVO exportReqVO) { + return appMapper.selectList(exportReqVO); + } + + /** + * 获取商户编号集合,根据商户名称模糊查询得到所有的商户编号集合 + * @param merchantName 商户名称 + * @return 商户编号集合 + */ + private Set<Long> getMerchantCondition(String merchantName) { + if (StrUtil.isBlank(merchantName)) { + return Collections.emptySet(); + } + return CollectionUtils.convertSet(merchantService.getMerchantListByName(merchantName), PayMerchantDO::getId); + } + + /** + * 修改应用信息状态 + * + * @param id 应用编号 + * @param status 状态{@link CommonStatusEnum} + */ + @Override + public void updateAppStatus(Long id, Integer status) { + // 校验商户存在 + this.checkAppExists(id); + // 更新状态 + PayAppDO app = new PayAppDO(); + app.setId(id); + app.setStatus(status); + appMapper.updateById(app); + } + + + /** + * 检查商户是否存在 + * @param id 商户编号 + */ + @VisibleForTesting + public void checkAppExists(Long id) { + + if (id == null) { + return; + } + PayAppDO payApp = appMapper.selectById(id); + if (payApp == null) { + throw exception(APP_NOT_EXISTS); + } + } +} diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/service/channel/PayChannelService.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/service/channel/PayChannelService.java new file mode 100644 index 000000000..420c23123 --- /dev/null +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/service/channel/PayChannelService.java @@ -0,0 +1,130 @@ +package cn.iocoder.yudao.adminserver.modules.pay.service.channel; + +import java.util.*; +import javax.validation.*; + +import cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo.*; +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import org.springframework.web.multipart.MultipartFile; + +/** + * 支付渠道 + * Service 接口 + * + * @author 芋艿 + */ +public interface PayChannelService { + + + /** + * 创建支付渠道 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createChannel(@Valid PayChannelCreateReqVO createReqVO); + + /** + * 更新支付渠道 + * + * @param updateReqVO 更新信息 + */ + void updateChannel(@Valid PayChannelUpdateReqVO updateReqVO); + + /** + * 删除支付渠道 + * + * @param id 编号 + */ + void deleteChannel(Long id); + + /** + * 获得支付渠道 + * + * @param id 编号 + * @return 支付渠道 + */ + PayChannelDO getChannel(Long id); + + /** + * 获得支付渠道 + * 列表 + * + * @param ids 编号 + * @return 支付渠道 + * 列表 + */ + List<PayChannelDO> getChannelList(Collection<Long> ids); + + /** + * 获得支付渠道 + * 分页 + * + * @param pageReqVO 分页查询 + * @return 支付渠道 + * 分页 + */ + PageResult<PayChannelDO> getChannelPage(PayChannelPageReqVO pageReqVO); + + /** + * 获得支付渠道 + * 列表, 用于 Excel 导出 + * + * @param exportReqVO 查询条件 + * @return 支付渠道 + * 列表 + */ + List<PayChannelDO> getChannelList(PayChannelExportReqVO exportReqVO); + + /** + * 根据支付应用ID集合获取所有的支付渠道 + * + * @param payIds 支付应用编号集合 + * @return 支付渠道 + */ + List<PayChannelDO> getSimpleChannels(Collection<Long> payIds); + + /** + * 解析pem文件获取公钥私钥字符串 + * + * @param file pem公私钥文件 + * @return 解析后的字符串 + */ + String parsingPemFile(MultipartFile file); + + /** + * 创建微信的渠道配置 + * + * @param reqVO 创建信息 + * @return 创建结果 + */ + Long createWechatChannel(PayWechatChannelCreateReqVO reqVO); + + /** + * 根据条件获取通道数量 + * + * @param merchantId 商户编号 + * @param appid 应用编号 + * @param code 通道编码 + * @return 数量 + */ + Integer getChannelCountByConditions(Long merchantId, Long appid, String code); + + /** + * 根据条件获取通道 + * + * @param merchantId 商户编号 + * @param appid 应用编号 + * @param code 通道编码 + * @return 数量 + */ + PayChannelDO getChannelByConditions(Long merchantId, Long appid, String code); + + /** + * 更新微信支付渠道 + * + * @param updateReqVO 更新信息 + */ + void updateWechatChannel(PayWechatChannelUpdateReqVO updateReqVO); +} diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/service/channel/impl/PayChannelServiceImpl.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/service/channel/impl/PayChannelServiceImpl.java new file mode 100644 index 000000000..4412ccf90 --- /dev/null +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/service/channel/impl/PayChannelServiceImpl.java @@ -0,0 +1,196 @@ +package cn.iocoder.yudao.adminserver.modules.pay.service.channel.impl; + +import cn.hutool.core.io.IoUtil; +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.json.JsonMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; + +import org.springframework.util.Assert; +import org.springframework.validation.annotation.Validated; + +import java.io.IOException; +import java.util.*; + +import cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo.*; +import cn.iocoder.yudao.framework.common.pojo.PageResult; + +import cn.iocoder.yudao.adminserver.modules.pay.convert.channel.PayChannelConvert; +import cn.iocoder.yudao.adminserver.modules.pay.dal.mysql.channel.PayChannelMapper; +import cn.iocoder.yudao.adminserver.modules.pay.service.channel.PayChannelService; +import org.springframework.web.multipart.MultipartFile; + + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants.*; + +/** + * 支付渠道 + * Service 实现类 + * + * @author 芋艿 + */ +@Slf4j +@Service +@Validated +public class PayChannelServiceImpl implements PayChannelService { + + @Resource + private PayChannelMapper channelMapper; + + @Override + public Long createChannel(PayChannelCreateReqVO createReqVO) { + // 插入 + PayChannelDO channel = PayChannelConvert.INSTANCE.convert(createReqVO); + channelMapper.insert(channel); + // 返回 + return channel.getId(); + } + + @Override + public void updateChannel(PayChannelUpdateReqVO updateReqVO) { + // 校验存在 + this.validateChannelExists(updateReqVO.getId()); + // 更新 + PayChannelDO updateObj = PayChannelConvert.INSTANCE.convert(updateReqVO); + channelMapper.updateById(updateObj); + } + + @Override + public void deleteChannel(Long id) { + // 校验存在 + this.validateChannelExists(id); + // 删除 + channelMapper.deleteById(id); + } + + private void validateChannelExists(Long id) { + if (channelMapper.selectById(id) == null) { + throw exception(CHANNEL_NOT_EXISTS); + } + } + + @Override + public PayChannelDO getChannel(Long id) { + return channelMapper.selectById(id); + } + + @Override + public List<PayChannelDO> getChannelList(Collection<Long> ids) { + return channelMapper.selectBatchIds(ids); + } + + @Override + public PageResult<PayChannelDO> getChannelPage(PayChannelPageReqVO pageReqVO) { + return channelMapper.selectPage(pageReqVO); + } + + @Override + public List<PayChannelDO> getChannelList(PayChannelExportReqVO exportReqVO) { + return channelMapper.selectList(exportReqVO); + } + + /** + * 根据支付应用ID集合获取所有的支付渠道 + * + * @param payIds 支付应用编号集合 + * @return 支付渠道 + */ + @Override + public List<PayChannelDO> getSimpleChannels(Collection<Long> payIds) { + return channelMapper.selectList(new QueryWrapper<PayChannelDO>().lambda().in(PayChannelDO::getAppId, payIds)); + } + + /** + * 解析pem文件获取公钥私钥字符串 + * + * @param file pem公私钥文件 + * @return 解析后的字符串 + */ + @Override + public String parsingPemFile(MultipartFile file) { + try { + return IoUtil.readUtf8(file.getInputStream()); + } catch (IOException e) { + log.error("[parsingPemToString]读取pem[{}]文件错误", file.getOriginalFilename()); + throw exception(CHANNEL_KEY_READ_ERROR); + } + } + + /** + * 创建微信的渠道配置 + * + * @param reqVO 创建信息 + * @return 创建结果 + */ + @Override + public Long createWechatChannel(PayWechatChannelCreateReqVO reqVO) { + + // 判断是否有重复的有责无法新增 + Integer channelCount = this.getChannelCountByConditions(reqVO.getMerchantId(), reqVO.getAppId(), reqVO.getCode()); + if (channelCount > 0) { + throw exception(EXIST_SAME_CHANNEL_ERROR); + } + + PayChannelDO channel = PayChannelConvert.INSTANCE.convert(reqVO); + WXPayClientConfig config = PayChannelConvert.INSTANCE.configConvert(reqVO.getWeChatConfig()); + channel.setConfig(config); + channelMapper.insert(channel); + return channel.getId(); + } + + /** + * 根据条件获取通道数量 + * + * @param merchantId 商户编号 + * @param appid 应用编号 + * @param code 通道编码 + * @return 数量 + */ + @Override + public Integer getChannelCountByConditions(Long merchantId, Long appid, String code) { + return this.channelMapper.selectCount(new QueryWrapper<PayChannelDO>().lambda() + .eq(PayChannelDO::getMerchantId, merchantId) + .eq(PayChannelDO::getAppId, appid) + .eq(PayChannelDO::getCode, code) + ); + } + + /** + * 根据条件获取通道 + * + * @param merchantId 商户编号 + * @param appid 应用编号 + * @param code 通道编码 + * @return 数量 + */ + @Override + public PayChannelDO getChannelByConditions(Long merchantId, Long appid, String code) { + return this.channelMapper.selectOne((new QueryWrapper<PayChannelDO>().lambda() + .eq(PayChannelDO::getMerchantId, merchantId) + .eq(PayChannelDO::getAppId, appid) + .eq(PayChannelDO::getCode, code) + )); + } + + /** + * 更新微信支付渠道 + * + * @param updateReqVO 更新信息 + */ + @Override + public void updateWechatChannel(PayWechatChannelUpdateReqVO updateReqVO) { + // 校验存在 + this.validateChannelExists(updateReqVO.getId()); + PayChannelDO channel = PayChannelConvert.INSTANCE.convert(updateReqVO); + WXPayClientConfig config = PayChannelConvert.INSTANCE.configConvert(updateReqVO.getWeChatConfig()); + channel.setConfig(config); + this.channelMapper.updateById(channel); + } +} diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/service/merchant/PayMerchantService.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/service/merchant/PayMerchantService.java index db7926c12..be0e66039 100644 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/service/merchant/PayMerchantService.java +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/service/merchant/PayMerchantService.java @@ -2,9 +2,13 @@ package cn.iocoder.yudao.adminserver.modules.pay.service.merchant; import java.util.*; import javax.validation.*; + +import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.adminserver.modules.pay.controller.merchant.vo.*; +import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.dept.SysDeptDO; import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayMerchantDO; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; /** * 支付商户信息 Service 接口 @@ -69,9 +73,46 @@ public interface PayMerchantService { /** * 修改商户状态 - * @param id 商户编号 + * + * @param id 商户编号 * @param status 状态 */ void updateMerchantStatus(Long id, Integer status); + /** + * 根据商户名称模糊查询商户集合 + * + * @param merchantName 商户名称 + * @return 商户集合 + */ + List<PayMerchantDO> getMerchantListByName(String merchantName); + + /** + * 根据商户名称模糊查询一定数量的商户集合 + * @param merchantName 商户名称 + * @return 商户集合 + */ + List<PayMerchantDO> getMerchantListByNameLimit(String merchantName); + + /** + * 获得指定编号的商户列表 + * + * @param merchantIds 商户编号数组 + * @return 商户列表 + */ + List<PayMerchantDO> getSimpleMerchants(Collection<Long> merchantIds); + + /** + * 获得指定编号的商户 Map + * + * @param merchantIds 商户编号数组 + * @return 商户 Map + */ + default Map<Long, PayMerchantDO> getMerchantMap(Collection<Long> merchantIds){ + if (CollUtil.isEmpty(merchantIds)) { + return Collections.emptyMap(); + } + List<PayMerchantDO> list = getSimpleMerchants(merchantIds); + return CollectionUtils.convertMap(list, PayMerchantDO::getId); + } } diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/service/merchant/impl/PayMerchantServiceImpl.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/service/merchant/impl/PayMerchantServiceImpl.java index b329de2c5..556792d2f 100644 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/service/merchant/impl/PayMerchantServiceImpl.java +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/service/merchant/impl/PayMerchantServiceImpl.java @@ -10,6 +10,8 @@ import cn.iocoder.yudao.adminserver.modules.pay.dal.mysql.merchant.PayMerchantMa import cn.iocoder.yudao.adminserver.modules.pay.service.merchant.PayMerchantService; import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayMerchantDO; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.google.common.annotations.VisibleForTesting; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; @@ -24,7 +26,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU /** * 支付商户信息 Service 实现类 * - * @author 芋艿 + * @author aquan */ @Service @Validated @@ -37,10 +39,7 @@ public class PayMerchantServiceImpl implements PayMerchantService { public Long createMerchant(PayMerchantCreateReqVO createReqVO) { // 插入 PayMerchantDO merchant = PayMerchantConvert.INSTANCE.convert(createReqVO); - // 根据 年月日时分秒毫秒 生成时间戳 - // TODO @aquan:生成 no 可以单独一个小方法 - String merchantNo = "M" + DateUtil.format(LocalDateTime.now(),"yyyyMMddHHmmssSSS"); - merchant.setNo(merchantNo); + merchant.setNo(this.generateMerchantNo()); merchantMapper.insert(merchant); // 返回 return merchant.getId(); @@ -106,6 +105,35 @@ public class PayMerchantServiceImpl implements PayMerchantService { merchantMapper.updateById(merchant); } + /** + * 根据商户名称模糊查询商户集合 + * + * @param merchantName 商户名称 + * @return 商户集合 + */ + @Override + public List<PayMerchantDO> getMerchantListByName(String merchantName) { + return this.merchantMapper.selectList(new QueryWrapper<PayMerchantDO>() + .lambda().likeRight(PayMerchantDO::getName, merchantName)); + } + + /** + * 根据商户名称模糊查询一定数量的商户集合 + * + * @param merchantName 商户名称 + * @return 商户集合 + */ + @Override + public List<PayMerchantDO> getMerchantListByNameLimit(String merchantName) { + + LambdaQueryWrapper<PayMerchantDO> queryWrapper = new QueryWrapper<PayMerchantDO>().lambda() + .select(PayMerchantDO::getId, PayMerchantDO::getName) + .likeRight(PayMerchantDO::getName, merchantName) + .last("limit 200"); + + return this.merchantMapper.selectList(queryWrapper); + } + /** * 检查商户是否存在 * @param id 商户编号 @@ -121,5 +149,24 @@ public class PayMerchantServiceImpl implements PayMerchantService { } } + /** + * 获得指定编号的商户列表 + * + * @param merchantIds 商户编号数组 + * @return 商户列表 + */ + @Override + public List<PayMerchantDO> getSimpleMerchants(Collection<Long> merchantIds) { + return merchantMapper.selectBatchIds(merchantIds); + } + + /** + * 根据年月日时分秒毫秒生成商户号 + * @return 商户号 + */ + private String generateMerchantNo(){ + return "M" + DateUtil.format(LocalDateTime.now(),"yyyyMMddHHmmssSSS"); + } + } diff --git a/yudao-admin-server/src/main/resources/application-local.yaml b/yudao-admin-server/src/main/resources/application-local.yaml index 4f45d9476..60197c27c 100644 --- a/yudao-admin-server/src/main/resources/application-local.yaml +++ b/yudao-admin-server/src/main/resources/application-local.yaml @@ -44,16 +44,16 @@ spring: datasource: master: name: ruoyi-vue-pro - url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.master.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT + url: jdbc:mysql://rm-j6cxl87683w973f78ho.mysql.rds.aliyuncs.com:3306/${spring.datasource.dynamic.datasource.master.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT driver-class-name: com.mysql.jdbc.Driver - username: root - password: 123456 + username: chenquan + password: Miraclequan@990429 slave: # 模拟从库,可根据自己需要修改 name: ruoyi-vue-pro - url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.slave.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT + url: jdbc:mysql://rm-j6cxl87683w973f78ho.mysql.rds.aliyuncs.com:3306/${spring.datasource.dynamic.datasource.slave.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT driver-class-name: com.mysql.jdbc.Driver - username: root - password: 123456 + username: chenquan + password: Miraclequan@990429 # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 redis: diff --git a/yudao-admin-server/src/main/resources/application.yaml b/yudao-admin-server/src/main/resources/application.yaml index cad787dfe..26af6c0ed 100644 --- a/yudao-admin-server/src/main/resources/application.yaml +++ b/yudao-admin-server/src/main/resources/application.yaml @@ -60,5 +60,6 @@ yudao: constants-class-list: - cn.iocoder.yudao.adminserver.modules.infra.enums.InfErrorCodeConstants - cn.iocoder.yudao.adminserver.modules.system.enums.SysErrorCodeConstants - + pay: + payReturnUrl: http://127.0.0.1 debug: false diff --git a/yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/pay/app/service/PayAppServiceTest.java b/yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/pay/app/service/PayAppServiceTest.java new file mode 100644 index 000000000..8bea96b5f --- /dev/null +++ b/yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/pay/app/service/PayAppServiceTest.java @@ -0,0 +1,196 @@ +package cn.iocoder.yudao.adminserver.modules.pay.app.service; + +import javax.annotation.Resource; + +import cn.iocoder.yudao.adminserver.BaseDbUnitTest; +import cn.iocoder.yudao.adminserver.modules.pay.controller.app.vo.PayAppCreateReqVO; +import cn.iocoder.yudao.adminserver.modules.pay.controller.app.vo.PayAppExportReqVO; +import cn.iocoder.yudao.adminserver.modules.pay.controller.app.vo.PayAppPageReqVO; +import cn.iocoder.yudao.adminserver.modules.pay.controller.app.vo.PayAppUpdateReqVO; +import cn.iocoder.yudao.adminserver.modules.pay.dal.mysql.app.PayAppMapper; +import cn.iocoder.yudao.adminserver.modules.pay.service.app.impl.PayAppServiceImpl; +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayAppDO; +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; +import cn.iocoder.yudao.framework.common.pojo.PageResult; + +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; +import java.util.*; + +import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants.*; +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.common.util.date.DateUtils.buildTime; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static org.junit.jupiter.api.Assertions.*; + +import static org.mockito.Mockito.*; + +/** +* {@link PayAppServiceImpl} 的单元测试类 +* +* @author 芋艿 +*/ +@Import(PayAppServiceImpl.class) +public class PayAppServiceTest extends BaseDbUnitTest { + + @Resource + private PayAppServiceImpl appService; + + @Resource + private PayAppMapper appMapper; + + @Test + public void testCreateApp_success() { + // 准备参数 + PayAppCreateReqVO reqVO = randomPojo(PayAppCreateReqVO.class); + + // 调用 + Long appId = appService.createApp(reqVO); + // 断言 + assertNotNull(appId); + // 校验记录的属性是否正确 + PayAppDO app = appMapper.selectById(appId); + assertPojoEquals(reqVO, app); + } + + @Test + public void testUpdateApp_success() { + // mock 数据 + PayAppDO dbApp = randomPojo(PayAppDO.class); + appMapper.insert(dbApp);// @Sql: 先插入出一条存在的数据 + // 准备参数 + PayAppUpdateReqVO reqVO = randomPojo(PayAppUpdateReqVO.class, o -> { + o.setId(dbApp.getId()); // 设置更新的 ID + }); + + // 调用 + appService.updateApp(reqVO); + // 校验是否更新正确 + PayAppDO app = appMapper.selectById(reqVO.getId()); // 获取最新的 + assertPojoEquals(reqVO, app); + } + + @Test + public void testUpdateApp_notExists() { + // 准备参数 + PayAppUpdateReqVO reqVO = randomPojo(PayAppUpdateReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> appService.updateApp(reqVO), APP_NOT_EXISTS); + } + + @Test + public void testDeleteApp_success() { + // mock 数据 + PayAppDO dbApp = randomPojo(PayAppDO.class); + appMapper.insert(dbApp);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbApp.getId(); + + // 调用 + appService.deleteApp(id); + // 校验数据不存在了 + assertNull(appMapper.selectById(id)); + } + + @Test + public void testDeleteApp_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> appService.deleteApp(id), APP_NOT_EXISTS); + } + + @Test // TODO 请修改 null 为需要的值 + public void testGetAppPage() { + // mock 数据 + PayAppDO dbApp = randomPojo(PayAppDO.class, o -> { // 等会查询到 + o.setName(null); + o.setStatus(null); + o.setRemark(null); + o.setPayNotifyUrl(null); + o.setRefundNotifyUrl(null); + o.setMerchantId(null); + o.setCreateTime(null); + }); + appMapper.insert(dbApp); + // 测试 name 不匹配 + appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setName(null))); + // 测试 status 不匹配 + appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setStatus(null))); + // 测试 remark 不匹配 + appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setRemark(null))); + // 测试 payNotifyUrl 不匹配 + appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setPayNotifyUrl(null))); + // 测试 refundNotifyUrl 不匹配 + appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setRefundNotifyUrl(null))); + // 测试 merchantId 不匹配 + appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setMerchantId(null))); + // 测试 createTime 不匹配 + appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setCreateTime(null))); + // 准备参数 + PayAppPageReqVO reqVO = new PayAppPageReqVO(); + reqVO.setName(null); + reqVO.setStatus(null); + reqVO.setRemark(null); + reqVO.setPayNotifyUrl(null); + reqVO.setRefundNotifyUrl(null); + reqVO.setBeginCreateTime(null); + reqVO.setEndCreateTime(null); + + // 调用 + PageResult<PayAppDO> pageResult = appService.getAppPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbApp, pageResult.getList().get(0)); + } + + @Test // TODO 请修改 null 为需要的值 + public void testGetAppList() { + // mock 数据 + PayAppDO dbApp = randomPojo(PayAppDO.class, o -> { // 等会查询到 + o.setName(null); + o.setStatus(null); + o.setRemark(null); + o.setPayNotifyUrl(null); + o.setRefundNotifyUrl(null); + o.setMerchantId(null); + o.setCreateTime(null); + }); + appMapper.insert(dbApp); + // 测试 name 不匹配 + appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setName(null))); + // 测试 status 不匹配 + appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setStatus(null))); + // 测试 remark 不匹配 + appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setRemark(null))); + // 测试 payNotifyUrl 不匹配 + appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setPayNotifyUrl(null))); + // 测试 refundNotifyUrl 不匹配 + appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setRefundNotifyUrl(null))); + // 测试 merchantId 不匹配 + appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setMerchantId(null))); + // 测试 createTime 不匹配 + appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setCreateTime(null))); + // 准备参数 + PayAppExportReqVO reqVO = new PayAppExportReqVO(); + reqVO.setName(null); + reqVO.setStatus(null); + reqVO.setRemark(null); + reqVO.setPayNotifyUrl(null); + reqVO.setRefundNotifyUrl(null); + reqVO.setMerchantId(null); + reqVO.setBeginCreateTime(null); + reqVO.setEndCreateTime(null); + + // 调用 + List<PayAppDO> list = appService.getAppList(reqVO); + // 断言 + assertEquals(1, list.size()); + assertPojoEquals(dbApp, list.get(0)); + } + +} diff --git a/yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/pay/channel/PayChannelServiceTest.java b/yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/pay/channel/PayChannelServiceTest.java new file mode 100644 index 000000000..5e1875f8d --- /dev/null +++ b/yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/pay/channel/PayChannelServiceTest.java @@ -0,0 +1,204 @@ +package cn.iocoder.yudao.adminserver.modules.pay.channel; + +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO; +import org.junit.jupiter.api.Test; + +import javax.annotation.Resource; + +import cn.iocoder.yudao.adminserver.BaseDbUnitTest; +import cn.iocoder.yudao.adminserver.modules.pay.service.channel.impl.PayChannelServiceImpl; +import cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo.*; +import cn.iocoder.yudao.adminserver.modules.pay.dal.mysql.channel.PayChannelMapper; +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; +import cn.iocoder.yudao.framework.common.pojo.PageResult; + +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; +import java.util.*; + +import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants.*; +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.common.util.date.DateUtils.buildTime; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static org.junit.jupiter.api.Assertions.*; + +import static org.mockito.Mockito.*; + +/** +* {@link PayChannelServiceImpl} 的单元测试类 +* +* @author 芋艿 +*/ +@Import(PayChannelServiceImpl.class) +public class PayChannelServiceTest extends BaseDbUnitTest { + + @Resource + private PayChannelServiceImpl channelService; + + @Resource + private PayChannelMapper channelMapper; + + @Test + public void testCreateChannel_success() { + // 准备参数 + PayChannelCreateReqVO reqVO = randomPojo(PayChannelCreateReqVO.class); + + // 调用 + Long channelId = channelService.createChannel(reqVO); + // 断言 + assertNotNull(channelId); + // 校验记录的属性是否正确 + PayChannelDO channel = channelMapper.selectById(channelId); + assertPojoEquals(reqVO, channel); + } + + @Test + public void testUpdateChannel_success() { + // mock 数据 + PayChannelDO dbChannel = randomPojo(PayChannelDO.class); + channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据 + // 准备参数 + PayChannelUpdateReqVO reqVO = randomPojo(PayChannelUpdateReqVO.class, o -> { + o.setId(dbChannel.getId()); // 设置更新的 ID + }); + + // 调用 + channelService.updateChannel(reqVO); + // 校验是否更新正确 + PayChannelDO channel = channelMapper.selectById(reqVO.getId()); // 获取最新的 + assertPojoEquals(reqVO, channel); + } + + @Test + public void testUpdateChannel_notExists() { + // 准备参数 + PayChannelUpdateReqVO reqVO = randomPojo(PayChannelUpdateReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> channelService.updateChannel(reqVO), CHANNEL_NOT_EXISTS); + } + + @Test + public void testDeleteChannel_success() { + // mock 数据 + PayChannelDO dbChannel = randomPojo(PayChannelDO.class); + channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbChannel.getId(); + + // 调用 + channelService.deleteChannel(id); + // 校验数据不存在了 + assertNull(channelMapper.selectById(id)); + } + + @Test + public void testDeleteChannel_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> channelService.deleteChannel(id), CHANNEL_NOT_EXISTS); + } + + @Test // TODO 请修改 null 为需要的值 + public void testGetChannelPage() { + // mock 数据 + PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> { // 等会查询到 + o.setCode(null); + o.setStatus(null); + o.setRemark(null); + o.setFeeRate(null); + o.setMerchantId(null); + o.setAppId(null); + o.setConfig(null); + o.setCreateTime(null); + }); + channelMapper.insert(dbChannel); + // 测试 code 不匹配 + channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setCode(null))); + // 测试 status 不匹配 + channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setStatus(null))); + // 测试 remark 不匹配 + channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setRemark(null))); + // 测试 feeRate 不匹配 + channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setFeeRate(null))); + // 测试 merchantId 不匹配 + channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setMerchantId(null))); + // 测试 appId 不匹配 + channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setAppId(null))); + // 测试 config 不匹配 + channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setConfig(null))); + // 测试 createTime 不匹配 + channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setCreateTime(null))); + // 准备参数 + PayChannelPageReqVO reqVO = new PayChannelPageReqVO(); + reqVO.setCode(null); + reqVO.setStatus(null); + reqVO.setRemark(null); + reqVO.setFeeRate(null); + reqVO.setMerchantId(null); + reqVO.setAppId(null); + reqVO.setConfig(null); + reqVO.setBeginCreateTime(null); + reqVO.setEndCreateTime(null); + + // 调用 + PageResult<PayChannelDO> pageResult = channelService.getChannelPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbChannel, pageResult.getList().get(0)); + } + + @Test // TODO 请修改 null 为需要的值 + public void testGetChannelList() { + // mock 数据 + PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> { // 等会查询到 + o.setCode(null); + o.setStatus(null); + o.setRemark(null); + o.setFeeRate(null); + o.setMerchantId(null); + o.setAppId(null); + o.setConfig(null); + o.setCreateTime(null); + }); + channelMapper.insert(dbChannel); + // 测试 code 不匹配 + channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setCode(null))); + // 测试 status 不匹配 + channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setStatus(null))); + // 测试 remark 不匹配 + channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setRemark(null))); + // 测试 feeRate 不匹配 + channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setFeeRate(null))); + // 测试 merchantId 不匹配 + channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setMerchantId(null))); + // 测试 appId 不匹配 + channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setAppId(null))); + // 测试 config 不匹配 + channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setConfig(null))); + // 测试 createTime 不匹配 + channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setCreateTime(null))); + // 准备参数 + PayChannelExportReqVO reqVO = new PayChannelExportReqVO(); + reqVO.setCode(null); + reqVO.setStatus(null); + reqVO.setRemark(null); + reqVO.setFeeRate(null); + reqVO.setMerchantId(null); + reqVO.setAppId(null); + reqVO.setConfig(null); + reqVO.setBeginCreateTime(null); + reqVO.setEndCreateTime(null); + + // 调用 + List<PayChannelDO> list = channelService.getChannelList(reqVO); + // 断言 + assertEquals(1, list.size()); + assertPojoEquals(dbChannel, list.get(0)); + } + +} diff --git a/yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/pay/merchant/service/PayMerchantServiceTest.java b/yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/pay/merchant/service/PayMerchantServiceTest.java index f1f4626e1..f09f570d6 100644 --- a/yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/pay/merchant/service/PayMerchantServiceTest.java +++ b/yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/pay/merchant/service/PayMerchantServiceTest.java @@ -1,6 +1,9 @@ package cn.iocoder.yudao.adminserver.modules.pay.merchant.service; import cn.hutool.core.util.RandomUtil; +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; import cn.iocoder.yudao.adminserver.BaseDbUnitTest; import cn.iocoder.yudao.adminserver.modules.pay.controller.merchant.vo.PayMerchantCreateReqVO; import cn.iocoder.yudao.adminserver.modules.pay.controller.merchant.vo.PayMerchantExportReqVO; @@ -28,7 +31,7 @@ import static org.junit.jupiter.api.Assertions.*; /** * {@link PayMerchantServiceImpl} 的单元测试类 * -* @author 芋艿 // TODO @aquan:修改成自己的。。。 +* @author aquan */ @Import(PayMerchantServiceImpl.class) public class PayMerchantServiceTest extends BaseDbUnitTest { diff --git a/yudao-admin-ui/src/api/pay/app.js b/yudao-admin-ui/src/api/pay/app.js new file mode 100644 index 000000000..788a959ee --- /dev/null +++ b/yudao-admin-ui/src/api/pay/app.js @@ -0,0 +1,67 @@ +import request from '@/utils/request' + +// 创建支付应用信息 +export function createApp(data) { + return request({ + url: '/pay/app/create', + method: 'post', + data: data + }) +} + +// 更新支付应用信息 +export function updateApp(data) { + return request({ + url: '/pay/app/update', + method: 'put', + data: data + }) +} + +// 支付应用信息状态修改 +export function changeAppStatus(id, status) { + const data = { + id, + status + } + return request({ + url: '/pay/app/update-status', + method: 'put', + data: data + }) +} + +// 删除支付应用信息 +export function deleteApp(id) { + return request({ + url: '/pay/app/delete?id=' + id, + method: 'delete' + }) +} + +// 获得支付应用信息 +export function getApp(id) { + return request({ + url: '/pay/app/get?id=' + id, + method: 'get' + }) +} + +// 获得支付应用信息分页 +export function getAppPage(query) { + return request({ + url: '/pay/app/page', + method: 'get', + params: query + }) +} + +// 导出支付应用信息 Excel +export function exportAppExcel(query) { + return request({ + url: '/pay/app/export-excel', + method: 'get', + params: query, + responseType: 'blob' + }) +} diff --git a/yudao-admin-ui/src/api/pay/channel.js b/yudao-admin-ui/src/api/pay/channel.js new file mode 100644 index 000000000..2a2accbd4 --- /dev/null +++ b/yudao-admin-ui/src/api/pay/channel.js @@ -0,0 +1,88 @@ +import request from '@/utils/request' + +// 创建支付渠道 +export function createChannel(data) { + return request({ + url: '/pay/channel/create', + method: 'post', + data: data + }) +} + + +// 更新支付渠道 +export function updateChannel(data) { + return request({ + url: '/pay/channel/update', + method: 'put', + data: data + }) +} + +// 删除支付渠道 +export function deleteChannel(id) { + return request({ + url: '/pay/channel/delete?id=' + id, + method: 'delete' + }) +} + +// 获得支付渠道 +export function getChannel(id) { + return request({ + url: '/pay/channel/get?id=' + id, + method: 'get' + }) +} + + + +// 获得支付渠道分页 +export function getChannelPage(query) { + return request({ + url: '/pay/channel/page', + method: 'get', + params: query + }) +} + +// 导出支付渠道Excel +export function exportChannelExcel(query) { + return request({ + url: '/pay/channel/export-excel', + method: 'get', + params: query, + responseType: 'blob' + }) +} + +// 创建微信支付渠道 +export function createWechatChannel(data) { + return request({ + url: '/pay/channel/create-wechat', + method: 'post', + data: data + }) +} + +// 获得支付渠道 +export function getWechatChannel(merchantId,appId,code) { + return request({ + url: '/pay/channel/get-wechat', + params:{ + merchantId:merchantId, + appId:appId, + code:code + }, + method: 'get' + }) +} + +// 更新支付渠道 +export function updateWechatChannel(data) { + return request({ + url: '/pay/channel/update-wechat', + method: 'put', + data: data + }) +} diff --git a/yudao-admin-ui/src/api/pay/merchant.js b/yudao-admin-ui/src/api/pay/merchant.js index 5dc6e96ee..1c6d7e20a 100644 --- a/yudao-admin-ui/src/api/pay/merchant.js +++ b/yudao-admin-ui/src/api/pay/merchant.js @@ -46,6 +46,16 @@ export function getMerchant(id) { method: 'get' }) } +// 根据商户名称搜索商户列表 +export function getMerchantListByName(name) { + return request({ + url: '/pay/merchant/list-name', + params:{ + name:name + }, + method: 'get' + }) +} // 获得支付商户信息分页 export function getMerchantPage(query) { diff --git a/yudao-admin-ui/src/assets/icons/svg/Mockitt-win32-x64-zh-1.1.7.exe b/yudao-admin-ui/src/assets/icons/svg/Mockitt-win32-x64-zh-1.1.7.exe new file mode 100644 index 000000000..0f2d4a27d Binary files /dev/null and b/yudao-admin-ui/src/assets/icons/svg/Mockitt-win32-x64-zh-1.1.7.exe differ diff --git a/yudao-admin-ui/src/assets/icons/svg/config.svg b/yudao-admin-ui/src/assets/icons/svg/config.svg new file mode 100644 index 000000000..79db5fb6a --- /dev/null +++ b/yudao-admin-ui/src/assets/icons/svg/config.svg @@ -0,0 +1,2 @@ +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1635996976125" class="icon" viewBox="0 0 1070 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4431" xmlns:xlink="http://www.w3.org/1999/xlink" width="208.984375" height="200"><defs><style type="text/css">@font-face { font-family: element-icons; src: url("chrome-extension://moombeodfomdpjnpocobemoiaemednkg/fonts/element-icons.woff") format("woff"), url("chrome-extension://moombeodfomdpjnpocobemoiaemednkg/fonts/element-icons.ttf ") format("truetype"); } +</style></defs><path d="M682.216727 1024a35.281455 35.281455 0 0 1-18.618182-7.912727 46.638545 46.638545 0 0 1-12.101818-16.337455l-3.258182-8.378182a19.549091 19.549091 0 0 1-3.258181-4.654545 139.915636 139.915636 0 0 0-35.793455-46.685091 104.448 104.448 0 0 0-134.888727 0c-19.083636 15.080727-33.698909 35.048727-42.356364 57.856a37.748364 37.748364 0 0 1-16.244363 19.083636c-18.757818 7.912727-39.842909 7.912727-58.600728 0a320.791273 320.791273 0 0 1-38.167272-13.498181 185.902545 185.902545 0 0 1-34.397091-19.083637 95.511273 95.511273 0 0 1-20.945455-21.038545 33.652364 33.652364 0 0 1-6.050909-18.152728c-0.465455-10.053818 1.629091-20.014545 6.050909-28.951272 8.238545-18.106182 11.450182-38.074182 9.309091-57.856a100.398545 100.398545 0 0 0-27.927273-59.717818 71.121455 71.121455 0 0 0-37.189818-21.41091 159.557818 159.557818 0 0 0-41.890909-3.723636c-15.825455 0.930909-31.418182 3.909818-46.498909 8.843636a46.405818 46.405818 0 0 1-30.72 0 50.269091 50.269091 0 0 1-22.295273-20.526545 237.661091 237.661091 0 0 1-18.618182-35.002182 219.229091 219.229091 0 0 1-12.567272-39.656727 93.556364 93.556364 0 0 1 0-33.559273 35.002182 35.002182 0 0 1 21.876363-30.813091 154.903273 154.903273 0 0 0 59.112728-43.380363c16.896-18.432 26.344727-42.589091 26.530909-67.630546a96.256 96.256 0 0 0-26.065455-67.165091c-16.337455-18.245818-36.538182-32.581818-59.112727-41.984a30.254545 30.254545 0 0 1-16.290909-15.872 56.087273 56.087273 0 0 1-10.24-27.508363c0.465455-10.891636 2.141091-21.736727 5.12-32.209455a217.367273 217.367273 0 0 1 27.927272-64.372364c5.306182-8.238545 12.288-15.220364 20.48-20.48a22.760727 22.760727 0 0 1 16.756364-4.70109c6.144 0.791273 12.241455 2.234182 18.152727 4.18909 21.643636 8.843636 45.195636 11.729455 68.328728 8.378182 23.738182-3.397818 45.754182-14.336 62.836363-31.232 8.890182-8.843636 15.592727-19.735273 19.549091-31.697454 3.770182-12.334545 6.283636-24.994909 7.447273-37.794909a168.401455 168.401455 0 0 0 0-34.071273c0-9.309091 0-16.756364-2.792727-21.876364a113.105455 113.105455 0 0 1-3.723637-13.591272 23.365818 23.365818 0 0 1 0-16.29091c5.306182-11.636364 14.056727-21.410909 25.088-28.020363 11.450182-7.912727 23.784727-14.429091 36.770909-19.549091 12.520727-4.794182 25.413818-8.517818 38.586182-11.217455 10.379636-2.466909 20.945455-3.909818 31.650909-4.189091a28.811636 28.811636 0 0 1 22.295273 9.309091c5.12 5.538909 9.076364 12.008727 11.636364 19.130182 8.564364 20.48 22.155636 38.446545 39.563636 52.270546 39.330909 29.323636 93.184 29.323636 132.514909 0 19.176727-13.498182 33.792-32.581818 41.890909-54.597819l2.327273-4.654545c3.444364-4.887273 7.354182-9.402182 11.636364-13.498182l3.258181-3.770182c14.848-6.330182 31.650909-6.330182 46.498909 0 12.101818 2.978909 23.924364 7.028364 35.374546 12.101819 11.682909 4.980364 22.667636 11.543273 32.535273 19.595636 9.309091 7.447273 17.361455 16.477091 23.738181 26.624 3.258182 5.771636 4.375273 12.567273 3.258182 19.083636a50.501818 50.501818 0 0 1-5.12 15.872c-9.169455 20.805818-12.101818 43.845818-8.378182 66.234182 3.677091 23.412364 14.754909 45.009455 31.65091 61.579636 17.221818 17.314909 40.261818 27.601455 64.60509 28.951273a142.894545 142.894545 0 0 0 71.214546-12.567273c6.423273-3.816727 13.963636-5.259636 21.364364-4.235636 8.564364 0.279273 16.709818 3.723636 22.807272 9.774546 12.939636 13.312 22.946909 29.184 29.323637 46.68509 8.285091 20.340364 14.056727 41.658182 17.175272 63.441455a38.353455 38.353455 0 0 1-6.050909 28.858182 40.96 40.96 0 0 1-17.221818 14.056727c-22.993455 8.331636-43.147636 23.04-58.135273 42.449455a107.985455 107.985455 0 0 0-25.134545 70.423272c-0.465455 23.365818 6.516364 46.219636 20.014545 65.303273a112.64 112.64 0 0 0 53.061819 40.122182 23.272727 23.272727 0 0 1 10.24 7.447273h2.792727c7.493818 5.957818 13.032727 14.103273 15.778909 23.319272 0.093091 1.396364 0.093091 2.792727 0 4.189091a97.745455 97.745455 0 0 1 0 29.882182c-2.234182 12.939636-5.818182 25.6-10.705455 37.794909-4.840727 12.194909-11.077818 23.738182-18.618181 34.490182a67.025455 67.025455 0 0 1-24.203637 21.922909c-14.475636 7.912727-31.976727 7.912727-46.498909 0a229.236364 229.236364 0 0 0-27.461818-6.050909 131.723636 131.723636 0 0 0-33.466182 0 159.650909 159.650909 0 0 0-33.978182 6.981818 65.582545 65.582545 0 0 0-28.811636 18.664727 108.357818 108.357818 0 0 0-30.72 62.510546c-3.025455 22.900364 0.372364 46.126545 9.774545 67.165091a42.077091 42.077091 0 0 1-3.258181 43.892363 86.574545 86.574545 0 0 1-20.945455 18.152728c-9.914182 6.562909-20.293818 12.334545-31.138909 17.268363a195.444364 195.444364 0 0 1-35.84 12.101819 150.807273 150.807273 0 0 1-31.604364 5.166545z m-378.135272-83.037091l7.912727 6.516364c8.098909 5.771636 16.849455 10.612364 26.065454 14.475636 10.705455 4.561455 21.736727 8.471273 33.000728 11.636364 6.656 2.094545 13.498182 3.351273 20.48 3.723636a179.525818 179.525818 0 0 1 54.411636-72.285091 150.807273 150.807273 0 0 1 194.420364 0c18.944 15.173818 34.722909 33.838545 46.545454 55.063273 2.187636 2.094545 3.956364 4.654545 5.12 7.447273l3.258182 8.843636h12.055273c9.309091-2.187636 18.292364-5.306182 26.996363-9.309091 8.704-3.863273 17.082182-8.378182 25.134546-13.498182l6.981818-4.654545a172.125091 172.125091 0 0 1-13.032727-90.996364 152.669091 152.669091 0 0 1 42.775272-88.622545 111.616 111.616 0 0 1 48.826182-31.232c14.196364-4.701091 28.858182-7.819636 43.752728-9.309091a179.479273 179.479273 0 0 1 46.498909 0c11.636364 1.861818 23.133091 4.468364 34.443636 7.912727h9.262545a22.807273 22.807273 0 0 0 6.981819-7.447273c5.492364-7.866182 10.007273-16.290909 13.498181-25.227636 3.816727-9.309091 6.609455-18.990545 8.378182-28.904727 0.372364-4.189091 0.372364-8.424727 0-12.613818l-3.258182-2.327273A158.673455 158.673455 0 0 1 932.910545 605.090909a155.648 155.648 0 0 1-28.811636-93.277091 153.274182 153.274182 0 0 1 34.443636-97.978182c18.199273-23.412364 42.077091-41.890909 69.306182-53.620363a216.901818 216.901818 0 0 0-13.498182-46.685091 108.311273 108.311273 0 0 0-13.498181-25.6 187.904 187.904 0 0 1-89.786182 14.894545 144.011636 144.011636 0 0 1-92.997818-42.914909 156.392727 156.392727 0 0 1-46.545455-87.226182 165.562182 165.562182 0 0 1 11.170909-90.484363 89.739636 89.739636 0 0 0-29.789091-20.992 161.978182 161.978182 0 0 0-27.880727-9.309091l-13.963636-4.701091a161.373091 161.373091 0 0 1-55.342546 73.216 157.323636 157.323636 0 0 1-190.696727-2.792727 178.641455 178.641455 0 0 1-52.596364-70.423273h-11.636363a204.241455 204.241455 0 0 0-31.138909 9.309091 139.403636 139.403636 0 0 0-27.461819 14.475636l-3.723636 2.792727v6.05091c0 8.843636 2.792727 17.268364 3.723636 27.089454 1.442909 14.242909 1.442909 28.625455 0 42.914909-1.582545 15.825455-4.840727 31.464727-9.774545 46.638546-6.050909 19.549091-16.756364 37.236364-31.138909 51.758545-24.203636 24.064-55.528727 39.563636-89.320727 44.311273a175.336727 175.336727 0 0 1-90.205091-10.24v2.327273a143.825455 143.825455 0 0 0-12.101819 22.388363 170.123636 170.123636 0 0 0-8.843636 26.577455 90.298182 90.298182 0 0 0-3.258182 25.693091A211.176727 211.176727 0 0 1 148.247273 410.530909c24.901818 26.670545 38.725818 61.905455 38.632727 98.443636a146.245818 146.245818 0 0 1-38.167273 97.931637c-19.409455 23.133091-43.752727 41.611636-71.168 54.132363-0.512 4.654545-0.512 9.309091 0 13.963637a175.755636 175.755636 0 0 0 24.669091 58.786909l2.792727 3.723636a198.097455 198.097455 0 0 1 112.081455-6.050909c23.412364 5.12 44.683636 17.361455 60.974545 35.002182a145.687273 145.687273 0 0 1 41.37891 87.691636c2.839273 27.927273-1.815273 56.133818-13.498182 81.640728l-1.861818 5.12zM77.544727 360.261818l-18.618182 13.498182 18.618182-9.774545v-3.723637zM405.922909 25.739636l-17.221818 15.36 3.723636 3.258182 13.498182-18.618182z" p-id="4432"></path><path d="M543.045818 698.181818a186.181818 186.181818 0 1 1 0-372.363636 186.181818 186.181818 0 0 1 0 372.363636z m0-325.818182a139.636364 139.636364 0 1 0 0 279.272728 139.636364 139.636364 0 0 0 0-279.272728z" p-id="4433"></path></svg> \ No newline at end of file diff --git a/yudao-admin-ui/src/assets/icons/svg/percentSign.svg b/yudao-admin-ui/src/assets/icons/svg/percentSign.svg new file mode 100644 index 000000000..bedbce63d --- /dev/null +++ b/yudao-admin-ui/src/assets/icons/svg/percentSign.svg @@ -0,0 +1,2 @@ +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1636341153732" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2282" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css">@font-face { font-family: element-icons; src: url("chrome-extension://moombeodfomdpjnpocobemoiaemednkg/fonts/element-icons.woff") format("woff"), url("chrome-extension://moombeodfomdpjnpocobemoiaemednkg/fonts/element-icons.ttf ") format("truetype"); } +</style></defs><path d="M294.912 874.666667a21.333333 21.333333 0 0 1-17.194667-33.962667l469.333334-640a21.333333 21.333333 0 0 1 34.432 25.258667l-469.333334 640a21.248 21.248 0 0 1-17.237333 8.704zM358.912 533.333333a161.28 161.28 0 0 1-149.333333-170.666666 161.28 161.28 0 0 1 149.333333-170.666667 161.28 161.28 0 0 1 149.333333 170.666667 161.28 161.28 0 0 1-149.333333 170.666666z m0-298.666666a118.912 118.912 0 0 0-106.666667 128 118.912 118.912 0 0 0 106.666667 128 118.912 118.912 0 0 0 106.666667-128 118.912 118.912 0 0 0-106.666667-128zM700.245333 874.666667a161.28 161.28 0 0 1-149.333333-170.666667 161.28 161.28 0 0 1 149.333333-170.666667 161.28 161.28 0 0 1 149.333334 170.666667 161.28 161.28 0 0 1-149.333334 170.666667z m0-298.666667a118.912 118.912 0 0 0-106.666666 128 118.912 118.912 0 0 0 106.666666 128 118.912 118.912 0 0 0 106.666667-128 118.912 118.912 0 0 0-106.666667-128z" fill="#757575" p-id="2283"></path></svg> \ No newline at end of file diff --git a/yudao-admin-ui/src/utils/constants.js b/yudao-admin-ui/src/utils/constants.js index d0251ae68..f4cd50a2c 100644 --- a/yudao-admin-ui/src/utils/constants.js +++ b/yudao-admin-ui/src/utils/constants.js @@ -16,7 +16,7 @@ export const SysCommonStatusEnum = { * 菜单的类型枚举 */ export const SysMenuTypeEnum = { - DIR : 1, // 目录 + DIR: 1, // 目录 MENU: 2, // 菜单 BUTTON: 3 // 按钮 } @@ -90,3 +90,34 @@ export const SysUserSocialTypeEnum = { img: "https://cdn.jsdelivr.net/gh/justauth/justauth-oauth-logo@1.11/wechat_enterprise.png", } } + +export const PayChannelEnum = { + WX_PUB: { + "code": "wx_pub", + "name": "微信 JSAPI 支付", + }, + WX_LITE: { + "code": "wx_lite", + "name": "微信小程序支付" + }, + WX_APP: { + "code": "wx_app", + "name": "微信 APP 支付" + }, + ALIPAY_PC: { + "code": "alipay_pc", + "name": "支付宝 PC 网站支付" + }, + ALIPAY_WAP: { + "code": "alipay_wap", + "name": "支付宝 WAP 网站支付" + }, + ALIPAY_APP: { + "code": "alipay_app", + "name": "支付宝 APP 支付" + }, + ALIPAY_QR: { + "code": "alipay_qr", + "name": "支付宝扫码支付" + }, +} diff --git a/yudao-admin-ui/src/utils/dict.js b/yudao-admin-ui/src/utils/dict.js index 7c17cceb8..f084b2e62 100644 --- a/yudao-admin-ui/src/utils/dict.js +++ b/yudao-admin-ui/src/utils/dict.js @@ -32,7 +32,14 @@ export const DICT_TYPE = { TOOL_CODEGEN_TEMPLATE_TYPE: 'tool_codegen_template_type', // 商户状态 - PAY_MERCHANT_STATUS: 'pay_merchant_status' + PAY_MERCHANT_STATUS: 'pay_merchant_status', + // 应用状态 + PAY_APP_STATUS: 'pay_app_status', + // 渠道状态 + PAY_CHANNEL_STATUS: 'pay_channel_status', + // 微信渠道版本 + PAY_CHANNEL_WECHAT_VERSION:'pay_channel_wechat_version', + } /** diff --git a/yudao-admin-ui/src/views/pay/app/components/wechatJsApiForm.vue b/yudao-admin-ui/src/views/pay/app/components/wechatJsApiForm.vue new file mode 100644 index 000000000..01a984c2e --- /dev/null +++ b/yudao-admin-ui/src/views/pay/app/components/wechatJsApiForm.vue @@ -0,0 +1,261 @@ +<template> + <div> + <el-dialog :visible.sync="transferParam.open" @close="close" append-to-body> + <el-form ref="wechatJsApiForm" :model="form" :rules="rules" size="medium" label-width="100px" + v-loading="transferParam.loading"> + <el-form-item label-width="180px" label="渠道费率" prop="feeRate"> + <el-input v-model="form.feeRate" placeholder="请输入渠道费率" clearable :style="{width: '100%'}"> + <template slot="append">%</template> + </el-input> + </el-form-item> + <el-form-item label-width="180px" label="公众号APPID" prop="weChatConfig.appId"> + <el-input v-model="form.weChatConfig.appId" placeholder="请输入公众号APPID" clearable :style="{width: '100%'}"> + </el-input> + </el-form-item> + <el-form-item label-width="180px" label="商户号" prop="weChatConfig.mchId"> + <el-input v-model="form.weChatConfig.mchId" :style="{width: '100%'}"></el-input> + </el-form-item> + <el-form-item label-width="180px" label="渠道状态" prop="status"> + <el-radio-group v-model="form.status" size="medium"> + <el-radio v-for="dict in statusDictDatas" :key="parseInt(dict.value)" :label="parseInt(dict.value)"> + {{ dict.label }} + </el-radio> + </el-radio-group> + </el-form-item> + <el-form-item label-width="180px" label="API 版本" prop="weChatConfig.apiVersion"> + <el-radio-group v-model="form.weChatConfig.apiVersion" size="medium"> + <el-radio v-for="dict in versionDictDatas" :key="dict.value" :label="dict.value"> + {{ dict.label }} + </el-radio> + </el-radio-group> + </el-form-item> + <el-form-item label-width="180px" label="商户秘钥" prop="weChatConfig.mchKey" + v-if="form.weChatConfig.apiVersion === 'v2'"> + <el-input v-model="form.weChatConfig.mchKey" placeholder="请输入商户秘钥" clearable + :style="{width: '100%'}"></el-input> + </el-form-item> + <div v-if="form.weChatConfig.apiVersion === 'v3'"> + <el-form-item label-width="180px" label="apiclient_key.perm证书" prop="weChatConfig.privateKeyContent"> + <el-input v-model="form.weChatConfig.privateKeyContent" type="textarea" + placeholder="请上传apiclient_key.perm证书" + readonly :autosize="{minRows: 8, maxRows: 8}" :style="{width: '100%'}"></el-input> + </el-form-item> + <el-form-item label-width="180px" label="" prop="privateKeyContentFile"> + <el-upload ref="privateKeyContentFile" + :limit="1" + :accept="fileAccept" + :headers="header" + :action="pemUploadAction" + :before-upload="pemFileBeforeUpload" + :on-success="privateKeyUploadSuccess" + > + <el-button size="small" type="primary" icon="el-icon-upload">点击上传</el-button> + </el-upload> + </el-form-item> + <el-form-item label-width="180px" label="apiclient_cert.perm证书" prop="weChatConfig.privateCertContent"> + <el-input v-model="form.weChatConfig.privateCertContent" type="textarea" + placeholder="请上传apiclient_cert.perm证书" + readonly :autosize="{minRows: 8, maxRows: 8}" :style="{width: '100%'}"></el-input> + </el-form-item> + <el-form-item label-width="180px" label="" prop="privateCertContentFile"> + <el-upload ref="privateCertContentFile" + :limit="1" + :accept="fileAccept" + :headers="header" + :action="pemUploadAction" + :before-upload="pemFileBeforeUpload" + :on-success="privateCertUploadSuccess" + > + <el-button size="small" type="primary" icon="el-icon-upload">点击上传</el-button> + </el-upload> + </el-form-item> + </div> + <el-form-item label-width="180px" label="备注" prop="remark"> + <el-input v-model="form.remark" :style="{width: '100%'}"></el-input> + </el-form-item> + </el-form> + <div slot="footer" class="dialog-footer"> + <el-button @click="close">取消</el-button> + <el-button type="primary" @click="handleConfirm">确定</el-button> + </div> + </el-dialog> + </div> +</template> +<script> +import {DICT_TYPE, getDictDatas} from "@/utils/dict"; +import {createWechatChannel, getWechatChannel, updateWechatChannel} from "@/api/pay/channel"; + +export default { + name: "wechatJsApiForm", + components: {}, + props: { + // 传输的参数 + transferParam: { + // 加载动画 + "loading": false, + // 是否修改 + "edit": false, + // 是否显示 + "open": false, + // 应用ID + "appId": null, + // 渠道编码 + "payCode": null, + // 商户对象 + "payMerchant": { + // 编号 + "id": null, + // 名称 + "name": null + }, + } + }, + data() { + return { + form: { + code: undefined, + status: undefined, + remark: undefined, + feeRate: undefined, + appId: undefined, + merchantId: undefined, + weChatConfig: { + appId: undefined, + mchId: undefined, + apiVersion: undefined, + mchKey: undefined, + privateKeyContent: undefined, + privateCertContent: undefined, + } + }, + rules: { + feeRate: [{ + required: true, + message: '请输入渠道费率', + trigger: 'blur' + }], + 'weChatConfig.mchId': [{ + required: true, + message: '请传入商户号', + trigger: 'blur' + }], + 'weChatConfig.appId': [{ + required: true, + message: '请输入公众号APPID', + trigger: 'blur' + }], + status: [{ + required: true, + message: '渠道状态不能为空', + trigger: 'change' + }], + 'weChatConfig.apiVersion': [{ + required: true, + message: 'API版本不能为空', + trigger: 'change' + }], + 'weChatConfig.mchKey': [{ + required: true, + message: '请输入商户秘钥', + trigger: 'blur' + }], + 'weChatConfig.privateKeyContent': [{ + required: true, + message: '请上传apiclient_key.perm证书', + trigger: 'blur' + }], + 'weChatConfig.privateCertContent': [{ + required: true, + message: '请上传apiclient_cert.perm证书', + trigger: 'blur' + }], + }, + // 文件上传的header + header: { + "Authorization": null + }, + pemUploadAction: 'http://127.0.0.1:48080/api/pay/channel/parsing-pem', + fileAccept: ".pem", + // 渠道状态 数据字典 + statusDictDatas: getDictDatas(DICT_TYPE.PAY_CHANNEL_STATUS), + versionDictDatas: getDictDatas(DICT_TYPE.PAY_CHANNEL_WECHAT_VERSION), + } + }, + watch: { + transferParam: { + deep: true, // 深度监听 + handler(newVal) { + this.form.code = newVal.payCode; + this.form.appId = newVal.appId; + this.form.merchantId = newVal.payMerchant.id; + // 只有在初次进来为编辑 并且为加载中的时候才回去请求数据 + if (newVal.edit === true && newVal.loading) { + this.init(); + } + } + } + }, + created() { + this.header.Authorization = "Bearer " + this.$store.getters.token; + }, + methods: { + init() { + getWechatChannel(this.transferParam.payMerchant.id, this.transferParam.appId, this.transferParam.payCode) + .then(response => { + this.form = response.data; + this.transferParam.loading = false; + }) + }, + close() { + this.transferParam.open = false; + this.$refs['wechatJsApiForm'].resetFields(); + }, + handleConfirm() { + this.$refs['wechatJsApiForm'].validate(valid => { + if (!valid) { + return + } + if (this.transferParam.edit) { + updateWechatChannel(this.form).then(response => { + if (response.code === 0 ) { + this.msgSuccess("修改成功"); + this.close(); + } + + }) + } else { + createWechatChannel(this.form).then(response => { + if (response.code === 0) { + this.msgSuccess("新增成功"); + this.$parent.refreshTable(); + this.close(); + } + }); + } + }); + }, + pemFileBeforeUpload(file) { + let format = '.' + file.name.split(".")[1]; + if (format !== this.fileAccept) { + this.$message.error('请上传指定格式"' + this.fileAccept + '"文件'); + return false; + } + let isRightSize = file.size / 1024 / 1024 < 2 + if (!isRightSize) { + this.$message.error('文件大小超过 2MB') + } + return isRightSize + }, + privateKeyUploadSuccess(response) { + this.form.weChatConfig.privateKeyContent = response.data; + }, + privateCertUploadSuccess(response) { + this.form.weChatConfig.privateCertContent = response.data; + } + } +} + +</script> +<style scoped> + +</style> diff --git a/yudao-admin-ui/src/views/pay/app/index.vue b/yudao-admin-ui/src/views/pay/app/index.vue new file mode 100644 index 000000000..e24ff4cd7 --- /dev/null +++ b/yudao-admin-ui/src/views/pay/app/index.vue @@ -0,0 +1,441 @@ +<template> + <div class="app-container"> + + <!-- 搜索工作栏 --> + <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px"> + <el-form-item label="应用名" prop="name"> + <el-input v-model="queryParams.name" placeholder="请输入应用名" clearable size="small" + @keyup.enter.native="handleQuery"/> + </el-form-item> + <el-form-item label="商户名称" prop="merchantName"> + <el-input v-model="queryParams.merchantName" placeholder="请输入商户名称" clearable size="small" + @keyup.enter.native="handleQuery"/> + </el-form-item> + <el-form-item label="开启状态" prop="status"> + <el-select v-model="queryParams.status" placeholder="请选择开启状态" clearable size="small"> + <el-option v-for="dict in statusDictDatas" :key="parseInt(dict.value)" :label="dict.label" + :value="parseInt(dict.value)"/> + </el-select> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> + <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button> + </el-form-item> + </el-form> + + <!-- 操作工具栏 --> + <el-row :gutter="10" class="mb8"> + <el-col :span="1.5"> + <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" + v-hasPermi="['pay:app:create']">新增 + </el-button> + </el-col> + <el-col :span="1.5"> + <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" + v-hasPermi="['pay:app:export']">导出 + </el-button> + </el-col> + <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> + </el-row> + + <!-- 列表 --> + <el-table v-loading="loading" :data="list"> + <el-table-column label="应用编号" align="center" prop="id"/> + <el-table-column label="应用名" align="center" prop="name"/> + <el-table-column label="开启状态" align="center" prop="status"> + <template slot-scope="scope"> + <el-switch v-model="scope.row.status" :active-value="0" :inactive-value="1" + @change="handleStatusChange(scope.row)"/> + </template> + </el-table-column> + <el-table-column label="商户名称" align="center" prop="payMerchant.name"/> + <el-table-column label="创建时间" align="center" prop="createTime" width="180"> + <template slot-scope="scope"> + <span>{{ parseTime(scope.row.createTime) }}</span> + </template> + </el-table-column> + <el-table-column label="支付宝配置" align="center"> + <el-table-column :label="payChannelEnum.ALIPAY_APP.name" align="center"> + <template slot-scope="scope"> + <el-button type="success" icon="el-icon-check" circle + v-if="scope.row.payChannel.alipayApp === sysCommonStatusEnum.ENABLE"></el-button> + <el-button type="danger" icon="el-icon-close" circle + v-if="scope.row.payChannel.alipayApp === sysCommonStatusEnum.DISABLE"></el-button> + </template> + </el-table-column> + <el-table-column :label="payChannelEnum.ALIPAY_PC.name" align="center"> + <template slot-scope="scope"> + <el-button type="success" icon="el-icon-check" circle + v-if="scope.row.payChannel.alipayPc === sysCommonStatusEnum.ENABLE"></el-button> + <el-button type="danger" icon="el-icon-close" circle + v-if="scope.row.payChannel.alipayPc === sysCommonStatusEnum.DISABLE"></el-button> + </template> + </el-table-column> + <el-table-column :label="payChannelEnum.ALIPAY_WAP.name" align="center"> + <template slot-scope="scope"> + <el-button type="success" icon="el-icon-check" circle + v-if="scope.row.payChannel.alipayWap === sysCommonStatusEnum.ENABLE"></el-button> + <el-button type="danger" icon="el-icon-close" circle + v-if="scope.row.payChannel.alipayWap === sysCommonStatusEnum.DISABLE"></el-button> + </template> + </el-table-column> + <el-table-column :label="payChannelEnum.ALIPAY_QR.name" align="center"> + <template slot-scope="scope"> + <el-button type="success" icon="el-icon-check" circle + v-if="scope.row.payChannel.alipayQr === sysCommonStatusEnum.ENABLE"></el-button> + <el-button type="danger" icon="el-icon-close" circle + v-if="scope.row.payChannel.alipayQr === sysCommonStatusEnum.DISABLE"></el-button> + </template> + </el-table-column> + </el-table-column> + <el-table-column label="微信配置" align="center"> + <el-table-column :label="payChannelEnum.WX_LITE.name" align="center"> + <template slot-scope="scope"> + <el-button type="success" icon="el-icon-check" circle + @click="handleUpdateChannel(scope.row,payChannelEnum.WX_LITE.code)" + v-if="scope.row.payChannel.wxLite === sysCommonStatusEnum.ENABLE"></el-button> + <el-button type="danger" icon="el-icon-close" circle + @click="handleCreateChannel(scope.row,payChannelEnum.WX_LITE.code)" + v-if="scope.row.payChannel.wxLite === sysCommonStatusEnum.DISABLE"></el-button> + </template> + </el-table-column> + <el-table-column :label="payChannelEnum.WX_PUB.name" align="center"> + <template slot-scope="scope"> + <el-button + type="success" icon="el-icon-check" circle + @click="handleUpdateChannel(scope.row,payChannelEnum.WX_PUB.code)" + v-if="scope.row.payChannel.wxPub === sysCommonStatusEnum.ENABLE"> + </el-button> + <el-button type="danger" icon="el-icon-close" circle + @click="handleCreateChannel(scope.row,payChannelEnum.WX_PUB.code)" + v-if="scope.row.payChannel.wxPub === sysCommonStatusEnum.DISABLE"></el-button> + </template> + </el-table-column> + <el-table-column :label="payChannelEnum.WX_APP.name" align="center"> + <template slot-scope="scope"> + <el-button type="success" icon="el-icon-check" circle + @click="handleUpdateChannel(scope.row,payChannelEnum.WX_APP.code)" + v-if="scope.row.payChannel.wxApp === sysCommonStatusEnum.ENABLE"></el-button> + <el-button type="danger" icon="el-icon-close" circle + @click="handleCreateChannel(scope.row,payChannelEnum.WX_APP.code)" + v-if="scope.row.payChannel.wxApp === sysCommonStatusEnum.DISABLE"></el-button> + </template> + </el-table-column> + </el-table-column> + <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> + <template slot-scope="scope"> + <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" + v-hasPermi="['pay:app:update']">修改 + </el-button> + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" + v-hasPermi="['pay:app:delete']">删除 + </el-button> + </template> + </el-table-column> + </el-table> + <!-- 分页组件 --> + <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" + @pagination="getList"/> + + <!-- 对话框(添加 / 修改) --> + <el-dialog :title="title" :visible.sync="open" width="800px" append-to-body> + <el-form ref="form" :model="form" :rules="rules" label-width="160px"> + <el-form-item label="应用名" prop="name"> + <el-input v-model="form.name" placeholder="请输入应用名"/> + </el-form-item> + <el-form-item label="所属商户" prop="merchantId"> + <el-select + v-model="form.merchantId" + filterable + remote + reserve-keyword + placeholder="请选择所属商户" + :remote-method="handleGetMerchantListByName" + :loading="loading"> + <el-option + v-for="item in merchantList" + :key="item.id" + :label="item.name" + :value="item.id"> + </el-option> + </el-select> + <!-- <el-input v-model="form.merchantId" placeholder="请输入商户编号"/>--> + </el-form-item> + <el-form-item label="开启状态" prop="status"> + <el-radio-group v-model="form.status"> + <el-radio v-for="dict in statusDictDatas" :key="parseInt(dict.value)" :label="parseInt(dict.value)"> + {{ dict.label }} + </el-radio> + </el-radio-group> + </el-form-item> + + <el-form-item label="支付结果的回调地址" prop="payNotifyUrl"> + <el-input v-model="form.payNotifyUrl" placeholder="请输入支付结果的回调地址"/> + </el-form-item> + <el-form-item label="退款结果的回调地址" prop="refundNotifyUrl"> + <el-input v-model="form.refundNotifyUrl" placeholder="请输入退款结果的回调地址"/> + </el-form-item> + + <el-form-item label="备注" prop="remark"> + <el-input v-model="form.remark" placeholder="请输入备注"/> + </el-form-item> + </el-form> + <div slot="footer" class="dialog-footer"> + <el-button type="primary" @click="submitForm">确 定</el-button> + <el-button @click="cancel">取 消</el-button> + </div> + </el-dialog> + + <wechat-js-api-form :transferParam="wechatChannelParam"></wechat-js-api-form> + </div> +</template> + +<script> +import {createApp, updateApp, changeAppStatus, deleteApp, getApp, getAppPage, exportAppExcel} from "@/api/pay/app"; +import {DICT_TYPE, getDictDatas} from "@/utils/dict"; +import {PayChannelEnum, SysCommonStatusEnum} from "@/utils/constants"; +import {getMerchantListByName} from "@/api/pay/merchant"; +import wechatJsApiForm from "@/views/pay/app/components/wechatJsApiForm"; + +export default { + name: "App", + components: { + "wechatJsApiForm": wechatJsApiForm + }, + data() { + return { + // 遮罩层 + loading: true, + // 显示搜索条件 + showSearch: true, + // 总条数 + total: 0, + // 支付应用信息列表 + list: [], + // 弹出层标题 + title: "", + // 是否显示弹出层 + open: false, + dateRangeCreateTime: [], + // 查询参数 + queryParams: { + pageNo: 1, + pageSize: 10, + name: null, + status: null, + remark: null, + payNotifyUrl: null, + refundNotifyUrl: null, + merchantName: null, + }, + // 表单参数 + form: {}, + // 表单校验 + rules: { + name: [{required: true, message: "应用名不能为空", trigger: "blur"}], + status: [{required: true, message: "开启状态不能为空", trigger: "blur"}], + payNotifyUrl: [{required: true, message: "支付结果的回调地址不能为空", trigger: "blur"}], + refundNotifyUrl: [{required: true, message: "退款结果的回调地址不能为空", trigger: "blur"}], + merchantId: [{required: true, message: "商户编号不能为空", trigger: "blur"}], + }, + // 数据字典 + statusDictDatas: getDictDatas(DICT_TYPE.PAY_APP_STATUS), + sysCommonStatusEnum: SysCommonStatusEnum, + // 支付渠道枚举 + payChannelEnum: PayChannelEnum, + // 商户列表 + merchantList: [], + // 是否显示支付窗口 + payOpen: false, + // 微信组件传参参数 + wechatChannelParam: { + // 是否修改 + "edit":false, + // 是否显示 + "open":false, + // 应用ID + "appId": null, + // 渠道编码 + "payCode": null, + // 商户对象 + "payMerchant": { + // 编号 + "id": null, + // 名称 + "name": null + }, + } + }; + }, + created() { + this.getList(); + }, + methods: { + /** 查询列表 */ + getList() { + this.loading = true; + // 处理查询参数 + let params = {...this.queryParams}; + this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime'); + // 执行查询 + getAppPage(params).then(response => { + console.log(response.data); + this.list = response.data.list; + this.total = response.data.total; + this.loading = false; + }); + }, + /** 取消按钮 */ + cancel() { + this.open = false; + this.reset(); + }, + /** 表单重置 */ + reset() { + this.form = { + id: undefined, + name: undefined, + status: undefined, + remark: undefined, + payNotifyUrl: undefined, + refundNotifyUrl: undefined, + merchantId: undefined, + }; + this.resetForm("form"); + }, + /** 搜索按钮操作 */ + handleQuery() { + this.queryParams.pageNo = 1; + this.getList(); + }, + /** 重置按钮操作 */ + resetQuery() { + this.dateRangeCreateTime = []; + this.resetForm("queryForm"); + this.handleQuery(); + }, + /** 新增按钮操作 */ + handleAdd() { + this.reset(); + this.open = true; + this.title = "添加支付应用信息"; + }, + /** 修改按钮操作 */ + handleUpdate(row) { + this.reset(); + const id = row.id; + getApp(id).then(response => { + this.form = response.data; + this.open = true; + this.title = "修改支付应用信息"; + }); + }, + // 用户状态修改 + handleStatusChange(row) { + let text = row.status === SysCommonStatusEnum.ENABLE ? "启用" : "停用"; + this.$confirm('确认要"' + text + '""' + row.name + '"应用吗?', "警告", { + confirmButtonText: "确定", + cancelButtonText: "取消", + type: "warning" + }).then(function () { + return changeAppStatus(row.id, row.status); + }).then(() => { + this.msgSuccess(text + "成功"); + }).catch(function () { + row.status = row.status === SysCommonStatusEnum.ENABLE ? SysCommonStatusEnum.DISABLE + : SysCommonStatusEnum.ENABLE; + }); + }, + /** 提交按钮 */ + submitForm() { + this.$refs["form"].validate(valid => { + if (!valid) { + return; + } + // 修改的提交 + if (this.form.id != null) { + updateApp(this.form).then(response => { + this.msgSuccess("修改成功"); + this.open = false; + this.getList(); + }); + return; + } + // 添加的提交 + createApp(this.form).then(response => { + this.msgSuccess("新增成功"); + this.open = false; + this.getList(); + }); + }); + }, + /** 删除按钮操作 */ + handleDelete(row) { + const id = row.id; + this.$confirm('是否确认删除支付应用信息编号为"' + id + '"的数据项?', "警告", { + confirmButtonText: "确定", + cancelButtonText: "取消", + type: "warning" + }).then(function () { + return deleteApp(id); + }).then(() => { + this.getList(); + this.msgSuccess("删除成功"); + }) + }, + /** 导出按钮操作 */ + handleExport() { + // 处理查询参数 + let params = {...this.queryParams}; + params.pageNo = undefined; + params.pageSize = undefined; + this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime'); + // 执行导出 + this.$confirm('是否确认导出所有支付应用信息数据项?', "警告", { + confirmButtonText: "确定", + cancelButtonText: "取消", + type: "warning" + }).then(function () { + return exportAppExcel(params); + }).then(response => { + this.downloadExcel(response, '支付应用信息.xls'); + }) + }, + /** + * 根据商户名称模糊匹配商户信息 + * @param name 商户名称 + */ + handleGetMerchantListByName(name) { + getMerchantListByName(name).then(response => { + console.log(response) + this.merchantList = response.data; + }); + }, + /** + * 修改支付渠道信息 + */ + handleUpdateChannel(row, payCode) { + this.wechatChannelParam.edit = true; + this.wechatChannelParam.loading = true; + this.wechatChannelParam.appId = row.id; + this.wechatChannelParam.payCode = payCode; + this.wechatChannelParam.payMerchant = row.payMerchant; + this.wechatChannelParam.open = true; + }, + /** + * 新增支付渠道信息 + */ + handleCreateChannel(row, payCode) { + this.wechatChannelParam.edit = false; + this.wechatChannelParam.loading = false; + this.wechatChannelParam.appId = row.id; + this.wechatChannelParam.payCode = payCode; + this.wechatChannelParam.payMerchant = row.payMerchant; + this.wechatChannelParam.open = true; + }, + refreshTable(){ + this.getList(); + } + } +}; +</script> diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/merchant/PayChannelDO.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/merchant/PayChannelDO.java index 2101e6634..074585e5d 100644 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/merchant/PayChannelDO.java +++ b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/merchant/PayChannelDO.java @@ -47,6 +47,11 @@ public class PayChannelDO extends BaseDO { */ private Double feeRate; + /** + * 备注 + */ + private String remark; + /** * 商户编号 * diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/merchant/PayMerchantDO.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/merchant/PayMerchantDO.java index 3c32cf86d..a0c72b9ec 100644 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/merchant/PayMerchantDO.java +++ b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/merchant/PayMerchantDO.java @@ -33,7 +33,6 @@ public class PayMerchantDO extends BaseDO { * 例如说,M233666999 * 只有新增时插入,不允许修改 */ - @TableField(fill = FieldFill.INSERT) // TODO @aquan:Service 逻辑里设置,所以不用这个注解哈 private String no; /** * 商户全称 diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/enums/PayErrorCodeCoreConstants.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/enums/PayErrorCodeCoreConstants.java index a4c88d8c0..a7ed739e3 100644 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/enums/PayErrorCodeCoreConstants.java +++ b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/enums/PayErrorCodeCoreConstants.java @@ -32,4 +32,18 @@ public interface PayErrorCodeCoreConstants { * ========== 支付商户信息 1-007-004-000 ========== */ ErrorCode MERCHANT_NOT_EXISTS = new ErrorCode(1007004000, "支付商户信息不存在"); + + + /** + * ========== 支付应用信息 1-007-005-000 ========== + */ + ErrorCode APP_NOT_EXISTS = new ErrorCode(1007005000, "支付应用信息不存在"); + + + /** + * ========== 支付渠道 1-007-006-000 ========== + */ + ErrorCode CHANNEL_NOT_EXISTS = new ErrorCode(1007006000, "支付渠道不存在"); + ErrorCode CHANNEL_KEY_READ_ERROR = new ErrorCode(1007006002, "支付渠道秘钥文件读取失败"); + ErrorCode EXIST_SAME_CHANNEL_ERROR = new ErrorCode(1007006003, "已存在相同的渠道"); } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImpl.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImpl.java index 16cc49ed2..7c1c3efb2 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImpl.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImpl.java @@ -58,8 +58,11 @@ public class PayClientFactoryImpl implements PayClientFactory { PayChannelEnum channelEnum = PayChannelEnum.getByCode(channelCode); Assert.notNull(channelEnum, String.format("支付渠道(%s) 为空", channelEnum)); // 创建客户端 + // TODO @芋艿 WX_LITE WX_APP 如果不添加在 项目启动的时候去初始化会报错无法启动。所以我手动加了两个,具体需要你来配 switch (channelEnum) { case WX_PUB: return (AbstractPayClient<Config>) new WXPubPayClient(channelId, (WXPayClientConfig) config); + case WX_LITE: return (AbstractPayClient<Config>) new WXPubPayClient(channelId, (WXPayClientConfig) config); + case WX_APP: return (AbstractPayClient<Config>) new WXPubPayClient(channelId, (WXPayClientConfig) config); case ALIPAY_WAP: return (AbstractPayClient<Config>) new AlipayWapPayClient(channelId, (AlipayPayClientConfig) config); case ALIPAY_QR: return (AbstractPayClient<Config>) new AlipayQrPayClient(channelId, (AlipayPayClientConfig) config); } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/PayChannelEnum.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/PayChannelEnum.java index ed1389e0e..a6734d961 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/PayChannelEnum.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/PayChannelEnum.java @@ -15,7 +15,8 @@ import lombok.Getter; public enum PayChannelEnum { WX_PUB("wx_pub", "微信 JSAPI 支付"), // 公众号的网页 - WX_LITE("wx_lit","微信小程序支付"), + // TODO @芋艿 这个地方你写的是 wx_lit 是不是少写了一个e? 还是我这里多加了一个e + WX_LITE("wx_lite","微信小程序支付"), WX_APP("wx_app", "微信 App 支付"), ALIPAY_PC("alipay_pc", "支付宝 PC 网站支付"),