package com.klp.da.task; import com.alibaba.fastjson2.JSON; import com.klp.pocket.acid.domain.vo.AcidOeeDailySummaryVo; import com.klp.pocket.acid.service.IAcidOeeService; import lombok.Data; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.List; import java.util.concurrent.TimeUnit; /** * 酸轧线 OEE 当月预计算任务 * * 需求对应 docs/oee-report-design.md 第 12.2 节: * - 项目启动完成后即计算当月 OEE 聚合结果并写入 Redis; * - 每天凌晨 04:00 重新计算当月数据并覆盖缓存。 * * 当前仅实现酸轧线(SY)的当月日汇总预计算; * key 约定: * - 汇总结果:oee:report:month:summary:{yyyyMM}:SY * - 元信息: oee:report:month:meta:{yyyyMM}:SY */ @Slf4j @RequiredArgsConstructor @Component public class AcidOeeMonthTask { /** Redis 缓存 key 模板:当月 OEE 汇总(酸轧线) */ private static final String SUMMARY_KEY_PATTERN = "oee:report:month:summary:%s:SY"; /** Redis 缓存 key 模板:当月元信息(酸轧线) */ private static final String META_KEY_PATTERN = "oee:report:month:meta:%s:SY"; private static final DateTimeFormatter YEAR_MONTH_FMT = DateTimeFormatter.ofPattern("yyyyMM"); private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ISO_DATE; private static final DateTimeFormatter DATE_TIME_FMT = DateTimeFormatter.ISO_LOCAL_DATE_TIME; private final IAcidOeeService acidOeeService; private final StringRedisTemplate stringRedisTemplate; /** * 项目启动完成后立即计算一次当月酸轧 OEE 汇总并写入 Redis。 */ @PostConstruct public void init() { try { computeCurrentMonth("startup"); } catch (Exception e) { log.error("[AcidOeeMonthTask] startup compute failed", e); } } /** * 每天凌晨 04:00 重新计算当月酸轧 OEE 汇总并覆盖 Redis 缓存。 */ @Scheduled(cron = "0 0 4 * * ?") public void scheduleDaily() { try { computeCurrentMonth("schedule-04"); } catch (Exception e) { log.error("[AcidOeeMonthTask] 4am compute failed", e); } } /** * 计算当前月份(从当月1号到今天)的酸轧 OEE 日汇总,并写入 Redis。 * * @param trigger 触发来源标记(startup / schedule-04 等) */ private void computeCurrentMonth(String trigger) { long startNs = System.nanoTime(); LocalDate now = LocalDate.now(); String yyyyMM = now.format(YEAR_MONTH_FMT); LocalDate startDate = now.withDayOfMonth(1); LocalDate endDate = now; String startStr = startDate.format(DATE_FMT); String endStr = endDate.format(DATE_FMT); log.info("[AcidOeeMonthTask] trigger={}, computing acid OEE month summary for {} ({} ~ {})", trigger, yyyyMM, startStr, endStr); // 1. 调用 pocket 的 AcidOeeService 获取当月日汇总 List dailySummaryList = acidOeeService.getDailySummary(startStr, endStr); // 2. 写入 Redis(summary) String summaryKey = String.format(SUMMARY_KEY_PATTERN, yyyyMM); String summaryJson = JSON.toJSONString(dailySummaryList); stringRedisTemplate.opsForValue().set(summaryKey, summaryJson, 1, TimeUnit.DAYS); long durationMs = (System.nanoTime() - startNs) / 1_000_000L; // 3. 写入 Redis(meta) Meta meta = new Meta(); meta.setComputedAt(LocalDateTime.now().format(DATE_TIME_FMT)); meta.setDurationMs(durationMs); meta.setStartDate(startStr); meta.setEndDate(endStr); meta.setTrigger(trigger); String metaKey = String.format(META_KEY_PATTERN, yyyyMM); stringRedisTemplate.opsForValue().set(metaKey, JSON.toJSONString(meta), 1, TimeUnit.DAYS); log.info("[AcidOeeMonthTask] compute finish for {} dailySize={}, durationMs={}ms, summaryKey={}", yyyyMM, dailySummaryList.size(), durationMs, summaryKey); } /** * 当月预计算元信息 */ @Data private static class Meta { /** 计算完成时间(ISO-8601 字符串) */ private String computedAt; /** 计算耗时(毫秒) */ private long durationMs; /** 统计起始日期(yyyy-MM-dd) */ private String startDate; /** 统计结束日期(yyyy-MM-dd) */ private String endDate; /** 触发来源(startup / schedule-04 等) */ private String trigger; } }