Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 53

Triển khai Task Listener cho User Task

Recommended resources

Chào bạn,

Ở bài học trước, chúng ta đã thành công trong việc sử dụng Camunda Forms để tạo các giao diện nhập liệu cho người dùng một cách nhanh chóng, giúp tách biệt logic giao diện khỏi quy trình nghiệp vụ. Đây là một công cụ mạnh mẽ để thu thập dữ liệu từ phía client.

Tuy nhiên, có những logic nghiệp vụ cần được thực thi ở phía server mỗi khi có một sự kiện xảy ra với User Task. Ví dụ: gửi email thông báo khi một task được giao, tính toán và chuẩn bị sẵn dữ liệu trước khi người dùng mở form, hoặc kiểm tra quyền hạn của người dùng khi họ hoàn thành một task.

Trong bài học hôm nay, "Implement một Task Listener để thực thi logic tùy chỉnh trong vòng đời của User Task", chúng ta sẽ học cách "gắn" các đoạn mã Java vào các sự kiện quan trọng trong vòng đời của một User Task. Kỹ thuật này cho phép bạn mở rộng chức năng của các tác vụ người dùng một cách linh hoạt và giữ cho sơ đồ BPMN của bạn sạch sẽ, dễ hiểu.


1. Task Listener là gì và tại sao lại cần đến nó?

Task Listener là một đoạn code được thực thi khi một sự kiện cụ thể xảy ra trong vòng đời của một User Task. Hãy tưởng tượng nó như một "trigger" cho phép bạn chạy logic nghiệp vụ tùy chỉnh tại các thời điểm quan trọng như khi một task được tạo, được giao, hay được hoàn thành.

Một câu hỏi thường gặp là: "Tại sao không dùng một Service Task ngay sau User Task để gửi email thông báo?"

How to send an email when a usertask is assigned

Bài viết sau từ blog của Camunda giải thích rất rõ ràng tại sao việc sử dụng Task Listener lại là một giải pháp "thanh lịch" hơn so với việc thêm các Service Task chỉ để xử lý các logic kỹ thuật, giúp cho sơ đồ quy trình của bạn tập trung vào luồng nghiệp vụ chính.

Hãy đọc phần giới thiệu đầu tiên của bài viết (đến trước phần code). Chú ý đến việc so sánh giữa việc dùng Service Task và Task Listener để giải quyết yêu cầu gửi email thông báo.

Như bạn đã đọc, việc thêm một Service Task cho mỗi logic kỹ thuật (như gửi thông báo) sẽ làm "rối" sơ đồ quy trình, khiến nó khó đọc hơn đối với các bên liên quan (ví dụ: nhà phân tích nghiệp vụ). Task Listener giúp chúng ta ẩn đi các chi tiết kỹ thuật này, gắn chúng trực tiếp vào task mà chúng liên quan, giữ cho mô hình quy trình trong sáng và đúng với mục đích ban đầu của BPMN: là cầu nối giữa nghiệp vụ và IT.


2. Vòng đời của User Task và các sự kiện

Một User Task trải qua nhiều trạng thái khác nhau từ khi được tạo ra cho đến khi được hoàn thành. Camunda cho phép chúng ta can thiệp vào các thời điểm chuyển giao trạng thái này thông qua các Event Type (Loại sự kiện).

Camunda Task Listener Event Type Selection
Giao diện cấu hình Task Listener trong Camunda Modeler, hiển thị các loại sự kiện mà bạn có thể "lắng nghe".

Các sự kiện chính trong vòng đời của một User Task bao gồm:

  • create: Sự kiện này được kích hoạt khi một thể hiện của task được tạo ra và lưu vào cơ sở dữ liệu. Tại thời điểm này, task đã tồn tại nhưng thường chưa được gán cho ai (trừ khi được cấu hình sẵn). Đây là thời điểm lý tưởng để khởi tạo dữ liệu hoặc chuẩn bị thông tin cho task.
  • assignment: Kích hoạt khi thuộc tính assignee của task được thiết lập hoặc thay đổi. Điều này xảy ra khi một người dùng "claim" một task từ một group, hoặc khi task được gán trực tiếp cho một người dùng cụ thể. Đây là sự kiện hoàn hảo để gửi thông báo cho người được giao việc.
  • complete: Kích hoạt ngay trước khi task được hoàn thành. Tại đây, bạn có thể truy cập tất cả các biến mà người dùng đã nhập vào form. Đây là nơi thích hợp để thực hiện các logic xác thực (validation) phức tạp, xử lý dữ liệu trước khi lưu, hoặc gọi các hệ thống bên ngoài.
  • delete: Kích hoạt khi task bị xóa, thường là do process instance chứa nó bị hủy bỏ. Sự kiện này ít được sử dụng hơn nhưng hữu ích cho các tác vụ dọn dẹp.

