简单的springboot整合activiti5.22.0

简单的springboot整合activiti5.22.0

1. 需求

我们公司原本的流程服务是本地workflow模块以及一个远程的webService对应的activiti服务,其中activiti版本为5.22.0,之前想将activiiti5.22.0进行升级,选择了camunda,也对项目进行了部分改造,新增camunda-server服务,但是后来觉得数据迁移比较费劲,并且行里数据准确性要求比较高,原有的老数据不能丢失,历史记录必须完整保存,所以因为历史数据原因就没有使用camnuda了,而是进行了workflow-server服务的改造,将activiti5.22.0对应的webService服务迁移至本地服务,由于本webService服务提供了风险与信贷两个系统的流程服务,所以在workflow-server中还提供了风险调用原webService接口,并且也是改造成了fiegn调用形式

2. 具体实现

以下为改造的具体实现,具体主要包含两个部分,具体如下:

  • 第一部分为本地服务提供的接口,包含流程的发布,部署,流程图获取,以及流程的发起提交结束等基本操作。
  • 第二部分则是为风险系统提供的流程操作接口,主要包含发起提交结束等

2.1 本地服务改造

2.1.1 pom修改

原有的workflow-server对应的pom中新增springboot整合activiti对应的jar包,具体添加部分如下标注activiti所示

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>credit-workflow</artifactId>
        <groupId>cn.git</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>workflow-server</artifactId>
    <dependencies>
        <dependency>
            <groupId>cn.git</groupId>
            <artifactId>business-common</artifactId>
            <version>1.0-SNAPSHOT</version>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>com.alibaba.cloud</groupId>
                    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>cn.git</groupId>
            <artifactId>credit-swagger-starter</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>cn.git</groupId>
            <artifactId>credit-log-starter</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>cn.git</groupId>
            <artifactId>cache-manage-client</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>cn.git</groupId>
            <artifactId>workflow-client</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>cn.git</groupId>
            <artifactId>afterloan-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>cn.git</groupId>
            <artifactId>credit-discovery-starter</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.git</groupId>
            <artifactId>loan-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>cn.git</groupId>
            <artifactId>account-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>cn.git</groupId>
            <artifactId>management-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>cn.git</groupId>
            <artifactId>query-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>cn.git</groupId>
            <artifactId>credit-monitor-starter</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.apache.skywalking</groupId>
            <artifactId>apm-toolkit-log4j-2.x</artifactId>
            <version>8.6.0</version>
        </dependency>
        <dependency>
            <groupId>cn.git</groupId>
            <artifactId>rule-manage-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <!-- activiti 5.22.0启动器,排除mybatis依赖 -->
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-spring-boot-starter-basic</artifactId>
            <version>5.22.0</version>
            <exclusions>
                <exclusion>
                    <groupId>org.mybatis</groupId>
                    <artifactId>mybatis</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-diagram-rest</artifactId>
            <version>5.22.0</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- compiler -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.mapstruct</groupId>
                            <artifactId>mapstruct-processor</artifactId>
                            <version>${mapstruct.version}</version>
                        </path>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                            <version>${lombok.version}</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
            <!-- package -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
2.1.2 controller修改

主要修改了部分controller方法,添加原有webService服务提供的流程部署,流程图获取,xml获取,部署流程列表等方法,具体代码如下:

package cn.git.workflow.controller;

import cn.git.common.limit.ApiLimitSlidingWindow;
import cn.git.common.limit.SlidingWindowEnum;
import cn.git.common.page.PageBean;
import cn.git.common.result.Result;
import cn.git.common.result.ResultStatusEnum;
import cn.git.redis.RedisUtil;
import cn.git.workflow.constant.WorkFlowServerConstant;
import cn.git.workflow.dto.*;
import cn.git.workflow.dto.activiti.WorkFlowLoadTemplateDTO;
import cn.git.workflow.entity.PageTbConVote;
import cn.git.workflow.entity.WorkFlowUndoTask;
import cn.git.workflow.entity.activiti.WorkFlowLoadTemplate;
import cn.git.workflow.mapstruct.WorkFlowConvert;
import cn.git.workflow.page.WorkFlowPage;
import cn.git.workflow.service.WorkFlowService;
import cn.git.workflow.util.WorkFlowUtil;
import cn.git.workflow.vo.*;
import cn.git.workflow.vo.activiti.WorkFlowLoadTemplateInVO;
import cn.git.workflow.vo.activiti.WorkFlowLoadTemplateOutVO;
import cn.git.workflow.webservice.process.Exception_Exception;
import cn.git.workflow.webservice.query.BpmException_Exception;
import cn.hutool.core.util.StrUtil;
import io.swagger.annotations.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.xml.sax.SAXException;

import javax.validation.Valid;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.util.List;
import java.util.Map;

/**
 * 流程通用controller
 *
 * @program: bank-credit-sy
 * @author: lixuchun
 * @create: 2021-06-09
 */
@Api(value = "WorkFlowController流程通用controller", tags = "WorkFlowController流程通用controller")
@RestController
@RequestMapping("/workflow")
@Slf4j
public class WorkFlowController {

    @Autowired
    private WorkFlowUtil workFlowUtil;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private WorkFlowConvert workFlowConvert;

    @Autowired
    private WorkFlowService workFlowService;

    /**
     * 代办未分类列表信息count查询
     *
     * @param userCd 柜员号
     * @param orgCd  机构号
     * @return WorkFlowPage 分页对象
     */
    @ApiOperation(value = "代办未分类列表信息count查询", notes = "代办未分类列表信息count查询")
    @ApiResponses({@ApiResponse(code = 1, message = "OK", response = Result.class)})
    @GetMapping(value = "/listUndoWorkInfoInit")
    public Result<WorkFlowPage<List<Map<String, Object>>>> listUndoWorkInfoInit(
            @ApiParam(name = "userCd", value = "柜员号", required = true)
                    String userCd,
            @ApiParam(name = "orgCd", value = "机构号", required = true)
                    String orgCd) {
        try {
            log.info("代办未分类列表信息count查询参数userCd[{}],orgCd[{}]!", userCd, orgCd);
            return Result.ok(workFlowService.findWorkCountFlowList(userCd, orgCd));
        } catch (Exception e) {
            e.printStackTrace();
            log.error(workFlowUtil.getStackTraceInfo(e));
            return Result.error(ResultStatusEnum.WORKFLOW_EXCEPTION.getCode(), e.getMessage());
        }
    }

    /**
     * 代办未分类列表信息count查询
     *
     * @param userCd 柜员号
     * @param orgCd  机构号
     * @param systemFlag 系统标识
     * @return WorkFlowPage 分页对象
     */
    @ApiOperation(value = "系统代办未分类列表信息count查询", notes = "系统代办未分类列表信息count查询")
    @ApiResponses({@ApiResponse(code = 1, message = "OK", response = Result.class)})
    @GetMapping(value = "/listSysUndoWorkInfoInit")
    public Result<WorkFlowPage<List<Map<String, Object>>>> listSysUndoWorkInfoInit(
            @ApiParam(name = "userCd", value = "柜员号", required = true)
                    String userCd,
            @ApiParam(name = "orgCd", value = "机构号", required = true)
                    String orgCd,
            @ApiParam(name = "systemFlag", value = "系统标识,0或者空为信贷,1为风险,2为储备系统", required = true)
                    String systemFlag) {
        try {
            log.info("代办未分类列表信息count查询参数userCd[{}],orgCd[{}]!", userCd, orgCd);
            return Result.ok(workFlowService.findSysWorkCountFlowList(userCd, orgCd, systemFlag));
        } catch (Exception e) {
            e.printStackTrace();
            log.error(workFlowUtil.getStackTraceInfo(e));
            return Result.error(ResultStatusEnum.WORKFLOW_EXCEPTION.getCode(), e.getMessage());
        }
    }

    /**
     * 获取流程图
     * @param processId
     * @return
     * @throws Exception_Exception
     */
    @GetMapping(value = "/findProcessPic")
    @ApiResponses({@ApiResponse(code = 1, message = "OK")})
    @ResponseBody
    public String findProcessPic(@ApiParam(value = "流程ID", name = "processId", required = true)
                                             String processId) throws Exception_Exception, IOException {
        return workFlowService.findProcessPic(processId);
    }

