945 行
51 KiB
TypeScript
945 行
51 KiB
TypeScript
import 'dotenv/config';
|
||
import mysql, { Pool } from 'mysql2/promise';
|
||
|
||
export const dbConfig = {
|
||
host: process.env.MYSQL_HOST || '127.0.0.1',
|
||
port: Number(process.env.MYSQL_PORT) || 3306,
|
||
user: process.env.MYSQL_USER || 'root',
|
||
password: process.env.MYSQL_PASSWORD || '123456',
|
||
database: process.env.MYSQL_DATABASE || 'peiwan',
|
||
charset: 'utf8mb4',
|
||
waitForConnections: true,
|
||
connectionLimit: 10,
|
||
queueLimit: 0,
|
||
};
|
||
|
||
export const pool: Pool = mysql.createPool(dbConfig);
|
||
|
||
// 初始化所有核心数据表与种子数据
|
||
export async function initDatabase(): Promise<void> {
|
||
const conn = await pool.getConnection();
|
||
try {
|
||
// 1. 活动表
|
||
await conn.query(`
|
||
CREATE TABLE IF NOT EXISTS activities (
|
||
id VARCHAR(64) PRIMARY KEY,
|
||
creator_id INT NOT NULL,
|
||
title VARCHAR(255) NOT NULL,
|
||
game_category VARCHAR(64) NOT NULL,
|
||
location VARCHAR(255),
|
||
pay_type INT NOT NULL DEFAULT 1,
|
||
total_fee_cents INT DEFAULT 0,
|
||
per_person_fee_cents INT DEFAULT 0,
|
||
max_participants INT DEFAULT 4,
|
||
allow_dynamic_price TINYINT DEFAULT 1,
|
||
price_adjust_rule VARCHAR(32) DEFAULT 'UNIFIED_HIGH',
|
||
status VARCHAR(32) DEFAULT 'RECRUITING',
|
||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||
`);
|
||
|
||
try {
|
||
await conn.query(`ALTER TABLE activities ADD COLUMN status VARCHAR(32) DEFAULT 'RECRUITING' AFTER price_adjust_rule`);
|
||
} catch (_) {
|
||
// 字段已存在忽略
|
||
}
|
||
|
||
// 2. 参与者与4位纯数字核销码表
|
||
await conn.query(`
|
||
CREATE TABLE IF NOT EXISTS participants (
|
||
id VARCHAR(64) PRIMARY KEY,
|
||
activity_id VARCHAR(64) NOT NULL,
|
||
user_id INT NOT NULL,
|
||
pay_amount_cents INT NOT NULL,
|
||
verify_code VARCHAR(4) NOT NULL,
|
||
checkin_status TINYINT DEFAULT 0,
|
||
verified_at DATETIME NULL,
|
||
verified_by INT NULL,
|
||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||
INDEX idx_activity (activity_id),
|
||
INDEX idx_verify_code (verify_code)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||
`);
|
||
|
||
// 3. 陪玩大神档案表
|
||
await conn.query(`
|
||
CREATE TABLE IF NOT EXISTS companions (
|
||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||
name VARCHAR(64) NOT NULL,
|
||
avatar TEXT,
|
||
gender VARCHAR(10),
|
||
age INT,
|
||
track VARCHAR(64) NOT NULL,
|
||
rank_name VARCHAR(64),
|
||
hourly_rate INT NOT NULL,
|
||
voice_duration INT DEFAULT 15,
|
||
voice_title VARCHAR(128),
|
||
rating DECIMAL(3,2) DEFAULT 4.90,
|
||
order_count INT DEFAULT 0,
|
||
status VARCHAR(32) DEFAULT '接单中',
|
||
audit_status VARCHAR(32) DEFAULT '已通过',
|
||
id_card_masked VARCHAR(32),
|
||
tags JSON,
|
||
apply_time DATETIME DEFAULT CURRENT_TIMESTAMP
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||
`);
|
||
|
||
// 动态为 companions 增加 tags 字段 (若历史表已存在)
|
||
try {
|
||
await conn.query('ALTER TABLE companions ADD COLUMN tags JSON AFTER id_card_masked');
|
||
} catch (_) {}
|
||
|
||
// 4. 订单表
|
||
await conn.query(`
|
||
CREATE TABLE IF NOT EXISTS orders (
|
||
id VARCHAR(64) PRIMARY KEY,
|
||
buyer_id INT DEFAULT 1001,
|
||
expert_id INT DEFAULT 101,
|
||
order_no VARCHAR(64) UNIQUE NOT NULL,
|
||
client_name VARCHAR(64) NOT NULL,
|
||
companion_name VARCHAR(64) NOT NULL,
|
||
expert_avatar TEXT,
|
||
track VARCHAR(64) NOT NULL,
|
||
service_title VARCHAR(255) DEFAULT '同城陪伴服务',
|
||
expense_burden VARCHAR(32) DEFAULT '客户全包',
|
||
hourly_rate INT NOT NULL,
|
||
duration_hours INT NOT NULL DEFAULT 2,
|
||
amount DECIMAL(10,2) NOT NULL,
|
||
location VARCHAR(255),
|
||
verify_code VARCHAR(8),
|
||
remark VARCHAR(255),
|
||
status VARCHAR(32) NOT NULL DEFAULT '待支付',
|
||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||
`);
|
||
|
||
// 动态增加移动端所需的扩展字段 (若历史表已存在则执行防断裂补丁)
|
||
const extraOrderCols = [
|
||
"ALTER TABLE orders ADD COLUMN buyer_id INT DEFAULT 1001 AFTER id",
|
||
"ALTER TABLE orders ADD COLUMN expert_id INT DEFAULT 101 AFTER buyer_id",
|
||
"ALTER TABLE orders ADD COLUMN expert_avatar TEXT AFTER companion_name",
|
||
"ALTER TABLE orders ADD COLUMN service_title VARCHAR(255) DEFAULT '同城陪伴服务' AFTER track",
|
||
"ALTER TABLE orders ADD COLUMN verify_code VARCHAR(8) AFTER location",
|
||
"ALTER TABLE orders ADD COLUMN remark VARCHAR(255) AFTER verify_code",
|
||
"ALTER TABLE orders ADD COLUMN pay_type VARCHAR(32) DEFAULT 'WECHAT' AFTER status",
|
||
"ALTER TABLE orders ADD COLUMN coupon_discount DECIMAL(10,2) DEFAULT 0.00 AFTER amount"
|
||
];
|
||
for (const colSql of extraOrderCols) {
|
||
try {
|
||
await conn.query(colSql);
|
||
} catch (_) {
|
||
// 字段已存在则忽略
|
||
}
|
||
}
|
||
|
||
// 5. 争议仲裁表
|
||
await conn.query(`
|
||
CREATE TABLE IF NOT EXISTS arbitrations (
|
||
id VARCHAR(64) PRIMARY KEY,
|
||
order_id VARCHAR(64) NOT NULL,
|
||
order_no VARCHAR(64) NOT NULL,
|
||
client_name VARCHAR(64) NOT NULL,
|
||
companion_name VARCHAR(64) NOT NULL,
|
||
reason VARCHAR(255) NOT NULL,
|
||
user_evidence TEXT,
|
||
status VARCHAR(32) NOT NULL DEFAULT '待平台仲裁',
|
||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||
`);
|
||
|
||
// 6. 资金财务流水表
|
||
await conn.query(`
|
||
CREATE TABLE IF NOT EXISTS finance_records (
|
||
id VARCHAR(64) PRIMARY KEY,
|
||
record_no VARCHAR(64) UNIQUE NOT NULL,
|
||
user_id INT,
|
||
type VARCHAR(64) NOT NULL,
|
||
amount DECIMAL(10,2) NOT NULL,
|
||
direction VARCHAR(8) NOT NULL DEFAULT 'IN',
|
||
counterparty VARCHAR(64),
|
||
related_id VARCHAR(64),
|
||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||
`);
|
||
|
||
try {
|
||
await conn.query('ALTER TABLE finance_records ADD COLUMN user_id INT AFTER record_no');
|
||
} catch (_) {}
|
||
|
||
// 7. 提现申请表
|
||
await conn.query(`
|
||
CREATE TABLE IF NOT EXISTS withdrawals (
|
||
id VARCHAR(64) PRIMARY KEY,
|
||
withdraw_no VARCHAR(64) UNIQUE NOT NULL,
|
||
companion_name VARCHAR(64) NOT NULL,
|
||
companion_id INT NOT NULL,
|
||
amount DECIMAL(10,2) NOT NULL,
|
||
balance DECIMAL(10,2) NOT NULL,
|
||
channel VARCHAR(64) DEFAULT '微信商户零钱',
|
||
status VARCHAR(32) DEFAULT '待审核',
|
||
apply_time DATETIME DEFAULT CURRENT_TIMESTAMP
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||
`);
|
||
|
||
try {
|
||
await conn.query('ALTER TABLE withdrawals ADD COLUMN reason VARCHAR(255) DEFAULT ""');
|
||
} catch (_) {}
|
||
|
||
// 8. 线下合规场所库
|
||
await conn.query(`
|
||
CREATE TABLE IF NOT EXISTS safety_places (
|
||
id VARCHAR(64) PRIMARY KEY,
|
||
name VARCHAR(128) NOT NULL,
|
||
area VARCHAR(128) NOT NULL,
|
||
category VARCHAR(64) NOT NULL,
|
||
safety_level VARCHAR(64) DEFAULT 'AAA级合规认证',
|
||
status VARCHAR(32) DEFAULT '正常营业',
|
||
approved TINYINT DEFAULT 1
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||
`);
|
||
|
||
// 9. 风控拦截日志表
|
||
await conn.query(`
|
||
CREATE TABLE IF NOT EXISTS safety_audit_logs (
|
||
id VARCHAR(64) PRIMARY KEY,
|
||
timestamp_str VARCHAR(64) NOT NULL,
|
||
user_id INT NOT NULL,
|
||
type VARCHAR(64) NOT NULL,
|
||
content TEXT NOT NULL,
|
||
action TEXT NOT NULL,
|
||
status VARCHAR(32) DEFAULT '已处置',
|
||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||
`);
|
||
|
||
// 10. 财务抽成与分销配置表
|
||
await conn.query(`
|
||
CREATE TABLE IF NOT EXISTS finance_config (
|
||
id INT PRIMARY KEY DEFAULT 1,
|
||
platform_rate_percent DECIMAL(5,2) DEFAULT 10.0,
|
||
first_referral_percent DECIMAL(5,2) DEFAULT 5.0,
|
||
second_referral_percent DECIMAL(5,2) DEFAULT 2.0,
|
||
min_withdraw_amount INT DEFAULT 50,
|
||
max_withdraw_amount INT DEFAULT 20000,
|
||
withdraw_fee_percent DECIMAL(5,2) DEFAULT 0.0
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||
`);
|
||
|
||
// 11. 全平台注册用户表 (普通玩家/认证大神/房主)
|
||
await conn.query(`
|
||
CREATE TABLE IF NOT EXISTS users (
|
||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||
user_no VARCHAR(32) UNIQUE NOT NULL,
|
||
nickname VARCHAR(64) NOT NULL,
|
||
avatar TEXT,
|
||
phone VARCHAR(32) NOT NULL,
|
||
gender VARCHAR(10) DEFAULT '保密',
|
||
role VARCHAR(32) NOT NULL DEFAULT '普通玩家',
|
||
balance DECIMAL(10,2) DEFAULT 0.00,
|
||
order_count INT DEFAULT 0,
|
||
activity_count INT DEFAULT 0,
|
||
status VARCHAR(32) DEFAULT '正常',
|
||
wechat_openid VARCHAR(64),
|
||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||
`);
|
||
|
||
// 12. 友圈动态表
|
||
await conn.query(`
|
||
CREATE TABLE IF NOT EXISTS posts (
|
||
id VARCHAR(64) PRIMARY KEY,
|
||
user_id INT NOT NULL DEFAULT 1001,
|
||
author VARCHAR(64) NOT NULL,
|
||
avatar TEXT,
|
||
tag VARCHAR(64) DEFAULT '同城搭子',
|
||
tag_type VARCHAR(32) DEFAULT 'warm-tag',
|
||
location VARCHAR(255) DEFAULT '北京市公开商圈',
|
||
distance VARCHAR(64) DEFAULT '100m内',
|
||
content TEXT NOT NULL,
|
||
topics JSON,
|
||
images JSON,
|
||
action_text VARCHAR(32) DEFAULT '加入车队',
|
||
likes INT DEFAULT 0,
|
||
comments INT DEFAULT 0,
|
||
views INT DEFAULT 1,
|
||
category VARCHAR(32) NOT NULL DEFAULT 'partner',
|
||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||
`);
|
||
|
||
// 13. 订单真实评价与评分表
|
||
await conn.query(`
|
||
CREATE TABLE IF NOT EXISTS order_reviews (
|
||
id VARCHAR(64) PRIMARY KEY,
|
||
order_id VARCHAR(64) NOT NULL,
|
||
expert_id INT NOT NULL,
|
||
user_id INT NOT NULL DEFAULT 1001,
|
||
user_nickname VARCHAR(64) NOT NULL DEFAULT '买家用户',
|
||
user_avatar TEXT,
|
||
rating INT NOT NULL DEFAULT 5,
|
||
tags JSON,
|
||
content TEXT,
|
||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||
INDEX idx_order (order_id),
|
||
INDEX idx_expert (expert_id)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||
`);
|
||
|
||
// 14. 友圈动态互动评论表
|
||
await conn.query(`
|
||
CREATE TABLE IF NOT EXISTS post_comments (
|
||
id VARCHAR(64) PRIMARY KEY,
|
||
post_id VARCHAR(64) NOT NULL,
|
||
user_id INT NOT NULL DEFAULT 1001,
|
||
author VARCHAR(64) NOT NULL DEFAULT '热心搭子',
|
||
avatar TEXT,
|
||
content TEXT NOT NULL,
|
||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||
INDEX idx_post (post_id)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||
`);
|
||
|
||
// 15. 友圈搭子互动约伴上车申请表
|
||
await conn.query(`
|
||
CREATE TABLE IF NOT EXISTS post_applies (
|
||
id VARCHAR(64) PRIMARY KEY,
|
||
post_id VARCHAR(64) NOT NULL,
|
||
applicant_id INT NOT NULL DEFAULT 1001,
|
||
applicant_name VARCHAR(64) NOT NULL DEFAULT '搭子玩家',
|
||
applicant_avatar TEXT,
|
||
status VARCHAR(32) NOT NULL DEFAULT 'PENDING',
|
||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||
INDEX idx_post_applies (post_id)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||
`);
|
||
|
||
// 16. 首页轮播图运营配置表
|
||
await conn.query(`
|
||
CREATE TABLE IF NOT EXISTS banners (
|
||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||
title VARCHAR(128) NOT NULL,
|
||
subtitle VARCHAR(255) DEFAULT '',
|
||
tag VARCHAR(64) DEFAULT '热门活动',
|
||
badge VARCHAR(64) DEFAULT 'HOT 热门组局',
|
||
badge_type VARCHAR(64) DEFAULT 'badge-hot',
|
||
bg VARCHAR(255) DEFAULT 'linear-gradient(135deg, #F43F5E 0%, #FB7185 100%)',
|
||
image TEXT,
|
||
link VARCHAR(255) DEFAULT '/pages/activity/list',
|
||
sort_order INT DEFAULT 0,
|
||
status TINYINT DEFAULT 1,
|
||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||
INDEX idx_sort (sort_order),
|
||
INDEX idx_status (status)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||
`);
|
||
|
||
// 17. 首页金刚区活动品类导航配置表
|
||
await conn.query(`
|
||
CREATE TABLE IF NOT EXISTS nav_categories (
|
||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||
code VARCHAR(64) UNIQUE NOT NULL,
|
||
name VARCHAR(64) NOT NULL,
|
||
icon TEXT NOT NULL,
|
||
bg_class VARCHAR(64) DEFAULT 'bg-indigo',
|
||
cat VARCHAR(64) DEFAULT 'all',
|
||
target_url VARCHAR(255) NOT NULL DEFAULT '/pages/activity/list?category=ALL',
|
||
badge_text VARCHAR(32) DEFAULT '',
|
||
sort_order INT DEFAULT 0,
|
||
status TINYINT DEFAULT 1,
|
||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||
INDEX idx_sort (sort_order),
|
||
INDEX idx_status (status)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||
`);
|
||
|
||
// 18. 钱包充值阶梯与赠送配置表
|
||
await conn.query(`
|
||
CREATE TABLE IF NOT EXISTS recharge_tiers (
|
||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||
amount DECIMAL(10,2) NOT NULL,
|
||
bonus_amount DECIMAL(10,2) DEFAULT 0.00,
|
||
tag VARCHAR(32) DEFAULT '',
|
||
sort_order INT DEFAULT 0,
|
||
status TINYINT DEFAULT 1,
|
||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||
INDEX idx_sort (sort_order),
|
||
INDEX idx_status (status)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||
`);
|
||
|
||
// 19. 平台营销优惠券配置与发放表
|
||
await conn.query(`
|
||
CREATE TABLE IF NOT EXISTS coupons (
|
||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||
name VARCHAR(64) NOT NULL,
|
||
discount_amount DECIMAL(10,2) NOT NULL,
|
||
min_spend DECIMAL(10,2) NOT NULL DEFAULT 0.00,
|
||
valid_days INT NOT NULL DEFAULT 7,
|
||
total_count INT NOT NULL DEFAULT 1000,
|
||
received_count INT NOT NULL DEFAULT 0,
|
||
category_limit VARCHAR(64) DEFAULT 'ALL',
|
||
description VARCHAR(255) DEFAULT '',
|
||
status TINYINT DEFAULT 1,
|
||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||
INDEX idx_status (status)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||
`);
|
||
|
||
// 20. 平台官方客服、公告及服务协议通用配置表
|
||
await conn.query(`
|
||
CREATE TABLE IF NOT EXISTS platform_configs (
|
||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||
config_key VARCHAR(64) UNIQUE NOT NULL,
|
||
name VARCHAR(128) NOT NULL,
|
||
value_json JSON NOT NULL,
|
||
description VARCHAR(255) DEFAULT '',
|
||
status TINYINT DEFAULT 1,
|
||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||
INDEX idx_key (config_key)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||
`);
|
||
|
||
// 初始化配置单条记录
|
||
const [cfgRows]: any = await conn.query('SELECT COUNT(*) as count FROM finance_config WHERE id = 1');
|
||
if (cfgRows[0].count === 0) {
|
||
await conn.query(`
|
||
INSERT INTO finance_config (id, platform_rate_percent, first_referral_percent, second_referral_percent, min_withdraw_amount, max_withdraw_amount, withdraw_fee_percent)
|
||
VALUES (1, 10.0, 5.0, 2.0, 50, 20000, 0.0);
|
||
`);
|
||
}
|
||
|
||
// 扩展 users 表结构: commission, invited_count
|
||
try {
|
||
await conn.query('ALTER TABLE users ADD COLUMN commission DECIMAL(10,2) DEFAULT 0.00');
|
||
} catch (_) {}
|
||
try {
|
||
await conn.query('ALTER TABLE users ADD COLUMN invited_count INT DEFAULT 0');
|
||
} catch (_) {}
|
||
try {
|
||
await conn.query('UPDATE users SET commission = 128.50, invited_count = 8 WHERE id = 1001 AND (commission IS NULL OR commission = 0)');
|
||
} catch (_) {}
|
||
// 扩展 users 表结构: 个性签名
|
||
try {
|
||
await conn.query("ALTER TABLE users ADD COLUMN signature VARCHAR(64) DEFAULT ''");
|
||
} catch (_) {}
|
||
// 确保存在 demo 账号 101 (金牌陪伴师/认证大神体验账号)
|
||
try {
|
||
await conn.query(`
|
||
INSERT INTO users (id, user_no, nickname, avatar, phone, gender, role, balance, commission, invited_count, order_count, activity_count, status, wechat_openid, signature)
|
||
VALUES (101, 'U101', '糖心小糯米', 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=200', '138****0101', '女', '认证大神', 2580.00, 128.50, 6, 88, 16, '正常', 'wx_openid_101', '人美声甜,擅长各类同城陪伴与开黑互动')
|
||
ON DUPLICATE KEY UPDATE nickname = VALUES(nickname)
|
||
`);
|
||
} catch (_) {}
|
||
|
||
// 21. 用户举报工单表 (涉黄/涉赌/线下欺诈等不良陪玩举报)
|
||
await conn.query(`
|
||
CREATE TABLE IF NOT EXISTS reports (
|
||
id VARCHAR(64) PRIMARY KEY,
|
||
reporter_id INT NOT NULL,
|
||
target_type VARCHAR(32) NOT NULL DEFAULT 'EXPERT',
|
||
target_id VARCHAR(64) NOT NULL,
|
||
target_name VARCHAR(64) DEFAULT '',
|
||
reason VARCHAR(64) NOT NULL,
|
||
description VARCHAR(500) DEFAULT '',
|
||
evidence JSON,
|
||
status VARCHAR(32) NOT NULL DEFAULT '待处理',
|
||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||
INDEX idx_target (target_type, target_id)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||
`);
|
||
|
||
// 22. 陪玩收藏表 (收藏喜欢的陪玩,实时高亮在接单状态)
|
||
await conn.query(`
|
||
CREATE TABLE IF NOT EXISTS favorites (
|
||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||
user_id INT NOT NULL,
|
||
expert_id INT NOT NULL,
|
||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||
UNIQUE KEY uk_user_expert (user_id, expert_id),
|
||
INDEX idx_user (user_id)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||
`);
|
||
|
||
// 23. 分销体系扩展 (下级团队与佣金订单记录)
|
||
try {
|
||
await conn.query('ALTER TABLE users ADD COLUMN inviter_id INT DEFAULT NULL');
|
||
} catch (_) {}
|
||
|
||
await conn.query(`
|
||
CREATE TABLE IF NOT EXISTS distribution_records (
|
||
id VARCHAR(64) PRIMARY KEY,
|
||
user_id INT NOT NULL,
|
||
from_user_id INT NOT NULL,
|
||
from_user_name VARCHAR(64) NOT NULL,
|
||
order_no VARCHAR(64) NOT NULL,
|
||
order_amount DECIMAL(10,2) NOT NULL,
|
||
commission DECIMAL(10,2) NOT NULL,
|
||
level INT NOT NULL DEFAULT 1,
|
||
rate_percent DECIMAL(5,2) NOT NULL,
|
||
status VARCHAR(32) NOT NULL DEFAULT '已入账',
|
||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||
INDEX idx_dist_user (user_id)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||
`);
|
||
|
||
// 检查并插入初始种子数据(若表为空)
|
||
await seedInitialDataIfEmpty(conn);
|
||
|
||
console.log('[MySQL] peiwan 数据库核心表及种子数据已完整就绪');
|
||
} finally {
|
||
conn.release();
|
||
}
|
||
}
|
||
|
||
async function seedInitialDataIfEmpty(conn: any): Promise<void> {
|
||
// 1. 种子活动
|
||
const [actRows]: any = await conn.query('SELECT COUNT(*) as count FROM activities');
|
||
if (actRows[0].count === 0) {
|
||
await conn.query(`
|
||
INSERT INTO activities (id, creator_id, title, game_category, location, pay_type, total_fee_cents, per_person_fee_cents, max_participants, allow_dynamic_price, price_adjust_rule)
|
||
VALUES
|
||
('act_101', 1001, '三里屯铜锅涮肉AA探店 · 8人热烈局', '同城陪吃', '北京市朝阳区三里屯聚宝源铜锅涮肉', 1, 40000, 5000, 4, 1, 'UNIFIED_HIGH'),
|
||
('act_102', 1002, '工体纯KTV沉浸式麦霸欢唱 · 寻找甜美副唱', '同城陪唱', '工体西路8号纯KTV 888包厢', 2, 240000, 80000, 3, 1, 'UNIFIED_HIGH'),
|
||
('act_103', 1003, '望京合生麒麟社微醺清吧精酿品鉴', '同城陪喝', '望京街道合生麒麟社地库跳海酒馆', 1, 39600, 19800, 4, 1, 'UNIFIED_HIGH');
|
||
`);
|
||
|
||
// 种子参与者与4位核销码
|
||
await conn.query(`
|
||
INSERT INTO participants (id, activity_id, user_id, pay_amount_cents, verify_code, checkin_status, verified_at, verified_by)
|
||
VALUES
|
||
('pt_101', 'act_101', 2001, 5000, '8392', 1, NOW(), 1001),
|
||
('pt_102', 'act_101', 2002, 5000, '5419', 0, NULL, NULL),
|
||
('pt_103', 'act_101', 2003, 5000, '7260', 0, NULL, NULL),
|
||
('pt_104', 'act_102', 2004, 80000, '9031', 0, NULL, NULL),
|
||
('pt_105', 'act_103', 2005, 19800, '6184', 0, NULL, NULL);
|
||
`);
|
||
}
|
||
|
||
// 2. 种子大神档案
|
||
const [compRows]: any = await conn.query('SELECT COUNT(*) as count FROM companions');
|
||
if (compRows[0].count === 0) {
|
||
await conn.query(`
|
||
INSERT INTO companions (name, avatar, gender, age, track, rank_name, hourly_rate, voice_duration, voice_title, rating, order_count, status, audit_status, id_card_masked)
|
||
VALUES
|
||
('苏苏学姐', 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=120', '女', 22, '同城陪吃', '金牌探店官', 158, 18, '甜美学姐元气语音条', 4.95, 382, '接单中', '已通过', '110105********3821'),
|
||
('林深见鹿', 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=120', '男', 24, 'KTV陪唱', '十佳青年歌手', 288, 25, '清澈烟嗓流行情歌试听', 4.98, 512, '接单中', '已通过', '310115********1028'),
|
||
('小鱼泡泡', 'https://images.unsplash.com/photo-1517841905240-472988babdf9?w=120', '女', 21, '同城陪喝', '精酿侍酒达人', 198, 14, '微醺甜美随和聊天', 4.88, 194, '休息中', '已通过', '440301********7824'),
|
||
('顾北辰', 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=120', '男', 23, '线上电竞', '百星王者国服第一射手', 88, 12, '低沉磁性指挥型男音', 4.92, 680, '接单中', '已通过', '510107********4419'),
|
||
('可可奶芙', 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=120', '女', 20, '同城陪吃', '美食品鉴官', 168, 16, '软糯元气自我介绍', 4.85, 42, '待审核', '待审核', '320106********9124'),
|
||
('夜风沉醉', 'https://images.unsplash.com/photo-1522075469751-3a6694fb2f61?w=120', '男', 25, 'KTV陪唱', '摇滚乐队副主唱', 220, 22, '摇滚副歌现场高音试听', 4.75, 18, '待审核', '待审核', '330104********2816');
|
||
`);
|
||
}
|
||
|
||
// 3. 种子订单
|
||
const [ordRows]: any = await conn.query('SELECT COUNT(*) as count FROM orders');
|
||
if (ordRows[0].count === 0) {
|
||
await conn.query(`
|
||
INSERT INTO orders (id, order_no, client_name, companion_name, track, expense_burden, hourly_rate, duration_hours, amount, location, status)
|
||
VALUES
|
||
('ord_01', 'PW202609280001', '赵公子', '苏苏学姐', '同城陪吃', '客户全包', 158, 2, 316.00, '朝阳区大悦城 太二酸菜鱼', '已到场核销'),
|
||
('ord_02', 'PW202609280002', '钱老板', '林深见鹿', 'KTV陪唱', '客户全包', 288, 3, 864.00, '工体西路8号纯KTV 888包厢', '履约中'),
|
||
('ord_03', 'PW202609280003', '孙先生', '小鱼泡泡', '同城陪喝', '费用AA', 198, 2, 396.00, '望京合生麒麟社跳海酒馆', '待支付'),
|
||
('ord_04', 'PW202609280004', '李小姐', '顾北辰', '线上电竞', '无需承担', 88, 4, 352.00, '线上语音频道 #9021', '已完成'),
|
||
('ord_05', 'PW202609280005', '周先生', '林深见鹿', 'KTV陪唱', '客户全包', 288, 2, 576.00, '海淀中关村好乐迪KTV', '争议仲裁中');
|
||
`);
|
||
}
|
||
|
||
// 4. 种子仲裁
|
||
const [arbRows]: any = await conn.query('SELECT COUNT(*) as count FROM arbitrations');
|
||
if (arbRows[0].count === 0) {
|
||
await conn.query(`
|
||
INSERT INTO arbitrations (id, order_id, order_no, client_name, companion_name, reason, user_evidence, status)
|
||
VALUES
|
||
('arb_001', 'ord_05', 'PW202609280005', '周先生', '林深见鹿', '未准时到场且提前离场', '大神迟到了35分钟,且中途以有急事为由提前40分钟离开,实际服务仅45分钟。现场有微信沟通记录与到场照片。', '待平台仲裁');
|
||
`);
|
||
}
|
||
|
||
// 5. 种子财务流水
|
||
const [finRows]: any = await conn.query('SELECT COUNT(*) as count FROM finance_records');
|
||
if (finRows[0].count === 0) {
|
||
await conn.query(`
|
||
INSERT INTO finance_records (id, record_no, type, amount, direction, counterparty, related_id)
|
||
VALUES
|
||
('fin_001', 'TR20260928001', '活动报名入账', 50.00, 'IN', '用户 2001', 'act_101'),
|
||
('fin_002', 'TR20260928002', '活动报名入账', 50.00, 'IN', '用户 2002', 'act_101'),
|
||
('fin_003', 'TR20260928003', '大神订单全款', 316.00, 'IN', '客户 赵公子', 'ord_01'),
|
||
('fin_004', 'TR20260928004', '平台抽成 (10%)', 31.60, 'IN', '系统结算', 'ord_01'),
|
||
('fin_005', 'TR20260928005', '一级分销佣金 (5%)', 15.80, 'OUT', '推荐人 张三', 'ord_01'),
|
||
('fin_006', 'TR20260928006', '房主追加赏金差额退补', 300.00, 'IN', '房主 1001', 'act_101');
|
||
`);
|
||
}
|
||
|
||
// 6. 种子提现
|
||
const [wdRows]: any = await conn.query('SELECT COUNT(*) as count FROM withdrawals');
|
||
if (wdRows[0].count === 0) {
|
||
await conn.query(`
|
||
INSERT INTO withdrawals (id, withdraw_no, companion_name, companion_id, amount, balance, channel, status)
|
||
VALUES
|
||
('wd_001', 'TX20260928001', '苏苏学姐', 101, 1250.00, 4890.00, '微信商户零钱', '待审核'),
|
||
('wd_002', 'TX20260928002', '顾北辰', 104, 2800.00, 6200.00, '银联企业直连', '已打款');
|
||
`);
|
||
}
|
||
|
||
// 7. 种子场所
|
||
const [placeRows]: any = await conn.query('SELECT COUNT(*) as count FROM safety_places');
|
||
if (placeRows[0].count === 0) {
|
||
await conn.query(`
|
||
INSERT INTO safety_places (id, name, area, category, safety_level, status, approved)
|
||
VALUES
|
||
('place_001', '三里屯聚宝源铜锅涮肉', '朝阳区三里屯商圈', '餐饮美食', 'AAA级合规认证', '正常营业', 1),
|
||
('place_002', '工体纯KTV', '朝阳区工人体育场商圈', '量贩KTV', 'AAA级合规认证', '正常营业', 1),
|
||
('place_003', '望京合生麒麟社跳海酒馆', '朝阳区望京商圈', '商圈清吧', 'AA级合规认证', '正常营业', 1),
|
||
('place_004', '朝阳大悦城太二酸菜鱼', '朝阳区青年路大悦城', '餐饮美食', 'AAA级合规认证', '正常营业', 1);
|
||
`);
|
||
}
|
||
|
||
// 8. 种子风控拦截日志
|
||
const [secRows]: any = await conn.query('SELECT COUNT(*) as count FROM safety_audit_logs');
|
||
if (secRows[0].count === 0) {
|
||
await conn.query(`
|
||
INSERT INTO safety_audit_logs (id, timestamp_str, user_id, type, content, action, status)
|
||
VALUES
|
||
('sec_001', '2026-09-28 14:12:05', 2088, '违规场所拦截', '用户尝试将线下陪吃地点填写为:某某苑4号楼2单元501室(私人住宅)', '系统自动阻断下单,弹出公共商圈合规红线告警', '已处置'),
|
||
('sec_002', '2026-09-28 11:35:20', 2095, '违规场所拦截', '用户尝试将线下陪唱地点填写为:某某精品快捷酒店308客房', '系统拦截并强制要求修改为正规商业量贩KTV', '已处置'),
|
||
('sec_003', '2026-09-27 21:04:18', 2012, '敏感词过滤', '组局留言中出现敏感违规引流词汇汇款、加私微等', '关键词自动打码屏蔽,并记录账号风控提示分', '已处置');
|
||
`);
|
||
}
|
||
|
||
// 9. 种子全平台注册用户
|
||
const [userRows]: any = await conn.query('SELECT COUNT(*) as count FROM users');
|
||
if (userRows[0].count === 0) {
|
||
await conn.query(`
|
||
INSERT INTO users (id, user_no, nickname, avatar, phone, gender, role, balance, order_count, activity_count, status, wechat_openid, created_at)
|
||
VALUES
|
||
(1001, 'U1001', '同城局长', 'https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=120', '138****1001', '男', '同城房主', 1880.00, 8, 12, '正常', 'wx_openid_1001', '2026-09-01 10:00:00'),
|
||
(2001, 'U2001', '涮肉小行家', 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=120', '139****2001', '女', '普通玩家', 260.00, 15, 6, '正常', 'wx_openid_2001', '2026-09-05 14:20:00'),
|
||
(2002, 'U2002', '吃货小张', 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=120', '136****2002', '男', '普通玩家', 120.00, 6, 2, '正常', 'wx_openid_2002', '2026-09-10 11:15:00'),
|
||
(2003, 'U2003', 'K歌达人阿伟', 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=120', '137****2003', '男', '普通玩家', 350.00, 21, 9, '正常', 'wx_openid_2003', '2026-09-12 18:30:00'),
|
||
(2004, 'U2004', '电竞刺客', 'https://images.unsplash.com/photo-1517841905240-472988babdf9?w=120', '135****2004', '男', '普通玩家', 50.00, 9, 3, '正常', 'wx_openid_2004', '2026-09-15 20:00:00'),
|
||
(2005, 'U2005', '橙子小七', 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=120', '138****2005', '女', '认证大神', 4280.00, 420, 28, '正常', 'wx_openid_2005', '2026-09-02 09:00:00'),
|
||
(2006, 'U2006', '苏苏学姐', 'https://images.unsplash.com/photo-1524504388940-b1c1722653e1?w=120', '139****2006', '女', '认证大神', 5690.00, 560, 35, '正常', 'wx_openid_2006', '2026-09-03 16:45:00'),
|
||
(2007, 'U2007', '莫吉托学长', 'https://images.unsplash.com/photo-1506794778202-cad84cf45f1d?w=120', '137****2007', '男', '认证大神', 3100.00, 310, 18, '正常', 'wx_openid_2007', '2026-09-08 12:20:00'),
|
||
(2008, 'U2008', '测试玩家小李', 'https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=120', '188****2008', '男', '普通玩家', 88.00, 2, 1, '正常', 'wx_openid_2008', '2026-09-28 10:10:00'),
|
||
(2009, 'U2009', '风险异常号', 'https://images.unsplash.com/photo-1492562080023-ab3db95bfbce?w=120', '131****2009', '未知', '普通玩家', 0.00, 0, 0, '已封禁', 'wx_openid_2009', '2026-09-27 15:30:00');
|
||
`);
|
||
}
|
||
|
||
// 10. 种子友圈动态
|
||
const [postRows]: any = await conn.query('SELECT COUNT(*) as count FROM posts');
|
||
if (postRows[0].count === 0) {
|
||
await conn.query(`
|
||
INSERT INTO posts (id, user_id, author, avatar, tag, tag_type, location, distance, content, topics, images, action_text, likes, comments, views, category, created_at)
|
||
VALUES
|
||
('post_1', 2005, '小甜橙', 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=150', '火锅达人', 'warm-tag', '朝阳区 · 三里屯太古里', '800m', '今晚7点半有没有在三里屯附近的小伙伴一起去吃铜锅涮肉?已预订包间,目前已有2人,再来2个饭搭子AA!吃完还可以顺路去清吧听歌放松~', '["三里屯探店", "铜锅涮肉AA", "饭搭子招募"]', '["https://images.unsplash.com/photo-1555396273-367ea4eb4db5?w=500", "https://images.unsplash.com/photo-1517248135467-4c7edcad34c4?w=500"]', '加入车队', 42, 8, 310, 'partner', NOW() - INTERVAL 15 MINUTE),
|
||
('post_2', 2004, '暗影刺客 (省一李白)', 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=150', '国服大神', 'cool-tag', '海淀区 · 五道口', '3.5km', '今晚王者荣耀荣耀王者冲分车队!目前缺一个听指挥的中单或者软辅妹子,全程开麦包赢不保段包赔,想上星的速速滴我!', '["王者荣耀", "荣耀车队", "连胜冲分"]', '["https://images.unsplash.com/photo-1542751371-adc38448a05e?w=500"]', '立即上车', 76, 15, 680, 'game', NOW() - INTERVAL 40 MINUTE),
|
||
('post_3', 2006, '苏苏学姐', 'https://images.unsplash.com/photo-1517841905240-472988babdf9?w=150', '金牌麦霸', 'gold-tag', '朝阳区 · 工体商业街', '1.2km', '工体纯K量贩欢唱局!甜美情歌、粤语老歌、摇滚气氛拉满。现场备有精美果盘与特饮,今晚欢唱不嗨不散,还有专属歌声试听哦!', '["KTV欢唱", "量贩陪唱", "同城聚会"]', '["https://images.unsplash.com/photo-1516450360452-9312f5e86fc7?w=500"]', '报名同去', 58, 11, 520, 'partner', NOW() - INTERVAL 1 HOUR);
|
||
`);
|
||
}
|
||
|
||
// 11. 种子订单真实评价
|
||
const [revRows]: any = await conn.query('SELECT COUNT(*) as count FROM order_reviews');
|
||
if (revRows[0].count === 0) {
|
||
await conn.query(`
|
||
INSERT INTO order_reviews (id, order_id, expert_id, user_id, user_nickname, user_avatar, rating, tags, content, created_at)
|
||
VALUES
|
||
('rev_101', 'ord_04', 104, 1001, '李小姐', 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=120', 5, '["技术过硬", "耐心温柔", "连胜吃鸡"]', '北辰大神意识太好了,几波团战残血反杀,全程听指挥轻轻松松上大分!', NOW() - INTERVAL 1 DAY),
|
||
('rev_102', 'ord_01', 101, 2001, '赵公子', 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=120', 5, '["随和健谈", "品味极佳", "准时守约"]', '苏苏学姐推荐的涮肉店真的一绝,性格温柔幽默,聚餐氛围特别好!', NOW() - INTERVAL 2 DAY);
|
||
`);
|
||
}
|
||
|
||
// 12. 种子友圈动态评论
|
||
const [cmtRows]: any = await conn.query('SELECT COUNT(*) as count FROM post_comments');
|
||
if (cmtRows[0].count === 0) {
|
||
await conn.query(`
|
||
INSERT INTO post_comments (id, post_id, user_id, author, avatar, content, created_at)
|
||
VALUES
|
||
('cmt_1', 'post_1', 2002, '吃货小张', 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=120', '三里屯聚宝源吗?这家手切羊肉超棒,已报名+1!', NOW() - INTERVAL 10 MINUTE),
|
||
('cmt_2', 'post_1', 2006, '苏苏学姐', 'https://images.unsplash.com/photo-1524504388940-b1c1722653e1?w=120', '吃完可以去附近跳海酒馆坐坐,环境很舒服~', NOW() - INTERVAL 5 MINUTE),
|
||
('cmt_3', 'post_2', 2001, '涮肉小行家', 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=120', '国服李白太强了!主玩大乔和张飞,晚上可以一起开黑吗?', NOW() - INTERVAL 25 MINUTE);
|
||
`);
|
||
}
|
||
|
||
// 13. 种子轮播图
|
||
const [bnrRows]: any = await conn.query('SELECT COUNT(*) as count FROM banners');
|
||
if (bnrRows[0].count === 0) {
|
||
await conn.query(`
|
||
INSERT INTO banners (id, title, subtitle, tag, badge, badge_type, bg, image, link, sort_order, status)
|
||
VALUES
|
||
(1, '同城聚会狂欢 | 网红火锅探店与量贩KTV嗨唱', 'AA制铜锅涮肉 · 8人热烈局', '热门陪伴', 'HOT 热门组局', 'badge-hot', 'linear-gradient(135deg, #F43F5E 0%, #FB7185 100%)', 'https://images.unsplash.com/photo-1555396273-367ea4eb4db5?w=800', '/pages/activity/detail?id=act_101', 1, 1),
|
||
(2, '量贩KTV麦霸沉浸欢唱 | 音乐达人歌声试听', '工体纯K 888豪华包厢', 'K歌对唱', '同城陪唱', 'badge-ktv', 'linear-gradient(135deg, #7C3AED 0%, #6366F1 100%)', 'https://images.unsplash.com/photo-1516450360452-9312f5e86fc7?w=800', '/pages/activity/detail?id=act_102', 2, 1),
|
||
(3, '精酿清吧微醺畅聊 | 线下陪吃陪喝立减30元', '望京跳海酒馆 · 慢调交友', '同城陪喝', '同城陪喝', 'badge-bar', 'linear-gradient(135deg, #059669 0%, #10B981 100%)', 'https://images.unsplash.com/photo-1517248135467-4c7edcad34c4?w=800', '/pages/activity/detail?id=act_103', 3, 1);
|
||
`);
|
||
}
|
||
|
||
// 14. 种子金刚区活动品类导航
|
||
const [navRows]: any = await conn.query('SELECT COUNT(*) as count FROM nav_categories');
|
||
if (navRows[0].count === 0) {
|
||
await conn.query(`
|
||
INSERT INTO nav_categories (id, code, name, icon, bg_class, cat, target_url, badge_text, sort_order, status)
|
||
VALUES
|
||
(1, 'esports', '电子竞技', 'gameAll', 'bg-indigo', 'all', '/pages/activity/list?category=ALL', 'HOT', 1, 1),
|
||
(2, 'meal', '美食探店', 'utensils', 'bg-amber', 'offline_meal', '/pages/activity/list?category=MEAL', 'AA制', 2, 1),
|
||
(3, 'sing', 'KTV嗨唱', 'micVocal', 'bg-rose', 'offline_sing', '/pages/activity/list?category=SING', '热度高', 3, 1),
|
||
(4, 'billiards', '同城台球', 'billiards', 'bg-sky', 'offline_play', '/pages/activity/list?category=ALL', '', 4, 1),
|
||
(5, 'hiking', '户外爬山', 'mountain', 'bg-emerald', 'offline_play', '/pages/activity/list?category=ALL', '周末', 5, 1),
|
||
(6, 'drink', '下午茶歇', 'coffee', 'bg-teal', 'offline_drink', '/pages/activity/list?category=DRINK', '', 6, 1),
|
||
(7, 'boardgame', '剧本桌游', 'dice', 'bg-purple', 'offline_play', '/pages/activity/list?category=ALL', '', 7, 1),
|
||
(8, 'fitness', '运动健身', 'fitness', 'bg-orange', 'offline_play', '/pages/activity/list?category=ALL', '', 8, 1),
|
||
(9, 'bar', '同城微醺', 'warmFlame', 'bg-rose', 'offline_drink', '/pages/activity/list?category=DRINK', '清吧', 9, 1),
|
||
(10, 'more', '全部活动', 'gridAll', 'bg-blue', 'all', '/pages/activity/list?category=ALL', '', 10, 1);
|
||
`);
|
||
}
|
||
|
||
// 15. 种子充值阶梯
|
||
const [tierRows]: any = await conn.query('SELECT COUNT(*) as count FROM recharge_tiers');
|
||
if (tierRows[0].count === 0) {
|
||
await conn.query(`
|
||
INSERT INTO recharge_tiers (id, amount, bonus_amount, tag, sort_order, status)
|
||
VALUES
|
||
(1, 50.00, 0.00, '基础充值', 1, 1),
|
||
(2, 100.00, 5.00, '热门畅享', 2, 1),
|
||
(3, 200.00, 15.00, '超值加赠', 3, 1),
|
||
(4, 500.00, 50.00, '尊享特惠', 4, 1),
|
||
(5, 1000.00, 120.00, '土豪专享', 5, 1);
|
||
`);
|
||
}
|
||
|
||
// 16. 种子平台优惠券
|
||
const [cpnRows]: any = await conn.query('SELECT COUNT(*) as count FROM coupons');
|
||
if (cpnRows[0].count === 0) {
|
||
await conn.query(`
|
||
INSERT INTO coupons (id, name, discount_amount, min_spend, valid_days, total_count, received_count, category_limit, description, status)
|
||
VALUES
|
||
(1, '新用户注册专享立减券', 20.00, 50.00, 15, 2000, 128, 'ALL', '全品类陪玩及同城组局满50可用', 1),
|
||
(2, '周末同城聚会AA抵扣券', 15.00, 80.00, 7, 1000, 85, 'MEAL', '仅限同城美食探店组局活动使用', 1),
|
||
(3, '工体K歌麦霸欢唱减免券', 30.00, 120.00, 10, 800, 62, 'SING', '仅限KTV陪唱预约与活动使用', 1),
|
||
(4, '电竞开黑连胜冲星券', 10.00, 30.00, 30, 5000, 340, 'ALL', '线上游戏陪玩下单立减', 1);
|
||
`);
|
||
}
|
||
|
||
// 17. 种子平台通用配置(官方客服、履约公告指引、规范协议、话题、公告、FAQ)
|
||
const communityTopicsVal = JSON.stringify([
|
||
{ id: 'top_1', name: '三里屯探店', hot: true, postsCount: 1280 },
|
||
{ id: 'top_2', name: '铜锅涮肉AA', hot: true, postsCount: 890 },
|
||
{ id: 'top_3', name: '工体纯K麦霸', hot: false, postsCount: 650 },
|
||
{ id: 'top_4', name: '国服上分车队', hot: true, postsCount: 2310 },
|
||
{ id: 'top_5', name: '望京跳海微醺', hot: false, postsCount: 420 },
|
||
{ id: 'top_6', name: '同城搭子招募', hot: true, postsCount: 3100 }
|
||
]);
|
||
|
||
const communityNoticeVal = JSON.stringify({
|
||
title: '友圈同城互动与公约置顶',
|
||
content: '欢迎来到同城搭子友圈!文明交友,发布真实组局诉求;平台全天候对动态与评论实施微信官方内容安全过滤,严禁广告引流及私下交易。',
|
||
tag: '置顶推荐',
|
||
showInBanner: true
|
||
});
|
||
|
||
const faqListVal = JSON.stringify([
|
||
{
|
||
id: 'faq_1',
|
||
category: '新手预约与组局',
|
||
question: '什么是4位纯数字核销码?',
|
||
answer: '为确保搭子碰面和订单真实履约,下单成功或报名组局后系统自动生成专属4位防伪数字码,到达公开活动现场后出示给陪伴师或房主签到核销。'
|
||
},
|
||
{
|
||
id: 'faq_2',
|
||
category: '新手预约与组局',
|
||
question: 'AA制拼桌与房主请客有什么区别?',
|
||
answer: '【AA制拼桌】由参与人员平摊费用,报名时支付人均分摊金额;【房主请客/项目制】由发起房主全额买单,且房主可随时追加赏金或提高出价。'
|
||
},
|
||
{
|
||
id: 'faq_3',
|
||
category: '资金与退款',
|
||
question: '如何申请退款与争议仲裁?',
|
||
answer: '在订单【待接单】或【待付款】状态可随时取消全额退款;在【服务中】或遇违约可在订单详情点击【发起争议仲裁】,由平台官方运营人工介入判定。'
|
||
},
|
||
{
|
||
id: 'faq_4',
|
||
category: '大神与收益',
|
||
question: '成为陪玩大神有什么资质要求?',
|
||
answer: '需在小程序个人中心提交实名认证与技能资质,通过平台运营团队人工审核后,即可解锁接单工作台与附近雷达扫单功能。'
|
||
}
|
||
]);
|
||
|
||
const securityWordsVal = JSON.stringify([
|
||
'加微信私聊', '私下走微信', '走线下免佣金', '线下给现金', '私下单',
|
||
'联系方式v', 'vx号', '微信转账', '私加好友', '线下私约'
|
||
]);
|
||
|
||
const homePopupVal = JSON.stringify({
|
||
enabled: true,
|
||
title: '新人专享同城陪伴好礼',
|
||
subtitle: '注册即送20元无门槛立减券,首单专享特惠',
|
||
imageUrl: 'https://images.unsplash.com/photo-1511671782779-c97d3d27a1d4?w=800',
|
||
link: '/pages/activity/list',
|
||
btnText: '立即领券组局',
|
||
frequency: 'once_daily' // once_daily | always | once_ever
|
||
});
|
||
|
||
const expertRulesVal = JSON.stringify({
|
||
minHourlyRate: 30,
|
||
maxHourlyRate: 500,
|
||
defaultHourlyRate: 88,
|
||
requireRealName: true,
|
||
allowCustomTags: true,
|
||
maxTagsCount: 6,
|
||
candidateTags: [
|
||
'国服王者', '声优达人', '幽默风趣', '随叫随到', '暖心倾听',
|
||
'百星野王', '金牌探店', '量贩麦霸', '微醺搭子', '绅士礼貌',
|
||
'活力开朗', '耐心教学', '高胜率车头', '拍照好看'
|
||
]
|
||
});
|
||
|
||
const financeRulesVal = JSON.stringify({
|
||
platformRatePercent: 10.0,
|
||
firstReferralPercent: 5.0,
|
||
secondReferralPercent: 2.0,
|
||
minWithdrawAmount: 50,
|
||
maxWithdrawAmount: 20000,
|
||
withdrawFeePercent: 0.0,
|
||
withdrawDesc: '提现将自动发起微信企业付款打款至微信零钱,工作日预计2小时内到账。'
|
||
});
|
||
|
||
const activityRulesVal = JSON.stringify({
|
||
minParticipants: 2,
|
||
maxParticipants: 10,
|
||
defaultParticipants: 4,
|
||
presetPerPersonFees: [200, 300, 500, 800, 1000],
|
||
presetTotalFees: [500, 1000, 2000, 3000, 5000],
|
||
defaultAaFee: 50
|
||
});
|
||
|
||
const [cfgItemRows]: any = await conn.query('SELECT COUNT(*) as count FROM platform_configs');
|
||
if (cfgItemRows[0].count === 0) {
|
||
const customerServiceVal = JSON.stringify({
|
||
hotline: '400-888-6666',
|
||
hotlineDesc: '7x24h 官方客服服务热线',
|
||
wechat: 'peiwan_vip_kefu',
|
||
wechatDesc: '官方 VIP 客服微信号',
|
||
workHours: '周一至周日 00:00 - 24:00'
|
||
});
|
||
|
||
const safetyNoticeVal = JSON.stringify({
|
||
title: '平台官方公告与指引',
|
||
points: [
|
||
'【安全履约须知】为保障搭子与陪伴师人身与财产安全,线下约局须在合规公开商业街区进行。',
|
||
'【现场核销】现场碰面后请出示 4 位数字核销码进行签到确认。',
|
||
'【平台兜底】平台提供 7x24 小时争议仲裁与资金托管保障服务,严禁脱离平台私下交易。'
|
||
],
|
||
noticePopup: false
|
||
});
|
||
|
||
const protocolVal = JSON.stringify({
|
||
title: '用户服务协议与隐私规范',
|
||
summary: '严禁发布任何违规言论及低俗诈骗行为,平台全面遵循微信内容安全风控与用户隐私保护标准。',
|
||
userAgreementTitle: '用户服务协议',
|
||
userAgreementContent: '1. 账号使用规范:用户注册与使用同城生活陪伴服务须符合国家法律法规,严禁利用平台从事任何违法违规行为。\n2. 资金与交易保障:平台提供全额担保交易,订单完成前款项由官方账户托管,碰面出示4位核销码确认后自动结算至大神钱包。\n3. 履约安全守则:所有同城活动须在合规公开商业街区进行,严禁私下导流与脱离平台交易。\n4. 争议与售后仲裁:若遇服务不符或搭子违约,可随时在订单中发起申诉,由平台运营仲裁专员裁决。',
|
||
privacyPolicyTitle: '隐私保护指引',
|
||
privacyPolicyContent: '1. 信息收集范围:为保障同城陪玩交易安全与现场4位核销码核验,平台在获得您授权后会依法收集手机号与必要位置信息。\n2. 数据加密保护:平台全面接入微信小程序官方安全风控规范,所有用户数据均实施高强度加密脱敏存储。\n3. 权限撤回与管理:您可随时在小程序系统设置中撤回位置或通讯授权,平台未经您的明确许可绝不会向任何第三方泄露您的隐私数据。',
|
||
content: '1. 账号使用规范:用户注册与使用陪玩服务须符合国家法律法规,严禁利用平台从事违法违规行为。\n2. 资金与交易保障:平台提供全额担保交易,订单完成前款项由官方账户托管,核销确认后自动结算至大神钱包。\n3. 争议与售后:若遇服务不符或搭子违约,可随时提交争议仲裁由运营平台人工介入裁决。'
|
||
});
|
||
|
||
await conn.query(`
|
||
INSERT INTO platform_configs (config_key, name, value_json, description, status)
|
||
VALUES
|
||
('customer_service', '官方在线客服与服务热线', ?, '小程序端个人中心客服联系方式', 1),
|
||
('safety_notice', '安全履约须知与官方公告', ?, '小程序端官方指引与安全红线弹窗', 1),
|
||
('platform_protocol', '平台服务协议与合规规范', ?, '小程序端服务协议与隐私政策详情', 1),
|
||
('community_topics', '友圈热门推荐话题库', ?, '友圈广场发帖推荐话题与热门标签', 1),
|
||
('community_notice', '友圈社区置顶头条公告', ?, '友圈广场顶部置顶横幅公告与社区公约', 1),
|
||
('faq_list', '帮助中心与常见问题FAQ', ?, '小程序端帮助中心常见问答与指引', 1),
|
||
('security_words', '私下引流与违规自定义敏感词库', ?, '全平台发帖、评论及订单防私下交易关键词实时过滤', 1),
|
||
('home_popup', '小程序首页运营全局弹窗', ?, '节日活动推广、新用户专享首单优惠全局浮层弹窗', 1),
|
||
('expert_rules', '大神技能标签库与定价区间规范', ?, '陪玩大神入驻审核、定价上下限限制与官方推荐技能标签库', 1),
|
||
('finance_rules', '平台抽成与推广分销规则', ?, '平台佣金抽成比例、二级分销返润与佣金提现门槛限制', 1),
|
||
('activity_rules', '同城组局发单与预算预设规范', ?, '招募席位上下限、快捷单人赏金预设与包场预算梯队', 1);
|
||
`, [
|
||
customerServiceVal, safetyNoticeVal, protocolVal,
|
||
communityTopicsVal, communityNoticeVal, faqListVal,
|
||
securityWordsVal, homePopupVal, expertRulesVal,
|
||
financeRulesVal, activityRulesVal
|
||
]);
|
||
} else {
|
||
// 增量保证各核心配置项完整就绪
|
||
const [existingRows]: any = await conn.query('SELECT config_key FROM platform_configs');
|
||
const existingKeys = new Set(existingRows.map((r: any) => r.config_key));
|
||
if (!existingKeys.has('community_topics')) {
|
||
await conn.query(`
|
||
INSERT INTO platform_configs (config_key, name, value_json, description, status)
|
||
VALUES ('community_topics', '友圈热门推荐话题库', ?, '友圈广场发帖推荐话题与热门标签', 1)
|
||
`, [communityTopicsVal]);
|
||
}
|
||
if (!existingKeys.has('community_notice')) {
|
||
await conn.query(`
|
||
INSERT INTO platform_configs (config_key, name, value_json, description, status)
|
||
VALUES ('community_notice', '友圈社区置顶头条公告', ?, '友圈广场顶部置顶横幅公告与社区公约', 1)
|
||
`, [communityNoticeVal]);
|
||
}
|
||
if (!existingKeys.has('faq_list')) {
|
||
await conn.query(`
|
||
INSERT INTO platform_configs (config_key, name, value_json, description, status)
|
||
VALUES ('faq_list', '帮助中心与常见问题FAQ', ?, '小程序端帮助中心常见问答与指引', 1)
|
||
`, [faqListVal]);
|
||
}
|
||
if (!existingKeys.has('security_words')) {
|
||
await conn.query(`
|
||
INSERT INTO platform_configs (config_key, name, value_json, description, status)
|
||
VALUES ('security_words', '私下引流与违规自定义敏感词库', ?, '全平台发帖、评论及订单防私下交易关键词实时过滤', 1)
|
||
`, [securityWordsVal]);
|
||
}
|
||
if (!existingKeys.has('home_popup')) {
|
||
await conn.query(`
|
||
INSERT INTO platform_configs (config_key, name, value_json, description, status)
|
||
VALUES ('home_popup', '小程序首页运营全局弹窗', ?, '节日活动推广、新用户专享首单优惠全局浮层弹窗', 1)
|
||
`, [homePopupVal]);
|
||
}
|
||
if (!existingKeys.has('expert_rules')) {
|
||
await conn.query(`
|
||
INSERT INTO platform_configs (config_key, name, value_json, description, status)
|
||
VALUES ('expert_rules', '大神技能标签库与定价区间规范', ?, '陪玩大神入驻审核、定价上下限限制与官方推荐技能标签库', 1)
|
||
`, [expertRulesVal]);
|
||
}
|
||
if (!existingKeys.has('finance_rules')) {
|
||
await conn.query(`
|
||
INSERT INTO platform_configs (config_key, name, value_json, description, status)
|
||
VALUES ('finance_rules', '平台抽成与推广分销规则', ?, '平台佣金抽成比例、二级分销返润与佣金提现门槛限制', 1)
|
||
`, [financeRulesVal]);
|
||
}
|
||
if (!existingKeys.has('activity_rules')) {
|
||
await conn.query(`
|
||
INSERT INTO platform_configs (config_key, name, value_json, description, status)
|
||
VALUES ('activity_rules', '同城组局发单与预算预设规范', ?, '招募席位上下限、快捷单人赏金预设与包场预算梯队', 1)
|
||
`, [activityRulesVal]);
|
||
}
|
||
if (existingKeys.has('platform_protocol')) {
|
||
const [protoRows]: any = await conn.query("SELECT value_json FROM platform_configs WHERE config_key = 'platform_protocol'");
|
||
if (protoRows && protoRows.length > 0) {
|
||
let curVal = protoRows[0].value_json;
|
||
if (typeof curVal === 'string') {
|
||
try { curVal = JSON.parse(curVal); } catch (_) {}
|
||
}
|
||
if (curVal && (!curVal.userAgreementContent || !curVal.privacyPolicyContent)) {
|
||
const defaultProto = {
|
||
userAgreementTitle: '用户服务协议',
|
||
userAgreementContent: '1. 账号使用规范:用户注册与使用同城生活陪伴服务须符合国家法律法规,严禁利用平台从事任何违法违规行为。\n2. 资金与交易保障:平台提供全额担保交易,订单完成前款项由官方账户托管,碰面出示4位核销码确认后自动结算至大神钱包。\n3. 履约安全守则:所有同城活动须在合规公开商业街区进行,严禁私下导流与脱离平台交易。\n4. 争议与售后仲裁:若遇服务不符或搭子违约,可随时在订单中发起申诉,由平台运营仲裁专员裁决。',
|
||
privacyPolicyTitle: '隐私保护指引',
|
||
privacyPolicyContent: '1. 信息收集范围:为保障同城陪玩交易安全与现场4位核销码核验,平台在获得您授权后会依法收集手机号与必要位置信息。\n2. 数据加密保护:平台全面接入微信小程序官方安全风控规范,所有用户数据均实施高强度加密脱敏存储。\n3. 权限撤回与管理:您可随时在小程序系统设置中撤回位置或通讯授权,平台未经您的明确许可绝不会向任何第三方泄露您的隐私数据。'
|
||
};
|
||
const mergedProto = {
|
||
...defaultProto,
|
||
...curVal
|
||
};
|
||
if (!mergedProto.userAgreementContent) mergedProto.userAgreementContent = defaultProto.userAgreementContent;
|
||
if (!mergedProto.privacyPolicyContent) mergedProto.privacyPolicyContent = defaultProto.privacyPolicyContent;
|
||
await conn.query("UPDATE platform_configs SET value_json = ? WHERE config_key = 'platform_protocol'", [JSON.stringify(mergedProto)]);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|