Files
SuperBizAgent-java/mvp/archive/2026-07-20-doc-cleanup/database/database-design-backup-20240622.md
T

48 KiB
Raw Blame History

数据库设计文档

归档说明:这是 2024 年数据库设计备份,已被当前 chat_session + diagnosis_run + run-scoped trace detail 模型替代,仅用于历史追溯。

一、设计原则

1.1 核心原则

  • ✅ 简单优先:满足诊断流程需要,避免过度设计
  • ✅ 渐进增强:先实现核心功能,再逐步扩展
  • ✅ 数据分离:诊断结果持久化(MySQL),会话上下文临时化(Redis)
  • ✅ 适度冗余:避免过度范式化,适当冗余提升查询性能

1.2 系统定位

自动化诊断系统

  • 核心:一键诊断 → 返回完整报告
  • 辅助:支持追问,但不是主要场景
  • 特点:大部分用户单次诊断即结束,少数用户会追问细节

二、核心表设计

2.1 diagnosis_record(诊断记录表)

设计理念:兼容多种故障类型

问题背景:

  • 初始设计过于聚焦"外部接口故障"(省份、接口URL、业务错误码)
  • 实际故障类型更丰富:空指针异常、数据库死锁、缓存穿透、线程池耗尽等
  • 需要字段泛化,支持外部接口故障 + 系统内部错误

解决方案:

  • 字段泛化:business_id 替代 order_id,fault_source 替代 province
  • 增加分类:fault_category 显式区分故障类别
  • 增强错误信息:error_message、stack_trace 支持内部错误

表结构(v2.0 泛化版)

