chore: initial commit

This commit is contained in:
2026-07-22 17:50:33 +08:00
parent 82936a8987
commit ee9f50b859
107 changed files with 8346 additions and 0 deletions

View File

@@ -0,0 +1,32 @@
-- migrate/down.sql
-- 此文件用于撤销 up.sql 文件的所有操作
-- 注意:执行此脚本将会永久删除相关表和数据!
-- 移除表上的触发器
-- 必须在删除表和函数之前移除依赖它们的触发器
DROP TRIGGER IF EXISTS update_sys_users_updated_at ON sys_users;
DROP TRIGGER IF EXISTS update_sys_roles_updated_at ON sys_roles;
DROP TRIGGER IF EXISTS update_sys_menus_updated_at ON sys_menus;
DROP TRIGGER IF EXISTS update_sys_permissions_updated_at ON sys_permissions;
DROP TRIGGER IF EXISTS update_sys_apis_updated_at ON sys_apis;
DROP TRIGGER IF EXISTS update_files_updated_at ON files;
-- 删除索引
DROP INDEX IF EXISTS idx_sys_users_account;
-- 移除表
-- 按照与创建相反的顺序删除,先删除依赖其他表的表
DROP TABLE IF EXISTS sys_api_permission;
DROP TABLE IF EXISTS sys_apis;
DROP TABLE IF EXISTS sys_menu_permission;
DROP TABLE IF EXISTS sys_role_permission;
DROP TABLE IF EXISTS sys_user_role;
DROP TABLE IF EXISTS sys_permissions;
DROP TABLE IF EXISTS sys_menus;
DROP TABLE IF EXISTS sys_roles;
DROP TABLE IF EXISTS sys_users;
DROP TABLE IF EXISTS files;
-- 移除触发器函数
-- 在所有使用此函数的触发器都被移除后,可以安全地删除此函数
DROP FUNCTION IF EXISTS update_updated_at_column;

View File

