# WS0 — Tổ chức & phân quyền (trên nền OpenGate)

> Cập nhật: 06/7/2026 · Thuộc [Kế hoạch giai đoạn 1](README.md) · Bản trực quan: [`WS0-co-cau-to-chuc.html`](WS0-co-cau-to-chuc.html)
>
> **Đã chỉnh lại theo nền tảng OpenGate.** Điểm mấu chốt: **phần lớn WS0 (multi-tenant, SSO, users, RBAC) ĐÃ CÓ SẴN** ở Control Plane — không tự xây. Việc của ta là **ánh xạ mô hình tổ chức/vai trò trường lái xe vào RBAC của OpenGate** và khai `permissions[]` trong từng module.

---

## 1. OpenGate lo sẵn những gì (không tự làm)

| Hạng mục WS0 (kế hoạch cũ) | OpenGate cung cấp | Ghi chú |
|---|---|---|
| Multi-tenant | **Workspace** — mỗi trung tâm 1 workspace, **DB riêng** (Level 2, cô lập) | Vĩnh An = 1 workspace; trường sau = workspace mới |
| Users + đăng nhập | Control Plane: `User`, `Membership` | Không tự làm bảng user |
| SSO | CP OAuth2 authorization-code, session toàn cục + session workspace | Đăng nhập 1 lần, vào nhiều workspace |
| RBAC | CP: `Role`, `RoleGroup`, catalog `Permission/Feature/FeatureGroup`; module khai quyền | Gán role cho user theo workspace |
| Store/cài module | CP `Store/Entitlements` + runtime cài `.ogapp` | License thương mại per-workspace |

→ **Tiết kiệm lớn** so với 8–12 MM ước tính cho "nền tảng" ở kế hoạch cũ. WS0 giờ chủ yếu là **cấu hình + ánh xạ**, không phải xây mới.

---

## 2. Mô hình quyền của OpenGate

- **Permission key:** `<key>.<group>.<feature>.<action>` — module khai trong `manifest.json` (`permissions[]`).
- **Action hợp lệ:** `view, create, edit, delete, export, import, approve, assign, configure, report, download, manage_role`.
- Backend chặn quyền qua `authorize('<perm-key>')` trên route; frontend ẩn/hiện qua `hasPermission('<perm-key>')`.
- **Role** = bó nhiều permission, gán cho user trong 1 workspace. **RoleGroup** = gom nhiều role.

### Quyền các module domain hiện có (group `main`)
| Module (key) | Feature | Permission (rút gọn) |
|---|---|---|
| `student` | student | `student.main.student.{view,create,edit,delete}` |
| `teacher` | teacher | `teacher.main.teacher.{view,create,edit,delete}` |
| `vehicle` | vehicle | `vehicle.main.vehicle.{...}` |
| `course` | course | `course.main.course.{...}` |
| `dat` | device, log | `dat.main.device.{...}`, `dat.main.log.view` |
| `rfcard` | card | `rfcard.main.card.{...}` |
| `catalog` | license, training-category, educational-level, teaching-subject | `catalog.main.<feature>.{...}` |

> Các module WS1 mới (đối soát DAT, báo cáo Sở, công nợ) sẽ **thêm feature/permission** vào manifest tương ứng — vd `student.main.hoso.approve`, `report.main.so-xd.export`.

---

## 3. Danh mục vai trò → ánh xạ Role OpenGate

Vai trò nghiệp vụ trường lái xe = **Role trong CP**, mỗi role bó các permission module. Một user gán **nhiều role** trong workspace (hợp trung tâm nhỏ kiêm nhiệm).

| Vai trò | Role (đề xuất) | Bó permission chính |
|---|---|---|
| Chủ trung tâm / Giám đốc | `director` | `*.view` toàn bộ + `*.approve` báo cáo + xem BI |
| Quản lý đào tạo / Giáo vụ trưởng | `training_mgr` | student/course/teacher/vehicle `view/create/edit` + `course.*.approve` + báo cáo Sở `create` |
| Nhân viên tuyển sinh | `admission` | `student.main.student.{view,create,edit}` (tiếp nhận hồ sơ) + CRM |
| Nhân viên giáo vụ | `registrar` | `student.*`, `course.*` create/edit + đối soát DAT `view` |
| Giáo viên | `instructor` | `student.main.student.view` (phạm vi phụ trách) + lịch dạy |
| Kế toán | `accountant` | công nợ/hóa đơn/lương `view/create/edit/approve` |
| Nhân sự – Hành chính | `hr` | teacher + nhân sự/chứng chỉ `view/create/edit` |
| Kỹ thuật / Xe – sân | `fleet` | `vehicle.*`, `dat.main.device.*` |
| Cán bộ sát hạch *(nếu có sân)* | `exam_op` | vận hành kỳ thi (module v2+) |
| Học viên | *(không phải role CP)* | truy cập qua **app học viên**, phạm vi dữ liệu của chính mình |

> **Data scope** (chỉ dữ liệu phụ trách / của chính mình) chưa chắc có sẵn ở CP — **cần verify** cơ chế scope của RBAC OpenGate; nếu chưa có, xử lý ở tầng service của module (lọc theo `giao_vien_id`, `nguoi_lxid`).

---

## 4. "Phòng ban" trong OpenGate — quyết định thiết kế

Control Plane **không có khái niệm Department/phòng ban** (chỉ có User/Role/RoleGroup). Ba lựa chọn:

1. **Không mô hình phòng ban** — chỉ dùng Role. Đủ cho phân quyền; đơn giản nhất. *(khuyến nghị cho MVP)*
2. **Dùng RoleGroup như "phòng"** — gom role theo phòng để gán nhanh.
3. **Thêm bảng tổ chức nhẹ trong module** — nếu cần quy trình **bàn giao hồ sơ giữa phòng** (cột `phong_phu_trach_id`, `phong_tu/den_id` ở [schema](schema-du-lieu-2026-07-06.md) §2). Bảng danh mục phòng đặt trong `catalog` hoặc module `student`.

→ **Đề xuất:** MVP đi (1) — phân quyền bằng Role. Chỉ thêm danh mục phòng (3) khi Vĩnh An thực sự cần luồng bàn giao liên phòng (chờ danh sách phòng ban — việc #3).

---

## 5. Việc thực tế của WS0 (đã thu hẹp)

- [ ] Tạo **workspace Vĩnh An** trên Control Plane (DB riêng).
- [ ] Cài các module domain (`catalog`, `student`, `course`, `teacher`, `vehicle`, `dat`, `rfcard`) vào workspace.
- [ ] Định nghĩa **Role** theo §3 (bó permission từ manifest các module).
- [ ] Tạo tài khoản nhân viên (việc #4) + gán role theo workspace.
- [ ] **Verify:** cơ chế **data scope** của RBAC (§3) và **audit chung** ở runtime-core/base ([schema §3](schema-du-lieu-2026-07-06.md)).
- [ ] Bổ sung permission mới cho các feature WS1 (hồ sơ approve, báo cáo Sở export).

## 6. Cần xác nhận
1. Danh sách phòng ban Vĩnh An (việc #3) → quyết mục 4 (có mô hình phòng hay không).
2. RBAC OpenGate có hỗ trợ **data scope** theo phụ trách/chủ thể không?
3. `runtime-core`/`base` đã có audit log chung chưa?
4. Vĩnh An có sân **sát hạch** không → có cần role `exam_op` (v2+).
</content>
