youfool-devops-gd-jdk21/frontend-api-documentation.md

35 KiB
Raw Blame History

运维管理系统前端开发API文档

📖 文档概述

本文档为前端开发团队提供完整的API使用指南包含运维工单管理和AI智能回答功能的所有接口说明、数据结构定义和开发建议。

🌐 基础信息

服务器配置

认证机制

  • 认证方式: JWT Token + Apache Shiro
  • Token有效期: 10小时后端Token
  • 加密方式: RSA加密 + SM3哈希

请求/响应格式

// 统一请求格式
{
  "headers": {
    "Content-Type": "application/json",
    "Authorization": "Bearer <JWT_TOKEN>"
  }
}

// 统一响应格式
{
  "code": 200,           // 状态码: 200=成功
  "message": "操作成功", // 消息说明
  "data": {},            // 数据载荷
  "timestamp": 1692000000 // 时间戳
}

// 错误响应格式
{
  "code": 500,
  "message": "系统内部错误: 具体错误信息",
  "data": null
}

🏗️ 核心API模块

1. 用户认证模块 (UserController)

1.1 用户登录

POST /user/login
Content-Type: application/json

{
  "username": "用户名",
  "password": "加密后的密码"
}

Response:
{
  "code": 200,
  "data": {
    "token": "jwt_token_string",
    "userInfo": {
      "id": "用户ID",
      "username": "用户名", 
      "nickname": "昵称",
      "org": "所属组织",
      "role": "角色类型"
    }
  }
}

1.2 获取用户信息

GET /user/info
Authorization: Bearer <token>

Response:
{
  "code": 200,
  "data": {
    "id": "用户ID",
    "username": "用户名",
    "nickname": "昵称", 
    "org": "所属组织",
    "mobile": "手机号",
    "email": "邮箱"
  }
}

1.3 用户退出

POST /user/logout
Authorization: Bearer <token>

2. 工单管理模块 (RepairController)

2.1 创建工单

POST /repair
Authorization: Bearer <token>
Content-Type: application/json

{
  "title": "工单标题",
  "faultDescription": "故障描述",
  "business": "业务模块",
  "priority": 2,        // 1=紧急, 2=高, 3=中, 4=低
  "org": "所属组织",
  "source": "1",        // 来源: 1=PC, 2=微信, 3=APP
  "contactPhone": "联系电话"
}

Response:
{
  "code": 200,
  "data": "YW202408140001"  // 工单ID
}

2.2 工单列表查询

POST /repair/list
Authorization: Bearer <token>
Content-Type: application/json

{
  "pageNum": 1,
  "pageSize": 20,
  "title": "搜索关键词",
  "business": "业务模块",
  "priority": 2,
  "dateStart": "2024-08-01",
  "dateEnd": "2024-08-14"
}

Response:
{
  "code": 200,
  "data": {
    "records": [
      {
        "repairId": "YW202408140001",
        "title": "工单标题",
        "faultDescription": "故障描述",
        "business": "业务模块", 
        "priority": 2,
        "statusName": "状态名称",
        "createTime": "2024-08-14T10:30:00",
        "username": "提交人",
        "nickname": "提交人昵称"
      }
    ],
    "total": 100,
    "size": 20,
    "current": 1
  }
}

2.3 工单详情

GET /repair/detail?repairId=YW202408140001
Authorization: Bearer <token>

Response:
{
  "code": 200,
  "data": {
    "repairId": "YW202408140001",
    "title": "工单标题",
    "faultDescription": "故障描述",
    "business": "业务模块",
    "priority": 2,
    "createTime": "2024-08-14T10:30:00",
    "handles": [
      {
        "step": "submit",
        "stepName": "提交",
        "result": "工单已提交",
        "happenTime": "2024-08-14T10:30:00",
        "username": "操作人"
      }
    ],
    "files": [
      {
        "fileName": "截图.png",
        "fileUrl": "/upload/files/xxx.png"
      }
    ]
  }
}

2.4 工单处理

POST /repair/handle/next
Authorization: Bearer <token>
Content-Type: application/json

{
  "repairId": "YW202408140001",
  "result": "处理结果",
  "nextStep": "resolve",  // 下一步状态
  "assignTo": "分配给谁"  // 可选
}

3. AI智能回答模块 (流式输出,已实现)

