1.修改返回创建时间为时间戳格式

2.修复排序功能 Bug
This commit is contained in:
2025-06-26 17:39:43 +08:00
parent 85c872ff07
commit c3c722e904
13 changed files with 412 additions and 61 deletions

View File

@@ -16,7 +16,7 @@ public enum ErrorCode {
TOKEN_INVALID(40400, "Token无效请重新登录"),
NO_AUTH_ERROR(40101, "无权限"),
PACKAGE_EXPIRED(40500, "套餐已到期"),
NOT_FOUND_ERROR(40400, "请求数据不存在"),
NOT_FOUND_ERROR(40800, "请求数据不存在"),
FORBIDDEN_ERROR(40300, "禁止访问"),
TENANT_NAME_NOT_EXISTS(40600, "租户不存在"),
LOGIN_NOW_ALLOWED(40700, "当前账号不允许登录"),

View File

@@ -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());
}
}
}

View File

@@ -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<LocalDateTime> {
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());
}
}

View File

@@ -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<LocalDateTime> {
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());
}
}

View File

@@ -0,0 +1,52 @@
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 com.yupi.springbootinit.utils.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfiguration;
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<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));
// 2. 设置 objectMapper 到 JsonUtils
JsonUtils.init(CollUtil.getFirst(objectMappers));
log.info("[init][初始化 JsonUtils 成功]");
return new JsonUtils();
}
}

View File

@@ -158,4 +158,11 @@ public class ServerBigBrotherDTO extends PageRequest implements Serializable {
@TableField(value = "total_gift_coins")
@ApiModelProperty(value="大哥历史打赏金币总和")
private Integer totalGiftCoins;
/**
* 大哥打赏的金币
*/
@TableField(value = "hostcoins")
@ApiModelProperty(value="大哥打赏的金币")
private Integer hostcoins;
}

View File

@@ -4,10 +4,16 @@ 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 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 io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.Date;
/*
@@ -19,12 +25,6 @@ import java.util.Date;
@Data
@TableName(value = "server_big_brother")
public class ServerBigBrotherVO {
/**
* 主键id
*/
@TableId(value = "id", type = IdType.AUTO)
@ApiModelProperty(value="主键id")
private Integer id;
/**
* 大哥的display_id
@@ -103,52 +103,10 @@ public class ServerBigBrotherVO {
@ApiModelProperty(value="大哥所在的直播间的主播display_id")
private String hostDisplayId;
/**
* 该数据所属的账号id
*/
@TableField(value = "owner_id")
@ApiModelProperty(value="该数据所属的账号id")
private String ownerId;
/**
* 创建时间
*/
@TableField(value = "create_time")
*/@TableField(value = "create_time")
@ApiModelProperty(value="创建时间")
private Date createTime;
private LocalDateTime createTime;
/**
* 更新时间
*/
@TableField(value = "update_time")
@ApiModelProperty(value="更新时间")
private Date updateTime;
/**
* 创建人
*/
@TableField(value = "creator")
@ApiModelProperty(value="创建人")
private Long creator;
/**
* 更新人
*/
@TableField(value = "updater")
@ApiModelProperty(value="更新人")
private String updater;
/**
* 逻辑删除标志0-未删除1-已删除
*/
@TableField(value = "deleted")
@ApiModelProperty(value="逻辑删除标志0-未删除1-已删除")
private Boolean deleted;
/**
* 租户 Id
*/
@TableField(value = "tenant_id")
@ApiModelProperty(value="租户 Id")
private Long tenantId;
}

View File

@@ -1,6 +1,8 @@
package com.yupi.springbootinit.service.impl;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yupi.springbootinit.common.ErrorCode;
import com.yupi.springbootinit.exception.BusinessException;
import com.yupi.springbootinit.model.dto.host.HostInfoDTO;
import com.yupi.springbootinit.model.dto.host.ServerBigBrotherDTO;
import com.yupi.springbootinit.model.vo.bigbrother.ServerBigBrotherVO;
@@ -30,7 +32,11 @@ public class ServerBigBrotherServiceImpl extends ServiceImpl<ServerBigBrotherMap
public Page<ServerBigBrotherVO> getConditionHosts(ServerBigBrotherDTO serverBigBrotherDTO) {
Page<ServerBigBrotherVO> page = new Page<>(serverBigBrotherDTO.getCurrent(),serverBigBrotherDTO.getPageSize());
return serverBigBrotherMapper.selectPageByCondition(page, serverBigBrotherDTO);
try {
return serverBigBrotherMapper.selectPageByCondition(page, serverBigBrotherDTO);
} catch (Exception e) {
throw new BusinessException(ErrorCode.OPERATION_ERROR);
}
}

View File

