# CommandManager 命令管理器使用指南

## 📖 概述

CommandManager 是一个基于经典命令模式的撤销/重做系统，提供队列机制防止快速执行时命令丢失，支持复合命令进行批量操作，确保了高性能和稳定性。

## ✨ 核心特性

- **撤销/重做**: 基于经典命令模式的撤销重做功能
- **队列机制**: 防止快速执行多个命令时的命令丢失
- **串行化执行**: 确保命令按顺序执行，避免并发问题
- **Promise支持**: 完全支持异步命令
- **性能监控**: 可选的性能统计功能
- **复合命令**: 支持批量操作和命令组合
- **状态管理**: 实时状态监控和回调

## 🎯 复合命令详解

### 什么是复合命令？

复合命令是一组相关操作的集合，这些操作作为一个整体执行，支持命令间的值传递和条件执行。在Canvas编辑器中，复合命令特别适用于：

- 创建复杂组件（同时创建多个对象和图层）
- 批量修改属性
- 导入文件（可能涉及多个图层和对象的创建）
- 复制粘贴操作

### 复合命令的执行流程

```mermaid
graph TD
    A[创建复合命令] --> B[添加子命令]
    B --> C[执行复合命令]
    C --> D[串行执行子命令]
    D --> E{执行成功?}
    E -->|是| F[记录已执行命令]
    F --> G[继续下一个命令]
    G --> D
    E -->|否| H[回滚已执行命令]
    H --> I[抛出错误]
    
    C --> J[撤销复合命令]
    J --> K[逆序撤销子命令]
```

## 🚀 快速开始

### 1. 基础批量操作

```javascript
import { CommandManager } from './CommandManager.js';
import { Command } from '../../commands/Command.js';

// 创建命令管理器
const commandManager = new CommandManager({
  maxHistorySize: 50,
  performanceManager: null
});

// 创建简单命令
class SetPropertyCommand extends Command {
  constructor(target, property, newValue) {
    super({ name: '设置属性', description: `设置${property}为${newValue}` });
    this.target = target;
    this.property = property;
    this.newValue = newValue;
    this.oldValue = null;
  }

  execute() {
    this.oldValue = this.target[this.property];
    this.target[this.property] = this.newValue;
    return { property: this.property, oldValue: this.oldValue, newValue: this.newValue };
  }

  undo() {
    this.target[this.property] = this.oldValue;
    return { property: this.property, restoredValue: this.oldValue };
  }
}

// 使用批量操作
const obj = { x: 0, y: 0, color: 'black' };

// 方法1: 使用 executeBatch
await commandManager.executeBatch([
  new SetPropertyCommand(obj, 'x', 100),
  new SetPropertyCommand(obj, 'y', 200),
  new SetPropertyCommand(obj, 'color', 'red')
], '批量设置对象属性');

console.log(obj); // { x: 100, y: 200, color: 'red' }

// 撤销时，所有属性会一次性恢复
await commandManager.undo();
console.log(obj); // { x: 0, y: 0, color: 'black' }
```

### 2. 创建自定义复合命令

```javascript
import { CompositeCommand } from '../../commands/Command.js';

// 支持值传递的复合命令
class CreateLayerWithObjectsCommand extends CompositeCommand {
  constructor(layerName, objects) {
    super([], { name: '创建图层并添加对象' });
    this.layerName = layerName;
    this.objects = objects;
    this.layerId = null;
  }
  
  async execute() {
    // 先创建图层
    const createLayerCmd = new CreateLayerCommand(this.layerName);
    this.layerId = await createLayerCmd.execute();
    this.addCommand(createLayerCmd);
    
    // 使用返回的 layerId 添加对象
    for (const obj of this.objects) {
      const addObjectCmd = new AddObjectCommand(obj, this.layerId);
      await addObjectCmd.execute();
      this.addCommand(addObjectCmd);
    }
    
    this.executedCommands = [...this.commands];
    return { layerId: this.layerId, objectCount: this.objects.length };
  }
}

// 使用自定义复合命令
const objects = [object1, object2, object3];
const command = new CreateLayerWithObjectsCommand('新图层', objects);
const result = await commandManager.execute(command);
console.log(`创建了图层 ${result.layerId}，包含 ${result.objectCount} 个对象`);
```

