Spring Boot集成Activity7實現簡單的審批流

由于客戶對于系統里的一些新增數據,例如照片墻、照片等,想實現上級逐級審批通過才可見的效果,于是引入了Acitivity7工作流技術來實現,本文是對實現過程的介紹講解,由于我是中途交接前同事的這塊需求,所以具體實現方式和代碼編寫我暫時先按前同事的思路簡單介紹,不代表我個人看法。

參考文章:

springboot+Activiti7整合實踐 (一)_vue2集成activit7-CSDN博客

org.springframework.security.core.userdetails.UsernameNotFoundException,三步解決Activiti7和Security沖突問題_cause: org.springframework.security.core.userdetai-CSDN博客

Activiti7筆記(二)Activiti7一共涉及到25張表,哪些操作會涉及哪些表,每張表的作用是什么_activiti7數據表詳細解讀-CSDN博客

Activiti7筆記(一)Activiti7是什么,入門流程操作的代碼實現-騰訊云開發者社區-騰訊云 (tencent.com)

文章目錄

  • 一、引入依賴
  • 二、修改配置文件
  • 三、解決Activity7和Security框架沖突
  • 四、啟動項目,生成activity的數據庫表
    • 審批流過程
  • 五、畫流程圖
  • 六、部署流程
  • 七、發起流程
    • 1.示例
    • 2.相關代碼
  • 八、審批過程
    • 1.審核通過
      • 1.示例
      • 2.相關代碼
    • 2.審批不通過
      • 1.相關代碼
  • 九、業務表相關字段
    • 1.任務表
    • 2.業務數據表
  • 十、業務表數據權限變化

一、引入依賴

        <!--activity7工作流--><dependency><groupId>org.activiti</groupId><artifactId>activiti-spring-boot-starter</artifactId><version>${activity.starter.version}</version><exclusions><exclusion><!-- 項目引入了mybatis-plus,則需要排除 --><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.activiti.dependencies</groupId><artifactId>activiti-dependencies</artifactId><version>${activity.starter.version}</version><type>pom</type></dependency><!-- 生成流程圖 --><dependency><groupId>org.activiti</groupId><artifactId>activiti-image-generator</artifactId><version>${activity.starter.version}</version></dependency>

二、修改配置文件

  spring:#activity工作流配置activiti:# 自動部署驗證設置:true-開啟(默認)、false-關閉check-process-definitions: false# 保存歷史數據history-level: full# 檢測歷史表是否存在db-history-used: true# 關閉自動部署deployment-mode: never-fail# 對數據庫中所有表進行更新操作,如果表不存在,則自動創建# create_drop:啟動時創建表,在關閉時刪除表(必須手動關閉引擎,才能刪除表)# drop-create:啟動時刪除原來的舊表,然后在創建新表(不需要手動關閉引擎)database-schema-update: true# 解決頻繁查詢SQL問題async-executor-activate: false

db-history-userdhistory-level,建議按圖中配置,方便查詢工作流歷史記錄

三、解決Activity7和Security框架沖突

場景:由于Activity升級到7版本后,引入了Security來用于權限校驗,但是本項目自身已經引入了Security框架,于是發生了沖突

【如果項目用的不是security框架,例如用的shiro,需要在啟動項排除,參考:springboot+Activiti7整合實踐 (一)_vue2集成activit7-CSDN博客】

  • 網上有很多的相關報錯和解決辦法,【參考:org.springframework.security.core.userdetails.UsernameNotFoundException,三步解決Activiti7和Security沖突問題_cause: org.springframework.security.core.userdetai-CSDN博客】

  • 但本項目主要報錯的地方是 根據userid獲取用戶任務列表這個邏輯,使用activity7原生api方法會報錯;

  • 解決辦法(前同事處理的):不去排除沖突,直接另外新寫一個方法去查activity7的表,實現上述的邏輯

本項目
在這里插入圖片描述
在這里插入圖片描述

網上其他做法(用的activity原生的api):

從Activiti工作流中檢索所有用戶任務_在activiti 7中獲取流程實例的所有任務_如何在Activiti工作流中的單獨實例中強制順序執行任務 - 騰訊云開發者社區 - 騰訊云 (tencent.com)
在這里插入圖片描述

四、啟動項目,生成activity的數據庫表

引入依賴、配置好后,第一次啟動會在數據庫生成相關的表
在這里插入圖片描述

具體介紹:Activiti7筆記(二)Activiti7一共涉及到25張表,哪些操作會涉及哪些表,每張表的作用是什么_activiti7數據表詳細解讀-CSDN博客

本項目主要用到的表:

  • 流程部署表 :[ACT_RE_PROCDEF]

    每對一個流程圖部署后,會記錄在該表里(部署過程下面會講到)

    在這里插入圖片描述

  • 歷史流程實例表:[ACT_HI_PROCINST]

    流程一次從頭到尾執行,對應一個流程實例,流程結束時會保留下來

  • 運行時流程執行對象表 :[ACT_RU_EXECUTION]

    流程實例與執行對象的關系:?一個流程實例在執行過程中,?如果流程包含分支或聚合,?那么執行對象的數量可以多個【至少有2條數據,其中第1條是對應歷史流程實例表】。?這是因為流程實例在運行過程中可能會產生多個并行的執行路徑,?每個路徑上的任務或活動都可以視為一個執行對象。?例如,?在一個具有多個分支的審批流程中,?不同的審批人可能會同時處理不同的分支任務,?這些分支任務就代表了多個執行對象

    Activiti工作流學習(二)流程實例、執行對象、任務 - 百度文庫 (baidu.com)

    這個表里面主要記錄的是當前已經執行到哪個節點了,把對應的節點對象記錄到這個里面

    流程結束后,這張表對應的數據會清除

    在這里插入圖片描述

