# 24 - OpenClaw 能力拓展平台 — 数据库设计

> **表前缀**：`occap_`  
> **更新日期**：2026-04-19  

## 1. 约定

- 主键：`id` bigint 自增或雪花（与 Midway 项目统一）。
- 软删除：`delete_time` datetime NULL。
- 时间：`create_time` / `update_time`。

## 2. 核心表

### 2.0 occap_plan（套餐，可选独立表）

| 字段 | 类型 | 说明 |
|------|------|------|
| id | PK | |
| name | string | 套餐名 |
| code | string | 唯一编码 |
| quota_template_json | text | 默认配额模板 |

### 2.1 occap_tenant（租户）

| 字段 | 类型 | 说明 |
|------|------|------|
| id | PK | |
| name | string | 组织名 |
| code | string | 唯一编码 |
| plan_id | FK | 当前套餐 |
| status | int | 正常/冻结 |

### 2.2 occap_user（用户，平台账号）

| 字段 | 类型 | 说明 |
|------|------|------|
| id | PK | |
| tenant_id | FK | 可空（平台超管） |
| username | string | |
| role | string | 角色枚举 |

### 2.3 occap_asset（能力资产）

| 字段 | 类型 | 说明 |
|------|------|------|
| id | PK | |
| type | string | skill / mcp_bundle / memory_policy / composite |
| slug | string | 唯一标识 |
| title | string | |
| status | string | draft / in_review / published / deprecated |
| risk_level | string | low / medium / high |
| license_spdx | string | 可选 |

### 2.4 occap_asset_version（资产版本）

| 字段 | 类型 | 说明 |
|------|------|------|
| id | PK | |
| asset_id | FK | |
| version | string | semver |
| manifest_json | text | MCP/SKILL 元数据快照 |
| bundle_uri | string | 对象存储路径或内部路径 |
| bundle_sha256 | string | 校验 |

### 2.5 occap_entitlement（授权）

| 字段 | 类型 | 说明 |
|------|------|------|
| id | PK | |
| tenant_id | FK | |
| asset_version_id | FK | |
| quota_json | text | 配额 JSON |

### 2.6 occap_delivery_bundle（交付包实例）

| 字段 | 类型 | 说明 |
|------|------|------|
| id | PK | |
| tenant_id | FK | |
| asset_version_ids | text | JSON 数组 |
| uri | string | 包地址 |
| signature | string | 可选 |
| created_by | FK | |

### 2.7 occap_audit_event（审计事件）

| 字段 | 类型 | 说明 |
|------|------|------|
| id | PK | |
| tenant_id | FK | 可空 |
| actor_id | FK | |
| action | string | publish / bundle_create / sync_ack / deprecate |
| payload_json | text | 脱敏后 |

### 2.8 occap_memory_item（记忆条目，若平台托管内容）

| 字段 | 类型 | 说明 |
|------|------|------|
| id | PK | |
| tenant_id | FK | |
| layer | string | session / long_term / org |
| content_encrypted | text | 视安全方案 |
| embedding_id | FK | 可空，指向向量侧 |

### 2.9 occap_search_audit（搜索审计）

| 字段 | 类型 | 说明 |
|------|------|------|
| id | PK | |
| tenant_id | FK | |
| query_type | string | keyword / vector / hybrid |
| latency_ms | int | |
| hit_count | int | |

### 2.10 occap_embedding_ref（向量索引引用，可选）

| 字段 | 类型 | 说明 |
|------|------|------|
| id | PK | |
| ref_type | string | asset_version / memory_item |
| ref_id | bigint | |
| provider | string | milvus / pgvector / cloud |
| external_id | string | 外部向量库主键 |

> **说明**：若向量库完全外置，本表可仅存映射；**禁止**在业务表存明文密钥。

---

## 2.11 occap_custom_order（定制工单）

| 字段 | 类型 | 说明 |
|------|------|------|
| id | PK | |
| order_no | string | 工单编号 `CO-{YYYYMMDD}-{6位序号}`，唯一 |
| tenant_id | FK | 所属租户 |
| title | string | 工单标题 |
| type | enum | `skill定制` / `mcp定制` / `记忆策略` / `组合包` / `咨询` |
| priority | enum | `low` / `normal` / `high` / `urgent` |
| status | enum | 见业务逻辑 §3.2 状态机 |
| openclaw_version_range | string | 目标 OpenClaw 版本范围 |
| compliance_requirements | text | JSON 合规约束数组 |
| initial_requirement | text | 原始需求描述 |
| internal_note | text | 运营内部备注（租户不可见） |
| quoted_workdays | int | 报价工时（人天） |
| quoted_price | decimal(12,2) | 报价金额 |
| currency | string | 默认 CNY |
| isv_id | FK | 接单 ISV 用户 ID（confirmed 后填） |
| estimated_delivery_date | date | 预计交付日期 |
| actual_delivery_date | date | 实际交付日期 |
| customer_rating | tinyint | 客户评分 1-5 |
| customer_feedback | text | 客户反馈 |
| dispute_reason | text | 争议原因 |
| submitted_at | datetime | 提交时间 |
| confirmed_at | datetime | 确认时间 |
| completed_at | datetime | 完成时间 |
| cancelled_at | datetime | 取消时间 |
| cancel_reason | string | 取消原因 |
| expire_at | datetime | 报价过期时间 |
| create_user | FK | 创建人 |
| update_user | FK | 更新人 |
| delete_time | datetime | 软删除 |

