Helius
2022-08-24 b90bb6e1cedd0210c231a5485b29c5724078d9f0
fix:add matrixTree
10 files modified
1 files deleted
2 files added
617 ■■■■■ changed files
src/main/java/cc/mrbird/febs/common/authentication/ShiroConfig.java 1 ●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/dapp/controller/ApiCommonController.java 28 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/dapp/controller/DappController.java 120 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/dapp/dto/ConnectDto.java 7 ●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/dapp/service/impl/BscUsdtContractEvent.java 105 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/dapp/service/impl/DappMemberServiceImpl.java 31 ●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/dapp/service/impl/DappWalletServiceImpl.java 107 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/job/ChainListenerJob.java 5 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/tree/MatrixTree.java 115 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/tree/MemberNode.java 5 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/tree/TreeController.java 33 ●●●●● patch | view | raw | blame | history
src/main/resources/application-dev.yml 24 ●●●● patch | view | raw | blame | history
src/test/java/cc/mrbird/febs/TreeTest.java 36 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/common/authentication/ShiroConfig.java
@@ -96,6 +96,7 @@
        // 排除手机api请求链接
        filterChainDefinitionMap.put("/api/**", "anon");
        filterChainDefinitionMap.put("/dapi/**", "anon");
        filterChainDefinitionMap.put("/test/**", "anon");
        filterChainDefinitionMap.put("/swagger-ui.html/**", "anon");
        filterChainDefinitionMap.put("/swagger-resources/**", "anon");
src/main/java/cc/mrbird/febs/dapp/controller/ApiCommonController.java
@@ -6,6 +6,7 @@
import cc.mrbird.febs.dapp.dto.ConnectDto;
import cc.mrbird.febs.dapp.dto.EncryptDto;
import cc.mrbird.febs.dapp.dto.SystemDto;
import cc.mrbird.febs.dapp.entity.DappMemberEntity;
import cc.mrbird.febs.dapp.service.DappMemberService;
import cc.mrbird.febs.dapp.service.DappSimulateDataService;
import cc.mrbird.febs.dapp.service.DappSystemService;
@@ -33,20 +34,15 @@
    private final DappMemberService dappMemberService;
    private final DappSystemService dappSystemService;
    private final DappSimulateDataService dappSimulateDataService;
//    @ApiOperation(value = "授权接口", notes = "授权接口")
//    @PostMapping(value = "/approve")
//    public FebsResponse approve(@RequestBody ApproveDto approveDto) {
//        dappMemberService.approve(approveDto);
//        return new FebsResponse().success().message("授权成功");
//    }
//
//    @ApiOperation(value = "是否授权接口", notes = "是否授权接口")
//    @GetMapping(value = "/isApprove/{chain}/{address}")
//    public FebsResponse isApprove(@PathVariable("address") String address,@PathVariable("chain") String chain) {
//        return new FebsResponse().success().message("获取成功").data(dappMemberService.isApprove(address, chain));
//    }
    @ApiOperation(value = "地址是否存在", notes = "地址是否存在")
    @GetMapping(value = "/exist/{address}")
    public FebsResponse exist(@PathVariable("address") String address) {
        DappMemberEntity member = dappMemberService.findByAddress(address, "BSC");
        int result = member == null ? 2 : 1;
        return new FebsResponse().success().data(result);
    }
    @ApiOperation(value = "链接接口", notes = "链接接口")
    @PostMapping(value = "/connect")
@@ -59,11 +55,5 @@
    @GetMapping(value = "/totalIncome")
    public FebsResponse totalIncome() {
        return new FebsResponse().success().data(dappSystemService.findTotalInComeAndList());
    }
    @PostMapping(value = "/encrypt")
    public FebsResponse encryptTest(@RequestBody EncryptDto encryptDto) {
        System.out.println(encryptDto.getTest());
        return new FebsResponse().success().data("123");
    }
}
src/main/java/cc/mrbird/febs/dapp/controller/DappController.java
File was deleted
src/main/java/cc/mrbird/febs/dapp/dto/ConnectDto.java
@@ -4,6 +4,8 @@
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
 * @author wzy
 * @date 2022-05-26
@@ -21,6 +23,7 @@
    @ApiModelProperty(value = "签名", example = "123")
    private String sign;
