Browse Source

notion文档创建优化

wjj 5 days ago
parent
commit
e79fe8002c

+ 35 - 5
fs-service/src/main/java/com/fs/notion/client/NotionClient.java

@@ -25,11 +25,19 @@ public class NotionClient implements AutoCloseable {
     private final String apiKey;
     private final OkHttpClient httpClient;
     private final ObjectMapper objectMapper;
+    private final int maxRetries;
+    private final long retryBaseDelayMs;
 
     public NotionClient(String apiKey) {
+        this(apiKey, 0, 0);
+    }
+
+    public NotionClient(String apiKey, int maxRetries, long retryBaseDelayMs) {
         this.apiKey = apiKey;
         this.httpClient = new OkHttpClient();
         this.objectMapper = new ObjectMapper();
+        this.maxRetries = maxRetries;
+        this.retryBaseDelayMs = retryBaseDelayMs;
     }
 
     @Override
@@ -134,12 +142,34 @@ public class NotionClient implements AutoCloseable {
     }
 
     private JsonNode executeRequest(Request request) throws IOException {
-        try (Response response = httpClient.newCall(request).execute()) {
-            String responseBody = response.body() != null ? response.body().string() : "{}";
-            if (!response.isSuccessful()) {
-                throw new IOException("Notion API 请求失败 [" + response.code() + "]: " + responseBody);
+        int attempt = 0;
+        while (true) {
+            Response response = httpClient.newCall(request).execute();
+            try {
+                String responseBody = response.body() != null ? response.body().string() : "{}";
+                int code = response.code();
+
+                if (code == 429 && attempt < maxRetries) {
+                    long delayMs = retryBaseDelayMs * (1L << attempt);
+                    log.warn("[Notion] API 限流 (429),第 {}/{} 次重试,等待 {}ms", attempt + 1, maxRetries, delayMs);
+                    try {
+                        Thread.sleep(delayMs);
+                    } catch (InterruptedException e) {
+                        Thread.currentThread().interrupt();
+                        throw new IOException("重试被中断", e);
+                    }
+                    attempt++;
+                    continue;
+                }
+
+                if (!response.isSuccessful()) {
+                    throw new IOException("Notion API 请求失败 [" + code + "]: " + responseBody);
+                }
+
+                return objectMapper.readTree(responseBody);
+            } finally {
+                response.close();
             }
-            return objectMapper.readTree(responseBody);
         }
     }
 

+ 4 - 0
fs-service/src/main/java/com/fs/notion/config/NotionConfig.java

@@ -17,4 +17,8 @@ public class NotionConfig {
     private String domain = "syysy.notion.site";
     /** 批量创建线程数 */
     private int threadCount = 8;
+    /** API 限流重试次数 */
+    private int retryCount = 3;
+    /** 重试基础间隔(毫秒),实际间隔 = baseDelay * 2^retry */
+    private long retryBaseDelayMs = 2000;
 }

+ 18 - 15
fs-service/src/main/java/com/fs/notion/service/impl/NotionServiceImpl.java

@@ -38,9 +38,9 @@ public class NotionServiceImpl implements INotionService {
     private String tokenV2;
     private ExecutorService executor;
 
-    /** 月度容器页缓存: "2026-08" -> pageId,避免所有页面堆积在同一父页面下导致内容超限 */
-    private final ConcurrentHashMap<String, String> monthlyContainers = new ConcurrentHashMap<>();
-    private static final DateTimeFormatter MONTH_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM");
+    /** 当日容器页缓存: "2026-08-10" -> pageId,避免所有页面堆积在同一父页面下导致内容超限 */
+    private final ConcurrentHashMap<String, String> dailyContainers = new ConcurrentHashMap<>();
+    private static final DateTimeFormatter DAY_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
 
     @PostConstruct
     public void init() {
@@ -62,7 +62,8 @@ public class NotionServiceImpl implements INotionService {
     private synchronized boolean initParentPage() {
         if (parentPageId != null) return true;
 
-        try (NotionClient client = new NotionClient(notionConfig.getApiKey())) {
+        try (NotionClient client = new NotionClient(notionConfig.getApiKey(),
+                notionConfig.getRetryCount(), notionConfig.getRetryBaseDelayMs())) {
             JsonNode result = client.search("");
             JsonNode results = result.get("results");
             if (results != null && results.size() > 0) {
@@ -92,19 +93,20 @@ public class NotionServiceImpl implements INotionService {
     }
 
     /**
-     * 获取或创建当前月份的容器页,将页面分散到月度子目录下,避免父页面内容超限
+     * 获取或创建当日的容器页,将页面分散到日子目录下,避免父页面内容超限
      */
-    private String getOrCreateMonthlyContainer() {
-        String monthKey = LocalDate.now().format(MONTH_FORMATTER);
-        return monthlyContainers.computeIfAbsent(monthKey, k -> {
-            try (NotionClient client = new NotionClient(notionConfig.getApiKey())) {
+    private String getOrCreateDailyContainer() {
+        String dayKey = LocalDate.now().format(DAY_FORMATTER);
+        return dailyContainers.computeIfAbsent(dayKey, k -> {
+            try (NotionClient client = new NotionClient(notionConfig.getApiKey(),
+                    notionConfig.getRetryCount(), notionConfig.getRetryBaseDelayMs())) {
                 JsonNode page = client.createPageUnderPage(parentPageId, k);
                 String containerId = page.get("id").asText();
-                log.info("[Notion] 创建月度容器页: {} -> {}", k, containerId);
+                log.info("[Notion] 创建当日容器页: {} -> {}", k, containerId);
                 return containerId;
             } catch (Exception e) {
-                log.error("[Notion] 创建月度容器页失败: {}", k, e);
-                throw new RuntimeException("创建月度容器页失败: " + e.getMessage(), e);
+                log.error("[Notion] 创建当日容器页失败: {}", k, e);
+                throw new RuntimeException("创建当日容器页失败: " + e.getMessage(), e);
             }
         });
     }
@@ -185,7 +187,8 @@ public class NotionServiceImpl implements INotionService {
             throw new IllegalStateException("父页面未初始化,请确保 Integration 已关联页面");
         }
 
-        NotionClient client = new NotionClient(notionConfig.getApiKey());
+        NotionClient client = new NotionClient(notionConfig.getApiKey(),
+                notionConfig.getRetryCount(), notionConfig.getRetryBaseDelayMs());
         NotionInternalClient ic = (tokenV2 != null) ? new NotionInternalClient(tokenV2) : null;
 
         try {
@@ -198,8 +201,8 @@ public class NotionServiceImpl implements INotionService {
             embedBlock.put("embed", Collections.singletonMap("url", iframeUrl));
             List<Map<String, Object>> children = Collections.singletonList(embedBlock);
 
-            // 获取当容器页,避免所有页面堆积在同一父页面下导致内容超限
-            String containerPageId = getOrCreateMonthlyContainer();
+            // 获取当容器页,避免所有页面堆积在同一父页面下导致内容超限
+            String containerPageId = getOrCreateDailyContainer();
 
             // 创建页面
             JsonNode page = client.createPageWithChildren(containerPageId, title, children);