feat: sync initial schema migration

This commit is contained in:
2026-01-09 14:01:09 +08:00
parent b88fec229c
commit 0fdd071306
5 changed files with 449 additions and 98 deletions

View File

@@ -1,9 +1,360 @@
-- +goose Up
-- +goose StatementBegin
SELECT 'up SQL query';
-- Users
CREATE TABLE IF NOT EXISTS users(
id bigserial PRIMARY KEY, -- 主键ID自增用途用户唯一标识约束PK/不可为空
username varchar(255) NOT NULL UNIQUE, -- 用户名:登录账号;用途:登录/展示;约束:唯一/非空/长度<=255
password varchar(255) NOT NULL, -- 密码:加密存储;用途:登录校验;约束:非空/不得明文
roles text[] DEFAULT '{user}', -- 角色:权限集合;用途:鉴权/授权;约束:枚举值数组/默认{user}
status varchar(50) DEFAULT 'active', -- 状态账号状态用途禁用控制约束active/inactive/banned/默认active
metas jsonb DEFAULT '{}', -- 元数据扩展信息用途灵活扩展约束JSON对象/默认{}
balance bigint DEFAULT 0, -- 余额:全局可用余额;用途:钱包/支付;约束:最小货币单位/默认0
balance_frozen bigint DEFAULT 0, -- 冻结余额:冻结资金;用途:风控/结算;约束:最小货币单位/默认0
verified_at timestamp with time zone, -- 实名认证时间:通过时间;用途:风控展示;约束:可空
nickname varchar(255) DEFAULT '', -- 昵称:展示名称;用途:前台展示;约束:长度<=255/可空
avatar varchar(512) DEFAULT '', -- 头像URL地址用途展示约束长度<=512/可空
gender varchar(32) DEFAULT 'secret', -- 性别用户性别用途资料展示约束male/female/secret/默认secret
bio varchar(512) DEFAULT '', -- 简介:用户简介;用途:个人主页;约束:长度<=512/可空
birthday date, -- 生日YYYY-MM-DD用途资料展示约束可空
location jsonb DEFAULT '{}', -- 位置省市信息用途资料展示约束JSON对象/默认{}
points bigint DEFAULT 0, -- 积分:用户积分;用途:积分体系;约束:>=0/默认0
phone varchar(32) DEFAULT '', -- 手机号:登录/验证用途OTP登录约束长度<=32/可空
is_real_name_verified boolean DEFAULT FALSE, -- 实名认证:是否认证;用途:风控/展示;约束:布尔/默认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自增用途租户唯一标识约束PK/不可为空
user_id bigint NOT NULL, -- 创建者ID关联users.id用途租户归属约束非空
code varchar(64) NOT NULL UNIQUE, -- 租户代码唯一编码用途URL/路由;约束:唯一/非空/长度<=64
uuid uuid NOT NULL, -- UUID全局唯一用途外部标识约束非空
name varchar(128) NOT NULL, -- 租户名称:展示名;用途:后台展示;约束:非空/长度<=128
status varchar(64) NOT NULL, -- 状态租户状态用途启停控制约束pending_verify/verified/banned
config jsonb DEFAULT '{}', -- 配置租户配置用途功能开关约束JSON对象/默认{}
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自增用途成员关系唯一标识约束PK/不可为空
tenant_id bigint NOT NULL, -- 租户ID关联tenants.id用途多租户隔离约束非空
user_id bigint NOT NULL, -- 用户ID关联users.id用途成员归属约束非空
role text[] DEFAULT '{member}', -- 角色成员角色用途权限控制约束member/tenant_admin/默认member
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自增用途内容唯一标识约束PK/不可为空
tenant_id bigint NOT NULL, -- 租户ID多租户隔离用途数据隔离约束非空
user_id bigint NOT NULL, -- 用户ID内容作者用途归属约束非空
title varchar(255) NOT NULL, -- 标题:内容标题;用途:列表展示/搜索;约束:非空/长度<=255
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, -- 试看秒数:试看时长;用途:试看控制;约束:>=0/默认60
preview_downloadable boolean DEFAULT FALSE, -- 试看下载:是否允许下载;用途:权限控制;约束:布尔/默认false
published_at timestamp with time zone, -- 发布时间:发布时刻;用途:排序/展示;约束:可空
summary varchar(256) DEFAULT '', -- 简介:列表摘要;用途:卡片展示;约束:长度<=256/可空
tags jsonb DEFAULT '[]', -- 标签标签列表用途筛选约束JSON数组/默认[]
body text DEFAULT '', -- 内容正文:图文内容;用途:详情页;约束:可空
genre varchar(64) DEFAULT '', -- 流派:内容类型;用途:分类筛选;约束:长度<=64/可空
views int DEFAULT 0, -- 浏览量:浏览计数;用途:热度排序;约束:>=0/默认0
likes int DEFAULT 0, -- 点赞数:点赞计数;用途:热度排序;约束:>=0/默认0
key varchar(32) DEFAULT '', -- 调性:音乐调性;用途:展示/筛选;约束:长度<=32/可空
is_pinned boolean DEFAULT FALSE, -- 置顶:是否置顶;用途:首页推荐;约束:布尔/默认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 -- 删除时间:软删除;用途:逻辑删除;约束:可空
);
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自增用途资源唯一标识约束PK/不可为空
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, -- 存储提供方:存储类型;用途:访问/迁移;约束:非空
bucket varchar(128) NOT NULL, -- 存储桶:桶名称;用途:定位对象;约束:非空
object_key varchar(512) NOT NULL, -- 对象Key对象路径用途访问对象约束非空/长度<=512
meta jsonb DEFAULT '{}', -- 元数据资源信息用途文件元数据约束JSON对象/默认{}
variant varchar(32) DEFAULT 'main', -- 产物类型:衍生版本;用途:主片/预览约束main/preview/默认main
source_asset_id bigint DEFAULT 0, -- 来源资源ID派生来源用途追溯约束0表示无
hash varchar(64) DEFAULT '', -- 文件哈希:用于去重;用途:秒传/去重;约束:长度<=64/可空
created_at timestamp with time zone DEFAULT NOW(), -- 创建时间记录创建用途审计约束默认now()
updated_at timestamp with time zone DEFAULT NOW(), -- 更新时间记录更新用途审计约束默认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自增用途内容资源关联约束PK/不可为空
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, -- 排序:展示排序;用途:前端排序;约束:>=0/默认0
created_at timestamp with time zone DEFAULT NOW(), -- 创建时间记录创建用途审计约束默认now()
updated_at timestamp with time zone DEFAULT NOW() -- 更新时间记录更新用途审计约束默认now()
);
-- ContentPrices
CREATE TABLE IF NOT EXISTS content_prices(
id bigserial PRIMARY KEY, -- 主键ID自增用途价格记录约束PK/不可为空
tenant_id bigint NOT NULL, -- 租户ID多租户隔离用途数据隔离约束非空
user_id bigint NOT NULL, -- 用户ID内容作者用途归属约束非空
content_id bigint NOT NULL, -- 内容ID关联内容用途定价归属约束非空
currency varchar(16) DEFAULT 'CNY', -- 币种货币类型用途计价约束ISO币种/默认CNY
price_amount bigint NOT NULL, -- 原价金额:最小货币单位;用途:计价;约束:>=0/非空
discount_type varchar(16) DEFAULT 'none', -- 折扣类型折扣规则用途促销约束none/percent/amount
discount_value bigint DEFAULT 0, -- 折扣值:折扣额度;用途:促销;约束:>=0/默认0
discount_start_at timestamp with time zone, -- 折扣开始:起始时间;用途:促销;约束:可空
discount_end_at timestamp with time zone, -- 折扣结束:结束时间;用途:促销;约束:可空
created_at timestamp with time zone DEFAULT NOW(), -- 创建时间记录创建用途审计约束默认now()
updated_at timestamp with time zone DEFAULT NOW(), -- 更新时间记录更新用途审计约束默认now()
UNIQUE (tenant_id, content_id) -- 约束:同租户同内容仅一条价格记录
);
-- ContentAccess
CREATE TABLE IF NOT EXISTS content_access(
id bigserial PRIMARY KEY, -- 主键ID自增用途内容权益记录约束PK/不可为空
tenant_id bigint NOT NULL, -- 租户ID多租户隔离用途数据隔离约束非空
user_id bigint NOT NULL, -- 用户ID权益持有者用途权限判断约束非空
content_id bigint NOT NULL, -- 内容ID关联内容用途权限判断约束非空
order_id bigint DEFAULT 0, -- 订单ID关联订单用途溯源约束0表示无
status varchar(16) DEFAULT 'active', -- 权益状态使用状态用途权限判断约束active/revoked
revoked_at timestamp with time zone, -- 撤销时间:撤销时刻;用途:审计;约束:可空
created_at timestamp with time zone DEFAULT NOW(), -- 创建时间记录创建用途审计约束默认now()
updated_at timestamp with time zone DEFAULT NOW(), -- 更新时间记录更新用途审计约束默认now()
UNIQUE (tenant_id, user_id, content_id) -- 约束:同用户同内容唯一权益
);
-- Orders
CREATE TABLE IF NOT EXISTS orders(
id bigserial PRIMARY KEY, -- 主键ID自增用途订单唯一标识约束PK/不可为空
tenant_id bigint NOT NULL, -- 租户ID多租户隔离用途数据隔离约束非空
user_id bigint NOT NULL, -- 用户ID下单用户用途归属约束非空
type varchar(32) DEFAULT 'content_purchase', -- 订单类型业务类型用途业务分支约束content_purchase/recharge
status varchar(32) DEFAULT 'created', -- 订单状态支付状态用途流程控制约束created/paid/refunded/closed
currency varchar(16) DEFAULT 'CNY', -- 币种货币类型用途计价约束ISO币种/默认CNY
amount_original bigint NOT NULL, -- 原价金额:订单原价;用途:结算;约束:>=0/非空
amount_discount bigint NOT NULL, -- 优惠金额:优惠合计;用途:结算;约束:>=0/非空
amount_paid bigint NOT NULL, -- 实付金额:实际支付;用途:结算;约束:>=0/非空
snapshot jsonb DEFAULT '{}', -- 订单快照:下单快照;用途:审计/对账约束JSON对象/默认{}
idempotency_key varchar(128) NOT NULL, -- 幂等键:请求幂等;用途:重复请求保护;约束:非空/长度<=128
paid_at timestamp with time zone, -- 支付时间:支付成功时间;用途:对账;约束:可空
refunded_at timestamp with time zone, -- 退款时间:退款时间;用途:对账;约束:可空
refund_forced boolean DEFAULT FALSE, -- 强制退款:是否强制;用途:风控;约束:布尔/默认false
refund_operator_user_id bigint DEFAULT 0, -- 退款操作人操作者ID用途审计约束0表示无
refund_reason varchar(255) DEFAULT '', -- 退款原因:原因说明;用途:审计;约束:长度<=255/可空
created_at timestamp with time zone DEFAULT NOW(), -- 创建时间记录创建用途审计约束默认now()
updated_at timestamp with time zone DEFAULT NOW(), -- 更新时间记录更新用途审计约束默认now()
coupon_id bigint DEFAULT 0 -- 优惠券ID使用的券用途优惠计算约束0表示未使用
);
-- OrderItems
CREATE TABLE IF NOT EXISTS order_items(
id bigserial PRIMARY KEY, -- 主键ID自增用途订单项唯一标识约束PK/不可为空
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, -- 实付金额:行实付;用途:结算;约束:>=0/非空
snapshot jsonb DEFAULT '{}', -- 内容快照下单内容快照用途审计约束JSON对象/默认{}
created_at timestamp with time zone DEFAULT NOW(), -- 创建时间记录创建用途审计约束默认now()
updated_at timestamp with time zone DEFAULT NOW() -- 更新时间记录更新用途审计约束默认now()
);
-- TenantLedgers
CREATE TABLE IF NOT EXISTS tenant_ledgers(
id bigserial PRIMARY KEY, -- 主键ID自增用途流水唯一标识约束PK/不可为空
tenant_id bigint NOT NULL, -- 租户ID多租户隔离用途数据隔离约束非空
user_id bigint NOT NULL, -- 用户ID租户主或操作者用途归属约束非空
order_id bigint DEFAULT 0, -- 订单ID关联订单用途溯源约束0表示无
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人工操作人用途审计约束0表示无
biz_ref_type varchar(32) DEFAULT '', -- 业务引用类型:外部关联;用途:对账;约束:长度<=32
biz_ref_id bigint DEFAULT 0, -- 业务引用ID外部关联用途对账约束0表示无
created_at timestamp with time zone DEFAULT NOW(), -- 创建时间记录创建用途审计约束默认now()
updated_at timestamp with time zone DEFAULT NOW() -- 更新时间记录更新用途审计约束默认now()
);
-- TenantInvites
CREATE TABLE IF NOT EXISTS tenant_invites(
id bigserial PRIMARY KEY, -- 主键ID自增用途邀请记录约束PK/不可为空
tenant_id bigint NOT NULL, -- 租户ID多租户隔离用途数据隔离约束非空
user_id bigint NOT NULL, -- 用户ID创建人用途溯源约束非空
code varchar(64) NOT NULL, -- 邀请码:邀请码;用途:邀请加入;约束:非空/长度<=64
status varchar(32) DEFAULT 'active', -- 邀请状态是否可用用途控制邀请约束active/disabled/expired
max_uses int NOT NULL, -- 最大次数:可用次数;用途:限制;约束:>0/非空
used_count int DEFAULT 0, -- 已使用:已用次数;用途:限制;约束:>=0/默认0
expires_at timestamp with time zone, -- 过期时间:失效时间;用途:控制;约束:可空
disabled_at timestamp with time zone, -- 禁用时间:禁用时刻;用途:审计;约束:可空
disabled_operator_user_id bigint DEFAULT 0, -- 禁用操作人操作者用途审计约束0表示无
remark varchar(255) DEFAULT '', -- 备注:说明;用途:审计;约束:长度<=255/可空
created_at timestamp with time zone DEFAULT NOW(), -- 创建时间记录创建用途审计约束默认now()
updated_at timestamp with time zone DEFAULT NOW() -- 更新时间记录更新用途审计约束默认now()
);
-- TenantJoinRequests
CREATE TABLE IF NOT EXISTS tenant_join_requests(
id bigserial PRIMARY KEY, -- 主键ID自增用途入驻申请约束PK/不可为空
tenant_id bigint NOT NULL, -- 租户ID多租户隔离用途数据隔离约束非空
user_id bigint NOT NULL, -- 用户ID申请人用途溯源约束非空
status varchar(32) DEFAULT 'pending', -- 申请状态审核状态用途审核流程约束pending/approved/rejected
reason varchar(255) NOT NULL, -- 申请原因:申请说明;用途:审核;约束:非空/长度<=255
decided_at timestamp with time zone, -- 处理时间:审核时间;用途:审计;约束:可空
decided_operator_user_id bigint DEFAULT 0, -- 处理人ID操作者用途审计约束0表示无
decided_reason varchar(255) DEFAULT '', -- 处理说明:审核意见;用途:审计;约束:长度<=255/可空
created_at timestamp with time zone DEFAULT NOW(), -- 创建时间记录创建用途审计约束默认now()
updated_at timestamp with time zone DEFAULT NOW() -- 更新时间记录更新用途审计约束默认now()
);
-- Comments
CREATE TABLE IF NOT EXISTS comments(
id bigserial PRIMARY KEY, -- 主键ID自增用途评论唯一标识约束PK/不可为空
tenant_id bigint NOT NULL, -- 租户ID多租户隔离用途数据隔离约束非空
user_id bigint NOT NULL, -- 用户ID评论者用途归属约束非空
content_id bigint NOT NULL, -- 内容ID关联内容用途评论归属约束非空
reply_to bigint DEFAULT 0, -- 回复评论ID0为一级用途楼中楼约束0表示无
content text NOT NULL, -- 评论内容:评论文本;用途:展示;约束:非空
likes int DEFAULT 0, -- 点赞数:点赞计数;用途:排序;约束:>=0/默认0
created_at timestamp with time zone DEFAULT NOW(), -- 创建时间记录创建用途审计约束默认now()
updated_at timestamp with time zone DEFAULT NOW(), -- 更新时间记录更新用途审计约束默认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自增用途内容互动约束PK/不可为空
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(), -- 创建时间记录创建用途审计约束默认now()
UNIQUE (user_id, content_id, type) -- 约束:同一用户同类型仅一次
);
-- User Comment Actions (Like)
CREATE TABLE IF NOT EXISTS user_comment_actions(
id bigserial PRIMARY KEY, -- 主键ID自增用途评论互动约束PK/不可为空
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(), -- 创建时间记录创建用途审计约束默认now()
UNIQUE (user_id, comment_id, type) -- 约束:同一用户同类型仅一次
);
-- Payout Accounts
CREATE TABLE IF NOT EXISTS payout_accounts(
id bigserial PRIMARY KEY, -- 主键ID自增用途提现账户约束PK/不可为空
tenant_id bigint NOT NULL, -- 租户ID多租户隔离用途数据隔离约束非空
user_id bigint NOT NULL, -- 用户ID账户持有人用途归属约束非空
type varchar(32) NOT NULL, -- 类型账户类型用途打款方式约束bank/alipay
name varchar(128) NOT NULL, -- 账户名称:开户行/支付宝名称;用途:打款;约束:非空/长度<=128
account varchar(128) NOT NULL, -- 账号:银行卡/支付宝账号;用途:打款;约束:非空/长度<=128
realname varchar(128) NOT NULL, -- 真实姓名:收款人;用途:打款;约束:非空/长度<=128
created_at timestamp with time zone DEFAULT NOW(), -- 创建时间记录创建用途审计约束默认now()
updated_at timestamp with time zone DEFAULT NOW() -- 更新时间记录更新用途审计约束默认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自增用途通知唯一标识约束PK/不可为空
user_id bigint NOT NULL, -- 用户ID接收者用途归属约束非空
tenant_id bigint DEFAULT 0, -- 租户ID来源租户用途来源标识约束0表示无
type varchar(32) NOT NULL, -- 类型通知类型用途分类展示约束system/order/audit/interaction
title varchar(255) NOT NULL, -- 标题:通知标题;用途:展示;约束:非空/长度<=255
content text NOT NULL, -- 内容:通知内容;用途:展示;约束:非空
is_read boolean DEFAULT FALSE, -- 已读:是否已读;用途:状态管理;约束:布尔/默认false
created_at timestamp with time zone DEFAULT NOW() -- 创建时间记录创建用途审计约束默认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自增用途优惠券唯一标识约束PK/不可为空
tenant_id bigint NOT NULL DEFAULT 0, -- 租户ID适用租户用途范围控制约束0表示全局
title varchar(255) NOT NULL, -- 标题:优惠券标题;用途:展示;约束:非空/长度<=255
description text, -- 描述:优惠券说明;用途:展示;约束:可空
type varchar(32) NOT NULL, -- 类型优惠类型用途优惠计算约束fix_amount/discount
value bigint NOT NULL, -- 面值:优惠金额/折扣;用途:优惠计算;约束:>0
min_order_amount bigint NOT NULL DEFAULT 0, -- 最低金额:使用门槛;用途:限制;约束:>=0/默认0
max_discount bigint, -- 最高折扣:上限金额;用途:限制;约束:可空
total_quantity integer NOT NULL DEFAULT 0, -- 总量:发行数量;用途:发放控制;约束:>=0/默认0
used_quantity integer NOT NULL DEFAULT 0, -- 已用:已使用数量;用途:核销统计;约束:>=0/默认0
start_at timestamptz, -- 开始时间:生效时间;用途:有效期控制;约束:可空
end_at timestamptz, -- 结束时间:失效时间;用途:有效期控制;约束:可空
created_at timestamptz NOT NULL DEFAULT NOW(), -- 创建时间记录创建用途审计约束默认now()
updated_at timestamptz NOT NULL DEFAULT NOW() -- 更新时间记录更新用途审计约束默认now()
);
CREATE INDEX IF NOT EXISTS idx_coupons_tenant_id ON coupons(tenant_id);
-- UserCoupons
CREATE TABLE IF NOT EXISTS user_coupons(
id bigserial PRIMARY KEY, -- 主键ID自增用途用户券唯一标识约束PK/不可为空
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() -- 创建时间记录创建用途审计约束默认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);
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
SELECT 'down SQL query';
DROP TABLE IF EXISTS user_coupons;
DROP TABLE IF EXISTS coupons;
DROP TABLE IF EXISTS notifications;
DROP TABLE IF EXISTS payout_accounts;
DROP TABLE IF EXISTS user_comment_actions;
DROP TABLE IF EXISTS user_content_actions;
DROP TABLE IF EXISTS comments;
DROP TABLE IF EXISTS tenant_join_requests;
DROP TABLE IF EXISTS tenant_invites;
DROP TABLE IF EXISTS tenant_ledgers;
DROP TABLE IF EXISTS order_items;
DROP TABLE IF EXISTS orders;
DROP TABLE IF EXISTS content_access;
DROP TABLE IF EXISTS content_prices;
DROP TABLE IF EXISTS content_assets;
DROP TABLE IF EXISTS media_assets;
DROP TABLE IF EXISTS contents;
DROP TABLE IF EXISTS tenant_users;
DROP TABLE IF EXISTS tenants;
DROP TABLE IF EXISTS users;
-- +goose StatementEnd

