Phân tích luồng sau chuyển — inventory/batch-on-hands — vòng 1
F-01 Tồn theo lô của danh sách ppu trong một kho
- Điểm vào: cũ
backend:src/main/java/vn/softdreams/easypos/web/rest/RsInoutWardManagementResource.java:337-341(class@RequestMapping("/api"), dòng 39) → mớisrc/main/java/com/softdreams/easypos/warehouse/presentation/web/inventory/BatchOnHandController.java:38-48 - 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 BatchOnHandController.getBatchOnHands
participant A as InventoryQueryService.getBatchOnHands (@Transactional)
participant U as CurrentUserAdapter.findCompanyId
participant P as InventoryRelationalRepository.findBatchOnHands
participant J as BatchOnHandJdbcRepository.findBatchOnHands
participant M as InventoryWebMapper.toBatchOnHandResponse
C->>W: POST /api/client/rs-inoutward/batch-on-hands {comId, warehouseId, ppuIds}
W->>A: getBatchOnHands(warehouseId, ppuIds) (comId bị bỏ)
A->>A: ppuIds == null → SystemException(EXCEPTION_ERROR)
A->>U: findCompanyId() (claim companyId; rỗng → AuthenticationException TOKEN_INVALID_OR_EXPIRED)
A->>P: findBatchOnHands(companyId, warehouseId)
P->>J: JdbcTemplate.query(SQL, comId, comId, warehouseId)
J-->>P: rows (i ⋈ bd[batch_id not null] ⋈ b[com_id]) ORDER BY b.id, i.id, bd.id
P-->>A: List<BatchOnHandResult> (DataAccessException → SystemException EXCEPTION_ERROR)
A->>A: new HashSet(ppuIds); duyệt rows, ppuId ∈ set → HashMap.computeIfAbsent(ppuId).add(row)
A-->>W: Map<Integer, List<BatchOnHandResult>>
W->>M: map → LinkedHashMap<Integer, List<BatchOnHandItemResponse>> (id=batchId, minDate/quantity=null)
W-->>C: 200 BaseResponseDTO.ok(map, GET_BATCH_ON_HANDS_SUCCESS)
Đố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) | — | CHỈ CÓ Ở CŨ, có chủ đích (D-05) |
| 2 | Deserialize BatchOnHandsRequest (backend:dto/rsinoutward/BatchOnHandsRequest.java:8-10), body hỏng → 400 body rỗng |
record BatchOnHandsRequest (dto/request/BatchOnHandsRequest.java), 400 theo starter |
KHÁC có chủ đích (D-02) |
| 3 | Gọi service với warehouseId, ppuIds, bỏ comId (RsInoutWardManagementResource.java:339) |
BatchOnHandController.java:43-45 |
KHỚP |
| 4 | @Transactional(readOnly=true), thực tế đọc DB chính (RsInoutWardManagementServiceImpl.java:8797) |
@Transactional không readOnly (InventoryQueryService.java:91) |
KHÁC có chủ đích (D-08) |
| 5 | getUserWithAuthorities() lấy companyId (:8799) |
findCompanyId() (InventoryQueryService.java:96-97) |
KHÁC có chủ đích (D-03) |
| 6 | NPE khi ppuList null, xảy ra sau bước 5 và 7 (:8820) |
kiểm null trước bước 5 (InventoryQueryService.java:93-95) |
Mã lỗi giữ theo D-06; thứ tự kiểm khác (F-logic-1) |
| 7 | findAllByWarehouseId: i ⟕ bd, i.com_id, i.warehouse_id, không ORDER BY (base:repositories/BaseInventoryRepository.java:27-28) |
một câu JOIN (BatchOnHandJdbcRepository.java:27-41) |
KHÁC có chủ đích (D-04, D-09); tập dòng tương đương |
| 8 | Lọc batchId != null && ppuId ∈ Set (:8820-8824) |
bd.batch_id IS NOT NULL trong SQL + HashSet.contains (InventoryQueryService.java:98-101) |
KHỚP (null trong ppuIds được giữ, phần tử trùng tính một lần) |
| 9 | findAllByComIdAndIdIn(companyId, batchIds của cả kho) (:8825-8828; BatchesRepository.java:129), lô không thuộc công ty bị bỏ |
JOIN batches b ON b.id = bd.batch_id AND b.com_id = ? |
KHỚP về kết quả; kho > 2048 lô cũ lỗi, mới thì không (D-09) |
| 10 | Duyệt lô theo thứ tự DB × dòng tồn của lô, đưa vào HashMap (:8829-8845) |
ORDER BY b.id, i.id, bd.id + computeIfAbsent theo thứ tự dòng (InventoryQueryService.java:99-104) |
KHÁC có chủ đích (D-04), giống thứ tự DB thường trả |
| 11 | Item: id/code/lotNo/status/mfgDate/expDate/onHand=bd.on_hand, minDate/quantity null (:8833-8840; BaseBatchOnHandItem.java:11-22) |
InventoryWebMapper.toBatchOnHandItem + BatchOnHandItemResponse @JsonInclude(ALWAYS) |
KHỚP; ngày phụ thuộc múi giờ JVM/Jackson (F-logic-2) |
| 12 | BaseResultDTO(SUCCESS, GET_BATCH_ON_HANDS_SUCCESS, true, map) (:8846) |
BaseResponseDTO.ok(map, GET_BATCH_ON_HANDS_SUCCESS) (BatchOnHandController.java:46-47) |
KHÁC có chủ đích (D-02); map rỗng mất key data (D-07) |
| 13 | Lỗi SQL → 417 EXCEPTION_ERROR (LF-13) |
InventoryRelationalRepository.java:68-74 bọc DataAccessException → SystemException 500 |
KHÁC có chủ đích (D-06) |
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 mọi dòng của kho, com_id = companyId, warehouse_id = ? |
Đọc, cùng điều kiện, chỉ dòng có lô | tenant giữ nguyên |
batches_detail |
Đọc qua LEFT JOIN, on_hand qua CASE (cả hai cột đều scale 6) |
Đọc qua INNER JOIN, batch_id IS NOT NULL, lấy thẳng bd.on_hand |
Kết quả như nhau với dòng có lô; scale như nhau (InventoryJpaEntity/BatchDetailJpaEntity precision 21, scale 6) |
batches |
Đọc com_id = companyId AND id IN (...) |
JOIN b.com_id = ? |
Không lọc trạng thái/HSD ở cả hai |
| Cache user / bảng user, company | Đọc (getUserWithAuthorities) |
Không đọc, chỉ claim JWT | D-03 |
| Bảng gói dịch vụ | Đọc (interceptor) | Không | D-05 |
| Ghi / event / cache ghi | Không | Không | Không có side effect |
Transaction, khoá, idempotency, tenant
- Ranh giới transaction: cũ
@Transactional(readOnly=true)ở service impl, thực tế đọc DB chính → mới@Transactionalkhông readOnly ởInventoryQueryService.getBatchOnHands, route về primary theo ADR-0201 (D-08).JdbcTemplatedùng beanDataSourcegiốngProductStockDetailJdbcRepository. - 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:i.com_idvàb.com_idđều lấy từ claim token;comIdtrong body bị bỏ như cũ. Không N+1: một câu SQL thay cho hai câu của code cũ.
Lệch so với luồng mới đã duyệt (spec mục 6)
không. Code đi đúng sơ đồ mục 6, kể cả việc kiểm ppuIds null trước findCompanyId (chỗ đó ghi ở F-logic-1 vì chưa có D-xx).
Dev cần chú ý khi review
| Vị trí | Vì sao |
|---|---|
BatchOnHandJdbcRepository.java:27-41 |
Một câu JOIN thay cho hai truy vấn cũ. Thứ tự tham số comId, comId, warehouseId (dòng 68) phải khớp b.com_id = ? rồi đến i.com_id = ?. ORDER BY thay cho thứ tự không xác định của code cũ (D-04). Mới chỉ test trên H2 (K-13). |
BatchOnHandJdbcRepository.java:72-74 |
Ngày gắn múi giờ JVM; chuỗi JSON còn tùy cấu hình múi giờ của Jackson (F-logic-2). |
InventoryQueryService.java:93-97 |
Thứ tự kiểm ppuIds null và token khác code cũ (F-logic-1). |
InventoryQueryService.java:98-104 |
Phải dùng HashSet (nhận phần tử null); thứ tự item trong list dựa vào thứ tự dòng mà repository trả. |
InventoryRelationalRepository.java:68-74 |
Nhánh lỗi DB → SystemException EXCEPTION_ERROR (500 thay 417, D-06). |
BatchOnHandItemResponse.java (@JsonInclude(ALWAYS)) |
Thiếu annotation này thì mapper NON_EMPTY của starter sẽ bỏ các key null như minDate, quantity, lotNo… |