tao.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. <?php
  2. /**
  3. * The tao file of metric module of ZenTaoPMS.
  4. *
  5. * @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
  6. * @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
  7. * @author zhouxin <zhouxin@easysoft.ltd>
  8. * @package metric
  9. * @link https://www.zentao.net
  10. */
  11. class metricTao extends metricModel
  12. {
  13. /**
  14. * 请求度量项数据列表。
  15. * Fetch metric list.
  16. *
  17. * @param string $scope
  18. * @param string $stage
  19. * @param string $object
  20. * @param string $purpose
  21. * @param string $query
  22. * @param stirng $sort
  23. * @param object $pager
  24. * @access protected
  25. * @return void
  26. */
  27. protected function fetchMetrics($scope, $stage = 'all', $object = '', $purpose = '', $query = '', $sort = 'id_desc', $pager = null)
  28. {
  29. return $this->dao->select('*')->from(TABLE_METRIC)
  30. ->where('deleted')->eq('0')
  31. ->andWhere('scope')->eq($scope)
  32. ->andWhere('object')->in(array_keys($this->lang->metric->objectList))
  33. ->beginIF($query)->andWhere($query)->fi()
  34. ->beginIF($stage != 'all')->andWhere('stage')->eq($stage)->fi()
  35. ->beginIF(!empty($object))->andWhere('object')->eq($object)->fi()
  36. ->beginIF(!empty($purpose))->andWhere('purpose')->eq($purpose)->fi()
  37. ->beginIF($this->config->edition == 'open')->andWhere('object')->notIN('feedback,ticket,issue,risk,demand')->fi()
  38. ->beginIF($this->config->edition == 'biz')->andWhere('object')->notIN('issue,risk,demand')->fi()
  39. ->beginIF($this->config->edition == 'ipd' && $this->config->vision == 'rnd')->andWhere('code')->notIN($this->config->metric->orMetricList)->fi()
  40. ->beginIF($this->config->systemMode == 'light')->andWhere('code')->notIN($this->config->metric->waterfallCode)->fi()
  41. ->beginIF(!helper::hasFeature('program'))->andWhere('object')->notIN('program')->fi()
  42. ->beginIF(!helper::hasFeature('devops'))->andWhere('object')->notIN('host,deployment,node,code,codebase,pipeline,artifact')->fi()
  43. ->beginIF($sort)->orderBy($sort)->fi()
  44. ->beginIF($pager)->page($pager)->fi()
  45. ->fetchAll();
  46. }
  47. /**
  48. * 根据范围获取度量项。
  49. * Fetch metric by scope.
  50. *
  51. * @param string|array $scopes
  52. * @param int $limit
  53. * @access protected
  54. * @return array
  55. */
  56. protected function fetchMetricsByScope($scopes, $limit = -1)
  57. {
  58. if(!is_array($scopes)) $scopes = array($scopes);
  59. $metrics = $this->dao->select('*')->from(TABLE_METRIC)
  60. ->where('deleted')->eq('0')
  61. ->andWhere('scope')->in($scopes)
  62. ->andWhere('object')->in(array_keys($this->lang->metric->objectList))
  63. ->beginIF($limit > 0)->limit($limit)->fi()
  64. ->fetchAll('scope');
  65. return $metrics;
  66. }
  67. /**
  68. * 根据编号获取度项。
  69. * Fetch metric by id.
  70. *
  71. * @param string $code
  72. * @param array|string $fields
  73. * @access protected
  74. * @return mixed
  75. */
  76. protected function fetchMetricByID($code, $fields = '*')
  77. {
  78. if(is_array($fields)) $fields = implode(',', $fields);
  79. $metric = $this->dao->select($fields)->from(TABLE_METRIC)->where('code')->eq($code)->fetch();
  80. return $metric;
  81. }
  82. /**
  83. * 根据编号列表获取度项。
  84. * Fetch metric list by id list.
  85. *
  86. * @param array $metricIDList
  87. * @access protected
  88. * @return array
  89. */
  90. protected function fetchMetricsByIDList($metricIDList)
  91. {
  92. return $this->dao->select('*')->from(TABLE_METRIC)
  93. ->where('deleted')->eq(0)
  94. ->andWhere('id')->in($metricIDList)
  95. ->fetchAll();
  96. }
  97. /**
  98. * 根据编号列表获取度项。
  99. * Fetch metric list by id list.
  100. *
  101. * @param array $metricIDList
  102. * @access protected
  103. * @return array
  104. */
  105. protected function fetchMetricsByCodeList($codeList)
  106. {
  107. return $this->dao->select('*')->from(TABLE_METRIC)
  108. ->where('deleted')->eq(0)
  109. ->andWhere('code')->in($codeList)
  110. ->fetchAll();
  111. }
  112. /**
  113. * 根据度量项编码获取度量项数据。
  114. * Fetch metric by code.
  115. *
  116. * @param string $code
  117. * @access protected
  118. * @return object|false
  119. */
  120. protected function fetchMetricByCode($code, $fieldList = '*')
  121. {
  122. return $this->dao->select($fieldList)->from(TABLE_METRIC)
  123. ->where('code')->eq($code)
  124. ->fetch();
  125. }
  126. /**
  127. * 根据筛选条件获取度量项数据。
  128. * Fetch metric by filter.
  129. *
  130. * @param array $filters
  131. * @param string $stage
  132. * @access protected
  133. * @return array
  134. */
  135. protected function fetchMetricsWithFilter($filters, $stage = 'all')
  136. {
  137. $scopes = null;
  138. $objects = null;
  139. $purposes = null;
  140. if(isset($filters['scope']) && !empty($filters['scope'])) $scopes = implode(',', $filters['scope']);
  141. if(isset($filters['object']) && !empty($filters['object'])) $objects = implode(',', $filters['object']);
  142. if(isset($filters['purpose']) && !empty($filters['purpose'])) $purposes = implode(',', $filters['purpose']);
  143. $metrics = $this->dao->select('*')->from(TABLE_METRIC)
  144. ->where('deleted')->eq('0')
  145. ->beginIF($stage != 'all')->andWhere('stage')->eq($stage)->fi()
  146. ->beginIF(!empty($scopes))->andWhere('scope')->in($scopes)->fi()
  147. ->beginIF(!empty($objects))->andWhere('object')->in($objects)->fi()
  148. ->beginIF(!empty($purposes))->andWhere('purpose')->in($purposes)->fi()
  149. ->beginIF($this->config->edition == 'open')->andWhere('object')->notIN('feedback,ticket,issue,risk,demand')->fi()
  150. ->beginIF($this->config->edition == 'biz')->andWhere('object')->notIN('issue,risk,demand')->fi()
  151. ->beginIF($this->config->edition == 'ipd' && $this->config->vision == 'rnd')->andWhere('code')->notIN($this->config->metric->orMetricList)->fi()
  152. ->beginIF($this->config->systemMode == 'light')->andWhere('code')->notIN($this->config->metric->waterfallCode)
  153. ->beginIF(!helper::hasFeature('program'))->andWhere('object')->notIN('program')->fi()
  154. ->beginIF(!helper::hasFeature('devops'))->andWhere('object')->notIN('host')->fi()
  155. ->fetchAll();
  156. return $metrics;
  157. }
  158. /**
  159. * 请求我的收藏度量项。
  160. * Fetch my collect metrics.
  161. *
  162. * @param string $stage
  163. * @access protected
  164. * @return array
  165. */
  166. protected function fetchMetricsByCollect($stage)
  167. {
  168. return $this->dao->select('*')->from(TABLE_METRIC)
  169. ->where('deleted')->eq('0')
  170. ->andWhere('collector')->like("%,{$this->app->user->account},%")
  171. ->beginIF($stage!= 'all')->andWhere('stage')->eq($stage)->fi()
  172. ->beginIF($this->config->edition == 'open')->andWhere('object')->notIN('feedback,ticket,issue,risk,demand')->fi()
  173. ->beginIF($this->config->edition == 'biz')->andWhere('object')->notIN('issue,risk,demand')->fi()
  174. ->beginIF($this->config->edition == 'ipd' && $this->config->vision == 'rnd')->andWhere('code')->notIN($this->config->metric->orMetricList)->fi()
  175. ->beginIF($this->config->systemMode == 'light')->andWhere('code')->notIN($this->config->metric->waterfallCode)
  176. ->beginIF(!helper::hasFeature('program'))->andWhere('object')->notIN('program')->fi()
  177. ->beginIF(!helper::hasFeature('devops'))->andWhere('object')->notIN('host')->fi()
  178. ->fetchAll();
  179. }
  180. /**
  181. * 请求模块数据。
  182. * Fetch module data.
  183. *
  184. * @param string $scope
  185. * @access protected
  186. * @return void
  187. */
  188. protected function fetchModules($scope)
  189. {
  190. return $this->dao->select('object, purpose')->from(TABLE_METRIC)
  191. ->where('deleted')->eq('0')
  192. ->andWhere('scope')->eq($scope)
  193. ->beginIF($this->config->edition == 'open')->andWhere('object')->notIN('feedback,ticket,issue,risk,demand')->fi()
  194. ->beginIF($this->config->edition == 'biz')->andWhere('object')->notIN('issue,risk,demand')->fi()
  195. ->beginIF(!helper::hasFeature('program'))->andWhere('object')->notIN('program')->fi()
  196. ->beginIF(!helper::hasFeature('devops'))->andWhere('object')->notIN('host')->fi()
  197. ->groupBy('object, purpose')
  198. ->fetchAll();
  199. }
  200. /**
  201. * 获取范围对象类型以构建分页对象。
  202. * Get object list with page.
  203. *
  204. * @param string $code
  205. * @param string $scope
  206. * @param object $pager
  207. * @access protected
  208. * @return array|false
  209. */
  210. protected function getObjectsWithPager($metric, $query, $pager = null, $extra = array())
  211. {
  212. $code = $metric->code;
  213. $scope = $metric->scope;
  214. $dateType = $metric->dateType;
  215. if($scope == 'system') return false;
  216. $scopeObjects = $this->dao->select($scope)->from(TABLE_METRICLIB)
  217. ->where('metricCode')->eq($code)
  218. ->beginIF(!empty($extra))->andWhere($scope)->in($extra)->fi();
  219. $scopeObjects = $this->processDAOWithDate($scopeObjects, $query, $dateType)->fetchPairs();
  220. $objects = null;
  221. if($scope == 'product')
  222. {
  223. $objects = $this->dao->select('id')->from(TABLE_PRODUCT)
  224. ->where('deleted')->eq(0)
  225. ->andWhere('shadow')->eq(0)
  226. ->andWhere('id')->in($scopeObjects);
  227. }
  228. elseif($scope == 'project')
  229. {
  230. $objects = $this->dao->select('id')->from(TABLE_PROJECT)
  231. ->where('deleted')->eq(0)
  232. ->andWhere('type')->eq('project')
  233. ->andWhere('id')->in($scopeObjects);
  234. }
  235. elseif($scope == 'execution')
  236. {
  237. $objects = $this->dao->select('id')->from(TABLE_EXECUTION)
  238. ->where('deleted')->eq(0)
  239. ->andWhere('type')->in('sprint,stage,kanban')
  240. ->andWhere('id')->in($scopeObjects);
  241. }
  242. elseif($scope == 'user')
  243. {
  244. $objects = $this->dao->select('account')->from(TABLE_USER)
  245. ->where('deleted')->eq('0')
  246. ->andWhere('account')->in($scopeObjects);
  247. }
  248. elseif($scope == 'repo')
  249. {
  250. $objects = $this->dao->select('id')->from(TABLE_REPO)
  251. ->where('deleted')->eq('0')
  252. ->andWhere('id')->in($scopeObjects);
  253. }
  254. if(!is_null($objects))
  255. {
  256. if(!empty($pager)) $objects = $objects->page($pager);
  257. return $objects->fetchPairs();
  258. }
  259. return array();
  260. }
  261. /**
  262. * Process dao with query date values.
  263. *
  264. * @param object $stmt
  265. * @param object $query
  266. * @param string $dateType
  267. * @access protected
  268. * @return object
  269. */
  270. protected function processDAOWithDate($stmt, $query, $dateType)
  271. {
  272. $dateBegin = $this->processRecordQuery($query, 'dateBegin', 'date');
  273. $dateEnd = $this->processRecordQuery($query, 'dateEnd', 'date');
  274. $calcDate = $this->processRecordQuery($query, 'calcDate', 'date');
  275. list($dateBegin, $dateEnd) = $this->processRecordQuery($query, 'dateLabel', 'date');
  276. $yearBegin = empty($dateBegin) ? '' : $dateBegin->year;
  277. $yearEnd = empty($dateEnd) ? '' : $dateEnd->year;
  278. $monthBegin = empty($dateBegin) ? '' : $dateBegin->month;
  279. $monthEnd = empty($dateEnd) ? '' : $dateEnd->month;
  280. $weekBegin = empty($dateBegin) ? '' : $dateBegin->week;
  281. $weekEnd = empty($dateEnd) ? '' : $dateEnd->week;
  282. $dayBegin = empty($dateBegin) ? '' : $dateBegin->day;
  283. $dayEnd = empty($dateEnd) ? '' : $dateEnd->day;
  284. $stmt = $stmt->beginIF(!empty($dateBegin) and $dateType == 'year')->andWhere('`year`')->ge($yearBegin)->fi()
  285. ->beginIF(!empty($dateEnd) and $dateType == 'year')->andWhere('`year`')->le($yearEnd)->fi()
  286. ->beginIF(!empty($dateBegin) and $dateType == 'month')->andWhere('CONCAT(`year`, `month`)')->ge($monthBegin)->fi()
  287. ->beginIF(!empty($dateEnd) and $dateType == 'month')->andWhere('CONCAT(`year`, `month`)')->le($monthEnd)->fi()
  288. ->beginIF(!empty($dateBegin) and $dateType == 'week')->andWhere('CONCAT(`year`, `week`)')->ge($weekBegin)->fi()
  289. ->beginIF(!empty($dateEnd) and $dateType == 'week')->andWhere('CONCAT(`year`, `week`)')->le($weekEnd)->fi()
  290. ->beginIF(!empty($dateBegin) and $dateType == 'day')->andWhere('CONCAT(`year`, `month`, `day`)')->ge($dayBegin)->fi()
  291. ->beginIF(!empty($dateEnd) and $dateType == 'day')->andWhere('CONCAT(`year`, `month`, `day`)')->le($dayEnd)->fi()
  292. ->beginIF(!empty($calcDate))->andWhere('date')->ge($calcDate)->fi();
  293. return $stmt;
  294. }
  295. /**
  296. * 请求度量数据。
  297. * Fetch metric data.
  298. *
  299. * @param string $code
  300. * @param array $fieldList
  301. * @param array $query
  302. * @param object|null $pager
  303. * @access protected
  304. * @return array
  305. */
  306. protected function fetchMetricRecords($code, $fieldList, $query = array(), $pager = null)
  307. {
  308. $metric = $this->getByCode($code);
  309. $scopeKey = $metric->scope;
  310. $dateType = $metric->dateType;
  311. $query['dateType'] = $dateType;
  312. $scopeValue = $this->processRecordQuery($query, 'scope');
  313. $objectList = $this->getObjectsWithPager($metric, $query, $pager, $scopeValue);
  314. $fieldList = array_merge($fieldList, array('id', 'value', 'date', 'calcType', 'calculatedBy'));
  315. $wrapFields = array_map(function ($value) {
  316. return "`$value`";
  317. }, $fieldList);
  318. $dataFieldStr = implode(',', $wrapFields);
  319. $stmt = $this->dao->select($dataFieldStr)
  320. ->from(TABLE_METRICLIB)
  321. ->where('metricCode')->eq($code)
  322. ->beginIF($scopeKey != 'system' && !empty($objectList))->andWhere($scopeKey)->in($objectList)->fi()
  323. ->beginIF(!empty($scopeValue))->andWhere($scopeKey)->in($scopeValue)->fi();
  324. $stmt = $this->processDAOWithDate($stmt, $query, $dateType)
  325. ->beginIF($scopeKey != 'system')->orderBy("date desc, $scopeKey, year desc, month desc, week desc, day desc")->fi()
  326. ->beginIF($scopeKey == 'system')->orderBy("date desc, year desc, month desc, week desc, day desc")->fi();
  327. if($scopeKey == 'system') $stmt = $stmt->page($pager); // beginIF not work with page()
  328. return $stmt->fetchAll();
  329. }
  330. protected function fetchMetricRecordsWithOption($code, $fieldList, $options = array(), $pager = null)
  331. {
  332. $metric = $this->getByCode($code);
  333. $scopeKey = $metric->scope;
  334. $dateType = $metric->dateType;
  335. $fieldList = array_merge($fieldList, array('id', 'value', 'date', 'calcType', 'calculatedBy'));
  336. $wrapFields = array_map(function ($value) {
  337. return "`$value`";
  338. }, $fieldList);
  339. $dataFieldStr = implode(',', $wrapFields);
  340. $stmt = $this->dao->select($dataFieldStr)->from(TABLE_METRICLIB)
  341. ->where('metricCode')->eq($code);
  342. if(!empty($options))
  343. {
  344. foreach($options as $key => $option)
  345. {
  346. $stmt = $stmt->andWhere($key)->in($option);
  347. }
  348. }
  349. if($dateType == 'nodate')
  350. {
  351. $stmt->andWhere('date')->ge(helper::today());
  352. }
  353. $stmt = $stmt->orderBy("date desc");
  354. if($scopeKey == 'system') $stmt = $stmt->page($pager); // beginIF not work with page()
  355. return $stmt->fetchAll();
  356. }
  357. /**
  358. * 请求最新的度量数据。
  359. * Fetch latest metric data.
  360. *
  361. * @param string $code
  362. * @param array $fieldList
  363. * @param array $query
  364. * @param object|null $pager
  365. * @access protected
  366. * @return array
  367. */
  368. protected function fetchLatestMetricRecords($code, $fieldList, $query = array(), $pager = null)
  369. {
  370. $metric = $this->getByCode($code);
  371. $dateType = $metric->dateType;
  372. $lastCalcDate = substr($metric->lastCalcTime, 0, 10);
  373. $query['dateType'] = $dateType;
  374. $objectList = $this->getObjectsWithPager($metric, $query);
  375. $scopeValue = $this->processRecordQuery($query, 'scope');
  376. $scopeKey = $metric->scope;
  377. $fieldList = array_merge($fieldList, array('id', 'value', 'date'));
  378. $wrapFields = array_map(function ($value) {
  379. return "`$value`";
  380. }, $fieldList);
  381. $dataFieldStr = implode(',', $wrapFields);
  382. $stmt = $this->dao->select($dataFieldStr)
  383. ->from(TABLE_METRICLIB)
  384. ->where('metricCode')->eq($code)
  385. ->beginIF($scopeKey != 'system')->andWhere($scopeKey)->in($objectList)->fi()
  386. ->beginIF(!empty($scopeValue))->andWhere($scopeKey)->in($scopeValue)->fi();
  387. $stmt = $this->processDAOWithDate($stmt, $query, $dateType)
  388. ->beginIF(!empty($scopeList))->orderBy("date desc, $scopeKey, year desc, month desc, week desc, day desc")->fi()
  389. ->beginIF(empty($scopeList))->orderBy("date desc, year desc, month desc, week desc, day desc")->fi();
  390. $stmt = $stmt->page($pager);
  391. return $stmt->fetchAll();
  392. }
  393. /**
  394. * 根据日期获取度量数据。
  395. * Fetch metric record by date.
  396. *
  397. * @param string $code
  398. * @param string $date
  399. * @param int $limit
  400. * @access protected
  401. * @return array
  402. */
  403. protected function fetchMetricRecordByDate($code = 'all', $date = '', $limit = 100)
  404. {
  405. $nextDate = empty($date) ? '' : date('Y-m-d', strtotime($date) + 86400);
  406. $records = $this->dao->select('id')->from(TABLE_METRICLIB)
  407. ->where('1 = 1')
  408. ->beginIF($code != 'all')->andWhere('metricCode')->eq($code)->fi()
  409. ->beginIF(!empty($date))
  410. ->andWhere('date')->ge($date)
  411. ->andWhere('date')->lt($nextDate)
  412. ->fi()
  413. ->beginIF($limit > 0)->limit($limit)->fi()
  414. ->fetchAll();
  415. return $records;
  416. }
  417. /**
  418. * Set deleted.
  419. *
  420. * @param string $code
  421. * @param string $value
  422. * @access protected
  423. * @return void
  424. */
  425. protected function setDeleted($code, $value)
  426. {
  427. $this->dao->update(TABLE_METRICLIB)
  428. ->set('deleted')->eq($value)
  429. ->where('metricCode')->eq($code)
  430. ->exec();
  431. }
  432. /**
  433. * Keep latest records.
  434. *
  435. * @param int $code
  436. * @param int $fields
  437. * @access protected
  438. * @return void
  439. */
  440. protected function keepLatestRecords($code, $fields)
  441. {
  442. if(empty($fields)) return;
  443. /**
  444. * 判断fields中的字段是否与array('year', 'month', 'week', 'day')存在交集
  445. */
  446. $intersect = array_intersect($fields, array('year', 'month', 'week', 'day'));
  447. foreach($fields as $key => $field) $fields[$key] = "`$field`";
  448. if(empty($intersect)) $fields[] = 'DATE(date)';
  449. $table = TABLE_METRICLIB;
  450. $sql = " UPDATE $table AS t1";
  451. $sql .= " SET t1.deleted = '0'";
  452. $sql .= " WHERE id IN (";
  453. $sql .= " SELECT maxid FROM (";
  454. $sql .= " SELECT MAX(id) AS maxid";
  455. $sql .= " FROM $table";
  456. $sql .= " WHERE metricCode = '$code'";
  457. $sql .= " GROUP BY ". implode(',', $fields);
  458. $sql .= " ) AS tmp";
  459. $sql .= " )";
  460. $this->dao->exec($sql);
  461. }
  462. /**
  463. * Execute delete.
  464. *
  465. * @param string $code
  466. * @access protected
  467. * @return void
  468. */
  469. protected function executeDelete($code)
  470. {
  471. $this->dao->delete()->from(TABLE_METRICLIB)
  472. ->where('metricCode')->eq($code)
  473. ->andWhere('deleted', true)->eq('1')
  474. ->orWhere('value')->eq(0)->markRight(1)
  475. ->exec();
  476. }
  477. /**
  478. * 重建id列顺序。
  479. * Rebuild id column order.
  480. *
  481. * @access protected
  482. * @return void
  483. */
  484. protected function rebuildIdColumn()
  485. {
  486. if($this->config->db->driver != 'mysql') return;
  487. $table = TABLE_METRICLIB;
  488. $tableRowCount = $this->dao->select('COUNT(id) as rowcount')->from(TABLE_METRICLIB)->fetch('rowcount');
  489. $autoIncrement = $tableRowCount + 1;
  490. $this->dao->exec("SET @count = 0;UPDATE $table SET `id` = @count:= @count + 1;");
  491. $this->dao->exec("ALTER TABLE $table AUTO_INCREMENT = $autoIncrement");
  492. }
  493. }