diff --git a/app/core/config.py b/app/core/config.py index 5e45ce6..7213658 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -124,22 +124,37 @@ class GlobalConfig(ConfigBase): Data_UID = ConfigItem("Data", "UID", str(uuid.uuid4()), UUIDValidator()) Data_LastStatisticsUpload = ConfigItem( - "Data", "LastStatisticsUpload", "2000-01-01 00:00:00", DateTimeValidator() + "Data", + "LastStatisticsUpload", + "2000-01-01 00:00:00", + DateTimeValidator("%Y-%m-%d %H:%M:%S"), ) Data_LastStageUpdated = ConfigItem( - "Data", "LastStageUpdated", "2000-01-01 00:00:00", DateTimeValidator() + "Data", + "LastStageUpdated", + "2000-01-01 00:00:00", + DateTimeValidator("%Y-%m-%d %H:%M:%S"), ) Data_StageTimeStamp = ConfigItem( - "Data", "StageTimeStamp", "2000-01-01 00:00:00", DateTimeValidator() + "Data", + "StageTimeStamp", + "2000-01-01 00:00:00", + DateTimeValidator("%Y-%m-%d %H:%M:%S"), ) Data_Stage = ConfigItem("Data", "Stage", "{ }", JSONValidator()) Data_LastNoticeUpdated = ConfigItem( - "Data", "LastNoticeUpdated", "2000-01-01 00:00:00", DateTimeValidator() + "Data", + "LastNoticeUpdated", + "2000-01-01 00:00:00", + DateTimeValidator("%Y-%m-%d %H:%M:%S"), ) Data_IfShowNotice = ConfigItem("Data", "IfShowNotice", True, BoolValidator()) Data_Notice = ConfigItem("Data", "Notice", "{ }", JSONValidator()) Data_LastWebConfigUpdated = ConfigItem( - "Data", "LastWebConfigUpdated", "2000-01-01 00:00:00", DateTimeValidator() + "Data", + "LastWebConfigUpdated", + "2000-01-01 00:00:00", + DateTimeValidator("%Y-%m-%d %H:%M:%S"), ) Data_WebConfig = ConfigItem("Data", "WebConfig", "{ }", JSONValidator()) @@ -265,11 +280,15 @@ class MaaUserConfig(ConfigBase): "Info", "SklandToken", "", EncryptValidator() ) - self.Data_LastProxyDate = ConfigItem("Data", "LastProxyDate", "2000-01-01") - self.Data_LastAnnihilationDate = ConfigItem( - "Data", "LastAnnihilationDate", "2000-01-01" + self.Data_LastProxyDate = ConfigItem( + "Data", "LastProxyDate", "2000-01-01", DateTimeValidator("%Y-%m-%d") + ) + self.Data_LastAnnihilationDate = ConfigItem( + "Data", "LastAnnihilationDate", "2000-01-01", DateTimeValidator("%Y-%m-%d") + ) + self.Data_LastSklandDate = ConfigItem( + "Data", "LastSklandDate", "2000-01-01", DateTimeValidator("%Y-%m-%d") ) - self.Data_LastSklandDate = ConfigItem("Data", "LastSklandDate", "2000-01-01") self.Data_ProxyTimes = ConfigItem( "Data", "ProxyTimes", 0, RangeValidator(0, 9999) ) diff --git a/app/models/ConfigBase.py b/app/models/ConfigBase.py index 67c263d..40d6c28 100644 --- a/app/models/ConfigBase.py +++ b/app/models/ConfigBase.py @@ -30,7 +30,7 @@ from typing import List, Any, Dict, Union, Optional from app.utils import dpapi_encrypt, dpapi_decrypt -from app.utils.constants import RESERVED_NAMES, ILLEGAL_CHARS +from app.utils.constants import RESERVED_NAMES, ILLEGAL_CHARS, DEFAULT_DATETIME class ConfigValidator: @@ -114,24 +114,30 @@ class UUIDValidator(ConfigValidator): class DateTimeValidator(ConfigValidator): + """日期时间验证器""" + + def __init__(self, date_format: str) -> None: + if not date_format: + raise ValueError("日期时间格式不能为空") + self.date_format = date_format def validate(self, value: Any) -> bool: if not isinstance(value, str): return False try: - datetime.strptime(value, "%Y-%m-%d %H:%M:%S") + datetime.strptime(value, self.date_format) return True except ValueError: return False def correct(self, value: Any) -> str: if not isinstance(value, str): - return "2000-01-01 00:00:00" + return DEFAULT_DATETIME.strftime(self.date_format) try: - datetime.strptime(value, "%Y-%m-%d %H:%M:%S") + datetime.strptime(value, self.date_format) return value except ValueError: - return "2000-01-01 00:00:00" + return DEFAULT_DATETIME.strftime(self.date_format) class JSONValidator(ConfigValidator): diff --git a/app/utils/constants.py b/app/utils/constants.py index 2877126..dac3cbf 100644 --- a/app/utils/constants.py +++ b/app/utils/constants.py @@ -20,6 +20,8 @@ # Contact: DLmaster_361@163.com +from datetime import datetime + RESOURCE_STAGE_INFO = [ {"value": "-", "text": "当前/上次", "days": [1, 2, 3, 4, 5, 6, 7]}, {"value": "1-7", "text": "1-7", "days": [1, 2, 3, 4, 5, 6, 7]}, @@ -270,3 +272,6 @@ MIRROR_ERROR_INFO = { 1: "未知错误类型", } """MirrorChyan错误代码映射表""" + +DEFAULT_DATETIME = datetime.strptime("2000-01-01 00:00:00", "%Y-%m-%d %H:%M:%S") +"""默认日期时间""" diff --git a/docs/Backend_Task_Scheduling_and_WebSocket_Messages.md b/docs/Backend_Task_Scheduling_and_WebSocket_Messages.md new file mode 100644 index 0000000..4f291ba --- /dev/null +++ b/docs/Backend_Task_Scheduling_and_WebSocket_Messages.md @@ -0,0 +1,388 @@ +# AUTO-MAS 后端任务调度逻辑与WebSocket消息格式说明 + +## 1. 任务调度架构概览 + +AUTO-MAS 后端采用基于 AsyncIO 的异步任务调度系统,主要由以下核心组件构成: + +### 1.1 核心组件 + +- **TaskManager**: 任务调度器,负责任务的创建、运行、停止和清理 +- **Broadcast**: 消息广播系统,负责在不同组件间传递消息 +- **WebSocket**: 与前端的实时通信通道 +- **Config**: 配置管理系统,包含脚本配置、队列配置等 + +### 1.2 任务类型 + +系统支持三种主要任务模式: + +1. **设置脚本** - 直接执行单个脚本配置 +2. **自动代理** - 按队列顺序自动执行多个脚本 +3. **人工排查** - 手动排查和执行任务 + +## 2. 任务调度流程 + +### 2.1 任务创建流程 + +``` +前端请求 → API接口 → TaskManager.add_task() → 任务验证 → 创建异步任务 → 返回任务ID +``` + +**具体步骤:** + +1. **任务验证**: 根据模式和UID验证任务配置是否存在 +2. **重复检查**: 确保相同任务未在运行中 +3. **任务创建**: 使用`asyncio.create_task()`创建异步任务 +4. **回调设置**: 添加任务完成回调用于清理工作 + +### 2.2 任务执行流程 + +#### 设置脚本模式 +``` +获取脚本配置 → 确定脚本类型(MAA/General) → 创建对应Manager → 执行任务 +``` + +#### 自动代理模式 +``` +获取队列配置 → 构建任务列表 → 逐个执行脚本 → 更新状态 → 发送完成信号 +``` + +### 2.3 任务状态管理 + +- **等待**: 任务已加入队列但未开始执行 +- **运行**: 任务正在执行中 +- **跳过**: 任务因重复或其他原因被跳过 +- **完成**: 任务执行完毕 + +## 3. WebSocket 消息系统 + +### 3.1 消息基础结构 + +所有WebSocket消息都遵循统一的JSON格式: + +```json +{ + "id": "消息ID或任务ID", + "type": "消息类型", + "data": { + "具体数据": "根据类型而定" + } +} +``` + +### 3.2 消息类型详解 + +#### 3.2.1 Update 类型 - 数据更新 + +**用途**: 通知前端更新界面数据 + +**常见数据格式:** + +```json +{ + "id": "task-uuid", + "type": "Update", + "data": { + "user_list": [ + { + "name": "用户名", + "status": "运行状态", + "config": "配置信息" + } + ] + } +} +``` + +```json +{ + "id": "task-uuid", + "type": "Update", + "data": { + "task_list": [ + { + "script_id": "脚本ID", + "status": "等待/运行/完成/跳过", + "name": "脚本名称" + } + ] + } +} +``` + +```json +{ + "id": "task-uuid", + "type": "Update", + "data": { + "log": "任务执行日志内容" + } +} +``` + +#### 3.2.2 Info 类型 - 信息显示 + +**用途**: 向前端发送需要显示的信息,包括普通信息、警告和错误 + +**数据格式:** + +```json +{ + "id": "task-uuid", + "type": "Info", + "data": { + "Error": "错误信息内容" + } +} +``` + +```json +{ + "id": "task-uuid", + "type": "Info", + "data": { + "Warning": "警告信息内容" + } +} +``` + +```json +{ + "id": "task-uuid", + "type": "Info", + "data": { + "Info": "普通信息内容" + } +} +``` + +#### 3.2.3 Message 类型 - 对话框请求 + +**用途**: 请求前端弹出对话框显示重要信息 + +**数据格式:** + +```json +{ + "id": "task-uuid", + "type": "Message", + "data": { + "title": "对话框标题", + "content": "对话框内容", + "type": "info/warning/error" + } +} +``` + +#### 3.2.4 Signal 类型 - 程序信号 + +**用途**: 发送程序控制信号和状态通知 + +**常见信号:** + +**任务完成信号:** +```json +{ + "id": "task-uuid", + "type": "Signal", + "data": { + "Accomplish": "任务完成后调度台显示的日志内容" + } +} +``` + +**电源操作信号:** +```json +{ + "id": "task-uuid", + "type": "Signal", + "data": { + "power": "NoAction/KillSelf/Sleep/Hibernate/Shutdown/ShutdownForce", + } +} +``` + +**心跳信号:** +```json +{ + "id": "Main", + "type": "Signal", + "data": { + "Ping": "无描述" + } +} +``` + +```json +{ + "id": "Main", + "type": "Signal", + "data": { + "Pong": "无描述" + } +} +``` + +## 4. 任务管理器详细说明 + +### 4.1 TaskManager 核心方法 + +#### add_task(mode: str, uid: str) +- **功能**: 添加新任务到调度队列 +- **参数**: + - `mode`: 任务模式 ("设置脚本", "自动代理", "人工排查") + - `uid`: 任务唯一标识符 +- **返回**: 任务UUID + +#### stop_task(task_id: str) +- **功能**: 停止指定任务 +- **参数**: + - `task_id`: 任务ID,支持 "ALL" 停止所有任务 + +#### run_task(mode: str, task_id: UUID, actual_id: Optional[UUID]) +- **功能**: 执行具体任务逻辑 +- **流程**: 根据模式选择相应的执行策略 + +### 4.2 任务执行器 + +#### GeneralManager +- **用途**: 处理通用脚本任务 +- **特点**: 支持自定义脚本路径和参数 +- **配置**: 基于 GeneralConfig 和 GeneralUserConfig + +#### MaaManager +- **用途**: 处理MAA (明日方舟助手) 专用任务 +- **特点**: 支持模拟器控制、ADB连接、游戏自动化 +- **配置**: 基于 MaaConfig 和 MaaUserConfig + +## 5. 消息广播系统 + +### 5.1 Broadcast 机制 + +- **设计模式**: 发布-订阅模式 +- **功能**: 实现组件间解耦的消息传递 +- **特点**: 支持多个订阅者同时接收消息 + +### 5.2 消息流向 + +``` +任务执行器 → Broadcast → WebSocket → 前端界面 + ↓ + 其他订阅者 +``` + +## 6. 配置管理系统 + +### 6.1 配置类型 + +- **ScriptConfig**: 脚本配置,包含MAA和General两种类型 +- **QueueConfig**: 队列配置,定义自动代理任务的执行顺序 +- **GlobalConfig**: 全局系统配置 + +### 6.2 配置操作 + +- **锁机制**: 防止配置在使用时被修改 +- **实时更新**: 支持动态加载配置变更 +- **类型验证**: 确保配置数据的正确性 + +## 7. API 接口说明 + +### 7.1 任务控制接口 + +**创建任务** +- **端点**: `POST /api/dispatch/start` +- **请求体**: + ```json + { + "mode": "自动代理|人工排查|设置脚本", + "taskId": "目标任务ID" + } + ``` +- **响应**: + ```json + { + "code": 200, + "status": "success", + "message": "操作成功", + "websocketId": "新任务ID" + } + ``` + +**停止任务** +- **端点**: `POST /api/dispatch/stop` +- **请求体**: + ```json + { + "taskId": "要停止的任务ID" + } + ``` + +**电源操作** +- **端点**: `POST /api/dispatch/power` +- **请求体**: + ```json + { + "signal": "NoAction|Shutdown|ShutdownForce|Hibernate|Sleep|KillSelf" + } + ``` + +### 7.2 WebSocket 连接 + +**端点**: `WS /api/core/ws` + +**连接特性**: +- 同时只允许一个WebSocket连接 +- 自动心跳检测 (15秒超时) +- 连接断开时自动清理资源 + +## 8. 错误处理机制 + +### 8.1 异常类型 + +- **ValueError**: 配置验证失败 +- **RuntimeError**: 任务状态冲突 +- **TimeoutError**: 操作超时 +- **ConnectionError**: 连接相关错误 + +### 8.2 错误响应格式 + +```json +{ + "id": "相关任务ID", + "type": "Info", + "data": { + "Error": "具体错误描述" + } +} +``` + +## 9. 性能和监控 + +### 9.1 日志系统 + +- **分层日志**: 按模块划分日志记录器 +- **实时监控**: 支持日志实时推送到前端 +- **文件轮转**: 自动管理日志文件大小 + +### 9.2 资源管理 + +- **进程管理**: 自动清理子进程 +- **内存监控**: 防止内存泄漏 +- **连接池**: 复用数据库和网络连接 + +## 10. 安全考虑 + +### 10.1 输入验证 + +- **参数校验**: 使用 Pydantic 模型验证 +- **路径安全**: 防止路径遍历攻击 +- **命令注入**: 严格控制执行的命令参数 + +### 10.2 权限控制 + +- **单一连接**: 限制WebSocket连接数量 +- **操作限制**: 防止重复或冲突操作 +- **资源保护**: 防止资源滥用 + +--- + +*此文档基于 AUTO-MAS v5.0.0 版本编写,详细的API文档和配置说明请参考相关配置文件和源代码注释。* diff --git a/frontend/src/components/ScriptTable.vue b/frontend/src/components/ScriptTable.vue index 7ddb8d7..b1ecdce 100644 --- a/frontend/src/components/ScriptTable.vue +++ b/frontend/src/components/ScriptTable.vue @@ -37,24 +37,24 @@ type="primary" ghost size="middle" - @click="handleMAAConfig(script)" + @click="handleStartMAAConfig(script)" > - 设置MAA全局配置 + 配置MAA - + - 断开配置连接 + 保存配置 @@ -291,8 +291,8 @@ import type { Script, User } from '../types/script' import { DeleteOutlined, EditOutlined, + SaveOutlined, SettingOutlined, - StopOutlined, UserAddOutlined, } from '@ant-design/icons-vue' @@ -312,9 +312,9 @@ interface Emits { (e: 'deleteUser', user: User): void - (e: 'maaConfig', script: Script): void + (e: 'startMaaConfig', script: Script): void - (e: 'disconnectMaa', script: Script): void + (e: 'saveMaaConfig', script: Script): void (e: 'toggleUserStatus', user: User): void } @@ -350,12 +350,12 @@ const handleDeleteUser = (user: User) => { emit('deleteUser', user) } -const handleMAAConfig = (script: Script) => { - emit('maaConfig', script) +const handleStartMAAConfig = (script: Script) => { + emit('startMaaConfig', script) } -const handleDisconnectMAA = (script: Script) => { - emit('disconnectMaa', script) +const handleSaveMAAConfig = (script: Script) => { + emit('saveMaaConfig', script) } const handleToggleUserStatus = (user: User) => { diff --git a/frontend/src/views/MAAUserEdit.vue b/frontend/src/views/MAAUserEdit.vue index 26cb968..8625809 100644 --- a/frontend/src/views/MAAUserEdit.vue +++ b/frontend/src/views/MAAUserEdit.vue @@ -17,18 +17,32 @@ + - MAA配置 + 配置MAA + + + + + + 保存配置 @@ -973,6 +987,7 @@ import { usePlanApi } from '@/composables/usePlanApi' import { useWebSocket } from '@/composables/useWebSocket' import { Service } from '@/api' import { GetStageIn } from '@/api/models/GetStageIn' +import { TaskCreateIn } from '@/api/models/TaskCreateIn' import { defineComponent } from 'vue' const router = useRouter() @@ -1235,9 +1250,9 @@ const getDefaultMAAUserData = () => ({ Data: { CustomInfrastPlanIndex: '', IfPassCheck: false, - LastAnnihilationDate: '', - LastProxyDate: '', - LastSklandDate: '', + LastAnnihilationDate: '2000-01-01', + LastProxyDate: '2000-01-01', + LastSklandDate: '2000-01-01', ProxyTimes: 0, }, }) @@ -1532,36 +1547,95 @@ const handleSubmit = async () => { } } -const handleMAAConfig = async () => { +const handleStartMAAConfig = async () => { if (!isEdit.value) { message.warning('请先保存用户后再进行MAA配置') return } try { - maaConfigLoading.value = true - - // 如果已有连接,先断开 + // 检查是否已有连接 if (maaWebsocketId.value) { - unsubscribe(maaWebsocketId.value) - maaWebsocketId.value = null + message.warning('该用户MAA配置已在进行中,请先保存配置') + return } - // 直接订阅(旧 connect 参数移除) - const subId = userId - subscribe(subId, { - onError: error => { - console.error(`用户 ${formData.userName} MAA配置错误:`, error) - message.error(`MAA配置连接失败: ${error}`) - maaWebsocketId.value = null - } + maaConfigLoading.value = true + + // 调用启动配置任务API + const response = await Service.addTaskApiDispatchStartPost({ + taskId: userId, + mode: TaskCreateIn.mode.SettingScriptMode }) - maaWebsocketId.value = subId - message.success(`已开始配置用户 ${formData.userName} 的MAA设置`) + if (response.code === 200) { + // 订阅WebSocket消息 + subscribe(response.websocketId, { + onError: error => { + console.error(`用户 ${formData.userName} MAA配置错误:`, error) + message.error(`MAA配置连接失败: ${error}`) + maaWebsocketId.value = null + }, + onResult: (data: any) => { + // 处理配置完成消息(兼容任何结构) + if (data.Accomplish) { + message.success(`用户 ${formData.userName} MAA配置已完成`) + maaWebsocketId.value = null + } + } + }) + + // 记录连接和websocketId + maaWebsocketId.value = response.websocketId + message.success(`已启动用户 ${formData.userName} 的MAA配置`) + + // 设置自动断开连接的定时器(30分钟后) + setTimeout( + () => { + if (maaWebsocketId.value) { + unsubscribe(maaWebsocketId.value) + maaWebsocketId.value = null + message.info(`用户 ${formData.userName} MAA配置会话已超时断开`) + } + }, + 30 * 60 * 1000 + ) // 30分钟 + } else { + message.error(response.message || '启动MAA配置失败') + } } catch (error) { - console.error('MAA配置失败:', error) - message.error('MAA配置失败') + console.error('启动MAA配置失败:', error) + message.error('启动MAA配置失败') + } finally { + maaConfigLoading.value = false + } +} + +const handleSaveMAAConfig = async () => { + try { + if (!maaWebsocketId.value) { + message.error('未找到活动的配置会话') + return + } + + maaConfigLoading.value = true + + // 调用停止配置任务API + const response = await Service.stopTaskApiDispatchStopPost({ + taskId: maaWebsocketId.value + }) + + if (response.code === 200) { + // 取消订阅 + unsubscribe(maaWebsocketId.value) + maaWebsocketId.value = null + message.success(`用户 ${formData.userName} 的MAA配置已保存`) + } else { + message.error(response.message || '保存配置失败') + } + } catch (error) { + console.error('保存MAA配置失败:', error) + message.error('保存MAA配置失败') } finally { maaConfigLoading.value = false } diff --git a/frontend/src/views/Scheduler.vue b/frontend/src/views/Scheduler.vue index e6da198..d4b0c50 100644 --- a/frontend/src/views/Scheduler.vue +++ b/frontend/src/views/Scheduler.vue @@ -1,630 +1,980 @@ - + + + + + 调度中心 + + + 任务完成后电源操作: + + 无动作 + 退出软件 + 睡眠 + 休眠 + 关机 + 强制关机 + + + + - - - - - - - 任务全部完成后操作: - - 无动作 - 退出软件 - 睡眠 - 休眠 - 关机 - 关机(强制) - - - - - + + + + {{ tab.title }} + + {{ tab.status }} + + + 🔒 + + - - - - 自动代理 - 人工排查 - - - 开始任务 - - - + + + + + + + 自动代理 + 人工排查 + 设置脚本 + + + + 开始任务 + + + 中止任务 + + - - - - - - - - - - - - {{ task.status }} - - - 停止 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {{ item.name }} - - {{ item.status }} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {{ log.time }} - {{ log.message }} + + + + + + + 任务队列 + + + + + + + + {{ item.name }} + + + + {{ item.status }} + + + + + + + + + 暂无任务队列 - + + + + + + + + + + + 用户队列 + + + + + + + + {{ item.name }} + + {{ item.extraStatus }} + + + + + {{ item.status }} + + + + + + + + + 暂无用户队列 + + + + + + + + + + + + 实时日志 + + + + + 暂无日志信息 - - - - - + + {{ log.time }} + {{ log.message }} + + + + + - - - - - - - - - - 自动代理 - 人工排查 - 设置脚本 - - - - - - + + {{ currentMessage.content }} - + + + + + + + + 取消 + + + ⚠️ + + 所有任务已完成,系统将在 {{ powerCountdown }} 秒后执行:{{ getPowerActionText(powerAction) }} + + diff --git a/frontend/src/views/Scripts.vue b/frontend/src/views/Scripts.vue index 717b5f5..ab40894 100644 --- a/frontend/src/views/Scripts.vue +++ b/frontend/src/views/Scripts.vue @@ -40,8 +40,8 @@ @add-user="handleAddUser" @edit-user="handleEditUser" @delete-user="handleDeleteUser" - @maa-config="handleMAAConfig" - @disconnect-maa="handleDisconnectMAA" + @start-maa-config="handleStartMAAConfig" + @save-maa-config="handleSaveMAAConfig" @toggle-user-status="handleToggleUserStatus" /> @@ -232,6 +232,8 @@ import { useScriptApi } from '@/composables/useScriptApi' import { useUserApi } from '@/composables/useUserApi' import { useWebSocket } from '@/composables/useWebSocket' import { useTemplateApi, type WebConfigTemplate } from '@/composables/useTemplateApi' +import { Service } from '@/api/services/Service' +import { TaskCreateIn } from '@/api/models/TaskCreateIn' import MarkdownIt from 'markdown-it' const router = useRouter() @@ -494,51 +496,89 @@ const handleDeleteUser = async (user: User) => { } } -const handleMAAConfig = async (script: Script) => { +const handleStartMAAConfig = async (script: Script) => { try { // 检查是否已有连接 const existingConnection = activeConnections.value.get(script.id) if (existingConnection) { - message.warning('该脚本已在配置中,请先断开连接') + message.warning('该脚本已在配置中,请先保存配置') return } - // 新订阅 - subscribe(script.id, { - onError: error => { - console.error(`脚本 ${script.name} 连接错误:`, error) - message.error(`MAA配置连接失败: ${error}`) - activeConnections.value.delete(script.id) - }, + // 调用启动配置任务API + const response = await Service.addTaskApiDispatchStartPost({ + taskId: script.id, + mode: TaskCreateIn.mode.SettingScriptMode }) - // 记录连接 - activeConnections.value.set(script.id, script.id) - message.success(`已开始配置 ${script.name}`) - - // 可选:设置自动断开连接的定时器(比如30分钟后) - setTimeout( - () => { - if (activeConnections.value.has(script.id)) { - unsubscribe(script.id) + if (response.code === 200) { + // 订阅WebSocket消息 + subscribe(response.websocketId, { + onError: error => { + console.error(`脚本 ${script.name} 连接错误:`, error) + message.error(`MAA配置连接失败: ${error}`) activeConnections.value.delete(script.id) - message.info(`${script.name} 配置会话已超时断开`) + }, + onResult: (data: any) => { + // 处理配置完成消息(兼容任何结构) + if (data.Accomplish) { + message.success(`${script.name} 配置已完成`) + activeConnections.value.delete(script.id) + } } - }, - 30 * 60 * 1000 - ) // 30分钟 + }) + + // 记录连接和websocketId + activeConnections.value.set(script.id, response.websocketId) + message.success(`已启动 ${script.name} 的MAA配置`) + + // 设置自动断开连接的定时器(30分钟后) + setTimeout( + () => { + if (activeConnections.value.has(script.id)) { + const wsId = activeConnections.value.get(script.id) + if (wsId) { + unsubscribe(wsId) + } + activeConnections.value.delete(script.id) + message.info(`${script.name} 配置会话已超时断开`) + } + }, + 30 * 60 * 1000 + ) // 30分钟 + } else { + message.error(response.message || '启动MAA配置失败') + } } catch (error) { - console.error('MAA配置失败:', error) - message.error('MAA配置失败') + console.error('启动MAA配置失败:', error) + message.error('启动MAA配置失败') } } -const handleDisconnectMAA = (script: Script) => { - const connectionId = activeConnections.value.get(script.id) - if (connectionId) { - unsubscribe(script.id) - activeConnections.value.delete(script.id) - message.success(`已断开 ${script.name} 的配置连接`) +const handleSaveMAAConfig = async (script: Script) => { + try { + const websocketId = activeConnections.value.get(script.id) + if (!websocketId) { + message.error('未找到活动的配置会话') + return + } + + // 调用停止配置任务API + const response = await Service.stopTaskApiDispatchStopPost({ + taskId: websocketId + }) + + if (response.code === 200) { + // 取消订阅 + unsubscribe(websocketId) + activeConnections.value.delete(script.id) + message.success(`${script.name} 的配置已保存`) + } else { + message.error(response.message || '保存配置失败') + } + } catch (error) { + console.error('保存MAA配置失败:', error) + message.error('保存MAA配置失败') } }
暂无任务队列
暂无用户队列
暂无日志信息
{{ currentMessage.content }}
所有任务已完成,系统将在 {{ powerCountdown }} 秒后执行:{{ getPowerActionText(powerAction) }}