From 38b0bf8ba555be5025e1f9ef21a651e9bbb2435b Mon Sep 17 00:00:00 2001 From: yml Date: Thu, 21 May 2026 16:04:09 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=8E=E7=AB=AF=E8=BF=81=E7=A7=BB=E5=B9=B3?= =?UTF-8?q?=E5=8F=B0=E9=85=8D=E7=BD=AE=E5=BA=95=E5=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{context.js => context.ts} | 46 +++++++------------ .../platform-config/{domain.js => domain.ts} | 18 ++++---- .../{mappers.js => mappers.ts} | 18 ++++---- .../{validation.js => validation.ts} | 20 ++++---- docs/backend-typescript-migration-plan.md | 11 +++++ 5 files changed, 56 insertions(+), 57 deletions(-) rename apps/backend/src/services/admin/platform-config/{context.js => context.ts} (65%) rename apps/backend/src/services/admin/platform-config/{domain.js => domain.ts} (86%) rename apps/backend/src/services/admin/platform-config/{mappers.js => mappers.ts} (85%) rename apps/backend/src/services/admin/platform-config/{validation.js => validation.ts} (91%) diff --git a/apps/backend/src/services/admin/platform-config/context.js b/apps/backend/src/services/admin/platform-config/context.ts similarity index 65% rename from apps/backend/src/services/admin/platform-config/context.js rename to apps/backend/src/services/admin/platform-config/context.ts index 96e73591..43677f5d 100644 --- a/apps/backend/src/services/admin/platform-config/context.js +++ b/apps/backend/src/services/admin/platform-config/context.ts @@ -1,6 +1,6 @@ -// @ts-check +type JsonObject = Record -export function pickFirstNonEmpty(values) { +export function pickFirstNonEmpty(values: unknown[]) { for (const value of values) { const normalized = String(value || "").trim(); if (normalized) { @@ -11,31 +11,23 @@ export function pickFirstNonEmpty(values) { return ""; } -export function isPlainObject(value) { +export function isPlainObject(value: unknown): value is JsonObject { return Boolean(value) && typeof value === "object" && !Array.isArray(value); } -export function resolveKuaishouEticketAdminContext(payload = {}, options = {}) { - const savedSource = /** @type {Record} */ ( - options.savedSource || {} - ); +export function resolveKuaishouEticketAdminContext(payload: JsonObject = {}, options: JsonObject = {}) { + const savedSource = options.savedSource || {}; const findShopConfig = - /** @type {(shopId: string, source: Record) => unknown} */ ( - options.findShopConfig || (() => null) - ); + (options.findShopConfig || (() => null)) as (shopId: string, source: JsonObject) => unknown; const getFirstAvailableShop = - /** @type {(source: Record) => unknown} */ ( - options.getFirstAvailableShop || (() => null) - ); + (options.getFirstAvailableShop || (() => null)) as (source: JsonObject) => unknown; 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) - ); + const configuredShop = (requestedShopId + ? findShopConfig(requestedShopId, savedSource) + : getFirstAvailableShop(savedSource)) as JsonObject | null; return { baseUrl: pickFirstNonEmpty([ @@ -52,18 +44,14 @@ export function resolveKuaishouEticketAdminContext(payload = {}, options = {}) { import { getCloudtentaclesSourceByKey } from "../../platforms/cloudtentacles/source-config-service.js"; import { getCloudtentaclesSessionStateByKey } from "../../platforms/cloudtentacles/session-state-service.js"; -export function resolveCloudtentaclesAdminContext(payload = {}, options = {}) { +export function resolveCloudtentaclesAdminContext(payload: JsonObject = {}, options: JsonObject = {}) { 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 savedSource = options.savedSource || getCloudtentaclesSourceByKey(sourceKey) || {}; + const persistedSession = options.persistedSession || + getCloudtentaclesSessionStateByKey(sourceKey) || + {}; const defaultBaseUrl = String( options.defaultBaseUrl || "https://123.207.217.176" ).trim(); @@ -89,8 +77,8 @@ export function resolveCloudtentaclesAdminContext(payload = {}, options = {}) { } export function hasCloudtentaclesCredentialContextChanged( - current = {}, - next = {} + current: JsonObject = {}, + next: JsonObject = {} ) { return ( String(current.baseUrl || "").trim() !== diff --git a/apps/backend/src/services/admin/platform-config/domain.js b/apps/backend/src/services/admin/platform-config/domain.ts similarity index 86% rename from apps/backend/src/services/admin/platform-config/domain.js rename to apps/backend/src/services/admin/platform-config/domain.ts index ce3f7bb0..303ca50d 100644 --- a/apps/backend/src/services/admin/platform-config/domain.js +++ b/apps/backend/src/services/admin/platform-config/domain.ts @@ -1,9 +1,9 @@ -// @ts-check - import { normalizeAgisoMessageTemplate } from '../../platforms/agiso/xianyu/message-service.js' import { maskSecret } from './mappers.js' -export function mapAdminAgisoMessagingDefaults(defaults = {}) { +type JsonObject = Record + +export function mapAdminAgisoMessagingDefaults(defaults: JsonObject = {}) { return { messageTemplate: normalizeAgisoMessageTemplate(String(defaults.messageTemplate || '').trim()), autoDeliveryMessageTemplate: normalizeAgisoMessageTemplate( @@ -12,7 +12,7 @@ export function mapAdminAgisoMessagingDefaults(defaults = {}) { } } -export function mapAdminAgisoShopConfigItem(shopId, config = {}) { +export function mapAdminAgisoShopConfigItem(shopId: unknown, config: JsonObject = {}) { return { shopId, shopName: String(config.shopName || '').trim(), @@ -29,7 +29,7 @@ export function mapAdminAgisoShopConfigItem(shopId, config = {}) { } } -export function applyOptionalStringField(target, key, source) { +export function applyOptionalStringField(target: JsonObject, key: string, source: JsonObject) { if (!source || typeof source[key] !== 'string') { return } @@ -43,7 +43,7 @@ export function applyOptionalStringField(target, key, source) { delete target[key] } -export function mapAdminFulfillmentBindingConfigItem(item) { +export function mapAdminFulfillmentBindingConfigItem(item: JsonObject) { const match = item?.match || {} return { provider: String(item?.provider || '').trim(), @@ -65,7 +65,7 @@ export function mapAdminFulfillmentBindingConfigItem(item) { } } -export function matchesObservedProduct(binding, observed) { +export function matchesObservedProduct(binding: JsonObject, observed: JsonObject) { const provider = String(binding?.provider || '').trim() const platform = String(binding?.platform || '').trim() const shopId = String(binding?.shopId || '').trim() @@ -93,11 +93,11 @@ export function matchesObservedProduct(binding, observed) { ) } -export function findMatchingObservedBinding(bindings, observed) { +export function findMatchingObservedBinding(bindings: JsonObject[], observed: JsonObject) { return (Array.isArray(bindings) ? bindings : []).find((binding) => matchesObservedProduct(binding, observed)) || null } -export function mapAdminObservedProductItem(item, bindings = []) { +export function mapAdminObservedProductItem(item: JsonObject, bindings: JsonObject[] = []) { const matchedBinding = findMatchingObservedBinding(bindings, item) return { diff --git a/apps/backend/src/services/admin/platform-config/mappers.js b/apps/backend/src/services/admin/platform-config/mappers.ts similarity index 85% rename from apps/backend/src/services/admin/platform-config/mappers.js rename to apps/backend/src/services/admin/platform-config/mappers.ts index 10adc64d..d4fcda74 100644 --- a/apps/backend/src/services/admin/platform-config/mappers.js +++ b/apps/backend/src/services/admin/platform-config/mappers.ts @@ -1,6 +1,6 @@ -// @ts-check +type JsonObject = Record -export function maskSecret(value) { +export function maskSecret(value: unknown) { const normalized = String(value || "").trim(); if (!normalized) { return ""; @@ -13,7 +13,7 @@ export function maskSecret(value) { return `${normalized.slice(0, 6)}****${normalized.slice(-6)}`; } -export function maskPhone(value) { +export function maskPhone(value: unknown) { const normalized = String(value || "").trim(); if (!normalized) { return ""; @@ -26,7 +26,7 @@ export function maskPhone(value) { return `${normalized.slice(0, 3)}****${normalized.slice(-4)}`; } -export function mapAdminKuaishouEticketShopItem(item = {}) { +export function mapAdminKuaishouEticketShopItem(item: JsonObject = {}) { return { shopId: String(item.shopId || "").trim(), kshopName: String(item.kshopName || "").trim(), @@ -38,7 +38,7 @@ export function mapAdminKuaishouEticketShopItem(item = {}) { }; } -export function mapAdminKuaishouEticketSourceConfig(config = {}, shops = []) { +export function mapAdminKuaishouEticketSourceConfig(config: JsonObject = {}, shops: JsonObject[] = []) { return { enabled: config.enabled !== false, baseUrl: String(config.baseUrl || "").trim(), @@ -46,7 +46,7 @@ export function mapAdminKuaishouEticketSourceConfig(config = {}, shops = []) { }; } -export function mapAdminCloudtentaclesSourceConfig(config = {}) { +export function mapAdminCloudtentaclesSourceConfig(config: JsonObject = {}) { return { key: String(config.key || "").trim(), label: String(config.label || "").trim(), @@ -60,7 +60,7 @@ export function mapAdminCloudtentaclesSourceConfig(config = {}) { }; } -export function mapAdminCloudtentaclesSession(session = {}) { +export function mapAdminCloudtentaclesSession(session: JsonObject = {}) { return { token: String(session.token || "").trim(), tokenMasked: maskSecret(session.token), @@ -74,7 +74,7 @@ export function mapAdminCloudtentaclesSession(session = {}) { }; } -export function mapAdminKuaishouCloudFulfillmentItem(item = {}) { +export function mapAdminKuaishouCloudFulfillmentItem(item: JsonObject = {}) { return { id: String(item.id || "").trim(), enabled: item.enabled !== false, @@ -103,7 +103,7 @@ export function mapAdminKuaishouCloudFulfillmentItem(item = {}) { }; } -export function mapAdminKuaishouCloudFulfillmentSource(config = {}) { +export function mapAdminKuaishouCloudFulfillmentSource(config: JsonObject = {}) { return { enabled: config.enabled !== false, items: (Array.isArray(config.items) ? config.items : []).map((item) => diff --git a/apps/backend/src/services/admin/platform-config/validation.js b/apps/backend/src/services/admin/platform-config/validation.ts similarity index 91% rename from apps/backend/src/services/admin/platform-config/validation.js rename to apps/backend/src/services/admin/platform-config/validation.ts index aa495db3..49a51cb2 100644 --- a/apps/backend/src/services/admin/platform-config/validation.js +++ b/apps/backend/src/services/admin/platform-config/validation.ts @@ -1,10 +1,10 @@ -// @ts-check - import { createHttpError } from '../../../utils/http.js' import { normalizeProductName } from '../../order/product-match-service.js' import { isPlainObject } from './context.js' -export function assertAdminFulfillmentBindingsInput(bindings) { +type JsonObject = Record + +export function assertAdminFulfillmentBindingsInput(bindings: unknown) { if (Array.isArray(bindings)) { return } @@ -16,8 +16,8 @@ export function assertAdminFulfillmentBindingsInput(bindings) { } export function normalizeAdminFulfillmentBindingItem( - rawBinding, - options = {}, + rawBinding: unknown, + options: JsonObject = {}, ) { const index = Number(options.index || 0) if (!isPlainObject(rawBinding)) { @@ -87,16 +87,16 @@ export function buildAdminFulfillmentBindingUniqueKey(binding) { } export async function validateAdminFulfillmentBindingConfigs( - bindings, - options = {}, + bindings: JsonObject[], + options: JsonObject = {}, ) { assertAdminFulfillmentBindingsInput(bindings) - const getFulfillmentProfileByKey = /** @type {(profileKey: string) => Promise} */ ( + const getFulfillmentProfileByKey = ( options.getFulfillmentProfileByKey || (async () => null) - ) + ) as (profileKey: string) => Promise const seenKeys = new Set() - const normalizedBindings = [] + const normalizedBindings: JsonObject[] = [] for (const [index, rawBinding] of bindings.entries()) { const normalized = normalizeAdminFulfillmentBindingItem(rawBinding, { index }) diff --git a/docs/backend-typescript-migration-plan.md b/docs/backend-typescript-migration-plan.md index 77900880..49ebaebb 100644 --- a/docs/backend-typescript-migration-plan.md +++ b/docs/backend-typescript-migration-plan.md @@ -578,6 +578,17 @@ - `npm run typecheck` - `npm run build` - `npm test` 共 139 个用例通过 +135. 后台平台配置底座模块迁移到 `.ts`: + - `src/services/admin/platform-config/context.ts` + - `src/services/admin/platform-config/domain.ts` + - `src/services/admin/platform-config/mappers.ts` + - `src/services/admin/platform-config/validation.ts` +136. 后台平台配置上下文解析、展示映射、敏感信息脱敏、履约绑定校验与重复规则检测已进入 TS 编译链路;动态配置对象补齐通用类型,`isPlainObject` 改为类型守卫 +137. Docker 内验证通过: + - `src/services/admin/platform-config/context.test.js`、`domain.test.js`、`mappers.test.js`、`validation.test.js` 共 23 个用例通过 + - `npm run typecheck` + - `npm run build` + - `npm test` 共 139 个用例通过 ## 下一步建议