优化领取结果和CloudTentacles平台兑换购买记录
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { createHttpError } from '../../../utils/http.js'
|
||||
import { createHttpError, type HttpErrorLike } from '../../../utils/http.js'
|
||||
import { cloudtentaclesRequest } from './http-client.js'
|
||||
import { resolveCloudtentaclesConfig } from './helpers.js'
|
||||
|
||||
@@ -86,6 +86,13 @@ export async function buyCloudtentaclesSku(payload: JsonObject = {}) {
|
||||
},
|
||||
businessErrorStatusCode: 401,
|
||||
businessErrorCode: 'cloudtentacles_sku_buy_failed',
|
||||
context: {
|
||||
operation: 'sku_buy',
|
||||
skuId,
|
||||
count,
|
||||
sourceKey: payload.sourceKey,
|
||||
accountLabel: payload.accountLabel,
|
||||
},
|
||||
})
|
||||
|
||||
return {
|
||||
@@ -113,17 +120,14 @@ export async function useCloudtentaclesSku(payload: JsonObject = {}) {
|
||||
}
|
||||
|
||||
const config = resolveCloudtentaclesConfig(payload)
|
||||
const result = await cloudtentaclesRequest(config.skuUsePath, {
|
||||
const result = await useCloudtentaclesSkuRequest({
|
||||
...config,
|
||||
method: 'POST',
|
||||
token,
|
||||
body: {
|
||||
id: skuId,
|
||||
virtual_number_id: virtualNumberId,
|
||||
phone,
|
||||
},
|
||||
businessErrorStatusCode: 401,
|
||||
businessErrorCode: 'cloudtentacles_sku_use_failed',
|
||||
skuId,
|
||||
virtualNumberId,
|
||||
phone,
|
||||
sourceKey: payload.sourceKey,
|
||||
accountLabel: payload.accountLabel,
|
||||
})
|
||||
|
||||
const responseData = isPlainObject(result.payload?.data) ? result.payload.data : {}
|
||||
@@ -140,6 +144,53 @@ export async function useCloudtentaclesSku(payload: JsonObject = {}) {
|
||||
}
|
||||
}
|
||||
|
||||
async function useCloudtentaclesSkuRequest(payload: JsonObject) {
|
||||
try {
|
||||
return await cloudtentaclesRequest(payload.skuUsePath, {
|
||||
...payload,
|
||||
method: 'POST',
|
||||
token: payload.token,
|
||||
body: {
|
||||
id: payload.skuId,
|
||||
virtual_number_id: payload.virtualNumberId,
|
||||
phone: payload.phone,
|
||||
},
|
||||
businessErrorStatusCode: 401,
|
||||
businessErrorCode: 'cloudtentacles_sku_use_failed',
|
||||
context: {
|
||||
operation: 'sku_use',
|
||||
skuId: payload.skuId,
|
||||
virtualNumberId: payload.virtualNumberId,
|
||||
phoneMasked: maskPhone(payload.phone),
|
||||
sourceKey: payload.sourceKey,
|
||||
accountLabel: payload.accountLabel,
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
if (isCloudtentaclesSkuUseLimitMessage(error instanceof Error ? error.message : '')) {
|
||||
const currentError = error as Error & HttpErrorLike
|
||||
currentError.statusCode = 409
|
||||
currentError.errorCode = 'cloudtentacles_sku_use_limit_reached'
|
||||
}
|
||||
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
function isCloudtentaclesSkuUseLimitMessage(message: unknown) {
|
||||
const normalized = String(message || '').trim()
|
||||
return normalized.includes('最大领取次数') || normalized.includes('领取次数')
|
||||
}
|
||||
|
||||
function maskPhone(value: unknown) {
|
||||
const text = String(value || '').trim()
|
||||
if (text.length <= 4) {
|
||||
return text ? '****' : ''
|
||||
}
|
||||
|
||||
return `${text.slice(0, 3)}****${text.slice(-4)}`
|
||||
}
|
||||
|
||||
function mapCategoryItem(item: unknown) {
|
||||
const source = isPlainObject(item) ? item : {}
|
||||
|
||||
|
||||
@@ -67,11 +67,30 @@ export async function cloudtentaclesRequest(pathname: unknown, options: JsonObje
|
||||
continue
|
||||
}
|
||||
|
||||
logWarn('[cloudtentacles/http]', '请求 HTTP 失败', {
|
||||
method,
|
||||
pathname: normalizedPathname,
|
||||
status: response.status,
|
||||
attempt: attempt + 1,
|
||||
sourceKey: options.sourceKey,
|
||||
accountLabel: options.accountLabel,
|
||||
context: options.context,
|
||||
upstreamPayload: payload,
|
||||
rawText: truncateText(rawText),
|
||||
})
|
||||
|
||||
throw createHttpError(`cloudtentacles 请求失败,HTTP ${response.status}`, {
|
||||
statusCode: isCloudtentaclesHttpRateLimited(response.status) ? 429 : 502,
|
||||
errorCode: isCloudtentaclesHttpRateLimited(response.status)
|
||||
? 'cloudtentacles_rate_limited'
|
||||
: 'cloudtentacles_http_error',
|
||||
context: {
|
||||
method,
|
||||
pathname: normalizedPathname,
|
||||
upstreamStatus: response.status,
|
||||
upstreamPayload: payload,
|
||||
requestContext: options.context,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -106,9 +125,32 @@ export async function cloudtentaclesRequest(pathname: unknown, options: JsonObje
|
||||
})
|
||||
}
|
||||
|
||||
logWarn('[cloudtentacles/http]', '请求业务失败', {
|
||||
method,
|
||||
pathname: normalizedPathname,
|
||||
status: response.status,
|
||||
attempt: attempt + 1,
|
||||
errorCode,
|
||||
upstreamCode: payload?.code,
|
||||
upstreamMessage: message,
|
||||
sourceKey: options.sourceKey,
|
||||
accountLabel: options.accountLabel,
|
||||
context: options.context,
|
||||
upstreamPayload: payload,
|
||||
})
|
||||
|
||||
throw createHttpError(message, {
|
||||
statusCode,
|
||||
errorCode,
|
||||
context: {
|
||||
method,
|
||||
pathname: normalizedPathname,
|
||||
upstreamStatus: response.status,
|
||||
upstreamCode: payload?.code,
|
||||
upstreamMessage: message,
|
||||
upstreamPayload: payload,
|
||||
requestContext: options.context,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -157,6 +199,15 @@ function isCloudtentaclesHttpRateLimited(status: unknown) {
|
||||
return Number(status) === 429
|
||||
}
|
||||
|
||||
function truncateText(value: unknown, maxLength = 1000) {
|
||||
const text = String(value || '')
|
||||
if (text.length <= maxLength) {
|
||||
return text
|
||||
}
|
||||
|
||||
return `${text.slice(0, maxLength)}...`
|
||||
}
|
||||
|
||||
async function executeCloudtentaclesRequest({
|
||||
url,
|
||||
method,
|
||||
|
||||
Reference in New Issue
Block a user