feat(lyra): 添加 PostgreSQL 初始化示例
- 定义用户、分类和链接表,包含密码哈希字段 - 添加数据约束、索引及更新时间触发器
This commit is contained in:
@@ -0,0 +1,130 @@
|
|||||||
|
-- Polaris 导航站 PostgreSQL 新库初始化示例(非幂等,仅执行一次)。
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
|
||||||
|
-- updated_at 维护函数
|
||||||
|
CREATE FUNCTION public.set_updated_at()
|
||||||
|
RETURNS trigger
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
AS $$
|
||||||
|
BEGIN
|
||||||
|
NEW.updated_at := now();
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$;
|
||||||
|
|
||||||
|
-- 用户表
|
||||||
|
CREATE TABLE public.users (
|
||||||
|
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||||
|
account text NOT NULL UNIQUE,
|
||||||
|
password_hash text NOT NULL,
|
||||||
|
display_name text NOT NULL,
|
||||||
|
created_at timestamptz NOT NULL DEFAULT now(),
|
||||||
|
updated_at timestamptz NOT NULL DEFAULT now(),
|
||||||
|
CONSTRAINT users_account_not_empty CHECK (btrim(account) <> ''),
|
||||||
|
CONSTRAINT users_display_name_not_empty CHECK (btrim(display_name) <> '')
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMENT ON TABLE public.users IS '后台管理用户表';
|
||||||
|
COMMENT ON COLUMN public.users.id IS '用户 ID';
|
||||||
|
COMMENT ON COLUMN public.users.account IS '登录账号,当前大小写敏感';
|
||||||
|
COMMENT ON COLUMN public.users.password_hash IS '密码哈希';
|
||||||
|
COMMENT ON COLUMN public.users.display_name IS '展示昵称';
|
||||||
|
COMMENT ON COLUMN public.users.created_at IS '创建时间';
|
||||||
|
COMMENT ON COLUMN public.users.updated_at IS '更新时间,触发器自动刷新';
|
||||||
|
|
||||||
|
-- 分类表
|
||||||
|
CREATE TABLE public.categories (
|
||||||
|
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||||
|
slug text NOT NULL UNIQUE,
|
||||||
|
title text NOT NULL,
|
||||||
|
description text NOT NULL DEFAULT '',
|
||||||
|
icon text NOT NULL,
|
||||||
|
sort integer NOT NULL DEFAULT 0,
|
||||||
|
created_at timestamptz NOT NULL DEFAULT now(),
|
||||||
|
updated_at timestamptz NOT NULL DEFAULT now(),
|
||||||
|
CONSTRAINT categories_slug_format CHECK (slug ~ '^[a-z0-9]+(-[a-z0-9]+)*$'),
|
||||||
|
CONSTRAINT categories_title_not_empty CHECK (btrim(title) <> '')
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMENT ON TABLE public.categories IS '链接分类表';
|
||||||
|
COMMENT ON COLUMN public.categories.id IS '分类 ID,内部关联用';
|
||||||
|
COMMENT ON COLUMN public.categories.slug IS '页面地址(路由)标识';
|
||||||
|
COMMENT ON COLUMN public.categories.title IS '分类标题';
|
||||||
|
COMMENT ON COLUMN public.categories.description IS '分类描述';
|
||||||
|
COMMENT ON COLUMN public.categories.icon IS '分类图标标识';
|
||||||
|
COMMENT ON COLUMN public.categories.sort IS '分类排序,越小越靠前';
|
||||||
|
COMMENT ON COLUMN public.categories.created_at IS '创建时间';
|
||||||
|
COMMENT ON COLUMN public.categories.updated_at IS '更新时间,触发器自动刷新';
|
||||||
|
|
||||||
|
-- 链接表
|
||||||
|
CREATE TABLE public.links (
|
||||||
|
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||||
|
category_id bigint NOT NULL,
|
||||||
|
title text NOT NULL,
|
||||||
|
description text NOT NULL DEFAULT '',
|
||||||
|
url text NOT NULL,
|
||||||
|
icon text,
|
||||||
|
keywords text[] NOT NULL DEFAULT ARRAY[]::text[],
|
||||||
|
sort integer NOT NULL DEFAULT 0,
|
||||||
|
pinned boolean NOT NULL DEFAULT false,
|
||||||
|
pinned_sort integer,
|
||||||
|
created_at timestamptz NOT NULL DEFAULT now(),
|
||||||
|
updated_at timestamptz NOT NULL DEFAULT now(),
|
||||||
|
CONSTRAINT links_category_fk
|
||||||
|
FOREIGN KEY (category_id) REFERENCES public.categories (id)
|
||||||
|
ON DELETE RESTRICT,
|
||||||
|
CONSTRAINT links_title_not_empty CHECK (btrim(title) <> ''),
|
||||||
|
CONSTRAINT links_url_not_empty CHECK (btrim(url) <> ''),
|
||||||
|
CONSTRAINT links_pinned_sort_consistent CHECK (pinned = (pinned_sort IS NOT NULL)),
|
||||||
|
CONSTRAINT links_keywords_one_dimension
|
||||||
|
CHECK (cardinality(keywords) = 0 OR array_ndims(keywords) = 1),
|
||||||
|
CONSTRAINT links_keywords_no_null_elements
|
||||||
|
CHECK (array_position(keywords, NULL) IS NULL)
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMENT ON TABLE public.links IS '链接表,归属分类';
|
||||||
|
COMMENT ON COLUMN public.links.id IS '链接 ID';
|
||||||
|
COMMENT ON COLUMN public.links.category_id IS '所属分类 ID,删分类前需先处理链接';
|
||||||
|
COMMENT ON COLUMN public.links.title IS '链接标题';
|
||||||
|
COMMENT ON COLUMN public.links.description IS '链接描述';
|
||||||
|
COMMENT ON COLUMN public.links.url IS '链接地址,应用层校验,去尾斜杠后全局唯一';
|
||||||
|
COMMENT ON COLUMN public.links.icon IS '链接图标,可空,前端回退';
|
||||||
|
COMMENT ON COLUMN public.links.keywords IS '搜索关键词数组,供本地搜索匹配';
|
||||||
|
COMMENT ON COLUMN public.links.sort IS '分类内排序,越小越靠前';
|
||||||
|
COMMENT ON COLUMN public.links.pinned IS '首页快捷入口(手动固定),非点击排行';
|
||||||
|
COMMENT ON COLUMN public.links.pinned_sort IS '首页快捷入口顺序,越小越靠前';
|
||||||
|
COMMENT ON COLUMN public.links.created_at IS '创建时间';
|
||||||
|
COMMENT ON COLUMN public.links.updated_at IS '更新时间,触发器自动刷新';
|
||||||
|
|
||||||
|
-- 索引
|
||||||
|
CREATE INDEX categories_order_idx
|
||||||
|
ON public.categories (sort, id);
|
||||||
|
|
||||||
|
CREATE INDEX links_category_order_idx
|
||||||
|
ON public.links (category_id, sort, id);
|
||||||
|
|
||||||
|
CREATE INDEX links_pinned_order_idx
|
||||||
|
ON public.links (pinned_sort, id)
|
||||||
|
WHERE pinned;
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX links_normalized_url_unique
|
||||||
|
ON public.links (rtrim(url, '/'));
|
||||||
|
|
||||||
|
-- 触发器
|
||||||
|
CREATE TRIGGER users_set_updated_at
|
||||||
|
BEFORE UPDATE ON public.users
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION public.set_updated_at();
|
||||||
|
|
||||||
|
CREATE TRIGGER categories_set_updated_at
|
||||||
|
BEFORE UPDATE ON public.categories
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION public.set_updated_at();
|
||||||
|
|
||||||
|
CREATE TRIGGER links_set_updated_at
|
||||||
|
BEFORE UPDATE ON public.links
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION public.set_updated_at();
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
Reference in New Issue
Block a user