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

Kho · warehouse/import-excel-save-data · api

POST/api/client/page/warehouse/import-excel/save-data

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

Báo cáo sai lệch — warehouse/import-excel-save-data — vòng 3

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 (ghi nhiều dòng, sinh mã, tenant R-03/R-04) → người cần đọc: spec + báo cáo + toàn bộ diff
  • Số quy tắc: 10 · PASS 4 · DEVIATION có chủ đích 4 · DEVIATION KHÔNG chủ đích 0 · UNVERIFIED 2
  • Coverage thấp nhất: F-01 98,7% dòng / 90,9% nhánh
  • Finding logic: CRITICAL 0 · MAJOR 0 còn mở (1 đã sửa) · MINOR 2 · UNSURE 0
  • Nghi vấn bug code cũ (mục 3c): 7 (CAO 1) · Quyết định tự chốt cần xác nhận (mục 3d): 11
  • Chạy bằng: /mig-auto (spec do spec-reviewer kiểm, chưa có người duyệt) · Số vòng: 3 verify / lần sửa 2/3

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

Mã Mục tiêu Kết quả Bằng chứng
G1 mvnw verify ✅ (660 test) verify/round-3/build.md
G2 Test khớp test-plan ✅ (29/29 TC, 10/10 R) verify/round-3/oracle.md
G3 Coverage ✅ verify/round-3/coverage.md
G4 Sonar CHỜ NGƯỜI sonar-ide.md
G5 0 CRITICAL/MAJOR CONFIRMED ✅ verify/round-3/review-logic.md
G6 Không lách cổng ✅ verify/round-3/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)

Coverage theo luồng

Luồng Tên % dòng % nhánh
F-01 Lưu import — cập nhật 98,7% 90,9%
F-02 Lưu import — tạo mới 99,2% 93,5%
F-03 Lưu import — type khác 1/2 100% 96,2%

2. Ma trận truy vết

Quy tắc Tầng Code mới Test Golden Kết luận
R-01 B WarehouseController.saveImportData, WarehouseImportSaveRequest/RowRequest TC-01, TC-02 — PASS (dòng null trong data → khác, F-logic-2 MINOR)
R-02 C WarehouseImportService, controller TC-03, TC-04 — DEVIATION (có chủ đích, D-12: "data": null)
R-03 A requireCompanyOfCurrentUser TC-05, TC-06 — DEVIATION (có chủ đích, D-01)
R-04 A controller gán comId request, bỏ id TC-01, TC-07 — DEVIATION (có chủ đích, D-03)
R-05 A applyUpdates, Warehouse.applyImport, findAllBySkuIn, saveAll TC-08..TC-15, TC-27, TC-08-EXTRA-1/2 GM-01 UNVERIFIED (collation, dirty-check thật — K-13)
R-06 A createWarehouses, Warehouse.createFromImport, countBySkuAllCompanies, WarehouseCodeGenerator TC-16..TC-23, TC-28 GM-01 UNVERIFIED (SQL thật, collation)
R-07 C WarehouseImportService TC-24 — PASS
R-08 B controller, toResponses TC-25 — PASS
R-09 A @Transactional saveImportData TC-26 — PASS
R-10 B (không chuyển) TC-29 — DEVIATION (có chủ đích, D-11)

3. Người cần xem

# Vị trí Vì sao cần người xem
1 Toàn bộ verify/round-3/changes.diff Tầng A
2 WarehouseRelationalRepository.countBySkuAllCompanies Cố ý không lọc com_id (giữ D-04, nghi bug CAO — 3c B-01)
3 WarehouseRelationalRepository.saveAll findAllById không lọc com_id trong SQL, an toàn tenant dựa vào phép so comId trong bộ nhớ
4 WarehouseManagementService + WarehouseCodeGenerator Refactor dùng chung vòng sinh mã với warehouse/create (không đổi hành vi, test cũ xanh)
5 WarehousePageReader (mới) Phần đọc trang của warehouse/get-with-paging tách nguyên ra khỏi adapter để qua PMD; nằm trong diff của unit này
6 WarehouseController.saveImportData F-logic-2 MINOR: phần tử null trong data → 500 trước cả kiểm công ty/type (cũ: type khác 1/2 vẫn 200)
7 Warehouse.createFromImport/applyImport F-logic-3 MINOR: comId lấy từ dòng do controller gán, không truyền comId đã kiểm vào domain (khác chữ ký spec mục 5)
8 Native SQL findAllBySkuInAndComId, countBySkuAllCompanies Chỉ test mock (K-13) — chạy GM-01
9 G4 Người chạy SonarQube for IDE

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

