后端迁移订单详情服务
This commit is contained in:
+121
-15
@@ -1,5 +1,3 @@
|
||||
// @ts-nocheck
|
||||
|
||||
import crypto from 'node:crypto'
|
||||
|
||||
import { runtimeConfig } from '../../../../config/runtime.js'
|
||||
@@ -11,11 +9,113 @@ import { parseJsonObject } from '../../../../utils/json.js'
|
||||
|
||||
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 })
|
||||
}
|
||||
|
||||
export async function enrichAgisoXianyuTradeOrderWithDeps(parsed, { requestId = '' } = {}, deps = {}) {
|
||||
export async function enrichAgisoXianyuTradeOrderWithDeps(
|
||||
parsed: AgisoParsedTrade,
|
||||
{ requestId = '' } = {},
|
||||
deps: AgisoDetailDeps = {},
|
||||
) {
|
||||
const queryOrderDetail = deps.queryAgisoXianyuOrderDetail || queryAgisoXianyuOrderDetail
|
||||
|
||||
if (!shouldHydrateAgisoXianyuTradeOrder(parsed)) {
|
||||
@@ -69,8 +169,8 @@ export async function queryAgisoXianyuOrderDetail({ shopId = '', platformOrderId
|
||||
|
||||
export async function queryAgisoXianyuOrderDetailWithDeps(
|
||||
{ shopId = '', platformOrderId = '', requestId = '' } = {},
|
||||
deps = {},
|
||||
) {
|
||||
deps: AgisoDetailDeps = {},
|
||||
): Promise<AgisoDetailResult> {
|
||||
const requestWithTimeout = deps.fetchWithTimeout || fetchWithTimeout
|
||||
const normalizedShopId = String(shopId || '').trim()
|
||||
const normalizedPlatformOrderId = String(platformOrderId || '').trim()
|
||||
@@ -114,7 +214,7 @@ export async function queryAgisoXianyuOrderDetailWithDeps(
|
||||
body: new URLSearchParams(requestBody).toString(),
|
||||
}, config.timeoutMs)
|
||||
const rawText = await response.text()
|
||||
const payload = parseJsonObject(rawText, { preserveLargeIntegers: true })
|
||||
const payload = parseJsonObject(rawText, { preserveLargeIntegers: true }) as JsonObject
|
||||
const detailPayload = extractAgisoDetailPayload(payload)
|
||||
const delivery = resolveAgisoXianyuOrderDeliveryState(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'
|
||||
&& parsed?.platform === 'xianyu'
|
||||
&& String(parsed?.platformOrderId || '').trim()
|
||||
&& Number(parsed?.totalAmount || 0) <= 0
|
||||
}
|
||||
|
||||
function resolveAgisoXianyuTradeDetailConfig(shopId) {
|
||||
const baseConfig = runtimeConfig.platforms?.agiso?.tradeDetail || {}
|
||||
const shopConfig = getAgisoShopConfig(shopId) || {}
|
||||
function resolveAgisoXianyuTradeDetailConfig(shopId: string): AgisoTradeDetailConfig {
|
||||
const baseConfig = runtimeConfig.platforms.agiso.tradeDetail
|
||||
const shopConfig = (getAgisoShopConfig(shopId) || {}) as AgisoShopConfig
|
||||
|
||||
return {
|
||||
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 {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
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 payload = {
|
||||
const payload: Record<string, string> = {
|
||||
tid: String(platformOrderId || '').trim(),
|
||||
timestamp,
|
||||
}
|
||||
@@ -255,7 +361,7 @@ function buildRequestBody({ platformOrderId, appSecret }) {
|
||||
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))
|
||||
let raw = appSecret
|
||||
|
||||
@@ -349,6 +349,14 @@
|
||||
- `npm run typecheck`
|
||||
- `npm run build`
|
||||
- `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 个用例通过
|
||||
|
||||
## 下一步建议
|
||||
|
||||
|
||||
Reference in New Issue
Block a user