需求背景

  • 测试人员反馈 禅道通过csv导入的用例数量大的情况下一一去关联需求很麻烦,效率不高
  • image.png
  • 希望可以有一个一键批量关联相关用例的功能以供使用。

    修改思路

    image.png

  • 在途中红框处添加一个相关需求的选项,以批量编辑关联需求。

    涉及修改 - 自顶向下

  • view

    • testcase/view/browse.html.php
  • control
    • 添加 batchCaseStoryChange 方法
  • model

    • 添加 btachCaseStoryChange 调用的model层方法

      具体实现

      browse.html.php

      ```php

      1. // FIXME 批量修改 pri 优先级
      2. if(common::hasPriv('testcase', 'batchCasePriChange'))
      3. {
      4. echo "<li class='dropdown-submenu'>";
      5. echo html::a('javascript:;', $lang->testcase->pri, '', "id='priChangeItem'");
      6. echo "<ul class='dropdown-menu'>";
      7. unset($lang->testcase->priList['']);
      8. foreach($lang->testcase->priList as $key => $result)
      9. {
      10. $actionLink = $this->createLink('testcase', 'batchCasePriChange', "result=$key");
      11. echo '<li>' . html::a('#', $result, '', "onclick=\"setFormAction('$actionLink', 'hiddenwin')\"") . '</li>';
      12. }
      13. echo '</ul></li>';
      14. }
      15. // end of FIXME
  1. // FIXME 批量修改 story 关联需求
  2. if(common::hasPriv('testcase', 'batchCaseStoryChange'))
  3. {
  4. echo "<li class='dropdown-submenu'>";
  5. echo html::a('javascript:;', $lang->testcase->story, '', "id='storyChangeItem'");
  6. echo "<ul class='dropdown-menu'>";

// unset($lang->testcase->priList[‘’]); foreach($storyList as $key => $result) { $actionLink = $this->createLink(‘testcase’, ‘batchCaseStoryChange’, “result=$key”); echo ‘

  • ‘ . html::a(‘#’, $result, ‘’, “onclick=\”setFormAction(‘$actionLink’, ‘hiddenwin’)\””) . ‘
  • ‘; } echo ‘‘; } // end of FIXME

    1. if(common::hasPriv('testcase', 'batchCaseTypeChange'))
    2. {
    3. echo "<li class='dropdown-submenu'>";
    4. echo html::a('javascript:;', $lang->testcase->type, '', "id='typeChangeItem'");
    5. echo "<ul class='dropdown-menu'>";
    6. unset($lang->testcase->typeList['']);
    7. foreach($lang->testcase->typeList as $key => $result)
    8. {
    9. $actionLink = $this->createLink('testcase', 'batchCaseTypeChange', "result=$key");
    10. echo '<li>' . html::a('#', $result, '', "onclick=\"setFormAction('$actionLink', 'hiddenwin')\"") . '</li>';
    11. }
    12. echo '</ul></li>';
    13. }
    1. - 参照原有逻辑,如批量修改用例类来进行本次拓展
    2. <a name="bZZhy"></a>
    3. ## control层拓展
    4. ```php
    5. <?php
    6. include '../../control.php';
    7. class myTestCase extends testcase{
    8. // FIXME 新增 批量更改关联需求
    9. /*
    10. * $result 前端页面传回的目标值
    11. *
    12. *
    13. *
    14. */
    15. public function batchCaseStoryChange($result){
    16. $caseIdList = $this->post->caseIDList ? $this->post->caseIDList : die(js::locate($this->session->caseList, 'parent'));
    17. $caseIDList = array_unique($caseIDList);
    18. // 调用新增的 btachCase
    19. $this->testcase->batchCaseStoryChange($caseIdList, $result);
    20. if(dao::isError()) die(js::error(dao::getError()));
    21. die(js::locate($this->session->caseList, 'parent'));
    22. }
    23. }
    • 按照禅道的扩展规则来进行control方法扩展,具体方法参考与安生逻辑

      model层扩展

      ```php <?php

    public function batchCaseStoryChange($caseIdList, $result){

    1. $now = helper::now();
    2. $actions = array();
    3. $this->loadModel('action');
    4. foreach($caseIdList as $caseID) {
    5. $case = new stdClass();
    6. $case->lastEditedBy = $this->app->user->account;
    7. $case->lastEditedDate = $now;
    8. // FIXME 优先级更改为需求变更菜单
    9. $case->story = $result;
    10. $this->dao->update(TABLE_CASE)->data($case)->autoCheck()->where('id')->eq($caseID)->exec();
    11. $this->action->create('case', $caseID, 'Edited', '', ucfirst($result));
    12. }

    }

    1. - model 层为control 层提供调用方法,
    2. - 整体调用链条 control 调用model 方法,获取到数据 渲染至页面。
    3. <a name="zeGwm"></a>
    4. # 坑点
    5. - 因是二开功能,所以页面上没有story变量 也就是说,browse页面没有story变量,所以如何吧当前产品的所有需求渲染至页面是本功能二开的一个难点。
    6. <a name="PcCiB"></a>
    7. # 解决思路
    8. - 首先去寻找禅道原生代码中类似的逻辑,参考其代码get到当前产品中所有的需求列表,渲染至前端。下见代码
    9. ```php
    10. <?php
    11. include '../../control.php';
    12. class myTestCase extends testcase{
    13. /**
    14. * Browse cases.
    15. *
    16. * @param int $productID
    17. * @param string $browseType
    18. * @param int $param
    19. * @param string $orderBy
    20. * @param int $recTotal
    21. * @param int $recPerPage
    22. * @param int $pageID
    23. * @access public
    24. * @return void
    25. */
    26. public function browse($productID = 0, $branch = '', $browseType = 'all', $param = 0, $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
    27. {
    28. $this->loadModel('datatable');
    29. // FIXME 加载story模块 供后续直接进行调用
    30. $this->loadModel('story');
    31. /* Set browse type. */
    32. $browseType = strtolower($browseType);
    33. /* Set browseType, productID, moduleID and queryID. */
    34. $productID = $this->product->saveState($productID, $this->products);
    35. $branch = ($branch === '') ? (int)$this->cookie->preBranch : (int)$branch;
    36. setcookie('preProductID', $productID, $this->config->cookieLife, $this->config->webRoot, '', false, true);
    37. setcookie('preBranch', (int)$branch, $this->config->cookieLife, $this->config->webRoot, '', false, true);
    38. if($this->cookie->preProductID != $productID or $this->cookie->preBranch != $branch)
    39. {
    40. $_COOKIE['caseModule'] = 0;
    41. setcookie('caseModule', 0, 0, $this->config->webRoot, '', false, false);
    42. }
    43. if($browseType == 'bymodule') setcookie('caseModule', (int)$param, 0, $this->config->webRoot, '', false, false);
    44. if($browseType == 'bysuite') setcookie('caseSuite', (int)$param, 0, $this->config->webRoot, '', false, true);
    45. if($browseType != 'bymodule') $this->session->set('caseBrowseType', $browseType);
    46. $moduleID = ($browseType == 'bymodule') ? (int)$param : ($browseType == 'bysearch' ? 0 : ($this->cookie->caseModule ? $this->cookie->caseModule : 0));
    47. $suiteID = ($browseType == 'bysuite') ? (int)$param : ($browseType == 'bymodule' ? ($this->cookie->caseSuite ? $this->cookie->caseSuite : 0) : 0);
    48. $queryID = ($browseType == 'bysearch') ? (int)$param : 0;
    49. /* Set menu, save session. */
    50. $this->testcase->setMenu($this->products, $productID, $branch, $moduleID, $suiteID, $orderBy);
    51. $this->session->set('caseList', $this->app->getURI(true));
    52. $this->session->set('productID', $productID);
    53. $this->session->set('moduleID', $moduleID);
    54. $this->session->set('browseType', $browseType);
    55. $this->session->set('orderBy', $orderBy);
    56. /* Load lang. */
    57. $this->app->loadLang('testtask');
    58. /* Load pager. */
    59. $this->app->loadClass('pager', $static = true);
    60. $pager = pager::init($recTotal, $recPerPage, $pageID);
    61. $sort = $this->loadModel('common')->appendOrder($orderBy);
    62. /* Get test cases. */
    63. $cases = $this->testcase->getTestCases($productID, $branch, $browseType, $browseType == 'bysearch' ? $queryID : $suiteID, $moduleID, $sort, $pager);
    64. /* save session .*/
    65. $this->loadModel('common')->saveQueryCondition($this->dao->get(), 'testcase', $browseType != 'bysearch' ? false : true);
    66. /* Process case for check story changed. */
    67. $cases = $this->loadModel('story')->checkNeedConfirm($cases);
    68. $cases = $this->testcase->appendData($cases);
    69. /* Build the search form. */
    70. $actionURL = $this->createLink('testcase', 'browse', "productID=$productID&branch=$branch&browseType=bySearch&queryID=myQueryID");
    71. $this->config->testcase->search['onMenuBar'] = 'yes';
    72. $this->testcase->buildSearchForm($productID, $this->products, $queryID, $actionURL);
    73. $showModule = !empty($this->config->datatable->testcaseBrowse->showModule) ? $this->config->datatable->testcaseBrowse->showModule : '';
    74. $this->view->modulePairs = $showModule ? $this->tree->getModulePairs($productID, 'case', $showModule) : array();
    75. // FIXME assign 赋值需求列表
    76. // test -- 调用story 模块下的方法去获取到所有的story列表之后渲染到前端页面
    77. // todo 核心代码为
    78. $status = '';
    79. $limit = 0;
    80. $type = 'full';
    81. $hasParent = 1;
    82. if($moduleID)
    83. {
    84. $moduleID = $this->loadModel('tree')->getStoryModule($moduleID);
    85. $moduleID = $this->tree->getAllChildID($moduleID);
    86. }
    87. $storyStatus = '';
    88. if($status == 'noclosed')
    89. {
    90. $storyStatus = $this->lang->story->statusList;
    91. unset($storyStatus['closed']);
    92. $storyStatus = array_keys($storyStatus);
    93. }
    94. // 观察到下面的代码是取得所有需求列表的关键代码,分析其参数构成,进行初始化调用。
    95. $stories = $this->story->getProductStoryPairs($productID, $branch ? "0,$branch" : $branch, $moduleID, $storyStatus, 'id_desc', $limit, $type, 'story', $hasParent);
    96. // 渲染到前端页面,之后进行测试
    97. $this->view->storyList = $stories;
    98. /* Assign. */
    99. $tree = $moduleID ? $this->tree->getByID($moduleID) : '';
    100. $this->view->title = $this->products[$productID] . $this->lang->colon . $this->lang->testcase->common;
    101. $this->view->position[] = html::a($this->createLink('testcase', 'browse', "productID=$productID&branch=$branch"), $this->products[$productID]);
    102. $this->view->position[] = $this->lang->testcase->common;
    103. $this->view->productID = $productID;
    104. $this->view->product = $this->product->getById($productID);
    105. $this->view->productName = $this->products[$productID];
    106. $this->view->modules = $this->tree->getOptionMenu($productID, $viewType = 'case', $startModuleID = 0, $branch);
    107. $this->view->moduleTree = $this->tree->getTreeMenu($productID, $viewType = 'case', $startModuleID = 0, array('treeModel', 'createCaseLink'), '', $branch);
    108. $this->view->moduleName = $moduleID ? $tree->name : $this->lang->tree->all;
    109. $this->view->moduleID = $moduleID;
    110. $this->view->summary = $this->testcase->summary($cases);
    111. $this->view->pager = $pager;
    112. $this->view->users = $this->user->getPairs('noletter');
    113. $this->view->orderBy = $orderBy;
    114. $this->view->browseType = $browseType;
    115. $this->view->param = $param;
    116. $this->view->cases = $cases;
    117. $this->view->branch = $branch;
    118. $this->view->branches = $this->loadModel('branch')->getPairs($productID);
    119. $this->view->suiteList = $this->loadModel('testsuite')->getSuites($productID);
    120. $this->view->suiteID = $suiteID;
    121. $this->view->setModule = true;
    122. $this->display();
    123. }
    124. }