package com.book.common.utils;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.book.common.function.CurrentFunction;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.*;
import java.math.BigDecimal;
import java.util.*;

/**
 * Description:
 * <p>
 * 通过JSON字符串实现
 * 对象转Map
 * Map转对象
 * </p>
 *
 * @Author: leo.xiong
 * @CreateDate: 2022/12/7 11:56
 * @Email: leo.xiong@suyun360.com
 * @Since:
 */
@Slf4j
public class BeanCopyUtil {
    private static final Logger LOGGER = LoggerFactory.getLogger(BeanCopyUtil.class);
    /**
     * 根据类缓存某个属性是否直接复制
     */
    private static final Map<Class, Map<String, FieldCanCopy>> FIELD_COPY_CACHE = Maps.newConcurrentMap();

    /**
     * 直接赋值
     */
    public static final int DIRECT_ASSIGNMENT = 1;
    /**
     * 转换赋值
     */
    public static final int TRANSFORM_ASSIGNMENT = 2;

    public static String toJSONString(List<Map> mapList) {
        return JSONObject.toJSONString(mapList);
    }

    public static String toJSONString(Map map) {
        return JSONObject.toJSONString(map);
    }

    /**
     * List<Bean>转List<Map>
     *
     * @param objectList
     * @return
     */
    public static List<Map> beanToMapList(List<Object> objectList) {
        if (CollectionUtils.isEmpty(objectList)) {
            return Collections.EMPTY_LIST;
        }
        List<Map> list = Lists.newArrayListWithExpectedSize(objectList.size());
        for (Object obj : objectList) {
            list.add(beanToMap(obj));
        }
        return list;
    }

    /**
     * Bean转Map
     *
     * @param object
     * @return
     * @throws IllegalAccessException
     */
    public static Map beanToMap(Object object) {
        if (object == null) {
            return null;
        }
        Map<String, String> map = Maps.newHashMap();
        Field[] fields = object.getClass().getDeclaredFields();
        for (Field field : fields) {
            boolean isAccessible = field.isAccessible();
            try {
                field.setAccessible(true);
                Object value = field.get(object);
                if (value == null) {
                    map.put(field.getName(), null);
                } else if (value instanceof Date) {
                    //时间都转为年月日时分秒,使用是可以具体处理
                    map.put(field.getName(), DatetimeUtil.formatDate((Date) value, DatetimeUtil.YYYY_MM_DD_HH_MM_SS_L_C));
                } else if (value.getClass().isArray() || value instanceof Collection || value instanceof Map) {
                    //集合数组类型先转为JSON字符串,注意循环引用问题,默认使用$rel对重复和递归引用,反序列化获取不到对应的值
                    map.put(field.getName(), JSONObject.toJSONString(value, SerializerFeature.DisableCircularReferenceDetect));
                } else {
                    map.put(field.getName(), String.valueOf(value));
                }
            } catch (IllegalAccessException e) {
                LOGGER.warn("属性获取失败 type: {} name: {}", field.getType(), field.getName(), e);
                map.put(field.getName(), null);
            } finally {
                field.setAccessible(isAccessible);
            }
        }
        return map;
    }

