MES本地审批共用钉钉审批等配置

This commit is contained in:
geht
2026-06-10 16:33:44 +08:00
parent c4447b91dd
commit 617d47a3db
19 changed files with 980 additions and 36 deletions

View File

@@ -61,6 +61,11 @@ public interface ISysImChatService {
* @return 消息VO发送失败返回 null
*/
SysImMessageVO sendSystemSingleMessage(String fromUserId, String toUserId, Integer tenantId, String content, String msgType);
/**
* 审批待办卡片:从「工作通知」公众号推送给处理人。
*/
SysImMessageVO sendApprovalHandlerMessage(String toUserId, Integer tenantId, String content, String msgType);
//update-end---author:GHT ---date:2026-05-29 for【QH-MES审批流设计】系统单聊消息(绕过同部门校验,供审批等系统通知场景)-----

View File

@@ -36,6 +36,7 @@ import org.jeecg.modules.system.mapper.SysUserTenantMapper;
import org.jeecg.modules.system.service.ISysPermissionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
@@ -64,6 +65,13 @@ public class SysImChatServiceImpl implements ISysImChatService {
private static final String MSG_IMAGE_PREVIEW = "[图片]";
private static final String MSG_BIZ_RECORD_PREVIEW = "[业务数据]";
private static final String IM_RECORD_QUERY_KEY = "imRecordId";
/** IM 工作通知公众号账号(审批等系统消息统一从此账号推送) */
private static final String WORK_NOTIFY_USERNAME = "im_work_notify";
private static final String WORK_NOTIFY_REALNAME = "工作通知";
private static final String WORK_NOTIFY_USER_ID = "1995000000000000999";
private static final String CONTACT_TYPE_WORK_NOTIFY = "work_notify";
private static final String CONTACT_TYPE_USER = "user";
private volatile String workNotifyUserIdCache;
@Autowired
private ISysPermissionService sysPermissionService;
@@ -107,7 +115,11 @@ public class SysImChatServiceImpl implements ISysImChatService {
if (conversation != null) {
return buildConversationVo(conversation, userId, targetUserId);
}
validateTenantChat(userId, tenantId, orgCode, targetUserId);
//update-begin---author:GHT ---date:20260610 for【IM审批通用化】工作通知公众号会话免同部门校验-----------
if (!isWorkNotifyUser(targetUserId)) {
validateTenantChat(userId, tenantId, orgCode, targetUserId);
}
//update-end---author:GHT ---date:20260610 for【IM审批通用化】工作通知公众号会话免同部门校验-----------
//update-end---author:cursor ---date:20260528 for【IM聊天-OA】已有会话快速打开-----------
conversation = new SysImConversation();
conversation.setConvType(CONV_TYPE_SINGLE);
@@ -439,6 +451,11 @@ public class SysImChatServiceImpl implements ISysImChatService {
throw new JeecgBootException("消息内容不能为空");
}
assertMember(userId, dto.getConversationId());
//update-begin---author:GHT ---date:20260610 for【IM审批通用化】工作通知公众号只读,禁止用户发消息-----------
if (isWorkNotifyConversation(dto.getConversationId())) {
throw new JeecgBootException("工作通知为系统消息通道,不支持发送消息");
}
//update-end---author:GHT ---date:20260610 for【IM审批通用化】工作通知公众号只读,禁止用户发消息-----------
Date now = new Date();
SysImMessage message = new SysImMessage();
message.setConversationId(dto.getConversationId());
@@ -480,8 +497,8 @@ public class SysImChatServiceImpl implements ISysImChatService {
if (oConvertUtils.isEmpty(fromUserId) || oConvertUtils.isEmpty(toUserId) || oConvertUtils.isEmpty(content)) {
return null;
}
// 不给自己发送
if (fromUserId.equals(toUserId)) {
log.warn("IM系统消息跳过收发为同一人 toUserId={}", toUserId);
return null;
}
Integer tenant = tenantId == null ? 0 : tenantId;
@@ -500,8 +517,17 @@ public class SysImChatServiceImpl implements ISysImChatService {
conversation.setCreateTime(now);
conversation.setUpdateTime(now);
conversationMapper.insert(conversation);
createMember(conversation.getId(), fromUserId, now);
createMember(conversation.getId(), toUserId, now);
ensureMember(conversation.getId(), fromUserId, now);
if (!fromUserId.equals(toUserId)) {
ensureMember(conversation.getId(), toUserId, now);
}
} else {
//update-begin---author:GHT ---date:20260610 for【IM审批通用化】已有会话补全缺失成员-----------
ensureMember(conversation.getId(), fromUserId, now);
if (!fromUserId.equals(toUserId)) {
ensureMember(conversation.getId(), toUserId, now);
}
//update-end---author:GHT ---date:20260610 for【IM审批通用化】已有会话补全缺失成员-----------
}
// 写入消息
SysImMessage message = new SysImMessage();
@@ -522,6 +548,22 @@ public class SysImChatServiceImpl implements ISysImChatService {
pushChatMessage(conversation.getId(), fromUserId, messageVo, conversation.getConvType());
return messageVo;
}
//update-begin---author:GHT ---date:20260610 for【IM审批通用化】审批待办统一由系统账号发给处理人-----------
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public SysImMessageVO sendApprovalHandlerMessage(String toUserId, Integer tenantId, String content, String msgType) {
if (oConvertUtils.isEmpty(toUserId) || oConvertUtils.isEmpty(content)) {
return null;
}
String fromUserId = ensureWorkNotifyUserId();
if (fromUserId.equals(toUserId)) {
log.warn("审批待办IM发送失败接收人不能是工作通知公众号 toUserId={}", toUserId);
return null;
}
return sendSystemSingleMessage(fromUserId, toUserId, tenantId, content, msgType);
}
//update-end---author:GHT ---date:20260610 for【IM审批通用化】审批待办统一由系统账号发给处理人-----------
//update-end---author:GHT ---date:2026-05-29 for【QH-MES审批流设计】系统单聊消息(绕过同部门校验)-----
//update-begin---author:cursor ---date:20260528 for【IM聊天-OA】标记已读-----------
@@ -541,13 +583,19 @@ public class SysImChatServiceImpl implements ISysImChatService {
//update-begin---author:cursor ---date:20260528 for【IM聊天-OA】本部门成员列表含会话摘要-----------
@Override
public List<SysImContactVO> listDeptMembers(String userId, Integer tenantId, String orgCode, String keyword) {
List<SysImContactVO> result = new ArrayList<>();
//update-begin---author:GHT ---date:20260610 for【IM审批通用化】同事列表置顶工作通知公众号-----------
if (matchWorkNotifyKeyword(keyword)) {
result.add(buildWorkNotifyContact(userId, tenantId));
}
String workNotifyId = ensureWorkNotifyUserId();
String resolvedOrgCode = resolveOrgCode(userId, tenantId, orgCode);
if (oConvertUtils.isEmpty(resolvedOrgCode)) {
throw new JeecgBootException("未获取到当前部门,请切换部门后重试");
return result;
}
List<SysUser> users = userDepartMapper.querySameDepartUserList(resolvedOrgCode, userId, tenantId, keyword);
if (users == null || users.isEmpty()) {
return Collections.emptyList();
return result;
}
Map<String, SysImConversationVO> convMap = new HashMap<>(16);
if (tenantId != null && tenantId > 0) {
@@ -557,20 +605,26 @@ public class SysImChatServiceImpl implements ISysImChatService {
}
}
}
List<SysImContactVO> result = users.stream().map(user -> {
SysImContactVO vo = toContactVo(user);
SysImConversationVO conv = convMap.get(user.getId());
if (conv != null) {
vo.setConversationId(conv.getConversationId());
vo.setLastContent(conv.getLastContent());
vo.setLastTime(conv.getLastTime());
vo.setUnreadCount(conv.getUnreadCount());
}
return vo;
}).collect(Collectors.toList());
result.sort(Comparator
List<SysImContactVO> colleagues = users.stream()
.filter(user -> !workNotifyId.equals(user.getId()) && !WORK_NOTIFY_USERNAME.equals(user.getUsername()))
.map(user -> {
SysImContactVO vo = toContactVo(user);
vo.setContactType(CONTACT_TYPE_USER);
SysImConversationVO conv = convMap.get(user.getId());
if (conv != null) {
vo.setConversationId(conv.getConversationId());
vo.setLastContent(conv.getLastContent());
vo.setLastTime(conv.getLastTime());
vo.setUnreadCount(conv.getUnreadCount());
}
return vo;
})
.collect(Collectors.toList());
colleagues.sort(Comparator
.comparing(SysImContactVO::getLastTime, Comparator.nullsLast(Comparator.reverseOrder()))
.thenComparing(item -> oConvertUtils.getString(item.getRealname(), item.getUsername())));
result.addAll(colleagues);
//update-end---author:GHT ---date:20260610 for【IM审批通用化】同事列表置顶工作通知公众号-----------
return result;
}
//update-end---author:cursor ---date:20260528 for【IM聊天-OA】本部门成员列表含会话摘要-----------
@@ -583,9 +637,111 @@ public class SysImChatServiceImpl implements ISysImChatService {
vo.setAvatar(user.getAvatar());
vo.setOrgCodeTxt(user.getOrgCodeTxt());
vo.setUnreadCount(0);
vo.setContactType(CONTACT_TYPE_USER);
return vo;
}
private SysImContactVO buildWorkNotifyContact(String userId, Integer tenantId) {
String workNotifyId = ensureWorkNotifyUserId();
SysImContactVO vo = new SysImContactVO();
vo.setId(workNotifyId);
vo.setUsername(WORK_NOTIFY_USERNAME);
vo.setRealname(WORK_NOTIFY_REALNAME);
vo.setUnreadCount(0);
vo.setContactType(CONTACT_TYPE_WORK_NOTIFY);
if (tenantId != null && tenantId > 0) {
SysImConversationVO conv = findSingleConversationSummary(userId, tenantId, workNotifyId);
if (conv != null) {
vo.setConversationId(conv.getConversationId());
vo.setLastContent(conv.getLastContent());
vo.setLastTime(conv.getLastTime());
vo.setUnreadCount(conv.getUnreadCount());
}
}
return vo;
}
private SysImConversationVO findSingleConversationSummary(String userId, Integer tenantId, String targetUserId) {
String pairKey = buildPairKey(userId, targetUserId);
SysImConversation conversation = conversationMapper.selectOne(new LambdaQueryWrapper<SysImConversation>()
.eq(SysImConversation::getTenantId, tenantId)
.eq(SysImConversation::getUserPairKey, pairKey));
if (conversation == null) {
return null;
}
SysImConversationMember member = getMember(userId, conversation.getId());
SysImConversationVO vo = buildConversationVo(conversation, userId, targetUserId);
vo.setUnreadCount(member == null ? 0 : member.getUnreadCount());
return vo;
}
private boolean matchWorkNotifyKeyword(String keyword) {
if (oConvertUtils.isEmpty(keyword)) {
return true;
}
String key = keyword.trim().toLowerCase();
return WORK_NOTIFY_REALNAME.contains(keyword.trim())
|| WORK_NOTIFY_REALNAME.toLowerCase().contains(key)
|| WORK_NOTIFY_USERNAME.contains(key)
|| key.contains("工作")
|| key.contains("通知");
}
private String ensureWorkNotifyUserId() {
if (oConvertUtils.isNotEmpty(workNotifyUserIdCache)) {
return workNotifyUserIdCache;
}
synchronized (this) {
if (oConvertUtils.isNotEmpty(workNotifyUserIdCache)) {
return workNotifyUserIdCache;
}
SysUser user = userMapper.selectOne(new LambdaQueryWrapper<SysUser>()
.eq(SysUser::getUsername, WORK_NOTIFY_USERNAME)
.eq(SysUser::getDelFlag, 0)
.last("LIMIT 1"));
if (user == null) {
user = userMapper.selectById(WORK_NOTIFY_USER_ID);
}
if (user == null) {
user = createWorkNotifyUser();
}
workNotifyUserIdCache = user.getId();
return workNotifyUserIdCache;
}
}
private SysUser createWorkNotifyUser() {
SysUser user = new SysUser();
user.setId(WORK_NOTIFY_USER_ID);
user.setUsername(WORK_NOTIFY_USERNAME);
user.setRealname(WORK_NOTIFY_REALNAME);
user.setPassword("disabled");
user.setSalt("");
user.setStatus(2);
user.setDelFlag(0);
user.setCreateBy("system");
user.setCreateTime(new Date());
userMapper.insert(user);
return user;
}
private boolean isWorkNotifyUser(String userId) {
return oConvertUtils.isNotEmpty(userId) && userId.equals(ensureWorkNotifyUserId());
}
private boolean isWorkNotifyConversation(String conversationId) {
if (oConvertUtils.isEmpty(conversationId)) {
return false;
}
SysImConversation conversation = conversationMapper.selectById(conversationId);
if (conversation == null || !CONV_TYPE_SINGLE.equals(conversation.getConvType())) {
return false;
}
String workNotifyId = ensureWorkNotifyUserId();
String pairKey = conversation.getUserPairKey();
return oConvertUtils.isNotEmpty(pairKey) && pairKey.contains(workNotifyId);
}
private String resolveOrgCode(String userId, Integer tenantId, String orgCode) {
if (oConvertUtils.isNotEmpty(orgCode)) {
return orgCode;
@@ -610,6 +766,17 @@ public class SysImChatServiceImpl implements ISysImChatService {
}
private void createMember(String conversationId, String userId, Date now) {
ensureMember(conversationId, userId, now);
}
/** 幂等创建会话成员,避免 uk_im_member 重复插入 */
private void ensureMember(String conversationId, String userId, Date now) {
if (oConvertUtils.isEmpty(conversationId) || oConvertUtils.isEmpty(userId)) {
return;
}
if (getMember(userId, conversationId) != null) {
return;
}
SysImConversationMember member = new SysImConversationMember();
member.setConversationId(conversationId);
member.setUserId(userId);
@@ -742,7 +909,11 @@ public class SysImChatServiceImpl implements ISysImChatService {
sender = userMapper.selectById(message.getSenderId());
}
if (sender != null) {
vo.setSenderName(sender.getRealname());
if (WORK_NOTIFY_USERNAME.equals(sender.getUsername())) {
vo.setSenderName(WORK_NOTIFY_REALNAME);
} else {
vo.setSenderName(sender.getRealname());
}
vo.setSenderAvatar(sender.getAvatar());
}
return vo;

View File

@@ -28,4 +28,6 @@ public class SysImContactVO {
private java.util.Date lastTime;
@Schema(description = "未读数")
private Integer unreadCount;
@Schema(description = "联系人类型 user=同事 work_notify=工作通知公众号")
private String contactType;
}

View File

@@ -0,0 +1,17 @@
-- IM 工作通知公众号系统账号审批等系统消息统一从此账号推送
SET NAMES utf8mb4;
INSERT IGNORE INTO `sys_user` (
`id`, `username`, `realname`, `password`, `salt`, `status`, `del_flag`, `activiti_sync`, `create_by`, `create_time`
) VALUES (
'1995000000000000999',
'im_work_notify',
'工作通知',
'disabled',
'',
2,
0,
1,
'system',
NOW()
);