512 lines
15 KiB
TypeScript
512 lines
15 KiB
TypeScript
|
|
// 数据库连接配置
|
|||
|
|
// 使用腾讯云数据库
|
|||
|
|
|
|||
|
|
import mysql from 'mysql2/promise'
|
|||
|
|
|
|||
|
|
// 数据库配置(不含database,用于创建数据库)
|
|||
|
|
const dbConfigWithoutDB = {
|
|||
|
|
host: '56b4c23f6853c.gz.cdb.myqcloud.com',
|
|||
|
|
port: 14413,
|
|||
|
|
user: 'cdb_outerroot',
|
|||
|
|
password: 'Zhiqun1984',
|
|||
|
|
waitForConnections: true,
|
|||
|
|
connectionLimit: 10,
|
|||
|
|
queueLimit: 0,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 数据库配置(含database)
|
|||
|
|
const dbConfig = {
|
|||
|
|
...dbConfigWithoutDB,
|
|||
|
|
database: 'soul_experiment',
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 创建连接池
|
|||
|
|
let pool: mysql.Pool | null = null
|
|||
|
|
|
|||
|
|
export function getPool() {
|
|||
|
|
if (!pool) {
|
|||
|
|
pool = mysql.createPool(dbConfig)
|
|||
|
|
}
|
|||
|
|
return pool
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 创建数据库(如果不存在)
|
|||
|
|
export async function createDatabaseIfNotExists() {
|
|||
|
|
const conn = await mysql.createConnection(dbConfigWithoutDB)
|
|||
|
|
try {
|
|||
|
|
await conn.execute('CREATE DATABASE IF NOT EXISTS soul_experiment CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci')
|
|||
|
|
console.log('Database soul_experiment created or already exists')
|
|||
|
|
return true
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('Error creating database:', error)
|
|||
|
|
throw error
|
|||
|
|
} finally {
|
|||
|
|
await conn.end()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 执行查询
|
|||
|
|
export async function query<T = any>(sql: string, params?: any[]): Promise<T[]> {
|
|||
|
|
const pool = getPool()
|
|||
|
|
const [rows] = await pool.execute(sql, params)
|
|||
|
|
return rows as T[]
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 执行单条插入/更新/删除
|
|||
|
|
export async function execute(sql: string, params?: any[]): Promise<mysql.ResultSetHeader> {
|
|||
|
|
const pool = getPool()
|
|||
|
|
const [result] = await pool.execute(sql, params)
|
|||
|
|
return result as mysql.ResultSetHeader
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 用户相关操作
|
|||
|
|
export const userDB = {
|
|||
|
|
// 获取所有用户
|
|||
|
|
async getAll() {
|
|||
|
|
return query(`SELECT * FROM users ORDER BY created_at DESC`)
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 根据ID获取用户
|
|||
|
|
async getById(id: string) {
|
|||
|
|
const rows = await query(`SELECT * FROM users WHERE id = ?`, [id])
|
|||
|
|
return rows[0] || null
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 根据手机号获取用户
|
|||
|
|
async getByPhone(phone: string) {
|
|||
|
|
const rows = await query(`SELECT * FROM users WHERE phone = ?`, [phone])
|
|||
|
|
return rows[0] || null
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 创建用户
|
|||
|
|
async create(user: {
|
|||
|
|
id: string
|
|||
|
|
phone: string
|
|||
|
|
nickname: string
|
|||
|
|
password?: string
|
|||
|
|
is_admin?: boolean
|
|||
|
|
referral_code: string
|
|||
|
|
referred_by?: string
|
|||
|
|
}) {
|
|||
|
|
await execute(
|
|||
|
|
`INSERT INTO users (id, phone, nickname, password, is_admin, referral_code, referred_by, created_at)
|
|||
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, NOW())`,
|
|||
|
|
[user.id, user.phone, user.nickname, user.password || '', user.is_admin || false, user.referral_code, user.referred_by || null]
|
|||
|
|
)
|
|||
|
|
return user
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 更新用户
|
|||
|
|
async update(id: string, updates: Partial<{
|
|||
|
|
nickname: string
|
|||
|
|
password: string
|
|||
|
|
is_admin: boolean
|
|||
|
|
has_full_book: boolean
|
|||
|
|
earnings: number
|
|||
|
|
pending_earnings: number
|
|||
|
|
withdrawn_earnings: number
|
|||
|
|
referral_count: number
|
|||
|
|
match_count_today: number
|
|||
|
|
last_match_date: string
|
|||
|
|
}>) {
|
|||
|
|
const fields: string[] = []
|
|||
|
|
const values: any[] = []
|
|||
|
|
|
|||
|
|
Object.entries(updates).forEach(([key, value]) => {
|
|||
|
|
if (value !== undefined) {
|
|||
|
|
fields.push(`${key} = ?`)
|
|||
|
|
values.push(value)
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
if (fields.length === 0) return
|
|||
|
|
|
|||
|
|
values.push(id)
|
|||
|
|
await execute(`UPDATE users SET ${fields.join(', ')} WHERE id = ?`, values)
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 删除用户
|
|||
|
|
async delete(id: string) {
|
|||
|
|
await execute(`DELETE FROM users WHERE id = ?`, [id])
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 验证密码
|
|||
|
|
async verifyPassword(phone: string, password: string) {
|
|||
|
|
const rows = await query(`SELECT * FROM users WHERE phone = ? AND password = ?`, [phone, password])
|
|||
|
|
return rows[0] || null
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 更新匹配次数
|
|||
|
|
async updateMatchCount(userId: string) {
|
|||
|
|
const today = new Date().toISOString().split('T')[0]
|
|||
|
|
const user = await this.getById(userId)
|
|||
|
|
|
|||
|
|
if (user?.last_match_date === today) {
|
|||
|
|
await execute(
|
|||
|
|
`UPDATE users SET match_count_today = match_count_today + 1 WHERE id = ?`,
|
|||
|
|
[userId]
|
|||
|
|
)
|
|||
|
|
} else {
|
|||
|
|
await execute(
|
|||
|
|
`UPDATE users SET match_count_today = 1, last_match_date = ? WHERE id = ?`,
|
|||
|
|
[today, userId]
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 获取今日匹配次数
|
|||
|
|
async getMatchCount(userId: string) {
|
|||
|
|
const today = new Date().toISOString().split('T')[0]
|
|||
|
|
const rows = await query(
|
|||
|
|
`SELECT match_count_today FROM users WHERE id = ? AND last_match_date = ?`,
|
|||
|
|
[userId, today]
|
|||
|
|
)
|
|||
|
|
return rows[0]?.match_count_today || 0
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 购买记录相关操作
|
|||
|
|
export const purchaseDB = {
|
|||
|
|
async getAll() {
|
|||
|
|
return query(`SELECT * FROM purchases ORDER BY created_at DESC`)
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
async getByUserId(userId: string) {
|
|||
|
|
return query(`SELECT * FROM purchases WHERE user_id = ? ORDER BY created_at DESC`, [userId])
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
async create(purchase: {
|
|||
|
|
id: string
|
|||
|
|
user_id: string
|
|||
|
|
type: 'section' | 'fullbook' | 'match'
|
|||
|
|
section_id?: string
|
|||
|
|
section_title?: string
|
|||
|
|
amount: number
|
|||
|
|
payment_method?: string
|
|||
|
|
referral_code?: string
|
|||
|
|
referrer_earnings?: number
|
|||
|
|
status: string
|
|||
|
|
}) {
|
|||
|
|
await execute(
|
|||
|
|
`INSERT INTO purchases (id, user_id, type, section_id, section_title, amount, payment_method, referral_code, referrer_earnings, status, created_at)
|
|||
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())`,
|
|||
|
|
[purchase.id, purchase.user_id, purchase.type, purchase.section_id || null, purchase.section_title || null,
|
|||
|
|
purchase.amount, purchase.payment_method || null, purchase.referral_code || null, purchase.referrer_earnings || 0, purchase.status]
|
|||
|
|
)
|
|||
|
|
return purchase
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 分销绑定相关操作
|
|||
|
|
export const distributionDB = {
|
|||
|
|
async getAllBindings() {
|
|||
|
|
return query(`SELECT * FROM referral_bindings ORDER BY bound_at DESC`)
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
async getBindingsByReferrer(referrerId: string) {
|
|||
|
|
return query(`SELECT * FROM referral_bindings WHERE referrer_id = ? ORDER BY bound_at DESC`, [referrerId])
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
async createBinding(binding: {
|
|||
|
|
id: string
|
|||
|
|
referrer_id: string
|
|||
|
|
referee_id: string
|
|||
|
|
referrer_code: string
|
|||
|
|
bound_at: string
|
|||
|
|
expires_at: string
|
|||
|
|
status: string
|
|||
|
|
}) {
|
|||
|
|
await execute(
|
|||
|
|
`INSERT INTO referral_bindings (id, referrer_id, referee_id, referrer_code, bound_at, expires_at, status)
|
|||
|
|
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|||
|
|
[binding.id, binding.referrer_id, binding.referee_id, binding.referrer_code, binding.bound_at, binding.expires_at, binding.status]
|
|||
|
|
)
|
|||
|
|
return binding
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
async updateBindingStatus(id: string, status: string) {
|
|||
|
|
await execute(`UPDATE referral_bindings SET status = ? WHERE id = ?`, [status, id])
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
async getActiveBindingByReferee(refereeId: string) {
|
|||
|
|
const rows = await query(
|
|||
|
|
`SELECT * FROM referral_bindings WHERE referee_id = ? AND status = 'active' AND expires_at > NOW()`,
|
|||
|
|
[refereeId]
|
|||
|
|
)
|
|||
|
|
return rows[0] || null
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 佣金记录
|
|||
|
|
async getAllCommissions() {
|
|||
|
|
return query(`SELECT * FROM distribution_commissions ORDER BY created_at DESC`)
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
async createCommission(commission: {
|
|||
|
|
id: string
|
|||
|
|
binding_id: string
|
|||
|
|
referrer_id: string
|
|||
|
|
referee_id: string
|
|||
|
|
order_id: string
|
|||
|
|
amount: number
|
|||
|
|
commission_rate: number
|
|||
|
|
commission_amount: number
|
|||
|
|
status: string
|
|||
|
|
}) {
|
|||
|
|
await execute(
|
|||
|
|
`INSERT INTO distribution_commissions (id, binding_id, referrer_id, referee_id, order_id, amount, commission_rate, commission_amount, status, created_at)
|
|||
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())`,
|
|||
|
|
[commission.id, commission.binding_id, commission.referrer_id, commission.referee_id, commission.order_id,
|
|||
|
|
commission.amount, commission.commission_rate, commission.commission_amount, commission.status]
|
|||
|
|
)
|
|||
|
|
return commission
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 提现记录相关操作
|
|||
|
|
export const withdrawalDB = {
|
|||
|
|
async getAll() {
|
|||
|
|
return query(`SELECT * FROM withdrawals ORDER BY created_at DESC`)
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
async getByUserId(userId: string) {
|
|||
|
|
return query(`SELECT * FROM withdrawals WHERE user_id = ? ORDER BY created_at DESC`, [userId])
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
async create(withdrawal: {
|
|||
|
|
id: string
|
|||
|
|
user_id: string
|
|||
|
|
amount: number
|
|||
|
|
method: string
|
|||
|
|
account: string
|
|||
|
|
name: string
|
|||
|
|
status: string
|
|||
|
|
}) {
|
|||
|
|
await execute(
|
|||
|
|
`INSERT INTO withdrawals (id, user_id, amount, method, account, name, status, created_at)
|
|||
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, NOW())`,
|
|||
|
|
[withdrawal.id, withdrawal.user_id, withdrawal.amount, withdrawal.method, withdrawal.account, withdrawal.name, withdrawal.status]
|
|||
|
|
)
|
|||
|
|
return withdrawal
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
async updateStatus(id: string, status: string) {
|
|||
|
|
const completedAt = status === 'completed' ? ', completed_at = NOW()' : ''
|
|||
|
|
await execute(`UPDATE withdrawals SET status = ?${completedAt} WHERE id = ?`, [status, id])
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 系统设置相关操作
|
|||
|
|
export const settingsDB = {
|
|||
|
|
async get() {
|
|||
|
|
const rows = await query(`SELECT * FROM settings WHERE id = 1`)
|
|||
|
|
return rows[0] || null
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
async update(settings: Record<string, any>) {
|
|||
|
|
const json = JSON.stringify(settings)
|
|||
|
|
await execute(
|
|||
|
|
`INSERT INTO settings (id, data, updated_at) VALUES (1, ?, NOW())
|
|||
|
|
ON DUPLICATE KEY UPDATE data = ?, updated_at = NOW()`,
|
|||
|
|
[json, json]
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// book内容相关操作
|
|||
|
|
export const bookDB = {
|
|||
|
|
async getAllSections() {
|
|||
|
|
return query(`SELECT * FROM book_sections ORDER BY sort_order ASC`)
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
async getSection(id: string) {
|
|||
|
|
const rows = await query(`SELECT * FROM book_sections WHERE id = ?`, [id])
|
|||
|
|
return rows[0] || null
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
async updateSection(id: string, updates: { title?: string; content?: string; price?: number; is_free?: boolean }) {
|
|||
|
|
const fields: string[] = []
|
|||
|
|
const values: any[] = []
|
|||
|
|
|
|||
|
|
Object.entries(updates).forEach(([key, value]) => {
|
|||
|
|
if (value !== undefined) {
|
|||
|
|
fields.push(`${key} = ?`)
|
|||
|
|
values.push(value)
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
if (fields.length === 0) return
|
|||
|
|
|
|||
|
|
fields.push('updated_at = NOW()')
|
|||
|
|
values.push(id)
|
|||
|
|
await execute(`UPDATE book_sections SET ${fields.join(', ')} WHERE id = ?`, values)
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
async createSection(section: {
|
|||
|
|
id: string
|
|||
|
|
part_id: string
|
|||
|
|
chapter_id: string
|
|||
|
|
title: string
|
|||
|
|
content: string
|
|||
|
|
price: number
|
|||
|
|
is_free: boolean
|
|||
|
|
sort_order: number
|
|||
|
|
}) {
|
|||
|
|
await execute(
|
|||
|
|
`INSERT INTO book_sections (id, part_id, chapter_id, title, content, price, is_free, sort_order, created_at, updated_at)
|
|||
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())`,
|
|||
|
|
[section.id, section.part_id, section.chapter_id, section.title, section.content, section.price, section.is_free, section.sort_order]
|
|||
|
|
)
|
|||
|
|
return section
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 导出所有章节
|
|||
|
|
async exportAll() {
|
|||
|
|
const sections = await this.getAllSections()
|
|||
|
|
return JSON.stringify(sections, null, 2)
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 导入章节
|
|||
|
|
async importSections(sectionsJson: string) {
|
|||
|
|
const sections = JSON.parse(sectionsJson)
|
|||
|
|
for (const section of sections) {
|
|||
|
|
const existing = await this.getSection(section.id)
|
|||
|
|
if (existing) {
|
|||
|
|
await this.updateSection(section.id, section)
|
|||
|
|
} else {
|
|||
|
|
await this.createSection(section)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return sections.length
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 初始化数据库表
|
|||
|
|
export async function initDatabase() {
|
|||
|
|
// 先创建数据库
|
|||
|
|
await createDatabaseIfNotExists()
|
|||
|
|
|
|||
|
|
const pool = getPool()
|
|||
|
|
|
|||
|
|
// 用户表
|
|||
|
|
await pool.execute(`
|
|||
|
|
CREATE TABLE IF NOT EXISTS users (
|
|||
|
|
id VARCHAR(50) PRIMARY KEY,
|
|||
|
|
phone VARCHAR(20) UNIQUE NOT NULL,
|
|||
|
|
nickname VARCHAR(100) NOT NULL,
|
|||
|
|
password VARCHAR(100) DEFAULT '',
|
|||
|
|
is_admin BOOLEAN DEFAULT FALSE,
|
|||
|
|
has_full_book BOOLEAN DEFAULT FALSE,
|
|||
|
|
referral_code VARCHAR(20) UNIQUE,
|
|||
|
|
referred_by VARCHAR(20),
|
|||
|
|
earnings DECIMAL(10,2) DEFAULT 0,
|
|||
|
|
pending_earnings DECIMAL(10,2) DEFAULT 0,
|
|||
|
|
withdrawn_earnings DECIMAL(10,2) DEFAULT 0,
|
|||
|
|
referral_count INT DEFAULT 0,
|
|||
|
|
match_count_today INT DEFAULT 0,
|
|||
|
|
last_match_date DATE,
|
|||
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|||
|
|
INDEX idx_phone (phone),
|
|||
|
|
INDEX idx_referral_code (referral_code)
|
|||
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
|||
|
|
`)
|
|||
|
|
|
|||
|
|
// 购买记录表
|
|||
|
|
await pool.execute(`
|
|||
|
|
CREATE TABLE IF NOT EXISTS purchases (
|
|||
|
|
id VARCHAR(50) PRIMARY KEY,
|
|||
|
|
user_id VARCHAR(50) NOT NULL,
|
|||
|
|
type ENUM('section', 'fullbook', 'match') NOT NULL,
|
|||
|
|
section_id VARCHAR(20),
|
|||
|
|
section_title VARCHAR(200),
|
|||
|
|
amount DECIMAL(10,2) NOT NULL,
|
|||
|
|
payment_method VARCHAR(20),
|
|||
|
|
referral_code VARCHAR(20),
|
|||
|
|
referrer_earnings DECIMAL(10,2) DEFAULT 0,
|
|||
|
|
status ENUM('pending', 'completed', 'refunded') DEFAULT 'pending',
|
|||
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|||
|
|
INDEX idx_user_id (user_id),
|
|||
|
|
INDEX idx_status (status)
|
|||
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
|||
|
|
`)
|
|||
|
|
|
|||
|
|
// 分销绑定表
|
|||
|
|
await pool.execute(`
|
|||
|
|
CREATE TABLE IF NOT EXISTS referral_bindings (
|
|||
|
|
id VARCHAR(50) PRIMARY KEY,
|
|||
|
|
referrer_id VARCHAR(50) NOT NULL,
|
|||
|
|
referee_id VARCHAR(50) NOT NULL,
|
|||
|
|
referrer_code VARCHAR(20) NOT NULL,
|
|||
|
|
bound_at DATETIME NOT NULL,
|
|||
|
|
expires_at DATETIME NOT NULL,
|
|||
|
|
status ENUM('active', 'converted', 'expired') DEFAULT 'active',
|
|||
|
|
INDEX idx_referrer (referrer_id),
|
|||
|
|
INDEX idx_referee (referee_id),
|
|||
|
|
INDEX idx_status (status)
|
|||
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
|||
|
|
`)
|
|||
|
|
|
|||
|
|
// 分销佣金表
|
|||
|
|
await pool.execute(`
|
|||
|
|
CREATE TABLE IF NOT EXISTS distribution_commissions (
|
|||
|
|
id VARCHAR(50) PRIMARY KEY,
|
|||
|
|
binding_id VARCHAR(50) NOT NULL,
|
|||
|
|
referrer_id VARCHAR(50) NOT NULL,
|
|||
|
|
referee_id VARCHAR(50) NOT NULL,
|
|||
|
|
order_id VARCHAR(50) NOT NULL,
|
|||
|
|
amount DECIMAL(10,2) NOT NULL,
|
|||
|
|
commission_rate DECIMAL(5,2) NOT NULL,
|
|||
|
|
commission_amount DECIMAL(10,2) NOT NULL,
|
|||
|
|
status ENUM('pending', 'paid', 'cancelled') DEFAULT 'pending',
|
|||
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|||
|
|
paid_at DATETIME,
|
|||
|
|
INDEX idx_referrer (referrer_id)
|
|||
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
|||
|
|
`)
|
|||
|
|
|
|||
|
|
// 提现记录表
|
|||
|
|
await pool.execute(`
|
|||
|
|
CREATE TABLE IF NOT EXISTS withdrawals (
|
|||
|
|
id VARCHAR(50) PRIMARY KEY,
|
|||
|
|
user_id VARCHAR(50) NOT NULL,
|
|||
|
|
amount DECIMAL(10,2) NOT NULL,
|
|||
|
|
method ENUM('wechat', 'alipay') NOT NULL,
|
|||
|
|
account VARCHAR(100) NOT NULL,
|
|||
|
|
name VARCHAR(50) NOT NULL,
|
|||
|
|
status ENUM('pending', 'completed', 'rejected') DEFAULT 'pending',
|
|||
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|||
|
|
completed_at DATETIME,
|
|||
|
|
INDEX idx_user_id (user_id),
|
|||
|
|
INDEX idx_status (status)
|
|||
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
|||
|
|
`)
|
|||
|
|
|
|||
|
|
// 系统设置表
|
|||
|
|
await pool.execute(`
|
|||
|
|
CREATE TABLE IF NOT EXISTS settings (
|
|||
|
|
id INT PRIMARY KEY,
|
|||
|
|
data JSON,
|
|||
|
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|||
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
|||
|
|
`)
|
|||
|
|
|
|||
|
|
// book章节表
|
|||
|
|
await pool.execute(`
|
|||
|
|
CREATE TABLE IF NOT EXISTS book_sections (
|
|||
|
|
id VARCHAR(20) PRIMARY KEY,
|
|||
|
|
part_id VARCHAR(20) NOT NULL,
|
|||
|
|
chapter_id VARCHAR(20) NOT NULL,
|
|||
|
|
title VARCHAR(200) NOT NULL,
|
|||
|
|
content LONGTEXT,
|
|||
|
|
price DECIMAL(10,2) DEFAULT 1,
|
|||
|
|
is_free BOOLEAN DEFAULT FALSE,
|
|||
|
|
sort_order INT DEFAULT 0,
|
|||
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|||
|
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|||
|
|
INDEX idx_part (part_id),
|
|||
|
|
INDEX idx_chapter (chapter_id)
|
|||
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
|||
|
|
`)
|
|||
|
|
|
|||
|
|
console.log('Database tables initialized successfully')
|
|||
|
|
}
|