执行重构加入镀锌线1的后端调用接口
This commit is contained in:
@@ -2,16 +2,7 @@ package com.klp.da.controller;
|
||||
|
||||
import com.klp.common.annotation.Log;
|
||||
import com.klp.common.core.controller.BaseController;
|
||||
import com.klp.common.core.domain.PageQuery;
|
||||
import com.klp.common.core.domain.R;
|
||||
import com.klp.common.core.page.TableDataInfo;
|
||||
import com.klp.common.enums.BusinessType;
|
||||
import com.klp.da.domain.bo.OeeQueryBo;
|
||||
import com.klp.da.domain.vo.OeeLineSummaryVo;
|
||||
import com.klp.da.domain.vo.OeeEventVo;
|
||||
import com.klp.da.service.IOeeReportService;
|
||||
import com.klp.da.service.OeeSummaryJobService;
|
||||
import com.klp.da.service.OeeTheoryCycleJobService;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
@@ -25,8 +16,6 @@ import java.util.Map;
|
||||
/**
|
||||
* OEE 报表聚合 Controller(方式 A:后端统一聚合多服务)
|
||||
*
|
||||
* 当前仅提供接口“架子”,具体聚合逻辑在 {@link IOeeReportService} 中实现。
|
||||
*
|
||||
* 路由前缀与 docs/oee-report-design.md 设计文档保持一致:
|
||||
* - /api/ems/oee/line/summary
|
||||
* - /api/ems/oee/line/loss7
|
||||
@@ -39,92 +28,6 @@ import java.util.Map;
|
||||
@RequestMapping("/oee/line")
|
||||
public class OeeReportController extends BaseController {
|
||||
|
||||
private final IOeeReportService oeeReportService;
|
||||
private final OeeTheoryCycleJobService oeeTheoryCycleJobService;
|
||||
private final OeeSummaryJobService oeeSummaryJobService;
|
||||
|
||||
/**
|
||||
* KPI + 趋势汇总
|
||||
*/
|
||||
@Log(title = "OEE 报表-汇总", businessType = BusinessType.OTHER)
|
||||
@GetMapping("/summary")
|
||||
public R<List<OeeLineSummaryVo>> summary(OeeQueryBo queryBo) {
|
||||
return R.ok(oeeReportService.summary(queryBo));
|
||||
}
|
||||
|
||||
/**
|
||||
* KPI + 趋势汇总(异步任务):立即返回 jobId,通过 WebSocket 推送进度与结果。
|
||||
*
|
||||
* 前端订阅:
|
||||
* ws://{host}/websocket?type={wsType}
|
||||
*/
|
||||
@Log(title = "OEE 报表-汇总(异步)", businessType = BusinessType.OTHER)
|
||||
@GetMapping("/summary/job")
|
||||
public R<Map<String, Object>> summaryJob(OeeQueryBo queryBo) {
|
||||
System.out.println("[OEE][summary][job][controller] request received, thread=" + Thread.currentThread().getName()
|
||||
+ ", startDate=" + (queryBo == null ? null : queryBo.getStartDate())
|
||||
+ ", endDate=" + (queryBo == null ? null : queryBo.getEndDate())
|
||||
+ ", lineIds=" + (queryBo == null ? null : queryBo.getLineIds()));
|
||||
Map<String, Object> resp = oeeSummaryJobService.createAndStart(queryBo);
|
||||
System.out.println("[OEE][summary][job][controller] job created, resp=" + resp);
|
||||
return R.ok(resp);
|
||||
}
|
||||
|
||||
/**
|
||||
* 7 大损失汇总
|
||||
*/
|
||||
@Log(title = "OEE 报表-7大损失", businessType = BusinessType.OTHER)
|
||||
@GetMapping("/loss7")
|
||||
public R<Map<String, Object>> lossSummary(OeeQueryBo queryBo) {
|
||||
Map<String, Object> result = oeeReportService.lossSummary(queryBo);
|
||||
return R.ok(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 停机/损失事件明细
|
||||
*/
|
||||
@Log(title = "OEE 报表-事件明细", businessType = BusinessType.OTHER)
|
||||
@GetMapping("/events")
|
||||
public TableDataInfo<OeeEventVo> events(OeeQueryBo queryBo, PageQuery pageQuery) {
|
||||
return oeeReportService.events(queryBo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出 Word 报表(整体版式由后端模板控制)
|
||||
*/
|
||||
@Log(title = "OEE 报表-导出Word", businessType = BusinessType.EXPORT)
|
||||
@GetMapping("/exportWord")
|
||||
public void exportWord(OeeQueryBo queryBo, HttpServletResponse response) {
|
||||
oeeReportService.exportWord(queryBo, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 理论节拍回归(散点+拟合线)
|
||||
*
|
||||
* 说明:该接口用于前端绘制“散点+回归折线”的图形,节拍数据来源于 WMS 回归结果。
|
||||
*/
|
||||
@Log(title = "OEE 报表-理论节拍回归", businessType = BusinessType.OTHER)
|
||||
@GetMapping("/theoryCycle/regression")
|
||||
public R<Map<String, Object>> theoryCycleRegression(OeeQueryBo queryBo) {
|
||||
return R.ok(oeeReportService.theoryCycleRegression(queryBo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 理论节拍回归(异步任务):立即返回 jobId,通过 WebSocket 推送进度与结果。
|
||||
*
|
||||
* 前端订阅:
|
||||
* ws://{host}/websocket?type={wsType}
|
||||
*/
|
||||
@Log(title = "OEE 报表-理论节拍回归(异步)", businessType = BusinessType.OTHER)
|
||||
@GetMapping("/theoryCycle/regression/job")
|
||||
public R<Map<String, Object>> theoryCycleRegressionJob(OeeQueryBo queryBo) {
|
||||
System.out.println("[OEE][theoryCycle][job][controller] request received, thread=" + Thread.currentThread().getName()
|
||||
+ ", startTime=" + (queryBo == null ? null : queryBo.getStartTime())
|
||||
+ ", endTime=" + (queryBo == null ? null : queryBo.getEndTime())
|
||||
+ ", lineIds=" + (queryBo == null ? null : queryBo.getLineIds()));
|
||||
Map<String, Object> resp = oeeTheoryCycleJobService.createAndStart(queryBo);
|
||||
System.out.println("[OEE][theoryCycle][job][controller] job created, resp=" + resp);
|
||||
return R.ok(resp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
占位文件,无需使用。
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
package com.klp.da.domain.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* OEE 事件(停机/损失)明细 VO
|
||||
*/
|
||||
@Data
|
||||
public class OeeEventVo {
|
||||
|
||||
private String lineId;
|
||||
|
||||
private String lineName;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime eventStartTime;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime eventEndTime;
|
||||
|
||||
/**
|
||||
* 时长(分钟)
|
||||
*/
|
||||
private Integer durationMin;
|
||||
|
||||
private String rawReasonCode;
|
||||
|
||||
private String rawReasonName;
|
||||
|
||||
private String lossCategoryCode;
|
||||
|
||||
private String lossCategoryName;
|
||||
|
||||
private String remark;
|
||||
}
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
package com.klp.da.domain.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 产线 OEE 汇总 + 日趋势 VO
|
||||
*
|
||||
* 对应设计文档 7.1 返回结构中的一条 line 记录。
|
||||
*/
|
||||
@Data
|
||||
public class OeeLineSummaryVo {
|
||||
|
||||
/**
|
||||
* 产线 ID
|
||||
*/
|
||||
private String lineId;
|
||||
|
||||
/**
|
||||
* 产线名称
|
||||
*/
|
||||
private String lineName;
|
||||
|
||||
/**
|
||||
* 区间汇总
|
||||
*/
|
||||
private Summary total;
|
||||
|
||||
/**
|
||||
* 日粒度数据(用于趋势图)
|
||||
*/
|
||||
private List<Daily> daily;
|
||||
|
||||
@Data
|
||||
public static class Summary {
|
||||
private Integer loadingTimeMin;
|
||||
private Integer downtimeMin;
|
||||
private Integer runTimeMin;
|
||||
private BigDecimal totalOutput;
|
||||
private BigDecimal goodOutput;
|
||||
private BigDecimal defectOutput;
|
||||
|
||||
private BigDecimal availability;
|
||||
private BigDecimal performance;
|
||||
private BigDecimal quality;
|
||||
private BigDecimal oee;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Daily {
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private LocalDate statDate;
|
||||
|
||||
private Integer loadingTimeMin;
|
||||
private Integer downtimeMin;
|
||||
private Integer runTimeMin;
|
||||
private BigDecimal totalOutput;
|
||||
private BigDecimal goodOutput;
|
||||
private BigDecimal defectOutput;
|
||||
|
||||
private BigDecimal availability;
|
||||
private BigDecimal performance;
|
||||
private BigDecimal quality;
|
||||
private BigDecimal oee;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
package com.klp.da.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 7 大损失分类汇总 VO
|
||||
*/
|
||||
@Data
|
||||
public class OeeLossCategorySummaryVo {
|
||||
|
||||
/**
|
||||
* 损失类别编码(1~7 或枚举)
|
||||
*/
|
||||
private String lossCategoryCode;
|
||||
|
||||
/**
|
||||
* 损失类别名称
|
||||
*/
|
||||
private String lossCategoryName;
|
||||
|
||||
/**
|
||||
* 损失时间(分钟)
|
||||
*/
|
||||
private Integer lossTimeMin;
|
||||
|
||||
/**
|
||||
* 损失占比(0~1 或 0~100,随整体口径配置)
|
||||
*/
|
||||
private BigDecimal lossTimeRate;
|
||||
|
||||
/**
|
||||
* 事件次数(可选)
|
||||
*/
|
||||
private Integer count;
|
||||
}
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
package com.klp.da.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 损失原因 TopN VO
|
||||
*/
|
||||
@Data
|
||||
public class OeeLossReasonVo {
|
||||
|
||||
private String lineId;
|
||||
|
||||
/**
|
||||
* 原因编码
|
||||
*/
|
||||
private String reasonCode;
|
||||
|
||||
/**
|
||||
* 原因名称
|
||||
*/
|
||||
private String reasonName;
|
||||
|
||||
/**
|
||||
* 所属损失类别编码
|
||||
*/
|
||||
private String lossCategoryCode;
|
||||
|
||||
/**
|
||||
* 损失时间(分钟)
|
||||
*/
|
||||
private Integer lossTimeMin;
|
||||
|
||||
/**
|
||||
* 时间占比
|
||||
*/
|
||||
private BigDecimal lossTimeRate;
|
||||
}
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
package com.klp.da.service;
|
||||
|
||||
import com.klp.common.core.domain.PageQuery;
|
||||
import com.klp.common.core.page.TableDataInfo;
|
||||
import com.klp.da.domain.bo.OeeQueryBo;
|
||||
import com.klp.da.domain.vo.OeeEventVo;
|
||||
import com.klp.da.domain.vo.OeeLineSummaryVo;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* OEE 报表聚合 Service 接口(方式 A:后端统一聚合多服务)
|
||||
*
|
||||
* 实现类负责:
|
||||
* - 调用酸轧线、镀锌一线等外部服务
|
||||
* - 做数据汇总、口径统一和格式转换
|
||||
*/
|
||||
public interface IOeeReportService {
|
||||
|
||||
/**
|
||||
* KPI + 趋势汇总
|
||||
*/
|
||||
List<OeeLineSummaryVo> summary(OeeQueryBo queryBo);
|
||||
|
||||
/**
|
||||
* 7 大损失汇总
|
||||
*
|
||||
* 返回 Map 以便后续扩展:
|
||||
* - byLine: List<LineLossSummary>
|
||||
* - topReasons: List<OeeLossReasonVo>
|
||||
* 等
|
||||
*/
|
||||
Map<String, Object> lossSummary(OeeQueryBo queryBo);
|
||||
|
||||
/**
|
||||
* 事件明细
|
||||
*/
|
||||
TableDataInfo<OeeEventVo> events(OeeQueryBo queryBo, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 导出 Word 报表
|
||||
*/
|
||||
void exportWord(OeeQueryBo queryBo, HttpServletResponse response);
|
||||
|
||||
/**
|
||||
* 理论节拍线性回归(散点+拟合线),用于前端绘图。
|
||||
*/
|
||||
Map<String, Object> theoryCycleRegression(OeeQueryBo queryBo);
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
package com.klp.da.service;
|
||||
|
||||
import com.klp.da.domain.bo.OeeQueryBo;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* OEE summary 异步任务(WebSocket 推送)
|
||||
*/
|
||||
public interface OeeSummaryJobService {
|
||||
|
||||
/**
|
||||
* 创建任务并异步执行,立即返回 job 信息。
|
||||
*
|
||||
* @return Map: { jobId, wsType }
|
||||
*/
|
||||
Map<String, Object> createAndStart(OeeQueryBo queryBo);
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
package com.klp.da.service;
|
||||
|
||||
import com.klp.da.domain.bo.OeeQueryBo;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 理论节拍回归异步任务(WebSocket 推送)
|
||||
*/
|
||||
public interface OeeTheoryCycleJobService {
|
||||
|
||||
/**
|
||||
* 创建任务并异步执行,立即返回 job 信息。
|
||||
*
|
||||
* @return Map: { jobId, wsType }
|
||||
*/
|
||||
Map<String, Object> createAndStart(OeeQueryBo queryBo);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,155 +0,0 @@
|
||||
package com.klp.da.service.impl;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.klp.da.domain.bo.OeeQueryBo;
|
||||
import com.klp.da.domain.vo.OeeLineSummaryVo;
|
||||
import com.klp.da.service.IOeeReportService;
|
||||
import com.klp.da.service.OeeSummaryJobService;
|
||||
import com.klp.framework.websocket.TypeWebSocketUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class OeeSummaryJobServiceImpl implements OeeSummaryJobService {
|
||||
|
||||
public static final String WS_TYPE_PREFIX = "oee_summary:";
|
||||
|
||||
private static final ExecutorService EXECUTOR = Executors.newFixedThreadPool(2, r -> {
|
||||
Thread t = new Thread(r);
|
||||
t.setName("oee-summary-job");
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
});
|
||||
|
||||
private final IOeeReportService oeeReportService;
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
@Override
|
||||
public Map<String, Object> createAndStart(OeeQueryBo queryBo) {
|
||||
String jobId = UUID.randomUUID().toString().replace("-", "");
|
||||
String wsType = WS_TYPE_PREFIX + jobId;
|
||||
|
||||
String auth = captureAuthorization();
|
||||
log.info("[OEE][summary][job] created jobId={}, wsType={}, window={}~{}, lines={}, authPresent={}",
|
||||
jobId,
|
||||
wsType,
|
||||
queryBo == null ? null : queryBo.getStartDate(),
|
||||
queryBo == null ? null : queryBo.getEndDate(),
|
||||
queryBo == null ? null : queryBo.getLineIds(),
|
||||
auth != null && !auth.trim().isEmpty());
|
||||
|
||||
push(jobId, wsType, "running", 5, "任务已创建,准备生成汇总…", null, null);
|
||||
|
||||
OeeQueryBo qb = copyQueryBo(queryBo);
|
||||
EXECUTOR.submit(() -> run(jobId, wsType, qb, auth));
|
||||
|
||||
Map<String, Object> resp = new HashMap<>();
|
||||
resp.put("jobId", jobId);
|
||||
resp.put("wsType", wsType);
|
||||
return resp;
|
||||
}
|
||||
|
||||
private void run(String jobId, String wsType, OeeQueryBo queryBo, String authorization) {
|
||||
long t0 = System.currentTimeMillis();
|
||||
try {
|
||||
System.out.println("[OEE][summary][job] start jobId=" + jobId + ", wsType=" + wsType
|
||||
+ ", thread=" + Thread.currentThread().getName()
|
||||
+ ", authPresent=" + (authorization != null && !authorization.trim().isEmpty()));
|
||||
log.info("[OEE][summary][job] start jobId={}, wsType={}, thread={}, authPresent={}",
|
||||
jobId, wsType, Thread.currentThread().getName(), authorization != null && !authorization.trim().isEmpty());
|
||||
push(jobId, wsType, "running", 20, "正在聚合 KPI 与趋势…", null, null);
|
||||
OeeReportServiceImpl.setAuthOverride(authorization);
|
||||
List<OeeLineSummaryVo> lines = oeeReportService.summary(queryBo);
|
||||
push(jobId, wsType, "running", 90, "正在整理汇总结果…", null, null);
|
||||
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("lines", lines);
|
||||
|
||||
push(jobId, wsType, "success", 100, "汇总生成完成", data, null);
|
||||
System.out.println("[OEE][summary][job] success jobId=" + jobId + ", costMs=" + (System.currentTimeMillis() - t0)
|
||||
+ ", lineCount=" + (lines == null ? 0 : lines.size()));
|
||||
log.info("[OEE][summary][job] success jobId={}, costMs={}, lineCount={}",
|
||||
jobId, (System.currentTimeMillis() - t0), lines == null ? 0 : lines.size());
|
||||
} catch (Exception e) {
|
||||
log.warn("summary job failed, jobId={}", jobId, e);
|
||||
push(jobId, wsType, "failed", 100, "汇总生成失败", null, e.getMessage());
|
||||
System.out.println("[OEE][summary][job] failed jobId=" + jobId + ", costMs=" + (System.currentTimeMillis() - t0)
|
||||
+ ", msg=" + e.getMessage());
|
||||
log.warn("[OEE][summary][job] failed jobId={}, costMs={}, msg={}",
|
||||
jobId, (System.currentTimeMillis() - t0), e.getMessage());
|
||||
} finally {
|
||||
OeeReportServiceImpl.clearAuthOverride();
|
||||
System.out.println("[OEE][summary][job] end jobId=" + jobId + ", costMs=" + (System.currentTimeMillis() - t0));
|
||||
log.info("[OEE][summary][job] end jobId={}, costMs={}", jobId, (System.currentTimeMillis() - t0));
|
||||
}
|
||||
}
|
||||
|
||||
private void push(String jobId, String wsType, String status, int progress, String text,
|
||||
Map<String, Object> data, String errorMsg) {
|
||||
try {
|
||||
Map<String, Object> msg = new HashMap<>();
|
||||
msg.put("jobId", jobId);
|
||||
msg.put("status", status);
|
||||
msg.put("progress", progress);
|
||||
msg.put("text", text);
|
||||
if (data != null) {
|
||||
msg.put("data", data);
|
||||
}
|
||||
if (errorMsg != null && !errorMsg.trim().isEmpty()) {
|
||||
msg.put("errorMsg", errorMsg);
|
||||
}
|
||||
TypeWebSocketUtil.sendToType(wsType, objectMapper.writeValueAsString(msg));
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
private OeeQueryBo copyQueryBo(OeeQueryBo queryBo) {
|
||||
OeeQueryBo qb = new OeeQueryBo();
|
||||
if (queryBo == null) {
|
||||
return qb;
|
||||
}
|
||||
qb.setStartDate(queryBo.getStartDate());
|
||||
qb.setEndDate(queryBo.getEndDate());
|
||||
qb.setLineIds(queryBo.getLineIds());
|
||||
qb.setStartTime(queryBo.getStartTime());
|
||||
qb.setEndTime(queryBo.getEndTime());
|
||||
qb.setTopN(queryBo.getTopN());
|
||||
qb.setLossCategoryCode(queryBo.getLossCategoryCode());
|
||||
qb.setKeyword(queryBo.getKeyword());
|
||||
return qb;
|
||||
}
|
||||
|
||||
private String captureAuthorization() {
|
||||
try {
|
||||
ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
if (attrs == null || attrs.getRequest() == null) {
|
||||
return null;
|
||||
}
|
||||
HttpServletRequest req = attrs.getRequest();
|
||||
String auth = req.getHeader("Authorization");
|
||||
if (auth != null && !auth.trim().isEmpty()) {
|
||||
return auth.trim();
|
||||
}
|
||||
String token = req.getHeader("token");
|
||||
if (token != null && !token.trim().isEmpty()) {
|
||||
return "Bearer " + token.trim();
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,182 +0,0 @@
|
||||
package com.klp.da.service.impl;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.klp.da.domain.bo.OeeQueryBo;
|
||||
import com.klp.da.service.IOeeReportService;
|
||||
import com.klp.da.service.OeeTheoryCycleJobService;
|
||||
import com.klp.framework.websocket.TypeWebSocketUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class OeeTheoryCycleJobServiceImpl implements OeeTheoryCycleJobService {
|
||||
|
||||
/**
|
||||
* 推送 type 前缀:前端用 ws://host/websocket?type=oee_theory_cycle_regression:{jobId} 订阅。
|
||||
*/
|
||||
public static final String WS_TYPE_PREFIX = "oee_theory_cycle_regression:";
|
||||
|
||||
private static final long TTL_MS = 10 * 60_000L;
|
||||
private static final ConcurrentHashMap<String, JobState> JOBS = new ConcurrentHashMap<>();
|
||||
|
||||
private static final ExecutorService EXECUTOR = Executors.newFixedThreadPool(2, r -> {
|
||||
Thread t = new Thread(r);
|
||||
t.setName("oee-theory-cycle-job");
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
});
|
||||
|
||||
private final IOeeReportService oeeReportService;
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
@Override
|
||||
public Map<String, Object> createAndStart(OeeQueryBo queryBo) {
|
||||
String jobId = UUID.randomUUID().toString().replace("-", "");
|
||||
String wsType = WS_TYPE_PREFIX + jobId;
|
||||
|
||||
JobState st = new JobState();
|
||||
st.updatedAt = System.currentTimeMillis();
|
||||
JOBS.put(jobId, st);
|
||||
|
||||
String auth = captureAuthorization();
|
||||
log.info("[OEE][theoryCycle][job] created jobId={}, wsType={}, window={}~{}, lines={}, authPresent={}",
|
||||
jobId,
|
||||
wsType,
|
||||
queryBo == null ? null : queryBo.getStartTime(),
|
||||
queryBo == null ? null : queryBo.getEndTime(),
|
||||
queryBo == null ? null : queryBo.getLineIds(),
|
||||
auth != null && !auth.trim().isEmpty());
|
||||
|
||||
// 立即推送“已创建”
|
||||
push(jobId, wsType, "running", 5, "任务已创建,准备计算…", null, null);
|
||||
|
||||
// 异步执行
|
||||
OeeQueryBo qb = copyQueryBo(queryBo);
|
||||
EXECUTOR.submit(() -> run(jobId, wsType, qb, auth));
|
||||
|
||||
Map<String, Object> resp = new HashMap<>();
|
||||
resp.put("jobId", jobId);
|
||||
resp.put("wsType", wsType);
|
||||
return resp;
|
||||
}
|
||||
|
||||
private void run(String jobId, String wsType, OeeQueryBo queryBo, String authorization) {
|
||||
long t0 = System.currentTimeMillis();
|
||||
try {
|
||||
cleanupExpired();
|
||||
System.out.println("[OEE][theoryCycle][job] start jobId=" + jobId + ", wsType=" + wsType
|
||||
+ ", thread=" + Thread.currentThread().getName()
|
||||
+ ", authPresent=" + (authorization != null && !authorization.trim().isEmpty()));
|
||||
log.info("[OEE][theoryCycle][job] start jobId={}, wsType={}, thread={}, authPresent={}",
|
||||
jobId, wsType, Thread.currentThread().getName(), authorization != null && !authorization.trim().isEmpty());
|
||||
push(jobId, wsType, "running", 20, "开始计算理论节拍回归…", null, null);
|
||||
|
||||
// 这里复用现有 WMS 同步回归聚合逻辑(内部会走 WMS 接口)
|
||||
push(jobId, wsType, "running", 50, "正在拉取样本并拟合回归…", null, null);
|
||||
OeeReportServiceImpl.setAuthOverride(authorization);
|
||||
Map<String, Object> data = oeeReportService.theoryCycleRegression(queryBo);
|
||||
|
||||
push(jobId, wsType, "running", 90, "正在整理回归结果…", null, null);
|
||||
JobState st = JOBS.get(jobId);
|
||||
if (st != null) {
|
||||
st.updatedAt = System.currentTimeMillis();
|
||||
}
|
||||
push(jobId, wsType, "success", 100, "回归计算完成", data, null);
|
||||
System.out.println("[OEE][theoryCycle][job] success jobId=" + jobId + ", costMs=" + (System.currentTimeMillis() - t0));
|
||||
log.info("[OEE][theoryCycle][job] success jobId={}, costMs={}", jobId, (System.currentTimeMillis() - t0));
|
||||
} catch (Exception e) {
|
||||
log.warn("theoryCycle job failed, jobId={}", jobId, e);
|
||||
JobState st = JOBS.get(jobId);
|
||||
if (st != null) {
|
||||
st.updatedAt = System.currentTimeMillis();
|
||||
}
|
||||
push(jobId, wsType, "failed", 100, "回归计算失败", null, e.getMessage());
|
||||
System.out.println("[OEE][theoryCycle][job] failed jobId=" + jobId + ", costMs=" + (System.currentTimeMillis() - t0)
|
||||
+ ", msg=" + e.getMessage());
|
||||
log.warn("[OEE][theoryCycle][job] failed jobId={}, costMs={}, msg={}",
|
||||
jobId, (System.currentTimeMillis() - t0), e.getMessage());
|
||||
} finally {
|
||||
OeeReportServiceImpl.clearAuthOverride();
|
||||
System.out.println("[OEE][theoryCycle][job] end jobId=" + jobId + ", costMs=" + (System.currentTimeMillis() - t0));
|
||||
log.info("[OEE][theoryCycle][job] end jobId={}, costMs={}", jobId, (System.currentTimeMillis() - t0));
|
||||
}
|
||||
}
|
||||
|
||||
private void push(String jobId, String wsType, String status, int progress, String text,
|
||||
Map<String, Object> data, String errorMsg) {
|
||||
try {
|
||||
Map<String, Object> msg = new HashMap<>();
|
||||
msg.put("jobId", jobId);
|
||||
msg.put("status", status);
|
||||
msg.put("progress", progress);
|
||||
msg.put("text", text);
|
||||
if (data != null) {
|
||||
msg.put("data", data);
|
||||
}
|
||||
if (errorMsg != null && !errorMsg.trim().isEmpty()) {
|
||||
msg.put("errorMsg", errorMsg);
|
||||
}
|
||||
TypeWebSocketUtil.sendToType(wsType, objectMapper.writeValueAsString(msg));
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
private void cleanupExpired() {
|
||||
long now = System.currentTimeMillis();
|
||||
JOBS.entrySet().removeIf(e -> e.getValue() == null || (now - e.getValue().updatedAt) > TTL_MS);
|
||||
}
|
||||
|
||||
private OeeQueryBo copyQueryBo(OeeQueryBo queryBo) {
|
||||
OeeQueryBo qb = new OeeQueryBo();
|
||||
if (queryBo == null) {
|
||||
return qb;
|
||||
}
|
||||
qb.setStartDate(queryBo.getStartDate());
|
||||
qb.setEndDate(queryBo.getEndDate());
|
||||
qb.setLineIds(queryBo.getLineIds());
|
||||
qb.setStartTime(queryBo.getStartTime());
|
||||
qb.setEndTime(queryBo.getEndTime());
|
||||
qb.setTopN(queryBo.getTopN());
|
||||
qb.setLossCategoryCode(queryBo.getLossCategoryCode());
|
||||
qb.setKeyword(queryBo.getKeyword());
|
||||
return qb;
|
||||
}
|
||||
|
||||
private String captureAuthorization() {
|
||||
try {
|
||||
ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
if (attrs == null || attrs.getRequest() == null) {
|
||||
return null;
|
||||
}
|
||||
HttpServletRequest req = attrs.getRequest();
|
||||
String auth = req.getHeader("Authorization");
|
||||
if (auth != null && !auth.trim().isEmpty()) {
|
||||
return auth.trim();
|
||||
}
|
||||
String token = req.getHeader("token");
|
||||
if (token != null && !token.trim().isEmpty()) {
|
||||
return "Bearer " + token.trim();
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static class JobState {
|
||||
long updatedAt;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user