Explorar el Código

add
优化定时任务

lifang hace 1 mes
padre
commit
4100e6cd93
Se han modificado 18 ficheros con 508 adiciones y 136 borrados
  1. 2 0
      nb-admin/src/main/java/com/nb/admin/StartUpRunner.java
  2. 57 7
      nb-admin/src/main/resources/application-dev.yml
  3. 31 0
      nb-admin/src/main/resources/application-prod.yml
  4. 40 4
      nb-admin/src/main/resources/application-test.yml
  5. 2 2
      nb-admin/src/main/resources/application.yml
  6. 27 15
      nb-admin/src/main/resources/logback-spring.xml
  7. 70 0
      nb-core/src/main/java/com/nb/core/utils/CronExpressionUtil.java
  8. 70 0
      nb-core/src/main/java/com/nb/core/utils/DateCompareUtil.java
  9. 74 81
      nb-service/web-service/src/main/java/com/nb/web/service/bus/hospital/HospitalManager.java
  10. 2 5
      nb-service/web-service/src/main/java/com/nb/web/service/bus/hospital/HospitalManagerRegister.java
  11. 10 2
      nb-service/web-service/src/main/java/com/nb/web/service/bus/hospital/his/strategy/onlynew/DefaultHisNewStrategyHandler.java
  12. 2 1
      nb-service/web-service/src/main/java/com/nb/web/service/bus/hospital/his/strategy/part/DefaultHisPartStrategyHandler.java
  13. 4 1
      nb-service/web-service/src/main/java/com/nb/web/service/bus/service/LocalBusClinicService.java
  14. 3 4
      nb-service/web-service/src/main/java/com/nb/web/service/bus/service/LocalBusPatientService.java
  15. 0 5
      nb-service/web-service/src/main/java/com/nb/web/service/quartz/job/service/impl/SysJobServiceImpl.java
  16. 105 0
      nb-service/web-service/src/main/java/com/nb/web/service/quartz/task/HospitalHisDataPullTask.java
  17. 1 1
      nb-service/web-service/src/main/java/com/nb/web/service/quartz/util/AbstractQuartzJob.java
  18. 8 8
      nb-service/web-service/src/main/resources/mapper/bus/BusPatientMapper.xml

+ 2 - 0
nb-admin/src/main/java/com/nb/admin/StartUpRunner.java

@@ -6,6 +6,7 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.boot.CommandLineRunner;
 import org.springframework.boot.web.context.WebServerInitializedEvent;
 import org.springframework.context.ApplicationListener;
+import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Component;
 
 import javax.annotation.Resource;
@@ -26,6 +27,7 @@ public class StartUpRunner implements CommandLineRunner , ApplicationListener<We
     ISysConfigService sysConfigService;
 
     @Override
