浏览代码

NFT自动查询上链状态

skyfffire 1 月之前
父节点
当前提交
a2e67b92e1

+ 4 - 0
src/main/java/common/jfinal/AppConfig.java

@@ -13,6 +13,7 @@ import common.model._MappingKit;
 import modules.hello.HelloController;
 import modules.news.NewsController;
 import modules.nftt.NfttController;
+import modules.nftt.NfttStatusMaintenanceTask;
 import modules.order.OrderController;
 import modules.order.OrderService;
 import modules.order.OrderStatusMaintenanceTask;
@@ -105,7 +106,10 @@ public class AppConfig extends JFinalConfig {
         // me.set("asyncTaskExecutor", asyncTaskExecutor); // 不推荐直接用put,使用自己的全局Manager
         // 更好的方式是创建一个单例的 TaskScheduler
         TaskScheduler.init(asyncTaskExecutor);
+        
+        // 数据维护线程
         TaskScheduler.submit(new OrderStatusMaintenanceTask(new OrderService(), new UserService()));
+        TaskScheduler.submit(new NfttStatusMaintenanceTask());
     }
 
     @Override

+ 0 - 59
src/main/java/modules/nftt/NfttService.java

@@ -84,65 +84,6 @@ public class NfttService {
         // Nftt.dao.find() 的参数:第一位是 SQL 字符串,后面是可变参数数组
         // 这里需要将 List<Object> params 转换为 Object[]
         List<Nftt> list = Nftt.dao.find(sqlBuilder.toString(), params.toArray());
-
-        // 遍历列表,处理 BSN 状态更新逻辑
-        // 这个逻辑会影响分页查询的性能,特别是当 BsnSDK.search 是耗时操作时
-        // 建议将其移到异步处理或专门的同步任务中
-        for (Nftt nftt : list) {
-            // bsnTokenId 如果不为空,说明已经处理过上链结果,跳过
-            if (StrKit.notBlank(nftt.getStr("bsn_token_id"))) { // 假设字段名为 bsn_token_id
-                continue;
-            }
-
-            try {
-                // 调用 BSN SDK 查询上链状态
-                // 确保 BsnSDK.search 返回的是 JSONObject,且参数正确
-                JSONObject searchObject = BsnSDK.search("DLTBH_NFT_" + nftt.getLong("id")); // 假设 id 是 Long 类型
-
-                // 记录原始响应,便于调试
-                nftt.set("bsn_status_json", searchObject.toJSONString()); // 假设字段名为 bsn_status_json
-                // 不需要在这里立即 update(),可以在最后处理完所有状态后再批量更新,或在成功时更新
-                // nftt.update(); // 暂时移除,避免频繁更新
-
-                // 校验 BSN SDK 响应
-                if (searchObject.getInteger("code") == null || searchObject.getInteger("code") != 0) {
-                    throw new RuntimeException("BSN API查询失败,返回值与预期不符或code不为0:" + searchObject.toJSONString());
-                }
-
-                JSONObject data = searchObject.getJSONObject("data");
-                if (data == null) {
-                    throw new RuntimeException("BSN API响应数据为空:" + searchObject.toJSONString());
-                }
-
-                Integer status = data.getInteger("status");
-                if (status == null) {
-                    throw new RuntimeException("BSN API响应状态字段缺失:" + searchObject.toJSONString());
-                }
-
-                if (status == 2) { // 假设 status == 2 表示上链失败
-                    nftt.set("bsn_token_id", "-1"); // 标记上链失败
-                    nftt.set("bsn_status_json", searchObject.toJSONString()); // 更新失败状态信息
-                    nftt.update(); // 立即更新失败状态
-                    throw new RuntimeException("BSN 上链失败,链上的错误:" + searchObject.toJSONString());
-                }
-
-                if (status == 1) { // 假设 status == 1 表示上链成功且有 tokenId
-                    String tokenId = data.getString("tokenId");
-                    if (StrKit.isBlank(tokenId)) {
-                        throw new RuntimeException("BSN 上链成功但 tokenId 为空:" + searchObject.toJSONString());
-                    }
-                    nftt.set("bsn_token_id", tokenId);
-                    nftt.set("bsn_status_json", searchObject.toJSONString()); // 更新成功状态信息
-                    nftt.update(); // 立即更新成功状态
-                }
-                // 如果 status 是其他值,可能表示正在处理中,不更新 bsn_token_id
-            } catch (Exception e) {
-                // 记录错误日志,但通常不应该影响整个列表的返回
-                AppConfig.LOGGER.error("查询并更新NFT {} 的BSN状态时出现问题:{}", nftt.getLong("id"), e.getMessage(), e);
-                // 这里可以选择不throw,或者throw RuntimeException让上层捕获
-                // 如果是服务故障,可能需要全局捕获并告警
-            }
-        }
         
         return list;
     }

+ 116 - 0
src/main/java/modules/nftt/NfttStatusMaintenanceTask.java

