diff --git a/pom.xml b/pom.xml
index cd4202d..014c1af 100644
--- a/pom.xml
+++ b/pom.xml
@@ -8,7 +8,7 @@
org.springframework.boot
spring-boot-starter-parent
- 2.6.11
+ 2.7.2
com.yupi
diff --git a/src/main/java/com/yupi/springbootinit/common/NumberSerializer.java b/src/main/java/com/yupi/springbootinit/common/NumberSerializer.java
new file mode 100644
index 0000000..6d2d298
--- /dev/null
+++ b/src/main/java/com/yupi/springbootinit/common/NumberSerializer.java
@@ -0,0 +1,37 @@
+package com.yupi.springbootinit.common;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
+
+import java.io.IOException;
+
+/**
+ * Long 序列化规则
+ *
+ * 会将超长 long 值转换为 string,解决前端 JavaScript 最大安全整数是 2^53-1 的问题
+ *
+ * @author 星语
+ */
+@JacksonStdImpl
+public class NumberSerializer extends com.fasterxml.jackson.databind.ser.std.NumberSerializer {
+
+ private static final long MAX_SAFE_INTEGER = 9007199254740991L;
+ private static final long MIN_SAFE_INTEGER = -9007199254740991L;
+
+ public static final NumberSerializer INSTANCE = new NumberSerializer(Number.class);
+
+ public NumberSerializer(Class extends Number> rawType) {
+ super(rawType);
+ }
+
+ @Override
+ public void serialize(Number value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
+ // 超出范围 序列化位字符串
+ if (value.longValue() > MIN_SAFE_INTEGER && value.longValue() < MAX_SAFE_INTEGER) {
+ super.serialize(value, gen, serializers);
+ } else {
+ gen.writeString(value.toString());
+ }
+ }
+}
diff --git a/src/main/java/com/yupi/springbootinit/common/TimestampLocalDateTimeDeserializer.java b/src/main/java/com/yupi/springbootinit/common/TimestampLocalDateTimeDeserializer.java
new file mode 100644
index 0000000..a293487
--- /dev/null
+++ b/src/main/java/com/yupi/springbootinit/common/TimestampLocalDateTimeDeserializer.java
@@ -0,0 +1,27 @@
+package com.yupi.springbootinit.common;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+
+import java.io.IOException;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+
+/**
+ * 基于时间戳的 LocalDateTime 反序列化器
+ *
+ * @author 老五
+ */
+public class TimestampLocalDateTimeDeserializer extends JsonDeserializer {
+
+ public static final TimestampLocalDateTimeDeserializer INSTANCE = new TimestampLocalDateTimeDeserializer();
+
+ @Override
+ public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
+ // 将 Long 时间戳,转换为 LocalDateTime 对象
+ return LocalDateTime.ofInstant(Instant.ofEpochMilli(p.getValueAsLong()), ZoneId.systemDefault());
+ }
+
+}
diff --git a/src/main/java/com/yupi/springbootinit/common/TimestampLocalDateTimeSerializer.java b/src/main/java/com/yupi/springbootinit/common/TimestampLocalDateTimeSerializer.java
new file mode 100644
index 0000000..4aa18db
--- /dev/null
+++ b/src/main/java/com/yupi/springbootinit/common/TimestampLocalDateTimeSerializer.java
@@ -0,0 +1,26 @@
+package com.yupi.springbootinit.common;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+import java.io.IOException;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+
+/**
+ * 基于时间戳的 LocalDateTime 序列化器
+ *
+ * @author 老五
+ */
+public class TimestampLocalDateTimeSerializer extends JsonSerializer {
+
+ public static final TimestampLocalDateTimeSerializer INSTANCE = new TimestampLocalDateTimeSerializer();
+
+ @Override
+ public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
+ // 将 LocalDateTime 对象,转换为 Long 时间戳
+ gen.writeNumber(value.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
+ }
+
+}
diff --git a/src/main/java/com/yupi/springbootinit/config/RabbitMQConfig.java b/src/main/java/com/yupi/springbootinit/config/RabbitMQConfig.java
index 980b298..23b149d 100644
--- a/src/main/java/com/yupi/springbootinit/config/RabbitMQConfig.java
+++ b/src/main/java/com/yupi/springbootinit/config/RabbitMQConfig.java
@@ -1,6 +1,13 @@
package com.yupi.springbootinit.config;
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import com.rabbitmq.client.ConnectionFactory;
import org.springframework.amqp.core.Queue;
+import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
@@ -18,9 +25,20 @@ public class RabbitMQConfig {
public Queue hostInfoQueue(){
return new Queue(QUEUE,true);
}
+
+//
+// @Bean
+// public MessageConverter messageConverter(){
+// return new Jackson2JsonMessageConverter();
+// }
+
@Bean
- public MessageConverter messageConverter(){
- return new Jackson2JsonMessageConverter();
+ public MessageConverter messageConverter() {
+ ObjectMapper om = new ObjectMapper();
+ om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
+ om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
+ om.registerModule(new JavaTimeModule());
+ return new Jackson2JsonMessageConverter(om);
}
}
\ No newline at end of file
diff --git a/src/main/java/com/yupi/springbootinit/config/YudaoJacksonAutoConfiguration.java b/src/main/java/com/yupi/springbootinit/config/YudaoJacksonAutoConfiguration.java
new file mode 100644
index 0000000..e864f7d
--- /dev/null
+++ b/src/main/java/com/yupi/springbootinit/config/YudaoJacksonAutoConfiguration.java
@@ -0,0 +1,53 @@
+package com.yupi.springbootinit.config;
+
+import cn.hutool.core.collection.CollUtil;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
+import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
+import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
+import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
+import com.yupi.springbootinit.common.NumberSerializer;
+import com.yupi.springbootinit.common.TimestampLocalDateTimeDeserializer;
+import com.yupi.springbootinit.common.TimestampLocalDateTimeSerializer;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import com.yupi.springbootinit.utils.JsonUtils;
+import lombok.extern.slf4j.Slf4j;
+
+import org.springframework.context.annotation.Bean;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.List;
+
+@AutoConfiguration
+@Slf4j
+public class YudaoJacksonAutoConfiguration {
+
+ @Bean
+ @SuppressWarnings("InstantiationOfUtilityClass")
+ public JsonUtils jsonUtils(List 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));
+
+ // 2. 设置 objectMapper 到 JsonUtils
+ JsonUtils.init(CollUtil.getFirst(objectMappers));
+ log.info("[init][初始化 JsonUtils 成功]");
+ return new JsonUtils();
+ }
+
+}
diff --git a/src/main/java/com/yupi/springbootinit/model/entity/ServerBigBrother.java b/src/main/java/com/yupi/springbootinit/model/entity/ServerBigBrother.java
index 3a870af..b7fefb8 100644
--- a/src/main/java/com/yupi/springbootinit/model/entity/ServerBigBrother.java
+++ b/src/main/java/com/yupi/springbootinit/model/entity/ServerBigBrother.java
@@ -4,8 +4,17 @@ import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
+
+import java.time.LocalDateTime;
import java.util.Date;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
+import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
/*
* @author: ziin
@@ -98,4 +107,8 @@ public class ServerBigBrother {
*/
@TableField(value = "tenant_id")
private Long tenantId;
+
+ @TableField(value = "create_time")
+
+ private LocalDateTime createTime;
}
\ No newline at end of file
diff --git a/src/main/java/com/yupi/springbootinit/rabbitMQ/MQSender.java b/src/main/java/com/yupi/springbootinit/rabbitMQ/MQSender.java
index df5eb54..d2bddbd 100644
--- a/src/main/java/com/yupi/springbootinit/rabbitMQ/MQSender.java
+++ b/src/main/java/com/yupi/springbootinit/rabbitMQ/MQSender.java
@@ -12,6 +12,7 @@ import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
+import java.time.LocalDateTime;
import java.util.List;
@Slf4j
@@ -37,10 +38,11 @@ public class MQSender {
public void bigBrotherSend(ServerBigBrother bigBrothers){
try {
// log.info("{} 接收到的消息数量----------->{}", DateTime.now(),list.size());
- this.rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
+// this.rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
//指定你队列的名字
rabbitTemplate.convertAndSend("BIG_BROTHER_QUEUE",bigBrothers);
}catch (Exception e){
+ log.error(e.getMessage() );
throw new BusinessException(ErrorCode.QUEUE_ERROR);
}
}
diff --git a/src/main/java/com/yupi/springbootinit/service/impl/ServerBigBrotherServiceImpl.java b/src/main/java/com/yupi/springbootinit/service/impl/ServerBigBrotherServiceImpl.java
index a8bb9e1..a0c86c9 100644
--- a/src/main/java/com/yupi/springbootinit/service/impl/ServerBigBrotherServiceImpl.java
+++ b/src/main/java/com/yupi/springbootinit/service/impl/ServerBigBrotherServiceImpl.java
@@ -33,13 +33,16 @@ public class ServerBigBrotherServiceImpl extends ServiceImpl
+ * 通过这样的方式,使用 Spring 创建的 ObjectMapper Bean
+ *
+ * @param objectMapper ObjectMapper 对象
+ */
+ public static void init(ObjectMapper objectMapper) {
+ JsonUtils.objectMapper = objectMapper;
+ }
+
+ @SneakyThrows
+ public static String toJsonString(Object object) {
+ return objectMapper.writeValueAsString(object);
+ }
+
+ @SneakyThrows
+ public static byte[] toJsonByte(Object object) {
+ return objectMapper.writeValueAsBytes(object);
+ }
+
+ @SneakyThrows
+ public static String toJsonPrettyString(Object object) {
+ return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(object);
+ }
+
+ public static T parseObject(String text, Class clazz) {
+ if (StrUtil.isEmpty(text)) {
+ return null;
+ }
+ try {
+ return objectMapper.readValue(text, clazz);
+ } catch (IOException e) {
+ log.error("json parse err,json:{}", text, e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static T parseObject(String text, String path, Class clazz) {
+ if (StrUtil.isEmpty(text)) {
+ return null;
+ }
+ try {
+ JsonNode treeNode = objectMapper.readTree(text);
+ JsonNode pathNode = treeNode.path(path);
+ return objectMapper.readValue(pathNode.toString(), clazz);
+ } catch (IOException e) {
+ log.error("json parse err,json:{}", text, e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static T parseObject(String text, Type type) {
+ if (StrUtil.isEmpty(text)) {
+ return null;
+ }
+ try {
+ return objectMapper.readValue(text, objectMapper.getTypeFactory().constructType(type));
+ } catch (IOException e) {
+ log.error("json parse err,json:{}", text, e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * 将字符串解析成指定类型的对象
+ * 使用 {@link #parseObject(String, Class)} 时,在@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) 的场景下,
+ * 如果 text 没有 class 属性,则会报错。此时,使用这个方法,可以解决。
+ *
+ * @param text 字符串
+ * @param clazz 类型
+ * @return 对象
+ */
+ public static T parseObject2(String text, Class clazz) {
+ if (StrUtil.isEmpty(text)) {
+ return null;
+ }
+ return JSONUtil.toBean(text, clazz);
+ }
+
+ public static T parseObject(byte[] bytes, Class clazz) {
+ if (ArrayUtil.isEmpty(bytes)) {
+ return null;
+ }
+ try {
+ return objectMapper.readValue(bytes, clazz);
+ } catch (IOException e) {
+ log.error("json parse err,json:{}", bytes, e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static T parseObject(String text, TypeReference typeReference) {
+ try {
+ return objectMapper.readValue(text, typeReference);
+ } catch (IOException e) {
+ log.error("json parse err,json:{}", text, e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * 解析 JSON 字符串成指定类型的对象,如果解析失败,则返回 null
+ *
+ * @param text 字符串
+ * @param typeReference 类型引用
+ * @return 指定类型的对象
+ */
+ public static T parseObjectQuietly(String text, TypeReference typeReference) {
+ try {
+ return objectMapper.readValue(text, typeReference);
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ public static List parseArray(String text, Class clazz) {
+ if (StrUtil.isEmpty(text)) {
+ return new ArrayList<>();
+ }
+ try {
+ return objectMapper.readValue(text, objectMapper.getTypeFactory().constructCollectionType(List.class, clazz));
+ } catch (IOException e) {
+ log.error("json parse err,json:{}", text, e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static List parseArray(String text, String path, Class clazz) {
+ if (StrUtil.isEmpty(text)) {
+ return null;
+ }
+ try {
+ JsonNode treeNode = objectMapper.readTree(text);
+ JsonNode pathNode = treeNode.path(path);
+ return objectMapper.readValue(pathNode.toString(), objectMapper.getTypeFactory().constructCollectionType(List.class, clazz));
+ } catch (IOException e) {
+ log.error("json parse err,json:{}", text, e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static JsonNode parseTree(String text) {
+ try {
+ return objectMapper.readTree(text);
+ } catch (IOException e) {
+ log.error("json parse err,json:{}", text, e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static JsonNode parseTree(byte[] text) {
+ try {
+ return objectMapper.readTree(text);
+ } catch (IOException e) {
+ log.error("json parse err,json:{}", text, e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static boolean isJson(String text) {
+ return JSONUtil.isTypeJSON(text);
+ }
+
+ /**
+ * 判断字符串是否为 JSON 类型的字符串
+ * @param str 字符串
+ */
+ public static boolean isJsonObject(String str) {
+ return JSONUtil.isTypeJSONObject(str);
+ }
+
+}
diff --git a/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
new file mode 100644
index 0000000..e4c6044
--- /dev/null
+++ b/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -0,0 +1 @@
+com.yupi.springbootinit.config.YudaoJacksonAutoConfiguration
\ No newline at end of file
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 9e5d91c..9427ee0 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -38,7 +38,12 @@ spring:
username: root
password: 123asd
jackson:
- date-format: yyyy-MM-dd HH:mm:ss
+ # date-format: yyyy-MM-dd HH:mm:ss
+ serialization:
+ write-dates-as-timestamps: true # 设置 Date 的格式,使用时间戳
+ write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401
+ write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳
+ fail-on-empty-beans: false # 允许序列化无属性的 Bean
servlet:
multipart:
# 大小限制
diff --git a/src/main/resources/mapper/ServerBigBrotherMapper.xml b/src/main/resources/mapper/ServerBigBrotherMapper.xml
index fa22aa3..eb6d1c2 100644
--- a/src/main/resources/mapper/ServerBigBrotherMapper.xml
+++ b/src/main/resources/mapper/ServerBigBrotherMapper.xml
@@ -18,10 +18,6 @@
-
-
-
-
diff --git a/src/test/java/com/yupi/springbootinit/MainApplicationTests.java b/src/test/java/com/yupi/springbootinit/MainApplicationTests.java
deleted file mode 100644
index c1133bf..0000000
--- a/src/test/java/com/yupi/springbootinit/MainApplicationTests.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.yupi.springbootinit;
-
-import org.junit.jupiter.api.Test;
-import org.springframework.boot.test.context.SpringBootTest;
-
-/**
- * 主类测试
- *
- * @author 程序员鱼皮
- * @from 编程导航知识星球
- */
-@SpringBootTest
-class MainApplicationTests {
-
- @Test
- void contextLoads() {
- }
-
-}