docs: v2.0 全面修订 — 开源优先 + 扁平化用户模型
核心变更: - 产品定位: '像用水电一样使用AI能力,注册即用,按量付费' - 用户模型: 去掉租户概念,扁平化为 订阅用户/非订阅用户 - 认证: Casdoor SSO + 阿里云实名认证(替代 Keycloak) - LLM 网关: NEW API 开源(替代自研 Go 网关) - AI 服务: FastAPI(替代 Go 自研,AI 生态更兼容) - 新增: 欠费不影响已发布能力(核心商业规则) - 新增: 开发者提现模块 - 开发周期: 20周 → 15周(开源组件复用) 文档列表: - 01-PRD-产品需求文档.md (v2.0) - 02-系统架构设计.md (v2.0) - 03-数据库设计.md (v2.0) - 04-API接口规范.md (v2.0) - 05-AGENTS-MiMoCode配置.md (v2.0)
This commit is contained in:
1 parent
d9a8a0e93c
commit
8209f15d0a
5 files changed
+1031
-1433
No files matched your search
+119
-243
@@ -1,6 +1,7 @@
|
||||
# AI PaaS 商业平台 — 数据库设计
|
||||
|
||||
> 版本: 1.0 | 日期: 2026-06-27
|
||||
> 版本: 2.0 | 日期: 2026-06-27
|
||||
> 状态: 已修订(扁平化:去掉租户,直接挂在用户下)
|
||||
> 数据库: PostgreSQL 16+ | 缓存: Redis 7+
|
||||
|
||||
---
|
||||
@@ -8,239 +9,181 @@
|
||||
## 1. ER 关系图
|
||||
|
||||
```
|
||||
tenants ──1:N──→ users
|
||||
tenants ──1:N──→ capabilities
|
||||
tenants ──1:N──→ subscriptions
|
||||
tenants ──1:N──→ credit_transactions
|
||||
tenants ──1:N──→ token_usage
|
||||
tenants ──1:N──→ skills
|
||||
tenants ──1:N──→ im_sessions
|
||||
|
||||
users ──1:N──→ api_keys
|
||||
users ──1:N──→ skill_purchases
|
||||
|
||||
capabilities ──1:N──→ capability_versions
|
||||
users ──1:1──→ subscriptions
|
||||
users ──1:N──→ credit_transactions
|
||||
users ──1:N──→ token_usage
|
||||
users ──1:N──→ capabilities (开发者发布)
|
||||
users ──1:N──→ skills (开发者发布)
|
||||
users ──1:N──→ skill_purchases (用户购买)
|
||||
skills ──1:N──→ skill_purchases
|
||||
|
||||
im_sessions ──1:N──→ im_messages
|
||||
capabilities ──1:N──→ capability_versions
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 表定义
|
||||
|
||||
### 2.1 tenants(租户)
|
||||
|
||||
```sql
|
||||
CREATE TABLE tenants (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name VARCHAR(255) NOT NULL,
|
||||
slug VARCHAR(100) NOT NULL UNIQUE, -- URL 友好标识
|
||||
plan VARCHAR(20) NOT NULL DEFAULT 'free', -- free/pro/team/enterprise
|
||||
credit_balance BIGINT NOT NULL DEFAULT 0, -- 毫 Credit (1 Credit = 1000)
|
||||
credit_included BIGINT NOT NULL DEFAULT 100000, -- 月包含 Credit (毫)
|
||||
credit_used BIGINT NOT NULL DEFAULT 0, -- 本月已用 (毫)
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'active', -- active/suspended/deleted
|
||||
settings JSONB DEFAULT '{}', -- 租户配置
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_tenants_slug ON tenants(slug);
|
||||
CREATE INDEX idx_tenants_status ON tenants(status);
|
||||
|
||||
-- RLS
|
||||
ALTER TABLE tenants ENABLE ROW LEVEL SECURITY;
|
||||
CREATE POLICY tenant_isolation ON tenants
|
||||
USING (id = current_setting('app.tenant_id')::uuid);
|
||||
```
|
||||
|
||||
### 2.2 users(用户)
|
||||
### 2.1 users(用户)
|
||||
|
||||
```sql
|
||||
CREATE TABLE users (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
email VARCHAR(255) NOT NULL,
|
||||
password_hash VARCHAR(255), -- Keycloak 管理时可为空
|
||||
name VARCHAR(255) NOT NULL,
|
||||
role VARCHAR(20) NOT NULL DEFAULT 'user', -- owner/admin/developer/user
|
||||
avatar_url VARCHAR(500),
|
||||
phone VARCHAR(20) UNIQUE, -- 手机号(主登录方式)
|
||||
email VARCHAR(255) UNIQUE, -- 邮箱(备用登录)
|
||||
password_hash VARCHAR(255), -- 密码哈希(邮箱登录时)
|
||||
name VARCHAR(255) NOT NULL, -- 显示名称
|
||||
real_name VARCHAR(100), -- 真实姓名(实名认证后)
|
||||
id_card_hash VARCHAR(64), -- 身份证号哈希(实名认证后)
|
||||
avatar_url VARCHAR(500), -- 头像
|
||||
role VARCHAR(20) NOT NULL DEFAULT 'user', -- user/admin
|
||||
verified BOOLEAN DEFAULT FALSE, -- 是否完成实名认证
|
||||
subscribed BOOLEAN DEFAULT FALSE, -- 是否为订阅用户
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'active', -- active/suspended/deleted
|
||||
casdoor_id VARCHAR(255), -- Casdoor 关联 ID
|
||||
last_login_at TIMESTAMP WITH TIME ZONE,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
UNIQUE(tenant_id, email)
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_users_tenant ON users(tenant_id);
|
||||
CREATE INDEX idx_users_phone ON users(phone);
|
||||
CREATE INDEX idx_users_email ON users(email);
|
||||
CREATE INDEX idx_users_role ON users(role);
|
||||
|
||||
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
|
||||
CREATE POLICY tenant_isolation ON users
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
CREATE INDEX idx_users_status ON users(status);
|
||||
CREATE INDEX idx_users_verified ON users(verified);
|
||||
CREATE INDEX idx_users_subscribed ON users(subscribed);
|
||||
CREATE INDEX idx_users_casdoor ON users(casdoor_id);
|
||||
```
|
||||
|
||||
### 2.3 api_keys(API 密钥)
|
||||
### 2.2 api_keys(API 密钥)
|
||||
|
||||
```sql
|
||||
CREATE TABLE api_keys (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
name VARCHAR(255) NOT NULL, -- 用途标识
|
||||
key_hash VARCHAR(64) NOT NULL UNIQUE, -- SHA-256 哈希
|
||||
key_prefix VARCHAR(10) NOT NULL, -- 前缀 aip_xxxx
|
||||
scopes TEXT[] DEFAULT '{}', -- 权限范围
|
||||
last_used_at TIMESTAMP WITH TIME ZONE,
|
||||
expires_at TIMESTAMP WITH TIME ZONE, -- 可选过期时间
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'active', -- active/revoked
|
||||
expires_at TIMESTAMP WITH TIME ZONE,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'active',
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_api_keys_tenant ON api_keys(tenant_id);
|
||||
CREATE INDEX idx_api_keys_hash ON api_keys(key_hash);
|
||||
CREATE INDEX idx_api_keys_user ON api_keys(user_id);
|
||||
|
||||
ALTER TABLE api_keys ENABLE ROW LEVEL SECURITY;
|
||||
CREATE POLICY tenant_isolation ON api_keys
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
CREATE INDEX idx_api_keys_hash ON api_keys(key_hash);
|
||||
```
|
||||
|
||||
### 2.4 subscriptions(订阅)
|
||||
### 2.3 subscriptions(订阅)
|
||||
|
||||
```sql
|
||||
CREATE TABLE subscriptions (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
plan VARCHAR(20) NOT NULL, -- free/pro/team/enterprise
|
||||
credits_included BIGINT NOT NULL, -- 月包含 Credit (毫)
|
||||
credits_used BIGINT NOT NULL DEFAULT 0, -- 本月已用
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
plan VARCHAR(20) NOT NULL DEFAULT 'free', -- free/developer/pro/flagship
|
||||
credits_included BIGINT NOT NULL DEFAULT 0, -- 月包含 Token 数
|
||||
credits_used BIGINT NOT NULL DEFAULT 0, -- 本月已用 Token
|
||||
price_cents INTEGER NOT NULL DEFAULT 0, -- 月费 (分)
|
||||
billing_cycle VARCHAR(10) NOT NULL DEFAULT 'monthly', -- monthly/yearly
|
||||
billing_cycle VARCHAR(10) NOT NULL DEFAULT 'monthly',
|
||||
period_start TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
period_end TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'active', -- active/cancelled/expired
|
||||
stripe_sub_id VARCHAR(255), -- Stripe 订阅 ID
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'active', -- active/cancelled/expired/grace
|
||||
grace_end TIMESTAMP WITH TIME ZONE, -- 欠费保护期截止
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
UNIQUE(user_id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_subscriptions_tenant ON subscriptions(tenant_id);
|
||||
CREATE INDEX idx_subscriptions_user ON subscriptions(user_id);
|
||||
CREATE INDEX idx_subscriptions_status ON subscriptions(status);
|
||||
CREATE INDEX idx_subscriptions_period ON subscriptions(period_start, period_end);
|
||||
|
||||
ALTER TABLE subscriptions ENABLE ROW LEVEL SECURITY;
|
||||
CREATE POLICY tenant_isolation ON subscriptions
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
```
|
||||
|
||||
### 2.5 credit_transactions(Credit 流水 — 不可变)
|
||||
### 2.4 credit_transactions(Credit 流水 — 不可变)
|
||||
|
||||
```sql
|
||||
CREATE TABLE credit_transactions (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id),
|
||||
user_id UUID NOT NULL REFERENCES users(id),
|
||||
type VARCHAR(30) NOT NULL, -- usage/topup/subscription/bonus/refund
|
||||
amount BIGINT NOT NULL, -- 变动量 (毫 Credit),正=加,负=减
|
||||
amount BIGINT NOT NULL, -- 变动量 (分),正=加,负=减
|
||||
balance_after BIGINT NOT NULL, -- 变动后余额
|
||||
description TEXT, -- 描述
|
||||
trace_id VARCHAR(64), -- 关联的调用 trace
|
||||
idempotency_key VARCHAR(64) UNIQUE, -- 幂等键 (防重复扣费)
|
||||
reference_type VARCHAR(30), -- llm_call/skill_purchase/topup/subscription
|
||||
reference_id UUID, -- 关联 ID
|
||||
description TEXT,
|
||||
trace_id VARCHAR(64),
|
||||
idempotency_key VARCHAR(64) UNIQUE,
|
||||
reference_type VARCHAR(30), -- llm_call/skill_purchase/topup
|
||||
reference_id UUID,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
-- 不可变: 无 updated_at, 无 DELETE
|
||||
);
|
||||
|
||||
CREATE INDEX idx_credit_tx_tenant ON credit_transactions(tenant_id);
|
||||
CREATE INDEX idx_credit_tx_user ON credit_transactions(user_id);
|
||||
CREATE INDEX idx_credit_tx_type ON credit_transactions(type);
|
||||
CREATE INDEX idx_credit_tx_created ON credit_transactions(created_at);
|
||||
CREATE INDEX idx_credit_tx_trace ON credit_transactions(trace_id);
|
||||
|
||||
-- 不可变表: 禁用 UPDATE 和 DELETE
|
||||
CREATE RULE credit_tx_no_update AS ON UPDATE TO credit_transactions DO INSTEAD NOTHING;
|
||||
CREATE RULE credit_tx_no_delete AS ON DELETE TO credit_transactions DO INSTEAD NOTHING;
|
||||
|
||||
ALTER TABLE credit_transactions ENABLE ROW LEVEL SECURITY;
|
||||
CREATE POLICY tenant_isolation ON credit_transactions
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
```
|
||||
|
||||
### 2.6 token_usage(Token 用量 — 按月分区)
|
||||
### 2.5 token_usage(Token 用量 — 按月分区)
|
||||
|
||||
```sql
|
||||
-- 主表 (自动分区)
|
||||
CREATE TABLE token_usage (
|
||||
id UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL,
|
||||
user_id UUID NOT NULL,
|
||||
capability_id UUID,
|
||||
user_id UUID,
|
||||
model VARCHAR(100) NOT NULL, -- deepseek-chat, qwen-turbo
|
||||
provider VARCHAR(50) NOT NULL, -- deepseek, qianwen, doubao
|
||||
model VARCHAR(100) NOT NULL,
|
||||
provider VARCHAR(50) NOT NULL,
|
||||
prompt_tokens INTEGER NOT NULL,
|
||||
completion_tokens INTEGER NOT NULL,
|
||||
total_tokens INTEGER NOT NULL,
|
||||
cost_credits BIGINT NOT NULL, -- 消耗 Credit (毫)
|
||||
cost_cents BIGINT NOT NULL, -- 消耗费用 (分)
|
||||
trace_id VARCHAR(64),
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (id, created_at)
|
||||
) PARTITION BY RANGE (created_at);
|
||||
|
||||
-- 按月创建分区
|
||||
CREATE TABLE token_usage_2026_07 PARTITION OF token_usage
|
||||
FOR VALUES FROM ('2026-07-01') TO ('2026-08-01');
|
||||
CREATE TABLE token_usage_2026_08 PARTITION OF token_usage
|
||||
FOR VALUES FROM ('2026-08-01') TO ('2026-09-01');
|
||||
-- ... 自动创建未来分区
|
||||
|
||||
CREATE INDEX idx_token_usage_tenant ON token_usage(tenant_id, created_at);
|
||||
CREATE INDEX idx_token_usage_user ON token_usage(user_id, created_at);
|
||||
CREATE INDEX idx_token_usage_model ON token_usage(model, created_at);
|
||||
CREATE INDEX idx_token_usage_trace ON token_usage(trace_id);
|
||||
|
||||
-- 不可变表
|
||||
CREATE RULE token_usage_no_update AS ON UPDATE TO token_usage DO INSTEAD NOTHING;
|
||||
CREATE RULE token_usage_no_delete AS ON DELETE TO token_usage DO INSTEAD NOTHING;
|
||||
```
|
||||
|
||||
### 2.7 capabilities(能力)
|
||||
### 2.6 capabilities(AI 能力)
|
||||
|
||||
```sql
|
||||
CREATE TABLE capabilities (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
type VARCHAR(30) NOT NULL, -- agent/mcp/skill/llm/custom
|
||||
version VARCHAR(50) NOT NULL, -- SemVer
|
||||
version VARCHAR(50) NOT NULL,
|
||||
description TEXT,
|
||||
tags TEXT[] DEFAULT '{}',
|
||||
endpoint VARCHAR(500), -- 调用地址
|
||||
protocol VARCHAR(20) DEFAULT 'rest', -- rest/grpc/websocket
|
||||
pricing_config JSONB DEFAULT '{}', -- 计费配置
|
||||
dependencies JSONB DEFAULT '[]', -- 依赖的其他能力
|
||||
owner_id UUID REFERENCES users(id),
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'draft', -- draft/pending_review/active/deprecated/rejected
|
||||
review_note TEXT, -- 审核备注
|
||||
view_count INTEGER DEFAULT 0, -- 浏览次数
|
||||
invoke_count BIGINT DEFAULT 0, -- 调用次数
|
||||
endpoint VARCHAR(500),
|
||||
protocol VARCHAR(20) DEFAULT 'rest',
|
||||
pricing_config JSONB DEFAULT '{}',
|
||||
dependencies JSONB DEFAULT '[]',
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'draft',
|
||||
review_note TEXT,
|
||||
view_count INTEGER DEFAULT 0,
|
||||
invoke_count BIGINT DEFAULT 0,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
UNIQUE(tenant_id, name, version)
|
||||
UNIQUE(user_id, name, version)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_capabilities_tenant ON capabilities(tenant_id);
|
||||
CREATE INDEX idx_capabilities_user ON capabilities(user_id);
|
||||
CREATE INDEX idx_capabilities_type ON capabilities(type);
|
||||
CREATE INDEX idx_capabilities_status ON capabilities(status);
|
||||
CREATE INDEX idx_capabilities_tags ON capabilities USING GIN(tags);
|
||||
|
||||
ALTER TABLE capabilities ENABLE ROW LEVEL SECURITY;
|
||||
CREATE POLICY tenant_isolation ON capabilities
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
```
|
||||
|
||||
### 2.8 capability_versions(能力版本)
|
||||
### 2.7 capability_versions(能力版本)
|
||||
|
||||
```sql
|
||||
CREATE TABLE capability_versions (
|
||||
@@ -249,175 +192,108 @@ CREATE TABLE capability_versions (
|
||||
version VARCHAR(50) NOT NULL,
|
||||
endpoint VARCHAR(500),
|
||||
changelog TEXT,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'active', -- active/deprecated
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'active',
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
UNIQUE(capability_id, version)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_cap_versions_cap ON capability_versions(capability_id);
|
||||
```
|
||||
|
||||
### 2.9 skills(技能)
|
||||
### 2.8 skills(技能市场)
|
||||
|
||||
```sql
|
||||
CREATE TABLE skills (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
developer_id UUID NOT NULL REFERENCES users(id),
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
category VARCHAR(50), -- 分类
|
||||
category VARCHAR(50),
|
||||
tags TEXT[] DEFAULT '{}',
|
||||
pricing_model VARCHAR(20) NOT NULL DEFAULT 'per_call', -- per_call/per_token/subscription/free
|
||||
price_credits BIGINT NOT NULL DEFAULT 0, -- 价格 (毫 Credit)
|
||||
pricing_model VARCHAR(20) NOT NULL DEFAULT 'per_call',
|
||||
price_credits BIGINT NOT NULL DEFAULT 0,
|
||||
platform_share NUMERIC(3,2) DEFAULT 0.25, -- 平台分成比例
|
||||
developer_share NUMERIC(3,2) DEFAULT 0.75, -- 开发者分成比例
|
||||
endpoint VARCHAR(500), -- API 地址
|
||||
documentation TEXT, -- 使用文档
|
||||
endpoint VARCHAR(500),
|
||||
documentation TEXT,
|
||||
icon_url VARCHAR(500),
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'draft', -- draft/active/suspended/deleted
|
||||
rating NUMERIC(3,2) DEFAULT 0, -- 评分
|
||||
rating_count INTEGER DEFAULT 0, -- 评分人数
|
||||
view_count INTEGER DEFAULT 0, -- 浏览次数
|
||||
purchase_count INTEGER DEFAULT 0, -- 购买次数
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'draft',
|
||||
rating NUMERIC(3,2) DEFAULT 0,
|
||||
rating_count INTEGER DEFAULT 0,
|
||||
view_count INTEGER DEFAULT 0,
|
||||
purchase_count INTEGER DEFAULT 0,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_skills_tenant ON skills(tenant_id);
|
||||
CREATE INDEX idx_skills_developer ON skills(developer_id);
|
||||
CREATE INDEX idx_skills_category ON skills(category);
|
||||
CREATE INDEX idx_skills_status ON skills(status);
|
||||
CREATE INDEX idx_skills_tags ON skills USING GIN(tags);
|
||||
CREATE INDEX idx_skills_rating ON skills(rating DESC);
|
||||
CREATE INDEX idx_skills_name_search ON skills USING GIN(to_tsvector('simple', name || ' ' || COALESCE(description, '')));
|
||||
|
||||
ALTER TABLE skills ENABLE ROW LEVEL SECURITY;
|
||||
CREATE POLICY tenant_isolation ON skills
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
CREATE INDEX idx_skills_name_search ON skills USING GIN(
|
||||
to_tsvector('simple', name || ' ' || COALESCE(description, ''))
|
||||
);
|
||||
```
|
||||
|
||||
### 2.10 skill_purchases(技能购买)
|
||||
### 2.9 skill_purchases(技能购买)
|
||||
|
||||
```sql
|
||||
CREATE TABLE skill_purchases (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id),
|
||||
user_id UUID NOT NULL REFERENCES users(id),
|
||||
skill_id UUID NOT NULL REFERENCES skills(id),
|
||||
developer_id UUID NOT NULL, -- 开发者 user_id
|
||||
amount_credits BIGINT NOT NULL, -- 支付金额 (毫 Credit)
|
||||
platform_share BIGINT NOT NULL, -- 平台分成
|
||||
developer_share BIGINT NOT NULL, -- 开发者分成
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'active', -- active/refunded
|
||||
developer_id UUID NOT NULL,
|
||||
amount_cents BIGINT NOT NULL,
|
||||
platform_share BIGINT NOT NULL,
|
||||
developer_share BIGINT NOT NULL,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'active',
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_skill_purchases_user ON skill_purchases(user_id);
|
||||
CREATE INDEX idx_skill_purchases_skill ON skill_purchases(skill_id);
|
||||
CREATE INDEX idx_skill_purchases_tenant ON skill_purchases(tenant_id);
|
||||
```
|
||||
|
||||
### 2.11 im_sessions(IM 会话)
|
||||
### 2.10 developer_payouts(开发者提现)
|
||||
|
||||
```sql
|
||||
CREATE TABLE im_sessions (
|
||||
CREATE TABLE developer_payouts (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id),
|
||||
platform VARCHAR(20) NOT NULL, -- wechat/qq/dingtalk/feishu
|
||||
user_identifier VARCHAR(255) NOT NULL, -- 平台用户标识
|
||||
user_name VARCHAR(255), -- 平台用户名
|
||||
bot_id VARCHAR(255), -- 机器人标识
|
||||
model VARCHAR(100) DEFAULT 'deepseek-chat', -- 当前使用模型
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'active', -- active/closed
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
last_active_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_im_sessions_tenant ON im_sessions(tenant_id);
|
||||
CREATE INDEX idx_im_sessions_platform ON im_sessions(platform, user_identifier);
|
||||
|
||||
ALTER TABLE im_sessions ENABLE ROW LEVEL SECURITY;
|
||||
CREATE POLICY tenant_isolation ON im_sessions
|
||||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||||
```
|
||||
|
||||
### 2.12 im_messages(IM 消息)
|
||||
|
||||
```sql
|
||||
CREATE TABLE im_messages (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
session_id UUID NOT NULL REFERENCES im_sessions(id),
|
||||
role VARCHAR(10) NOT NULL, -- user/assistant/system
|
||||
content TEXT NOT NULL,
|
||||
model VARCHAR(100), -- 使用的模型
|
||||
tokens_used INTEGER,
|
||||
cost_credits BIGINT,
|
||||
developer_id UUID NOT NULL REFERENCES users(id),
|
||||
amount_cents BIGINT NOT NULL,
|
||||
method VARCHAR(20) NOT NULL, -- alipay/bank
|
||||
account_info JSONB NOT NULL, -- 加密的收款信息
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'pending', -- pending/paid/rejected
|
||||
paid_at TIMESTAMP WITH TIME ZONE,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_im_messages_session ON im_messages(session_id, created_at);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Redis 数据结构
|
||||
|
||||
### 3.1 计费相关
|
||||
|
||||
```
|
||||
# Credit 余额 (毫 Credit)
|
||||
billing:balance:{tenant_id} → "12580000"
|
||||
# 类型: String
|
||||
# 操作: GET / DECRBY (Lua 原子)
|
||||
# TTL: 永不过期
|
||||
# Credit 余额 (分)
|
||||
billing:balance:{user_id} → "125800"
|
||||
|
||||
# 幂等键
|
||||
billing:idempotency:{tenant_id}:{idempotency_key} → {
|
||||
"result": "success",
|
||||
"amount": -1280,
|
||||
"trace_id": "abc-123"
|
||||
}
|
||||
# 类型: Hash
|
||||
# TTL: 24 小时
|
||||
billing:idempotency:{user_id}:{key} → "OK"
|
||||
# TTL: 24h
|
||||
|
||||
# 每日统计
|
||||
billing:daily:{tenant_id}:{YYYY-MM-DD} → {
|
||||
"calls": "342",
|
||||
"tokens": "456000",
|
||||
"credits": "1280000"
|
||||
}
|
||||
# 类型: Hash
|
||||
# TTL: 7 天
|
||||
billing:daily:{user_id}:{YYYY-MM-DD} → { calls, tokens, cost }
|
||||
# TTL: 7d
|
||||
|
||||
# 订阅信息
|
||||
billing:subscription:{tenant_id} → {
|
||||
"plan": "pro",
|
||||
"credits_included": "50000000",
|
||||
"credits_used": "12800000",
|
||||
"period_end": "2026-07-27T00:00:00Z"
|
||||
}
|
||||
# 类型: Hash
|
||||
# 订阅信息缓存
|
||||
billing:subscription:{user_id} → { plan, credits_included, credits_used, period_end }
|
||||
# TTL: 永不过期
|
||||
```
|
||||
|
||||
### 3.2 限流相关
|
||||
# 限流
|
||||
ratelimit:{user_id}:{window} → count
|
||||
# TTL: 窗口大小
|
||||
|
||||
```
|
||||
# 滑动窗口限流
|
||||
ratelimit:{tenant_id}:{window_key} → "1" (INCR)
|
||||
# 类型: String
|
||||
# TTL: 窗口大小 (如 60s)
|
||||
```
|
||||
|
||||
### 3.3 IM 会话
|
||||
|
||||
```
|
||||
# 会话上下文
|
||||
im:session:{platform}:{user_id} → {
|
||||
"tenant_id": "xxx",
|
||||
"model": "deepseek-chat",
|
||||
"messages": "[{role,content}, ...]" # 最近 20 条
|
||||
}
|
||||
# 类型: Hash
|
||||
# TTL: 24 小时
|
||||
# IM 会话上下文
|
||||
im:session:{platform}:{user_id} → { messages: [...] }
|
||||
# TTL: 24h
|
||||
```
|
||||
|
||||
---
|
||||
@@ -426,7 +302,7 @@ im:session:{platform}:{user_id} → {
|
||||
|
||||
```
|
||||
migrations/
|
||||
├── 001_init.sql # 所有表 + RLS + 索引
|
||||
├── 001_init.sql # 所有表
|
||||
├── 002_seed_data.sql # 开发环境种子数据
|
||||
├── 003_add_partitions.sql # token_usage 分区
|
||||
└── 004_add_fulltext.sql # 全文搜索索引
|
||||
@@ -436,7 +312,7 @@ migrations/
|
||||
- Migration 文件名: `{序号}_{描述}.sql`
|
||||
- 不允许修改已执行的 Migration
|
||||
- 每个 Migration 必须可回滚
|
||||
- 生产环境 Migration 必须在低峰期执行
|
||||
- 不可变表 (credit_transactions, token_usage): 无 UPDATE/DELETE
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in new issue
Block a user