+    @Async
     public void run(String... args) {
         log.info("============  web is running ,start load data============");
         sysDictService.loadAllDictCache();

+ 57 - 7
nb-admin/src/main/resources/application-dev.yml

@@ -53,10 +53,62 @@ spring:
       testWhileIdle: true
       testOnBorrow: false
       testOnReturn: false
-        # 通过别名的方式配置扩展插件,多个英文逗号分隔,常用的插件有:
-        # 监控统计用的filter:stat
-        # 日志用的filter:log4j2
-        # 防御sql注入的filter:wall
+      # 通过别名的方式配置扩展插件,多个英文逗号分隔,常用的插件有:
+      # 监控统计用的filter:stat
+      # 日志用的filter:log4j2
+      # 防御sql注入的filter:wall
+      filters: log4j2
+      webStatFilter:
+        enabled: true
+      statViewServlet:
+        enabled: true
+        # 设置白名单,不填则允许所有访问
+        allow:
+        url-pattern: /druid/*
+        # 控制台管理用户名和密码
+        login-username: admin
+        login-password: admin
+      filter:
+        stat:
+          enabled: true
+          # 慢SQL记录
+          log-slow-sql: true
+          slow-sql-millis: 10000
+          merge-sql: true
+        wall:
+          config:
+            multi-statement-allow: true
+  quartz:
+    job-store-type: jdbc
+    jdbc:
+      initialize-schema: never
+    properties:
+      org:
+        quartz:
+          scheduler:
+            instanceName: NbScheduler
+            instanceId: AUTO
+          jobStore:
+            class: org.quartz.impl.jdbcjobstore.JobStoreTX
+            driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
+            tablePrefix: QRTZ_
+            isClustered: true
+            clusterCheckinInterval: 10000
+            useProperties: false
+            dataSource: myDS
+          threadPool:
+            class: org.quartz.simpl.SimpleThreadPool
+            threadCount: 10
+            threadPriority: 5
+            threadsInheritContextClassLoaderOfInitializingThread: true
+          dataSource:
+            myDS:
+              provider: hikaricp
+              driver: com.mysql.cj.jdbc.Driver
+              URL: jdbc:mysql://192.168.100.32:3306/nbnetpump?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&autoReconnect=true
+              user: root
+              password: 123456
+              validationQuery: SELECT 1
   rabbitmq:
     host: 192.168.100.32
     port: 5672
@@ -109,7 +161,6 @@ iot:
         aliyunUid: "1238892013759131"
         regionId: "cn-shanghai"
         iotInstanceId: "iot-060a0bgd"
-        enable: false
         productKey: he1fACg7ySx
     - enable: false
       consumer: com.nb.aliyun.service.consumer.NBAndFourGConsumerGroupService
@@ -121,7 +172,6 @@ iot:
         aliyunUid: "1777492656465771"
         regionId: "cn-shanghai"
         iotInstanceId: "iot-06z00hi2guq5qi3"
-        enable: false
         productKey: k0g9w9xRhhi
 aliyun:
   accessKey: "LTAI4G7FA9ytMc76oNkJ45YJ"
@@ -136,4 +186,4 @@ oss:
 
 notify:
   wechat:
-    url: https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=c3e093fe-5125-47d5-a171-0f4be2f61a78
+    url: https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=c3e093fe-5125-47d5-a171-0f4be2f61a78

+ 31 - 0
nb-admin/src/main/resources/application-prod.yml

@@ -92,6 +92,37 @@ spring:
             multi-statement-allow: true
       #合并多个DruidDataSource的监控数据
       use-global-data-source-stat: true
+  quartz:
+    job-store-type: jdbc
+    jdbc:
+      initialize-schema: never
+    properties:
+      org:
+        quartz:
+          scheduler:
+            instanceName: NbScheduler
+            instanceId: AUTO
+          jobStore:
+            class: org.quartz.impl.jdbcjobstore.JobStoreTX
+            driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
+            tablePrefix: QRTZ_
+            isClustered: true
+            clusterCheckinInterval: 10000
+            useProperties: false
+            dataSource: myDS
+          threadPool:
+            class: org.quartz.simpl.SimpleThreadPool
+            threadCount: 10
+            threadPriority: 5
+            threadsInheritContextClassLoaderOfInitializingThread: true
+          dataSource:
+            myDS:
+              provider: hikaricp
+              driver: com.mysql.cj.jdbc.Driver
+              URL: jdbc:mysql://47.101.214.91:7001/nbnetpump?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&autoReconnect=true
+              user: root
+              password: Tuoren123.
+              validationQuery: SELECT 1
   # redis 配置
   redis:
     # 地址

+ 40 - 4
nb-admin/src/main/resources/application-test.yml

@@ -57,6 +57,11 @@ spring:
       testWhileIdle: true
       testOnBorrow: false
       testOnReturn: false
+      # 通过别名的方式配置扩展插件,多个英文逗号分隔,常用的插件有:
+      # 监控统计用的filter:stat
+      # 日志用的filter:log4j2
+      # 防御sql注入的filter:wall
+      filters: stat,wall,log4j2
       webStatFilter:
         enabled: true
       statViewServlet:
@@ -65,18 +70,49 @@ spring:
         allow:
         url-pattern: /druid/*
         # 控制台管理用户名和密码
-        login-username: scott
-        login-password: tiger
+        login-username: admin
+        login-password: admin
       filter:
         stat:
           enabled: true
           # 慢SQL记录
           log-slow-sql: true
-          slow-sql-millis: 1000
+          slow-sql-millis: 10000
           merge-sql: true
         wall:
           config:
             multi-statement-allow: true
+  quartz:
+    job-store-type: jdbc
+    jdbc:
+      initialize-schema: never
+    properties:
+      org:
+        quartz:
+          scheduler:
+            instanceName: NbScheduler
+            instanceId: AUTO
+          jobStore:
+            class: org.quartz.impl.jdbcjobstore.JobStoreTX
+            driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
+            tablePrefix: QRTZ_
+            isClustered: true
+            clusterCheckinInterval: 10000
+            useProperties: false
+            dataSource: myDS
+          threadPool:
+            class: org.quartz.simpl.SimpleThreadPool
+            threadCount: 10
+            threadPriority: 5
+            threadsInheritContextClassLoaderOfInitializingThread: true
+          dataSource:
+            myDS:
+              provider: hikaricp
+              driver: com.mysql.cj.jdbc.Driver
+              URL: jdbc:mysql://localhost:3306/coffee-test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&autoReconnect=true
+              user: root
+              password: 123456
+              validationQuery: SELECT 1
   # redis 配置
   redis:
     # 地址
@@ -98,4 +134,4 @@ spring:
         # 连接池的最大数据库连接数
         max-active: 8
         # #连接池最大阻塞等待时间(使用负值表示没有限制)
-        max-wait: -1ms
+        max-wait: -1ms

+ 2 - 2
nb-admin/src/main/resources/application.yml

@@ -64,8 +64,8 @@ logging:
     path: ./logs
   logback:
     rollingpolicy:
-      max-file-size: 10MB
-      max-history: 7
+      max-file-size: 100MB
+      max-history: 15
   charset:
     console: UTF-8
 

+ 27 - 15
nb-admin/src/main/resources/logback-spring.xml

@@ -22,12 +22,16 @@
 
     <!-- 控制台输出 -->
     <appender name="sys_console" class="ch.qos.logback.core.rolling.RollingFileAppender">
-            <file>${LOG_PATH}/sys-console.log</file>
-        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+        <file>${LOG_PATH}/sys-console.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
             <!-- 日志文件名格式 -->
-            <fileNamePattern>${LOG_PATH}/sys-console.%d{yyyy-MM-dd}.log</fileNamePattern>
-            <!-- 日志最大 1天 -->
-            <maxHistory>1</maxHistory>
+            <fileNamePattern>${LOG_PATH}/sys-console.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <!-- 日志最大的历史 15天 -->
+            <maxHistory>15</maxHistory>
+            <!-- 单个文件最大100MB -->
+            <maxFileSize>100MB</maxFileSize>
+            <!-- 总日志保留大小 -->
+            <totalSizeCap>10GB</totalSizeCap>
         </rollingPolicy>
         <encoder>
             <pattern>${log.pattern}</pattern>
@@ -42,12 +46,16 @@
     <!-- 系统日志输出 -->
     <appender name="sys_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
         <file>${LOG_PATH}/sys-info.log</file>
-        <!-- 循环政策:基于时间创建日志文件 -->
-        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+        <!-- 循环政策:基于时间和大小创建日志文件 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
             <!-- 日志文件名格式 -->
-            <fileNamePattern>${LOG_PATH}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
-            <!-- 日志最大的历史 60天 -->
+            <fileNamePattern>${LOG_PATH}/sys-info.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <!-- 日志最大的历史 15天 -->
             <maxHistory>15</maxHistory>
+            <!-- 单个文件最大100MB -->
+            <maxFileSize>100MB</maxFileSize>
+            <!-- 总日志保留大小 -->
+            <totalSizeCap>10GB</totalSizeCap>
         </rollingPolicy>
         <encoder>
             <pattern>${log.pattern}</pattern>
@@ -64,12 +72,16 @@
 
     <appender name="sys_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
         <file>${LOG_PATH}/sys-error.log</file>
-        <!-- 循环政策:基于时间创建日志文件 -->
-        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+        <!-- 循环政策:基于时间和大小创建日志文件 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
             <!-- 日志文件名格式 -->
-            <fileNamePattern>${LOG_PATH}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
-            <!-- 日志最大的历史 60天 -->
-            <maxHistory>60</maxHistory>
+            <fileNamePattern>${LOG_PATH}/sys-error.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <!-- 日志最大的历史 15天 -->
+            <maxHistory>15</maxHistory>
+            <!-- 单个文件最大100MB -->
+            <maxFileSize>100MB</maxFileSize>
+            <!-- 总日志保留大小 -->
+            <totalSizeCap>10GB</totalSizeCap>
         </rollingPolicy>
         <encoder>
             <pattern>${log.pattern}</pattern>
@@ -98,4 +110,4 @@
         <appender-ref ref="console"/>
     </root>
 
-</configuration> 
+</configuration>

+ 70 - 0
nb-core/src/main/java/com/nb/core/utils/CronExpressionUtil.java

@@ -0,0 +1,70 @@
+package com.nb.core.utils;
+
+/**
+ * @author 
+ * @version 1.0.0
+ * @ClassName CronExpressionUtil.java
+ * @Description Cron表达式工具类,提供将分钟数转换为Cron表达式的功能
+ * @createTime 2025年11月14日
+ */
+public class CronExpressionUtil {
+
+    /**
+     * 将分钟数转换为Cron表达式
+     * 
+     * @param minutes 间隔分钟数
+     * @return 对应的Cron表达式
+     * @throws IllegalArgumentException 当分钟数小于等于0时抛出异常
+     */
+    public static String minutesToCronExpression(int minutes) {
+        if (minutes <= 0) {
+            throw new IllegalArgumentException("分钟数必须大于0");
+        }
+        
+        // 对于大于等于60分钟的间隔,使用秒/分/时/日的表达式
+        if (minutes >= 60) {
+            // 使用 "0 0 0/小时 * * ?" 格式,表示从0点开始,每X小时执行一次
+            int hours = minutes / 60;
+            int remainingMinutes = minutes % 60;
+            
+            // 如果正好是整小时
+            if (remainingMinutes == 0) {
+                if (hours >= 24) {
+                    // 如果超过24小时,则转换为天
+                    int days = hours / 24;
+                    return String.format("0 0 0 */%d * ?", days);
+                } else {
+                    // 整小时表达式
+                    return String.format("0 0 0/%d * * ?", hours);
+                }
+            } else {
+                // 非整小时,使用 "0 分 0/小时 * * ?" 格式
+                if (hours >= 24) {
+                    // 如果超过24小时,转换为天
+                    int days = hours / 24;
+                    return String.format("0 %d 0 */%d * ?", remainingMinutes, days);
+                } else {
+                    return String.format("0 %d 0/%d * * ?", remainingMinutes, hours);
+                }
+            }
+        }
+        
+        // 分钟表达式(小于60分钟)
+        return String.format("0 0/%d * * * ?", minutes);
+    }
+    
+    /**
+     * 验证生成的Cron表达式是否有效(简单验证)
+     * 
+     * @param cronExpression Cron表达式
+     * @return 是否有效
+     */
+    public static boolean isValidCronExpression(String cronExpression) {
+        if (cronExpression == null || cronExpression.trim().isEmpty()) {
+            return false;
+        }
+        
+        String[] parts = cronExpression.split("\\s+");
+        return parts.length >= 6;
+    }
+}

+ 70 - 0
nb-core/src/main/java/com/nb/core/utils/DateCompareUtil.java

@@ -0,0 +1,70 @@
+package com.nb.core.utils;
+
+import cn.hutool.core.date.DateUtil;
+
+import java.util.Date;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName DateCompareUtil.java
+ * @Description 日期比较工具类,提供按不同精度进行日期比较的方法
+ * @createTime 2025年11月14日
+ */
+public class DateCompareUtil {
+
+    /**
+     * 按年月日时分秒进行比较,忽略毫秒部分
+     *
+     * @param date1 第一个日期
+     * @param date2 第二个日期
+     * @return 如果两个日期的年月日时分秒相同返回true,否则返回false
+     */
+    public static boolean isSameDateTimeIgnoreMillis(Date date1, Date date2) {
+        if (date1 == null && date2 == null) {
+            return true;
+        }
+        if (date1 == null || date2 == null) {
+            return false;
+        }
+
+        String format = "yyyy-MM-dd HH:mm:ss";
+        return DateUtil.format(date1, format).equals(DateUtil.format(date2, format));
+    }
+
+    /**
+     * 按年月日进行比较
+     *
+     * @param date1 第一个日期
+     * @param date2 第二个日期
+     * @return 如果两个日期的年月日相同返回true,否则返回false
+     */
+    public static boolean isSameDay(Date date1, Date date2) {
+        if (date1 == null && date2 == null) {
+            return true;
+        }
+        if (date1 == null || date2 == null) {
+            return false;
+        }
+
+        return DateUtil.isSameDay(date1, date2);
+    }
+
+    /**
+     * 按年月日时分秒毫秒进行精确比较
+     *
+     * @param date1 第一个日期
+     * @param date2 第二个日期
+     * @return 如果两个日期完全相同返回true,否则返回false
+     */
+    public static boolean isExactlySame(Date date1, Date date2) {
+        if (date1 == null && date2 == null) {
+            return true;
+        }
+        if (date1 == null || date2 == null) {
+            return false;
+        }
+
+        return date1.equals(date2);
+    }
+}

+ 74 - 81
nb-service/web-service/src/main/java/com/nb/web/service/bus/hospital/HospitalManager.java

@@ -1,23 +1,14 @@
 package com.nb.web.service.bus.hospital;
 
-import cn.hutool.core.bean.BeanUtil;
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.collection.CollectionUtil;
-import cn.hutool.core.date.DateUnit;
-import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.ObjectUtil;
-import cn.hutool.core.util.RandomUtil;
 import cn.hutool.core.util.StrUtil;
-import cn.hutool.cron.CronUtil;
 import cn.hutool.extra.spring.SpringUtil;
 import cn.hutool.json.JSONUtil;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.nb.core.utils.CronExpressionUtil;
 import com.nb.web.api.bean.Script;
 import com.nb.web.api.entity.BusHospitalConfigEntity;
-import com.nb.web.api.entity.BusInfusionHistoryEntity;
-import com.nb.web.api.feign.query.PatientMonitorQuery;
-import com.nb.web.api.feign.result.PatientMonitorResult;
+import com.nb.web.api.entity.common.BusDeviceRunningEntity;
 import com.nb.web.service.bus.entity.*;
 import com.nb.web.service.bus.hospital.config.*;
 import com.nb.web.service.bus.hospital.config.bean.*;
@@ -27,16 +18,14 @@ import com.nb.web.service.bus.hospital.script.ScriptManager;
 import com.nb.web.service.bus.service.*;
 import com.nb.core.cache.ConfigStorage;
 import com.nb.core.cache.manager.ConfigStorageManager;
-import com.nb.web.api.entity.common.BusDeviceRunningEntity;
 import lombok.Data;
 import lombok.extern.slf4j.Slf4j;
+import org.quartz.*;
 
 import java.util.*;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
 
 /**
  * @author lifang
@@ -87,6 +76,13 @@ public class HospitalManager {
     private LocalBusPatientService patientService;
 
     private LocalBusInfusionHistoryService infusionHistoryService;
+    
+    // 注入Quartz调度器
+    private Scheduler quartzScheduler;
+    
+    // 任务组名
+    private static final String JOB_GROUP = "HIS_DATA_PULL_GROUP";
+    
     public HospitalManager(String hospitalId,
                            ScriptManager scriptManager,
                            ConfigStorageManager configStorageManager) {
@@ -102,36 +98,8 @@ public class HospitalManager {
         this.codeHandler=new HospitalPatientCodeHandler(storage,hospitalId);
         this.infusionHistoryService=SpringUtil.getBean(LocalBusInfusionHistoryService.class);
         this.patientService=SpringUtil.getBean(LocalBusPatientService.class);
+        this.quartzScheduler = SpringUtil.getBean(Scheduler.class);
         init(configStorageManager);
-
-//        singleOffsetsDeviceExecutor.scheduleWithFixedDelay(()->{
-//                    try {
-//                        PatientMonitorQuery query = new PatientMonitorQuery();
-//                        query.setTenantId(this.getHospitalId());
-//                        query.setInfusionFinished(true);
-//                        query.setDeviceStatus(Arrays.asList(0,5));
-//                        List<PatientMonitorResult> patientMonitorResults = patientService.selectAll(query);
-//                        if(CollectionUtil.isEmpty(patientMonitorResults)){
-//                            return;
-//                        }
-//                        patientMonitorResults
-//                                .forEach(infusionInfo->{
-//                                    BusDeviceRunningEntity source = BeanUtil.toBean(infusionInfo, BusDeviceRunningEntity.class);
-//                                    source.setRunState(infusionInfo.getDeviceRunState());
-//                                    source.setAlarm(infusionInfo.getDeviceAlarm());
-//                                    source.setInfusionId(infusionInfo.getInfusionId());
-//                                    source.setUploadTime(infusionInfo.getLastUploadTime());
-//                                    source.setTenantId(this.getHospitalId());
-//                                    extraConfigHandler.handler(source);
-//                                    autoUndoConfigHandler.handler(source);
-//                                    finishMonitorConfigHandler.handler(source);
-//                                });
-//                    }catch (Exception e){
-//                        e.printStackTrace();
-//                    }
-//                },
-//                //1-10分钟内启动,间隔1天启动
-//                RandomUtil.randomInt(1,10),  Math.addExact(1440, RandomUtil.randomInt(1,12)),TimeUnit.MINUTES);
     }
 
     private void init(ConfigStorageManager configStorageManager){
@@ -150,7 +118,7 @@ public class HospitalManager {
      */
     public void refreshInfo(){
         BusHospitalEntity hospital = hospitalService.getById(hospitalId);
-        log.info("医院【{}】,刷新信息,info【{}】",hospitalId,hospital);
+        log.info("医院【{}】,刷新信息,info【{}】",hospitalId,hospital.getName());
         storage.setConfig("info",hospital);
         storage.setConfig("strategy",hospital.getStrategy());
 
@@ -254,53 +222,78 @@ public class HospitalManager {
             return;
         }
         if(!Boolean.TRUE.equals(updateConfig.getEnable())||updateConfig.getInterval()<0){
-            if(hisSchedule !=null&&!hisSchedule.isCancelled()){
-                //如果有定时任务正在运行,则关闭
-                hisSchedule.cancel(true);
-            }
+            // 如果有定时任务正在运行,则关闭
+            cancelQuartzJob();
             return;
         }
         //参数未改变
         if(ObjectUtil.isNotNull(this.getUpdateConfig())&&ObjectUtil.equals(this.getUpdateConfig().getInterval(),updateConfig.getInterval())){
             return;
         }else {
-            if(hisSchedule!=null){
-                //取消上次循环
-                hisSchedule.cancel(true);
-            }
-            hisSchedule = singleHisExecutor.scheduleAtFixedRate(this::scheduleHis, 0, updateConfig.getInterval(),TimeUnit.MINUTES);
+            // 取消之前的任务
+            cancelQuartzJob();
+            
+            // 创建新的Quartz任务
+            createQuartzJob(updateConfig);
         }
         this.updateConfig=updateConfig;
     }
-
+    
     /**
-     * 描述: 定时从his拉取数据
-     * @author lifang
-     * @date 2022/6/18 15:20
-     * @param
-     * @return void
+     * 取消Quartz任务
      */
-    private void scheduleHis(){
-        log.info("[定时拉取病人数据],hospitalId:{}",hospitalId);
-        //拉取最新的50条输注信息,更新病人数据
-        Page<BusInfusionHistoryEntity> infusionHistoryPage = new Page<>(0,50,false);
-        infusionHistoryPage= infusionHistoryService.page(infusionHistoryPage, new QueryWrapper<BusInfusionHistoryEntity>().lambda().eq(BusInfusionHistoryEntity::getTenantId, hospitalId)
-                .eq(BusInfusionHistoryEntity::getFinished,false)
-                .orderByDesc(BusInfusionHistoryEntity::getLastUploadTime));
-        List<BusInfusionHistoryEntity> records = infusionHistoryPage.getRecords();
-        if(CollUtil.isNotEmpty(records)){
-            records.stream().map(BusInfusionHistoryEntity::getPatientCode).collect(Collectors.toSet())
-                    .forEach(patientCode->{
-                        log.info("[拉取病人信息],hospitalId:{},patientCode:{}",hospitalId,patientCode);
-                        try {
-                            this.getScriptSession()
-                                    .asyncGetPatientInfo(patientCode,3,TimeUnit.SECONDS,false);
-                        }catch (Exception e){
-                            log.warn("[拉取病人信息失败],",e);
-                        }
-
-                    });
+    private void cancelQuartzJob() {
+        try {
+            String jobName = "HIS_DATA_PULL_" + hospitalId;
+            JobKey jobKey = JobKey.jobKey(jobName, JOB_GROUP);
+            
+            // 检查任务是否存在
+            if (quartzScheduler.checkExists(jobKey)) {
+                // 暂停并删除任务
+                quartzScheduler.deleteJob(jobKey);
+                log.info("成功取消HIS数据拉取任务,hospitalId:{}", hospitalId);
+            }
+        } catch (SchedulerException e) {
+            log.warn("取消HIS数据拉取任务失败,hospitalId:{}", hospitalId, e);
         }
     }
-
-}
+    
+    /**
+     * 创建Quartz任务
+     * @param updateConfig 更新配置
+     */
+    private void createQuartzJob(HisUpdateEntity updateConfig) {
+        try {
+            String jobName = "HIS_DATA_PULL_" + hospitalId;
+            JobKey jobKey = JobKey.jobKey(jobName, JOB_GROUP);
+            
+            // 先删除已存在的任务
+            if (quartzScheduler.checkExists(jobKey)) {
+                quartzScheduler.deleteJob(jobKey);
+            }
+            
+            // 创建JobDetail
+            JobDetail jobDetail = JobBuilder.newJob(HospitalHisDataPullJob.class)
+                    .withIdentity(jobKey)
+                    .storeDurably()
+                    .usingJobData("hospitalId", hospitalId)
+                    .build();
+            
+            // 创建CronTrigger
+            // 根据间隔时间生成cron表达式(每X分钟执行一次)
+            String cronExpression = CronExpressionUtil.minutesToCronExpression(updateConfig.getInterval());
+            CronTrigger trigger = TriggerBuilder.newTrigger()
+                    .withIdentity("trigger_" + jobName, JOB_GROUP)
+                    .withSchedule(CronScheduleBuilder.cronSchedule(cronExpression)
+                            .withMisfireHandlingInstructionIgnoreMisfires()) // 立即执行
+                    .build();
+            
+            // 调度任务
+            quartzScheduler.scheduleJob(jobDetail, trigger);
+            
+            log.info("成功创建HIS数据拉取任务,hospitalId:{},cron表达式:{}", hospitalId, cronExpression);
+        } catch (SchedulerException e) {
+            log.error("创建HIS数据拉取任务失败,hospitalId:{}", hospitalId, e);
+        }
+    }
+}

+ 2 - 5
nb-service/web-service/src/main/java/com/nb/web/service/bus/hospital/HospitalManagerRegister.java

@@ -63,12 +63,9 @@ public class HospitalManagerRegister {
             hospitalManager.refreshScript();
         }
         if(his){
-            if(hospitalManager.getInfo()!=null){
+            if(hospitalManager.getInfo()!=null&&hospitalManager.getInfo().getUpdateConfig()!=null){
                 hospitalManager.refreshUpdateConfig(hospitalManager.getInfo().getUpdateConfig());
             }
         }
     }
-
-
-
-}
+}