    /**
     * 具体业务代办列表查询
     *
     * @param workFlowUndoTaskInVO 代办分类任务查询inVO
     * @return WorkFlowPage 分页对象
     */
    @ApiOperation(value = "具体业务代办列表", notes = "具体业务代办列表")
    @ApiResponses({@ApiResponse(code = 1, message = "OK", response = Result.class)})
    @GetMapping(value = "/listUndoWorkInfoDetail")
    public Result<WorkFlowPage<WorkFlowUndoTask>> listUndoWorkInfoDetail(
            @ApiParam(name = "workFlowUndoTaskInVO", value = "代办分类任务查询inVO", required = true)
                    WorkFlowUndoTaskInVO workFlowUndoTaskInVO) throws BpmException_Exception {
        try {
            return Result.ok(workFlowService.findUndoWorkTaskDetailList(
                    workFlowConvert.workFlowTaskInVoConvertTaskDto(workFlowUndoTaskInVO)));
        } catch (Exception e) {
            e.printStackTrace();
            log.error(workFlowUtil.getStackTraceInfo(e));
            return Result.error(ResultStatusEnum.WORKFLOW_EXCEPTION.getCode(), e.getMessage());
        }
    }

    /**
     * 点击代办列表bizNo跳转到详情页面
     *
     * @param taskId 任务id
     * @param userCd 用户cd
     * @param orgCd  机构号
     * @return Result
     */
    @ApiOperation(value = "点击代办列表bizNo跳转到详情页面", notes = "点击代办列表bizNo跳转到详情页面")
    @ApiResponses({@ApiResponse(code = 1, message = "OK", response = Result.class),
            @ApiResponse(code = -1, message = "ERROR", response = Result.class)})
    @GetMapping(value = "/findTaskDetailInfo/{taskId}/{userCd}/{orgCd}")
    public Result findTaskDetailInfo(
            @ApiParam(name = "taskId", value = "代办任务id", required = true)
            @PathVariable(value = "taskId") String taskId,
            @ApiParam(name = "userCd", value = "柜员cd", required = true)
            @PathVariable(value = "userCd") String userCd,
            @ApiParam(name = "orgCd", value = "机构cd", required = true)
            @PathVariable(value = "orgCd") String orgCd) {
        WorkFlowUndoTaskDetailDTO workFlowDTO = workFlowService.findTaskDetailInfo(taskId, userCd, orgCd);
        if(StrUtil.isNotBlank(workFlowDTO.getErrorMessage())){
            return Result.error(workFlowDTO.getErrorMessage());
        }
        return Result.ok(workFlowDTO);
    }

    /**
     * 跟踪列表初始化列表信息查询
     *
     * @param workFlowTraceListInVO inVo
     * @return Result<WorkFlowTraceListOutVO>
     */
    @ApiLimitSlidingWindow(slidingWindowEnum = SlidingWindowEnum.WORKFLOW_NEW_TRACE_HIS_WINDOW)
    @ApiOperation(value = "跟踪列表初始化列表信息查询", notes = "跟踪列表初始化列表信息查询")
    @ApiResponses({@ApiResponse(code = 1, message = "OK", response = Result.class)})
    @GetMapping(value = "/findTraceList")
    public Result<WorkFlowTraceListOutVO> findTractList(
            @ApiParam(name = "workFlowTraceListInVO", value = "跟踪列表inVo")
                    WorkFlowTraceListInVO workFlowTraceListInVO) throws BpmException_Exception {
        try {
            WorkFlowTraceListDTO workFlowTraceListDTO = workFlowService.findTraceList(
                    workFlowConvert.workFlowTractListInVoConvertTractDto(workFlowTraceListInVO));
            return Result.ok(workFlowConvert.workFlowTractDtoConvertListOutVo(workFlowTraceListDTO));
        } catch (Exception e) {
            e.printStackTrace();
            log.error(workFlowUtil.getStackTraceInfo(e));
            return Result.error(ResultStatusEnum.WORKFLOW_EXCEPTION.getCode(), e.getMessage());
        }
    }

    /**
     * 历史统跟踪列表初始化列表信息查询
     *
     * @param workFlowTraceListInVO inVo
     * @return Result<WorkFlowTraceListOutVO>
     */
    @ApiLimitSlidingWindow(slidingWindowEnum = SlidingWindowEnum.WORKFLOW_TRACE_HIS_WINDOW)
    @ApiOperation(value = "历史跟踪列表初始化列表信息查询", notes = "历史跟踪列表初始化列表信息查询")
    @ApiResponses({@ApiResponse(code = 1, message = "OK", response = Result.class)})
    @GetMapping(value = "/old/findTraceList")
    public Result<WorkFlowTraceListOutVO> findOldSysTractList(
            @ApiParam(name = "workFlowTraceListInVO", value = "跟踪列表inVo")
                    WorkFlowTraceListInVO workFlowTraceListInVO) {
        try {
            WorkFlowTraceListDTO workFlowTraceListDTO = workFlowService.findOldTraceList(
                    workFlowConvert.workFlowTractListInVoConvertTractDto(workFlowTraceListInVO));
            return Result.ok(workFlowConvert.workFlowTractDtoConvertListOutVo(workFlowTraceListDTO));
        } catch (Exception e) {
            e.printStackTrace();
            log.error(workFlowUtil.getStackTraceInfo(e));
            return Result.error(ResultStatusEnum.WORKFLOW_EXCEPTION.getCode(), e.getMessage());
        }
    }

    /**
     * 查询跟踪列表单个任务历史记录
     *
     * @param historyListInVO inVo
     * @return WorkFlowTraceHistoryListOutVO outVo
     */
    @ApiOperation(value = "查询跟踪列表单个任务历史记录", notes = "查询跟踪列表单个任务历史记录")
    @ApiResponses({@ApiResponse(code = 1, message = "OK", response = Result.class)})
    @GetMapping("/findTraceTaskHistoryList")
    public Result<WorkFlowTraceHistoryListOutVO> findTraceTaskHistoryList(
            @ApiParam(name = "historyListInVO", value = "跟踪列表历史查询inVo")
                    WorkFlowTraceHistoryListInVO historyListInVO) throws BpmException_Exception {
        try {
            WorkFlowTraceHistoryListDTO workFlowTraceHistoryListDTO = workFlowService.findTraceHistoryList(
                    workFlowConvert.workflowTraceHisInVoConvertHisDto(historyListInVO));
            return Result.ok(workFlowConvert.workflowTraceHisDtoConvertHistoryListOutVo(workFlowTraceHistoryListDTO));
        } catch (Exception e) {
            e.printStackTrace();
            log.error(workFlowUtil.getStackTraceInfo(e));
            return Result.error(ResultStatusEnum.WORKFLOW_EXCEPTION.getCode(), e.getMessage());
        }
    }

    /**
     * 通过客户号获取贷审会列表信息
     * 包含urlName路径信息,点击进行跳转
     * @param customerNum 客户号
     * @return 贷审会列表信息
     */
    @ApiOperation(value = "柜员登录后选定客户查看贷审会列表获取", notes = "贷审会列表获取")
    @ApiResponses({@ApiResponse(code = 1, message = "OK", response = Result.class)})
    @GetMapping(value = "/findVoteMeetingList/{customerNum}")
    public Result<PageBean<PageTbConVote>> findVoteMeetingList(@PathVariable("customerNum") String customerNum) {
        try {
            PageBean<PageTbConVote> votePageBean = workFlowService.pageVoteMeetingList(customerNum);
            return Result.ok(votePageBean);
        } catch (Exception e) {
            log.error(workFlowUtil.getStackTraceInfo(e));
            return Result.error(ResultStatusEnum.WORKFLOW_EXCEPTION.getCode(), e.getMessage());
        }
    }

    /**
     * 获取下一处理环节
     * @param workFlowEndProcessDTO dto
     * @return WorkFlowNextPositionDTO dto
     */
    @ApiOperation(value = "获取下一处理环节", notes = "获取下一处理环节")
    @ApiResponses({@ApiResponse(code = 1, message = "OK")})
    @GetMapping(value = "/coreFindNextPositionList")
    public Result<WorkFlowNextPositionDTO> findNextPositionList(
            @ApiParam(value = "workFlowEndProcessDTO", name = "获取下一处理环节岗位信息dto", required = true)
            WorkFlowNextPositionDTO workFlowEndProcessDTO) {
        try {
            WorkFlowNextPositionDTO workFlowNextPositionDTO = workFlowService.findNextPositionList(workFlowEndProcessDTO);
            return Result.ok(workFlowNextPositionDTO);
        } catch (Exception e) {
            e.printStackTrace();
            log.error(workFlowUtil.getStackTraceInfo(e));
            return Result.error(ResultStatusEnum.WORKFLOW_EXCEPTION.getCode(), e.getMessage());
        }
    }

    /**
     * 获取下一处理人列表信息
     * @param workFlowNextUserListDTO dto
     * @return WorkFlowNextUserListDTO dto
     */
    @ApiOperation(value = "获取下一处理人列表信息", notes = "获取下一处理人列表信息")
    @ApiResponses({@ApiResponse(code = 1, message = "OK")})
    @GetMapping(value = "/coreFindNextUserInfoList")
    public Result<WorkFlowNextUserListDTO> findNextUserInfoList(
            @ApiParam(value = "workFlowNextUserListDTO", name = "获取下一处理人列表信息dto", required = true)
            WorkFlowNextUserListDTO workFlowNextUserListDTO) {
        try {
            return Result.ok(workFlowService.findNextUserInfoList(workFlowNextUserListDTO));
        } catch (Exception e) {
            e.printStackTrace();
            log.error(workFlowUtil.getStackTraceInfo(e));
            return Result.error(ResultStatusEnum.WORKFLOW_EXCEPTION.getCode(), e.getMessage());
        }
    }

