# OCDP 数据库结构说明

## 概述

OCDP (Open Container Deployment Platform) 是一个多租户容器部署平台，支持：
- 多 Workspace 隔离
- RBAC 权限控制 (Admin / User)
- Kubernetes 集群管理
- OCI Registry 集成 (Harbor)
- Helm Chart 部署
- Values 模板版本管理
- 资源配额控制
- 审计日志

## 数据库配置

```yaml
# PostgreSQL 连接信息
Host: localhost
Port: 5430 (docker) / 5432 (local)
Database: ocdp
User: ocdp
Password: ocdp_password
```

---

## 表结构

### 1. users - 用户表

存储用户账户信息，支持多租户和角色管理。

```sql
CREATE TABLE users (
    id VARCHAR(36) PRIMARY KEY,
    username VARCHAR(255) NOT NULL UNIQUE,
    password_hash TEXT NOT NULL,
    email VARCHAR(255) NOT NULL,
    role VARCHAR(20) NOT NULL DEFAULT 'user',        -- 'admin' | 'user'
    workspace_id VARCHAR(36),                         -- 所属工作空间，admin 为 NULL 表示全局
    is_active BOOLEAN NOT NULL DEFAULT TRUE,          -- 账户是否激活
    must_change_password BOOLEAN NOT NULL DEFAULT FALSE, -- 首次登录必须修改密码
    revoked_after TIMESTAMP NOT NULL DEFAULT '1970-01-01 00:00:00', -- 全局 Token 撤销时间
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
```

| 字段 | 类型 | 说明 | 示例 |
|------|------|------|------|
| id | VARCHAR(36) | 主键 UUID | 550e8400-e29b-41d4-a716-446655440000 |
| username | VARCHAR(255) | 用户名，唯一 | admin |
| password_hash | TEXT | bcrypt 密码哈希 | $2a$10$... |
| email | VARCHAR(255) | 邮箱 | admin@ocdp.local |
| role | VARCHAR(20) | 角色：admin/user | admin |
| workspace_id | VARCHAR(36) | 所属工作空间 ID | workspace-uuid |
| is_active | BOOLEAN | 账户是否激活 | true |
| must_change_password | BOOLEAN | 首次登录必须修改密码 | false |
| revoked_after | TIMESTAMP | Token 撤销时间（修改密码后自动撤销旧 Token） | 2024-01-01 10:00:00 |
| created_at | TIMESTAMP | 创建时间 | 2024-01-01 10:00:00 |
| updated_at | TIMESTAMP | 更新时间 | 2024-01-01 10:00:00 |

**索引**:
- `idx_users_username` - 用户名查询
- `idx_users_role` - 角色筛选
- `idx_users_workspace_id` - 工作空间筛选
- `idx_users_is_active` - 激活状态筛选

**角色说明**:
- `admin`: 管理员，可管理所有 Workspace 和资源，workspace_id 为 NULL
- `user`: 普通用户，仅可访问自己 Workspace 内的资源

---

### 2. workspaces - 工作空间表

租户/团队隔离单元。

```sql
CREATE TABLE workspaces (
    id VARCHAR(36) PRIMARY KEY,
    name VARCHAR(255) NOT NULL UNIQUE,
    description TEXT,
    created_by VARCHAR(36),
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
```

| 字段 | 类型 | 说明 | 示例 |
|------|------|------|------|
| id | VARCHAR(36) | 主键 UUID | workspace-uuid |
| name | VARCHAR(255) | 工作空间名称，唯一 | team-alpha |
| description | TEXT | 描述 | Alpha 团队工作空间 |
| created_by | VARCHAR(36) | 创建者用户 ID | user-uuid |
| created_at | TIMESTAMP | 创建时间 | 2024-01-01 10:00:00 |
| updated_at | TIMESTAMP | 更新时间 | 2024-01-01 10:00:00 |

**索引**:
- `idx_workspaces_name` - 名称查询

---

### 3. workspace_quotas - 工作空间配额表

每个 Workspace 的资源配额限制。

