• 首页
  • 栏目
  • ERP
  • 【DEVOPS】zentao二次扩展开发实践 - ERP任务报工同步

【DEVOPS】zentao二次扩展开发实践 - ERP任务报工同步

  • 2021-04-30
  • Admin

借助zentao提供的二次开发,实现在zentao中直接基于story / task / bug 粒度的完成情况,向公司内部ERP进行工作量同步(也就是俗称的"ERP每日报工")。

1. 前言

"上ERP找死,不上ERP等死”,这短短的一句话在揭示启用ERP的必要性同时,也暴露了推进ERP的艰难。

笔者所在的公司属于传统软件企业,秉承资源高效利用和业务特殊性考虑,决策层最终采取了自主进行ERP开发的路线。

同时为了对项目进行精细化管理,精确把控项目进度,公司又专门引入了zentao作为项目全生命周期管理平台。于是出现了:

  1. 人员需要在zentao中完成story / task / bug 状态扭转,接着在ERP中就所完成的工作进行填报。
  2. 人员需要在erp中进行项目计划的制定,接着在zentao中进行计划的再次创建。
  3. …凡此种种重复性的工作。

本文尝试解决第一个重复性问题,实现在zentao中,直接基于story / task / bug 粒度的完成情况,向公司内部ERP进行工作量同步(也就是俗称的"ERP每日报工")。

2. 优点

做任何一件事都必须得有理由,本文要介绍的工作也不例外。通过“zentao + ERP报工同步”,我们可以:

  1. 降低人员心智负担,通过减少重复性工作,减少低级错误出现频率,提高事情的完成度。
  2. 推进ERP报工日志格式的统一,为之后的各类报表打下坚实基础(周报 / 半月报 / 月报等)。
  3. 减少需求管理规范化的推进阻力。公司引入zentao是为了进行项目需求的有序性管理,只是就个人而言,原本一句话就能解决的问题现在必须在zentao上大费周章,因此"zentao规范化使用"的推进过程中势必困难重重,而通过这样的同步打通,将能够有效降低推进阻力。

3. 效果

先让我们看看最终的效果(笔者的主职语言为Java后端,php是初次接触,界面美观啥的还请读者多多包涵)。

  1. 在 story / task / bug 操作页面, 提供“ERP报工”按钮。
    在这里插入图片描述
  2. 点击以上按钮,弹出"ERP报工"填报页面。
    在这里插入图片描述
  3. 点击"上传"按钮,等待"ERP报工"结果反馈。
    在这里插入图片描述

4. 实现

关键文件说明。

4.1 文件module\common\model.php
    /**
     * `module\common\model.php`文件
     */
    public static function printDailyTaskIcon($storyOrBugOrTask, $session)
    {
        global $lang;

        echo html::commonButton(' ' . "对当前需求进行ERP报工", 'style="font-weight:bold" ', 'btn btn-link pull-right btn-dailyWork');
		
		// 模拟登录, 并从ERP中获取当前员工所参与的项目情况
		$cookie = common::loginErp("username","password");
		$taskList = common::http("http://xxxx/xxxx_queryDailyTask.action",array("rwzt" => 0, "sdate" => "", "edate" =>"", "xmmc" => "", "rwmc"=>""),array(CURLOPT_COOKIE => $cookie));
		
		
		$taskListForCurrentUser = json_decode($taskList)->data->tasks;
		// 将从erp查询到的员工参与项目情况存入session, 留作后用
		$session->set('taskListForCurrentUser',     $taskListForCurrentUser);
		foreach ($taskListForCurrentUser as $key => $value) {
			$arr2[sprintf("%s-%s", $value->xmId,$value->rwId)] = sprintf("%s > %s",$value->xmmc, $value->rwmc);
		}
		
		// 上面对key进行了特殊处理, 所以这里就不排序
		//rsort($arr2);

		// html 变成可筛选的(摘抄自 `module\story\view\change.html.php`)
		$projectHtmlSelect = html::select('taskSelected', $arr2, "", 'class="form-control chosen"');
		// 报工内容
		$dailyWorkContent = sprintf("从事禅道开发-%s %s", $storyOrBugOrTask->id, $storyOrBugOrTask->title);
		
		list($module,$method,$objectType,$objectId) = explode('-', $commentFormLink);
		$commentFormLink = sprintf("/zentao/action-dailyTask-%s-%s.html",$objectType,explode('.',$objectId)[0]);
/* */
        echo <<<EOD


EOD;

    }

    /**
     * 推送每日报工 (`module\common\model.php`文件)
     */	
	public static function writeDailyTask($data = array())
	{
		$cookie = common::loginErp("username","password");
		return common::http("http://xxxx/writeDiaryTask.action", $data, array(CURLOPT_COOKIE => $cookie));
	}
	
	  //登录接口,获取cookie
   static function loginErp($erpUsername, $erpPassowrd)
    {
        $re = common::http(sprintf("http://xxxxx/login.action?name=%s&pswd=%s",$erpUsername,$erpPassowrd), null, array(CURLOPT_HEADER=>true));

        // 解析HTTP数据流
        list($header, $body) = explode("\r\n\r\n", $re);
        // 解析COOKIE
        preg_match_all("/set\-cookie:([^\r\n]*)/i", $header, $matches);
       return $matches[1][1];

    }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
4.2 文件module\action\control.php

