KKSU
2025-01-03 7bf5945cdd7560922828f225c882d82f95405648
feat(mall): 添加会员购买功能并优化节点助力逻辑

- 新增 ApiBalanceBuyDto 类用于会员购买参数接收
- 在 ApiRunVipController 中添加 balanceBuy 方法处理会员购买请求
- 在 IRunVipService 接口中添加 balanceBuy 方法
- 在 RunVipServiceImpl 中实现 balanceBuy 方法的逻辑
- 优化节点助力逻辑,使用最小业绩的节点设置作为标准
- 调整节点助力奖励的计算方式,直接使用碳币数量
- 更新相关的枚举类和 VO 类以支持新的功能
1 files added
8 files modified
153 ■■■■ changed files
src/main/java/cc/mrbird/febs/common/enumerates/RunVipMoneyFlowTypeEnum.java 7 ●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/mall/controller/ApiRunVipController.java 12 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/mall/dto/ApiBalanceBuyDto.java 24 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/mall/service/AsyncService.java 2 ●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/mall/service/IRunVipService.java 7 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/mall/service/impl/AsyncServiceImpl.java 18 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/mall/service/impl/MemberProfitServiceImpl.java 18 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/mall/service/impl/RunVipServiceImpl.java 60 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/mall/vo/ApiRunVipVo.java 5 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/common/enumerates/RunVipMoneyFlowTypeEnum.java
@@ -8,6 +8,11 @@
    /**
     * 碳币兑换USDT
     */
    COMMISSION_PAY(16,"购买会员,APP支付{}","购买会员,APP支付"),
    /**
     * 碳币兑换USDT
     */
    BALANCE_OUT_COMMISSION_IN(15,"{}碳币兑换{}USDT","获得USDT"),
    /**
@@ -33,7 +38,7 @@
    /**
     * 节点助力
     */
    NODE_BALANCE(10,"节点助力碳币{}","节点助力碳币"),
    NODE_COMMISSION(10,"节点助力{}","节点助力"),
    /**
     * 获得碳积分
src/main/java/cc/mrbird/febs/mall/controller/ApiRunVipController.java
@@ -2,10 +2,7 @@
import cc.mrbird.febs.common.annotation.SubmitRepeat;
import cc.mrbird.febs.common.entity.FebsResponse;
import cc.mrbird.febs.mall.dto.ApiChargeListDto;
import cc.mrbird.febs.mall.dto.ApiGetRunDateDto;
import cc.mrbird.febs.mall.dto.ApiGoChargeDto;
import cc.mrbird.febs.mall.dto.ApiGoChargeInfoDto;
import cc.mrbird.febs.mall.dto.*;
import cc.mrbird.febs.mall.service.IRunVipService;
import cc.mrbird.febs.mall.vo.*;
import io.swagger.annotations.Api;
@@ -75,6 +72,13 @@
        return new FebsResponse().success().data(iRunVipService.getChargeList(apiChargeListDto));
    }
    @ApiOperation(value = "【购买VIP】APP支付", notes = "【购买VIP】APP支付")
    @PostMapping(value = "/goCharge")
    @SubmitRepeat
    public FebsResponse balanceBuy(@RequestBody @Valid ApiBalanceBuyDto apiBalanceBuyDto) {
        return iRunVipService.balanceBuy(apiBalanceBuyDto);
    }
    @ApiOperation(value = "【购买VIP】立即购买", notes = "【购买VIP】立即购买")
    @ApiResponses({
            @ApiResponse(code = 200, message = "success", response = ApiGoChargeVo.class)
src/main/java/cc/mrbird/febs/mall/dto/ApiBalanceBuyDto.java
New file
@@ -0,0 +1,24 @@
package cc.mrbird.febs.mall.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotNull;
@Data
@ApiModel(value = "ApiBalanceBuyDto", description = "参数接收类")
public class ApiBalanceBuyDto {
    @NotNull(message = "请选择会员")
    @ApiModelProperty(value = "会员ID", example = "123456")
    private Long runVipId;
    @NotNull(message = "请输入资金密码")
    @ApiModelProperty(value = "资金密码", example = "123456")
    private String tradeWord;
    @NotNull(message = "请选择数量")
    @ApiModelProperty(value = "数量", example = "123456")
    private Integer vipCnt;
}
src/main/java/cc/mrbird/febs/mall/service/AsyncService.java
@@ -13,5 +13,5 @@
    void releaseScore(BigDecimal amount,Long memberId);
    @Async(FebsConstant.ASYNC_POOL)
    void releaseNodeCoin(BigDecimal amount, BigDecimal percent, BigDecimal balanceToCoin, Long memberId);
    void releaseNodeCoin(BigDecimal amount, BigDecimal percent, Long memberId);
}
src/main/java/cc/mrbird/febs/mall/service/IRunVipService.java
@@ -1,10 +1,7 @@
package cc.mrbird.febs.mall.service;
import cc.mrbird.febs.common.entity.FebsResponse;
import cc.mrbird.febs.mall.dto.ApiChargeListDto;
import cc.mrbird.febs.mall.dto.ApiGetRunDateDto;
import cc.mrbird.febs.mall.dto.ApiGoChargeDto;
import cc.mrbird.febs.mall.dto.ApiGoChargeInfoDto;
import cc.mrbird.febs.mall.dto.*;
import cc.mrbird.febs.mall.entity.RunVip;
import cc.mrbird.febs.mall.vo.*;
import com.baomidou.mybatisplus.extension.service.IService;
@@ -16,6 +13,8 @@
    FebsResponse goCharge(ApiGoChargeDto apiGoChargeDto);
    FebsResponse balanceBuy(ApiBalanceBuyDto apiBalanceBuyDto);
    FebsResponse goChargeInfo(ApiGoChargeInfoDto apiGoChargeInfoDto);
    List<ApiChargeVo> getChargeList(ApiChargeListDto apiChargeListDto);
src/main/java/cc/mrbird/febs/mall/service/impl/AsyncServiceImpl.java
@@ -43,26 +43,22 @@
    }
    @Override
    public void releaseNodeCoin(BigDecimal amount, BigDecimal percent, BigDecimal balanceToCoin, Long memberId) {
    public void releaseNodeCoin(BigDecimal amount, BigDecimal percent, Long memberId) {
        //实际节点助力的碳币
        BigDecimal multiply = amount.multiply(percent);
        BigDecimal multiply = amount.multiply(percent).setScale(2,RoundingMode.DOWN);
        if(BigDecimal.ZERO.compareTo(multiply) >=0){
            return;
        }
        BigDecimal divide = multiply.divide(balanceToCoin, 2, RoundingMode.DOWN);
        if(BigDecimal.ZERO.compareTo(divide) >=0){
            return;
        }
        walletService.addBalance(divide,memberId);
        walletService.addCommission(multiply,memberId);
        String orderNo = MallUtils.getOrderNum("JD");
        mallMoneyFlowService.runVipMoneyFlowAdd(
                memberId,
                memberId,
                orderNo,
                FlowTypeEnum.BALANCE.getValue(),
                RunVipMoneyFlowTypeEnum.NODE_BALANCE.getValue(),
                divide,
                StrUtil.format(RunVipMoneyFlowTypeEnum.NODE_BALANCE.getDescription(),divide),
                FlowTypeEnum.COMMISSION.getValue(),
                RunVipMoneyFlowTypeEnum.NODE_COMMISSION.getValue(),
                multiply,
                StrUtil.format(RunVipMoneyFlowTypeEnum.NODE_COMMISSION.getDescription(),multiply),
                YesOrNoEnum.YES.getValue()
        );
src/main/java/cc/mrbird/febs/mall/service/impl/MemberProfitServiceImpl.java
@@ -857,24 +857,28 @@
        }
        //获取节点设置
        List<RunNodeSet> runNodeSets = runNodeSetMapper.selectList(null);
        BigDecimal balanceToCoin = new BigDecimal(
                dataDictionaryCustomMapper.selectDicDataByTypeAndCode(
                        RunVipDataDictionaryEnum.RUN_VIP_BALANCE_TO_COIN.getType(),
                        RunVipDataDictionaryEnum.RUN_VIP_BALANCE_TO_COIN.getCode()).getValue()
        ).setScale(2, BigDecimal.ROUND_DOWN);
        //获取节点设置runNodeSets中orderNumber最小的节点
        RunNodeSet minRunNodeSet = runNodeSets.stream().min(Comparator.comparingInt(RunNodeSet::getOrderNumber)).orElse(null);
        if(null == minRunNodeSet){
            return;
        }
        DateTime endTime = DateUtil.date();
        for (MallMember item : mallMembers) {
            //获取总业绩
            BigDecimal achieve = getDirectAchieve(item.getInviteId(),item.getDirectorTime(),endTime);
            if(BigDecimal.ZERO.compareTo(achieve) >= 0){
            //判断总业绩是否小于最小业绩
            if(minRunNodeSet.getMinAchieve().compareTo(achieve) > 0){
                item.setDirector(YesOrNoEnum.NO.getValue());
                mallMemberMapper.updateById(item);
                continue;
            }
            BigDecimal nodePercent = getNodePercent(runNodeSets, achieve);
            if(BigDecimal.ZERO.compareTo(nodePercent) >= 0){
                continue;
            }
            asyncService.releaseNodeCoin(achieve,nodePercent,balanceToCoin,item.getId());
            asyncService.releaseNodeCoin(achieve,nodePercent,item.getId());
            item.setDirectorTime(endTime);
            mallMemberMapper.updateById(item);
        }
src/main/java/cc/mrbird/febs/mall/service/impl/RunVipServiceImpl.java
@@ -12,6 +12,8 @@
import cc.mrbird.febs.mall.dto.*;
import cc.mrbird.febs.mall.entity.*;
import cc.mrbird.febs.mall.mapper.*;
import cc.mrbird.febs.mall.service.IApiMallMemberWalletService;
import cc.mrbird.febs.mall.service.IMallMoneyFlowService;
import cc.mrbird.febs.mall.service.IRunVipService;
import cc.mrbird.febs.mall.vo.*;
import cc.mrbird.febs.rabbit.producter.AgentProducer;
@@ -48,6 +50,8 @@
    private final MallMoneyFlowMapper mallMoneyFlowMapper;
    private final MallMemberWalletMapper mallMemberWalletMapper;
    private final MallMemberMapper mallMemberMapper;
    private final IApiMallMemberWalletService walletService;
    private final IMallMoneyFlowService mallMoneyFlowService;
    @Override
    public List<ApiRunVipVo> vipInfo() {
        Long memberId = LoginUserUtil.getLoginUser().getId();
@@ -152,6 +156,62 @@
    }
    @Override
    public FebsResponse balanceBuy(ApiBalanceBuyDto apiBalanceBuyDto) {
        Long memberId = LoginUserUtil.getLoginUser().getId();
        MallMember mallMember = mallMemberMapper.selectById(memberId);
        if (StrUtil.isBlank(mallMember.getTradePassword())) {
            throw new FebsException("未设置资金密码");
        }
        if (!mallMember.getTradePassword().equals(SecureUtil.md5(apiBalanceBuyDto.getTradeWord()))) {
            throw new FebsException("资金密码错误");
        }
        Long runVipId = apiBalanceBuyDto.getRunVipId();
        RunVip runVip = this.baseMapper.selectById(runVipId);
        if(runVip.getState() != YesOrNoEnum.YES.getValue()){
            throw new FebsException("会员套餐已下架");
        }
        //价格
        BigDecimal presentAmount = runVip.getPresentPrice().setScale(2,BigDecimal.ROUND_DOWN);
        MallMemberWallet mallMemberWallet = mallMemberWalletMapper.selectWalletByMemberId(memberId);
        if(mallMemberWallet.getCommission().compareTo(BigDecimal.ZERO) <= 0){
            throw new FebsException("余额不足");
        }
        if(mallMemberWallet.getCommission().compareTo(presentAmount) < 0){
            throw new FebsException("余额不足");
        }
        String orderNo = MallUtils.getOrderNum();
        walletService.reduceCommission(presentAmount,memberId);
        mallMoneyFlowService.runVipMoneyFlowAdd(
                memberId,
                memberId,
                orderNo,
                FlowTypeEnum.COMMISSION.getValue(),
                RunVipMoneyFlowTypeEnum.COMMISSION_PAY.getValue(),
                presentAmount,
                StrUtil.format(RunVipMoneyFlowTypeEnum.COMMISSION_PAY.getDescription(),presentAmount),
                YesOrNoEnum.YES.getValue()
        );
        MallCharge mallCharge = new MallCharge();
        mallCharge.setMemberId(memberId);
        mallCharge.setOrderNo(orderNo);
        mallCharge.setState(YesOrNoEnum.YES.getValue());
        mallCharge.setType(StrUtil.format(RunVipMoneyFlowTypeEnum.COMMISSION_PAY.getDescription(),presentAmount));
        mallCharge.setAddress(mallMember.getPhone());
        mallCharge.setAmount(presentAmount);
        mallCharge.setVipCode(runVip.getVipCode());
        mallCharge.setVipName(runVip.getVipName());
        mallCharge.setVipCnt(apiBalanceBuyDto.getVipCnt());
        mallChargeMapper.insert(mallCharge);
        agentProducer.sendBuyVipSuccessMsg(mallCharge.getId());
        return new FebsResponse().success().message("操作成功");
    }
    @Override
    public FebsResponse goChargeInfo(ApiGoChargeInfoDto apiGoChargeInfoDto) {
        Long memberId = LoginUserUtil.getLoginUser().getId();
        String orderNo = apiGoChargeInfoDto.getOrderNo();
src/main/java/cc/mrbird/febs/mall/vo/ApiRunVipVo.java
@@ -34,10 +34,15 @@
    private Integer commissionState;//是否允许USDT提现 1是0否
    @ApiModelProperty(value = "是否允许内转 1-是0-否")
    private Integer insideState;//是否允许内转 1-是0-否
    @ApiModelProperty(value = "是否允许权益升级 1-是0-否")
    private Integer growState;//是否允许权益升级 1-是0-否
    @ApiModelProperty(value = "每日碳积分增长倍数")
    private BigDecimal growthValue;//每日碳积分增长倍数
    @ApiModelProperty(value = "每日获取碳积分最大值")
    private Integer growthCnt;//每日获取碳积分最大值
    @ApiModelProperty(value = "购买会员返利金额比例(实际支付金额 * rebatePercent = 返利金额)")
    private BigDecimal rebatePercent;//购买会员返利金额比例(实际支付金额 * rebatePercent = 返利金额)
}