Module · WS đào tạo · 07/7/2026

dat — Thiết bị & dữ liệu DAT MVP Painkiller

Nhận dữ liệu phiên học từ vendor DAT, đối soát km/giờ theo hạng, cảnh báo thiếu chỉ tiêu trước sát hạch và phát hiện lệch GV/xe so với đăng ký Sở. Dữ liệu DAT là căn cứ xét hoàn thành khóa học (NĐ 94/2026).

✅ Trạng thái code: đã scaffold device + log — cần thêm session, reconcile, alert (lõi giá trị).
📦 Cập nhật 03/8 — đối chiếu backend DAT đang chạy (repo backend, branch develop): đặc tả trường thật của 8 đối tượng ở mục 4 — phát hiện 2 đối tượng thiếu (dat_device_component, history_data), trạng thái phiên là enum 3 giá trị chứ không phải bool, binding chưa có lịch sử, và chuẩn km/giờ nằm ngoài DB (Node-RED).
⚠️ Cập nhật 20/7 — dat thu trọn tầng "cảm biến & định danh": feature binding (gắn/tháo thiết bị vào xe) chuyển từ vehicle về đây; vehicle bỏ feature device-bind và hai cột seri_dat/dat_device_id. Lý do đầy đủ + lộ trình module device dùng chung: Kiến trúc quản lý thiết bị 3 tầng.
1

Feature / BO / Action

FeatureBO chínhAction
deviceThiết bị DAT (seri, sim, hạn)viewcreateeditdelete
component TỪ BACKENDLinh kiện thiết bị (thẻ nhớ… — seri riêng, tháo rời được)viewcreateeditassign
binding CHUYỂN TỪ vehicleGắn/tháo thiết bị vào xe (lịch sử theo thời điểm)viewassignexport
sessionPhiên học DAT (km/giờ)viewimportexcludeexport
reconcileKết quả đối soát theo học viên/hạngviewreconcilereport
alertCảnh báo thiếu km/giờ, lệch GV/xeviewconfiguresend
logNhật ký truyền/kết nốiview
2

Luồng đối soát

vendor DAT / hệ SUNS ──import/push──▶ dat_phien_hoc UNIQUE(nguon, phien_id_goc) │ đối chiếu phân công (training) + lịch sử gắn thiết bị (nội bộ — dat_gan_xe) ├─ khớp → hop_le = true ─┐ └─ lệch GV/xe → hop_le = false + lý do (danh sách xem xét) ▼ Σ km/giờ hợp lệ theo học viên ⟷ required_km/hours của hạng (catalog) ▼ % hoàn thành · còn thiếu · dự báo trước ngày sát hạch ──▶ alert → notify · report · payroll
3

Dữ liệu chính — phiên học

TrườngKiểuGhi chú
Nguồn + ID phiên gốctextUNIQUE — import idempotent, chạy lại không nhân đôi
Học viên / khóa / GV / xekhóa NVma_dk · ma_kh · ma_gv · ma_xe
Bắt đầu / kết thúcdatetime
Quãng đường (km) / thời lượng (phút)number
Trạng thái phiên + lý doenum·textVALID · WARNING (vùng xám, cần người xem lại) · INVALID — theo backend đang chạy (mục 4.4), không phải bool. Lệch GV/xe, trùng giờ, tỷ lệ nhận diện thấp → hạ trạng thái, ghi rõ lý do
Nhận diện khuôn mặtint·intSố lần / số lần thành công — tỷ lệ thấp là căn cứ đánh WARNING
Điểm GPS chi tiếtBO phụhistory_data — nguồn thật để tính km/giờ (mục 4.5); phiên chỉ giữ số cộng dồn

device (đã có): seri · số SIM · loại thiết bị · ngày bàn giao · ngày hết hạn (hợp quy/dịch vụ) · trạng thái.

bindingdat_gan_xe (mới, chuyển từ vehicle): seri thiết bị · ma_xe · từ ngày → đến ngày · người thao tác. Append-only — căn cứ quy phiên học về đúng xe theo đúng thời điểm. Thiết bị chưa gắn xe nào (dự phòng/bảo hành) = không có dòng mở.

⚠️ Hai quy tắc giữ cho bảng thiết bị "tách được" về sau (chuẩn bị cho module device dùng chung — kiến trúc 3 tầng §5): ① dat_thiet_bi chỉ chứa thuộc tính chung của một thiết bịkhông để cột nghiệp vụ phiên học/đối soát lẫn vào; ② seri là khóa toàn cục do SUNS cấp phát (duy nhất trên mọi khách hàng, không phải trong một WS) — điều kiện để thiết bị chuyển trung tâm mà vẫn truy được lịch sử.
4

Đặc tả trường — đối chiếu backend DAT đang chạy MỚI 03/8

