yxh
7 天以前 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
package cn.lihu.jh.module.ecg.dal.mysql.room;
 
import java.util.*;
 
import cn.lihu.jh.framework.common.pojo.PageResult;
import cn.lihu.jh.framework.mybatis.core.mapper.BaseMapperX;
import cn.lihu.jh.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.lihu.jh.module.ecg.dal.dataobject.room.RoomDO;
import org.apache.ibatis.annotations.*;
 
import cn.lihu.jh.module.ecg.controller.admin.room.vo.RoomPageReqVO;
import cn.lihu.jh.module.ecg.enums.BedStatusEnum;
 
/**
 * 诊室和诊疗床 Mapper
 *
 * @author 芋道源码
 */
@Mapper
public interface RoomMapper extends BaseMapperX<RoomDO> {
 
    /**
     * 诊室工位分页(诊室管理列表 / 导出共用)。
     *
     * <p>排序(本需求):**诊室编号升序 → 工位号升序**,同一诊室有多个工位时顺序稳定。
     * 原实现是 {@code orderByDesc(id)}(后加的排最前),与"按诊室编号看"的现场习惯不符。
     */
    default PageResult<RoomDO> selectPage(RoomPageReqVO reqVO) {
        return selectPage(reqVO, new LambdaQueryWrapperX<RoomDO>()
                .eqIfPresent(RoomDO::getRoomId, reqVO.getRoomId())
                .eqIfPresent(RoomDO::getBedNo, reqVO.getBedNo())
                .betweenIfPresent(RoomDO::getCreateTime, reqVO.getCreateTime())
                .orderByAsc(RoomDO::getRoomId)
                .orderByAsc(RoomDO::getBedNo));
    }
 
    /**
     * 按 IP 查该 IP 下**可用**的工位(供"按IP自动匹配工位"使用)。
     *
     * <p>三条过滤缺一不可:
     * <ul>
     *   <li>{@code deleted = 0}:原实现是裸查,已删除的工位也会被匹配到——
     *       一旦某 IP 被回收再分配给新工位,就会自动入座到历史脏工位上;</li>
     *   <li>{@code status != 0(已关闭)}:已关闭的工位不该被自动入座(否则人坐上去却没有候诊队列,看着像"没病人");</li>
     *   <li>{@code ip <> ''}:空 IP 不做匹配(避免历史空值行被扫到)。</li>
     * </ul>
     * 状态取值见 {@link cn.lihu.jh.module.ecg.enums.BedStatusEnum}:0已关闭 / 10已开通 / 20有医生 / 30暂停。</p>
     */
    @Select("select * from lihu.clinic_room " +
            " where ip = #{ip} and deleted = 0 and coalesce(status, 0) <> 0")
    @Results({
            @Result(property = "checkTypes", column = "check_types", typeHandler = com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler.class)
    })
    List<RoomDO> queueByIp(@Param("ip") String ip);
 
    /**
     * 该 IP 是否已绑定在**别的**工位上(用于"选择工位模式"里写 IP 前的冲突检测)。
     * <p>一个工位只有一个 ip 字段,若两台电脑走同一出口 IP,后绑定的会把前面的抢走,
     * 所以覆盖前要先让用户确认(前端拿到冲突工位后弹确认,再带 force 重调)。</p>
     *
     * @param ip        待绑定的 IP
     * @param excludeId 排除的工位主键(通常是本次要绑定的那个),可为空
     */
    @Select("<script> " +
            "SELECT * FROM lihu.clinic_room " +
            " WHERE deleted = 0 AND ip = #{ip} " +
            " <if test='excludeId != null'> AND id != #{excludeId} </if> " +
            " ORDER BY id" +
            "</script>")
    @Results({
            @Result(property = "checkTypes", column = "check_types", typeHandler = com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler.class)
    })
    List<RoomDO> listByIpExcludeId(@Param("ip") String ip, @Param("excludeId") Integer excludeId);
 
    /**
     * 把本机 IP 写到指定工位(只改 ip 一个字段,供"选择工位模式 2/3"使用)。
     */
    @Update("update lihu.clinic_room set ip = #{ip} where id = #{id} and deleted = 0")
    Integer updateIpById(@Param("id") Integer id, @Param("ip") String ip);
 
    @Update("<script> " +
            "update lihu.clinic_room set status=#{newStatus}, doc_id=null, doc_name=null " +
            " where deleted = 0 " +
            "</script>")
    Integer resetRoom(@Param("newStatus") Integer newStatus);
 