    /**
     * List<Map>转List<Bean>
     *
     * @param nameValueMapList
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> List<T> mapToBeanList(List<Map> nameValueMapList, Class<T> clazz) {
        if (CollectionUtils.isEmpty(nameValueMapList)) {
            return Collections.EMPTY_LIST;
        }
        List<T> valueList = Lists.newArrayListWithExpectedSize(nameValueMapList.size());
        for (Map nameValueMap : nameValueMapList) {
            valueList.add(mapToBean(nameValueMap, clazz));
        }
        return valueList;
    }

    /**
     * Map转Bean
     *
     * @param nameValueMap
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T mapToBean(Map nameValueMap, Class<T> clazz) {
        if (CollectionUtils.isEmpty(nameValueMap) || clazz == null) {
            return null;
        }
        T t = null;
        try {
            t = clazz.getDeclaredConstructor().newInstance();
        } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
            e.printStackTrace();
            return null;
        }
        Field[] fields = clazz.getDeclaredFields();
        Method[] methods = clazz.getDeclaredMethods();
        Map<String, Method> methodNameMethodMap = Maps.newHashMapWithExpectedSize(methods.length);
        for (Method method : methods) {
            methodNameMethodMap.put(method.getName().toLowerCase(), method);
        }
        for (Field field : fields) {
            Class<?> type = field.getType();
            String propertyName = field.getName();
            String value = (String) nameValueMap.get(propertyName);
            if (StringUtils.isEmpty(value)) {
                continue;
            }
            Method method = methodNameMethodMap.get("set" + propertyName.toLowerCase());
            if (method == null) {
                continue;
            }
            boolean isAccessible = method.isAccessible();
            try {
                method.setAccessible(true);
                if (isBasicClass(type)) {
                    method.invoke(t, getCommonDataTypeValue(type, value));
                } else if (type.isArray()) {
                    Class<?> childrenType = getCommonDataType(type);
                    if (childrenType == null) {
                        //数组里对象是自定义对象不做处理,可以引入Function处理
                        continue;
                    }
                    JSONArray jsonArray = JSONObject.parseArray(value);
                    method.invoke(t, buildArray(childrenType, jsonArray));
                } else if (Collection.class.isAssignableFrom(type) || Map.class.isAssignableFrom(type)) {
                    try {
                        Map values = JSONObject.parseObject(value, Map.class);
                        method.invoke(t, values);
                        continue;
                    } catch (Exception e) {
                    }
                    Type[] childrenType = method.getGenericParameterTypes();
                    ParameterizedType pType = (ParameterizedType) childrenType[0];
                    Object objValue = null;
                    try {
                        objValue = Class.forName(pType.getActualTypeArguments()[0].getTypeName()).newInstance();
                    } catch (Exception e) {
                    }
                    //获取泛型属性
                    if (objValue == null) {
                        continue;
                    }
                    List valuesList = JSONObject.parseArray(value, objValue.getClass());
                    if (pType.getRawType().toString().endsWith("List")) {
                        method.invoke(t, valuesList);
                    } else if (pType.getRawType().toString().endsWith("Set")) {
                        method.invoke(t, Sets.newHashSet(valuesList));
                    }
                } else {
                    method.invoke(t, mapToBean(nameValueMap, clazz));
                }
            } catch (Exception e) {
                LOGGER.warn("属性设置失败 type: {} name: {}", field.getType(), field.getName(), e);
            } finally {
                method.setAccessible(isAccessible);
            }
        }
        return t;
    }

    private static Object getCommonDataTypeValue(Class<?> type, String value) {
        if (type == Boolean.class || type.getName().toLowerCase().contains(Boolean.class.getSimpleName().toLowerCase())) {
            return Boolean.valueOf(value);
        } else if (type == Byte.class || type.getName().toLowerCase().contains(Byte.class.getSimpleName().toLowerCase())) {
            return Byte.valueOf(value);
        } else if (type == Short.class || type.getName().toLowerCase().contains(Short.class.getSimpleName().toLowerCase())) {
            return Short.valueOf(value);
        } else if (type == Integer.class || type.getName().toLowerCase().contains(int.class.getSimpleName().toLowerCase())) {
            return Integer.valueOf(value);
        } else if (type == Long.class || type.getName().toLowerCase().contains(Long.class.getSimpleName().toLowerCase())) {
            return Long.valueOf(value);
        } else if (type == Float.class || type.getName().toLowerCase().contains(Float.class.getSimpleName().toLowerCase())) {
            return Float.valueOf(value);
        } else if (type == Double.class || type.getName().toLowerCase().contains(Double.class.getSimpleName().toLowerCase())) {
            return Double.valueOf(value);
        } else if (type == Character.class || type.getName().toLowerCase().contains(char.class.getSimpleName().toLowerCase())) {
            return value.toCharArray()[0];
        } else if (type == String.class || type.getName().toLowerCase().contains(String.class.getSimpleName().toLowerCase())) {
            return value;
        } else if (type == BigDecimal.class || type.getName().toLowerCase().contains(BigDecimal.class.getSimpleName().toLowerCase())) {
            return new BigDecimal(value);
        } else if (type == Date.class || type.getName().toLowerCase().contains(Date.class.getSimpleName().toLowerCase())) {
            try {
                return DatetimeUtil.changeToDate(value);
            } catch (Exception e) {
                log.warn("时间格式化不对 value: {}", value);
            }
        }
        return null;
    }

    private static Class<?> getCommonDataType(Class<?> type) {
        return getCommonDataType(type, type.getName());
    }

    private static Class<?> getCommonDataType(Class<?> type, String className) {
        if (type == Boolean.class || className.toLowerCase().contains(Boolean.class.getSimpleName().toLowerCase())) {
            return Boolean.class;
        } else if (type == Byte.class || className.toLowerCase().contains(Byte.class.getSimpleName().toLowerCase())) {
            return Byte.class;
        } else if (type == Short.class || className.toLowerCase().contains(Short.class.getSimpleName().toLowerCase())) {
            return Short.class;
        } else if (type == Integer.class || className.toLowerCase().contains(int.class.getSimpleName().toLowerCase())) {
            return Integer.class;
        } else if (type == Long.class || className.toLowerCase().contains(Long.class.getSimpleName().toLowerCase())) {
            return Long.class;
        } else if (type == Float.class || className.toLowerCase().contains(Float.class.getSimpleName().toLowerCase())) {
            return Float.class;
        } else if (type == Double.class || className.toLowerCase().contains(Double.class.getSimpleName().toLowerCase())) {
            return Double.class;
        } else if (type == Character.class || className.toLowerCase().contains(char.class.getSimpleName().toLowerCase())) {
            return Character.class;
        } else if (type == String.class || className.toLowerCase().contains(String.class.getSimpleName().toLowerCase())) {
            return String.class;
        } else if (type == BigDecimal.class || className.toLowerCase().contains(BigDecimal.class.getSimpleName().toLowerCase())) {
            return BigDecimal.class;
        } else if (type == Date.class || className.toLowerCase().contains(Date.class.getSimpleName().toLowerCase())) {
            return Date.class;
        }
        return null;
    }

    private static Object buildArray(Class<?> type, JSONArray jsonArray) {
        if (type == Boolean.class || type.getName().toLowerCase().contains(Boolean.class.getSimpleName().toLowerCase())) {
            Boolean[] booleans = new Boolean[jsonArray.size()];
            for (int i = 0; i < jsonArray.size(); i++) {
                booleans[i] = (Boolean) getCommonDataTypeValue(type, jsonArray.get(i).toString());
            }
            return booleans;
        } else if (type == Byte.class || type.getName().toLowerCase().contains(Byte.class.getSimpleName().toLowerCase())) {
            Byte[] bytes = new Byte[jsonArray.size()];
            for (int i = 0; i < jsonArray.size(); i++) {
                bytes[i] = (Byte) getCommonDataTypeValue(type, jsonArray.get(i).toString());
            }
            return bytes;
        } else if (type == Short.class || type.getName().toLowerCase().contains(Short.class.getSimpleName().toLowerCase())) {
            Short[] shorts = new Short[jsonArray.size()];
            for (int i = 0; i < jsonArray.size(); i++) {
                shorts[i] = (Short) getCommonDataTypeValue(type, jsonArray.get(i).toString());
            }
            return shorts;
        } else if (type == Integer.class || type.getName().toLowerCase().contains(int.class.getSimpleName().toLowerCase())) {
            Integer[] integers = new Integer[jsonArray.size()];
            for (int i = 0; i < jsonArray.size(); i++) {
                integers[i] = (Integer) getCommonDataTypeValue(type, jsonArray.get(i).toString());
            }
            return integers;
        } else if (type == Long.class || type.getName().toLowerCase().contains(Long.class.getSimpleName().toLowerCase())) {
            Long[] longs = new Long[jsonArray.size()];
            for (int i = 0; i < jsonArray.size(); i++) {
                longs[i] = (Long) getCommonDataTypeValue(type, jsonArray.get(i).toString());
            }
            return longs;
        } else if (type == Float.class || type.getName().toLowerCase().contains(Float.class.getSimpleName().toLowerCase())) {
            Float[] floats = new Float[jsonArray.size()];
            for (int i = 0; i < jsonArray.size(); i++) {
                floats[i] = (Float) getCommonDataTypeValue(type, jsonArray.get(i).toString());
            }
            return floats;
        } else if (type == Double.class || type.getName().toLowerCase().contains(Double.class.getSimpleName().toLowerCase())) {
            Double[] doubles = new Double[jsonArray.size()];
            for (int i = 0; i < jsonArray.size(); i++) {
                doubles[i] = (Double) getCommonDataTypeValue(type, jsonArray.get(i).toString());
            }
            return doubles;
        } else if (type == Character.class || type.getName().toLowerCase().contains(char.class.getSimpleName().toLowerCase())) {
            Character[] characters = new Character[jsonArray.size()];
            for (int i = 0; i < jsonArray.size(); i++) {
                characters[i] = (Character) getCommonDataTypeValue(type, jsonArray.get(i).toString());
            }
            return characters;
        } else if (type == String.class || type.getName().toLowerCase().contains(String.class.getSimpleName().toLowerCase())) {
            String[] strings = new String[jsonArray.size()];
            for (int i = 0; i < jsonArray.size(); i++) {
                strings[i] = (String) getCommonDataTypeValue(type, jsonArray.get(i).toString());
            }
            return strings;
        } else if (type == BigDecimal.class || type.getName().toLowerCase().contains(BigDecimal.class.getSimpleName().toLowerCase())) {
            BigDecimal[] bigDecimals = new BigDecimal[jsonArray.size()];
            for (int i = 0; i < jsonArray.size(); i++) {
                bigDecimals[i] = (BigDecimal) getCommonDataTypeValue(type, jsonArray.get(i).toString());
            }
            return bigDecimals;
        } else if (type == Date.class || type.getName().toLowerCase().contains(Date.class.getSimpleName().toLowerCase())) {
            Date[] dates = new Date[jsonArray.size()];
            for (int i = 0; i < jsonArray.size(); i++) {
                dates[i] = (Date) getCommonDataTypeValue(type, jsonArray.get(i).toString());
            }
            return dates;
        }
        return null;
    }

    /**
     * 判断是否是基础数据类型,即 int,double,long等类似格式
     */
    public static boolean isCommonDataType(Class clazz) {
        return clazz.isPrimitive();
    }

