From 1dfdc3b0611211feba0570ec6d8cfad078c37135 Mon Sep 17 00:00:00 2001
From: xiaoyi <908147524@qq.com>
Date: 星期三, 26 八月 2026 15:13:28 +0800
Subject: [PATCH] feat 功能修正

---
 yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/service/decision/BiForecastServiceImpl.java |   95 +++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 86 insertions(+), 9 deletions(-)

diff --git a/yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/service/decision/BiForecastServiceImpl.java b/yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/service/decision/BiForecastServiceImpl.java
index 5455fb5..478da23 100644
--- a/yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/service/decision/BiForecastServiceImpl.java
+++ b/yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/service/decision/BiForecastServiceImpl.java
@@ -1,14 +1,26 @@
 package cn.iocoder.yudao.module.bi.service.decision;
 
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.date.DateUtil;
+import cn.iocoder.yudao.module.bi.controller.admin.decision.vo.DecisionForecastHistorySaveReqVO;
+import cn.iocoder.yudao.module.bi.controller.admin.decision.vo.DecisionForecastWorkOrderCreateReqVO;
 import cn.iocoder.yudao.module.bi.dal.dataobject.decision.BiForecastDataDO;
 import cn.iocoder.yudao.module.bi.dal.mysql.decision.BiForecastDataMapper;
+import cn.iocoder.yudao.module.mes.api.workorder.MesProWorkOrderApi;
+import cn.iocoder.yudao.module.mes.api.workorder.dto.MesProWorkOrderCreateReqDTO;
 import jakarta.annotation.Resource;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
 import java.math.BigDecimal;
+import java.math.RoundingMode;
 import java.time.LocalDateTime;
 import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.bi.enums.ErrorCodeConstants.BI_FORECAST_NO_HISTORY;
+import static cn.iocoder.yudao.module.bi.enums.ErrorCodeConstants.BI_FORECAST_NOT_EXISTS;
 
 /**
  * 璐熻嵎棰勬祴 Service 瀹炵幇
@@ -21,31 +33,96 @@
 @Service
 public class BiForecastServiceImpl implements BiForecastService {
 
+    /**
+     * 宸ュ崟鏉ユ簮绫诲瀷锛氳礋鑽烽娴嬶紙涓庡瓧鍏� mes_pro_work_order_source_type 鐨� value=4 涓�鑷达級
+     */
+    private static final Integer WORK_ORDER_SOURCE_TYPE_FORECAST = 4;
+
     @Resource
     private BiForecastDataMapper forecastDataMapper;
+    @Resource
+    private MesProWorkOrderApi mesProWorkOrderApi;
 
     @Override
     public List<BiForecastDataDO> getForecastList(String forecastCode, LocalDateTime beginTime, LocalDateTime endTime) {
         return forecastDataMapper.selectListByCodeAndTimeRange(forecastCode, beginTime, endTime);
     }
 
