feat(sse): 添加风险评估任务的SSE事件监听及状态管理
Some checks failed
Lint Code / Lint Code (push) Failing after 17m16s
Some checks failed
Lint Code / Lint Code (push) Failing after 17m16s
- 在SseClient中新增RiskAssessTaskVO接口定义 - 在SseClient事件处理里支持风险评估相关事件推送处理 - 在sse模块中新增风险评估任务状态及进度等响应式变量 - 实现风险评估任务的事件监听逻辑,处理提交、进度、完成、错误事件 - 发送风险评估完成和错误的通知弹窗提示 - 添加重置风险评估状态的方法resetRiskAssessStatus - 风险评估页面引入SSE Store,响应任务状态变化展示进度和提示信息 - 提交风险评估任务时确保SSE连接已建立 - 新增风险评估任务进行中的加载状态显示及进度条UI - 监听风险评估完成和错误状态,自动加载数据并重置状态 - 优化风险评估接口调用参数类型转换和错误处理 - 生命周期钩子内移除窗口resize事件监听防止内存泄漏
This commit is contained in:
@@ -1,6 +1,10 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { ref, computed } from "vue";
|
||||
import { SseClient, type ProjectInitTaskVO } from "@/utils/sse/SseClient";
|
||||
import {
|
||||
SseClient,
|
||||
type ProjectInitTaskVO,
|
||||
type RiskAssessTaskVO
|
||||
} from "@/utils/sse/SseClient";
|
||||
import { store } from "../utils";
|
||||
import {
|
||||
getMyTasks as fetchTasksApi,
|
||||
@@ -25,6 +29,14 @@ export const useSseStore = defineStore("sse", () => {
|
||||
myTasks.value.some(t => t.status === "processing" || t.status === "pending")
|
||||
);
|
||||
|
||||
// 风险评估任务状态
|
||||
const riskAssessTask = ref<RiskAssessTaskVO | null>(null);
|
||||
const riskAssessProgress = ref(0);
|
||||
const riskAssessStatus = ref<
|
||||
"idle" | "submitted" | "processing" | "completed" | "error"
|
||||
>("idle");
|
||||
const riskAssessErrorMessage = ref("");
|
||||
|
||||
// Getters
|
||||
const getIsConnected = computed(() => isConnected.value);
|
||||
const getCurrentTask = computed(() => currentTask.value);
|
||||
@@ -115,6 +127,56 @@ export const useSseStore = defineStore("sse", () => {
|
||||
isConnected.value = false;
|
||||
});
|
||||
|
||||
// ============ 风险评估事件监听 ============
|
||||
// 监听风险评估任务提交
|
||||
sseClient.value.on("risk-assess-submitted", (data: any) => {
|
||||
riskAssessStatus.value = "submitted";
|
||||
console.log("SSE Store: 风险评估任务已提交", data);
|
||||
});
|
||||
|
||||
// 监听风险评估进度
|
||||
sseClient.value.on("risk-assess-progress", (data: RiskAssessTaskVO) => {
|
||||
riskAssessTask.value = data;
|
||||
riskAssessProgress.value = data.progress;
|
||||
riskAssessStatus.value = "processing";
|
||||
console.log(
|
||||
`SSE Store: 风险评估进度 ${data.progress}% - ${data.progressMessage}`
|
||||
);
|
||||
});
|
||||
|
||||
// 监听风险评估完成
|
||||
sseClient.value.on("risk-assess-complete", (data: RiskAssessTaskVO) => {
|
||||
riskAssessTask.value = data;
|
||||
riskAssessProgress.value = 100;
|
||||
riskAssessStatus.value = "completed";
|
||||
console.log("SSE Store: 风险评估完成", data.result);
|
||||
|
||||
// 发送通知
|
||||
ElNotification({
|
||||
title: "风险评估完成",
|
||||
message: `项目风险评估已完成,已识别 ${data.result?.identifiedRisks?.length || 0} 个风险。`,
|
||||
type: "success",
|
||||
duration: 5000,
|
||||
position: "top-right"
|
||||
});
|
||||
});
|
||||
|
||||
// 监听风险评估错误
|
||||
sseClient.value.on("risk-assess-error", (data: { error: string }) => {
|
||||
riskAssessStatus.value = "error";
|
||||
riskAssessErrorMessage.value = data.error;
|
||||
console.error("SSE Store: 风险评估错误", data.error);
|
||||
|
||||
// 发送错误通知
|
||||
ElNotification({
|
||||
title: "风险评估失败",
|
||||
message: data.error || "风险评估失败,请重试",
|
||||
type: "error",
|
||||
duration: 5000,
|
||||
position: "top-right"
|
||||
});
|
||||
});
|
||||
|
||||
// 建立连接(异步,在后台运行)
|
||||
sseClient.value.connect().catch(err => {
|
||||
console.error("SSE Store: 连接失败", err);
|
||||
@@ -159,6 +221,16 @@ export const useSseStore = defineStore("sse", () => {
|
||||
errorMessage.value = "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置风险评估任务状态
|
||||
*/
|
||||
function resetRiskAssessStatus() {
|
||||
riskAssessTask.value = null;
|
||||
riskAssessProgress.value = 0;
|
||||
riskAssessStatus.value = "idle";
|
||||
riskAssessErrorMessage.value = "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询我的任务列表
|
||||
*/
|
||||
@@ -182,6 +254,11 @@ export const useSseStore = defineStore("sse", () => {
|
||||
taskStatus,
|
||||
errorMessage,
|
||||
myTasks,
|
||||
// 风险评估状态
|
||||
riskAssessTask,
|
||||
riskAssessProgress,
|
||||
riskAssessStatus,
|
||||
riskAssessErrorMessage,
|
||||
// Getters
|
||||
getIsConnected,
|
||||
getCurrentTask,
|
||||
@@ -194,6 +271,7 @@ export const useSseStore = defineStore("sse", () => {
|
||||
closeSse,
|
||||
submitProjectInitTask,
|
||||
resetTaskStatus,
|
||||
resetRiskAssessStatus,
|
||||
fetchMyTasks
|
||||
};
|
||||
});
|
||||
|
||||
@@ -28,6 +28,23 @@ export interface ProjectInitTaskVO {
|
||||
errorMessage?: string;
|
||||
}
|
||||
|
||||
/** 风险评估任务VO */
|
||||
export interface RiskAssessTaskVO {
|
||||
taskId: string;
|
||||
userId: string;
|
||||
projectId: string;
|
||||
projectName?: string;
|
||||
status: string;
|
||||
statusDesc: string;
|
||||
progress: number;
|
||||
progressMessage: string;
|
||||
createTime: string;
|
||||
startTime?: string;
|
||||
completeTime?: string;
|
||||
result?: any;
|
||||
errorMessage?: string;
|
||||
}
|
||||
|
||||
type SseEventCallback = (data: any, message?: SseMessage) => void;
|
||||
|
||||
export class SseClient {
|
||||
@@ -181,22 +198,30 @@ export class SseClient {
|
||||
console.log("任务已提交:", message);
|
||||
if (message.type === "project-init") {
|
||||
this.emit("project-init-submitted", message.data, message);
|
||||
} else if (message.type === "risk-assess") {
|
||||
this.emit("risk-assess-submitted", message.data, message);
|
||||
}
|
||||
break;
|
||||
case "progress":
|
||||
if (message.type === "project-init") {
|
||||
this.emit("project-init-progress", message.data, message);
|
||||
} else if (message.type === "risk-assess") {
|
||||
this.emit("risk-assess-progress", message.data, message);
|
||||
}
|
||||
break;
|
||||
case "complete":
|
||||
if (message.type === "project-init") {
|
||||
this.emit("project-init-complete", message.data, message);
|
||||
} else if (message.type === "risk-assess") {
|
||||
this.emit("risk-assess-complete", message.data, message);
|
||||
}
|
||||
break;
|
||||
case "error":
|
||||
console.error("SSE 错误事件:", message);
|
||||
if (message.type === "project-init") {
|
||||
this.emit("project-init-error", message.data, message);
|
||||
} else if (message.type === "risk-assess") {
|
||||
this.emit("risk-assess-error", message.data, message);
|
||||
}
|
||||
this.emit("error", message.data, message);
|
||||
break;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, computed } from "vue";
|
||||
import { ref, onMounted, computed, onUnmounted, watch } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { useUserStoreHook } from "@/store/modules/user";
|
||||
import { useSseStoreHook } from "@/store/modules/sse";
|
||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||
import {
|
||||
getRiskList,
|
||||
@@ -67,6 +69,34 @@ const trendPeriod = ref("month");
|
||||
// 项目列表
|
||||
const projectList = ref<ProjectItem[]>([]);
|
||||
|
||||
// SSE Store
|
||||
const sseStore = useSseStoreHook();
|
||||
const userStore = useUserStoreHook();
|
||||
|
||||
// 风险评估进度相关
|
||||
const riskAssessProgress = computed(() => sseStore.riskAssessProgress);
|
||||
const riskAssessStatus = computed(() => sseStore.riskAssessStatus);
|
||||
const riskAssessTask = computed(() => sseStore.riskAssessTask);
|
||||
const riskAssessErrorMessage = computed(() => sseStore.riskAssessErrorMessage);
|
||||
const isAssessing = computed(
|
||||
() =>
|
||||
riskAssessStatus.value === "submitted" ||
|
||||
riskAssessStatus.value === "processing"
|
||||
);
|
||||
|
||||
// 监听风险评估完成
|
||||
watch(riskAssessStatus, newStatus => {
|
||||
if (newStatus === "completed") {
|
||||
message("风险评估完成!", { type: "success" });
|
||||
loadRiskList();
|
||||
loadStatistics();
|
||||
sseStore.resetRiskAssessStatus();
|
||||
} else if (newStatus === "error") {
|
||||
message(riskAssessErrorMessage.value || "风险评估失败", { type: "error" });
|
||||
sseStore.resetRiskAssessStatus();
|
||||
}
|
||||
});
|
||||
|
||||
// 分页
|
||||
const pagination = ref({
|
||||
currentPage: 1,
|
||||
@@ -473,16 +503,36 @@ async function handleCreate() {
|
||||
message("请先选择项目", { type: "warning" });
|
||||
return;
|
||||
}
|
||||
|
||||
const userId = userStore.userId;
|
||||
if (!userId) {
|
||||
message("用户信息不存在", { type: "error" });
|
||||
return;
|
||||
}
|
||||
|
||||
// 确保SSE连接已建立
|
||||
if (!sseStore.getIsConnected) {
|
||||
sseStore.initSse(String(userId));
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await submitRiskAssessment(queryParams.value.projectId);
|
||||
const res = await submitRiskAssessment(Number(queryParams.value.projectId));
|
||||
console.log("风险评估API响应:", res);
|
||||
console.log("res.data:", res.data);
|
||||
const responseData = res.data as any;
|
||||
// 扁平化结构:res.data 直接是 { code: 200, data: {...}, message: "..." }
|
||||
if (responseData.code === 200) {
|
||||
message("风险评估任务已提交,AI正在分析中...", { type: "success" });
|
||||
// 可以在这里启动SSE连接监听进度
|
||||
} else {
|
||||
console.error(
|
||||
"响应code不是200:",
|
||||
responseData.code,
|
||||
responseData.message
|
||||
);
|
||||
message(responseData.message || "提交失败", { type: "error" });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("风险评估请求异常:", error);
|
||||
message("提交风险评估任务失败", { type: "error" });
|
||||
}
|
||||
}
|
||||
@@ -534,6 +584,10 @@ onMounted(() => {
|
||||
initTrendChart();
|
||||
window.addEventListener("resize", handleResize);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener("resize", handleResize);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -572,15 +626,38 @@ onMounted(() => {
|
||||
</template>
|
||||
筛选
|
||||
</el-button>
|
||||
<el-button type="primary" @click="handleCreate">
|
||||
<el-button type="primary" :loading="isAssessing" @click="handleCreate">
|
||||
<template #icon>
|
||||
<component :is="useRenderIcon(AddIcon)" />
|
||||
</template>
|
||||
新建评估
|
||||
{{ isAssessing ? "评估中..." : "新建评估" }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- AI风险评估进度显示 -->
|
||||
<el-card v-if="isAssessing" class="mb-4" shadow="hover">
|
||||
<div class="flex-c gap-4">
|
||||
<el-icon class="is-loading text-primary" :size="24">
|
||||
<component :is="useRenderIcon('ri:loader-4-line')" />
|
||||
</el-icon>
|
||||
<div class="flex-1">
|
||||
<div class="flex-bc mb-2">
|
||||
<span class="font-medium">AI 正在进行风险评估</span>
|
||||
<span class="text-sm text-gray-500">{{ riskAssessProgress }}%</span>
|
||||
</div>
|
||||
<el-progress
|
||||
:percentage="riskAssessProgress"
|
||||
:stroke-width="8"
|
||||
:show-text="false"
|
||||
/>
|
||||
<p class="text-sm text-gray-500 mt-2">
|
||||
{{ riskAssessTask?.progressMessage || "正在分析项目数据..." }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 统计卡片 -->
|
||||
<el-row :gutter="16" class="mb-4">
|
||||
<el-col
|
||||
|
||||
Reference in New Issue
Block a user