From d691007c86cd7882640e475a455608bf9e72a9ae Mon Sep 17 00:00:00 2001 From: Jeason <1710884619@qq.com> Date: Thu, 2 Apr 2026 10:04:39 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=B7=A5=E5=8D=95=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=E8=AF=AD=E4=B9=89=E7=BB=9F=E4=B8=80=20+=20AI?= =?UTF-8?q?=E5=BB=BA=E8=AE=AE=E4=BF=AE=E5=A4=8D=20+=20=E7=A7=9F=E6=88=B7?= =?UTF-8?q?=E7=AD=9B=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 统一字段语义:title=问题标题,description=问题详细描述,resolution=解决方案 2. 修复详情页展示:问题描述取 description 而非 title 3. 修复编辑页标签:description 标注为'问题详细描述'而非'处理过程' 4. 统一分类选项:创建和编辑使用相同的分类列表(技术问题/APP功能/远程控制/车辆绑定/系统故障/OTA升级/其他) 5. 修复 AI 建议生成:用 title+description 搜索知识库,prompt 中明确区分标题和描述 6. 修复入库逻辑:question 使用 title+description 完整内容,入库时带上工单的 tenant_id 7. 工单列表新增租户筛选器,API 支持 tenant_id 过滤 8. 工单列表和详情 API 返回 tenant_id 字段 9. 租户选择器同时填充对话和工单筛选下拉框 --- src/web/blueprints/workorders.py | 21 +++++++++++++--- src/web/static/js/dashboard.js | 42 +++++++++++++++++++------------- src/web/templates/dashboard.html | 21 ++++++++++------ 3 files changed, 56 insertions(+), 28 deletions(-) diff --git a/src/web/blueprints/workorders.py b/src/web/blueprints/workorders.py index 30ca154..93ed8ad 100644 --- a/src/web/blueprints/workorders.py +++ b/src/web/blueprints/workorders.py @@ -84,6 +84,7 @@ def get_workorders(): per_page = request.args.get('per_page', 10, type=int) status_filter = request.args.get('status', '') priority_filter = request.args.get('priority', '') + tenant_id = request.args.get('tenant_id', '') # 从数据库获取分页数据 from src.core.database import db_manager @@ -98,6 +99,8 @@ def get_workorders(): query = query.filter(WorkOrder.status == status_filter) if priority_filter: query = query.filter(WorkOrder.priority == priority_filter) + if tenant_id: + query = query.filter(WorkOrder.tenant_id == tenant_id) # 按创建时间倒序排列 query = query.order_by(WorkOrder.created_at.desc()) @@ -114,6 +117,7 @@ def get_workorders(): workorders_data.append({ 'id': workorder.id, 'order_id': workorder.order_id, + 'tenant_id': workorder.tenant_id, 'title': workorder.title, 'description': workorder.description, 'category': workorder.category, @@ -184,6 +188,7 @@ def get_workorder_details(workorder_id): workorder = { "id": w.id, "order_id": w.order_id, + "tenant_id": w.tenant_id, "title": w.title, "description": w.description, "category": w.category, @@ -315,15 +320,15 @@ def generate_workorder_ai_suggestion(workorder_id): if not w: return jsonify({"error": "工单不存在"}), 404 # 调用知识库搜索与LLM生成 - # 使用问题描述(title)而不是处理过程(description)作为主要查询依据 - query = f"{w.title}" + # 使用 title + description 作为查询依据,获取更精准的知识库匹配 + query = f"{w.title} {w.description or ''}" kb_results = service_manager.get_assistant().search_knowledge(query, top_k=3) kb_list = kb_results.get('results', []) if isinstance(kb_results, dict) else [] # 组装提示词 context = "\n".join([f"Q: {k.get('question','')}\nA: {k.get('answer','')}" for k in kb_list]) from src.core.llm_client import QwenClient llm = QwenClient() - prompt = f"请基于以下工单问题描述与知识库片段,给出简洁、可执行的处理建议。\n\n问题描述:\n{w.title}\n\n处理过程(仅供参考):\n{w.description}\n\n知识库片段:\n{context}\n\n请直接输出建议文本:" + prompt = f"请基于以下工单问题与知识库片段,给出简洁、可执行的处理建议。\n\n问题标题:\n{w.title}\n\n问题详细描述:\n{w.description or '无'}\n\n知识库片段:\n{context}\n\n请直接输出建议文本:" llm_resp = llm.chat_completion(messages=[{"role":"user","content":prompt}], temperature=0.3, max_tokens=800) suggestion = "" if llm_resp and 'choices' in llm_resp: @@ -432,10 +437,18 @@ def approve_workorder_to_knowledge(workorder_id): return jsonify({"error": "未找到可入库的内容"}), 400 # 入库为知识条目 + # question 使用标题+描述,确保知识条目的问题完整 + question_text = w.title or '' + if w.description: + question_text = f"{w.title}\n{w.description}" if w.title else w.description + if not question_text: + question_text = '工单问题' + entry = KnowledgeEntry( - question=w.title or (w.description[:20] if w.description else '工单问题'), + question=question_text, answer=answer_content, category=w.category or '其他', + tenant_id=w.tenant_id, confidence_score=confidence_score, is_active=True, is_verified=True, diff --git a/src/web/static/js/dashboard.js b/src/web/static/js/dashboard.js index 4d8d4c4..03f9bd0 100644 --- a/src/web/static/js/dashboard.js +++ b/src/web/static/js/dashboard.js @@ -386,13 +386,23 @@ class TSPDashboard { const tenants = await response.json(); if (!Array.isArray(tenants)) return; - const selectors = document.querySelectorAll('#chat-tenant-id'); - selectors.forEach(select => { - const currentVal = select.value; - select.innerHTML = tenants.map(t => + // 填充对话租户选择器 + const chatSelect = document.getElementById('chat-tenant-id'); + if (chatSelect) { + const currentVal = chatSelect.value; + chatSelect.innerHTML = tenants.map(t => `` ).join(''); - }); + } + + // 填充工单租户筛选器 + const woFilter = document.getElementById('workorder-tenant-filter'); + if (woFilter) { + const currentVal = woFilter.value; + woFilter.innerHTML = '' + tenants.map(t => + `` + ).join(''); + } } catch (e) { console.warn('加载租户列表失败:', e); } @@ -2800,6 +2810,7 @@ class TSPDashboard { try { const statusFilter = document.getElementById('workorder-status-filter')?.value || 'all'; const priorityFilter = document.getElementById('workorder-priority-filter')?.value || 'all'; + const tenantFilter = document.getElementById('workorder-tenant-filter')?.value || 'all'; let url = '/api/workorders'; const params = new URLSearchParams(); @@ -2808,6 +2819,7 @@ class TSPDashboard { params.append('per_page', pageSize.toString()); if (statusFilter !== 'all') params.append('status', statusFilter); if (priorityFilter !== 'all') params.append('priority', priorityFilter); + if (tenantFilter !== 'all') params.append('tenant_id', tenantFilter); // 添加强制刷新参数 if (forceRefresh) { @@ -2872,7 +2884,7 @@ class TSPDashboard {
${workorder.description ? workorder.description.substring(0, 100) + (workorder.description.length > 100 ? '...' : '') : '无处理过程'}
+${workorder.description ? workorder.description.substring(0, 100) + (workorder.description.length > 100 ? '...' : '') : '无问题描述'}