|
|
|
@@ -1,7 +1,9 @@ |
|
|
|
package com.ffii.fpsms.modules.common.alert |
|
|
|
|
|
|
|
import com.ffii.fpsms.m18.entity.M18DataLogRepository |
|
|
|
import com.ffii.fpsms.m18.entity.SchedulerSyncLog |
|
|
|
import com.ffii.fpsms.m18.entity.SchedulerSyncLogRepository |
|
|
|
import com.ffii.fpsms.m18.enums.M18DataLogStatus |
|
|
|
import com.ffii.fpsms.modules.common.SettingNames |
|
|
|
import com.ffii.fpsms.modules.common.scheduler.service.SchedulerService |
|
|
|
import com.ffii.fpsms.modules.settings.entity.Settings |
|
|
|
@@ -24,6 +26,7 @@ import kotlin.jvm.optionals.getOrNull |
|
|
|
* |
|
|
|
* - **DO1**: low volume (< min records), FAILED status, zero records, line failures. |
|
|
|
* - **PO / DO2 / master-data**: no SUCCESS log by schedule + grace window. |
|
|
|
* - **PO_LINE**: [m18_data_log] purchase order line FAIL rows (one email per PO code per day). |
|
|
|
*/ |
|
|
|
@Service |
|
|
|
open class SchedulerSyncAlertService( |
|
|
|
@@ -31,23 +34,36 @@ open class SchedulerSyncAlertService( |
|
|
|
private val smsSender: SmsSender, |
|
|
|
private val syncAlertEmailSender: SyncAlertEmailSender, |
|
|
|
private val schedulerSyncLogRepository: SchedulerSyncLogRepository, |
|
|
|
private val m18DataLogRepository: M18DataLogRepository, |
|
|
|
private val settingsService: SettingsService, |
|
|
|
private val webClientBuilder: WebClient.Builder, |
|
|
|
@Value("\${scheduler.m18Sync.enabled:false}") private val m18SyncEnabled: Boolean, |
|
|
|
) { |
|
|
|
private val logger = LoggerFactory.getLogger(SchedulerSyncAlertService::class.java) |
|
|
|
|
|
|
|
private companion object { |
|
|
|
const val PO_LINE_REF_TYPE = "Purchase Order Line" |
|
|
|
const val PO_LINE_ALERT_JOB = "PO_LINE" |
|
|
|
} |
|
|
|
|
|
|
|
/** Master-data sub-jobs written by [SchedulerService.getM18MasterData]. */ |
|
|
|
private val masterDataSyncTypes = |
|
|
|
listOf("Units", "Products", "Vendors", "BusinessUnits", "Currencies") |
|
|
|
|
|
|
|
open fun runChecks(now: LocalDateTime = LocalDateTime.now()): List<String> { |
|
|
|
val alerts = mutableListOf<Triple<String, String, String>>() |
|
|
|
alerts += checkPoLineFailures(now) |
|
|
|
|
|
|
|
if (!m18SyncEnabled) { |
|
|
|
logger.debug("Sync alert skipped (scheduler.m18Sync.enabled=false)") |
|
|
|
return emptyList() |
|
|
|
for (message in alerts) { |
|
|
|
sendAlert(message.first, message.second, message.third) |
|
|
|
} |
|
|
|
if (alerts.isEmpty()) { |
|
|
|
logger.debug("Sync alert skipped (scheduler.m18Sync.enabled=false)") |
|
|
|
} |
|
|
|
return alerts.map { it.third } |
|
|
|
} |
|
|
|
|
|
|
|
val alerts = mutableListOf<Triple<String, String, String>>() |
|
|
|
alerts += checkDo1(now) |
|
|
|
alerts += checkPresenceSync("PO", SettingNames.SCHEDULE_M18_PO, "0 0 2 * * *", now) |
|
|
|
alerts += checkPresenceSync("DO2", SettingNames.SCHEDULE_M18_DO2, SchedulerService.DO2_DEFAULT_CRON, now) |
|
|
|
@@ -59,6 +75,45 @@ open class SchedulerSyncAlertService( |
|
|
|
return alerts.map { it.third } |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Email when M18 PO line sync wrote FAIL rows to [m18_data_log] today. |
|
|
|
* Dedupes via [sendAlert] using job [PO_LINE_ALERT_JOB] and PO code (one email per PO per day). |
|
|
|
*/ |
|
|
|
private fun checkPoLineFailures(now: LocalDateTime): List<Triple<String, String, String>> { |
|
|
|
if (!properties.poLine.enabled) { |
|
|
|
return emptyList() |
|
|
|
} |
|
|
|
|
|
|
|
val dayStart = now.toLocalDate().atStartOfDay() |
|
|
|
val logs = |
|
|
|
m18DataLogRepository.findAllByRefTypeAndStatusAndDeletedIsFalseAndCreatedGreaterThanEqualOrderByIdAsc( |
|
|
|
PO_LINE_REF_TYPE, |
|
|
|
M18DataLogStatus.FAIL, |
|
|
|
dayStart, |
|
|
|
) |
|
|
|
if (logs.isEmpty()) { |
|
|
|
return emptyList() |
|
|
|
} |
|
|
|
|
|
|
|
val bulletsByPo = linkedMapOf<String, LinkedHashSet<String>>() |
|
|
|
for (log in logs) { |
|
|
|
val raw = log.dataLog?.get("Exception Message")?.toString() |
|
|
|
val parsed = PoLineFailureAlertSupport.parseExceptionMessage(raw) ?: continue |
|
|
|
bulletsByPo.getOrPut(parsed.first) { LinkedHashSet() }.add(parsed.second) |
|
|
|
} |
|
|
|
if (bulletsByPo.isEmpty()) { |
|
|
|
return emptyList() |
|
|
|
} |
|
|
|
|
|
|
|
return bulletsByPo.map { (poCode, bullets) -> |
|
|
|
Triple( |
|
|
|
PO_LINE_ALERT_JOB, |
|
|
|
poCode, |
|
|
|
PoLineFailureAlertSupport.buildEmailBody(poCode, bullets), |
|
|
|
) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private fun checkDo1(now: LocalDateTime): List<Triple<String, String, String>> { |
|
|
|
val runDate = now.toLocalDate() |
|
|
|
val scheduledTime = resolveDo1ScheduledTime(runDate) ?: return emptyList() |
|
|
|
|