Phân tích luồng sau chuyển — inventory/enable-inventory-tracking — vòng 2
Đường dẫn mới tính từ C:/Projects/warehouse-service-wt/ws1/src/main/java/com/softdreams/easypos/warehouse/; PMS = easyposbackend1:src/main/java/vn/softdreams/easypos/service/impl/ProductManagementServiceImpl.java.
F-01 Kiểm yêu cầu bật theo dõi tồn
- Điểm vào: cũ
easyposbackend1:.../web/rest/ProductManagementResource.java:766-772 → PMS:3279 → mới presentation/web/inventory/InventoryTrackingController.java:45 → application/inventory/InventoryTrackingService.java:69
- Quy tắc: R-01, R-02, R-03, R-04, R-05, R-06, R-11, R-12, R-13
Sơ đồ luồng mới (theo code thực tế)
sequenceDiagram
participant W as InventoryTrackingController
participant S as InventoryTrackingService (@Transactional)
participant U as CurrentUserPort
participant I as InitialStockRelationalRepository
participant P as ProductTrackingRelationalAdapter
W->>W: Guard.validateInput(validator, request) (mọi vi phạm, 400)
W->>S: enableInventoryTracking(command)
S->>U: findCompanyId() — rỗng → 400 TOKEN_EXPIRED; khác comId → 500 COMPANY_NOT_EXISTS_CODE
S->>I: findDefaultWarehouse(comId) (WH1 ORDER BY id, lấy dòng đầu, Optional)
S->>S: validateRequest: tracking≠true → 400 FALSE; count null → 400; price null → 400
S->>P: findUnit(comId, ppuId) — rỗng → 500 PRODUCT_NOT_FOUND; isPrimary null → 500 EXCEPTION_ERROR; false → 500 ENABLE_TRACKING_ERROR
S->>P: findActiveProduct(comId, productId) — rỗng → 500 PRODUCT_NOT_FOUND; tracking null → 500 EXCEPTION_ERROR; true → 400 INVALID
S-->>W: (sang F-02) rồi BaseResponseDTO.ok(null, UPDATE_PRODUCT_SUCCESS)
Đối chiếu từng bước
| # |
Luồng cũ |
Luồng mới |
Kết luận |
| 1 |
ProductManagementResource.java:766-772 validateInput (LF-01) |
InventoryTrackingController.java:46 + EnableInventoryTrackingRequest |
KHỚP (body hỏng: KHÁC có chủ đích D-02) |
| 2 |
PMS:3281 getUserWithAuthorities(comId) |
InventoryTrackingService.java:144-151 |
KHỚP 2 kiểm; các kiểm user khác: KHÁC có chủ đích (D-04) |
| 3 |
PMS:3282 getWarehouseDefault |
InventoryTrackingService.java:71, InitialStockRelationalRepository.java:57 |
KHỚP (đọc trước kiểm R-04) |
| 4 |
PMS:3289-3292 kiểm tracking/count/price |
InventoryTrackingService.java:153-163 |
KHỚP |
| 5 |
PMS:3294-3305 ppu, is_primary |
InventoryTrackingService.java:73-77,166-171 |
KHỚP; NPE → 500 thay 417: KHÁC có chủ đích (D-03) |
| 6 |
PMS:3308-3320 product active, inventory_tracking |
InventoryTrackingService.java:78-82 |
KHỚP về mã/HTTP; câu thông báo PRODUCT_NOT_FOUND: KHÁC (F-logic-2) |
| 7 |
Kiểm gói LF-21 |
không có |
CHỈ CÓ Ở CŨ (D-08) |
| 8 |
PMS:3406 BaseResultDTO SUCCESS |
InventoryTrackingController.java:56 |
KHÁC có chủ đích (D-02), giữ mã UPDATE_PRODUCT_SUCCESS |
Dữ liệu và side effect
| Bảng / topic / cache |
Cũ |
Mới |
Ghi chú |
warehouse |
đọc top(1) WH1 theo com_id |
đọc WH1 theo com_id ORDER BY id, lấy dòng đầu |
KHỚP |
product_product_unit |
đọc (id, com_id) |
đọc (id, com_id) |
KHỚP |
product |
đọc (id, com_id, active) |
đọc (id, com_id, active = 1) |
KHỚP |
| user/company cache |
đọc |
chỉ đọc token |
D-04 |
Transaction, khoá, idempotency, tenant
- Ranh giới transaction: cũ là class
@Transactional (PMS). Mới đặt @Transactional ở InventoryTrackingService.enableInventoryTracking. Ở F-01 không có lệnh ghi.
- Khoá: cả hai đều không khoá. Hai request đồng thời có thể cùng qua bước kiểm "chưa theo dõi" ở cả cũ lẫn mới, không tệ hơn cũ.
- Idempotency: không áp dụng (API đồng bộ).
- Lọc
comId: mọi truy vấn đọc ở F-01 đều có com_id.
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 |
InventoryTrackingService.java:70-72 |
Thứ tự token → kho → kiểm request phải giữ đúng như cũ (đọc kho trước kiểm R-04) |
InventoryTrackingService.java:74,79 |
Câu thông báo PRODUCT_NOT_FOUND có placeholder không được điền (F-logic-2) |
(F-01 chép nguyên từ vòng 1 — không đổi, không có file trong fix.diff.)
F-02 Tính và ghi sản phẩm, tồn, phiếu khởi tạo
- Điểm vào: cũ
PMS:3321-3405 → mới application/inventory/InventoryTrackingService.java:83-97,99-124
- Quy tắc: R-07, R-08, R-09, R-10, R-13
Sơ đồ luồng mới (theo code thực tế)
sequenceDiagram
participant S as InventoryTrackingService (@Transactional)
participant Pl as InventoryTrackingPlan
participant P as ProductTrackingRelationalAdapter (JDBC)
participant R as InitialRsInoutwardService
participant I as InitialStockRelationalRepository (JDBC)
participant K as LegacyInventoryLogKafkaProducer (KafkaTemplate riêng, value StringSerializer)
S->>P: findUnitsOfProduct (ORDER BY is_primary DESC)
S->>Pl: plan(units, count, price) (cờ null / rate 0 → EXCEPTION_ERROR)
S->>P: updateUnits: on_hand/purchase_price đổi → UPDATE toàn cột chuỗi đã làm sạch + audit
S->>P: enableTracking: UPDATE inventory_tracking=1, in_price, inventory_count, audit, cột chuỗi đổi
alt không có WH1
S-->>S: return (commit, 200)
else có WH1
loop mỗi ppu is_primary = true
S->>R: prepare: N2 (>1 → EXCEPTION_ERROR), SequenceService.nextCode(NK), dựng RsInoutward
end
S->>I: findStockRows(comId, WH1, ppuIds) — dòng không lô, trùng ppu giữ dòng cuối
S->>K: send(LegacyInventoryLog) mỗi dòng — JSON thô, key productId, header messageId (ngay, trước commit)
S->>I: updateOnHand(id, onHand, actor) mỗi dòng
S->>R: save(phiếu đầu tiên) → RsInoutwardRepository.saveAll
end
Đối chiếu từng bước
| # |
Luồng cũ |
Luồng mới |
Kết luận |
| 1 |
PMS:3324 getAllByProductIdAndComId |
InventoryTrackingService.java:84, ProductTrackingRelationalAdapter.java:97-99 |
KHỚP |
| 2 |
PMS:3326-3343 tính on_hand/giá |
InventoryTrackingPlan.java:32-51 |
KHỚP (NPE/chia 0 → 500: D-03) |
| 3 |
PMS:3353 saveAll ppu |
ProductTrackingRelationalAdapter.java:102-133 |
KHỚP (D-10, K-02) |
| 4 |
PMS:3354 save product |
ProductTrackingRelationalAdapter.java:136-157 |
KHÁC có chủ đích (D-11) |
| 5 |
PMS:3357-3367,3396-3399 NPE thiếu WH1 bị nuốt |
InventoryTrackingService.java:95-96 |
KHÁC có chủ đích (D-05), cùng kết quả |
| 6 |
PMS:6486-6490 N2 + genCode NK mỗi ppu chính |
InventoryTrackingService.java:101-105, InitialRsInoutwardService.java:71-78 |
KHỚP (D-03, D-09) |
| 7 |
PMS:6553-6608 dựng phiếu + 1 dòng |
InitialRsInoutwardService.java:79-119 |
KHỚP (D-09) |
| 8 |
BaseInventoryServiceImpl.java:639-665 gom dòng không lô, giữ dòng cuối |
InitialStockRelationalRepository.java:65-85 |
KHỚP (D-06) |
| 9 |
BaseInventoryServiceImpl.java:691-698,1021-1038 gửi log trước khi lưu, value StringSerializer |
InventoryTrackingService.java:111-117, LegacyInventoryLogKafkaProducer.java:43-73 |
KHỚP: nội dung (D-07) và serializer (đã sửa F-logic-1 vòng 1) |
| 10 |
PMS:3384-3390 saveAll BaseInventory |
InitialStockRelationalRepository.java:88-95 |
KHỚP |
| 11 |
PMS:3391-3393 lưu phiếu đầu tiên |
InventoryTrackingService.java:121-123 |
KHỚP |
| 12 |
PMS:3401-3405 task log (list rỗng) |
không có |
CHỈ CÓ Ở CŨ, không có side effect |
Dữ liệu và side effect
| Bảng / topic / cache |
Cũ |
Mới |
Ghi chú |
product_product_unit |
UPDATE toàn dòng khi đổi |
như cũ qua JDBC |
KHỚP; WHERE chỉ theo id (F-logic-3 vòng 1) |
product |
UPDATE cột đổi + cột chuỗi bị làm sạch |
như cũ |
D-11 |
inventory |
UPDATE on_hand, updater, update_time |
như cũ |
KHỚP; WHERE chỉ theo id (F-logic-3 vòng 1) |
rs_inoutward, rs_inoutward_detail |
INSERT 1 + 1 |
INSERT qua saveAll |
KHỚP |
| Redis sequence NK |
HINCRBY mỗi ppu chính |
SequenceService.nextCode mỗi ppu chính |
D-09/LF-44 |
Kafka inventory-events |
1 message JSON thô mỗi dòng tồn |
1 message JSON thô mỗi dòng tồn (StringSerializer) |
KHỚP (D-07) |
Transaction, khoá, idempotency, tenant
- Ranh giới transaction: một
@Transactional ở InventoryTrackingService.enableInventoryTracking; mọi lỗi khác thiếu WH1 đều rollback hết (R-09).
KafkaTemplate mới dựng từ copyWithConfigurationOverride của factory chung, nên thuộc tính transactional giữ như factory gốc. Cả cũ lẫn mới đều gửi ngay, không chờ commit (D-07).
- Khoá: không khoá, đặt
on_hand tuyệt đối, như cũ.
- Thời điểm phát event: trước khi đặt tồn, lưu phiếu và commit. Request lỗi sau đó thì message đã gửi vẫn ra ngoài, như cũ.
- Lọc
comId: các truy vấn đọc có com_id; các lệnh UPDATE theo id (F-logic-3 vòng 1).
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 |
infrastructure/messaging/LegacyInventoryLogKafkaProducer.java:35-49 |
Template riêng ghi đè value serializer, không dùng template mặc định (JsonSerializer). Nếu đổi lại inject KafkaTemplate thì payload sẽ bị bọc chuỗi như vòng 1 |
application/inventory/InventoryTrackingService.java:95-96 |
Nhánh D-05: không có WH1 vẫn commit ppu/product và trả 200 |
application/inventory/InventoryTrackingService.java:101-123 |
Sinh mã NK cho mọi ppu chính nhưng chỉ lưu phiếu đầu (D-06); Kafka gửi trước khi ghi tồn |
infrastructure/relational/product/adapter/ProductTrackingRelationalAdapter.java:102-157 |
Mô phỏng dirty check và làm sạch chuỗi (D-10, D-11); UPDATE theo id |
infrastructure/relational/inventory/adapter/InitialStockRelationalRepository.java:65-95 |
Gom dòng không lô "giữ dòng cuối", không ORDER BY; native SQL chỉ test bằng H2 (K-13) |