yxh
6 天以前 68ff33beff11310e830b0615b5a81f83fad0f683
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
package cn.lihu.jh.module.ecg.dal.mysql.queue;
 
import cn.lihu.jh.framework.common.pojo.PageResult;
import cn.lihu.jh.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.lihu.jh.framework.mybatis.core.mapper.BaseMapperX;
import cn.lihu.jh.module.ecg.dal.dataobject.queue.QueueDO;
import cn.lihu.jh.module.ecg.dal.dataobject.queue.BedQueueStatisticDO;
import cn.lihu.jh.module.ecg.dal.dataobject.queue.QueueStatisticDO;
import org.apache.ibatis.annotations.*;
import cn.lihu.jh.module.ecg.controller.admin.queue.vo.*;
 
import java.util.List;
import java.util.Map;
 
/**
 * 排队 Mapper
 *
 * @author 芋道源码
 */
@Mapper
public interface QueueMapper extends BaseMapperX<QueueDO> {
 
    default PageResult<QueueDO> selectPage(QueuePageReqVO reqVO) {
        return selectPage(reqVO, new LambdaQueryWrapperX<QueueDO>()
                .eqIfPresent(QueueDO::getPatId, reqVO.getPatId())
                .likeIfPresent(QueueDO::getPatName, reqVO.getPatName())
                .eqIfPresent(QueueDO::getPatGender, reqVO.getPatGender())
                .eqIfPresent(QueueDO::getBookTimeslot, reqVO.getBookTimeslot())
                .eqIfPresent(QueueDO::getBookCheckType, reqVO.getBookCheckType())
                .eqIfPresent(QueueDO::getSeqNum, reqVO.getSeqNum())
                .inIfPresent(QueueDO::getStatus, reqVO.getStatus())
                .eqIfPresent(QueueDO::getPassed, reqVO.getPassed())
                .eqIfPresent(QueueDO::getExpired, reqVO.getExpired())
                .eqIfPresent(QueueDO::getRoomId, reqVO.getRoomId())
                .eqIfPresent(QueueDO::getBedNo, reqVO.getBedNo())
                .betweenIfPresent(QueueDO::getCreateTime, reqVO.getCreateTime())
                .orderByDesc(QueueDO::getId));
    }
 
    @Select("SELECT * FROM lihu.queue where pat_id = #{patId} and book_check_type = #{bookCheckType}")
    @Results({
            @Result(property = "patDetails", column = "pat_details", typeHandler=com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler.class)
    })
    QueueDO getQueueByPatId(@Param("patId")String patId, @Param("bookCheckType")Integer bookCheckType);
 
    /**
     * 诊间屏({@code ecg_call/small.html})取数:本诊室的检查队列。
     *
     * <p>排序规则(号统一后的最终规则):<b>就诊中(30) 置顶 → 加急(jump_flag=1) → 其余按排队号 seq_num 升序</b>;
     * 号为空(改造当天尚未迁移的存量行)的排在最后。
     * <p>过号(3/5) <b>不再单独提前一组</b>:它与「等候」混排、留在自己的号位上,
     * 由屏上用红色 +「(过)」小字区分(需求:诊间屏「等候」与「过号」合并为一个「等候」面板)。
     */
    @Select("""
            <script>
                SELECT * FROM lihu.queue
                where room_id = #{roomId} and deleted = 0 and status in (
                 <foreach collection='statusList' separator=',' item='status'>
                   #{status} 
                 </foreach> )
                order by case
                           when status = 30 then 0
                           when jump_flag = 1 then 1
                           when seq_num is null then 3
                           else 2
                         end,
                         seq_num, book_timeslot
            </script>
            """)
    @Results({
            @Result(property = "patDetails", column = "pat_details", typeHandler=com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler.class)
    })
    List<QueueDO> getRoomQueueByStatus(@Param("roomId")Long roomId, @Param("statusList")List<Integer> statusList);
 