**索引**：`order_no`(唯一), `tenant_id`, `status`, `isv_id`, `create_user`

## 2.12 occap_custom_order_milestone（交付里程碑）

| 字段 | 类型 | 说明 |
|------|------|------|
| id | PK | |
| order_id | FK | 所属工单 |
| sequence | int | 序号（1-based） |
| title | string | 里程碑标题 |
| description | text | 交付标准 |
| workdays | int | 本里程碑工时 |
| due_date | date | 计划截止 |
| actual_finish_date | date | 实际完成 |
| status | enum | `pending` / `in_progress` / `review` / `passed` / `rejected` / `skipped` |
| delivery_bundle_id | FK | 关联 DeliveryBundle（passed 后填） |
| reject_reason | text | 驳回原因 |
| notes | text | 备注 |
| create_user | FK | |
| update_user | FK | |
| delete_time | datetime | |

**索引**：`order_id, sequence`(唯一), `status`

## 2.13 occap_custom_order_message（工单沟通记录）

| 字段 | 类型 | 说明 |
|------|------|------|
| id | PK | |
| order_id | FK | |
| sender_type | enum | `tenant` / `isv` / `ops` / `system` |
| sender_id | FK | 用户 ID（system 时为 null） |
| content | text | 支持 Markdown |
| attachment_ids | text | JSON 数组，附件 ID 列表 |
| is_internal | bool | 运营内部消息（租户不可见） |
| create_time | datetime | |

**索引**：`order_id`, `create_time`

## 2.14 occap_custom_order_attachment（工单附件）

| 字段 | 类型 | 说明 |
|------|------|------|
| id | PK | |
| order_id | FK | 可空（关联工单） |
| milestone_id | FK | 可空（关联里程碑） |
| uploader_type | enum | `tenant` / `isv` / `ops` |
| uploader_id | FK | |
| file_name | string | 原始文件名 |
| file_uri | string | 对象存储路径 |
| file_size | bigint | 字节 |
| mime_type | string | |
| category | enum | `requirement` / `draft` / `delivery` / `approval_doc` / `other` |
| create_time | datetime | |

**索引**：`order_id`, `milestone_id`

## 2.15 occap_custom_order_audit（工单审计）

| 字段 | 类型 | 说明 |
|------|------|------|
| id | PK | |
| order_id | FK | |
| actor_type | enum | `tenant` / `isv` / `ops` / `system` |
| actor_id | FK | |
| action | string | 动作类型 |
| old_value | text | JSON |
| new_value | text | JSON |
| ip | string | |
| user_agent | string | |
| create_time | datetime | |

**索引**：`order_id, create_time`

## 2.16 occap_task（轻量任务，与工单解耦）

| 字段 | 类型 | 说明 |
|------|------|------|
| id | PK | |
| task_no | string | `TSK-{YYYYMMDD}-{6位序号}`，唯一 |
| order_id | FK | 关联工单（可空） |
| title | string | |
| description | text | |
| type | enum | `review` / `approval` / `delivery` / `agent_job` / `misc` |
| priority | enum | `low` / `normal` / `high` / `urgent` |
| status | enum | `pending` / `assigned` / `in_progress` / `done` / `cancelled` |
| assignee_type | enum | `user` / `isv` / `agent` |
| assignee_id | FK | |
| due_date | datetime | |
| done_at | datetime | |
| done_note | text | |
| create_user | FK | |
| update_user | FK | |
| delete_time | datetime | |

**索引**：`task_no`(唯一), `order_id`, `assignee_id`, `status`

## 2.17 occap_user（平台账号）

