attempt to create delete event with null entity

本文介绍了一个关于Struts2框架中删除用户操作时出现的NullPointerException异常及其解决方法。异常发生在尝试使用空对象创建删除事件时。解决方案首先需要检查页面属性的对象名称是否已被正确引用。
type Exception report

message

description The server encountered an internal error () that prevented it from fulfilling this request.

exception

[b]javax.servlet.ServletException: java.lang.IllegalArgumentException: attempt to create delete event with null entity[/b] org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:515)
org.apache.struts2.dispatcher.FilterDispatcher.doFilter(FilterDispatcher.java:419)


root cause

java.lang.IllegalArgumentException: attempt to create delete event with null entity
org.hibernate.event.DeleteEvent.<init>(DeleteEvent.java:24)
org.hibernate.impl.SessionImpl.delete(SessionImpl.java:744)
org.springframework.orm.hibernate3.HibernateTemplate$25.doInHibernate(HibernateTemplate.java:790)
org.springframework.orm.hibernate3.HibernateTemplate.execute(HibernateTemplate.java:372)
org.springframework.orm.hibernate3.HibernateTemplate.delete(HibernateTemplate.java:784)
org.springframework.orm.hibernate3.HibernateTemplate.delete(HibernateTemplate.java:780)
com.dao.impl.user.DaoImpl.delete(DaoImpl.java:15)
com.service.impl.user.UserServiceImpl.delete(UserServiceImpl.java:20)
com.action.user.RemoveUserAction.execute(RemoveUserAction.java:35)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
java.lang.reflect.Method.invoke(Unknown Source)
com.opensymphony.xwork2.DefaultActionInvocation.invokeAction(DefaultActionInvocation.java:404)
com.opensymphony.xwork2.DefaultActionInvocation.invokeActionOnly(DefaultActionInvocation.java:267)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:229)
com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor.doIntercept(DefaultWorkflowInterceptor.java:221)
com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:86)
com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:224)
com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:223)
com.opensymphony.xwork2.util.profiling.UtilTimerStack.profile(UtilTimerStack.java:455)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:221)
com.opensymphony.xwork2.validator.ValidationInterceptor.doIntercept(ValidationInterceptor.java:150)
org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor.doIntercept(AnnotationValidationInterceptor.java:48)
com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:86)
com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:224)
com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:223)
com.opensymphony.xwork2.util.profiling.UtilTimerStack.profile(UtilTimerStack.java:455)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:221)
com.opensymphony.xwork2.interceptor.ConversionErrorInterceptor.intercept(ConversionErrorInterceptor.java:123)
com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:224)
com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:223)
com.opensymphony.xwork2.util.profiling.UtilTimerStack.profile(UtilTimerStack.java:455)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:221)
com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:167)
com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:86)
com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:224)
com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:223)
com.opensymphony.xwork2.util.profiling.UtilTimerStack.profile(UtilTimerStack.java:455)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:221)
com.opensymphony.xwork2.interceptor.StaticParametersInterceptor.intercept(StaticParametersInterceptor.java:105)
com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:224)
com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:223)
com.opensymphony.xwork2.util.profiling.UtilTimerStack.profile(UtilTimerStack.java:455)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:221)
org.apache.struts2.interceptor.CheckboxInterceptor.intercept(CheckboxInterceptor.java:83)
com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:224)
com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:223)
com.opensymphony.xwork2.util.profiling.UtilTimerStack.profile(UtilTimerStack.java:455)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:221)
org.apache.struts2.interceptor.FileUploadInterceptor.intercept(FileUploadInterceptor.java:207)
com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:224)
com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:223)
com.opensymphony.xwork2.util.profiling.UtilTimerStack.profile(UtilTimerStack.java:455)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:221)
com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor.intercept(ModelDrivenInterceptor.java:74)
com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:224)
com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:223)
com.opensymphony.xwork2.util.profiling.UtilTimerStack.profile(UtilTimerStack.java:455)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:221)
com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor.intercept(ScopedModelDrivenInterceptor.java:127)
com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:224)
com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:223)
com.opensymphony.xwork2.util.profiling.UtilTimerStack.profile(UtilTimerStack.java:455)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:221)
org.apache.struts2.interceptor.ProfilingActivationInterceptor.intercept(ProfilingActivationInterceptor.java:107)
com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:224)
com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:223)
com.opensymphony.xwork2.util.profiling.UtilTimerStack.profile(UtilTimerStack.java:455)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:221)
org.apache.struts2.interceptor.debugging.DebuggingInterceptor.intercept(DebuggingInterceptor.java:206)
com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:224)
com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:223)
com.opensymphony.xwork2.util.profiling.UtilTimerStack.profile(UtilTimerStack.java:455)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:221)
com.opensymphony.xwork2.interceptor.ChainingInterceptor.intercept(ChainingInterceptor.java:115)
com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:224)
com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:223)
com.opensymphony.xwork2.util.profiling.UtilTimerStack.profile(UtilTimerStack.java:455)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:221)
com.opensymphony.xwork2.interceptor.I18nInterceptor.intercept(I18nInterceptor.java:143)
com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:224)
com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:223)
com.opensymphony.xwork2.util.profiling.UtilTimerStack.profile(UtilTimerStack.java:455)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:221)
com.opensymphony.xwork2.interceptor.PrepareInterceptor.doIntercept(PrepareInterceptor.java:121)
com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:86)
com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:224)
com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:223)
com.opensymphony.xwork2.util.profiling.UtilTimerStack.profile(UtilTimerStack.java:455)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:221)
org.apache.struts2.interceptor.ServletConfigInterceptor.intercept(ServletConfigInterceptor.java:170)
com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:224)
com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:223)
com.opensymphony.xwork2.util.profiling.UtilTimerStack.profile(UtilTimerStack.java:455)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:221)
com.opensymphony.xwork2.interceptor.AliasInterceptor.intercept(AliasInterceptor.java:123)
com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:224)
com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:223)
com.opensymphony.xwork2.util.profiling.UtilTimerStack.profile(UtilTimerStack.java:455)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:221)
com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor.intercept(ExceptionMappingInterceptor.java:176)
com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:224)
com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:223)
com.opensymphony.xwork2.util.profiling.UtilTimerStack.profile(UtilTimerStack.java:455)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:221)
org.apache.struts2.impl.StrutsActionProxy.execute(StrutsActionProxy.java:50)
org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:504)
org.apache.struts2.dispatcher.FilterDispatcher.doFilter(FilterDispatcher.java:419)