    /**
     * 设置webService访问路径 todo: 测试环境使用,生产删除此方法
     * @param serviceType 类型
     * @return Result
     */
    @ApiOperation(value = "设置webService访问路径", notes = "设置webService访问路径")
    @ApiResponses({@ApiResponse(code = 1, message = "OK")})
    @GetMapping(value = "/webServiceType/{serviceType}")
    public Result setWebServiceType(
            @ApiParam(name = "serviceType", value = "service类型,UAT,TEST", required = true)
            @PathVariable(value = "serviceType") String serviceType) {
        if (!WorkFlowServerConstant.WEB_SERVICE_URL_UAT.equals(serviceType)
                && !WorkFlowServerConstant.WEB_SERVICE_URL_TEST.equals(serviceType)) {
            return Result.error("正确填入 环境类型, UAT 或者 TEST");
        } else {
            redisUtil.set(WorkFlowServerConstant.WEB_SERVICE_URL_KEY, serviceType);
            return Result.ok();
        }
    }

    /**
     * 回退流程到上一岗位
     * @return Result
     */
    @ApiOperation(value = "回退流程到上一岗位", notes = "回退流程到上一岗位")
    @ApiResponses({@ApiResponse(code = 1, message = "OK")})
    @GetMapping(value = "/submitBackPrevious")
    public Result submitBackPrevious(
            @ApiParam(value = "workFlowBackPreviousInVO", name = "退回上一岗位基本信息", required = true)
            WorkFlowBackPreviousInVO workFlowBackPreviousInVO)
            throws cn.git.workflow.webservice.task.BpmException_Exception, BpmException_Exception {
        String errorMessage = workFlowService
                .submitBackPrevious(workFlowConvert.backInVoConvertDto(workFlowBackPreviousInVO));
        if (StrUtil.isNotBlank(errorMessage)) {
            return Result.error(errorMessage);
        }
        return Result.ok();
    }

    /**
     * 通过文件部署流程图, 文件格式为xml
     * @return
     */
    @ApiOperation(value = "直接通过文件部署新流程图", notes = "部署新流程图,返回流程部署成功的流程定义id[processDefinitionId]")
    @PostMapping(value = "/deploy/process")
    public Result<String> uploadProcessDefinition(@RequestParam MultipartFile file) throws IOException,
            ParserConfigurationException, SAXException {
        // 获取文件进行部署
        String processDefinitionId = workFlowService.deployProcess(file);
        return Result.ok(processDefinitionId);
    }

    /**
     * 获取流程已经部署信息
     *
     * @param  loadTemplateInVO
     * @return WorkFlowPage 分页对象
     */
    @ApiOperation(value = "获取流程已经部署信息", notes = "获取流程已经部署信息")
    @ApiResponses({@ApiResponse(code = 1, message = "OK", response = Result.class)})
    @PostMapping(value = "/load/template/page")
    public Result<WorkFlowLoadTemplateOutVO> getLoadTemplatePage(
            @Valid @RequestBody WorkFlowLoadTemplateInVO loadTemplateInVO) {
        // 参数赋值操作
        WorkFlowLoadTemplateDTO templateDTO = new WorkFlowLoadTemplateDTO();
        templateDTO.setTemplateKey(loadTemplateInVO.getTemplateKey());
        templateDTO.setVersion(loadTemplateInVO.getVersion());

        // 进行查询
        WorkFlowPage<WorkFlowLoadTemplate> loadTemplatePage = workFlowService
                .getLoadTemplatePage(templateDTO);
        WorkFlowLoadTemplateOutVO outVO = new WorkFlowLoadTemplateOutVO();
        outVO.setPageBean(loadTemplatePage);
        return Result.ok(outVO);
    }

    /**
     * 根据流程部署key获取xml信息
     *
     * @param  loadTemplateInVO
     * @return WorkFlowPage 分页对象
     */
    @ApiOperation(value = "根据流程部署key获取xml信息", notes = "根据流程部署key获取xml信息")
    @ApiResponses({@ApiResponse(code = 1, message = "OK", response = Result.class)})
    @PostMapping(value = "/load/template/xml")
    public void downLoadTemplateXML(@RequestBody WorkFlowLoadTemplateInVO loadTemplateInVO) throws IOException {
        // 参数赋值操作
        WorkFlowLoadTemplateDTO templateDTO = new WorkFlowLoadTemplateDTO();
        templateDTO.setTemplateKey(loadTemplateInVO.getTemplateKey());
        templateDTO.setVersion(loadTemplateInVO.getVersion());

        // 进行查询
        workFlowService.getLoadTemplateXML(templateDTO);
    }

    /**
     * 根据流程部署key获取xml信息
     *
     * @param  loadTemplateInVO
     * @return WorkFlowPage 分页对象
     */
    @ApiOperation(value = "根据流程部署key获取xml信息", notes = "根据流程部署key获取xml信息")
    @ApiResponses({@ApiResponse(code = 1, message = "OK", response = Result.class)})
    @PostMapping(value = "/load/template/xml/str")
    public Result<String> getLoadTemplateXMLStr(@RequestBody WorkFlowLoadTemplateInVO loadTemplateInVO) throws IOException {
        // 参数赋值操作
        WorkFlowLoadTemplateDTO templateDTO = new WorkFlowLoadTemplateDTO();
        templateDTO.setTemplateKey(loadTemplateInVO.getTemplateKey());
        templateDTO.setVersion(loadTemplateInVO.getVersion());

        // 进行查询
        String loadTemplateXMLStr = workFlowService.getLoadTemplateXMLStr(templateDTO);
        return Result.ok(loadTemplateXMLStr);
    }

    /**
     * 根据流程部署key获取部署图片信息
     *
     * @param  loadTemplateInVO
     * @return WorkFlowPage 分页对象
     */
    @ApiOperation(value = "根据流程部署key获取xml信息", notes = "根据流程部署key获取xml信息")
    @ApiResponses({@ApiResponse(code = 1, message = "OK", response = Result.class)})
    @PostMapping(value = "/load/template/png")
    public void getLoadTemplatePIC(@RequestBody WorkFlowLoadTemplateInVO loadTemplateInVO) throws IOException {
        // 参数赋值操作
        WorkFlowLoadTemplateDTO templateDTO = new WorkFlowLoadTemplateDTO();
        templateDTO.setTemplateKey(loadTemplateInVO.getTemplateKey());
        templateDTO.setVersion(loadTemplateInVO.getVersion());

        // 进行查询
        workFlowService.getLoadTemplatePIC(templateDTO);
    }
}

2.1.3 具体的serviceImpl实现

流程的主要部分则是在此处实现,具体包含了发起,提交,撤销,退回,结束,跳岗,历史信息查询等功能,具体实现代码过多,所以单写了两片博文记录,地址分别如下

  • 第一部分代码 : 简单的springboot整合activiti5-serviceImpl部分(1)
  • 第二部分代码 : 简单的springboot整合activiti5-serviceImpl部分(2)
2.1.4 工具类实现

此处只记录activiti5.22.0使用的工具包了,其余部分都是业务实现逻辑util则不进行展示了

package cn.git.workflow.util;

import cn.git.common.exception.ServiceException;
import cn.git.workflow.constant.WorkFlowServerConstant;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import org.activiti.engine.TaskService;
import org.activiti.engine.impl.javax.el.ExpressionFactory;
import org.activiti.engine.impl.javax.el.ValueExpression;
import org.activiti.engine.impl.juel.ExpressionFactoryImpl;
import org.activiti.engine.impl.juel.SimpleContext;
import org.activiti.engine.impl.juel.SimpleResolver;
import org.activiti.engine.impl.pvm.PvmActivity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.impl.pvm.process.TransitionImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @description: activiti5.22.0 专用util包
 * @program: bank-credit-sy
 * @author: lixuchun
 * @create: 2024-06-14
 */
@Component
public class ActivitiUtil {

    @Autowired
    private TaskService taskService;

