307 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			PHP
		
	
	
		
			Executable File
		
	
			
		
		
	
	
			307 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			PHP
		
	
	
		
			Executable File
		
	
| <?php
 | ||
| 
 | ||
| namespace app\service;
 | ||
| 
 | ||
| use Exception;
 | ||
| use think\db\exception\DataNotFoundException;
 | ||
| use think\db\exception\DbException;
 | ||
| use think\db\exception\ModelNotFoundException;
 | ||
| use think\Model;
 | ||
| use think\facade\Db;
 | ||
| use think\Collection;
 | ||
| use think\facade\Env;
 | ||
| use think\facade\Log;
 | ||
| use think\Paginator;
 | ||
| use app\exception\RepositoryException;
 | ||
| 
 | ||
| /**
 | ||
|  * 领域模型-仓储
 | ||
|  *
 | ||
|  * Class Repository
 | ||
|  * @package app\service
 | ||
|  */
 | ||
| class Repository
 | ||
| {
 | ||
|     /**
 | ||
|      * @var array 已加载对象列表
 | ||
|      */
 | ||
|     private static $objects = [];
 | ||
| 
 | ||
|     /**
 | ||
|      * @var Model 模型对象
 | ||
|      */
 | ||
|     protected $model;
 | ||
| 
 | ||
|     // 布尔值数字关系
 | ||
|     public const BOOL_FALSE = 0;
 | ||
|     public const BOOL_TRUE  = 1;
 | ||
| 
 | ||
|     /**
 | ||
|      * 获取当前子对象实列(单例形式返回)
 | ||
|      *
 | ||
|      * @param  Model|null  $model  模型对象。未指定则自动获取
 | ||
|      * @return mixed
 | ||
|      */
 | ||
|     public static function getInstance(Model $model = null)
 | ||
|     {
 | ||
|         $class = get_called_class();
 | ||
| 
 | ||
|         if (isset(self::$objects[$class]) && self::$objects[$class] !== null) {
 | ||
|             return self::$objects[$class];
 | ||
|         }
 | ||
| 
 | ||
|         $obj = new $class();
 | ||
|         if ($model) {
 | ||
|             $obj->model = $model;
 | ||
|         } elseif (strpos($class, '\\repository') > 0) {
 | ||
|             $model = str_replace('\\repository', '\\model', $class);
 | ||
|             //去掉末尾Repository app\model\AccountRepository  =》 app\model\Account
 | ||
|             $model = substr($model, 0, strlen($model) - strlen('Repository'));
 | ||
|             if (class_exists($model)) {
 | ||
|                 $obj->model = new $model;
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         self::$objects[$class] = $obj;
 | ||
|         return $obj;
 | ||
|     }
 | ||
| 
 | ||
|     /**
 | ||
|      * @param  callable  $callback
 | ||
|      * @param  mixed  $failReturn
 | ||
|      * @param  bool  $transaction
 | ||
|      * @return mixed
 | ||
|      * @throws RepositoryException
 | ||
|      */
 | ||
|     protected function access(callable $callback, $failReturn, bool $transaction = false)
 | ||
|     {
 | ||
|         $exception = null;
 | ||
|         try {
 | ||
|             if ($transaction) {
 | ||
|                 Db::startTrans();
 | ||
|             }
 | ||
| 
 | ||
|             $r = $callback();
 | ||
| 
 | ||
|             if ($transaction) {
 | ||
|                 Db::commit();
 | ||
|             }
 | ||
| 
 | ||
|             if ($r) {
 | ||
|                 return $r;
 | ||
|             }
 | ||
| 
 | ||
|             if ($failReturn instanceof Exception) {
 | ||
|                 return null;
 | ||
|             }
 | ||
| 
 | ||
|             return $failReturn;
 | ||
|         } catch (Exception $e) {
 | ||
|             if ($transaction) {
 | ||
|                 Db::rollback();
 | ||
|             }
 | ||
| 
 | ||
|             if ($e instanceof RepositoryException) {
 | ||
|                 throw $e;
 | ||
|             }
 | ||
| 
 | ||
|             $name = 'Domain - Repository - 未知错误';
 | ||
| 
 | ||
|             $traces = $e->getTrace();
 | ||
| 
 | ||
|             foreach ($traces as $i => $trace) {
 | ||
|                 if (!empty($trace['class']) && $trace['class'] === Repository::class && $trace['function'] === 'access') {
 | ||
|                     $trace = $traces[$i - 2] ?? null;
 | ||
|                     break;
 | ||
|                 }
 | ||
|             }
 | ||
| 
 | ||
|             if (!empty($trace) && !empty($trace['file'][1]) && !empty($trace['line'])) {
 | ||
|                 $parts = explode('application\\', $trace['file']);
 | ||
|                 if (!empty($parts[1])) {
 | ||
|                     $name = $parts[1].':'.$trace['line'];
 | ||
|                 } else {
 | ||
|                     $name = $trace['file'].':'.$trace['line'];
 | ||
|                 }
 | ||
|             }
 | ||
| 
 | ||
|             $exception = $e;
 | ||
| 
 | ||
|             $line = '['.$name.'] '.$e->getMessage();
 | ||
|             Log::error($line);
 | ||
| 
 | ||
|             throw new RepositoryException('Repository异常'.(Env::get('app_debug') ? ': '.$line : ''), 5009);
 | ||
|         } finally {
 | ||
|             if ($exception && $failReturn instanceof RepositoryException) {
 | ||
|                 if (Env::get('app_debug')) {
 | ||
|                     $failReturn = new RepositoryException($failReturn->getMessage().': '.$exception->getMessage(),
 | ||
|                         $failReturn->getCode());
 | ||
|                 }
 | ||
|                 throw $failReturn;
 | ||
|             }
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     /**
 | ||
|      * 获取当前model对象
 | ||
|      *
 | ||
|      * @return Model
 | ||
|      */
 | ||
|     public function getModel(): Model
 | ||
|     {
 | ||
|         return $this->model;
 | ||
|     }
 | ||
| 
 | ||
|     /**
 | ||
|      * 根据条件查询列表
 | ||
|      *
 | ||
|      * @param  array  $where  查询条件
 | ||
|      * @param  array  $fields  查询字段 []表示全部
 | ||
|      * @param  int  $page  默认第一页 0不限制
 | ||
|      * @param  int  $limit  限制条数 0不限制
 | ||
|      * @param  callable|null  $callback 更为复杂的条件 使用闭包查询
 | ||
|      * @return array
 | ||
|      * @throws RepositoryException
 | ||
|      */
 | ||
|     public function findList(array $where = [], array $fields = [], int $page = 1, int $limit = 0, callable $callback = null, array $order = []): ?array
 | ||
|     {
 | ||
|         $failData = [
 | ||
|             'total'   => 0,
 | ||
|             'current' => $page,
 | ||
|             'size'    => $limit,
 | ||
|             'list'    => new Collection(),
 | ||
|         ];
 | ||
|         return $this->access(function () use ($where, $fields, $page, $limit, $callback, $order) {
 | ||
|             return $this->model->findList($where, $fields, $page, $limit, $callback, $order);
 | ||
|         }, $failData);
 | ||
|     }
 | ||
| 
 | ||
|     /**
 | ||
|      * 根据条件查询列表[带分页 适用于后台]
 | ||
|      *
 | ||
|      * @param  array  $data  查询数据
 | ||
|      * @param  array  $pageParams  分页参数
 | ||
|      * @param  callable|null  $callback 复杂查询条件  使用闭包查询
 | ||
|      * @return Paginator
 | ||
|      */
 | ||
|     public function findListWithPaginate(array $data = [], array $pageParams = [], callable $callback = null): Paginator
 | ||
|     {
 | ||
|         return $this->model->findListWithPaginate($data, $pageParams, $callback);
 | ||
|     }
 | ||
| 
 | ||
|     /**
 | ||
|      * 根据主键 ID 查询
 | ||
|      *
 | ||
|      * @param  int  $id  ID
 | ||
|      * @param  array  $fields  要返回的字段,默认全部
 | ||
|      * @return Mixed
 | ||
|      * @throws RepositoryException
 | ||
|      */
 | ||
|     public function findById(int $id, array $fields = [], callable $callback = null)
 | ||
|     {
 | ||
|         return $this->access(function () use ($id, $fields, $callback) {
 | ||
|             return $this->model->findById($id, $fields, $callback);
 | ||
|         }, null);
 | ||
|     }
 | ||
| 
 | ||
|     /**
 | ||
|      * @param  array  $where
 | ||
|      * @param  array  $fields
 | ||
|      * @return array|Model|null
 | ||
|      * @throws DataNotFoundException
 | ||
|      * @throws DbException
 | ||
|      * @throws ModelNotFoundException
 | ||
|      */
 | ||
|     public function findOneByWhere(array $where, array $fields = [])
 | ||
|     {
 | ||
|         return $this->model->field($fields)->where($where)->find();
 | ||
|     }
 | ||
| 
 | ||
|     /**
 | ||
|      * 创建
 | ||
|      *
 | ||
|      * @param  array  $data  数据
 | ||
|      * @return Model
 | ||
|      * @throws RepositoryException
 | ||
|      */
 | ||
|     public function create(array $data): Model
 | ||
|     {
 | ||
|         return $this->access(function () use ($data) {
 | ||
|             return $this->model->create($data);
 | ||
|         }, new RepositoryException('创建失败'));
 | ||
|     }
 | ||
| 
 | ||
|     /**
 | ||
|      * 更新
 | ||
|      *
 | ||
|      * @param  array  $data  数据
 | ||
|      * @param  array  $where  条件
 | ||
|      * @return bool|Exception
 | ||
|      * @throws RepositoryException
 | ||
|      */
 | ||
|     public function update(array $data, array $where): bool
 | ||
|     {
 | ||
|         return $this->access(function () use ($data, $where) {
 | ||
|             return $this->model->where($where)->find()->save($data);
 | ||
|         }, new RepositoryException('更新失败'));
 | ||
|     }
 | ||
| 
 | ||
|     /**
 | ||
|      * 删除
 | ||
|      *
 | ||
|      * @param  array  $where  删除条件
 | ||
|      * @param  bool  $softDelete  是否软删除 默认false
 | ||
|      * @param  string  $softDeleteTime  删除时间 softDelete=true时有效
 | ||
|      * @param  string  $softDeleteField  软删除字段 softDelete=true时有效
 | ||
|      * @return bool
 | ||
|      * @throws RepositoryException
 | ||
|      */
 | ||
|     public function delete(array $where, bool $softDelete = false, string $softDeleteTime = '', string $softDeleteField = 'deleted_at'): bool
 | ||
|     {
 | ||
|         return $this->access(function () use ($where, $softDelete, $softDeleteField, $softDeleteTime) {
 | ||
|             // 注意:如果model中引入了软删除trait,$softDelete又设置false 将无法正确删除
 | ||
|             return $this->model->where($where)
 | ||
|                 ->when($softDelete, function ($q) use ($softDeleteField, $softDeleteTime) {
 | ||
|                     $softDeleteTime = $softDeleteTime ?: date('Y-m-d H:i:s');
 | ||
|                     $q->useSoftDelete($softDeleteField, $softDeleteTime);
 | ||
|                 })->delete();
 | ||
|         }, false);
 | ||
|     }
 | ||
| 
 | ||
|     /**
 | ||
|      * 排序
 | ||
|      *
 | ||
|      * @param  int  $id 排序ID
 | ||
|      * @param  string  $type 排序类型 向上、向下
 | ||
|      * @param  int  $num 移动位数
 | ||
|      * @param  string  $listType 列表的排序类型  降序|升序
 | ||
|      * @param  array  $where 额外条件 格式如:
 | ||
|      *                                  $map[] = ['name','like','think'];
 | ||
|      *                                  $map[] = ['status','=',1];
 | ||
|      * @return array
 | ||
|      */
 | ||
|     public function sort(int $id,string $type,int $num,string $listType, array $where = []): array
 | ||
|     {
 | ||
|         return $this->model->sort($id, $type, $num, $listType, $where);
 | ||
|     }
 | ||
| 
 | ||
|     /**
 | ||
|      * 日志记录
 | ||
|      *
 | ||
|      * @param  string  $msg
 | ||
|      * @param  Exception|null  $e
 | ||
|      * @param  string  $level
 | ||
|      * @param  string  $channel
 | ||
|      */
 | ||
|     public static function log(string $msg, Exception $e = null, string $level = 'error', string $channel = 'file')
 | ||
|     {
 | ||
|         if ($e != null) {
 | ||
|             $msg = sprintf("[%s]%s:%s %s", $msg, $e->getFile(), $e->getLine(), $e->getMessage());
 | ||
|         } else {
 | ||
|             $msg = sprintf("%s", $msg);
 | ||
| 
 | ||
|         }
 | ||
|         Log::channel($channel)->$level($msg);
 | ||
|     }
 | ||
| }
 |