# RIS自动同步功能 - 最终实现方案 ## 📋 功能概述 RIS自动同步功能在后台静默运行,定期从RIS系统同步数据到本地,确保worklist显示最新信息。 ## 🎯 核心需求 1. **自动同步机制** - worklist组件挂载时启动自动同步 - worklist组件卸载时停止自动同步 - 根据配置的时间间隔自动触发 2. **配置管理** - 在程序启动时获取RIS配置 - 配置存储在全局Redux state中 - worklist从全局state读取配置 3. **状态反馈** - 使用Antd Message弹框显示同步结果 - 成功/失败信息自动消失 - 不显示固定的UI组件 ## 🏗️ 实现架构 ### 文件结构 ``` src/ ├── states/ │ └── ris/ │ └── risSyncSlice.ts # RIS配置和状态管理 ├── hooks/ │ └── useRisAutoSync.ts # 自动同步Hook ├── services/ │ └── risSync/ │ └── RisSyncService.ts # 同步服务类 ├── pages/ │ ├── index/ │ │ └── AppInitializer.tsx # [修改] 启动时获取配置 │ └── patient/ │ └── worklist.tsx # [修改] 集成自动同步 └── states/ └── store.ts # [修改] 注册reducer ``` ## 🔄 执行流程 ### 1. 程序启动流程 ```mermaid sequenceDiagram participant App as 应用启动 participant Init as AppInitializer participant API participant Redux App->>Init: 初始化应用 Init->>API: getRisConfig() API-->>Init: RIS配置 Init->>Redux: 保存配置到全局state Redux-->>Init: 配置保存成功 ``` ### 2. Worklist自动同步流程 ```mermaid sequenceDiagram participant User participant Worklist participant Hook as useRisAutoSync participant Redux participant Service as RisSyncService participant API participant Message User->>Worklist: 进入worklist页面 Worklist->>Hook: 组件挂载,启动Hook Hook->>Redux: 读取RIS配置 Redux-->>Hook: 返回配置 alt 自动同步启用 Hook->>Service: 启动定时器 loop 每N分钟 Service->>API: syncRis() API-->>Service: 同步结果 Service->>API: fetchTaskList() API-->>Service: 任务列表 Service->>Redux: 更新worklist数据 Service->>Message: 显示同步结果 Note over Message: 3秒后自动消失 end end User->>Worklist: 离开worklist页面 Worklist->>Hook: 组件卸载 Hook->>Service: 清理定时器 ``` ## 📊 数据结构 ### Redux State ```typescript // RIS同步状态(精简版) interface RisSyncState { // 配置信息(程序启动时获取) config: { mwl_enable: boolean; mwl_refresh_enable: boolean; mwl_refresh_interval: number; } | null; // 运行时状态(不需要UI显示) isRunning: boolean; // 同步是否正在运行 lastSyncTime: string | null; // 最后同步时间 } ``` ## 💻 代码实现 ### 1. risSyncSlice.ts ```typescript import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit'; import { getRisConfig, RisConfigData } from '@/API/patient/risActions'; interface RisSyncState { config: RisConfigData | null; isRunning: boolean; lastSyncTime: string | null; } const initialState: RisSyncState = { config: null, isRunning: false, lastSyncTime: null, }; // 获取配置的thunk(程序启动时调用) export const fetchRisConfigThunk = createAsyncThunk( 'risSync/fetchConfig', async () => { const response = await getRisConfig(); return response.data; } ); const risSyncSlice = createSlice({ name: 'risSync', initialState, reducers: { setRunning: (state, action: PayloadAction) => { state.isRunning = action.payload; }, setLastSyncTime: (state, action: PayloadAction) => { state.lastSyncTime = action.payload; }, }, extraReducers: (builder) => { builder.addCase(fetchRisConfigThunk.fulfilled, (state, action) => { state.config = action.payload; console.log('[RIS] 配置加载成功:', action.payload); }); }, }); export const { setRunning, setLastSyncTime } = risSyncSlice.actions; export default risSyncSlice.reducer; ``` ### 2. AppInitializer.tsx 修改 ```typescript // 在现有的 AppInitializer 中添加 import { fetchRisConfigThunk } from '@/states/ris/risSyncSlice'; // 在 useEffect 中添加 useEffect(() => { const fetchData = async () => { // ... 现有的初始化代码 // 获取RIS配置 try { await dispatch(fetchRisConfigThunk()); console.log('[AppInitializer] RIS配置加载完成'); } catch (error) { console.error('[AppInitializer] RIS配置加载失败:', error); // 配置加载失败不影响应用启动 } }; fetchData(); }, [dispatch]); ``` ### 3. useRisAutoSync.ts ```typescript import { useEffect, useRef } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { message } from 'antd'; import { RootState, AppDispatch } from '@/states/store'; import { setRunning, setLastSyncTime } from '@/states/ris/risSyncSlice'; import { RisSyncService } from '@/services/risSync/RisSyncService'; import { fetchWorkThunk } from '@/states/patient/worklist/slices/workSlice'; export const useRisAutoSync = () => { const dispatch = useDispatch(); const config = useSelector((state: RootState) => state.risSync.config); const filters = useSelector((state: RootState) => state.workFilters); const page = useSelector((state: RootState) => state.workPagination.page); const pageSize = useSelector((state: RootState) => state.workPagination.pageSize); const serviceRef = useRef(null); useEffect(() => { // 检查是否应该启动同步 if (!config?.mwl_enable || !config?.mwl_refresh_enable) { console.log('[useRisAutoSync] RIS自动同步未启用'); return; } console.log('[useRisAutoSync] 启动RIS自动同步,间隔:', config.mwl_refresh_interval, '分钟'); // 创建服务实例 serviceRef.current = new RisSyncService({ interval: config.mwl_refresh_interval * 60 * 1000, // 转换为毫秒 onSyncComplete: (success, count) => { if (success) { message.success(`RIS同步成功,同步了 ${count} 条数据`, 3); dispatch(setLastSyncTime(new Date().toISOString())); // 刷新worklist dispatch(fetchWorkThunk({ page, pageSize, filters })); } else { // 错误只在控制台显示,不打扰用户 console.error('[useRisAutoSync] 同步失败'); } }, }); // 启动同步 serviceRef.current.start(); dispatch(setRunning(true)); // 清理函数 return () => { console.log('[useRisAutoSync] 停止RIS自动同步'); if (serviceRef.current) { serviceRef.current.stop(); serviceRef.current = null; } dispatch(setRunning(false)); }; }, [config, dispatch, page, pageSize, filters]); }; ``` ### 4. RisSyncService.ts ```typescript import { syncRis, RisTimeUtils } from '@/API/patient/risActions'; interface RisSyncServiceOptions { interval: number; onSyncComplete?: (success: boolean, count: number) => void; } export class RisSyncService { private timer: NodeJS.Timeout | null = null; private isRunning = false; private options: RisSyncServiceOptions; constructor(options: RisSyncServiceOptions) { this.options = options; } async start() { if (this.isRunning) { console.log('[RisSyncService] 服务已在运行'); return; } this.isRunning = true; // 立即执行一次同步 await this.performSync(); // 设置定时器 this.timer = setInterval(() => { this.performSync(); }, this.options.interval); console.log('[RisSyncService] 自动同步已启动'); } stop() { if (this.timer) { clearInterval(this.timer); this.timer = null; } this.isRunning = false; console.log('[RisSyncService] 自动同步已停止'); } private async performSync() { try { console.log('[RisSyncService] 开始同步RIS数据...'); // 获取今天的时间范围 const timeRange = RisTimeUtils.getTodayRange(); // 执行同步 const result = await syncRis({ start_time: timeRange.start, end_time: timeRange.end, }); console.log(`[RisSyncService] 同步成功,共同步 ${result.data.count} 条数据`); // 回调通知 if (this.options.onSyncComplete) { this.options.onSyncComplete(true, result.data.count); } } catch (error) { console.error('[RisSyncService] 同步失败:', error); // 回调通知 if (this.options.onSyncComplete) { this.options.onSyncComplete(false, 0); } } } } ``` ### 5. worklist.tsx 修改 ```typescript // 在 import 部分添加 import { useRisAutoSync } from '@/hooks/useRisAutoSync'; // 在组件内部添加(其他代码保持不变) const WorklistPage: React.FC = () => { // ... 现有的代码 // 启用RIS自动同步 useRisAutoSync(); // ... 现有的代码 }; ``` ### 6. store.ts 修改 ```typescript // 添加 import import risSyncReducer from './ris/risSyncSlice'; // 在 reducer 对象中添加 const store = configureStore({ reducer: { // ... 现有的 reducers risSync: risSyncReducer, }, // ... 其他配置 }); ``` ## 🧪 测试步骤 ### 1. 验证配置加载 ``` 1. 启动应用 2. 打开浏览器控制台 3. 查看是否有 "[RIS] 配置加载成功" 日志 ``` ### 2. 验证自动同步 ``` 1. 进入worklist页面 2. 查看控制台是否有 "[useRisAutoSync] 启动RIS自动同步" 日志 3. 等待配置的时间间隔 4. 观察是否出现同步成功的消息弹框 5. 验证列表是否刷新 ``` ### 3. 验证生命周期控制 ``` 1. 在worklist页面等待同步启动 2. 切换到其他页面 3. 查看控制台是否有 "[useRisAutoSync] 停止RIS自动同步" 日志 4. 返回worklist页面 5. 验证同步重新启动 ``` ## 📝 注意事项 1. **性能优化**:同步操作不会阻塞UI 2. **错误处理**:同步失败只在控制台记录,不干扰用户 3. **内存管理**:组件卸载时正确清理定时器 4. **配置缓存**:配置在应用启动时加载一次,避免重复请求 --- *文档版本:2.0.0* *更新日期:2025-01-31*