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
package cn.lihu.jh.module.ecg.dal.mysql.appointment;
 
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.controller.admin.appointment.vo.AppointmentPageReqVO;
import cn.lihu.jh.module.ecg.dal.dataobject.appointment.AppointmentDO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
 
import java.time.LocalDate;
import java.util.Date;
import java.util.List;
 
/**
 * 预约 Mapper
 *
 * @author 马剑波
 */
@Mapper
public interface AppointmentMapper extends BaseMapperX<AppointmentDO> {
 
    default PageResult<AppointmentDO> selectPage(AppointmentPageReqVO reqVO) {
        LambdaQueryWrapperX<AppointmentDO> wrapper = (LambdaQueryWrapperX<AppointmentDO>) new LambdaQueryWrapperX<AppointmentDO>().eqIfPresent(AppointmentDO::getPatId, reqVO.getPatId()).likeIfPresent(AppointmentDO::getPatName, reqVO.getPatName()).eqIfPresent(AppointmentDO::getPatGender, reqVO.getPatGender()).eqIfPresent(AppointmentDO::getPatBirthday, reqVO.getPatBirthday()).eqIfPresent(AppointmentDO::getPatMobile, reqVO.getPatMobile()).eqIfPresent(AppointmentDO::getPatPhone, reqVO.getPatPhone()).eqIfPresent(AppointmentDO::getPatIdentityId, reqVO.getPatIdentityId()).eqIfPresent(AppointmentDO::getPatAddr, reqVO.getPatAddr()).eqIfPresent(AppointmentDO::getPatDeptCode, reqVO.getPatDeptCode()).eqIfPresent(AppointmentDO::getPatDeptDesc, reqVO.getPatDeptDesc()).eqIfPresent(AppointmentDO::getPatWardCode, reqVO.getPatWardCode()).eqIfPresent(AppointmentDO::getPatWardDesc, reqVO.getPatWardDesc()).eqIfPresent(AppointmentDO::getBookDate, reqVO.getBookDate()).eqIfPresent(AppointmentDO::getBookTimeslot, reqVO.getBookTimeslot()).eqIfPresent(AppointmentDO::getBookCheckType, reqVO.getBookCheckType()).eqIfPresent(AppointmentDO::getBookSrc, reqVO.getBookSrc()).eqIfPresent(AppointmentDO::getPaid, reqVO.getPaid()).eqIfPresent(AppointmentDO::getApplyNo, reqVO.getApplyNo()).eqIfPresent(AppointmentDO::getEpisodeId, reqVO.getEpisodeId()).inIfPresent(AppointmentDO::getStatus, reqVO.getStatus())
                // ✅ 新增:createTime 区间查询
                .betweenIfPresent(AppointmentDO::getCreateTime, reqVO.getCreateTimeStart(), reqVO.getCreateTimeEnd()).betweenIfPresent(AppointmentDO::getRegisterDate, reqVO.getRegisterDateStart(), reqVO.getRegisterDateEnd())
                // 【本需求】预约日期**时间段**查询(预约记录页的「预约日期」日期范围)
                .betweenIfPresent(AppointmentDO::getBookDate, reqVO.getBookDateStart(), reqVO.getBookDateEnd());
        // 动态排序
        if (reqVO.getOrderCreateTime() != null) {
            wrapper.orderByDesc(AppointmentDO::getCreateTime);
        } else {
            wrapper.orderByAsc(AppointmentDO::getRegisterDate).orderByAsc(AppointmentDO::getPatWardDesc);
        }
        return selectPage(reqVO, wrapper);
    }
 
 
    @Select("select * from lihu.appointment where deleted = 0 and pat_id = #{patId} and to_days(book_date) = to_days(Now())")
    List<AppointmentDO> getCurrentPatId(String patId);
 
    /**
     * 【本需求】按「患者编号 / 申请单号 / 就诊流水号 / 住院号 / 身份证号」匹配预约(±5/+15 天窗口)。
     *
     * <h3>为什么加 {@code pat_id} 与 {@code deleted = 0}</h3>
     * <ul>
     *   <li><b>pat_id</b>:手动新建的预约({@code /ecg/appointment/create})只有 pat_id,
     *       {@code apply_no} / {@code episode_id} / {@code hospital_no} / {@code pat_identity_id} 全为空,
     *       签到页按患者编号查询时永远查不到 → 提示「请先预约」→ 无法签到;</li>
     *   <li><b>deleted = 0</b>:原 SQL 是裸查询,逻辑删除的旧行也会被捞出来,
     *       再经 {@code queryAndCreateAppointmentByPatId} 的"不存在则插入"逻辑可能把已删的单再插回来。</li>
     * </ul>
     */
    @Select("select * from lihu.appointment where deleted = 0 "
            + " and (apply_no = #{code} or episode_id = #{code} or hospital_no = #{code} "
            + "      or pat_identity_id = #{code} or pat_id = #{code}) "
            + " and book_date >= DATE_ADD(CURDATE(), INTERVAL -5 DAY) "
            + " and book_date <= DATE_ADD(CURDATE(), INTERVAL 15 DAY) "
            + " order by book_date, id")
    List<AppointmentDO> getCurrentCode(String code);
 
