工艺日志推送

bom日志推送1
This commit is contained in:
tzy 2025-11-11 21:15:04 +08:00
parent 2cc73d72d7
commit e2dadf1c27
8 changed files with 530 additions and 6 deletions

View File

@ -76,9 +76,9 @@ spring:
servlet:
multipart:
# 单个文件大小
max-file-size: 10MB
max-file-size: 50MB
# 设置总上传的文件大小
max-request-size: 20MB
max-request-size: 50MB
# 服务模块
devtools:
restart:
@ -149,12 +149,12 @@ security:
- /system/proPlan/list2
- /system/mrp/**
- /system/orderPro/**
- /system/cost/**
# MyBatisPlus配置
# https://baomidou.com/config/
mybatis-plus:
mapperPath:
# 不支持多包, 如有需要可在注解配置 或 提升扫包等级
mapperPath: # 不支持多包, 如有需要可在注解配置 或 提升扫包等级
# 例如 com.**.**.mapper
mapperPackage: com.ruoyi.**.mapper
# 对应的 XML 文件位置

View File

@ -88,5 +88,79 @@ public interface Constants {
public static final String ETP_URL = "/1.1.9 ETP-P系列减速器标准图纸/";
public static final String ETH_URL = "/1.1.10 ETH-H系列减速器标准图纸/";
/**
* 企业ID
*/
String CORPORATE_ID = "ww3cd1a87df502cf3a";
/**
* 应用Id
*/
String APPLICATION_ID = "cyGYk5QxLTn-k3_QG_nO6EANAigqBWhdDaqy_3sF6Y0";
String AgentId = "1000006";
/**
* 苏硕
*/
String Y = "y";
/**
* 苏硕
*/
String SuShuo = "SuShuo";
/**
* 闫建楼
*/
String YanJianLou = "YanJianLou";
/**
* 何春玲
*/
String HeChunLing = "HeChunLing";
/**
* 曹盼
*/
String 曹盼 = "-";
/**
* 孙灵海
*/
String YeZhen = "YeZhen";
/**
* 师敏玲
*/
String PingAnXiLe = "PingAnXiLe";
/**
* 李祎泽
*/
String ronin = "ronin";
/**
* 吴海静
*/
String JingJing = "JingJing";
/**
* 李祎晗
*/
String LiYiHan = "LiYiHan";
/**
* 陈志学
*/
String 陈志学 = "-";
/**
* 马天宇
*/
String MaTianYu = "MaTianYu";
/**
* 康建新
*/
String JianXin = "JianXin";
/**
* 刘冉
*/
String ChengChengChengChengCheng = "ChengChengChengChengCheng";
/**
* 张敏
*/
String lingyan = "lingyan";
String WangXuDe = "WangXuDe";
}

View File

@ -6,7 +6,7 @@
<artifactId>ruoyi-vue-plus</artifactId>
<groupId>com.ruoyi</groupId>
<version>4.7.0</version>
</parent><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>8</source><target>8</target></configuration></plugin></plugins></build>
</parent><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>1.8</source><target>1.8</target></configuration></plugin></plugins></build>
<modelVersion>4.0.0</modelVersion>
<artifactId>ruoyi-system</artifactId>

View File

