mirror of
https://gitee.com/dromara/dax-pay
synced 2026-08-11 15:15:33 +08:00
feat(channel): 微信服务商通道管理迁移(后端)
This commit is contained in:
@@ -210,3 +210,21 @@ ON CONFLICT (id) DO UPDATE SET
|
||||
menu_type = EXCLUDED.menu_type,
|
||||
last_modified_time = EXCLUDED.last_modified_time;
|
||||
|
||||
-- ----------------------------
|
||||
-- 微信服务商相关菜单
|
||||
-- ----------------------------
|
||||
-- 微信服务商应用管理(隐藏,从微信服务商总览页卡片进入)
|
||||
INSERT INTO "public"."iam_perm_menu" VALUES (40503, 405, 'payment:wechat:isvApp', 'admin', 'WechatIsvAppManage', '微信服务商应用', 'WeChat ISV Application', 'menu.payment.wechat.isvApp', NULL, 't', 'f', '/payment/channel/wechat/manage/app/WechatIsvAppManage', '/payment/config/product/wechat-app-manage', NULL, 4, 'f', 't', 'f', 1, 1, 0, 'f', 'subpage', NULL, NULL, NULL, NULL, NULL, NULL, '2026-06-14 00:00:00+00', '2026-06-14 00:00:00+00')
|
||||
ON CONFLICT (id) DO UPDATE SET
|
||||
pid = EXCLUDED.pid,
|
||||
menu_code = EXCLUDED.menu_code,
|
||||
name = EXCLUDED.name,
|
||||
title_cn = EXCLUDED.title_cn,
|
||||
title_en = EXCLUDED.title_en,
|
||||
i18n_key = EXCLUDED.i18n_key,
|
||||
component = EXCLUDED.component,
|
||||
path = EXCLUDED.path,
|
||||
sort_no = EXCLUDED.sort_no,
|
||||
menu_type = EXCLUDED.menu_type,
|
||||
last_modified_time = EXCLUDED.last_modified_time;
|
||||
|
||||
|
||||
102
_config/sql/wechat_isv_app.sql
Normal file
102
_config/sql/wechat_isv_app.sql
Normal file
@@ -0,0 +1,102 @@
|
||||
-- ----------------------------
|
||||
-- 微信服务商应用表
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS wechat_isv_app;
|
||||
CREATE TABLE wechat_isv_app (
|
||||
id int8 NOT NULL,
|
||||
app_name varchar(64) COLLATE pg_catalog.default,
|
||||
app_type varchar(32) COLLATE pg_catalog.default,
|
||||
wx_app_id varchar(64) COLLATE pg_catalog.default,
|
||||
creator int8,
|
||||
create_time timestamptz(6),
|
||||
last_modifier int8,
|
||||
last_modified_time timestamptz(6),
|
||||
version int4 DEFAULT 0,
|
||||
deleted bool NOT NULL DEFAULT false
|
||||
);
|
||||
|
||||
COMMENT ON TABLE wechat_isv_app IS '微信服务商应用表';
|
||||
COMMENT ON COLUMN wechat_isv_app.id IS '主键';
|
||||
COMMENT ON COLUMN wechat_isv_app.app_name IS '应用名称';
|
||||
COMMENT ON COLUMN wechat_isv_app.app_type IS '应用类型';
|
||||
COMMENT ON COLUMN wechat_isv_app.wx_app_id IS '微信应用AppId';
|
||||
COMMENT ON COLUMN wechat_isv_app.creator IS '创建者ID';
|
||||
COMMENT ON COLUMN wechat_isv_app.create_time IS '创建时间';
|
||||
COMMENT ON COLUMN wechat_isv_app.last_modifier IS '最后修改ID';
|
||||
COMMENT ON COLUMN wechat_isv_app.last_modified_time IS '最后修改时间';
|
||||
COMMENT ON COLUMN wechat_isv_app.version IS '版本号';
|
||||
COMMENT ON COLUMN wechat_isv_app.deleted IS '删除标志';
|
||||
|
||||
ALTER TABLE wechat_isv_app ADD CONSTRAINT pk_wechat_isv_app PRIMARY KEY (id);
|
||||
|
||||
-- ----------------------------
|
||||
-- 微信服务商密钥配置表(平台为唯一服务商,密钥全局唯一,按 product 区分)
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS wechat_isv_key_config;
|
||||
CREATE TABLE wechat_isv_key_config (
|
||||
id int8 NOT NULL,
|
||||
product varchar(32) COLLATE pg_catalog.default,
|
||||
wx_mch_id varchar(64) COLLATE pg_catalog.default,
|
||||
api_key_v3 text COLLATE pg_catalog.default,
|
||||
public_key text COLLATE pg_catalog.default,
|
||||
public_key_id varchar(128) COLLATE pg_catalog.default,
|
||||
private_key text COLLATE pg_catalog.default,
|
||||
private_cert text COLLATE pg_catalog.default,
|
||||
cert_serial_no varchar(128) COLLATE pg_catalog.default,
|
||||
creator int8,
|
||||
create_time timestamptz(6),
|
||||
last_modifier int8,
|
||||
last_modified_time timestamptz(6),
|
||||
version int4 DEFAULT 0,
|
||||
deleted bool NOT NULL DEFAULT false
|
||||
);
|
||||
|
||||
COMMENT ON TABLE wechat_isv_key_config IS '微信服务商密钥配置表';
|
||||
COMMENT ON COLUMN wechat_isv_key_config.id IS '主键';
|
||||
COMMENT ON COLUMN wechat_isv_key_config.product IS '产品编码';
|
||||
COMMENT ON COLUMN wechat_isv_key_config.wx_mch_id IS '微信服务商商户号';
|
||||
COMMENT ON COLUMN wechat_isv_key_config.api_key_v3 IS 'APIv3密钥';
|
||||
COMMENT ON COLUMN wechat_isv_key_config.public_key IS '支付公钥';
|
||||
COMMENT ON COLUMN wechat_isv_key_config.public_key_id IS '支付公钥ID';
|
||||
COMMENT ON COLUMN wechat_isv_key_config.private_key IS 'API证书私钥';
|
||||
COMMENT ON COLUMN wechat_isv_key_config.private_cert IS 'API证书';
|
||||
COMMENT ON COLUMN wechat_isv_key_config.cert_serial_no IS '证书序列号';
|
||||
COMMENT ON COLUMN wechat_isv_key_config.creator IS '创建者ID';
|
||||
COMMENT ON COLUMN wechat_isv_key_config.create_time IS '创建时间';
|
||||
COMMENT ON COLUMN wechat_isv_key_config.last_modifier IS '最后修改ID';
|
||||
COMMENT ON COLUMN wechat_isv_key_config.last_modified_time IS '最后修改时间';
|
||||
COMMENT ON COLUMN wechat_isv_key_config.version IS '版本号';
|
||||
COMMENT ON COLUMN wechat_isv_key_config.deleted IS '删除标志';
|
||||
|
||||
ALTER TABLE wechat_isv_key_config ADD CONSTRAINT pk_wechat_isv_key_config PRIMARY KEY (id);
|
||||
|
||||
-- ----------------------------
|
||||
-- 微信服务商应用授权认证配置表
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS wechat_isv_app_auth_config;
|
||||
CREATE TABLE wechat_isv_app_auth_config (
|
||||
id int8 NOT NULL,
|
||||
app_id int8,
|
||||
app_secret text COLLATE pg_catalog.default,
|
||||
auth_callback_url varchar(512) COLLATE pg_catalog.default,
|
||||
creator int8,
|
||||
create_time timestamptz(6),
|
||||
last_modifier int8,
|
||||
last_modified_time timestamptz(6),
|
||||
version int4 DEFAULT 0,
|
||||
deleted bool NOT NULL DEFAULT false
|
||||
);
|
||||
|
||||
COMMENT ON TABLE wechat_isv_app_auth_config IS '微信服务商应用授权认证配置表';
|
||||
COMMENT ON COLUMN wechat_isv_app_auth_config.id IS '主键';
|
||||
COMMENT ON COLUMN wechat_isv_app_auth_config.app_id IS '关联应用ID';
|
||||
COMMENT ON COLUMN wechat_isv_app_auth_config.app_secret IS '应用密钥';
|
||||
COMMENT ON COLUMN wechat_isv_app_auth_config.auth_callback_url IS '授权回调URL';
|
||||
COMMENT ON COLUMN wechat_isv_app_auth_config.creator IS '创建者ID';
|
||||
COMMENT ON COLUMN wechat_isv_app_auth_config.create_time IS '创建时间';
|
||||
COMMENT ON COLUMN wechat_isv_app_auth_config.last_modifier IS '最后修改ID';
|
||||
COMMENT ON COLUMN wechat_isv_app_auth_config.last_modified_time IS '最后修改时间';
|
||||
COMMENT ON COLUMN wechat_isv_app_auth_config.version IS '版本号';
|
||||
COMMENT ON COLUMN wechat_isv_app_auth_config.deleted IS '删除标志';
|
||||
|
||||
ALTER TABLE wechat_isv_app_auth_config ADD CONSTRAINT pk_wechat_isv_app_auth_config PRIMARY KEY (id);
|
||||
@@ -0,0 +1,110 @@
|
||||
package org.dromara.daxpay.channel.wechat.controller.app;
|
||||
|
||||
import org.dromara.daxpay.platform.core.annotation.PermCode;
|
||||
import org.dromara.daxpay.platform.core.rest.Res;
|
||||
import org.dromara.daxpay.platform.core.rest.result.Result;
|
||||
import org.dromara.daxpay.platform.core.util.ValidationUtil;
|
||||
import org.dromara.daxpay.platform.core.validation.ValidationGroup;
|
||||
import org.dromara.daxpay.channel.wechat.param.app.WechatIsvAppParam;
|
||||
import org.dromara.daxpay.channel.wechat.param.config.WechatIsvAppAuthConfigParam;
|
||||
import org.dromara.daxpay.channel.wechat.result.app.WechatIsvAppResult;
|
||||
import org.dromara.daxpay.channel.wechat.result.config.WechatIsvAppAuthConfigResult;
|
||||
import org.dromara.daxpay.channel.wechat.service.app.WechatIsvAppService;
|
||||
import org.dromara.daxpay.channel.wechat.service.config.WechatIsvAppAuthConfigService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/// # 微信服务商应用管理
|
||||
///
|
||||
@PermCode(menuCode = "payment:wechat:isv")
|
||||
@Validated
|
||||
@Tag(name = "微信服务商应用管理")
|
||||
@RestController
|
||||
@RequestMapping("/admin/wechat/isv-app")
|
||||
@RequiredArgsConstructor
|
||||
public class WechatIsvAppController {
|
||||
|
||||
private final WechatIsvAppService wechatIsvAppService;
|
||||
private final WechatIsvAppAuthConfigService wechatIsvAppAuthConfigService;
|
||||
|
||||
@PermCode(code = "view", nameCn = "微信服务商查看", nameEn = "WeChat ISV View")
|
||||
@Operation(summary = "查询服务商应用列表")
|
||||
@GetMapping("/list-all")
|
||||
public Result<List<WechatIsvAppResult>> listAll() {
|
||||
return Res.ok(wechatIsvAppService.listAll());
|
||||
}
|
||||
|
||||
@PermCode(code = "view", nameCn = "微信服务商查看", nameEn = "WeChat ISV View")
|
||||
@Operation(summary = "查询应用详情")
|
||||
@GetMapping("/find-by-id")
|
||||
public Result<WechatIsvAppResult> findById(
|
||||
@NotNull(message = "{validation.field.id.notNull}") Long id) {
|
||||
return Res.ok(wechatIsvAppService.findById(id));
|
||||
}
|
||||
|
||||
@PermCode(code = "view", nameCn = "微信服务商查看", nameEn = "WeChat ISV View")
|
||||
@Operation(summary = "微信应用AppId是否已存在")
|
||||
@GetMapping("/exists-wx-app-id")
|
||||
public Result<Boolean> existsWxAppId(
|
||||
@NotBlank(message = "{validation.field.wxAppId.notBlank}") String wxAppId) {
|
||||
return Res.ok(wechatIsvAppService.existsWxAppId(wxAppId, null));
|
||||
}
|
||||
|
||||
@PermCode(code = "view", nameCn = "微信服务商查看", nameEn = "WeChat ISV View")
|
||||
@Operation(summary = "微信应用AppId是否已存在(排除自身)")
|
||||
@GetMapping("/exists-wx-app-id-not-id")
|
||||
public Result<Boolean> existsWxAppIdNotId(
|
||||
@NotBlank(message = "{validation.field.wxAppId.notBlank}") String wxAppId,
|
||||
@NotNull(message = "{validation.field.id.notNull}") Long id) {
|
||||
return Res.ok(wechatIsvAppService.existsWxAppId(wxAppId, id));
|
||||
}
|
||||
|
||||
@PermCode(code = "add", nameCn = "微信服务商新增", nameEn = "WeChat ISV Add")
|
||||
@Operation(summary = "新增服务商应用")
|
||||
@PostMapping("/add")
|
||||
public Result<Void> add(@RequestBody @Validated(ValidationGroup.add.class) WechatIsvAppParam param) {
|
||||
ValidationUtil.validateParam(param, ValidationGroup.add.class);
|
||||
wechatIsvAppService.add(param);
|
||||
return Res.ok();
|
||||
}
|
||||
|
||||
@PermCode(code = "edit", nameCn = "微信服务商编辑", nameEn = "WeChat ISV Edit")
|
||||
@Operation(summary = "修改服务商应用")
|
||||
@PostMapping("/update")
|
||||
public Result<Void> update(@RequestBody @Validated(ValidationGroup.edit.class) WechatIsvAppParam param) {
|
||||
ValidationUtil.validateParam(param, ValidationGroup.edit.class);
|
||||
wechatIsvAppService.update(param);
|
||||
return Res.ok();
|
||||
}
|
||||
|
||||
@PermCode(code = "edit", nameCn = "微信服务商编辑", nameEn = "WeChat ISV Edit")
|
||||
@Operation(summary = "删除服务商应用")
|
||||
@PostMapping("/delete")
|
||||
public Result<Void> delete(@NotNull(message = "{validation.field.id.notNull}") Long id) {
|
||||
wechatIsvAppService.delete(id);
|
||||
return Res.ok();
|
||||
}
|
||||
|
||||
@PermCode(code = "view", nameCn = "微信服务商查看", nameEn = "WeChat ISV View")
|
||||
@Operation(summary = "查询应用授权认证配置")
|
||||
@GetMapping("/find-auth-config-by-app-id")
|
||||
public Result<WechatIsvAppAuthConfigResult> findAuthConfigByAppId(
|
||||
@NotNull(message = "{validation.field.id.notNull}") Long appId) {
|
||||
return Res.ok(wechatIsvAppAuthConfigService.findByAppId(appId));
|
||||
}
|
||||
|
||||
@PermCode(code = "edit", nameCn = "微信服务商编辑", nameEn = "WeChat ISV Edit")
|
||||
@Operation(summary = "保存应用授权认证配置")
|
||||
@PostMapping("/save-auth-config")
|
||||
public Result<Void> saveAuthConfig(@RequestBody @Validated WechatIsvAppAuthConfigParam param) {
|
||||
wechatIsvAppAuthConfigService.save(param);
|
||||
return Res.ok();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package org.dromara.daxpay.channel.wechat.controller.config;
|
||||
|
||||
import org.dromara.daxpay.platform.core.annotation.PermCode;
|
||||
import org.dromara.daxpay.platform.core.rest.Res;
|
||||
import org.dromara.daxpay.platform.core.rest.result.Result;
|
||||
import org.dromara.daxpay.channel.wechat.param.config.WechatIsvKeyConfigParam;
|
||||
import org.dromara.daxpay.channel.wechat.result.config.WechatIsvKeyConfigResult;
|
||||
import org.dromara.daxpay.channel.wechat.service.config.WechatIsvKeyConfigService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/// # 微信服务商密钥配置
|
||||
///
|
||||
@PermCode(menuCode = "payment:wechat:isv")
|
||||
@Validated
|
||||
@Tag(name = "微信服务商密钥配置")
|
||||
@RestController
|
||||
@RequestMapping("/admin/wechat/isv-key-config")
|
||||
@RequiredArgsConstructor
|
||||
public class WechatIsvKeyConfigController {
|
||||
|
||||
private final WechatIsvKeyConfigService wechatIsvKeyConfigService;
|
||||
|
||||
@PermCode(code = "view", nameCn = "微信服务商查看", nameEn = "WeChat ISV View")
|
||||
@Operation(summary = "查询微信服务商密钥配置")
|
||||
@GetMapping("/find-config")
|
||||
public Result<WechatIsvKeyConfigResult> findConfig(
|
||||
@NotBlank(message = "{validation.field.product.notBlank}") String product) {
|
||||
return Res.ok(wechatIsvKeyConfigService.findByProduct(product));
|
||||
}
|
||||
|
||||
@PermCode(code = "edit", nameCn = "微信服务商编辑", nameEn = "WeChat ISV Edit")
|
||||
@Operation(summary = "保存微信服务商密钥配置")
|
||||
@PostMapping("/save-config")
|
||||
public Result<Void> saveConfig(@RequestBody @Validated WechatIsvKeyConfigParam param) {
|
||||
wechatIsvKeyConfigService.saveConfig(param);
|
||||
return Res.ok();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package org.dromara.daxpay.channel.wechat.convert;
|
||||
|
||||
import org.dromara.daxpay.channel.wechat.entity.config.WechatIsvAppAuthConfig;
|
||||
import org.dromara.daxpay.channel.wechat.param.config.WechatIsvAppAuthConfigParam;
|
||||
import org.dromara.daxpay.channel.wechat.result.config.WechatIsvAppAuthConfigResult;
|
||||
import org.mapstruct.BeanMapping;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.MappingTarget;
|
||||
import org.mapstruct.NullValuePropertyMappingStrategy;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/// # 微信服务商应用授权认证配置转换
|
||||
///
|
||||
@Mapper
|
||||
public interface WechatIsvAppAuthConfigConvert {
|
||||
|
||||
WechatIsvAppAuthConfigConvert CONVERT = Mappers.getMapper(WechatIsvAppAuthConfigConvert.class);
|
||||
|
||||
/// 转换为返回对象
|
||||
WechatIsvAppAuthConfigResult toResult(WechatIsvAppAuthConfig entity);
|
||||
|
||||
/// 转换为实体
|
||||
WechatIsvAppAuthConfig toEntity(WechatIsvAppAuthConfigParam param);
|
||||
|
||||
/// 更新源数据到实体(空值不覆盖)
|
||||
@BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
|
||||
void copy(WechatIsvAppAuthConfigParam param, @MappingTarget WechatIsvAppAuthConfig entity);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.dromara.daxpay.channel.wechat.convert;
|
||||
|
||||
import org.dromara.daxpay.channel.wechat.entity.app.WechatIsvApp;
|
||||
import org.dromara.daxpay.channel.wechat.param.app.WechatIsvAppParam;
|
||||
import org.dromara.daxpay.channel.wechat.result.app.WechatIsvAppResult;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.MappingTarget;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/// # 微信服务商应用转换
|
||||
///
|
||||
@Mapper
|
||||
public interface WechatIsvAppConvert {
|
||||
|
||||
WechatIsvAppConvert CONVERT = Mappers.getMapper(WechatIsvAppConvert.class);
|
||||
|
||||
/// 转换为返回对象
|
||||
WechatIsvAppResult toResult(WechatIsvApp entity);
|
||||
|
||||
/// 转换为实体
|
||||
WechatIsvApp toEntity(WechatIsvAppParam param);
|
||||
|
||||
/// 更新源数据到实体(忽略空值)
|
||||
void copy(WechatIsvAppParam param, @MappingTarget WechatIsvApp entity);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package org.dromara.daxpay.channel.wechat.convert;
|
||||
|
||||
import org.dromara.daxpay.channel.wechat.entity.config.WechatIsvKeyConfig;
|
||||
import org.dromara.daxpay.channel.wechat.param.config.WechatIsvKeyConfigParam;
|
||||
import org.dromara.daxpay.channel.wechat.result.config.WechatIsvKeyConfigResult;
|
||||
import org.mapstruct.BeanMapping;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.MappingTarget;
|
||||
import org.mapstruct.NullValuePropertyMappingStrategy;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/// # 微信服务商密钥配置转换
|
||||
///
|
||||
@Mapper
|
||||
public interface WechatIsvKeyConfigConvert {
|
||||
|
||||
WechatIsvKeyConfigConvert CONVERT = Mappers.getMapper(WechatIsvKeyConfigConvert.class);
|
||||
|
||||
/// 转换为返回对象
|
||||
WechatIsvKeyConfigResult toResult(WechatIsvKeyConfig entity);
|
||||
|
||||
/// 转换为实体
|
||||
WechatIsvKeyConfig toEntity(WechatIsvKeyConfigParam param);
|
||||
|
||||
/// 更新源数据到实体(空值不覆盖,密钥/证书为空时保留原值)
|
||||
@BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
|
||||
void copy(WechatIsvKeyConfigParam param, @MappingTarget WechatIsvKeyConfig entity);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package org.dromara.daxpay.channel.wechat.dao.app;
|
||||
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.impl.BaseManager;
|
||||
import org.dromara.daxpay.channel.wechat.entity.app.WechatIsvApp;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/// # 微信服务商应用
|
||||
///
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class WechatIsvAppManager extends BaseManager<WechatIsvAppMapper, WechatIsvApp> {
|
||||
|
||||
/// 查询全部应用(按创建时间升序,先创建的在前)
|
||||
public List<WechatIsvApp> listAll() {
|
||||
return lambdaQuery()
|
||||
.orderByAsc(WechatIsvApp::getCreateTime)
|
||||
.orderByAsc(WechatIsvApp::getId)
|
||||
.list();
|
||||
}
|
||||
|
||||
/// 查询第一个应用(运行时默认使用)
|
||||
public Optional<WechatIsvApp> findFirst() {
|
||||
return lambdaQuery()
|
||||
.orderByAsc(WechatIsvApp::getCreateTime)
|
||||
.orderByAsc(WechatIsvApp::getId)
|
||||
.last("LIMIT 1")
|
||||
.oneOpt();
|
||||
}
|
||||
|
||||
/// 校验微信应用AppId是否已存在(排除自身)
|
||||
public boolean existsByWxAppId(String wxAppId, Long excludeId) {
|
||||
return lambdaQuery()
|
||||
.eq(WechatIsvApp::getWxAppId, wxAppId)
|
||||
.ne(excludeId != null, WechatIsvApp::getId, excludeId)
|
||||
.exists();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.dromara.daxpay.channel.wechat.dao.app;
|
||||
|
||||
import org.dromara.daxpay.channel.wechat.entity.app.WechatIsvApp;
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/// # 微信服务商应用
|
||||
///
|
||||
@Mapper
|
||||
public interface WechatIsvAppMapper extends MPJBaseMapper<WechatIsvApp> {
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.dromara.daxpay.channel.wechat.dao.config;
|
||||
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.impl.BaseManager;
|
||||
import org.dromara.daxpay.channel.wechat.entity.config.WechatIsvAppAuthConfig;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/// # 微信服务商应用授权认证配置
|
||||
///
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class WechatIsvAppAuthConfigManager extends BaseManager<WechatIsvAppAuthConfigMapper, WechatIsvAppAuthConfig> {
|
||||
|
||||
/// 根据应用ID查询授权认证配置
|
||||
public Optional<WechatIsvAppAuthConfig> findByAppId(Long appId) {
|
||||
return lambdaQuery()
|
||||
.eq(WechatIsvAppAuthConfig::getAppId, appId)
|
||||
.oneOpt();
|
||||
}
|
||||
|
||||
/// 根据应用ID删除授权认证配置
|
||||
public void deleteByAppId(Long appId) {
|
||||
lambdaUpdate()
|
||||
.eq(WechatIsvAppAuthConfig::getAppId, appId)
|
||||
.remove();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.dromara.daxpay.channel.wechat.dao.config;
|
||||
|
||||
import org.dromara.daxpay.channel.wechat.entity.config.WechatIsvAppAuthConfig;
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/// # 微信服务商应用授权认证配置
|
||||
///
|
||||
@Mapper
|
||||
public interface WechatIsvAppAuthConfigMapper extends MPJBaseMapper<WechatIsvAppAuthConfig> {
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.dromara.daxpay.channel.wechat.dao.config;
|
||||
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.impl.BaseManager;
|
||||
import org.dromara.daxpay.channel.wechat.entity.config.WechatIsvKeyConfig;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/// # 微信服务商密钥配置
|
||||
///
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class WechatIsvKeyConfigManager extends BaseManager<WechatIsvKeyConfigMapper, WechatIsvKeyConfig> {
|
||||
|
||||
/// 根据产品编码查询(平台为唯一服务商,密钥全局唯一)
|
||||
public Optional<WechatIsvKeyConfig> findByProduct(String product) {
|
||||
return lambdaQuery()
|
||||
.eq(WechatIsvKeyConfig::getProduct, product)
|
||||
.oneOpt();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.dromara.daxpay.channel.wechat.dao.config;
|
||||
|
||||
import org.dromara.daxpay.channel.wechat.entity.config.WechatIsvKeyConfig;
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/// # 微信服务商密钥配置
|
||||
///
|
||||
@Mapper
|
||||
public interface WechatIsvKeyConfigMapper extends MPJBaseMapper<WechatIsvKeyConfig> {
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package org.dromara.daxpay.channel.wechat.entity.app;
|
||||
|
||||
import org.dromara.daxpay.channel.wechat.convert.WechatIsvAppConvert;
|
||||
import org.dromara.daxpay.channel.wechat.result.app.WechatIsvAppResult;
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.base.MpBaseEntity;
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.function.ToResult;
|
||||
import com.baomidou.mybatisplus.annotation.FieldStrategy;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/// # 微信服务商应用
|
||||
///
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@TableName(value = "wechat_isv_app", autoResultMap = true)
|
||||
public class WechatIsvApp extends MpBaseEntity implements ToResult<WechatIsvAppResult> {
|
||||
|
||||
/// 应用名称
|
||||
private String appName;
|
||||
|
||||
/// 应用类型
|
||||
@TableField(updateStrategy = FieldStrategy.NEVER)
|
||||
private String appType;
|
||||
|
||||
/// 微信应用AppId
|
||||
private String wxAppId;
|
||||
|
||||
/// 转换
|
||||
@Override
|
||||
public WechatIsvAppResult toResult() {
|
||||
return WechatIsvAppConvert.CONVERT.toResult(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package org.dromara.daxpay.channel.wechat.entity.config;
|
||||
|
||||
import org.dromara.daxpay.channel.wechat.convert.WechatIsvAppAuthConfigConvert;
|
||||
import org.dromara.daxpay.channel.wechat.result.config.WechatIsvAppAuthConfigResult;
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.base.MpBaseEntity;
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.function.ToResult;
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.handler.encrypt.DataEncryptTypeHandler;
|
||||
import com.baomidou.mybatisplus.annotation.FieldStrategy;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/// # 微信服务商应用授权认证配置
|
||||
///
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@TableName(value = "wechat_isv_app_auth_config", autoResultMap = true)
|
||||
public class WechatIsvAppAuthConfig extends MpBaseEntity implements ToResult<WechatIsvAppAuthConfigResult> {
|
||||
|
||||
/// 微信服务商应用ID
|
||||
@TableField(updateStrategy = FieldStrategy.NEVER)
|
||||
private Long appId;
|
||||
|
||||
/// 应用密钥(加密存储)
|
||||
@TableField(typeHandler = DataEncryptTypeHandler.class)
|
||||
private String appSecret;
|
||||
|
||||
/// 授权回调地址
|
||||
private String authCallbackUrl;
|
||||
|
||||
/// 转换
|
||||
@Override
|
||||
public WechatIsvAppAuthConfigResult toResult() {
|
||||
return WechatIsvAppAuthConfigConvert.CONVERT.toResult(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package org.dromara.daxpay.channel.wechat.entity.config;
|
||||
|
||||
import org.dromara.daxpay.channel.wechat.convert.WechatIsvKeyConfigConvert;
|
||||
import org.dromara.daxpay.channel.wechat.result.config.WechatIsvKeyConfigResult;
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.base.MpBaseEntity;
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.function.ToResult;
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.handler.encrypt.DataEncryptTypeHandler;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/// # 微信服务商密钥配置
|
||||
///
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@TableName(value = "wechat_isv_key_config", autoResultMap = true)
|
||||
public class WechatIsvKeyConfig extends MpBaseEntity implements ToResult<WechatIsvKeyConfigResult> {
|
||||
|
||||
/// 产品编码
|
||||
private String product;
|
||||
|
||||
/// 微信服务商商户号
|
||||
private String wxMchId;
|
||||
|
||||
/// APIv3密钥(加密存储)
|
||||
@TableField(typeHandler = DataEncryptTypeHandler.class)
|
||||
private String apiKeyV3;
|
||||
|
||||
/// 支付公钥(加密存储)
|
||||
@TableField(typeHandler = DataEncryptTypeHandler.class)
|
||||
private String publicKey;
|
||||
|
||||
/// 支付公钥ID
|
||||
private String publicKeyId;
|
||||
|
||||
/// apiclient_key证书(加密存储)
|
||||
@TableField(typeHandler = DataEncryptTypeHandler.class)
|
||||
private String privateKey;
|
||||
|
||||
/// apiclient_cert证书(加密存储)
|
||||
@TableField(typeHandler = DataEncryptTypeHandler.class)
|
||||
private String privateCert;
|
||||
|
||||
/// 证书序列号
|
||||
private String certSerialNo;
|
||||
|
||||
/// 转换
|
||||
@Override
|
||||
public WechatIsvKeyConfigResult toResult() {
|
||||
return WechatIsvKeyConfigConvert.CONVERT.toResult(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.dromara.daxpay.channel.wechat.enums;
|
||||
|
||||
import org.dromara.daxpay.platform.core.i18n.I18nSupport;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/// # 微信OpenId认证方式
|
||||
///
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public enum WechatAuthTypeEnum implements I18nSupport {
|
||||
/// 服务商用户标识
|
||||
SP("sp"),
|
||||
/// 子商户应用用户标识
|
||||
SUB("sub"),
|
||||
;
|
||||
|
||||
private final String code;
|
||||
|
||||
/// 翻译 key 前缀
|
||||
@Override
|
||||
public String getI18nPrefix() {
|
||||
return "enum.wechat_auth_type";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.dromara.daxpay.channel.wechat.enums;
|
||||
|
||||
import org.dromara.daxpay.platform.core.i18n.I18nSupport;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/// # 微信服务商应用类型
|
||||
///
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public enum WechatIsvAppTypeEnum implements I18nSupport {
|
||||
/// 公众号
|
||||
OFFICIAL_ACCOUNT("official_account"),
|
||||
/// 小程序
|
||||
MINI_PROGRAM("mini_program"),
|
||||
/// 移动应用
|
||||
MOBILE_APP("mobile_app"),
|
||||
;
|
||||
|
||||
private final String code;
|
||||
|
||||
/// 翻译 key 前缀
|
||||
@Override
|
||||
public String getI18nPrefix() {
|
||||
return "enum.wechat_isv_app_type";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.dromara.daxpay.channel.wechat.param.app;
|
||||
|
||||
import org.dromara.daxpay.platform.core.validation.ValidationGroup;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/// # 微信服务商应用保存参数
|
||||
///
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(title = "微信服务商应用保存参数")
|
||||
public class WechatIsvAppParam {
|
||||
|
||||
@NotNull(message = "{validation.field.id.notNull}", groups = ValidationGroup.edit.class)
|
||||
@Schema(description = "主键,新增时不传")
|
||||
private Long id;
|
||||
|
||||
@NotBlank(message = "{validation.field.appName.notBlank}")
|
||||
@Schema(description = "应用名称")
|
||||
private String appName;
|
||||
|
||||
@NotBlank(message = "{validation.field.appType.notBlank}", groups = ValidationGroup.add.class)
|
||||
@Schema(description = "应用类型")
|
||||
private String appType;
|
||||
|
||||
@NotBlank(message = "{validation.field.wxAppId.notBlank}")
|
||||
@Schema(description = "微信应用AppId")
|
||||
private String wxAppId;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.dromara.daxpay.channel.wechat.param.config;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/// # 微信服务商应用授权认证配置保存参数
|
||||
///
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(title = "微信服务商应用授权认证配置保存参数")
|
||||
public class WechatIsvAppAuthConfigParam {
|
||||
|
||||
@NotNull(message = "{validation.field.id.notNull}")
|
||||
@Schema(description = "微信服务商应用ID")
|
||||
private Long appId;
|
||||
|
||||
@Schema(description = "应用密钥(加密存储),所有应用类型首次保存时必填")
|
||||
private String appSecret;
|
||||
|
||||
@Size(max = 512, message = "{validation.field.authCallbackUrl.size}")
|
||||
@Schema(description = "授权回调地址(仅公众号应用需要)")
|
||||
private String authCallbackUrl;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.dromara.daxpay.channel.wechat.param.config;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/// # 微信服务商密钥配置保存参数
|
||||
///
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(title = "微信服务商密钥配置保存参数")
|
||||
public class WechatIsvKeyConfigParam {
|
||||
|
||||
@NotBlank(message = "{validation.field.product.notBlank}")
|
||||
@Schema(description = "产品编码")
|
||||
private String product;
|
||||
|
||||
@NotBlank(message = "{validation.field.wxMchId.notBlank}")
|
||||
@Schema(description = "微信服务商商户号")
|
||||
private String wxMchId;
|
||||
|
||||
@Schema(description = "APIv3密钥(加密存储)")
|
||||
private String apiKeyV3;
|
||||
|
||||
@Schema(description = "支付公钥(加密存储)")
|
||||
private String publicKey;
|
||||
|
||||
@Schema(description = "支付公钥ID")
|
||||
private String publicKeyId;
|
||||
|
||||
@Schema(description = "apiclient_key证书(加密存储)")
|
||||
private String privateKey;
|
||||
|
||||
@Schema(description = "apiclient_cert证书(加密存储)")
|
||||
private String privateCert;
|
||||
|
||||
@Schema(description = "证书序列号")
|
||||
private String certSerialNo;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.dromara.daxpay.channel.wechat.result.app;
|
||||
|
||||
import org.dromara.daxpay.platform.core.result.BaseResult;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/// # 微信服务商应用
|
||||
///
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(title = "微信服务商应用")
|
||||
public class WechatIsvAppResult extends BaseResult {
|
||||
|
||||
@Schema(description = "应用名称")
|
||||
private String appName;
|
||||
|
||||
@Schema(description = "应用类型")
|
||||
private String appType;
|
||||
|
||||
@Schema(description = "微信应用AppId")
|
||||
private String wxAppId;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.dromara.daxpay.channel.wechat.result.config;
|
||||
|
||||
import org.dromara.daxpay.platform.common.json.sensitive.SensitiveInfo;
|
||||
import org.dromara.daxpay.platform.core.result.BaseResult;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/// # 微信服务商应用授权认证配置
|
||||
///
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(title = "微信服务商应用授权认证配置")
|
||||
public class WechatIsvAppAuthConfigResult extends BaseResult {
|
||||
|
||||
@Schema(description = "微信服务商应用ID")
|
||||
private Long appId;
|
||||
|
||||
@SensitiveInfo(front = 12, end = 12)
|
||||
@Schema(description = "应用密钥(加密存储)")
|
||||
private String appSecret;
|
||||
|
||||
@Schema(description = "是否已配置应用密钥")
|
||||
private Boolean appSecretConfigured;
|
||||
|
||||
@Schema(description = "授权回调地址(仅公众号)")
|
||||
private String authCallbackUrl;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package org.dromara.daxpay.channel.wechat.result.config;
|
||||
|
||||
import org.dromara.daxpay.platform.common.json.sensitive.SensitiveInfo;
|
||||
import org.dromara.daxpay.platform.core.result.BaseResult;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/// # 微信服务商密钥配置
|
||||
///
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(title = "微信服务商密钥配置")
|
||||
public class WechatIsvKeyConfigResult extends BaseResult {
|
||||
|
||||
@Schema(description = "产品编码")
|
||||
private String product;
|
||||
|
||||
@Schema(description = "微信服务商商户号")
|
||||
private String wxMchId;
|
||||
|
||||
@SensitiveInfo(front = 12, end = 12)
|
||||
@Schema(description = "APIv3密钥(加密存储)")
|
||||
private String apiKeyV3;
|
||||
|
||||
@SensitiveInfo(front = 12, end = 12)
|
||||
@Schema(description = "支付公钥(加密存储)")
|
||||
private String publicKey;
|
||||
|
||||
@Schema(description = "支付公钥ID")
|
||||
private String publicKeyId;
|
||||
|
||||
@SensitiveInfo(front = 12, end = 12)
|
||||
@Schema(description = "apiclient_key证书(加密存储)")
|
||||
private String privateKey;
|
||||
|
||||
@SensitiveInfo(front = 12, end = 12)
|
||||
@Schema(description = "apiclient_cert证书(加密存储)")
|
||||
private String privateCert;
|
||||
|
||||
@Schema(description = "证书序列号")
|
||||
private String certSerialNo;
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package org.dromara.daxpay.channel.wechat.service.app;
|
||||
|
||||
import org.dromara.daxpay.channel.wechat.convert.WechatIsvAppConvert;
|
||||
import org.dromara.daxpay.channel.wechat.dao.app.WechatIsvAppManager;
|
||||
import org.dromara.daxpay.channel.wechat.entity.app.WechatIsvApp;
|
||||
import org.dromara.daxpay.channel.wechat.enums.WechatIsvAppTypeEnum;
|
||||
import org.dromara.daxpay.channel.wechat.param.app.WechatIsvAppParam;
|
||||
import org.dromara.daxpay.channel.wechat.result.app.WechatIsvAppResult;
|
||||
import org.dromara.daxpay.channel.wechat.service.config.WechatIsvAppAuthConfigService;
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.util.MpUtil;
|
||||
import org.dromara.daxpay.platform.core.code.CommonErrorCode;
|
||||
import org.dromara.daxpay.platform.core.exception.BizInfoException;
|
||||
import org.dromara.daxpay.platform.core.exception.DataNotExistException;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/// # 微信服务商应用管理
|
||||
///
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class WechatIsvAppService {
|
||||
|
||||
private final WechatIsvAppManager wechatIsvAppManager;
|
||||
private final WechatIsvAppAuthConfigService wechatIsvAppAuthConfigService;
|
||||
|
||||
/// 查询全部应用列表
|
||||
public List<WechatIsvAppResult> listAll() {
|
||||
List<WechatIsvApp> list = wechatIsvAppManager.listAll();
|
||||
return MpUtil.toListResult(list);
|
||||
}
|
||||
|
||||
/// 根据ID查询应用详情
|
||||
public WechatIsvAppResult findById(Long id) {
|
||||
return wechatIsvAppManager.findById(id)
|
||||
.map(WechatIsvApp::toResult)
|
||||
.orElseThrow(() -> new DataNotExistException("error.channel.wechat.appNotFound"));
|
||||
}
|
||||
|
||||
/// 查询第一个应用(运行时默认使用)
|
||||
public Optional<WechatIsvApp> findFirst() {
|
||||
return wechatIsvAppManager.findFirst();
|
||||
}
|
||||
|
||||
/// 判断微信应用AppId是否已存在
|
||||
public boolean existsWxAppId(String wxAppId, Long excludeId) {
|
||||
if (StrUtil.isBlank(wxAppId)) {
|
||||
return false;
|
||||
}
|
||||
return wechatIsvAppManager.existsByWxAppId(wxAppId, excludeId);
|
||||
}
|
||||
|
||||
/// 新增微信服务商应用
|
||||
public void add(WechatIsvAppParam param) {
|
||||
this.assertWxAppIdUnique(param.getWxAppId(), null);
|
||||
this.validateAppType(param.getAppType());
|
||||
WechatIsvApp entity = WechatIsvAppConvert.CONVERT.toEntity(param);
|
||||
wechatIsvAppManager.save(entity);
|
||||
}
|
||||
|
||||
/// 更新微信服务商应用
|
||||
public void update(WechatIsvAppParam param) {
|
||||
WechatIsvApp entity = wechatIsvAppManager.findById(param.getId())
|
||||
.orElseThrow(() -> new DataNotExistException("error.channel.wechat.appNotFound"));
|
||||
this.assertWxAppIdUnique(param.getWxAppId(), param.getId());
|
||||
WechatIsvAppConvert.CONVERT.copy(param, entity);
|
||||
wechatIsvAppManager.updateById(entity);
|
||||
}
|
||||
|
||||
/// 删除应用
|
||||
public void delete(Long id) {
|
||||
wechatIsvAppManager.findById(id)
|
||||
.orElseThrow(() -> new DataNotExistException("error.channel.wechat.appNotFound"));
|
||||
wechatIsvAppAuthConfigService.deleteByAppId(id);
|
||||
wechatIsvAppManager.deleteById(id);
|
||||
}
|
||||
|
||||
/// 校验应用AppId唯一
|
||||
private void assertWxAppIdUnique(String wxAppId, Long excludeId) {
|
||||
if (this.existsWxAppId(wxAppId, excludeId)) {
|
||||
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR, "error.channel.wechat.appIdDuplicate");
|
||||
}
|
||||
}
|
||||
|
||||
/// 校验应用类型
|
||||
private void validateAppType(String appType) {
|
||||
boolean valid = Arrays.stream(WechatIsvAppTypeEnum.values())
|
||||
.anyMatch(item -> item.getCode().equals(appType));
|
||||
if (!valid) {
|
||||
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR, "error.channel.wechat.appTypeInvalid");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package org.dromara.daxpay.channel.wechat.service.config;
|
||||
|
||||
import org.dromara.daxpay.channel.wechat.convert.WechatIsvAppAuthConfigConvert;
|
||||
import org.dromara.daxpay.channel.wechat.dao.app.WechatIsvAppManager;
|
||||
import org.dromara.daxpay.channel.wechat.dao.config.WechatIsvAppAuthConfigManager;
|
||||
import org.dromara.daxpay.channel.wechat.entity.app.WechatIsvApp;
|
||||
import org.dromara.daxpay.channel.wechat.entity.config.WechatIsvAppAuthConfig;
|
||||
import org.dromara.daxpay.channel.wechat.enums.WechatIsvAppTypeEnum;
|
||||
import org.dromara.daxpay.channel.wechat.param.config.WechatIsvAppAuthConfigParam;
|
||||
import org.dromara.daxpay.channel.wechat.result.config.WechatIsvAppAuthConfigResult;
|
||||
import org.dromara.daxpay.platform.core.code.CommonErrorCode;
|
||||
import org.dromara.daxpay.platform.core.exception.BizInfoException;
|
||||
import org.dromara.daxpay.platform.core.exception.DataNotExistException;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/// # 微信服务商应用授权认证配置
|
||||
///
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class WechatIsvAppAuthConfigService {
|
||||
|
||||
private final WechatIsvAppAuthConfigManager wechatIsvAppAuthConfigManager;
|
||||
private final WechatIsvAppManager wechatIsvAppManager;
|
||||
|
||||
/// 根据应用ID查询授权认证配置
|
||||
public WechatIsvAppAuthConfigResult findByAppId(Long appId) {
|
||||
WechatIsvApp app = wechatIsvAppManager.findById(appId)
|
||||
.orElseThrow(() -> new DataNotExistException("error.channel.wechat.appNotFound"));
|
||||
return wechatIsvAppAuthConfigManager.findByAppId(appId)
|
||||
.map(config -> WechatIsvAppAuthConfigConvert.CONVERT.toResult(config)
|
||||
.setAppSecretConfigured(StrUtil.isNotBlank(config.getAppSecret())))
|
||||
.orElseGet(() -> new WechatIsvAppAuthConfigResult()
|
||||
.setAppId(appId)
|
||||
.setAppSecretConfigured(false));
|
||||
}
|
||||
|
||||
/// 保存应用授权认证配置
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void save(WechatIsvAppAuthConfigParam param) {
|
||||
WechatIsvApp app = wechatIsvAppManager.findById(param.getAppId())
|
||||
.orElseThrow(() -> new DataNotExistException("error.channel.wechat.appNotFound"));
|
||||
String authCallbackUrl = this.resolveAuthCallbackUrl(app, param.getAuthCallbackUrl());
|
||||
Optional<WechatIsvAppAuthConfig> existing = wechatIsvAppAuthConfigManager.findByAppId(param.getAppId());
|
||||
if (existing.isPresent()) {
|
||||
WechatIsvAppAuthConfig config = existing.get();
|
||||
if (StrUtil.isNotBlank(param.getAppSecret())) {
|
||||
config.setAppSecret(param.getAppSecret());
|
||||
} else if (StrUtil.isBlank(config.getAppSecret())) {
|
||||
// 所有应用类型均须配置 AppSecret
|
||||
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR, "error.channel.wechat.appSecretRequired");
|
||||
}
|
||||
config.setAuthCallbackUrl(authCallbackUrl);
|
||||
wechatIsvAppAuthConfigManager.updateById(config);
|
||||
} else {
|
||||
if (StrUtil.isBlank(param.getAppSecret())) {
|
||||
// 所有应用类型首次保存均须填写 AppSecret
|
||||
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR, "error.channel.wechat.appSecretRequired");
|
||||
}
|
||||
WechatIsvAppAuthConfig config = WechatIsvAppAuthConfigConvert.CONVERT.toEntity(param);
|
||||
config.setAppId(param.getAppId());
|
||||
config.setAuthCallbackUrl(authCallbackUrl);
|
||||
wechatIsvAppAuthConfigManager.save(config);
|
||||
}
|
||||
}
|
||||
|
||||
/// 仅公众号保留授权回调地址,其他类型清空
|
||||
private String resolveAuthCallbackUrl(WechatIsvApp app, String authCallbackUrl) {
|
||||
if (!WechatIsvAppTypeEnum.OFFICIAL_ACCOUNT.getCode().equals(app.getAppType())) {
|
||||
return null;
|
||||
}
|
||||
if (StrUtil.isBlank(authCallbackUrl)) {
|
||||
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR, "validation.field.authCallbackUrl.notBlank");
|
||||
}
|
||||
return authCallbackUrl.trim();
|
||||
}
|
||||
|
||||
/// 删除应用授权认证配置
|
||||
public void deleteByAppId(Long appId) {
|
||||
wechatIsvAppAuthConfigManager.deleteByAppId(appId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package org.dromara.daxpay.channel.wechat.service.config;
|
||||
|
||||
import org.dromara.daxpay.channel.wechat.convert.WechatIsvKeyConfigConvert;
|
||||
import org.dromara.daxpay.channel.wechat.dao.config.WechatIsvKeyConfigManager;
|
||||
import org.dromara.daxpay.channel.wechat.entity.config.WechatIsvKeyConfig;
|
||||
import org.dromara.daxpay.channel.wechat.param.config.WechatIsvKeyConfigParam;
|
||||
import org.dromara.daxpay.channel.wechat.result.config.WechatIsvKeyConfigResult;
|
||||
import org.dromara.daxpay.platform.core.code.CommonErrorCode;
|
||||
import org.dromara.daxpay.platform.core.exception.BizInfoException;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/// # 微信服务商密钥配置
|
||||
///
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class WechatIsvKeyConfigService {
|
||||
|
||||
private final WechatIsvKeyConfigManager wechatIsvKeyConfigManager;
|
||||
|
||||
/// 根据产品编码查询微信服务商密钥配置(平台为唯一服务商,密钥全局唯一)
|
||||
/// 注意: 微信服务商模式不支持沙箱环境
|
||||
public WechatIsvKeyConfigResult findByProduct(String product) {
|
||||
Optional<WechatIsvKeyConfig> config = wechatIsvKeyConfigManager.findByProduct(product);
|
||||
return config.map(WechatIsvKeyConfigConvert.CONVERT::toResult).orElse(null);
|
||||
}
|
||||
|
||||
/// 保存微信服务商密钥配置
|
||||
/// 注意: 微信服务商模式不支持沙箱环境
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void saveConfig(WechatIsvKeyConfigParam param) {
|
||||
Optional<WechatIsvKeyConfig> existing = wechatIsvKeyConfigManager.findByProduct(param.getProduct());
|
||||
WechatIsvKeyConfig config;
|
||||
if (existing.isPresent()) {
|
||||
config = existing.get();
|
||||
config.setWxMchId(param.getWxMchId());
|
||||
if (StrUtil.isNotBlank(param.getApiKeyV3())) {
|
||||
config.setApiKeyV3(param.getApiKeyV3());
|
||||
}
|
||||
if (StrUtil.isNotBlank(param.getPublicKey())) {
|
||||
config.setPublicKey(param.getPublicKey());
|
||||
}
|
||||
if (StrUtil.isNotBlank(param.getPublicKeyId())) {
|
||||
config.setPublicKeyId(param.getPublicKeyId());
|
||||
}
|
||||
if (StrUtil.isNotBlank(param.getPrivateKey())) {
|
||||
config.setPrivateKey(param.getPrivateKey());
|
||||
}
|
||||
if (StrUtil.isNotBlank(param.getPrivateCert())) {
|
||||
config.setPrivateCert(param.getPrivateCert());
|
||||
}
|
||||
if (StrUtil.isNotBlank(param.getCertSerialNo())) {
|
||||
config.setCertSerialNo(param.getCertSerialNo());
|
||||
}
|
||||
wechatIsvKeyConfigManager.updateById(config);
|
||||
} else {
|
||||
this.validateForCreate(param);
|
||||
config = WechatIsvKeyConfigConvert.CONVERT.toEntity(param);
|
||||
config.setProduct(param.getProduct());
|
||||
wechatIsvKeyConfigManager.save(config);
|
||||
}
|
||||
}
|
||||
|
||||
/// 新增时的必填校验
|
||||
private void validateForCreate(WechatIsvKeyConfigParam param) {
|
||||
if (StrUtil.isBlank(param.getPrivateKey())) {
|
||||
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR, "error.channel.wechat.privateKeyRequired");
|
||||
}
|
||||
if (StrUtil.isBlank(param.getApiKeyV3())) {
|
||||
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR, "error.channel.wechat.apiV3KeyRequired");
|
||||
}
|
||||
if (StrUtil.isBlank(param.getPublicKey())) {
|
||||
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR, "error.channel.wechat.publicKeyRequired");
|
||||
}
|
||||
if (StrUtil.isBlank(param.getPrivateCert())) {
|
||||
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR, "error.channel.wechat.privateCertRequired");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user