From 91d74b3e59f50e2b9c47ec9c734b2eddb1d7e3df Mon Sep 17 00:00:00 2001 From: yml2213 Date: Thu, 14 May 2026 16:31:56 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=B0=8F=E5=BA=97=E6=A0=B8?= =?UTF-8?q?=E9=94=80bug,=20=E9=93=BE=E6=8E=A510=E5=88=86=E9=92=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.server.example | 2 +- apps/backend/config/default.cjs | 2 +- .../admin/write/kuaishou-cloud-actions.js | 8 +- .../admin/write/kuaishou-cloud-helpers.js | 2 +- .../kuaishou-cloud-task-service.js | 10 +- ...aishou-cloud-fulfillment-config-service.js | 11 ++- .../platforms/cloudtentacles/shared.js | 4 +- .../AdminKuaishouCloudFulfillmentView.vue | 96 +++++++++++++++---- 8 files changed, 104 insertions(+), 31 deletions(-) diff --git a/.env.server.example b/.env.server.example index 79cb7327..e28ee21c 100644 --- a/.env.server.example +++ b/.env.server.example @@ -25,7 +25,7 @@ TENCENT_BROWSER_PREWARM=true TENCENT_BROWSER_KEEP_ALIVE=true TENCENT_BROWSER_SLOW_MO=0 TENCENT_SESSION_DEBUG=false -CLOUDTENTACLES_BIND_URL_TTL_SECONDS=300 +CLOUDTENTACLES_BIND_URL_TTL_SECONDS=600 CLOUDTENTACLES_BIND_URL_PROBE_INTERVAL_SECONDS=30 CLOUDTENTACLES_BIND_URL_PROBE_TIMEOUT_MS=5000 CLOUDTENTACLES_BIND_URL_PROBE_ENDPOINT=https://comm.ams.game.qq.com/ide/ diff --git a/apps/backend/config/default.cjs b/apps/backend/config/default.cjs index 7e9ecd30..eaec9fc3 100644 --- a/apps/backend/config/default.cjs +++ b/apps/backend/config/default.cjs @@ -99,7 +99,7 @@ module.exports = { vnBindUrlPath: '/vn/bind_url', vnBindInfoPath: '/vn/bind_info', vnBackPath: '/vn/back', - bindUrlTtlSeconds: 300, + bindUrlTtlSeconds: 600, bindUrlProbeIntervalSeconds: 30, bindUrlProbeTimeoutMs: 5000, bindUrlProbeUserAgent: 'Mozilla/5.0 (Linux; Android 12; M2012K11AC Build/SKQ1.211006.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/98.0.4758.102 MQQBrowser/6.2 TBS/046141 Mobile Safari/537.36 V1_AND_SQ_8.8.85_2674_YYB_D QQ/8.8.85.7685 NetType/WIFI WebP/0.3.0 Pixel/1080 StatusBarHeight/108 SimpleUISwitch/0 QQTheme/1000', diff --git a/apps/backend/src/services/admin/write/kuaishou-cloud-actions.js b/apps/backend/src/services/admin/write/kuaishou-cloud-actions.js index e9289cfe..fd7fb4fa 100644 --- a/apps/backend/src/services/admin/write/kuaishou-cloud-actions.js +++ b/apps/backend/src/services/admin/write/kuaishou-cloud-actions.js @@ -459,11 +459,12 @@ export async function returnNumberAdminTaskKuaishouCloudFulfillment(taskId, sess const order = await getOrderById(task.order_id) const ticketCode = String(flow.ticket.code || '').trim() - const shopId = String(order?.shop_id || '').trim() + const shopId = String(flow.consume.shopId || order?.shop_id || '').trim() + const shopName = String(flow.consume.shopName || order?.shop_name || '').trim() const eticketSource = getKuaishouEticketSourceConfig() const shopConfig = resolveKuaishouEticketShopConfig({ shopId, - shopName: String(order?.shop_name || '').trim(), + shopName, }) let consumeStatus = 'pending' let consumeErrorMessage = '' @@ -530,7 +531,7 @@ export async function returnNumberAdminTaskKuaishouCloudFulfillment(taskId, sess ...flow.consume, status: consumeStatus, shopId: shopId || flow.consume.shopId, - shopName: String(flow.consume.shopName || order?.shop_name || '').trim(), + shopName, autoConsumeEnabled: flow.consume.autoConsumeEnabled === true, consumedAt, errorMessage: consumeErrorMessage, @@ -560,6 +561,7 @@ export async function returnNumberAdminTaskKuaishouCloudFulfillment(taskId, sess { ticketCodeMasked: maskCode(ticketCode), shopId, + shopName, consumeStatus, errorMessage: consumeErrorMessage, }, diff --git a/apps/backend/src/services/admin/write/kuaishou-cloud-helpers.js b/apps/backend/src/services/admin/write/kuaishou-cloud-helpers.js index 1d8edfa5..83d04994 100644 --- a/apps/backend/src/services/admin/write/kuaishou-cloud-helpers.js +++ b/apps/backend/src/services/admin/write/kuaishou-cloud-helpers.js @@ -144,7 +144,7 @@ export function resolveKuaishouCloudBindUrlExpiresAt(preparedAt) { } const config = resolveCloudtentaclesConfig() - const ttlSeconds = Number(config.bindUrlTtlSeconds || 300) + const ttlSeconds = Number(config.bindUrlTtlSeconds || 600) return new Date(preparedTime + Math.max(1, ttlSeconds) * 1000).toISOString() } diff --git a/apps/backend/src/services/fulfillment/kuaishou-cloud-task-service.js b/apps/backend/src/services/fulfillment/kuaishou-cloud-task-service.js index 8303ecf1..cb8c9b30 100644 --- a/apps/backend/src/services/fulfillment/kuaishou-cloud-task-service.js +++ b/apps/backend/src/services/fulfillment/kuaishou-cloud-task-service.js @@ -890,11 +890,12 @@ export async function returnKuaishouCloudFulfillmentTask(task, options = {}) { const order = await getOrderById(task.order_id) const ticketCode = String(flow.ticket.code || '').trim() - const shopId = String(order?.shop_id || '').trim() + const shopId = String(flow.consume.shopId || order?.shop_id || '').trim() + const shopName = String(flow.consume.shopName || order?.shop_name || '').trim() const eticketSource = getKuaishouEticketSourceConfig() const shopConfig = resolveKuaishouEticketShopConfig({ shopId, - shopName: String(order?.shop_name || '').trim(), + shopName, }) let consumeStatus = 'pending' @@ -956,7 +957,7 @@ export async function returnKuaishouCloudFulfillmentTask(task, options = {}) { ...flow.consume, status: consumeStatus, shopId: shopId || flow.consume.shopId, - shopName: String(flow.consume.shopName || order?.shop_name || '').trim(), + shopName, autoConsumeEnabled: flow.consume.autoConsumeEnabled === true, consumedAt, errorMessage: consumeErrorMessage, @@ -989,6 +990,7 @@ export async function returnKuaishouCloudFulfillmentTask(task, options = {}) { source: String(options.source || 'system').trim() || 'system', ticketCodeMasked: maskCode(ticketCode), shopId, + shopName, consumeStatus, errorMessage: consumeErrorMessage, actor, @@ -1035,7 +1037,7 @@ export function resolveKuaishouCloudBindUrlExpiresAt(preparedAt) { } const config = resolveCloudtentaclesConfig() - const ttlSeconds = Number(config.bindUrlTtlSeconds || 300) + const ttlSeconds = Number(config.bindUrlTtlSeconds || 600) return new Date(preparedTime + Math.max(1, ttlSeconds) * 1000).toISOString() } diff --git a/apps/backend/src/services/order/kuaishou-cloud-fulfillment-config-service.js b/apps/backend/src/services/order/kuaishou-cloud-fulfillment-config-service.js index b629a01e..07bafb4b 100644 --- a/apps/backend/src/services/order/kuaishou-cloud-fulfillment-config-service.js +++ b/apps/backend/src/services/order/kuaishou-cloud-fulfillment-config-service.js @@ -2,6 +2,7 @@ import fs from 'node:fs' import path from 'node:path' import { PROJECT_ROOT } from '../../config/runtime.js' +import { resolveKuaishouEticketShopConfig } from '../platforms/kuaishou-eticket/source-config-service.js' const KUAISHOU_CLOUD_FULFILLMENT_FILE_PATH = path.join(PROJECT_ROOT, 'data', 'kuaishou-cloud-fulfillment.json') @@ -103,6 +104,12 @@ function normalizeKuaishouCloudFulfillmentItem(rawValue) { const externalSkuCode = String(rawValue.externalSkuCode || '').trim() const externalItemId = String(rawValue.externalItemId || '').trim() const externalSkuName = String(rawValue.externalSkuName || '').trim() + const kuaishouConsumeShopId = String(rawValue.kuaishouConsumeShopId || '').trim() + const kuaishouConsumeShopName = String(rawValue.kuaishouConsumeShopName || '').trim() + const kuaishouShopConfig = resolveKuaishouEticketShopConfig({ + shopId: kuaishouConsumeShopId, + shopName: kuaishouConsumeShopName, + }) if (!internalSkuCode) { return null @@ -137,8 +144,8 @@ function normalizeKuaishouCloudFulfillmentItem(rawValue) { minAssetReserve: normalizeNonNegativeInteger(rawValue.minAssetReserve, 0), autoReturnNumberAfterDispatch: rawValue.autoReturnNumberAfterDispatch === true, autoConsumeAfterDispatch: rawValue.autoConsumeAfterDispatch === true, - kuaishouConsumeShopId: String(rawValue.kuaishouConsumeShopId || '').trim(), - kuaishouConsumeShopName: String(rawValue.kuaishouConsumeShopName || '').trim(), + kuaishouConsumeShopId: String(kuaishouShopConfig?.shopId || kuaishouConsumeShopId).trim(), + kuaishouConsumeShopName: String(kuaishouShopConfig?.kshopName || kuaishouConsumeShopName).trim(), notes: String(rawValue.notes || '').trim(), } } diff --git a/apps/backend/src/services/platforms/cloudtentacles/shared.js b/apps/backend/src/services/platforms/cloudtentacles/shared.js index 3896affb..9903b61c 100644 --- a/apps/backend/src/services/platforms/cloudtentacles/shared.js +++ b/apps/backend/src/services/platforms/cloudtentacles/shared.js @@ -27,7 +27,7 @@ export function resolveCloudtentaclesConfig(overrides = {}) { vnBindUrlPath: '/vn/bind_url', vnBindInfoPath: '/vn/bind_info', vnBackPath: '/vn/back', - bindUrlTtlSeconds: 300, + bindUrlTtlSeconds: 600, bindUrlProbeIntervalSeconds: 30, bindUrlProbeTimeoutMs: 5000, bindUrlProbeUserAgent: '', @@ -71,7 +71,7 @@ export function resolveCloudtentaclesConfig(overrides = {}) { vnBindUrlPath: normalizePath(overrides.vnBindUrlPath || baseConfig.vnBindUrlPath, '/vn/bind_url'), vnBindInfoPath: normalizePath(overrides.vnBindInfoPath || baseConfig.vnBindInfoPath, '/vn/bind_info'), vnBackPath: normalizePath(overrides.vnBackPath || baseConfig.vnBackPath, '/vn/back'), - bindUrlTtlSeconds: normalizePositiveInteger(overrides.bindUrlTtlSeconds || baseConfig.bindUrlTtlSeconds, 300), + bindUrlTtlSeconds: normalizePositiveInteger(overrides.bindUrlTtlSeconds || baseConfig.bindUrlTtlSeconds, 600), bindUrlProbeIntervalSeconds: normalizePositiveInteger( overrides.bindUrlProbeIntervalSeconds || baseConfig.bindUrlProbeIntervalSeconds, 30, diff --git a/apps/frontend/src/views/admin/AdminKuaishouCloudFulfillmentView.vue b/apps/frontend/src/views/admin/AdminKuaishouCloudFulfillmentView.vue index ba6e0f82..9294ccd4 100644 --- a/apps/frontend/src/views/admin/AdminKuaishouCloudFulfillmentView.vue +++ b/apps/frontend/src/views/admin/AdminKuaishouCloudFulfillmentView.vue @@ -5,6 +5,7 @@ import { showError, showSuccess } from '@/lib/feedback' import { fetchAdminCloudtentaclesSkuList, fetchAdminKuaishouCloudFulfillmentConfig, + fetchAdminKuaishouEticketSourceConfig, fetchAdminNinetyoneOrders, saveAdminKuaishouCloudFulfillmentConfig, } from '@/services/admin' @@ -12,6 +13,7 @@ import type { AdminCloudtentaclesSkuItem, AdminKuaishouCloudFulfillmentConfig, AdminKuaishouCloudFulfillmentItem, + AdminKuaishouEticketShopConfigItem, AdminNinetyoneOrderItem, } from '@/types/admin' import { hasAdminRole } from '@/utils/admin-auth' @@ -43,6 +45,7 @@ const importedNinetyoneOrderKeys = ref([]) const cloudSkuCatalogLoading = ref(false) const cloudSkuCatalogErrorMessage = ref('') const cloudSkuCatalog = ref([]) +const kuaishouConsumeShops = ref([]) const ninetyoneLookupForm = reactive({ status: 'pending_config' as 'pending_config' | 'all' | 'manual_failed', page: 1, @@ -83,6 +86,7 @@ const ninetyoneLookupMetrics = computed(() => { }) function createEmptyItem(): EditableItem { + const defaultShop = getDefaultKuaishouConsumeShop() return { localId: crypto.randomUUID(), id: '', @@ -105,13 +109,14 @@ function createEmptyItem(): EditableItem { minAssetReserve: 0, autoReturnNumberAfterDispatch: false, autoConsumeAfterDispatch: false, - kuaishouConsumeShopId: '', - kuaishouConsumeShopName: '', + kuaishouConsumeShopId: defaultShop?.shopId || '', + kuaishouConsumeShopName: defaultShop?.kshopName || '', notes: '', } } function mapEditableItem(item: AdminKuaishouCloudFulfillmentItem): EditableItem { + const matchedShop = findKuaishouConsumeShop(item.kuaishouConsumeShopId, item.kuaishouConsumeShopName) return { localId: crypto.randomUUID(), id: item.id || crypto.randomUUID(), @@ -134,13 +139,14 @@ function mapEditableItem(item: AdminKuaishouCloudFulfillmentItem): EditableItem minAssetReserve: Number(item.minAssetReserve || 0) || 0, autoReturnNumberAfterDispatch: item.autoReturnNumberAfterDispatch === true, autoConsumeAfterDispatch: item.autoConsumeAfterDispatch === true, - kuaishouConsumeShopId: item.kuaishouConsumeShopId || '', - kuaishouConsumeShopName: item.kuaishouConsumeShopName || '', + kuaishouConsumeShopId: matchedShop?.shopId || item.kuaishouConsumeShopId || '', + kuaishouConsumeShopName: matchedShop?.kshopName || item.kuaishouConsumeShopName || '', notes: item.notes || '', } } function mapSaveItem(item: EditableItem): AdminKuaishouCloudFulfillmentItem { + const matchedShop = findKuaishouConsumeShop(item.kuaishouConsumeShopId, item.kuaishouConsumeShopName) return { id: item.id.trim() || item.internalSkuCode.trim() || item.localId, enabled: item.enabled, @@ -162,8 +168,8 @@ function mapSaveItem(item: EditableItem): AdminKuaishouCloudFulfillmentItem { minAssetReserve: Math.max(0, Number(item.minAssetReserve || 0) || 0), autoReturnNumberAfterDispatch: item.autoReturnNumberAfterDispatch, autoConsumeAfterDispatch: item.autoConsumeAfterDispatch, - kuaishouConsumeShopId: item.kuaishouConsumeShopId.trim(), - kuaishouConsumeShopName: item.kuaishouConsumeShopName.trim(), + kuaishouConsumeShopId: String(matchedShop?.shopId || item.kuaishouConsumeShopId).trim(), + kuaishouConsumeShopName: String(matchedShop?.kshopName || item.kuaishouConsumeShopName).trim(), notes: item.notes.trim(), } } @@ -193,6 +199,8 @@ function hasMeaningfulContent(item: EditableItem) { || item.minAssetReserve !== 0 || item.autoReturnNumberAfterDispatch || item.autoConsumeAfterDispatch + || item.kuaishouConsumeShopId.trim() + || item.kuaishouConsumeShopName.trim() || item.enabled !== true, ) } @@ -201,7 +209,7 @@ function isItemComplete(item: EditableItem) { return Boolean( item.internalSkuCode.trim() && Number(item.cloudSkuId || 0) > 0 - && true + && (!item.autoConsumeAfterDispatch || Boolean(item.kuaishouConsumeShopId.trim())) && hasExternalMatch(item), ) } @@ -225,6 +233,10 @@ function resolveValidation(item: EditableItem, index: number): ValidationState { return { localId: item.localId, message: `${title} 至少填写一种外部匹配条件` } } + if (item.autoConsumeAfterDispatch && !item.kuaishouConsumeShopId.trim()) { + return { localId: item.localId, message: `${title} 已开启发货后核销,需要选择快手核销店铺` } + } + return null } @@ -280,9 +292,36 @@ function getExternalMatchSummary(item: EditableItem) { return parts.length > 0 ? parts.join(' / ') : '未设置外部命中条件' } +function getKuaishouConsumeShopOptions() { + return kuaishouConsumeShops.value.filter((shop) => shop.enabled !== false && shop.hasCookie) +} + +function findKuaishouConsumeShop(shopId = '', shopName = '') { + const normalizedShopId = String(shopId || '').trim() + const normalizedShopName = String(shopName || '').trim() + return getKuaishouConsumeShopOptions().find((shop) => ( + (normalizedShopId && shop.shopId === normalizedShopId) + || (normalizedShopName && shop.kshopName === normalizedShopName) + )) || null +} + +function getDefaultKuaishouConsumeShop() { + return getKuaishouConsumeShopOptions()[0] || null +} + +function formatKuaishouConsumeShopOption(shop: AdminKuaishouEticketShopConfigItem) { + return `${shop.kshopName || '未命名快手小店'} · ${shop.shopId}` +} + +function handleKuaishouConsumeShopSelected(item: EditableItem) { + const matchedShop = findKuaishouConsumeShop(item.kuaishouConsumeShopId) + item.kuaishouConsumeShopName = matchedShop?.kshopName || '' +} + function getConsumeShopSummary(item: EditableItem) { - const name = item.kuaishouConsumeShopName.trim() - const id = item.kuaishouConsumeShopId.trim() + const matchedShop = findKuaishouConsumeShop(item.kuaishouConsumeShopId, item.kuaishouConsumeShopName) + const name = String(matchedShop?.kshopName || item.kuaishouConsumeShopName).trim() + const id = String(matchedShop?.shopId || item.kuaishouConsumeShopId).trim() if (name && id) { return `${name} · ${id}` @@ -343,7 +382,11 @@ async function loadConfigs() { errorMessage.value = '' try { - const response = await fetchAdminKuaishouCloudFulfillmentConfig() + const [response, eticketResponse] = await Promise.all([ + fetchAdminKuaishouCloudFulfillmentConfig(), + fetchAdminKuaishouEticketSourceConfig(), + ]) + kuaishouConsumeShops.value = Array.isArray(eticketResponse.data.source.shops) ? eticketResponse.data.source.shops : [] filePath.value = response.data.filePath enabled.value = response.data.source.enabled !== false items.value = (response.data.source.items || []).map(mapEditableItem) @@ -373,6 +416,7 @@ function removeItem(localId: string) { function createItemFromNinetyoneOrder(item: AdminNinetyoneOrderItem): EditableItem { const productNo = String(item.productNo || '').trim() const productName = String(item.productName || productNo).trim() + const defaultShop = getDefaultKuaishouConsumeShop() return { localId: crypto.randomUUID(), id: '', @@ -395,8 +439,8 @@ function createItemFromNinetyoneOrder(item: AdminNinetyoneOrderItem): EditableIt minAssetReserve: 0, autoReturnNumberAfterDispatch: false, autoConsumeAfterDispatch: false, - kuaishouConsumeShopId: '', - kuaishouConsumeShopName: '', + kuaishouConsumeShopId: defaultShop?.shopId || '', + kuaishouConsumeShopName: defaultShop?.kshopName || '', notes: `从 91卡券订单 ${item.orderNo} 导入`, } } @@ -978,13 +1022,31 @@ onMounted(loadConfigs)
-