liusheng
2026-09-28 93078044580c2781cb8cf3ac18d2ab90513267de
smartor/src/main/resources/mapper/smartor/ServiceSubtaskMapper.xml
@@ -98,6 +98,7 @@
        <result property="scheduleDetailId" column="schedule_detail_id"/>
        <result property="scheduleSeq" column="schedule_seq"/>
        <result property="loopCount" column="loop_count"/>
        <result property="isManuallyCalled" column="is_manually_called"/>
        <result property="campusid" column="campusid"/>
    </resultMap>
@@ -245,7 +246,8 @@
               schedule_detail_id,
               schedule_seq,
               loop_count,
               task_situation
               task_situation,
               is_manually_called
        from service_subtask
    </sql>
@@ -513,6 +515,7 @@
        <if test="createBy != null">and create_by = #{createBy}</if>
        <if test="taskGuid != null">and task_guid = #{taskGuid}</if>
        <if test="isabnormal != null">and isabnormal = #{isabnormal}</if>
        <if test="isManuallyCalled != null">and is_manually_called = #{isManuallyCalled}</if>
        <if test="isVisitAgain != null">and is_visit_again = #{isVisitAgain}</if>
        <if test="visitCount != null and visitCount > 1 and visitDeptCodes != null and visitDeptCodes.size() > 0">
            AND visit_dept_code IN
