Tài liệu đào tạo nội bộ · Công ty TNHH Công nghệ PKH

Nghiệp vụ hệ thống PKHOffice

Tài liệu dành cho người phân tích nghiệp vụ (BA) mới nhận việc trên hệ thống Quản lý văn bản và điều hành. Không phải hướng dẫn bấm nút — phần đó nằm ở tài liệu hướng dẫn người dùng. Đây là mô hình nghiệp vụ, quy tắc, và những chỗ dễ hiểu sai.

Cách đọc tài liệu này

Mọi con số và mã trạng thái trong tài liệu đều lấy từ mã nguồn và từ cơ sở dữ liệu đang chạy, không phải từ tài liệu thiết kế. Chỗ nào chưa kiểm chứng được, tài liệu ghi rõ bằng nhãn chưa xác nhậnđừng coi những chỗ đó là chân lý, hãy đi hỏi nghiệp vụ.

PHẦN 1Hệ thống làm gì

Bối cảnh nghiệp vụ

PKHOffice giải quyết một việc: đưa quy trình ký văn bản hành chính từ giấy lên máy. Trước đây một tờ trình phải in ra, cầm đi xin ký lần lượt từng cấp, rồi mang xuống văn thư vào sổ và phát hành. Hệ thống làm đúng chuỗi đó nhưng bằng tệp PDF và chữ ký đóng lên tệp.

Bốn nhóm nghiệp vụ, theo đúng thứ tự quan trọng:

NhómNội dungSố endpointMức độ hoàn thiện
Văn bản ký điện tửTrình ký → ký duyệt → ban hành. Đây là lõi.40Đầy đủ, dùng hàng ngày
Quản lý văn bảnVăn bản đến, văn bản đi, hồ sơ, thư viện38Đọc tốt, phần ghi còn mỏng
Nhiệm vụ & công việcGiao việc, theo dõi, đánh giá hiệu quả35Nhiều màn chưa có giao diện
Lịch họpLịch cá nhân, lịch đơn vị, phòng họp, biên bản20Chỉ xem được, chưa tạo được

Nghiệp vụ văn bản hành chính Việt Nam chịu sự điều chỉnh của Nghị định 30/2020/NĐ-CP về công tác văn thư. Nghị định này quy định thể thức và kỹ thuật trình bày văn bản: quốc hiệu, tiêu ngữ, số và ký hiệu, nơi nhận, thể thức đề ký. BA làm việc trên hệ này cần biết NĐ30 tồn tại, vì nó là nguồn của nhiều quy tắc trong hệ thống — kể cả bộ luật kiểm tra thể thức bằng AI.

PHẦN 2Kiến trúc — điều quan trọng nhất phải biết

Nếu chỉ đọc được một phần của tài liệu này, đọc phần này

Ba hệ thống dùng CHUNG một cơ sở dữ liệu

Cơ sở dữ liệu vpdt đang được ba hệ thống cùng đọc và cùng ghi:

  • PiOffice web — hệ cũ, Java/ZK, cổng 8001
  • PiOffice service — hệ cũ, tầng nghiệp vụ, cổng 8005
  • PKHOffice — hệ mới, NestJS + React, cổng 3005/3002

Hệ quả trực tiếp tới công việc của BA: không được đề xuất đổi cấu trúc bảng, đổi ý nghĩa cột, hay đổi cách băm mật khẩu. Một thay đổi như vậy làm hỏng hai hệ Java đang chạy sản xuất mà không ai kịp phát hiện.

Vì sao lại như vậy: hệ mới được dựng lại từ hệ cũ theo cách chạy song song — người dùng chuyển dần sang hệ mới, trong khi hệ cũ vẫn phục vụ những nghiệp vụ chưa dựng lại xong. Hai hệ phải thấy cùng một dữ liệu, nếu không người dùng sẽ thấy hai sự thật khác nhau.

