后端迁移订单详情服务
This commit is contained in:
+121
-15
@@ -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 个用例通过
|
||||||
|
|
||||||
## 下一步建议
|
## 下一步建议
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user