sql.js 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. // SQLite 数据库操作模块
  2. const Database = require('better-sqlite3');
  3. const path = require('path');
  4. const fs = require('fs');
  5. const { promisify } = require('util');
  6. const mkdir = promisify(fs.mkdir);
  7. const access = promisify(fs.access);
  8. class DatabaseManager {
  9. constructor() {
  10. this.dbPath = path.join(__dirname, 'data.db');
  11. this.db = null;
  12. }
  13. // 初始化数据库连接
  14. async init() {
  15. try {
  16. this.db = new Database(this.dbPath);
  17. console.log('[SQL] 数据库连接成功');
  18. this.createTables();
  19. } catch (err) {
  20. console.error('[SQL] 数据库连接失败:', err);
  21. throw err;
  22. }
  23. }
  24. // 创建表
  25. createTables() {
  26. try {
  27. const createUsersTable = `
  28. CREATE TABLE IF NOT EXISTS users (
  29. id INTEGER PRIMARY KEY AUTOINCREMENT,
  30. username TEXT UNIQUE NOT NULL,
  31. phone TEXT UNIQUE NOT NULL,
  32. password TEXT NOT NULL,
  33. avatar TEXT,
  34. points INTEGER DEFAULT 0,
  35. created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
  36. updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
  37. )
  38. `;
  39. // 如果表已存在但没有 points 字段,添加该字段
  40. try {
  41. this.db.exec('ALTER TABLE users ADD COLUMN points INTEGER DEFAULT 0');
  42. console.log('[SQL] 已添加 points 字段到 users 表');
  43. } catch (err) {
  44. // 字段已存在,忽略错误
  45. }
  46. this.db.exec(createUsersTable);
  47. console.log('[SQL] users表已创建或已存在');
  48. } catch (err) {
  49. console.error('[SQL] 创建表失败:', err);
  50. throw err;
  51. }
  52. }
  53. // 根据用户名查找用户(不区分大小写)
  54. findUserByUsername(username) {
  55. // 将输入转换为小写进行查询,但返回原始数据
  56. const normalizedUsername = username.toLowerCase();
  57. const sql = 'SELECT * FROM users WHERE LOWER(username) = ?';
  58. return this.db.prepare(sql).get(normalizedUsername) || null;
  59. }
  60. // 根据手机号查找用户
  61. findUserByPhone(phone) {
  62. const sql = 'SELECT * FROM users WHERE phone = ?';
  63. return this.db.prepare(sql).get(phone) || null;
  64. }
  65. // 根据ID查找用户
  66. findUserById(id) {
  67. const sql = 'SELECT * FROM users WHERE id = ?';
  68. return this.db.prepare(sql).get(id) || null;
  69. }
  70. // 获取所有用户
  71. getAllUsers() {
  72. const sql = 'SELECT id, username, phone, points, avatar, created_at, updated_at FROM users ORDER BY id DESC';
  73. return this.db.prepare(sql).all() || [];
  74. }
  75. // 创建新用户
  76. createUser(userData) {
  77. const { username, phone, password, avatar, points } = userData;
  78. const sql = `
  79. INSERT INTO users (username, phone, password, avatar, points)
  80. VALUES (?, ?, ?, ?, ?)
  81. `;
  82. const stmt = this.db.prepare(sql);
  83. const result = stmt.run(username, phone, password, avatar || null, points || 0);
  84. return { id: result.lastInsertRowid, ...userData };
  85. }
  86. // 获取用户点数
  87. getUserPoints(username) {
  88. const normalizedUsername = username.toLowerCase();
  89. const sql = 'SELECT points FROM users WHERE LOWER(username) = ?';
  90. const result = this.db.prepare(sql).get(normalizedUsername);
  91. return result ? result.points : 0;
  92. }
  93. // 扣除用户点数
  94. deductPoints(username, points) {
  95. const normalizedUsername = username.toLowerCase();
  96. const sql = 'UPDATE users SET points = points - ?, updated_at = CURRENT_TIMESTAMP WHERE LOWER(username) = ? AND points >= ?';
  97. const stmt = this.db.prepare(sql);
  98. const result = stmt.run(points, normalizedUsername, points);
  99. return result.changes > 0;
  100. }
  101. // 增加用户点数
  102. addPoints(username, points) {
  103. const normalizedUsername = username.toLowerCase();
  104. const sql = 'UPDATE users SET points = points + ?, updated_at = CURRENT_TIMESTAMP WHERE LOWER(username) = ?';
  105. const stmt = this.db.prepare(sql);
  106. const result = stmt.run(points, normalizedUsername);
  107. return result.changes > 0;
  108. }
  109. // 更新用户信息
  110. updateUser(id, updates) {
  111. const fields = [];
  112. const values = [];
  113. if (updates.username !== undefined) {
  114. fields.push('username = ?');
  115. values.push(updates.username);
  116. }
  117. if (updates.phone !== undefined) {
  118. fields.push('phone = ?');
  119. values.push(updates.phone);
  120. }
  121. if (updates.password !== undefined) {
  122. fields.push('password = ?');
  123. values.push(updates.password);
  124. }
  125. if (updates.avatar !== undefined) {
  126. fields.push('avatar = ?');
  127. values.push(updates.avatar);
  128. }
  129. fields.push('updated_at = CURRENT_TIMESTAMP');
  130. values.push(id);
  131. const sql = `UPDATE users SET ${fields.join(', ')} WHERE id = ?`;
  132. const stmt = this.db.prepare(sql);
  133. const result = stmt.run(...values);
  134. return { changes: result.changes };
  135. }
  136. // ============ AI 历史记录操作(保存到用户目录) ============
  137. // 获取用户历史文件路径
  138. _getHistoryFilePath(username) {
  139. const userDir = path.join(__dirname, 'users', username.toLowerCase(), 'generate-history');
  140. if (!fs.existsSync(userDir)) {
  141. fs.mkdirSync(userDir, { recursive: true });
  142. }
  143. return path.join(userDir, 'history.json');
  144. }
  145. // 加载用户历史
  146. _loadUserHistory(username) {
  147. const filePath = this._getHistoryFilePath(username);
  148. try {
  149. if (fs.existsSync(filePath)) {
  150. return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
  151. }
  152. } catch (err) {
  153. console.error('[SQL] 加载用户历史失败:', err);
  154. }
  155. return [];
  156. }
  157. // 保存用户历史
  158. _saveUserHistory(username, history) {
  159. const filePath = this._getHistoryFilePath(username);
  160. try {
  161. fs.writeFileSync(filePath, JSON.stringify(history, null, 2), 'utf-8');
  162. } catch (err) {
  163. console.error('[SQL] 保存用户历史失败:', err);
  164. }
  165. }
  166. // 添加 AI 历史记录
  167. addAIHistory(taskId, username, status, previewUrl, retryData = null, createdAt = null) {
  168. const history = this._loadUserHistory(username);
  169. const timestamp = createdAt || new Date().toISOString();
  170. history.unshift({
  171. id: taskId,
  172. status: status,
  173. imageUrl: null,
  174. previewUrl: previewUrl,
  175. error: null,
  176. isRetry: false,
  177. retryData: retryData,
  178. createdAt: timestamp,
  179. completedAt: null
  180. });
  181. this._saveUserHistory(username, history);
  182. return history.length;
  183. }
  184. // 获取用户 AI 历史记录
  185. getAIHistory(username) {
  186. return this._loadUserHistory(username);
  187. }
  188. // 更新 AI 任务状态
  189. updateAITaskStatus(taskId, status, imageUrl = null, error = null) {
  190. // 需要找到任务所属用户
  191. const usersDir = path.join(__dirname, 'users');
  192. if (!fs.existsSync(usersDir)) return false;
  193. const users = fs.readdirSync(usersDir);
  194. for (const username of users) {
  195. const history = this._loadUserHistory(username);
  196. const task = history.find(t => t.id === taskId);
  197. if (task) {
  198. task.status = status;
  199. if (imageUrl) task.imageUrl = imageUrl;
  200. if (error) task.error = error;
  201. task.completedAt = new Date().toISOString();
  202. this._saveUserHistory(username, history);
  203. return true;
  204. }
  205. }
  206. return false;
  207. }
  208. // 获取 AI 任务
  209. getAITask(taskId) {
  210. const usersDir = path.join(__dirname, 'users');
  211. if (!fs.existsSync(usersDir)) return null;
  212. const users = fs.readdirSync(usersDir);
  213. for (const username of users) {
  214. const history = this._loadUserHistory(username);
  215. const task = history.find(t => t.id === taskId);
  216. if (task) {
  217. return { ...task, username };
  218. }
  219. }
  220. return null;
  221. }
  222. // 删除 AI 任务
  223. deleteAITask(taskId) {
  224. const usersDir = path.join(__dirname, 'users');
  225. if (!fs.existsSync(usersDir)) return false;
  226. const users = fs.readdirSync(usersDir);
  227. for (const username of users) {
  228. const history = this._loadUserHistory(username);
  229. const index = history.findIndex(t => t.id === taskId);
  230. if (index !== -1) {
  231. history.splice(index, 1);
  232. this._saveUserHistory(username, history);
  233. return true;
  234. }
  235. }
  236. return false;
  237. }
  238. // 更新 AI 任务重试数据
  239. updateAITaskRetryData(taskId, retryData) {
  240. const usersDir = path.join(__dirname, 'users');
  241. if (!fs.existsSync(usersDir)) return false;
  242. const users = fs.readdirSync(usersDir);
  243. for (const username of users) {
  244. const history = this._loadUserHistory(username);
  245. const task = history.find(t => t.id === taskId);
  246. if (task) {
  247. task.retryData = retryData;
  248. this._saveUserHistory(username, history);
  249. return true;
  250. }
  251. }
  252. return false;
  253. }
  254. // 关闭数据库连接
  255. close() {
  256. if (this.db) {
  257. this.db.close();
  258. console.log('[SQL] 数据库连接已关闭');
  259. }
  260. }
  261. }
  262. // 创建单例
  263. let dbInstance = null;
  264. async function getDatabase() {
  265. if (!dbInstance) {
  266. dbInstance = new DatabaseManager();
  267. await dbInstance.init();
  268. }
  269. return dbInstance;
  270. }
  271. module.exports = {
  272. Database: DatabaseManager,
  273. getDatabase
  274. };