```sql
CREATE TABLE workspace_quotas (
    id VARCHAR(36) PRIMARY KEY,
    workspace_id VARCHAR(36) NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
    resource_type VARCHAR(50) NOT NULL,              -- 'cpu' | 'gpu' | 'gpu_memory'
    hard_limit DECIMAL(10,2) NOT NULL,               -- 硬限制（0 表示无限制）
    soft_limit DECIMAL(10,2) NOT NULL,               -- 软限制（警告阈值）
    used DECIMAL(10,2) NOT NULL DEFAULT 0,           -- 当前使用量
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    UNIQUE(workspace_id, resource_type)
);
```

| 字段 | 类型 | 说明 | 示例 |
|------|------|------|------|
| id | VARCHAR(36) | 主键 UUID | quota-uuid |
| workspace_id | VARCHAR(36) | 所属工作空间 ID | workspace-uuid |
| resource_type | VARCHAR(50) | 资源类型：cpu/gpu/gpu_memory | cpu |
| hard_limit | DECIMAL(10,2) | 硬限制（0=无限制） | 10.00 |
| soft_limit | DECIMAL(10,2) | 软限制（警告阈值） | 8.00 |
| used | DECIMAL(10,2) | 当前使用量 | 5.00 |
| created_at | TIMESTAMP | 创建时间 | 2024-01-01 10:00:00 |
| updated_at | TIMESTAMP | 更新时间 | 2024-01-01 10:00:00 |

**配额检查逻辑**:
1. 部署实例前检查 `used + new_request <= hard_limit`
2. 超过硬限制返回 403 Forbidden
3. 超过软限制发送警告通知
4. 实例删除后释放配额

---

### 4. clusters - Kubernetes 集群表

管理 Kubernetes 集群连接信息。

```sql
CREATE TABLE clusters (
    id VARCHAR(36) PRIMARY KEY,
    workspace_id VARCHAR(36),                        -- 所属工作空间，NULL 表示全局共享
    owner_id VARCHAR(36),                            -- 创建者用户 ID
    name VARCHAR(255) NOT NULL UNIQUE,
    host TEXT NOT NULL,                              -- Kubernetes API Server URL
    ca_data TEXT,                                    -- CA 证书（Base64 编码）
    cert_data TEXT,                                  -- 客户端证书（Base64 编码）
    key_data TEXT,                                   -- 客户端密钥（Base64 编码）
    token TEXT,                                      -- Bearer Token（与证书认证二选一）
    description TEXT,
    isolation_mode VARCHAR(20) NOT NULL DEFAULT 'namespace', -- 'namespace' | 'cluster'
    default_namespace VARCHAR(255),                  -- 默认 namespace 前缀
    is_shared BOOLEAN NOT NULL DEFAULT FALSE,        -- 是否为共享集群
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
```

| 字段 | 类型 | 说明 | 示例 |
|------|------|------|------|
| id | VARCHAR(36) | 主键 UUID | cluster-uuid |
| workspace_id | VARCHAR(36) | 所属工作空间 ID | workspace-uuid |
| owner_id | VARCHAR(36) | 创建者用户 ID | user-uuid |
| name | VARCHAR(255) | 集群名称，唯一 | prod-k8s |
| host | VARCHAR(255) | Kubernetes API URL | https://k8s.example.com:6443 |
| ca_data | TEXT | CA 证书 Base64 | LS0tLS1... |
| cert_data | TEXT | 客户端证书 Base64 | LS0tLS1... |
| key_data | TEXT | 客户端密钥 Base64 | LS0tLS1... |
| token | TEXT | Bearer Token | eyJhbGci... |
| description | TEXT | 描述 | 生产环境集群 |
| isolation_mode | VARCHAR(20) | 隔离模式：namespace/cluster | namespace |
| default_namespace | VARCHAR(255) | 默认 namespace 前缀 | team-alpha |
| is_shared | BOOLEAN | 是否共享（admin 创建供多 Workspace 使用） | false |
| created_at | TIMESTAMP | 创建时间 | 2024-01-01 10:00:00 |
| updated_at | TIMESTAMP | 更新时间 | 2024-01-01 10:00:00 |