    /**
     * @description: 验证表达式
     *
     * @param condition 表达式
     * @param variablesMap 参数
     * @return: boolean
     */
    public boolean evalExpress(Object condition, Map<String, Object> variablesMap) {
        try {
            boolean flag;
            String conditionStr = StrUtil.toString(condition);
            ExpressionFactory factory = new ExpressionFactoryImpl();
            SimpleContext context = new SimpleContext(new SimpleResolver());
            for (String key : variablesMap.keySet()) {
                if (conditionStr.indexOf(key) > 0) {
                    factory.createValueExpression(context, "#{" + key + "}", String.class).setValue(context, variablesMap.get(key));
                }
            }

            ValueExpression valueExpression = factory.createValueExpression(context, conditionStr, boolean.class);
            flag = (Boolean) valueExpression.getValue(context);
            return flag;
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException(StrUtil.format("传入表达式验证报错了condition[{}],参数信息为[{}]",
                    condition, JSONObject.toJSONString(variablesMap)));
        }
    }

    /**
     * 获取排他网关正确的下一节点信息
     * @param currentActivity 当前节点信息
     * @param currentVariablesMap 当前节点处参数信息
     * @return
     */
    public List<PvmActivity> getNextPositionByExclusiveGateway(PvmActivity currentActivity, Map<String, Object> currentVariablesMap) {
        // 返回结果信息
        List<PvmActivity> activityList = new ArrayList<>();

        // 获取当前节点对外连接信息,并且计算最终选择哪条路线
        List<PvmTransition> transitionList = currentActivity.getOutgoingTransitions();
        for (PvmTransition transition : transitionList) {
            // 获取当前任务判定条件,注意此处不是节点,为对外条件flowSequence
            Object condition = transition.getProperty(WorkFlowServerConstant.CONDITION_TEXT);
            // 节点条件判定是否满足
            if (ObjectUtil.isNotNull(condition) && evalExpress(condition, currentVariablesMap)) {
                // 此处为对外指向flowSequence指向满足条件的节点
                activityList.add(transition.getDestination());
            }
        }
        return activityList;
    }

    /**
     * 获取并行网关正确的下一节点信息
     * @param currentActivity 当前节点信息
     * @return
     */
    public List<PvmActivity> getNextPositionByParallelGateway(PvmActivity currentActivity) {
        // 返回结果信息
        List<PvmActivity> activityList = new ArrayList<>();

        // 获取当前并行网关节点对外连接信息 transitionList 为并行网关对外的 -> 连线
        List<PvmTransition> transitionList = currentActivity.getOutgoingTransitions();
        for (PvmTransition transition : transitionList) {
            // 获取当前任务判定条件,注意此处不是节点,为对外条件 flowSequence,获取destination则为 -> 对应后面的节点
            PvmActivity destinationActivity = transition.getDestination();
            String activityNodeType = StrUtil.toString(destinationActivity.getProperty(WorkFlowServerConstant.NODE_TYPE));

            // 如果目标节点为userTask节点,则直接加入执行节点
            if (WorkFlowServerConstant.USER_TASK_FLAG.equalsIgnoreCase(activityNodeType)) {
                activityList.add(transition.getDestination());
            }
        }
        return activityList;
    }

    /**
     * 流程转向操作
     *
     * @param taskId 任务id
     * @param currentActivity 当前节点
     * @param destinationActivity 目标节点
     * @param variables 流程变量
     */
    public void turnTransitionForUTask(String taskId,
                                       ActivityImpl currentActivity,
                                       ActivityImpl destinationActivity,
                                       Map<String, Object> variables) {
        // 清空当前流向(存在风险,如果出现异常,则当前流向就消失了)
        List<PvmTransition> oriPvmTransitionList = clearTransition(currentActivity);

        try {
            // 当前节点创建新流向
            TransitionImpl newTransition = currentActivity.createOutgoingTransition();
            // 设置新流向的目标节点
            newTransition.setDestination(destinationActivity);
            // 执行转向任务
            if (ObjectUtil.isNotEmpty(variables)) {
                taskService.complete(taskId, variables);
            } else {
                taskService.complete(taskId);
            }
            // 删除目标节点新流入
            destinationActivity.getIncomingTransitions().remove(newTransition);
            // 还原以前流向
            restoreTransition(currentActivity, oriPvmTransitionList);
        } catch (Exception e) {
            e.printStackTrace();
            // 如果出现异常,则恢复以前流向
            restoreTransition(currentActivity, oriPvmTransitionList);
        }
    }

    /**
     * 清空指定活动节点流向
     *
     * @param activityImpl 活动节点
     * @return 节点流向集合
     */
    public List<PvmTransition> clearTransition(ActivityImpl activityImpl) {
        // 存储当前节点所有流向临时变量
        List<PvmTransition> pvmTransitionList = activityImpl.getOutgoingTransitions();
        // 获取当前节点所有流向,存储到临时变量,然后清空
        List<PvmTransition> oriPvmTransitionList = new ArrayList<>(pvmTransitionList);
        // 清空当前节点所有流向
        pvmTransitionList.clear();

        return oriPvmTransitionList;
    }

    /**
     * 还原指定活动节点流向
     *
     * @param activityImpl 活动节点
     * @param oriPvmTransitionList 原有节点流向集合
     */
    public void restoreTransition(ActivityImpl activityImpl, List<PvmTransition> oriPvmTransitionList) {
        // 清空现有流向
        List<PvmTransition> pvmTransitionList = activityImpl.getOutgoingTransitions();
        pvmTransitionList.clear();
        // 还原以前流向
        pvmTransitionList.addAll(oriPvmTransitionList);
    }

}

2.2 风险实现

风险部分主要是原有调用webService部分的三个接口类,需要信贷系统这边提供feign接口进行补充实现,三个接口分别为

  • ActProcessWebServiceApi,流程定义信息查询service api接口
  • ActQueryWebServiceApi,综合历史等查询service api接口
  • ActTaskWebServiceApi,任务信息查询service api接口
2.2.1 风险实现的api接口定义实现

ActProcessWebServiceApi实现如下

package cn.git.workflow.service.impl;

import cn.git.common.exception.ServiceException;
import cn.git.workflow.constant.WorkFlowServerConstant;
import cn.git.workflow.dto.activiti.WorkFlowActProcessInstanceDTO;
import cn.git.workflow.dto.activiti.WorkFlowEndProcessDTO;
import cn.git.workflow.dto.activiti.WorkFlowStartProcessDTO;
import cn.git.workflow.mapstruct.ActivitiConvert;
import cn.git.workflow.service.ActProcessWebService;
import cn.git.workflow.util.ActivitiUtil;
import cn.hutool.core.util.ObjectUtil;
import org.activiti.engine.*;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;


/**
 * @description: activiti5.22.0,流程定义信息查询service
 * @program: bank-credit-sy
 * @author: lixuchun
 * @create: 2024-06-14
 */
@Service
public class ActProcessWebServiceImpl implements ActProcessWebService {

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private ProcessEngine processEngine;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private IdentityService identityService;

    @Autowired
    private ActivitiUtil activitiUtil;

    @Autowired
    private ActivitiConvert activitiConvert;

    /**
     * 根据流程id查询流程信息,包含参数信息
     *
     * @param processId 流程实例id
     * @return
     */
    @Override
    public WorkFlowActProcessInstanceDTO findProcessByPId(String processId) {
        // 获取流程实例信息
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processId)
                .includeProcessVariables()
                .singleResult();

        // 返回转换内容信息
        return activitiConvert.processInstanceCovertToDTO(processInstance);
    }

    /**
     * 开启一个流程信息
     *
     * @param startProcessDTO
     * @return
     */
    @Override
    public WorkFlowStartProcessDTO startProcess(WorkFlowStartProcessDTO startProcessDTO) {
        // 权限设置
        identityService.setAuthenticatedUserId(startProcessDTO.getUserId());
        // 发起流程
        ProcessInstance processInstance;

        // 发起流程
        if (ObjectUtil.isNotEmpty(startProcessDTO.getParamMap())) {
            // 带参数
            processInstance = runtimeService.startProcessInstanceByKey(
                    startProcessDTO.getProcessKey(),
                    startProcessDTO.getBizId(),
                    startProcessDTO.getParamMap());
        } else {
            // 不带自定义参数信息
            processInstance = runtimeService.startProcessInstanceByKey(
                    startProcessDTO.getProcessKey(),
                    startProcessDTO.getBizId());
        }

        // 获取当前任务列表,刚刚发起业务,应该只有一个值
        List<Task> list = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).list();
        // 设置返回信息
        if (ObjectUtil.isNotEmpty(list)) {
            // 获取第一个任务
            Task task = list.get(0);
            WorkFlowStartProcessDTO.WorkFlowStartProcessOUTDTO processOUTDTO = new WorkFlowStartProcessDTO.WorkFlowStartProcessOUTDTO();
            processOUTDTO.setProcessId(processInstance.getProcessInstanceId());
            processOUTDTO.setActivityName(task.getName());
            processOUTDTO.setTaskDefKey(task.getTaskDefinitionKey());
            processOUTDTO.setTaskId(task.getId());
            startProcessDTO.setWorkFlowStartProcessOUTDTO(processOUTDTO);
        }
        return startProcessDTO;
    }

    /**
     * 根据流程id取消流程
     *
     * @param processId 流程实例id
     * @param userId    用户id userCd + ":" + orgCd
     */
    @Override
    public void cancelProcessByProcessId(String processId, String userId) {
        // 权限认证
        identityService.setAuthenticatedUserId(userId);

        // 开始进行业务撤销
        runtimeService.deleteProcessInstance(processId, WorkFlowServerConstant.DEFAULT_CANCEL_REASON);
    }

    /**
     * 结束流程
     *
     * @param flowEndProcessDTO
     */
    @Override
    public void endProcess(WorkFlowEndProcessDTO flowEndProcessDTO) {
        // 获取当前任务信息
        Task currentTask = taskService.createTaskQuery()
                .taskId(flowEndProcessDTO.getTaskId())
                .singleResult();

        // 获取流程定义信息
        ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService)
                .getDeployedProcessDefinition(currentTask.getProcessDefinitionId());

        // 获取流程定义节点详情信息
        List<ActivityImpl> allActivityList = processDefinition.getActivities();

        // 过滤出end节点信息
        ActivityImpl endActivity = allActivityList.stream().filter(activity ->
                ObjectUtil.isEmpty(activity.getOutgoingTransitions())
        ).findAny().orElse(null);

        // 空值判定
        if (ObjectUtil.isEmpty(endActivity)) {
            throw new ServiceException("获取流程end节点信息为空,请确认流程图有end节点信息配置正确!");
        }

        // 获取当前节点定义信息
        ActivityImpl currentTaskActivity = processDefinition.findActivity(currentTask.getTaskDefinitionKey());
        // 进行任务跳转,既结束任务
        activitiUtil.turnTransitionForUTask(currentTask.getId(),
                currentTaskActivity,
                endActivity,
                flowEndProcessDTO.getParamMap());

    }

}

