import { Middleware, PayloadAction } from '@reduxjs/toolkit'; import prepare, { unprepare } from '../domain/exam/prepare'; import { BusinessFlowState, setBusinessFlow } from './BusinessFlowSlice'; import { setFeedbackOpen } from './exam/largeScreenSlice'; import { setFeatureNotAvailableOpen } from './featureNotAvailableSlice'; import { suspendOrCompleteStudy } from '@/API/patient/workActions'; import { setExpDisable } from '@/API/exam/deviceActions'; import { RootState } from './store'; import { getQuota } from '@/API/security/quotaActions'; import { showQuotaAlert } from './security/quotaModalSlice'; import emitter from '@/utils/eventEmitter'; import { setBodyPositions, transformWorksToBodyPositions, } from './exam/bodyPositionListSlice'; import { worklistToProcess, prepareWorksForExam, } from '@/domain/patient/worklistToExam'; import { executeRegisterLogic } from '@/domain/patient/registerLogic'; import registerToExam from '@/domain/patient/registerToExam'; import { addWork, clearWorks } from './exam/examWorksCacheSlice'; import { resetViewerContainer } from './view/viewerContainerSlice'; let continueBusinessFlow = ''; const businessFlowMiddlewareLogic: Middleware = (store) => (next) => async (action: PayloadAction) => { //const result = next(action); // console.log( // `[businessFlowMiddleware] Action dispatched: ${action.type} ${action.payload}` // ); if (action.type !== setBusinessFlow.type) { return next(action); // Only handle setBusinessFlow actions } // 响应功能不可用后的继续操作 if (action.payload === 'continueAfterFeatureNotAvailable') { console.log( `[businessFlowMiddleware] Continuing to: ${continueBusinessFlow}` ); return next({ ...action, payload: continueBusinessFlow }); } const state = store.getState().BusinessFlow as BusinessFlowState; const currentKey = state.currentKey; const targetKey = action.payload; console.log(`[businessFlowMiddleware] currentKey: ${currentKey}`); console.log( `[businessFlowMiddleware] Current business flow is now: ${state.currentKey} ; Last key was: ${state.lastKey}` ); //进入检查 if (action.payload === 'exam') { //进入检查前判断配额 try { const quotaResponse = await getQuota(); if (quotaResponse.data.available <= 0) { store.dispatch(showQuotaAlert()); return; } if (quotaResponse.code !== '0x000000') { store.dispatch(showQuotaAlert()); return; } } catch (error) { console.error('[businessFlowMiddleware] Quota check failed:', error); store.dispatch(showQuotaAlert()); return; } if (currentKey === 'worklist' || currentKey === 'history') { const state = store.getState(); // 获取选中的 study const selectedIds = currentKey === 'worklist' ? state.workSelection.selectedIds : state.historySelection.selectedIds; const entitiesData = currentKey === 'worklist' ? state.workEntities.data : state.historyEntities.data; if (selectedIds.length === 0) { console.warn('[businessFlowMiddleware] No study selected for exam'); return; // 阻止进入 exam } const selectedWork = entitiesData.find( (work) => work.StudyID === selectedIds[0] ); if (!selectedWork) { console.error('[businessFlowMiddleware] Selected study not found'); return; // 阻止进入 exam } try { // 使用公共函数准备数据 const [updatedTask] = await prepareWorksForExam(selectedWork); // 清空并更新缓存 store.dispatch(clearWorks()); store.dispatch(addWork(updatedTask)); // 转换为 body positions const bodyPositions = await transformWorksToBodyPositions([ updatedTask, ]); store.dispatch(setBodyPositions(bodyPositions)); console.log( `[businessFlowMiddleware] Successfully prepared ${bodyPositions.length} body positions for exam` ); } catch (error) { console.error( '[businessFlowMiddleware] Failed to prepare data for exam:', error ); return; // 阻止进入 exam } } // 进入检查前,如果是从register来的,则判断数据合法性,执行注册等逻辑 if (currentKey === 'register') { const systemMode = (store.getState() as RootState).systemMode.mode; // 如果是急诊模式,跳过验证(急诊流程已经完成注册) if (systemMode === 'Emergency') { console.log( '[businessFlowMiddleware] Emergency mode, skipping register validation' ); // 急诊模式下仍需要准备 body positions try { const state = store.getState(); const works = state.examWorksCache.works; const bodyPositions = await transformWorksToBodyPositions(works); store.dispatch(setBodyPositions(bodyPositions)); } catch (error) { console.error( '[businessFlowMiddleware] Failed to prepare body positions:', error ); return; // 阻止进入exam } } else { // 正常注册流程,需要验证 const result = await executeRegisterLogic(store); if (!result.success) { console.log( '[businessFlowMiddleware] Register validation failed, blocking entry to exam' ); return; // 阻止进入exam } try { await registerToExam(result.data!); const state = store.getState(); const works = state.examWorksCache.works; await transformWorksToBodyPositions(works) .then((bodyPositions) => { store.dispatch(setBodyPositions(bodyPositions)); }) .catch((error) => { console.error( '[businessFlowMiddleware] Transform works to body positions failed:', error ); return; // 阻止进入exam }); } catch (_error) { console.log( '[businessFlowMiddleware] Register validation failed', _error ); return; // 阻止进入exam } console.log( '[businessFlowMiddleware] Register validation succeeded, proceeding to exam' ); } } prepare(); return next(action); } if (action.payload === 'process') { if (isFromExamToView(action.payload, currentKey)) { //从检查进入图像处理,有可能是曝光导致的,怎么知道呢?看发生器状态 if (store.getState().generatorMonitor.acquisitionState === 1) { //发生器正在采集 console.log( `[businessFlowMiddleware] Exiting exam flow, but generator is still acquiring.` ); return next(action); // 发生器正在采集,不能退出 } else { //非曝光导致的从检查进入处理 // 说明从检查退出 , 执行清理 console.log( `[businessFlowMiddleware] Exiting exam flow, last key was: ${state.lastKey}` ); // 离开检查前,禁止发生器曝光 try { await setExpDisable(); } catch (error) { console.error('[businessFlowMiddleware] setExpDisable Failed to disable exposure:', error); } unprepare(); } } else { //不是从检查进入图像处理, if (currentKey === 'worklist') { //从工单进入图像处理,准备数据 const state = store.getState() as RootState; const selectedIds = state.workSelection.selectedIds; if (selectedIds.length > 0) { try { await worklistToProcess(selectedIds); console.log( `[businessFlowMiddleware] Successfully processed ${selectedIds.length} selected works from worklist to process` ); } catch (error) { console.error( '[businessFlowMiddleware] Error processing works from worklist:', error ); // 可以选择是否阻止进入process,这里我们选择继续 return; } } else { console.warn( '[businessFlowMiddleware] No works selected in worklist to process' ); } } if (currentKey === 'history') { //从历史进入图像处理, //从工单进入图像处理,准备数据 const state = store.getState() as RootState; const selectedIds = state.historySelection.selectedIds; if (selectedIds.length > 0) { try { await worklistToProcess(selectedIds); console.log( `[businessFlowMiddleware] Successfully processed ${selectedIds.length} selected works from worklist to process` ); } catch (error) { console.error( '[businessFlowMiddleware] Error processing works from worklist:', error ); // 可以选择是否阻止进入process,这里我们选择继续 return; } } else { console.warn( '[businessFlowMiddleware] No works selected in worklist to process' ); } } return next(action); } } if ( action.payload === 'archivelist' || action.payload === 'bin' || action.payload === 'outputlist' || action.payload === 'print' ) { console.log( `[businessFlowMiddleware] Feature not available: ${action.payload}` ); continueBusinessFlow = action.payload; // 保存要继续的业务流程 store.dispatch(setFeatureNotAvailableOpen(true)); return; } if (isLeavingProcess(action.payload, currentKey)) { // 说明从图像处理退出 , 执行清理 console.log( `[businessFlowMiddleware] Leaving process flow, last key was: ${state.lastKey}` ); // 重置 viewerContainerSlice 到初始状态 store.dispatch(resetViewerContainer()); } //退出检查的判断和退出处理的判断 if (( isExitingExam(action.payload, currentKey) && action.payload !== 'exitExamCompleted' && action.payload !== 'exitExamSuspended' && action.payload !== 'process' // 从检查进入处理,不需要检查曝光情况 ) || isLeavingProcess(action.payload, currentKey) && action.payload !== 'exitExamCompleted' && action.payload !== 'exitExamSuspended' && action.payload !== 'exam' ){ console.log( `[businessFlowMiddleware] Exiting exam flow to go to : ${action.payload}` ); const exposureStatus = store.getState().bodyPositionList.exposureStatus; console.log( `[businessFlowMiddleware] Exposure status: ${exposureStatus}` ); if (exposureStatus === 'Half Exposed') { store.dispatch(setFeedbackOpen(true)); //显示反馈框 // store.dispatch({ type: 'SET_CONTINUE_BUSINESS_FLOW', payload: currentKey }); continueBusinessFlow = action.payload; // 保存退出检查时,要去哪个业务流程 return; //阻止退出exam 或者 process } else if (exposureStatus === 'Fully Exposed') { // Notify backend console.log( '[businessFlowMiddleware] Fully Exposed, notifying backend' ); const studyId = (store.getState() as RootState)?.bodyPositionList?.selectedBodyPosition ?.work?.StudyID ?? ''; suspendOrCompleteStudy(studyId, 'Completed').then(() => { console.log(`[businessFlowMiddleware] 全曝光,通知后端完成`) }).catch((error) => { console.log(`[businessFlowMiddleware] 全曝光,通知后端失败 ${error}`); }); // Proceed with the action return next(action); } else if (exposureStatus === 'Not Exposed') { // Proceed with the action return next(action); } } //退出检查的中间过程 if (action.payload === 'exitExamCompleted') { // Notify backend with different interfaces for completed and suspended console.log( `[businessFlowMiddleware] Notifying backend for ${action.payload}` ); // 通知服务端。 const studyId = (store.getState() as RootState)?.bodyPositionList?.selectedBodyPosition ?.work?.StudyID ?? ''; if (!studyId) { console.error( '[businessFlowMiddleware] No study ID found for the selected body position.' ); } await suspendOrCompleteStudy(studyId, 'Completed'); return next({ ...action, payload: continueBusinessFlow }); } if (action.payload === 'exitExamSuspended') { // Notify backend with different interfaces for completed and suspended const studyId = (store.getState() as RootState)?.bodyPositionList?.selectedBodyPosition ?.work?.StudyID ?? ''; if (!studyId) { console.error( '[businessFlowMiddleware] No study ID found for the selected body position.' ); } await suspendOrCompleteStudy(studyId, 'InProgress'); console.log( `[businessFlowMiddleware] Notifying backend for ${action.payload}` ); return next({ ...action, payload: continueBusinessFlow }); } const result = next(action); // 发射业务流程切换完成事件 emitter.emit('BUSINESS_FLOW_CHANGED', { from: currentKey, to: targetKey, timestamp: Date.now(), }); console.log( `[businessFlowMiddleware] Emitted BUSINESS_FLOW_CHANGED event: ${currentKey} -> ${targetKey}` ); return result; }; /** * 从检查到图像处理 */ function isFromExamToView(currentAction: string, currentKey: string): boolean { return currentAction === 'process' && currentKey === 'exam'; } function isExitingExam(currentAction: string, currentKey: string): boolean { console.log( `[businessFlowMiddleware] Checking if exiting exam: ${currentAction} vs ${currentKey}` ); return currentAction !== 'exam' && currentKey === 'exam'; } function isLeavingProcess(currentAction: string, currentKey: string): boolean { return currentAction !== 'process' && currentKey === 'process'; } export default businessFlowMiddlewareLogic;