KB-67D1

NĐ-LARK-PG-DIR — Prerequisites + Kế hoạch tổng thể POC Thông báo tuyển dụng

16 min read Revision 1
larkpgdirectuspdfgotenbergnuxtprerequisitespocthong-bao-tuyen-dung

NĐ-LARK-PG-DIR — Prerequisites + Kế hoạch tổng thể POC

Trạng thái: DRAFT chờ Huyên duyệt Tác giả: Claude Desktop (phiên Lark 6, 2026-04-13) Mục tiêu file: Bổ sung phần thiếu cho 3 file GPT đã viết trong lark_PG_Dir/. KHÔNG sửa file gốc của GPT. File này là pre-flight + roadmap chi tiết để các phiên sau đọc 1 lần là đủ context, không phải lội lại lịch sử chat. Lý do tồn tại: Token-saving. Mỗi lần phiên mới mà phải search KB + đọc lại ngữ cảnh lan man rất tốn token. File này gói toàn bộ context cần thiết vào 1 chỗ, các phiên sau chỉ cần get_document 1 call.


0. Cách dùng file này

Phiên mới vào:

  1. get_document knowledge/dev/lark/lark_PG_Dir/nd-lark-pg-dir-prerequisites-v1.md ← file này
  2. Đọc Section 9 (Trạng thái hiện tại) trước — biết đang ở đâu
  3. Đọc Section 6 (Việc nhóm A vs B) — biết phải làm gì tiếp
  4. Chỉ đọc các section khác khi cần chi tiết

Không cần search KB lan man. Không cần đọc lịch sử chat. File này là single source of truth cho POC này.


1. Mục tiêu POC (cụ thể, đo được)

Sinh tự động 1 file PDF "Thông báo tuyển dụng" từ data Lark Base 88, hiển thị trên Nuxt tại domain donhang.incomex..., chạy theo cron 30 phút/lần trong giờ làm việc Hà Nội.

Tiêu chí thành công:

  • 1 record trong bảng tổng hợp Lark (Huyên sẽ tạo) → tự động sinh ra 1 PDF trong vòng tối đa 30 phút
  • PDF hiển thị được trên Nuxt donhang.incomex... dưới dạng bảng matrix 3-5 cột (mã đơn, ngày, trạng thái, link tải PDF)
  • Toàn bộ pipeline READ-only từ phía Lark (không write back)
  • Có cờ tắt nhanh nếu xảy ra sự cố
  • Có Dual-trigger theo hiến pháp Incomex

KHÔNG nằm trong scope POC:

  • Không sync 2 chiều
  • Không thiết kế generic connector cho 18 Base
  • Không hiểu toàn bộ Lark
  • Không đụng automation Lark
  • Không ký số PDF chuẩn pháp lý (chỉ render dấu đỏ/chữ ký dạng ảnh)
  • Không sửa Lark production

2. Bối cảnh (gốc lý do làm POC này)

POC này sinh ra do 5 phiên Lark trước (Lark 1 → Lark 6) đã thử "hiểu toàn bộ Lark trước khi làm gì" — bất khả với SaaS đóng. Hướng đi mới (Huyên chốt phiên Lark 6):

"Mục tiêu thấp hơn. Kết nối một vài dữ liệu sang PG → Directus → PDF render → Nuxt. Quá trình tự động hoàn toàn."

Nguyên nhân thực tế: Lark hiện đang sinh thông báo tuyển dụng (đăng đơn hàng tuyển TTS theo luật) qua 1 cơ chế đã hỏng từ 29/07/2025 (Base "Thông báo đơn hàng" còn 87 record cũ, không sinh thêm). POC này không phải để fix Lark — mà là xây tuyến mới song song, không phụ thuộc Lark còn hỏng.


3. Kiến trúc tổng thể (đã được Huyên duyệt nguyên tắc, chờ duyệt chi tiết)

