Hệ thống đánh giá Eval360 hoạt động như thế nào ?

Ở thời điểm viết bài này là tháng 12/2023, chỉ còn ít thời gian nữa là một kỳ đánh giá nhân sự nữa lại đến. Mình viết bài blog này để chia sẻ các thông tin về hệ thống Eval360 cho những ai quan tâm hoặc chưa biết về nó.

Bài viết gồm 4 phần chính:

  • Tại sao cần nó
  • Công nghệ sử dụng
  • Nghiệp vụ
  • 1 vài bài học nhỏ

Mình học được nhiều kiến thức và trải nghiệm nhiều vai trò khi làm dự án vì cộng đồng này: Dev kiêm Tester kiêm luôn PO, BA

I. Tại sao lại cần Eval360 System

Các năm từ 2018 trở về trước, đánh giá nhân sự được thực hiện thông qua việc submit các Google Form. Ban đầu, số lượng nhân sự của công ty chưa nhiều nên việc sử dụng Google Form rất phù hợp. Vấn đề nảy sinh khi số lượng nhân sự ngày càng tăng:

  • BO & MNG tốn rất nhiều thời gian để tổng hợp & gửi kết quả đến member
  • Member gặp khó khăn khi lựa chọn các thành viên để đánh giá
  • Khó có thể mở rộng tính năng trong tương lai

=> Từ đây, phát sinh nhu cầu cần thiết phải có 1 hệ thống đánh giá riêng thay thế Google Form.

Dự án Eval360 đã ra đời như thế, đem lại nhiều tác dụng:

  • Việc tổng hợp dữ liệu diễn ra rất nhanh chóng, giảm tải cho BO. Ngày trước, phải mất hàng tháng để tính và gửi kết quả. Ngày nay, hệ thống giúp việc này có thể diễn ra chỉ với đơn vị phút.
  • MNG nhìn thấy điểm đánh giá của từng member rõ ràng. Hệ thống hiển thị trực quan thông tin các hạng mục điểm từ đó MNG có cái nhìn tổng quan để điền đánh giá.
  • Member tạo và chỉnh sửa đánh giá dễ dàng hơn
  • Tích hợp thêm nhiều tính năng khác

II. Công nghệ sử dụng

Trải qua vài phiên bản, công nghệ của dự án thay đổi kha khá:

  • Backend: Play Fw (Scala) -> Micronaut (Java) -> ActiveJ (Java)
  • Frontend: ReactJs -> ReactJs Typescript -> QwikJs

Bản thân mình khi làm sản phẩm luôn hướng đến các tiêu chí: Đúng - Ổn Định - Nhanh

  • Backend: Sử dụng ActiveJ và 1 số công nghệ khác của Java. Kèm với đó là ứng dụng tư tưởng DDD để xử lý phần business logic. Tại sao lại là ActiveJ ? Nó nhanh, nhẹ, đầy đủ các thành phần cần thiết từ đó tránh sử dụng quá nhiều các 3rd-party lib khác. Tham khảo benchmark tại đây
  • Frontend: QwikJs là một javascript framework kiểu mới, cho phép tạo ra các ứng dựng web có khả năng tải tức thì, ở bất kì kích thước hoặc độ phức tạp. Đại khái là bạn ít cần hoặc không cần quan tâm về việc tối ưu performance khi app mở rộng. QwikJs rất thân thiện do syntax khá tương đồng với ReactJs. Ta có thể sử dụng đồng thời cả 2 cơ chế: SSR  và CSR

eval360-system

III. Nghiệp vụ

Đối tượng sử dụng hệ thống là toàn bộ nhân viên của Flinters VN

Đối tượng thuộc diện xét duyệt đánh giá có hiệu lực cần thỏa mãn điều kiện: nhân viên chính thức có thời gian làm việc từ 3 tháng trở lên. Các member khác như nhân viên thử việc hoặc nhân viên chính thức làm ít hơn 3 tháng, vẫn có thể tham gia và nhận đánh giá nhưng sẽ không thuộc đối tượng được xét tăng lương.

Hệ thống có các luồng nghiệp vụ chính như sau:

  • Luồng xử lý form đánh giá của member (EForm Flow)
  • Luồng xử lý form đánh giá của manager (MForm Flow)
  • Luồng tổng hợp kết quả (Assessment Flow)

Tổng quát

EForm flow

eform-flow

MForm flow

Assessment flow

Giải thích các loại điểm được tính toán:

  • Me Points: Điểm đến từ các đánh giá của người khác cho mình
  • Similar Points: Điểm  của tất cả những người cùng rank với mình trong công ty (bao gồm cả chính mình)
  • Peer Points: Điểm đến từ các đánh giá của người khác (có cùng rank hoặc thấp hơn) cho mình
  • Superior Points: Điểm từ các đánh giá của người khác (có rank cao hơn) cho mình
  • Self Point: Điểm tự đánh giá bản thân
  • All Rates: Điểm của toàn bộ nhân viên công ty thuộc diện được xét duyệt

