refactor(backend): 清理死方法与 @Deprecated 代码

删除无引用工具类/方法及废弃字段,收缩 PayUtil/DateTimeUtil/MpUtil 等实现,并移除 Service/Manager 中未使用的查询脚手架。
This commit is contained in:
DaxPay Dev
2026-07-14 09:08:19 +08:00
parent a54450a998
commit b023805120
36 changed files with 6 additions and 1022 deletions

View File

@@ -2,7 +2,6 @@ package cn.daxpay.open.channel.alipay.dao.direct;
import cn.daxpay.open.channel.alipay.entity.direct.AlipayDirectAppCapability;
import cn.daxpay.open.platform.common.mybatisplus.impl.BaseManager;
import cn.daxpay.open.platform.core.annotation.IgnoreTenant;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -36,12 +35,6 @@ public class AlipayDirectAppCapabilityManager extends BaseManager<AlipayDirectAp
.oneOpt();
}
/// 根据通道商户号与支付能力查询单条关联(认证引导,忽略租户)
@IgnoreTenant
public Optional<AlipayDirectAppCapability> findOneNotTenant(String channelMchNo, String capability) {
return findOne(channelMchNo, capability);
}
/// 根据通道商户号删除全部关联(批量保存时先清后插)
public void deleteByChannelMchNo(String channelMchNo) {
lambdaUpdate()

View File

@@ -2,7 +2,6 @@ package cn.daxpay.open.channel.alipay.dao.direct;
import cn.daxpay.open.platform.common.mybatisplus.impl.BaseManager;
import cn.daxpay.open.channel.alipay.entity.direct.AlipayDirectApp;
import cn.daxpay.open.platform.core.annotation.IgnoreTenant;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -14,7 +13,6 @@ import java.util.Optional;
///
/// - 配置态 CRUD: [#listByMchNoAndChannelMchNo]、[#existsByChannelMchNoAndAliAppId]、[#findFirstByMchNo]
/// - 支付/回调(已装载 mchNo: 租户内 [#findFirstByChannelMchNo]、[#findFirstByChannelMchNoAndAppType]
/// - 认证引导: 方法名带 NotTenant
///
@Slf4j
@Service
@@ -66,16 +64,4 @@ public class AlipayDirectAppManager extends BaseManager<AlipayDirectAppMapper, A
.orderByAsc(AlipayDirectApp::getId));
}
/// 根据通道商户号与应用类型查询首个应用(认证引导,忽略租户)
@IgnoreTenant
public Optional<AlipayDirectApp> findFirstByChannelMchNoAndAppTypeNotTenant(String channelMchNo, String appType) {
return findFirstByChannelMchNoAndAppType(channelMchNo, appType);
}
/// 根据通道商户号查询首个应用(认证引导,忽略租户)
@IgnoreTenant
public Optional<AlipayDirectApp> findFirstByChannelMchNoNotTenant(String channelMchNo) {
return findFirstByChannelMchNo(channelMchNo);
}
}

View File

@@ -2,7 +2,6 @@ package cn.daxpay.open.channel.douyin.dao.direct;
import cn.daxpay.open.channel.douyin.entity.direct.DouyinDirectAppCapability;
import cn.daxpay.open.platform.common.mybatisplus.impl.BaseManager;
import cn.daxpay.open.platform.core.annotation.IgnoreTenant;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -36,11 +35,6 @@ public class DouyinDirectAppCapabilityManager extends BaseManager<DouyinDirectAp
.oneOpt();
}
/// 根据通道商户号与支付能力查询单条关联(认证引导,忽略租户)
@IgnoreTenant
public Optional<DouyinDirectAppCapability> findOneNotTenant(String channelMchNo, String capability) {
return findOne(channelMchNo, capability);
}
/// 根据通道商户号删除全部关联(批量保存时先清后插)
public void deleteByChannelMchNo(String channelMchNo) {

View File

@@ -2,7 +2,6 @@ package cn.daxpay.open.channel.douyin.dao.direct;
import cn.daxpay.open.channel.douyin.entity.direct.DouyinDirectApp;
import cn.daxpay.open.platform.common.mybatisplus.impl.BaseManager;
import cn.daxpay.open.platform.core.annotation.IgnoreTenant;
import org.springframework.stereotype.Repository;
import java.util.List;
@@ -12,7 +11,6 @@ import java.util.Optional;
///
/// - 配置态 CRUD: [#listByMchNoAndChannelMchNo]、[#existsByChannelMchNoAndDouyinAppId]
/// - 支付/回调(已装载 mchNo: 租户内 [#findFirstByChannelMchNo]、[#findFirstByChannelMchNoAndAppType]
/// - 认证引导: 方法名带 NotTenant
///
@Repository
public class DouyinDirectAppManager extends BaseManager<DouyinDirectAppMapper, DouyinDirectApp> {
@@ -58,16 +56,4 @@ public class DouyinDirectAppManager extends BaseManager<DouyinDirectAppMapper, D
.oneOpt();
}
/// 根据通道商户号取首个应用(认证引导,忽略租户)
@IgnoreTenant
public Optional<DouyinDirectApp> findFirstByChannelMchNoNotTenant(String channelMchNo) {
return findFirstByChannelMchNo(channelMchNo);
}
/// 根据通道商户号与应用类型取首个应用(认证引导,忽略租户)
@IgnoreTenant
public Optional<DouyinDirectApp> findFirstByChannelMchNoAndAppTypeNotTenant(String channelMchNo, String appType) {
return findFirstByChannelMchNoAndAppType(channelMchNo, appType);
}
}

View File

