字典项数据自动装配

基于【切面 + 注解 + 反射】实现字典项自动装配

一、存储装配

需求描述:
数据传输时使用明文字典,入库时存入对应字典项标识
如: 居民身份证 -> 01E0DBDC-E3D3-45DB-B23B-B76590478534

  • 定义注解 DictionaryMatching 标记实体属性
package com.xxx.modules.crypt.dictionary;

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

/**
 * @program: talent-interface-system
 * @description: 字典匹配注解 支持动态标记字典表,字段名
 * @author: orange
 * @create: 2023-08-28 10:33
 * @version: 1.0
 */
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DictionaryMatching {

    /**
     提示信息
     * @return: java.lang.String
     * @decription 提示信息
     * @date 2023/8/29 10:09
    */
    String message();
    /**
     * @return: java.lang.String
     * @decription 条件
     * @date 2023/8/28 16:34
     */
    String condition() default "";
    /**
     * @return: java.lang.String
     * @decription 列名
     * @date 2023/8/28 16:34
     */
    String columnName() default "";
    /**
     * @return: java.lang.String
     * @decription 对应的数据表名
     * @date 2023/8/28 16:34
     */
    String tableName() default "";
} 
  • 定义注解 QueryDictionaryMatching 标记调用接口
package com.xxx.modules.crypt.dictionary;

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

/**
 * @program: talent-interface-system
 * @description: 字典查询签注解
 * @author: orange
 * @create: 2023-08-28 10:59
 * @version: 1.0
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface QueryDictionaryMatching {
    int index() default 0;
}
  • 定义切面 DictionaryAspect
package com.xxx.modules.crypt.dictionary;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xxx.efpx.common.core.util.R;
import com.xxx.modules.talent.service.SysZdxService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;


/**
 * @program: talent-interface-system
 * @description: 字典项切面
 * @author: orange
 * @create: 2023-08-28 10:48
 * @version: 1.0
 */
@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class DictionaryAspect {

    private final SysZdxService sysZdxService;

    @Pointcut(value = "@annotation(com.xxx.modules.crypt.dictionary.QueryDictionaryMatching)")
    public void cutPoint() {
    }

    @Around(value = "cutPoint() && @annotation(param)")
    public Object around(ProceedingJoinPoint joinPoint, QueryDictionaryMatching param) throws Throwable {
        log.info("进入切面执行----------------------------》");
        // @QueryDictionaryMatching 注解定义传参索引
        Object arguments = joinPoint.getArgs()[param.index()];
        log.info("获取的参数是:{}", arguments);
        ObjectMapper mapper = new ObjectMapper();
        String json ="{}";
        try {
            json = mapper.writeValueAsString(arguments);
            JSONObject jsonObject = JSONObject.parseObject(json);
            StringBuilder builder = new StringBuilder();
            for (Field field : arguments.getClass().getDeclaredFields()){
                if (field.getAnnotation(DictionaryMatching.class) != null) {
                    String message = field.getAnnotation(DictionaryMatching.class).message();
                    String condition = field.getAnnotation(DictionaryMatching.class).condition();
                    String column = field.getAnnotation(DictionaryMatching.class).columnName();
                    String table = field.getAnnotation(DictionaryMatching.class).tableName();
                    String key = String.valueOf(jsonObject.get(field.getName()));
                    String dictId = translateDictValue(condition, column, table, key);
                    if (null == dictId) {
                        builder.append("【").append(message).append("】:").append("存在字典项不匹配错误;");
                    }
                    field.setAccessible(true);
                    field.set(arguments,dictId);
                }
            }
            if (builder.toString().length() > 0){
                return R.failed(builder.toString());
            }
        } catch (JsonProcessingException e){
            e.printStackTrace();
        }
        log.info("继续调用方法本体执行----------------------------》");
        return joinPoint.proceed();
    }


    /**
     * @param condition 查询条件
     * @param column 显示列
     * @param table  查询表
     * @param key 需要转义的字段
     * @return: java.lang.String
     * @decription 查询字典项id
     * @date 2023/8/28 17:04
     */
    private String translateDictValue(String condition, String column, String table, String key){
        return sysZdxService.queryDict(condition, column, table, key);
    }
}
  • 字典匹配业务代码
package com.xxx.modules.talent.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.xxx.modules.crypt.dictionary.DictionaryEcho;
import com.xxx.modules.talent.entity.SysZdxEntity;
import org.apache.ibatis.reflection.MetaObject;

import java.util.List;

/**
 *  字典项业务层
 * @author orange
 * @date 2023-06-25 13:55:48
 */
public interface SysZdxService extends IService<SysZdxEntity> {