**隔离模式说明**:
- `namespace`: 共享集群模式，多个 Workspace 使用不同 namespace
  - 部署时自动分配：`{default_namespace}-{instance_name}`
- `cluster`: 私有集群模式，每个 Workspace 独立集群或独立凭证

**认证方式**:
1. 证书认证：`ca_data` + `cert_data` + `key_data`
2. Token 认证：`token`

---

### 5. registries - OCI Registry 表

管理 Docker/OCI 镜像仓库（支持 Harbor）。

```sql
CREATE TABLE registries (
    id VARCHAR(36) PRIMARY KEY,
    workspace_id VARCHAR(36),                        -- 所属工作空间，NULL 表示全局共享
    owner_id VARCHAR(36),                            -- 创建者用户 ID
    name VARCHAR(255) NOT NULL UNIQUE,
    url TEXT NOT NULL,                               -- Registry URL
    description TEXT,
    username VARCHAR(255),                           -- 认证用户名
    password TEXT,                                   -- 认证密码（加密存储）
    insecure BOOLEAN DEFAULT FALSE,                  -- 是否跳过 TLS 验证
    is_shared BOOLEAN DEFAULT FALSE,                 -- 是否为共享 Registry
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
```

| 字段 | 类型 | 说明 | 示例 |
|------|------|------|------|
| id | VARCHAR(36) | 主键 UUID | registry-uuid |
| workspace_id | VARCHAR(36) | 所属工作空间 ID | workspace-uuid |
| owner_id | VARCHAR(36) | 创建者用户 ID | user-uuid |
| name | VARCHAR(255) | Registry 名称，唯一 | harbor-prod |
| url | TEXT | Registry URL | https://harbor.example.com |
| description | TEXT | 描述 | 生产环境 Harbor |
| username | VARCHAR(255) | 认证用户名 | admin |
| password | TEXT | 认证密码（加密） | encrypted... |
| insecure | BOOLEAN | 跳过 TLS 验证 | false |
| is_shared | BOOLEAN | 是否共享 | false |
| created_at | TIMESTAMP | 创建时间 | 2024-01-01 10:00:00 |
| updated_at | TIMESTAMP | 更新时间 | 2024-01-01 10:00:00 |

---

### 6. instances - Helm 实例表

部署的 Helm Release 管理。

```sql
CREATE TABLE instances (
    id VARCHAR(36) PRIMARY KEY,
    workspace_id VARCHAR(36),                        -- 所属工作空间
    owner_id VARCHAR(36),                            -- 创建者用户 ID
    cluster_id VARCHAR(36) NOT NULL,
    registry_id VARCHAR(36) NOT NULL,
    chart_reference_id VARCHAR(36),                  -- 引用的 Chart 引用
    values_template_id VARCHAR(36),                  -- 使用的 Values 模板

    name VARCHAR(255) NOT NULL,                      -- Helm Release 名称
    namespace VARCHAR(255) NOT NULL,                 -- Kubernetes 命名空间
    repository TEXT NOT NULL,                        -- OCI Repository (e.g., charts/app)
    chart VARCHAR(255) NOT NULL,                     -- Chart 名称
    version VARCHAR(255) NOT NULL,                   -- Chart 版本
    description TEXT,
    values JSONB,                                    -- Helm Values (JSON)
    values_yaml TEXT,                                -- Helm Values (YAML)
    user_override_yaml TEXT,                         -- 用户额外覆盖配置

    status VARCHAR(50) NOT NULL,                     -- 实例状态
    status_reason TEXT,                              -- 状态说明
    last_operation VARCHAR(50),                      -- 最后操作类型
    last_error TEXT,                                 -- 最近错误
    revision INTEGER NOT NULL DEFAULT 1,             -- Helm Release Revision

    cpu_requested DECIMAL(10,2) NOT NULL DEFAULT 0,  -- CPU 请求量 (cores)
    memory_requested VARCHAR(50) NOT NULL DEFAULT '0Mi', -- 内存请求量
    gpu_requested DECIMAL(10,2) NOT NULL DEFAULT 0,  -- GPU 请求量 (cards)
    gpu_memory_requested VARCHAR(50) NOT NULL DEFAULT '0Mi', -- GPU 内存请求量

    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,

    CONSTRAINT fk_cluster FOREIGN KEY (cluster_id) REFERENCES clusters(id) ON DELETE CASCADE,
    CONSTRAINT fk_registry FOREIGN KEY (registry_id) REFERENCES registries(id) ON DELETE CASCADE,
    UNIQUE (cluster_id, name, namespace)
);
```