@@ -2,7 +2,6 @@ package cn.daxpay.open.channel.wechat.dao.direct;
import cn.daxpay.open.channel.wechat.entity.direct.WechatDirectAppCapability;
import cn.daxpay.open.platform.common.mybatisplus.impl.BaseManager;
import cn.daxpay.open.platform.core.annotation.IgnoreTenant;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -36,12 +35,6 @@ public class WechatDirectAppCapabilityManager extends BaseManager<WechatDirectAp
.oneOpt();
}
/// 根据通道商户号与支付能力查询单条关联(认证等无租户上下文时使用,忽略租户)
@IgnoreTenant
public Optional<WechatDirectAppCapability> findOneNotTenant(String channelMchNo, String capability) {
return findOne(channelMchNo, capability);
}
/// 根据通道商户号删除全部关联(批量保存时先清后插)
public void deleteByChannelMchNo(String channelMchNo) {
lambdaUpdate()

View File

@@ -52,18 +52,6 @@ public class WechatDirectAppManager extends BaseManager<WechatDirectAppMapper, W
.eq(WechatDirectApp::getWxAppId, wxAppId));
}
/// 按通道商户号查询首个应用(认证引导,忽略租户)
@IgnoreTenant
public Optional<WechatDirectApp> findFirstByChannelMchNoNotTenant(String channelMchNo) {
return findFirstByChannelMchNo(channelMchNo);
}
/// 按通道商户号与应用类型查询首个应用(认证引导,忽略租户)
@IgnoreTenant
public Optional<WechatDirectApp> findFirstByChannelMchNoAndAppTypeNotTenant(String channelMchNo, String appType) {
return findFirstByChannelMchNoAndAppType(channelMchNo, appType);
}
/// 按通道商户号与wxAppId查询应用认证引导忽略租户
@IgnoreTenant
public Optional<WechatDirectApp> findByChannelMchNoAndWxAppIdNotTenant(String channelMchNo, String wxAppId) {

View File

@@ -2,7 +2,6 @@ package cn.daxpay.open.channel.wechat.dao.isv;
import cn.daxpay.open.channel.wechat.entity.isv.WechatIsvMchAppCapability;
import cn.daxpay.open.platform.common.mybatisplus.impl.BaseManager;
import cn.daxpay.open.platform.core.annotation.IgnoreTenant;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -36,12 +35,6 @@ public class WechatIsvMchAppCapabilityManager extends BaseManager<WechatIsvMchAp
.oneOpt();
}
/// 根据通道商户号与支付能力查询单条关联(认证引导,忽略租户)
@IgnoreTenant
public Optional<WechatIsvMchAppCapability> findOneNotTenant(String channelMchNo, String capability) {
return findOne(channelMchNo, capability);
}
/// 根据通道商户号删除全部关联(批量保存时先清后插)
public void deleteByChannelMchNo(String channelMchNo) {
lambdaUpdate()

View File

@@ -12,8 +12,7 @@ import java.util.Optional;
///
/// 服务商通道商户应用(子商户应用)数据访问管理器。方法按租户隔离边界分两类:
/// - 配置态(管理端 CRUD, 带租户隔离): [#listByMchNoAndChannelMchNo]、[#existsByChannelMchNoAndWxAppId]
/// - 运行态(认证/支付解析, 忽略租户, 方法名带 NotTenant): [#findFirstByChannelMchNoNotTenant]
/// [#findFirstByChannelMchNoAndAppTypeNotTenant]、[#findByChannelMchNoAndWxAppIdNotTenant]
/// - 运行态(认证/支付解析, 忽略租户, 方法名带 NotTenant): [#findByChannelMchNoAndWxAppIdNotTenant]
/// 按主键见基类 [BaseManager.findByIdNotTenant]
///
@Repository
@@ -29,25 +28,6 @@ public class WechatIsvMchAppManager extends BaseManager<WechatIsvMchAppMapper, W
.list();
}
/// 按通道商户号查询首个应用(能力解析兜底时使用, 忽略租户隔离)
@IgnoreTenant
public Optional<WechatIsvMchApp> findFirstByChannelMchNoNotTenant(String channelMchNo) {
return firstOpt(q -> q
.eq(WechatIsvMchApp::getChannelMchNo, channelMchNo)
.orderByAsc(WechatIsvMchApp::getCreateTime)
.orderByAsc(WechatIsvMchApp::getId));
}
/// 按通道商户号与应用类型查询首个应用(能力→应用类型推导时使用, 忽略租户隔离)
@IgnoreTenant
public Optional<WechatIsvMchApp> findFirstByChannelMchNoAndAppTypeNotTenant(String channelMchNo, String appType) {
return firstOpt(q -> q
.eq(WechatIsvMchApp::getChannelMchNo, channelMchNo)
.eq(WechatIsvMchApp::getAppType, appType)
.orderByAsc(WechatIsvMchApp::getCreateTime)
.orderByAsc(WechatIsvMchApp::getId));
}
/// 按通道商户号与wxAppId查询应用(opAppId显式指定认证应用时使用, 校验该appId在系统中预配过, 忽略租户隔离)
@IgnoreTenant
public Optional<WechatIsvMchApp> findByChannelMchNoAndWxAppIdNotTenant(String channelMchNo, String wxAppId) {

View File

@@ -1,24 +1,10 @@
package cn.daxpay.open.payment.common.util;
import cn.daxpay.open.platform.core.exception.operation.OperationFailException;
import cn.daxpay.open.platform.core.enums.pay.channel.PayMethodEnum;
import cn.daxpay.open.platform.core.enums.pay.channel.PayProviderEnum;
import cn.hutool.core.util.StrUtil;
import jakarta.servlet.http.HttpServletRequest;
import lombok.experimental.UtilityClass;
import org.springframework.util.LinkedMultiValueMap;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.time.Duration;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import java.util.HashMap;
import java.util.Map;
import cn.daxpay.open.platform.core.code.CommonCode;
/// # 支付工具类
///
@@ -30,84 +16,4 @@ public class PayUtil {
return OffsetDateTime.now(ZoneOffset.UTC).plus(minute, ChronoUnit.MINUTES);
}
/// 获取支付单的超时分钟数, 舍去秒数, 所以会有大约一分钟的误差
public int getPaymentExpiredTime(OffsetDateTime date) {
Duration duration = Duration.between(OffsetDateTime.now(ZoneOffset.UTC), date);
return Math.toIntExact(duration.getSeconds() / 60);
}
/// 保留两位小数
public BigDecimal toDecimal(BigDecimal amount) {
return amount.setScale(2, RoundingMode.HALF_UP);
}
/// 获取请求参数
public Map<String, String> toMap(HttpServletRequest request) {
Map<String, String> params = new HashMap<>();
Map<String, String[]> requestParams = request.getParameterMap();
for (String name : requestParams.keySet()) {
String[] values = requestParams.get(name);
String valueStr = "";
for (int i = 0; i < values.length; ++i) {
valueStr = i == values.length - 1 ? valueStr + values[i] : valueStr + values[i] + ",";
}
params.put(name, valueStr);
}
return params;
}
/// 是否为付款码类支付方式
public boolean isBarcodePayMethod(String methodCode) {
if (StrUtil.isBlank(methodCode)) {
return false;
}
PayMethodEnum method = PayMethodEnum.findByCode(methodCode);
return method == PayMethodEnum.AGGREGATE_PAY_BARCODE
|| method == PayMethodEnum.WECHAT_BARCODE
|| method == PayMethodEnum.ALIPAY_BARCODE
|| method == PayMethodEnum.UNION_PAY_BARCODE;
}
/// 获取付款码类型
public PayProviderEnum getBarCodeType(String barCode) {
// 支付宝
String[] ali = {"25", "26", "27", "28", "29", "30"};
if (StrUtil.startWithAny(barCode.substring(0, 2), ali)) {
return PayProviderEnum.ALIPAY;
}
// 微信
String[] wx = {"10", "11", "12", "13", "14", "15"};
if (StrUtil.startWithAny(barCode.substring(0, 2), wx)) {
return PayProviderEnum.WECHAT;
}
// 银联
if (StrUtil.startWith(barCode.substring(0, 2), "62")) {
return PayProviderEnum.UNION_PAY;
}
// 订单: 不支持的条码类型
throw new OperationFailException(CommonCode.FAIL_CODE, "error.payment.order.unsupportedBarcodeType");
}
/// 转换为RestClient使用的form表单参数
public LinkedMultiValueMap<String, String> toFormData(Map<String, ?> param) {
// 转换为 MultiValueMap<String, String>
var formData = new LinkedMultiValueMap<String, String>();
// 注意Object 要转 String
param.forEach((key, value) -> formData.add(key, String.valueOf(value)));
return formData;
}
/// 转换为Get请求使用的QueryParam参数不进行编码
public String toQueryParam(Map<String, String> param) {
StringBuilder sb = new StringBuilder();
param.forEach((key, value) -> sb.append(key).append("=").append(URLEncoder.encode(value, Charset.defaultCharset())).append("&"));
if (!sb.isEmpty()) {
sb.deleteCharAt(sb.length() - 1);
}
return sb.toString();
}
}

