* Date: 2019-07-31 * Time: 09:22 */ namespace common\libs\logging\operate; use common\helpers\Form; use common\helpers\Tool; use common\libs\logging\operate\provider\BatchProvider; use common\libs\logging\operate\provider\SingleProvider; use common\models\User; use SebastianBergmann\CodeCoverage\Report\PHP; use yii\base\Component; use yii\base\ErrorException; use yii\base\Exception; use yii\helpers\ArrayHelper; abstract class AbstractOperate extends Component { /** * 保存日志的model * @var null */ public $logModel = null; /** * 保存日志的form * @var null */ public $logForm = null; /** * 用于查询保存存数据的model * @var */ public $fetchClass = null; /** * @var array */ public $attrLabels = []; /** * @var null */ public $saveBeforeContent = null; /** * @var null */ public $saveAfterContent = null; /** * @var array */ public $columns = []; /** * @var null */ public $data = null; /** * @var null */ protected $_dataType = null; /** * @var array */ public $params = []; /** * @var bool */ protected $_isBatch = false; /** * @var string */ protected $_batchField = 'ID'; /** * @var string */ protected $_optObjField = 'USER_ID'; /** * @var null */ protected $_errors = null; /** * Factory constructor. * @param array $config */ public function __construct($config = []) { parent::__construct($config); } /** * @param array $format * @param array $params * @return array */ public function paramsFormat(array $format, array $params = []){ if(!$params){ return $format; } return ArrayHelper::merge($format, $params); } /** * @return |null */ public function getDataType(){ return $this->_dataType; } /** * @param $value * @return $this */ public function setDataType($value){ $this->_dataType = $value; return $this; } /** * @return mixed */ public function getIsBatch(){ return $this->_isBatch; } /** * @param $value * @return $this */ public function setIsBatch($value){ $this->_isBatch = $value; return $this; } /** * @param $value * @return $this */ public function setBatchField($value){ $this->_batchField = $value; return $this; } /** * @param $value * @return $this */ public function setOptObjField($value){ $this->_optObjField = $value; return $this; } /** * @param $value * @return $this */ public function setSaveBeforeContent($value){ $this->saveBeforeContent = $value; return $this; } /** * @param $value * @return $this */ public function setSaveAfterContent($value){ $this->saveAfterContent = $value; return $this; } /** * 分发器 * @param $method * @return array|null */ public function dispatcher($method){ $initParams = $this->paramsFormat([ 'data' => $this->data, 'fetchClass' => $this->fetchClass, 'dataType' => $this->_dataType, 'attrLabels' => $this->attrLabels, ], $this->params); if($this->_isBatch){ $entryClass = new BatchProvider($initParams); }else{ $entryClass = new SingleProvider($initParams); } if(method_exists($entryClass, $method)){ $entryClass->{$method}(); return $entryClass->result; } return null; } /** * 更新之前 * @param null $data * @param null $dataType * @param array $params * @return $this|bool */ public function beforeUpdate($data = null, $dataType = null, array $params = []){ if(!$data){ return false; } $this->data = $data; $this->_dataType = $dataType; $this->params = $params; $this->saveBeforeContent = $this->dispatcher('beforeUpdate'); return $this; } /** * @param $data * @param null $dataType * @param array $params * @return $this|bool */ public function afterUpdate($data, $dataType = null, array $params = []){ if(!$data){ return false; } $this->data = $data; $this->_dataType = $dataType; $this->params = $params; $this->saveAfterContent = $this->dispatcher('afterUpdate'); return $this; } /** * @param $data * @param null $dataType * @param array $params * @return $this|bool */ public function beforeDelete($data, $dataType = null, array $params = []){ if(!$data){ return false; } $this->data = $data; $this->params = $params; $this->_dataType = $dataType; $this->saveBeforeContent = $this->dispatcher('beforeDelete'); return $this; } /** * @param $data * @param string $dataType * @param array $params * @return $this|bool */ public function afterInsert($data, $dataType = 'ID', array $params = []){ if(!$data || !$dataType){ return false; } $this->data = $data; $this->params = $params; $this->_dataType = $dataType; $this->saveAfterContent = $this->dispatcher('afterInsert'); return $this; } /** * @return $this */ public function clean(){ $this->columns = []; return $this; } /** * @param array $params * @return bool */ public function save(array $params = []){ $this->setColumn($params); if(!$this->columns){ return false; } // $taskKey = \Yii::$app->swooleAsyncTimer->asyncHandle('log/save', [ // 'logModel' => $this->logModel, // 'logForm' => $this->logForm, // 'isBatch' => $this->_isBatch, // 'batchField' => $this->_batchField, // 'optObjField' => $this->_optObjField, // 'columns' => $this->columns, // ]); // if ($taskKey === false) { // $this->setErrors('异步请求日志服务失败'); // return false; // } if($this->_isBatch){ $modelClass = $this->logModel; // mongodb 的批量添加要特殊处理 $db = $modelClass::getDb(); $collectionName = $modelClass::collectionName(); if(is_array($collectionName)){ $collection = count($collectionName) > 1 ? $collectionName[1] : $collectionName[0]; }else{ $collection = $collectionName; } $batchList = []; $saveAfterContent = $this->columns[0]['save_after_content'] ?? []; $saveBeforeContent = $this->columns[0]['save_before_content'] ?? []; if( $saveBeforeContent && $saveAfterContent ) { $saveBeforeContentList = []; foreach ($saveBeforeContent as $key => $beforeItem) { if (isset($beforeItem[$this->_batchField])) { $saveBeforeContentList[$beforeItem[$this->_batchField]['value']] = $beforeItem; } else { $saveBeforeContentList[$key] = $beforeItem; } } foreach ($saveAfterContent as $key => $afterItem) { if (isset($afterItem[$this->_batchField])) { if (!isset($saveBeforeContentList[$afterItem[$this->_batchField]['value']])) continue; $beforeItem = $saveBeforeContentList[$afterItem[$this->_batchField]['value']]; } else { if (!isset($saveBeforeContentList[$key])) continue; $beforeItem = $saveBeforeContentList[$key]; } $mergeData = [ 'save_before_content' => $beforeItem, 'save_after_content' => $afterItem, ]; //记录opt_obj_id 和 opt_obj_name @todo待优化 $this->_fixOptObjField($collection, $beforeItem); $batchList[] = array_merge($this->columns[0], $mergeData); } unset($saveBeforeContentList); }else if( $saveBeforeContent && !$saveAfterContent ) { foreach ($saveBeforeContent as $key => $beforeItem) { $mergeData = [ 'save_before_content' => $beforeItem, 'save_after_content' => null, ]; //记录opt_obj_id 和 opt_obj_name @todo待优化 $this->_fixOptObjField($collection, $beforeItem); $batchList[] = array_merge($this->columns[0], $mergeData); } }else if( !$saveBeforeContent && $saveAfterContent ) { foreach ($saveAfterContent as $key => $afterItem) { $mergeData = [ 'save_before_content' => null, 'save_after_content' => $afterItem, ]; //记录opt_obj_id 和 opt_obj_name @todo待优化 $this->_fixOptObjField($collection, $afterItem); $batchList[] = array_merge($this->columns[0], $mergeData); } }else { $this->setErrors('日志批量写入错误'); return false; } unset($saveBeforeContent, $saveAfterContent); if (!$batchList) { $this->setErrors('数据无效'); return false; } if(!$db->createCommand()->batchInsert($collection, $batchList)){ $this->setErrors('日志批量写入失败'); return false; } // if(!$db->createCommand()->batchInsert($collection, $this->columns)){ // $this->setErrors('日志批量写入失败'); // return false; // } return true; }else { $modelFormClass = $this->logForm; $logForm = new $modelFormClass($this->columns[0]); if(!$logForm->add()){ $this->setErrors($logForm->getErrors()); return false; } } return true; } /** * 异步保存(异步控制器调用此方法) * @return bool */ public function asyncSave() { try { if( !$this->columns || !isset($this->columns[0]) ) { throw new Exception('储存的数据错误'); } if ($this->_isBatch) { $this->_batchInsert(); } else { $modelFormClass = $this->logForm; $logForm = new $modelFormClass($this->columns[0]); if (!$logForm->add()) { throw new Exception(Form::formatErrorsForApi($logForm->getErrors())); } } return true; }catch (\Exception $e) { $this->setErrors($e->getMessage()); return false; } } /** * console里面记日志,直接保存,无需调用异步,因为本身就是异步 * @param array $params * @return bool */ public function saveByConsole(array $params = []){ $this->setColumn($params); if(!$this->columns){ return false; } try { if( !$this->columns || !isset($this->columns[0]) ) { throw new Exception('储存的数据错误'); } if ($this->_isBatch) { $this->_batchInsert(); } else { $modelFormClass = $this->logForm; $logForm = new $modelFormClass($this->columns[0]); if (!$logForm->add()) { throw new Exception(Form::formatErrorsForApi($logForm->getErrors())); } } }catch (\Exception $e) { $this->setErrors($e->getMessage()); return false; } return true; } /** * 批量写入 * @return bool * @throws ErrorException * @throws \yii\base\InvalidConfigException */ protected function _batchInsert() { $modelClass = $this->logModel; // mongodb 的批量添加要特殊处理 $db = $modelClass::getDb(); $collectionName = $modelClass::collectionName(); if(is_array($collectionName)){ $collection = count($collectionName) > 1 ? $collectionName[1] : $collectionName[0]; }else{ $collection = $collectionName; } $batchList = []; $saveAfterContent = $this->columns[0]['save_after_content'] ?? []; $saveBeforeContent = $this->columns[0]['save_before_content'] ?? []; if( $saveBeforeContent && $saveAfterContent ) { $saveBeforeContentList = []; foreach ($saveBeforeContent as $key => $beforeItem) { if (isset($beforeItem[$this->_batchField])) { $saveBeforeContentList[$beforeItem[$this->_batchField]['value']] = $beforeItem; } else { $saveBeforeContentList[$key] = $beforeItem; } } foreach ($saveAfterContent as $key => $afterItem) { if (isset($afterItem[$this->_batchField])) { if (!isset($saveBeforeContentList[$afterItem[$this->_batchField]['value']])) continue; $beforeItem = $saveBeforeContentList[$afterItem[$this->_batchField]['value']]; } else { if (!isset($saveBeforeContentList[$key])) continue; $beforeItem = $saveBeforeContentList[$key]; } $mergeData = [ 'save_before_content' => $beforeItem, 'save_after_content' => $afterItem, ]; //记录opt_obj_id 和 opt_obj_name @todo待优化 $this->_fixOptObjField($collection, $beforeItem); $batchList[] = array_merge($this->columns[0], $mergeData); } unset($saveBeforeContentList); }else if( $saveBeforeContent && !$saveAfterContent ) { foreach ($saveBeforeContent as $key => $beforeItem) { $mergeData = [ 'save_before_content' => $beforeItem, 'save_after_content' => null, ]; //记录opt_obj_id 和 opt_obj_name @todo待优化 $this->_fixOptObjField($collection, $beforeItem); $batchList[] = array_merge($this->columns[0], $mergeData); } }else if( !$saveBeforeContent && $saveAfterContent ) { foreach ($saveAfterContent as $key => $afterItem) { $mergeData = [ 'save_before_content' => null, 'save_after_content' => $afterItem, ]; //记录opt_obj_id 和 opt_obj_name @todo待优化 $this->_fixOptObjField($collection, $afterItem); $batchList[] = array_merge($this->columns[0], $mergeData); } }else { throw new ErrorException('日志批量写入错误'); } unset($saveBeforeContent, $saveAfterContent); if (!$batchList) { throw new ErrorException('数据无效' . var_export($this->columns, true)); } if(!$db->createCommand()->batchInsert($collection, $batchList)){ throw new ErrorException('日志批量写入失败'); } return true; } /** * 修正批量时的补操作对象信息 * @param $collection * @param $item * @return bool * @throws \yii\base\InvalidConfigException */ private function _fixOptObjField($collection, $item) { if( $collection !== 'ar_bonus_admin_log') { return false; } if( !isset( $item[$this->_optObjField] ) ) { return false; } $this->columns[0]['opt_obj_id'] = $item[$this->_optObjField]['value']; if( isset( $item['USER_NAME'] ) ) { $this->columns[0]['opt_obj_name'] = $item['USER_NAME']['value']; }else { //@todo $userOne = User::find()->select(['USER_NAME'])->where('ID=:ID', ['ID'=>$item[$this->_optObjField]['value']])->asArray()->one(); if( $userOne && isset($userOne['USER_NAME']) ) { $this->columns[0]['opt_obj_name'] = $userOne['USER_NAME']; } } return true; } /** * @param $error * @return $this */ public function setErrors($error){ $this->_errors = $error; return $this; } /** * @return array|null */ public function getErrors(){ return $this->_errors; } }