| 字段 | 类型 | 说明 | 示例 |
|------|------|------|------|
| id | VARCHAR(36) | 主键 UUID | instance-uuid |
| workspace_id | VARCHAR(36) | 所属工作空间 ID | workspace-uuid |
| owner_id | VARCHAR(36) | 创建者用户 ID | user-uuid |
| cluster_id | VARCHAR(36) | 所属集群 ID | cluster-uuid |
| registry_id | VARCHAR(36) | 所属 Registry ID | registry-uuid |
| chart_reference_id | VARCHAR(36) | Chart 引用 ID | chart-ref-uuid |
| values_template_id | VARCHAR(36) | Values 模板 ID | template-uuid |
| name | VARCHAR(255) | Release 名称（RFC 1123） | my-app |
| namespace | VARCHAR(255) | Kubernetes 命名空间 | team-alpha-my-app |
| repository | TEXT | OCI Repository | harbor.example.com/charts/nginx |
| chart | VARCHAR(255) | Chart 名称 | nginx |
| version | VARCHAR(255) | Chart 版本 | 1.0.0 |
| description | TEXT | 描述 | Nginx 应用 |
| values | JSONB | Values JSON | {"replicas": 2} |
| values_yaml | TEXT | Values YAML | replicas: 2 |
| user_override_yaml | TEXT | 用户覆盖配置 | replicas: 3 |
| status | VARCHAR(50) | 状态 | deployed |
| status_reason | TEXT | 状态说明 | Install complete |
| last_operation | VARCHAR(50) | 最后操作 | install |
| last_error | TEXT | 错误信息 | - |
| revision | INTEGER | Helm Revision | 1 |
| cpu_requested | DECIMAL(10,2) | CPU 请求 | 2.00 |
| memory_requested | VARCHAR(50) | 内存请求 | 1Gi |
| gpu_requested | DECIMAL(10,2) | GPU 请求 | 0 |
| gpu_memory_requested | VARCHAR(50) | GPU 内存 | 0Mi |
| created_at | TIMESTAMP | 创建时间 | 2024-01-01 10:00:00 |
| updated_at | TIMESTAMP | 更新时间 | 2024-01-01 10:00:00 |

**状态说明**:
| 状态 | 说明 |
|------|------|
| deployed | 部署成功 |
| failed | 部署失败 |
| pending-install | 安装中 |
| pending-upgrade | 升级中 |
| pending-rollback | 回滚中 |
| pending-delete | 删除中 |
| uninstalled | 已卸载 |
| superseded | 已被取代 |
| unknown | 未知 |

---

### 7. storage_backends - 存储后端表

NFS/PV/HostPath 存储配置。

```sql
CREATE TABLE storage_backends (
    id VARCHAR(36) PRIMARY KEY,
    workspace_id VARCHAR(36),
    owner_id VARCHAR(36),
    name VARCHAR(255) NOT NULL,
    type VARCHAR(50) NOT NULL,                      -- 'nfs' | 'pv' | 'hostPath'
    config JSONB NOT NULL,                          -- 存储配置
    description TEXT,
    is_default BOOLEAN NOT NULL DEFAULT FALSE,      -- 是否默认存储
    is_shared BOOLEAN NOT NULL DEFAULT FALSE,       -- 是否共享
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    UNIQUE(workspace_id, name)
);
```

