UNPKG

@pisell/pisellos

Version:

一个可扩展的前端模块化SDK框架,支持插件系统

1,187 lines (1,185 loc) 43.9 kB
var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/server/modules/products/index.ts var products_exports = {}; __export(products_exports, { ProductsModule: () => ProductsModule }); module.exports = __toCommonJS(products_exports); var import_BaseModule = require("../../../modules/BaseModule"); var import_plugins = require("../../../plugins"); var import_types = require("./types"); var import_product = require("../../utils/product"); var INDEXDB_STORE_NAME = "products"; var PRODUCT_SYNC_DEBOUNCE_MS = 1e4; var ProductsModule = class extends import_BaseModule.BaseModule { constructor(name, version) { super(name, version); this.defaultName = "products"; this.defaultVersion = "1.0.0"; // LoggerManager 实例 this.otherParams = {}; // 商品价格缓存:Map<日期, 应用了价格的商品列表> this.productsPriceCache = /* @__PURE__ */ new Map(); // 缓存配置 this.CACHE_MAX_DAYS = 7; // 最多缓存7天 // 商品格式化器列表 this.formatters = []; // 是否已注册内置价格格式化器 this.isPriceFormatterRegistered = false; this.pendingSyncMessages = []; } async initialize(core, options) { var _a; this.core = core; this.store = options.store; this.otherParams = options.otherParams || {}; if (Array.isArray((_a = options.initialState) == null ? void 0 : _a.list)) { this.store.list = options.initialState.list; this.syncProductsMap(); this.core.effects.emit(import_types.ProductsHooks.onProductsChanged, this.store.list); } else { this.store.list = []; this.store.map = /* @__PURE__ */ new Map(); } this.request = core.getPlugin("request"); const appPlugin = core.getPlugin("app"); if (appPlugin) { const app = appPlugin.getApp(); this.dbManager = app.dbManager; this.logger = app.logger; if (this.dbManager) { console.log("[Products] IndexDB Manager 已初始化"); } else { console.warn("[Products] IndexDB Manager 未找到"); } } this.registerBuiltinPriceFormatter(); this.initProductDataSource(); this.setupProductSync(); this.logInfo("模块初始化完成", { hasDbManager: !!this.dbManager, hasLogger: !!this.logger, initialProductCount: this.store.list.length }); } /** * 记录信息日志 * @param title 日志标题 * @param metadata 日志元数据 */ logInfo(title, metadata) { try { if (this.logger) { this.logger.addLog({ type: "info", title: `[ProductsModule] ${title}`, metadata: metadata || {} }); } } catch { } } /** * 记录警告日志 * @param title 日志标题 * @param metadata 日志元数据 */ logWarning(title, metadata) { try { if (this.logger) { this.logger.addLog({ type: "warning", title: `[ProductsModule] ${title}`, metadata: metadata || {} }); } } catch { } } /** * 记录错误日志 * @param title 日志标题 * @param metadata 日志元数据 */ logError(title, metadata) { try { if (this.logger) { this.logger.addLog({ type: "error", title: `[ProductsModule] ${title}`, metadata: metadata || {} }); } } catch { } } /** * 加载商品价格(原始方法,不带缓存) * @private */ async loadProductsPrice({ ids = [], customer_id, schedule_date }) { var _a; this.logInfo("开始加载商品价格", { productCount: ids.length, schedule_date, customer_id }); const startTime = Date.now(); try { const productsData = await this.request.post( `/product/query/price`, { ids, customer_id, schedule_date }, { cache: { mode: import_plugins.RequestModeENUM.REMOTE_LOCAL, type: "memory" } } ); const duration = Date.now() - startTime; this.logInfo("加载商品价格成功", { productCount: ids.length, priceDataCount: ((_a = productsData.data) == null ? void 0 : _a.length) ?? 0, duration: `${duration}ms` }); return productsData.data; } catch (error) { const duration = Date.now() - startTime; const errorMessage = error instanceof Error ? error.message : String(error); this.logError("加载商品价格失败", { productCount: ids.length, duration: `${duration}ms`, error: errorMessage }); return []; } } /** * 获取应用了价格的商品列表(带缓存) * 使用日期作为缓存 key,同一天的商品价格会被缓存 * 缓存的是已经应用了价格的完整商品列表,避免重复转换 * @param schedule_date 日期 * @param extraContext 额外的上下文数据(可选,由 Server 层传入) * @param options 可选参数 * @param options.changedIds 变更的商品 IDs,非空时仅对这些商品增量执行 prepare 并更新缓存 * @returns 应用了价格的商品列表 */ async getProductsWithPrice(schedule_date, extraContext, options) { const t0 = performance.now(); const cacheKey = schedule_date; const changedIds = options == null ? void 0 : options.changedIds; if (this.productsPriceCache.has(cacheKey)) { const cachedProducts = this.productsPriceCache.get(cacheKey); if (changedIds && changedIds.length > 0) { this.logInfo("商品价格缓存命中,增量更新变更商品", { cacheKey, changedIds, cachedProductCount: cachedProducts.length }); try { const updatedProducts = await this.prepareProductsWithPrice( schedule_date, extraContext, { productIds: changedIds } ); if (updatedProducts.length > 0) { const updatedMap = new Map(updatedProducts.map((p) => [p.id, p])); const mergedCache = []; for (const p of cachedProducts) { if (updatedMap.has(p.id)) { mergedCache.push(updatedMap.get(p.id)); updatedMap.delete(p.id); } else { mergedCache.push(p); } } for (const p of updatedMap.values()) { mergedCache.push(p); } this.productsPriceCache.set(cacheKey, mergedCache); this.logInfo("增量更新完成", { cacheKey, changedCount: updatedProducts.length, totalCount: mergedCache.length }); (0, import_product.perfMark)("getProductsWithPrice(incrementalUpdate)", performance.now() - t0, { cacheKey, changedCount: changedIds.length, count: mergedCache.length }); return mergedCache; } } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); this.logError("增量更新失败,返回现有缓存", { cacheKey, changedIds, error: errorMessage }); } (0, import_product.perfMark)("getProductsWithPrice(incrementalUpdate)", performance.now() - t0, { cacheKey, changedCount: changedIds.length, count: cachedProducts.length }); return cachedProducts; } (0, import_product.perfMark)("getProductsWithPrice(cacheHit)", performance.now() - t0, { cacheKey, count: cachedProducts.length }); this.logInfo("商品价格缓存命中", { cacheKey, productCount: cachedProducts.length }); return cachedProducts; } this.logInfo("商品价格缓存未命中,准备获取", { cacheKey }); const result = await this.prepareProductsWithPrice(schedule_date, extraContext); this.productsPriceCache.set(cacheKey, result); this.logInfo("商品价格已缓存", { cacheKey, productCount: result.length }); this.cleanExpiredPriceCache(); (0, import_product.perfMark)("getProductsWithPrice(cacheMiss)", performance.now() - t0, { cacheKey, count: result.length }); return result; } /** * 准备带价格的商品数据(通过格式化器流程处理) * @param schedule_date 日期 * @param extraContext 额外的上下文数据(可选) * @param options 可选参数 * @param options.productIds 指定商品 IDs,仅处理这些商品;不传则处理全量 * @returns 处理后的商品列表 * @private */ async prepareProductsWithPrice(schedule_date, extraContext, options) { var _a, _b; const tTotal = performance.now(); const targetIds = options == null ? void 0 : options.productIds; const isIncremental = targetIds && targetIds.length > 0; this.logInfo("prepareProductsWithPrice 开始处理", { schedule_date, mode: isIncremental ? "incremental" : "full", targetIdsCount: targetIds == null ? void 0 : targetIds.length }); try { let products; let ids; if (isIncremental) { const idSet = new Set(targetIds); products = this.getProductsRef().filter((p) => idSet.has(p.id)); ids = products.map((p) => p.id); } else { products = this.getProductsRef(); const tIds = performance.now(); ids = new Array(products.length); for (let i = 0; i < products.length; i++) { ids[i] = products[i].id; } (0, import_product.perfMark)("prepareProducts.extractIds", performance.now() - tIds, { count: ids.length }); } this.logInfo("获取到商品列表", { productCount: products.length }); const tPrice = performance.now(); const priceData = await this.loadProductsPrice({ ids, schedule_date }); (0, import_product.perfMark)("prepareProducts.loadPrice", performance.now() - tPrice, { count: ids.length }); this.logInfo("获取商品报价单价格成功", { priceDataCount: (priceData == null ? void 0 : priceData.length) ?? 0 }); const context = { schedule_date, priceData, locale: (_b = (_a = this.core) == null ? void 0 : _a.context) == null ? void 0 : _b.locale, ...extraContext }; const tFormat = performance.now(); const processedProducts = await this.applyFormatters(products, context); (0, import_product.perfMark)("prepareProducts.applyFormatters", performance.now() - tFormat, { count: products.length, formatterCount: this.formatters.length }); this.logInfo("prepareProductsWithPrice 处理完成", { mode: isIncremental ? "incremental" : "full", originalProductCount: products.length, processedProductCount: processedProducts.length, formatterCount: this.formatters.length }); if (!isIncremental) { await this.core.effects.emit( import_types.ProductsHooks.onProductsPriceApplied, processedProducts ); } (0, import_product.perfMark)("prepareProductsWithPrice", performance.now() - tTotal, { productCount: products.length, formatterCount: this.formatters.length, mode: isIncremental ? "incremental" : "full" }); return processedProducts; } catch (err) { const errorMessage = err instanceof Error ? err.message : String(err); console.log(`[ProductsModule] 🌐 ERROR`, err); this.logError("prepareProductsWithPrice 处理失败", { schedule_date, error: errorMessage }); } return []; } /** * 应用所有已注册的格式化器 * @param products 商品列表 * @param context 上下文信息 * @returns 格式化后的商品列表 * @private */ async applyFormatters(products, context) { let result = products; if (this.formatters.length === 0) { console.warn("[ProductsModule] ⚠️ 没有注册任何格式化器"); this.logWarning("没有注册任何格式化器", { productCount: products.length }); return result; } this.logInfo("开始应用格式化器", { productCount: products.length, formatterCount: this.formatters.length }); for (let i = 0; i < this.formatters.length; i++) { const formatter = this.formatters[i]; try { const tF = performance.now(); result = await formatter(result, context); (0, import_product.perfMark)(`applyFormatters[${i}]`, performance.now() - tF, { index: i, total: this.formatters.length, productCount: result.length }); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); console.error(`[ProductsModule] ❌ 格式化器 ${i + 1} 执行失败:`, error); this.logError(`格式化器 ${i + 1} 执行失败`, { formatterIndex: i + 1, totalFormatters: this.formatters.length, error: errorMessage }); } } this.logInfo("所有格式化器已应用", { formatterCount: this.formatters.length, resultProductCount: result.length }); return result; } /** * 注册内置的价格格式化器 * 这个格式化器负责将价格数据应用到商品上 * @private */ registerBuiltinPriceFormatter() { if (this.isPriceFormatterRegistered) { console.warn("[ProductsModule] 内置价格格式化器已注册,跳过"); return; } const priceFormatter = (products, context) => { if (!context.priceData || context.priceData.length === 0) { console.log("[ProductsModule] 💰 没有价格数据,跳过价格应用"); return products; } console.log(`[ProductsModule] 💰 应用价格数据到 ${products.length} 个商品`); return (0, import_product.applyPriceDataToProducts)(products, context.priceData); }; const i18nFormatter = (products, context) => { return (0, import_product.applyI18nToProducts)(products, context.locale); }; const detailValueFormatter = (products, context) => { return (0, import_product.applyDetailValueToProducts)(products, context); }; this.formatters.unshift(priceFormatter); this.formatters.push(i18nFormatter); this.formatters.push(detailValueFormatter); this.isPriceFormatterRegistered = true; console.log("[ProductsModule] ✅ 内置价格格式化器已注册(第 1 个)"); } /** * 注册商品格式化器 * 格式化器会按注册顺序依次执行(内置价格格式化器始终是第一个) * @param formatter 格式化函数 * @param prepend 是否插入到队列开头(默认 false,追加到末尾) * @example * ```ts * productsModule.registerFormatter((products, context) => { * return products.map(p => ({ * ...p, * custom_field: 'custom_value' * })); * }); * ``` */ registerFormatter(formatter, prepend = false) { if (prepend) { const insertIndex = this.isPriceFormatterRegistered ? 1 : 0; this.formatters.splice(insertIndex, 0, formatter); console.log(`[ProductsModule] 📌 已在位置 ${insertIndex + 1} 插入格式化器,当前共 ${this.formatters.length} 个`); } else { this.formatters.push(formatter); console.log(`[ProductsModule] 📌 已注册格式化器,当前共 ${this.formatters.length} 个`); } } /** * 清空所有格式化器(包括内置价格格式化器) * @param keepBuiltin 是否保留内置价格格式化器(默认 true) */ clearFormatters(keepBuiltin = true) { if (keepBuiltin && this.isPriceFormatterRegistered) { this.formatters = [this.formatters[0]]; console.log("[ProductsModule] 🧹 已清空自定义格式化器,保留内置价格格式化器"); } else { this.formatters = []; this.isPriceFormatterRegistered = false; console.log("[ProductsModule] 🧹 已清空所有格式化器"); } } /** * 清理过期的价格缓存 * 当缓存数量超过限制时,删除最老的缓存 * @private */ cleanExpiredPriceCache() { if (this.productsPriceCache.size > this.CACHE_MAX_DAYS) { const keys = Array.from(this.productsPriceCache.keys()); const oldestKey = keys[0]; this.productsPriceCache.delete(oldestKey); console.log(`[ProductsModule] 🧹 清理过期商品价格缓存: ${oldestKey}`); } } /** * 清空商品价格缓存 * 可用于手动刷新价格数据 */ clearPriceCache() { this.productsPriceCache.clear(); console.log("[ProductsModule] 🗑️ 商品价格缓存已清空"); } /** * 通过 ProductDataSource SSE 加载完整商品列表 */ async loadProductsByServer() { if (!this.productDataSource) { this.logWarning("loadProductsByServer: ProductDataSource 不可用"); return []; } this.logInfo("开始通过 DataSource SSE 加载商品列表"); const t0 = performance.now(); try { const tSSE = performance.now(); const productList = await this.productDataSource.run({ sse: {} }); const list = productList || []; (0, import_product.perfMark)("loadProductsByServer.SSE", performance.now() - tSSE, { count: list.length }); this.logInfo("通过 DataSource SSE 加载商品列表成功", { productCount: list.length, duration: `${Math.round(performance.now() - t0)}ms` }); await this.saveProductsToIndexDB(list); await this.core.effects.emit(import_types.ProductsHooks.onProductsLoaded, list); (0, import_product.perfMark)("loadProductsByServer", performance.now() - t0, { count: list.length }); return list; } catch (error) { const duration = Math.round(performance.now() - t0); const errorMessage = error instanceof Error ? error.message : String(error); console.error("[Products] 加载商品数据失败:", error); this.logError("通过 DataSource SSE 加载商品列表失败", { duration: `${duration}ms`, error: errorMessage }); return []; } } /** * 纯请求方法:通过 HTTP 接口获取商品列表(无副作用,不触发事件、不写 IndexDB) * @param params 查询参数 * @returns 商品列表 */ async fetchProductsByHttp(params) { var _a, _b; const { category_ids = [], product_ids = [], collection = [], customer_id, cacheId } = params || {}; this.logInfo("fetchProductsByHttp: 开始请求", { categoryIdsCount: category_ids.length, productIdsCount: product_ids.length, collectionCount: Array.isArray(collection) ? collection.length : collection ? 1 : 0, customer_id }); const startTime = Date.now(); try { const productsData = await this.request.post( `/product/query`, { open_bundle: 1, exclude_extension_type: [ "product_party", "product_event", "product_series_event", "product_package_ticket", "ticket", "event_item" ], force_ignore_cache_flag: 1, with: [ "category", "collection", "resourceRelation", "bundleGroup.bundleItem", "optionGroup.optionItem", "variantGroup.variantItem" ], open_deposit: 1, is_append_product_description: 1, with_count: ["bundleGroup", "optionGroup"], with_category_tree: 1, status: "published", num: 500, skip: 1, category_ids, ids: product_ids, collection, front_end_cache_id: cacheId, application_code: (_a = this.otherParams) == null ? void 0 : _a.channel }, { cache: void 0 } ); const productList = ((_b = productsData == null ? void 0 : productsData.data) == null ? void 0 : _b.list) || []; const duration = Date.now() - startTime; this.logInfo("fetchProductsByHttp: 请求成功", { productCount: productList.length, duration: `${duration}ms` }); return productList; } catch (error) { const duration = Date.now() - startTime; const errorMessage = error instanceof Error ? error.message : String(error); console.error("[Products] fetchProductsByHttp 失败:", error); this.logError("fetchProductsByHttp: 请求失败", { duration: `${duration}ms`, error: errorMessage }); return []; } } /** * 加载完整商品列表通过接口(包含所有详细数据) * 包含副作用:保存到 IndexDB + 触发 onProductsLoaded 事件 * @param params 查询参数 */ async loadProductsByServerHttp(params) { const productList = await this.fetchProductsByHttp(params); if (productList.length > 0) { await this.saveProductsToIndexDB(productList); await this.core.effects.emit(import_types.ProductsHooks.onProductsLoaded, productList); } return productList; } /** * 获取商品列表(深拷贝,供外部安全使用) */ async getProducts() { const t0 = performance.now(); const result = structuredClone(this.store.list); (0, import_product.perfMark)("ProductsModule.getProducts(structuredClone)", performance.now() - t0, { count: result.length }); return result; } /** * 内部获取商品列表的直接引用(无拷贝) * 仅供内部 formatter 流程使用,因为 formatter 会创建新对象 */ getProductsRef() { return this.store.list; } /** * 根据ID获取单个商品(从内存缓存) * 使用 Map 快速查询,时间复杂度 O(1) */ async getProductById(id) { const product = this.store.map.get(id); return product ? structuredClone(product) : void 0; } /** * 根据 ID 列表删除商品(用于 pubsub 同步删除场景) * 同时更新 store.list、store.map、IndexDB 和价格缓存 */ async removeProductsByIds(ids) { const idSet = new Set(ids); this.logInfo("removeProductsByIds", { ids, count: ids.length }); this.store.list = this.store.list.filter((p) => !idSet.has(p.id)); for (const id of ids) { this.store.map.delete(id); } if (this.dbManager) { try { for (const id of ids) { await this.dbManager.delete(INDEXDB_STORE_NAME, id); } } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); this.logError("removeProductsByIds: IndexDB 删除失败", { ids, error: errorMessage }); } } for (const [dateKey, cachedProducts] of this.productsPriceCache.entries()) { this.productsPriceCache.set( dateKey, cachedProducts.filter((p) => !idSet.has(p.id)) ); } this.core.effects.emit(import_types.ProductsHooks.onProductsChanged, this.store.list); this.logInfo("removeProductsByIds 完成", { remaining: this.store.list.length }); } /** * 重新从服务器加载全量商品列表并更新本地 store * 用于 pubsub 同步 create / update / batch_update 场景 */ async refreshProducts() { const tTotal = performance.now(); this.logInfo("refreshProducts 开始"); const products = await this.loadProductsByServer(); if (products && products.length > 0) { this.store.list = products; this.syncProductsMap(); this.core.effects.emit(import_types.ProductsHooks.onProductsChanged, this.store.list); this.logInfo("refreshProducts 完成", { productCount: products.length }); } else { this.logWarning("refreshProducts: 服务器未返回数据"); } (0, import_product.perfMark)("refreshProducts", performance.now() - tTotal, { count: this.store.list.length }); return this.store.list; } /** * 清空缓存 */ async clear() { this.logInfo("开始清空缓存", { currentProductCount: this.store.list.length, priceCacheCount: this.productsPriceCache.size }); this.store.list = []; this.store.map.clear(); if (this.dbManager) { try { await this.dbManager.clear(INDEXDB_STORE_NAME); console.log("[Products] IndexDB 缓存已清空"); this.logInfo("IndexDB 缓存已清空"); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); console.error("[Products] 清空 IndexDB 缓存失败:", error); this.logError("清空 IndexDB 缓存失败", { error: errorMessage }); } } console.log("[Products] 缓存已清空"); this.logInfo("缓存清空完成"); } /** * 从 IndexDB 加载商品数据 * @private */ async loadProductsFromIndexDB() { if (!this.dbManager) { this.logWarning("loadProductsFromIndexDB: dbManager 不可用"); return []; } try { const t0 = performance.now(); const products = await this.dbManager.getAll(INDEXDB_STORE_NAME); (0, import_product.perfMark)("loadProductsFromIndexDB", performance.now() - t0, { count: (products == null ? void 0 : products.length) ?? 0 }); this.logInfo("从 IndexDB 加载商品数据", { productCount: (products == null ? void 0 : products.length) ?? 0 }); return products || []; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); console.error("[Products] 从 IndexDB 读取数据失败:", error); this.logError("从 IndexDB 读取数据失败", { error: errorMessage }); return []; } } /** * 保存商品数据到 IndexDB(平铺存储) * @private */ async saveProductsToIndexDB(products) { if (!this.dbManager) { this.logWarning("saveProductsToIndexDB: dbManager 不可用"); return; } this.logInfo("开始保存商品数据到 IndexDB", { productCount: products.length }); try { const t0 = performance.now(); await this.dbManager.clear(INDEXDB_STORE_NAME); await this.dbManager.bulkUpdate(INDEXDB_STORE_NAME, products); (0, import_product.perfMark)("saveProductsToIndexDB", performance.now() - t0, { count: products.length }); console.log( `[Products] 已将 ${products.length} 个商品平铺保存到 IndexDB` ); this.logInfo("商品数据已保存到 IndexDB", { productCount: products.length }); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); console.error("[Products] 保存数据到 IndexDB 失败:", error); this.logError("保存数据到 IndexDB 失败", { productCount: products.length, error: errorMessage }); } } /** * 同步更新商品 Map 缓存 * 将 list 中的商品同步到 map,以 id 为 key * @private */ syncProductsMap() { const t0 = performance.now(); this.store.map.clear(); for (const product of this.store.list) { this.store.map.set(product.id, product); } (0, import_product.perfMark)("syncProductsMap", performance.now() - t0, { count: this.store.map.size }); } /** * 预加载模块数据(统一接口) * 在模块注册后自动调用 */ async preload() { console.log("[Products] 开始预加载数据..."); const tTotal = performance.now(); this.logInfo("开始预加载数据"); try { const tIndexDB = performance.now(); const cachedData = await this.loadProductsFromIndexDB(); (0, import_product.perfMark)("preload.loadFromIndexDB", performance.now() - tIndexDB, { count: (cachedData == null ? void 0 : cachedData.length) ?? 0 }); if (cachedData && cachedData.length > 0) { console.log(`[Products] 从 IndexDB 加载了 ${cachedData.length} 个商品`); this.store.list = cachedData; const tSync = performance.now(); this.syncProductsMap(); (0, import_product.perfMark)("preload.syncProductsMap", performance.now() - tSync, { count: cachedData.length }); this.core.effects.emit( import_types.ProductsHooks.onProductsChanged, this.store.list ); (0, import_product.perfMark)("preload(IndexDB)", performance.now() - tTotal, { count: cachedData.length, source: "IndexDB" }); this.logInfo("预加载完成(从 IndexDB)", { productCount: cachedData.length, duration: `${Math.round(performance.now() - tTotal)}ms`, source: "IndexDB" }); return; } console.log("[Products] IndexDB 中没有缓存数据,从服务器加载..."); this.logInfo("IndexDB 中没有缓存数据,准备从服务器加载"); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); console.warn("[Products] 从 IndexDB 加载数据失败:", error); this.logWarning("从 IndexDB 加载数据失败,准备从服务器加载", { error: errorMessage }); } const tServer = performance.now(); const products = await this.loadProductsByServer(); (0, import_product.perfMark)("preload.loadFromServer", performance.now() - tServer, { count: (products == null ? void 0 : products.length) ?? 0 }); if (products && products.length > 0) { this.store.list = products; const tSync = performance.now(); this.syncProductsMap(); (0, import_product.perfMark)("preload.syncProductsMap", performance.now() - tSync, { count: products.length }); this.core.effects.emit(import_types.ProductsHooks.onProductsChanged, this.store.list); (0, import_product.perfMark)("preload(Server)", performance.now() - tTotal, { count: products.length, source: "Server" }); this.logInfo("预加载完成(从服务器)", { productCount: products.length, duration: `${Math.round(performance.now() - tTotal)}ms`, source: "Server" }); } else { (0, import_product.perfMark)("preload(empty)", performance.now() - tTotal, { source: "empty" }); this.logWarning("预加载完成但未获取到数据", { duration: `${Math.round(performance.now() - tTotal)}ms` }); } } // ============================================= // 商品数据同步(pubsub) // ============================================= /** * 初始化 ProductDataSource 实例 * 与 pubsub 订阅和数据获取分开,仅负责创建实例 */ initProductDataSource() { var _a, _b; const ProductDataSourceClass = (_b = (_a = this.core.serverOptions) == null ? void 0 : _a.All_DATA_SOURCES) == null ? void 0 : _b.ProductDataSource; if (!ProductDataSourceClass) { this.logWarning("initProductDataSource: ProductDataSource 不可用"); return; } this.productDataSource = new ProductDataSourceClass(); this.logInfo("ProductDataSource 实例已创建"); } /** * 初始化 pubsub 订阅,监听管理后台商品变更 * 仅负责订阅 product / product_quotation 频道,消息通过防抖合并后批量处理 * 数据获取由 loadProductsByServer 单独负责 */ setupProductSync() { if (!this.productDataSource) { this.logWarning("setupProductSync: ProductDataSource 不可用,跳过同步初始化"); return; } const createHandler = (channelKey) => (message) => { const data = (message == null ? void 0 : message.data) || message; if (!data) return; console.log(`[ProductsModule] 收到同步消息 [${channelKey}]:`, data); this.logInfo("收到同步消息", { channelKey, action: data.action, id: data.id, action_filed: data.action_filed }); this.pendingSyncMessages.push({ ...data, _channelKey: channelKey }); if (this.syncTimer) { clearTimeout(this.syncTimer); } this.syncTimer = setTimeout(() => { this.processProductSyncMessages(); }, PRODUCT_SYNC_DEBOUNCE_MS); }; this.productDataSource.run({ pubsub: { callback: (res) => { console.log("sse_products_callback", res); this.logInfo("sse_products_callback: 收到同步消息", { data: res == null ? void 0 : res.data }); if (!(res == null ? void 0 : res.data)) return; const data = res.data; const channelKey = data.module || "product"; createHandler(channelKey)(data); } } }).catch((err) => { this.logError("setupProductSync: DataSource run 出错", { error: err instanceof Error ? err.message : String(err) }); }); this.logInfo("setupProductSync: pubsub 订阅已建立"); } /** * 处理防抖后的同步消息批次 * * product 模块: * - operation === 'delete' → 本地删除 * - change_types 包含 price → 仅收集变更 IDs(不拉商品数据) * - 有 body → body 完整数据直接覆盖本地 * - 其他 → SSE 增量拉取 * * product_collection / product_category: * - 按 relation_product_ids SSE 拉取受影响商品 * * product_quotation: * - 报价单变更影响范围大,直接清除价格缓存走全量重建 * * 处理完成后 emit onProductsSyncCompleted(携带 changedIds), * Server 层监听该事件后对变更商品增量执行 prepareProductsWithPrice 并更新缓存 */ async processProductSyncMessages() { var _a, _b, _c, _d, _e, _f; const messages = [...this.pendingSyncMessages]; this.pendingSyncMessages = []; if (messages.length === 0) return; this.logInfo("processProductSyncMessages: 开始处理", { count: messages.length }); const deleteIds = []; const bodyUpdates = /* @__PURE__ */ new Map(); const sseRefreshIds = []; const priceRefreshIds = []; let shouldClearPriceCache = false; for (const msg of messages) { const channelKey = msg._channelKey || msg.module || "product"; if (channelKey === "product") { if ((_a = msg.relation_product_ids) == null ? void 0 : _a.length) { sseRefreshIds.push(...msg.relation_product_ids); } if (msg.operation === "delete" || msg.action === "delete") { if ((_b = msg.ids) == null ? void 0 : _b.length) deleteIds.push(...msg.ids); else if (msg.id) deleteIds.push(msg.id); continue; } if ((_c = msg.change_types) == null ? void 0 : _c.includes("price")) { const ids = msg.ids || (msg.id ? [msg.id] : []); priceRefreshIds.push(...ids); if (msg.body) { const bodyId = msg.body.id; if (bodyId) bodyUpdates.set(bodyId, msg.body); } continue; } if (msg.body) { const bodyId = msg.body.id || msg.id; if (bodyId) bodyUpdates.set(bodyId, msg.body); continue; } if ((_d = msg.ids) == null ? void 0 : _d.length) { sseRefreshIds.push(...msg.ids); } else if (msg.id) { sseRefreshIds.push(msg.id); } } else if (channelKey === "product_quotation") { shouldClearPriceCache = true; if ((_e = msg.relation_product_ids) == null ? void 0 : _e.length) { sseRefreshIds.push(...msg.relation_product_ids); } } else if (["product_collection", "product_category"].includes(channelKey)) { if ((_f = msg.relation_product_ids) == null ? void 0 : _f.length) { sseRefreshIds.push(...msg.relation_product_ids); } } } const uniqueDeleteIds = [...new Set(deleteIds)]; const uniqueSSEIds = [...new Set(sseRefreshIds)]; const uniquePriceIds = [...new Set(priceRefreshIds)]; if (uniqueDeleteIds.length > 0) { await this.removeProductsByIds(uniqueDeleteIds); } if (bodyUpdates.size > 0) { await this.applyBodyUpdatesToStore(bodyUpdates); } if (uniqueSSEIds.length > 0) { const freshProducts = await this.fetchProductsBySSE(uniqueSSEIds); if (freshProducts.length > 0) { await this.mergeProductsToStore(freshProducts); } this.logInfo("processProductSyncMessages: SSE 增量更新完成123", { requestedCount: uniqueSSEIds.length, receivedCount: freshProducts.length }); } const allChangedIds = [.../* @__PURE__ */ new Set([ ...Array.from(bodyUpdates.keys()), ...uniqueSSEIds, ...uniquePriceIds ])]; this.logInfo("processProductSyncMessages: 处理完成123", { deleteCount: uniqueDeleteIds.length, bodyUpdateCount: bodyUpdates.size, sseRefreshCount: uniqueSSEIds.length, priceRefreshCount: uniquePriceIds.length, allChangedIdsCount: allChangedIds.length, shouldClearPriceCache }); const hasChanges = uniqueDeleteIds.length > 0 || allChangedIds.length > 0 || shouldClearPriceCache; if (!hasChanges) { this.logInfo("processProductSyncMessages: 没有变更,不触发 onProductsSyncCompleted"); return; } if (shouldClearPriceCache) { this.clearPriceCache(); } await this.core.effects.emit(import_types.ProductsHooks.onProductsSyncCompleted, { changedIds: allChangedIds }); } /** * 通过 SSE 按 ids 增量拉取商品数据 * 请求 GET /shop/core/stream?type=product&ids={ids} */ async fetchProductsBySSE(ids) { if (!this.productDataSource) { this.logWarning("fetchProductsBySSE: ProductDataSource 不可用"); return []; } this.logInfo("fetchProductsBySSE: 开始", { ids, count: ids.length }); const t0 = performance.now(); try { const productList = await this.productDataSource.run({ sse: { query: { type: "product", ids } } }); const list = productList || []; (0, import_product.perfMark)("fetchProductsBySSE", performance.now() - t0, { count: list.length }); this.logInfo("fetchProductsBySSE: 成功", { requestedCount: ids.length, receivedCount: list.length, duration: `${Math.round(performance.now() - t0)}ms` }); return list; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); this.logError("fetchProductsBySSE: 失败", { ids, error: errorMessage }); return []; } } /** * 将 body 完整数据直接覆盖到本地 store(不调用报价单接口) * 已存在的 → 直接替换;不存在的 → 追加 * 同时更新 Map 缓存、IndexDB,触发 onProductsChanged * 价格缓存由 processProductSyncMessages 末尾统一清除 */ async applyBodyUpdatesToStore(bodyUpdates) { this.logInfo("applyBodyUpdatesToStore: 开始", { count: bodyUpdates.size }); let updatedCount = 0; let newCount = 0; const appliedIds = /* @__PURE__ */ new Set(); this.store.list = this.store.list.map((p) => { if (bodyUpdates.has(p.id)) { updatedCount++; appliedIds.add(p.id); return bodyUpdates.get(p.id); } return p; }); for (const [id, body] of bodyUpdates) { if (!appliedIds.has(id)) { this.store.list.push(body); newCount++; } } this.syncProductsMap(); await this.saveProductsToIndexDB(this.store.list); this.core.effects.emit(import_types.ProductsHooks.onProductsChanged, this.store.list); this.logInfo("applyBodyUpdatesToStore: 完成", { updatedCount, newCount, totalCount: this.store.list.length }); } /** * 将增量拉取的商品合并到 store * 已存在的 → 替换;新的 → 追加 * 同时更新 store.map、IndexDB,触发 onProductsChanged */ async mergeProductsToStore(freshProducts) { const freshMap = /* @__PURE__ */ new Map(); for (const p of freshProducts) { freshMap.set(p.id, p); } const updatedList = this.store.list.map((p) => { if (freshMap.has(p.id)) { const fresh = freshMap.get(p.id); freshMap.delete(p.id); return fresh; } return p; }); const newCount = freshMap.size; for (const p of freshMap.values()) { updatedList.push(p); } const updatedCount = freshProducts.length - newCount; this.store.list = updatedList; this.syncProductsMap(); await this.saveProductsToIndexDB(this.store.list); this.logInfo("mergeProductsToStore: 合并完成", { updatedCount, newCount, totalCount: this.store.list.length }); this.core.effects.emit(import_types.ProductsHooks.onProductsChanged, this.store.list); } /** * 静默全量刷新:后台重新拉取全量 SSE 数据并更新本地 * 拿到完整数据后一次性替换 store,清除价格缓存,触发 onProductsSyncCompleted * @returns 刷新后的商品列表 */ async silentRefresh() { const t0 = performance.now(); this.logInfo("silentRefresh 开始"); try { const products = await this.loadProductsByServer(); if (products && products.length > 0) { this.store.list = products; this.syncProductsMap(); this.clearPriceCache(); this.core.effects.emit(import_types.ProductsHooks.onProductsChanged, this.store.list); await this.core.effects.emit(import_types.ProductsHooks.onProductsSyncCompleted, null); this.logInfo("silentRefresh 完成", { productCount: products.length, duration: `${Math.round(performance.now() - t0)}ms` }); } else { this.logWarning("silentRefresh: 服务器未返回数据"); } (0, import_product.perfMark)("silentRefresh", performance.now() - t0, { count: this.store.list.length }); return this.store.list; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); this.logError("silentRefresh 失败", { duration: `${Math.round(performance.now() - t0)}ms`, error: errorMessage }); return this.store.list; } } /** * 销毁同步资源(取消 pubsub 订阅、清除定时器) */ destroyProductSync() { var _a; if (this.syncTimer) { clearTimeout(this.syncTimer); this.syncTimer = void 0; } if ((_a = this.productDataSource) == null ? void 0 : _a.destroy) { this.productDataSource.destroy(); } this.pendingSyncMessages = []; this.logInfo("destroyProductSync: 同步资源已销毁"); } /** * 获取模块的路由定义 * Products 模块暂不提供路由,由 Server 层统一处理 */ getRoutes() { return []; } }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { ProductsModule });