陈昶聿
5 天以前 55d724ddd52b2acb6e7ee2d291e04e4b6d96ad19
smartor/src/main/resources/mapper/smartor/ServiceSubtaskMapper.xml
@@ -27,7 +27,7 @@
        <result property="recordid" column="recordid"/>
        <result property="exrecallcount" column="exrecallcount"/>
        <result property="pulltime" column="pulltime"/>
        <result property="deptcode" column="deptcode"/>
        <result property="leaveldeptcode" column="leaveldeptcode"/>
        <result property="labelstatus" column="labelstatus"/>
        <result property="delFlag" column="del_flag"/>
        <result property="updateBy" column="update_by"/>
@@ -54,6 +54,11 @@
        <result property="leaveicd10code" column="leaveicd10code"/>
        <result property="leavediagname" column="leavediagname"/>
        <result property="diagname" column="diagname"/>
        <result property="leaveldeptname" column="leaveldeptname"/>
        <!-- 2026-11:原 sendlimit* 四个空闲列复用为「在院维度」(hospitaldistrict* = 在院病区,deptcode/name = 在院科室) -->
        <result property="hospitaldistrictcode" column="hospitaldistrictcode"/>
        <result property="hospitaldistrictname" column="hospitaldistrictname"/>
        <result property="deptcode" column="deptcode"/>
        <result property="deptname" column="deptname"/>
        <result property="admindate" column="admindate"/>
        <result property="roomno" column="roomno"/>
@@ -103,6 +108,7 @@
        <result property="loopCount" column="loop_count"/>
        <result property="isManuallyCalled" column="is_manually_called"/>
        <result property="campusid" column="campusid"/>
        <result property="diagType" column="diag_type"/>
    </resultMap>
    <resultMap type="com.smartor.domain.ServiceSubtaskCount" id="ServiceSubtaskResult2">
@@ -210,7 +216,7 @@
               recordid,
               exrecallcount,
               pulltime,
               deptcode,
               leaveldeptcode,
               labelstatus,
               del_flag,
               update_by,
@@ -233,6 +239,10 @@
               sendname,
               roomno,
               admindate,
               leaveldeptname,
               hospitaldistrictcode,
               hospitaldistrictname,
               deptcode,
               deptname,
               stop_state,
               leavehospitaldistrictcode,
