database.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. const { Pool } = require('pg');
  2. const fs = require('fs');
  3. const path = require('path');
  4. const { db } = require('./config');
  5. // 全局连接池
  6. let pool;
  7. // 创建连接池
  8. function createPool() {
  9. if (!pool) {
  10. pool = new Pool(db);
  11. // 错误处理
  12. pool.on('error', (err) => {
  13. console.error('Unexpected database error', err);
  14. });
  15. }
  16. return pool;
  17. }
  18. // 查询函数
  19. exports.query = async (text, params) => {
  20. const pool = createPool();
  21. try {
  22. return await pool.query(text, params);
  23. } catch (error) {
  24. console.error('Database query failed:', error);
  25. throw error;
  26. }
  27. };
  28. // 事务执行函数
  29. exports.executeInTransaction = async (callback) => {
  30. const pool = createPool();
  31. const client = await pool.connect();
  32. try {
  33. await client.query('BEGIN');
  34. await callback(client);
  35. await client.query('COMMIT');
  36. } catch (error) {
  37. await client.query('ROLLBACK');
  38. throw error;
  39. } finally {
  40. client.release();
  41. }
  42. };
  43. // 测试数据库连接
  44. exports.testDbConnection = async () => {
  45. try {
  46. const result = await exports.query('SELECT NOW()');
  47. return { time: result.rows[0].now };
  48. } catch (error) {
  49. throw new Error(`Database connection failed: ${error.message}`);
  50. }
  51. };
  52. // 完整的数据库初始化
  53. exports.initializeDatabase = async () => {
  54. try {
  55. // 第一步:检查数据库是否存在
  56. try {
  57. // 尝试连接到目标数据库
  58. await exports.testDbConnection();
  59. console.log('✅ 数据库连接成功');
  60. } catch (dbError) {
  61. // 如果数据库不存在,创建它
  62. if (dbError.code === '3D000') {
  63. console.log('🆕 数据库不存在,尝试创建...');
  64. // 连接到默认的postgres数据库
  65. const adminPool = new Pool({
  66. ...db,
  67. database: 'postgres'
  68. });
  69. // 创建新数据库
  70. await adminPool.query(`CREATE DATABASE ${db.database}`);
  71. console.log(`🎉 数据库 ${db.database} 创建成功`);
  72. // 关闭管理员连接
  73. await adminPool.end();
  74. } else {
  75. throw dbError;
  76. }
  77. }
  78. // 第二步:检查表是否存在
  79. const tableExists = await exports.query(
  80. `SELECT EXISTS (
  81. SELECT FROM information_schema.tables
  82. WHERE table_schema = 'public'
  83. AND table_name = 'design_usage'
  84. )`
  85. );
  86. // 如果表不存在则创建
  87. if (!tableExists.rows[0].exists) {
  88. console.log('🆕 创建 design_usage 表...');
  89. // 创建表结构
  90. await exports.query(`
  91. CREATE TABLE design_usage (
  92. id SERIAL PRIMARY KEY,
  93. part_id INTEGER NOT NULL CHECK (part_id BETWEEN 1 AND 4),
  94. color_code CHAR(2) NOT NULL CHECK (color_code ~ '^[0-9A-F]{2}$'),
  95. usage_count INTEGER NOT NULL DEFAULT 0,
  96. UNIQUE (part_id, color_code)
  97. )
  98. `);
  99. console.log('🎉 design_usage 表创建成功');
  100. // 插入初始数据
  101. console.log('📥 插入初始数据...');
  102. await exports.query(`
  103. INSERT INTO design_usage (part_id, color_code, usage_count) VALUES
  104. (1, '00', 1850), (1, '01', 1620), (1, '02', 1540), (1, '03', 1420),
  105. (2, '04', 1360), (2, '05', 1280), (2, '06', 1200), (2, '07', 1150),
  106. (3, '08', 1100), (3, '09', 1050), (3, '0A', 1000), (3, '0B', 950),
  107. (4, '0C', 900), (4, '0D', 850), (4, '0E', 800), (4, '0F', 750)
  108. `);
  109. console.log('🎉 初始数据插入完成');
  110. }
  111. // 第三步:测试数据库连接
  112. const dbStatus = await exports.testDbConnection();
  113. console.log('📡 数据库连接成功:', dbStatus);
  114. return true;
  115. } catch (error) {
  116. console.error('🔥 数据库初始化失败:');
  117. console.error(error);
  118. throw error;
  119. }
  120. };
  121. // 获取所有设计使用情况
  122. exports.getAllDesignUsage = async () => {
  123. try {
  124. const result = await exports.query(`
  125. SELECT part_id, color_code, usage_count
  126. FROM design_usage
  127. ORDER BY part_id, usage_count DESC
  128. `);
  129. return result.rows.map(row => ({
  130. partId: row.part_id,
  131. colorCode: row.color_code,
  132. usageCount: row.usage_count
  133. }));
  134. } catch (error) {
  135. throw new Error(`获取设计使用数据失败: ${error.message}`);
  136. }
  137. };
  138. // 记录设计使用情况
  139. exports.recordDesignUsage = async (design) => {
  140. await exports.executeInTransaction(async (client) => {
  141. const colors = [
  142. design.part1, design.part2,
  143. design.part3, design.part4
  144. ];
  145. for (let partId = 1; partId <= 4; partId++) {
  146. const colorCode = colors[partId - 1];
  147. await client.query(
  148. `INSERT INTO design_usage (part_id, color_code, usage_count)
  149. VALUES ($1, $2, 1)
  150. ON CONFLICT (part_id, color_code)
  151. DO UPDATE SET usage_count = design_usage.usage_count + 1`,
  152. [partId, colorCode]
  153. );
  154. }
  155. });
  156. };