liusheng
2026-11-03 b5d55e1dd793c01f08786f805f01345bca64c893
ruoyi-task/src/main/resources/mapper/task/ServiceSubtaskMapper.xml
@@ -1861,9 +1861,17 @@
    <select id="getSfStatisticsGroupKey" parameterType="com.ruoyi.task.domain.ServiceSubtaskCountReq"
            resultType="String">
        select
        <if test="groupKey != null and groupKey != ''">
        <choose>
            <when test="groupColumn != null and groupColumn != ''">
                ${groupColumn} as groupKey
            </when>
            <when test="groupKey != null and groupKey != ''">
            ${groupKey} as groupKey
        </if>
            </when>
            <otherwise>
                NULL as groupKey
            </otherwise>
        </choose>
        from service_subtask
        where 1=1
        and del_flag = 0
@@ -1877,6 +1885,29 @@
             南华满意度子任务的 leavehospitaldistrictcode 有 83%(3236/3811)为 NULL,
             原来「科室 AND 病区」两个条件永远不可能同时成立 → 整张统计表空。
             判空放行只影响病区为空的记录,有病区的数据行为不变。 -->
        <choose>
            <!-- 满意度统计(getSfStatisticsScript):改用“有效病区”列,
                 出院病区为空则回退到在院病区。筛选因此真正生效,
                 空病区的记录也会归到它真实的在院病区,而不是混进用户选中的病区。 -->
            <when test="districtColumn != null and districtColumn != ''">
                <!-- 出院病区为空回退到在院病区。这里刻意写成 OR 展开而不是 IFNULL(a,b) IN (...):
                     列被函数包住就用不了索引,56 万行明细表上是全表扫(实测 5.1s → 0.7s)。
                     两个分支分别命中 idx_ss_district_pair 的 (leave..., hospital...) 前缀。 -->
                <if test="leavehospitaldistrictcodes != null and leavehospitaldistrictcodes.size() > 0">
                    AND (leavehospitaldistrictcode IN
                    <foreach collection="leavehospitaldistrictcodes" item="leavehospitaldistrictcode" open="(" separator=","
                             close=")">
                        #{leavehospitaldistrictcode}
                    </foreach>
                    OR (leavehospitaldistrictcode IS NULL AND hospitaldistrictcode IN
                    <foreach collection="leavehospitaldistrictcodes" item="leavehospitaldistrictcode" open="(" separator=","
                             close=")">
                        #{leavehospitaldistrictcode}
                    </foreach>
                    ))
                </if>
            </when>
            <otherwise>
        <if test="leavehospitaldistrictcodes != null and leavehospitaldistrictcodes.size() > 0">
            AND (leavehospitaldistrictcode IS NULL OR leavehospitaldistrictcode IN
            <foreach collection="leavehospitaldistrictcodes" item="leavehospitaldistrictcode" open="(" separator=","
@@ -1885,6 +1916,8 @@
            </foreach>
            )
        </if>
            </otherwise>
        </choose>
        <if test="leaveldeptcodes != null and leaveldeptcodes.size() > 0">
            AND leaveldeptcode IN
            <foreach collection="leaveldeptcodes" item="leaveldeptcode" open="(" separator=","
@@ -1947,11 +1980,20 @@
        <if test="visitCount != null and visitCount > 1">
            AND visit_count > 1
        </if>
        <if test="groupKey != null and groupKey != ''">
        <choose>
            <when test="groupColumn != null and groupColumn != ''">
                group by ${groupColumn}
                order by ${groupColumn}
            </when>
            <when test="groupKey != null and groupKey != ''">
            group by ${groupKey}
        </if>
                order by ${groupKey}
            </when>
        </choose>
        <!-- pageNum 是 1 起始的,OFFSET 必须用 (pageNum-1)*pageSize;
             以前直接写 OFFSET ${pageNum},第一页会跳掉第一条分组。 -->
        <if test="pageSize != null  and pageNum != null">
            limit ${pageSize} OFFSET ${pageNum}
            limit ${pageSize} OFFSET ${pageOffset}
        </if>
    </select>
@@ -1977,6 +2019,8 @@
    -->
    <resultMap type="com.ruoyi.task.domain.ServiceSubtaskStatistic" id="ServiceSubtaskScriptStatisticResult">
        <result property="groupCode" column="group_code"/>
        <result property="groupName" column="group_name"/>
        <result property="dischargeCount" column="discharge_count"/>
        <result property="firstVisitCount" column="first_visit_count"/>
        <result property="againVisitCount" column="again_visit_count"/>
        <result property="drcode" column="drcode"/>