Chi tiết: verify/round-2/flow-analysis.md (vòng 3 không đổi)

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 6 KHỚP / 5 KHÁC có chủ đích (D-01, D-02/D-08, D-03, D-11, D-12) / 0 / 0 không nhánh lỗi SKU khác hoa thường; dirty-check thô; findAllById + so comId
F-02 Tạo mới 8 KHỚP / 3 KHÁC có chủ đích (D-03, D-04 417→500, D-02) / 0 / 0 không countBySkuAllCompanies không lọc com_id; mã Redis tiêu không hoàn lại
F-03 type khác 1/2 2 KHỚP / 3 KHÁC (D-01, D-02, F-logic-2) / 0 / 0 không map dòng trước khi rẽ type

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

B-01 Tạo kho qua import bị bỏ qua (hoặc lỗi) nếu công ty khác đã dùng mã kho đó

  • Mức nghi ngờ: CAO — câu kiểm tra thiếu điều kiện công ty, trong khi mọi câu khác đều lọc công ty; bước validate chỉ kiểm trong công ty nên báo hợp lệ.
  • Ở đâu: code cũ base:repositories/BaseWarehouseRepository.java:115-116 (findBySku), base:services/impl/BaseWarehouseServiceImpl.java:1121-1122 → code mới (giữ nguyên) WarehouseRelationalRepository.countBySkuAllCompanies, WarehouseImportService.createWarehouses
  • Code cũ đang làm gì: trước khi tạo kho, tìm kho có cùng mã (SKU) ở mọi công ty. Có 1 kho → bỏ qua dòng đó, không báo gì. Có từ 2 kho → lỗi hệ thống, không kho nào được tạo.
  • Ví dụ cụ thể: công ty B đã có kho mã KHO01. Công ty A import file có dòng KHO01 — màn validate báo hợp lệ; bấm lưu → báo "Nhập danh sách kho hàng thành công" nhưng không có kho nào được tạo (count=0). Nếu cả công ty B và C đều có KHO01 → công ty A nhận "Lỗi hệ thống" cho cả file. Đúng nghiệp vụ thì kho KHO01 của công ty A phải được tạo.
  • Vì sao nghi là sai: mã kho là duy nhất trong một công ty (các API thêm/sửa kho chỉ kiểm trong công ty); câu findBySkuIn ngay cạnh có com_id.
  • Ảnh hưởng nếu đúng là bug: công ty dùng mã phổ biến (K01, KHO1…) không import tạo kho được, không có thông báo lỗi; khó tự phát hiện.
  • Đã xử lý khi chuyển: GIỮ. Test khoá: TC-17 (WarehouseImportServiceTest#existingSkuAnywhereIsSkipped), TC-18 (#duplicatedSkuAnywhereIsSystemError), TC-28 (WarehouseRelationalRepositoryTest$ImportSku#countBySkuAllCompanies).
  • Nếu muốn sửa: thêm AND com_id = :comId vào countBySkuAllCompanies (đổi tên), báo lỗi/ghi rõ dòng bị bỏ qua; đổi TC-17, TC-18, TC-28. Hệ thống cũ còn chạy song song sẽ lệch kết quả.
  • Liên quan: D-04, R-06

B-02 Lưu import không kiểm lại dữ liệu: lưu cả dòng bị đánh dấu lỗi, tên rỗng, SKU trùng

  • Mức nghi ngờ: TRUNG BÌNH — dựa hoàn toàn vào client gửi đúng kết quả validate.
  • Ở đâu: backend:web/rest/WarehouseResource.java:117-121; base:services/impl/BaseWarehouseServiceImpl.java:1100-1143 → WarehouseController.saveImportData, WarehouseImportService
  • Code cũ đang làm gì: không validate, không lọc dòng statusImport=false, không kiểm trùng tên/SKU trong file và với DB.
  • Ví dụ cụ thể: client gửi dòng {name:"", sku:"K09", statusImport:false} → tạo kho K09 tên rỗng. Hai dòng cùng SKU K11 → tạo 2 kho trùng SKU. Dòng {sku:"WH7"} và dòng không SKU (mã sinh ra WH7) → 2 kho cùng SKU WH7.
  • Vì sao nghi là sai: API thêm kho thường chặn các trường hợp này.
  • Ảnh hưởng nếu đúng là bug: dữ liệu kho trùng/rỗng khi client lỗi hoặc request tự dựng.
  • Đã xử lý khi chuyển: GIỮ. Test khoá: TC-01, TC-23.
  • Nếu muốn sửa: lọc statusImport, kiểm lại tên/SKU; đổi TC-01, TC-23.
  • Liên quan: D-05, R-01, R-06

B-03 Cập nhật qua import đổi tên kho nhưng không cập nhật "tên tìm kiếm"

  • Mức nghi ngờ: TRUNG BÌNH
  • Ở đâu: base:services/impl/BaseWarehouseServiceImpl.java:1114-1115 → Warehouse.applyImport
  • Code cũ đang làm gì: chép dữ liệu mới vào kho nhưng giữ cột normalized_name cũ.
  • Ví dụ cụ thể: kho K01 "Kho cũ", import đổi thành "Kho Hà Nội" → ô tìm kiếm danh sách kho gõ "ha noi" không ra K01, gõ "kho cu" vẫn ra.
  • Vì sao nghi là sai: thêm/sửa kho thường luôn tính lại tên tìm kiếm.
  • Ảnh hưởng nếu đúng là bug: tìm kiếm kho sai sau khi import cập nhật.
  • Đã xử lý khi chuyển: GIỮ. Test khoá: TC-08.
  • Nếu muốn sửa: tính lại normalizedName trong applyImport; đổi TC-08; có thể cần chạy lại tên tìm kiếm cho dữ liệu cũ.
  • Liên quan: D-06, R-05

B-04 Import cập nhật có thể ngừng kho mặc định; không ghi trạng thái là ngừng kho

  • Mức nghi ngờ: TRUNG BÌNH
  • Ở đâu: base:services/impl/BaseWarehouseServiceImpl.java:1114-1115 → Warehouse.applyImport
  • Code cũ đang làm gì: cột trạng thái trống được hiểu là "ngừng hoạt động"; không áp luật kho mặc định WH1; không kiểm đơn chưa xử lý.
  • Ví dụ cụ thể: dòng {sku:"WH1", name:"Kho tổng"} (không có trạng thái) → kho mặc định bị ngừng hoạt động, "cho bán" thành trống. Lẽ ra bị chặn như API sửa kho (UPDATE_WAREHOUSE_DEFAULT_INVALID).
  • Vì sao nghi là sai: API sửa kho chặn đúng trường hợp này; bước validate có chặn nhưng bước lưu không kiểm lại.
  • Ảnh hưởng nếu đúng là bug: cửa hàng mất kho bán mặc định.
  • Đã xử lý khi chuyển: GIỮ. Test khoá: TC-09.
  • Nếu muốn sửa: gọi validateDefaultWarehouse sau applyImport; đổi TC-09.
  • Liên quan: D-07, R-05

B-05 Mã kho trong file khác hoa thường với mã đang lưu → "Lỗi hệ thống" cả file

  • Mức nghi ngờ: THẤP — phụ thuộc collation DB (UNVERIFIED).
  • Ở đâu: base:services/impl/BaseWarehouseServiceImpl.java:1108-1116 → WarehouseImportService.applyUpdates
  • Code cũ đang làm gì: DB tìm thấy kho (so không phân biệt hoa thường) nhưng bước ghép dòng so phân biệt hoa thường → lỗi.
  • Ví dụ cụ thể: kho lưu k01, file ghi K01 → cả file lỗi, không dòng nào được cập nhật. Lẽ ra cập nhật kho k01.
  • Đã xử lý khi chuyển: GIỮ (500 EXCEPTION_ERROR). Test khoá: TC-13.
  • Nếu muốn sửa: ghép không phân biệt hoa thường; đổi TC-13.
  • Liên quan: D-08, R-05

B-06 Tạo kho qua import không ghi trạng thái → kho ngừng hoạt động

  • Mức nghi ngờ: THẤP — bước validate thường điền trạng thái từ cột Excel.
  • Ở đâu: base:dto/warehouse/WarehouseSaveRequest.java (getStatus) → Warehouse.createFromImport
  • Ví dụ cụ thể: dòng {name:"Kho mới", sku:"K10"} → kho K10 ngừng hoạt động, "cho bán" trống. Thêm kho thường thì luôn hoạt động.
  • Đã xử lý khi chuyển: GIỮ. Test khoá: TC-22.
  • Nếu muốn sửa: mặc định hoạt động; đổi TC-22.
  • Liên quan: D-09, R-06

B-07 Loại import sai vẫn báo "Nhập thành công"

  • Mức nghi ngờ: THẤP
  • Ở đâu: base:services/impl/BaseWarehouseServiceImpl.java:1104-1142 → WarehouseImportService.saveImportData
  • Ví dụ cụ thể: type=3 với 5 dòng → "Nhập danh sách kho hàng thành công", không lưu gì.
  • Đã xử lý khi chuyển: GIỮ. Test khoá: TC-24.
  • Nếu muốn sửa: 400 khi type sai; đổi TC-24.
  • Liên quan: D-10, R-07

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-03 (LỖI KỸ THUẬT) Mọi kho dùng comId request, CREATE bỏ id dòng dòng comId=20 (request 10) → cũ chuyển/tạo kho ở công ty 20 / mới công ty 10; CREATE dòng id=5 → cũ ghi đè kho 5 / mới tạo mới Đồng ý (chỉ khác với request tự dựng)
2 D-01 (LỖI KỸ THUẬT) Kiểm comId của token comId công ty khác → cũ ghi được / mới 400; comId:null CREATE → cũ tạo kho với mã sequence null / mới 400 INVALID_COMPANY Đồng ý
3 D-12 (ADR BẮT KHÁC) "data": null như rỗng cũ 417 / mới 200 status=false FAIL Đồng ý
4 D-02 (ADR BẮT KHÁC) Body theo starter; 417 → 500 giữ mã reason mã thay text; lỗi hệ thống 500 Frontend đọc mã
5 D-11 (PHỤ THUỘC NGOÀI) Không kiểm gói công ty hết hạn vẫn import được Tầng đảm nhận (chung)
6 spec-review Spec qua 1 lần review (0 CRITICAL); R-xx sửa theo MAJOR: R-01, R-02 (data null/thiếu), R-05 (dirty-check trên giá trị thô → audit dù sau làm sạch trùng), R-06 (SKU ""/" "/null tách riêng) — Đọc kỹ R-01, R-02, R-05, R-06
7 Impl (lần sửa 1) Tách WarehousePageReader khỏi adapter (PMD độ phức tạp lớp) — code của get-with-paging không đổi hành vi (reviewer vòng 2 đối chiếu từng dòng) Đồng ý cấu trúc
8 Impl WarehouseCodeGenerator dùng chung với warehouse/create không đổi hành vi Đồng ý
9 F-logic-2 (MINOR) Không sửa {type:3, data:[null]} → cũ 200 / mới 500 Có cần giữ không
10 F-logic-3 (MINOR) Không sửa không khác kết quả với controller hiện tại Có cần truyền comId vào domain như spec mục 5 không
11 JSON NON_EMPTY của starter (chung với warehouse/get-with-paging F-logic-1b) response mất key null/rỗng (ví dụ phone:null trong WarehouseResponse, data:[]) Quyết định chung cho mọi API

4. Finding đã xác nhận

F-logic-1 — MAJOR, data-integrity — CONFIRMED vòng 1 → ĐÃ HẾT vòng 2

Lưu từng kho, mỗi kho cập nhật thêm 1 SELECT (N+1). Sửa: WarehouseRepository.saveAll (một findAllById, một saveAll kho mới); khoá bằng TC-08-EXTRA-1/2.

F-logic-2, F-logic-3 — MINOR (mục 3 #6, #7)

5. Finding bị loại

Finding Lý do loại
— —

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

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

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

Vấn đề Vòng 1 Vòng 2 Vòng 3
F-logic-1 (N+1) MAJOR CONFIRMED ĐÃ HẾT ĐÃ HẾT
G3 WarehousePageReader chưa gán luồng — MỚI — do lần sửa ĐÃ HẾT
F-logic-2, F-logic-3 MINOR CÒN CÒN

8. Thay đổi trong repo đích

22 file (verify/round-3/changed-files.md): 15 main Java + 2 i18n + 5 test, trong src/main, src/test. Mới: WarehouseCodeGenerator, WarehouseImportService, WarehouseImportCommand, WarehouseImportResult, WarehouseImportSaveRequest, WarehouseImportRowRequest, WarehousePageReader, WarehouseImportServiceTest. Sửa code của unit khác: WarehouseManagementService (dùng generator), WarehouseRelationalRepository (tách đọc trang). Không có file ngoài phạm vi.

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

Đề xuất Đưa vào đâu
PMD giới hạn tổng độ phức tạp cấp lớp 50: adapter dùng chung nhiều unit (như WarehouseRelationalRepository) sẽ chạm trần — tách lớp phụ trợ package-private theo chức năng sớm target-cheatsheet mục 4
Thêm file src/main mới ở bước sửa → cập nhật flows.json ngay, nếu không G3 trượt ở vòng sau (tốn một lần sửa) lessons 1b
Tuyệt đối không thêm @SuppressWarnings kể cả trong test (G6); dùng ArgumentCaptor.forClass(List.class) chấp nhận cảnh báo biên dịch lessons 1c
Native select * trả entity rồi save từng cái → N+1; dùng saveAll nạp một lần bằng findAllById lessons 1b
Code cũ BeanUtils.copyProperties + dirty-check của Hibernate: audit ghi theo giá trị thô trước CleanTextListener legacy-facts mục 4

Nguồn của trang này

  • Báo cáo sai lệch migrations/warehouse/import-excel-save-data/report.md vòng 3 · sha256 a1d4538cd552

v1 dựng 2026-11-06 00:42 · base commit đích a06ccfa6d4