-- ====================
-- region 日志
-- ====================

-- logs_request
drop table if exists logs_request cascade;
create table logs_request (
    id        int generated by default as identity primary key,
    ip        inet        not null,
    ua        text        not null,
    user_id   int,
    client_id int,
    method    text        not null,
    path      text        not null,
    status    smallint    not null,
    error     text,
    time      timestamptz not null,
    latency   text        not null
);

-- logs_access表字段注释
comment on table logs_request is '访问日志表';
comment on column logs_request.id is '访问日志ID';
comment on column logs_request.ip is 'IP地址';
comment on column logs_request.ua is '用户代理';
comment on column logs_request.user_id is '用户ID';
comment on column logs_request.client_id is '客户端ID';
comment on column logs_request.method is '请求方法';
comment on column logs_request.path is '请求路径';
comment on column logs_request.status is '响应状态码';
comment on column logs_request.error is '错误信息';
comment on column logs_request.time is '请求时间';
comment on column logs_request.latency is '请求延迟';

-- logs_login

drop table if exists logs_login cascade;
create table logs_login (
    id            int generated by default as identity primary key,
    ip            inet        not null,
    ua            text        not null,
    grant_type    text        not null,
    password_type text        not null,
    success       bool        not null,
    user_id       int,
    time          timestamptz not null
);

-- logs_login表字段注释
comment on table logs_login is '登录日志表';
comment on column logs_login.id is '登录日志ID';
comment on column logs_login.user_id is '用户ID';
comment on column logs_login.ip is 'IP地址';
comment on column logs_login.ua is '用户代理';
comment on column logs_login.grant_type is '授权类型：authorization_code-授权码模式，client_credentials-客户端凭证模式，refresh_token-刷新令牌模式，password-密码模式';
comment on column logs_login.password_type is '密码模式子授权类型：password-账号密码，phone_code-手机验证码，email_code-邮箱验证码';
comment on column logs_login.success is '登录是否成功';
comment on column logs_login.time is '登录时间';

-- logs_user_usage
drop table if exists logs_user_usage cascade;
create table logs_user_usage (
    id          int generated by default as identity primary key,
    user_id     int         not null,
    resource_id int         not null,
    batch_no    text        not null,
    count       int         not null,
    prov        text,
    city        text,
    isp         text,
    ip          inet        not null,
    time        timestamptz not null
);
create index idx_logs_user_usage_user_id on logs_user_usage (user_id);
create index idx_logs_user_usage_resource_id on logs_user_usage (resource_id);
create index idx_logs_user_usage_batch_no on logs_user_usage (batch_no);

-- logs_user_usage表字段注释
comment on table logs_user_usage is '用户使用日志表';
comment on column logs_user_usage.id is '日志ID';
comment on column logs_user_usage.user_id is '用户ID';
comment on column logs_user_usage.resource_id is '套餐ID';
comment on column logs_user_usage.batch_no is '批次编号';
comment on column logs_user_usage.count is '数量';
comment on column logs_user_usage.prov is '省份';
comment on column logs_user_usage.city is '城市';
comment on column logs_user_usage.isp is '运营商';
comment on column logs_user_usage.ip is 'IP地址';
comment on column logs_user_usage.time is '提取时间';

-- logs_user_bandwidth
drop table if exists logs_user_bandwidth cascade;
create table logs_user_bandwidth (
    id        int generated by default as identity primary key,
    user_id   int         not null,
    bandwidth int         not null,
    time      timestamptz not null
);
create index idx_logs_user_bandwidth_user_id on logs_user_bandwidth (user_id);
create index idx_logs_user_bandwidth_time on logs_user_bandwidth (time);

-- logs_user_bandwidth表字段注释
comment on table logs_user_bandwidth is '用户带宽日志表';
comment on column logs_user_bandwidth.id is '日志ID';
comment on column logs_user_bandwidth.user_id is '用户ID';
comment on column logs_user_bandwidth.bandwidth is '带宽使用量(KB)';
comment on column logs_user_bandwidth.time is '记录时间';

-- endregion

-- ====================
-- region 系统信息
-- ====================

-- announcement
drop table if exists announcement cascade;
create table announcement (
    id         int generated by default as identity primary key,
    title      text not null,
    content    text,
    type       int  not null default 1,
    pin        bool not null default false,
    status     int  not null default 1,
    sort       int  not null default 0,
    created_at timestamptz default current_timestamp,
    updated_at timestamptz default current_timestamp,
    deleted_at timestamptz
);
create index idx_announcement_type on announcement (type) where deleted_at is null;
create index idx_announcement_pin on announcement (pin) where deleted_at is null;
create index idx_announcement_created_at on announcement (created_at) where deleted_at is null;

-- announcement表字段注释
comment on table announcement is '公告表';
comment on column announcement.id is '公告ID';
comment on column announcement.title is '公告标题';
comment on column announcement.content is '公告内容';
comment on column announcement.type is '公告类型：1-普通公告';
comment on column announcement.status is '公告状态：0-禁用，1-正常';
comment on column announcement.pin is '是否置顶';
comment on column announcement.sort is '公告排序';
comment on column announcement.created_at is '创建时间';
comment on column announcement.updated_at is '更新时间';
comment on column announcement.deleted_at is '删除时间';

