支持快手行业电子凭证多店铺授权
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
import { maskSecret } from '../../../utils/masking.js'
|
||||
import { createHttpError } from '../../../utils/http.js'
|
||||
import {
|
||||
findKuaishouIndustryShopConfig,
|
||||
getKuaishouIndustrySourceConfig,
|
||||
getKuaishouIndustrySourceFilePath,
|
||||
saveKuaishouIndustrySourceConfig,
|
||||
type KuaishouIndustryShopConfig,
|
||||
type KuaishouIndustrySourceConfig,
|
||||
} from '../../platforms/kuaishou-industry/source-config-service.js'
|
||||
import {
|
||||
@@ -17,6 +19,9 @@ const SECRET_FIELDS = [
|
||||
'appSecret',
|
||||
'signSecret',
|
||||
'messageSecret',
|
||||
] as const
|
||||
|
||||
const SHOP_SECRET_FIELDS = [
|
||||
'accessToken',
|
||||
'refreshToken',
|
||||
] as const
|
||||
@@ -49,8 +54,10 @@ export function updateAdminKuaishouIndustrySourceConfig(payload: JsonObject = {}
|
||||
shopId: readConfigString(payload, 'shopId', current.shopId),
|
||||
shopName: readConfigString(payload, 'shopName', current.shopName),
|
||||
version: readConfigString(payload, 'version', current.version),
|
||||
accessTokenExpiresAt: readConfigString(payload, 'accessTokenExpiresAt', current.accessTokenExpiresAt, { allowBlank: true }),
|
||||
refreshTokenExpiresAt: readConfigString(payload, 'refreshTokenExpiresAt', current.refreshTokenExpiresAt, { allowBlank: true }),
|
||||
shops: normalizeShopConfigPayloads(
|
||||
Array.isArray(payload.shops) ? payload.shops : current.shops,
|
||||
current.shops,
|
||||
),
|
||||
...resolveSecretPatch(payload, current),
|
||||
})
|
||||
|
||||
@@ -60,12 +67,15 @@ export function updateAdminKuaishouIndustrySourceConfig(payload: JsonObject = {}
|
||||
}
|
||||
}
|
||||
|
||||
export async function refreshAdminKuaishouIndustryAccessToken() {
|
||||
const result = await refreshKuaishouIndustryAccessToken()
|
||||
export async function refreshAdminKuaishouIndustryAccessToken(payload: JsonObject = {}) {
|
||||
const result = await refreshKuaishouIndustryAccessToken({
|
||||
sellerId: String(payload.sellerId || '').trim(),
|
||||
})
|
||||
|
||||
return {
|
||||
filePath: getKuaishouIndustrySourceFilePath(),
|
||||
refreshed: result.refreshed,
|
||||
shop: mapAdminKuaishouIndustryShopConfig(result.shop, result.config),
|
||||
source: mapAdminKuaishouIndustrySourceConfig(result.config),
|
||||
}
|
||||
}
|
||||
@@ -79,17 +89,23 @@ export async function exchangeAdminKuaishouIndustryAuthorizationCode(payload: Js
|
||||
})
|
||||
}
|
||||
|
||||
const result = await exchangeKuaishouIndustryAuthorizationCode(code)
|
||||
const result = await exchangeKuaishouIndustryAuthorizationCode(code, {
|
||||
sellerId: String(payload.sellerId || '').trim(),
|
||||
shopName: String(payload.shopName || '').trim(),
|
||||
customShopName: String(payload.customShopName || '').trim(),
|
||||
})
|
||||
|
||||
return {
|
||||
filePath: getKuaishouIndustrySourceFilePath(),
|
||||
refreshed: result.refreshed,
|
||||
shop: mapAdminKuaishouIndustryShopConfig(result.shop, result.config),
|
||||
source: mapAdminKuaishouIndustrySourceConfig(result.config),
|
||||
}
|
||||
}
|
||||
|
||||
function mapAdminKuaishouIndustrySourceConfig(config: KuaishouIndustrySourceConfig) {
|
||||
const accessTokenStatus = resolveAccessTokenStatus(config)
|
||||
const primaryShop = config.shops[0] || null
|
||||
const accessTokenStatus = resolveAccessTokenStatus(primaryShop || config)
|
||||
|
||||
return {
|
||||
enabled: config.enabled !== false,
|
||||
@@ -127,12 +143,47 @@ function mapAdminKuaishouIndustrySourceConfig(config: KuaishouIndustrySourceConf
|
||||
shopId: config.shopId,
|
||||
shopName: config.shopName,
|
||||
version: config.version,
|
||||
shops: config.shops.map((shop) => mapAdminKuaishouIndustryShopConfig(shop, config)),
|
||||
lastRefreshedAt: config.lastRefreshedAt,
|
||||
lastRefreshError: config.lastRefreshError,
|
||||
}
|
||||
}
|
||||
|
||||
function buildKuaishouIndustryAuthorizationUrl(config: KuaishouIndustrySourceConfig): string {
|
||||
function mapAdminKuaishouIndustryShopConfig(
|
||||
shop: KuaishouIndustryShopConfig,
|
||||
config: KuaishouIndustrySourceConfig,
|
||||
) {
|
||||
const accessTokenStatus = resolveAccessTokenStatus(shop)
|
||||
|
||||
return {
|
||||
enabled: shop.enabled !== false,
|
||||
sellerId: shop.sellerId,
|
||||
shopId: shop.shopId,
|
||||
shopName: shop.shopName,
|
||||
customShopName: shop.customShopName,
|
||||
authState: shop.authState,
|
||||
authorizationUrl: buildKuaishouIndustryAuthorizationUrl(config, shop),
|
||||
accessToken: '',
|
||||
accessTokenMasked: maskSecret(shop.accessToken),
|
||||
hasAccessToken: Boolean(shop.accessToken),
|
||||
refreshToken: '',
|
||||
refreshTokenMasked: maskSecret(shop.refreshToken),
|
||||
hasRefreshToken: Boolean(shop.refreshToken),
|
||||
accessTokenExpiresAt: shop.accessTokenExpiresAt,
|
||||
refreshTokenExpiresAt: shop.refreshTokenExpiresAt,
|
||||
accessTokenStatus: accessTokenStatus.status,
|
||||
accessTokenExpiresInSeconds: accessTokenStatus.expiresInSeconds,
|
||||
openId: shop.openId,
|
||||
grantedScopes: shop.grantedScopes,
|
||||
lastRefreshedAt: shop.lastRefreshedAt,
|
||||
lastRefreshError: shop.lastRefreshError,
|
||||
}
|
||||
}
|
||||
|
||||
function buildKuaishouIndustryAuthorizationUrl(
|
||||
config: KuaishouIndustrySourceConfig,
|
||||
shop?: Pick<KuaishouIndustryShopConfig, 'sellerId' | 'authState'> | null,
|
||||
): string {
|
||||
if (!config.authBaseUrl || !config.appKey || !config.redirectUri || !config.scopes) {
|
||||
return ''
|
||||
}
|
||||
@@ -143,8 +194,9 @@ function buildKuaishouIndustryAuthorizationUrl(config: KuaishouIndustrySourceCon
|
||||
url.searchParams.set('redirect_uri', config.redirectUri)
|
||||
url.searchParams.set('scope', normalizeScopeText(config.scopes))
|
||||
url.searchParams.set('response_type', 'code')
|
||||
if (config.authState) {
|
||||
url.searchParams.set('state', config.authState)
|
||||
const state = String(shop?.authState || config.authState || shop?.sellerId || '').trim()
|
||||
if (state) {
|
||||
url.searchParams.set('state', state)
|
||||
}
|
||||
|
||||
return url.toString()
|
||||
@@ -174,7 +226,96 @@ function resolveSecretPatch(
|
||||
return patch
|
||||
}
|
||||
|
||||
function resolveAccessTokenStatus(config: KuaishouIndustrySourceConfig) {
|
||||
function normalizeShopConfigPayloads(
|
||||
rawShops: unknown[],
|
||||
currentShops: KuaishouIndustryShopConfig[] = [],
|
||||
): KuaishouIndustryShopConfig[] {
|
||||
return rawShops
|
||||
.map((rawShop, index) => normalizeShopConfigPayload(rawShop, index, currentShops))
|
||||
.filter((shop): shop is KuaishouIndustryShopConfig => Boolean(shop))
|
||||
}
|
||||
|
||||
function normalizeShopConfigPayload(
|
||||
rawShop: unknown,
|
||||
index: number,
|
||||
currentShops: KuaishouIndustryShopConfig[],
|
||||
): KuaishouIndustryShopConfig | null {
|
||||
if (!rawShop || typeof rawShop !== 'object' || Array.isArray(rawShop)) {
|
||||
return null
|
||||
}
|
||||
|
||||
const payload = rawShop as JsonObject
|
||||
const current = resolveCurrentShopConfig(payload, index, currentShops)
|
||||
const sellerId = readConfigString(payload, 'sellerId', current?.sellerId || '', { allowBlank: true })
|
||||
const shopId = readConfigString(payload, 'shopId', current?.shopId || sellerId, { allowBlank: true }) || sellerId
|
||||
const shopName = readConfigString(payload, 'shopName', current?.shopName || '', { allowBlank: true })
|
||||
const customShopName = readConfigString(payload, 'customShopName', current?.customShopName || '', { allowBlank: true })
|
||||
const accessToken = readSecretString(payload, 'accessToken', current?.accessToken || '')
|
||||
const refreshToken = readSecretString(payload, 'refreshToken', current?.refreshToken || '')
|
||||
const openId = readConfigString(payload, 'openId', current?.openId || '', { allowBlank: true })
|
||||
|
||||
if (!sellerId && !shopId && !shopName && !customShopName && !accessToken && !refreshToken && !openId) {
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
enabled: hasPayloadField(payload, 'enabled') ? payload.enabled !== false : current?.enabled !== false,
|
||||
sellerId,
|
||||
shopId,
|
||||
shopName,
|
||||
customShopName,
|
||||
authState: readConfigString(payload, 'authState', current?.authState || '', { allowBlank: true }),
|
||||
accessToken,
|
||||
refreshToken,
|
||||
accessTokenExpiresAt: readConfigString(payload, 'accessTokenExpiresAt', current?.accessTokenExpiresAt || '', { allowBlank: true }),
|
||||
refreshTokenExpiresAt: readConfigString(payload, 'refreshTokenExpiresAt', current?.refreshTokenExpiresAt || '', { allowBlank: true }),
|
||||
openId,
|
||||
grantedScopes: normalizeScopeText(readConfigString(payload, 'grantedScopes', current?.grantedScopes || '', { allowBlank: true })),
|
||||
lastRefreshedAt: readConfigString(payload, 'lastRefreshedAt', current?.lastRefreshedAt || '', { allowBlank: true }),
|
||||
lastRefreshError: readConfigString(payload, 'lastRefreshError', current?.lastRefreshError || '', { allowBlank: true }),
|
||||
...resolveShopSecretPatch(payload, current),
|
||||
}
|
||||
}
|
||||
|
||||
function resolveCurrentShopConfig(
|
||||
payload: JsonObject,
|
||||
index: number,
|
||||
currentShops: KuaishouIndustryShopConfig[],
|
||||
): KuaishouIndustryShopConfig | null {
|
||||
const sellerId = String(payload.sellerId || payload.shopId || '').trim()
|
||||
if (sellerId) {
|
||||
const sourceLike = { shops: currentShops } as KuaishouIndustrySourceConfig
|
||||
const matched = findKuaishouIndustryShopConfig(sellerId, sourceLike)
|
||||
if (matched) {
|
||||
return matched
|
||||
}
|
||||
}
|
||||
|
||||
return currentShops[index] || null
|
||||
}
|
||||
|
||||
function resolveShopSecretPatch(
|
||||
payload: JsonObject,
|
||||
current: KuaishouIndustryShopConfig | null,
|
||||
): Partial<KuaishouIndustryShopConfig> {
|
||||
const patch: Partial<KuaishouIndustryShopConfig> = {}
|
||||
for (const field of SHOP_SECRET_FIELDS) {
|
||||
patch[field] = readSecretString(payload, field, current?.[field] || '')
|
||||
}
|
||||
|
||||
return patch
|
||||
}
|
||||
|
||||
function readSecretString(
|
||||
payload: JsonObject,
|
||||
field: string,
|
||||
fallback: string,
|
||||
): string {
|
||||
const text = String(payload[field] || '').trim()
|
||||
return text || fallback
|
||||
}
|
||||
|
||||
function resolveAccessTokenStatus(config: Pick<KuaishouIndustrySourceConfig, 'accessToken' | 'accessTokenExpiresAt'>) {
|
||||
if (!config.accessToken) {
|
||||
return {
|
||||
status: 'missing',
|
||||
|
||||
Reference in New Issue
Block a user