Phân tích luồng sau chuyển — inventory/get-detail-product — vòng 1
F-01 Tồn chi tiết một ĐVT theo kho và lô
- Điểm vào: cũ
easyposbase:src/main/java/sds/ep/baseservice/controllers/BaseInventoryResource.java:38-44→ mớisrc/main/java/com/softdreams/easypos/warehouse/presentation/web/inventory/InventoryController.java:42-53 - Quy tắc: R-01 … R-11
Sơ đồ luồng mới (theo code thực tế)
sequenceDiagram
participant C as Client
participant W as InventoryController.getDetailProduct
participant A as InventoryQueryService.getProductStockDetails (@Transactional, không readOnly)
participant K as CurrentCompanyService.requireCompanyOfCurrentUser
participant P as InventoryRelationalRepository.findProductStockDetails
participant J as ProductStockDetailJdbcRepository.findProductStockDetails
participant M as InventoryWebMapper.toResponses
C->>W: GET /api/client/page/inventory/get-detail-product?comId&ppuId (@RequestParam Integer, thiếu/sai → 400 INVALID_REQUEST)
W->>A: getProductStockDetails(comId, ppuId)
A->>K: requireCompanyOfCurrentUser(comId)
K-->>A: comId<=0 → 400 INVALID_COMPANY / không claim → TOKEN_INVALID_OR_EXPIRED / khác → 400 COMPANY_NOT_OF_CURRENT_USER
A->>P: findProductStockDetails(comId, ppuId)
P->>J: findProductStockDetails(comId, ppuId)
J->>J: JdbcTemplate.query (inventory ⋈ warehouse ⋈ ppu ⟕ batches_detail, ORDER BY như cũ)
J-->>P: List<ProductStockDetailResult>
P-->>A: list (DataAccessException → SystemException EXCEPTION_ERROR 500)
A-->>W: list
W->>M: toResponses(list) (onHand null→0, ppuidAndWarehouseId)
W-->>C: 200 BaseResponseDTO.ok(data, SUCCESS_GET_DETAIL)
Đố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 | Interceptor kiểm gói dịch vụ (LF-21) | không có | KHÁC có chủ đích (D-05) |
| 2 | @RequestParam Integer comId, ppuId, lỗi → 400 body rỗng base:controllers/BaseInventoryResource.java:39-42, LF-03 |
InventoryController.java:44-47, lỗi → 400 INVALID_REQUEST |
KHÁC có chủ đích (D-02) |
| 3 | Không kiểm công ty (LF-20) | InventoryQueryService.java:44 → CurrentCompanyService.java:30-38 | KHÁC có chủ đích (D-01) |
| 4 | Transaction đọc-ghi cấp class base:services/impl/BaseInventoryServiceImpl.java:55 |
@Transactional không readOnly, InventoryQueryService.java:42 |
KHỚP (D-04) |
| 5 | getInventoryProducts(comId, List.of(ppuId), null, 1, null, 1) BaseInventoryServiceImpl.java:1088 → BaseInventoryRepositoryImpl.java:33-36 |
InventoryRelationalRepository.java:51-53 → ProductStockDetailJdbcRepository.java:75-85 | KHỚP (lệch tên class: F-logic-1) |
| 6 | SQL JOIN/WHERE/CASE/ORDER BY BaseInventoryRepositoryImpl.java:51-99 |
ProductStockDetailJdbcRepository.java:30-47 | KHỚP |
| 7 | Map tuple → PPUInventoryItem, batchId lấy vì isGetBatchId=true :107-122 |
ROW_MAPPER ProductStockDetailJdbcRepository.java:49-60 | KHỚP |
| 8 | Lỗi SQL → 417 EXCEPTION_ERROR (LF-13) |
InventoryRelationalRepository.java:54-56 → 500 EXCEPTION_ERROR |
KHÁC có chủ đích (D-06) |
| 9 | Getter getOnHand null→0, getPPUIdAndWarehouseId PPUInventoryItem.java:142-160 |
InventoryWebMapper.java:25-38 | KHỚP |
| 10 | Jackson mặc định, 14 key luôn xuất | ProductStockDetailResponse.java:31 @JsonInclude(ALWAYS) |
KHỚP |
| 11 | BaseResultDTO(SUCCESS, SUCCESS_GET_DETAIL, true, list) BaseInventoryServiceImpl.java:1089 |
InventoryController.java:51-52 ok(response, SUCCESS_GET_DETAIL); list rỗng → mất key data |
KHÁC có chủ đích (D-02, D-07) |
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ú |
|---|---|---|---|
inventory |
đọc, i.com_id=:comId, i.ppu_id in (:ppuId) |
đọc, i.com_id=?, i.ppu_id IN (?) |
khớp |
warehouse |
đọc, w.com_id=:comId, status=1, is_sellable=1 |
đọc, cùng điều kiện (hằng ở ProductStockDetailJdbcRepository.java:22-24) | khớp |
product_product_unit |
đọc, ppu.id=i.ppu_id, không lọc com_id |
giống cũ | khớp |
batches_detail |
LEFT JOIN bd.inventory_id=i.id |
giống cũ | khớp |
| Gói dịch vụ | đọc ở interceptor | không | D-05 |
Không ghi bảng nào, không cache, không event, ở cả code cũ và mới.
Transaction, khoá, idempotency, tenant
- Ranh giới transaction: cũ
@Transactionalcấp class củaBaseInventoryServiceImpl, đọc-ghi → mới@Transactional(không readOnly) ởInventoryQueryService.getProductStockDetails. JdbcTemplate dựng từDataSourcerouting nên dùng chung connection với transaction JPA, và vì không readOnly nên đọc DB chính (D-04). - Khoá / cập nhật nguyên tử: không có ở cả hai (chỉ đọc).
- Idempotency, thời điểm phát event: không áp dụng.
- Lọc
comId:w.com_idvài.com_idnhư cũ. Mới thêm kiểmcomIdphải là công ty trong token (D-01).
Lệch so với luồng mới đã duyệt (spec mục 6)
Chỉ thêm tầng ProductStockDetailJdbcRepository sau InventoryRelationalRepository (F-logic-1). Thứ tự gọi và điểm kiểm công ty khớp spec mục 6.
Dev cần chú ý khi review
| Vị trí | Vì sao |
|---|---|
| ProductStockDetailJdbcRepository.java:30-47, 76-84 | Câu SQL native và thứ tự tham số theo vị trí (comId hai lần). Mới chạy trên H2, MSSQL thật UNVERIFIED (K-13). Thứ tự lô trong cùng kho do DB quyết (D-03). |
| ProductStockDetailJdbcRepository.java:51 | getObject("is_primary", Boolean.class) phụ thuộc kiểu cột BIT trên MSSQL. |
| InventoryRelationalRepository.java:51-57 | Nhánh lỗi: chỉ DataAccessException thành EXCEPTION_ERROR. Lỗi khác ra UNCERTAIN_ERROR của starter. |
| InventoryQueryService.java:42-46 | Không readOnly là có chủ đích (D-04). InventoryQueryService vốn dùng cho các context khác, nay phụ thuộc thêm CurrentCompanyService (WarehouseTransferServiceTest.java:135 đã sửa theo). |
| ProductStockDetailResponse.java:31 | @JsonInclude(ALWAYS) giữ đủ 14 key. Bỏ annotation này thì key null hoặc "" biến mất do mapper starter để NON_EMPTY. |
| InventoryController.java:51-52 | List rỗng thì JSON không có key data (D-07, đang chờ người chốt chung với warehouse/get-with-paging). |