diff --git a/go/projects/lyra/docs/demo.sql b/go/projects/lyra/docs/demo.sql new file mode 100644 index 0000000..0f2aa58 --- /dev/null +++ b/go/projects/lyra/docs/demo.sql @@ -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;