Skip to content

US-I04: Bulk product — bắt đầu session, nạp và tạo course category ​

Thuộc FS: 00-overview.md — SAPP-24395 Contact / Deal instances & Bulk product creation 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. As-built tại commit e77c47a — suy từ code, chưa chạy thử.

FieldValue
US IDUS-I04
Role thực hiệnR1 - Người dùng tool (Assumption — OQ-I-16 về G-01)
PhaseAs-built (đã triển khai)
PriorityN/A — as-built
StatusDraft
NFR liên quanNFR-I-01, NFR-I-03, NFR-I-04, NFR-I-05, NFR-I-07 — xem Overview §5
OQ liên quanOQ-I-04, OQ-I-10, OQ-I-12, OQ-I-13, OQ-I-14, OQ-I-16 — xem Overview §6

Nguồn inventory: A-09, A-10, A-11, A-16, A-17. Ghi: SAPP Ops — môi trường theo SAPP_BASE_API_URL (❓ OQ-I-04) (chỉ bước 1.5, qua API); Redis mind_keep.

1. US Statement ​

text
As a Người dùng tool (Assumption — OQ-I-16)
I want bắt đầu một session tạo product hàng loạt, nạp toàn bộ course category từ SAPP Ops và biết category nào trong danh sách sản phẩm cố định đang thiếu
So that [Assumption] các bước sau (subject, product) tra được category đúng, và tôi có thể dừng / quay lại tiếp tục trong vòng 6 giờ mà không mất tiến trình

2. Dependencies & Definition Of Ready ​

N/A — đã triển khai (Mode B). Dependency runtime:

#Cần sẵn sàngAi cung cấpTrạng thái
D1SAPP_BASE_API_URL + SAPP_API_TOKEN (JWT tĩnh) trong env_systems (US-P04) — không dùng auto-login của US-P08Admin CMSN/A — runtime; môi trường ❓ OQ-I-04
D2Redis mind-keep hoạt độngHạ tầng BEN/A — runtime

3. Business Rules ​

IDRuleÁp dụng ởĐã cover bởi (AC)Nguồn code (Mode B/C)
BR-01Bắt đầu = tạo session mới (mã session do hệ thống sinh), nạp toàn bộ course category của SAPP Ops vào session, tính danh sách category thiếu; session ở bước 1. Bấm "Re-run" ở bước 1 cũng tạo một session mới thay cho session đang xemM1 - A1AC-M1.1, AC-M1.2be/libs/modules/src/lib/sapp/common.service.ts:2614-2638; fe/src/ui/components/works/sapp/sapp-24395-products-tool.tsx:260-273, :575-590
BR-02Category "thiếu" = tên category có trong danh sách sản phẩm cố định nhưng không khớp (không phân biệt hoa thường, bỏ khoảng trắng đầu cuối) với category nào trên SAPP OpsM1 - A1AC-M1.1be/libs/modules/src/lib/sapp/common.service.ts:2640-2696
BR-03Tool dùng một cấu hình kết nối SAPP Ops duy nhất (URL gốc + token tĩnh trong env_systems); màn không có lựa chọn môi trường. Thiếu URL hoặc token → lỗi 13012 "SAPP API base URL or token is not configured"M1 - mọi actionAC-M1.4be/libs/modules/src/lib/sapp/common.service.ts:278-302; be/libs/constants/src/lib/sapp/sapp.constants.ts:16-25; be/libs/constants/src/lib/error-codes/error-codes.constants.ts:599-603
BR-04Session sống 6 giờ tính từ lần ghi gần nhất (mỗi bước ghi đều đặt lại hạn); quá hạn → mọi thao tác trên session trả 13017 "SAPP-24395 product session not found or expired" (HTTP 404)M1 - A2AC-M1.5, EC-M1.2be/libs/modules/src/lib/sapp/common.service.ts:2585-2608; be/libs/constants/src/lib/sapp/sapp.constants.ts:57-61; be/libs/constants/src/lib/error-codes/error-codes.constants.ts:624-628
BR-05Trình duyệt nhớ mã session sau mỗi thao tác thành công; mở lại màn → tự tải lại session ("Đang khôi phục session…") và nhảy tới bước session đã đạtM1 - A2AC-M1.3fe/src/ui/components/works/sapp/sapp-24395-products-tool.tsx:165-245; fe/src/data/constants/local-storage.ts:6
BR-06"Bắt đầu lại" chỉ xoá mã session trong trình duyệt và trạng thái màn, quay về bước 1; session trên server không bị xoá, còn tới khi hết hạn (BR-04)M1 - A4AC-M1.6fe/src/ui/components/works/sapp/sapp-24395-products-tool.tsx:126-141, :498-507
BR-07(Chỉ qua API — bước 1.5) Tạo category thiếu: gửi danh sách tên, rỗng = tạo tất cả category thiếu; tên không nằm trong danh sách thiếu bị bỏ qua; lỗi từng mục được ghi vào "failed", không dừng cả lượt; category tạo được thêm vào session và bỏ khỏi danh sách thiếu; bước của session giữ nguyên; không kiểm tra session đang ở bước nàoM1 - A3AC-M1.7be/libs/modules/src/lib/sapp/common.service.ts:2739-2799; be/libs/interface/src/lib/dto/sapp/sapp-instance.dto.ts:247-254
BR-08Nạp category theo trang 100 dòng tới khi trang cuối < 100 dòng; lỗi đọc → 13013 "Failed to get SAPP course categories"M1 - A1AC-M1.4be/libs/modules/src/lib/sapp/common.service.ts:2496-2518, :2632-2637