Mã nguồn hệ cũ không có sẵn — nó được dịch ngược từ tệp WAR đang chạy và nằm ở repo vps-old-refactor. Khi cần biết "hệ cũ làm thế nào", đọc mã dịch ngược đó là cách đáng tin nhất. Tài liệu thiết kế gốc không tồn tại.

Việc BA sẽ phải làm thường xuyên

Khi nghiệp vụ nói "hệ cũ nó làm thế này", hãy kiểm lại trong mã dịch ngược trước khi ghi vào yêu cầu. Trong quá trình dựng lại đã nhiều lần phát hiện hành vi thật của hệ cũ khác với điều mọi người tưởng.

PHẦN 3Từ vựng nghiệp vụ

Nói đúng từ thì mới hỏi đúng câu

Trình ký
Gửi văn bản lên cho cấp trên ký. Là hành động của người soạn.
t.STATE 0 → 1
Ký duyệt
Ký chính thức, thể hiện sự đồng ý về nội dung.
SIGNATURE_TYPE = 3
Ký nháy
Ký xác nhận đã soát, đặt ở lề văn bản. Làm trước khi lãnh đạo ký chính thức.
SIGNATURE_TYPE = 2
Ban hành
Phát hành văn bản ra ngoài: cấp số chính thức và gửi tới nơi nhận. Việc của văn thư.
t.STATE 3 → 4
Thu hồi
Rút lại văn bản đã ban hành vì phát hiện sai sót.
t.STATE 4 → …
Thể thức
Loại văn bản: Tờ trình, Quyết định, Công văn, Thông báo… Theo NĐ30 có 28 loại.
document_type
Lĩnh vực
Mảng công việc để phân loại và tra cứu. Đặc thù từng doanh nghiệp.
area
Độ mật
Bình thường / Mật / Tối mật / Tuyệt mật. Doanh nghiệp thường chỉ dùng hai mức đầu.
security_type
Độ khẩn
Bình thường / Khẩn / Thượng khẩn / Hoả tốc.
cv_priority
Cấp ký
Thứ tự ký. Cấp 1 ký trước; cùng cấp thì ký song song.
tp.SIGN_LEVEL
Quốc hiệu · Tiêu ngữ
Hai dòng đầu văn bản hành chính: tên nước và "Độc lập – Tự do – Hạnh phúc". NĐ30 bắt buộc.
luật kiểm thể thức
Nơi nhận
Khối cuối văn bản liệt kê đơn vị/cá nhân được gửi tới.
text_sign_next

PHẦN 4Vòng đời văn bản ký điện tử

Cột text.STATE — trạng thái của cả văn bản

Đường đi thuận lợi nhất của một văn bản:

STATE 0Nháp1.182 văn bản
STATE 1Đang ký644
STATE 3Chờ ban hành5.173
STATE 4Đã ban hành30.939

Số liệu đếm từ cơ sở dữ liệu đang chạy, tháng 9/2026, đã loại văn bản bị xoá. STATE 4 chiếm đa số — phần lớn dữ liệu là văn bản đã hoàn tất.

Các nhánh rẽ khỏi đường thuận lợi:

STATE 2Bị từ chối5.585
·
STATE 5Ký nháy156
·
STATE 7Nháy xong54
·
STATE 6Đã huỷ8.853
·
STATE 27Huỷ sau duyệt130
·
STATE −1Ký song song96
Điều BA cần chú ý ở đây

8.853 văn bản ở trạng thái "Đã huỷ" — nhiều thứ hai sau "Đã ban hành", và nhiều hơn cả số bị từ chối. Đây là con số đáng đi hỏi nghiệp vụ: vì sao nhiều văn bản bị huỷ như vậy, huỷ ở bước nào, và ai huỷ. Có thể là dấu hiệu của một vấn đề quy trình mà hệ thống đang che đi.

Ngoài ra STATE 27 "Huỷ sau duyệt" là một mã lạ — không nằm trong dãy 0–7. Gần như chắc chắn là mã được thêm về sau cho một tình huống nghiệp vụ cụ thể. chưa xác nhận

