环境

  • Spring Boot 2应用程序
  • H2数据库(为了方便,可选mysql...)

POM文件增加以下依赖

<!--activiti整合springboot依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-spring-boot-starter</artifactId>
</dependency>
<!--H2数据库-->
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
</dependency>

activiti-spring-boot-starter 版本控制

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.activiti.dependencies</groupId>
            <artifactId>activiti-dependencies</artifactId>
            <version>7.1.123</version>
            <scope>import</scope>
            <type>pom</type>
        </dependency>
    </dependencies>
</dependencyManagement>

** 添加镜像源(because阿里云镜像找不到啊╭(╯^╰)╮)

<repositories>
    <repository>
        <id>alfresco</id>
        <name>Activiti Releases</name>
        <url>https://artifacts.alfresco.com/nexus/content/repositories/activiti-releases/</url>
        <releases>
            <enabled>true</enabled>
        </releases>
    </repository>
</repositories>

TaskRuntime

自动注入TaskRuntime

@Autowired
private TaskRuntime taskRuntime;

来让们看看这个TaskRuntime是做什么の

public class TaskRuntime {

    /**
     * 返回就任务配置文件,其实就是任务配置文件里只有两个属性( List<TaskRuntimeEventListener<?>> taskRuntimeEventListeners(); 任务监听事件,
     * List<VariableEventListener<?>> variableEventListeners(); 变量监听事件
     */
    TaskRuntimeConfiguration configuration();

    /**
     * 根据任务id获取任务,(前置条件用户验证透过)
     */
    Task task(String taskId);

    /**
     * 根据分页条件获取任务,(前置条件用户验证透过)
     */
    Page<Task> tasks(Pageable pageable);

    /**
     * 根据分页条件和过滤条件获取任务,(前置条件用户验证透过)
     * GetTasksPayload{  
     *     private String id; 任务id
     *     private String assigneeId; 受理人id
     *     private List<String> groups; 用户组
     *     private String processInstanceId; 流程实例id
     *     private String parentTaskId; 父任务id
     * }
     */
    Page<Task> tasks(Pageable pageable,
                     GetTasksPayload getTasksPayload);

    /**
     * 创建一个任务
     * CreateTaskPayload{
     *     private String id;自己生成UUID
     *     private String name;任务名
     *     private String description;任务描述
     *     private Date dueDate;预计时间
     *     private int priority;优先级
     *     private String assignee;代理人
     *     private List<String> candidateGroups;可见组
     *     private List<String> candidateUsers;可见用户
     *     private String parentTaskId;父任务id
     *     private String formKey;表单key
     * }
     */
    Task create(CreateTaskPayload createTaskPayload);

    /**
     * 受理一个任务
     * ClaimTaskPayload{
     *     private String id;自己生成UUID
     *     private String taskId;任务id
     *     private String assignee;受理人
     * }
     */
    Task claim(ClaimTaskPayload claimTaskPayload);

    /**
     * 释放一个任务
     * ReleaseTaskPayload{
     *     private String id;自己生成UUID
     *     private String taskId;释放任务id
     * }
     */
    Task release(ReleaseTaskPayload releaseTaskPayload);

    /**
     * 完成一个任务
     * CompleteTaskPayload{
     *     private String id; 自己生成UUID
     *     private String taskId;任务id
     *     private Map<String, Object> variables;任务的变量
     * }
     */
    Task complete(CompleteTaskPayload completeTaskPayload);

    /**
     * 保存一个任务(可以个任务设置变量)
     * SaveTaskPayload{
     *      private String id;自己生成UUID
     *     private String taskId;任务id
     *     private Map<String, Object> variables;任务的变量
     * }
     */
    void save(SaveTaskPayload saveTaskPayload);

    /**
     * 更新任务信息
     * UpdateTaskPayload{
     *     private String id;自己生成UUID
     *     private String name;任务名
     *     private String description;任务描述
     *     private Date dueDate;预计时间
     *     private int priority;优先级
     *     private String assignee;代理人
     *     private List<String> candidateGroups;可见组
     *     private List<String> candidateUsers;可见用户
     *     private String parentTaskId;父任务id
     *     private String formKey;表单key     *     
     * }
     */
    Task update(UpdateTaskPayload updateTaskPayload);

    /**
     * 删除一个任务
     * DeleteTaskPayload{
     *      private String id;自己生成UUID
     *     private String taskId;任务id
     *     private String reason;原因
     * }
     */
    Task delete(DeleteTaskPayload deleteTaskPayload);