Nguồn: repo backend (branch develop) — Sequelize models + mapping cột thật. Đây là hệ DAT đang vận hành, dùng làm chuẩn để thiết kế module dat trên OpenGate.

▸ 4.1 Bản đồ đối tượng

ModelBảng thậtVai tròTrong thiết kế
DatDevicedat_deviceThiết bị DAT✅ feature device
DatDeviceComponentdat_device_componentLinh kiện rời của thiết bịthiếu → thêm feature component
RollCallStudentdiem_danh_nguoi_lxPhiên học DAT (login→logout)✅ feature session — nhưng mô tả quá sơ sài
HistoryDatahistory_dataĐiểm GPS trong phiên — nguồn tính km/giờthiếu → BO phụ của session
DatAppConnectionLogdat_app_connection_logsNhật ký kết nối thiết bị ↔ app✅ feature log
TrainingVehiclexe_tap_laiXe tập lái — giữ khóa gắn thiết bị→ vehicle + dat.binding
VehicleInfoHistoryxe_tap_lai_historyAudit đổi thông tin xe (changes JSONB)→ vehicle
Student (một phần)nguoi_lxTiến độ km/giờ tích lũy→ nguồn feature reconcile

▸ 4.2 dat_device — thiết bị

CộtKiểuGhi chú
idint PKCấp qua bảng đếm DataIndex (bắt đầu 100) — không dùng sequence
so_serivarchar(100)Tự sinh tiền tố DAT- + chuỗi từ generate-serial-key nếu bỏ trống — khớp đề xuất "seri do nhà cung cấp cấp phát"
so_simvarchar(100)SIM dữ liệu
loai_thiet_bivarchar(50)mặc định 'DAT'đã chừa chỗ cho họ thiết bị khác (chip, sân bãi)
ngay_ban_giao · ngay_het_handateBàn giao / hết hạn hợp quy–dịch vụ
statusbool⚠️ Chỉ bật/tắt — không đủ cho các trạng thái thật: kho, đang gắn, bảo hành, hỏng

Quan hệ: hasOne TrainingVehicle (gắn xe, SET NULL khi xóa) · hasMany DatDeviceComponent. API: /dat-device/:id/assign-vehicle · /update-dat-assignment · /device/get-not-assigned · /available-components.

▸ 4.3 dat_device_component — linh kiện CHƯA CÓ TRONG THIẾT KẾ

CộtKiểuGhi chú
so_serivarchar(100) NOT NULLLinh kiện có seri riêng, truy vết độc lập với thiết bị mẹ
loai_thiet_bienumHiện chỉ storage (thẻ nhớ) — enum để mở rộng
dat_device_idint NULLNULL = linh kiện rời trong kho, chưa lắp vào thiết bị nào
✅ Mô hình thiết bị ⊃ linh kiện tháo rời, mỗi cấp một seri đúng là thứ tầng ① (WS nhà cung cấp) cần khi SUNS tự sản xuất — xem kiến trúc 3 tầng. Backend đang chạy đã đi trước thiết kế ở điểm này.

▸ 4.4 diem_danh_nguoi_lx — phiên học (BO chính của session)

NhómCột
Định danhdiem_danh_nguoi_lx_id (UUID PK) · diem_danh_giao_vien_id (UUID — phiên của GV, 1 phiên GV có nhiều phiên HV) · phien_dao_tao · thu_tu_phien · active_phien
Chủ thểma_dk · nguoi_lx_id · ma_gv · giao_vien_id · bien_so · vehicle_id · ma_csdtvừa khóa nghiệp vụ vừa id, khớp pattern P2 (chụp tại thời điểm)
Mở phiênthoi_diem · lat · lng · path_image_login
Đóng phiênlogout (bool) · thoi_diem_logout · lat_logout · lng_logout · path_image · thoi_diem_last_data
Nhận diệnso_lan_nhan_dien · so_lan_nhan_dien_thanh_congtỷ lệ nhận diện là chỉ số chất lượng phiên, căn cứ đánh WARNING
Số liệu km/giờTổng: tong_quang_duong · tong_thoi_gian
Trong phiên: tong_quang_duong_ld_phien · tong_thoi_gian_ld_phien
Ban đêm: tong_quang_duong_ld_ban_dem · tong_thoi_gian_ld_ban_dem
Hạng B11: quang_duong_b_11 · thoi_gian_b_11
Hợp lệsession_status enum VALID · WARNING · INVALID — API PUT /roll-call-student/:uuid/status cho phép sửa tay

▸ 4.5 history_data — điểm GPS trong phiên CHƯA CÓ TRONG THIẾT KẾ

BO phụ của session (RollCallStudent hasMany HistoryData) — đây mới là nguồn tính km/giờ, phiên chỉ giữ số cộng dồn. Khối lượng lớn nhất hệ.