@ -1,6 +1,7 @@
package com.ruoyi.system.controller;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.time.format.DateTimeFormatter;
@ -30,6 +31,7 @@ import com.ruoyi.system.mapper.WlStockDataMapper;
import com.ruoyi.system.runner.JdUtil;
import com.ruoyi.system.service.IProcessRouteService;
import com.ruoyi.system.service.ISafetyStockService;
import com.ruoyi.system.service.IWeComService;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.RequiredArgsConstructor;
@ -58,9 +60,12 @@ import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.system.domain.vo.KingdeeWorkCenterDataVo;
import com.ruoyi.system.domain.bo.KingdeeWorkCenterDataBo;
import com.ruoyi.system.service.IKingdeeWorkCenterDataService;
import java.util.stream.Collectors;
import com.ruoyi.common.core.page.TableDataInfo;
import org.springframework.util.StringUtils;
import static com.ruoyi.common.constant.Constants.*;
/**
* 金蝶工段数据
*
@ -79,8 +84,12 @@ public class KingdeeWorkCenterDataController extends BaseController {
private static final Logger log = LoggerFactory.getLogger(KingdeeWorkCenterDataController.class);
private final WlStockDataMapper baseMapper;
private final ISafetyStockService iSafetyStockService;
private final IWeComService iWeComService;
@Autowired
IProcessRouteService iProcessRouteService;
private static final String QUALITY_KANBAN_URL = "http://192.168.5.8/K3Cloud/DataBoard/QualityKanBan/index.html#/";
/**
* 查询金蝶工段数据列表
*/
@ -1661,5 +1670,130 @@ public class KingdeeWorkCenterDataController extends BaseController {
return R.fail("发送工段数据失败:" + e.getMessage());
}
}
private String buildTextCardDescription(ProcessInspectionDTO pd) {
// 日期
String dateStr = formatDate(pd.getFSubmitInspectTime());
if (dateStr == null || dateStr.isEmpty()) {
dateStr = "今日";
}
// 基础信息
String moOrderNo = pd.getMoBillNo() == null ? "-" : pd.getMoBillNo();
String productionNo = pd.getMoOrderNo() == null ? "-" : pd.getMoOrderNo(); // 生产令号
String materialName = pd.getFMaterialName() == null ? "-" : pd.getFMaterialName();
String center = pd.getFWorkCenterName() == null ? "-" : pd.getFWorkCenterName();
String process = pd.getFProcessName() == null ? "-" : pd.getFProcessName();
// 检验统计
int inspected = pd.getFFinishInspectQty();
int pending = pd.getFWaitInspectQty();
int qualified = pd.getFQuaQty();
int scrap = pd.getFFailQty();
int rework = pd.getFReworkQty();
String status = pd.getFInspectStatus() == null ? "未完成" : pd.getFInspectStatus();
StringBuilder sb = new StringBuilder();
sb.append("<div class=\"gray\">交检时间:").append(dateStr).append("</div>");
sb.append("<div class=\"normal\">生产订单编号:").append(moOrderNo).append("</div>");
sb.append("<div class=\"normal\">生产令号:").append(productionNo).append("</div>");
sb.append("<div class=\"normal\">产品名称:").append(materialName).append("</div>");
sb.append("<div class=\"normal\">工段 / 工序:").append(center).append(" / ").append(process).append("</div>");
sb.append("<div class=\"normal\">已检:").append(inspected)
.append(" 待检:").append(pending)
.append(" 合格:").append(qualified)
.append("</div>");
sb.append("<div class=\"normal\">废品:").append(scrap)
.append(" 返修:").append(rework)
.append("</div>");
sb.append("<div class=\"highlight\">质检状态:").append(status).append("</div>");
return sb.toString();
}
/**
* 应用消息质检数据
*/
@XxlJob("getApplicationMessage")
@Log(title = "应用消息质检数据", businessType = BusinessType.DELETE)
@PostMapping("/getApplicationMessage")
public R<List<ProcessInspectionDTO>> getApplicationMessage() throws UnsupportedEncodingException {
List<ProcessInspectionDTO> allList = JdUtil.getUninspectedData();
if (allList.isEmpty()) {
log.info("没有获取到质检数据");
return R.ok(Collections.emptyList());
}
/*LocalDate today = LocalDate.now();
List<ProcessInspectionDTO> todayList = allList.stream().filter(pd -> {
LocalDate submit = parseDate(pd.getFSubmitInspectTime());
LocalDate finish = parseDate(pd.getFFinishInspectTime());
return (submit != null && submit.isEqual(today)) || (finish != null && finish.isEqual(today));
}).collect(Collectors.toList());
*/
/* if (todayList.isEmpty()) {
log.info("今日没有质检数据");
return R.ok(Collections.emptyList());
}*/
String accessToken = iWeComService.getAccessToken(CORPORATE_ID, APPLICATION_ID);
// 工段/工序 -> 接收人映射
Map<String, String> workCenterMap = new HashMap<>();
/*workCenterMap.put("铆焊工段", YanJianLou);
workCenterMap.put("机二工段", "y");
workCenterMap.put("机三工段", YeZhen + "|" + PingAnXiLe);
workCenterMap.put("装一工段", ronin + "|" + JingJing);
workCenterMap.put("装二工段", LiYiHan + "|" + MaTianYu);*/
workCenterMap.put("铆焊工段", ChengChengChengChengCheng);
workCenterMap.put("机二工段", "WangXuDe");
workCenterMap.put("机三工段", ChengChengChengChengCheng + "|" + WangXuDe);
workCenterMap.put("装一工段", ChengChengChengChengCheng + "|" + WangXuDe);
workCenterMap.put("装二工段", ChengChengChengChengCheng + "|" + WangXuDe);
// 特殊机一工段下料
workCenterMap.put("机一工段", Y);
for (ProcessInspectionDTO pd : allList) {
try {
String description = buildTextCardDescription(pd);
String title = "质检通知";
String workCenter = pd.getFWorkCenterName() == null ? "" : pd.getFWorkCenterName();
String process = pd.getFProcessName() == null ? "" : pd.getFProcessName();
String key = workCenter;
// 机一工段下料特殊判断
if ("机一工段".equals(workCenter) && process.contains("下料")) {
key = "机一工段下料";
}
String receivers = workCenterMap.get(key);
if (receivers != null && !receivers.isEmpty()) {
iWeComService.sendTextCardMessage(
accessToken,
AgentId,
receivers,
"",
"",
workCenter + title,
description,
QUALITY_KANBAN_URL,
"查看详情",
0,
0,
1800
);
} else {
log.warn("未找到接收人映射:工段={} 工序={}", workCenter, process);
}
} catch (Exception e) {
log.error("发送质检消息异常:{}", pd, e);
}
}
return R.ok(null);
}
}

