File size: 13,549 Bytes
fcc1a30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
-- HR 对话质量评估系统 - Supabase 数据库表结构(含日志)
-- 在 Supabase SQL Editor 中执行此脚本

-- ============================================
-- 1. 对话历史表(已有)
-- ============================================
CREATE TABLE IF NOT EXISTS conversation_history (
    id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
    session_id TEXT NOT NULL,
    user_message TEXT NOT NULL,
    assistant_message TEXT NOT NULL,
    analysis_report JSONB,
    context_summary JSONB,
    metadata JSONB,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_conversation_history_session_id ON conversation_history(session_id);
CREATE INDEX IF NOT EXISTS idx_conversation_history_created_at ON conversation_history(created_at DESC);

-- ============================================
-- 2. API 访问日志表(新增)
-- ============================================
CREATE TABLE IF NOT EXISTS api_access_logs (
    id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
    timestamp TIMESTAMP WITH TIME ZONE DEFAULT NOW(),

    -- 请求信息
    endpoint TEXT NOT NULL,
    method TEXT NOT NULL,
    path TEXT,
    query_params JSONB,

    -- 客户端信息
    client_ip TEXT,
    user_agent TEXT,
    session_id TEXT,

    -- 性能指标
    response_time_ms INTEGER,
    status_code INTEGER,

    -- 附加信息
    metadata JSONB
);

-- 索引优化
CREATE INDEX IF NOT EXISTS idx_api_logs_timestamp ON api_access_logs(timestamp DESC);
CREATE INDEX IF NOT EXISTS idx_api_logs_endpoint ON api_access_logs(endpoint);
CREATE INDEX IF NOT EXISTS idx_api_logs_session_id ON api_access_logs(session_id);
CREATE INDEX IF NOT EXISTS idx_api_logs_status_code ON api_access_logs(status_code);

-- 分区(按月)- 可选,大数据量时使用
-- CREATE TABLE api_access_logs_y2025m01 PARTITION OF api_access_logs
--     FOR VALUES FROM ('2025-01-01') TO ('2025-02-01');

-- ============================================
-- 3. 错误日志表(新增)
-- ============================================
CREATE TABLE IF NOT EXISTS error_logs (
    id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
    timestamp TIMESTAMP WITH TIME ZONE DEFAULT NOW(),

    -- 错误信息
    error_type TEXT NOT NULL,  -- 'validation', 'model_load', 'api_error', etc.
    error_message TEXT NOT NULL,
    error_stack TEXT,

    -- 上下文
    endpoint TEXT,
    session_id TEXT,
    user_input TEXT,

    -- 请求详情
    request_data JSONB,

    -- 元数据
    metadata JSONB
);

CREATE INDEX IF NOT EXISTS idx_error_logs_timestamp ON error_logs(timestamp DESC);
CREATE INDEX IF NOT EXISTS idx_error_logs_type ON error_logs(error_type);
CREATE INDEX IF NOT EXISTS idx_error_logs_session_id ON error_logs(session_id);

-- ============================================
-- 4. 模型性能日志表(新增)
-- ============================================
CREATE TABLE IF NOT EXISTS model_performance_logs (
    id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
    timestamp TIMESTAMP WITH TIME ZONE DEFAULT NOW(),

    -- 模型信息
    model_name TEXT NOT NULL,
    model_type TEXT NOT NULL,  -- 'sbert', 'sentiment', 'compliance', 'llm'

    -- 性能指标
    load_time_ms INTEGER,
    inference_time_ms INTEGER,
    total_time_ms INTEGER,

    -- 输入输出
    input_text TEXT,
    output_summary TEXT,

    -- 附加信息
    metadata JSONB
);

CREATE INDEX IF NOT EXISTS idx_model_perf_timestamp ON model_performance_logs(timestamp DESC);
CREATE INDEX IF NOT EXISTS idx_model_perf_model_name ON model_performance_logs(model_name);
CREATE INDEX IF NOT EXISTS idx_model_perf_type ON model_performance_logs(model_type);

-- ============================================
-- 5. 调试日志表(新增)- 用于开发调试
-- ============================================
CREATE TABLE IF NOT EXISTS debug_logs (
    id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
    timestamp TIMESTAMP WITH TIME ZONE DEFAULT NOW(),

    -- 日志信息
    level TEXT NOT NULL,  -- 'DEBUG', 'INFO', 'WARNING', 'ERROR'
    logger TEXT NOT NULL,  -- 模块名称,如 'intelligence_analyzer'
    message TEXT NOT NULL,

    -- 上下文
    session_id TEXT,
    function_name TEXT,
    line_number INTEGER,

    -- 结构化数据
    data JSONB,

    -- 附加信息
    metadata JSONB
);

CREATE INDEX IF NOT EXISTS idx_debug_logs_timestamp ON debug_logs(timestamp DESC);
CREATE INDEX IF NOT EXISTS idx_debug_logs_level ON debug_logs(level);
CREATE INDEX IF NOT EXISTS idx_debug_logs_logger ON debug_logs(logger);
CREATE INDEX IF NOT EXISTS idx_debug_logs_session_id ON debug_logs(session_id);

-- ============================================
-- 6. 用户行为分析表(新增)
-- ============================================
CREATE TABLE IF NOT EXISTS user_behavior_logs (
    id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
    timestamp TIMESTAMP WITH TIME ZONE DEFAULT NOW(),

    -- 用户标识
    session_id TEXT NOT NULL,
    user_id TEXT,  -- 可选,如果有多用户系统

    -- 行为类型
    action_type TEXT NOT NULL,  -- 'chat', 'evaluate', 'config_check', etc.
    action_detail TEXT,

    -- 内容
    input_text TEXT,
    intent_detected TEXT,
    scenario_detected TEXT,

    -- 结果
    success BOOLEAN,
    error_message TEXT,

    -- 元数据
    metadata JSONB
);

CREATE INDEX IF NOT EXISTS idx_user_behavior_timestamp ON user_behavior_logs(timestamp DESC);
CREATE INDEX IF NOT EXISTS idx_user_behavior_session_id ON user_behavior_logs(session_id);
CREATE INDEX IF NOT EXISTS idx_user_behavior_action_type ON user_behavior_logs(action_type);

-- ============================================
-- 启用行级安全性 (RLS)
-- ============================================
ALTER TABLE api_access_logs ENABLE ROW LEVEL SECURITY;
ALTER TABLE error_logs ENABLE ROW LEVEL SECURITY;
ALTER TABLE model_performance_logs ENABLE ROW LEVEL SECURITY;
ALTER TABLE debug_logs ENABLE ROW LEVEL SECURITY;
ALTER TABLE user_behavior_logs ENABLE ROW LEVEL SECURITY;

-- 允许所有操作(演示环境)
CREATE POLICY "Enable all access for api_access_logs" ON api_access_logs FOR ALL USING (true);
CREATE POLICY "Enable all access for error_logs" ON error_logs FOR ALL USING (true);
CREATE POLICY "Enable all access for model_performance_logs" ON model_performance_logs FOR ALL USING (true);
CREATE POLICY "Enable all access for debug_logs" ON debug_logs FOR ALL USING (true);
CREATE POLICY "Enable all access for user_behavior_logs" ON user_behavior_logs FOR ALL USING (true);

-- ============================================
-- 实用函数
-- ============================================

-- 清理旧日志(保留最近 N 天)
CREATE OR REPLACE FUNCTION cleanup_old_logs(days_to_keep INTEGER DEFAULT 30)
RETURNS TABLE (
    api_logs_deleted BIGINT,
    error_logs_deleted BIGINT,
    debug_logs_deleted BIGINT,
    model_perf_logs_deleted BIGINT
)
LANGUAGE plpgsql
AS $$
DECLARE
    api_count BIGINT;
    error_count BIGINT;
    debug_count BIGINT;
    model_count BIGINT;
BEGIN
    -- 清理 API 日志
    DELETE FROM api_access_logs
    WHERE timestamp < NOW() - (days_to_keep || ' days')::INTERVAL;
    GET DIAGNOSTICS api_count = ROW_COUNT;

    -- 清理错误日志(保留更久)
    DELETE FROM error_logs
    WHERE timestamp < NOW() - ((days_to_keep * 2) || ' days')::INTERVAL;
    GET DIAGNOSTICS error_count = ROW_COUNT;

    -- 清理调试日志(保留更短时间)
    DELETE FROM debug_logs
    WHERE timestamp < NOW() - ((days_to_keep / 2) || ' days')::INTERVAL;
    GET DIAGNOSTICS debug_count = ROW_COUNT;

    -- 清理模型性能日志
    DELETE FROM model_performance_logs
    WHERE timestamp < NOW() - (days_to_keep || ' days')::INTERVAL;
    GET DIAGNOSTICS model_count = ROW_COUNT;

    RETURN QUERY SELECT api_count, error_count, debug_count, model_count;
END;
$$;

-- 获取 API 统计
CREATE OR REPLACE FUNCTION get_api_statistics(hours_ago INTEGER DEFAULT 24)
RETURNS TABLE (
    endpoint TEXT,
    request_count BIGINT,
    avg_response_time NUMERIC,
    error_rate NUMERIC,
    p95_response_time NUMERIC
)
LANGUAGE plpgsql
AS $$
BEGIN
    RETURN QUERY
    SELECT
        endpoint,
        COUNT(*) as request_count,
        AVG(response_time_ms) as avg_response_time,
        COUNT(*) FILTER (WHERE status_code >= 400) * 100.0 / COUNT(*) as error_rate,
        PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY response_time_ms) as p95_response_time
    FROM api_access_logs
    WHERE timestamp >= NOW() - (hours_ago || ' hours')::INTERVAL
    GROUP BY endpoint
    ORDER BY request_count DESC;