PHẦN 5Luồng ký — mô hình quan trọng nhất

Bảng text_process — mỗi dòng là một lượt ký của một người

Đây là chỗ BA hay hiểu sai nhất, nên đọc kỹ. Một văn bản có nhiều dòng trong text_process, mỗi dòng là "người này phải ký văn bản này, ở cấp này, theo kiểu ký này". Ba cột quyết định nghiệp vụ:

CộtÝ nghĩa nghiệp vụ
EMP_VHR_IDAi phải ký
SIGN_LEVELCấp ký — thứ tự. Cấp 1 ký trước. Hai dòng cùng cấp ⇒ ký song song.
SIGNATURE_TYPEKiểu ký: ký duyệt, ký nháy, hay lượt xét duyệt
STATELượt ký này đã xử lý chưa

SIGNATURE_TYPE — kiểu ký

NghĩaSố dòng thậtĐộ tin cậy
3Ký duyệt — ký chính thức170.318đã kiểm
2Ký nháy6.234đã kiểm
1Lượt xét duyệt / trình lên24.008chưa xác nhận

Mã 1 chỉ xuất hiện trong báo cáo thời gian ký của hệ cũ, đi kèm cột REVIEW_NEW_LEVEL. Phải hỏi nghiệp vụ để chốt nghĩa của mã này trước khi viết bất cứ yêu cầu nào liên quan tới nó.

text_process.STATE — trạng thái của một lượt ký

NghĩaSố dòngNguồn xác định
0Chưa xử lý — đang chờ người này ký35.994đã kiểm mọi truy vấn hàng chờ đều lọc STATE = 0
4Đã ký154.487đã kiểm hàm ký duyệt gán giá trị này
2Từ chối5.797đã kiểm hàm từ chối gán giá trị này
3Bị rút lại1.306suy ra từ hàm rollBackText() của hệ cũ
5Chuyển lại người ký trước130suy ra từ hàm transferToPreSigner()
11.390chưa rõ
61.462chưa rõ
Lỗ hổng trong hệ mới mà BA nên biết

Hệ mới chỉ dịch được 3 trong 7 mã trạng thái này thành chữ (0, 2, 4). Bốn mã còn lại — tổng 4.288 dòng dữ liệu thật — sẽ hiện ra màn hình dưới dạng số trơ, người dùng không hiểu gì. Đây là việc cần làm, và việc đầu tiên là đi chốt nghĩa của các mã 1, 3, 5, 6 với nghiệp vụ.

Hai bảng, hai vai trò khác nhau

Có hai bảng dễ bị lẫn, phân biệt được là hiểu được luồng ký:

BảngTrả lời câu hỏiKhi nào có dòng
text_process"Những ai phải ký văn bản này?"Ngay khi lưu nháp — kể cả khi chưa trình ký
text_sign_next"Ai là người ký kế tiếp, tức văn bản đang nằm ở hàng chờ của ai?"Chỉ khi thật sự trình ký

text_sign_next là thứ kích hoạt luồng ký. Có dòng text_process mà không có dòng text_sign_next nghĩa là "đã khai người ký nhưng chưa gửi đi".

PHẦN 6Vai trò và quyền

Bảng SYS_ROLEUSER_ROLE

Hệ thống có 23 vai trò khai trong bảng SYS_ROLE. Nhưng mã nguồn hệ mới chỉ thật sự kiểm 6 mã. Đây là khoảng cách BA cần nắm:

TênHệ mới có kiểm?Kiểm ở đâu
ADMINQuản trị hệ thốngCấu hình AI, chi tiêu AI, nhật ký hoạt động, ban hành
VTVăn thưBan hành và thu hồi văn bản
LDDVLãnh đạo đơn vị / Quản lýLọc phạm vi đơn vị trong truy vấn
TTDVThường trực đơn vị / Lãnh đạoLọc phạm vi đơn vị
TLTrợ lýLọc phạm vi đơn vị
LDCT, VTDVLãnh đạo công ty, Văn thư đơn vịChỉ trong truy vấn lọc đơn vị
17 vai trò còn lạiNV, LT, QLLH, UQGD, TNVB, DDVB, KDNN…khôngCó trong danh mục nhưng mã nguồn không kiểm tới
Hệ quả nghiệp vụ

