package cc.mrbird.febs.dapp.service.impl; import cc.mrbird.febs.common.configure.i18n.MessageSourceUtils; import cc.mrbird.febs.common.contants.AppContants; import cc.mrbird.febs.common.entity.QueryRequest; import cc.mrbird.febs.common.exception.FebsException; import cc.mrbird.febs.common.utils.FebsUtil; import cc.mrbird.febs.common.utils.LoginUserUtil; import cc.mrbird.febs.common.utils.RedisUtils; import cc.mrbird.febs.dapp.chain.ChainEnum; import cc.mrbird.febs.dapp.chain.ChainService; import cc.mrbird.febs.dapp.chain.ContractChainService; import cc.mrbird.febs.dapp.dto.*; import cc.mrbird.febs.dapp.entity.*; import cc.mrbird.febs.dapp.mapper.*; import cc.mrbird.febs.dapp.service.DappSystemService; import cc.mrbird.febs.dapp.service.DappWalletService; import cc.mrbird.febs.dapp.utils.BoxUtil; import cc.mrbird.febs.dapp.vo.ActiveNftListVo; import cc.mrbird.febs.dapp.vo.WalletInfoVo; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DateField; import cn.hutool.core.date.DateUnit; import cn.hutool.core.date.DateUtil; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.*; /** * @author * @date 2022-03-18 **/ @Slf4j @Service @RequiredArgsConstructor public class DappWalletServiceImpl implements DappWalletService { private final DappMemberDao dappMemberDao; private final DappWalletMineDao dappWalletMineDao; private final DappWalletCoinDao dappWalletCoinDao; private final DappFundFlowDao dappFundFlowDao; private final DappAccountMoneyChangeDao dappAccountMoneyChangeDao; private final RedisUtils redisUtils; private final DataDictionaryCustomMapper dataDictionaryCustomMapper; private final DappSystemService dappSystemService; private final DappNftActivationDao dappNftActivationDao; @Override public WalletInfoVo walletInfo() { DappMemberEntity member = LoginUserUtil.getAppUser(); Map map = dappFundFlowDao.selectAmountTotalByType(member.getId()); WalletInfoVo walletInfo = new WalletInfoVo(); List direct = dappMemberDao.selectChildMemberDirectOrNot(member.getInviteId(), 1); List notDirect = dappMemberDao.selectChildMemberDirectOrNot(member.getInviteId(), 2); BigDecimal childHoldAmount = dappMemberDao.selectChildHoldAmount(member.getInviteId()); DappWalletCoinEntity walletCoin = dappWalletCoinDao.selectByMemberId(member.getId()); walletInfo.setTotalChild(notDirect.size()); walletInfo.setDirectCnt(direct.size()); walletInfo.setTotalChildCoin(childHoldAmount); walletInfo.setTeamReward(map.get("teamReward")); walletInfo.setMiningAmount(map.get("mine")); walletInfo.setInviteId(member.getInviteId()); walletInfo.setBoxCnt(walletCoin.getBoxCnt()); return walletInfo; } @Override @Transactional(rollbackFor = Exception.class) public void change(WalletOperateDto walletOperateDto) { DappMemberEntity member = LoginUserUtil.getAppUser(); BigDecimal ethUsdtPrice = (BigDecimal) redisUtils.get(AppContants.REDIS_KEY_ETH_NEW_PRICE); DappWalletMineEntity walletMine = dappWalletMineDao.selectByMemberId(member.getId()); if (walletOperateDto.getAmount().compareTo(walletMine.getAvailableAmount()) > 0) { throw new FebsException("可用金额不足"); } DappFundFlowEntity fund = new DappFundFlowEntity(member.getId(), walletOperateDto.getAmount(), AppContants.MONEY_TYPE_CHANGE, null, null); dappFundFlowDao.insert(fund); BigDecimal preEthAmount = walletMine.getAvailableAmount(); // TODO 并发加悲观锁 // 更新eth金额 walletMine.setAvailableAmount(walletMine.getAvailableAmount().subtract(walletOperateDto.getAmount())); dappWalletMineDao.updateById(walletMine); DappWalletCoinEntity walletCoin = dappWalletCoinDao.selectByMemberId(member.getId()); BigDecimal preUsdtAmount = walletCoin.getAvailableAmount(); // 更新usdt金额 BigDecimal usdt = walletOperateDto.getAmount().multiply(ethUsdtPrice); walletCoin.setAvailableAmount(walletCoin.getAvailableAmount().add(usdt)); walletCoin.setTotalAmount(walletCoin.getTotalAmount().add(usdt)); dappWalletCoinDao.updateById(walletCoin); DappAccountMoneyChangeEntity ethChange = new DappAccountMoneyChangeEntity(member.getId(), preEthAmount, walletOperateDto.getAmount(), walletMine.getAvailableAmount(), "ETH兑换USDT-ETH, 兑换价格为:" + ethUsdtPrice, AppContants.MONEY_TYPE_CHANGE); DappAccountMoneyChangeEntity usdtChange = new DappAccountMoneyChangeEntity(member.getId(), preUsdtAmount, usdt, walletCoin.getAvailableAmount(), "ETH兑换USDT-USDT, 兑换价格为:" + ethUsdtPrice, AppContants.MONEY_TYPE_CHANGE); dappAccountMoneyChangeDao.insert(ethChange); dappAccountMoneyChangeDao.insert(usdtChange); } @Override @Transactional(rollbackFor = Exception.class) public void withdraw(WalletOperateDto walletOperateDto) { DappMemberEntity member = LoginUserUtil.getAppUser(); List fundFlows = dappFundFlowDao.selectListForMemberAndDay(member.getId(), 2); if (CollUtil.isNotEmpty(fundFlows)) { throw new FebsException("一天只能提现一次"); } DappWalletCoinEntity walletCoin = dappWalletCoinDao.selectByMemberId(member.getId()); if (walletOperateDto.getAmount().compareTo(walletCoin.getAvailableAmount()) > 0) { throw new FebsException("可用金额不足"); } Integer fee = (Integer) redisUtils.get(AppContants.REDIS_KEY_CHANGE_FEE); //TODO 并发加悲观锁 BigDecimal preAmount = walletCoin.getAvailableAmount(); walletCoin.setAvailableAmount(walletCoin.getAvailableAmount().subtract(walletOperateDto.getAmount())); walletCoin.setFrozenAmount(walletCoin.getFrozenAmount().add(walletOperateDto.getAmount())); dappWalletCoinDao.updateById(walletCoin); DappFundFlowEntity fund = new DappFundFlowEntity(member.getId(), walletOperateDto.getAmount(), AppContants.MONEY_TYPE_WITHDRAWAL, 1, new BigDecimal(fee)); dappFundFlowDao.insert(fund); DappAccountMoneyChangeEntity usdtChange = new DappAccountMoneyChangeEntity(member.getId(), preAmount, walletOperateDto.getAmount(), walletCoin.getAvailableAmount(), "USDT申请提现", AppContants.MONEY_TYPE_WITHDRAWAL); dappAccountMoneyChangeDao.insert(usdtChange); } @Override public List recordInPage(RecordInPageDto recordInPageDto) { Page page = new Page<>(recordInPageDto.getPageNum(), recordInPageDto.getPageSize()); DappMemberEntity member = LoginUserUtil.getAppUser(); DappFundFlowEntity dappFundFlowEntity = new DappFundFlowEntity(); if (recordInPageDto.getType() != null && recordInPageDto.getType() != 0) { dappFundFlowEntity.setType(recordInPageDto.getType()); } dappFundFlowEntity.setMemberId(member.getId()); dappFundFlowEntity.setStatus(2); IPage records = dappFundFlowDao.selectInPage(page, dappFundFlowEntity); return records.getRecords(); } @Override public IPage fundFlowInPage(DappFundFlowEntity dappFundFlowEntity, QueryRequest request) { Page page = new Page<>(request.getPageNum(), request.getPageSize()); return dappFundFlowDao.selectInPage(page, dappFundFlowEntity); } @Override @Transactional(rollbackFor = Exception.class) public void withdrawAgreeOrNot(Long id, int type) { DappFundFlowEntity fundFlow = dappFundFlowDao.selectById(id); if (fundFlow == null) { throw new FebsException("数据不存在"); } DappWalletCoinEntity walletCoin = dappWalletCoinDao.selectByMemberId(fundFlow.getMemberId()); if (type == 1) { walletCoin.setFrozenAmount(walletCoin.getFrozenAmount().subtract(fundFlow.getAmount())); walletCoin.setTotalAmount(walletCoin.getTotalAmount().subtract(fundFlow.getAmount())); fundFlow.setStatus(DappFundFlowEntity.WITHDRAW_STATUS_AGREE); } else if (type == 2) { BigDecimal preAmount = walletCoin.getAvailableAmount(); walletCoin.setFrozenAmount(walletCoin.getFrozenAmount().subtract(fundFlow.getAmount())); walletCoin.setAvailableAmount(walletCoin.getAvailableAmount().add(fundFlow.getAmount())); DappAccountMoneyChangeEntity accountMoneyChange = new DappAccountMoneyChangeEntity(walletCoin.getMemberId(), preAmount, fundFlow.getAmount(), walletCoin.getAvailableAmount(), "提现申请被驳回", 2); fundFlow.setStatus(DappFundFlowEntity.WITHDRAW_STATUS_DISAGREE); dappAccountMoneyChangeDao.insert(accountMoneyChange); } else { throw new FebsException("参数错误"); } dappWalletCoinDao.updateById(walletCoin); dappFundFlowDao.updateById(fundFlow); } @Override public IPage walletCoinInPage(DappWalletCoinEntity walletCoin, QueryRequest request) { Page page = new Page<>(request.getPageNum(), request.getPageSize()); return dappWalletCoinDao.selectInPage(walletCoin, page); } @Override public IPage walletMineInPage(DappWalletMineEntity walletMine, QueryRequest request) { Page page = new Page<>(request.getPageNum(), request.getPageSize()); return dappWalletMineDao.selectInPage(walletMine, page); } @Override public IPage accountMoneyChangeInPage(DappAccountMoneyChangeEntity change, QueryRequest request) { Page page = new Page<>(request.getPageNum(), request.getPageSize()); return dappAccountMoneyChangeDao.selectInPage(change, page); } @Override 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()); } } return null; } @Override public Map calPrice(PriceDto priceDto) { DappMemberEntity member = LoginUserUtil.getAppUser(); if (!dappSystemService.systemHasStart()) { HashMap map = new HashMap<>(); map.put("x", new BigDecimal("0.05")); map.put("y", new BigDecimal("0.05")); return map; } ContractChainService tfcInstance = ChainService.getInstance(ChainEnum.BSC_TFC.name()); // u剩余数量 BigDecimal sourceU = ChainService.getInstance(ChainEnum.BSC_USDT.name()).balanceOf(ChainEnum.BSC_USDT_SOURCE.getAddress()); // 源池代币剩余数量 BigDecimal sourceCoin = tfcInstance.balanceOf(ChainEnum.BSC_USDT_SOURCE.getAddress()); // 用户卖出数量 BigDecimal coin = priceDto.getAmount(); BigDecimal x = sourceU.divide(sourceCoin, tfcInstance.decimals(), RoundingMode.HALF_UP); BigDecimal y = sourceU.divide(sourceCoin.add(coin), tfcInstance.decimals(), RoundingMode.HALF_UP); log.info("购买价格:{}, 出卖价格:{}", x, y); HashMap map = new HashMap<>(); map.put("x", x); map.put("y", y); return map; } @Override public ActiveNftListVo boxSurprise() { DappMemberEntity member = LoginUserUtil.getAppUser(); DappWalletCoinEntity walletCoin = dappWalletCoinDao.selectByMemberId(member.getId()); if (walletCoin.getBoxCnt() < 1) { throw new FebsException(MessageSourceUtils.getString("box_surprise_001")); } walletCoin.setBoxCnt(walletCoin.getBoxCnt() - 1); dappWalletCoinDao.updateById(walletCoin); BoxUtil.Box box = BoxUtil.openBox(); // DappFundFlowEntity boxFundFlow = new DappFundFlowEntity(member.getId(), new BigDecimal(box.getIndex()), 6, 2, BigDecimal.ZERO); // dappFundFlowDao.insert(boxFundFlow); // // new Thread(() -> { // try { // int count = box.getIndex(); // while (count > 0) { // ChainService.getInstance(ChainEnum.BSC_NFT_SDC.name()).safeMintNFT(member.getAddress()); // Thread.sleep(5000); // count--; // } // } catch (Exception e) { // e.printStackTrace(); // log.error("发放卡牌错误", e); // } // }).start(); Date time = new Date(); Date expire = DateUtil.offset(time, DateField.HOUR, 2); DappNftActivation nftActivation = new DappNftActivation(); nftActivation.setMemberId(member.getId()); nftActivation.setCount(box.getIndex()); nftActivation.setOpenTime(time); nftActivation.setExpireTime(expire); nftActivation.setStatus(1); dappNftActivationDao.insert(nftActivation); ActiveNftListVo nft = new ActiveNftListVo(); nft.setCount(box.getIndex()); nft.setId(nftActivation.getId()); nft.setRemain(DateUtil.between(time, expire, DateUnit.SECOND)); return nft; } @Override @Transactional(rollbackFor = Exception.class) public synchronized void activeNft(ActiveDto activeDto) { DappMemberEntity member = LoginUserUtil.getAppUser(); DappNftActivation nftActive = dappNftActivationDao.selectById(activeDto.getId()); if (nftActive == null) { throw new FebsException(MessageSourceUtils.getString("nft_active_003")); } if (nftActive.getCount() < activeDto.getCount()) { throw new FebsException(MessageSourceUtils.getString("nft_active_004")); } if (DateUtil.between(new Date(), nftActive.getExpireTime(), DateUnit.SECOND) < 0) { throw new FebsException(MessageSourceUtils.getString("nft_active_005")); } if (nftActive.getStatus() != 1) { throw new FebsException(MessageSourceUtils.getString("nft_active_001")); } DappFundFlowEntity fundFlow = new DappFundFlowEntity(member.getId(), new BigDecimal(activeDto.getCount()), 8, 1, BigDecimal.ZERO, activeDto.getTxHash()); fundFlow.setNewestPrice(AppContants.NFT_ACTIVE_PRICE.multiply(BigDecimal.valueOf(nftActive.getCount()))); dappFundFlowDao.insert(fundFlow); int count = nftActive.getCount() - activeDto.getCount(); if (count == 0) { nftActive.setStatus(3); } nftActive.setCount(count); nftActive.setHash(activeDto.getTxHash()); dappNftActivationDao.updateById(nftActive); } @Override public List findUnActiveNftList() { DappMemberEntity member = LoginUserUtil.getAppUser(); List list = new ArrayList<>(); UpdateWrapper query = new UpdateWrapper<>(); query.eq("status", 1); query.eq("member_id", member.getId()); query.ge("expire_time", new Date()); List nftActivations = dappNftActivationDao.selectList(query); if (CollUtil.isEmpty(nftActivations)) { return list; } Date now = new Date(); nftActivations.forEach(nft -> { ActiveNftListVo nftVo = new ActiveNftListVo(); nftVo.setId(nft.getId()); nftVo.setRemain(DateUtil.between(now, nft.getExpireTime(), DateUnit.SECOND, false)); nftVo.setCount(nft.getCount()); list.add(nftVo); }); return list; } }