feat(auth): 添加认证模块和图片批注功能(项目初始化)
- 实现用户登录、登出、密码修改等认证功能 - 添加会话管理和权限控制中间件 - 创建图片批注组件和相关API路由 - 实现批注的增删改查功能 - 添加Docker和Git忽略配置文件 - 创建系统架构文档和开发约定说明 - 集成认证模块到前端应用路由中
This commit is contained in:
203
db/postgres/schema.sql
Normal file
203
db/postgres/schema.sql
Normal file
@@ -0,0 +1,203 @@
|
||||
BEGIN;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS operation_groups (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'disabled')),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
group_id BIGINT REFERENCES operation_groups(id) ON DELETE RESTRICT,
|
||||
username TEXT NOT NULL UNIQUE,
|
||||
display_name TEXT NOT NULL,
|
||||
password_hash TEXT NOT NULL,
|
||||
role TEXT NOT NULL CHECK (role IN ('platform_admin', 'group_admin', 'operator')),
|
||||
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'disabled')),
|
||||
must_change_password BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
last_login_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS one_group_admin_per_group ON users(group_id) WHERE role = 'group_admin';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS projects (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
group_id BIGINT NOT NULL REFERENCES operation_groups(id) ON DELETE RESTRICT,
|
||||
name TEXT NOT NULL,
|
||||
slug TEXT NOT NULL UNIQUE,
|
||||
client_description TEXT NOT NULL DEFAULT '',
|
||||
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'closed', 'archived')),
|
||||
access_password_hash TEXT NOT NULL DEFAULT '',
|
||||
customer_access_enabled BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
access_expires_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS collections (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
project_id BIGINT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
client_description TEXT NOT NULL DEFAULT '',
|
||||
status TEXT NOT NULL DEFAULT 'reviewing' CHECK (status IN ('draft', 'reviewing', 'completed', 'archived')),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (project_id, name)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS notes (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
collection_id BIGINT NOT NULL REFERENCES collections(id) ON DELETE CASCADE,
|
||||
title TEXT NOT NULL,
|
||||
description TEXT NOT NULL DEFAULT '',
|
||||
tags TEXT NOT NULL DEFAULT '[]',
|
||||
review_status TEXT NOT NULL DEFAULT 'pending' CHECK (review_status IN ('draft', 'pending', 'changes_requested', 'approved')),
|
||||
version_number INTEGER NOT NULL DEFAULT 1 CHECK (version_number > 0),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS images (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
note_id BIGINT NOT NULL REFERENCES notes(id) ON DELETE CASCADE,
|
||||
url TEXT NOT NULL,
|
||||
width INTEGER NOT NULL DEFAULT 0,
|
||||
height INTEGER NOT NULL DEFAULT 0,
|
||||
order_index INTEGER NOT NULL DEFAULT 0,
|
||||
storage_provider TEXT NOT NULL DEFAULT 'local' CHECK (storage_provider IN ('local', 'tencent_cos')),
|
||||
storage_key TEXT NOT NULL DEFAULT '',
|
||||
version_number INTEGER NOT NULL DEFAULT 1 CHECK (version_number > 0)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS annotations (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
image_id BIGINT NOT NULL REFERENCES images(id) ON DELETE CASCADE,
|
||||
x DOUBLE PRECISION NOT NULL CHECK (x BETWEEN 0 AND 1),
|
||||
y DOUBLE PRECISION NOT NULL CHECK (y BETWEEN 0 AND 1),
|
||||
content TEXT NOT NULL,
|
||||
author_name TEXT NOT NULL DEFAULT '客户',
|
||||
status TEXT NOT NULL DEFAULT 'open' CHECK (status IN ('open', 'resolved', 'confirmed')),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS work_comments (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
note_id BIGINT NOT NULL REFERENCES notes(id) ON DELETE CASCADE,
|
||||
content TEXT NOT NULL,
|
||||
author_name TEXT NOT NULL DEFAULT '客户',
|
||||
author_role TEXT NOT NULL DEFAULT 'client' CHECK (author_role IN ('client', 'operator')),
|
||||
status TEXT NOT NULL DEFAULT 'open' CHECK (status IN ('open', 'resolved', 'confirmed')),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS text_annotations (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
note_id BIGINT NOT NULL REFERENCES notes(id) ON DELETE CASCADE,
|
||||
version_number INTEGER NOT NULL CHECK (version_number > 0),
|
||||
target TEXT NOT NULL CHECK (target IN ('title', 'description')),
|
||||
content TEXT NOT NULL,
|
||||
author_name TEXT NOT NULL DEFAULT '客户',
|
||||
status TEXT NOT NULL DEFAULT 'open' CHECK (status IN ('open', 'resolved', 'confirmed')),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_text_annotations_note_id ON text_annotations(note_id, version_number);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS work_versions (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
note_id BIGINT NOT NULL REFERENCES notes(id) ON DELETE CASCADE,
|
||||
version_number INTEGER NOT NULL CHECK (version_number > 0),
|
||||
title TEXT NOT NULL,
|
||||
description TEXT NOT NULL DEFAULT '',
|
||||
tags TEXT NOT NULL DEFAULT '[]',
|
||||
review_status TEXT NOT NULL DEFAULT 'pending' CHECK (review_status IN ('draft', 'pending', 'changes_requested', 'approved')),
|
||||
created_by BIGINT REFERENCES users(id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (note_id, version_number)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS review_events (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
note_id BIGINT NOT NULL REFERENCES notes(id) ON DELETE CASCADE,
|
||||
version_number INTEGER NOT NULL,
|
||||
event_type TEXT NOT NULL CHECK (event_type IN ('submitted', 'approved', 'changes_requested', 'reopened')),
|
||||
from_status TEXT,
|
||||
to_status TEXT NOT NULL,
|
||||
reason TEXT NOT NULL DEFAULT '',
|
||||
actor_name TEXT NOT NULL,
|
||||
actor_role TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sessions (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
token_hash TEXT NOT NULL UNIQUE,
|
||||
expires_at TIMESTAMPTZ NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS customer_sessions (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
project_id BIGINT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
||||
reviewer_name TEXT NOT NULL,
|
||||
token_hash TEXT NOT NULL UNIQUE,
|
||||
expires_at TIMESTAMPTZ NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS audit_logs (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
group_id BIGINT REFERENCES operation_groups(id) ON DELETE SET NULL,
|
||||
user_id BIGINT REFERENCES users(id) ON DELETE SET NULL,
|
||||
action TEXT NOT NULL,
|
||||
entity_type TEXT NOT NULL,
|
||||
entity_id BIGINT,
|
||||
detail TEXT NOT NULL DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS api_keys (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
group_id BIGINT REFERENCES operation_groups(id) ON DELETE RESTRICT,
|
||||
project_id BIGINT REFERENCES projects(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
key_prefix TEXT NOT NULL UNIQUE,
|
||||
key_hash TEXT NOT NULL UNIQUE,
|
||||
scope TEXT NOT NULL CHECK (scope IN ('platform', 'project')),
|
||||
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'revoked')),
|
||||
created_by BIGINT NOT NULL REFERENCES users(id) ON DELETE RESTRICT,
|
||||
last_used_at TIMESTAMPTZ,
|
||||
revoked_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS storage_configs (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
provider TEXT NOT NULL DEFAULT 'tencent_cos',
|
||||
region TEXT NOT NULL,
|
||||
bucket TEXT NOT NULL,
|
||||
public_base_url TEXT NOT NULL DEFAULT '',
|
||||
cdn_domain TEXT NOT NULL DEFAULT '',
|
||||
path_prefix TEXT NOT NULL DEFAULT 'delivery-desk',
|
||||
secret_id_encrypted TEXT NOT NULL,
|
||||
secret_key_encrypted TEXT NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'draft' CHECK (status IN ('draft', 'active', 'archived')),
|
||||
test_status TEXT NOT NULL DEFAULT 'untested' CHECK (test_status IN ('untested', 'passed', 'failed')),
|
||||
test_message TEXT NOT NULL DEFAULT '',
|
||||
last_tested_at TIMESTAMPTZ,
|
||||
created_by BIGINT NOT NULL REFERENCES users(id) ON DELETE RESTRICT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
activated_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS one_active_storage_config ON storage_configs ((status)) WHERE status = 'active';
|
||||
CREATE INDEX IF NOT EXISTS collections_project_id_idx ON collections(project_id);
|
||||
CREATE INDEX IF NOT EXISTS notes_collection_id_idx ON notes(collection_id);
|
||||
CREATE INDEX IF NOT EXISTS images_note_version_idx ON images(note_id, version_number, order_index);
|
||||
CREATE INDEX IF NOT EXISTS annotations_image_id_idx ON annotations(image_id);
|
||||
CREATE INDEX IF NOT EXISTS comments_note_id_idx ON work_comments(note_id);
|
||||
CREATE INDEX IF NOT EXISTS customer_sessions_project_id_idx ON customer_sessions(project_id);
|
||||
CREATE INDEX IF NOT EXISTS review_events_note_version_idx ON review_events(note_id, version_number);
|
||||
CREATE INDEX IF NOT EXISTS audit_logs_group_id_idx ON audit_logs(group_id);
|
||||
|
||||
COMMIT;
|
||||
Reference in New Issue
Block a user