View File

@@ -30,11 +30,6 @@ public class PayCapabilityManager extends BaseManager<PayCapabilityMapper, PayCa
return lambdaQuery().eq(PayCapability::getCode, code).oneOpt();
}
/// 是否存在任意主数据行(预留:后续若能力参与运行时收窄可据此分支)
public boolean hasAny() {
return lambdaQuery().count() > 0;
}
/// 分页(默认 sort_no、id 升序)
public Page<PayCapability> page(PageParam pageParam, PayCapabilityQuery query) {
Page<PayCapability> mpPage = MpUtil.getMpPage(pageParam);

View File

@@ -37,15 +37,6 @@ public class PayProviderMethodManager extends BaseManager<PayProviderMethodMappe
return provider + "|" + method;
}
/// 某支付方式下的全部关联(未删除)
public List<PayProviderMethod> listByMethod(String methodCode) {
return lambdaQuery()
.eq(PayProviderMethod::getMethod, methodCode)
.orderByAsc(PayProviderMethod::getSortNo)
.orderByAsc(PayProviderMethod::getId)
.list();
}
/// 某支付渠道下的全部关联(未删除)
public List<PayProviderMethod> listByProvider(String providerCode) {
return lambdaQuery()

View File

@@ -73,11 +73,6 @@ public class PayChannelService {
return buildChannels();
}
/// 按通道编码取展示名称
public String findNameByCode(String code) {
return resolveChannelName(code);
}
/// 枚举与库表合并为通道列表
private List<PayChannelResult> buildChannels() {
Map<String, PayChannel> dbMap = payChannelManager.listAllOrdered().stream()

View File

@@ -90,11 +90,6 @@ public class PayProviderMethodService {
.toList();
}
/// 同 {@link #listMethodsForProvider(PayProviderEnum)}
public List<PayMethodEnum> listMethodsForBrand(PayProviderEnum brand) {
return listMethodsForProvider(brand);
}
/// 平铺返回目录中的渠道与支付方式(不按渠道分组,不含支持的产品)
public List<PayProviderMethodResult> listDirectoryFlat() {
DirectoryLoadContext context = loadDirectoryContext();

View File

@@ -7,7 +7,6 @@ import cn.daxpay.open.platform.core.annotation.IgnoreTenant;
import cn.daxpay.open.platform.core.exception.config.ConfigNotExistException;
import cn.daxpay.open.platform.core.rest.param.PageParam;
import cn.daxpay.open.payment.merchant.entity.appinfo.MchAppInfo;
import cn.daxpay.open.platform.core.enums.merchant.MchAppStatusEnum;
import cn.daxpay.open.payment.merchant.param.appinfo.MchAppInfoQuery;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -48,20 +47,6 @@ public class MchAppInfoManager extends BaseManager<MchAppInfoMapper, MchAppInfo>
return this.findByField(MchAppInfo::getAppId, appId);
}
/// 按应用号加载商户应用(运行态引导,忽略租户)
@IgnoreTenant
public MchAppInfo requireByAppIdNotTenant(String appId) {
return findByAppIdNotTenant(appId)
// 商户: 未找到指定应用的配置
.orElseThrow(() -> new ConfigNotExistException("error.payment.merchant.specifiedAppConfigNotFound"));
}
/// 按应用号解析商户号(运行态引导,忽略租户)
@IgnoreTenant
public String requireMchNoByAppIdNotTenant(String appId) {
return requireByAppIdNotTenant(appId).getMchNo();
}
/// 分页
public Page<MchAppInfo> page(PageParam pageParam, MchAppInfoQuery query) {
Page<MchAppInfo> mpPage = MpUtil.getMpPage(pageParam);
@@ -86,14 +71,6 @@ public class MchAppInfoManager extends BaseManager<MchAppInfoMapper, MchAppInfo>
.oneOpt();
}
/// 根据商户号查询启用的应用
public List<MchAppInfo> findAllByMchNoAndEnable(String mchNo) {
return lambdaQuery()
.eq(MchAppInfo::getMchNo, mchNo)
.eq(MchAppInfo::getStatus, MchAppStatusEnum.ENABLE.getCode())
.list();
}
/// 根据商户号查询应用
public List<MchAppInfo> findAllByMchNo(String mchNo) {
return lambdaQuery()

View File

@@ -49,9 +49,4 @@ public class MerchantInfoManager extends BaseManager<MerchantInfoMapper, Merchan
.list();
}
/// 查询商户号是否存在
public boolean existedByMchNo(String mchNo) {
return existedByField(MerchantInfo::getMchNo, mchNo);
}
}

View File

@@ -11,7 +11,6 @@ import cn.daxpay.open.platform.core.rest.param.PageParam;
import cn.daxpay.open.platform.core.rest.result.PageResult;
import cn.daxpay.open.platform.iam.service.client.ClientCodeService;
import cn.daxpay.open.platform.core.exception.config.ConfigErrorException;
import cn.daxpay.open.platform.core.exception.config.ConfigNotEnableException;
import cn.daxpay.open.platform.core.exception.config.ConfigNotExistException;
import cn.daxpay.open.payment.common.context.PaymentContext;
import cn.daxpay.open.payment.merchant.convert.appinfo.MchAppInfoConvert;
@@ -19,7 +18,6 @@ import cn.daxpay.open.payment.merchant.dao.appinfo.MchAppInfoManager;
import cn.daxpay.open.payment.merchant.dao.info.MerchantInfoManager;
import cn.daxpay.open.payment.merchant.entity.appinfo.MchAppInfo;
import cn.daxpay.open.payment.merchant.entity.info.MerchantInfo;
import cn.daxpay.open.platform.core.enums.merchant.MerchantStatusEnum;
import cn.daxpay.open.payment.merchant.param.appinfo.MchAppInfoParam;
import cn.daxpay.open.payment.merchant.param.appinfo.MchAppInfoQuery;
import cn.daxpay.open.payment.merchant.result.appinfo.MchAppInfoResult;
@@ -149,31 +147,6 @@ public class MchAppInfoService {
return mchApp.toResult();
}
/// 启用下拉列表, 需要应用和商户都是启用状态
public List<LabelValue> dropdownByEnable(String mchNo) {
if (clientCodeService.getClientCode().equals(ClientEnum.MERCHANT.getCode())) {
mchNo = paymentContext.getMchNo();
}
// 判断商户状态
MerchantInfo merchantInfo = merchantInfoManager.findByMchNo(mchNo)
// 商户: 商户不存在
.orElseThrow(() -> new DataNotExistException("error.payment.merchant.mchNotExist"));
if (!Objects.equals(merchantInfo.getStatus(), MerchantStatusEnum.ENABLE.getCode())) {
throw new ConfigNotEnableException(CommonCode.FAIL_CODE, "pay.error.assist.mchNotEnabled");
}
// 查询启用的应用
return mchAppInfoManager.findAllByMchNoAndEnable(mchNo).stream()
.map(o -> new LabelValue(o.getAppName(), o.getAppId()))
.collect(Collectors.toList());
}
/// 查询商户默认应用号
public String findDefaultAppId(String mchNo) {
return mchAppInfoManager.findDefaultByMchNo(mchNo)
.map(MchAppInfo::getAppId)
.orElseThrow(() -> new DataNotExistException("error.payment.merchant.defaultAppNotConfigured"));
}
/// 下拉列表, 不判断应用和商户的状态
public List<LabelValue> dropdown(String mchNo) {
if (clientCodeService.getClientCode().equals(ClientEnum.MERCHANT.getCode())) {

View File

@@ -16,20 +16,6 @@ import java.util.List;
@UtilityClass
public class PaymentStrategyFactory {
/// 根据通道编码获取策略
/// @param channel 通道编码
/// @param clazz 策略类型
/// @return 策略类
/// @param <T> 需要为 PaymentStrategy 的子类
public <T extends PaymentStrategy> T create(String channel, Class<T> clazz) {
var beansOfType = SpringUtil.getBeansOfType(clazz);
return beansOfType.values().stream()
.filter(strategy -> strategy.getProduct().getChannel().equals(channel))
.findFirst()
// 不支持的能力: {0}
.orElseThrow(() -> new UnsupportedAbilityException("pay.error.unsupportedAbilityWithDetail", channel));
}
/// 根据产品编码获取策略
/// @param product 产品编码
/// @param clazz 策略类型
@@ -44,12 +30,6 @@ public class PaymentStrategyFactory {
.orElseThrow(() -> new UnsupportedAbilityException("pay.error.unsupportedAbilityWithDetail", product));
}
/// 判断传入通道的策略是否存在
public <T extends PaymentStrategy> boolean existsByChannel(String channel, Class<T> clazz) {
var beansOfType = SpringUtil.getBeansOfType(clazz);
return beansOfType.values().stream().anyMatch(strategy -> strategy.getProduct().getChannel().equals(channel));
}
/// 判断传入产品的策略是否存在
public <T extends PaymentStrategy> boolean existsByProduct(String product, Class<T> clazz) {
var beansOfType = SpringUtil.getBeansOfType(clazz);

View File

@@ -27,13 +27,6 @@ public class PayRefundOrderManager extends BaseManager<PayRefundOrderMapper, Pay
return findByField(PayRefundOrder::getOutRefundNo, outRefundNo);
}
/// 根据原支付订单号查询退款订单数量(用于判断是否已退款)
public long countByOrderNo(String orderNo) {
return lambdaQuery()
.eq(PayRefundOrder::getOrderNo, orderNo)
.count();
}
/// 分页查询(管理端), 默认按创建时间倒序
public Page<PayRefundOrder> page(PageParam pageParam, PayRefundOrderQuery query) {
Page<PayRefundOrder> mpPage = MpUtil.getMpPage(pageParam);

View File

@@ -29,14 +29,6 @@ public class GenerateAuthUrlParam extends MerchantPaymentCommonParam {
@Schema(description = "认证类型")
private String authType = ChannelAuthTypeEnum.WECHAT.getCode();
/// 自定义授权认证路径(暂未实现)
///
/// 当前三通道统一使用固定路径(`/auth/wechat` / `/auth/alipay` / `/auth/douyin`) + state 透传 authToken,
/// 本字段传入会被忽略。保留字段仅为兼容旧调用方, 后续若支持自定义路径再启用。
@Deprecated
@Schema(description = "授权认证路径(暂未实现, 忽略)")
private String authPath;
/// 支付产品编码, 决定走哪个通道产品的认证策略
/// 可选: 缺失时由 [cn.daxpay.open.payment.auth.ChannelAuthService] 从通道商户号(channelMchNo)反查
/// @see cn.daxpay.open.platform.core.enums.pay.channel.ProductEnum

View File

@@ -61,12 +61,4 @@ public class MerchantInfoService {
.collect(Collectors.toList());
}
/// 商户下拉框
public List<LabelValue> dropdownByEnable() {
List<MerchantInfo> list = merchantInfoManager.findAllByEnable();
return list.stream()
.map(mch -> new LabelValue(mch.getMchName(),mch.getMchNo()))
.collect(Collectors.toList());
}
}

View File

@@ -1,44 +0,0 @@
package cn.daxpay.open.platform.capability.wechat.auth.result;
import lombok.Data;
import lombok.experimental.Accessors;
/// # 微信用户信息结果
///
@Data
@Accessors(chain = true)
public class WechatUserInfoResult {
/// 用户OpenId
private String openId;
/// 用户昵称
private String nickname;
/// 用户头像
private String headImgUrl;
/// 性别0-未知1-男2-女
private Integer sex;
/// 国家
private String country;
/// 省份
private String province;
/// 城市
private String city;
/// 语言
private String language;
/// 是否关注公众号
private Boolean subscribe;
/// 关注时间
private Long subscribeTime;
/// UnionId
private String unionId;
}

View File

@@ -3,13 +3,10 @@ package cn.daxpay.open.platform.capability.wechat.auth.service;
import cn.daxpay.open.platform.core.exception.operation.OperationFailException;
import cn.daxpay.open.platform.capability.wechat.auth.result.WechatAuthResult;
import cn.daxpay.open.platform.capability.wechat.auth.result.WechatAuthUrlResult;
import cn.daxpay.open.platform.capability.wechat.auth.result.WechatUserInfoResult;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.bean.WxOAuth2UserInfo;
import me.chanjar.weixin.common.bean.oauth2.WxOAuth2AccessToken;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
@@ -36,20 +33,6 @@ public class WechatMpAuthService {
return new WechatAuthUrlResult().setAuthUrl(authUrl).setQueryCode(queryCode);
}
/// 生成用户信息授权链接
/// @param redirectUrl 回调地址(固定路径, 不含动态段)
/// @param appId 公众号AppId
/// @param appSecret 公众号AppSecret
/// @param state OAuth state 参数(透传会话标识 authToken)
/// @return 授权链接结果
public WechatAuthUrlResult generateUserInfoAuthUrl(String redirectUrl, String appId, String appSecret, String state) {
WxMpService wxMpService = this.getWxMpService(appId, appSecret);
String queryCode = RandomUtil.randomString(10);
// 使用SNSAPI_USERINFO获取用户信息
String authUrl = wxMpService.getOAuth2Service().buildAuthorizationUrl(redirectUrl, WxConsts.OAuth2Scope.SNSAPI_USERINFO, state);
return new WechatAuthUrlResult().setAuthUrl(authUrl).setQueryCode(queryCode);
}
/// 获取微信AccessToken和OpenId
/// @param authCode 授权码
/// @param appId 微信公众号AppId
@@ -68,43 +51,6 @@ public class WechatMpAuthService {
.setOpenId(accessToken.getOpenId());
}
/// 获取用户信息包含OpenId和用户详情
/// @param authCode 授权码
/// @param appId 公众号AppId
/// @param appSecret 公众号AppSecret
/// @return 用户信息结果
public WechatUserInfoResult getUserInfoByAuthCode(String authCode, String appId, String appSecret) {
WxMpService wxMpService = this.getWxMpService(appId, appSecret);
try {
// 获取AccessToken
WxOAuth2AccessToken accessToken = wxMpService.getOAuth2Service().getAccessToken(authCode);
// 获取用户信息
WxOAuth2UserInfo userInfo = wxMpService.getOAuth2Service().getUserInfo(accessToken, null);
// 转换结果
var result = new WechatUserInfoResult();
result.setOpenId(userInfo.getOpenid());
result.setNickname(userInfo.getNickname());
result.setHeadImgUrl(userInfo.getHeadImgUrl());
result.setSex(userInfo.getSex());
result.setCountry(userInfo.getCountry());
result.setProvince(userInfo.getProvince());
result.setCity(userInfo.getCity());
// 注意weixin-java-common 4.8.1.B版本的WxOAuth2UserInfo不再提供getLanguage()方法
result.setUnionId(userInfo.getUnionId());
log.info("获取用户信息成功openId: {}", userInfo.getOpenid());
return result;
} catch (WxErrorException e) {
log.error("获取用户信息失败,错误: {}", e.getMessage());
// 微信: 获取用户信息失败: {0}
throw new OperationFailException("error.channel.wechat.userInfoFetchFailed", e.getMessage());
}
}
/// 获取微信公众号API的Service
public WxMpService getWxMpService(String appId, String appSecret) {
WxMpService wxMpService = new WxMpServiceImpl();

View File

@@ -1,15 +1,8 @@
package cn.daxpay.open.platform.common.mybatisplus.util;
import cn.daxpay.open.platform.common.mybatisplus.function.ToResult;
import cn.daxpay.open.platform.core.annotation.BigField;
import cn.daxpay.open.platform.core.exception.BizException;
import cn.daxpay.open.platform.core.rest.param.PageParam;
import cn.daxpay.open.platform.core.rest.result.PageResult;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.plugins.IgnoreStrategy;
@@ -19,23 +12,16 @@ import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
import com.baomidou.mybatisplus.core.toolkit.support.ColumnCache;
import com.baomidou.mybatisplus.core.toolkit.support.LambdaMeta;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.experimental.UtilityClass;
import org.apache.ibatis.reflection.property.PropertyNamer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.sql.DataSource;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import cn.daxpay.open.platform.core.code.CommonCode;
/// # MP工具类
///
@@ -72,27 +58,6 @@ public class MpUtil {
return columnCache.getColumn();
}
/// 获取行名称
/// @param readMethod 对象字段对应的读取方法对象
/// @param clazz 实体类类型. 辅助进行判断, 传多个只有第一个生效,可以为空, 为空时使用读取方法对应的Class类
/// @return 字段名
@SafeVarargs
public <T> String getColumnName(Method readMethod, Class<T>... clazz) {
Class<?> beanClass;
if (ArrayUtil.isNotEmpty(clazz)) {
beanClass = clazz[0];
}
else {
beanClass = readMethod.getDeclaringClass();
}
Map<String, ColumnCache> columnMap = LambdaUtils.getColumnMap(beanClass);
Assert.notEmpty(columnMap, "错误:无法执行.因为无法获取到实体类的表对应缓存!");
String fieldName = PropertyNamer.methodToProperty(readMethod.getName());
ColumnCache columnCache = columnMap.get(LambdaUtils.formatKey(fieldName));
return columnCache.getColumn();
}
/// mp page转换为 PageResult 同时进行dto转换
public <T> PageResult<T> toPageResult(Page<? extends ToResult<T>> page) {
if (Objects.isNull(page)) {
@@ -111,38 +76,6 @@ public class MpUtil {
return list.stream().map(ToResult::toResult).toList();
}
/// 批量执行语句, 通常用于for循环方式的批量插入
public <T> void executeBatch(List<T> saveList, Consumer<List<T>> consumer, int batchSize) {
// 开始游标
int start = 0;
// 结束游标
int end = Math.min(batchSize, saveList.size());
while (start < end) {
List<T> list = ListUtil.sub(saveList, start, end);
start = end;
end = Math.min(end + batchSize, saveList.size());
consumer.accept(list);
}
}
/// 字段是否存在长文本注解
public static boolean excludeBigField(TableFieldInfo tableFieldInfo) {
BigField annotation = tableFieldInfo.getField().getAnnotation(BigField.class);
return Objects.isNull(annotation);
}
/// 获取的一条数据, 有多条取第一条
public <T> Optional<T> findOne(LambdaQueryChainWrapper<T> lambdaQuery) {
Page<T> mpPage = new Page<>(0, 1);
Page<T> page = lambdaQuery.page(mpPage);
// 关闭 count 查询
page.setSearchCount(false);
if (CollUtil.isNotEmpty(page.getRecords())) {
return Optional.of(page.getRecords().getFirst());
}
return Optional.empty();
}
/// 获取关联的 TableInfo
public TableInfo getTableInfo(String tableName) {
for (TableInfo tableInfo : TableInfoHelper.getTableInfos()) {
@@ -153,17 +86,6 @@ public class MpUtil {
return null;
}
/// 获取当前数据库类型, 别忘了关闭, 不然会连接池泄露
public String getDbType(){
try {
try (Connection connection = SpringUtil.getBean(DataSource.class).getConnection()) {
return connection.getMetaData().getDatabaseProductName();
}
} catch (SQLException e) {
throw new BizException(CommonCode.FAIL_CODE, "error.common.dbTypeNotFound");
}
}
/// 获取主键名称
public String getKeyProperty(Class<?> clazz) {
TableInfo tableInfo = TableInfoHelper.getTableInfo(clazz);
@@ -173,16 +95,6 @@ public class MpUtil {
return keyProperty;
}
/// 获取主键值
public Object getKeyPropertyValue(Object object) {
TableInfo tableInfo = TableInfoHelper.getTableInfo(object.getClass());
Assert.notNull(tableInfo, "错误:无法执行.因为找不到实体的 TableInfo 缓存!");
String keyProperty = tableInfo.getKeyProperty();
Assert.notEmpty(keyProperty, "错误:无法执行.因为无法从实体中找到主键的列!");
// 反射获取值
return tableInfo.getPropertyValue(object, keyProperty);
}
/// 进入忽略租户作用域(可重入,与 [#clearIgnoreTenant] 成对使用)
///
/// 基于线程内引用计数:仅 depth 从 0→1 时真正打开 MP 忽略策略;

View File

@@ -4,12 +4,7 @@ import java.lang.annotation.*;
/// # 大字段注解
///
/// 配合 {@linkplain cn.hutool.core.map.MapUtil} 中 excludeBigField 方法使用使用
///
/// 使用Mp条件构造器器分页查询时样例
/// ```java
/// wrapper.select(this.getEntityClass (), MpUtil::excludeBigField);
/// ```
/// 标记实体中体积较大的字段(如文本/二进制),供查询构造时选择性排除。
@Target({ ElementType.PARAMETER, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Documented

View File

@@ -1,43 +0,0 @@
package cn.daxpay.open.platform.core.util;
import lombok.experimental.UtilityClass;
import java.math.BigDecimal;
/// # BigDecimal相关的工具类
///
@UtilityClass
public class BigDecimalUtil {
/// 比较大小
/// @param first 数字1
/// @param last 数字2
/// @return first > last =1 / first == last = 0 / first < last = -1
public int compareTo(BigDecimal first, BigDecimal last) {
BigDecimal newFirst = first != null ? first : BigDecimal.ZERO;
BigDecimal newLast = last != null ? last : BigDecimal.ZERO;
return newFirst.compareTo(newLast);
}
/// 是否大于
public boolean isGreaterThan(BigDecimal first, BigDecimal last) {
return compareTo(first, last) > 0;
}
/// 是否大于或等于
public boolean isGreaterAndEqualThan(BigDecimal first, BigDecimal last) {
return compareTo(first, last) >= 0;
}
/// 是否小于
public boolean isLessThan(BigDecimal first, BigDecimal last) {
return compareTo(first, last) < 0;
}
/// 是否等于
public boolean isEqual(BigDecimal first, BigDecimal last) {
return compareTo(first, last) == 0;
}
}

View File

@@ -1,29 +0,0 @@
package cn.daxpay.open.platform.core.util;
import cn.hutool.core.codec.Base64Encoder;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
/// # 证书工具类
///
@UtilityClass
public class CertUtil {
/// 根据证书文本获取证书
/// @param certContent 证书字符串
@SneakyThrows
public String getCertByContent(String certContent) {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate cert = cf.generateCertificate(new ByteArrayInputStream(certContent.getBytes(StandardCharsets.UTF_8)));
PublicKey publicKey = cert.getPublicKey();
return Base64Encoder.encode(publicKey.getEncoded());
}
}

View File

@@ -4,8 +4,6 @@ import cn.hutool.core.util.ClassUtil;
import lombok.experimental.UtilityClass;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/// # 类工具类
@@ -29,32 +27,4 @@ public class ClassUtils {
}
return null;
}
/// 递归扫描父类的fields
/// @param clas 类
/// @param fields 属性
public Field[] recursionParents(Class<?> clas, Field[] fields) {
if (Objects.nonNull(clas.getSuperclass())) {
Class<?> clsSup = clas.getSuperclass();
List<Field> fieldList = new ArrayList<>(List.of(fields));
// 获取当前class的所有fields的name列表
List<String> fdNames = fieldList.stream().map(Field::getName).toList();
for (Field pfd : clsSup.getDeclaredFields()) {
// 避免重载属性
if (fdNames.contains(pfd.getName())) {
continue;
}
fieldList.add(pfd);
}
fields = new Field[fieldList.size()];
int i = 0;
for (Object field : fieldList.toArray()) {
fields[i] = (Field) field;
i++;
}
fields = recursionParents(clsSup, fields);
}
return fields;
}
}

View File

@@ -2,7 +2,7 @@ package cn.daxpay.open.platform.core.util;
import lombok.experimental.UtilityClass;
import java.time.*;
import java.time.OffsetDateTime;
/// # java8 时间工具类
///
@@ -11,11 +11,6 @@ import java.time.*;
@UtilityClass
public class DateTimeUtil {
/// 大于
public boolean gt(OffsetDateTime now, OffsetDateTime next) {
return now.toEpochSecond() > next.toEpochSecond();
}
/// 小于
public boolean lt(OffsetDateTime now, OffsetDateTime next) {
return now.toEpochSecond() < next.toEpochSecond();
@@ -31,49 +26,4 @@ public class DateTimeUtil {
return now.toEpochSecond() <= next.toEpochSecond();
}
/// 获取当前 UTC 时间
public OffsetDateTime nowUtc() {
return OffsetDateTime.now(ZoneOffset.UTC);
}
/// 将 long 类型的 timestamp 转为 OffsetDateTime (UTC)
public OffsetDateTime fromEpochMilli(long timestamp) {
return Instant.ofEpochMilli(timestamp).atOffset(ZoneOffset.UTC);
}
/// OffsetDateTime 转为 long 类型的 timestamp
public long toEpochMilli(OffsetDateTime dateTime) {
return dateTime.toInstant().toEpochMilli();
}
/// 将 localDate 转换成当天开始时间的 OffsetDateTime (UTC)
/// 用于日期范围查询等场景
public OffsetDateTime dateStartUtc(LocalDate localDate) {
return localDate.atStartOfDay(ZoneOffset.UTC).toOffsetDateTime();
}
/// 将 localDate 转换成当天结束时间的 OffsetDateTime (UTC)
public OffsetDateTime dateEndUtc(LocalDate localDate) {
return localDate.atTime(LocalTime.MAX).atZone(ZoneOffset.UTC).toOffsetDateTime();
}
/// 旧版兼容: 将 Asia/Shanghai 时区的 LocalDateTime 转为 UTC OffsetDateTime
public OffsetDateTime shanghaiToUtc(LocalDateTime shanghaiTime) {
if (shanghaiTime == null) return null;
return shanghaiTime.atOffset(ZoneOffset.ofHours(8)).withOffsetSameInstant(ZoneOffset.UTC);
}
/// 旧版兼容: 将 UTC OffsetDateTime 转为 Asia/Shanghai 时区的 LocalDateTime
public LocalDateTime utcToShanghai(OffsetDateTime utcTime) {
if (utcTime == null) return null;
return utcTime.withOffsetSameInstant(ZoneOffset.ofHours(8)).toLocalDateTime();
}
/// 格式化为 ISO 8601 标准时间字符串 (UTC)
public String formatIso(OffsetDateTime dateTime) {
if (dateTime == null) return null;
return dateTime.withOffsetSameInstant(ZoneOffset.UTC).toString();
}
}

View File

@@ -1,128 +0,0 @@
package cn.daxpay.open.platform.core.util;
import cn.daxpay.open.platform.core.enums.pay.channel.PayMethodEnum;
import cn.daxpay.open.platform.core.enums.pay.channel.PayProviderEnum;
import cn.daxpay.open.platform.core.model.PayProviderMethodEntry;
import lombok.experimental.UtilityClass;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import static cn.daxpay.open.platform.core.enums.pay.channel.PayMethodEnum.*;
/// # 支付渠道支付方式目录(平台 V1
///
/// 全平台固定的「支付渠道 × 支付方式」组合,供路由配置、下单校验、管理端展示复用。
/// 与 PayMethodEnum、PayProviderEnum 等枚举类区分,本类为静态目录定义,不放在 enums 包下。
@Deprecated
@UtilityClass
public class PayProviderMethodUtil {
/// 各支付渠道下固定、有序的支付方式列表
private static final Map<PayProviderEnum, List<PayMethodEnum>> PROVIDER_METHODS;
/// 扁平化目录项,顺序与渠道内列表一致
private static final List<PayProviderMethodEntry> ALL_ENTRIES;
static {
Map<PayProviderEnum, List<PayMethodEnum>> providerMethods = new EnumMap<>(PayProviderEnum.class);
// 聚合支付:扫码 / 付款码
providerMethods.put(PayProviderEnum.AGGREGATE_PAY, List.of(
AGGREGATE_PAY_QRCODE,
AGGREGATE_PAY_BARCODE
));
// 微信JSAPI / APP / H5 / Native / 小程序 / 付款码 / 小程序收银台
providerMethods.put(PayProviderEnum.WECHAT, List.of(
WECHAT_JSAPI,
WECHAT_APP,
WECHAT_H5,
WECHAT_QR,
WECHAT_MINI,
WECHAT_BARCODE,
WECHAT_CASHIER
));
// 支付宝:付款码 / 扫码 / APP / 手机网站 / 电脑网站 / JSAPI
providerMethods.put(PayProviderEnum.ALIPAY, List.of(
ALIPAY_BARCODE,
ALIPAY_QR,
ALIPAY_APP,
ALIPAY_H5,
ALIPAY_PC,
ALIPAY_JSAPI
));
// 银联:扫码 / 付款码 / H5 / JSAPI
providerMethods.put(PayProviderEnum.UNION_PAY, List.of(
UNION_QR,
UNION_PAY_BARCODE,
UNION_H5,
UNION_JSAPI
));
// Visa网关 / 刷卡
providerMethods.put(PayProviderEnum.VISA, List.of(
VISA_CARD_GATEWAY,
VISA_CARD_PRESENT
));
// 万事达:网关 / 刷卡
providerMethods.put(PayProviderEnum.MASTERCARD, List.of(
MASTERCARD_CARD_GATEWAY,
MASTERCARD_CARD_PRESENT
));
PROVIDER_METHODS = Collections.unmodifiableMap(copyProviderMethods(providerMethods));
List<PayProviderMethodEntry> entries = new ArrayList<>();
for (Map.Entry<PayProviderEnum, List<PayMethodEnum>> entry : PROVIDER_METHODS.entrySet()) {
for (PayMethodEnum method : entry.getValue()) {
entries.add(new PayProviderMethodEntry(entry.getKey(), method));
}
}
ALL_ENTRIES = List.copyOf(entries);
}
/// 深拷贝渠道-支付方式映射,用于保护静态常量的不可变性
private static Map<PayProviderEnum, List<PayMethodEnum>> copyProviderMethods(
Map<PayProviderEnum, List<PayMethodEnum>> source) {
Map<PayProviderEnum, List<PayMethodEnum>> copy = new EnumMap<>(PayProviderEnum.class);
source.forEach((provider, methods) -> copy.put(provider, List.copyOf(methods)));
return copy;
}
/// 指定支付渠道下的固定支付方式(有序)
public List<PayMethodEnum> methodsOf(PayProviderEnum provider) {
if (provider == null) {
return List.of();
}
return PROVIDER_METHODS.getOrDefault(provider, List.of());
}
/// 指定支付渠道编码下的固定支付方式(有序)
public List<PayMethodEnum> methodsOf(String providerCode) {
return methodsOf(PayProviderEnum.findByCode(providerCode));
}
/// 是否属于渠道支付方式目录中的 (provider, method) 组合
public boolean contains(PayProviderEnum provider, String methodCode) {
if (provider == null || methodCode == null || methodCode.isBlank()) {
return false;
}
return methodsOf(provider).stream().anyMatch(method -> Objects.equals(method.getCode(), methodCode));
}
/// 是否属于渠道支付方式目录中的 (provider, method) 组合
public boolean contains(String providerCode, String methodCode) {
return contains(PayProviderEnum.findByCode(providerCode), methodCode);
}
/// 渠道支付方式目录全量项(以本类静态定义为准,运行时启用态见 `PayProviderService`
public List<PayProviderMethodEntry> allEntries() {
return ALL_ENTRIES;
}
/// 是否必须显式传入支付渠道(仅 other 无法推导)
public boolean requiresProviderForMethod(String methodCode) {
return Objects.equals(PayMethodEnum.OTHER.getCode(), methodCode);
}
}

View File

@@ -28,7 +28,7 @@ public final class PermCodeUtil {
}
/// 解析操作码(未拼接 menuCode
public static String resolveActionCode(PermCode classPermCode, PermCode methodPermCode) {
private static String resolveActionCode(PermCode classPermCode, PermCode methodPermCode) {
if (Objects.nonNull(methodPermCode) && StrUtil.isNotBlank(methodPermCode.code())) {
return methodPermCode.code();
}
@@ -49,26 +49,4 @@ public final class PermCodeUtil {
return "";
}
/// 解析中文名称。
public static String resolveNameCn(PermCode classPermCode, PermCode methodPermCode) {
if (Objects.nonNull(methodPermCode) && StrUtil.isNotBlank(methodPermCode.nameCn())) {
return methodPermCode.nameCn();
}
if (Objects.nonNull(classPermCode) && StrUtil.isNotBlank(classPermCode.nameCn())) {
return classPermCode.nameCn();
}
return "";
}
/// 解析英文名称。
public static String resolveNameEn(PermCode classPermCode, PermCode methodPermCode) {
if (Objects.nonNull(methodPermCode) && StrUtil.isNotBlank(methodPermCode.nameEn())) {
return methodPermCode.nameEn();
}
if (Objects.nonNull(classPermCode) && StrUtil.isNotBlank(classPermCode.nameEn())) {
return classPermCode.nameEn();
}
return "";
}
}

View File

@@ -1,127 +0,0 @@
package cn.daxpay.open.platform.core.util;
import cn.daxpay.open.platform.core.exception.DangerSqlException;
import cn.hutool.core.util.StrUtil;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/// # sql注入处理工具类 参考自 Jeecg Boot
///
@Slf4j
@UtilityClass
public class SqlInjectionUtil {
private final String XSS_STR = "and |extractvalue|updatexml|geohash|gtid_subset|gtid_subtract|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |;|or |+|user()";
/// 正则 user() 匹配更严谨
private final String REGULAR_EXPRE_USER = "user[\\s]*\\([\\s]*\\)";
/// 正则 show tables
private final String SHOW_TABLES = "show\\s+tables";
/// sql注释的正则
private final Pattern SQL_ANNOTATION = Pattern.compile("/\\*[\\s\\S]*\\*/");
/// sql注入过滤处理遇到注入关键字抛异常
public void filterContent(String value) {
filterContent(value, null);
}
/// sql注入过滤处理遇到注入关键字抛异常
public void filterContent(String value, String customXssString) {
if (StrUtil.isEmpty(value)) {
return;
}
// 校验sql注释 不允许有sql注释
checkSqlAnnotation(value);
// 统一转为小写
value = value.toLowerCase();
// SQL注入检测存在绕过风险
String[] xssArr = XSS_STR.split("\\|");
for (String s : xssArr) {
if (value.contains(s)) {
log.error("请注意存在SQL注入关键词---> {}", s);
log.error("请注意值可能存在SQL注入风险!---> {}", value);
// 通用: 检测到SQL注入关键词
throw new DangerSqlException("error.common.dangerSqlWithValue", value);
}
}
// 除了XSS_STR这些提前设置好的还需要额外的校验比如 单引号
if (customXssString != null) {
String[] xssArr2 = customXssString.split("\\|");
for (String s : xssArr2) {
if (value.contains(s)) {
log.error("请注意存在SQL注入关键词---> {}", s);
log.error("请注意值可能存在SQL注入风险!---> {}", value);
// 通用: 检测到SQL注入关键词
throw new DangerSqlException("error.common.dangerSqlWithValue", value);
}
}
}
// 除了XSS_STR这些提前设置好的还需要额外的校验比如 单引号
if (Pattern.matches(SHOW_TABLES, value) || Pattern.matches(REGULAR_EXPRE_USER, value)) {
// 通用: 检测到SQL注入关键词
throw new DangerSqlException("error.common.dangerSqlWithValue", value);
}
}
/// sql注入过滤处理遇到注入关键字抛异常
public void filterContent(String[] values) {
filterContent(values, null);
}
/// sql注入过滤处理遇到注入关键字抛异常
public void filterContent(String[] values, String customXssString) {
String[] xssArr = XSS_STR.split("\\|");
for (String value : values) {
if (StrUtil.isEmpty(value)) {
return;
}
// 校验sql注释 不允许有sql注释
checkSqlAnnotation(value);
// 统一转为小写
value = value.toLowerCase();
for (String s : xssArr) {
if (value.contains(s)) {
log.error("请注意存在SQL注入关键词---> {}", s);
log.error("请注意值可能存在SQL注入风险!---> {}", value);
// 通用: 检测到SQL注入关键词
throw new DangerSqlException("error.common.dangerSqlWithValue", value);
}
}
// 除了XSS_STR这些提前设置好的还需要额外的校验比如 单引号
if (customXssString != null) {
String[] xssArr2 = customXssString.split("\\|");
for (String s : xssArr2) {
if (value.contains(s)) {
log.error("请注意存在SQL注入关键词---> {}", s);
log.error("请注意值可能存在SQL注入风险!---> {}", value);
// 通用: 检测到SQL注入关键词
throw new DangerSqlException("error.common.dangerSqlWithValue", value);
}
}
}
// 除了XSS_STR这些提前设置好的还需要额外的校验比如 单引号
if (Pattern.matches(SHOW_TABLES, value) || Pattern.matches(REGULAR_EXPRE_USER, value)) {
// 通用: 检测到SQL注入关键词
throw new DangerSqlException("error.common.dangerSqlWithValue", value);
}
}
}
/// 校验是否有sql注释
public void checkSqlAnnotation(String str) {
Matcher matcher = SQL_ANNOTATION.matcher(str);
if (matcher.find()) {
String error = "请注意值可能存在SQL注入风险---> \\*.*\\";
log.error(error);
// 请注意值可能存在SQL注入风险
throw new DangerSqlException("error.common.dangerSqlWithValue", "\\*.*\\");
}
}
}

View File

@@ -6,7 +6,7 @@ import lombok.Setter;
/// # 各类型订单号生成工具类
///
/// 格式: {环境前缀}{3字母类型}{雪花ID}, 例如 PRODPAY1872635419283456
/// 类型使用3字母缩写: PAY(支付) ALL(分账) REP(修复)
/// 类型使用3字母缩写: PAY(支付) REF(退款)
@Setter
public final class TradeNoGenerateUtil {
@@ -23,11 +23,6 @@ public final class TradeNoGenerateUtil {
return prefix() + "REF" + IdUtil.getSnowflakeNextId();
}
/// 生成修复单号
public static String repair() {
return prefix() + "REP" + IdUtil.getSnowflakeNextId();
}
/// 拼接环境前缀, 不添加分隔符, 部分通道不允许特殊符号
private static String prefix() {
return env == null ? "" : env;

View File

@@ -63,32 +63,6 @@ public class TreeBuildUtil {
}
return children;
}
/// 展开树为list列表
///
/// @param list 要进行展开的列表
/// @param getChildren 获取子节点列表的方法引用
/// @return 展开后的结果列表
public <T> List<T> unfold(List<T> list, Function<T, List<T>> getChildren){
return unfold(list,getChildren, new ArrayList<>());
}
/// 展开树为list列表
///
/// @param list 要进行展开的列表
/// @param getChildren 获取子节点列表的方法引用
/// @param result 用于存储展开后的列表对象
/// @return 展开后的结果列表
private <T> List<T> unfold(List<T> list, Function<T, List<T>> getChildren, List<T> result){
if (CollectionUtil.isEmpty(list)) {
return new ArrayList<>(0);
}
for (T region : list) {
unfold(getChildren.apply(region), getChildren, result);
result.add(region);
}
return result;
}
}

View File

@@ -30,15 +30,4 @@ public class RoleMenuManager extends BaseManager<RoleMenuMapper, RoleMenu> {
public List<RoleMenu> findAllByRole(Long roleId) {
return findAllByField(RoleMenu::getRoleId, roleId);
}
/// 根据角色id、菜单ID进行删除
public void deleteByMenuIds(Long roleId, List<Long> menuIds) {
if (menuIds.isEmpty()) {
return;
}
lambdaUpdate()
.eq(RoleMenu::getRoleId, roleId)
.in(RoleMenu::getMenuId, menuIds)
.remove();
}
}

View File

@@ -80,11 +80,6 @@ public class DictItemService {
.orElseThrow(() -> new DataNotExistException("error.system.dict.itemNotExist"));
}
/// 根据字典编码和字典项编码查询启用的菜单项
public Optional<DictItem> findEnableByCode(String dictCode, String code) {
return dictItemManager.findByCodeAndEnable(dictCode, code, true);
}
/// 查询指定目录下的所有内容
public List<DictItemResult> findByDictionaryId(Long dictionaryId) {
return dictItemManager.findByDictId(dictionaryId)
@@ -94,20 +89,8 @@ public class DictItemService {
.toList();
}
/// 查询指定字典编码下的所有启用的字典项
/// @return code -> nameCn 的映射
@Cacheable(value = "system:dict", key = "#dictCode")
public Map<String, String> findEnableByDictCode(String dictCode) {
return dictItemManager.findByDictCodeAndEnable(dictCode, true)
.stream()
.collect(HashMap::new,
(map, item) -> map.put(item.getCode(), item.getNameCn()),
HashMap::putAll);
}
/// 查询指定字典编码下所有启用的字典项完整列表(含多语言字段)
/// 供 DictTranslatorImpl 使用,缓存键与 findEnableByDictCode 区分以避免类型冲突
/// 供 DictTranslatorImpl 使用
/// @return 字典项完整实体列表
@Cacheable(value = "system:dict", key = "#dictCode + ':list'")
public List<DictItem> findAllEnableByDictCode(String dictCode) {

View File

@@ -92,40 +92,6 @@ public class MobileAppService {
.map(this::toMaskedResult);
}
/// 运行时读取实体(不脱敏, appConfig 为解密后 JSON 明文)
public MobileApp getEntity(String appType, String platform) {
return manager.findByAppTypeAndPlatform(appType, platform)
.orElseThrow(() -> new DataNotExistException("error.mobile_app.notExist"));
}
/// 运行时读取已启用的配置实体
public MobileApp getEnabledEntity(String appType, String platform) {
MobileApp entity = getEntity(appType, platform);
if (!Boolean.TRUE.equals(entity.getEnabled())) {
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR,
"error.mobile_app.notEnabled", appType, platform);
}
return entity;
}
/// 运行时: 解析微信小程序配置(明文密钥)
public WxMiniAppConfig getWxMiniConfig(String appType) {
return parseConfig(getEnabledEntity(appType, MobilePlatformEnum.WX_MINI.getCode()).getAppConfig(),
WxMiniAppConfig.class);
}
/// 运行时: 解析支付宝小程序配置(明文密钥)
public AlipayMiniAppConfig getAlipayMiniConfig(String appType) {
return parseConfig(getEnabledEntity(appType, MobilePlatformEnum.ALIPAY_MINI.getCode()).getAppConfig(),
AlipayMiniAppConfig.class);
}
/// 运行时: 解析抖音小程序配置(明文密钥)
public DyMiniAppConfig getDyMiniConfig(String appType) {
return parseConfig(getEnabledEntity(appType, MobilePlatformEnum.DY_MINI.getCode()).getAppConfig(),
DyMiniAppConfig.class);
}
/// 保存(按端类型+平台组合 upsert)
@Transactional(rollbackFor = Exception.class)
public MobileAppResult save(MobileAppParam param) {