Phân tích luồng sau chuyển — batch/update — vòng 2
logic-reviewerviế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ùngchanges.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 trongfix.diff) nên viết lại. Các bước không có trongfix.difflấ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ớisrc/main/java/com/softdreams/easypos/warehouse/presentation/web/batch/BatchController.java:67-71(dùng chungsaveprivate ở 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ũ
@Transactionalcấp class (BatchesManagementServiceImpl.java:55-57) → mới@TransactionaltrênBatchManagementService.save(BatchManagementService.java:56). Lệnh UPDATE của nhánh sửa nay chạy trongtranslate(BatchRelationalRepository.java:149-160). Lỗi DB ném raSystemException, transaction rollback, trả 500EXCEPTION_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.
flushsớm giữ khoá ghi dòng tới commit như cũ, vì sausavekhông còn bước nào. Lô bị xoá đồng thời + dirty → UPDATE 0 dòng →OptimisticLockException→ 500EXCEPTION_ERROR(cũ 417). Nhánh khôngidcósp_getapplocktheo 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ôngflush, không ghi (như cũ). - Lọc
comId:findByIdAndComId(id, comId token)vàfindDuplicateđều lọccomId.em.findtheoidchỉ 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). |