View File

@ -0,0 +1,62 @@
package com.ruoyi.system.controller;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.system.service.IWeComService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.io.UnsupportedEncodingException;
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/system/wecom")
public class WeComController {
private final IWeComService weComService;
@PostMapping("/getToken")
public R<String> getToken(@RequestParam String corpId, @RequestParam String corpSecret) throws UnsupportedEncodingException {
String token = weComService.getAccessToken(corpId, corpSecret);
return R.ok(token);
}
@PostMapping("/sendText")
public R<String> sendText(@RequestParam String accessToken,
@RequestParam String agentId,
@RequestParam(required = false) String toUser,
@RequestParam(required = false) String toParty,
@RequestParam(required = false) String toTag,
@RequestParam String content,
@RequestParam(required = false) Integer safe,
@RequestParam(required = false, name = "enable_id_trans") Integer enableIdTrans,
@RequestParam(required = false, name = "enable_duplicate_check") Integer enableDuplicateCheck,
@RequestParam(required = false, name = "duplicate_check_interval") Integer duplicateCheckInterval) {
String result = weComService.sendTextMessage(accessToken, agentId, toUser, toParty, toTag, content, safe, enableIdTrans, enableDuplicateCheck, duplicateCheckInterval);
return R.ok(result);
}
@PostMapping("/sendMarkdown")
public R<String> sendMarkdown(@RequestParam String accessToken,
@RequestParam String agentId,
@RequestParam(required = false) String toUser,
@RequestParam(required = false) String toParty,
@RequestParam(required = false) String toTag,
@RequestParam String content,
@RequestParam(required = false) Integer safe,
@RequestParam(required = false, name = "enable_id_trans") Integer enableIdTrans,
@RequestParam(required = false, name = "enable_duplicate_check") Integer enableDuplicateCheck,
@RequestParam(required = false, name = "duplicate_check_interval") Integer duplicateCheckInterval) {
String result = weComService.sendMarkdownMessage(accessToken, agentId, toUser, toParty, toTag, content, safe, enableIdTrans, enableDuplicateCheck, duplicateCheckInterval);
return R.ok(result);
}
@PostMapping("/sendTextCard")
public R<String> sendTextCard(@RequestParam String accessToken, @RequestParam String agentId, @RequestParam(required = false) String toUser, @RequestParam(required = false) String toParty, @RequestParam(required = false) String toTag, @RequestParam String title,
@RequestParam String description, @RequestParam String url, @RequestParam(required = false) String btntxt, @RequestParam(required = false, name = "enable_id_trans") Integer enableIdTrans, @RequestParam(required = false, name = "enable_duplicate_check") Integer enableDuplicateCheck, @RequestParam(required = false, name = "duplicate_check_interval") Integer duplicateCheckInterval) {
String result = weComService.sendTextCardMessage(accessToken, agentId, toUser, toParty, toTag,
title, description, url, btntxt, enableIdTrans, enableDuplicateCheck, duplicateCheckInterval);
return R.ok(result);
}
}

View File

@ -0,0 +1,20 @@
package com.ruoyi.system.service;
import java.io.UnsupportedEncodingException;
public interface IWeComService {
String getAccessToken(String corpId, String corpSecret) throws UnsupportedEncodingException;
String sendTextMessage(String accessToken, String agentId, String toUser, String toParty, String toTag, String content,
Integer safe, Integer enableIdTrans, Integer enableDuplicateCheck, Integer duplicateCheckInterval
);
String sendMarkdownMessage(String accessToken, String agentId, String toUser, String toParty, String toTag, String content,
Integer safe, Integer enableIdTrans, Integer enableDuplicateCheck, Integer duplicateCheckInterval
);
String sendTextCardMessage(String accessToken, String agentId, String toUser, String toParty, String toTag,
String title, String description, String url, String btntxt,
Integer enableIdTrans, Integer enableDuplicateCheck, Integer duplicateCheckInterval
);
}

