Phân tích luồng sau chuyển — inventory/listener-cogs-completion — vòng 1
F-01 Nhận message, đọc task log, ghi kết quả, ack/nack
- Điểm vào: cũ
easyposasync:src/main/java/sds/ep/async/queueListener/CogsCompletionListener.java:34,51→ mớisrc/main/java/com/softdreams/easypos/warehouse/presentation/messaging/CogsCompletionConsumer.java:58-59 - Quy tắc: R-01, R-02, R-03, R-04, R-05, R-07, R-09
Sơ đồ luồng mới (theo code thực tế)
sequenceDiagram
participant Q as Rabbit cogs-completion (container starter, MANUAL)
participant C as CogsCompletionConsumer
participant A as CogsCompletionTaskService
participant T as CogsTaskLogMongoStore
participant S as CogsComputationService (@Transactional)
Q->>C: onMessage(raw, taskLogId, channel)
C->>C: sleeper.sleep(2000)
C->>A: handle(taskLogId)
A->>T: find(id) — findOne {_id: ObjectId|String}
alt không có
T-->>A: empty
A-->>C: throw SystemException(TASK_LOG_NOT_FOUND) → ra container, không ack/nack
else status == 1
A-->>C: DONE → basicAck
else
A->>T: parseCompletion(content) (lỗi → Failure(message của cause))
A->>A: from/to/ppuIds null → Failure(null); comId null → bỏ tính
A->>S: calculate(ppuIds, comId, CogsTaskPeriod.toCogsPeriod(from,to)) (xem F-02)
A->>T: markResult(id, 1,"OK" | 2,msg) — updateFirst $set status/update_time/updater=0, $set|$unset error_message
A-->>C: SUCCESS → basicAck | FAILED → basicNack(requeue=false)
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 | @ConditionalOnProperty + @RabbitListener(queues=${spring.rabbitmq.queues.cogs-completion}, containerFactory MANUAL) — L:33-34; RC:56-99 |
CogsCompletionConsumer.java:27-28,58; container mặc định SC:231-248 |
KHÁC có chủ đích (D-04, D-09) |
| 2 | APM name, RetrySynchronizationManager — L:52-57 |
— | CHỈ CÓ Ở CŨ (D-08) |
| 3 | Thread.sleep(2000L) — L:55 |
CogsCompletionConsumer.java:60 (cấu hình, mặc định 2000) |
KHỚP (D-02) |
| 4 | findById(taskLogId) (MongoRepository<…,String>: ObjectId khi hợp lệ), không có → RuntimeException ra ngoài — L:61-65 |
CogsCompletionTaskService.java:40-41; CogsTaskLogMongoStore.java:52-69,100-103 |
KHỚP (D-01); khác loại exception, container xử lý như nhau (cùng không fatal) |
| 5 | status == 1 → ack, return — L:67-74 |
CogsCompletionTaskService.java:42-44; CogsCompletionConsumer.java:64-68 |
KHỚP |
| 6 | ObjectMapper+JavaTimeModule, tắt ADJUST_DATES…, readValue(content, CogsCompletionMessage) — L:110-115 |
CogsTaskLogMongoStore.java:73-79,106-111 (record CogsCompletionTask) |
KHỚP; text lỗi parse: F-logic-2; content "null": F-logic-1 |
| 7 | calculateByPpuIds(...) trong try/catch(Exception) → status 2, e.getMessage() — L:81-89 |
CogsCompletionTaskService.java:55-71,75-77 |
KHÁC có chủ đích (D-06: lấy message của cause); kiểm null trước để giữ error_message null như NPE Java 11 |
| 8 | save(taskLog) (replace, upsert; auditing update_time, updater=0) — L:90-93 |
CogsTaskLogMongoStore.java:82-97 (updateFirst $set/$unset) |
KHÁC có chủ đích (D-07); không upsert: F-logic-3 |
| 9 | BaseRequestContext.clear() — L:94 |
— | CHỈ CÓ Ở CŨ (D-08) |
| 10 | ack khi thành công / basicNack(tag,false,false) khi lỗi, catch (Exception) chỉ log — L:96-104 |
CogsCompletionConsumer.java:62-71 (catch IOException | ShutdownSignalException) |
KHỚP (exception runtime khác lúc ack/nack sẽ ra container, nhưng document đã ghi rồi nên kết quả như nhau) |
| 11 | auditor Mongo không token → creator/updater = 0 cho task log COGS_UPDATE_ASYNC — easyposconfig:…/BaseSpringSecurityAuditorAware.java:42 |
CogsTaskLogMongoRabbitAdapter.java:131-133 |
KHỚP (R-09; sửa adapter của cogs-calculate, đã ghi auto-changes.md) |
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ú |
|---|---|---|---|
Rabbit cogs-completion |
ack (xong/thành công), nack không requeue (lỗi), treo khi lỗi đọc/ghi Mongo hoặc thiếu document | như cũ | D-01, D-03 |
Mongo task_log_reference (đọc) |
findById, chỉ theo _id |
findOne {_id}, chỉ theo _id |
không lọc type/com_id, như cũ (R-03) |
Mongo task_log_reference (ghi) |
save replace + upsert: status, error_message (null thì mất field), update_time, updater=0 |
updateFirst: $set status, update_time, updater=0, $set/$unset error_message |
D-07; không upsert: F-logic-3 |
Mongo task_log_reference (COGS_UPDATE_ASYNC, do F-02 tạo) |
creator/updater = 0 |
creator/updater = 0 |
R-09 |
Transaction, khoá, idempotency, tenant
- Ranh giới transaction: cũ chỉ
calculateByPpuIds(CH:60) → mới chỉCogsComputationService.calculate(@Transactional, gọi qua bean khác nên proxy có hiệu lực). Đọc/ghi Mongo và ack nằm ngoài transaction ở cả hai bên. - Khoá / cập nhật nguyên tử: cả hai không có khoá. Mới dùng
$setnên không ghi đè các field khác. - Idempotency: chỉ dựa vào
status == 1như cũ (D-05, lệch ADR-0301 có chủ đích). Giao trùng đồng thời hoặc crash trướcmarkResultthì tính lại, cũ cũng vậy. - Lọc
comId: tìm task log không lọccom_id(như cũ).comIddùng để tính lấy từ content (CogsCompletionTaskService.java:62-65).
Lệch so với luồng mới đã duyệt (spec mục 6)
Không có. Code đúng như sơ đồ mục 6; có thêm parseCompletion trên port.
Dev cần chú ý khi review
| Vị trí | Vì sao |
|---|---|
CogsCompletionTaskService.java:58-66 |
Thứ tự kiểm dữ liệu hỏng (ngày → ppuIds → comId) thay cho NPE của code cũ. task == null chưa được xử lý (F-logic-1). |
CogsCompletionTaskService.java:69-77 |
Chỉ bắt RuntimeException. error_message lấy message của cause (D-06), nên text lỗi khác cũ. |
CogsTaskLogMongoStore.java:62-68 |
status không phải số thì thành null và vẫn được xử lý. |
CogsTaskLogMongoStore.java:82-97 |
Ghi $set/$unset, không upsert (D-07, F-logic-3). |
CogsCompletionConsumer.java:59-71 |
Thiếu document hoặc lỗi Mongo thì exception ra container, message không ack (D-01). Phụ thuộc container MANUAL của starter (D-09), phải bật app.rabbitmq.activate. |
CogsTaskLogMongoRabbitAdapter.java:131-133 |
Đổi hành vi của unit cogs-calculate: không có user thì creator/updater = 0. |
F-02 Tính giá vốn từ nội dung task log
- Điểm vào: cũ
easyposasync:…/CogsCompletionListener.java:117→easyposbase:…/services/impl/BaseCogsHistoryServiceImpl.java:61→ mớiCogsCompletionTaskService.java:63-65→CogsComputationService.java:65-66 - Quy tắc: R-06, R-08
Sơ đồ luồng mới (theo code thực tế)
sequenceDiagram
participant A as CogsCompletionTaskService
participant P as CogsTaskPeriod
participant S as CogsComputationService (@Transactional)
participant R as CogsRelationalRepository
participant TL as CogsTaskLogMongoRabbitAdapter
A->>P: toCogsPeriod(from, to) — norm/quý theo zone message, from/to đổi sang giờ VN
A->>S: calculate(ppuIds, comId, period)
S->>R: findChildren, theo lô 100: findConvertRates, findDetailRows, findBillRows
S->>TL: publishUpdate (requireValid comId → insert Mongo → send Rabbit) mỗi chunk 1000
S->>TL: publishUpdate ward deltas
S->>R: findWarehouseIds, findProductUnitIds, saveHistories (cogs_history)
Đố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 | normalizedDate(from/to) theo zone của giá trị, normalizedQuarter — CH:62-65; easyposmodel:…/BaseCommon.java:2612-2615,2123-2130 |
CogsTaskPeriod.java:27-37,41-45 |
KHỚP |
| 2 | cogs_history.from/to_date = Timestamp.from(instant) (giờ JVM) — base:…/BaseCogsHistoryRepositoryCustomImpl.java:51-52; CH:662-665 |
CogsTaskPeriod.java:31-32 + CogsRelationalRepository.java:262-265 (giờ VN) |
KHÁC có chủ đích (D-09 của cogs-calculate) |
| 3 | ngày null → NPE ở normalizedDate; ppuIds null → NPE ở new HashSet<>(null) (CH:62,78) |
CogsCompletionTaskService.java:58-61 → Failure(null) |
KHỚP (cùng status 2, không có error_message) |
| 4 | comId null → mọi truy vấn com_id = null không ra dòng → không ghi, thành công (CH:70,88-100,110) |
CogsCompletionTaskService.java:62-66 bỏ qua calculate → SUCCESS |
KHỚP (riêng comId null + ppuIds rỗng: UNVERIFIED theo R-06) |
| 5 | Gộp ppu con, lô 100, gửi task log, ghi cogs_history — CH:70-124 |
CogsComputationService.java:65-91 (code của cogs-calculate, không đổi) |
KHỚP (đã review ở unit cogs-calculate) |
| 6 | Lỗi → rollback cogs_history; task log đã gửi vẫn còn — CH:60 |
@Transactional CogsComputationService.java:65; Mongo/Rabbit ngoài JDBC transaction |
KHỚP (R-08, D-05 của cogs-calculate) |
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ú |
|---|---|---|---|
product_product_unit, rs_inoutward*, bill*, inventory |
đọc theo com_id |
đọc theo com_id |
như cogs-calculate |
config, company |
đọc mỗi lần gửi task log (getConfigSendQueue) |
SendQueueConfigRelationalAdapter.requireValid mỗi lần publish |
cogs-calculate D-10 (bỏ Redis) |
cogs_history |
INSERT ppu cha, trong transaction | INSERT ppu cha, trong transaction | from/to theo giờ VN (D-09 cogs-calculate) |
Mongo task_log_reference + Rabbit cogs-update-async |
insert + send trong lúc tính, không rollback | như cũ | creator/updater = 0 (R-09) |
Transaction, khoá, idempotency, tenant
- Ranh giới transaction: cũ
@Transactional calculateByPpuIds→ mới@Transactional CogsComputationService.calculate, gọi từ bean khác. - Khoá / cập nhật nguyên tử: không khoá. Cập nhật phiếu/hoá đơn nằm ở consumer
COGS_UPDATE_ASYNC, ngoài unit này. - Idempotency, thời điểm phát event: task log được gửi trước khi commit, như cũ. Tính lại (giao lại message) thì gửi lại task log và INSERT thêm
cogs_history, như cũ (D-05). - Lọc
comId: mọi truy vấn và lệnh ghi dùngcomIdlấy từ content.
Lệch so với luồng mới đã duyệt (spec mục 6)
Không có.
Dev cần chú ý khi review
| Vị trí | Vì sao |
|---|---|
CogsTaskPeriod.java:28-36 |
Ngày chuẩn hoá theo zone của message, nhưng from/to đổi sang giờ VN. Phải khớp với CogsRelationalRepository.java:262-263 (ghi toLocalDateTime). |
CogsComputationService.java:65-91 |
Transaction bao cả bước gửi Mongo/Rabbit (không rollback được). Lỗi ở đây thành status 2 qua messageOf. |