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