-- QuyUn v2 数据库 DDL(PostgreSQL) -- 说明:本文件用于规格参考,需与 backend/database/migrations 保持一致。 BEGIN; -- Users CREATE TABLE IF NOT EXISTS users( id bigserial PRIMARY KEY, -- 主键ID:自增 username varchar(255) NOT NULL UNIQUE, -- 用户名:唯一,用于登录 password varchar(255) NOT NULL, -- 密码:加密存储 roles text[] DEFAULT '{user}', -- 角色:用户角色列表,如 {user, super_admin} status varchar(50) DEFAULT 'active', -- 状态:active/inactive/banned metas jsonb DEFAULT '{}', -- 元数据:额外扩展信息 balance bigint DEFAULT 0, -- 全局可用余额:分/最小货币单位 balance_frozen bigint DEFAULT 0, -- 全局冻结余额:分/最小货币单位 verified_at timestamp with time zone, -- 实名认证时间 nickname varchar(255) DEFAULT '', -- 昵称:用户显示名称 avatar varchar(512) DEFAULT '', -- 头像:URL地址 gender varchar(32) DEFAULT 'secret', -- 性别:male/female/secret bio varchar(512) DEFAULT '', -- 简介:用户个人简介 birthday date, -- 生日:YYYY-MM-DD location jsonb DEFAULT '{}', -- 位置:省市区信息 {province: "...", city: "..."} points bigint DEFAULT 0, -- 积分:用户积分 phone varchar(32) DEFAULT '', -- 手机号:用于登录/验证 is_real_name_verified boolean DEFAULT FALSE, -- 是否实名认证:true/false created_at timestamp with time zone DEFAULT NOW(), -- 创建时间:默认 now() updated_at timestamp with time zone DEFAULT NOW(), -- 更新时间:默认 now() deleted_at timestamp with time zone -- 删除时间:软删除 ); -- Tenants CREATE TABLE IF NOT EXISTS tenants( id bigserial PRIMARY KEY, -- 主键ID:自增 user_id bigint NOT NULL, -- 创建者ID:关联 users.id code varchar(64) NOT NULL UNIQUE, -- 租户代码:唯一标识,用于 URL 等 uuid uuid NOT NULL, -- UUID:全局唯一标识 name varchar(128) NOT NULL, -- 租户名称 status varchar(64) NOT NULL, -- 状态:pending_verify/verified/banned config jsonb DEFAULT '{}', -- 配置:租户配置信息 expired_at timestamp with time zone, -- 过期时间:租户有效期 created_at timestamp with time zone DEFAULT NOW(), -- 创建时间:默认 now() updated_at timestamp with time zone DEFAULT NOW() -- 更新时间:默认 now() ); CREATE INDEX IF NOT EXISTS idx_tenants_user_id ON tenants(user_id); -- TenantUsers CREATE TABLE IF NOT EXISTS tenant_users( id bigserial PRIMARY KEY, -- 主键ID:自增 tenant_id bigint NOT NULL, -- 租户ID:关联 tenants.id user_id bigint NOT NULL, -- 用户ID:关联 users.id role text[] DEFAULT '{member}', -- 角色:member/tenant_admin status varchar(50) DEFAULT 'verified', -- 状态:pending_verify/verified/banned created_at timestamp with time zone DEFAULT NOW(), -- 创建时间:默认 now() updated_at timestamp with time zone DEFAULT NOW(), -- 更新时间:默认 now() UNIQUE (tenant_id, user_id) ); -- Contents CREATE TABLE IF NOT EXISTS contents( id bigserial PRIMARY KEY, -- 主键ID:自增;用于内容引用 tenant_id bigint NOT NULL, -- 租户ID:多租户隔离关键字段 user_id bigint NOT NULL, -- 用户ID:内容创建者/发布者 title varchar(255) NOT NULL, -- 标题:用于列表展示与搜索 description text NOT NULL, -- 描述:用于详情页展示 status varchar(32) DEFAULT 'draft', -- 状态:draft/reviewing/published/unpublished/blocked visibility varchar(32) DEFAULT 'tenant_only', -- 可见性:public/tenant_only/private preview_seconds int DEFAULT 60, -- 试看秒数:默认 60 preview_downloadable boolean DEFAULT FALSE, -- 试看是否允许下载 published_at timestamp with time zone, -- 发布时间 summary varchar(256) DEFAULT '', -- 简介:用于列表/卡片展示 tags jsonb DEFAULT '[]', -- 标签:JSON 数组 body text DEFAULT '', -- 内容主体:文章内容/详细介绍 genre varchar(64) DEFAULT '', -- 类型/流派 views int DEFAULT 0, -- 浏览量 likes int DEFAULT 0, -- 点赞数 key varchar(32) DEFAULT '', -- 音乐调性/主音 is_pinned boolean DEFAULT FALSE, -- 是否置顶 created_at timestamp with time zone DEFAULT NOW(), -- 创建时间 updated_at timestamp with time zone DEFAULT NOW(), -- 更新时间 deleted_at timestamp with time zone -- 软删除时间 ); CREATE INDEX IF NOT EXISTS idx_contents_tenant_id ON contents(tenant_id); -- MediaAssets CREATE TABLE IF NOT EXISTS media_assets( id bigserial PRIMARY KEY, -- 主键ID:自增 tenant_id bigint NOT NULL, -- 租户ID:多租户隔离关键字段 user_id bigint NOT NULL, -- 用户ID:资源上传者 type varchar(32) DEFAULT 'video', -- 资源类型:video/audio/image status varchar(32) DEFAULT 'uploaded', -- 处理状态:uploaded/processing/ready/failed/deleted provider varchar(64) NOT NULL, -- 存储提供方:s3/minio/oss/local bucket varchar(128) NOT NULL, -- 存储桶 object_key varchar(512) NOT NULL, -- 对象键 meta jsonb DEFAULT '{}', -- 元数据:JSON variant varchar(32) DEFAULT 'main', -- 产物类型:main/preview source_asset_id bigint DEFAULT 0, -- 派生来源资源ID hash varchar(64) DEFAULT '', -- 文件 MD5 哈希 created_at timestamp with time zone DEFAULT NOW(), -- 创建时间 updated_at timestamp with time zone DEFAULT NOW(), -- 更新时间 deleted_at timestamp with time zone -- 软删除时间 ); CREATE INDEX IF NOT EXISTS idx_media_assets_hash ON media_assets (hash); -- ContentAssets CREATE TABLE IF NOT EXISTS content_assets( id bigserial PRIMARY KEY, -- 主键ID:自增 tenant_id bigint NOT NULL, -- 租户ID user_id bigint NOT NULL, -- 用户ID content_id bigint NOT NULL, -- 内容ID asset_id bigint NOT NULL, -- 资源ID role varchar(32) DEFAULT 'main', -- 资源角色:main/cover/preview sort int DEFAULT 0, -- 排序 created_at timestamp with time zone DEFAULT NOW(), -- 创建时间 updated_at timestamp with time zone DEFAULT NOW() -- 更新时间 ); -- ContentPrices CREATE TABLE IF NOT EXISTS content_prices( id bigserial PRIMARY KEY, -- 主键ID:自增 tenant_id bigint NOT NULL, -- 租户ID user_id bigint NOT NULL, -- 用户ID content_id bigint NOT NULL, -- 内容ID currency varchar(16) DEFAULT 'CNY', -- 币种 price_amount bigint NOT NULL, -- 基础价格:分 discount_type varchar(16) DEFAULT 'none', -- 折扣类型:none/percent/amount discount_value bigint DEFAULT 0, -- 折扣值 discount_start_at timestamp with time zone, -- 折扣开始时间 discount_end_at timestamp with time zone, -- 折扣结束时间 created_at timestamp with time zone DEFAULT NOW(), -- 创建时间 updated_at timestamp with time zone DEFAULT NOW(), -- 更新时间 UNIQUE (tenant_id, content_id) ); -- ContentAccess CREATE TABLE IF NOT EXISTS content_access( id bigserial PRIMARY KEY, -- 主键ID:自增 tenant_id bigint NOT NULL, -- 租户ID user_id bigint NOT NULL, -- 用户ID content_id bigint NOT NULL, -- 内容ID order_id bigint DEFAULT 0, -- 订单ID status varchar(16) DEFAULT 'active', -- 权益状态 revoked_at timestamp with time zone, -- 撤销时间 created_at timestamp with time zone DEFAULT NOW(), -- 创建时间 updated_at timestamp with time zone DEFAULT NOW(), -- 更新时间 UNIQUE (tenant_id, user_id, content_id) ); -- Orders CREATE TABLE IF NOT EXISTS orders( id bigserial PRIMARY KEY, -- 主键ID:自增 tenant_id bigint NOT NULL, -- 租户ID user_id bigint NOT NULL, -- 用户ID type varchar(32) DEFAULT 'content_purchase', -- 订单类型 status varchar(32) DEFAULT 'created', -- 订单状态 currency varchar(16) DEFAULT 'CNY', -- 币种 amount_original bigint NOT NULL, -- 原价金额 amount_discount bigint NOT NULL, -- 优惠金额 amount_paid bigint NOT NULL, -- 实付金额 snapshot jsonb DEFAULT '{}', -- 订单快照 idempotency_key varchar(128) NOT NULL, -- 幂等键 paid_at timestamp with time zone, -- 支付时间 refunded_at timestamp with time zone, -- 退款时间 refund_forced boolean DEFAULT FALSE, -- 是否强制退款 refund_operator_user_id bigint DEFAULT 0, -- 退款操作人 refund_reason varchar(255) DEFAULT '', -- 退款原因 created_at timestamp with time zone DEFAULT NOW(), -- 创建时间 updated_at timestamp with time zone DEFAULT NOW(), -- 更新时间 coupon_id bigint DEFAULT 0 -- 关联优惠券ID (0表示未使用) ); -- OrderItems CREATE TABLE IF NOT EXISTS order_items( id bigserial PRIMARY KEY, -- 主键ID:自增 tenant_id bigint NOT NULL, -- 租户ID user_id bigint NOT NULL, -- 用户ID order_id bigint NOT NULL, -- 订单ID content_id bigint NOT NULL, -- 内容ID content_user_id bigint NOT NULL, -- 内容作者用户ID amount_paid bigint NOT NULL, -- 该行实付金额 snapshot jsonb DEFAULT '{}', -- 内容快照 created_at timestamp with time zone DEFAULT NOW(), -- 创建时间 updated_at timestamp with time zone DEFAULT NOW() -- 更新时间 ); -- TenantLedgers CREATE TABLE IF NOT EXISTS tenant_ledgers( id bigserial PRIMARY KEY, -- 主键ID:自增 tenant_id bigint NOT NULL, -- 租户ID user_id bigint NOT NULL, -- 用户ID order_id bigint DEFAULT 0, -- 关联订单ID type varchar(32) NOT NULL, -- 流水类型 amount bigint NOT NULL, -- 流水金额 balance_before bigint NOT NULL, -- 变更前可用余额 balance_after bigint NOT NULL, -- 变更后可用余额 frozen_before bigint NOT NULL, -- 变更前冻结余额 frozen_after bigint NOT NULL, -- 变更后冻结余额 idempotency_key varchar(128) NOT NULL, -- 幂等键 remark varchar(255) NOT NULL, -- 备注 operator_user_id bigint DEFAULT 0, -- 操作者用户ID biz_ref_type varchar(32) DEFAULT '', -- 业务引用类型 biz_ref_id bigint DEFAULT 0, -- 业务引用ID created_at timestamp with time zone DEFAULT NOW(), -- 创建时间 updated_at timestamp with time zone DEFAULT NOW() -- 更新时间 ); -- TenantInvites CREATE TABLE IF NOT EXISTS tenant_invites( id bigserial PRIMARY KEY, -- 主键ID:自增 tenant_id bigint NOT NULL, -- 租户ID user_id bigint NOT NULL, -- 创建人用户ID code varchar(64) NOT NULL, -- 邀请码 status varchar(32) DEFAULT 'active', -- 邀请状态 max_uses int NOT NULL, -- 最大可使用次数 used_count int DEFAULT 0, -- 已使用次数 expires_at timestamp with time zone, -- 过期时间 disabled_at timestamp with time zone, -- 禁用时间 disabled_operator_user_id bigint DEFAULT 0, -- 禁用操作人用户ID remark varchar(255) DEFAULT '', -- 备注 created_at timestamp with time zone DEFAULT NOW(), -- 创建时间 updated_at timestamp with time zone DEFAULT NOW() -- 更新时间 ); -- TenantJoinRequests CREATE TABLE IF NOT EXISTS tenant_join_requests( id bigserial PRIMARY KEY, -- 主键ID:自增 tenant_id bigint NOT NULL, -- 租户ID user_id bigint NOT NULL, -- 申请人用户ID status varchar(32) DEFAULT 'pending', -- 申请状态 reason varchar(255) NOT NULL, -- 申请原因 decided_at timestamp with time zone, -- 处理时间 decided_operator_user_id bigint DEFAULT 0, -- 处理人用户ID decided_reason varchar(255) DEFAULT '', -- 处理说明 created_at timestamp with time zone DEFAULT NOW(), -- 创建时间 updated_at timestamp with time zone DEFAULT NOW() -- 更新时间 ); -- Comments CREATE TABLE IF NOT EXISTS comments( id bigserial PRIMARY KEY, -- 主键ID:自增 tenant_id bigint NOT NULL, -- 租户ID user_id bigint NOT NULL, -- 用户ID content_id bigint NOT NULL, -- 内容ID reply_to bigint DEFAULT 0, -- 回复评论ID:0表示一级评论 content text NOT NULL, -- 评论内容 likes int DEFAULT 0, -- 点赞数 created_at timestamp with time zone DEFAULT NOW(), -- 创建时间 updated_at timestamp with time zone DEFAULT NOW(), -- 更新时间 deleted_at timestamp with time zone -- 软删除时间 ); CREATE INDEX IF NOT EXISTS idx_comments_content_id ON comments(content_id); CREATE INDEX IF NOT EXISTS idx_comments_user_id ON comments(user_id); -- User Content Actions (Like, Favorite) CREATE TABLE IF NOT EXISTS user_content_actions( id bigserial PRIMARY KEY, -- 主键ID:自增 user_id bigint NOT NULL, -- 用户ID content_id bigint NOT NULL, -- 内容ID type varchar(32) NOT NULL, -- 类型:like, favorite created_at timestamp with time zone DEFAULT NOW(), -- 创建时间 UNIQUE (user_id, content_id, type) ); -- User Comment Actions (Like) CREATE TABLE IF NOT EXISTS user_comment_actions( id bigserial PRIMARY KEY, -- 主键ID:自增 user_id bigint NOT NULL, -- 用户ID comment_id bigint NOT NULL, -- 评论ID type varchar(32) NOT NULL, -- 类型:like created_at timestamp with time zone DEFAULT NOW(), -- 创建时间 UNIQUE (user_id, comment_id, type) ); -- Payout Accounts CREATE TABLE IF NOT EXISTS payout_accounts( id bigserial PRIMARY KEY, -- 主键ID:自增 tenant_id bigint NOT NULL, -- 租户ID user_id bigint NOT NULL, -- 用户ID type varchar(32) NOT NULL, -- 类型:bank, alipay name varchar(128) NOT NULL, -- 账户名称/开户行 account varchar(128) NOT NULL, -- 账号 realname varchar(128) NOT NULL, -- 真实姓名 created_at timestamp with time zone DEFAULT NOW(), -- 创建时间 updated_at timestamp with time zone DEFAULT NOW() -- 更新时间 ); CREATE INDEX IF NOT EXISTS idx_payout_accounts_tenant_id ON payout_accounts(tenant_id); -- Notifications CREATE TABLE IF NOT EXISTS notifications( id bigserial PRIMARY KEY, -- 主键ID:自增 user_id bigint NOT NULL, -- 接收用户ID tenant_id bigint DEFAULT 0, -- 来源租户ID type varchar(32) NOT NULL, -- 类型:system, order, audit, interaction title varchar(255) NOT NULL, -- 标题 content text NOT NULL, -- 内容 is_read boolean DEFAULT FALSE, -- 是否已读 created_at timestamp with time zone DEFAULT NOW() -- 创建时间 ); CREATE INDEX IF NOT EXISTS idx_notifications_user_id ON notifications(user_id); -- Coupons CREATE TABLE IF NOT EXISTS coupons( id bigserial PRIMARY KEY, -- 主键ID tenant_id bigint NOT NULL DEFAULT 0, -- 租户ID title varchar(255) NOT NULL, -- 优惠券标题 description text, -- 优惠券描述 type varchar(32) NOT NULL, -- 优惠券类型: fix_amount/discount value bigint NOT NULL, -- 优惠券面值 min_order_amount bigint NOT NULL DEFAULT 0, -- 最低订单金额门槛 max_discount bigint, -- 最高抵扣金额 total_quantity integer NOT NULL DEFAULT 0, -- 发行总量 used_quantity integer NOT NULL DEFAULT 0, -- 已使用数量 start_at timestamptz, -- 开始生效时间 end_at timestamptz, -- 过期失效时间 created_at timestamptz NOT NULL DEFAULT NOW(), updated_at timestamptz NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_coupons_tenant_id ON coupons(tenant_id); CREATE TABLE IF NOT EXISTS user_coupons( id bigserial PRIMARY KEY, user_id bigint NOT NULL, -- 用户ID coupon_id bigint NOT NULL, -- 优惠券ID order_id bigint, -- 使用该优惠券的订单ID status varchar(32) NOT NULL DEFAULT 'unused', -- 状态: unused/used/expired used_at timestamptz, -- 使用时间 created_at timestamptz NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_user_coupons_user_id ON user_coupons(user_id); CREATE INDEX IF NOT EXISTS idx_user_coupons_coupon_id ON user_coupons(coupon_id); COMMIT;