RIS_AUTO_SYNC_FINAL.md 10 KB

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. 程序启动流程

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自动同步流程

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

// 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

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<boolean>) => {
      state.isRunning = action.payload;
    },
    setLastSyncTime: (state, action: PayloadAction<string>) => {
      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 修改

// 在现有的 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

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<AppDispatch>();
  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<RisSyncService | null>(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

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 修改

// 在 import 部分添加
import { useRisAutoSync } from '@/hooks/useRisAutoSync';

// 在组件内部添加(其他代码保持不变)
const WorklistPage: React.FC = () => {
  // ... 现有的代码
  
  // 启用RIS自动同步
  useRisAutoSync();
  
  // ... 现有的代码
};

6. store.ts 修改

// 添加 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