## 🔧 高级功能

### 1. 条件执行的复合命令

```javascript
class ConditionalSetupCommand extends CompositeCommand {
  constructor(config) {
    super([], { name: '条件设置' });
    this.config = config;
  }
  
  async execute() {
    // 总是创建基础对象
    const createCmd = new CreateObjectCommand(this.config.object);
    await createCmd.execute();
    this.addCommand(createCmd);
    
    // 根据条件决定是否创建图层
    if (this.config.shouldCreateLayer) {
      const layerCmd = new CreateLayerCommand(this.config.layerName);
      await layerCmd.execute();
      this.addCommand(layerCmd);
    }
    
    // 根据条件应用样式
    if (this.config.applyStyle) {
      const styleCmd = new ApplyStyleCommand(this.config.style);
      await styleCmd.execute();
      this.addCommand(styleCmd);
    }
    
    this.executedCommands = [...this.commands];
    return { commandCount: this.commands.length };
  }
}
```

### 2. 错误处理和回滚

```javascript
class RobustBatchCommand extends CompositeCommand {
  constructor(operations) {
    super([], { name: '健壮的批量操作' });
    this.operations = operations;
  }
  
  async execute() {
    try {
      for (const operation of this.operations) {
        const command = new operation.CommandClass(...operation.args);
        
        // 执行命令
        await command.execute();
        this.addCommand(command);
        
        console.log(`✅ 操作成功: ${command.constructor.name}`);
      }
      
      this.executedCommands = [...this.commands];
      return { success: true, executedCount: this.commands.length };
    } catch (error) {
      console.error('❌ 批量操作失败:', error);
      
      // 自动回滚已执行的命令
      await this._rollbackExecutedCommands();
      throw error;
    }
  }
}
```

### 3. 状态监控

```javascript
// 设置状态变化回调
commandManager.setChangeCallback((state) => {
  console.log('命令管理器状态:', {
    canUndo: state.canUndo,
    canRedo: state.canRedo,
    undoCount: state.undoCount,
    redoCount: state.redoCount,
    isExecuting: state.isExecuting,
    lastCommand: state.lastCommand
  });
});

// 获取命令历史
const history = commandManager.getHistory();
console.log('撤销历史:', history.undoHistory);
console.log('重做历史:', history.redoHistory);
```

## 💡 实际应用场景

### 1. 复杂UI组件创建

```javascript
class CreateComplexCardCommand extends CompositeCommand {
  constructor(canvas, config) {
    super([], { name: '创建复杂卡片' });
    this.canvas = canvas;
    this.config = config;
    this.createdObjects = [];
  }

  async execute() {
    try {
      // 创建背景
      const background = await this._createBackground();
      const bgCmd = new AddObjectCommand(background);
      await bgCmd.execute();
      this.addCommand(bgCmd);

      // 创建头部
      const header = await this._createHeader();
      const headerCmd = new AddObjectCommand(header);
      await headerCmd.execute();
      this.addCommand(headerCmd);

      // 创建内容区域
      const content = await this._createContent();
      const contentCmd = new AddObjectCommand(content);
      await contentCmd.execute();
      this.addCommand(contentCmd);

      // 创建按钮
      const buttons = await this._createButtons();
      for (const button of buttons) {
        const buttonCmd = new AddObjectCommand(button);
        await buttonCmd.execute();
        this.addCommand(buttonCmd);
      }

      this.executedCommands = [...this.commands];
      return { success: true, objectCount: this.commands.length };
    } catch (error) {
      await this._rollbackExecutedCommands();
      throw error;
    }
  }

  async _createBackground() {
    // 创建背景逻辑
    return new fabric.Rect({
      width: this.config.width,
      height: this.config.height,
      fill: this.config.backgroundColor
    });
  }

  // ...existing code...
}
```

