增加feifei 日志

This commit is contained in:
yml2213
2026-07-07 16:04:54 +08:00
parent affba7e924
commit 59f6f5ee1e
4 changed files with 347 additions and 97 deletions
@@ -2,10 +2,7 @@ import crypto from 'node:crypto'
import test from 'node:test'
import assert from 'node:assert/strict'
import {
isKuaishouFeifeiSuccessResponse,
signKuaishouFeifeiPayload,
} from './http-client.js'
import { isKuaishouFeifeiSuccessResponse, signKuaishouFeifeiPayload } from './http-client.js'
test('signKuaishouFeifeiPayload signs app key, timestamp and raw body with HMAC SHA256', () => {
const input = {
@@ -26,6 +23,8 @@ test('signKuaishouFeifeiPayload signs app key, timestamp and raw body with HMAC
test('isKuaishouFeifeiSuccessResponse accepts documented and actual success codes', () => {
assert.equal(isKuaishouFeifeiSuccessResponse({ code: 0, message: 'success' }), true)
assert.equal(isKuaishouFeifeiSuccessResponse({ code: 10000, message: 'success' }), true)
assert.equal(isKuaishouFeifeiSuccessResponse({ code: 10000, message: '下单成功' }), true)
assert.equal(isKuaishouFeifeiSuccessResponse({ code: 10000, message: '查询成功' }), true)
assert.equal(isKuaishouFeifeiSuccessResponse({ code: 10000, message: '商品不存在' }), false)
assert.equal(isKuaishouFeifeiSuccessResponse({ code: 1, message: 'success' }), false)
})
@@ -1,6 +1,7 @@
import crypto from 'node:crypto'
import { createHttpError } from '../../../utils/http.js'
import { createRequestId, logExternalHttpPacket } from '../../../utils/logger.js'
import { assertKuaishouFeifeiConfig } from './config.js'
type JsonObject = Record<string, any>
@@ -9,6 +10,9 @@ export async function kuaishouFeifeiRequest(pathname: string, payload: JsonObjec
const config = assertKuaishouFeifeiConfig()
const body = JSON.stringify(payload)
const timestamp = String(Math.floor(Date.now() / 1000))
const packetId = createRequestId('ff')
const startedAt = Date.now()
let upstreamPacketLogged = false
const sign = signKuaishouFeifeiPayload({
appKey: config.appKey,
appSecret: config.appSecret,
@@ -17,23 +21,62 @@ export async function kuaishouFeifeiRequest(pathname: string, payload: JsonObjec
})
const controller = new AbortController()
const timeout = setTimeout(() => controller.abort(), config.timeoutMs)
const url = `${config.baseUrl}${pathname}`
const requestHeaders = {
'content-type': 'application/json',
'x-app-key': config.appKey,
'x-timestamp': timestamp,
'x-sign': sign,
}
logExternalHttpPacket('[kuaishou-feifei/http]', '发送请求', {
packetId,
method: 'POST',
pathname,
url,
timeoutMs: config.timeoutMs,
request: {
headers: requestHeaders,
body: payload,
rawBody: body,
},
})
try {
const response = await fetch(`${config.baseUrl}${pathname}`, {
const response = await fetch(url, {
method: 'POST',
headers: {
'content-type': 'application/json',
'x-app-key': config.appKey,
'x-timestamp': timestamp,
'x-sign': sign,
},
headers: requestHeaders,
body,
signal: controller.signal,
})
const text = await response.text()
const json = parseJsonObject(text)
const durationMs = Date.now() - startedAt
const responsePacket = {
packetId,
method: 'POST',
pathname,
url,
status: response.status,
durationMs,
request: {
headers: requestHeaders,
body: payload,
},
response: {
ok: response.ok,
headers: normalizeFetchHeaders(response.headers),
summary: summarizeKuaishouFeifeiResponse(json),
body: json,
rawText: text,
},
}
if (!response.ok) {
upstreamPacketLogged = true
logExternalHttpPacket('[kuaishou-feifei/http]', 'HTTP 响应失败', responsePacket, {
level: 'warn',
})
throw createHttpError(`kuaishou-feifei 请求失败: HTTP ${response.status}`, {
statusCode: 502,
errorCode: 'kuaishou_feifei_http_failed',
@@ -45,6 +88,10 @@ export async function kuaishouFeifeiRequest(pathname: string, payload: JsonObjec
}
if (!isKuaishouFeifeiSuccessResponse(json)) {
upstreamPacketLogged = true
logExternalHttpPacket('[kuaishou-feifei/http]', '业务响应失败', responsePacket, {
level: 'warn',
})
throw createHttpError(String(json.message || 'kuaishou-feifei 业务失败'), {
statusCode: 502,
errorCode: 'kuaishou_feifei_business_failed',
@@ -52,15 +99,60 @@ export async function kuaishouFeifeiRequest(pathname: string, payload: JsonObjec
})
}
upstreamPacketLogged = true
logExternalHttpPacket('[kuaishou-feifei/http]', '请求完成', responsePacket)
return json
} catch (error) {
if ((error as Error)?.name === 'AbortError') {
logExternalHttpPacket(
'[kuaishou-feifei/http]',
'请求超时',
{
packetId,
method: 'POST',
pathname,
url,
timeoutMs: config.timeoutMs,
durationMs: Date.now() - startedAt,
request: {
headers: requestHeaders,
body: payload,
},
},
{
level: 'warn',
},
)
throw createHttpError('kuaishou-feifei 请求超时', {
statusCode: 504,
errorCode: 'kuaishou_feifei_timeout',
})
}
if (!upstreamPacketLogged) {
logExternalHttpPacket(
'[kuaishou-feifei/http]',
'请求异常',
{
packetId,
method: 'POST',
pathname,
url,
timeoutMs: config.timeoutMs,
durationMs: Date.now() - startedAt,
request: {
headers: requestHeaders,
body: payload,
},
error,
},
{
level: 'error',
},
)
}
throw error
} finally {
clearTimeout(timeout)
@@ -87,9 +179,11 @@ export function signKuaishouFeifeiPayload({
export function isKuaishouFeifeiSuccessResponse(json: JsonObject) {
const code = Number(json.code ?? 0)
const message = String(json.message || json.msg || '').trim().toLowerCase()
const message = String(json.message || json.msg || '')
.trim()
.toLowerCase()
return code === 0 || (code === 10000 && message === 'success')
return code === 0 || (code === 10000 && isKuaishouFeifeiSuccessMessage(message))
}
function parseJsonObject(text: string): JsonObject {
@@ -100,3 +194,37 @@ function parseJsonObject(text: string): JsonObject {
return {}
}
}
function isKuaishouFeifeiSuccessMessage(message: string) {
return message === 'success' || message === 'ok' || message.endsWith('成功')
}
function normalizeFetchHeaders(headers: Headers) {
const result: Record<string, string> = {}
headers.forEach((value, key) => {
result[key] = value
})
return result
}
function summarizeKuaishouFeifeiResponse(json: JsonObject) {
const data = json.data && typeof json.data === 'object' ? (json.data as JsonObject) : {}
const order = data.order && typeof data.order === 'object' ? (data.order as JsonObject) : null
const h5 = order?.h5 && typeof order.h5 === 'object' ? (order.h5 as JsonObject) : {}
const list = Array.isArray(data.list) ? data.list : []
return {
code: json.code,
message: String(json.message || json.msg || '').trim(),
hasOrder: Boolean(order),
orderNo: String(order?.order_no || '').trim(),
platformOrderNo: String(order?.platform_order_no || '').trim(),
productCode: String(order?.product_code || '').trim(),
rechargeStatus: Number(order?.recharge_status ?? order?.status ?? 0) || 0,
rechargeStatusLabel: String(order?.recharge_status_label || order?.status_label || '').trim(),
hasH5EntryUrl: Boolean(String(h5.entry_url || '').trim()),
hasH5RechargeUrl: Boolean(String(h5.recharge_url || '').trim()),
listCount: list.length,
total: Number(data.total || list.length || 0) || 0,
}
}