feat 分账接收处理

This commit is contained in:
xxm1995
2024-04-01 23:27:49 +08:00
parent 6334461971
commit 12fb44a241
32 changed files with 1082 additions and 36 deletions

View File

@@ -1,8 +1,18 @@
package cn.bootx.platform.daxpay.admin.controller.allocation;
import cn.bootx.platform.common.core.rest.PageResult;
import cn.bootx.platform.common.core.rest.Res;
import cn.bootx.platform.common.core.rest.ResResult;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.daxpay.service.core.payment.allocation.service.AllocationReceiverService;
import cn.bootx.platform.daxpay.service.dto.allocation.AllocationReceiverDto;
import cn.bootx.platform.daxpay.service.param.allocation.AllocationReceiverParam;
import cn.bootx.platform.daxpay.service.param.allocation.AllocationReceiverQuery;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -17,5 +27,46 @@ import org.springframework.web.bind.annotation.RestController;
@RequiredArgsConstructor
public class AllocationReceiverController {
private final AllocationReceiverService allocationReceiverService;
private final AllocationReceiverService receiverService;
@Operation(summary = "分页")
@GetMapping("/page")
public ResResult<PageResult<AllocationReceiverDto>> page(PageParam pageParam, AllocationReceiverQuery query){
return Res.ok(receiverService.page(pageParam, query));
}
@Operation(summary = "查询详情")
@GetMapping("/findById")
public ResResult<AllocationReceiverDto> findById(Long id){
return Res.ok(receiverService.findById(id));
}
@Operation(summary = "新增")
@PostMapping("")
public ResResult<Void> add(AllocationReceiverParam param){
receiverService.add(param);
return Res.ok();
}
@Operation(summary = "修改")
@PostMapping("update")
public ResResult<Void> update(AllocationReceiverParam param){
receiverService.update(param);
return Res.ok();
}
@Operation(summary = "同步到三方支付系统中")
@PostMapping("registerByGateway")
public ResResult<Void> registerByGateway(Long id){
receiverService.registerByGateway(id);
return Res.ok();
}
@Operation(summary = "从三方支付系统中删除")
@PostMapping("removeByGateway")
public ResResult<Void> removeByGateway(Long id){
receiverService.removeByGateway(id);
return Res.ok();
}
}

View File

@@ -0,0 +1,29 @@
package cn.bootx.platform.daxpay.code;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 分账接收方类型枚举
* @author xxm
* @since 2024/4/1
*/
@Getter
@AllArgsConstructor
public enum AllocationReceiverTypeEnum {
/** 个人 */
WX_PERSONAL("PERSONAL_OPENID", "个人"),
/** 商户 */
WX_MERCHANT("MERCHANT_ID", "商户"),
/** userId 以2088开头的纯16位数字 */
ALI_USER_ID("userId", "账号ID"),
/** openId */
ALI_OPEN_ID("openId", "登录号"),
/** 账号 */
ALI_LOGIN_NAME("loginName", "openId");
private final String code;
private final String name;
}

View File

@@ -1,9 +1,28 @@
package cn.bootx.platform.daxpay.code;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 分账关系类型
* @author xxm
* @since 2024/3/27
*/
@Getter
@AllArgsConstructor
public enum AllocationRelationTypeEnum {
SERVICE_PROVIDER("SERVICE_PROVIDER","服务商"),
STORE("STORE","门店"),
STAFF("STAFF","员工"),
STORE_OWNER("STORE_OWNER","店主"),
PARTNER("PARTNER","合作伙伴"),
HEADQUARTER("HEADQUARTER","总部"),
BRAND("BRAND","品牌方"),
DISTRIBUTOR("DISTRIBUTOR","分销商"),
USER("USER","用户"),
SUPPLIER("SUPPLIER","供应商"),
CUSTOM("CUSTOM","自定义");
private final String code;
private final String name;
}

View File

@@ -140,4 +140,10 @@ public interface WeChatPayCode {
/** 资金账单 - 基本账户 */
String ACCOUNT_TYPE_BASIC = "Basic";
/* 分账 */
/** 商户号 */
String MERCHANT_ID = "MERCHANT_ID";
/** 个人openid */
String PERSONAL_OPENID = "PERSONAL_OPENID";
}

View File