END;
$$;

-- 获取错误统计
CREATE OR REPLACE FUNCTION get_error_summary(hours_ago INTEGER DEFAULT 24)
RETURNS TABLE (
    error_type TEXT,
    error_count BIGINT,
    most_common_message TEXT
)
LANGUAGE plpgsql
AS $$
BEGIN
    RETURN QUERY
    SELECT
        error_type,
        COUNT(*) as error_count,
        mode() WITHIN GROUP (ORDER BY error_message) as most_common_message
    FROM error_logs
    WHERE timestamp >= NOW() - (hours_ago || ' hours')::INTERVAL
    GROUP BY error_type
    ORDER BY error_count DESC;
END;
$$;

-- 搜索日志
CREATE OR REPLACE FUNCTION search_logs(
    search_term TEXT,
    log_type TEXT DEFAULT 'all',  -- 'api', 'error', 'debug', 'all'
    hours_ago INTEGER DEFAULT 24,
    limit_count INTEGER DEFAULT 100
)
RETURNS TABLE (
    log_type TEXT,
    log_id UUID,
    timestamp TIMESTAMP WITH TIME ZONE,
    summary TEXT
)
LANGUAGE plpgsql
AS $$
BEGIN
    -- API 日志
    IF log_type = 'api' OR log_type = 'all' THEN
        RETURN QUERY
        SELECT
            'api'::TEXT as log_type,
            id as log_id,
            timestamp,
            endpoint || ' - ' || status_code::TEXT as summary
        FROM api_access_logs
        WHERE (
            search_term ILIKE '%' || search_term || '%'
            OR endpoint ILIKE '%' || search_term || '%'
            OR path ILIKE '%' || search_term || '%'
        )
        AND timestamp >= NOW() - (hours_ago || ' hours')::INTERVAL
        LIMIT limit_count;
    END IF;

    -- 错误日志
    IF log_type = 'error' OR log_type = 'all' THEN
        RETURN QUERY
        SELECT
            'error'::TEXT as log_type,
            id as log_id,
            timestamp,
            error_type || ' - ' || LEFT(error_message, 50) as summary
        FROM error_logs
        WHERE error_message ILIKE '%' || search_term || '%'
        AND timestamp >= NOW() - (hours_ago || ' hours')::INTERVAL
        LIMIT limit_count;
    END IF;

    -- 调试日志
    IF log_type = 'debug' OR log_type = 'all' THEN
        RETURN QUERY
        SELECT
            'debug'::TEXT as log_type,
            id as log_id,
            timestamp,
            level || ' - ' || logger || ' - ' || LEFT(message, 50) as summary
        FROM debug_logs
        WHERE message ILIKE '%' || search_term || '%'
        AND timestamp >= NOW() - (hours_ago || ' hours')::INTERVAL
        LIMIT limit_count;
    END IF;
