From 26215e8e47e3e3cee39871baac226a30ad8250b6 Mon Sep 17 00:00:00 2001 From: Rogee Date: Thu, 8 Jan 2026 16:24:51 +0800 Subject: [PATCH] chore: align db spec with migrations --- specs/DB.sql | 444 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 316 insertions(+), 128 deletions(-) diff --git a/specs/DB.sql b/specs/DB.sql index fd0335c..d30aa0d 100644 --- a/specs/DB.sql +++ b/specs/DB.sql @@ -1,151 +1,339 @@ --- 新项目数据库 DDL(PostgreSQL) --- 目标:多租户(tenant_id 全表隔离)+ tenant_uuid 用于 OSS Key:quyun//. --- 注意:tenant_uuid 由业务代码生成写入(不使用 DB 扩展默认值) +-- QuyUn v2 数据库 DDL(PostgreSQL) +-- 说明:本文件用于规格参考,需与 backend/database/migrations 保持一致。 BEGIN; --- 1) 租户 -CREATE TABLE IF NOT EXISTS tenants ( - id BIGSERIAL PRIMARY KEY, - tenant_code VARCHAR(64) NOT NULL, - tenant_uuid UUID NOT NULL, - name VARCHAR(128) NOT NULL DEFAULT '', - status INT2 NOT NULL DEFAULT 0, - config JSONB NOT NULL DEFAULT '{}'::jsonb, - created_at TIMESTAMPTZ NOT NULL DEFAULT now(), - updated_at TIMESTAMPTZ NOT NULL DEFAULT now() +-- 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 -- 删除时间:软删除 ); --- tenant_code:不区分大小写(写入/查询均 lower),并限制字符集 a-z0-9_- -ALTER TABLE tenants - ADD CONSTRAINT tenants_tenant_code_format - CHECK (tenant_code ~ '^[A-Za-z0-9_-]+$'); - -CREATE UNIQUE INDEX IF NOT EXISTS ux_tenants_tenant_code_lower - ON tenants (lower(tenant_code)); - -CREATE UNIQUE INDEX IF NOT EXISTS ux_tenants_tenant_uuid - ON tenants (tenant_uuid); - --- 2) 租户后台账号 -CREATE TABLE IF NOT EXISTS admin_users ( - id BIGSERIAL PRIMARY KEY, - tenant_id BIGINT NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, - username VARCHAR(64) NOT NULL, - password_hash TEXT NOT NULL, - role VARCHAR(32) NOT NULL DEFAULT 'admin', - status INT2 NOT NULL DEFAULT 0, - created_at TIMESTAMPTZ NOT NULL DEFAULT now(), - updated_at TIMESTAMPTZ NOT NULL DEFAULT now() +-- 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 UNIQUE INDEX IF NOT EXISTS ux_admin_users_tenant_username - ON admin_users (tenant_id, lower(username)); +CREATE INDEX IF NOT EXISTS idx_tenants_user_id ON tenants(user_id); --- 3) 用户(微信 openid 用户) -CREATE TABLE IF NOT EXISTS users ( - id BIGSERIAL PRIMARY KEY, - tenant_id BIGINT NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, - created_at TIMESTAMPTZ NOT NULL DEFAULT now(), - updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), - deleted_at TIMESTAMPTZ, - status INT2 NOT NULL DEFAULT 0, - open_id VARCHAR(128) NOT NULL, - username VARCHAR(128) NOT NULL DEFAULT '', - avatar TEXT, - metas JSONB NOT NULL DEFAULT '{}'::jsonb, - auth_token JSONB NOT NULL DEFAULT '{}'::jsonb, - balance BIGINT NOT NULL DEFAULT 0 +-- 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) ); -CREATE UNIQUE INDEX IF NOT EXISTS ux_users_tenant_openid - ON users (tenant_id, open_id); - -CREATE INDEX IF NOT EXISTS ix_users_tenant_id - ON users (tenant_id, id); - --- 4) 媒体 -CREATE TABLE IF NOT EXISTS medias ( - id BIGSERIAL PRIMARY KEY, - tenant_id BIGINT NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, - created_at TIMESTAMPTZ NOT NULL DEFAULT now(), - name VARCHAR(255) NOT NULL DEFAULT '', - mime_type VARCHAR(128) NOT NULL DEFAULT '', - size BIGINT NOT NULL DEFAULT 0, - path VARCHAR(512) NOT NULL DEFAULT '', - metas JSONB NOT NULL DEFAULT '{}'::jsonb, - hash VARCHAR(64) NOT NULL DEFAULT '' +-- 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 -- 软删除时间 ); --- 租户内按 md5 去重 -CREATE UNIQUE INDEX IF NOT EXISTS ux_medias_tenant_hash - ON medias (tenant_id, hash); +CREATE INDEX IF NOT EXISTS idx_contents_tenant_id ON contents(tenant_id); -CREATE INDEX IF NOT EXISTS ix_medias_tenant_id - ON medias (tenant_id, id); - --- 5) 曲谱/内容 -CREATE TABLE IF NOT EXISTS posts ( - id BIGSERIAL PRIMARY KEY, - tenant_id BIGINT NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, - created_at TIMESTAMPTZ NOT NULL DEFAULT now(), - updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), - deleted_at TIMESTAMPTZ, - status INT2 NOT NULL DEFAULT 0, - title VARCHAR(128) NOT NULL, - head_images JSONB NOT NULL DEFAULT '[]'::jsonb, - description VARCHAR(256) NOT NULL DEFAULT '', - content TEXT NOT NULL DEFAULT '', - price BIGINT NOT NULL DEFAULT 0, - discount INT2 NOT NULL DEFAULT 100, - views BIGINT NOT NULL DEFAULT 0, - likes BIGINT NOT NULL DEFAULT 0, - tags JSONB NOT NULL DEFAULT '[]'::jsonb, - assets JSONB NOT NULL DEFAULT '[]'::jsonb +-- 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 ix_posts_tenant_status_deleted - ON posts (tenant_id, status, deleted_at); +CREATE INDEX IF NOT EXISTS idx_media_assets_hash ON media_assets (hash); --- 6) 授权关系(购买/赠送) -CREATE TABLE IF NOT EXISTS user_posts ( - id BIGSERIAL PRIMARY KEY, - tenant_id BIGINT NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, - created_at TIMESTAMPTZ NOT NULL DEFAULT now(), - updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), - user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, - post_id BIGINT NOT NULL REFERENCES posts(id) ON DELETE CASCADE, - price BIGINT NOT NULL DEFAULT 0 +-- 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() -- 更新时间 ); -CREATE UNIQUE INDEX IF NOT EXISTS ux_user_posts_tenant_user_post - ON user_posts (tenant_id, user_id, post_id); - -CREATE INDEX IF NOT EXISTS ix_user_posts_tenant_user - ON user_posts (tenant_id, user_id, id); - --- 7) 订单(仅余额) -CREATE TABLE IF NOT EXISTS orders ( - id BIGSERIAL PRIMARY KEY, - tenant_id BIGINT NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, - created_at TIMESTAMPTZ NOT NULL DEFAULT now(), - updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), - order_no VARCHAR(64) NOT NULL, - price BIGINT NOT NULL DEFAULT 0, - discount INT2 NOT NULL DEFAULT 100, - currency VARCHAR(10) NOT NULL DEFAULT 'CNY', - payment_method VARCHAR(50) NOT NULL DEFAULT 'balance', - post_id BIGINT NOT NULL REFERENCES posts(id) ON DELETE CASCADE, - user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, - status INT2 NOT NULL DEFAULT 0, - meta JSONB NOT NULL DEFAULT '{}'::jsonb +-- 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) ); -CREATE UNIQUE INDEX IF NOT EXISTS ux_orders_tenant_order_no - ON orders (tenant_id, order_no); +-- 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) +); -CREATE INDEX IF NOT EXISTS ix_orders_tenant_user - ON orders (tenant_id, user_id, 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; -