Báo cáo sai lệch — warehouse/export-excel-error-data — vòng 1
SVC = easyposbase:src/main/java/sds/ep/baseservice/services/impl/BaseWarehouseServiceImpl.java; đường dẫn mới tính từ
warehouse-service:src/main/java/com/softdreams/easypos/warehouse/. (Báo cáo CHẶN lần trước — thiếu Apache POI — đã bị ghi đè khi viết báo cáo này; lý do CHẶN còn trong nhật ký status.md.)
0. Kết luận nhanh
- Điều kiện đạt:
G1 ✅ G2 ✅ G3 ✅ G4 — G5 ✅ G6 ✅ → CHỜ KIỂM SONAR (máy đạt hết, chờ người kiểm G4)
- Trạng thái: SẴN SÀNG CHO NGƯỜI XEM
- Tầng rủi ro: C (tự động — không đọc/ghi DB) → người cần đọc: spec + báo cáo
- Số quy tắc: 9 · PASS 6 · DEVIATION có chủ đích 2 (R-01/R-02 định dạng lỗi theo starter, R-07 xoá file tạm) · DEVIATION KHÔNG chủ đích 0 · UNVERIFIED 1 (R-09)
- Coverage thấp nhất: F-01 100% dòng / 100% nhánh
- Finding logic: CRITICAL 0 · MAJOR 0 · MINOR 2 · UNSURE 0
- Nghi vấn bug code cũ (mục 3c): 4 (CAO 0) · Quyết định tự chốt cần xác nhận (mục 3d): 6
- Chạy bằng:
/mig-auto (spec do spec-reviewer kiểm, chưa có người duyệt) · Số vòng: 1 / lần sửa 0/3
- Sự cố trong lượt: working tree repo đích bị GitHub Desktop stash (
f14b118) giữa chừng; người đã khôi phục. Build vòng 0 lúc đó vô hiệu; snapshot dựng
lại (89a6df6… = HEAD a54d38b + code import-excel-validate), verify vòng 1 chạy sau khi khôi phục.
1. Điều kiện đạt
| Mã |
Mục tiêu |
Kết quả |
Bằng chứng |
| G1 |
Kiến trúc ADR (mvnw verify) |
✅ |
verify/round-1/build.md — 747 test, ArchUnit 36/36, 0 vi phạm |
| G2 |
Test khớp test-plan, mọi R-xx có test |
✅ |
verify/round-1/oracle.md — 16/16 TC |
| G3 |
Coverage mỗi luồng |
✅ |
verify/round-1/coverage.md — F-01 100% / 100% |
| G4 |
0 issue Sonar |
CHỜ NGƯỜI |
sonar-ide.md (CHƯA KIỂM) |
| G5 |
0 finding CRITICAL/MAJOR đã xác nhận |
✅ |
verify/round-1/review-logic.md (2 MINOR, không cần xác minh) |
| G6 |
Không lách cổng |
✅ |
verify/round-1/gates.md — 12 file, đều có trong impl-notes.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 |
Xuất file dòng lỗi import kho |
100.0% |
100.0% |
2. Ma trận truy vết
| Quy tắc |
Tầng |
Code mới |
Test |
Golden |
Kết luận |
| R-01 |
C |
WarehouseController.exportImportErrors, WarehouseImportErrorRowRequest, WarehouseWebMapper.toImportErrorRows |
TC-01, TC-02 |
GM-01 |
DEVIATION (có chủ đích, D-01: 400 INVALID_REQUEST) |
| R-02 |
C |
WarehouseImportErrorExportService.exportErrors |
TC-03, TC-04 |
— |
DEVIATION (có chủ đích, D-01: text lỗi theo starter — UNVERIFIED, F-logic-2) |
| R-03 |
C |
exportErrors (phần tử null), PoiExcelTableWriter.write |
TC-05..TC-07 |
— |
PASS |
| R-04 |
C |
PoiExcelTableWriter.fill/createCellStyle, hằng của service |
TC-08, TC-09 |
GM-01 |
PASS (thiếu 1 style không dùng — F-logic-1) |
| R-05 |
C |
toTableRow, setValue |
TC-10..TC-13 |
GM-01 |
PASS |
| R-06 |
C |
WarehouseController.exportImportErrors |
TC-14 |
GM-01 |
PASS |
| R-07 |
C |
PoiExcelTableWriter.write (dispose) |
TC-15 |
— |
DEVIATION (có chủ đích, D-06) |
| R-08 |
C |
không @Transactional |
TC-16 |
— |
PASS |
| R-09 |
B |
— |
— |
— |
UNVERIFIED (D-02) |
3. Người cần xem
| # |
Vị trí |
Vì sao cần người xem |
| 1 |
WarehouseImportErrorExportService.exportErrors, WarehouseErrorCode.DATA_NOT_FOUND_LEGACY |
Giữ mã lỗi lạ sds.ep.warehouse (D-03); không có text i18n — body 500 của starter chưa chạy thử (F-logic-2) |
| 2 |
PoiExcelTableWriter.write |
Nuốt lỗi, trả file 0 byte như cũ (D-04) |
| 3 |
WarehouseController.exportImportErrors |
Trả ResponseEntity<byte[]> — lệch câu chữ CODING_RULES về body BaseResponseDTO (D-08) |
| 4 |
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 |
Xuất file dòng lỗi |
10 / 4 / 0 / 0 |
không |
nhánh nuốt lỗi + dispose; 2 nhánh lỗi giữ như cũ; mã lỗi không i18n; 3 style thay 4; key lạ phụ thuộc cấu hình Jackson thật |
3c. Nghi vấn bug / sai ở code cũ (đã GIỮ nguyên khi chuyển)
B-01 Bấm tải file lỗi khi không có dòng nào → "lỗi hệ thống" không có câu thông báo
- Mức nghi ngờ: TRUNG BÌNH — tham số truyền đảo chỗ rõ ràng.
- Ở đâu: code cũ
SVC:1204-1206 (new BaseInternalServerException(DATA_NOT_FOUND, ENTITY_NAME), constructor là (entityName, errorKey)) → code mới
application/warehouse/WarehouseImportErrorExportService.java (SystemException(DATA_NOT_FOUND_LEGACY))
- Code cũ đang làm gì: danh sách rỗng thì báo lỗi, nhưng mã lỗi nhận nhầm tên module
sds.ep.warehouse thay vì DATA_NOT_FOUND.
- Ví dụ cụ thể: gửi
[] → HTTP 500, message[0].code = "sds.ep.warehouse", reason = null — giao diện không có câu thông báo. Lẽ ra là mã
DATA_NOT_FOUND (và có thể nên là 400).
- Vì sao nghi là sai: thứ tự tham số ngược với constructor.
- Ảnh hưởng nếu đúng là bug: chỉ khi client gửi danh sách rỗng; người dùng thấy lỗi không rõ nghĩa.
- Đã xử lý khi chuyển: GIỮ mã
sds.ep.warehouse, HTTP 500. Test khoá: TC-03 (WarehouseImportErrorExportServiceTest#emptyRowsIsLegacyError), TC-04.
- Nếu muốn sửa: đổi
DATA_NOT_FOUND_LEGACY thành DATA_NOT_FOUND + thêm text i18n; đổi TC-03, TC-04.
- Liên quan: D-03, R-02
B-02 Lỗi khi tạo file bị nuốt → tải về file 0 byte
- Mức nghi ngờ: TRUNG BÌNH
- Ở đâu:
SVC:1207-1251 → PoiExcelTableWriter.write, WarehouseImportErrorExportService.exportErrors
- Code cũ đang làm gì: mọi lỗi khi dựng file chỉ in log, vẫn trả HTTP 200 với phần đã ghi (thường rỗng).
- Ví dụ cụ thể: body
[{"name":"Kho 1"}, null] hoặc mô tả dài 40.000 ký tự → 200, file 0 byte, không mở được.
- Vì sao nghi là sai: người dùng nhận file hỏng thay vì thông báo lỗi.
- Ảnh hưởng nếu đúng là bug: hiếm (dữ liệu lạ); khó chẩn đoán.
- Đã xử lý khi chuyển: GIỮ. Test khoá:
TC-05, TC-06, TC-07.
- Nếu muốn sửa: ném
SystemException khi dựng file lỗi; đổi TC-05..TC-07.
- Liên quan: D-04, R-03
B-03 File lỗi ghi ô trạng thái trống thành "Ngừng hoạt động" — ngược cách import hiểu ô trống
- Mức nghi ngờ: THẤP — thực tế bước kiểm luôn điền hai giá trị này.
- Ở đâu:
SVC:1300-1301 → WarehouseImportErrorExportService.toTableRow
- Code cũ đang làm gì: không có trạng thái → ghi "Ngừng hoạt động"; không có bán trực tiếp → "Không". Bước import hiểu ô trống là "đang hoạt động"/"có".
- Ví dụ cụ thể: dòng
{name:"Kho 1"} → file ghi "Ngừng hoạt động", "Không"; sửa file rồi import lại → kho bị ngừng hoạt động.
- Vì sao nghi là sai: hai chiều xuất/nhập không khớp.
- Ảnh hưởng nếu đúng là bug: chỉ khi client tự dựng body thiếu hai field.
- Đã xử lý khi chuyển: GIỮ. Test khoá:
TC-11, TC-10.
- Nếu muốn sửa: để trống ô khi không có giá trị; đổi TC-10, TC-11.
- Liên quan: D-05, R-05
B-04 Header tải file: tên file filename.extension, kiểu form-data
- Mức nghi ngờ: THẤP — client hiện tại có lẽ tự đặt tên file.
- Ở đâu:
backend:web/rest/WarehouseResource.java:113-114 → WarehouseController.exportImportErrors
- Ví dụ cụ thể:
Content-Disposition: form-data; name="attachment"; filename="filename.extension" — tải trực tiếp bằng trình duyệt ra file không đuôi .xlsx.
- Đã xử lý khi chuyển: GIỮ (hợp đồng API). Test khoá:
TC-14.
- Nếu muốn sửa:
ContentDisposition.attachment().filename("…xlsx"); đổi TC-14; kiểm client.
- Liên quan: D-07, 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 + F-logic-2 (MINOR) |
Lỗi theo starter; mã sds.ep.warehouse cố ý không có text i18n (lệch CODING_RULES 5.1 để giữ message rỗng như cũ) |
[] → cũ 500 message:null, reason:null / mới 500 mã sds.ep.warehouse, text tuỳ starter (UNVERIFIED); body null/{} → cũ 400 rỗng / mới 400 INVALID_REQUEST |
Chạy thử [] để xem body; chọn có thêm text i18n hay không |
| 2 |
D-06 (LỖI KỸ THUẬT) |
dispose() xoá file tạm SXSSF |
mỗi lần tải: cũ để lại file poi-sxssf-sheet*.xml / mới xoá |
Đồng ý |
| 3 |
D-08 (ADR BẮT KHÁC — giữ hợp đồng) |
ResponseEntity<byte[]>, không bọc BaseResponseDTO |
không khác cũ; lệch câu chữ CODING_RULES (mục controller) |
Đồng ý ngoại lệ cho API tải file |
| 4 |
D-02 (PHỤ THUỘC NGOÀI) |
Không kiểm gói dịch vụ |
công ty hết hạn gói → cũ 400 / mới tải được |
Chung mọi API |
| 5 |
F-logic-1 (MINOR) |
Không tạo style "canh phải" mà code cũ tạo nhưng không dùng |
styles.xml ít hơn 1 style + 1 font; nội dung ô, định dạng hiển thị như nhau |
Chấp nhận (không quan sát được) hoặc chỉ thị thêm style |
| 6 |
spec-review |
spec qua 1 lần review, các R-xx đã sửa theo MAJOR: R-01, R-02 (F-spec-01: body null → 400 do Spring), mục 5 mô hình ô (F-spec-02) |
— |
Đọc kỹ R-02 và mục 5 |
| — |
Ghi chú |
Key lạ trong body ("SKU") bị bỏ qua chỉ khi ObjectMapper thật tắt FAIL_ON_UNKNOWN_PROPERTIES (Spring Boot mặc định tắt; chưa đọc starter) |
— |
Chạy GM-01 với body có key id, comId, statusImport, SKU |
4. Finding đã xác nhận
Không có CRITICAL/MAJOR. MINOR (không qua xác minh, chuyển cho người): F-logic-1 (3 style thay 4), F-logic-2 (mã lỗi không có i18n) — nguyên văn ở
verify/round-1/review-logic.md.
5. Finding bị loại
6. Xử lý của người
| Vấn đề |
Cổng |
Chỉ thị |
| Kiểm Sonar các file thay đổi |
G4 |
|
7. So với vòng trước
| Vấn đề |
Vòng trước |
Vòng này |
| CHẶN — thiếu Apache POI |
CHẶN (lượt 20261105-1253) |
ĐÃ HẾT |
| F-logic-1, F-logic-2 |
— |
MỚI |
8. Thay đổi trong repo đích
12 file (verify/round-1/changed-files.md): 6 mới ở src/main (ExcelTable, ExcelTableWriter, PoiExcelTableWriter, WarehouseImportErrorRow,
WarehouseImportErrorExportService, WarehouseImportErrorRowRequest), 3 sửa ở src/main (WarehouseErrorCode, WarehouseWebMapper,
WarehouseController), 2 test mới, WarehouseControllerTest sửa. Không file ngoài phạm vi; không chạm pom.xml.
9. Đề xuất bổ sung kinh nghiệm (/mig-learn gom vào proposal để người duyệt)
| Đề xuất |
Đưa vào đâu |
GitHub Desktop tự stash thay đổi chưa commit khi người commit/đổi nhánh giữa lượt /mig-auto → verify chạy trên tree thiếu code mà vẫn xanh. Trước mỗi build/gates kiểm số file thay đổi so với lần trước; giảm đột ngột → dừng |
lessons.md Phần 1 + skill mig-auto |
| Dựng lại snapshot khi HEAD đổi giữa lượt: index tạm, lấy HEAD mới + code các unit trước (cây vòng verify của unit trước) |
lessons.md Phần 2 |
@RequestBody mặc định bắt buộc: body literal null → 400 trước service; nhánh request == null của code cũ là chết qua HTTP |
legacy-facts.md (đề xuất) |
Tham số truyền đảo vào constructor exception cũ ((entityName, errorKey)) → mã lỗi thật khác tên hằng; kiểm thứ tự tham số khi trích mã lỗi |
lessons.md 1a |