后端迁移自动发货服务
This commit is contained in:
+185
-59
@@ -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,23 +9,125 @@ import { ensureAgisoXianyuAutoDeliveryMessageDeliveredForTask } from './message-
|
|||||||
import { parseJsonObject } from '../../../../utils/json.js'
|
import { parseJsonObject } from '../../../../utils/json.js'
|
||||||
import { logWebhook } from '../../../../utils/logger.js'
|
import { logWebhook } from '../../../../utils/logger.js'
|
||||||
import { nowIso } from '../../../../utils/time.js'
|
import { nowIso } from '../../../../utils/time.js'
|
||||||
|
import type { TaskRow } from '../../../../types/repository-rows.js'
|
||||||
|
|
||||||
const AGISO_DUMMY_SEND_ENDPOINT = 'https://gw-api.agiso.com/aldsIdle/Order/DummySend'
|
const AGISO_DUMMY_SEND_ENDPOINT = 'https://gw-api.agiso.com/aldsIdle/Order/DummySend'
|
||||||
|
|
||||||
/**
|
type JsonObject = Record<string, unknown>
|
||||||
* @typedef {{
|
|
||||||
* order?: Record<string, any> | null
|
type AutoDeliveryOrder = {
|
||||||
* task?: Record<string, any> | null
|
id?: number | string
|
||||||
* trigger?: string
|
provider?: string
|
||||||
* }} EnsureAgisoXianyuAutoDeliveryInput
|
platform?: string
|
||||||
*/
|
shop_id?: string
|
||||||
|
shop_name?: string
|
||||||
|
platform_order_id?: string
|
||||||
|
[key: string]: unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
type SupportedAgisoOrder = AutoDeliveryOrder & {
|
||||||
|
id: number | string
|
||||||
|
provider: 'agiso'
|
||||||
|
platform: 'xianyu'
|
||||||
|
platform_order_id: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type AutoDeliveryTask = Partial<TaskRow> & {
|
||||||
|
id?: number | string
|
||||||
|
delivery_status?: string
|
||||||
|
task_status?: string
|
||||||
|
context_json?: string | JsonObject | null
|
||||||
|
[key: string]: unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
type AutoDeliveryTaskWithId = AutoDeliveryTask & {
|
||||||
|
id: number | string
|
||||||
|
}
|
||||||
|
|
||||||
|
type EnsureAgisoXianyuAutoDeliveryInput = {
|
||||||
|
order?: AutoDeliveryOrder | null
|
||||||
|
task?: AutoDeliveryTask | null
|
||||||
|
trigger?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type AutoDeliveryStatus = 'success' | 'failed' | 'skipped'
|
||||||
|
|
||||||
|
type FetchResponseLike = {
|
||||||
|
status: number
|
||||||
|
text: () => Promise<string>
|
||||||
|
}
|
||||||
|
|
||||||
|
type FetchLike = (
|
||||||
|
input: string | URL,
|
||||||
|
init?: RequestInit,
|
||||||
|
) => Promise<FetchResponseLike>
|
||||||
|
|
||||||
|
type AgisoAutoDeliveryConfirmInput = {
|
||||||
|
shopId?: string
|
||||||
|
platformOrderId?: string
|
||||||
|
requestId?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type AgisoAutoDeliveryConfirmResult = {
|
||||||
|
shipped: boolean
|
||||||
|
orderStatus: number
|
||||||
|
shipTime: number
|
||||||
|
reason: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type AutoDeliveryMessageResult = {
|
||||||
|
sent: boolean
|
||||||
|
skipped?: boolean
|
||||||
|
reason?: string
|
||||||
|
[key: string]: unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
type EnsureAgisoXianyuAutoDeliveryDeps = {
|
||||||
|
listTasksByOrderId?: (orderId: number | string) => Promise<AutoDeliveryTask[]>
|
||||||
|
fetch?: FetchLike
|
||||||
|
confirmAgisoXianyuAutoDeliveryShipped?: (
|
||||||
|
input: AgisoAutoDeliveryConfirmInput,
|
||||||
|
) => Promise<AgisoAutoDeliveryConfirmResult>
|
||||||
|
updateTask?: (
|
||||||
|
taskId: number | string,
|
||||||
|
patch: { context_json: string; updated_at: string },
|
||||||
|
) => Promise<AutoDeliveryTask | null>
|
||||||
|
createTaskEvent?: (
|
||||||
|
taskId: number | string,
|
||||||
|
eventType: string,
|
||||||
|
payload?: unknown,
|
||||||
|
createdAt?: string,
|
||||||
|
) => Promise<unknown>
|
||||||
|
nowIso?: () => string
|
||||||
|
ensureAgisoXianyuAutoDeliveryMessageDeliveredForTask?: (
|
||||||
|
input: { order?: AutoDeliveryOrder | null; task?: AutoDeliveryTask | null },
|
||||||
|
) => Promise<AutoDeliveryMessageResult>
|
||||||
|
}
|
||||||
|
|
||||||
|
type PersistAgisoAutoDeliveryResultInput = {
|
||||||
|
status: AutoDeliveryStatus
|
||||||
|
trigger?: string
|
||||||
|
reason?: string
|
||||||
|
order?: AutoDeliveryOrder | null
|
||||||
|
responseStatus?: number
|
||||||
|
response?: unknown
|
||||||
|
errorMessage?: string
|
||||||
|
detail?: JsonObject
|
||||||
|
}
|
||||||
|
|
||||||
|
type AgisoAutoDeliveryConfig = {
|
||||||
|
enabled: boolean
|
||||||
|
endpoint: string
|
||||||
|
apiVersion: string
|
||||||
|
appSecret: string
|
||||||
|
accessToken: string
|
||||||
|
}
|
||||||
|
|
||||||
/** @param {EnsureAgisoXianyuAutoDeliveryInput} input */
|
|
||||||
export async function ensureAgisoXianyuAutoDeliveryForDeliveredTask({
|
export async function ensureAgisoXianyuAutoDeliveryForDeliveredTask({
|
||||||
order,
|
order,
|
||||||
task,
|
task,
|
||||||
trigger = 'task_delivered',
|
trigger = 'task_delivered',
|
||||||
} = {}) {
|
}: EnsureAgisoXianyuAutoDeliveryInput = {}) {
|
||||||
return ensureAgisoXianyuAutoDeliveryForDeliveredTaskWithDeps({ order, task, trigger })
|
return ensureAgisoXianyuAutoDeliveryForDeliveredTaskWithDeps({ order, task, trigger })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,29 +135,30 @@ export async function ensureAgisoXianyuAutoDeliveryForDeliveredTaskWithDeps({
|
|||||||
order,
|
order,
|
||||||
task,
|
task,
|
||||||
trigger = 'task_delivered',
|
trigger = 'task_delivered',
|
||||||
} = {}, deps = {}) {
|
}: EnsureAgisoXianyuAutoDeliveryInput = {}, deps: EnsureAgisoXianyuAutoDeliveryDeps = {}) {
|
||||||
const listOrderTasks = deps.listTasksByOrderId || listTasksByOrderId
|
const listOrderTasks = deps.listTasksByOrderId || listTasksByOrderId
|
||||||
const sendRequest = deps.fetch || fetch
|
const sendRequest = deps.fetch || (fetch as FetchLike)
|
||||||
const confirmShipped = deps.confirmAgisoXianyuAutoDeliveryShipped || confirmAgisoXianyuAutoDeliveryShipped
|
const confirmShipped = deps.confirmAgisoXianyuAutoDeliveryShipped || confirmAgisoXianyuAutoDeliveryShipped
|
||||||
|
|
||||||
if (!isAgisoXianyuOrder(order) || !task?.id) {
|
if (!isAgisoXianyuOrder(order) || !task?.id) {
|
||||||
return { sent: false, skipped: true, reason: 'not_supported', task }
|
return { sent: false, skipped: true, reason: 'not_supported', task }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (String(task.delivery_status || '').trim() !== 'delivered') {
|
const currentTask = task as AutoDeliveryTaskWithId
|
||||||
return { sent: false, skipped: true, reason: 'task_not_delivered', task }
|
if (String(currentTask.delivery_status || '').trim() !== 'delivered') {
|
||||||
|
return { sent: false, skipped: true, reason: 'task_not_delivered', task: currentTask }
|
||||||
}
|
}
|
||||||
|
|
||||||
const orderTasks = await listOrderTasks(order.id)
|
const orderTasks = await listOrderTasks(order.id)
|
||||||
if (hasAgisoAutoDeliverySucceeded(orderTasks)) {
|
if (hasAgisoAutoDeliverySucceeded(orderTasks)) {
|
||||||
return { sent: false, skipped: true, reason: 'already_sent', task }
|
return { sent: false, skipped: true, reason: 'already_sent', task: currentTask }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isOrderReadyForAgisoAutoDelivery(orderTasks)) {
|
if (!isOrderReadyForAgisoAutoDelivery(orderTasks)) {
|
||||||
logWebhook('[agiso/xianyu/auto-delivery]', '跳过自动发货:订单下仍有任务未完成交付', {
|
logWebhook('[agiso/xianyu/auto-delivery]', '跳过自动发货:订单下仍有任务未完成交付', {
|
||||||
trigger,
|
trigger,
|
||||||
orderId: order.id,
|
orderId: order.id,
|
||||||
taskId: task.id,
|
taskId: currentTask.id,
|
||||||
platformOrderId: order.platform_order_id,
|
platformOrderId: order.platform_order_id,
|
||||||
taskStatuses: orderTasks.map((current) => ({
|
taskStatuses: orderTasks.map((current) => ({
|
||||||
taskId: current.id,
|
taskId: current.id,
|
||||||
@@ -66,12 +167,12 @@ export async function ensureAgisoXianyuAutoDeliveryForDeliveredTaskWithDeps({
|
|||||||
})),
|
})),
|
||||||
})
|
})
|
||||||
|
|
||||||
return { sent: false, skipped: true, reason: 'waiting_other_tasks', task }
|
return { sent: false, skipped: true, reason: 'waiting_other_tasks', task: currentTask }
|
||||||
}
|
}
|
||||||
|
|
||||||
const config = resolveAgisoXianyuAutoDeliveryConfig(order)
|
const config = resolveAgisoXianyuAutoDeliveryConfig(order)
|
||||||
if (!config.enabled) {
|
if (!config.enabled) {
|
||||||
return persistAgisoAutoDeliveryResult(task, {
|
return persistAgisoAutoDeliveryResult(currentTask, {
|
||||||
status: 'skipped',
|
status: 'skipped',
|
||||||
trigger,
|
trigger,
|
||||||
reason: 'auto_delivery_disabled',
|
reason: 'auto_delivery_disabled',
|
||||||
@@ -80,7 +181,7 @@ export async function ensureAgisoXianyuAutoDeliveryForDeliveredTaskWithDeps({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!config.endpoint || !config.accessToken || !config.appSecret) {
|
if (!config.endpoint || !config.accessToken || !config.appSecret) {
|
||||||
return persistAgisoAutoDeliveryResult(task, {
|
return persistAgisoAutoDeliveryResult(currentTask, {
|
||||||
status: 'skipped',
|
status: 'skipped',
|
||||||
trigger,
|
trigger,
|
||||||
reason: 'missing_config',
|
reason: 'missing_config',
|
||||||
@@ -105,7 +206,7 @@ export async function ensureAgisoXianyuAutoDeliveryForDeliveredTaskWithDeps({
|
|||||||
logWebhook('[agiso/xianyu/auto-delivery]', '开始执行 Agiso 咸鱼自动发货', {
|
logWebhook('[agiso/xianyu/auto-delivery]', '开始执行 Agiso 咸鱼自动发货', {
|
||||||
trigger,
|
trigger,
|
||||||
orderId: order.id,
|
orderId: order.id,
|
||||||
taskId: task.id,
|
taskId: currentTask.id,
|
||||||
shopId: order.shop_id,
|
shopId: order.shop_id,
|
||||||
platformOrderId: order.platform_order_id,
|
platformOrderId: order.platform_order_id,
|
||||||
endpoint: config.endpoint,
|
endpoint: config.endpoint,
|
||||||
@@ -118,13 +219,13 @@ export async function ensureAgisoXianyuAutoDeliveryForDeliveredTaskWithDeps({
|
|||||||
body: new URLSearchParams(requestBody).toString(),
|
body: new URLSearchParams(requestBody).toString(),
|
||||||
})
|
})
|
||||||
const rawText = await response.text()
|
const rawText = await response.text()
|
||||||
const parsed = parseJsonObject(rawText)
|
const parsed = parseJsonObject(rawText) as JsonObject
|
||||||
const success = isAgisoAutoDeliverySuccess(response.status, parsed)
|
const success = isAgisoAutoDeliverySuccess(response.status, parsed)
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
const requestId = String(parsed?.RequestId || '').trim()
|
const requestId = String(parsed?.RequestId || '').trim()
|
||||||
|
|
||||||
// 发货接口返回成功后,再查一次 Order/Detail 确认订单真的进入已发货状态 meow~
|
// 发货接口返回成功后,再查一次 Order/Detail 确认订单真的进入已发货状态。
|
||||||
const confirmResult = await confirmShipped({
|
const confirmResult = await confirmShipped({
|
||||||
shopId: order.shop_id,
|
shopId: order.shop_id,
|
||||||
platformOrderId: order.platform_order_id,
|
platformOrderId: order.platform_order_id,
|
||||||
@@ -135,7 +236,7 @@ export async function ensureAgisoXianyuAutoDeliveryForDeliveredTaskWithDeps({
|
|||||||
logWebhook('[agiso/xianyu/auto-delivery]', 'Agiso 咸鱼自动发货接口返回成功,但订单状态未确认进入已发货', {
|
logWebhook('[agiso/xianyu/auto-delivery]', 'Agiso 咸鱼自动发货接口返回成功,但订单状态未确认进入已发货', {
|
||||||
trigger,
|
trigger,
|
||||||
orderId: order.id,
|
orderId: order.id,
|
||||||
taskId: task.id,
|
taskId: currentTask.id,
|
||||||
shopId: order.shop_id,
|
shopId: order.shop_id,
|
||||||
platformOrderId: order.platform_order_id,
|
platformOrderId: order.platform_order_id,
|
||||||
responseStatus: response.status,
|
responseStatus: response.status,
|
||||||
@@ -145,7 +246,7 @@ export async function ensureAgisoXianyuAutoDeliveryForDeliveredTaskWithDeps({
|
|||||||
confirmReason: confirmResult.reason,
|
confirmReason: confirmResult.reason,
|
||||||
}, { level: 'warn' })
|
}, { level: 'warn' })
|
||||||
|
|
||||||
return persistAgisoAutoDeliveryResult(task, {
|
return persistAgisoAutoDeliveryResult(currentTask, {
|
||||||
status: 'failed',
|
status: 'failed',
|
||||||
trigger,
|
trigger,
|
||||||
reason: 'delivery_not_confirmed',
|
reason: 'delivery_not_confirmed',
|
||||||
@@ -164,7 +265,7 @@ export async function ensureAgisoXianyuAutoDeliveryForDeliveredTaskWithDeps({
|
|||||||
logWebhook('[agiso/xianyu/auto-delivery]', 'Agiso 咸鱼自动发货成功,订单已确认进入已发货状态', {
|
logWebhook('[agiso/xianyu/auto-delivery]', 'Agiso 咸鱼自动发货成功,订单已确认进入已发货状态', {
|
||||||
trigger,
|
trigger,
|
||||||
orderId: order.id,
|
orderId: order.id,
|
||||||
taskId: task.id,
|
taskId: currentTask.id,
|
||||||
shopId: order.shop_id,
|
shopId: order.shop_id,
|
||||||
platformOrderId: order.platform_order_id,
|
platformOrderId: order.platform_order_id,
|
||||||
responseStatus: response.status,
|
responseStatus: response.status,
|
||||||
@@ -173,7 +274,7 @@ export async function ensureAgisoXianyuAutoDeliveryForDeliveredTaskWithDeps({
|
|||||||
confirmShipTime: confirmResult.shipTime,
|
confirmShipTime: confirmResult.shipTime,
|
||||||
})
|
})
|
||||||
|
|
||||||
const result = await persistAgisoAutoDeliveryResult(task, {
|
const result = await persistAgisoAutoDeliveryResult(currentTask, {
|
||||||
status: 'success',
|
status: 'success',
|
||||||
trigger,
|
trigger,
|
||||||
reason: '',
|
reason: '',
|
||||||
@@ -187,8 +288,8 @@ export async function ensureAgisoXianyuAutoDeliveryForDeliveredTaskWithDeps({
|
|||||||
},
|
},
|
||||||
}, deps)
|
}, deps)
|
||||||
|
|
||||||
// 发货确认成功后,发送自定义消息通知 meow~
|
// 发货确认成功后,发送自定义消息通知。
|
||||||
await sendAutoDeliveryMessage({ order, task }, deps)
|
await sendAutoDeliveryMessage({ order, task: currentTask }, deps)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@@ -197,7 +298,7 @@ export async function ensureAgisoXianyuAutoDeliveryForDeliveredTaskWithDeps({
|
|||||||
logWebhook('[agiso/xianyu/auto-delivery]', 'Agiso 咸鱼自动发货失败', {
|
logWebhook('[agiso/xianyu/auto-delivery]', 'Agiso 咸鱼自动发货失败', {
|
||||||
trigger,
|
trigger,
|
||||||
orderId: order.id,
|
orderId: order.id,
|
||||||
taskId: task.id,
|
taskId: currentTask.id,
|
||||||
shopId: order.shop_id,
|
shopId: order.shop_id,
|
||||||
platformOrderId: order.platform_order_id,
|
platformOrderId: order.platform_order_id,
|
||||||
responseStatus: response.status,
|
responseStatus: response.status,
|
||||||
@@ -205,7 +306,7 @@ export async function ensureAgisoXianyuAutoDeliveryForDeliveredTaskWithDeps({
|
|||||||
response: parsed,
|
response: parsed,
|
||||||
}, { level: 'warn' })
|
}, { level: 'warn' })
|
||||||
|
|
||||||
return persistAgisoAutoDeliveryResult(task, {
|
return persistAgisoAutoDeliveryResult(currentTask, {
|
||||||
status: 'failed',
|
status: 'failed',
|
||||||
trigger,
|
trigger,
|
||||||
reason: 'request_failed',
|
reason: 'request_failed',
|
||||||
@@ -219,13 +320,13 @@ export async function ensureAgisoXianyuAutoDeliveryForDeliveredTaskWithDeps({
|
|||||||
logWebhook('[agiso/xianyu/auto-delivery]', 'Agiso 咸鱼自动发货异常', {
|
logWebhook('[agiso/xianyu/auto-delivery]', 'Agiso 咸鱼自动发货异常', {
|
||||||
trigger,
|
trigger,
|
||||||
orderId: order.id,
|
orderId: order.id,
|
||||||
taskId: task.id,
|
taskId: currentTask.id,
|
||||||
shopId: order.shop_id,
|
shopId: order.shop_id,
|
||||||
platformOrderId: order.platform_order_id,
|
platformOrderId: order.platform_order_id,
|
||||||
errorMessage,
|
errorMessage,
|
||||||
}, { level: 'warn' })
|
}, { level: 'warn' })
|
||||||
|
|
||||||
return persistAgisoAutoDeliveryResult(task, {
|
return persistAgisoAutoDeliveryResult(currentTask, {
|
||||||
status: 'failed',
|
status: 'failed',
|
||||||
trigger,
|
trigger,
|
||||||
reason: 'request_error',
|
reason: 'request_error',
|
||||||
@@ -237,21 +338,22 @@ export async function ensureAgisoXianyuAutoDeliveryForDeliveredTaskWithDeps({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hasAgisoAutoDeliverySucceeded(tasks = []) {
|
export function hasAgisoAutoDeliverySucceeded(tasks: AutoDeliveryTask[] = []): boolean {
|
||||||
return (Array.isArray(tasks) ? tasks : []).some((task) => {
|
return (Array.isArray(tasks) ? tasks : []).some((task) => {
|
||||||
const context = parseTaskContext(task)
|
const context = parseTaskContext(task)
|
||||||
return String(context?.agisoAutoDelivery?.status || '').trim() === 'success'
|
const autoDelivery = isPlainObject(context.agisoAutoDelivery) ? context.agisoAutoDelivery : {}
|
||||||
|
return String(autoDelivery.status || '').trim() === 'success'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isOrderReadyForAgisoAutoDelivery(tasks = []) {
|
export function isOrderReadyForAgisoAutoDelivery(tasks: AutoDeliveryTask[] = []): boolean {
|
||||||
const normalizedTasks = Array.isArray(tasks) ? tasks.filter(Boolean) : []
|
const normalizedTasks = Array.isArray(tasks) ? tasks.filter(Boolean) : []
|
||||||
return normalizedTasks.length > 0
|
return normalizedTasks.length > 0
|
||||||
&& normalizedTasks.every((task) => String(task.delivery_status || '').trim() === 'delivered')
|
&& normalizedTasks.every((task) => String(task.delivery_status || '').trim() === 'delivered')
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveAgisoXianyuAutoDeliveryConfig(order) {
|
function resolveAgisoXianyuAutoDeliveryConfig(order: AutoDeliveryOrder): AgisoAutoDeliveryConfig {
|
||||||
const baseConfig = runtimeConfig.platforms?.agiso?.autoDelivery || {}
|
const baseConfig = runtimeConfig.platforms.agiso.autoDelivery
|
||||||
const shopConfig = getAgisoShopConfig(String(order?.shop_id || '').trim()) || {}
|
const shopConfig = getAgisoShopConfig(String(order?.shop_id || '').trim()) || {}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -263,7 +365,10 @@ function resolveAgisoXianyuAutoDeliveryConfig(order) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildRequestHeaders({ accessToken, apiVersion }) {
|
function buildRequestHeaders({
|
||||||
|
accessToken,
|
||||||
|
apiVersion,
|
||||||
|
}: { accessToken: string; apiVersion: string }): Record<string, string> {
|
||||||
return {
|
return {
|
||||||
Authorization: `Bearer ${accessToken}`,
|
Authorization: `Bearer ${accessToken}`,
|
||||||
ApiVersion: String(apiVersion || '1').trim() || '1',
|
ApiVersion: String(apiVersion || '1').trim() || '1',
|
||||||
@@ -271,8 +376,11 @@ function buildRequestHeaders({ accessToken, apiVersion }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildRequestBody({ platformOrderId, appSecret }) {
|
function buildRequestBody({
|
||||||
const payload = {
|
platformOrderId,
|
||||||
|
appSecret,
|
||||||
|
}: { platformOrderId: string; appSecret: string }): Record<string, string> {
|
||||||
|
const payload: Record<string, string> = {
|
||||||
tid: String(platformOrderId || '').trim(),
|
tid: String(platformOrderId || '').trim(),
|
||||||
timestamp: String(Math.floor(Date.now() / 1000)),
|
timestamp: String(Math.floor(Date.now() / 1000)),
|
||||||
}
|
}
|
||||||
@@ -281,7 +389,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 = String(appSecret || '').trim()
|
let raw = String(appSecret || '').trim()
|
||||||
|
|
||||||
@@ -294,7 +402,7 @@ function generateSign(params, appSecret) {
|
|||||||
return crypto.createHash('md5').update(raw, 'utf8').digest('hex').toLowerCase()
|
return crypto.createHash('md5').update(raw, 'utf8').digest('hex').toLowerCase()
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isAgisoAutoDeliverySuccess(statusCode, payload) {
|
export function isAgisoAutoDeliverySuccess(statusCode: number, payload: unknown): boolean {
|
||||||
if (statusCode < 200 || statusCode >= 300) {
|
if (statusCode < 200 || statusCode >= 300) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -303,24 +411,35 @@ export function isAgisoAutoDeliverySuccess(statusCode, payload) {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (payload.IsSuccess === true) {
|
const normalizedPayload = payload as JsonObject
|
||||||
|
if (normalizedPayload.IsSuccess === true) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Number(payload.Error_Code) === 0) {
|
if (Number(normalizedPayload.Error_Code) === 0) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Number(payload.code) === 0) {
|
if (Number(normalizedPayload.code) === 0) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resolveAgisoAutoDeliveryErrorMessage(payload, rawText, statusCode) {
|
export function resolveAgisoAutoDeliveryErrorMessage(
|
||||||
|
payload: unknown,
|
||||||
|
rawText: string,
|
||||||
|
statusCode: number,
|
||||||
|
): string {
|
||||||
if (payload && typeof payload === 'object') {
|
if (payload && typeof payload === 'object') {
|
||||||
for (const value of [payload.Error_Msg, payload.msg, payload.message, payload.error]) {
|
const normalizedPayload = payload as JsonObject
|
||||||
|
for (const value of [
|
||||||
|
normalizedPayload.Error_Msg,
|
||||||
|
normalizedPayload.msg,
|
||||||
|
normalizedPayload.message,
|
||||||
|
normalizedPayload.error,
|
||||||
|
]) {
|
||||||
const normalized = String(value || '').trim()
|
const normalized = String(value || '').trim()
|
||||||
if (normalized) {
|
if (normalized) {
|
||||||
return normalized
|
return normalized
|
||||||
@@ -332,11 +451,11 @@ export function resolveAgisoAutoDeliveryErrorMessage(payload, rawText, statusCod
|
|||||||
return text || `Agiso 咸鱼自动发货失败,HTTP ${statusCode}`
|
return text || `Agiso 咸鱼自动发货失败,HTTP ${statusCode}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resolveAgisoAutoDeliveryEndpoint(value) {
|
export function resolveAgisoAutoDeliveryEndpoint(value: unknown): string {
|
||||||
return AGISO_DUMMY_SEND_ENDPOINT
|
return AGISO_DUMMY_SEND_ENDPOINT
|
||||||
}
|
}
|
||||||
|
|
||||||
async function persistAgisoAutoDeliveryResult(task, {
|
async function persistAgisoAutoDeliveryResult(task: AutoDeliveryTaskWithId, {
|
||||||
status,
|
status,
|
||||||
trigger,
|
trigger,
|
||||||
reason,
|
reason,
|
||||||
@@ -345,7 +464,7 @@ async function persistAgisoAutoDeliveryResult(task, {
|
|||||||
response = {},
|
response = {},
|
||||||
errorMessage = '',
|
errorMessage = '',
|
||||||
detail = {},
|
detail = {},
|
||||||
} = {}, deps = {}) {
|
}: PersistAgisoAutoDeliveryResultInput, deps: EnsureAgisoXianyuAutoDeliveryDeps = {}) {
|
||||||
const getNowIso = deps.nowIso || nowIso
|
const getNowIso = deps.nowIso || nowIso
|
||||||
const patchTask = deps.updateTask || updateTask
|
const patchTask = deps.updateTask || updateTask
|
||||||
const insertTaskEvent = deps.createTaskEvent || createTaskEvent
|
const insertTaskEvent = deps.createTaskEvent || createTaskEvent
|
||||||
@@ -392,7 +511,7 @@ async function persistAgisoAutoDeliveryResult(task, {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseTaskContext(task) {
|
function parseTaskContext(task: AutoDeliveryTask | null | undefined): JsonObject {
|
||||||
const value = task?.context_json
|
const value = task?.context_json
|
||||||
|
|
||||||
if (!value) {
|
if (!value) {
|
||||||
@@ -410,14 +529,14 @@ function parseTaskContext(task) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isAgisoXianyuOrder(order) {
|
function isAgisoXianyuOrder(order: AutoDeliveryOrder | null | undefined): order is SupportedAgisoOrder {
|
||||||
return String(order?.provider || '').trim() === 'agiso'
|
return String(order?.provider || '').trim() === 'agiso'
|
||||||
&& String(order?.platform || '').trim() === 'xianyu'
|
&& String(order?.platform || '').trim() === 'xianyu'
|
||||||
&& Boolean(String(order?.platform_order_id || '').trim())
|
&& Boolean(String(order?.platform_order_id || '').trim())
|
||||||
&& Number(order?.id || 0) > 0
|
&& Number(order?.id || 0) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeBooleanLike(value, fallbackValue) {
|
function normalizeBooleanLike(value: unknown, fallbackValue: boolean): boolean {
|
||||||
if (typeof value === 'boolean') {
|
if (typeof value === 'boolean') {
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
@@ -438,15 +557,19 @@ function normalizeBooleanLike(value, fallbackValue) {
|
|||||||
return fallbackValue
|
return fallbackValue
|
||||||
}
|
}
|
||||||
|
|
||||||
function isPlainObject(value) {
|
function isPlainObject(value: unknown): value is JsonObject {
|
||||||
return Object.prototype.toString.call(value) === '[object Object]'
|
return Object.prototype.toString.call(value) === '[object Object]'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发货接口返回成功后,再查一次 Order/Detail 确认订单是否真的进入已发货状态 meow~
|
* 发货接口返回成功后,再查一次 Order/Detail 确认订单是否真的进入已发货状态。
|
||||||
* 只有 ship_time > 0 或 orderStatus >= 3 才算发货确认通过
|
* 只有 ship_time > 0 或 orderStatus >= 3 才算发货确认通过
|
||||||
*/
|
*/
|
||||||
async function confirmAgisoXianyuAutoDeliveryShipped({ shopId = '', platformOrderId = '', requestId = '' } = {}) {
|
async function confirmAgisoXianyuAutoDeliveryShipped({
|
||||||
|
shopId = '',
|
||||||
|
platformOrderId = '',
|
||||||
|
requestId = '',
|
||||||
|
}: AgisoAutoDeliveryConfirmInput = {}): Promise<AgisoAutoDeliveryConfirmResult> {
|
||||||
try {
|
try {
|
||||||
const detailResult = await queryAgisoXianyuOrderDetail({ shopId, platformOrderId, requestId })
|
const detailResult = await queryAgisoXianyuOrderDetail({ shopId, platformOrderId, requestId })
|
||||||
|
|
||||||
@@ -484,9 +607,12 @@ async function confirmAgisoXianyuAutoDeliveryShipped({ shopId = '', platformOrde
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 自动发货成功后发送消息通知 meow~
|
* 自动发货成功后发送消息通知。
|
||||||
*/
|
*/
|
||||||
async function sendAutoDeliveryMessage({ order, task } = {}, deps = {}) {
|
async function sendAutoDeliveryMessage(
|
||||||
|
{ order, task }: { order?: AutoDeliveryOrder | null; task?: AutoDeliveryTask | null } = {},
|
||||||
|
deps: EnsureAgisoXianyuAutoDeliveryDeps = {},
|
||||||
|
): Promise<AutoDeliveryMessageResult> {
|
||||||
try {
|
try {
|
||||||
const deliverMessage = deps.ensureAgisoXianyuAutoDeliveryMessageDeliveredForTask
|
const deliverMessage = deps.ensureAgisoXianyuAutoDeliveryMessageDeliveredForTask
|
||||||
|| ensureAgisoXianyuAutoDeliveryMessageDeliveredForTask
|
|| ensureAgisoXianyuAutoDeliveryMessageDeliveredForTask
|
||||||
@@ -20,6 +20,6 @@
|
|||||||
"data",
|
"data",
|
||||||
"src/**/*.test.js",
|
"src/**/*.test.js",
|
||||||
"src/services/session/**",
|
"src/services/session/**",
|
||||||
"src/services/platforms/agiso/**"
|
"src/services/platforms/agiso/**/*.js"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -320,6 +320,15 @@
|
|||||||
- `npm run typecheck`
|
- `npm run typecheck`
|
||||||
- `npm run build`
|
- `npm run build`
|
||||||
- `npm test` 共 131 个用例通过
|
- `npm test` 共 131 个用例通过
|
||||||
|
47. Agiso 自动发货服务迁移到 `.ts`:
|
||||||
|
- `src/services/platforms/agiso/xianyu/auto-delivery-service.ts`
|
||||||
|
48. 自动发货订单 / 任务形态、依赖注入、HTTP 响应、发货确认结果、上下文落库 payload 已显式类型化
|
||||||
|
49. `tsconfig.json` 已将 Agiso 新增 `.ts` 文件纳入 `typecheck`,旧 Agiso `.js` 文件继续暂时排除
|
||||||
|
50. Docker 内验证通过:
|
||||||
|
- `src/services/platforms/agiso/xianyu/auto-delivery-service.test.js` 共 9 个用例通过
|
||||||
|
- `npm run typecheck`
|
||||||
|
- `npm run build`
|
||||||
|
- `npm test` 共 131 个用例通过
|
||||||
|
|
||||||
## 下一步建议
|
## 下一步建议
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user