-- inquiry
drop table if exists inquiry cascade;
create table inquiry (
    id         int generated by default as identity primary key,
    company    text,
    name       text,
    phone      text,
    email      text,
    content    text,
    status     int not null default 0,
    remark     text,
    created_at timestamptz default current_timestamp,
    updated_at timestamptz default current_timestamp,
    deleted_at timestamptz
);
create index idx_inquiry_phone on inquiry (phone) where deleted_at is null;
create index idx_inquiry_status on inquiry (status) where deleted_at is null;
create index idx_inquiry_created_at on inquiry (created_at) where deleted_at is null;

-- inquiry表字段注释
comment on table inquiry is '用户咨询表';
comment on column inquiry.id is '咨询ID';
comment on column inquiry.name is '联系人姓名';
comment on column inquiry.phone is '联系电话';
comment on column inquiry.email is '联系邮箱';
comment on column inquiry.company is '公司名称';
comment on column inquiry.content is '咨询内容';
comment on column inquiry.status is '处理状态：0-待处理，1-已处理';
comment on column inquiry.remark is '备注';
comment on column inquiry.created_at is '创建时间';
comment on column inquiry.updated_at is '更新时间';
comment on column inquiry.deleted_at is '删除时间';

-- endregion

-- ====================
-- region 管理员信息
-- ====================

-- admin
drop table if exists admin cascade;
create table admin (
    id            int generated by default as identity primary key,
    username      text not null,
    password      text not null,
    name          text,
    avatar        text,
    phone         text,
    email         text,
    status        int  not null default 1,
    last_login    timestamptz,
    last_login_ip inet,
    last_login_ua text,
    created_at    timestamptz default current_timestamp,
    updated_at    timestamptz default current_timestamp,
    deleted_at    timestamptz
);
create unique index udx_admin_username on admin (username);
create index idx_admin_status on admin (status) where deleted_at is null;
create index idx_admin_created_at on admin (created_at) where deleted_at is null;

-- admin表字段注释
comment on table admin is '管理员表';
comment on column admin.id is '管理员ID';
comment on column admin.username is '用户名';
comment on column admin.password is '密码';
comment on column admin.name is '真实姓名';
comment on column admin.avatar is '头像URL';
comment on column admin.phone is '手机号码';
comment on column admin.email is '邮箱';
comment on column admin.status is '状态：0-禁用，1-正常';
comment on column admin.last_login is '最后登录时间';
comment on column admin.last_login_ip is '最后登录地址';
comment on column admin.last_login_ua is '最后登录代理';
comment on column admin.created_at is '创建时间';
comment on column admin.updated_at is '更新时间';
comment on column admin.deleted_at is '删除时间';

-- admin_role
drop table if exists admin_role cascade;
create table admin_role (
    id          int generated by default as identity primary key,
    name        text not null,
    description text,
    active      bool not null default true,
    sort        int  not null default 0,
    created_at  timestamptz default current_timestamp,
    updated_at  timestamptz default current_timestamp,
    deleted_at  timestamptz
);
create unique index udx_admin_role_name on admin_role (name) where deleted_at is null;
create index idx_admin_role_created_at on admin_role (created_at) where deleted_at is null;

-- admin_role表字段注释
comment on table admin_role is '管理员角色关联表';
comment on column admin_role.id is '管理员角色ID';
comment on column admin_role.name is '角色名称';
comment on column admin_role.description is '角色描述';
comment on column admin_role.active is '是否激活';
comment on column admin_role.sort is '排序';
comment on column admin_role.created_at is '创建时间';
comment on column admin_role.updated_at is '更新时间';
comment on column admin_role.deleted_at is '删除时间';

-- endregion

-- ====================
-- region 用户信息
-- ====================

-- user
drop table if exists "user" cascade;
create table "user" (
    id             int generated by default as identity primary key,
    admin_id       int,
    phone          text           not null unique,
    username       text,
    email          text,
    password       text,
    name           text,
    avatar         text,
    status         int            not null default 1,
    balance        decimal(12, 2) not null default 0,
    id_type        int            not null default 0,
    id_no          text,
    id_token       text,
    contact_qq     text,
    contact_wechat text,
    last_login     timestamptz,
    last_login_ip  inet,
    last_login_ua  text,
    created_at     timestamptz default current_timestamp,
    updated_at     timestamptz default current_timestamp,
    deleted_at     timestamptz
);
create index idx_user_admin_id on "user" (admin_id) where deleted_at is null;
create unique index udx_user_phone on "user" (phone) where deleted_at is null;
create unique index udx_user_username on "user" (username) where deleted_at is null;
create unique index udx_user_email on "user" (email) where deleted_at is null;
create index idx_user_created_at on "user" (created_at) where deleted_at is null;

-- user表字段注释
comment on table "user" is '用户表';
comment on column "user".id is '用户ID';
comment on column "user".admin_id is '管理员ID';
comment on column "user".password is '用户密码';
comment on column "user".username is '用户名';
comment on column "user".phone is '手机号码';
comment on column "user".name is '真实姓名';
comment on column "user".avatar is '头像URL';
comment on column "user".status is '用户状态：0-禁用，1-正常';
comment on column "user".balance is '账户余额';
comment on column "user".id_type is '认证类型：0-未认证，1-个人认证，2-企业认证';
comment on column "user".id_no is '身份证号或营业执照号';
comment on column "user".id_token is '身份验证标识';
comment on column "user".contact_qq is 'QQ联系方式';
comment on column "user".contact_wechat is '微信联系方式';
comment on column "user".last_login is '最后登录时间';
comment on column "user".last_login_ip is '最后登录地址';
comment on column "user".last_login_ua is '最后登录代理';
comment on column "user".created_at is '创建时间';
comment on column "user".updated_at is '更新时间';
comment on column "user".deleted_at is '删除时间';

