Phân tích luồng sau chuyển — warehouse/export-excel-error-data — vòng 1
F-01 Xuất file dòng lỗi import kho
- Điểm vào: cũ
easyposbackend1:src/main/java/vn/softdreams/easypos/web/rest/WarehouseResource.java:108-116→ mớisrc/main/java/com/softdreams/easypos/warehouse/presentation/web/warehouse/WarehouseController.java:251-260 - Quy tắc: R-01 … R-09
Sơ đồ luồng mới (theo code thực tế)
sequenceDiagram
participant C as Client
participant WC as WarehouseController.exportImportErrors
participant M as WarehouseWebMapperImpl.toImportErrorRows
participant ES as WarehouseImportErrorExportService.exportErrors
participant TW as PoiExcelTableWriter.write
C->>WC: POST /api/client/page/warehouse/export-excel/error-data [WarehouseImportErrorRowRequest…] (@RequestBody bắt buộc, không validate)
WC->>M: toImportErrorRows(request) (null-safe, phần tử null → null, bỏ id/comId/statusImport)
WC->>ES: exportErrors(rows)
ES->>ES: rows null/rỗng → throw SystemException("sds.ep.warehouse")
ES->>ES: có phần tử null → return byte[0]
ES->>ES: toTableRow: [i+1, errorMessage, sku, name, phone, address, status?"Đang hoạt động":"Ngừng hoạt động", sellable?"Có":"Không", description]
ES->>TW: write(ExcelTable("Danh sách bản ghi lỗi", 9 headers, widths {1:7680, 2..8:5120}, centered {0}, rows))
TW->>TW: new SXSSFWorkbook → createSheet → 3 style → setColumnWidth → dòng 0 tiêu đề → dòng 1.. dữ liệu → workbook.write(output)
TW->>TW: finally dispose(); try-with-resources close(); IOException|RuntimeException → LOG.error, nuốt
TW-->>ES: output.toByteArray()
WC-->>C: 200, application/octet-stream, Content-Disposition: form-data; name="attachment"; filename="filename.extension"
Đố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 | Interceptor kiểm gói dịch vụ (LF-21) | — | KHÁC có chủ đích (D-02) |
| 2 | @RequestBody List<WarehouseSaveRequest> bắt buộc, không validateInput — backend:web/rest/WarehouseResource.java:109 |
WarehouseController.java:254, record WarehouseImportErrorRowRequest (key sku, sellable khớp getter cũ — LF-05; base:dto/warehouse/WarehouseSaveRequest.java:51-89) |
KHỚP |
| 3 | Body hỏng/thiếu → 400 body rỗng (LF-03) | 400 INVALID_REQUEST (handler starter) |
KHÁC có chủ đích (D-01) |
| 4 | Getter getStatus() trả false khi null (base:…/WarehouseSaveRequest.java:75-77) |
Giữ null rồi Boolean.TRUE.equals (WarehouseImportErrorExportService.java:89) |
KHỚP (kết quả như nhau) |
| 5 | Mở transaction cấp class (LF-33), không có truy vấn | Không @Transactional |
KHỚP với R-08 (không có lệnh DB) |
| 6 | null/rỗng → BaseInternalServerException(DATA_NOT_FOUND, ENTITY_NAME) → 500 mã sds.ep.warehouse — SVC:1204-1206 |
SystemException(DATA_NOT_FOUND_LEGACY) — WarehouseImportErrorExportService.java:64-66 |
KHỚP mã và status (D-03); text theo starter (D-01, F-logic-2) |
| 7 | Phần tử null → NPE ở SVC:1295 trước write → nuốt → 0 byte |
Kiểm trước, trả new byte[0] — WarehouseImportErrorExportService.java:68-70 |
KHỚP (D-04) |
| 8 | 4 style (SVC:1211-1214, createCellStyle SVC:1590-1616) |
3 style — PoiExcelTableWriter.fill, createCellStyle |
KHÁC (F-logic-1), không đổi nội dung ô |
| 9 | Độ rộng cột 1 = 30*256, cột 2..8 = 20*256 — SVC:1216-1222 |
COLUMN_WIDTHS Map, đặt theo từng mục — WarehouseImportErrorExportService.java:39-42 |
KHỚP (thứ tự đặt không ảnh hưởng) |
| 10 | Dòng 0 tiêu đề 9 cột, cột bắt đầu từ 0 (rowIndex == 0) — SVC:1223-1235, SVC:1624-1635 |
fill dòng HEADER_ROW |
KHỚP |
| 11 | Dòng i+1: STT ô số canh giữa, còn lại canh trái; null → setBlank có style; kiểu lạ → IAE — SVC:1288-1305, SVC:1565-1588 |
toTableRow + setValue |
KHỚP |
| 12 | workbook.write(bos); catch (Exception) printStackTrace; finally close() (lỗi close cũng nuốt) — SVC:1238-1250 |
write + catch (IOException | RuntimeException) → LOG.error; trả phần byte đã ghi |
KHỚP (code chỉ ném được hai loại checked/unchecked này) |
| 13 | Không dispose() → file tạm còn lại |
finally workbook.dispose() |
KHÁC có chủ đích (D-06) |
| 14 | 200, octet-stream, setContentDispositionFormData("attachment","filename.extension") — backend:…/WarehouseResource.java:111-115 |
WarehouseController.java:256-259 |
KHỚP (D-07 GIỮ, D-08 GIỮ) |
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ú |
|---|---|---|---|
| Body JSON | Đọc, không validate | Đọc, không validate | id, comId, statusImport nhận nhưng bỏ |
| File tạm SXSSF | Ghi, không xoá | Ghi, xoá bằng dispose() cả khi thành công lẫn khi lỗi |
D-06 |
| Redis gói dịch vụ | Đọc (interceptor) | Không | D-02 |
| DB | Không (chỉ mở transaction) | Không | — |
Transaction, khoá, idempotency, tenant
- Ranh giới transaction: cũ có transaction cấp class nhưng không truy vấn → mới không có.
- Khoá / cập nhật nguyên tử: không áp dụng.
- Idempotency, thời điểm phát event: không có event.
- Lọc
comId: cả cũ và mới đều không dùng (R-08); không có dữ liệu tenant nào bị truy cập.
Lệch so với luồng mới đã duyệt (spec mục 6)
Không. Code đi đúng Controller → Mapper → WarehouseImportErrorExportService → ExcelTableWriter/PoiExcelTableWriter, như spec mục 5–6.
Dev cần chú ý khi review
| Vị trí | Vì sao |
|---|---|
src/main/java/com/softdreams/easypos/warehouse/infrastructure/excel/PoiExcelTableWriter.java write (try-with-resources lồng try/finally) |
Nhánh lỗi nuốt exception và trả byte đã ghi (D-04). Thứ tự là dispose() trước, close() sau. Lỗi khởi tạo SXSSFWorkbook cũng nằm trong catch. |
src/main/java/com/softdreams/easypos/warehouse/application/warehouse/WarehouseImportErrorExportService.java:64-70 |
Hai nhánh lỗi giữ như cũ: rỗng → 500 mã sds.ep.warehouse (D-03); phần tử null → 0 byte thay cho NPE bị nuốt. |
src/main/java/com/softdreams/easypos/warehouse/domain/warehouse/exception/WarehouseErrorCode.java:31 |
Mã lỗi không có entry i18n (F-logic-2). Cần chạy thử body [] để xem body 500 của starter. |
PoiExcelTableWriter.fill, createCellStyle |
Có 3 style thay vì 4 (F-logic-1). Font không đậm dùng setBold(false) thay vì không gọi, kết quả XML vẫn như nhau. |
src/main/java/com/softdreams/easypos/warehouse/presentation/web/warehouse/WarehouseController.java:254 |
Key lạ (ví dụ "SKU") chỉ bị bỏ qua nếu ObjectMapper khi chạy thật tắt FAIL_ON_UNKNOWN_PROPERTIES. Test controller dùng ObjectMapper riêng đã tắt sẵn, nên chưa kiểm được điều này với cấu hình thật của starter (UNVERIFIED). |