mirror of
https://gitee.com/dromara/dax-pay
synced 2026-08-08 22:25:32 +08:00
refactor(wx): 微信应用解析/能力管理重构, WxAppResolveService 调整解析逻辑
WxPlatformAppManager 新增查询方法, WxAppResolveService 重构应用解析流程, WxChannelAppCapabilityBatchParam 新增字段, admin/merchant 两端 Controller 适配, PaymentCommonParam 同步补充字段。
This commit is contained in:
@@ -39,11 +39,8 @@ public class WxChannelAppCapabilityController {
|
||||
@PermCode(code = PermCodes.Action.MANAGE)
|
||||
@Operation(summary = "全量保存能力绑定")
|
||||
@PostMapping("/save-batch")
|
||||
public Result<Void> saveBatch(
|
||||
@NotBlank(message = "{validation.field.mchNo.notBlank}") String mchNo,
|
||||
@NotBlank(message = "{validation.field.channelMchNo.notBlank}") String channelMchNo,
|
||||
@RequestBody @Validated WxChannelAppCapabilityBatchParam param) {
|
||||
wxChannelAppCapabilityService.saveBatch(mchNo, channelMchNo, param.getItems());
|
||||
public Result<Void> saveBatch(@RequestBody @Validated WxChannelAppCapabilityBatchParam param) {
|
||||
wxChannelAppCapabilityService.saveBatch(param.getMchNo(), param.getChannelMchNo(), param.getItems());
|
||||
return Res.ok();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
@@ -41,9 +42,13 @@ public abstract class PaymentCommonParam {
|
||||
private String sign;
|
||||
|
||||
/// 请求时间(北京时间,格式 yyyy-MM-dd HH:mm:ss)
|
||||
///
|
||||
/// 双注解支撑两条绑定路径:`@JsonFormat` 供 POST JSON body(Jackson)使用,
|
||||
/// `@DateTimeFormat` 供 GET query / form(Spring ConversionService)使用。
|
||||
@Schema(description = "请求时间(北京时间,yyyy-MM-dd HH:mm:ss)")
|
||||
@NotNull(message = "{validation.field.reqTime.notNull}")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private OffsetDateTime reqTime;
|
||||
|
||||
}
|
||||
|
||||
@@ -44,4 +44,13 @@ public class WxPlatformAppManager extends BaseManager<WxPlatformAppMapper, WxPla
|
||||
.orderByAsc(WxPlatformApp::getCreateTime)
|
||||
.orderByAsc(WxPlatformApp::getId));
|
||||
}
|
||||
|
||||
/// 按应用类型查询全部应用(按创建时间升序),供应用解析做唯一性判断
|
||||
public List<WxPlatformApp> listByAppType(String appType) {
|
||||
return lambdaQuery()
|
||||
.eq(WxPlatformApp::getAppType, appType)
|
||||
.orderByAsc(WxPlatformApp::getCreateTime)
|
||||
.orderByAsc(WxPlatformApp::getId)
|
||||
.list();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package cn.daxpay.open.payment.wx.param;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
@@ -10,12 +11,22 @@ import java.util.List;
|
||||
/// # 通道商户微信应用能力绑定批量保存参数
|
||||
///
|
||||
/// 按通道商户全量覆盖绑定:先清后插;items 为空表示清空该通道商户下全部绑定。
|
||||
/// mchNo 由运营端请求体传入;商户端不传,由登录上下文注入。
|
||||
///
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(title = "通道商户微信应用能力绑定批量保存参数")
|
||||
public class WxChannelAppCapabilityBatchParam {
|
||||
|
||||
// 商户号(运营端必传;商户端由上下文注入,故不加 @NotBlank)
|
||||
@Schema(description = "商户号")
|
||||
private String mchNo;
|
||||
|
||||
// 通道商户号
|
||||
@NotBlank(message = "{validation.field.channelMchNo.notBlank}")
|
||||
@Schema(description = "通道商户号")
|
||||
private String channelMchNo;
|
||||
|
||||
@Valid
|
||||
@Schema(description = "能力绑定列表")
|
||||
private List<WxChannelAppCapabilityParam> items;
|
||||
|
||||
@@ -2,7 +2,6 @@ package cn.daxpay.open.payment.wx.service;
|
||||
|
||||
import cn.daxpay.open.payment.wx.dao.WxChannelAppCapabilityManager;
|
||||
import cn.daxpay.open.payment.wx.dao.WxMchAppManager;
|
||||
import cn.daxpay.open.payment.wx.dao.WxPlatformAppCapabilityManager;
|
||||
import cn.daxpay.open.payment.wx.dao.WxPlatformAppManager;
|
||||
import cn.daxpay.open.payment.wx.entity.WxMchApp;
|
||||
import cn.daxpay.open.payment.wx.entity.WxPlatformApp;
|
||||
@@ -19,6 +18,8 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/// # 微信开放应用解析服务
|
||||
///
|
||||
/// 实现 [WxAppFacade],供通道组装器统一解析 wxAppId + Secret。
|
||||
@@ -33,7 +34,6 @@ public class WxAppResolveService implements WxAppFacade {
|
||||
private final WxPlatformAppAuthConfigService wxPlatformAppAuthConfigService;
|
||||
private final WxMchAppAuthConfigService wxMchAppAuthConfigService;
|
||||
private final WxChannelAppCapabilityManager wxChannelAppCapabilityManager;
|
||||
private final WxPlatformAppCapabilityManager wxPlatformAppCapabilityManager;
|
||||
|
||||
/// 按档位与主键加载应用视图(含 Auth)
|
||||
@Override
|
||||
@@ -60,7 +60,7 @@ public class WxAppResolveService implements WxAppFacade {
|
||||
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR, "error.payment.wx.scopeNotExist");
|
||||
}
|
||||
|
||||
/// 解析单应用:显式 channelAppId → 通道能力绑 → 产品级平台默认能力绑 → appType 推导
|
||||
/// 解析单应用:显式 channelAppId → 通道能力绑 → appType 推导(平台应用唯一命中)
|
||||
@Override
|
||||
public WxAppView resolve(String mchNo, String channelMchNo, String capability, String channelAppId, String product) {
|
||||
// 1. 显式 channelAppId
|
||||
@@ -80,13 +80,14 @@ public class WxAppResolveService implements WxAppFacade {
|
||||
return this.getById(WxAppScopeEnum.PLATFORM, platformBind.get().getWxAppRefId());
|
||||
}
|
||||
}
|
||||
// 3. 产品级平台默认能力绑 + appType 推导可选
|
||||
WxAppView platformFallback = this.resolvePlatformFallback(product, capability);
|
||||
// 3. appType 推导:要求该类型平台应用唯一命中
|
||||
WxAppView platformFallback = this.resolvePlatformFallback(capability);
|
||||
if (platformFallback != null) {
|
||||
return platformFallback;
|
||||
}
|
||||
// 微信: 未绑定应用
|
||||
throw new BizInfoException(CommonErrorCode.UN_SUPPORTED_OPERATE, "error.payment.wx.appNotBound");
|
||||
// 微信: 未配置该能力对应的应用
|
||||
throw new BizInfoException(CommonErrorCode.UN_SUPPORTED_OPERATE,
|
||||
"error.payment.wx.appNotConfigured", capability);
|
||||
}
|
||||
|
||||
/// 解析 ISV 双应用:platform(sp) 必填 + merchant(sub) 可选
|
||||
@@ -108,9 +109,9 @@ public class WxAppResolveService implements WxAppFacade {
|
||||
merchant = this.getById(WxAppScopeEnum.MERCHANT, merchantBind.get().getWxAppRefId());
|
||||
}
|
||||
}
|
||||
// 平台侧回退:产品级平台默认能力绑 / appType 推导
|
||||
// 平台侧回退:appType 推导(平台应用唯一命中)
|
||||
if (platform == null) {
|
||||
platform = this.resolvePlatformFallback(product, capability);
|
||||
platform = this.resolvePlatformFallback(capability);
|
||||
}
|
||||
// channelAppId 命中某侧时按命中侧覆盖
|
||||
if (StrUtil.isNotBlank(channelAppId)) {
|
||||
@@ -130,8 +131,9 @@ public class WxAppResolveService implements WxAppFacade {
|
||||
}
|
||||
}
|
||||
if (platform == null) {
|
||||
// 微信: 未绑定平台应用
|
||||
throw new BizInfoException(CommonErrorCode.UN_SUPPORTED_OPERATE, "error.payment.wx.appNotBound");
|
||||
// 微信: 未配置该能力对应的平台应用(sp 必填)
|
||||
throw new BizInfoException(CommonErrorCode.UN_SUPPORTED_OPERATE,
|
||||
"error.payment.wx.appNotConfigured", capability);
|
||||
}
|
||||
return new WxIsvAppPair(platform, merchant);
|
||||
}
|
||||
@@ -150,23 +152,26 @@ public class WxAppResolveService implements WxAppFacade {
|
||||
throw new DataNotExistException("error.payment.wx.channelAppIdNotFound", channelAppId);
|
||||
}
|
||||
|
||||
/// 产品级平台默认能力绑 → appType 推导(可选)
|
||||
private WxAppView resolvePlatformFallback(String product, String capability) {
|
||||
/// appType 推导兜底:要求该类型平台应用唯一命中
|
||||
///
|
||||
/// - 唯一命中:返回该应用
|
||||
/// - 该类型存在多个平台应用:抛 notUnique,要求显式配置通道能力绑
|
||||
/// - 该类型无平台应用:返回 null,由调用方走最终报错
|
||||
private WxAppView resolvePlatformFallback(String capability) {
|
||||
if (StrUtil.isBlank(capability)) {
|
||||
return null;
|
||||
}
|
||||
if (StrUtil.isNotBlank(product)) {
|
||||
var rel = wxPlatformAppCapabilityManager.findByProductAndCapability(product, capability);
|
||||
if (rel.isPresent()) {
|
||||
return this.getById(WxAppScopeEnum.PLATFORM, rel.get().getWxPlatformAppId());
|
||||
}
|
||||
}
|
||||
// appType 推导可选
|
||||
// appType 推导:要求该类型平台应用唯一命中,>1 拒绝猜测
|
||||
String appType = WxAppTypeEnum.resolveAppType(capability);
|
||||
if (appType != null) {
|
||||
var byType = wxPlatformAppManager.findFirstByAppType(appType);
|
||||
if (byType.isPresent()) {
|
||||
return this.toPlatformView(byType.get());
|
||||
List<WxPlatformApp> apps = wxPlatformAppManager.listByAppType(appType);
|
||||
if (apps.size() > 1) {
|
||||
// 存在多个同类型平台应用,请显式配置通道能力绑定以明确选择
|
||||
throw new BizInfoException(CommonErrorCode.UN_SUPPORTED_OPERATE,
|
||||
"error.payment.wx.appNotUnique", appType);
|
||||
}
|
||||
if (apps.size() == 1) {
|
||||
return this.toPlatformView(apps.getFirst());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -61,6 +61,11 @@ public class WxChannelAppCapabilityService {
|
||||
/// 按通道商户全量保存能力绑定(先清后插)
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void saveBatch(String mchNo, String channelMchNo, List<WxChannelAppCapabilityParam> items) {
|
||||
// mchNo 缺失直接归入"通道商户与商户号不匹配",避免下方 equals 触发 NPE
|
||||
if (mchNo == null || mchNo.isBlank()) {
|
||||
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR,
|
||||
"error.payment.wx.channelMerchantMismatch");
|
||||
}
|
||||
// 校验通道商户存在且 mchNo 匹配
|
||||
ChannelMerchant channelMerchant = channelMerchantManager.findByChannelMchNo(channelMchNo)
|
||||
// 微信: 通道商户不存在或商户号不匹配
|
||||
|
||||
@@ -86,12 +86,10 @@ public class MchWxChannelAppCapabilityController {
|
||||
@PermCode(code = PermCodes.Action.MANAGE)
|
||||
@Operation(summary = "全量保存能力绑定")
|
||||
@PostMapping("/save-batch")
|
||||
public Result<Void> saveBatch(
|
||||
@NotBlank(message = "{validation.field.channelMchNo.notBlank}") String channelMchNo,
|
||||
@RequestBody @Validated WxChannelAppCapabilityBatchParam param) {
|
||||
public Result<Void> saveBatch(@RequestBody @Validated WxChannelAppCapabilityBatchParam param) {
|
||||
String mchNo = this.requireMchNo();
|
||||
this.assertChannelMchOwned(channelMchNo);
|
||||
wxChannelAppCapabilityService.saveBatch(mchNo, channelMchNo, param.getItems());
|
||||
this.assertChannelMchOwned(param.getChannelMchNo());
|
||||
wxChannelAppCapabilityService.saveBatch(mchNo, param.getChannelMchNo(), param.getItems());
|
||||
return Res.ok();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user