feat(detection): 添加检测锁机制防止多用户同时操作
- 新增 detectionLock store 管理检测锁状态 - 实现检测锁相关的弹窗提示功能 - 添加 DETECTION_BUSY 错误码处理多人竞争逻辑 - 在 websocket 中集成检测锁超时处理 - 修改程序源控制接口以同步锁状态 - 更新项目标题和图标配置 - 添加 docs 目录到忽略列表
This commit is contained in:
72
frontend/src/utils/detectionLockDialog.ts
Normal file
72
frontend/src/utils/detectionLockDialog.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import type { DetectionLockHolder } from '@/stores/modules/detectionLock'
|
||||
|
||||
/**
|
||||
* S1:他人正在做检测,自己抢锁被挡
|
||||
*/
|
||||
export const showLockBusyDialog = (holder: DetectionLockHolder) => {
|
||||
ElMessageBox.confirm(
|
||||
`「${holder.holderUserName}」正在做检测,请稍后。`,
|
||||
'检测进行中',
|
||||
{
|
||||
confirmButtonText: '观看检测视频教学',
|
||||
cancelButtonText: '我知道了',
|
||||
type: 'warning',
|
||||
distinguishCancelAndClose: true,
|
||||
customClass: 'detection-lock-busy-dialog'
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
// 视频教学跳转 URL 暂未配置,先用 Toast 兜底
|
||||
ElMessage.info('视频教学功能开发中,敬请期待')
|
||||
})
|
||||
.catch(() => {
|
||||
// 用户点了"我知道了"或关闭,什么都不做
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* S2:未开始检测就调中间接口
|
||||
*/
|
||||
export const showLockNotStartedToast = () => {
|
||||
ElMessage.warning('请先点击"开始检测"按钮启动本轮检测')
|
||||
}
|
||||
|
||||
/**
|
||||
* S3:自己暂停超 10 分钟,被 WS 推 STOP_TIMEOUT 强制结束
|
||||
*/
|
||||
export const showPauseTimeoutDialog = () => {
|
||||
ElMessageBox.alert('暂停超过 10 分钟未恢复,系统已自动结束本次检测。\n\n如需继续,请重新发起检测。', '本次检测已结束', {
|
||||
confirmButtonText: '我知道了',
|
||||
type: 'warning'
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
/**
|
||||
* S4:被管理员强制释放
|
||||
* - holder 为 null → 强释后无人接手
|
||||
* - holder 非 null → 强释后被别人立刻抢占
|
||||
*/
|
||||
export const showForceReleasedDialog = (holder: DetectionLockHolder | null) => {
|
||||
if (holder) {
|
||||
ElMessageBox.confirm(
|
||||
`当前「${holder.holderUserName}」正在做检测,您无法继续检测,请稍后。`,
|
||||
'检测已被中止',
|
||||
{
|
||||
confirmButtonText: '观看检测视频教学',
|
||||
cancelButtonText: '我知道了',
|
||||
type: 'warning',
|
||||
distinguishCancelAndClose: true
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
ElMessage.info('视频教学功能开发中,敬请期待')
|
||||
})
|
||||
.catch(() => {})
|
||||
} else {
|
||||
ElMessageBox.alert('您的检测已被管理员强制结束。\n如需继续,请重新发起检测。', '检测已被中止', {
|
||||
confirmButtonText: '我知道了',
|
||||
type: 'warning'
|
||||
}).catch(() => {})
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
import { ElMessage } from "element-plus";
|
||||
import { jwtUtil } from "./jwtUtil";
|
||||
import { useDetectionLockStore } from "@/stores/modules/detectionLock";
|
||||
import { showPauseTimeoutDialog } from "@/utils/detectionLockDialog";
|
||||
|
||||
// ============================================================================
|
||||
// 类型定义 (Types & Interfaces)
|
||||
@@ -190,8 +192,8 @@ export default class SocketService {
|
||||
* WebSocket连接配置
|
||||
*/
|
||||
private config: SocketConfig = {
|
||||
url: 'ws://127.0.0.1:7778/hello',
|
||||
//url: 'ws://192.168.1.124:7777/hello',
|
||||
// url: 'ws://127.0.0.1:7778/hello',
|
||||
url: 'ws://127.0.0.1:7777/hello',
|
||||
heartbeatInterval: 9000, // 9秒心跳间隔
|
||||
reconnectDelay: 5000, // 5秒重连延迟
|
||||
maxReconnectAttempts: 5, // 最多重连5次
|
||||
@@ -546,6 +548,18 @@ export default class SocketService {
|
||||
// 检查是否为JSON格式
|
||||
if (typeof event.data === 'string' && (event.data.startsWith('{') || event.data.startsWith('['))) {
|
||||
const message: WebSocketMessage = JSON.parse(event.data);
|
||||
|
||||
// 全局拦截:暂停 10 分钟超时,后端推 STOP_TIMEOUT 表示锁已被释放
|
||||
// 不管页面是否注册了对应回调,都要做"清本地持锁标记 + 弹窗告知"
|
||||
if (message?.operateCode === 'STOP_TIMEOUT') {
|
||||
try {
|
||||
useDetectionLockStore().clearHolder();
|
||||
showPauseTimeoutDialog();
|
||||
} catch (e) {
|
||||
console.error('STOP_TIMEOUT 全局处理失败:', e);
|
||||
}
|
||||
}
|
||||
|
||||
if (message?.type && this.callBackMapping[message.type]) {
|
||||
this.callBackMapping[message.type](message);
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user