Giải thích quy trình làm việc lấy tài liệu làm trung tâm với mô hình dữ liệu và mẫu UI thực tế cho phiên bản, preview, metadata và trạng thái rõ ràng.

Một ứng dụng là lấy tài liệu làm trung tâm khi chính tài liệu là sản phẩm người dùng tạo, xem xét và phụ thuộc vào. Trải nghiệm được xây quanh các tệp như PDF, hình ảnh, quét và biên nhận, chứ không phải một biểu mẫu trong đó tệp chỉ là tập tin đính kèm.
Trong các quy trình lấy tài liệu làm trung tâm, người ta làm việc thực sự bên trong tài liệu: họ mở nó, kiểm tra những gì đã thay đổi, thêm bối cảnh và quyết định bước tiếp theo. Nếu tài liệu không đáng tin, ứng dụng ngừng hữu ích.
Hầu hết ứng dụng lấy tài liệu làm trung tâm cần một vài màn hình cốt lõi ngay từ đầu:
Vấn đề xuất hiện nhanh. Người dùng tải cùng một biên nhận hai lần. Ai đó chỉnh sửa PDF rồi tải lại mà không giải thích. Một tệp quét không có ngày, không có nhà cung cấp và không có chủ sở hữu. Vài tuần sau, không ai biết phiên bản nào đã được phê duyệt hoặc quyết định dựa trên gì.
Một ứng dụng lấy tài liệu làm trung tâm tốt cảm giác nhanh và đáng tin. Người dùng nên trả lời những câu hỏi này trong vài giây:
Sự rõ ràng đó đến từ định nghĩa. Trước khi xây màn hình, hãy quyết định “phiên bản”, “preview”, “metadata” và “trạng thái” nghĩa là gì trong ứng dụng của bạn. Nếu những thuật ngữ đó mơ hồ, bạn sẽ có bản trùng lặp, lịch sử khó hiểu và luồng duyệt không khớp với công việc thực.
Giao diện thường trông đơn giản (một danh sách, một trình xem, vài nút), nhưng mô hình dữ liệu mang trọng lượng. Nếu các đối tượng cốt lõi đúng, lịch sử kiểm toán, preview nhanh và phê duyệt đáng tin trở nên dễ dàng hơn nhiều.
Bắt đầu bằng cách tách “bản ghi tài liệu” khỏi “nội dung tệp”. Bản ghi là thứ người dùng nói đến (Hóa đơn từ ACME, biên nhận taxi). Nội dung là các byte (PDF, JPG) có thể bị thay thế, xử lý lại, hoặc di chuyển mà không làm thay đổi ý nghĩa tài liệu trong ứng dụng.
Một tập đối tượng thực dụng để mô hình hóa:
Quyết định cái nào cần ID không đổi. Một quy tắc hữu dụng: Document ID tồn tại mãi mãi, trong khi Files và Previews có thể được sinh lại. Versions cũng cần ID ổn định, vì người ta tham chiếu “nó trông như thế nào hôm qua” và bạn cần một dấu vết kiểm toán.
Mô hình hóa quan hệ một cách rõ ràng. Một Document có nhiều Versions. Mỗi Version có thể có nhiều Previews (kích thước hoặc định dạng khác nhau). Điều này giữ cho màn hình danh sách nhanh vì chúng có thể tải dữ liệu preview nhẹ, trong khi màn hình chi tiết chỉ tải file đầy đủ khi cần.
Ví dụ: người dùng tải lên một ảnh biên nhận nhàu. Bạn tạo một Document, lưu File gốc, sinh ảnh thu nhỏ Preview và tạo Version 1. Sau đó người dùng tải lên bản quét rõ hơn. Đó sẽ là Version 2, không làm hỏng bình luận, phê duyệt hoặc tìm kiếm gắn với Document.
Mọi người mong đợi tài liệu thay đổi theo thời gian mà không “biến thành” một mục khác. Cách đơn giản nhất để đạt được điều đó là tách danh tính (Document) khỏi nội dung (Version và Files).
Bắt đầu với một document_id ổn định không bao giờ thay đổi. Ngay cả khi người dùng tải lại cùng một PDF, thay thế một ảnh mờ, hoặc tải lên bản quét sửa lỗi, nó vẫn là cùng một bản ghi document. Bình luận, phân công và log kiểm toán gắn rõ ràng với một ID bền.
Xem mỗi thay đổi có ý nghĩa như một hàng version mới. Mỗi version nên ghi ai tạo nó và khi nào, cộng với con trỏ lưu trữ (file key, checksum, size, page count) và các tác phẩm dẫn xuất (văn bản OCR, ảnh preview) liên kết với file chính xác đó. Tránh “chỉnh sửa tại chỗ.” Ban đầu trông có vẻ đơn giản hơn, nhưng nó phá vỡ khả năng truy vết và gây khó khăn khi debug.
Để đọc nhanh, giữ một current_version_id trên document. Hầu hết màn hình chỉ cần “mới nhất”, nên bạn không phải sắp xếp các version trên mỗi lần tải. Khi cần lịch sử, tải các version riêng và hiển thị timeline rõ ràng.
Rollback chỉ là thay đổi con trỏ. Thay vì xóa bất cứ thứ gì, đặt current_version_id về phiên bản cũ. Nó nhanh, an toàn và giữ nguyên dấu vết kiểm toán.
Để lịch sử dễ hiểu, ghi lại lý do mỗi version tồn tại. Một trường reason nhỏ, nhất quán (cộng thêm ghi chú tuỳ chọn) ngăn timeline đầy những cập nhật bí ẩn. Lý do phổ biến gồm re-upload replacement, scan cleanup, OCR correction, redaction và approval edit.
Ví dụ: đội tài chính tải ảnh biên nhận, thay bằng bản quét rõ hơn, rồi sửa OCR để tổng tiền đọc được. Mỗi bước là một version mới, nhưng document vẫn là một mục trong hộp thư. Nếu sửa OCR sai, rollback chỉ một click vì bạn chỉ chuyển current_version_id.
Trong các quy trình lấy tài liệu làm trung tâm, preview thường là thứ người dùng tương tác nhiều nhất. Nếu preview chậm hoặc hay lỗi, cả ứng dụng cảm thấy hỏng.
Xử lý sinh preview như một job riêng, không phải thứ màn hình upload phải chờ. Lưu file gốc trước, trả quyền điều khiển cho người dùng, rồi sinh preview nền. Điều này giữ UI phản hồi và làm cho retry an toàn.
Lưu nhiều kích thước preview. Một kích thước không phù hợp cho mọi màn hình: ảnh thu nhỏ nhỏ cho danh sách, ảnh trung bình cho chế độ xem chia, và ảnh trang đầy đủ cho xem chi tiết (từng trang cho PDF).
Theo dõi trạng thái preview một cách rõ ràng để UI luôn biết hiển thị gì: pending, ready, failed, và needs_retry. Giữ nhãn thân thiện cho UI, nhưng trạng thái dữ liệu rõ ràng.
Để render nhanh, cache các giá trị dẫn xuất cùng với bản ghi preview thay vì tính lại mỗi lần xem. Các trường phổ biến gồm page count, chiều rộng và cao preview, rotation (0/90/180/270), và tuỳ chọn “trang tốt nhất cho thumbnail”.
Thiết kế cho tệp chậm và lộn xộn. Một PDF quét 200 trang hoặc ảnh biên nhận nhàu có thể mất thời gian xử lý. Dùng nạp từng phần: hiển thị trang đầu sẵn có ngay khi có, rồi lấp đầy phần còn lại.
Ví dụ: người dùng tải 30 ảnh biên nhận. Danh sách hiển thị ảnh thu nhỏ là “pending”, rồi từng card chuyển sang “ready” khi preview xong. Nếu một vài tệp lỗi do file bị hỏng, chúng vẫn hiển thị với hành động retry rõ ràng thay vì biến mất hoặc chặn cả lô.
Metadata biến đống tệp thành thứ bạn có thể tìm, sắp xếp, xét duyệt và phê duyệt. Nó giúp trả lời nhanh các câu hỏi đơn giản: Đây là gì? Ai gửi? Có hợp lệ không? Tiếp theo nên làm gì?
Cách thực dụng để giữ metadata sạch là tách theo nguồn gốc:
Những nhóm này ngăn tranh cãi sau này. Nếu tổng tiền sai, bạn có thể thấy nó đến từ OCR hay chỉnh sửa của con người.
Với hóa đơn và biên nhận, một bộ trường nhỏ có tác dụng nếu dùng nhất quán (cùng tên, cùng định dạng). Các trường mỏ neo phổ biến là vendor, date, total, currency và document_number. Giữ chúng tuỳ chọn ban đầu. Người ta tải lên bản quét thiếu trang và ảnh mờ, và chặn tiến trình vì thiếu một trường sẽ làm chậm cả quy trình.
Đối xử với giá trị không biết như thứ hạng một: dùng trạng thái rõ ràng như null/unknown, cùng lý do khi cần (thiếu trang, không đọc được, không áp dụng). Điều đó cho phép tài liệu tiếp tục tiến mà vẫn hiển thị cho người kiểm duyệt những gì cần chú ý.
Cũng lưu nguồn gốc và độ tin cậy cho các trường trích xuất. Nguồn có thể là user, OCR, import hoặc API. Độ tin cậy có thể là điểm 0-1 hoặc tập nhỏ như high/medium/low. Nếu OCR đọc “$18.70” với độ tin cậy thấp vì chữ số cuối bị lem, UI có thể làm nổi bật và yêu cầu xác nhận nhanh.
Tài liệu nhiều trang cần quyết định thêm: cái gì thuộc toàn bộ tài liệu so với một trang đơn. Tổng tiền và vendor thường thuộc tài liệu. Ghi chú theo trang, làm mờ (redaction), xoay và phân loại theo trang thường thuộc cấp trang.
Trạng thái trả lời một câu hỏi: “Tài liệu này đang ở đâu trong quy trình?” Giữ nó nhỏ và nhàm. Nếu bạn thêm trạng thái mới mỗi khi ai đó yêu cầu, cuối cùng bạn sẽ có bộ lọc mà không ai tin tưởng.
Một tập trạng thái nghiệp vụ thực dụng phản ánh quyết định thực tế:
Giữ “processing” ra khỏi trạng thái nghiệp vụ. OCR đang chạy và preview đang sinh mô tả hệ thống đang làm gì, không phải việc tiếp theo người cần làm. Lưu chúng như trạng thái xử lý riêng.
Cũng tách phân công khỏi trạng thái (assignee_id, team_id, due_date). Một tài liệu có thể đã Approved nhưng vẫn được phân công để theo dõi, hoặc Needs review mà chưa có người phụ trách.
Ghi lại lịch sử trạng thái, không chỉ giá trị hiện tại. Một log đơn giản như (from_status, to_status, changed_at, changed_by, reason) có ích khi ai đó hỏi, “Ai đã từ chối biên nhận này và vì sao?”.
Cuối cùng, quyết định hành động nào được phép ở mỗi trạng thái. Giữ quy tắc đơn giản: Imported có thể sang Needs review; Approved là chỉ đọc trừ khi tạo version mới; Rejected có thể được mở lại nhưng phải giữ lý do trước đó.
Hầu hết thời gian dành cho việc quét danh sách, mở một mục, sửa vài trường và tiếp tục. Giao diện tốt làm các bước đó nhanh và dễ dự đoán.
Với danh sách tài liệu, coi mỗi hàng như một tóm tắt để người dùng quyết định mà không cần mở từng file. Một hàng mạnh nên hiển thị ảnh thu nhỏ nhỏ, tiêu đề rõ, vài trường chính (merchant, date, total), huy hiệu trạng thái và cảnh báo khi cần chú ý.
Giữ trang chi tiết thanh thản và dễ quét. Bố cục phổ biến là preview bên trái và metadata bên phải, với điều khiển chỉnh sửa cạnh mỗi trường. Người dùng nên có thể phóng to, xoay và lật trang mà không mất vị trí trên form. Nếu một trường được trích xuất từ OCR, hiển thị một chú thích độ tin cậy nhỏ, và lý tưởng là làm nổi bật vùng nguồn trên preview khi trường được focus.
Phiên bản hoạt động tốt nhất dưới dạng timeline, không phải dropdown. Hiển thị ai đã thay đổi gì và khi nào, và cho phép mở bất kỳ phiên bản cũ nào ở chế độ chỉ đọc. Nếu bạn cung cấp so sánh, tập trung vào khác biệt metadata (số tiền thay đổi, vendor được sửa) thay vì ép buộc so sánh pixel từng PDF.
Chế độ duyệt nên tối ưu cho tốc độ. Một luồng phân loại ưu tiên bàn phím thường đủ: hành động duyệt/từ chối nhanh, sửa nhanh các trường phổ biến, và một hộp bình luận ngắn cho từ chối.
Trạng thái trống cũng quan trọng vì tài liệu thường đang xử lý. Thay vì ô trống, giải thích điều đang diễn ra: “Preview đang được tạo”, “OCR đang chạy”, hoặc “Loại file này chưa có preview”.
Một luồng đơn giản cảm giác như “tải lên, kiểm tra, phê duyệt”. Ở dưới cùng, nó hoạt động tốt nhất khi bạn tách file thực tế (versions và previews) khỏi ý nghĩa nghiệp vụ (metadata và status).
Người dùng tải PDF, ảnh hoặc quét biên nhận và thấy ngay trong hộp thư. Đừng chờ xử lý xong. Hiển thị tên file, thời gian tải và huy hiệu rõ ràng như “Processing.” Nếu bạn đã biết nguồn (nhập qua email, camera di động, kéo-thả), cũng hiển thị.
Khi tải lên, tạo một bản ghi Document (thứ tồn tại lâu dài) và một bản ghi Version (file cụ thể này). Đặt current_version_id thành version mới. Lưu preview_state = pending và extraction_state = pending để UI biết điều gì đã sẵn sàng.
Trang chi tiết nên mở ngay, nhưng hiển thị khung xem tạm thời và thông báo “Đang chuẩn bị preview” thay vì một khung lỗi.
Một job nền tạo ảnh thu nhỏ và preview (ảnh trang cho PDF, ảnh thay đổi kích thước cho hình). Job khác trích xuất metadata (vendor, date, total, currency, loại tài liệu). Khi từng job xong, cập nhật chỉ trạng thái và timestamps của nó để bạn có thể retry khi lỗi mà không đụng chạm phần còn lại.
Giữ UI gọn: hiển thị trạng thái preview, trạng thái dữ liệu, và làm nổi bật trường có độ tin cậy thấp.
Khi preview sẵn, người kiểm duyệt sửa trường, thêm ghi chú và chuyển tài liệu qua các trạng thái nghiệp vụ như Imported -> Needs review -> Approved (hoặc Rejected). Ghi lại ai thay đổi gì và khi nào.
Nếu người kiểm duyệt tải lên file sửa, nó trở thành Version mới và document tự động về Needs review.
Xuất, đồng bộ kế toán hoặc báo cáo nội bộ nên đọc từ current_version_id và snapshot metadata đã được phê duyệt, không phải “kết quả trích xuất mới nhất.” Điều đó tránh trường hợp một tải lên chưa hoàn chỉnh làm thay đổi số liệu.
Quy trình lấy tài liệu thất bại vì những lý do nhàm chán: các lối tắt ban đầu trở thành nỗi đau hằng ngày khi người ta tải trùng, sửa lỗi, hoặc hỏi “Ai đã thay đổi cái này và khi nào?”.
Dùng tên file làm danh tính tài liệu là sai lầm cổ điển. Tên thay đổi, người dùng tải lại, và camera sinh ra trùng lặp như IMG_0001. Cho mỗi document một ID ổn định, và coi tên file như nhãn.
Ghi đè file gốc khi ai đó tải bản thay thế cũng gây rắc rối. Trông có vẻ đơn giản, nhưng bạn mất lịch sử kiểm toán và không thể trả lời câu hỏi cơ bản sau này (gì được phê duyệt, gì đã chỉnh sửa, gì đã gửi). Giữ file nhị phân bất biến và thêm bản ghi version mới.
Nhầm lẫn trạng thái tạo ra lỗi tinh vi. “OCR đang chạy” không phải là “Needs review”. Trạng thái xử lý mô tả máy đang làm gì; trạng thái nghiệp vụ mô tả việc con người nên làm tiếp. Khi trộn, tài liệu bị kẹt ở nhóm sai.
Quyết định UI cũng có thể tạo ma sát. Nếu bạn chặn màn hình cho đến khi preview tạo xong, người dùng cảm thấy ứng dụng chậm dù upload thành công. Hiển thị tài liệu ngay với placeholder hữu ích, rồi thay bằng ảnh thu nhỏ khi sẵn sàng.
Cuối cùng, metadata trở nên không đáng tin khi bạn lưu giá trị mà không có nguồn gốc. Nếu tổng tiền từ OCR, hãy nói rõ. Giữ timestamps.
Một danh sách kiểm tra nhanh:
Ví dụ: trong một ứng dụng quản lý biên nhận, người dùng tải ảnh rõ hơn. Nếu bạn version hóa, giữ ảnh cũ, đánh dấu OCR đang xử lý lại, và giữ trạng thái Needs review cho đến khi người xem xác nhận số tiền.
Quy trình lấy tài liệu chỉ cảm thấy “hoàn chỉnh” khi người dùng có thể tin tưởng những gì họ thấy và phục hồi khi có sự cố. Trước khi ra mắt, thử với tài liệu thật lộn xộn (biên nhận mờ, PDF xoay, tải trùng).
Năm kiểm tra bắt gặp hầu hết bất ngờ:
Một bài kiểm tra thực tế nhanh: yêu cầu ai đó kiểm duyệt ba biên nhận tương tự và cố ý làm một thay đổi sai. Nếu họ có thể tìm phiên bản hiện tại, hiểu trạng thái và sửa lỗi trong dưới một phút, bạn gần hoàn thiện.
Thanh toán hoàn trả biên nhận hàng tháng là ví dụ rõ ràng về công việc lấy tài liệu làm trung tâm. Nhân viên tải biên nhận, rồi hai người kiểm duyệt xem: quản lý, rồi tài chính. Biên nhận là sản phẩm, nên ứng dụng của bạn sống hoặc chết phụ thuộc vào versioning, preview, metadata và trạng thái rõ ràng.
Jamie tải ảnh biên nhận taxi. Hệ thống tạo Document #1842 với Version v1 (file gốc), một thumbnail và preview, và metadata như merchant, date, currency, total và điểm OCR confidence. Document bắt đầu ở Imported, rồi chuyển sang Needs review khi preview và extraction sẵn sàng.
Sau đó, Jamie vô tình tải cùng biên nhận một lần nữa. Kiểm tra trùng lặp (hash file + merchant/date/total tương tự) có thể hiện ra lựa chọn: “Có vẻ trùng với #1842. Gắn tiếp hay loại bỏ?” Nếu họ gắn, lưu nó như một File khác liên kết cùng Document để bạn giữ một luồng review và một trạng thái.
Trong khi duyệt, quản lý thấy preview, các trường chính và cảnh báo. OCR đoán tổng là $18.00, nhưng ảnh rõ là $13.00. Jamie sửa tổng. Đừng ghi đè lịch sử. Tạo Version v2 với các trường cập nhật, giữ v1 nguyên, và ghi “Total corrected by Jamie.”
Nếu bạn muốn xây luồng này nhanh, Koder.ai (koder.ai) có thể giúp sinh phiên bản đầu của ứng dụng từ một kế hoạch chat, nhưng nguyên tắc vẫn vậy: định nghĩa các đối tượng và trạng thái trước, rồi để màn hình theo sau.
Bước tiếp thực dụng:
Một ứng dụng lấy tài liệu làm trung tâm coi tài liệu là thứ chính người dùng làm việc với, không phải một tệp đính kèm phụ. Người dùng cần mở tài liệu, tin tưởng những gì họ thấy, hiểu những gì đã thay đổi và quyết định bước tiếp theo dựa trên chính tài liệu đó.
Bắt đầu với một hộp thư/danh sách, một trang chi tiết tài liệu có preview nhanh, một khu vực hành động kiểm duyệt đơn giản (duyệt/từ chối/yêu cầu sửa) và một cách để xuất hoặc chia sẻ. Bốn màn hình này bao phủ vòng lặp phổ biến: tìm, mở, quyết định, và chuyển tiếp.
Mô hình một bản ghi Document ổn định không bao giờ đổi, và lưu bytes thực tế dưới dạng các đối tượng File riêng. Sau đó thêm Version như ảnh chụp liên kết một document với file cụ thể (và các kết quả dẫn xuất). Sự tách biệt này giữ cho bình luận, phân công và lịch sử nguyên vẹn ngay cả khi file bị thay thế.
Mỗi thay đổi có ý nghĩa nên là một version mới thay vì ghi đè file. Giữ current_version_id trên document để đọc nhanh “phiên bản mới nhất”, và lưu timeline các phiên bản cũ để kiểm toán và rollback. Điều này tránh nhầm lẫn về những gì đã được phê duyệt và vì sao.
Sinh preview bất đồng bộ sau khi lưu file gốc, để việc upload cảm nhận là tức thì và retry an toàn. Theo dõi trạng thái preview như pending/ready/failed để UI luôn trung thực, và lưu nhiều kích thước để list view nhẹ còn detail view sắc nét.
Lưu metadata theo ba nhóm: system (kích thước file, loại), extracted (trường OCR và độ tin cậy), và user-entered (chỉnh sửa tay). Giữ nguồn gốc để biết giá trị đến từ OCR hay con người, và tránh bắt buộc phải điền mọi trường trước khi công việc có thể tiếp tục.
Dùng một tập trạng thái nghiệp vụ nhỏ mô tả việc người dùng nên làm tiếp theo, ví dụ Imported, Needs review, Approved, Rejected, và Archived. Theo dõi trạng thái xử lý riêng (preview/OCR đang chạy) để tài liệu không bị “kẹt” trong trạng thái trộn lẫn công việc người và máy.
Lưu checksum file bất biến và so sánh khi upload, rồi thêm kiểm tra thứ hai dùng các trường chính như vendor/date/total khi có. Khi nghi ngờ trùng lặp, đưa ra lựa chọn rõ ràng: gắn vào cùng một thread Document hoặc loại bỏ, để bạn không tách lịch sử kiểm duyệt ra nhiều bản sao.
Giữ log lịch sử trạng thái với ai thay đổi gì, khi nào và lý do, và giữ các phiên bản đọc được qua timeline. Rollback nên là thay đổi con trỏ về phiên bản cũ hơn, không phải xóa, để phục hồi nhanh mà không mất dấu vết kiểm toán.
Định nghĩa các đối tượng và trạng thái trước, rồi để giao diện theo đúng những định nghĩa đó. Nếu dùng Koder.ai để sinh app từ một kế hoạch chat, hãy rõ ràng về Document/Version/File, trạng thái preview và extraction, và quy tắc trạng thái để màn hình sinh ra khớp với hành vi quy trình thực tế.