3.1 流式生成AI回答 (SSE)

GET /api/ai/answer/stream/{repairId}
Authorization: Bearer <token>
Accept: text/event-stream

Stream Response (Server-Sent Events):
event: progress
data: {"stage": "analyzing", "message": "正在分析工单内容...", "progress": 20}

event: progress  
data: {"stage": "searching", "message": "搜索相似案例...", "progress": 40}

event: progress
data: {"stage": "generating", "message": "生成AI回答...", "progress": 60}

event: chunk
data: {"text": "## 问题分析\n根据您描述的", "isComplete": false}

event: chunk
data: {"text": "网络连接问题,可能的原因包括:", "isComplete": false}

event: complete
data: {
  "answerId": "answer_20241016001",
  "repairId": "YW202408140001",
  "fullAnswer": "## 问题分析\n根据您描述的网络连接问题...",
  "confidenceScore": 0.85,
  "generateTime": "2024-08-14T10:35:00",
  "processingTimeMs": 3250,
  "usedMcpTools": "knowledge_search,similar_cases",
  "isComplete": true
}

3.2 非流式生成AI回答

POST /api/ai/answer/generate
Authorization: Bearer <token>
Content-Type: application/json

{
  "repairId": "YW202408140001",
  "streaming": false,
  "includeHistory": true
}

Response:
{
  "code": 200,
  "data": {
    "answerId": "answer_20241016001",
    "repairId": "YW202408140001", 
    "answer": "## 问题分析\n根据您描述的网络连接问题...",
    "confidenceScore": 0.85,
    "generateTime": "2024-08-14T10:35:00",
    "processingTimeMs": 1250,
    "usedMcpTools": "knowledge_search,similar_cases"
  }
}

3.3 流式聊天对话 (SSE)

POST /api/ai/chat/stream
Authorization: Bearer <token>
Content-Type: application/json
Accept: text/event-stream

{
  "message": "如何解决网络连接问题?",
  "sessionId": "chat_session_001",  // 可选,用于多轮对话
  "context": {
    "repairId": "YW202408140001",   // 可选,关联工单
    "previousMessages": []          // 可选,历史对话
  }
}

Stream Response:
event: start
data: {"sessionId": "chat_session_001", "timestamp": "2024-08-14T10:35:00"}

event: chunk
data: {"text": "根据您的描述,网络连接问题", "index": 0}

event: chunk  
data: {"text": "通常由以下几个方面引起:", "index": 1}

event: complete
data: {
  "sessionId": "chat_session_001",
  "fullResponse": "根据您的描述,网络连接问题通常由以下几个方面引起:...",
  "tokenCount": 245,
  "responseTime": 2100,
  "isComplete": true
}

3.4 中断流式生成

POST /api/ai/chat/stream/{sessionId}/stop
Authorization: Bearer <token>

Response:
{
  "code": 200,
  "data": {
    "sessionId": "chat_session_001",
    "status": "stopped",
    "partialResponse": "已生成的部分内容...",
    "stopTime": "2024-08-14T10:35:30"
  }
}

3.5 获取AI回答历史

GET /api/ai/answer/history/{repairId}
Authorization: Bearer <token>

Response:
{
  "code": 200,
  "data": [
    {
      "answerId": "answer_20241016001",
      "question": "用户提出的问题",
      "answer": "AI生成的回答",
      "confidenceScore": 0.85,
      "status": "generated",  // generated, accepted, rejected
      "generateTime": "2024-08-14T10:35:00",
      "isStreaming": true,
      "responseTime": 3250
    }
  ]
}

3.6 用户反馈AI回答

POST /api/ai/answer/feedback
Authorization: Bearer <token>
Content-Type: application/json

{
  "answerId": "answer_20241016001",
  "feedbackType": "accept",  // accept, reject, escalate
  "userRating": 4,           // 1-5分评价
  "userComment": "回答很有帮助",
  "improvement": "建议添加更详细的步骤"
}

4. 工单待办模块 (RepairTodoController)

4.1 我的待办工单

POST /repairTodo/list
Authorization: Bearer <token>
Content-Type: application/json

{
  "pageNum": 1,
  "pageSize": 20,
  "step": "declare",     // 可选: 按步骤筛选
  "urgent": true         // 可选: 只看紧急工单
}

