|
|
|
|
@ -0,0 +1,218 @@
|
|
|
|
|
package com.aucma.production.controller;
|
|
|
|
|
|
|
|
|
|
import com.aucma.common.annotation.Anonymous;
|
|
|
|
|
import com.aucma.common.constant.AnDonConstants;
|
|
|
|
|
import com.aucma.common.core.domain.AjaxResult;
|
|
|
|
|
import com.aucma.common.utils.DateUtils;
|
|
|
|
|
import com.aucma.production.domain.AndonBoardConfig;
|
|
|
|
|
import com.aucma.production.domain.AndonEvent;
|
|
|
|
|
import com.aucma.production.service.IAndonBoardConfigService;
|
|
|
|
|
import com.aucma.production.service.IAndonEventService;
|
|
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
|
import org.springframework.web.bind.annotation.GetMapping;
|
|
|
|
|
import org.springframework.web.bind.annotation.RequestMapping;
|
|
|
|
|
import org.springframework.web.bind.annotation.RequestParam;
|
|
|
|
|
import org.springframework.web.bind.annotation.RestController;
|
|
|
|
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.Comparator;
|
|
|
|
|
import java.util.Date;
|
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 安灯看板展示接口(对外展示使用)
|
|
|
|
|
*/
|
|
|
|
|
@RestController
|
|
|
|
|
@RequestMapping("/production/andonBoard")
|
|
|
|
|
public class AndonBoardController {
|
|
|
|
|
|
|
|
|
|
/** 安灯看板配置服务 */
|
|
|
|
|
@Autowired
|
|
|
|
|
private IAndonBoardConfigService andonBoardConfigService;
|
|
|
|
|
|
|
|
|
|
/** 安灯事件服务 */
|
|
|
|
|
@Autowired
|
|
|
|
|
private IAndonEventService andonEventService;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 根据看板编码获取看板配置与事件数据。
|
|
|
|
|
*
|
|
|
|
|
* 说明:
|
|
|
|
|
* - boardCode 作为对外展示的“唯一入口参数”;
|
|
|
|
|
* - 事件范围由看板配置的 productLineCode / stationCode 决定;
|
|
|
|
|
* - 返回 activeEvents(待处理/处理中)与 closedEvents(已解决/已取消)两组数据,便于大屏展示。
|
|
|
|
|
* - 接口使用 @Anonymous 标记,可免登录访问(大屏/外部展示使用)。
|
|
|
|
|
*/
|
|
|
|
|
@Anonymous
|
|
|
|
|
@GetMapping("/view")
|
|
|
|
|
public AjaxResult view(@RequestParam String boardCode) {
|
|
|
|
|
// 1) 参数校验:boardCode 必填
|
|
|
|
|
if (boardCode == null || boardCode.trim().isEmpty()) {
|
|
|
|
|
return AjaxResult.error("boardCode不能为空");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2) 查询启用的看板配置(同一编码可能存在多条记录,取最新一条)
|
|
|
|
|
AndonBoardConfig config = selectActiveBoardConfig(boardCode.trim());
|
|
|
|
|
if (config == null) {
|
|
|
|
|
return AjaxResult.error("看板配置不存在或未启用:" + boardCode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3) 根据看板配置的范围(产线/工位)查询事件
|
|
|
|
|
List<AndonEvent> allEvents = selectEventsByConfig(config);
|
|
|
|
|
|
|
|
|
|
// 4) 统计数量 + 拆分进行中/已关闭事件
|
|
|
|
|
Map<String, Long> stats = buildStats(allEvents);
|
|
|
|
|
List<AndonEvent> activeEvents = filterByStatus(allEvents, true);
|
|
|
|
|
List<AndonEvent> closedEvents = filterByStatus(allEvents, false);
|
|
|
|
|
|
|
|
|
|
// 5) 排序:进行中按状态/优先级/创建时间;已关闭按结束时间/更新时间
|
|
|
|
|
sortActiveEvents(activeEvents);
|
|
|
|
|
sortClosedEvents(closedEvents);
|
|
|
|
|
|
|
|
|
|
// 为大屏限制历史记录数量(避免数据过多影响渲染)
|
|
|
|
|
if (closedEvents.size() > 20) {
|
|
|
|
|
closedEvents = new ArrayList<>(closedEvents.subList(0, 20));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 6) 组装返回数据(大屏依赖 config / stats / activeEvents / closedEvents / serverTime)
|
|
|
|
|
Map<String, Object> data = new HashMap<>();
|
|
|
|
|
data.put("config", config);
|
|
|
|
|
data.put("stats", stats);
|
|
|
|
|
data.put("activeEvents", activeEvents);
|
|
|
|
|
data.put("closedEvents", closedEvents);
|
|
|
|
|
data.put("serverTime", DateUtils.getNowDate());
|
|
|
|
|
return AjaxResult.success(data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 查询指定看板编码下启用的配置记录。
|
|
|
|
|
* 若存在多条启用记录,则按更新时间(优先)/创建时间倒序取最新一条。
|
|
|
|
|
*/
|
|
|
|
|
private AndonBoardConfig selectActiveBoardConfig(String boardCode) {
|
|
|
|
|
AndonBoardConfig q = new AndonBoardConfig();
|
|
|
|
|
q.setBoardCode(boardCode);
|
|
|
|
|
q.setIsFlag(AnDonConstants.FLAG_VALID);
|
|
|
|
|
List<AndonBoardConfig> list = andonBoardConfigService.selectAndonBoardConfigList(q);
|
|
|
|
|
if (list == null || list.isEmpty()) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 按更新时间优先、否则按创建时间,倒序排序,取第一条(最新配置)
|
|
|
|
|
list.sort((a, b) -> {
|
|
|
|
|
Date ta = (a.getUpdateTime() != null ? a.getUpdateTime() : a.getCreateTime());
|
|
|
|
|
Date tb = (b.getUpdateTime() != null ? b.getUpdateTime() : b.getCreateTime());
|
|
|
|
|
if (ta == null && tb == null) return 0;
|
|
|
|
|
if (ta == null) return 1;
|
|
|
|
|
if (tb == null) return -1;
|
|
|
|
|
return tb.compareTo(ta);
|
|
|
|
|
});
|
|
|
|
|
return list.get(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 根据看板配置的作用域查询事件列表。
|
|
|
|
|
* 仅当配置字段非空时才作为过滤条件;为空表示不按该维度过滤。
|
|
|
|
|
*/
|
|
|
|
|
private List<AndonEvent> selectEventsByConfig(AndonBoardConfig config) {
|
|
|
|
|
AndonEvent q = new AndonEvent();
|
|
|
|
|
q.setIsFlag(AnDonConstants.FLAG_VALID);
|
|
|
|
|
// 产线范围过滤
|
|
|
|
|
if (config.getProductLineCode() != null && !config.getProductLineCode().trim().isEmpty()) {
|
|
|
|
|
q.setProductLineCode(config.getProductLineCode());
|
|
|
|
|
}
|
|
|
|
|
// 工位/工序范围过滤
|
|
|
|
|
if (config.getStationCode() != null && !config.getStationCode().trim().isEmpty()) {
|
|
|
|
|
q.setStationCode(config.getStationCode());
|
|
|
|
|
}
|
|
|
|
|
return andonEventService.selectAndonEventList(q);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 构建看板统计数据。
|
|
|
|
|
* 返回字段:pending / processing / resolved / cancelled / active / total。
|
|
|
|
|
*/
|
|
|
|
|
private Map<String, Long> buildStats(List<AndonEvent> events) {
|
|
|
|
|
long pending = 0;
|
|
|
|
|
long processing = 0;
|
|
|
|
|
long resolved = 0;
|
|
|
|
|
long cancelled = 0;
|
|
|
|
|
if (events != null) {
|
|
|
|
|
for (AndonEvent e : events) {
|
|
|
|
|
if (e == null) continue;
|
|
|
|
|
String s = e.getEventStatus();
|
|
|
|
|
if (AnDonConstants.EventStatus.PENDING.equals(s)) pending++;
|
|
|
|
|
else if (AnDonConstants.EventStatus.PROCESSING.equals(s)) processing++;
|
|
|
|
|
else if (AnDonConstants.EventStatus.RESOLVED.equals(s)) resolved++;
|
|
|
|
|
else if (AnDonConstants.EventStatus.CANCELLED.equals(s)) cancelled++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Map<String, Long> m = new HashMap<>();
|
|
|
|
|
m.put("pending", pending);
|
|
|
|
|
m.put("processing", processing);
|
|
|
|
|
m.put("resolved", resolved);
|
|
|
|
|
m.put("cancelled", cancelled);
|
|
|
|
|
m.put("active", pending + processing);
|
|
|
|
|
m.put("total", events == null ? 0L : (long) events.size());
|
|
|
|
|
return m;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 按状态将事件分为“进行中”和“已关闭”两组。
|
|
|
|
|
* active=true:待处理/处理中;active=false:已解决/已取消(以及其他未知状态)。
|
|
|
|
|
*/
|
|
|
|
|
private List<AndonEvent> filterByStatus(List<AndonEvent> events, boolean active) {
|
|
|
|
|
List<AndonEvent> list = new ArrayList<>();
|
|
|
|
|
if (events == null) return list;
|
|
|
|
|
for (AndonEvent e : events) {
|
|
|
|
|
if (e == null) continue;
|
|
|
|
|
String s = e.getEventStatus();
|
|
|
|
|
// 待处理/处理中 视为进行中
|
|
|
|
|
boolean isActive = AnDonConstants.EventStatus.PENDING.equals(s) || AnDonConstants.EventStatus.PROCESSING.equals(s);
|
|
|
|
|
if (active == isActive) {
|
|
|
|
|
list.add(e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return list;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 进行中事件排序:
|
|
|
|
|
* 1) 状态(待处理优先于处理中)
|
|
|
|
|
* 2) 优先级(数值越小越紧急;空值排最后)
|
|
|
|
|
* 3) 创建时间(越早越靠前;空值排最后)
|
|
|
|
|
*/
|
|
|
|
|
private void sortActiveEvents(List<AndonEvent> events) {
|
|
|
|
|
if (events == null) return;
|
|
|
|
|
events.sort(Comparator
|
|
|
|
|
.comparingInt((AndonEvent e) -> statusOrder(e.getEventStatus()))
|
|
|
|
|
.thenComparing(e -> e.getPriority() == null ? Long.MAX_VALUE : e.getPriority())
|
|
|
|
|
.thenComparing(AndonEvent::getCreateTime, Comparator.nullsLast(Date::compareTo))
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 已关闭事件排序:
|
|
|
|
|
* 1) 解决/结束时间 responseEndTime 倒序(最近关闭的在前;空值排最后)
|
|
|
|
|
* 2) 更新时间 updateTime 倒序(空值排最后)
|
|
|
|
|
*/
|
|
|
|
|
private void sortClosedEvents(List<AndonEvent> events) {
|
|
|
|
|
if (events == null) return;
|
|
|
|
|
events.sort(Comparator
|
|
|
|
|
.comparing(AndonEvent::getResponseEndTime, Comparator.nullsLast(Comparator.reverseOrder()))
|
|
|
|
|
.thenComparing(AndonEvent::getUpdateTime, Comparator.nullsLast(Comparator.reverseOrder()))
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 状态排序权重:待处理(0) < 处理中(1) < 已解决(2) < 已取消(3)。
|
|
|
|
|
*/
|
|
|
|
|
private int statusOrder(String status) {
|
|
|
|
|
if (AnDonConstants.EventStatus.PENDING.equals(status)) return 0;
|
|
|
|
|
if (AnDonConstants.EventStatus.PROCESSING.equals(status)) return 1;
|
|
|
|
|
if (AnDonConstants.EventStatus.RESOLVED.equals(status)) return 2;
|
|
|
|
|
if (AnDonConstants.EventStatus.CANCELLED.equals(status)) return 3;
|
|
|
|
|
return 99;
|
|
|
|
|
}
|
|
|
|
|
}
|