UNPKG

@pisell/pisellos

Version:

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

1,278 lines (1,276 loc) 47.2 kB
var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; 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 __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default")); var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/server/index.ts var server_exports = {}; __export(server_exports, { default: () => server_default }); module.exports = __toCommonJS(server_exports); var import_dayjs = __toESM(require("dayjs")); var import_products = require("./modules/products"); var import_menu = require("./modules/menu"); var import_quotation = require("./modules/quotation"); var import_schedule = require("./modules/schedule"); var import_resource = require("./modules/resource"); var import_schedule2 = require("./utils/schedule"); var import_types = require("./modules/products/types"); var import_product = require("./utils/product"); var import_order = require("./modules/order"); var import_types2 = require("./modules/order/types"); var import_filterOrders = require("./modules/order/utils/filterOrders"); var import_filterBookings = require("./modules/order/utils/filterBookings"); __reExport(server_exports, require("./modules"), module.exports); var Server = class { constructor(core) { // 路由注册表 this.router = { get: {}, post: {}, put: {}, remove: {} }; // ---- 商品查询订阅者 ---- this.productQuerySubscribers = /* @__PURE__ */ new Map(); // ---- 订单 / 预约列表查询订阅者 ---- this.orderQuerySubscribers = /* @__PURE__ */ new Map(); this.bookingQuerySubscribers = /* @__PURE__ */ new Map(); // 模块注册表 - 定义所有可用的模块配置 this.moduleRegistry = { products: { name: "products", moduleClass: import_products.ProductsModule, moduleName: "server_products", version: "1.0.0", defaultStore: { list: [], selectProducts: [] } }, menu: { name: "menu", moduleClass: import_menu.MenuModule, moduleName: "server_menu", version: "1.0.0", defaultStore: { menuList: [] } }, quotation: { name: "quotation", moduleClass: import_quotation.QuotationModule, moduleName: "server_quotation", version: "1.0.0", defaultStore: { quotationList: [] } }, schedule: { name: "schedule", moduleClass: import_schedule.ScheduleModuleEx, moduleName: "server_schedule", version: "1.1.0", defaultStore: { scheduleList: [], availabilityDateList: [], otherProductsIds: [] } }, order: { name: "order", moduleClass: import_order.OrderModule, moduleName: "server_order", version: "1.0.0", defaultStore: { list: [] } }, resource: { name: "resource", moduleClass: import_resource.ResourceModule, moduleName: "server_resource", version: "1.0.0", defaultStore: { list: [], map: /* @__PURE__ */ new Map(), bookings: [] } } }; /** * 处理商品查询请求 * 存储订阅者信息,便于数据变更时推送最新结果 */ this.handleProductQuery = async ({ url, method, data, config }) => { console.log("[Server] handleProductQuery:", url, method, data, config); const { menu_list_ids, schedule_datetime, schedule_date } = data; const { callback, subscriberId } = config || {}; this.logInfo("handleProductQuery: 开始处理商品查询请求", { menu_list_ids, schedule_datetime, schedule_date }); if (subscriberId && typeof callback === "function") { this.productQuerySubscribers.set(subscriberId, { callback, context: { menu_list_ids, schedule_date, schedule_datetime } }); this.logInfo("handleProductQuery: 已注册订阅者", { subscriberId, totalSubscribers: this.productQuerySubscribers.size }); } return this.computeProductQueryResult({ menu_list_ids, schedule_date, schedule_datetime }); }; /** * 取消商品查询订阅(HTTP 路由入口) */ this.handleUnsubscribeProductQuery = async ({ data }) => { const { subscriberId } = data || {}; this.removeProductQuerySubscriber(subscriberId); return { code: 200, message: "ok", status: true }; }; /** * 处理订单列表查询 * 存储订阅者信息,本地计算结果;数据变更时通过 callback 推送 */ this.handleOrderList = async ({ url, method, data, config }) => { console.log("[Server] handleOrderList:", url, method, data, config); const queryPayload = data && typeof data === "object" ? { ...data } : {}; const { callback, subscriberId } = config || {}; this.logInfo("handleOrderList: 开始处理订单列表请求", { data: queryPayload }); if (subscriberId && typeof callback === "function") { this.orderQuerySubscribers.set(subscriberId, { callback, context: queryPayload }); this.logInfo("handleOrderList: 已注册订阅者", { subscriberId, totalSubscribers: this.orderQuerySubscribers.size }); } return this.computeOrderQueryResult(queryPayload); }; /** * 取消订单列表查询订阅(HTTP 路由入口) */ this.handleUnsubscribeOrderQuery = async ({ data }) => { const { subscriberId } = data || {}; if (subscriberId) { this.orderQuerySubscribers.delete(subscriberId); this.logInfo("handleUnsubscribeOrderQuery: 已移除订阅者", { subscriberId, remaining: this.orderQuerySubscribers.size }); } return { code: 200, message: "ok", status: true }; }; /** * 处理预约列表查询 * 今天:注册订阅者 + 本地数据筛选;非今天:清理订阅者 + 走真实 API */ this.handleBookingList = async ({ url, method, data, config }) => { console.log("[Server] handleBookingList:", url, method, data, config); const queryPayload = data && typeof data === "object" ? { ...data } : {}; const { callback, subscriberId } = config || {}; const isToday = this.isBookingQueryForToday(queryPayload); this.logInfo("handleBookingList: 开始处理预约列表请求", { data: queryPayload, isToday }); if (isToday) { if (subscriberId && typeof callback === "function") { this.bookingQuerySubscribers.set(subscriberId, { callback, context: queryPayload }); this.logInfo("handleBookingList: 已注册订阅者(今天)", { subscriberId, totalSubscribers: this.bookingQuerySubscribers.size }); } return this.computeBookingQueryResult(queryPayload); } else { if (subscriberId) { this.bookingQuerySubscribers.delete(subscriberId); this.logInfo("handleBookingList: 已清理订阅者(非今天)", { subscriberId, remaining: this.bookingQuerySubscribers.size }); } return this.fetchBookingListFromAPI(queryPayload); } }; /** * 处理资源列表查询 * 转发到资源模块去 */ this.handleResourceList = async ({ url, method, data, config }) => { var _a; console.log("[Server] handleResourceList:", url, method, data, config); const list = (_a = this.resource) == null ? void 0 : _a.getResources({ skip: (data == null ? void 0 : data.skip) || 1, num: (data == null ? void 0 : data.num) || 10 }); return { code: 200, data: { list, count: (list == null ? void 0 : list.length) || 0 }, message: "", status: true }; }; /** * 取消预约列表查询订阅(HTTP 路由入口) */ this.handleUnsubscribeBookingQuery = async ({ data }) => { const { subscriberId } = data || {}; if (subscriberId) { this.bookingQuerySubscribers.delete(subscriberId); this.logInfo("handleUnsubscribeBookingQuery: 已移除订阅者", { subscriberId, remaining: this.bookingQuerySubscribers.size }); } return { code: 200, message: "ok", status: true }; }; /** * 处理获取日程时间段点的请求 * 通过餐牌ID列表获取所有相关日程的时间段点 */ this.handleGetScheduleTimePoints = async ({ url, method, data, config }) => { console.log("[Server] handleGetScheduleTimePoints:", url, method, data, config); const { menu_list_ids } = data; this.logInfo("handleGetScheduleTimePoints 开始处理", { menuListIdsCount: (menu_list_ids == null ? void 0 : menu_list_ids.length) ?? 0, menu_list_ids }); if (!this.menu) { console.error("[Server] Menu 模块未注册"); this.logError("handleGetScheduleTimePoints: Menu 模块未注册"); return { code: 500, message: "Menu 模块未注册", data: [], status: false }; } if (!this.schedule) { console.error("[Server] Schedule 模块未注册"); this.logError("handleGetScheduleTimePoints: Schedule 模块未注册"); return { code: 500, message: "Schedule 模块未注册", data: [], status: false }; } if (!menu_list_ids || !Array.isArray(menu_list_ids) || menu_list_ids.length === 0) { console.error("[Server] menu_list_ids 参数无效"); this.logWarning("handleGetScheduleTimePoints: menu_list_ids 参数无效", { menuListIdsCount: (menu_list_ids == null ? void 0 : menu_list_ids.length) ?? 0, menu_list_ids }); return { code: 400, message: "menu_list_ids 参数无效", data: [], status: false }; } try { const menuList = this.menu.getMenuByIds(menu_list_ids); console.log(`[Server] 找到 ${menuList.length} 个餐牌`); this.logInfo("handleGetScheduleTimePoints: 获取到餐牌列表", { requestedCount: menu_list_ids.length, foundCount: menuList.length, menu_list_ids, menuList }); const scheduleIds = (0, import_schedule2.extractScheduleIdsFromMenus)(menuList); console.log(`[Server] 提取到 ${scheduleIds.length} 个日程ID:`, scheduleIds); this.logInfo("handleGetScheduleTimePoints: 提取到日程 IDs", { scheduleCount: scheduleIds.length }); if (scheduleIds.length === 0) { this.logInfo("handleGetScheduleTimePoints: 没有找到相关日程", { menuListIdsCount: menu_list_ids.length }); return { code: 200, message: "没有找到相关日程", data: [], status: true }; } const scheduleList = this.schedule.getScheduleByIds(scheduleIds); console.log(`[Server] 找到 ${scheduleList.length} 个日程`); this.logInfo("handleGetScheduleTimePoints: 获取到日程详情", { requestedCount: scheduleIds.length, foundCount: scheduleList.length }); const timePoints = (0, import_schedule2.extractTimePointsFromSchedules)(scheduleList); console.log(`[Server] 提取到 ${timePoints.length} 个时间点:`, timePoints); this.logInfo("handleGetScheduleTimePoints 处理完成", { menuListIdsCount: menu_list_ids.length, menuCount: menuList.length, scheduleCount: scheduleList.length, timePointCount: timePoints.length }); return { code: 200, message: "获取成功", data: timePoints, status: true }; } catch (error) { const errorMessage = error instanceof Error ? error.message : "未知错误"; console.error("[Server] 获取日程时间点失败:", error); this.logError("handleGetScheduleTimePoints: 获取日程时间点失败", { menuListIdsCount: menu_list_ids.length, error: errorMessage }); return { code: 500, message: `获取失败: ${errorMessage}`, data: [], status: false }; } }; var _a; this.core = core; const appPlugin = core.getPlugin("app"); this.app = (appPlugin == null ? void 0 : appPlugin.getApp()) || null; this.logger = ((_a = this.app) == null ? void 0 : _a.logger) || null; console.log("[Server] Server 初始化", this.core); this.logInfo("Server 初始化", { hasApp: !!this.app, hasLogger: !!this.logger }); } /** * 注册路由 * @param routes 路由定义数组 */ registerRoutes(routes) { routes.forEach((route) => { const { method, path, handler } = route; if (!this.router[method]) { console.warn(`[Server] 不支持的 HTTP 方法: ${method}`); this.logWarning(`不支持的 HTTP 方法`, { method, path }); return; } if (this.router[method][path]) { console.warn(`[Server] 路由已存在,将被覆盖: ${method.toUpperCase()} ${path}`); this.logWarning(`路由已存在,将被覆盖`, { method, path }); } this.router[method][path] = handler; console.log(`[Server] 📝 注册路由: ${method.toUpperCase()} ${path}`); this.logInfo(`注册路由`, { method: method.toUpperCase(), path }); }); } /** * 注册单个模块并自动注册其路由 * @param module 模块实例 * @param options 模块配置 * @param moduleName 模块名称(用于日志) */ async registerModuleWithRoutes(module2, options, moduleName) { this.logInfo(`开始注册模块: ${moduleName}`, { moduleName, options }); await this.core.registerModule(module2, options); console.log(`[Server] ✅ ${moduleName} 模块已注册`); this.logInfo(`模块注册成功: ${moduleName}`, { moduleName }); if (typeof module2.getRoutes === "function") { const routes = module2.getRoutes(); if (routes && routes.length > 0) { this.registerRoutes(routes); this.logInfo(`模块路由已注册: ${moduleName}`, { moduleName, routesCount: routes.length }); } } } /** * 根据模块名称注册单个模块 * @param moduleConfig 模块配置(可以是字符串或配置对象) */ async registerModuleByName(moduleConfig) { const moduleName = typeof moduleConfig === "string" ? moduleConfig : moduleConfig.name; const shouldPreload = typeof moduleConfig === "object" ? moduleConfig.preload !== false : true; const customConfig = typeof moduleConfig === "object" ? moduleConfig.config : {}; this.logInfo(`解析模块配置: ${moduleName}`, { moduleName, shouldPreload, customConfig }); const registryConfig = this.moduleRegistry[moduleName]; if (!registryConfig) { this.logError(`未找到模块配置: ${moduleName}`, { moduleName }); throw new Error(`[Server] 未找到模块配置: ${moduleName}`); } const ModuleClass = registryConfig.moduleClass; const moduleInstance = new ModuleClass(registryConfig.moduleName, registryConfig.version); const moduleOptions = { store: { ...registryConfig.defaultStore }, ...customConfig }; await this.registerModuleWithRoutes( moduleInstance, moduleOptions, moduleName.charAt(0).toUpperCase() + moduleName.slice(1) ); this[moduleName] = moduleInstance; this.logInfo(`模块注册完成: ${moduleName}`, { moduleName, shouldPreload, version: registryConfig.version }); return { module: moduleInstance, config: registryConfig, shouldPreload }; } /** * 注册指定的服务端模块 * @param moduleConfigs 要注册的模块配置数组(模块名称或配置对象) */ async registerModules(moduleConfigs) { console.log("[Server] 开始注册模块..."); const configs = moduleConfigs || Object.keys(this.moduleRegistry); this.logInfo("开始批量注册模块", { moduleConfigs: configs, totalCount: configs.length }); const registeredModules = []; for (const config of configs) { const configName = typeof config === "string" ? config : config.name; try { const result = await this.registerModuleByName(config); registeredModules.push(result); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); this.logError(`模块注册失败: ${configName}`, { moduleName: configName, error: errorMessage }); console.error(`[Server] 模块注册失败:`, error); throw error; } } console.log("[Server] ✅ 所有模块注册完成"); this.logInfo("所有模块注册完成", { registeredCount: registeredModules.length, moduleNames: registeredModules.map((m) => { var _a; return (_a = m == null ? void 0 : m.config) == null ? void 0 : _a.name; }) }); return registeredModules; } /** * 预加载指定模块的数据 * @param registeredModules 已注册的模块信息 */ async preloadModulesData(registeredModules, options) { var _a, _b; console.log("[Server] 开始预加载模块数据..."); const modulesToPreload = registeredModules.filter((m) => m.shouldPreload); this.logInfo("开始预加载模块数据", { totalModules: registeredModules.length, preloadCount: modulesToPreload.length, modulesToPreload }); const preloadResults = []; for (const { module: module2, config, shouldPreload } of registeredModules) { if (!shouldPreload) { console.log(`[Server] ⏭️ 跳过 ${config.name} 模块预加载`); this.logInfo(`跳过模块预加载: ${config.name}`, { moduleName: config.name, reason: "shouldPreload=false" }); continue; } if (typeof module2.preload === "function") { const startTime = Date.now(); try { this.logInfo(`开始预加载模块: ${config.name}`, { moduleName: config.name }); (_a = options == null ? void 0 : options.onModuleLoad) == null ? void 0 : _a.call(options, config.name); await module2.preload(); (_b = options == null ? void 0 : options.onModuleLoadComplete) == null ? void 0 : _b.call(options, config.name); const duration = Date.now() - startTime; console.log(`[Server] ✅ ${config.name} 模块数据已预加载`); this.logInfo(`模块预加载成功: ${config.name}`, { moduleName: config.name, duration: `${duration}ms` }); preloadResults.push({ name: config.name, success: true }); } catch (error) { const duration = Date.now() - startTime; const errorMessage = error instanceof Error ? error.message : String(error); console.error(`[Server] ❌ ${config.name} 模块预加载失败:`, error); this.logError(`模块预加载失败: ${config.name}`, { moduleName: config.name, duration: `${duration}ms`, error: errorMessage }); preloadResults.push({ name: config.name, success: false, error: errorMessage }); } } else { console.log(`[Server] ⚠️ ${config.name} 模块未实现 preload() 方法`); this.logWarning(`模块未实现 preload 方法: ${config.name}`, { moduleName: config.name }); } } const successCount = preloadResults.filter((r) => r.success).length; const failCount = preloadResults.filter((r) => !r.success).length; console.log("[Server] ✅ 所有模块数据预加载完成"); this.logInfo("所有模块预加载完成", { successCount, failCount }); } /** * 初始化 Server(注册模块并预加载数据) * @param moduleConfigs 要注册的模块配置 * @param autoPreload 是否自动预加载数据 */ async initialize(moduleConfigs, autoPreload = true, options) { const startTime = Date.now(); this.logInfo("Server 初始化开始", { moduleConfigCount: (moduleConfigs == null ? void 0 : moduleConfigs.length) ?? "all", autoPreload, moduleConfigs }); this.registerServerRoutes(); const registeredModules = await this.registerModules(moduleConfigs); if (autoPreload) { await this.preloadModulesData(registeredModules, options); } else { this.logInfo("跳过自动预加载", { autoPreload }); } this.core.effects.on(import_types.ProductsHooks.onProductsSyncCompleted, (payload) => { this.recomputeAndNotifyProductQuery({ changedIds: payload == null ? void 0 : payload.changedIds }); }); this.core.effects.on(import_types2.OrderHooks.onOrdersChanged, () => { this.recomputeAndNotifyOrderQuery(); this.recomputeAndNotifyBookingQuery(); }); const duration = Date.now() - startTime; this.logInfo("Server 初始化完成", { duration: `${duration}ms`, registeredModuleCount: registeredModules.length }); return registeredModules; } /** * 获取 Products 模块实例 */ getProducts() { return this.products; } /** * 获取 Menu 模块实例 */ getMenu() { return this.menu; } /** * 获取 Quotation 模块实例 */ getQuotation() { return this.quotation; } /** * 获取 Schedule 模块实例 */ getSchedule() { return this.schedule; } /** * 获取所有已注册的模块列表 */ getRegisteredModules() { const modules = []; if (this.products) modules.push("products"); if (this.menu) modules.push("menu"); if (this.quotation) modules.push("quotation"); if (this.schedule) modules.push("schedule"); if (this.resource) modules.push("resource"); if (this.order) modules.push("order"); return modules; } /** * 后台静默刷新商品数据 * 重新拉取全量 SSE 接口,更新本地数据后触发 onProductsSyncCompleted * 不影响当前界面展示,适用于切回前台、定时刷新等场景 */ async refreshProductsInBackground() { if (!this.products) { this.logWarning("refreshProductsInBackground: Products 模块未注册"); return; } this.logInfo("refreshProductsInBackground 开始"); const startTime = Date.now(); try { await this.products.silentRefresh(); const duration = Date.now() - startTime; this.logInfo("refreshProductsInBackground 完成", { duration: `${duration}ms` }); } catch (error) { const duration = Date.now() - startTime; const errorMessage = error instanceof Error ? error.message : String(error); console.error("[Server] refreshProductsInBackground 失败:", error); this.logError("refreshProductsInBackground 失败", { duration: `${duration}ms`, error: errorMessage }); } } /** * 清空所有server模块的IndexedDB缓存 * @returns Promise<void> */ async clearAllIndexDB() { console.log("[Server] 开始清空所有模块的IndexedDB缓存..."); this.logInfo("开始清空所有模块的 IndexedDB 缓存"); const clearTasks = []; const moduleNames = []; if (this.products) { clearTasks.push(this.products.clear()); moduleNames.push("Products"); } if (this.menu) { clearTasks.push(this.menu.clear()); moduleNames.push("Menu"); } if (this.quotation) { clearTasks.push(this.quotation.clear()); moduleNames.push("Quotation"); } if (this.schedule) { clearTasks.push(this.schedule.clear()); moduleNames.push("Schedule"); } if (this.resource) { clearTasks.push(this.resource.clear()); moduleNames.push("Resource"); } if (this.order) { clearTasks.push(this.order.clear()); moduleNames.push("Order"); } if (clearTasks.length === 0) { console.warn("[Server] 没有找到已注册的模块,无需清空"); this.logWarning("没有找到已注册的模块,无需清空 IndexedDB"); return; } this.logInfo("准备清空模块缓存", { moduleNames }); try { await Promise.all(clearTasks); console.log(`[Server] ✅ 已成功清空所有模块的IndexedDB缓存: ${moduleNames.join(", ")}`); this.logInfo("成功清空所有模块的 IndexedDB 缓存", { moduleNames }); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); console.error("[Server] ❌ 清空IndexedDB缓存时发生错误:", error); this.logError("清空 IndexedDB 缓存时发生错误", { moduleNames, error: errorMessage }); throw error; } } /** * 获取所有注册的路由 */ getRoutes() { return this.router; } /** * 根据方法和路径获取路由处理函数 * @param method HTTP 方法 * @param path 路径 */ getRouteHandler(method, path) { var _a; return (_a = this.router[method]) == null ? void 0 : _a[path]; } /** * 检查路由是否存在 * @param method HTTP 方法 * @param path 路径 */ hasRoute(method, path) { var _a; console.log(method, path, "method, path"); return !!((_a = this.router[method]) == null ? void 0 : _a[path]); } /** * 执行路由处理函数 * @param method HTTP 方法 * @param path 路径 * @param params 请求参数 */ async handleRoute(method, path, params) { const startTime = Date.now(); this.logInfo(`路由请求开始: ${method.toUpperCase()} ${path}`, { method: method.toUpperCase(), path, url: params.url, data: params.data }); const handler = this.getRouteHandler(method, path); if (!handler) { this.logError(`路由未找到: ${method.toUpperCase()} ${path}`, { method, path }); throw new Error(`Route not found: ${method.toUpperCase()} ${path}`); } try { const result = await handler(params); const duration = Date.now() - startTime; this.logInfo(`路由请求完成: ${method.toUpperCase()} ${path}`, { method: method.toUpperCase(), path, duration: `${duration}ms`, resultCode: result == null ? void 0 : result.code, resultStatus: result == null ? void 0 : result.status }); return result; } catch (error) { const duration = Date.now() - startTime; const errorMessage = error instanceof Error ? error.message : String(error); this.logError(`路由处理错误: ${method.toUpperCase()} ${path}`, { method: method.toUpperCase(), path, duration: `${duration}ms`, error: errorMessage, data: params.data }); console.error(`[Server] 路由处理错误: ${method.toUpperCase()} ${path}`, error); throw error; } } /** * 获取所有路由列表(用于调试和文档) */ getAllRoutes() { const routes = []; ["get", "post", "put", "remove"].forEach((method) => { Object.keys(this.router[method]).forEach((path) => { routes.push({ method, path }); }); }); return routes; } /** * 注册 Server 层的业务路由 * 这些路由需要编排多个模块的数据 * @private */ registerServerRoutes() { console.log("[Server] 注册 Server 层业务路由..."); this.registerRoutes([ { method: "post", path: "/shop/product/query", handler: this.handleProductQuery.bind(this) }, { method: "post", path: "/shop/product/query/unsubscribe", handler: this.handleUnsubscribeProductQuery.bind(this) }, { method: "post", path: "/shop/menu/schedule-time-points", handler: this.handleGetScheduleTimePoints.bind(this) }, { method: "post", path: "/shop/order/v2/list", handler: this.handleOrderList.bind(this) }, { method: "post", path: "/shop/order/v2/list/unsubscribe", handler: this.handleUnsubscribeOrderQuery.bind(this) }, { method: "get", path: "/shop/schedule/booking", handler: this.handleBookingList.bind(this) }, { method: "get", path: "/shop/schedule/booking/unsubscribe", handler: this.handleUnsubscribeBookingQuery.bind(this) }, { method: "get", path: "/shop/form/resource/page", handler: this.handleResourceList.bind(this) } ]); } /** * 根据 subscriberId 移除商品查询订阅者 */ removeProductQuerySubscriber(subscriberId) { if (subscriberId) { this.productQuerySubscribers.delete(subscriberId); this.logInfo("removeProductQuerySubscriber: 已移除订阅者", { subscriberId, remaining: this.productQuerySubscribers.size }); } } /** * 判断预约查询的 sales_time_between 起始日期是否为今天 */ isBookingQueryForToday(data) { const range = data == null ? void 0 : data.sales_time_between; if (!Array.isArray(range) || range.length < 1) return true; const startDateStr = String(range[0]).split("T")[0].split(" ")[0]; const todayStr = (0, import_dayjs.default)().format("YYYY-MM-DD"); return startDateStr === todayStr; } /** * 非今天的预约查询:通过真实 API 获取数据,再做 flattenOrdersToBookings 拆分 */ async fetchBookingListFromAPI(data) { var _a, _b; if (!((_a = this.app) == null ? void 0 : _a.request)) { this.logError("fetchBookingListFromAPI: app.request 不可用"); return { code: 500, message: "app.request 不可用", data: { list: [], count: 0 }, status: false }; } try { const response = await this.app.request.get("/shop/order/sales", { ...data, form_record_ids: void 0, with: ["all"] }, { isShopApi: true }); const rawList = ((_b = response == null ? void 0 : response.data) == null ? void 0 : _b.list) ?? (response == null ? void 0 : response.list) ?? []; const list = (0, import_filterBookings.filterBookingsFromOrders)(rawList, data); this.logInfo("fetchBookingListFromAPI: API 返回并拆分完成", { rawCount: rawList.length, flattenedCount: list.count }); return { code: 200, data: { ...response.data, list: (list == null ? void 0 : list.list) || [] }, message: "", status: true }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); this.logError("fetchBookingListFromAPI: 请求失败", { error: errorMessage }); return { code: 500, message: errorMessage, data: { list: [], count: 0 }, status: false }; } } /** * 订单列表本地计算(编排 Order 模块) * filter 逻辑暂为 mock,仅记录参数 */ async computeOrderQueryResult(data) { this.logInfo("computeOrderQueryResult: 开始过滤", { data }); console.log("[Server] computeOrderQueryResult", data); if (!this.order) { this.logError("computeOrderQueryResult: Order 模块未注册"); return { code: 500, message: "Order 模块未注册", data: { list: [], count: 0 }, status: false }; } const rawList = this.order.getOrders(); this.logInfo("computeOrderQueryResult: 本地订单数量", { rawCount: rawList.length }); const result = (0, import_filterOrders.filterOrders)(rawList, data); this.logInfo("computeOrderQueryResult: 过滤结果", { rawCount: rawList.length, filteredCount: result.count, size: result.size, skip: result.skip }); return { code: 200, data: result, message: "", status: true }; } /** * 预约列表本地计算(编排 Order 模块) * 从订单中展开 bookings,再按条件筛选 + 分页 */ async computeBookingQueryResult(data) { if (!this.order) { this.logError("computeBookingQueryResult: Order 模块未注册"); return { code: 500, message: "Order 模块未注册", data: { list: [], count: 0 }, status: false }; } const rawOrders = this.order.getOrders(); let result = (0, import_filterBookings.filterBookingsFromOrders)(rawOrders, data); result = (0, import_filterBookings.sortBookings)(result, data); this.logInfo("computeBookingQueryResult: 过滤结果", { orderCount: rawOrders.length, filteredCount: result.count, size: result.size, skip: result.skip }); return { code: 200, data: result, message: "", status: true }; } /** * 商品查询的核心计算逻辑(编排 Products、Menu、Schedule 模块) * 供 handleProductQuery 首次返回及 pubsub 变更推送复用 * @param context 查询上下文 * @param options 可选参数 * @param options.changedIds 变更的商品 IDs,用于增量更新价格缓存 */ async computeProductQueryResult(context, options) { const tTotal = performance.now(); const { menu_list_ids, schedule_date, schedule_datetime } = context; this.logInfo("computeProductQueryResult 开始", { menuListIdsCount: (menu_list_ids == null ? void 0 : menu_list_ids.length) ?? 0, schedule_datetime, schedule_date, changedIds: options == null ? void 0 : options.changedIds }); if (!this.products) { this.logError("computeProductQueryResult: Products 模块未注册"); return { message: "Products 模块未注册", data: { list: [], count: 0 } }; } if (!this.menu) { this.logError("computeProductQueryResult: Menu 模块未注册"); return { message: "Menu 模块未注册", data: { list: [], count: 0 } }; } if (!this.schedule) { this.logError("computeProductQueryResult: Schedule 模块未注册"); return { message: "Schedule 模块未注册", data: { list: [], count: 0 } }; } let activeMenuList = []; if (menu_list_ids && Array.isArray(menu_list_ids) && menu_list_ids.length > 0) { const tMenu = performance.now(); const menuList = this.menu.getMenuByIds(menu_list_ids); activeMenuList = menuList.filter((menu) => { var _a; return ((_a = this.schedule) == null ? void 0 : _a.getDateIsInSchedule(schedule_datetime, menu.schedule)) || false; }); (0, import_product.perfMark)("computeQuery.filterActiveMenu", performance.now() - tMenu, { totalMenu: menuList.length, activeMenu: activeMenuList.length }); } const tPrice = performance.now(); const allProductsWithPrice = await this.products.getProductsWithPrice(schedule_date, { scheduleModule: this.getSchedule() }, { changedIds: options == null ? void 0 : options.changedIds }); (0, import_product.perfMark)("computeQuery.getProductsWithPrice", performance.now() - tPrice, { count: allProductsWithPrice.length }); const tFilter = performance.now(); let filteredProducts = this.filterProductsByMenuConfig(allProductsWithPrice, activeMenuList); (0, import_product.perfMark)("computeQuery.filterByMenu", performance.now() - tFilter, { before: allProductsWithPrice.length, after: filteredProducts.length }); const tStatus = performance.now(); const beforeStatusCount = filteredProducts.length; filteredProducts = filteredProducts.filter((p) => ((p == null ? void 0 : p.status) || "published") === "published"); (0, import_product.perfMark)("computeQuery.filterByStatus", performance.now() - tStatus, { before: beforeStatusCount, after: filteredProducts.length }); const tSort = performance.now(); filteredProducts = filteredProducts.sort((a, b) => { const sortDiff = Number(b.sort) - Number(a.sort); if (sortDiff !== 0) return sortDiff; return (a.title || "").localeCompare(b.title || ""); }); (0, import_product.perfMark)("computeQuery.sort", performance.now() - tSort, { count: filteredProducts.length }); (0, import_product.perfMark)("computeProductQueryResult", performance.now() - tTotal, { originalCount: allProductsWithPrice.length, filteredCount: filteredProducts.length, activeMenuCount: activeMenuList.length }); this.logInfo("computeProductQueryResult 完成", { originalCount: allProductsWithPrice.length, filteredCount: filteredProducts.length, activeMenuCount: activeMenuList.length }); return { code: 200, data: { list: filteredProducts, count: filteredProducts.length }, message: "", status: true }; } /** * 数据变更后,遍历所有订阅者重新计算查询结果并通过 callback 推送 * 由 ProductsModule 的 onProductsSyncCompleted 事件触发 * @param options 可选参数 * @param options.changedIds 变更的商品 IDs,用于增量更新价格缓存 */ async recomputeAndNotifyProductQuery(options) { if (this.productQuerySubscribers.size === 0) return; this.logInfo("recomputeAndNotifyProductQuery: 开始推送", { subscriberCount: this.productQuerySubscribers.size, changedIds: options == null ? void 0 : options.changedIds }); for (const [subscriberId, subscriber] of this.productQuerySubscribers.entries()) { try { const result = await this.computeProductQueryResult(subscriber.context, { changedIds: options == null ? void 0 : options.changedIds }); subscriber.callback(result); this.logInfo("recomputeAndNotifyProductQuery: 已推送", { subscriberId }); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); this.logError("recomputeAndNotifyProductQuery: 推送失败", { subscriberId, error: errorMessage }); } } } /** * 订单数据变更后,遍历订阅者重新计算并通过 callback 推送 */ async recomputeAndNotifyOrderQuery() { if (this.orderQuerySubscribers.size === 0) return; this.logInfo("recomputeAndNotifyOrderQuery: 开始推送", { subscriberCount: this.orderQuerySubscribers.size }); for (const [subscriberId, subscriber] of this.orderQuerySubscribers.entries()) { try { const result = await this.computeOrderQueryResult(subscriber.context); subscriber.callback(result); this.logInfo("recomputeAndNotifyOrderQuery: 已推送", { subscriberId }); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); this.logError("recomputeAndNotifyOrderQuery: 推送失败", { subscriberId, error: errorMessage }); } } } /** * 预约数据变更后,遍历订阅者重新计算并通过 callback 推送 */ async recomputeAndNotifyBookingQuery() { if (this.bookingQuerySubscribers.size === 0) return; this.logInfo("recomputeAndNotifyBookingQuery: 开始推送", { subscriberCount: this.bookingQuerySubscribers.size }); for (const [subscriberId, subscriber] of this.bookingQuerySubscribers.entries()) { try { const result = await this.computeBookingQueryResult(subscriber.context); subscriber.callback(result); this.logInfo("recomputeAndNotifyBookingQuery: 已推送", { subscriberId }); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); this.logError("recomputeAndNotifyBookingQuery: 推送失败", { subscriberId, error: errorMessage }); } } } /** * 根据餐牌配置过滤商品 * @param products 所有商品列表 * @param activeMenuList 生效的餐牌列表 * @returns 过滤后的商品列表 * @private */ filterProductsByMenuConfig(products, activeMenuList) { if (!activeMenuList || activeMenuList.length === 0) { console.log("[Server] 没有生效的餐牌,返回空数组"); this.logInfo("filterProductsByMenuConfig: 没有生效的餐牌,返回空数组", { productCount: products.length, activeMenuCount: 0 }); return []; } let allowedProductIds = /* @__PURE__ */ new Set(); let allowedCollectionIds = /* @__PURE__ */ new Set(); let hasProductAll = false; for (const menu of activeMenuList) { const config = menu.partyroom_package; if (!config) { console.warn("[Server] 餐牌缺少 partyroom_package 配置:", menu); this.logWarning("filterProductsByMenuConfig: 餐牌缺少 partyroom_package 配置", { menuId: menu.form_record_id, menuName: menu.name }); continue; } if (config.type === "product_all") { console.log("[Server] 餐牌允许所有商品:", menu.name); this.logInfo("filterProductsByMenuConfig: 餐牌允许所有商品", { menuId: menu.form_record_id, menuName: menu.name, configType: config.type }); hasProductAll = true; break; } if (config.type === "products" && Array.isArray(config.products)) { const productIds = config.products.map((item) => item.product_id); productIds.forEach((id) => id && allowedProductIds.add(id)); console.log("[Server] 餐牌允许指定商品 (product_ids):", menu.name, productIds); this.logInfo("filterProductsByMenuConfig: 餐牌允许指定商品", { menuId: menu.form_record_id, menuName: menu.name, configType: config.type, productIdsCount: productIds.length }); } if (config.type === "product_collection" && Array.isArray(config.products)) { const collectionIds = config.products.map((item) => item.product_collection_id); collectionIds.forEach((id) => id && allowedCollectionIds.add(id)); console.log("[Server] 餐牌允许商品集合 (product_collection):", menu.name, collectionIds); this.logInfo("filterProductsByMenuConfig: 餐牌允许商品集合", { menuId: menu.form_record_id, menuName: menu.name, configType: config.type, collectionIdsCount: collectionIds.length }); } } if (hasProductAll) { this.logInfo("filterProductsByMenuConfig: 返回所有商品(product_all)", { productCount: products.length }); return products; } if (allowedProductIds.size === 0 && allowedCollectionIds.size === 0) { console.log("[Server] 没有允许的商品和集合"); this.logWarning("filterProductsByMenuConfig: 没有允许的商品和集合", { productCount: products.length, activeMenuCount: activeMenuList.length }); return []; } const allowedProductIdsArray = Array.from(allowedProductIds); const allowedCollectionIdsArray = Array.from(allowedCollectionIds); console.log("[Server] 允许的商品 IDs:", allowedProductIdsArray); console.log("[Server] 允许的集合 IDs:", allowedCollectionIdsArray); this.logInfo("filterProductsByMenuConfig: 收集到的允许规则", { allowedProductCount: allowedProductIds.size, allowedCollectionCount: allowedCollectionIds.size }); const filteredProducts = products.filter((product) => { if (allowedProductIds.has(product.id)) { return true; } if (allowedCollectionIds.size > 0 && Array.isArray(product.collection)) { const hasMatchingCollection = product.collection.some( (col) => allowedCollectionIds.has(col.id) ); if (hasMatchingCollection) { return true; } } return false; }); console.log("[Server] 过滤结果: 原始 %d 个,过滤后 %d 个", products.length, filteredProducts.length); this.logInfo("filterProductsByMenuConfig: 过滤完成", { originalCount: products.length, filteredCount: filteredProducts.length, removedCount: products.length - filteredProducts.length }); return filteredProducts; } /** * 记录信息日志 * @param title 日志标题 * @param metadata 日志元数据 */ logInfo(title, metadata) { try { if (this.logger) { this.logger.addLog({ type: "info", title: `[Server] ${title}`, metadata: metadata || {} }); } } catch { } } /** * 记录警告日志 * @param title 日志标题 * @param metadata 日志元数据 */ logWarning(title, metadata) { try { if (this.logger) { this.logger.addLog({ type: "warning", title: `[Server] ${title}`, metadata: metadata || {} }); } } catch { } } /** * 记录错误日志 * @param title 日志标题 * @param metadata 日志元数据 */ logError(title, metadata) { try { if (this.logger) { this.logger.addLog({ type: "error", title: `[Server] ${title}`, metadata: metadata || {} }); } } catch { } } }; var server_default = Server; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { ...require("./modules") });