Files

183 lines
5.1 KiB
TypeScript
Raw Permalink Normal View History

import path from 'path';
import fs from 'fs';
import { v4 as uuidv4 } from 'uuid';
import { createRequire } from 'module';
// 在ES模块中创建require函数用于兼容CommonJS模块
const require = createRequire(import.meta.url);
const DEFAULT_DB_DIR = path.join(process.cwd(), 'data');
const DEFAULT_DB_PATH = path.join(DEFAULT_DB_DIR, 'survey.db');
const DB_PATH = process.env.DB_PATH || DEFAULT_DB_PATH;
const DB_DIR = path.dirname(DB_PATH);
// 确保数据目录存在
if (!fs.existsSync(DB_DIR)) {
fs.mkdirSync(DB_DIR, { recursive: true });
}
// 延迟初始化数据库连接
let db: any = null;
let isInitialized = false;
// 初始化数据库连接的函数
export const initDbConnection = async () => {
if (!isInitialized) {
try {
// 使用require加载sqlite3因为它是CommonJS模块
const sqlite3 = require('sqlite3');
db = new sqlite3.Database(DB_PATH, (err: Error) => {
if (err) {
console.error('数据库连接失败:', err);
} else {
console.log('数据库连接成功');
// 启用外键约束
db.run('PRAGMA foreign_keys = ON');
}
});
isInitialized = true;
} catch (error) {
console.error('初始化数据库失败:', error);
// 初始化失败不设置isInitialized为true允许后续调用再次尝试
return null;
}
}
return db;
};
// 导出一个函数,用于获取数据库连接
export const getDb = async () => {
if (!db) {
return await initDbConnection();
}
return db;
};
const exec = async (sql: string): Promise<void> => {
return new Promise(async (resolve, reject) => {
const db = await getDb();
if (!db) {
reject(new Error('数据库连接未初始化'));
return;
}
db.exec(sql, (err: Error) => {
if (err) reject(err);
else resolve();
});
});
};
const tableExists = async (tableName: string): Promise<boolean> => {
const row = await get(
"SELECT name FROM sqlite_master WHERE type='table' AND name=?",
[tableName]
);
return Boolean(row);
};
const columnExists = async (tableName: string, columnName: string): Promise<boolean> => {
const columns = await query(`PRAGMA table_info(${tableName})`);
return columns.some((col: any) => col.name === columnName);
};
const ensureColumn = async (tableName: string, columnDefSql: string, columnName: string) => {
if (!(await columnExists(tableName, columnName))) {
await exec(`ALTER TABLE ${tableName} ADD COLUMN ${columnDefSql}`);
}
};
const ensureTable = async (createTableSql: string) => {
await exec(createTableSql);
};
const ensureIndex = async (createIndexSql: string) => {
await exec(createIndexSql);
};
const migrateDatabase = async () => {
// 跳过迁移,因为数据库连接可能未初始化
console.log('跳过数据库迁移');
};
// 数据库初始化函数
export const initDatabase = async () => {
try {
// 确保数据库连接已初始化
await initDbConnection();
// 检查是否需要初始化如果users表不存在则执行初始化
const usersTableExists = await tableExists('users');
if (!usersTableExists) {
// 读取并执行初始化SQL文件
const initSqlPath = path.join(path.dirname(import.meta.url.replace('file:///', '')), 'init.sql');
const initSql = fs.readFileSync(initSqlPath, 'utf8');
await exec(initSql);
console.log('数据库初始化成功');
} else {
console.log('数据库表已存在,跳过初始化');
await ensureColumn('questions', "analysis TEXT NOT NULL DEFAULT ''", 'analysis');
}
} catch (error) {
console.error('数据库初始化失败:', error);
// 即使初始化失败,服务器也应该继续运行
}
};
// 数据库查询工具函数
export const query = async (sql: string, params: any[] = []): Promise<any[]> => {
return new Promise(async (resolve, reject) => {
const db = await getDb();
if (!db) {
reject(new Error('数据库连接未初始化'));
return;
}
db.all(sql, params, (err: Error, rows: any[]) => {
if (err) {
reject(err);
} else {
resolve(rows);
}
});
});
};
// all函数是query函数的别名用于向后兼容
export const all = query;
export const run = async (sql: string, params: any[] = []): Promise<{ id: string }> => {
return new Promise(async (resolve, reject) => {
const db = await getDb();
if (!db) {
reject(new Error('数据库连接未初始化'));
return;
}
2025-12-22 18:29:23 +08:00
db.run(sql, params, function(this: any, err: Error | null) {
if (err) {
reject(err);
} else {
resolve({ id: this.lastID ? this.lastID.toString() : '' });
}
});
});
};
export const get = async (sql: string, params: any[] = []): Promise<any> => {
return new Promise(async (resolve, reject) => {
const db = await getDb();
if (!db) {
reject(new Error('数据库连接未初始化'));
return;
}
db.get(sql, params, (err: Error, row: any) => {
if (err) {
reject(err);
} else {
resolve(row);
}
});
});
};