liusheng
2026-10-12 b25c3b5188e402695f3792ad7da53e452682cd80
满意度统计BUG修改
已修改6个文件
237 ■■■■■ 文件已修改
ruoyi-admin/src/main/resources/application.yml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-satisfaction/src/main/java/com/ruoyi/satisfaction/domain/entity/PatSatisfactionDetailEntity.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-satisfaction/src/main/java/com/ruoyi/satisfaction/mapper/SatisfactionMapper.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-satisfaction/src/main/java/com/ruoyi/satisfaction/service/impl/PatSatisfactionServiceImpl.java 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-satisfaction/src/main/resources/mapper/satisfaction/SatisfactionMapper.xml 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-satisfaction/src/main/resources/mapper/satisfaction/SatisfactionScriptMapper.xml 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/application.yml
@@ -75,7 +75,7 @@
    # 国际化资源文件路径
    basename: i18n/messages
  profiles:
    active: nhfy
    active: sltd
  # 文件上传
  servlet:
ruoyi-satisfaction/src/main/java/com/ruoyi/satisfaction/domain/entity/PatSatisfactionDetailEntity.java
@@ -67,6 +67,14 @@
    private Double answerRate;
    /**
     * 维度(ivr_liba_template_script.dimension / svy_lib_template_script.dimension)
     * <p>
     * 供「维度题目明细」页面展示维度列使用;非维度口径的接口也会带上该值。
     */
    @ApiModelProperty(value = "维度")
    private String dimension;
    /**
     * 各选项(matchedtext)数量及占比
     * key=matchedtext,value={count: 数量, ratio: 占比}
     */