    /**
     * 增加一个变量
     * CreateTaskVariablePayload{
     *     private String id;自己生成UUID
     *     private String taskId;任务id
     *     private String name;要增加的key
     *     private Object value;要增加value
     * }
     */
    void createVariable(CreateTaskVariablePayload createTaskVariablePayload);

    /**
     * 修改一个变量
     * UpdateTaskVariablePayload[
     *     private String id;自己生成UUID
     *     private String taskId;任务id
     *     private String name;要修改的key
     *     private Object value;要修改value     
     * }
     */
    void updateVariable(UpdateTaskVariablePayload updateTaskVariablePayload);

    /**
     * 获取所以变量
     * GetTaskVariablesPayload{
     *     private String id;自己生成UUID
     *     private String taskId;任务id   
     * }
     */
    List<VariableInstance> variables(GetTaskVariablesPayload getTaskVariablesPayload);

    /**
     * 增加可见用户
     * CandidateUsersPayload{
     *     private String id;自己生成UUID
     *     private String taskId;任务id   
     *     private List<String> candidateUsers;用户集合
     * }
     */
    void addCandidateUsers(CandidateUsersPayload candidateUsersPayload);

    /**
     * 要删除的可见用户
     * CandidateUsersPayload{
     *     private String id;自己生成UUID
     *     private String taskId;任务id   
     *     private List<String> candidateUsers;用户集合
     * }
     */
    void deleteCandidateUsers(CandidateUsersPayload candidateUsersPayload);

    /**
     * 要增加可见用户组
     * CandidateGroupsPayload{
     *     private String id;自己生成UUID
     *     private String taskId;任务id   
     *     private List<String> candidateGroups;用户组
     * }
     */
    void addCandidateGroups(CandidateGroupsPayload candidateGroupsPayload);

    /**
     * 要删除的可见用户
     * CandidateGroupsPayload{
     *     private String id;自己生成UUID
     *     private String taskId;任务id   
     *     private List<String> candidateUsers;用户集合
     * }
     */
    void deleteCandidateGroups(CandidateGroupsPayload candidateGroupsPayload);

    /**
     * 根据任务id查询可见用户集合
     */
    List<String> userCandidates(String taskId);

    /**
     * 根据任务id查询可见用户组集合
     */
    List<String> groupCandidates(String taskId);
}

新建任务

taskRuntime.create(
            TaskPayloadBuilder.create()
                .withName("First Team Task")
                .withDescription("This is something really important")
                .withGroup("activitiTeam")
                .withPriority(10)
           .build());

activiti Payload一般使用建造者模式构建,也就是链式调用。好方便!
创建了一个

  • 名字为First Team Task的任务,
  • 任务描述:This is something really important
  • 用户组:activitiTeam
  • 优先级:10

新建监听

@Bean
public TaskRuntimeEventListener taskAssignedListener() {
  return taskAssigned
           -> logger.info(
                 ">>> Task Assigned: '"
                + taskAssigned.getEntity().getName()
                +"' We can send a notification to the assignee: "
                + taskAssigned.getEntity().getAssignee());
}

当任务发生状态变化则会触发回调监听方法,也就是上边lambda表达式(就是->后面的)

例子

例子地址
https://github.com/Activiti/activiti-examples/blob/master/activiti-api-basic-task-example

SecurityUtil

@Component
public class SecurityUtil {

    private Logger logger = LoggerFactory.getLogger(SecurityUtil.class);

    @Autowired
    private UserDetailsService userDetailsService;

    public void logInAs(String username) {

        UserDetails user = userDetailsService.loadUserByUsername(username);
        if (user == null) {
            throw new IllegalStateException("User " + username + " doesn't exist, please provide a valid user");
        }
        logger.info("> Logged in as: " + username);
        SecurityContextHolder.setContext(new SecurityContextImpl(new Authentication() {
            @Override
            public Collection<? extends GrantedAuthority> getAuthorities() {
                return user.getAuthorities();
            }

            @Override
            public Object getCredentials() {
                return user.getPassword();
            }

            @Override
            public Object getDetails() {
                return user;
            }

            @Override
            public Object getPrincipal() {
                return user;
            }

            @Override
            public boolean isAuthenticated() {
                return true;
            }

            @Override
            public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {

            }

            @Override
            public String getName() {
                return user.getUsername();
            }
        }));
        org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(username);
    }
}

DemoApplicationConfiguration

@Configuration
public class DemoApplicationConfiguration {

    private Logger logger = LoggerFactory.getLogger(DemoApplicationConfiguration.class);

