Bỏ qua

ONE-ANNA — Tổng quan

Tài liệu nội bộ cho đội dự án và người dùng portal. Viết từ code nhánh feat/lich-hop (commit da90b4b4), cấu hình Worker one-anna-api, D1 one-anna-buffer (chỉ SELECT) và cấu trúc các Lark Base (đọc qua Lark API), ngày 22/10/2026. Chỗ ghi cần xác nhận là chỗ chưa kiểm chứng được từ nguồn.

1. Mục đích

ONE-ANNA là portal nội bộ của Kính mắt ANNA gom việc của Nhân sự (HR), Chăm sóc khách hàng (CSKH) và Vận hành cửa hàng vào một chỗ, chạy trong app Lark:

  • Nhân viên xem chấm công, phiếu lương, hồ sơ, KPI, học tập của chính mình; đặt lịch họp; gửi yêu cầu pháp chế; dùng tài liệu.
  • Quản lý (AM, RSM, QLCH, Leader SS…) xem báo cáo doanh thu, lượt khách, xử lý ticket CSKH, chấm KPI, phỏng vấn ứng viên.
  • HR / CSKH / Pháp chế / BOD xem báo cáo tổng hợp, quản trị dữ liệu và phân quyền.
  • Tự động hoá: ~70 job định kỳ và các event Base tạo dòng dữ liệu, đối soát, sao lưu và gửi thông báo Lark (bot ONE - ANNA).

Dữ liệu gốc nằm trên Lark Base; ONE-ANNA đọc/ghi Base qua Lark Open API và giữ bộ đệm, nhật ký, khoá ở Cloudflare D1/KV.

2. Kiến trúc

