feat 储值卡和钱包适配多商户

This commit is contained in:
xxm1995
2023-07-21 15:46:57 +08:00
parent 30894b1cab
commit 5bc9969779
17 changed files with 227 additions and 38 deletions

View File

@@ -1,5 +1,6 @@
1.0.1
- 微信V3支付接口
- 钱包支持多商户和多应用
- 储值卡支持多商户和多应用
- x 拆分网关同步相关代码
- x 记录网关同步记录
- x 重构支付消息通知结构, 支持多种消息中间件
@@ -8,8 +9,9 @@
- 储值卡多卡支付和退款演示
- x 储值卡信息调整
- x 储值卡多卡退款到单卡, 不传卡号自动设置为默认卡
- 储值卡批量导入
- 储值卡支持多卡合一
1.0.2
- 微信V3支付接口
- 支付超时逻辑重构
-
- 储值卡支持多卡合一
- 储值卡批量导入
- 删除应用或商户是做校验, 级联删除对应的支付配置

View File

@@ -81,14 +81,8 @@ public class WalletAdminController {
@Operation(summary = "分页(未开通钱包的用户)")
@GetMapping("/pageByNotWallet")
public ResResult<PageResult<UserInfoDto>> pageByNotWallet(PageParam pageParam, UserInfoParam param) {
return Res.ok(walletQueryService.pageByNotWallet(pageParam, param));
}
@Operation(summary = "根据用户ID查询钱包")
@GetMapping("/findByUserId")
public ResResult<WalletDto> findByUserId(Long userId) {
return Res.ok(walletQueryService.findByUserId(userId));
public ResResult<PageResult<UserInfoDto>> pageByNotWallet(PageParam pageParam,String mchCode, UserInfoParam param) {
return Res.ok(walletQueryService.pageByNotWallet(pageParam,mchCode,param));
}
@Operation(summary = "根据钱包ID查询钱包")

View File

@@ -0,0 +1,44 @@
package cn.bootx.platform.daxpay.controller;
import cn.bootx.platform.common.core.rest.Res;
import cn.bootx.platform.common.core.rest.ResResult;
import cn.bootx.platform.daxpay.core.channel.wallet.service.WalletConfigService;
import cn.bootx.platform.daxpay.dto.channel.wallet.WalletConfigDto;
import cn.bootx.platform.daxpay.param.channel.wechat.WalletConfigParam;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
/**
* 钱包配置
* @author xxm
* @since 2023/7/20
*/
@Tag(name = "钱包配置")
@RestController
@RequestMapping("/wallet/config")
@RequiredArgsConstructor
public class WalletConfigController {
private final WalletConfigService walletConfigService;
@Operation(summary = "获取钱包支付配置")
@GetMapping("/findByMchCode")
public ResResult<WalletConfigDto> findByMchCode(String mchCode){
return Res.ok(walletConfigService.findByMchCode(mchCode));
}
@Operation(summary = "添加")
@PostMapping("/add")
public ResResult<Void> add(@RequestBody WalletConfigParam param) {
walletConfigService.add(param);
return Res.ok();
}
@Operation(summary = "更新")
@PostMapping("/update")
public ResResult<Void> update(@RequestBody WalletConfigParam param) {
walletConfigService.update(param);
return Res.ok();
}
}

View File

@@ -1,6 +1,7 @@
package cn.bootx.platform.daxpay.core.channel.alipay.entity;
import cn.bootx.mybatis.table.modify.annotation.DbColumn;
import cn.bootx.mybatis.table.modify.annotation.DbTable;
import cn.bootx.mybatis.table.modify.mybatis.mysq.annotation.DbMySqlFieldType;
import cn.bootx.mybatis.table.modify.mybatis.mysq.annotation.DbMySqlIndex;
import cn.bootx.mybatis.table.modify.mybatis.mysq.constants.MySqlFieldTypeEnum;
@@ -30,7 +31,7 @@ import lombok.experimental.Accessors;
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
//@DbTable(comment = "支付宝支付配置")
@DbTable(comment = "支付宝支付配置")
@TableName("pay_alipay_config")
public class AlipayConfig extends MpBaseEntity implements EntityBaseFunction<AlipayConfigDto> {

View File

@@ -2,12 +2,15 @@ package cn.bootx.platform.daxpay.core.channel.voucher.entity;
import cn.bootx.mybatis.table.modify.annotation.DbColumn;
import cn.bootx.mybatis.table.modify.annotation.DbComment;
import cn.bootx.mybatis.table.modify.annotation.DbTable;
import cn.bootx.mybatis.table.modify.mybatis.mysq.annotation.DbMySqlIndex;
import cn.bootx.platform.common.core.function.EntityBaseFunction;
import cn.bootx.platform.common.mybatisplus.base.MpBaseEntity;
import cn.bootx.platform.daxpay.code.paymodel.VoucherCode;
import cn.bootx.platform.daxpay.core.channel.voucher.convert.VoucherConvert;
import cn.bootx.platform.daxpay.dto.channel.voucher.VoucherDto;
import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -24,11 +27,21 @@ import java.time.LocalDateTime;
*/
@EqualsAndHashCode(callSuper = true)
@Data
//@DbTable(comment = "储值卡")
@DbTable(comment = "储值卡")
@Accessors(chain = true)
@TableName("pay_voucher")
public class Voucher extends MpBaseEntity implements EntityBaseFunction<VoucherDto> {
/** 商户编码 */
@TableField(updateStrategy = FieldStrategy.NEVER)
@DbColumn(comment = "商户编码")
private String mchCode;
/** 商户应用编码 */
@TableField(updateStrategy = FieldStrategy.NEVER)
@DbColumn(comment = "商户应用编码")
private String mchAppCode;
/** 卡号 */
@DbComment("卡号")
@DbMySqlIndex(comment = "卡号索引")

View File

@@ -1,11 +1,14 @@
package cn.bootx.platform.daxpay.core.channel.wallet.convert;
import cn.bootx.platform.daxpay.core.channel.wallet.entity.Wallet;
import cn.bootx.platform.daxpay.core.channel.wallet.entity.WalletConfig;
import cn.bootx.platform.daxpay.core.channel.wallet.entity.WalletLog;
import cn.bootx.platform.daxpay.core.channel.wallet.entity.WalletPayment;
import cn.bootx.platform.daxpay.core.channel.wallet.entity.Wallet;
import cn.bootx.platform.daxpay.dto.channel.wallet.WalletConfigDto;
import cn.bootx.platform.daxpay.dto.channel.wallet.WalletDto;
import cn.bootx.platform.daxpay.dto.channel.wallet.WalletLogDto;
import cn.bootx.platform.daxpay.dto.channel.wallet.WalletPaymentDto;
import cn.bootx.platform.daxpay.param.channel.wechat.WalletConfigParam;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@@ -24,6 +27,10 @@ public interface WalletConvert {
WalletPaymentDto convert(WalletPayment in);
WalletLogDto convert(WalletLog walletLog);
WalletLogDto convert(WalletLog in);
WalletConfigDto convert(WalletConfig in);
WalletConfig convert(WalletConfigParam in);
}

View File

@@ -6,6 +6,8 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
import java.util.Optional;
/**
* 钱包配置
* @author xxm
@@ -15,4 +17,8 @@ import org.springframework.stereotype.Repository;
@Repository
@RequiredArgsConstructor
public class WalletConfigManager extends BaseManager<WalletConfigMapper, WalletConfig> {
public Optional<WalletConfig> findByMchCode(String mchCode){
return this.findByField(WalletConfig::getMchCode,mchCode);
}
}

View File

@@ -11,7 +11,6 @@ import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import java.util.Objects;
import java.util.Optional;
/**
* 钱包日志
@@ -23,19 +22,11 @@ import java.util.Optional;
@RequiredArgsConstructor
public class WalletLogManager extends BaseManager<WalletLogMapper, WalletLog> {
/**
* 按付款查找优先
*/
public Optional<WalletLog> findFirstByPayment(Long paymentId) {
return MpUtil.findOne(lambdaQuery().eq(WalletLog::getPaymentId, paymentId).orderByDesc(MpIdEntity::getId));
}
/**
* 分页查询指定用户的钱包日志
*/
public Page<WalletLog> pageByUserId(PageParam pageParam, WalletLogQueryParam param, Long userId) {
Page<WalletLog> mpPage = MpUtil.getMpPage(pageParam, WalletLog.class);
return this.lambdaQuery().orderByDesc(MpIdEntity::getId).eq(WalletLog::getUserId, userId).page(mpPage);
}

View File

@@ -125,11 +125,12 @@ public class WalletManager extends BaseManager<WalletMapper, Wallet> {
/**
* 待开通钱包的用户列表
*/
public Page<UserInfo> pageByNotWallet(PageParam pageParam, UserInfoParam userInfoParam) {
public Page<UserInfo> pageByNotWallet(PageParam pageParam, String mchCode, UserInfoParam userInfoParam) {
Page<UserInfo> mpPage = MpUtil.getMpPage(pageParam, UserInfo.class);
QueryWrapper<UserInfo> wrapper = new QueryWrapper<>();
wrapper.isNull("w.id")
.orderByDesc("w.id")
.eq("w.mch_code",mchCode)
.like(StrUtil.isNotBlank(userInfoParam.getUsername()), "w.username", userInfoParam.getUsername())
.like(StrUtil.isNotBlank(userInfoParam.getName()), "w.name", userInfoParam.getName());
return walletMapper.pageByNotWallet(mpPage, wrapper);

View File

@@ -1,19 +1,26 @@
package cn.bootx.platform.daxpay.core.channel.wallet.entity;
import cn.bootx.mybatis.table.modify.annotation.DbColumn;
import cn.bootx.mybatis.table.modify.annotation.DbTable;
import cn.bootx.mybatis.table.modify.mybatis.mysq.annotation.DbMySqlIndex;
import cn.bootx.mybatis.table.modify.mybatis.mysq.constants.MySqlIndexType;
import cn.bootx.platform.common.core.function.EntityBaseFunction;
import cn.bootx.platform.common.mybatisplus.base.MpBaseEntity;
import cn.bootx.platform.daxpay.code.paymodel.WalletCode;
import cn.bootx.platform.daxpay.core.channel.wallet.convert.WalletConvert;
import cn.bootx.platform.daxpay.dto.channel.wallet.WalletDto;
import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import lombok.experimental.FieldNameConstants;
import java.math.BigDecimal;
import static cn.bootx.platform.daxpay.core.channel.wallet.entity.Wallet.Fields.*;
/**
* 钱包
*
@@ -22,13 +29,25 @@ import java.math.BigDecimal;
*/
@EqualsAndHashCode(callSuper = true)
@Data
//@DbTable(comment = "钱包")
@DbMySqlIndex(comment = "用户和应用编码联合唯一索引",fields = {userId,mchAppCode},type = MySqlIndexType.UNIQUE)
@DbTable(comment = "钱包")
@Accessors(chain = true)
@TableName("pay_wallet")
@FieldNameConstants
public class Wallet extends MpBaseEntity implements EntityBaseFunction<WalletDto> {
/** 商户编码 */
@TableField(updateStrategy = FieldStrategy.NEVER)
@DbColumn(comment = "商户编码")
private String mchCode;
/** 商户应用编码 */
@TableField(updateStrategy = FieldStrategy.NEVER)
@DbColumn(comment = "商户应用编码")
private String mchAppCode;
/** 关联用户id */
@DbMySqlIndex
@TableField(updateStrategy = FieldStrategy.NEVER)
@DbColumn(comment = "关联用户id")
private Long userId;

View File

@@ -3,7 +3,10 @@ package cn.bootx.platform.daxpay.core.channel.wallet.entity;
import cn.bootx.mybatis.table.modify.annotation.DbColumn;
import cn.bootx.mybatis.table.modify.annotation.DbTable;
import cn.bootx.mybatis.table.modify.mybatis.mysq.annotation.DbMySqlIndex;
import cn.bootx.platform.common.core.function.EntityBaseFunction;
import cn.bootx.platform.common.mybatisplus.base.MpBaseEntity;
import cn.bootx.platform.daxpay.core.channel.wallet.convert.WalletConvert;
import cn.bootx.platform.daxpay.dto.channel.wallet.WalletConfigDto;
import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
@@ -23,7 +26,7 @@ import java.math.BigDecimal;
@DbTable(comment = "钱包配置")
@Accessors(chain = true)
@TableName("pay_wallet")
public class WalletConfig extends MpBaseEntity {
public class WalletConfig extends MpBaseEntity implements EntityBaseFunction<WalletConfigDto> {
/** 商户编码 */
@TableField(updateStrategy = FieldStrategy.NEVER)
@@ -40,4 +43,11 @@ public class WalletConfig extends MpBaseEntity {
@DbColumn(comment = "默认余额")
private BigDecimal defaultBalance;
/**
* 转换
*/
@Override
public WalletConfigDto toDto() {
return WalletConvert.CONVERT.convert(this);
}
}

View File

@@ -1,6 +1,14 @@
package cn.bootx.platform.daxpay.core.channel.wallet.service;
import cn.bootx.platform.common.core.exception.BizException;
import cn.bootx.platform.common.core.exception.DataNotExistException;
import cn.bootx.platform.daxpay.core.channel.wallet.convert.WalletConvert;
import cn.bootx.platform.daxpay.core.channel.wallet.dao.WalletConfigManager;
import cn.bootx.platform.daxpay.core.channel.wallet.entity.WalletConfig;
import cn.bootx.platform.daxpay.core.merchant.service.MchAppService;
import cn.bootx.platform.daxpay.dto.channel.wallet.WalletConfigDto;
import cn.bootx.platform.daxpay.param.channel.wechat.WalletConfigParam;
import cn.hutool.core.bean.BeanUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -15,6 +23,34 @@ import org.springframework.stereotype.Service;
@RequiredArgsConstructor
public class WalletConfigService {
private final WalletConfigManager walletConfigManager;
private final MchAppService mchAppService;
/**
* 根据应用编码获取钱包配置
*/
public WalletConfigDto findByMchCode(String mchCode){
return walletConfigManager.findByMchCode(mchCode)
.map(WalletConfig::toDto)
.orElse(new WalletConfigDto());
}
/**
* 新增或更新
*/
public void add(WalletConfigParam param){
// 是否有管理关系判断
if (!mchAppService.checkMatch(param.getMchCode(), param.getMchAppCode())) {
throw new BizException("应用信息与商户信息不匹配");
}
WalletConfig walletConfig = WalletConvert.CONVERT.convert(param);
walletConfigManager.save(walletConfig);
}
public void update(WalletConfigParam param){
WalletConfig walletConfig = walletConfigManager.findById(param.getId())
.orElseThrow(DataNotExistException::new);
BeanUtil.copyProperties(param,walletConfig);
walletConfigManager.updateById(walletConfig);
}
}

View File

@@ -44,13 +44,6 @@ public class WalletQueryService {
}
/**
* 根据用户ID查询钱包
*/
public WalletDto findByUserId(Long userId) {
return walletManager.findByUser(userId).map(Wallet::toDto).orElseThrow(DataNotExistException::new);
}
/**
* 根据用户ID查询钱包
*/
@@ -81,8 +74,8 @@ public class WalletQueryService {
/**
* 待开通钱包的用户列表
*/
public PageResult<UserInfoDto> pageByNotWallet(PageParam pageParam, UserInfoParam userInfoParam) {
return MpUtil.convert2DtoPageResult(walletManager.pageByNotWallet(pageParam, userInfoParam));
public PageResult<UserInfoDto> pageByNotWallet(PageParam pageParam,String mchCode, UserInfoParam userInfoParam) {
return MpUtil.convert2DtoPageResult(walletManager.pageByNotWallet(pageParam,mchCode ,userInfoParam));
}

View File

@@ -4,6 +4,7 @@ import cn.bootx.platform.common.core.exception.BizException;
import cn.bootx.platform.common.core.exception.DataNotExistException;
import cn.bootx.platform.common.core.util.BigDecimalUtil;
import cn.bootx.platform.daxpay.code.paymodel.WalletCode;
import cn.bootx.platform.daxpay.core.channel.wallet.dao.WalletConfigManager;
import cn.bootx.platform.daxpay.core.channel.wallet.dao.WalletLogManager;
import cn.bootx.platform.daxpay.core.channel.wallet.dao.WalletManager;
import cn.bootx.platform.daxpay.core.channel.wallet.entity.Wallet;
@@ -30,6 +31,8 @@ public class WalletService {
private final WalletManager walletManager;
private final WalletConfigManager walletConfigManager;
private final WalletLogManager walletLogManager;
/**
@@ -37,6 +40,7 @@ public class WalletService {
*/
@Transactional(rollbackFor = Exception.class)
public void createWallet(Long userId) {
// 判断钱包是否已开通
if (walletManager.existsByUser(userId)) {
throw new BizException("钱包已经开通");

View File

@@ -0,0 +1,31 @@
package cn.bootx.platform.daxpay.dto.channel.wallet;
import cn.bootx.platform.common.core.rest.dto.BaseDto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.math.BigDecimal;
/**
*
* @author xxm
* @since 2023/7/20
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@Schema(title = "钱包配置")
public class WalletConfigDto extends BaseDto {
@Schema(description = "商户编码")
private String mchCode;
@Schema(description = "商户应用编码")
private String mchAppCode;
@Schema(description = "默认余额")
private BigDecimal defaultBalance;
}

View File

@@ -25,6 +25,12 @@ public class WalletLogQueryParam implements Serializable {
@Schema(description = "用户ID (钱包至少存在一个)")
private Long userId;
@Schema(description = "商户编码")
private String mchCode;
@Schema(description = "商户应用编码")
private String mchAppCode;
@Schema(description = "开始日期")
private LocalDateTime startDate;

View File

@@ -0,0 +1,31 @@
package cn.bootx.platform.daxpay.param.channel.wechat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.math.BigDecimal;
/**
* 钱包配置
* @author xxm
* @since 2023/7/20
*/
@Data
@Accessors(chain = true)
@Schema(title = "钱包配置")
public class WalletConfigParam {
@Schema(description = "主键")
private Long id;
@Schema(description = "商户编码")
private String mchCode;
@Schema(description = "商户应用编码")
private String mchAppCode;
@Schema(description = "默认余额")
private BigDecimal defaultBalance;
}