package com.yanzuoguang.dao.impl;

import com.yanzuoguang.dao.BaseDao;
import com.yanzuoguang.dao.DaoConst;
import com.yanzuoguang.dao.QueryPara;
import com.yanzuoguang.db.DbExecute;
import com.yanzuoguang.util.base.ObjectHelper;
import com.yanzuoguang.util.exception.CodeException;
import com.yanzuoguang.util.helper.DateHelper;
import com.yanzuoguang.util.helper.StringHelper;
import com.yanzuoguang.util.vo.*;

import java.util.*;

/**
 * 数据库操作的基本工具类
 * 1. 实现了基本的增删该查
 * 2. 实现了统计的增肌和修改
 * 3. 实现了一定功能的基本验证,验证数据是否存在
 * 4. 数据主键的获取等功能。
 * 5. 获取自增等功能
 *
 * @author 颜佐光
 */
public abstract class BaseDaoImpl extends BaseDaoSql implements BaseDao {

    /**
     * 构造函数
     */
    public BaseDaoImpl() {
    }

    public BaseDaoImpl(DbExecute db, TableSqlCache table) {
        super(db, table);
    }

    /**
     * 获取当前主键
     *
     * @return
     */
    private String getIdentity() {
        return StringHelper.toString(this.getDb().queryCell(this.getClass(), "GET_KEY", "SELECT @@IDENTITY"));
    }

    /**
     * 获取主键名称
     *
     * @return 获取主键名称
     */
    protected String getKey() {
        if (this.table == null) {
            throw new RuntimeException("类" + this.getClass().getName() + "未发现表结构");
        }
        return this.table.getTable().getKeyName();
    }

    /**
     * 获取主键值
     *
     * @param model 需要获取主键的实体
     * @return
     */
    protected String getKeyString(Object model) {
        String keyField = this.getKey();
        Object key = ObjectHelper.get(model, keyField);
        if (StringHelper.isEmpty(key)) {
            return "";
        }
        String keyString = key.toString();
        if (DaoConst.ZERO.equals(keyString)) {
            keyString = "";
        }
        return keyString;
    }

    /**
     * 设置主键值
     *
     * @param model 需要设置的实体
     * @param key   需要设置的主键值
     */
    protected void setKeyString(Object model, String key) {
        String keyField = this.getKey();
        ObjectHelper.set(model, keyField, key);
    }

    /**
     * 根据输入参数来获取主键
     *
     * @param from 可以为实体或字符串。为实体时必须包含 主键 字段 或者 id 字段。
     * @return
     */
    protected String getInputKey(Object from) {
        String key;
        if (from != null && from.getClass() == String.class) {
            key = StringHelper.toString(from);
        } else {
            key = this.getKeyString(from);
        }
        if (StringHelper.isEmpty(key)) {
            key = StringHelper.toString(ObjectHelper.get(from, "id"));
        }
        if (StringHelper.isEmpty(key)) {
            key = "";
        }
        return key;
    }

    protected String createReplace(String sqlName, Object model) {
        // 判断主键是字符串和需要生成主键
        boolean isKeyString = this.table.getTable().getKeyType() == String.class;
        String keyString = this.getKeyString(model);

        //  生成主键
        if (StringHelper.isEmpty(keyString) && isKeyString) {
            keyString = StringHelper.getNewID();
            this.setKeyString(model, keyString);
        }

        // 检测数据合法性
        this.check(DaoConst.OPERATOR_TYPE_CREATE, keyString, model);

        // 执行创建的SQL语句
        int ret = updateSql(sqlName, model);
        // 判断是否需要获取自增编号(主键为整形)
        if (StringHelper.isEmpty(keyString) && !isKeyString) {
            keyString = this.getIdentity();
            this.setKeyString(model, keyString);
        }

        // 最终处理
        this.created(model);

        // 返回执行结果
        String retVal = ret > 0 ? keyString : "";
        return retVal;
    }

