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 >= DATE_ADD(CURDATE(), INTERVAL -30 DAY) " +
|
" and book_date <= 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);
|
}
|