import buildKnex from "knex" import knexConfig from "../../knexfile.mjs" // 简单内存缓存(支持 TTL 与按前缀清理) const queryCache = new Map() const getNow = () => Date.now() const computeExpiresAt = (ttlMs) => { if (!ttlMs || ttlMs <= 0) return null return getNow() + ttlMs } const isExpired = (entry) => { if (!entry) return true if (entry.expiresAt == null) return false return entry.expiresAt <= getNow() } const getCacheKeyForBuilder = (builder) => { if (builder._customCacheKey) return String(builder._customCacheKey) return builder.toString() } // 全局工具,便于在 QL 外部操作缓存 export const DbQueryCache = { get(key) { const entry = queryCache.get(String(key)) if (!entry) return undefined if (isExpired(entry)) { queryCache.delete(String(key)) return undefined } return entry.value }, set(key, value, ttlMs) { const expiresAt = computeExpiresAt(ttlMs) queryCache.set(String(key), { value, expiresAt }) return value }, has(key) { const entry = queryCache.get(String(key)) return !!entry && !isExpired(entry) }, delete(key) { return queryCache.delete(String(key)) }, clear() { queryCache.clear() }, clearByPrefix(prefix) { const p = String(prefix) for (const k of queryCache.keys()) { if (k.startsWith(p)) queryCache.delete(k) } }, stats() { let valid = 0 let expired = 0 for (const [k, entry] of queryCache.entries()) { if (isExpired(entry)) expired++ else valid++ } return { size: queryCache.size, valid, expired } } } // QueryBuilder 扩展 // 1) cache(ttlMs?): 读取缓存,不存在则执行并写入 buildKnex.QueryBuilder.extend("cache", async function (ttlMs) { const key = getCacheKeyForBuilder(this) const entry = queryCache.get(key) if (entry && !isExpired(entry)) { return entry.value } const data = await this queryCache.set(key, { value: data, expiresAt: computeExpiresAt(ttlMs) }) return data }) // 2) cacheAs(customKey): 设置自定义 key buildKnex.QueryBuilder.extend("cacheAs", function (customKey) { this._customCacheKey = String(customKey) return this }) // 3) cacheSet(value, ttlMs?): 手动设置当前查询 key 的缓存 buildKnex.QueryBuilder.extend("cacheSet", function (value, ttlMs) { const key = getCacheKeyForBuilder(this) queryCache.set(key, { value, expiresAt: computeExpiresAt(ttlMs) }) return value }) // 4) cacheGet(): 仅从缓存读取当前查询 key 的值 buildKnex.QueryBuilder.extend("cacheGet", function () { const key = getCacheKeyForBuilder(this) const entry = queryCache.get(key) if (!entry || isExpired(entry)) return undefined return entry.value }) // 5) cacheInvalidate(): 使当前查询 key 的缓存失效 buildKnex.QueryBuilder.extend("cacheInvalidate", function () { const key = getCacheKeyForBuilder(this) queryCache.delete(key) return this }) // 6) cacheInvalidateByPrefix(prefix): 按前缀清理 buildKnex.QueryBuilder.extend("cacheInvalidateByPrefix", function (prefix) { const p = String(prefix) for (const k of queryCache.keys()) { if (k.startsWith(p)) queryCache.delete(k) } return this }) const environment = process.env.NODE_ENV || "development" const db = buildKnex(knexConfig[environment]) export default db // async function createDatabase() { // try { // // SQLite会自动创建数据库文件,只需验证连接 // await db.raw("SELECT 1") // console.log("SQLite数据库连接成功") // // 检查users表是否存在(示例) // const [tableExists] = await db.raw(` // SELECT name // FROM sqlite_master // WHERE type='table' AND name='users' // `) // if (tableExists) { // console.log("表 users 已存在") // } else { // console.log("表 users 不存在,需要创建(通过迁移)") // } // await db.destroy() // } catch (error) { // console.error("数据库操作失败:", error) // process.exit(1) // } // } // createDatabase()