Giao diện
US-P08: Đọc / ghi SAPP Ops qua passthrough (auto-login, kiểm tra kết nối) ​
Thuộc FS: 00-overview.md — Nền tảng dùng chung của cụm tool SAPP (as-built @
e77c47a) Phần chung (tổng quan, roles, flow tổng, related features, NFR, Open Questions, References) xem ở Overview. File này chỉ chứa nội dung riêng của US.
| Field | Value |
|---|---|
| US ID | US-P08 |
| Role thực hiện | R1 - Người dùng tool (qua sub-tool sapp.010 — SAPP 26878; auto-login dùng thêm bởi sapp.011 — SAPP 26297) — Assumption (OQ về G-01), xem OQ-P-13 |
| Phase | As-built (đã triển khai) |
| Priority | N/A — as-built |
| Status | Draft |
| NFR liên quan | NFR-P-01, NFR-P-03, NFR-P-04, NFR-P-05, NFR-P-08 — xem Overview §5 |
| OQ liên quan | OQ-P-01, OQ-P-02, OQ-P-08, OQ-P-15, OQ-P-19 — xem Overview §6 |
Nguồn inventory: A-18, A-19, A-21.
Sub-tool đang dùng dịch vụ này (Fact):
| Sub-tool | Dùng phần nào | Bằng chứng |
|---|---|---|
| sapp.010 — FS 26878 | Toàn bộ passthrough (bước ops, classcode, màn re-sync / gửi lại, kiểm tra kết nối) qua route /sapp-ops/* | fe/src/data/services/sapp/sapp-ops.service.ts:26-470; importer duy nhất: fe/src/ui/components/works/sapp/sapp-26878-step-ops.tsx:31-32, fe/src/ui/components/works/sapp/sap-resync-panel.tsx:8, fe/src/ui/components/works/sapp/sapp-26878-step-sql.tsx:10 |
| sapp.011 — FS 26297 | Chỉ phần tự đăng nhập Ops (A-21) cho route riêng /sapp-26297/*; không gọi route /sapp-ops/* | be/libs/modules/src/lib/sapp/sapp-26297.common.service.ts:46, :62 |
| sapp.007 — FS 24395 bulk product | Không dùng — đi bằng bộ xác thực Ops cũ SAPP_BASE_API_URL + SAPP_API_TOKEN (BR-12) | be/libs/modules/src/lib/sapp/common.service.ts:278-302 |
1. US Statement ​
text
As a Người dùng tool (qua sub-tool 26878) [Assumption — OQ-P-13 / G-01]
I want đọc và tạo/sửa dữ liệu SAPP Ops (lớp, lịch, học viên, đơn, giao dịch, resync SAP…) trên đúng môi trường mà không phải cầm tài khoản Ops
So that [Assumption] dựng dữ liệu test cho các flow doanh thu mà trình duyệt không bao giờ thấy credential Ops2. Dependencies & Definition Of Ready ​
N/A — đã triển khai (Mode B).
Dependency runtime:
| # | Cần sẵn sàng | Ai cung cấp | Trạng thái |
|---|---|---|---|
| D1 | SAPP_OPS_{API_URL,USERNAME,PASSWORD}_<ENV> (US-P04) | Admin CMS | Theo môi trường |
| D2 | Redis của mind_keep (giữ token) | Hạ tầng | Đã có |
| D3 | SAPP Ops REST API POST {baseUrl}/api/v1/auth/login | Đội SAPP | Ngoài hệ thống |
3. Business Rules ​
| ID | Rule | Áp dụng ở | Đã cover bởi (AC) | Nguồn code (Mode B/C) |
|---|---|---|---|---|
| BR-01 | Môi trường chọn bằng đoạn cố định trong đường dẫn (prod / staging / dev), mỗi môi trường một route; 33 thao tác × 3 môi trường (bảng §4 M1 Fields) | M1 | AC-M1.1 | be/apps/api/src/sapp/sapp-ops.controller.ts:61-73, :634-2386 |
| BR-02 | BE tự đăng nhập Ops thay người dùng bằng URL + username + password của môi trường trong env_systems; thiếu một trong ba → lỗi 13030 "SAPP Ops login credentials are not configured". Không có route đăng nhập nào được mở ra | M1 | AC-M1.2, EC-M1.1 | be/libs/modules/src/lib/sapp/sapp-auth.common.service.ts:58-84, be/apps/api/src/sapp/sapp-ops.controller.ts:68-70 |
| BR-03 | Token Ops giữ trong Redis theo môi trường, tới (hạn của token − 60 giây); token không có hạn → 30 phút; tối đa 24 giờ | M1 | AC-M1.3 | be/libs/modules/src/lib/sapp/sapp-auth.common.service.ts:136-144, :171-202, be/libs/constants/src/lib/sapp/sapp-ops.constants.ts:66-84 |
| BR-04 | Ops trả 401 → xoá token, đăng nhập lại, thử lại đúng một lần; 403 không thử lại | M1 | AC-M1.4, EC-M1.2 | be/libs/modules/src/lib/sapp/sapp-auth.common.service.ts:121-134, be/libs/constants/src/lib/sapp/sapp-ops.constants.ts:86-92 |
| BR-05 | Nhiều yêu cầu cùng lúc chỉ tạo một lượt đăng nhập mỗi môi trường (trong một tiến trình API) | M1 | AC-M1.5 | be/libs/modules/src/lib/sapp/sapp-auth.common.service.ts:43-49, :98-105 |
| BR-06 | Đăng nhập lỗi → 13031 "Failed to login to the SAPP Ops API"; đăng nhập được mà không thấy token → 13032. Token được dò theo danh sách trường ưu tiên (có act), cố ý bỏ qua refresh token rft | M1 | EC-M1.3 | be/libs/modules/src/lib/sapp/sapp-auth.common.service.ts:146-169, be/libs/constants/src/lib/sapp/sapp-ops.constants.ts:43-63 |
| BR-07 | Passthrough không tự kiểm rule nghiệp vụ của Ops (ngày học tương lai, giá > 0, định dạng giờ…) — Ops quyết | M1 | AC-M1.6 | be/libs/modules/src/lib/sapp/sapp-ops.common.service.ts:73-77 |
| BR-08 | Lỗi từ Ops trả về kèm câu thông báo gốc của Ops (và mã lỗi riêng nếu nhận ra được, vd đơn đã thanh toán 13053); Ops trả success=false với HTTP 200 cũng coi là lỗi | M1 | AC-M1.6, EC-M1.4 | be/libs/modules/src/lib/sapp/sapp-ops.common.service.ts:37-41, :94-126 |
| BR-09 | Số trang / kích thước trang không phải số nguyên dương bị bỏ qua (dùng mặc định) thay vì gửi sang Ops | M1 | EC-M1.5 | be/apps/api/src/sapp/sapp-ops.controller.ts:48-59 |
| BR-10 | Kiểm tra kết nối (ping) thực hiện một lời gọi Ops có xác thực rẻ nhất và chỉ trả { ok: true } — không bao giờ trả token | M2 | AC-M2.1 | be/libs/modules/src/lib/sapp/sapp-ops.common.service.ts:280-296 |
| BR-11 | Timeout: đăng nhập 30 giây; lời gọi Ops thường 10 phút | M1 | NFR-P-05 | be/libs/constants/src/lib/sapp/sapp-ops.constants.ts:38-41, :69-74, be/libs/constants/src/lib/sapp/sapp.constants.ts:38 |
| BR-12 | Tồn tại song song hai cách xác thực Ops: bộ cũ một token cho mọi môi trường (thay tay khi hết hạn) và bộ tự đăng nhập theo môi trường của US này | Cấu hình | AC-M1.7 | be/libs/modules/src/lib/sapp/common.service.ts:278-302, be/libs/modules/src/lib/sapp/sapp-auth.common.service.ts:33-38 |
⚠️ Observed (không phải BR): mở đủ thao tác ghi trên prod, không chốt chặn prod ở tầng dùng chung (OQ-P-02); nhánh giữ token 30 phút khi hạn còn < 30 giây (OQ-P-08); route public (OQ-P-01).
4. Design ​
a. Mối liên hệ giữa các màn & User Flow ​
text
[Sub-tool 26878: bước ops / classcode / re-sync] ──→ M1 /api/v1/sapp/<env>/sapp-ops/<thao tác>
├─ token Redis còn hạn → gọi Ops
├─ chưa có token → đăng nhập Ops (1 lượt/env) → gọi Ops
└─ Ops 401 → xoá token → đăng nhập lại → thử lại 1 lần
[Sub-tool 26878: kiểm tra kết nối] ──────────────────→ M2 /sapp-ops/ping → { ok: true }
[Sub-tool 26297 (route riêng)] ── dùng chung cơ chế đăng nhập của M1b. Chi tiết từng màn ​
Màn M1 - Dịch vụ passthrough SAPP Ops ​
| Field | Value |
|---|---|
| Figma frame | N/A — as-built, không có Figma |
| Trigger vào màn | Sub-tool gọi một thao tác Ops |
| Mô tả ngắn | Không có màn riêng ở nền tảng — dịch vụ API; UI thuộc FS 26878 (US-D03, US-D18) |
UI States ​
N/A — dịch vụ không có màn riêng; UI thuộc sub-tool gọi (FS 26878 US-D03).
Fields ​
33 thao tác mỗi môi trường (route prod/…, lặp lại cho staging/…, dev/…) — Fact, be/apps/api/src/sapp/sapp-ops.controller.ts:634-2386:
| Nhóm | Đọc | Ghi (⚠️ có bản PROD) |
|---|---|---|
| Kết nối | GET ping | — |
| Danh mục / môn / cơ sở / khoá | GET course_categories, GET subjects, GET facilities, GET courses, GET courses/:courseId/sections | POST subjects, POST facilities, POST courses |
| Lớp | GET classes, GET classes/:classId/students, GET classes/:classId/sections | POST classes, PUT classes, PUT classes/:classId/class-room, POST classes/auto-update-schedule, PUT classes/:classId/students/transfer |
| Lịch học / giáo viên | GET class-schedules/:classId/teachers | POST class-schedules, PUT class-schedules/:id, PUT class-schedules/:id/cancel, DELETE class-schedules/:id, POST teachers |
| User | — | POST users |
| Sản phẩm / đơn / giao dịch | GET payment-products, GET product-orders/deal/:dealId | POST payment-products, POST product-orders, POST product-orders/:orderId/transactions, POST product-orders/transactions/:id/confirm |
| Resync SAP | — | POST sap-revenue/entries/:entryId/resync, POST sap-revenue/classes/:classId/resync-failed, POST product-orders/transactions/:id/resync-sap |
Tham số phân trang chung: page_index, page_size (số nguyên dương, BR-09). Body các thao tác ghi theo DTO của từng thao tác (mô tả ở shared/api-docs/sapp.md §SAPP Ops passthrough) — không đặc tả lại ở đây.
Filter / Search / Sort ​
N/A — dịch vụ API, không phải màn List (phân trang theo BR-09).
Actions ​
A1 - Gọi một thao tác Ops ​
| Item | Description |
|---|---|
| Trigger | Sub-tool gọi route /api/v1/sapp/<env>/sapp-ops/<thao tác> |
| Behavior | Lấy token (BR-02, BR-03, BR-05), gọi Ops, xử lý 401 (BR-04), chuyển lỗi (BR-08). Đọc/ghi SAPP Ops của môi trường trong đường dẫn. ⚠️ Ghi PROD: route prod/… ghi thẳng dữ liệu Ops production (tạo user/lớp/lịch/đơn/giao dịch, xoá/huỷ lịch, chuyển học viên, xác nhận giao dịch, resync SAP); tầng dùng chung không có chốt chặn prod (OQ-P-02). Chốt chặn prod nếu có nằm ở FE sub-tool (FS 26878) |
| Success Result | Dữ liệu Ops (lớp bọc data) |
Error cases:
| Case | Expected Handling |
|---|---|
| Thiếu credential | 13030 |
| Đăng nhập lỗi / không có token | 13031 / 13032 |
| Ops từ chối nghiệp vụ | Mã lỗi của thao tác (vd 13043, 13044, 13045, 13046) + ": <câu gốc của Ops>" |
Ops success=false HTTP 200 | Mã lỗi của thao tác |
Acceptance Criteria của màn M1 ​
AC-M1.1 - Môi trường theo đường dẫn ​
gherkin
Given sub-tool gọi GET /api/v1/sapp/staging/sapp-ops/classes
When BE xử lý
Then lời gọi đi tới SAPP_OPS_API_URL_STAGING bằng token của staging
And không có tham số nào đổi được môi trường ngoài đoạn đường dẫnMaps to:
- Business Rule: BR-01
AC-M1.2 - Tự đăng nhập, không lộ credential ​
gherkin
Given Redis chưa có token dev
When sub-tool gọi một thao tác dev
Then BE đăng nhập Ops bằng credential dev trong env_systems rồi gọi thao tác
And trình duyệt không nhận username / password / tokenMaps to:
- Business Rule: BR-02
AC-M1.3 - Dùng lại token ​
gherkin
Given token prod trong Redis còn hạn
When có lời gọi prod tiếp theo
Then không đăng nhập lạiMaps to:
- Business Rule: BR-03
AC-M1.4 - 401 → đăng nhập lại một lần ​
gherkin
Given token trong Redis đã bị Ops thu hồi
When Ops trả 401
Then BE xoá token, đăng nhập lại và thử lại đúng một lần
And nếu lần thử lại vẫn lỗi thì trả lỗi, không lặp tiếpMaps to:
- Business Rule: BR-04
AC-M1.5 - Một lượt đăng nhập cho yêu cầu đồng thời ​
gherkin
Given chưa có token staging
When 5 yêu cầu staging đến cùng lúc
Then chỉ một lượt đăng nhập Ops được gửiMaps to:
- Business Rule: BR-05
AC-M1.6 - Lỗi Ops mang câu gốc ​
gherkin
Given Ops từ chối tạo lịch vì ngày học ở quá khứ
When sub-tool gọi POST class-schedules
Then BE không tự chặn trước; lỗi trả về là "Failed to create SAPP Ops class schedule: <câu gốc của Ops>"Maps to:
- Business Rule: BR-07, BR-08
AC-M1.7 - Hai bộ xác thực tách biệt ​
gherkin
Given SAPP_API_TOKEN (bộ cũ) hết hạn
When sub-tool 26878 gọi passthrough
Then passthrough vẫn chạy (dùng bộ tự đăng nhập); chỉ sub-tool dùng bộ cũ (24395 bulk product) bị ảnh hưởngMaps to:
- Business Rule: BR-12
Negative AC / Edge Cases của màn M1 ​
| ID | Tình huống | Hành vi mong đợi |
|---|---|---|
| EC-M1.1 | Thiếu SAPP_OPS_PASSWORD_DEV | 13030 |
| EC-M1.2 | Ops trả 403 | Không đăng nhập lại, trả lỗi (BR-04) |
| EC-M1.3 | Đăng nhập Ops trả 200 nhưng không có trường token nào | 13032 |
| EC-M1.4 | Tạo giao dịch cho đơn đã thanh toán | Mã 13053 kèm câu gốc (BR-08) |
| EC-M1.5 | page_size=abc | Bỏ qua, dùng mặc định (BR-09) |
| EC-M1.6 | ⚠️ Observed — Gọi thao tác ghi trên prod/… | Thực thi trên Ops production; không đăng nhập, không chốt chặn ở tầng dùng chung (OQ-P-02, NFR-P-01) |
| EC-M1.7 | ⚠️ Observed — Token nhận về còn hạn < 30 giây | Vẫn được giữ 30 phút; lời gọi sau có thể dùng token đã chết và được cứu bởi nhánh 401 (be/libs/modules/src/lib/sapp/sapp-auth.common.service.ts:195-200; OQ-P-08, DR-P-04) |
| EC-M1.8 | ⚠️ Observed — Mã lỗi 13058 / 13059 bị dùng cho hai lỗi khác nhau (cập nhật/xoá lịch Ops và cấu hình LMS của 26297) | Người nhận không phân biệt được lỗi theo mã (be/libs/constants/src/lib/error-codes/error-codes.constants.ts:812-820, :834-845; OQ-P-19) |
Màn M2 - Kiểm tra kết nối Ops (ping) ​
| Field | Value |
|---|---|
| Figma frame | N/A — as-built, không có Figma |
| Trigger vào màn | Sub-tool 26878 gọi GET /api/v1/sapp/<env>/sapp-ops/ping (bước ops ping, fe/src/ui/components/works/sapp/sapp-26878-step-ops.tsx:1018) |
| Mô tả ngắn | Chứng minh credential của môi trường dùng được |
UI States ​
N/A — dịch vụ không có màn riêng; hiển thị do bước Ops của FS 26878.
Fields ​
N/A — không có tham số ngoài môi trường trong đường dẫn.
Filter / Search / Sort ​
N/A — không phải màn List.
Actions ​
A1 - Ping ​
| Item | Description |
|---|---|
| Trigger | Bước ping |
| Behavior | Đăng nhập (nếu cần) + một lời gọi Ops đọc rẻ nhất. Chỉ đọc |
| Success Result | { ok: true } |
Error cases:
| Case | Expected Handling |
|---|---|
| Credential sai / thiếu | 13031 / 13030 |
Acceptance Criteria của màn M2 ​
AC-M2.1 - Ping không trả token ​
gherkin
Given credential dev đúng
When gọi ping dev
Then nhận { ok: true } và không có token trong responseMaps to:
- Business Rule: BR-10
Negative AC / Edge Cases của màn M2 ​
| ID | Tình huống | Hành vi mong đợi |
|---|---|---|
| EC-M2.1 | Ops tạm không phản hồi | Lỗi theo timeout (BR-11) |
5. Cross-feature AC ​
AC-X.1 - Chốt chặn prod thuộc sub-tool ​
gherkin
Given passthrough mở route prod cho mọi thao tác ghi
When FS 26878 đặc tả bước Ops
Then FS 26878 phải ghi rõ chốt chặn prod (nếu có) nằm ở FE và BE vẫn nhận prod (⚠️ Observed)Maps to:
- REL-05 (Be Affected - FS 26878)
AC-X.2 - 26297 dùng chung cơ chế đăng nhập ​
gherkin
Given FS 26297 gọi Ops qua route riêng
When token hết hạn / 401
Then hành vi đăng nhập lại theo BR-02…BR-06 của US nàyMaps to:
- REL-06 (Be Affected - FS 26297)
AC-X.3 - 24395 bulk product dùng bộ cũ ​
gherkin
Given FS 24395 bulk product
When đặc tả xác thực Ops
Then FS 24395 tham chiếu BR-12 (bộ cũ), không phải cơ chế tự đăng nhậpMaps to:
- REL-03 (Be Affected - FS 24395)