fix
Some checks failed
部署后端服务 / 🧪 测试后端 (push) Failing after 5m8s
部署后端服务 / 🚀 构建并部署 (push) Has been skipped
部署后端服务 / 🔄 回滚部署 (push) Has been skipped

This commit is contained in:
xujiang
2025-07-10 18:09:11 +08:00
parent 35004f224e
commit 010fe2a8c7
96 changed files with 23709 additions and 19 deletions

View File

@ -0,0 +1,54 @@
-- +migrate Up
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
name VARCHAR(100),
avatar VARCHAR(500),
bio TEXT,
website VARCHAR(200),
location VARCHAR(100),
role VARCHAR(20) DEFAULT 'user' CHECK (role IN ('user', 'admin', 'photographer')),
is_active BOOLEAN DEFAULT true,
is_public BOOLEAN DEFAULT true,
email_verified BOOLEAN DEFAULT false,
last_login TIMESTAMP,
login_count INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP
);
-- 创建索引
CREATE INDEX idx_users_username ON users(username);
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_role ON users(role);
CREATE INDEX idx_users_is_active ON users(is_active);
CREATE INDEX idx_users_is_public ON users(is_public);
CREATE INDEX idx_users_email_verified ON users(email_verified);
CREATE INDEX idx_users_created_at ON users(created_at);
CREATE INDEX idx_users_deleted_at ON users(deleted_at) WHERE deleted_at IS NOT NULL;
-- 添加更新时间触发器
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$ language 'plpgsql';
CREATE TRIGGER update_users_updated_at BEFORE UPDATE ON users
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- 插入默认管理员用户 (密码: admin123)
INSERT INTO users (username, email, password, name, role, email_verified) VALUES
('admin', 'admin@photography.com', '$2a$10$D4Zz6m3j1YJzp8Y7zW4l2OXcQ5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0', '管理员', 'admin', true);
-- +migrate Down
DROP TRIGGER IF EXISTS update_users_updated_at ON users;
DROP FUNCTION IF EXISTS update_updated_at_column();
DROP TABLE IF EXISTS users;

View File

@ -0,0 +1,33 @@
-- +migrate Up
CREATE TABLE categories (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
description TEXT,
parent_id INTEGER REFERENCES categories(id),
color VARCHAR(7) DEFAULT '#3b82f6',
cover_image VARCHAR(500),
sort INTEGER DEFAULT 0,
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP
);
-- 创建索引
CREATE INDEX idx_categories_parent_id ON categories(parent_id);
CREATE INDEX idx_categories_is_active ON categories(is_active);
CREATE INDEX idx_categories_sort ON categories(sort);
CREATE INDEX idx_categories_deleted_at ON categories(deleted_at);
-- 插入默认分类
INSERT INTO categories (name, description, color, sort) VALUES
('风景摄影', '自然风景摄影作品', '#10b981', 1),
('人像摄影', '人物肖像摄影作品', '#f59e0b', 2),
('街头摄影', '街头纪实摄影作品', '#ef4444', 3),
('建筑摄影', '建筑和城市摄影作品', '#3b82f6', 4),
('抽象摄影', '抽象艺术摄影作品', '#8b5cf6', 5);
-- +migrate Down
DROP TABLE IF EXISTS categories;

View File

@ -0,0 +1,35 @@
-- +migrate Up
CREATE TABLE tags (
id SERIAL PRIMARY KEY,
name VARCHAR(50) UNIQUE NOT NULL,
color VARCHAR(7) DEFAULT '#6b7280',
use_count INTEGER DEFAULT 0,
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP
);
-- 创建索引
CREATE INDEX idx_tags_name ON tags(name);
CREATE INDEX idx_tags_use_count ON tags(use_count);
CREATE INDEX idx_tags_is_active ON tags(is_active);
CREATE INDEX idx_tags_deleted_at ON tags(deleted_at);
-- 插入默认标签
INSERT INTO tags (name, color) VALUES
('自然', '#10b981'),
('人物', '#f59e0b'),
('城市', '#3b82f6'),
('夜景', '#1f2937'),
('黑白', '#6b7280'),
('色彩', '#ec4899'),
('构图', '#8b5cf6'),
('光影', '#f97316'),
('街头', '#ef4444'),
('建筑', '#0891b2');
-- +migrate Down
DROP TABLE IF EXISTS tags;

View File

