Báo cáo chuyển đổi warehouse-service

Kho · warehouse/delete · api

DELETE/api/client/page/warehouse/delete

Vòng
1
Cổng G1–G6
PENDING_SONAR
Bước
sonar-check
Nhánh đích
feat/thongpd/warehouse-create

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ới src/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_NEW readOnly trên 2 luồng, delete chạy transaction riêng của SimpleJpaRepository. Mới: một @Transactional ở WarehouseManagementService.java:115 bao 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_getapplock warehouse: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. comId là 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.

Nguồn của trang này

  • Phân tích luồng migrations/warehouse/delete/verify/round-1/flow-analysis.md vòng 1 · sha256 c8f61a86508b

v1 dựng 2026-11-06 00:42 · base commit đích a06ccfa6d4