Không phải BR (⚠️ Observed → OQ): môi trường SAPP Ops thực tế không xác định từ code, có thể là prod (inventory R-23 → OQ-I-04); màn không hiện category thiếu và không có nút bước 1.5 (R-35, DR-I-07 → OQ-I-13); nhận diện "session hết hạn" có thể không bao giờ khớp (R-34 → OQ-I-10); cảnh báo CORS có thể sai (R-38 → OQ-I-14); tạo thành công không kèm id vẫn tính "created" (R-32 → OQ-I-12).

4. Design &ZeroWidthSpace;

a. Mối liên hệ giữa các màn & User Flow &ZeroWidthSpace;

text
[Workspace SAPP › "Bulk product creation" (sapp.007)]
  ↓ vào màn
  ├─ trình duyệt có mã session ─→ "Đang khôi phục session…" ─┬─ OK → nhảy tới bước đã đạt (US-I05 / US-I06)
  │                                                          └─ lỗi → popup / (hết hạn) xoá mã — xem EC-M1.2
  └─ không có ─→ [M1: Bước 1 · Bắt đầu session & nạp categories]
                    ↓ "Bắt đầu — Nạp categories"
                 [Đã nạp n categories] ── "Tiếp" ──→ [US-I05 Bước 2]
  (Bước 1.5 tạo category thiếu: chỉ API, không có trên màn)
  "Bắt đầu lại" (mọi bước) ──→ về M1 trạng thái trống

Link flow chi tiết: sơ đồ trạng thái session ở inventory §5b.

b. Chi tiết từng màn &ZeroWidthSpace;

Màn M1 - Bulk product creation: khung màn + Bước 1 &ZeroWidthSpace;

FieldValue
Figma frameN/A
Trigger vào mànMở tool "Bulk product creation" (/works/sapp/tools/sapp-24395-products)
Mô tả ngắnHeader "SAPP-24395 · Bulk product creation": "Tạo hàng loạt payment products trên SAPP Ops theo từng bước… dùng base URL & token cấu hình trong env_systems. Tiến trình được lưu và tự khôi phục (session sống 6 giờ)". Khối cảnh báo "Lưu ý: API gốc chặn CORS" (3 điều kiện: chạy dev local; expose qua ngrok; sửa SAPP_BASE_API_URL). Stepper 3 bước (Categories · Subjects · Create products — quy tắc ở US-I06 BR-07). Nội dung bước. Chân: "Bắt đầu lại" · "Quay lại" · "Tiếp"
UI States &ZeroWidthSpace;
StateKhi nàoHiển thịFigma frame
DefaultChưa có sessionBước 1: "Bước 1 · Bắt đầu session & nạp categories" — "Tạo một session mới và nạp toàn bộ danh mục khóa học (category) từ SAPP. Không cần nhập gì." + nút "Bắt đầu — Nạp categories"; bước 2, 3 khoáN/A
LoadingĐang khôi phục / đang nạp"Đang khôi phục session…" / nút "Đang nạp…"; mọi nút khoáN/A
Has dataĐã nạp"Đã nạp n categories." + nút "Re-run"; "Session: <mã>"; tối đa 24 tên category dạng thẻ + "+k khác"; bước 1 có dấu hoàn thành; "Tiếp" bậtN/A
EmptySAPP Ops không có category"Đã nạp 0 categories." — mọi category cố định thành "thiếu" (không hiển thị — OQ-I-13)N/A
ErrorAPI lỗiPopup <tiêu đề thao tác> — <message> (<code>) (NFR-I-07)N/A
DisabledĐang bậnStepper, "Bắt đầu lại", "Quay lại", "Tiếp" khoáN/A
Fields &ZeroWidthSpace;
FieldRequiredTypeDefaultFormatValidationError MessageVisibilityDepends On
(không có ô nhập)————————
Số category đã nạp—Readonly—"Đã nạp n categories."——Khi đã có sessionSession
Mã session—Readonly—UUID——Khi đã có sessionSession
Danh sách tên category—Readonly (tối đa 24 + "+k khác")————Khi có categorySession
Filter / Search / Sort (chỉ màn List/Report — xóa nếu không phải) &ZeroWidthSpace;

