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

1361 lines
35 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 运维管理系统前端开发API文档
## 📖 文档概述
本文档为前端开发团队提供完整的API使用指南包含运维工单管理和AI智能回答功能的所有接口说明、数据结构定义和开发建议。
## 🌐 基础信息
### 服务器配置
- **开发环境**: http://localhost:8080
- **API文档**: http://localhost:8080/doc.html
- **数据库监控**: http://localhost:8080/druid (admin/123456)
### 认证机制
- **认证方式**: JWT Token + Apache Shiro
- **Token有效期**: 10小时后端Token
- **加密方式**: RSA加密 + SM3哈希
### 请求/响应格式
```javascript
// 统一请求格式
{
"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 用户登录
```http
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 获取用户信息
```http
GET /user/info
Authorization: Bearer <token>
Response:
{
"code": 200,
"data": {
"id": "用户ID",
"username": "用户名",
"nickname": "昵称",
"org": "所属组织",
"mobile": "手机号",
"email": "邮箱"
}
}
```
#### 1.3 用户退出
```http
POST /user/logout
Authorization: Bearer <token>
```
### 2. 工单管理模块 (RepairController)
#### 2.1 创建工单
```http
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 工单列表查询
```http
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 工单详情
```http
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 工单处理
```http
POST /repair/handle/next
Authorization: Bearer <token>
Content-Type: application/json
{
"repairId": "YW202408140001",
"result": "处理结果",
"nextStep": "resolve", // 下一步状态
"assignTo": "分配给谁" // 可选
}
```
### 3. AI智能回答模块 (流式输出,已实现)
#### 3.1 流式生成AI回答 (SSE)
```http
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回答
```http
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)
```http
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 中断流式生成
```http
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回答历史
```http
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回答
```http
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 我的待办工单
```http
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 工单统计概览
```http
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推送
#### 关键组件
```vue
<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回答展示实时显示生成过程
- 进度指示器(分析、搜索、生成阶段)
- 用户反馈收集(满意度、评分、意见)
- 相似案例推荐
- 回答质量评估指标
- 中断生成功能
#### 设计建议
```vue
<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. 状态管理
```javascript
// 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. 错误处理
```javascript
// 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实时通知
```javascript
// 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功能的各个环节
```vue
<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
### 部署说明
- 开发环境已启动在 http://localhost:8080
- API文档可访问 http://localhost:8080/doc.html
- 当前可使用基础工单管理功能
-**AI流式输出功能已实现并可用**
### 🚀 流式输出特性
- **Server-Sent Events (SSE)**: 实时推送AI生成进度
- **实时进度指示**: 分析→搜索→生成阶段可视化
- **中断控制**: 用户可随时停止生成过程
- **性能监控**: 完整的流程追踪和性能指标
- **降级支持**: 自动切换到非流式模式作为备选
### 🎯 前端集成要点
1. **EventSource API**: 用于接收SSE流式数据
2. **状态管理**: 生成进度、内容缓冲、会话控制
3. **用户体验**: 打字机效果、进度条、停止按钮
4. **错误处理**: 连接失败、超时、异常恢复
5. **资源清理**: 组件卸载时关闭SSE连接
这份文档为前端开发提供了完整的API接口规范、流式输出实现方案和测试Demo确保前后端协作顺利进行。AI智能回答功能已完全就绪支持现代化的流式用户体验。