feat(database): add PostgreSQL repository and migration scripts
- Implement PostgresUserRepository with full CRUD operations - Support all query methods (by_id/email/username/token) - Use psycopg2 with RealDictCursor for clean mapping - Create initial schema migration (users/workspaces/members/invitations) - Add database indexes for performance - Setup foreign key constraints for data integrity - Include migration guide and rollback instructions - Add psycopg2 dependency Phase 4 Task 28/68 completed
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
"""
|
||||
数据库迁移脚本 - 初始化表结构
|
||||
"""
|
||||
|
||||
-- 用户表
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id VARCHAR(255) PRIMARY KEY,
|
||||
email VARCHAR(255) UNIQUE NOT NULL,
|
||||
display_name VARCHAR(255) NOT NULL,
|
||||
username VARCHAR(255) UNIQUE,
|
||||
password_hash VARCHAR(255),
|
||||
email_verified BOOLEAN DEFAULT FALSE,
|
||||
email_verification_token VARCHAR(255),
|
||||
password_reset_token VARCHAR(255),
|
||||
password_reset_expires_at TIMESTAMP,
|
||||
last_login_at TIMESTAMP,
|
||||
last_login_ip VARCHAR(50),
|
||||
created_at TIMESTAMP NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_users_email ON users(email);
|
||||
CREATE INDEX idx_users_username ON users(username);
|
||||
CREATE INDEX idx_users_email_verification_token ON users(email_verification_token);
|
||||
CREATE INDEX idx_users_password_reset_token ON users(password_reset_token);
|
||||
|
||||
|
||||
-- 工作空间表
|
||||
CREATE TABLE IF NOT EXISTS workspaces (
|
||||
id VARCHAR(255) PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
owner_user_id VARCHAR(255) NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
subscription_plan VARCHAR(50) DEFAULT 'free',
|
||||
subscription_status VARCHAR(50) DEFAULT 'active',
|
||||
subscription_expires_at TIMESTAMP,
|
||||
max_projects INTEGER DEFAULT 3,
|
||||
max_storage_gb INTEGER DEFAULT 10,
|
||||
used_storage_gb DECIMAL(10, 2) DEFAULT 0,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_workspaces_owner ON workspaces(owner_user_id);
|
||||
|
||||
|
||||
-- 工作空间成员表
|
||||
CREATE TABLE IF NOT EXISTS workspace_members (
|
||||
id VARCHAR(255) PRIMARY KEY,
|
||||
workspace_id VARCHAR(255) NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
|
||||
user_id VARCHAR(255) NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
role VARCHAR(50) NOT NULL,
|
||||
invited_by VARCHAR(255) REFERENCES users(id),
|
||||
joined_at TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||
UNIQUE(workspace_id, user_id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_workspace_members_workspace ON workspace_members(workspace_id);
|
||||
CREATE INDEX idx_workspace_members_user ON workspace_members(user_id);
|
||||
|
||||
|
||||
-- 工作空间邀请表
|
||||
CREATE TABLE IF NOT EXISTS workspace_invitations (
|
||||
id VARCHAR(255) PRIMARY KEY,
|
||||
workspace_id VARCHAR(255) NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
|
||||
inviter_user_id VARCHAR(255) NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
invitee_email VARCHAR(255) NOT NULL,
|
||||
role VARCHAR(50) NOT NULL,
|
||||
invitation_token VARCHAR(255) UNIQUE NOT NULL,
|
||||
status VARCHAR(50) DEFAULT 'pending',
|
||||
expires_at TIMESTAMP,
|
||||
accepted_at TIMESTAMP,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_invitations_workspace ON workspace_invitations(workspace_id);
|
||||
CREATE INDEX idx_invitations_token ON workspace_invitations(invitation_token);
|
||||
CREATE INDEX idx_invitations_workspace_email ON workspace_invitations(workspace_id, invitee_email, status);
|
||||
|
||||
|
||||
-- 项目表(占位,后续 Phase 完善)
|
||||
CREATE TABLE IF NOT EXISTS projects (
|
||||
id VARCHAR(255) PRIMARY KEY,
|
||||
workspace_id VARCHAR(255) NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_projects_workspace ON projects(workspace_id);
|
||||
Reference in New Issue
Block a user