在這里插入圖片描述

  • 運行時節點人員數據信息表 :[ACT_RU_IDENTITYLINK]

    運行時用戶關系信息,存儲任務節點與參與者的相關信息
    也就是只要當前任務有參與者,就會將參與者的信息保存到這個表,多個人參與,保存多個信息

    在這里插入圖片描述

  • **運行時任務節點表:[ACT_RU_TASK] **

    一個流程有多個節點,到某一個節點的時候,都會更新這個表,將當前節點的數據更新到這個表

    在這里插入圖片描述

審批流過程

  1. 部署流程,到ACT_RE_PROCDEF表看有沒有新增一條對應的數據
  2. 啟動流程實例,執行流程對象(可能多個),到ACT_HI_PROCINSTACT_RU_EXECUTION表看有沒有新增對應數據
  3. 完成整個流程,過程主要看ACT_RU_TASKACT_RU_IDENTITYLINK表的更新情況

五、畫流程圖

  • IDEA安裝插件:

    在這里插入圖片描述

  • 在resource下創建一個目錄process存放流程圖文件

    在這里插入圖片描述

  • 右鍵,新建流程圖文件

    在這里插入圖片描述

  • 具體畫圖過程省略,這里介紹畫完之后的流程圖的重點信息

    2個審批節點的流程圖為例(增加、減少節點都需要另外畫圖部署)

    1. 定義流程的id和名字(后續代碼可以取到)

      在這里插入圖片描述

    2. 開始節點

      在這里插入圖片描述

    3. 審批節點

      第1個審批節點:

      在這里插入圖片描述

      第2個審批節點,同上

      在這里插入圖片描述

    4. 結束節點

      在這里插入圖片描述

    5. 二級節點之后的網關

      這里二級審批完之后,會出現2種不同的可能走向(審批通過,繼續到一級審批節點;審批不通過,直接結束),所以需要加上一個網關

      【由于時間較趕等原因,本項目不做回退的實現,所以審核不通過都是直接走向結束節點(一級審批節點通過或不通過都是走向結束節點)】

      在這里插入圖片描述

    6. 節點之間或節點與網關之間的連接線

      在這里插入圖片描述

      在這里插入圖片描述

      在這里插入圖片描述

      重點注意:網關后的線

      定義一個條件變量,當SecondJudge=true時(代碼賦值),即審核通過,會走向 一級審核 節點,否則走向結束節點,如下圖所示

      【當二級組織發起審核,但是選擇最終審批組織也是二級的時候,審核通過會賦值SecondJudge=false,直接走結束節點】

      在這里插入圖片描述

      在這里插入圖片描述

六、部署流程