-- user_role
drop table if exists user_role cascade;
create table user_role (
    id          int generated by default as identity primary key,
    name        text not null,
    description text,
    active      bool default true,
    sort        int default 0,
    created_at  timestamptz default current_timestamp,
    updated_at  timestamptz default current_timestamp,
    deleted_at  timestamptz
);
create unique index udx_user_role_name on user_role (name) where deleted_at is null;
create index idx_user_role_created_at on user_role (created_at) where deleted_at is null;

-- user_role表字段注释
comment on table user_role is '用户角色表';
comment on column user_role.id is '角色ID';
comment on column user_role.name is '角色名称';
comment on column user_role.description is '角色描述';
comment on column user_role.active is '是否激活';
comment on column user_role.sort is '排序';
comment on column user_role.created_at is '创建时间';
comment on column user_role.updated_at is '更新时间';
comment on column user_role.deleted_at is '删除时间';

-- endregion

-- ====================
-- region 客户端信息
-- ====================

drop table if exists client cascade;
create table client (
    id            int generated by default as identity primary key,
    client_id     text not null unique,
    client_secret text not null,
    redirect_uri  text,
    spec          int  not null,
    name          text not null,
    icon          text,
    status        int  not null default 1,
    type          int  not null default 0,
    created_at    timestamptz default current_timestamp,
    updated_at    timestamptz default current_timestamp,
    deleted_at    timestamptz
);
create unique index udx_client_client_id on client (client_id);
create index idx_client_name on client (name) where deleted_at is null;
create index idx_client_created_at on client (created_at) where deleted_at is null;

-- client表字段注释
comment on table client is '客户端表';
comment on column client.id is '客户端ID';
comment on column client.client_id is 'OAuth2客户端标识符';
comment on column client.client_secret is 'OAuth2客户端密钥';
comment on column client.redirect_uri is 'OAuth2 重定向URI';
comment on column client.spec is '安全规范：1-native，2-browser，3-web，4-api';
comment on column client.name is '名称';
comment on column client.icon is '图标URL';
comment on column client.status is '状态：0-禁用，1-正常';
comment on column client.type is '类型：0-普通，1-官方';
comment on column client.created_at is '创建时间';
comment on column client.updated_at is '更新时间';
comment on column client.deleted_at is '删除时间';

-- endregion

-- ====================
-- region 权限信息
-- ====================

-- session
drop table if exists session cascade;
create table session (
    id                    int generated by default as identity primary key,
    user_id               int,
    admin_id              int,
    client_id             int,
    ip                    inet,
    ua                    text,
    access_token          text        not null,
    access_token_expires  timestamptz not null,
    refresh_token         text,
    refresh_token_expires timestamptz,
    scopes                text,
    created_at            timestamptz default current_timestamp,
    updated_at            timestamptz default current_timestamp,
    deleted_at            timestamptz
);
create unique index udx_session_access_token on session (access_token) where deleted_at is null;
create unique index udx_session_refresh_token on session (refresh_token) where deleted_at is null;
create index idx_session_user_id on session (user_id) where deleted_at is null;
create index idx_session_admin_id on session (admin_id) where deleted_at is null;
create index idx_session_client_id on session (client_id) where deleted_at is null;
create index idx_session_created_at on session (created_at) where deleted_at is null;

-- session表字段注释
comment on table session is '会话表';
comment on column session.id is '会话ID';
comment on column session.user_id is '用户ID';
comment on column session.admin_id is '管理员ID';
comment on column session.client_id is '客户端ID';
comment on column session.ip is 'IP地址';
comment on column session.ua is '用户代理';
comment on column session.access_token is '访问令牌';
comment on column session.access_token_expires is '访问令牌过期时间';
comment on column session.refresh_token is '刷新令牌';
comment on column session.refresh_token_expires is '刷新令牌过期时间';
comment on column session.scopes is '权限范围';
comment on column session.created_at is '创建时间';
comment on column session.updated_at is '更新时间';
comment on column session.deleted_at is '删除时间';

-- permission
drop table if exists permission cascade;
create table permission (
    id          int generated by default as identity primary key,
    parent_id   int,
    name        text not null,
    description text,
    created_at  timestamptz default current_timestamp,
    updated_at  timestamptz default current_timestamp,
    deleted_at  timestamptz
);
create unique index udx_permission_name on permission (name) where deleted_at is null;
create index idx_permission_parent_id on permission (parent_id) where deleted_at is null;
create index idx_permission_created_at on permission (created_at) where deleted_at is null;

-- permission表字段注释
comment on table permission is '权限表';
comment on column permission.id is '权限ID';
comment on column permission.parent_id is '父权限ID';
comment on column permission.name is '权限名称';
comment on column permission.description is '权限描述';
comment on column permission.created_at is '创建时间';
comment on column permission.updated_at is '更新时间';
comment on column permission.deleted_at is '删除时间';

-- link_user_role
drop table if exists link_user_role cascade;
create table link_user_role (
    id           int generated by default as identity primary key,
    user_id      int not null,
    user_role_id int not null
);
create index idx_link_user_role_user_id on link_user_role (user_id);
create index idx_link_user_role_role_id on link_user_role (user_role_id);

-- link_user_role表字段注释
comment on table link_user_role is '用户角色关联表';
comment on column link_user_role.id is '关联ID';
comment on column link_user_role.user_id is '用户ID';
comment on column link_user_role.user_role_id is '角色ID';