CREATE TABLE diagnosis_record (
    -- 主键
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    diagnosis_id VARCHAR(64) UNIQUE NOT NULL COMMENT '诊断唯一ID(UUID)',
    
    -- 关联信息
    session_id VARCHAR(64) COMMENT '会话ID(关联Redis)',
    business_id VARCHAR(128) COMMENT '业务标识(订单号/请求ID/线程ID/任务ID...)',
    trace_id VARCHAR(64) COMMENT '链路追踪ID',
    
    -- 故障分类(泛化设计)
    fault_category VARCHAR(32) COMMENT '故障类别(EXTERNAL_API/INTERNAL_ERROR/DATABASE/CACHE/NETWORK/THREAD/MEMORY/CONFIG)',
    fault_source VARCHAR(128) COMMENT '故障源(省份/服务名/类名/数据库实例/Redis集群...)',
    fault_target VARCHAR(256) COMMENT '故障目标(接口URL/方法名/SQL语句/缓存键...)',
    
    -- 错误信息(通用)
    error_code VARCHAR(64) COMMENT '错误码(业务错误码/HTTP状态码/异常类名/数据库错误码)',
    error_message TEXT COMMENT '错误消息',
    stack_trace TEXT COMMENT '堆栈信息(内部错误时记录)',
    
    -- 诊断结果
    problem_type VARCHAR(32) COMMENT '问题类型(参数/网络/权限/逻辑/空指针/死锁/缓存穿透/线程池满...)',
    root_cause TEXT COMMENT '根因分析',
    solution TEXT COMMENT '修复方案',
    report_markdown TEXT COMMENT '完整诊断报告(Markdown格式)',
    
    -- 评估指标
    status VARCHAR(16) DEFAULT 'PENDING' COMMENT '诊断状态(PENDING/RUNNING/SUCCESS/FAILED)',
    confidence INT COMMENT '诊断置信度(0-100)',
    duration INT COMMENT '诊断耗时(毫秒)',
    
    -- 调试字段(可选)
    tool_calls JSON COMMENT '工具调用记录',
    
    -- 元数据
    created_by VARCHAR(64) COMMENT '创建人',
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    
    -- 索引
    INDEX idx_business_id (business_id),
    INDEX idx_trace_id (trace_id),
    INDEX idx_session_id (session_id),
    INDEX idx_fault_category (fault_category),
    INDEX idx_fault_source_target (fault_source, fault_target(100)),
    INDEX idx_error_code (error_code),
    INDEX idx_created_at (created_at),
    INDEX idx_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='诊断记录表(v2.0 泛化版)';

字段说明(泛化后)

字段 类型 必填 说明
diagnosis_id VARCHAR(64) 是 诊断唯一标识(UUID)
session_id VARCHAR(64) 否 会话ID,关联Redis会话上下文
business_id VARCHAR(128) 否 泛化:业务标识(订单号/请求ID/线程ID/任务ID),根据故障类型灵活填写
trace_id VARCHAR(64) 否 链路追踪ID,用于串联日志
fault_category VARCHAR(32) 否 新增:故障类别(EXTERNAL_API/INTERNAL_ERROR/DATABASE/CACHE/NETWORK/THREAD/MEMORY/CONFIG)
fault_source VARCHAR(128) 否 泛化:故障源(省份/服务名/类名/数据库实例),根据故障类别填写
fault_target VARCHAR(256) 否 泛化:故障目标(接口URL/方法名/SQL语句/缓存键),描述具体位置
error_code VARCHAR(64) 否 扩展:错误码(业务错误码/HTTP状态码/异常类名/数据库错误码)
error_message TEXT 否 新增:错误消息,通用描述
stack_trace TEXT 否 新增:堆栈信息,内部错误时记录
problem_type VARCHAR(32) 否 问题类型(参数/网络/权限/逻辑/空指针/死锁/缓存穿透/线程池满...)
root_cause TEXT 否 根因分析
solution TEXT 否 修复方案
report_markdown TEXT 否 完整诊断报告(Markdown格式)
status VARCHAR(16) 是 诊断状态(PENDING/RUNNING/SUCCESS/FAILED)
confidence INT 否 置信度(0-100)
duration INT 否 诊断耗时(毫秒)
tool_calls JSON 否 工具调用记录

核心设计决策

1. 字段泛化:支持多种故障类型

泛化前 → 泛化后:

order_id (订单号)              → business_id (业务标识)
province (省份)                → fault_source (故障源)
api_name (接口名称)            → 移除(信息合并到 fault_target)
api_url (接口URL)              → fault_target (故障目标)
error_code (业务错误码)        → error_code (通用错误码,扩展支持)
新增 fault_category (故障类别)
新增 error_message (错误消息)
新增 stack_trace (堆栈信息)

设计理由:

  • 原设计假设"故障 = 外部接口调用失败",但实际故障类型更多样
  • 泛化后支持:外部接口故障、内部异常、数据库问题、缓存问题、线程问题等
  • 字段语义更通用,根据故障类型灵活填写

2. fault_category 枚举值

故障类别分类:
├─ EXTERNAL_API:外部接口调用失败(第三方API、政府接口)
├─ INTERNAL_ERROR:系统内部错误(空指针、NPE、业务异常)
├─ DATABASE:数据库问题(死锁、慢查询、连接池耗尽)
├─ CACHE:缓存问题(穿透、雪崩、击穿)
├─ NETWORK:网络问题(超时、连接失败、DNS解析失败)
├─ THREAD:线程问题(线程池满、死锁)
├─ MEMORY:内存问题(OOM、内存泄漏)
└─ CONFIG:配置问题(配置错误、配置缺失)

用途:
- 统计不同类别故障的分布
- 路由不同的诊断策略(不同类别使用不同工具)
- 支持按类别过滤查询

3. 字段映射示例

示例1:外部接口故障(原场景)

business_id: "202406150001" (订单号)
fault_category: "EXTERNAL_API"
fault_source: "广东" (省份)
fault_target: "/api/v1/guangdong/social-security" (接口URL)
error_code: "40003" (业务错误码)
error_message: "参数缺失:idCard"
stack_trace: NULL (无堆栈)

示例2:空指针异常(内部错误)

business_id: "req-xyz789" (请求ID)
fault_category: "INTERNAL_ERROR"
fault_source: "order-service" (服务名)
fault_target: "OrderController.createOrder()" (方法名)
error_code: "NullPointerException" (异常类名)
error_message: "Cannot invoke 'User.getName()' because 'user' is null"
stack_trace: "java.lang.NullPointerException: ...\n at OrderController.java:45\n ..." (完整堆栈)

示例3:数据库死锁

business_id: "txn-20240615-001" (事务ID)
fault_category: "DATABASE"
fault_source: "mysql-master-01" (数据库实例)
fault_target: "UPDATE orders SET status=? WHERE order_id=?" (SQL)
error_code: "1213" (MySQL死锁错误码)
error_message: "Deadlock found when trying to get lock"
stack_trace: NULL (数据库错误无堆栈)

示例4:缓存穿透

business_id: "cache-key-user:99999" (缓存键)
fault_category: "CACHE"
fault_source: "redis-cluster" (Redis集群)
fault_target: "user:99999" (缓存键)
error_code: "CACHE_MISS" (自定义)
error_message: "恶意查询不存在的用户ID,导致缓存穿透"
stack_trace: NULL

示例5:线程池耗尽

business_id: "pool-async-executor" (线程池名称)
fault_category: "THREAD"
fault_source: "order-service" (服务名)
fault_target: "asyncExecutor ThreadPool" (线程池)
error_code: "RejectedExecutionException" (异常类名)
error_message: "Task rejected from ThreadPoolExecutor"
stack_trace: "java.util.concurrent.RejectedExecutionException: ...\n at ThreadPoolExecutor.java:2063\n ..."

4. 向下兼容策略

如果已有数据使用旧字段(order_id、province、api_url),可以通过以下方式迁移:

-- 数据迁移脚本
UPDATE diagnosis_record SET
  business_id = order_id,
  fault_category = 'EXTERNAL_API',
  fault_source = province,
  fault_target = api_url,
  error_message = CONCAT('错误码: ', error_code)
WHERE fault_category IS NULL;

应用层可以同时支持新旧字段:

读取时:优先使用新字段,兼容旧字段
写入时:只写新字段

5. 一次诊断 = 一条记录

特点:
- 用户发起一次诊断任务,创建一条记录
- 不是聊天记录(不存多轮对话)
- 追问对话存在 conversation_history 表(可选)

示例:
用户:"诊断订单 202406150001"
→ 创建 diagnosis_record(status=RUNNING)
→ Agent 执行
→ 更新 diagnosis_record(status=SUCCESS)
→ 返回报告

6. report_markdown 字段的必要性

为什么要存储完整报告?
- 固化结果:Prompt变化不影响历史报告
- 快速展示:不需要重新生成
- 历史审计:可以看到当时的诊断结果

成本:
- 字段较大(TEXT类型)
- 有一定冗余

结论:存储,因为报告是最终产物

7. session_id 的作用

用途:
- 关联 Redis 会话(支持追问)
- 同一会话可能有多次诊断
- 用于会话级别的数据分析

场景:
用户:"诊断订单 A"(session_id=sess-001, diagnosis_id=diag-001)
用户:"再诊断订单 B"(session_id=sess-001, diagnosis_id=diag-002)
→ 同一会话,两次诊断

典型查询场景

1. 查询历史诊断

-- 按业务标识查询(兼容订单号、请求ID等)
SELECT * FROM diagnosis_record 
WHERE business_id = '202406150001' 
ORDER BY created_at DESC;

-- 按链路ID查询
SELECT * FROM diagnosis_record 
WHERE trace_id = 'trace-abc-123' 
ORDER BY created_at DESC;

2. 按故障类别统计

-- 统计最近7天各类别故障分布
SELECT 
  fault_category,
  COUNT(*) as count,
  ROUND(AVG(duration), 2) as avg_duration_ms,
  ROUND(AVG(confidence), 2) as avg_confidence
FROM diagnosis_record
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)
GROUP BY fault_category
ORDER BY count DESC;

-- 结果示例:
-- +------------------+-------+-----------------+----------------+
-- | fault_category   | count | avg_duration_ms | avg_confidence |
-- +------------------+-------+-----------------+----------------+
-- | EXTERNAL_API     |   120 |          5234.5 |          85.3  |
-- | INTERNAL_ERROR   |    45 |          3456.2 |          90.1  |
-- | DATABASE         |    20 |          4567.8 |          88.5  |
-- | CACHE            |    15 |          2345.1 |          82.0  |
-- | THREAD           |     5 |          6789.3 |          87.2  |
-- +------------------+-------+-----------------+----------------+

3. 内部错误Top异常统计

-- 统计内部错误中最频繁的异常
SELECT 
  error_code,
  fault_target,
  COUNT(*) as count,
  AVG(duration) as avg_duration
FROM diagnosis_record
WHERE fault_category = 'INTERNAL_ERROR'
  AND created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY error_code, fault_target
ORDER BY count DESC
LIMIT 10;

