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

batch · batch/job-daily-update-status · job

SCHEDULER@Scheduled(cron = "0 25 0/4 * * ?") dailyUpdateStatus

Vòng
1
Cổng G1–G6
PENDING_SONAR
Bước
sonar-check
Nhánh đích
feat/thongpd/migrate-batch2

Báo cáo sai lệch — batch/job-daily-update-status — vòng 1

0. Kết luận nhanh

  • Điều kiện đạt: G1 ✅ G2 ✅ G3 ✅ G4 — G5 ✅ G6 ✅ → CHỜ KIỂM SONAR
  • Trạng thái: SẴN SÀNG CHO NGƯỜI XEM
  • Tầng rủi ro: A → người cần đọc: spec + toàn bộ diff + chạy GM-01
  • Số quy tắc: 6 · PASS 3 (R-03, R-04, R-05) · DEVIATION có chủ đích 3 (R-01 D-01, R-02 D-02/D-03, R-06 D-09/D-10) · DEVIATION KHÔNG chủ đích 0 · UNVERIFIED 1 (R-05 cột UPDATE thực tế, K-13)
  • Coverage thấp nhất: F-01 92.5% dòng / 100% nhánh
  • Finding logic: CRITICAL 0 · MAJOR 0 · MINOR 2 (1 nghi-bug-cu) — không cần xác minh
  • Nghi vấn bug code cũ (mục 3c): 4 (CAO 1) · Quyết định tự chốt cần xác nhận (mục 3d): 9
  • Chạy bằng: /mig-auto · Số vòng: 1 / lần sửa 0/3

1. Điều kiện đạt

Mã Mục tiêu Kết quả Bằng chứng
G1 mvnw verify ✅ verify/round-1/build.md (729 test)
G2 Test khớp test-plan ✅ verify/round-1/tc-map.md (13/13), oracle.md
G3 Coverage ✅ verify/round-1/coverage.md
G4 Sonar CHỜ NGƯỜI sonar-ide.md
G5 Finding CRITICAL/MAJOR ✅ verify/round-1/review-logic.md (2 MINOR)
G6 Lách cổng ✅ verify/round-1/gates.md

Không chặn nhưng cần người: Integration test CHƯA CHẠY · Golden master CHƯA CHẠY (GM-01 — câu UPDATE @DynamicUpdate, giờ ghi, khoá Redis chỉ khoá bằng mock, K-13)

2. Ma trận truy vết

Quy tắc Tầng Code mới Test Golden Kết luận
R-01 A BatchStatusUpdateScheduler, application.yml TC-01 — DEVIATION có chủ đích (D-01)
R-02 A BatchStatusUpdateScheduler TC-02, 03 — DEVIATION có chủ đích (D-02, D-03); log khác (F-logic-1)
R-03 A BatchStatusWriter.findWithExpiryDate TC-11 GM-01 PASS (giữ D-05)
R-04 A BatchStatusUpdateService, BatchStatusPolicy, BatchExpiryWarning TC-05, 06, 07 GM-01 PASS (giữ D-04, D-08)
R-05 A BatchStatusUpdateService, BatchStatusWriter.updateStatuses TC-05, 08, 09, 12, 13 GM-01 PASS (giữ D-07; D-06); UNVERIFIED cột UPDATE
R-06 A service @Transactional, try/catch ở scheduler TC-04, 06, 10 — DEVIATION có chủ đích (D-09, D-10)

3. Người cần xem

# Vị trí Vì sao cần người xem
1 Toàn bộ diff (11 file, verify/round-1/changed-files.md) Tầng A: job ghi batches của mọi công ty mỗi 4 giờ
2 B-01 (CAO) — lô nhập kho thiếu cấu hình cảnh báo Cũ và mới đều NPE, không lô nào được cập nhật; cần kiểm DB prod trước khi bật job
3 BatchStatusWriter:46-52 Tự làm audit (updater 0, update_time) và làm sạch chuỗi thay listener JPA của monolith; cột UPDATE thật chưa chạy MSSQL (GM-01)
4 jdbc.time_zone Chỉ khai ở profile dev/uat/prod của repo đích — profile khác đọc/ghi giờ khác (ghi chú reviewer)
5 F-logic-1 (MINOR) Log "Another node ( {} )…" in tên khoá thay vì clientId node giữ khoá — DistributedLockManager.acquireLock của starter chỉ trả boolean
6 Job BatchExpiryWarningScheduler (unit batch/job-daily-warning-out-date) Chưa bọc try/catch như rule docs/rules/performance.md:109-111 mà unit này áp dụng (D-10) — người quyết có thống nhất không
7 G4 Chưa kiểm Sonar

3b. Phân tích luồng sau chuyển

Chi tiết: verify/round-1/flow-analysis.md

