/**
     * 设置需要追加的输出属性
     * @access public
     * @param array $append 属性列表
     * @return $this
     */
    public function append($append = [])
    {
        $this->append = $append;// 属性追加
        return $this;
    }

    /**
     * 设置需要隐藏的输出属性
     * @access public
     * @param array $hidden 属性列表
     * @return $this
     */
    public function hidden($hidden = [])
    {
        $this->hidden = $hidden;// 属性隐藏
        return $this;
    }

    /**
     * 设置需要输出的属性
     * @param array $visible
     * @return $this
     */
    public function visible($visible = [])
    {
        $this->visible = $visible;// 输出的属性
        return $this;
    }

    /**
     * 转换当前模型对象为数组
     * @access public
     * @return array
     */
    public function toArray()
    {
        $item = [];

        //过滤属性
        if (!empty($this->visible)) {
            $data = array_intersect_key($this->data, array_flip($this->visible));
        } elseif (!empty($this->hidden)) {
            $data = array_diff_key($this->data, array_flip($this->hidden));
        } else {
            $data = $this->data;
        }

        foreach ($data as $key => $val) {
            if ($val instanceof Model || $val instanceof Collection) {
                // 关联模型对象
                $item[$key] = $val->toArray();
            } elseif (is_array($val) && reset($val) instanceof Model) {
                // 关联模型数据集
                $arr = [];
                foreach ($val as $k => $value) {
                    $arr[$k] = $value->toArray();
                }
                $item[$key] = $arr;
            } else {
                // 模型属性
                $item[$key] = $this->getAttr($key);
            }
        }
        // 追加属性(必须定义获取器)
        if (!empty($this->append)) {
            foreach ($this->append as $name) {
                $item[$name] = $this->getAttr($name);
            }
        }
        return !empty($item) ? $item : [];
    }// 转换为数组

    /**
     * 转换当前模型对象为JSON字符串
     * @access public
     * @param integer   $options json参数
     * @return string
     */
    public function toJson($options = JSON_UNESCAPED_UNICODE)
    {
        return json_encode($this->toArray(), $options);// 转换wield json
    }

    /**
     * 获取模型对象的主键
     * @access public
     * @param string $name 模型名
     * @return mixed
     */
    public function getPk($name = '')
    {// 获取主键
        if (!empty($name)) {
            $table = $this->db()->getTable($name);
            return $this->db()->getPk($table);
        } elseif (empty($this->pk)) {
            $this->pk = $this->db()->getPk();
        }
        return $this->pk;
    }

    /**
     * 判断一个字段名是否为主键字段
     * @access public
     * @param string $key 名称
     * @return bool
     */
    protected function isPk($key)
    {// 检测是否为主键
        $pk = $this->getPk();
        if (is_string($pk) && $pk == $key) {
            return true;
        } elseif (is_array($pk) && in_array($key, $pk)) {
            return true;
        }
        return false;
    }

    /**
     * 保存当前数据对象
     * @access public
     * @param array     $data 数据
     * @param array     $where 更新条件
     * @param string    $sequence     自增序列名
     * @return integer|false
     */
    public function save($data = [], $where = [], $sequence = null)
    {// 数据保存
        if (!empty($data)) {// 数据不为空
            // 数据自动验证
            if (!$this->validateData($data)) {
                return false;
            }
            // 数据对象赋值
            foreach ($data as $key => $value) {// 数据对象赋值
                $this->setAttr($key, $value, $data);
            }
            if (!empty($where)) {// 条件不为空 代表为 更新状态
                $this->isUpdate = true;
            }
        }

        // 检测字段
        if (!empty($this->field)) {// 字段不为空
            $this->db();
            foreach ($this->data as $key => $val) {// 删除数据里面的 多有的字段信息
                if (!in_array($key, $this->field)) {
                    unset($this->data[$key]);
                }
            }
        }

        // 数据自动完成
        $this->autoCompleteData($this->auto);// 数据自动完成

        // 自动写入更新时间
        if ($this->autoWriteTimestamp && $this->updateTime) {// 时间属性设置
            $this->setAttr($this->updateTime, null);
        }

        // 事件回调
        if (false === $this->trigger('before_write', $this)) {// 回调
            return false;
        }

        if ($this->isUpdate) {// 执行更新
            // 自动更新
            $this->autoCompleteData($this->update);

            // 事件回调
            if (false === $this->trigger('before_update', $this)) {
                return false;
            }

            // 去除没有更新的字段
            $data = [];
            foreach ($this->data as $key => $val) {
                if (in_array($key, $this->change) || $this->isPk($key)) {
                    $data[$key] = $val;
                }
            }

            if (!empty($this->readonly)) {
                // 只读字段不允许更新
                foreach ($this->readonly as $key => $field) {
                    if (isset($data[$field])) {
                        unset($data[$field]);
                    }
                }
            }

            if (empty($where) && !empty($this->updateWhere)) {
                $where = $this->updateWhere;
            }

            if (!empty($where)) {
                $pk = $this->getPk();
                if (is_string($pk) && isset($data[$pk])) {
                    unset($data[$pk]);
                }
            }

            $result = $this->db()->where($where)->update($data);// 还是使用数据库 模型的更新
            // 清空change
            $this->change = [];
            // 更新回调
            $this->trigger('after_update', $this);
        } else {
            // 自动写入
            $this->autoCompleteData($this->insert);

            // 自动写入创建时间
            if ($this->autoWriteTimestamp && $this->createTime) {
                $this->setAttr($this->createTime, null);
            }

            if (false === $this->trigger('before_insert', $this)) {
                return false;
            }

            $result = $this->db()->insert($this->data);// 数据模型的插入

            // 获取自动增长主键
            if ($result) {
                $insertId = $this->db()->getLastInsID($sequence);// 获取自动增长的主键
                $pk       = $this->getPk();
                if (is_string($pk) && $insertId) {
                    $this->data[$pk] = $insertId;
                }
            }
            // 标记为更新
            $this->isUpdate = true;
            // 清空change
            $this->change = [];
            // 新增回调
            $this->trigger('after_insert', $this);
        }
        // 写入回调
        $this->trigger('after_write', $this);

        return $result;
    }

    /**
     * 保存多个数据到当前数据对象
     * @access public
     * @param array     $dataSet 数据
     * @param boolean   $replace 是否自动识别更新和写入
     * @return array|false
     */
    public function saveAll($dataSet, $replace = true)// 基于事务的 批量插入及更新
    {
        if ($this->validate) {//批量数据验证
            // 数据批量验证
            $validate = $this->validate;
            foreach ($dataSet as $data) {
                if (!$this->validate($validate)->validateData($data)) {
                    return false;
                }
            }
        }

        $result = [];
        $db     = $this->db();
        $db->startTrans();// 启动事务
        try {
            $pk = $this->getPk();
            if (is_string($pk) && $replace) {
                $auto = true;
            }
            foreach ($dataSet as $key => $data) {
                if (!empty($auto) && isset($data[$pk])) {
                    $result[$key] = self::update($data);
                } else {
                    $result[$key] = self::create($data);
                }
            }
            $db->commit();// 提交事务
            return $result;
        } catch (\Exception $e) {
            $db->rollback();// 否则回滚
            throw $e;
        }
    }