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.DappWalletService; import cc.mrbird.febs.dapp.utils.BoxUtil; import cc.mrbird.febs.dapp.vo.ActiveNftListVo; import cc.mrbird.febs.dapp.vo.WalletInfoVo; import cc.mrbird.febs.rabbit.producer.ChainProducer; 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 DappNftActivationDao dappNftActivationDao; private final ChainProducer chainProducer; @Override public WalletInfoVo walletInfo() { DappMemberEntity member = LoginUserUtil.getAppUser(); DappMemberEntity memberInfo = dappMemberDao.selectById(member.getId()); WalletInfoVo walletInfo = new WalletInfoVo(); List direct = dappMemberDao.selectChildMemberDirectOrNot(member.getInviteId(), 1); DappWalletCoinEntity walletCoin = dappWalletCoinDao.selectByMemberId(member.getId()); DappWalletMineEntity walletMine = dappWalletMineDao.selectByMemberId(member.getId()); walletInfo.setDirectCnt(direct.size()); walletInfo.setInviteId(member.getInviteId()); walletInfo.setBalance(walletCoin.getAvailableAmount()); walletInfo.setHasBuy(memberInfo.getActiveStatus()); walletInfo.setOutCnt(memberInfo.getOutCnt()); walletInfo.setProfit(dappFundFlowDao.selectProfitAmountByMemberId(member.getId())); walletInfo.setTFCbalance(walletMine.getAvailableAmount()); 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 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(); member = dappMemberDao.selectById(member.getId()); if (member.getActiveStatus() == 1) { throw new FebsException("Do not repeat purchase"); } if (transferDto.getBuyType() == 1) { DappWalletCoinEntity walletCoin = dappWalletCoinDao.selectByMemberId(member.getId()); if (transferDto.getAmount().compareTo(walletCoin.getAvailableAmount()) > 0) { throw new FebsException("Balance Not Enough"); } updateWalletCoinWithLock(transferDto.getAmount().negate(), member.getId()); DappFundFlowEntity fundFlow = new DappFundFlowEntity(member.getId(), transferDto.getAmount().negate(), 1, 2, transferDto.getFee(), transferDto.getTxHash()); dappFundFlowDao.insert(fundFlow); chainProducer.sendAchieveTreeMsg(member.getId()); } else { int type = 1; // 1-认购 2-充值tfc if (transferDto.getType() == 2) { type = 6; } if (transferDto.getId() == null) { DappFundFlowEntity fundFlow = new DappFundFlowEntity(member.getId(), transferDto.getAmount(), type, 1, transferDto.getFee(), transferDto.getTxHash()); 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) { dappFundFlowDao.deleteById(transferDto.getId()); } } } return null; } @Override public Map calPrice(PriceDto priceDto) { String priceStr = redisUtils.getString(AppContants.REDIS_KEY_TFC_NEW_PRICE); Map data = new HashMap<>(); data.put("fee", new BigDecimal(priceStr)); return data; } @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")); } PriceDto priceDto = new PriceDto(); priceDto.setAmount(BigDecimal.ZERO); Map price = calPrice(priceDto); DappFundFlowEntity fundFlow = new DappFundFlowEntity(member.getId(), new BigDecimal(activeDto.getCount()), 8, 1, BigDecimal.ZERO, activeDto.getTxHash()); fundFlow.setTargetAmount(price.get("x").multiply(new BigDecimal(activeDto.getCount()))); 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; } @Override public void withdraw(WithdrawDto withdrawDto) { DappMemberEntity member = LoginUserUtil.getAppUser(); updateWalletCoinWithLock(withdrawDto.getAmount().negate(), member.getId()); updateWalletMineWithLock(withdrawDto.getFee(), member.getId()); DappFundFlowEntity feeFlow = new DappFundFlowEntity(member.getId(), withdrawDto.getFee().negate(), 7, 2, null, null); dappFundFlowDao.insert(feeFlow); DappFundFlowEntity fundFlow = new DappFundFlowEntity(member.getId(), withdrawDto.getAmount().negate(), 5, 2, withdrawDto.getFee(), null); dappFundFlowDao.insert(fundFlow); String hash = ChainService.getInstance(ChainEnum.BSC_USDT.name()).transfer(member.getAddress(), withdrawDto.getAmount()); fundFlow.setToHash(hash); dappFundFlowDao.updateById(fundFlow); chainProducer.sendTfcFee(withdrawDto.getFee().toPlainString()); } @Override public void updateWalletCoinWithLock(BigDecimal amount, Long memberId) { boolean isSuccess = false; while(!isSuccess) { DappWalletCoinEntity walletCoin = dappWalletCoinDao.selectByMemberId(memberId); walletCoin.setTotalAmount(walletCoin.getTotalAmount().add(amount)); walletCoin.setAvailableAmount(walletCoin.getAvailableAmount().add(amount)); int i = dappWalletCoinDao.updateWithLock(walletCoin); if (i > 0) { isSuccess = true; } } } @Override public void updateWalletMineWithLock(BigDecimal amount, Long memberId) { boolean isSuccess = false; while(!isSuccess) { DappWalletCoinEntity walletCoin = dappWalletCoinDao.selectByMemberId(memberId); walletCoin.setTotalAmount(walletCoin.getTotalAmount().add(amount)); walletCoin.setAvailableAmount(walletCoin.getAvailableAmount().add(amount)); int i = dappWalletCoinDao.updateWithLock(walletCoin); if (i > 0) { isSuccess = true; } } } }