Cơ chế tính điểm theo công thức tính trung bình: Tổng số điểm / Tổng số câu
Công thức này áp dụng cho từng tiêu chí, như vậy có 6 tiêu chí thì có 6 điểm trung bình cho tất cả các loại điểm được liệt kê bên trên

VD1: Bạn nhận được 2 đánh giá lần lượt như sau:
Đánh giá 1: C1:6, C2:6, C3:5, C5:5, C5:5, C6:6
Đánh giá 2: C1:5, C2:4, C3:3, C4:5, C5:2, C6:6
=> AVG of C1 = (6+5)/2 = 5.5
=> AVG of C2 = (6+4)/2 = 5
Tương tự cho các tiêu chí còn lại.

Với những tiêu chí lựa chọn là Unobserved thì không được tính điểm
Phần tính điểm từng loại thực tế impl code thì phức tạp hơn, nhưng cơ bản nội dung như trên.

Còn 1 vài phần khác như xử lý file output, gửi email, tính budget từng phòng ban không tiện nêu ở đây.

IV. 1 vài bài học nhỏ

Mình xin liệt kê 1 số vấn đề nho nhỏ học được:

1. Xử lý tính toán với số thập phân:
Phép toán đơn giản như sau: 0.5 + 0.5 -> Nếu sử dụng biểu thức + thông thường thì đôi khi kết quả là 0.9
=> Giải pháp: Sử dụng class BigDecimal để tính kết quả chính xác

2. So sánh thứ bậc rank của manager
Trong vấn đề phân quyền, 1 assessment có thể được xem & chỉnh sửa bởi nhiều cấp độ manager rank. Cấp độ thấp không thể sửa được đánh giá của cấp độ cao hơn. Lúc đầu, nghĩ cứ if…else theo loại rank nhưng như vậy sẽ tạo thành khá nhiều tổ hợp chập 2 các loại rank của manager, chưa kể có những anh/chị cùng role MNG nhưng lại khác rank level. vd: Cùng role MNG có thể là rank A hoặc L hoặc B.
=> Giải pháp: Đánh trọng số cho từng loại rank, sau đó làm 1 hàm compare trọng số.

public enum Rank {
    M(1), I(2), A(3), L(4), B(5), IF(Integer.MAX_VALUE);

    private final Integer weight;

    public boolean compare(Operator operator, Rank that) {
        return switch (operator) {
            case Gt -> this.weight > that.weight;
            case Ge -> this.weight >= that.weight;
            case Lt -> this.weight < that.weight;
            case Le -> this.weight <= that.weight;
            case Eq -> this == that;
        };
    }
}

Ở Scala, impl cái trên dễ và trực quan hơn nhiều so với Java. Dù sao vẫn ổn để xài

3. Xử lý tính toán các loại điểm

Ban đầu ở v1, mình không tham gia vào phần này, cách làm lúc ấy là cứ tạo 1 đánh giá (EForm) thì sẽ gọi luôn hàm tính các loại điểm. Cách làm này, khi chạy ở local rất OK lun, tính toán chính xác. Nhưng thực tế ở production thì phần tính toán lại bị sai. Do đồng thời có nhiều người cùng tạo/sửa đánh giá gây ra vấn đề race condition Nếu thiếu kinh nghiệm thì sẽ không nhìn ra trường hợp như này và không thể tái hiện được ở local.
=> Giải pháp ở đây là: Cần phải tách rời phần lưu form và phần tính điểm để chúng xử lý bất đồng bộ. Thêm nữa, phần tạo/chỉnh sửa form sẽ xảy ra thường xuyên với số lượng nhiều, trong khi phần tính điểm lại không nhất thiết phải chạy nhiều như thế, bên cạch đó cần đảm bảo thứ tự chạy.

  • Khi tạo/chỉnh sửa form xong -> Sẽ push event tới Message Bus -> End flow
  • Message Bus sẽ chịu trách nhiệm phân phối event đến phần tính điểm
  • Phần tính điểm sẽ được lập lịch chạy. Quá trình tính điểm sẽ diễn ra mỗi 2 phút kể từ khi nó nhận event đầu tiên. Các event sẽ được xử lý có thứ tự, đảm bảo việc tính toán chính xác

Như vậy, nhờ có Message Bus hệ thống hoạt động chính xác mà vẫn nhanh. Muốn có kết quả tổng hợp “real-time” hơn, đơn giản là thay đổi thời gian từ 2 phút về 1 con số thấp hơn. Ở đây, 2 phút là thời gian phù hợp.

Còn 1 số vấn đề khác cần giải quyết, bài viết đã dài nên xin phép dừng ở đây. Cảm ơn mọi người đã đọc đến đây. Hết!

Add a Comment

Scroll Up