基于activiti框架的工作流提交了袁、審批以及撤銷

基于activiti框架的工作流提交朗恳、審批以及撤銷

背景:在一個企業(yè)中,大部分的事情都不不是一個人做決定的载绿,往往需要經(jīng)過幾個層級的審批粥诫。這樣就有一個流程,工作流就是方便這一個方面的需求崭庸。

1.認(rèn)識工作流常用表

  • 歷史節(jié)點表( act_hi_actinst )
    • 簡要描述

      歷史活動信息怀浆,這里記錄流程流轉(zhuǎn)過的所有節(jié)點。act_hi_taskinst表中只記錄了歷史節(jié)點表中節(jié)點類型為usertask的信息怕享。
      
    • 表結(jié)構(gòu)說明

      歷史節(jié)點表
  • 運行時流程執(zhí)行實例表( act_ru_execution )
    • 簡要描述

      流程執(zhí)行記錄表执赡。
      
    • 表結(jié)構(gòu)說明

      運行時流程執(zhí)行實例表
  • 運行時任務(wù)節(jié)點表( act_ru_task )
    • 簡要描述

      運行時任務(wù)數(shù)據(jù)表。
      
    • 表結(jié)構(gòu)說明

      運行時任務(wù)節(jié)點表

2.工作流的提交(以HGXP項目中的供應(yīng)商提交為例)

  • 前臺請求

    $.ajax({
        type: 'GET',
        url: "${base.contextPath}/cux/gxp/vendor/basic/pubapprove",//提交審批先走各自的入口函筋,方便進行狀態(tài)等的校驗沙合。然后再調(diào)用通入審批。
        success: function (json) {
            kendo.ui.showInfoDialog({
                message: json.message
            }).done(function () {
                if (json.success) {
                    window.location.reload();//如果提交成功跌帐,刷新狀態(tài)
                }
            });
        },
        async: false,
        dataType: "json",
        data: {"docId": headerId, "approveCode": "GXP_VENDOR"} //提交需要審批的供應(yīng)商頭ID首懈,以及在通用審批設(shè)置平臺中維護的CODE(對應(yīng)應(yīng)該走哪條工作流)
    });
    

    如下圖,HGXP中就是在這個塊碼中維護approveCode

    mark

    并在通用審批設(shè)置平臺中維護:
    mark

    可以看出谨敛,在這個通用審批設(shè)置平臺中究履,我們維護好了各工作流對應(yīng)的頭表、主鍵以及記錄審批狀態(tài)的字段status_Code

  • 后臺走通用審批處理service (PubApproveServiceImpl.java)

    public ResponseData  pubApprove(IRequest request, String approveCode, Long docId) {
        PubApproveSetup pubApproveSetup = new PubApproveSetup();
        ResponseData responseData = null;
        String errMsg = "";
        int result = 0;
    
        //根據(jù)docId查找對應(yīng)資質(zhì)頭信息并校驗是否可以提交審批
        if("GXP_VENDOR".equals(approveCode)){
            GxpVendorBasic gxpVendorBasic =new GxpVendorBasic();
            gxpVendorBasic.setBasicId(docId);
            List<GxpVendorBasic> gxpVendorBasicList=gxpVendorBasicMapper.select(gxpVendorBasic);
            if(gxpVendorBasicList.size()==1){
                String status=gxpVendorBasicList.get(0).getStatusCode();
                responseData =returnResponseData(status);  //這一步就是校驗該資質(zhì)頭是否可以提交(不為已提交和已審批狀態(tài))
                if(responseData !=null){
                    return responseData;
                }
    
            }
        }
    
        /*************************************************************************
         * 1.0 根據(jù)approveCode獲取審批配置平臺配置屬性
         ************************************************************************ */
        pubApproveSetup.setApproveCode(approveCode);
        pubApproveSetup = pubApproveSetupMapper.selectOne(pubApproveSetup);
        if (pubApproveSetup == null) {
            errMsg += "審批代碼" + approveCode + "未在通用審批平臺定義!";
            responseData = new ResponseData(false);
            responseData.setMessage(errMsg);
            return responseData;
        }
    
        /*************************************************************************
         * 1.0 根據(jù)approveCode獲取審批配置平臺配置屬性
         ************************************************************************ */
        if ("Y".equals(pubApproveSetup.getApproveByself()) && "Y".equals(pubApproveSetup.getApproveAlterFlag())) {
            //如果為自審批并且審批直接修改狀態(tài)佣盒,則直接修改狀態(tài)
            try {
                result = updateDocStatus(pubApproveSetup.getTableName(), pubApproveSetup.getTableKey(), pubApproveSetup.getStatusField(), pubApproveSetup.getApproveStatus(), docId);
            } catch (Exception e) {
                e.printStackTrace();
                errMsg += "自審批失斂嫱唷:" + ExceptionUtils.getRootCauseMessage(e);
            }
        } else if ("N".equals(pubApproveSetup.getApproveByself())) {
            //提交審批工作流進行審批
            //組合參數(shù)
            List<RestVariable> list=new ArrayList<>();
            RestVariable var1=new RestVariable();
            var1.setName("approveCode");
            var1.setValue(approveCode);
            RestVariable var2=new RestVariable();
            var2.setName("processDefinitionKey");
            var2.setValue(pubApproveSetup.getActivitiKey());
            RestVariable var3=new RestVariable();
            var3.setName("docId");
            var3.setValue(docId);
            RestVariable var4=new RestVariable();
            var4.setName("title");
            var4.setValue(pubApproveSetup.getFormTitle());
            RestVariable var5=new RestVariable();
            var5.setName("url");
            var5.setValue(pubApproveSetup.getFormUrl());
            list.add(var1);
            list.add(var2);
            list.add(var3);
            list.add(var4);
            list.add(var5);
            ProcessInstanceResponse response=startActivitiService.startActiviti(request,list);
            System.out.println(response==null);
            if(response!=null)
            {
                if ("Y".equals(pubApproveSetup.getSubmitAlterFlag())) {
                    //如果不為自審批,并且提交時自動修改狀態(tài),則修改為提交動作的狀態(tài)
                    errMsg+=processStatus(request,approveCode,docId,"SUBMIT");
                }
                //工作流提交成功 向通用審批事務(wù)表表插入數(shù)據(jù)
                PubApproveTransaction pubApproveTransaction=new PubApproveTransaction();
                pubApproveTransaction.setActivitiKey(pubApproveSetup.getActivitiKey());
                pubApproveTransaction.setActivitiId(Long.parseLong(response.getId()));
                pubApproveTransaction.setApproveCode(approveCode);
                pubApproveTransaction.setDocId(docId);
                pubApproveTransaction.setObjectVersionNumber(1L);
                pubApproveTransactionService.insertSelective(request,pubApproveTransaction);
            }
            else
            {
                errMsg+="提交工作流出錯!";
            }
        }
    
        if (errMsg==null||"".equals(errMsg)) {
            responseData = new ResponseData(true);
            responseData.setMessage("提交成功!");
        } else {
            responseData = new ResponseData(false);
            responseData.setMessage(errMsg);
        }
    
        return responseData;
    }
    

    上面的代碼可以分割為:

    • 排除為已提交和已審批的狀態(tài)(若為這兩個狀態(tài)就提示不能提交)
    • 獲取通用審批配置平臺上維護的數(shù)據(jù)
    • 判斷是否為自審批
      • 若為自審批則直接修改資質(zhì)頭的狀態(tài)不發(fā)起工作流
      • 若不為自審批狀態(tài)盯仪,則發(fā)起工作流紊搪,List<RestVariable>中我們就將通用審批配置平臺中的值全部帶在工作流中

    其中startActivitiService.startActiviti(request,list)這個方法是發(fā)起工作流的方法

    mark

    實現(xiàn)類實現(xiàn)紅色框中的接口,重寫其中的startActiviti方法:

    @Autowired
    private IActivitiService activitiService;
    
    @Override
    public ProcessInstanceResponse startActiviti(IRequest irequest, List<RestVariable> list) {
        ProcessInstanceCreateRequest request=new ProcessInstanceCreateRequest();
        request.setBusinessKey("104");
       // request.setProcessDefinitionId(null);
        String key="";
        for (int i=0;i<list.size();i++)
        {
            System.out.println(list.get(i).getName());
            System.out.println(list.get(i).getName()=="processDefinitionKey");
            System.out.println("processDefinitionKey".equals(list.get(i).getName()));
            if(list.get(i).getName()=="processDefinitionKey"||"processDefinitionKey".equals(list.get(i).getName()))
            {
                key=list.get(i).getValue().toString();
            }
        }
        System.out.println(key);
        request.setProcessDefinitionKey(key);
        request.setReturnVariables(false);
        request.setTenantId(null);
        request.setVariables(list);
        request.setTransientVariables(null);
        return activitiService.startProcess(irequest, request);
    }
    

    上面的代碼就可以理解為:

    • 模擬一個request請求
      • 其中 processDefinitionKey 就是通用配置平臺中維護的工作流程字段全景,這個字段的作用就決定著我們是走的那一條工作流耀石,這個字段就是與我們流程設(shè)計中的流程編碼對應(yīng)
        mark
      • variables 就是我們流程中的變量,這其中就是上一步驟中獲取的通用審批配置平臺中數(shù)據(jù)爸黄,存在 運行時流程變量數(shù)據(jù)表(act_ru_variable)中

    至此工作流的發(fā)起工作就完了滞伟,工作流程發(fā)起了之后我們剩下需要做的工作就是改變我們對應(yīng)資質(zhì)頭的狀態(tài),并記錄工作流程到 通用審批事務(wù)表(cux_pub_approve_transaction)中

    if ("Y".equals(pubApproveSetup.getSubmitAlterFlag())) {
        //如果不為自審批炕贵,并且提交時自動修改狀態(tài)梆奈,則修改為提交動作的狀態(tài)
        errMsg+=processStatus(request,approveCode,docId,"SUBMIT");
    }
    //工作流提交成功 向通用審批事務(wù)表表插入數(shù)據(jù)
    PubApproveTransaction pubApproveTransaction=new PubApproveTransaction();
    pubApproveTransaction.setActivitiKey(pubApproveSetup.getActivitiKey());
    pubApproveTransaction.setActivitiId(Long.parseLong(response.getId()));
    pubApproveTransaction.setApproveCode(approveCode);
    pubApproveTransaction.setDocId(docId);
    pubApproveTransaction.setObjectVersionNumber(1L);
    pubApproveTransactionService.insertSelective(request,pubApproveTransaction);
    
    • 第一步,就是改變資質(zhì)頭的狀態(tài)為 SUBMITTED(已提交)

    • 第二部称开,就是將工作流程記錄到通用審批事務(wù)表中

      • activitiKey 工作流程的標(biāo)識即:流程設(shè)計頁面中的流程編碼
      • activitiId 發(fā)起工作流后的亩钟,產(chǎn)生的流程Id
      • approveCode 審批代碼(塊碼中維護的值)
      • docId 資質(zhì)頭Id
      • objectversionnumber 版本號