    @Select("select * from lihu.appointment where pat_id = #{patId} and book_check_type = #{checkType} and to_days(book_date) = to_days(Now()) limit 1")
    AppointmentDO getByPatAndCheckTypeAndBookDate(@Param("patId") String patId, @Param("checkType") Integer checkType);
 
    /**
     * 【本需求:紧急患者不排队直接检查】按「申请单号 / 就诊号 / 患者编号」查近一个月的预约记录。
     *
     * <p>窗口 = [今天-30 天, 今天+30 天];结果**按预约时间倒序**(最近的排前面),便于医生选择。
     * 检查项目过滤(本窗口开展的检查项目)由 {@code checkTypeList} 传入,空/未传表示不限制。
     */
    @Select("<script>" +
            "select * from lihu.appointment where deleted = 0 " +
            " and (apply_no = #{code} or episode_id = #{code} or pat_id = #{code}) " +
            " and book_date &gt;= DATE_ADD(CURDATE(), INTERVAL -30 DAY) " +
            " and book_date &lt;= DATE_ADD(CURDATE(), INTERVAL 30 DAY) " +
            " <if test='checkTypeList != null and checkTypeList.size() > 0'> " +
            "   and book_check_type in ( <foreach collection='checkTypeList' separator=',' item='ct'>#{ct}</foreach> ) " +
            " </if> " +
            " order by book_date desc, book_timeslot desc, id desc " +
            "</script>")
    List<AppointmentDO> getEmergencyCandidates(@Param("code") String code,
                                               @Param("checkTypeList") List<Integer> checkTypeList);
 
    Integer updateStatusByApplyNo(@Param("applyNo") String applyNo, @Param("status") String status, @Param("registerDate") Date registerDate);
 
    /**
     * 只更新状态(不动其它字段)。
     *
     * <p>背景:状态流原来是把 {@code getByApplyNo} 查出来的整行对象再 {@code updateById} 写回,
     * 属于「读-改-写」。并发/交错时会把别的字段(如 book_date / book_check_type)按旧快照覆盖掉,
     * 实测 2026-10-05 07:58 就出现过一笔预约的 check_type 被状态流覆盖。
     * 状态流只该改状态,因此单独提供这个只改 status 的语句。</p>
     */
    @Update("update lihu.appointment set status = #{status}, update_time = sysdate() where apply_no = #{applyNo} and deleted = 0")
    Integer updateStatusOnlyByApplyNo(@Param("applyNo") String applyNo, @Param("status") String status);
    /** 【本次修复】补 {@code deleted = 0}:逻辑删除的行不应再被当作"已存在"而跳过插入。 */
    @Select("select * from lihu.appointment where deleted = 0 and episode_id = #{episodeId} and apply_no = #{applyNo} limit 1")
    AppointmentDO getByEpisodeIdAndApplyNo(@Param("episodeId") String episodeId, @Param("applyNo") String applyNo);
 
    /**
     * 按申请单号取未删除的记录。
     *
     * <p>注意:这里必须带 {@code deleted = 0}。原实现是裸 select,会把**逻辑删除**的旧行也捞出来,
     * 配合 {@code LIMIT 1} 时若旧行 id 更小就会取到已删除的那条 —— 这是"删了又被更新回来"的隐患。
     * 另外本方法配合唯一索引 {@code uk_appointment_apply_no_active}(只约束未删除行)使用。</p>
     */
    @Select("select * from lihu.appointment where apply_no = #{applyNo} and deleted = 0 limit 1")
    AppointmentDO getByApplyNo(@Param("applyNo") String applyNo);
 
    /**
     * 【需求1】按申请号批量查询,供列表页一次性回填患者信息,避免 N+1 查询。
     *
     * @param applyNoList 申请号列表(调用方需保证非空,MySQL 的 IN () 会语法错误)
     */
    @Select("<script>" +
            "select * from lihu.appointment where deleted = 0 and apply_no in (" +
            " <foreach collection='applyNoList' separator=',' item='_applyNo'>" +
            "   #{_applyNo} " +
            " </foreach> )" +
            "</script>")
    List<AppointmentDO> getByApplyNoList(@Param("applyNoList") List<String> applyNoList);
 
    /**
     * 取该患者**最近一条**未删除记录,用于容错新增时继承患者固有属性
     * (生日/电话/身份证/地址等 —— S050401 报文只带姓名,这些字段报文里没有)。
     *
     * <p>按 {@code create_time desc, id desc} 排序,保证结果稳定(同一秒内插入的多条也能定序)。
     * 只继承**患者固有属性**,不继承开单医生/开单时间等流水属性(那属于推断,可能张冠李戴)。</p>
     *
     * @param patId 患者编号({@code appointment.pat_id})
     * @return 最近一条记录;无记录时返回 {@code null}
     */
    @Select("select * from lihu.appointment where pat_id = #{patId} and deleted = 0 "
            + "order by create_time desc, id desc limit 1")
    AppointmentDO getLatestByPatId(@Param("patId") String patId);
}