    /**
     * 大屏取数。
     *
     * <p>排序规则(号统一后的最终规则):<b>就诊中(30) 置顶 → 加急(jump_flag=1) → 其余按排队号 seq_num 升序</b>;
     * 号为空(改造当天尚未迁移的存量行)的排在最后。
     * <ul>
     *   <li>过号(3/5) <b>不再单独提前一组</b>:它留在自己的号位上,屏上用红色 + 过号标记区分,
     *       与诊间屏({@code ecg_call/small.html})改造后的口径一致。
     *       (原实现是 {@code status desc},会把过号压到最底部、等同于"过号不显示"。)</li>
     *   <li>加急必须参与排序:排队号改为签到时一次性分配、之后不再改动,
     *       加急无法再靠"分流时改号/插队重编号"体现。</li>
     *   <li>沿用 {@code deleted = 0}:本方法是自定义 {@code @Select},
     *       会绕过 MyBatis-Plus 的 {@code @TableLogic} 逻辑删除过滤。</li>
     * </ul>
     */
    @Select("""
            <script>
                SELECT * FROM lihu.queue
                where deleted = 0 and status in (
                 <foreach collection='statusList' separator=',' item='status'>
                   #{status} 
                 </foreach> )
                order by case
                           when status = 30 then 0
                           when jump_flag = 1 then 1
                           when seq_num is null then 3
                           else 2
                         end,
                         seq_num, book_timeslot
            </script>
            """)
    @Results({
            @Result(property = "patDetails", column = "pat_details", typeHandler=com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler.class)
    })
    List<QueueDO> getBigScreenQueueByStatus(@Param("statusList")List<Integer> statusList);
 
    @Select("<script>" +
            "SELECT * FROM lihu.queue " +
            "where pat_id = #{patId} " +
            "  and book_check_type in (" +
            "    <foreach collection='arrCheckType' separator=',' item='checkType'>" +
            "      #{checkType} " +
            "    </foreach> )" +
            "  and TO_DAYS(book_date) = TO_DAYS(NOW()) " +
            "</script>")
    @Results({
            @Result(property = "patDetails", column = "pat_details", typeHandler=com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler.class)
    })
    List<QueueDO> getCurPatGivenCheckTypes(@Param("patId")String patId, @Param("arrCheckType")Integer[] arrCheckType);
 
    @Select("<script>" +
            "SELECT * FROM lihu.queue " +
            "where pat_id = #{patId} " +
            "  and book_check_type in (" +
            "    <foreach collection='arrCheckType' separator=',' item='_checkType'>" +
            "      #{_checkType} " +
            "    </foreach> ) " +
            "  and status = #{status} " +
            "  and TO_DAYS(book_date) = TO_DAYS(NOW()) " +
            "</script>")
    @Results({
            @Result(property = "patDetails", column = "pat_details", typeHandler=com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler.class)
    })
    List<QueueDO> getCurPatGivenCheckTypesAndStatus(@Param("patId")String patId, @Param("arrCheckType")Integer[] arrCheckType, @Param("status")Integer status);
 
    @Select("<script>" +
            "SELECT * FROM lihu.queue " +
            "where pat_id = #{patId} " +
            "  and book_check_type in (" +
            "    <foreach collection='arrCheckType' separator=',' item='_checkType'>" +
            "      #{_checkType} " +
            "    </foreach> ) " +
            "  and status in (" +
            "    <foreach collection='statusList' separator=',' item='_status'>" +
            "      #{_status} " +
            "    </foreach> ) " +
            "  and TO_DAYS(book_date) = TO_DAYS(NOW()) " +
            "</script>")
    @Results({
            @Result(property = "patDetails", column = "pat_details", typeHandler=com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler.class)
    })
    List<QueueDO> getCurPatGivenCheckTypesAndStatusList(@Param("patId")String patId, @Param("arrCheckType")Integer[] arrCheckType, @Param("statusList")List<Integer> statusList);
 
    @Select("<script>" +
            "SELECT * FROM lihu.queue " +
            "where id >= #{from} and status in (" +
            " <foreach collection='statusList' separator=',' item='status'>" +
            "  #{status} " +
            " </foreach> )" +
            " limit #{size} " +
            " order by id" +
            "</script>")
    @Results({
            @Result(property = "patDetails", column = "pat_details", typeHandler=com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler.class)
    })
    List<QueueDO> getPartialQueueByStatus( @Param("statusList")List<Integer> statusList, @Param("from") Integer from, @Param("size") Integer size);
 
    @Select("<script>" +
            "SELECT count(1) as total_in_status FROM lihu.queue where status in (" +
            " <foreach collection='statusList' separator=',' item='status'>" +
            "  #{status} " +
            " </foreach> );" +
            "</script>")
    Integer statusStatistic(@Param("statusList")List<Integer> statusList);
 
