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.
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óm | Nội dung | Số endpoint | Mứ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ản | Văn bản đến, văn bản đi, hồ sơ, thư viện | 38 | Đọc tốt, phần ghi còn mỏng |
| Nhiệm vụ & công việc | Giao việc, theo dõi, đánh giá hiệu quả | 35 | Nhiều màn chưa có giao diện |
| Lịch họp | Lịch cá nhân, lịch đơn vị, phòng họp, biên bản | 20 | Chỉ 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
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.
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
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:
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:
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_ID | Ai phải ký |
| SIGN_LEVEL | Cấp ký — thứ tự. Cấp 1 ký trước. Hai dòng cùng cấp ⇒ ký song song. |
| SIGNATURE_TYPE | Kiểu ký: ký duyệt, ký nháy, hay lượt xét duyệt |
| STATE | Lượt ký này đã xử lý chưa |
SIGNATURE_TYPE — kiểu ký
| Mã | Nghĩa | Số dòng thật | Độ tin cậy |
|---|---|---|---|
| 3 | Ký duyệt — ký chính thức | 170.318 | đã kiểm |
| 2 | Ký nháy | 6.234 | đã kiểm |
| 1 | Lượt xét duyệt / trình lên | 24.008 | chư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ý
| Mã | Nghĩa | Số dòng | Nguồn xác định |
|---|---|---|---|
| 0 | Chư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 |
| 2 | Từ chối | 5.797 | đã kiểm hàm từ chối gán giá trị này |
| 3 | Bị rút lại | 1.306 | suy ra từ hàm rollBackText() của hệ cũ |
| 5 | Chuyển lại người ký trước | 130 | suy ra từ hàm transferToPreSigner() |
| 1 | — | 1.390 | chưa rõ |
| 6 | — | 1.462 | chưa rõ |
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ảng | Trả lời câu hỏi | Khi 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_ROLE và USER_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:
| Mã | Tên | Hệ mới có kiểm? | Kiểm ở đâu |
|---|---|---|---|
| ADMIN | Quản trị hệ thống | có | Cấu hình AI, chi tiêu AI, nhật ký hoạt động, ban hành |
| VT | Văn thư | có | Ban hành và thu hồi văn bản |
| LDDV | Lãnh đạo đơn vị / Quản lý | có | Lọc phạm vi đơn vị trong truy vấn |
| TTDV | Thường trực đơn vị / Lãnh đạo | có | Lọc phạm vi đơn vị |
| TL | Trợ lý | có | Lọc phạm vi đơn vị |
| LDCT, VTDV | Lãnh đạo công ty, Văn thư đơn vị | có | Chỉ trong truy vấn lọc đơn vị |
| 17 vai trò còn lại | NV, LT, QLLH, UQGD, TNVB, DDVB, KDNN… | không | Có trong danh mục nhưng mã nguồn không kiểm tới |
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_ID và
SYS_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ảng | Chứa gì | Số dòng |
|---|---|---|
| text | Văn bản trong luồng ký điện tử | ~53.000 |
| text_process | Từng lượt ký của từng người | ~200.000 |
| text_sign_next | Người ký kế tiếp — hàng chờ thật sự | — |
| text_attach | Nối văn bản ↔ File ký chính | — |
| text_attach_other | Nối văn bản ↔ tài liệu kèm theo | — |
| ATTACH | Siêu dữ liệu tệp đính kèm của hệ cũ | — |
| DOCUMENT | Văn bản đến / đi (nghiệp vụ khác với text) | 38.942 |
| DOCUMENT_IN_STAFF | Ai nhận văn bản đến nào | 700.946 |
| STAFF | Tài khoản đăng nhập | 2.035 |
| vhr_employee | Hồ sơ nhân sự (hệ nhân sự riêng) | 2.033 |
| vhr_org | Cây tổ chức | 164 |
| USER_ROLE | Gán vai trò theo đơn vị | 2.354 |
Bảng text và bảng DOCUMENT là hai 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
STAFFkhông có cộtEMPLOYEE_ID. Nối sangvhr_employeephải quaSTAFF.LOGINNAME = vhr_employee.USER_NAME. - Nối theo
USER_NAMEkhông bảo đảm duy nhất: trong dữ liệu thật có mộtLOGINNAMEứng với 49 dòngvhr_employee. Khi cần cập nhật đúng một người, nối theoEMPLOYEE_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ả STAFF và vhr_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ượng | Nhóm | Nghĩa với BA |
|---|---|---|
| 96 | Có giao diện, đang dùng hàng ngày | Đã được kiểm chứng qua sử dụng thật. Tin được. |
| 89 | API chạy được nhưng chưa giao diện nào gọi | Biê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. |
| 22 | Bản rỗng — gọi vào luôn trả 404 | Chư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ọp | Chỉ xem được lịch |
| Tạo / sửa / xoá thư mục hồ sơ | Chưa có |
| Chữ ký số USB token / HSM | Chưa có — hiện ký bằng ảnh chữ ký |
| Bình luận, đánh dấu trên văn bản đến | Chư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 |
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ên | Nguồn | Dùng để trả lời |
|---|---|---|
| 1 | Dữ liệu đang chạy trong CSDL vpdt | "Thực tế nghiệp vụ đang diễn ra thế nào?" |
| 2 | Mã nguồn hệ mới (vps-new) | "Hệ mới đang xử lý thế nào?" |
| 3 | Mã 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ệu | Nội dung |
|---|---|
| docs/api-swagger.md | Tài liệu API, danh sách endpoint nào là bản rỗng |
| /api/docs | Trang 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-*.md | Kết quả kiểm thử tự động và giới hạn phủ |
| docs/ai-review-false-positive-*.md | Số đo tỉ lệ báo nhầm của bộ luật kiểm thể thức |
- Đă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.
- 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.
- Mở
/api/docs, thử gọiGET /api/text-sign/trinh-ky. - 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ủatext_process.STATEmã 1 và 6, và vì sao có 8.853 văn bản ở trạng thái "Đã huỷ".