UNPKG

cs-element

Version:

Advanced reactive data management library with state machines, blueprints, persistence, compression, networking, and multithreading support

749 lines (649 loc) 25 kB
# 🔄 State Machine и управление состояниями CSElement включает мощную систему State Machine для управления состояниями элементов с поддержкой переходов, условий и действий. ## 📋 Содержание - [Основы State Machine](#основы-state-machine) - [Создание состояний и переходов](#создание-состояний-и-переходов) - [События и действия](#события-и-действия) - [Условия и охранники](#условия-и-охранники) - [Полный пример](#полный-пример-система-заказов) ## Основы State Machine ### Создание State Machine ```typescript import { StateMachine, StateMachineConfig, StateMachineEventType, State, Transition } from 'cs-element'; // Конфигурация State Machine const config: StateMachineConfig = { name: 'OrderStateMachine', initialState: 'draft', enableLogging: true, enableProfiling: true, states: [ { name: 'draft', description: 'Черновик заказа', type: 'normal', onEnter: async (context) => { console.log('📝 Заказ переведен в черновик'); await context.element.setData('status', 'draft'); }, onExit: async (context) => { console.log('📝 Выход из состояния черновика'); } }, { name: 'pending', description: 'Ожидает подтверждения', type: 'normal', onEnter: async (context) => { console.log('⏳ Заказ ожидает подтверждения'); await context.element.setData('status', 'pending'); await context.element.setData('submittedAt', new Date().toISOString()); } }, { name: 'confirmed', description: 'Подтвержден', type: 'normal', onEnter: async (context) => { console.log('✅ Заказ подтвержден'); await context.element.setData('status', 'confirmed'); await context.element.setData('confirmedAt', new Date().toISOString()); } }, { name: 'cancelled', description: 'Отменен', type: 'final', onEnter: async (context) => { console.log('❌ Заказ отменен'); await context.element.setData('status', 'cancelled'); await context.element.setData('cancelledAt', new Date().toISOString()); } } ], transitions: [ { id: 'submit', from: 'draft', to: 'pending', event: 'SUBMIT', description: 'Отправка заказа на подтверждение', priority: 1 }, { id: 'confirm', from: 'pending', to: 'confirmed', event: 'CONFIRM', description: 'Подтверждение заказа', priority: 1 }, { id: 'cancel-draft', from: 'draft', to: 'cancelled', event: 'CANCEL', description: 'Отмена черновика', priority: 2 }, { id: 'cancel-pending', from: 'pending', to: 'cancelled', event: 'CANCEL', description: 'Отмена ожидающего заказа', priority: 2 } ] }; // Создание элемента и State Machine const orderElement = new CSElement('order-123'); const stateMachine = new StateMachine(orderElement, config); ``` ### Отправка событий ```typescript // Отправка заказа const submitResult = await stateMachine.send('SUBMIT'); console.log('Результат отправки:', submitResult); // Подтверждение заказа const confirmResult = await stateMachine.send('CONFIRM'); console.log('Результат подтверждения:', confirmResult); // Отмена заказа const cancelResult = await stateMachine.send('CANCEL'); console.log('Результат отмены:', cancelResult); // Получение текущего состояния console.log('Текущее состояние:', stateMachine.currentState); // Получение возможных переходов const possibleTransitions = stateMachine.getPossibleTransitions(); console.log('Возможные переходы:', possibleTransitions.map(t => t.event)); ``` ## Создание состояний и переходов ### Динамическое создание состояний ```typescript class OrderStateMachineBuilder { private states: State[] = []; private transitions: Transition[] = []; addState(name: string, config: { description?: string; type?: 'normal' | 'initial' | 'final'; onEnter?: (context: any) => Promise<void>; onExit?: (context: any) => Promise<void>; }): this { this.states.push({ name, description: config.description || name, type: config.type || 'normal', onEnter: config.onEnter, onExit: config.onExit }); return this; } addTransition(from: string, to: string, event: string, config: { description?: string; priority?: number; condition?: (context: any, event: any) => Promise<boolean>; action?: (context: any, event: any) => Promise<void>; } = {}): this { this.transitions.push({ id: `${from}-${to}-${event}`, from, to, event, description: config.description || `${from} → ${to}`, priority: config.priority || 1, condition: config.condition, action: config.action }); return this; } build(element: CSElement, name: string, initialState: string): StateMachine { const config: StateMachineConfig = { name, initialState, enableLogging: true, states: this.states, transitions: this.transitions }; return new StateMachine(element, config); } } // Использование builder'а const orderSM = new OrderStateMachineBuilder() .addState('draft', { description: 'Черновик заказа', onEnter: async (context) => { await context.element.setData('status', 'draft'); } }) .addState('processing', { description: 'Обработка заказа', onEnter: async (context) => { await context.element.setData('status', 'processing'); await context.element.setData('processingStarted', new Date().toISOString()); } }) .addState('completed', { description: 'Заказ выполнен', type: 'final', onEnter: async (context) => { await context.element.setData('status', 'completed'); await context.element.setData('completedAt', new Date().toISOString()); } }) .addTransition('draft', 'processing', 'START_PROCESSING', { description: 'Начать обработку заказа', condition: async (context) => { const items = await context.element.getData('items') as any[]; return items && items.length > 0; }, action: async (context) => { console.log('🚀 Начинаем обработку заказа'); await context.element.setData('processingBy', 'system'); } }) .addTransition('processing', 'completed', 'COMPLETE', { description: 'Завершить заказ', action: async (context) => { console.log('✅ Заказ завершен'); await context.element.setData('completedBy', 'system'); } }) .build(orderElement, 'OrderProcessor', 'draft'); ``` ## События и действия ### Обработка событий ```typescript // Подписка на события State Machine stateMachine.subscribe(StateMachineEventType.TRANSITION_STARTED, (data) => { console.log(`🔄 Начался переход: ${data.fromState} → ${data.toState}`); }); stateMachine.subscribe(StateMachineEventType.TRANSITION_COMPLETED, (data) => { console.log(`✅ Переход завершен: ${data.fromState} → ${data.toState} (${data.duration}ms)`); }); stateMachine.subscribe(StateMachineEventType.TRANSITION_FAILED, (data) => { console.error(`❌ Переход неудачен: ${data.fromState} → ${data.toState}`, data.error); }); stateMachine.subscribe(StateMachineEventType.STATE_ENTERED, (data) => { console.log(`📍 Вошли в состояние: ${data.state}`); }); stateMachine.subscribe(StateMachineEventType.STATE_EXITED, (data) => { console.log(`📤 Покинули состояние: ${data.state}`); }); ``` ### Сложные действия ```typescript // Переход с асинхронными действиями const complexTransition: Transition = { id: 'process-payment', from: 'pending', to: 'paid', event: 'PROCESS_PAYMENT', description: 'Обработка платежа', condition: async (context, event) => { const amount = await context.element.getData('amount') as number; const paymentMethod = event.payload?.paymentMethod; return amount > 0 && paymentMethod; }, action: async (context, event) => { const amount = await context.element.getData('amount') as number; const paymentMethod = event.payload.paymentMethod; // Имитация обработки платежа console.log(`💳 Обработка платежа ${amount} через ${paymentMethod}`); // Сохраняем детали платежа await context.element.setData('paymentMethod', paymentMethod); await context.element.setData('paymentProcessedAt', new Date().toISOString()); // Имитация внешнего API await new Promise(resolve => setTimeout(resolve, 1000)); if (Math.random() > 0.1) { // 90% успеха await context.element.setData('paymentStatus', 'success'); console.log('✅ Платеж успешно обработан'); } else { await context.element.setData('paymentStatus', 'failed'); throw new Error('Ошибка обработки платежа'); } } }; // Отправка события с данными const paymentResult = await stateMachine.send('PROCESS_PAYMENT', { paymentMethod: 'credit_card', cardNumber: '**** **** **** 1234' }); ``` ## Условия и охранники ### Условные переходы ```typescript // Переход с проверкой условий const conditionalTransition: Transition = { id: 'approve-order', from: 'pending', to: 'approved', event: 'APPROVE', description: 'Одобрение заказа', condition: async (context, event) => { const amount = await context.element.getData('amount') as number; const userRole = event.payload?.userRole; // Менеджеры могут одобрять заказы до 10000 if (userRole === 'manager' && amount <= 10000) { return true; } // Директора могут одобрять любые заказы if (userRole === 'director') { return true; } return false; }, action: async (context, event) => { const approver = event.payload.userRole; await context.element.setData('approvedBy', approver); await context.element.setData('approvedAt', new Date().toISOString()); console.log(`✅ Заказ одобрен пользователем с ролью: ${approver}`); } }; // Использование условного перехода const approvalResult = await stateMachine.send('APPROVE', { userRole: 'manager', userId: 'user-123' }); ``` ### Сложные условия ```typescript class OrderValidator { static async validateOrderItems(context: any): Promise<boolean> { const items = await context.element.getData('items') as any[]; if (!items || items.length === 0) { return false; } // Проверяем каждый товар for (const item of items) { if (!item.id || !item.quantity || item.quantity <= 0) { return false; } // Проверяем наличие на складе const inStock = await this.checkInventory(item.id, item.quantity); if (!inStock) { return false; } } return true; } static async validateCustomer(context: any): Promise<boolean> { const customerId = await context.element.getData('customerId') as string; if (!customerId) { return false; } // Проверяем статус клиента const customerStatus = await this.getCustomerStatus(customerId); return customerStatus === 'active'; } static async validatePayment(context: any): Promise<boolean> { const amount = await context.element.getData('amount') as number; const paymentMethod = await context.element.getData('paymentMethod') as string; if (!amount || amount <= 0) { return false; } if (!paymentMethod) { return false; } // Проверяем лимиты платежей return amount <= 100000; // максимум 100,000 } private static async checkInventory(itemId: string, quantity: number): Promise<boolean> { // Имитация проверки склада return Math.random() > 0.1; // 90% товаров в наличии } private static async getCustomerStatus(customerId: string): Promise<string> { // Имитация проверки статуса клиента return Math.random() > 0.05 ? 'active' : 'blocked'; } } // Использование валидатора в переходах const validateTransition: Transition = { id: 'validate-order', from: 'draft', to: 'validated', event: 'VALIDATE', description: 'Валидация заказа', condition: async (context, event) => { const itemsValid = await OrderValidator.validateOrderItems(context); const customerValid = await OrderValidator.validateCustomer(context); const paymentValid = await OrderValidator.validatePayment(context); return itemsValid && customerValid && paymentValid; }, action: async (context, event) => { await context.element.setData('validatedAt', new Date().toISOString()); await context.element.setData('validatedBy', 'system'); console.log('✅ Заказ успешно валидирован'); } }; ``` ## Полный пример: Система заказов ```typescript class OrderManagementSystem { private orders: Map<string, { element: CSElement; stateMachine: StateMachine }> = new Map(); async createOrder(orderData: { customerId: string; items: Array<{ id: string; quantity: number; price: number }>; paymentMethod: string; }): Promise<string> { const orderId = `order-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; // Создаем элемент заказа const orderElement = new CSElement(orderId); await orderElement.setData('customerId', orderData.customerId); await orderElement.setData('items', orderData.items); await orderElement.setData('paymentMethod', orderData.paymentMethod); const totalAmount = orderData.items.reduce((sum, item) => sum + (item.price * item.quantity), 0); await orderElement.setData('amount', totalAmount); await orderElement.setData('createdAt', new Date().toISOString()); // Создаем State Machine const stateMachine = this.createOrderStateMachine(orderElement); // Сохраняем заказ this.orders.set(orderId, { element: orderElement, stateMachine }); console.log(`🛒 Заказ ${orderId} создан на сумму ${totalAmount}`); return orderId; } private createOrderStateMachine(element: CSElement): StateMachine { const config: StateMachineConfig = { name: 'OrderLifecycle', initialState: 'draft', enableLogging: true, states: [ { name: 'draft', description: 'Черновик заказа', type: 'initial', onEnter: async (context) => { await context.element.setData('status', 'draft'); } }, { name: 'validated', description: 'Валидирован', type: 'normal', onEnter: async (context) => { await context.element.setData('status', 'validated'); } }, { name: 'payment_processing', description: 'Обработка платежа', type: 'normal', onEnter: async (context) => { await context.element.setData('status', 'payment_processing'); } }, { name: 'paid', description: 'Оплачен', type: 'normal', onEnter: async (context) => { await context.element.setData('status', 'paid'); } }, { name: 'processing', description: 'В обработке', type: 'normal', onEnter: async (context) => { await context.element.setData('status', 'processing'); } }, { name: 'shipped', description: 'Отправлен', type: 'normal', onEnter: async (context) => { await context.element.setData('status', 'shipped'); } }, { name: 'delivered', description: 'Доставлен', type: 'final', onEnter: async (context) => { await context.element.setData('status', 'delivered'); await context.element.setData('deliveredAt', new Date().toISOString()); } }, { name: 'cancelled', description: 'Отменен', type: 'final', onEnter: async (context) => { await context.element.setData('status', 'cancelled'); await context.element.setData('cancelledAt', new Date().toISOString()); } } ], transitions: [ { id: 'validate', from: 'draft', to: 'validated', event: 'VALIDATE', condition: async (context) => { return await OrderValidator.validateOrderItems(context) && await OrderValidator.validateCustomer(context) && await OrderValidator.validatePayment(context); } }, { id: 'process-payment', from: 'validated', to: 'payment_processing', event: 'PROCESS_PAYMENT' }, { id: 'payment-success', from: 'payment_processing', to: 'paid', event: 'PAYMENT_SUCCESS', action: async (context) => { await context.element.setData('paidAt', new Date().toISOString()); } }, { id: 'start-processing', from: 'paid', to: 'processing', event: 'START_PROCESSING' }, { id: 'ship', from: 'processing', to: 'shipped', event: 'SHIP', action: async (context, event) => { await context.element.setData('trackingNumber', event.payload?.trackingNumber); await context.element.setData('shippedAt', new Date().toISOString()); } }, { id: 'deliver', from: 'shipped', to: 'delivered', event: 'DELIVER' }, { id: 'cancel', from: 'draft', to: 'cancelled', event: 'CANCEL' }, { id: 'cancel-validated', from: 'validated', to: 'cancelled', event: 'CANCEL' } ] }; return new StateMachine(element, config); } async processOrder(orderId: string, event: string, payload?: any): Promise<any> { const order = this.orders.get(orderId); if (!order) { throw new Error(`Заказ ${orderId} не найден`); } const result = await order.stateMachine.send(event, payload); if (result.success) { console.log(`✅ Событие ${event} для заказа ${orderId} обработано успешно`); } else { console.error(`❌ Ошибка обработки события ${event} для заказа ${orderId}:`, result.error); } return result; } async getOrderStatus(orderId: string): Promise<{ id: string; status: string; currentState: string; possibleActions: string[]; history: any[]; }> { const order = this.orders.get(orderId); if (!order) { throw new Error(`Заказ ${orderId} не найден`); } const status = await order.element.getData('status') as string; const possibleTransitions = order.stateMachine.getPossibleTransitions(); const history = order.stateMachine.getHistory(); return { id: orderId, status, currentState: order.stateMachine.currentState, possibleActions: possibleTransitions.map(t => t.event), history }; } async getAllOrders(): Promise<Array<{ id: string; status: string; amount: number; createdAt: string; }>> { const orders = []; for (const [orderId, order] of this.orders) { const status = await order.element.getData('status') as string; const amount = await order.element.getData('amount') as number; const createdAt = await order.element.getData('createdAt') as string; orders.push({ id: orderId, status, amount, createdAt }); } return orders; } } // Демонстрация использования async function demonstrateOrderSystem() { const orderSystem = new OrderManagementSystem(); try { // Создаем заказ const orderId = await orderSystem.createOrder({ customerId: 'customer-123', items: [ { id: 'item-1', quantity: 2, price: 100 }, { id: 'item-2', quantity: 1, price: 200 } ], paymentMethod: 'credit_card' }); // Валидируем заказ await orderSystem.processOrder(orderId, 'VALIDATE'); // Обрабатываем платеж await orderSystem.processOrder(orderId, 'PROCESS_PAYMENT'); await orderSystem.processOrder(orderId, 'PAYMENT_SUCCESS'); // Начинаем обработку await orderSystem.processOrder(orderId, 'START_PROCESSING'); // Отправляем заказ await orderSystem.processOrder(orderId, 'SHIP', { trackingNumber: 'TRACK123456' }); // Доставляем заказ await orderSystem.processOrder(orderId, 'DELIVER'); // Проверяем финальный статус const finalStatus = await orderSystem.getOrderStatus(orderId); console.log('📊 Финальный статус заказа:', finalStatus); // Получаем все заказы const allOrders = await orderSystem.getAllOrders(); console.log('📋 Все заказы:', allOrders); } catch (error) { console.error('❌ Ошибка в системе заказов:', error); } } demonstrateOrderSystem().catch(console.error); ``` ## 🎯 Заключение State Machine в CSElement обеспечивает: - **🔄 Четкое управление состояниями** - структурированные переходы между состояниями - **🛡️ Валидация переходов** - условия и охранники для безопасных переходов - **⚡ Асинхронные действия** - поддержка сложных операций при переходах - **📊 Мониторинг** - отслеживание истории переходов и событий - **🔧 Гибкость** - динамическое создание состояний и переходов --- **Следующий раздел:** [Diff Engine и слияние данных →](12-diff-engine.md)