3.工作流的審批(以HGXP項目中供應(yīng)商資質(zhì)審批為例)

  • 前臺請求

    var currentTaskId = '${RequestParameters.taskId!taskId}';   //獲取任務(wù)ID
    
    $('#btn-approved').click(function () {
        kendo.ui.showConfirmDialog({
            title: $l('hap.confirm'),
            message: $l('hap.confirm') + '<span class="action_ok">' + $('#text-approved').text() + '</span>?'
        }).done(function (e) {
            if (e.button == 'OK') {
                taskAction({approveResult: 'APPROVED'}); //傳的參數(shù)為:`approveResult`屬性值為`APPROVED`的對象
            }
        })
    });
    
    function taskAction(p) {
        p = p || {};         //傳進的對象(會帶有`approveResult`屬性)
        p.action = p.action || 'complete'     //設(shè)置 `action` 屬性為 `complete` (完成任務(wù))
        if('pending' == currentTaskInfo.delegationState && 'delegate'!=p.action){ //如果有轉(zhuǎn)交任務(wù)的操作
            p.action = 'resolve'  //設(shè)置 `action` 屬性為 `resolve`(轉(zhuǎn)交操作)
        }
        var variables = [];
        if (p.action != 'delegate') {
            var formVars = {};
            formVars.approveResult = p.approveResult;
            formVars.comment = $("#ta-comment").val();
            $.each(formVars, function (k, v) {
                variables.push({name: k, value: v});  //設(shè)置 `variables` 的 `approveResult` 和 `comment` 屬性的值
            })
        }
    
        var param = {
            assignee: p.targetUser || null,
            action: p.action,  //任務(wù)的動作`complete`
            comment: $("#ta-comment").val(), // 審批意見
            variables: variables,
            jumpTarget: p.jumpTarget || null,
            carbonCopyUsers :null,
        };
        /**
        * p{approveResult: "APPROVED", action: "complete"}
        * variables = [{name: "approveResult", value: "APPROVED"}, {name: "comment", value: ""}]
        * param = {assignee: null, action: "complete", comment: "", variables: Array(2), jumpTarget: null, carbonCopyUsers:""}
        */
        $.ajax({
            url: contextPath_ + '/wfl/runtime/<#if isAdmin!false>admin/</#if>tasks/' + currentTaskId,
            type: 'POST',
            contentType: 'application/json',
            data: kendo.stringify(param),
            success: function (args) {
                if (args.success === false) {
                    kendo.ui.showErrorDialog({
                        title: $l('hap.error'),
                        message: args.message
                    });
                } else {
                    if (p.callback) {
                        p.callback(args);
                    } else {
                        kendo.ui.showInfoDialog({
                            title: $l('hap.tip.info'),
                            message: '操作完成!'
                        }).done(function () {
                            closeCurrentWin()
                        });
                    }
                }
            }, error: function (args) {
                kendo.ui.showInfoDialog({
                    title: $l('hap.error'),
                    message: kendo.stringify(args)
                });
            }
        })
    }
    
  • 后臺走框架封裝好的ActivitiController.class

    @RequestMapping(
        value = {"/runtime/tasks/{taskId}"},
        method = {RequestMethod.POST}
    )
    @ResponseStatus(HttpStatus.OK)
    public void executeTaskAction(@PathVariable String taskId, @RequestBody TaskActionRequestExt actionRequest, HttpServletRequest request, HttpServletResponse response) throws TaskActionException {
        IRequest iRequest = this.createRequestContext(request);
        this.activitiService.executeTaskAction(iRequest, taskId, actionRequest, false);
    }
    
    • 從目前來應(yīng)用方面來講鳖轰,主要就是在前臺設(shè)置好完成當(dāng)前任務(wù)的幾個參數(shù)清酥,主要的參數(shù)
      • action:"complete"
      • variables中的 approveResult

