From 9aafddd624103361b98e89c9bc0fbf8a99a89e68 Mon Sep 17 00:00:00 2001 From: yml Date: Mon, 25 May 2026 21:12:03 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=AD=E5=BC=80=E5=BF=AB=E6=89=8B=E5=90=8E?= =?UTF-8?q?=E7=AB=AF=E6=97=A7=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/backend/src/app-kuaishou.ts | 2 - apps/backend/src/routes/admin-kuaishou.ts | 2 +- .../routes/admin/platform-config-kuaishou.ts | 19 ++++++ .../admin/platform-config/cloudtentacles.ts | 2 +- .../kuaishou-cloud-fulfillment.ts | 2 +- .../admin/platform-config/kuaishou-eticket.ts | 2 +- .../routes/admin/platform-config/ninetyone.ts | 2 +- .../admin/platform-config/notifications.ts | 2 +- .../routes/admin/webhook-events-kuaishou.ts | 19 +++--- .../admin/admin-read-shared-helpers.ts | 8 --- .../admin/platform-config/agiso-template.ts | 6 ++ .../platform-config/cloudtentacles-service.ts | 2 +- .../platform-config/cloudtentacles-writes.ts | 19 ++++++ .../services/admin/platform-config/domain.ts | 2 +- .../services/admin/platform-config/writes.ts | 16 ----- .../src/services/order/order-service.ts | 59 ++++++++----------- .../src/services/order/webhook-service.ts | 26 +++++++- .../platforms/agiso/xianyu/message-service.ts | 8 +-- apps/backend/tsconfig.kuaishou.json | 1 + 19 files changed, 113 insertions(+), 86 deletions(-) create mode 100644 apps/backend/src/routes/admin/platform-config-kuaishou.ts create mode 100644 apps/backend/src/services/admin/platform-config/agiso-template.ts create mode 100644 apps/backend/src/services/admin/platform-config/cloudtentacles-writes.ts diff --git a/apps/backend/src/app-kuaishou.ts b/apps/backend/src/app-kuaishou.ts index 0848ceec..954ffe71 100644 --- a/apps/backend/src/app-kuaishou.ts +++ b/apps/backend/src/app-kuaishou.ts @@ -5,7 +5,6 @@ import process from "node:process"; import adminRouter from "./routes/admin-kuaishou.js"; import claimsRouter from "./routes/claims-kuaishou.js"; import open91Router from "./routes/open-91.js"; -import webhooksRouter from "./routes/webhooks.js"; import { accessLogMiddleware } from "./middleware/access-log.js"; import { createCorsMiddleware } from "./middleware/cors.js"; import { buildHealthPayload, type StartupState } from "./startup/state.js"; @@ -91,7 +90,6 @@ export function createKuaishouApp({ }); app.use("/api/v1/open/91", open91Router); - app.use("/api/v1/webhooks", webhooksRouter); app.use("/api/v1/claim", claimsRouter); app.use("/api/v1/admin", adminRouter); diff --git a/apps/backend/src/routes/admin-kuaishou.ts b/apps/backend/src/routes/admin-kuaishou.ts index 6f5a54b4..d329014c 100644 --- a/apps/backend/src/routes/admin-kuaishou.ts +++ b/apps/backend/src/routes/admin-kuaishou.ts @@ -6,7 +6,7 @@ import dashboardRouter from "./admin/dashboard.js"; import inventoryRouter from "./admin/inventory-kuaishou.js"; import messageDeliveriesRouter from "./admin/message-deliveries.js"; import ordersRouter from "./admin/orders.js"; -import platformConfigRouter from "./admin/platform-config.js"; +import platformConfigRouter from "./admin/platform-config-kuaishou.js"; import { requireAdminSession } from "./admin/shared.js"; import tasksRouter from "./admin/tasks-kuaishou.js"; import usersRouter from "./admin/users.js"; diff --git a/apps/backend/src/routes/admin/platform-config-kuaishou.ts b/apps/backend/src/routes/admin/platform-config-kuaishou.ts new file mode 100644 index 00000000..f5cb0345 --- /dev/null +++ b/apps/backend/src/routes/admin/platform-config-kuaishou.ts @@ -0,0 +1,19 @@ +import { Router } from "express"; + +import { requireAdminRoles } from "./shared.js"; +import cloudtentaclesRouter from "./platform-config/cloudtentacles.js"; +import kuaishouCloudFulfillmentRouter from "./platform-config/kuaishou-cloud-fulfillment.js"; +import kuaishouEticketRouter from "./platform-config/kuaishou-eticket.js"; +import ninetyoneRouter from "./platform-config/ninetyone.js"; +import notificationsRouter from "./platform-config/notifications.js"; + +const router = Router(); + +router.use("/platform-config", requireAdminRoles(["admin"])); +router.use("/platform-config", notificationsRouter); +router.use("/platform-config", kuaishouEticketRouter); +router.use("/platform-config", ninetyoneRouter); +router.use("/platform-config", cloudtentaclesRouter); +router.use("/platform-config", kuaishouCloudFulfillmentRouter); + +export default router; diff --git a/apps/backend/src/routes/admin/platform-config/cloudtentacles.ts b/apps/backend/src/routes/admin/platform-config/cloudtentacles.ts index 26852a12..854b1878 100644 --- a/apps/backend/src/routes/admin/platform-config/cloudtentacles.ts +++ b/apps/backend/src/routes/admin/platform-config/cloudtentacles.ts @@ -21,7 +21,7 @@ import { useAdminCloudtentaclesSku, validateAdminCloudtentaclesSession, verifyAdminCloudtentaclesLoginCode, -} from "../../../services/admin/platform-config/service.js"; +} from "../../../services/admin/platform-config/cloudtentacles-service.js"; import type { AdminCloudtentaclesCatalogQueryRouteBody, AdminCloudtentaclesFullFlowRouteBody, diff --git a/apps/backend/src/routes/admin/platform-config/kuaishou-cloud-fulfillment.ts b/apps/backend/src/routes/admin/platform-config/kuaishou-cloud-fulfillment.ts index c76b106f..170dce6c 100644 --- a/apps/backend/src/routes/admin/platform-config/kuaishou-cloud-fulfillment.ts +++ b/apps/backend/src/routes/admin/platform-config/kuaishou-cloud-fulfillment.ts @@ -3,7 +3,7 @@ import { Router } from "express"; import { getAdminKuaishouCloudFulfillmentConfig, updateAdminKuaishouCloudFulfillmentConfig, -} from "../../../services/admin/platform-config/service.js"; +} from "../../../services/admin/platform-config/kuaishou-cloud-fulfillment-service.js"; import type { AdminKuaishouCloudFulfillmentConfigRouteBody } from "../../../types/admin-route-inputs.js"; import { createJsonHandler } from "../shared.js"; import type { JsonRecord } from "./shared.js"; diff --git a/apps/backend/src/routes/admin/platform-config/kuaishou-eticket.ts b/apps/backend/src/routes/admin/platform-config/kuaishou-eticket.ts index 0979b248..6fce9fa8 100644 --- a/apps/backend/src/routes/admin/platform-config/kuaishou-eticket.ts +++ b/apps/backend/src/routes/admin/platform-config/kuaishou-eticket.ts @@ -6,7 +6,7 @@ import { queryAdminKuaishouEticketDetail, queryAdminKuaishouEticketShopInfo, updateAdminKuaishouEticketSourceConfig, -} from "../../../services/admin/platform-config/service.js"; +} from "../../../services/admin/platform-config/kuaishou-eticket-service.js"; import type { AdminKuaishouEticketConsumeRouteBody, AdminKuaishouEticketDetailQueryRouteBody, diff --git a/apps/backend/src/routes/admin/platform-config/ninetyone.ts b/apps/backend/src/routes/admin/platform-config/ninetyone.ts index 51ffb0bb..1da89d08 100644 --- a/apps/backend/src/routes/admin/platform-config/ninetyone.ts +++ b/apps/backend/src/routes/admin/platform-config/ninetyone.ts @@ -4,7 +4,7 @@ import { failAdminNinetyoneOrder, getAdminNinetyoneOrders, retryAdminNinetyoneOrder, -} from "../../../services/admin/platform-config/service.js"; +} from "../../../services/admin/platform-config/ninetyone-service.js"; import type { AdminEntityRouteParams } from "../../../types/admin-route-inputs.js"; import { createJsonHandler } from "../shared.js"; import type { JsonRecord } from "./shared.js"; diff --git a/apps/backend/src/routes/admin/platform-config/notifications.ts b/apps/backend/src/routes/admin/platform-config/notifications.ts index 3a94680a..4fcf800a 100644 --- a/apps/backend/src/routes/admin/platform-config/notifications.ts +++ b/apps/backend/src/routes/admin/platform-config/notifications.ts @@ -7,7 +7,7 @@ import { testAdminNotification, updateAdminNotificationConfig, updateAdminScheduledJobsConfig, -} from "../../../services/admin/platform-config/service.js"; +} from "../../../services/admin/platform-config/notification-service.js"; import type { AdminEntityRouteParams, AdminNotificationConfigRouteBody, diff --git a/apps/backend/src/routes/admin/webhook-events-kuaishou.ts b/apps/backend/src/routes/admin/webhook-events-kuaishou.ts index 1b71dea5..b60970a9 100644 --- a/apps/backend/src/routes/admin/webhook-events-kuaishou.ts +++ b/apps/backend/src/routes/admin/webhook-events-kuaishou.ts @@ -1,16 +1,15 @@ import { Router } from "express"; -import { replayAdminWebhookEvent } from "../../services/admin/write/webhook-events.js"; import { getAdminWebhookEventDetail, getAdminWebhookEvents, } from "../../services/admin/admin-read-service.js"; +import { createHttpError } from "../../utils/http.js"; import { createJsonHandler, requireAdminRoles } from "./shared.js"; import type { AdminWebhookEventRouteParams, AdminWebhookEventRouteQuery, } from "../../types/admin-route-inputs.js"; -import type { AdminWebhookReplayResponse } from "../../types/admin-write-models.js"; const router = Router(); @@ -47,17 +46,17 @@ router.post( "/webhook-events/:eventId/replay", requireAdminRoles(["admin"]), createJsonHandler( - (req) => - replayAdminWebhookEvent( - (req.params as AdminWebhookEventRouteParams).eventId - ), + () => { + throw createHttpError("快手轻量后端不支持重放旧 Webhook", { + statusCode: 409, + errorCode: "admin_webhook_replay_disabled", + }); + }, { successMessage: "Webhook 已重放", errorMessage: "重放 webhook 失败", scope: "[admin-kuaishou/webhook-events/:eventId/replay]", audit: (req, data) => { - const result = data as AdminWebhookReplayResponse; - return { action: "webhook_replayed", targetType: "webhook_event", @@ -65,8 +64,8 @@ router.post( (req.params as AdminWebhookEventRouteParams).eventId ), data: { - eventId: result.eventId, - replayed: result.replayed, + disabled: true, + result: data, }, }; }, diff --git a/apps/backend/src/services/admin/admin-read-shared-helpers.ts b/apps/backend/src/services/admin/admin-read-shared-helpers.ts index 2b442d67..6dc5db72 100644 --- a/apps/backend/src/services/admin/admin-read-shared-helpers.ts +++ b/apps/backend/src/services/admin/admin-read-shared-helpers.ts @@ -1,4 +1,3 @@ -import { getAgisoShopConfig } from '../platforms/agiso/shop-config-service.js' import { safeParseJson } from './admin-query-utils.js' import { normalizeAdminRole } from './admin-auth-service.js' import { @@ -224,13 +223,6 @@ export function resolveDisplayShopName(provider: unknown, shopId: unknown, shopN return '' } - if (String(provider || '').trim().toLowerCase() === 'agiso') { - const configuredName = String(getAgisoShopConfig(normalizedShopId)?.shopName || '').trim() - if (configuredName) { - return configuredName - } - } - return normalizedShopId } diff --git a/apps/backend/src/services/admin/platform-config/agiso-template.ts b/apps/backend/src/services/admin/platform-config/agiso-template.ts new file mode 100644 index 00000000..0aec7536 --- /dev/null +++ b/apps/backend/src/services/admin/platform-config/agiso-template.ts @@ -0,0 +1,6 @@ +export function normalizeAgisoMessageTemplate(value: unknown): string { + return String(value || "") + .replaceAll("\\r\\n", "\n") + .replaceAll("\\n", "\n") + .replaceAll("\r\n", "\n"); +} diff --git a/apps/backend/src/services/admin/platform-config/cloudtentacles-service.ts b/apps/backend/src/services/admin/platform-config/cloudtentacles-service.ts index 94e4a9e8..6465c039 100644 --- a/apps/backend/src/services/admin/platform-config/cloudtentacles-service.ts +++ b/apps/backend/src/services/admin/platform-config/cloudtentacles-service.ts @@ -49,7 +49,7 @@ import { resolveAdminCloudtentaclesCredentialPayload, resolveAdminCloudtentaclesSessionPayload, } from "./cloudtentacles.js"; -import { normalizeAdminCloudtentaclesSourceConfigPayload } from "./writes.js"; +import { normalizeAdminCloudtentaclesSourceConfigPayload } from "./cloudtentacles-writes.js"; import { mapAdminCloudtentaclesSession, mapAdminCloudtentaclesSourceConfig, diff --git a/apps/backend/src/services/admin/platform-config/cloudtentacles-writes.ts b/apps/backend/src/services/admin/platform-config/cloudtentacles-writes.ts new file mode 100644 index 00000000..070fd78e --- /dev/null +++ b/apps/backend/src/services/admin/platform-config/cloudtentacles-writes.ts @@ -0,0 +1,19 @@ +type JsonObject = Record; + +export function normalizeAdminCloudtentaclesSourceConfigPayload( + payload: JsonObject = {} +) { + const sourceKey = + String(payload.sourceKey || payload.key || "").trim() || "default"; + return { + key: sourceKey, + label: String(payload.label || "").trim(), + enabled: payload.enabled !== false, + baseUrl: String(payload.baseUrl || "").trim() || "https://123.207.217.176", + username: String(payload.username || "").trim(), + password: String(payload.password || "").trim(), + phone: String(payload.phone || "").trim(), + deviceId: String(payload.deviceId || "-").trim() || "-", + deviceType: Number(payload.deviceType || 0), + }; +} diff --git a/apps/backend/src/services/admin/platform-config/domain.ts b/apps/backend/src/services/admin/platform-config/domain.ts index 303ca50d..a84c41d0 100644 --- a/apps/backend/src/services/admin/platform-config/domain.ts +++ b/apps/backend/src/services/admin/platform-config/domain.ts @@ -1,4 +1,4 @@ -import { normalizeAgisoMessageTemplate } from '../../platforms/agiso/xianyu/message-service.js' +import { normalizeAgisoMessageTemplate } from './agiso-template.js' import { maskSecret } from './mappers.js' type JsonObject = Record diff --git a/apps/backend/src/services/admin/platform-config/writes.ts b/apps/backend/src/services/admin/platform-config/writes.ts index 8886455d..68c7a9bb 100644 --- a/apps/backend/src/services/admin/platform-config/writes.ts +++ b/apps/backend/src/services/admin/platform-config/writes.ts @@ -98,19 +98,3 @@ export function buildAdminAgisoMessagingSavePayload( shops: nextMap, }; } - -export function normalizeAdminCloudtentaclesSourceConfigPayload(payload: JsonObject = {}) { - const sourceKey = - String(payload.sourceKey || payload.key || "").trim() || "default"; - return { - key: sourceKey, - label: String(payload.label || "").trim(), - enabled: payload.enabled !== false, - baseUrl: String(payload.baseUrl || "").trim() || "https://123.207.217.176", - username: String(payload.username || "").trim(), - password: String(payload.password || "").trim(), - phone: String(payload.phone || "").trim(), - deviceId: String(payload.deviceId || "-").trim() || "-", - deviceType: Number(payload.deviceType || 0), - }; -} diff --git a/apps/backend/src/services/order/order-service.ts b/apps/backend/src/services/order/order-service.ts index b1290a4d..8fc3624c 100644 --- a/apps/backend/src/services/order/order-service.ts +++ b/apps/backend/src/services/order/order-service.ts @@ -5,10 +5,7 @@ import { updateOrder, } from '../../repositories/order-repo.js' import { replaceOrderItems } from '../../repositories/order-item-repo.js' -import { getClaimTokenById } from '../../repositories/claim-token-repo.js' -import { buildClaimUrl } from '../claim/claim-service.js' import { syncDeliveryTasksForOrder } from './delivery-task-service.js' -import { ensureAgisoXianyuClaimMessageDeliveredForTask } from '../platforms/agiso/xianyu/message-service.js' import { resolveOrderItemForFulfillment } from './product-match-service.js' import { nowIso } from '../../utils/time.js' import { logWebhook } from '../../utils/logger.js' @@ -51,6 +48,11 @@ type SourceOrderEvent = { type UpsertOrderSourceOptions = { sourceLabel?: string + handleClaimLinkTask?: (input: { + event: SourceOrderEvent + order: OrderRow + task: TaskRow + }) => Promise | null | undefined> } type MessageDeliveryResult = { @@ -99,7 +101,7 @@ export async function upsertOrderFromWebhook(event: SourceOrderEvent): Promise { const now = nowIso() const exactExisting = await findOrderByPlatformOrderId({ @@ -219,37 +221,28 @@ export async function upsertOrderFromSource( resolvedSkuCodes: configuredItems.map((item) => item.skuCode), }) - for (const task of tasks) { - const isClaimLinkTask = String(task.task_status || '') === 'link_generated' - || ( - String(task.executor_key || '').trim() === 'kuaishou_ct_assisted' - && ['pending_binding_prepare', 'waiting_binding', 'role_confirmed', 'dispatched_pending_return'].includes(String(task.task_status || '').trim()) - ) + if (handleClaimLinkTask) { + for (const task of tasks) { + const isClaimLinkTask = String(task.task_status || '') === 'link_generated' + || ( + String(task.executor_key || '').trim() === 'kuaishou_ct_assisted' + && ['pending_binding_prepare', 'waiting_binding', 'role_confirmed', 'dispatched_pending_return'].includes(String(task.task_status || '').trim()) + ) - if ( - event.provider !== 'agiso' - || event.platform !== 'xianyu' - || !isClaimLinkTask - || !task.primary_claim_token_id - ) { - continue + if (!isClaimLinkTask || !task.primary_claim_token_id) { + continue + } + + const result = await handleClaimLinkTask({ event, order, task }) + if (!result) { + continue + } + + messageDeliveries.push({ + taskId: task.id, + ...result, + }) } - - const claimToken = await getClaimTokenById(task.primary_claim_token_id) - if (!claimToken) { - continue - } - - const result = await ensureAgisoXianyuClaimMessageDeliveredForTask({ - order, - task, - claimUrl: buildClaimUrl(claimToken.token), - expiredAt: claimToken.expired_at, - }) - messageDeliveries.push({ - taskId: task.id, - ...result, - }) } return { diff --git a/apps/backend/src/services/order/webhook-service.ts b/apps/backend/src/services/order/webhook-service.ts index 7b8a1628..4f050c34 100644 --- a/apps/backend/src/services/order/webhook-service.ts +++ b/apps/backend/src/services/order/webhook-service.ts @@ -1,14 +1,17 @@ import crypto from 'node:crypto' import { runtimeConfig } from '../../config/runtime.js' +import { getClaimTokenById } from '../../repositories/claim-token-repo.js' import { createWebhookEvent, updateWebhookEvent } from '../../repositories/webhook-event-repo.js' +import { buildClaimUrl } from '../claim/claim-service.js' +import { ensureAgisoXianyuClaimMessageDeliveredForTask } from '../platforms/agiso/xianyu/message-service.js' import { enrichAgisoXianyuTradeOrder } from '../platforms/agiso/xianyu/order-detail-service.js' import { extractAgisoTradeOrderItemSources, extractAgisoTradePayload, resolveAgisoTradePlatformOrderId, } from './agiso-trade-parsing.js' -import { upsertOrderFromWebhook } from './order-service.js' +import { upsertOrderFromSource, upsertOrderFromWebhook } from './order-service.js' import { hasConfiguredOrderItems } from './product-match-service.js' import { createHttpError } from '../../utils/http.js' import { parseJsonObject } from '../../utils/json.js' @@ -98,7 +101,7 @@ export async function executeAgisoTradeWebhookWithDeps( ) { const updateEvent = deps.updateWebhookEvent || updateWebhookEvent const hasConfiguredItems = deps.hasConfiguredOrderItems || hasConfiguredOrderItems - const upsertOrder = deps.upsertOrderFromWebhook || upsertOrderFromWebhook + const upsertOrder = deps.upsertOrderFromWebhook || upsertAgisoOrderFromWebhook try { if (!parsed.signatureValid) { @@ -287,6 +290,25 @@ export async function executeAgisoTradeWebhookWithDeps( } } +async function upsertAgisoOrderFromWebhook(event) { + return upsertOrderFromSource(event, { + sourceLabel: 'webhook', + handleClaimLinkTask: async ({ order, task }) => { + const claimToken = await getClaimTokenById(task.primary_claim_token_id) + if (!claimToken) { + return null + } + + return ensureAgisoXianyuClaimMessageDeliveredForTask({ + order, + task, + claimUrl: buildClaimUrl(claimToken.token), + expiredAt: claimToken.expired_at, + }) + }, + }) +} + async function enrichTradeBeforeUpsert( parsed, { requestId = '', webhookEventId = null } = {}, diff --git a/apps/backend/src/services/platforms/agiso/xianyu/message-service.ts b/apps/backend/src/services/platforms/agiso/xianyu/message-service.ts index 5a9ccd14..1a7aebe0 100644 --- a/apps/backend/src/services/platforms/agiso/xianyu/message-service.ts +++ b/apps/backend/src/services/platforms/agiso/xianyu/message-service.ts @@ -1,6 +1,7 @@ import crypto from 'node:crypto' import { runtimeConfig } from '../../../../config/runtime.js' +import { normalizeAgisoMessageTemplate } from '../../../admin/platform-config/agiso-template.js' import { getAgisoMessagingDefaults, getAgisoShopConfigMap } from '../shop-config-service.js' import { createMessageDelivery, @@ -193,13 +194,6 @@ export async function ensureAgisoXianyuAutoDeliveryMessageDeliveredForTask({ }) } -export function normalizeAgisoMessageTemplate(value: unknown): string { - return String(value || '') - .replaceAll('\\r\\n', '\n') - .replaceAll('\\n', '\n') - .replaceAll('\r\n', '\n') -} - async function deliverAgisoXianyuMessageForTask({ order, task, diff --git a/apps/backend/tsconfig.kuaishou.json b/apps/backend/tsconfig.kuaishou.json index 3dcacbb1..95aff275 100644 --- a/apps/backend/tsconfig.kuaishou.json +++ b/apps/backend/tsconfig.kuaishou.json @@ -3,6 +3,7 @@ "files": [ "src/index-kuaishou.ts" ], + "include": [], "exclude": [ "node_modules", "data",