Phân tích luồng sau chuyển — warehouse/delete — vòng 1
F-01 Xoá kho
- Điểm vào: cũ
easyposbackend1:src/main/java/vn/softdreams/easypos/web/rest/WarehouseResource.java:80-83→ mớisrc/main/java/com/softdreams/easypos/warehouse/presentation/web/warehouse/WarehouseController.java:93-96 - Quy tắc: R-01, R-02, R-03, R-04, R-05, R-06, R-07, R-08, R-09, R-10
Sơ đồ luồng mới (theo code thực tế)
sequenceDiagram
participant C as Client
participant WC as WarehouseController.deleteWarehouse
participant MS as WarehouseManagementService.deleteWarehouse (@Transactional)
participant CC as CurrentCompanyService.requireCompanyOfCurrentUser
participant WR as WarehouseRelationalRepository
participant UC as WarehouseUsageChecker.validateNotInUse
participant IR as InventoryRelationalRepository.findOnHandsByWarehouse
participant BA as BillRelationalAdapter.existsByWarehouse
participant RR as RsInoutwardRelationalRepository.existsByFromWarehouse
C->>WC: DELETE /delete?comId&id (@RequestParam Integer, thiếu → 400 starter)
WC->>MS: deleteWarehouse(comId, id)
MS->>CC: comId null/<=0 → 400 INVALID_COMPANY; khác token → 400 COMPANY_NOT_OF_CURRENT_USER
MS->>WR: findById(comId,id) → rỗng: SystemException(WAREHOUSE_NOT_FOUND) 500
MS->>MS: Warehouse.validateDeletable(): code null → 500 EXCEPTION_ERROR; "WH1" → 400 DELETE_WAREHOUSE_DEFAULT_INVALID
MS->>UC: validateNotInUse(comId,id)
UC->>IR: native SELECT 10 cột (inventory LEFT JOIN batches_detail), kiểm kiểu mọi dòng → sai: 500 EXCEPTION_ERROR
UC->>UC: WarehouseDeletePolicy.validateOnHands: null → 500; >0 → 400 WAREHOUSE_DELETE_INVALID_ON_HAND
UC->>BA: EXISTS bill/bill_product → true: 400 WAREHOUSE_DELETE_EXIST_DOCUMENT (bỏ qua câu phiếu kho)
UC->>RR: EXISTS rs_inoutward/detail (from_warehouse_id) → true: 400 WAREHOUSE_DELETE_EXIST_DOCUMENT
MS->>WR: deleteById: native DELETE warehouse WHERE com_id AND id (0 dòng vẫn OK)
WC-->>C: 200 BaseResponseDTO.ok(null, SUCCESS_UPDATE)
Đối chiếu từng bước
| # | Luồng cũ (repo:path:dòng) |
Luồng mới (path:dòng) |
Kết luận |
|---|---|---|---|
| 1 | Nhận @RequestParam Integer comId, id — easyposbackend1:.../web/rest/WarehouseResource.java:80-83 |
presentation/web/warehouse/WarehouseController.java:93-94 |
KHỚP (body 400 khác: có chủ đích D-02) |
| 2 | Không kiểm công ty — LF-20 | application/company/CurrentCompanyService.java:30-38, gọi ở WarehouseManagementService.java:117 |
KHÁC có chủ đích (D-01) |
| 3 | findByComIdAndId → BaseInternalServerException(WAREHOUSE_NOT_FOUND) — easyposbackend1:.../service/impl/WarehouseServiceImpl.java:39-41 |
WarehouseManagementService.java:118-119 |
KHỚP (500, giữ theo D-04) |
| 4 | getCode().equals("WH1") → 400; NPE → 417 — WarehouseServiceImpl.java:44-49 |
domain/warehouse/model/Warehouse.java:143-150 |
KHỚP; null 417→500 có chủ đích (D-03) |
| 5 | findAllByWarehouseId (i.* + batchId, onHand) rồi dựng BaseInventory cho mọi dòng — WarehouseServiceImpl.java:51-68; easyposbase:.../BaseInventoryRepository.java:27 |
infrastructure/relational/inventory/adapter/InventoryRelationalRepository.java:34-100 (SELECT đúng 10 cột, kiểm kiểu) |
KHÁC có chủ đích (D-13: chỉ đọc 10 cột, kiểm kiểu bằng isInstance) |
| 6 | forEach: onHand.compareTo(ZERO) > 0 → 400; null → NPE 417 — WarehouseServiceImpl.java:69-75 |
domain/warehouse/policy/WarehouseDeletePolicy.java:28-36 |
KHỚP (dùng i.on_hand theo D-05; 417→500 theo D-03) |
| 7 | checkExistsBillOrRsInout: hai câu song song, REQUIRES_NEW readOnly — easyposbackend1:.../CheckExistDocumentServiceImpl.java:25-72 |
application/warehouse/WarehouseUsageChecker.java:46-49 → BillRelationalAdapter.java:38-50,75-81, RsInoutwardRelationalRepository.java:27-38,54-60 |
KHÁC có chủ đích (D-07 tuần tự, D-11 chung transaction); câu SQL KHỚP BaseWarehouseRepository.java:58-82 |
| 8 | baseWarehouseRepository.delete(warehouse) (DELETE ... WHERE id=?, transaction riêng, không có thì bỏ qua) — WarehouseServiceImpl.java:80-82 |
WarehouseRelationalRepository.java:53-57,145-150 |
KHỚP (có thêm com_id vào WHERE, cùng kết quả vì kho đã tìm theo comId) |
| 9 | Không xoá inventory (dòng đã comment) — WarehouseServiceImpl.java:81 |
Không có lệnh ghi khác | KHỚP (D-08) |
| 10 | BaseResultDTO(SUCCESS, SUCCESS_UPDATE, true, null) — WarehouseServiceImpl.java:83 |
WarehouseController.java:95 |
KHỚP mã; định dạng body theo D-02, reason giữ theo D-10 |
| 11 | Interceptor kiểm gói — LF-21 | Không có | KHÁC có chủ đích (D-12) |
Dữ liệu và side effect
| Bảng / topic / cache | Cũ (đọc/ghi, điều kiện) | Mới (đọc/ghi, điều kiện) | Ghi chú |
|---|---|---|---|
warehouse |
Đọc com_id, id; DELETE id |
Đọc com_id, id (JPA); DELETE native com_id, id |
Không kiểm số dòng bị xoá, khớp R-07 |
inventory + batches_detail |
Đọc i.* + 2 cột tính, com_id, warehouse_id |
Đọc 10 cột, cùng FROM/JOIN/WHERE, không ORDER BY | D-13; join chỉ làm nhân bản dòng |
bill, bill_product |
EXISTS, b.com_id, bp.warehouse_id, join norm_quarter + id |
Như cũ | Mọi trạng thái hoá đơn |
rs_inoutward, rs_inoutward_detail |
EXISTS, rs.com_id, rsd.from_warehouse_id |
Như cũ | Chỉ xét kho đi (D-06) |
| Event / cache | Không | Không | — |
| Redis gói dịch vụ | Đọc (interceptor) | Không | D-12 |
Transaction, khoá, idempotency, tenant
- Ranh giới transaction: cũ không có transaction bao ngoài. Mỗi câu đọc chạy riêng (có thể qua replica), kiểm chứng từ chạy
REQUIRES_NEWreadOnly trên 2 luồng, delete chạy transaction riêng củaSimpleJpaRepository. Mới: một@TransactionalởWarehouseManagementService.java:115bao toàn bộ kiểm và xoá trên DB chính (D-11). Câu đầu tiên trong transaction là SELECT trên bảng nên không vướng K-12. Lỗi nghiệp vụ ném trước khi ghi nên rollback không mất gì. - Khoá / cập nhật nguyên tử: cả cũ và mới đều không khoá. Hoá đơn hoặc tồn tạo đồng thời sau bước kiểm vẫn có thể lọt (cả hai như nhau, R-09). Xoá không lấy
sp_getapplockwarehouse:save:<comId>như thêm/sửa; code cũ cũng không khoá. - Idempotency, thời điểm phát event: không phát event. Xoá lặp lại cùng kho: lần sau ra 500
WAREHOUSE_NOT_FOUND(D-04). Kho bị xoá giữa lúc tìm và lúc xoá thì DELETE 0 dòng vẫn trả 200, như cũ. - Lọc
comId: có ở mọi truy vấn và ở lệnh DELETE.comIdlà giá trị đã khớp với token.
Lệch so với luồng mới đã duyệt (spec mục 6)
Có lệch về cấu trúc, không lệch hành vi: các bước kiểm tồn, hoá đơn, phiếu kho đi qua WarehouseUsageChecker.validateNotInUse (application/warehouse/WarehouseUsageChecker.java:44-50) thay vì gọi thẳng từ WarehouseManagementService. Thứ tự và mã lỗi đúng như spec mục 6.
Dev cần chú ý khi review
| Vị trí | Vì sao |
|---|---|
InventoryRelationalRepository.java:34-100 |
Mô phỏng Tuple.get(alias, Class) của Hibernate 5 bằng isInstance trên kiểu cột trả về từ Hibernate 6. Kiểu thật từng cột trên MSSQL chưa kiểm (ví dụ tinyint/char(1)/bigint cho ra Byte/Character/Long thì 500). Code cũ đọc i.* nên một cột ngoài 10 cột có kiểu Hibernate 5 không map được sẽ làm code cũ lỗi, còn code mới thì không. Đây là UNVERIFIED theo D-13/K-13, cần chạy thử trên DB thật. |
WarehouseRelationalRepository.java:145-150 |
Native DELETE không kiểm số dòng, đây là chủ đích của R-07. Không đổi sang JPA remove/deleteById của Spring Data: flush 0 dòng sẽ ra StaleStateException 500. |
WarehouseUsageChecker.java:46-47 |
Nhờ || đoản mạch, có hoá đơn thì không chạy câu phiếu kho (D-07). Câu hoá đơn lỗi DB thì ra 500 ngay. |
BillRelationalAdapter.java:38-50, RsInoutwardRelationalRepository.java:27-38 |
Native SQL chép từ BaseWarehouseRepository.java:58-82 (join norm_quarter). Chỉ có test mock, UNVERIFIED trên MSSQL (K-13). |
WarehouseManagementService.java:115-123 |
Toàn bộ chạy trong một transaction trên DB chính (D-11), không khoá. Khác code cũ ở chỗ không đọc replica và kiểm chứng từ không chạy ở transaction riêng. |
Warehouse.java:143-150 |
code null ra 500 EXCEPTION_ERROR thay cho 417 của NPE cũ (D-03). So "WH1" phân biệt hoa thường, không trim. |