-- 结果示例:
-- +---------------------------+--------------------------------+-------+--------------+
-- | error_code                | fault_target                   | count | avg_duration |
-- +---------------------------+--------------------------------+-------+--------------+
-- | NullPointerException      | OrderController.createOrder()  |    15 |       3245.2 |
-- | IllegalArgumentException  | UserService.validateUser()     |    10 |       2567.8 |
-- | NullPointerException      | PaymentService.processPayment()|     8 |       4123.5 |
-- +---------------------------+--------------------------------+-------+--------------+

4. 外部接口故障统计(按省份)

-- 统计外部接口故障(按省份)
SELECT 
  fault_source as province,
  COUNT(*) as count
FROM diagnosis_record
WHERE fault_category = 'EXTERNAL_API'
  AND created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY fault_source
ORDER BY count DESC;

-- 结果示例:
-- +----------+-------+
-- | province | count |
-- +----------+-------+
-- | 广东     |    45 |
-- | 江苏     |    32 |
-- | 浙江     |    28 |
-- +----------+-------+

5. 数据库问题分析

-- 统计数据库问题(按错误码)
SELECT 
  error_code,
  COUNT(*) as count,
  fault_target as example_sql
FROM diagnosis_record
WHERE fault_category = 'DATABASE'
  AND created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY error_code, fault_target
ORDER BY count DESC
LIMIT 5;

-- 结果示例:
-- +------------+-------+---------------------------------------------+
-- | error_code | count | example_sql                                 |
-- +------------+-------+---------------------------------------------+
-- | 1213       |    12 | UPDATE orders SET status=? WHERE order_id=? |
-- | 1205       |     8 | SELECT * FROM orders WHERE user_id=?        |
-- +------------+-------+---------------------------------------------+

6. 诊断成功率统计

-- 统计最近7天的诊断成功率
SELECT 
  COUNT(*) as total,
  SUM(CASE WHEN status = 'SUCCESS' THEN 1 ELSE 0 END) as success,
  ROUND(SUM(CASE WHEN status = 'SUCCESS' THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 2) as success_rate
FROM diagnosis_record
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY);

7. 性能监控(P50/P90/P95)

-- MySQL 8.0+ 使用 PERCENTILE_CONT
SELECT 
  'P50' as metric,
  PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY duration) as value_ms
FROM diagnosis_record
WHERE status = 'SUCCESS' 
  AND created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)
UNION ALL
SELECT 'P90', PERCENTILE_CONT(0.9) WITHIN GROUP (ORDER BY duration)
FROM diagnosis_record
WHERE status = 'SUCCESS' 
  AND created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)
UNION ALL
SELECT 'P95', PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY duration)
FROM diagnosis_record
WHERE status = 'SUCCESS' 
  AND created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY);

-- 或使用近似方式(兼容旧版本MySQL)
SELECT 
  'P50' as metric,
  duration as value_ms
FROM (
  SELECT duration, ROW_NUMBER() OVER (ORDER BY duration) as rn,
         COUNT(*) OVER() as total
  FROM diagnosis_record
  WHERE status = 'SUCCESS' 
    AND created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)
) t
WHERE rn = FLOOR(total * 0.5);

2.2 case_library(案例库表)

设计理念:知识沉淀,系统越用越智能

核心价值:

  • 质量过滤:只存储高质量案例(成功诊断 + 用户反馈有用)
  • 知识沉淀:历史诊断经验可复用
  • 提升准确率:相似问题提供历史参考
  • 加速诊断:快速推荐相似案例

MVP版本设计原则:

  • ✅ 能用:满足基本案例推荐功能
  • ✅ 简单:字段不多,逻辑清晰
  • ✅ 可扩展:后续可增加字段
  • ❌ 不做(Phase 2):复杂评分、版本管理、标签分类

表结构(MVP版)

