From c4509b42fa6443f8346fd29a8b53d5bcbcfc90c3 Mon Sep 17 00:00:00 2001 From: JiaoTianBo Date: Sat, 28 Mar 2026 18:03:08 +0800 Subject: [PATCH] =?UTF-8?q?feat(project):=20=E6=94=AF=E6=8C=81=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E5=88=9D=E5=A7=8B=E5=8C=96=E4=BB=BB=E5=8A=A1=E7=9A=84?= =?UTF-8?q?=E5=BC=82=E6=AD=A5=E6=9F=A5=E8=AF=A2=E5=92=8C=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增项目初始化任务相关类型定义及接口封装,包括任务列表、任务统计和单个任务状态查询 - 集成 Element Plus 通知组件,增加任务完成和失败时的用户通知提醒 - 在 SSE 状态管理版块添加任务列表状态管理,支持任务状态的动态获取和展示 - 在创建项目向导组件中集成任务列表展示,支持查看任务进度、完成结果及错误信息 - 增加“使用此结果”按钮,允许用户直接应用已完成任务的项目初始化结果 - 对任务列表样式进行设计,区分不同状态的任务视觉效果提升用户体验 - 打开项目创建对话框时自动刷新并加载最新的任务列表数据 --- src/api/project.ts | 50 ++ src/api/项目sse.openapi.json | 606 ++++++++++++++++++ src/store/modules/sse.ts | 57 +- .../components/CreateProjectWizard.vue | 157 ++++- 4 files changed, 867 insertions(+), 3 deletions(-) create mode 100644 src/api/项目sse.openapi.json diff --git a/src/api/project.ts b/src/api/project.ts index 391ebcb..ab21df1 100644 --- a/src/api/project.ts +++ b/src/api/project.ts @@ -239,3 +239,53 @@ export const confirmProjectInit = (data: ProjectInitResult) => { { data } ); }; + +// ==================== 项目初始化任务(SSE) ==================== + +/** 项目初始化任务 VO - 根据 OpenAPI 定义 */ +export type ProjectInitTaskVO = { + taskId: string; + userId?: number; + status: string; // pending, processing, completed, failed + statusDesc: string; + progress: number; + progressMessage: string; + originalFilename: string; + createTime: string; + startTime?: string; + completeTime?: string; + result?: ProjectInitResult; + errorMessage?: string; +}; + +/** 任务统计信息 */ +export type TaskStats = { + total: number; + processing: number; + completed: number; + failed: number; +}; + +/** 查询我的任务列表 */ +export const getMyTasks = () => { + return http.request>( + "get", + "/api/v1/project-init/my-tasks" + ); +}; + +/** 查询我的任务统计信息 */ +export const getMyTaskStats = () => { + return http.request>( + "get", + "/api/v1/project-init/my-tasks/stats" + ); +}; + +/** 查询单个任务状态 */ +export const getTaskStatus = (taskId: string) => { + return http.request>( + "get", + `/api/v1/project-init/task/${taskId}` + ); +}; diff --git a/src/api/项目sse.openapi.json b/src/api/项目sse.openapi.json new file mode 100644 index 0000000..51a9a2e --- /dev/null +++ b/src/api/项目sse.openapi.json @@ -0,0 +1,606 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "默认模块", + "description": "", + "version": "1.0.0" + }, + "tags": [], + "paths": { + "/api/v1/project-init/sse/submit-task": { + "post": { + "summary": "通过 SSE 提交项目初始化任务", + "deprecated": false, + "description": "使用通用 SSE 通道,通过 userId 推送进度", + "tags": [], + "parameters": [ + { + "name": "userId", + "in": "query", + "description": "用户ID", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "Authorization", + "in": "header", + "description": "", + "example": "Bearer 6bf1de0f-9edf-413f-a98f-1103b8dc30fc", + "schema": { + "type": "string", + "default": "Bearer 6bf1de0f-9edf-413f-a98f-1103b8dc30fc" + } + } + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "file": { + "description": "项目资料文件", + "type": "string", + "format": "binary" + } + }, + "required": ["file"] + } + } + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BaseResponseMapObject", + "description": "提交结果" + } + } + } + } + }, + "security": [] + } + }, + "/api/v1/project-init/my-tasks": { + "get": { + "summary": "查询我的任务列表", + "deprecated": false, + "description": "根据当前登录用户的token查询其所有任务", + "tags": [], + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "", + "example": "Bearer 6bf1de0f-9edf-413f-a98f-1103b8dc30fc", + "schema": { + "type": "string", + "default": "Bearer 6bf1de0f-9edf-413f-a98f-1103b8dc30fc" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BaseResponseListProjectInitTaskVO", + "description": "任务列表" + } + } + } + } + }, + "security": [] + } + }, + "/api/v1/project-init/my-tasks/stats": { + "get": { + "summary": "查询我的任务统计信息", + "deprecated": false, + "description": "", + "tags": [], + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "", + "example": "Bearer 6bf1de0f-9edf-413f-a98f-1103b8dc30fc", + "schema": { + "type": "string", + "default": "Bearer 6bf1de0f-9edf-413f-a98f-1103b8dc30fc" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BaseResponseMapObject", + "description": "统计信息" + } + } + } + } + }, + "security": [] + } + }, + "/api/v1/project-init/task/{taskId}": { + "get": { + "summary": "查询单个任务状态", + "deprecated": false, + "description": "", + "tags": [], + "parameters": [ + { + "name": "taskId", + "in": "path", + "description": "任务ID", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "Authorization", + "in": "header", + "description": "", + "example": "Bearer 6bf1de0f-9edf-413f-a98f-1103b8dc30fc", + "schema": { + "type": "string", + "default": "Bearer 6bf1de0f-9edf-413f-a98f-1103b8dc30fc" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BaseResponseProjectInitTaskVO", + "description": "任务状态" + } + } + } + } + }, + "security": [] + } + } + }, + "components": { + "schemas": { + "ProjectInfo": { + "type": "object", + "properties": { + "project_name": { + "type": "string", + "description": "" + }, + "project_type": { + "type": "string", + "description": "" + }, + "description": { + "type": "string", + "description": "" + }, + "objectives": { + "type": "string", + "description": "" + }, + "plan_start_date": { + "type": "string", + "description": "" + }, + "plan_end_date": { + "type": "string", + "description": "" + }, + "budget": { + "type": "number", + "description": "" + }, + "currency": { + "type": "string", + "description": "" + }, + "priority": { + "type": "string", + "description": "" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "description": "" + } + } + }, + "MilestoneInfo": { + "type": "object", + "properties": { + "milestone_name": { + "type": "string", + "description": "" + }, + "description": { + "type": "string", + "description": "" + }, + "plan_date": { + "type": "string", + "description": "" + }, + "deliverables": { + "type": "string", + "description": "" + }, + "owner_role": { + "type": "string", + "description": "" + } + } + }, + "TaskInfo": { + "type": "object", + "properties": { + "task_id": { + "type": "string", + "description": "" + }, + "task_name": { + "type": "string", + "description": "" + }, + "parent_task_id": { + "type": "string", + "description": "" + }, + "description": { + "type": "string", + "description": "" + }, + "plan_start_date": { + "type": "string", + "description": "" + }, + "plan_end_date": { + "type": "string", + "description": "" + }, + "estimated_hours": { + "type": "integer", + "description": "" + }, + "priority": { + "type": "string", + "description": "" + }, + "assignee_role": { + "type": "string", + "description": "" + }, + "dependencies": { + "type": "array", + "items": { + "type": "string" + }, + "description": "" + }, + "deliverables": { + "type": "string", + "description": "" + } + } + }, + "BaseResponseMapObject": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "description": "" + }, + "data": { + "type": "object", + "properties": { + "total": { + "type": "integer" + }, + "processing": { + "type": "integer" + }, + "completed": { + "type": "null" + }, + "failed": { + "type": "null" + } + }, + "description": "" + }, + "message": { + "type": "string", + "description": "" + } + } + }, + "MemberInfo": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "" + }, + "role_code": { + "type": "string", + "description": "" + }, + "responsibility": { + "type": "string", + "description": "" + }, + "department": { + "type": "string", + "description": "" + }, + "weekly_hours": { + "type": "integer", + "description": "" + } + } + }, + "ResourceInfo": { + "type": "object", + "properties": { + "resource_name": { + "type": "string", + "description": "" + }, + "resource_type": { + "type": "string", + "description": "" + }, + "quantity": { + "type": "number", + "description": "" + }, + "unit": { + "type": "string", + "description": "" + }, + "unit_price": { + "type": "number", + "description": "" + }, + "supplier": { + "type": "string", + "description": "" + } + } + }, + "RiskInfo": { + "type": "object", + "properties": { + "risk_name": { + "type": "string", + "description": "" + }, + "category": { + "type": "string", + "description": "" + }, + "description": { + "type": "string", + "description": "" + }, + "probability": { + "type": "integer", + "description": "" + }, + "impact": { + "type": "integer", + "description": "" + }, + "mitigation_plan": { + "type": "string", + "description": "" + } + } + }, + "TimelineNodeInfo": { + "type": "object", + "properties": { + "node_name": { + "type": "string", + "description": "" + }, + "node_type": { + "type": "string", + "description": "" + }, + "plan_date": { + "type": "string", + "description": "" + }, + "description": { + "type": "string", + "description": "" + }, + "kb_scope": { + "type": "array", + "items": { + "type": "string" + }, + "description": "" + } + } + }, + "ProjectInitResult": { + "type": "object", + "properties": { + "project": { + "$ref": "#/components/schemas/ProjectInfo", + "description": "项目基本信息" + }, + "milestones": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MilestoneInfo", + "description": "cn.yinlihupo.domain.dto.ProjectInitResult.MilestoneInfo" + }, + "description": "里程碑列表" + }, + "tasks": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TaskInfo", + "description": "cn.yinlihupo.domain.dto.ProjectInitResult.TaskInfo" + }, + "description": "任务清单" + }, + "members": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MemberInfo", + "description": "cn.yinlihupo.domain.dto.ProjectInitResult.MemberInfo" + }, + "description": "项目成员" + }, + "resources": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ResourceInfo", + "description": "cn.yinlihupo.domain.dto.ProjectInitResult.ResourceInfo" + }, + "description": "资源需求" + }, + "risks": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RiskInfo", + "description": "cn.yinlihupo.domain.dto.ProjectInitResult.RiskInfo" + }, + "description": "风险识别" + }, + "timeline_nodes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TimelineNodeInfo", + "description": "cn.yinlihupo.domain.dto.ProjectInitResult.TimelineNodeInfo" + }, + "description": "时间节点" + } + } + }, + "ProjectInitTaskVO": { + "type": "object", + "properties": { + "taskId": { + "type": "string", + "description": "任务ID" + }, + "userId": { + "type": "integer", + "description": "用户ID(任务所属用户)", + "format": "int64" + }, + "status": { + "type": "string", + "description": "任务状态: pending-待处理, processing-处理中, completed-已完成, failed-失败" + }, + "statusDesc": { + "type": "string", + "description": "状态描述" + }, + "progress": { + "type": "integer", + "description": "当前进度百分比 (0-100)" + }, + "progressMessage": { + "type": "string", + "description": "进度描述信息" + }, + "originalFilename": { + "type": "string", + "description": "原始文件名" + }, + "createTime": { + "type": "string", + "description": "任务创建时间" + }, + "startTime": { + "type": "string", + "description": "任务开始处理时间" + }, + "completeTime": { + "type": "string", + "description": "任务完成时间" + }, + "result": { + "$ref": "#/components/schemas/ProjectInitResult", + "description": "处理结果(仅当status=completed时有值)" + }, + "errorMessage": { + "type": "string", + "description": "错误信息(仅当status=failed时有值)" + } + } + }, + "BaseResponseListProjectInitTaskVO": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "description": "" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProjectInitTaskVO", + "description": "项目初始化异步任务VO" + }, + "description": "" + }, + "message": { + "type": "string", + "description": "" + } + } + }, + "BaseResponseProjectInitTaskVO": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "description": "" + }, + "data": { + "$ref": "#/components/schemas/ProjectInitTaskVO", + "description": "" + }, + "message": { + "type": "string", + "description": "" + } + } + } + }, + "responses": {}, + "securitySchemes": {} + }, + "servers": [], + "security": [] +} diff --git a/src/store/modules/sse.ts b/src/store/modules/sse.ts index 03d5916..f0a2c68 100644 --- a/src/store/modules/sse.ts +++ b/src/store/modules/sse.ts @@ -2,6 +2,11 @@ import { defineStore } from "pinia"; import { ref, computed } from "vue"; import { SseClient, type ProjectInitTaskVO } from "@/utils/sse/SseClient"; import { store } from "../utils"; +import { + getMyTasks as fetchTasksApi, + type ProjectInitTaskVO as ApiTaskVO +} from "@/api/project"; +import { ElNotification } from "element-plus"; export const useSseStore = defineStore("sse", () => { // State @@ -13,12 +18,20 @@ export const useSseStore = defineStore("sse", () => { "idle" | "submitted" | "processing" | "completed" | "error" >("idle"); const errorMessage = ref(""); + // 我的任务列表 + const myTasks = ref([]); + // 是否有待处理的任务 + const hasProcessingTask = computed(() => + myTasks.value.some(t => t.status === "processing" || t.status === "pending") + ); // Getters const getIsConnected = computed(() => isConnected.value); const getCurrentTask = computed(() => currentTask.value); const getTaskProgress = computed(() => taskProgress.value); const getTaskStatus = computed(() => taskStatus.value); + const getMyTasks = computed(() => myTasks.value); + const getHasProcessingTask = computed(() => hasProcessingTask.value); // Actions /** @@ -55,6 +68,18 @@ export const useSseStore = defineStore("sse", () => { taskProgress.value = 100; taskStatus.value = "completed"; console.log("SSE Store: 任务完成", data.result); + + // 发送通知 + ElNotification({ + title: "项目解析完成", + message: `文件 "${data.originalFilename}" 已解析完成,请前往新建项目查看预览数据。`, + type: "success", + duration: 5000, + position: "top-right" + }); + + // 刷新任务列表 + fetchMyTasks(); }); // 监听任务提交 @@ -71,6 +96,18 @@ export const useSseStore = defineStore("sse", () => { taskStatus.value = "error"; errorMessage.value = data.error; console.error("SSE Store: 任务错误", data.error); + + // 发送错误通知 + ElNotification({ + title: "项目解析失败", + message: data.error || "文件解析失败,请重试", + type: "error", + duration: 5000, + position: "top-right" + }); + + // 刷新任务列表 + fetchMyTasks(); }); // 监听连接错误 @@ -122,6 +159,20 @@ export const useSseStore = defineStore("sse", () => { errorMessage.value = ""; } + /** + * 查询我的任务列表 + */ + async function fetchMyTasks() { + try { + const { code, data } = await fetchTasksApi(); + if (code === 200 && data) { + myTasks.value = data; + } + } catch (error) { + console.error("获取任务列表失败", error); + } + } + return { // State sseClient, @@ -130,16 +181,20 @@ export const useSseStore = defineStore("sse", () => { taskProgress, taskStatus, errorMessage, + myTasks, // Getters getIsConnected, getCurrentTask, getTaskProgress, getTaskStatus, + getMyTasks, + getHasProcessingTask, // Actions initSse, closeSse, submitProjectInitTask, - resetTaskStatus + resetTaskStatus, + fetchMyTasks }; }); diff --git a/src/views/project/components/CreateProjectWizard.vue b/src/views/project/components/CreateProjectWizard.vue index 83125c1..64338d6 100644 --- a/src/views/project/components/CreateProjectWizard.vue +++ b/src/views/project/components/CreateProjectWizard.vue @@ -1,7 +1,7 @@