@@ -0,0 +1,221 @@
--------------------------------------------------------------------------------------------------------------
-- function
CREATE
OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS
$$
BEGIN
NEW.updated_at
:= NOW();
RETURN NEW;
END;
$$
language 'plpgsql';
--------------------------------------------------------------------------------------------------------------
CREATE TABLE sys_users
(
id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
account TEXT NOT NULL UNIQUE,
username TEXT NOT NULL,
password_hash TEXT NOT NULL,
avatar_id INTEGER,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ
);
CREATE INDEX idx_sys_users_account ON sys_users (account);
CREATE TRIGGER update_sys_users_updated_at
BEFORE UPDATE
ON sys_users
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
COMMENT ON COLUMN sys_users.id IS '主键ID';
COMMENT ON COLUMN sys_users.account IS '用户账号';
COMMENT ON COLUMN sys_users.username IS '用户名称';
COMMENT ON COLUMN sys_users.password_hash IS 'hash密码';
COMMENT ON COLUMN sys_users.avatar_id IS '头像文件id';
COMMENT ON COLUMN sys_users.created_at IS '创建时间';
COMMENT ON COLUMN sys_users.updated_at IS '更新时间';
--------------------------------------------------------------------------------------------------------------
CREATE TABLE sys_roles
(
id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
name TEXT NOT NULL,
code TEXT NOT NULL UNIQUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ
);
CREATE TRIGGER update_sys_roles_updated_at
BEFORE UPDATE
ON sys_roles
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
COMMENT ON COLUMN sys_roles.id IS '主键ID';
COMMENT ON COLUMN sys_roles.name IS '角色名称';
COMMENT ON COLUMN sys_roles.code IS '角色编码,唯一';
COMMENT ON COLUMN sys_roles.created_at IS '创建时间';
COMMENT ON COLUMN sys_roles.updated_at IS '更新时间';
--------------------------------------------------------------------------------------------------------------
CREATE TABLE sys_permissions
(
id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
code TEXT UNIQUE,
type SMALLINT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ,
CONSTRAINT unique_type_code UNIQUE (type, code)
);
CREATE TRIGGER update_sys_permissions_updated_at
BEFORE UPDATE
ON sys_permissions
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
COMMENT ON COLUMN sys_permissions.id IS '主键ID';
COMMENT ON COLUMN sys_permissions.code IS '权限字符,唯一';
COMMENT ON COLUMN sys_permissions.code IS '权限类型0: 菜单 1: api';
COMMENT ON COLUMN sys_permissions.created_at IS '创建时间';
COMMENT ON COLUMN sys_permissions.updated_at IS '更新时间';
--------------------------------------------------------------------------------------------------------------
CREATE TABLE sys_menus
(
id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
name TEXT NOT NULL,
path TEXT NOT NULL,
component TEXT,
type SMALLINT NOT NULL,
hidden BOOLEAN DEFAULT FALSE,
sort INTEGER DEFAULT 0,
status SMALLINT NOT NULL DEFAULT 1,
parent_id INTEGER,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ,
CHECK (type IN (0, 1, 2)),
CHECK (status IN (0, 1))
);
CREATE TRIGGER update_sys_menus_updated_at
BEFORE UPDATE
ON sys_menus
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
COMMENT ON COLUMN sys_menus.id IS '主键ID';
COMMENT ON COLUMN sys_menus.name IS '菜单名称';
COMMENT ON COLUMN sys_menus.path IS '菜单路径';
COMMENT ON COLUMN sys_menus.component IS '组件路径';
COMMENT ON COLUMN sys_menus.type IS '菜单类型 0: 目录 1: 菜单 2: button';
COMMENT ON COLUMN sys_menus.sort IS '菜单排序';
COMMENT ON COLUMN sys_menus.hidden IS '是否隐藏菜单';
COMMENT ON COLUMN sys_menus.status IS '菜单状态 1: 启用 0: 禁用';
COMMENT ON COLUMN sys_menus.parent_id IS '父级id';
COMMENT ON COLUMN sys_menus.created_at IS '创建时间';
COMMENT ON COLUMN sys_menus.updated_at IS '更新时间';
--------------------------------------------------------------------------------------------------------------
CREATE TABLE sys_apis
(
id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
group_name TEXT NOT NULL,
name TEXT NOT NULL,
method TEXT NOT NULL,
path TEXT NOT NULL,
sort INTEGER DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ,
UNIQUE (method, path)
);
CREATE TRIGGER update_sys_apis_updated_at
BEFORE UPDATE
ON sys_apis
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
COMMENT ON COLUMN sys_apis.id IS '主键ID';
COMMENT ON COLUMN sys_apis.group_name IS '分组名称';
COMMENT ON COLUMN sys_apis.name IS '接口名称';
COMMENT ON COLUMN sys_apis.method IS 'api方法';
COMMENT ON COLUMN sys_apis.path IS 'api路径';
COMMENT ON COLUMN sys_apis.sort IS '排序';
COMMENT ON COLUMN sys_apis.created_at IS '创建时间';
COMMENT ON COLUMN sys_apis.updated_at IS '更新时间';
--------------------------------------------------------------------------------------------------------------
CREATE TABLE files
(
id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
file_name TEXT NOT NULL,
file_path TEXT NOT NULL,
original_name TEXT NOT NULL,
folder_name TEXT NOT NULL,
mime_type TEXT NOT NULL,
file_size BIGINT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ
);
CREATE TRIGGER update_files_updated_at
BEFORE UPDATE
ON files
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
COMMENT ON COLUMN files.id IS '主键ID';
COMMENT ON COLUMN files.file_name IS '文件名 xxx.jpg';
COMMENT ON COLUMN files.file_path IS '文件路径 带path /xxx/xxx.jpg';
COMMENT ON COLUMN files.original_name IS '原始文件名';
COMMENT ON COLUMN files.folder_name IS '文件夹名称';
COMMENT ON COLUMN files.mime_type IS 'mime_type';
COMMENT ON COLUMN files.file_size IS '文件大小';
COMMENT ON COLUMN files.created_at IS '上传时间';
COMMENT ON COLUMN files.updated_at IS '修改时间';
--------------------------------------------------------------------------------------------------------------
CREATE TABLE sys_user_role
(
user_id INTEGER NOT NULL,
role_id INTEGER NOT NULL,
PRIMARY KEY (user_id, role_id)
);
COMMENT ON COLUMN sys_user_role.user_id IS '用户ID';
COMMENT ON COLUMN sys_user_role.role_id IS '角色ID';
--------------------------------------------------------------------------------------------------------------
CREATE TABLE sys_role_permission
(
role_id INTEGER NOT NULL,
permission_id INTEGER NOT NULL,
PRIMARY KEY (role_id, permission_id)
);
COMMENT ON COLUMN sys_role_permission.role_id IS '角色id';
COMMENT ON COLUMN sys_role_permission.permission_id IS '权限id';
--------------------------------------------------------------------------------------------------------------
CREATE TABLE sys_menu_permission
(
menu_id INTEGER NOT NULL,
permission_id INTEGER NOT NULL,
PRIMARY KEY (menu_id, permission_id)
);
COMMENT ON COLUMN sys_menu_permission.menu_id IS '菜单id';
COMMENT ON COLUMN sys_menu_permission.permission_id IS '权限id';
--------------------------------------------------------------------------------------------------------------
CREATE TABLE sys_api_permission
(
api_id INTEGER NOT NULL,
permission_id INTEGER NOT NULL,
PRIMARY KEY (api_id, permission_id)
);
COMMENT ON COLUMN sys_api_permission.api_id IS 'api ID';
COMMENT ON COLUMN sys_api_permission.permission_id IS '权限id';
--------------------------------------------------------------------------------------------------------------