针对url请求 /zentao/action-dailyTask.html

	// module\action\control.php 文件
    public function dailyTask($objectType, $objectID)
    {
		// 工时
		$zctrgs = $this->post->zctrgs;
		if(!filter_var($zctrgs, FILTER_VALIDATE_INT)){
			die(js::alert("[工时]必须为数值"));
		}
		if(((int)$zctrgs) > 8){
			die(js::alert("[工时]必须小于等于8"));
		}
		
		$taskSelected = $this->post->taskSelected;
		list($xmId,$rwId) = explode('-', $taskSelected);
		$item = null;
		foreach ($this->session->taskListForCurrentUser as $key => $value) {
			if($value->xmId == $xmId and $value->rwId == $rwId){
				$item = $value;
				break;
			}
		}
		
		if(!$item){
			die(js::alert("没有找到可报工的项目!"));
			return;
		}
		
		
        $result = common::writeDailyTask(array("xmbh" => $xmId, "rwId" => $rwId, "rwmc" =>$item->rwmc, "txrq" => date("Y-m-d")));
        // 同时向zentao的当前story写入一条comment
		if($result->status == 0){
			$this->action->create($objectType, $objectID, 'Commented', sprintf("ERP-%s-%s-%s-%s-%s-%s", $xmId, $rwId, $item->xmmc, $item->rwmc,$this->post->zctrgs,$this->post->wcbfb));
		}
        if(defined('RUN_MODE') && RUN_MODE == 'api')
        {
            die(array('status' => 'success', 'data' => $actionID));
        }
        else
        {
        	// 为了最大化真实地反馈报工情况,我们直接将ERP的返回值进行弹框展示, 不进行二次封装.
			echo js::alert(json_encode($result));
            die(js::reload('parent'));
        }
    }	
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
4.3 更新权限 zt_grouppriv

这一步操作的必要性是实际的试点过程中发现的,我们需要为当前操作用户授予填写报工的权限。

鉴权初期管理还没有细化,这里我们直接给出为所有用户组赋予填写报工的权限。

-- 创建存储过程之前需判断该存储过程是否已存在,若存在则删除
DROP PROCEDURE IF EXISTS shxc40;
-- 创建存储过程
CREATE PROCEDURE shxc40()
BEGIN
     
-- 定义变量
    DECLARE s int DEFAULT 0;
		DECLARE gro int;
    DECLARE p varchar(255);

 
    -- 定义游标,并将sql结果集赋值到游标中
    DECLARE groups CURSOR FOR select distinct `group` from zt_grouppriv;
 
    -- 声明当游标遍历完后将标志变量置成某个值
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET s=1;
 
    -- 打开游标
    open groups;
 
        -- 将游标中的值赋值给变量,注意:变量名不要和返回的列名同名,变量顺序要和sql结果列的顺序一致
        fetch groups into gro;
 
        -- 当s不等于1,也就是未遍历完时,会一直循环
        while s<>1 do
             
            -- 执行业务逻辑
            INSERT INTO zt_grouppriv (`group`, module, method) VALUES(gro, 'action', 'dailyTask');
 
            -- 当s等于1时表明遍历以完成,退出循环
            fetch groups into gro;
        end while;
    -- 关闭游标
    close groups;
END;


-- 执行存储过程
call shxc40()


-- // Other 
-- select distinct `group` from zt_grouppriv
-- select  * from zt_grouppriv where method='dailyTask'
-- delete from zt_grouppriv where  method='dailyTask'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
4.4 备注说明
  1. module\story\control.php 文件。
    a. 其中的 view($storyID, $version = 0, $from = 'product', $param = 0)方法负责请求story-view-xxx.html的响应 。
  2. module\story\view\view.html.php文件。
    a. 请求story-view-xxx.html的响应页面,这属于zentao中默认的二次开发约定。
    b. 其中的
    属于以模块化引入的"历史修改记录"模块。
  3. module\common\view\action.html.php文件。
    a. 上一步以模块化引入的"历史修改记录"模块文件。
    b. 其中的 正是zentao中默认的"添加备注"按钮的实现,也是本次我们的借鉴对象。
    c. 最终我们的实现为:app->user->account, array('admin','xxxx')) and isset($actionFormLink)) echo common::printDailyTaskIcon($actionFormLink, $story, $this->session);?>
  4. module\common\model.php文件。
    a. 上一步中common::printCommentIcon($actionFormLink);的实现位置。
  5. www\js\zui\min.js文件。
    a. 让我们扩展的按钮点击生效。
    b. 实现代码:;n.on("click",".btn-comment",function(t){r.modal("toggle"),t.preventDefault()}).on("click",".btn-dailyWork",function(t){var r = n.find(".modal-dailyWork");
  6. www\theme\zui\css\min.css文件。
    a. 按钮前面icon的来源,例如这里的icon-cards-view
  7. module\action\control.php文件。
    a. 添加 public function dailyTask() 方法。
  8. \lib\base\front\front.class.php文件。
    a. html::commonButton的来源。
  9. 权限配置。
    a. 按钮执行是需要权限的。相关表为zt_grouppriv
    b. 执行SQL:INSERT INTO zt_grouppriv (group, module, method) VALUES('2', 'action', 'dailyTask') 。其中 2 为 group Id,需要自行查找用户所在组对应的id。

5. 优化方向

目前想到的,还未来得及做的:

  1. 已有报工展示。针对当天,人员尚未报工的时长进行展示,方便人员知晓当前报工情况,避免人员需要再次登录ERP查看。
  2. 更新。当出现填报错误时候,能够用新的报工替换掉之前的报工。
  3. 界面输入参数校验。例如需求完成用时只能为数值。

6. Links

  1. 【DEVOPS】zentao核心概念速览

原文:https://blog.csdn.net/lqzkcx3/article/details/116308089

联系站长

QQ:769220720