This commit is contained in:
Your Name
2026-08-27 14:04:28 +08:00
parent f7720831be
commit 334890171e
3016 changed files with 263403 additions and 27971 deletions
+123
View File
@@ -0,0 +1,123 @@
SET NAMES utf8mb4;
CREATE TABLE IF NOT EXISTS users (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
public_id VARCHAR(20) NOT NULL,
country_code VARCHAR(8) NOT NULL DEFAULT '+86',
phone_hash BINARY(32) NULL,
phone_cipher VARBINARY(255) NULL,
password_hash VARCHAR(255) NOT NULL,
status TINYINT UNSIGNED NOT NULL DEFAULT 1,
risk_level TINYINT UNSIGNED NOT NULL DEFAULT 0,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
deleted_at DATETIME(3) NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_users_public_id (public_id),
UNIQUE KEY uk_users_phone_hash (phone_hash),
KEY idx_users_status_created (status, created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS user_profiles (
user_id BIGINT UNSIGNED NOT NULL,
nickname VARCHAR(50) NOT NULL,
avatar_url VARCHAR(500) NOT NULL DEFAULT '',
cover_url VARCHAR(500) NOT NULL DEFAULT '',
gender TINYINT UNSIGNED NOT NULL DEFAULT 0,
birthday DATE NULL,
height_cm SMALLINT UNSIGNED NULL,
city_code VARCHAR(20) NOT NULL DEFAULT '',
city_name VARCHAR(50) NOT NULL DEFAULT '',
occupation VARCHAR(100) NOT NULL DEFAULT '',
education TINYINT UNSIGNED NOT NULL DEFAULT 0,
relationship_status TINYINT UNSIGNED NOT NULL DEFAULT 0,
bio VARCHAR(500) NOT NULL DEFAULT '',
profile_score SMALLINT UNSIGNED NOT NULL DEFAULT 0,
is_vip TINYINT UNSIGNED NOT NULL DEFAULT 0,
vip_level TINYINT UNSIGNED NOT NULL DEFAULT 0,
last_active_at DATETIME(3) NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (user_id),
KEY idx_profiles_city_active (city_code, last_active_at),
CONSTRAINT fk_profiles_user FOREIGN KEY (user_id) REFERENCES users(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS user_privacy_settings (
user_id BIGINT UNSIGNED NOT NULL,
nearby_visible TINYINT(1) NOT NULL DEFAULT 1,
distance_visible TINYINT(1) NOT NULL DEFAULT 1,
online_visible TINYINT(1) NOT NULL DEFAULT 1,
last_active_visible TINYINT(1) NOT NULL DEFAULT 1,
allow_stranger_message TINYINT(1) NOT NULL DEFAULT 1,
allow_profile_visit_record TINYINT(1) NOT NULL DEFAULT 1,
allow_search TINYINT(1) NOT NULL DEFAULT 1,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (user_id),
CONSTRAINT fk_privacy_user FOREIGN KEY (user_id) REFERENCES users(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS user_devices (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
user_id BIGINT UNSIGNED NOT NULL,
device_id VARCHAR(100) NOT NULL,
platform VARCHAR(20) NOT NULL,
device_model VARCHAR(100) NOT NULL DEFAULT '',
os_version VARCHAR(50) NOT NULL DEFAULT '',
app_version VARCHAR(30) NOT NULL DEFAULT '',
push_provider VARCHAR(30) NOT NULL DEFAULT '',
push_token VARCHAR(255) NOT NULL DEFAULT '',
last_ip VARCHAR(45) NOT NULL DEFAULT '',
last_active_at DATETIME(3) NULL,
status TINYINT UNSIGNED NOT NULL DEFAULT 1,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_device_user_device (user_id, device_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS user_sessions (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
user_id BIGINT UNSIGNED NOT NULL,
device_id VARCHAR(100) NOT NULL,
refresh_token_hash BINARY(32) NOT NULL,
expires_at DATETIME(3) NOT NULL,
last_active_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
revoked_at DATETIME(3) NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
KEY idx_sessions_user (user_id, revoked_at),
UNIQUE KEY uk_sessions_refresh (refresh_token_hash)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS tags (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
category VARCHAR(30) NOT NULL,
name VARCHAR(50) NOT NULL,
icon VARCHAR(100) NOT NULL DEFAULT '',
status TINYINT UNSIGNED NOT NULL DEFAULT 1,
sort_order INT NOT NULL DEFAULT 0,
PRIMARY KEY (id),
UNIQUE KEY uk_tags_category_name (category, name)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS user_tags (
user_id BIGINT UNSIGNED NOT NULL,
tag_id BIGINT UNSIGNED NOT NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (user_id, tag_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS admin_users (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
username VARCHAR(50) NOT NULL,
password_hash VARCHAR(255) NOT NULL,
real_name VARCHAR(50) NOT NULL DEFAULT '',
avatar_url VARCHAR(500) NOT NULL DEFAULT '',
status TINYINT UNSIGNED NOT NULL DEFAULT 1,
last_login_at DATETIME(3) NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_admin_username (username)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+65
View File
@@ -0,0 +1,65 @@
CREATE TABLE IF NOT EXISTS user_follows (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
user_id BIGINT UNSIGNED NOT NULL,
target_user_id BIGINT UNSIGNED NOT NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_follow_pair (user_id, target_user_id),
KEY idx_follow_target (target_user_id, created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS user_likes (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
user_id BIGINT UNSIGNED NOT NULL,
target_user_id BIGINT UNSIGNED NOT NULL,
source VARCHAR(30) NOT NULL DEFAULT 'discover',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_like_pair (user_id, target_user_id),
KEY idx_like_target (target_user_id, created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS user_matches (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
user1_id BIGINT UNSIGNED NOT NULL,
user2_id BIGINT UNSIGNED NOT NULL,
matched_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
status TINYINT UNSIGNED NOT NULL DEFAULT 1,
PRIMARY KEY (id),
UNIQUE KEY uk_match_pair (user1_id, user2_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS user_blocks (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
user_id BIGINT UNSIGNED NOT NULL,
blocked_user_id BIGINT UNSIGNED NOT NULL,
reason VARCHAR(255) NOT NULL DEFAULT '',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_block_pair (user_id, blocked_user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS profile_visits (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
viewer_user_id BIGINT UNSIGNED NOT NULL,
target_user_id BIGINT UNSIGNED NOT NULL,
source VARCHAR(30) NOT NULL DEFAULT 'profile',
visited_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
KEY idx_visit_target_time (target_user_id, visited_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS user_location_states (
user_id BIGINT UNSIGNED NOT NULL,
city_code VARCHAR(20) NOT NULL,
location_cell VARCHAR(32) NOT NULL DEFAULT '',
latitude DECIMAL(10,7) NULL,
longitude DECIMAL(10,7) NULL,
last_location_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
source VARCHAR(20) NOT NULL DEFAULT 'gps',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (user_id),
KEY idx_location_city_time (city_code, last_location_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+72
View File
@@ -0,0 +1,72 @@
CREATE TABLE IF NOT EXISTS media_assets (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
owner_user_id BIGINT UNSIGNED NOT NULL,
media_type VARCHAR(20) NOT NULL,
storage_provider VARCHAR(20) NOT NULL DEFAULT 'local',
bucket VARCHAR(100) NOT NULL DEFAULT '',
object_key VARCHAR(500) NOT NULL DEFAULT '',
public_url VARCHAR(500) NOT NULL DEFAULT '',
mime_type VARCHAR(100) NOT NULL DEFAULT '',
file_size BIGINT UNSIGNED NOT NULL DEFAULT 0,
width INT UNSIGNED NULL,
height INT UNSIGNED NULL,
duration_ms INT UNSIGNED NULL,
moderation_status TINYINT UNSIGNED NOT NULL DEFAULT 1,
status TINYINT UNSIGNED NOT NULL DEFAULT 1,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
deleted_at DATETIME(3) NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS posts (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
user_id BIGINT UNSIGNED NOT NULL,
content VARCHAR(2000) NOT NULL DEFAULT '',
visibility TINYINT UNSIGNED NOT NULL DEFAULT 1,
city_code VARCHAR(20) NOT NULL DEFAULT '',
location_text VARCHAR(100) NOT NULL DEFAULT '',
status TINYINT UNSIGNED NOT NULL DEFAULT 1,
moderation_status TINYINT UNSIGNED NOT NULL DEFAULT 1,
like_count INT UNSIGNED NOT NULL DEFAULT 0,
comment_count INT UNSIGNED NOT NULL DEFAULT 0,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
deleted_at DATETIME(3) NULL,
PRIMARY KEY (id),
KEY idx_posts_status_created (status, created_at),
KEY idx_posts_user_created (user_id, created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS post_media (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
post_id BIGINT UNSIGNED NOT NULL,
media_id BIGINT UNSIGNED NULL,
media_url VARCHAR(500) NOT NULL DEFAULT '',
media_type VARCHAR(20) NOT NULL DEFAULT 'image',
sort_order INT NOT NULL DEFAULT 0,
PRIMARY KEY (id),
KEY idx_post_media_post (post_id, sort_order)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS post_likes (
post_id BIGINT UNSIGNED NOT NULL,
user_id BIGINT UNSIGNED NOT NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (post_id, user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS post_comments (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
post_id BIGINT UNSIGNED NOT NULL,
user_id BIGINT UNSIGNED NOT NULL,
parent_comment_id BIGINT UNSIGNED NULL,
reply_user_id BIGINT UNSIGNED NULL,
content VARCHAR(1000) NOT NULL,
status TINYINT UNSIGNED NOT NULL DEFAULT 1,
moderation_status TINYINT UNSIGNED NOT NULL DEFAULT 1,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
deleted_at DATETIME(3) NULL,
PRIMARY KEY (id),
KEY idx_comments_post_created (post_id, created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+68
View File
@@ -0,0 +1,68 @@
CREATE TABLE IF NOT EXISTS im_conversations (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
conversation_type TINYINT UNSIGNED NOT NULL DEFAULT 1,
last_seq BIGINT UNSIGNED NOT NULL DEFAULT 0,
last_message_id BIGINT UNSIGNED NULL,
last_message_at DATETIME(3) NULL,
status TINYINT UNSIGNED NOT NULL DEFAULT 1,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
KEY idx_conversation_last (last_message_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS im_direct_conversations (
conversation_id BIGINT UNSIGNED NOT NULL,
user1_id BIGINT UNSIGNED NOT NULL,
user2_id BIGINT UNSIGNED NOT NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (conversation_id),
UNIQUE KEY uk_direct_pair (user1_id, user2_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS im_conversation_members (
conversation_id BIGINT UNSIGNED NOT NULL,
user_id BIGINT UNSIGNED NOT NULL,
join_seq BIGINT UNSIGNED NOT NULL DEFAULT 0,
read_seq BIGINT UNSIGNED NOT NULL DEFAULT 0,
delivered_seq BIGINT UNSIGNED NOT NULL DEFAULT 0,
clear_seq BIGINT UNSIGNED NOT NULL DEFAULT 0,
pinned TINYINT(1) NOT NULL DEFAULT 0,
muted TINYINT(1) NOT NULL DEFAULT 0,
status TINYINT UNSIGNED NOT NULL DEFAULT 1,
joined_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (conversation_id, user_id),
KEY idx_member_user (user_id, updated_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS im_messages (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
conversation_id BIGINT UNSIGNED NOT NULL,
seq BIGINT UNSIGNED NOT NULL,
sender_id BIGINT UNSIGNED NOT NULL,
client_msg_id CHAR(26) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
message_type SMALLINT UNSIGNED NOT NULL,
body MEDIUMBLOB NOT NULL,
reply_to_message_id BIGINT UNSIGNED NULL,
moderation_status TINYINT UNSIGNED NOT NULL DEFAULT 1,
recalled_at DATETIME(3) NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_conv_seq (conversation_id, seq),
UNIQUE KEY uk_sender_client_msg (sender_id, client_msg_id),
KEY idx_conv_created (conversation_id, created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS im_user_sync_events (
user_id BIGINT UNSIGNED NOT NULL,
event_seq BIGINT UNSIGNED NOT NULL,
event_type SMALLINT UNSIGNED NOT NULL,
conversation_id BIGINT UNSIGNED NOT NULL,
message_seq BIGINT UNSIGNED NOT NULL,
event_data MEDIUMBLOB NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (user_id, event_seq),
KEY idx_sync_created (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+80
View File
@@ -0,0 +1,80 @@
CREATE TABLE IF NOT EXISTS membership_plans (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
code VARCHAR(30) NOT NULL,
name VARCHAR(50) NOT NULL,
level TINYINT UNSIGNED NOT NULL,
duration_days INT UNSIGNED NOT NULL,
price_cent INT UNSIGNED NOT NULL,
original_price_cent INT UNSIGNED NOT NULL,
status TINYINT UNSIGNED NOT NULL DEFAULT 1,
sort_order INT NOT NULL DEFAULT 0,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_plan_code (code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS benefit_definitions (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
benefit_key VARCHAR(50) NOT NULL,
name VARCHAR(100) NOT NULL,
value_type VARCHAR(20) NOT NULL,
description VARCHAR(255) NOT NULL DEFAULT '',
PRIMARY KEY (id),
UNIQUE KEY uk_benefit_key (benefit_key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS plan_benefits (
plan_id BIGINT UNSIGNED NOT NULL,
benefit_id BIGINT UNSIGNED NOT NULL,
benefit_value VARCHAR(255) NOT NULL,
PRIMARY KEY (plan_id, benefit_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS subscriptions (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
user_id BIGINT UNSIGNED NOT NULL,
plan_id BIGINT UNSIGNED NOT NULL,
source VARCHAR(30) NOT NULL,
status TINYINT UNSIGNED NOT NULL DEFAULT 1,
started_at DATETIME(3) NOT NULL,
expires_at DATETIME(3) NOT NULL,
auto_renew TINYINT(1) NOT NULL DEFAULT 0,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
KEY idx_sub_user_expire (user_id, expires_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS user_entitlements (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
user_id BIGINT UNSIGNED NOT NULL,
benefit_key VARCHAR(50) NOT NULL,
benefit_value VARCHAR(255) NOT NULL,
source_type VARCHAR(30) NOT NULL,
source_id BIGINT UNSIGNED NOT NULL,
started_at DATETIME(3) NOT NULL,
expires_at DATETIME(3) NOT NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
KEY idx_entitlement_user_key (user_id, benefit_key, expires_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS orders (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
order_no VARCHAR(40) NOT NULL,
user_id BIGINT UNSIGNED NOT NULL,
product_type VARCHAR(30) NOT NULL,
product_id BIGINT UNSIGNED NOT NULL,
amount_cent INT UNSIGNED NOT NULL,
currency CHAR(3) NOT NULL DEFAULT 'CNY',
status VARCHAR(20) NOT NULL DEFAULT 'CREATED',
channel VARCHAR(30) NOT NULL DEFAULT '',
paid_at DATETIME(3) NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_order_no (order_no),
KEY idx_order_user_created (user_id, created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+72
View File
@@ -0,0 +1,72 @@
CREATE TABLE IF NOT EXISTS reports (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
reporter_user_id BIGINT UNSIGNED NOT NULL,
target_type VARCHAR(30) NOT NULL,
target_id BIGINT UNSIGNED NOT NULL,
reason_code VARCHAR(50) NOT NULL,
description VARCHAR(1000) NOT NULL DEFAULT '',
evidence_json JSON NULL,
status VARCHAR(20) NOT NULL DEFAULT 'PENDING',
handled_by BIGINT UNSIGNED NULL,
handled_at DATETIME(3) NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
KEY idx_reports_status_created (status, created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS moderation_tasks (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
target_type VARCHAR(30) NOT NULL,
target_id BIGINT UNSIGNED NOT NULL,
content_type VARCHAR(30) NOT NULL,
risk_score INT NOT NULL DEFAULT 0,
machine_result JSON NULL,
status VARCHAR(20) NOT NULL DEFAULT 'PENDING',
reviewer_id BIGINT UNSIGNED NULL,
review_result VARCHAR(255) NOT NULL DEFAULT '',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
reviewed_at DATETIME(3) NULL,
PRIMARY KEY (id),
KEY idx_moderation_status_created (status, created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS user_risk_profiles (
user_id BIGINT UNSIGNED NOT NULL,
risk_score INT NOT NULL DEFAULT 0,
risk_level TINYINT UNSIGNED NOT NULL DEFAULT 0,
message_score INT NOT NULL DEFAULT 0,
device_score INT NOT NULL DEFAULT 0,
report_score INT NOT NULL DEFAULT 0,
behavior_score INT NOT NULL DEFAULT 0,
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (user_id),
KEY idx_risk_level_score (risk_level, risk_score)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS risk_events (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
user_id BIGINT UNSIGNED NOT NULL,
event_type VARCHAR(50) NOT NULL,
score_delta INT NOT NULL,
device_id VARCHAR(100) NOT NULL DEFAULT '',
ip VARCHAR(45) NOT NULL DEFAULT '',
metadata JSON NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
KEY idx_risk_event_user_created (user_id, created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS notifications (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
user_id BIGINT UNSIGNED NOT NULL,
type VARCHAR(30) NOT NULL,
title VARCHAR(100) NOT NULL,
content VARCHAR(1000) NOT NULL,
biz_type VARCHAR(30) NOT NULL DEFAULT '',
biz_id BIGINT UNSIGNED NULL,
read_at DATETIME(3) NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
KEY idx_notify_user_read_created (user_id, read_at, created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+70
View File
@@ -0,0 +1,70 @@
CREATE TABLE IF NOT EXISTS system_configs (
config_key VARCHAR(100) NOT NULL,
config_value TEXT NOT NULL,
value_type VARCHAR(20) NOT NULL DEFAULT 'string',
description VARCHAR(255) NOT NULL DEFAULT '',
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (config_key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS banners (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
title VARCHAR(100) NOT NULL,
image_url VARCHAR(500) NOT NULL,
link_url VARCHAR(500) NOT NULL DEFAULT '',
position VARCHAR(30) NOT NULL DEFAULT 'home',
status TINYINT UNSIGNED NOT NULL DEFAULT 1,
sort_order INT NOT NULL DEFAULT 0,
starts_at DATETIME(3) NULL,
ends_at DATETIME(3) NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS app_versions (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
platform VARCHAR(20) NOT NULL,
version VARCHAR(30) NOT NULL,
build_number INT UNSIGNED NOT NULL,
force_update TINYINT(1) NOT NULL DEFAULT 0,
download_url VARCHAR(500) NOT NULL DEFAULT '',
release_notes VARCHAR(2000) NOT NULL DEFAULT '',
status TINYINT UNSIGNED NOT NULL DEFAULT 1,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_version_platform_build (platform, build_number)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS admin_audit_logs (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
admin_user_id BIGINT UNSIGNED NOT NULL,
action VARCHAR(100) NOT NULL,
target_type VARCHAR(50) NOT NULL DEFAULT '',
target_id BIGINT UNSIGNED NULL,
request_data JSON NULL,
ip VARCHAR(45) NOT NULL DEFAULT '',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
KEY idx_audit_admin_created (admin_user_id, created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO system_configs (config_key, config_value, value_type, description) VALUES
('im.recall_seconds', '120', 'number', '消息撤回时间窗口'),
('nearby.max_distance_km', '50', 'number', '附近的人最大距离'),
('stranger.daily_limit', '10', 'number', '普通用户每日主动聊天人数')
ON DUPLICATE KEY UPDATE description = VALUES(description);
INSERT INTO membership_plans (code, name, level, duration_days, price_cent, original_price_cent, status, sort_order) VALUES
('VIP_1M', 'VIP 1个月', 1, 30, 2800, 4000, 1, 10),
('VIP_3M', 'VIP 3个月', 1, 90, 6800, 9000, 1, 20),
('SVIP_12M', 'SVIP 12个月', 2, 365, 22800, 36000, 1, 30)
ON DUPLICATE KEY UPDATE name = VALUES(name), price_cent = VALUES(price_cent), original_price_cent = VALUES(original_price_cent);
INSERT INTO tags (category, name, icon, status, sort_order) VALUES
('personality', '天秤座', '', 1, 10),
('hobby', '摄影爱好者', '', 1, 20),
('hobby', '旅行达人', '', 1, 30),
('hobby', '电影', '', 1, 40),
('hobby', '音乐', '', 1, 50)
ON DUPLICATE KEY UPDATE status = VALUES(status);
@@ -0,0 +1,37 @@
CREATE TABLE IF NOT EXISTS sms_verification_codes (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
phone_hash BINARY(32) NOT NULL,
scene VARCHAR(30) NOT NULL,
code_hash BINARY(32) NOT NULL,
expires_at DATETIME(3) NOT NULL,
used_at DATETIME(3) NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
KEY idx_sms_phone_scene_created (phone_hash, scene, created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO system_configs (config_key, config_value, value_type, description) VALUES
('sms.enabled', 'true', 'boolean', '是否启用短信服务'),
('sms.provider', 'debug', 'string', '短信提供商:debug 或 webhook'),
('sms.sign_name', '星遇社交', 'string', '短信签名'),
('sms.template_register', 'REGISTER', 'string', '注册验证码模板 ID'),
('sms.template_login', 'LOGIN', 'string', '登录验证码模板 ID'),
('sms.template_reset', 'RESET', 'string', '找回密码模板 ID'),
('sms.webhook_url', '', 'string', '短信网关 Webhook 地址'),
('sms.webhook_token', '', 'secret', '短信网关鉴权令牌'),
('sms.debug_code', '123456', 'secret', '本地调试验证码'),
('sms.expire_seconds', '300', 'number', '验证码有效期(秒)'),
('payment.mode', 'sandbox', 'string', '支付模式:sandbox 或 live'),
('payment.alipay.enabled', 'true', 'boolean', '是否启用支付宝'),
('payment.alipay.app_id', '', 'string', '支付宝应用 APPID'),
('payment.alipay.private_key', '', 'secret', '支付宝应用私钥'),
('payment.alipay.public_key', '', 'secret', '支付宝公钥'),
('payment.alipay.notify_url', '', 'string', '支付宝异步通知地址'),
('payment.wechat.enabled', 'true', 'boolean', '是否启用微信支付'),
('payment.wechat.app_id', '', 'string', '微信支付 AppID'),
('payment.wechat.mch_id', '', 'string', '微信支付商户号'),
('payment.wechat.api_v3_key', '', 'secret', '微信支付 APIv3 密钥'),
('payment.wechat.private_key', '', 'secret', '微信支付商户私钥'),
('payment.wechat.serial_no', '', 'string', '微信支付证书序列号'),
('payment.wechat.notify_url', '', 'string', '微信支付回调地址')
ON DUPLICATE KEY UPDATE description = VALUES(description), value_type = VALUES(value_type);
@@ -0,0 +1,6 @@
-- PowerShell 5 may encode text piped to native executables as the active ANSI
-- code page. Repair the only user-visible non-ASCII integration default for
-- databases initialized by the earlier migration runner.
UPDATE system_configs
SET config_value = CONVERT(0xE6989FE98187E7A4BEE4BAA4 USING utf8mb4)
WHERE config_key = 'sms.sign_name' AND config_value = '????';
@@ -0,0 +1,64 @@
SET NAMES utf8mb4;
CREATE TABLE IF NOT EXISTS user_verifications (
user_id BIGINT UNSIGNED NOT NULL,
verification_type VARCHAR(30) NOT NULL DEFAULT 'real_name',
status VARCHAR(20) NOT NULL DEFAULT 'UNVERIFIED',
real_name VARCHAR(50) NOT NULL DEFAULT '',
document_mask VARCHAR(80) NOT NULL DEFAULT '',
remark VARCHAR(500) NOT NULL DEFAULT '',
reviewer_admin_id BIGINT UNSIGNED NULL,
submitted_at DATETIME(3) NULL,
reviewed_at DATETIME(3) NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (user_id),
KEY idx_verification_status_updated (status, updated_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS user_sanctions (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
user_id BIGINT UNSIGNED NOT NULL,
sanction_type VARCHAR(30) NOT NULL,
reason VARCHAR(500) NOT NULL,
starts_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
expires_at DATETIME(3) NULL,
status VARCHAR(20) NOT NULL DEFAULT 'ACTIVE',
operator_admin_id BIGINT UNSIGNED NOT NULL,
revoked_by BIGINT UNSIGNED NULL,
revoked_at DATETIME(3) NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
KEY idx_sanction_user_status_expire (user_id, status, expires_at),
KEY idx_sanction_created (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS user_security_controls (
user_id BIGINT UNSIGNED NOT NULL,
token_version INT UNSIGNED NOT NULL DEFAULT 0,
force_logout_at DATETIME(3) NULL,
password_reset_at DATETIME(3) NULL,
last_operator_admin_id BIGINT UNSIGNED NULL,
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
SET @token_version_exists=(SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='user_security_controls' AND COLUMN_NAME='token_version');
SET @token_version_sql=IF(@token_version_exists=0,'ALTER TABLE user_security_controls ADD COLUMN token_version INT UNSIGNED NOT NULL DEFAULT 0 AFTER user_id','SELECT 1');
PREPARE token_version_stmt FROM @token_version_sql;
EXECUTE token_version_stmt;
DEALLOCATE PREPARE token_version_stmt;
INSERT INTO user_verifications (user_id, status)
SELECT u.id, 'UNVERIFIED' FROM users u
LEFT JOIN user_verifications v ON v.user_id=u.id
WHERE v.user_id IS NULL;
INSERT INTO subscriptions (user_id, plan_id, source, status, started_at, expires_at)
SELECT p.user_id, mp.id, 'legacy_backfill', 1, NOW(3), DATE_ADD(NOW(3), INTERVAL mp.duration_days DAY)
FROM user_profiles p
JOIN membership_plans mp ON mp.level=p.vip_level
AND mp.duration_days=(SELECT MAX(mp2.duration_days) FROM membership_plans mp2 WHERE mp2.level=p.vip_level)
WHERE p.is_vip=1 AND p.vip_level>0
AND NOT EXISTS (SELECT 1 FROM subscriptions s WHERE s.user_id=p.user_id AND s.status=1 AND s.expires_at>NOW(3));
@@ -0,0 +1,25 @@
SET NAMES utf8mb4;
SET @plan_deleted_exists=(SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='membership_plans' AND COLUMN_NAME='deleted_at');
SET @plan_deleted_sql=IF(@plan_deleted_exists=0,'ALTER TABLE membership_plans ADD COLUMN deleted_at DATETIME(3) NULL AFTER updated_at','SELECT 1');
PREPARE plan_deleted_stmt FROM @plan_deleted_sql;
EXECUTE plan_deleted_stmt;
DEALLOCATE PREPARE plan_deleted_stmt;
SET @order_deleted_exists=(SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='orders' AND COLUMN_NAME='deleted_at');
SET @order_deleted_sql=IF(@order_deleted_exists=0,'ALTER TABLE orders ADD COLUMN deleted_at DATETIME(3) NULL AFTER updated_at','SELECT 1');
PREPARE order_deleted_stmt FROM @order_deleted_sql;
EXECUTE order_deleted_stmt;
DEALLOCATE PREPARE order_deleted_stmt;
SET @plan_deleted_index_exists=(SELECT COUNT(*) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='membership_plans' AND INDEX_NAME='idx_plan_deleted_sort');
SET @plan_deleted_index_sql=IF(@plan_deleted_index_exists=0,'ALTER TABLE membership_plans ADD KEY idx_plan_deleted_sort (deleted_at,sort_order)','SELECT 1');
PREPARE plan_deleted_index_stmt FROM @plan_deleted_index_sql;
EXECUTE plan_deleted_index_stmt;
DEALLOCATE PREPARE plan_deleted_index_stmt;
SET @order_deleted_index_exists=(SELECT COUNT(*) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='orders' AND INDEX_NAME='idx_order_deleted_created');
SET @order_deleted_index_sql=IF(@order_deleted_index_exists=0,'ALTER TABLE orders ADD KEY idx_order_deleted_created (deleted_at,created_at)','SELECT 1');
PREPARE order_deleted_index_stmt FROM @order_deleted_index_sql;
EXECUTE order_deleted_index_stmt;
DEALLOCATE PREPARE order_deleted_index_stmt;
@@ -0,0 +1,6 @@
SET NAMES utf8mb4;
-- Accept both the canonical 26-character client ID and UUID-style IDs from
-- older/cached clients while preserving sender-level idempotency.
ALTER TABLE im_messages
MODIFY COLUMN client_msg_id VARCHAR(64) CHARACTER SET ascii COLLATE ascii_bin NOT NULL;
@@ -0,0 +1,12 @@
SET NAMES utf8mb4;
CREATE TABLE IF NOT EXISTS api_rate_limits (
bucket_key BINARY(32) NOT NULL,
action_name VARCHAR(40) NOT NULL,
hits INT UNSIGNED NOT NULL DEFAULT 1,
expires_at DATETIME(3) NOT NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (bucket_key),
KEY idx_rate_limit_expires (expires_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
@@ -0,0 +1,61 @@
SET NAMES utf8mb4;
SET @provider_order_exists=(SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='orders' AND COLUMN_NAME='provider_order_no');
SET @provider_order_sql=IF(@provider_order_exists=0,'ALTER TABLE orders ADD COLUMN provider_order_no VARCHAR(100) NOT NULL DEFAULT '''' AFTER channel','SELECT 1');
PREPARE provider_order_stmt FROM @provider_order_sql;
EXECUTE provider_order_stmt;
DEALLOCATE PREPARE provider_order_stmt;
SET @checkout_url_exists=(SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='orders' AND COLUMN_NAME='checkout_url');
SET @checkout_url_sql=IF(@checkout_url_exists=0,'ALTER TABLE orders ADD COLUMN checkout_url VARCHAR(1000) NOT NULL DEFAULT '''' AFTER provider_order_no','SELECT 1');
PREPARE checkout_url_stmt FROM @checkout_url_sql;
EXECUTE checkout_url_stmt;
DEALLOCATE PREPARE checkout_url_stmt;
SET @payment_payload_exists=(SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='orders' AND COLUMN_NAME='payment_payload');
SET @payment_payload_sql=IF(@payment_payload_exists=0,'ALTER TABLE orders ADD COLUMN payment_payload MEDIUMTEXT NULL AFTER checkout_url','SELECT 1');
PREPARE payment_payload_stmt FROM @payment_payload_sql;
EXECUTE payment_payload_stmt;
DEALLOCATE PREPARE payment_payload_stmt;
SET @paid_amount_exists=(SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='orders' AND COLUMN_NAME='paid_amount_cent');
SET @paid_amount_sql=IF(@paid_amount_exists=0,'ALTER TABLE orders ADD COLUMN paid_amount_cent INT UNSIGNED NULL AFTER amount_cent','SELECT 1');
PREPARE paid_amount_stmt FROM @paid_amount_sql;
EXECUTE paid_amount_stmt;
DEALLOCATE PREPARE paid_amount_stmt;
SET @payment_notified_exists=(SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='orders' AND COLUMN_NAME='payment_notified_at');
SET @payment_notified_sql=IF(@payment_notified_exists=0,'ALTER TABLE orders ADD COLUMN payment_notified_at DATETIME(3) NULL AFTER paid_at','SELECT 1');
PREPARE payment_notified_stmt FROM @payment_notified_sql;
EXECUTE payment_notified_stmt;
DEALLOCATE PREPARE payment_notified_stmt;
SET @provider_order_index_exists=(SELECT COUNT(*) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='orders' AND INDEX_NAME='idx_order_provider_no');
SET @provider_order_index_sql=IF(@provider_order_index_exists=0,'ALTER TABLE orders ADD KEY idx_order_provider_no (provider_order_no)','SELECT 1');
PREPARE provider_order_index_stmt FROM @provider_order_index_sql;
EXECUTE provider_order_index_stmt;
DEALLOCATE PREPARE provider_order_index_stmt;
CREATE TABLE IF NOT EXISTS payment_events (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
event_id VARCHAR(100) NOT NULL,
order_no VARCHAR(40) NOT NULL,
channel VARCHAR(30) NOT NULL,
provider_order_no VARCHAR(100) NOT NULL DEFAULT '',
event_status VARCHAR(30) NOT NULL,
amount_cent INT UNSIGNED NOT NULL DEFAULT 0,
raw_payload MEDIUMTEXT NOT NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_payment_event (event_id),
KEY idx_payment_event_order (order_no,created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO system_configs (config_key,config_value,value_type,description) VALUES
('payment.gateway.create_url','','string','统一支付网关创建支付地址'),
('payment.gateway.token','','secret','统一支付网关 Bearer Token'),
('payment.gateway.notify_secret','','secret','支付通知 HMAC-SHA256 密钥'),
('payment.gateway.notify_url','','string','本系统支付通知公网 HTTPS 地址'),
('payment.gateway.return_url','','string','支付完成后的客户端返回地址'),
('payment.gateway.timeout_seconds','10','number','支付网关请求超时秒数')
ON DUPLICATE KEY UPDATE description=VALUES(description),value_type=VALUES(value_type);
@@ -0,0 +1,5 @@
SET NAMES utf8mb4;
INSERT INTO system_configs (config_key,config_value,value_type,description) VALUES
('payment.gateway.refund_url','','string','统一支付网关退款申请地址')
ON DUPLICATE KEY UPDATE description=VALUES(description),value_type=VALUES(value_type);
@@ -0,0 +1,6 @@
SET NAMES utf8mb4;
ALTER TABLE admin_users
ADD COLUMN token_version INT UNSIGNED NOT NULL DEFAULT 0 AFTER status,
ADD COLUMN password_changed_at DATETIME(3) NULL AFTER last_login_at;
@@ -0,0 +1,39 @@
SET NAMES utf8mb4;
INSERT INTO system_configs (config_key, config_value, value_type, description) VALUES
('sms.aliyun.endpoint', 'https://dysmsapi.aliyuncs.com', 'string', '阿里云短信 API 地址'),
('sms.aliyun.access_key_id', '', 'secret', '阿里云 AccessKey ID'),
('sms.aliyun.access_key_secret', '', 'secret', '阿里云 AccessKey Secret'),
('sms.aliyun.sign_name', '', 'string', '阿里云短信签名'),
('sms.aliyun.template_register', '', 'string', '阿里云注册模板 Code'),
('sms.aliyun.template_login', '', 'string', '阿里云登录模板 Code'),
('sms.aliyun.template_reset', '', 'string', '阿里云重置密码模板 Code'),
('sms.aliyun.template_params', '{"code":"{{code}}"}', 'string', '阿里云模板变量 JSON'),
('sms.tencent.endpoint', 'https://sms.tencentcloudapi.com', 'string', '腾讯云短信 API 地址'),
('sms.tencent.secret_id', '', 'secret', '腾讯云 SecretId'),
('sms.tencent.secret_key', '', 'secret', '腾讯云 SecretKey'),
('sms.tencent.sdk_app_id', '', 'string', '腾讯云短信 SdkAppId'),
('sms.tencent.region', 'ap-guangzhou', 'string', '腾讯云短信地域'),
('sms.tencent.sign_name', '', 'string', '腾讯云短信签名'),
('sms.tencent.template_register', '', 'string', '腾讯云注册模板 ID'),
('sms.tencent.template_login', '', 'string', '腾讯云登录模板 ID'),
('sms.tencent.template_reset', '', 'string', '腾讯云重置密码模板 ID'),
('sms.tencent.template_params', '["{{code}}"]', 'string', '腾讯云模板参数 JSON'),
('sms.huawei.endpoint', '', 'string', '华为云短信 APP 接入地址'),
('sms.huawei.app_key', '', 'secret', '华为云短信 Application Key'),
('sms.huawei.app_secret', '', 'secret', '华为云短信 Application Secret'),
('sms.huawei.sender', '', 'string', '华为云短信签名通道号'),
('sms.huawei.signature', '', 'string', '华为云短信签名名称'),
('sms.huawei.template_register', '', 'string', '华为云注册模板 ID'),
('sms.huawei.template_login', '', 'string', '华为云登录模板 ID'),
('sms.huawei.template_reset', '', 'string', '华为云重置密码模板 ID'),
('sms.huawei.template_params', '["{{code}}"]', 'string', '华为云模板参数 JSON'),
('sms.huawei.status_callback', '', 'string', '华为云短信状态回调地址')
ON DUPLICATE KEY UPDATE description = VALUES(description), value_type = VALUES(value_type);
UPDATE system_configs
SET description = '短信提供商:aliyun、tencent、huawei、webhook 或 debug'
WHERE config_key = 'sms.provider';
@@ -0,0 +1,39 @@
SET NAMES utf8mb4;
INSERT INTO system_configs (config_key, config_value, value_type, description) VALUES
('storage.provider', 'local', 'string', '文件存储提供商:local、aliyun_oss、tencent_cos、qiniu、huawei_obs 或 huawei_flexus'),
('storage.object_prefix', 'media', 'string', '云端对象键前缀'),
('storage.local.directory', './uploads', 'string', '本地文件存储目录'),
('storage.local.public_base_url', '', 'string', '本地文件公开访问地址'),
('storage.aliyun_oss.endpoint', 'https://oss-cn-hangzhou.aliyuncs.com', 'string', '阿里云 OSS Endpoint'),
('storage.aliyun_oss.region', 'cn-hangzhou', 'string', '阿里云 OSS Region'),
('storage.aliyun_oss.bucket', '', 'string', '阿里云 OSS Bucket'),
('storage.aliyun_oss.access_key_id', '', 'secret', '阿里云 OSS AccessKey ID'),
('storage.aliyun_oss.access_key_secret', '', 'secret', '阿里云 OSS AccessKey Secret'),
('storage.aliyun_oss.public_base_url', '', 'string', '阿里云 OSS 文件访问域名'),
('storage.tencent_cos.endpoint', '', 'string', '腾讯云 COS Bucket URL'),
('storage.tencent_cos.bucket', '', 'string', '腾讯云 COS Bucket'),
('storage.tencent_cos.secret_id', '', 'secret', '腾讯云 COS SecretId'),
('storage.tencent_cos.secret_key', '', 'secret', '腾讯云 COS SecretKey'),
('storage.tencent_cos.public_base_url', '', 'string', '腾讯云 COS 文件访问域名'),
('storage.qiniu.bucket', '', 'string', '七牛云 Kodo 空间名称'),
('storage.qiniu.access_key', '', 'secret', '七牛云 AccessKey'),
('storage.qiniu.secret_key', '', 'secret', '七牛云 SecretKey'),
('storage.qiniu.public_base_url', '', 'string', '七牛云文件访问域名'),
('storage.huawei_obs.endpoint', 'https://obs.cn-north-4.myhuaweicloud.com', 'string', '华为云 OBS Endpoint'),
('storage.huawei_obs.bucket', '', 'string', '华为云 OBS Bucket'),
('storage.huawei_obs.access_key', '', 'secret', '华为云 OBS Access Key'),
('storage.huawei_obs.secret_key', '', 'secret', '华为云 OBS Secret Key'),
('storage.huawei_obs.public_base_url', '', 'string', '华为云 OBS 文件访问域名'),
('storage.huawei_flexus.endpoint', '', 'string', '华为云 Flexus 对象存储 Endpoint'),
('storage.huawei_flexus.bucket', '', 'string', '华为云 Flexus 对象存储 Bucket'),
('storage.huawei_flexus.access_key', '', 'secret', '华为云 Flexus 对象存储 Access Key'),
('storage.huawei_flexus.secret_key', '', 'secret', '华为云 Flexus 对象存储 Secret Key'),
('storage.huawei_flexus.public_base_url', '', 'string', '华为云 Flexus 对象存储文件访问域名')
ON DUPLICATE KEY UPDATE description = VALUES(description), value_type = VALUES(value_type);
@@ -0,0 +1,34 @@
SET @daily_chat_limit_exists=(SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='membership_plans' AND COLUMN_NAME='daily_active_chat_limit');
SET @daily_chat_limit_sql=IF(@daily_chat_limit_exists=0,'ALTER TABLE membership_plans ADD COLUMN daily_active_chat_limit INT UNSIGNED NOT NULL DEFAULT 20 AFTER duration_days','SELECT 1');
PREPARE daily_chat_limit_stmt FROM @daily_chat_limit_sql;
EXECUTE daily_chat_limit_stmt;
DEALLOCATE PREPARE daily_chat_limit_stmt;
UPDATE membership_plans
SET daily_active_chat_limit=CASE WHEN level>=2 THEN 100 ELSE 20 END
WHERE daily_active_chat_limit=20;
INSERT INTO system_configs(config_key,config_value,value_type,description) VALUES
('membership.free_daily_active_chat_limit','5','integer','普通用户每日可主动聊天的不同用户数,0 表示不限制')
ON DUPLICATE KEY UPDATE description=VALUES(description);
CREATE TABLE IF NOT EXISTS im_daily_active_chat_usage (
user_id BIGINT UNSIGNED NOT NULL,
usage_date DATE NOT NULL,
used_count INT UNSIGNED NOT NULL DEFAULT 0,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (user_id,usage_date),
KEY idx_daily_chat_usage_date (usage_date)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS im_daily_active_chat_targets (
user_id BIGINT UNSIGNED NOT NULL,
target_user_id BIGINT UNSIGNED NOT NULL,
usage_date DATE NOT NULL,
conversation_id BIGINT UNSIGNED NOT NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (user_id,target_user_id,usage_date),
KEY idx_daily_chat_target_date (target_user_id,usage_date),
KEY idx_daily_chat_conversation (conversation_id,created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
@@ -0,0 +1,87 @@
SET NAMES utf8mb4;
CREATE TABLE IF NOT EXISTS admin_oauth_identities (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
provider VARCHAR(20) NOT NULL,
subject VARCHAR(191) NOT NULL,
admin_user_id BIGINT UNSIGNED NOT NULL,
email VARCHAR(255) NOT NULL DEFAULT '',
display_name VARCHAR(100) NOT NULL DEFAULT '',
avatar_url VARCHAR(500) NOT NULL DEFAULT '',
last_login_at DATETIME(3) NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_admin_oauth_provider_subject (provider, subject),
UNIQUE KEY uk_admin_oauth_user_provider (admin_user_id, provider),
CONSTRAINT fk_admin_oauth_identity_user FOREIGN KEY (admin_user_id) REFERENCES admin_users(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS admin_oauth_states (
state_hash BINARY(32) NOT NULL,
provider VARCHAR(20) NOT NULL,
code_verifier VARCHAR(128) NOT NULL DEFAULT '',
expires_at DATETIME(3) NOT NULL,
used_at DATETIME(3) NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (state_hash),
KEY idx_admin_oauth_state_expiry (expires_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS admin_oauth_login_codes (
code_hash BINARY(32) NOT NULL,
provider VARCHAR(20) NOT NULL,
subject VARCHAR(191) NOT NULL,
email VARCHAR(255) NOT NULL DEFAULT '',
display_name VARCHAR(100) NOT NULL DEFAULT '',
avatar_url VARCHAR(500) NOT NULL DEFAULT '',
admin_user_id BIGINT UNSIGNED NULL,
expires_at DATETIME(3) NOT NULL,
used_at DATETIME(3) NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (code_hash),
KEY idx_admin_oauth_code_expiry (expires_at),
KEY idx_admin_oauth_code_admin (admin_user_id),
CONSTRAINT fk_admin_oauth_code_user FOREIGN KEY (admin_user_id) REFERENCES admin_users(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO system_configs (config_key, config_value, value_type, description) VALUES
('oauth.admin.frontend_callback_url', 'http://localhost:5560/auth/social-callback', 'string', '第三方登录完成后跳转的管理端页面;生产环境必须使用 HTTPS'),
('oauth.wechat.enabled', 'false', 'boolean', '启用微信扫码登录'),
('oauth.wechat.client_id', '', 'string', '微信开放平台网站应用 AppID'),
('oauth.wechat.client_secret', '', 'secret', '微信开放平台网站应用 AppSecret'),
('oauth.wechat.authorization_url', 'https://open.weixin.qq.com/connect/qrconnect', 'string', '微信登录授权地址'),
('oauth.wechat.token_url', 'https://api.weixin.qq.com/sns/oauth2/access_token', 'string', '微信登录令牌地址'),
('oauth.wechat.userinfo_url', 'https://api.weixin.qq.com/sns/userinfo', 'string', '微信用户信息地址'),
('oauth.wechat.scope', 'snsapi_login', 'string', '微信网站应用登录授权范围'),
('oauth.wechat.redirect_uri', 'http://127.0.0.1:8888/admin/v1/auth/oauth/callback', 'string', '微信开放平台登记的授权回调地址;生产环境必须使用 HTTPS'),
('oauth.qq.enabled', 'false', 'boolean', '启用 QQ 登录'),
('oauth.qq.client_id', '', 'string', 'QQ 互联应用 AppID'),
('oauth.qq.client_secret', '', 'secret', 'QQ 互联应用 AppKey'),
('oauth.qq.authorization_url', 'https://graph.qq.com/oauth2.0/authorize', 'string', 'QQ 登录授权地址'),
('oauth.qq.token_url', 'https://graph.qq.com/oauth2.0/token', 'string', 'QQ 登录令牌地址'),
('oauth.qq.openid_url', 'https://graph.qq.com/oauth2.0/me', 'string', 'QQ OpenID 查询地址'),
('oauth.qq.userinfo_url', 'https://graph.qq.com/user/get_user_info', 'string', 'QQ 用户信息地址'),
('oauth.qq.scope', 'get_user_info', 'string', 'QQ 登录授权范围'),
('oauth.qq.redirect_uri', 'http://127.0.0.1:8888/admin/v1/auth/oauth/callback', 'string', 'QQ 互联登记的授权回调地址;生产环境必须使用 HTTPS'),
('oauth.github.enabled', 'false', 'boolean', '启用 GitHub 登录'),
('oauth.github.client_id', '', 'string', 'GitHub OAuth App Client ID'),
('oauth.github.client_secret', '', 'secret', 'GitHub OAuth App Client Secret'),
('oauth.github.authorization_url', 'https://github.com/login/oauth/authorize', 'string', 'GitHub OAuth 授权地址'),
('oauth.github.token_url', 'https://github.com/login/oauth/access_token', 'string', 'GitHub OAuth 令牌地址'),
('oauth.github.userinfo_url', 'https://api.github.com/user', 'string', 'GitHub 当前用户信息地址'),
('oauth.github.scope', 'read:user user:email', 'string', 'GitHub 登录最小授权范围'),
('oauth.github.redirect_uri', 'http://127.0.0.1:8888/admin/v1/auth/oauth/callback', 'string', 'GitHub OAuth App 登记的 callback URL;生产环境必须使用 HTTPS'),
('oauth.google.enabled', 'false', 'boolean', '启用 Google 登录'),
('oauth.google.client_id', '', 'string', 'Google OAuth 2.0 Client ID'),
('oauth.google.client_secret', '', 'secret', 'Google OAuth 2.0 Client Secret'),
('oauth.google.authorization_url', 'https://accounts.google.com/o/oauth2/v2/auth', 'string', 'Google OAuth 授权地址'),
('oauth.google.token_url', 'https://oauth2.googleapis.com/token', 'string', 'Google OAuth 令牌地址'),
('oauth.google.userinfo_url', 'https://openidconnect.googleapis.com/v1/userinfo', 'string', 'Google OpenID Connect UserInfo 地址'),
('oauth.google.scope', 'openid profile email', 'string', 'Google 登录授权范围'),
('oauth.google.redirect_uri', 'http://127.0.0.1:8888/admin/v1/auth/oauth/callback', 'string', 'Google Cloud Console 登记的 redirect URI;生产环境必须使用 HTTPS')
ON DUPLICATE KEY UPDATE description = VALUES(description);
@@ -0,0 +1,54 @@
SET NAMES utf8mb4;
CREATE TABLE IF NOT EXISTS user_oauth_identities (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
provider VARCHAR(20) NOT NULL,
subject VARCHAR(191) NOT NULL,
user_id BIGINT UNSIGNED NOT NULL,
email VARCHAR(255) NOT NULL DEFAULT '',
display_name VARCHAR(100) NOT NULL DEFAULT '',
avatar_url VARCHAR(500) NOT NULL DEFAULT '',
last_login_at DATETIME(3) NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_user_oauth_provider_subject (provider, subject),
UNIQUE KEY uk_user_oauth_user_provider (user_id, provider),
CONSTRAINT fk_user_oauth_identity_user FOREIGN KEY (user_id) REFERENCES users(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS user_oauth_states (
state_hash BINARY(32) NOT NULL,
provider VARCHAR(20) NOT NULL,
code_verifier VARCHAR(128) NOT NULL DEFAULT '',
expires_at DATETIME(3) NOT NULL,
used_at DATETIME(3) NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (state_hash),
KEY idx_user_oauth_state_expiry (expires_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS user_oauth_login_codes (
code_hash BINARY(32) NOT NULL,
provider VARCHAR(20) NOT NULL,
subject VARCHAR(191) NOT NULL,
email VARCHAR(255) NOT NULL DEFAULT '',
display_name VARCHAR(100) NOT NULL DEFAULT '',
avatar_url VARCHAR(500) NOT NULL DEFAULT '',
user_id BIGINT UNSIGNED NULL,
expires_at DATETIME(3) NOT NULL,
used_at DATETIME(3) NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (code_hash),
KEY idx_user_oauth_code_expiry (expires_at),
KEY idx_user_oauth_code_user (user_id),
CONSTRAINT fk_user_oauth_code_user FOREIGN KEY (user_id) REFERENCES users(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO system_configs (config_key, config_value, value_type, description) VALUES
('oauth.user.frontend_callback_url', 'http://localhost:5174/#/pages/auth/oauth-callback', 'string', '第三方登录完成后跳转的 uni-app H5 页面;生产环境必须使用 HTTPS'),
('oauth.user.wechat.enabled', 'false', 'boolean', '在客户端启用微信登录'),
('oauth.user.qq.enabled', 'false', 'boolean', '在客户端启用 QQ 登录'),
('oauth.user.github.enabled', 'false', 'boolean', '在客户端启用 GitHub 登录'),
('oauth.user.google.enabled', 'false', 'boolean', '在客户端启用 Google 登录')
ON DUPLICATE KEY UPDATE description = VALUES(description);