ruoyi-satisfaction/src/main/java/com/ruoyi/satisfaction/mapper/SatisfactionMapper.java
@@ -85,6 +85,25 @@
     */
    List<Map<String, Object>> selectSatisfactionFillPersonCount(SatisfactionStatQuery query);
    /**
     * 【按 模板 + 问题】统计答题人数({@code count(distinct ssd.sub_id)})。
     * <p>
     * <b>为什么单独加这一条</b>:{@code /statistics} 的「答题人数 / 未答题人数」必须与
     * 「发送人数」({@link #selectSatisfactionSendCount},也是人数)同口径。
     * 原实现误用 {@link #selectSatisfactionFillStatByMatchedtext} 的 {@code count(*)}(明细<b>行数</b>),
     * 而一条 {@code (sub_id, questiontext)} 可能有多行明细(多选项 / 重复明细),
     * 导致「未答题人数 = 发送人数 − 明细行数」出现负数(实测 -120)。
     * <p>
     * ⚠️ 平均分 / 最高分 / 最低分 / 各选项占比仍使用明细行数(那些口径本来就按行计),未改动。
     * <p>
     * 除聚合键与 {@code distinct} 外,from/where 与 {@link #selectSatisfactionFillStatByMatchedtext}
     * 完全一致,保证两者统计的是同一批数据。
     *
     * @param query 聚合查询条件
     * @return 每行含 libtemplateid、questiontext、cnt(人数)
     */
    List<Map<String, Object>> selectSatisfactionFillPersonCountByQuestion(SatisfactionStatQuery query);
    // ================================================================ 省立同德:趋势与维度
    /**
ruoyi-satisfaction/src/main/java/com/ruoyi/satisfaction/service/impl/PatSatisfactionServiceImpl.java
@@ -216,7 +216,7 @@
            //3 一次统计全部问题的发送量/填报量/选项分值(原来是每个问题各查 3 次)
            List<SatScriptRef> scriptRefs = new ArrayList<>();
            for (SatisfactionIvrScriptRow ivrLibaTemplateScript : ivrLibaTemplateScripts) {
                scriptRefs.add(new SatScriptRef(ivrLibaTemplateScript.getTemplateid(), ivrLibaTemplateScript.getScriptid(), ivrLibaTemplateScript.getScriptContent()));
                scriptRefs.add(new SatScriptRef(ivrLibaTemplateScript.getTemplateid(), ivrLibaTemplateScript.getScriptid(), ivrLibaTemplateScript.getScriptContent(), ivrLibaTemplateScript.getDimension()));
            }
            buildSatisfactionDetailEntities(patSatisfactionReqVO, "1", true, scriptRefs, patSatisfactionDetailEntities, personTotals);
        }
@@ -231,7 +231,7 @@
            List<SatScriptRef> scriptRefs = new ArrayList<>();
            for (SatisfactionSvyScriptRow svyLibTemplateScript : svyLibTemplateScripts) {
                // 问卷的选项表按 svy_lib_template_script 的主键关联
                scriptRefs.add(new SatScriptRef(svyLibTemplateScript.getSvyid(), svyLibTemplateScript.getId(), svyLibTemplateScript.getScriptContent()));
                scriptRefs.add(new SatScriptRef(svyLibTemplateScript.getSvyid(), svyLibTemplateScript.getId(), svyLibTemplateScript.getScriptContent(), svyLibTemplateScript.getDimension()));
            }
            buildSatisfactionDetailEntities(patSatisfactionReqVO, "2", false, scriptRefs, patSatisfactionDetailEntities, personTotals);
        }
@@ -270,7 +270,7 @@
                //3 一次统计全部问题的发送量/填报量/选项分值(原来是每个问题各查 3 次)
                List<SatScriptRef> scriptRefs = new ArrayList<>();
                for (SatisfactionIvrScriptRow ivrLibaTemplateScript : ivrLibaTemplateScripts) {
                    scriptRefs.add(new SatScriptRef(ivrLibaTemplateScript.getTemplateid(), ivrLibaTemplateScript.getScriptid(), ivrLibaTemplateScript.getScriptContent()));
                    scriptRefs.add(new SatScriptRef(ivrLibaTemplateScript.getTemplateid(), ivrLibaTemplateScript.getScriptid(), ivrLibaTemplateScript.getScriptContent(), ivrLibaTemplateScript.getDimension()));
                }
                buildSatisfactionDetailEntities(patSatisfactionReqVO, "1", true, scriptRefs, patSatisfactionDetailEntities, personTotals);
            }
@@ -284,7 +284,7 @@
                List<SatScriptRef> scriptRefs = new ArrayList<>();
                for (SatisfactionSvyScriptRow svyLibTemplateScript : svyLibTemplateScripts) {
                    // 问卷的选项表按 svy_lib_template_script 的主键关联
                    scriptRefs.add(new SatScriptRef(svyLibTemplateScript.getSvyid(), svyLibTemplateScript.getId(), svyLibTemplateScript.getScriptContent()));
                    scriptRefs.add(new SatScriptRef(svyLibTemplateScript.getSvyid(), svyLibTemplateScript.getId(), svyLibTemplateScript.getScriptContent(), svyLibTemplateScript.getDimension()));
                }
                buildSatisfactionDetailEntities(patSatisfactionReqVO, "2", false, scriptRefs, patSatisfactionDetailEntities, personTotals);
            }
@@ -763,31 +763,66 @@
        SatisfactionStatQuery statQuery = buildSatisfactionStatQuery(reqVO, type, templateIds);
        Map<Long, Long> sendCountMap = mapSatisfactionSendCount(statQuery);
        SatisfactionFillStatByMatchedtext fillStat = querySatisfactionFillStatByMatchedtext(statQuery);
        //答题【人数】:按 模板+问题 的 distinct sub_id(与「发送人数」同口径)
        Map<String, Long> answerPersonMap = mapSatisfactionFillPersonCountByQuestion(statQuery);
        Map<Long, List<String>> optionScores = loadTargetOptionScores(voice, scriptRefs);
        for (SatScriptRef scriptRef : scriptRefs) {
            Long templateId = scriptRef.getTemplateId();
            //发送总量(原来是把全部 sub_id 查回 Java 再取 size)
            long sendCount = templateId == null ? 0L : sendCountMap.getOrDefault(templateId, 0L);
            //填报数量(原来是把明细行全部查回 Java 再统计)
            //填报【行数】:用于平均分 / 最高分 / 最低分 / 各选项占比(这些口径本来就按明细行计)
            List<MatchedtextCount> fillRows = fillStat.get(templateId, scriptRef.getScriptContent());
            long fillCount = countFillRowsByMatchedtext(fillRows);
            //答题【人数】:用于 答题人数 / 未答题人数 / 答题率
            long answerPersonCount = answerPersonMap.getOrDefault(
                    statKey(templateId, scriptRef.getScriptContent()), 0L);
            PatSatisfactionDetailEntity detail = new PatSatisfactionDetailEntity();
            detail.setScriptContent(scriptRef.getScriptContent());
            detail.setAnswerPerson((int) fillCount);
            detail.setNoAnswerPerson(fillCount > 0L && sendCount > 0L ? (int) (sendCount - fillCount) : sendCount > 0L ? (int) sendCount : 0);
            //维度:原本查询里已取到,但构造 SatScriptRef 时被丢掉,导致前端「维度」列一直为空
            detail.setDimension(scriptRef.getDimension());
            // ⚠️ 人数口径修正:原来这里用的是 fillCount(明细行数),
            //    而发送人数是 count(distinct id)(人数),两者相减会出现负数(实测 -120 = 15 − 135)。
            //    一条 (sub_id, questiontext) 可能有多行明细,所以答题人数必须取 distinct sub_id。
            detail.setAnswerPerson((int) answerPersonCount);
            detail.setNoAnswerPerson(answerPersonCount > 0L && sendCount > 0L ? (int) (sendCount - answerPersonCount)
                    : sendCount > 0L ? (int) sendCount : 0);
            detail.setAverageScore(String.valueOf(averageScoreByMatchedtext(fillRows)));
            List<String> scores = optionScores.get(scriptRef.getOptionScriptId());
            detail.setMaxScore(String.valueOf(maxScoreOf(scores)));
            detail.setMinScore(String.valueOf(minScoreOf(scores)));
            detail.setAnswerRate(sendCount == 0L ? 0 : fillCount == 0L ? 0 : fillCount * 1.0 / sendCount);
            detail.setAnswerRate(sendCount == 0L ? 0 : answerPersonCount == 0L ? 0 : answerPersonCount * 1.0 / sendCount);
            detail.setMatchedtextStats(buildMatchedtextStats(fillRows, fillCount));
            out.add(detail);
            totals[0] += (int) fillCount;
            totals[0] += (int) answerPersonCount;
            totals[1] += (int) sendCount;
        }
    }
    /**
     * 一次查出「全部 模板+问题」的答题人数({@code count(distinct ssd.sub_id)})。
     * <p>
     * key 用与 {@link #statKey(Long, String)} 相同的规则构造,保证与调用方的查找一致
     * ({@code statKey} 已做 trim + 小写,等价于 MySQL 的忽略大小写与尾部空格比较)。
     */
    private Map<String, Long> mapSatisfactionFillPersonCountByQuestion(SatisfactionStatQuery statQuery) {
        Map<String, Long> answerPersonMap = new HashMap<>();
        if (statQuery == null || CollectionUtils.isEmpty(statQuery.getLibTemplateIds())) return answerPersonMap;
        List<Map<String, Object>> rows = satisfactionMapper.selectSatisfactionFillPersonCountByQuestion(statQuery);
        if (CollectionUtils.isEmpty(rows)) return answerPersonMap;
        for (Map<String, Object> row : rows) {
            Long templateId = toLong(row.get("libtemplateid"));
            if (templateId == null) continue;
            long cnt = toLong(row.get("cnt"), 0L);
            if (cnt <= 0L) continue;
            Object questiontext = row.get("questiontext");
            answerPersonMap.merge(
                    statKey(templateId, questiontext == null ? null : String.valueOf(questiontext)),
                    cnt, Long::sum);
        }
        return answerPersonMap;
    }
    /**
@@ -914,7 +949,7 @@
    /**
     * 获取平均分(聚合版:分值与条数)
     * <p>
     * 口径与 {@link #averageScore(List)} 一致:分子为可解析分值之和,分母为明细行数(含分值为空的行)。
     * 分子为可解析分值之和,分母为明细行数(含分值为空的行)。
     */
    private Double averageScoreByMatchedtext(List<MatchedtextCount> fillRows) {
        if (CollectionUtils.isEmpty(fillRows)) return 0.0;
@@ -932,7 +967,7 @@
    }
    /**
     * 获取最高分(聚合版,口径同 {@link #maxScore(List)}:空列表返回 0.0)
     * 获取最高分(聚合版,空列表返回 0.0)
     */
    private Double maxScoreOf(List<String> scores) {
        if (CollectionUtils.isEmpty(scores)) return 0.0;
@@ -941,7 +976,7 @@
    }
    /**
     * 获取最低分(聚合版,口径同 {@link #minScore(List)}:空列表返回 0.0)
     * 获取最低分(聚合版,口径同 {空列表返回 0.0)
     */
    private Double minScoreOf(List<String> scores) {
        if (CollectionUtils.isEmpty(scores)) return 0.0;
@@ -950,17 +985,19 @@
    }
    /**
     * 满意度统计用的问题引用:模板id、选项表关联的问题id(可能为 null)、问题内容
     * 满意度统计用的问题引用:模板id、选项表关联的问题id(可能为 null)、问题内容、维度
     */
    private static class SatScriptRef {
        private final Long templateId;
        private final Long optionScriptId;
        private final String scriptContent;
        private final String dimension;
        SatScriptRef(Long templateId, Long optionScriptId, String scriptContent) {
        SatScriptRef(Long templateId, Long optionScriptId, String scriptContent, String dimension) {
            this.templateId = templateId;
            this.optionScriptId = optionScriptId;
            this.scriptContent = scriptContent;
            this.dimension = dimension;
        }
        Long getTemplateId() {
@@ -973,6 +1010,10 @@
        String getScriptContent() {
            return scriptContent;
        }
        String getDimension() {
            return dimension;
        }
    }
@@ -1038,19 +1079,33 @@
    // 原实现直接 new smartor 的实体当入参;模块化后统一用 SatisfactionScriptQuery,
    // 这里把三处构造收敛为帮助方法,条件与原来逐字对应。
    /** 满意度分类查询(orgid + type=myd) */
    /**
     * 满意度分类查询(语音侧)。
     * <p>
     * ⚠️ <b>与原文一致:只传 orgid,<u>不传 type</u></b>。原实现是
     * <pre>
     * IvrLibaScriptAssort e = new IvrLibaScriptAssort();
     * e.setOrgid(reqVO.getOrgid());      // ← 只有 orgid
     * mapper.selectIvrLibaScriptAssortList(e);
     * </pre>
     * 分类的「是不是满意度(myd)」是在 <b>Java 侧</b>用
     * {@code item.getType().contains("myd")} 过滤的(见 {@link #statistics} /
     * {@link #satisfactionGraph})。
     * <p>
     * 如果在这里顺手加上 {@code type="myd"},SQL 就会多出一个
     * {@code type like '%myd%'} 条件 —— 而 MySQL 的 LIKE 默认<b>不区分大小写</b>,
     * 会比 Java 的 {@code contains("myd")} 多命中分类,导致统计结果出现差异。
     */
    private SatisfactionScriptQuery ivrCategoryQuery(PatSatisfactionReqVO reqVO) {
        SatisfactionScriptQuery q = new SatisfactionScriptQuery();
        q.setOrgid(reqVO.getOrgid());
        q.setType("myd");
        return q;
    }
    /** 满意度分类查询(问卷侧,条件同语音侧) */
    /** 满意度分类查询(问卷侧,同样只传 orgid,见 {@link #ivrCategoryQuery}) */
    private SatisfactionScriptQuery svyCategoryQuery(PatSatisfactionReqVO reqVO) {
        SatisfactionScriptQuery q = new SatisfactionScriptQuery();
        q.setOrgid(reqVO.getOrgid());
        q.setType("myd");
        return q;
    }
ruoyi-satisfaction/src/main/resources/mapper/satisfaction/SatisfactionMapper.xml
@@ -801,6 +801,72 @@
        </if>
        group by ss.libtemplateid
    </select>
    <!-- ================================================
         「按 模板 + 问题」的答题【人数】(distinct sub_id)
         为什么需要它:/statistics 的「答题人数 / 未答题人数」必须与「发送人数」同口径(都是【人】)。
         原实现把「答题人数」取成了 selectSatisfactionFillStatByMatchedtext 的 count(*)(【明细行数】),
         而同一条 (sub_id, questiontext) 可能对应多行明细(多选项 / 重复明细),
         于是「未答题人数 = 发送人数 − 明细行数」会算成负数(实测 -120 = 15 − 135)。
         这里补一条按问题聚合的 distinct 人数查询,专供「答题人数 / 未答题人数 / 答题率」使用;
         平均分 / 最高分 / 最低分 / 各选项占比仍按明细行数统计(那些口径本来就是按行计,未改动)。
         除聚合键与 distinct 之外,from/where 与 selectSatisfactionFillStatByMatchedtext 完全一致,
         保证两者统计的是同一批数据。
         ================================================ -->
    <select id="selectSatisfactionFillPersonCountByQuestion"
            parameterType="com.ruoyi.satisfaction.domain.entity.SatisfactionStatQuery"
            resultType="java.util.Map">
        select ss.libtemplateid as libtemplateid,
               ssd.questiontext as questiontext,
               count(distinct ssd.sub_id) as cnt
        from service_subtask_detail ssd
        join service_subtask ss on ss.id = ssd.sub_id
        where ssd.del_flag = 0
        and 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="type != null and type != ''">and ss.type = #{type}</if>
        <if test="startTime != null">
            and ss.visit_time &gt;= #{startTime}
        </if>
        <if test="endTime != null">
            and ss.visit_time &lt; date_add(#{endTime}, interval 1 day)
        </if>
        <if test="libTemplateIds != null and libTemplateIds.size() > 0">
            and ss.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">
                ss.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">
                ss.deptcode IN
                <foreach collection="deptcodes" item="deptcode" open="(" separator="," close=")">
                    #{deptcode}
                </foreach>
            </if>
            )
        </if>
        group by ss.libtemplateid, ssd.questiontext
    </select>
    <!-- ================================================ 重复明细清理(满意度统计前置)
         原属 ServiceSubtaskDetailMapper.xml,只有满意度统计调用(已核实),故一并搬入。
         注意 deleteRepeatDetailByIds 是【写操作】:按 id 逻辑删除重复明细(del_flag=1)。 -->
ruoyi-satisfaction/src/main/resources/mapper/satisfaction/SatisfactionScriptMapper.xml
@@ -16,7 +16,7 @@
      口径要点(必须与原文逐字一致):
      - queryIvrScriptByAssort / querySvyScriptByCategory:ids 为空时不拼 IN 条件;
      - selectIvrScriptByDimension:dimensionList 为空时拼 `dimension IS NOT NULL`;
      - selectSvyScriptByDimension:dimensionList 为空时【不拼】该条件(两侧不一致是原文行为);
      - selectSvyScriptByDimension:dimensionList 为空时【同样】拼该条件(原文写在 <where> 最后一行,极易看漏;早期迁移曾误判为不拼,已修正);
      - selectIvr/SvyTargetOptionScores:scriptIds 为 null 时不拼过滤,等于取全部分值。
    -->
@@ -45,7 +45,11 @@
    <!-- ================================================ 满意度分类(问卷侧)
         原:SvyLibScriptCategoryMapper.selectSvyLibScriptCategoryList
             (该 statement 实际定义在 SvyLibTopicCategoryMapper.xml,namespace 与文件名不一致)
         注意:问卷侧原文就是直接取【全部行】(不拼树、不筛顶层),与语音侧不同。 -->
         注意:问卷侧原文就是直接取【全部行】(不拼树、不筛顶层),与语音侧不同。
         ⚠️ orgid 的条件与原文逐字一致:原文问卷侧是 `<if test="orgid != null">`(**没有** != ''),
            语音侧才是 `orgid != null and orgid != ''`。两侧不一致是原文行为,必须保留:
            若给问卷侧也加上 `!= ''`,当入参 orgid 为空串时,原实现按 `orgid = ''` 查(无数据),
            加了 != '' 则变成不带 orgid 条件(返回全部机构数据)—— 结果会变。 -->
    <select id="selectSvyCategoryList"
            resultType="com.ruoyi.satisfaction.domain.row.SatisfactionCategoryRow">
        select id,
@@ -57,7 +61,7 @@
        <if test="q.type != null and q.type != ''">
            and type like concat('%', #{q.type}, '%')
        </if>
        <if test="q.orgid != null and q.orgid != ''">
        <if test="q.orgid != null">
            and orgid = #{q.orgid}
        </if>
        order by seqno asc
@@ -75,11 +79,11 @@
        select id,
               templateid,
               scriptid,
               script_content,
               script_assortname,
               script_assortid,
               script_topic,
               script_type,
               script_content as scriptContent,
               script_assortname as scriptAssortname,
               script_assortid as scriptAssortid,
               script_topic as scriptTopic,
               script_type as scriptType,
               dimension
        from ivr_liba_template_script
        where del_flag = 0
@@ -102,11 +106,11 @@
            resultType="com.ruoyi.satisfaction.domain.row.SatisfactionSvyScriptRow">
        select id,
               svyid,
               script_content,
               script_content as scriptContent,
               categoryName,
               categoryid,
               script_topic,
               script_type,
               script_topic as scriptTopic,
               script_type as scriptType,
               dimension
        from svy_lib_template_script
        where del_flag = 0
@@ -120,13 +124,13 @@
    <!-- ================================================ 按维度取问题(语音侧)
         原:IvrLibaTemplateScriptMapper.selectILTSBydimension
         口径要点:dimensionList 为空时拼 `dimension IS NOT NULL`(问卷侧不拼,两侧不同是原文行为) -->
         口径要点:dimensionList 为空时拼 `dimension IS NOT NULL`(问卷侧原文也有,见 selectSvyScriptByDimension) -->
    <select id="selectIvrScriptByDimension"
            resultType="com.ruoyi.satisfaction.domain.row.SatisfactionIvrScriptRow">
        select templateid,
               scriptid,
               script_content,
               script_assortname,
               script_content as scriptContent,
               script_assortname as scriptAssortname,
               dimension
        from ivr_liba_template_script
        <where>
@@ -145,13 +149,19 @@
    <!-- ================================================ 按维度取问题(问卷侧)
         原:SvyLibTemplateScriptMapper.selectSLTScriptListBydimension
         口径要点:dimensionList 为空时【不拼】dimension IS NOT NULL(等于全表 del_flag=0);
                   聚合用的模板 id 与选项关联 id 都是 svyid -->
         ⚠️ 两侧其实【都要】拼 dimension IS NOT NULL:
            - 语音侧(selectIvrScriptByDimension)该条件紧跟在 dimensionList 分支之后;
            - 问卷侧原文把它写在 <where> 的**最后一行**(在 duty/report/circulation 那些
              满意度用不到的条件之后),容易看漏 —— 早期迁移时曾误判为"问卷侧不拼",已修正。
         ⚠️ 必须同时取 id 与 svyid 两个【不同】的列(原文的 <include> 片段就是这样):
            聚合统计(发送量/填报量)按 svyid 分组,而取「选项分值」时用的是 id(主键),
            对应 svy_lib_template_targetoption.scriptid。早期迁移时曾误写成 `svyid as id`,
            导致选项分值匹配不上 -> 最高分/最低分恒为 0.0。 -->
    <select id="selectSvyScriptByDimension"
            resultType="com.ruoyi.satisfaction.domain.row.SatisfactionSvyScriptRow">
        select svyid,
               svyid as id,
               script_content,
        select id,
               svyid,
               script_content as scriptContent,
               categoryName,
               dimension
        from svy_lib_template_script
@@ -163,6 +173,9 @@
                    #{dimension}
                </foreach>
            </if>
            <if test="q.dimensionList == null or q.dimensionList.size() == 0">
                and dimension IS NOT NULL
            </if>
        </where>
    </select>