add: some user api and all api desc

This commit is contained in:
wwweww
2026-02-27 19:17:01 +08:00
parent a0c720eb2f
commit 5930fb0dde
156 changed files with 9457 additions and 1086 deletions
+9
View File
@@ -0,0 +1,9 @@
CREATE TABLE comment_likes (
comment_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (comment_id, user_id)
);
CREATE INDEX idx_comment_likes_lookup ON comment_likes(user_id, comment_id);
+21
View File
@@ -0,0 +1,21 @@
CREATE TABLE comments (
id BIGINT PRIMARY KEY,
post_id BIGINT NOT NULL REFERENCES posts(id),
author_id BIGINT NOT NULL,
content TEXT NOT NULL,
like_count INT DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMPTZ
);
-- 基础索引
CREATE INDEX idx_comments_post ON comments(post_id, created_at) WHERE deleted_at IS NULL;
CREATE INDEX idx_comments_author ON comments(author_id, created_at DESC) WHERE deleted_at IS NULL;
-- 三元组索引用于评论内容搜索
CREATE INDEX idx_comments_content_trgm ON comments USING gin(content gin_trgm_ops)
WHERE deleted_at IS NULL;
-- 热门评论索引
CREATE INDEX idx_comments_post_likes ON comments(post_id, like_count DESC, created_at)
WHERE deleted_at IS NULL;
+14
View File
@@ -0,0 +1,14 @@
CREATE TABLE post_likes (
post_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
-- 复合主键,防止重复点赞
PRIMARY KEY (post_id, user_id)
);
-- [核心索引] 优化 "feed流中判断我是否已赞" (user_id = ? AND post_id IN (...))
CREATE INDEX idx_post_likes_lookup ON post_likes(user_id, post_id);
-- [核心索引] 优化 "我赞过的帖子" 列表
CREATE INDEX idx_post_likes_user_timeline ON post_likes(user_id, created_at DESC);
+58
View File
@@ -0,0 +1,58 @@
CREATE TABLE posts (
id BIGINT PRIMARY KEY,
author_id BIGINT NOT NULL,
author_role VARCHAR(20) NOT NULL,
title VARCHAR(500) NOT NULL,
content TEXT NOT NULL,
images TEXT[] DEFAULT ARRAY[]::TEXT[],
tags TEXT[] DEFAULT ARRAY[]::TEXT[],
linked_order_id BIGINT,
quoted_post_id BIGINT REFERENCES posts(id),
like_count INT DEFAULT 0,
comment_count INT DEFAULT 0,
pinned BOOLEAN DEFAULT FALSE,
search_text TEXT GENERATED ALWAYS AS (
title || ' ' || content
) STORED,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMPTZ
);
-- 基础索引
CREATE INDEX idx_posts_author ON posts(author_id, created_at DESC) WHERE deleted_at IS NULL;
CREATE INDEX idx_posts_created ON posts(created_at DESC) WHERE deleted_at IS NULL;
-- 三元组索引用于帖子内容搜索
CREATE INDEX idx_posts_search_trgm ON posts USING gin(search_text gin_trgm_ops)
WHERE deleted_at IS NULL;
-- 全文搜索索引
CREATE INDEX idx_posts_fulltext ON posts USING gin(
to_tsvector('simple', title || ' ' || content)
) WHERE deleted_at IS NULL;
-- 标签 GIN 索引
CREATE INDEX idx_posts_tags_gin ON posts USING gin(tags) WHERE deleted_at IS NULL;
-- 复合索引优化热门排序
CREATE INDEX idx_posts_hot_score ON posts(
(like_count * 2 + comment_count) DESC,
created_at DESC
) WHERE deleted_at IS NULL;
-- 置顶+时间索引
CREATE INDEX idx_posts_pinned_created ON posts(author_id, pinned DESC, created_at DESC)
WHERE deleted_at IS NULL;
-- 关联订单索引
CREATE INDEX idx_posts_linked_order ON posts(linked_order_id)
WHERE linked_order_id IS NOT NULL AND deleted_at IS NULL;
-- 图片数组索引
CREATE INDEX idx_posts_images ON posts USING gin(images) WHERE deleted_at IS NULL;
CREATE TRIGGER trigger_posts_updated_at
BEFORE UPDATE ON posts
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
+17
View File
@@ -0,0 +1,17 @@
CREATE TABLE dispute_timeline (
id BIGINT PRIMARY KEY,
dispute_id BIGINT NOT NULL REFERENCES disputes(id),
event_type VARCHAR(30) NOT NULL,
actor_id BIGINT,
actor_name VARCHAR(100),
details JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT chk_event_type CHECK (event_type IN (
'created', 'response', 'reviewing', 'resolved', 'appealed'
))
);
CREATE INDEX idx_timeline_dispute ON dispute_timeline(dispute_id, created_at);
CREATE INDEX idx_timeline_dispute_created ON dispute_timeline(dispute_id, created_at);
CREATE INDEX idx_timeline_details ON dispute_timeline USING gin(details);
+49
View File
@@ -0,0 +1,49 @@
CREATE TABLE disputes (
id BIGINT PRIMARY KEY,
order_id BIGINT NOT NULL UNIQUE,
initiator_id BIGINT NOT NULL,
initiator_name VARCHAR(100) NOT NULL,
respondent_id BIGINT NOT NULL,
reason TEXT NOT NULL,
evidence TEXT[] DEFAULT ARRAY[]::TEXT[],
status VARCHAR(20) NOT NULL DEFAULT 'open',
result VARCHAR(30),
respondent_reason TEXT,
respondent_evidence TEXT[] DEFAULT ARRAY[]::TEXT[],
appeal_reason TEXT,
appealed_at TIMESTAMPTZ,
resolved_by BIGINT,
resolved_at TIMESTAMPTZ,
search_text TEXT GENERATED ALWAYS AS (
initiator_name || ' ' || reason || ' ' || coalesce(respondent_reason, '') || ' ' || coalesce(appeal_reason, '')
) STORED,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT chk_dispute_status CHECK (status IN ('open', 'reviewing', 'resolved', 'appealed')),
CONSTRAINT chk_dispute_result CHECK (result IS NULL OR result IN ('full_refund', 'full_payment', 'partial_refund'))
);
-- 基础索引
CREATE INDEX idx_disputes_order ON disputes(order_id);
CREATE INDEX idx_disputes_status ON disputes(status, created_at DESC);
CREATE INDEX idx_disputes_initiator ON disputes(initiator_id);
-- 三元组索引用于争议内容搜索
CREATE INDEX idx_disputes_search_trgm ON disputes USING gin(search_text gin_trgm_ops);
-- 复合索引优化状态查询
CREATE INDEX idx_disputes_status_created ON disputes(status, created_at DESC);
-- 参与者索引
CREATE INDEX idx_disputes_initiator_status ON disputes(initiator_id, status, created_at DESC);
CREATE INDEX idx_disputes_respondent_status ON disputes(respondent_id, status, created_at DESC);
-- 数组索引优化证据查询
CREATE INDEX idx_disputes_evidence ON disputes USING gin(evidence);
CREATE INDEX idx_disputes_respondent_evidence ON disputes USING gin(respondent_evidence);
CREATE TRIGGER trigger_disputes_updated_at
BEFORE UPDATE ON disputes
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
+30
View File
@@ -0,0 +1,30 @@
CREATE TABLE games (
id BIGINT PRIMARY KEY,
name VARCHAR(100) NOT NULL UNIQUE,
icon TEXT NOT NULL,
category VARCHAR(50) NOT NULL,
sort_order INT DEFAULT 0,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- 基础索引
CREATE INDEX idx_games_category ON games(category) WHERE is_active = TRUE;
CREATE INDEX idx_games_sort ON games(sort_order, id) WHERE is_active = TRUE;
-- 三元组索引用于游戏名称模糊搜索
CREATE INDEX idx_games_name_trgm ON games USING gin(name gin_trgm_ops) WHERE is_active = TRUE;
-- 复合索引优化分类+排序查询
CREATE INDEX idx_games_category_sort ON games(category, sort_order, id) WHERE is_active = TRUE;
-- 全文搜索索引
CREATE INDEX idx_games_fulltext ON games USING gin(
to_tsvector('simple', name || ' ' || category)
) WHERE is_active = TRUE;
CREATE TRIGGER trigger_games_updated_at
BEFORE UPDATE ON games
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
+49
View File
@@ -0,0 +1,49 @@
CREATE TABLE player_services (
id BIGINT PRIMARY KEY,
player_id BIGINT NOT NULL REFERENCES players(id),
game_id BIGINT NOT NULL,
title VARCHAR(200) NOT NULL,
description TEXT,
price DECIMAL(10,2) NOT NULL,
unit VARCHAR(20) NOT NULL,
rank_range VARCHAR(100),
availability TEXT[] DEFAULT ARRAY[]::TEXT[],
rating DECIMAL(3,2) DEFAULT 5.00,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT chk_price_positive CHECK (price > 0),
CONSTRAINT chk_service_rating CHECK (rating >= 0 AND rating <= 5)
);
-- 基础索引
CREATE INDEX idx_services_player ON player_services(player_id) WHERE is_active = TRUE;
CREATE INDEX idx_services_game ON player_services(game_id) WHERE is_active = TRUE;
CREATE INDEX idx_services_price ON player_services(price);
-- 三元组索引用于服务标题模糊搜索
CREATE INDEX idx_services_title_trgm ON player_services USING gin(title gin_trgm_ops)
WHERE is_active = TRUE;
-- 全文搜索索引
CREATE INDEX idx_services_fulltext ON player_services USING gin(
to_tsvector('simple', title || ' ' || coalesce(description, ''))
) WHERE is_active = TRUE;
-- 复合索引优化价格区间查询
CREATE INDEX idx_services_game_price ON player_services(game_id, price, rating DESC)
WHERE is_active = TRUE;
-- 打手+游戏复合索引
CREATE INDEX idx_services_player_game ON player_services(player_id, game_id)
WHERE is_active = TRUE;
-- GIN 索引优化时间段查询
CREATE INDEX idx_services_availability ON player_services USING gin(availability)
WHERE is_active = TRUE;
CREATE TRIGGER trigger_services_updated_at
BEFORE UPDATE ON player_services
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
+40
View File
@@ -0,0 +1,40 @@
CREATE TABLE players
(
id BIGINT PRIMARY KEY,
CREATE TABLE players (
id BIGINT PRIMARY KEY,
user_id BIGINT NOT NULL UNIQUE,
status VARCHAR(20) NOT NULL DEFAULT 'offline',
rating DECIMAL(3,2) DEFAULT 5.00,
total_orders INT DEFAULT 0,
completed_orders INT DEFAULT 0,
-- [注意] 此字段为冗余缓存,通过消息队列与 shop_players 表保持一致
shop_id BIGINT,
tags TEXT[] DEFAULT ARRAY[]::TEXT[],
games BIGINT[] DEFAULT ARRAY[]::BIGINT[],
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- 基础索引
CREATE INDEX idx_players_user ON players (user_id);
CREATE INDEX idx_players_shop ON players (shop_id) WHERE shop_id IS NOT NULL;
CREATE INDEX idx_players_status ON players (status);
CREATE INDEX idx_players_rating ON players (rating DESC);
-- 复合索引优化多条件筛选
CREATE INDEX idx_players_status_rating ON players (status, rating DESC) WHERE status IN ('available', 'busy');
-- GIN 索引优化数组查询
CREATE INDEX idx_players_games_gin ON players USING gin(games);
CREATE INDEX idx_players_tags_gin ON players USING gin(tags);
-- 店铺+状态复合索引
CREATE INDEX idx_players_shop_status ON players (shop_id, status, rating DESC) WHERE shop_id IS NOT NULL;
-- CREATE TRIGGER trigger_players_updated_at
-- BEFORE UPDATE
-- ON players
-- FOR EACH ROW
-- EXECUTE FUNCTION update_updated_at_column();
+49
View File
@@ -0,0 +1,49 @@
CREATE TABLE shops (
id BIGINT PRIMARY KEY,
owner_id BIGINT NOT NULL UNIQUE,
name VARCHAR(200) NOT NULL,
banner TEXT,
description TEXT,
rating DECIMAL(3,2) DEFAULT 5.00,
total_orders INT DEFAULT 0,
player_count INT DEFAULT 0,
commission_type VARCHAR(20) NOT NULL DEFAULT 'percentage',
commission_value DECIMAL(10,2) NOT NULL,
allow_multi_shop BOOLEAN DEFAULT FALSE,
allow_independent_orders BOOLEAN DEFAULT TRUE,
dispatch_mode VARCHAR(20) NOT NULL DEFAULT 'manual',
announcements TEXT[] DEFAULT ARRAY[]::TEXT[],
template_config JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT chk_commission_type CHECK (commission_type IN ('fixed', 'percentage')),
CONSTRAINT chk_dispatch_mode CHECK (dispatch_mode IN ('manual', 'auto')),
CONSTRAINT chk_rating_range CHECK (rating >= 0 AND rating <= 5)
);
-- 基础索引
CREATE INDEX idx_shops_owner ON shops(owner_id);
CREATE INDEX idx_shops_rating ON shops(rating DESC);
-- 三元组索引用于店铺名称模糊搜索
CREATE INDEX idx_shops_name_trgm ON shops USING gin(name gin_trgm_ops);
-- 全文搜索索引
CREATE INDEX idx_shops_fulltext ON shops USING gin(
to_tsvector('simple', name || ' ' || coalesce(description, ''))
);
-- 复合索引优化排序查询
CREATE INDEX idx_shops_rating_orders ON shops(rating DESC, total_orders DESC);
-- 公告数组索引
CREATE INDEX idx_shops_announcements ON shops USING gin(announcements);
-- JSONB 索引优化模板配置查询
CREATE INDEX idx_shops_template ON shops USING gin(template_config);
CREATE TRIGGER trigger_shops_updated_at
BEFORE UPDATE ON shops
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
+17
View File
@@ -0,0 +1,17 @@
CREATE TABLE shop_invitations (
id BIGINT PRIMARY KEY,
shop_id BIGINT NOT NULL REFERENCES shops(id),
player_id BIGINT NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'pending',
invited_by BIGINT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
responded_at TIMESTAMPTZ,
CONSTRAINT chk_invitation_status CHECK (status IN ('pending', 'accepted', 'rejected', 'cancelled')),
UNIQUE(shop_id, player_id, status) WHERE status = 'pending'
);
CREATE INDEX idx_invitations_shop ON shop_invitations(shop_id);
CREATE INDEX idx_invitations_player ON shop_invitations(player_id) WHERE status = 'pending';
CREATE INDEX idx_invitations_player_status ON shop_invitations(player_id, status, created_at DESC);
CREATE INDEX idx_invitations_shop_status ON shop_invitations(shop_id, status, created_at DESC);
+21
View File
@@ -0,0 +1,21 @@
CREATE TABLE shop_players
(
shop_id BIGINT NOT NULL REFERENCES shops (id),
player_id BIGINT NOT NULL,
-- [新增] 标记是否为主店铺。用于个人主页展示和 players.shop_id 缓存源
is_primary BOOLEAN DEFAULT FALSE,
joined_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
left_at TIMESTAMPTZ, -- 软删除,表示已离职
PRIMARY KEY (shop_id, player_id)
);
-- 索引
CREATE INDEX idx_shop_players_player ON shop_players (player_id) WHERE left_at IS NULL;
CREATE INDEX idx_shop_players_shop_active ON shop_players (shop_id, joined_at DESC) WHERE left_at IS NULL;
-- [新增] 唯一索引:确保一个打手同一时间只能有一个主店铺
CREATE UNIQUE INDEX idx_shop_players_one_primary
ON shop_players (player_id) WHERE is_primary = TRUE AND left_at IS NULL;
+15
View File
@@ -0,0 +1,15 @@
CREATE TABLE order_state_logs (
id BIGINT PRIMARY KEY,
order_id BIGINT NOT NULL REFERENCES orders(id),
from_status VARCHAR(30),
to_status VARCHAR(30) NOT NULL,
action VARCHAR(50) NOT NULL,
actor_id BIGINT NOT NULL,
actor_role VARCHAR(20) NOT NULL,
metadata JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_state_logs_order ON order_state_logs(order_id, created_at);
CREATE INDEX idx_state_logs_order_created ON order_state_logs(order_id, created_at DESC);
CREATE INDEX idx_state_logs_actor ON order_state_logs(actor_id, created_at DESC);
+65
View File
@@ -0,0 +1,65 @@
CREATE TABLE orders (
id BIGINT PRIMARY KEY,
consumer_id BIGINT NOT NULL,
consumer_name VARCHAR(100) NOT NULL,
player_id BIGINT NOT NULL,
player_name VARCHAR(100) NOT NULL,
shop_id BIGINT,
shop_name VARCHAR(200),
service_snapshot JSONB NOT NULL,
status VARCHAR(30) NOT NULL DEFAULT 'pending_payment',
total_price DECIMAL(10,2) NOT NULL,
note TEXT,
version INT NOT NULL DEFAULT 1,
timeout_job_id VARCHAR(100),
search_text TEXT GENERATED ALWAYS AS (
consumer_name || ' ' || player_name || ' ' || coalesce(shop_name, '') || ' ' || coalesce(note, '')
) STORED,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
accepted_at TIMESTAMPTZ,
closed_at TIMESTAMPTZ,
completed_at TIMESTAMPTZ,
cancelled_at TIMESTAMPTZ,
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT chk_order_status CHECK (status IN (
'pending_payment', 'pending_accept', 'in_progress',
'pending_close', 'pending_review', 'disputed',
'completed', 'cancelled'
)),
CONSTRAINT chk_price_positive CHECK (total_price > 0)
);
-- 基础索引
CREATE INDEX idx_orders_consumer ON orders(consumer_id, created_at DESC);
CREATE INDEX idx_orders_player ON orders(player_id, created_at DESC);
CREATE INDEX idx_orders_shop ON orders(shop_id, created_at DESC) WHERE shop_id IS NOT NULL;
CREATE INDEX idx_orders_status ON orders(status, created_at DESC);
CREATE INDEX idx_orders_timeout ON orders(timeout_job_id) WHERE timeout_job_id IS NOT NULL;
-- 三元组索引用于订单搜索
CREATE INDEX idx_orders_search_trgm ON orders USING gin(search_text gin_trgm_ops);
-- 复合索引优化多条件查询
CREATE INDEX idx_orders_consumer_status_created ON orders(consumer_id, status, created_at DESC);
CREATE INDEX idx_orders_player_status_created ON orders(player_id, status, created_at DESC);
CREATE INDEX idx_orders_shop_status_created ON orders(shop_id, status, created_at DESC)
WHERE shop_id IS NOT NULL;
-- 状态+时间复合索引 (用于超时任务扫描)
CREATE INDEX idx_orders_status_timeout ON orders(status, created_at)
WHERE status IN ('pending_accept', 'pending_close', 'pending_review');
-- JSONB 索引优化服务快照查询
CREATE INDEX idx_orders_service_snapshot ON orders USING gin(service_snapshot);
-- 价格区间索引
CREATE INDEX idx_orders_price ON orders(total_price) WHERE status = 'completed';
-- 时间范围索引 (用于统计)
CREATE INDEX idx_orders_completed_at ON orders(completed_at DESC) WHERE completed_at IS NOT NULL;
CREATE TRIGGER trigger_orders_updated_at
BEFORE UPDATE ON orders
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
+37
View File
@@ -0,0 +1,37 @@
CREATE TABLE reviews (
id BIGINT PRIMARY KEY,
order_id BIGINT NOT NULL,
from_user_id BIGINT NOT NULL,
from_user_name VARCHAR(100) NOT NULL,
from_user_avatar TEXT,
to_user_id BIGINT NOT NULL,
rating SMALLINT NOT NULL,
content TEXT,
sealed BOOLEAN DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
unsealed_at TIMESTAMPTZ,
CONSTRAINT chk_rating_range CHECK (rating >= 1 AND rating <= 5),
UNIQUE(order_id, from_user_id)
);
-- 基础索引
CREATE INDEX idx_reviews_order ON reviews(order_id);
CREATE INDEX idx_reviews_to_user ON reviews(to_user_id, created_at
CREATE INDEX idx_reviews_to_user ON reviews(to_user_id, created_at DESC) WHERE sealed = FALSE;
CREATE INDEX idx_reviews_from_user ON reviews(from_user_id);
-- 三元组索引用于评价内容搜索
CREATE INDEX idx_reviews_content_trgm ON reviews USING gin(content gin_trgm_ops)
WHERE sealed = FALSE AND content IS NOT NULL;
-- 复合索引优化用户评价列表
CREATE INDEX idx_reviews_to_user_rating ON reviews(to_user_id, rating DESC, created_at DESC)
WHERE sealed = FALSE;
-- 复合索引优化订单评价查询
CREATE INDEX idx_reviews_order_sealed ON reviews(order_id, sealed);
-- 评分统计索引
CREATE INDEX idx_reviews_rating_created ON reviews(rating, created_at DESC) WHERE sealed = FALSE;
+20
View File
@@ -0,0 +1,20 @@
CREATE TABLE favorites (
id BIGINT PRIMARY KEY,
user_id BIGINT NOT NULL,
target_type VARCHAR(20) NOT NULL,
target_id BIGINT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT chk_target_type CHECK (target_type IN ('player', 'shop')),
UNIQUE(user_id, target_type, target_id)
);
-- 基础索引
CREATE INDEX idx_favorites_user ON favorites(user_id, created_at DESC);
CREATE INDEX idx_favorites_target ON favorites(target_type, target_id);
-- 复合索引优化收藏列表查询
CREATE INDEX idx_favorites_user_type_created ON favorites(user_id, target_type, created_at DESC);
-- 目标反查索引(统计收藏数)
CREATE INDEX idx_favorites_target_count ON favorites(target_type, target_id);
+12
View File
@@ -0,0 +1,12 @@
CREATE TABLE user_follows (
id BIGINT PRIMARY KEY,
follower_id BIGINT NOT NULL REFERENCES users(id),
followee_id BIGINT NOT NULL REFERENCES users(id),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE(follower_id, followee_id),
CONSTRAINT chk_no_self_follow CHECK (follower_id != followee_id)
);
CREATE INDEX idx_follows_follower ON user_follows(follower_id);
CREATE INDEX idx_follows_followee ON user_follows(followee_id);
+16
View File
@@ -0,0 +1,16 @@
CREATE TABLE user_preferences (
user_id BIGINT PRIMARY KEY REFERENCES users(id),
notification_order BOOLEAN DEFAULT TRUE,
notification_community BOOLEAN DEFAULT TRUE,
notification_system BOOLEAN DEFAULT TRUE,
theme VARCHAR(20) DEFAULT 'light',
language VARCHAR(10) DEFAULT 'zh-CN',
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT chk_theme CHECK (theme IN ('light', 'dark', 'auto'))
);
CREATE TRIGGER trigger_preferences_updated_at
BEFORE UPDATE ON user_preferences
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
+40 -23
View File
@@ -1,35 +1,52 @@
CREATE TABLE user_verifications (
id BIGINT PRIMARY KEY,
user_id BIGINT NOT NULL REFERENCES users(id),
role VARCHAR(20) NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'pending',
materials JSONB NOT NULL,
reject_reason TEXT,
reviewed_by BIGINT,
reviewed_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CREATE TABLE user_verifications
(
id BIGINT PRIMARY KEY,
user_id BIGINT NOT NULL REFERENCES users (id),
role VARCHAR(20) NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'pending',
materials JSONB NOT NULL,
reject_reason TEXT,
reviewed_by BIGINT,
reviewed_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT chk_verification_status CHECK (status IN ('pending', 'approved', 'rejected')),
UNIQUE(user_id, role) -- 每个角色只有一条最新的认证记录
CONSTRAINT chk_verification_status CHECK (status IN ('pending', 'approved', 'rejected')),
UNIQUE (user_id, role) -- 每个角色只有一条最新的认证记录
);
-- NoteAuto update verification_status of users table
CREATE OR REPLACE FUNCTION sync_user_verification_status()
RETURNS TRIGGER AS $$
RETURNS TRIGGER AS
$$
DECLARE
status_json JSONB;
status_json JSONB;
BEGIN
SELECT jsonb_object_agg(role, status)
INTO status_json
FROM user_verifications
WHERE user_id = NEW.user_id;
SELECT jsonb_object_agg(role, status)
INTO status_json
FROM user_verifications
WHERE user_id = NEW.user_id;
UPDATE users SET verification_status = status_json WHERE id = NEW.user_id;
RETURN NEW;
UPDATE users SET verification_status = status_json WHERE id = NEW.user_id;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trigger_sync_verifications
AFTER INSERT OR UPDATE ON user_verifications
FOR EACH ROW EXECUTE FUNCTION sync_user_verification_status();
AFTER INSERT OR UPDATE
ON user_verifications
FOR EACH ROW
EXECUTE FUNCTION sync_user_verification_status();
-- | 字段名 | 类型 | 技术含义 | 业务目的与设计思考 |
-- | :--- | :--- | :--- | :--- |
-- | **`id`** | BIGINT | **主键 (Primary Key)** | **这张认证申请单的唯一编号**。<br>后端代码在处理“审核通过”这个动作时,操作的是这个 ID(例如:`Approve(verificationId)`)。 |
-- | **`user_id`** | BIGINT | **外键 (Foreign Key)** | **谁在申请?**<br>关联到 `users` 表。通过这个字段,我们可以查到申请人的昵称、手机号等信息。 |
-- | **`role`** | VARCHAR | 普通字段 | **申请什么身份?**<br>枚举值:`player` (打手), `owner` (店长)。<br>因为一个用户可以同时是打手和店长,所以需要区分这条记录是申请哪个身份的。 |
-- | **`status`** | VARCHAR | 状态字段 | **当前进度如何?**<br>枚举值:`pending` (待审核), `approved` (已通过), `rejected` (已驳回)。 |
-- | **`materials`** | **JSONB** | **非结构化数据** | **提交了什么证明材料?**<br>**精华设计**:这里不使用多张表或多个字段,而是用 JSON 存。因为打手需要传“身份证+段位图”,店长需要传“营业执照”。不同角色的材料结构不同,用 JSONB 最灵活,以后改规则不需要改表结构。 |
-- | **`reject_reason`** | TEXT | 文本 | **驳回理由**。<br>只有当 `status` = 'rejected' 时才有值,告诉用户哪里填错了。 |
-- | **`reviewed_by`** | BIGINT | 审计字段 | **谁审核的?**<br>记录是哪个管理员(Admin ID)点击了通过或拒绝。用于内部追责和审计。 |
-- | **`reviewed_at`** | TIMESTAMPTZ | 时间字段 | **什么时候审核的?**<br>用于统计管理员的工作效率,或者展示给用户“审核耗时”。 |
-- | **`created_at`** | TIMESTAMPTZ | 时间字段 | **申请提交时间**。 |
-- | **`updated_at`** | TIMESTAMPTZ | 时间字段 | **最后更新时间**。<br>比如用户重新上传了图片,这个时间会变。 |
+33
View File
@@ -0,0 +1,33 @@
CREATE TABLE wallet_transactions (
id BIGINT PRIMARY KEY,
user_id BIGINT NOT NULL,
type VARCHAR(20) NOT NULL,
amount DECIMAL(12,2) NOT NULL,
balance_after DECIMAL(12,2) NOT NULL,
description VARCHAR(500) NOT NULL,
order_id BIGINT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
search_text TEXT GENERATED ALWAYS AS (
type || ' ' || description
) STORED,
CONSTRAINT chk_transaction_type CHECK (type IN ('topup', 'payment', 'income', 'withdrawal', 'refund'))
);
-- 基础索引
CREATE INDEX idx_transactions_user ON wallet_transactions(user_id, created_at DESC);
CREATE INDEX idx_transactions_order ON wallet_transactions(order_id) WHERE order_id IS NOT NULL;
CREATE INDEX idx_transactions_type ON wallet_transactions(user_id, type, created_at DESC);
-- 三元组索引用于交易描述搜索
CREATE INDEX idx_transactions_search_trgm ON wallet_transactions USING gin(search_text gin_trgm_ops);
-- 复合索引优化用户交易查询
CREATE INDEX idx_transactions_user_type_created ON wallet_transactions(user_id, type, created_at DESC);
-- 时间范围索引 (用于统计)
CREATE INDEX idx_transactions_created_amount ON wallet_transactions(created_at DESC, amount);
-- 订单关联索引
CREATE INDEX idx_transactions_order_type ON wallet_transactions(order_id, type)
WHERE order_id IS NOT NULL;
+17
View File
@@ -0,0 +1,17 @@
CREATE TABLE wallets (
user_id BIGINT PRIMARY KEY,
balance DECIMAL(12,2) NOT NULL DEFAULT 0.00,
frozen_balance DECIMAL(12,2) NOT NULL DEFAULT 0.00,
version INT NOT NULL DEFAULT 1, -- 必须使用乐观锁
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT chk_balance_non_negative CHECK (balance >= 0),
CONSTRAINT chk_frozen_non_negative CHECK (frozen_balance >= 0)
);
CREATE INDEX idx_wallets_updated ON wallets(updated_at DESC);
CREATE TRIGGER trigger_wallets_updated_at
BEFORE UPDATE ON wallets
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();