Ngoài ra, còn có các sự kiện đặc biệt khác như timeout, cho phép bạn kích hoạt một listener nếu một task không được hoàn thành trong một khoảng thời gian nhất định.

Camunda User Task with Timeout Task Listener
Một ví dụ về Task Listener được kích hoạt bởi sự kiện 'Timeout'. Nếu task 'Pick items' không hoàn thành sau 1 giờ, listener sẽ thực thi lớp Java 'ReassignListener', có thể để gán lại task cho người khác.

3. Implement Task Listener trong Spring Boot

Trong môi trường Spring Boot, cách tốt nhất để implement một Task Listener là tạo một Spring Bean. Điều này cho phép bạn tận dụng cơ chế Dependency Injection để đưa các service khác (ví dụ: RestTemplate để gọi API, hoặc các Repository của bạn) vào trong listener một cách dễ dàng.

Để tạo một Task Listener, bạn cần:

  1. Tạo một class Java.
  2. Đánh dấu nó là một Spring bean bằng annotation @Component.
  3. Implement interface org.camunda.bpm.engine.delegate.TaskListener.
  4. Ghi đè (override) phương thức notify(DelegateTask delegateTask).

Phương thức notify là nơi bạn sẽ viết logic của mình. Tham số delegateTask là một đối tượng cực kỳ quan trọng, nó cho phép bạn:

  • Lấy thông tin về task: delegateTask.getId(), delegateTask.getName(), delegateTask.getAssignee().
  • Truy cập các biến quy trình (process variables): delegateTask.getVariable("variableName"), delegateTask.setVariable("variableName", value).
  • Tương tác với các service của Camunda engine.

How to send an email when a usertask is assigned

Bài viết chúng ta đã xem ở trên cung cấp một ví dụ code Java hoàn chỉnh cho một Task Listener. Hãy cùng phân tích nó.

Hãy xem kỹ đoạn code trong phần thứ hai của bài viết. Chú ý đến cách class TaskAssignmentListener implement interface TaskListener và cách phương thức notify sử dụng đối tượng delegateTask để lấy thông tin người được gán (assignee) và ID của task.

Ví dụ trong bài viết sử dụng một thư viện để gửi email thực sự. Trong bài thực hành của chúng ta, để đơn giản hóa, chúng ta sẽ chỉ ghi một thông báo ra console log để xác nhận rằng listener đã được kích hoạt thành công.



4. Thực hành: Xây dựng Listener thông báo

Bây giờ, hãy áp dụng những gì đã học để tạo một listener có chức năng ghi log mỗi khi một task được gán cho người dùng trong dự án Spring Boot của bạn.

  1. Tạo class Listener:
    Trong package của ứng dụng, tạo một class Java mới tên là AssignmentNotificationListener.

  2. Implement code:
    Chỉnh sửa class của bạn với nội dung sau. Chú ý annotation @Component và việc implement interface TaskListener.

    package com.example.camunda.yourpackage; // Thay thế bằng package của bạn
    
    import org.camunda.bpm.engine.delegate.DelegateTask;
    import org.camunda.bpm.engine.delegate.TaskListener;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    
    @Component("assignmentNotificationListener") // Tên bean để tham chiếu từ BPMN
    public class AssignmentNotificationListener implements TaskListener {
    
        private static final Logger LOGGER = LoggerFactory.getLogger(AssignmentNotificationListener.class);
    
        @Override
        public void notify(DelegateTask delegateTask) {
            String assignee = delegateTask.getAssignee();
            String taskId = delegateTask.getId();
            String taskName = delegateTask.getName();
    
            if (assignee != null) {
                LOGGER.info("✅ Task Assigned! Task '{}' (ID: {}) has been assigned to user '{}'.", taskName, taskId, assignee);
            }
        }
    }
    

    Lưu ý:

    • @Component("assignmentNotificationListener"): Chúng ta đặt tên cho Spring bean này là assignmentNotificationListener. Tên này sẽ được dùng trong file BPMN để tham chiếu đến listener.
    • Chúng ta sử dụng SLF4J Logger để ghi log, đây là một thực hành tốt trong các ứng dụng Java.