Gán cho người dùng một vai trò ngoài 6 mã trên thì không thay đổi gì về quyền trong hệ mới. Nếu nghiệp vụ nói "cho vai trò X quyền làm Y", BA phải hỏi tiếp: hiện tại hệ có kiểm vai trò đó không, hay phải làm thêm.

Một điểm về mô hình: vai trò luôn gắn với một đơn vị. Bảng USER_ROLE có cả SYS_ROLE_IDSYS_ORGANIZATION_ID, nghĩa là một người có thể là Trưởng phòng ở đơn vị A và Nhân viên ở đơn vị B. Cột IS_DEFAULT đánh dấu vai trò hiện lên khi vừa đăng nhập.

PHẦN 7Mô hình dữ liệu — các bảng cần biết

Cơ sở dữ liệu vpdt có 328 bảng. Đây là 12 bảng BA sẽ gặp hàng ngày.

BảngChứa gìSố dòng
textVăn bản trong luồng ký điện tử~53.000
text_processTừng lượt ký của từng người~200.000
text_sign_nextNgười ký kế tiếp — hàng chờ thật sự
text_attachNối văn bản ↔ File ký chính
text_attach_otherNối văn bản ↔ tài liệu kèm theo
ATTACHSiêu dữ liệu tệp đính kèm của hệ cũ
DOCUMENTVăn bản đến / đi (nghiệp vụ khác với text)38.942
DOCUMENT_IN_STAFFAi nhận văn bản đến nào700.946
STAFFTài khoản đăng nhập2.035
vhr_employeeHồ sơ nhân sự (hệ nhân sự riêng)2.033
vhr_orgCây tổ chức164
USER_ROLEGán vai trò theo đơn vị2.354
Bẫy đặt tên — nhớ kỹ

Bảng text và bảng DOCUMENThai nghiệp vụ khác nhau, không phải hai phiên bản của một thứ:

  • text = văn bản nội bộ đang đi qua luồng ký
  • DOCUMENT = văn bản đến và đi (công văn vào/ra khỏi công ty)

Một văn bản ký xong và ban hành sẽ sinh ra một dòng trong DOCUMENT. Viết yêu cầu mà lẫn hai bảng này là nguồn của lỗi rất khó tìm.

Hai điểm nữa về mô hình nhân sự, cùng là chỗ dễ sai:

  • Bảng STAFF không có cột EMPLOYEE_ID. Nối sang vhr_employee phải qua STAFF.LOGINNAME = vhr_employee.USER_NAME.
  • Nối theo USER_NAME không bảo đảm duy nhất: trong dữ liệu thật có một LOGINNAME ứng với 49 dòng vhr_employee. Khi cần cập nhật đúng một người, nối theo EMPLOYEE_ID.

PHẦN 8Sáu cái bẫy đã sập trong quá trình dựng lại

Mỗi cái đều là lỗi thật, mất thời gian thật

1. Lưu nháp mà người khác thấy trong hàng chờ ký

Khi cho phép lưu nháp cũng ghi người ký vào text_process, văn bản nháp lập tức xuất hiện trong màn "Ký duyệt" của người khác — vì truy vấn hàng chờ không lọc trạng thái nháp. Bài học nghiệp vụ: nháp là tài liệu riêng của người soạn, tuyệt đối không được lộ ra ngoài dù đã khai người ký.

2. Nhãn độ khẩn viết cứng, lệch với cơ sở dữ liệu

API trả danh sách độ khẩn viết cứng trong mã: mã 3 ghi "Rất khẩn", mã 4 ghi "Hoả tốc". Trong khi cơ sở dữ liệu ghi mã 3 = "Hỏa tốc", mã 4 = "Thượng khẩn". Người dùng chọn "Hoả tốc" thì hệ lưu thành "Thượng khẩn". Bài học: danh mục phải đọc từ cơ sở dữ liệu, đừng viết cứng.