[Lark Base 88 - Phái cử (CORE, 80 bảng)]
    app_token: YSIkb8PxOaNaozs2vwalOOcagkf
    │
    │ Bảng tổng hợp (Huyên tạo, table_id sẽ báo sau)
    │ ─ Huyên tự fill data từ nhiều bảng/Base về 1 chỗ
    │ ─ Pipeline KHÔNG cần filter/join cross-table
    │
    │ Cổng 2 READ (lark-client, polling cron)
    │ Identity: app "For Gem" (cli_a785d634437a502f)
    │ Scope: bitable READ verified P3
    │ Cron: 30 phút/lần, 8h-18h GMT+7
    │ Dual-trigger theo hiến pháp Incomex
    ▼
[PostgreSQL 16 trên VPS Contabo]
    ├─ lark_jobs            (cấu hình job: base_token, table_id, field whitelist)
    ├─ lark_job_runs        (mỗi lần chạy = 1 run, có status, rows_fetched)
    ├─ lark_raw_records     (JSONB, không mất bit nào, idempotent theo source_record_id)
    ├─ lark_document_rows   (canonical, cột thật, dùng để render)
    ├─ lark_pdf_outputs     (track file output, status render)
    └─ cron_flags           (cờ tắt nhanh dual-trigger)
    │
    │ Directus expose collection
    ▼
[Directus CMS]
    ├─ Collection lark_document_rows (read-only mirror PG)
    └─ Files collection (lưu PDF output, có metadata + permission)
    │
    │ Cron quét row mới → render
    ▼
[Gotenberg container Docker]
    Input: HTML template + data merged
    Output: PDF
    │
    ▼
[Directus Files] ← lưu PDF output
    │
    ▼
[Nuxt 3 tại donhang.incomex...]
    Trang list: bảng matrix 3-5 cột (mã đơn, ngày, trạng thái, link PDF)
    Click → tải PDF từ Directus Files

Luồng cũ Directus → Nuxt vẫn chạy song song, không bị POC này ảnh hưởng.


4. Tài sản đã có sẵn (KHÔNG phải build mới)

# Tài sản Vị trí Trạng thái
1 Base 88 - Phái cử (core, 80 bảng) app_token YSIkb8PxOaNaozs2vwalOOcagkf ✅ Active production, edit cuối 13/04/2026 14:57 bởi Nhung.PTH
2 App "For Gem" Lark cli_a785d634437a502f ✅ Đã invite vào toàn bộ 18 Base
3 LARK_APP_ID + LARK_APP_SECRET GSM project github-chatgpt-ggcloud ✅ Sẵn sàng
4 lark-client toolkit /opt/incomex/lark-client/ ✅ Phase 2 test 8/8 PASS (2026-04-11)
5 LarkCore + LarkReader trong lark-client ✅ Read-only, có rate limit + audit
6 CLI commands schema list/dump/summarize, audit tail ✅ Sẵn sàng
7 15/15 READ endpoint Bitable v1 verified P3 (cong2-p6-final-report)
8 PostgreSQL 16 VPS Contabo, Docker ✅ DUY NHẤT
9 Directus CMS VPS Contabo, Docker
10 Nuxt 3 VPS Contabo, Docker
11 Domain donhang.incomex... Đã trỏ về VPS ✅ Huyên đã setup
12 Schema Đơn hàng - Chính thức tblh7nrQpK8TqIs2, 234 fields ✅ Có trong lark-base-88-data-flow.md
13 Schema TTS - Thông tin tblKnzaih6154r2e, 233 fields ✅ Có trong lark-base-88-data-flow.md
14 Schema Nghiệp đoàn tblG18kR9aFhWrJW, 87 fields ✅ Có trong lark-base-88-data-flow.md
15 Blueprint 80 bảng Base 88 lark-base-88-phai-cu-blueprint.md ✅ Phân nhóm A-O
16 Registry 18 Base lark-base-registry.md ✅ Có app_token đầy đủ

Không cần build mới gì ở tầng reader/storage. Chỉ cần ráp.


5. Quyết định kỹ thuật đã chốt

