后端迁移订单详情服务

This commit is contained in:
yml
2026-05-21 14:56:14 +08:00
parent 6d6e64beda
commit b9d84679a1
2 changed files with 129 additions and 15 deletions
@@ -1,5 +1,3 @@
// @ts-nocheck
import crypto from 'node:crypto' import crypto from 'node:crypto'
import { runtimeConfig } from '../../../../config/runtime.js' import { runtimeConfig } from '../../../../config/runtime.js'
@@ -11,11 +9,113 @@ import { parseJsonObject } from '../../../../utils/json.js'
const DEFAULT_DETAIL_TIMEOUT_MS = 5000 const DEFAULT_DETAIL_TIMEOUT_MS = 5000
export async function enrichAgisoXianyuTradeOrder(parsed, { requestId = '' } = {}) { type JsonObject = Record<string, unknown>
type AgisoParsedTrade = {
provider?: string
platform?: string
shopId?: string
shopName?: string
platformOrderId?: string
totalAmount?: number
paidAt?: string | null
buyerId?: string
buyerName?: string
receiverContact?: string
rawPayload?: JsonObject
items?: AgisoOrderItem[]
[key: string]: unknown
}
type AgisoOrderItem = {
skuCode: string
skuName: string
externalItemId: string
externalSkuCode: string
externalSkuName: string
quantity: number
spec: JsonObject
snapshot: {
externalItemId: string
externalSkuCode: string
externalSkuName: string
}
}
type AgisoDetailQueryInput = {
shopId?: string
platformOrderId?: string
requestId?: string
}
type FetchResponseLike = {
status: number
text: () => Promise<string>
}
type FetchWithTimeout = (
url: string,
options: RequestInit,
timeoutMs: number,
) => Promise<FetchResponseLike>
type AgisoDetailDeps = {
fetchWithTimeout?: FetchWithTimeout
queryAgisoXianyuOrderDetail?: (
input: Required<AgisoDetailQueryInput>,
) => Promise<AgisoDetailResult>
}
type AgisoTradeDetailConfig = {
endpoint: string
apiVersion: string
accessToken: string
appSecret: string
timeoutMs: number
}
type AgisoShopConfig = {
accessToken?: string
appSecret?: string
tradeDetailEndpoint?: string
tradeDetailApiVersion?: string
tradeDetailTimeoutMs?: string | number
}
type DeliveryState = {
shipped: boolean
shipTime: number
orderStatus: number
}
type AgisoDetailResult = {
success: boolean
reason: string
errorMessage: string
responseStatus: number
payload: JsonObject
detailPayload: JsonObject
totalAmountFen: number
shipped: boolean
shipTime: number
orderStatus: number
}
type ExternalSkuDescriptor = {
raw: string
externalSkuCode: string
externalSkuName: string
}
export async function enrichAgisoXianyuTradeOrder(parsed: AgisoParsedTrade, { requestId = '' } = {}) {
return enrichAgisoXianyuTradeOrderWithDeps(parsed, { requestId }) return enrichAgisoXianyuTradeOrderWithDeps(parsed, { requestId })
} }
export async function enrichAgisoXianyuTradeOrderWithDeps(parsed, { requestId = '' } = {}, deps = {}) { export async function enrichAgisoXianyuTradeOrderWithDeps(
parsed: AgisoParsedTrade,
{ requestId = '' } = {},
deps: AgisoDetailDeps = {},
) {
const queryOrderDetail = deps.queryAgisoXianyuOrderDetail || queryAgisoXianyuOrderDetail const queryOrderDetail = deps.queryAgisoXianyuOrderDetail || queryAgisoXianyuOrderDetail
if (!shouldHydrateAgisoXianyuTradeOrder(parsed)) { if (!shouldHydrateAgisoXianyuTradeOrder(parsed)) {
@@ -69,8 +169,8 @@ export async function queryAgisoXianyuOrderDetail({ shopId = '', platformOrderId
export async function queryAgisoXianyuOrderDetailWithDeps( export async function queryAgisoXianyuOrderDetailWithDeps(
{ shopId = '', platformOrderId = '', requestId = '' } = {}, { shopId = '', platformOrderId = '', requestId = '' } = {},
deps = {}, deps: AgisoDetailDeps = {},
) { ): Promise<AgisoDetailResult> {
const requestWithTimeout = deps.fetchWithTimeout || fetchWithTimeout const requestWithTimeout = deps.fetchWithTimeout || fetchWithTimeout
const normalizedShopId = String(shopId || '').trim() const normalizedShopId = String(shopId || '').trim()
const normalizedPlatformOrderId = String(platformOrderId || '').trim() const normalizedPlatformOrderId = String(platformOrderId || '').trim()
@@ -114,7 +214,7 @@ export async function queryAgisoXianyuOrderDetailWithDeps(
body: new URLSearchParams(requestBody).toString(), body: new URLSearchParams(requestBody).toString(),
}, config.timeoutMs) }, config.timeoutMs)
const rawText = await response.text() const rawText = await response.text()
const payload = parseJsonObject(rawText, { preserveLargeIntegers: true }) const payload = parseJsonObject(rawText, { preserveLargeIntegers: true }) as JsonObject
const detailPayload = extractAgisoDetailPayload(payload) const detailPayload = extractAgisoDetailPayload(payload)
const delivery = resolveAgisoXianyuOrderDeliveryState(detailPayload) const delivery = resolveAgisoXianyuOrderDeliveryState(detailPayload)
const totalAmountFen = resolveTotalAmountFen(detailPayload) const totalAmountFen = resolveTotalAmountFen(detailPayload)
@@ -216,16 +316,16 @@ export async function queryAgisoXianyuOrderDetailWithDeps(
} }
} }
function shouldHydrateAgisoXianyuTradeOrder(parsed) { function shouldHydrateAgisoXianyuTradeOrder(parsed: AgisoParsedTrade | null | undefined): boolean {
return parsed?.provider === 'agiso' return parsed?.provider === 'agiso'
&& parsed?.platform === 'xianyu' && parsed?.platform === 'xianyu'
&& String(parsed?.platformOrderId || '').trim() && String(parsed?.platformOrderId || '').trim()
&& Number(parsed?.totalAmount || 0) <= 0 && Number(parsed?.totalAmount || 0) <= 0
} }
function resolveAgisoXianyuTradeDetailConfig(shopId) { function resolveAgisoXianyuTradeDetailConfig(shopId: string): AgisoTradeDetailConfig {
const baseConfig = runtimeConfig.platforms?.agiso?.tradeDetail || {} const baseConfig = runtimeConfig.platforms.agiso.tradeDetail
const shopConfig = getAgisoShopConfig(shopId) || {} const shopConfig = (getAgisoShopConfig(shopId) || {}) as AgisoShopConfig
return { return {
endpoint: String(shopConfig.tradeDetailEndpoint || baseConfig.endpoint || '').trim(), endpoint: String(shopConfig.tradeDetailEndpoint || baseConfig.endpoint || '').trim(),
@@ -236,7 +336,10 @@ function resolveAgisoXianyuTradeDetailConfig(shopId) {
} }
} }
function buildRequestHeaders({ accessToken, apiVersion }) { function buildRequestHeaders({
accessToken,
apiVersion,
}: { accessToken: string; apiVersion: string }): Record<string, string> {
return { return {
Authorization: `Bearer ${accessToken}`, Authorization: `Bearer ${accessToken}`,
ApiVersion: apiVersion, ApiVersion: apiVersion,
@@ -244,9 +347,12 @@ function buildRequestHeaders({ accessToken, apiVersion }) {
} }
} }
function buildRequestBody({ platformOrderId, appSecret }) { function buildRequestBody({
platformOrderId,
appSecret,
}: { platformOrderId: string; appSecret: string }): Record<string, string> {
const timestamp = String(Math.floor(Date.now() / 1000)) const timestamp = String(Math.floor(Date.now() / 1000))
const payload = { const payload: Record<string, string> = {
tid: String(platformOrderId || '').trim(), tid: String(platformOrderId || '').trim(),
timestamp, timestamp,
} }
@@ -255,7 +361,7 @@ function buildRequestBody({ platformOrderId, appSecret }) {
return payload return payload
} }
function generateSign(params, appSecret) { function generateSign(params: Record<string, string>, appSecret: string): string {
const sortedEntries = Object.entries(params).sort(([left], [right]) => left.localeCompare(right)) const sortedEntries = Object.entries(params).sort(([left], [right]) => left.localeCompare(right))
let raw = appSecret let raw = appSecret
@@ -349,6 +349,14 @@
- `npm run typecheck` - `npm run typecheck`
- `npm run build` - `npm run build`
- `npm test` 共 136 个用例通过 - `npm test` 共 136 个用例通过
57. Agiso 订单详情服务迁移到 `.ts`
- `src/services/platforms/agiso/xianyu/order-detail-service.ts`
58. 订单详情补查入参、依赖注入、HTTP 响应、详情查询结果、发货状态、店铺配置、外部 SKU 解析结果已显式类型化
59. Docker 内验证通过:
- `src/services/platforms/agiso/xianyu/order-detail-service.test.js` 共 5 个用例通过
- `npm run typecheck`
- `npm run build`
- `npm test` 共 136 个用例通过
## 下一步建议 ## 下一步建议