Response:
{
  "code": 200,
  "data": {
    "records": [
      {
        "repairId": "YW202408140001",
        "title": "工单标题",
        "step": "declare",
        "stepName": "待分派",
        "priority": 2,
        "createTime": "2024-08-14T10:30:00",
        "deadline": "2024-08-15T18:00:00"
      }
    ],
    "total": 10
  }
}

5. 统计分析模块 (StatisticController)

5.1 工单统计概览

GET /statistic/overview
Authorization: Bearer <token>

Response:
{
  "code": 200,
  "data": {
    "totalRepairs": 1250,
    "pendingRepairs": 45,
    "resolvedToday": 28,
    "avgResolutionTime": 4.2,  // 小时
    "monthlyStats": [
      {
        "month": "2024-08",
        "submitted": 156,
        "resolved": 142,
        "resolutionRate": 91.0
      }
    ]
  }
}

🎨 前端页面开发指南

1. 工单列表页面

核心功能

  • 工单列表展示(支持分页、筛选、排序)
  • 工单状态标识(颜色区分优先级)
  • 快速操作按钮(查看详情、处理、分派)
  • 实时状态更新WebSocket推送

关键组件

<template>
  <div class="repair-list">
    <!-- 搜索筛选区 -->
    <div class="filter-section">
      <el-form inline>
        <el-form-item label="关键词">
          <el-input v-model="searchForm.title" placeholder="工单标题或ID" />
        </el-form-item>
        <el-form-item label="业务模块">
          <el-select v-model="searchForm.business">
            <el-option label="全部" value="" />
            <el-option label="财务系统" value="财务系统" />
          </el-select>
        </el-form-item>
        <el-form-item label="优先级">
          <el-select v-model="searchForm.priority">
            <el-option label="全部" value="" />
            <el-option label="紧急" :value="1" />
            <el-option label="高" :value="2" />
          </el-select>
        </el-form-item>
        <el-button type="primary" @click="searchRepairs">搜索</el-button>
      </el-form>
    </div>

    <!-- 工单列表 -->
    <div class="repair-table">
      <el-table :data="repairList" v-loading="loading">
        <el-table-column prop="repairId" label="工单ID" width="140" />
        <el-table-column prop="title" label="标题" min-width="200" />
        <el-table-column prop="business" label="业务模块" width="120" />
        <el-table-column prop="priority" label="优先级" width="80">
          <template #default="{ row }">
            <el-tag :type="getPriorityType(row.priority)">
              {{ getPriorityText(row.priority) }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="statusName" label="状态" width="100" />
        <el-table-column prop="createTime" label="创建时间" width="160" />
        <el-table-column label="操作" width="200" fixed="right">
          <template #default="{ row }">
            <el-button size="small" @click="viewDetail(row.repairId)">
              详情
            </el-button>
            <el-button size="small" type="primary" 
                       @click="handleRepair(row.repairId)"
                       v-if="canHandle(row)">
              处理
            </el-button>
            <!-- AI回答按钮 (AI功能启用后) -->
            <el-dropdown v-if="needsAIAnswer(row)" trigger="click">
              <el-button size="small" type="success">
                AI回答 <el-icon><ArrowDown /></el-icon>
              </el-button>
              <template #dropdown>
                <el-dropdown-menu>
                  <el-dropdown-item @click="generateAIAnswer(row.repairId, true)">
                    流式生成
                  </el-dropdown-item>
                  <el-dropdown-item @click="generateAIAnswer(row.repairId, false)">
                    标准生成
                  </el-dropdown-item>
                </el-dropdown-menu>
              </template>
            </el-dropdown>
          </template>
        </el-table-column>
      </el-table>
      
      <el-pagination 
        v-model:current-page="pagination.current"
        v-model:page-size="pagination.size"
        :total="pagination.total"
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange" />
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { repairAPI } from '@/api/repair'

const loading = ref(false)
const repairList = ref([])
const searchForm = ref({
  title: '',
  business: '',
  priority: ''
})
const pagination = ref({
  current: 1,
  size: 20,
  total: 0
})

// 搜索工单
const searchRepairs = async () => {
  loading.value = true
  try {
    const params = {
      pageNum: pagination.value.current,
      pageSize: pagination.value.size,
      ...searchForm.value
    }
    const response = await repairAPI.getRepairList(params)
    repairList.value = response.data.records
    pagination.value.total = response.data.total
  } catch (error) {
    ElMessage.error('获取工单列表失败')
  } finally {
    loading.value = false
  }
}

// 查看详情
const viewDetail = (repairId) => {
  router.push(`/repair/detail/${repairId}`)
}

// 生成AI回答 (支持流式和非流式)
const generateAIAnswer = async (repairId, streaming = true) => {
  try {
    if (streaming) {
      ElMessage.info('正在启动AI流式回答生成...')
      // 跳转到AI回答页面并启动流式生成
      router.push(`/repair/ai-answer/${repairId}?streaming=true`)
    } else {
      ElMessage.info('AI正在分析问题请稍候...')
      const response = await repairAPI.generateAIAnswer(repairId)
      ElMessage.success('AI回答生成成功')
      // 跳转到回答页面显示完整结果
      router.push(`/repair/ai-answer/${repairId}`)
    }
  } catch (error) {
    ElMessage.error('AI回答生成失败: ' + error.message)
  }
}

// 优先级标识
const getPriorityType = (priority) => {
  const types = { 1: 'danger', 2: 'warning', 3: 'info', 4: 'success' }
  return types[priority] || 'info'
}

const getPriorityText = (priority) => {
  const texts = { 1: '紧急', 2: '高', 3: '中', 4: '低' }
  return texts[priority] || '未知'
}

onMounted(() => {
  searchRepairs()
})
</script>

2. AI智能回答页面 (支持流式输出)

核心功能

  • 流式AI回答展示实时显示生成过程
  • 进度指示器(分析、搜索、生成阶段)
  • 用户反馈收集(满意度、评分、意见)
  • 相似案例推荐
  • 回答质量评估指标
  • 中断生成功能

设计建议

<template>
  <div class="ai-answer-page">
    <!-- 工单信息概览 -->
    <div class="repair-summary">
      <h3>{{ repairInfo.title }}</h3>
      <p>{{ repairInfo.faultDescription }}</p>
      <div class="meta-info">
        <span>业务模块: {{ repairInfo.business }}</span>
        <span>优先级: {{ getPriorityText(repairInfo.priority) }}</span>
      </div>
    </div>

    <!-- AI回答内容支持流式显示 -->
    <div class="ai-answer-content">
      <div class="answer-header">
        <h4>🤖 AI智能解答</h4>
        <div class="answer-controls">
          <div class="confidence-score" v-if="!isGenerating">
            <span>置信度: </span>
            <el-progress 
              :percentage="aiAnswer.confidenceScore * 100"
              :color="getConfidenceColor(aiAnswer.confidenceScore)" />
          </div>
          <el-button 
            v-if="isGenerating" 
            type="danger" 
            size="small"
            @click="stopGeneration">
            停止生成
          </el-button>
        </div>
      </div>
      
      <!-- 生成进度指示器 -->
      <div class="generation-progress" v-if="isGenerating">
        <el-progress 
          :percentage="generationProgress.progress"
          :status="generationProgress.status"
          striped
          striped-flow>
          <template #default="{ percentage }">
            <span class="progress-text">
              {{ generationProgress.message }} ({{ percentage }}%)
            </span>
          </template>
        </el-progress>
      </div>
      
      <div class="answer-body">
        <!-- 流式显示的AI回答内容 -->
        <div class="markdown-content streaming" v-if="isGenerating">
          <div class="typing-indicator">
            {{ streamingContent }}
            <span class="cursor">|</span>
          </div>
        </div>
        
        <!-- 完整的AI回答内容 -->
        <div class="markdown-content" v-else v-html="renderMarkdown(aiAnswer.answer)"></div>
      </div>
      
      <div class="answer-meta" v-if="!isGenerating">
        <span>生成时间: {{ formatTime(aiAnswer.generateTime) }}</span>
        <span>处理耗时: {{ aiAnswer.processingTimeMs }}ms</span>
        <span>使用工具: {{ aiAnswer.usedMcpTools }}</span>
        <span v-if="aiAnswer.isStreaming">流式生成: 是</span>
      </div>
    </div>

    <!-- 用户反馈区 -->
    <div class="feedback-section">
      <h4>📝 回答评价</h4>
      <div class="feedback-actions">
        <el-button type="success" @click="submitFeedback('accept')">
          👍 回答有帮助
        </el-button>
        <el-button type="warning" @click="submitFeedback('reject')">
          👎 回答无效
        </el-button>
        <el-button type="danger" @click="submitFeedback('escalate')">
          🆘 需要人工处理
        </el-button>
      </div>
      
      <!-- 详细反馈表单 -->
      <div class="detailed-feedback" v-if="showFeedbackForm">
        <el-form :model="feedbackForm">
          <el-form-item label="评分">
            <el-rate v-model="feedbackForm.userRating" />
          </el-form-item>
          <el-form-item label="意见">
            <el-input type="textarea" 
                      v-model="feedbackForm.userComment"
                      placeholder="请说明回答的有用程度或改进建议" />
          </el-form-item>
          <el-form-item>
            <el-button type="primary" @click="submitDetailedFeedback">
              提交反馈
            </el-button>
          </el-form-item>
        </el-form>
      </div>
    </div>

    <!-- 相似案例推荐 -->
    <div class="similar-cases" v-if="similarCases.length > 0">
      <h4>📚 相似案例参考</h4>
      <div class="cases-list">
        <div v-for="case in similarCases" :key="case.repairId" 
             class="case-item">
          <h5>{{ case.title }}</h5>
          <p>{{ case.solution }}</p>
          <div class="case-meta">
            <span>相似度: {{ (case.similarity * 100).toFixed(1) }}%</span>
            <span>解决时间: {{ case.resolveTime }}</span>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import { useRoute } from 'vue-router'
import { repairAPI, aiAPI } from '@/api'
import { marked } from 'marked'

const route = useRoute()
const repairId = route.params.repairId

const repairInfo = ref({})
const aiAnswer = ref({})
const similarCases = ref([])
const showFeedbackForm = ref(false)
const feedbackForm = ref({
  userRating: 5,
  userComment: ''
})

// 流式生成相关状态
const isGenerating = ref(false)
const streamingContent = ref('')
const generationProgress = ref({
  progress: 0,
  message: '准备开始...',
  status: 'active'
})
const eventSource = ref(null)
const sessionId = ref(null)

// 启动流式AI回答生成
const startStreamingGeneration = async () => {
  isGenerating.value = true
  streamingContent.value = ''
  generationProgress.value = { progress: 0, message: '连接AI服务...', status: 'active' }
  
  try {
    // 建立SSE连接
    eventSource.value = new EventSource(
      `/api/ai/answer/stream/${repairId}`,
      {
        headers: {
          'Authorization': `Bearer ${getToken()}`
        }
      }
    )
    
    eventSource.value.onmessage = (event) => {
      const data = JSON.parse(event.data)
      
      switch (event.type) {
        case 'progress':
          generationProgress.value = {
            progress: data.progress,
            message: data.message,
            status: 'active'
          }
          break
          
        case 'chunk':
          streamingContent.value += data.text
          break
          
        case 'complete':
          aiAnswer.value = data
          isGenerating.value = false
          eventSource.value.close()
          break
      }
    }
    
    eventSource.value.onerror = (error) => {
      console.error('流式生成出错:', error)
      isGenerating.value = false
      generationProgress.value.status = 'exception'
      ElMessage.error('AI回答生成失败')
    }
    
  } catch (error) {
    console.error('启动流式生成失败:', error)
    isGenerating.value = false
  }
}

// 停止流式生成
const stopGeneration = async () => {
  if (sessionId.value) {
    try {
      await aiAPI.stopGeneration(sessionId.value)
      ElMessage.info('已停止AI回答生成')
    } catch (error) {
      console.error('停止生成失败:', error)
    }
  }
  
  if (eventSource.value) {
    eventSource.value.close()
  }
  
  isGenerating.value = false
  generationProgress.value.status = 'warning'
  generationProgress.value.message = '已停止生成'
}

// 获取AI回答非流式
const getAIAnswer = async () => {
  try {
    const response = await aiAPI.getAIAnswer(repairId)
    aiAnswer.value = response.data
  } catch (error) {
    console.error('获取AI回答失败:', error)
  }
}

// Markdown渲染
const renderMarkdown = (content) => {
  return marked(content || '')
}

// 提交反馈
const submitFeedback = (type) => {
  if (type === 'accept') {
    // 直接标记为满意
    submitDetailedFeedback({
      feedbackType: 'accept',
      userRating: 5,
      userComment: '回答有帮助'
    })
  } else {
    // 显示详细反馈表单
    showFeedbackForm.value = true
  }
}

const submitDetailedFeedback = async (feedback = null) => {
  try {
    const params = feedback || {
      answerId: aiAnswer.value.answerId,
      feedbackType: 'detailed',
      ...feedbackForm.value
    }
    
    await aiAPI.submitFeedback(repairId, params)
    ElMessage.success('反馈提交成功')
    
    // 根据反馈类型处理后续逻辑
    if (params.feedbackType === 'accept') {
      // 工单可能自动关闭
      ElMessage.info('工单已标记为已解决')
    } else if (params.feedbackType === 'escalate') {
      ElMessage.info('工单已转人工处理')
    }
  } catch (error) {
    ElMessage.error('反馈提交失败')
  }
}

onMounted(() => {
  // 自动启动流式生成,或者加载已有回答
  if (route.query.streaming === 'true') {
    startStreamingGeneration()
  } else {
    getAIAnswer()
  }
})

// 组件卸载时清理资源
onUnmounted(() => {
  if (eventSource.value) {
    eventSource.value.close()
  }
})
</script>

🔧 开发建议

1. 状态管理

// Pinia Store for Repair Management
import { defineStore } from 'pinia'

export const useRepairStore = defineStore('repair', {
  state: () => ({
    repairList: [],
    currentRepair: null,
    aiAnswers: {},
    loading: false
  }),
  
  actions: {
    async fetchRepairList(params) {
      this.loading = true
      try {
        const response = await repairAPI.getRepairList(params)
        this.repairList = response.data.records
        return response.data
      } finally {
        this.loading = false
      }
    },
    
    async generateAIAnswer(repairId) {
      try {
        const response = await repairAPI.generateAIAnswer(repairId)
        this.aiAnswers[repairId] = response.data
        return response.data
      } catch (error) {
        throw error
      }
    }
  }
})

2. 错误处理

// API错误统一处理
import axios from 'axios'
import { ElMessage } from 'element-plus'

const apiClient = axios.create({
  baseURL: '/api',
  timeout: 30000
})

apiClient.interceptors.response.use(
  response => {
    if (response.data.code !== 200) {
      ElMessage.error(response.data.message)
      throw new Error(response.data.message)
    }
    return response.data
  },
  error => {
    if (error.response?.status === 401) {
      // 跳转到登录页
      router.push('/login')
    } else {
      ElMessage.error(error.message || '网络请求失败')
    }
    return Promise.reject(error)
  }
)

3. WebSocket实时通知

// WebSocket连接管理
class WebSocketManager {
  constructor() {
    this.ws = null
    this.reconnectCount = 0
  }
  
  connect(userId) {
    this.ws = new WebSocket(`ws://localhost:8080/websocket/${userId}`)
    
    this.ws.onmessage = (event) => {
      const notification = JSON.parse(event.data)
      this.handleNotification(notification)
    }
    
    this.ws.onclose = () => {
      // 自动重连逻辑
      if (this.reconnectCount < 5) {
        setTimeout(() => {
          this.connect(userId)
          this.reconnectCount++
        }, 5000)
      }
    }
  }
  
  handleNotification(notification) {
    switch (notification.type) {
      case 'ai_answer':
        ElMessage.success('AI助手已为您的问题提供了解决方案')
        // 更新页面状态
        break
      case 'repair_assigned':
        ElMessage.info('您有新的工单分配')
        break
    }
  }
}

🧪 AI功能测试Demo

测试页面实现

创建一个专门的测试页面用于验证AI功能的各个环节

<template>
  <div class="ai-test-demo">
    <el-card header="AI智能回答功能测试">
      <!-- 测试用例选择 -->
      <div class="test-scenarios">
        <h4>选择测试场景</h4>
        <el-radio-group v-model="selectedScenario">
          <el-radio label="network">网络连接问题</el-radio>
          <el-radio label="login">系统登录问题</el-radio>
          <el-radio label="printer">打印机故障</el-radio>
          <el-radio label="custom">自定义问题</el-radio>
        </el-radio-group>
      </div>

      <!-- 测试参数配置 -->
      <div class="test-config">
        <el-form :model="testForm" label-width="120px">
          <el-form-item label="工单标题">
            <el-input v-model="testForm.title" />
          </el-form-item>
          <el-form-item label="问题描述">
            <el-input type="textarea" v-model="testForm.description" rows="4" />
          </el-form-item>
          <el-form-item label="业务模块">
            <el-select v-model="testForm.business">
              <el-option label="办公网络" value="办公网络" />
              <el-option label="财务系统" value="财务系统" />
              <el-option label="设备维护" value="设备维护" />
            </el-select>
          </el-form-item>
        </el-form>
      </div>

      <!-- 测试执行按钮 -->
      <div class="test-actions">
        <el-button type="primary" @click="runAITest" :loading="testing">
          {{ testing ? 'AI分析中...' : '开始AI测试' }}
        </el-button>
        <el-button @click="resetTest">重置</el-button>
      </div>

      <!-- 测试结果展示 -->
      <div class="test-results" v-if="testResult">
        <el-divider>测试结果</el-divider>
        
        <!-- AI回答 -->
        <div class="ai-response">
          <h4>🤖 AI智能回答</h4>
          <div class="answer-content" v-html="testResult.answer"></div>
          <div class="answer-metrics">
            <el-descriptions :column="2" border>
              <el-descriptions-item label="置信度">
                <el-progress :percentage="testResult.confidence * 100" />
              </el-descriptions-item>
              <el-descriptions-item label="响应时间">
                {{ testResult.responseTime }}ms
              </el-descriptions-item>
              <el-descriptions-item label="相似案例">
                {{ testResult.similarCases }}
              </el-descriptions-item>
              <el-descriptions-item label="使用工具">
                {{ testResult.usedTools }}
              </el-descriptions-item>
            </el-descriptions>
          </div>
        </div>

        <!-- 测试反馈 -->
        <div class="test-feedback">
          <h4>📝 测试反馈</h4>
          <el-button-group>
            <el-button type="success" @click="submitTestFeedback('good')">
              👍 回答质量好
            </el-button>
            <el-button type="warning" @click="submitTestFeedback('average')">
              👌 回答一般
            </el-button>
            <el-button type="danger" @click="submitTestFeedback('poor')">
              👎 回答质量差
            </el-button>
          </el-button-group>
        </div>
      </div>
    </el-card>
  </div>
</template>

<script setup>
import { ref, watch } from 'vue'
import { ElMessage } from 'element-plus'

const testing = ref(false)
const selectedScenario = ref('network')
const testForm = ref({
  title: '',
  description: '',
  business: '办公网络'
})
const testResult = ref(null)

// 预定义测试场景
const testScenarios = {
  network: {
    title: '办公室网络连接异常',
    description: '办公室所有电脑都无法连接网络,已检查网线连接正常,路由器指示灯正常。之前网络使用正常,今天上午开始出现问题。',
    business: '办公网络'
  },
  login: {
    title: '财务系统无法登录',
    description: '用户反映财务系统登录页面输入账号密码后提示"用户名或密码错误",但是密码确认无误。其他同事可以正常登录。',
    business: '财务系统'
  },
  printer: {
    title: '办公室打印机故障',
    description: '打印机显示"脱机"状态无法打印任何文件。已检查电源连接正常USB线已重新插拔电脑上显示打印机驱动正常。',
    business: '设备维护'
  },
  custom: {
    title: '',
    description: '',
    business: '办公网络'
  }
}

// 监听场景切换
watch(selectedScenario, (newScenario) => {
  if (newScenario !== 'custom') {
    Object.assign(testForm.value, testScenarios[newScenario])
  }
})

// 运行AI测试
const runAITest = async () => {
  testing.value = true
  testResult.value = null
  
  try {
    // 模拟API调用 (实际环境中调用真实接口)
    const response = await mockAIAPICall(testForm.value)
    testResult.value = response
    ElMessage.success('AI测试完成')
  } catch (error) {
    ElMessage.error('AI测试失败: ' + error.message)
  } finally {
    testing.value = false
  }
}

// 模拟AI API调用 (开发阶段使用)
const mockAIAPICall = async (testData) => {
  // 模拟网络延迟
  await new Promise(resolve => setTimeout(resolve, 2000))
  
  // 模拟AI回答生成
  const mockResponses = {
    network: {
      answer: `## 问题分析
网络连接异常可能由以下原因导致:

## 解决方案
1. **检查网络设备**
   - 重启路由器和交换机
   - 检查网线接头是否松动

2. **检查网络配置**
   - 确认IP地址设置正确
   - 检查DNS配置

3. **联系网络管理员**
   - 如上述方法无效,建议联系网络管理员检查上级网络设备

## 预计解决时间
10-30分钟`,
      confidence: 0.87,
      responseTime: 1850,
      similarCases: 5,
      usedTools: 'knowledge_search,similar_cases'
    },
    login: {
      answer: `## 问题分析
登录失败通常由账户状态或权限问题导致:

## 解决方案
1. **密码重置**
   - 联系系统管理员重置密码
   - 检查账户是否被锁定

2. **浏览器清理**
   - 清除浏览器缓存和Cookie
   - 尝试使用其他浏览器登录

3. **权限检查**
   - 确认账户具有系统访问权限
   - 检查账户是否过期

## 预计解决时间
5-15分钟`,
      confidence: 0.92,
      responseTime: 1420,
      similarCases: 8,
      usedTools: 'user_info,knowledge_search'
    }
  }
  
  const scenario = testData.business === '办公网络' ? 'network' : 'login'
  return mockResponses[scenario]
}

// 提交测试反馈
const submitTestFeedback = (feedbackType) => {
  const feedbackMessages = {
    good: '感谢反馈AI回答质量评价优秀',
    average: '感谢反馈AI回答质量评价良好',
    poor: '感谢反馈我们会继续改进AI回答质量'
  }
  
  ElMessage.info(feedbackMessages[feedbackType])
}

// 重置测试
const resetTest = () => {
  testForm.value = { title: '', description: '', business: '办公网络' }
  testResult.value = null
  selectedScenario.value = 'network'
}

// 初始化
selectedScenario.value = 'network'
</script>

<style scoped>
.ai-test-demo {
  max-width: 1000px;
  margin: 20px auto;
}

.test-scenarios, .test-config, .test-actions {
  margin-bottom: 20px;
}

.test-results {
  margin-top: 20px;
}

.ai-response {
  margin-bottom: 20px;
}

.answer-content {
  background: #f8f9fa;
  padding: 15px;
  border-radius: 8px;
  border-left: 4px solid #28a745;
  margin-bottom: 15px;
  white-space: pre-wrap;
}

/* 流式生成样式 */
.streaming .typing-indicator {
  font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
}

.streaming .cursor {
  animation: blink 1s infinite;
  color: #409eff;
  font-weight: bold;
}

@keyframes blink {
  0%, 50% { opacity: 1; }
  51%, 100% { opacity: 0; }
}

.generation-progress {
  margin-bottom: 15px;
}

.progress-text {
  font-size: 12px;
  color: #606266;
}

.answer-controls {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.answer-metrics {
  margin-top: 15px;
}

.test-feedback {
  text-align: center;
}
</style>

📋 总结

开发优先级

  1. Phase 1: 基础工单管理功能(已可用)
  2. Phase 2: 用户认证和权限系统(已可用)
  3. Phase 3: AI智能回答功能 已实现,支持流式输出)
  4. Phase 4: 高级统计分析和可视化

技术栈建议

  • 前端框架: Vue 3 + Element Plus
  • 状态管理: Pinia
  • 路由: Vue Router 4
  • HTTP客户端: Axios
  • Markdown渲染: marked
  • 图表库: ECharts

部署说明

🚀 流式输出特性

  • Server-Sent Events (SSE): 实时推送AI生成进度
  • 实时进度指示: 分析→搜索→生成阶段可视化
  • 中断控制: 用户可随时停止生成过程
  • 性能监控: 完整的流程追踪和性能指标
  • 降级支持: 自动切换到非流式模式作为备选

🎯 前端集成要点

  1. EventSource API: 用于接收SSE流式数据
  2. 状态管理: 生成进度、内容缓冲、会话控制
  3. 用户体验: 打字机效果、进度条、停止按钮
  4. 错误处理: 连接失败、超时、异常恢复
  5. 资源清理: 组件卸载时关闭SSE连接

这份文档为前端开发提供了完整的API接口规范、流式输出实现方案和测试Demo确保前后端协作顺利进行。AI智能回答功能已完全就绪支持现代化的流式用户体验。