@@ -257,50 +267,61 @@
    </sql>
    <!-- 超时完成、延期完成逻辑 完成时状态(finish_state)计算:仅当本次更新把 sendstate 置为 6(随访完成)时使用。
         基准截止时间 base = endtime + send_day(缺省1) + (Σ service_subtask_preachform.compensate_time + 1) 天,
         与 RyTask/RedisMqReceiver 的 isSubtaskDeadline 口径保持一致;
         基准截止时间 base:
           非任务组(appltype != 5):endtime + send_day(缺省1) + (Σ service_subtask_preachform.compensate_time + 1) 天;
           任务组(appltype = 5):按 schedule_benchmark 解析基准时间
                                   (1-入院 2-出院 3-就诊 4-入组 0/其它-自定义时间)
                                   + day_offset + (Σ compensate_time + 1) 天(不叠加 send_day);
         与 RyTask/RedisMqReceiver 的 isSubtaskDeadline、selectTimelyRateBatch 口径保持一致;
         delay = COALESCE(service_task.delay_finish_days, 0);
         finish_time = COALESCE(入参 finishtime, 库中 finishtime, NOW()):
           finish_time <= base                                    -> 1 正常完成
           delay > 0 且 finish_time <= base + delay               -> 2 延期完成
           其余(含 delay 为 0 或 NULL、或已超出延期窗口)          -> 3 超时完成
         finishtime 为空时用 NOW() 兜底,保证完成时一定写入状态;
         仅当 endtime 为空导致无法计算时才保持原值不覆盖。 -->
    <sql id="finishStateExpr">
         base 为空(endtime / schedule / day_offset 缺失)时保持原值不覆盖。 -->
    <sql id="finishDeadlineExpr">
        DATE_ADD(
        CASE
        WHEN service_subtask.endtime IS NULL THEN finish_state
            WHEN COALESCE(st.appltype, '') = '5' THEN
                DATE_ADD(
                CASE sts.schedule_benchmark
                    WHEN 1 THEN service_subtask.starttime
                    WHEN 2 THEN service_subtask.endtime
                    WHEN 3 THEN service_subtask.admindate
                    WHEN 4 THEN service_subtask.create_time
                    ELSE sts.schedule_benchmark_custom_time
                END,
                INTERVAL std.day_offset DAY)
            ELSE
                DATE_ADD(service_subtask.endtime, INTERVAL COALESCE(st.send_day, 1) DAY)
        END,
        INTERVAL (COALESCE((SELECT SUM(CAST(COALESCE(NULLIF(TRIM(pf.compensate_time), ''), '0') AS SIGNED))
        FROM service_subtask_preachform pf
        WHERE pf.subid = service_subtask.id AND pf.del_flag = 0), 0) + 1) DAY)
    </sql>
    <sql id="finishStateExpr">
        COALESCE((
        SELECT
        CASE
        WHEN <include refid="finishDeadlineExpr"/> IS NULL THEN NULL
        WHEN COALESCE(#{finishtime,jdbcType=TIMESTAMP}, service_subtask.finishtime, NOW())
        &lt;= DATE_ADD(
        DATE_ADD(
        service_subtask.endtime,
        INTERVAL COALESCE((SELECT st.send_day FROM service_task st
        WHERE st.taskid = service_subtask.taskid AND st.del_flag = '0' LIMIT 1), 1) DAY
        ),
        INTERVAL (COALESCE((SELECT SUM(CAST(COALESCE(NULLIF(TRIM(pf.compensate_time), ''), '0') AS SIGNED))
        FROM service_subtask_preachform pf
        WHERE pf.subid = service_subtask.id AND pf.del_flag = 0), 0) + 1) DAY
        )
        &lt;= <include refid="finishDeadlineExpr"/>
        THEN 1
        WHEN COALESCE((SELECT st.delay_finish_days FROM service_task st
        WHERE st.taskid = service_subtask.taskid AND st.del_flag = '0' LIMIT 1), 0) &gt; 0
        WHEN COALESCE(st.delay_finish_days, 0) &gt; 0
        AND COALESCE(#{finishtime,jdbcType=TIMESTAMP}, service_subtask.finishtime, NOW())
        &lt;= DATE_ADD(
        DATE_ADD(
        DATE_ADD(
        service_subtask.endtime,
        INTERVAL COALESCE((SELECT st.send_day FROM service_task st
        WHERE st.taskid = service_subtask.taskid AND st.del_flag = '0' LIMIT 1), 1) DAY
        ),
        INTERVAL (COALESCE((SELECT SUM(CAST(COALESCE(NULLIF(TRIM(pf.compensate_time), ''), '0') AS SIGNED))
        FROM service_subtask_preachform pf
        WHERE pf.subid = service_subtask.id AND pf.del_flag = 0), 0) + 1) DAY
        ),
        INTERVAL COALESCE((SELECT st.delay_finish_days FROM service_task st
        WHERE st.taskid = service_subtask.taskid AND st.del_flag = '0' LIMIT 1), 0) DAY
        )
        &lt;= DATE_ADD(<include refid="finishDeadlineExpr"/>,
        INTERVAL COALESCE(st.delay_finish_days, 0) DAY)
        THEN 2
        ELSE 3
        END
        FROM (SELECT 1) dummy
        LEFT JOIN service_task st ON st.taskid = service_subtask.taskid
        LEFT JOIN service_task_schedule sts ON sts.id = service_subtask.schedule_id
        LEFT JOIN service_task_schedule_detail std ON std.id = service_subtask.schedule_detail_id
        LIMIT 1
        ), finish_state)
    </sql>
    <!-- 当前执行方式:首次随访 current_preachform 为空时,取 preachform 的第一个执行方式兜底 -->
@@ -410,7 +431,6 @@
        <if test="managementDoctorCode != null ">and management_doctor_code = #{managementDoctorCode}</if>
        <if test="submit != null ">and submit = #{submit}</if>
        <if test="upid != null ">and upid = #{upid}</if>
        <if test="visitCount != null ">and visit_count = #{visitCount}</if>
        <if test="patfrom != null ">and patfrom = #{patfrom}</if>
        <if test="serviceType != null ">and service_type = #{serviceType}</if>
        <if test="patid != null ">and patid = #{patid}</if>
@@ -486,7 +506,7 @@
                </foreach>
            </if>
            <if test=" leaveldeptcodes != null and leaveldeptcodes.size()>0">
                AND deptcode IN
                AND leaveldeptcode IN
                <foreach collection="leaveldeptcodes" item="leaveldeptcode" open="(" separator=","
                         close=")">
                    #{leaveldeptcode}
@@ -508,7 +528,7 @@
                OR
            </if>
            <if test=" leaveldeptcodes != null and leaveldeptcodes.size() > 0">
                deptcode IN
                leaveldeptcode IN
                <foreach collection="leaveldeptcodes" item="leaveldeptcode" open="(" separator=","
                         close=")">
                    #{leaveldeptcode}
@@ -564,8 +584,8 @@
        <if test="recordid != null ">and recordid = #{recordid}</if>
        <if test="exrecallcount != null ">and exrecallcount = #{exrecallcount}</if>
        <if test="pulltime != null ">and pulltime = #{pulltime}</if>
        <if test="deptcode != null  and deptcode != ''">and deptcode = #{deptcode}</if>
        <if test="deptname != null  and deptname != ''">and deptname = #{deptname}</if>
        <if test="leaveldeptcode != null  and leaveldeptcode != ''">and leaveldeptcode = #{leaveldeptcode}</if>
        <if test="leaveldeptname != null  and leaveldeptname != ''">and leaveldeptname = #{leaveldeptname}</if>
        <if test="labelstatus != null ">and labelstatus = #{labelstatus}</if>
        <if test="isupload != null ">and isupload = #{isupload}</if>
        <if test="uploadTime != null ">and upload_time = #{uploadTime}</if>
@@ -651,7 +671,7 @@
            </foreach>
        </if>
        <if test="deptcodes != null and deptcodes.size() > 0">
            AND deptcode IN
            AND leaveldeptcode IN
            <foreach collection="deptcodes" item="deptcode" open="(" separator=","
                     close=")">
                #{deptcode}
@@ -750,8 +770,8 @@
        <if test="recordid != null ">and recordid = #{recordid}</if>
        <if test="exrecallcount != null ">and exrecallcount = #{exrecallcount}</if>
        <if test="pulltime != null ">and pulltime = #{pulltime}</if>
        <if test="deptcode != null  and deptcode != ''">and deptcode = #{deptcode}</if>
        <if test="deptname != null  and deptname != ''">and deptname = #{deptname}</if>
        <if test="leaveldeptcode != null  and leaveldeptcode != ''">and leaveldeptcode = #{leaveldeptcode}</if>
        <if test="leaveldeptname != null  and leaveldeptname != ''">and leaveldeptname = #{leaveldeptname}</if>
        <if test="labelstatus != null ">and labelstatus = #{labelstatus}</if>
        <if test="isupload != null ">and isupload = #{isupload}</if>
        <if test="uploadTime != null ">and upload_time = #{uploadTime}</if>
@@ -827,8 +847,8 @@
        <if test="campusid != null and campusid != ''">and campusid = #{campusid}</if>
        <if test="serviceType != null and serviceType != ''">and service_type = #{serviceType}</if>
        <if test="serviceTypes != null and serviceTypes != ''">and service_type IN (#{serviceTypes})</if>
        <if test="deptcode != null  and deptcode != ''">and deptcode = #{deptcode}</if>
        <if test="deptname != null  and deptname != ''">and deptname = #{deptname}</if>
        <if test="leaveldeptcode != null  and leaveldeptcode != ''">and leaveldeptcode = #{leaveldeptcode}</if>
        <if test="leaveldeptname != null  and leaveldeptname != ''">and leaveldeptname = #{leaveldeptname}</if>
        <if test="isabnormal != null">and isabnormal = #{isabnormal}</if>
        <if test="leavehospitaldistrictcode != null  and leavehospitaldistrictcode != ''">and
            leavehospitaldistrictcode = #{leavehospitaldistrictcode}
@@ -854,26 +874,57 @@
        </if>
        SUM(CASE WHEN ss.finishtime IS NOT NULL AND DATE(ss.visit_time) &lt;= DATE(ss.finishtime) THEN 1 ELSE 0 END) /
        COUNT(*) AS finishRate,
        <if test="rateDay != null">
            SUM(CASE WHEN ss.finishtime IS NOT NULL
            AND DATEDIFF(ss.finishtime, ss.endtime) &lt;= #{rateDay} + COALESCE(cp.compensate_days, 0)
            THEN 1 ELSE 0 END) / COUNT(*) AS rate
        </if>
        <if test="rateDay == null">
            NULL AS rate
        </if>
        <!-- 及时率基准:
             1) 非任务组(appltype != 5):endtime + send_day(缺省1) + Σ补偿天数 + 1 天;
             2) 任务组(appltype = 5):按 schedule_benchmark 解析基准时间
                (1-入院时间 2-出院时间 3-就诊时间 4-入组时间 0/其它-自定义时间)
                + day_offset + Σ补偿天数 + 1 天(不再叠加 send_day);
             完成时间 finishtime <= 基准时间计为及时,分母仍为应随访数。
             补偿天数只取 service_subtask_preachform.compensate_time 的汇总值一次,
             避免与补偿明细相加造成重复补偿或一对多放大分母。 -->
        SUM(CASE
            WHEN ss.finishtime IS NOT NULL
             AND COALESCE(st.appltype, '') != '5'
             AND ss.endtime IS NOT NULL
             AND ss.finishtime &lt;= DATE_ADD(
                   DATE_ADD(ss.endtime, INTERVAL COALESCE(st.send_day, 1) DAY),
                   INTERVAL (COALESCE(cp.compensate_days, 0) + 1) DAY)
            THEN 1
            WHEN ss.finishtime IS NOT NULL
             AND st.appltype = '5'
             AND std.day_offset IS NOT NULL
             AND ss.finishtime &lt;= DATE_ADD(
                   DATE_ADD(
                       CASE sts.schedule_benchmark
                         WHEN 1 THEN ss.starttime
                         WHEN 2 THEN ss.endtime
                         WHEN 3 THEN ss.admindate
                         WHEN 4 THEN ss.create_time
                         ELSE sts.schedule_benchmark_custom_time
                       END, INTERVAL std.day_offset DAY),
                   INTERVAL (COALESCE(cp.compensate_days, 0) + 1) DAY)
            THEN 1
            ELSE 0
        END) / COUNT(*) AS rate
        FROM service_subtask ss
        <if test="rateDay != null">
            <!-- 补偿时间:先按子任务聚合成一行再关联,避免一对多放大 COUNT(*) 分母。
                 compensate_time 是 varchar 且可能存成空串,需先置空再转数字 -->
            LEFT JOIN (
            SELECT subid,
            SUM(CAST(COALESCE(NULLIF(TRIM(compensate_time), ''), '0') AS SIGNED)) AS compensate_days
            FROM service_subtask_preachform
            WHERE del_flag = 0
            GROUP BY subid
            ) cp ON cp.subid = ss.id
        </if>
        <!-- 这里按 taskid / 历史 schedule 快照关联,不按 del_flag 过滤:
             任务或计划被编辑后旧 schedule/detail 会置为 del_flag=1,
             但历史 subtask 仍引用旧 ID,过滤会丢失历史及时率基准。 -->
        LEFT JOIN service_task st
               ON st.taskid = ss.taskid
        LEFT JOIN service_task_schedule sts
               ON sts.id = ss.schedule_id
        LEFT JOIN service_task_schedule_detail std
               ON std.id = ss.schedule_detail_id
        <!-- 补偿时间:先按子任务聚合成一行再关联,避免一对多放大 COUNT(*) 分母。
             compensate_time 是 varchar 且可能存成空串,需先置空再转数字 -->
        LEFT JOIN (
        SELECT subid,
        SUM(CAST(COALESCE(NULLIF(TRIM(compensate_time), ''), '0') AS SIGNED)) AS compensate_days
        FROM service_subtask_preachform
        WHERE del_flag = 0
        GROUP BY subid
        ) cp ON cp.subid = ss.id
        WHERE ss.del_flag = 0
        AND ss.visit_time IS NOT NULL
        AND ss.sendstate != 4
@@ -886,8 +937,8 @@
        </if>
        <if test="orgid != null and orgid != ''">AND ss.orgid = #{orgid}</if>
        <if test="campusid != null and campusid != ''">and ss.campusid = #{campusid}</if>
        <if test="deptcode != null  and deptcode != ''">and ss.deptcode = #{deptcode}</if>
        <if test="deptname != null  and deptname != ''">and ss.deptname = #{deptname}</if>
        <if test="leaveldeptcode != null  and leaveldeptcode != ''">and ss.leaveldeptcode = #{leaveldeptcode}</if>
        <if test="leaveldeptname != null  and leaveldeptname != ''">and ss.leaveldeptname = #{leaveldeptname}</if>
        <if test="isabnormal != null">and ss.isabnormal = #{isabnormal}</if>
        <if test="leavehospitaldistrictcode != null  and leavehospitaldistrictcode != ''">and
            ss.leavehospitaldistrictcode = #{leavehospitaldistrictcode}
@@ -949,8 +1000,8 @@
        <if test="serviceType != null and serviceType != ''">and service_type = #{serviceType}</if>
        <if test="serviceTypes != null and serviceTypes != ''">and service_type IN (#{serviceTypes})</if>
        <if test="isabnormal != null ">and isabnormal = #{isabnormal}</if>
        <if test="deptcode != null  and deptcode != ''">and deptcode = #{deptcode}</if>
        <if test="deptname != null  and deptname != ''">and deptname = #{deptname}</if>
        <if test="leaveldeptcode != null  and leaveldeptcode != ''">and leaveldeptcode = #{leaveldeptcode}</if>
        <if test="leaveldeptname != null  and leaveldeptname != ''">and leaveldeptname = #{leaveldeptname}</if>
        <if test="leavehospitaldistrictcode != null  and leavehospitaldistrictcode != ''">and
            leavehospitaldistrictcode = #{leavehospitaldistrictcode}
        </if>
@@ -1022,7 +1073,11 @@
            <if test="recordid != null">recordid,</if>
            <if test="exrecallcount != null">exrecallcount,</if>
            <if test="pulltime != null">pulltime,</if>
            <if test="deptcode != null">deptcode,</if>
            <if test="leaveldeptcode != null">leaveldeptcode,</if>
            <if test="hospitaldistrictcode != null  and hospitaldistrictcode != ''">hospitaldistrictcode,</if>
            <if test="hospitaldistrictname != null  and hospitaldistrictname != ''">hospitaldistrictname,</if>
            <if test="deptcode != null  and deptcode != ''">deptcode,</if>
            <if test="deptname != null  and deptname != ''">deptname,</if>
            <if test="labelstatus != null">labelstatus,</if>
            <if test="updateBy != null">update_by,</if>
            <if test="updateTime != null">update_time,</if>
@@ -1047,7 +1102,7 @@
            <if test="wechat != null  and wechat != ''">wechat,</if>
            <if test="roomno != null  and roomno != ''">roomno,</if>
            <if test="admindate != null">admindate,</if>
            <if test="deptname != null  and deptname != ''">deptname,</if>
            <if test="leaveldeptname != null  and leaveldeptname != ''">leaveldeptname,</if>
            <if test="leaveicd10code != null  and leaveicd10code != ''">leaveicd10code,</if>
            <if test="leavediagname != null  and leavediagname != ''">leavediagname,</if>
            <if test="libtemplateid != null ">libtemplateid,</if>
@@ -1122,7 +1177,11 @@
            <if test="recordid != null">#{recordid},</if>
            <if test="exrecallcount != null">#{exrecallcount},</if>
            <if test="pulltime != null">#{pulltime},</if>
            <if test="deptcode != null">#{deptcode},</if>
            <if test="leaveldeptcode != null">#{leaveldeptcode},</if>
            <if test="hospitaldistrictcode != null  and hospitaldistrictcode != ''">#{hospitaldistrictcode},</if>
            <if test="hospitaldistrictname != null  and hospitaldistrictname != ''">#{hospitaldistrictname},</if>
            <if test="deptcode != null  and deptcode != ''">#{deptcode},</if>
            <if test="deptname != null  and deptname != ''">#{deptname},</if>
            <if test="labelstatus != null">#{labelstatus},</if>
            <if test="updateBy != null">#{updateBy},</if>
            <if test="updateTime != null">#{updateTime},</if>
@@ -1147,7 +1206,7 @@
            <if test="wechat != null  and wechat != ''">#{wechat},</if>
            <if test="roomno != null  and roomno != ''">#{roomno},</if>
            <if test="admindate != null ">#{admindate},</if>
            <if test="deptname != null  and deptname != ''">#{deptname},</if>
            <if test="leaveldeptname != null  and leaveldeptname != ''">#{leaveldeptname},</if>
            <if test="leaveicd10code != null  and leaveicd10code != ''">#{leaveicd10code},</if>
            <if test="leavediagname != null  and leavediagname != ''">#{leavediagname},</if>
            <if test="libtemplateid != null ">#{libtemplateid},</if>
@@ -1235,7 +1294,7 @@
            <if test="recordid != null">recordid = #{recordid},</if>
            <if test="exrecallcount != null">exrecallcount = #{exrecallcount},</if>
            <if test="pulltime != null">pulltime = #{pulltime},</if>
            <if test="deptcode != null">deptcode = #{deptcode},</if>
            <if test="leaveldeptcode != null">leaveldeptcode = #{leaveldeptcode},</if>
            <if test="labelstatus != null">labelstatus = #{labelstatus},</if>
            <if test="updateBy != null">update_by = #{updateBy},</if>
            <if test="updateTime != null">update_time = #{updateTime},</if>
@@ -1260,7 +1319,7 @@
            <if test="wechat != null  and wechat != ''">wechat = #{wechat},</if>
            <if test="roomno != null  and roomno != ''">roomno = #{roomno},</if>
            <if test="admindate != null ">admindate = #{admindate},</if>
            <if test="deptname != null  and deptname != ''">deptname = #{deptname},</if>
            <if test="leaveldeptname != null  and leaveldeptname != ''">leaveldeptname = #{leaveldeptname},</if>
            <if test="leaveicd10code != null  and leaveicd10code != ''">leaveicd10code = #{leaveicd10code},</if>
            <if test="leavediagname != null  and leavediagname != ''">leavediagname = #{leavediagname},</if>
            <if test="libtemplateid != null ">libtemplateid = #{libtemplateid},</if>
@@ -1319,6 +1378,25 @@
        where id = #{id}
    </update>
    <!-- 微信公众号已领取未填写按默认选项完成:只更新当前子任务。
         条件 sendstate=1 + 未提交,保证只处理当前被领取且未填写的子任务,不波及其他任务。 -->
    <update id="updateWechatDefaultComplete" parameterType="com.smartor.domain.ServiceSubtask">
        update service_subtask
        set sendstate = 6,
            finishtime = #{finishtime},
            result = #{result},
            remark = case
                         when remark is null or remark = '' then #{remark}
                         else concat(remark, ',', #{remark})
                     end,
            update_time = now(),
            finish_state = <include refid="finishStateExpr"/>
        where id = #{id}
          and orgid = #{orgid}
          and sendstate = 1
          and (submit is null or submit != 1)
    </update>
    <update id="updateServiceSubtaskByPatId" parameterType="com.smartor.domain.ServiceSubtask">
        update service_subtask
        <trim prefix="SET" suffixOverrides=",">
@@ -1341,7 +1419,7 @@
            <if test="recordid != null">recordid = #{recordid},</if>
            <if test="exrecallcount != null">exrecallcount = #{exrecallcount},</if>
            <if test="pulltime != null">pulltime = #{pulltime},</if>
            <if test="deptcode != null">deptcode = #{deptcode},</if>
            <if test="leaveldeptcode != null">leaveldeptcode = #{leaveldeptcode},</if>
            <if test="labelstatus != null">labelstatus = #{labelstatus},</if>
            <if test="updateBy != null">update_by = #{updateBy},</if>
            <if test="updateTime != null">update_time = #{updateTime},</if>
@@ -1366,7 +1444,7 @@
            <if test="wechat != null  and wechat != ''">wechat = #{wechat},</if>
            <if test="roomno != null  and roomno != ''">roomno = #{roomno},</if>
            <if test="admindate != null">admindate = #{admindate},</if>
            <if test="deptname != null  and deptname != ''">deptname = #{deptname},</if>
            <if test="leaveldeptname != null  and leaveldeptname != ''">leaveldeptname = #{leaveldeptname},</if>
            <if test="leaveicd10code != null  and leaveicd10code != ''">leaveicd10code = #{leaveicd10code},</if>
            <if test="leavediagname != null  and leavediagname != ''">leavediagname = #{leavediagname},</if>
            <if test="libtemplateid != null ">libtemplateid = #{libtemplateid},</if>
@@ -1448,7 +1526,7 @@
            <if test="recordid != null">recordid = #{recordid},</if>
            <if test="exrecallcount != null">exrecallcount = #{exrecallcount},</if>
            <if test="pulltime != null">pulltime = #{pulltime},</if>
            <if test="deptcode != null">deptcode = #{deptcode},</if>
            <if test="leaveldeptcode != null">leaveldeptcode = #{leaveldeptcode},</if>
            <if test="labelstatus != null">labelstatus = #{labelstatus},</if>
            <if test="updateBy != null">update_by = #{updateBy},</if>
            <if test="updateTime != null">update_time = #{updateTime},</if>
@@ -1475,7 +1553,7 @@
            <if test="wechat != null  and wechat != ''">wechat = #{wechat},</if>
            <if test="roomno != null  and roomno != ''">roomno = #{roomno},</if>
            <if test="admindate != null">admindate = #{admindate},</if>
            <if test="deptname != null  and deptname != ''">deptname = #{deptname},</if>
            <if test="leaveldeptname != null  and leaveldeptname != ''">leaveldeptname = #{leaveldeptname},</if>
            <if test="leaveicd10code != null  and leaveicd10code != ''">leaveicd10code = #{leaveicd10code},</if>
            <if test="leavediagname != null  and leavediagname != ''">leavediagname = #{leavediagname},</if>
            <if test="libtemplateid != null ">libtemplateid = #{libtemplateid},</if>
@@ -1561,7 +1639,7 @@
            <if test="recordid != null">recordid = #{recordid},</if>
            <if test="exrecallcount != null">exrecallcount = #{exrecallcount},</if>
            <if test="pulltime != null">pulltime = #{pulltime},</if>
            <if test="deptcode != null">deptcode = #{deptcode},</if>
            <if test="leaveldeptcode != null">leaveldeptcode = #{leaveldeptcode},</if>
            <if test="labelstatus != null">labelstatus = #{labelstatus},</if>
            <if test="updateBy != null">update_by = #{updateBy},</if>
            <if test="updateTime != null">update_time = #{updateTime},</if>
@@ -1588,7 +1666,7 @@
            <if test="admindate != null ">admindate = #{admindate},</if>
            <if test="visitCount != null ">visit_count = #{visitCount},</if>
            <if test="isVisitAgain != null ">is_visit_again = #{isVisitAgain},</if>
            <if test="deptname != null  and deptname != ''">deptname = #{deptname},</if>
            <if test="leaveldeptname != null  and leaveldeptname != ''">leaveldeptname = #{leaveldeptname},</if>
            <if test="leaveicd10code != null  and leaveicd10code != ''">leaveicd10code = #{leaveicd10code},</if>
            <if test="leavediagname != null  and leavediagname != ''">leavediagname = #{leavediagname},</if>
            <if test="libtemplateid != null ">libtemplateid = #{libtemplateid},</if>
@@ -1697,8 +1775,8 @@
        management_doctor,
        leavehospitaldistrictcode,
        leavehospitaldistrictname,
        deptcode,
        deptname,
        leaveldeptcode,
        leaveldeptname,
        sendstate,
        preachform,
        <include refid="effectiveCurrentPreachformExpr"/> AS current_preachform,
@@ -1707,6 +1785,9 @@
        nurse_name,
        taskid,
        task_name,
        <if test="groupKey != null and groupKey == 'taskid'">
            (SELECT st.diag_type FROM service_task st WHERE st.taskid = service_subtask.taskid LIMIT 1) AS diag_type,
        </if>
        visit_time,
        visit_dept_code,
        finishtime,
@@ -1746,10 +1827,17 @@
            </foreach>
        </if>
        <if test="deptcodes != null and deptcodes.size() > 0">
            AND deptcode IN
            AND leaveldeptcode IN
            <foreach collection="deptcodes" item="deptcode" open="(" separator=","
                     close=")">
                #{deptcode}
            </foreach>
        </if>
        <if test="taskIds != null and taskIds.size() > 0">
            AND taskid IN
            <foreach collection="taskIds" item="taskId" open="(" separator=","
                     close=")">
                #{taskId}
            </foreach>
        </if>
        <if test="serviceType != null and serviceType.size() > 0">
@@ -1864,7 +1952,7 @@
            </foreach>
        </if>
        <if test="deptcodes != null and deptcodes.size() > 0">
            AND b.deptcode IN
            AND b.leaveldeptcode IN
            <foreach collection="deptcodes" item="deptcode" open="(" separator=","
                     close=")">
                #{deptcode}
@@ -1929,10 +2017,17 @@
            </foreach>
        </if>
        <if test="deptcodes != null and deptcodes.size() > 0">
            AND deptcode IN
            AND leaveldeptcode IN
            <foreach collection="deptcodes" item="deptcode" open="(" separator=","
                     close=")">
                #{deptcode}
            </foreach>
        </if>
        <if test="taskIds != null and taskIds.size() > 0">
            AND taskid IN
            <foreach collection="taskIds" item="taskId" open="(" separator=","
                     close=")">
                #{taskId}
            </foreach>
        </if>
        <if test="serviceType != null and serviceType.size() > 0">
@@ -1970,9 +2065,6 @@
        <if test="groupKey != null and groupKey != ''">
            group by ${groupKey}
        </if>
        <if test="pageSize != null  and pageNum != null">
            limit ${pageSize} OFFSET ${pageNum}
        </if>
    </select>
    <select id="getSfStatisticsHyperlink" parameterType="com.smartor.domain.ServiceSubtaskCountReq"
@@ -1986,8 +2078,8 @@
        drname,
        leavehospitaldistrictcode,
        leavehospitaldistrictname,
        deptcode,
        deptname,
        leaveldeptcode,
        leaveldeptname,
        sendstate,
        preachform,
        <include refid="effectiveCurrentPreachformExpr"/> AS current_preachform,
@@ -2038,7 +2130,7 @@
            </foreach>
        </if>
        <if test="deptcodes != null and deptcodes.size() > 0">
            AND deptcode IN
            AND leaveldeptcode IN
            <foreach collection="deptcodes" item="deptcode" open="(" separator=","
                     close=")">
                #{deptcode}
@@ -2070,10 +2162,15 @@
        <if test="type != null">
            and type = #{type}
        </if>
        <if test="visitCount != null and visitCount == 1">
        <!-- 首个再次随访:精确查 visit_count = 2,与统计口径一致 -->
        <if test="hyperLinkInfoType == 'againFollowUpPushCountInfo'">
            AND visit_count = 2
        </if>
        <!-- 其他类型保持原语义:1 首次,>1 再次 -->
        <if test="hyperLinkInfoType != 'againFollowUpPushCountInfo' and visitCount != null and visitCount == 1">
            AND visit_count = 1
        </if>
        <if test="visitCount != null and visitCount > 1">
        <if test="hyperLinkInfoType != 'againFollowUpPushCountInfo' and visitCount != null and visitCount > 1">
            AND visit_count > 1
        </if>
        <if test="groupKey != null and groupKey != '' and groupKeyList != null and groupKeyList.size>0">
@@ -2112,10 +2209,13 @@
            AND leavehospitaldistrictcode = #{leavehospitaldistrictcode}
        </if>
        <if test="deptcode != null and deptcode != ''">
            AND deptcode = #{deptcode}
            AND leaveldeptcode = #{deptcode}
        </if>
        <if test="isManuallyCalled != null">
            AND is_manually_called = #{isManuallyCalled}
        </if>
        <if test="suggest != null and suggest != ''">
            AND TRIM(suggest) = #{suggest}
        </if>
        order by endtime desc
        <if test="pageSize != null  and pageNum != null">
@@ -2146,7 +2246,7 @@
            </foreach>
        </if>
        <if test="deptcodes != null and deptcodes.size() > 0">
            AND deptcode IN
            AND leaveldeptcode IN
            <foreach collection="deptcodes" item="deptcode" open="(" separator=","
                     close=")">
                #{deptcode}
@@ -2193,7 +2293,7 @@
            </foreach>
        </if>
        <if test="deptcodes != null and deptcodes.size() > 0">
            AND deptcode IN
            AND leaveldeptcode IN
            <foreach collection="deptcodes" item="deptcode" open="(" separator=","
                     close=")">
                #{deptcode}
@@ -2229,7 +2329,7 @@
            </foreach>
        </if>
        <if test="deptcodes != null and deptcodes.size() > 0">
            AND deptcode IN
            AND leaveldeptcode IN
            <foreach collection="deptcodes" item="deptcode" open="(" separator=","
                     close=")">
                #{deptcode}
@@ -2286,7 +2386,7 @@
            </foreach>
        </if>
        <if test="deptcodes != null and deptcodes.size() > 0">
            AND t.deptcode IN
            AND t.leaveldeptcode IN
            <foreach collection="deptcodes" item="deptcode" open="(" separator=","
                     close=")">
                #{deptcode}
@@ -2372,7 +2472,7 @@
            </foreach>
        </if>
        <if test="deptcodes != null and deptcodes.size() > 0">
            AND ss.deptcode IN
            AND ss.leaveldeptcode IN
            <foreach collection="deptcodes" item="deptcode" open="(" separator=","
                     close=")">
                #{deptcode}
@@ -2444,7 +2544,7 @@
            </foreach>
        </if>
        <if test="deptcodes != null and deptcodes.size() > 0">
            AND ss.deptcode IN
            AND ss.leaveldeptcode IN
            <foreach collection="deptcodes" item="deptcode" open="(" separator=","
                     close=")">
                #{deptcode}
@@ -2469,6 +2569,136 @@
            AND ssd.categoryid IN (${configValue})
        </if>
        group by a.script_content, ss.type, IFNULL(ssd.matchedtext,ssd.asrtext)
    </select>
    <!--
      问题选项数字下钻:返回选择了指定选项的 service_subtask 列表。
      过滤口径与 getSfStatisticsScriptDetails / getSfStatisticsScriptItemDetails 保持一致,
      额外支持 statisticaltype:1-病区、2-科室、3-病种。
    -->
    <select id="getSfStatisticsScriptDetailsHyperlink" parameterType="com.smartor.domain.ServiceSubtaskCountReq"
            resultMap="ServiceSubtaskResult">
        SELECT ss.*
        FROM service_subtask ss
        WHERE ss.del_flag = 0
        <if test="orgid != null and orgid != ''">
            AND ss.orgid = #{orgid}
        </if>
        <if test="campusid != null and campusid != ''">
            AND ss.campusid = #{campusid}
        </if>
        <if test="leavehospitaldistrictcodes != null and leavehospitaldistrictcodes.size() > 0">
            AND ss.leavehospitaldistrictcode IN
            <foreach collection="leavehospitaldistrictcodes" item="leavehospitaldistrictcode" open="(" separator=","
                     close=")">
                #{leavehospitaldistrictcode}
            </foreach>
        </if>
        <if test="deptcodes != null and deptcodes.size() > 0">
            AND ss.leaveldeptcode IN
            <foreach collection="deptcodes" item="deptcode" open="(" separator="," close=")">
                #{deptcode}
            </foreach>
        </if>
        <if test="serviceType != null and serviceType.size() > 0">
            AND ss.service_type IN
            <foreach collection="serviceType" item="serviceType" open="(" separator="," close=")">
                #{serviceType}
            </foreach>
        </if>
        <if test="startTime != null and endTime != null">
            AND ss.visit_time >= DATE(#{startTime})
            AND ss.visit_time &lt; DATE_ADD(DATE(#{endTime}), INTERVAL 1 DAY)
        </if>
        <if test="type != null and type != ''">
            AND ss.type = #{type}
        </if>
        <!-- 统计维度:1-按病区 2-按科室 3-按taskid -->
        <if test="statisticaltype != null and statisticaltype == 1">
            <choose>
                <when test="deptcode != null and deptcode != ''">
                    AND ss.leavehospitaldistrictcode = #{deptcode}
                </when>
                <when test="leavehospitaldistrictcode != null and leavehospitaldistrictcode != ''">
                    AND ss.leavehospitaldistrictcode = #{leavehospitaldistrictcode}
                </when>
            </choose>
        </if>
        <if test="statisticaltype != null and statisticaltype == 2 and deptcode != null and deptcode != ''">
            AND ss.leaveldeptcode = #{deptcode}
        </if>
        <if test="statisticaltype != null and statisticaltype == 3 and taskId != null">
            AND ss.taskid = #{taskId}
        </if>
        <!-- 兼容不传 statisticaltype 时按单值条件过滤 -->
        <if test="statisticaltype == null">
            <if test="leavehospitaldistrictcode != null and leavehospitaldistrictcode != ''">
                AND ss.leavehospitaldistrictcode = #{leavehospitaldistrictcode}
            </if>
            <if test="deptcode != null and deptcode != ''">
                AND ss.leaveldeptcode = #{deptcode}
            </if>
            <if test="taskId != null">
                AND ss.taskid = #{taskId}
            </if>
        </if>
        <!-- 题目 + 选项匹配:只保留回答中选择了该选项的子任务 -->
        AND EXISTS (
            SELECT 1
            FROM service_subtask_detail ssd
            WHERE ssd.sub_id = ss.id
              AND ssd.del_flag = 0
            <if test="configValue != null and configValue != ''">
                AND ssd.categoryid IN (${configValue})
            </if>
            <choose>
                <when test="optionText != null">
                    AND IFNULL(ssd.matchedtext, ssd.asrtext) = #{optionText}
                </when>
                <otherwise>
                    AND 1 = 0
                </otherwise>
            </choose>
            <choose>
                <!-- 优先按题目内容匹配:同一道题复制到多个任务模板时 scriptid 不同,但 script_content 相同 -->
                <when test="scriptContent != null and scriptContent != ''">
                    AND ssd.scriptid IN (
                        <choose>
                            <when test="type == '1'">
                                SELECT its.id FROM ivr_task_template_script its
                                WHERE its.script_content = #{scriptContent}
                            </when>
                            <when test="type == '2'">
                                SELECT sts.id FROM svy_task_template_script sts
                                WHERE sts.script_content = #{scriptContent}
                            </when>
                            <otherwise>
                                SELECT its.id FROM ivr_task_template_script its
                                WHERE its.script_content = #{scriptContent}
                                UNION
                                SELECT sts.id FROM svy_task_template_script sts
                                WHERE sts.script_content = #{scriptContent}
                            </otherwise>
                        </choose>
                    )
                </when>
                <when test="scriptid != null">
                    AND ssd.scriptid = #{scriptid}
                </when>
                <otherwise>
                    AND 1 = 0
                </otherwise>
            </choose>
        )
        ORDER BY ss.endtime DESC, ss.id DESC
        <if test="pageSize != null and pageSize > 0">
            LIMIT #{pageSize}
            <if test="pageNum != null and pageNum >= 0">
                OFFSET #{pageNum}
            </if>
        </if>
    </select>
    <select id="getDataByTime" resultMap="ServiceSubtaskResult">
@@ -2497,7 +2727,7 @@
               recordid,
               exrecallcount,
               pulltime,
               deptcode,
               leaveldeptcode,
               labelstatus,
               del_flag,
               upid,
@@ -2527,7 +2757,7 @@
               admindate,
               patid,
               service_type,
               deptname,
               leaveldeptname,
               score,
               drname,
               drcode,
@@ -2587,7 +2817,7 @@
    </select>
    <select id="getDeptRanking" parameterType="com.smartor.domain.PatMedReq" resultType="com.smartor.domain.PatMedRes">
        SELECT deptname,
        SELECT leaveldeptname AS deptname,
        count(1) AS rc
        FROM service_subtask
        WHERE DATE_FORMAT(visit_time, '%Y-%m-%d') >= #{startDate}
@@ -2600,7 +2830,7 @@
        <if test="campusid != null and campusid != ''">
            AND campusid = #{campusid}
        </if>
        GROUP BY deptname
        GROUP BY leaveldeptname
    </select>
    <!-- 服务统计查询 - 按时间维度统计出院/门诊随访数据 -->
@@ -2651,7 +2881,7 @@
            AND campusid = #{campusid}
        </if>
        <if test="deptcodes != null and deptcodes.size() > 0">
            AND deptcode IN
            AND leaveldeptcode IN
            <foreach collection="deptcodes" item="deptcode" open="(" separator="," close=")">
                #{deptcode}
            </foreach>
@@ -2816,7 +3046,7 @@
                </foreach>
            </if>
            <if test=" leaveldeptcodes != null and leaveldeptcodes.size()>0">
                AND deptcode IN
                AND leaveldeptcode IN
                <foreach collection="leaveldeptcodes" item="leaveldeptcode" open="(" separator=","
                         close=")">
                    #{leaveldeptcode}
@@ -2838,7 +3068,7 @@
                OR
            </if>
            <if test=" leaveldeptcodes != null and leaveldeptcodes.size()>0">
                deptcode IN
                leaveldeptcode IN
                <foreach collection="leaveldeptcodes" item="leaveldeptcode" open="(" separator=","
                         close=")">
                    #{leaveldeptcode}
@@ -2941,14 +3171,14 @@
                </foreach>
            </if>
            <if test="leaveldeptcodes != null and leaveldeptcodes.size() > 0">
                AND deptcode IN
                AND leaveldeptcode IN
                <foreach collection="leaveldeptcodes" item="leaveldeptcode" open="(" separator="," close=")">
                    #{leaveldeptcode}
                </foreach>
            </if>
        </if>
        <if test="deptcode != null and deptcode != ''">
            AND deptcode = #{deptcode}
        <if test="leaveldeptcode != null and leaveldeptcode != ''">
            AND leaveldeptcode = #{leaveldeptcode}
        </if>
        <if test="sendstate != null">
            AND sendstate = #{sendstate}
@@ -3012,7 +3242,7 @@
            AND date_format( visit_time, '%y%m%d' ) &lt;= date_format(#{endDate},'%y%m%d')
        </if>
        <if test="deptcodeList != null and deptcodeList.size()>0">
            and deptcode in
            and leaveldeptcode in
            <foreach collection="deptcodeList" item="deptcode" open="(" separator="," close=")">
                #{deptcode}
            </foreach>
@@ -3086,7 +3316,7 @@
                OR
            </if>
            <if test=" leaveldeptcodes != null and leaveldeptcodes.size()>0">
                ss.deptcode IN
                ss.leaveldeptcode IN
                <foreach collection="leaveldeptcodes" item="leaveldeptcode" open="(" separator=","
                         close=")">
                    #{leaveldeptcode}
@@ -3134,7 +3364,7 @@
                OR
            </if>
            <if test="deptcodes != null and deptcodes.size() > 0">
                deptcode IN
                leaveldeptcode IN
                <foreach collection="deptcodes" item="deptcode" open="(" separator=","
                         close=")">
                    #{deptcode}
@@ -3184,14 +3414,14 @@
            parameterType="com.smartor.domain.VO.ServiceSubtaskCotinueCountVO"
            resultType="java.util.Map">
        SELECT
        deptcode AS groupCode,
        deptname AS groupName,
        leaveldeptcode AS groupCode,
        leaveldeptname AS groupName,
        SUM(CASE WHEN continue_flag = 1 THEN 1 ELSE 0 END) AS noContinueCnt,
        SUM(CASE WHEN continue_flag = 2 THEN 1 ELSE 0 END) AS continueCnt
        FROM service_subtask
        WHERE del_flag = 0
        AND continue_flag IN (1, 2)
        AND deptcode IS NOT NULL AND deptcode != ''
        AND leaveldeptcode IS NOT NULL AND leaveldeptcode != ''
        <if test="orgid != null and orgid != ''">
            AND orgid = #{orgid}
        </if>
@@ -3199,12 +3429,12 @@
            AND campusid = #{campusid}
        </if>
        <if test="deptcodes != null and deptcodes.size() > 0">
            AND deptcode IN
            AND leaveldeptcode IN
            <foreach collection="deptcodes" item="code" open="(" separator="," close=")">
                #{code}
            </foreach>
        </if>
        GROUP BY deptcode, deptname
        GROUP BY leaveldeptcode, leaveldeptname
        ORDER BY groupCode
    </select>
@@ -3217,8 +3447,8 @@
                leavehospitaldistrictname AS groupName,
            </when>
            <otherwise>
                deptcode AS groupCode,
                deptname AS groupName,
                leaveldeptcode AS groupCode,
                leaveldeptname AS groupName,
            </otherwise>
        </choose>
        COUNT(*) AS totalCount,
@@ -3255,12 +3485,12 @@
            </when>
            <otherwise>
                <if test="deptcodes != null and deptcodes.size() > 0">
                    AND deptcode IN
                    AND leaveldeptcode IN
                    <foreach collection="deptcodes" item="code" open="(" separator="," close=")">
                        #{code}
                    </foreach>
                </if>
                GROUP BY deptcode, deptname
                GROUP BY leaveldeptcode, leaveldeptname
                ORDER BY groupCode
            </otherwise>
        </choose>
@@ -3407,8 +3637,8 @@
        drname,
        leavehospitaldistrictcode,
        leavehospitaldistrictname,
        deptcode,
        deptname,
        leaveldeptcode,
        leaveldeptname,
        sendstate,
        preachform,
        current_preachform,
@@ -3462,7 +3692,7 @@
            </foreach>
        </if>
        <if test="deptcodes != null and deptcodes.size() > 0">
            AND deptcode IN
            AND leaveldeptcode IN
            <foreach collection="deptcodes" item="deptcode" open="(" separator=","
                     close=")">
                #{deptcode}
@@ -3653,8 +3883,8 @@
        t.task_name AS taskName,
        d.asrtext AS optionresult,
        d.score AS score,
        s.deptcode AS deptcode,
        s.deptname AS deptname,
        s.leaveldeptcode AS deptcode,
        s.leaveldeptname AS deptname,
        s.leavehospitaldistrictcode AS leavehospitaldistrictcode,
        s.leavehospitaldistrictname AS leavehospitaldistrictname,
        d.targetvalue AS targetvalue,
@@ -3761,8 +3991,8 @@
        d.targetvalue,
        d.asrtext,
        t.task_name,
        s.deptcode,
        s.deptname,
        s.leaveldeptcode,
        s.leaveldeptname,
        s.leavehospitaldistrictcode,
        s.leavehospitaldistrictname,
        d.value_type
@@ -3774,7 +4004,7 @@
    <select id="statQuestionOptionByKsOrBq" resultType="com.smartor.domain.DTO.QuestionCountKsOrBqDTO"
            parameterType="com.smartor.domain.VO.QuestionCountKsOrBqVO">
        SELECT
        s.deptname AS deptname,
        IFNULL(s.leaveldeptname, s.deptname) AS deptname,
        s.leavehospitaldistrictname AS leavehospitaldistrictname,
        d.questiontext AS questionText,
        d.targetvalue AS targetvalue, -- 所有选项拼接
@@ -3804,7 +4034,7 @@
                </foreach>
            </if>
            <if test=" leaveldeptcodes != null and leaveldeptcodes.size()>0">
                AND deptcode IN
                AND leaveldeptcode IN
                <foreach collection="leaveldeptcodes" item="leaveldeptcode" open="(" separator=","
                         close=")">
                    #{leaveldeptcode}
@@ -3821,7 +4051,7 @@
                </foreach>
            </if>
            <if test=" leaveldeptcodes != null and leaveldeptcodes.size()>0">
                OR deptcode IN
                OR leaveldeptcode IN
                <foreach collection="leaveldeptcodes" item="leaveldeptcode" open="(" separator=","
                         close=")">
                    #{leaveldeptcode}
@@ -3856,8 +4086,8 @@
                s.leavehospitaldistrictname AS name,
            </when>
            <otherwise>
                s.deptcode AS code,
                s.deptname AS name,
                s.leaveldeptcode AS code,
                s.leaveldeptname AS name,
            </otherwise>
        </choose>
        CAST(d.score AS DECIMAL(10,4)) AS w
@@ -3910,10 +4140,10 @@
                </if>
            </when>
            <otherwise>
                AND s.deptcode IS NOT NULL AND s.deptcode &lt;&gt; ''
                AND s.deptname IS NOT NULL AND s.deptname &lt;&gt; ''
                AND s.leaveldeptcode IS NOT NULL AND s.leaveldeptcode &lt;&gt; ''
                AND s.leaveldeptname IS NOT NULL AND s.leaveldeptname &lt;&gt; ''
                <if test="deptCodes != null and deptCodes.size() > 0">
                    AND s.deptcode IN
                    AND s.leaveldeptcode IN
                    <foreach collection="deptCodes" item="deptCode" open="(" separator="," close=")">
                        #{deptCode}
                    </foreach>