### 2. 批量导入处理

```javascript
class ImportMultipleImagesCommand extends CompositeCommand {
  constructor(files, layerManager) {
    super([], { name: `导入${files.length}个图片文件` });
    this.files = files;
    this.layerManager = layerManager;
    this.importedLayers = [];
  }

  async execute() {
    try {
      for (const file of this.files) {
        // 加载图片
        const image = await this._loadImageFromFile(file);
        
        // 创建图层
        const createLayerCmd = new CreateLayerCommand(file.name);
        const layerId = await createLayerCmd.execute();
        this.addCommand(createLayerCmd);
        
        // 添加对象到图层
        const addObjectCmd = new AddObjectToLayerCommand(image, layerId);
        await addObjectCmd.execute();
        this.addCommand(addObjectCmd);
        
        this.importedLayers.push(layerId);
      }
      
      this.executedCommands = [...this.commands];
      return { importedLayers: this.importedLayers };
    } catch (error) {
      console.error('批量导入失败:', error);
      await this._rollbackExecutedCommands();
      throw error;
    }
  }

  async _loadImageFromFile(file) {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.onload = (e) => {
        fabric.Image.fromURL(e.target.result, resolve);
      };
      reader.onerror = reject;
      reader.readAsDataURL(file);
    });
  }
}

// 使用
const files = [file1, file2, file3];
const importCmd = new ImportMultipleImagesCommand(files, layerManager);
const result = await commandManager.execute(importCmd);
console.log('导入的图层:', result.importedLayers);
```

### 3. 复制粘贴操作

```javascript
class CopyPasteObjectsCommand extends CompositeCommand {
  constructor(sourceObjects, targetPosition) {
    super([], { name: '复制粘贴对象' });
    this.sourceObjects = sourceObjects;
    this.targetPosition = targetPosition;
    this.copiedObjects = [];
  }

  async execute() {
    try {
      for (const obj of this.sourceObjects) {
        // 克隆对象
        const clonedObj = await this._cloneObject(obj);
        
        // 设置新位置
        clonedObj.set({
          left: this.targetPosition.x + (clonedObj.left - this.sourceObjects[0].left),
          top: this.targetPosition.y + (clonedObj.top - this.sourceObjects[0].top)
        });

        // 添加到画布
        const addCmd = new AddObjectCommand(clonedObj);
        await addCmd.execute();
        this.addCommand(addCmd);
        
        this.copiedObjects.push(clonedObj);
      }
      
      this.executedCommands = [...this.commands];
      return { copiedObjects: this.copiedObjects };
    } catch (error) {
      await this._rollbackExecutedCommands();
      throw error;
    }
  }

  async _cloneObject(obj) {
    return new Promise((resolve) => {
      obj.clone(resolve);
    });
  }
}
```

## 🛠️ 错误处理策略

### 1. 复合命令中的错误处理

```javascript
// 推荐的错误处理模式
class SafeBatchCommand extends CompositeCommand {
  constructor(operations, options = {}) {
    super([], { name: options.name || '安全批量操作' });
    this.operations = operations;
    this.stopOnError = options.stopOnError !== false; // 默认遇到错误就停止
  }

  async execute() {
    const results = [];
    const errors = [];

    for (let i = 0; i < this.operations.length; i++) {
      const operation = this.operations[i];
      
      try {
        const command = new operation.CommandClass(...operation.args);
        const result = await command.execute();
        
        this.addCommand(command);
        results.push({ index: i, success: true, result });
        
        console.log(`✅ 操作 ${i + 1}/${this.operations.length} 成功`);
      } catch (error) {
        errors.push({ index: i, error: error.message });
        console.error(`❌ 操作 ${i + 1}/${this.operations.length} 失败:`, error);
        
        if (this.stopOnError) {
          // 回滚已执行的命令
          await this._rollbackExecutedCommands();
          throw new Error(`批量操作在第 ${i + 1} 个操作时失败: ${error.message}`);
        }
      }
    }

    this.executedCommands = [...this.commands];
    return { 
      results, 
      errors, 
      successCount: results.length,
      errorCount: errors.length 
    };
  }
}
```

