diff --git a/db/update20.0.sql b/db/update20.0.sql
index e8bbe6fb8a..f52dc6224d 100644
--- a/db/update20.0.sql
+++ b/db/update20.0.sql
@@ -24,10 +24,10 @@ CREATE TABLE `zt_storygrade` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `zt_story` ADD `grade` smallint(6) NOT NULL AFTER `parent`;
-ALTER TABLE `zt_story` ADD `path` varchar(255) NULL AFTER `grade`;
-update zt_story set grade = 1, path = concat(',', id, ',') where type != 'story';
-update zt_story set grade = 1, path = concat(',', id, ',') where type = 'story' and parent <= 0;
-update zt_story set grade = 2, path = concat(',', parent, ',', id, ',') where type = 'story' and parent > 0;
+ALTER TABLE `zt_story` ADD `top` mediumint NOT NULL DEFAULT '0' AFTER `parent`;
+update zt_story set grade = 1, parent = 0, top = id where type != 'story';
+update zt_story set grade = 1, parent = 0, top = id where type = 'story' and parent <= 0;
+update zt_story set grade = 2, top = parent where type = 'story' and parent > 0;
INSERT INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system', 'story', '', 'gradeRule', 'stepwise');
INSERT INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system', 'requirement', '', 'gradeRule', 'stepwise');
diff --git a/module/product/js/browse.ui.js b/module/product/js/browse.ui.js
index 08a5e55e03..6c260f7c6f 100644
--- a/module/product/js/browse.ui.js
+++ b/module/product/js/browse.ui.js
@@ -63,11 +63,11 @@ window.renderCell = function(result, info)
const story = info.row.data;
let html = '';
if(typeof modulePairs[story.rawModule] != 'undefined') html += "" + modulePairs[story.rawModule] + " ";
- if(story.parent > 0)
- {
- if($.cookie.get('tab') == 'project') html += story.parentName + ' / ';
- html += "" + (storyType == 'requirement' ? 'SR' : childrenAB) + " ";
- }
+ if(story.parent > 0) if($.cookie.get('tab') == 'project') html += story.parentName + ' / ';
+
+ let gradeLabel = gradeGroup[story.type][story.grade];
+ if(!showGrade && story.grade < 2) gradeLabel = '';
+ if(gradeLabel) html += "" + gradeLabel + " ";
if(story.color) result[0].props.style = 'color: ' + story.color;
if(html) result.unshift({html});
}
diff --git a/module/product/ui/browse.html.php b/module/product/ui/browse.html.php
index f68c60bc42..6092400bb5 100644
--- a/module/product/ui/browse.html.php
+++ b/module/product/ui/browse.html.php
@@ -15,6 +15,8 @@ namespace zin;
data('storyType', $storyType);
data('activeMenuID', $storyType);
jsVar('URChanged', $this->lang->story->URChanged);
+jsVar('gradeGroup', $gradeGroup);
+jsVar('showGrade', $showGrade);
$storyCommon = $storyType == 'requirement' ? $lang->URCommon : $lang->SRCommon;
$isProjectStory = $this->app->rawModule == 'projectstory';
diff --git a/module/product/zen.php b/module/product/zen.php
index 78bc0c5027..9dc85c39fc 100644
--- a/module/product/zen.php
+++ b/module/product/zen.php
@@ -1451,6 +1451,10 @@ class productZen extends product
$projectProducts = $this->getProjectProductList($projectID, $storyType, $isProjectStory);
list($branchOpt, $branchTagOpt) = $this->getBranchAndTagOption($projectID, $product, $isProjectStory);
+ $gradeList = $this->loadModel('story')->getGradeList('');
+ $gradeGroup = array();
+ foreach($gradeList as $grade) $gradeGroup[$grade->type][$grade->grade] = $grade->name;
+
/* Set show module by config. */
$showModule = empty($this->config->product->browse->showModule) ? 0 : $this->config->product->browse->showModule;
if($isProjectStory) $showModule = empty($this->config->projectstory->story->showModule) ? 0 : $this->config->projectstory->story->showModule;
@@ -1461,6 +1465,8 @@ class productZen extends product
$this->view->projectID = $projectID;
$this->view->project = $project;
$this->view->stories = $stories;
+ $this->view->gradeGroup = $gradeGroup;
+ $this->view->showGrade = $storyType == 'story' ? count($gradeGroup[$storyType]) > 2 : count($gradeGroup[$storyType]) > 1;
$this->view->storyType = $storyType;
$this->view->browseType = $browseType;
$this->view->isProjectStory = $isProjectStory;
diff --git a/module/story/model.php b/module/story/model.php
index 41cc961d1f..1bbf860cfb 100644
--- a/module/story/model.php
+++ b/module/story/model.php
@@ -447,8 +447,6 @@ class storyModel extends model
$storyID = $this->storyTao->doCreateStory($story);
if(!$storyID) return false;
- $this->dao->update(TABLE_STORY)->set('path')->eq(",{$storyID},")->where('id')->eq($storyID)->exec();
-
/* Upload files. */
$this->loadModel('action');
$this->loadModel('file')->updateObjectID($this->post->uid, $storyID, $story->type);
@@ -466,7 +464,14 @@ class storyModel extends model
if($bugID) $this->storyTao->closeBugWhenToStory($bugID, $storyID);
if(!empty($story->reviewer)) $this->storyTao->doCreateReviewer($storyID, $story->reviewer);
if(!empty($story->URS)) $this->storyTao->doCreateURRelations($storyID, $story->URS);
- if(!empty($story->parent)) $this->subdivide($story->parent, array($storyID));
+ if(!empty($story->parent))
+ {
+ $this->subdivide($story->parent, array($storyID));
+ }
+ else
+ {
+ $this->dao->update(TABLE_STORY)->set('top')->eq($storyID)->where('id')->eq($storyID)->exec();
+ }
if(!empty($story->plan))
{
$this->updateStoryOrderOfPlan($storyID, (string)$story->plan); // Set story order in this plan.
@@ -586,7 +591,14 @@ class storyModel extends model
if(!$storyID) return array();
$this->storyTao->doCreateSpec($storyID, $story);
- if($story->parent > 0) $this->subdivide($story->parent, array($storyID));
+ if(!empty($story->parent))
+ {
+ $this->subdivide($story->parent, array($storyID));
+ }
+ else
+ {
+ $this->dao->update(TABLE_STORY)->set('top')->eq($storyID)->where('id')->eq($storyID)->exec();
+ }
/* Update product plan stories order. */
if(!empty($story->reviewer)) $this->storyTao->doCreateReviewer($storyID, $story->reviewer);
@@ -748,10 +760,15 @@ class storyModel extends model
if($story->grade != $oldStory->grade)
{
$gradeDiff = (int)$story->grade - (int)$oldStory->grade;
- $this->dao->update(TABLE_STORY)->set("grade = grade + $gradeDiff")->where('path')->like($oldStory->path . '%')->andWhere('id')->ne($storyID)->exec();
+ $this->dao->update(TABLE_STORY)
+ ->set("grade = grade + $gradeDiff")
+ ->where('top')->eq($oldStory->top)
+ ->andWhere('grade')->gt($oldStory->grade)
+ ->andWhere('id')->ne($storyID)
+ ->exec();
}
$parentChanged = $story->parent != $oldStory->parent;
- if($parentChanged) $this->doChangeParent($storyID, $story, $oldStory->parent, $oldStory->path);
+ if($parentChanged) $this->doChangeParent($storyID, $story, $oldStory->parent);
if($oldStory->parent > 0) $this->updateParentStatus($storyID, $oldStory->parent, !$parentChanged);
if($story->parent > 0) $this->updateParentStatus($storyID, $story->parent, !$parentChanged);
@@ -1004,12 +1021,11 @@ class storyModel extends model
}
$this->loadModel('action');
- $changeGradeList = array();
foreach($stories as $storyID => $story)
{
$oldStory = $oldStories[$storyID];
if(!$story->grade) $story->grade = $oldStory->grade;
- $this->dao->update(TABLE_STORY)->data($story)
+ $this->dao->update(TABLE_STORY)->data($story, 'grade')
->autoCheck()
->checkIF($story->closedBy, 'closedReason', 'notempty')
->checkIF($story->closedReason == 'done', 'stage', 'notempty')
@@ -1020,14 +1036,20 @@ class storyModel extends model
if(dao::isError()) return false;
/* Update story sort of plan when story plan has changed. */
- if($oldStory->plan != $story->plan) $this->updateStoryOrderOfPlan($storyID, (string)$story->plan, $oldStory->plan);
if($story->grade != $oldStory->grade)
{
- $changeGradeList[$storyID]['oldGrade'] = $oldStory->grade;
- $changeGradeList[$storyID]['newGrade'] = $story->grade;
+ $gradeDiff = (int)$story->grade - (int)$oldStory->grade;
+ $this->dao->update(TABLE_STORY)
+ ->set("grade = grade + $gradeDiff")
+ ->where('top')->eq($oldStory->top)
+ ->andWhere('grade')->gt($oldStory->grade)
+ ->andWhere('id')->ne($storyID)
+ ->exec();
+ $this->dao->update(TABLE_STORY)->set('grade')->eq($story->grade)->where('id')->eq($storyID)->exec();
}
+ if($oldStory->plan != $story->plan) $this->updateStoryOrderOfPlan($storyID, (string)$story->plan, $oldStory->plan);
$parentChanged = $story->parent != $oldStory->parent;
- if($parentChanged) $this->doChangeParent($storyID, $story, $oldStory->parent, $oldStory->path);
+ if($parentChanged) $this->doChangeParent($storyID, $story, $oldStory->parent);
if($oldStory->parent > 0) $this->updateParentStatus($storyID, $oldStory->parent, !$parentChanged);
if($story->parent > 0) $this->updateParentStatus($storyID, $story->parent, !$parentChanged);
@@ -1049,12 +1071,6 @@ class storyModel extends model
}
}
- foreach($changeGradeList as $storyID => $oldStory)
- {
- $gradeDiff = $oldStory['newGrade'] - $oldStory['oldGrade'];
- $this->dao->update(TABLE_STORY)->set("grade = grade + $gradeDiff")->where('path')->like("%,{$storyID},%")->andWhere('id')->ne($storyID)->exec();
- }
-
$this->loadModel('score')->create('ajax', 'batchEdit');
foreach($unlinkPlans as $planID => $stories) $this->action->create('productplan', $planID, 'unlinkstory', '', $stories);
foreach($link2Plans as $planID => $stories) $this->action->create('productplan', $planID, 'linkstory', '', $stories);
@@ -1334,10 +1350,9 @@ class storyModel extends model
/* Set parent to child story. */
foreach($SRList as $childStoryID)
{
- $path = rtrim($oldStory->path, ',') . ",$childStoryID,";
$this->dao->update(TABLE_STORY)
->set('parent')->eq($storyID)
- ->set('path')->eq($path)
+ ->set('top')->eq($oldStory->top)
->where('id')->eq($childStoryID)
->exec();
}
@@ -2043,11 +2058,10 @@ class storyModel extends model
* 获取需求的所有子需求ID。
*
* @param int $storyID
- * @param bool $includeSelf
* @access public
* @return array
*/
- public function getAllChildId(int $storyID, bool $includeSelf = true): array
+ public function getAllChildId(int $storyID): array
{
if($storyID == 0) return array();
@@ -2055,9 +2069,9 @@ class storyModel extends model
if(empty($story)) return array();
$children = $this->dao->select('id')->from(TABLE_STORY)
- ->where('path')->like($story->path . '%')
- ->andWhere('deleted')->eq(0)
- ->beginIF(!$includeSelf)->andWhere('id')->ne($storyID)->fi()
+ ->where('deleted')->eq(0)
+ ->andWhere('top')->eq($story->top)
+ ->andWhere('grade')->gt($story->grade)
->fetchPairs();
return array_keys($children);
@@ -2214,10 +2228,10 @@ class storyModel extends model
* @param string|array $modules
* @param string $fieldName
* @param string $fieldValue
- * @param string $type requirement|story
+ * @param string $type requirement|story
* @param string $orderBy
* @param object $pager
- * @param string $operator equal|include
+ * @param string $operator equal|include
* @access public
* @return array
*/
@@ -4636,7 +4650,11 @@ class storyModel extends model
*/
public function getGradeList($type = 'story'): array
{
- return $this->dao->select('*')->from(TABLE_STORYGRADE)->where('type')->eq($type)->orderBy('grade_asc')->fetchAll();
+ return $this->dao->select('*')->from(TABLE_STORYGRADE)
+ ->where('1=1')
+ ->beginIF($type)->andWhere('type')->eq($type)->fi()
+ ->orderBy('grade_asc')
+ ->fetchAll();
}
/**
@@ -4719,7 +4737,7 @@ class storyModel extends model
public function checkGrade(object $story, object $oldStory, string $method = 'edit')
{
$maxGrade = $this->dao->select('max(grade) as maxGrade')->from(TABLE_STORY)
- ->where('path')->like("{$oldStory->path}%")
+ ->where('top')->eq($oldStory->top)
->andWhere('deleted')->eq('0')
->fetch('maxGrade');
diff --git a/module/story/tao.php b/module/story/tao.php
index a91b75a560..03062a0229 100644
--- a/module/story/tao.php
+++ b/module/story/tao.php
@@ -817,14 +817,11 @@ class storyTao extends storyModel
* @param int $storyID
* @param object $story
* @param int $oldStoryParent
- * @param string $oldStoryPath
* @access protected
* @return void
*/
- protected function doChangeParent(int $storyID, object $story, int $oldStoryParent, string $oldStoryPath)
+ protected function doChangeParent(int $storyID, object $story, int $oldStoryParent)
{
- if($story->parent == $oldStoryParent) return;
-
$this->loadModel('action');
if($oldStoryParent > 0)
{
@@ -841,9 +838,9 @@ class storyTao extends storyModel
if($story->parent > 0)
{
- $parentStory = $this->dao->select('*')->from(TABLE_STORY)->where('id')->eq($story->parent)->fetch();
- $story->path = rtrim($parentStory->path, ',') . ',' . $storyID . ',';
- $children = $this->dao->select('id')->from(TABLE_STORY)->where('parent')->eq($story->parent)->andWhere('deleted')->eq(0)->fetchPairs('id', 'id');
+ $parentStory = $this->dao->select('*')->from(TABLE_STORY)->where('id')->eq($story->parent)->fetch();
+ $newTop = $parentStory->top;
+ $children = $this->dao->select('id')->from(TABLE_STORY)->where('parent')->eq($story->parent)->andWhere('deleted')->eq(0)->fetchPairs('id', 'id');
$this->dao->update(TABLE_STORY)
->set('childStories')->eq(implode(',', $children))
->set('lastEditedBy')->eq($this->app->user->account)
@@ -859,16 +856,12 @@ class storyTao extends storyModel
}
else
{
- $story->path = ",$storyID,";
+ $newTop = $storyID;
}
$childStories = $this->getAllChildId($storyID);
- foreach($childStories as $childStoryID)
- {
- $oldChildPath = $this->dao->select('path')->from(TABLE_STORY)->where('id')->eq($childStoryID)->fetch('path');
- $newChildPath = str_replace($oldStoryPath, $story->path, $oldChildPath);
- $this->dao->update(TABLE_STORY)->set('path')->eq($newChildPath)->where('id')->eq($childStoryID)->exec();
- }
+ if($childStories) $this->dao->update(TABLE_STORY)->set('top')->eq($newTop)->where('id')->in($childStories)->exec();
+ $this->dao->update(TABLE_STORY)->set('top')->eq($newTop)->where('id')->eq($storyID)->exec();
}
/**