-- link_admin_role
drop table if exists link_admin_role cascade;
create table link_admin_role (
    id            int generated by default as identity primary key,
    admin_id      int not null,
    admin_role_id int not null
);
create index idx_link_admin_role_admin_id on link_admin_role (admin_id);
create index idx_link_admin_role_role_id on link_admin_role (admin_role_id);

-- link_admin_role表字段注释
comment on table link_admin_role is '管理员角色关联表';
comment on column link_admin_role.id is '关联ID';
comment on column link_admin_role.admin_id is '管理员ID';
comment on column link_admin_role.admin_role_id is '角色ID';

-- link_user_role_permission
drop table if exists link_user_role_permission cascade;
create table link_user_role_permission (
    id            int generated by default as identity primary key,
    user_role_id  int not null,
    permission_id int not null
);
create index idx_link_user_role_permission_role_id on link_user_role_permission (user_role_id);
create index idx_link_user_role_permission_permission_id on link_user_role_permission (permission_id);

-- link_user_role_permission表字段注释
comment on table link_user_role_permission is '用户角色权限关联表';
comment on column link_user_role_permission.id is '关联ID';
comment on column link_user_role_permission.user_role_id is '角色ID';
comment on column link_user_role_permission.permission_id is '权限ID';

-- link_admin_role_permission
drop table if exists link_admin_role_permission cascade;
create table link_admin_role_permission (
    id            int generated by default as identity primary key,
    admin_role_id int not null,
    permission_id int not null
);
create index idx_link_admin_role_permission_role_id on link_admin_role_permission (admin_role_id);
create index idx_link_admin_role_permission_permission_id on link_admin_role_permission (permission_id);

-- link_admin_role_permission表字段注释
comment on table link_admin_role_permission is '管理员角色权限关联表';
comment on column link_admin_role_permission.id is '关联ID';
comment on column link_admin_role_permission.admin_role_id is '角色ID';
comment on column link_admin_role_permission.permission_id is '权限ID';

-- link_client_permission
drop table if exists link_client_permission cascade;
create table link_client_permission (
    id            int generated by default as identity primary key,
    client_id     int not null,
    permission_id int not null
);
create index idx_link_client_permission_client_id on link_client_permission (client_id);
create index idx_link_client_permission_permission_id on link_client_permission (permission_id);

-- link_client_permission表字段注释
comment on table link_client_permission is '客户端权限关联表';
comment on column link_client_permission.id is '关联ID';
comment on column link_client_permission.client_id is '客户端ID';
comment on column link_client_permission.permission_id is '权限ID';

-- endregion

-- ====================
-- region 节点信息
-- ====================

-- proxy
drop table if exists proxy cascade;
create table proxy (
    id         int generated by default as identity primary key,
    version    int  not null,
    mac        text not null,
    ip         inet not null,
    host       text,
    secret     text,
    type       int  not null,
    status     int  not null,
    meta       jsonb,
    created_at timestamptz default current_timestamp,
    updated_at timestamptz default current_timestamp,
    deleted_at timestamptz
);
create unique index udx_proxy_mac on proxy (mac) where deleted_at is null;
create unique index udx_proxy_ip on proxy (ip) where deleted_at is null;
create index idx_proxy_created_at on proxy (created_at) where deleted_at is null;

-- proxy表字段注释
comment on table proxy is '代理服务表';
comment on column proxy.id is '代理服务ID';
comment on column proxy.version is '代理服务版本';
comment on column proxy.mac is '代理服务名称';
comment on column proxy.ip is '代理服务地址';
comment on column proxy.host is '代理服务域名';
comment on column proxy.secret is '代理服务密钥';
comment on column proxy.type is '代理服务类型：1-自有，2-白银';
comment on column proxy.status is '代理服务状态：0-离线，1-在线';
comment on column proxy.meta is '代理服务元信息';
comment on column proxy.created_at is '创建时间';
comment on column proxy.updated_at is '更新时间';
comment on column proxy.deleted_at is '删除时间';

-- edge
drop table if exists edge cascade;
create table edge (
    id         int generated by default as identity primary key,
    type       int  not null,
    version    int  not null,
    mac        text not null,
    ip         inet not null,
    isp        int  not null,
    prov       text not null,
    city       text not null,
    status     int  not null default 0,
    rtt        int default 0,
    loss       int default 0,
    created_at timestamptz default current_timestamp,
    updated_at timestamptz default current_timestamp,
    deleted_at timestamptz
);
create unique index udx_edge_mac on edge (mac) where deleted_at is null;
create index idx_edge_isp on edge (isp) where deleted_at is null;
create index idx_edge_prov on edge (prov) where deleted_at is null;
create index idx_edge_city on edge (city) where deleted_at is null;
create index idx_edge_created_at on edge (created_at) where deleted_at is null;

-- edge表字段注释
comment on table edge is '节点表';
comment on column edge.id is '节点ID';
comment on column edge.type is '节点类型：1-自建';
comment on column edge.version is '节点版本';
comment on column edge.mac is '节点 mac 地址';
comment on column edge.ip is '节点地址';
comment on column edge.isp is '运营商：1-电信，2-联通，3-移动';
comment on column edge.prov is '省份';
comment on column edge.city is '城市';
comment on column edge.status is '节点状态：0-离线，1-正常';
comment on column edge.rtt is '最近平均延迟';
comment on column edge.loss is '最近丢包率';
comment on column edge.created_at is '创建时间';
comment on column edge.updated_at is '更新时间';
comment on column edge.deleted_at is '删除时间';