3. Tệp đính kèm hệ cũ được mã hoá

Toàn bộ khoảng 127.000 tệp đính kèm cũ được mã hoá DES trên đĩa. Đọc trực tiếp ra chỉ thấy dữ liệu rác. Điều này ảnh hưởng mọi yêu cầu liên quan tới tệp: xem trước, tìm kiếm toàn văn, trích xuất nội dung.

4. Cơ sở dữ liệu chạy theo giờ UTC

MySQL trong hệ đặt múi giờ UTC, còn nghiệp vụ tính theo giờ Việt Nam (+07). Mọi yêu cầu có chữ "trong ngày", "theo tháng", "hạn xử lý" đều phải nói rõ tính theo giờ nào. Một hạn mức "theo ngày" tính sai múi giờ sẽ reset lúc 7 giờ sáng thay vì nửa đêm.

5. Đổi mật khẩu phải ghi vào hai bảng

Mật khẩu nằm ở cả STAFFvhr_employee. Đổi một bảng thì người dùng vào được hệ mới nhưng không vào được hệ cũ. Mật khẩu băm theo cách của hệ cũ: base64 của SHA-1, không phải hex.

6. Giao diện từng in tên bảng cơ sở dữ liệu ra cho người dùng

Hộp thoại ký duyệt từng hiện câu "Hệ thống sẽ cập nhật text_process…" cho người dùng văn phòng đọc. Bài học cho BA: khi viết nội dung hiển thị trong yêu cầu, luôn viết bằng ngôn ngữ nghiệp vụ, đừng để lọt tên bảng, tên cột, mã trạng thái ra màn hình.

PHẦN 9Phạm vi còn thiếu

BA cần biết ranh giới hiện tại của hệ thống trước khi hứa với khách

Hệ có 207 endpoint. Phân loại theo mức độ thật sự dùng được:

Số lượngNhómNghĩa với BA
96Có giao diện, đang dùng hàng ngàyĐã được kiểm chứng qua sử dụng thật. Tin được.
89API chạy được nhưng chưa giao diện nào gọiBiên dịch được nhưng chưa ai dùng thật ⇒ chưa kiểm chứng. Cần test kỹ trước khi dựa vào.
22Bản rỗng — gọi vào luôn trả 404Chưa làm. Không hứa với khách.

Các nghiệp vụ chưa có ở cả hai đầu (không có API và cũng không có giao diện):

Nghiệp vụTình trạng
Tạo / sửa / xoá cuộc họpChỉ xem được lịch
Tạo / sửa / xoá thư mục hồ sơChưa có
Chữ ký số USB token / HSMChưa có — hiện ký bằng ảnh chữ ký
Bình luận, đánh dấu trên văn bản đếnChưa có
Văn bản nội bộ (/api/text)Có API đủ bộ tạo/sửa/xoá nhưng chưa từng được gọi lần nào
Ứng dụng di độngĐang làm — tài liệu API đã sẵn sàng cho 96 endpoint
Điểm yếu nghiệp vụ nên ưu tiên

Khi văn thư ban hành văn bản, ô chọn nơi nhận yêu cầu nhập mã số staff ID cách nhau bằng dấu phẩy (ví dụ 6076, 5622, 6510), chưa có danh sách chọn người. Mã số này còn không phải mã nhân viên người dùng thấy ở màn hình khác. Văn thư dùng chức năng này hàng ngày, nên đây là việc nên đưa lên đầu hàng đợi.

Đối chiếu: màn "Chuyển văn bản" của văn bản đến đã có danh sách chọn trực quan — tức là hệ đã có sẵn thành phần giao diện để dùng lại.

Về tính năng AI