@@ -0,0 +1,210 @@
package com.yupi.springbootinit.utils;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
/**
* JSON 工具类
*
* @author 芋道源码
*/
@Slf4j
public class JsonUtils {
private static ObjectMapper objectMapper = new ObjectMapper();
static {
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // 忽略 null 值
objectMapper.registerModules(new JavaTimeModule()); // 解决 LocalDateTime 的序列化
}
/**
* 初始化 objectMapper 属性
* <p>
* 通过这样的方式,使用 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> T parseObject(String text, Class<T> 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> T parseObject(String text, String path, Class<T> 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> 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> T parseObject2(String text, Class<T> clazz) {
if (StrUtil.isEmpty(text)) {
return null;
}
return JSONUtil.toBean(text, clazz);
}
public static <T> T parseObject(byte[] bytes, Class<T> 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> T parseObject(String text, TypeReference<T> 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> T parseObjectQuietly(String text, TypeReference<T> typeReference) {
try {
return objectMapper.readValue(text, typeReference);
} catch (IOException e) {
return null;
}
}
public static <T> List<T> parseArray(String text, Class<T> 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 <T> List<T> parseArray(String text, String path, Class<T> 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);
}
}

View File

@@ -0,0 +1 @@
com.yupi.springbootinit.config.YudaoJacksonAutoConfiguration

View File

@@ -25,7 +25,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:
# 大小限制
@@ -50,7 +55,8 @@ mybatis-plus:
logic-delete-field: isDelete # 全局逻辑删除的实体字段名
logic-delete-value: 1 # 逻辑已删除值(默认为 1
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0
# 微信相关
# 微信相关·
# Jackson 配置项
wx:
# 微信公众平台
# todo 需替换配置

View File

@@ -317,11 +317,11 @@
order by
<choose>
<!-- 传空和默认的情况下按照时间降序排序 -->
<when test="hostInfoDTO.sortName == '' and hostInfoDTO.sortName == null">
<when test="hostInfoDTO.sortName == '' or hostInfoDTO.sortName == null">
ns.create_time desc
</when>
<!-- sortNmae 有值的情况下排序 -->
<when test="hostInfoDTO.sortName != null and hostInfoDTO.sort != null ">
<when test="hostInfoDTO.sortName != null or hostInfoDTO.sort != null ">
<if test="hostInfoDTO.sortName == 'createTime' and hostInfoDTO.sort != null">
ns.create_time ${hostInfoDTO.sort}
</if>

View File

@@ -30,9 +30,30 @@
region, historic_high_coins, total_gift_coins, host_display_id, owner_id, create_time,
update_time, creator, updater, deleted, tenant_id
</sql>
<select id="selectPageByCondition" resultType="com.yupi.springbootinit.model.vo.bigbrother.ServerBigBrotherVO">
select * from
<resultMap id="BaseResultMapCopy" type="com.yupi.springbootinit.model.vo.bigbrother.ServerBigBrotherVO">
<!--@mbg.generated-->
<!--@Table server_big_brother-->
<result column="display_id" jdbcType="VARCHAR" property="displayId" />
<result column="user_id_str" jdbcType="VARCHAR" property="userIdStr" />
<result column="nickname" jdbcType="VARCHAR" property="nickname" />
<result column="level" jdbcType="INTEGER" property="level" />
<result column="hostcoins" jdbcType="INTEGER" property="hostcoins" />
<result column="follower_count" jdbcType="INTEGER" property="followerCount" />
<result column="following_count" jdbcType="INTEGER" property="followingCount" />
<result column="region" jdbcType="VARCHAR" property="region" />
<result column="historic_high_coins" jdbcType="INTEGER" property="historicHighCoins" />
<result column="total_gift_coins" jdbcType="INTEGER" property="totalGiftCoins" />
<result column="host_display_id" jdbcType="VARCHAR" property="hostDisplayId" />
<result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
</resultMap>
<select id="selectPageByCondition" resultMap="BaseResultMapCopy">
select sbr.display_id, sbr.user_id_str,sbr.nickname,sbr.level,sbr.hostcoins,
sbr.follower_count, sbr.following_count, sbr.region, sbr.historic_high_coins, sbr.total_gift_coins,
sbr.host_display_id,sbr.create_time from
server_big_brother sbr left join server_country_info ci ON sbr.region = ci.country_name
where sbr.tenant_id=#{dto.tenantId}
<!-- 大哥所属国家筛选 -->
@@ -77,11 +98,11 @@
<if test="dto.levelMin != null and dto.levelMax != null " >
and sbr.level between #{dto.levelMin,jdbcType=INTEGER} and #{dto.levelMax,jdbcType=INTEGER}
</if>
<!-- 排序类型 -->
order by
<!-- 排序类型 -->
<choose>
<!-- 传空和默认的情况下按照时间降序排序 -->
<when test="dto.sortName == '' and dto.sortName == null">
<when test="dto.sortName == '' or dto.sortName == null">
sbr.create_time desc
</when>
<!-- sortNmae 有值的情况下排序 -->