+ 10 - 2
nb-service/web-service/src/main/java/com/nb/web/service/bus/hospital/his/strategy/onlynew/DefaultHisNewStrategyHandler.java

@@ -1,8 +1,10 @@
 package com.nb.web.service.bus.hospital.his.strategy.onlynew;
 
 import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.json.JSONUtil;
+import com.nb.core.utils.DateCompareUtil;
 import com.nb.web.api.entity.BusClinicEntity;
 import com.nb.web.service.bus.service.LocalBusClinicService;
 import lombok.AllArgsConstructor;
@@ -53,7 +55,8 @@ public class DefaultHisNewStrategyHandler implements HisNewStrategyHandler {
             target.sort(Comparator.comparing(BusClinicEntity::getStartTime));
             //获取最新的临床数据
             BusClinicEntity newClinic = target.get(target.size() - 1);
-            if(clinic.getStartTime().equals(newClinic.getStartTime())){
+            // 使用按年月日时分秒进行比较的方式判断是否为同一天数据
+            if(DateCompareUtil.isSameDateTimeIgnoreMillis(clinic.getStartTime(), newClinic.getStartTime())){
                 //临床数据未发生变化
                 clinicService.compareFromHis(clinic,newClinic);
                 insert=false;
@@ -76,4 +79,9 @@ public class DefaultHisNewStrategyHandler implements HisNewStrategyHandler {
     public boolean apply(List<BusClinicEntity> source, List<BusClinicEntity> target) {
         return CollectionUtil.isNotEmpty(source);
     }
-}
+
+    public static void main(String[] args) {
+        System.out.println(DateCompareUtil.isSameDateTimeIgnoreMillis(DateUtil.parse("2025-08-01T00:00:00.004"),DateUtil.parse("2025-08-01T00:00:00")));
+        System.out.println(DateUtil.parse("2025-08-01T00:00:00").equals(DateUtil.parse("2025-08-01T00:00:00")));
+    }
+}

+ 2 - 1
nb-service/web-service/src/main/java/com/nb/web/service/bus/hospital/his/strategy/part/DefaultHisPartStrategyHandler.java

@@ -3,6 +3,7 @@ package com.nb.web.service.bus.hospital.his.strategy.part;
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.json.JSONUtil;
 import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
+import com.nb.core.utils.DateCompareUtil;
 import com.nb.web.api.entity.BusClinicEntity;
 import com.nb.web.service.bus.hospital.his.strategy.HisStrategyHandler;
 import com.nb.web.service.bus.hospital.his.strategy.all.HisAllStrategyHandler;
@@ -58,7 +59,7 @@ public class DefaultHisPartStrategyHandler implements HisStrategyHandler {
             int start=0;
             for (; start < target.size(); start++) {
                 BusClinicEntity existClinic = target.get(start);
-                if(existClinic.getStartTime().equals(earliest.getStartTime())){
+                if(DateCompareUtil.isSameDateTimeIgnoreMillis(existClinic.getStartTime(),earliest.getStartTime())){
                     break;
                 }
                 if(existClinic.getStartTime().after(earliest.getStartTime())){

+ 4 - 1
nb-service/web-service/src/main/java/com/nb/web/service/bus/service/LocalBusClinicService.java

@@ -12,6 +12,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.nb.core.utils.DateCompareUtil;
 import com.nb.web.api.bean.FormulaDrugDetailDomain;
 import com.nb.web.api.entity.BusDeviceEntity;
 import com.nb.web.api.entity.BusEvaluationEntity;
@@ -194,7 +195,7 @@ public class LocalBusClinicService extends BaseService<BusClinicMapper, BusClini
         source.setFinished(target.getFinished());
         this.updateById(source);
         //手术开始时间发生变化,重新计算输注数据
-        if(source.getStartTime()!=null&&source.getStartTime()!=target.getStartTime()){
+        if(source.getStartTime()!=null&&!DateCompareUtil.isSameDateTimeIgnoreMillis(source.getStartTime(),target.getStartTime())){
             infusionHistoryService.adjustInfusionByClinic(source.getId(),source.getPatientId(),source.getTenantId(),source.getStartTime());
         }
     }
@@ -361,6 +362,7 @@ public class LocalBusClinicService extends BaseService<BusClinicMapper, BusClini
         List<BusClinicEntity> clinicList = this.baseMapper.selectList(new LambdaQueryWrapper<BusClinicEntity>()
                 .in(BusClinicEntity::getId, clinicIds)
                 .orderByDesc(BusClinicEntity::getStartTime));
+
         if(CollectionUtil.isEmpty(clinicList)){
             return ExportExcelUtils.exportClinicExcelBase64(result);
         }
@@ -387,6 +389,7 @@ public class LocalBusClinicService extends BaseService<BusClinicMapper, BusClini
             if (CollectionUtil.isNotEmpty(infusions)) {
                 for (BusInfusionHistoryEntity infusion : infusions) {
                     BusClinicExcelVO clinicExcel = BeanUtil.copyProperties(clinicEntity, BusClinicExcelVO.class);
+                    clinicExcel.setBedNo(clinicEntity.getBedNo());
                     clinicExcel.fillInfusion(infusion);
 
                     List<BusEvaluationEntity> excelEvals = clinicMap.get(infusion.getId());

+ 3 - 4
nb-service/web-service/src/main/java/com/nb/web/service/bus/service/LocalBusPatientService.java

@@ -17,7 +17,7 @@ import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.toolkit.IdWorker;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.nb.app.assistant.api.feign.result.ContactQuery;
+import com.nb.app.assistant.api.feign.result .ContactQuery;
 import com.nb.app.doctor.api.entity.AppDoctorUserEntity;
 import com.nb.app.doctor.api.entity.AppUserConsultConfigEntity;
 import com.nb.app.doctor.api.feign.IAppDoctorUserClient;
@@ -35,7 +35,6 @@ import com.nb.web.api.feign.IPatientClient;
 import com.nb.web.api.feign.query.DoctorPatientMonitorQuery;
 import com.nb.web.api.feign.result.*;
 import com.nb.web.api.feign.query.PatientMonitorQuery;
-import com.nb.web.service.bus.controller.vo.BusLiquidListVO;
 import com.nb.web.service.bus.entity.*;
 import com.nb.web.api.enums.DeviceAlarmEnum;
 import com.nb.web.api.enums.DeviceStatusEnum;
@@ -825,7 +824,7 @@ public class LocalBusPatientService extends BaseService<BusPatientMapper, BusPat
                 result.setEvaluate(3);
             }else {
                 DateTime date = DateUtil.offset(result.getEvalTime(), DateField.MINUTE, interval);
-                result.setEvaluate(ObjectUtil.compare(date,now)<0?2:1);
+                result.setEvaluate(ObjectUtil.compare(date,now)>0?1:2);
             }
         }
         return results;
@@ -852,4 +851,4 @@ public class LocalBusPatientService extends BaseService<BusPatientMapper, BusPat
         BusClinicEntity clinic = clinicMapper.selectById(clinicId);
         return clinic;
     }
-}
+}

+ 0 - 5
nb-service/web-service/src/main/java/com/nb/web/service/quartz/job/service/impl/SysJobServiceImpl.java

@@ -38,11 +38,6 @@ public class SysJobServiceImpl implements ISysJobService {
     public void init() throws SchedulerException, TaskException
     {
         scheduler.clear();
-        List<SysJobPO> jobList = jobMapper.selectJobAll();
-        for (SysJobPO job : jobList)
-        {
-            ScheduleUtils.createScheduleJob(scheduler, job);
-        }
     }
     @Override
     public List<SysJobPO> selectJobList(SysJobPO job) {

+ 105 - 0
nb-service/web-service/src/main/java/com/nb/web/service/quartz/task/HospitalHisDataPullTask.java

@@ -0,0 +1,105 @@
+package com.nb.web.service.quartz.task;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.nb.web.api.entity.BusInfusionHistoryEntity;
+import com.nb.web.service.bus.hospital.HospitalManager;
+import com.nb.web.service.bus.hospital.HospitalManagerRegister;
+import com.nb.web.service.bus.service.LocalBusHospitalService;
+import com.nb.web.service.bus.service.LocalBusInfusionHistoryService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+/**
+ * 医院HIS数据拉取任务
+ * 用于定时从HIS系统拉取病人信息
+ */
+@Component("hospitalHisDataPullTask")
+@Slf4j
+public class HospitalHisDataPullTask {
+
+    @Autowired
+    private LocalBusHospitalService hospitalService;
+
+    @Autowired
+    private LocalBusInfusionHistoryService infusionHistoryService;
+
+    @Autowired
+    private HospitalManagerRegister managerRegister;
+
+    /**
+     * 定时拉取HIS数据
+     * @param hospitalId 医院ID,如果为空则拉取所有医院的数据
+     */
+    public void pullHisData(String hospitalId) {
+        log.info("[定时拉取病人数据]开始执行任务,hospitalId:{}", hospitalId);
+        
+        try {
+            // 如果指定了医院ID,则只处理该医院的数据
+            if (StrUtil.isNotEmpty(hospitalId)) {
+                pullHospitalData(hospitalId);
+            } else {
+                // 否则处理所有医院的数据
+                List<String> hospitalIds = hospitalService.list().stream()
+                        .map(entity -> entity.getId())
+                        .collect(Collectors.toList());
+                for (String hid : hospitalIds) {
+                    pullHospitalData(hid);
+                }
+            }
+            
+            log.info("[定时拉取病人数据]任务执行完成,hospitalId:{}", hospitalId);
+        } catch (Exception e) {
+            log.error("[定时拉取病人数据]任务执行异常,hospitalId:{}", hospitalId, e);
+        }
+    }
+
+    /**
+     * 拉取指定医院的数据
+     * @param hospitalId 医院ID
+     */
+    private void pullHospitalData(String hospitalId) {
+        try {
+            // 获取医院管理器
+            HospitalManager hospitalManager = managerRegister.get(hospitalId);
+            if (hospitalManager == null) {
+                log.warn("[定时拉取病人数据]未找到医院管理器,hospitalId:{}", hospitalId);
+                return;
+            }
+            // 拉取最新的9999条输注信息,更新病人数据
+            Page<BusInfusionHistoryEntity> infusionHistoryPage = new Page<>(0, 9999, false);
+            infusionHistoryPage = infusionHistoryService.page(
+                    infusionHistoryPage,
+                    new QueryWrapper<BusInfusionHistoryEntity>().lambda()
+                            .eq(BusInfusionHistoryEntity::getTenantId, hospitalId)
+                            .eq(BusInfusionHistoryEntity::getFinished, false)
+                            .orderByDesc(BusInfusionHistoryEntity::getLastUploadTime)
+            );
+            List<BusInfusionHistoryEntity> records = infusionHistoryPage.getRecords();
+            if (CollUtil.isNotEmpty(records)) {
+                Set<String> patientCodes = records.stream()
+                        .map(BusInfusionHistoryEntity::getPatientCode)
+                        .collect(Collectors.toSet());
+                for (String patientCode : patientCodes) {
+                    log.info("[拉取病人信息]hospitalId:{}, patientCode:{}", hospitalId, patientCode);
+                    try {
+                        hospitalManager.getScriptSession()
+                                .asyncGetPatientInfo(patientCode, 3, TimeUnit.SECONDS, false);
+                    } catch (Exception e) {
+                        log.warn("[拉取病人信息失败]hospitalId:{}, patientCode:{}", hospitalId, patientCode, e);
+                    }
+                }
+            }
+        } catch (Exception e) {
+            log.error("[定时拉取病人数据]处理医院数据异常,hospitalId:{}", hospitalId, e);
+        }
+    }
+}

+ 1 - 1
nb-service/web-service/src/main/java/com/nb/web/service/quartz/util/AbstractQuartzJob.java

@@ -93,7 +93,7 @@ public abstract class AbstractQuartzJob implements Job
         }
 
         // 写入数据库当中
-        SpringUtils.getBean(ISysJobLogService.class).addJobLog(sysJobLog);
+//        SpringUtils.getBean(ISysJobLogService.class).addJobLog(sysJobLog);
     }
 
     /**

+ 8 - 8
nb-service/web-service/src/main/resources/mapper/bus/BusPatientMapper.xml

@@ -46,7 +46,7 @@
         <result column="max_dose" property="maxDose"/>
         <result column="last_upload_time" property="lastUploadTime"/>
         <result column="product_no" property="productNo"/>
-        <!--<result column="self_control_count" property="selfControlCount"/>-->
+<!--        <result column="self_control_count" property="selfControlCount"/>-->
         <result column="self_control_lock_time" property="selfControlLockTime"/>
         <result column="pca_valid_count" property="pcaValidCount"/>
         <result column="pca_invalid_count" property="pcaInvalidCount"/>
@@ -128,7 +128,7 @@
         c.`patient_name` as name,
         c.patient_age as age,
         c.ward,
-        c.bed_no,
+        COALESCE(c.manual_bedno, c.bed_no) as bed_no,
         c.id as clinic_id,
         c.`surgery_name` as clinic_name,
         i.device_id as device_id,
@@ -160,7 +160,7 @@
             c.`patient_name` as name,
             c.patient_age as age,
             c.ward as ward,
-            c.bed_no as bed_no,
+            COALESCE(c.manual_bedno, c.bed_no) as bed_no,
             c.`surgery_name` as surgery_name,
             c.weight as weight,
             c.height as height,
@@ -245,7 +245,7 @@
         c.surgery_doctor as surgery_doctor,
         c.surgery_name as surgery_name,
         c.ward as ward,
-        c.bed_no as bed_no,
+        COALESCE(c.manual_bedno, c.bed_no) as bed_no,
         c.eval_time as eval_time,
         c.manage_type as manage_type,
         c.abnormal as abnormal,
@@ -458,7 +458,7 @@
         c.surgery_doctor as surgery_doctor,
         c.surgery_name as surgery_name,
         c.ward as ward,
-        c.bed_no as bed_no,
+        COALESCE(c.manual_bedno, c.bed_no) as bed_no,
         c.eval_time as eval_time,
         bh.code as hospital_code
         from
@@ -570,7 +570,7 @@
         <!-- 临床信息 -->
         c.patient_name AS patient_name,
         c.ward AS ward,
-        c.bed_no AS bed_no,
+        COALESCE(i.bed_no, c.bed_no) as bed_no,
         c.ana_doctor AS ana_doctor,
         c.surgery_name AS surge_name,
         c.ana_type AS ana_type,
@@ -646,7 +646,7 @@
                 </foreach>
             </if>
             <if test="query.bedNo != null">
-                AND c.bed_no LIKE CONCAT('%', #{query.bedNo}, '%')
+                AND IFNULL(c.manual_bedno, c.bed_no) LIKE CONCAT('%', #{query.bedNo}, '%')
             </if>
             <if test="query.anaDoctor != null">
                 AND c.ana_doctor LIKE CONCAT('%', #{query.anaDoctor}, '%')
@@ -722,4 +722,4 @@
             order by c.bed_no ASC  <!-- 按病区和床号升序 -->
         </if>
     </select>
-</mapper>
+</mapper>