Fai Luk 6 часов назад
Родитель
Сommit
bc7d646421
5 измененных файлов: 46 добавлений и 19 удалений
  1. +3
    -2
      src/main/java/com/ffii/fpsms/m18/entity/M18GoodsReceiptNoteLogRepository.kt
  2. +31
    -8
      src/main/java/com/ffii/fpsms/m18/service/M18GrnCodeSyncService.kt
  3. +9
    -7
      src/main/java/com/ffii/fpsms/modules/common/scheduler/service/SchedulerService.kt
  4. +1
    -1
      src/main/resources/application-prod.yml
  5. +2
    -1
      src/main/resources/application.yml

+ 3
- 2
src/main/java/com/ffii/fpsms/m18/entity/M18GoodsReceiptNoteLogRepository.kt Просмотреть файл

@@ -11,7 +11,8 @@ interface M18GoodsReceiptNoteLogRepository : AbstractRepository<M18GoodsReceiptN
fun existsByPurchaseOrderIdAndStatusTrue(purchaseOrderId: Long): Boolean

/**
* GRN log rows that need M18 AN code backfill: have record id, no grn_code yet, created in [start, end).
* GRN log rows that need M18 AN code backfill: have record id, no grn_code yet,
* created in [start, end] inclusive (e.g. start = 4 days ago 00:00, end = now).
*/
@Query(
"""
@@ -19,7 +20,7 @@ interface M18GoodsReceiptNoteLogRepository : AbstractRepository<M18GoodsReceiptN
WHERE l.deleted = false
AND l.m18RecordId IS NOT NULL
AND (l.grnCode IS NULL OR l.grnCode = '')
AND l.created >= :start AND l.created < :end
AND l.created >= :start AND l.created <= :end
"""
)
fun findNeedingGrnCode(


+ 31
- 8
src/main/java/com/ffii/fpsms/m18/service/M18GrnCodeSyncService.kt Просмотреть файл

@@ -5,7 +5,9 @@ import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.ZonedDateTime

/**
* Fills [com.ffii.fpsms.m18.entity.M18GoodsReceiptNoteLog.grnCode] by calling M18 GET /root/api/read/an?id=...
@@ -18,16 +20,37 @@ open class M18GrnCodeSyncService(
private val logger = LoggerFactory.getLogger(M18GrnCodeSyncService::class.java)

/**
* For today's [created] window, backfill grn_code for rows with m18_record_id but empty grn_code.
* Backfill `grn_code` for rows created in **[start of calendar day (today − syncOffsetDays), now]** (inclusive),
* with `m18_record_id` set and `grn_code` empty.
*
* Example: `syncOffsetDays = 4` → `created` from 4 days ago 00:00:00 up to **now**.
* Example: `syncOffsetDays = 0` → `created` from **today** 00:00:00 up to **now**.
*
* One M18 read per distinct [m18_record_id]; all matching log rows get the same code.
*/
@Transactional
open fun syncGrnCodesForLookback(syncOffsetDays: Int, zoneId: ZoneId = ZoneId.systemDefault()): Int {
val days = syncOffsetDays.coerceAtLeast(0)
val nowZ = ZonedDateTime.now(zoneId)
val startLdt = nowZ.toLocalDate().minusDays(days.toLong()).atStartOfDay(zoneId).toLocalDateTime()
val endLdt = nowZ.toLocalDateTime()
return syncGrnCodesInWindow(startLdt, endLdt, "lookback syncOffsetDays=$days")
}

/**
* Single calendar day window: [day 00:00, next day 00:00) — useful for manual/testing.
*/
@Transactional
open fun syncGrnCodesForDate(day: LocalDate, zoneId: ZoneId = ZoneId.systemDefault()): Int {
val start = day.atStartOfDay(zoneId).toLocalDateTime()
val end = day.plusDays(1).atStartOfDay(zoneId).toLocalDateTime()
val rows = m18GoodsReceiptNoteLogRepository.findNeedingGrnCode(start, end)
val startLdt = day.atStartOfDay(zoneId).toLocalDateTime()
val endLdt = day.plusDays(1).atStartOfDay(zoneId).toLocalDateTime().minusNanos(1)
return syncGrnCodesInWindow(startLdt, endLdt, "single day=$day")
}

private fun syncGrnCodesInWindow(startLdt: LocalDateTime, endLdt: LocalDateTime, label: String): Int {
val rows = m18GoodsReceiptNoteLogRepository.findNeedingGrnCode(startLdt, endLdt)
if (rows.isEmpty()) {
logger.info("[M18GrnCodeSync] No GRN log rows need grn_code for day=$day")
logger.info("[M18GrnCodeSync] No GRN log rows need grn_code ($label, window=[$startLdt, $endLdt])")
return 0
}
val byRecord = rows.groupBy { it.m18RecordId }
@@ -36,7 +59,7 @@ open class M18GrnCodeSyncService(
val rid = recordId ?: continue
val code = m18GoodsReceiptNoteService.readAnCodeByRecordId(rid)
if (code.isNullOrBlank()) {
logger.warn("[M18GrnCodeSync] No code from M18 for m18_record_id=$rid (day=$day)")
logger.warn("[M18GrnCodeSync] No code from M18 for m18_record_id=$rid ($label)")
continue
}
for (log in list) {
@@ -49,6 +72,6 @@ open class M18GrnCodeSyncService(
return updated
}

/** Convenience: sync for calendar "today" in the default timezone. */
open fun syncGrnCodesForToday(): Int = syncGrnCodesForDate(LocalDate.now())
/** Convenience: lookback 0 days = from start of today to now. */
open fun syncGrnCodesForToday(): Int = syncGrnCodesForLookback(0)
}

+ 9
- 7
src/main/java/com/ffii/fpsms/modules/common/scheduler/service/SchedulerService.kt Просмотреть файл

@@ -32,7 +32,10 @@ open class SchedulerService(
@Value("\${scheduler.postCompletedDnGrn.enabled:true}") val postCompletedDnGrnEnabled: Boolean,
@Value("\${scheduler.postCompletedDnGrn.receiptDate:}") val postCompletedDnGrnReceiptDate: String,
@Value("\${scheduler.grnCodeSync.enabled:true}") val grnCodeSyncEnabled: Boolean,
/** 0 = calendar day of run; 1 = previous day (typical for 1:20 AM job to backfill yesterday's GRNs). */
/**
* Lookback window for GRN code sync: rows with `created` from **start of (today − N days)** through **now**,
* missing `grn_code`. Example: 4 = from 4 days ago 00:00 to now.
*/
@Value("\${scheduler.grnCodeSync.syncOffsetDays:0}") val grnCodeSyncSyncOffsetDays: Int,
val settingsService: SettingsService,
val taskScheduler: TaskScheduler,
@@ -149,7 +152,7 @@ open class SchedulerService(
else try { LocalDate.parse(s) } catch (e: Exception) { LocalDate.now().minusDays(1) }
}

/** Backfill grn_code from M18 read/an for today's logs missing code. Default 1:20 AM daily. Set scheduler.grnCodeSync.enabled=false to disable. */
/** Backfill grn_code from M18 read/an for logs in [today−syncOffsetDays 00:00, now] missing code. Set scheduler.grnCodeSync.enabled=false to disable. */
fun scheduleGrnCodeSync() {
if (!grnCodeSyncEnabled) {
scheduledGrnCodeSync?.cancel(false)
@@ -160,16 +163,15 @@ open class SchedulerService(
commonSchedule(
scheduledGrnCodeSync,
SettingNames.SCHEDULE_GRN_CODE_SYNC,
"0 29 1 * * *",
"0 10 1 * * *",
{ syncGrnCodesFromM18() },
)
}

open fun syncGrnCodesFromM18() {
try {
val day = java.time.LocalDate.now().minusDays(grnCodeSyncSyncOffsetDays.toLong().coerceAtLeast(0))
val updated = m18GrnCodeSyncService.syncGrnCodesForDate(day)
logger.info("Scheduler - M18 GRN code sync done for date=$day, rows updated=$updated")
val updated = m18GrnCodeSyncService.syncGrnCodesForLookback(grnCodeSyncSyncOffsetDays)
logger.info("Scheduler - M18 GRN code sync done (syncOffsetDays=${grnCodeSyncSyncOffsetDays}), rows updated=$updated")
} catch (e: Exception) {
logger.error("Scheduler - M18 GRN code sync failed: ${e.message}", e)
}
@@ -186,7 +188,7 @@ open class SchedulerService(
commonSchedule(
scheduledPostCompletedDnGrn,
SettingNames.SCHEDULE_POST_COMPLETED_DN_GRN,
"0 57 0 * * *",
"0 1 0 * * *",
{ getPostCompletedDnAndProcessGrn(receiptDate = getPostCompletedDnGrnReceiptDate()) }
)
}


+ 1
- 1
src/main/resources/application-prod.yml Просмотреть файл

@@ -24,7 +24,7 @@ scheduler:
enabled: true
grnCodeSync:
enabled: true
syncOffsetDays: 0 # 0 = m18_goods_receipt_note_log rows created today; use 1 for "yesterday" if you prefer night backfill
syncOffsetDays: 10 # from (today − 10) 00:00 to now, rows missing grn_code

m18:
config:


+ 2
- 1
src/main/resources/application.yml Просмотреть файл

@@ -17,7 +17,8 @@ scheduler:
# receiptDate: # leave unset for production (uses yesterday)
grnCodeSync:
enabled: false # set true in prod; backfills grn_code from M18 GET /root/api/read/an
syncOffsetDays: 0 # 0=today, 1=yesterday (use 1 for 1:20 AM job to backfill prior day)
# Lookback: created from start of (today − N days) through now, missing grn_code. E.g. 4 = last 4 days + today.
syncOffsetDays: 0

spring:
servlet:


Загрузка…
Отмена
Сохранить