Administrator
6 days ago cf8dc9ea458b5efc54d6d6d9d8e89db49ca6cf0f
src/main/java/cc/mrbird/febs/mall/quartz/ChatTrc20ChargeOkLinkTask.java
@@ -1,178 +1,121 @@
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 TRON_API_KEY = "fq6S2XK5zfNccu6QfGCi";
    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);
    }
    /**
     * 每分钟轮询 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;
        }
        //https://services.tokenview.io/vipapi/trx/token/TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t
        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()+" 自动充值定时任务 "+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("查询链上数据返回为空,传参:{}",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;
        }