@@ -2025,6 +2069,10 @@
            resultMap="ServiceSubtaskScriptStatisticResult">
        select
        IFNULL(${groupColumn}, 'Unknown') as group_code,
        <!-- 分组键对应的显示名:病区统计时就是这一组的病区名。
             以前这一列是 MAX(hospitaldistrictname),一个组混了多个病区时会随机显示其中一个,
             于是出现“选血液内科病区却显示骨科中心三病区”。 -->
        MAX(IFNULL(leavehospitaldistrictname, hospitaldistrictname)) as group_name,
        MAX(drcode) as drcode,
        MAX(drname) as drname,
        MAX(leaveldeptcode) as deptcode,
@@ -2056,7 +2104,10 @@
        SUM(CASE WHEN visit_count > 1 and sendstate is not null and current_preachform = '3' THEN 1 ELSE 0 END) as voice_again,
        SUM(CASE WHEN visit_count > 1 and sendstate is not null and current_preachform = '4' THEN 1 ELSE 0 END) as sms_again,
        SUM(CASE WHEN visit_count > 1 and sendstate is not null and current_preachform = '5' THEN 1 ELSE 0 END) as we_chat_again,
        SUM(CASE WHEN visit_count > 1 and sendstate is not null and excep = '1' THEN 1 ELSE 0 END) as abnormal_again
        SUM(CASE WHEN visit_count > 1 and sendstate is not null and excep = '1' THEN 1 ELSE 0 END) as abnormal_again,
        <!-- 出院人次:原来的内存实现是 serviceSubtaskList.size(),即分组内命中的子任务条数。
             改成本语句后一直没有聚合这一列,页面上“出院人次”恒为 0,与应随访人次自相矛盾。 -->
        COUNT(*) as discharge_count
        from service_subtask
        where 1=1
        and del_flag = 0
@@ -2066,6 +2117,27 @@
        <if test="campusid != null and campusid != ''">
            and campusid = #{campusid}
        </if>
        <choose>
            <!-- 满意度统计:改用“有效病区”列(出院病区为空回退在院病区),筛选才真正生效 -->
            <when test="districtColumn != null and districtColumn != ''">
                <!-- 出院病区为空回退到在院病区。这里刻意写成 OR 展开而不是 IFNULL(a,b) IN (...):
                     列被函数包住就用不了索引,56 万行明细表上是全表扫(实测 5.1s → 0.7s)。
                     两个分支分别命中 idx_ss_district_pair 的 (leave..., hospital...) 前缀。 -->
                <if test="leavehospitaldistrictcodes != null and leavehospitaldistrictcodes.size() > 0">
                    AND (leavehospitaldistrictcode IN
                    <foreach collection="leavehospitaldistrictcodes" item="leavehospitaldistrictcode" open="(" separator=","
                             close=")">
                        #{leavehospitaldistrictcode}
                    </foreach>
                    OR (leavehospitaldistrictcode IS NULL AND hospitaldistrictcode IN
                    <foreach collection="leavehospitaldistrictcodes" item="leavehospitaldistrictcode" open="(" separator=","
                             close=")">
                        #{leavehospitaldistrictcode}
                    </foreach>
                    ))
                </if>
            </when>
            <otherwise>
        <if test="leavehospitaldistrictcodes != null and leavehospitaldistrictcodes.size() > 0">
            AND (leavehospitaldistrictcode IS NULL OR leavehospitaldistrictcode IN
            <foreach collection="leavehospitaldistrictcodes" item="leavehospitaldistrictcode" open="(" separator=","
@@ -2074,6 +2146,8 @@
            </foreach>
            )
        </if>
            </otherwise>
        </choose>
        <if test="deptcodes != null and deptcodes.size() > 0">
            AND leaveldeptcode IN
            <foreach collection="deptcodes" item="deptcode" open="(" separator=","
@@ -2134,8 +2208,10 @@
        <if test="visitCount != null and visitCount > 1">
            AND visit_count > 1
        </if>
        <if test="groupKey != null and groupKey != '' and groupKeyList != null and groupKeyList.size>0">
            AND IFNULL(${groupKey}, 'Unknown') IN
        <!-- 过滤列必须与 GROUP BY 的列一致:都用 groupColumn(满意度病区统计时它是
             IFNULL(出院病区, 在院病区) 这样的表达式),否则聚合出来的分组键过滤不上,题目量会全变 0。 -->
        <if test="groupColumn != null and groupColumn != '' and groupKeyList != null and groupKeyList.size>0">
            AND IFNULL(${groupColumn}, 'Unknown') IN
            <foreach collection="groupKeyList" item="key" open="(" separator=","
                     close=")">
                #{key}
