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

batch · batch/by-id · api

POST/api/client/page/product-batch/by-id

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

Phân tích luồng sau chuyển — batch/by-id — vòng 1

logic-reviewer viết dựa trên code đã implement, đối chiếu với code cũ. Viết tắt: backend: = easyposbackend1:src/main/java/vn/softdreams/easypos/, BRI = backend:repository/impl/BatchRepositoryImpl.java, PSI = backend:service/impl/ProductManagementServiceImpl.java, new: = src/main/java/com/softdreams/easypos/warehouse/, PBR = new:infrastructure/relational/batch/adapter/ProductBatchReader.java, BQS = new:application/batch/BatchQueryService.java.

F-01 Lô và tồn theo lô của sản phẩm

  • Điểm vào: cũ backend:web/rest/ProductManagementResource.java:967-972 → mới new:presentation/web/batch/ProductBatchController.java:42
  • Quy tắc: R-01, R-02, R-03, R-04, R-05, R-06, R-07

Sơ đồ luồng mới (theo code thực tế)

sequenceDiagram
    participant C as Client
    participant PC as ProductBatchController.byId
    participant M as BatchWebMapper
    participant QS as BatchQueryService.getProductBatches (@Transactional)
    participant CU as CurrentUserPort.findCompanyId
    participant BR as BatchRelationalRepository.findProductBatches
    participant RD as ProductBatchReader.read/doRead
    C->>PC: POST ProductBatchRequest (không validate)
    PC->>M: toProductBatchQuery
    PC->>QS: getProductBatches(query)
    QS->>CU: findCompanyId → rỗng: BadRequestException(TOKEN_EXPIRED)
    QS->>QS: comId null → SystemException(EXCEPTION_ERROR, cause NPE)
    QS->>QS: comId ≠ token → SystemException(COMPANY_NOT_EXISTS_CODE)
    QS->>QS: requireValidPage: page<0 / size<1 → SystemException(EXCEPTION_ERROR, cause IAE)
    QS->>BR: findProductBatches(tokenCompanyId, query)
    BR->>RD: read(comId, query)
    RD->>RD: dựng FROM/WHERE + status + keyword (BaseNormalizedName.of)
    RD->>RD: COUNT(1) (null → "")
    RD->>RD: PageRequest.of nếu page & size; SELECT (không phân trang: null → ""; có phân trang: bind null, firstResult (int) offset, âm → IAE)
    RD->>RD: map Object[] → ProductBatchLine; count = size hoặc PageImpl.getTotalElements
    RD-->>RD: lỗi Persistence/IAE/ClassCast → SystemException(EXCEPTION_ERROR)
    PC->>M: toProductBatchResponses (ngày yyyy-MM-dd)
    PC-->>C: 200 count((long) n).ok(data, SUCCESS_GET_LIST)