N/A — màn wizard, không có danh sách lọc.

Actions &ZeroWidthSpace;
A1 - Bắt đầu — Nạp categories (Bước 1) &ZeroWidthSpace;
ItemDescription
TriggerBấm "Bắt đầu — Nạp categories" (hoặc "Re-run" khi đã nạp)
BehaviorBR-03 lấy cấu hình → BR-08 nạp category → BR-01, BR-02 tạo session → lưu Redis (BR-04) → trình duyệt nhớ mã (BR-05). Chỉ đọc SAPP Ops; ghi Redis mind_keep + localStorage
Success ResultPopup "Đã bắt đầu session" — "Đã nạp n categories. Bấm "Tiếp" để nạp subjects."

Error cases:

CaseExpected Handling
Thiếu URL / tokenPopup "Bắt đầu session thất bại" — "SAPP API base URL or token is not configured (13012)"
SAPP Ops lỗi / token hết hạnPopup "Bắt đầu session thất bại" — "Failed to get SAPP course categories (13013)"
A2 - Khôi phục session khi mở màn &ZeroWidthSpace;
ItemDescription
TriggerMở màn khi trình duyệt còn mã session
BehaviorĐọc session theo mã (Redis), hiện bước đã đạt (BR-05). Không ghi
Success ResultMàn hiện đúng bước + dữ liệu session

Error cases:

CaseExpected Handling
Session hết hạnThiết kế: xoá mã đã lưu, im lặng. ⚠️ Observed — so mã lỗi sai kiểu → rơi vào popup "Không khôi phục được session" — "SAPP-24395 product session not found or expired (13017)", mã cũ không bị xoá (OQ-I-10)
Lỗi khácPopup "Không khôi phục được session"
A3 - Tạo category thiếu (Bước 1.5 — chỉ API) &ZeroWidthSpace;
ItemDescription
TriggerGọi POST /api/v1/sapp/sapp-24395/products/sessions/<mã>/categories/create với danh sách tên (hoặc rỗng). Không có nút trên màn, FE không gọi (OQ-I-13, DR-I-07)
BehaviorBR-07. Ghi vào: SAPP Ops — course category mới tại SAPP_BASE_API_URL (⚠️ môi trường ❓ — có thể là PROD, OQ-I-04) + Redis mind_keep
Success ResultSession trả về có thêm category mới và kết quả created / failed của lượt tạo

Error cases:

CaseExpected Handling
Session hết hạn13017 (HTTP 404)
Một category tạo lỗiVào "failed" kèm lý do, các mục khác vẫn chạy
Lỗi chung13020 "Failed to create SAPP course category"
A4 - Bắt đầu lại &ZeroWidthSpace;
ItemDescription
TriggerBấm "Bắt đầu lại" ở chân màn (khi không bận)
BehaviorBR-06. Chỉ xoá localStorage + trạng thái màn; không gọi API
Success ResultMàn về bước 1 trống

Error cases:

CaseExpected Handling
—Không có
Acceptance Criteria của màn M1 &ZeroWidthSpace;
AC-M1.1 - Bắt đầu session &ZeroWidthSpace;
gherkin
Given chưa có session, SAPP Ops có 30 course category
When bấm "Bắt đầu — Nạp categories"
Then hệ thống tạo session mới ở bước 1 chứa 30 category và danh sách category thiếu
And popup "Đã bắt đầu session" — "Đã nạp 30 categories. Bấm "Tiếp" để nạp subjects."
And màn hiện "Session: `<mã>`" và tối đa 24 tên category + "+6 khác"

Maps to:

  • Business Rule: BR-01, BR-02, BR-08
  • Field/Action: A1
AC-M1.2 - Re-run tạo session mới &ZeroWidthSpace;
gherkin
Given đang có session S1 ở bước 1
When bấm "Re-run"
Then một session mới S2 được tạo và mã S2 được lưu trong trình duyệt
And S1 vẫn còn trên server tới khi hết hạn

Maps to:

  • Business Rule: BR-01, BR-04
