| | |
| | | import org.apache.cxf.common.util.CollectionUtils; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.transaction.support.TransactionSynchronization; |
| | | import org.springframework.transaction.support.TransactionSynchronizationManager; |
| | | import org.springframework.validation.annotation.Validated; |
| | | |
| | | import cn.lihu.jh.module.ecg.dal.dataobject.room.RoomDO; |
| | |
| | | |
| | | @Override |
| | | public void startHurryUpOneBed(Long roomId, String bedNo) { |
| | | singleThreadExecutor.execute(() -> { |
| | | queueServiceTxFunctions.hurryupOneBed(roomId, bedNo); |
| | | }); |
| | | // 【缺陷修复】必须等事务提交后再跑:见 runAfterCommit 的说明 |
| | | runAfterCommit(() -> singleThreadExecutor.execute(() -> { |
| | | try { |
| | | queueServiceTxFunctions.hurryupOneBed(roomId, bedNo); |
| | | } catch (Exception e) { |
| | | // 原实现不捕获:异常只留在工作线程里,日志一条都没有,现场无从排查 |
| | | log.error("[hurryupOneBed] 自动补人执行异常. 工位={}/{}(不影响主流程,下一轮会重试)", |
| | | roomId, bedNo, e); |
| | | } |
| | | })); |
| | | } |
| | | |
| | | /** |
| | | * 把「触发自动分流」推迟到**事务提交之后**再执行;不在事务里则直接执行。 |
| | | * |
| | | * <p>【缺陷修复·现场反馈】运动试验签到后,患者一直停在医生工作台的 |
| | | * 「本诊室排队中(未分配工位)」,必须手工点「拉入」才进候诊区。 |
| | | * |
| | | * <p>原因:{@link #queue} 是 {@code @Transactional},插入「排队中」行之后**立即**把 |
| | | * 分流任务丢给 {@code singleThreadExecutor}。工作线程用的是**另一个数据库连接**, |
| | | * READ COMMITTED 下看不到主事务尚未提交的那一行 ⇒ |
| | | * {@code getFirstWaitingByCheckTypes} 返回 null ⇒ 这次分流什么都没做就退出; |
| | | * 事务提交后又没有任何补偿 ⇒ 患者永远留在「排队中」。 |
| | | * |
| | | * <p>为什么低频检查项目(运动试验、食道电生理)几乎必然中招、高频的却"看起来正常": |
| | | * 执行器平时空闲,任务能**立刻**执行(必然早于提交);高频项目任务会排队, |
| | | * 轮到时事务往往已经提交,于是侥幸成功。 |
| | | */ |
| | | private void runAfterCommit(Runnable task) { |
| | | if (TransactionSynchronizationManager.isSynchronizationActive()) { |
| | | TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { |
| | | @Override |
| | | public void afterCommit() { |
| | | task.run(); |
| | | } |
| | | }); |
| | | } else { |
| | | task.run(); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | |
| | | |
| | | @Override |
| | | public void startHurryUpOneCheckType(Integer checkType) { |
| | | singleThreadExecutor.execute(() -> { |
| | | queueServiceTxFunctions.hurryupOneCheckType(checkType); |
| | | }); |
| | | // 【缺陷修复】必须等事务提交后再跑:见 runAfterCommit 的说明 |
| | | runAfterCommit(() -> singleThreadExecutor.execute(() -> { |
| | | try { |
| | | queueServiceTxFunctions.hurryupOneCheckType(checkType); |
| | | } catch (Exception e) { |
| | | log.error("[hurryupOneCheckType] 自动分流执行异常. checkType={}(不影响主流程,下一轮会重试)", |
| | | checkType, e); |
| | | } |
| | | })); |
| | | } |
| | | |
| | | /** |
| | | * 兜底:对**有医生在座**的工位触发一次自动补人(由 {@code QueueAutoRefillJob} 周期调用)。 |
| | | * |
| | | * <p>注意用的是 {@code hurryupAllSeatedBeds} 而不是 {@code hurryupAllBed}: |
| | | * 后者会把患者推进"已开通但没医生"的工位,周期跑会放大该副作用。 |
| | | */ |
| | | @Override |
| | | public void startHurryUpAll() { |
| | | runAfterCommit(() -> singleThreadExecutor.execute(() -> { |
| | | try { |
| | | queueServiceTxFunctions.hurryupAllSeatedBeds(); |
| | | } catch (Exception e) { |
| | | log.error("[hurryupAllSeatedBeds] 周期补人执行异常(下一轮继续)", e); |
| | | } |
| | | })); |
| | | } |
| | | |
| | | @Override |