Phân tích luồng sau chuyển — batch/create — vòng 1
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ỹ.
Viết tắt đường dẫn mới: ctl/ = presentation/web/batch/, app/ = application/batch/, dom/ = domain/batch/,
inf/ = infrastructure/relational/batch/ (gốc src/main/java/com/softdreams/easypos/warehouse/).
Cũ: svc = easyposbackend1:src/main/java/vn/softdreams/easypos/service/impl/BatchesManagementServiceImpl.java.
F-01 Lưu lô (tạo; /create kèm id thì sửa)
- Điểm vào: cũ
easyposbackend1:src/main/java/vn/softdreams/easypos/web/rest/BatchesManagementResource.java:40-45→ mớictl/BatchController.java:54-59 - Quy tắc: R-01 … R-14
Sơ đồ luồng mới (theo code thực tế)
sequenceDiagram
participant C as Client
participant BC as BatchController.create
participant MS as BatchManagementService.save (@Transactional)
participant BR as BatchRelationalRepository (translate)
participant BW as BatchWriter
participant DB as batches
C->>BC: POST BatchSaveRequest
BC->>BC: Guard.validateInput (mọi vi phạm, 400)
BC->>MS: save(BatchWebMapper.toSaveCommand)
MS->>MS: requireCompany: CurrentUserPort.findCompanyId → TOKEN_EXPIRED 400 / COMPANY_NOT_EXISTS_CODE 500
MS->>MS: BatchExpiryWarning.requireWithinMax → BatchDateParser.parse(mfg), parse(exp) → MFG_DATE_EXCEED_EXP_DATE
opt id != null
MS->>BR: findByIdAndComId(id, comId) → rỗng: LOT_NO_NOT_FOUND 400
end
MS->>BR: findDuplicate(comId, lotNo thô, mfg, exp) (JPQL, null → IS NULL)
alt trùng và (id null hoặc id khác)
MS-->>BC: BatchSaveResult.duplicated()
BC-->>C: 200 status=false LOT_NO_DUPLICATE
else tạo (id null)
MS->>BW: lockCompanyBatchCode (SELECT TOP 1 batches + sp_getapplock 10s)
MS->>BW: countByComId → code = "LH"+(n+1)
MS->>MS: status (BatchStatusPolicy), normalizedName thô → LegacyCleanText.clean(code, lotNo, normalizedName), audit = user/now
MS->>BW: save → persist (INSERT ngay)
BC-->>C: 200 SUCCESS_CREATE, data = lô đã làm sạch
else sửa (id có)
MS->>MS: code NULL → SystemException EXCEPTION_ERROR
MS->>MS: gán giá trị thô (lotNo, ngày, status, normalizedName, expiryWarning*) → BatchDirtyCheck.isDirty
opt dirty
MS->>BW: save(đã làm sạch, updater/updateTime) → gán entity managed (UPDATE lúc commit)
end
BC-->>C: 200 SUCCESS_UPDATE, data.lotNo thô
end
Đố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 | Validate SaveBatchRequest, trả mọi vi phạm — BatchesManagementResource.java:42, SaveBatchRequest.java:11-28, LF-01 |
ctl/BatchController.java:57, ctl/dto/request/BatchSaveRequest.java:26-41 |
KHỚP (body hỏng: D-02) |
| 2 | getUserWithAuthorities(comId) — svc:165, UserService.java:1838-1851 |
app/BatchManagementService.java:58,80-87 |
KHÁC có chủ đích (D-01) |
| 3 | Giới hạn cảnh báo theo đơn vị — svc:167-185 |
dom/model/BatchExpiryWarning.java:30-47; app/BatchManagementService.java:59-60 |
KHỚP |
| 4 | Parse NSX rồi HSD, lỗi → DATE_TIME_INVALID — svc:187-194, model:util/BaseCommon.java:1128-1142 |
dom/model/BatchDateParser.java:29-38; app/BatchManagementService.java:61-62 |
KHỚP |
| 5 | HSD trước NSX → MFG_DATE_EXCEED_EXP_DATE — svc:196-198 |
app/BatchManagementService.java:63-65 |
KHỚP |
| 6 | id có → findByIdAndComId rỗng → LOT_NO_NOT_FOUND — svc:200-204 |
app/BatchManagementService.java:66,89-92; inf/adapter/BatchRelationalRepository.java:125-128 |
KHỚP |
| 7 | Kiểm trùng số lô thô + ngày, trả 200 status=false — svc:206-217 |
app/BatchManagementService.java:67-72; inf/jpa/BatchJpaRepository.java:27-34; ctl/BatchController.java:66-70 |
KHỚP (định dạng D-02; nhiều dòng → 500 D-05) |
| 8 | Nhánh sửa nạp lại lô (SELECT lần 2) — svc:221-222 |
dùng lại existing (bước 6); inf/adapter/BatchWriter.java:69-71 find lấy entity managed |
KHỚP (bớt một SELECT, không quan sát được) |
| 9 | Sinh mã LH + count + 1, không khoá — svc:224-226, base:services/BaseSequenceService.java:51-72,250-252,296-374, base:repositories/impl/BaseUserRepositoryImpl.java:35-51 |
app/BatchManagementService.java:95-96; inf/adapter/BatchWriter.java:18-60 |
KHÁC có chủ đích (D-11); nhánh chờ khoá quá 10s → KHÁC (F-logic-2) |
| 10 | Chép lotNo, expiryWarning*, ngày, tính status — svc:229-245,551-568 |
app/BatchManagementService.java:73-76,107,136; dom/model/BatchStatusPolicy.java:25-33 |
KHỚP |
| 11 | normalizedName(List.of(code, lotNo thô)); code NULL → NPE — svc:247, util/Common.java:1131-1139,1315-1329 |
dom/model/BatchNormalizedName.java:30-53; app/BatchManagementService.java:108,121-127,137 |
KHỚP (NPE → 500 EXCEPTION_ERROR, D-05) |
| 12 | Tạo: save → persist, CleanTextListener + auditing — svc:248, domain/Batches.java:14-16, LF-30, LF-31 |
app/BatchManagementService.java:97-116; dom/model/LegacyCleanText.java:24-30; inf/adapter/BatchWriter.java:68-89 |
KHỚP |
| 13 | Sửa: entity managed, UPDATE lúc commit chỉ khi dirty trên giá trị thô, listener làm sạch, @LastModified* — svc:248, K-02 |
app/BatchManagementService.java:120-169; dom/model/BatchDirtyCheck.java:24-43; inf/adapter/BatchWriter.java:68-89 |
KHỚP về dữ liệu ghi (D-08); lỗi DB lúc commit ra ngoài translate → KHÁC (F-logic-1) |
| 14 | Response BatchItem từ entity; tạo: đã làm sạch, sửa: lotNo thô — svc:250-257 |
app/BatchManagementService.java:117,149,171-182; ctl/BatchController.java:61-65; ctl/BatchWebMapper.java:49-65; ctl/dto/response/BatchItemResponse.java |
KHỚP (D-09; data khi trùng D-02) |
| 15 | Interceptor kiểm gói dịch vụ — LF-21 | (không có) | CHỈ CÓ Ở CŨ (D-10) |
| 16 | Lỗi không lường trước → 417 EXCEPTION_ERROR — LF-13 |
inf/adapter/BatchRelationalRepository.java:153-160 → SystemException 500 |
KHÁC có chủ đích (D-05) |
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 (nhánh sửa), com_id+lot_no+mfg_date+exp_date (derived), count(*) where com_id (tạo) |
id+com_id ×1, JPQL trùng (cùng điều kiện, null → IS NULL), SELECT TOP 1 … where com_id trong câu khoá + count(*) where com_id (tạo) |
thêm một đọc trong câu khoá (K-12) |
batches (ghi) |
INSERT mọi cột (tạo); UPDATE cột đổi + updater, update_time khi dirty (sửa) |
như cũ: persist (tạo); gán entity @DynamicUpdate chỉ khi BatchDirtyCheck true (sửa) |
creator, create_time updatable=false |
Khoá ứng dụng MSSQL batch-code-<comId> |
— | sp_getapplock Exclusive, owner Transaction, timeout 10s (chỉ nhánh tạo) |
D-11 |
| Redis, event, cache | không | không | — |
Transaction, khoá, idempotency, tenant
- Ranh giới transaction: cũ
@Transactionalcấp class trênBatchesManagementServiceImpl(svc:55-57) → mới@TransactionaltrênBatchManagementService.save(app/BatchManagementService.java:56), gọi từ controller (không self-invocation); kiểm, đếm, ghi trong một transaction; trùng trả bình thường, không ghi. - Khoá / cập nhật nguyên tử: cũ không khoá → mới khoá theo công ty ngay trước câu đếm, giữ tới commit (D-11). Kiểm trùng vẫn chạy trước khoá như cũ (hai request cùng số lô/ngày đồng thời vẫn lọt trùng — giống cũ). Nhánh sửa không khoá, không version — giống cũ.
- Idempotency, thời điểm phát event: không có event/consumer.
- Lọc
comId: mọi truy vấn cócomId(=companyIdtoken);BatchWriter.savenhánh sửafindtheoidnhưngidđã được xác nhận thuộccomIdở bước 6 cùng transaction.
Lệch so với luồng mới đã duyệt (spec mục 6)
Không lệch thứ tự bước. Chi tiết thêm so với sơ đồ: nhánh sửa không gọi save khi không dirty; khoá nằm trong BatchWriter (thêm lớp) thay vì trực tiếp ở BatchRelationalRepository.
Dev cần chú ý khi review
| Vị trí | Vì sao |
|---|---|
inf/adapter/BatchWriter.java:18-53 |
Câu khoá nhiều lệnh (SET NOCOUNT ON + đọc bảng + sp_getapplock + SELECT @result) đọc bằng getSingleResult; chưa chạy trên MSSQL thật (K-12, K-13); timeout 10s → 500 (F-logic-2) |
inf/adapter/BatchWriter.java:68-89 + BatchRelationalRepository.java:148-160 |
Nhánh sửa ghi lúc commit, ngoài translate → mã lỗi khi UPDATE thất bại (F-logic-1) |
app/BatchManagementService.java:120-150 |
Mô phỏng dirty-check Hibernate trên giá trị thô (K-02, D-08) và response trả lotNo thô (D-09) — khác dữ liệu lưu |
inf/jpa/BatchJpaRepository.java:27-34, inf/jpa/BatchDuplicateParams.java:24-33 |
Kiểm trùng viết tay thay derived query: null → IS NULL, ngày bind Timestamp theo jdbc.time_zone; nhiều dòng → IncorrectResultSizeDataAccessException → 500 |
app/BatchManagementService.java:121-127 |
Mô phỏng NPE của List.of(code, …) khi code NULL — chỉ ở nhánh sửa, sau kiểm trùng |