**Config 结构**:
```json
// NFS
{"nfs": {"server": "192.168.1.100", "path": "/data"}}

// PV
{"pv": {"storageClassName": "nfs", "capacity": "10Gi", "accessModes": ["ReadWriteMany"]}}

// HostPath
{"hostPath": {"path": "/mnt/data"}}
```

---

### 8. chart_references - Chart 引用表

管理可用的 Helm Chart 引用。

```sql
CREATE TABLE chart_references (
    id VARCHAR(36) PRIMARY KEY,
    workspace_id VARCHAR(36),
    registry_id VARCHAR(36),
    repository VARCHAR(500) NOT NULL,               -- OCI repository path
    chart_name VARCHAR(255) NOT NULL,
    description TEXT,
    is_enabled BOOLEAN NOT NULL DEFAULT TRUE,
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    UNIQUE(workspace_id, registry_id, repository)
);
```

---

### 9. values_templates - Values 模板表

Helm Values 模板，支持版本管理。

```sql
CREATE TABLE values_templates (
    id VARCHAR(36) PRIMARY KEY,
    workspace_id VARCHAR(36),
    owner_id VARCHAR(36),
    chart_reference_id VARCHAR(36),
    name VARCHAR(255) NOT NULL,
    description TEXT,
    values_yaml TEXT NOT NULL,
    version INTEGER NOT NULL DEFAULT 1,             -- 模板版本号
    is_default BOOLEAN NOT NULL DEFAULT FALSE,
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    UNIQUE(workspace_id, chart_reference_id, name)
);
```

**版本管理**:
- 每次更新创建新版本（version + 1）
- 支持回滚到历史版本

---

### 10. user_config_overrides - 用户配置覆盖表

用户个人配置覆盖。

```sql
CREATE TABLE user_config_overrides (
    id VARCHAR(36) PRIMARY KEY,
    workspace_id VARCHAR(36),
    user_id VARCHAR(36),
    target_type VARCHAR(50) NOT NULL,               -- 'storage' | 'template' | 'global'
    target_id VARCHAR(36),
    config JSONB NOT NULL,                          -- 覆盖配置
    priority INTEGER NOT NULL DEFAULT 0,            -- 优先级
    is_active BOOLEAN NOT NULL DEFAULT TRUE,
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
```

---

### 11. audit_logs - 审计日志表

记录所有操作行为。

```sql
CREATE TABLE audit_logs (
    id VARCHAR(36) PRIMARY KEY,
    workspace_id VARCHAR(36),
    user_id VARCHAR(36),
    action VARCHAR(100) NOT NULL,                   -- 'create' | 'update' | 'delete' | 'deploy' | 'scale'
    resource_type VARCHAR(50) NOT NULL,             -- 'cluster' | 'registry' | 'instance' | ...
    resource_id VARCHAR(36),
    resource_name VARCHAR(255),
    details JSONB,
    ip_address VARCHAR(50),
    user_agent TEXT,
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
```

---

### 12. schema_migrations - 迁移版本表

数据库版本记录。

```sql
CREATE TABLE schema_migrations (
    version VARCHAR(50) PRIMARY KEY,
    applied_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
```

---

## ER 关系图

