package jnpf.dmsService.expiry; import jnpf.dmsEntity.storage.expiry.DmsFileExpiryItem; import jnpf.dmsMapper.DmsFileExpiryMapper; import jnpf.dmsService.search.DmsSearchProperties; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import java.util.List; @Slf4j @Service @EnableScheduling @RequiredArgsConstructor public class DmsFileExpiryJob { private final DmsFileExpiryProperties properties; private final DmsFileExpiryMapper mapper; private final DmsFileExpiryExecutor executor; private final DmsFileExpirySearchCleanup cleanup; private final DmsSearchProperties search; @Scheduled(cron = "${dms.expiry.cron:0 * * * * ?}") @jnpf.audit.sdk.annotation.AuditLog(action = "DMS_FILE_EXPIRY_SCAN", bizCodeExpr = "''", recordSuccess = false) public Result run() { int limit = Math.max(1, Math.min(1000, properties.getBatchSize())); int scanned = 0, applied = 0, skipped = 0, failed = 0; long oldestLagSeconds = 0; DmsFileExpiryItem after = null; for (int page = 0; page < Math.max(1, properties.getMaxPages()); page++) { List items = mapper.candidates(after, limit); for (DmsFileExpiryItem item : items) { scanned++; oldestLagSeconds = Math.max(oldestLagSeconds, Math.max(0, (System.currentTimeMillis() - item.getEffectiveTo().getTime()) / 1000)); try { if (executor.process(item)) applied++; else skipped++; } catch (RuntimeException error) { failed++; log.error("DMS expiry failed; retry next scan tenant={} version={}", item.getTenantId(), item.getFileVersionId(), error); } } if (items.size() < limit) break; after = items.get(items.size() - 1); // Advance over failures; a poison row cannot starve this scan. } log.info("DMS expiry scanned={} applied={} skipped={} failed={} oldestLagSeconds={}", scanned, applied, skipped, failed, oldestLagSeconds); if (search.isEnabled()) { for (DmsFileExpiryItem item : mapper.cleanupCandidates(limit)) { try { cleanup.process(item); } catch (RuntimeException error) { log.error("DMS expiry cleanup retry tenant={} version={}", item.getTenantId(), item.getFileVersionId(), error); } } } return new Result(scanned, applied, skipped, failed, oldestLagSeconds); } @Getter public static final class Result { private final int scanned; private final int applied; private final int skipped; private final int failed; private final long oldestLagSeconds; public Result(int scanned, int applied, int skipped, int failed, long oldestLagSeconds) { this.scanned = scanned; this.applied = applied; this.skipped = skipped; this.failed = failed; this.oldestLagSeconds = oldestLagSeconds; } } }