@@ -568,6 +571,43 @@
            AND date_format(visit_time,'%y%m%d') &lt;= date_format(#{endTime},'%y%m%d')
        </if>
    </select>
    <!--
        满意度统计:只查本次条件下实际存在哪些 type。
        过滤条件与 selectSatisfactionSubtaskList 完全一致,只是:
          1) 只 select distinct type(原来是把上百个字段全查回来);
          2) visit_time 改成可走索引的范围条件(原来对列套 date_format,索引用不上)。
    -->
    <select id="selectSatisfactionSubtaskTypes" parameterType="com.smartor.domain.VO.PatSatisfactionReqVO"
            resultType="java.lang.String">
        select distinct type
        from service_subtask
        where del_flag = 0
        <if test="questionType != null">and type = #{questionType}</if>
        <if test="hospitaldistrictcodes != null and hospitaldistrictcodes.size() > 0">
            AND leavehospitaldistrictcode IN
            <foreach collection="hospitaldistrictcodes" item="hospitaldistrictcode" open="(" separator=","
                     close=")">
                #{hospitaldistrictcode}
            </foreach>
        </if>
        <if test="deptcodes != null and deptcodes.size() > 0">
            AND deptcode IN
            <foreach collection="deptcodes" item="deptcode" open="(" separator="," close=")">
                #{deptcode}
            </foreach>
        </if>
        <if test="serviceTypes != null and serviceTypes.size() > 0">
            AND service_type IN
            <foreach collection="serviceTypes" item="serviceType" open="(" separator="," close=")">
                #{serviceType}
            </foreach>
        </if>
        <if test="startTime != null and endTime != null">
            AND visit_time &gt;= #{startTime}
            AND visit_time &lt; date_add(#{endTime}, interval 1 day)
        </if>
    </select>
    <select id="getCompensateServiceSubtaskList" parameterType="com.smartor.domain.entity.ServiceSubtaskEntity"
            resultMap="ServiceSubtaskResult">
        <include refid="selectServiceSubtaskVo"/>
@@ -976,6 +1016,7 @@
            <if test="scheduleDetailId != null ">schedule_detail_id,</if>
            <if test="scheduleSeq != null ">schedule_seq,</if>
            <if test="loopCount != null ">loop_count,</if>
            <if test="isManuallyCalled != null ">is_manually_called,</if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="sendname != null">#{sendname},</if>
@@ -1073,6 +1114,7 @@
            <if test="scheduleDetailId != null ">#{scheduleDetailId},</if>
            <if test="scheduleSeq != null ">#{scheduleSeq},</if>
            <if test="loopCount != null ">#{loopCount},</if>
            <if test="isManuallyCalled != null ">#{isManuallyCalled},</if>
        </trim>
    </insert>
@@ -1175,6 +1217,7 @@
            <if test="scheduleDetailId != null ">schedule_detail_id = #{scheduleDetailId},</if>
            <if test="scheduleSeq != null ">schedule_seq = #{scheduleSeq},</if>
            <if test="loopCount != null ">loop_count = #{loopCount},</if>
            <if test="isManuallyCalled != null ">is_manually_called = #{isManuallyCalled},</if>
        </trim>
        where id = #{id}
    </update>
@@ -1273,6 +1316,7 @@
            <if test="continueCount != null ">continue_count = #{continueCount},</if>
            <if test="continueTimeNext != null ">continue_time_next = #{continueTimeNext,jdbcType=TIMESTAMP},</if>
            <if test="continueContent != null ">continue_content = #{continueContent},</if>
            <if test="isManuallyCalled != null ">is_manually_called = #{isManuallyCalled},</if>
        </trim>
        where 1=1
        <if test="patid != null ">and patid = #{patid}</if>
@@ -1375,6 +1419,7 @@
            <if test="continueCount != null ">continue_count = #{continueCount},</if>
            <if test="continueTimeNext != null ">continue_time_next = #{continueTimeNext,jdbcType=TIMESTAMP},</if>
            <if test="continueContent != null ">continue_content = #{continueContent},</if>
            <if test="isManuallyCalled != null ">is_manually_called = #{isManuallyCalled},</if>
        </trim>
        <where>
            <if test="patid != null ">
@@ -1385,6 +1430,9 @@
            </if>
            <if test="id != null ">
                and id = #{id}
            </if>
            <if test="inhospid != null ">
                and inhospid = #{inhospid}
            </if>
        </where>
    </update>
@@ -1482,6 +1530,7 @@
            <if test="continueCount != null ">continue_count = #{continueCount},</if>
            <if test="continueTimeNext != null ">continue_time_next = #{continueTimeNext,jdbcType=TIMESTAMP},</if>
            <if test="continueContent != null ">continue_content = #{continueContent},</if>
            <if test="isManuallyCalled != null ">is_manually_called = #{isManuallyCalled},</if>
        </trim>
        where task_guid = #{taskGuid} and task_name = #{taskName}
    </update>
@@ -2572,6 +2621,8 @@
        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
        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,
        SUM(CASE WHEN sendstate = 6 THEN 1 ELSE 0 END) AS ywc,
        SUM(CASE WHEN excep IS NOT NULL AND excep = '1' THEN 1 ELSE 0 END) AS yc,
        SUM(CASE WHEN excep IS NOT NULL AND excep = '2' THEN 1 ELSE 0 END) AS jg,
@@ -2584,6 +2635,12 @@
        </if>
        <if test="phone != null and phone != ''">
            AND phone = #{phone}
        </if>
        <if test="finishtimeStart != null">
            AND date_format(finishtime,'%y%m%d') &gt;= date_format(#{finishtimeStart},'%y%m%d')
        </if>
        <if test="finishtimeEnd != null">
            AND date_format(finishtime,'%y%m%d') &lt;= date_format(#{finishtimeEnd},'%y%m%d')
        </if>
        <if test="deptOrDistrict==1">
            <if test="leavehospitaldistrictcodes != null and leavehospitaldistrictcodes.size() > 0">
@@ -2876,6 +2933,60 @@
    </select>
    <!--
        满意度柱状图:一次性统计本次请求全部模板的发送量。
        与 querySendCount 的过滤条件完全一致,区别是:
          1) 按 libtemplateid 分组,一次返回所有模板(替代逐个模板查询);
          2) visit_time 改成可走索引的范围条件(原来对列套 date_format,索引用不上)。
    -->
    <select id="selectSatisfactionSendCount" parameterType="com.smartor.domain.entity.SatisfactionStatQuery"
            resultType="java.util.Map">
        select libtemplateid as libtemplateid, count(distinct id) as cnt
        from service_subtask
        where del_flag = 0
        <if test="orgid != null and orgid != ''">
            and orgid = #{orgid}
        </if>
        <if test="campusid != null and campusid != ''">
            and campusid = #{campusid}
        </if>
        <if test="type != null and type != ''">and type = #{type}</if>
        <if test="startTime != null">
            and visit_time &gt;= #{startTime}
        </if>
        <if test="endTime != null">
            and visit_time &lt; date_add(#{endTime}, interval 1 day)
        </if>
        <if test="libTemplateIds != null and libTemplateIds.size() > 0">
            and libtemplateid IN
            <foreach collection="libTemplateIds" item="libTemplateId" open="(" separator="," close=")">
                #{libTemplateId}
            </foreach>
        </if>
        <if test="(hospitaldistrictcodes != null and hospitaldistrictcodes.size() > 0) or (deptcodes != null and deptcodes.size() > 0)">
            AND (
            <if test="hospitaldistrictcodes != null and hospitaldistrictcodes.size() > 0">
                leavehospitaldistrictcode IN
                <foreach collection="hospitaldistrictcodes" item="hospitaldistrictcode" open="("
                         separator=","
                         close=")">
                    #{hospitaldistrictcode}
                </foreach>
            </if>
            <if test="hospitaldistrictcodes != null and hospitaldistrictcodes.size() > 0 and deptcodes != null and deptcodes.size() > 0">
                OR
            </if>
            <if test="deptcodes != null and deptcodes.size() > 0">
                deptcode IN
                <foreach collection="deptcodes" item="deptcode" open="(" separator="," close=")">
                    #{deptcode}
                </foreach>
            </if>
            )
        </if>
        group by libtemplateid
    </select>
    <!--
        按病区或科室分组统计 continue_flag 数量
        传了 leavehospitaldistrictcodes → 按病区维度
        传了 deptcodes              → 按科室维度
@@ -3007,6 +3118,117 @@
          and sendstate in (1, 2, 3)
    </select>
    <select id="mydTrend" resultType="com.smartor.domain.DTO.MydTrendReqDTO">
        SELECT CASE #{type}
                   WHEN 'year' THEN DATE_FORMAT(d.create_time, '%Y')
                   WHEN 'quarter' THEN CONCAT(YEAR(d.create_time), '-Q', QUARTER(d.create_time))
                   WHEN 'month' THEN DATE_FORMAT(d.create_time, '%Y-%m')
                   WHEN 'week' THEN FLOOR(DATEDIFF(d.create_time, #{startFinishTime}) / 7) + 1
                   ELSE DATE_FORMAT(d.create_time, '%Y-%m-%d')
                   END                AS period,
               ROUND(avg(d.score), 2) AS totalScore
        FROM service_subtask_detail d
                 JOIN service_task t ON d.taskid = t.taskid
        WHERE t.service_type IN (14)
          AND t.del_flag = 0
          AND d.del_flag = 0
          AND d.create_time >= #{startFinishTime}
          AND d.create_time &lt; DATE_ADD(#{endFinishTime}, INTERVAL 1 DAY)
        GROUP BY period
        ORDER BY period;
    </select>
    <select id="sltdMydTotalByDimension" resultType="com.smartor.domain.DTO.MydTrendReqDTO">
        SELECT
        p.period,
        dim.dimension,
        IFNULL(ROUND(SUM(d.score), 2), 0) AS totalScore
        FROM (
        <!-- 生成查询范围内实际出现过的时间段 -->
        SELECT DISTINCT
        CASE #{type}
        WHEN 'year' THEN DATE_FORMAT(d.create_time, '%Y')
        WHEN 'quarter' THEN CONCAT(YEAR(d.create_time), '-Q', QUARTER(d.create_time))
        WHEN 'month' THEN DATE_FORMAT(d.create_time, '%Y-%m')
        WHEN 'week' THEN FLOOR(DATEDIFF(d.create_time, #{startFinishTime}) / 7) + 1
        ELSE DATE_FORMAT(d.create_time, '%Y-%m-%d')
        END AS period
        FROM service_subtask_detail d
        WHERE d.del_flag = 0
        AND d.create_time >= #{startFinishTime}
        AND d.create_time &lt; #{endFinishTime}
        ) p
        CROSS JOIN (
        <!-- 所有需要展示的 dimension -->
        SELECT DISTINCT dimension
        FROM service_subtask_detail
        WHERE del_flag = 0
        AND dimension IS NOT NULL
        ) dim
        LEFT JOIN service_task t
        ON t.service_type IN (6, 14)
        AND t.del_flag = 0
        LEFT JOIN service_subtask_detail d
        ON d.taskid = t.taskid
        AND d.del_flag = 0
        AND d.dimension = dim.dimension
        AND CASE #{type}
        WHEN 'year' THEN DATE_FORMAT(d.create_time, '%Y')
        WHEN 'quarter' THEN CONCAT(YEAR(d.create_time), '-Q', QUARTER(d.create_time))
        WHEN 'month' THEN DATE_FORMAT(d.create_time, '%Y-%m')
        WHEN 'week' THEN FLOOR(DATEDIFF(d.create_time, #{startFinishTime}) / 7) + 1
        ELSE DATE_FORMAT(d.create_time, '%Y-%m-%d')
        END = p.period
        AND d.create_time >= #{startFinishTime}
        AND d.create_time &lt; #{endFinishTime}
        GROUP BY p.period, dim.dimension
        ORDER BY p.period, dim.dimension
    </select>
    <select id="sltdMydTotalByYQ" resultType="com.smartor.domain.DTO.MydTrendReqDTO">
        SELECT p.period,
               c.campusid,
               IFNULL(ROUND(AVG(d.score), 2), 0) AS totalScore
        FROM (
                 -- 生成查询范围内实际出现过的时间段
                 SELECT DISTINCT CASE #{type}
                                     WHEN 'year' THEN DATE_FORMAT(d.create_time, '%Y')
                                     WHEN 'quarter' THEN CONCAT(YEAR(d.create_time), '-Q', QUARTER(d.create_time))
                                     WHEN 'month' THEN DATE_FORMAT(d.create_time, '%Y-%m')
                                     WHEN 'week' THEN FLOOR(DATEDIFF(d.create_time, #{startFinishTime}) / 7) + 1
                                     ELSE DATE_FORMAT(d.create_time, '%Y-%m-%d')
                                     END AS period
                 FROM service_subtask_detail d
                 WHERE d.del_flag = 0
                   AND d.create_time >= #{startFinishTime}
                   AND d.create_time &lt; #{endFinishTime}) p
                 CROSS JOIN (
            -- 所有需要展示的院区
            SELECT DISTINCT campusid
            FROM service_task
            WHERE service_type IN (6, 14)
              AND del_flag = 0
              AND campusid IS NOT NULL) c
                 LEFT JOIN service_task t
                           ON t.campusid = c.campusid
                               AND t.service_type IN (6, 14)
                               AND t.del_flag = 0
                 LEFT JOIN service_subtask_detail d
                           ON d.taskid = t.taskid
                               AND d.del_flag = 0
                               AND CASE #{type}
                                       WHEN 'year' THEN DATE_FORMAT(d.create_time, '%Y')
                                       WHEN 'quarter' THEN CONCAT(YEAR(d.create_time), '-Q', QUARTER(d.create_time))
                                       WHEN 'month' THEN DATE_FORMAT(d.create_time, '%Y-%m')
                                       WHEN 'week' THEN FLOOR(DATEDIFF(d.create_time, #{startFinishTime}) / 7) + 1
                                       ELSE DATE_FORMAT(d.create_time, '%Y-%m-%d')
                                       END = p.period
                               AND d.create_time >= #{startFinishTime}
                               AND d.create_time &lt; #{endFinishTime}
        GROUP BY p.period, c.campusid
        ORDER BY p.period, c.campusid;
    </select>
    <!-- 任务组(appltype=5)滚动调度用:拉本轮全量 subtask,作为下一轮的克隆模板 -->
    <select id="selectByScheduleIdAndLoop" resultMap="ServiceSubtaskResult">
        <include refid="selectServiceSubtaskVo"/>
@@ -3129,6 +3351,13 @@
                #{taskId}
            </foreach>
        </if>
        <!-- 传了 subtaskIds 只统计这批子任务(本次导出的患者) -->
        <if test="subtaskIds != null and subtaskIds.size() > 0">
            AND s.id IN
            <foreach collection="subtaskIds" item="subtaskId" open="(" separator="," close=")">
                #{subtaskId}
            </foreach>
        </if>
        <if test="valueType != null">AND d.value_type = #{valueType}</if>
        <if test="scoreStart != null">AND d.score &gt;= #{scoreStart}</if>
        <if test="scoreEnd != null">AND d.score &lt;= #{scoreEnd}</if>
@@ -3137,6 +3366,19 @@
        </if>
        and s.sendstate=6
        and d.del_flag=0
        <!-- 分母必须和明细用同一套时间条件,否则选了时间范围后比例会按全量历史被稀释 -->
        <if test="startOutHospTime != null">
            AND date_format(s.endtime,'%y%m%d') &gt;= date_format(#{startOutHospTime},'%y%m%d')
        </if>
        <if test="endOutHospTime != null">
            AND date_format(s.endtime,'%y%m%d') &lt;= date_format(#{endOutHospTime},'%y%m%d')
        </if>
        <if test="startFinishTime != null">
            AND date_format(s.finishtime,'%y%m%d') &gt;= date_format(#{startFinishTime},'%y%m%d')
        </if>
        <if test="endFinishTime != null">
            AND date_format(s.finishtime,'%y%m%d') &lt;= date_format(#{endFinishTime},'%y%m%d')
        </if>
        GROUP BY
        t.taskid,
        d.questiontext
@@ -3146,6 +3388,12 @@
            AND t.taskid IN
            <foreach collection="taskIds" item="taskId" open="(" separator="," close=")">
                #{taskId}
            </foreach>
        </if>
        <if test="subtaskIds != null and subtaskIds.size() > 0">
            AND s.id IN
            <foreach collection="subtaskIds" item="subtaskId" open="(" separator="," close=")">
                #{subtaskId}
            </foreach>
        </if>
        <if test="valueType != null">AND d.value_type = #{valueType}</if>
@@ -3161,6 +3409,12 @@
        </if>
        <if test="endOutHospTime != null">
            AND date_format(s.endtime,'%y%m%d') &lt;= date_format(#{endOutHospTime},'%y%m%d')
        </if>
        <if test="startFinishTime != null">
            AND date_format(s.finishtime,'%y%m%d') &gt;= date_format(#{startFinishTime},'%y%m%d')
        </if>
        <if test="endFinishTime != null">
            AND date_format(s.finishtime,'%y%m%d') &lt;= date_format(#{endFinishTime},'%y%m%d')
        </if>
        GROUP BY
        t.taskid,
@@ -3174,18 +3428,77 @@
        t.taskid
    </select>
    <!--
      导出「问题详情占比」专用统计(只统计本次导出的这批患者)
      数量 = 选择该选项的患者数(COUNT(DISTINCT s.id),并用 MAX(id) 保证同一患者同一题只算最新一条)
      分数 = 该选项明细的 score 之和
      比例 = 数量 / 该题作答患者数,在 Java 侧统一计算,因此「单题各选项数量之和 = 该题作答患者数」
    -->
    <select id="statMydQuestionOptionForExport" resultType="com.smartor.domain.DTO.ServiceSubtaskOptionStatDTO">
        SELECT
        t.taskid AS taskid,
        t.task_name AS taskName,
        d.questiontext AS questiontext,
        d.asrtext AS optionresult,
        d.targetvalue AS targetvalue,
        d.value_type AS valueType,
        COUNT(DISTINCT s.id) AS patientCount,
        SUM(COALESCE(CAST(d.score AS DECIMAL(18,2)), 0)) AS scoreSum
        FROM
        service_task t
        INNER JOIN service_subtask s ON s.taskid = t.taskid
        INNER JOIN service_subtask_detail d ON d.sub_id = s.id
        WHERE 1=1
        AND s.del_flag = '0'
        AND d.del_flag = 0
        <if test="subtaskIds != null and subtaskIds.size() > 0">
            AND s.id IN
            <foreach collection="subtaskIds" item="subtaskId" open="(" separator="," close=")">
                #{subtaskId}
            </foreach>
        </if>
        <!-- 不传子任务时直接查不到数据,避免误统计全量 -->
        <if test="subtaskIds == null or subtaskIds.size() == 0">
            AND 1 = 0
        </if>
        <if test="questiontext != null and questiontext != ''">
            AND d.questiontext LIKE CONCAT('%', #{questiontext}, '%')
        </if>
        <!-- 同一患者同一题目只保留最新一条,重答不重复计入数量 -->
        AND d.id = (
        SELECT MAX(d2.id)
        FROM service_subtask_detail d2
        WHERE d2.sub_id = d.sub_id
        AND d2.questiontext &lt;=&gt; d.questiontext
        AND d2.del_flag = '0'
        )
        GROUP BY
        t.taskid,
        t.task_name,
        d.questiontext,
        d.asrtext,
        d.targetvalue,
        d.value_type
        <!-- 按任务名称分组展示:先任务名称、再任务编号,同一任务的题目/选项连续排列 -->
        ORDER BY
        t.task_name,
        t.taskid,
        d.questiontext
    </select>
    <!-- 新方法:根据子任务 ID 统计问题选项百分比 (用于满意度统计 serviceType=6) -->
    <select id="statQuestionOptionBySubtaskIds" resultType="com.smartor.domain.ServiceSubtaskDetailRatioExport">
        SELECT
        d.questiontext AS questiontext,
        t.taskid AS taskid,
        t.task_name AS taskName,
        <!-- 明细已按「题目 + 选项」汇总,任务/科室/病区只是附带信息,必须用聚合函数取值(MySQL ONLY_FULL_GROUP_BY) -->
        MAX(t.taskid) AS taskid,
        MAX(t.task_name) AS taskName,
        d.asrtext AS optionresult,
        d.score AS score,
        s.deptcode AS deptcode,
        s.deptname AS deptname,
        s.leavehospitaldistrictcode AS leavehospitaldistrictcode,
        s.leavehospitaldistrictname AS leavehospitaldistrictname,
        MAX(s.deptcode) AS deptcode,
        MAX(s.deptname) AS deptname,
        MAX(s.leavehospitaldistrictcode) AS leavehospitaldistrictcode,
        MAX(s.leavehospitaldistrictname) AS leavehospitaldistrictname,
        d.targetvalue AS targetvalue,
        d.value_type AS valueType,
        COUNT(*) AS count,
@@ -3194,14 +3507,36 @@
        service_task t
        INNER JOIN service_subtask s ON s.taskid = t.taskid
        INNER JOIN service_subtask_detail d ON d.sub_id = s.id
        <!-- 去重:同一子任务同一题目只保留最新一条,避免重答/重复回调把同一道题重复计入项数(与 mzMydScoreRank 口径一致) -->
        AND d.id = (
        SELECT MAX(d2.id)
        FROM service_subtask_detail d2
        WHERE d2.sub_id = d.sub_id
        AND d2.questiontext &lt;=&gt; d.questiontext
        AND d2.del_flag = '0'
        )
        INNER JOIN (
        <!-- 分母 = 该题各选项项数之和(未作答的空 asrtext 不进分母),这样一道题各选项比例合计才是 100% -->
        SELECT
        x.questiontext AS questiontext,
        SUM(x.option_count) AS total_count
        FROM (
        SELECT
        d.questiontext,
        COUNT(*) AS total_count
        d.asrtext,
        COUNT(*) AS option_count
        FROM
        service_task t
        INNER JOIN service_subtask s ON s.taskid = t.taskid
        INNER JOIN service_subtask_detail d ON d.sub_id = s.id
        <!-- 去重:分母(该题有效答卷总数)与明细口径保持一致,保证各选项比例合计 100% -->
        AND d.id = (
        SELECT MAX(d4.id)
        FROM service_subtask_detail d4
        WHERE d4.sub_id = d.sub_id
        AND d4.questiontext &lt;=&gt; d.questiontext
        AND d4.del_flag = '0'
        )
        WHERE 1=1
        <if test="subtaskIds != null and subtaskIds.size() > 0">
            AND s.id IN
@@ -3210,16 +3545,41 @@
            </foreach>
        </if>
        <if test="valueType != null">AND d.value_type &gt;= #{valueType}</if>
        <if test="scoreStart != null">AND d.score = #{scoreStart}</if>
        <if test="scoreStart != null">AND d.score &gt;= #{scoreStart}</if>
        <if test="scoreEnd != null">AND d.score &lt;= #{scoreEnd}</if>
        <if test="questiontext != null  and questiontext != ''">and d.questiontext like concat('%', #{questiontext},
            '%')
        </if>
        AND s.sendstate = 6
        AND d.del_flag=0
        GROUP BY d.questiontext
        <!-- 分母与明细用同一套时间条件,否则选了日期区间后各选项比例合计不到 100% -->
        <if test="startOutHospTime != null">
            AND date_format(s.endtime,'%y%m%d') &gt;= date_format(#{startOutHospTime},'%y%m%d')
        </if>
        <if test="endOutHospTime != null">
            AND date_format(s.endtime,'%y%m%d') &lt;= date_format(#{endOutHospTime},'%y%m%d')
        </if>
        <if test="startFinishTime != null">
            AND date_format(s.finishtime,'%y%m%d') &gt;= date_format(#{startFinishTime},'%y%m%d')
        </if>
        <if test="endFinishTime != null">
            AND date_format(s.finishtime,'%y%m%d') &lt;= date_format(#{endFinishTime},'%y%m%d')
        </if>
        <!-- 未作答(asrtext 为空)的明细不计入分母 -->
        AND d.asrtext IS NOT NULL AND TRIM(d.asrtext) &lt;&gt; ''
        GROUP BY d.questiontext, d.asrtext
        ) x
        GROUP BY x.questiontext
        ) t2 ON t2.questiontext = d.questiontext
        WHERE 1=1
        <!-- 去重:明细行同一子任务同一题目只保留最新一条,重答不重复计入项数 -->
        AND d.id = (
        SELECT MAX(d3.id)
        FROM service_subtask_detail d3
        WHERE d3.sub_id = d.sub_id
        AND d3.questiontext &lt;=&gt; d.questiontext
        AND d3.del_flag = '0'
        )
        <if test="subtaskIds != null and subtaskIds.size() > 0">
            AND s.id IN
            <foreach collection="subtaskIds" item="subtaskId" open="(" separator="," close=")">
@@ -3228,6 +3588,8 @@
        </if>
        AND s.sendstate = 6
        AND d.del_flag=0
        <!-- 未作答(asrtext 为空)不算任何选项,明细里直接排除,避免出现空选项行 -->
        AND d.asrtext IS NOT NULL AND TRIM(d.asrtext) &lt;&gt; ''
        <if test="valueType != null">AND d.value_type &gt;= #{valueType}</if>
        <if test="scoreStart != null">AND d.score &gt;= #{scoreStart}</if>
        <if test="scoreEnd != null">AND d.score &lt;= #{scoreEnd}</if>
@@ -3240,63 +3602,82 @@
        <if test="endOutHospTime != null">
            AND date_format(s.endtime,'%y%m%d') &lt;= date_format(#{endOutHospTime},'%y%m%d')
        </if>
        <if test="startFinishTime != null">
            AND date_format(s.finishtime,'%y%m%d') &gt;= date_format(#{startFinishTime},'%y%m%d')
        </if>
        <if test="endFinishTime != null">
            AND date_format(s.finishtime,'%y%m%d') &lt;= date_format(#{endFinishTime},'%y%m%d')
        </if>
        <!-- 只按「题目 + 选项」汇总:原来 GROUP BY 带上 taskid/科室/病区,同一个选项被拆成多行、每行数量都是 1,
             前端/导出拿到的「数量」就永远做不了累加 -->
        GROUP BY
        t.taskid,
        d.questiontext,
        d.asrtext,
        d.score,
        d.targetvalue,
        d.asrtext
        d.value_type
        ORDER BY
        d.questiontext,
        t.taskid
        MIN(t.taskid)
    </select>
    <select id="statQuestionOptionByKsOrBq" resultType="com.smartor.domain.DTO.QuestionCountKsOrBqDTO"
            parameterType="com.smartor.domain.VO.QuestionCountKsOrBqVO">
        SELECT questiontext,
        d.taskid,
        s.deptname,
        d.create_time as createTime,
        s.leavehospitaldistrictname,
        d.targetvalue,
        d.asrtext,
        COUNT(d.asrtext) AS answercount,
        d.score AS singleScore
        FROM service_subtask_detail d,
        service_task s
        WHERE d.taskid = s.taskid
        AND deptname IS NOT NULL
        AND leavehospitaldistrictname IS NOT NULL
        SELECT
        s.deptname AS deptname,
        s.leavehospitaldistrictname AS leavehospitaldistrictname,
        d.questiontext AS questionText,
        d.targetvalue AS targetvalue, -- 所有选项拼接
        d.asrtext AS asrtext, -- 用户实际选中的选项
        d.score AS singleScore -- 单条记录的分数
        FROM service_subtask_detail d
        JOIN service_subtask s ON d.sub_id = s.id
        WHERE s.del_flag = 0
        AND d.del_flag = 0
        AND s.service_type IN ('6', '14')
        <if test="taskTemplateid != null">
            AND s.templateid = #{taskTemplateid}
        </if>
        <if test="endFinishTime != null">
            AND d.create_time &lt; #{endFinishTime}
        </if>
        <if test="startFinishTime != null">
            AND  d.create_time &gt;= #{startFinishTime}
            AND d.create_time &gt;= #{startFinishTime}
        </if>
        <if test="leavehospitaldistrictcodes != null and leavehospitaldistrictcodes.size() > 0">
            AND leavehospitaldistrictcode IN
            <foreach collection="leavehospitaldistrictcodes" item="leavehospitaldistrictcode" open="("
                     separator=","
                     close=")">
                #{leavehospitaldistrictcode}
            </foreach>
        <if test="deptOrDistrict ==1">
            <if test="leavehospitaldistrictcodes != null and leavehospitaldistrictcodes.size() > 0">
                AND leavehospitaldistrictcode IN
                <foreach collection="leavehospitaldistrictcodes" item="leavehospitaldistrictcode" open="("
                         separator=","
                         close=")">
                    #{leavehospitaldistrictcode}
                </foreach>
            </if>
            <if test=" leaveldeptcodes != null and leaveldeptcodes.size()>0">
                AND deptcode IN
                <foreach collection="leaveldeptcodes" item="leaveldeptcode" open="(" separator=","
                         close=")">
                    #{leaveldeptcode}
                </foreach>
            </if>
        </if>
        <if test=" leaveldeptcodes != null and leaveldeptcodes.size()>0">
            AND deptcode IN
            <foreach collection="leaveldeptcodes" item="leaveldeptcode" open="(" separator=","
                     close=")">
                #{leaveldeptcode}
            </foreach>
        <if test="deptOrDistrict ==2">
            <if test="leavehospitaldistrictcodes != null and leavehospitaldistrictcodes.size() > 0">
                AND leavehospitaldistrictcode IN
                <foreach collection="leavehospitaldistrictcodes" item="leavehospitaldistrictcode" open="("
                         separator=","
                         close=")">
                    #{leavehospitaldistrictcode}
                </foreach>
            </if>
            <if test=" leaveldeptcodes != null and leaveldeptcodes.size()>0">
                OR deptcode IN
                <foreach collection="leaveldeptcodes" item="leaveldeptcode" open="(" separator=","
                         close=")">
                    #{leaveldeptcode}
                </foreach>
            </if>
        </if>
        GROUP BY d.questiontext,
        d.taskid,
        d.score,
        s.deptname,
        d.targetvalue,
        d.create_time,
        s.leavehospitaldistrictname,
        d.asrtext
        ORDER BY d.taskid
    </select>
    <select id="selectServiceSubtaskListDeadLine" parameterType="com.smartor.domain.entity.ServiceSubtaskEntity"
@@ -3308,5 +3689,96 @@
        and endtime
    </select>
    <!-- 门诊满意度综合得分排名(按科室或病区) -->
    <!-- 综合满意度 = Σ(评分明细权重) / 评分明细条数 × 100%,权重只取 1、0.8、0.6、0.4、0.2 五档 -->
    <select id="selectMzMydScoreRank" parameterType="com.smartor.domain.VO.MzMydScoreRankVO"
            resultType="com.smartor.domain.DTO.MzMydScoreRankDTO">
        SELECT
        t.code                   AS code,
        t.name                   AS name,
        ROUND(AVG(t.w) * 100, 2) AS score,
        COUNT(*)                 AS itemCount
        FROM (
        SELECT
        <choose>
            <when test='dimensionType == "2"'>
                s.leavehospitaldistrictcode AS code,
                s.leavehospitaldistrictname AS name,
            </when>
            <otherwise>
                s.deptcode AS code,
                s.deptname AS name,
            </otherwise>
        </choose>
        CAST(d.score AS DECIMAL(10,4)) AS w
        FROM service_subtask s
        INNER JOIN service_subtask_detail d ON d.sub_id = s.id
        WHERE s.del_flag = '0'
        AND d.del_flag = '0'
        AND s.sendstate = 6
        <if test="serviceTypes != null and serviceTypes.size() > 0">
            AND service_type IN
            <foreach collection="serviceTypes" item="serviceType" open="(" separator=","
                     close=")">
                #{serviceType}
            </foreach>
        </if>
<!--        &lt;!&ndash; 只取公式里的五档权重,空值/0/未答等既不进分子也不进分母 &ndash;&gt;-->
<!--        AND CAST(d.score AS DECIMAL(10,4)) IN (1, 0.8, 0.6, 0.4, 0.2)-->
        <!-- 同一子任务同一题目只保留最新一条,避免重答被重复计入项数 -->
        AND d.id = (
        SELECT MAX(d2.id)
        FROM service_subtask_detail d2
        WHERE d2.sub_id = d.sub_id
        AND d2.questiontext &lt;=&gt; d.questiontext
        AND d2.del_flag = '0'
        )
        <if test="orgid != null and orgid != ''">
            AND s.orgid = #{orgid}
        </if>
        <if test="campusid != null and campusid != ''">
            AND s.campusid = #{campusid}
        </if>
        <if test="startFinishTime != null">
            AND s.finishtime &gt;= #{startFinishTime}
        </if>
        <if test="endFinishTime != null">
            AND s.finishtime &lt; DATE_ADD(#{endFinishTime}, INTERVAL 1 DAY)
        </if>
        <if test="questionText != null  and questionText != ''">and d.questiontext like concat('%', #{questionText},
            '%')
        </if>
        <choose>
            <when test='dimensionType == "2"'>
                AND s.leavehospitaldistrictcode IS NOT NULL AND s.leavehospitaldistrictcode &lt;&gt; ''
                AND s.leavehospitaldistrictname IS NOT NULL AND s.leavehospitaldistrictname &lt;&gt; ''
                <if test="districtCodes != null and districtCodes.size() > 0">
                    AND s.leavehospitaldistrictcode IN
                    <foreach collection="districtCodes" item="districtCode" open="(" separator="," close=")">
                        #{districtCode}
                    </foreach>
                </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; ''
                <if test="deptCodes != null and deptCodes.size() > 0">
                    AND s.deptcode IN
                    <foreach collection="deptCodes" item="deptCode" open="(" separator="," close=")">
                        #{deptCode}
                    </foreach>
                </if>
            </otherwise>
        </choose>
        ) t
        GROUP BY t.code, t.name
        ORDER BY AVG(t.w) DESC, COUNT(*) DESC
        <if test="limit != null and limit > 0">
            LIMIT #{limit}
        </if>
    </select>
</mapper>