    /**
     * @param condition 查询条件
     * @param column 显示列
     * @param table  查询表
     * @param key 需要转义的字段
     * @return: java.lang.String
     * @decription 查询字典项id
     * @date 2023/8/28 18:39
     */
    String queryDict(String condition, String column, String table, String key);

    /**
     * @param type 类型
     * @decription 查询字典项数据
     * @date 2023/8/28 18:39
     */
	List<SysZdxEntity> selectDictData(String type);
 }
package com.xxx.modules.talent.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xxx.modules.crypt.dictionary.DictionaryEcho;
import com.xxx.modules.system.entity.SysDictItem;
import com.xxx.modules.system.service.ISysDictItemService;
import com.xxx.modules.talent.entity.SysZdxEntity;
import com.xxx.modules.talent.mapper.SysZdxMapper;
import com.xxx.modules.talent.service.SysZdxService;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @program: talent-interface-system
 * @description: 描述
 * @author: orange
 * @create: 2023-08-28 13:46
 * @version: 1.0
 */
@Service
public class SysZdxServiceImpl extends ServiceImpl<SysZdxMapper, SysZdxEntity> implements SysZdxService {

    @Autowired
    private SysZdxMapper sysZdxMapper;


    @Override
    public String queryDict(String condition, String column, String table, String key) {
        return sysZdxMapper.queryDictTable(condition, column, table, key);
    }
    
	@Override
    @Cacheable(value = "dictCache", key = "'type:'+#type")
    public List<SysZdxEntity> selectDictData(String type) {
        return sysZdxMapper.selectDictData(type);
    }


package com.xxx.modules.talent.mapper;

import com.xxx.core.datascope.EfpBaseMapper;
import com.xxx.modules.talent.entity.AppClient;
import com.xxx.modules.talent.entity.SysZdxEntity;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * 字典项
 *
 * @author orange
 * @date 2023-06-25 13:55:48
 */
@Mapper
public interface SysZdxMapper extends EfpBaseMapper<SysZdxEntity> {

    String queryDictTable(@Param("condition") String condition,
                          @Param("column") String column,
                          @Param("table") String table,
                          @Param("key") String key);
                         
    List<SysZdxEntity> selectDictData(@Param("type")String type);
                       
 }
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xxx.modules.talent.mapper.SysZdxMapper">
	<!--支持动态指定需要查询的字典表-->
    <select id="queryDictTable" resultType="java.lang.String">
        select
            <choose>
                <when test="column != '' and column != null">
                    ${column}
                </when>
                <otherwise>
                    id
                </otherwise>
            </choose>
        from
            <choose>
                <when test="table != '' and table != null">
                    ${table}
                </when>
                <otherwise>
                    sys_zdx
                </otherwise>
            </choose>
        <where>
            <choose>
                <when test="condition != '' and condition != null">
                    ${condition} = #{key}
                </when>
                <otherwise>
                    `name` = #{key}
                </otherwise>
            </choose>
            and del_flag = '1'
        </where>
        limit 1
    </select>


    <select id="selectDictData" resultType="com.xxx.modules.talent.entity.SysZdxEntity">
        select
            *
        from
            sys_zdx
        <where>
           <if test="type != null and type != ''">
               description like CONCAT("%",#{type},"%")
           </if>
        </where>
    </select>
</mapper>
  • 实体类标记注解 xxxVO
 @DictionaryEcho(target = "idType",type = "证件类型")
 @DictionaryMatching(message = "证件类型")
 private String idType;
  • 接口类使用 xxxController
/**
 * 新增  xxxVO 
 * @param  基本情况
  * @return R
 */    
@PostMapping
@QueryDictionaryMatching //标记需要实现字典项装配的接口
@ApiOperation(value = "基本信息保存", notes = "基本信息保存")
@Log(title = "基本信息新增", businessType = BusinessType.INSERT)
public R save(@RequestBody xxxVO xxxVO) {
	//....略
} 

二、 回显装配

需求描述:
字典数据查询回显时,显示对应的明文字典项名称
如: 01E0DBDC-E3D3-45DB-B23B-B76590478534 -> 居民身份证

  • 定义注解 DictionaryEcho 标记需要匹配的属性值
package com.xxx.modules.crypt.dictionary;

import java.lang.annotation.*;

/**
 * @program: talent-interface-system
 * @description: 字典数据回显
 * @author: orange
 * @create: 2023-08-30 10:59
 * @version: 1.0
 */
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
public @interface DictionaryEcho {

    /**
     * @return: java.lang.String
     * @decription 类型
     * @date 2023/8/28 16:34
     */
    String type() default "";

    String target();

}
  • 定义拦截器 DictionInterceptor
package com.xxx.modules.crypt.dictionary;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.xxx.modules.mybatis.annotation.FieldDict;
import com.xxx.modules.talent.service.SysZdxService;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.ReflectorFactory;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;

/**
 * @program: talent-interface-system
 * @description: 字典项数据处理拦截器
 * @author: orange
 * @create: 2023-08-30 16:56
 * @version: 1.0
 */
@Slf4j
@Intercepts({@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})})
@Component
public class DictionInterceptor implements Interceptor {


