From 540d6588e6311778ffa0c095bab108045cf78c71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E8=87=AA=E8=BE=BE?= <980324341@qq.com> Date: Fri, 11 Sep 2026 17:02:13 +0800 Subject: [PATCH] =?UTF-8?q?fix(=E7=B4=AB=E9=B8=9F):=20=E5=BA=97=E9=93=BA?= =?UTF-8?q?=E7=B4=A2=E5=BC=95=E5=88=B7=E6=96=B0=E5=8A=A0=E8=BD=AE=E7=BA=A7?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E9=A2=84=E7=AE=97=E7=86=94=E6=96=AD=E2=80=94?= =?UTF-8?q?=E2=80=94=E5=8D=95=E8=BD=AE=E8=B6=85=208=20=E5=88=86=E9=92=9F?= =?UTF-8?q?=E5=8D=B3=E4=B8=AD=E6=96=AD=EF=BC=8C=E5=89=A9=E4=BD=99=20key=20?= =?UTF-8?q?=E9=A1=BA=E5=BB=B6=E4=B8=8B=E4=B8=80=E8=BD=AE=EF=BC=88=E6=B8=B8?= =?UTF-8?q?=E6=A0=87=E7=BB=AD=E8=B7=91=EF=BC=89=EF=BC=8C=E9=81=BF=E5=85=8D?= =?UTF-8?q?=E7=B4=AB=E9=B8=9F=E6=8E=A5=E5=8F=A3=E5=8D=8A=E6=AD=BB=E6=97=B6?= =?UTF-8?q?=E6=95=B4=E8=BD=AE=E5=8D=A0=E9=94=81=E5=87=A0=E5=8D=81=E5=88=86?= =?UTF-8?q?=E9=92=9F=E9=98=BB=E5=A1=9E=E5=90=8E=E7=BB=AD=E5=AE=9A=E6=97=B6?= =?UTF-8?q?=E8=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nanri/aiimage/config/ZiniaoProperties.java | 6 ++++++ .../ziniao/service/ZiniaoShopIndexService.java | 18 ++++++++++++++++++ .../src/main/resources/application.yml | 1 + 3 files changed, 25 insertions(+) diff --git a/backend-java/src/main/java/com/nanri/aiimage/config/ZiniaoProperties.java b/backend-java/src/main/java/com/nanri/aiimage/config/ZiniaoProperties.java index ad9ba596..a87f4934 100644 --- a/backend-java/src/main/java/com/nanri/aiimage/config/ZiniaoProperties.java +++ b/backend-java/src/main/java/com/nanri/aiimage/config/ZiniaoProperties.java @@ -61,4 +61,10 @@ public class ZiniaoProperties { * null/空/<=0 表示不限制(不推荐)。 */ private Integer shopIndexRefreshBatchSize; + + /** + * 单轮索引刷新的时间预算(秒);超时即中断本轮,剩余 key 顺延下一轮。 + * null/<=0 使用内置默认(8 分钟)。 + */ + private Integer shopIndexRoundTimeoutSeconds; } diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/ziniao/service/ZiniaoShopIndexService.java b/backend-java/src/main/java/com/nanri/aiimage/modules/ziniao/service/ZiniaoShopIndexService.java index 2a8abf7a..2bfb54b9 100644 --- a/backend-java/src/main/java/com/nanri/aiimage/modules/ziniao/service/ZiniaoShopIndexService.java +++ b/backend-java/src/main/java/com/nanri/aiimage/modules/ziniao/service/ZiniaoShopIndexService.java @@ -57,6 +57,7 @@ public class ZiniaoShopIndexService { private static final Duration DEFAULT_ENTRY_TTL = Duration.ofHours(12); private static final Duration DEFAULT_CURSOR_TTL = Duration.ofHours(24); private static final int DEFAULT_REFRESH_BATCH_SIZE = 200; + private static final long DEFAULT_ROUND_TIMEOUT_MILLIS = Duration.ofMinutes(8).toMillis(); private static final int SHOP_INDEX_LIST_LIMIT = 10000; private static final String REFRESH_BLOCKED_REASON_IP_WHITELIST = "IP_WHITELIST"; @@ -206,6 +207,14 @@ public class ZiniaoShopIndexService { int nextOffset = computeNextOffset(allApiKeyAccounts.size(), previousOffset, apiKeyAccounts.size(), refreshBatchSize); apiKeyLoop: for (ZiniaoApiKeyProvider.ApiKeyAccount apiKeyAccount : apiKeyAccounts) { + // 轮级熔断:单轮耗时超上限即跳出,剩余 key 顺延下一轮(游标续跑),避免紫鸟接口半死时整轮占锁几十分钟 + long roundElapsed = System.currentTimeMillis() - now; + if (refreshRoundTimeoutMillis() > 0 && roundElapsed > refreshRoundTimeoutMillis() && completedApiKeyCount > 0) { + completeCoverage = false; + log.warn("[ziniao-index] refresh round budget exceeded, break early elapsedMs={} budgetMs={} completedApiKeys={} remainingKeys={}", + roundElapsed, refreshRoundTimeoutMillis(), completedApiKeyCount, apiKeyAccounts.size() - completedApiKeyCount - skippedApiKeyCount); + break; + } String apiKey = apiKeyAccount.apiKey(); String companyName = apiKeyAccount.accountName(); Map> apiKeyGrouped = new LinkedHashMap<>(); @@ -798,6 +807,15 @@ public class ZiniaoShopIndexService { return batchSize; } + /** 轮级刷新预算(毫秒),<=0 表示不限制。 */ + private long refreshRoundTimeoutMillis() { + Integer seconds = ziniaoProperties.getShopIndexRoundTimeoutSeconds(); + if (seconds == null || seconds <= 0) { + return DEFAULT_ROUND_TIMEOUT_MILLIS; + } + return Duration.ofSeconds(seconds).toMillis(); + } + private List selectApiKeyBatchByOffset( List allApiKeyAccounts, int offset, diff --git a/backend-java/src/main/resources/application.yml b/backend-java/src/main/resources/application.yml index d58362dc..8ea69b98 100644 --- a/backend-java/src/main/resources/application.yml +++ b/backend-java/src/main/resources/application.yml @@ -347,3 +347,4 @@ aiimage: shop-index-fresh-minutes: ${AIIMAGE_ZINIAO_SHOP_INDEX_FRESH_MINUTES:20} shop-index-entry-ttl-hours: ${AIIMAGE_ZINIAO_SHOP_INDEX_ENTRY_TTL_HOURS:12} shop-index-refresh-batch-size: ${AIIMAGE_ZINIAO_SHOP_INDEX_REFRESH_BATCH_SIZE:200} + shop-index-round-timeout-seconds: ${AIIMAGE_ZINIAO_SHOP_INDEX_ROUND_TIMEOUT_SECONDS:480}