Commit 8fdc34e9b3af191bf5d4b2bfb2cce5bd92533451

Authored by YunaiV
1 parent 74e32f50

fix(mybatis): 修复 Map 查询结果的列名大小写兼容

yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/config/YudaoMybatisAutoConfiguration.java
... ... @@ -4,11 +4,15 @@ import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.util.StrUtil;
5 5 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
6 6 import cn.iocoder.yudao.framework.mybatis.core.handler.DefaultDBFieldHandler;
  7 +import cn.iocoder.yudao.framework.mybatis.core.injector.YudaoSqlInjector;
  8 +import cn.iocoder.yudao.framework.mybatis.core.util.CaseInsensitiveResultMap;
7 9 import com.baomidou.mybatisplus.annotation.DbType;
  10 +import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
8 11 import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
9 12 import com.baomidou.mybatisplus.core.handlers.IJsonTypeHandler;
10 13 import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
11 14 import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;
  15 +import com.baomidou.mybatisplus.core.injector.ISqlInjector;
12 16 import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
13 17 import com.baomidou.mybatisplus.extension.incrementer.*;
14 18 import com.baomidou.mybatisplus.extension.parser.JsqlParserGlobal;
... ... @@ -17,12 +21,15 @@ import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
17 21 import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
18 22 import com.fasterxml.jackson.databind.ObjectMapper;
19 23 import org.apache.ibatis.annotations.Mapper;
  24 +import org.apache.ibatis.mapping.ResultMap;
20 25 import org.mybatis.spring.annotation.MapperScan;
21 26 import org.springframework.boot.autoconfigure.AutoConfiguration;
22 27 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
23 28 import org.springframework.context.annotation.Bean;
  29 +import org.springframework.context.annotation.Primary;
24 30 import org.springframework.core.env.ConfigurableEnvironment;
25 31  
  32 +import java.util.ArrayList;
26 33 import java.util.List;
27 34 import java.util.concurrent.TimeUnit;
28 35  
... ... @@ -59,6 +66,19 @@ public class YudaoMybatisAutoConfiguration {
59 66 }
60 67  
61 68 @Bean
  69 + @Primary // 特殊:使用 @Primary 的原因,避免 MyBatis Plus Join 再创建 MPJSqlInjector 包装它
  70 + public ISqlInjector yudaoSqlInjector() {
  71 + return new YudaoSqlInjector(); // selectMaps 的结果行,使用 CaseInsensitiveResultMap
  72 + }
  73 +
  74 + @Bean
  75 + public ConfigurationCustomizer caseInsensitiveResultMapConfigurationCustomizer() {
  76 + // 注册 CaseInsensitiveResultMap 的 resultMap,用于注解 SQL 的 @ResultMap
  77 + return configuration -> configuration.addResultMap(new ResultMap.Builder(configuration,
  78 + CaseInsensitiveResultMap.RESULT_MAP_ID, CaseInsensitiveResultMap.class, new ArrayList<>()).build());
  79 + }
  80 +
  81 + @Bean
