znt/module/metric/zen.php

855 lines
30 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
declare(strict_types=1);
/**
* The zen file of metric module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author zhouxin <zhouxin@easysoft.ltd>
* @package metric
* @link https://www.zentao.net
*/
class metricZen extends metric
{
private $validObjects;
/**
* 构建创建度量的数据。
* Build metric data for create.
*
* @access protected
* @return object
*/
protected function buildMetricForCreate()
{
return form::data($this->config->metric->form->create)
->setDefault('createdBy', $this->app->user->account)
->setDefault('createdDate', helper::now())
->get();
}
/**
* 构建编辑度量的数据。
* Build metric data for edit.
*
* @access protected
* @return object
*/
protected function buildMetricForEdit()
{
return form::data($this->config->metric->form->create)
->setDefault('editedBy', $this->app->user->account)
->setDefault('editedDate', helper::now())
->get();
}
/**
* 成功插入度量数据后,其他的额外操作。
* Process after create metric.
*
* @param int $metricID
* @param string $afterCreate
* @param string $from metric|metriclib
* @param string $location
* @access protected
* @return array
*/
protected function responseAfterCreate($metricID, $afterCreate, $from, $location)
{
if($afterCreate == 'back' && $location) return array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true, 'load' => $location);
$location = $this->createLink('metric', 'implement', "metricID=$metricID&from=$from");
$callback = array('name' => 'loadImplement', 'params' => $location);
return array('result' => 'success', 'message' => $this->lang->saveSuccess, 'callback' => $callback);
}
/**
* 成功编辑度量数据后,其他的额外操作。
* Process after edit metric.
*
* @access protected
* @return array
*/
protected function responseAfterEdit($metricID, $afterEdit, $location)
{
if($afterEdit == 'back' && $location) return array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true, 'load' => $location);
$location = $this->createLink('metric', 'implement', "metricID=$metricID");
$callback = array('name' => 'loadImplement', 'params' => $location);
return array('result' => 'success', 'message' => $this->lang->saveSuccess, 'callback' => $callback);
}
/**
* 根据分类后的度量项,准备数据源句柄。
* Prepare the data source handle based on the classified measures.
*
* @param object $calcGroup
* @access protected
* @return array
*/
protected function prepareDataset($calcGroup)
{
$dao = $this->metric->getDAO();
$dataSource = $calcGroup->dataset;
$calcList = $calcGroup->calcList;
foreach($calcList as $calc)
{
$calc->setDAO($dao);
if($calc->useSCM && ($this->config->inQuickon || $this->config->inCompose))
{
$scm = $this->app->loadClass('scm');
$calc->setSCM($scm);
$calc->setGitFoxRepos($this->loadModel('repo')->getGitFoxRepos());
}
}
if(empty($dataSource))
{
$calc = current($calcList);
$calc->setDAO($dao);
return $calc->getStatement();
}
foreach($calcList as $calc)
{
$calc->setHolidays($this->loadModel('holiday')->getList());
$calc->setWeekend(isset($this->config->project->weekend) ? $this->config->project->weekend : 2);
}
$dataset = $this->metric->getDataset($dao);
$fieldList = $this->metric->uniteFieldList($calcList);
return $dataset->$dataSource($fieldList);
}
/**
* 构建模块树数据。
* Prepare module tree data.
*
* @param string $scope
* @param string $stage
* @param array $modules
* @access protected
* @return void
*/
protected function prepareTree($scope, $stage, $modules)
{
$sortedModules = array();
$groupModules = array();
foreach($modules as $module)
{
$groupModules[$module->object][] = $module;
}
foreach(array_keys($this->lang->metric->objectList) as $object)
{
if(isset($groupModules[$object]) and !empty($groupModules[$object]))
{
$sortedModules = array_merge($sortedModules, $groupModules[$object]);
}
}
$moduleTree = array();
foreach($sortedModules as $module)
{
$object = $module->object;
$purpose = $module->purpose;
$moduleTree[$object] = (object)array
(
'id' => $object,
'parent' => '0',
'name' => $this->lang->metric->objectList[$object],
'url' => $this->inlink('browse', "scope=$scope&stage=$stage&param=$object&type=byTree")
);
$moduleTree["{$object}_{$purpose}"] = (object)array
(
'id' => "{$object}_{$purpose}",
'parent' => $object,
'name' => $this->lang->metric->purposeList[$purpose],
'url' => $this->inlink('browse', "scope=$scope&stage=$stage&param={$object}_{$purpose}&type=byTree")
);
}
return $moduleTree;
}
/**
* 构建范围下拉数据。
* Prepare scope picker data.
*
* @access protected
* @return void
*/
protected function prepareScopeList()
{
$scopeList = array();
foreach($this->lang->metric->scopeList as $scope => $scopeText) $scopeList[] = array('key' => $scope, 'text' => $scopeText);
return $scopeList;
}
/**
* 开始计时。
*
* @access private
* @return float
*/
private function startTime()
{
return microtime(true);
}
/**
* 结束计时。
*
* @param float $beginTime
* @access private
* @return string
*/
private function endTime($beginTime)
{
$time = microtime(true) - $beginTime;
return number_format((float)$time, 5, '.', '0');
}
/**
* 获取有效的对象。
* Get valid objects.
*
* @access private
* @return array
*/
private function getValidObjects()
{
if($this->validObjects !== null) return $this->validObjects;
/* 保证逻辑集中这里直接使用sql查询获取数据保证查询性能。*/
/* To ensure logical concentration, here we directly use sql to get data to ensure query performance. */
$productList = $this->dao->select('id,closedDate')->from(TABLE_PRODUCT)
->where('deleted')->eq(0)
->andWhere('status')->ne('closed')
->andWhere('shadow')->eq(0)
->fetchPairs('id', 'closedDate');
$projectList = $this->dao->select('id,closedDate')->from(TABLE_PROJECT)
->where('deleted')->eq(0)
->andWhere('status')->notIN('closed,done')
->andWhere('type')->eq('project')
->fetchPairs('id', 'closedDate');
$executionList = $this->dao->select('id,closedDate')->from(TABLE_EXECUTION)
->where('deleted')->eq(0)
->andWhere('status')->notIN('closed,done')
->andWhere('type')->in('sprint,stage,kanban')
->fetchPairs('id', 'closedDate');
$this->validObjects = array('product' => $productList, 'project' => $projectList, 'execution' => $executionList);
return $this->validObjects;
}
/**
* 计算度量数据。
* Calculate metric data.
*
* @param array $classifiedCalcGroup
* @access protected
* @return void
*/
protected function calculateMetric($classifiedCalcGroup)
{
set_time_limit(0);
$calcBeginTime = $this->startTime();
foreach($classifiedCalcGroup as $calcGroup)
{
try
{
/* 开始计算度量数据。*/
/* Start calculating metric data. */
$beginTime = $this->startTime();
$statement = $this->prepareDataset($calcGroup);
$this->calcMetric($statement, $calcGroup->calcList);
$recordWithCode = $this->prepareMetricRecord($calcGroup->calcList);
$calcTime = $this->endTime($beginTime);
/* 开始插入度量数据。*/
/* Start inserting metric data. */
$beginTime = $this->startTime();
$this->metric->insertMetricLib($recordWithCode);
$executeTime = $this->endTime($beginTime);
/* 记录度量数据计算和插入的时间以及sql语句。*/
/* Record the time of metric data calculation and insertion and sql statement. */
$total = 0;
$codes = '';
$sql = $statement ? $statement->get() : '';
foreach($recordWithCode as $code => $records)
{
$count = count($records);
$codes .= "$code($count), ";
$total += $count;
}
$this->metric->saveLogs("Calculate consumed time(seconds): calc: $calcTime, insert: $executeTime, total: $total, sql: $sql, $codes");
}
catch(Exception $e)
{
a($this->formatException($e));
}
catch(Error $e)
{
a($this->formatException($e));
}
}
/* 记录度量数据计算的总时间。*/
/* Record the total time of metric data calculation. */
$executeTime = $this->endTime($calcBeginTime);
$this->metric->saveLogs("Calculate all consumed time: {$executeTime} seconds");
/* 开始去重。*/
/* Start deduplication. */
$beginTime = $this->startTime();
$metrics = $this->metric->getExecutableMetric();
foreach($metrics as $code)
{
$deduplicationBeginTime = $this->startTime();
$this->metric->deduplication($code);
$executeTime = $this->endTime($deduplicationBeginTime);
$this->metric->saveLogs("Deduplication consumed time: {$executeTime} seconds, the code: $code");
}
/* 重建主键。*/
/* Rebuild the primary key. */
$this->metric->rebuildPrimaryKey();
/* 记录去重的时间。*/
/* Record the time of deduplication. */
$executeTime = $this->endTime($beginTime);
$this->metric->saveLogs("Deduplicate all consumed time: {$executeTime} seconds");
}
/**
* 根据度量项计算的结果,构建可插入表的度量数据。
* Build measurements that can be inserted into tables based on the results of the measurements computed.
*
* @param array $calcList
* @access protected
* @return array
*/
protected function prepareMetricRecord($calcList)
{
/* 获取今天和昨天的日期。*/
/* Get the date of today and yesterday. */
$yesterday = date('j', strtotime('-1 day', strtotime('today')));
$today = date('j');
$options = array('year' => date('Y'), 'month' => date('n'), 'week' => substr(date('oW'), -2), 'day' => "$today,$yesterday");
/* 获取未关闭的对象id。*/
/* Get the id of the unclosed objects. */
$this->getValidObjects();
$validObjects = $this->validObjects;
$records = array();
$yesterday = strtotime('-1 day');
foreach($calcList as $code => $calc)
{
$metric = $this->metric->getByCode($code);
/* 如果度量项是复用的,则计算复用的度量数据。*/
/* If the metric is reused, calculate the reused metric data. */
if($calc->reuse) $this->prepareReuseMetricResult($calc, $options);
/* 判断是否统计关闭时的对象的度量数据。*/
$endWithClosing = preg_match('/_when_closing$/', $code);
$results = $calc->getResult($options);
$records[$code] = array();
if(is_array($results))
{
$scope = $metric->scope;
foreach($results as $record)
{
$record = (object)$record;
if(!is_numeric($record->value) || empty($record->value)) continue;
/* 如果度量项是产品、项目、执行且不统计关闭时的对象的度量数据,则过滤掉已关闭的数据。*/
/* If the metric is product, project, execution, filter out the closed data. */
$closedDate = isset($validObjects[$scope]) && !helper::isZeroDate(zget($validObjects[$scope], $record->$scope, '')) ? $validObjects[$scope][$record->$scope] : null;
$isClosedEarlierThanYesterday = $closedDate ? strtotime($closedDate) < $yesterday : false;
if(!$endWithClosing && $closedDate !== null && $isClosedEarlierThanYesterday) continue;
$record->metricID = $calc->id;
$record->metricCode = $code;
$record->date = helper::now();
$record->system = $metric->scope == 'system' ? 1 : 0;
$records[$code][] = $record;
}
}
}
return $records;
}
/**
* 获取复用的度量数据。
* Prepare metric result for reuse metric.
*
* @param object $calc
* @param array $options
* @access protected
* @return array
*/
protected function prepareReuseMetricResult($calc, $options)
{
$reuseMetrics = array();
foreach($calc->reuseMetrics as $key => $reuseMetric) $reuseMetrics[$key] = $this->metric->getResultByCode($reuseMetric, $options);
$calc->calculate($reuseMetrics);
}
/**
* 获取某度量项某日期的度量数据。
* Get metric record by code and date.
*
* @param string $code
* @param object $calc
* @param string $date
* @param string $type
* @access protected
* @return array
*/
protected function getRecordByCodeAndDate($code, $calc, $date, $type = 'single')
{
$now = helper::now();
$metric = $this->metric->getByCode($code);
$dateType = $this->metric->getDateTypeByCode($code);
if($dateType == 'nodate') return array();
if($type == 'all' && $this->metric->checkHasInferenceOfDate($code, $dateType, $date)) return array();
$records = array();
$dateConfig = $this->metric->parseDateStr($date, $dateType);
$results = $calc->getResult($dateConfig);
if(is_array($results))
{
foreach($results as $record)
{
$record = (object)$record;
if(empty($record->value)) continue;
$record->metricID = $metric->id;
$record->metricCode = $code;
$record->date = $now;
$record->system = $metric->scope == 'system' ? 1 : 0;
$records[] = $record;
}
}
return $records;
}
/**
* 根据度量项编码,初始化度量数据。
* Initialize metric data based on metric code.
*
* @param string $code
* @param string $dateType
* @param string $date
* @access protected
* @return array
*/
protected function initMetricRecords($recordCommon, $scope, $date = 'now')
{
$records = array();
if($scope == 'system')
{
$record = clone $recordCommon;
//$record->system = 1;
$uniqueKey = $this->getUniqueKeyByRecord($record);
$records[$uniqueKey] = $record;
}
else
{
if($date == 'now') $date = helper::now();
$scopeList = $this->metric->getPairsByScopeAndDate($scope, $date);
foreach($scopeList as $key => $value)
{
$record = clone $recordCommon;
$record->$scope = $key;
$uniqueKey = $this->getUniqueKeyByRecord($record);
$records[$uniqueKey] = $record;
}
}
return $records;
}
/**
* 根据度量项范围,构建度量数据的通用字段。
* Build common fields of metric data based on metric scope.
*
* @param int $metricID
* @param string $code
* @param string $date
* @param array $dateValues
* @access protected
* @return array
*/
protected function buildRecordCommonFields($metricID, $code, $date, $dateValues)
{
$record = new stdclass();
$record->value = 0;
$record->date = $date;
$record->calcType = 'cron';
$record->calculatedBy = 'system';
$record = (object)array_merge($dateValues, (array)$record);
return $record;
}
/**
* 补全缺失的度量数据。
* Complete missing metric data.
*
* @param array $records
* @param array $metric
* @access protected
* @return array
*/
protected function completeMissingRecords($records, $metric)
{
$now = helper::now();
$dateValues = $this->metric->parseDateStr($now);
$dateType = $metric->dateType;
$recordCommon = $this->buildRecordCommonFields($metric->id, $metric->code, $now, $dateValues->$dateType);
$initRecords = $this->initMetricRecords($recordCommon, $metric->scope);
foreach($records as $record)
{
$uniqueKey = $this->getUniqueKeyByRecord($record, $metric->scope);
if(!isset($initRecords[$uniqueKey]))
{
$initRecords[$uniqueKey] = $record;
continue;
}
$initRecords[$uniqueKey]->value = $record->value;
}
return array_values($initRecords);
}
/**
* 根据度量数据,获取度量数据的唯一键。
* Get the unique key of metric data based on metric data.
*
* @param object $record
* @param string $scope
* @access protected
* @return string
*/
protected function getUniqueKeyByRecord($record, $scope = '')
{
$record = (array)$record;
$uniqueKeys = array();
$ignoreFields = array('value', 'metricID', 'metricCode', 'calcType', 'calculatedBy', 'date');
if($scope == 'system') $ignoreFields[] = 'id';
foreach($record as $field => $value)
{
if(in_array($field, $ignoreFields) || empty($value)) continue;
$uniqueKeys[] = $field . $value;
}
return implode('_', $uniqueKeys);
}
/**
* 遍历数据,对每个度量项计算每一行数据。
* Calculate metric for every row.
*
* @param array $rows
* @param array $calcList
* @access protected
* @return void
*/
protected function calcMetric($statement, $calcList)
{
if(empty($statement)) return;
$dbType = $this->config->metricDB->type;
if($dbType == 'duckdb')
{
$this->loadModel('bi');
$sql = $statement->get();
$dbh = $this->app->loadDriver('duckdb');
$rows = $dbh->query($sql)->fetchAll();
foreach($rows as $row)
{
foreach($calcList as $calc)
{
if(!$calc->reuse)
{
$record = $this->getCalcFields($calc, $row);
$calc->calculate($record);
}
}
}
return;
}
$statement = $statement->query();
while($row = $statement->fetch())
{
foreach($calcList as $code => $calc)
{
if(!$calc->reuse)
{
$record = $this->getCalcFields($calc, $row);
$calc->calculate($record);
}
}
}
}
protected function getCalcFields($calc, $row)
{
if(!isset($calc->dataset) || empty($calc->dataset)) return (object)$row;
$pureRow = new stdclass();
foreach($calc->fieldList as $field)
{
if(strpos(strtoupper($field), ' AS ') !== false)
{
$pos = strpos(strtoupper($field), ' AS ');
$tag = substr($field, $pos, 4);
$extractField = explode($tag, $field);
$pureField = end($extractField);
$aliasField = $pureField;
}
else
{
$extractField = explode('.', $field);
$pureField = end($extractField);
$aliasField = str_replace('.', '_', $field);
}
$pureRow->$pureField = $row->$aliasField;
}
if(isset($row->defaultHours)) $pureRow->defaultHours = $row->defaultHours;
return $pureRow;
}
/**
* 获取度量项的基本信息。
* Get the basic information of the metric.
*
* @param object $view
* @param string $fields
* @access protected
* @return array
*/
protected function getBasicInfo(object $view, string $fields = 'scope,object,purpose,dateType,name,alias,code,unit,stage')
{
extract((array)$view);
$isOldMetric = $this->metric->isOldMetric($metric);
$unit = $isOldMetric ? $metric->oldUnit : zget($this->lang->metric->unitList, $metric->unit);
$legendBasic = array();
if(strpos($fields, 'scope') !== false) $legendBasic['scope'] = array('name' => $this->lang->metric->scope, 'text' => zget($this->lang->metric->scopeList, $metric->scope));
if(strpos($fields, 'object') !== false) $legendBasic['object'] = array('name' => $this->lang->metric->object, 'text' => zget($this->lang->metric->objectList, $metric->object));
if(strpos($fields, 'purpose') !== false) $legendBasic['purpose'] = array('name' => $this->lang->metric->purpose, 'text' => zget($this->lang->metric->purposeList, $metric->purpose));
if(strpos($fields, 'dateType') !== false) $legendBasic['dateType'] = array('name' => $this->lang->metric->dateType, 'text' => zget($this->lang->metric->dateTypeList, $metric->dateType));
if(strpos($fields, 'name') !== false) $legendBasic['name'] = array('name' => $this->lang->metric->name, 'text' => $metric->name);
if(strpos($fields, 'alias') !== false) $legendBasic['alias'] = array('name' => $this->lang->metric->alias, 'text' => $metric->alias);
if(strpos($fields, 'code') !== false) $legendBasic['code'] = array('name' => $this->lang->metric->code, 'text' => $metric->code);
if(strpos($fields, 'unit') !== false) $legendBasic['unit'] = array('name' => $this->lang->metric->unit, 'text' => $unit);
if($isOldMetric)
{
$legendBasic['collectType'] = array('name' => $this->lang->metric->collectType, 'text' => zget($this->lang->metric->old->collectTypeList, $metric->collectType));
$legendBasic['collectConf'] = array('name' => $this->lang->metric->collectConf, 'text' => $this->metric->getCollectConfText($metric));
}
if(strpos($fields, 'desc') !== false) $legendBasic['desc'] = array('name' => $this->lang->metric->desc, 'text' => $metric->desc);
if(strpos($fields, 'definition') !== false) $legendBasic['definition'] = array('name' => $this->lang->metric->definition, 'text' => $metric->definition);
if(strpos($fields, 'stage') !== false) $legendBasic['stage'] = array('name' => $this->lang->metric->stage, 'text' => zget($this->lang->metric->stageList, $metric->stage));
return $legendBasic;
}
/**
* 获取度量项的创建和编辑信息。
* Get the create and edit information of the metric.
*
* @param object $view
* @param string $fields
* @access protected
* @return array
*/
protected function getCreateEditInfo(object $view, string $fields = 'createdBy,implementedBy,offlineBy,lastEdited')
{
extract((array)$view);
$users = $this->loadModel('user')->getPairs('noletter');
$users['system'] = 'system';
$createEditInfo = array();
if(strpos($fields, 'createdBy') !== false) $createEditInfo['createdBy'] = array('name' => $this->lang->metric->createdBy, 'text' => zget($users, $metric->createdBy) . ($metric->createdBy ? $this->lang->at . $metric->createdDate : ''));
if(strpos($fields, 'implementedBy') !== false) $createEditInfo['implementedBy'] = array('name' => $this->lang->metric->implementedBy, 'text' => zget($users, $metric->implementedBy) . ($metric->implementedBy ? $this->lang->at . $metric->implementedDate : ''));
if(strpos($fields, 'offlineBy') !== false) $createEditInfo['offlineBy'] = array('name' => $this->lang->metric->offlineBy, 'text' => zget($users, $metric->delistedBy) . ($metric->delistedBy ? $this->lang->at . $metric->delistedDate : ''));
if(strpos($fields, 'lastEdited') !== false) $createEditInfo['lastEdited'] = array('name' => $this->lang->metric->lastEdited, 'text' => zget($users, $metric->editedBy) . ($metric->editedBy ? $this->lang->at . $metric->editedDate : ''));
return $createEditInfo;
}
/**
* 获取旧版详情区块。
* Get old metric info panel.
*
* @param int $oldMetricID
* @access protected
* @return array
*/
protected function getOldMetricInfo($oldMetricID)
{
$oldMetric = $this->metric->getOldMetricByID($oldMetricID);
$oldMetricInfo = array();
$oldMetricInfo['scope'] = array('name' => $this->lang->metric->scope, 'text' => zget($this->lang->metric->old->scopeList, $oldMetric->scope));
$oldMetricInfo['object'] = array('name' => $this->lang->metric->object, 'text' => zget($this->lang->metric->old->objectList, $oldMetric->object));
$oldMetricInfo['purpose'] = array('name' => $this->lang->metric->purpose, 'text' => zget($this->lang->metric->old->purposeList, $oldMetric->purpose));
$oldMetricInfo['code'] = array('name' => $this->lang->metric->code, 'text' => $oldMetric->code);
$oldMetricInfo['unit'] = array('name' => $this->lang->metric->unit, 'text' => $oldMetric->unit);
$oldMetricInfo['collectType'] = array('name' => $this->lang->metric->collectType, 'text' => zget($this->lang->metric->old->collectTypeList, $oldMetric->collectType));
$oldMetricInfo['collectConf'] = array('name' => $this->lang->metric->collectConf, 'text' => $this->metric->getCollectConfText($oldMetric));
$oldMetricInfo['definition'] = array('name' => $this->lang->metric->declaration, 'text' => $oldMetric->definition);
$oldMetricInfo['sql'] = array('name' => $this->lang->metric->sqlStatement, 'text' => $oldMetric->configure);
return $oldMetricInfo;
}
/**
* 处理周定时配置。
* Process week configuration.
*
* @param string $dateConf
* @access protected
* @return string
*/
protected function processWeekConf(string $dateConf): string
{
$days = explode(',', $dateConf);
$dateConfNames = array();
foreach($days as $day)
{
$dateConfNames[] = $this->lang->metric->oldMetric->dayNames[$day];
}
$dateConf = implode('、', $dateConfNames);
return $dateConf;
}
/**
* 获取度量数据表的宽度。
* Get width of result table in view.
*
* @param array $headers
* @access protected
* @return int
*/
protected function getViewTableWidth($headers)
{
$width = 0;
foreach($headers as $header) $width += isset($header['width']) ? $header['width'] : 160;
/* Add a little redundancy. */
$width ++;
return $width;
}
/**
* Get pager extra.
*
* @param string $tableWidth
* @access protected
* @return string
*/
public function getPagerExtra($tableWidth)
{
return ($tableWidth > 300) ? '' : 'shortPageSize';
}
/**
* 格式化异常为字符串。
* Format exception to string.
*
* @param Exception $e
* @access protected
* @return string
*/
public function formatException($e)
{
$message = $e->getMessage();
$line = $e->getLine();
$file = $e->getFile();
return "Error: $message in $file on line $line";
}
/**
* 根据后台配置的估算单位对列表赋值。
* Assign unitList['measure'] by custom hourPoint.
*
* @access protected
* @return void
*/
protected function processUnitList()
{
$this->app->loadLang('custom');
$key = zget($this->config->custom, 'hourPoint', '0');
$this->lang->metric->unitList['measure'] = $this->lang->custom->conceptOptions->hourPoint[$key];
}
/**
* 构建操作权限。
* Prepare action priv.
*
* @param array $metrics
* @access protected
* @return array
*/
protected function prepareActionPriv(array $metrics): array
{
$this->loadModel('screen');
foreach($metrics as $metric)
{
$metric->canEdit = $metric->stage == 'wait';
$metric->canImplement = ($metric->stage == 'wait' && !$this->metric->isOldMetric($metric) && $metric->builtin == '0');
$metric->canDelist = $metric->stage == 'released' && $metric->builtin == '0';
$metric->canRecalculate = $metric->stage == 'released' && !empty($metric->dateType) && $metric->dateType != 'nodate';
$metric->isUsed = $this->screen->checkIFChartInUse($metric->id, 'metric');
}
return $metrics;
}
}