    @Select("<script>" +
            "SELECT count(1) as total_in_status FROM lihu.queue where " +
            " deleted = 0 and book_date = CURDATE() and " +
            " status in ( " +
            " <foreach collection='statusList' separator=',' item='status'>" +
            "  #{status} " +
            " </foreach> ) " +
            " and book_check_type in ( " +
            " <foreach collection='checkTypes' separator=',' item='checkType'>" +
            "  #{checkType} " +
            " </foreach> ) " +
            "</script>")
    Integer checkTypeAndStatusStatistic( @Param("checkTypes")Integer[] checkTypes, @Param("statusList")List<Integer> statusList );
 
    @Select("<script>" +
            "SELECT room_id, bed_no, count(1) as total_in_status FROM lihu.queue where status in (" +
            " <foreach collection='statusList' separator=',' item='status'>" +
            " #{status} " +
            " </foreach> )" +
            " group by room_id, bed_no; "
            +"</script>")
    List<QueueStatisticDO> queueStatistic(@Param("statusList")List<Integer> statusList);
 
    @Select("<script>" +
            "SELECT status, count(1) as total_in_status FROM lihu.queue where " +
            " deleted = 0 and book_date = CURDATE() and " +
            " room_id = #{roomId} and bed_no = #{bedNo} " +
            " group by status " +
            "</script>")
    List<BedQueueStatisticDO> bedQueueStatistic(@Param("roomId")Long roomId, @Param("bedNo")String bedNo);
 
    @Select("<script>" +
            "SELECT status, count(1) as total_in_status FROM lihu.queue where " +
            " deleted = 0 and book_date = CURDATE() and " +
            " room_id = #{roomId} " +
            " group by status " +
            "</script>")
    List<BedQueueStatisticDO> roomQueueStatistic(@Param("roomId")Long roomId);
 
    @Select("<script>" +
            "SELECT count(1) FROM lihu.queue " +
            "where deleted = 0 and book_date = CURDATE() and " +
            " room_id = #{roomId} and bed_no = #{bedNo} and status in (" +
            " <foreach collection='statusList' separator=',' item='status'>" +
            "   #{status} " +
            " </foreach> )" +
            "</script>")
    Integer bedQueueStatisticByStatus(@Param("roomId")Long roomId, @Param("bedNo")String bedNo, @Param("statusList")List<Integer> statusList);
 
    @Select("""
            <script>
                SELECT * FROM lihu.queue 
                where deleted = 0 and room_id = #{roomId} and bed_no = #{bedNo} and status in (
                  <foreach collection='statusList' separator=',' item='status'>
                    #{status} 
                  </foreach> ) 
                order by case when status = 30 then 0 else 1 end,
                         jump_flag desc,
                         case when seq_num is null then 1 else 0 end,
                         seq_num, book_timeslot
            </script>
            """)
    @Results({
            @Result(property = "patDetails", column = "pat_details", typeHandler=com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler.class)
    })
    List<QueueDO> getBedQueueByStatus(@Param("roomId")Long roomId, @Param("bedNo")String bedNo, @Param("statusList")List<Integer> statusList);
 
    /**
     * 叫号取人:本工位「候诊中(20)」里**应优先叫**的那一位。
     *
     * <p>排序(号统一后的最终规则):<b>加急(jump_flag=1) → 排队号 seq_num 升序</b>,号为空(存量行)排最后。
     * <p>排队号在签到时一次性分配、之后不再改动,所以"下一位叫谁"完全由本方法决定;
     * 加急必须在这里参与排序,否则加急患者永远不会被优先叫到。
     */
    @Select("""
            SELECT * from lihu.queue where room_id = #{roomId} and bed_no = #{bedNo}
               and status = #{curStatus} and deleted = 0
             order by jump_flag desc,
                      case when seq_num is null then 1 else 0 end,
                      seq_num, book_timeslot
             limit 1
            """)
    QueueDO getBedFirstQueueItemByStatus(@Param("roomId")Long roomId, @Param("bedNo")String bedNo, @Param("curStatus")Integer curStatus);
 
