You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

149 lines
4.2 KiB

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()