ActQueryWebServiceImpl实现如下

package cn.git.workflow.service.impl;

import cn.git.common.exception.ServiceException;
import cn.git.workflow.constant.WorkFlowServerConstant;
import cn.git.workflow.dto.activiti.WorkFlowActHisActInstListDTO;
import cn.git.workflow.dto.activiti.WorkFlowActHisProcessListDTO;
import cn.git.workflow.dto.activiti.WorkFlowActWorkListDTO;
import cn.git.workflow.entity.ActHiProcinst;
import cn.git.workflow.mapper.ActHiProcinstMapper;
import cn.git.workflow.mapstruct.ActivitiConvert;
import cn.git.workflow.service.ActQueryWebService;
import cn.git.workflow.util.ActivitiUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricActivityInstanceQuery;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricProcessInstanceQuery;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @description: activiti5.22.0,综合历史等查询service
 * @program: bank-credit-sy
 * @author: lixuchun
 * @create: 2024-06-14
 */
@Service
public class ActQueryWebServiceImpl implements ActQueryWebService {

    @Autowired
    private ActHiProcinstMapper actHiProcinstMapper;

    @Autowired
    private ActivitiConvert activitiConvert;

    @Autowired
    private TaskService taskService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private ActivitiUtil activitiUtil;

    /**
     * 通过用户id获取此用户全部业务信息
     *
     * @param userId
     * @return
     */
    @Override
    public List<WorkFlowActWorkListDTO> queryWorkList(String userId) {
        // 空值校验
        if (StrUtil.isBlank(userId)) {
            throw new ServiceException("userId参数为空,查询用户流程信息失败!");
        }

        // 分页查询初始化任务列表信息,processVariableValueEquals可以对参数进行过滤,此处用不到
        TaskQuery taskQuery = taskService.createTaskQuery()
                // 当前处理人
                .taskAssignee(userId)
                // 活动流程任务
                .active()
                // 查询包含参数信息,可以过滤使用
                .includeProcessVariables()
                // 创建时间排序
                .orderByTaskCreateTime()
                // 倒序排序
                .desc();

        // 进行数据查询,此处无需分页处理,直接查询全部
        List<Task> taskList = taskQuery.list();

        // 空值判定
        if (ObjectUtil.isEmpty(taskList)) {
            return new ArrayList<>();
        }

        // 转换为可用对象
        List<WorkFlowActWorkListDTO> workList = taskList.stream().map(task ->
                activitiConvert.taskConvertToActList(task)
        ).collect(Collectors.toList());

        return workList;
    }

    /**
     * 通过processId获取流程历史信息
     *
     * @param processId
     * @return
     */
    @Override
    public List<WorkFlowActHisActInstListDTO> queryHisTaskList(String processId) {
        // 获取历史活动实例
        HistoricActivityInstanceQuery activityInstanceQuery = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processId)
                .orderByHistoricActivityInstanceId()
                .desc();
        // 获取历史活动实例列表
        List<HistoricActivityInstance> activityInstanceList = activityInstanceQuery.list();

        // 类型信息转换
        List<WorkFlowActHisActInstListDTO> actHisActInstListDTOList = activityInstanceList.stream().map(instance ->
                activitiConvert.hisInstanceToActList(instance)
        ).collect(Collectors.toList());

        // 进行非task节点信息判定,排除无用节点
        actHisActInstListDTOList = actHisActInstListDTOList.stream().filter(actInst -> {
            boolean ifRightType = actInst.getActivityType().equals(WorkFlowServerConstant.EXCLUSIVE_GATEWAY)
                    || actInst.getActivityType().equals(WorkFlowServerConstant.PARALLEL_GATEWAY)
                    || actInst.getActivityType().equals(WorkFlowServerConstant.INCLUSIVE_GATEWAY)
                    || actInst.getActivityType().equals(WorkFlowServerConstant.EVENT_GATEWAY);
            return !ifRightType;
        }).collect(Collectors.toList());

        // 设置common评论信息
        actHisActInstListDTOList.forEach(actInst -> {
            // 获取common信息
            List<Comment> commentList = taskService.getTaskComments(actInst.getTaskId());
            if (ObjectUtil.isNotEmpty(commentList)) {
                // 设置评论信息
                actInst.setOpinion(commentList.get(0).getFullMessage());
            }
        });

        return actHisActInstListDTOList;
    }

    /**
     * 获取新流程已经结束的历史信息
     *
     * @param userId 用户id
     * @param finishStatus 流程状态 finished,running
     * @return 任务列表
     */
    public List<WorkFlowActHisProcessListDTO> queryStatusHisList(String userId, String finishStatus) {
        // 参数校验
        if (StrUtil.isBlank(userId)) {
            throw new ServiceException("历史信息查询时候,用户id为空,请确认");
        }

        // 设置查询条件
        HistoricProcessInstanceQuery instanceQuery = historyService.createHistoricProcessInstanceQuery()
                // 参与用户id
                .involvedUser(userId)
                // 按照时间倒序排序
                .orderByProcessInstanceStartTime()
                .desc();
        // 是否结束条件判定 0否1是
        if (WorkFlowServerConstant.WORKFLOW_STATUS_FINISHED.equals(finishStatus)) {
            instanceQuery.finished();
        } else {
            // 解决空值情况下,默认查询历史为running状态
            finishStatus = WorkFlowServerConstant.WORKFLOW_STATUS_RUNNING;
            instanceQuery.unfinished();
        }

        // 获取查询结果集合
        List<HistoricProcessInstance> historicProcessInstanceList = instanceQuery.list();

        // 查询信息进行转换
        List<WorkFlowActHisProcessListDTO> actHisProcessListDTOList = historicProcessInstanceList
                .stream().map(historyInstance ->
                        activitiConvert.hisProcessInstanceCovertToDTO(historyInstance)
                ).collect(Collectors.toList());

        // 跟踪列表查询数据不正确 优化处理
        List<ActHiProcinst> historyInstanceList = actHiProcinstMapper.listHistoryInstance(finishStatus, userId);

        // 非空判定
        if (ObjectUtil.isNotEmpty(historyInstanceList)) {
            Map<String, String> instanceMap = new HashMap<>();
            for (ActHiProcinst actHiProcinst : historyInstanceList) {
                String businessKey = actHiProcinst.getBusinessKey();
                instanceMap.put(businessKey, businessKey);
            }
            // 去除不正确处理人
            List<WorkFlowActHisProcessListDTO> removeList = new ArrayList<>();
            for (WorkFlowActHisProcessListDTO vo : actHisProcessListDTOList) {
                if (instanceMap.get(vo.getBusinessKey()) == null) {
                    removeList.add(vo);
                }
            }
            // 删除错误信息
            if (removeList.size() > 0) {
                actHisProcessListDTOList.removeAll(removeList);
            }
        } else {
            // 如果正确查询没有信息,则全部去除即可
            actHisProcessListDTOList = new ArrayList<>();
        }
        return actHisProcessListDTOList;
    }
}

ActTaskWebServiceImpl实现如下