AC-M1.3 - Khôi phục khi mở lại màn &ZeroWidthSpace;
gherkin
Given session S đang ở bước 2, được ghi lần cuối cách đây 1 giờ
When đóng rồi mở lại màn trên cùng trình duyệt
Then màn hiện "Đang khôi phục session…" rồi hiển thị bước 2 với dữ liệu của S

Maps to:

  • Business Rule: BR-05
  • Field/Action: A2
AC-M1.4 - Thiếu cấu hình &ZeroWidthSpace;
gherkin
Given `SAPP_API_TOKEN` chưa cấu hình trong env_systems
When bấm "Bắt đầu — Nạp categories"
Then popup "Bắt đầu session thất bại" — "SAPP API base URL or token is not configured (13012)"

Maps to:

  • Business Rule: BR-03
AC-M1.5 - Hạn 6 giờ trượt theo lần ghi cuối &ZeroWidthSpace;
gherkin
Given session tạo lúc 08:00, nạp subjects (ghi) lúc 13:00
When gọi thao tác trên session lúc 18:30
Then session vẫn còn (hạn tính từ 13:00 → 19:00)
And gọi lúc 19:01 không có thao tác ghi nào khác thì trả 13017 (HTTP 404)

Maps to:

  • Business Rule: BR-04
AC-M1.6 - Bắt đầu lại không xoá session server &ZeroWidthSpace;
gherkin
Given session S ở bước 2
When bấm "Bắt đầu lại"
Then màn về bước 1 trống, trình duyệt không còn mã S
And gọi GET session S qua API vẫn trả dữ liệu tới khi hết hạn

Maps to:

  • Business Rule: BR-06
  • Field/Action: A4
AC-M1.7 - Bước 1.5 qua API &ZeroWidthSpace;
gherkin
Given session S có category thiếu ["CGMA", "IFRS"]
When gọi API tạo category thiếu với danh sách ["CGMA", "XYZ"]
Then chỉ "CGMA" được tạo trên SAPP Ops ("XYZ" bị bỏ qua vì không nằm trong danh sách thiếu)
And session có thêm category "CGMA", danh sách thiếu còn ["IFRS"], bước session không đổi

Maps to:

  • Business Rule: BR-07
  • Field/Action: A3
Negative AC / Edge Cases của màn M1 &ZeroWidthSpace;
IDTình huốngHành vi mong đợi
EC-M1.1Không biết SAPP Ops đang trỏ môi trường nào⚠️ Observed — màn không cho biết; phụ thuộc giá trị SAPP_BASE_API_URL (OQ-I-04)
EC-M1.2Mở lại màn sau khi session hết hạn⚠️ Observed (suy từ code) — thay vì xoá mã im lặng, có thể hiện popup "Không khôi phục được session … (13017)" mỗi lần mở màn cho tới khi bấm "Bắt đầu lại" (OQ-I-10)
EC-M1.3Có category thiếu⚠️ Observed — màn không hiện danh sách thiếu, không có cách tạo; product của category đó sẽ "failed" ở bước 3 (OQ-I-13)
EC-M1.4Làm theo cảnh báo CORS (ngrok)⚠️ Observed — BE gọi SAPP Ops từ server nên CORS không áp dụng; cảnh báo có thể không còn đúng (OQ-I-14)
EC-M1.5Token JWT tĩnh hết hạnNạp lỗi 13013; phải thay token tay trong env_systems (OQ-I-04)
EC-M1.6Mở màn ở 2 tab cùng trình duyệtHai tab dùng chung mã session trong localStorage; tab bấm "Re-run" sau cùng ghi đè mã của tab kia
EC-M1.7Trình duyệt chặn localStorage (chế độ riêng tư)Vẫn chạy được trong phiên; không khôi phục được khi mở lại

5. Cross-feature AC &ZeroWidthSpace;

AC-X.1 - Cấu hình kết nối lấy từ env_systems &ZeroWidthSpace;

gherkin
Given Admin đổi `SAPP_BASE_API_URL` trong env_systems (US-P04)
When bấm "Bắt đầu — Nạp categories"
Then category được nạp từ URL mới — và mọi ghi sau đó (bước 1.5, 2.5, 3) đi vào hệ thống tại URL đó

Maps to:

  • REL-02 (Affects - Nền tảng cấu hình env_systems)

AC-X.2 - Category mới xuất hiện trên SAPP Ops &ZeroWidthSpace;

gherkin
Given gọi bước 1.5 thành công
When mở danh mục course category trên SAPP Ops
Then thấy category mới với tên trùng tên trong danh sách cố định

Maps to:

  • REL-07 (Be Affected - Danh mục SAPP Ops)