@pisell/pisellos
Version:
一个可扩展的前端模块化SDK框架,支持插件系统
1,278 lines (1,276 loc) • 47.2 kB
JavaScript
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")
});