View File

@@ -40,9 +40,9 @@ type Content struct {
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:timestamp with time zone" json:"deleted_at"`
Key string `gorm:"column:key;type:character varying(32);comment:Musical key/tone" json:"key"` // Musical key/tone
IsPinned bool `gorm:"column:is_pinned;type:boolean;comment:Whether content is pinned/featured" json:"is_pinned"` // Whether content is pinned/featured
Comments []*Comment `gorm:"foreignKey:ContentID;references:ID" json:"comments,omitempty"`
Author *User `gorm:"foreignKey:UserID;references:ID" json:"author,omitempty"`
ContentAssets []*ContentAsset `gorm:"foreignKey:ContentID;references:ID" json:"content_assets,omitempty"`
Comments []*Comment `gorm:"foreignKey:ContentID;references:ID" json:"comments,omitempty"`
}
// Quick operations without importing query package

View File

@@ -46,12 +46,6 @@ func newContent(db *gorm.DB, opts ...gen.DOOption) contentQuery {
_contentQuery.DeletedAt = field.NewField(tableName, "deleted_at")
_contentQuery.Key = field.NewString(tableName, "key")
_contentQuery.IsPinned = field.NewBool(tableName, "is_pinned")
_contentQuery.Comments = contentQueryHasManyComments{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("Comments", "Comment"),
}
_contentQuery.Author = contentQueryBelongsToAuthor{
db: db.Session(&gorm.Session{}),
@@ -64,6 +58,12 @@ func newContent(db *gorm.DB, opts ...gen.DOOption) contentQuery {
RelationField: field.NewRelation("ContentAssets", "ContentAsset"),
}
_contentQuery.Comments = contentQueryHasManyComments{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("Comments", "Comment"),
}
_contentQuery.fillFieldMap()
return _contentQuery
@@ -94,12 +94,12 @@ type contentQuery struct {
DeletedAt field.Field
Key field.String // Musical key/tone
IsPinned field.Bool // Whether content is pinned/featured
Comments contentQueryHasManyComments
Author contentQueryBelongsToAuthor
Author contentQueryBelongsToAuthor
ContentAssets contentQueryHasManyContentAssets
Comments contentQueryHasManyComments
fieldMap map[string]field.Expr
}
@@ -195,104 +195,23 @@ func (c *contentQuery) fillFieldMap() {
func (c contentQuery) clone(db *gorm.DB) contentQuery {
c.contentQueryDo.ReplaceConnPool(db.Statement.ConnPool)
c.Comments.db = db.Session(&gorm.Session{Initialized: true})
c.Comments.db.Statement.ConnPool = db.Statement.ConnPool
c.Author.db = db.Session(&gorm.Session{Initialized: true})
c.Author.db.Statement.ConnPool = db.Statement.ConnPool
c.ContentAssets.db = db.Session(&gorm.Session{Initialized: true})
c.ContentAssets.db.Statement.ConnPool = db.Statement.ConnPool
c.Comments.db = db.Session(&gorm.Session{Initialized: true})
c.Comments.db.Statement.ConnPool = db.Statement.ConnPool
return c
}
func (c contentQuery) replaceDB(db *gorm.DB) contentQuery {
c.contentQueryDo.ReplaceDB(db)
c.Comments.db = db.Session(&gorm.Session{})
c.Author.db = db.Session(&gorm.Session{})
c.ContentAssets.db = db.Session(&gorm.Session{})
c.Comments.db = db.Session(&gorm.Session{})
return c
}
type contentQueryHasManyComments struct {
db *gorm.DB
field.RelationField
}
func (a contentQueryHasManyComments) Where(conds ...field.Expr) *contentQueryHasManyComments {
if len(conds) == 0 {
return &a
}
exprs := make([]clause.Expression, 0, len(conds))
for _, cond := range conds {
exprs = append(exprs, cond.BeCond().(clause.Expression))
}
a.db = a.db.Clauses(clause.Where{Exprs: exprs})
return &a
}
func (a contentQueryHasManyComments) WithContext(ctx context.Context) *contentQueryHasManyComments {
a.db = a.db.WithContext(ctx)
return &a
}
func (a contentQueryHasManyComments) Session(session *gorm.Session) *contentQueryHasManyComments {
a.db = a.db.Session(session)
return &a
}
func (a contentQueryHasManyComments) Model(m *Content) *contentQueryHasManyCommentsTx {
return &contentQueryHasManyCommentsTx{a.db.Model(m).Association(a.Name())}
}
func (a contentQueryHasManyComments) Unscoped() *contentQueryHasManyComments {
a.db = a.db.Unscoped()
return &a
}
type contentQueryHasManyCommentsTx struct{ tx *gorm.Association }
func (a contentQueryHasManyCommentsTx) Find() (result []*Comment, err error) {
return result, a.tx.Find(&result)
}
func (a contentQueryHasManyCommentsTx) Append(values ...*Comment) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Append(targetValues...)
}
func (a contentQueryHasManyCommentsTx) Replace(values ...*Comment) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Replace(targetValues...)
}
func (a contentQueryHasManyCommentsTx) Delete(values ...*Comment) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Delete(targetValues...)
}
func (a contentQueryHasManyCommentsTx) Clear() error {
return a.tx.Clear()
}
func (a contentQueryHasManyCommentsTx) Count() int64 {
return a.tx.Count()
}
func (a contentQueryHasManyCommentsTx) Unscoped() *contentQueryHasManyCommentsTx {
a.tx = a.tx.Unscoped()
return &a
}
type contentQueryBelongsToAuthor struct {
db *gorm.DB
@@ -455,6 +374,87 @@ func (a contentQueryHasManyContentAssetsTx) Unscoped() *contentQueryHasManyConte
return &a
}
type contentQueryHasManyComments struct {
db *gorm.DB
field.RelationField
}
func (a contentQueryHasManyComments) Where(conds ...field.Expr) *contentQueryHasManyComments {
if len(conds) == 0 {
return &a
}
exprs := make([]clause.Expression, 0, len(conds))
for _, cond := range conds {
exprs = append(exprs, cond.BeCond().(clause.Expression))
}
a.db = a.db.Clauses(clause.Where{Exprs: exprs})
return &a
}
func (a contentQueryHasManyComments) WithContext(ctx context.Context) *contentQueryHasManyComments {
a.db = a.db.WithContext(ctx)
return &a
}
func (a contentQueryHasManyComments) Session(session *gorm.Session) *contentQueryHasManyComments {
a.db = a.db.Session(session)
return &a
}
func (a contentQueryHasManyComments) Model(m *Content) *contentQueryHasManyCommentsTx {
return &contentQueryHasManyCommentsTx{a.db.Model(m).Association(a.Name())}
}
func (a contentQueryHasManyComments) Unscoped() *contentQueryHasManyComments {
a.db = a.db.Unscoped()
return &a
}
type contentQueryHasManyCommentsTx struct{ tx *gorm.Association }
func (a contentQueryHasManyCommentsTx) Find() (result []*Comment, err error) {
return result, a.tx.Find(&result)
}
func (a contentQueryHasManyCommentsTx) Append(values ...*Comment) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Append(targetValues...)
}
func (a contentQueryHasManyCommentsTx) Replace(values ...*Comment) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Replace(targetValues...)
}
func (a contentQueryHasManyCommentsTx) Delete(values ...*Comment) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Delete(targetValues...)
}
func (a contentQueryHasManyCommentsTx) Clear() error {
return a.tx.Clear()
}
func (a contentQueryHasManyCommentsTx) Count() int64 {
return a.tx.Count()
}
func (a contentQueryHasManyCommentsTx) Unscoped() *contentQueryHasManyCommentsTx {
a.tx = a.tx.Unscoped()
return &a
}
type contentQueryDo struct{ gen.DO }
func (c contentQueryDo) Debug() *contentQueryDo {

View File

@@ -34,7 +34,7 @@ type MediaAsset struct {
CreatedAt time.Time `gorm:"column:created_at;type:timestamp with time zone;default:now()" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at;type:timestamp with time zone;default:now()" json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:timestamp with time zone" json:"deleted_at"`
Hash string `gorm:"column:hash;type:character varying(64);comment:File SHA-256 hash" json:"hash"` // File SHA-256 hash
Hash string `gorm:"column:hash;type:character varying(64);comment:文件 MD5 哈希" json:"hash"` // 文件 MD5 哈希
}
// Quick operations without importing query package

View File

@@ -64,7 +64,7 @@ type mediaAssetQuery struct {
CreatedAt field.Time
UpdatedAt field.Time
DeletedAt field.Field
Hash field.String // File SHA-256 hash
Hash field.String // 文件 MD5 哈希
fieldMap map[string]field.Expr
}