-- whitelist
drop table if exists whitelist cascade;
create table whitelist (
    id         int generated by default as identity primary key,
    user_id    int  not null,
    ip         inet not null,
    remark     text,
    created_at timestamptz default current_timestamp,
    updated_at timestamptz default current_timestamp,
    deleted_at timestamptz
);
create index idx_whitelist_user_id on whitelist (user_id) where deleted_at is null;
create index idx_whitelist_ip on whitelist (ip) where deleted_at is null;
create index idx_whitelist_created_at on whitelist (created_at) where deleted_at is null;

-- whitelist表字段注释
comment on table whitelist is '白名单表';
comment on column whitelist.id is '白名单ID';
comment on column whitelist.user_id is '用户ID';
comment on column whitelist.ip is 'IP地址';
comment on column whitelist.remark is '备注';
comment on column whitelist.created_at is '创建时间';
comment on column whitelist.updated_at is '更新时间';
comment on column whitelist.deleted_at is '删除时间';

-- channel
drop table if exists channel cascade;
create table channel (
    id          int generated by default as identity primary key,
    user_id     int         not null,
    resource_id int         not null,
    batch_no    text        not null,
    proxy_id    int         not null,
    host        text        not null,
    port        int         not null,
    edge_id     int,
    edge_ref    text,
    filter_isp  int,
    filter_prov text,
    filter_city text,
    ip          inet,
    whitelists  text,
    username    text,
    password    text,
    expired_at  timestamptz not null,
    created_at  timestamptz default current_timestamp,
    updated_at  timestamptz default current_timestamp,
    deleted_at  timestamptz
);
create index idx_channel_user_resource on channel (user_id, resource_id) where deleted_at is null;
create index idx_channel_batch_no on channel (batch_no);
create index idx_channel_proxy_id on channel (proxy_id) where deleted_at is null;
create index idx_channel_created_at on channel (created_at) where deleted_at is null;

-- channel表字段注释
comment on table channel is '通道表';
comment on column channel.id is '通道ID';
comment on column channel.user_id is '用户ID';
comment on column channel.resource_id is '套餐ID';
comment on column channel.batch_no is '批次编号';
comment on column channel.proxy_id is '代理ID';
comment on column channel.host is '代理主机（快照）';
comment on column channel.port is '代理端口';
comment on column channel.edge_id is '节点ID（手动配置）';
comment on column channel.edge_ref is '外部节点引用，用于索引没有ID的外部非受控节点';
comment on column channel.filter_isp is '运营商过滤（自动配置）：参考 edge.isp';
comment on column channel.filter_prov is '省份过滤（自动配置）';
comment on column channel.filter_city is '城市过滤（自动配置）';
comment on column channel.ip is '节点地址';
comment on column channel.whitelists is 'IP白名单，逗号分隔';
comment on column channel.username is '用户名';
comment on column channel.password is '密码';
comment on column channel.expired_at is '过期时间';
comment on column channel.created_at is '创建时间';
comment on column channel.updated_at is '更新时间';
comment on column channel.deleted_at is '删除时间';

-- endregion

-- ====================
-- region 产品信息
-- ====================

-- product
drop table if exists product cascade;
create table product (
    id          int generated by default as identity primary key,
    code        text not null unique,
    name        text not null,
    description text,
    sort        int  not null default 0,
    status      int  not null default 1,
    created_at  timestamptz default current_timestamp,
    updated_at  timestamptz default current_timestamp,
    deleted_at  timestamptz
);
create unique index udx_product_code on product (code);
create index idx_product_name on product (name) where deleted_at is null;
create index idx_product_created_at on product (created_at) where deleted_at is null;

-- product表字段注释
comment on table product is '产品表';
comment on column product.id is '产品ID';
comment on column product.code is '产品代码';
comment on column product.name is '产品名称';
comment on column product.description is '产品描述';
comment on column product.sort is '排序';
comment on column product.status is '产品状态：0-禁用，1-正常';
comment on column product.created_at is '创建时间';
comment on column product.updated_at is '更新时间';
comment on column product.deleted_at is '删除时间';


-- product_discount
drop table if exists product_discount cascade;
create table product_discount (
    id         int generated by default as identity primary key,
    name       text,
    discount   int,
    created_at timestamptz default current_timestamp,
    updated_at timestamptz default current_timestamp,
    deleted_at timestamptz
);

-- product_discount表字段注释
comment on table product_discount is '产品折扣表';
comment on column product_discount.id is 'ID';
comment on column product_discount.name is '折扣名称';
comment on column product_discount.discount is '折扣，0 - 100 的整数，表示 xx 折';
comment on column product_discount.created_at is '创建时间';
comment on column product_discount.updated_at is '更新时间';
comment on column product_discount.deleted_at is '删除时间';

-- product_sku
drop table if exists product_sku cascade;
create table product_sku (
    id          int generated by default as identity primary key,
    product_id  int     not null,
    discount_id int,
    code        text    not null unique,
    name        text    not null,
    price       decimal not null,
    created_at  timestamptz default current_timestamp,
    updated_at  timestamptz default current_timestamp,
    deleted_at  timestamptz
);
create index idx_product_sku_product_id on product_sku (product_id) where deleted_at is null;
create index idx_product_sku_discount_id on product_sku (discount_id) where deleted_at is null;
create unique index idx_product_sku_code on product_sku (code) where deleted_at is null;