END;
$$;

-- ============================================
-- 定时清理任务(可选)
-- ============================================
-- 创建一个函数来定期清理日志
CREATE OR REPLACE FUNCTION auto_cleanup_logs()
RETURNS VOID
LANGUAGE plpgsql
AS $$
BEGIN
    -- 清理 7 天前的调试日志
    DELETE FROM debug_logs
    WHERE timestamp < NOW() - INTERVAL '7 days';

    -- 清理 30 天前的 API 日志
    DELETE FROM api_access_logs
    WHERE timestamp < NOW() - INTERVAL '30 days';

    -- 清理 90 天前的模型性能日志
    DELETE FROM model_performance_logs
    WHERE timestamp < NOW() - INTERVAL '90 days';

    -- 错误日志保留 180 天
    DELETE FROM error_logs
    WHERE timestamp < NOW() - INTERVAL '180 days';
END;
$$;

-- 在 Supabase Dashboard 中设置 cron job 来调用这个函数
-- 或者使用 pg_cron 扩展(如果可用)
-- SELECT cron.schedule('cleanup-logs', '0 2 * * *', 'SELECT auto_cleanup_logs()');

-- ============================================
-- 视图 - 方便查询
-- ============================================

-- 综合日志视图
CREATE OR REPLACE VIEW v_recent_logs AS
SELECT
    'api' as log_type,
    timestamp,
    endpoint as title,
    status_code::TEXT as detail,
    session_id
