Fix Bug #550: AI修复

This commit is contained in:
2026-05-27 03:00:08 +08:00
parent 8e6cb5c79f
commit 16c42ca108
5433 changed files with 171 additions and 778731 deletions

View File

@@ -1,461 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.openhis.appointmentmanage.mapper.ScheduleSlotMapper">
<!--
统一状态值映射: DB 数值 → 规范化输出
0=待约 1=已约(签到后) 2=锁定(预约后) 3=已签到 4=已停诊 5=已退号
-->
<sql id="slotStatusNormExpr">
CASE
WHEN LOWER(CONCAT('', s.status)) IN ('0', 'unbooked', 'available') THEN 0
WHEN LOWER(CONCAT('', s.status)) IN ('1', 'booked') THEN 1
WHEN LOWER(CONCAT('', s.status)) IN ('2', 'locked') THEN 2
WHEN LOWER(CONCAT('', s.status)) IN ('3', 'checked', 'checked_in', 'checkin') THEN 3
WHEN LOWER(CONCAT('', s.status)) IN ('4', 'cancelled', 'canceled', 'stopped') THEN 4
WHEN LOWER(CONCAT('', s.status)) IN ('5', 'returned') THEN 5
ELSE NULL
END
</sql>
<sql id="orderStatusNormExpr">
CASE
WHEN LOWER(CONCAT('', o.status)) IN ('1', 'booked') THEN 1
WHEN LOWER(CONCAT('', o.status)) IN ('2', 'checked', 'checked_in', 'checkin') THEN 2
WHEN LOWER(CONCAT('', o.status)) IN ('3', 'cancelled', 'canceled') THEN 3
WHEN LOWER(CONCAT('', o.status)) IN ('4', 'returned') THEN 4
ELSE NULL
END
</sql>
<sql id="poolStatusNormExpr">
CASE
WHEN LOWER(CONCAT('', p.status)) IN ('0', 'unbooked', 'available') THEN 0
WHEN LOWER(CONCAT('', p.status)) IN ('1', 'booked') THEN 1
WHEN LOWER(CONCAT('', p.status)) IN ('2', 'locked') THEN 2
WHEN LOWER(CONCAT('', p.status)) IN ('3', 'checked', 'checked_in', 'checkin') THEN 3
WHEN LOWER(CONCAT('', p.status)) IN ('4', 'cancelled', 'canceled', 'stopped') THEN 4
WHEN LOWER(CONCAT('', p.status)) IN ('5', 'returned') THEN 5
ELSE NULL
END
</sql>
<!-- 注意这里的 resultType 指向了您刚建好的 DTO 实体类 -->
<select id="selectAllTicketSlots" resultType="com.openhis.appointmentmanage.domain.TicketSlotDTO">
SELECT
s.id AS slotId,
s.seq_no AS seqNo,
p.schedule_id AS scheduleId,
p.doctor_name AS doctor,
p.dept_id AS departmentId,
p.fee AS fee,
o.patient_id AS patientId,
o.patient_name AS patientName,
o.medical_card AS medicalCard,
o.phone AS phone,
<include refid="orderStatusNormExpr" /> AS orderStatus,
<include refid="slotStatusNormExpr" /> AS slotStatus,
s.expect_time AS expectTime,
p.schedule_date AS scheduleDate,
d.reg_type AS regType,
<include refid="poolStatusNormExpr" /> AS poolStatus,
p.stop_reason AS stopReason,
d.is_stopped AS isStopped
FROM
adm_schedule_slot s
INNER JOIN adm_schedule_pool p ON s.pool_id = p.id
LEFT JOIN adm_doctor_schedule d ON p.schedule_id = d.id
LEFT JOIN (
SELECT DISTINCT
ON (slot_id) slot_id,
patient_id,
patient_name,
medical_card,
phone,
status
FROM
order_main
WHERE
LOWER(CONCAT('', status)) IN ('1', '2', '4', 'booked', 'checked', 'checked_in', 'checkin', 'returned')
ORDER BY
slot_id,
create_time DESC
) o ON o.slot_id = s.id
WHERE
p.delete_flag = '0'
AND s.delete_flag = '0'
ORDER BY
p.schedule_date,
p.doctor_id,
s.expect_time,
s.seq_no ASC
</select>
<select id="selectTicketSlotById" resultType="com.openhis.appointmentmanage.domain.TicketSlotDTO">
SELECT
s.id AS slotId,
s.seq_no AS seqNo,
p.schedule_id AS scheduleId,
p.doctor_name AS doctor,
p.doctor_id AS doctorId,
p.dept_id AS departmentId,
org.name AS departmentName,
p.fee AS fee,
o.patient_id AS patientId,
o.patient_name AS patientName,
o.medical_card AS medicalCard,
o.phone AS phone,
o.id AS orderId,
o.order_no AS orderNo,
COALESCE(CAST(o.gender AS VARCHAR), CAST(pinfo.gender_enum AS VARCHAR)) AS patientGender,
pinfo.gender_enum AS genderEnum,
pinfo.id_card AS idCard,
o.appointment_time AS appointmentTime,
<include refid="orderStatusNormExpr" /> AS orderStatus,
<include refid="slotStatusNormExpr" /> AS slotStatus,
s.expect_time AS expectTime,
p.schedule_date AS scheduleDate,
d.reg_type AS regType,
<include refid="poolStatusNormExpr" /> AS poolStatus,
p.stop_reason AS stopReason,
d.is_stopped AS isStopped
FROM
adm_schedule_slot s
INNER JOIN adm_schedule_pool p ON s.pool_id = p.id
LEFT JOIN adm_doctor_schedule d ON p.schedule_id = d.id
LEFT JOIN adm_organization org ON p.dept_id = org.id
AND org.delete_flag = '0'
LEFT JOIN (
SELECT DISTINCT
ON (slot_id) slot_id,
patient_id,
patient_name,
medical_card,
phone,
id,
order_no,
gender,
appointment_time,
status
FROM
order_main
WHERE
LOWER(CONCAT('', status)) IN ('1', '2', '4', 'booked', 'checked', 'checked_in', 'checkin', 'returned')
ORDER BY
slot_id,
create_time DESC
) o ON o.slot_id = s.id
LEFT JOIN adm_patient pinfo ON o.patient_id = pinfo.id
WHERE
s.id = #{id}
</select>
<!-- 预约锁定: 0→#{lockedStatus} (AVAILABLE→LOCKED),由枚举传入 -->
<update id="lockSlotForBooking">
UPDATE adm_schedule_slot
SET
status = #{lockedStatus},
update_time = now()
WHERE
id = #{slotId}
AND status = 0
AND delete_flag = '0'
</update>
<!-- status=0(待约)时清空order_id释放号源使退号后号源可再被预约 -->
<update id="updateSlotStatus">
UPDATE adm_schedule_slot
SET
status = #{status},
<if test="status != null and status == 0">
order_id = NULL,
</if>
update_time = now()
WHERE
id = #{slotId}
AND delete_flag = '0'
</update>
<!-- 签到: #{requiredStatus}→#{status} (LOCKED→BOOKED),前置条件由枚举传入 -->
<update id="updateSlotStatusAndCheckInTime">
UPDATE adm_schedule_slot
SET
status = #{status},
check_in_time = #{checkInTime},
update_time = NOW()
WHERE
id = #{slotId}
AND status = #{requiredStatus}
AND delete_flag = '0'
</update>
<select id="selectPoolIdBySlotId" resultType="java.lang.Long">
SELECT
pool_id
FROM
adm_schedule_slot
WHERE
id = #{slotId}
AND delete_flag = '0'
</select>
<update id="bindOrderToSlot">
UPDATE adm_schedule_slot
SET
order_id = #{orderId},
update_time = now()
WHERE
id = #{slotId}
AND status = 2
AND delete_flag = '0'
</update>
<select id="selectTicketSlotsPage" resultType="com.openhis.appointmentmanage.domain.TicketSlotDTO">
SELECT
s.id AS slotId,
s.seq_no AS seqNo,
p.schedule_id AS scheduleId,
p.doctor_name AS doctor,
p.doctor_id AS doctorId,
p.dept_id AS departmentId,
org.name AS departmentName,
p.fee AS fee,
o.patient_id AS patientId,
o.patient_name AS patientName,
o.medical_card AS medicalCard,
o.phone AS phone,
o.id AS orderId,
o.order_no AS orderNo,
COALESCE(CAST(o.gender AS VARCHAR), CAST(pinfo.gender_enum AS VARCHAR)) AS patientGender,
pinfo.gender_enum AS genderEnum,
pinfo.id_card AS idCard,
o.appointment_time AS appointmentTime,
<include refid="orderStatusNormExpr" /> AS orderStatus,
<include refid="slotStatusNormExpr" /> AS slotStatus,
s.expect_time AS expectTime,
p.schedule_date AS scheduleDate,
d.reg_type AS regType,
<include refid="poolStatusNormExpr" /> AS poolStatus,
p.stop_reason AS stopReason,
d.is_stopped AS isStopped
FROM
adm_schedule_slot s
INNER JOIN adm_schedule_pool p ON s.pool_id = p.id
LEFT JOIN adm_doctor_schedule d ON p.schedule_id = d.id
LEFT JOIN adm_organization org ON p.dept_id = org.id
AND org.delete_flag = '0'
LEFT JOIN (
SELECT DISTINCT
ON (slot_id) slot_id,
patient_id,
patient_name,
medical_card,
phone,
id,
order_no,
gender,
appointment_time,
status
FROM
order_main
WHERE
LOWER(CONCAT('', status)) IN ('1', '2', '4', 'booked', 'checked', 'checked_in', 'checkin', 'returned')
ORDER BY
slot_id,
create_time DESC
) o ON o.slot_id = s.id
LEFT JOIN adm_patient pinfo ON o.patient_id = pinfo.id
<where>
p.delete_flag = '0'
AND s.delete_flag = '0'
<!-- 1. 按日期查 -->
<if test="query.date != null and query.date != ''">
AND p.schedule_date = CAST(#{query.date} AS DATE)
</if>
<!-- 2. 按科室查 -->
<if test="query.department != null and query.department != '' and query.department != 'all'">
AND org.name = #{query.department}
</if>
<if test="query.doctorId != null">
AND p.doctor_id = #{query.doctorId}
</if>
<!-- 3. 按号源类型查 (专家/普通) -->
<if test="query.type != null and query.type != ''">
<choose>
<when test="query.type == 'expert'">
AND d.reg_type = 1
</when>
<when test="query.type == 'general'">
AND (
d.reg_type != 1
OR d.reg_type IS NULL
)
</when>
</choose>
</if>
<!-- 4. 模糊搜索患者信息 -->
<if test="query.name != null and query.name != ''">
AND o.patient_name LIKE CONCAT('%', #{query.name}, '%')
</if>
<if test="query.card != null and query.card != ''">
AND o.medical_card LIKE CONCAT('%', #{query.card}, '%')
</if>
<if test="query.phone != null and query.phone != ''">
AND o.phone LIKE CONCAT('%', #{query.phone}, '%')
</if>
<!-- 5. 时间过滤: 仅待约(0)受时间限制,已锁定(2)/已约(1)/已签到(3)/已退号(5)不受影响 -->
AND (
(<include refid="slotStatusNormExpr" /> = 0 AND (p.schedule_date > CURRENT_DATE OR (p.schedule_date = CURRENT_DATE AND (CAST(p.schedule_date AS TIMESTAMP) + CAST(s.expect_time AS TIME)) >= NOW())))
OR <include refid="slotStatusNormExpr" /> = 1
OR <include refid="slotStatusNormExpr" /> = 2
OR <include refid="slotStatusNormExpr" /> = 3
OR <include refid="slotStatusNormExpr" /> = 5
OR <include refid="orderStatusNormExpr" /> = 4
)
<!-- 6. 状态筛选: unbooked(0) locked(2) booked(2) checked(1) cancelled(4) returned(5) -->
<if test="query.status != null and query.status != '' and query.status != 'all'">
<choose>
<when test="'unbooked'.equals(query.status) or '未预约'.equals(query.status)">
AND <include refid="slotStatusNormExpr" /> = 0
AND (
d.is_stopped IS NULL
OR d.is_stopped = FALSE
)
</when>
<when test="'booked'.equals(query.status) or '已预约'.equals(query.status)">
AND <include refid="slotStatusNormExpr" /> = 2
AND <include refid="orderStatusNormExpr" /> = 1
AND (
d.is_stopped IS NULL
OR d.is_stopped = FALSE
)
</when>
<when test="'locked'.equals(query.status) or '已锁定'.equals(query.status)">
AND <include refid="slotStatusNormExpr" /> = 2
AND <include refid="orderStatusNormExpr" /> = 1
AND (
d.is_stopped IS NULL
OR d.is_stopped = FALSE
)
</when>
<when test="'checked'.equals(query.status) or '已取号'.equals(query.status)">
AND <include refid="slotStatusNormExpr" /> = 1
AND (
d.is_stopped IS NULL
OR d.is_stopped = FALSE
)
</when>
<when test="'cancelled'.equals(query.status) or '已停诊'.equals(query.status) or '已取消'.equals(query.status)">
AND (
<include refid="slotStatusNormExpr" /> = 4
OR d.is_stopped = TRUE
)
</when>
<when test="'returned'.equals(query.status) or '已退号'.equals(query.status)">
AND (
<include refid="slotStatusNormExpr" /> = 5
OR <include refid="orderStatusNormExpr" /> = 4
)
</when>
<otherwise>
AND 1 = 2
</otherwise>
</choose>
</if>
</where>
ORDER BY
p.schedule_date DESC,
s.expect_time ASC,
s.seq_no ASC
</select>
<select id="selectDoctorAvailabilitySummary" resultType="com.openhis.appointmentmanage.domain.DoctorAvailabilityDTO">
SELECT
p.doctor_id AS doctorId,
p.doctor_name AS doctorName,
p.schedule_date AS scheduleDate,
<!-- 直接 COUNT 未预约的号源,前端已经做了时间过滤,这里只按日期统计 -->
COUNT(
CASE
WHEN s.delete_flag = '0'
AND <include refid="slotStatusNormExpr" /> = 0
<!-- 使用前端传来的当前时间戳过滤过期号源,保证和前端完全一致 -->
AND (
p.schedule_date > CURRENT_DATE
OR (
p.schedule_date = CURRENT_DATE
AND (CAST(p.schedule_date AS TIMESTAMP) + CAST(s.expect_time AS TIME)) >= NOW()
)
)
THEN s.id
ELSE NULL
END
) AS available,
COUNT(DISTINCT p.id) AS poolCount,
CASE
WHEN MAX(
CASE
WHEN d.reg_type = 1 THEN 1
ELSE 0
END
) = 1 THEN 'expert'
ELSE 'general'
END AS ticketType
FROM
adm_schedule_pool p
LEFT JOIN adm_doctor_schedule d ON p.schedule_id = d.id
LEFT JOIN adm_organization org ON p.dept_id = org.id AND org.delete_flag = '0'
LEFT JOIN adm_schedule_slot s ON s.pool_id = p.id AND s.delete_flag = '0'
<where>
p.delete_flag = '0'
<!-- 排除医生已停诊的号源 -->
AND (d.is_stopped IS NULL OR d.is_stopped = FALSE)
<!-- 过滤未来号源:只统计当前日期及未来日期的号源 -->
<if test="query.date != null and query.date != ''">
AND p.schedule_date = CAST(#{query.date} AS DATE)
</if>
<!-- 增加时间过滤:排除已过去的就诊日期 -->
AND (p.schedule_date > CURRENT_DATE OR (p.schedule_date = CURRENT_DATE AND (CAST(p.schedule_date AS TIMESTAMP) + CAST(s.expect_time AS TIME)) >= NOW()))
<if test="query.department != null and query.department != '' and query.department != 'all'">
AND org.name = #{query.department}
</if>
<if test="query.type != null and query.type != '' and query.type != 'all'">
<choose>
<when test="query.type == 'expert'">
AND d.reg_type = 1
</when>
<when test="query.type == 'general'">
AND (
d.reg_type != 1
OR d.reg_type IS NULL
)
</when>
</choose>
</if>
<if test="query.doctorId != null">
AND p.doctor_id = #{query.doctorId}
</if>
</where>
GROUP BY
p.doctor_id,
p.doctor_name,
p.schedule_date
ORDER BY
p.schedule_date ASC,
p.doctor_name ASC
</select>
<select id="selectSeqNoBySlotIds" resultType="com.openhis.appointmentmanage.domain.ScheduleSlot">
SELECT id, seq_no
FROM adm_schedule_slot
WHERE id IN
<foreach collection="slotIds" item="slotId" open="(" separator="," close=")">
#{slotId}
</foreach>
AND delete_flag = '0'
</select>
</mapper>