package com.huigou.data.domain.service;

import com.huigou.context.MessageSourceContext;
import com.huigou.data.domain.model.*;
import com.huigou.data.domain.query.CheckBaseInfoDuplicateParameter;
import com.huigou.data.domain.query.QueryParameter;
import com.huigou.data.query.executor.SQLExecutorDao;
import com.huigou.data.query.model.QueryDescriptor;
import com.huigou.data.repository.GeneralRepositorySuper;
import com.huigou.domain.IdentifiedEntity;
import com.huigou.exception.ApplicationException;
import com.huigou.util.StringUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.persistence.Column;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.ManagedType;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 通用领域服务
 *
 * @author gongmm
 */
public class CommonDomainService {

    private final static Logger LOG = LoggerFactory.getLogger(CommonDomainService.class);
    private static final String COMMON_XML_FILE_PATH = "config/uasp/query/bmp/common.xml";

    protected GeneralRepositorySuper generalRepository;

    protected SQLExecutorDao sqlExecutorDao;

    public GeneralRepositorySuper getGeneralRepository() {
        return generalRepository;
    }

    public void setGeneralRepository(GeneralRepositorySuper generalRepository) {
        this.generalRepository = generalRepository;
    }

    public SQLExecutorDao getSqlExecutorDao() {
        return sqlExecutorDao;
    }

    public void setSqlExecutorDao(SQLExecutorDao sqlExecutorDao) {
        this.sqlExecutorDao = sqlExecutorDao;
    }

    public String getSqlByName(String name) {
        QueryDescriptor queryDescriptor = sqlExecutorDao.getQuery(COMMON_XML_FILE_PATH, "common");
        return queryDescriptor.getSqlByName(name);
    }

    /**
     * 获取下一个排序号
     *
     * @param clazz 实体类型
     * @return 下一个排序号
     */
    @Transactional(rollbackFor = RuntimeException.class, propagation = Propagation.REQUIRES_NEW)
    public Integer getNextSequence(Class<? extends IdentifiedEntity> clazz) {
        return getNextSequence(clazz, null, null);
    }

    /**
     * 获取下一个排序号
     *
     * @param clazz             实体类型
     * @param parentIdFieldName 父ID字段名称
     * @param parentId          父ID
     * @return 下一个排序号
     */
    @Transactional(rollbackFor = RuntimeException.class, propagation = Propagation.REQUIRES_NEW)
    public Integer getNextSequence(Class<? extends IdentifiedEntity> clazz, String parentIdFieldName, String parentId) {
        CriteriaBuilder cb = generalRepository.getCriteriaBuilder();
        CriteriaQuery<Integer> query = cb.createQuery(Integer.class);
        Root<?> root = query.from(clazz);
        query.select(cb.max(root.get(CommonDomainConstants.SEQUENCE_FIELD_NAME)));
        if (!StringUtil.isBlank(parentIdFieldName)) {
            query.where(cb.equal(root.get(parentIdFieldName), parentId));
        }
        Integer result = (Integer) generalRepository.createQuery(query).getSingleResult();
        return result == null ? 1 : ++result;
    }

    /**
     * 移动
     *
     * @param clazz              实体类型
     * @param parentIdColumnName 父ID对应的数据库表列名
     * @param parentId           父ID
     * @param ids                ID列表
     */
    @Transactional(rollbackFor = RuntimeException.class)
    public void move(Class<? extends AbstractEntity> clazz, List<String> ids, String parentIdColumnName, String parentId) {
        Assert.notNull(clazz, MessageSourceContext.getMessage(MessageConstants.CLAZZ_NOT_NULL));
        Assert.notEmpty(ids, MessageSourceContext.getMessage(MessageConstants.ID_NOT_BLANK));
        Assert.hasText(parentIdColumnName, MessageSourceContext.getMessage(MessageConstants.PARENT_ID_FIELD_NAME_NOT_BLANK));
        Assert.hasText(parentId, MessageSourceContext.getMessage(MessageConstants.PARENT_ID_NOT_BLANK));
        AbstractEntity moveTo = generalRepository.findOne(clazz, parentId);
        ids.stream().map(id -> generalRepository.findOne(clazz, id))
                .filter(Objects::nonNull)
                .forEach(entity -> moveInternal(entity, parentIdColumnName, moveTo));
    }