//    @ApiModelProperty(value = "邀请码", example = "12345678")
//    private String inviteId;
    @NotBlank(message = "invite code can not be null")
    @ApiModelProperty(value = "邀请码", example = "12345678")
    private String inviteId;
}
src/main/java/cc/mrbird/febs/dapp/service/impl/BscUsdtContractEvent.java
@@ -5,10 +5,7 @@
import cc.mrbird.febs.common.utils.ShareCodeUtil;
import cc.mrbird.febs.dapp.chain.*;
import cc.mrbird.febs.dapp.entity.*;
import cc.mrbird.febs.dapp.mapper.DappFundFlowDao;
import cc.mrbird.febs.dapp.mapper.DappMemberDao;
import cc.mrbird.febs.dapp.mapper.DappOnlineTransferDao;
import cc.mrbird.febs.dapp.mapper.DataDictionaryCustomMapper;
import cc.mrbird.febs.dapp.mapper.*;
import cc.mrbird.febs.dapp.service.DappMemberService;
import cc.mrbird.febs.dapp.service.DappSystemService;
import cc.mrbird.febs.dapp.utils.OnlineTransferUtil;
@@ -43,15 +40,7 @@
    private DappFundFlowDao dappFundFlowDao;
    @Resource
    private ChainProducer chainProducer;
    @Resource
    private DappOnlineTransferDao dappOnlineTransferDao;
    @Resource
    private DataDictionaryCustomMapper dataDictionaryCustomMapper;
    @Autowired
    private DappSystemService dappSystemService;
    private DappWalletCoinDao dappWalletCoinDao;
    @Override
@@ -68,7 +57,7 @@
            // 如果得到触发,则休眠10秒。 因为此处监听器触发可能优先于前端调用transfer接口
            try {
                Thread.sleep(10000);
                Thread.sleep(5000);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
@@ -80,13 +69,6 @@
            }
            DappMemberEntity fromMember = dappMemberService.findByAddress(e.from, null);
            // 如果此时fromMember为null,则说明该用户未经过转账绑定关系,而是直接注册并购买币,则将关系绑定到顶级账户
            if (fromMember == null) {
                DappMemberEntity toAddress = dappMemberService.findByAddress(e.to, null);
                fromMember = dappMemberService.insertMember(e.from, toAddress.getInviteId());
            }
            String hasStart = redisUtils.getString(AppContants.SYSTEM_START_FLAG);
            BigInteger tokens = e.tokens;
            BigDecimal amount = new BigDecimal(tokens.toString()).divide(BigDecimal.TEN.pow(decimals), decimals, RoundingMode.HALF_DOWN);
