侧边栏壁纸
博主头像
人生短短几个秋

行动起来,活在当下

  • 累计撰写 45 篇文章
  • 累计创建 20 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

优雅的枚举处理方案:代码转换与验证

人生短短几个秋
2025-04-12 / 0 评论 / 0 点赞 / 6 阅读 / 0 字

🌟 优雅的枚举处理方案:代码转换与验证

🎯 核心功能

这套工具提供两大核心能力:

  1. 枚举代码转名称:将存储的枚举代码转换为友好的展示名称
  2. 枚举值验证:确保传入的值在枚举定义范围内

💎 核心组件

1. 枚举转换注解 @EnumCodeToName

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = EnumCodeToNameSerializer.class)
public @interface EnumCodeToName {
    Class<? extends Enum<?>> enumType();  // 枚举类型
    String codeField() default "code";    // 代码字段名
    String nameField() default "chineseName"; // 名称字段名
    boolean useDefaultIfNotMatched() default true; // 是否使用默认值
}

2. 枚举验证注解 @EnumValid

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = EnumValidator.class)
public @interface EnumValid {
    String message() default "枚举值非法";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
    Class<? extends Enum<?>> enumType();  // 枚举类型
    String codeField() default "code";    // 代码字段名
    boolean nullable() default false;     // 是否允许null
}

3. 枚举序列化器 EnumCodeToNameSerializer

public class EnumCodeToNameSerializer extends JsonSerializer<String> {
    @Override
    public void serialize(String code, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        if (code == null) {
            gen.writeNull();
            return;
        }

        try {
            Object currentObj = gen.getCurrentValue();
            String fieldName = gen.getOutputContext().getCurrentName();
            Field field = currentObj.getClass().getDeclaredField(fieldName);
            
            EnumCodeToName annotation = field.getAnnotation(EnumCodeToName.class);
            if (annotation == null) {
                gen.writeString(code);
                return;
            }

            Object result = getValueNameByCode(
                annotation.enumType(), 
                code.trim(),
                annotation.codeField(),
                annotation.nameField(),
                annotation.useDefaultIfNotMatched()
            );
            
            gen.writeString(result != null ? result.toString() : code);
            
        } catch (NoSuchFieldException e) {
            throw new IOException("序列化失败: 字段不存在", e);
        } catch (Exception e) {
            throw new IOException("枚举序列化失败: " + e.getMessage(), e);
        }
    }

    private Object getValueNameByCode(Class<? extends Enum<?>> enumClass, String code,
                                    String codeField, String nameField, boolean useDefault) {
        try {
            // 查找匹配的枚举值
            for (Enum<?> enumConstant : enumClass.getEnumConstants()) {
                Field codeF = enumClass.getDeclaredField(codeField);
                codeF.setAccessible(true);
                if (code.equals(String.valueOf(codeF.get(enumConstant)))) {
                    Field nameF = enumClass.getDeclaredField(nameField);
                    nameF.setAccessible(true);
                    return nameF.get(enumConstant);
                }
            }

            // 使用默认值
            if (useDefault) {
                Method getDefault = enumClass.getMethod("getDefault");
                Enum<?> defaultEnum = (Enum<?>) getDefault.invoke(null);
                Field nameF = enumClass.getDeclaredField(nameField);
                nameF.setAccessible(true);
                return nameF.get(defaultEnum);
            }
        } catch (Exception ignored) {}
        return null;
    }
}

4. 枚举验证器 EnumValidator

public class EnumValidator implements ConstraintValidator<EnumValid, String> {
    private Class<? extends Enum<?>> enumType;
    private String codeField;
    private boolean nullable;

    @Override
    public void initialize(EnumValid constraintAnnotation) {
        this.enumType = constraintAnnotation.enumType();
        this.codeField = constraintAnnotation.codeField();
        this.nullable = constraintAnnotation.nullable();
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (value == null) return nullable;
        
        try {
            String trimmed = value.trim();
            for (Enum<?> enumConstant : enumType.getEnumConstants()) {
                Field field = enumType.getDeclaredField(codeField);
                field.setAccessible(true);
                if (trimmed.equals(String.valueOf(field.get(enumConstant)))) {
                    return true;
                }
            }
            
            context.disableDefaultConstraintViolation();
            context.buildConstraintViolationWithTemplate(
                "无效的枚举值: " + value + ",可选值: " + getValidValues()
            ).addConstraintViolation();
            
        } catch (Exception e) {
            context.disableDefaultConstraintViolation();
            context.buildConstraintViolationWithTemplate(
                "验证枚举值时发生系统错误"
            ).addConstraintViolation();
        }
        return false;
    }
    
    private String getValidValues() {
        try {
            return Arrays.stream(enumType.getEnumConstants())
                .map(e -> {
                    try {
                        Field f = enumType.getDeclaredField(codeField);
                        f.setAccessible(true);
                        return String.valueOf(f.get(e));
                    } catch (Exception ex) {
                        return "";
                    }
                })
                .filter(s -> !s.isEmpty())
                .collect(Collectors.joining(", "));
        } catch (Exception e) {
            return "无法获取有效值列表";
        }
    }
}

🚀 使用示例

1. 定义枚举

public enum OrderStatus {
    CREATED("C", "已创建"),
    PAID("P", "已支付"),
    SHIPPED("S", "已发货"),
    COMPLETED("F", "已完成");
    
    private final String code;
    private final String desc;
    
    OrderStatus(String code, String desc) {
        this.code = code;
        this.desc = desc;
    }
    
    public static OrderStatus getDefault() {
        return CREATED;
    }
}

2. 在DTO中使用

public class OrderDTO {
    @EnumValid(
        enumType = OrderStatus.class,
        message = "无效的订单状态"
    )
    private String statusCode;
    
    @EnumCodeToName(
        enumType = OrderStatus.class,
        codeField = "code",
        nameField = "desc"
    )
    private String statusDisplay;
    
    // getters/setters
}

3. 控制器示例

@RestController
@RequestMapping("/orders")
public class OrderController {
    
    @PostMapping
    public ResponseEntity<?> createOrder(@Valid @RequestBody OrderDTO orderDTO) {
        // 业务逻辑
        return ResponseEntity.ok(orderDTO);
    }
}

4. 效果展示

请求:

{
    "statusCode": "P",
    "statusDisplay": "P"
}

响应:

{
    "statusCode": "P",
    "statusDisplay": "已支付"
}

错误示例:

{
    "statusCode": "X",
    "statusDisplay": "X"
}

错误响应:

{
    "timestamp": "2023-08-20T10:00:00",
    "status": 400,
    "error": "Bad Request",
    "message": "无效的订单状态: X,可选值: C, P, S, F",
    "path": "/orders"
}

🌈 特性总结

  1. 自动转换:存储代码,展示友好名称
  2. 严格验证:确保数据合法性
  3. 灵活配置:可自定义字段名
  4. 容错处理:找不到匹配时使用默认值
  5. 友好提示:验证失败时显示有效值列表

这套工具让枚举处理变得简单而强大!🎉

0

评论区