package cn.iocoder.yudao.module.wxcp.service.notify; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserBindDO; import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO; import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserBindMapper; import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserMapper; import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import me.chanjar.weixin.cp.api.WxCpService; import me.chanjar.weixin.cp.bean.message.WxCpMessage; import me.chanjar.weixin.cp.bean.message.WxCpMessageSendResult; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; /** * 企业微信消息通知服务 * * @author 超级管理员 */ @Service @Slf4j public class WxCpNotifyService { @Resource private WxCpService wxCpService; @Resource private SocialUserBindMapper socialUserBindMapper; @Resource private SocialUserMapper socialUserMapper; /** * 发送登录通知消息到企业微信 * * @param userId 系统用户 ID * @param username 用户登录名 * @param loginType 登录方式(如:账号密码、短信、社交) */ @Async public void sendLoginNotification(Long userId, String username, String loginType) { try { // 1. 查询用户绑定的企业微信 String wxUserId = getWxUserId(userId); if (wxUserId == null) { log.info("[sendLoginNotification][用户({})未绑定企业微信,跳过通知]", userId); return; } // 2. 构造文本消息 // 注意:不能用 TEXTCARD —— 企业微信要求 textcard 的 url 必填, // 留空会返回 41010 missing url,导致通知全部发送失败 String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); WxCpMessage message = WxCpMessage.TEXT() .toUser(wxUserId) .content(String.format( "【登录通知】您的账号于 %s 通过「%s」方式登录系统。\n如非本人操作,请立即修改密码。", time, loginType)) .build(); // 3. 发送消息 WxCpMessageSendResult result = wxCpService.getMessageService().send(message); if (result.getErrCode() == 0) { log.info("[sendLoginNotification][企业微信登录通知发送成功 userId={} wxUserId={}]", userId, wxUserId); } else { log.warn("[sendLoginNotification][企业微信登录通知发送失败 userId={} wxUserId={} errCode={} errMsg={}]", userId, wxUserId, result.getErrCode(), result.getErrMsg()); } } catch (Exception e) { log.error("[sendLoginNotification][企业微信登录通知发送异常 userId={}]", userId, e); } } /** * 发送普通文本消息到企业微信 * * @param userId 系统用户 ID * @param content 消息内容 */ @Async public void sendTextMessage(Long userId, String content) { try { String wxUserId = getWxUserId(userId); if (wxUserId == null) { return; } WxCpMessage message = WxCpMessage.TEXT() .toUser(wxUserId) .content(content) .build(); WxCpMessageSendResult result = wxCpService.getMessageService().send(message); if (result.getErrCode() == 0) { log.info("[sendTextMessage][企业微信文本消息发送成功 userId={}]", userId); } else { log.warn("[sendTextMessage][企业微信文本消息发送失败 userId={} errCode={} errMsg={}]", userId, result.getErrCode(), result.getErrMsg()); } } catch (Exception e) { log.error("[sendTextMessage][企业微信文本消息发送异常 userId={}]", userId, e); } } /** * 根据系统用户 ID 获取企业微信 UserId * * @param userId 系统用户 ID * @return 企业微信 UserId(即 openid) */ private String getWxUserId(Long userId) { // 1. 查绑定关系 SocialUserBindDO bind = socialUserBindMapper.selectByUserIdAndUserTypeAndSocialType( userId, UserTypeEnum.ADMIN.getValue(), SocialTypeEnum.WECHAT_ENTERPRISE.getType()); if (bind == null) { return null; } // 2. 查社交用户信息 SocialUserDO socialUser = socialUserMapper.selectById(bind.getSocialUserId()); if (socialUser == null) { return null; } return socialUser.getOpenid(); } }