| 字段 | 类型 | 说明 |
|------|------|------|
| id | PK | |
| tenant_id | FK | 可空（platform_super 无租户） |
| username | string | 唯一 |
| password_hash | string | bcrypt |
| email | string | |
| phone | string | |
| role | enum | `platform_super` / `platform_ops` / `tenant_admin` / `tenant_member` / `isv` / `audit_readonly` / `agent_principal` |
| status | enum | `active` / `disabled` / `deleted` |
| last_login_at | datetime | |
| create_user | FK | 创建人 |
| create_time | datetime | |
| update_time | datetime | |
| delete_time | datetime | 软删除 |

**索引**：`username`(唯一), `tenant_id`, `role`, `status`

## 2.18 occap_user_session（会话）

| 字段 | 类型 | 说明 |
|------|------|------|
| id | PK | |
| user_id | FK | |
| session_id | string | 唯一，存 Cookie |
| refresh_token | string | JWT RefreshToken |
| ip | string | |
| user_agent | string | |
| expires_at | datetime | Session 过期时间（默认7天） |
| create_time | datetime | |

**索引**：`session_id`(唯一), `user_id`, `expires_at`

## 2.19 occap_api_key（API Key，Agent/ISV 用）

| 字段 | 类型 | 说明 |
|------|------|------|
| id | PK | |
| user_id | FK | |
| key_prefix | string | 前6位可见，如 `occap_` |
| key_hash | string | SHA256 哈希后存 |
| name | string | 名称，如「ISV_A 密钥」 |
| role | enum | 继承 user.role |
| permissions_json | text | 细粒度权限覆盖（可空） |
| rate_limit | int | 每分钟限制次数，默认 60 |
| status | enum | `active` / `revoked` |
| last_used_at | datetime | |
| expires_at | datetime | 可空（永不过期） |
| create_time | datetime | |

**索引**：`key_prefix`(唯一), `user_id`

## 2.20 occap_tenant（租户）

| 字段 | 类型 | 说明 |
|------|------|------|
| id | PK | |
| tenant_no | string | `TN-{6位序号}`，唯一 |
| name | string | |
| code | string | 唯一编码 |
| plan_id | FK | 当前套餐，NULL=试用 |
| status | enum | `trial` / `active` / `frozen` / `cancelled` |
| owner_user_id | FK | 租户创建人 |
| expired_at | datetime | 试用/套餐过期时间 |
| frozen_at | datetime | |
| create_time | datetime | |
| update_time | datetime | |
| delete_time | datetime | |

**索引**：`tenant_no`(唯一), `code`(唯一), `status`

## 2.21 occap_workspace（OpenClaw 工作区关联）

| 字段 | 类型 | 说明 |
|------|------|------|
| id | PK | |
| tenant_id | FK | |
| workspace_id | string | OpenClaw 工作区逻辑 ID |
| workspace_name | string | 别名，如「风控组生产环境」 |
| description | text | |
| bind_token | string | 绑定令牌（一次性验证） |
| status | enum | `pending`（待验证） / `active` / `unbound` |
| first_seen_at | datetime | |
| last_seen_at | datetime | |
| create_time | datetime | |

**索引**：`tenant_id`, `workspace_id`(唯一), `status`

## 2.22 occap_plan（套餐）

| 字段 | 类型 | 说明 |
|------|------|------|
| id | PK | |
| name | string | |
| code | string | 唯一，如 `basic` / `enterprise` |
| quota_json | text | JSON 配额模板 |
| price_monthly | decimal(10,2) | 月付价 |
| price_yearly | decimal(10,2) | 年付价 |
| sort_order | int | |
| status | enum | `active` / `deprecated` |
| create_time | datetime | |
| update_time | datetime | |

**索引**：`code`(唯一), `status`

## 2.23 occap_tenant_subscription（租户订阅记录）

| 字段 | 类型 | 说明 |
|------|------|------|
| id | PK | |
| tenant_id | FK | |
| plan_id | FK | |
| subscription_no | string | `SUB-{6位序号}`，唯一 |
| period_start | date | 本周期开始 |
| period_end | date | 本周期结束 |
| status | enum | `active` / `expired` / `cancelled` |
| auto_renew | bool | 是否自动续费 |
| create_time | datetime | |

**索引**：`tenant_id`, `period_end`

## 2.24 occap_asset（能力资产）

| 字段 | 类型 | 说明 |
|------|------|------|
| id | PK | |
| asset_no | string | `AS-{YYYYMM}-{6位序号}` |
| tenant_id | FK | 所属租户（NULL=平台） |
| isv_id | FK | 创作者 |
| type | enum | `skill` / `mcp_bundle` / `memory_policy` / `composite` |
| slug | string | URL 唯一标识 |
| title | string | |
| summary | text | 一句话简介 |
| description | text | 详细说明 |
| tags | text | JSON 数组 |
| visibility | enum | `public` / `tenant_private` / `channel_canary` |
| risk_level | enum | `low` / `medium` / `high` |
| license_spdx | string | |
| official_review_required | bool | |
| current_version_id | FK | 当前生效版本 |
| create_user | FK | |
| update_user | FK | |
| create_time | datetime | |
| update_time | datetime | |
| delete_time | datetime | |

