Bladeren bron

feat:【framework 框架】优化 jackson 的初始化

YunaiV 9 maanden geleden
bovenliggende
commit
441fe18720

+ 1 - 1
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/db/TenantDatabaseInterceptor.java

@@ -75,7 +75,7 @@ public class TenantDatabaseInterceptor implements TenantLineHandler {
         if (TenantBaseDO.class.isAssignableFrom(tableInfo.getEntityType())) {
             return false;
         }
-        // 如果添加了 @TenantIgnore 注解,显然也不忽略租户
+        // 如果添加了 @TenantIgnore 注解,忽略租户
         TenantIgnore tenantIgnore = tableInfo.getEntityType().getAnnotation(TenantIgnore.class);
         return tenantIgnore != null;
     }

+ 49 - 23
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/jackson/config/YudaoJacksonAutoConfiguration.java

@@ -1,10 +1,10 @@
 package cn.iocoder.yudao.framework.jackson.config;
 
-import cn.hutool.core.collection.CollUtil;
 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
 import cn.iocoder.yudao.framework.common.util.json.databind.NumberSerializer;
 import cn.iocoder.yudao.framework.common.util.json.databind.TimestampLocalDateTimeDeserializer;
 import cn.iocoder.yudao.framework.common.util.json.databind.TimestampLocalDateTimeSerializer;
+import com.fasterxml.jackson.databind.Module;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.module.SimpleModule;
 import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
@@ -13,39 +13,65 @@ import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
 import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
+import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
 import org.springframework.context.annotation.Bean;
 
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.LocalTime;
-import java.util.List;
 
-@AutoConfiguration
+@AutoConfiguration(after = JacksonAutoConfiguration.class)
 @Slf4j
 public class YudaoJacksonAutoConfiguration {
 
+    /**
+     * 从 Builder 源头定制(关键:使用 *ByType,避免 handledType 要求)
+     */
     @Bean
-    @SuppressWarnings("InstantiationOfUtilityClass")
-    public JsonUtils jsonUtils(List<ObjectMapper> objectMappers) {
-        // 1.1 创建 SimpleModule 对象
-        SimpleModule simpleModule = new SimpleModule();
-        simpleModule
-                // 新增 Long 类型序列化规则,数值超过 2^53-1,在 JS 会出现精度丢失问题,因此 Long 自动序列化为字符串类型
-                .addSerializer(Long.class, NumberSerializer.INSTANCE)
-                .addSerializer(Long.TYPE, NumberSerializer.INSTANCE)
-                .addSerializer(LocalDate.class, LocalDateSerializer.INSTANCE)
-                .addDeserializer(LocalDate.class, LocalDateDeserializer.INSTANCE)
-                .addSerializer(LocalTime.class, LocalTimeSerializer.INSTANCE)
-                .addDeserializer(LocalTime.class, LocalTimeDeserializer.INSTANCE)
-                // 新增 LocalDateTime 序列化、反序列化规则,使用 Long 时间戳
-                .addSerializer(LocalDateTime.class, TimestampLocalDateTimeSerializer.INSTANCE)
-                .addDeserializer(LocalDateTime.class, TimestampLocalDateTimeDeserializer.INSTANCE);
-        // 1.2 注册到 objectMapper
-        objectMappers.forEach(objectMapper -> objectMapper.registerModule(simpleModule));
+    public Jackson2ObjectMapperBuilderCustomizer ldtEpochMillisCustomizer() {
+        return builder -> builder
+                // Long -> Number
+                .serializerByType(Long.class, NumberSerializer.INSTANCE)
+                .serializerByType(Long.TYPE, NumberSerializer.INSTANCE)
+                // LocalDate / LocalTime
+                .serializerByType(LocalDate.class, LocalDateSerializer.INSTANCE)
+                .deserializerByType(LocalDate.class, LocalDateDeserializer.INSTANCE)
+                .serializerByType(LocalTime.class, LocalTimeSerializer.INSTANCE)
+                .deserializerByType(LocalTime.class, LocalTimeDeserializer.INSTANCE)
+                // LocalDateTime < - > EpochMillis
+                .serializerByType(LocalDateTime.class, TimestampLocalDateTimeSerializer.INSTANCE)
+                .deserializerByType(LocalDateTime.class, TimestampLocalDateTimeDeserializer.INSTANCE);
+    }
 
-        // 2. 设置 objectMapper 到 JsonUtils
-        JsonUtils.init(CollUtil.getFirst(objectMappers));
-        log.info("[init][初始化 JsonUtils 成功]");
+    /**
+     * 以 Bean 形式暴露 Module(Boot 会自动注册到所有 ObjectMapper)
+     */
+    @Bean
+    public Module timestampSupportModuleBean() {
+        SimpleModule m = new SimpleModule("TimestampSupportModule");
+        // Long -> Number,避免前端精度丢失
+        m.addSerializer(Long.class, NumberSerializer.INSTANCE);
+        m.addSerializer(Long.TYPE, NumberSerializer.INSTANCE);
+        // LocalDate / LocalTime
+        m.addSerializer(LocalDate.class, LocalDateSerializer.INSTANCE);
+        m.addDeserializer(LocalDate.class, LocalDateDeserializer.INSTANCE);
+        m.addSerializer(LocalTime.class, LocalTimeSerializer.INSTANCE);
+        m.addDeserializer(LocalTime.class, LocalTimeDeserializer.INSTANCE);
+        // LocalDateTime < - > EpochMillis
+        m.addSerializer(LocalDateTime.class, TimestampLocalDateTimeSerializer.INSTANCE);
+        m.addDeserializer(LocalDateTime.class, TimestampLocalDateTimeDeserializer.INSTANCE);
+        return m;
+    }
+
+    /**
+     * 初始化全局 JsonUtils,直接使用主 ObjectMapper
+     */
+    @Bean
+    @SuppressWarnings("InstantiationOfUtilityClass")
+    public JsonUtils jsonUtils(ObjectMapper objectMapper) {
+        JsonUtils.init(objectMapper);
+        log.debug("[init][初始化 JsonUtils 成功]");
         return new JsonUtils();
     }
 

+ 1 - 1
yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java

@@ -166,7 +166,7 @@ public class CouponServiceImpl implements CouponService {
     public void invalidateCouponsByAdmin(List<Long> giveCouponIds, Long userId) {
         // 循环收回
         for (Long couponId : giveCouponIds) {
-            // couponId为空或0则跳过
+            // couponId 为空或 0 则跳过
             if (null == couponId || couponId <= 0) {
                 continue;
             }