diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/config/convert/ChannelConfigConvert.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/config/convert/ChannelConfigConvert.java new file mode 100644 index 000000000..a1aa7bf1f --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/config/convert/ChannelConfigConvert.java @@ -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); +} diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/config/entity/ChannelConfig.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/config/entity/ChannelConfig.java new file mode 100644 index 000000000..ae2319f87 --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/config/entity/ChannelConfig.java @@ -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 { + + /// 支付通道 + /// @see ChannelEnum + private String channel; + + /// 是否启用 + private boolean enable; + + @Override + public ChannelConfigResult toResult() { + return ChannelConfigConvert.INSTANCE.toResult(this); + } +} + diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/config/result/ChannelConfigResult.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/config/result/ChannelConfigResult.java new file mode 100644 index 000000000..44e74dd1d --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/config/result/ChannelConfigResult.java @@ -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; +} + diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/capability/convert/PayCapabilityConvert.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/capability/convert/PayCapabilityConvert.java new file mode 100644 index 000000000..4bb47cd1e --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/capability/convert/PayCapabilityConvert.java @@ -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); +} diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/capability/dao/PayCapabilityManager.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/capability/dao/PayCapabilityManager.java new file mode 100644 index 000000000..bd1e61c19 --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/capability/dao/PayCapabilityManager.java @@ -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 { + + /// 按编码查询 + public Optional findByCode(String code) { + return lambdaQuery().eq(PayCapability::getCode, code).oneOpt(); + } + + /// 是否存在任意主数据行(预留:后续若能力参与运行时收窄可据此分支) + public boolean hasAny() { + return lambdaQuery().count() > 0; + } + + /// 分页(默认 sort_no、id 升序) + public Page page(PageParam pageParam, PayCapabilityQuery query) { + Page mpPage = MpUtil.getMpPage(pageParam); + QueryWrapper 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 listAllOrdered() { + return lambdaQuery() + .orderByAsc(PayCapability::getSortNo) + .orderByAsc(PayCapability::getId) + .list(); + } + + /// code -> 实体 + public Map mapByCode() { + return listAllOrdered().stream() + .collect(Collectors.toMap(PayCapability::getCode, e -> e, (a, b) -> a)); + } + + /// 按编码批量查询 + public List listByCodes(Collection codes) { + if (CollUtil.isEmpty(codes)) { + return Collections.emptyList(); + } + return lambdaQuery().in(PayCapability::getCode, codes).list(); + } +} \ No newline at end of file diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/capability/dao/PayCapabilityMapper.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/capability/dao/PayCapabilityMapper.java new file mode 100644 index 000000000..15f58f2c7 --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/capability/dao/PayCapabilityMapper.java @@ -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 { +} \ No newline at end of file diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/capability/dao/PayProductCapabilityManager.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/capability/dao/PayProductCapabilityManager.java new file mode 100644 index 000000000..0fe5fc38e --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/capability/dao/PayProductCapabilityManager.java @@ -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 { + + /// 某能力下的全部关联 + public List listByCapability(String capabilityCode) { + return lambdaQuery() + .eq(PayProductCapability::getCapabilityCode, capabilityCode) + .orderByAsc(PayProductCapability::getSortNo) + .orderByAsc(PayProductCapability::getId) + .list(); + } + + /// 某产品下的全部关联 + public List listByProduct(String productCode) { + return lambdaQuery() + .eq(PayProductCapability::getProductCode, productCode) + .orderByAsc(PayProductCapability::getSortNo) + .orderByAsc(PayProductCapability::getId) + .list(); + } + + /// 多个产品下的全部关联 + public List listByProductCodes(Collection 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 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(); + } +} \ No newline at end of file diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/capability/dao/PayProductCapabilityMapper.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/capability/dao/PayProductCapabilityMapper.java new file mode 100644 index 000000000..cd95c8011 --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/capability/dao/PayProductCapabilityMapper.java @@ -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 { +} \ No newline at end of file diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/capability/entity/PayCapability.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/capability/entity/PayCapability.java new file mode 100644 index 000000000..8ef48d05f --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/capability/entity/PayCapability.java @@ -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 { + + /// 支付能力编码 + /// @see PayCapabilityEnum + private String code; + + /// 全局排序 + private Integer sortNo; + + /// 说明 + private String description; + + /// 转换 + @Override + public PayCapabilityResult toResult() { + return PayCapabilityConvert.CONVERT.toResult(this); + } +} \ No newline at end of file diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/capability/param/PayCapabilityQuery.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/capability/param/PayCapabilityQuery.java new file mode 100644 index 000000000..c870e4425 --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/capability/param/PayCapabilityQuery.java @@ -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; +} \ No newline at end of file diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/capability/result/PayCapabilityResult.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/capability/result/PayCapabilityResult.java new file mode 100644 index 000000000..24e7021b3 --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/capability/result/PayCapabilityResult.java @@ -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 products; +} \ No newline at end of file diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/capability/service/PayCapabilityService.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/capability/service/PayCapabilityService.java new file mode 100644 index 000000000..19b8da868 --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/capability/service/PayCapabilityService.java @@ -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 page(PageParam pageParam, PayCapabilityQuery query, String nameKeyword) { + PageResult pageResult = MpUtil.toPageResult(payCapabilityManager.page(pageParam, query)); + List 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 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); + } +} diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/channel/convert/PayChannelConvert.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/channel/convert/PayChannelConvert.java new file mode 100644 index 000000000..2f78c9434 --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/channel/convert/PayChannelConvert.java @@ -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); +} \ No newline at end of file diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/channel/dao/PayChannelManager.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/channel/dao/PayChannelManager.java new file mode 100644 index 000000000..c9f17ebee --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/channel/dao/PayChannelManager.java @@ -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 { + + /// 根据通道编码查询 + public Optional findByCode(String code) { + return lambdaQuery() + .eq(PayChannel::getCode, code) + .oneOpt(); + } + + /// 分页 + public Page page(PageParam pageParam, PayChannelQuery query) { + Page mpPage = MpUtil.getMpPage(pageParam); + QueryWrapper generator = QueryGenerator.generator(query); + return page(mpPage, generator); + } + + /// 查询所有(按排序号升序) + public List listAllOrdered() { + return lambdaQuery() + .orderByAsc(PayChannel::getSortNo) + .list(); + } +} \ No newline at end of file diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/channel/dao/PayChannelMapper.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/channel/dao/PayChannelMapper.java new file mode 100644 index 000000000..83cb22b5b --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/channel/dao/PayChannelMapper.java @@ -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 { +} \ No newline at end of file diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/channel/entity/PayChannel.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/channel/entity/PayChannel.java new file mode 100644 index 000000000..712adc30f --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/channel/entity/PayChannel.java @@ -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 { + + /// 通道编码 + /// @see ChannelEnum + private String code; + + /// 排序 + private Integer sortNo; + + /// 通道介绍 + private String description; + + /// 图标 + private String icon; + + /// 转换 + @Override + public PayChannelResult toResult() { + return PayChannelConvert.CONVERT.toResult(this); + } +} \ No newline at end of file diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/channel/param/PayChannelQuery.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/channel/param/PayChannelQuery.java new file mode 100644 index 000000000..f6a079272 --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/channel/param/PayChannelQuery.java @@ -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; +} diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/channel/result/PayChannelResult.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/channel/result/PayChannelResult.java new file mode 100644 index 000000000..96c8ebd80 --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/channel/result/PayChannelResult.java @@ -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; +} \ No newline at end of file diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/channel/service/PayChannelService.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/channel/service/PayChannelService.java new file mode 100644 index 000000000..6fe0cfac2 --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/channel/service/PayChannelService.java @@ -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 page(PageParam pageParam, PayChannelQuery query, String nameKeyword) { + PageResult 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 dropdown() { + return payChannelManager.listAllOrdered().stream() + .map(PayChannel::toResult) + .map(this::fillChannelName) + .map(e -> new LabelValue(e.getName(), e.getCode())) + .toList(); + } + + /// 返回全部支付通道 + public List listAll() { + return buildChannels(); + } + + /// 按通道编码取展示名称 + public String findNameByCode(String code) { + return resolveChannelName(code); + } + + /// 枚举与库表合并为通道列表 + private List buildChannels() { + Map 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 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); + } + +} diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/method/dao/PayMethodManager.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/method/dao/PayMethodManager.java new file mode 100644 index 000000000..600f1949d --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/method/dao/PayMethodManager.java @@ -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 { + + /// 按编码查询 + public Optional findByCode(String code) { + return lambdaQuery().eq(PayMethod::getCode, code).oneOpt(); + } + + /// 全部未删除,按 sortNo、id 排序 + public List listAllOrdered() { + return lambdaQuery() + .orderByAsc(PayMethod::getSortNo) + .orderByAsc(PayMethod::getId) + .list(); + } + + /// code -> 实体 + public Map mapByCode() { + return listAllOrdered().stream() + .collect(Collectors.toMap(PayMethod::getCode, e -> e, (a, b) -> a)); + } +} \ No newline at end of file diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/method/dao/PayMethodMapper.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/method/dao/PayMethodMapper.java new file mode 100644 index 000000000..e084a8f81 --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/method/dao/PayMethodMapper.java @@ -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 { +} \ No newline at end of file diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/method/entity/PayMethod.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/method/entity/PayMethod.java new file mode 100644 index 000000000..955a3085c --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/method/entity/PayMethod.java @@ -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; +} \ No newline at end of file diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/convert/PayProductConfigConvert.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/convert/PayProductConfigConvert.java new file mode 100644 index 000000000..6ed42f74d --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/convert/PayProductConfigConvert.java @@ -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); +} diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/convert/PayProductConvert.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/convert/PayProductConvert.java new file mode 100644 index 000000000..17fc9559e --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/convert/PayProductConvert.java @@ -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); +} diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/dao/PayProductConfigManager.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/dao/PayProductConfigManager.java new file mode 100644 index 000000000..9901ecad2 --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/dao/PayProductConfigManager.java @@ -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 { + + /// 根据产品编码查询 + public Optional findByProduct(String product) { + return lambdaQuery() + .eq(PayProductConfig::getProduct, product) + .oneOpt(); + } +} diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/dao/PayProductConfigMapper.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/dao/PayProductConfigMapper.java new file mode 100644 index 000000000..c13d8354d --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/dao/PayProductConfigMapper.java @@ -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 { +} diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/dao/PayProductManager.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/dao/PayProductManager.java new file mode 100644 index 000000000..c7407c66a --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/dao/PayProductManager.java @@ -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 { + + /// 根据产品编码查询 + public Optional findByCode(String code) { + return lambdaQuery() + .eq(PayProduct::getCode, code) + .oneOpt(); + } + + /// 按通道编码查询 + public List listByChannel(String channel) { + return lambdaQuery() + .eq(PayProduct::getChannel, channel) + .orderByAsc(PayProduct::getSortNo) + .orderByAsc(PayProduct::getId) + .list(); + } + + /// 分页 + public Page page(PageParam pageParam, PayProductQuery query) { + Page mpPage = MpUtil.getMpPage(pageParam); + QueryWrapper generator = QueryGenerator.generator(query); + generator.lambda().orderByAsc(PayProduct::getSortNo).orderByAsc(PayProduct::getId); + return page(mpPage, generator); + } +} diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/dao/PayProductMapper.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/dao/PayProductMapper.java new file mode 100644 index 000000000..d50761d5c --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/dao/PayProductMapper.java @@ -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 { +} diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/entity/PayProduct.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/entity/PayProduct.java new file mode 100644 index 000000000..c4ea320bc --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/entity/PayProduct.java @@ -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 { + + /// 产品编码 + /// @see ProductEnum + private String code; + + /// 关联通道编码 + private String channel; + + /// 产品介绍 + private String description; + + /// 图标 + private String icon; + + /// 支持的结算周期列表 + @TableField(typeHandler = StringListTypeHandler.class) + private List settlePeriods; + + /// 排序 + private Integer sortNo; + + /// 是否支持沙箱环境 + private Boolean sandbox; + + /// 转换 + @Override + public PayProductResult toResult() { + return PayProductConvert.CONVERT.toResult(this); + } +} \ No newline at end of file diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/entity/PayProductCapability.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/entity/PayProductCapability.java new file mode 100644 index 000000000..3ad4b4422 --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/entity/PayProductCapability.java @@ -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; +} \ No newline at end of file diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/entity/PayProductConfig.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/entity/PayProductConfig.java new file mode 100644 index 000000000..0cbffecdb --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/entity/PayProductConfig.java @@ -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 { + + /// 产品编码 + 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); + } +} diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/param/PayProductConfigParam.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/param/PayProductConfigParam.java new file mode 100644 index 000000000..385ed401b --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/param/PayProductConfigParam.java @@ -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; +} diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/param/PayProductQuery.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/param/PayProductQuery.java new file mode 100644 index 000000000..2055295c9 --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/param/PayProductQuery.java @@ -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; +} \ No newline at end of file diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/result/PayProductCapabilityResult.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/result/PayProductCapabilityResult.java new file mode 100644 index 000000000..eaa72dde8 --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/result/PayProductCapabilityResult.java @@ -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; +} \ No newline at end of file diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/result/PayProductConfigResult.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/result/PayProductConfigResult.java new file mode 100644 index 000000000..98c5f2172 --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/result/PayProductConfigResult.java @@ -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; +} diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/result/PayProductResult.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/result/PayProductResult.java new file mode 100644 index 000000000..7f0ed0ce3 --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/result/PayProductResult.java @@ -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 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 capabilities; +} \ No newline at end of file diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/service/PayProductCapabilityService.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/service/PayProductCapabilityService.java new file mode 100644 index 000000000..c5f4b9ff1 --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/service/PayProductCapabilityService.java @@ -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> loadCapabilityCodesByProduct() { + Map capabilityMap = payCapabilityManager.mapByCode(); + Map> 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> capabilityCodesByProduct, String productCode, String methodCode) { + Set 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 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 products) { + if (CollUtil.isEmpty(products)) { + return; + } + Set productCodes = products.stream() + .map(PayProductResult::getCode) + .filter(StrUtil::isNotBlank) + .collect(Collectors.toSet()); + if (productCodes.isEmpty()) { + return; + } + List allRels = payProductCapabilityManager.listByProductCodes(productCodes); + Map> relsByProduct = allRels.stream() + .collect(Collectors.groupingBy(PayProductCapability::getProductCode)); + Set capabilityCodes = allRels.stream() + .map(PayProductCapability::getCapabilityCode) + .collect(Collectors.toSet()); + Map 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 rels = relsByProduct.getOrDefault(product.getCode(), List.of()); + List 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 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); + } +} \ No newline at end of file diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/service/PayProductConfigService.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/service/PayProductConfigService.java new file mode 100644 index 000000000..a01c1a76a --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/service/PayProductConfigService.java @@ -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 listAll() { + Map 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 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); + } +} diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/service/PayProductService.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/service/PayProductService.java new file mode 100644 index 000000000..308778bda --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/product/service/PayProductService.java @@ -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 page(PageParam pageParam, PayProductQuery query, String nameKeyword) { + PageResult 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 listSortedProducts() { + return payProductManager.lambdaQuery() + .orderByAsc(PayProduct::getSortNo) + .list(); + } + + /// 按支付通道编码查询所属支付产品 + public List listByChannel(String channelCode) { + return payProductManager.listByChannel(channelCode).stream() + .map(PayProduct::toResult) + .map(this::fillProductFeatures) + .toList(); + } + + /// 返回全部支付产品,按 sortNo、id 排序 + /// 策略存在时补全特性字段,不存在时仅返回枚举与数据库基础信息 + public List listAll() { + Map 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 dropdown() { + return listAll().stream() + .map(e -> new LabelValue(e.getName(), e.getCode())) + .toList(); + } + + /// 枚举与库表合并为产品结果 + private PayProductResult toProductResult(ProductEnum product, Map 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); + } +} diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/provider/dao/PayProviderManager.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/provider/dao/PayProviderManager.java new file mode 100644 index 000000000..ca6e50196 --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/provider/dao/PayProviderManager.java @@ -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 { + + /// 按编码查询 + public Optional findByCode(String code) { + return lambdaQuery().eq(PayProvider::getCode, code).oneOpt(); + } + + /// 全部未删除记录,按 sortNo、id 排序 + public List listAllOrdered() { + return lambdaQuery() + .orderByAsc(PayProvider::getSortNo) + .orderByAsc(PayProvider::getId) + .list(); + } + + /// code -> 实体 + public Map mapByCode() { + return listAllOrdered().stream() + .collect(Collectors.toMap(PayProvider::getCode, e -> e, (a, b) -> a)); + } +} \ No newline at end of file diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/provider/dao/PayProviderMapper.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/provider/dao/PayProviderMapper.java new file mode 100644 index 000000000..69917cf6c --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/provider/dao/PayProviderMapper.java @@ -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 { +} \ No newline at end of file diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/provider/dao/PayProviderMethodManager.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/provider/dao/PayProviderMethodManager.java new file mode 100644 index 000000000..9ff1d4a58 --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/provider/dao/PayProviderMethodManager.java @@ -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 { + + /// 全部未删除记录 + public List listAllOrdered() { + return lambdaQuery() + .orderByAsc(PayProviderMethod::getProvider) + .orderByAsc(PayProviderMethod::getSortNo) + .orderByAsc(PayProviderMethod::getId) + .list(); + } + + /// (provider|method) -> 实体 + public Map 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 listByMethod(String methodCode) { + return lambdaQuery() + .eq(PayProviderMethod::getMethod, methodCode) + .orderByAsc(PayProviderMethod::getSortNo) + .orderByAsc(PayProviderMethod::getId) + .list(); + } + + /// 某支付渠道下的全部关联(未删除) + public List listByProvider(String providerCode) { + return lambdaQuery() + .eq(PayProviderMethod::getProvider, providerCode) + .orderByAsc(PayProviderMethod::getSortNo) + .orderByAsc(PayProviderMethod::getId) + .list(); + } +} \ No newline at end of file diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/provider/dao/PayProviderMethodMapper.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/provider/dao/PayProviderMethodMapper.java new file mode 100644 index 000000000..f5b94e551 --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/provider/dao/PayProviderMethodMapper.java @@ -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 { +} \ No newline at end of file diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/provider/entity/PayProvider.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/provider/entity/PayProvider.java new file mode 100644 index 000000000..c0e1fb46f --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/provider/entity/PayProvider.java @@ -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; +} \ No newline at end of file diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/provider/entity/PayProviderMethod.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/provider/entity/PayProviderMethod.java new file mode 100644 index 000000000..7a5ae21b4 --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/provider/entity/PayProviderMethod.java @@ -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; +} \ No newline at end of file diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/provider/result/PayProviderGroupResult.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/provider/result/PayProviderGroupResult.java new file mode 100644 index 000000000..1e5547cb1 --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/provider/result/PayProviderGroupResult.java @@ -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 methods; +} \ No newline at end of file diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/provider/result/PayProviderMethodResult.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/provider/result/PayProviderMethodResult.java new file mode 100644 index 000000000..c5d220f17 --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/provider/result/PayProviderMethodResult.java @@ -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 supportedProducts; +} \ No newline at end of file diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/provider/result/PayProviderProductResult.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/provider/result/PayProviderProductResult.java new file mode 100644 index 000000000..51dd726e2 --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/provider/result/PayProviderProductResult.java @@ -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; +} \ No newline at end of file diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/provider/service/PayProviderMethodService.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/provider/service/PayProviderMethodService.java new file mode 100644 index 000000000..6270e2c91 --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/provider/service/PayProviderMethodService.java @@ -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 providerMap = payProviderManager.mapByCode(); + Map methodMap = payMethodManager.mapByCode(); + Map relationMap = payProviderMethodManager.mapByPairKey(); + Map> realsByProvider = payProviderMethodManager.listAllOrdered().stream() + .collect(Collectors.groupingBy(PayProviderMethod::getProvider)); + Map> relationsByProvider = new HashMap<>(); + for (Map.Entry> entry : realsByProvider.entrySet()) { + relationsByProvider.put(entry.getKey(), mergeRelations(entry.getValue(), methodMap)); + } + return new DirectoryLoadContext(providerMap, methodMap, relationMap, relationsByProvider); + } + + /// 从预加载数据中取目录中的「渠道 + 支付方式」 + public List listDirectoryEntriesFromContext(DirectoryLoadContext context) { + List 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 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 listMethodsForProvider(PayProviderEnum provider) { + return listMergedRelationsForProvider(provider.getCode()).stream() + .map(MergedRelationRow::methodEnum) + .toList(); + } + + /// 同 {@link #listMethodsForProvider(PayProviderEnum)} + public List listMethodsForBrand(PayProviderEnum brand) { + return listMethodsForProvider(brand); + } + + /// 平铺返回目录中的渠道与支付方式(不按渠道分组,不含支持的产品) + public List listDirectoryFlat() { + DirectoryLoadContext context = loadDirectoryContext(); + return listDirectoryEntriesFromContext(context).stream() + .map(entry -> toFlatMethod(entry, context)) + .toList(); + } + + /// 管理端:某支付渠道下的全部支付方式 + public List listMergedRelationsForProvider(String providerCode) { + Map methodMap = payMethodManager.mapByCode(); + List 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 mergeRelations(List relations, Map methodMap) { + List 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 providerMap, + Map methodMap, + Map relationMap, + Map> relationsByProvider) { + } + + /// 一条「渠道 + 支付方式」合并后的展示行 + public record MergedRelationRow( + PayMethodEnum methodEnum, + int sortNo, + String description) { + } +} \ No newline at end of file diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/provider/service/PayProviderProductService.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/provider/service/PayProviderProductService.java new file mode 100644 index 000000000..c171a5906 --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/provider/service/PayProviderProductService.java @@ -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> buildSupportedProductsIndex( + List directoryEntries) { + Map> index = new HashMap<>(); + if (directoryEntries.isEmpty()) { + return index; + } + Map> 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; + } +} \ No newline at end of file diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/provider/service/PayProviderService.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/provider/service/PayProviderService.java new file mode 100644 index 000000000..696d77da2 --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/masterdata/constants/provider/service/PayProviderService.java @@ -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 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 directoryEntries = + payProviderMethodService.listDirectoryEntriesFromContext(directoryContext); + Map> supportedProductsIndex = + payProviderProductService.buildSupportedProductsIndex(directoryEntries); + return new AdminDirectoryContext(directoryContext, supportedProductsIndex); + } + + /// 按支付渠道分组组装列表 + private List buildGroups(AdminDirectoryContext adminContext) { + PayProviderMethodService.DirectoryLoadContext directoryContext = adminContext.directoryContext(); + Map providerMap = directoryContext.providerMap(); + List 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 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 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> supportedProductsIndex) { + } +} diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/old/pay/service/route/PayRouteFacade.java b/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/old/pay/service/route/PayRouteFacade.java deleted file mode 100644 index 09af57720..000000000 --- a/daxpay-payment/daxpay-payment-common/src/main/java/org/dromara/daxpay/payment/old/pay/service/route/PayRouteFacade.java +++ /dev/null @@ -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); -}