    /**
     * 创建数据,当不传入了主键时,则会自动生成主键,传入时不会生成。
     *
     * @param model 需要创建的数据
     * @return 创建的主键编号, 创建成功,返回主键,否则为空
     */
    @Override
    public String create(Object model) {
        return createReplace(DaoConst.CREATE, model);
    }


    /**
     * 创建数据,当不传入了主键时,则会自动生成主键,传入时不会生成。
     *
     * @param model 需要创建的数据
     * @return 创建的主键编号, 创建成功,返回主键,否则为空
     */
    @Override
    public String replace(Object model) {
        return createReplace(DaoConst.REPLACE, model);
    }

    /**
     * 修改数据
     *
     * @param model 需要修改的数据
     * @return 删除的主键编号
     */
    @Override
    public String update(Object model) {
        String keyString = this.getKeyString(model);
        if (StringHelper.isEmpty(keyString)) {
            throw new RuntimeException("表" + this.table.getTable().getName() + "主键值为空时不能更新");
        }
        this.check(DaoConst.OPERATOR_TYPE_UPDATE, keyString, model);
        SqlData sqlData = this.getSql(DaoConst.UPDATE);
        int ret = updateSql(sqlData, model);
        if (ret == 0) {
            throw new RuntimeException("修改失败,请确认是否被其他人修改,版本号传入是否正确");
        }
        String retVal = ret > 0 ? keyString : "";
        return retVal;
    }

    /**
     * 保存数据
     *
     * @param model 需要保存的数据
     * @return 保存的主键编号
     */
    @Override
    public String save(Object model) {
        String id = this.getKeyString(model);
        if (StringHelper.isEmpty(id)) {
            return create(model);
        } else {
            return update(model);
        }
    }

    /**
     * 删除数据
     *
     * @param model 需要删除的数据
     * @return 删除的主键编号
     */
    @Override
    public int remove(Object model) {
        // 获取来源主键
        Object from = model;
        String keyString = getInputKey(from);
        // 调用删除日志
        this.check(DaoConst.OPERATOR_TYPE_REMOVE, keyString, from);
        // 当主键存在值时,直接通过主键删除
        if (!StringHelper.isEmpty(keyString)) {
            // 去掉其他非主键的属性
            from = new HashMap<String, Object>(DaoConst.COLLECTION_INIT_SIZE);
            this.setKeyString(from, keyString);
        }

        // 处理来源值
        for (TableFieldVo fieldVo : this.table.getTable().getRemoveUpdate()) {
            Object fromValue = ObjectHelper.get(model, fieldVo.inputName);
            ObjectHelper.set(from, fieldVo.inputName, fromValue);
        }

        // 调用删除语句
        SqlData sqlData = this.getSql(DaoConst.REMOVE);
        return updateSql(sqlData, from);
    }

    /**
     * 创建数据,当不传入了主键时,则会自动生成主键,传入时不会生成。
     *
     * @param model 需要创建的数据
     * @return 创建的主键编号
     */
    @Override
    public List<String> createList(Collection model) {
        List<String> ret = new ArrayList<>();
        for (Object item : model) {
            ret.add(this.create(item));
        }
        return ret;
    }

    /**
     * 创建数据,当不传入了主键时,则会自动生成主键,传入时不会生成。
     *
     * @param model 需要创建的数据
     * @return 创建的主键编号
     */
    @Override
    public List<String> createArray(Object... model) {
        return createList(Arrays.asList(model));
    }

    /**
     * 修改数据
     *
     * @param model 需要修改的数据
     * @return 删除的主键编号
     */
    @Override
    public List<String> updateList(Collection model) {
        List<String> ret = new ArrayList<>();
        for (Object item : model) {
            ret.add(this.update(item));
        }
        return ret;

    }

    /**
     * 创建数据,当不传入了主键时,则会自动生成主键,传入时不会生成。
     *
     * @param model 需要创建的数据
     * @return 创建的主键编号
     */
    @Override
    public List<String> replaceArray(Object... model) {
        return replaceList(Arrays.asList(model));
    }

