From 28c8575a11e6e212f5daaf6ac779093259f47a18 Mon Sep 17 00:00:00 2001 From: yml Date: Mon, 25 May 2026 16:48:23 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=20cloudtentacles=20=E5=A4=9A?= =?UTF-8?q?=E8=B4=A6=E5=8F=B7=E7=AE=A1=E7=90=86=E4=B8=8E=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/admin-read-shared-helpers.ts | 4 + .../platform-config/cloudtentacles-service.ts | 8 +- .../platform-config/cloudtentacles.test.ts | 20 + .../admin/platform-config/mappers.test.ts | 1 + .../services/admin/platform-config/mappers.ts | 5 + .../kuaishou-cloud/cloudtentacles-context.ts | 7 + .../cloudtentacles/session-state-service.ts | 37 ++ .../cloudtentacles/source-config-service.ts | 1 + .../admin/platform-config/cloudtentacles.ts | 1 + apps/frontend/src/types/admin/tasks.ts | 2 + .../composables/useKuaishouCloudConfig.ts | 4 +- .../platform-shops/AdminPlatformShopsView.vue | 8 +- .../AdminCloudtentaclesConfigCard.vue | 492 ++++++++++++------ .../AdminCloudtentaclesInfoCard.vue | 32 +- .../AdminPlatformCloudtentaclesSection.vue | 13 +- .../useAdminCloudtentaclesPlatform.ts | 13 + .../AdminTaskKuaishouCloudPanel.vue | 14 +- 17 files changed, 495 insertions(+), 167 deletions(-) diff --git a/apps/backend/src/services/admin/admin-read-shared-helpers.ts b/apps/backend/src/services/admin/admin-read-shared-helpers.ts index 31b8ed09..2b442d67 100644 --- a/apps/backend/src/services/admin/admin-read-shared-helpers.ts +++ b/apps/backend/src/services/admin/admin-read-shared-helpers.ts @@ -90,6 +90,10 @@ export function mapKuaishouCloudFulfillmentContext(value: unknown): JsonRecord | binding: { prepareStatus: String(binding.prepareStatus || 'pending').trim() || 'pending', cloudSourceKey: String(binding.cloudSourceKey || '').trim(), + cloudSourceKeyFallbacks: Array.isArray(binding.cloudSourceKeyFallbacks) + ? binding.cloudSourceKeyFallbacks.map((value) => String(value || '').trim()).filter(Boolean) + : [], + resolvedSourceKey: String(binding.resolvedSourceKey || '').trim(), skuId: Number(binding.skuId || 0) || 0, skuName: String(binding.skuName || '').trim(), vnKey: String(binding.vnKey || '').trim(), diff --git a/apps/backend/src/services/admin/platform-config/cloudtentacles-service.ts b/apps/backend/src/services/admin/platform-config/cloudtentacles-service.ts index 3d5d92b5..94e4a9e8 100644 --- a/apps/backend/src/services/admin/platform-config/cloudtentacles-service.ts +++ b/apps/backend/src/services/admin/platform-config/cloudtentacles-service.ts @@ -8,10 +8,12 @@ import { } from "../../platforms/cloudtentacles/source-config-service.js"; import { clearCloudtentaclesSessionStateByKey, + deleteCloudtentaclesSessionStateByKey, getCloudtentaclesSessionFilePath, getCloudtentaclesSessionStateByKey, saveCloudtentaclesSessionStateByKey, getAllCloudtentaclesSessionStates, + pruneCloudtentaclesSessionStates, } from "../../platforms/cloudtentacles/session-state-service.js"; import { loginCloudtentaclesSession, @@ -102,7 +104,9 @@ export function updateAdminCloudtentaclesSourceConfig( enabled: payload.enabled !== false, sources: normalizedList, }); - const sessions = getAllCloudtentaclesSessionStates(); + const sessions = pruneCloudtentaclesSessionStates( + normalizedList.map((item) => item.key) + ); const sources = Array.isArray(savedList.sources) ? savedList.sources.map((s) => mapAdminCloudtentaclesSourceConfig(s)) @@ -151,7 +155,7 @@ export function deleteAdminCloudtentaclesSource(sourceKey: string) { if (!key) { throw new Error("sourceKey is required for deletion"); } - clearCloudtentaclesSessionStateByKey(key); + deleteCloudtentaclesSessionStateByKey(key); deleteCloudtentaclesSourceByKey(key); } diff --git a/apps/backend/src/services/admin/platform-config/cloudtentacles.test.ts b/apps/backend/src/services/admin/platform-config/cloudtentacles.test.ts index b3681123..7c3a8cf7 100644 --- a/apps/backend/src/services/admin/platform-config/cloudtentacles.test.ts +++ b/apps/backend/src/services/admin/platform-config/cloudtentacles.test.ts @@ -190,3 +190,23 @@ test('buildAdminCloudtentaclesValidateResult assembles validate response payload }, ) }) + +test('buildAdminCloudtentaclesSessionSummary preserves sourceKey', () => { + assert.deepEqual( + buildAdminCloudtentaclesSessionSummary( + { + token: 'abcdef1234567890', + permissions: [], + }, + {}, + 'account2', + ), + { + sourceKey: 'account2', + tokenMasked: 'abcdef****567890', + permissionCount: 0, + permissions: [], + persisted: false, + }, + ) +}) diff --git a/apps/backend/src/services/admin/platform-config/mappers.test.ts b/apps/backend/src/services/admin/platform-config/mappers.test.ts index d6aa08b8..64aa9747 100644 --- a/apps/backend/src/services/admin/platform-config/mappers.test.ts +++ b/apps/backend/src/services/admin/platform-config/mappers.test.ts @@ -104,6 +104,7 @@ test('mapAdminKuaishouCloudFulfillmentSource normalizes items and defaults', () externalSkuName: '', resolvedSkuName: '', cloudSourceKey: 'default', + cloudSourceKeyFallbacks: [], cloudSkuId: 0, cloudSkuName: '', vnKey: '', diff --git a/apps/backend/src/services/admin/platform-config/mappers.ts b/apps/backend/src/services/admin/platform-config/mappers.ts index c07b1313..4c8cb1ba 100644 --- a/apps/backend/src/services/admin/platform-config/mappers.ts +++ b/apps/backend/src/services/admin/platform-config/mappers.ts @@ -77,6 +77,11 @@ export function mapAdminKuaishouCloudFulfillmentItem(item: JsonObject = {}) { resolvedSkuName: String(item.resolvedSkuName || "").trim(), cloudSourceKey: String(item.cloudSourceKey || "default").trim() || "default", + cloudSourceKeyFallbacks: Array.isArray(item.cloudSourceKeyFallbacks) + ? item.cloudSourceKeyFallbacks + .map((value) => String(value || "").trim()) + .filter(Boolean) + : [], cloudSkuId: Number(item.cloudSkuId || 0) || 0, cloudSkuName: String(item.cloudSkuName || "").trim(), vnKey: String(item.vnKey || "").trim(), diff --git a/apps/backend/src/services/fulfillment/kuaishou-cloud/cloudtentacles-context.ts b/apps/backend/src/services/fulfillment/kuaishou-cloud/cloudtentacles-context.ts index b8b0a81e..d6977a6c 100644 --- a/apps/backend/src/services/fulfillment/kuaishou-cloud/cloudtentacles-context.ts +++ b/apps/backend/src/services/fulfillment/kuaishou-cloud/cloudtentacles-context.ts @@ -61,6 +61,13 @@ export function resolvePersistedCloudtentaclesContextWithFallback( const session = getCloudtentaclesSessionStateByKey(sourceKey); if (!source) continue; + if (source.enabled === false) { + lastError = createHttpError( + `cloudtentacles 账号 ${sourceKey} 已停用`, + { statusCode: 409, errorCode: "kuaishou_cloud_source_disabled" } + ); + continue; + } const token = String(session?.token || "").trim(); if (!token) { diff --git a/apps/backend/src/services/platforms/cloudtentacles/session-state-service.ts b/apps/backend/src/services/platforms/cloudtentacles/session-state-service.ts index b5c3e343..c55b7df3 100644 --- a/apps/backend/src/services/platforms/cloudtentacles/session-state-service.ts +++ b/apps/backend/src/services/platforms/cloudtentacles/session-state-service.ts @@ -65,6 +65,23 @@ export function saveCloudtentaclesSessionStateByKey(sourceKey: unknown, rawValue return states.sessions[key] } +/** + * Delete a session by sourceKey. + */ +export function deleteCloudtentaclesSessionStateByKey(sourceKey: unknown) { + const key = String(sourceKey || '').trim() + if (!key) { + throw new Error('deleteCloudtentaclesSessionStateByKey: sourceKey is required') + } + + const states = loadCloudtentaclesSessionStatesFromFile() + delete states.sessions[key] + + fs.mkdirSync(path.dirname(CLOUDTENTACLES_SESSION_FILE_PATH), { recursive: true }) + fs.writeFileSync(CLOUDTENTACLES_SESSION_FILE_PATH, `${JSON.stringify(states, null, 2)}\n`, 'utf8') + return states +} + /** * Backward-compatible clear: clears the 'default' session only. */ @@ -86,6 +103,26 @@ export function clearCloudtentaclesSessionStateByKey(sourceKey: unknown) { return cleared } +/** + * Keep only sessions whose sourceKey still exists in source config. + */ +export function pruneCloudtentaclesSessionStates(sourceKeys: unknown[] = []) { + const allowedKeys = new Set( + Array.isArray(sourceKeys) + ? sourceKeys.map((value) => String(value || '').trim()).filter(Boolean) + : [] + ) + + const states = loadCloudtentaclesSessionStatesFromFile() + states.sessions = Object.fromEntries( + Object.entries(states.sessions || {}).filter(([key]) => allowedKeys.has(String(key || '').trim())) + ) + + fs.mkdirSync(path.dirname(CLOUDTENTACLES_SESSION_FILE_PATH), { recursive: true }) + fs.writeFileSync(CLOUDTENTACLES_SESSION_FILE_PATH, `${JSON.stringify(states, null, 2)}\n`, 'utf8') + return states +} + // --------------------------------------------------------------------------- // Internal helpers // --------------------------------------------------------------------------- diff --git a/apps/backend/src/services/platforms/cloudtentacles/source-config-service.ts b/apps/backend/src/services/platforms/cloudtentacles/source-config-service.ts index b5ef8faa..5ec9fba9 100644 --- a/apps/backend/src/services/platforms/cloudtentacles/source-config-service.ts +++ b/apps/backend/src/services/platforms/cloudtentacles/source-config-service.ts @@ -133,6 +133,7 @@ function normalizeCloudtentaclesSourceItem(rawValue: unknown) { return { key: String(source.key || 'default').trim() || 'default', label: String(source.label || '').trim(), + enabled: source.enabled !== false, baseUrl: String(source.baseUrl || 'https://123.207.217.176').trim() || 'https://123.207.217.176', username: String(source.username || '').trim(), password: String(source.password || '').trim(), diff --git a/apps/frontend/src/types/admin/platform-config/cloudtentacles.ts b/apps/frontend/src/types/admin/platform-config/cloudtentacles.ts index 45c29aa3..5b90b085 100644 --- a/apps/frontend/src/types/admin/platform-config/cloudtentacles.ts +++ b/apps/frontend/src/types/admin/platform-config/cloudtentacles.ts @@ -1,6 +1,7 @@ export interface AdminCloudtentaclesSourceItem { key: string label: string + enabled: boolean baseUrl: string username: string password: string diff --git a/apps/frontend/src/types/admin/tasks.ts b/apps/frontend/src/types/admin/tasks.ts index 65ae3afe..8e0c76dc 100644 --- a/apps/frontend/src/types/admin/tasks.ts +++ b/apps/frontend/src/types/admin/tasks.ts @@ -174,6 +174,8 @@ export interface AdminTaskDetail { binding: { prepareStatus: string cloudSourceKey: string + cloudSourceKeyFallbacks: string[] + resolvedSourceKey: string skuId: number skuName: string vnKey: string diff --git a/apps/frontend/src/views/admin/fulfillment/kuaishou-cloud/composables/useKuaishouCloudConfig.ts b/apps/frontend/src/views/admin/fulfillment/kuaishou-cloud/composables/useKuaishouCloudConfig.ts index 5a7d465f..0301aee8 100644 --- a/apps/frontend/src/views/admin/fulfillment/kuaishou-cloud/composables/useKuaishouCloudConfig.ts +++ b/apps/frontend/src/views/admin/fulfillment/kuaishou-cloud/composables/useKuaishouCloudConfig.ts @@ -81,8 +81,8 @@ export function useKuaishouCloudConfig() { vnKey: '1', autoBuyEnabled: true, minAssetReserve: 0, - autoReturnNumberAfterDispatch: false, - autoConsumeAfterDispatch: false, + autoReturnNumberAfterDispatch: true, + autoConsumeAfterDispatch: true, kuaishouConsumeShopId: defaultShop?.shopId || '', kuaishouConsumeShopName: defaultShop?.kshopName || '', notes: '', diff --git a/apps/frontend/src/views/admin/platform-shops/AdminPlatformShopsView.vue b/apps/frontend/src/views/admin/platform-shops/AdminPlatformShopsView.vue index 05f48c66..809569d4 100644 --- a/apps/frontend/src/views/admin/platform-shops/AdminPlatformShopsView.vue +++ b/apps/frontend/src/views/admin/platform-shops/AdminPlatformShopsView.vue @@ -135,7 +135,9 @@ const { sources: cloudtentaclesSources, sessions: cloudtentaclesSessions, selectedSourceKey: selectedCloudtentaclesSourceKey, - sourceOptions: cloudtentaclesSourceOptions, + currentSourceEnabled: currentCloudtentaclesSourceEnabled, + currentSource: currentCloudtentaclesSource, + currentSession: currentCloudtentaclesSession, hydrateCloudtentaclesConfig, switchSource: switchCloudtentaclesSource, addSource: addCloudtentaclesSource, @@ -426,7 +428,9 @@ onMounted(loadConfigs) :sources="cloudtentaclesSources" :sessions="cloudtentaclesSessions" :selected-source-key="selectedCloudtentaclesSourceKey" - :source-options="cloudtentaclesSourceOptions" + :current-source-enabled="currentCloudtentaclesSourceEnabled" + :current-source="currentCloudtentaclesSource" + :current-session="currentCloudtentaclesSession" :switch-source="switchCloudtentaclesSource" :add-source="addCloudtentaclesSource" :remove-source="removeCloudtentaclesSource" diff --git a/apps/frontend/src/views/admin/platform-shops/components/cloudtentacles/AdminCloudtentaclesConfigCard.vue b/apps/frontend/src/views/admin/platform-shops/components/cloudtentacles/AdminCloudtentaclesConfigCard.vue index 1511d205..f55d4ac3 100644 --- a/apps/frontend/src/views/admin/platform-shops/components/cloudtentacles/AdminCloudtentaclesConfigCard.vue +++ b/apps/frontend/src/views/admin/platform-shops/components/cloudtentacles/AdminCloudtentaclesConfigCard.vue @@ -1,6 +1,7 @@