Spring boot中实现字典管理

数据库脚本

CREATE TABLE `data_dict` (
  `id` bigint NOT NULL COMMENT '主键',
  `dict_code` varchar(32) DEFAULT NULL COMMENT '字典编码',
  `dict_name` varchar(64) DEFAULT NULL COMMENT '字典名称',
  `dict_description` varchar(255) DEFAULT NULL COMMENT '字典描述',
  `dict_status` tinyint DEFAULT NULL COMMENT '字典状态;0:禁用;1:启用',
  `created_by` varchar(32) DEFAULT NULL COMMENT '创建人;姓名[域账号]',
  `created_time` datetime DEFAULT NULL COMMENT '创建时间',
  `updated_by` varchar(32) DEFAULT NULL COMMENT '更新人;姓名[域账号]',
  `updated_time` datetime DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='数据字典';

INSERT INTO data_dict (id, dict_code, dict_name, dict_description, dict_status, created_by, created_time, updated_by, updated_time) VALUES(1889143437849174016, 'test', 'test', 'test', 1, 'xx', '2025-02-11 10:44:29', 'xx', '2025-02-11 10:44:29');
INSERT INTO data_dict (id, dict_code, dict_name, dict_description, dict_status, created_by, created_time, updated_by, updated_time) VALUES(1889143437849174017, 'test2', 'test2', 'test2', 1, 'xx', '2025-02-11 10:44:29', 'xx', '2025-02-11 10:44:29');



CREATE TABLE `data_dict_item` (
  `id` bigint NOT NULL COMMENT '主键',
  `dict_code` varchar(32) DEFAULT NULL COMMENT '字典编码',
  `item_code` varchar(32) DEFAULT NULL COMMENT '条目编码',
  `item_value` varchar(255) DEFAULT NULL COMMENT '条目值',
  `item_description` varchar(255) DEFAULT NULL COMMENT '条目描述',
  `item_status` tinyint DEFAULT NULL COMMENT '条目状态;0:禁用;1:启用',
  `seq` int DEFAULT NULL COMMENT '序号',
  `created_by` varchar(32) DEFAULT NULL COMMENT '创建人;姓名[域账号]',
  `created_time` datetime DEFAULT NULL COMMENT '创建时间',
  `updated_by` varchar(32) DEFAULT NULL COMMENT '更新人;姓名[域账号]',
  `updated_time` datetime DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='数据字典条目';

INSERT INTO data_dict_item (id, dict_code, item_code, item_value, item_description, item_status, seq, created_by, created_time, updated_by, updated_time) VALUES(1889143437849174016, 'test', 'xxx', 'xx', 'xxx', 1, 1, 'xx', '2025-02-11 10:44:29', 'xx', '2025-02-11 10:44:29');
INSERT INTO data_dict_item (id, dict_code, item_code, item_value, item_description, item_status, seq, created_by, created_time, updated_by, updated_time) VALUES(1889143437849174017, 'test2', '111', '222', 'xxx', 1, 1, 'xx', '2025-02-11 10:44:29', 'xx', '2025-02-11 10:44:29');
INSERT INTO data_dict_item (id, dict_code, item_code, item_value, item_description, item_status, seq, created_by, created_time, updated_by, updated_time) VALUES(1889143437849174018, 'test2', '33', '44', 'xxx', 1, 1, 'xx', '2025-02-11 10:44:29', 'xx', '2025-02-11 10:44:29');

代码

package com.demo.dto.sys;

import lombok.Data;
import lombok.experimental.Accessors;

import java.io.Serial;
import java.io.Serializable;

/**
 * 字典缓存
 *
 * @since 2025/02/11 上午 10:39
 */
@Data
@Accessors(chain = true)
public class DataDictItemCache implements Serializable {

    @Serial
    private static final long serialVersionUID = -2394718872868472043L;

    /**
     * 字典编码
     */
    private String dictCode;
    /**
     * 条目编码
     */
    private String itemCode;
    /**
     * 条目值
     */
    private String itemValue;
    /**
     * 条目状态;0:禁用;1:启用
     */
    private Integer itemStatus;
}

package com.demo.annotation;

import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.demo.config.DictSerializer;

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

/**
 * 数据字典注解
 *
 * @since 2025/02/08 下午 01:41
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = DictSerializer.class)
public @interface Dict {

    /**
     * 字典编码
     */
    String dictCode();
}

// 使用方式如下

// @Data
// public class Test {
//
//     @Dict(dictCode = "xxx")
//     private String itemCode;
//
//     private String other;
// }

// {
//     "code": 200,
//     "message": "操作成功",
//     "result": {
//         "itemCode": {
//             "dictCode": "xxx",
//             "itemCode": "111",
//             "itemValue": "222",
//             "itemStatus": 1
//         },
//         "other": null
//     },
//     "timestamp": "1739245075643"
// }
package com.demo.config;

import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector;
import com.demo.annotation.Dict;
import org.springframework.stereotype.Component;

import java.io.Serial;

/**
 * 字典注解序列化拦截器
 *
 * @since 2025/02/08 下午 02:20
 */
@Component
public class DictAnnotationIntrospector extends NopAnnotationIntrospector {

    @Serial
    private static final long serialVersionUID = 5139608634773791712L;

    @Override
    public Object findSerializer(Annotated am) {
        Dict dict = am.getAnnotation(Dict.class);
        if (dict != null) {
            return DictSerializer.class;
        }
        return null;
    }
}
package com.demo.config;

import com.fasterxml.jackson.databind.AnnotationIntrospector;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 修改被@Dict注解标注的属性字典序列化方式
 *
 * @since 2025/02/08 下午 02:22
 */
@Configuration
public class DictSerializerConfig {

    @Autowired
    private DictSensitiveAnnotationIntrospector dictSensitiveAnnotationIntrospector;

    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
        return builder -> {
            // JacksonAnnotationIntrospector:这是 Jackson 默认的注解解析器,用于处理 Jackson 自带的注解(如 @JsonProperty)。

            // AnnotationIntrospector.pair:这个方法允许你组合两个注解解析器,使得 Jackson 在解析注解时会依次检查这两个解析器。

            // 通过这种方式,你可以确保 @JsonProperty 等 Jackson 标准注解仍然生效,
            // 同时也能使用自定义的 DictSensitiveAnnotationIntrospector 来处理特定的注解逻辑。

            // 组合使用多个注解解析器
            AnnotationIntrospector primary = new JacksonAnnotationIntrospector();
            AnnotationIntrospector secondary = dictSensitiveAnnotationIntrospector;
            AnnotationIntrospector pair = AnnotationIntrospector.pair(primary, secondary);
            builder.annotationIntrospector(pair);
        };
    }
}
package com.demo.config;