    /**
     * 修改数据
     *
     * @param model 需要修改的数据
     * @return 删除的主键编号
     */
    @Override
    public List<String> replaceList(Collection model) {
        List<String> ret = new ArrayList<>();
        for (Object item : model) {
            ret.add(this.replace(item));
        }
        return ret;

    }

    /**
     * 修改数据
     *
     * @param model 需要修改的数据
     * @return 删除的主键编号
     */
    @Override
    public List<String> updateArray(Object... model) {
        return updateList(Arrays.asList(model));
    }

    /**
     * 保存数据,有主键时修改,无主键时创建
     *
     * @param model 需要保存的数据
     * @return 保存的主键编号
     */
    @Override
    public List<String> saveList(Collection model) {
        List<String> ret = new ArrayList<>();
        for (Object item : model) {
            ret.add(this.save(item));
        }
        return ret;
    }

    /**
     * 保存数据,有主键时修改,无主键时创建
     *
     * @param model 需要保存的数据
     * @return 保存的主键编号
     */
    @Override
    public List<String> saveArray(Object... model) {
        return saveList(Arrays.asList(model));
    }


    /**
     * 删除数据,可以用于父子表删除,如通过订单删除游客信息 visitorDao.remove({orderId:1});
     * int field;
     *
     * @param model 需要删除的数据,可以是主键字符串(Int),或者是包含主键的实体,或者是包含其他非主键的实体完全匹配.
     * @return 删除的记录数量
     */
    @Override
    public int removeList(Collection model) {
        int ret = 0;
        for (Object item : model) {
            ret += this.remove(item);
        }
        return ret;
    }

    /**
     * 删除数据,可以用于父子表删除,如通过订单删除游客信息 visitorDao.remove({orderId:1});
     * int field;
     *
     * @param model 需要删除的数据,可以是主键字符串(Int),或者是包含主键的实体,或者是包含其他非主键的实体完全匹配.
     * @return 删除的记录数量
     */
    @Override
    public int removeArray(Object... model) {
        return removeList(Arrays.asList(model));
    }

    /**
     * 检测数据
     *
     * @param operatorType 操作方法类型
     * @param keyString    主键
     * @param model        需要检测的数据
     */
    protected void check(int operatorType, String keyString, Object model) {
        if (operatorType == DaoConst.OPERATOR_TYPE_CREATE || operatorType == DaoConst.OPERATOR_TYPE_UPDATE) {
            if (model instanceof InitDao) {
                InitDao to = (InitDao) model;
                to.init();
            }
            List<SqlData> sqlArray = this.table.getSqlType(DaoConst.SQL_TYPE_EXISTS);
            if (sqlArray != null) {
                for (SqlData sql : sqlArray) {
                    this.checkExist(sql.getName(), model, String.format("%s已经存在", sql.getName()));
                }
            }
        } else if (operatorType == DaoConst.OPERATOR_TYPE_REMOVE) {
            if (model instanceof InitRemoveDao) {
                InitRemoveDao to = (InitRemoveDao) model;
                to.initRemove();
            }
        }
    }

    /**
     * 获取请求对象爱
     *
     * @param model 来源对象
     * @return
     */
    protected Object getLoadFrom(Object model, QueryPara queryPara) {
        if (queryPara != null && queryPara.isFullCond()) {
            return model;
        }

        // 对查询条件进行初始化
        if (model instanceof InitDaoQuery) {
            ((InitDaoQuery) model).initCond();
        }

        // 获取来源主键
        Object from = model;
        String key = this.getInputKey(from);

        // 当主键存在时,只通过主键加载
        if (!StringHelper.isEmpty(key)) {
            from = new HashMap<String, Object>(DaoConst.COLLECTION_INIT_SIZE);
            this.setKeyString(from, key);
        }

        return from;
    }

    private <T extends Object> void checkLoadResult(T toItem, List<T> tos) {
        // 判断来源主键是否存在,不存在则获取加载后的主键
        if (toItem != null) {
            String key = this.getKeyString(toItem);
            check(DaoConst.OPERATOR_TYPE_LOAD, key, toItem);
        }
        if (tos != null) {
            for (T item : tos) {
                String key = this.getKeyString(item);
                check(DaoConst.OPERATOR_TYPE_LOAD, key, item);
            }
        }
    }