62 82 @ConditionalOnProperty(prefix = "mybatis-plus.global-config.db-config", name = "id-type", havingValue = "INPUT")
63 83 public IKeyGenerator keyGenerator(ConfigurableEnvironment environment) {
64 84 DbType dbType = IdTypeEnvironmentPostProcessor.getDbType(environment);
... ...
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/injector/YudaoSqlInjector.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.mybatis.core.injector;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.util.CaseInsensitiveResultMap;
  4 +import com.baomidou.mybatisplus.core.injector.AbstractMethod;
  5 +import com.baomidou.mybatisplus.core.metadata.TableInfo;
  6 +import com.github.yulichang.injector.MPJSqlInjector;
  7 +import com.github.yulichang.method.mp.SelectMaps;
  8 +import org.apache.ibatis.mapping.MappedStatement;
  9 +import org.apache.ibatis.mapping.SqlSource;
  10 +import org.apache.ibatis.session.Configuration;
  11 +
  12 +import java.util.List;
  13 +
  14 +/**
  15 + * 自定义 SQL 注入器
  16 + *
  17 + * 在 MyBatis Plus Join 的 {@link MPJSqlInjector} 基础上,将 selectMaps 的结果行替换为 {@link CaseInsensitiveResultMap},
  18 + * 解决 PostgreSQL、Oracle 等数据库列标签大小写折叠,导致按固定 key 取不到值的问题
  19 + *
  20 + * @author 芋道源码
  21 + */
  22 +public class YudaoSqlInjector extends MPJSqlInjector {
  23 +
  24 + @Override
  25 + public List<AbstractMethod> getMethodList(Configuration configuration, Class<?> mapperClass, TableInfo tableInfo) {
  26 + List<AbstractMethod> methods = super.getMethodList(configuration, mapperClass, tableInfo);
  27 + methods.replaceAll(method -> method instanceof SelectMaps ? new CaseInsensitiveSelectMaps() : method);
  28 + return methods;
  29 + }
  30 +
  31 + /**
  32 + * 结果行为 {@link CaseInsensitiveResultMap} 的 selectMaps
  33 + *
  34 + * 仅修改结果类型,SQL 仍由 MyBatis Plus Join 的 {@link SelectMaps} 生成,兼容 MPJ 的 Wrapper
  35 + */
  36 + private static class CaseInsensitiveSelectMaps extends SelectMaps {
  37 +
  38 + @Override
  39 + protected MappedStatement addSelectMappedStatementForOther(Class<?> mapperClass, String id, SqlSource sqlSource,
  40 + Class<?> resultType) {
  41 + return super.addSelectMappedStatementForOther(mapperClass, id, sqlSource, CaseInsensitiveResultMap.class);
  42 + }
  43 +
  44 + }
  45 +
  46 +}
... ...
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java
... ... @@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 6 import cn.iocoder.yudao.framework.common.pojo.SortablePageParam;
7 7 import cn.iocoder.yudao.framework.common.pojo.SortingField;
8 8 import cn.iocoder.yudao.framework.common.util.object.PageUtils;
  9 +import cn.iocoder.yudao.framework.mybatis.core.util.CaseInsensitiveResultMap;
9 10 import cn.iocoder.yudao.framework.mybatis.core.util.JdbcUtils;
10 11 import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
11 12 import com.baomidou.mybatisplus.annotation.DbType;
... ... @@ -14,6 +15,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
14 15 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
15 16 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
16 17 import com.baomidou.mybatisplus.core.metadata.IPage;
  18 +import com.baomidou.mybatisplus.core.toolkit.Constants;
17 19 import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
18 20 import com.baomidou.mybatisplus.extension.toolkit.Db;
19 21 import com.github.yulichang.base.MPJBaseMapper;
... ... @@ -23,6 +25,7 @@ import org.apache.ibatis.annotations.Param;
23 25  
24 26 import java.util.Collection;
25 27 import java.util.List;
  28 +import java.util.Map;
26 29  
27 30 /**
28 31 * 在 MyBatis Plus 的 BaseMapper 的基础上拓展,提供更多的能力
... ... @@ -99,6 +102,24 @@ public interface BaseMapperX&lt;T&gt; extends MPJBaseMapper&lt;T&gt; {
99 102 return new PageResult<>(mpPage.getRecords(), mpPage.getTotal());
100 103 }
101 104  
  105 + @Override
  106 + @SuppressWarnings("unchecked")
  107 + default Map<String, Object> selectJoinMap(@Param(Constants.WRAPPER) MPJBaseJoin<T> wrapper) {
  108 + return selectJoinOne(CaseInsensitiveResultMap.class, wrapper);
  109 + }
  110 +
  111 + @Override
  112 + @SuppressWarnings("unchecked")
  113 + default List<Map<String, Object>> selectJoinMaps(@Param(Constants.WRAPPER) MPJBaseJoin<T> wrapper) {
  114 + return (List<Map<String, Object>>) ((Object) selectJoinList(CaseInsensitiveResultMap.class, wrapper));
  115 + }
  116 +
  117 + @Override
  118 + @SuppressWarnings({"unchecked", "rawtypes"})
  119 + default <P extends IPage<Map<String, Object>>> P selectJoinMapsPage(P page, @Param(Constants.WRAPPER) MPJBaseJoin<T> wrapper) {
  120 + return (P) selectJoinPage((IPage) page, CaseInsensitiveResultMap.class, wrapper);
  121 + }
  122 +
102 123 default T selectOne(String field, Object value) {
103 124 return selectOne(new QueryWrapper<T>().eq(field, value));
104 125 }
... ...
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/CaseInsensitiveResultMap.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.mybatis.core.util;
  2 +
  3 +import org.springframework.util.LinkedCaseInsensitiveMap;
  4 +
  5 +import java.util.Locale;
  6 +
  7 +/**
  8 + * key 大小写不敏感的 MyBatis 查询结果 Map
  9 + *
  10 + * 背景:使用 Map 接收查询结果时,key 为 JDBC 返回的列标签。PostgreSQL 等会将未加引号的列名、别名折叠为小写,
  11 + * Oracle、达梦等会折叠为大写,导致 map.get("conversationId") 这类按固定 key 取值的代码,在 MySQL 以外的数据库取不到值。
  12 + *
  13 + * 实现:基于 {@link LinkedCaseInsensitiveMap},get / containsKey / remove 时忽略 key 的大小写,
  14 + * 同时保留原始的列标签作为 key(遍历、序列化时不变)。固定使用 {@link Locale#ROOT},避免服务器默认 Locale(例如土耳其语)影响 key 的匹配。
  15 + *
  16 + * 使用方式:由 MyBatis 直接创建结果行,Mapper 的返回类型仍声明为 Map,业务无需感知该类
  17 + * 1. MyBatis Plus 的 selectMaps:由 YudaoSqlInjector 注入,自动使用该类
  18 + * 2. MyBatis Plus Join 的 selectJoinMaps 等:由 BaseMapperX 覆盖,自动使用该类
  19 + * 3. XML SQL:将 resultType 设置为该类
  20 + * 4. 注解 SQL:添加 @ResultMap({@link #RESULT_MAP_ID})
  21 + *
  22 + * 注意:仅大小写不同的多个 key,会合并为一个,保留后写入的 key 和值
  23 + *
  24 + * @author 芋道源码
  25 + * @see <a href="https://t.zsxq.com/kPvqm">星球讨论:Map 查询结果的跨数据库兼容问题</a>
  26 + */
  27 +public class CaseInsensitiveResultMap<V> extends LinkedCaseInsensitiveMap<V> {
  28 +
  29 + /**
  30 + * 注解 SQL 使用的 resultMap 编号
  31 + *
  32 + * 由 YudaoMybatisAutoConfiguration 注册到 MyBatis 全局配置;包含“.”,所以不会拼接 Mapper 的 namespace
  33 + */
  34 + public static final String RESULT_MAP_ID = "cn.iocoder.yudao.framework.mybatis.core.util.CaseInsensitiveResultMap";
  35 +
  36 + /**
  37 + * 创建空的结果 Map
  38 + *
  39 + * 用途:MyBatis 通过无参构造方法,实例化结果行
  40 + */
  41 + public CaseInsensitiveResultMap() {
  42 + super(Locale.ROOT);
  43 + }
  44 +
  45 +}
... ...
yudao-framework/yudao-spring-boot-starter-mybatis/src/test/java/cn/iocoder/yudao/framework/mybatis/core/util/CaseInsensitiveResultMapTest.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.mybatis.core.util;
  2 +
  3 +import cn.hutool.core.map.MapUtil;
  4 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
  5 +import org.junit.jupiter.api.Test;
  6 +import org.springframework.util.LinkedCaseInsensitiveMap;
  7 +
  8 +import java.util.*;
  9 +
  10 +import static org.junit.jupiter.api.Assertions.*;
  11 +
  12 +/**
  13 + * {@link CaseInsensitiveResultMap} 的单元测试
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +public class CaseInsensitiveResultMapTest {
  18 +
  19 + @Test
  20 + public void testGet_ignoreCase() {
  21 + // 准备参数
  22 + CaseInsensitiveResultMap<Object> map = new CaseInsensitiveResultMap<>();
  23 + map.put("conversationId", 1024L);
  24 +
  25 + // 调用,并断言:PostgreSQL 小写、Oracle 大写,都可以取到值
  26 + assertEquals(1024L, map.get("conversationId"));
  27 + assertEquals(1024L, map.get("conversationid"));
  28 + assertEquals(1024L, map.get("CONVERSATIONID"));
  29 + assertTrue(map.containsKey("CONVERSATIONID"));
  30 + assertEquals(1024L, MapUtil.getLong(map, "conversationid"));
  31 + // 断言:保留原始的 key
  32 + assertEquals(Collections.singletonList("conversationId"), new ArrayList<>(map.keySet()));
  33 + }
  34 +
  35 + @Test
  36 + public void testPut_nullValue() {
  37 + // 准备参数
  38 + CaseInsensitiveResultMap<Object> map = new CaseInsensitiveResultMap<>();
  39 + map.put("sum_count", null);
  40 +
  41 + // 调用,并断言
  42 + assertTrue(map.containsKey("SUM_COUNT"));
  43 + assertNull(map.get("SUM_COUNT"));
  44 + assertEquals(0, MapUtil.getInt(map, "SUM_COUNT", 0));
  45 + }
  46 +
  47 + @Test
  48 + public void testPut_sameKeyIgnoreCase() {
  49 + // 准备参数
  50 + CaseInsensitiveResultMap<Object> map = new CaseInsensitiveResultMap<>();
  51 +
  52 + // 调用:仅大小写不同的 key
  53 + map.put("id", 1L);
  54 + map.put("ID", 2L);
  55 +
  56 + // 断言:合并为一个,保留后写入的 key 和值
  57 + assertEquals(1, map.size());
  58 + assertEquals(2L, map.get("id"));
  59 + assertEquals(Collections.singletonList("ID"), new ArrayList<>(map.keySet()));
  60 + }
  61 +
  62 + @Test
  63 + public void testGet_turkishDefaultLocale() {
  64 + Locale defaultLocale = Locale.getDefault();
  65 + try {
  66 + // 准备参数:土耳其语下,大写 I 转小写为无点的 ı
  67 + Locale.setDefault(Locale.forLanguageTag("tr-TR"));
  68 + CaseInsensitiveResultMap<Object> map = new CaseInsensitiveResultMap<>();
  69 + map.put("CONVERSATIONID", 1024L);
  70 + LinkedCaseInsensitiveMap<Object> defaultLocaleMap = new LinkedCaseInsensitiveMap<>();
  71 + defaultLocaleMap.put("CONVERSATIONID", 1024L);
  72 +
  73 + // 调用,并断言:固定 Locale.ROOT,不受默认 Locale 影响
  74 + assertEquals(1024L, map.get("conversationId"));
  75 + // 断言:使用默认 Locale 时,取不到值
  76 + assertNull(defaultLocaleMap.get("conversationId"));
  77 + } finally {
  78 + Locale.setDefault(defaultLocale);
  79 + }
  80 + }
  81 +
  82 + @Test
  83 + public void testConvertMap_numberKey() {
  84 + // 准备参数:模拟 Oracle 返回的结果行
  85 + List<Map<String, Object>> rows = Arrays.asList(buildRow(1024L, 3), buildRow(2048L, 5));
  86 +
  87 + // 调用:业务转换为数字 key 的 Map
  88 + Map<Long, Integer> result = CollectionUtils.convertMap(rows,
  89 + row -> MapUtil.getLong(row, "conversationId"),
  90 + row -> MapUtil.getInt(row, "count"));
  91 +
  92 + // 断言:数字 key 的业务 Map 不受影响
  93 + assertInstanceOf(HashMap.class, result);
  94 + assertEquals(3, result.get(1024L));
  95 + assertEquals(5, result.get(2048L));
  96 + }
  97 +
  98 + private static Map<String, Object> buildRow(Long conversationId, Integer count) {
  99 + CaseInsensitiveResultMap<Object> row = new CaseInsensitiveResultMap<>();
  100 + row.put("CONVERSATIONID", conversationId);
  101 + row.put("COUNT", count);
  102 + return row;
  103 + }
  104 +
  105 +}
... ...
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/statistics/ImStatisticsManagerMapper.java
1 1 package cn.iocoder.yudao.module.im.dal.mysql.statistics;
2 2  
  3 +import cn.iocoder.yudao.framework.mybatis.core.util.CaseInsensitiveResultMap;
3 4 import org.apache.ibatis.annotations.Mapper;
4 5 import org.apache.ibatis.annotations.Param;
  6 +import org.apache.ibatis.annotations.ResultMap;
5 7 import org.apache.ibatis.annotations.Select;
6 8  
7 9 import java.time.LocalDateTime;
... ... @@ -59,6 +61,7 @@ public interface ImStatisticsManagerMapper {
59 61 @Select("SELECT DATE(create_time) AS date, COUNT(*) AS count FROM system_users " +
60 62 "WHERE deleted = 0 AND create_time >= #{beginTime} AND create_time < #{endTime} " +
61 63 "GROUP BY DATE(create_time)")
  64 + @ResultMap(CaseInsensitiveResultMap.RESULT_MAP_ID)
62 65 List<Map<String, Object>> selectNewUserDailyCount(@Param("beginTime") LocalDateTime beginTime,
63 66 @Param("endTime") LocalDateTime endTime);
64 67  
... ... @@ -74,6 +77,7 @@ public interface ImStatisticsManagerMapper {
74 77 " WHERE deleted = 0 AND " + NORMAL_MESSAGE_CONDITION +
75 78 " AND send_time >= #{beginTime} AND send_time < #{endTime}" +
76 79 ") t GROUP BY day")
  80 + @ResultMap(CaseInsensitiveResultMap.RESULT_MAP_ID)
77 81 List<Map<String, Object>> selectActiveUserDailyCount(@Param("beginTime") LocalDateTime beginTime, @Param("endTime") LocalDateTime endTime);
78 82  
79 83 // ==================== 群 ====================
... ... @@ -111,6 +115,7 @@ public interface ImStatisticsManagerMapper {
111 115 " GROUP BY g.id" +
112 116 ") t " +
113 117 "GROUP BY `range`")
  118 + @ResultMap(CaseInsensitiveResultMap.RESULT_MAP_ID)
114 119 List<Map<String, Object>> selectGroupSizeDistribution();
115 120  
116 121 // ==================== 消息 ====================
... ... @@ -140,6 +145,7 @@ public interface ImStatisticsManagerMapper {
140 145 "WHERE deleted = 0 AND " + NORMAL_MESSAGE_CONDITION +
141 146 " AND send_time >= #{beginTime} AND send_time < #{endTime} " +
142 147 "GROUP BY DATE(send_time)")
  148 + @ResultMap(CaseInsensitiveResultMap.RESULT_MAP_ID)
143 149 List<Map<String, Object>> selectPrivateMessageDailyCount(@Param("beginTime") LocalDateTime beginTime,
144 150 @Param("endTime") LocalDateTime endTime);
145 151  
... ... @@ -150,6 +156,7 @@ public interface ImStatisticsManagerMapper {
150 156 "WHERE deleted = 0 AND " + NORMAL_MESSAGE_CONDITION +
151 157 " AND send_time >= #{beginTime} AND send_time < #{endTime} " +
152 158 "GROUP BY DATE(send_time)")
  159 + @ResultMap(CaseInsensitiveResultMap.RESULT_MAP_ID)
153 160 List<Map<String, Object>> selectGroupMessageDailyCount(@Param("beginTime") LocalDateTime beginTime,
154 161 @Param("endTime") LocalDateTime endTime);
155 162  
... ... @@ -167,6 +174,7 @@ public interface ImStatisticsManagerMapper {
167 174 " WHERE deleted = 0 AND " + NORMAL_MESSAGE_CONDITION +
168 175 " AND send_time >= #{beginTime} AND send_time < #{endTime}" +
169 176 ") t GROUP BY type")
  177 + @ResultMap(CaseInsensitiveResultMap.RESULT_MAP_ID)
170 178 List<Map<String, Object>> selectMessageTypeDistribution(@Param("beginTime") LocalDateTime beginTime,
171 179 @Param("endTime") LocalDateTime endTime);
172 180  
... ... @@ -184,6 +192,7 @@ public interface ImStatisticsManagerMapper {
184 192 " WHERE deleted = 0 AND " + NORMAL_MESSAGE_CONDITION +
185 193 " AND send_time >= #{beginTime} AND send_time < #{endTime}" +
186 194 ") t GROUP BY user_id ORDER BY messageCount DESC LIMIT #{limit}")
  195 + @ResultMap(CaseInsensitiveResultMap.RESULT_MAP_ID)
187 196 List<Map<String, Object>> selectTopSenders(@Param("beginTime") LocalDateTime beginTime,
188 197 @Param("endTime") LocalDateTime endTime,
189 198 @Param("limit") int limit);
... ...
yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/dal/mysql/statistics/ImStatisticsManagerMapperTest.java
... ... @@ -228,6 +228,9 @@ public class ImStatisticsManagerMapperTest extends BaseDbUnitTest {
228 228 assertEquals(3L, ((Number) tops.get(0).get("messageCount")).longValue());
229 229 assertEquals(2L, ((Number) tops.get(1).get("userId")).longValue());
230 230 assertEquals(2L, ((Number) tops.get(1).get("messageCount")).longValue());
  231 + // 断言:key 大小写不敏感,兼容 PostgreSQL 小写、Oracle 大写的列标签
  232 + assertEquals(1L, ((Number) tops.get(0).get("userid")).longValue());
  233 + assertEquals(3L, ((Number) tops.get(0).get("MESSAGECOUNT")).longValue());
231 234 }
232 235  
233 236 // ========== 工具方法 ==========
... ...
yudao-module-mes/src/main/resources/mapper/home/MesHomeStatisticsMapper.xml
... ... @@ -3,7 +3,7 @@
3 3 <mapper namespace="cn.iocoder.yudao.module.mes.dal.mysql.home.MesHomeStatisticsMapper">
4 4  
5 5 <!-- 按状态统计工单数量 -->
6   - <select id="selectWorkOrderCountGroupByStatus" resultType="java.util.Map">
  6 + <select id="selectWorkOrderCountGroupByStatus" resultType="cn.iocoder.yudao.framework.mybatis.core.util.CaseInsensitiveResultMap">
7 7 SELECT status, COUNT(*) AS count
8 8 FROM mes_pro_work_order
9 9 WHERE deleted = 0
... ... @@ -11,7 +11,7 @@
11 11 </select>
12 12  
13 13 <!-- 统计报工产量合计 -->
14   - <select id="selectFeedbackSummary" resultType="java.util.Map">
  14 + <select id="selectFeedbackSummary" resultType="cn.iocoder.yudao.framework.mybatis.core.util.CaseInsensitiveResultMap">
15 15 SELECT IFNULL(SUM(feedback_quantity), 0) AS feedbackQuantity,
16 16 IFNULL(SUM(qualified_quantity), 0) AS qualifiedQuantity,
17 17 IFNULL(SUM(unqualified_quantity), 0) AS unqualifiedQuantity
... ... @@ -25,7 +25,7 @@
25 25 </select>
26 26  
27 27 <!-- 按设备状态统计设备台数 -->
28   - <select id="selectMachineryCountGroupByStatus" resultType="java.util.Map">
  28 + <select id="selectMachineryCountGroupByStatus" resultType="cn.iocoder.yudao.framework.mybatis.core.util.CaseInsensitiveResultMap">
29 29 SELECT status, COUNT(*) AS count
30 30 FROM mes_dv_machinery
31 31 WHERE deleted = 0
... ... @@ -51,7 +51,7 @@
51 51 </select>
52 52  
53 53 <!-- 按天聚合报工产量 -->
54   - <select id="selectDailyFeedbackTrend" resultType="java.util.Map">
  54 + <select id="selectDailyFeedbackTrend" resultType="cn.iocoder.yudao.framework.mybatis.core.util.CaseInsensitiveResultMap">
55 55 SELECT DATE_FORMAT(feedback_time, '%Y-%m-%d') AS date,
56 56 IFNULL(SUM(feedback_quantity), 0) AS quantity,
57 57 IFNULL(SUM(qualified_quantity), 0) AS qualifiedQuantity,
... ...
yudao-module-wms/src/main/resources/mapper/home/WmsHomeStatisticsMapper.xml
... ... @@ -3,7 +3,7 @@
3 3 <mapper namespace="cn.iocoder.yudao.module.wms.dal.mysql.home.WmsHomeStatisticsMapper">
4 4  
5 5 <!-- 按单据类型和状态统计单据数量 -->
6   - <select id="selectOrderCountGroupByTypeAndStatus" resultType="java.util.Map">
  6 + <select id="selectOrderCountGroupByTypeAndStatus" resultType="cn.iocoder.yudao.framework.mybatis.core.util.CaseInsensitiveResultMap">
7 7 SELECT orderType, status, SUM(count) AS count
8 8 FROM (
9 9 SELECT ${@cn.iocoder.yudao.module.wms.enums.order.WmsOrderTypeEnum@RECEIPT.type} AS orderType,
... ... @@ -51,7 +51,7 @@
51 51 </select>
52 52  
53 53 <!-- 按天聚合单据数量 -->
54   - <select id="selectDailyOrderTrend" resultType="java.util.Map">
  54 + <select id="selectDailyOrderTrend" resultType="cn.iocoder.yudao.framework.mybatis.core.util.CaseInsensitiveResultMap">
55 55 SELECT date, orderType, SUM(count) AS count
56 56 FROM (
57 57 SELECT DATE_FORMAT(order_time, '%Y-%m-%d') AS date,
... ... @@ -117,7 +117,7 @@
117 117 </select>
118 118  
119 119 <!-- 按商品统计库存数量排行 -->
120   - <select id="selectInventoryItemRank" resultType="java.util.Map">
  120 + <select id="selectInventoryItemRank" resultType="cn.iocoder.yudao.framework.mybatis.core.util.CaseInsensitiveResultMap">
121 121 SELECT item.id AS id,
122 122 item.name AS name,
123 123 IFNULL(SUM(inventory.quantity), 0) AS quantity
... ... @@ -135,7 +135,7 @@
135 135 </select>
136 136  
137 137 <!-- 按仓库统计库存数量排行 -->
138   - <select id="selectInventoryWarehouseRank" resultType="java.util.Map">
  138 + <select id="selectInventoryWarehouseRank" resultType="cn.iocoder.yudao.framework.mybatis.core.util.CaseInsensitiveResultMap">
139 139 SELECT warehouse.id AS id,
140 140 warehouse.name AS name,
141 141 IFNULL(SUM(inventory.quantity), 0) AS quantity
... ...