CREATE TABLE case_library (
    -- 主键
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    case_id VARCHAR(64) UNIQUE NOT NULL COMMENT '案例唯一ID(UUID)',
    
    -- 来源关联
    diagnosis_id VARCHAR(64) COMMENT '关联诊断记录(可选,人工录入时为空)',
    source_type VARCHAR(16) DEFAULT 'AUTO' COMMENT '来源类型(AUTO:自动生成/MANUAL:人工录入)',
    
    -- 案例分类(复用 diagnosis_record 的分类字段)
    fault_category VARCHAR(32) COMMENT '故障类别(EXTERNAL_API/INTERNAL_ERROR/DATABASE/CACHE/NETWORK/THREAD/MEMORY/CONFIG)',
    fault_source VARCHAR(128) COMMENT '故障源(省份/服务名/类名/数据库实例...)',
    error_code VARCHAR(64) COMMENT '错误码(业务错误码/HTTP状态码/异常类名)',
    
    -- 案例内容
    title VARCHAR(256) NOT NULL COMMENT '案例标题(简短描述,如"广东社保查询idCard字段缺失")',
    root_cause TEXT NOT NULL COMMENT '根因分析',
    solution TEXT NOT NULL COMMENT '解决方案',
    
    -- 简单统计
    reference_count INT DEFAULT 0 COMMENT '引用次数(被推荐的次数,用于排序)',
    
    -- 元数据
    created_by VARCHAR(64) COMMENT '创建人',
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    
    -- 索引
    INDEX idx_fault_category (fault_category),
    INDEX idx_error_code (error_code),
    INDEX idx_fault_source (fault_source),
    INDEX idx_diagnosis_id (diagnosis_id),
    INDEX idx_reference_count (reference_count),
    INDEX idx_created_at (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='案例库表(MVP版)';

字段说明

字段 类型 必填 说明
case_id VARCHAR(64) 是 案例唯一标识(UUID)
diagnosis_id VARCHAR(64) 否 关联诊断记录,追溯案例来源(人工录入时为空)
source_type VARCHAR(16) 是 来源类型:AUTO(自动生成)/MANUAL(人工录入)
fault_category VARCHAR(32) 否 故障类别,与 diagnosis_record 一致
fault_source VARCHAR(128) 否 故障源,按省份/服务检索
error_code VARCHAR(64) 否 错误码,精确匹配检索
title VARCHAR(256) 是 案例标题,快速浏览
root_cause TEXT 是 根因分析,核心内容
solution TEXT 是 解决方案,核心内容
reference_count INT 是 引用次数,用于简单排序(引用多的排前面)

核心设计决策

1. 案例来源

来源1:自动生成(source_type=AUTO)
├─ 触发条件:诊断成功 + 用户反馈"有用"
├─ 关联诊断:diagnosis_id 不为空
└─ 质量保证:用户验证过

来源2:人工录入(source_type=MANUAL)
├─ 运维团队总结的经典案例
├─ diagnosis_id 为空
└─ 质量最高

注意:诊断失败或用户反馈"无用"的不自动生成案例

2. 简化的评分机制(MVP)

MVP版本:只按 reference_count 排序
- 引用次数多的排前面
- 简单有效

Phase 2 可增强:
- 增加 useful_count(用户反馈有用次数)
- 增加 score(综合评分:引用加分 + 有用率加分 - 时间衰减)
- 增加 is_featured(人工标记的经典案例)

3. 不做版本管理(MVP)

当前:直接更新案例
UPDATE case_library 
SET root_cause = '修正后的根因', 
    solution = '修正后的方案'
WHERE case_id = 'xxx';

优点:简单,保持单一案例
缺点:历史版本丢失

Phase 2 如需版本管理:
- 方案A:增加 version 字段
- 方案B:建 case_history 表

4. 与 diagnosis_record 的关系

关系:一对一(可选)
- 一次诊断 → 可以生成一个案例
- 通过 diagnosis_id 关联
- diagnosis_id 可为空(人工录入案例)

流程:
diagnosis_record(成功)
    ↓
用户反馈"有用"
    ↓
自动生成 case_library
    ↓
后续可人工修正、合并相似案例

数据流

场景1:自动生成案例

诊断完成 + 用户反馈"有用"
    ↓
INSERT INTO case_library
- diagnosis_id: diag-001
- source_type: AUTO
- fault_category: INTERNAL_ERROR
- error_code: NullPointerException
- title: "订单服务创建订单空指针异常"
- root_cause: "OrderController.createOrder()方法中user对象为null,未做空判断"
- solution: "在第45行添加空判断:if (user == null) throw new BizException(...)"
- reference_count: 0

场景2:人工录入案例

运维团队总结经验
    ↓
INSERT INTO case_library
- diagnosis_id: NULL
- source_type: MANUAL
- fault_category: EXTERNAL_API
- error_code: 40003
- title: "广东省社保接口参数缺失通用处理"
- root_cause: "前端表单未做必填校验,导致请求报文缺少关键参数"
- solution: "前端增加必填校验;后端返回明确的字段缺失提示"
- reference_count: 0

场景3:推荐案例并更新引用次数

诊断时查询相似案例
    ↓
SELECT * FROM case_library
WHERE error_code = '40003'
  AND fault_category = 'EXTERNAL_API'
ORDER BY reference_count DESC
LIMIT 3;
    ↓
返回 Top 3 案例
    ↓
UPDATE case_library 
SET reference_count = reference_count + 1
WHERE case_id IN ('case-001', 'case-005', 'case-012');

典型查询场景

1. 精确匹配查询(优先)

-- 按错误码查询
SELECT * FROM case_library
WHERE error_code = '40003'
ORDER BY reference_count DESC
LIMIT 5;

-- 按故障类别 + 错误码查询
SELECT * FROM case_library
WHERE fault_category = 'INTERNAL_ERROR'
  AND error_code = 'NullPointerException'
ORDER BY reference_count DESC
LIMIT 5;

-- 按故障源查询(如省份、服务名)
SELECT * FROM case_library
WHERE fault_source = '广东'
  AND fault_category = 'EXTERNAL_API'
ORDER BY reference_count DESC
LIMIT 5;

2. 统计分析

-- 统计案例分布
SELECT 
  fault_category,
  COUNT(*) as count,
  AVG(reference_count) as avg_reference
FROM case_library
GROUP BY fault_category
ORDER BY count DESC;

-- Top 引用案例
SELECT title, reference_count, created_at
FROM case_library
ORDER BY reference_count DESC
LIMIT 10;

-- 人工录入的案例
SELECT * FROM case_library
WHERE source_type = 'MANUAL'
ORDER BY created_at DESC;

3. 案例查重(避免重复)

-- 检查是否已有相同错误码的案例
SELECT * FROM case_library
WHERE error_code = '40003'
  AND fault_source = '广东'
  AND fault_category = 'EXTERNAL_API';

数据示例

-- 外部接口故障案例
INSERT INTO case_library VALUES
(1, 'case-001', 'diag-001', 'AUTO', 'EXTERNAL_API', '广东', '40003', 
 '广东社保查询idCard字段缺失', 
 '请求报文中未传入idCard字段,导致参数校验失败', 
 '前端表单增加idCard必填校验;后端增加参数校验提示', 
 15, 'system', NOW(), NOW());

-- 内部错误案例
INSERT INTO case_library VALUES
(2, 'case-002', 'diag-045', 'AUTO', 'INTERNAL_ERROR', 'order-service', 'NullPointerException',
 '订单服务创建订单空指针异常',
 'OrderController.createOrder()方法中user对象为null,未做空判断',
 '在第45行添加空判断:if (user == null) throw new BizException("用户信息不存在")',
 8, 'system', NOW(), NOW());

-- 人工录入的经典案例
INSERT INTO case_library VALUES
(3, 'case-003', NULL, 'MANUAL', 'DATABASE', 'mysql-master-01', '1213',
 '订单库存更新死锁通用处理',
 '两个事务互相等待对方释放锁:事务A持有订单锁等待库存锁,事务B持有库存锁等待订单锁',
 '调整事务加锁顺序:统一先锁订单,再锁库存;或使用乐观锁方案',
 3, 'admin', NOW(), NOW());

-- 缓存问题案例
INSERT INTO case_library VALUES
(4, 'case-004', 'diag-078', 'AUTO', 'CACHE', 'redis-cluster', 'CACHE_MISS',
 '用户信息缓存穿透',
 '恶意查询不存在的用户ID,缓存未命中,每次都打到数据库',
 '使用布隆过滤器拦截无效查询;或缓存空结果(TTL 5分钟)',
 5, 'system', NOW(), NOW());

与 Milvus 向量库的配合

案例检索策略(混合检索):

1. 精确匹配(MySQL)
   - 按 error_code 精确查询
   - 按 fault_category + fault_source 组合查询
   - 优点:快速、准确
   - 缺点:只能匹配相同错误码

2. 语义检索(Milvus)
   - 将案例内容(title + root_cause + solution)向量化
   - 存储到 Milvus 的 case_library_collection
   - 按语义相似度查询
   - 优点:能找到相似但不同错误码的案例
   - 缺点:召回可能不精确

3. 混合策略(推荐)
   Step 1: 先精确匹配(MySQL)
   Step 2: 如果结果 < 3 个,补充语义检索(Milvus)
   Step 3: 合并去重,按 reference_count 排序
   Step 4: 返回 Top 5

Milvus Collection 设计:
{
  "collection_name": "case_library_collection",
  "fields": [
    {"name": "case_id", "type": "VARCHAR"},
    {"name": "embedding", "type": "FLOAT_VECTOR", "dim": 1536},
    {"name": "reference_count", "type": "INT32"}
  ],
  "metric_type": "COSINE"
}

MVP 版本的简化

Phase 1(当前):
✅ 基础字段和表结构
✅ 自动生成案例(诊断成功 + 用户反馈)
✅ 人工录入案例
✅ 按 reference_count 简单排序
✅ 精确匹配查询

Phase 2(未来增强):
❌ 复杂评分机制(useful_count + score)
❌ 版本管理(case_history 表)
❌ 标签分类(tags 字段)
❌ 案例合并功能(多个相似案例 → 1个综合案例)
❌ 人工标注(is_featured 字段)
❌ 时间衰减(score 计算中考虑时间因素)

2.4 api_document(文档元数据表)

设计理念:文档管理,不是文档检索

核心定位:

  • MySQL 负责文档元数据管理(状态、版本、去重)
  • Milvus 负责文档内容存储和检索
  • 通过 doc_id 关联两者

MVP版本原则:

  • ✅ 最简字段,满足基本管理需求
  • ✅ 文件去重(基于 file_hash)
  • ✅ 状态追踪(索引进度)
  • ✅ 硬删除(同步删除 Milvus 数据)
  • ❌ 暂不支持:软删除、启用开关、版本管理(Phase 2)

表结构(MVP版)

CREATE TABLE api_document (
    -- 主键
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    doc_id VARCHAR(64) UNIQUE NOT NULL COMMENT '文档唯一ID(UUID),关联Milvus',
    
    -- 文档分类
    fault_category VARCHAR(32) DEFAULT 'EXTERNAL_API' COMMENT '文档类别(EXTERNAL_API/INTERNAL_ERROR...)',
    fault_source VARCHAR(128) COMMENT '文档归属(省份/服务名,如"广东"/"order-service")',
    api_name VARCHAR(128) COMMENT '接口名称(如"社保查询")',
    version VARCHAR(32) DEFAULT 'v1.0' COMMENT '文档版本',
    
    -- 文件信息
    file_name VARCHAR(256) NOT NULL COMMENT '原始文件名',
    file_path VARCHAR(512) COMMENT '文件存储路径',
    file_hash VARCHAR(64) COMMENT '文件MD5 hash(用于去重)',
    file_size BIGINT COMMENT '文件大小(字节)',
    
    -- 索引状态
    status VARCHAR(16) DEFAULT 'PENDING' COMMENT '索引状态(PENDING/PROCESSING/INDEXED/FAILED)',
    chunk_count INT DEFAULT 0 COMMENT '分块数量',
    error_message TEXT COMMENT '失败原因',
    
    -- 时间字段
    indexed_at DATETIME COMMENT '索引完成时间',
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    
    -- 索引
    UNIQUE INDEX uk_file_hash (file_hash),
    INDEX idx_doc_id (doc_id),
    INDEX idx_fault_source (fault_source),
    INDEX idx_status (status),
    INDEX idx_created_at (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文档元数据表(MVP版)';

字段说明

字段 类型 必填 说明
doc_id VARCHAR(64) 是 核心:文档唯一ID,关联 Milvus 中的所有分块
fault_category VARCHAR(32) 否 文档类别,与 diagnosis_record 一致
fault_source VARCHAR(128) 否 文档归属(省份/服务名)
api_name VARCHAR(128) 否 接口名称
version VARCHAR(32) 否 文档版本
file_name VARCHAR(256) 是 原始文件名
file_path VARCHAR(512) 否 文件存储路径
file_hash VARCHAR(64) 否 去重关键:文件MD5,唯一约束
file_size BIGINT 否 文件大小
status VARCHAR(16) 是 状态追踪:PENDING/PROCESSING/INDEXED/FAILED
chunk_count INT 否 分块数量
error_message TEXT 否 失败原因
indexed_at DATETIME 否 索引完成时间

核心设计决策

1. doc_id:MySQL 与 Milvus 的桥梁

作用:
- MySQL:通过 doc_id 管理文档元数据
- Milvus:每个 chunk 的 metadata 中携带 doc_id

关联关系:
api_document (MySQL)
    doc_id: doc-001
    ↓ 1:N
Milvus chunks
    chunk_1: {doc_id: 'doc-001', text: '...', vector: [...]}
    chunk_2: {doc_id: 'doc-001', text: '...', vector: [...]}

管理操作:
- 删除文档:
  DELETE FROM milvus_collection WHERE metadata["doc_id"] == 'doc-001';
  DELETE FROM api_document WHERE doc_id = 'doc-001';
  
- 重新索引:
  先删除旧数据,再重新导入

2. file_hash:文件去重

去重流程:
1. 用户上传文件
   ↓
2. 计算文件 MD5
   file_hash = md5(file_content)
   ↓
3. 检查是否已存在
   SELECT * FROM api_document WHERE file_hash = 'abc123...';
   ↓
4a. 如果存在 → 提示"文档已存在"
4b. 如果不存在 → 继续导入

唯一约束:
UNIQUE INDEX uk_file_hash (file_hash)

3. status:状态追踪

状态流转:
PENDING (待处理)
    ↓
PROCESSING (处理中)
    ↓ 成功
INDEXED (已索引)

    ↓ 失败
FAILED (失败)

用途:
- 批量导入时监控进度
- 失败重试
- 统计索引成功率

4. 硬删除策略(MVP)

删除文档时:
1. 删除 Milvus 中的所有分块
   DELETE FROM milvus_collection WHERE metadata["doc_id"] == 'xxx';
   
2. 删除 MySQL 元数据
   DELETE FROM api_document WHERE doc_id = 'xxx';
   
3. 可选:删除原始文件
   Files.delete(file_path);

特点:
- 简单直接
- 数据彻底删除
- 不可恢复(需谨慎)

Phase 2 可增强:
- 软删除(archived_at)
- 启用开关(enabled)

数据流

场景1:导入新文档

1. 用户上传文件
   file: "广东社保查询v2.1.docx"
   ↓
2. 计算 hash
   file_hash = md5(file)
   ↓
3. 检查去重(MySQL)
   SELECT * FROM api_document WHERE file_hash = 'abc123';
   → 不存在
   ↓
4. 插入元数据(MySQL)
   INSERT INTO api_document VALUES (
     NULL, 'doc-001', 'EXTERNAL_API', '广东', '社保查询', 'v2.1',
     '广东社保查询v2.1.docx', '/docs/guangdong/social-v2.1.docx',
     'abc123...', 1048576,
     'PROCESSING', 0, NULL, NULL, NOW(), NOW()
   );
   ↓
5. 后台任务处理
   - 解析 → Markdown
   - 分块(15个chunk)
   - 向量化
   - 存入 Milvus(每个chunk的metadata中携带doc_id='doc-001')
   ↓
6. 更新状态(MySQL)
   UPDATE api_document 
   SET status = 'INDEXED', 
       chunk_count = 15,
       indexed_at = NOW()
   WHERE doc_id = 'doc-001';

场景2:删除文档

1. 用户删除文档
   doc_id = 'doc-001'
   ↓
2. 删除 Milvus 中的所有分块
   DELETE FROM milvus_collection 
   WHERE metadata["doc_id"] == 'doc-001';
   ↓
3. 删除 MySQL 元数据
   DELETE FROM api_document WHERE doc_id = 'doc-001';
   ↓
4. 可选:删除原始文件
   rm /docs/guangdong/social-v2.1.docx

场景3:重新索引文档

1. 文档内容更新,需要重新索引
   doc_id = 'doc-001'
   ↓
2. 删除旧数据
   - Milvus: DELETE WHERE metadata["doc_id"] == 'doc-001'
   - MySQL: DELETE FROM api_document WHERE doc_id = 'doc-001'
   ↓
3. 重新导入(同场景1)

典型查询

1. 查看文档列表

-- 按省份查询
SELECT doc_id, file_name, version, status, chunk_count, indexed_at
FROM api_document
WHERE fault_source = '广东'
  AND status = 'INDEXED'
ORDER BY indexed_at DESC;

-- 查询失败的文档
SELECT doc_id, file_name, error_message
FROM api_document
WHERE status = 'FAILED';

2. 文档去重检查

-- 导入前检查
SELECT doc_id, file_name 
FROM api_document 
WHERE file_hash = 'abc123...';

3. 统计分析

-- 统计各状态文档数量
SELECT status, COUNT(*) as count
FROM api_document
GROUP BY status;

-- 统计各省份文档数量
SELECT fault_source, COUNT(*) as count
FROM api_document
WHERE status = 'INDEXED'
GROUP BY fault_source
ORDER BY count DESC;

与 Milvus 的协作

Milvus Collection Schema

{
  "collection_name": "api_doc_collection",
  "fields": [
    {"name": "id", "type": "VARCHAR", "max_length": 64, "is_primary": true},
    {"name": "content", "type": "VARCHAR", "max_length": 2000},
    {"name": "vector", "type": "FLOAT_VECTOR", "dim": 1536},
    {"name": "metadata", "type": "JSON"}
  ]
}

# metadata 结构
{
  "doc_id": "doc-001",           # 关联 MySQL
  "_source": "/path/to/file",
  "_file_name": "xxx.docx",
  "chunkIndex": 0,
  "totalChunks": 15,
  "fault_category": "EXTERNAL_API",
  "fault_source": "广东",
  "error_code": "40003"
}

Java 代码示例

// 插入时携带 doc_id
Map<String, Object> metadata = new HashMap<>();
metadata.put("doc_id", docId);  // 关联 MySQL
metadata.put("_source", filePath);
metadata.put("chunkIndex", chunkIndex);
metadata.put("fault_source", province);

// 删除文档的所有分块
String expr = String.format("metadata[\"doc_id\"] == \"%s\"", docId);
DeleteParam deleteParam = DeleteParam.newBuilder()
    .withCollectionName(COLLECTION_NAME)
    .withExpr(expr)
    .build();
milvusClient.delete(deleteParam);

数据示例

-- 外部接口文档
INSERT INTO api_document VALUES
(1, 'doc-001', 'EXTERNAL_API', '广东', '社保查询', 'v2.1',
 '广东社保查询v2.1.docx', '/docs/guangdong/social-v2.1.docx',
 'abc123...', 1048576,
 'INDEXED', 15, NULL, '2024-06-15 10:30:00', NOW(), NOW());

-- 内部服务文档
INSERT INTO api_document VALUES
(2, 'doc-002', 'INTERNAL_ERROR', 'order-service', '订单服务API', 'v1.0',
 '订单服务API文档.pdf', '/docs/internal/order-service-api.pdf',
 'def456...', 2097152,
 'INDEXED', 20, NULL, '2024-06-14 15:20:00', NOW(), NOW());

-- 处理失败的文档
INSERT INTO api_document VALUES
(3, 'doc-003', 'EXTERNAL_API', '江苏', '公积金查询', 'v1.5',
 '江苏公积金查询.html', '/docs/jiangsu/fund-v1.5.html',
 'ghi789...', 512000,
 'FAILED', 0, '不支持HTML格式,请转换为Word或PDF', NULL, NOW(), NOW());

MVP 版本的简化

Phase 1(当前):
✅ 基础字段和表结构
✅ 文件去重(file_hash)
✅ 状态追踪(status)
✅ 硬删除(彻底删除)
✅ 通过 doc_id 关联 Milvus

Phase 2(未来增强):
❌ enabled(启用开关)
❌ archived_at(软删除)
❌ batch_id(批次管理)
❌ status 细化(PARSING/SPLITTING/INDEXING...)
❌ tags(标签分类)
❌ is_latest(版本标记)

三、会话管理设计

表结构

CREATE TABLE conversation_history (
    -- 主键
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    conversation_id VARCHAR(64) NOT NULL COMMENT '对话ID(UUID)',
    
    -- 关联信息
    session_id VARCHAR(64) NOT NULL COMMENT '会话ID',
    diagnosis_id VARCHAR(64) COMMENT '关联诊断ID(追问时可为空)',
    
    -- 对话内容
    round_number INT NOT NULL COMMENT '对话轮次(1, 2, 3...)',
    role VARCHAR(16) NOT NULL COMMENT '角色(user/assistant)',
    content TEXT NOT NULL COMMENT '对话内容',
    
    -- 调试字段(可选)
    tool_calls JSON COMMENT '工具调用记录',
    
    -- 元数据
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    
    -- 索引
    INDEX idx_session_id (session_id),
    INDEX idx_diagnosis_id (diagnosis_id),
    INDEX idx_created_at (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='对话历史表(可选,用于分析)';

字段说明

字段 类型 必填 说明
conversation_id VARCHAR(64) 是 对话唯一标识
session_id VARCHAR(64) 是 会话ID,关联多轮对话
diagnosis_id VARCHAR(64) 否 关联诊断ID,初次诊断时填写,追问时为空
round_number INT 是 对话轮次,从1开始递增
role VARCHAR(16) 是 角色:user(用户)/assistant(AI)
content TEXT 是 对话内容

核心设计决策

1. 用途定位

主要用途:
- BadCase分析(用户追问什么?)
- 功能优化(哪些问题常被追问?)
- 审计追溯(完整对话记录)

不是:
- 主要业务表(诊断记录才是)
- 实时查询(对话上下文在Redis)

结论:辅助表,Phase 2 再加

2. diagnosis_id 可为空

场景1:初次诊断
- diagnosis_id: diag-001
- round 1: user → "诊断订单 A"
- round 2: assistant → "完整报告..."

场景2:追问(不创建新诊断)
- diagnosis_id: NULL
- round 3: user → "为什么会缺失字段?"
- round 4: assistant → "因为前端表单未校验..."

场景3:新诊断
- diagnosis_id: diag-002
- round 5: user → "诊断订单 B"
- round 6: assistant → "完整报告..."

典型查询场景

1. 查询会话的所有对话

-- 按轮次排序
SELECT * FROM conversation_history
WHERE session_id = 'sess-abc'
ORDER BY round_number;

2. 查询某次诊断的对话

-- 包括诊断前后的追问
SELECT * FROM conversation_history
WHERE diagnosis_id = 'diag-001'
   OR (session_id IN (
       SELECT session_id FROM conversation_history WHERE diagnosis_id = 'diag-001'
   ))
ORDER BY round_number;

3. 统计追问频率

-- 统计有多少诊断被追问
SELECT 
  COUNT(DISTINCT diagnosis_id) as total_diagnosis,
  COUNT(DISTINCT CASE WHEN round_number > 2 THEN diagnosis_id END) as with_followup,
  ROUND(COUNT(DISTINCT CASE WHEN round_number > 2 THEN diagnosis_id END) * 100.0 / COUNT(DISTINCT diagnosis_id), 2) as followup_rate
FROM conversation_history
WHERE diagnosis_id IS NOT NULL;

三、会话管理设计

3.1 会话存储策略

Redis(主)

数据结构:
key: session:{session_id}
value: {
  "sessionId": "sess-abc",
  "userId": "user-123",
  "currentDiagnosisId": "diag-001",
  "messages": [
    {"role": "user", "content": "诊断订单 A"},
    {"role": "assistant", "content": "完整报告..."}
  ],
  "context": {
    "province": "广东",
    "apiName": "社保查询",
    "errorCode": "40003"
  },
  "createdAt": "2024-06-15T14:30:00Z",
  "lastActiveAt": "2024-06-15T14:35:00Z"
}
ttl: 1800秒(30分钟)

优势:
- 快速读写
- 自动过期
- 支持追问(保存上下文)

MySQL(辅助,可选)

同步策略:
1. 重要会话同步
   - 有用户反馈的会话
   - 诊断失败的会话(BadCase)
   - 多轮对话 > 3 轮的会话

2. 同步时机
   - 会话结束时(30分钟过期)
   - 用户反馈时(实时)
   - 定时任务(每小时,可选)

3. 同步目标
   - conversation_history 表
   - 用于长期分析和审计

3.2 数据流设计

场景1:单次诊断(主流 80%)

1. 用户发起诊断
   POST /api/diagnosis/start
   {
     "orderId": "202406150001"
   }

2. 创建会话(Redis)
   key: session:sess-abc
   ttl: 1800秒

3. 创建诊断记录(MySQL)
   INSERT INTO diagnosis_record
   - diagnosis_id: diag-001
   - session_id: sess-abc
   - status: RUNNING

4. Agent 执行诊断
   - 调用工具(queryOrder, queryLogs, searchDoc...)
   - 生成报告

5. 更新诊断记录(MySQL)
   UPDATE diagnosis_record
   - status: SUCCESS
   - root_cause: "idCard字段缺失"
   - report_markdown: "完整报告..."

6. 返回报告
   → 大部分用户到此结束

场景2:追问(少数 20%)

1. 用户追问
   POST /api/chat
   {
     "sessionId": "sess-abc",
     "message": "为什么会缺失字段?"
   }

2. 从 Redis 获取上下文
   GET session:sess-abc
   - 有之前的诊断结果
   - 有对话历史

3. Agent 基于上下文回答
   - 不创建新的 diagnosis_record
   - 只是普通对话

4. 更新 Redis 会话
   - 追加对话历史
   - 刷新 TTL(重新计时30分钟)

5. 可选:保存到 conversation_history(MySQL)
   - 如果需要长期分析
   - 异步存储

场景3:同一会话多次诊断

1. 用户第一次诊断
   "诊断订单 A"
   → diagnosis_record(diag-001, session_id=sess-abc)

2. 用户第二次诊断
   "再诊断订单 B"
   → diagnosis_record(diag-002, session_id=sess-abc)

3. 会话关联
   - 同一个 session_id
   - 两条 diagnosis_record
   - Redis 中保存完整对话历史

四、实施规划

4.1 Phase 1:核心功能(第1周)

实现内容:

✅ diagnosis_record 表
✅ Redis 会话管理
✅ 单次诊断流程

不实现:
❌ conversation_history 表(先不加)
❌ 会话同步(先不做)
❌ 追问功能(先不支持)

验收标准:

- 用户输入订单号 → 返回诊断报告
- 诊断记录持久化到 MySQL
- 可以查询历史诊断
- 可以统计诊断成功率

4.2 Phase 2:追问功能(第2周)

实现内容:

✅ 支持多轮对话(基于 Redis 上下文)
✅ conversation_history 表(可选)
✅ 会话上下文管理

验收标准:

- 用户可以追问细节
- Agent 能基于上下文回答
- 追问不创建新的诊断记录

4.3 Phase 3:优化分析(第3周)

实现内容:

✅ 会话同步(Redis → MySQL)
✅ BadCase 分析
✅ 追问频率统计

验收标准:

- 重要会话自动同步到 MySQL
- 可以分析用户追问模式
- 可以优化 Prompt 和功能

五、关键设计决策总结

5.1 单次诊断 vs 多轮对话

决策:主要是单次诊断,辅助支持追问

理由:

  • 系统定位是"自动化诊断",不是聊天机器人
  • 大部分用户需求:输入 → 报告 → 结束
  • 追问是少数场景,不应主导设计

实现:

  • diagnosis_record 只记录诊断任务
  • conversation_history 记录追问对话(可选)

5.2 会话存储:Redis vs MySQL

决策:Redis 主存储,MySQL 辅助备份

理由:

  • 会话是临时数据,30分钟过期
  • Redis 读写快,适合实时交互
  • MySQL 用于长期分析,不是主路径

实现:

  • Redis 存所有会话(自动过期)
  • MySQL 只存重要会话(按需同步)

5.3 report_markdown 是否存储

决策:存储完整报告

理由:

  • 报告是最终产物,需要固化
  • Prompt 可能变化,历史报告不应变
  • 查询历史时直接展示,不重新生成

成本:

  • TEXT 字段较大
  • 适度冗余可接受

5.4 conversation_history 是否必需

决策:Phase 2 再加,不是必需

理由:

  • 核心功能不依赖对话历史
  • 主要用于分析和优化
  • 可以后期扩展

六、数据量预估

6.1 diagnosis_record

场景:中型企业运维团队
- 日均诊断:100 次
- 月均诊断:3000 次
- 年均诊断:36000 次

存储预估:
- 单条记录:约 5KB(含报告)
- 年存储量:36000 × 5KB = 180MB
- 三年存储:540MB

结论:数据量不大,可以全量保留

6.2 conversation_history

场景:20% 用户会追问
- 日均追问:20 次
- 平均追问轮次:3 轮
- 日均对话记录:20 × 3 × 2(user+assistant)= 120 条

存储预估:
- 单条记录:约 1KB
- 年存储量:120 × 365 × 1KB = 44MB

结论:数据量很小,可以全量保留

七、索引设计说明

7.1 diagnosis_record 索引

-- 业务查询索引
INDEX idx_business_id (business_id)     -- 按业务标识查询(订单号/请求ID/线程ID)
INDEX idx_trace_id (trace_id)           -- 按链路ID查询
INDEX idx_session_id (session_id)       -- 按会话查询

-- 故障分类索引
INDEX idx_fault_category (fault_category)                    -- 按故障类别过滤
INDEX idx_fault_source_target (fault_source, fault_target(100))  -- 按故障源+目标统计
INDEX idx_error_code (error_code)                            -- 按错误码统计

-- 通用索引
INDEX idx_created_at (created_at)       -- 时间范围查询
INDEX idx_status (status)               -- 状态过滤

7.2 索引使用场景

-- 场景1:查询业务标识历史(使用 idx_business_id)
SELECT * FROM diagnosis_record 
WHERE business_id = '202406150001';

-- 场景2:统计故障类别(使用 idx_fault_category)
SELECT fault_category, COUNT(*) 
FROM diagnosis_record 
WHERE fault_category = 'INTERNAL_ERROR' 
GROUP BY fault_category;

-- 场景3:统计特定服务的异常(使用 idx_fault_source_target)
SELECT fault_target, COUNT(*) 
FROM diagnosis_record 
WHERE fault_source = 'order-service' 
  AND fault_category = 'INTERNAL_ERROR'
GROUP BY fault_target;

-- 场景4:时间范围统计(使用 idx_created_at)
SELECT DATE(created_at) as date, COUNT(*) 
FROM diagnosis_record 
WHERE created_at >= '2024-06-01' 
GROUP BY DATE(created_at);

八、数据安全考虑

8.1 敏感数据处理

敏感字段:
- 请求报文中的手机号、身份证
- 响应报文中的个人信息

脱敏策略:
- 存储时脱敏(在 tool_calls JSON 中)
- 手机号:138****5678
- 身份证:110101********1234

实现:
- 报文查询工具自动脱敏
- 存入数据库前已脱敏
- 降低泄露风险

8.2 数据保留策略

diagnosis_record:
- 保留周期:3 年
- 清理策略:定时任务(每月)
- 归档:超过 3 年的数据导出后删除

conversation_history:
- 保留周期:1 年
- 清理策略:定时任务(每月)
- 可选:关联诊断被删除时级联删除

九、扩展性考虑

9.1 预留扩展字段

-- diagnosis_record 可选扩展字段
├─ tags VARCHAR(256)              -- 标签(用于分类)
├─ severity VARCHAR(16)           -- 严重级别(LOW/MEDIUM/HIGH/CRITICAL)
├─ affected_users INT             -- 影响用户数
├─ resolved_at DATETIME           -- 解决时间
└─ resolver VARCHAR(64)           -- 解决人

-- 添加方式(不影响现有功能)
ALTER TABLE diagnosis_record ADD COLUMN tags VARCHAR(256);

9.2 分表策略(未来)

场景:数据量达到千万级别

方案1:按时间分表
- diagnosis_record_2024_06
- diagnosis_record_2024_07
- ...

方案2:按省份分表
- diagnosis_record_guangdong
- diagnosis_record_jiangsu
- ...

当前:不分表,单表够用(年均 36000 条)

十、变更日志

版本 日期 变更内容 变更人
v1.0 2024-06-15 初版,定义核心表结构 -
v1.1 2024-06-15 添加会话管理设计 -
v1.2 2024-06-15 补充实施规划和数据流 -
v2.0 2024-06-15 重大更新:字段泛化,支持多种故障类型(外部接口+内部错误) -

v2.0 主要变更

字段泛化:

  • order_id → business_id(订单号→业务标识)
  • province → fault_source(省份→故障源)
  • api_name → 移除(信息合并到fault_target)
  • api_url → fault_target(接口URL→故障目标)
  • error_code:扩展支持(业务错误码→通用错误码)

新增字段:

  • fault_category:故障类别(EXTERNAL_API/INTERNAL_ERROR/DATABASE/CACHE/NETWORK/THREAD/MEMORY/CONFIG)
  • error_message:错误消息(通用描述)
  • stack_trace:堆栈信息(内部错误专用)

设计理念:

  • 从"只支持外部接口故障"扩展到"支持所有故障类型"
  • 字段语义更通用,根据故障类型灵活填写
  • 保持向下兼容,可通过数据迁移支持旧数据

影响范围:

  • SQL建表语句
  • 索引设计
  • 查询示例
  • 数据示例

文档维护说明:

  • 本文档随系统演进持续更新
  • 任何表结构变更需同步更新此文档
  • 重大设计调整需记录决策理由