package cn.git.workflow.service.impl;

import cn.git.common.exception.ServiceException;
import cn.git.workflow.constant.WorkFlowServerConstant;
import cn.git.workflow.dto.activiti.*;
import cn.git.workflow.service.ActTaskWebService;
import cn.git.workflow.util.ActivitiUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.PvmActivity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.impl.task.TaskDefinition;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @description: activiti5.22.0,任务信息查询service
 * @program: bank-credit-sy
 * @author: lixuchun
 * @create: 2024-06-14
 */
@Service
public class ActTaskWebServiceImpl implements ActTaskWebService {

    @Autowired
    private ActivitiUtil activitiUtil;

    @Autowired
    private TaskService taskService;

    @Autowired
    private IdentityService identityService;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private RuntimeService runtimeService;

    /**
     * 根据任务id和用户id查询任务信息
     *
     * @param workFlowTaskInfoDTO
     * @return
     */
    @Override
    public WorkFlowTaskInfoDTO findTaskByInAndUserId(WorkFlowTaskInfoDTO workFlowTaskInfoDTO) {
        // 设置权限
        identityService.setAuthenticatedUserId(workFlowTaskInfoDTO.getUserId());

        // 获取task信息
        Task task = taskService.createTaskQuery()
                .taskId(workFlowTaskInfoDTO.getTaskId())
                .includeProcessVariables()
                .singleResult();

        // 设置响应信息
        WorkFlowTaskInfoDTO.WorkFlowTaskInfoOutDTO taskOutDTO = new WorkFlowTaskInfoDTO.WorkFlowTaskInfoOutDTO();
        taskOutDTO.setTaskName(task.getName());
        taskOutDTO.setTaskDefKey(task.getTaskDefinitionKey());
        taskOutDTO.setTaskVariableMap(task.getProcessVariables());
        taskOutDTO.setProcessId(task.getProcessInstanceId());
        workFlowTaskInfoDTO.setWorkFlowTaskInfoOutDTO(taskOutDTO);

        return workFlowTaskInfoDTO;
    }

    /**
     * 退回到发起岗位
     *
     * @param workFlowBackToStartDTO
     * @return
     */
    @Override
    public WorkFlowBackToStartDTO submitTaskBack(WorkFlowBackToStartDTO workFlowBackToStartDTO) {
        // 获取使用参数信息
        String userId = workFlowBackToStartDTO.getUserId();
        String taskId = workFlowBackToStartDTO.getTaskId();
        String opinion = workFlowBackToStartDTO.getOpinion();

        // 设置权限信息
        identityService.setAuthenticatedUserId(userId);

        // 获取任务信息
        Task currentTask = taskService.createTaskQuery()
                .taskId(taskId)
                .includeProcessVariables()
                .singleResult();

        // 设置意见信息
        if (StrUtil.isNotBlank(workFlowBackToStartDTO.getOpinion())) {
            //只保留一条批注记录
            List<Comment> commentList = taskService.getTaskComments(currentTask.getId());
            if(ObjectUtil.isNotEmpty(commentList)){
                commentList.forEach(common -> {
                    taskService.deleteComment(common.getId());
                });
            }
            // 添加新的批注信息
            taskService.addComment(currentTask.getId(),
                    currentTask.getProcessInstanceId(), workFlowBackToStartDTO.getOpinion());
        }

        // 获取当前任务参数信息
        Map<String, Object> opinionMap = currentTask.getProcessVariables();
        opinionMap.put(WorkFlowServerConstant.COMMENT_FLAG, opinion);
        opinionMap.put(WorkFlowServerConstant.OPINION_FLAG, opinion);

        // 对历史任务进行正序排序,获取最早的任务节点
        List<HistoricActivityInstance> activityInstanceList = historyService.createHistoricActivityInstanceQuery()
                // 流程执行id
                .executionId(currentTask.getProcessInstanceId())
                // 按照时间排序
                .orderByHistoricActivityInstanceStartTime()
                .asc()
                .list();

        // 找到第一个userTask类型的任务节点,既为发起节点
        HistoricActivityInstance firstActivityInstance = activityInstanceList.stream().filter(activityInstance ->
                WorkFlowServerConstant.USER_TASK_FLAG.equalsIgnoreCase(activityInstance.getActivityType())
        ).findFirst().orElse(null);

        // 获取实际发起节点的流程定义信息
        ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService)
                .getDeployedProcessDefinition(firstActivityInstance.getProcessDefinitionId());

        // 获取发起节点定义信息
        ActivityImpl startActivity = processDefinition.findActivity(firstActivityInstance.getActivityId());

        // 获取当前节点定义信息
        ActivityImpl currentTaskActivity = processDefinition.findActivity(currentTask.getTaskDefinitionKey());

        // 退回首节点校验,首节点不能再次退回首节点
        if(startActivity.getId().equals(currentTask.getTaskDefinitionKey())){
            throw new ServiceException("当前节点为首节点,无法退回");
        }

        // 退回首节点校验,首节点不能再次退回首节点
        if(startActivity.getId().equals(currentTask.getTaskDefinitionKey())){
            throw new ServiceException("当前节点为首节点,无法退回");
        }

        // 通过流程实例id以及任务定义key获取同级别任务列表信息,既需要退回的任务
        List<Task> backTaskList = taskService.createTaskQuery()
                .processInstanceId(currentTask.getProcessInstanceId())
                .taskDefinitionKey(currentTask.getTaskDefinitionKey())
                .list();

        // 进行回退操作
        backTaskList.forEach(task -> {
            // 进行任务跳转
            activitiUtil.turnTransitionForUTask(task.getId(),
                    currentTaskActivity,
                    startActivity,
                    opinionMap);
        });

        // 通过processId获取最新的一个task任务信息
        Task nextTask = taskService.createTaskQuery()
                // 流程实例id
                .processInstanceId(currentTask.getProcessInstanceId())
                // 存活状态
                .active()
                // 按照日期排序,最新的一个
                .orderByTaskCreateTime()
                .desc()
                .singleResult();

        // 获取此流程发起人信息
        Map<String, Object> processVariables = currentTask.getProcessVariables();
        String creator = (String) processVariables.get(WorkFlowServerConstant.CREATOR_FLAG);

        // 进行处理人强制转换,修改流程处理人信息
        nextTask.setAssignee(creator);
        taskService.saveTask(nextTask);
        taskService.claim(nextTask.getId(), creator);

        // 设置响应信息
        WorkFlowBackToStartDTO.WorkFlowBackToStartOutDTO taskOutDTO = new WorkFlowBackToStartDTO.WorkFlowBackToStartOutDTO();
        taskOutDTO.setProcessId(nextTask.getAssignee());
        taskOutDTO.setTaskName(nextTask.getName());
        taskOutDTO.setTaskDefKey(nextTask.getTaskDefinitionKey());
        taskOutDTO.setTaskVariableMap(nextTask.getProcessVariables());
        taskOutDTO.setCreator(nextTask.getAssignee());
        workFlowBackToStartDTO.setWorkFlowBackToStartOutDTO(taskOutDTO);