    @Select("<script> " +
            "SELECT * FROM lihu.clinic_room " +
            "where status in ( " +
                    " <foreach collection='curStatusList' separator=',' item='curStatus'>" +
                    "  #{curStatus.status} " +
                    " </foreach> )" +
            " and deleted = 0" +
            " order by room_id;" +
            "</script>")
    @Results({
            @Result(property = "checkTypes", column = "check_types", typeHandler=com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler.class)
    })
    List<RoomDO> simpleRoomList(@Param("curStatusList") List<BedStatusEnum> curStatusList);
 
    @Select("SELECT * FROM lihu.clinic_room where room_id=#{roomId} and bed_no=#{bedNo}")
    @Results({
        @Result(property = "checkTypes", column = "check_types", typeHandler=com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler.class)
    })
    RoomDO getRoom(@Param("roomId")Long roomId, @Param("bedNo")String bedNo);
 
    @Select("SELECT * FROM lihu.clinic_room where room_id=#{roomId} and bed_no=#{bedNo} and doc_id=#{docId}")
    RoomDO getRoomByRoomBedDoc(@Param("roomId")Long roomId, @Param("bedNo")String bedNo, @Param("docId")Long docId);
 
    @Select("SELECT * FROM lihu.clinic_room where doc_id=#{docId}")
    List<RoomDO> getRoomByDocId(@Param("docId")Long docId);
 
    /**
     * 【本需求·一人多工位】该医生**当前有效**占用的全部工位。
     *
     * <p>比 {@link #getRoomByDocId} 多了两个过滤:
     * <ul>
     *   <li>{@code deleted = 0}:排除历史逻辑删除行(原 SQL 没有,可能返回脏行);</li>
     *   <li>{@code status in (20,30)}:只取「医生在座(DOCTOR_ON) / 医生暂停(DOCTOR_PAUSE)」,
     *       排除数据不一致残留的"doc_id 还在但状态已回空闲"。两个值以常量传入,避免硬编码。</li>
     * </ul>
     */
    @Select("<script> " +
            "SELECT * FROM lihu.clinic_room where doc_id = #{docId} and deleted = 0 " +
            " and status in ( " +
            "  <foreach collection='statusList' separator=',' item='st'>#{st}</foreach> " +
            " ) order by room_id, bed_no " +
            "</script>")
    List<RoomDO> getMyActiveBeds(@Param("docId") Long docId, @Param("statusList") List<Integer> statusList);
 
    /**
     * 【本需求·统计按人合并】当前**在座医生数**(按 doc_id 去重)。
     *
     * <p>一人多工位后,"在座工位数"会按工位累计(数值本身正确),
     * 但监控页还需要"到底几个医生在岗",用它补一个指标。
     */
    @Select("<script> " +
            "select count(distinct doc_id) from lihu.clinic_room " +
            " where doc_id is not null and deleted = 0 and status in ( " +
            "  <foreach collection='statusList' separator=',' item='st'>#{st}</foreach> " +
            " ) " +
            "</script>")
    Integer countSeatedDoctors(@Param("statusList") List<Integer> statusList);
 
    @Update("<script> " +
            "update lihu.clinic_room set status=#{newStatus.status} " +
            " where room_id=#{roomId} and bed_no=#{bedNo} and isNull(doc_id) " +
            " and status in ( " +
            "  <foreach collection='curStatusList' separator=',' item='curStatus'>" +
            "   #{curStatus.status} " +
            "  </foreach> );" +
            "</script>")
    Integer setBedOpeningOpening(@Param("roomId")Long roomId, @Param("bedNo")String bedNo, @Param("newStatus") BedStatusEnum newStatus, @Param("curStatusList") List<BedStatusEnum> curStatusList);
 
    @Update("<script> " +
            "update lihu.clinic_room set status=#{newStatus.status}, doc_id=null, doc_name=null " +
            " where room_id=#{roomId} and bed_no=#{bedNo} and isNull(doc_id) " +
            " and status in ( " +
            "  <foreach collection='curStatusList' separator=',' item='curStatus'>" +
            "   #{curStatus.status} " +
            "  </foreach> );" +
            "</script>")
    Integer setBedOpeningClosed(@Param("roomId")Long roomId, @Param("bedNo")String bedNo, @Param("newStatus") BedStatusEnum newStatus, @Param("curStatusList") List<BedStatusEnum> curStatusList);
 
    @Update("<script> " +
            "update lihu.clinic_room set status=#{newStatus.status} " +
            "where room_id=#{roomId} and bed_no=#{bedNo} and doc_id=#{docId} " +
            "and status in ( " +
            " <foreach collection='curStatusList' separator=',' item='curStatus'>" +
            "  #{curStatus.status} " +
            " </foreach> );" +
            "</script>")
    Integer setBedDoctorStatus(@Param("roomId")Long roomId, @Param("bedNo")String bedNo, @Param("docId")Long docId, @Param("newStatus") BedStatusEnum newStatus, @Param("curStatusList") List<BedStatusEnum> curStatusList);
 