```
┌─────────────────────────────────────────────────────────────────────────┐
│                              workspaces                                  │
│  (id, name, description, created_by, created_at, updated_at)          │
└────────────────────────────────────┬────────────────────────────────────┘
                                     │ 1:N
        ┌────────────────────────────┼────────────────────────────┐
        │                            │                            │
        ▼                            ▼                            ▼
┌───────────────────┐    ┌───────────────────┐    ┌───────────────────┐
│   workspace_quotas│    │     clusters      │    │    registries    │
│ (workspace_id,    │    │ (workspace_id,    │    │ (workspace_id,   │
│  resource_type,   │    │  owner_id, name,  │    │  owner_id, name, │
│  hard_limit,      │    │  host, is_shared) │    │  url, is_shared) │
│  soft_limit, used)│    └─────────┬─────────┘    └────────┬─────────┘
└───────────────────┘              │                       │
                                   │                       │
       ┌───────────────────────────┼───────────────────────┘
       │                           │
       ▼                           ▼
┌───────────────────┐    ┌───────────────────┐
│     instances     │    │   storage_backends│
│ (workspace_id,    │    │ (workspace_id,    │
│  owner_id,        │    │  owner_id, name,  │
│  cluster_id,      │    │  type, config)    │
│  registry_id,     │    └───────────────────┘
│  values_template) │
└───────────────────┘

┌─────────────────────────────────────────────────────────────────────────┐
│                                users                                     │
│  (id, username, password_hash, email, role, workspace_id, is_active)  │
└────────────────────────────────────┬────────────────────────────────────┘
                                     │
        ┌────────────────────────────┼────────────────────────────┐
        │                            │                            │
        ▼                            ▼                            ▼
┌───────────────────┐    ┌───────────────────┐    ┌───────────────────┐
│   chart_references│    │ values_templates  │    │   audit_logs      │
│ (workspace_id,    │    │ (workspace_id,    │    │ (user_id, action,│
│  registry_id,     │    │  owner_id,        │    │  resource_type)  │
│  repository)      │    │  chart_ref_id)    │    └───────────────────┘
└───────────────────┘    └───────────────────┘
```

---

## 资源可见性规则

| 用户角色 | 可见范围 |
|---------|---------|
| Admin | 所有 Workspace 的所有资源（workspace_id 为 NULL 或有值都能看到） |
| User | 仅自己 Workspace 的资源 |
| 共享资源 | `is_shared=TRUE` 时，同 Workspace 内可见 |

---

## 常用 SQL 操作

### 查询用户及其 Workspace
```sql
SELECT u.id, u.username, u.role, w.name as workspace_name
FROM users u
LEFT JOIN workspaces w ON u.workspace_id = w.id
WHERE u.is_active = TRUE;
```

### 查询 Workspace 配额使用情况
```sql
SELECT w.name as workspace,
       q.resource_type,
       q.hard_limit,
       q.soft_limit,
       q.used,
       CASE WHEN q.hard_limit > 0 THEN ROUND(q.used / q.hard_limit * 100, 2) ELSE 0 END as usage_percent
FROM workspace_quotas q
JOIN workspaces w ON q.workspace_id = w.id;
```

### 查询用户可用的集群
```sql
-- Admin: 所有集群
SELECT * FROM clusters;

-- User: 自己 Workspace 的集群 + 共享集群
SELECT * FROM clusters
WHERE workspace_id = 'user-workspace-id'
   OR is_shared = TRUE;
```

### 查询实例状态统计
```sql
SELECT status, COUNT(*) as count
FROM instances
WHERE workspace_id = 'workspace-id'
GROUP BY status;
```

### 查询审计日志
```sql
SELECT a.created_at, u.username, a.action, a.resource_type, a.resource_name
FROM audit_logs a
JOIN users u ON a.user_id = u.id
WHERE a.workspace_id = 'workspace-id'
ORDER BY a.created_at DESC
LIMIT 50;
```

---

## 迁移历史

| 版本 | 说明 | 日期 |
|------|------|------|
| v1.0.0 | 初始版本（单租户） | 2024-01 |
| v2.0.0-multi-tenant | 多租户迁移：添加 workspaces, quotas, 扩展 users/clusters/registries/instances | 2025-04 |

---

## 初始数据

### 创建 Admin 用户
```sql
-- 默认密码: admin123 (bcrypt hash 需由应用设置)
INSERT INTO users (id, username, password_hash, email, role, workspace_id, is_active, must_change_password)
VALUES (
    '00000000-0000-0000-0000-000000000001',
    'admin',
    '$2a$10$placeholder',  -- 由应用初始化时设置
    'admin@ocdp.local',
    'admin',
    NULL,   -- admin 的 workspace_id 为 NULL，表示全局
    TRUE,
    TRUE    -- 首次登录必须修改密码
);
```