        return workFlowBackToStartDTO;
    }

    /**
     * 提交普通任务
     *
     * @param workFlowSubmitNormalDTO 参数信息
     * @return
     */
    @Override
    public WorkFlowSubmitNormalDTO submitNormalTask(WorkFlowSubmitNormalDTO workFlowSubmitNormalDTO) {
        // 获取使用参数信息
        String userId = workFlowSubmitNormalDTO.getWfusers();
        String taskId = workFlowSubmitNormalDTO.getTaskId();
        String taskComment = workFlowSubmitNormalDTO.getComment();

        // 设置权限信息
        identityService.setAuthenticatedUserId(userId);

        // 获取当前任务信息
        Task task = taskService.createTaskQuery()
                .taskId(taskId)
                .includeProcessVariables()
                .singleResult();

        // 获取执行id
        String executionId = task.getExecutionId();

        // 设置留言信息,只留一条批注信息
        List<Comment> commentList = taskService.getTaskComments(task.getId());
        if(ObjectUtil.isNotEmpty(commentList)){
            for(Comment comment : commentList){
                // 删除现有批注信息
                taskService.deleteComment(comment.getId());
            }
        }

        // 添加批注信息
        taskService.addComment(task.getId(), task.getProcessInstanceId(), taskComment);

        // 获取参数信息
        Map<String, Object> paramMap = workFlowSubmitNormalDTO.getVariableMap();
        paramMap.put("comment", taskComment);

        // 设置参数信息
        runtimeService.setVariables(executionId, paramMap);

        // 开始提交任务
        taskService.complete(task.getId(), paramMap);

        // 获取下一个任务信息
        List<Task> taskList = taskService.createTaskQuery()
                // 执行id
                .executionId(executionId)
                .list();

        // 进行认领操作,默认第一个任务
        if (ObjectUtil.isNotEmpty(taskList)) {
            // 进行处理人强制转换
            Task nextTask = taskService.createTaskQuery()
                    .taskId(taskList.get(0).getId())
                    .singleResult();
            nextTask.setAssignee(userId);
            taskService.saveTask(nextTask);
            taskService.claim(nextTask.getId(), userId);

            // 设置响应信息
            WorkFlowSubmitNormalDTO.WorkFlowSubmitNormalOutDTO outDTO = new WorkFlowSubmitNormalDTO.WorkFlowSubmitNormalOutDTO();
            outDTO.setNextTaskId(nextTask.getId());
            outDTO.setTaskDefKey(nextTask.getTaskDefinitionKey());
            outDTO.setTaskName(nextTask.getName());
            workFlowSubmitNormalDTO.setWorkFlowSubmitNormalOutDTO(outDTO);
        }

        return workFlowSubmitNormalDTO;
    }

    /**
     * 保存任务
     *
     * @param workFlowSaveTaskDTO 参数dto
     */
    @Override
    public void saveTask(WorkFlowSaveTaskDTO workFlowSaveTaskDTO) {
        // 获取任务service
        Task task = taskService.createTaskQuery()
                .taskId(workFlowSaveTaskDTO.getTaskId())
                .singleResult();

        // 设置意见信息
        String comment = workFlowSaveTaskDTO.getComment();
        if (StrUtil.isNotBlank(comment)) {
            //只保留一条批注记录
            List<Comment> commentList = taskService.getTaskComments(task.getId());
            if(ObjectUtil.isNotEmpty(commentList)){
                commentList.forEach(common -> {
                    taskService.deleteComment(common.getId());
                });
            }
            // 添加新的批注信息
            taskService.addComment(task.getId(), task.getProcessInstanceId(), comment);
        }

        // 保存参数
        if (ObjectUtil.isNotEmpty(workFlowSaveTaskDTO.getParamMap())) {
            taskService.setVariables(task.getId(), workFlowSaveTaskDTO.getParamMap());
        }
    }

    /**
     * 委派任务
     *
     * @param delegator
     * @param taskId
     */
    @Override
    public void delegateTask(String delegator, String taskId) {
        // 获取任务信息
        Task task = taskService.createTaskQuery()
                .taskId(taskId)
                .singleResult();

        // 进行任务委派
        task.setAssignee(delegator);
        task.setOwner(task.getAssignee());
        taskService.saveTask(task);
    }

    /**
     * 跳转任务
     *
     * @param workFlowGoToTaskDTO 参数dto
     */
    @Override
    public void gotoTask(WorkFlowGoToTaskDTO workFlowGoToTaskDTO) {
        // 获取使用参数信息
        String taskId = workFlowGoToTaskDTO.getTaskId();
        String nextTaskDefinitionKey = workFlowGoToTaskDTO.getDesTaskDefinitionKey();
        String authUserId = workFlowGoToTaskDTO.getUserId();


        // 获取任务信息
        Task task = taskService.createTaskQuery()
                .taskId(taskId)
                .singleResult();

        // 设置意见信息
        if (StrUtil.isNotBlank(workFlowGoToTaskDTO.getComment())) {
            //只保留一条批注记录
            List<Comment> commentList = taskService.getTaskComments(task.getId());
            if(ObjectUtil.isNotEmpty(commentList)){
                commentList.forEach(common -> {
                    taskService.deleteComment(common.getId());
                });
            }
            // 添加新的批注信息
            taskService.addComment(task.getId(), task.getProcessInstanceId(), workFlowGoToTaskDTO.getComment());
        }

        // 如果未传入权限设置id,则设置为当前task权限
        if (StrUtil.isBlank(authUserId)) {
            authUserId = task.getAssignee();
        }

        // 设置权限信息
        identityService.setAuthenticatedUserId(authUserId);

        // 任务进行跳节点,获取实际发起节点的流程定义信息
        ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService)
                .getDeployedProcessDefinition(task.getProcessDefinitionId());

        // 获取当前节点定义信息以及目标节点的节点信息
        ActivityImpl currentTaskActivity = processDefinition.findActivity(task.getTaskDefinitionKey());
        ActivityImpl destinationTaskActivity = processDefinition.findActivity(nextTaskDefinitionKey);

        // 通过流程实例id以及任务定义key获取同级别任务列表信息,既需要退回的任务
        List<Task> backTaskList = taskService.createTaskQuery()
                .processInstanceId(task.getProcessInstanceId())
                .taskDefinitionKey(task.getTaskDefinitionKey())
                .list();

        // 设置参数信息,我们系统userTask里面处理人的属性都是使用的wfuser,如果没有设定,跳转会报错,先默认设定一个,跳转之后再进行修改处理人即可
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put(WorkFlowServerConstant.WORKFLOW_USERS_FLAG, authUserId);

        backTaskList.forEach(backTask -> {
            // 进行任务跳转
            activitiUtil.turnTransitionForUTask(backTask.getId(),
                    currentTaskActivity,
                    destinationTaskActivity,
                    paramMap);
        });
    }

    /**
     * 查询下一节点信息
     *
     * @param nextPositionInfoDTO
     * @return
     */
    @Override
    public WorkFlowNextPositionInfoDTO findNextActivityUserInfo(WorkFlowNextPositionInfoDTO nextPositionInfoDTO) {
        // 获取参数信息
        String taskId = nextPositionInfoDTO.getTaskId();

        // 获取task信息
        Task task = taskService.createTaskQuery()
                .taskId(taskId)
                .singleResult();

        // 获取全部流程自定义变量信息
        Map<String, Object> taskVariables = runtimeService.getVariables(task.getExecutionId());

        // 任务进行跳节点,获取实际发起节点的流程定义信息
        ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService)
                .getDeployedProcessDefinition(task.getProcessDefinitionId());

        // 获取当前节点信息,可以获取outgoingSequenceFlows,结合自定义变量信息,可以获取下一节点信息
        ActivityImpl currentActivity = processDefinition.findActivity(task.getTaskDefinitionKey());

        // 获取当前节点对外指向,并且对指向进行分析
        List<PvmTransition> transitionList = currentActivity.getOutgoingTransitions();
        if (ObjectUtil.isEmpty(transitionList)) {
            throw new ServiceException("当前节点没有下一节点信息");
        }

        // 最终获取全部下一处理岗位定义节点
        List<PvmActivity> finalActivityList = new ArrayList<>();

        // 循环获取目标节点信息,当前行内服务,只使用排他网关,所以后续分支只支持单一条件满足,下一节点为单一分支
        for (PvmTransition transition : transitionList) {
            // 获取目标节点
            PvmActivity destinationActivity = transition.getDestination();
            // 获取目标节点nodeType类型
            String activityNodeType = StrUtil.toString(destinationActivity.getProperty(WorkFlowServerConstant.NODE_TYPE));
            // 目标节点不同类型进行不同处理,
            if (WorkFlowServerConstant.EXCLUSIVE_GATEWAY.equals(activityNodeType) || WorkFlowServerConstant.INCLUSIVE_GATEWAY.equals(activityNodeType)) {
                // 注意此处为 节点->排他网关/包含网关->节点 形式,更复杂形式需要针对业务进行调整
                List<PvmActivity> exclusiveGateActivityList = activitiUtil.getNextPositionByExclusiveGateway(destinationActivity, taskVariables);
                finalActivityList.addAll(exclusiveGateActivityList);
            } else if (WorkFlowServerConstant.PARALLEL_GATEWAY.equals(activityNodeType)) {
                // 并行网关 节点->并行网关->节点
                List<PvmActivity> parallelGateActivityList = activitiUtil.getNextPositionByParallelGateway(destinationActivity);
                finalActivityList.addAll(parallelGateActivityList);
            } else if (WorkFlowServerConstant.USER_TASK_FLAG.equalsIgnoreCase(activityNodeType)) {
                // 普通用户任务 节点->节点
                finalActivityList.add(destinationActivity);
            } else {
                throw new ServiceException(StrUtil.format("当前获取下一岗位信息暂时activityNodeType[{}]暂时不支持!", activityNodeType));
            }
        }

        // 获取下一岗位信息非空判定
        if (ObjectUtil.isEmpty(finalActivityList)) {
            throw new ServiceException(StrUtil.format("当前taskId[{}]对应节点[{}]获取下一岗位信息为空!",
                    taskId, task.getTaskDefinitionKey()));
        }

        // 获取下一岗位名称id信息
        ActivityImpl nextActivity = (ActivityImpl)finalActivityList.get(0);
        String nextTaskName = nextActivity.getProperty(WorkFlowServerConstant.NAME_FLAG).toString();
        // 下一岗位id
        TaskDefinition taskDefinition = (TaskDefinition)nextActivity.getProperty(WorkFlowServerConstant.TASK_DEFINITION);
        String nextDefinitionKey = taskDefinition.getKey();

        // 设置响应参数
        WorkFlowNextPositionInfoDTO.WorkFlowNextPositionInfoOutDTO outDTO = new WorkFlowNextPositionInfoDTO.WorkFlowNextPositionInfoOutDTO();
        outDTO.setNextTaskName(nextTaskName);
        outDTO.setNextTaskDefinitionKey(nextDefinitionKey);
        nextPositionInfoDTO.setWorkFlowNextPositionInfoOutDTO(outDTO);

        return nextPositionInfoDTO;
    }
}