    /**
     * 移动树形节点。
     *
     * @param node               待移动的节点
     * @param parentIdColumnName 父id对应的数据库列名
     * @param moveTo             父。
     */
    private void moveInternal(AbstractEntity node, String parentIdColumnName, AbstractEntity moveTo) {
        // 实体类的属性信息
        List<Attribute> attributes = generalRepository.getEntityManager().getMetamodel()
                .getManagedTypes()
                .stream()
                .filter(managedType -> managedType.getJavaType() == node.getClass())
                .map(ManagedType::getAttributes)
                .flatMap(Collection::stream)
                .collect(Collectors.toList());

        Field parentIdField = attributes.stream()
                .filter(attribute -> {
                    Field field = (Field) attribute.getJavaMember();
                    Column column = field.getAnnotation(Column.class);
                    String columnName = column != null && StringUtils.isNotBlank(column.name()) ? column.name() : field.getName();
                    return columnName.equals(parentIdColumnName);
                })
                .map(Attribute::getJavaMember)
                .map(Field.class::cast)
                .findAny()
                .orElseThrow(() -> new ApplicationException(String.format("根据列名%s未找到%s对应的属性", parentIdColumnName, node.getClass())));
        // 移动自己到...
        parentIdField.setAccessible(true);
        try {
            parentIdField.set(node, moveTo.getId());
        } catch (IllegalAccessException e) {
            throw new ApplicationException(String.format("设置%s出错", parentIdField.getName()), e);
        }
        // 设置fullId
        attributes.stream().filter(attribute -> attribute.getName().equals("fullId"))
                .map(Attribute::getJavaMember)
                .map(Field.class::cast)
                .findAny()
                .ifPresent(fullIdField -> {
                    try {
                        fullIdField.setAccessible(true);
                        String fullId = StringUtils.trimToEmpty((String) fullIdField.get(moveTo)) + "/" + node.getId();
                        fullIdField.set(node, fullId);
                    } catch (IllegalAccessException e) {
                        LOG.error("设置fullId出错", e);
                    }
                });
        // 设置fullName
        attributes.stream().filter(attribute -> attribute.getName().equals("fullName"))
                .map(Attribute::getJavaMember)
                .map(Field.class::cast)
                .findAny()
                .ifPresent(fullNameField -> {
                    fullNameField.setAccessible(true);
                    attributes.stream().filter(attr -> attr.getName().equals("name"))
                            .map(Attribute::getJavaMember)
                            .map(Field.class::cast)
                            .findAny()
                            .ifPresent(nameField -> {
                                nameField.setAccessible(true);
                                try {
                                    String name = (String) nameField.get(node);
                                    String fullName = StringUtils.trimToEmpty((String) fullNameField.get(moveTo)) + "/" + name;
                                    fullNameField.set(node, fullName);
                                } catch (IllegalAccessException e) {
                                    LOG.error("设置fullName出错", e);
                                }
                            });
                });

        generalRepository.save(node);

        // 递归移动子节点
        String jpql = new StringBuilder("select t from ")
                .append(node.getClass().getName())
                .append(" t where ")
                .append(parentIdField.getName())
                .append("=:parentId")
                .toString();
        generalRepository.getEntityManager().createQuery(jpql)
                .setParameter("parentId", node.getId())
                .getResultList()
                .forEach(chlid -> moveInternal((TreeEntity) chlid, parentIdColumnName, node));
    }

