# 版本管理

分支管理、版本保存、分支合并、冲突解决的完整指南。

## 概念

### 版本结构

```
图纸 (mapid/version)
├── main 分支
│   ├── base (初始版本)
│   ├── patch-001
│   ├── patch-002
│   └── patch-003 (当前)
└── feature-xxx 分支
    ├── base (从 main 分叉)
    └── patch-001
```

### Patch 版本链

```
base → patch-001 → patch-002 → patch-003
```

每个 Patch 记录相对于父版本的增量变更：
- 新增的实体
- 修改的实体
- 删除的实体 ID
- 图层变更
- 元数据（作者、时间、备注等）

---

## 创建分支

### 命令方式

```javascript
// 打开图纸浏览器
await Engine.editor.executerWithOp('OPENFROMSERVER');
// 在版本上右键选择「创建分支」
```

### API 方式

```javascript
const { DrawingManagerService, BranchCreateDialog, message } = vjcad;

const drawingManager = new DrawingManagerService();

// 方式1：通过对话框创建
const dialog = new BranchCreateDialog();
const result = await dialog.showDialog({
    type: 'imports',
    mapid: 'drawing-123',
    version: 'v1',
    fromBranchName: 'main',
    fromPatchId: 'base'
});

if (result && result.action === 'create') {
    const createResult = await drawingManager.createBranch({
        type: result.type,
        mapid: result.mapid,
        version: result.version,
        sourceBranch: result.fromBranchName,
        sourcePatchId: result.fromPatchId,
        branchName: result.newBranchName
    });
    
    if (createResult.status) {
        message.info(`分支 "${result.newBranchName}" 创建成功！`);
    }
}

// 方式2：直接调用 API
const createResult = await drawingManager.createBranch({
    type: 'imports',
    mapid: 'drawing-123',
    version: 'v1',
    sourceBranch: 'main',
    sourcePatchId: 'base',
    branchName: 'feature-new-layer'
});
```

---

## 保存版本（Patch）

### 命令方式

```javascript
await Engine.editor.executerWithOp('SAVESERVER');
```

### API 方式

```javascript
const { DrawingManagerService, Engine } = vjcad;

const drawingManager = new DrawingManagerService();

// 1. 从服务器打开图纸（获取原始数据）
const openResult = await drawingManager.openDrawing({
    type: 'imports',
    mapid: 'drawing-123',
    version: 'v1',
    branch: 'main'
    // 不传 patchId = 该分支最新 patch；传 'base' 会打开原始导入版本，且 latestPatchId 也回传 'base'
});

// 2. 加载到编辑器
const virtualFile = new File([openResult.webcadJson], 'doc', { type: 'application/json' });
await Engine.view.openDbDoc(virtualFile, openResult.webcadData);

// 必须先设 serverSource：openDbDoc 不设置它，而 setOriginalJson 在没有 serverSource 时直接返回 false
// （之后 getOriginalJson 为 null，saveDrawing 会退化成整图保存而不是增量 patch）
Engine.currentDoc.serverSource = {
    type: 'imports',
    mapid: 'drawing-123',
    version: 'v1',
    branchName: 'main',
    lastPatchId: openResult.latestPatchId || 'base'
};
// 保存原始数据（用于 diff 计算）
await Engine.currentDoc.setOriginalJson(openResult.webcadJson);

// 3. 编辑图纸...
// （添加、修改、删除实体）

// 4. 保存（计算 diff 生成 patch）
const currentDoc = Engine.currentDoc;
const currentJson = JSON.stringify(currentDoc.toDb());
const originalJson = await currentDoc.getOriginalJson();

const saveResult = await drawingManager.saveDrawing({
    type: 'imports',
    mapid: 'drawing-123',
    version: 'v1',
    branchName: 'main',
    originalJson: originalJson,
    currentJson: currentJson,
    parentId: currentDoc.serverSource.lastPatchId,
    drawingName: '我的修改',
    author: '用户名',
    remark: '添加了新图层'
});

if (saveResult.status) {
    if (saveResult.patchId === 'no_change') {
        console.log('没有需要保存的修改');
    } else {
        console.log(`保存成功! Patch ID: ${saveResult.patchId}`);
        // 更新本地原始数据与父版本（下次保存以本次 patch 为 parentId）
        await currentDoc.setOriginalJson(currentJson);
        currentDoc.serverSource.lastPatchId = saveResult.patchId;
    }
} else if (saveResult.conflict && saveResult.conflict.hasConflict) {
    console.warn('检测到与其他用户的修改冲突');
} else {
    console.error(`保存失败: ${saveResult.error}`);
}
```