    @Bean
    public UserDetailsService myUserDetailsService() {

        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();

        String[][] usersGroupsAndRoles = {
                {"salaboy", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
                {"ryandawsonuk", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
                {"erdemedeiros", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
                {"other", "password", "ROLE_ACTIVITI_USER", "GROUP_otherTeam"},
                {"admin", "password", "ROLE_ACTIVITI_ADMIN"},
        };

        for (String[] user : usersGroupsAndRoles) {
            List<String> authoritiesStrings = Arrays.asList(Arrays.copyOfRange(user, 2, user.length));
            logger.info("> Registering new user: " + user[0] + " with the following Authorities[" + authoritiesStrings + "]");
            inMemoryUserDetailsManager.createUser(new User(user[0], passwordEncoder().encode(user[1]),
                    authoritiesStrings.stream().map(s -> new SimpleGrantedAuthority(s)).collect(Collectors.toList())));
        }


        return inMemoryUserDetailsManager;
    }


    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}

DemoApplication

@SpringBootApplication
public class DemoApplication implements CommandLineRunner {

    private Logger logger = LoggerFactory.getLogger(DemoApplication.class);

    @Autowired
    private TaskRuntime taskRuntime;

    @Autowired
    private SecurityUtil securityUtil;


    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);

    }

    @Override
    public void run(String... args) {

        // Using Security Util to simulate a logged in user
        securityUtil.logInAs("salaboy");

        // Let's create a Group Task (not assigned, all the members of the group can claim it)
        //  Here 'salaboy' is the owner of the created task
        logger.info("> Creating a Group Task for 'activitiTeam'");
        taskRuntime.create(TaskPayloadBuilder.create()
                .withName("First Team Task")
                .withDescription("This is something really important")
                .withCandidateGroup("activitiTeam")
                .withPriority(10)
                .build());

        // Let's log in as 'other' user that doesn't belong to the 'activitiTeam' group
        securityUtil.logInAs("other");

        // Let's get all my tasks (as 'other' user)
        logger.info("> Getting all the tasks");
        Page<Task> tasks = taskRuntime.tasks(Pageable.of(0, 10));

        // No tasks are returned
        logger.info(">  Other cannot see the task: " + tasks.getTotalItems());


        // Now let's switch to a user that belongs to the activitiTeam
        securityUtil.logInAs("erdemedeiros");

        // Let's get 'erdemedeiros' tasks
        logger.info("> Getting all the tasks");
        tasks = taskRuntime.tasks(Pageable.of(0, 10));

        // 'erdemedeiros' can see and claim the task
        logger.info(">  erdemedeiros can see the task: " + tasks.getTotalItems());


        String availableTaskId = tasks.getContent().get(0).getId();

        // Let's claim the task, after the claim, nobody else can see the task and 'erdemedeiros' becomes the assignee
        logger.info("> Claiming the task");
        taskRuntime.claim(TaskPayloadBuilder.claim().withTaskId(availableTaskId).build());


        // Let's complete the task
        logger.info("> Completing the task");
        taskRuntime.complete(TaskPayloadBuilder.complete().withTaskId(availableTaskId).build());


    }

    @Bean
    public TaskRuntimeEventListener<TaskAssignedEvent> taskAssignedListener() {
        return taskAssigned -> logger.info(">>> Task Assigned: '"
                + taskAssigned.getEntity().getName() +
                "' We can send a notification to the assginee: " + taskAssigned.getEntity().getAssignee());
    }

    @Bean
    public TaskRuntimeEventListener<TaskCompletedEvent> taskCompletedListener() {
        return taskCompleted -> logger.info(">>> Task Completed: '"
                + taskCompleted.getEntity().getName() +
                "' We can send a notification to the owner: " + taskCompleted.getEntity().getOwner());
    }


}

详解DemoApplication

SecurityUtil和DemoApplicationConfiguration 这两类只是模拟用户登录的不重要略过

DemoApplication很重要

程序先是登录salaboy用户创建了一个

  • 名为First Team Task
  • 描述为This is something really important
  • 用户组activitiTeam优先级为10的任务。

根据DemoApplicationConfiguration

  • 可以知道other不属于activitiTeam,erdemedeiros属于activitiTeam 所以可以验证不同组的用户的任务是相互隔离的。

  • 当用户erdemedeiros claim(受理)任务则会回调taskAssignedListener监听里的方法

  • 当用户erdemedeiros complete(完成)任务则会回调taskCompletedListener监听里的方法

Q.E.D.


Very lazy chimp