4.工作流的撤銷

  • 前臺請求
    function cancelApproveData() {
        $.ajax({
            url:"${base.contextPath}/hmdmwfl/canclewf?id="+headerId+"&approveCode="+"GXP_VENDOR",
            dataType:"json",
            contentType:"application/json",
            type:"POST",
            success:function (data) {
                if(data.success==true){
                    Hap.submitForm({
                        url: '${base.contextPath}/cux/gxp/vendor/basic/ignore/submit',
                        formModel: viewModel.model,
                        success: function (json){
                            if(json.success){
                                kendo.ui.showInfoDialog({
                                    message: "成功"
                                }).done(function () {
                                    window.location.reload();
                                });
                            }else{
                                kendo.ui.showErrorDialog({
                                    message: "失敗"
                                }).done(function () {
                                    window.location.reload();
                                });
                            }
                        }
                    });
                }else {
                    kendo.ui.showErrorDialog({
                        message: data.message
                    });
                }
            }
        });
    }
    
    工作流的撤銷,前臺請求和提交工作流類似蕴侣,就是從前臺傳一個basicIdapproveCode到后臺
  • 后臺走HmdmActivitiController.java
    @RequestMapping(value="/canclewf")
    public ResponseData cancle(HttpServletRequest request, @RequestParam Long id,@RequestParam String approveCode){
        IRequest irequest = createRequestContext(request);
        ResponseData rDate = new ResponseData();
        List<PubApproveTransaction> transactionList=pubApproveTransactionService.selectAllByHeaderId(id,approveCode);
        if(transactionList.size()==0){
            rDate.setSuccess(false);
            rDate.setMessage("此流程為批量分配數(shù)據(jù)審批流程焰轻,正在審批中,不能撤銷昆雀!");
            return rDate;
        }else {
            PubApproveTransaction approveTransaction=transactionList.get(transactionList.size()-1);
            String procId=approveTransaction.getActivitiId().toString();
    
            // 當(dāng)前用戶的ID
            Long userId = irequest.getUserId();
            User currentUser = userMapper.selectByPrimaryKey(userId);
            String currentUserName = currentUser.getUserName();
            String employeeCode = irequest.getEmployeeCode();
    
            // 根據(jù)流程Id,查找流程實例
            HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery();
            historicProcessInstanceQuery.processInstanceId(procId);
            // 流程實例
            HistoricProcessInstance historicProcessInstance = historicProcessInstanceQuery.singleResult();
            // 流程的啟動人
            String startUser = historicProcessInstance.getStartUserId();
            String processInstanceId = historicProcessInstance.getId();
            String priorActType = pubApproveTransactionService.selectPriorNodeActType(processInstanceId);
            // 當(dāng)前用戶和流程啟動是同一人
            if (employeeCode.equals(startUser)) {
                if(priorActType.equals("startEvent")){
                    // 根據(jù)流程實例辱志,查找蓋流程實例的所有任務(wù)
                    HistoricTaskInstanceQuery historicTaskInstanceQuery = historyService.createHistoricTaskInstanceQuery();
                    historicTaskInstanceQuery.processInstanceId(processInstanceId);
                    historicTaskInstanceQuery.orderByTaskCreateTime().asc();
                    List<HistoricTaskInstance> historicTaskInstanceList = historicTaskInstanceQuery.list();
                    // 排除只有一個節(jié)點的流程,而且提交人就是審批人的情況
                    TaskQuery taskQuery = taskService.createTaskQuery();
                    List<Task> taskList = taskQuery.processInstanceId(processInstanceId).list();
                    HistoricTaskInstance instance = historicTaskInstanceList.get(0);
                    String taskId = instance.getId();
                    //增加撤銷節(jié)點忆肾,并辦理
                    Map<String, Object> variables = new HashMap<String, Object>();
                    variables.put("approveResult", "RETRACT");//REJECTED
                    taskService.addComment(taskId, null, "action", "RETRACT");
                    taskService.addComment(taskId, processInstanceId, "comment", "");  //添加評論
                    taskService.claim(taskId, currentUserName);
                    taskService.complete(taskId, variables); //辦理任務(wù)荸频,相當(dāng)于將任務(wù)辦理掉以此來達(dá)到撤銷工作流的效果
                    rDate.setSuccess(true);
                    rDate.setMessage(id.toString());
                    return rDate;
                }else {
                    rDate.setSuccess(false);
                    rDate.setMessage("流程已有審批,不能撤回客冈!");
                    return rDate;
                }
            }
        }
    
        rDate.setSuccess(false);
        rDate.setMessage("非發(fā)起人,不能撤回稳强!");
        return rDate;
    }
    
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末场仲,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子退疫,更是在濱河造成了極大的恐慌渠缕,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件褒繁,死亡現(xiàn)場離奇詭異亦鳞,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門燕差,熙熙樓的掌柜王于貴愁眉苦臉地迎上來遭笋,“玉大人,你說我怎么就攤上這事徒探⊥吆簦” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵测暗,是天一觀的道長央串。 經(jīng)常有香客問我,道長碗啄,這世上最難降的妖魔是什么质和? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮稚字,結(jié)果婚禮上饲宿,老公的妹妹穿的比我還像新娘。我一直安慰自己尉共,他們只是感情好褒傅,可當(dāng)我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著袄友,像睡著了一般殿托。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上剧蚣,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天支竹,我揣著相機與錄音,去河邊找鬼鸠按。 笑死礼搁,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的目尖。 我是一名探鬼主播馒吴,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼瑟曲!你這毒婦竟也來了饮戳?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤洞拨,失蹤者是張志新(化名)和其女友劉穎扯罐,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體烦衣,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡歹河,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年掩浙,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片秸歧。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡厨姚,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出寥茫,到底是詐尸還是另有隱情遣蚀,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布纱耻,位于F島的核電站芭梯,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏弄喘。R本人自食惡果不足惜玖喘,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蘑志。 院中可真熱鬧累奈,春花似錦、人聲如沸急但。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽波桩。三九已至戒努,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間镐躲,已是汗流浹背储玫。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留萤皂,地道東北人撒穷。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像裆熙,于是被迫代替她去往敵國和親端礼。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內(nèi)容