From f6acc0b9de9eb4a48ab5cda17075f26eb070b2c3 Mon Sep 17 00:00:00 2001 From: yml Date: Wed, 20 May 2026 00:35:19 +0800 Subject: [PATCH] =?UTF-8?q?feat(cloudtentacles):=20=E5=90=8E=E7=AB=AF=20Ad?= =?UTF-8?q?min=20=E5=B1=82=E5=A4=9A=E8=B4=A6=E5=8F=B7=E6=94=AF=E6=8C=81=20?= =?UTF-8?q?-=20admin-service/context/routes/types=20=E6=94=B9=E9=80=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/routes/admin/platform-config.js | 1438 ++++++++++------- .../platform-config/cloudtentacles-service.js | 314 ++-- .../admin/platform-config/cloudtentacles.js | 109 +- .../services/admin/platform-config/context.js | 113 +- .../services/admin/platform-config/mappers.js | 99 +- .../services/admin/platform-config/service.js | 17 +- .../services/admin/platform-config/writes.js | 113 +- apps/backend/src/types/admin-route-inputs.js | 6 +- apps/backend/src/types/admin-write-inputs.js | 16 +- 9 files changed, 1386 insertions(+), 839 deletions(-) diff --git a/apps/backend/src/routes/admin/platform-config.js b/apps/backend/src/routes/admin/platform-config.js index 4bf4bd09..6d95ee83 100644 --- a/apps/backend/src/routes/admin/platform-config.js +++ b/apps/backend/src/routes/admin/platform-config.js @@ -1,6 +1,6 @@ // @ts-check -import { Router } from 'express' +import { Router } from "express"; import { appointAdminCloudtentaclesVirtualNumber, @@ -16,7 +16,8 @@ import { getAdminKuaishouCloudFulfillmentConfig, getAdminKuaishouEticketSourceConfig, getAdminCloudtentaclesKnapsack, - getAdminCloudtentaclesSourceConfig, + listAdminCloudtentaclesSources, + deleteAdminCloudtentaclesSource, getAdminCloudtentaclesSkuList, useAdminCloudtentaclesSku, getAdminAgisoShopConfigs, @@ -43,8 +44,8 @@ import { updateAdminFulfillmentBindingConfigs, verifyAdminCloudtentaclesLoginCode, validateAdminCloudtentaclesSession, -} from '../../services/admin/platform-config/service.js' -import { createJsonHandler, requireAdminRoles } from './shared.js' +} from "../../services/admin/platform-config/service.js"; +import { createJsonHandler, requireAdminRoles } from "./shared.js"; /** @typedef {import('../../types/admin-route-inputs.js').AdminAgisoShopConfigRouteBody} AdminAgisoShopConfigRouteBody */ /** @typedef {import('../../types/admin-route-inputs.js').AdminFulfillmentBindingConfigRouteBody} AdminFulfillmentBindingConfigRouteBody */ @@ -69,608 +70,911 @@ import { createJsonHandler, requireAdminRoles } from './shared.js' /** @typedef {import('../../types/admin-route-inputs.js').AdminCloudtentaclesFullFlowRouteBody} AdminCloudtentaclesFullFlowRouteBody */ /** @typedef {import('../../types/admin-write-models.js').AdminAgisoShopConfigSaveResponse} AdminAgisoShopConfigSaveResponse */ -const router = Router() +const router = Router(); -router.use('/platform-config', requireAdminRoles(['admin'])) +router.use("/platform-config", requireAdminRoles(["admin"])); -router.get('/platform-config/agiso-shops', createJsonHandler( - () => getAdminAgisoShopConfigs(), - { - successMessage: 'ok', - errorMessage: '读取 Agiso 店铺配置失败', - scope: '[admin/platform-config/agiso-shops]', - }, -)) +router.get( + "/platform-config/agiso-shops", + createJsonHandler(() => getAdminAgisoShopConfigs(), { + successMessage: "ok", + errorMessage: "读取 Agiso 店铺配置失败", + scope: "[admin/platform-config/agiso-shops]", + }) +); -router.post('/platform-config/agiso-shops', createJsonHandler( - (req) => updateAdminAgisoShopConfigs(/** @type {AdminAgisoShopConfigRouteBody} */ (req.body)), - { - successMessage: 'Agiso 店铺配置已保存', - errorMessage: '保存 Agiso 店铺配置失败', - scope: '[admin/platform-config/agiso-shops]', - audit: (_req, data) => { - const result = /** @type {AdminAgisoShopConfigSaveResponse} */ (data) - return { - action: 'platform_shop_config_updated', - targetType: 'platform_config', - targetId: 'agiso_shops', - data: { - shopCount: result.shops.length, - filePath: result.filePath, - }, - } - }, - }, -)) +router.post( + "/platform-config/agiso-shops", + createJsonHandler( + (req) => + updateAdminAgisoShopConfigs( + /** @type {AdminAgisoShopConfigRouteBody} */ (req.body) + ), + { + successMessage: "Agiso 店铺配置已保存", + errorMessage: "保存 Agiso 店铺配置失败", + scope: "[admin/platform-config/agiso-shops]", + audit: (_req, data) => { + const result = /** @type {AdminAgisoShopConfigSaveResponse} */ (data); + return { + action: "platform_shop_config_updated", + targetType: "platform_config", + targetId: "agiso_shops", + data: { + shopCount: result.shops.length, + filePath: result.filePath, + }, + }; + }, + } + ) +); -router.get('/platform-config/notifications', createJsonHandler( - () => getAdminNotificationConfig(), - { - successMessage: 'ok', - errorMessage: '读取内部通知配置失败', - scope: '[admin/platform-config/notifications]', - }, -)) +router.get( + "/platform-config/notifications", + createJsonHandler(() => getAdminNotificationConfig(), { + successMessage: "ok", + errorMessage: "读取内部通知配置失败", + scope: "[admin/platform-config/notifications]", + }) +); -router.post('/platform-config/notifications', createJsonHandler( - (req) => updateAdminNotificationConfig(/** @type {AdminNotificationConfigRouteBody} */ (req.body)), - { - successMessage: '内部通知配置已保存', - errorMessage: '保存内部通知配置失败', - scope: '[admin/platform-config/notifications]', - audit: (_req, data) => { - const result = /** @type {{ filePath?: string, source?: { enabled?: boolean, channels?: { bark?: { recipients?: unknown[] } } } }} */ (data) - return { - action: 'platform_notification_config_updated', - targetType: 'platform_config', - targetId: 'notifications', - data: { - filePath: String(result.filePath || '').trim(), - enabled: Boolean(result.source?.enabled), - barkRecipientCount: Array.isArray(result.source?.channels?.bark?.recipients) - ? result.source.channels.bark.recipients.length - : 0, - }, - } - }, - }, -)) +router.post( + "/platform-config/notifications", + createJsonHandler( + (req) => + updateAdminNotificationConfig( + /** @type {AdminNotificationConfigRouteBody} */ (req.body) + ), + { + successMessage: "内部通知配置已保存", + errorMessage: "保存内部通知配置失败", + scope: "[admin/platform-config/notifications]", + audit: (_req, data) => { + const result = + /** @type {{ filePath?: string, source?: { enabled?: boolean, channels?: { bark?: { recipients?: unknown[] } } } }} */ ( + data + ); + return { + action: "platform_notification_config_updated", + targetType: "platform_config", + targetId: "notifications", + data: { + filePath: String(result.filePath || "").trim(), + enabled: Boolean(result.source?.enabled), + barkRecipientCount: Array.isArray( + result.source?.channels?.bark?.recipients + ) + ? result.source.channels.bark.recipients.length + : 0, + }, + }; + }, + } + ) +); -router.post('/platform-config/notifications/test', createJsonHandler( - (req) => testAdminNotification(/** @type {AdminNotificationTestRouteBody} */ (req.body)), - { - successMessage: '内部通知测试已执行', - errorMessage: '内部通知测试失败', - scope: '[admin/platform-config/notifications/test]', - audit: (_req, data) => { - const result = /** @type {{ successCount?: number, failedCount?: number, channel?: string }} */ (data) - return { - action: 'platform_notification_test_sent', - targetType: 'platform_config', - targetId: 'notifications', - data: { - channel: String(result.channel || '').trim(), - successCount: Number(result.successCount || 0), - failedCount: Number(result.failedCount || 0), - }, - } - }, - }, -)) +router.post( + "/platform-config/notifications/test", + createJsonHandler( + (req) => + testAdminNotification( + /** @type {AdminNotificationTestRouteBody} */ (req.body) + ), + { + successMessage: "内部通知测试已执行", + errorMessage: "内部通知测试失败", + scope: "[admin/platform-config/notifications/test]", + audit: (_req, data) => { + const result = + /** @type {{ successCount?: number, failedCount?: number, channel?: string }} */ ( + data + ); + return { + action: "platform_notification_test_sent", + targetType: "platform_config", + targetId: "notifications", + data: { + channel: String(result.channel || "").trim(), + successCount: Number(result.successCount || 0), + failedCount: Number(result.failedCount || 0), + }, + }; + }, + } + ) +); -router.get('/platform-config/scheduled-jobs', createJsonHandler( - () => getAdminScheduledJobsConfig(), - { - successMessage: 'ok', - errorMessage: '读取定时任务配置失败', - scope: '[admin/platform-config/scheduled-jobs]', - }, -)) +router.get( + "/platform-config/scheduled-jobs", + createJsonHandler(() => getAdminScheduledJobsConfig(), { + successMessage: "ok", + errorMessage: "读取定时任务配置失败", + scope: "[admin/platform-config/scheduled-jobs]", + }) +); -router.post('/platform-config/scheduled-jobs', createJsonHandler( - (req) => updateAdminScheduledJobsConfig(/** @type {AdminScheduledJobsConfigRouteBody} */ (req.body)), - { - successMessage: '定时任务配置已保存', - errorMessage: '保存定时任务配置失败', - scope: '[admin/platform-config/scheduled-jobs]', - audit: (_req, data) => { - const result = /** @type {{ filePath?: string, source?: { enabled?: boolean, jobs?: unknown[] } }} */ (data) - return { - action: 'platform_scheduled_jobs_updated', - targetType: 'platform_config', - targetId: 'scheduled_jobs', - data: { - filePath: String(result.filePath || '').trim(), - enabled: Boolean(result.source?.enabled), - jobCount: Array.isArray(result.source?.jobs) ? result.source.jobs.length : 0, - }, - } - }, - }, -)) +router.post( + "/platform-config/scheduled-jobs", + createJsonHandler( + (req) => + updateAdminScheduledJobsConfig( + /** @type {AdminScheduledJobsConfigRouteBody} */ (req.body) + ), + { + successMessage: "定时任务配置已保存", + errorMessage: "保存定时任务配置失败", + scope: "[admin/platform-config/scheduled-jobs]", + audit: (_req, data) => { + const result = + /** @type {{ filePath?: string, source?: { enabled?: boolean, jobs?: unknown[] } }} */ ( + data + ); + return { + action: "platform_scheduled_jobs_updated", + targetType: "platform_config", + targetId: "scheduled_jobs", + data: { + filePath: String(result.filePath || "").trim(), + enabled: Boolean(result.source?.enabled), + jobCount: Array.isArray(result.source?.jobs) + ? result.source.jobs.length + : 0, + }, + }; + }, + } + ) +); -router.post('/platform-config/scheduled-jobs/:id/run', createJsonHandler( - (req) => runAdminScheduledJobNow((/** @type {AdminEntityRouteParams} */ (req.params)).id), - { - successMessage: '定时任务已执行', - errorMessage: '执行定时任务失败', - scope: '[admin/platform-config/scheduled-jobs/:id/run]', - audit: (req, data) => { - const result = /** @type {{ result?: { status?: string, message?: string } }} */ (data) - return { - action: 'platform_scheduled_job_run', - targetType: 'platform_config', - targetId: String((/** @type {AdminEntityRouteParams} */ (req.params)).id || '').trim(), - data: { - status: String(result.result?.status || '').trim(), - message: String(result.result?.message || '').trim(), - }, - } - }, - }, -)) +router.post( + "/platform-config/scheduled-jobs/:id/run", + createJsonHandler( + (req) => + runAdminScheduledJobNow( + /** @type {AdminEntityRouteParams} */ (req.params).id + ), + { + successMessage: "定时任务已执行", + errorMessage: "执行定时任务失败", + scope: "[admin/platform-config/scheduled-jobs/:id/run]", + audit: (req, data) => { + const result = + /** @type {{ result?: { status?: string, message?: string } }} */ ( + data + ); + return { + action: "platform_scheduled_job_run", + targetType: "platform_config", + targetId: String( + /** @type {AdminEntityRouteParams} */ (req.params).id || "" + ).trim(), + data: { + status: String(result.result?.status || "").trim(), + message: String(result.result?.message || "").trim(), + }, + }; + }, + } + ) +); -router.get('/platform-config/kuaishou-eticket-source', createJsonHandler( - () => getAdminKuaishouEticketSourceConfig(), - { - successMessage: 'ok', - errorMessage: '读取快手小店核销配置失败', - scope: '[admin/platform-config/kuaishou-eticket-source]', - }, -)) +router.get( + "/platform-config/kuaishou-eticket-source", + createJsonHandler(() => getAdminKuaishouEticketSourceConfig(), { + successMessage: "ok", + errorMessage: "读取快手小店核销配置失败", + scope: "[admin/platform-config/kuaishou-eticket-source]", + }) +); -router.post('/platform-config/kuaishou-eticket-source', createJsonHandler( - (req) => updateAdminKuaishouEticketSourceConfig(/** @type {AdminKuaishouEticketSourceConfigRouteBody} */ (req.body)), - { - successMessage: '快手小店核销配置已保存', - errorMessage: '保存快手小店核销配置失败', - scope: '[admin/platform-config/kuaishou-eticket-source]', - audit: (_req, data) => { - const result = /** @type {{ filePath?: string, source?: { enabled?: boolean, shops?: unknown[] } }} */ (data) - return { - action: 'platform_kuaishou_eticket_source_updated', - targetType: 'platform_config', - targetId: 'kuaishou_eticket_source', - data: { - filePath: String(result.filePath || '').trim(), - enabled: Boolean(result.source?.enabled), - shopCount: Array.isArray(result.source?.shops) ? result.source.shops.length : 0, - }, - } - }, - }, -)) +router.post( + "/platform-config/kuaishou-eticket-source", + createJsonHandler( + (req) => + updateAdminKuaishouEticketSourceConfig( + /** @type {AdminKuaishouEticketSourceConfigRouteBody} */ (req.body) + ), + { + successMessage: "快手小店核销配置已保存", + errorMessage: "保存快手小店核销配置失败", + scope: "[admin/platform-config/kuaishou-eticket-source]", + audit: (_req, data) => { + const result = + /** @type {{ filePath?: string, source?: { enabled?: boolean, shops?: unknown[] } }} */ ( + data + ); + return { + action: "platform_kuaishou_eticket_source_updated", + targetType: "platform_config", + targetId: "kuaishou_eticket_source", + data: { + filePath: String(result.filePath || "").trim(), + enabled: Boolean(result.source?.enabled), + shopCount: Array.isArray(result.source?.shops) + ? result.source.shops.length + : 0, + }, + }; + }, + } + ) +); -router.get('/platform-config/ninetyone/orders', createJsonHandler( - (req) => getAdminNinetyoneOrders(req.query), - { - successMessage: 'ok', - errorMessage: '读取 91卡券订单失败', - scope: '[admin/platform-config/ninetyone/orders]', - }, -)) +router.get( + "/platform-config/ninetyone/orders", + createJsonHandler((req) => getAdminNinetyoneOrders(req.query), { + successMessage: "ok", + errorMessage: "读取 91卡券订单失败", + scope: "[admin/platform-config/ninetyone/orders]", + }) +); -router.post('/platform-config/ninetyone/orders/:id/retry', createJsonHandler( - (req) => retryAdminNinetyoneOrder((/** @type {AdminEntityRouteParams} */ (req.params)).id), - { - successMessage: '91卡券订单已重试', - errorMessage: '重试 91卡券订单失败', - scope: '[admin/platform-config/ninetyone/orders/:id/retry]', - audit: (_req, data) => { - const result = /** @type {{ orderId?: number, orderNo?: string, taskCount?: number }} */ (data) - return { - action: 'platform_ninetyone_order_retried', - targetType: 'order', - targetId: String(result.orderId || '').trim(), - data: { - orderNo: String(result.orderNo || '').trim(), - taskCount: Number(result.taskCount || 0), - }, - } - }, - }, -)) +router.post( + "/platform-config/ninetyone/orders/:id/retry", + createJsonHandler( + (req) => + retryAdminNinetyoneOrder( + /** @type {AdminEntityRouteParams} */ (req.params).id + ), + { + successMessage: "91卡券订单已重试", + errorMessage: "重试 91卡券订单失败", + scope: "[admin/platform-config/ninetyone/orders/:id/retry]", + audit: (_req, data) => { + const result = + /** @type {{ orderId?: number, orderNo?: string, taskCount?: number }} */ ( + data + ); + return { + action: "platform_ninetyone_order_retried", + targetType: "order", + targetId: String(result.orderId || "").trim(), + data: { + orderNo: String(result.orderNo || "").trim(), + taskCount: Number(result.taskCount || 0), + }, + }; + }, + } + ) +); -router.post('/platform-config/ninetyone/orders/:id/fail', createJsonHandler( - (req) => failAdminNinetyoneOrder( - (/** @type {AdminEntityRouteParams} */ (req.params)).id, - /** @type {{ reason?: string }} */ (req.body), - ), - { - successMessage: '91卡券订单已标记失败', - errorMessage: '标记 91卡券订单失败', - scope: '[admin/platform-config/ninetyone/orders/:id/fail]', - audit: (req, data) => { - const result = /** @type {{ orderId?: number, orderNo?: string }} */ (data) - return { - action: 'platform_ninetyone_order_failed', - targetType: 'order', - targetId: String(result.orderId || '').trim(), - data: { - orderNo: String(result.orderNo || '').trim(), - reason: String((/** @type {{ reason?: string }} */ (req.body)).reason || '').trim(), - }, - } - }, - }, -)) +router.post( + "/platform-config/ninetyone/orders/:id/fail", + createJsonHandler( + (req) => + failAdminNinetyoneOrder( + /** @type {AdminEntityRouteParams} */ (req.params).id, + /** @type {{ reason?: string }} */ (req.body) + ), + { + successMessage: "91卡券订单已标记失败", + errorMessage: "标记 91卡券订单失败", + scope: "[admin/platform-config/ninetyone/orders/:id/fail]", + audit: (req, data) => { + const result = /** @type {{ orderId?: number, orderNo?: string }} */ ( + data + ); + return { + action: "platform_ninetyone_order_failed", + targetType: "order", + targetId: String(result.orderId || "").trim(), + data: { + orderNo: String(result.orderNo || "").trim(), + reason: String( + /** @type {{ reason?: string }} */ (req.body).reason || "" + ).trim(), + }, + }; + }, + } + ) +); -router.post('/platform-config/kuaishou-eticket/query-detail', createJsonHandler( - (req) => queryAdminKuaishouEticketDetail(/** @type {AdminKuaishouEticketDetailQueryRouteBody} */ (req.body)), - { - successMessage: '快手小店核销信息查询成功', - errorMessage: '查询快手小店核销信息失败', - scope: '[admin/platform-config/kuaishou-eticket/query-detail]', - audit: (req, data) => { - const body = /** @type {AdminKuaishouEticketDetailQueryRouteBody} */ (req.body) - const result = /** @type {{ eTicketId?: string, result?: number, ok?: boolean, alreadyConsumed?: boolean }} */ (data) - return { - action: 'platform_kuaishou_eticket_query_detail', - targetType: 'platform_config', - targetId: String(result.eTicketId || body.eTicketId || '').trim() || 'kuaishou_eticket', - data: { - result: Number(result.result || 0), - ok: Boolean(result.ok), - alreadyConsumed: Boolean(result.alreadyConsumed), - }, - } - }, - }, -)) +router.post( + "/platform-config/kuaishou-eticket/query-detail", + createJsonHandler( + (req) => + queryAdminKuaishouEticketDetail( + /** @type {AdminKuaishouEticketDetailQueryRouteBody} */ (req.body) + ), + { + successMessage: "快手小店核销信息查询成功", + errorMessage: "查询快手小店核销信息失败", + scope: "[admin/platform-config/kuaishou-eticket/query-detail]", + audit: (req, data) => { + const body = /** @type {AdminKuaishouEticketDetailQueryRouteBody} */ ( + req.body + ); + const result = + /** @type {{ eTicketId?: string, result?: number, ok?: boolean, alreadyConsumed?: boolean }} */ ( + data + ); + return { + action: "platform_kuaishou_eticket_query_detail", + targetType: "platform_config", + targetId: + String(result.eTicketId || body.eTicketId || "").trim() || + "kuaishou_eticket", + data: { + result: Number(result.result || 0), + ok: Boolean(result.ok), + alreadyConsumed: Boolean(result.alreadyConsumed), + }, + }; + }, + } + ) +); -router.post('/platform-config/kuaishou-eticket/query-shop-info', createJsonHandler( - (req) => queryAdminKuaishouEticketShopInfo(/** @type {AdminKuaishouEticketShopInfoRouteBody} */ (req.body)), - { - successMessage: '快手小店店铺信息查询成功', - errorMessage: '查询快手小店店铺信息失败', - scope: '[admin/platform-config/kuaishou-eticket/query-shop-info]', - audit: (req, data) => { - const body = /** @type {AdminKuaishouEticketShopInfoRouteBody} */ (req.body) - const result = /** @type {{ shop?: { shopId?: string, kshopName?: string }, result?: number, ok?: boolean }} */ (data) - return { - action: 'platform_kuaishou_eticket_query_shop_info', - targetType: 'platform_config', - targetId: String(result.shop?.shopId || body.shopId || '').trim() || 'kuaishou_eticket_shop_info', - data: { - result: Number(result.result || 0), - ok: Boolean(result.ok), - kshopName: String(result.shop?.kshopName || '').trim(), - }, - } - }, - }, -)) +router.post( + "/platform-config/kuaishou-eticket/query-shop-info", + createJsonHandler( + (req) => + queryAdminKuaishouEticketShopInfo( + /** @type {AdminKuaishouEticketShopInfoRouteBody} */ (req.body) + ), + { + successMessage: "快手小店店铺信息查询成功", + errorMessage: "查询快手小店店铺信息失败", + scope: "[admin/platform-config/kuaishou-eticket/query-shop-info]", + audit: (req, data) => { + const body = /** @type {AdminKuaishouEticketShopInfoRouteBody} */ ( + req.body + ); + const result = + /** @type {{ shop?: { shopId?: string, kshopName?: string }, result?: number, ok?: boolean }} */ ( + data + ); + return { + action: "platform_kuaishou_eticket_query_shop_info", + targetType: "platform_config", + targetId: + String(result.shop?.shopId || body.shopId || "").trim() || + "kuaishou_eticket_shop_info", + data: { + result: Number(result.result || 0), + ok: Boolean(result.ok), + kshopName: String(result.shop?.kshopName || "").trim(), + }, + }; + }, + } + ) +); -router.post('/platform-config/kuaishou-eticket/consume', createJsonHandler( - (req) => consumeAdminKuaishouEticket(/** @type {AdminKuaishouEticketConsumeRouteBody} */ (req.body)), - { - successMessage: '快手小店核销请求已执行', - errorMessage: '执行快手小店核销失败', - scope: '[admin/platform-config/kuaishou-eticket/consume]', - audit: (req, data) => { - const body = /** @type {AdminKuaishouEticketConsumeRouteBody} */ (req.body) - const result = /** @type {{ eTicketId?: string, result?: number, consumed?: boolean, alreadyConsumed?: boolean }} */ (data) - return { - action: 'platform_kuaishou_eticket_consume', - targetType: 'platform_config', - targetId: String(result.eTicketId || body.eTicketId || '').trim() || 'kuaishou_eticket', - data: { - result: Number(result.result || 0), - consumed: Boolean(result.consumed), - alreadyConsumed: Boolean(result.alreadyConsumed), - }, - } - }, - }, -)) +router.post( + "/platform-config/kuaishou-eticket/consume", + createJsonHandler( + (req) => + consumeAdminKuaishouEticket( + /** @type {AdminKuaishouEticketConsumeRouteBody} */ (req.body) + ), + { + successMessage: "快手小店核销请求已执行", + errorMessage: "执行快手小店核销失败", + scope: "[admin/platform-config/kuaishou-eticket/consume]", + audit: (req, data) => { + const body = /** @type {AdminKuaishouEticketConsumeRouteBody} */ ( + req.body + ); + const result = + /** @type {{ eTicketId?: string, result?: number, consumed?: boolean, alreadyConsumed?: boolean }} */ ( + data + ); + return { + action: "platform_kuaishou_eticket_consume", + targetType: "platform_config", + targetId: + String(result.eTicketId || body.eTicketId || "").trim() || + "kuaishou_eticket", + data: { + result: Number(result.result || 0), + consumed: Boolean(result.consumed), + alreadyConsumed: Boolean(result.alreadyConsumed), + }, + }; + }, + } + ) +); -router.get('/platform-config/cloudtentacles-source', createJsonHandler( - () => getAdminCloudtentaclesSourceConfig(), - { - successMessage: 'ok', - errorMessage: '读取 cloudtentacles 履约平台配置失败', - scope: '[admin/platform-config/cloudtentacles-source]', - }, -)) +router.get( + "/platform-config/cloudtentacles-source", + createJsonHandler(() => listAdminCloudtentaclesSources(), { + successMessage: "ok", + errorMessage: "读取 cloudtentacles 履约平台配置失败", + scope: "[admin/platform-config/cloudtentacles-source]", + }) +); -router.post('/platform-config/cloudtentacles-source', createJsonHandler( - (req) => updateAdminCloudtentaclesSourceConfig(/** @type {AdminCloudtentaclesSourceConfigRouteBody} */ (req.body)), - { - successMessage: 'cloudtentacles 履约平台配置已保存', - errorMessage: '保存 cloudtentacles 履约平台配置失败', - scope: '[admin/platform-config/cloudtentacles-source]', - audit: (_req, data) => { - const result = /** @type {{ filePath?: string, source?: { username?: string, enabled?: boolean } }} */ (data) - return { - action: 'platform_cloudtentacles_source_updated', - targetType: 'platform_config', - targetId: 'cloudtentacles_source', - data: { - filePath: String(result.filePath || '').trim(), - username: String(result.source?.username || '').trim(), - enabled: Boolean(result.source?.enabled), - }, - } - }, - }, -)) +router.delete( + "/platform-config/cloudtentacles-source/:sourceKey", + createJsonHandler( + (req) => + deleteAdminCloudtentaclesSource( + String(req.params.sourceKey || "").trim() + ), + { + successMessage: "cloudtentacles 履约平台配置已删除", + errorMessage: "删除 cloudtentacles 履约平台配置失败", + scope: "[admin/platform-config/cloudtentacles-source/:sourceKey]", + audit: (req) => ({ + action: "platform_cloudtentacles_source_deleted", + targetType: "platform_config", + targetId: String(req.params.sourceKey || "").trim(), + data: {}, + }), + } + ) +); -router.post('/platform-config/cloudtentacles/send-sms-code', createJsonHandler( - (req) => sendAdminCloudtentaclesSmsCode(/** @type {AdminCloudtentaclesSendSmsCodeRouteBody} */ (req.body)), - { - successMessage: 'cloudtentacles 短信验证码已发送', - errorMessage: 'cloudtentacles 发送短信验证码失败', - scope: '[admin/platform-config/cloudtentacles/send-sms-code]', - audit: (req, data) => { - const body = /** @type {AdminCloudtentaclesSendSmsCodeRouteBody} */ (req.body) - const result = /** @type {{ username?: string, phoneMasked?: string, baseUrl?: string }} */ (data) - return { - action: 'platform_cloudtentacles_send_sms_code', - targetType: 'platform_config', - targetId: String(result.username || body.username || '').trim() || 'cloudtentacles', - data: { - baseUrl: result.baseUrl || String(body.baseUrl || '').trim(), - phoneMasked: result.phoneMasked || '', - }, - } - }, - }, -)) +router.post( + "/platform-config/cloudtentacles-source", + createJsonHandler( + (req) => + updateAdminCloudtentaclesSourceConfig( + /** @type {AdminCloudtentaclesSourceConfigRouteBody} */ (req.body) + ), + { + successMessage: "cloudtentacles 履约平台配置已保存", + errorMessage: "保存 cloudtentacles 履约平台配置失败", + scope: "[admin/platform-config/cloudtentacles-source]", + audit: (_req, data) => { + const result = + /** @type {{ filePath?: string, source?: { username?: string, enabled?: boolean } }} */ ( + data + ); + return { + action: "platform_cloudtentacles_source_updated", + targetType: "platform_config", + targetId: "cloudtentacles_source", + data: { + filePath: String(result.filePath || "").trim(), + username: String(result.source?.username || "").trim(), + enabled: Boolean(result.source?.enabled), + }, + }; + }, + } + ) +); -router.post('/platform-config/cloudtentacles/test-login', createJsonHandler( - (req) => testAdminCloudtentaclesLogin(/** @type {AdminCloudtentaclesTestLoginRouteBody} */ (req.body)), - { - successMessage: 'cloudtentacles 登录测试成功', - errorMessage: 'cloudtentacles 登录测试失败', - scope: '[admin/platform-config/cloudtentacles/test-login]', - audit: (req, data) => { - const body = /** @type {AdminCloudtentaclesTestLoginRouteBody} */ (req.body) - const result = /** @type {{ baseUrl?: string, username?: string, session?: { permissionCount?: number } }} */ (data) - return { - action: 'platform_cloudtentacles_test_login', - targetType: 'platform_config', - targetId: String(result.username || body.username || '').trim() || 'cloudtentacles', - data: { - baseUrl: result.baseUrl || String(body.baseUrl || '').trim(), - permissionCount: Number(result.session?.permissionCount || 0), - }, - } - }, - }, -)) +router.post( + "/platform-config/cloudtentacles/send-sms-code", + createJsonHandler( + (req) => + sendAdminCloudtentaclesSmsCode( + /** @type {AdminCloudtentaclesSendSmsCodeRouteBody} */ (req.body) + ), + { + successMessage: "cloudtentacles 短信验证码已发送", + errorMessage: "cloudtentacles 发送短信验证码失败", + scope: "[admin/platform-config/cloudtentacles/send-sms-code]", + audit: (req, data) => { + const body = /** @type {AdminCloudtentaclesSendSmsCodeRouteBody} */ ( + req.body + ); + const result = + /** @type {{ username?: string, phoneMasked?: string, baseUrl?: string }} */ ( + data + ); + return { + action: "platform_cloudtentacles_send_sms_code", + targetType: "platform_config", + targetId: + String(result.username || body.username || "").trim() || + "cloudtentacles", + data: { + baseUrl: result.baseUrl || String(body.baseUrl || "").trim(), + phoneMasked: result.phoneMasked || "", + }, + }; + }, + } + ) +); -router.post('/platform-config/cloudtentacles/validate-session', createJsonHandler( - (req) => validateAdminCloudtentaclesSession(/** @type {AdminCloudtentaclesValidateSessionRouteBody} */ (req.body)), - { - successMessage: 'cloudtentacles 会话校验成功', - errorMessage: 'cloudtentacles 会话校验失败', - scope: '[admin/platform-config/cloudtentacles/validate-session]', - audit: (_req, data) => { - const result = /** @type {{ baseUrl?: string, session?: { permissionCount?: number } }} */ (data) - return { - action: 'platform_cloudtentacles_validate_session', - targetType: 'platform_config', - targetId: 'cloudtentacles_session', - data: { - baseUrl: String(result.baseUrl || '').trim(), - permissionCount: Number(result.session?.permissionCount || 0), - }, - } - }, - }, -)) +router.post( + "/platform-config/cloudtentacles/test-login", + createJsonHandler( + (req) => + testAdminCloudtentaclesLogin( + /** @type {AdminCloudtentaclesTestLoginRouteBody} */ (req.body) + ), + { + successMessage: "cloudtentacles 登录测试成功", + errorMessage: "cloudtentacles 登录测试失败", + scope: "[admin/platform-config/cloudtentacles/test-login]", + audit: (req, data) => { + const body = /** @type {AdminCloudtentaclesTestLoginRouteBody} */ ( + req.body + ); + const result = + /** @type {{ baseUrl?: string, username?: string, session?: { permissionCount?: number } }} */ ( + data + ); + return { + action: "platform_cloudtentacles_test_login", + targetType: "platform_config", + targetId: + String(result.username || body.username || "").trim() || + "cloudtentacles", + data: { + baseUrl: result.baseUrl || String(body.baseUrl || "").trim(), + permissionCount: Number(result.session?.permissionCount || 0), + }, + }; + }, + } + ) +); -router.post('/platform-config/cloudtentacles/asset', createJsonHandler( - (req) => getAdminCloudtentaclesAsset(/** @type {AdminCloudtentaclesCatalogQueryRouteBody} */ (req.body)), - { - successMessage: 'cloudtentacles 余额查询成功', - errorMessage: 'cloudtentacles 余额查询失败', - scope: '[admin/platform-config/cloudtentacles/asset]', - }, -)) +router.post( + "/platform-config/cloudtentacles/validate-session", + createJsonHandler( + (req) => + validateAdminCloudtentaclesSession( + /** @type {AdminCloudtentaclesValidateSessionRouteBody} */ (req.body) + ), + { + successMessage: "cloudtentacles 会话校验成功", + errorMessage: "cloudtentacles 会话校验失败", + scope: "[admin/platform-config/cloudtentacles/validate-session]", + audit: (_req, data) => { + const result = + /** @type {{ baseUrl?: string, session?: { permissionCount?: number } }} */ ( + data + ); + return { + action: "platform_cloudtentacles_validate_session", + targetType: "platform_config", + targetId: "cloudtentacles_session", + data: { + baseUrl: String(result.baseUrl || "").trim(), + permissionCount: Number(result.session?.permissionCount || 0), + }, + }; + }, + } + ) +); -router.post('/platform-config/cloudtentacles/categories', createJsonHandler( - (req) => getAdminCloudtentaclesCategories(/** @type {AdminCloudtentaclesCatalogQueryRouteBody} */ (req.body)), - { - successMessage: 'cloudtentacles 分类查询成功', - errorMessage: 'cloudtentacles 分类查询失败', - scope: '[admin/platform-config/cloudtentacles/categories]', - }, -)) +router.post( + "/platform-config/cloudtentacles/asset", + createJsonHandler( + (req) => + getAdminCloudtentaclesAsset( + /** @type {AdminCloudtentaclesCatalogQueryRouteBody} */ (req.body) + ), + { + successMessage: "cloudtentacles 余额查询成功", + errorMessage: "cloudtentacles 余额查询失败", + scope: "[admin/platform-config/cloudtentacles/asset]", + } + ) +); -router.post('/platform-config/cloudtentacles/sku/list', createJsonHandler( - (req) => getAdminCloudtentaclesSkuList(/** @type {AdminCloudtentaclesCatalogQueryRouteBody} */ (req.body)), - { - successMessage: 'cloudtentacles SKU 列表查询成功', - errorMessage: 'cloudtentacles SKU 列表查询失败', - scope: '[admin/platform-config/cloudtentacles/sku/list]', - }, -)) +router.post( + "/platform-config/cloudtentacles/categories", + createJsonHandler( + (req) => + getAdminCloudtentaclesCategories( + /** @type {AdminCloudtentaclesCatalogQueryRouteBody} */ (req.body) + ), + { + successMessage: "cloudtentacles 分类查询成功", + errorMessage: "cloudtentacles 分类查询失败", + scope: "[admin/platform-config/cloudtentacles/categories]", + } + ) +); -router.post('/platform-config/cloudtentacles/sku/buy', createJsonHandler( - (req) => buyAdminCloudtentaclesSku(/** @type {AdminCloudtentaclesSkuBuyRouteBody} */ (req.body)), - { - successMessage: 'cloudtentacles SKU 购买成功', - errorMessage: 'cloudtentacles SKU 购买失败', - scope: '[admin/platform-config/cloudtentacles/sku/buy]', - }, -)) +router.post( + "/platform-config/cloudtentacles/sku/list", + createJsonHandler( + (req) => + getAdminCloudtentaclesSkuList( + /** @type {AdminCloudtentaclesCatalogQueryRouteBody} */ (req.body) + ), + { + successMessage: "cloudtentacles SKU 列表查询成功", + errorMessage: "cloudtentacles SKU 列表查询失败", + scope: "[admin/platform-config/cloudtentacles/sku/list]", + } + ) +); -router.post('/platform-config/cloudtentacles/sku/use', createJsonHandler( - (req) => useAdminCloudtentaclesSku(/** @type {AdminCloudtentaclesSkuUseRouteBody} */ (req.body)), - { - successMessage: 'cloudtentacles 发货成功', - errorMessage: 'cloudtentacles 发货失败', - scope: '[admin/platform-config/cloudtentacles/sku/use]', - }, -)) +router.post( + "/platform-config/cloudtentacles/sku/buy", + createJsonHandler( + (req) => + buyAdminCloudtentaclesSku( + /** @type {AdminCloudtentaclesSkuBuyRouteBody} */ (req.body) + ), + { + successMessage: "cloudtentacles SKU 购买成功", + errorMessage: "cloudtentacles SKU 购买失败", + scope: "[admin/platform-config/cloudtentacles/sku/buy]", + } + ) +); -router.post('/platform-config/cloudtentacles/knapsack', createJsonHandler( - (req) => getAdminCloudtentaclesKnapsack(/** @type {AdminCloudtentaclesCatalogQueryRouteBody} */ (req.body)), - { - successMessage: 'cloudtentacles 背包查询成功', - errorMessage: 'cloudtentacles 背包查询失败', - scope: '[admin/platform-config/cloudtentacles/knapsack]', - }, -)) +router.post( + "/platform-config/cloudtentacles/sku/use", + createJsonHandler( + (req) => + useAdminCloudtentaclesSku( + /** @type {AdminCloudtentaclesSkuUseRouteBody} */ (req.body) + ), + { + successMessage: "cloudtentacles 发货成功", + errorMessage: "cloudtentacles 发货失败", + scope: "[admin/platform-config/cloudtentacles/sku/use]", + } + ) +); -router.post('/platform-config/cloudtentacles/vn/list', createJsonHandler( - (req) => listAdminCloudtentaclesVirtualNumbers(/** @type {AdminCloudtentaclesVirtualNumberRouteBody} */ (req.body)), - { - successMessage: 'cloudtentacles 虚拟号列表查询成功', - errorMessage: 'cloudtentacles 虚拟号列表查询失败', - scope: '[admin/platform-config/cloudtentacles/vn/list]', - }, -)) +router.post( + "/platform-config/cloudtentacles/knapsack", + createJsonHandler( + (req) => + getAdminCloudtentaclesKnapsack( + /** @type {AdminCloudtentaclesCatalogQueryRouteBody} */ (req.body) + ), + { + successMessage: "cloudtentacles 背包查询成功", + errorMessage: "cloudtentacles 背包查询失败", + scope: "[admin/platform-config/cloudtentacles/knapsack]", + } + ) +); -router.post('/platform-config/cloudtentacles/vn/appoint', createJsonHandler( - (req) => appointAdminCloudtentaclesVirtualNumber(/** @type {AdminCloudtentaclesVirtualNumberRouteBody} */ (req.body)), - { - successMessage: 'cloudtentacles 虚拟号申请成功', - errorMessage: 'cloudtentacles 虚拟号申请失败', - scope: '[admin/platform-config/cloudtentacles/vn/appoint]', - }, -)) +router.post( + "/platform-config/cloudtentacles/vn/list", + createJsonHandler( + (req) => + listAdminCloudtentaclesVirtualNumbers( + /** @type {AdminCloudtentaclesVirtualNumberRouteBody} */ (req.body) + ), + { + successMessage: "cloudtentacles 虚拟号列表查询成功", + errorMessage: "cloudtentacles 虚拟号列表查询失败", + scope: "[admin/platform-config/cloudtentacles/vn/list]", + } + ) +); -router.post('/platform-config/cloudtentacles/vn/generate-login-code', createJsonHandler( - (req) => generateAdminCloudtentaclesLoginCode(/** @type {AdminCloudtentaclesVirtualNumberRouteBody} */ (req.body)), - { - successMessage: 'cloudtentacles 登录码生成成功', - errorMessage: 'cloudtentacles 登录码生成失败', - scope: '[admin/platform-config/cloudtentacles/vn/generate-login-code]', - }, -)) +router.post( + "/platform-config/cloudtentacles/vn/appoint", + createJsonHandler( + (req) => + appointAdminCloudtentaclesVirtualNumber( + /** @type {AdminCloudtentaclesVirtualNumberRouteBody} */ (req.body) + ), + { + successMessage: "cloudtentacles 虚拟号申请成功", + errorMessage: "cloudtentacles 虚拟号申请失败", + scope: "[admin/platform-config/cloudtentacles/vn/appoint]", + } + ) +); -router.post('/platform-config/cloudtentacles/vn/fetch-code', createJsonHandler( - (req) => fetchAdminCloudtentaclesVirtualNumberCode(/** @type {AdminCloudtentaclesVirtualNumberRouteBody} */ (req.body)), - { - successMessage: 'cloudtentacles 验证码获取成功', - errorMessage: 'cloudtentacles 验证码获取失败', - scope: '[admin/platform-config/cloudtentacles/vn/fetch-code]', - }, -)) +router.post( + "/platform-config/cloudtentacles/vn/generate-login-code", + createJsonHandler( + (req) => + generateAdminCloudtentaclesLoginCode( + /** @type {AdminCloudtentaclesVirtualNumberRouteBody} */ (req.body) + ), + { + successMessage: "cloudtentacles 登录码生成成功", + errorMessage: "cloudtentacles 登录码生成失败", + scope: "[admin/platform-config/cloudtentacles/vn/generate-login-code]", + } + ) +); -router.post('/platform-config/cloudtentacles/vn/verify-code', createJsonHandler( - (req) => verifyAdminCloudtentaclesLoginCode(/** @type {AdminCloudtentaclesVirtualNumberRouteBody} */ (req.body)), - { - successMessage: 'cloudtentacles 登录码校验成功', - errorMessage: 'cloudtentacles 登录码校验失败', - scope: '[admin/platform-config/cloudtentacles/vn/verify-code]', - }, -)) +router.post( + "/platform-config/cloudtentacles/vn/fetch-code", + createJsonHandler( + (req) => + fetchAdminCloudtentaclesVirtualNumberCode( + /** @type {AdminCloudtentaclesVirtualNumberRouteBody} */ (req.body) + ), + { + successMessage: "cloudtentacles 验证码获取成功", + errorMessage: "cloudtentacles 验证码获取失败", + scope: "[admin/platform-config/cloudtentacles/vn/fetch-code]", + } + ) +); -router.post('/platform-config/cloudtentacles/vn/bind-url', createJsonHandler( - (req) => getAdminCloudtentaclesBindUrl(/** @type {AdminCloudtentaclesVirtualNumberRouteBody} */ (req.body)), - { - successMessage: 'cloudtentacles 兑换链接获取成功', - errorMessage: 'cloudtentacles 兑换链接获取失败', - scope: '[admin/platform-config/cloudtentacles/vn/bind-url]', - }, -)) +router.post( + "/platform-config/cloudtentacles/vn/verify-code", + createJsonHandler( + (req) => + verifyAdminCloudtentaclesLoginCode( + /** @type {AdminCloudtentaclesVirtualNumberRouteBody} */ (req.body) + ), + { + successMessage: "cloudtentacles 登录码校验成功", + errorMessage: "cloudtentacles 登录码校验失败", + scope: "[admin/platform-config/cloudtentacles/vn/verify-code]", + } + ) +); -router.post('/platform-config/cloudtentacles/vn/back', createJsonHandler( - (req) => backAdminCloudtentaclesVirtualNumber(/** @type {AdminCloudtentaclesVirtualNumberRouteBody} */ (req.body)), - { - successMessage: 'cloudtentacles 号码退还成功', - errorMessage: 'cloudtentacles 号码退还失败', - scope: '[admin/platform-config/cloudtentacles/vn/back]', - }, -)) +router.post( + "/platform-config/cloudtentacles/vn/bind-url", + createJsonHandler( + (req) => + getAdminCloudtentaclesBindUrl( + /** @type {AdminCloudtentaclesVirtualNumberRouteBody} */ (req.body) + ), + { + successMessage: "cloudtentacles 兑换链接获取成功", + errorMessage: "cloudtentacles 兑换链接获取失败", + scope: "[admin/platform-config/cloudtentacles/vn/bind-url]", + } + ) +); -router.post('/platform-config/cloudtentacles/debug/full-flow', createJsonHandler( - (req) => runAdminCloudtentaclesFullFlow(/** @type {AdminCloudtentaclesFullFlowRouteBody} */ (req.body)), - { - successMessage: 'cloudtentacles 完整调试流程执行成功', - errorMessage: 'cloudtentacles 完整调试流程执行失败', - scope: '[admin/platform-config/cloudtentacles/debug/full-flow]', - }, -)) +router.post( + "/platform-config/cloudtentacles/vn/back", + createJsonHandler( + (req) => + backAdminCloudtentaclesVirtualNumber( + /** @type {AdminCloudtentaclesVirtualNumberRouteBody} */ (req.body) + ), + { + successMessage: "cloudtentacles 号码退还成功", + errorMessage: "cloudtentacles 号码退还失败", + scope: "[admin/platform-config/cloudtentacles/vn/back]", + } + ) +); -router.get('/platform-config/fulfillment-bindings', createJsonHandler( - () => getAdminFulfillmentBindingConfigs(), - { - successMessage: 'ok', - errorMessage: '读取履约配置失败', - scope: '[admin/platform-config/fulfillment-bindings]', - }, -)) +router.post( + "/platform-config/cloudtentacles/debug/full-flow", + createJsonHandler( + (req) => + runAdminCloudtentaclesFullFlow( + /** @type {AdminCloudtentaclesFullFlowRouteBody} */ (req.body) + ), + { + successMessage: "cloudtentacles 完整调试流程执行成功", + errorMessage: "cloudtentacles 完整调试流程执行失败", + scope: "[admin/platform-config/cloudtentacles/debug/full-flow]", + } + ) +); -router.post('/platform-config/fulfillment-bindings/lookup-order', createJsonHandler( - (req) => lookupAdminFulfillmentBindingOrder(/** @type {AdminFulfillmentBindingLookupRouteBody} */ (req.body)), - { - successMessage: '订单商品查询成功', - errorMessage: '手动查询订单商品失败', - scope: '[admin/platform-config/fulfillment-bindings/lookup-order]', - audit: (req, data) => { - const result = /** @type {{ order?: { provider?: string, platform?: string, shopId?: string, platformOrderId?: string }, items?: unknown[] }} */ (data) - return { - action: 'platform_fulfillment_order_lookup', - targetType: 'platform_config', - targetId: [ - result.order?.provider || 'unknown', - result.order?.platform || 'unknown', - result.order?.shopId || 'unknown', - result.order?.platformOrderId || 'unknown', - ].join(':'), - data: { - itemCount: Array.isArray(result.items) ? result.items.length : 0, - shopId: result.order?.shopId || String((/** @type {AdminFulfillmentBindingLookupRouteBody} */ (req.body))?.shopId || '').trim(), - platformOrderId: result.order?.platformOrderId || String((/** @type {AdminFulfillmentBindingLookupRouteBody} */ (req.body))?.platformOrderId || '').trim(), - }, - } - }, - }, -)) +router.get( + "/platform-config/fulfillment-bindings", + createJsonHandler(() => getAdminFulfillmentBindingConfigs(), { + successMessage: "ok", + errorMessage: "读取履约配置失败", + scope: "[admin/platform-config/fulfillment-bindings]", + }) +); -router.post('/platform-config/fulfillment-bindings', createJsonHandler( - (req) => updateAdminFulfillmentBindingConfigs(/** @type {AdminFulfillmentBindingConfigRouteBody} */ (req.body)), - { - successMessage: '履约配置已保存', - errorMessage: '保存履约配置失败', - scope: '[admin/platform-config/fulfillment-bindings]', - audit: (_req, data) => { - const result = /** @type {{ bindings?: unknown[], filePath?: string }} */ (data) - return { - action: 'platform_fulfillment_config_updated', - targetType: 'platform_config', - targetId: 'fulfillment_bindings', - data: { - bindingCount: Array.isArray(result.bindings) ? result.bindings.length : 0, - filePath: result.filePath || '', - }, - } - }, - }, -)) +router.post( + "/platform-config/fulfillment-bindings/lookup-order", + createJsonHandler( + (req) => + lookupAdminFulfillmentBindingOrder( + /** @type {AdminFulfillmentBindingLookupRouteBody} */ (req.body) + ), + { + successMessage: "订单商品查询成功", + errorMessage: "手动查询订单商品失败", + scope: "[admin/platform-config/fulfillment-bindings/lookup-order]", + audit: (req, data) => { + const result = + /** @type {{ order?: { provider?: string, platform?: string, shopId?: string, platformOrderId?: string }, items?: unknown[] }} */ ( + data + ); + return { + action: "platform_fulfillment_order_lookup", + targetType: "platform_config", + targetId: [ + result.order?.provider || "unknown", + result.order?.platform || "unknown", + result.order?.shopId || "unknown", + result.order?.platformOrderId || "unknown", + ].join(":"), + data: { + itemCount: Array.isArray(result.items) ? result.items.length : 0, + shopId: + result.order?.shopId || + String( + /** @type {AdminFulfillmentBindingLookupRouteBody} */ (req.body) + ?.shopId || "" + ).trim(), + platformOrderId: + result.order?.platformOrderId || + String( + /** @type {AdminFulfillmentBindingLookupRouteBody} */ (req.body) + ?.platformOrderId || "" + ).trim(), + }, + }; + }, + } + ) +); -router.get('/platform-config/kuaishou-cloud-fulfillment', createJsonHandler( - () => getAdminKuaishouCloudFulfillmentConfig(), - { - successMessage: 'ok', - errorMessage: '读取新履约配置失败', - scope: '[admin/platform-config/kuaishou-cloud-fulfillment]', - }, -)) +router.post( + "/platform-config/fulfillment-bindings", + createJsonHandler( + (req) => + updateAdminFulfillmentBindingConfigs( + /** @type {AdminFulfillmentBindingConfigRouteBody} */ (req.body) + ), + { + successMessage: "履约配置已保存", + errorMessage: "保存履约配置失败", + scope: "[admin/platform-config/fulfillment-bindings]", + audit: (_req, data) => { + const result = + /** @type {{ bindings?: unknown[], filePath?: string }} */ (data); + return { + action: "platform_fulfillment_config_updated", + targetType: "platform_config", + targetId: "fulfillment_bindings", + data: { + bindingCount: Array.isArray(result.bindings) + ? result.bindings.length + : 0, + filePath: result.filePath || "", + }, + }; + }, + } + ) +); -router.post('/platform-config/kuaishou-cloud-fulfillment', createJsonHandler( - (req) => updateAdminKuaishouCloudFulfillmentConfig(/** @type {AdminKuaishouCloudFulfillmentConfigRouteBody} */ (req.body)), - { - successMessage: '新履约配置已保存', - errorMessage: '保存新履约配置失败', - scope: '[admin/platform-config/kuaishou-cloud-fulfillment]', - audit: (_req, data) => { - const result = /** @type {{ filePath?: string, source?: { items?: unknown[] } }} */ (data) - return { - action: 'platform_kuaishou_cloud_fulfillment_updated', - targetType: 'platform_config', - targetId: 'kuaishou_cloud_fulfillment', - data: { - itemCount: Array.isArray(result.source?.items) ? result.source.items.length : 0, - filePath: String(result.filePath || '').trim(), - }, - } - }, - }, -)) +router.get( + "/platform-config/kuaishou-cloud-fulfillment", + createJsonHandler(() => getAdminKuaishouCloudFulfillmentConfig(), { + successMessage: "ok", + errorMessage: "读取新履约配置失败", + scope: "[admin/platform-config/kuaishou-cloud-fulfillment]", + }) +); -export default router +router.post( + "/platform-config/kuaishou-cloud-fulfillment", + createJsonHandler( + (req) => + updateAdminKuaishouCloudFulfillmentConfig( + /** @type {AdminKuaishouCloudFulfillmentConfigRouteBody} */ (req.body) + ), + { + successMessage: "新履约配置已保存", + errorMessage: "保存新履约配置失败", + scope: "[admin/platform-config/kuaishou-cloud-fulfillment]", + audit: (_req, data) => { + const result = + /** @type {{ filePath?: string, source?: { items?: unknown[] } }} */ ( + data + ); + return { + action: "platform_kuaishou_cloud_fulfillment_updated", + targetType: "platform_config", + targetId: "kuaishou_cloud_fulfillment", + data: { + itemCount: Array.isArray(result.source?.items) + ? result.source.items.length + : 0, + filePath: String(result.filePath || "").trim(), + }, + }; + }, + } + ) +); + +export default router; diff --git a/apps/backend/src/services/admin/platform-config/cloudtentacles-service.js b/apps/backend/src/services/admin/platform-config/cloudtentacles-service.js index 5c631818..b81e3427 100644 --- a/apps/backend/src/services/admin/platform-config/cloudtentacles-service.js +++ b/apps/backend/src/services/admin/platform-config/cloudtentacles-service.js @@ -1,29 +1,33 @@ // @ts-check import { - getCloudtentaclesSourceConfig, + getCloudtentaclesSourceByKey, getCloudtentaclesSourcesFilePath, - saveCloudtentaclesSourceConfig, -} from '../../platforms/cloudtentacles/source-config-service.js' + listCloudtentaclesSources, + saveCloudtentaclesSourceByKey, + saveCloudtentaclesSourcesList, + deleteCloudtentaclesSourceByKey, +} from "../../platforms/cloudtentacles/source-config-service.js"; import { - clearCloudtentaclesSessionState, + clearCloudtentaclesSessionStateByKey, getCloudtentaclesSessionFilePath, - getCloudtentaclesSessionState, - saveCloudtentaclesSessionState, -} from '../../platforms/cloudtentacles/session-state-service.js' + getCloudtentaclesSessionStateByKey, + saveCloudtentaclesSessionStateByKey, + getAllCloudtentaclesSessionStates, +} from "../../platforms/cloudtentacles/session-state-service.js"; import { loginCloudtentaclesSession, sendCloudtentaclesSmsCode, validateCloudtentaclesSession, -} from '../../platforms/cloudtentacles/session-service.js' +} from "../../platforms/cloudtentacles/session-service.js"; import { buyCloudtentaclesSku, getCloudtentaclesAsset, getCloudtentaclesCategories, listCloudtentaclesSku, useCloudtentaclesSku, -} from '../../platforms/cloudtentacles/catalog-service.js' -import { getCloudtentaclesKnapsack } from '../../platforms/cloudtentacles/knapsack-service.js' +} from "../../platforms/cloudtentacles/catalog-service.js"; +import { getCloudtentaclesKnapsack } from "../../platforms/cloudtentacles/knapsack-service.js"; import { appointCloudtentaclesVirtualNumber, backCloudtentaclesVirtualNumber, @@ -32,22 +36,25 @@ import { getCloudtentaclesBindUrl, listCloudtentaclesVirtualNumbers, verifyCloudtentaclesLoginCode, -} from '../../platforms/cloudtentacles/virtual-number-service.js' -import { runCloudtentaclesFullDebugFlow } from '../../platforms/cloudtentacles/debug-flow-service.js' -import { hasCloudtentaclesCredentialContextChanged, pickFirstNonEmpty } from './context.js' +} from "../../platforms/cloudtentacles/virtual-number-service.js"; +import { runCloudtentaclesFullDebugFlow } from "../../platforms/cloudtentacles/debug-flow-service.js"; +import { + hasCloudtentaclesCredentialContextChanged, + pickFirstNonEmpty, +} from "./context.js"; import { buildAdminCloudtentaclesLoginResult, buildAdminCloudtentaclesPersistedSessionPayload, buildAdminCloudtentaclesValidateResult, resolveAdminCloudtentaclesCredentialPayload, resolveAdminCloudtentaclesSessionPayload, -} from './cloudtentacles.js' -import { normalizeAdminCloudtentaclesSourceConfigPayload } from './writes.js' +} from "./cloudtentacles.js"; +import { normalizeAdminCloudtentaclesSourceConfigPayload } from "./writes.js"; import { mapAdminCloudtentaclesSession, mapAdminCloudtentaclesSourceConfig, maskPhone, -} from './mappers.js' +} from "./mappers.js"; /** @typedef {import('../../../types/admin-write-inputs.js').AdminCloudtentaclesSourceConfigInput} AdminCloudtentaclesSourceConfigInput */ /** @typedef {import('../../../types/admin-write-inputs.js').AdminCloudtentaclesSendSmsCodeInput} AdminCloudtentaclesSendSmsCodeInput */ @@ -59,192 +66,319 @@ import { /** @typedef {import('../../../types/admin-write-inputs.js').AdminCloudtentaclesVirtualNumberInput} AdminCloudtentaclesVirtualNumberInput */ /** @typedef {import('../../../types/admin-write-inputs.js').AdminCloudtentaclesFullFlowInput} AdminCloudtentaclesFullFlowInput */ -export function getAdminCloudtentaclesSourceConfig() { - const config = getCloudtentaclesSourceConfig() - const session = getCloudtentaclesSessionState() +export function listAdminCloudtentaclesSources() { + const list = listCloudtentaclesSources(); + const sessions = getAllCloudtentaclesSessionStates(); + + const sources = Array.isArray(list.sources) + ? list.sources.map((s) => mapAdminCloudtentaclesSourceConfig(s)) + : []; + + const mappedSessions = {}; + for (const [key, session] of Object.entries(sessions.sessions || {})) { + mappedSessions[key] = mapAdminCloudtentaclesSession(session); + } return { filePath: getCloudtentaclesSourcesFilePath(), sessionFilePath: getCloudtentaclesSessionFilePath(), - source: mapAdminCloudtentaclesSourceConfig(config), - session: mapAdminCloudtentaclesSession(session), - } + enabled: list.enabled, + sources, + sessions: mappedSessions, + }; } /** @param {AdminCloudtentaclesSourceConfigInput} [payload] */ -export function updateAdminCloudtentaclesSourceConfig(payload = /** @type {AdminCloudtentaclesSourceConfigInput} */ ({})) { - const current = getCloudtentaclesSourceConfig() - const saved = saveCloudtentaclesSourceConfig(normalizeAdminCloudtentaclesSourceConfigPayload(payload)) - const shouldClearSession = hasCloudtentaclesCredentialContextChanged(current, saved) - const session = shouldClearSession ? clearCloudtentaclesSessionState() : getCloudtentaclesSessionState() +export function updateAdminCloudtentaclesSourceConfig( + payload = /** @type {AdminCloudtentaclesSourceConfigInput} */ ({}) +) { + // Batch save: if payload.sources is an array, save the whole list + if (Array.isArray(payload.sources)) { + const normalizedList = payload.sources.map((item) => + normalizeAdminCloudtentaclesSourceConfigPayload(item) + ); + const savedList = saveCloudtentaclesSourcesList({ + enabled: payload.enabled !== false, + sources: normalizedList, + }); + const sessions = getAllCloudtentaclesSessionStates(); + + const sources = Array.isArray(savedList.sources) + ? savedList.sources.map((s) => mapAdminCloudtentaclesSourceConfig(s)) + : []; + const mappedSessions = {}; + for (const [key, session] of Object.entries(sessions.sessions || {})) { + mappedSessions[key] = mapAdminCloudtentaclesSession(session); + } + + return { + filePath: getCloudtentaclesSourcesFilePath(), + sessionFilePath: getCloudtentaclesSessionFilePath(), + enabled: savedList.enabled, + sources, + sessions: mappedSessions, + }; + } + + // Single source save + const sourceKey = String(payload.sourceKey || "").trim() || "default"; + const current = getCloudtentaclesSourceByKey(sourceKey); + const normalized = normalizeAdminCloudtentaclesSourceConfigPayload({ + ...payload, + sourceKey, + }); + const saved = saveCloudtentaclesSourceByKey(sourceKey, normalized); + const shouldClearSession = hasCloudtentaclesCredentialContextChanged( + current, + saved + ); + const session = shouldClearSession + ? (clearCloudtentaclesSessionStateByKey(sourceKey), + getCloudtentaclesSessionStateByKey(sourceKey)) + : getCloudtentaclesSessionStateByKey(sourceKey); return { filePath: getCloudtentaclesSourcesFilePath(), sessionFilePath: getCloudtentaclesSessionFilePath(), source: mapAdminCloudtentaclesSourceConfig(saved), session: mapAdminCloudtentaclesSession(session), + }; +} + +/** @param {string} sourceKey */ +export function deleteAdminCloudtentaclesSource(sourceKey) { + const key = String(sourceKey || "").trim(); + if (!key) { + throw new Error("sourceKey is required for deletion"); } + clearCloudtentaclesSessionStateByKey(key); + deleteCloudtentaclesSourceByKey(key); } /** @param {AdminCloudtentaclesSendSmsCodeInput} [payload] */ -export async function sendAdminCloudtentaclesSmsCode(payload = /** @type {AdminCloudtentaclesSendSmsCodeInput} */ ({})) { - const result = await sendCloudtentaclesSmsCode(resolveAdminCloudtentaclesCredentialPayload(payload)) +export async function sendAdminCloudtentaclesSmsCode( + payload = /** @type {AdminCloudtentaclesSendSmsCodeInput} */ ({}) +) { + const result = await sendCloudtentaclesSmsCode( + resolveAdminCloudtentaclesCredentialPayload(payload) + ); return { ...result, phoneMasked: maskPhone(result.phone), - } + }; } /** @param {AdminCloudtentaclesTestLoginInput} [payload] */ -export async function testAdminCloudtentaclesLogin(payload = /** @type {AdminCloudtentaclesTestLoginInput} */ ({})) { - const savedSource = getCloudtentaclesSourceConfig() - const credentialContext = resolveAdminCloudtentaclesCredentialPayload(payload, { savedSource }) +export async function testAdminCloudtentaclesLogin( + payload = /** @type {AdminCloudtentaclesTestLoginInput} */ ({}) +) { + const sourceKey = String(payload.sourceKey || "").trim() || "default"; + const savedSource = getCloudtentaclesSourceByKey(sourceKey) || {}; + const credentialContext = resolveAdminCloudtentaclesCredentialPayload( + payload, + { savedSource } + ); const session = await loginCloudtentaclesSession({ ...credentialContext, - code: String(payload.code || '').trim(), - }) - const savedSession = saveCloudtentaclesSessionState(buildAdminCloudtentaclesPersistedSessionPayload(session, { - deviceId: credentialContext.deviceId, - deviceType: credentialContext.deviceType, - })) + code: String(payload.code || "").trim(), + }); + const savedSession = saveCloudtentaclesSessionStateByKey( + sourceKey, + buildAdminCloudtentaclesPersistedSessionPayload(session, { + deviceId: credentialContext.deviceId, + deviceType: credentialContext.deviceType, + }) + ); - return buildAdminCloudtentaclesLoginResult(session, savedSession) + return buildAdminCloudtentaclesLoginResult(session, savedSession, sourceKey); } /** @param {AdminCloudtentaclesValidateSessionInput} [payload] */ -export async function validateAdminCloudtentaclesSession(payload = /** @type {AdminCloudtentaclesValidateSessionInput} */ ({})) { - const savedSource = getCloudtentaclesSourceConfig() - const persistedSession = getCloudtentaclesSessionState() - const sessionContext = resolveAdminCloudtentaclesSessionPayload(payload, { savedSource, persistedSession }) - const session = await validateCloudtentaclesSession(sessionContext) - const savedSession = saveCloudtentaclesSessionState(buildAdminCloudtentaclesPersistedSessionPayload(session, { - username: pickFirstNonEmpty([persistedSession.username, savedSource.username]), - phone: pickFirstNonEmpty([persistedSession.phone, savedSource.phone]), - deviceId: sessionContext.deviceId, - deviceType: sessionContext.deviceType, - })) +export async function validateAdminCloudtentaclesSession( + payload = /** @type {AdminCloudtentaclesValidateSessionInput} */ ({}) +) { + const sourceKey = String(payload.sourceKey || "").trim() || "default"; + const savedSource = getCloudtentaclesSourceByKey(sourceKey) || {}; + const persistedSession = getCloudtentaclesSessionStateByKey(sourceKey) || {}; + const sessionContext = resolveAdminCloudtentaclesSessionPayload(payload, { + savedSource, + persistedSession, + }); + const session = await validateCloudtentaclesSession(sessionContext); + const savedSession = saveCloudtentaclesSessionStateByKey( + sourceKey, + buildAdminCloudtentaclesPersistedSessionPayload(session, { + username: pickFirstNonEmpty([ + persistedSession.username, + savedSource.username, + ]), + phone: pickFirstNonEmpty([persistedSession.phone, savedSource.phone]), + deviceId: sessionContext.deviceId, + deviceType: sessionContext.deviceType, + }) + ); - return buildAdminCloudtentaclesValidateResult(session, savedSession) + return buildAdminCloudtentaclesValidateResult( + session, + savedSession, + sourceKey + ); } /** @param {AdminCloudtentaclesCatalogQueryInput} [payload] */ -export async function getAdminCloudtentaclesAsset(payload = /** @type {AdminCloudtentaclesCatalogQueryInput} */ ({})) { - return getCloudtentaclesAsset(resolveAdminCloudtentaclesSessionPayload(payload)) +export async function getAdminCloudtentaclesAsset( + payload = /** @type {AdminCloudtentaclesCatalogQueryInput} */ ({}) +) { + return getCloudtentaclesAsset( + resolveAdminCloudtentaclesSessionPayload(payload) + ); } /** @param {AdminCloudtentaclesCatalogQueryInput} [payload] */ -export async function getAdminCloudtentaclesCategories(payload = /** @type {AdminCloudtentaclesCatalogQueryInput} */ ({})) { - return getCloudtentaclesCategories(resolveAdminCloudtentaclesSessionPayload(payload)) +export async function getAdminCloudtentaclesCategories( + payload = /** @type {AdminCloudtentaclesCatalogQueryInput} */ ({}) +) { + return getCloudtentaclesCategories( + resolveAdminCloudtentaclesSessionPayload(payload) + ); } /** @param {AdminCloudtentaclesCatalogQueryInput} [payload] */ -export async function getAdminCloudtentaclesSkuList(payload = /** @type {AdminCloudtentaclesCatalogQueryInput} */ ({})) { - return listCloudtentaclesSku(resolveAdminCloudtentaclesSessionPayload(payload)) +export async function getAdminCloudtentaclesSkuList( + payload = /** @type {AdminCloudtentaclesCatalogQueryInput} */ ({}) +) { + return listCloudtentaclesSku( + resolveAdminCloudtentaclesSessionPayload(payload) + ); } /** @param {AdminCloudtentaclesSkuBuyInput} [payload] */ -export async function buyAdminCloudtentaclesSku(payload = /** @type {AdminCloudtentaclesSkuBuyInput} */ ({})) { - const context = resolveAdminCloudtentaclesSessionPayload(payload) +export async function buyAdminCloudtentaclesSku( + payload = /** @type {AdminCloudtentaclesSkuBuyInput} */ ({}) +) { + const context = resolveAdminCloudtentaclesSessionPayload(payload); return buyCloudtentaclesSku({ ...context, id: payload.id, count: payload.count, - }) + }); } /** @param {AdminCloudtentaclesSkuUseInput} [payload] */ -export async function useAdminCloudtentaclesSku(payload = /** @type {AdminCloudtentaclesSkuUseInput} */ ({})) { - const context = resolveAdminCloudtentaclesSessionPayload(payload) +export async function useAdminCloudtentaclesSku( + payload = /** @type {AdminCloudtentaclesSkuUseInput} */ ({}) +) { + const context = resolveAdminCloudtentaclesSessionPayload(payload); return useCloudtentaclesSku({ ...context, id: payload.id, virtualNumberId: payload.virtualNumberId, phone: payload.phone, - }) + }); } /** @param {AdminCloudtentaclesCatalogQueryInput} [payload] */ -export async function getAdminCloudtentaclesKnapsack(payload = /** @type {AdminCloudtentaclesCatalogQueryInput} */ ({})) { - return getCloudtentaclesKnapsack(resolveAdminCloudtentaclesSessionPayload(payload)) +export async function getAdminCloudtentaclesKnapsack( + payload = /** @type {AdminCloudtentaclesCatalogQueryInput} */ ({}) +) { + return getCloudtentaclesKnapsack( + resolveAdminCloudtentaclesSessionPayload(payload) + ); } /** @param {AdminCloudtentaclesVirtualNumberInput} [payload] */ -export async function listAdminCloudtentaclesVirtualNumbers(payload = /** @type {AdminCloudtentaclesVirtualNumberInput} */ ({})) { - const context = resolveAdminCloudtentaclesSessionPayload(payload) +export async function listAdminCloudtentaclesVirtualNumbers( + payload = /** @type {AdminCloudtentaclesVirtualNumberInput} */ ({}) +) { + const context = resolveAdminCloudtentaclesSessionPayload(payload); return listCloudtentaclesVirtualNumbers({ ...context, key: payload.key, - }) + }); } /** @param {AdminCloudtentaclesVirtualNumberInput} [payload] */ -export async function appointAdminCloudtentaclesVirtualNumber(payload = /** @type {AdminCloudtentaclesVirtualNumberInput} */ ({})) { - const context = resolveAdminCloudtentaclesSessionPayload(payload) +export async function appointAdminCloudtentaclesVirtualNumber( + payload = /** @type {AdminCloudtentaclesVirtualNumberInput} */ ({}) +) { + const context = resolveAdminCloudtentaclesSessionPayload(payload); return appointCloudtentaclesVirtualNumber({ ...context, key: payload.key, - }) + }); } /** @param {AdminCloudtentaclesVirtualNumberInput} [payload] */ -export async function generateAdminCloudtentaclesLoginCode(payload = /** @type {AdminCloudtentaclesVirtualNumberInput} */ ({})) { - const context = resolveAdminCloudtentaclesSessionPayload(payload) +export async function generateAdminCloudtentaclesLoginCode( + payload = /** @type {AdminCloudtentaclesVirtualNumberInput} */ ({}) +) { + const context = resolveAdminCloudtentaclesSessionPayload(payload); return generateCloudtentaclesLoginCode({ ...context, key: payload.key, id: payload.id, - }) + }); } /** @param {AdminCloudtentaclesVirtualNumberInput} [payload] */ -export async function fetchAdminCloudtentaclesVirtualNumberCode(payload = /** @type {AdminCloudtentaclesVirtualNumberInput} */ ({})) { - const context = resolveAdminCloudtentaclesSessionPayload(payload) +export async function fetchAdminCloudtentaclesVirtualNumberCode( + payload = /** @type {AdminCloudtentaclesVirtualNumberInput} */ ({}) +) { + const context = resolveAdminCloudtentaclesSessionPayload(payload); return fetchCloudtentaclesVirtualNumberCode({ ...context, key: payload.key, phone: payload.phone, - }) + }); } /** @param {AdminCloudtentaclesVirtualNumberInput} [payload] */ -export async function verifyAdminCloudtentaclesLoginCode(payload = /** @type {AdminCloudtentaclesVirtualNumberInput} */ ({})) { - const context = resolveAdminCloudtentaclesSessionPayload(payload) +export async function verifyAdminCloudtentaclesLoginCode( + payload = /** @type {AdminCloudtentaclesVirtualNumberInput} */ ({}) +) { + const context = resolveAdminCloudtentaclesSessionPayload(payload); return verifyCloudtentaclesLoginCode({ ...context, key: payload.key, id: payload.id, code: payload.code, - }) + }); } /** @param {AdminCloudtentaclesVirtualNumberInput} [payload] */ -export async function getAdminCloudtentaclesBindUrl(payload = /** @type {AdminCloudtentaclesVirtualNumberInput} */ ({})) { - const context = resolveAdminCloudtentaclesSessionPayload(payload) +export async function getAdminCloudtentaclesBindUrl( + payload = /** @type {AdminCloudtentaclesVirtualNumberInput} */ ({}) +) { + const context = resolveAdminCloudtentaclesSessionPayload(payload); return getCloudtentaclesBindUrl({ ...context, key: payload.key, id: payload.id, - }) + }); } /** @param {AdminCloudtentaclesVirtualNumberInput} [payload] */ -export async function backAdminCloudtentaclesVirtualNumber(payload = /** @type {AdminCloudtentaclesVirtualNumberInput} */ ({})) { - const context = resolveAdminCloudtentaclesSessionPayload(payload) +export async function backAdminCloudtentaclesVirtualNumber( + payload = /** @type {AdminCloudtentaclesVirtualNumberInput} */ ({}) +) { + const context = resolveAdminCloudtentaclesSessionPayload(payload); return backCloudtentaclesVirtualNumber({ ...context, key: payload.key, id: payload.id, - }) + }); } /** @param {AdminCloudtentaclesFullFlowInput} [payload] */ -export async function runAdminCloudtentaclesFullFlow(payload = /** @type {AdminCloudtentaclesFullFlowInput} */ ({})) { - const context = resolveAdminCloudtentaclesSessionPayload(payload) +export async function runAdminCloudtentaclesFullFlow( + payload = /** @type {AdminCloudtentaclesFullFlowInput} */ ({}) +) { + const context = resolveAdminCloudtentaclesSessionPayload(payload); return runCloudtentaclesFullDebugFlow({ ...context, skuId: payload.skuId, skuCount: payload.skuCount, vnKey: payload.vnKey, - }) + }); } diff --git a/apps/backend/src/services/admin/platform-config/cloudtentacles.js b/apps/backend/src/services/admin/platform-config/cloudtentacles.js index 5f085bd6..da2a26bf 100644 --- a/apps/backend/src/services/admin/platform-config/cloudtentacles.js +++ b/apps/backend/src/services/admin/platform-config/cloudtentacles.js @@ -1,104 +1,139 @@ // @ts-check -import { getCloudtentaclesSourceConfig } from '../../platforms/cloudtentacles/source-config-service.js' -import { getCloudtentaclesSessionState } from '../../platforms/cloudtentacles/session-state-service.js' -import { pickFirstNonEmpty, resolveCloudtentaclesAdminContext } from './context.js' -import { maskPhone, maskSecret } from './mappers.js' +import { getCloudtentaclesSourceByKey } from "../../platforms/cloudtentacles/source-config-service.js"; +import { getCloudtentaclesSessionStateByKey } from "../../platforms/cloudtentacles/session-state-service.js"; +import { + pickFirstNonEmpty, + resolveCloudtentaclesAdminContext, +} from "./context.js"; +import { maskPhone, maskSecret } from "./mappers.js"; -const DEFAULT_CLOUDTENTACLES_BASE_URL = 'https://123.207.217.176' +const DEFAULT_CLOUDTENTACLES_BASE_URL = "https://123.207.217.176"; + +function _resolveSourceKey(payload = {}) { + return String(payload.sourceKey || "").trim() || "default"; +} export function resolveAdminCloudtentaclesCredentialPayload( payload = {}, - options = {}, + options = {} ) { + const sourceKey = _resolveSourceKey(payload); const savedSource = /** @type {Record} */ ( - options.savedSource || getCloudtentaclesSourceConfig() - ) - const defaultBaseUrl = String(options.defaultBaseUrl || DEFAULT_CLOUDTENTACLES_BASE_URL).trim() + options.savedSource || getCloudtentaclesSourceByKey(sourceKey) || {} + ); + const defaultBaseUrl = String( + options.defaultBaseUrl || DEFAULT_CLOUDTENTACLES_BASE_URL + ).trim(); return { - baseUrl: pickFirstNonEmpty([payload.baseUrl, savedSource.baseUrl, defaultBaseUrl]), + sourceKey, + baseUrl: pickFirstNonEmpty([ + payload.baseUrl, + savedSource.baseUrl, + defaultBaseUrl, + ]), username: pickFirstNonEmpty([payload.username, savedSource.username]), password: pickFirstNonEmpty([payload.password, savedSource.password]), phone: pickFirstNonEmpty([payload.phone, savedSource.phone]), - deviceId: pickFirstNonEmpty([payload.deviceId, savedSource.deviceId, '-']), + deviceId: pickFirstNonEmpty([payload.deviceId, savedSource.deviceId, "-"]), deviceType: payload.deviceType ?? savedSource.deviceType, - } + }; } export function resolveAdminCloudtentaclesSessionPayload( payload = {}, - options = {}, + options = {} ) { + const sourceKey = _resolveSourceKey(payload); const savedSource = /** @type {Record} */ ( - options.savedSource || getCloudtentaclesSourceConfig() - ) + options.savedSource || getCloudtentaclesSourceByKey(sourceKey) || {} + ); const persistedSession = /** @type {Record} */ ( - options.persistedSession || getCloudtentaclesSessionState() - ) + options.persistedSession || + getCloudtentaclesSessionStateByKey(sourceKey) || + {} + ); return resolveCloudtentaclesAdminContext(payload, { savedSource, persistedSession, + sourceKey, defaultBaseUrl: options.defaultBaseUrl || DEFAULT_CLOUDTENTACLES_BASE_URL, - }) + }); } export function buildAdminCloudtentaclesPersistedSessionPayload( session = {}, - options = {}, + options = {} ) { return { - token: String(session.token || '').trim(), - baseUrl: String(session.baseUrl || '').trim(), + token: String(session.token || "").trim(), + baseUrl: String(session.baseUrl || "").trim(), username: pickFirstNonEmpty([options.username, session.username]), phone: pickFirstNonEmpty([options.phone, session.phone]), - loggedInAt: String(session.loggedInAt || '').trim(), - deviceId: pickFirstNonEmpty([options.deviceId, session.deviceId, '-']), + loggedInAt: String(session.loggedInAt || "").trim(), + deviceId: pickFirstNonEmpty([options.deviceId, session.deviceId, "-"]), deviceType: options.deviceType ?? session.deviceType, - } + }; } export function buildAdminCloudtentaclesSessionSummary( session = {}, savedSession = {}, + sourceKey = "default" ) { - const permissions = Array.isArray(session.permissions) ? session.permissions : [] + const permissions = Array.isArray(session.permissions) + ? session.permissions + : []; return { + sourceKey, tokenMasked: maskSecret(session.token), permissionCount: permissions.length, permissions, persisted: Boolean(savedSession.token), - } + }; } export function buildAdminCloudtentaclesLoginResult( session = {}, savedSession = {}, + sourceKey = "default" ) { return { - baseUrl: String(session.baseUrl || '').trim(), - username: String(session.username || '').trim(), + sourceKey, + baseUrl: String(session.baseUrl || "").trim(), + username: String(session.username || "").trim(), phoneMasked: maskPhone(session.phone), - loggedInAt: String(session.loggedInAt || '').trim(), - responseMessage: String(session.responseMessage || '').trim(), - token: String(session.token || '').trim(), - session: buildAdminCloudtentaclesSessionSummary(session, savedSession), + loggedInAt: String(session.loggedInAt || "").trim(), + responseMessage: String(session.responseMessage || "").trim(), + token: String(session.token || "").trim(), + session: buildAdminCloudtentaclesSessionSummary( + session, + savedSession, + sourceKey + ), userInfo: session.userInfo, asset: session.asset, - } + }; } export function buildAdminCloudtentaclesValidateResult( session = {}, savedSession = {}, + sourceKey = "default" ) { return { - baseUrl: String(session.baseUrl || '').trim(), - loggedInAt: String(session.loggedInAt || '').trim(), - session: buildAdminCloudtentaclesSessionSummary(session, savedSession), + sourceKey, + baseUrl: String(session.baseUrl || "").trim(), + loggedInAt: String(session.loggedInAt || "").trim(), + session: buildAdminCloudtentaclesSessionSummary( + session, + savedSession, + sourceKey + ), userInfo: session.userInfo, asset: session.asset, - } + }; } diff --git a/apps/backend/src/services/admin/platform-config/context.js b/apps/backend/src/services/admin/platform-config/context.js index 4e00cccc..96e73591 100644 --- a/apps/backend/src/services/admin/platform-config/context.js +++ b/apps/backend/src/services/admin/platform-config/context.js @@ -2,67 +2,104 @@ export function pickFirstNonEmpty(values) { for (const value of values) { - const normalized = String(value || '').trim() + const normalized = String(value || "").trim(); if (normalized) { - return normalized + return normalized; } } - return '' + return ""; } export function isPlainObject(value) { - return Boolean(value) && typeof value === 'object' && !Array.isArray(value) + return Boolean(value) && typeof value === "object" && !Array.isArray(value); } -export function resolveKuaishouEticketAdminContext( - payload = {}, - options = {}, -) { - const savedSource = /** @type {Record} */ (options.savedSource || {}) - const findShopConfig = /** @type {(shopId: string, source: Record) => unknown} */ ( - options.findShopConfig || (() => null) - ) - const getFirstAvailableShop = /** @type {(source: Record) => unknown} */ ( - options.getFirstAvailableShop || (() => null) - ) - const defaultBaseUrl = String(options.defaultBaseUrl || 'https://s.kwaixiaodian.com').trim() - const requestedShopId = String(payload.shopId || '').trim() +export function resolveKuaishouEticketAdminContext(payload = {}, options = {}) { + const savedSource = /** @type {Record} */ ( + options.savedSource || {} + ); + const findShopConfig = + /** @type {(shopId: string, source: Record) => unknown} */ ( + options.findShopConfig || (() => null) + ); + const getFirstAvailableShop = + /** @type {(source: Record) => unknown} */ ( + options.getFirstAvailableShop || (() => null) + ); + const defaultBaseUrl = String( + options.defaultBaseUrl || "https://s.kwaixiaodian.com" + ).trim(); + const requestedShopId = String(payload.shopId || "").trim(); const configuredShop = /** @type {Record | null} */ ( requestedShopId ? findShopConfig(requestedShopId, savedSource) : getFirstAvailableShop(savedSource) - ) + ); return { - baseUrl: pickFirstNonEmpty([payload.baseUrl, savedSource.baseUrl, defaultBaseUrl]), + baseUrl: pickFirstNonEmpty([ + payload.baseUrl, + savedSource.baseUrl, + defaultBaseUrl, + ]), cookie: pickFirstNonEmpty([payload.cookie, configuredShop?.cookie]), shopId: pickFirstNonEmpty([requestedShopId, configuredShop?.shopId]), shop: configuredShop, - } + }; } -export function resolveCloudtentaclesAdminContext( - payload = {}, - options = {}, -) { - const savedSource = /** @type {Record} */ (options.savedSource || {}) - const persistedSession = /** @type {Record} */ (options.persistedSession || {}) - const defaultBaseUrl = String(options.defaultBaseUrl || 'https://123.207.217.176').trim() +import { getCloudtentaclesSourceByKey } from "../../platforms/cloudtentacles/source-config-service.js"; +import { getCloudtentaclesSessionStateByKey } from "../../platforms/cloudtentacles/session-state-service.js"; + +export function resolveCloudtentaclesAdminContext(payload = {}, options = {}) { + const sourceKey = String( + payload.sourceKey || options.sourceKey || "default" + ).trim(); + const savedSource = /** @type {Record} */ ( + options.savedSource || getCloudtentaclesSourceByKey(sourceKey) || {} + ); + const persistedSession = /** @type {Record} */ ( + options.persistedSession || + getCloudtentaclesSessionStateByKey(sourceKey) || + {} + ); + const defaultBaseUrl = String( + options.defaultBaseUrl || "https://123.207.217.176" + ).trim(); return { - baseUrl: pickFirstNonEmpty([payload.baseUrl, savedSource.baseUrl, defaultBaseUrl]), + sourceKey, + baseUrl: pickFirstNonEmpty([ + payload.baseUrl, + savedSource.baseUrl, + defaultBaseUrl, + ]), token: pickFirstNonEmpty([payload.token, persistedSession.token]), - deviceId: pickFirstNonEmpty([payload.deviceId, savedSource.deviceId, persistedSession.deviceId, '-']), - deviceType: payload.deviceType ?? savedSource.deviceType ?? persistedSession.deviceType, - } + deviceId: pickFirstNonEmpty([ + payload.deviceId, + savedSource.deviceId, + persistedSession.deviceId, + "-", + ]), + deviceType: + payload.deviceType ?? + savedSource.deviceType ?? + persistedSession.deviceType, + }; } -export function hasCloudtentaclesCredentialContextChanged(current = {}, next = {}) { +export function hasCloudtentaclesCredentialContextChanged( + current = {}, + next = {} +) { return ( - String(current.baseUrl || '').trim() !== String(next.baseUrl || '').trim() - || String(current.username || '').trim() !== String(next.username || '').trim() - || String(current.phone || '').trim() !== String(next.phone || '').trim() - || String(current.deviceId || '').trim() !== String(next.deviceId || '').trim() - || Number(current.deviceType || 0) !== Number(next.deviceType || 0) - ) + String(current.baseUrl || "").trim() !== + String(next.baseUrl || "").trim() || + String(current.username || "").trim() !== + String(next.username || "").trim() || + String(current.phone || "").trim() !== String(next.phone || "").trim() || + String(current.deviceId || "").trim() !== + String(next.deviceId || "").trim() || + Number(current.deviceType || 0) !== Number(next.deviceType || 0) + ); } diff --git a/apps/backend/src/services/admin/platform-config/mappers.js b/apps/backend/src/services/admin/platform-config/mappers.js index dd7bedfb..10adc64d 100644 --- a/apps/backend/src/services/admin/platform-config/mappers.js +++ b/apps/backend/src/services/admin/platform-config/mappers.js @@ -1,110 +1,113 @@ // @ts-check export function maskSecret(value) { - const normalized = String(value || '').trim() + const normalized = String(value || "").trim(); if (!normalized) { - return '' + return ""; } if (normalized.length <= 10) { - return `${normalized.slice(0, 2)}****${normalized.slice(-2)}` + return `${normalized.slice(0, 2)}****${normalized.slice(-2)}`; } - return `${normalized.slice(0, 6)}****${normalized.slice(-6)}` + return `${normalized.slice(0, 6)}****${normalized.slice(-6)}`; } export function maskPhone(value) { - const normalized = String(value || '').trim() + const normalized = String(value || "").trim(); if (!normalized) { - return '' + return ""; } if (normalized.length < 7) { - return normalized + return normalized; } - return `${normalized.slice(0, 3)}****${normalized.slice(-4)}` + return `${normalized.slice(0, 3)}****${normalized.slice(-4)}`; } export function mapAdminKuaishouEticketShopItem(item = {}) { return { - shopId: String(item.shopId || '').trim(), - kshopName: String(item.kshopName || '').trim(), - cookie: String(item.cookie || '').trim(), + shopId: String(item.shopId || "").trim(), + kshopName: String(item.kshopName || "").trim(), + cookie: String(item.cookie || "").trim(), cookieMasked: maskSecret(item.cookie), - hasCookie: Boolean(String(item.cookie || '').trim()), - userAvatar: String(item.userAvatar || '').trim(), + hasCookie: Boolean(String(item.cookie || "").trim()), + userAvatar: String(item.userAvatar || "").trim(), enabled: item.enabled !== false, - } + }; } export function mapAdminKuaishouEticketSourceConfig(config = {}, shops = []) { return { enabled: config.enabled !== false, - baseUrl: String(config.baseUrl || '').trim(), + baseUrl: String(config.baseUrl || "").trim(), shops: shops.map((item) => mapAdminKuaishouEticketShopItem(item)), - } + }; } export function mapAdminCloudtentaclesSourceConfig(config = {}) { return { + key: String(config.key || "").trim(), + label: String(config.label || "").trim(), enabled: config.enabled !== false, - baseUrl: String(config.baseUrl || '').trim(), - username: String(config.username || '').trim(), - password: String(config.password || '').trim(), - phone: String(config.phone || '').trim(), - deviceId: String(config.deviceId || '-').trim() || '-', + baseUrl: String(config.baseUrl || "").trim(), + username: String(config.username || "").trim(), + password: String(config.password || "").trim(), + phone: String(config.phone || "").trim(), + deviceId: String(config.deviceId || "-").trim() || "-", deviceType: Number(config.deviceType || 0), - } + }; } export function mapAdminCloudtentaclesSession(session = {}) { return { - token: String(session.token || '').trim(), + token: String(session.token || "").trim(), tokenMasked: maskSecret(session.token), - baseUrl: String(session.baseUrl || '').trim(), - username: String(session.username || '').trim(), + baseUrl: String(session.baseUrl || "").trim(), + username: String(session.username || "").trim(), phoneMasked: maskPhone(session.phone), - loggedInAt: String(session.loggedInAt || '').trim(), - deviceId: String(session.deviceId || '-').trim() || '-', + loggedInAt: String(session.loggedInAt || "").trim(), + deviceId: String(session.deviceId || "-").trim() || "-", deviceType: Number(session.deviceType || 0), - hasToken: Boolean(String(session.token || '').trim()), - } + hasToken: Boolean(String(session.token || "").trim()), + }; } export function mapAdminKuaishouCloudFulfillmentItem(item = {}) { return { - id: String(item.id || '').trim(), + id: String(item.id || "").trim(), enabled: item.enabled !== false, priority: Number(item.priority || 100) || 100, - provider: String(item.provider || '91kaquan').trim() || '91kaquan', - platform: String(item.platform || 'kuaishou').trim() || 'kuaishou', - shopId: String(item.shopId || '').trim(), - internalSkuCode: String(item.internalSkuCode || '').trim(), - internalSkuName: String(item.internalSkuName || '').trim(), - externalSkuCode: String(item.externalSkuCode || '').trim(), - externalItemId: String(item.externalItemId || '').trim(), - externalSkuName: String(item.externalSkuName || '').trim(), - resolvedSkuName: String(item.resolvedSkuName || '').trim(), - cloudSourceKey: String(item.cloudSourceKey || 'default').trim() || 'default', + provider: String(item.provider || "91kaquan").trim() || "91kaquan", + platform: String(item.platform || "kuaishou").trim() || "kuaishou", + shopId: String(item.shopId || "").trim(), + internalSkuCode: String(item.internalSkuCode || "").trim(), + internalSkuName: String(item.internalSkuName || "").trim(), + externalSkuCode: String(item.externalSkuCode || "").trim(), + externalItemId: String(item.externalItemId || "").trim(), + externalSkuName: String(item.externalSkuName || "").trim(), + resolvedSkuName: String(item.resolvedSkuName || "").trim(), + cloudSourceKey: + String(item.cloudSourceKey || "default").trim() || "default", cloudSkuId: Number(item.cloudSkuId || 0) || 0, - cloudSkuName: String(item.cloudSkuName || '').trim(), - vnKey: String(item.vnKey || '').trim(), + cloudSkuName: String(item.cloudSkuName || "").trim(), + vnKey: String(item.vnKey || "").trim(), autoBuyEnabled: item.autoBuyEnabled !== false, minAssetReserve: Number(item.minAssetReserve || 0) || 0, autoReturnNumberAfterDispatch: item.autoReturnNumberAfterDispatch === true, autoConsumeAfterDispatch: item.autoConsumeAfterDispatch === true, - kuaishouConsumeShopId: String(item.kuaishouConsumeShopId || '').trim(), - kuaishouConsumeShopName: String(item.kuaishouConsumeShopName || '').trim(), - notes: String(item.notes || '').trim(), - } + kuaishouConsumeShopId: String(item.kuaishouConsumeShopId || "").trim(), + kuaishouConsumeShopName: String(item.kuaishouConsumeShopName || "").trim(), + notes: String(item.notes || "").trim(), + }; } export function mapAdminKuaishouCloudFulfillmentSource(config = {}) { return { enabled: config.enabled !== false, items: (Array.isArray(config.items) ? config.items : []).map((item) => - mapAdminKuaishouCloudFulfillmentItem(item), + mapAdminKuaishouCloudFulfillmentItem(item) ), - } + }; } diff --git a/apps/backend/src/services/admin/platform-config/service.js b/apps/backend/src/services/admin/platform-config/service.js index 4e5d12fe..273c4f35 100644 --- a/apps/backend/src/services/admin/platform-config/service.js +++ b/apps/backend/src/services/admin/platform-config/service.js @@ -1,13 +1,13 @@ export { getAdminAgisoShopConfigs, updateAdminAgisoShopConfigs, -} from './agiso-service.js' +} from "./agiso-service.js"; export { getAdminNinetyoneOrders, retryAdminNinetyoneOrder, failAdminNinetyoneOrder, -} from './ninetyone-service.js' +} from "./ninetyone-service.js"; export { getAdminKuaishouEticketSourceConfig, @@ -15,10 +15,11 @@ export { queryAdminKuaishouEticketDetail, queryAdminKuaishouEticketShopInfo, consumeAdminKuaishouEticket, -} from './kuaishou-eticket-service.js' +} from "./kuaishou-eticket-service.js"; export { - getAdminCloudtentaclesSourceConfig, + listAdminCloudtentaclesSources, + deleteAdminCloudtentaclesSource, updateAdminCloudtentaclesSourceConfig, sendAdminCloudtentaclesSmsCode, testAdminCloudtentaclesLogin, @@ -37,18 +38,18 @@ export { getAdminCloudtentaclesBindUrl, backAdminCloudtentaclesVirtualNumber, runAdminCloudtentaclesFullFlow, -} from './cloudtentacles-service.js' +} from "./cloudtentacles-service.js"; export { getAdminFulfillmentBindingConfigs, lookupAdminFulfillmentBindingOrder, updateAdminFulfillmentBindingConfigs, -} from './fulfillment-bindings-service.js' +} from "./fulfillment-bindings-service.js"; export { getAdminKuaishouCloudFulfillmentConfig, updateAdminKuaishouCloudFulfillmentConfig, -} from './kuaishou-cloud-fulfillment-service.js' +} from "./kuaishou-cloud-fulfillment-service.js"; export { getAdminNotificationConfig, @@ -57,4 +58,4 @@ export { getAdminScheduledJobsConfig, updateAdminScheduledJobsConfig, runAdminScheduledJobNow, -} from './notification-service.js' +} from "./notification-service.js"; diff --git a/apps/backend/src/services/admin/platform-config/writes.js b/apps/backend/src/services/admin/platform-config/writes.js index ded6a60f..bb8782c3 100644 --- a/apps/backend/src/services/admin/platform-config/writes.js +++ b/apps/backend/src/services/admin/platform-config/writes.js @@ -1,101 +1,120 @@ // @ts-check -import { applyOptionalStringField } from './domain.js' +import { applyOptionalStringField } from "./domain.js"; export function buildAdminAgisoShopConfigUpdate(item, current = {}) { - const shopId = String(item?.shopId || '').trim() + const shopId = String(item?.shopId || "").trim(); if (!shopId) { - return null + return null; } - const next = /** @type {Record} */ ({ ...current }) - const shopName = String(item?.shopName || '').trim() - const accessToken = String(item?.accessToken || '').trim() - const messageTemplate = String(item?.messageTemplate || '').trim() - const autoDeliveryMessageTemplate = String(item?.autoDeliveryMessageTemplate || '').trim() - const appSecret = String(item?.appSecret || '').trim() - const apiVersion = String(item?.apiVersion || '').trim() - const sendMessageEndpoint = String(item?.sendMessageEndpoint || '').trim() + const next = /** @type {Record} */ ({ ...current }); + const shopName = String(item?.shopName || "").trim(); + const accessToken = String(item?.accessToken || "").trim(); + const messageTemplate = String(item?.messageTemplate || "").trim(); + const autoDeliveryMessageTemplate = String( + item?.autoDeliveryMessageTemplate || "" + ).trim(); + const appSecret = String(item?.appSecret || "").trim(); + const apiVersion = String(item?.apiVersion || "").trim(); + const sendMessageEndpoint = String(item?.sendMessageEndpoint || "").trim(); if (shopName) { - next.shopName = shopName + next.shopName = shopName; } else { - delete next.shopName + delete next.shopName; } if (accessToken) { - next.accessToken = accessToken + next.accessToken = accessToken; } if (messageTemplate) { - next.messageTemplate = messageTemplate - } else if (typeof item?.messageTemplate === 'string') { - delete next.messageTemplate + next.messageTemplate = messageTemplate; + } else if (typeof item?.messageTemplate === "string") { + delete next.messageTemplate; } if (autoDeliveryMessageTemplate) { - next.autoDeliveryMessageTemplate = autoDeliveryMessageTemplate - } else if (typeof item?.autoDeliveryMessageTemplate === 'string') { - delete next.autoDeliveryMessageTemplate + next.autoDeliveryMessageTemplate = autoDeliveryMessageTemplate; + } else if (typeof item?.autoDeliveryMessageTemplate === "string") { + delete next.autoDeliveryMessageTemplate; } if (appSecret) { - next.appSecret = appSecret + next.appSecret = appSecret; } if (apiVersion) { - next.apiVersion = apiVersion + next.apiVersion = apiVersion; } if (sendMessageEndpoint) { - next.sendMessageEndpoint = sendMessageEndpoint + next.sendMessageEndpoint = sendMessageEndpoint; } - if (typeof item?.enabled === 'boolean') { - next.enabled = item.enabled + if (typeof item?.enabled === "boolean") { + next.enabled = item.enabled; } if (!next.accessToken) { - return null + return null; } - return { shopId, config: next } + return { shopId, config: next }; } export function buildAdminAgisoMessagingSavePayload( payload = {}, - options = {}, + options = {} ) { - const rawItems = Array.isArray(payload.shops) ? payload.shops : [] - const currentDefaults = /** @type {Record} */ (options.currentDefaults || {}) - const currentMap = /** @type {Record>} */ (options.currentMap || {}) - const nextDefaults = /** @type {Record} */ ({ ...currentDefaults }) - const nextMap = /** @type {Record>} */ ({}) + const rawItems = Array.isArray(payload.shops) ? payload.shops : []; + const currentDefaults = /** @type {Record} */ ( + options.currentDefaults || {} + ); + const currentMap = /** @type {Record>} */ ( + options.currentMap || {} + ); + const nextDefaults = /** @type {Record} */ ({ + ...currentDefaults, + }); + const nextMap = /** @type {Record>} */ ({}); - applyOptionalStringField(nextDefaults, 'messageTemplate', payload.defaults) - applyOptionalStringField(nextDefaults, 'autoDeliveryMessageTemplate', payload.defaults) + applyOptionalStringField(nextDefaults, "messageTemplate", payload.defaults); + applyOptionalStringField( + nextDefaults, + "autoDeliveryMessageTemplate", + payload.defaults + ); for (const item of rawItems) { - const shopId = String(item?.shopId || '').trim() + const shopId = String(item?.shopId || "").trim(); if (!shopId) { - continue + continue; } - const updated = buildAdminAgisoShopConfigUpdate(item, currentMap[shopId] || {}) + const updated = buildAdminAgisoShopConfigUpdate( + item, + currentMap[shopId] || {} + ); if (!updated) { - continue + continue; } - nextMap[updated.shopId] = updated.config + nextMap[updated.shopId] = updated.config; } return { defaults: nextDefaults, shops: nextMap, - } + }; } export function normalizeAdminCloudtentaclesSourceConfigPayload(payload = {}) { + 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() || '-', + 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/types/admin-route-inputs.js b/apps/backend/src/types/admin-route-inputs.js index d2fcbfb9..86f495be 100644 --- a/apps/backend/src/types/admin-route-inputs.js +++ b/apps/backend/src/types/admin-route-inputs.js @@ -1,6 +1,6 @@ // @ts-check -export {} +export {}; /** * @typedef {import('./admin-read-inputs.js').AdminInventoryListQueryInput} AdminInventoryRouteQuery @@ -161,3 +161,7 @@ export {} /** * @typedef {{ id?: string }} AdminEntityRouteParams */ + +/** + * @typedef {{ sourceKey?: string }} AdminCloudtentaclesSourceRouteParams + */ diff --git a/apps/backend/src/types/admin-write-inputs.js b/apps/backend/src/types/admin-write-inputs.js index c63bd9d0..12d48f77 100644 --- a/apps/backend/src/types/admin-write-inputs.js +++ b/apps/backend/src/types/admin-write-inputs.js @@ -1,6 +1,6 @@ // @ts-check -export {} +export {}; /** * @typedef {{ @@ -172,7 +172,7 @@ export {} /** * @typedef {{ - * baseUrl?: string + * baseUrl?: string * shopId?: string * cookie?: string * }} AdminKuaishouEticketShopInfoInput @@ -189,7 +189,7 @@ export {} /** * @typedef {{ - * baseUrl?: string + * baseUrl?: string * shopId?: string * cookie?: string * eTicketId?: string @@ -202,6 +202,7 @@ export {} /** * @typedef {{ + * sourceKey?: string * enabled?: boolean * baseUrl?: string * username?: string @@ -209,11 +210,13 @@ export {} * phone?: string * deviceId?: string * deviceType?: number | string + * sources?: AdminCloudtentaclesSourceConfigInput[] * }} AdminCloudtentaclesSourceConfigInput */ /** * @typedef {{ + * sourceKey?: string * baseUrl?: string * username?: string * phone?: string @@ -224,6 +227,7 @@ export {} /** * @typedef {{ + * sourceKey?: string * baseUrl?: string * username?: string * password?: string @@ -236,6 +240,7 @@ export {} /** * @typedef {{ + * sourceKey?: string * baseUrl?: string * token?: string * deviceId?: string @@ -245,6 +250,7 @@ export {} /** * @typedef {{ + * sourceKey?: string * baseUrl?: string * token?: string * deviceId?: string @@ -254,6 +260,7 @@ export {} /** * @typedef {{ + * sourceKey?: string * baseUrl?: string * token?: string * deviceId?: string @@ -265,6 +272,7 @@ export {} /** * @typedef {{ + * sourceKey?: string * baseUrl?: string * token?: string * deviceId?: string @@ -277,6 +285,7 @@ export {} /** * @typedef {{ + * sourceKey?: string * baseUrl?: string * token?: string * deviceId?: string @@ -290,6 +299,7 @@ export {} /** * @typedef {{ + * sourceKey?: string * baseUrl?: string * token?: string * deviceId?: string