@@ -2,15 +2,21 @@ package cn.bootx.platform.daxpay.service.core.channel.alipay.service;
import cn.bootx.platform.daxpay.service.core.payment.allocation.entity.AllocationReceiver;
import com.alipay.api.domain.AlipayTradeRoyaltyRelationBindModel;
import com.alipay.api.domain.AlipayTradeRoyaltyRelationUnbindModel;
import com.alipay.api.domain.RoyaltyEntity;
import com.alipay.api.response.AlipayTradeRoyaltyRelationBindResponse;
import com.alipay.api.response.AlipayTradeRoyaltyRelationUnbindResponse;
import com.ijpay.alipay.AliPayApi;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static cn.bootx.platform.daxpay.code.AllocationReceiverTypeEnum.*;
/**
* 支付宝分账
@@ -23,18 +29,49 @@ import java.util.Collections;
public class AliPayAllocationReceiverService {
/**
* 注册
* 校验
*/
public boolean validation(AllocationReceiver allocationReceiver){
List<String> list = Arrays.asList(ALI_USER_ID.getCode(), ALI_OPEN_ID.getCode(), ALI_LOGIN_NAME.getCode());
String receiverType = allocationReceiver.getReceiverType();
return list.contains(receiverType);
}
/**
* 绑定关系
*/
@SneakyThrows
public void bind(AllocationReceiver allocationReceiver){
AlipayTradeRoyaltyRelationBindModel model = new AlipayTradeRoyaltyRelationBindModel();
model.setOutRequestNo(String.valueOf(allocationReceiver.getId()));
// 分账接收方方类型。
RoyaltyEntity entity = new RoyaltyEntity();
entity.setType(allocationReceiver.getReceiverType());
entity.setAccount(allocationReceiver.getReceiverAccount());
entity.setMemo(allocationReceiver.getRelationName());
model.setReceiverList(Collections.singletonList(entity));
AlipayTradeRoyaltyRelationBindResponse response = AliPayApi.tradeRoyaltyRelationBind(model);
// 如果返回成功或者已经绑定, 关系
}
/**
* 解绑关系
*/
@SneakyThrows
public void unbind(AllocationReceiver allocationReceiver){
AlipayTradeRoyaltyRelationUnbindModel model = new AlipayTradeRoyaltyRelationUnbindModel();
model.setOutRequestNo(String.valueOf(allocationReceiver.getId()));
RoyaltyEntity entity = new RoyaltyEntity();
entity.setAccount(allocationReceiver.getReceiverAccount());
entity.setMemo(allocationReceiver.getRelationName());
model.setReceiverList(Collections.singletonList(entity));
AlipayTradeRoyaltyRelationBindResponse response = AliPayApi.tradeRoyaltyRelationBind(model);
AlipayTradeRoyaltyRelationUnbindResponse response = AliPayApi.tradeRoyaltyRelationUnBind(model);
}
}

View File

@@ -0,0 +1,91 @@
package cn.bootx.platform.daxpay.service.core.channel.wechat.service;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.service.code.WeChatPayCode;
import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WeChatPayConfig;
import cn.bootx.platform.daxpay.service.core.payment.allocation.entity.AllocationReceiver;
import cn.hutool.core.util.StrUtil;
import com.ijpay.core.enums.SignType;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.model.ReceiverModel;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import static cn.bootx.platform.daxpay.code.AllocationReceiverTypeEnum.WX_MERCHANT;
/**
*
* @author xxm
* @since 2024/4/1
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class WeChatPayAllocationReceiverService {
/**
* 校验参数是否合法
*/
public boolean validation(AllocationReceiver allocationReceiver){
List<String> list = Arrays.asList(WX_MERCHANT.getCode(), WX_MERCHANT.getCode());
String receiverType = allocationReceiver.getReceiverType();
return !list.contains(receiverType);
}
/**
* 绑定
*/
public void bind(AllocationReceiver allocationReceiver, WeChatPayConfig weChatPayConfig){
Map<String, String> param = ReceiverModel.builder()
.type(allocationReceiver.getReceiverType())
.account(allocationReceiver.getReceiverAccount())
.name(allocationReceiver.getReceiverName())
.relation_type(allocationReceiver.getRelationType())
.custom_relation(allocationReceiver.getRelationType())
.custom_relation(allocationReceiver.getRelationName())
.build()
.createSign(weChatPayConfig.getApiKeyV2(), SignType.HMACSHA256);
String xmlResult = WxPayApi.profitSharingAddReceiver(param);
Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
this.verifyErrorMsg(result);
}
/**
* 解除绑定
*/
public void unbind(AllocationReceiver allocationReceiver, WeChatPayConfig weChatPayConfig){
Map<String, String> param = ReceiverModel.builder()
.type(allocationReceiver.getReceiverType())
.account(allocationReceiver.getReceiverAccount())
.build()
.createSign(weChatPayConfig.getApiKeyV2(), SignType.HMACSHA256);
String xmlResult = WxPayApi.profitSharingRemoveReceiver(param);
Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
this.verifyErrorMsg(result);
}
/**
* 验证错误信息
*/
private void verifyErrorMsg(Map<String, String> result) {
String returnCode = result.get(WeChatPayCode.RETURN_CODE);
String resultCode = result.get(WeChatPayCode.RESULT_CODE);
if (!WxPayKit.codeIsOk(returnCode) || !WxPayKit.codeIsOk(resultCode)) {
String errorMsg = result.get(WeChatPayCode.ERR_CODE_DES);
if (StrUtil.isBlank(errorMsg)) {
errorMsg = result.get(WeChatPayCode.RETURN_MSG);
}
log.error("支付失败 {}", errorMsg);
throw new PayFailureException(errorMsg);
}
}
}