Luồng Tên Số bước KHỚP / KHÁC / CHỈ CÓ Ở CŨ / CHỈ CÓ Ở MỚI Lệch luồng đã duyệt (spec mục 6) Điểm dev cần chú ý
F-01 Cập nhật trạng thái hạn dùng của lô 15 bước: KHỚP 8 / KHÁC 7 (6 có chủ đích D-01, D-02, D-03, D-06, D-09, D-10; 1 F-logic-1 — bước 4 vừa theo D-03, vừa có dòng log lệch) / CHỈ CÓ Ở CŨ 0 / CHỈ CÓ Ở MỚI 0 không BatchStatusWriter:46-52 tự làm audit và làm sạch chuỗi (cột UPDATE chưa xác minh trên MSSQL); jdbc.time_zone chỉ khai ở profile dev/uat/prod; NPE có chủ đích theo D-04 ở BatchExpiryWarning; khoá nằm ngoài transaction và try/catch nằm ngoài proxy ở BatchStatusUpdateScheduler:47-55

3c. Nghi vấn bug / sai ở code cũ (đã GIỮ nguyên khi chuyển)

B-01 Lô nhập kho thiếu cấu hình cảnh báo làm job không bao giờ cập nhật được

  • Mức nghi ngờ: CAO
  • Ở đâu: backend:service/impl/BatchesManagementServiceImpl.java:415-429,551-568; nguồn dữ liệu backend:service/impl/RsInoutWardManagementServiceImpl.java:8849-8869,9486-9503 → BatchStatusUpdateService, BatchExpiryWarning
  • Code cũ đang làm gì: tính mốc cảnh báo bằng switch trên đơn vị cảnh báo kiểu Integer; đơn vị NULL → NullPointerException, cả job rollback. Nhập kho Excel/XML tạo lô có HSD nhưng không đặt đơn vị/giá trị cảnh báo.
  • Ví dụ cụ thể: chỉ cần 1 lô nhập từ Excel (id 77, HSD 01/12/2026, đơn vị NULL) ở bất kỳ công ty nào → mỗi 4 giờ job lỗi, không lô nào của mọi công ty đổi trạng thái; lô hết hạn hôm qua vẫn hiện "còn hạn".
  • Vì sao nghi là sai: một dòng dữ liệu hợp lệ theo đường nhập kho chặn toàn hệ thống; prod nhiều khả năng đang hỏng mọi lượt.
  • Ảnh hưởng nếu đúng là bug: trạng thái lô (lọc "đã hết hạn"/"sắp hết hạn" ở các màn) sai cho mọi khách hàng.
  • Đã xử lý khi chuyển: GIỮ. Test khoá: TC-06 (BatchStatusUpdateServiceTest#importedBatchWithoutWarning), TC-07.
  • Nếu muốn sửa: coi đơn vị/giá trị NULL như Period.ZERO trong BatchExpiryWarning.period() (hoặc bỏ qua dòng lỗi); đổi TC-06, TC-07. Kiểm trước: SELECT COUNT(*) FROM batches WHERE exp_date IS NOT NULL AND expiry_warning_unit IS NULL.
  • Liên quan: D-04, R-04, R-06

B-02 Nạp mọi lô của mọi công ty vào bộ nhớ mỗi 4 giờ

  • Mức nghi ngờ: THẤP
  • Ở đâu: backend:repository/BatchesRepository.java:124, BMS:414 → BatchStatusWriter.findWithExpiryDate
  • Code cũ đang làm gì: nạp đủ entity mọi lô có HSD, không phân trang, không lọc công ty/gói.
  • Ví dụ cụ thể: 2 triệu lô → 2 triệu entity trong một transaction; có thể vượt 10 phút khoá.
  • Vì sao nghi là sai: hiệu năng tăng theo dữ liệu.
  • Ảnh hưởng nếu đúng là bug: chậm, tốn bộ nhớ; lượt chồng lượt khi khác node.
  • Đã xử lý khi chuyển: GIỮ. Test khoá: TC-11.
  • Nếu muốn sửa: UPDATE set-based theo SQL hoặc chia trang; ảnh hưởng audit/làm sạch chuỗi (B-03).
  • Liên quan: D-05, R-03

B-03 Job trạng thái sửa luôn chuỗi code/lot_no/normalized_name

  • Mức nghi ngờ: THẤP
  • Ở đâu: backend:domain/Batches.java:13-16 (CleanTextListener + @DynamicUpdate) → BatchStatusWriter.updateStatuses
  • Code cũ đang làm gì: dòng đổi status bị listener làm sạch chuỗi, cột chuỗi đổi được ghi theo.
  • Ví dụ cụ thể: lot_no = "L 01" (2 dấu cách) sang ngày hết hạn → ghi status = 0, lot_no = 'L 01'; lô cùng chuỗi nhưng status không đổi giữ "L 01".
  • Vì sao nghi là sai: job trạng thái sửa dữ liệu văn bản ngoài ý muốn.
  • Ảnh hưởng nếu đúng là bug: dữ liệu chuỗi đổi không có người sửa; kiểm trùng số lô có thể khác.
  • Đã xử lý khi chuyển: GIỮ. Test khoá: TC-12.
  • Nếu muốn sửa: bỏ 3 dòng LegacyCleanText trong BatchStatusWriter; đổi TC-12.
  • Liên quan: D-07, R-05

B-04 Job ghi đè trạng thái tính từ dữ liệu cũ khi người dùng sửa lô giữa chừng

  • Mức nghi ngờ: THẤP
  • Ở đâu: BMS:414-429 → BatchStatusUpdateService, BatchStatusWriter (F-logic-2)
  • Code cũ đang làm gì: nạp đầu transaction, ghi lúc commit, không @Version/khoá dòng.
  • Ví dụ cụ thể: 04:25:00 job nạp lô 50 (HSD 04/11/2026, status 1); 04:25:10 người dùng sửa HSD thành 01/06/2027 (API lưu status 1); 04:25:30 job commit status 0 → lô hạn 2027 hiện "đã hết hạn" tới lượt 08:25.
  • Vì sao nghi là sai: mất cập nhật (lost update).
  • Ảnh hưởng nếu đúng là bug: sai tạm thời ≤ 4 giờ, tự khỏi.
  • Đã xử lý khi chuyển: GIỮ (không có D-xx — phát hiện ở review). Test khoá: không (cần chạy song song thật).
  • Nếu muốn sửa: UPDATE có điều kiện exp_date/cấu hình chưa đổi, hoặc khoá lạc quan.
  • Liên quan: R-05, R-06

3d. Quyết định AI tự chốt và thay đổi tự động (cần người xác nhận)

# Nguồn AI đã làm gì Khác code cũ ở điểm nào (input → cũ / mới) Người cần xác nhận gì
1 D-01 (ADR BẮT KHÁC) Cờ riêng app.scheduler.batch-status-update.enabled mặc định false SCHEDULER_ENABLED=true → cũ chạy / mới không chạy tới khi bật Khi nào bật; chung Redis hay tắt job monolith
2 D-02 (PHỤ THUỘC NGOÀI) Bỏ whitelist clientCanDoSchedule node ngoài whitelist → cũ không chạy / mới tranh khoá Chấp nhận
3 D-03 (MƠ HỒ) Không nhả khoá; nhánh race theo starter khoá hết hạn đúng lúc → cũ chạy / mới không Chấp nhận (UNVERIFIED)
4 D-06 (MƠ HỒ) update_time = một now lấy sau khi tính xong cũ: thời điểm flush từng dòng (khác vài ms) / mới: cùng giá trị Chấp nhận
5 D-08 (MƠ HỒ) today, trừ tháng/năm theo múi JVM như cũ — Múi JVM prod
6 D-09 (LỖI KỸ THUẬT) Khoá Redis ngoài transaction DB lỗi lúc bắt đầu → cũ: không đặt khoá, node khác chạy / mới: giữ khoá 10 phút, lượt đó không node nào chạy Chấp nhận
7 D-10 (ADR BẮT KHÁC) try/catch RuntimeException ở scheduler (ngoài transaction) chỉ khác dòng log Chấp nhận; cân nhắc áp cho BatchExpiryWarningScheduler
8 F-logic-1 (MINOR) Log in tên khoá thay vì clientId node giữ khoá chỉ khác log (starter không trả owner) Chấp nhận
9 spec-review Tầng A, lần 1 không có CRITICAL → không chạy lần 2; sửa theo MAJOR F-spec-1: D-04 (mức CAO), R-04; MINOR: R-02, R-06, D-01, D-02, D-03, D-09, D-10, mục 5/6 — Đọc kỹ D-04, R-06, mục 5

4. Finding đã xác nhận

Không có CRITICAL/MAJOR. MINOR: F-logic-1 (mục 3d #8), F-logic-2 nghi-bug-cu (mục 3c B-04).

5. Finding bị loại

Không có.

6. Xử lý của người

Vấn đề Cổng Chỉ thị
Chưa kiểm Sonar G4

7. So với vòng trước

Vòng đầu.

8. Thay đổi trong repo đích

11 file (verify/round-1/changed-files.md): mới scheduler, service, BatchStatusCandidate, BatchStatusChange, BatchStatusWriter + 3 test; sửa BatchRepository, BatchRelationalRepository, application.yml.

9. Đề xuất bổ sung kinh nghiệm

Đề xuất Đưa vào đâu
Nguồn dữ liệu của bảng không chỉ là API đang chuyển: lần mọi chỗ new <Entity>()/save (import Excel/XML, ETL) trước khi đánh giá "dữ liệu NULL hiếm" (F-spec-1) lessons.md 1a
@Transactional cấp class + method @Scheduled public → transaction bao cả bước khoá Redis; mới đặt khoá ngoài transaction cần D-xx legacy-facts.md mục 4
Rule docs/rules/performance.md:109-111: method @Scheduled phải try/catch — đặt ngoài service @Transactional target-cheatsheet.md
DistributedLockManager.acquireLock chỉ trả boolean → không log được owner như monolith target-cheatsheet.md

Nguồn của trang này

  • Báo cáo sai lệch migrations/batch/job-daily-update-status/report.md vòng 1 · sha256 85f04c980ea7

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