@@ -121,89 +103,18 @@
                    return;
                }
            }
            // 激活卡牌
            if (fundFlow.getType() == 8) {
                DappFundFlowEntity finalFundFlow = fundFlow;
                DappMemberEntity member = dappMemberService.getById(fundFlow.getMemberId());
                new Thread(() -> {
                    try {
                        int count = finalFundFlow.getAmount().intValue();
                        String token = null;
                        while (count > 0) {
                            token = ChainService.getInstance(ChainEnum.BSC_NFT_SDC.name()).safeMintNFT(member.getAddress());
                            Thread.sleep(5000);
                            count--;
                        }
                        finalFundFlow.setStatus(2);
                        finalFundFlow.setToHash(token);
                        dappFundFlowDao.updateById(finalFundFlow);
                    } catch (Exception ex) {
                        ex.printStackTrace();
                        log.error("发放卡牌错误", ex);
                    }
                }).start();
                ChainService.getInstance(ChainEnum.BSC_TFC.name()).transfer(AppContants.DESTROY_ADDRESS, finalFundFlow.getTargetAmount());
                return;
            }
            OnlineTransferUtil.addTransferRecord(e.from, e.to, amount, e.log.getTransactionHash(), DappTransferRecordEntity.TRANSFER_SOURCE_FLAG_APPLICATION, "USDT");
            // 这个方法主要是处理,tp钱包在调用transfer时,交易详情中的金额可能因为网速慢而显示晚,从而导致实际转账比记录中的少,顾做此处理
            BigDecimal realCoin = amount.divide(fundFlow.getNewestPrice(), decimals, RoundingMode.HALF_UP);
            if (fundFlow.getAmount().subtract(new BigDecimal("0.1")).compareTo(realCoin) > 0) {
                fundFlow.setAmount(realCoin);
                BigDecimal subtract = fundFlow.getAmount().subtract(realCoin);
                BigDecimal poolRemain = (BigDecimal) redisUtils.get(AppContants.REDIS_KEY_TRANSFER_POOL_VOL_REMAIN);
                redisUtils.set(AppContants.REDIS_KEY_TRANSFER_POOL_VOL_REMAIN, poolRemain.add(subtract));
            }
            // 更改状态为已同步
            fundFlow.setStatus(2);
            fundFlow.setTargetAmount(amount);
            dappFundFlowDao.updateById(fundFlow);
            // 如果系统会开启,则使用自动打款
            if (!"start".equals(hasStart)) {
                OnlineTransferUtil.addTransfer(e.from, fundFlow.getAmount(), 1, 1, ChainEnum.BSC_TFC.name(), "BSC", fundFlow.getId().toString());
                Map<String, String> map = new HashMap<>();
                map.put("batchNo", fundFlow.getId().toString());
                map.put("type", "flow");
                // 发送转账消息
                chainProducer.sendOnlineTransfer(JSONObject.toJSONString(map));
                // 发送盲盒消息
                chainProducer.sendNftBoxMsg(fundFlow.getId());
                fromMember.setMakerType(1);
                dappMemberService.updateById(fromMember);
            } else {
                // 买入收手续费,到账币数量减去手续费
                OnlineTransferUtil.addTransfer(e.from, fundFlow.getAmount().subtract(fundFlow.getFee()), 1, 1, ChainEnum.BSC_TFC_SOURCE.name(), AppContants.SYMBOL_COIN, fundFlow.getId().toString());
                Map<String, String> map = new HashMap<>();
                map.put("batchNo", fundFlow.getId().toString());
                map.put("type", "flow");
                // 发送转账消息
                chainProducer.sendOnlineTransfer(JSONObject.toJSONString(map));
                // 发送购买奖励消息
                chainProducer.sendUserBuyReward(fundFlow.getId());
                // 发送盲盒消息
                chainProducer.sendNftBoxMsg(fundFlow.getId());
            synchronized (this) {
                DappWalletCoinEntity walletCoin = dappWalletCoinDao.selectByMemberId(fundFlow.getMemberId());
                walletCoin.setTotalAmount(walletCoin.getTotalAmount().add(amount));
                walletCoin.setAvailableAmount(walletCoin.getAvailableAmount().add(amount));
                dappWalletCoinDao.updateById(walletCoin);
            }
        }
    }
    public static void main(String[] args) {
        System.out.println(ChainService.getInstance(ChainEnum.BSC_USDT.name()).balanceOf("0x9DDE1834683D642D4D077498DC7fbdb8CF70E8FE"));
    }
}
src/main/java/cc/mrbird/febs/dapp/service/impl/DappMemberServiceImpl.java
@@ -126,18 +126,15 @@
        DappMemberEntity member = dappMemberDao.selectByAddress(connectDto.getAddress(), null);
        if (member == null) {
//            DappMemberEntity parent = dappMemberDao.selectMemberInfoByInviteId(connectDto.getInviteId());
//            if (parent == null) {
//                throw new FebsException("邀请人不存在");
//            }
            throw new FebsException(MessageSourceUtils.getString("system_regist_error"));
//            member = insertMember(connectDto.getAddress(), null);
            if (!"asdf4321".equals(connectDto.getInviteId())) {
                DappMemberEntity parent = dappMemberDao.selectMemberInfoByInviteId(connectDto.getInviteId());
                if (parent == null) {
                    throw new FebsException("recommender is not exist");
                }
        }
//        if (member.getActiveStatus() == 2) {
//            throw new FebsException("注册成功,账号暂未激活,请联系推荐人");
//        }
            member = insertMember(connectDto.getAddress(), null);
        }
        String key = LoginUserUtil.getLoginKey(connectDto.getAddress(), connectDto.getNonce(), connectDto.getSign());
        redisUtils.set(connectDto.getAddress(), member);
