陈昶聿
7 天以前 839e23b23b2bfb13282e9e5f621d32a1c55186ea
ruoyi-task/src/main/resources/mapper/task/ServiceSubtaskMapper.xml
@@ -108,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.ruoyi.task.domain.ServiceSubtaskCount" id="ServiceSubtaskResult2">
@@ -939,26 +940,59 @@
        </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 + send_day(缺省1) + Σ补偿天数 + 1 天;
             完成时间 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(
                     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(st.send_day, 1) 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
@@ -1418,6 +1452,25 @@
        where id = #{id}
    </update>
    <!-- 微信公众号已领取未填写按默认选项完成:只更新当前子任务。
         条件 sendstate=1 + 未提交,保证只处理当前被领取且未填写的子任务,不波及其他任务。 -->
    <update id="updateWechatDefaultComplete" parameterType="com.ruoyi.task.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.ruoyi.task.domain.ServiceSubtask">
        update service_subtask
        <trim prefix="SET" suffixOverrides=",">
@@ -1806,6 +1859,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,
@@ -1854,6 +1910,13 @@
            <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">
@@ -2100,6 +2163,13 @@
                #{hospitaldeptcode}
            </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">
            AND service_type IN
            <foreach collection="serviceType" item="serviceType" open="(" separator=","
@@ -2173,6 +2243,8 @@
    <resultMap type="com.ruoyi.task.domain.ServiceSubtaskStatistic" id="ServiceSubtaskScriptStatisticResult">
        <result property="groupCode" column="group_code"/>
        <result property="groupName" column="group_name"/>
        <result property="taskId" column="taskid"/>
        <result property="taskName" column="task_name"/>
        <result property="dischargeCount" column="discharge_count"/>
        <result property="firstVisitCount" column="first_visit_count"/>
        <result property="againVisitCount" column="again_visit_count"/>
@@ -2225,7 +2297,16 @@
        <!-- 分组键对应的显示名:病区统计时就是这一组的病区名。
             以前这一列是 MAX(hospitaldistrictname),一个组混了多个病区时会随机显示其中一个,
             于是出现“选血液内科病区却显示骨科中心三病区”。 -->
        MAX(IFNULL(leavehospitaldistrictname, hospitaldistrictname)) as group_name,
        <choose>
            <when test="groupKey != null and groupKey == 'taskid'">
                MAX(task_name) as group_name,
            </when>
            <otherwise>
                MAX(IFNULL(leavehospitaldistrictname, hospitaldistrictname)) as group_name,
            </otherwise>
        </choose>
        MAX(taskid) as taskid,
        MAX(task_name) as task_name,
        MAX(drcode) as drcode,
        MAX(drname) as drname,
        MAX(leaveldeptcode) as deptcode,
@@ -3103,6 +3184,136 @@
        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.ruoyi.task.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.deptcode 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.deptcode = #{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.deptcode = #{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">
        select id,