# -*- coding: utf-8 -*- """ 对话管理蓝图 处理对话相关的API路由 """ from flask import Blueprint, request, jsonify from src.core.database import db_manager from src.core.models import Conversation from src.core.query_optimizer import query_optimizer from datetime import timedelta conversations_bp = Blueprint('conversations', __name__, url_prefix='/api/conversations') @conversations_bp.route('') def get_conversations(): """获取对话历史列表(分页)- 优化版""" try: page = request.args.get('page', 1, type=int) per_page = request.args.get('per_page', 10, type=int) search = request.args.get('search', '') user_id = request.args.get('user_id', '') date_filter = request.args.get('date_filter', '') # 使用优化后的查询 result = query_optimizer.get_conversations_paginated( page=page, per_page=per_page, search=search, user_id=user_id, date_filter=date_filter ) # 规范化:移除不存在的user_id字段,避免前端误用 for conv in result.get('conversations', []): if 'user_id' in conv and conv['user_id'] is None: conv.pop('user_id', None) return jsonify(result) except Exception as e: return jsonify({"error": str(e)}), 500 @conversations_bp.route('/') def get_conversation_detail(conversation_id): """获取对话详情""" try: with db_manager.get_session() as session: conv = session.query(Conversation).filter(Conversation.id == conversation_id).first() if not conv: return jsonify({"error": "对话不存在"}), 404 # Conversation模型没有user_id字段,这里用占位或由外层推断 return jsonify({ 'success': True, 'id': conv.id, 'user_id': None, 'user_message': conv.user_message, 'assistant_response': conv.assistant_response, 'timestamp': conv.timestamp.isoformat() if conv.timestamp else None, 'response_time': conv.response_time, 'work_order_id': conv.work_order_id }) except Exception as e: return jsonify({"error": str(e)}), 500 @conversations_bp.route('/', methods=['DELETE']) def delete_conversation(conversation_id): """删除对话记录""" try: with db_manager.get_session() as session: conv = session.query(Conversation).filter(Conversation.id == conversation_id).first() if not conv: return jsonify({"error": "对话不存在"}), 404 session.delete(conv) session.commit() # 清除对话历史相关缓存 from src.core.cache_manager import cache_manager cache_manager.clear() # 清除所有缓存 return jsonify({"success": True, "message": "对话记录已删除"}) except Exception as e: return jsonify({"error": str(e)}), 500 @conversations_bp.route('/clear', methods=['DELETE']) def clear_all_conversations(): """清空所有对话历史""" try: with db_manager.get_session() as session: session.query(Conversation).delete() session.commit() # 清除对话历史相关缓存 from src.core.cache_manager import cache_manager cache_manager.clear() # 清除所有缓存 return jsonify({"success": True, "message": "对话历史已清空"}) except Exception as e: return jsonify({"error": str(e)}), 500 @conversations_bp.route('/migrate-merge', methods=['POST']) def migrate_merge_conversations(): """一次性迁移:将历史上拆分存储的用户/助手两条记录合并为一条 规则: - 只处理一端为空的记录(user_only 或 assistant_only) - 优先将 user_only 与其后最近的 assistant_only 合并(同工单且5分钟内) - 若当前为 assistant_only 且前一条是 user_only 也合并到前一条 - 合并后删除被吸收的那条记录 - 可重复执行(幂等):已合并的不再满足“一端为空”的条件 """ try: merged_pairs = 0 deleted_rows = 0 time_threshold_seconds = 300 to_delete_ids = [] with db_manager.get_session() as session: conversations = session.query(Conversation).order_by(Conversation.timestamp.asc(), Conversation.id.asc()).all() total = len(conversations) i = 0 def is_empty(text: str) -> bool: return (text is None) or (str(text).strip() == '') while i < total: c = conversations[i] user_only = (not is_empty(c.user_message)) and is_empty(c.assistant_response) assistant_only = (not is_empty(c.assistant_response)) and is_empty(c.user_message) if user_only: # 向后寻找匹配的assistant_only j = i + 1 while j < total: n = conversations[j] # 跳过已经标记删除的 if n.id in to_delete_ids: j += 1 continue # 超过阈值不再尝试 if c.timestamp and n.timestamp and (n.timestamp - c.timestamp).total_seconds() > time_threshold_seconds: break # 同工单或两者都为空均可 same_wo = (c.work_order_id == n.work_order_id) or (c.work_order_id is None and n.work_order_id is None) if same_wo and (not is_empty(n.assistant_response)) and is_empty(n.user_message): # 合并 c.assistant_response = n.assistant_response if c.response_time is None and c.timestamp and n.timestamp: try: c.response_time = max(0.0, (n.timestamp - c.timestamp).total_seconds() * 1000.0) except Exception: pass # 继承辅助信息 if (not c.confidence_score) and n.confidence_score is not None: c.confidence_score = n.confidence_score if (not c.knowledge_used) and n.knowledge_used: c.knowledge_used = n.knowledge_used session.add(c) to_delete_ids.append(n.id) merged_pairs += 1 break j += 1 elif assistant_only: # 向前与最近的 user_only 合并(如果尚未被其他合并吸收) j = i - 1 while j >= 0: p = conversations[j] if p.id in to_delete_ids: j -= 1 continue if p.timestamp and c.timestamp and (c.timestamp - p.timestamp).total_seconds() > time_threshold_seconds: break same_wo = (c.work_order_id == p.work_order_id) or (c.work_order_id is None and p.work_order_id is None) if same_wo and (not is_empty(p.user_message)) and is_empty(p.assistant_response): p.assistant_response = c.assistant_response if p.response_time is None and p.timestamp and c.timestamp: try: p.response_time = max(0.0, (c.timestamp - p.timestamp).total_seconds() * 1000.0) except Exception: pass if (not p.confidence_score) and c.confidence_score is not None: p.confidence_score = c.confidence_score if (not p.knowledge_used) and c.knowledge_used: p.knowledge_used = c.knowledge_used session.add(p) to_delete_ids.append(c.id) merged_pairs += 1 break j -= 1 i += 1 if to_delete_ids: deleted_rows = session.query(Conversation).filter(Conversation.id.in_(to_delete_ids)).delete(synchronize_session=False) session.commit() return jsonify({ 'success': True, 'merged_pairs': merged_pairs, 'deleted_rows': deleted_rows }) except Exception as e: return jsonify({"error": str(e)}), 500