From a4a30f7e4a609ac84478b6a328a70986453b5b60 Mon Sep 17 00:00:00 2001 From: "CANCERYS\\kw093" Date: Sat, 18 Oct 2025 02:18:40 +0800 Subject: [PATCH] updated --- .../service/DeliveryOrderService.kt | 58 ++--- .../service/DoPickOrderService.kt | 243 +++++++++++++----- .../service/DoReleaseCoordinatorService.kt | 238 ++++++++++++++++- 3 files changed, 428 insertions(+), 111 deletions(-) diff --git a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DeliveryOrderService.kt b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DeliveryOrderService.kt index 00f1ae3..37abfe1 100644 --- a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DeliveryOrderService.kt +++ b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DeliveryOrderService.kt @@ -572,11 +572,18 @@ val truck = deliveryOrder.shop?.id?.let { shopId -> println("🔍 DEBUG: Preferred floor based on inventory: $preferredFloor") - // 根据楼层选择对应的 truck - val selectedTruck = when (preferredFloor) { - "2F" -> trucks.find { it.storeId == 2 } - "4F" -> trucks.find { it.storeId == 4 } - else -> trucks.minByOrNull { it.departureTime ?: LocalTime.MAX } + val preferredStoreId = when (preferredFloor) { + "2F" -> 2 + "4F" -> 4 + else -> 2 + } + + val selectedTruck = if (trucks.size > 1) { + // Multiple trucks: prefer matching preferredStoreId, then earliest departure + trucks.find { it.storeId == preferredStoreId } + ?: trucks.minByOrNull { it.departureTime ?: LocalTime.of(23, 59, 59) } + } else { + trucks.firstOrNull() } println("🔍 DEBUG: Selected truck: ID=${selectedTruck?.id}, StoreId=${selectedTruck?.storeId}, DepartureTime=${selectedTruck?.departureTime}") @@ -589,42 +596,11 @@ val storeId = when (truck?.storeId) { 2 -> "2/F" else -> "2/F" // 默认值 } - -println("🔍 DEBUG: Determined store ID: $storeId") - -// ✅ Get ticket number for this store (只需要一次) -val nextTicketNumber = doPickOrderService.getNextTicketNumber(datePrefix, storeId) -println("🔍 DEBUG: Next ticket number for store $storeId: $nextTicketNumber") - -// ✅ 每个 pick order 只创建一条 DoPickOrderRecord -val doPickOrderRecord = DoPickOrderRecord( - storeId = storeId, - ticketNo = nextTicketNumber, - ticketStatus = DoPickOrderStatus.pending, - truckId = truck?.id, - pickOrderId = createdPickOrder.id, - truckDepartureTime = truck?.departureTime, - shopId = deliveryOrder.shop?.id, - handledBy = null, - doOrderId = deliveryOrder.id, - pickOrderCode = createdPickOrder.code, - deliveryOrderCode = deliveryOrder.code, - loadingSequence = deliveryOrder.deliveryOrderLines.size, - // ✅ 填充新增字段 - truckLanceCode = truck?.truckLanceCode, - shopCode = deliveryOrder.shop?.code, - shopName = deliveryOrder.shop?.name, - requiredDeliveryDate = targetDate -) - -println("🔍 DEBUG: Creating DoPickOrderRecord - Store: $storeId, Ticket: $nextTicketNumber, Truck: ${truck?.id}") -val savedDoPickOrderRecord = doPickOrderRecordRepository.save(doPickOrderRecord) -println("🔍 DEBUG: Saved DoPickOrderRecord - ID: ${savedDoPickOrderRecord.id}") - +val loadingSequence = truck?.loadingSequence ?: 999 // ✅ 每个 pick order 只创建一条 DoPickOrder val doPickOrder = DoPickOrder( storeId = storeId, - ticketNo = nextTicketNumber, + ticketNo = "TEMP-${System.currentTimeMillis()}", ticketStatus = DoPickOrderStatus.pending, truckId = truck?.id, doOrderId = deliveryOrder.id, @@ -634,7 +610,7 @@ val doPickOrder = DoPickOrder( handledBy = null, pickOrderCode = createdPickOrder.code, deliveryOrderCode = deliveryOrder.code, - loadingSequence = deliveryOrder.deliveryOrderLines.size, + loadingSequence = loadingSequence, ticketReleaseTime = null, // ✅ 填充新增字段 truckLanceCode = truck?.truckLanceCode, @@ -643,7 +619,7 @@ val doPickOrder = DoPickOrder( requiredDeliveryDate = targetDate ) -println("🔍 DEBUG: Creating DoPickOrder - Store: $storeId, Ticket: $nextTicketNumber, Truck: ${truck?.id}") +println("🔍 DEBUG: Creating DoPickOrder - Store: $storeId, Ticket: , Truck: ${truck?.id}") val savedDoPickOrder = doPickOrderService.save(doPickOrder) println("🔍 DEBUG: Saved DoPickOrder - ID: ${savedDoPickOrder.id}") @@ -867,5 +843,5 @@ return MessageResponse( } } - + } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoPickOrderService.kt b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoPickOrderService.kt index 659b5e5..0181121 100644 --- a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoPickOrderService.kt +++ b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoPickOrderService.kt @@ -28,7 +28,6 @@ import com.ffii.fpsms.modules.pickOrder.service.PickOrderService import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrder import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrderRepository import com.ffii.fpsms.modules.deliveryOrder.enums.DoPickOrderStatus -import java.time.LocalDate import java.math.BigDecimal import com.ffii.fpsms.modules.master.web.models.MessageResponse import java.time.LocalDateTime @@ -38,13 +37,19 @@ import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrderRecordRepository import com.ffii.fpsms.modules.deliveryOrder.web.models.* // ✅ 导入 import com.ffii.fpsms.modules.pickOrder.entity.PickOrderRepository import com.ffii.fpsms.modules.pickOrder.enums.PickOrderStatus - +import com.ffii.fpsms.modules.pickOrder.entity.TruckRepository +import java.time.LocalDate +import java.time.format.DateTimeFormatter +import com.ffii.core.support.JdbcDao +import com.ffii.fpsms.modules.pickOrder.entity.Truck @Service class DoPickOrderService( private val doPickOrderRepository: DoPickOrderRepository, private val doPickOrderRecordRepository: DoPickOrderRecordRepository, private val userRepository: UserRepository, private val pickOrderRepository: PickOrderRepository, + private val jdbcDao: JdbcDao, // ✅ 添加这行 + private val truckRepository: TruckRepository ) { fun findReleasedDoPickOrders(): List { @@ -80,16 +85,17 @@ class DoPickOrderService( throw e } } + fun save(record: DoPickOrder): DoPickOrder { return doPickOrderRepository.save(record) } - + fun findByStoreIdOrderByTruckDepartureTime(storeId: String): List { return doPickOrderRepository.findByStoreIdAndTicketStatusOrderByTruckDepartureTimeAsc( storeId, DoPickOrderStatus.pending ) } - + // Add these missing methods fun assignByStore(request: AssignByStoreRequest): MessageResponse { // TODO: Implement store-based assignment logic @@ -103,7 +109,7 @@ class DoPickOrderService( entity = null ) } - + fun releaseAssignedByStore(request: AssignByStoreRequest): MessageResponse { // TODO: Implement store-based release logic return MessageResponse( @@ -116,29 +122,30 @@ class DoPickOrderService( entity = null ) } - // ✅ Updated method to set ticketReleaseTime when assigning order to user - fun updateHandledByForPickOrder(pickOrderId: Long, userId: Long): List { - val doPickOrders = doPickOrderRepository.findByPickOrderId(pickOrderId) - val user = userRepository.findById(userId).orElse(null) // ✅ 改用 orElse(null) - val handlerName = user?.name ?: "Unknown" - doPickOrders.forEach { - it.handledBy = userId - it.handlerName = handlerName - it.ticketStatus = DoPickOrderStatus.released - it.ticketReleaseTime = LocalDateTime.now() - } - return doPickOrderRepository.saveAll(doPickOrders) + + // ✅ Updated method to set ticketReleaseTime when assigning order to user + fun updateHandledByForPickOrder(pickOrderId: Long, userId: Long): List { + val doPickOrders = doPickOrderRepository.findByPickOrderId(pickOrderId) + val user = userRepository.findById(userId).orElse(null) // ✅ 改用 orElse(null) + val handlerName = user?.name ?: "Unknown" + doPickOrders.forEach { + it.handledBy = userId + it.handlerName = handlerName + it.ticketStatus = DoPickOrderStatus.released + it.ticketReleaseTime = LocalDateTime.now() } - - fun completeDoPickOrdersForPickOrder(pickOrderId: Long): List { - val doPickOrders = doPickOrderRepository.findByPickOrderId(pickOrderId) - doPickOrders.forEach { - it.ticketStatus = DoPickOrderStatus.completed - it.ticketCompleteDateTime = LocalDateTime.now() // ✅ 设置完成时间 - } - return doPickOrderRepository.saveAll(doPickOrders) + return doPickOrderRepository.saveAll(doPickOrders) + } + + fun completeDoPickOrdersForPickOrder(pickOrderId: Long): List { + val doPickOrders = doPickOrderRepository.findByPickOrderId(pickOrderId) + doPickOrders.forEach { + it.ticketStatus = DoPickOrderStatus.completed + it.ticketCompleteDateTime = LocalDateTime.now() // ✅ 设置完成时间 } - + return doPickOrderRepository.saveAll(doPickOrders) + } + // ✅ New method to remove do_pick_order records when auto-assigning by store fun removeDoPickOrdersForPickOrder(pickOrderId: Long): Int { val doPickOrders = doPickOrderRepository.findByPickOrderId(pickOrderId) @@ -153,13 +160,13 @@ class DoPickOrderService( fun saveRecord(record: DoPickOrderRecord): DoPickOrderRecord { return doPickOrderRecordRepository.save(record) } - + // ✅ Add method to update DoPickOrderRecord status fun updateRecordHandledByForPickOrder(pickOrderId: Long, userId: Long): List { val doPickOrderRecords = doPickOrderRecordRepository.findByPickOrderId(pickOrderId) val user = userRepository.findById(userId).orElse(null) // ✅ 改用 orElse(null) val handlerName = user?.name ?: "Unknown" - doPickOrderRecords.forEach { + doPickOrderRecords.forEach { it.handledBy = userId it.handlerName = handlerName it.ticketStatus = DoPickOrderStatus.released @@ -167,43 +174,45 @@ class DoPickOrderService( } return doPickOrderRecordRepository.saveAll(doPickOrderRecords) } - + // ✅ Add method to complete DoPickOrderRecord fun completeDoPickOrderRecordsForPickOrder(pickOrderId: Long): List { val doPickOrderRecords = doPickOrderRecordRepository.findByPickOrderId(pickOrderId) - doPickOrderRecords.forEach { + doPickOrderRecords.forEach { it.ticketStatus = DoPickOrderStatus.completed it.ticketCompleteDateTime = LocalDateTime.now() // ✅ 设置完成时间 } return doPickOrderRecordRepository.saveAll(doPickOrderRecords) } - + // Add method to find do_pick_order records by pick order ID fun findByPickOrderId(pickOrderId: Long): List { return doPickOrderRepository.findByPickOrderId(pickOrderId) } + fun updateDoOrderIdForPickOrder(pickOrderId: Long, doOrderId: Long): List { val doPickOrders = doPickOrderRepository.findByPickOrderId(pickOrderId) - doPickOrders.forEach { + doPickOrders.forEach { it.doOrderId = doOrderId } return doPickOrderRepository.saveAll(doPickOrders) } + fun getSummaryByStore(storeId: String, requiredDate: LocalDate?): StoreLaneSummary { // ✅ 使用传入的日期,如果没有传入则使用今天 val targetDate = requiredDate ?: LocalDate.now() - + println("🔍 DEBUG: Getting summary for store=$storeId, date=$targetDate") - + // ✅ 修改:按日期查询订单 val allRecords = doPickOrderRepository.findByStoreIdAndRequiredDeliveryDateAndTicketStatusIn( - storeId, + storeId, targetDate, listOf(DoPickOrderStatus.pending, DoPickOrderStatus.released, DoPickOrderStatus.completed) ) - + println("🔍 DEBUG: Found ${allRecords.size} records for date $targetDate") - + val grouped = allRecords.groupBy { it.truckDepartureTime to it.truckLanceCode } .mapValues { (_, list) -> LaneBtn( @@ -212,10 +221,10 @@ class DoPickOrderService( total = list.size // 总订单数(包括已分配和未分配) ) } - + val timeGroups = grouped.entries .groupBy { it.key.first } - .mapValues { (_, entries) -> + .mapValues { (_, entries) -> entries.map { it.value } .sortedByDescending { it.unassigned } .take(3) @@ -229,16 +238,17 @@ class DoPickOrderService( lanes = lanes ) } - + return StoreLaneSummary(storeId = storeId, rows = timeGroups) } + // ✅ 修复:把 assignByLane 移到类里面 fun assignByLane(request: AssignByLaneRequest): MessageResponse { val existingOrders = doPickOrderRepository.findByHandledByAndTicketStatusIn( - request.userId, + request.userId, listOf(DoPickOrderStatus.released, DoPickOrderStatus.pending) ) - + if (existingOrders.isNotEmpty()) { return MessageResponse( id = null, code = "USER_BUSY", name = null, type = null, @@ -246,19 +256,19 @@ class DoPickOrderService( errorPosition = null, entity = null ) } - + val candidates = doPickOrderRepository .findByStoreIdAndTicketStatusOrderByTruckDepartureTimeAsc( - request.storeId, + request.storeId, DoPickOrderStatus.pending ) - .filter { + .filter { it.handledBy == null && - it.truckLanceCode == request.truckLanceCode && - (request.truckDepartureTime == null || - it.truckDepartureTime?.toString() == request.truckDepartureTime) + it.truckLanceCode == request.truckLanceCode && + (request.truckDepartureTime == null || + it.truckDepartureTime?.toString() == request.truckDepartureTime) } - + if (candidates.isEmpty()) { return MessageResponse( id = null, code = "NO_ORDERS", name = null, type = null, @@ -266,22 +276,22 @@ class DoPickOrderService( errorPosition = null, entity = null ) } - + val firstOrder = candidates.first() val user = userRepository.findById(request.userId).orElse(null) val handlerName = user?.name ?: "Unknown" - + // ✅ 更新 do_pick_order - 保持原有的卡车信息 firstOrder.handledBy = request.userId firstOrder.handlerName = handlerName firstOrder.ticketStatus = DoPickOrderStatus.released firstOrder.ticketReleaseTime = LocalDateTime.now() - + // ✅ 重要:不要修改 truckDepartureTime 和 truckLanceCode // 这些信息应该保持用户选择的值 - + doPickOrderRepository.save(firstOrder) - + // ✅ 同步更新 pick_order 表 if (firstOrder.pickOrderId != null) { val pickOrder = pickOrderRepository.findById(firstOrder.pickOrderId!!).orElse(null) @@ -292,7 +302,7 @@ class DoPickOrderService( pickOrderRepository.save(pickOrder) } } - + // 同步更新 record val records = doPickOrderRecordRepository.findByPickOrderId(firstOrder.pickOrderId!!) records.forEach { @@ -302,7 +312,7 @@ class DoPickOrderService( it.ticketReleaseTime = LocalDateTime.now() } doPickOrderRecordRepository.saveAll(records) - + return MessageResponse( id = firstOrder.pickOrderId, code = "SUCCESS", @@ -316,7 +326,124 @@ class DoPickOrderService( ) ) } - // 在 DoPickOrderService 类中添加这个方法 + // 在 DoPickOrderService 类中添加这个方法 + open fun determineStoreId(doOrderId: Long): String { + val sql = """ + SELECT + dol.deliveryOrderId, + w.store_id, + SUM(ill.inQty - COALESCE(ill.outQty, 0) - COALESCE(ill.holdQty, 0)) AS total_inventory + FROM fpsmsdb.delivery_order_line dol + LEFT JOIN fpsmsdb.inventory_lot il ON il.itemId = dol.itemId AND il.deleted = 0 + LEFT JOIN fpsmsdb.inventory_lot_line ill ON ill.inventoryLotId = il.id AND ill.deleted = 0 + LEFT JOIN fpsmsdb.warehouse w ON w.id = ill.warehouseId AND w.deleted = 0 + WHERE dol.deleted = 0 AND dol.deliveryOrderId = :doOrderId + GROUP BY dol.deliveryOrderId, w.store_id + ORDER BY total_inventory DESC + LIMIT 1 + """.trimIndent() + + val results = jdbcDao.queryForList(sql, mapOf("doOrderId" to doOrderId)) + return if (results.isNotEmpty()) { + val storeId = results.first()["store_id"] as? String ?: "2F" + when (storeId) { + "2F" -> "2/F" + "4F" -> "4/F" + "3F" -> "3/F" + else -> "2/F" + } + } else { + "2/F" // 默认值 + } + } -} // ✅ 类结束 \ No newline at end of file + open fun selectTruck(shopId: Long?, storeId: String): Truck? { + if (shopId == null) return null + + val storeIdInt = when (storeId) { + "2/F" -> 2 + "4/F" -> 4 + "3/F" -> 3 + else -> 2 + } + + val trucks = truckRepository.findByShopIdAndDeletedFalse(shopId) + return trucks.find { it.storeId == storeIdInt } ?: trucks.firstOrNull() + } + + open fun finishDoPickOrder(doPickOrderId: Long): MessageResponse { + val doPickOrder = doPickOrderRepository.findById(doPickOrderId).orElse(null) + ?: return MessageResponse( + id = null, + code = "NOT_FOUND", + name = null, + type = null, + message = "DO Pick Order not found", + errorPosition = null, + entity = null + ) + + try { + // 确定 store_id 和 truck + val storeId = determineStoreId(doPickOrder.doOrderId ?: 0L) + val truck = selectTruck(doPickOrder.shopId, storeId) + + // 生成 ticket 编号 + val targetDate = doPickOrder.requiredDeliveryDate ?: LocalDate.now() + val datePrefix = targetDate.format(DateTimeFormatter.ofPattern("yyyyMMdd")) + val ticketNumber = getNextTicketNumber(datePrefix, storeId) + + // 更新 do_pick_order + doPickOrder.storeId = storeId + doPickOrder.ticketNo = ticketNumber + doPickOrder.truckId = truck?.id + doPickOrder.truckDepartureTime = truck?.departureTime + doPickOrder.truckLanceCode = truck?.truckLanceCode + + val updatedDoPickOrder = doPickOrderRepository.save(doPickOrder) + + // 创建 do_pick_order_record + val doPickOrderRecord = DoPickOrderRecord( + storeId = storeId, + ticketNo = ticketNumber, + ticketStatus = DoPickOrderStatus.pending, + truckId = truck?.id, + pickOrderId = doPickOrder.pickOrderId, + truckDepartureTime = truck?.departureTime, + shopId = doPickOrder.shopId, + handledBy = null, + doOrderId = doPickOrder.doOrderId, + pickOrderCode = doPickOrder.pickOrderCode, + deliveryOrderCode = doPickOrder.deliveryOrderCode, + loadingSequence = doPickOrder.loadingSequence, + truckLanceCode = truck?.truckLanceCode, + shopCode = doPickOrder.shopCode, + shopName = doPickOrder.shopName, + requiredDeliveryDate = targetDate + ) + + doPickOrderRecordRepository.save(doPickOrderRecord) + + return MessageResponse( + id = updatedDoPickOrder.id, + code = "SUCCESS", + name = null, + type = null, + message = "DO Pick Order finished successfully", + errorPosition = null, + entity = mapOf("ticketNo" to ticketNumber) + ) + } catch (e: Exception) { + return MessageResponse( + id = null, + code = "ERROR", + name = null, + type = null, + message = "Error finishing DO Pick Order: ${e.message}", + errorPosition = null, + entity = null + ) + } + } +}// ✅ 类结束 \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoReleaseCoordinatorService.kt b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoReleaseCoordinatorService.kt index 5727e5d..c246f1e 100644 --- a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoReleaseCoordinatorService.kt +++ b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoReleaseCoordinatorService.kt @@ -9,6 +9,7 @@ import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.Executors import java.util.concurrent.atomic.AtomicInteger import kotlin.math.min +import com.ffii.core.support.JdbcDao data class BatchReleaseJobStatus( val jobId: String, @@ -22,12 +23,221 @@ data class BatchReleaseJobStatus( @Service class DoReleaseCoordinatorService( - private val deliveryOrderService: DeliveryOrderService + private val deliveryOrderService: DeliveryOrderService, + private val jdbcDao: JdbcDao ) { private val poolSize = Runtime.getRuntime().availableProcessors() private val executor = Executors.newFixedThreadPool(min(poolSize, 4)) private val jobs = ConcurrentHashMap() - + private fun updateBatchTicketNumbers() { + try { + val updateSql = """ + UPDATE fpsmsdb.do_pick_order dpo + INNER JOIN ( + WITH DoWarehouseFloor AS ( + SELECT + dol.deliveryOrderId, + w.store_id, + COUNT(DISTINCT dol.itemId) AS item_count + FROM fpsmsdb.delivery_order_line dol + LEFT JOIN fpsmsdb.inventory_lot il ON il.itemId = dol.itemId AND il.deleted = 0 + LEFT JOIN fpsmsdb.inventory_lot_line ill ON ill.inventoryLotId = il.id AND ill.deleted = 0 + LEFT JOIN fpsmsdb.warehouse w ON w.id = ill.warehouseId AND w.deleted = 0 + WHERE dol.deleted = 0 + GROUP BY dol.deliveryOrderId, w.store_id + ), + PreferredFloor AS ( + SELECT + deliveryOrderId, + store_id, + ROW_NUMBER() OVER ( + PARTITION BY deliveryOrderId + ORDER BY item_count DESC, store_id ASC + ) AS rn + FROM DoWarehouseFloor + WHERE store_id IS NOT NULL + ) + SELECT + dpo2.id, + CONCAT('TI-', + DATE_FORMAT(dpo2.RequiredDeliveryDate, '%Y%m%d'), + '-', + COALESCE(pf.store_id, '2F'), + '-', + LPAD( + ROW_NUMBER() OVER ( + PARTITION BY DATE(dpo2.RequiredDeliveryDate), COALESCE(pf.store_id, '2F') + ORDER BY COALESCE(dpo2.truck_departure_time, '23:59:59'), + COALESCE(dpo2.TruckLanceCode, 'ZZ'), + COALESCE(dpo2.loading_sequence, 999), + dpo2.id + ), + 3, '0' + ) + ) AS new_ticket_no + FROM fpsmsdb.do_pick_order dpo2 + LEFT JOIN PreferredFloor pf ON pf.deliveryOrderId = dpo2.do_order_id AND pf.rn = 1 + WHERE dpo2.ticket_no LIKE 'TEMP-%' + AND dpo2.deleted = 0 + ) AS ticket_mapping ON dpo.id = ticket_mapping.id + SET dpo.ticket_no = ticket_mapping.new_ticket_no + """.trimIndent() + + val rowsUpdated = jdbcDao.executeUpdate(updateSql, emptyMap()) + println("✅ Updated $rowsUpdated ticket numbers") + } catch (e: Exception) { + println("❌ Error updating ticket numbers: ${e.message}") + e.printStackTrace() + } + } + private fun getOrderedDeliveryOrderIds(ids: List): List { + try { + println("🔍 DEBUG: Getting ordered IDs for ${ids.size} orders") + + val sql = """ + WITH DoFloorCounts AS ( + SELECT + dol.deliveryOrderId, + w.store_id, + COUNT(DISTINCT dol.itemId) AS item_count + FROM fpsmsdb.delivery_order_line dol + INNER JOIN fpsmsdb.inventory i ON i.itemId = dol.itemId + AND i.deleted = 0 + AND i.onHandQty > 0 + INNER JOIN fpsmsdb.inventory_lot il ON il.itemId = i.itemId + AND il.deleted = 0 + INNER JOIN fpsmsdb.inventory_lot_line ill ON ill.inventoryLotId = il.id + AND ill.deleted = 0 + INNER JOIN fpsmsdb.warehouse w ON w.id = ill.warehouseId + AND w.deleted = 0 + AND w.store_id IN ('2F', '4F') + WHERE dol.deleted = 0 + AND dol.deliveryOrderId IN (${ids.joinToString(",")}) + GROUP BY dol.deliveryOrderId, w.store_id + ), + DoFloorSummary AS ( + SELECT + do.id AS deliveryOrderId, + COALESCE(SUM(CASE WHEN dfc.store_id = '2F' THEN dfc.item_count ELSE 0 END), 0) AS count_2f, + COALESCE(SUM(CASE WHEN dfc.store_id = '4F' THEN dfc.item_count ELSE 0 END), 0) AS count_4f + FROM fpsmsdb.delivery_order do + LEFT JOIN DoFloorCounts dfc ON dfc.deliveryOrderId = do.id + WHERE do.id IN (${ids.joinToString(",")}) + AND do.deleted = 0 + GROUP BY do.id + ), + PreferredFloor AS ( + SELECT + deliveryOrderId, + count_2f, + count_4f, + CASE + WHEN count_2f > count_4f THEN '2F' + WHEN count_4f > count_2f THEN '4F' + ELSE '2F' + END AS preferred_floor, + CASE + WHEN count_2f > count_4f THEN 2 + WHEN count_4f > count_2f THEN 4 + ELSE 2 + END AS preferred_store_id + FROM DoFloorSummary + ), + TruckSelection AS ( + SELECT + do.id AS delivery_order_id, + do.shopId, + do.estimatedArrivalDate, + pf.preferred_floor, + pf.preferred_store_id, + CASE + WHEN (SELECT COUNT(*) FROM fpsmsdb.truck t WHERE t.shopId = do.shopId AND t.deleted = 0) > 1 THEN + COALESCE( + (SELECT t.DepartureTime + FROM fpsmsdb.truck t + WHERE t.shopId = do.shopId AND t.deleted = 0 + AND t.Store_id = pf.preferred_store_id + ORDER BY t.DepartureTime ASC + LIMIT 1), + (SELECT t.DepartureTime + FROM fpsmsdb.truck t + WHERE t.shopId = do.shopId AND t.deleted = 0 + ORDER BY t.DepartureTime ASC + LIMIT 1), + '23:59:59' + ) + ELSE + COALESCE( + (SELECT t.DepartureTime + FROM fpsmsdb.truck t + WHERE t.shopId = do.shopId AND t.deleted = 0 + ORDER BY t.DepartureTime ASC + LIMIT 1), + '23:59:59' + ) + END AS selected_departure_time, + CASE + WHEN (SELECT COUNT(*) FROM fpsmsdb.truck t WHERE t.shopId = do.shopId AND t.deleted = 0) > 1 THEN + COALESCE( + (SELECT t.TruckLanceCode + FROM fpsmsdb.truck t + WHERE t.shopId = do.shopId AND t.deleted = 0 + AND t.Store_id = pf.preferred_store_id + ORDER BY t.DepartureTime ASC + LIMIT 1), + (SELECT t.TruckLanceCode + FROM fpsmsdb.truck t + WHERE t.shopId = do.shopId AND t.deleted = 0 + ORDER BY t.DepartureTime ASC + LIMIT 1), + 'ZZ' + ) + ELSE + COALESCE( + (SELECT t.TruckLanceCode + FROM fpsmsdb.truck t + WHERE t.shopId = do.shopId AND t.deleted = 0 + ORDER BY t.DepartureTime ASC + LIMIT 1), + 'ZZ' + ) + END AS selected_truck_lance, + (SELECT COUNT(*) FROM fpsmsdb.delivery_order_line dol + WHERE dol.deliveryOrderId = do.id AND dol.deleted = 0) AS loading_sequence + FROM fpsmsdb.delivery_order do + LEFT JOIN PreferredFloor pf ON pf.deliveryOrderId = do.id + WHERE do.id IN (${ids.joinToString(",")}) + AND do.deleted = 0 + ) + SELECT delivery_order_id AS id + FROM TruckSelection + ORDER BY + DATE(estimatedArrivalDate), + preferred_floor, + selected_departure_time, + selected_truck_lance, + loading_sequence ASC, + delivery_order_id ASC + """.trimIndent() + + val results = jdbcDao.queryForList(sql) + val sortedIds = results.mapNotNull { it["id"] as? Long } + + println("🔍 DEBUG: Query returned ${sortedIds.size} sorted IDs") + println("🔍 DEBUG: First 10 sorted IDs: ${sortedIds.take(10)}") + + return if (sortedIds.isEmpty()) { + println("⚠️ WARNING: No sorted IDs, using original order") + ids + } else { + sortedIds + } + } catch (e: Exception) { + println("❌ ERROR: ${e.message}") + e.printStackTrace() + return ids + } + } fun startBatchReleaseAsync(ids: List, userId: Long): MessageResponse { if (ids.isEmpty()) { return MessageResponse(id = null, code = "NO_IDS", name = null, type = null, @@ -41,8 +251,10 @@ class DoReleaseCoordinatorService( executor.submit { try { println("📦 Starting serial batch release for ${ids.size} orders") + val sortedIds = getOrderedDeliveryOrderIds(ids) // ✅ 使用本地方法 + println("🔍 DEBUG: Got ${sortedIds.size} sorted orders") - ids.forEachIndexed { index, id -> + sortedIds.forEachIndexed { index, id -> try { val res = deliveryOrderService.releaseDeliveryOrder( ReleaseDoRequest(id = id, userId = userId) @@ -51,13 +263,9 @@ class DoReleaseCoordinatorService( println("🔍 DO $id -> code='$code', msg='${res.message}'") - // 🔧 改进成功判定: - // 1. 标准成功码 - // 2. code是订单号格式(如TORU05PO25090110) - // 3. 没有明确的错误消息 val isSuccess = code in setOf("SUCCESS", "OK", "PARTIAL_SUCCESS") || - code.matches(Regex("TO[A-Z]{2}\\d{2}PO\\d+")) || // 订单号格式 - (res.message == null && code.isNotEmpty()) // 无错误消息且有返回码 + code.matches(Regex("TO[A-Z]{2}\\d{2}PO\\d+")) || + (res.message == null && code.isNotEmpty()) if (isSuccess) { status.success.incrementAndGet() @@ -69,25 +277,31 @@ class DoReleaseCoordinatorService( } if ((index + 1) % 50 == 0) { - println("📊 Progress: ${index + 1}/${ids.size} (Success: ${status.success.get()}, Failed: ${status.failed.size})") + println("📊 Progress: ${index + 1}/${sortedIds.size} (Success: ${status.success.get()}, Failed: ${status.failed.size})") } } catch (e: Exception) { synchronized(status.failed) { status.failed.add(id to (e.message ?: "Exception")) } println("❌ DO $id exception: ${e.javaClass.simpleName} - ${e.message}") + e.printStackTrace() } } - + if (status.success.get() > 0) { + println("🎫 Updating ticket numbers...") + updateBatchTicketNumbers() + } println("✅ Batch completed: ${status.success.get()} success, ${status.failed.size} failed") - // 打印前10个失败的原因 if (status.failed.isNotEmpty()) { println("🔴 Failed examples:") status.failed.take(10).forEach { (id, msg) -> println(" DO $id: $msg") } } + } catch (e: Exception) { + println("❌ Batch release exception: ${e.javaClass.simpleName} - ${e.message}") + e.printStackTrace() } finally { status.running = false status.finishedAt = Instant.now().toEpochMilli()