347 lines
14 KiB
PHP
347 lines
14 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
class bugTao extends bugModel
|
|
{
|
|
/**
|
|
* Get bug details, including all contents of the TABLE_BUG, execution name, associated story name, associated story status, associated story version, associated task name, and associated plan name.
|
|
* 获取bug的详情,包含bug表的所有内容、所属执行名称、关联需求名称、关联需求状态、关联需求版本、关联任务名称、关联计划名称
|
|
*
|
|
* @param int $bugID
|
|
* @access protected
|
|
* @return object|false
|
|
*/
|
|
protected function fetchBugInfo(int $bugID): object|false
|
|
{
|
|
return $this->dao->select('t1.*, t2.name AS executionName, t3.title AS storyTitle, t3.status AS storyStatus, t3.version AS latestStoryVersion, t4.name AS taskName, t5.title AS planName')
|
|
->from(TABLE_BUG)->alias('t1')
|
|
->leftJoin(TABLE_EXECUTION)->alias('t2')->on('t1.execution = t2.id')
|
|
->leftJoin(TABLE_STORY)->alias('t3')->on('t1.story = t3.id')
|
|
->leftJoin(TABLE_TASK)->alias('t4')->on('t1.task = t4.id')
|
|
->leftJoin(TABLE_PRODUCTPLAN)->alias('t5')->on('t1.plan = t5.id')
|
|
->where('t1.id')->eq((int)$bugID)->fetch();
|
|
}
|
|
|
|
/**
|
|
* Get bug list by browse type.
|
|
* 通过浏览类型获取bug列表。
|
|
*
|
|
* @param string $browseType
|
|
* @param int|array $productIdList
|
|
* @param int|string $branch
|
|
* @param int|array $moduleIdList
|
|
* @param int[] $executionIdList
|
|
* @param string $orderBy
|
|
* @param object $pager
|
|
* @param int $projectID
|
|
* @access protected
|
|
* @return array
|
|
*/
|
|
protected function getListByBrowseType(string $browseType, int|array $productIdList, int $projectID, array $executionIdList, int|string $branch, int|array $moduleIdList, string $orderBy, object $pager = null): array
|
|
{
|
|
$browseType = strtolower($browseType);
|
|
$lastEditedDate = '';
|
|
$bugIdListAssignedByMe = array();
|
|
|
|
if($browseType == 'longlifebugs') $lastEditedDate = date(DT_DATE1, time() - $this->config->bug->longlife * 24 * 3600);
|
|
if($browseType == 'assignedbyme')
|
|
{
|
|
$bugIdListAssignedByMe = $this->dao->select('objectID')->from(TABLE_ACTION)
|
|
->where('objectType')->eq('bug')
|
|
->andWhere('action')->eq('assigned')
|
|
->andWhere('actor')->eq($this->app->user->account)
|
|
->fetchPairs('objectID', 'objectID');
|
|
}
|
|
|
|
$bugList = $this->dao->select("*, IF(`pri` = 0, {$this->config->maxPriValue}, `pri`) as priOrder, IF(`severity` = 0, {$this->config->maxPriValue}, `severity`) as severityOrder")->from(TABLE_BUG)
|
|
->where('deleted')->eq('0')
|
|
->andWhere('product')->in($productIdList)
|
|
->beginIF($projectID)->andWhere('project')->eq($projectID)->fi()
|
|
->beginIF($this->app->tab !== 'qa')->andWhere('execution')->in($executionIdList)->fi()
|
|
->beginIF($branch !== 'all')->andWhere('branch')->in($branch)->fi()
|
|
->beginIF($moduleIdList)->andWhere('module')->in($moduleIdList)->fi()
|
|
->beginIF(!$this->app->user->admin)->andWhere('project')->in('0,' . $this->app->user->view->projects)->fi()
|
|
|
|
->beginIF($browseType == 'assigntome')->andWhere('assignedTo')->eq($this->app->user->account)->fi()
|
|
->beginIF($browseType == 'openedbyme')->andWhere('openedBy')->eq($this->app->user->account)->fi()
|
|
->beginIF($browseType == 'resolvedbyme')->andWhere('resolvedBy')->eq($this->app->user->account)->fi()
|
|
->beginIF($browseType == 'assigntonull')->andWhere('assignedTo')->eq('')->fi()
|
|
->beginIF($browseType == 'unconfirmed')->andWhere('confirmed')->eq(0)->fi()
|
|
->beginIF($browseType == 'unclosed')->andWhere('status')->ne('closed')->fi()
|
|
->beginIF($browseType == 'unresolved')->andWhere('status')->eq('active')->fi()
|
|
->beginIF($browseType == 'toclosed')->andWhere('status')->eq('resolved')->fi()
|
|
->beginIF($browseType == 'postponedbugs')->andWhere('resolution')->eq('postponed')->fi()
|
|
->beginIF($browseType == 'review')->andWhere("FIND_IN_SET('{$this->app->user->account}', reviewers)")->fi()
|
|
|
|
->beginIF($browseType == 'longlifebugs')
|
|
->andWhere('lastEditedDate')->lt($lastEditedDate)
|
|
->andWhere('openedDate')->lt($lastEditedDate)
|
|
->andWhere('status')->ne('closed')
|
|
->fi()
|
|
|
|
->beginIF($browseType == 'overduebugs')
|
|
->andWhere('status')->eq('active')
|
|
->andWhere('deadline')->lt(helper::today())
|
|
->fi()
|
|
|
|
->beginIF($browseType == 'assignedbyme')
|
|
->andWhere('status')->ne('closed')
|
|
->andWhere('id')->in($bugIdListAssignedByMe)
|
|
->fi()
|
|
|
|
->orderBy($orderBy)
|
|
->page($pager)
|
|
->fetchAll('id');
|
|
|
|
$this->loadModel('common')->saveQueryCondition($this->dao->get(), 'bug');
|
|
return $bugList;
|
|
}
|
|
|
|
/**
|
|
* Get bug list of story need confirm.
|
|
* 获取需要确认需求变动的bug列表。
|
|
*
|
|
* @param int|array $productIdList
|
|
* @param int $projectID
|
|
* @param int[] $executionIdList
|
|
* @param int|string $branch
|
|
* @param int|array $moduleIdList
|
|
* @param string $orderBy
|
|
* @param object $pager
|
|
* @access protected
|
|
* @return array
|
|
*/
|
|
protected function getListByNeedconfirm(int|array $productIdList, int $projectID, array $executionIdList, int|string $branch, int|array $moduleIdList, string $orderBy, object $pager = null): array
|
|
{
|
|
return $this->dao->select("t1.*, t2.title AS storyTitle, IF(t1.`pri` = 0, {$this->config->maxPriValue}, t1.`pri`) as priOrder, IF(t1.`severity` = 0, {$this->config->maxPriValue}, t1.`severity`) as severityOrder")->from(TABLE_BUG)->alias('t1')
|
|
->leftJoin(TABLE_STORY)->alias('t2')->on('t1.story = t2.id')
|
|
->where('t1.deleted')->eq('0')
|
|
->andWhere("t2.status = 'active'")
|
|
->andWhere('t2.version > t1.storyVersion')
|
|
->andWhere('t1.product')->in($productIdList)
|
|
->beginIF($projectID)->andWhere('t1.project')->eq($projectID)->fi()
|
|
->beginIF($this->app->tab !== 'qa')->andWhere('t1.execution')->in($executionIdList)->fi()
|
|
->beginIF($branch !== 'all')->andWhere('t1.branch')->in($branch)->fi()
|
|
->beginIF($moduleIdList)->andWhere('t1.module')->in($moduleIdList)->fi()
|
|
->beginIF(!$this->app->user->admin)->andWhere('t1.project')->in('0,' . $this->app->user->view->projects)->fi()
|
|
->orderBy($orderBy)
|
|
->page($pager)
|
|
->fetchAll('id');
|
|
}
|
|
|
|
/**
|
|
* Get cases created by bug.
|
|
* 获取bug建的用例。
|
|
*
|
|
* @param int $bugID
|
|
* @access protected
|
|
* @return array
|
|
*/
|
|
protected function getCasesFromBug(int $bugID): array
|
|
{
|
|
return $this->dao->select('id, title')->from(TABLE_CASE)->where('`fromBug`')->eq($bugID)->fetchPairs();
|
|
}
|
|
|
|
/**
|
|
* Get an array of id and title pairs by buglist.
|
|
* 传入一个buglist,获得bug的id和title键值对数组。
|
|
*
|
|
* @param string|array $bugList
|
|
* @access protected
|
|
* @return array
|
|
*/
|
|
protected function getBugPairsByList(string|array $bugList): array
|
|
{
|
|
return $this->dao->select('id,title')->from(TABLE_BUG)->where('id')->in($bugList)->fetchPairs();
|
|
}
|
|
|
|
/**
|
|
* Get object title/name base on the params.
|
|
* 根据传入的参数,获取对象名称。
|
|
*
|
|
* @param int $objectID
|
|
* @param string $table
|
|
* @param string $field
|
|
* @access protected
|
|
* @return string
|
|
*/
|
|
protected function getNameFromTable(int $objectID, string $table, string $field): string
|
|
{
|
|
return $this->dao->findById($objectID)->from($table)->fields($field)->fetch($field);
|
|
}
|
|
|
|
/**
|
|
* 更新完 bug 后的相关处理。
|
|
* Relevant processing after updating bug.
|
|
*
|
|
* @param object $bug
|
|
* @param object $oldBug
|
|
* @access protected
|
|
* @return bool
|
|
*/
|
|
protected function afterUpdate(object $bug, object $oldBug): bool
|
|
{
|
|
/* 解除旧的版本关联关系,关联新的版本。*/
|
|
/* Unlink old resolved build and link new resolved build. */
|
|
if($bug->resolution == 'fixed' && !empty($bug->resolvedBuild) && $bug->resolvedBuild != $oldBug->resolvedBuild)
|
|
{
|
|
if(!empty($oldBug->resolvedBuild)) $this->loadModel('build')->unlinkBug($oldBug->resolvedBuild, $bug->id);
|
|
$this->linkBugToBuild($bug->id, $bug->resolvedBuild);
|
|
}
|
|
|
|
/* 记录解除旧的计划关联关系和关联新的计划的历史。*/
|
|
/* Create actions for linking new plan and unlinking old plan. */
|
|
if($bug->plan != $oldBug->plan)
|
|
{
|
|
$this->loadModel('action');
|
|
if(!empty($oldBug->plan)) $this->action->create('productplan', $oldBug->plan, 'unlinkbug', '', $bug->id);
|
|
if(!empty($bug->plan)) $this->action->create('productplan', $bug->plan, 'linkbug', '', $bug->id);
|
|
}
|
|
|
|
$this->updateLinkBug($bug->id, $bug->linkBug, $oldBug->linkBug);
|
|
|
|
/* 给 bug 解决者积分奖励。*/
|
|
/* Add score to the user who resolved the bug. */
|
|
if(!empty($bug->resolvedBy)) $this->loadModel('score')->create('bug', 'resolve', $bug->id);
|
|
|
|
/* 更新 bug 所属看板的泳道。*/
|
|
/* Update the lane of the bug kanban. */
|
|
if($bug->execution and $bug->status != $oldBug->status) $this->loadModel('kanban')->updateLane($bug->execution, 'bug');
|
|
|
|
/* 更新反馈的状态。*/
|
|
/* Update the status of feedback. */
|
|
if(($this->config->edition != 'open') && $oldBug->feedback) $this->loadModel('feedback')->updateStatus('bug', $oldBug->feedback, $bug->status, $oldBug->status);
|
|
|
|
/* 更新 bug 的附件。*/
|
|
/* Update the files of bug. */
|
|
$this->loadModel('file')->processFile4Object('bug', $oldBug, $bug);
|
|
|
|
return !dao::isError();
|
|
}
|
|
|
|
/**
|
|
* 更新相关 bug。
|
|
* Update the linked bug.
|
|
*
|
|
* @param int $bugID
|
|
* @param string $linkBug
|
|
* @param string $oldLinkBug
|
|
* @access protected
|
|
* @return bool
|
|
*/
|
|
protected function updateLinkBug(int $bugID, string $linkBug, string $oldLinkBug): bool
|
|
{
|
|
$linkBugs = explode(',', $linkBug);
|
|
$oldLinkBugs = explode(',', $oldLinkBug);
|
|
$addedLinkBugs = array_diff($linkBugs, $oldLinkBugs);
|
|
$removedLinkBugs = array_diff($oldLinkBugs, $linkBugs);
|
|
$changedLinkBugs = array_merge($addedLinkBugs, $removedLinkBugs);
|
|
$changedLinkBugs = $this->dao->select('id, linkbug')->from(TABLE_BUG)->where('id')->in(array_filter($changedLinkBugs))->fetchPairs();
|
|
|
|
foreach($changedLinkBugs as $changedBugID => $linkBugs)
|
|
{
|
|
if(in_array($changedBugID, $addedLinkBugs))
|
|
{
|
|
$linkBugs = explode(',', $linkBugs);
|
|
if(!empty($linkBugs) && !in_array($bugID, $linkBugs)) $linkBugs[] = $bugID;
|
|
}
|
|
else
|
|
{
|
|
$linkBugs = explode(',', $linkBugs);
|
|
unset($linkBugs[array_search($bugID, $linkBugs)]);
|
|
}
|
|
|
|
$currentLinkBug = implode(',', array_filter($linkBugs));
|
|
|
|
$this->dao->update(TABLE_BUG)->set('linkBug')->eq($currentLinkBug)->where('id')->eq($changedBugID)->exec();
|
|
}
|
|
|
|
return !dao::isError();
|
|
}
|
|
|
|
/**
|
|
* Call checkDelayBug in foreach to check if the bug is delay.
|
|
* 循环调用checkDelayBug,检查bug是否延期
|
|
*
|
|
* @param array $bugs
|
|
* @access protected
|
|
* @return object[]
|
|
*/
|
|
protected function batchAppendDelayedDays(array $bugs): array
|
|
{
|
|
foreach($bugs as $bug) $this->appendDelayedDays($bug);
|
|
|
|
return $bugs;
|
|
}
|
|
|
|
/**
|
|
* If the bug is delayed, add the bug->delay field to show the delay time (day).
|
|
* 添加bug->delay字段,内容为延期的时长(天),不延期则为0
|
|
*
|
|
* @param object $bug
|
|
* @access protected
|
|
* @return object
|
|
*/
|
|
protected function appendDelayedDays(object $bug): object
|
|
{
|
|
if(helper::isZeroDate($bug->deadline)) return $bug;
|
|
|
|
$delay = 0;
|
|
if($bug->resolvedDate and !helper::isZeroDate($bug->resolvedDate))
|
|
{
|
|
$delay = helper::diffDate(substr($bug->resolvedDate, 0, 10), $bug->deadline);
|
|
}
|
|
elseif($bug->status == 'active')
|
|
{
|
|
$delay = helper::diffDate(helper::today(), $bug->deadline);
|
|
}
|
|
|
|
if($delay > 0) $bug->delay = $delay;
|
|
|
|
return $bug;
|
|
}
|
|
|
|
/**
|
|
* 关闭bug后,更新看板的状态。
|
|
* Update kanban status after close bug.
|
|
*
|
|
* @param object $bug
|
|
* @param object $oldBug
|
|
* @access protected
|
|
* @return array
|
|
*/
|
|
protected function updateKanbanAfterClose(object $bug, object $oldBug):array
|
|
{
|
|
$extra = str_replace(array(',', ' '), array('&', ''), $extra);
|
|
parse_str($extra, $output);
|
|
if($oldBug->execution)
|
|
{
|
|
$this->loadModel('kanban');
|
|
if(!isset($output['toColID'])) $this->kanban->updateLane($oldBug->execution, 'bug', $bug->id);
|
|
if(isset($output['toColID'])) $this->kanban->moveCard($bug->id, $output['fromColID'], $output['toColID'], $output['fromLaneID'], $output['toLaneID']);
|
|
}
|
|
|
|
return array($bug, $oldBug);
|
|
}
|
|
|
|
/**
|
|
* 关闭bug后,更新动态
|
|
* Update action after close bug.
|
|
*
|
|
* @param object $bug
|
|
* @param object $oldBug
|
|
* @access protected
|
|
* @return array
|
|
*/
|
|
protected function updateActionAfterClose(object $bug, object $oldBug):array
|
|
{
|
|
if(($this->config->edition == 'biz' || $this->config->edition == 'max') && $oldBug->feedback) $this->loadModel('feedback')->updateStatus('bug', $oldBug->feedback, $bug->status, $oldBug->status);
|
|
|
|
$this->loadModel('action');
|
|
$changes = common::createChanges($oldBug, $bug);
|
|
$actionID = $this->action->create('bug', $bug->id, 'Closed', $bug->comment);
|
|
$this->action->logHistory($actionID, $changes);
|
|
|
|
return array($bug, $oldBug);
|
|
}
|
|
}
|