    /**
     * 加载数据
     *
     * @param model       加载数据的请求参数
     * @param resultClass 需要加载的数据的类型
     * @param queryPara   查询参数
     * @param <T>         返回数据的类型
     * @return 需要返回的数据
     */
    @Override
    public <T extends Object> T load(Object model, Class<T> resultClass, QueryPara queryPara) {
        // 获取来源主键
        Object from = this.getLoadFrom(model, queryPara);
        // 通过传入数据进行加载
        T to = this.queryFirst(resultClass, DaoConst.LOAD, from, queryPara);
        if (to == null) {
            return null;
        }

        // 判断来源主键是否存在,不存在则获取加载后的主键
        checkLoadResult(to, null);

        return to;
    }


    /**
     * 加载数据
     *
     * @param model       加载数据的请求参数
     * @param resultClass 需要加载的数据的类型
     * @param <T>         返回数据的类型
     * @return 需要返回的数据
     */
    @Override
    public <T extends Object> T load(Object model, Class<T> resultClass) {
        return this.load(model, resultClass, null);
    }

    /**
     * 加载数据
     *
     * @param model       加载数据的请求参数
     * @param resultClass 需要加载的数据的类型
     * @param queryPara   查询参数
     * @param <T>         返回数据的类型
     * @return 需要返回的数据
     */
    @Override
    public <T extends Object> List<T> loadList(Object model, Class<T> resultClass, QueryPara queryPara) {
        // 获取来源主键
        Object from = this.getLoadFrom(model, queryPara);

        // 通过传入数据进行加载
        List<T> to = this.query(resultClass, DaoConst.LOAD, from, queryPara);
        if (to == null || to.size() == 0) {
            return new ArrayList<>();
        }

        checkLoadResult(null, to);

        return to;
    }

    /**
     * 加载数据
     *
     * @param model       加载数据的请求参数
     * @param resultClass 需要加载的数据的类型
     * @param <T>         返回数据的类型
     * @return 需要返回的数据
     */
    @Override
    public <T extends Object> List<T> loadList(Object model, Class<T> resultClass) {
        return this.loadList(model, resultClass, null);
    }


    /**
     * 加载分页数据
     *
     * @param model       加载数据的请求参数
     * @param resultClass 需要加载的数据的类型
     * @param <T>         返回数据的类型
     * @return 需要返回的数据
     */
    @Override
    public <T extends Object> PageSizeData<T> loadPage(PageSizeReqVo model, Class<T> resultClass) {
        return this.loadPage(model, model, resultClass, null);
    }

    /**
     * 加载分页数据
     *
     * @param model       加载数据的请求参数
     * @param resultClass 需要加载的数据的类型
     * @param queryPara   查询参数
     * @param <T>         返回数据的类型
     * @return 需要返回的数据
     */
    @Override
    public <T extends Object> PageSizeData<T> loadPage(PageSizeReqVo model, Class<T> resultClass, QueryPara queryPara) {
        return this.loadPage(model, model, resultClass, queryPara);
    }

    /**
     * 加载分页数据
     *
     * @param model       加载数据的请求参数
     * @param resultClass 需要加载的数据的类型
     * @param <T>         返回数据的类型
     * @return 需要返回的数据
     */
    @Override
    public <T extends Object> PageSizeData<T> loadPage(PageSizeReqVo pageReq, Object model, Class<T> resultClass) {
        return this.loadPage(pageReq, model, resultClass, null);
    }