    /**
     * 移动树
     *
     * @param clazz              实体类型
     * @param parentIdColumnName 父ID对应的数据库列名
     * @param parentId           父ID
     * @param ids                ID列表
     */
    @SuppressWarnings("unchecked")
    public void moveForTree(Class<? extends TreeEntity> clazz, List<String> ids, String
            parentIdColumnName, String parentId) {
        Assert.notNull(clazz, MessageSourceContext.getMessage(MessageConstants.CLAZZ_NOT_NULL));
        Assert.notEmpty(ids, MessageSourceContext.getMessage(MessageConstants.ID_NOT_BLANK));
        Assert.hasText(parentIdColumnName, MessageSourceContext.getMessage(MessageConstants.PARENT_ID_FIELD_NAME_NOT_BLANK));
        Assert.hasText(parentId, MessageSourceContext.getMessage(MessageConstants.PARENT_ID_NOT_BLANK));
        //   List<TreeEntity> treeEntities = (List<TreeEntity>) generalRepository.findAll(clazz, ids);
        //    TreeEntity moveToParent = generalRepository.findOne(clazz, parentId);
        //   String parentFullId = moveToParent.getFullId();
        // 1、更新fullId 和fullName
//        for (TreeEntity chlid : treeEntities) {
//            // 校验不能循环引用
//            if (parentFullId != null) {
//                Assert.isTrue(parentFullId.indexOf(chlid.getFullId()) == -1, MessageSourceContext.getMessage(MessageConstants.UNABLE_TO_MOVE_TO, chlid.getName(), moveToParent.getName()));
//            }
//            TreeEntity oldParent = generalRepository.findOne(clazz, chlid.getParentId());
//            updateFullIdAndName(clazz, moveToParent, oldParent, chlid);
//        }
        // 2、移动
        AbstractEntity moveTo = generalRepository.findOne(clazz, parentId);
        ids.stream().map(id -> generalRepository.findOne(clazz, id))
                .filter(Objects::nonNull)
                .forEach(entity -> moveInternal(entity, parentIdColumnName, moveTo));
    }

    private void updateFullIdAndName(Class<? extends TreeEntity> clazz, TreeEntity moveToParent, TreeEntity
            oldParent, TreeEntity chlid) {
//        update %s
//          set full_id = concat(:parentNewFullId,substr(full_Id,length(:parentOldFullId) + 1,length(full_Id))),
//              full_Name = concat(:parentNewFullName,substr(full_Name, length(:parentOldFullName) + 1,length(full_Name))),
//              version = (select next_sequence('version_seq'))
//        where full_Id like :likeFullId
        String jpql = new StringBuilder("update ").append(clazz.getName())
                .append(" set fullId=concat(:parentNewFullId,substring(fullId,length(:parentOldFullId) + 1,length(fullId))),")
                .append("fullName=concat(:parentNewFullName,substring(fullName, length(:parentOldFullName) + 1,length(fullName))),")
                .append("version=:version")
                .append(" where fullId like :likeFullId")
                .toString();
        generalRepository.getEntityManager().createQuery(jpql)
                .setParameter("parentNewFullId", moveToParent.getFullId())
                .setParameter("parentOldFullId", oldParent.getFullId())
                .setParameter("parentNewFullName", moveToParent.getFullName())
                .setParameter("parentOldFullName", oldParent.getFullName())
                .setParameter("version", generalRepository.getVersionNextId())
                .setParameter("likeFullId", chlid.getFullId() + "%")
                .executeUpdate();
    }

    /**
     * 移动树
     *
     * @param clazz
     * @param ids
     * @param parentId
     */
    @SuppressWarnings("unchecked")
    public void moveTree(Class<? extends TreeEntity> clazz, List<String> ids, String parentId) {
        Assert.notNull(clazz, MessageSourceContext.getMessage(MessageConstants.CLAZZ_NOT_NULL));
        Assert.notEmpty(ids, MessageSourceContext.getMessage(MessageConstants.ID_NOT_BLANK));
        Assert.hasText(parentId, MessageSourceContext.getMessage(MessageConstants.PARENT_ID_NOT_BLANK));

        TreeEntity moveToParent = generalRepository.findOne(clazz, parentId);
        Assert.notNull(moveToParent, MessageSourceContext.getMessage(MessageConstants.OBJECT_NOT_NULL));
        String parentFullId = moveToParent.getFullId();
        List<TreeEntity> treeEntities = (List<TreeEntity>) generalRepository.findAll(clazz, ids);
//        for (TreeEntity child : treeEntities) {
//            // 校验不能循环引用
//            Assert.isTrue(parentFullId.indexOf(child.getFullId()) == -1, MessageSourceContext.getMessage(MessageConstants.UNABLE_TO_MOVE_TO, child.getName(), moveToParent.getName()));
//            TreeEntity oldParent = generalRepository.findOne(clazz, child.getParentId());
//            Assert.notNull(oldParent, MessageSourceContext.getMessage(MessageConstants.UNABLE_TO_MOVE_TO, child.getName(), moveToParent.getName()));
//            updateFullIdAndName(clazz, moveToParent, oldParent, child);
//        }
        TreeEntity moveTo = generalRepository.findOne(clazz, parentId);
        ids.stream().map(id -> generalRepository.findOne(clazz, id))
                .filter(Objects::nonNull)
                .forEach(id -> moveInternal(id, CommonDomainConstants.PARENT_ID_COLUMN_NAME, moveTo));
    }