[color=red][size=medium]遇到以上问题的解决办法是:首先检查页面属性的对象名称是否已正确引用[/size][/color]
/* 文件路径: data/DataManager.js */ /** * DataManager - 数据管理主类 * 整合所有功能模块并保持原有API不变 */ import { DataManagerCore } from './core/dataManagerCore.js'; import { createEntityAccessors } from './utils/entityAccessors.js'; import { checkDuplicate } from './utils/dataUtils.js'; /** * 数据管理器类 * 提供统一的数据访问、操作和同步接口 */ export class DataManager { constructor() { // 初始化核心管理器 this.core = new DataManagerCore(); // 混入实体访问器方法 const accessors = createEntityAccessors(this.core); Object.assign(this, accessors); // 绑定方法到当前实例 this.bindMethods(); } /** * 绑定方法到当前实例 */ bindMethods() { // 绑定核心方法 const coreMethods = [ 'initialize', 'createEmptyData', 'registerCallback', 'unregisterCallback', 'triggerCallbacks', 'crudOperation', 'updateLocalData', 'loadDataFromStorage', 'saveDataToStorage', 'startAutoSync', 'stopAutoSync', 'initNetwork', 'syncData', 'fetchAll', 'fetchEntityData', 'initWebSocket', 'handleWebSocketUpdate', 'refreshEntityData', 'updateWechatUser', 'transactionalOperation' ]; coreMethods.forEach(methodName => { if (typeof this.core[methodName] === 'function') { this[methodName] = this.core[methodName].bind(this.core); } }); // 绑定存储方法 const storageMethods = [ 'getEntityById', 'addEntity', 'updateEntity', 'deleteEntity', 'addEntities', 'clearEntities' ]; storageMethods.forEach(methodName => { if (typeof this.core.storage[methodName] === 'function') { this[methodName] = this.core.storage[methodName].bind(this.core.storage); } }); } /** * 获取数据访问器 */ get data() { return this.core.data; } /** * 从本地存储加载数据 * @returns {boolean} 加载是否成功 */ loadDataFromStorage() { return this.core.storage.loadDataFromStorage(); } /** * 保存数据到本地存储 * @returns {boolean} 保存是否成功 */ saveDataToStorage() { return this.core.storage.saveDataToStorage(); } /** * 初始化网络状态监听 */ initNetwork() { // 此方法已在DataSync初始化时调用 console.log('Network initialized'); } /** * 检查实体是否重复 * @param {string} entityType - 实体类型 * @param {Object} newEntity - 新实体 * @returns {boolean} 是否重复 */ checkDuplicate(entityType, newEntity) { const entities = this.core.storage.getEntities(entityType); return checkDuplicate(entities, newEntity); } // ===== 以下是为了保持向后兼容的额外方法 ===== /** * 获取特定类型的所有实体 * @param {string} entityType - 实体类型 * @returns {Array} 实体数组 */ getAllEntities(entityType) { return this.core.storage.getEntities(entityType); } /** * 批量更新实体 * @param {string} entityType - 实体类型 * @param {Array} entities - 实体数组 * @returns {Array} 更新结果数组 */ bulkUpdateEntities(entityType, entities) { return entities.map(entity => this.core.storage.updateEntity(entityType, entity)); } /** * 批量删除实体 * @param {string} entityType - 实体类型 * @param {Array} ids - 实体ID数组 * @returns {Array} 删除结果数组 */ bulkDeleteEntities(entityType, ids) { return ids.map(id => this.core.storage.deleteEntity(entityType, id)); } /** * 清空所有数据 */ clearAllData() { return this.core.storage.clearAllData(); } } // 创建默认实例 export const dataManager = new DataManager(); ================================================================================ /* 文件路径: data/core/dataManagerCore.js */ /** * DataManager核心类 * 整合所有组件并提供统一的数据管理接口 */ import { DataStorage } from './dataStorage.js'; import { DataSync } from './dataSync.js'; import { LazyLoader } from '../components/LazyLoader.js'; import { TransactionApi } from '../components/TransactionApi.js'; import { WebSocketClient } from '../components/WebSocketClient.js'; import { resolveDataReferences } from '../utils/dataUtils.js'; export class DataManagerCore { constructor() { // 初始化各个组件 this.storage = new DataStorage(); this.sync = new DataSync(this); this.lazyLoader = new LazyLoader(this); this.transactionApi = new TransactionApi(this); this.webSocketClient = new WebSocketClient(this); // 初始化回调管理 this.callbacks = {}; // 初始化数据 this.initialize(); } /** * 初始化数据管理器 */ initialize() { // 从本地存储加载数据 this.storage.loadDataFromStorage(); // 初始化WebSocket连接 this.initWebSocket(); } /** * 创建空数据对象 * @returns {Object} 空数据对象 */ createEmptyData() { return { dingdans: [], chanpins: [], zujians: [], bancais: [], cailiaos: [], xiaoshous: [], shengchans: [], xiaohaos: [], wechatUsers: [] }; } /** * 获取数据访问器 */ get data() { return this.storage.data; } // ===== 回调管理 ===== /** * 注册回调函数 * @param {string} eventType - 事件类型 * @param {Function} callback - 回调函数 * @returns {Function} 取消注册的函数 */ registerCallback(eventType, callback) { if (!this.callbacks[eventType]) { this.callbacks[eventType] = []; } this.callbacks[eventType].push(callback); // 返回取消注册的函数 return () => this.unregisterCallback(eventType, callback); } /** * 取消注册回调函数 * @param {string} eventType - 事件类型 * @param {Function} callback - 回调函数 */ unregisterCallback(eventType, callback) { if (!this.callbacks[eventType]) { return; } this.callbacks[eventType] = this.callbacks[eventType].filter(cb => cb !== callback); } /** * 触发回调函数 * @param {string} eventType - 事件类型 * @param {*} data - 回调数据 */ triggerCallbacks(eventType, data) { if (!this.callbacks[eventType]) { return; } this.callbacks[eventType].forEach(callback => { try { callback(data); } catch (error) { console.error(`Error in callback for ${eventType}:`, error); } }); } // ===== WebSocket相关 ===== /** * 初始化WebSocket连接 */ initWebSocket() { this.webSocketClient.connect(); } /** * 处理WebSocket更新 * @param {Object} updateData - 更新数据 */ handleWebSocketUpdate(updateData) { if (!updateData || !updateData.type) { return; } switch (updateData.type) { case 'entity_update': this.handleEntityUpdate(updateData); break; case 'entity_delete': this.handleEntityDelete(updateData); break; case 'sync_complete': this.triggerCallbacks('syncComplete', updateData); break; default: this.triggerCallbacks('ws_update', updateData); break; } } /** * 处理实体更新 * @param {Object} updateData - 更新数据 */ handleEntityUpdate(updateData) { const { entityType, entity } = updateData; if (entityType && entity) { this.updateEntity(entityType, entity); this.triggerCallbacks(`${entityType}_updated`, entity); } } /** * 处理实体删除 * @param {Object} updateData - 更新数据 */ handleEntityDelete(updateData) { const { entityType, entityId } = updateData; if (entityType && entityId) { this.deleteEntity(entityType, entityId); this.triggerCallbacks(`${entityType}_deleted`, { id: entityId }); } } // ===== 实体操作方法 ===== /** * 执行CRUD操作 * @param {string} operation - 操作类型 * @param {string} entityType - 实体类型 * @param {Object|Array} data - 操作数据 * @returns {Promise} 操作结果Promise */ async crudOperation(operation, entityType, data) { try { let result; switch (operation.toLowerCase()) { case 'create': case 'add': result = Array.isArray(data) ? this.storage.addEntities(entityType, data) : this.storage.addEntity(entityType, data); break; case 'read': case 'get': result = data ? this.storage.getEntityById(entityType, data) : this.storage.getEntities(entityType); break; case 'update': result = Array.isArray(data) ? data.map(item => this.storage.updateEntity(entityType, item)) : this.storage.updateEntity(entityType, data); break; case 'delete': case 'remove': result = Array.isArray(data) ? data.map(id => this.storage.deleteEntity(entityType, id)) : this.storage.deleteEntity(entityType, data); break; default: throw new Error(`Unknown operation: ${operation}`); } // 保存到本地存储 this.storage.saveDataToStorage(); // 触发回调 this.triggerCallbacks(`${entityType}_${operation}d`, result); return result; } catch (error) { console.error(`Error performing ${operation} on ${entityType}:`, error); throw error; } } /** * 更新本地数据 * @param {Object} newData - 新数据 */ updateLocalData(newData) { for (const entityType in newData) { if (Object.prototype.hasOwnProperty.call(newData, entityType)) { const entities = newData[entityType]; if (Array.isArray(entities)) { this.storage.addEntities(entityType, entities); } } } // 保存到本地存储 this.storage.saveDataToStorage(); } /** * 刷新实体数据 * @param {string} entityType - 实体类型 * @returns {Promise} 刷新结果Promise */ async refreshEntityData(entityType) { const data = await this.sync.fetchEntityData(entityType); this.triggerCallbacks(`${entityType}_refreshed`, data); return data; } /** * 执行事务操作 * @param {Function} operation - 操作函数 * @returns {Promise} 操作结果Promise */ transactionalOperation(operation) { return this.transactionApi.executeTransaction(operation); } /** * 更新微信用户信息 * @param {Object} userInfo - 用户信息 * @returns {Promise} 更新结果Promise */ async updateWechatUser(userInfo) { return this.crudOperation('update', 'wechatUsers', userInfo); } } ================================================================================ /* 文件路径: data/core/dataStorage.js */ /** * 数据存储核心类 * 提供数据的基本CRUD操作和本地存储功能 */ import { removeCircularReferences } from '../utils/dataUtils.js'; const STORAGE_KEY = 'inventory_data'; export class DataStorage { constructor() { this.data = { dingdans: [], chanpins: [], zujians: [], bancais: [], cailiaos: [], xiaoshous: [], shengchans: [], xiaohaos: [], wechatUsers: [] }; this.entityTypes = Object.keys(this.data); } /** * 获取特定类型的所有实体 * @param {string} entityType - 实体类型 * @returns {Array} 实体数组 */ getEntities(entityType) { if (!this.entityTypes.includes(entityType)) { console.warn(`Unknown entity type: ${entityType}`); return []; } return this.data[entityType] || []; } /** * 根据ID获取特定实体 * @param {string} entityType - 实体类型 * @param {string|number} id - 实体ID * @returns {Object|null} 实体对象或null */ getEntityById(entityType, id) { const entities = this.getEntities(entityType); return entities.find(entity => entity.id === id) || null; } /** * 添加实体 * @param {string} entityType - 实体类型 * @param {Object} entity - 实体对象 * @returns {Object} 添加后的实体对象 */ addEntity(entityType, entity) { if (!this.entityTypes.includes(entityType)) { console.warn(`Cannot add to unknown entity type: ${entityType}`); return entity; } if (!this.data[entityType]) { this.data[entityType] = []; } // 检查是否已存在相同ID的实体 const existingIndex = this.data[entityType].findIndex(e => e.id === entity.id); if (existingIndex >= 0) { // 如果已存在,更新它 this.data[entityType][existingIndex] = entity; } else { // 否则添加新实体 this.data[entityType].push(entity); } return entity; } /** * 更新实体 * @param {string} entityType - 实体类型 * @param {Object} entity - 实体对象 * @returns {boolean} 更新是否成功 */ updateEntity(entityType, entity) { if (!this.entityTypes.includes(entityType)) { console.warn(`Cannot update unknown entity type: ${entityType}`); return false; } if (!entity.id) { console.warn('Cannot update entity without ID'); return false; } const entities = this.data[entityType]; const index = entities.findIndex(e => e.id === entity.id); if (index === -1) { console.warn(`Entity not found: ${entityType} with ID ${entity.id}`); return false; } entities[index] = entity; return true; } /** * 删除实体 * @param {string} entityType - 实体类型 * @param {string|number} id - 实体ID * @returns {boolean} 删除是否成功 */ deleteEntity(entityType, id) { if (!this.entityTypes.includes(entityType)) { console.warn(`Cannot delete from unknown entity type: ${entityType}`); return false; } const entities = this.data[entityType]; const index = entities.findIndex(e => e.id === id); if (index === -1) { console.warn(`Entity not found: ${entityType} with ID ${id}`); return false; } entities.splice(index, 1); return true; } /** * 批量添加实体 * @param {string} entityType - 实体类型 * @param {Array} entities - 实体数组 * @returns {Array} 添加后的实体数组 */ addEntities(entityType, entities) { if (!Array.isArray(entities)) { console.warn('Entities must be an array'); return []; } return entities.map(entity => this.addEntity(entityType, entity)); } /** * 清空特定类型的所有实体 * @param {string} entityType - 实体类型 */ clearEntities(entityType) { if (!this.entityTypes.includes(entityType)) { console.warn(`Cannot clear unknown entity type: ${entityType}`); return; } this.data[entityType] = []; } /** * 从本地存储加载数据 * @returns {boolean} 加载是否成功 */ loadDataFromStorage() { try { const storedData = localStorage.getItem(STORAGE_KEY); if (storedData) { const parsedData = JSON.parse(storedData); // 合并存储的数据到当前数据对象 for (const entityType in parsedData) { if (Object.prototype.hasOwnProperty.call(parsedData, entityType) && this.entityTypes.includes(entityType)) { this.data[entityType] = parsedData[entityType]; } } return true; } } catch (error) { console.error('Failed to load data from storage:', error); } return false; } /** * 保存数据到本地存储 * @returns {boolean} 保存是否成功 */ saveDataToStorage() { try { // 移除循环引用 const dataToSave = removeCircularReferences(this.data); localStorage.setItem(STORAGE_KEY, JSON.stringify(dataToSave)); return true; } catch (error) { console.error('Failed to save data to storage:', error); } return false; } /** * 清空所有数据 */ clearAllData() { for (const entityType of this.entityTypes) { this.data[entityType] = []; } localStorage.removeItem(STORAGE_KEY); } } ================================================================================ /* 文件路径: data/core/dataSync.js */ /** * 数据同步核心类 * 负责处理数据与服务器的同步、自动同步和网络状态监听 */ export class DataSync { constructor(dataManager, autoSyncInterval = 30000) { this.dataManager = dataManager; this.autoSyncInterval = autoSyncInterval; // 默认30秒 this.syncTimer = null; this.isAutoSyncEnabled = false; this.isOnline = true; this.pendingOperations = []; // 初始化网络状态监听 this.initNetworkStatus(); } /** * 初始化网络状态监听 */ initNetworkStatus() { // 监听在线状态变化 window.addEventListener('online', () => this.handleNetworkChange(true)); window.addEventListener('offline', () => this.handleNetworkChange(false)); // 初始状态 this.isOnline = window.navigator.onLine; } /** * 处理网络状态变化 * @param {boolean} isOnline - 是否在线 */ handleNetworkChange(isOnline) { this.isOnline = isOnline; if (isOnline) { console.log('Network connection restored, syncing pending operations...'); // 当网络恢复时,同步待处理的操作 this.syncPendingOperations(); } else { console.log('Network connection lost'); } // 触发回调通知网络状态变化 this.dataManager.triggerCallbacks('networkStatusChange', { isOnline }); } /** * 同步待处理的操作 */ async syncPendingOperations() { while (this.pendingOperations.length > 0) { const operation = this.pendingOperations.shift(); try { await operation(); } catch (error) { console.error('Failed to sync pending operation:', error); // 如果同步失败,将操作重新添加到队列末尾 this.pendingOperations.push(operation); break; } } } /** * 开始自动同步 */ startAutoSync() { if (this.isAutoSyncEnabled) { return; } this.isAutoSyncEnabled = true; this.syncTimer = setInterval(() => { if (this.isOnline) { this.syncData(); } }, this.autoSyncInterval); console.log('Auto-sync started with interval:', this.autoSyncInterval); } /** * 停止自动同步 */ stopAutoSync() { if (this.syncTimer) { clearInterval(this.syncTimer); this.syncTimer = null; } this.isAutoSyncEnabled = false; console.log('Auto-sync stopped'); } /** * 同步数据到服务器 * @returns {Promise} 同步结果Promise */ async syncData() { if (!this.isOnline) { console.warn('Cannot sync data while offline'); return false; } try { // 收集所有需要同步的数据 const syncData = this.collectSyncData(); if (Object.keys(syncData).length === 0) { console.log('No data to sync'); return true; } const response = await fetch('/api/sync', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(syncData) }); if (!response.ok) { throw new Error(`Sync failed with status: ${response.status}`); } const result = await response.json(); // 处理同步结果 this.handleSyncResult(result); console.log('Data synced successfully'); return true; } catch (error) { console.error('Error syncing data:', error); // 如果同步失败,将操作添加到待处理队列 this.pendingOperations.push(() => this.syncData()); return false; } } /** * 收集需要同步的数据 * @returns {Object} 同步数据对象 */ collectSyncData() { const syncData = {}; // 在实际应用中,这里应该只收集需要同步的变更数据 // 为简化实现,我们收集所有数据 for (const entityType of this.dataManager.storage.entityTypes) { const entities = this.dataManager.storage.getEntities(entityType); if (entities.length > 0) { syncData[entityType] = entities; } } return syncData; } /** * 处理同步结果 * @param {Object} result - 同步结果 */ handleSyncResult(result) { // 在实际应用中,这里应该根据服务器返回的结果更新本地数据 // 例如,处理冲突、应用服务器端的变更等 if (result.success && result.updatedEntities) { // 应用服务器端的更新 for (const entityType in result.updatedEntities) { const updatedEntities = result.updatedEntities[entityType]; updatedEntities.forEach(entity => { this.dataManager.storage.updateEntity(entityType, entity); }); } } } /** * 从服务器获取数据 * @param {Array} entityTypes - 要获取的实体类型数组 * @returns {Promise} 获取结果Promise */ async fetchData(entityTypes) { if (!this.isOnline) { console.warn('Cannot fetch data while offline'); return false; } try { const response = await fetch('/api/data', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ entityTypes }) }); if (!response.ok) { throw new Error(`Fetch data failed with status: ${response.status}`); } const data = await response.json(); // 处理获取的数据 this.handleFetchedData(data); return true; } catch (error) { console.error('Error fetching data:', error); return false; } } /** * 处理获取的数据 * @param {Object} data - 获取的数据 */ handleFetchedData(data) { // 将获取的数据保存到存储中 for (const entityType in data) { if (Object.prototype.hasOwnProperty.call(data, entityType)) { const entities = data[entityType]; if (Array.isArray(entities)) { this.dataManager.storage.clearEntities(entityType); this.dataManager.storage.addEntities(entityType, entities); } } } } /** * 获取所有实体数据 * @returns {Promise} 获取结果Promise */ fetchAll() { return this.fetchData(this.dataManager.storage.entityTypes); } /** * 获取特定实体类型的数据 * @param {string} entityType - 实体类型 * @param {Object} filters - 过滤条件 * @returns {Promise} 获取结果Promise */ async fetchEntityData(entityType, filters = {}) { if (!this.isOnline) { console.warn('Cannot fetch entity data while offline'); return []; } try { const response = await fetch(`/api/data/${entityType}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(filters) }); if (!response.ok) { throw new Error(`Fetch ${entityType} data failed with status: ${response.status}`); } const data = await response.json(); // 将获取的数据保存到存储中 if (Array.isArray(data)) { this.dataManager.storage.clearEntities(entityType); this.dataManager.storage.addEntities(entityType, data); return data; } return []; } catch (error) { console.error(`Error fetching ${entityType} data:`, error); return []; } } } ================================================================================ /* 文件路径: data/utils/dataUtils.js */ /** * 数据工具函数 * 提供数据引用解析、循环引用处理等功能 */ /** * 解析数据对象中的所有引用 * @param {Object} data - 要解析的数据对象 * @param {Object} entitiesMap - 实体映射表 * @returns {Object} 解析后的对象 */ export function resolveDataReferences(data, entitiesMap) { if (!data || typeof data !== 'object') { return data; } if (Array.isArray(data)) { return data.map(item => resolveDataReferences(item, entitiesMap)); } const result = {}; for (const key in data) { if (Object.prototype.hasOwnProperty.call(data, key)) { result[key] = resolveDataReference(data[key], entitiesMap); } } return result; } /** * 解析单个数据引用 * @param {*} value - 要解析的值 * @param {Object} entitiesMap - 实体映射表 * @returns {*} 解析后的值 */ export function resolveDataReference(value, entitiesMap) { if (!value || typeof value !== 'object') { return value; } if (Array.isArray(value)) { return value.map(item => resolveDataReference(item, entitiesMap)); } // 检查是否是引用对象 if (value.__ref && value.entityType && entitiesMap[value.entityType]) { const entity = entitiesMap[value.entityType].find(e => e.id === value.__ref); return entity || value; } // 递归处理嵌套对象 const result = {}; for (const key in value) { if (Object.prototype.hasOwnProperty.call(value, key)) { result[key] = resolveDataReference(value[key], entitiesMap); } } return result; } /** * 移除对象中的循环引用 * @param {Object} obj - 要处理的对象 * @returns {Object} 处理后的对象 */ export function removeCircularReferences(obj) { const seen = new WeakSet(); const replacer = (key, value) => { if (typeof value === 'object' && value !== null) { if (seen.has(value)) { return undefined; } seen.add(value); } return value; }; return JSON.parse(JSON.stringify(obj, replacer)); } /** * 检查实体是否重复 * @param {Array} entities - 实体数组 * @param {Object} newEntity - 新实体 * @returns {boolean} 是否重复 */ export function checkDuplicate(entities, newEntity) { if (!entities || !Array.isArray(entities)) { return false; } return entities.some(entity => entity.id === newEntity.id); } ================================================================================ /* 文件路径: data/utils/entityAccessors.js */ /** * 实体访问器函数 * 提供各种实体数据的访问方法 */ import { resolveDataReferences } from './dataUtils.js'; /** * 创建实体访问器方法 * @param {DataManagerCore} dataManager - 数据管理器实例 * @returns {Object} 访问器方法对象 */ export function createEntityAccessors(dataManager) { const accessors = {}; // 创建实体访问方法 const entityTypes = [ 'dingdans', 'chanpins', 'zujians', 'bancais', 'cailiaos', 'xiaoshous', 'shengchans', 'xiaohaos', 'wechatUsers' ]; // 创建同步访问方法 entityTypes.forEach(entityType => { const methodName = `get${capitalize(entityType)}`; accessors[methodName] = function() { const entities = dataManager.storage.getEntities(entityType); return dataManager.lazyLoader.proxyEntities(entities, entityType); }; }); // 创建异步访问方法 entityTypes.forEach(entityType => { const methodName = `get${capitalize(entityType)}Async`; accessors[methodName] = async function(filters = {}) { // 先尝试从缓存获取 let entities = dataManager.storage.getEntities(entityType); // 如果没有数据或强制刷新,则从服务器获取 if (entities.length === 0 || filters.forceRefresh) { entities = await dataManager.sync.fetchEntityData(entityType, filters); } // 应用过滤器 if (filters) { entities = applyFilters(entities, filters); } return dataManager.lazyLoader.proxyEntities(entities, entityType); }; }); // 创建关联数据查询方法 accessors.getChanpinsForDingdan = function(dingdanId) { const chanpins = dataManager.storage.getEntities('chanpins'); return chanpins.filter(chanpin => chanpin.dingdanId === dingdanId); }; accessors.getZujiansForChanpin = function(chanpinId) { const zujians = dataManager.storage.getEntities('zujians'); return zujians.filter(zujian => zujian.chanpinId === chanpinId); }; accessors.getShengchanXiaohaoRecords = function(params = {}) { const { startDate, endDate, chanpinId } = params; const shengchans = dataManager.storage.getEntities('shengchans'); const xiaohaos = dataManager.storage.getEntities('xiaohaos'); // 合并生产和消耗记录 let records = [...shengchans, ...xiaohaos]; // 应用日期过滤 if (startDate) { records = records.filter(record => new Date(record.date) >= new Date(startDate)); } if (endDate) { records = records.filter(record => new Date(record.date) <= new Date(endDate)); } // 应用产品过滤 if (chanpinId) { records = records.filter(record => record.chanpinId === chanpinId); } // 按日期排序 records.sort((a, b) => new Date(a.date) - new Date(b.date)); return records; }; accessors.getShengchanStatistics = function(params = {}) { const { startDate, endDate } = params; const shengchans = dataManager.storage.getEntities('shengchans'); // 应用日期过滤 let filteredShengchans = shengchans; if (startDate) { filteredShengchans = filteredShengchans.filter(record => new Date(record.date) >= new Date(startDate)); } if (endDate) { filteredShengchans = filteredShengchans.filter(record => new Date(record.date) <= new Date(endDate)); } // 计算统计数据 const statistics = {}; filteredShengchans.forEach(record => { if (!statistics[record.chanpinId]) { statistics[record.chanpinId] = { chanpinId: record.chanpinId, totalQuantity: 0, records: [] }; } statistics[record.chanpinId].totalQuantity += record.quantity; statistics[record.chanpinId].records.push(record); }); // 转换为数组并添加产品名称 const chanpinsMap = createEntityMap(dataManager.storage.getEntities('chanpins')); return Object.values(statistics).map(stat => { const chanpin = chanpinsMap[stat.chanpinId]; return { ...stat, chanpinName: chanpin ? chanpin.name : 'Unknown' }; }); }; return accessors; } /** * 首字母大写 * @param {string} str - 字符串 * @returns {string} 首字母大写的字符串 */ function capitalize(str) { return str.charAt(0).toUpperCase() + str.slice(1); } /** * 应用过滤器 * @param {Array} entities - 实体数组 * @param {Object} filters - 过滤条件 * @returns {Array} 过滤后的实体数组 */ function applyFilters(entities, filters) { return entities.filter(entity => { for (const key in filters) { if (key !== 'forceRefresh' && filters[key] !== undefined) { if (entity[key] !== filters[key]) { return false; } } } return true; }); } /** * 创建实体映射 * @param {Array} entities - 实体数组 * @returns {Object} 实体ID到实体的映射 */ function createEntityMap(entities) { const map = {}; entities.forEach(entity => { map[entity.id] = entity; }); return map; } ================================================================================ /* 文件路径: data/index.js */ /** * 数据管理模块入口 * 导出所有公共API,保持向后兼容 */ import { DataManager, dataManager } from './DataManager.js'; import { DataStorage } from './core/dataStorage.js'; import { DataSync } from './core/dataSync.js'; import { LazyLoader } from './components/LazyLoader.js'; import { TransactionApi } from './components/TransactionApi.js'; import { WebSocketClient } from './components/WebSocketClient.js'; import { resolveDataReferences, resolveDataReference, removeCircularReferences, checkDuplicate } from './utils/dataUtils.js'; // 导出所有公共API export { DataManager, dataManager, DataStorage, DataSync, LazyLoader, TransactionApi, WebSocketClient, resolveDataReferences, resolveDataReference, removeCircularReferences, checkDuplicate }; // 导出默认模块 export default dataManager; ================================================================================ /* 文件路径: data/LazyLoader.js */ /** * 懒加载器 - Web版 (优化版) * 修复了缓存键设计、集合名解析和数组处理问题 * 负责为数据对象创建代理,实现关联数据的延迟加载 * 支持按需加载机制 */ class LazyLoader { /** * 构造函数 * @param {DataManager} dataManager - 数据管理器实例 */ constructor(dataManager) { this.dataManager = dataManager; // 数据管理器引用 // 使用WeakMap避免内存泄漏 this.proxyCache = new WeakMap(); // 代理对象缓存,避免重复创建代理 // 实体名到集合名的映射表 this.entityToCollectionMap = { bancai: 'bancais', dingdan: 'dingdans', mupi: 'mupis', chanpin: 'chanpins', kucun: 'kucuns', chanpin_zujian: 'chanpin_zujians', dingdan_bancai: 'dingdan_bancais', zujian: 'zujians', caizhi: 'caizhis', dingdan_chanpin: 'dingdan_chanpins', user: 'users', jinhuo: 'jinhuos' }; // 集合名到实体名的映射表(反向映射) this.collectionToEntityMap = {}; for (const [entity, collection] of Object.entries(this.entityToCollectionMap)) { this.collectionToEntityMap[collection] = entity; } } /** * 清除所有缓存 * 用于数据刷新后重置所有缓存状态 */ clearCache() { this.proxyCache = new WeakMap(); // 重新创建WeakMap以清空缓存 } /** * 创建数据代理 * 为对象添加延迟加载关联数据的能力 * @param {Object} item - 原始数据项 * @param {string} entityType - 实体类型 * @returns {Proxy} 具有延迟加载能力的代理对象 */ createProxy(item, entityType) { // 非对象直接返回 if (!item || typeof item !== 'object') return item; // 检查是否已有代理 if (this.proxyCache.has(item)) { return this.proxyCache.get(item); } const proxy = new Proxy(item, { get: (target, prop) => { // 直接返回基本属性、符号属性或以下划线开头的属性 if (typeof prop === 'symbol' || prop.startsWith('_') || typeof target[prop] !== 'object') { return target[prop]; } const value = target[prop]; // 处理null值 if (value === null) return null; // 处理数组关联 - 对数组中的每个元素进行关系解析 if (Array.isArray(value)) { return value.map(relatedItem => this.resolveRelation(relatedItem, prop) ); } // 处理对象关联 - 对单个对象进行关系解析 else if (typeof value === 'object') { return this.resolveRelation(value, prop); } return value; }, set: (target, prop, value) => { // 允许设置属性值 target[prop] = value; return true; } }); // 缓存代理对象,避免重复创建 this.proxyCache.set(item, proxy); return proxy; } /** * 解析关联对象 * 根据属性名和关联项获取完整的关联对象 * 支持按需加载关联数据 * @param {Object|string|number} relatedItem - 关联项 * @param {string} propName - 属性名 * @returns {Object} 解析后的关联对象 */ resolveRelation(relatedItem, propName) { // 如果关联项是简单的ID值 if (typeof relatedItem === 'string' || typeof relatedItem === 'number') { // 从属性名推断关联的实体类型 const entityType = this.getEntityTypeFromPropName(propName); if (!entityType) return relatedItem; // 查找对应的实体 const collectionName = this.entityToCollectionMap[entityType.replace(/s$/, '')] || entityType; const collection = this.dataManager._rawData[collectionName]; // 如果集合不存在或为空,但存在关联ID,则返回原始ID(按需加载时可能发生) if (!collection || !Array.isArray(collection) || collection.length === 0) { return relatedItem; } // 查找匹配的实体 const matchedEntity = collection.find(item => item.id === relatedItem); return matchedEntity ? this.createProxy(matchedEntity, entityType.replace(/s$/, '')) : relatedItem; } // 如果关联项是对象,创建代理 if (typeof relatedItem === 'object' && relatedItem !== null) { // 从属性名推断实体类型 const entityType = this.getEntityTypeFromPropName(propName); if (entityType) { return this.createProxy(relatedItem, entityType.replace(/s$/, '')); } return this.createProxy(relatedItem, null); } return relatedItem; } /** * 从集合名获取实体类型 * @param {string} collectionName - 集合名称 * @returns {string|null} 实体类型名称 */ getEntityTypeFromCollection(collectionName) { return this.collectionToEntityMap[collectionName] || null; } /** * 从属性名获取实体类型 * @param {string} propName - 属性名称 * @returns {string|null} 实体类型名称 */ getEntityTypeFromPropName(propName) { // 处理常见的关联属性命名模式 if (propName.endsWith('Id') || propName.endsWith('_id')) { // 例如: userId -> user, dingdan_id -> dingdan const baseName = propName.replace(/Id$/, '').replace(/_id$/, ''); return this.entityToCollectionMap[baseName.toLowerCase()] || null; } // 直接查找属性名对应的实体类型 const entityType = this.collectionToEntityMap[propName]; if (entityType) { return this.entityToCollectionMap[entityType]; } // 尝试单复数转换 if (propName.endsWith('s')) { const singular = propName.slice(0, -1); return this.entityToCollectionMap[singular] || null; } return null; } } export { LazyLoader }; ================================================================================ /* 文件路径: data/components/LazyLoader.js */ /** * 延迟加载器组件 * 为数据对象创建代理,实现关联数据的延迟加载 */ export class LazyLoader { constructor(dataManager) { this.dataManager = dataManager; } /** * 为对象创建延迟加载代理 * @param {Object} obj - 要代理的对象 * @param {string} entityType - 实体类型 * @returns {Proxy} 代理对象 */ createProxy(obj, entityType) { const self = this; return new Proxy(obj, { get(target, property) { // 正常属性访问 if (property in target) { return target[property]; } // 处理延迟加载属性 if (property.startsWith('get') && property.endsWith('Async')) { const methodName = property; return function() { return self.dataManager[methodName].apply(self.dataManager, arguments); }; } // 尝试加载关联数据 return self.loadRelatedData(target, property, entityType); }, set(target, property, value) { target[property] = value; return true; } }); } /** * 加载关联数据 * @param {Object} entity - 实体对象 * @param {string} relationName - 关联名称 * @param {string} entityType - 实体类型 * @returns {Promise|undefined} 关联数据或undefined */ loadRelatedData(entity, relationName, entityType) { // 检查是否有对应的关联数据加载方法 const relationMethodMap = { 'chanpins': 'getChanpinsForDingdan', 'zujians': 'getZujiansForChanpin', 'dingdan': 'getDingdanForChanpin', 'bancai': 'getBancaiForZuJian' // 可以添加更多的关联映射 }; const methodName = relationMethodMap[relationName]; if (methodName && this.dataManager[methodName]) { // 返回一个Promise,用于异步加载数据 return this.dataManager[methodName](entity.id); } return undefined; } /** * 批量为实体数组创建代理 * @param {Array} entities - 实体数组 * @param {string} entityType - 实体类型 * @returns {Array} 代理对象数组 */ proxyEntities(entities, entityType) { if (!Array.isArray(entities)) { return entities; } return entities.map(entity => this.createProxy(entity, entityType)); } } ================================================================================ /* 文件路径: data/TransactionApi.js */ // @ts-nocheck /** * 事务API类 * 提供复合数据操作的事务支持 * 处理需要原子性执行的多个相关操作 * 支持按需加载机制 */ class TransactionApi { /** * 构造函数 * @param {DataManager} dataManager - 数据管理器实例 */ constructor(dataManager) { this.dataManager = dataManager; // 数据管理器引用,用于访问数据和基本操作 this.baseUrl = dataManager.baseUrl; } /** * 执行事务操作 * 根据端点名称调用对应的事务接口 * @param {string} endpoint - 事务端点名称 * @param {Object} data - 事务数据 * @returns {Promise<Object>} 事务执行结果 */ async execute(endpoint, data) { try { // 调用API执行事务 const response = await fetch(`${this.baseUrl}/app/transactional/${endpoint}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); // 检查响应状态 if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } // 解析响应结果 const result = await response.json(); if (result.status !== 200) { throw new Error(result.text || 'Transaction failed'); } // 事务成功后,根据端点类型决定是否需要刷新特定实体数据 // 不再同步所有数据,而是只刷新受影响的数据 await this.refreshRelatedData(endpoint, result.data); // 触发事务成功回调 this.dataManager.triggerCallbacks('transaction_success', endpoint, result.data); return result.data; } catch (error) { console.error(`Transaction ${endpoint} failed:`, error); // 触发事务失败回调 this.dataManager.triggerCallbacks('transaction_error', endpoint, { data, error: error.message }); throw error; } } /** * 根据事务端点和结果刷新相关数据 * 按需刷新,而不是刷新所有数据 * @param {string} endpoint - 事务端点名称 * @param {Object} resultData - 事务结果数据 * @returns {Promise<void>} */ async refreshRelatedData(endpoint, resultData) { try { // 根据端点名称确定需要刷新的实体类型 const entitiesToRefresh = this.getEntitiesToRefresh(endpoint); // 刷新受影响的实体数据 for (const entityType of entitiesToRefresh) { // 标记实体类型为未加载,下次访问时会重新获取 if (this.dataManager.loadedEntities) { this.dataManager.loadedEntities.delete(entityType); } } // 清除懒加载缓存 if (this.dataManager.lazyLoader) { this.dataManager.lazyLoader.clearCache(); } } catch (error) { console.error('Failed to refresh related data:', error); // 失败时不抛出异常,避免影响事务整体结果 } } /** * 根据事务端点确定需要刷新的实体类型 * @param {string} endpoint - 事务端点名称 * @returns {Array<string>} 需要刷新的实体类型列表 */ getEntitiesToRefresh(endpoint) { // 定义事务端点与影响实体类型的映射关系 const endpointToEntitiesMap = { 'kucunbianji': ['kucun', 'jinhuo'], // 库存编辑影响库存和进货记录 'shengchanXiaohao': ['kucun', 'jinhuo', 'dingdan_bancai'], // 生产消耗影响库存、进货记录和订单板材 'batchUpdateDingdan_bancai': ['dingdan_bancai'], // 批量更新订单板材 'submitDingdan_bancai': ['dingdan_bancai', 'kucun'], // 提交订单板材影响订单板材和库存 'consumeDingdan_bancai': ['dingdan_bancai', 'kucun', 'jinhuo'], // 消耗订单板材影响订单板材、库存和进货记录 '采购Dingdan_bancai': ['dingdan_bancai', 'kucun', 'jinhuo'] // 采购订单板材影响订单板材、库存和进货记录 }; // 返回与端点相关的实体类型列表,如果没有匹配项则返回空数组 return endpointToEntitiesMap[endpoint] || []; } /** * 库存编辑事务 * 更新指定材料的库存数量 * @param {Object} params - 事务参数 * @param {Number} params.kucunId - 库存ID * @param {Number} params.newStock - 新的库存数量 * @param {Number} params.oldStock - 原库存数量 * @param {String} params.note - 备注信息 * @param {Number} params.userId - 用户ID * @returns {Promise<Object>} 操作结果 */ async updateStock(params) { return this.execute('kucunbianji', params); } /** * 生产消耗事务 * 记录生产过程中消耗的材料 * @param {Object} params - 生产消耗参数 * @returns {Promise<Object>} 操作结果 */ async shengchanXiaohao(params) { return this.execute('shengchanXiaohao', params); } /** * 批量更新订单板材事务 * 同时更新多个订单中的板材信息 * @param {Object} params - 批量更新参数 * @returns {Promise<Object>} 操作结果 */ async batchUpdateDingdan_bancai(params) { return this.execute('batchUpdateDingdan_bancai', params); } /** * 提交订单板材采购事务 * 将订单板材提交到采购流程 * @param {Object} params - 提交参数 * @returns {Promise<Object>} 操作结果 */ async submitDingdan_bancai(params) { return this.execute('submitDingdan_bancai', params); } /** * 消耗订单板材事务 * 标记订单中的板材已被消耗 * @param {Object} params - 消耗参数 * @returns {Promise<Object>} 操作结果 */ async consumeDingdan_bancai(params) { return this.execute('consumeDingdan_bancai', params); } /** * 采购订单板材事务 * 执行订单板材的采购操作 * @param {Object} params - 采购参数 * @returns {Promise<Object>} 操作结果 */ async 采购Dingdan_bancai(params) { return this.execute('采购Dingdan_bancai', params); } /** * 保存所有数据事务 * 批量保存多种类型的数据到服务器 * @param {Object} params - 包含所有需要保存数据的对象 * @returns {Promise<Object>} 事务执行结果 */ async saveAll(params) { try { // 调用API执行批量保存 const response = await fetch(`${this.baseUrl}/app/save-all`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(params) }); // 检查响应状态 if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } // 解析响应结果 const result = await response.json(); if (result.status !== 200) { throw new Error(result.text || 'Save all failed'); } // 保存成功后刷新数据 await this.dataManager.syncData(); return result.data; } catch (error) { console.error('Save all failed:', error); throw error; } } } export { TransactionApi }; ================================================================================ /* 文件路径: data/components/TransactionApi.js */ /** * 事务API组件 * 提供复合数据操作的事务支持 */ export class TransactionApi { constructor(dataManager) { this.dataManager = dataManager; } /** * 执行事务操作 * @param {Function} operation - 要执行的操作函数 * @returns {Promise} 事务结果Promise */ async executeTransaction(operation) { try { // 标记事务开始 this.beginTransaction(); // 执行操作 const result = await operation(); // 提交事务 this.commitTransaction(); return result; } catch (error) { // 回滚事务 this.rollbackTransaction(); throw error; } } /** * 开始事务 */ beginTransaction() { // 在实际应用中,这里可能需要保存当前状态以便回滚 // 对于这个简单实现,我们只是记录事务开始 this.isInTransaction = true; this.transactionOperations = []; } /** * 提交事务 */ commitTransaction() { // 执行所有事务操作 this.transactionOperations.forEach(op => { if (typeof op === 'function') { try { op(); } catch (error) { console.error('Transaction operation failed:', error); } } }); // 清空事务状态 this.isInTransaction = false; this.transactionOperations = []; } /** * 回滚事务 */ rollbackTransaction() { // 在实际应用中,这里需要恢复到事务开始前的状态 // 对于这个简单实现,我们只是清空事务状态 this.isInTransaction = false; this.transactionOperations = []; } /** * 向事务中添加操作 * @param {Function} operation - 要添加的操作函数 */ addOperation(operation) { if (this.isInTransaction) { this.transactionOperations.push(operation); } } /** * 执行复合数据更新 * @param {Object} updateData - 更新数据对象 * @returns {Promise} 更新结果Promise */ async updateMultipleEntities(updateData) { return this.executeTransaction(async () => { const results = {}; // 处理不同类型的实体更新 for (const entityType in updateData) { if (Object.prototype.hasOwnProperty.call(updateData, entityType)) { const entities = updateData[entityType]; if (Array.isArray(entities)) { results[entityType] = await this.updateEntitiesOfType(entityType, entities); } } } return results; }); } /** * 更新特定类型的实体 * @param {string} entityType - 实体类型 * @param {Array} entities - 要更新的实体数组 * @returns {Promise} 更新结果Promise */ async updateEntitiesOfType(entityType, entities) { const results = []; for (const entity of entities) { if (entity.id) { // 已存在的实体,执行更新 const result = await this.dataManager.updateEntity(entityType, entity); results.push(result); } else { // 新实体,执行添加 const result = await this.dataManager.addEntity(entityType, entity); results.push(result); } } return results; } } ================================================================================ /* 文件路径: data/WebSocketClient.js */ /** * WebSocket客户端类 * 负责连接WebSocket服务器并处理实体更新通知 * 注意:使用前需要在HTML页面中引入SockJS和Stomp客户端库 * <script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script> * <script src="https://cdn.jsdelivr.net/npm/stompjs@2.3.3/lib/stomp.min.js"></script> */ export class WebSocketClient { constructor() { // WebSocket连接实例 this.socket = null; this.stompClient = null; // 消息处理回调函数 this.onEntityUpdate = null; this.onConnectionStatusChange = null; // 连接状态标志 this.isConnected = false; // 检查SockJS和Stomp客户端库是否已加载 if (typeof window.SockJS === 'undefined') { console.warn('SockJS客户端库未加载,请在HTML页面中引入SockJS库'); } if (typeof window.Stomp === 'undefined') { console.warn('Stomp客户端库未加载,请在HTML页面中引入Stomp库'); } } /** * 连接WebSocket服务器 */ connect() { // 构建WebSocket服务器URL const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; const wsUrl = `${wsProtocol}//${window.location.host}${window.location.pathname}ws`; // 使用SockJS客户端连接 this.socket = new SockJS(wsUrl); const stompClient = Stomp.over(this.socket); // 设置调试标志 stompClient.debug = null; // 禁用调试输出 // 连接回调 stompClient.connect({}, // 连接成功回调 () => { console.log('WebSocket连接成功,开始接收实时数据更新'); this.isConnected = true; // 触发连接状态变化回调 if (this.onConnectionStatusChange) { this.onConnectionStatusChange(true); } // 订阅实体更新频道 stompClient.subscribe('/topic/entity-updates', (message) => { this.handleEntityUpdate(message); }); }, // 连接错误/断开回调 (error) => { console.error('WebSocket连接失败:', error); this.isConnected = false; // 触发连接状态变化回调 if (this.onConnectionStatusChange) { this.onConnectionStatusChange(false); } // 尝试重新连接 setTimeout(() => { console.log('尝试重新连接WebSocket...'); this.connect(); }, 5000); // 5秒后重试 } ); // 保存Stomp客户端引用 this.stompClient = stompClient; } /** * 断开WebSocket连接 */ disconnect() { if (this.stompClient) { this.stompClient.disconnect(() => { console.log('WebSocket连接已断开'); this.isConnected = false; if (this.onConnectionStatusChange) { this.onConnectionStatusChange(false); } }); } } /** * 处理接收到的实体更新消息 */ handleEntityUpdate(message) { try { // 解析消息内容 const updateData = JSON.parse(message.body); // 触发实体更新回调 if (this.onEntityUpdate) { this.onEntityUpdate(updateData); } } catch (error) { console.error('解析实体更新消息失败:', error); } } /** * 获取当前连接状态 */ getConnectionStatus() { return this.isConnected; } } ================================================================================ /* 文件路径: data/components/WebSocketClient.js */ /** * WebSocket客户端组件 * 负责连接WebSocket服务器并处理实体更新通知 */ export class WebSocketClient { constructor(dataManager, url = '/ws') { this.dataManager = dataManager; this.url = url; this.socket = null; this.reconnectAttempts = 0; this.maxReconnectAttempts = 5; this.reconnectInterval = 1000; this.isConnecting = false; } /** * 连接到WebSocket服务器 */ connect() { if (this.socket && this.socket.readyState === WebSocket.OPEN) { return; } if (this.isConnecting) { return; } this.isConnecting = true; try { // 创建WebSocket连接 this.socket = new WebSocket(this.url); // 设置事件处理器 this.socket.onopen = this.handleOpen.bind(this); this.socket.onmessage = this.handleMessage.bind(this); this.socket.onclose = this.handleClose.bind(this); this.socket.onerror = this.handleError.bind(this); } catch (error) { console.error('Failed to create WebSocket connection:', error); this.isConnecting = false; this.attemptReconnect(); } } /** * 断开WebSocket连接 */ disconnect() { if (this.socket) { this.socket.close(); this.socket = null; } this.reconnectAttempts = 0; this.isConnecting = false; } /** * 处理WebSocket连接打开事件 * @param {Event} event - 连接事件 */ handleOpen(event) { console.log('WebSocket connection established'); this.isConnecting = false; this.reconnectAttempts = 0; // 发送连接确认消息 this.send({ type: 'connect', timestamp: new Date().getTime() }); } /** * 处理WebSocket消息 * @param {MessageEvent} event - 消息事件 */ handleMessage(event) { try { const data = JSON.parse(event.data); this.dataManager.handleWebSocketUpdate(data); } catch (error) { console.error('Failed to parse WebSocket message:', error); } } /** * 处理WebSocket连接关闭事件 * @param {CloseEvent} event - 关闭事件 */ handleClose(event) { console.log('WebSocket connection closed:', event.code, event.reason); this.socket = null; this.isConnecting = false; // 尝试重新连接 this.attemptReconnect(); } /** * 处理WebSocket错误事件 * @param {Event} event - 错误事件 */ handleError(event) { console.error('WebSocket error:', event); } /** * 发送消息到服务器 * @param {Object} message - 要发送的消息对象 */ send(message) { if (this.socket && this.socket.readyState === WebSocket.OPEN) { this.socket.send(JSON.stringify(message)); } else { console.warn('WebSocket is not connected, cannot send message'); } } /** * 尝试重新连接 */ attemptReconnect() { if (this.reconnectAttempts < this.maxReconnectAttempts) { this.reconnectAttempts++; const delay = this.reconnectInterval * Math.pow(2, this.reconnectAttempts - 1); console.log(`Attempting to reconnect in ${delay}ms (attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts})`); setTimeout(() => { this.connect(); }, delay); } else { console.error('Max reconnection attempts reached, giving up'); } } /** * 订阅特定实体类型的更新 * @param {string} entityType - 实体类型 */ subscribe(entityType) { this.send({ type: 'subscribe', entityType: entityType }); } /** * 取消订阅特定实体类型的更新 * @param {string} entityType - 实体类型 */ unsubscribe(entityType) { this.send({ type: 'unsubscribe', entityType: entityType }); } }
最新发布
09-19
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值