/** * 数据库连接配置 * 使用MySQL数据库存储用户、订单、推广关系等数据 */ import mysql from 'mysql2/promise' // 腾讯云外网数据库配置 const DB_CONFIG = { host: '56b4c23f6853c.gz.cdb.myqcloud.com', port: 14413, user: 'cdb_outerroot', password: 'Zhiqun1984', database: 'soul_miniprogram', charset: 'utf8mb4', timezone: '+08:00', acquireTimeout: 60000, timeout: 60000, reconnect: true } // 连接池 let pool: mysql.Pool | null = null /** * 获取数据库连接池 */ export function getPool() { if (!pool) { pool = mysql.createPool({ ...DB_CONFIG, waitForConnections: true, connectionLimit: 10, queueLimit: 0 }) } return pool } /** * 执行SQL查询 */ export async function query(sql: string, params?: any[]) { try { const connection = getPool() const [results] = await connection.execute(sql, params) return results } catch (error) { console.error('数据库查询错误:', error) throw error } } /** * 初始化数据库表结构 */ export async function initDatabase() { try { console.log('开始初始化数据库表结构...') // 用户表(完整字段) await query(` CREATE TABLE IF NOT EXISTS users ( id VARCHAR(50) PRIMARY KEY, open_id VARCHAR(100) UNIQUE, session_key VARCHAR(100) COMMENT '微信session_key', nickname VARCHAR(100), avatar VARCHAR(500), phone VARCHAR(20), password VARCHAR(100) COMMENT '密码(可选)', wechat_id VARCHAR(100) COMMENT '用户填写的微信号', referral_code VARCHAR(20) UNIQUE, referred_by VARCHAR(50) COMMENT '推荐人ID', purchased_sections JSON DEFAULT '[]', has_full_book BOOLEAN DEFAULT FALSE, is_admin BOOLEAN DEFAULT FALSE COMMENT '是否管理员', earnings DECIMAL(10,2) DEFAULT 0 COMMENT '已提现收益', pending_earnings DECIMAL(10,2) DEFAULT 0 COMMENT '待提现收益', withdrawn_earnings DECIMAL(10,2) DEFAULT 0 COMMENT '累计已提现', referral_count INT DEFAULT 0 COMMENT '推广人数', match_count_today INT DEFAULT 0 COMMENT '今日匹配次数', last_match_date DATE COMMENT '最后匹配日期', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_open_id (open_id), INDEX idx_phone (phone), INDEX idx_referral_code (referral_code), INDEX idx_referred_by (referred_by) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci `) // 尝试添加可能缺失的字段(用于升级已有数据库) try { await query('ALTER TABLE users ADD COLUMN IF NOT EXISTS session_key VARCHAR(100)') } catch (e) { /* 忽略 */ } try { await query('ALTER TABLE users ADD COLUMN IF NOT EXISTS password VARCHAR(100)') } catch (e) { /* 忽略 */ } try { await query('ALTER TABLE users ADD COLUMN IF NOT EXISTS referred_by VARCHAR(50)') } catch (e) { /* 忽略 */ } try { await query('ALTER TABLE users ADD COLUMN IF NOT EXISTS is_admin BOOLEAN DEFAULT FALSE') } catch (e) { /* 忽略 */ } try { await query('ALTER TABLE users ADD COLUMN IF NOT EXISTS match_count_today INT DEFAULT 0') } catch (e) { /* 忽略 */ } try { await query('ALTER TABLE users ADD COLUMN IF NOT EXISTS last_match_date DATE') } catch (e) { /* 忽略 */ } try { await query('ALTER TABLE users ADD COLUMN IF NOT EXISTS withdrawn_earnings DECIMAL(10,2) DEFAULT 0') } catch (e) { /* 忽略 */ } console.log('用户表初始化完成') // 订单表 await query(` CREATE TABLE IF NOT EXISTS orders ( id VARCHAR(50) PRIMARY KEY, order_sn VARCHAR(50) UNIQUE NOT NULL, user_id VARCHAR(50) NOT NULL, open_id VARCHAR(100) NOT NULL, product_type ENUM('section', 'fullbook', 'match') NOT NULL, product_id VARCHAR(50), amount DECIMAL(10,2) NOT NULL, description VARCHAR(200), status ENUM('pending', 'paid', 'cancelled', 'refunded') DEFAULT 'pending', transaction_id VARCHAR(100), pay_time TIMESTAMP NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id), INDEX idx_order_sn (order_sn), INDEX idx_user_id (user_id), INDEX idx_status (status) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci `) // 推广绑定关系表 await query(` CREATE TABLE IF NOT EXISTS referral_bindings ( id VARCHAR(50) PRIMARY KEY, referrer_id VARCHAR(50) NOT NULL, referee_id VARCHAR(50) NOT NULL, referral_code VARCHAR(20) NOT NULL, status ENUM('active', 'converted', 'expired') DEFAULT 'active', binding_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, expiry_date TIMESTAMP NOT NULL, conversion_date TIMESTAMP NULL, commission_amount DECIMAL(10,2) DEFAULT 0, order_id VARCHAR(50) NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, FOREIGN KEY (referrer_id) REFERENCES users(id), FOREIGN KEY (referee_id) REFERENCES users(id), FOREIGN KEY (order_id) REFERENCES orders(id), UNIQUE KEY unique_referrer_referee (referrer_id, referee_id), INDEX idx_referrer_id (referrer_id), INDEX idx_referee_id (referee_id), INDEX idx_status (status), INDEX idx_expiry_date (expiry_date) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci `) // 匹配记录表 await query(` CREATE TABLE IF NOT EXISTS match_records ( id VARCHAR(50) PRIMARY KEY, user_id VARCHAR(50) NOT NULL, match_type ENUM('partner', 'investor', 'mentor', 'team') NOT NULL, phone VARCHAR(20), wechat_id VARCHAR(100), matched_user_id VARCHAR(50), match_score INT, status ENUM('pending', 'matched', 'contacted') DEFAULT 'pending', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id), INDEX idx_user_id (user_id), INDEX idx_match_type (match_type), INDEX idx_status (status) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci `) // 推广访问记录表(用于统计「通过链接进的人数」) await query(` CREATE TABLE IF NOT EXISTS referral_visits ( id INT AUTO_INCREMENT PRIMARY KEY, referrer_id VARCHAR(50) NOT NULL COMMENT '推广者ID', visitor_id VARCHAR(50) COMMENT '访客ID(可能为空)', visitor_openid VARCHAR(100) COMMENT '访客openId', source VARCHAR(50) DEFAULT 'miniprogram' COMMENT '来源:miniprogram/web/share', page VARCHAR(200) COMMENT '落地页路径', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, INDEX idx_referrer_id (referrer_id), INDEX idx_visitor_id (visitor_id), INDEX idx_created_at (created_at) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci `) // 系统配置表 await query(` CREATE TABLE IF NOT EXISTS system_config ( id INT AUTO_INCREMENT PRIMARY KEY, config_key VARCHAR(100) UNIQUE NOT NULL, config_value JSON NOT NULL, description VARCHAR(200), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_config_key (config_key) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci `) // 章节内容表 - 存储书籍所有章节 await query(` CREATE TABLE IF NOT EXISTS chapters ( id VARCHAR(20) PRIMARY KEY COMMENT '章节ID,如1.1、preface等', part_id VARCHAR(20) NOT NULL COMMENT '所属篇ID,如part-1', part_title VARCHAR(100) NOT NULL COMMENT '篇标题,如第一篇|真实的人', chapter_id VARCHAR(20) NOT NULL COMMENT '所属章ID,如chapter-1', chapter_title VARCHAR(200) NOT NULL COMMENT '章标题,如第1章|人与人之间的底层逻辑', section_title VARCHAR(200) NOT NULL COMMENT '节标题', content LONGTEXT NOT NULL COMMENT '章节正文内容(Markdown格式)', word_count INT DEFAULT 0 COMMENT '字数统计', is_free BOOLEAN DEFAULT FALSE COMMENT '是否免费章节', price DECIMAL(10,2) DEFAULT 1.00 COMMENT '单章价格', sort_order INT DEFAULT 0 COMMENT '排序顺序', status ENUM('draft', 'published', 'archived') DEFAULT 'published' COMMENT '状态', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_part_id (part_id), INDEX idx_chapter_id (chapter_id), INDEX idx_status (status), INDEX idx_sort_order (sort_order) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci `) console.log('数据库表结构初始化完成') // 插入默认配置 await initDefaultConfig() } catch (error) { console.error('初始化数据库失败:', error) throw error } } /** * 初始化默认配置 */ async function initDefaultConfig() { try { // 匹配类型配置 const matchConfig = { matchTypes: [ { id: 'partner', label: '创业合伙', matchLabel: '创业伙伴', icon: '⭐', matchFromDB: true, showJoinAfterMatch: false, enabled: true }, { id: 'investor', label: '资源对接', matchLabel: '资源对接', icon: '👥', matchFromDB: false, showJoinAfterMatch: true, enabled: true }, { id: 'mentor', label: '导师顾问', matchLabel: '商业顾问', icon: '❤️', matchFromDB: false, showJoinAfterMatch: true, enabled: true }, { id: 'team', label: '团队招募', matchLabel: '加入项目', icon: '🎮', matchFromDB: false, showJoinAfterMatch: true, enabled: true } ], freeMatchLimit: 3, matchPrice: 1, settings: { enableFreeMatches: true, enablePaidMatches: true, maxMatchesPerDay: 10 } } await query(` INSERT INTO system_config (config_key, config_value, description) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE config_value = VALUES(config_value) `, ['match_config', JSON.stringify(matchConfig), '匹配功能配置']) // 推广配置 const referralConfig = { distributorShare: 90, // 推广者分成比例 minWithdrawAmount: 10, // 最小提现金额 bindingDays: 30, // 绑定有效期(天) userDiscount: 5 // 用户优惠比例 } await query(` INSERT INTO system_config (config_key, config_value, description) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE config_value = VALUES(config_value) `, ['referral_config', JSON.stringify(referralConfig), '推广功能配置']) console.log('默认配置初始化完成') } catch (error) { console.error('初始化默认配置失败:', error) } } /** * 获取系统配置 */ export async function getConfig(key: string) { try { const results = await query( 'SELECT config_value FROM system_config WHERE config_key = ?', [key] ) as any[] if (results.length > 0) { return results[0].config_value } return null } catch (error) { console.error('获取配置失败:', error) return null } } /** * 设置系统配置 */ export async function setConfig(key: string, value: any, description?: string) { try { await query(` INSERT INTO system_config (config_key, config_value, description) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE config_value = VALUES(config_value), description = COALESCE(VALUES(description), description) `, [key, JSON.stringify(value), description]) return true } catch (error) { console.error('设置配置失败:', error) return false } } // 导出数据库实例 export default { getPool, query, initDatabase, getConfig, setConfig }