From 39f6361e29f48a1e302486fb1d27a42181e199e5 Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Thu, 18 Jun 2026 11:54:45 +0800
Subject: [PATCH] feat(mall): 集成 Tokenview Webhook 服务实现 TRC20 充值处理
---
src/main/java/cc/mrbird/febs/mall/quartz/ChatTrc20ChargeOkLinkTask.java | 143 ++++++++++++++---------------------------------
1 files changed, 43 insertions(+), 100 deletions(-)
diff --git a/src/main/java/cc/mrbird/febs/mall/quartz/ChatTrc20ChargeOkLinkTask.java b/src/main/java/cc/mrbird/febs/mall/quartz/ChatTrc20ChargeOkLinkTask.java
index 329b4c4..c15c01e 100644
--- a/src/main/java/cc/mrbird/febs/mall/quartz/ChatTrc20ChargeOkLinkTask.java
+++ b/src/main/java/cc/mrbird/febs/mall/quartz/ChatTrc20ChargeOkLinkTask.java
@@ -1,51 +1,36 @@
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 = "X8Np5zbDuhmG6cntbhLu";
@@ -54,125 +39,83 @@
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;
}
--
Gitblit v1.9.1