View File

@@ -0,0 +1,83 @@
-- 创建用户
INSERT INTO sys_users (account, username, password_hash, avatar_id, created_at)
VALUES ('admin', '管理员', '$2a$10$d/xYJVUFTmpSVUaV3uawZ.7hj6FanbooMJ9YjsFOmQKNopWc6oR9i', NULL,
'2025-10-18 03:05:54.692714');
-- 批量插入菜单及关联权限
WITH input(name, path, component, type, sort, status, code)
AS (VALUES ('首页', '/', '/system/home/index', 1, 0, 1, 'home'),
('用户管理', '/user', '/system/user/index', 1, 1, 1, 'user'),
('角色管理', '/role', '/system/role/index', 1, 2, 1, 'role'),
('菜单管理', '/menu', '/system/menu/index', 1, 3, 1, 'menu'),
('接口管理', '/sys_api', '/system/api/index', 1, 4, 1, 'api'),
('文件管理', '/file', '/system/file/index', 1, 5, 1, 'file')),
-- 插入菜单
inserted_menus AS (
INSERT INTO sys_menus (name, path, component, type, sort, status)
SELECT name, path, component, type, sort, status FROM input RETURNING id),
menu_rows AS (SELECT id, ROW_NUMBER() OVER () AS rn FROM inserted_menus),
-- 插入权限
inserted_permissions AS (
INSERT INTO sys_permissions (type, code)
SELECT 0, code FROM input RETURNING id),
permission_rows AS (SELECT id, ROW_NUMBER() OVER () AS rn FROM inserted_permissions)
-- 关联权限
INSERT
INTO sys_menu_permission(menu_id, permission_id)
SELECT m.id, p.id
FROM menu_rows m
JOIN permission_rows p ON m.rn = p.rn;
-- 批量插入接口及关联权限
WITH inserted_apis AS (
-- 插入菜单
INSERT INTO sys_apis (group_name, name, method, path)
VALUES ('用户管理', '用户列表', 'GET', '/admin/user'),
('用户管理', '用户信息', 'GET', '/admin/user/info'),
('用户管理', '查询用户', 'GET', '/admin/user/{id}'),
('用户管理', '用户角色', 'GET', '/admin/user/{id}/roles'),
('用户管理', '创建用户', 'POST', '/admin/user'),
('用户管理', '修改用户', 'PATCH', '/admin/user/{id}'),
('用户管理', '删除用户', 'DELETE', '/admin/user/{id}'),
('用户管理', '分配用户角色', 'PUT', '/admin/user/{id}/roles'),
('用户管理', '修改密码', 'PATCH', '/admin/user/{id}/password'),
--
('角色管理', '角色列表', 'GET', '/admin/role'),
('角色管理', '创建角色', 'POST', '/admin/role'),
('角色管理', '所有角色', 'GET', '/admin/role/all'),
('角色管理', '单个角色', 'GET', '/admin/role/{id}'),
('角色管理', '角色菜单', 'GET', '/admin/role/{id}/menus'),
('角色管理', '角色接口', 'GET', '/admin/role/{id}/apis'),
('角色管理', '修改角色', 'PATCH', '/admin/role/{id}'),
('角色管理', '删除角色', 'DELETE', '/admin/role/{id}'),
('角色管理', '分配角色菜单', 'PUT', '/admin/role/{id}/menus'),
('角色管理', '分配角色接口', 'PUT', '/admin/role/{id}/apis'),
--
('接口管理', '接口列表', 'GET', '/admin/api'),
('接口管理', '所有接口', 'GET', '/admin/api/all'),
('接口管理', '接口分组', 'GET', '/admin/api/group_names'),
('接口管理', '创建接口', 'POST', '/admin/api'),
('接口管理', '修改接口', 'PATCH', '/admin/api/{id}'),
('接口管理', '删除接口', 'DELETE', '/admin/api/{id}'),
--
('菜单管理', '菜单列表', 'GET', '/admin/menu'),
('菜单管理', '创建菜单', 'POST', '/admin/menu'),
('菜单管理', '所有菜单', 'GET', '/admin/menu/all'),
('菜单管理', '修改菜单', 'PATCH', '/admin/menu/{id}'),
('菜单管理', '删除菜单', 'DELETE', '/admin/menu/{id}'),
--
('文件管理', '文件列表', 'GET', '/admin/file'),
('文件管理', '上传文件', 'POST', '/admin/file') RETURNING id),
api_rows AS (SELECT id, ROW_NUMBER() OVER () AS rn
FROM inserted_apis),
-- 创建权限
inserted_permissions AS (
INSERT INTO sys_permissions (type)
SELECT 1 FROM api_rows RETURNING id),
permission_rows AS (SELECT id, ROW_NUMBER() OVER () AS rn
FROM inserted_permissions)
INSERT
INTO sys_api_permission(api_id, permission_id)
SELECT a.id, b.id
FROM api_rows a
INNER JOIN permission_rows b ON a.rn = b.rn;