View File

@@ -0,0 +1,21 @@
package cn.bootx.platform.daxpay.service.core.payment.allocation.convert;
import cn.bootx.platform.daxpay.service.core.payment.allocation.entity.AllocationGroup;
import cn.bootx.platform.daxpay.service.dto.allocation.AllocationGroupDto;
import cn.bootx.platform.daxpay.service.param.allocation.AllocationGroupParam;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
*
* @author xxm
* @since 2024/4/1
*/
@Mapper
public interface AllocationGroupConvert {
AllocationGroupConvert CONVERT = Mappers.getMapper(AllocationGroupConvert.class);
AllocationGroupDto convert(AllocationGroup in);
AllocationGroup convert(AllocationGroupParam in);
}

View File

@@ -0,0 +1,21 @@
package cn.bootx.platform.daxpay.service.core.payment.allocation.convert;
import cn.bootx.platform.daxpay.service.core.payment.allocation.entity.AllocationGroupReceiver;
import cn.bootx.platform.daxpay.service.dto.allocation.AllocationGroupReceiverDto;
import cn.bootx.platform.daxpay.service.param.allocation.AllocationGroupReceiverParam;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
*
* @author xxm
* @since 2024/4/1
*/
@Mapper
public interface AllocationGroupReceiverConvert {
AllocationGroupReceiverConvert CONVERT = Mappers.getMapper(AllocationGroupReceiverConvert.class);
AllocationGroupReceiverDto convert(AllocationGroupReceiver in);
AllocationGroupReceiver convert(AllocationGroupReceiverParam in);
}

View File