    /**
     * 加载分页数据
     *
     * @param model       加载数据的请求参数
     * @param resultClass 需要加载的数据的类型
     * @param queryPara   查询参数
     * @param <T>         返回数据的类型
     * @return 需要返回的数据
     */
    @Override
    public <T extends Object> PageSizeData<T> loadPage(PageSizeReqVo pageReq, Object model, Class<T> resultClass, QueryPara queryPara) {
        // 获取来源主键
        Object from = this.getLoadFrom(model, queryPara);

        // 通过传入数据进行加载
        PageSizeData<T> to = this.queryPage(resultClass, pageReq, DaoConst.LOAD, from, queryPara);
        if (to == null || to.getPageTotal() == 0) {
            return to;
        }

        // 判断来源主键是否存在,不存在则获取加载后的主键
        checkLoadResult(null, to.getList());

        return to;
    }


    /**
     * 加载分页数据
     *
     * @param model       加载数据的请求参数
     * @param resultClass 需要加载的数据的类型
     * @param <T>         返回数据的类型
     * @return 需要返回的数据
     */
    @Override
    public <T extends Object> List<T> loadPageData(PageSizeReqVo model, Class<T> resultClass) {
        return this.loadPageData(model, model, resultClass, null);
    }

    /**
     * 加载分页数据
     *
     * @param model       加载数据的请求参数
     * @param resultClass 需要加载的数据的类型
     * @param queryPara   查询参数
     * @param <T>         返回数据的类型
     * @return 需要返回的数据
     */
    @Override
    public <T extends Object> List<T> loadPageData(PageSizeReqVo model, Class<T> resultClass, QueryPara queryPara) {
        return this.loadPageData(model, model, resultClass, queryPara);
    }


    /**
     * 加载分页数据
     *
     * @param model       加载数据的请求参数
     * @param resultClass 需要加载的数据的类型
     * @param <T>         返回数据的类型
     * @return 需要返回的数据
     */
    @Override
    public <T extends Object> List<T> loadPageData(PageSizeReqVo pageReq, Object model, Class<T> resultClass) {
        return this.loadPageData(pageReq, model, resultClass, null);
    }

    /**
     * 加载分页数据
     *
     * @param model       加载数据的请求参数
     * @param resultClass 需要加载的数据的类型
     * @param queryPara   查询参数
     * @param <T>         返回数据的类型
     * @return 需要返回的数据
     */
    @Override
    public <T extends Object> List<T> loadPageData(PageSizeReqVo pageReq, Object model, Class<T> resultClass, QueryPara queryPara) {
        // 获取来源主键
        Object from = this.getLoadFrom(model, queryPara);

        // 通过传入数据进行加载
        List<T> to = this.queryPageData(resultClass, pageReq, DaoConst.LOAD, from, queryPara);
        if (to == null) {
            return new ArrayList<>();
        }

        // 判断来源主键是否存在,不存在则获取加载后的主键
        checkLoadResult(null, to);

        return to;
    }

    /**
     * 设置MD5主键
     *
     * @param model
     * @param <T>
     */
    @Override
    public <T extends Object> String setGroupId(T model) {
        // 判断前台实体
        if (model == null) {
            return StringHelper.EMPTY;
        }

        String md5Field = this.table.getTable().getMD5KeyName();
        String md5From = StringHelper.isEmpty(md5Field) ? StringHelper.EMPTY : ObjectHelper.getString(model, md5Field);
        // 获取前台分组的MD5标识
        String md5 = md5From;
        if (StringHelper.isEmpty(md5)) {
            md5 = this.getMd5(DaoConst.GROUP_QUERY, model);
        }
        if (!StringHelper.isEmpty(md5Field)) {
            ObjectHelper.set(model, md5Field, md5);
        }
        if (this.table.getTable().getKeyType() == String.class) {
            this.setKeyString(model, md5);
        }
        return md5;
    }