@ -0,0 +1,64 @@
-- +migrate Up
CREATE TABLE photos (
id SERIAL PRIMARY KEY,
title VARCHAR(200) NOT NULL,
description TEXT,
filename VARCHAR(255) NOT NULL,
original_url VARCHAR(500) NOT NULL,
thumbnail_url VARCHAR(500),
medium_url VARCHAR(500),
large_url VARCHAR(500),
file_size BIGINT,
mime_type VARCHAR(100),
width INTEGER,
height INTEGER,
camera_make VARCHAR(100),
camera_model VARCHAR(100),
lens_model VARCHAR(100),
focal_length DECIMAL(5,2),
aperture DECIMAL(3,1),
shutter_speed VARCHAR(20),
iso INTEGER,
taken_at TIMESTAMP,
location_name VARCHAR(200),
latitude DECIMAL(10,8),
longitude DECIMAL(11,8),
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
album_id INTEGER,
category_id INTEGER,
is_public BOOLEAN DEFAULT true,
is_featured BOOLEAN DEFAULT false,
view_count INTEGER DEFAULT 0,
like_count INTEGER DEFAULT 0,
download_count INTEGER DEFAULT 0,
sort_order INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP
);
-- 创建索引
CREATE INDEX idx_photos_user_id ON photos(user_id);
CREATE INDEX idx_photos_album_id ON photos(album_id);
CREATE INDEX idx_photos_category_id ON photos(category_id);
CREATE INDEX idx_photos_is_public ON photos(is_public);
CREATE INDEX idx_photos_is_featured ON photos(is_featured);
CREATE INDEX idx_photos_taken_at ON photos(taken_at);
CREATE INDEX idx_photos_created_at ON photos(created_at);
CREATE INDEX idx_photos_view_count ON photos(view_count);
CREATE INDEX idx_photos_like_count ON photos(like_count);
CREATE INDEX idx_photos_sort_order ON photos(sort_order);
CREATE INDEX idx_photos_deleted_at ON photos(deleted_at) WHERE deleted_at IS NOT NULL;
-- 地理位置索引
CREATE INDEX idx_photos_location ON photos(latitude, longitude) WHERE latitude IS NOT NULL AND longitude IS NOT NULL;
-- 添加更新时间触发器
CREATE TRIGGER update_photos_updated_at BEFORE UPDATE ON photos
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- +migrate Down
DROP TRIGGER IF EXISTS update_photos_updated_at ON photos;
DROP TABLE IF EXISTS photos;

View File

@ -0,0 +1,73 @@
-- +migrate Up
CREATE TABLE albums (
id SERIAL PRIMARY KEY,
title VARCHAR(200) NOT NULL,
description TEXT,
slug VARCHAR(255) UNIQUE,
cover_photo_id INTEGER REFERENCES photos(id) ON DELETE SET NULL,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
category_id INTEGER REFERENCES categories(id) ON DELETE SET NULL,
is_public BOOLEAN DEFAULT true,
is_featured BOOLEAN DEFAULT false,
password VARCHAR(255),
view_count INTEGER DEFAULT 0,
like_count INTEGER DEFAULT 0,
photo_count INTEGER DEFAULT 0,
sort_order INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP
);
-- 创建索引
CREATE INDEX idx_albums_user_id ON albums(user_id);
CREATE INDEX idx_albums_category_id ON albums(category_id);
CREATE INDEX idx_albums_cover_photo_id ON albums(cover_photo_id);
CREATE INDEX idx_albums_slug ON albums(slug);
CREATE INDEX idx_albums_is_public ON albums(is_public);
CREATE INDEX idx_albums_is_featured ON albums(is_featured);
CREATE INDEX idx_albums_created_at ON albums(created_at);
CREATE INDEX idx_albums_view_count ON albums(view_count);
CREATE INDEX idx_albums_like_count ON albums(like_count);
CREATE INDEX idx_albums_photo_count ON albums(photo_count);
CREATE INDEX idx_albums_sort_order ON albums(sort_order);
CREATE INDEX idx_albums_deleted_at ON albums(deleted_at) WHERE deleted_at IS NOT NULL;
-- 添加更新时间触发器
CREATE TRIGGER update_albums_updated_at BEFORE UPDATE ON albums
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- 添加 slug 自动生成触发器
CREATE OR REPLACE FUNCTION generate_album_slug()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.slug IS NULL OR NEW.slug = '' THEN
NEW.slug = lower(regexp_replace(NEW.title, '[^a-zA-Z0-9]+', '-', 'g'));
NEW.slug = trim(both '-' from NEW.slug);
-- 确保 slug 唯一
DECLARE
counter INTEGER := 0;
base_slug VARCHAR(255);
BEGIN
base_slug := NEW.slug;
WHILE EXISTS (SELECT 1 FROM albums WHERE slug = NEW.slug AND id != COALESCE(NEW.id, 0)) LOOP
counter := counter + 1;
NEW.slug := base_slug || '-' || counter;
END LOOP;
END;
END IF;
RETURN NEW;
END;
$$ language 'plpgsql';
CREATE TRIGGER generate_albums_slug BEFORE INSERT OR UPDATE ON albums
FOR EACH ROW EXECUTE FUNCTION generate_album_slug();
-- +migrate Down
DROP TRIGGER IF EXISTS generate_albums_slug ON albums;
DROP FUNCTION IF EXISTS generate_album_slug();
DROP TRIGGER IF EXISTS update_albums_updated_at ON albums;
DROP TABLE IF EXISTS albums;