import cn.hutool.extra.spring.SpringUtil;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import com.demo.annotation.Dict;
import com.demo.service.sys.DataDictItemService;
import com.demo.dto.sys.DataDictItemCache;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.io.Serial;
import java.util.List;

/**
 * 数据字典自定序列化类
 *
 * @since 2025/02/08 下午 02:18
 */
public class DictSerializer extends StdSerializer<Object> implements ContextualSerializer {

    @Serial
    private static final long serialVersionUID = -3774620761859983477L;

    /**
     * 字典编码
     */
    private final String dictCode;

    public DictSerializer() {
        super(Object.class);
        this.dictCode = null;
    }

    public DictSerializer(String dictCode) {
        super(Object.class);
        this.dictCode = dictCode;
    }

    @Override
    public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) {
        Dict annotation = property.getAnnotation(Dict.class);
        if (annotation != null) {
            return new DictSerializer(annotation.dictCode());
        }
        return this;
    }

    @Override
    public void serialize(Object itemCode, JsonGenerator gen, SerializerProvider provider) throws IOException {
        DataDictItemCache itemCache = null;

        if (StringUtils.isNotBlank(dictCode) && ObjectUtils.isNotEmpty(itemCode)) {
            DataDictItemService service = SpringUtil.getBean(DataDictItemService.class);
            List<DataDictItemCache> allItemCache = service.getDictItemCacheByDictCode(dictCode);
            if (!CollectionUtils.isEmpty(allItemCache)) {
                itemCache = allItemCache.stream()
                        .filter(item -> item.getItemCode().equals(itemCode))
                        .findFirst()
                        .orElse(null);
            }
        }

        gen.writeObject(itemCache);
    }
}
Spring Boot中,我们可以使用枚举类型来实现字典管理,这样可以提高代码的可维护性和可读性。下面介绍一种优雅的实现字典管理的方式。 1. 定义字典枚举类 首先,我们需要定义一个枚举类来表示字典,例如: ```java public enum DictionaryEnum { GENDER("gender", "性别"), EDUCATION("education", "学历"), // ... private String code; private String name; DictionaryEnum(String code, String name) { this.code = code; this.name = name; } public String getCode() { return code; } public String getName() { return name; } } ``` 其中,code表示字典项的编码,name表示字典项的名称。可以根据实际情况添加更多的字典项。 2. 定义字典项枚举类 接下来,我们需要定义每个字典项的枚举类,例如: ```java public enum GenderEnum { MALE("male", "男"), FEMALE("female", "女"); private String code; private String name; GenderEnum(String code, String name) { this.code = code; this.name = name; } public String getCode() { return code; } public String getName() { return name; } } ``` 其中,code表示字典项的编码,name表示字典项的名称。可以根据实际情况添加更多的字典项。 3. 定义字典项服务类 接下来,我们需要定义一个字典项服务类,用于获取字典项列表,例如: ```java @Service public class DictionaryService { public List<DictionaryItemVO> getDictionaryItems(DictionaryEnum dictionaryEnum) { List<DictionaryItemVO> items = new ArrayList<>(); switch (dictionaryEnum) { case GENDER: for (GenderEnum gender : GenderEnum.values()) { items.add(new DictionaryItemVO(gender.getCode(), gender.getName())); } break; case EDUCATION: // ... break; // ... } return items; } } ``` 其中,getDictionaryItems方法根据传入的字典枚举类获取对应的字典项列表,并转换为一个包含code和name的字典项视图对象(DictionaryItemVO)列表。 4. 定义字典项视图对象类 字典项视图对象类(DictionaryItemVO)是一个包含code和name的简单JavaBean,例如: ```java public class DictionaryItemVO { private String code; private String name; public DictionaryItemVO(String code, String name) { this.code = code; this.name = name; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getName() { return name; } public void setName(String name) { this.name = name; } } ``` 5. 在Controller中使用字典项服务类 最后,在Controller中使用字典项服务类获取字典项列表,例如: ```java @RestController @RequestMapping("/user") public class UserController { @Autowired private DictionaryService dictionaryService; @GetMapping("/gender") public List<DictionaryItemVO> getGenderDictionaryItems() { return dictionaryService.getDictionaryItems(DictionaryEnum.GENDER); } // ... } ``` 这样,我们就可以优雅地实现字典管理,提高代码的可维护性和可读性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值