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

batch · batch/update · api

POST/api/client/page/batches/update

Vòng
2
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/update — vòng 2

logic-reviewer viết dựa trên code đã implement (không phải thiết kế trong spec), đối chiếu với code cũ. Dev đọc file này cùng changes.diff: mỗi luồng cho biết code mới đi qua những đâu, khác code cũ ở bước nào, và chỗ nào cần đọc kỹ. Chế độ thu hẹp: F-01 có BatchWriter.java (nằm trong fix.diff) nên viết lại. Các bước không có trong fix.diff lấy kết luận của vòng 1.

F-01 Sửa lô (không id thì tạo)

  • Điểm vào: cũ backend:web/rest/BatchesManagementResource.java:47-52 → mới src/main/java/com/softdreams/easypos/warehouse/presentation/web/batch/BatchController.java:67-71 (dùng chung save private ở dòng 74-86 với /create)
  • Quy tắc: R-01 … R-09

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

sequenceDiagram
    participant C as Client
    participant BC as BatchController.update/save
    participant MS as BatchManagementService.save
    participant RR as BatchRelationalRepository (translate)
    participant W as BatchWriter
    C->>BC: POST /update BatchSaveRequest
    BC->>BC: Guard.validateInput (400 mọi vi phạm)
    BC->>MS: mapper.toSaveCommand
    MS->>MS: requireCompany (TOKEN_EXPIRED / COMPANY_NOT_EXISTS_CODE)
    MS->>MS: BatchExpiryWarning.requireWithinMax
    MS->>MS: BatchDateParser.parse(mfg, exp); exp < mfg → MFG_DATE_EXCEED_EXP_DATE
    alt id != null
        MS->>RR: findByIdAndComId(id, comId) → rỗng: LOT_NO_NOT_FOUND
    end
    MS->>RR: findDuplicate(comId, lotNo thô, mfg, exp) (nhiều dòng → EXCEPTION_ERROR)
    alt trùng lô khác / id null
        MS-->>BC: duplicated()
    else id null
        MS->>W: lockCompanyBatchCode (sp_getapplock) → countByComId → persist (INSERT ngay)
        MS-->>BC: saved(item đã làm sạch, created=true)
    else có id
        MS->>MS: code null → SystemException(EXCEPTION_ERROR)
        MS->>MS: gán thô: status (BatchStatusPolicy), normalizedName(code, lotNo thô)
        opt BatchDirtyCheck.isDirty
            MS->>RR: save(cleanedForUpdate: LegacyCleanText + updater/update_time)
            RR->>W: em.find (entity managed) → setter → em.flush() (UPDATE @DynamicUpdate, lỗi DB → EXCEPTION_ERROR)
        end
        MS-->>BC: saved(item, lotNo thô, created=false)
    end
    BC-->>C: 200 SUCCESS_UPDATE | SUCCESS_CREATE | status=false LOT_NO_DUPLICATE