5. Cấu hình Listener trong Camunda Modeler

Sau khi đã có code Java, bước cuối cùng là "bảo" cho Camunda Modeler biết phải sử dụng nó khi nào và ở đâu.

  1. Mở file BPMN của bạn trong Camunda Modeler.

  2. Chọn User Task mà bạn muốn gắn listener (ví dụ: task "Submit Leave Request").

  3. Trong Properties Panel ở bên phải, chọn tab Listeners.

  4. Nhấn vào nút + để thêm một listener mới.

  5. Cấu hình các thuộc tính sau:

    • Event Type: Chọn assignment.
    • Listener Type: Chọn Delegate Expression. Đây là lựa chọn tốt nhất khi dùng Spring, vì nó cho phép Camunda sử dụng Spring bean mà chúng ta đã tạo, thay vì tự tạo một instance mới.
    • Delegate Expression: Nhập vào ${assignmentNotificationListener}. Cú pháp ${...} là Expression Language (EL), và assignmentNotificationListener chính là tên bean bạn đã định nghĩa trong annotation @Component.

    Phần cấu hình của bạn sẽ trông tương tự như hình ảnh minh họa trong bài viết How to send an email when a usertask is assigned (phần "assign the class as a task listener").

  6. Lưu file BPMN.


6. Deploy và Kiểm tra

Bây giờ là lúc kiểm tra thành quả!

  1. Khởi động lại ứng dụng Spring Boot của bạn để deploy phiên bản quy trình mới.
  2. Mở Camunda Tasklist (http://localhost:8080/camunda/app/tasklist/).
  3. Start a process cho quy trình của bạn.
  4. Nếu task của bạn được tạo ra mà chưa gán cho ai, hãy vào task đó và nhấn nút Claim. Hành động "Claim" sẽ set giá trị cho trường assignee và do đó sẽ kích hoạt sự kiện assignment.
  5. Kiểm tra Console Log của ứng dụng Spring Boot. Bạn sẽ thấy một dòng log tương tự như sau:
    ✅ Task Assigned! Task 'Submit Leave Request' (ID: a1b2c3d4-...) has been assigned to user 'demo'.
    

Nếu thấy dòng log này, bạn đã thành công! Bạn vừa implement thành công một Task Listener để thực thi logic tùy chỉnh trong vòng đời của một User Task.



Tổng kết

Trong bài học này, chúng ta đã khám phá một khái niệm rất mạnh mẽ trong Camunda là Task Listeners. Chúng cho phép bạn thực thi code Java tùy chỉnh tại các điểm then chốt trong vòng đời của một User Task, giúp mở rộng chức năng của quy trình mà không làm phức tạp hóa sơ đồ BPMN.

Những điểm chính cần nhớ:

  • Task Listeners được kích hoạt bởi các sự kiện như create, assignment, complete, và delete.
  • Chúng giúp tách biệt logic kỹ thuật ra khỏi luồng nghiệp vụ chính, giữ cho mô hình quy trình gọn gàng.
  • Trong Spring Boot, cách tốt nhất để implement listener là tạo một Spring Bean (@Component) implement interface TaskListener.
  • Sử dụng Delegate Expression (${myBeanName}) trong Camunda Modeler để liên kết User Task với Spring bean của bạn.

Xem trước Module tiếp theo

Bạn đã đi qua một chặng đường dài, từ việc mô hình hóa quy trình, tích hợp với Spring Boot, quản lý dữ liệu, điều khiển luồng, xây dựng giao diện và giờ là thêm logic tùy chỉnh với listeners. Quy trình của bạn giờ đây đã khá hoàn chỉnh.

Bước tiếp theo trong hành trình của một nhà phát triển là học cách quản lý, vận hành và bảo trì những quy trình này trong môi trường thực tế. Trong module tiếp theo, "Vận hành, Giám sát và Bảo trì", chúng ta sẽ bắt đầu với một chủ đề thiết yếu: tìm hiểu cách Camunda quản lý các phiên bản khác nhau của một quy trình và làm thế nào để nâng cấp các quy trình đang chạy lên phiên bản mới một cách an toàn.