初始化项目

This commit is contained in:
2026-09-01 22:15:50 +08:00
commit b5a26a8848
11217 changed files with 1710987 additions and 0 deletions
+36
View File
@@ -0,0 +1,36 @@
<?php
// +----------------------------------------------------------------------
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
// +----------------------------------------------------------------------
// | Author: 萤火科技 <admin@yiovo.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace cores;
use think\Service;
use think\facade\Log;
use think\facade\Request;
/**
* 应用服务类
*/
class AppService extends Service
{
// 服务注册
public function register()
{
}
// 服务启动
public function boot()
{
}
}
+104
View File
@@ -0,0 +1,104 @@
<?php
// +----------------------------------------------------------------------
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
// +----------------------------------------------------------------------
// | Author: 萤火科技 <admin@yiovo.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace cores;
use think\App;
use think\Validate;
use think\exception\ValidateException;
/**
* 控制器基础类
*/
abstract class BaseController
{
/**
* Request实例
* @var \think\Request
*/
protected $request;
/**
* 应用实例
* @var \think\App
*/
protected $app;
/**
* 是否批量验证
* @var bool
*/
protected $batchValidate = false;
/**
* 控制器中间件
* @var array
*/
protected $middleware = [];
/**
* 构造方法
* @access public
* @param App $app 应用对象
*/
public function __construct(App $app)
{
$this->app = $app;
$this->request = $this->app->request;
// 控制器初始化
$this->initialize();
}
// 初始化
protected function initialize()
{
}
/**
* 验证数据
* @access protected
* @param array $data 数据
* @param string|array $validate 验证器名或者验证规则数组
* @param array $message 提示信息
* @param bool $batch 是否批量验证
* @return array|string|true
* @throws ValidateException
*/
protected function validate(array $data, $validate, array $message = [], bool $batch = false)
{
if (is_array($validate)) {
$v = new Validate();
$v->rule($validate);
} else {
if (strpos($validate, '.')) {
// 支持场景
[$validate, $scene] = explode('.', $validate);
}
$class = false !== strpos($validate, '\\') ? $validate : $this->app->parseClass('validate', $validate);
$v = new $class();
if (!empty($scene)) {
$v->scene($scene);
}
}
$v->message($message);
// 是否批量验证
if ($batch || $this->batchValidate) {
$v->batch(true);
}
return $v->failException(true)->check($data);
}
}
+298
View File
@@ -0,0 +1,298 @@
<?php
// +----------------------------------------------------------------------
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
// +----------------------------------------------------------------------
// | Author: 萤火科技 <admin@yiovo.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace cores;
use think\Model;
use think\db\Query;
use cores\traits\ErrorTrait;
/**
* 模型基类
* Class BaseModel
* @package app\common\model
*/
abstract class BaseModel extends Model
{
use ErrorTrait;
// 当前访问的商城ID
public static $storeId;
// 定义表名
protected $name;
// 模型别名
protected $alias = '';
// 定义全局的查询范围
protected $globalScope = ['store_id'];
// 是否允许全局查询store_id
protected $isGlobalScopeStoreId = true;
// 错误信息
protected $error = '';
/**
* 模型基类初始化
*/
public static function init()
{
parent::init();
// 绑定store_id
self::getStoreId();
}
/**
* 查找单条记录
* @param $data
* @param array $with
* @return array|false|static|null
*/
public static function get($data, array $with = [])
{
try {
$query = (new static)->with($with);
return is_array($data) ? $query->where($data)->find() : $query->find((int)$data);
} catch (\Exception $e) {
return false;
}
}
/**
* 定义全局的查询范围
* @param Query $query
* @return bool
*/
public function scopeStore_id(Query $query): bool
{
if (!$this->isGlobalScopeStoreId) return false;
$storeId = self::getStoreId();
$storeId > 0 && $query->where($query->getTable() . '.store_id', $storeId);
return true;
}
/**
* 获取当前操作的商城ID
* @return int|null
*/
private static function getStoreId(): ?int
{
if (empty(self::$storeId) && in_array(app_name(), ['store', 'api'])) {
self::$storeId = getStoreId();
}
return self::$storeId;
}
/**
* 获取当前调用来源的应用名称
* 例如:admin, api, store, task
* @return string|bool
*/
protected static function getCalledModule()
{
if (preg_match('/app\\\(\w+)/', get_called_class(), $class)) {
return $class[1];
}
return 'common';
}
/**
* 设置默认的检索数据
* @param array $query
* @param array $default
* @return array
*/
protected function setQueryDefaultValue(array $query, array $default = []): array
{
$data = array_merge($default, $query);
foreach ($query as $field => $value) {
// 不存在默认值跳出循环
if (!isset($default[$field])) continue;
// 如果传参为空, 设置默认值
if (empty($value) && $value !== '0') {
$data[$field] = $default[$field];
}
}
return $data;
}
/**
* 设置基础查询条件(用于简化基础alias和field)
* @test 2019-4-25
* @param string $alias
* @param array $join
* @return static
*/
public function setBaseQuery(string $alias = '', array $join = [])
{
// 设置别名
$aliasValue = $alias ?: $this->alias;
$query = $this->alias($aliasValue)->field("{$aliasValue}.*");
// join条件
if (!empty($join)) : foreach ($join as $item):
$query->join($item[0], "{$item[0]}.{$item[1]}={$aliasValue}." . ($item[2] ?? $item[1]));
endforeach; endif;
return $query;
}
/**
* 更新数据
* 重写\think\Model::update方法, 目的可以返回更新的状态bool
* @access public
* @param array $data 数据数组
* @param array $where 更新条件
* @param array $allowField 允许字段
* @return bool
*/
public static function updateBase(array $data, array $where = [], array $allowField = []): bool
{
$model = new static;
if (!empty($allowField)) {
$model->allowField($allowField);
}
if (!empty($where)) {
$model->setUpdateWhere($where);
}
return $model->exists(true)->save($data);
}
/**
* 批量更新数据(支持带where条件)
* @param iterable $dataSet [0 => ['data'=>[], 'where'=>[]]]
* @return array|false
*/
public function updateAll(iterable $dataSet)
{
if (empty($dataSet)) {
return false;
}
return $this->transaction(function () use ($dataSet) {
$result = [];
foreach ($dataSet as $key => $item) {
$result[$key] = self::updateBase($item['data'], $item['where']);
}
return $result;
});
}
/**
* 批量新增数据
* @param iterable $dataSet [0 => ['id'=>10001, 'name'=>'wang']]
* @return array|false
*/
public function addAll(iterable $dataSet)
{
if (empty($dataSet)) {
return false;
}
return $this->transaction(function () use ($dataSet) {
$result = [];
foreach ($dataSet as $key => $item) {
$result[$key] = self::create($item, $this->field);
}
return $result;
});
}
/**
* 删除记录
* @param array $where
* 方式1: ['goods_id' => $goodsId]
* 方式2: [
* ['store_user_id', '=', $storeUserId],
* ['role_id', 'in', $deleteRoleIds]
* ]
* @return bool|int
*/
public static function deleteAll(array $where)
{
return (new static)->where($where)->delete();
}
/**
* 字段值增长
* @param array|int|bool $where
* @param string $field
* @param float $step
* @return mixed
*/
protected function setInc($where, string $field, float $step = 1)
{
if (is_numeric($where)) {
$where = [$this->getPk() => (int)$where];
}
return $this->where($where)->inc($field, $step)->update();
}
/**
* 字段值消减
* @param array|int|bool $where
* @param string $field
* @param float $step
* @return mixed
*/
protected function setDec($where, string $field, float $step = 1)
{
if (is_numeric($where)) {
$where = [$this->getPk() => (int)$where];
}
return $this->where($where)->dec($field, $step)->update();
}
/**
* 实例化新查询对象
* @return \think\db\BaseQuery
*/
protected function getNewQuery()
{
return $this->db();
}
/**
* 新增hidden属性
* @param array $hidden
* @return $this
*/
protected function addHidden(array $hidden)
{
$this->hidden = array_merge($this->hidden, $hidden);
return $this;
}
/**
* 生成字段列表(字段加上$alias别名)
* @param string $alias 别名
* @param array $withoutFields 排除的字段
* @return array
*/
protected function getAliasFields(string $alias, array $withoutFields = []): array
{
$fields = array_diff($this->getTableFields(), $withoutFields);
foreach ($fields as &$field) {
$field = "$alias.$field";
}
return $fields;
}
/**
* 获取隐藏的属性
* @param array $hidden
* @return array
*/
public static function getHidden(array $hidden = []): array
{
$model = new static;
return array_merge($model->hidden, $hidden);
}
}
+152
View File
@@ -0,0 +1,152 @@
<?php
// +----------------------------------------------------------------------
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
// +----------------------------------------------------------------------
// | Author: 萤火科技 <admin@yiovo.com>
// +----------------------------------------------------------------------
namespace cores;
use Throwable;
use think\Response;
use think\response\Json;
use think\facade\Log;
use think\facade\Request;
use think\exception\Handle;
use think\db\exception\PDOException;
use think\exception\HttpResponseException;
use cores\exception\BaseException;
/**
* 应用异常处理类
*/
class ExceptionHandle extends Handle
{
// 状态码
private int $status = 200;
// 错误信息
private string $message;
// 附加数据
public array $data = [];
/**
* 记录异常信息(包括日志或者其它方式记录)
* @access public
* @param Throwable $exception
* @return void
*/
public function report(Throwable $exception): void
{
// 不使用内置的方式记录异常日志
// parent::report($exception);
}
/**
* Render an exception into an HTTP response.
*
* @access public
* @param $request
* @param Throwable $e
* @return Response
*/
public function render($request, Throwable $e): Response
{
if ($e instanceof HttpResponseException) {
return $e->getResponse();
}
// 手动触发的异常 BaseException
if ($e instanceof BaseException) {
$this->status = $e->status;
$this->message = $e->message;
$this->data = $e->data;
$extend = property_exists($e, 'extend') ? $e->extend : [];
return $this->output($extend);
}
// 系统运行的异常
$this->status = config('status.error');
$this->message = $e->getMessage() ?: '很抱歉,服务器内部错误';
// 如果是debug模式, 输出调试信息
if (is_debug()) {
return $this->outputDebug($e);
}
// 将运行异常写入日志
$this->errorLog($e);
return $this->output();
}
/**
* 返回json格式数据
* @param array $extend 扩展的数据
* @return Json
*/
private function output(array $extend = []): Json
{
$jsonData = ['message' => $this->message, 'status' => $this->status, 'data' => $this->data];
return json(array_merge($jsonData, $extend));
}
/**
* 返回json格式数据 (debug模式)
* @param Throwable $e
* @return Json
*/
private function outputDebug(Throwable $e): Json
{
$debug = [
'name' => get_class($e),
'file' => $e->getFile(),
'line' => $e->getLine(),
'code' => $this->getCode($e),
'message' => $this->getMessage($e),
'trace' => $e->getTrace(),
'source' => $this->getSourceCode($e),
];
return $this->output(['debug' => $debug]);
}
/**
* 将异常写入日志
* @param Throwable $e
*/
private function errorLog(Throwable $e)
{
// 错误信息
$data = [
'file' => $e->getFile(),
'line' => $e->getLine(),
'message' => $this->getMessage($e),
'status' => $this->getCode($e),
];
// 日志内容
$log = $this->getVisitor();
$log .= "\r\n" . "[ message ] [{$data['status']}] {$data['message']}";
$log .= "\r\n" . "[ file ] {$data['file']}:{$data['line']}";
// $log .= "\r\n" . "[ time ] " . format_time(time());
$log .= "\r\n" . '[ header ] ' . print_r(Request::header(), true);
$log .= '[ param ] ' . print_r(Request::param(), true);
// 如果是数据库报错, 则记录sql语句
if ($e instanceof PDOException) {
$log .= "[ Error SQL ] " . $e->getData()['Database Status']['Error SQL'];
$log .= "\r\n";
}
$log .= "\r\n" . $e->getTraceAsString();
$log .= "\r\n" . '--------------------------------------------------------------------------------------------';
// 写入日志文件
Log::record($log, 'error');
}
/**
* 获取请求路径信息
* @return string
*/
private function getVisitor(): string
{
$data = [Request::ip(), Request::method(), Request::url(true)];
return implode(' ', $data);
}
}
+19
View File
@@ -0,0 +1,19 @@
<?php
// +----------------------------------------------------------------------
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
// +----------------------------------------------------------------------
// | Author: 萤火科技 <admin@yiovo.com>
// +----------------------------------------------------------------------
namespace cores;
// 应用请求对象类
class Request extends \think\Request
{
// 全局过滤规则
protected $filter = ['my_trim', 'my_htmlspecialchars', 'filter_emoji'];
}
@@ -0,0 +1,45 @@
<?php
// +----------------------------------------------------------------------
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
// +----------------------------------------------------------------------
// | Author: 萤火科技 <admin@yiovo.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace cores\exception;
use think\Exception;
/**
* 自定义异常类的基类
* Class BaseException
* @package cores\exception
*/
class BaseException extends Exception
{
// 状态码
public $status;
// 错误信息
public $message = '';
// 输出的数据
public $data = [];
/**
* 构造函数,接收一个关联数组
* @param array $params 关联数组只应包含status、msg、data,且不应该是空值
*/
public function __construct($params = [])
{
parent::__construct();
$this->status = $params['status'] ?? config('status.error');
$this->message = $params['message'] ?? '很抱歉,服务器内部错误';
$this->data = $params['data'] ?? [];
}
}
@@ -0,0 +1,38 @@
<?php
// +----------------------------------------------------------------------
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
// +----------------------------------------------------------------------
// | Author: 萤火科技 <admin@yiovo.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace cores\exception;
/**
* 自定义异常类:调试输出
* Class DebugException
* @package cores\exception
*/
class DebugException extends BaseException
{
// 附加的数据
public $extend = [];
/**
* 构造函数,接收一个关联数组
* @param array $params 关联数组只应包含status、msg、data,且不应该是空值
* @param mixed $debug 调试输出的数据
*/
public function __construct(array $params = [], $debug = null)
{
parent::__construct($params);
$this->status = config('status.success');
$this->message = '-- 调试输出 --';
$this->extend = ['debug' => $debug];
}
}
+110
View File
@@ -0,0 +1,110 @@
<?php
namespace cores\library;
use app\common\library\helper;
use cores\exception\BaseException;
/**
* 版本号工具类
*/
class Version
{
/**
* 获取当前系统版本号
* @return string
* @throws BaseException
*/
public static function getVersion(): string
{
static $version = [];
if (empty($version)) {
// 读取version.json文件
$filePath = root_path() . '/version.json';
!file_exists($filePath) && throwError('version.json not found');
// 解析json数据
$version = helper::jsonDecode(file_get_contents($filePath));
!is_array($version) && throwError('version cannot be decoded');
}
return $version['version'];
}
/**
* 获取下一个版本号
* @param string $currentVersion 当前的版本号
* @param array $versionCollection 版本号列表
* @return false|string
* @throws BaseException
*/
public static function nextVersion(string $currentVersion, array $versionCollection)
{
$vers1 = self::versionToInteger($currentVersion);
$dataset = [];
foreach ($versionCollection as $value) {
$vers2 = self::versionToInteger($value);
$vers2 > $vers1 && $dataset[] = $vers2;
}
if (empty($dataset)) {
return false;
}
return self::integerToVersion(min($dataset));
}
/**
* 将版本转为数字
* @param string $version
* @return int
* @throws BaseException
*/
public static function versionToInteger(string $version): int
{
if (!self::check($version)) {
throwError('version Validate Error');
}
list($major, $minor, $sub) = explode('.', $version);
return intval($major * 10000 + $minor * 100 + $sub);
}
/**
* 将数字转为版本
* @param int $versionCode 版本的数字表示
* @return string
* @throws BaseException
*/
public static function integerToVersion(int $versionCode): string
{
if (!is_numeric($versionCode) || $versionCode >= 100000) {
throwError('version code Validate Error');
}
$version = array();
$version[0] = (int)($versionCode / 10000);
$version[1] = (int)($versionCode % 10000 / 100);
$version[2] = $versionCode % 100;
return implode('.', $version);
}
/**
* 检查版本格式是否正确
* @param string $version 版本
* @return bool
*/
public static function check(string $version): bool
{
return (bool)preg_match('/^[0-9]{1,3}\.[0-9]{1,2}\.[0-9]{1,2}$/', $version);
}
/**
* 比较两个版本的值
* @param string $version1 版本1
* @param string $version2 版本2
* @return int -1:版本1小于版本2, 0:相等, 1:版本1大于版本2
* @throws BaseException
*/
public static function compare(string $version1, string $version2): int
{
if (!self::check($version1) || !self::check($version2)) {
throwError('version1 or version2 Validate Error');
}
return version_compare($version1, $version2);
}
}
@@ -0,0 +1,101 @@
<?php
// +----------------------------------------------------------------------
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
// +----------------------------------------------------------------------
// | Author: 萤火科技 <admin@yiovo.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace cores\middleware;
use Closure;
use think\Config;
use think\Request;
use think\Response;
/**
* 跨域请求支持
* Class AllowCrossDomain
* @package cores\middleware
*/
class AllowCrossDomain
{
// cookie的所属域名
protected $cookieDomain;
/**
* 构造方法
* AllowCrossDomain constructor.
* @param Config $config
*/
public function __construct(Config $config)
{
$this->cookieDomain = $config->get('cookie.domain', '');
}
/**
* 获取允许跨域的header参数 [自定义]
* @return array
*/
private function getCustomHeader()
{
return [
'Access-Token',
'storeId',
'platform',
];
}
/**
* 获取允许跨域的header参数
* @return array
*/
private function getHeader()
{
$headers = array_merge([
'Authorization', 'Content-Type', 'X-CSRF-TOKEN', 'X-Requested-With',
'If-Match', 'If-Modified-Since', 'If-None-Match', 'If-Unmodified-Since'
], $this->getCustomHeader());
return [
// 允许所有域名访问
'Access-Control-Allow-Origin' => '*',
// 允许cookie跨域访问
'Access-Control-Allow-Credentials' => 'true',
// 预检请求的有效期
'Access-Control-Max-Age' => 1800,
// 允许跨域的方法
'Access-Control-Allow-Methods' => 'GET, POST, PATCH, PUT, DELETE, OPTIONS',
// 跨域请求header头
'Access-Control-Allow-Headers' => implode(',', $headers),
];
}
/**
* 允许跨域请求
* @access public
* @param Request $request
* @param Closure $next
* @param array|null $header
* @return Response
*/
public function handle(Request $request, Closure $next, ?array $header = [])
{
$header = !empty($header) ? array_merge($this->getHeader(), $header) : $this->getHeader();
if (!isset($header['Access-Control-Allow-Origin'])) {
$origin = $request->header('origin');
if ($origin && ('' == $this->cookieDomain || strpos($origin, $this->cookieDomain))) {
$header['Access-Control-Allow-Origin'] = $origin;
} else {
$header['Access-Control-Allow-Origin'] = '*';
}
}
return $next($request)->header($header);
}
}
+66
View File
@@ -0,0 +1,66 @@
<?php
// +----------------------------------------------------------------------
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
// +----------------------------------------------------------------------
// | Author: 萤火科技 <admin@yiovo.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace cores\middleware;
use think\Response;
use think\facade\Log as FacadeLog;
use app\common\library\Log;
/**
* 中间件:应用日志
*/
class AppLog
{
// 访问日志
private static $beginLog = '';
/**
* 前置中间件
* @param \think\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle(\think\Request $request, \Closure $next)
{
// 记录访问日志
if (env('begin_log')) {
$log = $this->getVisitor($request);
$log .= "\r\n" . '[ header ] ' . print_r($request->header(), true);
$log .= "" . '[ param ] ' . print_r($request->param(), true);
$log .= '--------------------------------------------------------------------------------------------';
static::$beginLog = $log;
}
return $next($request);
}
/**
* 记录访问日志
* @param Response $response
*/
public function end(Response $response)
{
FacadeLog::record(static::$beginLog, 'begin');
Log::end();
}
/**
* 获取请求路径信息
* @param \think\Request $request
* @return string
*/
private function getVisitor(\think\Request $request): string
{
$data = [$request->ip(), $request->method(), $request->url(true)];
return implode(' ', $data);
}
}
+52
View File
@@ -0,0 +1,52 @@
<?php
// +----------------------------------------------------------------------
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
// +----------------------------------------------------------------------
// | Author: 萤火科技 <admin@yiovo.com>
// +----------------------------------------------------------------------
namespace cores\traits;
/**
* 错误信息Trait类
*/
trait ErrorTrait
{
/**
* 错误信息
* @var string
*/
protected $error = '';
/**
* 设置错误信息
* @param string $error
* @return bool
*/
protected function setError(string $error): bool
{
$this->error = $error ?: '未知错误';
return false;
}
/**
* 获取错误信息
* @return string
*/
public function getError(): string
{
return $this->error;
}
/**
* 是否存在错误信息
* @return bool
*/
public function hasError(): bool
{
return !empty($this->error);
}
}