# 版本控制设计 (Git-like)

> DDS-Section: 5. 版本控制设计 (GIT-like)
> DDS-Lines: L350-L567

## 设计原则

采用**统一版本表**设计，将正式版本和草稿版本存储在同一张表中，通过 `version_type` 字段区分。

## 版本类型

| 版本类型 | 代码 | 说明 |
|:---|:---|:---|
| 正式版本 | `official` | 审核通过后的正式版本，构成版本历史 |
| 填写草稿 | `fill_draft` | 项目创建时填写人的草稿 |
| 修改草稿 | `modify_draft` | 发起变更工单时的草稿 |

## 版本与工单关系

1. **填写草稿**: 与填写工单 1:1 关联
2. **修改草稿**: 与修改工单 1:1 关联
3. **正式版本**: 审核通过后由草稿转化而来
4. **一个项目可以有多个修改草稿**（对应多个修改工单）

## 版本快照机制

每次审核通过后，系统自动生成一个**完整快照**存储到 `project_versions` 表中。

### 快照结构

```go
// VersionSnapshot 版本快照结构
type VersionSnapshot struct {
    BasicInfo        *BasicInfo        `json:"basic_info"`
    DeployBusiness   *DeployBusiness   `json:"deploy_business"`
    DeployEnv        *DeployEnv        `json:"deploy_env"`
    DeployMiddleware *DeployMiddleware `json:"deploy_middleware"`
}
```

### 快照生成时机

| 场景 | 版本号 | 版本类型 | 说明 |
|:---|:---|:---|:---|
| 项目首次审批通过 | v1 | official | 项目初始版本 |
| 修改工单审批通过 | v(N+1) | official | 增量版本 |
| **超管直接修改** | v(N+1) | official | **重要：超管直改也必须生成新版本** |
| 用户保存草稿 | 0 | fill_draft/modify_draft | 临时版本，不计入历史 |

### 超级管理员直改与版本一致性

**问题风险**：
如果超级管理员直接修改 `projects` 表数据而不生成版本历史，会导致版本链断裂，后续基于旧版本的工单 Diff 结果将失效或产生误导。

**解决方案**：
SuperAdmin 的 "Direct Edit" 操作必须被视为一次**自动审批通过的事务**：

1.  **原子操作**：更新 `projects` 表 + 插入 `project_versions` 表必须在同一数据库事务中完成。
2.  **版本归属**：生成的 Version 记录中，`workflow_id` 为空（或特定系统标识），`committer_id` 记录为 SuperAdmin ID。
3.  **结果**：确保 `projects.current_version` 永远指向最新的 `project_versions.version`。

## 并发修改与冲突检测 (Optimistic Locking)

### 冲突场景

1.  用户 A 基于 v3 版本创建草稿（Draft based on v3）。
2.  超级管理员直接修改项目，版本升级为 v4（Current = v4）。
3.  用户 A 提交草稿审核。

### 处理策略

1.  **提交时校验**：工单提交/审核接口需校验 `draft.base_version == project.current_version`。
2.  **冲突提示**：如果版本不一致，后端返回 `409 Conflict` 错误。
3.  **前端交互**：
    *   提示用户："项目已被修改，当前草稿已过期"。
    *   提供 **"Rebase" (变基)** 选项。
    *   或者提供 **"Diff Check"**：让用户查看差异。

## 版本 Diff 算法

采用 **JSON Diff** 算法，对比两个版本快照的差异。

### 差异结构

```go
// DiffResult 差异结果
type DiffResult struct {
    Module     string       `json:"module"`       // 模块名称
    FieldDiffs []FieldDiff  `json:"field_diffs"`  // 字段差异列表
}

// FieldDiff 字段差异
type FieldDiff struct {
    FieldPath  string      `json:"field_path"`   // 字段路径
    FieldName  string      `json:"field_name"`   // 字段中文名
    OldValue   interface{} `json:"old_value"`    // 旧值
    NewValue   interface{} `json:"new_value"`    // 新值
    ChangeType string      `json:"change_type"`  // add/modify/delete
}
```

### Diff 实现

```go
// CompareVersions 比较两个版本的差异
// @param baseVersion 基准版本（通常是较早的版本或 master）
// @param targetVersion 目标版本（通常是较新的版本或草稿）
// @return []DiffResult 差异结果列表，按模块分组
func (s *VersionService) CompareVersions(
    ctx context.Context, 
    baseVersion, targetVersion *VersionSnapshot,
) ([]DiffResult, error) {
    var results []DiffResult
    
    modules := []struct {
        Name   string
        Base   interface{}
        Target interface{}
    }{
        {"基本信息", baseVersion.BasicInfo, targetVersion.BasicInfo},
        {"部署业务", baseVersion.DeployBusiness, targetVersion.DeployBusiness},
        {"部署环境", baseVersion.DeployEnv, targetVersion.DeployEnv},
        {"部署中间件", baseVersion.DeployMiddleware, targetVersion.DeployMiddleware},
    }
    
    for _, m := range modules {
        diffs := s.diffJSON(m.Base, m.Target)
        if len(diffs) > 0 {
            results = append(results, DiffResult{
                Module:     m.Name,
                FieldDiffs: diffs,
            })
        }
    }
    return results, nil
}
```

## 版本历史查询

### 版本列表结构

```go
// VersionHistory 版本历史记录
type VersionHistory struct {
    Version       int       `json:"version"`        // 版本号
    VersionType   string    `json:"version_type"`   // 版本类型
    CommitMessage string    `json:"commit_message"` // 变更说明
    CommitterID   int64     `json:"committer_id"`   // 提交人 ID
    CommitterName string    `json:"committer_name"` // 提交人姓名
    WorkflowID    string    `json:"workflow_id"`    // 关联工单 ID
    CreatedAt     time.Time `json:"created_at"`     // 创建时间
    ChangeSummary string    `json:"change_summary"` // 变更摘要
}
```

### 版本历史 API

| 方法 | 路径 | 描述 |
|:---|:---|:---|
| POST | `/api/project/version/list` | 获取版本历史列表 |
| POST | `/api/project/version/detail` | 获取指定版本详情 |
| POST | `/api/project/version/diff` | 对比两个版本差异 |
| POST | `/api/project/version/diff-with-current` | 对比指定版本与当前版本差异 |