flowchart LR subgraph Lark["Lark (kinhmatanna.sg.larksuite.com)"] U["Người dùng<br/>app Lark / trình duyệt"] BOT["Bot ONE - ANNA<br/>(DM, thẻ, nhóm)"] BASE[("Lark Base<br/>HRIS · CSKH · KPI · RSM · ATS<br/>Đào tạo · Lượt khách · Vận hành<br/>Pháp chế · Sinh nhật")] CAL["Lark Calendar · Wiki · Drive · Approval"] AUTO["Lark Automation<br/>+ Event Subscription"] end subgraph CF["Cloudflare"] W["Worker one-anna-api<br/>/app · /rpc · /api/* · /webhook · /cron"] AS["Static assets<br/>portal-assets (giao diện)"] D1[("D1 one-anna-buffer")] KV[("KV ANNA_KV")] Q["Queues: lark-events · media · jobs · dlq"] CRON["Cron Triggers (35 biểu thức)"] PG["Pages one-anna<br/>(chuyển hướng / OAuth dự phòng)"] end GAS["Google Apps Script<br/>(legacy, đang gỡ dần)"] U -->|"mở /app"| W W --> AS W <-->|"Open API"| BASE W <--> CAL W --> BOT AUTO -->|"webhook / event"| W CRON --> W W <--> D1 W <--> KV W <--> Q W -.->|"một số RPC / job cũ"| GAS PG -.-> W
Thành phần Chi tiết
Worker one-anna-api (apps/api, src/index.js, nodejs_compat). URL: https://one-anna-api.tungpham-hn219.workers.dev
Giao diện Bundle tĩnh trong apps/api/portal-assets (nguồn: apps/web/* — lark-shell, cskh, kpi, lich-hop, phan-quyen, luot-khach…). Giao diện cũ (?ui=old) từ legacy/gas/Index.html
RPC POST /rpc — gọi theo tên hàm. Hàm "native" chạy trong Worker; một số hàm còn chuyển sang GAS (GAS_ASYNC_RPCS)
Lưu trữ D1 one-anna-buffer, KV ANNA_KV, 4 Queue (xem Lưu trữ)
Quan sát Workers Observability (head sampling 0.2)
Pages Project one-anna chỉ còn chuyển hướng / OAuth dự phòng

Các route chính của Worker

Route Việc
/app Portal (giao diện). ?ui=old\|lark\|auto chọn giao diện; ?cskh_table=&cskh_record= mở thẳng bản ghi CSKH
/login, /auth/callback Đăng nhập Lark OAuth
/rpc Gọi hàm dữ liệu
/api/me, /api/me/attendance/month, /api/me/attendance/sync Thông tin người đăng nhập, chấm công tháng
/ats-photo, /cv-file Ảnh, CV ứng viên
/api/cskh/drive/folder\|file, /api/cskh/mark-notify-viewed Tệp và đánh dấu đã xem của CSKH
/webhook, /api/webhook/* Event Base, thẻ tương tác, Lark Automation (xem Trigger)
/cron, /api/run Chạy tay một cron / job (cần secret)
/health, /api/version, /api/d1/ping, /diag* Kiểm tra sức khoẻ, phiên bản, chẩn đoán

3. Đăng nhập

  • Trong app Lark (Workplace › ONE-ANNA): tự đăng nhập (tt.requestAuthCode → completeLarkLoginWithCode), không cần thao tác.
  • Trình duyệt: mở https://one-anna-api.tungpham-hn219.workers.dev/app → chuyển tới /login → đăng nhập Lark (OAuth) → quay về /auth/callback → /app.
  • Quyền trong portal theo bảng Phân quyền Portal (mục 5). Điều kiện chính xác để một tài khoản Lark vào được portal và thời hạn phiên: cần xác nhận.

Chi tiết: Trang chủ & Đăng nhập.

4. Danh sách module

Module Vị trí trên portal (Rail) Tài liệu
Trang chủ & Đăng nhập Hôm nay (home) Base dữ liệu · ERD
Trung tâm nhân sự (TT nhân sự) TT nhân sự (ns) Base dữ liệu · ERD
Báo cáo nhân sự (M1–M5) Nhân sự (hr) › Báo cáo nhân sự Base dữ liệu · ERD
KPI Nhân sự (hr) › KPI; TT nhân sự › KPI cá nhân Base dữ liệu · ERD
Tuyển dụng (ATS) Tuyển dụng (td) Base dữ liệu · ERD
Đào tạo Đào tạo (dt); TT nhân sự › Học tập Base dữ liệu · ERD
Báo cáo doanh thu (RSM) & Lượt khách Doanh thu (kd) Base dữ liệu · ERD
Chăm sóc khách hàng (CSKH) CSKH (cskh) Base dữ liệu · ERD
Pháp chế Pháp chế (pc) Base dữ liệu · ERD
Lịch họp Lịch họp (hop) Base dữ liệu · ERD
Tài liệu (Lark Wiki) Tài liệu (tl) Base dữ liệu · ERD
Quản trị (phân quyền, truy cập, Chứng từ, KSNB, HR Admin) Quản trị (qt) Base dữ liệu · ERD
Vận hành cửa hàng (tự động hoá Base Vận hành) Không có rail riêng — thông báo Lark Base dữ liệu · ERD

Thanh Rail bên trái portal: Hôm nay · TT nhân sự · Nhân sự · Tuyển dụng · Đào tạo · Doanh thu · CSKH · Pháp chế · Lịch họp · Tài liệu · Quản trị (mục hiện theo quyền).

5. Vai trò và phân quyền

5.1 Nguồn quyền

Quyền đọc từ bảng HRIS Phân quyền Portal (tblBk8DNs33GZt7N): mỗi dòng một người (cột "User ID" = mã nhân viên), cột vai trò, Chi nhánh / Khu vực phụ trách (cắt phạm vi số liệu doanh thu), Trạng thái (Ngưng = chặn), và các cột checkbox chức năng. Danh mục chức năng: apps/api/src/portal/role-core/catalog.js. Màn sửa: Quản trị phân quyền.

5.2 Vai trò (thứ tự ưu tiên khi một người có nhiều vai trò)

super admin › admin › bod › hr › quản lý cskh › ksnb › rsm › am › leader ss › ss › cskh › qlch › ktv › stylist › nhân viên.

  • Không giới hạn chức năng: super admin, admin, hr — trừ các ô Admin phải tick rõ (bên dưới).
  • Mặc định khi chưa tick gì: bod, rsm, am, leader ss, ss → quyền tự phục vụ nhân sự + Báo cáo RSM/AM + Tài liệu; vai trò khác → quyền tự phục vụ nhân sự + Tài liệu.
  • Admin phải tick rõ: Báo cáo RSM/AM, các ô CSKH, Báo cáo KPI.
  • Chỉ Super Admin: Quản trị phân quyền.
  • Vai trò CSKH (AM, QLCH, KTV…) trong module CSKH còn đọc từ vai trò trên Base CSKH.

5.3 Cột chức năng (checkbox trên bảng Phân quyền Portal)

Nhóm Cột trên Base Khoá Mở cái gì
Trung tâm nhân sự Chấm công · Phiếu lương · Quản lý đơn từ · Thông tin cá nhân · KPI Cá nhân HRM_ATTENDANCE · HRM_PAYSLIP · HRM_LEAVE · HRM_PROFILE · HRM_KPI TT nhân sự
Báo cáo nhân sự M1 Hồ sơ · M2 Công & Lương · M3 Luân chuyển · M4 Vòng đời REPORTS_PROFILE · REPORTS_WORKPAY · REPORTS_MOBILITY · REPORTS_LIFECYCLE Nhân sự › Báo cáo
Tuyển dụng Xem ứng viên · Đánh giá ứng viên RECRUITMENT_VIEW · RECRUITMENT_EVAL Tuyển dụng
Doanh thu Báo cáo RSM/AM RSM_REPORTS Doanh thu
CSKH Báo cáo CSKH · Xử lý ticket CSKH · Hồ sơ 360 nhân sự CSKH_REPORTS · CSKH_OPERATE · CSKH_EMPLOYEE_360 CSKH
KPI Báo cáo KPI KPI_VIEW Nhân sự › KPI
Tài liệu Tài liệu TAI_LIEU Tài liệu
Quản trị HR Admin · Lịch sử truy cập · Quản trị phân quyền ADMIN_HR · ADMIN_ACCESS_LOG · ADMIN_PERMISSIONS Quản trị

5.4 Quyền không đi qua bảng Phân quyền

Module Quy tắc
Đào tạo, TT nhân sự › Học tập Đang chạy thử: Tùng + danh sách trong config/training-access.mjs
Pháp chế Mọi người: Quản lý yêu cầu, Báo cáo yêu cầu. Nhân sự pháp chế (config/phap-che-access.mjs) hoặc BOD: Báo cáo tổng hợp, Vận hành, Quản lý hồ sơ
Lịch họp Mọi người đã đăng nhập; sửa/huỷ: người tạo, Host, Admin
Màn mới đang thử (canary) Chỉ Tùng (config/ui-lark.mjs › canaryUsers): Dashboard của tôi, Dashboard quản trị, Chứng từ, KSNB, HR Admin giao diện mới, Cài đặt KPI, r252, dashboard Tài liệu / Lịch họp

Luật hiển thị phía giao diện: apps/web/lark-shell/src/v16/perm.ts; máy chủ kiểm lại mỗi RPC.

6. Lưu trữ

Nơi Dùng cho
Lark Base Dữ liệu nghiệp vụ gốc (HRIS, CSKH, KPI, RSM, ATS, Đào tạo, Lượt khách, Vận hành, Pháp chế, Sinh nhật)
D1 one-anna-buffer Bộ đệm và nhật ký: access_log, audit_logs, cskh_write_log, rpc_write_log, employee_attendance_cache, att_dirty, payroll_*, report_snapshot, meetings/meeting_*, pc_*, perm_*, revoked_users, trigger_runs, trigger_switches, scheduled_job_locks, short_locks, deploy_lock, deploy_log, lark_event_dedup, webhook_inbox, im_send_outbox, pending_records, data_reconciliation_*, lark_user_tokens (mã hoá)… Migration: apps/api/migrations
KV ANNA_KV Khoá gửi-một-lần, con trỏ đồng bộ, cache ngắn
Queues one-anna-lark-events (event Lark), one-anna-media (ảnh/tệp), one-anna-jobs (job định kỳ nặng), one-anna-dlq (thư lỗi)

Bảng lark_api_usage có trong tài liệu cũ (migration 008) nhưng không thấy trên D1 live — cần xác nhận.

7. Trigger và cron

Nguồn sự thật: config/triggers.registry.mjs; trang đầy đủ docs/04-van-hanh/triggers.md sinh bằng node tools/triggers/render.mjs (kiểm: node tools/triggers/check.mjs). Tổng: 71 job theo lịch · 35 cron · 17 event Base · 10 webhook.

  • Bật/tắt/dry-run một job không cần deploy: ghi bảng D1 trigger_switches (tên job, enabled, dry_run, lý do, người sửa).
  • Job nặng chạy qua Queue one-anna-jobs cho các cron trong JOBS_QUEUE_CRONS. Cột "Chạy bằng": native = trong Worker, GAS = còn gọi Apps Script.
  • Lịch họp: meetingLarkReconcile mỗi 5 phút, nhắc họp chạy cùng lượt.

7.1 Job theo lịch

Job — mô tả Nhóm Tác động Giờ chạy (VN) Chạy bằng
runDailySync — Đồng bộ giờ cửa hàng → bảng 3.11 Chấm công sync 02:00 VN hằng ngày GAS
runMonthEndCatchUp — Chốt kỳ địa điểm tháng trước → bảng 3.11 (chỉ ngày 1) Chấm công seed 00:30 VN hằng ngày GAS
runMonthlySummary — Tổng hợp bảng 3.12 Chấm công sync 22:15 VN hằng ngày GAS
scanDuplicateRecords — Quét bản ghi trùng 3.4/3.6/3.7 mỗi 30 phút 06:00–23:00 VN → DM thẻ có nút xoá (chỉ nút mới xoá; mặc định dry-run) Chấm công notify 0,30 23 * * *
mỗi 30 phút, 07–22h VN
23:00 VN hằng ngày
native
seed36StoreGapsDaily — 02:35 VN — seed 3.6 Kiểm tra bảng công ngày cho hôm qua (NV Cửa hàng ∩ đang làm 2.3); thay workflow Lark Chấm công seed 02:30 VN hằng ngày native
seed36StoreGapsToday — 15:00 VN — seed 3.6 cho HÔM NAY (workflow Lark đã tắt 11/10); khoá chung + đếm trùng sau khi tạo Chấm công seed 15:00 VN hằng ngày native
syncAttendanceCacheBatch — Quét chấm công tháng hiện tại → D1 employee_attendance_cache Chấm công cache mỗi 15 phút, 07–19h VN native
syncNoAccountTimesheet — Phiếu công NV chưa có tài khoản (Approved, chưa Done) → bảng công chi tiết, 20 phút/lần Chấm công sync mỗi 20 phút cả ngày native
bcltLockBackfillOnce — MỘT LẦN (28/09): bù BC → BCLT cho record BC có event trong ngày bị kẹt Queue — upsert ép ghi theo khoá, không xoá; kết quả KV bclt:backfill:20260928, xong thì tự bỏ qua CSKH reconcile — native
cskhArchiveHourly — Sao lưu BC Base CSKH → bảng LT mỗi giờ CSKH archive phút 00 mỗi giờ GAS
cskhAuditToBase — Mỗi giờ :45 — chép nhật ký sửa ô CSKH (D1 cskh_write_log, con trỏ KV) sang Base "LOG - Nhật ký chỉnh sửa CSKH" CSKH sync phút 45 mỗi giờ native
cskhBcltLockPendingDrain — Chạy tiếp event khoá BC → BCLT dở (hàng chờ D1) — chỉ tạo dòng BCLT thiếu CSKH reconcile mỗi 5 phút cả ngày native
cskhBcltLockReconcile — Khoá dòng BC → BCLT (đối soát định kỳ) CSKH reconcile phút 00 mỗi giờ native
cskhCameraRemindCron — Nhắc hạn CAMERA (1 lần / bản ghi) CSKH notify mỗi 20 phút cả ngày native
cskhHeatmapFreezeD1 — Heatmap CSKH: đóng băng đếm thô ngày D-1 → HM tổng/AM/cửa hàng (00:05 VN) CSKH sync 00:05 VN hằng ngày native
cskhMapsRemindCron — Nhắc hạn MAPS (1 lần / bản ghi) CSKH notify mỗi 20 phút cả ngày native
cskhOpsDayDigests — Digest TRỰC PAGE / MAPS / HAPPY CALL 17:00 CSKH notify 17:00 VN hằng ngày native
cskhPagesRemindCron — Nhắc hạn TRỰC PAGE (1 lần / bản ghi) CSKH notify mỗi 20 phút cả ngày native
cskhSaiSoAmNoteDigest — Digest SAI SỐ cho AM 17:00 CSKH notify 17:00 VN hằng ngày native
cskhSyncBcCameraAllStoreStartOfMonth — BC Camera toàn hệ thống: seed mùng 1 VN (tháng mới) CSKH seed 02:00 VN hằng ngày GAS
cskhSyncBcCameraStaffDaily — BC Camera theo NV: bổ sung NV mới CSKH seed 02:00 VN hằng ngày GAS
cskhSyncBcCameraStaffStartOfMonth — BC Camera theo NV: seed NV cửa hàng (chạy hằng ngày 01:00 VN, create-if-missing) CSKH seed 01:00 VN hằng ngày GAS
cskhSyncBcCdntKtvDaily — BC ĐIỂM CĐNT – KTV: bổ sung NV mới 08:00 VN CSKH seed 08:00 VN hằng ngày native
cskhSyncBcCdntKtvStartOfMonth — BC ĐIỂM CĐNT – KTV: seed đầu tháng từ 0.1 (Vị trí làm việc = Kỹ thuật viên) CSKH seed 08:00 VN hằng ngày native
cskhSyncBcCdntStylistDaily — BC ĐIỂM CĐNT – STYLIST: bổ sung NV mới 08:00 VN CSKH seed 08:00 VN hằng ngày native
cskhSyncBcCdntStylistStartOfMonth — BC ĐIỂM CĐNT – STYLIST: seed đầu tháng từ 0.1 (Vị trí làm việc = Stylist) CSKH seed 08:00 VN hằng ngày native
cskhSyncBcKtvDaily — BC Nhức mắt – KTV: bổ sung hằng ngày CSKH seed 05:00 VN hằng ngày GAS
cskhSyncBcKtvStartOfMonth — BC Nhức mắt – KTV: seed đầu tháng CSKH seed 02:30 VN hằng ngày GAS
cskhSyncBcNhucMatMonthly — Đồng bộ 2.1 Nhức mắt → BC mỗi giờ CSKH sync phút 00 mỗi giờ 08–22h VN GAS
cskhSyncBcSlCuocGoiSaiSoStartOfMonth — SL cuộc gọi – Sai số: seed từng ngày × cửa hàng ngày 1 CSKH seed 03:00 VN hằng ngày GAS
cskhTrucPageLateDigest — TRỰC PAGE lượt hai 23:05 CSKH notify 23:05 VN hằng ngày native
reconcileNhucMatNotify — Gửi bù DM KTV 2.1 Nhức mắt bị sót (24h gần nhất) CSKH reconcile mỗi 20 phút cả ngày native
runCskhPageWriteGuard — Ghi bù giải trình TRỰC PAGE bị mất CSKH reconcile mỗi 20 phút cả ngày native
runCskhWriteReconcile — Ghi bù lượt ghi ô lỗi kỹ thuật (từ cskh_write_log) CSKH reconcile mỗi 20 phút cả ngày native
runLichHenKinhNotify — Lịch hẹn kính trong ngày → AM + KTV + Stylist CSKH notify mỗi 20 phút cả ngày
09:30 VN hằng ngày
native
runLichHenKinhRemind — Nhắc trước giờ hẹn 3 tiếng CSKH notify mỗi 20 phút cả ngày native
refreshRsmDataCache_ — Làm nóng cache RSM (target/daily/am/ssp) — GAS, thôi lên lịch 12/10 (vượt bộ nhớ) Doanh thu cache — GAS
refreshRsmDataNative — 05:05/14:05/18:05 VN — làm mới ảnh chụp D1 doanh thu + mốc đồng bộ (thay refreshRsmDataCache_) Doanh thu cache 05:05 VN hằng ngày
14:05 VN hằng ngày
18:05 VN hằng ngày
native
revArchiveScheduled — Sao lưu BC Doanh thu → bảng LT (05/14/20h) Doanh thu archive 05:00 VN hằng ngày
14:00 VN hằng ngày
20:00 VN hằng ngày
GAS
runNopTienVpRemind — 14:00 VN — DM QLCH bản ghi nộp tiền mặt chưa xác nhận (nút Đã nộp tiền → Trạng thái update) Doanh thu notify 14:00 VN hằng ngày native
runRevenueDailySeed — Tạo khung 2.2 Doanh số ngày theo Danh sách cửa hàng (hiện ghi bảng Test) Doanh thu seed 01:00 VN hằng ngày native
runRevenueDailySync — Báo cáo doanh thu theo cửa hàng → 2.2 Doanh số ngày mỗi 5 phút (hiện ghi bảng Test) Doanh thu sync mỗi 5 phút cả ngày native
runDataReconciliationAts — Base ↔ ONE-ANNA: tuyển dụng Đối soát reconcile phút 08 mỗi giờ native
runDataReconciliationCskhBc — Base ↔ ONE-ANNA: BC CSKH (bảng lớn, cron riêng mỗi giờ) Đối soát reconcile phút 25 mỗi giờ native
runDataReconciliationCskhOps — Base ↔ ONE-ANNA: bảng nghiệp vụ CSKH Đối soát reconcile phút 35 mỗi giờ native
runDataReconciliationHrPayroll — Base ↔ ONE-ANNA: HR/lương Đối soát reconcile phút 08 mỗi giờ native
runDataReconciliationRsm — Base ↔ ONE-ANNA: RSM Đối soát reconcile phút 52 mỗi giờ native
runReconciliationSummary — Báo cáo đối soát hằng ngày 09:00 (đọc D1) Đối soát notify 09:00 VN hằng ngày native
createTestBaseCskhOnce — MỘT LẦN: sao chép cấu trúc Base CSKH (không dữ liệu) làm Base thử nghiệm, tắt automation, cấp quyền Tùng; trạng thái KV test_base:cskh, xong thì tự bỏ qua Hệ thống seed — native
pruneWebhookInbox — Hằng ngày 03:00 VN, xoá theo khúc (D1): webhook_inbox cũ hơn 7 ngày (500 dòng × ≤40), lark_event_dedup cũ hơn 3 ngày (5000 × ≤80), trigger_runs cũ hơn 14 ngày Hệ thống cleanup 03:00 VN hằng ngày native
runDailyCatchUp — Ké cron :10/:30/:50 — job 1 lần/ngày (HRIS, cảnh báo HĐ) có lượt gần nhất hôm nay lỗi → chạy bù tuần tự, tối đa 3 lượt/ngày/job (cron ghi catchup) Hệ thống reconcile 10-59/20 0-16,22-23 * * * native
runJobHealthCheck — Kiểm job lịch lỡ lịch (24h) / lần cuối lỗi theo D1 scheduled_job_locks → DM JOB_ALERT_USER_ID, 1 lần/ngày từ 09:30 VN; im khi ổn Hệ thống notify 10-59/20 0-16,22-23 * * * native
runLoadAlertCheck — Cảnh báo tải Worker mỗi 5 phút (event/phút, vòng lặp tự kích, job bị cắt, cron > 10 phút, KV lỗi, Lark rate-limit, exception) → DM JOB_ALERT_USER_ID, 1 lần/giờ/loại; im khi ổn Hệ thống notify mỗi 5 phút cả ngày native
watchBaseSchema — Phát hiện cột Base bị đổi tên/kiểu Hệ thống reconcile 02:00 VN hằng ngày native
runBirthdayNotify — Sinh nhật Leader (tuần + D-1/D0) HR notify 09:00 VN hằng ngày native
runContractExpiryNotify — Cảnh báo hợp đồng sắp hết hạn HR notify 08:30 VN hằng ngày native
runHrisPersonnelSync — HRIS: bảo đảm 2.3/6.1 + hợp đồng theo trạng thái HR sync 08:30 VN hằng ngày native
runKpiMonthlySeed — Seed KPI 1.3 đầu tháng (chỉ ngày 1 VN) KPI seed 02:00 VN hằng ngày native
meetingLarkReconcile — Mỗi 5 phút: người đặt xoá/từ chối trên Lark Calendar hoặc sự kiện bị xoá → huỷ lịch ở D1 + xoá sự kiện lịch bot (trả phòng); không ghi Base Lịch họp reconcile mỗi 5 phút cả ngày native
backfillMonthlyPayrollSheets — Bổ sung dòng tháng (3.0/3.1/4.0.1/5.2) cho NS mới còn hiệu lực — idempotent, sau runHrisPersonnelSync Lương seed 08:30 VN hằng ngày GAS
checkSnapshotDrift — Phát hiện bảng 3.0 bị sửa sau khi đã chốt snapshot Lương reconcile 02:00 VN hằng ngày native
drainPayrollOutbox — Đẩy các lượt xác nhận phiếu công/lương đang chờ lên Base Lương sync mỗi 15 phút, 07–19h VN
phút 00 mỗi giờ
native
healDriftedPayroll31 — Snapshot drift → đồng bộ lại 3.1 (hoặc release khi HR xoá XN); tối đa 20 dòng/lượt, tháng hiện tại + tháng trước Lương sync 02:00 VN hằng ngày native
runVisitorMissReportDigest — Cửa hàng chưa báo cáo lượt khách Lượt khách notify 09:00 VN hằng ngày native
runVisitorSeedGuard — Lưới seed lượt khách: dòng ngày hôm qua+hôm nay, tuần hiện tại (từ T2 07:00), backfill theo khoá KV visitor_seed:backfill Lượt khách seed 10-59/20 0-16,22-23 * * * native
seedVisitorDetailDaily — Seed chi tiết lượt khách hôm qua + hôm nay (gọi tay; cron đi qua runVisitorSeedGuard) Lượt khách seed — native
seedVisitorWeek — Seed báo cáo lượt khách tuần (chỉ Thứ Hai) Lượt khách seed 06:00 VN hằng ngày native
cskhKtvAutoGrantCron — Tự cấp Role KTV cho Mã KTV mới Phân quyền grant 08:30 VN hằng ngày GAS
cskhQlchAutoGrantCron — Tự cấp Role QLCH Phân quyền grant 08:30 VN hằng ngày GAS
reconcileHoanTienNotify — Gửi bù thẻ Hoàn tiền bị sót Vận hành reconcile mỗi 20 phút cả ngày native
replayLostVhImgErr — Mỗi 5 phút: xả event P10 kẹt (lỗi waitUntil cũ) — chỉ phiếu trong ngày hôm nay, qua cổng điểm/ngày/khoá once rồi đẩy MEDIA_Q; event ngày cũ → expired, không gửi Vận hành notify mỗi 5 phút cả ngày native

7.2 Event Base (bản ghi thay đổi → Worker xử lý)

Bảng Handler Làm gì
2.2 MAPS handleMapsEvent Tick Gửi thông báo → DM; Điểm trừ → TOÀ TUYÊN ÁN
4.1 CAMERA handleCameraEvent Tick Gửi thông báo + stamp khi QLCH Xác nhận
3.1 TRỰC PAGE handlePagesEvent Tick Gửi thông báo → DM AM+QLCH; QLCH Xử lý → stamp + DM
1.3 KHÔNG HÀI LÒNG – HAPPY CALL handleHappyCallUnhappyEvent Dòng mới/sửa đủ điều kiện → thông báo
2.1 XỬ LÝ NHỨC MẮT handleNhucMatEvent Dòng mới → DM KTV + stamp Thời gian gửi thông báo KTV; sửa cột CS → trả lời trong thread
1.5 LỊCH HẸN KÍNH handleLichHenPhkhEvent Tick Gửi thông báo → phiếu; AM ghi chú → cập nhật phiếu
1.2 SAI SỐ handleSaiSoXuLyEvent Tick Chuyển thông tin → DM AM+QLCH; ghi chú → stamp thời gian
Vận hành – Phiếu chấm 10 tiêu chí handleVanHanhImgErrEventSplit Điểm KSNB ≠ 100 → DM + Group
Vận hành – Hoàn tiền handleHoanTienEvent Approved → thẻ; KT điền → DM người yêu cầu
BC ĐIỂM CĐNT handleBcltLockEventMany Khoá dòng BC → BCLT
BC ĐIỂM PHỤC VỤ P.TNKH handleBcltLockEventMany Khoá dòng BC → BCLT
BC ĐIỂM PHỤC VỤ CỤM handleBcltLockEventMany Khoá dòng BC → BCLT
BC ĐIỂM PHỤC VỤ HỆ THỐNG handleBcltLockEventMany Khoá dòng BC → BCLT
BC SL CUỘC GỌI – SAI SỐ handleBcltLockEventMany Khoá dòng BC → BCLT
BC Hoàn thành target cuộc gọi CSKH handleBcltLockEventMany Khoá dòng BC → BCLT
BC ĐIỂM CĐNT – KTV handleBcltLockEventMany Khoá dòng BC → BCLT
BC ĐIỂM CĐNT – STYLIST handleBcltLockEventMany Khoá dòng BC → BCLT

7.3 Webhook HTTP

Đường dẫn Ai gọi Làm gì
/cron Gọi tay / công cụ Chạy một cron theo biểu thức
/webhook Lark Event Subscription + thẻ tương tác Cửa chung cho event Base (xem EVENTS) và card action
/api/webhook/base-attendance Lark Automation Chấm công từ Base
/api/webhook/payroll-unlock Lark Automation Mở khoá phiếu lương
/api/webhook/timesheet-detail Lark Automation Chi tiết bảng công
/api/webhook/cskh-notify Lark Automation Dispatch thông báo CSKH (song song với event — chỉ bật MỘT đường)
/api/webhook/van-hanh-img-err Lark Automation Lỗi hình ảnh vận hành
/api/webhook/hoan-tien Lark Automation Hoàn tiền cho khách
/api/webhook/dao-tao-hr-enrich Lark Automation AddRecord ×5 bảng Đào tạo employer_id → HRIS 2.1 → Khối phòng ban/Vị trí/Leader (chỉ ô trống)
/api/run Gọi tay / công cụ Chạy một job theo tên

8. Deploy

Chỉ Tùng deploy, chỉ một đường: npm run deploy. Không chạy wrangler deploy trực tiếp (bị [build] preflight chặn). Mọi người làm trên nhánh feat/lich-hop trong ~/code/"ANNA 2".

Trước khi deploy 1. git pull, commit + push hết (cây sạch). 2. Chạy kiểm tra (/deploy-check: test mức full, xem access_log 15 phút gần nhất). 3. Giờ an toàn: 03:15–05:45 hoặc 23:15–00:15 (VN); tránh cao điểm 09h, 14h, 16h và phút cron chạy. 4. Có migration mới → chạy trước: cd apps/api && npm run d1:migrate:<số>.

Lệnh

cd ~/code/"ANNA 2" && git pull
cd apps/api && npm run deploy:dry   # chỉ kiểm, không đẩy
npm run deploy                      # hoặc: npm run deploy -- --note "lý do"
scripts/deploy.mjs từ chối nếu: cây bẩn · sai nhánh · HEAD chưa push · bản live (/api/version) không phải tổ tiên của HEAD · khoá D1 deploy_lock đang bị giữ (tự hết hạn 20 phút). Qua cổng: đóng gói GAS → build lịch họp → chuẩn bị public → test → build-info → sync → wrangler deploy → chờ /api/version = HEAD → ghi deploy_log (D1) → nhả khoá. Ghi thêm một dòng vào .claude/plans/deploy-log.md.

Sau deploy 30 phút: node tools/regression/run.mjs --live (chỉ đọc); xem trigger_runs, cskh_write_log/rpc_write_log; mở thử mọi module bằng tài khoản thử đủ vai trò (NV, QLCH, AM, HR).

Quay lại: npx wrangler rollback (Tùng) rồi git revert; hành vi sau cờ tính năng → tắt cờ; trigger lỗi → tắt bằng trigger_switches.

9. Điểm cần xác nhận (toàn dự án)

  • Thời hạn phiên đăng nhập.
  • Tài khoản thử theo vai trò (NV, QLCH, AM, HR) có sẵn hay chưa — deploy.md nhắc tới nhưng không ghi tên.
  • Bảng lark_api_usage (migration 008) không có trên D1 live.
  • Bảng HRIS 2.5 và 0.0.2 Sơ đồ tổ chức không thấy code ONE-ANNA dùng.
  • KSNB đang dùng dữ liệu mẫu; Chứng từ mới ở giai đoạn 1.
  • Module noti-hoso-lan1..3 và config/automations/ đang làm dở trên máy Tùng, chưa commit — chưa đưa vào tài liệu.
  • Ghi doanh số ngày đang vào bảng 2.2 - Doanh số ngày Test.