Đố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 POST /client/page/batches/update — backend:web/rest/BatchesManagementResource.java:47-48 BatchController.java:67-69 KHỚP (vòng 1)
2 validateInput trả mọi vi phạm; body hỏng 400 rỗng — BatchesManagementResource.java:49, LF-01, LF-03 BatchController.java:75 KHÁC có chủ đích (D-02) (vòng 1)
3 getUserWithAuthorities(comId) — backend:service/impl/BatchesManagementServiceImpl.java:165 BatchManagementService.java:58,80-87 KHÁC có chủ đích (D-01) (vòng 1)
4 Giới hạn cảnh báo — BatchesManagementServiceImpl.java:168-185 BatchManagementService.java:59-60 KHỚP (vòng 1)
5 Parse ngày, rỗng → null — BatchesManagementServiceImpl.java:187-194 BatchManagementService.java:61-62; BatchDateParser.java:29-38 KHỚP
6 expDate < mfgDate → MFG_DATE_EXCEED_EXP_DATE — BatchesManagementServiceImpl.java:196-198 BatchManagementService.java:63-65 KHỚP (vòng 1)
7 Có id: findByIdAndComId rỗng → LOT_NO_NOT_FOUND — BatchesManagementServiceImpl.java:200-204 BatchManagementService.java:66,89-92 KHỚP (vòng 1)
8 Kiểm trùng số lô thô — BatchesManagementServiceImpl.java:206-217 BatchManagementService.java:67-72 KHỚP (D-04; nhiều dòng 500 theo D-05) (vòng 1)
9 Nạp lại lô findByIdAndComId(...).get() — BatchesManagementServiceImpl.java:222 không có; dùng lại kết quả bước 7 CHỈ CÓ Ở CŨ (F-logic-1)
10 Không id: new Batches, genCode — BatchesManagementServiceImpl.java:223-227,248 BatchManagementService.java:94-118; BatchWriter.java:44-60,85-86 KHÁC có chủ đích (D-03 giữ tạo; D-11 của batch/create: khoá sp_getapplock)
11 copyProperties (bỏ id,comId,code), ngày, status, normalizedName([code, lotNo thô]); code null → NPE — BatchesManagementServiceImpl.java:229-247 BatchManagementService.java:120-144; BatchWriter.java:69-84 KHỚP (D-06; NPE → 500 EXCEPTION_ERROR theo D-05)
12 save (merge entity managed), UPDATE lúc flush khi commit: dirty theo giá trị thô → CleanTextListener + @LastModified*; lỗi DB → 417 (LF-13, LF-30, LF-31, K-02) — BatchesManagementServiceImpl.java:248 BatchManagementService.java:145-147,152-169; BatchDirtyCheck.java:24-43; BatchWriter.java:69-90 (gán lên entity managed, flush ngay trong translate) KHỚP về giá trị ghi (D-07, UNVERIFIED). UPDATE chạy sớm hơn, trước commit, nhưng sau nó không còn lệnh nào nên kết quả không đổi. Lỗi DB ra 500 EXCEPTION_ERROR, KHÁC có chủ đích (D-05). F-logic-1 của vòng 1 đã xử lý.
13 Response BatchItem copy từ entity trước flush (lotNo thô), reason theo id — BatchesManagementServiceImpl.java:250-257 BatchManagementService.java:149,171-182; BatchController.java:77-85 KHỚP (D-08; trùng không có key data theo D-02) (vòng 1)
14 Kiểm gói dịch vụ (interceptor) — LF-21 không có CHỈ CÓ Ở CŨ (D-09)

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ú
batches Đọc id+com_id 2 lần; đọc kiểm trùng; UPDATE cột đổi khi dirty (lúc commit); không id → INSERT Đọc id+com_id 1 lần; đọc kiểm trùng; UPDATE cột đổi khi BatchDirtyCheck đúng (flush ngay trong save); không id → khoá + đếm + INSERT ngay creator/create_time updatable=false; @DynamicUpdate
User/JWT getUserWithAuthorities CurrentUserPort.findCompanyId/findUserId D-01
Event/cache không không —

Transaction, khoá, idempotency, tenant

  • Ranh giới transaction: cũ @Transactional cấp class (BatchesManagementServiceImpl.java:55-57) → mới @Transactional trên BatchManagementService.save (BatchManagementService.java:56). Lệnh UPDATE của nhánh sửa nay chạy trong translate (BatchRelationalRepository.java:149-160). Lỗi DB ném ra SystemException, transaction rollback, trả 500 EXCEPTION_ERROR.
  • Khoá / cập nhật nguyên tử: nhánh sửa ở cả cũ và mới đều không khoá, không version, ai ghi sau thì thắng. flush sớm giữ khoá ghi dòng tới commit như cũ, vì sau save không còn bước nào. Lô bị xoá đồng thời + dirty → UPDATE 0 dòng → OptimisticLockException → 500 EXCEPTION_ERROR (cũ 417). Nhánh không id có sp_getapplock theo công ty.
  • Idempotency, thời điểm phát event: không có event. Gửi lại cùng request sửa → lần 2 không dirty → không gọi save, không flush, không ghi (như cũ).
  • Lọc comId: findByIdAndComId(id, comId token) và findDuplicate đều lọc comId. em.find theo id chỉ chạy sau khi đã xác nhận lô thuộc công ty.

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
BatchWriter.java:85-90 flush mới thêm ở nhánh sửa: UPDATE chạy ngay trong translate. flush đẩy mọi entity đang managed; hiện chỉ có lô đang sửa, nhưng nếu sau này gọi save khi đã sửa entity khác trong cùng transaction thì các entity đó cũng bị ghi sớm.
BatchManagementService.java:120-150 Nhánh sửa: NPE khi code null; mô phỏng dirty bằng giá trị thô; response trả lotNo thô (D-06, D-07, D-08).
BatchDirtyCheck.java:24-43 Thay cho dirty-check của Hibernate 5.6. Chưa có golden master (UNVERIFIED).
BatchManagementService.java:66,77 Chỉ đọc lô một lần (F-logic-1).

Nguồn của trang này

  • Phân tích luồng migrations/batch/update/verify/round-2/flow-analysis.md vòng 2 · sha256 f760adcdef15

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