    @Autowired
    private DataBind dataBind;
    private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();

    private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();

    private static final ReflectorFactory REFLECTOR_FACTORY = new DefaultReflectorFactory();

    @Override
    @SuppressWarnings("unchecked")
    public Object intercept(Invocation invocation) throws Throwable {
        Object returnValue = invocation.proceed();
        // 对结果进行处理
        if (returnValue instanceof ArrayList<?>) {
            List<?> list = (ArrayList<?>) returnValue;
            if (CollUtil.isNotEmpty(list)) {
                Class<?> clazz = list.get(0).getClass();
                //获取所有有该注解的成员变量
                Field[] declaredFields = clazz.getDeclaredFields();
                list.forEach(obj -> {
                    MetaObject metaObject = MetaObject.forObject(obj, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, REFLECTOR_FACTORY);
                    Arrays.stream(declaredFields).forEach(field -> {
                        DictionaryEcho annotation = field.getAnnotation(DictionaryEcho.class);
                        if (ObjectUtil.isNotEmpty(annotation)) {
                            Object fieldValue = metaObject.getValue(field.getName());
                            dataBind.setMetaObject(annotation, fieldValue, metaObject);
                        }
                    });
                });
            }
        }
        return returnValue;
    }
}
  • 业务代码
package com.xxx.modules.crypt.dictionary;

import org.apache.ibatis.reflection.MetaObject;

/**
 * @Author orange
 * @Date 2023/8/11 14:59
 * @Description: 绑定业务
 * @Version 1.0
 */

public interface DataBind {

    void setMetaObject(DictionaryEcho annotation, Object fieldValue, MetaObject metaObject);
}
package com.xxx.modules.crypt.dictionary;

import com.xxx.modules.mybatis.annotation.FieldDict;
import com.xxx.modules.mybatis.databind.IDictDataBind;
import com.xxx.modules.system.entity.SysDictItem;
import com.xxx.modules.system.service.ISysDictItemService;
import com.xxx.modules.talent.entity.SysZdxEntity;
import com.xxx.modules.talent.service.SysZdxService;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Component
public class DataBindImpl implements DataBind {

    @Resource
    ApplicationContext applicationContext;


    /**
     * 设置元数据对象<br>
     * 根据源对象映射绑定指定属性(自行处理缓存逻辑)
     *
     * @param dictionaryEcho  数据绑定注解
     * @param fieldValue 属性值
     * @param metaObject 元数据对象 {@link MetaObject}
     * @return
     */
    @Override
    public void setMetaObject(DictionaryEcho dictionaryEcho, Object fieldValue, MetaObject metaObject) {
        SysZdxService sysZdxService = applicationContext.getBean(SysZdxService.class);
        List<SysZdxEntity> sysZdxEntityList = sysZdxService.selectDictData(dictionaryEcho.type());
        //找到相应的数据
        Map<String, String> dictMap = sysZdxEntityList.stream().collect(Collectors.toMap(it -> it.getId(),it -> it.getName()));
        // 赋值
        metaObject.setValue(dictionaryEcho.target(), dictMap.get(String.valueOf(fieldValue)));
    }
}
  • 实体属性标记、DAO层查询如上述代码所示
  • 附录sys_zdx表设计

CREATE TABLE sys_zdx (
id char(64) COLLATE utf8_bin NOT NULL COMMENT ‘编号’,
zdid char(64) COLLATE utf8_bin NOT NULL COMMENT ‘字典’,
name varchar(1000) COLLATE utf8_bin DEFAULT NULL COMMENT ‘字典名称’,
value varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT ‘数据值’,
type varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT ‘类型’,
description varchar(1000) COLLATE utf8_bin DEFAULT NULL COMMENT ‘描述’,
sort decimal(10,0) DEFAULT NULL COMMENT ‘排序(升序)’,
parent_id varchar(64) COLLATE utf8_bin DEFAULT ‘0’ COMMENT ‘父级编号’,
remarks varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT ‘备注信息’,
del_flag char(1) COLLATE utf8_bin DEFAULT ‘0’ COMMENT ‘删除标记’,
PRIMARY KEY (id) USING BTREE,
KEY zdid (zdid) USING BTREE,
KEY parent_id (parent_id) USING BTREE,
KEY id (id) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT=‘字典表’;

总结

前者基于基础的切面来实现字典项匹配入库,通过注解来标记执行的方法。
后者基于Mybatis中的拦截器Interceptor
两者均可解决相似的业务需求。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值