@@ -2153,7 +2229,14 @@
    <select id="getSfStatisticsScriptJoySum" parameterType="com.ruoyi.task.domain.ServiceSubtaskCountReq"
            resultType="map">
        select
        <choose>
            <when test="districtColumn != null and districtColumn != ''">
                IFNULL(${districtColumn}, 'Unknown') as groupCode,
            </when>
            <otherwise>
        IFNULL(t.${groupColumn}, 'Unknown') as groupCode,
            </otherwise>
        </choose>
        SUM(CASE WHEN a.categoryid IN(${configValue})
        THEN 1 ELSE 0 END) as joyAllCount,
        SUM(CASE WHEN a.categoryid IN(${configValue})
@@ -2171,6 +2254,28 @@
        <if test="campusid != null and campusid != ''">
            and t.campusid = #{campusid}
        </if>
        <choose>
            <!-- 满意度统计:与聚合主查询一致,用“有效病区”列过滤,
                 否则题目量会和主查询的行对不上(分组键不一样,过滤不上就全变 0)。 -->
            <when test="districtColumn != null and districtColumn != ''">
                <!-- 出院病区为空回退到在院病区。这里刻意写成 OR 展开而不是 IFNULL(a,b) IN (...):
                     列被函数包住就用不了索引,56 万行明细表上是全表扫(实测 5.1s → 0.7s)。
                     两个分支分别命中 idx_ss_district_pair 的 (leave..., hospital...) 前缀。 -->
                <if test="leavehospitaldistrictcodes != null and leavehospitaldistrictcodes.size() > 0">
                    AND (leavehospitaldistrictcode IN
                    <foreach collection="leavehospitaldistrictcodes" item="leavehospitaldistrictcode" open="(" separator=","
                             close=")">
                        #{leavehospitaldistrictcode}
                    </foreach>
                    OR (leavehospitaldistrictcode IS NULL AND hospitaldistrictcode IN
                    <foreach collection="leavehospitaldistrictcodes" item="leavehospitaldistrictcode" open="(" separator=","
                             close=")">
                        #{leavehospitaldistrictcode}
                    </foreach>
                    ))
                </if>
            </when>
            <otherwise>
        <if test="leavehospitaldistrictcodes != null and leavehospitaldistrictcodes.size() > 0">
            AND (t.leavehospitaldistrictcode IS NULL OR t.leavehospitaldistrictcode IN
            <foreach collection="leavehospitaldistrictcodes" item="leavehospitaldistrictcode" open="(" separator=","
@@ -2179,6 +2284,8 @@
            </foreach>
            )
        </if>
            </otherwise>
        </choose>
        <if test="deptcodes != null and deptcodes.size() > 0">
            AND t.leaveldeptcode IN
            <foreach collection="deptcodes" item="deptcode" open="(" separator=","
@@ -2239,14 +2346,28 @@
        <if test="visitCount != null and visitCount > 1">
            AND t.visit_count > 1
        </if>
        <if test="groupKey != null and groupKey != '' and groupKeyList != null and groupKeyList.size>0">
        <if test="districtColumn != null and districtColumn != '' and groupKeyList != null and groupKeyList.size>0">
            AND IFNULL(${districtColumn}, 'Unknown') IN
            <foreach collection="groupKeyList" item="key" open="(" separator=","
                     close=")">
                #{key}
            </foreach>
        </if>
        <if test="(districtColumn == null or districtColumn == '') and groupKey != null and groupKey != '' and groupKeyList != null and groupKeyList.size>0">
            AND IFNULL(t.${groupKey}, 'Unknown') IN
            <foreach collection="groupKeyList" item="key" open="(" separator=","
                     close=")">
                #{key}
            </foreach>
        </if>
        <choose>
            <when test="districtColumn != null and districtColumn != ''">
                group by IFNULL(${districtColumn}, 'Unknown')
            </when>
            <otherwise>
        group by IFNULL(t.${groupColumn}, 'Unknown')
            </otherwise>
        </choose>
    </select>
    <select id="getSfStatisticsHyperlink" parameterType="com.ruoyi.task.domain.ServiceSubtaskCountReq"
@@ -2300,13 +2421,20 @@
            and campusid = #{campusid}
        </if>
        <!-- 2026-10-21:病区为空时视为"不限"(南华满意度 83% 子任务无病区),有病区的数据行为不变 -->
        <!-- 与 getSfStatisticsScript 的汇总行保持一致:出院病区为空则回退到在院病区,
             否则点“查看详情”查出来的条数会和汇总行的数字对不上。 -->
        <if test="leavehospitaldistrictcodes != null and leavehospitaldistrictcodes.size() > 0">
            AND (leavehospitaldistrictcode IS NULL OR leavehospitaldistrictcode IN
            AND (leavehospitaldistrictcode IN
            <foreach collection="leavehospitaldistrictcodes" item="leavehospitaldistrictcode" open="(" separator=","
                     close=")">
                #{leavehospitaldistrictcode}
            </foreach>
            )
            OR (leavehospitaldistrictcode IS NULL AND hospitaldistrictcode IN
            <foreach collection="leavehospitaldistrictcodes" item="leavehospitaldistrictcode" open="(" separator=","
                     close=")">
                #{leavehospitaldistrictcode}
            </foreach>
            ))
        </if>
        <if test="deptcodes != null and deptcodes.size() > 0">
            AND leaveldeptcode IN
@@ -2637,11 +2765,17 @@
            AND ss.campusid = #{campusid}
        </if>
        <if test="leavehospitaldistrictcodes != null and leavehospitaldistrictcodes.size() > 0">
            AND ss.leavehospitaldistrictcode IN
            AND (ss.leavehospitaldistrictcode IN
            <foreach collection="leavehospitaldistrictcodes" item="leavehospitaldistrictcode" open="(" separator=","
                     close=")">
                #{leavehospitaldistrictcode}
            </foreach>
            OR (ss.leavehospitaldistrictcode IS NULL AND ss.hospitaldistrictcode IN
            <foreach collection="leavehospitaldistrictcodes" item="leavehospitaldistrictcode" open="(" separator=","
                     close=")">
                #{leavehospitaldistrictcode}
            </foreach>
            ))
        </if>
        <if test="deptcodes != null and deptcodes.size() > 0">
            AND ss.leaveldeptcode IN
@@ -2719,11 +2853,17 @@
            and ss.campusid = #{campusid}
        </if>
        <if test="leavehospitaldistrictcodes != null and leavehospitaldistrictcodes.size() > 0">
            AND ss.leavehospitaldistrictcode IN
            AND (ss.leavehospitaldistrictcode IN
            <foreach collection="leavehospitaldistrictcodes" item="leavehospitaldistrictcode" open="(" separator=","
                     close=")">
                #{leavehospitaldistrictcode}
            </foreach>
            OR (ss.leavehospitaldistrictcode IS NULL AND ss.hospitaldistrictcode IN
            <foreach collection="leavehospitaldistrictcodes" item="leavehospitaldistrictcode" open="(" separator=","
                     close=")">
                #{leavehospitaldistrictcode}
            </foreach>
            ))
        </if>
        <if test="deptcodes != null and deptcodes.size() > 0">
            AND ss.leaveldeptcode IN
@@ -3085,7 +3225,7 @@
        SUM(CASE WHEN sendstate = 1 THEN 1 ELSE 0 END) AS blq,
        SUM(CASE WHEN sendstate = 4 THEN 1 ELSE 0 END) AS wxsf,
        SUM(CASE WHEN sendstate != 4 THEN 1 ELSE 0 END) AS xsf,
        SUM(CASE WHEN (sendstate = 1 OR sendstate = 2 OR sendstate = 3 OR sendstate = 5 OR sendstate = 7 ) THEN 1 ELSE 0
        SUM(CASE WHEN (sendstate = 1 OR sendstate = 2 OR sendstate = 3 OR sendstate = 7 ) THEN 1 ELSE 0
        END) AS dsf,
        SUM(CASE WHEN sendstate != 2 THEN 1 ELSE 0 END) AS yfs,
        SUM(CASE WHEN sendstate = 2 THEN 1 ELSE 0 END) AS wfs,
@@ -3282,7 +3422,8 @@
        当前登录人「待处理」随访任务,按出院年月(endtime)分组统计,出院随访 / 门诊随访分开计数。
        口径(与随访列表「待随访」页签一致):
          1) 出院随访 service_type = '2',门诊随访 service_type = '3'
          2) 待随访(未完成)→ sendstate IN (1,2,3,5,7),排除 4 不执行 / 6 完成
          2) 统计状态 → sendstate IN (1,2,3,7):1 被领取 / 2 待发送 / 3 已发送 / 7 超时
             (不含 4 不执行、5 发送失败、6 已完成)
          3) 出院满 7 天 → endtime < (今天 - 7 天) + 1 天,
             即「7 天前那一天」出院的也计入(用 <= DATE_SUB(...) 只会截到当天 0 点,会漏掉 9 条)
          4) 只看最近 4 个出院月份(含当月)→ endtime >= 当月往前推 3 个月的 1 号
@@ -3302,7 +3443,8 @@
        FROM service_subtask
        WHERE del_flag = '0'
        AND service_type IN ('2', '3')
        AND sendstate IN (1, 2, 3, 5, 7)
        <!-- 浮窗「待处理随访任务」口径:1 被领取 / 2 待发送 / 3 已发送 / 7 超时 -->
        AND sendstate IN (1, 2, 3, 7)
        AND endtime IS NOT NULL
        AND endtime &lt; DATE_ADD(DATE_SUB(CURDATE(), INTERVAL 7 DAY), INTERVAL 1 DAY)
        AND endtime &gt;= DATE_FORMAT(DATE_SUB(CURDATE(), INTERVAL 3 MONTH), '%Y-%m-01')