Hệ có hai chức năng AI, cả hai chỉ mang tính tham khảo, không chặn quy trình ký:

  • Tóm tắt văn bản — gọi nhà cung cấp AI, tốn phí mỗi lượt, có lưu lại để lần sau không gọi lại. Đo thực tế: khoảng 14.800 token, 0,018 USD, 10–15 giây một văn bản.
  • Kiểm tra thể thức theo NĐ30 — chạy luật cục bộ, không tốn phí.

Điều BA phải biết: trong bộ luật kiểm thể thức, 9 trong 12 luật đang bị tắt vì đo được tỉ lệ báo nhầm trên 5% khi chạy với văn bản đã ban hành thật. Nghĩa là chức năng này hiện chỉ kiểm được một phần nhỏ. Đừng trình bày với khách như một công cụ kiểm thể thức hoàn chỉnh.

PHẦN 10Tự kiểm chứng — kỹ năng quan trọng nhất

Đừng tin tài liệu, kể cả tài liệu này. Hãy kiểm.

Tài liệu thiết kế gốc của hệ cũ không tồn tại. Tài liệu này viết dựa trên mã nguồn và dữ liệu đang chạy, nên có thể lạc hậu ngay khi mã nguồn đổi. Ba nguồn đáng tin, theo thứ tự:

Ưu tiênNguồnDùng để trả lời
1Dữ liệu đang chạy trong CSDL vpdt"Thực tế nghiệp vụ đang diễn ra thế nào?"
2Mã nguồn hệ mới (vps-new)"Hệ mới đang xử lý thế nào?"
3Mã dịch ngược hệ cũ (vps-old-refactor)"Hệ cũ làm thế nào, vì sao lại có quy tắc này?"

Ba câu truy vấn nên biết

Đếm văn bản theo trạng thái — kiểm ngay số liệu ở Phần 4:

SELECT STATE, COUNT(*) FROM text
WHERE IS_DELETED IS NULL OR IS_DELETED = 0
GROUP BY STATE ORDER BY STATE;

Xem luồng ký thật của một văn bản — hiểu Phần 5 bằng ví dụ cụ thể:

SELECT SIGN_LEVEL, SIGNATURE_TYPE, STATE, EMP_VHR_ID, SEND_DATE, ACTION_DATE
FROM text_process WHERE TEXT_ID = <id>
ORDER BY SIGN_LEVEL, SIGNATURE_TYPE;

Xem một tính năng có thật sự được dùng hay không:

SELECT COUNT(*) FROM <tên_bảng>;

Bảng rỗng nghĩa là nghiệp vụ đó chưa ai dùng — dù giao diện và API có sẵn. Đây là cách nhanh nhất để biết một tính năng là thật hay chỉ tồn tại trên giấy.

Tài liệu kỹ thuật kèm theo

Tài liệuNội dung
docs/api-swagger.mdTài liệu API, danh sách endpoint nào là bản rỗng
/api/docsTrang Swagger chạy trực tiếp, thử gọi API được
docs/huong-dan/Hướng dẫn người dùng theo vai trò — đọc để biết người dùng thấy gì
docs/e2e-live-*.mdKết quả kiểm thử tự động và giới hạn phủ
docs/ai-review-false-positive-*.mdSố đo tỉ lệ báo nhầm của bộ luật kiểm thể thức
Việc đầu tiên nên làm sau khi đọc tài liệu này
  1. Đăng nhập hệ thống bằng tài khoản thật, tự trình ký một văn bản từ đầu tới cuối.
  2. Chạy câu truy vấn đếm trạng thái ở trên, đối chiếu với Phần 4 xem có khớp không.
  3. Mở /api/docs, thử gọi GET /api/text-sign/trinh-ky.
  4. Lập danh sách câu hỏi cho nghiệp vụ, bắt đầu từ ba chỗ tài liệu này ghi chưa xác nhận: nghĩa của SIGNATURE_TYPE = 1, nghĩa của text_process.STATE mã 1 và 6, và vì sao có 8.853 văn bản ở trạng thái "Đã huỷ".