vue3 electron 引入 better-sqlite3

49 阅读2分钟

一、数据库配置

路径: electron/ipc/db/db-config.js

const path = require('path')
const fs = require('fs')

// 数据库存放路径(建议放在用户数据目录)
const { app } = require('electron')
const dbDir = path.join(app.getPath('userData'), 'database')

// 确保目录存在
if (!fs.existsSync(dbDir)) {
  fs.mkdirSync(dbDir, { recursive: true })
}

module.exports = {
  dbFile: path.join(dbDir, 'app_data.db'),
  dbVersionFile: path.join(dbDir, 'db_version.json'),
  currentVersion: 1 // 🔧 修改此处即可触发结构重建
}

二、表结构定义

electron/ipc/db/schema/goods.js

module.exports = {
  name: 'goods',
  createTableSQL: `
    CREATE TABLE IF NOT EXISTS goods (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      name TEXT NOT NULL,
      barcode TEXT,
      price REAL DEFAULT 0,
      stock INTEGER DEFAULT 0,
      category TEXT,
      create_time DATETIME DEFAULT CURRENT_TIMESTAMP
    )
  `
}

electron/ipc/db/schema/version.js

module.exports = {
  name: 'db_version',
  createTableSQL: `
    CREATE TABLE IF NOT EXISTS db_version (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      version INTEGER,
      updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
    )
  `
}

三、数据库初始化与版本检测逻辑

路径: electron/ipc/db/index.js

const Database = require('better-sqlite3')
const fs = require('fs')
const path = require('path')
const config = require('./db-config')

// 加载所有表结构定义
const goods = require('./schema/goods')
const orders = require('./schema/orders')
const version = require('./schema/version')

const tables = [goods, orders, version]

let dbInstance = null

/**
 * 获取当前数据库实例
 */
function getDB() {
  if (!dbInstance) initializeDatabase()
  return dbInstance
}

/**
 * 初始化数据库
 */
function initializeDatabase() {
  console.log('[DB] 初始化数据库...')

  // 检查数据库版本
  const currentVersion = config.currentVersion
  const versionFile = config.dbVersionFile
  let savedVersion = 0

  if (fs.existsSync(versionFile)) {
    try {
      savedVersion = JSON.parse(fs.readFileSync(versionFile, 'utf8')).version || 0
    } catch {
      savedVersion = 0
    }
  }

  // 如果版本不一致则重建数据库
  if (savedVersion !== currentVersion) {
    console.log(`[DB] 版本不一致 (${savedVersion} -> ${currentVersion}),重建数据库...`)
    if (fs.existsSync(config.dbFile)) fs.unlinkSync(config.dbFile)
  }

  // 初始化数据库连接
  dbInstance = new Database(config.dbFile)
  dbInstance.pragma('journal_mode = WAL')

  // 创建表
  for (const table of tables) {
    dbInstance.prepare(table.createTableSQL).run()
    console.log(`[DB] 表 ${table.name} 已创建`)
  }

  // 更新版本文件
  fs.writeFileSync(versionFile, JSON.stringify({ version: currentVersion }, null, 2))
  console.log(`[DB] 数据库版本 ${currentVersion} 已同步`)
}

/**
 * 重建数据库(手动触发)
 */
function rebuildDatabase() {
  if (fs.existsSync(config.dbFile)) {
    fs.unlinkSync(config.dbFile)
  }
  initializeDatabase()
}

/**
 * 执行查询(封装简单查询接口)
 */
function query(sql, params = []) {
  return getDB().prepare(sql).all(params)
}

/**
 * 执行更新/插入
 */
function execute(sql, params = []) {
  return getDB().prepare(sql).run(params)
}

module.exports = {
  getDB,
  initializeDatabase,
  rebuildDatabase,
  query,
  execute
}

四、数据库 API 暴露(前端可调用)

路径: electron/ipc/api/db.js

const db = require('../db/index')

/**
 * 获取所有商品
 */
async function getAllGoods() {
  return db.query('SELECT * FROM goods ORDER BY id DESC')
}

/**
 * 新增商品
 */
async function addGood(_, { name, barcode, price, stock, category }) {
  const sql = `INSERT INTO goods (name, barcode, price, stock, category)
               VALUES (?, ?, ?, ?, ?)`
  return db.execute(sql, [name, barcode, price, stock, category])
}

/**
 * 获取所有订单
 */
async function getAllOrders() {
  return db.query('SELECT * FROM orders ORDER BY id DESC')
}

/**
 * 新增订单
 */
async function addOrder(_, { order_no, goods_list, total_amount, pay_type }) {
  const sql = `INSERT INTO orders (order_no, goods_list, total_amount, pay_type)
               VALUES (?, ?, ?, ?)`
  return db.execute(sql, [order_no, JSON.stringify(goods_list), total_amount, pay_type])
}

module.exports = {
  getAllGoods,
  addGood,
  getAllOrders,
  addOrder
}

五、在更新模块中集成数据库版本检查

路径: electron/ipc/api/updater.js
在自动更新成功后(或下载完成后)检查数据库结构。

在原 update-downloaded 事件中加入:

const db = require('../db/index')

// ...

autoUpdater.on('update-downloaded', (info) => {
  console.log('更新下载完成,准备安装')
  updateState.isDownloading = false
  updateState.updateDownloaded = true
  updateState.downloadProgress = 100

  // ✅ 自动检查数据库版本
  try {
    db.initializeDatabase()
    console.log('[DB] 数据库结构检查完毕')
  } catch (err) {
    console.error('[DB] 初始化失败:', err)
  }

  if (mainWindow) {
    mainWindow.webContents.send('updater:updateDownloaded', info)
    mainWindow.webContents.send('updater:updateMessage', '更新下载完成,准备安装')
  }

  // ...
})

六、manifest 增加 db 模块

路径: electron/ipc/manifest.js

{
  namespace: 'db',
  methods: ['getAllGoods', 'addGood', 'getAllOrders', 'addOrder'],
  events: []
},

七、前端调用封装

路径: src/utils/electron.js

export const ipcInvoke = (channel, ...args) => {
  return window.electron?.ipcRenderer.invoke(channel, ...args)
}

export const DB = {
  getAllGoods: () => ipcInvoke('db:getAllGoods'),
  addGood: (data) => ipcInvoke('db:addGood', data),
  getAllOrders: () => ipcInvoke('db:getAllOrders'),
  addOrder: (data) => ipcInvoke('db:addOrder', data)
}