소스 검색

updated

master
CANCERYS\kw093 2 달 전
부모
커밋
a4a30f7e4a
3개의 변경된 파일428개의 추가작업 그리고 111개의 파일을 삭제
  1. +17
    -41
      src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DeliveryOrderService.kt
  2. +185
    -58
      src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoPickOrderService.kt
  3. +226
    -12
      src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoReleaseCoordinatorService.kt

+ 17
- 41
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(
}

}
}

+ 185
- 58
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<DoPickOrder> {
@@ -80,16 +85,17 @@ class DoPickOrderService(
throw e
}
}

fun save(record: DoPickOrder): DoPickOrder {
return doPickOrderRepository.save(record)
}
fun findByStoreIdOrderByTruckDepartureTime(storeId: String): List<DoPickOrder> {
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<DoPickOrder> {
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<DoPickOrder> {
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<DoPickOrder> {
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<DoPickOrder> {
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<DoPickOrderRecord> {
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<DoPickOrderRecord> {
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<DoPickOrder> {
return doPickOrderRepository.findByPickOrderId(pickOrderId)
}

fun updateDoOrderIdForPickOrder(pickOrderId: Long, doOrderId: Long): List<DoPickOrder> {
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" // 默认值
}
}

} // ✅ 类结束
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
)
}
}
}// ✅ 类结束

+ 226
- 12
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<String, BatchReleaseJobStatus>()

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<String, Any>())
println("✅ Updated $rowsUpdated ticket numbers")
} catch (e: Exception) {
println("❌ Error updating ticket numbers: ${e.message}")
e.printStackTrace()
}
}
private fun getOrderedDeliveryOrderIds(ids: List<Long>): List<Long> {
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<Long>, 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()


불러오는 중...
취소
저장