---

## 合并分支

### API 方式

```javascript
const { DrawingManagerService, message } = vjcad;

const drawingManager = new DrawingManagerService();

// 将 feature 分支合并到 main
const mergeResult = await drawingManager.mergeBranch({
    type: 'imports',
    mapid: 'drawing-123',
    version: 'v1',
    sourceBranch: 'feature-new-layer',
    targetBranch: 'main',
    remark: '合并新图层功能'
});

if (mergeResult.status) {
    message.info(`合并成功！Patch ID: ${mergeResult.patchId}`);
} else if (mergeResult.conflict && mergeResult.conflict.hasConflict) {
    message.warn('检测到冲突，需要手动解决');
    // 处理冲突...
} else {
    message.error(`合并失败: ${mergeResult.error}`);
}
```

---

## 查看版本历史

### API 方式

```javascript
const { DrawingManagerService } = vjcad;

const drawingManager = new DrawingManagerService();

// 获取分支列表（包含各分支的 patch 信息）
const branches = await drawingManager.listBranches({
    type: 'imports',
    mapid: 'drawing-123',
    version: 'v1'
});

console.log('分支列表:', branches.map(b => b.name));

// 获取 main 分支的 patch 列表
const mainBranch = branches.find(b => b.name === 'main');
const patches = mainBranch ? mainBranch.patches : [];

console.log(`共 ${patches.length} 个版本`);

// 显示版本详情
for (const patch of patches) {
    console.log(`- ${patch.id}: ${patch.remark || '(无备注)'} by ${patch.author}`);
}
```

### 打开特定版本

```javascript
// 打开指定 Patch 版本
const result = await drawingManager.openDrawing({
    type: 'imports',
    mapid: 'drawing-123',
    version: 'v1',
    branch: 'main',
    patchId: 'patch-001'  // 指定版本
});
```

---

## 删除版本/分支

### 删除 Patch

```javascript
const deleteResult = await drawingManager.deletePatch({
    type: 'imports',
    mapid: 'drawing-123',
    version: 'v1',
    branch: 'main',
    patchId: 'patch-003'
});

if (deleteResult.status) {
    console.log('Patch 已删除');
}
```

### 删除分支

```javascript
const deleteResult = await drawingManager.deleteBranch({
    type: 'imports',
    mapid: 'drawing-123',
    version: 'v1',
    branchName: 'feature-old'
});

if (deleteResult.status) {
    console.log('分支已删除');
}
```

---

## 冲突处理

当两个分支修改了同一个实体时，合并会产生冲突。

### 检测冲突

```javascript
const mergeResult = await drawingManager.mergeBranch({
    type: 'imports',
    mapid: 'drawing-123',
    version: 'v1',
    sourceBranch: 'feature-a',
    targetBranch: 'main'
});

if (mergeResult.conflict && mergeResult.conflict.hasConflict) {
    // 字段是 conflictingEntities（不是 entities）；元素是 IConflictEntityInfo
    const conflicts = mergeResult.conflict.conflictingEntities;
    console.log(`冲突实体数: ${conflicts.length}`);

    for (const c of conflicts) {
        console.log(`实体 ${c.entityId} (${c.entityType}):`);
        console.log(`  - 服务端版本: ${JSON.stringify(c.serverVersion)}`);
        console.log(`  - 客户端版本: ${JSON.stringify(c.clientVersion)}`);
        console.log(`  - 冲突属性: ${c.conflictingProperties}`);   // 如 ["position/x","color"]
    }
}
```

### 解决冲突

冲突解决策略：
1. **选择源分支版本** - 使用功能分支的修改
2. **选择目标分支版本** - 保留主分支的修改
3. **手动合并** - 人工决定最终结果

