更新rmdc-project-management的SKILL
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: developing-project-management
|
||||
description: Guides development of rmdc-project-management module including project lifecycle management, version control (Git-like), ACL permissions, TOTP authorization, and workflow integration. Triggered when modifying project CRUD, draft/version APIs, permission grants, or authorization features. Keywords: project mangement, project lifecycle, version snapshot, ACL, TOTP, workflow callback, SuperAdmin.
|
||||
argument-hint: "<change-type> [target]" where change-type is one of: api|entity|service|migration|frontend|auth. Example: "api draft-submit" or "migration add-field"
|
||||
description: Guides development of rmdc-project-management module including project lifecycle management, Git-like version control with snapshot/diff, ACL permissions, TOTP authorization, and workflow integration. Triggered when modifying project CRUD, draft/version APIs, permission grants, or authorization features. Keywords: project lifecycle, version snapshot, diff algorithm, ACL, TOTP, workflow callback, SuperAdmin, optimistic lock.
|
||||
argument-hint: "<change-type> [target]" where change-type is one of: api|entity|service|migration|frontend|auth|version. Example: "api draft-submit" or "version diff-algorithm"
|
||||
allowed-tools:
|
||||
- Read
|
||||
- Glob
|
||||
@@ -17,9 +17,10 @@ allowed-tools:
|
||||
|
||||
## 模块定位
|
||||
|
||||
- **核心职责**: 项目 CRUD、版本控制(Git-like)、细粒度 ACL 权限、一级 TOTP 授权
|
||||
- **核心职责**: 项目 CRUD、Git-like 版本控制、细粒度 ACL 权限、一级 TOTP 授权
|
||||
- **技术栈**: Go + Gin + GORM + PostgreSQL (JSONB)
|
||||
- **架构**: 模块化单体,通过接口注入与 `rmdc-work-procedure` 工单模块协作
|
||||
- **版本控制思想**: 类似 Git 的分支管理(Master 主线 + 用户草稿分支)
|
||||
|
||||
## 动态上下文注入
|
||||
|
||||
@@ -29,8 +30,8 @@ allowed-tools:
|
||||
# 查看项目管理模块目录结构
|
||||
!`find . -path "*/rmdc-project-management/*" -name "*.go" | head -20`
|
||||
|
||||
# 查找生命周期状态相关代码
|
||||
!`grep -rn "lifecycle_status\|LifecycleStatus" --include="*.go" | head -15`
|
||||
# 查找版本控制相关代码
|
||||
!`grep -rn "VersionSnapshot\|CompareVersions\|DiffResult" --include="*.go" | head -15`
|
||||
```
|
||||
|
||||
---
|
||||
@@ -41,32 +42,23 @@ allowed-tools:
|
||||
|
||||
根据 `$ARGUMENTS` 确定变更范围:
|
||||
|
||||
| 变更类型 | 产物文件 | 影响模块 |
|
||||
|:---|:---|:---|
|
||||
| `api` | `handler/*.go`, `router.go` | rmdc-core 路由注册 |
|
||||
| `entity` | `entity/*.go` | 数据库迁移、DTO 映射 |
|
||||
| `service` | `service/*.go` | 业务逻辑、版本快照 |
|
||||
| `migration` | `migrations/*.sql` | 数据库 Schema |
|
||||
| `frontend` | `pages/*.vue`, `components/*.vue` | 前端联调 |
|
||||
| `auth` | `service/auth_*.go` | TOTP 授权、Exchange-Hub 交互 |
|
||||
| 变更类型 | 产物文件 | 影响模块 | 参考文档 |
|
||||
|:---|:---|:---|:---|
|
||||
| `api` | `handler/*.go`, `router.go` | rmdc-core 路由注册 | `reference/api-endpoints.md` |
|
||||
| `entity` | `entity/*.go` | 数据库迁移、DTO 映射 | `reference/data-structures.md` |
|
||||
| `service` | `service/*.go` | 业务逻辑、版本快照 | `reference/version-control-design.md` |
|
||||
| `migration` | `migrations/*.sql` | 数据库 Schema | `reference/database-schema.md` |
|
||||
| `frontend` | `pages/*.vue`, `components/*.vue` | 前端联调 | `reference/frontend-design.md` |
|
||||
| `auth` | `service/auth_*.go` | TOTP 授权、Exchange-Hub 交互 | `reference/acl-permission-model.md` |
|
||||
| `version` | `service/version_*.go` | 版本快照、Diff 算法 | `reference/version-control-design.md` |
|
||||
|
||||
### 决策点
|
||||
|
||||
1. **是否涉及生命周期状态变更?**
|
||||
- 若涉及,必须同步更新状态机转换逻辑
|
||||
- 检查 `reference/lifecycle-state-machine.md`
|
||||
|
||||
2. **是否修改版本快照结构?**
|
||||
- 若涉及,需评估历史版本兼容性
|
||||
- 更新 `VersionSnapshot` 结构体
|
||||
|
||||
3. **是否变更 ACL 权限模型?**
|
||||
- 若涉及,需同步 `rmdc-user-auth` 模块
|
||||
- 检查 `reference/acl-permission-model.md`
|
||||
|
||||
4. **是否影响工单模块回调?**
|
||||
- 若涉及,需更新 `ProjectLifecycleUpdater` 接口实现
|
||||
- 检查 `reference/workflow-state-mapping.md`
|
||||
1. **是否涉及生命周期状态变更?** → 检查 `reference/lifecycle-state-machine.md`
|
||||
2. **是否修改版本快照结构?** → 检查 `reference/version-control-design.md` 第5节
|
||||
3. **是否涉及并发修改冲突?** → 检查乐观锁实现(base_version 校验)
|
||||
4. **是否变更 ACL 权限模型?** → 检查 `reference/acl-permission-model.md`
|
||||
5. **是否影响工单模块回调?** → 检查 `reference/workflow-state-mapping.md`
|
||||
|
||||
---
|
||||
|
||||
@@ -77,20 +69,25 @@ allowed-tools:
|
||||
- [ ] **生命周期状态机完整性**: 所有状态转换有明确的触发条件和权限控制
|
||||
- [ ] **版本快照一致性**: `projects` 表与 `project_versions` 表数据同步
|
||||
- [ ] **乐观锁检查**: 并发修改时 `base_version == current_version` 校验存在
|
||||
- [ ] **ACL 权限验证**: 接口权限注解与业务逻辑一致
|
||||
- [ ] **超管直改版本生成**: SuperAdmin 直接修改必须同时生成版本记录(原子事务)
|
||||
- [ ] **Diff 算法正确性**: 版本对比结果按模块分组,字段路径完整,中文名映射正确
|
||||
- [ ] **ACL 权限验证**: 接口权限注解与业务逻辑一致,授权模块仅 SuperAdmin 可见
|
||||
- [ ] **工单回调幂等**: 状态更新操作具备幂等性
|
||||
- [ ] **敏感字段加密**: 密码字段使用 AES-256 加密存储
|
||||
- [ ] **审计日志**: 所有写操作记录到 `rmdc-audit-log`
|
||||
- [ ] **TOTP 授权安全**: 一级密钥仅 SuperAdmin 可访问
|
||||
- [ ] **Namespace 校验**: 符合 RFC 1123 DNS 标签规范
|
||||
|
||||
### 验证命令
|
||||
|
||||
```bash
|
||||
# 检查实体字段与数据库 Schema 一致性
|
||||
!`grep -rn "gorm:\"" entity/project.go | head -20`
|
||||
# 检查版本服务实现
|
||||
!`grep -rn "CompareVersions\|CreateOfficialVersion\|VersionSnapshot" service/*.go`
|
||||
|
||||
# 检查 API 路由权限注解
|
||||
!`grep -rn "RequireRole\|RequirePermission" handler/*.go`
|
||||
# 检查乐观锁实现
|
||||
!`grep -rn "base_version\|BaseVersion\|VersionConflict\|409" --include="*.go"`
|
||||
|
||||
# 检查敏感字段加密
|
||||
!`grep -rn "EncryptAES\|DecryptAES\|admin_password\|ssh_pwd" --include="*.go"`
|
||||
|
||||
# 运行模块单元测试
|
||||
go test ./internal/project/... -v -cover
|
||||
@@ -102,19 +99,26 @@ go test ./internal/project/... -v -cover
|
||||
|
||||
### API 开发流程
|
||||
|
||||
1. **定义请求/响应结构体** → `dto/project_dto.go`
|
||||
2. **实现 Service 方法** → `service/project_service.go`
|
||||
3. **实现 Handler 方法** → `handler/project_handler.go`
|
||||
4. **注册路由** → `router.go` (注意权限中间件)
|
||||
5. **编写单元测试** → `*_test.go`
|
||||
1. 定义请求/响应结构体 → `dto/project_dto.go`
|
||||
2. 实现 Service 方法 → `service/project_service.go`
|
||||
3. 实现 Handler 方法 → `handler/project_handler.go`
|
||||
4. 注册路由 → `router.go` (注意权限中间件)
|
||||
5. 编写单元测试 → `*_test.go`
|
||||
|
||||
### 版本快照变更流程
|
||||
|
||||
1. 更新 `VersionSnapshot` 结构体定义
|
||||
2. 确保 `CompareVersions` Diff 算法兼容新字段
|
||||
3. 添加字段到 Diff 结果的字段名映射表
|
||||
1. 更新 `VersionSnapshot` 结构体定义 → `reference/data-structures.md`
|
||||
2. 更新字段名映射表 `fieldNameMap` → 确保 Diff 显示中文名
|
||||
3. 确保 `CompareVersions` Diff 算法兼容新字段
|
||||
4. 测试历史版本查看功能不受影响
|
||||
|
||||
### SuperAdmin 直改流程
|
||||
|
||||
1. 更新 `projects` 表 + 插入 `project_versions` 表**必须在同一事务**
|
||||
2. `workflow_id` 设为空或 `DIRECT_EDIT` 标识
|
||||
3. `committer_id` 记录 SuperAdmin ID
|
||||
4. 更新 `current_version` 字段
|
||||
|
||||
### 生命周期状态变更流程
|
||||
|
||||
1. 更新 `reference/lifecycle-state-machine.md` 状态图
|
||||
@@ -122,32 +126,25 @@ go test ./internal/project/... -v -cover
|
||||
3. 同步更新 `ProjectLifecycleUpdater` 接口实现
|
||||
4. 验证与工单模块的状态映射表一致
|
||||
|
||||
### 授权功能变更流程
|
||||
|
||||
1. 检查 `project_auth_configs` 表结构
|
||||
2. 更新 `AuthorizationInfo` 结构体
|
||||
3. 确保 TOTP 密钥生成/验证逻辑正确
|
||||
4. 测试与 Exchange-Hub 的授权指令下发
|
||||
|
||||
---
|
||||
|
||||
## Pitfalls(常见问题)
|
||||
|
||||
1. **超管直改未生成版本**: SuperAdmin 直接修改 `projects` 表时,必须同时插入 `project_versions` 记录,否则版本链断裂
|
||||
1. **超管直改未生成版本**: SuperAdmin 直接修改 `projects` 表时,必须同时插入 `project_versions` 记录,否则版本链断裂,后续 Diff 失效
|
||||
|
||||
2. **草稿基准版本过期**: 用户 A 基于 v3 创建草稿,超管修改产生 v4,用户 A 提交时需检测冲突并提示 Rebase
|
||||
2. **草稿基准版本过期**: 用户 A 基于 v3 创建草稿,超管修改产生 v4,用户 A 提交时需检测冲突(`draft.base_version != project.current_version`)并返回 409 Conflict
|
||||
|
||||
3. **工单回调重复处理**: 工单模块可能重试回调,`ProjectLifecycleUpdater` 实现必须幂等
|
||||
|
||||
4. **ACL 权限遗漏授权模块**: `authorization_info` 模块仅 SuperAdmin 可见,其他角色查询时需过滤
|
||||
|
||||
5. **密码字段明文泄露**: `AdminPassword`、`SSHPwd` 等字段响应时必须脱敏或不返回
|
||||
5. **密码字段明文泄露**: `AdminPassword`、`SSHPwd` 等字段响应时必须脱敏(返回 `********`)
|
||||
|
||||
6. **省市级联校验缺失**: 前端省市级联选择后,后端需校验省市对应关系有效性
|
||||
6. **Namespace 唯一性**: 创建项目时必须校验 `namespace` 全局唯一且符合 RFC 1123 DNS 标签规范(小写字母开头,只含小写字母/数字/-/.)
|
||||
|
||||
7. **Namespace 唯一性**: 创建项目时必须校验 `namespace` 全局唯一且符合 RFC 1123 DNS 标签规范
|
||||
7. **JSONB 字段空值处理**: `basic_info`、`deploy_business` 等 JSONB 字段为空时,需返回空对象 `{}` 而非 `null`
|
||||
|
||||
8. **JSONB 字段空值处理**: `basic_info`、`deploy_business` 等 JSONB 字段为空时,需返回空对象 `{}` 而非 `null`
|
||||
8. **版本号混淆**: 草稿版本号为 0,正式版本从 1 开始递增,切勿混淆;正式版本必须保证唯一性
|
||||
|
||||
---
|
||||
|
||||
@@ -156,29 +153,34 @@ go test ./internal/project/... -v -cover
|
||||
```
|
||||
rmdc-project-management
|
||||
├── → rmdc-user-auth (用户鉴权、ACL 权限查询)
|
||||
├── → rmdc-work-procedure (工单创建、状态转换)
|
||||
├── ↔ rmdc-work-procedure (工单创建/状态转换 + 回调更新生命周期)
|
||||
├── → rmdc-audit-log (操作审计记录)
|
||||
├── → rmdc-exchange-hub (授权指令下发)
|
||||
└── ← rmdc-core (路由注册、依赖注入)
|
||||
```
|
||||
|
||||
## 关键接口
|
||||
## 关键接口速查
|
||||
|
||||
| 类别 | 路径 | 权限 |
|
||||
|:---|:---|:---|
|
||||
| 项目列表 | `POST /api/project/list` | Login |
|
||||
| 项目详情 | `POST /api/project/detail` | View ACL |
|
||||
| 创建项目 | `POST /api/project/create` | SuperAdmin |
|
||||
| 直接更新 | `POST /api/project/update` | SuperAdmin |
|
||||
| 保存草稿 | `POST /api/project/draft/save` | View ACL |
|
||||
| 提交审核 | `POST /api/project/draft/submit` | View ACL |
|
||||
| 版本历史 | `POST /api/project/version/list` | View ACL |
|
||||
| 权限分配 | `POST /api/project/permission/grant` | SuperAdmin |
|
||||
| 类别 | 路径 | 权限 | 说明 |
|
||||
|:---|:---|:---|:---|
|
||||
| 项目列表 | `POST /api/project/list` | Login | 自动过滤 ACL |
|
||||
| 项目详情 | `POST /api/project/detail` | View ACL | Master 版本 |
|
||||
| 创建项目 | `POST /api/project/create` | SuperAdmin | 同时创建填写工单 |
|
||||
| 直接更新 | `POST /api/project/update` | SuperAdmin | 必须生成新版本 |
|
||||
| 保存草稿 | `POST /api/project/draft/save` | View ACL | 更新草稿快照 |
|
||||
| 提交审核 | `POST /api/project/draft/submit` | View ACL | 检测版本冲突 |
|
||||
| 版本历史 | `POST /api/project/version/list` | View ACL | 仅 official 类型 |
|
||||
| 版本对比 | `POST /api/project/version/diff` | View ACL | 按模块分组 |
|
||||
| 权限分配 | `POST /api/project/permission/grant` | SuperAdmin | 模块级权限 |
|
||||
|
||||
## 相关文档
|
||||
|
||||
- 生命周期状态机: `reference/lifecycle-state-machine.md`
|
||||
- API 端点清单: `reference/api-endpoints.md`
|
||||
- 数据库 Schema: `reference/database-schema.md`
|
||||
- ACL 权限模型: `reference/acl-permission-model.md`
|
||||
- 工单状态映射: `reference/workflow-state-mapping.md`
|
||||
| 文档 | 内容 |
|
||||
|:---|:---|
|
||||
| `reference/lifecycle-state-machine.md` | 生命周期状态机、状态转换条件 |
|
||||
| `reference/version-control-design.md` | 版本快照、Diff 算法、乐观锁、冲突检测 |
|
||||
| `reference/database-schema.md` | DDL、索引、JSONB 结构示例 |
|
||||
| `reference/data-structures.md` | 实体定义、枚举常量、字段校验规则 |
|
||||
| `reference/acl-permission-model.md` | RBAC/ACL 权限模型、权限检查流程 |
|
||||
| `reference/workflow-state-mapping.md` | 工单状态与项目生命周期映射、回调接口 |
|
||||
| `reference/api-endpoints.md` | API 清单、请求/响应示例 |
|
||||
|
||||
@@ -0,0 +1,428 @@
|
||||
# 数据结构定义
|
||||
|
||||
本文档定义项目管理模块中所有核心数据结构,包括实体、DTO、JSONB 存储结构。
|
||||
|
||||
---
|
||||
|
||||
## 1. 项目主表实体 (Project)
|
||||
|
||||
```go
|
||||
// Project 项目主表
|
||||
type Project struct {
|
||||
ID int64 `gorm:"primaryKey;autoIncrement" json:"id"`
|
||||
ProjectID string `gorm:"type:varchar(64);uniqueIndex;not null" json:"project_id"`
|
||||
Name string `gorm:"type:varchar(128);not null" json:"name"`
|
||||
Namespace string `gorm:"type:varchar(64);uniqueIndex;not null" json:"namespace"`
|
||||
|
||||
// 生命周期状态: INIT/DRAFTING/REVIEWING/RELEASED/MODIFYING/ARCHIVED
|
||||
LifecycleStatus string `gorm:"type:varchar(32);default:'INIT'" json:"lifecycle_status"`
|
||||
// 认证状态: draft/pending/official
|
||||
CertificationStatus string `gorm:"type:varchar(32);default:'draft'" json:"certification_status"`
|
||||
|
||||
// 当前正式版本号
|
||||
CurrentVersion int `gorm:"default:0" json:"current_version"`
|
||||
|
||||
// 主版本数据 (使用JSONB存储,便于版本快照)
|
||||
BasicInfo json.RawMessage `gorm:"type:jsonb" json:"basic_info"`
|
||||
DeployBusiness json.RawMessage `gorm:"type:jsonb" json:"deploy_business"`
|
||||
DeployEnv json.RawMessage `gorm:"type:jsonb" json:"deploy_env"`
|
||||
DeployMiddleware json.RawMessage `gorm:"type:jsonb" json:"deploy_middleware"`
|
||||
|
||||
// 项目填写人
|
||||
DetailFillerID int64 `json:"detail_filler_id"`
|
||||
DetailFillerName string `gorm:"type:varchar(64)" json:"detail_filler_name"`
|
||||
|
||||
// 审计字段
|
||||
CreatedBy int64 `json:"created_by"`
|
||||
CreatedByName string `gorm:"type:varchar(64)" json:"created_by_name"`
|
||||
|
||||
common.BaseModel // CreatedAt, UpdatedAt, DeletedAt
|
||||
}
|
||||
|
||||
func (Project) TableName() string {
|
||||
return "projects"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 版本表实体 (ProjectVersion)
|
||||
|
||||
```go
|
||||
// ProjectVersion 项目版本表 (含草稿)
|
||||
type ProjectVersion struct {
|
||||
ID int64 `gorm:"primaryKey;autoIncrement" json:"id"`
|
||||
ProjectID string `gorm:"type:varchar(64);index;not null" json:"project_id"`
|
||||
|
||||
// 版本号 (正式版本递增, 草稿为0)
|
||||
Version int `gorm:"not null;default:0" json:"version"`
|
||||
|
||||
// 版本类型: official/fill_draft/modify_draft
|
||||
VersionType string `gorm:"type:varchar(32);not null" json:"version_type"`
|
||||
|
||||
// 基准版本号(草稿基于哪个正式版本创建,用于乐观锁冲突检测)
|
||||
BaseVersion int `gorm:"default:0" json:"base_version"`
|
||||
|
||||
// 草稿所属用户ID (仅草稿类型有值)
|
||||
UserID int64 `gorm:"index" json:"user_id"`
|
||||
UserName string `gorm:"type:varchar(64)" json:"user_name"`
|
||||
|
||||
// 关联工单ID (1:1关系)
|
||||
WorkflowID string `gorm:"type:varchar(64);index" json:"workflow_id"`
|
||||
|
||||
// 完整快照数据
|
||||
SnapshotData json.RawMessage `gorm:"type:jsonb" json:"snapshot_data"`
|
||||
|
||||
// 变更信息
|
||||
CommitMessage string `gorm:"type:varchar(255)" json:"commit_message"`
|
||||
CommitterID int64 `json:"committer_id"`
|
||||
CommitterName string `gorm:"type:varchar(64)" json:"committer_name"`
|
||||
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
func (ProjectVersion) TableName() string {
|
||||
return "project_versions"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 项目工单关联表 (ProjectWorkflow)
|
||||
|
||||
```go
|
||||
// ProjectWorkflow 项目与工单关联表
|
||||
type ProjectWorkflow struct {
|
||||
ID int64 `gorm:"primaryKey;autoIncrement" json:"id"`
|
||||
ProjectID string `gorm:"type:varchar(64);index;not null" json:"project_id"`
|
||||
WorkflowID string `gorm:"type:varchar(64);uniqueIndex;not null" json:"workflow_id"`
|
||||
|
||||
// 工单类型: fill(填写)/modify(修改)
|
||||
WorkflowType string `gorm:"type:varchar(32);not null" json:"workflow_type"`
|
||||
|
||||
// 工单状态 (冗余存储,便于查询)
|
||||
Status string `gorm:"type:varchar(32)" json:"status"`
|
||||
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
```
|
||||
|
||||
### 项目与工单关系说明
|
||||
|
||||
| 关系类型 | 项目状态 | 约束 |
|
||||
|:---|:---|:---|
|
||||
| 项目:填写工单 = 1:1 | INIT/DRAFTING | 项目创建时只能有一个填写工单 |
|
||||
| 项目:修改工单 = 1:N | RELEASED/MODIFYING | 已发布项目可以有多个修改工单 |
|
||||
| 用户:修改工单 = 1:1 (per project) | - | 非SuperAdmin用户同一项目只能有一个活跃修改工单 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 授权配置表 (ProjectAuthConfig)
|
||||
|
||||
```go
|
||||
// ProjectAuthConfig 项目授权配置
|
||||
type ProjectAuthConfig struct {
|
||||
ID int64 `gorm:"primaryKey;autoIncrement" json:"id"`
|
||||
ProjectID string `gorm:"type:varchar(64);uniqueIndex;not null" json:"project_id"`
|
||||
|
||||
// 一级授权 (项目管理模块管理)
|
||||
TierOneSecret string `gorm:"type:varchar(128)" json:"tier_one_secret"` // 加密存储
|
||||
TimeOffset int `gorm:"default:30" json:"time_offset"` // 允许时间偏移(秒)
|
||||
TOTPEnabled bool `gorm:"default:false" json:"totp_enabled"`
|
||||
|
||||
// 二级授权 (来自 Watchdog)
|
||||
TierTwoSecret string `gorm:"type:varchar(128)" json:"tier_two_secret"` // 加密存储
|
||||
|
||||
// 授权状态
|
||||
AuthType string `gorm:"type:varchar(32)" json:"auth_type"` // permanent/time_limited
|
||||
AuthDays int `json:"auth_days"` // 授权有效期(天)
|
||||
AuthorizedAt time.Time `json:"authorized_at"`
|
||||
RevokedAt time.Time `json:"revoked_at"`
|
||||
IsOffline bool `gorm:"default:false" json:"is_offline"` // 是否离线授权
|
||||
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. JSONB 结构定义
|
||||
|
||||
### 5.1 基本信息 (BasicInfo)
|
||||
|
||||
```go
|
||||
type BasicInfo struct {
|
||||
Province string `json:"province"` // 省份(枚举,参见省市列表)
|
||||
City string `json:"city"` // 城市(级联选择)
|
||||
IndustryContact string `json:"industry_contact"` // 行业组人员姓名
|
||||
IndustryPhone string `json:"industry_phone"` // 行业组人员电话
|
||||
ProjectNature string `json:"project_nature"` // 项目性质(枚举)
|
||||
}
|
||||
```
|
||||
|
||||
**项目性质枚举**:
|
||||
|
||||
| 值 | 说明 |
|
||||
|:---|:---|
|
||||
| `research` | 科研 |
|
||||
| `test` | 测试 |
|
||||
| `trial` | 试用 |
|
||||
| `market` | 市场化 |
|
||||
| `sub_platform` | 二级平台 |
|
||||
|
||||
### 5.2 部署业务 (DeployBusiness)
|
||||
|
||||
```go
|
||||
type DeployBusiness struct {
|
||||
DeployerName string `json:"deployer_name"` // 部署人姓名
|
||||
DeployerPhone string `json:"deployer_phone"` // 部署人电话
|
||||
DeployStartTime string `json:"deploy_start_time"` // 部署开始时间 (YYYY-MM-DD)
|
||||
DeployEndTime string `json:"deploy_end_time"` // 部署结束时间 (YYYY-MM-DD)
|
||||
SystemVersion string `json:"system_version"` // 部署系统版本
|
||||
SystemType string `json:"system_type"` // 系统类型(枚举)
|
||||
MainEntrance string `json:"main_entrance"` // 业务主要入口URL
|
||||
AdminUsername string `json:"admin_username"` // 系统超管用户名
|
||||
AdminPassword string `json:"admin_password"` // 系统超管密码 ⚠️加密存储
|
||||
}
|
||||
```
|
||||
|
||||
**系统类型枚举**:
|
||||
|
||||
| 值 | 说明 |
|
||||
|:---|:---|
|
||||
| `business` | 老行业平台 |
|
||||
| `fly-control` | 新飞控平台 |
|
||||
| `supervisor` | 监管平台 |
|
||||
|
||||
### 5.3 部署环境 (DeployEnv)
|
||||
|
||||
```go
|
||||
type DeployEnv struct {
|
||||
// 主机信息列表
|
||||
Hosts []HostInfo `json:"hosts"`
|
||||
|
||||
// 网络环境
|
||||
NetworkType string `json:"network_type"` // 网络类型(枚举)
|
||||
MainPublicIP string `json:"main_public_ip"` // 主要公网IP
|
||||
DomainURL string `json:"domain_url"` // 域名URL
|
||||
SSLEnabled bool `json:"ssl_enabled"` // 是否开启SSL
|
||||
|
||||
// 管理方式
|
||||
ManagementType string `json:"management_type"` // 管理类型(枚举)
|
||||
ManagementURL string `json:"management_url"` // 管理后台URL
|
||||
ManagementUser string `json:"management_user"` // 管理后台用户名
|
||||
ManagementPwd string `json:"management_pwd"` // 管理后台密码 ⚠️加密存储
|
||||
|
||||
// 统计信息
|
||||
HostCount int `json:"host_count"` // 主机台数
|
||||
TotalCPU int `json:"total_cpu"` // CPU总核数
|
||||
CPUModel string `json:"cpu_model"` // CPU型号
|
||||
TotalMemory int `json:"total_memory"` // 内存总大小(GB)
|
||||
TotalStorage int `json:"total_storage"` // 存储总大小(GB)
|
||||
}
|
||||
|
||||
type HostInfo struct {
|
||||
Hostname string `json:"hostname"` // 主机名
|
||||
InternalIP string `json:"internal_ip"` // 内网IP
|
||||
PublicIP string `json:"public_ip"` // 公网IP(可选)
|
||||
CanAccessPublic bool `json:"can_access_public"` // 能否访问公网
|
||||
SSHPort int `json:"ssh_port"` // SSH端口
|
||||
SSHUser string `json:"ssh_user"` // SSH用户名
|
||||
SSHPwd string `json:"ssh_pwd"` // SSH密码 ⚠️加密存储
|
||||
Role string `json:"role"` // 主机角色(枚举)
|
||||
}
|
||||
```
|
||||
|
||||
**网络类型枚举**:
|
||||
|
||||
| 值 | 说明 |
|
||||
|:---|:---|
|
||||
| `internal` | 完全内网 |
|
||||
| `single_public` | 单主机公网 |
|
||||
| `full_public` | 全访问公网 |
|
||||
|
||||
**管理类型枚举**:
|
||||
|
||||
| 值 | 说明 |
|
||||
|:---|:---|
|
||||
| `bastion` | 堡垒机 |
|
||||
| `whitelist` | 白名单 |
|
||||
| `vpn` | VPN |
|
||||
|
||||
**主机角色枚举**:
|
||||
|
||||
| 值 | 说明 |
|
||||
|:---|:---|
|
||||
| `master` | 主节点 |
|
||||
| `worker` | 工作节点 |
|
||||
| `storage` | 存储节点 |
|
||||
|
||||
### 5.4 部署中间件 (DeployMiddleware)
|
||||
|
||||
```go
|
||||
type DeployMiddleware struct {
|
||||
MySQL MiddlewareInfo `json:"mysql"`
|
||||
Redis MiddlewareInfo `json:"redis"`
|
||||
EMQX MiddlewareInfo `json:"emqx"`
|
||||
MinIO MiddlewareInfo `json:"minio"`
|
||||
InfluxDB MiddlewareInfo `json:"influxdb"`
|
||||
Nacos MiddlewareInfo `json:"nacos"`
|
||||
K8SDashboard MiddlewareInfo `json:"k8s_dashboard"`
|
||||
}
|
||||
|
||||
// MiddlewareInfo 通用中间件信息
|
||||
type MiddlewareInfo struct {
|
||||
PublicIP string `json:"public_ip"` // 公网IP
|
||||
PublicPort int `json:"public_port"` // 公网端口
|
||||
InternalIP string `json:"internal_ip"` // 内网IP
|
||||
InternalPort int `json:"internal_port"` // 内网端口
|
||||
K8SAddress string `json:"k8s_address"` // K8S集群内访问地址 (Service Name)
|
||||
K8SPort int `json:"k8s_port"` // K8S端口
|
||||
AdminUser string `json:"admin_user"` // 超管用户名
|
||||
AdminPwd string `json:"admin_pwd"` // 超管密码 ⚠️加密存储
|
||||
Version string `json:"version"` // 中间件版本
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 版本快照结构 (VersionSnapshot)
|
||||
|
||||
```go
|
||||
// VersionSnapshot 版本快照结构(存储在 project_versions.snapshot_data)
|
||||
type VersionSnapshot struct {
|
||||
BasicInfo *BasicInfo `json:"basic_info"`
|
||||
DeployBusiness *DeployBusiness `json:"deploy_business"`
|
||||
DeployEnv *DeployEnv `json:"deploy_env"`
|
||||
DeployMiddleware *DeployMiddleware `json:"deploy_middleware"`
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 状态常量定义
|
||||
|
||||
```go
|
||||
// 生命周期状态
|
||||
const (
|
||||
LifecycleInit = "INIT" // 已创建,等待填写
|
||||
LifecycleDrafting = "DRAFTING" // 填写中
|
||||
LifecycleReviewing = "REVIEWING" // 审核中
|
||||
LifecycleReleased = "RELEASED" // 已发布
|
||||
LifecycleModifying = "MODIFYING" // 变更中
|
||||
LifecycleArchived = "ARCHIVED" // 已归档
|
||||
)
|
||||
|
||||
// 认证状态
|
||||
const (
|
||||
CertificationDraft = "draft" // 草稿
|
||||
CertificationPending = "pending" // 待审核
|
||||
CertificationOfficial = "official" // 正式
|
||||
)
|
||||
|
||||
// 版本类型
|
||||
const (
|
||||
VersionTypeOfficial = "official" // 正式版本
|
||||
VersionTypeFillDraft = "fill_draft" // 填写草稿
|
||||
VersionTypeModifyDraft = "modify_draft" // 修改草稿
|
||||
)
|
||||
|
||||
// 工单类型
|
||||
const (
|
||||
WorkflowTypeFill = "fill" // 填写工单
|
||||
WorkflowTypeModify = "modify" // 修改工单
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 敏感字段加密说明
|
||||
|
||||
以下字段必须使用 **AES-256** 加密存储,密钥使用项目的 `TierOneSecret`:
|
||||
|
||||
| 结构体 | 字段 | 说明 |
|
||||
|:---|:---|:---|
|
||||
| DeployBusiness | `admin_password` | 系统超管密码 |
|
||||
| DeployEnv | `management_pwd` | 管理后台密码 |
|
||||
| HostInfo | `ssh_pwd` | SSH密码 |
|
||||
| MiddlewareInfo | `admin_pwd` | 中间件超管密码 |
|
||||
| ProjectAuthConfig | `tier_one_secret` | 一级TOTP密钥 |
|
||||
| ProjectAuthConfig | `tier_two_secret` | 二级TOTP密钥 |
|
||||
|
||||
### 加密/解密示例
|
||||
|
||||
```go
|
||||
// 加密敏感字段
|
||||
func (s *CryptoService) EncryptSensitiveFields(data *DeployBusiness, key []byte) error {
|
||||
if data.AdminPassword != "" {
|
||||
encrypted, err := s.EncryptAES256(data.AdminPassword, key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data.AdminPassword = encrypted
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 解密敏感字段(返回给前端时脱敏)
|
||||
func (s *CryptoService) MaskSensitiveFields(data *DeployBusiness) {
|
||||
if data.AdminPassword != "" {
|
||||
data.AdminPassword = "********" // 脱敏处理
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 字段校验规则
|
||||
|
||||
### Namespace 校验 (RFC 1123 DNS 标签规范)
|
||||
|
||||
```go
|
||||
var namespaceRegex = regexp.MustCompile(`^[a-z][a-z0-9.-]{0,251}[a-z0-9]$`)
|
||||
|
||||
func ValidateNamespace(namespace string) error {
|
||||
if len(namespace) > 253 {
|
||||
return errors.New("命名空间长度不能超过253个字符")
|
||||
}
|
||||
if !namespaceRegex.MatchString(namespace) {
|
||||
return errors.New("命名空间只能包含小写字母、数字、'-'和'.',必须以字母开头,以字母或数字结尾")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
### IP 地址校验
|
||||
|
||||
```go
|
||||
func ValidateIP(ip string) error {
|
||||
if ip == "" || ip == "无" {
|
||||
return nil // 允许空值
|
||||
}
|
||||
if net.ParseIP(ip) == nil {
|
||||
return errors.New("无效的IP地址格式")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
### 省市级联校验
|
||||
|
||||
```go
|
||||
// 后端需维护省市对应关系表,校验城市是否属于所选省份
|
||||
func ValidateProvinceCity(province, city string) error {
|
||||
validCities, ok := provinceCityMap[province]
|
||||
if !ok {
|
||||
return errors.New("无效的省份")
|
||||
}
|
||||
for _, c := range validCities {
|
||||
if c == city {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return errors.New("城市不属于所选省份")
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,496 @@
|
||||
# 前端页面设计规范
|
||||
|
||||
本文档定义项目详情页面的前端设计规范,包括页面架构、组件设计、交互行为和视觉规范。
|
||||
|
||||
---
|
||||
|
||||
## 1. 页面文件结构
|
||||
|
||||
```
|
||||
frontend/src/modules/admin/
|
||||
├── pages/
|
||||
│ ├── admin/
|
||||
│ │ └── ProjectDetail.vue # 超级管理员端项目详情
|
||||
│ └── user/
|
||||
│ └── UserProjectDetail.vue # 普通用户端项目详情
|
||||
├── components/
|
||||
│ ├── BasicInfoForm.vue # 基本信息编辑表单
|
||||
│ ├── BasicInfoReadonly.vue # 基本信息只读展示
|
||||
│ ├── BusinessInfoReadonly.vue # 业务信息只读展示
|
||||
│ ├── DeploymentBusinessForm.vue # 部署业务编辑表单
|
||||
│ ├── DeploymentEnvironmentForm.vue # 部署环境编辑表单
|
||||
│ ├── EnvironmentInfoReadonly.vue # 环境信息只读展示
|
||||
│ ├── HostsInfoReadonly.vue # 主机信息只读展示
|
||||
│ ├── HostsManagement.vue # 主机管理组件
|
||||
│ ├── MiddlewareCardsGrid.vue # 中间件卡片网格
|
||||
│ ├── MiddlewareInfoReadonly.vue # 中间件只读展示
|
||||
│ ├── AuthorizationManagement.vue # 授权管理 (SuperAdmin Only)
|
||||
│ ├── VersionHistory.vue # 版本历史 (SuperAdmin Only)
|
||||
│ ├── SaveConfirmDialog.vue # 保存确认对话框
|
||||
│ ├── CopyableField.vue # 可复制字段组件
|
||||
│ └── DiffTextField.vue # 差异高亮输入框
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 页面架构设计
|
||||
|
||||
### 2.1 整体布局
|
||||
|
||||
采用 **「固定头部 + 固定 Tab 导航 + 可滚动内容区域」** 三段式布局:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ [固定区域] 生命周期状态提示横幅 (Alert Banner) │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ [固定区域] 页面头部 Header │
|
||||
│ ┌─────────────────────────────────────┬─────────────────────────────┐ │
|
||||
│ │ ← 返回 项目名称 │ [查看工单] [打回] [通过] │ │
|
||||
│ │ Namespace | 省份 城市 │ [下载配置] [编辑/保存] │ │
|
||||
│ │ 状态标签组 │ │ │
|
||||
│ └─────────────────────────────────────┴─────────────────────────────┘ │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ [固定区域] Tab 导航栏 │
|
||||
│ ┌─────────────────────────────────────────────────────────────────────┐│
|
||||
│ │ 基本信息 | 部署业务 | 部署环境 | 主机管理 | 中间件 | 授权 | 版本历史 ││
|
||||
│ └─────────────────────────────────────────────────────────────────────┘│
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ [滚动区域] Tab 内容区域 │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.2 CSS 布局核心
|
||||
|
||||
```css
|
||||
.project-detail-page {
|
||||
height: 100%;
|
||||
max-height: 100%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.header-section {
|
||||
flex-shrink: 0;
|
||||
background: rgb(var(--v-theme-surface));
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.content-area {
|
||||
flex: 1 1 auto;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
min-height: 0; /* 关键:防止 Flex 子元素撑破父容器 */
|
||||
padding-bottom: 24px;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 查看/编辑状态分离
|
||||
|
||||
### 3.1 状态定义
|
||||
|
||||
| 状态 | 变量名 | 说明 |
|
||||
|:---|:---|:---|
|
||||
| **查看状态** | `isEditMode = false` | 默认状态,展示 `*Readonly.vue` 组件 |
|
||||
| **编辑状态** | `isEditMode = true` | 编辑模式,展示 `*Form.vue` 组件 |
|
||||
|
||||
### 3.2 查看状态交互
|
||||
|
||||
| 交互 | 实现 |
|
||||
|:---|:---|
|
||||
| 一键复制 | `CopyableField` 组件,点击图标复制到剪贴板 |
|
||||
| 密码查看 | 点击"小眼睛"切换明文/密文 |
|
||||
| 链接跳转 | URL 字段点击新窗口打开 |
|
||||
|
||||
### 3.3 编辑状态数据流
|
||||
|
||||
```typescript
|
||||
// 进入编辑模式
|
||||
const enterEditMode = () => {
|
||||
editForm.value = JSON.parse(JSON.stringify(masterData.value)) // 深拷贝
|
||||
isEditMode.value = true
|
||||
}
|
||||
|
||||
// 脏数据检测
|
||||
const hasChanges = computed(() => {
|
||||
return JSON.stringify(editForm.value) !== JSON.stringify(masterData.value)
|
||||
})
|
||||
|
||||
// 退出保护
|
||||
const exitEditMode = () => {
|
||||
if (hasChanges.value) {
|
||||
exitConfirmDialog.value = true // 弹出确认对话框
|
||||
} else {
|
||||
isEditMode.value = false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 用户侧 vs 管理侧差异
|
||||
|
||||
| 特性 | 管理员端 (ProjectDetail.vue) | 用户端 (UserProjectDetail.vue) |
|
||||
|:---|:---|:---|
|
||||
| **默认模式** | 查看模式 | 根据工单状态决定 |
|
||||
| **授权信息 Tab** | ✅ 可见 | ❌ 不可见 |
|
||||
| **版本历史 Tab** | ✅ 可见 | ❌ 不可见 |
|
||||
| **主机管理 Tab** | ✅ 可见 | ❌ 不可见 |
|
||||
| **基本信息** | 可编辑 | 只读(由管理员填写) |
|
||||
| **编辑操作** | 直接保存(上帝模式) | 草稿 → 提交审核(工单流程) |
|
||||
| **审批按钮** | ✅ 通过/打回 | ❌ 无 |
|
||||
| **保存按钮** | 「保存修改」 | 「保存草稿」 |
|
||||
|
||||
### Tab 导航配置
|
||||
|
||||
```html
|
||||
<!-- 管理员端 -->
|
||||
<v-tabs v-model="activeTab">
|
||||
<v-tab value="basic">基本信息</v-tab>
|
||||
<v-tab value="business">部署业务</v-tab>
|
||||
<v-tab value="environment">部署环境</v-tab>
|
||||
<v-tab value="hosts">主机管理</v-tab>
|
||||
<v-tab value="middlewares">中间件</v-tab>
|
||||
<v-tab value="authorization" v-if="isSuperAdmin">授权信息</v-tab>
|
||||
<v-tab value="version-history" v-if="isSuperAdmin">版本历史</v-tab>
|
||||
</v-tabs>
|
||||
|
||||
<!-- 用户端 -->
|
||||
<v-tabs v-model="activeTab">
|
||||
<v-tab value="basic">基本信息</v-tab>
|
||||
<v-tab value="business">部署业务</v-tab>
|
||||
<v-tab value="environment">部署环境</v-tab>
|
||||
<v-tab value="middlewares">中间件</v-tab>
|
||||
</v-tabs>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 生命周期状态展示
|
||||
|
||||
### 5.1 状态标签配置
|
||||
|
||||
```typescript
|
||||
// 生命周期状态枚举
|
||||
export const LIFECYCLE_STATUS = {
|
||||
init: '初始化',
|
||||
drafting: '填写中',
|
||||
reviewing: '审核中',
|
||||
released: '已发布',
|
||||
modifying: '变更中',
|
||||
archived: '已归档'
|
||||
}
|
||||
|
||||
// 状态颜色映射
|
||||
export const LIFECYCLE_STATUS_COLORS: Record<string, string> = {
|
||||
init: 'grey',
|
||||
drafting: 'info',
|
||||
reviewing: 'warning',
|
||||
released: 'success',
|
||||
modifying: 'primary',
|
||||
archived: 'grey-darken-1'
|
||||
}
|
||||
|
||||
// 状态图标
|
||||
const LIFECYCLE_STATUS_ICONS: Record<string, string> = {
|
||||
init: 'mdi-clock-outline',
|
||||
drafting: 'mdi-pencil',
|
||||
reviewing: 'mdi-eye',
|
||||
released: 'mdi-check-circle',
|
||||
modifying: 'mdi-sync',
|
||||
archived: 'mdi-archive'
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 生命周期提示横幅
|
||||
|
||||
根据当前状态显示上下文提示:
|
||||
|
||||
```typescript
|
||||
const lifecycleStatusAlert = computed(() => {
|
||||
const status = masterData.value?.lifecycle_status
|
||||
switch (status) {
|
||||
case 'init':
|
||||
return { type: 'info', message: '项目已创建,等待指定填写人录入详细信息' }
|
||||
case 'drafting':
|
||||
return { type: 'info', message: `项目详情正在由 ${masterData.value.detail_filler_name} 填写中` }
|
||||
case 'reviewing':
|
||||
return { type: 'warning', message: '项目详情已提交,等待审核' }
|
||||
case 'modifying':
|
||||
return { type: 'info', message: '项目存在活跃的变更工单,主线数据不受影响' }
|
||||
case 'archived':
|
||||
return { type: 'warning', message: '项目已归档,仅保留历史数据' }
|
||||
default:
|
||||
return null
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 工单关联与跳转
|
||||
|
||||
### 6.1 工单按钮显示逻辑
|
||||
|
||||
```typescript
|
||||
const showWorkflowButton = computed(() => {
|
||||
if (!masterData.value?.workflow_id) return false
|
||||
const status = masterData.value.lifecycle_status
|
||||
return ['drafting', 'reviewing', 'modifying'].includes(status)
|
||||
})
|
||||
|
||||
const workflowButtonText = computed(() => {
|
||||
const status = masterData.value?.lifecycle_status
|
||||
switch (status) {
|
||||
case 'drafting': return '查看填写工单'
|
||||
case 'reviewing': return '查看审核工单'
|
||||
case 'modifying': return '查看修改工单'
|
||||
default: return '查看工单'
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### 6.2 多工单场景(MODIFYING 状态)
|
||||
|
||||
当存在多个修改工单时,使用下拉菜单或对话框展示工单列表:
|
||||
|
||||
```html
|
||||
<v-menu v-if="multipleWorkflows">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn v-bind="props" color="info" variant="tonal">
|
||||
查看工单 ({{ workflowCount }})
|
||||
<v-icon end>mdi-chevron-down</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-list>
|
||||
<v-list-item
|
||||
v-for="wf in relatedWorkflows"
|
||||
:key="wf.workflow_id"
|
||||
@click="navigateToWorkflow(wf.workflow_id)"
|
||||
>
|
||||
<v-list-item-title>{{ wf.workflow_id }}</v-list-item-title>
|
||||
<v-list-item-subtitle>{{ wf.creator_name }} | {{ formatDate(wf.created_at) }}</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 模块字段规范
|
||||
|
||||
### 7.1 基本信息模块
|
||||
|
||||
| 字段 | 只读模式 | 编辑模式 |
|
||||
|:---|:---|:---|
|
||||
| 项目名称 | 文本 + 复制 | `v-text-field` |
|
||||
| 命名空间 | 文本 + 复制 | `disabled` 不可编辑 |
|
||||
| 省份/城市 | 文本 | 级联选择器 |
|
||||
| 项目性质 | 文本 | `v-select` |
|
||||
|
||||
### 7.2 部署业务模块
|
||||
|
||||
| 字段 | 只读模式 | 编辑模式 |
|
||||
|:---|:---|:---|
|
||||
| 部署人姓名 | 文本 | `v-text-field` 或用户搜索 |
|
||||
| 业务入口 URL | 可点击链接 | `v-text-field` |
|
||||
| 超管密码 | 脱敏 `******` + 查看按钮 | `v-text-field` 密码输入 |
|
||||
|
||||
### 7.3 中间件模块
|
||||
|
||||
采用 **卡片网格** 设计:
|
||||
- 每个中间件一张卡片,响应式布局
|
||||
- 卡片包含:类型图标 + 标题 + IP/Port
|
||||
- 编辑模式:右上角显示「编辑」「删除」按钮
|
||||
- 列表末尾显示「添加中间件」虚线框卡片
|
||||
|
||||
```typescript
|
||||
const MIDDLEWARE_ICONS: Record<string, string> = {
|
||||
'mysql': 'mdi-database',
|
||||
'redis': 'mdi-database-clock',
|
||||
'emqx': 'mdi-broadcast',
|
||||
'minio': 'mdi-bucket',
|
||||
'influxdb': 'mdi-chart-timeline-variant',
|
||||
'nacos': 'mdi-cog-outline',
|
||||
'k8s-dashboard': 'mdi-kubernetes'
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 核心组件设计
|
||||
|
||||
### 8.1 CopyableField - 可复制字段
|
||||
|
||||
```html
|
||||
<template>
|
||||
<div class="copyable-field d-flex align-center gap-2">
|
||||
<span class="field-value">{{ displayValue }}</span>
|
||||
<v-btn icon="mdi-content-copy" size="x-small" variant="text" @click="copyToClipboard">
|
||||
<v-tooltip activator="parent" location="top">复制</v-tooltip>
|
||||
</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
### 8.2 SaveConfirmDialog - 保存确认
|
||||
|
||||
展示变更 Diff 表格:
|
||||
|
||||
```html
|
||||
<v-table density="compact">
|
||||
<thead>
|
||||
<tr><th>字段</th><th>修改前</th><th>修改后</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="item in diffItems" :key="item.field">
|
||||
<td>{{ item.label }}</td>
|
||||
<td class="text-error">{{ item.oldValue || '空' }}</td>
|
||||
<td class="text-success">{{ item.newValue || '空' }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</v-table>
|
||||
```
|
||||
|
||||
### 8.3 DiffTextField - 差异高亮输入框
|
||||
|
||||
编辑模式下显示与主线数据的差异:
|
||||
|
||||
```html
|
||||
<v-text-field
|
||||
v-model="inputValue"
|
||||
:label="label"
|
||||
:class="{ 'diff-highlight': hasDiff }"
|
||||
:hint="hasDiff ? `主线值: ${masterValue}` : ''"
|
||||
persistent-hint
|
||||
>
|
||||
<template v-slot:prepend-inner v-if="hasDiff">
|
||||
<v-icon color="warning" size="small">mdi-alert-circle</v-icon>
|
||||
</template>
|
||||
</v-text-field>
|
||||
|
||||
<style scoped>
|
||||
.diff-highlight :deep(.v-field__outline) {
|
||||
--v-field-border-color: rgb(var(--v-theme-warning));
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 视觉设计规范
|
||||
|
||||
### 9.1 色彩系统
|
||||
|
||||
| 用途 | Vuetify 类 |
|
||||
|:---|:---|
|
||||
| 主色调 | `color="primary"` (Deep Purple) |
|
||||
| 成功状态 | `color="success"` (Green) |
|
||||
| 警告状态 | `color="warning"` (Orange) |
|
||||
| 错误状态 | `color="error"` (Red) |
|
||||
| 页面背景 | `bg-grey-lighten-4` |
|
||||
|
||||
### 9.2 卡片设计
|
||||
|
||||
```html
|
||||
<v-card elevation="2" rounded="lg" class="pa-4">
|
||||
<!-- 圆角 8px, 阴影 level-2, 内边距 16px -->
|
||||
</v-card>
|
||||
```
|
||||
|
||||
### 9.3 排版规范
|
||||
|
||||
| 元素 | 样式类 |
|
||||
|:---|:---|
|
||||
| 页面标题 | `text-h4 font-weight-bold` |
|
||||
| 卡片标题 | `text-h6` |
|
||||
| 字段标签 | `text-medium-emphasis text-body-2` |
|
||||
| 字段值 | `text-high-emphasis` |
|
||||
|
||||
### 9.4 间距规范(8px 网格)
|
||||
|
||||
| 间距 | 类 | 值 |
|
||||
|:---|:---|:---|
|
||||
| 紧凑 | `pa-2` | 8px |
|
||||
| 标准 | `pa-4` | 16px |
|
||||
| 宽松 | `pa-6` | 24px |
|
||||
|
||||
---
|
||||
|
||||
## 10. 响应式设计
|
||||
|
||||
### 10.1 断点
|
||||
|
||||
| 断点 | 宽度 |
|
||||
|:---|:---|
|
||||
| xs | < 600px |
|
||||
| sm | 600px - 960px |
|
||||
| md | 960px - 1280px |
|
||||
| lg | 1280px - 1920px |
|
||||
|
||||
### 10.2 中间件卡片响应式
|
||||
|
||||
```html
|
||||
<v-row>
|
||||
<v-col v-for="mw in middlewares" :key="mw.type"
|
||||
cols="12" sm="6" md="4" lg="3"
|
||||
>
|
||||
<MiddlewareCard :data="mw" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. TypeScript 类型定义
|
||||
|
||||
```typescript
|
||||
// 项目详情
|
||||
interface ProjectDetail {
|
||||
id: number
|
||||
project_id: string
|
||||
project_name: string
|
||||
namespace: string
|
||||
province: string
|
||||
city: string
|
||||
project_nature: string
|
||||
lifecycle_status: string
|
||||
project_certification: string
|
||||
workflow_id: string
|
||||
detail_filler_id: number
|
||||
detail_filler_name: string
|
||||
deployment_business: DeploymentBusiness | null
|
||||
deployment_environment: DeploymentEnvironment | null
|
||||
middlewares: Middleware[]
|
||||
hosts: Host[]
|
||||
draft_data: Record<string, unknown> | null
|
||||
}
|
||||
|
||||
// Diff 项
|
||||
interface DiffItem {
|
||||
field: string
|
||||
label: string
|
||||
oldValue: string | number | boolean
|
||||
newValue: string | number | boolean
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. 组件清单
|
||||
|
||||
| 组件 | 说明 | 复用范围 |
|
||||
|:---|:---|:---|
|
||||
| `BasicInfoForm.vue` | 基本信息编辑表单 | 管理员/用户 |
|
||||
| `BasicInfoReadonly.vue` | 基本信息只读 | 管理员/用户 |
|
||||
| `DeploymentBusinessForm.vue` | 业务信息表单 | 管理员/用户 |
|
||||
| `DeploymentEnvironmentForm.vue` | 环境信息表单 | 管理员/用户 |
|
||||
| `MiddlewareCardsGrid.vue` | 中间件卡片网格 | 管理员/用户 |
|
||||
| `AuthorizationManagement.vue` | 授权管理 | 仅管理员 |
|
||||
| `VersionHistory.vue` | 版本历史 | 仅管理员 |
|
||||
| `HostsManagement.vue` | 主机管理 | 仅管理员 |
|
||||
| `SaveConfirmDialog.vue` | 保存确认对话框 | 管理员 |
|
||||
| `CopyableField.vue` | 可复制字段 | 通用 |
|
||||
| `DiffTextField.vue` | 差异高亮输入框 | 通用 |
|
||||
@@ -0,0 +1,448 @@
|
||||
# 版本控制设计 (Git-like)
|
||||
|
||||
## 设计原则
|
||||
|
||||
采用**统一版本表**设计,将正式版本和草稿版本存储在同一张表中,通过 `version_type` 字段区分。项目信息采用类似 Git 的分支管理模式:
|
||||
- **Master 分支**: 由 SuperAdmin 审核维护的正式版本
|
||||
- **用户草稿**: 每个用户都有自己的临时分支,提交审核后合并入 Master
|
||||
|
||||
## 版本类型
|
||||
|
||||
| 版本类型 | 代码 | version 值 | 说明 |
|
||||
|:---|:---|:---|:---|
|
||||
| 正式版本 | `official` | 1, 2, 3... (递增) | 审核通过后的正式版本,构成版本历史 |
|
||||
| 填写草稿 | `fill_draft` | 0 | 项目创建时填写人的草稿 |
|
||||
| 修改草稿 | `modify_draft` | 0 | 发起变更工单时的草稿 |
|
||||
|
||||
## 版本与工单关系
|
||||
|
||||
| 关系 | 说明 |
|
||||
|:---|:---|
|
||||
| 填写草稿 : 填写工单 | 1:1 关联 |
|
||||
| 修改草稿 : 修改工单 | 1:1 关联 |
|
||||
| 正式版本 : 工单 | 审核通过后由草稿转化而来 |
|
||||
| 项目 : 修改草稿 | 1:N(一个项目可有多个修改草稿) |
|
||||
|
||||
## 版本快照机制
|
||||
|
||||
每次审核通过后,系统自动生成一个**完整快照**存储到 `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` 表数据而不生成版本历史,会导致:
|
||||
1. 版本链断裂
|
||||
2. 后续基于旧版本的工单 Diff 结果失效或产生误导
|
||||
3. 审计日志不完整
|
||||
|
||||
### 解决方案
|
||||
|
||||
超级管理员的 "Direct Edit" 操作必须被视为一次**自动审批通过的事务**:
|
||||
|
||||
1. **原子操作**:更新 `projects` 表 + 插入 `project_versions` 表必须在同一数据库事务中完成
|
||||
2. **版本归属**:
|
||||
- `workflow_id` 为空或特定系统标识(如 `DIRECT_EDIT`)
|
||||
- `committer_id` 记录为 SuperAdmin ID
|
||||
- `commit_message` 强制填写或自动生成(如 "SuperAdmin Direct Update")
|
||||
3. **结果**:确保 `projects.current_version` 永远指向最新的 `project_versions.version`
|
||||
|
||||
### 实现代码
|
||||
|
||||
```go
|
||||
// SuperAdmin 直接修改项目(必须同时生成版本)
|
||||
func (s *ProjectService) DirectUpdate(ctx context.Context, req *DirectUpdateRequest) error {
|
||||
return s.db.Transaction(func(tx *gorm.DB) error {
|
||||
// 1. 获取当前项目
|
||||
var project entity.Project
|
||||
if err := tx.Where("project_id = ?", req.ProjectID).First(&project).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 2. 更新项目主表
|
||||
newVersion := project.CurrentVersion + 1
|
||||
if err := tx.Model(&project).Updates(map[string]interface{}{
|
||||
"basic_info": req.BasicInfo,
|
||||
"deploy_business": req.DeployBusiness,
|
||||
"deploy_env": req.DeployEnv,
|
||||
"deploy_middleware": req.DeployMiddleware,
|
||||
"current_version": newVersion,
|
||||
}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 3. 同时生成版本记录(关键!)
|
||||
version := &entity.ProjectVersion{
|
||||
ProjectID: req.ProjectID,
|
||||
Version: newVersion,
|
||||
VersionType: "official",
|
||||
BaseVersion: project.CurrentVersion,
|
||||
SnapshotData: buildSnapshot(req),
|
||||
CommitMessage: req.CommitMessage, // 或自动生成
|
||||
CommitterID: req.OperatorID,
|
||||
CommitterName: req.OperatorName,
|
||||
}
|
||||
if err := tx.Create(version).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 4. 记录审计日志
|
||||
return s.auditSvc.Log(ctx, tx, AuditLog{
|
||||
Resource: "project",
|
||||
Action: "direct_update",
|
||||
ResourceID: req.ProjectID,
|
||||
Details: map[string]interface{}{"new_version": newVersion},
|
||||
})
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## 并发修改与冲突检测 (Optimistic Locking)
|
||||
|
||||
由于超级管理员可能在其他用户编辑草稿期间直接修改项目,需要引入乐观锁机制处理冲突。
|
||||
|
||||
### 冲突场景
|
||||
|
||||
```
|
||||
时间线:
|
||||
T1: 用户 A 基于 v3 版本创建草稿 (Draft.base_version = 3)
|
||||
T2: 超级管理员直接修改项目,版本升级为 v4 (Project.current_version = 4)
|
||||
T3: 用户 A 提交草稿审核 → 检测到冲突!
|
||||
```
|
||||
|
||||
### 处理策略
|
||||
|
||||
1. **提交时校验**:工单提交/审核接口需校验 `draft.base_version == project.current_version`
|
||||
|
||||
2. **冲突提示**:如果版本不一致,后端返回 `409 Conflict` 错误
|
||||
|
||||
3. **前端交互**:
|
||||
- 提示用户:"项目已被修改,当前草稿已过期"
|
||||
- 提供 **"Rebase" (变基)** 选项:将当前草稿的修改重新应用到最新版本
|
||||
- 或提供 **"Diff Check"**:让用户查看当前草稿与最新版本的差异
|
||||
|
||||
### 冲突检测代码
|
||||
|
||||
```go
|
||||
// 提交草稿时检测版本冲突
|
||||
func (s *DraftService) SubmitDraft(ctx context.Context, req *SubmitDraftRequest) error {
|
||||
// 1. 获取草稿
|
||||
var draft entity.ProjectVersion
|
||||
if err := s.db.Where("project_id = ? AND user_id = ? AND version_type IN (?, ?)",
|
||||
req.ProjectID, req.UserID, "fill_draft", "modify_draft").First(&draft).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 2. 获取项目当前版本
|
||||
var project entity.Project
|
||||
if err := s.db.Where("project_id = ?", req.ProjectID).First(&project).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 3. 乐观锁检查
|
||||
if draft.BaseVersion != project.CurrentVersion {
|
||||
return &VersionConflictError{
|
||||
DraftBaseVersion: draft.BaseVersion,
|
||||
CurrentVersion: project.CurrentVersion,
|
||||
Message: "项目已被修改,当前草稿已过期,请重新基于最新版本编辑",
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 继续提交流程...
|
||||
return s.workflowTransitioner.TransitionWorkflow(draft.WorkflowID, "complete", ...)
|
||||
}
|
||||
```
|
||||
|
||||
### 错误响应格式
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 40901,
|
||||
"message": "版本冲突:项目已被修改",
|
||||
"data": {
|
||||
"draft_base_version": 3,
|
||||
"current_version": 4,
|
||||
"suggestion": "请点击\"重新加载\"获取最新版本后重新编辑"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 版本 Diff 算法
|
||||
|
||||
采用 **JSON Diff** 算法,对比两个版本快照的差异,按模块分组展示。
|
||||
|
||||
### 差异结构
|
||||
|
||||
```go
|
||||
// DiffResult 差异结果(按模块分组)
|
||||
type DiffResult struct {
|
||||
Module string `json:"module"` // 模块名称(中文)
|
||||
ModuleCode string `json:"module_code"` // 模块代码
|
||||
FieldDiffs []FieldDiff `json:"field_diffs"` // 字段差异列表
|
||||
}
|
||||
|
||||
// FieldDiff 字段差异
|
||||
type FieldDiff struct {
|
||||
FieldPath string `json:"field_path"` // 字段路径 如 "deploy_env.host_count"
|
||||
FieldName string `json:"field_name"` // 字段中文名
|
||||
OldValue interface{} `json:"old_value"` // 旧值
|
||||
NewValue interface{} `json:"new_value"` // 新值
|
||||
ChangeType string `json:"change_type"` // add/modify/delete
|
||||
}
|
||||
```
|
||||
|
||||
### 字段名映射表
|
||||
|
||||
```go
|
||||
// fieldNameMap 字段路径到中文名的映射
|
||||
var fieldNameMap = map[string]string{
|
||||
// 基本信息
|
||||
"basic_info.province": "省份",
|
||||
"basic_info.city": "城市",
|
||||
"basic_info.industry_contact": "行业组人员",
|
||||
"basic_info.industry_phone": "行业组电话",
|
||||
"basic_info.project_nature": "项目性质",
|
||||
|
||||
// 部署业务
|
||||
"deploy_business.deployer_name": "部署人姓名",
|
||||
"deploy_business.deployer_phone": "部署人电话",
|
||||
"deploy_business.deploy_start_time": "部署开始时间",
|
||||
"deploy_business.deploy_end_time": "部署结束时间",
|
||||
"deploy_business.system_version": "系统版本",
|
||||
"deploy_business.system_type": "系统类型",
|
||||
"deploy_business.main_entrance": "业务主入口",
|
||||
"deploy_business.admin_username": "超管用户名",
|
||||
|
||||
// 部署环境
|
||||
"deploy_env.network_type": "网络环境",
|
||||
"deploy_env.main_public_ip": "主要公网IP",
|
||||
"deploy_env.domain_url": "域名URL",
|
||||
"deploy_env.ssl_enabled": "是否开启SSL",
|
||||
"deploy_env.host_count": "主机台数",
|
||||
"deploy_env.total_cpu": "CPU总核数",
|
||||
"deploy_env.total_memory": "内存总大小(GB)",
|
||||
"deploy_env.total_storage": "存储总大小(GB)",
|
||||
|
||||
// 部署中间件
|
||||
"deploy_middleware.mysql.internal_port": "MySQL内网端口",
|
||||
"deploy_middleware.redis.internal_port": "Redis内网端口",
|
||||
// ... 其他字段
|
||||
}
|
||||
```
|
||||
|
||||
### 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
|
||||
Code string
|
||||
Base interface{}
|
||||
Target interface{}
|
||||
}{
|
||||
{"基本信息", "basic_info", baseVersion.BasicInfo, targetVersion.BasicInfo},
|
||||
{"部署业务", "deploy_business", baseVersion.DeployBusiness, targetVersion.DeployBusiness},
|
||||
{"部署环境", "deploy_env", baseVersion.DeployEnv, targetVersion.DeployEnv},
|
||||
{"部署中间件", "deploy_middleware", baseVersion.DeployMiddleware, targetVersion.DeployMiddleware},
|
||||
}
|
||||
|
||||
for _, m := range modules {
|
||||
diffs := s.diffJSON(m.Code, m.Base, m.Target)
|
||||
if len(diffs) > 0 {
|
||||
results = append(results, DiffResult{
|
||||
Module: m.Name,
|
||||
ModuleCode: m.Code,
|
||||
FieldDiffs: diffs,
|
||||
})
|
||||
}
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// diffJSON 对比两个 JSON 对象的差异
|
||||
func (s *VersionService) diffJSON(moduleCode string, base, target interface{}) []FieldDiff {
|
||||
var diffs []FieldDiff
|
||||
|
||||
baseMap := structToMap(base)
|
||||
targetMap := structToMap(target)
|
||||
|
||||
// 检查修改和删除
|
||||
for key, oldVal := range baseMap {
|
||||
fieldPath := moduleCode + "." + key
|
||||
if newVal, exists := targetMap[key]; exists {
|
||||
if !reflect.DeepEqual(oldVal, newVal) {
|
||||
diffs = append(diffs, FieldDiff{
|
||||
FieldPath: fieldPath,
|
||||
FieldName: getFieldName(fieldPath),
|
||||
OldValue: oldVal,
|
||||
NewValue: newVal,
|
||||
ChangeType: "modify",
|
||||
})
|
||||
}
|
||||
} else {
|
||||
diffs = append(diffs, FieldDiff{
|
||||
FieldPath: fieldPath,
|
||||
FieldName: getFieldName(fieldPath),
|
||||
OldValue: oldVal,
|
||||
NewValue: nil,
|
||||
ChangeType: "delete",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 检查新增
|
||||
for key, newVal := range targetMap {
|
||||
if _, exists := baseMap[key]; !exists {
|
||||
fieldPath := moduleCode + "." + key
|
||||
diffs = append(diffs, FieldDiff{
|
||||
FieldPath: fieldPath,
|
||||
FieldName: getFieldName(fieldPath),
|
||||
OldValue: nil,
|
||||
NewValue: newVal,
|
||||
ChangeType: "add",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return diffs
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 版本历史查询
|
||||
|
||||
### 版本列表结构
|
||||
|
||||
```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"` // 变更摘要(如:修改了 3 个字段)
|
||||
IsCurrent bool `json:"is_current"` // 是否为当前版本
|
||||
}
|
||||
```
|
||||
|
||||
### 版本历史 API
|
||||
|
||||
| 方法 | 路径 | 描述 |
|
||||
|:---|:---|:---|
|
||||
| POST | `/api/project/version/list` | 获取版本历史列表 |
|
||||
| POST | `/api/project/version/detail` | 获取指定版本详情(完整快照) |
|
||||
| POST | `/api/project/version/diff` | 对比两个版本差异 |
|
||||
| POST | `/api/project/version/diff-with-current` | 对比指定版本与当前版本差异 |
|
||||
|
||||
---
|
||||
|
||||
## 前端展示设计
|
||||
|
||||
### 版本历史页面
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 项目版本历史 - [项目名称] │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ●──v3 (当前版本) 2026-01-14 15:30 张三 │
|
||||
│ │ └─ 变更说明: 更新部署环境信息 │
|
||||
│ │ └─ 关联工单: #WF-20260114-001 [点击跳转] │
|
||||
│ │ └─ 变更摘要: 修改了 2 个字段 │
|
||||
│ │ │
|
||||
│ ●──v2 2026-01-10 10:00 李四 │
|
||||
│ │ └─ 变更说明: 修改中间件配置 │
|
||||
│ │ └─ 关联工单: #WF-20260110-002 │
|
||||
│ │ │
|
||||
│ ●──v1 (初始版本) 2026-01-05 09:00 王五 │
|
||||
│ └─ 变更说明: 项目初始填写 │
|
||||
│ └─ 关联工单: #WF-20260105-001 │
|
||||
│ │
|
||||
│ [查看详情] [对比版本] │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Diff 对比页面
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 版本对比: v2 → v3 │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ 模块: 部署环境 │
|
||||
│ ┌───────────────────────────────────────────────────────────┐ │
|
||||
│ │ 字段 │ v2 (旧值) │ v3 (新值) │ │
|
||||
│ ├───────────────────────────────────────────────────────────┤ │
|
||||
│ │ 主机台数 │ 3 │ 5 [修改] │ │
|
||||
│ │ 主要公网 IP │ 10.0.0.1 │ 192.168.1.100 [修改] │ │
|
||||
│ │ 域名 URL │ - │ www.example.com [新增] │ │
|
||||
│ └───────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ 模块: 部署中间件 │
|
||||
│ ┌───────────────────────────────────────────────────────────┐ │
|
||||
│ │ 字段 │ v2 (旧值) │ v3 (新值) │ │
|
||||
│ ├───────────────────────────────────────────────────────────┤ │
|
||||
│ │ MySQL.内网端口 │ 3306 │ 3307 [修改] │ │
|
||||
│ └───────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ 变更统计: 共 4 个字段变更 (新增: 1, 修改: 3, 删除: 0) │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 草稿编辑页面 Diff 提示
|
||||
|
||||
在用户编辑草稿时,实时显示与主线版本的差异:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 编辑项目详情 - [项目名称] [保存草稿] [提交审核] │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ ⚠️ 您的草稿基于 v3 版本,与当前版本有以下差异: │
|
||||
│ ┌───────────────────────────────────────────────────────────┐ │
|
||||
│ │ • 主机台数: 3 → 5 │ │
|
||||
│ │ • 系统版本: v2.0.0 → v2.1.0 │ │
|
||||
│ └───────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ [基本信息] [部署业务] [部署环境] [部署中间件] │
|
||||
│ ───────────────────────────────────────────────────────────── │
|
||||
│ 省份: [北京市 ▼] │
|
||||
│ 城市: [北京市 ▼] │
|
||||
│ ... │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
Reference in New Issue
Block a user