NhómCột
Định danhhistory_id (UUID, cấp qua DataIndex) · diem_danh_nguoi_lx_id (FK phiên) · imei
Điểm đothoi_diem · lat · lng · van_toc · quang_duong · path_image (ảnh nhận diện tại điểm)
Cộng dồntong_quang_duong_ld_phien · tong_thoi_gian_ld_phien · tong_quang_duong_ld
Phân loạiis_night (tính giờ ban đêm) · is_manual (nhập tay — cờ quan trọng cho đối soát/thanh tra) · hang_dao_tao · phien_dao_tao
Chụp tại thời điểmbien_so · ma_dk · nguoi_lx_id · giao_vien_id · ma_csdt · vehicle_id

▸ 4.6 Tiến độ tích lũy ở nguoi_lx — nguồn của reconcile

tong_quang_duong_ld · tong_thoi_gian_ld · quang_duong_ban_dem · thoi_gian_ban_dem · quang_duong_b_11 · thoi_gian_b_11 · thoi_diem_bat_dau_ld · thoi_diem_tap_cuoi_ld · thoi_diem_chay_dat · thoi_diem_ket_thuc_dat · count_send_data · send_tong_cuc · is_dao_tao. Định danh sinh trắc: is_face_id · is_finger · is_rfid · rfid · so_the_rfid · add_date_rfid. Xe: ma_xe_tap · ma_xe_tap_phu · xe_b_11.

▸ 4.7 Nhật ký kết nối — dat_app_connection_logs

Cột: payload · server_message · log_type · status · sub_status + timestamps. Trạng thái đã có: SUCCESS · NOT_FOUND_DEVICE (sub: NOT_REGISTERED, COLLISION) · INVALID_DEVICE · DEVICE_REGISTERED. Giữ nguyên bộ mã này khi làm module — đây là ngôn ngữ kỹ thuật viên đã quen.

▸ 4.8 Bốn phát hiện làm đổi thiết kế

hop_le bool → 3 trạng thái

  • Thực tế là VALID · WARNING · INVALIDWARNING là vùng xám cần người xem lại (tỷ lệ nhận diện thấp, dữ liệu ngắt quãng)
  • Có API sửa tay ⇒ phải kèm lý do + lịch sử + quyền riêng (đúng như action exclude đã thiết kế)
  • → Sửa mục 3: bỏ cột bool, dùng enum 3 giá trị

② Thiếu hẳn 2 đối tượng

  • dat_device_component — linh kiện có seri riêng, tháo rời (thẻ nhớ)
  • history_data — điểm GPS, nguồn thật của km/giờ; phiên chỉ là số cộng dồn
  • → Thêm feature component; history_data là BO phụ của session

③ Binding không có lịch sử

  • Backend gắn xe bằng xe_tap_lai.dat_device_id — quan hệ 1-1 hiện tại, SET NULL khi tháo
  • Mất dấu quá khứ: thiết bị đổi xe thì không quy được phiên cũ về đúng xe
  • Bảng xe_tap_laicả dat_device_id lẫn dat_device_serial — đúng mâu thuẫn review §2.3 đã cảnh báo, nay có bằng chứng từ hệ chạy thật
  • → Củng cố đề xuất dat_gan_xe có từ ngày–đến ngày

④ Chuẩn km/giờ nằm ngoài DB

  • Lấy qua Node-RED: /metatek/training-category · /metatek/training-requirements
  • Dịch vụ ngoài chết → không đối soát được, không có bản dự phòng trong DB
  • → Đưa chuẩn về catalog_v_hang_gplx (có hiệu lực theo thời gian), Node-RED chỉ còn là nguồn đồng bộ
5

Quy tắc & liên kết

Quy tắc nghiệp vụ

  • Connector theo vendor (plug-in) — mỗi nhà cung cấp 1 adapter. Khi SUNS tự làm thiết bị → chuyển sang nhận push từ hệ SUNS (gần thời gian thực) thay vì kéo theo lịch ⇒ phải chốt format sự kiện chuẩn sớm: đây là hợp đồng phần cứng–phần mềm, sửa sau phải cập nhật firmware ngoài hiện trường
  • Lệch phân công đã đăng ký → hop_le=false, không tính đối soát
  • Trùng giờ cùng học viên/xe → giữ 1, loại phần trùng
  • exclude/khôi phục = quyền riêng + ghi lịch sử
  • Kết quả đối soát = đầu vào report (xét hoàn thành) + payroll (giờ dạy GV)

