* product: refactor view function.

This commit is contained in:
chentao
2023-05-09 14:43:22 +08:00
parent 0a3786c69e
commit ea394d6800
11 changed files with 482 additions and 62 deletions
+8 -13
View File
@@ -348,36 +348,31 @@ class product extends control
}
/**
* 查看产品。
* View a product.
*
* @param int $productID
* @access public
* @return void
*/
public function view($productID)
public function view(string $productID)
{
$productID = (int)$productID;
$product = $this->product->getStatByID($productID);
if(!$product)
{
if(defined('RUN_MODE') && RUN_MODE == 'api') return $this->send(array('status' => 'fail', 'code' => 404, 'message' => '404 Not found'));
return print(js::error($this->lang->notFound) . js::locate($this->createLink('product', 'index')));
}
/* Get product. */
$product = $this->product->getStatByID($productID);
if(!$product) return $this->productZen->responseNotFound4View();
$product->desc = $this->loadModel('file')->setImgSize($product->desc);
/* Set navigation menu. */
$this->product->setMenu($productID);
/* Load pager. */
$this->app->loadClass('pager', $static = true);
$pager = new pager(0, 30, 1);
/* Execute hooks. */
$this->executeHooks($productID);
$this->view->title = $product->name . $this->lang->colon . $this->lang->product->view;
$this->view->position[] = html::a($this->createLink($this->moduleName, 'browse'), $product->name);
$this->view->position[] = $this->lang->product->view;
$this->view->product = $product;
$this->view->actions = $this->loadModel('action')->getList('product', $productID);
$this->view->users = $this->user->getPairs('noletter');
+16 -48
View File
@@ -349,7 +349,7 @@ class productModel extends model
*/
public function getStatusGroups()
{
$products = $this->dao->select('id, name, status')->from(TABLE_PRODUCT)->where('deleted')->eq(0)->fetchGroup('status');
return $this->dao->select('id, name, status')->from(TABLE_PRODUCT)->where('deleted')->eq(0)->fetchGroup('status');
}
/**
@@ -1279,7 +1279,8 @@ class productModel extends model
}
/**
* Get product stat by id
* 使用产品ID获取产品统计信息。
* Get product stat data by product ID.
*
* @param int $productID
* @param string $storyType
@@ -1288,57 +1289,24 @@ class productModel extends model
*/
public function getStatByID($productID, $storyType = 'story')
{
/* Check privilege. */
if(!$this->checkPriv($productID)) return false;
/* Get product. */
$product = $this->getById($productID);
if(empty($product)) return false;
$stories = $this->dao->select('product, status, count(status) AS count')->from(TABLE_STORY)
->where('deleted')->eq(0)
->andWhere('type')->eq($storyType)
->andWhere('product')->eq($productID)
->groupBy('product, status')
->fetchAll('status');
/* Padding the stories to sure all status have records. */
foreach(array_keys($this->lang->story->statusList) as $status)
{
$stories[$status] = isset($stories[$status]) ? $stories[$status]->count : 0;
}
$plans = $this->dao->select('count(*) AS count')->from(TABLE_PRODUCTPLAN)->where('deleted')->eq(0)->andWhere('product')->eq($productID)->andWhere('end')->gt(helper::now())->fetch();
$builds = $this->dao->select('count(*) AS count')->from(TABLE_BUILD)->where('product')->eq($productID)->andWhere('deleted')->eq(0)->fetch();
$cases = $this->dao->select('count(*) AS count')->from(TABLE_CASE)->where('product')->eq($productID)->andWhere('deleted')->eq(0)->fetch();
$bugs = $this->dao->select('count(*) AS count')->from(TABLE_BUG)->where('product')->eq($productID)->andWhere('deleted')->eq(0)->fetch();
$docs = $this->dao->select('count(*) AS count')->from(TABLE_DOC)->where('product')->eq($productID)->andWhere('deleted')->eq(0)->fetch();
$releases = $this->dao->select('count(*) AS count')->from(TABLE_RELEASE)->where('deleted')->eq(0)->andWhere('product')->eq($productID)->fetch();
$projects = $this->dao->select('count(*) AS count')->from(TABLE_PROJECTPRODUCT)->alias('t1')
->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project = t2.id')
->where('t2.deleted')->eq(0)
->andWhere('t1.product')->eq($productID)
->andWhere('t2.type')->eq('project')
->fetch();
$executions = $this->dao->select('count(*) AS count')->from(TABLE_PROJECTPRODUCT)->alias('t1')
->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project = t2.id')
->where('t2.deleted')->eq(0)
->andWhere('t1.product')->eq($productID)
->andWhere('t2.type')->in('sprint,stage,kanban')
->fetch();
$product->stories = $stories;
$product->plans = $plans ? $plans->count : 0;
$product->releases = $releases ? $releases->count : 0;
$product->builds = $builds ? $builds->count : 0;
$product->cases = $cases ? $cases->count : 0;
$product->projects = $projects ? $projects->count : 0;
$product->executions = $executions ? $executions->count : 0;
$product->bugs = $bugs ? $bugs->count : 0;
$product->docs = $docs ? $docs->count : 0;
$closedTotal = $this->dao->select('count(id) AS count')->from(TABLE_STORY)->where('deleted')->eq(0)->andWhere('product')->eq($productID)->andWhere('status')->eq('closed')->fetch('count');
$allTotal = $this->dao->select('count(id) AS count')->from(TABLE_STORY)->where('deleted')->eq(0)->andWhere('product')->eq($productID)->fetch('count');
$product->progress = empty($closedTotal) ? 0 : round($closedTotal / $allTotal * 100, 1);
/* Attach statistic data. */
$product->stories = $this->productTao->getStoryStatusCountByID($productID, $storyType);
$product->plans = $this->productTao->getStatCountByID(TABLE_PRODUCTPLAN, $productID);
$product->releases = $this->productTao->getStatCountByID(TABLE_BUILD, $productID);
$product->builds = $this->productTao->getStatCountByID(TABLE_CASE, $productID);
$product->cases = $this->productTao->getStatCountByID(TABLE_BUG, $productID);
$product->projects = $this->productTao->getStatCountByID(TABLE_DOC, $productID);
$product->executions = $this->productTao->getStatCountByID(TABLE_RELEASE, $productID);
$product->bugs = $this->productTao->getStatCountByID(TABLE_PROJECTPRODUCT, $productID);
$product->docs = $this->productTao->getStatCountByID('executions', $productID);
$product->progress = $this->productTao->getStatCountByID('progress', $productID);
return $product;
}
+93
View File
@@ -918,4 +918,97 @@ class productTao extends productModel
return array($roadmaps, $total, $return);
}
/**
* 通过产品ID查询产品关联需求的各状态统计总数。
* Get stories total count of each status by product ID.
*
* @param int $productID
* @param string $storyType
* @access protected
* @return array
*/
protected function getStoryStatusCountByID(int $productID, string $storyType = 'story'): array
{
/* TODO move to story module. */
$stories = $this->dao->select('product, status, count(status) AS count')->from(TABLE_STORY)
->where('deleted')->eq(0)
->andWhere('type')->eq($storyType)
->andWhere('product')->eq($productID)
->groupBy('product, status')
->fetchAll('status');
/* Padding the stories to sure all status have records. */
foreach(array_keys($this->lang->story->statusList) as $status)
{
$stories[$status] = isset($stories[$status]) ? $stories[$status]->count : 0;
}
return $stories;
}
/**
* 通过产品ID查询产品关联的统计数据。
* Get stat count by product ID.
*
* @param int $productID
* @access protected
* @return int
*/
protected function getStatCountByID(string $tableName, int $productID): int
{
switch ($tableName)
{
case TABLE_PRODUCTPLAN:
/* Get unclosed plans count. */
$record = $this->dao->select('count(*) AS count')->from(TABLE_PRODUCTPLAN)->where('deleted')->eq(0)->andWhere('product')->eq($productID)->andWhere('end')->gt(helper::now())->fetch();
break;
case TABLE_BUILD:
/* Get builds count. */
$record = $this->dao->select('count(*) AS count')->from(TABLE_BUILD)->where('product')->eq($productID)->andWhere('deleted')->eq(0)->fetch();
break;
case TABLE_CASE:
/* Get cases count. */
$record = $this->dao->select('count(*) AS count')->from(TABLE_CASE)->where('product')->eq($productID)->andWhere('deleted')->eq(0)->fetch();
break;
case TABLE_BUG:
/* Get bugs count. */
$record = $this->dao->select('count(*) AS count')->from(TABLE_BUG)->where('product')->eq($productID)->andWhere('deleted')->eq(0)->fetch();
break;
case TABLE_DOC:
/* Get docs count. */
$record = $this->dao->select('count(*) AS count')->from(TABLE_DOC)->where('product')->eq($productID)->andWhere('deleted')->eq(0)->fetch();
break;
case TABLE_RELEASE:
/* Get releases count. */
$record = $this->dao->select('count(*) AS count')->from(TABLE_RELEASE)->where('deleted')->eq(0)->andWhere('product')->eq($productID)->fetch();
break;
case TABLE_PROJECTPRODUCT:
$record = $this->dao->select('count(*) AS count')->from(TABLE_PROJECTPRODUCT)->alias('t1')
->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project = t2.id')
->where('t2.deleted')->eq(0)
->andWhere('t1.product')->eq($productID)
->andWhere('t2.type')->eq('project')
->fetch();
break;
case 'executions':
$record = $this->dao->select('count(*) AS count')->from(TABLE_PROJECTPRODUCT)->alias('t1')
->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project = t2.id')
->where('t2.deleted')->eq(0)
->andWhere('t1.product')->eq($productID)
->andWhere('t2.type')->in('sprint,stage,kanban')
->fetch();
break;
case 'progress':
$closedTotal = $this->dao->select('count(id) AS count')->from(TABLE_STORY)->where('deleted')->eq(0)->andWhere('product')->eq($productID)->andWhere('status')->eq('closed')->fetch('count');
if(empty($closedTotal)) return 0;
$allTotal = $this->dao->select('count(id) AS count')->from(TABLE_STORY)->where('deleted')->eq(0)->andWhere('product')->eq($productID)->fetch('count');
return round($closedTotal / $allTotal * 100, 1);
default:
return 0;
}
return $record ? $record->count : 0;
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 5) . '/test/lib/init.php';
include dirname(__FILE__, 2) . '/product.class.php';
function initData()
View File
View File
+24
View File
@@ -0,0 +1,24 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . '/test/lib/init.php';
include dirname(__FILE__, 2) . '/product.class.php';
function initData()
{
zdTable('productplan')->config('productplan')->gen(100);
zdTable('case')->config('case')->gen(100);
zdTable('bug')->config('bug')->gen(100);
}
initData();
/**
title=productTao->getStatCountByID();
cid=2
*/
$product = new productTest('admin');
r($product->objectModel->getStatCountByID(TABLE_PRODUCTPLAN, 1)) && p('') && e('5');
r($product->objectModel->getStatCountByID(TABLE_CASE, 1)) && p('') && e('4');
r($product->objectModel->getStatCountByID(TABLE_BUG, 1)) && p('') && e('3');
@@ -0,0 +1,129 @@
title: zt_bug
desc: "Bug表"
author: automated export
version: "1.0"
fields:
- field: id
range: 1-10000
- field: project
range: 11-100{3},0{30}
- field: product
range: 1-100{3}
- field: branch
range: 0{120},0-2,0,3-4,0,5-6,0,7-8,0,9-10,0{100000000}
- field: module
range: 1821-1823,1825-1827,1831-1833,0{1000000}
- field: execution
range: 101-700{3}
- field: plan
range: 1{3},4{3},7{3},0{1111111}
- field: story
range: 2-400:4
- field: storyVersion
range: 1
- field: task
range: 0
- field: toTask
range: 0
- field: toStory
range: 0
- field: title
fields:
- field: title1
range: BUG{6},`缺陷!@()[]{}|\+=%^&*$#测试bug名称到底可以有多长!@#¥%&*'":.<>。?/();`,bug
- field: title2
range: 1-10000
- field: keywords
range: "[]"
- field: severity
range: 1-4
- field: pri
range: 1-4
- field: type
range: "[codeerror,config,install,security,performance,standard,automation,designdefect,others]"
- field: os
range: "[]{2},[win10,windows,android,ios]"
- field: browser
range: "[]"
- field: hardware
range: "[]"
- field: found
range: "[]"
- field: steps
range: "[<p>【步骤】</p><br/><p>【结果】</p><br/><p>【期望】</p><br/>,【步骤】进入成果展示<br/>【结果】乱码<br/>【期望】正常显示<br/>]"
- field: status
range: active{50},resolved{30},closed{20}
- field: subStatus
range: "[]"
- field: color
range: "[#3da7f5,#75c941,#2dbdb2,#797ec9,#ffaf38,#ff4e3e]"
- field: confirmed
range: 0,1
- field: activatedCount
range: 0
- field: mailto
range: admin{100},[]{200}
- field: openedBy
range: admin
- field: openedDate
range: "(-1M)-(+1w):-1D"
type: timestamp
format: "YYYY-MM-DD hh:mm:ss"
- field: openedBuild
range: 1,0,1,trunk{97}
- field: assignedTo
range: "[admin,dev1,test1]{30},[]{20},[admin,dev1,test1]{20},[]{10},closed{20}"
- field: assignedDate
range: "(-1M)-(+1w):-1D"
type: timestamp
format: "YYYY-MM-DD"
- field: deadline
range: "(-1M)-(+1w):-1D"
type: timestamp
prefix: ""
postfix: ""
loop: 0
format: "YYYY-MM-DD"
- field: resolvedBy
range: "[]{50},$assignedTo{30},admin{20}"
- field: resolution
range: "[]{50},bydesign{5},duplicate{5},external{5},fixed{5},notrepro{2},postponed{3},willnotfix{5},fixed{10},bydesign,external,notrepro,postponed,willnotfix,duplicate{5}"
- field: resolvedBuild
range: "[]{55},trunk{15},[]{25},trunk{5}"
- field: resolvedDate
range: "(+1w)-(-2w):-1D"
type: timestamp
prefix: ""
postfix: ""
loop: 0
format: "YYYY-MM-DD"
- field: closedBy
range: "[]{50},admin{30}"
- field: duplicateBug
range: 0{55},1-5,0{40},10-15
- field: linkBug
range:
- field: case
range: 0
- field: caseVersion
range: 0
- field: result
range: 0
- field: repo
range: 0
- field: entry
range:
- field: lines
range:
- field: v1
range:
- field: v2
range:
- field: repoType
range:
- field: testtask
range: 0
- field: lastEditedBy
range:
- field: deleted
range: 0{40},1{10},0{50}
@@ -0,0 +1,146 @@
title: table zt_case
desc: "测试用例"
author: automated export
version: "1.0"
fields:
- field: id
note: "ID"
range: 1-400
- field: product
note: "产品ID"
range: 1-100{4}
- field: branch
note: "分支ID"
range: 0
- field: execution
note: "执行ID"
range: 101-700{4}
- field: lib
note: "所属库"
range: 0
- field: module
note: "所属模块"
range: 0
- field: path
note: ""
range: "0"
- field: story
note: "相关需求"
range: 2-400:4{4}
- field: storyVersion
note: "需求版本"
range: "1"
- field: title
note: "用例标题"
range: "1-400"
prefix: "这个是测试用例"
- field: precondition
note: "前置条件"
range: "1-400"
prefix: "这是前置条件"
- field: keywords
note: "关键词"
range: "1-400"
prefix: "这是关键词"
- field: pri
note: "优先级"
range: 1-4
- field: type
note: "用例类型"
range: feature,performance,config,install,security,interface,other
- field: auto
note: "是否是自动化测试用例"
range: "no"
- field: frame
note: "自动化测试框架"
range: ""
- field: stage
note: "适用阶段"
range: unittest,feature,intergrate,system,smoke,bvt
- field: howRun
note: "测试方式"
range: ""
- field: scriptedBy
note: "脚本由谁创建"
range: ""
- field: scriptedDate
note: "脚本创建日期"
range: "(M)-(w)"
type: timestamp
postfix: ""
format: "YY/MM/DD"
- field: scriptStatus
note: "脚本状态"
range: ""
- field: scriptLocation
note: "脚本地址"
range: ""
- field: status
note: "用例状态"
range: wait,normal,blocked,investigate
- field: subStatus
note: "子状态"
range: ""
- field: color
note: "标题颜色"
- field: frequency
note: "使用频率"
range: 1-3
- field: order
note: "排序"
range: 1-500:5
- field: openedBy
note: "由谁创建"
range: 1-100
prefix: "test"
- field: openedDate
note: "创建日期"
range: "-:600"
type: timestamp
format: "YYYY-MM-DD hh:mm:ss"
- field: reviewedBy
note: "由谁评审"
range: admin
- field: reviewedDate
note: "评审时间"
range: "-:300"
type: timestamp
format: "YYYY-MM-DD hh:mm:ss"
- field: lastEditedBy
note: "最后修改者"
range: admin
- field: lastEditedDate
note: "修改日期"
range: "-:300"
type: timestamp
format: "YYYY-MM-DD hh:mm:ss"
- field: version
note: "用例版本"
range: "1"
- field: linkCase
note: "相关用例"
range: 0
- field: fromBug
note: "来源Bug"
range: 0
- field: fromCaseID
note: ""
range: 0
- field: fromCaseVersion
note: ""
range: 1
- field: deleted
note: "是否删除"
range: 0{9950},1{50}
- field: lastRunner
note: "执行人"
range: 1-100
prefix: "test"
- field: lastRunDate
note: "执行时间"
range: "-:500"
type: timestamp
format: "YYYY-MM-DD hh:mm:ss"
- field: lastRunResult
note: "结果"
range: "[pass,fail]"
@@ -0,0 +1,46 @@
title: table zt_productplan
desc: "产品计划"
author: sgm
version: "1.0"
fields:
- field: id
note: "ID"
range: 1-100
- field: product
note: "所属产品"
range: 1-10
- field: branch
note: "所属分支"
range: 0{30},1-40
- field: parent
note: "父计划"
range: 0
- field: title
note: "名称"
range: "[1.0,1.1,长名称好长好长好长好长好长好长好长好长好长好长好长好好长好长好长好长好长好好长好长好长好长好长好好长好长好长好长好长好的名字啊!@ase!!@#%@^&#!]"
- field: desc
note: "描述"
range: <div>
<p>asdxxxfa阿萨德开机as家世界发卡机发设计费啥登记卡设计费卡加斯加附件阿峰</p>
<p>123a阿士大夫姐姐姐经历过反对派呸呸呸史蒂夫接收到付款纪委第三方乐山大佛量减少地方</p>
<p>IU风格就叫阿萨德奥斯丁哦IG是DAU发加速度快哦哦阿萨德几年内发,,,奥斯丁,,,切,,,请问加十多年你</p>
<p>IUIG是DAU发加速度快哦哦阿萨德几年内发,,,奥斯丁,,,切,,,请问加十多年你</p>
<p>IU风格就叫阿萨德奥斯丁哦IG是DAU,,,奥斯丁,,,切,,,请问加十多年你</p>
</div>
- field: begin
note: "开始日期"
range: "(-10M)-(-7M):7D"
type: timestamp
format: "YYYY-MM-DD"
- field: end
note: "结束日期"
range: "(-6M)-(+6M):7D"
type: timestamp
format: "YYYY-MM-DD"
- field: order
note: "排序"
value: "$id * 5"
- field: deleted
note: "是否删除"
range: 0,1
+19
View File
@@ -1311,4 +1311,23 @@ class productZen extends product
$this->session->set('releaseList', $this->app->getURI(true), 'product');
$this->session->set('productPlanList', $this->app->getURI(true), 'product');
}
/**
* 回复产品不存在提示消息。
* Response product not found message.
*
* @access protected
* @return void
*/
protected function responseNotFound4View(): void
{
if(defined('RUN_MODE') && RUN_MODE == 'api')
{
$this->send(array('status' => 'fail', 'code' => 404, 'message' => '404 Not found'));
return;
}
print(js::error($this->lang->notFound) . js::locate($this->createLink('product', 'index')));
return;
}
}