    /**
     * 移动
     *
     * @param clazz    移动实体类型
     * @param folderId 文件夹
     * @param ids      ID列表
     */
    @Transactional
    public void moveForFolder(Class<? extends AbstractEntity> clazz, List<String> ids, String folderId) {
        Assert.notNull(clazz, MessageSourceContext.getMessage(MessageConstants.CLAZZ_NOT_NULL));
        Assert.hasText(folderId, MessageSourceContext.getMessage(MessageConstants.FOLDER_ID_NOT_BLANK));
        Assert.notEmpty(ids, MessageSourceContext.getMessage(MessageConstants.ID_NOT_BLANK));
        // update %s set folder_Id = :folderId, version = (select next_sequence('version_seq')) where id in :ids
        String jpql = new StringBuilder("update ")
                .append(clazz.getName())
                .append(" set ")
                .append(CommonDomainConstants.FOLDER_ID_FIELD_NAME)
                .append("=:folderId,version=:version where id=:id")
                .toString();
        ids.forEach(id -> generalRepository.getEntityManager().createQuery(jpql)
                .setParameter("folderId", folderId)
                .setParameter("version", generalRepository.getVersionNextId())
                .setParameter("id", id)
                .executeUpdate());
    }

    @SuppressWarnings("unchecked")
    public List<? extends IdentifiedEntity> findDuplicateEntities(Class<? extends
            IdentifiedEntity> clazz, CheckBaseInfoDuplicateParameter parameter) {
        Assert.notNull(clazz, MessageSourceContext.getMessage(MessageConstants.CLAZZ_NOT_NULL));
        Assert.notNull(parameter, MessageSourceContext.getMessage(MessageConstants.PARAMETER_NOT_NULL_FORMAT, "parameter"));
        parameter.checkConstraints();
        String jpql = String.format(this.getSqlByName(parameter.getSqlName()), clazz.getSimpleName());
        if (parameter.isTenantFilter()) {
            jpql = String.format("%s and  %s = :tenantId", jpql, parameter.getTenantFieldName());
        }
        Map<String, Object> params = parameter.getQueryParams();
        return this.generalRepository.query(jpql, params);
    }

    @Transactional(rollbackFor = RuntimeException.class)
    public void updateSequence(Class<? extends AbstractEntity> clazz, Map<String, Integer> params) {
        Assert.notNull(clazz, MessageSourceContext.getMessage(MessageConstants.CLAZZ_NOT_NULL));
        Assert.notEmpty(params, MessageSourceContext.getMessage(MessageConstants.PARAMETER_NOT_NULL_FORMAT, "params"));
        for (String id : params.keySet()) {
            Integer sequence = params.get(id);
            String jpql = new StringBuilder("update ")
                    .append(clazz.getName())
                    .append(" set ")
                    .append(CommonDomainConstants.SEQUENCE_FIELD_NAME)
                    .append("=:sequence,version=:version where id=:id")
                    .toString();
            int affectedRows = generalRepository.getEntityManager().createQuery(jpql)
                    .setParameter("sequence", sequence)
                    .setParameter("version", generalRepository.getVersionNextId())
                    .setParameter("id", id)
                    .executeUpdate();
            Assert.isTrue(affectedRows <= 1, String.format("根据主键[%s]修改排序号出错，匹配到 %d 条数据", id, affectedRows));
        }
    }

    public void updateStatus(Class<? extends AbstractEntity> clazz, List<String> ids, Integer status) {
        ids.forEach(id -> updateStatusInternal(clazz, id, status));
    }

    public void updateStatus(Class<? extends AbstractEntity> clazz, String id, Integer status) {
        updateStatusInternal(clazz, id, status);
    }