@@ -1,18 +1,25 @@
package cn.bootx.platform.daxpay.service.core.payment.allocation.dao;
import cn.bootx.platform.common.mybatisplus.impl.BaseManager;
import cn.bootx.platform.daxpay.service.core.payment.allocation.entity.AllocationReceiverGroup;
import cn.bootx.platform.daxpay.service.core.payment.allocation.entity.AllocationGroupReceiver;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
/**
*
* 分账组关联接收方
* @author xxm
* @since 2024/3/27
*/
@Slf4j
@Repository
@RequiredArgsConstructor
public class AllocationReceiverGroupManager extends BaseManager<AllocationReceiverGroupMapper, AllocationReceiverGroup> {
public class AllocationGroupReceiverManager extends BaseManager<AllocationGroupReceiverMapper, AllocationGroupReceiver> {
/**
* 判断接收者是否已经被使用
*/
public boolean isUsed(Long receiverId){
return existedByField(AllocationGroupReceiver::getReceiverId, receiverId);
}
}

View File

@@ -1,6 +1,6 @@
package cn.bootx.platform.daxpay.service.core.payment.allocation.dao;
import cn.bootx.platform.daxpay.service.core.payment.allocation.entity.AllocationReceiverGroup;
import cn.bootx.platform.daxpay.service.core.payment.allocation.entity.AllocationGroupReceiver;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@@ -10,5 +10,5 @@ import org.apache.ibatis.annotations.Mapper;
* @since 2024/3/27
*/
@Mapper
public interface AllocationReceiverGroupMapper extends BaseMapper<AllocationReceiverGroup> {
public interface AllocationGroupReceiverMapper extends BaseMapper<AllocationGroupReceiver> {
}

View File

@@ -1,13 +1,19 @@
package cn.bootx.platform.daxpay.service.core.payment.allocation.dao;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.common.mybatisplus.impl.BaseManager;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.bootx.platform.common.query.generator.QueryGenerator;
import cn.bootx.platform.daxpay.service.core.payment.allocation.entity.AllocationReceiver;
import cn.bootx.platform.daxpay.service.param.allocation.AllocationReceiverQuery;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
/**
*
* 分账接收方
* @author xxm
* @since 2024/3/27
*/
@@ -15,4 +21,14 @@ import org.springframework.stereotype.Repository;
@Repository
@RequiredArgsConstructor
public class AllocationReceiverManager extends BaseManager<AllocationReceiverMapper,AllocationReceiver> {
/**
* 分页
*/
public Page<AllocationReceiver> page(PageParam pageParam, AllocationReceiverQuery param){
Page<AllocationReceiver> mpPage = MpUtil.getMpPage(pageParam, AllocationReceiver.class);
QueryWrapper<AllocationReceiver> generator = QueryGenerator.generator(param);
return this.page(mpPage, generator);
}
}

View File

@@ -1,8 +1,13 @@
package cn.bootx.platform.daxpay.service.core.payment.allocation.entity;
import cn.bootx.platform.common.core.function.EntityBaseFunction;
import cn.bootx.platform.common.mybatisplus.base.MpBaseEntity;
import cn.bootx.platform.daxpay.service.core.payment.allocation.convert.AllocationGroupConvert;
import cn.bootx.platform.daxpay.service.dto.allocation.AllocationGroupDto;
import cn.bootx.table.modify.annotation.DbColumn;
import cn.bootx.table.modify.annotation.DbComment;
import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -17,14 +22,26 @@ import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
@TableName("pay_allocation_receiver_group")
public class AllocationGroup extends MpBaseEntity {
public class AllocationGroup extends MpBaseEntity implements EntityBaseFunction<AllocationGroupDto> {
@DbComment("名称")
private String name;
@DbColumn("通道")
@DbColumn(comment = "通道")
@TableField(updateStrategy = FieldStrategy.NEVER)
private String channel;
@DbColumn("备注")
@DbColumn(comment = "总分账比例(万分之多少)")
private Integer totalRate;
@DbColumn(comment = "备注")
private String remark;
/**
* 转换
*/
@Override
public AllocationGroupDto toDto() {
return AllocationGroupConvert.CONVERT.convert(this);
}
}

View File

@@ -14,11 +14,14 @@ import lombok.experimental.Accessors;
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
public class AllocationReceiverGroup extends MpCreateEntity {
public class AllocationGroupReceiver extends MpCreateEntity {
@DbColumn("分账ID")
@DbColumn(comment = "分账ID")
private Long groupId;
@DbColumn("接收者ID")
@DbColumn(comment = "接收者ID")
private Long receiverId;
@DbColumn(comment = "分账比例(万分之多少)")
private Integer rate;
}

View File

@@ -2,6 +2,7 @@ package cn.bootx.platform.daxpay.service.core.payment.allocation.entity;
import cn.bootx.platform.common.core.function.EntityBaseFunction;
import cn.bootx.platform.common.mybatisplus.base.MpBaseEntity;
import cn.bootx.platform.daxpay.code.AllocationReceiverTypeEnum;
import cn.bootx.platform.daxpay.code.AllocationRelationTypeEnum;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.service.core.payment.allocation.convert.AllocationReceiverConvert;
@@ -34,7 +35,8 @@ public class AllocationReceiver extends MpBaseEntity implements EntityBaseFuncti
private String channel;
/**
* 分账接收方类型 个人/商户
* 分账接收方类型
* @see AllocationReceiverTypeEnum
*/
@DbColumn(comment = "分账接收方类型")
@TableField(updateStrategy = FieldStrategy.NEVER)
@@ -45,9 +47,6 @@ public class AllocationReceiver extends MpBaseEntity implements EntityBaseFuncti
@TableField(updateStrategy = FieldStrategy.NEVER)
private String receiverAccount;
@DbColumn(comment = "账号类型")
private String accountType;
/** 接收方姓名 */
@DbColumn(comment = "接收方姓名")
@TableField(updateStrategy = FieldStrategy.NEVER)

View File

@@ -0,0 +1,38 @@
package cn.bootx.platform.daxpay.service.core.payment.allocation.factory;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.exception.pay.PayUnsupportedMethodException;
import cn.bootx.platform.daxpay.service.core.payment.allocation.strategy.receiver.AliPayAllocationReceiverStrategy;
import cn.bootx.platform.daxpay.service.core.payment.allocation.strategy.receiver.WeChatPayAllocationReceiverStrategy;
import cn.bootx.platform.daxpay.service.func.AbsAllocationReceiverStrategy;
import cn.hutool.extra.spring.SpringUtil;
import lombok.experimental.UtilityClass;
/**
* 分账接收方策略工厂
* @author xxm
* @since 2024/4/1
*/
@UtilityClass
public class AllocationReceiverFactory {
/**
* 根据传入的支付通道创建策略
* @return 支付策略
*/
public static AbsAllocationReceiverStrategy create(PayChannelEnum channelEnum) {
AbsAllocationReceiverStrategy strategy;
switch (channelEnum) {
case ALI:
strategy = SpringUtil.getBean(AliPayAllocationReceiverStrategy.class);
break;
case WECHAT:
strategy = SpringUtil.getBean(WeChatPayAllocationReceiverStrategy.class);
break;
default:
throw new PayUnsupportedMethodException();
}
return strategy;
}
}

View File

@@ -0,0 +1,161 @@
package cn.bootx.platform.daxpay.service.core.payment.allocation.service;
import cn.bootx.platform.common.core.exception.DataNotExistException;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.service.core.payment.allocation.convert.AllocationGroupConvert;
import cn.bootx.platform.daxpay.service.core.payment.allocation.dao.AllocationGroupManager;
import cn.bootx.platform.daxpay.service.core.payment.allocation.dao.AllocationGroupReceiverManager;
import cn.bootx.platform.daxpay.service.core.payment.allocation.dao.AllocationReceiverManager;
import cn.bootx.platform.daxpay.service.core.payment.allocation.entity.AllocationGroup;
import cn.bootx.platform.daxpay.service.core.payment.allocation.entity.AllocationGroupReceiver;
import cn.bootx.platform.daxpay.service.core.payment.allocation.entity.AllocationReceiver;
import cn.bootx.platform.daxpay.service.param.allocation.AllocationGroupBindParam;
import cn.bootx.platform.daxpay.service.param.allocation.AllocationGroupParam;
import cn.bootx.platform.daxpay.service.param.allocation.AllocationGroupReceiverParam;
import cn.bootx.platform.daxpay.service.param.allocation.AllocationGroupUnbindParam;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
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.stream.Collectors;
/**
* 分账组服务
* @author xxm
* @since 2024/4/1
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class AllocationGroupService {
private final AllocationGroupManager groupManager;
private final AllocationGroupReceiverManager groupReceiverManager;
private final AllocationReceiverManager receiverManager;
/**
* 创建分账组
*/
public void create(AllocationGroupParam param){
PayChannelEnum.findByCode(param.getChannel());
AllocationGroup allocation = AllocationGroupConvert.CONVERT.convert(param);
groupManager.save(allocation);
}
/**
* 更新分账组
*/
public void update(AllocationGroupParam param){
AllocationGroup group = groupManager.findById(param.getId())
.orElseThrow(() -> new DataNotExistException("未找到分账组"));
BeanUtil.copyProperties(param,group, CopyOptions.create().ignoreNullValue());
group.setTotalRate(0);
groupManager.updateById(group);
}
/**
* 删除分账组
*/
public void delete(){
}
/**
* 绑定分账接收方
*/
@Transactional(rollbackFor = Exception.class)
public void bind(AllocationGroupBindParam param) {
// 分账组
AllocationGroup group = groupManager.findById(param.getGroupId())
.orElseThrow(() -> new DataNotExistException("未找到分账组"));
// 查询接收方
List<AllocationGroupReceiverParam> receiverParams = param.getReceivers();
List<Long> receiverIds = receiverParams.stream()
.map(AllocationGroupReceiverParam::getReceiverId)
.collect(Collectors.toList());
List<AllocationReceiver> receivers = receiverManager.findAllByIds(receiverIds);
if (receivers.size() != receiverIds.size()){
throw new DataNotExistException("传入的分账接收房数量与查询到的不一致");
}
// 接收方需要已经同步到三方值系统中
receivers.stream()
.filter(receiver -> !receiver.isSync())
.findAny()
.ifPresent(receiver -> {
throw new DataNotExistException("接收方未同步到三方值系统中");
});
// 保存分账接收者
List<AllocationGroupReceiver> groupReceivers = receivers.stream()
.map(receiver -> new AllocationGroupReceiver().setGroupId(group.getId())
.setReceiverId(receiver.getId())
.setRate(receiverParams.get(receivers.indexOf(receiver))
.getRate()))
.collect(Collectors.toList());
groupReceiverManager.saveAll(groupReceivers);
// 计算分账比例
int sum = receiverParams.stream()
.mapToInt(AllocationGroupReceiverParam::getRate)
.sum();
group.setTotalRate(group.getTotalRate() + sum);
groupManager.updateById(group);
}
/**
* 批量删除分账接收方
*/
@Transactional(rollbackFor = Exception.class)
public void removeReceivers(AllocationGroupUnbindParam param){
// 分账组
AllocationGroup group = groupManager.findById(param.getGroupId())
.orElseThrow(() -> new DataNotExistException("未找到分账组"));
// 删除接收方
List<AllocationGroupReceiver> receivers = groupReceiverManager.findAllByIds(param.getReceiverIds());
if (receivers.size() != param.getReceiverIds().size()){
throw new DataNotExistException("传入的分账接收房数量与查询到的不一致");
}
groupReceiverManager.deleteByIds(param.getReceiverIds());
// 计算分账比例
int sum = receivers.stream()
.mapToInt(AllocationGroupReceiver::getRate)
.sum();
group.setTotalRate(group.getTotalRate() - sum);
groupManager.updateById(group);
}
/**
* 删除单个分账接收方
*/
@Transactional
public void removeReceiver(Long receiverId){
AllocationGroupReceiver groupReceiver = groupReceiverManager.findById(receiverId)
.orElseThrow(() -> new DataNotExistException("未找到分账接收方"));
AllocationGroup group = groupManager.findById(groupReceiver.getGroupId())
.orElseThrow(() -> new DataNotExistException("未找到分账组"));
// 更新分账比例
group.setTotalRate(group.getTotalRate() - groupReceiver.getRate());
// 更新接收比例
groupReceiverManager.updateById(groupReceiver);
groupManager.deleteById(group);
}
/**
* 修改分账比例
*/
public void updateRate(Long receiverId, Integer rate){
AllocationGroupReceiver groupReceiver = groupReceiverManager.findById(receiverId)
.orElseThrow(() -> new DataNotExistException("未找到分账接收方"));
AllocationGroup group = groupManager.findById(groupReceiver.getGroupId())
.orElseThrow(() -> new DataNotExistException("未找到分账组"));
// 更新分账比例
group.setTotalRate(group.getTotalRate() - groupReceiver.getRate() + rate);
// 更新接收比例
groupReceiver.setRate(rate);
groupReceiverManager.updateById(groupReceiver);
groupManager.updateById(group);
}
}

View File

@@ -1,9 +1,20 @@
package cn.bootx.platform.daxpay.service.core.payment.allocation.service;
import cn.bootx.platform.common.core.exception.DataNotExistException;
import cn.bootx.platform.common.core.rest.PageResult;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.service.core.payment.allocation.convert.AllocationReceiverConvert;
import cn.bootx.platform.daxpay.service.core.payment.allocation.dao.AllocationGroupReceiverManager;
import cn.bootx.platform.daxpay.service.core.payment.allocation.dao.AllocationReceiverManager;
import cn.bootx.platform.daxpay.service.core.payment.allocation.entity.AllocationReceiver;
import cn.bootx.platform.daxpay.service.core.payment.allocation.factory.AllocationReceiverFactory;
import cn.bootx.platform.daxpay.service.dto.allocation.AllocationReceiverDto;
import cn.bootx.platform.daxpay.service.func.AbsAllocationReceiverStrategy;
import cn.bootx.platform.daxpay.service.param.allocation.AllocationReceiverParam;
import cn.bootx.platform.daxpay.service.param.allocation.AllocationReceiverQuery;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -20,20 +31,46 @@ public class AllocationReceiverService {
private final AllocationReceiverManager manager;
private final AllocationGroupReceiverManager groupReceiverManager;
/**
* 分页
*/
public PageResult<AllocationReceiverDto> page(PageParam pageParam, AllocationReceiverQuery query){
return MpUtil.convert2DtoPageResult(manager.page(pageParam, query));
}
/**
* 查询详情
*/
public AllocationReceiverDto findById(Long id){
return manager.findById(id).map(AllocationReceiver::toDto).orElseThrow(() -> new DataNotExistException("分账接收方不存在"));
}
/**
* 添加分账接收方
*/
public void add(AllocationReceiverParam param){
// 首先添加网关的分账接收方
AllocationReceiver receiver = AllocationReceiverConvert.CONVERT.convert(param);
// 获取策略
PayChannelEnum channelEnum = PayChannelEnum.findByCode(param.getChannel());
AbsAllocationReceiverStrategy receiverStrategy = AllocationReceiverFactory.create(channelEnum);
// 校验
receiverStrategy.setAllocationReceiver(receiver);
receiverStrategy.validation();
receiver.setSync(false);
manager.save(receiver);
}
/**
* 修改
* 修改信息
*/
public void update(AllocationReceiverParam param){
// 未同步状态可以修改
}
@@ -41,15 +78,25 @@ public class AllocationReceiverService {
* 删除分账接收方
*/
public void remove(Long id){
// 首先删除网关的分账接收方
// 然后删除本地数据
}
/**
* 同步到三方支付系统中
*/
public void registerByGateway(Long id){
AllocationReceiver receiver = manager.findById(id).orElseThrow(() -> new PayFailureException("未找到分账接收方"));
// 获取策略
PayChannelEnum channelEnum = PayChannelEnum.findByCode(receiver.getChannel());
AbsAllocationReceiverStrategy receiverStrategy = AllocationReceiverFactory.create(channelEnum);
// 校验
receiverStrategy.setAllocationReceiver(receiver);
receiverStrategy.validation();
receiverStrategy.doBeforeHandler();
receiverStrategy.bind();
receiver.setSync(true);
manager.updateById(receiver);
}
@@ -57,7 +104,20 @@ public class AllocationReceiverService {
* 从三方支付系统中删除
*/
public void removeByGateway(Long id){
if (groupReceiverManager.isUsed(id)){
throw new PayFailureException("该接收方已被使用,无法删除");
}
AllocationReceiver receiver = manager.findById(id).orElseThrow(() -> new PayFailureException("未找到分账接收方"));
// 获取策略
PayChannelEnum channelEnum = PayChannelEnum.findByCode(receiver.getChannel());
AbsAllocationReceiverStrategy receiverStrategy = AllocationReceiverFactory.create(channelEnum);
// 校验
receiverStrategy.setAllocationReceiver(receiver);
receiverStrategy.validation();
receiverStrategy.doBeforeHandler();
receiverStrategy.unbind();
receiver.setSync(false);
manager.updateById(receiver);
}
}

View File

@@ -0,0 +1,20 @@
package cn.bootx.platform.daxpay.service.core.payment.allocation.strategy.allocation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
/**
* 支付宝分账策略
* @author xxm
* @since 2024/4/1
*/
@Slf4j
@Service
@Scope(SCOPE_PROTOTYPE)
@RequiredArgsConstructor
public class AliPayAllocationStrategy {
}

View File

@@ -0,0 +1,20 @@
package cn.bootx.platform.daxpay.service.core.payment.allocation.strategy.allocation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
/**
* 微信支付分账策略
* @author xxm
* @since 2024/4/1
*/
@Slf4j
@Service
@Scope(SCOPE_PROTOTYPE)
@RequiredArgsConstructor
public class WeChatPayAllocationStrategy {
}

View File

@@ -0,0 +1,84 @@
package cn.bootx.platform.daxpay.service.core.payment.allocation.strategy.receiver;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.service.core.channel.alipay.entity.AliPayConfig;
import cn.bootx.platform.daxpay.service.core.channel.alipay.service.AliPayAllocationReceiverService;
import cn.bootx.platform.daxpay.service.core.channel.alipay.service.AliPayConfigService;
import cn.bootx.platform.daxpay.service.func.AbsAllocationReceiverStrategy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import java.util.Objects;
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
/**
* 支付宝分账接收者策略
* @author xxm
* @since 2024/4/1
*/
@Slf4j
@Service
@Scope(SCOPE_PROTOTYPE)
@RequiredArgsConstructor
public class AliPayAllocationReceiverStrategy extends AbsAllocationReceiverStrategy {
private final AliPayAllocationReceiverService receiverService;
private final AliPayConfigService payConfigService;
private AliPayConfig aliPayConfig;
/**
* 策略标识
*/
@Override
public PayChannelEnum getChannel() {
return PayChannelEnum.WECHAT;
}
/**
* 校验方法
*/
@Override
public boolean validation(){
return receiverService.validation(this.getAllocationReceiver());
}
/**
* 分账前处理
*/
@Override
public void doBeforeHandler() {
this.aliPayConfig = payConfigService.getAndCheckConfig();
// 判断是否支持分账
if (Objects.equals(aliPayConfig.getAllocation(),false)){
throw new PayFailureException("微信支付配置不支持分账");
}
}
/**
* 添加到支付系统中
*/
@Override
public void bind() {
if (!receiverService.validation(this.getAllocationReceiver())){
throw new PayFailureException("分账接收者参数未通过校验");
}
receiverService.bind(this.getAllocationReceiver());
}
/**
* 从三方支付系统中删除
*/
@Override
public void unbind() {
if (!receiverService.validation(this.getAllocationReceiver())){
throw new PayFailureException("分账参数未通过校验");
}
receiverService.unbind(this.getAllocationReceiver());
}
}

View File

@@ -0,0 +1,84 @@
package cn.bootx.platform.daxpay.service.core.payment.allocation.strategy.receiver;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WeChatPayConfig;
import cn.bootx.platform.daxpay.service.core.channel.wechat.service.WeChatPayAllocationReceiverService;
import cn.bootx.platform.daxpay.service.core.channel.wechat.service.WeChatPayConfigService;
import cn.bootx.platform.daxpay.service.func.AbsAllocationReceiverStrategy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import java.util.Objects;
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
/**
* 微信分账接收策略
* @author xxm
* @since 2024/4/1
*/
@Slf4j
@Service
@Scope(SCOPE_PROTOTYPE)
@RequiredArgsConstructor
public class WeChatPayAllocationReceiverStrategy extends AbsAllocationReceiverStrategy {
private final WeChatPayAllocationReceiverService receiverService;
private final WeChatPayConfigService payConfigService;
private WeChatPayConfig weChatPayConfig;
/**
* 策略标识
*/
@Override
public PayChannelEnum getChannel() {
return PayChannelEnum.WECHAT;
}
/**
* 校验方法
*/
@Override
public boolean validation(){
return receiverService.validation(this.getAllocationReceiver());
}
/**
* 分账前处理
*/
@Override
public void doBeforeHandler() {
this.weChatPayConfig = payConfigService.getAndCheckConfig();
// 判断是否支持分账
if (Objects.equals(weChatPayConfig.getAllocation(),false)){
throw new PayFailureException("微信支付配置不支持分账");
}
}
/**
* 添加到支付系统中
*/
@Override
public void bind() {
if (!receiverService.validation(this.getAllocationReceiver())){
throw new PayFailureException("分账接收者参数未通过校验");
}
receiverService.bind(this.getAllocationReceiver(),this.weChatPayConfig);
}
/**
* 从三方支付系统中删除
*/
@Override
public void unbind() {
if (!receiverService.validation(this.getAllocationReceiver())){
throw new PayFailureException("分账参数未通过校验");
}
receiverService.unbind(this.getAllocationReceiver(),this.weChatPayConfig);
}
}

View File

@@ -0,0 +1,31 @@
package cn.bootx.platform.daxpay.service.dto.allocation;
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;
/**
*
* @author xxm
* @since 2024/4/1
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@Schema(title = "")
public class AllocationGroupDto extends BaseDto {
@Schema(description = "名称")
private String name;
@Schema(description = "通道")
private String channel;
@Schema(description = "总分账比例(万分之多少)")
private Integer totalRate;
@Schema(description = "备注")
private String remark;
}

View File

@@ -0,0 +1,19 @@
package cn.bootx.platform.daxpay.service.dto.allocation;
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;
/**
*
* @author xxm
* @since 2024/4/1
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@Schema(title = "")
public class AllocationGroupReceiverDto extends BaseDto {
}

View File

@@ -0,0 +1,36 @@
package cn.bootx.platform.daxpay.service.func;
import cn.bootx.platform.daxpay.service.core.payment.allocation.entity.AllocationReceiver;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
/**
* 分账接收方管理抽象策略
* @author xxm
* @since 2024/4/1
*/
@Slf4j
@Getter
@Setter
public abstract class AbsAllocationReceiverStrategy implements PayStrategy{
private AllocationReceiver allocationReceiver;
public abstract boolean validation();
/**
* 支付前处理, 校验和初始化支付配置
*/
public abstract void doBeforeHandler();
/**
* 添加到三方支付系统中
*/
public abstract void bind();
/**
* 从三方支付系统中删除
*/
public abstract void unbind();
}

View File

@@ -0,0 +1,30 @@
package cn.bootx.platform.daxpay.service.param.allocation;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.List;
/**
* 分账组绑定参数
* @author xxm
* @since 2024/4/1
*/
@Data
@Accessors(chain = true)
@Schema(title = "分账组绑定参数")
public class AllocationGroupBindParam {
@NotNull(message = "分账组ID不可为空")
@Schema(description = "分账组ID")
private Long groupId;
@Valid
@NotEmpty(message = "分账接收方不可为空")
@Schema(description = "分账接收方集合")
List<AllocationGroupReceiverParam> receivers;
}

View File

@@ -0,0 +1,29 @@
package cn.bootx.platform.daxpay.service.param.allocation;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 分账组参数
* @author xxm
* @since 2024/4/1
*/
@Data
@Accessors(chain = true)
@Schema(title = "分账组参数")
public class AllocationGroupParam {
@Schema(description = "主键")
private Long id;
@Schema(description = "分组名称")
private String name;
@Schema(description = "通道")
private String channel;
@Schema(description = "备注")
private String remark;
}

View File

@@ -0,0 +1,28 @@
package cn.bootx.platform.daxpay.service.param.allocation;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
/**
* 分账组接收者参数
* @author xxm
* @since 2024/4/1
*/
@Data
@Accessors(chain = true)
@Schema(title = "分账组接收者参数")
public class AllocationGroupReceiverParam {
@NotNull(message = "接收者ID不可为空")
@Schema(description = "接收者ID")
private Long receiverId;
@Schema(description = "分账比例(万分之多少)")
@NotNull(message = "分账比例不可为空")
@Min(value = 1,message = "分账比例最低为1")
private Integer rate;
}

View File

@@ -0,0 +1,28 @@
package cn.bootx.platform.daxpay.service.param.allocation;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.List;
/**
* 分账组接收者解除绑定
* @author xxm
* @since 2024/4/1
*/
@Data
@Accessors(chain = true)
@Schema(title = "分账组取消接收者绑定")
public class AllocationGroupUnbindParam {
@NotNull(message = "分账组ID不可为空")
@Schema(description = "分账组ID")
private Long groupId;
@NotEmpty(message = "分账接收方不可为空")
@Schema(description = "分账接收方集合")
List<Long> receiverIds;
}

View File

@@ -16,6 +16,7 @@ import lombok.experimental.Accessors;
@Schema(title = "分账接收方参数")
public class AllocationReceiverParam {
@Schema(description = "主键")
private Long id;

View File

@@ -0,0 +1,53 @@
package cn.bootx.platform.daxpay.service.param.allocation;
import cn.bootx.platform.common.core.annotation.QueryParam;
import cn.bootx.platform.daxpay.code.AllocationRelationTypeEnum;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
/**
* 分账接收方查询条件
* @author xxm
* @since 2024/4/1
*/
@Getter
@Setter
public class AllocationReceiverQuery {
@QueryParam(type = QueryParam.CompareTypeEnum.LIKE)
@Schema(description = "账号别名")
private String name;
/**
* @see PayChannelEnum
*/
@Schema(description = "所属通道")
private String channel;
/**
* 分账接收方类型 个人/商户
*/
@Schema(description = "分账接收方类型")
private String receiverType;
@QueryParam(type = QueryParam.CompareTypeEnum.LIKE)
@Schema(description = "接收方账号")
private String receiverAccount;
/** 接收方姓名 */
@QueryParam(type = QueryParam.CompareTypeEnum.LIKE)
@Schema(description = "接收方姓名")
private String receiverName;
/**
* 分账关系类型
* @see AllocationRelationTypeEnum
*/
@Schema(description = "分账关系类型")
private String relationType;
@Schema(description = "是否已经同步到网关")
private boolean sync;
}

View File

@@ -1,6 +1,6 @@
package cn.bootx.platform.daxpay.service.sdk.wechat;
import com.ijpay.wxpay.model.MicroPayModel;
import com.ijpay.core.model.BaseModel;
import lombok.*;
import lombok.experimental.Accessors;
@@ -15,7 +15,7 @@ import lombok.experimental.Accessors;
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class BarPayModel extends MicroPayModel {
public class BarPayModel extends BaseModel {
/**
* 是否押金支付
*/