feat(andon): 添加安灯看板展示接口和前端列配置功能

- 新增 AndonBoardController 提供看板数据查询接口
- 实现根据看板编码获取配置与事件数据功能
- 添加进行中和已关闭事件的数据筛选和排序逻辑
- 支持按产线/工位范围动态过滤事件数据
- 提供看板统计信息包括待处理/处理中/已解决/已取消数量
- 实现前端展示字段配置的多种写法支持
- 添加逗号分隔字符串、JSON数组、JSON对象三种配置格式
- 支持区分进行中和已关闭表的独立列配置
- 提供字段标签、宽度、对齐方式等显示属性配置
master
zangch@mesnac.com 6 days ago
parent 4e8ab17d82
commit c26e4a7a8f

@ -167,6 +167,45 @@
- `isFlag`:是否有效;
- `remark`:备注。
**展示字段配置displayFields写法示例**
用于前端大屏动态决定列显示可用字符串、JSON 数组或对象:
```text
1) 逗号分隔字符串(两张表共用同列)
"callCode,stationCode,eventStatus,priority,createTime"
2) JSON 数组(两张表共用同列,可设置宽度/对齐)
[
"callCode",
"stationCode",
{ "field": "eventStatus", "label": "状态", "align": "center" }
]
3) JSON 对象区分“进行中”和“已关闭”列
{
"activeFields": [
"callCode",
"stationCode",
{ "field": "eventStatus", "label": "状态", "align": "center" },
"priority",
"createTime"
],
"closedFields": [
"callCode",
"stationCode",
"eventStatus",
{ "field": "responseEndTime", "label": "完成时间" },
{ "field": "resolution", "label": "解决措施" }
]
}
```
解析逻辑(前端 `views/board/andonBoard/index.vue`
- 为空或解析失败时回退默认列进行中callCode/callTypeCode/stationCode/deviceCode/eventStatus/priority/createTime/description已关闭callCode/callTypeCode/stationCode/deviceCode/eventStatus/responseEndTime/resolution/cancelReason
- 纯字符串或数组:进行中与已关闭共用同一列集合。
- 对象:优先读取 `activeFields` / `closedFields`(或 `active` / `closed`),否则回退到 `fields` 或整对象键值。
- 每列可附加 `label`、`width`、`align`;缺省时按字段名映射默认中文标题。
前端看板程序可根据 `boardCode` 读取配置,再按产线/工位范围实时拉取 AndonEvent 数据进行可视化展示。
---

@ -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;
}
}
Loading…
Cancel
Save