@@ -0,0 +1,116 @@
+package modules.nftt;
+
+import com.alibaba.fastjson.JSONObject;
+import com.jfinal.kit.StrKit;
+import common.jfinal.AppConfig;
+import common.model.Nftt;
+import common.utils.bsn.BsnSDK;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+public class NfttStatusMaintenanceTask implements Runnable {
+    private static final Logger LOGGER = LoggerFactory.getLogger(NfttStatusMaintenanceTask.class);
+
+    // 任务是否应该停止的标志
+    private volatile boolean running = true; // 使用 volatile 确保多线程可见性
+    
+    public NfttStatusMaintenanceTask() {
+        LOGGER.info("NftStatusMaintenanceTask 实例已创建。");
+    }
+    
+    @Override
+    public void run() {
+        LOGGER.info("NftStatusMaintenanceTask 后台任务开始运行...");
+        while (running) { // 循环直到收到停止指令
+            try {
+                // 每次循环后休眠一段时间,避免CPU空转,并控制检查频率
+                Thread.sleep(TimeUnit.SECONDS.toMillis(10)); // 每 10 秒检查一次
+                
+                // 只处理已支付但未确权的
+                String sql = "SELECT * FROM t_nftt WHERE bsn_token_id IS NULL";
+                flushNftStatus(Nftt.dao.find(sql));
+            } catch (InterruptedException e) {
+                LOGGER.warn("NftStatusMaintenanceTask 因中断而停止。");
+                Thread.currentThread().interrupt(); // 重新设置中断状态
+                running = false; // 退出循环
+            } catch (Exception e) {
+                // 捕获所有其他异常,记录日志,但保持线程继续运行
+                LOGGER.error("NftStatusMaintenanceTask 执行异常: {}", e.getMessage(), e);
+                // 可以在这里添加告警,或在短时间后重试
+                try {
+                    Thread.sleep(TimeUnit.SECONDS.toMillis(5)); // 发生错误时短暂休眠,避免错误日志刷屏
+                } catch (InterruptedException ie) {
+                    Thread.currentThread().interrupt();
+                    running = false;
+                }
+            }
+        }
+        LOGGER.info("NftStatusMaintenanceTask 后台任务已停止运行。");
+    }
+
+    private void flushNftStatus(List<Nftt> list) throws InterruptedException {
+        // 遍历列表,处理 BSN 状态更新逻辑
+        // 这个逻辑会影响分页查询的性能,特别是当 BsnSDK.search 是耗时操作时
+        // 建议将其移到异步处理或专门的同步任务中
+        for (Nftt nftt : list) {
+            // bsnTokenId 如果不为空,说明已经处理过上链结果,跳过
+            if (StrKit.notBlank(nftt.getBsnTokenId())) {
+                continue;
+            }
+
+            try {
+                // 调用 BSN SDK 查询上链状态
+                // 确保 BsnSDK.search 返回的是 JSONObject,且参数正确
+                JSONObject searchObject = BsnSDK.search("DLTBH_NFT_" + nftt.getLong("id")); // 假设 id 是 Long 类型
+
+                // 记录原始响应,便于调试
+                nftt.set("bsn_status_json", searchObject.toJSONString()); // 假设字段名为 bsn_status_json
+                // 不需要在这里立即 update(),可以在最后处理完所有状态后再批量更新,或在成功时更新
+                // nftt.update(); // 暂时移除,避免频繁更新
+
+                // 校验 BSN SDK 响应
+                if (searchObject.getInteger("code") == null || searchObject.getInteger("code") != 0) {
+                    throw new RuntimeException("BSN API查询失败,返回值与预期不符或code不为0:" + searchObject.toJSONString());
+                }
+
+                JSONObject data = searchObject.getJSONObject("data");
+                if (data == null) {
+                    throw new RuntimeException("BSN API响应数据为空:" + searchObject.toJSONString());
+                }
+
+                Integer status = data.getInteger("status");
+                if (status == null) {
+                    throw new RuntimeException("BSN API响应状态字段缺失:" + searchObject.toJSONString());
+                }
+
+                if (status == 2) { // 假设 status == 2 表示上链失败
+                    nftt.set("bsn_token_id", "-1"); // 标记上链失败
+                    nftt.set("bsn_status_json", searchObject.toJSONString()); // 更新失败状态信息
+                    nftt.update(); // 立即更新失败状态
+                    throw new RuntimeException("BSN 上链失败,链上的错误:" + searchObject.toJSONString());
+                }
+
+                if (status == 1) { // 假设 status == 1 表示上链成功且有 tokenId
+                    String tokenId = data.getString("tokenId");
+                    if (StrKit.isBlank(tokenId)) {
+                        throw new RuntimeException("BSN 上链成功但 tokenId 为空:" + searchObject.toJSONString());
+                    }
+                    nftt.set("bsn_token_id", tokenId);
+                    nftt.set("bsn_status_json", searchObject.toJSONString()); // 更新成功状态信息
+                    nftt.update(); // 立即更新成功状态
+                    
+                    AppConfig.LOGGER.error("查询并更新NFT {} 的BSN状态成功{}", nftt.getLong("id"), searchObject);
+                }
+                // 如果 status 是其他值,可能表示正在处理中,不更新 bsn_token_id
+            } catch (Exception e) {
+                // 记录错误日志,但通常不应该影响整个列表的返回
+                AppConfig.LOGGER.error("查询并更新NFT {} 的BSN状态时出现问题:{}", nftt.getLong("id"), e.getMessage(), e);
+                // 这里可以选择不throw,或者throw RuntimeException让上层捕获
+                // 如果是服务故障,可能需要全局捕获并告警
+            }
+        }
+    }
+}