View File

@ -0,0 +1,111 @@
-- +migrate Up
-- 添加照片表的外键约束
ALTER TABLE photos
ADD CONSTRAINT fk_photos_album_id
FOREIGN KEY (album_id) REFERENCES albums(id) ON DELETE SET NULL;
ALTER TABLE photos
ADD CONSTRAINT fk_photos_category_id
FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE SET NULL;
-- 添加相册表的外键约束(如果还没有的话)
-- 这些约束在创建相册表时可能已经存在,这里做一个保险
-- 添加一些有用的触发器和函数
-- 创建照片计数更新触发器(用于相册和分类)
CREATE OR REPLACE FUNCTION update_photo_counts()
RETURNS TRIGGER AS $$
BEGIN
IF TG_OP = 'INSERT' THEN
-- 更新相册照片数量
IF NEW.album_id IS NOT NULL THEN
UPDATE albums SET photo_count = photo_count + 1 WHERE id = NEW.album_id;
END IF;
-- 更新分类照片数量
IF NEW.category_id IS NOT NULL THEN
UPDATE categories SET photo_count = photo_count + 1 WHERE id = NEW.category_id;
END IF;
RETURN NEW;
ELSIF TG_OP = 'DELETE' THEN
-- 更新相册照片数量
IF OLD.album_id IS NOT NULL THEN
UPDATE albums SET photo_count = photo_count - 1 WHERE id = OLD.album_id;
END IF;
-- 更新分类照片数量
IF OLD.category_id IS NOT NULL THEN
UPDATE categories SET photo_count = photo_count - 1 WHERE id = OLD.category_id;
END IF;
RETURN OLD;
ELSIF TG_OP = 'UPDATE' THEN
-- 处理相册变更
IF OLD.album_id IS DISTINCT FROM NEW.album_id THEN
IF OLD.album_id IS NOT NULL THEN
UPDATE albums SET photo_count = photo_count - 1 WHERE id = OLD.album_id;
END IF;
IF NEW.album_id IS NOT NULL THEN
UPDATE albums SET photo_count = photo_count + 1 WHERE id = NEW.album_id;
END IF;
END IF;
-- 处理分类变更
IF OLD.category_id IS DISTINCT FROM NEW.category_id THEN
IF OLD.category_id IS NOT NULL THEN
UPDATE categories SET photo_count = photo_count - 1 WHERE id = OLD.category_id;
END IF;
IF NEW.category_id IS NOT NULL THEN
UPDATE categories SET photo_count = photo_count + 1 WHERE id = NEW.category_id;
END IF;
END IF;
RETURN NEW;
END IF;
RETURN NULL;
END;
$$ language 'plpgsql';
-- 创建触发器
CREATE TRIGGER trigger_update_photo_counts
AFTER INSERT OR UPDATE OR DELETE ON photos
FOR EACH ROW EXECUTE FUNCTION update_photo_counts();
-- 创建相册计数更新触发器(用于分类)
CREATE OR REPLACE FUNCTION update_album_counts()
RETURNS TRIGGER AS $$
BEGIN
IF TG_OP = 'INSERT' THEN
IF NEW.category_id IS NOT NULL THEN
UPDATE categories SET album_count = album_count + 1 WHERE id = NEW.category_id;
END IF;
RETURN NEW;
ELSIF TG_OP = 'DELETE' THEN
IF OLD.category_id IS NOT NULL THEN
UPDATE categories SET album_count = album_count - 1 WHERE id = OLD.category_id;
END IF;
RETURN OLD;
ELSIF TG_OP = 'UPDATE' THEN
IF OLD.category_id IS DISTINCT FROM NEW.category_id THEN
IF OLD.category_id IS NOT NULL THEN
UPDATE categories SET album_count = album_count - 1 WHERE id = OLD.category_id;
END IF;
IF NEW.category_id IS NOT NULL THEN
UPDATE categories SET album_count = album_count + 1 WHERE id = NEW.category_id;
END IF;
END IF;
RETURN NEW;
END IF;
RETURN NULL;
END;
$$ language 'plpgsql';
CREATE TRIGGER trigger_update_album_counts
AFTER INSERT OR UPDATE OR DELETE ON albums
FOR EACH ROW EXECUTE FUNCTION update_album_counts();
-- +migrate Down
DROP TRIGGER IF EXISTS trigger_update_album_counts ON albums;
DROP FUNCTION IF EXISTS update_album_counts();
DROP TRIGGER IF EXISTS trigger_update_photo_counts ON photos;
DROP FUNCTION IF EXISTS update_photo_counts();
ALTER TABLE photos DROP CONSTRAINT IF EXISTS fk_photos_category_id;
ALTER TABLE photos DROP CONSTRAINT IF EXISTS fk_photos_album_id;