FROM api_access_logs
WHERE timestamp >= NOW() - INTERVAL '24 hours'

UNION ALL

SELECT
    'error' as log_type,
    timestamp,
    error_type as title,
    LEFT(error_message, 50) as detail,
    session_id
FROM error_logs
WHERE timestamp >= NOW() - INTERVAL '24 hours'

UNION ALL

SELECT
    'debug' as log_type,
    timestamp,
    logger as title,
    level || ' - ' || LEFT(message, 30) as detail,
    session_id
FROM debug_logs
WHERE timestamp >= NOW() - INTERVAL '24 hours'

ORDER BY timestamp DESC
LIMIT 100;

-- ============================================
-- 注释
-- ============================================
COMMENT ON TABLE api_access_logs IS 'API 访问日志';
COMMENT ON TABLE error_logs IS '错误日志';
COMMENT ON TABLE model_performance_logs IS '模型性能日志';
COMMENT ON TABLE debug_logs IS '调试日志';
COMMENT ON TABLE user_behavior_logs IS '用户行为日志';

COMMENT ON FUNCTION cleanup_old_logs IS '清理旧日志,默认保留 30 天';
COMMENT ON FUNCTION get_api_statistics IS '获取 API 统计信息';
COMMENT ON FUNCTION get_error_summary IS '获取错误摘要';
COMMENT ON FUNCTION search_logs IS '搜索日志内容';
COMMENT ON FUNCTION auto_cleanup_logs IS '自动清理日志';
COMMENT ON VIEW v_recent_logs IS '最近 24 小时的综合日志视图';