✨ feat(database): 完善请求日志体系并为全表补充时间字段
- request_log 新增 user_agent 列(含旧表 ALTER 迁移)及 ip/status 索引,记录解码后的明文 query(含 key,不脱敏) - 请求日志批量写入改为多值 INSERT 单次往返,替代事务逐条 Exec - 新增按本地时区分天聚合查询,状态页展示今日请求量与近 7 天请求趋势(峰值日高亮) - 新增 TZ 时区支持:.env 配置 TZ 后日志时间戳与状态页全部时间统一生效,二进制内嵌 tzdata 保证容器可用 - 五张表统一补齐 created_at/updated_at 字段,启动时自动补列并按业务时间回填旧行 - device_pool 的 device_header 增加唯一约束;audio_cache 写入改为 UPSERT 修复同 key 重复报错 - 状态页"最近请求"新增 UA 列与限流状态显示;config.yaml 暴露 rate_limit_qps 配置并默认启用请求日志
This commit is contained in:
1 parent
1a4997e031
commit
126a96d744
12 files changed
+277
-55
No files matched your search
@@ -149,12 +149,14 @@ func createTables() error {
|
||||
`CREATE TABLE IF NOT EXISTS cache (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT,
|
||||
created_at INTEGER DEFAULT (strftime('%s', 'now'))
|
||||
created_at INTEGER DEFAULT (strftime('%s', 'now')),
|
||||
updated_at INTEGER DEFAULT (strftime('%s', 'now'))
|
||||
)`,
|
||||
`CREATE TABLE IF NOT EXISTS cache (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT,
|
||||
created_at INTEGER DEFAULT 0
|
||||
created_at BIGINT DEFAULT extract(epoch FROM now())::bigint,
|
||||
updated_at BIGINT DEFAULT extract(epoch FROM now())::bigint
|
||||
)`},
|
||||
{"statistics",
|
||||
`CREATE TABLE IF NOT EXISTS statistics (
|
||||
@@ -168,6 +170,7 @@ func createTables() error {
|
||||
audio_requests INTEGER DEFAULT 0,
|
||||
video_requests INTEGER DEFAULT 0,
|
||||
category_requests INTEGER DEFAULT 0,
|
||||
created_at INTEGER DEFAULT (strftime('%s', 'now')),
|
||||
updated_at INTEGER DEFAULT (strftime('%s', 'now'))
|
||||
)`,
|
||||
`CREATE TABLE IF NOT EXISTS statistics (
|
||||
@@ -181,26 +184,29 @@ func createTables() error {
|
||||
audio_requests INTEGER DEFAULT 0,
|
||||
video_requests INTEGER DEFAULT 0,
|
||||
category_requests INTEGER DEFAULT 0,
|
||||
updated_at INTEGER DEFAULT 0
|
||||
created_at BIGINT DEFAULT extract(epoch FROM now())::bigint,
|
||||
updated_at BIGINT DEFAULT extract(epoch FROM now())::bigint
|
||||
)`},
|
||||
{"device_pool",
|
||||
`CREATE TABLE IF NOT EXISTS device_pool (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
device_header TEXT NOT NULL,
|
||||
device_header TEXT NOT NULL UNIQUE,
|
||||
key TEXT NOT NULL,
|
||||
key_ver INTEGER NOT NULL,
|
||||
is_valid INTEGER DEFAULT 1,
|
||||
created_at INTEGER NOT NULL,
|
||||
last_used_at INTEGER NOT NULL
|
||||
last_used_at INTEGER NOT NULL,
|
||||
updated_at INTEGER DEFAULT (strftime('%s', 'now'))
|
||||
)`,
|
||||
`CREATE TABLE IF NOT EXISTS device_pool (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
device_header TEXT NOT NULL,
|
||||
device_header TEXT NOT NULL UNIQUE,
|
||||
key TEXT NOT NULL,
|
||||
key_ver INTEGER NOT NULL,
|
||||
is_valid INTEGER DEFAULT 1,
|
||||
created_at INTEGER NOT NULL,
|
||||
last_used_at INTEGER NOT NULL
|
||||
last_used_at INTEGER NOT NULL,
|
||||
updated_at BIGINT DEFAULT extract(epoch FROM now())::bigint
|
||||
)`},
|
||||
{"audio_cache",
|
||||
`CREATE TABLE IF NOT EXISTS audio_cache (
|
||||
@@ -208,14 +214,16 @@ func createTables() error {
|
||||
original_url TEXT NOT NULL,
|
||||
device_id TEXT NOT NULL,
|
||||
headers TEXT,
|
||||
created_at INTEGER DEFAULT (strftime('%s', 'now'))
|
||||
created_at INTEGER DEFAULT (strftime('%s', 'now')),
|
||||
updated_at INTEGER DEFAULT (strftime('%s', 'now'))
|
||||
)`,
|
||||
`CREATE TABLE IF NOT EXISTS audio_cache (
|
||||
key TEXT PRIMARY KEY,
|
||||
original_url TEXT NOT NULL,
|
||||
device_id TEXT NOT NULL,
|
||||
headers TEXT,
|
||||
created_at INTEGER DEFAULT 0
|
||||
created_at BIGINT DEFAULT extract(epoch FROM now())::bigint,
|
||||
updated_at BIGINT DEFAULT extract(epoch FROM now())::bigint
|
||||
)`},
|
||||
}
|
||||
|
||||
@@ -242,6 +250,42 @@ func createTables() error {
|
||||
if _, err := cacheDB.Exec(`CREATE INDEX IF NOT EXISTS idx_device_pool_valid ON device_pool(is_valid)`); err != nil {
|
||||
cacheLogger.Warning("创建device_pool索引失败: " + err.Error())
|
||||
}
|
||||
// 旧库补唯一约束:表已存在时 DDL 的 UNIQUE 不会生效,用唯一索引补齐;
|
||||
// 若存量数据已有重复 device_header 则创建失败,仅告警提示人工清理
|
||||
if _, err := cacheDB.Exec(`CREATE UNIQUE INDEX IF NOT EXISTS idx_device_pool_header ON device_pool(device_header)`); err != nil {
|
||||
cacheLogger.Warning("补建device_header唯一索引失败(可能存在重复设备,清理后重启即可): " + err.Error())
|
||||
}
|
||||
|
||||
// 旧表补 created_at/updated_at 列(新表 DDL 自带,此处仅老库生效)
|
||||
for _, m := range []struct{ table, col, ddl string }{
|
||||
{"cache", "updated_at", "INTEGER DEFAULT 0"}, // SQLite ALTER 不允许非常量默认值,先补 0 再回填
|
||||
{"statistics", "created_at", "INTEGER DEFAULT 0"},
|
||||
{"device_pool", "updated_at", "INTEGER DEFAULT 0"},
|
||||
{"audio_cache", "updated_at", "INTEGER DEFAULT 0"},
|
||||
} {
|
||||
if dbDriver == "pgx" {
|
||||
m.ddl = "BIGINT DEFAULT 0"
|
||||
}
|
||||
alter := "ALTER TABLE " + m.table + " ADD COLUMN "
|
||||
if dbDriver == "pgx" {
|
||||
alter += "IF NOT EXISTS "
|
||||
}
|
||||
if _, err := cacheDB.Exec(alter + m.col + " " + m.ddl); err != nil {
|
||||
cacheLogger.Debug("补列跳过(已存在): " + m.table + "." + m.col)
|
||||
}
|
||||
}
|
||||
|
||||
// 旧行回填(幂等:仅 0 值行受影响)
|
||||
for _, backfill := range []string{
|
||||
`UPDATE cache SET updated_at = created_at WHERE updated_at = 0`,
|
||||
`UPDATE statistics SET created_at = updated_at WHERE created_at = 0`,
|
||||
`UPDATE device_pool SET updated_at = last_used_at WHERE updated_at = 0`,
|
||||
`UPDATE audio_cache SET updated_at = created_at WHERE updated_at = 0`,
|
||||
} {
|
||||
if _, err := cacheDB.Exec(backfill); err != nil {
|
||||
cacheLogger.Warning("时间列回填失败: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -337,8 +381,9 @@ func UpdateCache(key string, value string, expi int) error {
|
||||
}
|
||||
|
||||
// UPSERT:ON CONFLICT 语法 SQLite 3.24+ 与 PostgreSQL 通用
|
||||
_, err = cacheDB.Exec(q(`INSERT INTO cache (key, value, created_at) VALUES (?, ?, ?)
|
||||
ON CONFLICT(key) DO UPDATE SET value = excluded.value`), key, string(jsonData), time.Now().Unix())
|
||||
_, err = cacheDB.Exec(q(`INSERT INTO cache (key, value, created_at, updated_at) VALUES (?, ?, ?, ?)
|
||||
ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at`),
|
||||
key, string(jsonData), time.Now().Unix(), time.Now().Unix())
|
||||
if err != nil {
|
||||
cacheLogger.Error("更新缓存失败: " + err.Error())
|
||||
return err
|
||||
|
||||
Reference in new issue
Block a user