### 2. 超时处理

```javascript
class TimeLimitedCommand extends CompositeCommand {
  constructor(operations, timeout = 5000) {
    super([], { name: '限时批量操作' });
    this.operations = operations;
    this.timeout = timeout;
  }

  async execute() {
    const timeoutPromise = new Promise((_, reject) => {
      setTimeout(() => reject(new Error('操作超时')), this.timeout);
    });

    try {
      const executePromise = this._executeOperations();
      const result = await Promise.race([executePromise, timeoutPromise]);
      
      this.executedCommands = [...this.commands];
      return result;
    } catch (error) {
      await this._rollbackExecutedCommands();
      throw error;
    }
  }

  async _executeOperations() {
    for (const operation of this.operations) {
      const command = new operation.CommandClass(...operation.args);
      await command.execute();
      this.addCommand(command);
    }
    return { success: true, operationCount: this.operations.length };
  }
}
```

## 📊 最佳实践

### 1. 命令粒度控制

```javascript
// ✅ 好的命令粒度 - 逻辑相关的操作组合
class CreateUserProfileCommand extends CompositeCommand {
  constructor(userData) {
    super([], { name: '创建用户档案' });
    this.userData = userData;
  }
  
  async execute() {
    const avatarCmd = new CreateAvatarCommand(this.userData.avatar);
    await avatarCmd.execute();
    this.addCommand(avatarCmd);
    
    const nameCmd = new CreateNameLabelCommand(this.userData.name);
    await nameCmd.execute();
    this.addCommand(nameCmd);
    
    const infoCmd = new CreateInfoPanelCommand(this.userData.info);
    await infoCmd.execute();
    this.addCommand(infoCmd);
    
    this.executedCommands = [...this.commands];
    return { success: true };
  }
}

// ❌ 过大的命令粒度 - 不相关的操作混合
// 应该拆分成多个独立的命令
```

### 2. 命名规范

```javascript
// ✅ 清晰的命令命名
await commandManager.executeBatch([...], '上传并创建图片图层');
new CreateLayerWithObjectsCommand('新图层', objects);
new ImportMultipleImagesCommand(files, layerManager);

// ❌ 模糊的命令命名
await commandManager.executeBatch([...], '操作');
new BatchCommand();
new ProcessCommand();
```

### 3. 性能优化

```javascript
// 对于大量操作，考虑分批处理
class BatchProcessor {
  constructor(commandManager, batchSize = 10) {
    this.commandManager = commandManager;
    this.batchSize = batchSize;
  }

  async processBatch(items, createCommand, progressCallback) {
    const batches = this._createBatches(items, this.batchSize);

    for (let i = 0; i < batches.length; i++) {
      const batch = batches[i];
      const commands = batch.map(item => createCommand(item));
      
      await this.commandManager.executeBatch(
        commands, 
        `批处理 ${i + 1}/${batches.length}`
      );
      
      // 更新进度
      if (progressCallback) {
        const progress = ((i + 1) / batches.length) * 100;
        progressCallback(progress);
      }
    }
  }

  _createBatches(items, batchSize) {
    const batches = [];
    for (let i = 0; i < items.length; i += batchSize) {
      batches.push(items.slice(i, i + batchSize));
    }
    return batches;
  }
}

// 使用示例
const processor = new BatchProcessor(commandManager, 10);
await processor.processBatch(
  largeItemList,
  (item) => new ProcessItemCommand(item),
  (progress) => console.log(`处理进度: ${progress.toFixed(1)}%`)
);
```

## 🔧 API 参考

### CommandManager 核心方法

```javascript
// 执行单个命令
await commandManager.execute(command: Command): Promise<any>

// 批量执行命令
await commandManager.executeBatch(commands: Command[], batchName?: string): Promise<any>

// 撤销/重做
await commandManager.undo(): Promise<any>
await commandManager.redo(): Promise<any>

// 状态管理
commandManager.getState(): ManagerState
commandManager.getHistory(): HistoryInfo
commandManager.clear(): void

// 回调设置
commandManager.setChangeCallback(callback: (state: ManagerState) => void): void
```

