diff --git a/klp-admin/src/main/resources/application.yml b/klp-admin/src/main/resources/application.yml index 5121d0e8..89dadbaa 100644 --- a/klp-admin/src/main/resources/application.yml +++ b/klp-admin/src/main/resources/application.yml @@ -337,3 +337,9 @@ stamp: base-url: http://python-stamp-service.example.com # 替换为实际地址 api-key: changeme # 替换为实际鉴权信息 timeout-ms: 5000 # 可按需调整 + +# OEE配置 +oee: + acid: + # 酸轧入场卷创建人(wms_material_coil.create_by) + coil-create-by: suanzhakuguan diff --git a/klp-da/src/main/java/com/klp/da/controller/OeeReportController.java b/klp-da/src/main/java/com/klp/da/controller/OeeReportController.java index 7af9aab9..bd1b3daa 100644 --- a/klp-da/src/main/java/com/klp/da/controller/OeeReportController.java +++ b/klp-da/src/main/java/com/klp/da/controller/OeeReportController.java @@ -76,24 +76,16 @@ public class OeeReportController extends BaseController { * * 路由:GET /oee/line/acid/summary * 说明: - * - 不接受 start/end 参数,固定返回“当前月份(1号~今天)”的当月预计算结果; - * - 优先从 Redis 当月缓存读取;若缓存缺失则实时计算一次当前月。 + * - 支持 startDate/endDate 参数(yyyy-MM-dd); + * - 若不传则默认查询当前月份(1号~今天); + * - 仅实时计算,不走缓存。 */ @GetMapping("/acid/summary") - public R> getAcidSummary() { - String yyyyMM = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMM")); - String summaryKey = String.format("oee:report:month:summary:%s:SY", yyyyMM); - - // 1. 优先从 Redis 读取当月预计算结果 - String json = stringRedisTemplate.opsForValue().get(summaryKey); - if (StringUtils.isNotBlank(json)) { - List cached = - JSON.parseArray(json, AcidOeeDailySummaryVo.class); - return R.ok(cached); - } - - // 2. 缓存缺失时,回退为实时计算当前月 - String[] range = resolveDateRange(null, null); + public R> getAcidSummary( + @RequestParam(required = false) String startDate, + @RequestParam(required = false) String endDate + ) { + String[] range = resolveDateRange(startDate, endDate); List dailyList = acidOeeService.getDailySummary(range[0], range[1]); return R.ok(dailyList); diff --git a/klp-da/src/main/java/com/klp/da/task/AcidOeeMonthTask.java b/klp-da/src/main/java/com/klp/da/task/AcidOeeMonthTask.java deleted file mode 100644 index 8097f3b4..00000000 --- a/klp-da/src/main/java/com/klp/da/task/AcidOeeMonthTask.java +++ /dev/null @@ -1,143 +0,0 @@ -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.Async; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; - -import org.springframework.boot.ApplicationArguments; -import org.springframework.boot.ApplicationRunner; - -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 implements ApplicationRunner { - - /** 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。 - * 使用 ApplicationRunner 在 Spring Boot 启动完成后执行。 - * 使用 @Async 异步执行,不阻塞项目启动。 - */ - @Async - @Override - public void run(ApplicationArguments args) throws Exception { - 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; - } -} - - diff --git a/klp-pocket/src/main/java/com/klp/pocket/acid/domain/vo/AcidOeeCoilInfoByDateVo.java b/klp-pocket/src/main/java/com/klp/pocket/acid/domain/vo/AcidOeeCoilInfoByDateVo.java new file mode 100644 index 00000000..c3d9b357 --- /dev/null +++ b/klp-pocket/src/main/java/com/klp/pocket/acid/domain/vo/AcidOeeCoilInfoByDateVo.java @@ -0,0 +1,24 @@ +package com.klp.pocket.acid.domain.vo; + +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 酸轧OEE按日钢卷信息(主库来源)。 + */ +@Data +public class AcidOeeCoilInfoByDateVo { + + /** 统计日期 yyyy-MM-dd */ + private String statDate; + + /** 当前钢卷号 */ + private String coilNo; + + /** 重量(吨) */ + private BigDecimal weight; + + /** 判级 */ + private String qualityStatus; +} diff --git a/klp-pocket/src/main/java/com/klp/pocket/acid/mapper/AcidOeeAcidMapper.java b/klp-pocket/src/main/java/com/klp/pocket/acid/mapper/AcidOeeAcidMapper.java new file mode 100644 index 00000000..c3efacdd --- /dev/null +++ b/klp-pocket/src/main/java/com/klp/pocket/acid/mapper/AcidOeeAcidMapper.java @@ -0,0 +1,22 @@ +package com.klp.pocket.acid.mapper; + +import com.baomidou.dynamic.datasource.annotation.DS; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.math.BigDecimal; +import java.util.List; + +/** + * 酸轧线OEE酸轧库Mapper。 + */ +@Mapper +@DS("acid") +public interface AcidOeeAcidMapper { + + /** + * 查询卷级生产节拍(min/吨),用于理论节拍计算。 + */ + List selectCoilCycleMinPerTon(@Param("startDate") String startDate, + @Param("endDate") String endDate); +} diff --git a/klp-pocket/src/main/java/com/klp/pocket/acid/mapper/AcidOeeMapper.java b/klp-pocket/src/main/java/com/klp/pocket/acid/mapper/AcidOeeMapper.java deleted file mode 100644 index a0111cfa..00000000 --- a/klp-pocket/src/main/java/com/klp/pocket/acid/mapper/AcidOeeMapper.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.klp.pocket.acid.mapper; - -import com.klp.pocket.acid.domain.vo.AcidOeeDailySummaryVo; -import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Param; - -import java.util.List; - -/** - * 酸轧线OEE Mapper接口 - * - * @author klp - * @date 2026-01-30 - */ -@Mapper -public interface AcidOeeMapper { - - /** - * 查询OEE日汇总(按日期范围) - * 聚合产量(吨/卷)、停机时间等 - * - * @param startDate 开始日期(yyyy-MM-dd) - * @param endDate 结束日期(yyyy-MM-dd) - * @return 日汇总列表 - */ - List selectDailySummary(@Param("startDate") String startDate, - @Param("endDate") String endDate); - - /** - * 查询每日的钢卷号和重量(用于良品/次品判定) - * - * @param startDate 开始日期(yyyy-MM-dd) - * @param endDate 结束日期(yyyy-MM-dd) - * @return Map列表,key为日期,value为卷号和重量信息 - */ - List selectCoilInfoByDate(@Param("startDate") String startDate, - @Param("endDate") String endDate); - - /** - * 查询卷级生产节拍(min/吨),用于理论节拍计算。 - * - * @param startDate 开始日期(yyyy-MM-dd) - * @param endDate 结束日期(yyyy-MM-dd) - * @return 每卷的生产节拍列表(min/吨) - */ - List selectCoilCycleMinPerTon(@Param("startDate") String startDate, - @Param("endDate") String endDate); - - /** - * 卷号信息内部类(用于Mapper返回) - */ - class CoilInfoByDate { - private String statDate; - private String coilNo; - private java.math.BigDecimal weight; - - public String getStatDate() { return statDate; } - public void setStatDate(String statDate) { this.statDate = statDate; } - public String getCoilNo() { return coilNo; } - public void setCoilNo(String coilNo) { this.coilNo = coilNo; } - public java.math.BigDecimal getWeight() { return weight; } - public void setWeight(java.math.BigDecimal weight) { this.weight = weight; } - } -} - diff --git a/klp-pocket/src/main/java/com/klp/pocket/acid/mapper/AcidOeeMasterMapper.java b/klp-pocket/src/main/java/com/klp/pocket/acid/mapper/AcidOeeMasterMapper.java new file mode 100644 index 00000000..dd9e9452 --- /dev/null +++ b/klp-pocket/src/main/java/com/klp/pocket/acid/mapper/AcidOeeMasterMapper.java @@ -0,0 +1,32 @@ +package com.klp.pocket.acid.mapper; + +import com.baomidou.dynamic.datasource.annotation.DS; +import com.klp.pocket.acid.domain.vo.AcidOeeCoilInfoByDateVo; +import com.klp.pocket.acid.domain.vo.AcidOeeDailySummaryVo; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 酸轧线OEE主库Mapper。 + */ +@Mapper +@DS("master") +public interface AcidOeeMasterMapper { + + /** + * 查询OEE日汇总(总产量来自主库 wms_material_coil)。 + */ + List selectDailySummary(@Param("startDate") String startDate, + @Param("endDate") String endDate, + @Param("createBy") String createBy); + + /** + * 查询每日钢卷重量与判级(来自主库 wms_material_coil)。 + */ + List selectCoilInfoByDate(@Param("startDate") String startDate, + @Param("endDate") String endDate, + @Param("createBy") String createBy); +} + diff --git a/klp-pocket/src/main/java/com/klp/pocket/acid/service/impl/AcidOeeServiceImpl.java b/klp-pocket/src/main/java/com/klp/pocket/acid/service/impl/AcidOeeServiceImpl.java index a5ec1a6d..2081a4e9 100644 --- a/klp-pocket/src/main/java/com/klp/pocket/acid/service/impl/AcidOeeServiceImpl.java +++ b/klp-pocket/src/main/java/com/klp/pocket/acid/service/impl/AcidOeeServiceImpl.java @@ -1,25 +1,34 @@ package com.klp.pocket.acid.service.impl; -import com.baomidou.dynamic.datasource.annotation.DS; import com.klp.common.utils.StringUtils; +import com.klp.pocket.acid.domain.vo.AcidOeeCoilInfoByDateVo; import com.klp.pocket.acid.domain.vo.AcidOeeDailySummaryVo; import com.klp.pocket.acid.domain.vo.AcidOeeIdealCycleVo; import com.klp.pocket.acid.domain.vo.AcidOeeLoss7Vo; import com.klp.pocket.acid.domain.vo.Klptcm1ProStoppageVo; import com.klp.pocket.acid.domain.bo.Klptcm1ProStoppageBo; -import com.klp.pocket.acid.mapper.AcidOeeMapper; +import com.klp.pocket.acid.mapper.AcidOeeMasterMapper; import com.klp.pocket.acid.service.IAcidOeeService; import com.klp.pocket.acid.service.IKlptcm1ProStoppageService; -import com.klp.pocket.common.service.ICoilQualityJudgeService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.math.BigDecimal; import java.math.RoundingMode; import java.text.SimpleDateFormat; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Calendar; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; /** * 酸轧线OEE Service实现类 @@ -29,53 +38,65 @@ import java.util.Calendar; */ @Slf4j @RequiredArgsConstructor -@DS("acid") @Service public class AcidOeeServiceImpl implements IAcidOeeService { - /** 酸轧成品库库区ID */ - private static final Long ACID_FINISHED_WAREHOUSE_ID = 1988150099140866050L; - /** 固定理论节拍(min/吨) */ - private static final BigDecimal FIXED_IDEAL_CYCLE = BigDecimal.valueOf(0.47); - private final AcidOeeMapper acidOeeMapper; + /** 次品判级集合:命中这些 quality_status 判为次品 */ + private static final Set SCRAP_QUALITY_STATUS = new HashSet<>( + Arrays.asList("C+", "C", "C-", "D+", "D", "D-") + ); + + private final AcidOeeMasterMapper acidOeeMasterMapper; private final IKlptcm1ProStoppageService stoppageService; - private final ICoilQualityJudgeService coilQualityJudgeService; + + @Value("${oee.acid.coil-create-by}") + private String acidCreateName; @Override public List getDailySummary(String startDate, String endDate) { // 1. 查询基础日汇总(产量、停机时间等) - List summaries = acidOeeMapper.selectDailySummary(startDate, endDate); - + List summaries = acidOeeMasterMapper.selectDailySummary( + startDate, + endDate, + acidCreateName + ); if (summaries == null || summaries.isEmpty()) { return Collections.emptyList(); } - // 2. 查询停机事件,按日期聚合停机时间 Map downtimeByDate = aggregateDowntimeByDate(startDate, endDate); // 3. 查询产量明细,用于良品/次品判定 Map> coilInfoByDate = getCoilNosByDate(startDate, endDate); - // 4. 理论节拍:使用固定值0.47 - BigDecimal idealCycleTon = FIXED_IDEAL_CYCLE; - - // 5. 填充每个日汇总的完整数据 + // 4. 先按天计算 dailyCycle = runTime/totalOutputTon,再取中位数作为理论节拍 + List dailyCycles = new ArrayList<>(); for (AcidOeeDailySummaryVo summary : summaries) { String statDate = summary.getStatDate(); summary.setLineId("SY"); summary.setLineName("酸轧线"); - // 填充停机时间 Long downtime = downtimeByDate.getOrDefault(statDate, 0L); summary.setDowntimeMin(downtime); - // 计算运转时间 Long loadingTime = summary.getLoadingTimeMin() != null ? summary.getLoadingTimeMin() : 0L; Long runTime = Math.max(0, loadingTime - downtime); summary.setRunTimeMin(runTime); - // 理论节拍:若尚未填充,则统一使用“优良日统计”得到的节拍 - if (summary.getIdealCycleTimeMinPerTon() == null && idealCycleTon != null) { + BigDecimal totalOutputTon = summary.getTotalOutputTon(); + if (runTime > 0 && totalOutputTon != null && totalOutputTon.compareTo(BigDecimal.ZERO) > 0) { + BigDecimal dailyCycle = BigDecimal.valueOf(runTime) + .divide(totalOutputTon, 6, RoundingMode.HALF_UP); + dailyCycles.add(dailyCycle); + } + } + dailyCycles.sort(BigDecimal::compareTo); + BigDecimal idealCycleTon = applyEightyPercent(median(dailyCycles)); + + // 5. 回填理论节拍、良品次品并计算派生指标 + for (AcidOeeDailySummaryVo summary : summaries) { + String statDate = summary.getStatDate(); + if (idealCycleTon != null) { summary.setIdealCycleTimeMinPerTon(idealCycleTon); } @@ -91,11 +112,6 @@ public class AcidOeeServiceImpl implements IAcidOeeService { summary.setDefectOutputCoil(0L); } - // 填充理论节拍(从回归数据或缓存获取,这里暂时留空,由调用方填充) - // summary.setIdealCycleTimeMinPerTon(...); - // summary.setIdealCycleTimeMinPerCoil(...); - - // 计算派生指标 calculateDerivedMetrics(summary); } @@ -127,7 +143,11 @@ public class AcidOeeServiceImpl implements IAcidOeeService { @Override public AcidOeeIdealCycleVo getIdealCycle(String startDate, String endDate) { // 1) 取基础日汇总(产量、负荷时间等) - List daily = acidOeeMapper.selectDailySummary(startDate, endDate); + List daily = acidOeeMasterMapper.selectDailySummary( + startDate, + endDate, + acidCreateName + ); AcidOeeIdealCycleVo rsp = new AcidOeeIdealCycleVo(); rsp.setLineId("SY"); rsp.setLineName("酸轧线"); @@ -151,23 +171,31 @@ public class AcidOeeServiceImpl implements IAcidOeeService { d.setRunTimeMin(Math.max(0, loading - downtime)); } - // 3) 卷级节拍 = (END_DATE - START_DATE)/出口重量,计算中位数(用于展示,不用于OEE计算) - List coilCycles = acidOeeMapper.selectCoilCycleMinPerTon(startDate, endDate); - coilCycles.removeIf(c -> c == null || c.compareTo(BigDecimal.ZERO) <= 0); - coilCycles.sort(BigDecimal::compareTo); - BigDecimal medianCycle = median(coilCycles); - - // 理论节拍使用固定值0.47(用于OEE计算) - rsp.setIdealCycleTimeMinPerTon(FIXED_IDEAL_CYCLE); - // 中位数理论节拍(用于展示) + // 3) 理论节拍按“天维度”:每天(运转时间/总吨),再按日样本统计中位数用于展示 + List dailyCycles = new ArrayList<>(); + for (AcidOeeDailySummaryVo d : daily) { + Long run = d.getRunTimeMin(); + BigDecimal ton = d.getTotalOutputTon(); + if (run == null || run <= 0 || ton == null || ton.compareTo(BigDecimal.ZERO) <= 0) { + continue; + } + dailyCycles.add(BigDecimal.valueOf(run).divide(ton, 6, RoundingMode.HALF_UP)); + } + dailyCycles.sort(BigDecimal::compareTo); + BigDecimal medianCycle = median(dailyCycles); + + // 理论节拍:按“当天运转时间/当天总吨”逐日计算,接口返回前乘以80% + BigDecimal idealCycle = applyEightyPercent(medianCycle); + rsp.setIdealCycleTimeMinPerTon(idealCycle); + // 展示字段保持为中位数 rsp.setMedianCycleTimeMinPerTon(medianCycle); // 样本天数:当前查询区间内有产量的自然日数量(与传入的日期范围一一对应) rsp.setSampleDays(daily.size()); // 4) 日粒度对比数据:理论耗时 vs 实际运转时间(用于前端展示"有效性") - // 使用固定值0.47计算理论耗时 + // 使用“中间50%样本平均理论节拍”计算理论耗时 List compare = new ArrayList<>(); - if (FIXED_IDEAL_CYCLE != null) { + if (idealCycle != null) { for (AcidOeeDailySummaryVo d : daily) { BigDecimal ton = d.getTotalOutputTon(); Long run = d.getRunTimeMin(); @@ -175,7 +203,7 @@ public class AcidOeeServiceImpl implements IAcidOeeService { AcidOeeIdealCycleVo.DailyComparePointVo p = new AcidOeeIdealCycleVo.DailyComparePointVo(); p.setStatDate(d.getStatDate()); p.setActualRunTimeMin(run); - p.setTheoreticalTimeMin(FIXED_IDEAL_CYCLE.multiply(ton)); + p.setTheoreticalTimeMin(idealCycle.multiply(ton)); compare.add(p); } } @@ -270,25 +298,25 @@ public class AcidOeeServiceImpl implements IAcidOeeService { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); Calendar cal = Calendar.getInstance(); - + for (Klptcm1ProStoppageVo event : events) { if (event.getStartDate() == null || event.getDuration() == null || event.getDuration() <= 0) { continue; } - + Date eventStart = event.getStartDate(); long durationSec = event.getDuration(); long durationMin = (durationSec + 59) / 60; // 向上取整,避免丢失秒数 - + // 计算停机结束时间 cal.setTime(eventStart); cal.add(Calendar.SECOND, (int) durationSec); Date eventEnd = cal.getTime(); - + // 如果停机事件在同一天,直接累加 String startDateStr = dateFormat.format(eventStart); String endDateStr = dateFormat.format(eventEnd); - + if (startDateStr.equals(endDateStr)) { // 同一天,直接累加 downtimeMap.merge(startDateStr, durationMin, Long::sum); @@ -300,23 +328,23 @@ public class AcidOeeServiceImpl implements IAcidOeeService { cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); Date dayStart = cal.getTime(); - + Date currentDayStart = dayStart; - + while (currentDayStart.before(eventEnd)) { cal.setTime(currentDayStart); cal.add(Calendar.DAY_OF_MONTH, 1); Date nextDayStart = cal.getTime(); - + // 计算当前天的停机分钟数 Date dayEnd = nextDayStart.before(eventEnd) ? nextDayStart : eventEnd; long dayMinutes = Math.max(0, (dayEnd.getTime() - Math.max(currentDayStart.getTime(), eventStart.getTime())) / (1000 * 60)); - + if (dayMinutes > 0) { String dateKey = dateFormat.format(currentDayStart); downtimeMap.merge(dateKey, dayMinutes, Long::sum); } - + currentDayStart = nextDayStart; } } @@ -329,13 +357,18 @@ public class AcidOeeServiceImpl implements IAcidOeeService { * 获取每日的钢卷号和重量(用于良品/次品判定) */ private Map> getCoilNosByDate(String startDate, String endDate) { - List coilInfoList = acidOeeMapper.selectCoilInfoByDate(startDate, endDate); + + List coilInfoList = acidOeeMasterMapper.selectCoilInfoByDate( + startDate, + endDate, + acidCreateName + ); Map> result = new HashMap<>(); - for (AcidOeeMapper.CoilInfoByDate info : coilInfoList) { + for (AcidOeeCoilInfoByDateVo info : coilInfoList) { String date = info.getStatDate(); result.computeIfAbsent(date, k -> new ArrayList<>()) - .add(new CoilInfo(info.getCoilNo(), info.getWeight())); + .add(new CoilInfo(info.getCoilNo(), info.getWeight(), info.getQualityStatus())); } return result; @@ -347,10 +380,12 @@ public class AcidOeeServiceImpl implements IAcidOeeService { private static class CoilInfo { final String coilNo; final BigDecimal weight; + final String qualityStatus; - CoilInfo(String coilNo, BigDecimal weight) { + CoilInfo(String coilNo, BigDecimal weight, String qualityStatus) { this.coilNo = coilNo; this.weight = weight; + this.qualityStatus = qualityStatus; } } @@ -362,19 +397,18 @@ public class AcidOeeServiceImpl implements IAcidOeeService { long goodCoil = 0L; BigDecimal defectTon = BigDecimal.ZERO; long defectCoil = 0L; - for (CoilInfo coilInfo : coilInfos) { - String coilNo = coilInfo.coilNo; BigDecimal coilWeight = coilInfo.weight != null ? coilInfo.weight : BigDecimal.ZERO; + String qualityStatus = StringUtils.trim(coilInfo.qualityStatus); - // 通过WMS判定良品/次品 - Boolean isScrap = coilQualityJudgeService.isScrap(ACID_FINISHED_WAREHOUSE_ID, coilNo); - if (isScrap == null) { - // 匹配不到,忽略不计 + // 没有判级时按良品处理(避免再次跨库匹配导致错配) + if (StringUtils.isBlank(qualityStatus)) { + goodTon = goodTon.add(coilWeight); + goodCoil++; continue; } - if (Boolean.TRUE.equals(isScrap)) { + if (SCRAP_QUALITY_STATUS.contains(qualityStatus)) { // 次品 defectTon = defectTon.add(coilWeight); defectCoil++; @@ -406,6 +440,7 @@ public class AcidOeeServiceImpl implements IAcidOeeService { } // 性能稼动率(吨维度) + // 口径:理论节拍单位为 min/吨 时,性能稼动率 = (理论节拍 × 实际产量) / 实际运转时间 × 100 Long runTime = summary.getRunTimeMin() != null ? summary.getRunTimeMin() : 0L; BigDecimal idealCycleTon = summary.getIdealCycleTimeMinPerTon(); BigDecimal totalOutputTon = summary.getTotalOutputTon(); @@ -415,12 +450,14 @@ public class AcidOeeServiceImpl implements IAcidOeeService { && totalOutputTon != null && totalOutputTon.compareTo(BigDecimal.ZERO) > 0) { BigDecimal idealTime = idealCycleTon.multiply(totalOutputTon); - BigDecimal performanceTon = idealTime.divide(BigDecimal.valueOf(runTime), 4, RoundingMode.HALF_UP) + BigDecimal performanceTon = idealTime + .divide(BigDecimal.valueOf(runTime), 4, RoundingMode.HALF_UP) .multiply(BigDecimal.valueOf(100)); summary.setPerformanceTon(performanceTon); } // 性能稼动率(卷维度) + // 口径:理论节拍单位为 min/卷 时,性能稼动率 = (理论节拍 × 实际产量) / 实际运转时间 × 100 Long totalOutputCoil = summary.getTotalOutputCoil() != null ? summary.getTotalOutputCoil() : 0L; BigDecimal idealCycleCoil = summary.getIdealCycleTimeMinPerCoil(); if (runTime > 0 @@ -428,7 +465,8 @@ public class AcidOeeServiceImpl implements IAcidOeeService { && idealCycleCoil.compareTo(BigDecimal.ZERO) > 0 && totalOutputCoil > 0) { BigDecimal idealTime = idealCycleCoil.multiply(BigDecimal.valueOf(totalOutputCoil)); - BigDecimal performanceCoil = idealTime.divide(BigDecimal.valueOf(runTime), 4, RoundingMode.HALF_UP) + BigDecimal performanceCoil = idealTime + .divide(BigDecimal.valueOf(runTime), 4, RoundingMode.HALF_UP) .multiply(BigDecimal.valueOf(100)); summary.setPerformanceCoil(performanceCoil); } @@ -463,6 +501,15 @@ public class AcidOeeServiceImpl implements IAcidOeeService { return a.add(b).divide(BigDecimal.valueOf(2), 6, RoundingMode.HALF_UP); } + /** 理论节拍返回前按业务口径乘以70% */ + private BigDecimal applyEightyPercent(BigDecimal cycle) { + if (cycle == null) { + return null; + } + return cycle.multiply(BigDecimal.valueOf(0.7)).setScale(6, RoundingMode.HALF_UP); + } + + /** * 解析日期字符串为Date对象 */ diff --git a/klp-pocket/src/main/resources/mapper/pocket/AcidOeeAcidMapper.xml b/klp-pocket/src/main/resources/mapper/pocket/AcidOeeAcidMapper.xml new file mode 100644 index 00000000..f63ee2b1 --- /dev/null +++ b/klp-pocket/src/main/resources/mapper/pocket/AcidOeeAcidMapper.xml @@ -0,0 +1,25 @@ + + + + + + + + diff --git a/klp-pocket/src/main/resources/mapper/pocket/AcidOeeMapper.xml b/klp-pocket/src/main/resources/mapper/pocket/AcidOeeMapper.xml deleted file mode 100644 index d6856fc6..00000000 --- a/klp-pocket/src/main/resources/mapper/pocket/AcidOeeMapper.xml +++ /dev/null @@ -1,95 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/klp-pocket/src/main/resources/mapper/pocket/AcidOeeMasterMapper.xml b/klp-pocket/src/main/resources/mapper/pocket/AcidOeeMasterMapper.xml new file mode 100644 index 00000000..af3e1b83 --- /dev/null +++ b/klp-pocket/src/main/resources/mapper/pocket/AcidOeeMasterMapper.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +