Files
soul/lib/db.ts
卡若 263da246c9 feat: 章节数据库化 + 支付配置更新
1. 章节内容迁移到MySQL数据库(67篇文章)
2. 章节API改为从数据库读取,不再依赖book文件夹
3. 新增章节管理API(增删改查)
4. 更新小程序支付AppSecret
5. 整理完整API配置清单
2026-01-25 09:57:21 +08:00

289 lines
9.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 数据库连接配置
* 使用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 NOT NULL,
nickname VARCHAR(100),
avatar VARCHAR(500),
phone VARCHAR(20),
wechat_id VARCHAR(100),
referral_code VARCHAR(20) UNIQUE,
purchased_sections JSON,
has_full_book BOOLEAN DEFAULT FALSE,
earnings DECIMAL(10,2) DEFAULT 0,
pending_earnings DECIMAL(10,2) DEFAULT 0,
referral_count INT DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_open_id (open_id),
INDEX idx_referral_code (referral_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
`)
// 订单表
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 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 }