|
|
|
@@ -120,6 +120,7 @@ open class WarehouseService( |
|
|
|
val sheet: Sheet = workbook.getSheetAt(0); |
|
|
|
|
|
|
|
// Columns |
|
|
|
val COLUMN_REF_INDEX = 0; |
|
|
|
val COLUMN_WAREHOSE_INDEX = 1; |
|
|
|
val COLUMN_ZONE_INDEX = 2; |
|
|
|
val COLUMN_SLOT_INDEX = 3; |
|
|
|
@@ -131,12 +132,13 @@ open class WarehouseService( |
|
|
|
|
|
|
|
val START_ROW_INDEX = 5; |
|
|
|
|
|
|
|
// 第一步:收集所有 Excel 数据(包含行索引,用于调试) |
|
|
|
// first step: collect all Excel data (contains row index, for debugging) |
|
|
|
data class ExcelWarehouseDataWithRow( |
|
|
|
val rowIndex: Int, |
|
|
|
val warehouse: String, |
|
|
|
val area: String, |
|
|
|
val slot: String |
|
|
|
val slot: String, |
|
|
|
val ref: String? |
|
|
|
) |
|
|
|
|
|
|
|
val excelDataList = mutableListOf<ExcelWarehouseDataWithRow>() |
|
|
|
@@ -150,6 +152,7 @@ open class WarehouseService( |
|
|
|
} |
|
|
|
|
|
|
|
try { |
|
|
|
val ref = ExcelUtils.getStringValue(row.getCell(COLUMN_REF_INDEX))?.trim() |
|
|
|
val warehouse = ExcelUtils.getStringValue(row.getCell(COLUMN_WAREHOSE_INDEX))?.trim() |
|
|
|
val area = ExcelUtils.getStringValue(row.getCell(COLUMN_ZONE_INDEX))?.trim() |
|
|
|
val slot = ExcelUtils.getStringValue(row.getCell(COLUMN_SLOT_INDEX))?.trim() |
|
|
|
@@ -159,7 +162,7 @@ open class WarehouseService( |
|
|
|
continue |
|
|
|
} |
|
|
|
|
|
|
|
excelDataList.add(ExcelWarehouseDataWithRow(i, warehouse, area, slot)) |
|
|
|
excelDataList.add(ExcelWarehouseDataWithRow(i, warehouse, area, slot,ref)) |
|
|
|
} catch (e: Exception) { |
|
|
|
logger.error("Read Error (Row ${i + 1}): ${e.message}") |
|
|
|
skippedRows++ |
|
|
|
@@ -168,25 +171,26 @@ open class WarehouseService( |
|
|
|
|
|
|
|
logger.info("Total rows in Excel: ${sheet.lastRowNum - START_ROW_INDEX + 1}, Valid rows collected: ${excelDataList.size}, Skipped: $skippedRows") |
|
|
|
|
|
|
|
// 第二步:计算 order 映射(使用 ExcelWarehouseData) |
|
|
|
val excelDataForOrder = excelDataList.map { |
|
|
|
ExcelWarehouseData(it.warehouse, it.area, it.slot) |
|
|
|
// second step: calculate order map (using ExcelWarehouseData) |
|
|
|
val excelDataForOrder = excelDataList.map { |
|
|
|
ExcelWarehouseData(it.warehouse, it.area, it.slot, it.ref ?: "${it.rowIndex}") // 如果 ref 为空,使用 rowIndex |
|
|
|
} |
|
|
|
val orderMap = calculateOrderMap(excelDataForOrder) |
|
|
|
|
|
|
|
logger.info("Order map size: ${orderMap.size}") |
|
|
|
|
|
|
|
// 第三步:在循环中使用 orderMap 匹配 |
|
|
|
// third step: use orderMap to match in the loop |
|
|
|
var updateCount = 0 |
|
|
|
var createCount = 0 |
|
|
|
var skippedInSecondLoop = 0 |
|
|
|
val processedKeys = mutableSetOf<String>() |
|
|
|
// val processedKeys = mutableSetOf<String>() |
|
|
|
|
|
|
|
for (i in START_ROW_INDEX..<sheet.lastRowNum) { |
|
|
|
val row = sheet.getRow(i) |
|
|
|
if (row == null) continue |
|
|
|
|
|
|
|
try { |
|
|
|
val ref = ExcelUtils.getStringValue(row.getCell(COLUMN_REF_INDEX))?.trim() |
|
|
|
val warehouse = ExcelUtils.getStringValue(row.getCell(COLUMN_WAREHOSE_INDEX))?.trim() |
|
|
|
val area = ExcelUtils.getStringValue(row.getCell(COLUMN_ZONE_INDEX))?.trim() |
|
|
|
val slot = ExcelUtils.getStringValue(row.getCell(COLUMN_SLOT_INDEX))?.trim() |
|
|
|
@@ -200,12 +204,12 @@ open class WarehouseService( |
|
|
|
continue |
|
|
|
} |
|
|
|
|
|
|
|
// 使用唯一标识从 orderMap 获取 order |
|
|
|
val uniqueKey = "$warehouse-$area-$slot" |
|
|
|
val order = orderMap[uniqueKey] |
|
|
|
// use unique identifier from orderMap to get order |
|
|
|
val refKey = ref ?: "${i + 1}" |
|
|
|
val order = orderMap[refKey] |
|
|
|
|
|
|
|
if (order == null) { |
|
|
|
logger.warn("Order not found for key: $uniqueKey at row ${i + 1}") |
|
|
|
logger.warn("Order not found for key: ${refKey}Key at row ${i + 1}") |
|
|
|
skippedInSecondLoop++ |
|
|
|
continue |
|
|
|
} |
|
|
|
@@ -215,28 +219,22 @@ open class WarehouseService( |
|
|
|
val name = "$store_id-$storeLocation" |
|
|
|
val description = "$store_id-$storeLocation" |
|
|
|
|
|
|
|
// 检查是否已经处理过这个 uniqueKey(避免重复处理) |
|
|
|
if (processedKeys.contains(uniqueKey)) { |
|
|
|
logger.warn("Duplicate key found: $uniqueKey at row ${i + 1}, skipping") |
|
|
|
skippedInSecondLoop++ |
|
|
|
continue |
|
|
|
} |
|
|
|
processedKeys.add(uniqueKey) |
|
|
|
|
|
|
|
// 查找现有仓库 |
|
|
|
val existingWarehouse = warehouseRepository.findAll() |
|
|
|
.firstOrNull { |
|
|
|
it.code == code && it.deleted == false |
|
|
|
} ?: warehouseRepository.findAll() |
|
|
|
.firstOrNull { |
|
|
|
it.warehouse == warehouse && |
|
|
|
it.area == area && |
|
|
|
it.slot == slot && |
|
|
|
it.deleted == false |
|
|
|
// check if the warehouse exists |
|
|
|
val existingWarehouse = if (!ref.isNullOrBlank()) { |
|
|
|
try { |
|
|
|
val refId = ref.toLong() |
|
|
|
warehouseRepository.findById(refId).orElse(null)?.takeIf { !it.deleted } |
|
|
|
} catch (e: NumberFormatException) { |
|
|
|
logger.warn("Invalid ref format: $ref at row ${i + 1}, treating as new record") |
|
|
|
null |
|
|
|
} |
|
|
|
} else { |
|
|
|
null |
|
|
|
} |
|
|
|
|
|
|
|
if (existingWarehouse != null) { |
|
|
|
// 更新 |
|
|
|
// update the warehouse |
|
|
|
existingWarehouse.apply { |
|
|
|
this.code = code |
|
|
|
this.name = name |
|
|
|
@@ -253,7 +251,7 @@ open class WarehouseService( |
|
|
|
warehouseRepository.save(existingWarehouse) |
|
|
|
updateCount++ |
|
|
|
} else { |
|
|
|
// 创建 |
|
|
|
// create the warehouse |
|
|
|
val newWarehouse = Warehouse().apply { |
|
|
|
this.code = code |
|
|
|
this.name = name |
|
|
|
@@ -281,40 +279,45 @@ open class WarehouseService( |
|
|
|
return "Import Excel success - Created: $createCount, Updated: $updateCount, Skipped: $skippedInSecondLoop"; |
|
|
|
} |
|
|
|
|
|
|
|
// 计算 order 映射:接收 Excel 数据列表,返回 Map<唯一标识, order> |
|
|
|
// calculate order map: receive Excel data list, return Map<unique identifier, order> |
|
|
|
private fun calculateOrderMap(excelDataList: List<ExcelWarehouseData>): Map<String, Int> { |
|
|
|
data class WarehouseWithSort( |
|
|
|
val data: ExcelWarehouseData, |
|
|
|
val sortValue: Long, |
|
|
|
val uniqueKey: String |
|
|
|
val ref: String, // ref as unique identifier |
|
|
|
val warehouse: String, |
|
|
|
val area: String, |
|
|
|
val slot: String, |
|
|
|
val sortValue: Long |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
val sortedWarehouses = excelDataList.map { data -> |
|
|
|
val slot = data.slot.toIntOrNull() ?: 0 |
|
|
|
val sortValue = calculateSortValue(data.warehouse, data.area, slot) |
|
|
|
val uniqueKey = "${data.warehouse}-${data.area}-${data.slot}" |
|
|
|
WarehouseWithSort(data, sortValue, uniqueKey) |
|
|
|
}.sortedBy { it.sortValue } |
|
|
|
|
|
|
|
// 分配连续的 order(1, 2, 3...),类似 ROW_NUMBER() |
|
|
|
// use ref as unique identifier (ref is stored in rowIndex field) |
|
|
|
val ref = data.rowIndex |
|
|
|
WarehouseWithSort(ref, data.warehouse, data.area, data.slot, sortValue) |
|
|
|
}.sortedBy { it.sortValue } // sort by sortValue |
|
|
|
|
|
|
|
// assign consecutive order (1, 2, 3...), return Map<ref, order> |
|
|
|
return sortedWarehouses.mapIndexed { index, warehouseWithSort -> |
|
|
|
warehouseWithSort.uniqueKey to (index + 1) // 唯一标识 -> order |
|
|
|
warehouseWithSort.ref to (index + 1) // ref -> order |
|
|
|
}.toMap() |
|
|
|
} |
|
|
|
|
|
|
|
// 计算单个仓库的排序值(与 MySQL 逻辑一致) |
|
|
|
|
|
|
|
// calculate sort value for a single warehouse (consistent with MySQL logic) |
|
|
|
private fun calculateSortValue(warehouseStr: String, areaStr: String, slot: Int): Long { |
|
|
|
// 提取楼层号 |
|
|
|
// extract floor number |
|
|
|
val floorNumber = if (warehouseStr.length >= 2) { |
|
|
|
warehouseStr.substring(1, 2).toIntOrNull() ?: 99 |
|
|
|
} else 99 |
|
|
|
|
|
|
|
// 提取字母部分 |
|
|
|
// extract letter part |
|
|
|
val letterPart = if (areaStr.startsWith("#") && areaStr.length >= 2) { |
|
|
|
areaStr.substring(1, 2) |
|
|
|
} else null |
|
|
|
|
|
|
|
// 计算楼层排序值 |
|
|
|
// calculate floor sort value |
|
|
|
val floorOrder = when (floorNumber) { |
|
|
|
2 -> 1 |
|
|
|
3 -> 2 |
|
|
|
@@ -322,7 +325,7 @@ open class WarehouseService( |
|
|
|
else -> 99 |
|
|
|
} |
|
|
|
|
|
|
|
// 计算字母排序值(与 MySQL 逻辑一致) |
|
|
|
// calculate letter sort value (consistent with MySQL logic) |
|
|
|
val letterOrder = when { |
|
|
|
letterPart == null -> 9999 |
|
|
|
letterPart in listOf("C", "B", "A", "E", "F", "G", "H", "I", "N", "M", "L", "P", "Q", "R", "S", "Y", "O", "K", "D") -> { |
|
|
|
@@ -336,7 +339,7 @@ open class WarehouseService( |
|
|
|
else -> 1000 + letterPart[0].code |
|
|
|
} |
|
|
|
|
|
|
|
// 计算总排序值:楼层 * 10000 + 字母 * 100 + slot |
|
|
|
// calculate total sort value: floor * 10000 + letter * 100 + slot |
|
|
|
return floorOrder * 10000L + letterOrder * 100L + slot |
|
|
|
} |
|
|
|
} |