    /**
     * 【需求2 相关缺陷修复】按「工位 + 患者 + 检查类型 + 状态」**精确**定位队列行。
     * <p>
     * 用于替代 {@link #getBedFirstQueueItemByStatus}:后者只按"工位 + 状态取 seq_num 最小的一条",
     * 当医生先点"放弃检查"(会把该患者置为 40)后再点"检查完成"时,
     * 会取到**下一位患者**并把他误置为已完成(详见 DevRentServiceImpl.routineFinishOperation)。
     */
    @Select("""
            SELECT * from lihu.queue
            where room_id = #{roomId} and bed_no = #{bedNo}
              and status = #{curStatus}
              and pat_id = #{patId}
              and book_check_type = #{checkType}
              and TO_DAYS(book_date) = TO_DAYS(NOW())
            order by seq_num limit 1
            """)
    @Results({
            @Result(property = "patDetails", column = "pat_details", typeHandler=com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler.class)
    })
    QueueDO getBedQueueItemByPatAndStatus(@Param("roomId")Long roomId, @Param("bedNo")String bedNo,
                                          @Param("patId")String patId, @Param("checkType")Integer checkType,
                                          @Param("curStatus")Integer curStatus);
 
    /**
     * 按工位 + 状态 + 今日取**最近更新**的一条。
     * <p>
     * 比「seq_num 最小」更贴合"当前正在就诊/装机的那位患者":seq_num 最小是本时段最早排队的,
     * 而 update_time 最新才是刚被叫号推进到该状态的。
     */
    @Select("""
            SELECT * from lihu.queue
            where room_id = #{roomId} and bed_no = #{bedNo}
              and status = #{curStatus}
              and TO_DAYS(book_date) = TO_DAYS(NOW())
            order by update_time desc, seq_num limit 1
            """)
    @Results({
            @Result(property = "patDetails", column = "pat_details", typeHandler=com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler.class)
    })
    QueueDO getBedLatestQueueItemByStatus(@Param("roomId")Long roomId, @Param("bedNo")String bedNo,
                                          @Param("curStatus")Integer curStatus);
 
    @Select("""
            SELECT * from lihu.queue where apply_no = #{applyNo} and status = #{curStatus}  order by seq_num, book_timeslot limit 1
            """)
    QueueDO getBedFirstQueueItemByApplyNo(@Param("applyNo")String applyNo, @Param("curStatus")Integer curStatus);
 
    // 某个诊室范围内 [已领用] [已召回-安装] 人员 抢占, 会更新 [工位号]
    // 没有限定检查类型,不要使用时间槽
    // 排序:加急优先 → 排队号升序(号为空排最后)。装机叫号取人也走这里。
    @Select("<script> " +
            "select * from lihu.queue where deleted = 0 and room_id = #{roomId} and status in ( " +
            "       <foreach collection='curStatusList' separator=',' item='status'>" +
            "           #{status} " +
            "       </foreach> )" +
            "       order by jump_flag desc, case when seq_num is null then 1 else 0 end, seq_num, book_timeslot limit 1 " +
            "</script>" )
    QueueDO getRoomFirstQueueItemByStatus(@Param("roomId")Long roomId, @Param("curStatusList")List<Integer> curStatusList);
 
    /**
     * 按预约单 id 批量取 queue 行。
     *
     * <p>用途:预约签到前,把「该预约是否已排队、拿到的排队号是多少」回填给前端。
     * 号统一后取的是 {@code seq_num}(见 AppointmentServiceImpl#queryAndCreateAppointmentByPatId)。
     * <p>原方法名为 {@code selectBookSeqNumByAppointIdList},因号已统一为 seq_num 而改名。
     */
    @Select("<script> " +
            "select * from lihu.queue where deleted = 0 and appoint_id in ( " +
            "       <foreach collection='appointIdList' separator=',' item='appointId'>" +
            "           #{appointId} " +
            "       </foreach> )" +
            "</script>" )
    List<QueueDO> selectQueueByAppointIdList(@Param("appointIdList") List<Long> appointIdList);
 
/*
    @Select("select * from lihu.queue where room_id = #{roomId} and bed_no = #{bedNo} " +
            " and status = #{curStatus} and pat_id = #{patId} and book_check_type=#{checkType}")
    QueueDO XXX(@Param("roomId")Long roomId, @Param("bedNo")String bedNo,
                                       @Param("patId")String patId, @Param("checkType")Integer checkType,
                                       @Param("curStatus")Integer curStatus);
*/
 