### CompositeCommand 核心方法

```javascript
// 添加子命令
compositeCommand.addCommand(command: Command): CompositeCommand
compositeCommand.addCommands(commands: Command[]): CompositeCommand

// 执行和撤销
await compositeCommand.execute(): Promise<any>
await compositeCommand.undo(): Promise<any>

// 获取信息
compositeCommand.getInfo(): CommandInfo
```

## 🚨 常见问题

### Q1: 什么时候使用 executeBatch，什么时候创建自定义复合命令？

A: 
- **使用 executeBatch**: 当命令之间没有数据依赖，只是简单的批量执行时
- **创建自定义复合命令**: 当需要命令间值传递、条件执行或复杂逻辑时

```javascript
// 简单批量 - 使用 executeBatch
await commandManager.executeBatch([
  new SetPropertyCommand(obj, 'x', 100),
  new SetPropertyCommand(obj, 'y', 200)
], '批量设置属性');

// 复杂逻辑 - 创建自定义复合命令
class CreateLayerWithObjectsCommand extends CompositeCommand {
  // 需要使用前一个命令的返回值
}
```

### Q2: 复合命令执行失败时会自动回滚吗？

A: 是的，`CompositeCommand` 在执行失败时会自动回滚已执行的子命令。

```javascript
// 如果第3个命令失败，前2个命令会被自动撤销
const commands = [command1, command2, failingCommand, command4];
try {
  await commandManager.executeBatch(commands);
} catch (error) {
  // 此时 command1 和 command2 已被自动撤销
}
```

### Q3: 如何处理部分命令失败的情况？

A: 创建自定义复合命令，设置错误处理策略：

```javascript
class TolerantBatchCommand extends CompositeCommand {
  constructor(commands, options = {}) {
    super([], { name: '容错批量命令' });
    this.originalCommands = commands;
    this.continueOnError = options.continueOnError || false;
  }
  
  async execute() {
    const results = [];
    
    for (const cmd of this.originalCommands) {
      try {
        await cmd.execute();
        this.addCommand(cmd);
        results.push({ success: true, command: cmd.constructor.name });
      } catch (error) {
        results.push({ success: false, error: error.message });
        
        if (!this.continueOnError) {
          await this._rollbackExecutedCommands();
          throw error;
        }
      }
    }
    
    this.executedCommands = [...this.commands];
    return results;
  }
}
```

### Q4: 如何优化大量命令的性能？

A: 使用分批处理和进度反馈：

```javascript
// 分批处理大量命令
const BATCH_SIZE = 20;
const batches = [];

for (let i = 0; i < largeCommandList.length; i += BATCH_SIZE) {
  batches.push(largeCommandList.slice(i, i + BATCH_SIZE));
}

for (const [index, batch] of batches.entries()) {
  await commandManager.executeBatch(batch, `批次 ${index + 1}/${batches.length}`);
  
  // 给UI更新的机会
  await new Promise(resolve => setTimeout(resolve, 0));
}
```

## 🎯 总结

移除事务机制后的 CommandManager 更加简洁和灵活：

1. **简化架构**: 去除了不必要的事务状态管理
2. **更好的数据流**: 复合命令支持命令间值传递
3. **灵活的错误处理**: 自定义复合命令可以实现各种错误处理策略
4. **更清晰的API**: `executeBatch` 用于简单批量，自定义复合命令用于复杂逻辑
5. **更好的性能**: 减少了状态管理开销

推荐的使用模式：
- **简单批量操作**: 使用 `commandManager.executeBatch()`
- **复杂业务逻辑**: 创建继承自 `CompositeCommand` 的自定义命令
- **需要值传递**: 在自定义复合命令中处理命令间的数据依赖
- **错误处理**: 根据业务需求在复合命令中实现相应的错误处理策略