View File

@@ -0,0 +1,19 @@
DELETE FROM sys_api_permission
WHERE api_id IN (SELECT id FROM sys_apis WHERE group_name = '文章管理');
DELETE FROM sys_permissions
WHERE id IN (
SELECT permission_id FROM sys_api_permission
WHERE api_id IN (SELECT id FROM sys_apis WHERE group_name = '文章管理')
);
DELETE FROM sys_apis WHERE group_name = '文章管理';
DELETE FROM sys_menu_permission
WHERE menu_id IN (SELECT id FROM sys_menus WHERE path = '/post');
DELETE FROM sys_permissions WHERE code = 'post' AND type = 1;
DELETE FROM sys_menus WHERE path = '/post';
DROP TABLE IF EXISTS posts;

View File

@@ -0,0 +1,82 @@
CREATE TABLE posts
(
id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
title TEXT NOT NULL,
cover_id INTEGER,
slug TEXT UNIQUE NOT NULL,
content TEXT NOT NULL,
summary TEXT NOT NULL,
status SMALLINT NOT NULL DEFAULT 1,
view INTEGER DEFAULT 0,
sort INTEGER DEFAULT 0,
published_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ,
CHECK (status IN (0, 1, 2))
);
CREATE TRIGGER update_posts_updated_at
BEFORE UPDATE
ON posts
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
COMMENT ON COLUMN posts.id IS '主键ID';
COMMENT ON COLUMN posts.title IS '文章标题';
COMMENT ON COLUMN posts.cover_id IS '文章封面图片id';
COMMENT ON COLUMN posts.slug IS 'URL友好地址';
COMMENT ON COLUMN posts.content IS '文章正文';
COMMENT ON COLUMN posts.summary IS '摘要';
COMMENT ON COLUMN posts.status IS '状态 0:草稿 1:已发布 2:已下线';
COMMENT ON COLUMN posts.view IS '阅读量';
COMMENT ON COLUMN posts.sort IS '文章排序';
COMMENT ON COLUMN posts.published_at IS '发布时间';
COMMENT ON COLUMN posts.created_at IS '创建时间';
COMMENT ON COLUMN posts.updated_at IS '修改时间';
-- 批量插入菜单及关联权限
WITH input(name, path, component, type, sort, status, code)
AS (VALUES ('文章管理', '/post', '/blog/post/index', 1, 6, 1, 'post'),
('文章编辑', '/post/edit', '/blog/post-edit/index', 1, 7, 1, 'post:edit')
),
-- 插入菜单
inserted_menus AS (
INSERT INTO sys_menus (name, path, component, type, sort, status)
SELECT name, path, component, type, sort, status FROM input RETURNING id),
menu_rows AS (SELECT id, ROW_NUMBER() OVER () AS rn FROM inserted_menus),
-- 插入权限
inserted_permissions AS (
INSERT INTO sys_permissions (type, code)
SELECT 0, code FROM input RETURNING id),
permission_rows AS (SELECT id, ROW_NUMBER() OVER () AS rn FROM inserted_permissions)
-- 关联权限
INSERT
INTO sys_menu_permission(menu_id, permission_id)
SELECT m.id, p.id
FROM menu_rows m
JOIN permission_rows p ON m.rn = p.rn;
-- 批量插入接口及关联权限
WITH inserted_apis AS (
-- 插入菜单
INSERT INTO sys_apis (group_name, name, method, path)
VALUES ('文章管理', '文章列表', 'GET', '/admin/post'),
('文章管理', '创建文章', 'POST', '/admin/post'),
('文章管理', '修改文章', 'PATCH', '/admin/post/{id}'),
('文章管理', '删除文章', 'DELETE', '/admin/post/{id}')
RETURNING id),
api_rows AS (SELECT id, ROW_NUMBER() OVER () AS rn
FROM inserted_apis),
-- 创建权限
inserted_permissions AS (
INSERT INTO sys_permissions (type)
SELECT 1 FROM api_rows RETURNING id),
permission_rows AS (SELECT id, ROW_NUMBER() OVER () AS rn
FROM inserted_permissions)
INSERT
INTO sys_api_permission(api_id, permission_id)
SELECT a.id, b.id
FROM api_rows a
INNER JOIN permission_rows b ON a.rn = b.rn;