3. 测试效果

以下为流程测试部分,截取几个比较重要的操作,简单进行以下接口测试

3.1 部署一个流程

在这里插入图片描述

3.2 获取部署列表

在这里插入图片描述
在这里插入图片描述

3.3 发起流程

在这里插入图片描述
在这里插入图片描述

3.4 其余接口

功能比较多,此处就不一一进行测试了,交给测试人员进行测试吧,其余提供功能如下所示:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/748108.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

《梦醒蝶飞:释放Excel函数与公式的力量》6.1 DATE函数

6.1 DATE函数 第一节&#xff1a;DATE函数 1&#xff09;DATE函数概述 DATE函数是Excel中的一个内置函数&#xff0c;用于根据指定的年、月、日返回对应的日期序列号。这个函数非常有用&#xff0c;尤其是在处理日期数据时&#xff0c;它可以帮助你构建特定的日期&#xff0…

20-OWASP top10--XXS跨站脚本攻击

目录 什么是xxs&#xff1f; XSS漏洞出现的原因 XSS分类 反射型XSS 储存型XSS DOM型 XSS XSS漏洞复现 XSS的危害或能做什么&#xff1f; 劫持用户cookie 钓鱼登录 XSS获取键盘记录 同源策略 &#xff08;1&#xff09;什么是跨域 &#xff08;2&#xff09;同源策略…

文章解读与仿真程序复现思路——电网技术EI\CSCD\北大核心《计及氢储能与需求响应的路域综合能源系统规划方法》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

在数据库领域是如何实现“多租户”的呢?

数据库多租技术介绍 随着云计算时代的到来&#xff0c;多租户的概念也逐渐广为人知。“多租户”使得租户之间可以共享物理资源&#xff0c;能够帮助用户节约硬件成本和运维成本&#xff0c;提高资源利用效率。同时&#xff0c;在实现的过程中&#xff0c;考虑到共享带来的安全…

【单片机毕业设计选题24031】-基于STM32的智能手环设计

系统功能: 使用12864OLED液晶屏显示当前的步数&#xff0c;温度值&#xff0c;心率和报警值&#xff0c;单位是心率/分钟设置步长&#xff0c;测量里程&#xff1b;可以设置温度心率的上下限报警值&#xff0c;设置、加、减&#xff1b;用红外传感器XL01实现心率的测量&#x…

华为云x86架构下部署mysql

华为云x86架构下部署mysql 1. 配置X86架构ESC2. 查看本系统中有没有安装mariadb相关的组件&#xff0c;有则卸载3. 安装mysql4. 启动mysql5. 登录MySQL&#xff0c;修改密码&#xff0c;开放访问权限 1. 配置X86架构ESC 2. 查看本系统中有没有安装mariadb相关的组件&#xff0c…

拥抱数字化未来,如何以费控驱动业务发展?

管理费用是企业运营中仅次于人力成本的第二大可控成本&#xff0c;一般会占到企业年度收入的5%—10%&#xff0c;但多数企业存在费用疏于管理、费用管理制度流于纸面难落地、费用浪费严重等问题。 如果不进行科学管理&#xff0c;有专家表示&#xff0c;估计企业每年至少有10%的…

Java家教系统小程序APP公众号h5源码

让学习更高效&#xff0c;更便捷 &#x1f31f; 引言&#xff1a;家教新选择&#xff0c;小程序来助力 在快节奏的现代生活中&#xff0c;家长们越来越注重孩子的教育问题。然而&#xff0c;如何为孩子找到一位合适的家教老师&#xff0c;成为了许多家长头疼的问题。现在&…

Flutter笔记(一)- 安装和配置Flutter

一、下载Flutter 访问网址&#xff1a;https://docs.flutter.dev/get-started/install?hlzh-cn 根据电脑所使用的操作系统的平台进行选择。笔者电脑的操作系统为Windows&#xff0c;因此选择如图1-1的Windows图片&#xff1a; 图1-1 Flutter网站&#xff08;一&#xff09; …

controller不同的后端路径对应vue前端传递数据发送请求的方式

目录 案例一&#xff1a; 为什么使用post发送请求&#xff0c;参数依旧会被拼接带url上呢&#xff1f;这应该就是param 与data传参的区别。即param传参数参数会被拼接到url后&#xff0c;data会以请求体传递 补充&#xff1a;后端controller 参数上如果没写任何注解&#xff0c…

Vue3抽屉(Drawer)

效果如下图&#xff1a;在线预览 APIs 参数说明类型默认值必传width宽度&#xff0c;在 placement 为 right 或 left 时使用string | number378falseheight高度&#xff0c;在 placement 为 top 或 bottom 时使用string | number378falsetitle标题string | slotundefinedfalse…

sheng的学习笔记-hive框架原理

需要学习的前置知识&#xff1a;hadoop 可参考 sheng的学习笔记-hadoop-CSDN博客 相关网址 官网&#xff1a;http://hive.apache.org 文档&#xff1a;https://cwiki.apache.org/confluence/display/Hive/GettingStarted https://cwiki.apache.org/confluence/display/Hive/…

FPGA SATA高速存储设计

今天来讲一篇如何在fpga上实现sata ip&#xff0c;然后利用sata ip实现读写sata 盘的目的&#xff0c;如果需要再速度和容量上增加&#xff0c;那么仅仅需要增加sata ip个数就能够实现增加sata盘&#xff0c;如果仅仅实现data的读写整体来说sata ip设计比较简单&#xff0c;下面…

华为开发者调试工具使用介绍(MDC)

MDC的开发过程的三大工具&#xff1a;MMC、MDS、Mind Studio&#xff0c;这三个工具完成了开发过程中的配置文件编写、代码编写以及AI模型的开发三个任务。除了开发&#xff0c;MDC还准备了两个调试工具&#xff0c;用于使用过程中数据的查看等。这一些调试工具分别对映射MDC中…

Claude走向开放

Claude的愿景一直是创建能够与人们协同工作并显著提升他们工作流程的人工智能系统。朝着这一方向迈进&#xff0c;Claude.ai的专业版和团队版用户现在可以将他们的聊天组织到项目中&#xff0c;将精选的知识和聊天活动整合到一个地方&#xff0c;并且可以让团队成员查看他们与C…

Redis发布、订阅模式(Pub/Sub)详解

Redis发布、订阅模式&#xff08;PUB-SUB&#xff09;详解 Redis的发布订阅&#xff08;Pub/Sub&#xff09;机制是一种消息通信模式&#xff0c;用于消息的广播。它允许多个客户端订阅&#xff08;Subscribe&#xff09;特定的频道&#xff08;Channel&#xff09;&#xff0c…

【unity笔记】八、Unity人物动画介绍

一、效果预览 本内容仅介绍为unity场景中的任务添加简单的动画效果。 二、小试牛刀 2.1 插件准备 在unity 中导入人物模型。常使用的免费人物模型和动画模型有Robot Kyle&#xff0c;Unity-Chan! Model&#xff0c;Basic Motions FREE。 其中Robot Kyle仅支持URP渲染。如…

喂饭级AI神器!免代码一键绘制图表,文本数据秒变惊艳视觉盛宴!

由于目前的AI生成图表工具存在以下几个方面的问题&#xff1a; 大多AI图表平台是纯英文&#xff0c;对国内用户来说不够友好&#xff1b;部分平台在生成图表前仍需选择图表类型、配置项&#xff0c;操作繁琐&#xff1b;他们仍需一份规整的数据表格&#xff0c;需要人为对数据…

盲源信道分离—FastICA算法性能仿真

本案例中使用Matlab软件对FastICA算法的声音分离性能进行了仿真&#xff0c;分别对简单波形的混合信号、不同类型声音的混合信号、同一类型的混合信号这三种情况进行仿真&#xff0c;主要从分离信号的波形形状、串音误差两方面对分离性能进行衡量&#xff0c;仿真结果显示快速I…

APP 自动化测试框架如何设计?

自动化测试框架是为了增强测试效率和准确性而设计的工具。它可以帮助开发人员和测试人员在软件开发周期中自动执行各种测试任务。在本文中&#xff0c;我们将从零开始详细介绍如何设计一个自动化测试框架。 1. 确定测试需求&#xff1a; 在设计测试框架之前&#xff0c;首先需…