| | |
| | | package cc.mrbird.febs.mall.quartz; |
| | | |
| | | import cc.mrbird.febs.common.enumerates.OrderStatusEnum; |
| | | import cc.mrbird.febs.common.utils.RedisUtils; |
| | | import cc.mrbird.febs.mall.controller.dependentStation.constant.OrderConstants; |
| | | import cc.mrbird.febs.mall.controller.dependentStation.enums.SalesServiceEnums; |
| | | import cc.mrbird.febs.mall.controller.dependentStation.TokenviewWebhookService; |
| | | import cc.mrbird.febs.mall.controller.dependentStation.utils.OkHttpUtil2; |
| | | import cc.mrbird.febs.mall.controller.dependentStation.utils.Trc20TokenviewContentModel; |
| | | import cc.mrbird.febs.mall.controller.dependentStation.utils.Trc20TokenviewModel; |
| | | import cc.mrbird.febs.mall.entity.DataDictionaryCustom; |
| | | import cc.mrbird.febs.mall.entity.MallOrderInfo; |
| | | import cc.mrbird.febs.mall.mapper.DataDictionaryCustomMapper; |
| | | import cc.mrbird.febs.mall.mapper.MallOrderInfoMapper; |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.hutool.core.util.ObjectUtil; |
| | | import cn.hutool.core.util.StrUtil; |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
| | | import org.springframework.scheduling.annotation.Scheduled; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.nio.charset.StandardCharsets; |
| | | import java.util.Date; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | @Slf4j |
| | | @Component |
| | | @ConditionalOnProperty(prefix = "system", name = "job", havingValue = "true") |
| | | public class ChatTrc20ChargeOkLinkTask { |
| | | |
| | | @Resource |
| | | private TokenviewWebhookService tokenviewWebhookService; |
| | | |
| | | @Resource |
| | | private RedisUtils redisUtils; |
| | | @Resource |
| | | private MallOrderInfoMapper mallOrderInfoMapper; |
| | | @Resource |
| | | private DataDictionaryCustomMapper dataDictionaryCustomMapper; |
| | | /** |
| | | * 五分钟 毫秒 |
| | | * 30分钟(毫秒) |
| | | */ |
| | | private final static long TIME_INTERVAL = 300000*6; |
| | | private final static long TIME_INTERVAL = 300000 * 6; |
| | | |
| | | private final static String TRC20_TRANSFER_API = "https://apilist.tronscanapi.com/api/token_trc20/transfers"; |
| | | |
| | | public final static String TRC20_CONTRACT_ADDRESS = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t"; |
| | | |
| | | private final static String TRON_API_KEY = "32c9841d-19ae-48cf-a535-cf56d9d25f1c"; |
| | | private final static String TRON_API_KEY = "X8Np5zbDuhmG6cntbhLu"; |
| | | |
| | | private static Map<String, String> REQUEST_HEADER = new HashMap<>(); |
| | | static { |
| | | REQUEST_HEADER.put("TRON-PRO-API-KEY", TRON_API_KEY); |
| | | } |
| | | |
| | | @Scheduled(cron = "0 0/5 * * * ? ") |
| | | /** |
| | | * 每分钟轮询 Tokenview API,查询 TRC20-USDT 充值记录 |
| | | * 充值匹配逻辑委托给 {@link TokenviewWebhookService} |
| | | */ |
| | | @Scheduled(cron = "0 0/1 * * * ? ") |
| | | public void recharge() { |
| | | // 查询过去5分钟的记录 |
| | | |
| | | DataDictionaryCustom dataDictionaryCustom = dataDictionaryCustomMapper.selectDicDataByTypeAndCode( |
| | | SalesServiceEnums.TRC_ADDRESS.getType(), |
| | | SalesServiceEnums.TRC_ADDRESS.getCode() |
| | | ); |
| | | String receiveAddress = dataDictionaryCustom.getValue(); |
| | | String receiveAddress = tokenviewWebhookService.getReceiveAddress(); |
| | | if (receiveAddress == null) { |
| | | log.error("请先配置系统地址"); |
| | | return; |
| | | } |
| | | String url = "https://services.tokenview.io/vipapi/trx/address/tokentrans/"+receiveAddress+"/TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t/1/10"; |
| | | // 定时任务的执行时间和转账时间区间间隔5分钟 |
| | | |
| | | // 每次将上次的时间存入redis,第一次使用默认的时间 |
| | | String url = "https://services.tokenview.io/vipapi/trx/address/tokentrans/" |
| | | + receiveAddress + "/TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t/1/10"; |
| | | |
| | | long endTime = System.currentTimeMillis(); |
| | | long startTime = endTime-TIME_INTERVAL; |
| | | long startTime = endTime - TIME_INTERVAL; |
| | | |
| | | System.out.println(new Date()+" 自动充值定时任务fyTrc20RechargeTokenviewTask "+startTime+" "+endTime); |
| | | // 当前的充值地址 TRC20USDT_ADDRESS |
| | | log.info("自动充值定时任务 startTime={}, endTime={}", startTime, endTime); |
| | | |
| | | Map<String, String> param = new HashMap<>(); |
| | | param.put("timestampStart", startTime + ""); |
| | | param.put("timestampEnd", endTime + ""); |
| | | param.put("toAddress", receiveAddress); |
| | | param.put("apikey", TRON_API_KEY); |
| | | long l = System.currentTimeMillis(); |
| | | |
| | | long start = System.currentTimeMillis(); |
| | | byte[] bytes = OkHttpUtil2.doGetSingle(url, REQUEST_HEADER, param, "application/json"); |
| | | |
| | | if (ObjectUtil.isEmpty( bytes )) { |
| | | log.error("FyTrc20RechargeTask查询链上数据返回为空,传参:{}",param); |
| | | if (ObjectUtil.isEmpty(bytes)) { |
| | | log.error("查询链上数据返回为空, param={}", param); |
| | | return; |
| | | } |
| | | |
| | | String s = new String(bytes, StandardCharsets.UTF_8); |
| | | log.info("查询到的充值记录:{}", s); |
| | | Trc20TokenviewModel trc20TransfersModel = JSONObject.parseObject(s, Trc20TokenviewModel.class); |
| | | List<Trc20TokenviewContentModel> tokenTransfers = trc20TransfersModel.getData(); |
| | | String response = new String(bytes, StandardCharsets.UTF_8); |
| | | log.info("查询到的充值记录:{}", response); |
| | | |
| | | Trc20TokenviewModel model = JSONObject.parseObject(response, Trc20TokenviewModel.class); |
| | | List<Trc20TokenviewContentModel> tokenTransfers = model.getData(); |
| | | if (CollUtil.isEmpty(tokenTransfers)) { |
| | | //logger.error("FyTrc20RechargeTask查询链上数据返回没有token转账,返回结果:{}",s); |
| | | return; |
| | | } |
| | | long l1 = System.currentTimeMillis(); |
| | | log.info("查询trc耗时:{}", (l1 - l)); |
| | | log.info("查询到的充值记录:{}", JSONObject.toJSONString(trc20TransfersModel)); |
| | | log.info("时间区间,start:{},end:{}", startTime, endTime); |
| | | // 有记录 |
| | | for (Trc20TokenviewContentModel tokenTransfer : tokenTransfers) { |
| | | log.info("链上时间:{}", tokenTransfer.getTime()); |
| | | // 从数据库 |
| | | String transactionId = tokenTransfer.getTxid(); |
| | | List<MallOrderInfo> chatOrders = mallOrderInfoMapper.selectList( |
| | | Wrappers.lambdaQuery(MallOrderInfo.class) |
| | | .eq(MallOrderInfo::getTradeHash, transactionId) |
| | | ); |
| | | if(CollUtil.isNotEmpty(chatOrders)){ |
| | | log.info("扫描到HASH已使用:{}",transactionId); |
| | | continue; |
| | | } |
| | | // 金额 |
| | | String quant = tokenTransfer.getValue(); |
| | | BigDecimal amount = new BigDecimal(quant).divide(new BigDecimal("1000000")).setScale(2, RoundingMode.DOWN); |
| | | String amountKey = OrderConstants.TRC20_ORDER_KEY + amount; |
| | | String orderCode = redisUtils.getString(amountKey); |
| | | if (StrUtil.isBlank(orderCode)) { |
| | | log.info("Redis未扫描到充值金额:{}",transactionId); |
| | | continue; |
| | | } |
| | | MallOrderInfo chatOrder = mallOrderInfoMapper.selectOne( |
| | | Wrappers.lambdaQuery(MallOrderInfo.class) |
| | | .eq(MallOrderInfo::getOrderNo, orderCode) |
| | | ); |
| | | if(chatOrder==null){ |
| | | log.error("未找到订单:{}",orderCode); |
| | | continue; |
| | | } |
| | | if(OrderStatusEnum.WAIT_PAY.getValue() != chatOrder.getStatus()){ |
| | | log.error("订单不是待充值状态: {},订单编号:{}",transactionId,orderCode); |
| | | continue; |
| | | } |
| | | |
| | | mallOrderInfoMapper.update( |
| | | null, |
| | | Wrappers.lambdaUpdate(MallOrderInfo.class) |
| | | .set(MallOrderInfo::getStatus, OrderStatusEnum.WAIT_SHIPPING.getValue()) |
| | | .set(MallOrderInfo::getTradeHash, transactionId) |
| | | .set(MallOrderInfo::getPayTime, new Date()) |
| | | .set(MallOrderInfo::getPayResult, "1") |
| | | .eq(MallOrderInfo::getId, chatOrder.getId()) |
| | | log.info("查询耗时:{}ms, 记录数:{}, 时间区间 [{}, {}]", |
| | | System.currentTimeMillis() - start, tokenTransfers.size(), startTime, endTime); |
| | | |
| | | for (Trc20TokenviewContentModel transfer : tokenTransfers) { |
| | | log.info("链上时间: {}", transfer.getTime()); |
| | | String result = tokenviewWebhookService.processPollTransaction( |
| | | transfer.getTxid(), |
| | | transfer.getValue(), |
| | | receiveAddress |
| | | ); |
| | | redisUtils.del(amountKey); |
| | | log.info("Redis扫描到充值记录:{},订单编号:{}",transactionId,orderCode); |
| | | log.info("轮询处理结果: {}", result); |
| | | } |
| | | } |
| | | |
| | | |
| | | public static void main(String[] args) { |
| | | String receiveAddress = "TExto1UjtFcXKw5QdJDRqtx7wTQ15D37GD"; |
| | | String url = "https://services.tokenview.io/vipapi/trx/address/tokentrans/"+receiveAddress+"/TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t/1/10"; |
| | | // 定时任务的执行时间和转账时间区间间隔5分钟 |
| | | String url = "https://services.tokenview.io/vipapi/trx/address/tokentrans/" |
| | | + receiveAddress + "/TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t/1/10"; |
| | | |
| | | // 每次将上次的时间存入redis,第一次使用默认的时间 |
| | | long endTime = System.currentTimeMillis(); |
| | | long startTime = endTime-TIME_INTERVAL; |
| | | long startTime = endTime - TIME_INTERVAL; |
| | | |
| | | System.out.println(new Date()+" 自动充值定时任务fyTrc20RechargeTokenviewTask "+startTime+" "+endTime); |
| | | // 当前的充值地址 TRC20USDT_ADDRESS |
| | | System.out.println(new Date() + " 自动充值定时任务 " + startTime + " " + endTime); |
| | | |
| | | Map<String, String> param = new HashMap<>(); |
| | | param.put("timestampStart", startTime + ""); |
| | | param.put("timestampEnd", endTime + ""); |
| | | param.put("toAddress", receiveAddress); |
| | | param.put("apikey", TRON_API_KEY); |
| | | long l = System.currentTimeMillis(); |
| | | |
| | | byte[] bytes = OkHttpUtil2.doGetSingle(url, REQUEST_HEADER, param, "application/json"); |
| | | |
| | | if (bytes == null) { |
| | | log.error("FyTrc20RechargeTask查询链上数据返回为空,传参:{}",param); |
| | | System.out.println("查询链上数据返回为空, param=" + param); |
| | | return; |
| | | } |
| | | |