本項目采用的是通過接口,手動部署的方式【每新建或修改流程圖都要調用一次接口來部署(后續打算優化成自動部署或者定時任務調用接口部署)】

    @ApiOperation("手動部署照片審核")@GetMapping("/photo/process/{typeLevel}")public String auditingPhotoProcessByTypeLevel(@PathVariable("typeLevel") Integer typeLevel, @RequestParam("force") Boolean force) {return auditingPhotoService.auditingPhotoProcessByTypeLevel(typeLevel,force);}
    /*** 手動部署* @param typeLevel 組織等級* @param force 是否強制部署(當修改了流程圖的時候需要傳true)* @return 部署結果*/public String auditingPhotoProcessByTypeLevel(Integer typeLevel, Boolean force);
    @Overridepublic String auditingPhotoProcessByTypeLevel(Integer typeLevel, Boolean force) {switch (typeLevel) {case 6:auditingPhotoSixService.process(force);break;case 5:auditingPhotoFiveService.process(force);break;case 4:auditingPhotoFourService.process(force);break;case 3:auditingPhotoThreeService.process(force);break;case 2:auditingPhotoTwoService.process(force);break;case 1:auditingPhotoOneService.process(force);break;default:throw new ServicesException(ResultStatus.ARGS_VALID_ERROR);}return typeLevel + "級照片流程部署成功";}
    /*** 部署流程* @param force* @return*/public void process(Boolean force);
    public void process(Boolean force) {//判斷流程是否已經部署,當force=0時,if (force == null || force) {//部署流程DeploymentBuilder builder = repositoryService.createDeployment();builder.addClasspathResource(AuditingConstant.BpmnXmlPhotoPathConstant.AUDITING_PHOTO_SIX_XML).disableSchemaValidation();String id = builder.deploy().getId();repositoryService.setDeploymentKey(id, AuditingConstant.BpmnIdConstant.AUDITING_PHOTO_SIX_ID);} else {//檢測流程是否已經部署過List<ProcessDefinition> definitions = repositoryService.createProcessDefinitionQuery().processDefinitionKey(AuditingConstant.BpmnIdConstant.AUDITING_PHOTO_SIX_ID).list();if (!definitions.isEmpty()) {// 已經部署過流程定義throw new ServicesException(ResultStatus.AUDITING_PROCESS_DUPLICATE);} else {//部署流程DeploymentBuilder builder = repositoryService.createDeployment();builder.addClasspathResource(AuditingConstant.BpmnXmlPhotoPathConstant.AUDITING_PHOTO_SIX_XML).disableSchemaValidation();String id = builder.deploy().getId();repositoryService.setDeploymentKey(id, AuditingConstant.BpmnIdConstant.AUDITING_PHOTO_SIX_ID);}}}

這里AuditingConstant.BpmnXmlPhotoPathConstant.AUDITING_PHOTO_SIX_XML是流程圖的路徑,如下圖所示

在這里插入圖片描述

部署好后,查看數據表 ACT_RE_PROCDEF 即可判斷是否成功部署

在這里插入圖片描述

七、發起流程

前提: 業務表(需要審批的)數據創建 ——> 點擊提交審核

如下圖的照片墻審核,創建一條新的數據,此時一開始沒審核,狀態是“草稿”,點擊發布后,才會觸發審批流程

在這里插入圖片描述

1.示例

二級組織用戶 新增一個照片墻,點擊發布(選擇審核等級為 上級(一級)組織,即需要經過兩個審批節點才截止

【只要是二級發起的,都走的是2個審批節點的流程圖,只不過根據結束節點會有不同的走向邏輯,如果這里選擇本級(二級)結束,則不論二級通過不通過,都會直接走向結束節點】

在這里插入圖片描述

點擊發布后,會啟動一個流程實例,并執行流程對象,見表ACT_HI_PROCINSTACT_RU_EXECUTION

ACT_HI_PROCINST(歷史流程實例表)

在這里插入圖片描述

ACT_RU_EXECUTION(運行時流程執行對象表):

在這里插入圖片描述

如下圖,當二級用戶點擊發步后,會發起審核,先走到 “二級審核2” 節點,對應上圖的第1條數據

在這里插入圖片描述

2.相關代碼

    @ApiOperation("發起照片審核")@ApiImplicitParams({@ApiImplicitParam(type = "query", name = "taskName", value = "任務名稱", required = true),@ApiImplicitParam(type = "query", name = "priority", value = "優先級", required = true),@ApiImplicitParam(type = "query", name = "desc", value = "描述", required = true),@ApiImplicitParam(type = "query", name = "photoWallId", value = "照片墻id", required = true),@ApiImplicitParam(type = "query", name = "auditingOrganizeId", value = "審核的組織id", required = true)})@ApiResponses({@ApiResponse(response = ResponseResult.class, message = "1", code = 200),@ApiResponse(response = ServiceException.class, message = "系統錯誤,請稍等!", code = 4000)})@PostMapping("/startPhotoAuditProcess")public ResponseResult<String> photoStartAuditing(@RequestBody Map<String, Object> map) {return ResponseResult.success(auditingPhotoService.photoStartAuditing(map));}
    /*** 啟動流程* @param map* @return success*/public String photoStartAuditing(Map<String,Object> map);

下面代碼解釋:根據當前用戶的組織級別,走不同的實現方法【代碼比較臃腫,后續再優化,后面的代碼同理】

    @Override@Transactional(rollbackFor = Exception.class)public String photoStartAuditing(Map<String, Object> map) {//根據照片墻id查找照片墻,給照片墻設置審核的組織IDPhotoWall photoWall = photoWallService.selectPhotoWallDetail(map.get("photoWallId") + "");photoWall.setOrganizeAuditingId(Long.parseLong(map.get("auditingOrganizeId") + ""));photoWall.setOpinion("");OrganizeTreeVO userOrganize = iOrganizeService.getUserOrganize();photoWallService.updatePhotoWallForAuditing(photoWall);//當前組織的級別Integer typeLevel = userOrganize.getTypeLevel();Integer typeLevelSix = 6;Integer typeLevelFive = 5;Integer typeLevelFour = 4;Integer typeLevelThree = 3;Integer typeLevelTwo = 2;Integer typeLevelOne = 1;if (typeLevel.equals(typeLevelSix)) {auditingPhotoSixService.startAuditProcess(map);} else if (typeLevel.equals(typeLevelFive)) {auditingPhotoFiveService.startAuditProcess(map);} else if (typeLevel.equals(typeLevelFour)) {auditingPhotoFourService.startAuditProcess(map);} else if (typeLevel.equals(typeLevelThree)) {auditingPhotoThreeService.startAuditProcess(map);} else if (typeLevel.equals(typeLevelTwo)) {auditingPhotoTwoService.startAuditProcess(map);} else if (typeLevel.equals(typeLevelOne)) {auditingPhotoOneService.startAuditProcess(map);}return "success";}
/*** 啟動流程* @param map*/
public void startAuditProcess(Map<String,Object> map);
public void startAuditProcess(Map<String, Object> map) {String id = map.get("photoWallId") + "";//修改照片墻的狀態PhotoWall photoWall = photoWallService.selectPhotoWallDetail(id);photoWall.setAuditState(1);photoWall.setOrganizeAuditingId(Long.parseLong(map.get("auditingOrganizeId") + ""));Boolean code = photoWallService.updatePhotoWallForAuditing(photoWall);if (code) {// 獲取當前登錄用戶的組織信息OrganizeTreeVO userOrganize = iOrganizeService.getUserOrganize();//當前組織的級別Integer typeLevel = userOrganize.getTypeLevel();//用來封裝存儲每一級組織的審核人,key是integer,表示不同級的組織,value是個list集合,每一級組織的審核人HashMap<String, List<SysUser>> auditors = new HashMap<>(10);//找出每一級審批流人員activitiService.getAuditors(userOrganize,typeLevel,auditors);// 設置流程變量Map<String, Object> variables = new HashMap<>(10);//獲得每級審核的審核人List<String> firstAuditor = getAuditorsId(auditors.get("1"));List<String> secondAuditor = getAuditorsId(auditors.get("2"));//審核人variables.put("firstAuditor", firstAuditor);variables.put("secondAuditor", secondAuditor);String formKey = AuditingConstant.BpmnIdConstant.AUDITING_PHOTO_TWO_ID + ":";String bussinessKey = formKey + id;variables.put("bussinessKey", bussinessKey);// 啟動流程實例runtimeService.startProcessInstanceByKey(AuditingConstant.BpmnIdConstant.AUDITING_PHOTO_TWO_ID, bussinessKey, variables);//給接下來的每一個審核人都創建一個,在剛發起的時候,是同一級的審核人List<SysUser> auditorsTypeLevel = auditors.get(typeLevel + "");//獲得和登錄用戶同一級的審核人的idList<String> auditorsTypeLevelIds = getAuditorsId(auditorsTypeLevel);for (int i = 0; i < auditorsTypeLevelIds.size(); i++) {SysTask sysTask = new SysTask();sysTask.setTaskName((String) map.get("taskName"));sysTask.setPriority(Integer.valueOf(map.get("priority") + ""));sysTask.setDescribe((String) map.get("desc"));sysTask.setPhotoWallId((String) map.get("photoWallId"));sysTask.setCreatorName(SecurityUtils.getLoginUser().getUsername());sysTask.setCreatorId(SecurityUtils.getLoginUser().getUserId());//審核人的名字  這里要怎么設置啊,審核人的名字和id,用的是候選用戶啊sysTask.setAuditingPeople(auditorsTypeLevel.get(i).getUserName());//審核人的idsysTask.setAuditingPeopleid(Long.parseLong(auditorsTypeLevelIds.get(i)));sysTask.setState(1);sysTask.setAuditingType(2);//任務表添加iSysTaskService.addTask(sysTask);//審核人的消息Message message = new Message();SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");String date = sdf.format(new Date());message.setContent("你在" + date + "有一個照片墻的審核");message.setCreateTime(new Date());message.setState(1);message.setUserId(auditorsTypeLevelIds.get(i));message.setType(1);messageService.addMessage(message);}//登錄用戶的消息Message message = new Message();SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");String date = sdf.format(new Date());message.setContent("你在" + date + "發起了一個照片墻審核");message.setCreateTime(new Date());message.setState(1);message.setUserId(SecurityUtils.getLoginUser().getUserId() + "");message.setType(2);messageService.addMessage(message);}
}

上方代碼重點

  • 設置變量,啟動流程實例

    這里把每一級審核人提前都設置好(后續不變)【這里根據業務需求應該也可以設計成后續動態添加】

    業務id bussinessKey 規則在此設定,調用最后一行的activity api時候需要傳遞,同時將其設置到流程變量 variables 中,后續方便讀取

    在這里插入圖片描述

  • 業務部分

    在這里插入圖片描述

    在這里插入圖片描述

    1. 修改 照片墻業務表 流程相關字段
    2. 新增 審核任務業務表
    3. 發送消息

八、審批過程

現在流程走到第一個審批節點,即 二級審核2

在這里插入圖片描述

此時,運行時節點人員數據信息表 ACT_RU_IDENTITYLINK,存入二級審核2 節點的 是審核人角色 的 用戶,尾號20用戶和尾號52用戶都是該節點的審批人【該項目里,只要其中一個審批通過,就算通過,反之,一個拒絕就算拒絕】,

且被分配到了任務(同個任務),見表ACT_RU_TASK,如下圖

在這里插入圖片描述

在這里插入圖片描述

對應業務表 任務表里,也加了2條數據

在這里插入圖片描述

他們之間通過 BUSINESS_KEY_ 來關聯

1.審核通過

審批通過,在activity7框架中,只需要設置SecondJudge變量,然后將task任務完成即可

在這里插入圖片描述

1.示例

二級組織人員 尾號52用戶 創建了一個照片墻,點擊發布后,生成本級人員(尾號52和尾號20)的任務,此時52用戶進入“我的待辦”里(業務表 任務表),找到對應任務,點擊審批通過,如下圖

在這里插入圖片描述

2.相關代碼

@ApiOperation("照片墻審批通過")
@ApiImplicitParams({@ApiImplicitParam(type = "query", name = "id", value = "任務id", required = true),@ApiImplicitParam(type = "query", name = "opinion", value = "建議", required = false)
})
@PostMapping("/auditing/photo/approve/{id}")
public ResponseResult<String> auditingPhotoApprove(@PathVariable Integer id, @RequestParam(required = false) String opinion) {return ResponseResult.success(auditingPhotoService.auditingPhotoApprove(id, opinion));
}
/*** 審核通過* @param id* @param opinion* @return success*/
public String auditingPhotoApprove(Integer id,String opinion);

下面代碼解釋:

通過照片墻的創建者的組織等級,走不同的審核通過實現類

@Override
@Transactional(rollbackFor = Exception.class)
public String auditingPhotoApprove(Integer id, String opinion) {//id是任務的id,可以獲得照片墻的id//再去新建表中查詢這個照片墻是哪級創建的,//這個級數直接判斷嗎,然后去調用哪個類的approve方法?SysTask sysTask = iSysTaskService.selectTaskById(id);//通過任務的informatinid來獲得照片墻idString photoId = sysTask.getPhotoWallId();//根據照片墻的id去查詢審核創建者的級別LambdaQueryWrapper<PhotoWall> lambdaQueryWrapper = new LambdaQueryWrapper<PhotoWall>();lambdaQueryWrapper.eq(PhotoWall::getPhotoWallId, photoId);PhotoWall photoWall = photoWallService.getOne(lambdaQueryWrapper);//獲得當前等級Long organizeId = photoWall.getOrganizeId();Integer userOrganizeLevel = iOrganizeService.searchOrganizeById(organizeId).getOrganizeLevel();Integer userOrganizeLevelOne = 1;Integer userOrganizeLevelTwo = 2;Integer userOrganizeLevelThree = 3;Integer userOrganizeLevelFour = 4;Integer userOrganizeLevelFive = 5;Integer userOrganizeLevelSix = 6;//獲得審核組織的級別Long auditingOrganizeId = photoWall.getOrganizeAuditingId();Integer auditingOrganizeLevel = iOrganizeService.searchOrganizeById(auditingOrganizeId).getOrganizeLevel();if (userOrganizeLevel.equals(userOrganizeLevelOne)) {auditingPhotoOneService.approve(id, opinion);} else if (userOrganizeLevel.equals(userOrganizeLevelTwo)) {auditingPhotoTwoService.approve(id, opinion, auditingOrganizeLevel);} else if (userOrganizeLevel.equals(userOrganizeLevelThree)) {auditingPhotoThreeService.approve(id, opinion, auditingOrganizeLevel);} else if (userOrganizeLevel.equals(userOrganizeLevelFour)) {auditingPhotoFourService.approve(id, opinion, auditingOrganizeLevel);} else if (userOrganizeLevel.equals(userOrganizeLevelFive)) {auditingPhotoFiveService.approve(id, opinion, auditingOrganizeLevel);} else if (userOrganizeLevel.equals(userOrganizeLevelSix)) {auditingPhotoSixService.approve(id, opinion, auditingOrganizeLevel);}return "success";
}
/*** 審核通過* @param id* @param opinion* @param auditingOrganizeLevel*/
public void approve(Integer id, String opinion,Integer auditingOrganizeLevel);
@Override
public void approve(Integer id, String opinion, Integer auditingOrganizeLevel) {//獲取任務的候選人是登錄用戶的任務的方法List<Task> taskList = activitiService.getTaskIdByCandidate(SecurityUtils.getLoginUser().getUserId().toString());//當前登錄用戶的組織級別Integer typeLevel = iOrganizeService.getUserOrganize().getTypeLevel();//搜索出工作臺選的任務idSysTask sysTask = iSysTaskService.selectTaskById(id);//通過任務的photoWallId來獲得照片墻idPhotoWall photoWall = photoWallService.selectPhotoWallDetail(sysTask.getPhotoWallId() + "");String photoWallId = photoWall.getPhotoWallId();auditingInformTwoApproveService(taskList, photoWallId, typeLevel, photoWall, auditingOrganizeLevel, opinion);// 獲取當前登錄用戶的組織信息OrganizeTreeVO userOrganize = iOrganizeService.getUserOrganize();//用來封裝存儲每一級組織的審核人,key是integer,表示不同級的組織,value是個list集合,每一級組織的審核人HashMap<String, List<SysUser>> auditors = new HashMap<>(10);//找出每一級審批流人員activitiService.getAuditors(userOrganize,typeLevel,auditors);List<String> auditorsList = new ArrayList<>();if(typeLevel.equals(AuditingConstant.Number.TWO)){auditorsList = getAuditorsId(auditors.get("2"));}if (typeLevel == 1) {auditorsList = getAuditorsId(auditors.get("1"));}sysTask.setState(2);sysTask.setOpinion(opinion);sysTask.setCompletionTime(new Date());iSysTaskService.updateTask(sysTask);Long userId1 = SecurityUtils.getLoginUser().getUserId();String photoWallId1 = sysTask.getPhotoWallId();//刪除其他用戶中關聯這個照片墻id的任務//循環審核人列表for (String s : auditorsList) {//當審核人不等于登錄用戶時,即其他用戶if (!Objects.equals(s, userId1 + "")) {//搜索出他的全部任務List<SysTask> sysTaskList = iSysTaskService.selectTaskByAuditorId(Long.parseLong(s));//循環任務for (SysTask task : sysTaskList) {//找出任務中關聯的照片墻id等于當前登錄用戶審核的任務的關聯的照片墻idif (photoWallId1.equals(task.getPhotoWallId())) {//刪除iSysTaskService.deleteTask(task.getTaskId().intValue());}}}}if (typeLevel == 1 || typeLevel.equals(auditingOrganizeLevel)) {photoWall.setAuditState(2);photoWallService.updatePhotoWallForAuditing(photoWall);Message message = new Message();Date date = new Date();message.setContent("你的照片墻在" + typeLevel + "級審核通過");message.setCreateTime(date);message.setState(1);String userId = sysTask.getCreatorId() + "";message.setUserId(userId);message.setType(2);message.setTaskId(sysTask.getTaskId().intValue());messageService.addMessage(message);return;}if (typeLevel.equals(AuditingConstant.Number.TWO)) {auditorsList = getAuditorsId(auditors.get("1"));}for (String s : auditorsList) {SysTask sysTask1 = new SysTask();sysTask1.setTaskName(sysTask.getTaskName());sysTask1.setPriority(sysTask.getPriority());sysTask1.setDescribe(sysTask.getDescribe());sysTask1.setPhotoWallId(sysTask.getPhotoWallId());sysTask1.setCreatorName(SecurityUtils.getLoginUser().getUsername());sysTask1.setCreatorId(SecurityUtils.getLoginUser().getUserId());sysTask1.setAuditingPeopleid(Long.parseLong(s));SysUser sysUser = iSysUserService.selectUserById(Long.parseLong(s));sysTask1.setAuditingPeople(sysUser.getUserName());sysTask1.setState(1);sysTask1.setAuditingType(2);iSysTaskService.addTask(sysTask1);sendMessage(s, sysTask);}sendMessage(typeLevel, sysTask);
}
private void auditingInformTwoApproveService(List<Task> taskList, String photoWallId, Integer typeLevel, PhotoWall photoWall, Integer auditingOrganizeLevel, String opinion) {if (!ObjectUtils.isEmpty(taskList)) {for (Task item : taskList) {ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(item.getProcessInstanceId()).singleResult();//根據流程實例得到bussinesskey,格式是formkey:idString a = processInstance.getBusinessKey();if (StringUtils.isNotBlank(a)) {String[] b = a.split(":");String photoId = b[1];//判斷bussinesskey,bussinesskey是任務表中的記錄的id,在發起流程的時候會插入任務表,任務表的id也會變成流程變量if (photoId.equals(photoWallId)) {if (typeLevel == 2) {if (typeLevel.equals(auditingOrganizeLevel)) {taskService.setVariable(item.getId(), "SecondJudge", false);} else {taskService.setVariable(item.getId(), "SecondJudge", true);}} else if (typeLevel == 1) {//如果當前審核人是一級組織的,那審核就結束了photoWall.setAuditState(2);photoWall.setOpinion(opinion);photoWallService.updatePhotoWallForAuditing(photoWall);}taskService.addComment(item.getId(), item.getProcessInstanceId(), "審核通過");taskService.complete(item.getId());break;}}}}
}

上面代碼重點

  • 找到activity的任務表中,當前該用戶的所有任務

    在這里插入圖片描述

    @Override
    public List<Task> getTaskIdByCandidate(String userId) {List<ActivitiTaskId> activitiTaskId = activitiMapper.getTaskIdByCandidate(userId);List<String> activitiTaskIds = new ArrayList<>();for (com.znak.platform.entity.ActivitiTaskId taskId : activitiTaskId) {activitiTaskIds.add(taskId.getTaskId());}List<Task> taskList = new ArrayList<>();for (String taskId : activitiTaskIds) {Task task = taskService.createTaskQuery().taskId(taskId).singleResult();taskList.add(task);}return taskList;
    }
    
    <select id="getTaskIdByCandidate" resultType="com.znak.platform.entity.ActivitiTaskId">select distinct TASK_ID_ from ACT_RU_IDENTITYLINKwhere USER_ID_ = #{userId}  and TASK_ID_ != ""
    </select>
    
  • 根據bussinesskey找到對應的任務,修改變量SecondJudge,并完成任務

    在這里插入圖片描述

    這里注意,如果當前是走到一級審批節點了,則走下面的else if邏輯,直接將業務表修改審核通過(不用設置變量,因為沒有網關了),如下圖

    在這里插入圖片描述

    在這里插入圖片描述

  • 業務相關

    • 修改業務表的審批流相關字段,同時發送消息,同上

    • 然后刪除同級別組織人員的 業務表 任務表,新建下一級的人員的業務表 任務表,見代碼

2.審批不通過

過程和審批通過大致一樣

在這里插入圖片描述

1.相關代碼

@ApiOperation("照片審核不通過")
@ApiResponses({@ApiResponse(response = ResponseResult.class, message = "1", code = 200),@ApiResponse(response = ServiceException.class, message = "系統錯誤,請稍等!", code = 4000)})
@ApiImplicitParams({//發起資訊申請相關字段@ApiImplicitParam(type = "query", name = "id", value = "任務id", required = true),@ApiImplicitParam(type = "query", name = "opinion", value = "建議", required = false)
})
@PostMapping("/auditing/photo/reject/{id}")
public ResponseResult<String> auditingPhotoReject(@PathVariable Integer id, @RequestParam(required = false) String opinion) {return ResponseResult.success(auditingPhotoService.auditingPhotoReject(id, opinion));
}
/*** 審核不通過* @param id* @param opinion* @return success*/
public String auditingPhotoReject(Integer id,String opinion);
@Override
@Transactional(rollbackFor = Exception.class)
public String auditingPhotoReject(Integer id, String opinion) {//id是任務的id,可以獲得資訊的id//再去新建表中查詢這個資訊是哪級創建的,//這個級數直接判斷嗎,然后去調用哪個類的approve方法?SysTask sysTask = iSysTaskService.selectTaskById(id);//通過任務的informatinid來獲得資訊idString photoWallId = sysTask.getPhotoWallId();//根據資訊的id去查詢審核創建者的級別LambdaQueryWrapper<PhotoWall> lambdaQueryWrapper = new LambdaQueryWrapper<PhotoWall>();lambdaQueryWrapper.eq(PhotoWall::getPhotoWallId, photoWallId);PhotoWall photoWall = photoWallService.getOne(lambdaQueryWrapper);//獲得當前等級Long organizeId = photoWall.getOrganizeId();Integer userOrganizeLevel = iOrganizeService.searchOrganizeById(organizeId).getOrganizeLevel();Integer userOrganizeLevelOne = 1;Integer userOrganizeLevelTwo = 2;Integer userOrganizeLevelThree = 3;Integer userOrganizeLevelFour = 4;Integer userOrganizeLevelFive = 5;Integer userOrganizeLevelSix = 6;if (userOrganizeLevel.equals(userOrganizeLevelOne)) {auditingPhotoOneService.rej(id, opinion);} else if (userOrganizeLevel.equals(userOrganizeLevelTwo)) {auditingPhotoTwoService.rej(id, opinion);} else if (userOrganizeLevel.equals(userOrganizeLevelThree)) {auditingPhotoThreeService.rej(id, opinion);} else if (userOrganizeLevel.equals(userOrganizeLevelFour)) {auditingPhotoFourService.rej(id, opinion);} else if (userOrganizeLevel.equals(userOrganizeLevelFive)) {auditingPhotoFiveService.rej(id, opinion);} else if (userOrganizeLevel.equals(userOrganizeLevelSix)) {auditingPhotoSixService.rej(id, opinion);}return "success";
}
/*** 審核不通過* @param id* @param opinion*/
public void rej(Integer id, String opinion);
public void rej(Integer id, String opinion) {//獲取任務的候選人是登錄用戶的任務的方法List<Task> taskList = activitiService.getTaskIdByCandidate(SecurityUtils.getLoginUser().getUserId().toString());//當前登錄用戶的組織級別Integer typeLevel = iOrganizeService.getUserOrganize().getTypeLevel();//搜索出工作臺選的任務idSysTask sysTask = iSysTaskService.selectTaskById(id);//通過任務的informatinid來獲得資訊idPhotoWall photoWall = photoWallService.selectPhotoWallDetail(sysTask.getPhotoWallId());String photoWallId = photoWall.getPhotoWallId();auditingInformTwoRejService(taskList, photoWallId, typeLevel, photoWall,opinion);// 獲取當前登錄用戶的組織信息OrganizeTreeVO userOrganize = iOrganizeService.getUserOrganize();//用來封裝存儲每一級組織的審核人,key是integer,表示不同級的組織,value是個list集合,每一級組織的審核人HashMap<String, List<SysUser>> auditors = new HashMap<>(4);//找出每一級審批流人員activitiService.getAuditors(userOrganize,typeLevel,auditors);//防止四級發起的審核,然后任務到達三級的時候調用此方法,會報錯4級審核人找不到List<String> auditorsList = new ArrayList<>();Integer typeLevelTwo = 2;if (typeLevel.equals(typeLevelTwo)) {auditorsList = getAuditorsId(auditors.get("2"));}if (typeLevel == 1) {auditorsList = getAuditorsId(auditors.get("1"));}//這一個等級的組織的審核任務處理,處理上一階段的任務//獲得當前級別的審核人sysTask.setState(3);sysTask.setOpinion(opinion);sysTask.setCompletionTime(new Date());iSysTaskService.updateTask(sysTask);Long userId1 = SecurityUtils.getLoginUser().getUserId();String photoWallId1 = photoWall.getPhotoWallId();//刪除其他用戶中關聯這個咨詢id的任務//循環審核人列表for (String s : auditorsList) {//當審核人不等于登錄用戶時,即其他用戶if (!Objects.equals(s, userId1 + "")){//搜索出他的全部任務List<SysTask> sysTaskList = iSysTaskService.selectTaskByAuditorId(Long.parseLong(s));//循環任務for(SysTask task : sysTaskList){//找出任務中關聯的照片墻id等于當前登錄用戶審核的任務的關聯的照片墻idif(photoWallId1.equals(task.getPhotoWallId())){//刪除iSysTaskService.deleteTask(task.getTaskId().intValue());}}}}Message message = new Message();Date date = new Date();message.setContent("你的照片墻在" + typeLevel + "級審核沒有通過");message.setCreateTime(date);message.setState(1);//這個審核的是誰的流程String userId = sysTask.getCreatorId() + "";message.setUserId(userId);message.setType(2);message.setTaskId(sysTask.getTaskId().intValue());messageService.addMessage(message);}
private void auditingInformTwoRejService(List<Task> taskList, String photoWallId, Integer typeLevel, PhotoWall photoWall,String opinion) {if (!ObjectUtils.isEmpty(taskList)) {for (Task item : taskList) {ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(item.getProcessInstanceId()).singleResult();//根據流程實例得到bussinesskey,格式是formkey:idString a = processInstance.getBusinessKey();if (StringUtils.isNotBlank(a)) {String[] b = a.split(":");String photoId = b[1];//判斷bussinesskey,bussinesskey是任務表中的記錄的id,在發起流程的時候會插入任務表,任務表的id也會變成流程變量if (photoId.equals(photoWallId)) {//根據當前登錄用戶的組織級別判斷是第幾級判斷,設置相關的流程變量if (typeLevel == 2) {photoWall.setAuditState(4);photoWall.setOpinion(opinion);photoWallService.updatePhotoWallForAuditing(photoWall);taskService.setVariable(item.getId(), "SecondJudge", false);} else if (typeLevel == 1) {//如果當前審核人是一級組織的,那審核就結束了photoWall.setAuditState(4);photoWall.setOpinion(opinion);photoWallService.updatePhotoWallForAuditing(photoWall);}taskService.addComment(item.getId(), item.getProcessInstanceId(), "審核不通過");taskService.complete(item.getId());break;}}}}
}

上面代碼的重點:

  • 找到activity的任務表中,當前該用戶的所有任務

    同上

  • 根據bussinesskey找到對應的任務,修改變量SecondJudge,并完成任務

    同上

  • 業務相關

    • 修改業務表的審批流相關字段,同時發送消息,同上

    • 然后刪除同級別組織人員的 業務表 任務表,見代碼

九、業務表相關字段

1.任務表

在這里插入圖片描述

2.業務數據表

以 照片墻 為例

在這里插入圖片描述

十、業務表數據權限變化

  • 審核前,即草稿狀態的數據,只能看自己創建的
  • 審核通過,本級 到 最終審核組織 之間的用戶都可見

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/45779.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/45779.shtml
英文地址,請注明出處:http://en.pswp.cn/web/45779.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

uniapp開發釘釘小程序流程

下載開發工具 1、小程序開發工具 登錄釘釘開發平臺&#xff0c;根據自己的需求下載合適的版本&#xff0c;我這里下載的是Windows &#xff08;64位&#xff09;版本 小程序開發工具 - 釘釘開放平臺 2、HBuilder X HBuilderX-高效極客技巧 新建項目及相關配置 新建項目 …

Llama - Prompting

本文翻譯整理自&#xff1a;Prompting https://llama.meta.com/docs/how-to-guides/prompting/ 文章目錄 制作有效的提示明確說明風格化格式化限制 提示使用 Zero- and Few-Shot 學習Zero-Shot PromptingFew-Shot Prompting 基于角色的提示思維鏈技術Self-Consistency檢索-增強…

單臂路由組網實驗,單臂路由的定義、適用情況、作用

一、定義 單臂路由是指通過在路由器的一個接口上配置許多子接口,從而實現原來相互隔離的不同VLAN之間的互通。 子接口:把路由器上的實際的物理接口劃分為多個邏輯上的接口,這些被劃分的邏輯接口就是子接口。 二、適用情況 用在沒有三層交換機,卻要實現不同VLAN之間的互…

Github07-16 Python開源項目日報 Top10

根據Github Trendings的統計,今日(2024-07-16統計)共有10個項目上榜。根據開發語言中項目的數量,匯總情況如下: 開發語言項目數量Python項目10C++項目1AutoGPT: 人工智能革命的先鋒 創建周期:486 天開發語言:Python協議類型:MIT LicenseStar數量:164105 個Fork數量:435…

axios 下載大文件時,展示下載進度的組件封裝——js技能提升

之前面試的時候&#xff0c;有遇到一個問題&#xff1a;就是下載大文件的時候&#xff0c;如何得知下載進度&#xff0c;當時的回復是沒有處理過。。。 現在想到了。axios中本身就有一個下載進度的方法&#xff0c;可以直接拿來使用。 下面記錄一下處理步驟&#xff1a; 參考…

深度學習 | CNN 基本原理

目錄 1?什么是 CNN2?輸入層3?卷積層3.1?卷積操作3.2?Padding 零填充3.3?處理彩色圖像 4?池化層4.1?池化操作4.2?池化的平移不變性 5?全連接層6?輸出層 前言 這篇博客不夠詳細&#xff0c;因為沒有介紹卷積操作的具體計算&#xff1b;但是它介紹了 CNN 各層次的功能…

golang AST語法樹解析

1. 源碼示例 package mainimport ("context" )// Foo 結構體 type Foo struct {i int }// Bar 接口 type Bar interface {Do(ctx context.Context) error }// main方法 func main() {a : 1 }2. Golang中的AST golang官方提供的幾個包&#xff0c;可以幫助我們進行A…

[雜談] 關于 Mac 電腦使用 Logitech 鼠標導致 Vscode 側鍵無法進行代碼前進、回退的問題

我個人使用的是一臺 14 寸的 Mac_Apple_M1&#xff0c;外接鍵盤顯示器羅技的 MX Master 3 for Mac 的鼠標。 之前一直使用的 GoLand 開發&#xff0c;查看代碼時進行代碼跳轉就很方便&#xff0c;滾輪鍵 進入函數方法&#xff0c;鼠標側鍵進行前進、后退。看代碼完全可以右手單…

【大模型入門】LLM-AI大模型介紹

大語言模型 (LLM) 背景 &#x1f379;大語言模型 (Large Language Model) 是一種人工智能模型, 它們通常包含數千億甚至更多的參數&#xff0c;并在大規模數據集上進行訓練。大語言模型可以處理多種自然語言任務&#xff0c;如文本分類、問答、翻譯、對話等等。 自然語言模型…

qt explicit 啥意思

explicit 在 Qt 和 C 中是一個關鍵字&#xff0c;主要用于修飾類的構造函數。其含義和用法可以歸納為以下幾點&#xff1a; 意義&#xff1a; explicit 英文直譯為“明確的”、“顯式的”。在 C 中&#xff0c;當一個構造函數只接受一個參數時&#xff0c;它可能會被編譯器用于…

【Nail it】ROS1 ROS2 通信(ros2/ros1_bridge)

情況說明&#xff1a;目標是實現ros2容器和ros1主機的通信&#xff0c;可以起一個ros1容器作為橋梁&#xff08;若是在一個主機同時包含ros1&ros2&#xff0c;配置更加方便&#xff09;. 1.起一個 noetic 的容器 docker run -it --network host --name my_bridge ros:noe…

Java中的成員內部類

Java中的成員內部類&#xff08;也稱為非靜態內部類&#xff09;是定義在另一個類&#xff08;外部類&#xff09;內部的類。 這種內部類與它的外部類之間有著緊密的聯系&#xff0c;主要體現在幾個方面&#xff1a;它可以訪問外部類的所有成員&#xff08;包括私有成員&#…

C++小白Python選手2小時入門C++

學習鏈接&#xff1a;C入門/2小時從C到C快速入門&#xff08;2018&#xff0c;C教程&#xff09; C在C語言的基礎上增加了面向對象和通用算法語言特征。 C頭文件不必是.h結尾&#xff0c;C頭文件舉例&#xff1a;cmath、cstdio注釋&#xff1a;單行//、多行/**/為了防止名字沖…

MQ基礎1

對應B站視頻&#xff1a; MQ入門-01.MQ課程介紹_嗶哩嗶哩_bilibili 微服務一旦拆分&#xff0c;必然涉及到服務之間的相互調用&#xff0c;目前我們服務之間調用采用的都是基于OpenFeign的調用。這種調用中&#xff0c;調用者發起請求后需要等待服務提供者執行業務返回結果后…

【JavaScript腳本宇宙】強大的自然語言處理:六款JavaScript庫詳解

從語義到實體&#xff1a;深入了解JavaScript自然語言處理庫 前言 隨著人工智能和自然語言處理技術的飛速發展&#xff0c;JavaScript在這一領域也有了越來越多的應用。本文將介紹幾個優秀的JavaScript庫&#xff0c;它們專注于處理英語文本&#xff0c;并提供了豐富的功能和…

求立方體面積體積以及判斷(c++)

代碼&#xff1a; #include<iostream> using namespace std;class Cube { public:void setL(int l){m_L l;}int getL(){return m_L;}void setW(int w){m_W w;}int getW(){return m_W;}void setH(int h){m_H h;}int getH(){return m_H;}int calculateS(){return 2 * (…

netdata 監控軟件安裝與學習

netdata官網 netdata操作文檔 前言&#xff1a; netdata是一款監控軟件&#xff0c;可以監控多臺主機也可以監控單臺主機&#xff0c;監控單臺主機時&#xff0c;開箱即用&#xff0c;web ui很棒。 環境&#xff1a; [root192 ~]# cat /etc/system-release CentOS Linux rel…

徹底清理Conda環境:使用conda remove命令的終極指南

徹底清理Conda環境&#xff1a;使用conda remove命令的終極指南 在Conda環境中&#xff0c;隨著時間的推移&#xff0c;可能會積累大量不再需要的包和它們的依賴項。這不僅會占用寶貴的磁盤空間&#xff0c;還可能影響環境的性能。conda remove命令是Conda提供的一個強大工具&…

GD32F407VET6新建固件庫工程并下載運行

零、所需文件及環境&#xff1a; 1、固件庫的壓縮包 GD32F4xx_Firmware_Library_V3.2.0.7z 官網 2、GD32F407的keil支持包 官網 兆易創新GigaDevice-資料下載兆易創新GD32 MCU 2、 keilkilll.bat 用來刪除編譯過程文件 可以不要 &#xff08;原子、野火資料里都有&…

LeetCode熱題100(JavaScript)

哈希 兩數之和 暴力解法 /*** param {number[]} nums* param {number} target* return {number[]}*/ var twoSum function(nums, target) {for(let i 0;i<nums.length;i){let x1 nums[i]for(let j 0 ; j<nums.length;j){if(i!j){let x2 nums[j]if(x1x2target){ret…