diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockTakeRecord.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockTakeRecord.kt index 9258f59..7e6608e 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockTakeRecord.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockTakeRecord.kt @@ -27,6 +27,9 @@ open class StockTakeRecord : BaseEntity() { @ManyToOne @JoinColumn(name = "stockTakeId", nullable = false) open var stockTake: StockTake? = null + + @Column(name = "stockTakeRoundId") + open var stockTakeRoundId: Long? = null @Column(name = "approverId") open var approverId: Long? = null diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockTakeRecordRepository.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockTakeRecordRepository.kt index 36397a6..4f78083 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockTakeRecordRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockTakeRecordRepository.kt @@ -11,6 +11,7 @@ import java.time.LocalDate interface StockTakeRecordRepository : AbstractRepository { fun findAllByStockTakeIdAndDeletedIsFalse(stockTakeId: Long): List; fun findAllByStockTakeIdInAndDeletedIsFalse(stockTakeIds: Collection): List; + fun findAllByStockTakeRoundIdAndDeletedIsFalse(stockTakeRoundId: Long): List; fun findByIdAndDeletedIsFalse(id: Serializable): StockTakeRecord?; @Query(""" 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 e0efb04..9050b2b 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 @@ -230,19 +230,25 @@ class StockTakeRecordService( println("getApproverInventoryLotDetailsAll called with stockTakeId: $stockTakeId, pageNum: $pageNum, pageSize: $pageSize") // 3. 如果传了 stockTakeId,就把「同一轮」的所有 stockTake 找出来(stockTakeRoundId,舊資料則 planStart) - val roundStockTakeIds: Set = if (stockTakeId != null) { + // stockTakeId != null 时,优先用 stocktakerecord.stockTakeRoundId 取整轮记录(更快) + // 如果该轮因为旧数据尚未写入 roundId 导致取不到,再 fallback 到旧逻辑(根据 stock_take 的 planStart / stockTakeRoundId 求 stockTakeId 列表) + val (roundStockTakeIds, roundStockTakeRecords) = if (stockTakeId != null) { val baseStockTake = stockTakeRepository.findByIdAndDeletedIsFalse(stockTakeId) ?: throw IllegalArgumentException("Stock take not found: $stockTakeId") - resolveRoundStockTakeIds(baseStockTake) - } else { - emptySet() - } - // 4. 如果有 stockTakeId,则预先把这一轮相关的 stockTakeRecord 查出来建 map(避免全表扫描 + N^2) - val roundStockTakeRecords = if (stockTakeId != null && roundStockTakeIds.isNotEmpty()) { - stockTakeRecordRepository.findAllByStockTakeIdInAndDeletedIsFalse(roundStockTakeIds) + val roundId = baseStockTake.stockTakeRoundId ?: baseStockTake.id + val recordsByRound = stockTakeRecordRepository.findAllByStockTakeRoundIdAndDeletedIsFalse(roundId) + + if (recordsByRound.isNotEmpty()) { + val ids = recordsByRound.mapNotNull { it.stockTake?.id }.toSet() + ids to recordsByRound + } else { + val ids = resolveRoundStockTakeIds(baseStockTake) + val records = if (ids.isNotEmpty()) stockTakeRecordRepository.findAllByStockTakeIdInAndDeletedIsFalse(ids) else emptyList() + ids to records + } } else { - emptyList() + emptySet() to emptyList() } val stockTakeRecordsMap = roundStockTakeRecords .groupBy { Pair(it.lotId ?: 0L, it.warehouse?.id ?: 0L) } @@ -413,13 +419,19 @@ class StockTakeRecordService( .maxByOrNull { it.actualStart ?: it.planStart ?: LocalDateTime.MIN } ?: return emptyList() - val roundStockTakeIds = resolveRoundStockTakeIds(latestBaseStockTake) - if (roundStockTakeIds.isEmpty()) return emptyList() - - // stockTakeRecord 存在性:用来对齐 details 接口的 `stockTakeRecordId != null` - val roundStockTakeRecords = stockTakeRecordRepository.findAllByStockTakeIdInAndDeletedIsFalse(roundStockTakeIds) + // 优先用 stocktakerecord.stockTakeRoundId 直接取该轮次记录(避免再算 stockTakeId 列表 + in 查询) + val roundId = latestBaseStockTake.stockTakeRoundId ?: latestBaseStockTake.id + var roundStockTakeRecords = stockTakeRecordRepository.findAllByStockTakeRoundIdAndDeletedIsFalse(roundId) .filter { it.warehouse?.id in warehouseIds } + // 兼容旧数据:如果该轮次 roundId 为空/未补写,则 fallback 到旧逻辑 + if (roundStockTakeRecords.isEmpty()) { + val roundStockTakeIds = resolveRoundStockTakeIds(latestBaseStockTake) + if (roundStockTakeIds.isEmpty()) return emptyList() + roundStockTakeRecords = stockTakeRecordRepository.findAllByStockTakeIdInAndDeletedIsFalse(roundStockTakeIds) + .filter { it.warehouse?.id in warehouseIds } + } + val recordKeySet = roundStockTakeRecords.mapNotNull { r -> val lotId = r.lotId val whId = r.warehouse?.id @@ -476,6 +488,40 @@ class StockTakeRecordService( ) } + /** + * 轻量版:只返回 Approver 最新一轮的基本信息,不做 totalItem/totalLot 重计算。 + * 用于前端先拿 stockTakeId,再调用 pending/approved 明细接口。 + */ + open fun getLatestApproverStockTakeHeader(): AllPickedStockTakeListReponse? { + val latestBaseStockTake = stockTakeRepository.findAll() + .filter { !it.deleted } + .maxByOrNull { it.actualStart ?: it.planStart ?: LocalDateTime.MIN } + ?: return null + + val statusValue = latestBaseStockTake.status?.let { st -> + if (st == StockTakeStatus.APPROVING || st == StockTakeStatus.COMPLETED) st.value else "" + } ?: "" + + return AllPickedStockTakeListReponse( + id = 1L, + stockTakeSession = "", + lastStockTakeDate = latestBaseStockTake.actualStart?.toLocalDate(), + status = statusValue, + currentStockTakeItemNumber = 0, + totalInventoryLotNumber = 0, + stockTakeId = latestBaseStockTake.id ?: 0, + stockTakeRoundId = latestBaseStockTake.stockTakeRoundId ?: latestBaseStockTake.id, + stockTakerName = null, + approverName = null, + TotalItemNumber = 0, + startTime = latestBaseStockTake.actualStart, + endTime = latestBaseStockTake.actualEnd, + ReStockTakeTrueFalse = false, + planStartDate = latestBaseStockTake.planStart?.toLocalDate(), + stockTakeSectionDescription = null + ) + } + open fun getInventoryLotDetailsByWarehouseCode(warehouseCode: String): List { println("getInventoryLotDetailsByWarehouseCode called with code: $warehouseCode") @@ -690,6 +736,8 @@ return RecordsRes(paginatedResult, filteredResults.size) val varianceQty = availableQty - request.qty - request.badQty // 更新字段(第二次盘点) existingRecord.apply { + // 兼容旧数据:如果之前没写 round id,则补写 + this.stockTakeRoundId = this.stockTakeRoundId ?: (stockTake.stockTakeRoundId ?: stockTake.id) this.pickerSecondStockTakeQty = request.qty this.pickerSecondBadQty = request.badQty // 更新 badQty this.status = "pass" @@ -711,6 +759,7 @@ return RecordsRes(paginatedResult, filteredResults.size) this.lotId = inventoryLot.id this.warehouse = warehouse this.stockTake = stockTake + this.stockTakeRoundId = stockTake.stockTakeRoundId ?: stockTake.id this.stockTakeSection = warehouse.stockTakeSection this.inventoryLotId = inventoryLot.id this.stockTakerId = stockTakerId @@ -853,6 +902,7 @@ return RecordsRes(paginatedResult, filteredResults.size) this.lotId = inventoryLot.id this.warehouse = warehouse this.stockTake = stockTake + this.stockTakeRoundId = stockTake.stockTakeRoundId ?: stockTake.id this.stockTakeSection = request.stockTakeSection this.inventoryLotId = inventoryLot.id this.stockTakerId = request.stockTakerId diff --git a/src/main/java/com/ffii/fpsms/modules/stock/web/StockTakeRecordController.kt b/src/main/java/com/ffii/fpsms/modules/stock/web/StockTakeRecordController.kt index d7545d3..76a0cdc 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/web/StockTakeRecordController.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/web/StockTakeRecordController.kt @@ -94,6 +94,12 @@ class StockTakeRecordController( fun AllApproverStockTakeList(): List { return stockOutRecordService.AllApproverStockTakeList() } + @GetMapping("/LatestApproverStockTakeHeader") + fun latestApproverStockTakeHeader(): ResponseEntity { + val header = stockOutRecordService.getLatestApproverStockTakeHeader() + ?: return ResponseEntity.notFound().build() + return ResponseEntity.ok(header) + } @GetMapping("/inventoryLotDetailsBySectionNotMatch") fun getInventoryLotDetailsByStockTakeSectionNotMatch( @RequestParam stockTakeSection: String, diff --git a/src/main/resources/db/changelog/changes/20260323_04_Enson/01_alter_stock_take.sql b/src/main/resources/db/changelog/changes/20260323_04_Enson/01_alter_stock_take.sql new file mode 100644 index 0000000..a922aa3 --- /dev/null +++ b/src/main/resources/db/changelog/changes/20260323_04_Enson/01_alter_stock_take.sql @@ -0,0 +1,5 @@ +-- liquibase formatted sql +-- changeset Enson:alter_stocktakerecord_stockTakeRoundId + +ALTER TABLE `fpsmsdb`.`stocktakerecord` +ADD COLUMN `stockTakeRoundId` INT NULL AFTER `stockTakeId`; diff --git a/src/main/resources/db/changelog/changes/20260323_04_Enson/02_add_stock_take_round_indexes.sql b/src/main/resources/db/changelog/changes/20260323_04_Enson/02_add_stock_take_round_indexes.sql new file mode 100644 index 0000000..df833a3 --- /dev/null +++ b/src/main/resources/db/changelog/changes/20260323_04_Enson/02_add_stock_take_round_indexes.sql @@ -0,0 +1,18 @@ +-- liquibase formatted sql +-- changeset Enson:add_stock_take_round_indexes + +-- 1) stocktakerecord:优先按“同一轮次”取记录(避免再用 stockTakeId 列表 + in) +ALTER TABLE `fpsmsdb`.`stocktakerecord` +ADD INDEX `idx_stocktakerecord_round_deleted_wh_lot` +(`stockTakeRoundId`, `deleted`, `warehouseId`, `inventoryLotId`); + +-- 2) stock_take_line:给 (stockTakeId, inventoryLotLineId) 的 join / map 提速 +ALTER TABLE `fpsmsdb`.`stock_take_line` +ADD INDEX `idx_stocktakeline_stockTakeId_lineId_deleted` +(`stockTakeId`, `inventoryLotLineId`, `deleted`); + +-- 3) inventory_lot_line:给 (warehouseId, inventoryLotId) 的库存行筛选提速 +ALTER TABLE `fpsmsdb`.`inventory_lot_line` +ADD INDEX `idx_inventorylotline_wh_lot_deleted` +(`warehouseId`, `inventoryLotId`, `deleted`); +