Liên kết chéo

  • trainingma_dk (tiến độ hồ sơ) · ma_kh (đối chiếu phân công)
  • teacher/vehiclema_gv/ma_xe (P1 chỉ-đọc). Chiều ngược: vehicle đọc dat_v_thiet_bi_theo_xe để xét điều kiện dạy — dat chưa cài thì bỏ qua điều kiện, không crash
  • catalog — km/giờ chuẩn theo hạng
  • notify — gửi cảnh báo · report — căn cứ tốt nghiệp

⚠ Câu hỏi mở

  • Vendor DAT của Vĩnh An + định dạng dữ liệu (API/file) → connector đầu tiên
  • Chuẩn "phiên hợp lệ" chi tiết theo quy định hiện hành
Quyền

Ma trận phân quyền — ốp vào manifest & seed role

Khối permissions[] dán vào manifest.json; bảng dưới là gán quyền theo vai trò (seed rt_role). Permission key đầy đủ: dat.main.<feature>.<action>.

▸ permissions[] — manifest.json

[ { "group": "main", "feature": "device", "label": "Thiết bị DAT", "actions": [ "view", "create", "edit", "delete" ] }, { "group": "main", "feature": "component", "label": "Linh kiện thiết bị", "actions": [ "view", "create", "edit", "assign" ] }, { "group": "main", "feature": "binding", "label": "Gắn/tháo thiết bị", "actions": [ "view", "assign", "export" ] }, { "group": "main", "feature": "session", "label": "Phiên học DAT", "actions": [ "view", "import", "exclude", "export" ] }, { "group": "main", "feature": "reconcile", "label": "Đối soát km/giờ", "actions": [ "view", "reconcile", "report" ] }, { "group": "main", "feature": "alert", "label": "Cảnh báo", "actions": [ "view", "configure", "send" ] }, { "group": "main", "feature": "log", "label": "Nhật ký truyền", "actions": [ "view" ] } ]

⚠️ Action đặc thù (exclude, reconcile, send) cần bổ sung vào validator manifest — xem plan data-scope P1.

▸ Ma trận vai trò × feature

FeatureQTHTGIAM_DOCKY_THUATQL_DAO_TAOGIAO_VUGIAO_VIENHOC_VIEN
deviceRRW
componentRRW
bindingRRWRR
sessionRRRWR¹
reconcileRRWWR¹R²
alertRRWR
logRRR

R xem · W tạo/sửa · A duyệt · không có quyền · ¹ dữ liệu phân công · ² của chính mình · ³ theo trạng thái hồ sơ. Vai trò không liệt kê = không có quyền.

Đặc thù: component +assign (KY_THUAT) — lắp/tháo linh kiện vào thiết bị · binding +assign (KY_THUAT) — port từ vehicle.device-bind cũ · session +import,exclude (GIAO_VU) · reconcile +reconcile (QL_DAO_TAO, GIAO_VU) · alert +send,configure (QL_DAO_TAO)

▸ Quyền mức record (row-level)

(a) Manifest bổ sung fields + scopeVars cho feature bị scope (mở rộng schema — plan data-scope P1; manifest chỉ khai "cái gì scope được", không chứa rule):

[ { "group": "main", "feature": "session", "fields": [ "ma_gv", "ma_dk", "ma_kh" ], "scopeVars": [ "current_ma_gv", "current_ma_dk_list" ] }, { "group": "main", "feature": "reconcile", "fields": [ "ma_dk", "ma_gv" ], "scopeVars": [ "current_ma_gv", "current_ma_dk_list" ] } ]

(b) Điều kiện record theo vai trò:

Vai tròFeature (action)Điều kiện record
GIAO_VIENsession, reconcile (view)ma_gv = ctx.current_ma_gv
HOC_VIENreconcile (view)ma_dk ∈ ctx.current_ma_dk_list (các lượt đăng ký của mình)

(c) Seed rt_role.grants (kind rule — nhiều rule cùng resource+action = OR):

[ { "kind": "rule", "module": "dat", "feature": "session", "actions": [ "view" ], "predicate": { "op": "cmp", "field": "ma_gv", "cmp": "=", "value": { "ctx": "current_ma_gv" } } }, { "kind": "rule", "module": "dat", "feature": "reconcile", "actions": [ "view" ], "predicate": { "op": "cmp", "field": "ma_gv", "cmp": "=", "value": { "ctx": "current_ma_gv" } } }, { "kind": "rule", "module": "dat", "feature": "reconcile", "actions": [ "view" ], "predicate": { "op": "in", "field": "ma_dk", "values": [ { "ctx": "current_ma_dk_list" } ] } } ]

Biến ngữ cảnh: current_user_id — engine có sẵn · current_giao_vien_id/current_ma_gv — module teacher tra từ user đăng nhập (ctxVars, plan P3) · current_nguoi_lxid/current_ma_dk_list — module student tra (1 người nhiều lượt đăng ký) · current_assigned_* — danh sách phụ trách/được phân công.