修复小店核销bug, 链接10分钟
This commit is contained in:
+1
-1
@@ -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/
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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<string[]>([])
|
||||
const cloudSkuCatalogLoading = ref(false)
|
||||
const cloudSkuCatalogErrorMessage = ref('')
|
||||
const cloudSkuCatalog = ref<AdminCloudtentaclesSkuItem[]>([])
|
||||
const kuaishouConsumeShops = ref<AdminKuaishouEticketShopConfigItem[]>([])
|
||||
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)
|
||||
</div>
|
||||
|
||||
<div v-if="!isCollapsed(item.localId)" class="mapping-extra">
|
||||
<label class="field-block">
|
||||
<span>快手核销店铺 ID</span>
|
||||
<input v-model="item.kuaishouConsumeShopId" class="text-input" maxlength="80" placeholder="预留给后续自动核销" />
|
||||
<label class="field-block field-wide">
|
||||
<span>快手核销店铺</span>
|
||||
<select
|
||||
v-model="item.kuaishouConsumeShopId"
|
||||
class="text-input"
|
||||
@change="handleKuaishouConsumeShopSelected(item)"
|
||||
>
|
||||
<option value="">请选择已配置 Cookie 的快手小店</option>
|
||||
<option
|
||||
v-for="shop in getKuaishouConsumeShopOptions()"
|
||||
:key="shop.shopId"
|
||||
:value="shop.shopId"
|
||||
>
|
||||
{{ formatKuaishouConsumeShopOption(shop) }}
|
||||
</option>
|
||||
</select>
|
||||
<small class="field-help">来自“平台店铺 -> 快手小店核销”配置,保存时会自动带出店铺名。</small>
|
||||
</label>
|
||||
<label class="field-block">
|
||||
<span>快手核销店铺名</span>
|
||||
<input v-model="item.kuaishouConsumeShopName" class="text-input" maxlength="120" placeholder="便于快速确认" />
|
||||
<span>核销店铺 ID</span>
|
||||
<input :value="item.kuaishouConsumeShopId || '-'" class="text-input text-input--readonly" readonly />
|
||||
</label>
|
||||
<label class="field-block">
|
||||
<span>核销店铺名</span>
|
||||
<input :value="item.kuaishouConsumeShopName || '-'" class="text-input text-input--readonly" readonly />
|
||||
</label>
|
||||
<label class="field-block field-wide">
|
||||
<span>备注</span>
|
||||
|
||||
Reference in New Issue
Block a user