# Quyết định Lý do
1 PDF engine: Gotenberg API REST sẵn, Docker container, render HTML+CSS bằng Chromium, hỗ trợ tốt: con dấu đỏ overlay (CSS absolute), bảng phức tạp, header/footer, font Unicode tiếng Việt, MIT license. Ứng cử viên dự phòng nếu Gotenberg fail: Typst
2 Reader: Cổng 2 (lark-client/LarkReader) Read-only, đã verified, có audit + rate limit
3 Cron: 30 phút/lần, 8h-18h GMT+7 Huyên chốt. Nghỉ qua đêm, sáng 8h chạy tiếp
4 Dual-trigger theo hiến pháp Incomex Bắt buộc. Cần verify lại Điều mấy quy định Dual-trigger trước khi implement (xem Section 10 Open Questions)
5 Storage PDF: Directus Files collection Có metadata + permission sẵn, Nuxt đã đọc Directus rồi, không cần thêm credential storage. Đề xuất, chờ Huyên duyệt
6 Template engine merge data: Handlebars Node native, đơn giản nhất, đủ dùng. Chờ Huyên duyệt
7 Template HTML lưu trong git Nuxt repo Versioning qua git commit. Chờ Huyên duyệt
8 Mapping format: YAML, dùng field_id (KHÔNG dùng tên) Tên field Lark có thể đổi, field_id không đổi
9 Schema PG: dùng đúng 5 bảng GPT đề xuất trong nd-lark-mvp Không sửa, không thêm
10 POC chỉ đọc 1 table tổng hợp Huyên tạo Không filter, không join cross-table phía pipeline. Cắt độ phức tạp

6. Việc cần làm — chia 2 nhóm

Nhóm A — Làm được NGAY (không phụ thuộc bảng tổng hợp Huyên tạo)

# Việc Ai làm Estimate Trạng thái
A1 File này (nd-lark-pg-dir-prerequisites-v1.md) Claude Desktop 30 phút 🟡 đang viết
A2 Setup Gotenberg container Docker trên VPS Claude Code CLI 15 phút
A3 Tạo 5 bảng PG schema theo nd-lark-mvp (lark_jobs, lark_job_runs, lark_raw_records, lark_document_rows, lark_pdf_outputs) + bảng cron_flags Claude Code CLI qua DOT cặp A/B 30 phút
A4 Tạo Directus collection mirror PG (read-only) Claude Code CLI qua DOT 20 phút
A5 Skeleton template HTML A4 (sườn, Huyên sẽ sửa khi có data thật) Claude Desktop 30 phút
A6 Cron systemd 30 phút/lần 8h-18h GMT+7, đọc cờ từ cron_flags, dual-trigger Claude Code CLI (sau khi verify hiến pháp) 45 phút
A7 Endpoint Nuxt skeleton tại donhang.incomex... Claude Code CLI 30 phút

Tổng nhóm A: ~3.5 giờ, có thể chạy song song nhiều việc.

Nhóm B — CHỜ Huyên báo table_id bảng tổng hợp

# Việc Ai làm Phụ thuộc
B1 Mapping field Lark → PG column (YAML, field_id) Claude Desktop Huyên báo table_id + tên field
B2 Tạo job config trong lark_jobs (base_token, source_table_id, field_whitelist) Claude Code CLI B1
B3 Lần chạy đầu tiên end-to-end (Lark → PG → Directus → Gotenberg → PDF → Nuxt) Claude Code CLI B2 + nhóm A xong
B4 Sửa template HTML cho khớp data thật + layout đúng PDF mẫu Huyên + Claude Desktop B3
B5 Hardtest end-to-end (1 record → PDF đúng, cron tự chạy đúng giờ, cờ tắt OK) Claude Code CLI B4

Tổng nhóm B: ~2-4 giờ, sequential.


7. Ràng buộc tuyệt đối (KHÔNG được vi phạm)

  1. Cổng 2 READ-only. KHÔNG dùng bất kỳ tool write nào của Cổng 2 trong toàn bộ POC. Nếu cần verify quyền, dùng lark-client schema list hoặc LarkReader.list_tables.
  2. KHÔNG đụng Lark production (Base 88 đang chạy, edit cuối hôm nay 13/04/2026).
  3. KHÔNG sync 2 chiều, KHÔNG write-back.
  4. Mọi DOT ghi (cấp B) phải có DOT kiểm tra (cấp A) theo nguyên tắc Huyên đã chốt.
  5. §0-AE: Không báo DONE nếu chưa verify.
  6. Smoke test sau mọi deploy.
  7. Dual-trigger theo hiến pháp Incomex (Điều cần verify).
  8. GSM = SSOT cho secrets — không hardcode token.
  9. §0-AU: Không hardcode (path, URL, token).