    /**
     * 判断是否是基础数据类型的包装类型
     *
     * @param clz
     * @return
     */
    public static boolean isWrapClass(Class clz) {
        try {
            return ((Class) clz.getField("TYPE").get(null)).isPrimitive();
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 是否基础数据类型
     *
     * @param type
     * @return
     */
    private static boolean isBasicClass(Class type) {
        return isWrapClass(type)
                || isCommonDataType(type)
                || BigDecimal.class.isAssignableFrom(type)
                || Date.class.isAssignableFrom(type)
                || String.class.isAssignableFrom(type);
    }

    /**
     * 多次复制的对象
     * PropertyUtils会缓存属性信息
     *
     * @param sourceList
     * @param targetClass
     * @param <T>
     * @return
     */
    public static <T, D> List<T> copyListProperties(List<D> sourceList, Class<T> targetClass) {
        return copyListProperties(sourceList, targetClass, null);
    }

    /**
     * 多次复制的对象
     * PropertyUtils会缓存属性信息
     *
     * @param sourceList
     * @param targetClass
     * @param currentFunction
     * @param <T>
     * @return
     */
    public static <T, D> List<T> copyListProperties(List<D> sourceList, Class<T> targetClass, CurrentFunction<Object, T> currentFunction) {
        if (CollectionUtils.isEmpty(sourceList)) {
            return Lists.newArrayList();
        }
        List<T> valueList = Lists.newArrayListWithExpectedSize(sourceList.size());
        for (Object source : sourceList) {
            T value = copyProperties(source, targetClass, currentFunction);
            if (value == null) {
                continue;
            }
            valueList.add(value);
        }
        return valueList;
    }

    /**
     * 多次复制的对象
     * PropertyUtils会缓存属性信息
     *
     * @param source           原始数据
     * @param targetClass      返回的对象类型
     * @param ignoreProperties 忽略的属性
     * @param <T>
     * @return
     */
    public static <T> T copyProperties(Object source, Class<T> targetClass, String... ignoreProperties) {
        return copyProperties(source, targetClass, null, ignoreProperties);
    }

    /**
     * 多次复制的对象
     * 缓存属性信息,便于下次直接赋值
     *
     * @param source           原始数据
     * @param targetClass      返回的对象类型
     * @param currentFunction  自定义实现
     * @param ignoreProperties 忽略的属性
     * @param <T>
     * @return
     */
    public static <T> T copyProperties(Object source, Class<T> targetClass, CurrentFunction<Object, T> currentFunction, String... ignoreProperties) {
        if (source == null) {
            return null;
        }
        T targetObj = null;
        try {
            targetObj = (T) targetClass.getDeclaredConstructor().newInstance();
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            log.warn("创建对象 {} 失败", targetClass.getName(), e);
            return null;
        }
        List<String> ignorePropertiesList = ignoreProperties == null || ignoreProperties.length <= 0 ? null : Lists.newArrayList(ignoreProperties);
        PropertyDescriptor[] targetPropertyDesc = PropertyUtils.getPropertyDescriptors(targetObj);
        Map<String, FieldCanCopy> fieldCanCopyMap = FIELD_COPY_CACHE.get(targetClass);
        Field[] sourceFields = null, targetFields = null;
        if (fieldCanCopyMap == null) {
            sourceFields = source.getClass().getDeclaredFields();
            targetFields = targetClass.getDeclaredFields();
            synchronized (targetClass) {
                fieldCanCopyMap = FIELD_COPY_CACHE.get(targetClass);
                if (fieldCanCopyMap == null) {
                    fieldCanCopyMap = Maps.newConcurrentMap();
                    FIELD_COPY_CACHE.put(targetClass, fieldCanCopyMap);
                }
            }
        }
        Class targetPropertyType = null, sourcePropertyType = null;
        try {
            for (int i = 0; i < targetPropertyDesc.length; i++) {
                if (ignorePropertiesList != null && ignorePropertiesList.contains(targetPropertyDesc[i].getName())) {
                    continue;
                }
                targetPropertyType = targetPropertyDesc[i].getPropertyType();
                boolean isCacheFill = isCacheFill(source, targetObj, targetPropertyType, fieldCanCopyMap, targetPropertyDesc[i].getName());
                if (isCacheFill) {
                    continue;
                }
                if (targetPropertyDesc[i].getName().equalsIgnoreCase(Class.class.getSimpleName())) {
                    fieldCanCopyMap.put(targetPropertyDesc[i].getName(), new FieldCanCopy(false));
                    continue;
                }
                if (PropertyUtils.getWriteMethod(targetPropertyDesc[i]) == null) {
                    fieldCanCopyMap.put(targetPropertyDesc[i].getName(), new FieldCanCopy(false));
                    continue;
                }
                sourcePropertyType = PropertyUtils.getPropertyType(source, targetPropertyDesc[i].getName());
                Object value = null;
                if (sourcePropertyType != null) {
                    value = PropertyUtils.getProperty(source, targetPropertyDesc[i].getName());
                    if (value == null) {
                        continue;
                    }
                    //如果根据名称能取到值
                    if (targetPropertyType.equals(sourcePropertyType)) {
                        boolean isCollection = targetPropertyType.getClass().isArray() || value instanceof Collection || value instanceof Map;
                        if (!isCollection) {
                            PropertyUtils.setProperty(targetObj, targetPropertyDesc[i].getName(), value);
                            fieldCanCopyMap.put(targetPropertyDesc[i].getName(), new FieldCanCopy(true, DIRECT_ASSIGNMENT));
                        } else {
                            if (ObjectUtils.isEmpty(value)) {
                                continue;
                            }
                            Field field = getFieldByName(targetFields, targetPropertyDesc[i].getName());
                            Type[] types = ((ParameterizedType) field.getGenericType()).getActualTypeArguments();
                            if (types == null || types.length > 2) {
                                fieldCanCopyMap.put(targetPropertyDesc[i].getName(), new FieldCanCopy(false));
                                continue;
                            }
                            if (targetPropertyType.getClass().isArray()) {
                                if (types.length != 1) {
                                    fieldCanCopyMap.put(targetPropertyDesc[i].getName(), new FieldCanCopy(false));
                                    continue;
                                }
                                Class genericType = getCommonDataType(types[0].getClass(), types[0].getTypeName());
                                if (getCommonDataType(((Object[]) value).getClass()).equals(genericType)) {
                                    PropertyUtils.setProperty(targetObj, targetPropertyDesc[i].getName(), value);
                                    fieldCanCopyMap.put(targetPropertyDesc[i].getName(), new FieldCanCopy(true, DIRECT_ASSIGNMENT));
                                } else {
                                    fieldCanCopyMap.put(targetPropertyDesc[i].getName(), new FieldCanCopy(false));
                                    log.info("属性名称相同,类型为数组,但数组里面的子元素不是基本类型或类型不同,不做处理 name: {} targetPropertyType: {} sourcePropertyType: {}", targetPropertyDesc[i].getName(), targetPropertyType, sourcePropertyType);
                                }
                            } else if (value instanceof Collection) {
                                if (types.length != 1) {
                                    fieldCanCopyMap.put(targetPropertyDesc[i].getName(), new FieldCanCopy(false));
                                    continue;
                                }
                                Class genericType = getCommonDataType(types[0].getClass(), types[0].getTypeName());
                                if (getCommonDataType(((Collection) value).iterator().next().getClass()).equals(genericType)) {
                                    PropertyUtils.setProperty(targetObj, targetPropertyDesc[i].getName(), value);
                                    fieldCanCopyMap.put(targetPropertyDesc[i].getName(), new FieldCanCopy(true, DIRECT_ASSIGNMENT));
                                } else {
                                    fieldCanCopyMap.put(targetPropertyDesc[i].getName(), new FieldCanCopy(false));
                                    log.info("属性名称相同,类型为集合,但集合里面的属性获取失败,不做处理 name: {} targetPropertyType: {} sourcePropertyType: {}", targetPropertyDesc[i].getName(), targetPropertyType, sourcePropertyType);
                                }
                            } else if (value instanceof Map) {
                                if (types.length != 2) {
                                    fieldCanCopyMap.put(targetPropertyDesc[i].getName(), new FieldCanCopy(false));
                                    continue;
                                }
                                Class genericKeyType = getCommonDataType(types[0].getClass(), types[0].getTypeName());
                                Class genericValueType = getCommonDataType(types[1].getClass(), types[1].getTypeName());
                                Map<?, ?> keyValueMap = (Map) value;
                                boolean isKeyValueTypeSameFlag = false;
                                for (Map.Entry<?, ?> entry : keyValueMap.entrySet()) {
                                    isKeyValueTypeSameFlag = getCommonDataType(entry.getKey().getClass()).equals(genericKeyType) && getCommonDataType(entry.getValue().getClass()).equals(genericValueType);
                                    break;
                                }
                                if (isKeyValueTypeSameFlag) {
                                    PropertyUtils.setProperty(targetObj, targetPropertyDesc[i].getName(), value);
                                    fieldCanCopyMap.put(targetPropertyDesc[i].getName(), new FieldCanCopy(true, DIRECT_ASSIGNMENT));
                                } else {
                                    fieldCanCopyMap.put(targetPropertyDesc[i].getName(), new FieldCanCopy(false));
                                    log.info("属性名称相同,类型为Map,但Map的Key和Value存在不同的类型,不做处理 name: {} targetPropertyType: {} sourcePropertyType: {}", targetPropertyDesc[i].getName(), targetPropertyType, sourcePropertyType);
                                }
                            }
                        }
                    } else if (isBasicClass(targetPropertyType) && isBasicClass(sourcePropertyType)) {
                        //如果类型不同,且类型都是基本类型,尝试转换
                        sameNameDiffType(value, targetObj, targetPropertyDesc[i].getName(), targetPropertyType);
                        fieldCanCopyMap.put(targetPropertyDesc[i].getName(), new FieldCanCopy(true, TRANSFORM_ASSIGNMENT));
                    } else {
                        fieldCanCopyMap.put(targetPropertyDesc[i].getName(), new FieldCanCopy(false));
                        log.info("属性名称相同,但属性类型不同,且不是可以转换的类型,不做处理 name: {} targetPropertyType: {} sourcePropertyType: {}", targetPropertyDesc[i].getName(), targetPropertyType, sourcePropertyType);
                    }
                    continue;
                }
                Field field = getFieldByName(sourceFields, targetPropertyDesc[i].getName());
                if (field == null) {
                    fieldCanCopyMap.put(targetPropertyDesc[i].getName(), new FieldCanCopy(false));
                    continue;
                }
                value = PropertyUtils.getProperty(source, field.getName());
                if (value == null) {
                    continue;
                }
                sourcePropertyType = field.getType();
                if (isSimilarClass(sourcePropertyType.getName(), targetPropertyType.getName())) {
                    //如果是dto/vo/entity三者转换,递归调用赋值
                    PropertyUtils.setProperty(targetObj, targetPropertyDesc[i].getName(), copyProperties(value, targetPropertyType));
                    fieldCanCopyMap.put(targetPropertyDesc[i].getName(), new FieldCanCopy(true, field.getName(), DIRECT_ASSIGNMENT));
                    continue;
                }
                //判断两者是不是都是基本数据类型,如果一个不是,则不能进行赋值,否则转换赋值
                boolean isNotBasicType = !isBasicClass(targetPropertyType) || !isBasicClass(sourcePropertyType);
                if (isNotBasicType) {
                    fieldCanCopyMap.put(targetPropertyDesc[i].getName(), new FieldCanCopy(false));
                    continue;
                }
                sameNameDiffType(value, targetObj, targetPropertyDesc[i].getName(), targetPropertyType);
                fieldCanCopyMap.put(targetPropertyDesc[i].getName(), new FieldCanCopy(true, field.getName(), TRANSFORM_ASSIGNMENT));
            }
            if (currentFunction != null) {
                targetObj = currentFunction.apply(source, targetObj);
            }
        } catch (Exception e) {
            log.warn("复制属性错误 source:{} class: {}", JSONObject.toJSONString(source), targetClass, e);
        }
        return targetObj;
    }

    private static <T> void sameNameDiffType(Object value, T targetObj, String propertyName, Class targetPropertyType) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        String valueStr = null;
        if (value instanceof Date) {
            valueStr = DatetimeUtil.formatDate((Date) value, DatetimeUtil.YYYY_MM_DD_HH_MM_SS_L_C);
        } else if (value instanceof BigDecimal) {
            valueStr = ((BigDecimal) value).toPlainString();
        } else {
            valueStr = String.valueOf(value);
        }
        PropertyUtils.setProperty(targetObj, propertyName, getCommonDataTypeValue(targetPropertyType, valueStr));
    }

    /**
     * 是否是项目中存在的 dto/vo/entity 三者之间的转换
     *
     * @param sourceClass
     * @param targetClass
     * @return
     */
    private static boolean isSimilarClass(String sourceClass, String targetClass) {
        String[] sourceClassSplits = getClassStr(sourceClass);
        String[] targetClassSplits = getClassStr(targetClass);
        if (sourceClassSplits == null || targetClassSplits == null) {
            return false;
        }
        return sourceClassSplits[sourceClassSplits.length - 1].equalsIgnoreCase(targetClassSplits[targetClassSplits.length - 1]);
    }

    /**
     * 根据属性名判断是否相同
     *
     * @param fields
     * @param className
     * @return
     */
    private static Field getFieldByName(Field[] fields, String className) {
        String[] typeNames = getClassStr(className);
        if (typeNames == null) {
            return null;
        }
        String typeName = typeNames[typeNames.length - 1];
        String fieldName = null;
        boolean isSame = false;
        for (Field field : fields) {
            fieldName = field.getName();
            if ("serialVersionUID".equalsIgnoreCase(fieldName)) {
                continue;
            }
            isSame = fieldName.equalsIgnoreCase(typeName) || fieldName.equalsIgnoreCase(typeName + "vo") || fieldName.equalsIgnoreCase(typeName + "dto");
            if (isSame) {
                return field;
            }
        }
        return null;
    }

    /**
     * 自定义vo,dto为同一个对象信息
     *
     * @param className
     * @return
     */
    private static String[] getClassStr(String className) {
        if (StringUtils.isEmpty(className)) {
            return null;
        }
        String[] sourceClassSplits = className.split("\\.", -1);
        String sourceName = sourceClassSplits[sourceClassSplits.length - 1];
        if (sourceName.toLowerCase().endsWith("dto")) {
            sourceName = sourceName.substring(0, sourceName.length() - 3);
        } else if (sourceName.toLowerCase().endsWith("vo")) {
            sourceName = sourceName.substring(0, sourceName.length() - 2);
        }
        sourceClassSplits[sourceClassSplits.length - 1] = sourceName;
        return sourceClassSplits;
    }

    /**
     * 是否缓存直接填充值,如果是,则直接填充值,否则分析是否填充值
     *
     * @param source
     * @param targetObj
     * @param targetPropertyType
     * @param fieldCanCopyMap
     * @param targetPropertyDescName
     * @param <T>
     * @return
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     * @throws NoSuchMethodException
     */
    private static <T> boolean isCacheFill(Object source, T targetObj, Class targetPropertyType, Map<String, FieldCanCopy> fieldCanCopyMap, String targetPropertyDescName) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        FieldCanCopy fieldCanCopy = fieldCanCopyMap.get(targetPropertyDescName);
        if (fieldCanCopy == null) {
            return false;
        }
        Object value = null;
        if (fieldCanCopy != null && fieldCanCopy.isCanCopy()) {
            if (StringUtils.isNotEmpty(fieldCanCopy.getFieldName())) {
                value = PropertyUtils.getProperty(source, fieldCanCopy.getFieldName());
            } else {
                value = PropertyUtils.getProperty(source, targetPropertyDescName);
            }
            if (value == null) {
                return true;
            }
            switch (fieldCanCopy.getFillValue()) {
                case DIRECT_ASSIGNMENT:
                    PropertyUtils.setProperty(targetObj, targetPropertyDescName, value);
                    break;
                case TRANSFORM_ASSIGNMENT:
                    sameNameDiffType(value, targetObj, targetPropertyDescName, targetPropertyType);
                    break;
                default:
                    break;
            }
        }
        return true;
    }

    /**
     * 是否能复制
     */
    @Data
    static class FieldCanCopy {
        /**
         * 默认不能复制
         */
        private boolean canCopy = false;
        /**
         * 复制属性名称
         */
        private String fieldName;
        /**
         * 填充值方式
         * 1、PropertyUtils.setProperty(targetObj, targetPropertyDesc[i].getName(), value);
         * 2、sameNameDiffType(value, targetObj, targetPropertyDesc[i].getName(), targetPropertyType);
         */
        private int fillValue;

        public FieldCanCopy(boolean canCopy) {
            this.canCopy = canCopy;
        }

        public FieldCanCopy(boolean canCopy, int fillValue) {
            this.canCopy = canCopy;
            this.fillValue = fillValue;
        }

        public FieldCanCopy(boolean canCopy, String fieldName, int fillValue) {
            this.canCopy = canCopy;
            this.fieldName = fieldName;
            this.fillValue = fillValue;
        }
    }
}

注意:数组,Map,List,Set(List和Set可以是对象,数组和Map只是基本类型)

java fastjson Map 转换为对象_java


java fastjson Map 转换为对象_java_02


使用缓存,第一次复制耗时340ms,第二次0ms,查询List转换时可以提高性能

java fastjson Map 转换为对象_json_03