    private void updateStatusInternal(Class<? extends AbstractEntity> clazz, String id, Integer status) {
        Assert.notNull(id, MessageSourceContext.getMessage(MessageConstants.ID_NOT_BLANK));
        Assert.notNull(status, MessageSourceContext.getMessage(MessageConstants.STATUS_NOT_BLANK));
        String jpql = new StringBuilder("update ")
                .append(clazz.getName())
                .append(" set  ")
                .append(CommonDomainConstants.STATUS_FIELD_NAME)
                .append("=:status")
                .append(",version=:version")
                .append(" where ")
                .append(CommonDomainConstants.ID_FIELD_NAME)
                .append("=:id")
                .toString();
        int affectedRows = this.generalRepository.getEntityManager()
                .createQuery(jpql)
                .setParameter("status", status)
                .setParameter("version", generalRepository.getVersionNextId())
                .setParameter("id", id)
                .executeUpdate();
        Assert.isTrue(affectedRows <= 1, String.format("根据主键修改状态出错，匹配到 %d 条数据", affectedRows));
    }

    public void updateChildenFullName(Class<? extends AbstractEntity> clazz, String fullId, String
            oldFullName, String newFullName) {
        // update %s
        //   set full_Name = concat(:newFullName, substr(full_Name,length(:oldFullName) + 1,length(full_Name))),
        //       version = (select next_sequence('version_seq'))
        // where full_id like :fullId
        String jpql = new StringBuilder("update ")
                .append(clazz.getName())
                .append(" set fullName=concat(:newFullName, substring(full_Name,length(:oldFullName) + 1,length(full_Name))),version=:version")
                .append(" where  fullId like :fullId")
                .toString();
        generalRepository.getEntityManager().createQuery(jpql)
                .setParameter("newFullName", newFullName)
                .setParameter("oldFullName", oldFullName)
                .setParameter("version", generalRepository.getVersionNextId())
                .setParameter("fullId", fullId + "/%")
                .executeUpdate();
    }

    @SuppressWarnings({"rawtypes", "unchecked"})
    public BaseInfoWithFolderAbstractEntity saveBaseInfoWithFolderEntity(BaseInfoWithFolderAbstractEntity
                                                                                 entity, JpaRepository repository) {
        Assert.notNull(entity, MessageSourceContext.getMessage(MessageConstants.OBJECT_NOT_NULL));
        Assert.notNull(repository, MessageSourceContext.getMessage(MessageConstants.REPOSITORY_NOT_NULL));

        CheckBaseInfoDuplicateParameter checkParameter = new CheckBaseInfoDuplicateParameter();
        if (entity.hasTenant()) {
            checkParameter.setCheckTenantInfo(entity.getTenantField_(), entity.getTenantId_());
        }
        checkParameter.setCheckFolderIdAndGlobalCodeAndName(entity.getFolderId(), entity.getId(), entity.getCode(), entity.getName());
        checkParameter.checkConstraints();

        List<BaseInfoWithFolderAbstractEntity> duplicateEntities = (List<BaseInfoWithFolderAbstractEntity>) findDuplicateEntities(entity.getClass(),
                checkParameter);
        BaseInfoWithFolderAbstractEntity other = null;
        if (duplicateEntities.size() > 0) {
            other = duplicateEntities.get(0);
        }

        if (entity.isNew() && entity.getStatus() == null) {
            entity.setStatus(BaseInfoStatus.ENABLED.getId());
        }

        entity.checkConstraints(other);

        entity = (BaseInfoWithFolderAbstractEntity) repository.save(entity);
        return entity;
    }

    @SuppressWarnings({"rawtypes", "unchecked"})
    public FlowBillAbstractEntity saveFlowBillEntity(FlowBillAbstractEntity entity, JpaRepository repository) {
        Assert.notNull(entity, MessageSourceContext.getMessage(MessageConstants.OBJECT_NOT_NULL));
        Assert.notNull(repository, MessageSourceContext.getMessage(MessageConstants.REPOSITORY_NOT_NULL));
        if (entity.isNew()) {
            String jpql = new StringBuilder("select count(e) from ")
                    .append(entity.getClass().getName())
                    .append(" e where billCode=:billCode")
                    .toString();
            Number count = (Number) generalRepository.getEntityManager().createQuery(jpql)
                    .setParameter("billCode", entity.getBillCode())
                    .getSingleResult();
            Assert.isTrue(count.intValue() == 0, MessageSourceContext.getMessage(MessageConstants.BILLCODE_NOT_DUPLICATE));
            if (entity.getStatusId() == null) {
                entity.setStatusId(0);
            }
        }
        entity = (FlowBillAbstractEntity) repository.save(entity);
        repository.flush();
        return entity;
    }