    @Update("update lihu.queue set status = #{newStatus} where pat_id = #{patId} and book_check_type = #{checkType} and status = #{curStatus}; " )
    Integer updatePatientQueueStatus(@Param("patId")String patId, @Param("checkType")Integer checkType, @Param("curStatus")Integer curStatus, @Param("newStatus")Integer newStatus);
 
    /**
     * 把本工位、指定状态中「排队号最小」的一位改成新状态(过号用)。
     *
     * <p><b>【号统一改造必修】必须按行 id 更新。</b>
     * 旧实现外层条件是 {@code where status = ? and seq_num = (select min(seq_num) ...)}——
     * 外层<b>没有 room_id / bed_no 条件</b>,完全依赖"seq_num 全表唯一"才不会误伤。
     * 本需求把排队号改为「按检查类型各自编号」,号会跨检查类型重复,
     * 旧写法会一次性把<b>全院所有同号同状态的患者</b>一起改掉
     * (例:常规心电 5 号过号,动态心电的 5 号也被过号)。故改为先取 id、再按 id 更新。
     */
    @Update("update lihu.queue set status = #{newStatus} where id = " +
            "  (select a.id from " +
            "    (select id from lihu.queue where room_id = #{roomId} and bed_no = #{bedNo} " +
            "       and status = #{curStatus} and deleted = 0 " +
            "       order by case when seq_num is null then 1 else 0 end, seq_num, book_timeslot limit 1) a)")
    Integer updateBedQueueStatus(@Param("roomId")Long roomId, @Param("bedNo")String bedNo, @Param("curStatus")Integer curStatus, @Param("newStatus")Integer newStatus);
 
    // 弃用原因:需要先获取,判断逻辑处理后,再更新
    // 某个诊室范围内 [已领用] [已召回-安装] 人员 抢占, 会更新 [工位号]
    // 没有限定检查类型,不要使用时间槽
    @Update("<script> " +
            "update lihu.queue set status = #{newStatus}, bed_no = #{bedNo} where id = \n" +
            "  (select a.id from \n" +
            "    (select id from lihu.queue where room_id = #{roomId} " +
            "       and status in ( " +
            "           <foreach collection='curStatusList' separator=',' item='status'>" +
            "             #{status} " +
            "           </foreach> )" +
            "       order by seq_num, book_timeslot limit 1 " +
            "    ) a " +
            "  )" +
            "</script>"
    )
    Integer preemptToBeInstalledPatient(@Param("roomId")Long roomId, @Param("bedNo")String bedNo, @Param("curStatusList")List<Integer> curStatusList, @Param("newStatus")Integer newStatus);
 
    /**
     * 取「本工位开展的检查项目」中下一位应分流的「排队中(10)」患者。
     *
     * <p>排序:<b>加急(jump_flag=1) → 预约时段 → 建单时间</b>;号为空(存量行)排最后。
     * <p><b>这里不再分配排队号</b>:号已在签到时一次性写入 {@code seq_num},
     * 分流只负责把患者从「排队中」搬到某个工位的「候诊中」。
     * <p>取出后再用 {@link #preemptWaitingPatientById} 做条件更新,
     * 靠 {@code and status = 10} 保证并发下只有一方能抢到该行。
     */
    @Select("<script>" +
            "select * from lihu.queue where deleted = 0 and status = #{curStatus} " +
            "   and book_check_type in ( " +
            "     <foreach collection='checkTypeArray' separator=',' item='_checkType'>" +
            "       #{_checkType} " +
            "     </foreach> )" +
            "   order by jump_flag desc, case when seq_num is null then 1 else 0 end, book_timeslot, create_time limit 1" +
            "</script>")
    @Results({
            @Result(property = "patDetails", column = "pat_details", typeHandler=com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler.class)
    })
    QueueDO getFirstWaitingByCheckTypes(@Param("curStatus")Integer curStatus, @Param("checkTypeArray")Integer[] checkTypeArray);
 