```javascript
// 冲突解决走 mergeBranch 的 conflictResolution 参数（按实体 ID 指定取舍）
const mergeResult = await drawingManager.mergeBranch({
    type: 'imports',
    mapid: 'drawing-123',
    version: 'v1',
    sourceBranch: 'feature-a',
    targetBranch: 'main',
    conflictResolution: {
        [entityId]: { choice: 'source' }   // 'source' | 'target'，可附 entityData
    }
});
```

---

## 完整工作流示例

```javascript
const { DrawingManagerService, Engine, LineEnt, CircleEnt, message } = vjcad;

const drawingManager = new DrawingManagerService();

// === 1. 创建功能分支 ===
const createResult = await drawingManager.createBranch({
    type: 'imports',
    mapid: 'drawing-123',
    version: 'v1',
    sourceBranch: 'main',
    sourcePatchId: 'base',
    branchName: 'feature-new-layer'
});

if (!createResult.status) {
    throw new Error(`创建分支失败: ${createResult.error}`);
}

message.info('功能分支创建成功');

// === 2. 在功能分支上编辑 ===
const openResult = await drawingManager.openDrawing({
    type: 'imports',
    mapid: 'drawing-123',
    version: 'v1',
    branch: 'feature-new-layer',
    patchId: 'base'
});

const virtualFile = new File([openResult.webcadJson], 'doc', { type: 'application/json' });
await Engine.view.openDbDoc(virtualFile, openResult.webcadData);
Engine.currentDoc.serverSource = {    // 先设来源，否则 setOriginalJson 直接返回 false
    type: 'imports', mapid: 'drawing-123', version: 'v1',
    branchName: 'feature-new-layer', lastPatchId: openResult.latestPatchId || 'base'
};
await Engine.currentDoc.setOriginalJson(openResult.webcadJson);

// 添加新图形
const circle = new CircleEnt([100, 100], 50);
circle.setDefaults();
circle.color = 1;
Engine.addEntities(circle);

// === 3. 保存修改 ===
const currentJson = JSON.stringify(Engine.currentDoc.toDb());

const saveResult = await drawingManager.saveDrawing({
    type: 'imports',
    mapid: 'drawing-123',
    version: 'v1',
    branchName: 'feature-new-layer',
    originalJson: openResult.webcadJson,
    currentJson: currentJson,
    parentId: openResult.latestPatchId || 'base',
    drawingName: '新功能',
    author: '开发者',
    remark: '添加红色圆形'
});

message.info(`保存成功: ${saveResult.patchId}`);

// === 4. 合并到主分支 ===
const mergeResult = await drawingManager.mergeBranch({
    type: 'imports',
    mapid: 'drawing-123',
    version: 'v1',
    sourceBranch: 'feature-new-layer',
    targetBranch: 'main',
    remark: '合并新功能'
});

if (mergeResult.status) {
    message.info('合并成功！');
    
    // === 5. 清理功能分支（可选）===
    await drawingManager.deleteBranch({
        type: 'imports',
        mapid: 'drawing-123',
        version: 'v1',
        branchName: 'feature-new-layer'
    });
    
    message.info('功能分支已删除');
} else if (mergeResult.conflict?.hasConflict) {
    message.warn('存在冲突，需要手动解决');
}
```

---

## API 参考

### DrawingManagerService

| 方法 | 说明 |
|------|------|
| `createBranch(params)` | 创建分支 |
| `deleteBranch(params)` | 删除分支 |
| `listBranches(params)` | 获取分支列表 |
| `mergeBranch(params)` | 合并分支 |
| `saveDrawing(params)` | 保存版本（Patch）|
| `deletePatch(params)` | 删除版本 |
| `openDrawing(params)` | 打开图纸 |

### 分支操作参数

| 参数 | 说明 |
|------|------|
| `type` | 图纸类型：'imports' \| 'designs' |
| `mapid` | 图纸 ID |
| `version` | 版本号（如 'v1'）|
| `branchName` | 分支名称 |
| `sourceBranch` | 源分支 |
| `targetBranch` | 目标分支 |
| `sourcePatchId` | 源 Patch ID |