@@ -283,15 +280,6 @@
        member.setBalance(ChainService.getInstance(ChainEnum.BSC_TFC.name()).balanceOf(address));
        member.setUsdtBalance(ChainService.getInstance(ChainEnum.BSC_USDT.name()).balanceOf(address));
        DataDictionaryCustom dic = dataDictionaryCustomMapper.selectDicDataByTypeAndCode(AppContants.DIC_TYPE_SYSTEM_SETTING, AppContants.DIC_VALUE_SYSTEM_NODE_CNT_LIMIT);
        int nodeCntLimit = Integer.parseInt(dic.getValue());
        List<DappMemberEntity> memberCount = dappMemberDao.selectList(null);
        if (memberCount.size() <= (nodeCntLimit + 4)) {
            member.setNodeType(1);
        } else {
            member.setNodeType(2);
        }
        dappMemberDao.insert(member);
        DappWalletCoinEntity walletCoin = new DappWalletCoinEntity();
@@ -312,8 +300,6 @@
            String ids = "";
            String feeProfitIds = "";
            int i = 1;
            List<DataDictionaryCustom> feeProfitDic = dataDictionaryCustomMapper.selectDicByType(AppContants.DIC_TYPE_DISTRIBUTE_PROP);
            while (!flag && StringUtils.isNotBlank(parentId)) {
                if (StrUtil.isBlank(ids)) {
                    ids += parentId;
@@ -321,15 +307,12 @@
                    ids += ("," + parentId);
                }
                if (i <= feeProfitDic.size()) {
                    if (StrUtil.isBlank(feeProfitIds)) {
                        feeProfitIds += parentId;
                    } else {
                        feeProfitIds += ("," + parentId);
                    }
                }
                i++;
                DappMemberEntity parentMember = dappMemberDao.selectMemberInfoByInviteId(parentId);
                if (parentMember == null) {
                    break;
src/main/java/cc/mrbird/febs/dapp/service/impl/DappWalletServiceImpl.java
@@ -219,126 +219,19 @@
    public Long transfer(TransferDto transferDto) {
        DappMemberEntity member = LoginUserUtil.getAppUser();
        if (member.getActiveStatus() == 2) {
            throw new FebsException("请联系邀请人转币激活");
        }
        // 每日出U剩余量(卖币)
        BigDecimal usdtRemain = (BigDecimal) redisUtils.get(AppContants.REDIS_KEY_USDT_OUT_LIMIT_REMAIN);
        // 用户24小时可出售量
        BigDecimal saleCoinRemain = (BigDecimal) redisUtils.get(AppContants.REDIS_KEY_COIN_REMAIN + member.getAddress());
        // 用户24小时可购买USDT
        BigDecimal buyUsdtMax = (BigDecimal) redisUtils.get(AppContants.REDIS_KEY_IDO_USDT_MAX_BUY_DAILY + member.getAddress());
        BigDecimal buyCoinRemain = (BigDecimal) redisUtils.get(AppContants.REDIS_KEY_TRANSFER_POOL_VOL_REMAIN);
        // 铸池中的币的剩余量
        BigDecimal makeCoinRemain = (BigDecimal) redisUtils.get(AppContants.REDIS_KEY_MAKE_POOL_CNT);
        String hasStart = redisUtils.getString(AppContants.SYSTEM_START_FLAG);
        if (transferDto.getId() == null) {
            if (DappFundFlowEntity.TYPE_SALE == transferDto.getType()) {
                if (!"start".equals(hasStart)) {
                    throw new FebsException(MessageSourceUtils.getString("transfer_msg_001"));
                }
                if (transferDto.getAmount().multiply(transferDto.getPrice()).compareTo(usdtRemain) > 0) {
                    throw new FebsException(MessageSourceUtils.getString("transfer_msg_002"));
                }
                if (transferDto.getAmount().compareTo(saleCoinRemain) > 0) {
                    throw new FebsException(MessageSourceUtils.getString("transfer_msg_003"));
                }
                usdtRemain = usdtRemain.subtract(transferDto.getAmount().multiply(transferDto.getPrice()));
                saleCoinRemain = saleCoinRemain.subtract(transferDto.getAmount());
                // 修改当日U剩余量
                redisUtils.set(AppContants.REDIS_KEY_USDT_OUT_LIMIT_REMAIN, usdtRemain);
                // 修改用户24小时可售量
                redisUtils.set(AppContants.REDIS_KEY_COIN_REMAIN + member.getAddress(), saleCoinRemain);
            } else if (DappFundFlowEntity.TYPE_BUY == transferDto.getType()) {
                // 购买时,前端传来的amount是USDT,卖出amount是TFC
                BigDecimal usdtAmount = transferDto.getAmount();
                BigDecimal coinAmount = transferDto.getAmount().divide(transferDto.getPrice(), 6, RoundingMode.HALF_UP);
                transferDto.setAmount(coinAmount);
                if ("start".equals(hasStart)) {
                    if (transferDto.getAmount().compareTo(buyCoinRemain) > 0) {
                        throw new FebsException(MessageSourceUtils.getString("transfer_msg_004"));
                    }
                    buyCoinRemain = buyCoinRemain.subtract(transferDto.getAmount());
                    // 修改当日可购买量
                    redisUtils.set(AppContants.REDIS_KEY_TRANSFER_POOL_VOL_REMAIN, buyCoinRemain);
                    // 如果系统还没有启动,则判断铸池中的剩余量
                } else {
                    // 最少购买
//                    DataDictionaryCustom dic = dataDictionaryCustomMapper.selectDicDataByTypeAndCode(AppContants.DIC_TYPE_SYSTEM_SETTING, AppContants.DIC_VALUE_MAKER_MIN_LIMIT);
//                    if (transferDto.getAmount().compareTo(new BigDecimal(dic.getValue())) < 0) {
//                        throw new FebsException(MessageSourceUtils.getString("transfer_msg_005"));
//                    }
                    if (buyUsdtMax.compareTo(usdtAmount) < 0) {
                        throw new FebsException(MessageSourceUtils.getString("transfer_msg_007"));
                    }
                    if (transferDto.getAmount().compareTo(makeCoinRemain) > 0) {
                        throw new FebsException(MessageSourceUtils.getString("transfer_msg_006"));
                    }
                    makeCoinRemain = makeCoinRemain.subtract(transferDto.getAmount());
                    buyUsdtMax = buyUsdtMax.subtract(usdtAmount);
                    // 修改每日最大购买USDT量
                    redisUtils.set(AppContants.REDIS_KEY_IDO_USDT_MAX_BUY_DAILY + member.getAddress(), buyUsdtMax);
                    // 修改铸池量
                    redisUtils.set(AppContants.REDIS_KEY_MAKE_POOL_CNT, makeCoinRemain);
                }
            }
            DappFundFlowEntity fundFlow = new DappFundFlowEntity(member.getId(), transferDto.getAmount(), transferDto.getType(), 1, transferDto.getFee(), transferDto.getTxHash());
            fundFlow.setNewestPrice(transferDto.getPrice());
            dappFundFlowDao.insert(fundFlow);
            return fundFlow.getId();
        }
        if ("success".equals(transferDto.getFlag())) {
            DappFundFlowEntity flow = dappFundFlowDao.selectById(transferDto.getId());
            flow.setFromHash(transferDto.getTxHash());
            dappFundFlowDao.updateById(flow);
        } else {
            DappFundFlowEntity flow = dappFundFlowDao.selectById(transferDto.getId());
            if (flow.getStatus() == 1) {
                if (DappFundFlowEntity.TYPE_BUY == flow.getType()) {
                    // 购买时,前端传来的amount是USDT,卖出amount是TFC
                    BigDecimal usdtAmount = transferDto.getAmount();
                    BigDecimal coinAmount = transferDto.getAmount().divide(flow.getNewestPrice(), 6, RoundingMode.HALF_UP);
                    transferDto.setAmount(coinAmount);
                    if ("start".equals(hasStart)) {
                        buyCoinRemain = buyCoinRemain.add(flow.getAmount());
                        // 修改当日可购买量
                        redisUtils.set(AppContants.REDIS_KEY_TRANSFER_POOL_VOL_REMAIN, buyCoinRemain);
                    } else {
                        makeCoinRemain = makeCoinRemain.add(flow.getAmount());
                        buyUsdtMax = buyUsdtMax.add(usdtAmount);
                        // 修改铸池量
                        redisUtils.set(AppContants.REDIS_KEY_MAKE_POOL_CNT, makeCoinRemain);
                        redisUtils.set(AppContants.REDIS_KEY_IDO_USDT_MAX_BUY_DAILY + member.getAddress(), buyUsdtMax);
                    }
                } else {
                    usdtRemain = usdtRemain.add(transferDto.getAmount().multiply(transferDto.getPrice()));
                    saleCoinRemain = saleCoinRemain.add(transferDto.getAmount());
                    // 修改当日U剩余量
                    redisUtils.set(AppContants.REDIS_KEY_USDT_OUT_LIMIT_REMAIN, usdtRemain);
                    // 修改用户24小时可售量
                    redisUtils.set(AppContants.REDIS_KEY_COIN_REMAIN + member.getAddress(), saleCoinRemain);
                }
                dappFundFlowDao.deleteById(transferDto.getId());
            }
        }
src/main/java/cc/mrbird/febs/job/ChainListenerJob.java
@@ -23,9 +23,6 @@
public class ChainListenerJob implements ApplicationRunner {
    @Autowired
    private ContractEventService bscCoinContractEvent;
    @Autowired
    private ContractEventService bscUsdtContractEvent;
    @Autowired
@@ -83,7 +80,6 @@
            BigInteger end = block.add(section);
            log.info("监听:[{} - {}]", block, end);
            ChainService.contractEventListener(block, end, bscUsdtContractEvent, ChainEnum.BSC_USDT_LISTENER.name());
            ChainService.contractEventListener(block, end, bscCoinContractEvent, ChainEnum.BSC_TFC_LISTENER.name());
            block = block.add(section);
            if (block.compareTo(newest) > 0) {
@@ -91,7 +87,6 @@
            }
        }
        ChainService.contractEventListener(block, bscUsdtContractEvent, ChainEnum.BSC_USDT_LISTENER.name());
        ChainService.contractEventListener(block, bscCoinContractEvent, ChainEnum.BSC_TFC_LISTENER.name());
        long end = System.currentTimeMillis();
        log.info("区块链监听启动完成, 消耗时间{}", end - start);
src/main/java/cc/mrbird/febs/tree/MatrixTree.java
@@ -1,16 +1,30 @@
package cc.mrbird.febs.tree;
import com.sun.jmx.remote.internal.ArrayQueue;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayDeque;
import java.util.Queue;
/**
 * @author wzy
 * @date 2022-08-23
 **/
@Slf4j
public class MatrixTree {
    private static final MatrixTree matrixTree = new MatrixTree();
    public static MatrixTree getInstance() {
        return matrixTree;
    }
    // 节点数量
    private int count = 0;
    private MemberNode root;
    public MatrixTree() {}
    public MatrixTree() {
        count = 1;
    }
    public MatrixTree(MemberNode root) {
        count = 1;
@@ -22,6 +36,105 @@
    }
    /**
     * 添加节点到树中
     * 先判断该节点的直推荐人是否在树中,若在则判断该推荐人的矩阵是否完成,若未完成则添加到该矩阵中,若已完成,则完善整个树
     * 如果直推荐人没有在树中,则直接添加到树
     *
     * @param node 待添加的节点
     * @return 返回父节点
     */
    public MemberNode addNode(MemberNode node) {
        if (root == null) {
            root = node;
            return null;
        }
        count++;
        MemberNode parent = getNode(node.getRefererId());
        MemberNode memberNode = null;
        if (parent != null) {
            memberNode = wholeMatrix(parent, node, 2, 1);
        }
        if (memberNode != null) {
            return memberNode;
        }
        MemberNode notWholeNode = getNotWholeNode();
        if (notWholeNode.getLeft() == null) {
            notWholeNode.setLeft(node);
        } else {
            notWholeNode.setRight(node);
        }
        return notWholeNode;
    }
    /**
     * 通过层级遍历,依次获取没有完整左右子节点的节点
     *
     * @return
     */
    public MemberNode getNotWholeNode() {
        ArrayDeque<MemberNode> deque = new ArrayDeque<>();
        deque.add(root);
        MemberNode result = null;
        while(!deque.isEmpty()) {
            int num = deque.size();
            for (int i = 0; i < num; i++) {
                MemberNode memberNode = deque.removeFirst();
                if (memberNode.getLeft() == null || memberNode.getRight() == null) {
                    result = memberNode;
                    break;
                }
                deque.addLast(memberNode.getLeft());
                deque.addLast(memberNode.getRight());
            }
            if (result != null) {
                break;
            }
        }
        return result;
    }
    /**
     * 将节点添加到矩阵。若矩阵已完成,直接返回null
     *
     * @param node 矩阵root节点
     * @param newNode 待添加的节点
     * @param depth 矩阵深度(目前为2)
     * @param index 当前所有深度
     * @return
     */
    public MemberNode wholeMatrix(MemberNode node, MemberNode newNode, int depth, int index) {
        if (node.getLeft() == null) {
            node.setLeft(newNode);
            return node;
        }
        if (node.getRight() == null) {
            node.setRight(newNode);
            return node;
        }
        if (index >= depth) {
            return null;
        }
        MemberNode left = wholeMatrix(node.getLeft(), newNode, depth, index + 1);
        if (left != null) {
            return left;
        }
        return wholeMatrix(node.getRight(), newNode, depth, index + 1);
    }
    /**
     * 查找整个树下对应节点
     * @param param memberId/address/inviteId
     * @return
src/main/java/cc/mrbird/febs/tree/MemberNode.java
@@ -17,10 +17,9 @@
    private String inviteId;
    private String refererId;
    private MemberNode left;
    private MemberNode right;
    // 矩阵
    private List<MemberNode> matrix;
}
src/main/java/cc/mrbird/febs/tree/TreeController.java
New file
@@ -0,0 +1,33 @@
package cc.mrbird.febs.tree;
import cc.mrbird.febs.common.entity.FebsResponse;
import cn.hutool.core.util.RandomUtil;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * @author wzy
 * @date 2022-08-24
 **/
@Api(value = "TreeController", tags = "矩阵树测试")
@Slf4j
@RequestMapping(value = "/test")
@RestController
public class TreeController {
    @GetMapping(value = "/addNode/{id}")
    public FebsResponse addNode(@PathVariable("id") Long id) {
        MatrixTree matrixTree = MatrixTree.getInstance();
        MemberNode memberNode = new MemberNode();
        memberNode.setMemberId(id);
        memberNode.setAddress(RandomUtil.randomString(16));
        memberNode.setInviteId(RandomUtil.randomNumbers(6));
        matrixTree.addNode(memberNode);
        return new FebsResponse().success();
    }
}
src/main/resources/application-dev.yml
@@ -15,25 +15,13 @@
      datasource:
        # 数据源-1,名称为 base
        base:
#          username: db_tfc
#          password: tfc!@#123
#          driver-class-name: com.mysql.cj.jdbc.Driver
#          url: jdbc:mysql://154.91.195.155:3306/db_tfc?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2b8
          username: ct_test
          password: 123456
          driver-class-name: com.mysql.cj.jdbc.Driver
          url: jdbc:mysql://120.27.238.55:3306/db_tfc?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2b8
          url: jdbc:mysql://120.27.238.55:3306/db_sdm?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2b8
  redis:
#    # Redis数据库索引(默认为 0)
#    database: 4
#    # Redis服务器地址
#    host: 154.91.195.155
#    # Redis服务器连接端口
#    port: 6379
#    # Redis 密码
#    password: ann123!@#
    database: 9
    database: 10
    # Redis服务器地址
    host: 120.27.238.55
    # Redis服务器连接端口
@@ -53,10 +41,6 @@
    # 连接超时时间(毫秒)
    timeout: 5000
  rabbitmq:
#    host: 154.91.195.155
#    port: 5672
#    username: abc_user
#    password: abc123
    host: 120.27.238.55
    port: 5672
    username: ct_rabbit
@@ -64,6 +48,6 @@
    publisher-confirm-type: correlated
system:
  online-transfer: true
  chain-listener: true
  online-transfer: false
  chain-listener: false
  trade-job: false
src/test/java/cc/mrbird/febs/TreeTest.java
New file
@@ -0,0 +1,36 @@
package cc.mrbird.febs;
import cc.mrbird.febs.tree.MatrixTree;
import cc.mrbird.febs.tree.MemberNode;
import cn.hutool.core.util.RandomUtil;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
/**
 * @author wzy
 * @date 2022-08-24
 **/
@SpringBootTest
public class TreeTest {
    @Test
    public void matrixTest() {
        MatrixTree matrixTree = MatrixTree.getInstance();
        String refererId = null;
        for (int i = 0; i < 11; i++) {
            String inviteId = RandomUtil.randomString(6);
            if (i == 2) {
                refererId = inviteId;
            }
            MemberNode memberNode = new MemberNode();
            memberNode.setMemberId(Long.parseLong(i + 1 + ""));
            memberNode.setInviteId(inviteId);
            memberNode.setAddress(RandomUtil.randomString(14));
            memberNode.setRefererId(refererId);
            matrixTree.addNode(memberNode);
        }
        System.out.println(System.currentTimeMillis());
    }
}