    /**
     * 【需求3】设置工位的「停止候诊」开关(持久化,供 bedReload 后恢复)。
     *
     * @param stopReady 0=正常接收候诊患者;1=停止新增候诊患者
     * @return 受影响行数
     */
    @Update("update lihu.clinic_room set stop_ready = #{stopReady}, update_time = NOW() "
          + "where room_id = #{roomId} and bed_no = #{bedNo} and deleted = 0")
    Integer setBedStopReady(@Param("roomId")Long roomId, @Param("bedNo")String bedNo,
                            @Param("stopReady")Integer stopReady);
 
    /**
     * 入座:把该工位的**主人**设为我(按 room_id + bed_no 定位)。
     *
     * <p>【本次放宽 + 修复】
     * <ul>
     *   <li>可入座条件统一为:<b>(无主人 或 主人是我)且 状态 ∈ claimableStatusList</b>。
     *       状态集合由调用方传 {已开通, 有医生, 暂停}(**排除"已关闭"**)。<br>
     *       原来"无主人"时只认"已开通":一旦数据里出现"doc_id 为空但状态仍是有医生/暂停"
     *       这种不一致,医生站在工位前却始终坐不上,只报"状态异常",现场无从处理。</li>
     *   <li>主人是我时同样放行 → 上次没正常离座(关浏览器/超时)也能坐回自己的工位。</li>
     *   <li>主人是**别人**时更新 0 行,调用方据此拒绝(不挤掉正在操作的人)。</li>
     * </ul>
     */
    @Update("<script> " +
            "update lihu.clinic_room set status=#{newStatus.status}, doc_id=#{docId}, doc_name=#{docName}, update_time=NOW() " +
            "where room_id=#{roomId} and bed_no=#{bedNo} and deleted = 0 " +
            "  and ( isNull(doc_id) or doc_id = #{docId} ) " +
            "  and status in ( " +
            "      <foreach collection='claimableStatusList' separator=',' item='curStatus'>#{curStatus.status}</foreach> );" +
            "</script>")
    Integer setBedDoctorOn(@Param("roomId")Long roomId, @Param("bedNo")String bedNo, @Param("docId")Long docId, @Param("docName")String docName,
                           @Param("newStatus") BedStatusEnum newStatus,
                           @Param("claimableStatusList") List<BedStatusEnum> claimableStatusList);
 
    /**
     * 入座:同上,但按**主键 id** 定位。
     *
     * <p>【本次修复】现场出现过"工位在库里明明是空闲的,入座却更新 0 行 → 报状态异常"。
     * 已确认不是被占用;剩下的可能是 {@code bed_no} 与请求值存在肉眼难辨的差异
     * (结尾空格、大小写、全角等 —— JSON 与复制粘贴都看不出来)。
     * 前端请求里本来就带 {@code id},因此优先按主键更新,彻底绕开这类问题。
     */
    @Update("<script> " +
            "update lihu.clinic_room set status=#{newStatus.status}, doc_id=#{docId}, doc_name=#{docName}, update_time=NOW() " +
            "where id=#{id} and deleted = 0 " +
            "  and ( isNull(doc_id) or doc_id = #{docId} ) " +
            "  and status in ( " +
            "      <foreach collection='claimableStatusList' separator=',' item='curStatus'>#{curStatus.status}</foreach> );" +
            "</script>")
    Integer setBedDoctorOnById(@Param("id")Integer id, @Param("docId")Long docId, @Param("docName")String docName,
                               @Param("newStatus") BedStatusEnum newStatus,
                               @Param("claimableStatusList") List<BedStatusEnum> claimableStatusList);
 
    /**
     * 诊断用:取工位**原始行**(含 {@code bed_no} 的长度与十六进制,用于发现结尾空格/全角差异)。
     *
     * <p>传入 id 时按 id 查(含已删除行);否则按 room_id + {@code TRIM(bed_no)} 宽松查。
     * 返回 Map 而不是 DO:这里要把 {@code deleted}、长度、HEX 这些"非实体字段"一起带出来。
     */
    @Select("<script>" +
            "select id, room_id, room_name, bed_no," +
            "       CHAR_LENGTH(bed_no) as bed_no_len, HEX(bed_no) as bed_no_hex," +
            "       ip, status, doc_id, doc_name, op_type, deleted, update_time" +
            "  from lihu.clinic_room" +
            " where 1 = 1" +
            " <if test='id != null'> and id = #{id} </if>" +
            " <if test='id == null'> and room_id = #{roomId} and TRIM(bed_no) = TRIM(#{bedNo}) </if>" +
            " order by deleted, id" +
            "</script>")
    List<Map<String, Object>> selectBedRawState(@Param("id") Integer id, @Param("roomId") Long roomId, @Param("bedNo") String bedNo);
 
