java使用自定义注解配合Jackson实现动态输出花样时间

前言

在项目中经常越到需要将时间字段转化为诸如:几年前、几月前、几天前、几小时前、分钟前等等的花样时间,如果每次都因为一个字段从数据库取出后还需要进行一次数据处理这样对于开发来说是很不友好的,本可以直接取数据返回,现因一个字段要加一层处理,实在有些繁琐,这时就需要这样一个插件来为您解决开发处理层的问题。
本文章命名为花样时间处理,实现了通过自定义注解把时间字段处理为花样时间然后返回前端。期间对于开发者来说就是在字段上加入注解,并根据需要添加规则参数,之后便可不用理会,Jackson底层序列化时会自动进行处理。

相关技术

Jackson、Annotation
常用于Spring项目中,因为Spring跟Jackson是天然结合的

编码

花样时间级别枚举

该级别用于定义花样时间的最高级,超过级别则使用具体时间戳

package cn.rc100.common.core.time;

import lombok.AllArgsConstructor;

/**
 * <p>
 * 花样时间级别
 * </p>
 *
 * @author Tophua
 * @since 2020/8/26
 */
@AllArgsConstructor
public enum TricksTimeLevelEnum {
    /**
     * 分钟
     */
    MINUTE("分"),
    /**
     * 小时
     */
    HOUR("小时"),
    /**
     * 天
     */
    DAY("天"),
    /**
     * 月
     */
    MONTH("月"),
    /**
     * 年
     */
    YEAR("年");
    /**
     * 级别名称
     */
    private final String name;
}

注解

package cn.rc100.common.core.time;

import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * <p>
 * 花样时间注解
 * 带注解的字段类型必须是LocalDateTime
 * </p>
 *
 * @author Tophua
 * @since 2020/8/26
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@JacksonAnnotationsInside
@JsonSerialize(using = TricksTimeSerialize.class)
public @interface TricksTime {
    /**
     * 花样时间级别,超过这个级别返回具体时间
     */
    TricksTimeLevelEnum level() default TricksTimeLevelEnum.HOUR;
    /**
     * 花样时间级别对应最大值,超过这个最大值返回具体时间
     */
    int maxValue() default 24;
}

核心处理

package cn.rc100.common.core.time;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;

import java.io.IOException;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Objects;

/**
 * <p>
 * 花样时间序列化
 * </p>
 *
 * @author Tophua
 * @since 2020/8/26
 */
@NoArgsConstructor
@AllArgsConstructor
public class TricksTimeSerialize extends JsonSerializer<LocalDateTime> implements ContextualSerializer {
    private TricksTimeLevelEnum level;
    private int maxValue;

    @Override
    public void serialize(LocalDateTime time, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        jsonGenerator.writeObject(time);
        String newField = jsonGenerator.getOutputContext().getCurrentName() + "TricksTime";
        LocalDateTime now = LocalDateTime.now();
        int ordinal = level.ordinal();
        long year = time.until(now, ChronoUnit.YEARS);
        if (ordinal == TricksTimeLevelEnum.YEAR.ordinal() && year > 0) {
            if (year >= maxValue) {
                jsonGenerator.writeObjectField(newField, time);
                return;
            }
            jsonGenerator.writeObjectField(newField, year + "年前");
            return;
        }
        long month = time.until(now, ChronoUnit.MONTHS);
        if (ordinal >= TricksTimeLevelEnum.MONTH.ordinal() && month > 0) {
            if (level == TricksTimeLevelEnum.MONTH && month >= maxValue) {
                jsonGenerator.writeObjectField(newField, time);
                return;
            }
            jsonGenerator.writeObjectField(newField, month + "月前");
            return;
        }
        long day = time.until(now, ChronoUnit.DAYS);
        if (ordinal >= TricksTimeLevelEnum.DAY.ordinal() && day > 0) {
            if (level == TricksTimeLevelEnum.DAY && day >= maxValue) {
                jsonGenerator.writeObjectField(newField, time);
                return;
            }
            jsonGenerator.writeObjectField(newField, day + "天前");
            return;
        }
        long hour = time.until(now, ChronoUnit.HOURS);
        if (ordinal >= TricksTimeLevelEnum.HOUR.ordinal() && hour > 0) {
            if (level == TricksTimeLevelEnum.HOUR && hour >= maxValue) {
                jsonGenerator.writeObjectField(newField, time);
                return;
            }
            jsonGenerator.writeObjectField(newField, hour + "小时前");
            return;
        }
        long minute = time.until(now, ChronoUnit.MINUTES);
        if (ordinal >= TricksTimeLevelEnum.MINUTE.ordinal() && minute > 0) {
            if (level == TricksTimeLevelEnum.MINUTE && minute >= maxValue) {
                jsonGenerator.writeObjectField(newField, time);
                return;
            }
            jsonGenerator.writeObjectField(newField, minute >= 5 ? minute + "分钟前" : "刚刚");
            return;
        }
        jsonGenerator.writeObjectField(newField, time);
    }

    @Override
    public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
        if (beanProperty != null) {
            if (Objects.equals(beanProperty.getType().getRawClass(), LocalDateTime.class)) {
                TricksTime tricksTime = beanProperty.getAnnotation(TricksTime.class);
                if (tricksTime == null) {
                    tricksTime = beanProperty.getContextAnnotation(TricksTime.class);
                }
                if (tricksTime != null) {
                    return new TricksTimeSerialize(tricksTime.level(), tricksTime.maxValue());
                }
            }
            return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
        }
        return serializerProvider.findNullValueSerializer(null);
    }

}

结果

测试时间 2020-08-27 15:01:37

{
            "id": 1,
            "sn": "BGYZT00001",
            "createTime": "2020-08-27 01:30:46",
            "createTimeTricksTime": "13小时前",
            "updateTime": "2020-08-27 14:21:45",
            "tenantId": 1
        }

总结

实际运用中可以根据此思路完成各种类似的需求,减少重复枣轮子。

我是Tophua
虽然写不出逆天的代码,但是每行代码都是规范的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值