1.通过应用发送消息
2.通过群机器人发送群消息
要清楚几个概念
使用企业微信管理员登录到控制台
企业微信后台管理地址
1.CorpId
这个是每个企业独有的企业ID
2.agentId 和 secret
agentId 相当于是应用ID,secret就理解成key
可以自己创建个应用,然后绑定到相关部门或者具体到人
知道这三个概念后,就可以通过SDK发送企业微信消息了
1.引入依赖
<dependency> <groupId>com.github.binarywang</groupId> <artifactId>weixin-java-cp</artifactId> <version>3.7.0</version> </dependency>
2.添加固定写法的代码
2.1.增加config类,自己新增个config包写到里面
package com.linkyoyo.weatherpredict.config;import lombok.AllArgsConstructor;import lombok.Builder;import lombok.Data;import lombok.NoArgsConstructor;import org.springframework.stereotype.Component;/** * @author GuoZiXuan * @date 2022/06/13 14:45 */@Data@AllArgsConstructor@NoArgsConstructor@Builder@Componentpublic class AppConfig { /** * 设置微信企业应用的AgentId */ private Integer agentId; /** * 设置微信企业应用的Secret */ private String secret; /** * 设置微信企业号的token */ private String token; /** * 设置微信企业号的EncodingAESKey */ private String aesKey;}
package com.linkyoyo.weatherpredict.config;import com.linkyoyo.weatherpredict.handler.*;import com.google.common.collect.Lists;import com.google.common.collect.Maps;import lombok.val;import me.chanjar.weixin.common.api.WxConsts;import me.chanjar.weixin.cp.api.WxCpService;import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl;import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl;import me.chanjar.weixin.cp.constant.WxCpConsts;import me.chanjar.weixin.cp.message.WxCpMessageRouter;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import java.util.List;import java.util.Map;import java.util.stream.Collectors;/** * @author Binary Wang(https://github.com/binarywang) *///@Configuration//@EnableConfigurationProperties(WxCpProperties.class)@Componentpublic class WxCpConfiguration { private LogHandler logHandler; private NullHandler nullHandler; private LocationHandler locationHandler; private MenuHandler menuHandler; private MsgHandler msgHandler; private UnsubscribeHandler unsubscribeHandler; private SubscribeHandler subscribeHandler; private WxCpProperties properties; private static Map<Integer, WxCpMessageRouter> routers = Maps.newHashMap(); private static Map<Integer, WxCpService> cpServices = Maps.newHashMap(); @Autowired public WxCpConfiguration(LogHandler logHandler, NullHandler nullHandler, LocationHandler locationHandler, MenuHandler menuHandler, MsgHandler msgHandler, UnsubscribeHandler unsubscribeHandler, SubscribeHandler subscribeHandler, WxCpProperties properties) { this.logHandler = logHandler; this.nullHandler = nullHandler; this.locationHandler = locationHandler; this.menuHandler = menuHandler; this.msgHandler = msgHandler; this.unsubscribeHandler = unsubscribeHandler; this.subscribeHandler = subscribeHandler; this.properties = properties; } public static Map<Integer, WxCpMessageRouter> getRouters() { return routers; } public WxCpService getCpService(Integer agentId, String secret, String corpId) { AppConfig appConfig = AppConfig.builder() .agentId(agentId) .secret(secret) .build(); List<AppConfig> list = Lists.newArrayList(); list.add(appConfig); properties.setAppConfigs(list); properties.setCorpId(corpId); cpServices = this.properties.getAppConfigs().stream().map(a -> { val configStorage = new WxCpDefaultConfigImpl(); configStorage.setCorpId(this.properties.getCorpId()); configStorage.setAgentId(a.getAgentId()); configStorage.setCorpSecret(a.getSecret()); configStorage.setToken(a.getToken()); configStorage.setAesKey(a.getAesKey()); val service = new WxCpServiceImpl(); service.setWxCpConfigStorage(configStorage); routers.put(a.getAgentId(), this.newRouter(service)); return service; }).collect(Collectors.toMap(service -> service.getWxCpConfigStorage().getAgentId(), a -> a)); return cpServices.get(agentId); } private WxCpMessageRouter newRouter(WxCpService wxCpService) { final val newRouter = new WxCpMessageRouter(wxCpService); // 记录所有事件的日志 (异步执行) newRouter.rule().handler(this.logHandler).next(); // 自定义菜单事件 newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) .event(WxConsts.MenuButtonType.CLICK).handler(this.menuHandler).end(); // 点击菜单链接事件(这里使用了一个空的处理器,可以根据自己需要进行扩展) newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) .event(WxConsts.MenuButtonType.VIEW).handler(this.nullHandler).end(); // 关注事件 newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) .event(WxConsts.EventType.SUBSCRIBE).handler(this.subscribeHandler) .end(); // 取消关注事件 newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) .event(WxConsts.EventType.UNSUBSCRIBE) .handler(this.unsubscribeHandler).end(); // 上报地理位置事件 newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) .event(WxConsts.EventType.LOCATION).handler(this.locationHandler) .end(); // 接收地理位置消息 newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.LOCATION) .handler(this.locationHandler).end(); // 扫码事件(这里使用了一个空的处理器,可以根据自己需要进行扩展) newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) .event(WxConsts.EventType.SCAN).handler(this.nullHandler).end(); newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) .event(WxCpConsts.EventType.CHANGE_CONTACT).handler(new ContactChangeHandler()).end(); newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) .event(WxCpConsts.EventType.ENTER_AGENT).handler(new EnterAgentHandler()).end(); // 默认 newRouter.rule().async(false).handler(this.msgHandler).end(); return newRouter; }}
package com.linkyoyo.weatherpredict.config;import com.linkyoyo.weatherpredict.util.JsonUtils;import lombok.AllArgsConstructor;import lombok.Builder;import lombok.Data;import lombok.NoArgsConstructor;import org.springframework.stereotype.Component;import java.util.List;@Data@AllArgsConstructor@NoArgsConstructor@Builder@Componentpublic class WxCpProperties { /** * 设置微信企业号的corpId */ private String corpId; private List<AppConfig> appConfigs; @Override public String toString() { return JsonUtils.toJson(this); }}
2.2.增加builder类,自己新增个builder包写到里面
package com.linkyoyo.weatherpredict.builder;import me.chanjar.weixin.cp.api.WxCpService;import me.chanjar.weixin.cp.bean.WxCpXmlMessage;import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage;import org.slf4j.Logger;import org.slf4j.LoggerFactory;/** * @author Binary Wang(https://github.com/binarywang) */public abstract class AbstractBuilder { protected final Logger logger = LoggerFactory.getLogger(getClass()); /** * 创建WxCpXmlOutMessage信息 * @param content * @param wxMessage * @param service * @return */ public abstract WxCpXmlOutMessage build(String content, WxCpXmlMessage wxMessage, WxCpService service);}
package com.linkyoyo.weatherpredict.builder;import me.chanjar.weixin.cp.api.WxCpService;import me.chanjar.weixin.cp.bean.WxCpXmlMessage;import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage;import me.chanjar.weixin.cp.bean.WxCpXmlOutTextMessage;/** * @author Binary Wang(https://github.com/binarywang) */public class TextBuilder extends AbstractBuilder { @Override public WxCpXmlOutMessage build(String content, WxCpXmlMessage wxMessage, WxCpService service) { WxCpXmlOutTextMessage m = WxCpXmlOutMessage.TEXT().content(content) .fromUser(wxMessage.getToUserName()).toUser(wxMessage.getFromUserName()) .build(); return m; }}
2.3 增加handle类,放在handler包下
package com.linkyoyo.weatherpredict.handler;import me.chanjar.weixin.cp.message.WxCpMessageHandler;import org.slf4j.Logger;import org.slf4j.LoggerFactory;/** * @author Binary Wang(https://github.com/binarywang) */public abstract class AbstractHandler implements WxCpMessageHandler { protected Logger logger = LoggerFactory.getLogger(getClass());}
package com.linkyoyo.weatherpredict.handler;/** * @author Binary Wang(https://github.com/binarywang) */public abstract class AbstractScanHandler extends AbstractHandler {}
package com.linkyoyo.weatherpredict.handler;import com.linkyoyo.weatherpredict.builder.TextBuilder;import com.linkyoyo.weatherpredict.util.JsonUtils;import me.chanjar.weixin.common.session.WxSessionManager;import me.chanjar.weixin.cp.api.WxCpService;import me.chanjar.weixin.cp.bean.WxCpXmlMessage;import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage;import org.springframework.stereotype.Component;import java.util.Map;/** * 通讯录变更事件处理器. * * @author Binary Wang(https://github.com/binarywang) */@Componentpublic class ContactChangeHandler extends AbstractHandler { @Override public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService cpService, WxSessionManager sessionManager) { String content = "收到通讯录变更事件,内容:" + JsonUtils.toJson(wxMessage); this.logger.info(content); return new TextBuilder().build(content, wxMessage, cpService); }}
package com.linkyoyo.weatherpredict.handler;import lombok.extern.slf4j.Slf4j;import me.chanjar.weixin.common.error.WxErrorException;import me.chanjar.weixin.common.session.WxSessionManager;import me.chanjar.weixin.cp.api.WxCpService;import me.chanjar.weixin.cp.bean.WxCpXmlMessage;import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage;import java.util.Map;/** * <pre> * * Created by Binary Wang on 2018/8/27. * </pre> * * @author <a href="https://github.com/binarywang">Binary Wang</a> */@Slf4jpublic class EnterAgentHandler extends AbstractHandler { private static final int TEST_AGENT = 1000002; @Override public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService wxCpService, WxSessionManager sessionManager) throws WxErrorException { // do something return null; }}
package com.linkyoyo.weatherpredict.handler;import com.linkyoyo.weatherpredict.builder.TextBuilder;import me.chanjar.weixin.common.api.WxConsts;import me.chanjar.weixin.common.session.WxSessionManager;import me.chanjar.weixin.cp.api.WxCpService;import me.chanjar.weixin.cp.bean.WxCpXmlMessage;import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage;import org.springframework.stereotype.Component;import java.util.Map;/** * @author Binary Wang(https://github.com/binarywang) */@Componentpublic class LocationHandler extends AbstractHandler { @Override public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService cpService, WxSessionManager sessionManager) { if (wxMessage.getMsgType().equals(WxConsts.XmlMsgType.LOCATION)) { //TODO 接收处理用户发送的地理位置消息 try { String content = "感谢反馈,您的的地理位置已收到!"; return new TextBuilder().build(content, wxMessage, null); } catch (Exception e) { this.logger.error("位置消息接收处理失败", e); return null; } } //上报地理位置事件 this.logger.info("\n上报地理位置,纬度 : {}\n经度 : {}\n精度 : {}", wxMessage.getLatitude(), wxMessage.getLongitude(), String.valueOf(wxMessage.getPrecision())); //TODO 可以将用户地理位置信息保存到本地数据库,以便以后使用 return null; }}
package com.linkyoyo.weatherpredict.handler;import com.linkyoyo.weatherpredict.util.JsonUtils;import me.chanjar.weixin.common.session.WxSessionManager;import me.chanjar.weixin.cp.api.WxCpService;import me.chanjar.weixin.cp.bean.WxCpXmlMessage;import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage;import org.springframework.stereotype.Component;import java.util.Map;/** * @author Binary Wang(https://github.com/binarywang) */@Componentpublic class LogHandler extends AbstractHandler { @Override public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService cpService, WxSessionManager sessionManager) { this.logger.info("\n接收到请求消息,内容:{}", JsonUtils.toJson(wxMessage)); return null; }}
package com.linkyoyo.weatherpredict.handler;import me.chanjar.weixin.common.api.WxConsts.MenuButtonType;import me.chanjar.weixin.common.session.WxSessionManager;import me.chanjar.weixin.cp.api.WxCpService;import me.chanjar.weixin.cp.bean.WxCpXmlMessage;import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage;import org.springframework.stereotype.Component;import java.util.Map;/** * @author Binary Wang(https://github.com/binarywang) */@Componentpublic class MenuHandler extends AbstractHandler { @Override public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService cpService, WxSessionManager sessionManager) { String msg = String.format("type:%s, event:%s, key:%s", wxMessage.getMsgType(), wxMessage.getEvent(), wxMessage.getEventKey()); if (MenuButtonType.VIEW.equals(wxMessage.getEvent())) { return null; } return WxCpXmlOutMessage.TEXT().content(msg) .fromUser(wxMessage.getToUserName()).toUser(wxMessage.getFromUserName()) .build(); }}
package com.linkyoyo.weatherpredict.handler;import com.linkyoyo.weatherpredict.builder.TextBuilder;import com.linkyoyo.weatherpredict.util.JsonUtils;import me.chanjar.weixin.common.api.WxConsts;import me.chanjar.weixin.common.session.WxSessionManager;import me.chanjar.weixin.cp.api.WxCpService;import me.chanjar.weixin.cp.bean.WxCpXmlMessage;import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage;import org.springframework.stereotype.Component;import java.util.Map;/** * @author Binary Wang(https://github.com/binarywang) */@Componentpublic class MsgHandler extends AbstractHandler { @Override public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService cpService, WxSessionManager sessionManager) { if (!wxMessage.getMsgType().equals(WxConsts.XmlMsgType.EVENT)) { //TODO 可以选择将消息保存到本地 } //TODO 组装回复消息 String content = "收到信息内容:" + JsonUtils.toJson(wxMessage); return new TextBuilder().build(content, wxMessage, cpService); }}
package com.linkyoyo.weatherpredict.handler;import me.chanjar.weixin.common.session.WxSessionManager;import me.chanjar.weixin.cp.api.WxCpService;import me.chanjar.weixin.cp.bean.WxCpXmlMessage;import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage;import org.springframework.stereotype.Component;import java.util.Map;/** * @author Binary Wang(https://github.com/binarywang) */@Componentpublic class NullHandler extends AbstractHandler { @Override public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService cpService, WxSessionManager sessionManager) { return null; }}
package com.linkyoyo.weatherpredict.handler;import com.linkyoyo.weatherpredict.builder.TextBuilder;import me.chanjar.weixin.common.error.WxErrorException;import me.chanjar.weixin.common.session.WxSessionManager;import me.chanjar.weixin.cp.api.WxCpService;import me.chanjar.weixin.cp.bean.WxCpUser;import me.chanjar.weixin.cp.bean.WxCpXmlMessage;import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage;import org.springframework.stereotype.Component;import java.util.Map;/** * @author Binary Wang(https://github.com/binarywang) */@Componentpublic class SubscribeHandler extends AbstractHandler { @Override public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService cpService, WxSessionManager sessionManager) throws WxErrorException { this.logger.info("新关注用户 OPENID: " + wxMessage.getFromUserName()); // 获取微信用户基本信息 WxCpUser userWxInfo = cpService.getUserService().getById(wxMessage.getFromUserName()); if (userWxInfo != null) { // TODO 可以添加关注用户到本地 } WxCpXmlOutMessage responseResult = null; try { responseResult = handleSpecial(wxMessage); } catch (Exception e) { this.logger.error(e.getMessage(), e); } if (responseResult != null) { return responseResult; } try { return new TextBuilder().build("感谢关注", wxMessage, cpService); } catch (Exception e) { this.logger.error(e.getMessage(), e); } return null; } /** * 处理特殊请求,比如如果是扫码进来的,可以做相应处理 */ private WxCpXmlOutMessage handleSpecial(WxCpXmlMessage wxMessage) { //TODO return null; }}
package com.linkyoyo.weatherpredict.handler;import me.chanjar.weixin.common.session.WxSessionManager;import me.chanjar.weixin.cp.api.WxCpService;import me.chanjar.weixin.cp.bean.WxCpXmlMessage;import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage;import org.springframework.stereotype.Component;import java.util.Map;/** * @author Binary Wang(https://github.com/binarywang) */@Componentpublic class UnsubscribeHandler extends AbstractHandler { @Override public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService cpService, WxSessionManager sessionManager) { String openId = wxMessage.getFromUserName(); this.logger.info("取消关注用户 OPENID: " + openId); // TODO 可以更新本地数据库为取消关注状态 return null; }}
2.4 增加表及实体类,用于存应用的信息
这里使用的是postresql,字段一样就行,增加该表的原因是,后面发送消息需要传入应用的agentId和secret还有公司Id,所以需要维护起来
CREATE TABLE "public"."tbl_department" ( "id" int4 NOT NULL DEFAULT nextval('tbl_department_id_seq'::regclass), "agent_id" int4 NOT NULL, "secret" varchar(255) COLLATE "pg_catalog"."default" NOT NULL, "name" varchar(255) COLLATE "pg_catalog"."default" NOT NULL, "corp_id" varchar(32) COLLATE "pg_catalog"."default" NOT NULL, "create_time" timestamp(6), "update_time" timestamp(6), CONSTRAINT "tbl_department_pkey" PRIMARY KEY ("id"));ALTER TABLE "public"."tbl_department" OWNER TO "hzcloud_master";COMMENT ON COLUMN "public"."tbl_department"."agent_id" IS '微信-应用-agentId';COMMENT ON COLUMN "public"."tbl_department"."secret" IS '微信-应用-secret';COMMENT ON COLUMN "public"."tbl_department"."name" IS '微信-应用(部门)-名称';COMMENT ON COLUMN "public"."tbl_department"."corp_id" IS '微信-企业ID';COMMENT ON COLUMN "public"."tbl_department"."create_time" IS '创建时间';COMMENT ON COLUMN "public"."tbl_department"."update_time" IS '修改时间';COMMENT ON TABLE "public"."tbl_department" IS '微信-部门表';
package com.linkyoyo.weatherpredict.entity;import lombok.AllArgsConstructor;import lombok.Builder;import lombok.Data;import lombok.NoArgsConstructor;import lombok.experimental.Accessors;import org.hibernate.annotations.DynamicInsert;import org.hibernate.annotations.DynamicUpdate;import org.springframework.data.annotation.CreatedDate;import org.springframework.data.annotation.LastModifiedDate;import org.springframework.data.jpa.domain.support.AuditingEntityListener;import javax.persistence.*;import java.time.LocalDateTime;/** * @author ZhangWeiXiang * @date 2021/11/15 14:19 * 微信部门 */@Data@Accessors(chain = true)@NoArgsConstructor@AllArgsConstructor@Builder@DynamicInsert@DynamicUpdate@EntityListeners(AuditingEntityListener.class)@Entity@Table(name = "tbl_department")public class Department { /** * 主键 */ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; /** * 每个应用都有唯一的agentid */ @Column(name = "agent_id") private Integer agentId; /** * 独立的访问密钥 */ @Column(name = "secret") private String secret; /** * 应用名称 */ private String name; /** * 企业ID */ @Column(name = "corp_id") private String corpId; /** * 创建时间 */ @CreatedDate @Column(name = "create_time") private LocalDateTime createTime; /** * 修改时间 */ @LastModifiedDate @Column(name = "update_time") private LocalDateTime updateTime;}
2.5 增加工具类
package com.linkyoyo.weatherpredict.util;import com.fasterxml.jackson.annotation.JsonInclude.Include;import com.fasterxml.jackson.core.JsonProcessingException;import com.fasterxml.jackson.databind.ObjectMapper;import com.fasterxml.jackson.databind.SerializationFeature;/** * @author Binary Wang(https://github.com/binarywang) */public class JsonUtils { private static final ObjectMapper JSON = new ObjectMapper(); static { JSON.setSerializationInclusion(Include.NON_NULL); JSON.configure(SerializationFeature.INDENT_OUTPUT, Boolean.TRUE); } public static String toJson(Object obj) { try { return JSON.writeValueAsString(obj); } catch (JsonProcessingException e) { e.printStackTrace(); } return null; }}
下面就是我自己封装后的写法了,这里主要有两个,发送普通消息和发送文件消息
1.引入hutool工具类的依赖,我后面有用到
<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>4.6.2</version> </dependency>
2.编写文件转换方法,就是将上传的文件暂存
2.1新增类FileStoreService
package com.linkyoyo.weatherpredict.service;import org.springframework.web.multipart.MultipartFile;import java.io.File;public interface FileStoreService { File upload(MultipartFile multipartFile);}// A code blockvar foo = 'bar';
2.2编写实现类
package com.linkyoyo.weatherpredict.service.impl;import cn.hutool.core.io.FileUtil;import com.linkyoyo.weatherpredict.exception.BizException;import com.linkyoyo.weatherpredict.properties.LinkyoyoProperties;import com.linkyoyo.weatherpredict.result.CodeMsg;import com.linkyoyo.weatherpredict.service.FileStoreService;import lombok.RequiredArgsConstructor;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;import org.springframework.web.multipart.MultipartFile;import java.io.File;import java.io.IOException;@Service@Slf4j@Transactional@RequiredArgsConstructorpublic class FileStoreServiceImpl implements FileStoreService { @Autowired private LinkyoyoProperties linkyoyoProperties ; @Override public File upload(MultipartFile multipartFile) { String fileName = multipartFile.getOriginalFilename(); if(!FileUtil.exist(linkyoyoProperties.getPy().getWordPath()+ File.separator+"temporary")) FileUtil.mkdir(linkyoyoProperties.getPy().getWordPath()+File.separator+"temporary") ;//linkyoyoProperties.getPy().getWordPath() 这个方法就是从我的配置类获取个路径,这个你可以自己设置成自己的本地某个路径就好 String path = linkyoyoProperties.getPy().getWordPath()+File.separator+"temporary"+File.separator; if(!FileUtil.exist(path)) FileUtil.mkdir(path) ; File file = FileUtil.touch(new File(path, fileName)); try { multipartFile.transferTo(file); } catch (IOException e) { log.error("文件转存失败 :FileStoreService" + file.getAbsolutePath()); throw new BizException(CodeMsg.FILE_UPLOAD_ERROR.fillArgs("转存失败")); } return file; }}
2.2编写接收类,主要是用于调用企业微信API后,转换为实体类用
package com.linkyoyo.weatherpredict.info;import lombok.AllArgsConstructor;import lombok.Builder;import lombok.Data;import lombok.NoArgsConstructor;@Data@Builder@AllArgsConstructor@NoArgsConstructorpublic class UserInfo { private String userid;}
3.!重点来了,编写消息发送工具类
package com.linkyoyo.weatherpredict.util;import cn.hutool.http.HttpUtil;import com.alibaba.fastjson.JSONArray;import com.alibaba.fastjson.JSONObject;import com.linkyoyo.weatherpredict.config.WxCpConfiguration;import com.linkyoyo.weatherpredict.entity.Department;import com.linkyoyo.weatherpredict.exception.BizException;import com.linkyoyo.weatherpredict.info.UserInfo;import com.linkyoyo.weatherpredict.result.CodeMsg;import lombok.extern.slf4j.Slf4j;import me.chanjar.weixin.common.error.WxErrorException;import me.chanjar.weixin.cp.api.WxCpService;import me.chanjar.weixin.cp.bean.WxCpAgent;import me.chanjar.weixin.cp.bean.WxCpDepart;import me.chanjar.weixin.cp.bean.WxCpMessage;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import org.springframework.util.CollectionUtils;import java.io.File;import java.util.List;import java.util.Map;import java.util.stream.Collectors;@Component@Slf4jpublic class WxSendUtils { @Autowired private WxCpConfiguration wxCpConfiguration; public void sendTextMsg(Department department, String msg) throws Exception{ WxCpService cpService = wxCpConfiguration.getCpService(department.getAgentId(), department.getSecret(), department.getCorpId()); //拿到所有应用的名称和agentId List<WxCpAgent> list = cpService.getAgentService().list(); if(CollectionUtils.isEmpty(list)){ throw new BizException(CodeMsg.API_CALL_ERROR.fillArgs("该应用不存在!")); } //获取到该应用下绑定的所有部门 List<WxCpDepart> wxCpDepartList = cpService.getDepartmentService().list(null); //对该应用绑定的部门进行消息的发送 wxCpDepartList.forEach(wxCpDepart -> { WxCpMessage wxCpMessage = WxCpMessage.TEXT() .agentId(department.getAgentId()) // 企业号应用ID .toParty(wxCpDepart.getId()+"") .content(msg) .build(); try { cpService.messageSend(wxCpMessage); } catch (WxErrorException e) { log.info("微信告警发送报错: {}", e.getError()); } }); //对该应用绑定的个人进行消息的发送 List<String> userInfos = getUseInfos(department.getCorpId(), department.getSecret(), department.getAgentId()); userInfos.forEach(userInfo -> { WxCpMessage wxCpMessage = WxCpMessage.TEXT() .agentId(department.getAgentId()) // 企业号应用ID .toUser(userInfo) .content(msg) .build(); try { cpService.messageSend(wxCpMessage); } catch (WxErrorException e) { log.info("微信告警发送报错: {}", e.getError()); } }); } public void sendFileMsg(Department department, File file) throws WxErrorException { //调用资源上传接口并返回资源Id String mediaId = getMediaId(department.getCorpId(), department.getSecret(), "file", file); WxCpService cpService = wxCpConfiguration.getCpService(department.getAgentId(), department.getSecret(), department.getCorpId()); //拿到所有应用的名称和agentId List<WxCpAgent> list = cpService.getAgentService().list(); if(CollectionUtils.isEmpty(list)){ throw new BizException(CodeMsg.API_CALL_ERROR.fillArgs("该应用不存在!")); } //获取到该应用下绑定的所有部门 List<WxCpDepart> wxCpDepartList = cpService.getDepartmentService().list(null); //对该应用绑定的部门进行消息的发送 wxCpDepartList.forEach(wxCpDepart -> { WxCpMessage wxCpMessage = WxCpMessage.FILE() .agentId(department.getAgentId()) // 企业号应用ID .toParty(wxCpDepart.getId()+"") .mediaId(mediaId) .build(); try { cpService.messageSend(wxCpMessage); } catch (WxErrorException e) { log.info("微信告警发送报错: {}", e.getError()); } }); //对该应用绑定的个人进行消息的发送 List<String> userInfos = getUseInfos(department.getCorpId(), department.getSecret(), department.getAgentId()); userInfos.forEach(userInfo -> { WxCpMessage wxCpMessage = WxCpMessage.FILE() .agentId(department.getAgentId()) // 企业号应用ID .toUser(userInfo) .mediaId(mediaId) .build(); try { cpService.messageSend(wxCpMessage); } catch (WxErrorException e) { log.info("微信告警发送报错: {}", e.getError()); } }); } /** * * @param corpid 企业ID * @param corpsecret Secret * @param type 媒体文件类型:图片(image)、语音(voice)、视频(video),普通文件(file) * @param file * @throws Exception */ public String getMediaId(String corpid, String corpsecret, String type,File file){ String token = null; String mediaId = null; //获取token try { String toKenBody = HttpUtil.createGet("https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid="+corpid+"&corpsecret="+corpsecret) .execute().body(); JSONObject toKenObject = JSONObject.parseObject(toKenBody); token = toKenObject.get("access_token").toString(); }catch (Exception e){ throw new BizException(CodeMsg.API_CALL_ERROR.fillArgs("企业微信 获取token接口 调用失败")); } try { String body = HttpUtil.createPost("https://qyapi.weixin.qq.com/cgi-bin/media/upload?access_token="+token+"&type="+type) .form("media",file) .execute().body(); JSONObject jsonObject = JSONObject.parseObject(body); mediaId = jsonObject.get("media_id").toString(); }catch (Exception e){ throw new BizException(CodeMsg.API_CALL_ERROR.fillArgs("企业微信 上传临时素材接口 调用失败")); } return mediaId; } public List<String> getUseInfos(String corpid, String corpsecret,Integer agentid){ try { String body = HttpUtil.createGet("https://qyapi.weixin.qq.com/cgi-bin/agent/get?access_token="+getToKen(corpid,corpsecret)+"&agentid="+agentid) .execute().body(); JSONObject toKenObject = JSONObject.parseObject(body); Map<String,Object> userinfo = (Map<String, Object>) toKenObject.get("allow_userinfos"); if(CollectionUtils.isEmpty(userinfo)) return null; JSONArray user = (JSONArray)userinfo.get("user"); List<UserInfo> userInfos = user.toJavaList(UserInfo.class); if(CollectionUtils.isEmpty(user)) return null; return userInfos.stream().map(e->e.getUserid()).collect(Collectors.toList()); }catch (Exception e){ throw new BizException(CodeMsg.API_CALL_ERROR.fillArgs("企业微信 上传临时素材接口 调用失败")); } } public String getToKen(String corpid, String corpsecret){ try { String toKenBody = HttpUtil.createGet("https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid="+corpid+"&corpsecret="+corpsecret) .execute().body(); JSONObject toKenObject = JSONObject.parseObject(toKenBody); return toKenObject.get("access_token").toString(); }catch (Exception e){ throw new BizException(CodeMsg.API_CALL_ERROR.fillArgs("企业微信 获取token接口 调用失败")); } }}
代码讲解:普通消息的发送只需要传入四个重要信息,1.公司Id 2.agentId 3.secret 4.消息,而文件消息就有点不一样了,发送的文件必须先调用上传临时文件接口,然后会返回一个mediaId,然后发送文件消息的时候就传这个Id,这样就实现了文件消息的发送。
还有个点就是,应用既可以绑部门,也可以绑个人,所以可以通过方法先获取到该应用下绑定的部门或个人,然后再去往这个部门下的所有人去发消息,或去发个人。
4.编写测试类
package com.linkyoyo.weatherpredict.controller;import com.linkyoyo.weatherpredict.entity.Department;import com.linkyoyo.weatherpredict.result.R;import com.linkyoyo.weatherpredict.service.FileStoreService;import com.linkyoyo.weatherpredict.util.WxSendUtils;import lombok.RequiredArgsConstructor;import me.chanjar.weixin.common.error.WxErrorException;import org.springframework.web.bind.annotation.*;import org.springframework.web.multipart.MultipartFile;@RestController@RequiredArgsConstructor@RequestMapping("/sendWx")public class SendWxController { private final WxSendUtils wxSendUtils; private final FileStoreService fileStoreService; //发送文件消息 @PostMapping(value = "/FileMsg", headers = "Content-Type=multipart/form-data") public R FileMsg(@RequestPart("file") MultipartFile file) throws WxErrorException { Department department = new Department(); department.setName("消息推送"); department.setCorpId("***"); department.setAgentId(***); department.setSecret("***"); wxSendUtils.sendFileMsg(department, fileStoreService.upload(file)); return R.ok(); } //发送普通文本消息 @PostMapping(value = "/TextMsg") public R TextMsg(String msg) throws Exception { Department department = new Department(); department.setName("消息推送"); department.setCorpId("***"); department.setAgentId(***); department.setSecret("***"); wxSendUtils.sendTextMsg(department, msg); return R.ok(); } @PostMapping(value = "/getUse") public R getUse() throws Exception { Department department = new Department(); department.setName("消息推送"); department.setCorpId("***"); department.setAgentId(***); department.setSecret("***"); wxSendUtils.getUseInfos(department.getCorpId(),department.getSecret(),department.getAgentId()); return R.ok(); }}
发送群消息
1.创建群机器人
主要就是通过这个地址去发送消息的
相关API地址:https://developer.work.weixin.qq.com/document/path/91770