View File

@ -0,0 +1,234 @@
package com.ruoyi.system.service.impl;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.system.service.IWeComService;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Service
public class WeComServiceImpl implements IWeComService {
private final RestTemplate restTemplate = new RestTemplate();
private static class TokenEntry {
final String token;
final long expireAtMillis;
TokenEntry(String token, long expireAtMillis) {
this.token = token;
this.expireAtMillis = expireAtMillis;
}
}
private final Map<String, TokenEntry> tokenCache = new ConcurrentHashMap<>();
/**
* 获取企业微信 access_token
* @param corpId 企业 IDcorpid
* @param corpSecret 企业密钥corpsecret
* @return access_token 字符串
*/
@Override
public String getAccessToken(String corpId, String corpSecret) throws UnsupportedEncodingException {
String cacheKey = corpId + "|" + corpSecret;
TokenEntry cached = tokenCache.get(cacheKey);
long now = System.currentTimeMillis();
if (cached != null && cached.expireAtMillis > now) {
return cached.token;
}
String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=" + URLEncoder.encode(corpId, "UTF-8") + "&corpsecret=" + URLEncoder.encode(corpSecret, "UTF-8");
ResponseEntity<String> resp = restTemplate.getForEntity(url, String.class);
String body = resp.getBody();
if (body == null) {
throw new ServiceException("企业微信 gettoken 接口响应为空");
}
JSONObject json = JSONUtil.parseObj(body);
int errcode = json.getInt("errcode", -1);
if (errcode != 0) {
String errmsg = json.getStr("errmsg");
throw new ServiceException("企业微信 gettoken 调用失败,错误码: " + errcode + ",错误信息: " + errmsg);
}
String token = json.getStr("access_token");
int expiresIn = json.getInt("expires_in", 7200);
long expireAt = now + Math.max(0, (expiresIn - 300)) * 1000L; // 缓冲 5 分钟
tokenCache.put(cacheKey, new TokenEntry(token, expireAt));
return token;
}
/**
* 发送企业微信文本消息
*
* @param accessToken 已获取的 access_token
* @param agentId 应用的 AgentId数字
* @param toUser 目标用户多个用 | 分隔可为空
* @param toParty 目标部门多个用 | 分隔可为空
* @param toTag 目标标签多个用 | 分隔可为空
* @param content 文本内容
* @param safe 保密消息标志0 1
* @param enableIdTrans 是否开启 id 转译
* @param enableDuplicateCheck 是否开启重复消息检查
* @param duplicateCheckInterval 重复消息检查间隔
* @return 企业微信原始响应 JSON 字符串
*/
@Override
public String sendTextMessage(String accessToken,
String agentId,
String toUser,
String toParty,
String toTag,
String content,
Integer safe,
Integer enableIdTrans,
Integer enableDuplicateCheck,
Integer duplicateCheckInterval) {
String url = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=" + accessToken;
Map<String, Object> payload = new HashMap<>();
if (toUser != null && !toUser.isEmpty()) payload.put("touser", toUser);
if (toParty != null && !toParty.isEmpty()) payload.put("toparty", toParty);
if (toTag != null && !toTag.isEmpty()) payload.put("totag", toTag);
payload.put("msgtype", "text");
try {
payload.put("agentid", Integer.parseInt(agentId));
} catch (NumberFormatException e) {
throw new ServiceException("agentId 必须为数字");
}
Map<String, Object> text = new HashMap<>();
text.put("content", content);
payload.put("text", text);
if (safe != null) payload.put("safe", safe);
if (enableIdTrans != null) payload.put("enable_id_trans", enableIdTrans);
if (enableDuplicateCheck != null) payload.put("enable_duplicate_check", enableDuplicateCheck);
if (duplicateCheckInterval != null) payload.put("duplicate_check_interval", duplicateCheckInterval);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<>(JSONUtil.toJsonStr(payload), headers);
ResponseEntity<String> resp = restTemplate.postForEntity(url, entity, String.class);
String body = resp.getBody();
if (body == null) {
throw new ServiceException("企业微信 message/send 接口响应为空");
}
return body;
}
/**
* 发送企业微信 Markdown 消息
*
* @param accessToken 已获取的 access_token
* @param agentId 应用的 AgentId数字
* @param toUser 目标用户多个用 | 分隔可为空
* @param toParty 目标部门多个用 | 分隔可为空
* @param toTag 目标标签多个用 | 分隔可为空
* @param content Markdown 内容
* @param safe 保密消息标志0 1
* @param enableIdTrans 是否开启 id 转译
* @param enableDuplicateCheck 是否开启重复消息检查
* @param duplicateCheckInterval 重复消息检查间隔
* @return 企业微信原始响应 JSON 字符串
*/
@Override
public String sendMarkdownMessage(String accessToken, String agentId, String toUser, String toParty, String toTag, String content,Integer safe, Integer enableIdTrans,
Integer enableDuplicateCheck, Integer duplicateCheckInterval) {
String url = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=" + accessToken;
Map<String, Object> payload = new HashMap<>();
if (toUser != null && !toUser.isEmpty()) payload.put("touser", toUser);
if (toParty != null && !toParty.isEmpty()) payload.put("toparty", toParty);
if (toTag != null && !toTag.isEmpty()) payload.put("totag", toTag);
payload.put("msgtype", "markdown");
try {
payload.put("agentid", Integer.parseInt(agentId));
} catch (NumberFormatException e) {
throw new ServiceException("agentId 必须为数字");
}
Map<String, Object> markdown = new HashMap<>();
markdown.put("content", content);
payload.put("markdown", markdown);
if (safe != null) payload.put("safe", safe);
if (enableIdTrans != null) payload.put("enable_id_trans", enableIdTrans);
if (enableDuplicateCheck != null) payload.put("enable_duplicate_check", enableDuplicateCheck);
if (duplicateCheckInterval != null) payload.put("duplicate_check_interval", duplicateCheckInterval);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<>(JSONUtil.toJsonStr(payload), headers);
ResponseEntity<String> resp = restTemplate.postForEntity(url, entity, String.class);
String body = resp.getBody();
if (body == null) {
throw new ServiceException("企业微信 message/send 接口响应为空");
}
return body;
}
/**
* 发送企业微信文本卡片消息textcard
*
* @param accessToken 已获取的 access_token
* @param agentId 应用的 AgentId数字
* @param toUser 目标用户多个用 | 分隔可为空
* @param toParty 目标部门多个用 | 分隔可为空
* @param toTag 目标标签多个用 | 分隔可为空
* @param title 卡片标题
* @param description 卡片描述支持少量 HTMLgray/normal/highlight
* @param url 点击卡片跳转链接
* @param btntxt 按钮文案可选默认详情/更多
* @param enableIdTrans 是否开启 id 转译可选
* @param enableDuplicateCheck 是否开启重复消息检查可选
* @param duplicateCheckInterval 重复消息检查时间间隔可选
* @return 企业微信原始响应 JSON 字符串
*/
@Override
public String sendTextCardMessage(String accessToken, String agentId, String toUser, String toParty, String toTag, String title, String description, String url,
String btntxt, Integer enableIdTrans, Integer enableDuplicateCheck, Integer duplicateCheckInterval) {
String api = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=" + accessToken;
Map<String, Object> payload = new HashMap<>();
if (toUser != null && !toUser.isEmpty()) payload.put("touser", toUser);
if (toParty != null && !toParty.isEmpty()) payload.put("toparty", toParty);
if (toTag != null && !toTag.isEmpty()) payload.put("totag", toTag);
payload.put("msgtype", "textcard");
try {
payload.put("agentid", Integer.parseInt(agentId));
} catch (NumberFormatException e) {
throw new ServiceException("agentId 必须为数字");
}
Map<String, Object> textcard = new HashMap<>();
textcard.put("title", title);
textcard.put("description", description);
textcard.put("url", url);
if (btntxt != null && !btntxt.isEmpty()) textcard.put("btntxt", btntxt);
payload.put("textcard", textcard);
if (enableIdTrans != null) payload.put("enable_id_trans", enableIdTrans);
if (enableDuplicateCheck != null) payload.put("enable_duplicate_check", enableDuplicateCheck);
if (duplicateCheckInterval != null) payload.put("duplicate_check_interval", duplicateCheckInterval);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<>(JSONUtil.toJsonStr(payload), headers);
ResponseEntity<String> resp = restTemplate.postForEntity(api, entity, String.class);
String body = resp.getBody();
if (body == null) {
throw new ServiceException("企业微信 message/send 接口响应为空");
}
return body;
}
}