-- product_sku表字段注释
comment on table product_sku is '产品SKU表';
comment on column product_sku.id is 'SKU ID';
comment on column product_sku.product_id is '产品ID';
comment on column product_sku.discount_id is '折扣ID';
comment on column product_sku.code is 'SKU 代码：格式为 key=value,key=value,...，其中，key:value 是 SKU 的属性，多个属性用逗号分隔';
comment on column product_sku.name is 'SKU 可读名称';
comment on column product_sku.price is '定价';
comment on column product_sku.created_at is '创建时间';
comment on column product_sku.updated_at is '更新时间';
comment on column product_sku.deleted_at is '删除时间';

-- product_sku_user
drop table if exists product_sku_user cascade;
create table product_sku_user (
    id             int generated by default as identity primary key,
    user_id        int not null,
    product_sku_id int not null,
    discount_id    int,
    created_at     timestamptz default current_timestamp,
    updated_at     timestamptz default current_timestamp
);
create index idx_product_sku_user_user_id on product_sku_user (user_id);
create index idx_product_sku_user_product_sku_id on product_sku_user (product_sku_id);
create index idx_product_sku_user_discount_id on product_sku_user (discount_id);

-- product_sku_user表字段注释
comment on table product_sku_user is '用户产品SKU表';
comment on column product_sku_user.id is 'ID';
comment on column product_sku_user.user_id is '用户ID';
comment on column product_sku_user.product_sku_id is '产品SKU ID';
comment on column product_sku_user.discount_id is '折扣ID';
comment on column product_sku_user.created_at is '创建时间';
comment on column product_sku_user.updated_at is '更新时间';

-- resource
drop table if exists resource cascade;
create table resource (
    id          int generated by default as identity primary key,
    user_id     int  not null,
    resource_no text not null,
    code        text,
    type        int  not null,
    active      bool not null default true,
    created_at  timestamptz default current_timestamp,
    updated_at  timestamptz default current_timestamp,
    deleted_at  timestamptz
);
create unique index udx_resource_resource_no on resource (resource_no);
create index idx_resource_user_id on resource (user_id) where deleted_at is null;
create index idx_resource_created_at on resource (created_at) where deleted_at is null;
create index idx_resource_code on resource (code) where deleted_at is null;

-- resource表字段注释
comment on table resource is '套餐表';
comment on column resource.id is '套餐ID';
comment on column resource.user_id is '用户ID';
comment on column resource.resource_no is '套餐编号';
comment on column resource.active is '套餐状态';
comment on column resource.type is '套餐类型：1-短效动态，2-长效动态';
comment on column resource.code is '产品编码';
comment on column resource.created_at is '创建时间';
comment on column resource.updated_at is '更新时间';
comment on column resource.deleted_at is '删除时间';

-- resource_short
drop table if exists resource_short cascade;
create table resource_short (
    id          int generated by default as identity primary key,
    resource_id int not null,
    code        text,
    live        int not null,
    type        int not null,
    quota       int not null,
    expire_at   timestamptz,
    used        int not null default 0,
    daily       int not null default 0,
    last_at     timestamptz
);
create index idx_resource_short_resource_id on resource_short (resource_id);
create index idx_resource_short_code on resource_short (code);

-- resource_short表字段注释
comment on table resource_short is '短效动态套餐表';
comment on column resource_short.id is 'ID';
comment on column resource_short.resource_id is '套餐ID';
comment on column resource_short.code is '产品套餐编码';
comment on column resource_short.live is '可用时长(秒)';
comment on column resource_short.type is '套餐类型：1-包时，2-包量';
comment on column resource_short.quota is '每日配额（包时）或总配额（包量）';
comment on column resource_short.expire_at is '套餐过期时间，包时模式可用';
comment on column resource_short.used is '总用量';
comment on column resource_short.daily is '当日用量';
comment on column resource_short.last_at is '最后使用时间';

-- resource_long
drop table if exists resource_long cascade;
create table resource_long (
    id          int generated by default as identity primary key,
    resource_id int not null,
    code        text,
    live        int not null,
    type        int not null,
    quota       int not null,
    expire_at   timestamptz,
    used        int not null default 0,
    daily       int not null default 0,
    last_at     timestamptz
);
create index idx_resource_long_resource_id on resource_long (resource_id);
create index idx_resource_long_code on resource_long (code);

-- resource_long表字段注释
comment on table resource_long is '长效动态套餐表';
comment on column resource_long.id is 'ID';
comment on column resource_long.resource_id is '套餐ID';
comment on column resource_long.code is '产品套餐编码';
comment on column resource_long.live is '可用时长(小时)';
comment on column resource_long.type is '套餐类型：1-包时，2-包量';
comment on column resource_long.quota is '每日配额（包时）或总配额（包量）';
comment on column resource_long.expire_at is '套餐过期时间，包时模式可用';
comment on column resource_long.used is '总用量';
comment on column resource_long.daily is '当日用量';
comment on column resource_long.last_at is '最后使用时间';

-- endregion

-- ====================
-- region 订单信息
-- ====================

-- trade
drop table if exists trade cascade;
create table trade (
    id           int generated by default as identity primary key,
    user_id      int            not null,
    inner_no     text           not null,
    outer_no     text,
    type         int            not null,
    subject      text           not null,
    remark       text,
    amount       decimal(12, 2) not null default 0,
    payment      decimal(12, 2) not null default 0,
    method       int            not null,
    platform     int            not null,
    acquirer     int,
    status       int            not null default 0,
    refunded     bool           not null default false,
    payment_url  text,
    completed_at timestamptz,
    canceled_at  timestamptz,
    created_at   timestamptz default current_timestamp,
    updated_at   timestamptz default current_timestamp,
    deleted_at   timestamptz
);
create unique index udx_trade_inner_no on trade (inner_no);
create index idx_trade_outer_no on trade (outer_no) where deleted_at is null;
create index idx_trade_user_id on trade (user_id) where deleted_at is null;
create index idx_trade_created_at on trade (created_at) where deleted_at is null;