    /**
     * 「本诊室/本工位**排队中、还没分配工位**」的患者(工作台"本诊室排队中(未分配)"分区用)。
     *
     * <p>背景:工作台的候诊列表按 {@code room_id + bed_no} 查,只包含**已经分流到该工位**的患者;
     * 而"排队中(10)"的患者 {@code bed_no} 是空的(见 QueueServiceImpl 重置逻辑:room_id=0 / bed_no=' '),
     * 大屏会显示他们(大屏不按诊室/工位过滤),于是出现"大屏有、工作台列表没有"的困惑。
     * 这里把"本工位检查类型、尚未分配工位"的排队患者单独查出来,由医生**手工拉入**。
     *
     * <p>判定"未分配"用 {@code bed_no} 为空(兼容 null / 空串 / 单个空格),
     * 不依赖 {@code room_id = 0} 这一约定,容错更好。
     */
    @Select("""
            <script>
                SELECT * FROM lihu.queue
                where deleted = 0 and status = #{curStatus}
                  and (bed_no is null or trim(bed_no) = '')
                  and book_check_type in (
                    <foreach collection='checkTypeArray' separator=',' item='_checkType'>
                      #{_checkType}
                    </foreach> )
                order by jump_flag desc,
                         case when seq_num is null then 1 else 0 end,
                         book_timeslot, create_time
            </script>
            """)
    @Results({
            @Result(property = "patDetails", column = "pat_details", typeHandler=com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler.class)
    })
    List<QueueDO> selectUnassignedWaitingByCheckTypes(@Param("curStatus")Integer curStatus,
                                                      @Param("checkTypeArray")Integer[] checkTypeArray);
 
    /**
     * 把「排队中」的某一行抢占到指定工位。
     *
     * <p><b>不改排队号</b>(号是签到时定的):只改状态、诊室、工位。
     * <p>{@code and status = #{curStatus}} 是并发保护 —— 两个工位同时抢同一位患者时,
     * 只有第一个 UPDATE 会命中(返回 1),另一方返回 0,需重新取人。
     */
    @Update("update lihu.queue set status = #{newStatus}, room_id = #{roomId}, room_name = #{roomName}, bed_no = #{bedNo} " +
            " where id = #{id} and status = #{curStatus} and deleted = 0")
    Integer preemptWaitingPatientById(@Param("id")Integer id, @Param("roomId")Long roomId, @Param("roomName")String roomName,
                                      @Param("bedNo")String bedNo, @Param("curStatus")Integer curStatus, @Param("newStatus")Integer newStatus);
 
    /**
     * 【本需求·并发加固】按主键 + **当前状态**条件推进状态(叫号:候诊中(20) → 就诊中(30))。
     *
     * <p>为什么需要它:{@code nextPatient} 原来是"先查最小号的候诊行 → updateById",
     * 两个并发请求(双击 / 多端)可能各推进一条 READY,导致同一工位出现 **2 个「就诊中」**。
     * 有了 {@code and status = #{curStatus}}:只有第一个 UPDATE 命中(返回 1),
     * 另一方返回 0,由调用方放弃本次叫号,等下一次再取。
     */
    @Update("update lihu.queue set status = #{newStatus}, update_time = NOW() "
            + " where id = #{id} and status = #{curStatus} and deleted = 0")
    Integer updateStatusByIdIfCurrent(@Param("id") Integer id,
                                      @Param("curStatus") Integer curStatus,
                                      @Param("newStatus") Integer newStatus);
 
    // 常规检查 或 领用 的患者召回, 原工位处理
    @Update("update lihu.queue set status = #{newStatus}, passed = 1 " +
            " where room_id = #{roomId} and bed_no = #{bedNo} and status = #{curStatus} and pat_id = #{patId} and book_check_type=#{checkType}")
    Integer recallPassedPatient(@Param("roomId")Long roomId, @Param("bedNo")String bedNo,
                                @Param("patId")String patId, @Param("checkType")Integer checkType,
                                @Param("curStatus")Integer curStatus, @Param("newStatus")Integer newStatus);
 
    // 装机医生 允许抢占 本科室 [安装过号] 的患者
    @Update("update lihu.queue set status = #{newStatus}, bed_no = #{bedNo}, passed = 1 " +
            " where room_id = #{roomId} and status = #{curStatus} and pat_id = #{patId} and book_check_type=#{checkType}")
    Integer recallPassedInstallPatient(@Param("roomId")Long roomId, @Param("bedNo")String bedNo,
                                       @Param("patId")String patId, @Param("checkType")Integer checkType,
                                @Param("curStatus")Integer curStatus, @Param("newStatus")Integer newStatus);
 