    /**
     * 【本需求】一个用户只允许占一个工位:入座成功后,把**我的其它工位**释放掉。
     *
     * <p>只清 {@code doc_id/doc_name} 并把状态置回"已开通",不动 check_types / ip / stop_ready;
     * 用 {@code not (room_id = ? and bed_no = ?)} 排除本次入座的工位,避免把自己刚坐上的也清掉。
     *
     * @param keepRoomId 本次入座的诊室编号(保留不动)
     * @param keepBedNo  本次入座的工位号(保留不动)
     */
    @Update("<script> " +
            "update lihu.clinic_room set status=#{newStatus.status}, doc_id=null, doc_name=null, update_time=NOW() " +
            "where doc_id=#{docId} and deleted = 0 " +
            "<if test='keepRoomId != null and keepBedNo != null'>" +
            "  and not (room_id = #{keepRoomId} and bed_no = #{keepBedNo}) " +
            "</if>" +
            "</script>")
    Integer releaseOtherBedsOfDoc(@Param("docId")Long docId, @Param("keepRoomId")Long keepRoomId,
                                  @Param("keepBedNo")String keepBedNo, @Param("newStatus") BedStatusEnum newStatus);
 
    @Update("<script> " +
            "update lihu.clinic_room set status=#{newStatus.status}, doc_id=null, doc_name=null " +
            "where room_id=#{roomId} and bed_no=#{bedNo} and doc_id=#{docId} " +
            "and status in ( " +
            " <foreach collection='curStatusList' separator=',' item='curStatus'>" +
            "  #{curStatus.status} " +
            " </foreach> );" +
            "</script>")
    Integer setBedDoctorOff(@Param("roomId")Long roomId, @Param("bedNo")String bedNo, @Param("docId")Long docId, @Param("newStatus") BedStatusEnum newStatus, @Param("curStatusList") List<BedStatusEnum> curStatusList);
 
    /**
     * 【本次需求】闭诊时清空**所有被医生占用**的工位(doc_id 非空,且状态为「医生在岗 / 暂停」)。
     * <p>
     * 背景:工位不再因「空闲超时 / 令牌失效 / 刷新页面」被释放(避免医生离开一会儿就丢工位),
     * 因此需要一个兜底,防止医生下班忘离座导致工位长期被占用 —— 闭诊时统一清空当班工位。
     *
     * @param newStatus     清空后置为的状态(空闲 / 已开通)
     * @param curStatusList 允许被清空的当前状态集合
     */
    @Update("<script> " +
            "update lihu.clinic_room set status=#{newStatus.status}, doc_id=null, doc_name=null, update_time=NOW() " +
            "where deleted = 0 and doc_id is not null " +
            "and status in ( " +
            " <foreach collection='curStatusList' separator=',' item='curStatus'>" +
            "  #{curStatus.status} " +
            " </foreach> )" +
            "</script>")
    Integer clearAllBedDoctors(@Param("newStatus") BedStatusEnum newStatus, @Param("curStatusList") List<BedStatusEnum> curStatusList);
 
    /**
     * 【本次需求】管理员强制清空某个工位。
     * <p>
     * 与 {@link #setBedDoctorOff} 的唯一区别:**不带 `doc_id = ?` 条件**,
     * 因此可以把"别人占用中"的工位清掉(医生下班忘离座、换人使用同一工位时用)。
     * 仍然要求当前状态是「医生在岗 / 暂停」且 doc_id 非空,避免误清空闲工位。
     */
    @Update("<script> " +
            "update lihu.clinic_room set status=#{newStatus.status}, doc_id=null, doc_name=null, update_time=NOW() " +
            "where room_id=#{roomId} and bed_no=#{bedNo} and doc_id is not null " +
            "and status in ( " +
            " <foreach collection='curStatusList' separator=',' item='curStatus'>" +
            "  #{curStatus.status} " +
            " </foreach> )" +
            "</script>")
    Integer setBedDoctorOffAdmin(@Param("roomId")Long roomId, @Param("bedNo")String bedNo,
                                 @Param("newStatus") BedStatusEnum newStatus, @Param("curStatusList") List<BedStatusEnum> curStatusList);
}