8. Mapping schema PG (draft, theo nd-lark-mvp của GPT)

8.1 lark_jobs

CREATE TABLE lark_jobs (
  job_code        TEXT PRIMARY KEY,
  base_token      TEXT NOT NULL,
  source_table_id TEXT NOT NULL,
  source_view_id  TEXT,
  field_whitelist JSONB NOT NULL,  -- [{lark_field_id, lark_field_name, pg_column, type, required}]
  document_type   TEXT NOT NULL,    -- 'thong_bao_tuyen_dung'
  status          TEXT NOT NULL DEFAULT 'active', -- 'active' | 'paused'
  schedule_mode   TEXT NOT NULL DEFAULT 'cron',   -- 'cron' | 'manual'
  notes           TEXT,
  created_at      TIMESTAMPTZ DEFAULT NOW(),
  updated_at      TIMESTAMPTZ DEFAULT NOW()
);

8.2 lark_job_runs

CREATE TABLE lark_job_runs (
  run_id        BIGSERIAL PRIMARY KEY,
  job_code      TEXT NOT NULL REFERENCES lark_jobs(job_code),
  trigger_source TEXT NOT NULL, -- 'cron_primary' | 'cron_backup' | 'manual'
  started_at    TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  finished_at   TIMESTAMPTZ,
  status        TEXT NOT NULL DEFAULT 'running', -- 'running' | 'success' | 'failed' | 'skipped'
  rows_fetched  INTEGER DEFAULT 0,
  rows_new      INTEGER DEFAULT 0,
  rows_updated  INTEGER DEFAULT 0,
  error_log     TEXT
);

8.3 lark_raw_records

CREATE TABLE lark_raw_records (
  id                BIGSERIAL PRIMARY KEY,
  run_id            BIGINT NOT NULL REFERENCES lark_job_runs(run_id),
  job_code          TEXT NOT NULL,
  source_record_id  TEXT NOT NULL,
  raw_jsonb         JSONB NOT NULL,
  fetched_at        TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  UNIQUE(job_code, source_record_id, fetched_at)
);
CREATE INDEX idx_raw_records_lookup ON lark_raw_records(job_code, source_record_id);

8.4 lark_document_rows (canonical)

CREATE TABLE lark_document_rows (
  row_id           BIGSERIAL PRIMARY KEY,
  job_code         TEXT NOT NULL,
  source_record_id TEXT NOT NULL,
  document_type    TEXT NOT NULL,
  -- Cột canonical sẽ được điền sau khi Huyên báo bảng tổng hợp
  -- Ví dụ cho thong_bao_tuyen_dung:
  -- ma_don_hang     TEXT,
  -- ten_xi_nghiep   TEXT,
  -- nganh_nghe      TEXT,
  -- ngay_ban_hanh   DATE,
  -- ...
  payload_jsonb    JSONB,  -- phần linh hoạt cho field chưa biết
  render_status    TEXT NOT NULL DEFAULT 'pending', -- 'pending' | 'rendering' | 'rendered' | 'failed'
  created_at       TIMESTAMPTZ DEFAULT NOW(),
  updated_at       TIMESTAMPTZ DEFAULT NOW(),
  UNIQUE(job_code, source_record_id, document_type)
);

8.5 lark_pdf_outputs

CREATE TABLE lark_pdf_outputs (
  output_id        BIGSERIAL PRIMARY KEY,
  row_id           BIGINT NOT NULL REFERENCES lark_document_rows(row_id),
  source_record_id TEXT NOT NULL,
  document_type    TEXT NOT NULL,
  directus_file_id UUID,        -- UUID file trong Directus Files collection
  file_name        TEXT,
  file_size_bytes  BIGINT,
  rendered_at      TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  status           TEXT NOT NULL DEFAULT 'success', -- 'success' | 'failed'
  error_log        TEXT
);

8.6 cron_flags (mới, cho dual-trigger + tắt nhanh)