+    /**
+     * 绉诲姩骞冲潎绐楀彛澶у皬
+     */
+    private static final int SMA_WINDOW = 12;
+
+    @Override
+    public Long createForecastHistory(DecisionForecastHistorySaveReqVO reqVO) {
+        BiForecastDataDO data = BiForecastDataDO.builder()
+                .forecastCode(reqVO.getForecastCode())
+                .forecastName(reqVO.getForecastName())
+                .pointTime(reqVO.getPointTime())
+                .forecastValue(reqVO.getForecastValue())
+                .actualValue(reqVO.getActualValue())
+                .dimension(reqVO.getDimension())
+                .dimensionValue(reqVO.getDimensionValue())
+                .remark(reqVO.getRemark())
+                .modelVersion("manual-history")
+                .build();
+        forecastDataMapper.insert(data);
+        return data.getId();
+    }
+
     @Override
     public void generateForecast(String forecastCode, LocalDateTime pointTime) {
-        // 鏈湡鍗犱綅锛氱畝鍗曠Щ鍔ㄥ钩鍧囬娴�
-        // 瀹為檯椤圭洰搴旈泦鎴� ML 妯″瀷锛圓RIMA/Prophet/LSTM锛�
-        log.info("[generateForecast][鐢熸垚棰勬祴] forecastCode={}, pointTime={}, 褰撳墠涓哄崰浣嶅疄鐜�", forecastCode, pointTime);
+        // 鍩轰簬鍚岄娴嬬紪鐮佺殑鍘嗗彶棰勬祴鏁版嵁鍋氱畝鍗曠Щ鍔ㄥ钩鍧囷紱鏃犲巻鍙叉暟鎹椂涓嶄吉閫犻浂鍊�
+        List<BiForecastDataDO> history = forecastDataMapper.selectRecentByCode(forecastCode, SMA_WINDOW);
+        if (CollUtil.isEmpty(history)) {
+            throw exception(BI_FORECAST_NO_HISTORY);
+        }
+        BigDecimal sum = history.stream()
+                .map(BiForecastDataDO::getForecastValue)
+                .filter(value -> value != null)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+        BigDecimal avg = sum.divide(BigDecimal.valueOf(history.size()), 4, RoundingMode.HALF_UP);
+        BigDecimal bound = avg.multiply(new BigDecimal("0.10"));
 
         BiForecastDataDO forecast = BiForecastDataDO.builder()
                 .forecastCode(forecastCode)
-                .forecastName("璐熻嵎棰勬祴(鍗犱綅)")
+                .forecastName(history.get(0).getForecastName())
                 .pointTime(pointTime)
-                .forecastValue(BigDecimal.ZERO)
-                .lowerBound(BigDecimal.ZERO)
-                .upperBound(BigDecimal.ZERO)
+                .forecastValue(avg)
+                .lowerBound(avg.subtract(bound))
+                .upperBound(avg.add(bound))
                 .confidenceLevel(new BigDecimal("95.00"))
-                .modelVersion("v1.0-placeholder")
+                .modelVersion("v1.0-sma")
                 .build();
         forecastDataMapper.insert(forecast);
     }
 
-}
\ No newline at end of file
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Long createWorkOrderFromForecast(DecisionForecastWorkOrderCreateReqVO reqVO) {
+        // 1. 鏍¢獙棰勬祴鏁版嵁瀛樺湪
+        BiForecastDataDO forecast = forecastDataMapper.selectById(reqVO.getForecastId());
+        if (forecast == null) {
+            throw exception(BI_FORECAST_NOT_EXISTS);
+        }
+        // 2. 缁勮宸ュ崟 DTO 骞惰皟鐢� MES API 鍒涘缓锛堟棤浜у搧宸ュ崟锛岃烦 BOM锛�
+        MesProWorkOrderCreateReqDTO dto = new MesProWorkOrderCreateReqDTO();
+        dto.setCode(generateWorkOrderCode());
+        dto.setName(reqVO.getWorkOrderName() != null
+                ? reqVO.getWorkOrderName()
+                : ("璐熻嵎棰勬祴-" + forecast.getForecastName()));
+        dto.setType(reqVO.getWorkOrderType());
+        dto.setOrderSourceType(WORK_ORDER_SOURCE_TYPE_FORECAST);
+        dto.setOrderSourceCode(forecast.getForecastCode());
+        dto.setRequestDate(reqVO.getRequestDate());
+        dto.setQuantity(reqVO.getQuantity());
+        dto.setRemark(reqVO.getRemark());
+        return mesProWorkOrderApi.createWorkOrder(dto);
+    }
+
+    private String generateWorkOrderCode() {
+        return "GZ" + DateUtil.format(LocalDateTime.now(), "yyyyMMddHHmmss")
+                + String.format("%03d", (int) (Math.random() * 1000));
+    }
+
+}

--
Gitblit v1.9.3