yxh
7 天以前 68ff33beff11310e830b0615b5a81f83fad0f683
jh-module-ecg/jh-module-ecg-biz/src/main/java/cn/lihu/jh/module/ecg/service/room/RoomService.java
@@ -6,6 +6,8 @@
import cn.lihu.jh.module.ecg.controller.admin.room.vo.MonitorInfoVO;
import cn.lihu.jh.module.ecg.controller.admin.room.vo.RoomPageReqVO;
import cn.lihu.jh.module.ecg.controller.admin.room.vo.RoomSaveReqVO;
import cn.lihu.jh.module.ecg.controller.admin.room.vo.RoomSeatRespVO;
import cn.lihu.jh.module.ecg.controller.admin.room.vo.RoomSelectBedRespVO;
import cn.lihu.jh.module.ecg.dal.dataobject.room.RoomDO;
import cn.lihu.jh.module.ecg.dal.dataobject.room.RoomProfile;
@@ -50,6 +52,137 @@
    RoomProfile getRoomByIP(String ip, Long roomId);
    /**
     * 「选择工位模式」- 按本机IP匹配工位:返回该IP下的候选工位(本人已占用 → 空闲 → 被他人占用)。
     * <p>仅查询、不改变任何状态;自动入座由前端拿到结果后沿用原有的 bedDoctorOn 流程完成。</p>
     *
     * @param ip    来访IP
     * @param docId 当前登录人ID(可能为空)
     */
    List<RoomSelectBedRespVO.RoomSelectBedItem> matchBedsByIp(String ip, Long docId);
    /**
     * 「选择工位模式」- 把本机IP写到指定工位(只改 clinic_room.ip 一个字段)。
     *
     * @param id 工位主键
     * @param ip 来访IP
     * @return 受影响行数
     */
    Integer bindIpToBed(Integer id, String ip);
    /**
     * 该IP是否已经绑在别的工位上(冲突检测,供前端弹确认后再覆盖)。
     *
     * @param ip        待绑定IP
     * @param excludeId 排除的工位主键,可为空
     */
    List<RoomDO> listIpBindings(String ip, Integer excludeId);
    /**
     * 「选择工位模式」- 一键自动入座:按来访 IP 匹配工位并**直接落座**(模式 1/3 用)。
     *
     * <p>为什么要有这个接口:前端原来把「{@code get-ip-bed} 匹配 → {@code bind-ip} 写 IP →
     * {@code bed-doctor-on} 入座」拆成三个请求自己拼,任一步失败都只能回落到"手选工位",
     * 现场表现就是"登录后没有自动入座"且看不出原因。这里收敛成一次调用,并返回**识别到的 IP**
     * 与**未入座原因**(ip_invalid / nomatch / multi / occupied / error)。
     *
     * <p>策略与原有前端逻辑保持一致:
     * ① 已是我的工位 → 幂等恢复;② **恰好一个**空闲工位 → 占用;③ 多个空闲 → 不擅自入座(multi);
     * ④ 全被他人占用 → 不挤掉(occupied + 占用人姓名)。
     *
     * @param ip      来访 IP(调用方已用 {@code IpUtils.normalize} 规范化)
     * @param docId   当前登录人
     * @param docName 当前登录人姓名
     */
    RoomSeatRespVO autoSeatByIp(String ip, Long docId, String docName);
    /**
     * 查询当前登录人**已入座**的工位(给系统顶栏显示"当前工位"用)。
     *
     * @param docId 当前登录人
     * @return 未入座时返回 {@code seated=false} 的空结果
     */
    RoomSeatRespVO getMySeat(Long docId);
    /**
     * 【本需求·一人多工位】查询当前登录人**占据的全部工位**(节假日值班多岗位)。
     *
     * <p>返回「医生在座 / 医生暂停」状态的工位,按 roomId + bedNo 升序;
     * 未占任何工位时返回空列表(前端据此显示"未入座")。
     *
     * @param docId 当前登录人
     */
    List<RoomSeatRespVO> getMySeats(Long docId);
    /**
     * 【本需求·接班自助接管】把指定工位从"别人占用"改为"当前登录人占用"。
     *
     * <p>使用场景:上一班医生下班未点「离座」,下一班医生在同一台电脑/同一工位接班。
     * 行为:
     * <ol>
     *   <li>工位不存在 → 报错;</li>
     *   <li>工位空闲,或本来就属于当前登录人 → **幂等成功**(不报错);</li>
     *   <li>被他人占用 → 记录审计日志(工位、原占用人、操作人)→ 强制清空原占用人 → **立即让当前登录人入座**。</li>
     * </ol>
     *
     * @param roomId 诊室编号
     * @param bedNo  工位编号
     */
    void takeOverBed(Long roomId, String bedNo);
    /**
     * 校验「当前登录人必须是该工位的主人」,否则抛
     * {@link cn.lihu.jh.module.ecg.enums.ErrorCodeConstants#ROOM_NOT_OWNER}。
     *
     * <p>用于"主/围观"模型的写操作鉴权:围观人只能看,不能改数据。
     * {@code @RequireBedOwner} 切面会调用它;只带 rentId 之类的接口需要自行调用。
     *
     * @param roomId 诊室编号
     * @param bedNo  工位号
     */
    void assertBedOwner(Long roomId, String bedNo);
    /**
     * 同上,但**按患者反查工位**:用于只带患者信息、没有 roomId/bedNo 的写接口
     * (放弃检查 / 放弃领用 / 放弃装机)。
     *
     * <p>反查依据:{@code queue} 表当前该患者 + 检查项目的那一行上的 room_id / bed_no。
     * 查不到工位时**拒绝**(宁可拒绝也不放行)。
     */
    void assertBedOwnerByPatient(String patId, Integer checkType);
    /**
     * 同上,但查不到工位时**记警告放行**。
     *
     * <p>用于确费/取消确费:这两步常在流程末端调用,患者可能已经不在工位队列上;
     * 一律拒绝会把正常业务挡掉,故这里只记警告(前端在围观模式下已禁用按钮)。
     */
    void assertBedOwnerByPatientSoft(String patId, Integer checkType);
    /**
     * 校验「该工位当前的主人就是 ownerDocId」。
     *
     * <p>用于围观人查看**工位主人的已完成检查记录**:不要求调用者入座(围观本来就是不入座也能看),
     * 但必须确认这个 docId 确实是该工位现在的主人,避免被当成"任意查别人记录"的入口。
     *
     * @param roomId     诊室编号
     * @param bedNo      工位号
     * @param ownerDocId 期望的工位主人
     */
    void assertBedOwnerIs(Long roomId, String bedNo, Long ownerDocId);
    /**
     * 工位状态诊断(排查"入座报状态异常/找不到工位")。
     *
     * <p>把库里的**原始行**摊开:`id / room_id / bed_no(含长度与 HEX,用于发现结尾空格/全角) /
     * status / doc_id / doc_name / deleted / update_time`,并给出一句结论。
     *
     * @param id     工位主键(优先,含已删除行)
     * @param roomId 诊室编号(id 为空时用)
     * @param bedNo  工位号(id 为空时用,按 TRIM 宽松匹配)
     */
    Map<String, Object> diagnoseBed(Integer id, Long roomId, String bedNo);
    /**
     * 获得诊室和诊疗床分页
     *
     * @param pageReqVO 分页查询