tao.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  1. <?php
  2. class pivotTao extends pivotModel
  3. {
  4. /**
  5. * 获取透视表。
  6. * Fetch pivot by id.
  7. *
  8. * @param int $id
  9. * @param string|null $version
  10. * @access public
  11. * @return object|bool
  12. */
  13. protected function fetchPivot($id, $version = null)
  14. {
  15. $pivot = $this->dao->select('*')->from(TABLE_PIVOT)->where('id')->eq($id)->andWhere('deleted')->eq('0')->fetch();
  16. if(!$pivot) return false;
  17. if(is_null($version)) return $this->mergePivotSpecData($pivot);
  18. $specData = $this->dao->select('*')->from(TABLE_PIVOTSPEC)->where('pivot')->eq($id)->andWhere('version')->eq($version)->fetch();
  19. if(!$specData) return $pivot;
  20. unset($specData->id);
  21. foreach($specData as $specKey => $specValue) $pivot->$specKey = $specValue;
  22. return $pivot;
  23. }
  24. /**
  25. * 合并 pivotSpec 的数据。
  26. * Merge pivotSpec data to pivot.
  27. *
  28. * @param int $id
  29. * @param bool $isObject
  30. * @access public
  31. * @return object|bool
  32. */
  33. protected function mergePivotSpecData($pivots, $isObject = true)
  34. {
  35. if($isObject) $pivots = array($pivots);
  36. $pivotIDList = array_column($pivots, 'id');
  37. $pivotSpecs = $this->dao->select('t2.pivot,t2.version,t2.driver,t2.mode,t2.name,t2.desc,t2.sql,t2.fields,t2.langs,t2.vars,t2.objects,t2.settings,t2.filters,t2.createdDate')->from(TABLE_PIVOT)->alias('t1')
  38. ->leftJoin(TABLE_PIVOTSPEC)->alias('t2')->on('t1.id = t2.pivot and t1.version = t2.version')
  39. ->where('t1.id')->in($pivotIDList)
  40. ->fetchAll('pivot', false);
  41. foreach($pivots as $index => $pivot)
  42. {
  43. if(!isset($pivotSpecs[$pivot->id])) continue;
  44. foreach($pivotSpecs[$pivot->id] as $specKey => $specValue) $pivot->$specKey = $specValue;
  45. $pivots[$index] = $pivot;
  46. }
  47. return $isObject ? current($pivots) : $pivots;
  48. }
  49. /**
  50. * 获取产品列表。
  51. * Get product list.
  52. *
  53. * @param string $conditions
  54. * @param array|string $IDList
  55. * @param array $filters
  56. * @access public
  57. * @return array
  58. * @param mixed[]|string $idList
  59. */
  60. protected function getProductList($conditions, $idList = array(), $filters = array())
  61. {
  62. $productID = isset($filters['productID']) ? $filters['productID'] : 0;
  63. $productStatus = isset($filters['productStatus']) ? $filters['productStatus'] : '';
  64. $productType = isset($filters['productType']) ? $filters['productType'] : '';
  65. return $this->dao->select('t1.id, t1.code, t1.name, t1.PO')->from(TABLE_PRODUCT)->alias('t1')
  66. ->leftJoin(TABLE_PROGRAM)->alias('t2')->on('t1.program = t2.id')
  67. ->where('t1.deleted')->eq('0')
  68. ->andWhere('t1.shadow')->eq('0')
  69. ->beginIF(!empty($idList))->andWhere('t1.id')->in($idList)->fi()
  70. ->beginIF($productID)->andWhere('t1.id')->eq($productID)->fi()
  71. ->beginIF($productStatus)->andWhere('t1.status')->eq($productStatus)->fi()
  72. ->beginIF($productType)->andWhere('t1.type')->eq($productType)->fi()
  73. ->filterTpl('skip')
  74. ->orderBy('t2.order_asc, t1.line_desc, t1.order_asc')
  75. ->fetchAll('id');
  76. }
  77. /**
  78. * 补充产品的计划信息。
  79. * Supplement product plan information.
  80. *
  81. * @param array $products
  82. * @access public
  83. * @return array
  84. * @param string $conditions
  85. */
  86. protected function processProductPlan(&$products, $conditions)
  87. {
  88. /* 获取产品的计划信息,并且根据产品id进行分组。 */
  89. /* Get the plan information of the product and group it by product id. */
  90. $plans = $this->dao->select('id, product, branch, parent, title, begin, end')->from(TABLE_PRODUCTPLAN)
  91. ->where('deleted')->eq('0')
  92. ->andWhere('product')->in(array_keys($products))
  93. ->beginIF(strpos($conditions, 'overduePlan') === false)->andWhere('end')->gt(date('Y-m-d'))->fi()
  94. ->orderBy('product, parent_desc, begin')
  95. ->fetchAll('id');
  96. foreach($plans as $plan)
  97. {
  98. if($plan->parent > 0)
  99. {
  100. $parentPlan = zget($plans, $plan->parent, null);
  101. if($parentPlan)
  102. {
  103. $products[$plan->product]->plans[$parentPlan->id] = $parentPlan;
  104. unset($plans[$parentPlan->id]);
  105. }
  106. $plan->title = '>>' . $plan->title;
  107. }
  108. $products[$plan->product]->plans[$plan->id] = $plan;
  109. }
  110. return $plans;
  111. }
  112. /**
  113. * 获取产品的需求信息。
  114. * Get product demand information.
  115. *
  116. * @param string $storyType
  117. * @param array $plans
  118. * @access public
  119. * @return array
  120. * @param mixed[] $products
  121. */
  122. protected function processPlanStories(&$products, $storyType, $plans)
  123. {
  124. /* 获取所有符合条件的需求。 */
  125. /* Get all the requirements that meet the conditions. */
  126. $plannedStories = array();
  127. $unplannedStories = array();
  128. $stmt = $this->dao->select('id, plan, product, status')->from(TABLE_STORY)
  129. ->where('deleted')->eq('0')
  130. ->andWhere('parent')->ge(0)
  131. ->beginIF($storyType)->andWhere('type')->eq($storyType)->fi()
  132. ->query();
  133. /* 根据需求的计划信息,将需求分组到不同的计划中。 */
  134. /* According to the plan information of the demand, the demand is grouped into different plans. */
  135. while($story = $stmt->fetch())
  136. {
  137. if(empty($story->plan))
  138. {
  139. $unplannedStories[$story->id] = $story;
  140. continue;
  141. }
  142. $storyPlans = array();
  143. $storyPlans[] = $story->plan;
  144. if(strpos($story->plan, ',') !== false) $storyPlans = explode(',', trim($story->plan, ','));
  145. foreach($storyPlans as $planID)
  146. {
  147. if(isset($plans[$planID]))
  148. {
  149. $plannedStories[$story->id] = $story;
  150. break;
  151. }
  152. }
  153. }
  154. /* 将需求统计信息添加到产品中。 */
  155. /* Add demand statistics information to the product. */
  156. $this->getPlanStatusStatistics($products, $plans, $plannedStories, $unplannedStories);
  157. return $products;
  158. }
  159. /**
  160. * 获取产品计划的需求统计信息。
  161. * Get product demand statistics information.
  162. *
  163. * @param array $products
  164. * @param array $plans
  165. * @param array $plannedStories
  166. * @param array $unplannedStories
  167. * @access protected
  168. * @return void
  169. */
  170. protected function getPlanStatusStatistics(&$products, $plans, $plannedStories, $unplannedStories)
  171. {
  172. /* 统计已经计划过的产品计划的需求状态信息。 */
  173. /* Statistics of demand status information for planned product plans. */
  174. foreach($plannedStories as $story)
  175. {
  176. $storyPlans = strpos($story->plan, ',') !== false ? $storyPlans = explode(',', trim($story->plan, ',')) : array($story->plan);
  177. foreach($storyPlans as $planID)
  178. {
  179. if(!isset($plans[$planID])) continue;
  180. $plan = $plans[$planID];
  181. $products[$plan->product]->plans[$planID]->status[$story->status] = isset($products[$plan->product]->plans[$planID]->status[$story->status]) ? $products[$plan->product]->plans[$planID]->status[$story->status] + 1 : 1;
  182. }
  183. }
  184. /* 统计还未计划的产品计划的需求状态信息。 */
  185. /* Statistics of demand status information for unplanned product plans. */
  186. foreach($unplannedStories as $story)
  187. {
  188. $product = $story->product;
  189. if(isset($products[$product]))
  190. {
  191. if(!isset($products[$product]->plans[0]))
  192. {
  193. $products[$product]->plans[0] = new stdClass();
  194. $products[$product]->plans[0]->title = $this->lang->pivot->unplanned;
  195. $products[$product]->plans[0]->begin = '';
  196. $products[$product]->plans[0]->end = '';
  197. }
  198. $products[$product]->plans[0]->status[$story->status] = isset($products[$product]->plans[0]->status[$story->status]) ? $products[$product]->plans[0]->status[$story->status] + 1 : 1;
  199. }
  200. }
  201. }
  202. /**
  203. * 获取执行列表。
  204. * Get execution list.
  205. *
  206. * @param string $begin
  207. * @param string $end
  208. * @param array $executionIDList
  209. * @access public
  210. * @return array
  211. */
  212. protected function getExecutionList($begin, $end, $executionIDList = array())
  213. {
  214. return $this->dao->select("t1.project AS projectID, t1.execution AS executionID, t2.multiple, t2.end, IF(t3.multiple = '1', t2.name, '') AS executionName, t3.name AS projectName, ROUND(SUM(t1.estimate), 2) AS estimate, ROUND(SUM(t1.consumed), 2) AS consumed")->from(TABLE_TASK)->alias('t1')
  215. ->leftJoin(TABLE_EXECUTION)->alias('t2')->on('t1.execution = t2.id')
  216. ->leftJoin(TABLE_PROJECT)->alias('t3')->on('t1.project = t3.id')
  217. ->where('t1.status')->ne('cancel')
  218. ->andWhere('t1.deleted')->eq('0')
  219. ->andWhere('t1.parent')->lt(1)
  220. ->andWhere('t2.deleted')->eq('0')
  221. ->andWhere('t2.status')->eq('closed')
  222. ->beginIF($begin)->andWhere('t2.realBegan')->ge($begin)->fi()
  223. ->beginIF($end)->andWhere('t2.realEnd')->le($end)->fi()
  224. ->beginIF(!empty($executionIDList))->andWhere('t2.id')->in($executionIDList)->fi()
  225. ->groupBy('t1.project, t1.execution, t2.multiple, t2.end, t2.name, t3.multiple, t3.name')
  226. ->orderBy('t2.end_desc')
  227. ->fetchAll();
  228. }
  229. /**
  230. * 获取bug分组信息。
  231. * Get bug group information.
  232. *
  233. * @param string $begin
  234. * @param string $end
  235. * @param int $product
  236. * @param int $execution
  237. * @access public
  238. * @return array
  239. */
  240. protected function getBugGroup($begin, $end, $product, $execution)
  241. {
  242. return $this->dao->select("IF(resolution = '', 'unResolved', resolution) AS resolution, openedBy, status")->from(TABLE_BUG)
  243. ->where('deleted')->eq('0')
  244. ->andWhere('openedDate')->ge($begin)
  245. ->andWhere('openedDate')->le($end)
  246. ->beginIF($product)->andWhere('product')->eq($product)->fi()
  247. ->beginIF($execution)->andWhere('execution')->eq($execution)->fi()
  248. ->fetchGroup('openedBy');
  249. }
  250. /**
  251. * 获取未指派的执行。
  252. * Get unassigned executions.
  253. *
  254. * @param array $deptUsers
  255. * @access public
  256. * @return array
  257. */
  258. protected function getNoAssignExecution($deptUsers)
  259. {
  260. $assignedToList = $this->dao->select("DISTINCT IF(tt1.mode = '', tt1.assignedTo, tt2.account) AS assignedTo")->from(TABLE_TASK)->alias('tt1')
  261. ->leftJoin(TABLE_TASKTEAM)->alias('tt2')->on("tt1.id=tt2.task AND tt1.mode IN ('multi', 'linear')")
  262. ->where('tt1.status')->notIn('cancel,closed,done,pause')
  263. ->andWhere("IF(tt1.mode = '', tt1.assignedTo, tt2.account)")->ne('')
  264. ->andWhere('tt1.execution = t1.`root`')
  265. ->get();
  266. return $this->dao->select('t1.account AS user, t2.multiple, t2.id AS executionID, t2.name AS executionName, t3.id AS projectID, t3.name AS projectName')->from(TABLE_TEAM)->alias('t1')
  267. ->leftJoin(TABLE_EXECUTION)->alias('t2')->on('t2.id = t1.root')
  268. ->leftJoin(TABLE_PROJECT)->alias('t3')->on('t3.id = t2.project')
  269. ->where('t1.type')->eq('execution')
  270. ->andWhere("t1.account NOT IN ($assignedToList)")
  271. ->andWhere('t2.deleted')->eq('0')
  272. ->andWhere('t2.status')->notin('cancel, closed, done, suspended')
  273. ->beginIF($deptUsers)->andWhere('t1.account')->in($deptUsers)->fi()
  274. ->fetchAll();
  275. }
  276. /**
  277. * 获取已指派的执行。
  278. * Get assigned executions.
  279. *
  280. * @param array $deptUsers
  281. * @access public
  282. * @return array
  283. */
  284. protected function getAssignTask($deptUsers)
  285. {
  286. return $this->dao->select(<<<EOT
  287. t1.id,
  288. t1.isParent,
  289. CASE WHEN t1.mode = '' THEN t1.assignedTo ELSE t4.account END AS user,
  290. CASE WHEN t1.mode = '' THEN ROUND(t1.`left`, 2) ELSE ROUND(t4.`left`, 2) END AS `left`,
  291. t2.multiple,
  292. t2.id AS executionID,
  293. t2.name AS executionName,
  294. t3.id AS projectID,
  295. t3.name AS projectName
  296. EOT
  297. )->from(TABLE_TASK)->alias('t1')
  298. ->leftJoin(TABLE_EXECUTION)->alias('t2')->on('t1.execution = t2.id')
  299. ->leftJoin(TABLE_PROJECT)->alias('t3')->on('t3.id = t2.project')
  300. ->leftJoin(TABLE_TASKTEAM)->alias('t4')->on("t1.id=t4.task and t1.mode IN ('multi', 'linear')")
  301. ->where('t1.deleted')->eq('0')
  302. ->andWhere('t1.parent')->ge(0)
  303. ->andWhere('t1.status')->in('wait,pause,doing')
  304. ->andWhere("if(t1.mode = '', t1.assignedTo, t4.account)")->ne('')
  305. ->beginIF($deptUsers)->andWhere("if(t1.mode = '', t1.assignedTo, t4.account)")->in($deptUsers)->fi()
  306. ->andWhere('t2.deleted')->eq('0')
  307. ->andWhere('t2.vision')->like('rnd')
  308. ->andWhere('t2.status')->in('wait,suspended,doing')
  309. ->fetchAll();
  310. }
  311. /**
  312. * 获取任务相关的团队信息。
  313. * Get team information related to tasks.
  314. *
  315. * @param array $taskIDList
  316. * @access public
  317. * @return array
  318. */
  319. protected function getTeamTasks($taskIDList)
  320. {
  321. return $this->dao->select('task, SUM(`left`) AS `left`')->from(TABLE_TASKTEAM)
  322. ->where('task')->in($taskIDList)
  323. ->groupBy('task')
  324. ->fetchPairs('task');
  325. }
  326. /**
  327. * 获取指派的bug。
  328. * Get assigned bugs.
  329. *
  330. * @access public
  331. * @return array
  332. */
  333. protected function getAssignBugGroup()
  334. {
  335. return $this->dao->select('product, assignedTo, COUNT(1) AS bugCount')->from(TABLE_BUG)
  336. ->where('deleted')->eq('0')
  337. ->andWhere('status')->eq('active')
  338. ->andWhere('assignedTo')->ne('')
  339. ->andWhere('assignedTo')->ne('closed')
  340. ->groupBy('product, assignedTo')
  341. ->fetchGroup('assignedTo');
  342. }
  343. /**
  344. * 获取产品项目关联关系。
  345. * Get product project association.
  346. *
  347. * @access public
  348. * @return array
  349. */
  350. protected function getProductProjects()
  351. {
  352. return $this->dao->select('t2.product, t2.project')->from(TABLE_PROJECT)->alias('t1')
  353. ->leftJoin(TABLE_PROJECTPRODUCT)->alias('t2')->on('t1.id = t2.project')
  354. ->where('t1.type')->eq('project')
  355. ->andWhere('t1.hasProduct')->eq(0)
  356. ->fetchPairs();
  357. }
  358. /**
  359. * 获取所有产品的id和name。
  360. * Get the id and name of all products.
  361. *
  362. * @access public
  363. * @return array
  364. */
  365. protected function getAllProductsIDAndName()
  366. {
  367. return $this->dao->select('id, name')->from(TABLE_PRODUCT)
  368. ->where('deleted')->eq('0')
  369. ->fetchPairs();
  370. }
  371. /**
  372. * 获取产品和执行名称。
  373. * Get product and execution name.
  374. *
  375. * @access public
  376. * @return array
  377. */
  378. protected function getProjectAndExecutionNameQuery()
  379. {
  380. return $this->dao->select('t1.id, t1.name, t2.name as projectname, t1.status, t1.multiple')
  381. ->from(TABLE_EXECUTION)->alias('t1')
  382. ->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project=t2.id')
  383. ->where('t1.deleted')->eq(0)
  384. ->andWhere('t1.type')->in('stage,sprint')
  385. ->fetchAll();
  386. }
  387. /**
  388. * 获取一个维度的第一个分组。
  389. * Get the first group of a dimension.
  390. *
  391. * @param int $dimensionID
  392. * @access protected
  393. * @return int
  394. */
  395. protected function getFirstGroup($dimensionID)
  396. {
  397. return (int)$this->dao->select('id')->from(TABLE_MODULE)
  398. ->where('deleted')->eq('0')
  399. ->andWhere('type')->eq('pivot')
  400. ->andWhere('root')->eq($dimensionID)
  401. ->andWhere('grade')->eq(1)
  402. ->orderBy('`order`')
  403. ->limit(1)
  404. ->fetch('id');
  405. }
  406. /**
  407. * 通过维度和路径获取分组。
  408. * Get group by dimension and path.
  409. *
  410. * @param int $dimensionID
  411. * @param string $path
  412. * @access protected
  413. * @return array
  414. */
  415. protected function getGroupsByDimensionAndPath($dimensionID, $path)
  416. {
  417. return $this->dao->select('id, grade, name, collector')->from(TABLE_MODULE)
  418. ->where('deleted')->eq('0')
  419. ->andWhere('root')->eq($dimensionID)
  420. ->andWhere('path')->like("{$path}%")
  421. ->orderBy('`order`')
  422. ->fetchAll();
  423. }
  424. /**
  425. * 根据分组获取对应的透视表id。
  426. * Get the corresponding pivot table id according to the group.
  427. *
  428. * @param int $groupID
  429. * @access protected
  430. * @return int
  431. */
  432. protected function getPivotID($groupID)
  433. {
  434. $viewableObjects = $this->bi->getViewableObject('pivot');
  435. return (int)$this->dao->select('id')->from(TABLE_PIVOT)
  436. ->where("FIND_IN_SET({$groupID}, `group`)")
  437. ->andWhere('stage')->ne('draft')
  438. ->andWhere('deleted')->eq('0')
  439. ->andWhere('id')->in($viewableObjects)
  440. ->orderBy('id_desc')
  441. ->limit(1)
  442. ->fetch('id');
  443. }
  444. /**
  445. * 根据一个分组下的所有透视表。
  446. * Get all pivot tables under a group.
  447. *
  448. * @param int $groupID
  449. * @access protected
  450. * @return array
  451. */
  452. protected function getAllPivotByGroupID($groupID)
  453. {
  454. $pivots = $this->dao->select('*')->from(TABLE_PIVOT)
  455. ->where("FIND_IN_SET({$groupID}, `group`)")
  456. ->andWhere('stage')->ne('draft')
  457. ->andWhere('deleted')->eq('0')
  458. ->orderBy('id_desc')
  459. ->fetchAll('', false);
  460. return $this->mergePivotSpecData($pivots, false);
  461. }
  462. /**
  463. * 获取透视表汇总列的下钻配置。
  464. * Get drill config of pivot summary column field.
  465. *
  466. * @param int $pivotID
  467. * @param string $field
  468. * @param string $status
  469. * @access public
  470. * @return object|bool
  471. * @param string|mixed[] $fields
  472. * @param string $version
  473. */
  474. public function fetchPivotDrills($pivotID, $version, $fields)
  475. {
  476. if(is_string($fields)) $fields = array($fields);
  477. $records = $this->dao->select('*')->from(TABLE_PIVOTDRILL)
  478. ->where('pivot')->eq($pivotID)
  479. ->andWhere('version')->eq($version)
  480. ->andWhere('field')->in($fields)
  481. ->fetchAll('field', false);
  482. foreach($records as $field => $record)
  483. {
  484. $record->condition = json_decode($record->condition, true);
  485. $records[$field] = $record;
  486. }
  487. return $records;
  488. }
  489. }