-- trade表字段注释
comment on table trade is '订单表';
comment on column trade.id is '订单ID';
comment on column trade.user_id is '用户ID';
comment on column trade.inner_no is '内部订单号';
comment on column trade.outer_no is '外部订单号';
comment on column trade.type is '订单类型：1-购买产品，2-充值余额';
comment on column trade.subject is '订单主题';
comment on column trade.remark is '订单备注';
comment on column trade.amount is '订单总金额';
comment on column trade.payment is '实际支付金额';
comment on column trade.method is '支付方式：1-支付宝，2-微信，3-商福通，4-商福通渠道支付宝，5-商福通渠道微信';
comment on column trade.platform is '支付平台：1-电脑网站，2-手机网站';
comment on column trade.acquirer is '收单机构：1-支付宝，2-微信，3-银联';
comment on column trade.status is '订单状态：0-待支付，1-已支付，2-已取消';
comment on column trade.payment_url is '支付链接';
comment on column trade.completed_at is '支付时间';
comment on column trade.canceled_at is '取消时间';
comment on column trade.created_at is '创建时间';
comment on column trade.updated_at is '更新时间';
comment on column trade.deleted_at is '删除时间';

-- refund
drop table if exists refund cascade;
create table refund (
    id         int generated by default as identity primary key,
    trade_id   int            not null,
    product_id int,
    amount     decimal(12, 2) not null default 0,
    reason     text,
    status     int            not null default 0,
    created_at timestamptz default current_timestamp,
    updated_at timestamptz default current_timestamp,
    deleted_at timestamptz
);
create index idx_refund_trade_id on refund (trade_id) where deleted_at is null;
create index idx_refund_product_id on refund (product_id) where deleted_at is null;
create index idx_refund_created_at on refund (created_at) where deleted_at is null;

-- refund表字段注释
comment on table refund is '退款记录表';
comment on column refund.id is '退款ID';
comment on column refund.trade_id is '订单ID';
comment on column refund.product_id is '产品ID';
comment on column refund.amount is '退款金额';
comment on column refund.reason is '退款原因';
comment on column refund.status is '退款状态：0-待处理，1-已退款，2-已拒绝';
comment on column refund.created_at is '创建时间';
comment on column refund.updated_at is '更新时间';
comment on column refund.deleted_at is '删除时间';

-- bill
drop table if exists bill cascade;
create table bill (
    id          int generated by default as identity primary key,
    user_id     int            not null,
    trade_id    int,
    resource_id int,
    refund_id   int,
    coupon_id   int,
    bill_no     text           not null,
    info        text,
    type        int            not null,
    amount      decimal(12, 2) not null default 0,
    actual      decimal(12, 2) not null default 0,
    created_at  timestamptz default current_timestamp,
    updated_at  timestamptz default current_timestamp,
    deleted_at  timestamptz
);
create unique index udx_bill_bill_no on bill (bill_no);
create index idx_bill_user_id on bill (user_id) where deleted_at is null;
create index idx_bill_trade_id on bill (trade_id) where deleted_at is null;
create index idx_bill_resource_id on bill (resource_id) where deleted_at is null;
create index idx_bill_refund_id on bill (refund_id) where deleted_at is null;
create index idx_bill_coupon_id on bill (coupon_id) where deleted_at is null;
create index idx_bill_created_at on bill (created_at) where deleted_at is null;

-- bill表字段注释
comment on table bill is '账单表';
comment on column bill.id is '账单ID';
comment on column bill.user_id is '用户ID';
comment on column bill.trade_id is '订单ID';
comment on column bill.resource_id is '套餐ID';
comment on column bill.refund_id is '退款ID';
comment on column bill.bill_no is '易读账单号';
comment on column bill.info is '产品可读信息';
comment on column bill.type is '账单类型：1-消费，2-退款，3-充值';
comment on column bill.amount is '应付金额';
comment on column bill.actual is '实付金额';
comment on column bill.created_at is '创建时间';
comment on column bill.updated_at is '更新时间';
comment on column bill.deleted_at is '删除时间';

-- coupon 优惠券
drop table if exists coupon cascade;
create table coupon (
    id         int generated by default as identity primary key,
    user_id    int,
    code       text           not null,
    remark     text,
    amount     decimal(12, 2) not null default 0,
    min_amount decimal(12, 2) not null default 0,
    status     int            not null default 0,
    expire_at  timestamptz,
    created_at timestamptz default current_timestamp,
    updated_at timestamptz default current_timestamp,
    deleted_at timestamptz
);
create index idx_coupon_user_id on coupon (user_id) where deleted_at is null;
create index idx_coupon_code on coupon (code) where deleted_at is null;
create index idx_coupon_created_at on coupon (created_at) where deleted_at is null;

-- coupon表字段注释
comment on table coupon is '优惠券表';
comment on column coupon.id is '优惠券ID';
comment on column coupon.user_id is '用户ID';
comment on column coupon.code is '优惠券代码';
comment on column coupon.remark is '优惠券备注';
comment on column coupon.amount is '优惠券金额';
comment on column coupon.min_amount is '最低消费金额';
comment on column coupon.status is '优惠券状态：0-未使用，1-已使用，2-已过期';
comment on column coupon.expire_at is '过期时间';
comment on column coupon.created_at is '创建时间';
comment on column coupon.updated_at is '更新时间';
comment on column coupon.deleted_at is '删除时间';