    @SuppressWarnings({"rawtypes"})
    public TreeEntity saveTreeEntity(TreeEntity entity, JpaRepository repository) {
        return saveTreeEntity(entity, repository, true);
    }

    @SuppressWarnings({"rawtypes"})
    public TreeEntity saveTreeEntity(TreeEntity entity, JpaRepository repository, boolean useDefaultCheck) {
        return saveTreeEntity(entity, repository, useDefaultCheck, true);
    }

    // public Integer getNextSequence(String entityName, String parentIdFieldName, String parentId) {
    // Integer result = 1;
    // String sql = getSqlByName("getMaxSequenceByParentId");
    // sql = String.format(sql, entityName, parentIdFieldName);
    // Number maxSequence = (Number) this.generalRepository.single(sql, QueryParameter.buildParameters(parentIdFieldName, parentId));
    // if (maxSequence != null) {
    // result = maxSequence.intValue() + 1;
    // }
    // return result;
    // }

    @SuppressWarnings({"rawtypes", "unchecked"})
    public TreeEntity saveTreeEntity(TreeEntity entity, JpaRepository repository, boolean useDefaultCheck,
                                     boolean globalCode) {
        Assert.notNull(entity, MessageSourceContext.getMessage(MessageConstants.PARAMETER_NOT_NULL_FORMAT, "entity"));
        Assert.notNull(repository, MessageSourceContext.getMessage(MessageConstants.REPOSITORY_NOT_NULL));

        if (entity.getStatus() == null) {
            entity.setStatus(BaseInfoStatus.ENABLED.getId());
        }

        if (useDefaultCheck) {
            CheckBaseInfoDuplicateParameter checkParameter = new CheckBaseInfoDuplicateParameter();

            if (entity.hasTenant()) {
                checkParameter.setCheckTenantInfo(entity.getTenantField_(), entity.getTenantId_());
            }
            if (globalCode) {
                checkParameter.setCheckParentIdAndGlobalCodeAndName(entity.getParentId(), entity.getId(), entity.getCode(), entity.getName());
            } else {
                checkParameter.setCheckParentIdAndCodeAndName(entity.getParentId(), entity.getId(), entity.getCode(), entity.getName());
            }
            checkParameter.checkConstraints();
            List<TreeEntity> duplicateEntities = (List<TreeEntity>) findDuplicateEntities(entity.getClass(), checkParameter);

            TreeEntity other = null;
            if (duplicateEntities.size() > 0) {
                other = duplicateEntities.get(0);
            }
            entity.checkConstraints(other);
        }

        TreeEntity parent = (TreeEntity) repository.findOne(entity.getParentId());
        if (entity.getSequence() == null) {
            Integer sequence = 0;
            if (parent != null) {
                String sql = getSqlByName("getMaxSequenceByParentId");
                sql = String.format(sql, entity.getClass().getSimpleName(), CommonDomainConstants.PARENT_ID_FIELD_NAME);
                Number maxSequence = (Number) this.generalRepository.single(sql,
                        QueryParameter.buildParameters(CommonDomainConstants.PARENT_ID_FIELD_NAME,
                                entity.getParentId()));
                if (maxSequence != null) {
                    sequence = maxSequence.intValue();
                }
            }
            entity.setSequence(sequence + 1);
        }
        // 先保存实体，获取ID
        entity = (TreeEntity) repository.save(entity);
        entity.buildFullIdAndName(parent);
        entity = (TreeEntity) repository.save(entity);

        return entity;
    }

    @SuppressWarnings("rawtypes")
    public TreeEntity saveTreeEntity(TreeEntity entity, JpaRepository repository, String oldName) {
        return saveTreeEntity(entity, repository, oldName, true);
    }

    @SuppressWarnings("rawtypes")
    public TreeEntity saveTreeEntity(TreeEntity entity, JpaRepository repository, String oldName,
                                     boolean useDefaultCheck) {
        return saveTreeEntity(entity, repository, oldName, useDefaultCheck, true);
    }

