PRD: Phân cấp tài nguyên, hành động & khai báo quyền
| Module | Phân quyền | PRD ID | PRD-HIER-001 |
| Trạng thái | Sẵn sàng dev | FEAT | HIER |
| Epic | — | Plane | BANA-1572 |
| Ngày | 2026-06-04 | Phiên bản | v1.0 |
| Gói | @nx/identity | URD | HIER |
| Surface | Backend | ||
| Phụ trách | Phát Nguyễn | ||
Phân cấp tài nguyên, hành động & khai báo quyền là gì
Engine RBAC theo phạm vi merchant hỗ trợ ba trục kế thừa mà BANA chưa khai thác hết: trục hành động (quản lý bao đọc), trục tài nguyên (thao tác hoàn tiền của một đơn hàng nằm dưới đơn hàng; dòng sản phẩm của một đơn hàng nằm dưới đơn hàng), và trục phạm vi (organizer bao merchant).
Trước đợt này, quyền ở dạng phẳng: gần 950 mã quyền theo từng thao tác riêng lẻ, mỗi mã cấp một dòng. Một vai trò owner mang gần 950 dòng cấp quyền; việc "trụ sở thấy được mọi merchant" được giả bằng cách ghi một hàng thành viên cho từng merchant. Không có hành động rộng kiểu quản lý/ghi, và không có cách khai báo thuận tiện - mỗi module tự viết tay phần khai báo quyền của mình. Hệ quả: vai trò cồng kềnh, dễ vỡ khi thêm thao tác mới, tải chậm và khó đọc lại để suy luận ai được làm gì.
Ba trục xếp lồng nhau
Đợt này mô hình hoá quyền thành cây tài nguyên nhân bậc thang hành động nhân cây phạm vi, để một vai trò gộp lại còn vài dòng cấp thô trong khi vai trò cần chi tiết vẫn cấp chính xác từng thao tác, và biến việc khai báo quyền của một controller mới thành một lệnh gọi.
Khi admin gán quyền cho vai trò owner tại màn tạo/sửa vai trò, giờ chỉ cần chọn một dòng cấp quản lý cho cả module Sale, thay vì tick từng thao tác lẻ - mọi đối tượng và thao tác bên dưới Sale tự động được bao phủ. Cấp quyền ở cấp module hoặc cấp đối tượng thì mọi màn hình, thao tác con bên trong đều dùng được ngay, không phải cấp riêng từng thao tác một. Một vai trò trụ sở/quản lý vươn tới mọi merchant của organizer mình qua một dòng cấp quyền duy nhất ở phạm vi organizer, không cần thêm dòng thành viên cho từng merchant.
Kỹ sư khai báo quyền của một controller mới - gồm tài nguyên, các thao tác và hành động cơ bản của nó - chỉ bằng một lệnh gọi trong code, không phải viết tay từng dòng quyền. Khi migration chạy lúc khởi động backend, các quyền và cạnh phân cấp được đồng bộ tự động và không đổi khi chạy lại nhiều lần; quyền nào bị gỡ khỏi khai báo trong code sẽ tự động dọn theo cùng các dòng cấp quyền của nó, không tồn đọng rác.
Chỗ dễ sai nhất
Quản lý Sale không phải một mẫu ký tự đại diện
Độ thô của một dòng cấp quyền đến từ cây tài nguyên cộng với hành động quản lý, không phải từ một mẫu ký tự đại diện (wildcard) kiểu "mọi thứ thuộc sale". Không có quyền dạng mẫu đại diện tuỳ ý trong hệ thống - mọi độ rộng đều đi qua phân cấp có khai báo tường minh.
Tương tự, tầm với của một vai trò trụ sở qua trục phạm vi (organizer bao merchant) là một cạnh riêng, còn đang xây dựng - hiện tại vai trò trụ sở vẫn cần thêm dòng thành viên cho từng merchant, chưa vươn được chỉ bằng một dòng cấp quyền theo organizer.
1. Mục tiêu & Loại trừ
Mục tiêu
- Gộp các dòng cấp quyền của vai trò - vai trò owner trở thành một dòng (Sale, quản lý) thay vì mọi dòng thao tác của đơn hàng bán.
- Một bậc thang hành động ba tầng: quản lý bao {ghi, đọc, thực thi}; ghi bao {tạo, sửa, xoá}.
- Một phân cấp tài nguyên không đổi mã - đối tượng giữ nguyên mã định danh; nhóm theo module cộng lồng liên-thực-thể, và lồng thao tác tự động qua mã có dấu chấm.
- Một phân cấp phạm vi merchant thuộc organizer - để vai trò trụ sở/quản lý vươn tới mọi merchant của một organizer qua một dòng cấp quyền theo phạm vi organizer, thay cho việc thêm dòng thành viên cho từng merchant.
- Một bộ công cụ khai báo tối giản boilerplate (một lệnh gọi khai báo tài nguyên/quyền, một ma trận vai trò→cấp quyền, và các bước seed cạnh cấu trúc không đổi khi chạy lại), xây trong BANA trước. Bậc thang hành động, các biến thể cấp quyền và bộ máy đối chiếu quyền vốn đã có sẵn trong framework nền; BANA xây các bước seed, lệnh gọi khai báo và ma trận trên nền đó.
Loại trừ
- Đưa bộ công cụ khai báo vào framework nền tảng - dời sang đợt sau (BANA trước, chuyển sau).
- Quyền theo mẫu ký tự đại diện tuỳ ý - độ thô đến từ phân cấp, không phải mẫu ký tự đại diện.
- Đổi tập vai trò hay ý nghĩa độ ưu tiên vai trò - một mối quan tâm riêng.
- Quyền theo thời gian/ca làm việc và nhật ký kiểm toán quyền.
2. Thước đo thành công
| Thước đo | Mục tiêu / tín hiệu |
|---|---|
| Nén số dòng cấp quyền | Số dòng cấp quyền mỗi vai trò giảm từ khoảng 950 xuống còn vài chục |
| Trải nghiệm khai báo | Thêm một controller mới chỉ cần một lệnh gọi khai báo, không phải liệt kê từng thao tác |
| Ổn định khi seed lại | Chạy lại bước seed không đổi kết quả; mã quyền đã gỡ khỏi khai báo được dọn tự động |
| Tương đương kết quả phân quyền | Launchpad và mọi route được gác quyền giữ nguyên kết quả cho phép/từ chối so với mô hình phẳng trước đó (kiểm thử hồi quy) |
| Tầm với của vai trò trụ sở | Vai trò trụ sở/quản lý vươn tới mọi merchant của organizer mà không cần thêm dòng thành viên cho từng merchant |
3. Persona & Tình huống
| Persona | Mục tiêu trong tính năng này |
|---|---|
| Chủ sở hữu / admin nền tảng | Gán một dòng cấp quyền thô, dễ đọc (quản lý Sale) thay vì sửa hàng trăm dòng cấp quyền |
| Owner trụ sở | Vươn tới mọi merchant của organizer mình qua một dòng cấp quyền theo phạm vi organizer |
| Kỹ sư | Khai báo quyền, node tài nguyên và hành động cơ bản của một controller trong một lệnh gọi |
| Người vận hành hệ thống | Tin rằng seed lại là an toàn - không đổi khi chạy lặp lại, và mã đã gỡ được dọn tự động |
Tình huống cốt lõi: kỹ sư khai báo tài nguyên và các thao tác của một controller trong một lệnh gọi → migration seed bậc thang hành động, cạnh gom-lên-module và cạnh phạm vi organizer-merchant, không đổi khi chạy lại → admin gán vai trò dưới dạng một ma trận cấp quyền thô, gọn → một request được giải quyết qua bậc thang hành động, cây tài nguyên và cây phạm vi mà không cần liệt kê phẳng từng thao tác.
4. User Stories
| # | Là một | Tôi muốn | Để |
|---|---|---|---|
| 01 | admin | cấp (Sale, quản lý) cho vai trò owner | vai trò đó bao phủ mọi đối tượng và thao tác của Sale mà không phải liệt kê từng cái |
| 02 | owner trụ sở | một dòng cấp quyền theo phạm vi organizer vươn tới mọi merchant của organizer mình | không còn phụ thuộc vào dòng thành viên cho từng merchant |
| 03 | kỹ sư | khai báo node tài nguyên, thao tác và hành động cơ bản của một controller trong một lệnh gọi | thêm route mới không đồng nghĩa với viết tay từng quyền |
| 04 | người vận hành hệ thống | việc seed dọn các mã quyền đã gỡ khỏi khai báo một cách tự động | quyền và dòng cấp quyền cũ không bao giờ tích tụ |
| 05 | người thiết kế vai trò | một hành động rộng hơn (quản lý/ghi) đáp ứng mọi request hẹp hơn | tôi cấp theo ý định thay vì liệt kê từng động từ thao tác |
5. Yêu cầu chức năng
| # | Yêu cầu | Trạng thái | URD ref |
|---|---|---|---|
FR-001 | Một dòng cấp quyền CÓ THỂ nhắm vào một tài nguyên ở bất kỳ cấp nào - module, đối tượng hoặc thao tác. Tự động bao phủ mọi hậu duệ của cấp đó. | ✅ | URD-HIER-001 |
FR-002 | Tài nguyên thao tác lồng dưới đối tượng của nó theo mã có dấu chấm (thao tác hoàn tiền của một đơn hàng nằm dưới đơn hàng) mà không cần cấu hình thêm. | ✅ | URD-HIER-002 |
FR-003 | Một đối tượng CÓ THỂ gom lên một module. Một dòng cấp quyền cấp module bao phủ mọi đối tượng trong module đó. | ✅ | URD-HIER-003 |
FR-004 | Một thực thể con CÓ THỂ lồng dưới thực thể cha bất kể cách đặt mã (dòng sản phẩm của một đơn hàng nằm dưới đơn hàng). Dòng cấp quyền của cha bao phủ luôn con. | 🚧 | URD-HIER-004 |
FR-005 | Hành động tạo thành một bậc thang - quản lý bao {ghi, đọc, thực thi}; ghi bao {tạo, sửa, xoá}. Một hành động cấp rộng hơn thoả mãn mọi yêu cầu hẹp hơn. | ✅ | URD-HIER-005 |
FR-006 | Một route yêu cầu một hành động cơ bản (đọc/tạo/sửa/xoá/thực thi). Một dòng cấp quyền CÓ THỂ dùng bất kỳ tầng nào kể cả quản lý/ghi để đáp ứng. | ✅ | URD-HIER-006 |
FR-007 | Một dòng cấp quyền giới hạn phạm vi vào một organizer áp dụng cho mọi merchant dưới organizer đó. Một dòng cấp quyền giới hạn vào một merchant chỉ áp dụng tại merchant đó. | 🚧 | URD-HIER-007 |
FR-008 | Một vai trò trụ sở/quản lý vươn tới mọi merchant của organizer mình qua một dòng cấp quyền theo phạm vi organizer. Không cần thêm dòng thành viên cho từng merchant. | 🚧 | URD-HIER-008 |
FR-009 | Một dòng cấp quyền CÓ THỂ ghim vào phạm vi toàn hệ thống (mọi nơi) hoặc mọi-merchant-đã-tham-gia. | ✅ | URD-HIER-009 |
FR-010 | Quyền, bậc thang hành động và các cạnh phân cấp tài nguyên/phạm vi được khai báo trong code. Seed không đổi khi chạy lại. | 🔶 | URD-DECL-001 |
FR-011 | Khai báo thao tác, node tài nguyên và hành động cơ bản của một controller chỉ tốn một lệnh gọi. | ✅ | URD-DECL-002 |
FR-012 | Gán vai trò→cấp quyền được khai báo dưới dạng một ma trận gọn (owner → quản lý Sale), không liệt kê theo từng thao tác. | ✅ | URD-DECL-003 |
FR-013 | Việc seed dọn các mã quyền đã gỡ khỏi khai báo một cách tự động - quyền và dòng cấp quyền cũ không tích tụ. | ✅ | URD-DECL-004 |
FR-014 | Bộ công cụ khai báo được xây dựng trong BANA trước. Bậc thang hành động, các biến thể cấp quyền và bộ máy đối chiếu quyền vốn đã có sẵn trong framework nền. | ✅ | URD-DECL-005 |
5.1 Tiêu chí nghiệm thu
- Gán (Sale, quản lý) cho một vai trò → vai trò đó có thể thực hiện mọi thao tác trên mọi đối tượng của module Sale, không cần thêm dòng cấp quyền nào khác.
- Gán một dòng cấp quyền chỉ trên một đối tượng cụ thể (ví dụ đơn hàng, hành động đọc) → thao tác tạo trên chính đối tượng đó bị từ chối, vì đọc không bao phủ tạo.
- Thêm một thao tác con mới của một đối tượng (ví dụ thao tác hoàn tiền của đơn hàng) mà không khai báo gì thêm → dòng cấp quyền trên đối tượng cha đã bao phủ sẵn thao tác con này.
- Thêm một thực thể con mới lồng dưới một thực thể cha khác tên (ví dụ dòng sản phẩm của đơn hàng) → dòng cấp quyền của thực thể cha chưa tự bao phủ con, phải cấp quyền riêng cho thực thể con.
- Vai trò được cấp hành động ghi trên một đối tượng → mọi request tạo/sửa/xoá trên đối tượng đó được chấp nhận; request đọc cũng được chấp nhận qua bậc thang.
- Một route yêu cầu hành động đọc trên một đối tượng, vai trò chỉ được cấp hành động đọc → được chấp nhận; vai trò chỉ được cấp hành động thực thi trên đối tượng khác → bị từ chối.
- Một vai trò trụ sở được cấp quyền theo phạm vi organizer, thao tác trên một merchant bất kỳ thuộc organizer đó → hiện vẫn bị từ chối trừ khi có thêm dòng thành viên riêng cho merchant đó; một dòng cấp quyền chỉ theo phạm vi một merchant khác không áp dụng.
- Một vai trò trụ sở/quản lý muốn vươn tới toàn bộ merchant của organizer mình → hiện vẫn phải có dòng thành viên cho từng merchant, chưa vươn được chỉ bằng một dòng cấp quyền theo organizer.
- Cấp một dòng quyền ghim vào phạm vi toàn hệ thống → áp dụng ở mọi merchant kể cả merchant mới tạo sau này; ghim vào phạm vi mọi-merchant-đã-tham-gia → chỉ áp dụng ở các merchant người dùng đã là thành viên.
- Khai báo quyền và các cạnh phân cấp tài nguyên của một module trong code, chạy migration lúc khởi động → quyền, bậc thang hành động và cạnh tài nguyên được đồng bộ; cạnh theo phạm vi organizer-merchant thì chưa được seed dù engine đã hỗ trợ.
- Chạy lại migration nhiều lần liên tiếp không thay đổi gì thêm → không sinh thêm dòng trùng, không đổi kết quả phân quyền.
- Thêm một controller mới, khai báo tài nguyên và các thao tác của nó bằng một lệnh gọi → quyền và đặc tả gác quyền của route được tạo ra mà không cần liệt kê từng thao tác CRUD theo tay.
- Gán vai trò→cấp quyền bằng một dòng trong ma trận khai báo (ví dụ owner → quản lý Sale) → mọi vai trò owner có ngay quyền quản lý Sale, không cần lặp qua từng thao tác.
- Gỡ một mã quyền khỏi khai báo trong code, chạy lại migration → mã quyền đó cùng các dòng cấp quyền của nó bị dọn khỏi hệ thống, không còn hiển thị khi liệt kê quyền.
6. Yêu cầu phi chức năng
| Khía cạnh | Yêu cầu |
|---|---|
| Tương đương kết quả phân quyền | Việc giải quyết phủ qua các trục tài nguyên, hành động và phạm vi giữ nguyên kết quả cho phép/từ chối so với mô hình phẳng trước đó |
| Ổn định khi seed lại | Các cạnh cấu trúc (hành động / tài nguyên / phạm vi) và mã quyền seed không đổi khi chạy lại; mã đã gỡ khỏi khai báo được dọn tự động |
| Phạm vi merchant mỗi request | Một dòng cấp quyền được giải quyết trong đúng một merchant đang hoạt động cho mỗi request; phạm vi organizer mở rộng xuống cây phạm vi, không qua từng dòng thành viên |
| Hiệu năng / quy mô | Số dòng cấp quyền mỗi vai trò gộp từ khoảng 950 xuống còn vài chục, giảm tải nạp chính sách và chi phí đối chiếu mỗi request |
| Khả năng bảo trì | Một lệnh gọi khai báo cho mỗi controller; không còn phần khai báo quyền viết tay riêng lẻ theo từng module |
| i18n | Tên và mô tả quyền hiển thị cho người dùng vẫn song ngữ (Anh và Việt) |
7. UX & Luồng
Đợt này là hạ tầng phân quyền, không có màn hình người dùng cuối riêng. Nó thể hiện qua các view cây quyền của vai trò đã có sẵn (trong màn tạo/sửa vai trò), nơi các dòng cấp quyền thô giờ hiển thị gọn thành vài dòng thay vì gần 950 dòng như trước. Một màn phân cấp quyền riêng biệt chưa được dựng; trang Phân cấp quyền và Permission Matrix là bản đồ khái niệm trên wiki, không phải màn hình của app.
8. Dữ liệu & Miền nghiệp vụ
| Thực thể | Vai trò |
|---|---|
| Quyền (mã tài nguyên) | Danh mục module, đối tượng và thao tác tuỳ biến - không còn gần 950 dòng theo từng thao tác |
| Bậc thang hành động | Các cạnh kế thừa quản lý/ghi/đọc/thực thi/tạo/sửa/xoá |
| Cạnh tài nguyên | Cạnh gom-lên-module và lồng liên-thực-thể/thao tác |
| Cạnh phạm vi | Organizer bao merchant, để dòng cấp quyền theo phạm vi organizer vươn tới mọi merchant |
| Dòng cấp quyền | Vai trò hoặc người dùng, tài nguyên, hành động, phạm vi, hiệu lực cho phép/từ chối |
| Ma trận vai trò→cấp quyền | Gán thô theo kiểu khai báo (owner → quản lý Sale) |
Chỉ mang tính khái niệm - toàn bộ schema và mô hình RBAC theo phạm vi merchant nằm trong tài liệu developer RBAC và Casbin Authorization.
9. Phụ thuộc & Giả định
Phụ thuộc vào
| # | Feature | Phụ thuộc điều gì |
|---|---|---|
| 01 | Engine RBAC theo phạm vi merchant | cung cấp các trục hành động, tài nguyên và phạm vi cùng việc lồng thao tác theo mã có dấu chấm. |
| 02 | Vai trò cố định & vai trò tuỳ biến (URD-ROLE · URD-CROLE) | vai trò là chủ thể của dòng cấp quyền. |
| 03 | Danh mục quyền & cấp/thu hồi quyền (URD-PERM · URD-GRANT) | danh mục mà phân cấp này định hình lại. |
| 04 | Quyền hiệu lực & phạm vi (URD-EFF) | việc giải quyết vẫn nằm trong đúng một merchant đang hoạt động. |
| 05 | Lõi nền tảng & identity | mô hình RBAC theo phạm vi merchant, bộ nạp chính sách, engine đối chiếu (lõi nền tảng); vai trò, quyền, dòng cấp quyền (identity). |
Giả định
| # | Giả định | Sai thì sao |
|---|---|---|
| 01 | Mã đối tượng ổn định - phân cấp mang tính bổ sung, không đổi mã đã có. | Mã đối tượng không ổn định thì mọi liên kết phân cấp (gom-lên-module, lồng thao tác qua mã có dấu chấm) gắn sai đối tượng, và các dòng cấp quyền cũ có thể phủ nhầm hoặc bỏ sót tài nguyên. |
| 02 | Quan hệ organizer-merchant sẵn có để seed các cạnh phạm vi. | Không có quan hệ đó thì cạnh phạm vi organizer→merchant không dựng được, vai trò trụ sở không vươn xuống merchant con qua một dòng cấp quyền duy nhất. |
| 03 | Một bộ kiểm thử hồi quy bao phủ launchpad và mọi route được gác quyền, để so kết quả cho phép/từ chối. | Thiếu bộ kiểm thử đó thì không có cách nào xác nhận việc giải quyết phủ qua ba trục mới cho ra đúng kết quả cho phép/từ chối như mô hình phẳng cũ - một sai lệch âm thầm có thể lọt qua. |
10. Kế hoạch phát hành & Tiêu chí
| Khía cạnh | Kế hoạch |
|---|---|
| Phase | P3 - xem danh mục tính năng URD (HIER + DECL) |
| Rollout | Theo giai đoạn: (1) bộ công cụ khai báo, seed bậc thang hành động, gộp dòng cấp quyền owner/cashier/employee về dòng thô theo từng module; (2) cây phạm vi organizer-merchant và dòng cấp quyền quản lý theo phạm vi organizer, thay cho việc thêm dòng thành viên cho từng merchant; (3) chuyển bộ công cụ khai báo sang framework nền tảng (một PRD riêng) |
| Migration | Data migration: các dòng thành viên owner theo từng merchant hiện có chuyển sang dòng cấp quyền theo phạm vi organizer; bước seed cạnh cấu trúc chạy lúc khởi động backend |
| Tiêu chí phát hành | Số dòng cấp quyền mỗi vai trò gộp xuống còn vài chục; seed lại không đổi kết quả; launchpad và mọi route được gác quyền giữ nguyên kết quả cho phép/từ chối |
| Theo dõi | Số dòng cấp quyền mỗi vai trò, kiểm tra tính ổn định khi seed lại, bộ kiểm thử hồi quy cho phép/từ chối phân quyền |
Tham chiếu
- URD: Quyền - Phân cấp tài nguyên, hành động & phạm vi · Khai báo quyền
- Xây trên: Vai trò cố định · Vai trò tuỳ biến · Danh mục quyền · Cấp/thu hồi quyền · Quyền hiệu lực & phạm vi
- Bản đồ: Phân cấp quyền · Permission Matrix
- Module: Quyền - URD
- Developer: @nx/identity · RBAC · Casbin Authorization
Rủi ro & Câu hỏi mở
| # | Rủi ro / câu hỏi | Giảm thiểu / trạng thái |
|---|---|---|
| 01 | Việc giải quyết phủ lệch khỏi mô hình phẳng trước đó | Kiểm thử hồi quy launchpad và mọi route được gác quyền, so kết quả cho phép/từ chối giống hệt |
| 02 | Danh sách gom-lên-module chính xác (đối tượng nào thuộc module nào) | Khai báo tường minh theo từng module ngay tại nơi đối tượng được định nghĩa, rà soát khi thêm module mới |
| 03 | Thao tác thực thi của các thao tác tuỳ biến cấp theo từng thao tác hay chỉ gộp vào quản lý | Mở - quyết định trước khi mở rộng bậc thang hành động cho các thao tác tuỳ biến mới |
| 04 | Di chuyển các dòng thành viên theo từng merchant hiện có sang dòng cấp quyền theo phạm vi organizer | Mở - cần định nghĩa hình dạng data migration trước khi cạnh phạm vi organizer-merchant được seed |
| 05 | Seed lại có thể làm rớt mã quyền đang dùng | Việc dọn mã cũ do chính khai báo trong code điều khiển; chạy lại không đổi kết quả |
Câu hỏi thường gặp
| # | Câu hỏi | Trả lời |
|---|---|---|
| 01 | Việc này có cần đổi mã đối tượng đang dùng không? | Không. Phân cấp mang tính bổ sung - đối tượng giữ nguyên mã; việc nhóm và lồng diễn ra qua các cạnh tài nguyên và mã thao tác có dấu chấm. |
| 02 | Quản lý Sale khác gì một mẫu ký tự đại diện kiểu "mọi thứ thuộc sale"? | Độ thô đến từ cây tài nguyên cộng với hành động quản lý, không phải mẫu ký tự đại diện. Không có quyền dạng mẫu đại diện tuỳ ý trong hệ thống. |
| 03 | Một owner trụ sở sẽ vươn tới mọi merchant bằng cách nào? | Qua một dòng cấp quyền theo phạm vi organizer, giải quyết xuống cây phạm vi organizer-merchant. Đây là trục phạm vi của mô hình phân cấp; hiện tại việc vươn tới của vai trò trụ sở vẫn cần thêm dòng thành viên cho từng merchant. |
| 04 | Một hành động rộng hơn có bao các hành động hẹp hơn không? | Có. Quản lý bao {ghi, đọc, thực thi} và ghi bao {tạo, sửa, xoá}, nên một dòng cấp quyền quản lý đáp ứng mọi request hẹp hơn. |
| 05 | Bộ công cụ khai báo có nằm trong framework nền tảng không? | Trong đợt này thì không - nó được xây trong BANA trước, với hướng đi rõ ràng để chuyển vào framework nền tảng ở một PRD sau. |
| 06 | Điều gì xảy ra với một mã quyền bị gỡ khỏi khai báo trong code? | Việc seed dọn mã đó tự động, cùng với các dòng cấp quyền của nó - không tồn đọng dòng cũ. |