    @Update("update lihu.queue set jump_flag = #{jumped} " +
            " where status = #{curStatus} and pat_id = #{patId} and book_check_type=#{bookCheckType}")
    Integer queueJump(@Param("patId")String patId, @Param("bookCheckType")Integer bookCheckType,
                      @Param("curStatus")Integer curStatus, @Param("jumped")Byte jumped );
 
    // ==================== 需求2:误点「叫号 / 检查完成」的撤销 ====================
 
    /**
     * 批量取「这些患者今日的队列状态」(用于判断"已完成记录"是否被撤销过)。
     *
     * <p>【需求:撤销标识】撤销「检查完成」只是把队列行 40(已就诊) 改回 20(准备中),
     * 不碰 job_record。因此"这条完成记录是否已撤销"用**当日队列行的当前状态**判断即可:
     * 状态 ≠ 40 → 已被撤销;再次完成时会变回 40 → 自动恢复成"已完成"(无需额外清标记)。
     *
     * <p>只取当日(撤销本身就只支持当日),一次查询解决整页记录。
     */
    @Select("<script>" +
            "select pat_id as patId, book_check_type as checkType, status " +
            "  from lihu.queue " +
            " where deleted = 0 and TO_DAYS(book_date) = TO_DAYS(NOW()) " +
            "   and pat_id in ( " +
            "     <foreach collection='patIdList' separator=',' item='_patId'>#{_patId}</foreach> ) " +
            "</script>")
    List<Map<String, Object>> selectTodayStatusByPatIds(@Param("patIdList")List<String> patIdList);
 
    /**
     * 撤销「检查完成」:把本工位今日的**指定患者**从 已就诊(40) 退回 候诊中(20)。
     * <p>
     * 必须按 {@code pat_id + book_check_type + 今日} 精确定位,**不能**只按
     * "工位 + status=40 取第一条"——那会在期间已叫下一位患者时误改他人(见
     * {@code DevRentServiceImpl.routineFinishOperation} 的既有缺陷)。
     * <p>
     * {@code passed = 0} 是为了让患者重新回到正常候诊序列(非"过号"态)。
     */
    @Update("update lihu.queue set status = #{newStatus}, passed = 0 " +
            " where room_id = #{roomId} and bed_no = #{bedNo} and status = #{curStatus} " +
            "   and pat_id = #{patId} and book_check_type = #{checkType} " +
            "   and TO_DAYS(book_date) = TO_DAYS(NOW())")
    Integer undoFinishPatient(@Param("roomId")Long roomId, @Param("bedNo")String bedNo,
                              @Param("patId")String patId, @Param("checkType")Integer checkType,
                              @Param("curStatus")Integer curStatus, @Param("newStatus")Integer newStatus);
 
    /**
     * 撤销「误点叫号」:把本工位当前 就诊中(30) 的患者退回 候诊中(20)。
     * <p>
     * 仅按工位+状态定位("当前正在就诊的那一位"),不使用 seq_num 子查询,
     * 避免与 {@code updateBedQueueStatus} 的写法混淆。
     */
    @Update("update lihu.queue set status = #{newStatus} " +
            " where id = " +
            "  (select a.id from " +
            "    (select id from lihu.queue where room_id = #{roomId} and bed_no = #{bedNo} " +
            "       and status = #{curStatus} and TO_DAYS(book_date) = TO_DAYS(NOW()) " +
            "       order by seq_num, book_timeslot limit 1) a)")
    Integer undoCallPatient(@Param("roomId")Long roomId, @Param("bedNo")String bedNo,
                            @Param("curStatus")Integer curStatus, @Param("newStatus")Integer newStatus);
 
    /**
     * 取本工位今日**最近一位已完成**的患者(用于界面提示"可撤销的是谁")。
     */
    @Select("SELECT * FROM lihu.queue where room_id = #{roomId} and bed_no = #{bedNo} " +
            " and status = #{status} and TO_DAYS(book_date) = TO_DAYS(NOW()) " +
            " order by update_time desc, seq_num desc limit 1")
    @Results({
            @Result(property = "patDetails", column = "pat_details", typeHandler=com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler.class)
    })
    QueueDO getBedLastFinishedQueueItem(@Param("roomId")Long roomId, @Param("bedNo")String bedNo,
                                        @Param("status")Integer status);
 
    //@Update("TRUNCATE TABLE lihu.queue")
    @Delete("delete from lihu.queue where TO_DAYS(book_date) != TO_DAYS(NOW())")
    void clearQueue();
}