-- endregion

-- ====================
-- region 外键约束
-- ====================

-- user表外键
alter table "user"
    add constraint fk_user_admin_id foreign key (admin_id) references admin (id) on delete set null;

-- session表外键
alter table session
    add constraint fk_session_user_id foreign key (user_id) references "user" (id) on delete cascade;
alter table session
    add constraint fk_session_client_id foreign key (client_id) references client (id) on delete cascade;

-- permission表外键
alter table permission
    add constraint fk_permission_parent_id foreign key (parent_id) references permission (id) on delete set null;

-- link_user_role表外键
alter table link_user_role
    add constraint fk_link_user_role_user_id foreign key (user_id) references "user" (id) on delete cascade;
alter table link_user_role
    add constraint fk_link_user_role_role_id foreign key (user_role_id) references user_role (id) on delete cascade;

-- link_admin_role表外键
alter table link_admin_role
    add constraint fk_link_admin_role_admin_id foreign key (admin_id) references admin (id) on delete cascade;
alter table link_admin_role
    add constraint fk_link_admin_role_role_id foreign key (admin_role_id) references admin_role (id) on delete cascade;

-- link_user_role_permission表外键
alter table link_user_role_permission
    add constraint fk_link_user_role_permission_role_id foreign key (user_role_id) references user_role (id) on delete cascade;
alter table link_user_role_permission
    add constraint fk_link_user_role_permission_permission_id foreign key (permission_id) references permission (id) on delete cascade;

-- link_admin_role_permission表外键
alter table link_admin_role_permission
    add constraint fk_link_admin_role_permission_role_id foreign key (admin_role_id) references admin_role (id) on delete cascade;
alter table link_admin_role_permission
    add constraint fk_link_admin_role_permission_permission_id foreign key (permission_id) references permission (id) on delete cascade;

-- link_client_permission表外键
alter table link_client_permission
    add constraint fk_link_client_permission_client_id foreign key (client_id) references client (id) on delete cascade;
alter table link_client_permission
    add constraint fk_link_client_permission_permission_id foreign key (permission_id) references permission (id) on delete cascade;

-- whitelist表外键
alter table whitelist
    add constraint fk_whitelist_user_id foreign key (user_id) references "user" (id) on delete cascade;

-- channel表外键
alter table channel
    add constraint fk_channel_user_id foreign key (user_id) references "user" (id) on delete cascade;
alter table channel
    add constraint fk_channel_proxy_id foreign key (proxy_id) references proxy (id) on delete set null;
alter table channel
    add constraint fk_channel_edge_id foreign key (edge_id) references edge (id) on delete set null;
alter table channel
    add constraint fk_channel_resource_id foreign key (resource_id) references resource (id) on delete set null;

-- resource表外键
alter table resource
    add constraint fk_resource_user_id foreign key (user_id) references "user" (id) on delete cascade;
alter table resource
    add constraint fk_product_code foreign key (code) references product (code) on update cascade on delete restrict;

-- resource_short表外键
alter table resource_short
    add constraint fk_resource_short_resource_id foreign key (resource_id) references resource (id) on delete cascade;
alter table resource_short
    add constraint fk_resource_short_code foreign key (code) references product_sku (code) on update cascade on delete restrict;

-- resource_long表外键
alter table resource_long
    add constraint fk_resource_long_resource_id foreign key (resource_id) references resource (id) on delete cascade;
alter table resource_long
    add constraint fk_resource_long_code foreign key (code) references product_sku (code) on update cascade on delete restrict;

-- trade表外键
alter table trade
    add constraint fk_trade_user_id foreign key (user_id) references "user" (id) on delete set null;

-- refund表外键
alter table refund
    add constraint fk_refund_trade_id foreign key (trade_id) references trade (id) on delete cascade;
alter table refund
    add constraint fk_refund_product_id foreign key (product_id) references product (id) on delete set null;

-- bill表外键
alter table bill
    add constraint fk_bill_user_id foreign key (user_id) references "user" (id) on delete cascade;
alter table bill
    add constraint fk_bill_trade_id foreign key (trade_id) references trade (id) on delete set null;
alter table bill
    add constraint fk_bill_resource_id foreign key (resource_id) references resource (id) on delete set null;
alter table bill
    add constraint fk_bill_refund_id foreign key (refund_id) references refund (id) on delete set null;
alter table bill
    add constraint fk_bill_coupon_id foreign key (coupon_id) references coupon (id) on delete set null;

-- coupon表外键
alter table coupon
    add constraint fk_coupon_user_id foreign key (user_id) references "user" (id) on delete cascade;

-- product_sku表外键
alter table product_sku
    add constraint fk_product_sku_product_id foreign key (product_id) references product (id) on delete cascade;
alter table product_sku
    add constraint fk_product_sku_discount_id foreign key (discount_id) references product_discount (id) on delete restrict;

-- product_sku_user表外键
alter table product_sku_user
    add constraint fk_product_sku_user_user_id foreign key (user_id) references "user" (id) on delete cascade;
alter table product_sku_user
    add constraint fk_product_sku_user_product_sku_id foreign key (product_sku_id) references product_sku (id) on delete cascade;
alter table product_sku_user
    add constraint fk_product_sku_user_discount_id foreign key (discount_id) references product_discount (id) on delete restrict;

-- endregion