Đố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 body GetProductBatchRequest, validateInput không có ràng buộc — backend:web/rest/ProductManagementResource.java:968-969 ProductBatchController.java:42, không @Valid KHỚP
2 Body hỏng → 400 body rỗng (LF-03) Starter 400 INVALID_REQUEST KHÁC có chủ đích (D-02)
3 getUserWithAuthorities(comId): JWT không có công ty → 400 TOKEN_EXPIRED — backend:service/UserService.java:1844-1847 BQS:60-61 KHỚP
4 comId null → NPE ở companyId.equals → 417 — UserService.java:1849 BQS:62-68 500 EXCEPTION_ERROR KHÁC có chủ đích (D-03)
5 comId ≠ token → 500 COMPANY_NOT_EXISTS_CODE — UserService.java:1849-1851 BQS:69-71 KHỚP
6 Kiểm user/cache user, mật khẩu đổi — UserService.java:1853-1906; kiểm gói (LF-21) không có KHÁC có chủ đích (D-01, D-08)
7 request.getPageable() → PageRequest.of ném IAE trước câu đếm — PSI:2326, backend:dto/CommonPageable.java:11-16 BQS:72,77-87 ném trước khi gọi repository; 500 EXCEPTION_ERROR KHỚP (mã HTTP: D-03)
8 FROM/WHERE nguyên văn, comId từ request — BRI:210-218 PBR:30-37,85-87, comId từ token (đã bằng request ở bước 5) KHỚP
9 status != null → and b.status = :status — BRI:219-222 PBR:89-92 KHỚP (D-04 giữ)
10 keyword không null/rỗng → like "%" + normalizedName + "%" — BRI:223-226, model:util/BaseCommon.java:1371-1389 PBR:93-96, BaseNormalizedName.of KHỚP với một phần tử; port lệch ở trường hợp nhiều phần tử (F-logic-1, không ảnh hưởng API)
11 COUNT(1), setParams null → "" — BRI:228-230, BaseCommon.java:332-341 PBR:97-99 KHỚP
12 Câu dữ liệu 8 cột, không ORDER BY — BRI:231-243 PBR:39-42,104 KHỚP (D-05 giữ)
13 Có phân trang: setParamsWithPageable bind thẳng null, setFirstResult((int) offset), setMaxResults — BRI:244-247, BaseCommon.java:372-386 PBR:107-115; offset âm → IAE trước setFirstResult (cũ do Hibernate ném) KHỚP
14 Không phân trang: setParams, new PageImpl<>(content) — BRI:248-252 PBR:105-106,118 count = lines.size() KHỚP
15 count = (int) PageImpl(content, pageable, total).getTotalElements() — BRI:247, PSI:2327 PBR:118 (cùng pageable long offset nên trường hợp quay vòng cũng khớp) KHỚP
16 Map @SqlResultSetMapping "ProductBatchDetail": id Integer, ngày ZonedDateTime, onHand BigDecimal, status type=String — backend:domain/Batches.java:60-77 PBR:122-140 theo vị trí; status String.valueOf; ngày qua BatchDateTimes KHỚP (D-06 giữ)
17 JSON ProductBatchDetail, ngày @JsonFormat yyyy-MM-dd — backend:dto/batches/ProductBatchDetail.java:14-18 BatchWebMapper.java:79-81,91-94, ProductBatchResponse @JsonInclude(ALWAYS) KHỚP
18 BaseResultDTO(SUCCESS, SUCCESS_GET_LIST, true, content, count) — PSI:2327 ProductBatchController.java:44-46; rỗng → mất key data KHÁC có chủ đích (D-02)
19 Lỗi SQL/map → 417 EXCEPTION_ERROR (LF-13) PBR:76-81 → 500 EXCEPTION_ERROR giữ cause KHÁC có chủ đích (D-03)

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, product_product_unit, batches_detail, batches, warehouse Đọc; mọi bảng com_id = :comId, ppu.is_primary = 1, ppu.product_id = :productId Đọc, cùng SQL Không ghi
Cache/bảng user Đọc (getUserWithAuthorities) Không D-01

Transaction, khoá, idempotency, tenant

  • Ranh giới transaction: cũ @Transactional cấp class của ProductManagementServiceImpl → mới @Transactional (không readOnly) trên BQS.getProductBatches, gọi từ controller qua proxy. Chỉ đọc.
  • Khoá / cập nhật nguyên tử: không có ở cả hai.
  • Idempotency, thời điểm phát event: không áp dụng (không ghi, không event).
  • Lọc comId: cả 5 bảng trong JOIN và WHERE; giá trị là công ty token, đã kiểm bằng comId request.
  • Nguồn đọc: cũ EntityManager readOnlyPersistenceUnit (BRI:27-29) → mới EntityManager mặc định (D-07 GIỮ DB chính, UNVERIFIED).

Lệch so với luồng mới đã duyệt (spec mục 6)

không

Dev cần chú ý khi review

Vị trí Vì sao
PBR:98 và PBR:105-108 Hai cách bind null khác nhau giữa câu đếm và câu dữ liệu có phân trang — cố ý giữ như code cũ (R-03); đừng "sửa" cho thống nhất
PBR:110-113 Ép (int) offset, ném IAE khi âm — cố ý giữ quay vòng như cũ (R-04)
PBR:118 PageImpl dùng pageable gốc (offset long) — giữ đúng count của cũ ở trường hợp tràn
PBR:126-135 Ép kiểu theo vị trí (BigDecimal, String, Number); kiểu cột Hibernate 6 trả về chỉ test bằng mock (K-13)
BQS:62-71 Thứ tự token → comId null → khác công ty → phân trang phải giữ như cũ
new:domain/batch/model/BaseNormalizedName.java:36 F-logic-1

Nguồn của trang này

  • Phân tích luồng migrations/batch/by-id/verify/round-1/flow-analysis.md vòng 1 · sha256 97a44bc664a3

v1 dựng 2026-11-09 14:46 · base commit đích 661efe9b8c