From e2b5608fc56c19b79a9259c041ea122a3cbea8be Mon Sep 17 00:00:00 2001 From: luorijun Date: Sat, 20 Dec 2025 19:00:20 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=83=A8=E7=BD=B2=E9=85=8D?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 11 +- .gitignore | 4 + README.md | 4 + docker-compose.yaml | 14 +- init.sql | 1061 +++++++++++++++++++++++++++++++++++++++++++ startup.sh | 25 + 6 files changed, 1109 insertions(+), 10 deletions(-) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 init.sql create mode 100644 startup.sh diff --git a/.env.example b/.env.example index a697090..32e5a7d 100644 --- a/.env.example +++ b/.env.example @@ -1,8 +1,9 @@ -# postgres -DB_USERNAME= -DB_PASSWORD= -DB_NAME= +DB_USERNAME=test +DB_PASSWORD=test +DB_NAME=app DB_PORT=5432 -# redis REDIS_PORT=6379 + +PLATFORM_PORT=8080 +WEB_PORT=3000 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b940263 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.env +.env.* +!.env.example +init.sql diff --git a/README.md b/README.md new file mode 100644 index 0000000..3d3b274 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +1. 拉取项目 +2. 配置环境变量 +3. 启动项目 +4. 填充基础数据(client 表) diff --git a/docker-compose.yaml b/docker-compose.yaml index d4db625..0cd889a 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,4 +1,4 @@ -name: lanhu-prod +name: lanhu services: postgres: @@ -8,7 +8,7 @@ services: POSTGRES_PASSWORD: ${DB_PASSWORD} POSTGRES_DB: ${DB_NAME} ports: - - "localhost:${DB_PORT}:{DB_PORT}" + - "127.0.0.1:${DB_PORT}:5432" volumes: - postgres_data:/var/lib/postgresql/data - ./init.sql:/docker-entrypoint-initdb.d/init.sql @@ -19,7 +19,7 @@ services: redis: image: redis:7.4 ports: - - "localhost:${REDIS_PORT}:{REDIS_PORT}" + - "127.0.0.1:${REDIS_PORT}:6379" volumes: - redis_data:/data restart: unless-stopped @@ -27,24 +27,28 @@ services: driver: local platform: - image: ghcr.io/wyongk/juip-proxy-back:main + image: 43.226.58.254:53000/zxf/lanhu-platform:latest env_file: - .env - .env.platform depends_on: - postgres - redis + ports: + - "127.0.0.1:${PLATFORM_PORT}:8080" restart: unless-stopped logging: driver: local web: - image: ghcr.io/wyongk/juip-proxy-react:main + image: 43.226.58.254:53000/wmp/lanhu-web:latest env_file: - .env - .env.web depends_on: - platform + ports: + - "127.0.0.1:${WEB_PORT}:3000" restart: unless-stopped logging: driver: local diff --git a/init.sql b/init.sql new file mode 100644 index 0000000..6224dba --- /dev/null +++ b/init.sql @@ -0,0 +1,1061 @@ +-- ==================== +-- 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 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_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, + 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 (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.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, + 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 (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.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, + role_id int not null, + permission_id int not null +); +create index idx_link_user_role_permission_role_id on link_user_role_permission (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.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, + role_id int not null, + permission_id int not null +); +create index idx_link_admin_role_permission_role_id on link_admin_role_permission (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.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 '删除时间'; + +-- 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, + active bool not null default true, + type int not null, + 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; + +-- 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.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, + 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); + +-- 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.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, + 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); + +-- 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.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, + bill_no text not null, + info text, + type int not null, + amount 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_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.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 (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 (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 (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 (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; + +-- resource_short表外键 +alter table resource_short + add constraint fk_resource_short_resource_id foreign key (resource_id) references resource (id) on delete cascade; + +-- resource_long表外键 +alter table resource_long + add constraint fk_resource_long_resource_id foreign key (resource_id) references resource (id) on delete cascade; + +-- 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; + +-- coupon表外键 +alter table coupon + add constraint fk_coupon_user_id foreign key (user_id) references "user" (id) on delete cascade; + +-- endregion diff --git a/startup.sh b/startup.sh new file mode 100644 index 0000000..3cedd99 --- /dev/null +++ b/startup.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# 检查必要文件 +req_files=( + ".env" + ".env.platform" + # ".env.task" + ".env.web" + "init.sql" +) + +for i in "${!req_files[@]}"; do + if [ ! -f "${req_files[$i]}" ]; then + echo "缺少必要文件: ${req_files[$i]}" + exit 1 + fi +done + +# 启动项目 +if ! command -v docker &> /dev/null; then + echo "Docker 未安装,请先安装 Docker" + exit 1 +fi + +# docker compose up -d