    /**
     * 添加统计数据
     *
     * @param cls   类型
     * @param model 实体
     * @param <T>   泛型类型
     * @return 增加统计的数据编号
     */
    @Override
    public <T extends Object> String addGroup(Class<T> cls, T model) {
        this.setGroupId(model);
        // 判断前台实体
        if (model == null) {
            return "";
        }

        // 获取标识的实体
        T from;
        String md5Field = this.table.getTable().getMD5KeyName();
        if (this.table.getTable().getKeyType() == String.class) {
            from = model;
        } else if (!StringHelper.isEmpty(md5Field)) {
            Map<String, Object> map = new HashMap<>(DaoConst.COLLECTION_INIT_SIZE);
            map.put(md5Field, ObjectHelper.get(model, md5Field));
            from = this.load(map, cls);
        } else {
            from = this.queryFirst(cls, DaoConst.GROUP_QUERY, model);
        }

        // 当没有存在,则创建,否则 UpdateAdd
        if (from == null) {
            return this.create(model);
        } else {
            // 获取后台的编号,并写入到前台传入的数据上面
            String keyField = this.getKey();
            Object key = ObjectHelper.get(from, keyField);
            ObjectHelper.set(model, keyField, key);
            if (model instanceof InitDao) {
                ((InitDao) model).init();
            }
            // 执行更新SQL语句
            SqlData sqlData = this.getSql(DaoConst.GROUP_ADD);
            int ret = this.updateSql(sqlData, model);
            if (ret < 1) {
                return this.create(model);
            } else {
                return key.toString();
            }
        }
    }

    /**
     * 添加统计数组
     *
     * @param cls    类型
     * @param models 请求实体
     * @param <T>    泛型数据
     * @return 返回列表
     */
    @Override
    public <T extends Object> List<String> addGroupList(Class<T> cls, List<T> models) {
        List<String> ret = new ArrayList<>();
        for (T model : models) {
            ret.add(this.addGroup(cls, model));
        }
        return ret;
    }

    /**
     * 添加统计数组
     *
     * @param cls    类型
     * @param models 请求实体
     * @param <T>    泛型数据
     * @return 返回列表
     */
    @Override
    public <T extends Object> List<String> addGroupArray(Class<T> cls, T... models) {
        return this.addGroupList(cls, Arrays.asList(models));
    }

    /**
     * 根据SQL语句名称和实体获取MD5值
     *
     * @param sqlName SQL语句名称
     * @param model   获取加密之后的实体
     * @param <T>     获取的类型
     * @return 获取加密之后的值
     */
    protected <T extends Object> String getMd5(String sqlName, T model) {
        SqlData sql = this.getSql(sqlName);
        StringBuilder sb = new StringBuilder();
        String date = StringHelper.EMPTY;
        for (SqlDataField field : sql.getSqlDataFields()) {
            List<Object> values = field.getCond().getValues(model);
            for (Object item : values) {
                if (sb.length() > 0) {
                    sb.append(":");
                }
                sb.append(item);
                if (StringHelper.isEmpty(date)) {
                    String itemString = String.valueOf(item);
                    if (item instanceof Date) {
                        date = DateHelper.getDateTimeString((Date) item);
                    } else if (DateHelper.isDateFormat(itemString)) {
                        date = itemString;
                    }
                }
            }
        }
        if (StringHelper.isEmpty(date)) {
            return StringHelper.md5(sb.toString());
        } else {
            return StringHelper.getNewMD5Id(DateHelper.getDateTime(date), sb.toString());
        }
    }

    /**
     * 添加统计数据列表
     *
     * @param sqlName 需要执行的SQL语句名称
     * @param model   需要执行的SQL语句的实体
     * @return
     */
    protected int addGroupList(String sqlName, Object model) {
        this.updateSql(sqlName + "_GroupInit", model);
        return this.updateSql(sqlName + "_GroupAdd", model);
    }


    /***
     * 查询数据是否存在,当存在时修改,否则增加
     * @param cls 需要创建的实体的类型
     * @param requests 前台参数,不能包含主键以及其他不需要修改的字段
     * @return 保存成功,返回保存的ID,保存失败,返回空值
     */
    @Override
    public <T extends Object> List<String> saveByLoadArray(Class<T> cls, Object... requests) {
        return this.saveByLoadList(cls, Arrays.asList(requests));
    }