**索引**：`slug`(唯一), `(status, type)`, `tenant_id`, `isv_id`

## 2.25 occap_asset_version（资产版本）

| 字段 | 类型 | 说明 |
|------|------|------|
| id | PK | |
| asset_id | FK | |
| version | string | semver |
| version_no | int | 递增序号 |
| manifest_json | text | 完整 manifest |
| bundle_uri | string | 包存储路径 |
| bundle_sha256 | string | 校验和 |
| signature | string | 可选签名 |
| changelog | text | 变更说明 |
| status | enum | `draft` / `in_review` / `published` / `deprecated` |
| review_passed_at | datetime | |
| review_passed_by | FK | |
| published_at | datetime | |
| published_by | FK | |
| deprecate_reason | text | |
| rollback_version_id | FK | 可回滚版本 |
| create_user | FK | |
| create_time | datetime | |
| delete_time | datetime | |

**索引**：`(asset_id, version)`(唯一), `(asset_id, status)`, `status`

## 2.26 occap_entitlement（授权）

| 字段 | 类型 | 说明 |
|------|------|------|
| id | PK | |
| entitlement_no | string | `ET-{YYYYMM}-{6位序号}` |
| tenant_id | FK | |
| asset_version_id | FK | |
| plan_id | FK | 来源套餐（可空） |
| source_type | enum | `plan_included` / `addon` / `trial` / `custom_order` |
| quota_json | text | 配额覆盖 |
| status | enum | `active` / `expired` / `revoked` |
| granted_at | datetime | |
| expired_at | datetime | NULL=永不过期 |
| revoked_at | datetime | |
| revoked_reason | string | |
| create_user | FK | |
| create_time | datetime | |

**索引**：`(tenant_id, asset_version_id)`(唯一), `tenant_id`, `status`, `expired_at`

## 2.27 occap_capability_slot（能力槽位，Agent 订阅）

| 字段 | 类型 | 说明 |
|------|------|------|
| id | PK | |
| slot_no | string | `CS-{6位序号}` |
| tenant_id | FK | |
| agent_principal_id | FK | 绑定 Agent 主体 |
| asset_version_id | FK | |
| quota_json | text | 调用次数/并发限制 |
| used_count | int | 当期已用 |
| period_type | enum | `monthly` / `yearly` / `lifetime` |
| status | enum | `active` / `exhausted` / `expired` |
| create_time | datetime | |

**索引**：`slot_no`(唯一), `tenant_id`, `status`

## 2.28 occap_delivery_bundle（交付包）

| 字段 | 类型 | 说明 |
|------|------|------|
| id | PK | |
| bundle_no | string | `DB-{YYYYMM}-{6位序号}` |
| tenant_id | FK | |
| entitlement_id | FK | |
| custom_order_id | FK | 可空 |
| asset_version_ids | text | JSON 数组 |
| bundle_uri | string | |
| bundle_sha256 | string | |
| signature | string | |
| size_bytes | bigint | |
| delivery_type | enum | `direct` / `script` |
| script_version | string | |
| status | enum | `created` / `downloaded` / `applied` / `failed` |
| downloaded_at | datetime | |
| applied_at | datetime | |
| failure_reason | text | |
| create_time | datetime | |

**索引**：`bundle_no`(唯一), `tenant_id`, `status`

## 2.29 occap_notification（站内通知）

| 字段 | 类型 | 说明 |
|------|------|------|
| id | PK | |
| user_id | FK | 通知对象 |
| tenant_id | FK | |
| title | string | |
| content | text | |
| type | enum | `order_submitted` / `order_quoted` / `order_confirmed` / `milestone_rejected` / `milestone_passed` / `task_assigned` / `system` |
| read_time | datetime | 已读时间，NULL=未读 |
| link_url | string | 点击跳转链接 |
| create_time | datetime | |

**索引**：`user_id, read_time`, `tenant_id`, `type`

## 3. 索引建议

- `occap_asset(slug)` 唯一；`occap_asset(status, type)`。
- `occap_asset_version(asset_id, version)` 唯一。
- `occap_entitlement(tenant_id, asset_version_id)` 唯一。
- 全文：`occap_asset(title)` + 可选 `manifest_json` 生成列（或同步 ES）。

## 4. ER 图

见 [database_er.html](database_er.html)（与上表 ASCII 名一致，避免 Mermaid 特殊字符问题）。