CREATE TABLE cron_flags (
  flag_key    TEXT PRIMARY KEY,
  flag_value  BOOLEAN NOT NULL,
  description TEXT,
  updated_at  TIMESTAMPTZ DEFAULT NOW(),
  updated_by  TEXT
);
INSERT INTO cron_flags VALUES
  ('lark_pdf_pipeline_enabled', TRUE, 'Cờ tắt nhanh toàn bộ pipeline POC'),
  ('lark_pdf_trigger_primary',  TRUE, 'Cờ trigger chính (cron 30 phút)'),
  ('lark_pdf_trigger_backup',   TRUE, 'Cờ trigger backup (dual-trigger theo hiến pháp)');

9. Trạng thái hiện tại (cập nhật mỗi phiên)

Phiên Lark 6, 2026-04-13, 15:30 GMT+7:

  • File này đang được tạo (A1)
  • Việc nhóm A (A2-A7) ⏳ chưa bắt đầu
  • Việc nhóm B ⏳ chờ Huyên báo table_id bảng tổng hợp
  • Open Questions: chưa verify Điều mấy của hiến pháp về Dual-trigger

Cần Huyên quyết để đi tiếp:

  1. Duyệt file này có ổn không? Nội dung đã đầy đủ chưa?
  2. Duyệt Storage = Directus Files, Template engine = Handlebars, Template lưu git Nuxt?
  3. Cho phép Claude Desktop search KB tìm Điều hiến pháp về Dual-trigger?
  4. Khi Huyên tạo xong bảng tổng hợp → báo table_id + tên các field cần đọc (text plain, dán vào chat hoặc file mới nd-lark-pg-dir-source-table-info-v1.md)

10. Open Questions (cần verify, không tự đoán)

# Câu hỏi Cách verify Khi nào
Q1 Điều mấy của hiến pháp Incomex quy định Dual-trigger? Cơ chế chính xác là gì (1 chính + 1 backup? 2 độc lập + dedup?) agent-data:search_knowledge với query "dual trigger điều" và "hiến pháp dual" Trước A6
Q2 Directus có Files collection sẵn không, hay cần tạo? Permission default ra sao? Kiểm tra Directus admin UI Trước A4
Q3 Nuxt có sẵn route engine cho subdomain donhang.incomex... không, hay cần config riêng? Kiểm tra Nuxt config + nginx VPS Trước A7
Q4 Gotenberg version nào dùng? Latest stable hay LTS? Check Docker Hub gotenberg/gotenberg Trước A2
Q5 Bảng tổng hợp Huyên tạo sẽ là bảng MỚI hay 1 bảng có sẵn (như Đơn hàng - Chính thức)? Hỏi Huyên Trước B1
Q6 Tên 8 cột template thông báo tuyển dụng cụ thể là gì? Phân tích PDF mẫu Huyên gửi (đã có) + map sang field Lark Trước B1

11. Tham khảo chéo

  • knowledge/dev/lark/lark_PG_Dir/README.md — README nhóm tài liệu
  • knowledge/dev/lark/lark_PG_Dir/nd-lark-pg-dir-short-plan-v1-gpt.md — Plan ngắn của GPT
  • knowledge/dev/lark/lark_PG_Dir/nd-lark-mvp-lark-to-pg-to-pdf-v1-gpt.md — MVP detail của GPT (schema PG 5 bảng nguồn)
  • knowledge/dev/lark/lark_PG_Dir/nd-lark-pg-dir-pdf-engine-decision-v1-gpt.md — Quyết định Gotenberg
  • knowledge/dev/lark/lark-base-registry.md — 18 Base + app_token
  • knowledge/dev/lark/lark-base-88-phai-cu-blueprint.md — Blueprint 80 bảng Base 88
  • knowledge/dev/lark/lark-base-88-data-flow.md — Schema 3 bảng core + 12 link field
  • knowledge/dev/lark/lark-client-architecture.md — lark-client toolkit doc
  • knowledge/dev/lark/cong2-p6-final-report — Kết quả P6 verify Cổng 2

12. Lịch sử file

Rev Ngày Người Thay đổi
1 2026-04-13 Claude Desktop (phiên Lark 6) Khởi tạo. Tổng hợp 6 phiên Lark trước + 3 file GPT + KB Lark sẵn có. Mục đích: token-saving cho phiên sau.

EOF