    @SuppressWarnings("rawtypes")
    public TreeEntity saveTreeEntity(TreeEntity entity, JpaRepository repository, String oldName,
                                     boolean useDefaultCheck, boolean globalCode) {
        Assert.notNull(entity, MessageSourceContext.getMessage(MessageConstants.OBJECT_NOT_NULL));
        Assert.notNull(repository, MessageSourceContext.getMessage(MessageConstants.REPOSITORY_NOT_NULL));
        // Assert.hasText(oldName, "参数oldName不能为空。");

        boolean updatedName = entity.isUpdateName(oldName);
        String oldFullName = entity.getFullName();
        entity = this.saveTreeEntity(entity, repository, useDefaultCheck, globalCode);
        if (updatedName) {
            this.updateChildenFullName(entity.getClass(), entity.getFullId(), oldFullName, entity.getFullName());
        }
        return entity;
    }

    @SuppressWarnings({"rawtypes", "unchecked"})
    public BaseInfoAbstractEntity saveBaseInfoEntity(BaseInfoAbstractEntity entity, JpaRepository repository) {
        Assert.notNull(entity, MessageSourceContext.getMessage(MessageConstants.OBJECT_NOT_NULL));

        CheckBaseInfoDuplicateParameter checkParameter = new CheckBaseInfoDuplicateParameter();
        if (entity.hasTenant()) {
            checkParameter.setCheckTenantInfo(entity.getTenantField_(), entity.getTenantId_());
        }
        checkParameter.setCheckCodeAndName(entity.getId(), entity.getCode(), entity.getName());
        checkParameter.checkConstraints();
        List<BaseInfoAbstractEntity> duplicateEntities = (List<BaseInfoAbstractEntity>) this.findDuplicateEntities(entity.getClass(), checkParameter);

        BaseInfoAbstractEntity other = null;
        if (duplicateEntities.size() > 0) {
            other = duplicateEntities.get(0);
        }

        if (StringUtil.isBlank(entity.getId()) && null == entity.getStatus()) {
            entity.setStatus(BaseInfoStatus.ENABLED.getId());
        }

        entity.checkConstraints(other);

        entity = (BaseInfoAbstractEntity) repository.save(entity);
        return entity;
    }

    @SuppressWarnings("unchecked")
    public void checkBaseInfoEntity(BaseInfoAbstractEntity entity) {
        Assert.notNull(entity, MessageSourceContext.getMessage(MessageConstants.OBJECT_NOT_NULL));
        CheckBaseInfoDuplicateParameter checkParameter = new CheckBaseInfoDuplicateParameter();
        checkParameter.setCheckCodeAndName(entity.getId(), entity.getCode(), entity.getName());
        checkParameter.checkConstraints();

        List<BaseInfoAbstractEntity> duplicateEntities = (List<BaseInfoAbstractEntity>) findDuplicateEntities(entity.getClass(), checkParameter);
        BaseInfoAbstractEntity other = null;
        if (duplicateEntities.size() > 0) {
            other = duplicateEntities.get(0);
        }
        entity.checkConstraints(other);
    }

    public AbstractEntity loadAndFillinProperties(AbstractEntity entity) {
        if (entity.isNew()) {
            return entity;
        }

        AbstractEntity result = this.generalRepository.findOne(entity.getClass(), entity.getId());
        Assert.notNull(result,
                MessageSourceContext.getMessage(MessageConstants.LOAD_OBJECT_IS_NULL)
                        + MessageSourceContext.getMessage(MessageConstants.OBJECT_NOT_FOUND_BY_ID, entity.getId(), entity.getClass().getName()));
        result.fromEntity(entity);
        return result;
    }

    @SuppressWarnings("unchecked")
    public <T> T loadAndFillinProperties(AbstractEntity entity, Class<T> clazz) {
        if (entity.isNew()) {
            return (T) entity;
        }

        AbstractEntity result = this.generalRepository.findOne(entity.getClass(), entity.getId());
        Assert.notNull(result,
                MessageSourceContext.getMessage(MessageConstants.LOAD_OBJECT_IS_NULL)
                        + MessageSourceContext.getMessage(MessageConstants.OBJECT_NOT_FOUND_BY_ID, entity.getId(), entity.getClass().getName()));
        result.fromEntity(entity);
        return (T) result;
    }

    public Date getDBSystemDateTime() {
        return this.generalRepository.getDBSystemDateTime();
    }

    public String getCountByParentIdSql() {
        return getSqlByName("countByParentId");
    }
}
