浏览代码

update

master
CANCERYS\kw093 1 个月前
父节点
当前提交
ee2ee33189
共有 6 个文件被更改,包括 1661 次插入1246 次删除
  1. +75
    -45
      src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoReleaseCoordinatorService.kt
  2. +3
    -1
      src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickExecutionIssue.kt
  3. +107
    -0
      src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickExecutionIssueService.kt
  4. +1459
    -1195
      src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt
  5. +5
    -5
      src/main/java/com/ffii/fpsms/modules/pickOrder/web/PickOrderController.kt
  6. +12
    -0
      src/main/resources/db/changelog/changes/20251023_01_enson/01_alter_table.sql

+ 75
- 45
src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoReleaseCoordinatorService.kt 查看文件

@@ -18,6 +18,7 @@ import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrderLineRepository
import com.ffii.fpsms.modules.deliveryOrder.entity.DeliveryOrderRepository import com.ffii.fpsms.modules.deliveryOrder.entity.DeliveryOrderRepository
import com.ffii.fpsms.modules.deliveryOrder.enums.DeliveryOrderStatus import com.ffii.fpsms.modules.deliveryOrder.enums.DeliveryOrderStatus
import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrderRepository import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrderRepository
import com.ffii.fpsms.modules.pickOrder.service.PickExecutionIssueService
data class BatchReleaseJobStatus( data class BatchReleaseJobStatus(
val jobId: String, val jobId: String,
val total: Int, val total: Int,
@@ -34,7 +35,8 @@ class DoReleaseCoordinatorService(
private val jdbcDao: JdbcDao, private val jdbcDao: JdbcDao,
private val doPickOrderLineRepository: DoPickOrderLineRepository, private val doPickOrderLineRepository: DoPickOrderLineRepository,
private val deliveryOrderRepository: DeliveryOrderRepository, private val deliveryOrderRepository: DeliveryOrderRepository,
private val doPickOrderRepository: DoPickOrderRepository
private val doPickOrderRepository: DoPickOrderRepository,
private val pickExecutionIssueService: PickExecutionIssueService,
) { ) {
private val poolSize = Runtime.getRuntime().availableProcessors() private val poolSize = Runtime.getRuntime().availableProcessors()
private val executor = Executors.newFixedThreadPool(min(poolSize, 4)) private val executor = Executors.newFixedThreadPool(min(poolSize, 4))
@@ -362,28 +364,32 @@ class DoReleaseCoordinatorService(
} }
fun startBatchReleaseAsync(ids: List<Long>, userId: Long): MessageResponse { fun startBatchReleaseAsync(ids: List<Long>, userId: Long): MessageResponse {
if (ids.isEmpty()) { if (ids.isEmpty()) {
return MessageResponse(id = null, code = "NO_IDS", name = null, type = null,
message = "No IDs provided", errorPosition = null, entity = null)
return MessageResponse(
id = null, code = "NO_IDS", name = null, type = null,
message = "No IDs provided", errorPosition = null, entity = null
)
} }
val jobId = UUID.randomUUID().toString() val jobId = UUID.randomUUID().toString()
val status = BatchReleaseJobStatus(jobId = jobId, total = ids.size) val status = BatchReleaseJobStatus(jobId = jobId, total = ids.size)
jobs[jobId] = status jobs[jobId] = status
executor.submit { executor.submit {
try { try {
println("📦 Starting batch release for ${ids.size} orders") println("📦 Starting batch release for ${ids.size} orders")
val sortedIds = getOrderedDeliveryOrderIds(ids) val sortedIds = getOrderedDeliveryOrderIds(ids)
println("🔍 DEBUG: Got ${sortedIds.size} sorted orders") println("🔍 DEBUG: Got ${sortedIds.size} sorted orders")
val releaseResults = mutableListOf<ReleaseDoResult>() val releaseResults = mutableListOf<ReleaseDoResult>()
// 第一步:发布所有 DO(创建 pick orders,但不创建 DoPickOrder) // 第一步:发布所有 DO(创建 pick orders,但不创建 DoPickOrder)
// 第一步:发布所有 DO(创建 pick orders,但不创建 DoPickOrder)
sortedIds.forEach { id -> sortedIds.forEach { id ->
try { try {
val deliveryOrder = deliveryOrderRepository.findByIdAndDeletedIsFalse(id) val deliveryOrder = deliveryOrderRepository.findByIdAndDeletedIsFalse(id)
if (deliveryOrder?.status == DeliveryOrderStatus.COMPLETED ||
deliveryOrder?.status == DeliveryOrderStatus.RECEIVING) {
if (deliveryOrder?.status == DeliveryOrderStatus.COMPLETED ||
deliveryOrder?.status == DeliveryOrderStatus.RECEIVING
) {
println("⏭️ DO $id is already ${deliveryOrder.status?.value}, skipping") println("⏭️ DO $id is already ${deliveryOrder.status?.value}, skipping")
return@forEach return@forEach
} }
@@ -398,27 +404,51 @@ class DoReleaseCoordinatorService(
status.failed.add(id to (e.message ?: "Exception")) status.failed.add(id to (e.message ?: "Exception"))
} }
println("❌ DO $id skipped: ${e.message}") println("❌ DO $id skipped: ${e.message}")

// ✅ 调用 PickExecutionIssueService 创建 issue 记录
try {
val issueCategory = when {
e.message?.contains("Unable to find") == true &&
e.message?.contains("Warehouse") == true ->
com.ffii.fpsms.modules.pickOrder.entity.IssueCategory.warehouse_issue

e.message?.contains("No matching truck") == true ->
com.ffii.fpsms.modules.pickOrder.entity.IssueCategory.truck_issue

else ->
com.ffii.fpsms.modules.pickOrder.entity.IssueCategory.match_issue
}

pickExecutionIssueService.createBatchReleaseIssue(
deliveryOrderId = id,
errorMessage = e.message ?: "Unknown error",
issueCategory = issueCategory
)
} catch (issueException: Exception) {
println("⚠️ Failed to create issue for DO $id: ${issueException.message}")
}
} }
}
// 第二步:按日期、楼层、店铺分组(与 SQL 逻辑一致)
val sortedResults = releaseResults.sortedWith(compareBy(
{ it.estimatedArrivalDate },
{ it.preferredFloor },
{ it.truckDepartureTime },
{ it.truckLanceCode },
{ it.loadingSequence },
{ it.shopId }
))
} // ✅ forEach 循环在这里结束


// ✅ 然后按正确的顺序分组(保持排序后的顺序)
val grouped = sortedResults.groupBy {
Triple(it.estimatedArrivalDate, it.preferredFloor, it.shopId)
}
// ✅ 第二步:按日期、楼层、店铺分组(在 forEach 之后)
val sortedResults = releaseResults.sortedWith(
compareBy(
{ it.estimatedArrivalDate },
{ it.preferredFloor },
{ it.truckDepartureTime },
{ it.truckLanceCode },
{ it.loadingSequence },
{ it.shopId }
)
)

// ✅ 然后按正确的顺序分组(保持排序后的顺序)
val grouped = sortedResults.groupBy {
Triple(it.estimatedArrivalDate, it.preferredFloor, it.shopId)
}


println("🔍 DEBUG: Grouped into ${grouped.size} DoPickOrders") println("🔍 DEBUG: Grouped into ${grouped.size} DoPickOrders")
// 第三步:为每组创建一个 DoPickOrder 和多条 DoPickOrderLine // 第三步:为每组创建一个 DoPickOrder 和多条 DoPickOrderLine
grouped.forEach { (key, group) -> grouped.forEach { (key, group) ->
try { try {
@@ -429,31 +459,31 @@ class DoReleaseCoordinatorService(
e.printStackTrace() e.printStackTrace()
} }
} }
// 第四步:更新 ticket numbers // 第四步:更新 ticket numbers
if (status.success.get() > 0) { if (status.success.get() > 0) {
println("🎫 Updating ticket numbers...") println("🎫 Updating ticket numbers...")
updateBatchTicketNumbers() updateBatchTicketNumbers()
} }
println("✅ Batch completed: ${status.success.get()} success, ${status.failed.size} failed") println("✅ Batch completed: ${status.success.get()} success, ${status.failed.size} failed")
} catch (e: Exception) {
println("❌ Batch release exception: ${e.javaClass.simpleName} - ${e.message}")
e.printStackTrace()
} finally {
status.running = false
status.finishedAt = Instant.now().toEpochMilli()
}
}
return MessageResponse(
id = null, code = "STARTED", name = null, type = null,
message = "Batch release started", errorPosition = null,
entity = mapOf("jobId" to jobId, "total" to ids.size)
)
}
} catch (e: Exception) {
println("❌ Batch release exception: ${e.javaClass.simpleName} - ${e.message}")
e.printStackTrace()
} finally {
status.running = false
status.finishedAt = Instant.now().toEpochMilli()
}
}
return MessageResponse(
id = null, code = "STARTED", name = null, type = null,
message = "Batch release started", errorPosition = null,
entity = mapOf("jobId" to jobId, "total" to ids.size)
)
}
private fun createMergedDoPickOrder(results: List<ReleaseDoResult>) { private fun createMergedDoPickOrder(results: List<ReleaseDoResult>) {
val first = results.first() val first = results.first()


+ 3
- 1
src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickExecutionIssue.kt 查看文件

@@ -144,5 +144,7 @@ enum class HandleStatus {
enum class IssueCategory { enum class IssueCategory {
lot_issue, // 正常 Issue Button 提交 lot_issue, // 正常 Issue Button 提交
resuggest_issue, // Resuggest 失败 resuggest_issue, // Resuggest 失败
match_issue // Matching 问题
match_issue ,
warehouse_issue,
truck_issue // Matching 问题
} }

+ 107
- 0
src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickExecutionIssueService.kt 查看文件

@@ -674,4 +674,111 @@ private fun handleBothMissAndBadItem(request: PickExecutionIssueRequest, missQty
println("=== End Debug ===") println("=== End Debug ===")
return result return result
} }
/**
* 为 batch release 失败的 delivery order 创建 issue 记录
*/
@Transactional(rollbackFor = [Exception::class])
open fun createBatchReleaseIssue(
deliveryOrderId: Long,
errorMessage: String,
issueCategory: IssueCategory = IssueCategory.match_issue
): Int {
try {
println("=== Creating batch release issue ===")
println("Delivery Order ID: $deliveryOrderId")
println("Error Message: $errorMessage")
println("Issue Category: $issueCategory")
// 查询 delivery order
val deliveryOrderSql = """
SELECT do.id, do.code, do.estimatedArrivalDate
FROM fpsmsdb.delivery_order do
WHERE do.id = :deliveryOrderId
AND do.deleted = 0
""".trimIndent()
val deliveryOrder = jdbcDao.queryForMap(
deliveryOrderSql,
mapOf("deliveryOrderId" to deliveryOrderId)
).orElse(null)
if (deliveryOrder == null) {
println("❌ Delivery Order not found: $deliveryOrderId")
return 0
}
// 查询 delivery order lines
val linesSql = """
SELECT dol.id, dol.itemId, i.code as itemCode, i.name as itemName, dol.qty
FROM fpsmsdb.delivery_order_line dol
INNER JOIN fpsmsdb.item i ON i.id = dol.itemId
WHERE dol.doId = :deliveryOrderId
AND dol.deleted = 0
""".trimIndent()
val lines = jdbcDao.queryForList(linesSql, mapOf("deliveryOrderId" to deliveryOrderId))
if (lines.isEmpty()) {
println("⚠️ No delivery order lines found for DO: $deliveryOrderId")
return 0
}
// 生成 issue number
val issueNo = generateIssueNo()
var createdCount = 0
// 为每个 line 创建 issue 记录
lines.forEach { line ->
val itemId = (line["itemId"] as? Number)?.toLong() ?: 0L
val itemCode = line["itemCode"] as? String
val itemName = line["itemName"] as? String
val qty = (line["qty"] as? Number)?.let { BigDecimal(it.toString()) } ?: BigDecimal.ZERO
val issue = PickExecutionIssue(
id = null,
pickOrderId = 0L, // batch release 失败时可能还没有 pick order
pickOrderCode = deliveryOrder["code"] as? String ?: "",
pickOrderCreateDate = LocalDate.now(),
pickExecutionDate = LocalDate.now(),
pickOrderLineId = 0L, // batch release 失败时可能还没有 pick order line
issueNo = issueNo,
joPickOrderId = null,
doPickOrderId = null,
issueCategory = issueCategory,
itemId = itemId,
itemCode = itemCode,
itemDescription = itemName,
lotId = null,
lotNo = null,
storeLocation = null,
requiredQty = qty,
actualPickQty = BigDecimal.ZERO,
missQty = qty,
badItemQty = BigDecimal.ZERO,
issueRemark = "Batch release failed: $errorMessage",
pickerName = null,
handleStatus = HandleStatus.pending,
handleDate = null,
handledBy = null,
created = LocalDateTime.now(),
createdBy = "SYSTEM",
version = 0,
modified = LocalDateTime.now(),
modifiedBy = "SYSTEM",
deleted = false
)
pickExecutionIssueRepository.save(issue)
createdCount++
}
println("✅ Created $createdCount ${issueCategory.name} issues for DO $deliveryOrderId")
return createdCount
} catch (e: Exception) {
println("❌ Failed to create batch release issue: ${e.message}")
e.printStackTrace()
return 0
}
}
} }

+ 1459
- 1195
src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt
文件差异内容过多而无法显示
查看文件


+ 5
- 5
src/main/java/com/ffii/fpsms/modules/pickOrder/web/PickOrderController.kt 查看文件

@@ -161,7 +161,10 @@ class PickOrderController(
fun getPickOrderLineLotDetails(@PathVariable pickOrderLineId: Long): List<Map<String, Any>> { fun getPickOrderLineLotDetails(@PathVariable pickOrderLineId: Long): List<Map<String, Any>> {
return pickOrderService.getPickOrderLineLotDetails(pickOrderLineId); return pickOrderService.getPickOrderLineLotDetails(pickOrderLineId);
} }

@GetMapping("/lot-details-by-do-pick-order-record/{doPickOrderRecordId}")
fun getLotDetailsByDoPickOrderRecordId(@PathVariable doPickOrderRecordId: Long): Map<String, Any?> {
return pickOrderService.getLotDetailsByDoPickOrderRecordId(doPickOrderRecordId)
}


@PostMapping("/groups") @PostMapping("/groups")
fun createGroup(@RequestBody request: Map<String, Any>): ResponseEntity<Map<String, Any>> { fun createGroup(@RequestBody request: Map<String, Any>): ResponseEntity<Map<String, Any>> {
@@ -297,9 +300,6 @@ fun getCompletedDoPickOrders(
): List<Map<String, Any?>> { ): List<Map<String, Any?>> {
return pickOrderService.getCompletedDoPickOrders(userId, pickOrderCode, shopName, deliveryNo, ticketNo) return pickOrderService.getCompletedDoPickOrders(userId, pickOrderCode, shopName, deliveryNo, ticketNo)
} }
@GetMapping("/lot-details-by-pick-order/{pickOrderId}")
fun getLotDetailsByPickOrderId(@PathVariable pickOrderId: Long): List<Map<String, Any>> {
return pickOrderService.getLotDetailsByPickOrderId(pickOrderId);
}



} }

+ 12
- 0
src/main/resources/db/changelog/changes/20251023_01_enson/01_alter_table.sql 查看文件

@@ -0,0 +1,12 @@
-- liquibase formatted sql
-- changeset enson:alter_pick_execution_issue_category_20251023

ALTER TABLE pick_execution_issue
MODIFY COLUMN issue_category ENUM(
'lot_issue',
'resuggest_issue',
'match_issue',
'warehouse_issue',
'truck_issue'
) NOT NULL;


正在加载...
取消
保存