# 数据库设计文档 ## 一、设计原则 ### 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 泛化版) ```sql 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),可以通过以下方式迁移: ```sql -- 数据迁移脚本 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. 查询历史诊断** ```sql -- 按业务标识查询(兼容订单号、请求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. 按故障类别统计** ```sql -- 统计最近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异常统计** ```sql -- 统计内部错误中最频繁的异常 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. 外部接口故障统计(按省份)** ```sql -- 统计外部接口故障(按省份) 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. 数据库问题分析** ```sql -- 统计数据库问题(按错误码) 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. 诊断成功率统计** ```sql -- 统计最近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)** ```sql -- 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版) ```sql 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. 精确匹配查询(优先)** ```sql -- 按错误码查询 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. 统计分析** ```sql -- 统计案例分布 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. 案例查重(避免重复)** ```sql -- 检查是否已有相同错误码的案例 SELECT * FROM case_library WHERE error_code = '40003' AND fault_source = '广东' AND fault_category = 'EXTERNAL_API'; ``` #### 数据示例 ```sql -- 外部接口故障案例 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版) ```sql 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. 查看文档列表** ```sql -- 按省份查询 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. 文档去重检查** ```sql -- 导入前检查 SELECT doc_id, file_name FROM api_document WHERE file_hash = 'abc123...'; ``` **3. 统计分析** ```sql -- 统计各状态文档数量 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** ```python { "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 代码示例** ```java // 插入时携带 doc_id Map 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); ``` --- #### 数据示例 ```sql -- 外部接口文档 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(版本标记) ``` --- ## 三、会话管理设计 #### 表结构 ```sql 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. 查询会话的所有对话** ```sql -- 按轮次排序 SELECT * FROM conversation_history WHERE session_id = 'sess-abc' ORDER BY round_number; ``` **2. 查询某次诊断的对话** ```sql -- 包括诊断前后的追问 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. 统计追问频率** ```sql -- 统计有多少诊断被追问 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 索引 ```sql -- 业务查询索引 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 索引使用场景 ```sql -- 场景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 预留扩展字段 ```sql -- 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建表语句 - 索引设计 - 查询示例 - 数据示例 --- **文档维护说明**: - 本文档随系统演进持续更新 - 任何表结构变更需同步更新此文档 - 重大设计调整需记录决策理由