|
@@ -4,23 +4,35 @@ import com.jeesite.common.lang.DateUtils;
|
|
|
import com.jeesite.common.lang.StringUtils;
|
|
import com.jeesite.common.lang.StringUtils;
|
|
|
import com.jeesite.common.service.ServiceException;
|
|
import com.jeesite.common.service.ServiceException;
|
|
|
import com.jeesite.modules.mes.entity.*;
|
|
import com.jeesite.modules.mes.entity.*;
|
|
|
|
|
+import com.jeesite.modules.mes.service.MesDingMsgConfigService;
|
|
|
|
|
+import com.jeesite.modules.sys.utils.DictUtils;
|
|
|
|
|
+import org.slf4j.Logger;
|
|
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
import org.springframework.scheduling.annotation.Async;
|
|
import org.springframework.scheduling.annotation.Async;
|
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
|
|
|
|
+import java.util.ArrayList;
|
|
|
import java.util.Date;
|
|
import java.util.Date;
|
|
|
|
|
+import java.util.HashMap;
|
|
|
import java.util.List;
|
|
import java.util.List;
|
|
|
|
|
+import java.util.Map;
|
|
|
import java.util.stream.Collectors;
|
|
import java.util.stream.Collectors;
|
|
|
import java.util.stream.Stream;
|
|
import java.util.stream.Stream;
|
|
|
|
|
|
|
|
@Service
|
|
@Service
|
|
|
public class MesDingTalkAlertService {
|
|
public class MesDingTalkAlertService {
|
|
|
|
|
|
|
|
|
|
+ private static final Logger logger = LoggerFactory.getLogger(MesDingTalkAlertService.class);
|
|
|
|
|
+
|
|
|
private final DingTalkRobotClient robotClient;
|
|
private final DingTalkRobotClient robotClient;
|
|
|
private final DingTalkAppClient appClient;
|
|
private final DingTalkAppClient appClient;
|
|
|
|
|
+ private final MesDingMsgConfigService mesDingMsgConfigService;
|
|
|
|
|
|
|
|
- public MesDingTalkAlertService(DingTalkRobotClient robotClient, DingTalkAppClient appClient) {
|
|
|
|
|
|
|
+ public MesDingTalkAlertService(DingTalkRobotClient robotClient, DingTalkAppClient appClient,
|
|
|
|
|
+ MesDingMsgConfigService mesDingMsgConfigService) {
|
|
|
this.robotClient = robotClient;
|
|
this.robotClient = robotClient;
|
|
|
this.appClient = appClient;
|
|
this.appClient = appClient;
|
|
|
|
|
+ this.mesDingMsgConfigService = mesDingMsgConfigService;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -40,9 +52,116 @@ public class MesDingTalkAlertService {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * 把配置渲染成钉钉 OA 卡片(占位符换成实际值)。
|
|
|
|
|
|
|
+ * 正式推送:消息已启用、产线已配接收人才发工作通知。
|
|
|
*/
|
|
*/
|
|
|
|
|
+ public boolean sendEnabled(String msgCode, String lineSn, Map<String, String> extra,
|
|
|
|
|
+ List<DingMsgCard.Item> extraItems) {
|
|
|
|
|
+ if (!mesDingMsgConfigService.isEnabled(msgCode)) {
|
|
|
|
|
+ logger.info("钉钉消息未启用,跳过:{}", msgCode);
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ MesDingMsgConfig config = mesDingMsgConfigService.getByMsgCode(msgCode);
|
|
|
|
|
+ if (config == null) {
|
|
|
|
|
+ logger.warn("钉钉消息配置不存在:{}", msgCode);
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ MesDingMsgLine line = mesDingMsgConfigService.findLine(config.getId(), lineSn);
|
|
|
|
|
+ if (line == null) {
|
|
|
|
|
+ logger.info("产线未配置接收人,跳过:msgCode={}, lineSn={}", msgCode, lineSn);
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ List<String> mobiles = splitMobiles(line.getMobiles());
|
|
|
|
|
+ if (mobiles.isEmpty()) {
|
|
|
|
|
+ logger.warn("产线接收人无手机号,跳过:lineSn={}", lineSn);
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ DingMsgCard card = renderCard(config, line, extra);
|
|
|
|
|
+ if (extraItems != null) {
|
|
|
|
|
+ for (DingMsgCard.Item item : extraItems) {
|
|
|
|
|
+ if (item == null || (StringUtils.isBlank(item.getLabel()) && StringUtils.isBlank(item.getValue()))) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ card.addItem(item.getLabel(), item.getValue());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ boolean ok = appClient.sendOa("MES通知", card.getTitle(), card.toOaForm(), mobiles);
|
|
|
|
|
+ if (!ok) {
|
|
|
|
|
+ logger.warn("钉钉工作通知发送失败:msgCode={}, lineSn={}", msgCode, lineSn);
|
|
|
|
|
+ }
|
|
|
|
|
+ return ok;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 正式推送:发给该消息已配置的全部产线接收人(手机号去重)。
|
|
|
|
|
+ * 维护计划本身不绑产线时用这个。
|
|
|
|
|
+ */
|
|
|
|
|
+ public boolean sendEnabledAllLines(String msgCode, Map<String, String> extra,
|
|
|
|
|
+ List<DingMsgCard.Item> extraItems) {
|
|
|
|
|
+ if (!mesDingMsgConfigService.isEnabled(msgCode)) {
|
|
|
|
|
+ logger.info("钉钉消息未启用,跳过:{}", msgCode);
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ MesDingMsgConfig config = mesDingMsgConfigService.getByMsgCode(msgCode);
|
|
|
|
|
+ if (config == null) {
|
|
|
|
|
+ logger.warn("钉钉消息配置不存在:{}", msgCode);
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ List<MesDingMsgLine> lines = config.getMesDingMsgLineList();
|
|
|
|
|
+ if (lines == null || lines.isEmpty()) {
|
|
|
|
|
+ logger.info("未配置任何产线接收人,跳过:{}", msgCode);
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ List<String> mobiles = new java.util.ArrayList<String>();
|
|
|
|
|
+ List<String> lineTitles = new java.util.ArrayList<String>();
|
|
|
|
|
+ List<String> userNames = new java.util.ArrayList<String>();
|
|
|
|
|
+ for (MesDingMsgLine line : lines) {
|
|
|
|
|
+ if (line == null) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (StringUtils.isNotBlank(line.getLineTitle())) {
|
|
|
|
|
+ lineTitles.add(line.getLineTitle());
|
|
|
|
|
+ }
|
|
|
|
|
+ if (StringUtils.isNotBlank(line.getUserNames())) {
|
|
|
|
|
+ userNames.add(line.getUserNames());
|
|
|
|
|
+ }
|
|
|
|
|
+ for (String mobile : splitMobiles(line.getMobiles())) {
|
|
|
|
|
+ if (!mobiles.contains(mobile)) {
|
|
|
|
|
+ mobiles.add(mobile);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (mobiles.isEmpty()) {
|
|
|
|
|
+ logger.warn("已配产线均无有效手机号,跳过:{}", msgCode);
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ MesDingMsgLine merged = new MesDingMsgLine();
|
|
|
|
|
+ merged.setLineSn("");
|
|
|
|
|
+ merged.setLineTitle(String.join("、", lineTitles));
|
|
|
|
|
+ merged.setUserNames(String.join("、", userNames));
|
|
|
|
|
+ DingMsgCard card = renderCard(config, merged, extra);
|
|
|
|
|
+ if (extraItems != null) {
|
|
|
|
|
+ for (DingMsgCard.Item item : extraItems) {
|
|
|
|
|
+ if (item == null || (StringUtils.isBlank(item.getLabel()) && StringUtils.isBlank(item.getValue()))) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ card.addItem(item.getLabel(), item.getValue());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ boolean ok = appClient.sendOa("MES通知", card.getTitle(), card.toOaForm(), mobiles);
|
|
|
|
|
+ if (!ok) {
|
|
|
|
|
+ logger.warn("钉钉工作通知发送失败:msgCode={}", msgCode);
|
|
|
|
|
+ }
|
|
|
|
|
+ return ok;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
public DingMsgCard renderCard(MesDingMsgConfig config, MesDingMsgLine line) {
|
|
public DingMsgCard renderCard(MesDingMsgConfig config, MesDingMsgLine line) {
|
|
|
|
|
+ return renderCard(config, line, null);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 把配置渲染成钉钉 OA 卡片(占位符换成实际值)。
|
|
|
|
|
+ */
|
|
|
|
|
+ public DingMsgCard renderCard(MesDingMsgConfig config, MesDingMsgLine line, Map<String, String> extra) {
|
|
|
String content = config == null ? "" : config.getMsgContent();
|
|
String content = config == null ? "" : config.getMsgContent();
|
|
|
DingMsgCard card = DingMsgCard.parse(content);
|
|
DingMsgCard card = DingMsgCard.parse(content);
|
|
|
if (card.getItems().isEmpty() && config != null) {
|
|
if (card.getItems().isEmpty() && config != null) {
|
|
@@ -57,7 +176,7 @@ public class MesDingTalkAlertService {
|
|
|
String msgName = config == null ? "" : StringUtils.defaultString(config.getMsgName());
|
|
String msgName = config == null ? "" : StringUtils.defaultString(config.getMsgName());
|
|
|
String triggerDesc = config == null ? "" : StringUtils.defaultString(config.getTriggerDesc());
|
|
String triggerDesc = config == null ? "" : StringUtils.defaultString(config.getTriggerDesc());
|
|
|
String time = DateUtils.formatDateTime(new Date());
|
|
String time = DateUtils.formatDateTime(new Date());
|
|
|
- return card.applyPlaceholders(msgName, time, lineTitle, lineSn, userNames, triggerDesc);
|
|
|
|
|
|
|
+ return card.applyPlaceholders(msgName, time, lineTitle, lineSn, userNames, triggerDesc, extra);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -115,6 +234,151 @@ public class MesDingTalkAlertService {
|
|
|
field("结果", alarm.getResult()),
|
|
field("结果", alarm.getResult()),
|
|
|
field("记录ID", alarm.getId())));
|
|
field("记录ID", alarm.getId())));
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * PDA 抽检提交:仅当结果为 NG 时,按检查类型推对应消息给该产线接收人。
|
|
|
|
|
+ * 1首件 / 2中间件(抽检) / 3末件。
|
|
|
|
|
+ */
|
|
|
|
|
+ @Async
|
|
|
|
|
+ public void sendPdaQualityNg(MesQuality quality) {
|
|
|
|
|
+ if (quality == null) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!isNg(quality.getResult())) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ DingMsgCode code = qualityNgCode(quality.getType());
|
|
|
|
|
+ if (code == null) {
|
|
|
|
|
+ logger.info("抽检NG类型未匹配消息:type={}", quality.getType());
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (StringUtils.isBlank(quality.getLineSn())) {
|
|
|
|
|
+ logger.info("抽检NG无产线编号,跳过:{}", code.getCode());
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ String typeName = DictUtils.getDictLabel("mes_quality_type", quality.getType(), quality.getType());
|
|
|
|
|
+ Map<String, String> extra = new HashMap<String, String>();
|
|
|
|
|
+ extra.put("工件码", StringUtils.defaultString(quality.getSn()));
|
|
|
|
|
+ extra.put("工位", StringUtils.defaultString(quality.getOprno()));
|
|
|
|
|
+ extra.put("检查类型", StringUtils.defaultString(typeName));
|
|
|
|
|
+ extra.put("检查结果", StringUtils.defaultString(quality.getResult()));
|
|
|
|
|
+ extra.put("备注", StringUtils.defaultString(quality.getRemark()));
|
|
|
|
|
+ List<DingMsgCard.Item> extraItems = new ArrayList<DingMsgCard.Item>();
|
|
|
|
|
+ extraItems.add(new DingMsgCard.Item("工件码", extra.get("工件码")));
|
|
|
|
|
+ extraItems.add(new DingMsgCard.Item("工位", extra.get("工位")));
|
|
|
|
|
+ extraItems.add(new DingMsgCard.Item("检查类型", extra.get("检查类型")));
|
|
|
|
|
+ extraItems.add(new DingMsgCard.Item("检查结果", extra.get("检查结果")));
|
|
|
|
|
+ if (StringUtils.isNotBlank(quality.getRemark())) {
|
|
|
|
|
+ extraItems.add(new DingMsgCard.Item("备注", extra.get("备注")));
|
|
|
|
|
+ }
|
|
|
|
|
+ sendEnabled(code.getCode(), quality.getLineSn(), extra, extraItems);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private DingMsgCode qualityNgCode(String type) {
|
|
|
|
|
+ if (MesQuality.TYPE_FIRST.equals(type)) {
|
|
|
|
|
+ return DingMsgCode.QM_FIRST_NG;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (MesQuality.TYPE_SPOT.equals(type)) {
|
|
|
|
|
+ return DingMsgCode.QM_SPOT_NG;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (MesQuality.TYPE_LAST.equals(type)) {
|
|
|
|
|
+ return DingMsgCode.QM_LAST_NG;
|
|
|
|
|
+ }
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private boolean isNg(String result) {
|
|
|
|
|
+ return StringUtils.isNotBlank(result) && "NG".equalsIgnoreCase(result.trim());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 返修检查 OK、工件重新上线时推送。对应接口 mesRepairQuality/add,result=1。
|
|
|
|
|
+ */
|
|
|
|
|
+ @Async
|
|
|
|
|
+ public void sendRepairOnline(MesRepair repair) {
|
|
|
|
|
+ if (repair == null) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!"1".equals(repair.getResult())) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (StringUtils.isBlank(repair.getLineSn())) {
|
|
|
|
|
+ logger.info("返修重新上线无产线编号,跳过:sn={}", repair.getSn());
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ Map<String, String> extra = new HashMap<String, String>();
|
|
|
|
|
+ extra.put("工件码", StringUtils.defaultString(repair.getSn()));
|
|
|
|
|
+ extra.put("责任工位", StringUtils.defaultString(repair.getOprno()));
|
|
|
|
|
+ extra.put("返回工位", StringUtils.defaultString(repair.getReturnOprno()));
|
|
|
|
|
+ extra.put("返修工位", StringUtils.defaultString(repair.getFxOprno()));
|
|
|
|
|
+ extra.put("返修原因", StringUtils.defaultString(repair.getItemTitle()));
|
|
|
|
|
+ extra.put("检查结果", "OK");
|
|
|
|
|
+ List<DingMsgCard.Item> extraItems = new ArrayList<DingMsgCard.Item>();
|
|
|
|
|
+ extraItems.add(new DingMsgCard.Item("工件码", extra.get("工件码")));
|
|
|
|
|
+ extraItems.add(new DingMsgCard.Item("责任工位", extra.get("责任工位")));
|
|
|
|
|
+ extraItems.add(new DingMsgCard.Item("返回工位", extra.get("返回工位")));
|
|
|
|
|
+ extraItems.add(new DingMsgCard.Item("返修工位", extra.get("返修工位")));
|
|
|
|
|
+ if (StringUtils.isNotBlank(repair.getItemTitle())) {
|
|
|
|
|
+ extraItems.add(new DingMsgCard.Item("返修原因", extra.get("返修原因")));
|
|
|
|
|
+ }
|
|
|
|
|
+ extraItems.add(new DingMsgCard.Item("检查结果", extra.get("检查结果")));
|
|
|
|
|
+ sendEnabled(DingMsgCode.QM_REPAIR_ONLINE.getCode(), repair.getLineSn(), extra, extraItems);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 报废提交时推送。对应接口 mesScrap/add。
|
|
|
|
|
+ * 报废表无产线,由调用方从生产记录传入 lineSn;没有产线则发给该消息已配置的全部接收人。
|
|
|
|
|
+ */
|
|
|
|
|
+ @Async
|
|
|
|
|
+ public void sendScrap(MesScrap scrap, String lineSn) {
|
|
|
|
|
+ if (scrap == null) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ Map<String, String> extra = new HashMap<String, String>();
|
|
|
|
|
+ extra.put("工件码", StringUtils.defaultString(scrap.getSn()));
|
|
|
|
|
+ extra.put("产品类型", StringUtils.defaultString(scrap.getProductCate()));
|
|
|
|
|
+ extra.put("报废原因", StringUtils.defaultString(scrap.getReason()));
|
|
|
|
|
+ List<DingMsgCard.Item> extraItems = new ArrayList<DingMsgCard.Item>();
|
|
|
|
|
+ extraItems.add(new DingMsgCard.Item("工件码", extra.get("工件码")));
|
|
|
|
|
+ if (StringUtils.isNotBlank(scrap.getProductCate())) {
|
|
|
|
|
+ extraItems.add(new DingMsgCard.Item("产品类型", extra.get("产品类型")));
|
|
|
|
|
+ }
|
|
|
|
|
+ extraItems.add(new DingMsgCard.Item("报废原因", extra.get("报废原因")));
|
|
|
|
|
+ if (StringUtils.isNotBlank(lineSn)) {
|
|
|
|
|
+ sendEnabled(DingMsgCode.QM_SCRAP.getCode(), lineSn, extra, extraItems);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ sendEnabledAllLines(DingMsgCode.QM_SCRAP.getCode(), extra, extraItems);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 当班同产品不良数达到 4 / 11 / 51 时分别推送超过 3 / 10 / 50。
|
|
|
|
|
+ */
|
|
|
|
|
+ @Async
|
|
|
|
|
+ public void sendShiftProductNg(String lineSn, String cateSn, String oprno, String sn, long ngCount) {
|
|
|
|
|
+ DingMsgCode code = null;
|
|
|
|
|
+ if (ngCount == 4L) {
|
|
|
|
|
+ code = DingMsgCode.QM_NG_3;
|
|
|
|
|
+ } else if (ngCount == 11L) {
|
|
|
|
|
+ code = DingMsgCode.QM_NG_10;
|
|
|
|
|
+ } else if (ngCount == 51L) {
|
|
|
|
|
+ code = DingMsgCode.QM_NG_50;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (code == null || StringUtils.isBlank(lineSn)) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ Map<String, String> extra = new HashMap<String, String>();
|
|
|
|
|
+ extra.put("当班不良数", String.valueOf(ngCount));
|
|
|
|
|
+ extra.put("产品类型", StringUtils.defaultString(cateSn));
|
|
|
|
|
+ extra.put("工件码", StringUtils.defaultString(sn));
|
|
|
|
|
+ extra.put("工位", StringUtils.defaultString(oprno));
|
|
|
|
|
+ List<DingMsgCard.Item> extraItems = new ArrayList<DingMsgCard.Item>();
|
|
|
|
|
+ extraItems.add(new DingMsgCard.Item("当班不良数", extra.get("当班不良数")));
|
|
|
|
|
+ extraItems.add(new DingMsgCard.Item("产品类型", extra.get("产品类型")));
|
|
|
|
|
+ extraItems.add(new DingMsgCard.Item("工件码", extra.get("工件码")));
|
|
|
|
|
+ extraItems.add(new DingMsgCard.Item("工位", extra.get("工位")));
|
|
|
|
|
+ sendEnabled(code.getCode(), lineSn, extra, extraItems);
|
|
|
|
|
+ }
|
|
|
@Async
|
|
@Async
|
|
|
public void sendDeviceAlarm(MesDeviceAlarm alarm) {
|
|
public void sendDeviceAlarm(MesDeviceAlarm alarm) {
|
|
|
sendMarkdown("设备异常预警", buildMessage("设备异常预警",
|
|
sendMarkdown("设备异常预警", buildMessage("设备异常预警",
|