View File

@@ -0,0 +1,2 @@
DROP TABLE IF EXISTS categories;
DROP TABLE IF EXISTS post_category;

View File

@@ -0,0 +1,30 @@
-- 分类表
CREATE TABLE categories
(
id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
name TEXT NOT NULL,
code TEXT UNIQUE NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ
);
CREATE TRIGGER update_categories_updated_at
BEFORE UPDATE
ON categories
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
COMMENT ON COLUMN categories.id IS '分类ID';
COMMENT ON COLUMN categories.name IS '分类名称';
COMMENT ON COLUMN categories.code IS '分类编码';
-- 关联表
CREATE TABLE post_category
(
post_id INT NOT NULL,
category_id INT NOT NULL,
PRIMARY KEY (post_id, category_id)
);
COMMENT ON COLUMN post_category.post_id IS '文章ID';
COMMENT ON COLUMN post_category.category_id IS '分类ID';

View File

@@ -0,0 +1 @@
DROP TABLE IF EXISTS post_stats;

View File

@@ -0,0 +1,12 @@
CREATE TABLE post_stats
(
post_id INTEGER PRIMARY KEY,
view INTEGER NOT NULL DEFAULT 0
);
COMMENT ON COLUMN post_stats.post_id IS '文章id';
COMMENT ON COLUMN post_stats.view IS '文章阅读量';
-- 删除view字段
ALTER TABLE posts
DROP COLUMN IF EXISTS view;

View File

@@ -0,0 +1,2 @@
ALTER TABLE sys_menus
DROP COLUMN IF EXISTS icon;

View File

@@ -0,0 +1,2 @@
ALTER TABLE sys_menus
ADD COLUMN IF NOT EXISTS icon INTEGER;

View File

@@ -0,0 +1 @@
DROP TABLE IF EXISTS post_viewers;

View File

@@ -0,0 +1,12 @@
CREATE TABLE post_viewers
(
post_id INTEGER NOT NULL,
ip INET NOT NULL,
last_viewed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY(post_id, ip)
);
COMMENT ON COLUMN post_viewers.post_id IS '文章id';
COMMENT ON COLUMN post_viewers.ip IS '访问ip';
COMMENT ON COLUMN post_viewers.last_viewed_at IS '最后访问时间';