From 75f704c8a97f940da8300b156e8b8f0f921430e2 Mon Sep 17 00:00:00 2001 From: "CANCERYS\\kw093" Date: Sun, 8 Feb 2026 21:06:05 +0800 Subject: [PATCH] updat --- .../entity/DeliveryOrderRepository.kt | 2 +- .../entity/models/DeliveryOrderInfo.kt | 6 +- .../service/DeliveryOrderService.kt | 245 ++++++++++++++++-- .../web/DeliveryOrderController.kt | 30 ++- .../web/models/ReleaseDoRequest.kt | 1 + .../jobOrder/service/JoPickOrderService.kt | 4 +- .../jobOrder/web/JobOrderController.kt | 3 +- .../web/model/SecondScanSubmitRequest.kt | 3 +- .../pickOrder/entity/TruckRepository.kt | 4 + .../stock/service/StockTakeRecordService.kt | 2 +- 10 files changed, 251 insertions(+), 49 deletions(-) diff --git a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DeliveryOrderRepository.kt b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DeliveryOrderRepository.kt index 8cdd395..389fe7b 100644 --- a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DeliveryOrderRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DeliveryOrderRepository.kt @@ -102,7 +102,7 @@ fun searchDoLite( @Query(""" - select d from DeliveryOrder d + select distinct d from DeliveryOrder d where d.deleted = false and (:code is null or d.code like concat('%', :code, '%')) and (:shopName is null or d.shop.name like concat('%', :shopName, '%')) diff --git a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/models/DeliveryOrderInfo.kt b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/models/DeliveryOrderInfo.kt index b646bac..1ff21e8 100644 --- a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/models/DeliveryOrderInfo.kt +++ b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/models/DeliveryOrderInfo.kt @@ -43,7 +43,8 @@ interface DeliveryOrderInfoLite { @get:Value("#{target.supplier?.name}") val supplierName: String? - + @get:Value("#{target.supplier?.code}") + val supplierCode: String? @get:Value("#{target.shop?.addr3}") val shopAddress: String? } @@ -55,5 +56,6 @@ data class DeliveryOrderInfoLiteDto( val status: String?, val shopName: String?, val supplierName: String?, - val shopAddress: String? + val shopAddress: String?, + val truckLanceCode: String? ) \ No newline at end of file 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 e045feb..0c9f5bd 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 @@ -40,7 +40,7 @@ import com.ffii.fpsms.modules.master.service.PrinterService import java.time.format.DateTimeFormatter import com.ffii.fpsms.modules.pickOrder.entity.TruckRepository import java.time.LocalTime - +import com.ffii.fpsms.modules.pickOrder.entity.Truck import com.ffii.fpsms.modules.stock.entity.InventoryLotLineRepository import com.ffii.fpsms.modules.stock.entity.StockOutRepository import com.ffii.fpsms.modules.stock.entity.StockOutLIneRepository @@ -113,47 +113,236 @@ open class DeliveryOrderService( private val itemsRepository: ItemsRepository ) { open fun searchDoLiteByPage( - code: String?, - shopName: String?, - status: String?, - estimatedArrivalDate: LocalDateTime?, - pageNum: Int?, - pageSize: Int? - ): RecordsRes { - - val page = (pageNum ?: 1) - 1 // 如果你前端是 1-based;如果前端本来就是 0-based 就不要 -1 - val size = pageSize ?: 10 - val pageable = PageRequest.of(page.coerceAtLeast(0), size) - - val statusEnum = status?.let { s -> DeliveryOrderStatus.entries.find { it.value == s } } - - val etaStart = estimatedArrivalDate - val etaEnd = estimatedArrivalDate?.plusDays(1) - + code: String?, + shopName: String?, + status: String?, + estimatedArrivalDate: LocalDateTime?, + pageNum: Int?, + pageSize: Int?, + truckLanceCode: String? +): RecordsRes { + + val page = (pageNum ?: 1) - 1 + val size = pageSize ?: 10 + val statusEnum = status?.let { s -> DeliveryOrderStatus.entries.find { it.value == s } } + + val etaStart = estimatedArrivalDate + val etaEnd = estimatedArrivalDate?.plusDays(1) + + val searchTruckLanceCode = truckLanceCode?.ifBlank { null }?.lowercase() + + if (searchTruckLanceCode != null) { + println("DEBUG: Filtering by truckLanceCode: $searchTruckLanceCode") + + // ✅ 优化:先从 truck 表找到匹配的 truck,获取 Store_id 和 shopId + val matchingTrucks = truckRepository.findAllByTruckLanceCodeContainingAndDeletedFalse(searchTruckLanceCode) + println("DEBUG: Found ${matchingTrucks.size} matching trucks") + + // 收集所有匹配的 Store_id 和 shopId + val matchingStoreIds = matchingTrucks.mapNotNull { it.storeId }.distinct() + val matchingShopIds = matchingTrucks.mapNotNull { it.shop?.id }.distinct() + + println("DEBUG: Matching storeIds: $matchingStoreIds") + println("DEBUG: Matching shopIds: ${matchingShopIds.size} shops") + + // 根据 Store_id 确定需要过滤的 supplier codes + // Store_id = "2F" → supplier code != "P06B" + // Store_id = "4F" → supplier code = "P06B" + val supplierCodesToInclude = mutableSetOf() + val supplierCodesToExclude = mutableSetOf() + + if (matchingStoreIds.contains("2F")) { + supplierCodesToExclude.add("P06B") // 2F 排除 P06B + } + if (matchingStoreIds.contains("4F")) { + supplierCodesToInclude.add("P06B") // 4F 只包含 P06B + } + + // 查询符合条件的 DeliveryOrder(根据 supplier code 和 shopId 预过滤) + // 注意:这里需要在 Repository 层面添加 supplier code 过滤 + // 或者先查询所有,然后在代码层面过滤 + + val allResult = deliveryOrderRepository.searchDoLitePage( + code = code?.ifBlank { null }, + shopName = shopName?.ifBlank { null }, + status = statusEnum, + etaStart = etaStart, + etaEnd = etaEnd, + pageable = PageRequest.of(0, 100000) + ) + + println("DEBUG: Total records from DB before filtering: ${allResult.totalElements}") + + // ✅ 优化1: 批量查询所有 DeliveryOrder 实体 + val deliveryOrderIds = allResult.content.mapNotNull { it.id } + val deliveryOrdersMap = deliveryOrderRepository.findAllById(deliveryOrderIds) + .associateBy { it.id } + + println("DEBUG: Loaded ${deliveryOrdersMap.size} delivery orders in batch") + + // ✅ 优化2: 预过滤 - 根据 supplier code 和 shopId 过滤 + val preFilteredContent = allResult.content.filter { info -> + val deliveryOrder = deliveryOrdersMap[info.id] + val supplierCode = deliveryOrder?.supplier?.code + + // 检查 supplier code 是否匹配 + val supplierMatches = when { + supplierCodesToExclude.contains(supplierCode) -> false // 排除 + supplierCodesToInclude.isNotEmpty() && !supplierCodesToInclude.contains(supplierCode) -> false // 只包含特定值 + else -> true // 没有限制或匹配 + } + + // 如果提供了 shopId 过滤,也检查 shopId + val shopMatches = if (matchingShopIds.isNotEmpty()) { + deliveryOrder?.shop?.id in matchingShopIds + } else { + true // 如果没有匹配的 shopId,不过滤 + } + + supplierMatches && shopMatches + } + + println("DEBUG: Pre-filtered records: ${preFilteredContent.size} (from ${allResult.content.size})") + + // ✅ 优化3: 收集所有需要查询的 shopId 和日期组合(只处理预过滤后的记录) + val shopIdAndDatePairs = preFilteredContent.mapNotNull { info -> + val deliveryOrder = deliveryOrdersMap[info.id] + val shopId = deliveryOrder?.shop?.id + val estimatedArrivalDate = info.estimatedArrivalDate + if (shopId != null && estimatedArrivalDate != null) { + val targetDate = estimatedArrivalDate.toLocalDate() + val dayAbbr = getDayOfWeekAbbr(targetDate) + val supplierCode = deliveryOrder.supplier?.code + val preferredFloor = if (supplierCode == "P06B") "4F" else "2F" + Triple(shopId, preferredFloor, dayAbbr) + } else { + null + } + }.distinct() + + println("DEBUG: Unique shopId/floor/day combinations: ${shopIdAndDatePairs.size}") + + // ✅ 优化4: 批量查询所有需要的 Truck + val truckCache = mutableMapOf, Truck?>() + shopIdAndDatePairs.forEach { (shopId, preferredFloor, dayAbbr) -> + val trucks = truckRepository.findByShopIdAndStoreIdAndDayOfWeek(shopId, preferredFloor, dayAbbr) + val matchedTruck = if (trucks.isEmpty()) { + truckRepository.findByShopIdAndDeletedFalse(shopId) + .filter { it.storeId == preferredFloor } + .minByOrNull { it.departureTime ?: LocalTime.of(23, 59, 59) } + } else { + trucks.minByOrNull { it.departureTime ?: LocalTime.of(23, 59, 59) } + } + truckCache[Triple(shopId, preferredFloor, dayAbbr)] = matchedTruck + } + + println("DEBUG: Cached ${truckCache.size} truck lookups") + + // 处理预过滤后的记录:计算 truckLanceCode 并过滤 + val processedRecords = preFilteredContent.map { info -> + val deliveryOrder = deliveryOrdersMap[info.id] + val supplierCode = deliveryOrder?.supplier?.code + val preferredFloor = if (supplierCode == "P06B") "4F" else "2F" + val shop = deliveryOrder?.shop + val shopId = shop?.id + val estimatedArrivalDate = info.estimatedArrivalDate + + val calculatedTruckLanceCode = if (deliveryOrder != null && shopId != null && estimatedArrivalDate != null) { + val targetDate = estimatedArrivalDate.toLocalDate() + val dayAbbr = getDayOfWeekAbbr(targetDate) + + // 从缓存中获取 Truck + val matchedTruck = truckCache[Triple(shopId, preferredFloor, dayAbbr)] + matchedTruck?.truckLanceCode + } else { + null + } + + DeliveryOrderInfoLiteDto( + id = info.id, + code = info.code, + orderDate = info.orderDate, + estimatedArrivalDate = info.estimatedArrivalDate, + status = info.status, + shopName = info.shopName, + supplierName = info.supplierName, + shopAddress = info.shopAddress, + truckLanceCode = calculatedTruckLanceCode + ) + }.filter { dto -> + val dtoTruckLanceCode = dto.truckLanceCode?.lowercase() ?: "" + dtoTruckLanceCode.contains(searchTruckLanceCode) + } + + // 计算准确的总数(过滤后的总数) + val totalCount = processedRecords.size + println("DEBUG: Final filtered records count: $totalCount") + + // 对处理后的记录进行分页 + val startIndex = page * size + val endIndex = minOf(startIndex + size, processedRecords.size) + + val paginatedRecords = if (startIndex < processedRecords.size) { + processedRecords.subList(startIndex, endIndex) + } else { + emptyList() + } + + return RecordsRes(paginatedRecords, totalCount) + } else { + // 如果没有提供 truckLanceCode,使用分页查询 val result = deliveryOrderRepository.searchDoLitePage( code = code?.ifBlank { null }, shopName = shopName?.ifBlank { null }, status = statusEnum, etaStart = etaStart, etaEnd = etaEnd, - pageable = pageable + pageable = PageRequest.of(page.coerceAtLeast(0), size) ) - val records = result.content.map { + // 处理当前页的记录 + val records = result.content.map { info -> + val deliveryOrder = deliveryOrderRepository.findByIdAndDeletedIsFalse(info.id) + val supplierCode = deliveryOrder?.supplier?.code + val preferredFloor = if (supplierCode == "P06B") "4F" else "2F" + val shop = deliveryOrder?.shop + val shopId = shop?.id + val estimatedArrivalDate = info.estimatedArrivalDate + + val calculatedTruckLanceCode = if (deliveryOrder != null && shopId != null && estimatedArrivalDate != null) { + val targetDate = estimatedArrivalDate.toLocalDate() + val dayAbbr = getDayOfWeekAbbr(targetDate) + val trucks = truckRepository.findByShopIdAndStoreIdAndDayOfWeek(shopId, preferredFloor, dayAbbr) + + val matchedTruck = if (trucks.isEmpty()) { + truckRepository.findByShopIdAndDeletedFalse(shopId) + .filter { it.storeId == preferredFloor } + .minByOrNull { it.departureTime ?: LocalTime.of(23, 59, 59) } + } else { + trucks.minByOrNull { it.departureTime ?: LocalTime.of(23, 59, 59) } + } + + matchedTruck?.truckLanceCode + } else { + null + } + DeliveryOrderInfoLiteDto( - id = it.id, - code = it.code, - orderDate = it.orderDate, - estimatedArrivalDate = it.estimatedArrivalDate, - status = it.status, - shopName = it.shopName, - supplierName = it.supplierName, - shopAddress = it.shopAddress + id = info.id, + code = info.code, + orderDate = info.orderDate, + estimatedArrivalDate = info.estimatedArrivalDate, + status = info.status, + shopName = info.shopName, + supplierName = info.supplierName, + shopAddress = info.shopAddress, + truckLanceCode = calculatedTruckLanceCode ) } return RecordsRes(records, result.totalElements.toInt()) } +} diff --git a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/DeliveryOrderController.kt b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/DeliveryOrderController.kt index 12fb82b..62376e1 100644 --- a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/DeliveryOrderController.kt +++ b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/DeliveryOrderController.kt @@ -52,20 +52,22 @@ class DeliveryOrderController( ) { @PostMapping("/search-do-lite") -fun searchDoLite(@RequestBody request: SearchDeliveryOrderInfoRequest): RecordsRes { // 改为 DeliveryOrderInfoLiteDto - println("DEBUG: searchDoLite - request: code=${request.code}, shopName=${request.shopName}, status=${request.status}") - println("DEBUG: searchDoLite - estimatedArrivalDate=${request.estimatedArrivalDate}") - println("DEBUG: searchDoLite - pagination: pageNum=${request.pageNum}, pageSize=${request.pageSize}") - - return deliveryOrderService.searchDoLiteByPage( - code = request.code, - shopName = request.shopName, - status = request.status, - estimatedArrivalDate = request.estimatedArrivalDate, - pageNum = request.pageNum, - pageSize = request.pageSize - ) -} + fun searchDoLite(@RequestBody request: SearchDeliveryOrderInfoRequest): RecordsRes { + println("DEBUG: searchDoLite - request: code=${request.code}, shopName=${request.shopName}, status=${request.status}") + println("DEBUG: searchDoLite - estimatedArrivalDate=${request.estimatedArrivalDate}") + println("DEBUG: searchDoLite - truckLanceCode=${request.truckLanceCode}") // 添加这行 + println("DEBUG: searchDoLite - pagination: pageNum=${request.pageNum}, pageSize=${request.pageSize}") + + return deliveryOrderService.searchDoLiteByPage( + code = request.code, + shopName = request.shopName, + status = request.status, + estimatedArrivalDate = request.estimatedArrivalDate, + pageNum = request.pageNum, + pageSize = request.pageSize, + truckLanceCode = request.truckLanceCode + ) + } @GetMapping("/list") fun getDoList(): List { return deliveryOrderService.getDoList(); diff --git a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/models/ReleaseDoRequest.kt b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/models/ReleaseDoRequest.kt index d91004d..90309af 100644 --- a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/models/ReleaseDoRequest.kt +++ b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/models/ReleaseDoRequest.kt @@ -28,4 +28,5 @@ data class SearchDeliveryOrderInfoRequest( val estimatedArrivalDate: LocalDateTime?, val pageSize: Int?, val pageNum: Int?, + val truckLanceCode: String? ) diff --git a/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JoPickOrderService.kt b/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JoPickOrderService.kt index 1c643b0..20a6fa9 100644 --- a/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JoPickOrderService.kt +++ b/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JoPickOrderService.kt @@ -1024,7 +1024,9 @@ open fun submitSecondScanQty(request: SecondScanSubmitRequest): MessageResponse // Set status to completed when submitting quantity joPickOrderEntity.matchStatus = JoPickOrderStatus.completed - // 添加:如果 ticketCompleteTime 还没设置,现在设置(通常已经在拣货完成时设置了) + request.userId?.let { uid -> + joPickOrderEntity.matchBy = uid + } if (joPickOrderEntity.ticketCompleteTime == null) { joPickOrderEntity.ticketCompleteTime = LocalDateTime.now() } diff --git a/src/main/java/com/ffii/fpsms/modules/jobOrder/web/JobOrderController.kt b/src/main/java/com/ffii/fpsms/modules/jobOrder/web/JobOrderController.kt index e655215..33c97d4 100644 --- a/src/main/java/com/ffii/fpsms/modules/jobOrder/web/JobOrderController.kt +++ b/src/main/java/com/ffii/fpsms/modules/jobOrder/web/JobOrderController.kt @@ -156,7 +156,8 @@ class JobOrderController( itemId = itemId, // Use path variable qty = (data["qty"] as Number).toDouble(), isMissing = data["isMissing"] as? Boolean ?: false, - isBad = data["isBad"] as? Boolean ?: false + isBad = data["isBad"] as? Boolean ?: false, + userId = (data["userId"] as Number).toLong() ) return joPickOrderService.submitSecondScanQty(request) } diff --git a/src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/SecondScanSubmitRequest.kt b/src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/SecondScanSubmitRequest.kt index 30ee7bb..2bb639c 100644 --- a/src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/SecondScanSubmitRequest.kt +++ b/src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/SecondScanSubmitRequest.kt @@ -11,5 +11,6 @@ data class SecondScanSubmitRequest( val qty: Double, val isMissing: Boolean = false, val isBad: Boolean = false, - val reason: String? = null + val reason: String? = null, + val userId: Long? = null ) \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/TruckRepository.kt b/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/TruckRepository.kt index 95942dc..c6d4277 100644 --- a/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/TruckRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/TruckRepository.kt @@ -143,4 +143,8 @@ fun findByShopIdAndStoreIdAndDayOfWeek( """ ) fun findAllUniqueShopNamesFromTrucks(): List + + + @Query("SELECT t FROM Truck t WHERE t.truckLanceCode LIKE CONCAT('%', :truckLanceCode, '%') AND t.deleted = false") +fun findAllByTruckLanceCodeContainingAndDeletedFalse(@Param("truckLanceCode") truckLanceCode: String): List } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/service/StockTakeRecordService.kt b/src/main/java/com/ffii/fpsms/modules/stock/service/StockTakeRecordService.kt index 75b217d..cbc64f5 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/service/StockTakeRecordService.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/service/StockTakeRecordService.kt @@ -899,7 +899,7 @@ open fun saveApproverStockTakeRecord( } stockOutLineRepository.save(stockOutLine) - val newBalance = (inventory.onHandQty ?: BigDecimal.ZERO).toDouble()-(varianceQty).toDouble() + val newBalance = (inventory.onHandQty ?: BigDecimal.ZERO).toDouble()+(varianceQty).toDouble() val stockLedger = StockLedger().apply { this.inventory=inventory this.itemId=inventoryLot.item?.id