mirror of
https://gitee.com/dromara/dax-pay
synced 2026-08-12 23:45:39 +08:00
feat(masterdata): 迁移主数据模块到 payment.masterdata
- 新建 masterdata/constants/{channel/capability/method/product/provider} 子域
- 迁移 masterdata/config (ChannelConfig)
- 包结构: business-domain-first (masterdata.{subdomain}.{layer})
- 继承 MpBaseEntity (平台层)
- 删除 old/pay/service/route/PayRouteFacade.java (路由代码在 merchant 模块独立存在)
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
package org.dromara.daxpay.payment.masterdata.config.convert;
|
||||
|
||||
import org.dromara.daxpay.payment.masterdata.config.entity.ChannelConfig;
|
||||
import org.dromara.daxpay.payment.masterdata.config.result.ChannelConfigResult;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/// # 通道配置
|
||||
///
|
||||
@Mapper
|
||||
public interface ChannelConfigConvert {
|
||||
ChannelConfigConvert INSTANCE = Mappers.getMapper(ChannelConfigConvert.class);
|
||||
|
||||
ChannelConfigResult toResult(ChannelConfig in);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.dromara.daxpay.payment.masterdata.config.entity;
|
||||
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.function.ToResult;
|
||||
import org.dromara.daxpay.platform.core.enums.pay.channel.ChannelEnum;
|
||||
import org.dromara.daxpay.payment.common.entity.merchant.MchAppBaseEntity;
|
||||
import org.dromara.daxpay.payment.masterdata.config.convert.ChannelConfigConvert;
|
||||
import org.dromara.daxpay.payment.masterdata.config.result.ChannelConfigResult;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/// # 通道支付配置
|
||||
///
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@TableName("pay_channel_config" )
|
||||
public class ChannelConfig extends MchAppBaseEntity implements ToResult<ChannelConfigResult> {
|
||||
|
||||
/// 支付通道
|
||||
/// @see ChannelEnum
|
||||
private String channel;
|
||||
|
||||
/// 是否启用
|
||||
private boolean enable;
|
||||
|
||||
@Override
|
||||
public ChannelConfigResult toResult() {
|
||||
return ChannelConfigConvert.INSTANCE.toResult(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package org.dromara.daxpay.payment.masterdata.config.result;
|
||||
|
||||
import org.dromara.daxpay.platform.core.enums.pay.channel.ChannelEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/// # 通道配置
|
||||
///
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(title = "通道配置")
|
||||
public class ChannelConfigResult {
|
||||
|
||||
@Schema(description = "主键ID")
|
||||
private Long id;
|
||||
|
||||
/// 支付通道
|
||||
/// @see ChannelEnum
|
||||
@Schema(description = "支付通道")
|
||||
private String channel;
|
||||
|
||||
/// 通道名称
|
||||
@Schema(description = "通道名称")
|
||||
private String name;
|
||||
|
||||
/// 是否启用
|
||||
@Schema(description = "是否启用")
|
||||
private Boolean enable;
|
||||
|
||||
/// 商户号
|
||||
@Schema(description = "商户号")
|
||||
private String mchNo;
|
||||
|
||||
/// 应用号
|
||||
@Schema(description = "应用号")
|
||||
private String appId;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.capability.convert;
|
||||
|
||||
import org.dromara.daxpay.payment.masterdata.constants.capability.entity.PayCapability;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.capability.result.PayCapabilityResult;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/// # 支付能力转换
|
||||
@Mapper
|
||||
public interface PayCapabilityConvert {
|
||||
PayCapabilityConvert CONVERT = Mappers.getMapper(PayCapabilityConvert.class);
|
||||
|
||||
PayCapabilityResult toResult(PayCapability entity);
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.capability.dao;
|
||||
|
||||
import org.dromara.daxpay.payment.masterdata.constants.capability.entity.PayCapability;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.capability.param.PayCapabilityQuery;
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.impl.BaseManager;
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.query.generator.QueryGenerator;
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.util.MpUtil;
|
||||
import org.dromara.daxpay.platform.core.rest.param.PageParam;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/// # 支付能力
|
||||
@Repository
|
||||
@RequiredArgsConstructor
|
||||
public class PayCapabilityManager extends BaseManager<PayCapabilityMapper, PayCapability> {
|
||||
|
||||
/// 按编码查询
|
||||
public Optional<PayCapability> findByCode(String code) {
|
||||
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);
|
||||
QueryWrapper<PayCapability> wrapper = QueryGenerator.generator(query);
|
||||
if (StrUtil.isBlank(query.getSortField())) {
|
||||
wrapper.orderByAsc(MpUtil.getColumnName(PayCapability::getSortNo))
|
||||
.orderByAsc(MpUtil.getColumnName(PayCapability::getId));
|
||||
}
|
||||
return page(mpPage, wrapper);
|
||||
}
|
||||
|
||||
/// 全部未删除,有序
|
||||
public List<PayCapability> listAllOrdered() {
|
||||
return lambdaQuery()
|
||||
.orderByAsc(PayCapability::getSortNo)
|
||||
.orderByAsc(PayCapability::getId)
|
||||
.list();
|
||||
}
|
||||
|
||||
/// code -> 实体
|
||||
public Map<String, PayCapability> mapByCode() {
|
||||
return listAllOrdered().stream()
|
||||
.collect(Collectors.toMap(PayCapability::getCode, e -> e, (a, b) -> a));
|
||||
}
|
||||
|
||||
/// 按编码批量查询
|
||||
public List<PayCapability> listByCodes(Collection<String> codes) {
|
||||
if (CollUtil.isEmpty(codes)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return lambdaQuery().in(PayCapability::getCode, codes).list();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.capability.dao;
|
||||
|
||||
import org.dromara.daxpay.payment.masterdata.constants.capability.entity.PayCapability;
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/// # 支付能力 Mapper
|
||||
@Mapper
|
||||
public interface PayCapabilityMapper extends MPJBaseMapper<PayCapability> {
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.capability.dao;
|
||||
|
||||
import org.dromara.daxpay.payment.masterdata.constants.product.entity.PayProductCapability;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.impl.BaseManager;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/// # 支付产品能力关联
|
||||
@Repository
|
||||
@RequiredArgsConstructor
|
||||
public class PayProductCapabilityManager extends BaseManager<PayProductCapabilityMapper, PayProductCapability> {
|
||||
|
||||
/// 某能力下的全部关联
|
||||
public List<PayProductCapability> listByCapability(String capabilityCode) {
|
||||
return lambdaQuery()
|
||||
.eq(PayProductCapability::getCapabilityCode, capabilityCode)
|
||||
.orderByAsc(PayProductCapability::getSortNo)
|
||||
.orderByAsc(PayProductCapability::getId)
|
||||
.list();
|
||||
}
|
||||
|
||||
/// 某产品下的全部关联
|
||||
public List<PayProductCapability> listByProduct(String productCode) {
|
||||
return lambdaQuery()
|
||||
.eq(PayProductCapability::getProductCode, productCode)
|
||||
.orderByAsc(PayProductCapability::getSortNo)
|
||||
.orderByAsc(PayProductCapability::getId)
|
||||
.list();
|
||||
}
|
||||
|
||||
/// 多个产品下的全部关联
|
||||
public List<PayProductCapability> listByProductCodes(Collection<String> productCodes) {
|
||||
if (CollUtil.isEmpty(productCodes)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return lambdaQuery()
|
||||
.in(PayProductCapability::getProductCode, productCodes)
|
||||
.orderByAsc(PayProductCapability::getProductCode)
|
||||
.orderByAsc(PayProductCapability::getSortNo)
|
||||
.orderByAsc(PayProductCapability::getId)
|
||||
.list();
|
||||
}
|
||||
|
||||
/// 全部未删除关联
|
||||
public List<PayProductCapability> listAllOrdered() {
|
||||
return lambdaQuery()
|
||||
.orderByAsc(PayProductCapability::getProductCode)
|
||||
.orderByAsc(PayProductCapability::getSortNo)
|
||||
.orderByAsc(PayProductCapability::getId)
|
||||
.list();
|
||||
}
|
||||
|
||||
/// 产品是否关联该能力
|
||||
public boolean exists(String productCode, String capabilityCode) {
|
||||
return lambdaQuery()
|
||||
.eq(PayProductCapability::getProductCode, productCode)
|
||||
.eq(PayProductCapability::getCapabilityCode, capabilityCode)
|
||||
.exists();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.capability.dao;
|
||||
|
||||
import org.dromara.daxpay.payment.masterdata.constants.product.entity.PayProductCapability;
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/// # 支付产品能力关联 Mapper
|
||||
@Mapper
|
||||
public interface PayProductCapabilityMapper extends MPJBaseMapper<PayProductCapability> {
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.capability.entity;
|
||||
|
||||
import org.dromara.daxpay.payment.masterdata.constants.capability.convert.PayCapabilityConvert;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.capability.result.PayCapabilityResult;
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.base.MpBaseEntity;
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.function.ToResult;
|
||||
import org.dromara.daxpay.platform.core.enums.pay.channel.PayCapabilityEnum;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/// # 支付能力主数据
|
||||
///
|
||||
/// `code` 对齐 `PayCapabilityEnum`(独立发版字典,与支付方式无表字段关联);展示名走 enum i18n。
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@TableName("pay_capability")
|
||||
public class PayCapability extends MpBaseEntity implements ToResult<PayCapabilityResult> {
|
||||
|
||||
/// 支付能力编码
|
||||
/// @see PayCapabilityEnum
|
||||
private String code;
|
||||
|
||||
/// 全局排序
|
||||
private Integer sortNo;
|
||||
|
||||
/// 说明
|
||||
private String description;
|
||||
|
||||
/// 转换
|
||||
@Override
|
||||
public PayCapabilityResult toResult() {
|
||||
return PayCapabilityConvert.CONVERT.toResult(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.capability.param;
|
||||
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.query.entity.SortParam;
|
||||
import org.dromara.daxpay.platform.core.annotation.QueryParam;
|
||||
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 PayCapabilityQuery extends SortParam {
|
||||
|
||||
@QueryParam(type = QueryParam.CompareTypeEnum.LIKE)
|
||||
@Schema(description = "支付能力编码")
|
||||
private String code;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.capability.result;
|
||||
|
||||
import org.dromara.daxpay.platform.core.rest.dto.LabelValue;
|
||||
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;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/// # 支付能力
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(title = "支付能力")
|
||||
public class PayCapabilityResult extends BaseResult {
|
||||
|
||||
@Schema(description = "支付能力编码")
|
||||
private String code;
|
||||
|
||||
@Schema(description = "支付能力名称(i18n)")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "排序")
|
||||
private Integer sortNo;
|
||||
|
||||
@Schema(description = "说明")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "已关联支付产品")
|
||||
private List<LabelValue> products;
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.capability.service;
|
||||
|
||||
import org.dromara.daxpay.payment.masterdata.constants.capability.dao.PayCapabilityManager;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.capability.dao.PayProductCapabilityManager;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.capability.entity.PayCapability;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.capability.param.PayCapabilityQuery;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.capability.result.PayCapabilityResult;
|
||||
import org.dromara.daxpay.platform.common.i18n.util.I18nUtil;
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.util.MpUtil;
|
||||
import org.dromara.daxpay.platform.core.enums.pay.channel.PayCapabilityEnum;
|
||||
import org.dromara.daxpay.platform.core.enums.pay.channel.ProductEnum;
|
||||
import org.dromara.daxpay.platform.core.exception.DataNotExistException;
|
||||
import org.dromara.daxpay.platform.core.rest.dto.LabelValue;
|
||||
import org.dromara.daxpay.platform.core.rest.param.PageParam;
|
||||
import org.dromara.daxpay.platform.core.rest.result.PageResult;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/// # 支付能力主数据
|
||||
///
|
||||
/// 供管理端维护与展示支付能力字典。
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class PayCapabilityService {
|
||||
|
||||
private final PayCapabilityManager payCapabilityManager;
|
||||
private final PayProductCapabilityManager payProductCapabilityManager;
|
||||
|
||||
/// 分页查询
|
||||
public PageResult<PayCapabilityResult> page(PageParam pageParam, PayCapabilityQuery query, String nameKeyword) {
|
||||
PageResult<PayCapabilityResult> pageResult = MpUtil.toPageResult(payCapabilityManager.page(pageParam, query));
|
||||
List<PayCapabilityResult> records = pageResult.getRecords().stream()
|
||||
.map(this::fillDisplay)
|
||||
.filter(row -> matchNameKeyword(row, nameKeyword))
|
||||
.toList();
|
||||
pageResult.setRecords(records);
|
||||
return pageResult;
|
||||
}
|
||||
|
||||
/// 按能力编码查详情,含已关联的支付产品
|
||||
public PayCapabilityResult findByCode(String code) {
|
||||
PayCapability row = payCapabilityManager.findByCode(code)
|
||||
.orElseThrow(() -> new DataNotExistException("error.payment.capability.notExist"));
|
||||
return fillDisplayWithProducts(row.toResult());
|
||||
}
|
||||
|
||||
/// 补全支付能力 i18n 展示名
|
||||
private PayCapabilityResult fillDisplay(PayCapabilityResult result) {
|
||||
PayCapabilityEnum capabilityEnum = PayCapabilityEnum.findByCode(result.getCode());
|
||||
if (capabilityEnum != null) {
|
||||
result.setName(I18nUtil.getEnumName(capabilityEnum));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// 补全展示名与已关联支付产品
|
||||
private PayCapabilityResult fillDisplayWithProducts(PayCapabilityResult result) {
|
||||
fillDisplay(result);
|
||||
List<LabelValue> products = payProductCapabilityManager.listByCapability(result.getCode()).stream()
|
||||
.map(rel -> {
|
||||
ProductEnum productEnum = ProductEnum.findByCode(rel.getProductCode());
|
||||
String label = productEnum != null
|
||||
? I18nUtil.getEnumName(productEnum)
|
||||
: rel.getProductCode();
|
||||
return new LabelValue(label, rel.getProductCode());
|
||||
})
|
||||
.toList();
|
||||
result.setProducts(products);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// 分页后在内存中按展示名过滤
|
||||
private boolean matchNameKeyword(PayCapabilityResult row, String nameKeyword) {
|
||||
if (StrUtil.isBlank(nameKeyword)) {
|
||||
return true;
|
||||
}
|
||||
String name = row.getName();
|
||||
return name != null && name.contains(nameKeyword);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.channel.convert;
|
||||
|
||||
import org.dromara.daxpay.payment.masterdata.constants.channel.entity.PayChannel;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.channel.result.PayChannelResult;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/// # 支付通道转换
|
||||
///
|
||||
@Mapper
|
||||
public interface PayChannelConvert {
|
||||
PayChannelConvert CONVERT = Mappers.getMapper(PayChannelConvert.class);
|
||||
|
||||
PayChannelResult toResult(PayChannel entity);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.channel.dao;
|
||||
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.impl.BaseManager;
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.util.MpUtil;
|
||||
import org.dromara.daxpay.platform.core.rest.param.PageParam;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.channel.entity.PayChannel;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.channel.param.PayChannelQuery;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.query.generator.QueryGenerator;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/// # 支付通道
|
||||
///
|
||||
@Slf4j
|
||||
@Repository
|
||||
@RequiredArgsConstructor
|
||||
public class PayChannelManager extends BaseManager<PayChannelMapper, PayChannel> {
|
||||
|
||||
/// 根据通道编码查询
|
||||
public Optional<PayChannel> findByCode(String code) {
|
||||
return lambdaQuery()
|
||||
.eq(PayChannel::getCode, code)
|
||||
.oneOpt();
|
||||
}
|
||||
|
||||
/// 分页
|
||||
public Page<PayChannel> page(PageParam pageParam, PayChannelQuery query) {
|
||||
Page<PayChannel> mpPage = MpUtil.getMpPage(pageParam);
|
||||
QueryWrapper<PayChannel> generator = QueryGenerator.generator(query);
|
||||
return page(mpPage, generator);
|
||||
}
|
||||
|
||||
/// 查询所有(按排序号升序)
|
||||
public List<PayChannel> listAllOrdered() {
|
||||
return lambdaQuery()
|
||||
.orderByAsc(PayChannel::getSortNo)
|
||||
.list();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.channel.dao;
|
||||
|
||||
import org.dromara.daxpay.payment.masterdata.constants.channel.entity.PayChannel;
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/// # 支付通道
|
||||
///
|
||||
@Mapper
|
||||
public interface PayChannelMapper extends MPJBaseMapper<PayChannel> {
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.channel.entity;
|
||||
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.function.ToResult;
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.base.MpBaseEntity;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.channel.convert.PayChannelConvert;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.channel.result.PayChannelResult;
|
||||
import org.dromara.daxpay.platform.core.enums.pay.channel.ChannelEnum;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/// # 支付通道
|
||||
///
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@TableName("pay_channel")
|
||||
public class PayChannel extends MpBaseEntity implements ToResult<PayChannelResult> {
|
||||
|
||||
/// 通道编码
|
||||
/// @see ChannelEnum
|
||||
private String code;
|
||||
|
||||
/// 排序
|
||||
private Integer sortNo;
|
||||
|
||||
/// 通道介绍
|
||||
private String description;
|
||||
|
||||
/// 图标
|
||||
private String icon;
|
||||
|
||||
/// 转换
|
||||
@Override
|
||||
public PayChannelResult toResult() {
|
||||
return PayChannelConvert.CONVERT.toResult(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.channel.param;
|
||||
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.query.entity.SortParam;
|
||||
import org.dromara.daxpay.platform.core.annotation.QueryParam;
|
||||
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 PayChannelQuery extends SortParam {
|
||||
|
||||
@QueryParam(type = QueryParam.CompareTypeEnum.LIKE)
|
||||
@Schema(description = "通道编码")
|
||||
private String code;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.channel.result;
|
||||
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.base.MpBaseEntity;
|
||||
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 PayChannelResult extends MpBaseEntity {
|
||||
|
||||
@Schema(description = "通道编码")
|
||||
private String code;
|
||||
|
||||
@Schema(description = "通道名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "排序")
|
||||
private Integer sortNo;
|
||||
|
||||
@Schema(description = "通道介绍")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "图标")
|
||||
private String icon;
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.channel.service;
|
||||
|
||||
import org.dromara.daxpay.payment.masterdata.constants.channel.dao.PayChannelManager;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.channel.entity.PayChannel;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.channel.param.PayChannelQuery;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.channel.result.PayChannelResult;
|
||||
import org.dromara.daxpay.platform.common.i18n.util.I18nUtil;
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.util.MpUtil;
|
||||
import org.dromara.daxpay.platform.core.enums.pay.channel.ChannelEnum;
|
||||
import org.dromara.daxpay.platform.core.exception.DataNotExistException;
|
||||
import org.dromara.daxpay.platform.core.rest.dto.LabelValue;
|
||||
import org.dromara.daxpay.platform.core.rest.param.PageParam;
|
||||
import org.dromara.daxpay.platform.core.rest.result.PageResult;
|
||||
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.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/// # 支付通道主数据
|
||||
///
|
||||
/// 通道指接入层分类(如微信通道、支付宝通道),与「支付渠道」不同。数据来自 `ChannelEnum` 与 `pay_channel` 表。
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class PayChannelService {
|
||||
|
||||
private final PayChannelManager payChannelManager;
|
||||
|
||||
/// 分页查询支付通道
|
||||
public PageResult<PayChannelResult> page(PageParam pageParam, PayChannelQuery query, String nameKeyword) {
|
||||
PageResult<PayChannelResult> pageResult = MpUtil.toPageResult(payChannelManager.page(pageParam, query));
|
||||
pageResult.setRecords(pageResult.getRecords().stream()
|
||||
.map(this::fillChannelName)
|
||||
.filter(row -> matchNameKeyword(row, nameKeyword))
|
||||
.toList());
|
||||
return pageResult;
|
||||
}
|
||||
|
||||
/// 库表无记录时回退到通道枚举定义
|
||||
public PayChannelResult findByCode(String code) {
|
||||
return payChannelManager.findByCode(code)
|
||||
.map(PayChannel::toResult)
|
||||
.map(this::fillChannelName)
|
||||
.orElseGet(() -> resolveFromEnum(code));
|
||||
}
|
||||
|
||||
/// 库表无记录时从通道枚举构造结果
|
||||
private PayChannelResult resolveFromEnum(String code) {
|
||||
ChannelEnum enumVal = ChannelEnum.findByCode(code);
|
||||
if (enumVal == null) {
|
||||
throw new DataNotExistException("error.payment.channel.notExist");
|
||||
}
|
||||
return fillChannelName(toChannelResult(enumVal, Map.of()));
|
||||
}
|
||||
|
||||
/// 支付通道下拉选项
|
||||
public List<LabelValue> dropdown() {
|
||||
return payChannelManager.listAllOrdered().stream()
|
||||
.map(PayChannel::toResult)
|
||||
.map(this::fillChannelName)
|
||||
.map(e -> new LabelValue(e.getName(), e.getCode()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
/// 返回全部支付通道
|
||||
public List<PayChannelResult> listAll() {
|
||||
return buildChannels();
|
||||
}
|
||||
|
||||
/// 按通道编码取展示名称
|
||||
public String findNameByCode(String code) {
|
||||
return resolveChannelName(code);
|
||||
}
|
||||
|
||||
/// 枚举与库表合并为通道列表
|
||||
private List<PayChannelResult> buildChannels() {
|
||||
Map<String, PayChannel> dbMap = payChannelManager.listAllOrdered().stream()
|
||||
.collect(Collectors.toMap(PayChannel::getCode, c -> c, (a, b) -> a));
|
||||
|
||||
return Arrays.stream(ChannelEnum.values())
|
||||
.map(enumVal -> toChannelResult(enumVal, dbMap))
|
||||
.sorted(Comparator.comparing(PayChannelResult::getSortNo,
|
||||
Comparator.nullsLast(Comparator.naturalOrder())))
|
||||
.toList();
|
||||
}
|
||||
|
||||
/// 枚举与库表合并为单条通道结果
|
||||
private PayChannelResult toChannelResult(ChannelEnum enumVal, Map<String, PayChannel> dbMap) {
|
||||
PayChannelResult result = new PayChannelResult()
|
||||
.setCode(enumVal.getCode())
|
||||
.setName(resolveChannelName(enumVal.getCode()));
|
||||
|
||||
PayChannel dbRow = dbMap.get(enumVal.getCode());
|
||||
if (dbRow != null) {
|
||||
result.setId(dbRow.getId());
|
||||
result.setSortNo(dbRow.getSortNo());
|
||||
result.setDescription(dbRow.getDescription());
|
||||
result.setIcon(dbRow.getIcon());
|
||||
} else {
|
||||
result.setId(null);
|
||||
result.setSortNo(0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// 补全通道 i18n 展示名
|
||||
private PayChannelResult fillChannelName(PayChannelResult result) {
|
||||
if (result == null || result.getCode() == null) {
|
||||
return result;
|
||||
}
|
||||
return result.setName(resolveChannelName(result.getCode()));
|
||||
}
|
||||
|
||||
/// 按编码取通道展示名
|
||||
private String resolveChannelName(String code) {
|
||||
ChannelEnum channelEnum = ChannelEnum.findByCode(code);
|
||||
return channelEnum != null ? I18nUtil.getEnumName(channelEnum) : code;
|
||||
}
|
||||
|
||||
/// 分页后在内存中按展示名过滤
|
||||
private boolean matchNameKeyword(PayChannelResult row, String nameKeyword) {
|
||||
if (StrUtil.isBlank(nameKeyword)) {
|
||||
return true;
|
||||
}
|
||||
String name = row.getName();
|
||||
return name != null && name.contains(nameKeyword);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.method.dao;
|
||||
|
||||
import org.dromara.daxpay.payment.masterdata.constants.method.entity.PayMethod;
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.impl.BaseManager;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/// # 支付方式
|
||||
@Repository
|
||||
@RequiredArgsConstructor
|
||||
public class PayMethodManager extends BaseManager<PayMethodMapper, PayMethod> {
|
||||
|
||||
/// 按编码查询
|
||||
public Optional<PayMethod> findByCode(String code) {
|
||||
return lambdaQuery().eq(PayMethod::getCode, code).oneOpt();
|
||||
}
|
||||
|
||||
/// 全部未删除,按 sortNo、id 排序
|
||||
public List<PayMethod> listAllOrdered() {
|
||||
return lambdaQuery()
|
||||
.orderByAsc(PayMethod::getSortNo)
|
||||
.orderByAsc(PayMethod::getId)
|
||||
.list();
|
||||
}
|
||||
|
||||
/// code -> 实体
|
||||
public Map<String, PayMethod> mapByCode() {
|
||||
return listAllOrdered().stream()
|
||||
.collect(Collectors.toMap(PayMethod::getCode, e -> e, (a, b) -> a));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.method.dao;
|
||||
|
||||
import org.dromara.daxpay.payment.masterdata.constants.method.entity.PayMethod;
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/// # 支付方式
|
||||
@Mapper
|
||||
public interface PayMethodMapper extends MPJBaseMapper<PayMethod> {
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.method.entity;
|
||||
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.base.MpBaseEntity;
|
||||
import org.dromara.daxpay.platform.core.enums.pay.channel.PayMethodEnum;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/// # 支付方式主数据
|
||||
///
|
||||
/// code 对齐 `PayMethodEnum`;展示名走 enum i18n。
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@TableName("pay_method")
|
||||
public class PayMethod extends MpBaseEntity {
|
||||
|
||||
/// 支付方式编码
|
||||
/// @see PayMethodEnum
|
||||
private String code;
|
||||
|
||||
/// 全局排序
|
||||
private Integer sortNo;
|
||||
|
||||
/// 说明
|
||||
private String description;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.product.convert;
|
||||
|
||||
import org.dromara.daxpay.payment.masterdata.constants.product.entity.PayProductConfig;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.product.result.PayProductConfigResult;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/// # 支付产品配置转换
|
||||
///
|
||||
@Mapper
|
||||
public interface PayProductConfigConvert {
|
||||
|
||||
PayProductConfigConvert CONVERT = Mappers.getMapper(PayProductConfigConvert.class);
|
||||
|
||||
PayProductConfigResult toResult(PayProductConfig entity);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.product.convert;
|
||||
|
||||
import org.dromara.daxpay.payment.masterdata.constants.product.entity.PayProduct;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.product.result.PayProductResult;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/// # 支付产品转换
|
||||
///
|
||||
@Mapper
|
||||
public interface PayProductConvert {
|
||||
PayProductConvert CONVERT = Mappers.getMapper(PayProductConvert.class);
|
||||
|
||||
PayProductResult toResult(PayProduct entity);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.product.dao;
|
||||
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.impl.BaseManager;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.product.entity.PayProductConfig;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/// # 支付产品配置
|
||||
///
|
||||
@Slf4j
|
||||
@Repository
|
||||
@RequiredArgsConstructor
|
||||
public class PayProductConfigManager extends BaseManager<PayProductConfigMapper, PayProductConfig> {
|
||||
|
||||
/// 根据产品编码查询
|
||||
public Optional<PayProductConfig> findByProduct(String product) {
|
||||
return lambdaQuery()
|
||||
.eq(PayProductConfig::getProduct, product)
|
||||
.oneOpt();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.product.dao;
|
||||
|
||||
import org.dromara.daxpay.payment.masterdata.constants.product.entity.PayProductConfig;
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/// # 支付产品配置
|
||||
///
|
||||
@Mapper
|
||||
public interface PayProductConfigMapper extends MPJBaseMapper<PayProductConfig> {
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.product.dao;
|
||||
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.impl.BaseManager;
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.util.MpUtil;
|
||||
import org.dromara.daxpay.platform.core.rest.param.PageParam;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.product.entity.PayProduct;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.product.param.PayProductQuery;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.query.generator.QueryGenerator;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/// # 支付产品
|
||||
///
|
||||
@Slf4j
|
||||
@Repository
|
||||
@RequiredArgsConstructor
|
||||
public class PayProductManager extends BaseManager<PayProductMapper, PayProduct> {
|
||||
|
||||
/// 根据产品编码查询
|
||||
public Optional<PayProduct> findByCode(String code) {
|
||||
return lambdaQuery()
|
||||
.eq(PayProduct::getCode, code)
|
||||
.oneOpt();
|
||||
}
|
||||
|
||||
/// 按通道编码查询
|
||||
public List<PayProduct> listByChannel(String channel) {
|
||||
return lambdaQuery()
|
||||
.eq(PayProduct::getChannel, channel)
|
||||
.orderByAsc(PayProduct::getSortNo)
|
||||
.orderByAsc(PayProduct::getId)
|
||||
.list();
|
||||
}
|
||||
|
||||
/// 分页
|
||||
public Page<PayProduct> page(PageParam pageParam, PayProductQuery query) {
|
||||
Page<PayProduct> mpPage = MpUtil.getMpPage(pageParam);
|
||||
QueryWrapper<PayProduct> generator = QueryGenerator.generator(query);
|
||||
generator.lambda().orderByAsc(PayProduct::getSortNo).orderByAsc(PayProduct::getId);
|
||||
return page(mpPage, generator);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.product.dao;
|
||||
|
||||
import org.dromara.daxpay.payment.masterdata.constants.product.entity.PayProduct;
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/// # 支付产品
|
||||
///
|
||||
@Mapper
|
||||
public interface PayProductMapper extends MPJBaseMapper<PayProduct> {
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.product.entity;
|
||||
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.function.ToResult;
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.base.MpBaseEntity;
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.handler.type.StringListTypeHandler;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.product.convert.PayProductConvert;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.product.result.PayProductResult;
|
||||
import org.dromara.daxpay.platform.core.enums.pay.channel.ProductEnum;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/// # 支付产品
|
||||
///
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@TableName(value = "pay_product", autoResultMap = true)
|
||||
public class PayProduct extends MpBaseEntity implements ToResult<PayProductResult> {
|
||||
|
||||
/// 产品编码
|
||||
/// @see ProductEnum
|
||||
private String code;
|
||||
|
||||
/// 关联通道编码
|
||||
private String channel;
|
||||
|
||||
/// 产品介绍
|
||||
private String description;
|
||||
|
||||
/// 图标
|
||||
private String icon;
|
||||
|
||||
/// 支持的结算周期列表
|
||||
@TableField(typeHandler = StringListTypeHandler.class)
|
||||
private List<String> settlePeriods;
|
||||
|
||||
/// 排序
|
||||
private Integer sortNo;
|
||||
|
||||
/// 是否支持沙箱环境
|
||||
private Boolean sandbox;
|
||||
|
||||
/// 转换
|
||||
@Override
|
||||
public PayProductResult toResult() {
|
||||
return PayProductConvert.CONVERT.toResult(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.product.entity;
|
||||
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.base.MpBaseEntity;
|
||||
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("pay_product_capability")
|
||||
public class PayProductCapability extends MpBaseEntity {
|
||||
|
||||
/// 支付产品编码
|
||||
private String productCode;
|
||||
|
||||
/// 支付能力编码
|
||||
private String capabilityCode;
|
||||
|
||||
/// 排序
|
||||
private Integer sortNo;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.product.entity;
|
||||
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.base.MpBaseEntity;
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.function.ToResult;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.product.convert.PayProductConfigConvert;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.product.result.PayProductConfigResult;
|
||||
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 = "pay_product_config", autoResultMap = true)
|
||||
public class PayProductConfig extends MpBaseEntity implements ToResult<PayProductConfigResult> {
|
||||
|
||||
/// 产品编码
|
||||
private String product;
|
||||
|
||||
/// 通道编码
|
||||
private String channel;
|
||||
|
||||
/// 生效环境: prod/sandbox
|
||||
private String activeEnv;
|
||||
|
||||
/// 是否已配置参数
|
||||
private boolean configured;
|
||||
|
||||
/// 备注
|
||||
private String remark;
|
||||
|
||||
/// 转换
|
||||
@Override
|
||||
public PayProductConfigResult toResult() {
|
||||
return PayProductConfigConvert.CONVERT.toResult(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.product.param;
|
||||
|
||||
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 PayProductConfigParam {
|
||||
|
||||
@NotBlank(message = "{validation.field.product.notBlank}")
|
||||
@Schema(description = "产品编码")
|
||||
private String product;
|
||||
|
||||
@NotBlank(message = "{validation.field.channel.notBlank}")
|
||||
@Schema(description = "通道编码")
|
||||
private String channel;
|
||||
|
||||
@Schema(description = "生效环境: prod/sandbox")
|
||||
private String activeEnv;
|
||||
|
||||
@Schema(description = "是否已配置参数")
|
||||
private boolean configured;
|
||||
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.product.param;
|
||||
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.query.entity.SortParam;
|
||||
import org.dromara.daxpay.platform.core.annotation.QueryParam;
|
||||
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 PayProductQuery extends SortParam {
|
||||
|
||||
@QueryParam(type = QueryParam.CompareTypeEnum.LIKE)
|
||||
@Schema(description = "产品编码")
|
||||
private String code;
|
||||
|
||||
@QueryParam(type = QueryParam.CompareTypeEnum.EQ)
|
||||
@Schema(description = "关联通道编码")
|
||||
private String channel;
|
||||
|
||||
@QueryParam(type = QueryParam.CompareTypeEnum.EQ)
|
||||
@Schema(description = "是否启用")
|
||||
private Boolean enable;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.product.result;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/// # 支付产品关联的支付能力项
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(title = "支付产品支付能力项")
|
||||
public class PayProductCapabilityResult {
|
||||
|
||||
@Schema(description = "支付能力编码")
|
||||
private String code;
|
||||
|
||||
@Schema(description = "支付能力名称(i18n)")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "关联表排序")
|
||||
private Integer sortNo;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.product.result;
|
||||
|
||||
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 PayProductConfigResult extends BaseResult {
|
||||
|
||||
@Schema(description = "产品编码")
|
||||
private String product;
|
||||
|
||||
@Schema(description = "产品名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "通道编码")
|
||||
private String channel;
|
||||
|
||||
@Schema(description = "通道名称")
|
||||
private String channelName;
|
||||
|
||||
@Schema(description = "是否支持沙箱环境")
|
||||
private boolean sandboxSupport;
|
||||
|
||||
@Schema(description = "生效环境: prod/sandbox")
|
||||
private String activeEnv;
|
||||
|
||||
@Schema(description = "是否已配置参数")
|
||||
private boolean configured;
|
||||
|
||||
/// 是否为服务商模式(true=服务商, false=直连)
|
||||
private boolean isv;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.product.result;
|
||||
|
||||
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;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/// # 支付产品
|
||||
///
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(title = "支付产品")
|
||||
public class PayProductResult extends BaseResult {
|
||||
|
||||
@Schema(description = "产品编码")
|
||||
private String code;
|
||||
|
||||
@Schema(description = "产品名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "关联通道编码")
|
||||
private String channel;
|
||||
|
||||
@Schema(description = "通道名称")
|
||||
private String channelName;
|
||||
|
||||
@Schema(description = "产品介绍")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "图标")
|
||||
private String icon;
|
||||
|
||||
@Schema(description = "支持的结算周期列表")
|
||||
private List<String> settlePeriods;
|
||||
|
||||
@Schema(description = "排序")
|
||||
private Integer sortNo;
|
||||
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "是否支持服务商")
|
||||
private boolean isv;
|
||||
|
||||
@Schema(description = "是否支持分账")
|
||||
private boolean allocatable;
|
||||
|
||||
@Schema(description = "是否支持终端报备")
|
||||
private boolean terminal;
|
||||
|
||||
@Schema(description = "是否支持沙箱环境")
|
||||
private boolean sandbox;
|
||||
|
||||
@Schema(description = "API调用模式")
|
||||
private String apiCallMode;
|
||||
|
||||
@Schema(description = "支付标识类型")
|
||||
private String payIdType;
|
||||
|
||||
@Schema(description = "已挂载的支付能力")
|
||||
private List<PayProductCapabilityResult> capabilities;
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.product.service;
|
||||
|
||||
import org.dromara.daxpay.payment.common.util.PaymentStrategyFactory;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.capability.dao.PayCapabilityManager;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.capability.dao.PayProductCapabilityManager;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.capability.entity.PayCapability;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.product.entity.PayProductCapability;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.product.result.PayProductCapabilityResult;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.product.result.PayProductResult;
|
||||
import org.dromara.daxpay.payment.old.pay.strategy.AbsProductStrategy;
|
||||
import org.dromara.daxpay.payment.old.pay.support.ProductStrategySupport;
|
||||
import org.dromara.daxpay.platform.common.i18n.util.I18nUtil;
|
||||
import org.dromara.daxpay.platform.core.enums.pay.channel.PayCapabilityEnum;
|
||||
import org.dromara.daxpay.platform.core.enums.pay.channel.PayMethodEnum;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/// # 支付产品与支付能力关联
|
||||
///
|
||||
/// 查询产品挂载了哪些支付能力,并判断某支付产品是否支持某种支付方式。
|
||||
/// 方式→能力以各产品策略 {@link AbsProductStrategy#methodCapabilityMapping()} 为准,再与 `pay_product_capability` 求交。
|
||||
/// 内调约定:`productCode` / `methodCode` 均为已解析的合法编码,不在本类重复判空。
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class PayProductCapabilityService {
|
||||
|
||||
private final PayProductCapabilityManager payProductCapabilityManager;
|
||||
private final PayCapabilityManager payCapabilityManager;
|
||||
|
||||
|
||||
/// 预加载「支付产品 → 能力编码」,供批量判断使用
|
||||
public Map<String, Set<String>> loadCapabilityCodesByProduct() {
|
||||
Map<String, PayCapability> capabilityMap = payCapabilityManager.mapByCode();
|
||||
Map<String, Set<String>> capabilityCodesByProduct = new HashMap<>();
|
||||
for (PayProductCapability rel : payProductCapabilityManager.listAllOrdered()) {
|
||||
PayCapability cap = capabilityMap.get(rel.getCapabilityCode());
|
||||
if (cap == null) {
|
||||
continue;
|
||||
}
|
||||
capabilityCodesByProduct
|
||||
.computeIfAbsent(rel.getProductCode(), key -> new HashSet<>())
|
||||
.add(rel.getCapabilityCode());
|
||||
}
|
||||
return capabilityCodesByProduct;
|
||||
}
|
||||
|
||||
/// 用预加载结果判断产品是否支持该支付方式(策略声明能力 ∩ 已挂载能力,不再查库)
|
||||
public boolean supportsMethod(Map<String, Set<String>> capabilityCodesByProduct, String productCode, String methodCode) {
|
||||
Set<String> mountedCodes = capabilityCodesByProduct.getOrDefault(productCode, Set.of());
|
||||
return strategyCapabilitiesForMethod(productCode, methodCode).stream()
|
||||
.map(PayCapabilityEnum::getCode)
|
||||
.anyMatch(mountedCodes::contains);
|
||||
}
|
||||
|
||||
/// 判断产品是否支持该支付方式(策略声明能力 ∩ 已挂载能力,按产品查库)
|
||||
public boolean productSupportsMethod(String productCode, String methodCode) {
|
||||
Set<String> mountedCodes = payProductCapabilityManager.listByProduct(productCode).stream()
|
||||
.map(PayProductCapability::getCapabilityCode)
|
||||
.filter(code -> payCapabilityManager.findByCode(code).isPresent())
|
||||
.collect(Collectors.toSet());
|
||||
return strategyCapabilitiesForMethod(productCode, methodCode).stream()
|
||||
.map(PayCapabilityEnum::getCode)
|
||||
.anyMatch(mountedCodes::contains);
|
||||
}
|
||||
|
||||
/// 填充单个产品的支付能力列表
|
||||
public void fillCapabilities(PayProductResult result) {
|
||||
if (StrUtil.isBlank(result.getCode())) {
|
||||
return;
|
||||
}
|
||||
fillCapabilitiesBatch(List.of(result));
|
||||
}
|
||||
|
||||
/// 批量填充产品的支付能力列表
|
||||
public void fillCapabilitiesBatch(List<PayProductResult> products) {
|
||||
if (CollUtil.isEmpty(products)) {
|
||||
return;
|
||||
}
|
||||
Set<String> productCodes = products.stream()
|
||||
.map(PayProductResult::getCode)
|
||||
.filter(StrUtil::isNotBlank)
|
||||
.collect(Collectors.toSet());
|
||||
if (productCodes.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<PayProductCapability> allRels = payProductCapabilityManager.listByProductCodes(productCodes);
|
||||
Map<String, List<PayProductCapability>> relsByProduct = allRels.stream()
|
||||
.collect(Collectors.groupingBy(PayProductCapability::getProductCode));
|
||||
Set<String> capabilityCodes = allRels.stream()
|
||||
.map(PayProductCapability::getCapabilityCode)
|
||||
.collect(Collectors.toSet());
|
||||
Map<String, PayCapability> capabilityMap = payCapabilityManager.listByCodes(capabilityCodes).stream()
|
||||
.collect(Collectors.toMap(PayCapability::getCode, e -> e, (a, b) -> a));
|
||||
|
||||
for (PayProductResult product : products) {
|
||||
if (StrUtil.isBlank(product.getCode())) {
|
||||
product.setCapabilities(List.of());
|
||||
continue;
|
||||
}
|
||||
List<PayProductCapability> rels = relsByProduct.getOrDefault(product.getCode(), List.of());
|
||||
List<PayProductCapabilityResult> items = new ArrayList<>();
|
||||
for (PayProductCapability rel : rels) {
|
||||
PayCapability cap = capabilityMap.get(rel.getCapabilityCode());
|
||||
if (cap == null) {
|
||||
continue;
|
||||
}
|
||||
items.add(toItem(rel, cap));
|
||||
}
|
||||
product.setCapabilities(items);
|
||||
}
|
||||
}
|
||||
|
||||
/// 关联行转为能力展示项
|
||||
private PayProductCapabilityResult toItem(PayProductCapability rel, PayCapability cap) {
|
||||
PayProductCapabilityResult item = new PayProductCapabilityResult()
|
||||
.setCode(cap.getCode())
|
||||
.setSortNo(rel.getSortNo());
|
||||
PayCapabilityEnum capabilityEnum = PayCapabilityEnum.findByCode(cap.getCode());
|
||||
if (capabilityEnum != null) {
|
||||
item.setName(I18nUtil.getEnumName(capabilityEnum));
|
||||
} else {
|
||||
item.setName(cap.getCode());
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
/// 产品策略在该支付方式下声明的支付能力列表
|
||||
private List<PayCapabilityEnum> strategyCapabilitiesForMethod(String productCode, String methodCode) {
|
||||
if (!PaymentStrategyFactory.existsByProduct(productCode, AbsProductStrategy.class)) {
|
||||
return List.of();
|
||||
}
|
||||
PayMethodEnum method = findMethodByCode(methodCode);
|
||||
if (method == null) {
|
||||
return List.of();
|
||||
}
|
||||
AbsProductStrategy strategy = PaymentStrategyFactory.createByProduct(productCode, AbsProductStrategy.class);
|
||||
return ProductStrategySupport.capabilitiesForMethod(strategy, method);
|
||||
}
|
||||
|
||||
/// 按编码解析支付方式枚举(未知编码返回 null,不抛异常)
|
||||
private static PayMethodEnum findMethodByCode(String code) {
|
||||
return Arrays.stream(PayMethodEnum.values())
|
||||
.filter(method -> Objects.equals(method.getCode(), code))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.product.service;
|
||||
|
||||
import org.dromara.daxpay.payment.common.util.PaymentStrategyFactory;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.product.dao.PayProductConfigManager;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.product.entity.PayProduct;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.product.entity.PayProductConfig;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.product.param.PayProductConfigParam;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.product.result.PayProductConfigResult;
|
||||
import org.dromara.daxpay.payment.old.pay.strategy.AbsProductStrategy;
|
||||
import org.dromara.daxpay.platform.common.i18n.util.I18nUtil;
|
||||
import org.dromara.daxpay.platform.core.enums.pay.channel.ChannelEnum;
|
||||
import org.dromara.daxpay.platform.core.enums.pay.channel.ProductEnum;
|
||||
import org.dromara.daxpay.platform.core.enums.pay.config.PayEnvEnum;
|
||||
import org.dromara.daxpay.platform.core.exception.DataNotExistException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/// # 支付产品配置服务
|
||||
///
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class PayProductConfigService {
|
||||
|
||||
private final PayProductConfigManager payProductConfigManager;
|
||||
|
||||
private final PayProductService payProductService;
|
||||
|
||||
/// 查询全部产品配置列表(卡片页使用)
|
||||
/// 融合 PayProduct + pay_product_config 表 + 策略信息
|
||||
public List<PayProductConfigResult> listAll() {
|
||||
Map<String, PayProductConfig> configMap = payProductConfigManager.lambdaQuery().list().stream()
|
||||
.collect(Collectors.toMap(PayProductConfig::getProduct, c -> c, (a, b) -> a));
|
||||
|
||||
return payProductService.listSortedProducts()
|
||||
.stream()
|
||||
.map(payProduct -> toConfigResult(payProduct, configMap))
|
||||
.toList();
|
||||
}
|
||||
|
||||
/// 切换产品的生效环境
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void switchEnv(String product, boolean sandbox) {
|
||||
PayProductConfig config = payProductConfigManager.findByProduct(product)
|
||||
.orElseGet(() -> createDefaultConfig(product));
|
||||
|
||||
if (sandbox) {
|
||||
config.setActiveEnv(PayEnvEnum.SANDBOX.getCode());
|
||||
} else {
|
||||
config.setActiveEnv(PayEnvEnum.PROD.getCode());
|
||||
}
|
||||
payProductConfigManager.saveOrUpdate(config);
|
||||
}
|
||||
|
||||
/// 保存或更新配置
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void saveOrUpdate(PayProductConfigParam param) {
|
||||
PayProductConfig config = payProductConfigManager.findByProduct(param.getProduct())
|
||||
.orElseGet(PayProductConfig::new);
|
||||
|
||||
config.setProduct(param.getProduct());
|
||||
config.setChannel(param.getChannel());
|
||||
config.setActiveEnv(param.getActiveEnv() != null ? param.getActiveEnv() : PayEnvEnum.PROD.getCode());
|
||||
config.setConfigured(param.isConfigured());
|
||||
config.setRemark(param.getRemark());
|
||||
payProductConfigManager.saveOrUpdate(config);
|
||||
}
|
||||
|
||||
/// 更新配置状态
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void markConfigured(String product, boolean configured) {
|
||||
PayProductConfig config = payProductConfigManager.findByProduct(product)
|
||||
.orElseGet(() -> createDefaultConfig(product));
|
||||
config.setConfigured(configured);
|
||||
payProductConfigManager.saveOrUpdate(config);
|
||||
}
|
||||
|
||||
/// PayProduct + 库表 + 策略合并为配置结果
|
||||
private PayProductConfigResult toConfigResult(PayProduct payProduct, Map<String, PayProductConfig> configMap) {
|
||||
ProductEnum productEnum = ProductEnum.findByCode(payProduct.getCode());
|
||||
|
||||
PayProductConfigResult result = new PayProductConfigResult()
|
||||
.setProduct(payProduct.getCode())
|
||||
.setName(productEnum != null ? I18nUtil.getEnumName(productEnum) : payProduct.getCode())
|
||||
.setChannel(payProduct.getChannel())
|
||||
.setChannelName(I18nUtil.getEnumName(ChannelEnum.findByCode(payProduct.getChannel())));
|
||||
|
||||
AbsProductStrategy strategy = resolveStrategy(payProduct.getCode());
|
||||
if (strategy != null) {
|
||||
result.setSandboxSupport(strategy.isSandbox());
|
||||
result.setIsv(strategy.isIsv());
|
||||
}
|
||||
|
||||
PayProductConfig config = configMap.get(payProduct.getCode());
|
||||
if (config != null) {
|
||||
result.setId(config.getId());
|
||||
result.setActiveEnv(config.getActiveEnv());
|
||||
result.setConfigured(config.isConfigured());
|
||||
} else {
|
||||
result.setActiveEnv(PayEnvEnum.PROD.getCode());
|
||||
result.setConfigured(false);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// 创建默认配置记录
|
||||
private PayProductConfig createDefaultConfig(String product) {
|
||||
ProductEnum productEnum = ProductEnum.findByCode(product);
|
||||
if (productEnum == null) {
|
||||
throw new DataNotExistException("error.payment.product.notExist");
|
||||
}
|
||||
|
||||
PayProductConfig config = new PayProductConfig();
|
||||
config.setProduct(product);
|
||||
config.setChannel(productEnum.getChannel());
|
||||
config.setActiveEnv(PayEnvEnum.PROD.getCode());
|
||||
config.setConfigured(false);
|
||||
payProductConfigManager.save(config);
|
||||
return config;
|
||||
}
|
||||
|
||||
private AbsProductStrategy resolveStrategy(String productCode) {
|
||||
if (!PaymentStrategyFactory.existsByProduct(productCode, AbsProductStrategy.class)) {
|
||||
return null;
|
||||
}
|
||||
return PaymentStrategyFactory.createByProduct(productCode, AbsProductStrategy.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.product.service;
|
||||
|
||||
import org.dromara.daxpay.payment.common.util.PaymentStrategyFactory;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.product.dao.PayProductManager;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.product.entity.PayProduct;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.product.param.PayProductQuery;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.product.result.PayProductResult;
|
||||
import org.dromara.daxpay.payment.old.pay.strategy.AbsProductStrategy;
|
||||
import org.dromara.daxpay.platform.common.i18n.util.I18nUtil;
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.util.MpUtil;
|
||||
import org.dromara.daxpay.platform.core.enums.pay.channel.ChannelEnum;
|
||||
import org.dromara.daxpay.platform.core.enums.pay.channel.ProductEnum;
|
||||
import org.dromara.daxpay.platform.core.exception.DataNotExistException;
|
||||
import org.dromara.daxpay.platform.core.rest.dto.LabelValue;
|
||||
import org.dromara.daxpay.platform.core.rest.param.PageParam;
|
||||
import org.dromara.daxpay.platform.core.rest.result.PageResult;
|
||||
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.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/// # 支付产品主数据
|
||||
///
|
||||
/// 名称、支付通道、是否进件/终端/沙箱等。数据来自产品枚举、产品策略与 `pay_product` 表。
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class PayProductService {
|
||||
|
||||
private final PayProductManager payProductManager;
|
||||
private final PayProductCapabilityService payProductCapabilityService;
|
||||
|
||||
/// 分页查询支付产品
|
||||
public PageResult<PayProductResult> page(PageParam pageParam, PayProductQuery query, String nameKeyword) {
|
||||
PageResult<PayProductResult> pageResult = MpUtil.toPageResult(payProductManager.page(pageParam, query));
|
||||
pageResult.setRecords(pageResult.getRecords().stream()
|
||||
.map(this::fillProductFeatures)
|
||||
.filter(row -> matchNameKeyword(row, nameKeyword))
|
||||
.toList());
|
||||
return pageResult;
|
||||
}
|
||||
|
||||
/// 根据产品编码查询
|
||||
public PayProductResult findByCode(String code) {
|
||||
PayProduct payProduct = payProductManager.findByCode(code)
|
||||
.orElseThrow(() -> new DataNotExistException("error.payment.product.notExist"));
|
||||
return enrich(payProduct.toResult());
|
||||
}
|
||||
|
||||
/// 查询全部支付产品,按 sortNo 升序排列
|
||||
public List<PayProduct> listSortedProducts() {
|
||||
return payProductManager.lambdaQuery()
|
||||
.orderByAsc(PayProduct::getSortNo)
|
||||
.list();
|
||||
}
|
||||
|
||||
/// 按支付通道编码查询所属支付产品
|
||||
public List<PayProductResult> listByChannel(String channelCode) {
|
||||
return payProductManager.listByChannel(channelCode).stream()
|
||||
.map(PayProduct::toResult)
|
||||
.map(this::fillProductFeatures)
|
||||
.toList();
|
||||
}
|
||||
|
||||
/// 返回全部支付产品,按 sortNo、id 排序
|
||||
/// 策略存在时补全特性字段,不存在时仅返回枚举与数据库基础信息
|
||||
public List<PayProductResult> listAll() {
|
||||
Map<String, PayProduct> dbMap = payProductManager.lambdaQuery().list().stream()
|
||||
.collect(Collectors.toMap(PayProduct::getCode, p -> p, (a, b) -> a));
|
||||
|
||||
return Arrays.stream(ProductEnum.values())
|
||||
.map(e -> toProductResult(e, dbMap))
|
||||
.sorted(Comparator.comparing(PayProductResult::getSortNo,
|
||||
Comparator.nullsLast(Comparator.naturalOrder()))
|
||||
.thenComparing(PayProductResult::getId, Comparator.nullsLast(Comparator.naturalOrder())))
|
||||
.toList();
|
||||
}
|
||||
|
||||
/// 支付产品下拉选项
|
||||
public List<LabelValue> dropdown() {
|
||||
return listAll().stream()
|
||||
.map(e -> new LabelValue(e.getName(), e.getCode()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
/// 枚举与库表合并为产品结果
|
||||
private PayProductResult toProductResult(ProductEnum product, Map<String, PayProduct> dbMap) {
|
||||
PayProductResult result = new PayProductResult()
|
||||
.setCode(product.getCode())
|
||||
.setChannel(product.getChannel())
|
||||
.setName(I18nUtil.getEnumName(product))
|
||||
.setChannelName(I18nUtil.getEnumName(ChannelEnum.findByCode(product.getChannel())));
|
||||
|
||||
PayProduct dbRow = dbMap.get(product.getCode());
|
||||
if (dbRow != null) {
|
||||
result.setId(dbRow.getId());
|
||||
result.setSortNo(dbRow.getSortNo());
|
||||
result.setDescription(dbRow.getDescription());
|
||||
result.setIcon(dbRow.getIcon());
|
||||
result.setSettlePeriods(dbRow.getSettlePeriods());
|
||||
if (dbRow.getSandbox() != null) {
|
||||
result.setSandbox(dbRow.getSandbox());
|
||||
}
|
||||
} else {
|
||||
result.setSortNo(0);
|
||||
}
|
||||
AbsProductStrategy strategy = resolveStrategy(product.getCode());
|
||||
if (strategy != null) {
|
||||
applyStrategyFields(strategy, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// 补全支付能力与展示字段
|
||||
private PayProductResult enrich(PayProductResult result) {
|
||||
payProductCapabilityService.fillCapabilities(result);
|
||||
return fillProductFeatures(result);
|
||||
}
|
||||
|
||||
/// 补全名称、通道名与策略特性
|
||||
private PayProductResult fillProductFeatures(PayProductResult result) {
|
||||
String code = result.getCode();
|
||||
ProductEnum productEnum = ProductEnum.findByCode(code);
|
||||
if (productEnum != null) {
|
||||
result.setName(I18nUtil.getEnumName(productEnum));
|
||||
}
|
||||
result.setChannelName(I18nUtil.getEnumName(ChannelEnum.findByCode(result.getChannel())));
|
||||
AbsProductStrategy strategy = resolveStrategy(code);
|
||||
if (strategy != null) {
|
||||
applyStrategyFields(strategy, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// 按产品编码获取策略,不存在则返回 null
|
||||
private AbsProductStrategy resolveStrategy(String productCode) {
|
||||
if (!PaymentStrategyFactory.existsByProduct(productCode, AbsProductStrategy.class)) {
|
||||
return null;
|
||||
}
|
||||
return PaymentStrategyFactory.createByProduct(productCode, AbsProductStrategy.class);
|
||||
}
|
||||
|
||||
/// 写入策略层产品特性字段
|
||||
private void applyStrategyFields(AbsProductStrategy strategy, PayProductResult target) {
|
||||
target.setIsv(strategy.isIsv())
|
||||
.setAllocatable(strategy.isAllocatable())
|
||||
.setTerminal(strategy.isTerminal())
|
||||
.setSandbox(strategy.isSandbox())
|
||||
.setApiCallMode(strategy.getApiCallMode().getCode())
|
||||
.setPayIdType(strategy.getPayIdType().getCode());
|
||||
}
|
||||
|
||||
/// 按产品名称关键字过滤(分页后在内存中匹配 i18n 展示名)
|
||||
private boolean matchNameKeyword(PayProductResult row, String nameKeyword) {
|
||||
if (StrUtil.isBlank(nameKeyword)) {
|
||||
return true;
|
||||
}
|
||||
String name = row.getName();
|
||||
return name != null && name.contains(nameKeyword);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.provider.dao;
|
||||
|
||||
import org.dromara.daxpay.payment.masterdata.constants.provider.entity.PayProvider;
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.impl.BaseManager;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/// # 支付渠道
|
||||
@Repository
|
||||
@RequiredArgsConstructor
|
||||
public class PayProviderManager extends BaseManager<PayProviderMapper, PayProvider> {
|
||||
|
||||
/// 按编码查询
|
||||
public Optional<PayProvider> findByCode(String code) {
|
||||
return lambdaQuery().eq(PayProvider::getCode, code).oneOpt();
|
||||
}
|
||||
|
||||
/// 全部未删除记录,按 sortNo、id 排序
|
||||
public List<PayProvider> listAllOrdered() {
|
||||
return lambdaQuery()
|
||||
.orderByAsc(PayProvider::getSortNo)
|
||||
.orderByAsc(PayProvider::getId)
|
||||
.list();
|
||||
}
|
||||
|
||||
/// code -> 实体
|
||||
public Map<String, PayProvider> mapByCode() {
|
||||
return listAllOrdered().stream()
|
||||
.collect(Collectors.toMap(PayProvider::getCode, e -> e, (a, b) -> a));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.provider.dao;
|
||||
|
||||
import org.dromara.daxpay.payment.masterdata.constants.provider.entity.PayProvider;
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/// # 支付渠道
|
||||
@Mapper
|
||||
public interface PayProviderMapper extends MPJBaseMapper<PayProvider> {
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.provider.dao;
|
||||
|
||||
import org.dromara.daxpay.payment.masterdata.constants.provider.entity.PayProviderMethod;
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.impl.BaseManager;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/// # 渠道支付方式目录项
|
||||
@Repository
|
||||
@RequiredArgsConstructor
|
||||
public class PayProviderMethodManager extends BaseManager<PayProviderMethodMapper, PayProviderMethod> {
|
||||
|
||||
/// 全部未删除记录
|
||||
public List<PayProviderMethod> listAllOrdered() {
|
||||
return lambdaQuery()
|
||||
.orderByAsc(PayProviderMethod::getProvider)
|
||||
.orderByAsc(PayProviderMethod::getSortNo)
|
||||
.orderByAsc(PayProviderMethod::getId)
|
||||
.list();
|
||||
}
|
||||
|
||||
/// (provider|method) -> 实体
|
||||
public Map<String, PayProviderMethod> mapByPairKey() {
|
||||
return listAllOrdered().stream()
|
||||
.collect(Collectors.toMap(this::pairKey, e -> e, (a, b) -> a));
|
||||
}
|
||||
|
||||
private String pairKey(PayProviderMethod row) {
|
||||
return pairKey(row.getProvider(), row.getMethod());
|
||||
}
|
||||
|
||||
public static String pairKey(String provider, String method) {
|
||||
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()
|
||||
.eq(PayProviderMethod::getProvider, providerCode)
|
||||
.orderByAsc(PayProviderMethod::getSortNo)
|
||||
.orderByAsc(PayProviderMethod::getId)
|
||||
.list();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.provider.dao;
|
||||
|
||||
import org.dromara.daxpay.payment.masterdata.constants.provider.entity.PayProviderMethod;
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/// # 渠道支付方式目录项
|
||||
@Mapper
|
||||
public interface PayProviderMethodMapper extends MPJBaseMapper<PayProviderMethod> {
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.provider.entity;
|
||||
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.base.MpBaseEntity;
|
||||
import org.dromara.daxpay.platform.core.enums.pay.channel.PayProviderEnum;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/// # 支付渠道(运营元数据)
|
||||
///
|
||||
/// code 对齐 `PayProviderEnum`;展示名走 enum i18n。
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@TableName("pay_provider")
|
||||
public class PayProvider extends MpBaseEntity {
|
||||
|
||||
/// 支付渠道编码
|
||||
/// @see PayProviderEnum
|
||||
private String code;
|
||||
|
||||
/// 图标(可选)
|
||||
private String icon;
|
||||
|
||||
/// 排序
|
||||
private Integer sortNo;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.provider.entity;
|
||||
|
||||
import org.dromara.daxpay.platform.common.mybatisplus.base.MpBaseEntity;
|
||||
import org.dromara.daxpay.platform.core.enums.pay.channel.PayMethodEnum;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/// # 渠道下支付方式目录项
|
||||
///
|
||||
/// pay_provider + method 关联 `pay_method.code` 主数据。
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@TableName("pay_provider_method")
|
||||
public class PayProviderMethod extends MpBaseEntity {
|
||||
|
||||
/// 支付渠道编码
|
||||
private String provider;
|
||||
|
||||
/// 支付方式编码
|
||||
/// @see PayMethodEnum
|
||||
private String method;
|
||||
|
||||
/// 渠道内排序
|
||||
private Integer sortNo;
|
||||
|
||||
/// 目录项说明
|
||||
private String description;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.provider.result;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/// # 按支付渠道分组(管理端)
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(title = "支付渠道分组")
|
||||
public class PayProviderGroupResult {
|
||||
|
||||
@Schema(description = "支付渠道编码")
|
||||
private String provider;
|
||||
|
||||
@Schema(description = "支付渠道展示名")
|
||||
private String providerLabel;
|
||||
|
||||
@Schema(description = "图标")
|
||||
private String icon;
|
||||
|
||||
@Schema(description = "排序")
|
||||
private Integer sortNo;
|
||||
|
||||
@Schema(description = "渠道下支付方式列表")
|
||||
private List<PayProviderMethodResult> methods;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.provider.result;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/// # 渠道支持的支付方式
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(title = "渠道支付方式")
|
||||
public class PayProviderMethodResult {
|
||||
|
||||
@Schema(description = "支付渠道编码")
|
||||
private String provider;
|
||||
|
||||
@Schema(description = "支付方式编码")
|
||||
private String method;
|
||||
|
||||
@Schema(description = "支付方式展示名")
|
||||
private String methodLabel;
|
||||
|
||||
@Schema(description = "渠道内排序")
|
||||
private Integer sortNo;
|
||||
|
||||
@Schema(description = "目录项说明")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "支持的支付产品")
|
||||
private List<PayProviderProductResult> supportedProducts;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.provider.result;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/// # 渠道支付方式支持的支付产品
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(title = "渠道支付方式支持的支付产品")
|
||||
public class PayProviderProductResult {
|
||||
|
||||
@Schema(description = "产品名称")
|
||||
private String label;
|
||||
|
||||
@Schema(description = "产品编码")
|
||||
private String value;
|
||||
|
||||
@Schema(description = "所属通道编码")
|
||||
private String channel;
|
||||
|
||||
@Schema(description = "通道名称")
|
||||
private String channelName;
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.provider.service;
|
||||
|
||||
import org.dromara.daxpay.payment.masterdata.constants.method.dao.PayMethodManager;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.provider.dao.PayProviderManager;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.provider.dao.PayProviderMethodManager;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.method.entity.PayMethod;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.provider.entity.PayProvider;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.provider.entity.PayProviderMethod;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.provider.result.PayProviderMethodResult;
|
||||
import org.dromara.daxpay.platform.common.i18n.util.I18nUtil;
|
||||
import org.dromara.daxpay.platform.core.enums.pay.channel.PayMethodEnum;
|
||||
import org.dromara.daxpay.platform.core.enums.pay.channel.PayProviderEnum;
|
||||
import org.dromara.daxpay.platform.core.model.PayProviderMethodEntry;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/// # 支付渠道与支付方式的配置与查询
|
||||
///
|
||||
/// 读取 `pay_provider`、`pay_method`、`pay_provider_method`,供下单校验和管理端「支付渠道」页面使用。
|
||||
/// 内调约定:`providerCode` / `methodCode` 均为已解析的合法编码,不在本类重复判空。
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class PayProviderMethodService {
|
||||
|
||||
private final PayMethodManager payMethodManager;
|
||||
private final PayProviderManager payProviderManager;
|
||||
private final PayProviderMethodManager payProviderMethodManager;
|
||||
|
||||
/// 预加载全部支付渠道、支付方式及二者关联
|
||||
public DirectoryLoadContext loadDirectoryContext() {
|
||||
Map<String, PayProvider> providerMap = payProviderManager.mapByCode();
|
||||
Map<String, PayMethod> methodMap = payMethodManager.mapByCode();
|
||||
Map<String, PayProviderMethod> relationMap = payProviderMethodManager.mapByPairKey();
|
||||
Map<String, List<PayProviderMethod>> realsByProvider = payProviderMethodManager.listAllOrdered().stream()
|
||||
.collect(Collectors.groupingBy(PayProviderMethod::getProvider));
|
||||
Map<String, List<MergedRelationRow>> relationsByProvider = new HashMap<>();
|
||||
for (Map.Entry<String, List<PayProviderMethod>> entry : realsByProvider.entrySet()) {
|
||||
relationsByProvider.put(entry.getKey(), mergeRelations(entry.getValue(), methodMap));
|
||||
}
|
||||
return new DirectoryLoadContext(providerMap, methodMap, relationMap, relationsByProvider);
|
||||
}
|
||||
|
||||
/// 从预加载数据中取目录中的「渠道 + 支付方式」
|
||||
public List<PayProviderMethodEntry> listDirectoryEntriesFromContext(DirectoryLoadContext context) {
|
||||
List<PayProviderMethodEntry> entries = new ArrayList<>();
|
||||
for (PayProviderEnum providerEnum : PayProviderEnum.values()) {
|
||||
for (MergedRelationRow row : context.relationsByProvider()
|
||||
.getOrDefault(providerEnum.getCode(), List.of())) {
|
||||
entries.add(new PayProviderMethodEntry(providerEnum, row.methodEnum()));
|
||||
}
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
/// 返回目录中全部「渠道 + 支付方式」
|
||||
public List<PayProviderMethodEntry> listDirectoryEntries() {
|
||||
DirectoryLoadContext context = loadDirectoryContext();
|
||||
return listDirectoryEntriesFromContext(context);
|
||||
}
|
||||
|
||||
/// 判断该支付渠道下是否存在该支付方式(目录有效组合)
|
||||
public boolean contains(String providerCode, String methodCode) {
|
||||
PayProviderEnum provider = PayProviderEnum.findByCode(providerCode);
|
||||
if (provider == null) {
|
||||
return false;
|
||||
}
|
||||
PayMethodEnum methodEnum = resolveMethodEnum(methodCode);
|
||||
if (methodEnum == null) {
|
||||
return false;
|
||||
}
|
||||
if (payMethodManager.findByCode(methodCode).isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
return listMergedRelationsForProvider(providerCode).stream()
|
||||
.anyMatch(row -> Objects.equals(row.methodEnum().getCode(), methodCode));
|
||||
}
|
||||
|
||||
/// 某支付渠道目录中的支付方式列表
|
||||
public List<PayMethodEnum> listMethodsForProvider(PayProviderEnum provider) {
|
||||
return listMergedRelationsForProvider(provider.getCode()).stream()
|
||||
.map(MergedRelationRow::methodEnum)
|
||||
.toList();
|
||||
}
|
||||
|
||||
/// 同 {@link #listMethodsForProvider(PayProviderEnum)}
|
||||
public List<PayMethodEnum> listMethodsForBrand(PayProviderEnum brand) {
|
||||
return listMethodsForProvider(brand);
|
||||
}
|
||||
|
||||
/// 平铺返回目录中的渠道与支付方式(不按渠道分组,不含支持的产品)
|
||||
public List<PayProviderMethodResult> listDirectoryFlat() {
|
||||
DirectoryLoadContext context = loadDirectoryContext();
|
||||
return listDirectoryEntriesFromContext(context).stream()
|
||||
.map(entry -> toFlatMethod(entry, context))
|
||||
.toList();
|
||||
}
|
||||
|
||||
/// 管理端:某支付渠道下的全部支付方式
|
||||
public List<MergedRelationRow> listMergedRelationsForProvider(String providerCode) {
|
||||
Map<String, PayMethod> methodMap = payMethodManager.mapByCode();
|
||||
List<MergedRelationRow> rows = new ArrayList<>();
|
||||
int ordinal = 0;
|
||||
for (PayProviderMethod rel : payProviderMethodManager.listByProvider(providerCode)) {
|
||||
PayMethod methodRow = methodMap.get(rel.getMethod());
|
||||
if (methodRow == null) {
|
||||
continue;
|
||||
}
|
||||
PayMethodEnum methodEnum = resolveMethodEnum(rel.getMethod());
|
||||
if (methodEnum == null) {
|
||||
continue;
|
||||
}
|
||||
int sortNo = rel.getSortNo() != null ? rel.getSortNo() : ordinal;
|
||||
rows.add(new MergedRelationRow(methodEnum, sortNo, rel.getDescription()));
|
||||
ordinal++;
|
||||
}
|
||||
rows.sort(Comparator.comparingInt(MergedRelationRow::sortNo));
|
||||
return rows;
|
||||
}
|
||||
|
||||
/// 转为平铺展示用的渠道+方式结果
|
||||
private PayProviderMethodResult toFlatMethod(PayProviderMethodEntry entry, DirectoryLoadContext context) {
|
||||
String pairKey = PayProviderMethodManager.pairKey(entry.getProviderCode(), entry.getMethodCode());
|
||||
PayProviderMethod relation = context.relationMap().get(pairKey);
|
||||
return new PayProviderMethodResult()
|
||||
.setProvider(entry.getProviderCode())
|
||||
.setMethod(entry.getMethodCode())
|
||||
.setMethodLabel(I18nUtil.getEnumName(entry.getMethod()))
|
||||
.setDescription(relation != null ? relation.getDescription() : null);
|
||||
}
|
||||
|
||||
/// 合并关联表与支付方式主数据
|
||||
private List<MergedRelationRow> mergeRelations(List<PayProviderMethod> relations, Map<String, PayMethod> methodMap) {
|
||||
List<MergedRelationRow> rows = new ArrayList<>();
|
||||
int ordinal = 0;
|
||||
for (PayProviderMethod rel : relations) {
|
||||
PayMethod methodRow = methodMap.get(rel.getMethod());
|
||||
if (methodRow == null) {
|
||||
continue;
|
||||
}
|
||||
PayMethodEnum methodEnum = resolveMethodEnum(rel.getMethod());
|
||||
if (methodEnum == null) {
|
||||
continue;
|
||||
}
|
||||
int sortNo = rel.getSortNo() != null ? rel.getSortNo() : ordinal;
|
||||
rows.add(new MergedRelationRow(methodEnum, sortNo, rel.getDescription()));
|
||||
ordinal++;
|
||||
}
|
||||
rows.sort(Comparator.comparingInt(MergedRelationRow::sortNo));
|
||||
return rows;
|
||||
}
|
||||
|
||||
/// 按编码解析支付方式枚举
|
||||
private PayMethodEnum resolveMethodEnum(String methodCode) {
|
||||
return Arrays.stream(PayMethodEnum.values())
|
||||
.filter(e -> Objects.equals(e.getCode(), methodCode))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/// 预加载的渠道、方式、关联数据
|
||||
public record DirectoryLoadContext(
|
||||
Map<String, PayProvider> providerMap,
|
||||
Map<String, PayMethod> methodMap,
|
||||
Map<String, PayProviderMethod> relationMap,
|
||||
Map<String, List<MergedRelationRow>> relationsByProvider) {
|
||||
}
|
||||
|
||||
/// 一条「渠道 + 支付方式」合并后的展示行
|
||||
public record MergedRelationRow(
|
||||
PayMethodEnum methodEnum,
|
||||
int sortNo,
|
||||
String description) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.provider.service;
|
||||
|
||||
import org.dromara.daxpay.payment.masterdata.constants.provider.dao.PayProviderMethodManager;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.product.result.PayProductResult;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.provider.result.PayProviderProductResult;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.product.service.PayProductCapabilityService;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.product.service.PayProductService;
|
||||
import org.dromara.daxpay.platform.common.i18n.util.I18nUtil;
|
||||
import org.dromara.daxpay.platform.core.enums.pay.channel.ChannelEnum;
|
||||
import org.dromara.daxpay.platform.core.model.PayProviderMethodEntry;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/// # 支付渠道与支付方式对应的产品
|
||||
///
|
||||
/// 计算每种「支付渠道 + 支付方式」可用的支付产品列表。
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class PayProviderProductService {
|
||||
|
||||
private final PayProductService payProductService;
|
||||
private final PayProductCapabilityService payProductCapabilityService;
|
||||
|
||||
/// 构建「渠道+方式 → 产品列表」索引,供管理端批量展示
|
||||
public Map<String, List<PayProviderProductResult>> buildSupportedProductsIndex(
|
||||
List<PayProviderMethodEntry> directoryEntries) {
|
||||
Map<String, List<PayProviderProductResult>> index = new HashMap<>();
|
||||
if (directoryEntries.isEmpty()) {
|
||||
return index;
|
||||
}
|
||||
Map<String, Set<String>> capabilityCodesByProduct = payProductCapabilityService.loadCapabilityCodesByProduct();
|
||||
for (PayProductResult product : payProductService.listAll()) {
|
||||
PayProviderProductResult productItem = new PayProviderProductResult()
|
||||
.setLabel(product.getName())
|
||||
.setValue(product.getCode())
|
||||
.setChannel(product.getChannel())
|
||||
.setChannelName(product.getChannelName() != null
|
||||
? product.getChannelName()
|
||||
: I18nUtil.getEnumName(ChannelEnum.findByCode(product.getChannel())));
|
||||
for (PayProviderMethodEntry entry : directoryEntries) {
|
||||
if (!payProductCapabilityService.supportsMethod(
|
||||
capabilityCodesByProduct, product.getCode(), entry.getMethodCode())) {
|
||||
continue;
|
||||
}
|
||||
String pairKey = PayProviderMethodManager.pairKey(entry.getProviderCode(), entry.getMethodCode());
|
||||
index.computeIfAbsent(pairKey, key -> new ArrayList<>()).add(productItem);
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package org.dromara.daxpay.payment.masterdata.constants.provider.service;
|
||||
|
||||
import org.dromara.daxpay.payment.masterdata.constants.provider.dao.PayProviderMethodManager;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.provider.entity.PayProvider;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.provider.result.PayProviderGroupResult;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.provider.result.PayProviderMethodResult;
|
||||
import org.dromara.daxpay.payment.masterdata.constants.provider.result.PayProviderProductResult;
|
||||
import org.dromara.daxpay.platform.common.i18n.util.I18nUtil;
|
||||
import org.dromara.daxpay.platform.core.enums.pay.channel.PayProviderEnum;
|
||||
import org.dromara.daxpay.platform.core.exception.DataNotExistException;
|
||||
import org.dromara.daxpay.platform.core.model.PayProviderMethodEntry;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/// # 管理端支付渠道配置
|
||||
///
|
||||
/// 按支付渠道分组展示支付方式,并附带每种方式支持的产品列表。
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class PayProviderService {
|
||||
|
||||
private final PayProviderMethodService payProviderMethodService;
|
||||
private final PayProviderProductService payProviderProductService;
|
||||
|
||||
/// 按支付渠道分组,返回各渠道下的支付方式
|
||||
public List<PayProviderGroupResult> listByProvider() {
|
||||
return buildGroups(loadAdminDirectoryContext());
|
||||
}
|
||||
|
||||
/// 按支付渠道编码与支付方式编码查一条配置详情(含支持的产品列表)
|
||||
public PayProviderMethodResult get(String providerCode, String methodCode) {
|
||||
if (PayProviderEnum.findByCode(providerCode) == null) {
|
||||
throw new DataNotExistException("error.payment.capability.invalidProvider");
|
||||
}
|
||||
AdminDirectoryContext adminContext = loadAdminDirectoryContext();
|
||||
PayProviderMethodService.MergedRelationRow row = adminContext.directoryContext().relationsByProvider()
|
||||
.getOrDefault(providerCode, List.of()).stream()
|
||||
.filter(r -> Objects.equals(r.methodEnum().getCode(), methodCode))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new DataNotExistException("error.payment.capability.methodNotInDirectory"));
|
||||
return toAdminMethod(providerCode, row, adminContext);
|
||||
}
|
||||
|
||||
/// 预加载管理端展示所需数据
|
||||
private AdminDirectoryContext loadAdminDirectoryContext() {
|
||||
PayProviderMethodService.DirectoryLoadContext directoryContext =
|
||||
payProviderMethodService.loadDirectoryContext();
|
||||
List<PayProviderMethodEntry> directoryEntries =
|
||||
payProviderMethodService.listDirectoryEntriesFromContext(directoryContext);
|
||||
Map<String, List<PayProviderProductResult>> supportedProductsIndex =
|
||||
payProviderProductService.buildSupportedProductsIndex(directoryEntries);
|
||||
return new AdminDirectoryContext(directoryContext, supportedProductsIndex);
|
||||
}
|
||||
|
||||
/// 按支付渠道分组组装列表
|
||||
private List<PayProviderGroupResult> buildGroups(AdminDirectoryContext adminContext) {
|
||||
PayProviderMethodService.DirectoryLoadContext directoryContext = adminContext.directoryContext();
|
||||
Map<String, PayProvider> providerMap = directoryContext.providerMap();
|
||||
List<PayProviderGroupResult> groups = new ArrayList<>();
|
||||
int brandOrdinal = 0;
|
||||
for (PayProviderEnum brandEnum : PayProviderEnum.values()) {
|
||||
PayProvider dbBrand = providerMap.get(brandEnum.getCode());
|
||||
PayProviderGroupResult group = new PayProviderGroupResult()
|
||||
.setProvider(brandEnum.getCode())
|
||||
.setProviderLabel(I18nUtil.getEnumName(brandEnum))
|
||||
.setIcon(dbBrand != null ? dbBrand.getIcon() : null)
|
||||
.setSortNo(resolveSortNo(dbBrand, brandOrdinal));
|
||||
List<PayProviderMethodResult> methods = directoryContext.relationsByProvider()
|
||||
.getOrDefault(brandEnum.getCode(), List.of()).stream()
|
||||
.map(row -> toAdminMethod(brandEnum.getCode(), row, adminContext))
|
||||
.toList();
|
||||
group.setMethods(methods);
|
||||
groups.add(group);
|
||||
brandOrdinal++;
|
||||
}
|
||||
groups.sort(Comparator.comparingInt(g -> g.getSortNo() != null ? g.getSortNo() : 0));
|
||||
return groups;
|
||||
}
|
||||
|
||||
/// 组装管理端单条渠道+方式详情
|
||||
private PayProviderMethodResult toAdminMethod(
|
||||
String providerCode,
|
||||
PayProviderMethodService.MergedRelationRow row,
|
||||
AdminDirectoryContext adminContext) {
|
||||
String methodCode = row.methodEnum().getCode();
|
||||
String pairKey = PayProviderMethodManager.pairKey(providerCode, methodCode);
|
||||
List<PayProviderProductResult> supportedProducts =
|
||||
adminContext.supportedProductsIndex().getOrDefault(pairKey, List.of());
|
||||
return new PayProviderMethodResult()
|
||||
.setProvider(providerCode)
|
||||
.setMethod(methodCode)
|
||||
.setMethodLabel(I18nUtil.getEnumName(row.methodEnum()))
|
||||
.setSortNo(row.sortNo())
|
||||
.setDescription(row.description())
|
||||
.setSupportedProducts(supportedProducts);
|
||||
}
|
||||
|
||||
/// 取渠道排序号,无则使用枚举顺序
|
||||
private static int resolveSortNo(PayProvider dbBrand, int fallback) {
|
||||
return dbBrand != null && dbBrand.getSortNo() != null ? dbBrand.getSortNo() : fallback;
|
||||
}
|
||||
|
||||
/// 管理端预加载:渠道方式配置与支持的产品索引
|
||||
private record AdminDirectoryContext(
|
||||
PayProviderMethodService.DirectoryLoadContext directoryContext,
|
||||
Map<String, List<PayProviderProductResult>> supportedProductsIndex) {
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package org.dromara.daxpay.payment.old.pay.service.route;
|
||||
|
||||
import org.dromara.daxpay.payment.unipay.param.trade.pay.PayParam;
|
||||
|
||||
/// # 支付通道路由门面
|
||||
///
|
||||
public interface PayRouteFacade {
|
||||
|
||||
/// 当 product 为空时执行路由并回填支付参数
|
||||
void resolve(PayParam payParam);
|
||||
}
|
||||
Reference in New Issue
Block a user