    /***
     * 查询数据是否存在,当存在时修改,否则增加
     * @param cls 需要创建的实体的类型
     * @param requests 前台参数,不能包含主键以及其他不需要修改的字段
     * @return 保存成功,返回保存的ID,保存失败,返回空值
     */
    @Override
    public <T extends Object> List<String> saveByLoadList(Class<T> cls, List requests) {
        List<String> ret = new ArrayList<>();
        if (requests != null) {
            for (Object model : requests) {
                ret.add(this.saveByLoad(cls, model));
            }
        }
        return ret;
    }

    /***
     * 查询数据是否存在,当存在时修改,否则增加
     * @param cls 需要创建的实体的类型
     * @param request 前台参数,不能包含主键以及其他不需要修改的字段
     * @return 保存成功,返回保存的ID,保存失败,返回空值
     */
    @Override
    public <T extends Object> String saveByLoad(Class<T> cls, Object request) {
        String key = this.getInputKey(request);
        T from = null;
        if (!StringHelper.isEmpty(key)) {
            from = this.load(request, cls);
            return saveFromCreate(cls, from, request);
        }
        return saveFromCreate(cls, from, request);
    }

    /**
     * 根据from来创建对象,对象存在时修改,对象不存在时创建
     *
     * @param cls     自动创建的对象类型
     * @param from    需要修改的对象,当该对象不存在时创建数据
     * @param request 需要创建的对象
     * @param <T>     创建的数据类型
     * @return 创建成功的编号
     */
    protected <T extends Object> String saveFromCreate(Class<T> cls, T from, Object request) {
        if (from == null) {
            try {
                from = cls.newInstance();
            } catch (Exception ex) {
                throw new RuntimeException("创建对象" + cls.getName() + "出错", ex);
            }
            ObjectHelper.writeWithFrom(from, request);
            return this.create(from);
        } else {
            TableStruct tableStruct = this.table.getTable();
            // 写入历史版本号
            TableFieldVo version = tableStruct.getVersion();
            if (version != null) {
                Object hisVersion = ObjectHelper.get(from, version.inputName);
                ObjectHelper.writeWithFrom(from, request);
                ObjectHelper.set(from, version.inputName, hisVersion);
            } else {
                ObjectHelper.writeWithFrom(from, request);
            }
            return this.update(from);
        }
    }

    /***
     * 查询数据是否存在,当存在时修改,否则增加
     * @param cls 需要创建的实体的类型
     * @param sqlName   执行的SQL语句
     * @param request 前台参数,不能包含主键以及其他不需要修改的字段
     * @return 保存成功,返回保存的ID,保存失败,返回空值
     */
    public <T extends Object> String saveWith(Class<T> cls, String sqlName, Object request) {
        if (request instanceof InitDao) {
            ((InitDao) request).init();
        }
        TableStruct tableStruct = this.table.getTable();
        // 获取所有条件字段
        MapRow condition = new MapRow();
        List<TableFieldVo> fields = tableStruct.getFields(DaoConst.FIELD_SAVE_WITH);
        for (TableFieldVo field : fields) {
            condition.put(field.inputName, ObjectHelper.get(request, field.inputName));
        }
        // 查询历史数据
        T from = this.queryFirst(cls, sqlName, condition);
        return saveFromCreate(cls, from, request);
    }

    /**
     * 创建成功后的处理
     *
     * @param model 创建的实体数据
     */
    protected void created(Object model) {

    }

    /**
     * 执行是否存在的SQL语句
     *
     * @param sqlName SQL语句名称
     * @param model   实体
     * @param msg     消息
     */
    protected void checkExist(String sqlName, Object model, String msg) {
        MapRow row = this.queryFirst(sqlName, model);
        if (row != null) {
            throw new CodeException(msg);
        }
    }


    /**
     * 对数据据进行集合操作
     *
     * @param daoVo
     * @return
     */
    @Override
    public List<String> dataDao(DataDaoVo daoVo) {
        List<String> res = new ArrayList<>();
        res.addAll(this.createList(daoVo.getCreates()));
        res.addAll(this.updateList(daoVo.getUpdates()));
        this.removeList(daoVo.getRemoves());
        return res;
    }
}