JAVA-实现接入企业微信发送消息(最详细代码讲解!)

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
@Component
public 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)
@Component
public 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
@Component
public 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)
 */
@Component
public 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>
 */
@Slf4j
public 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)
 */
@Component
public 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)
 */
@Component
public 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)
 */
@Component
public 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)
 */
@Component
public 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)
 */
@Component
public 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)
 */
@Component
public 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)
 */
@Component
public 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 block
var 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
@RequiredArgsConstructor
public 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;
    }
}

LinkyoyoProperties 这个类的属性在配置文件中


@Data
@ConfigurationProperties(prefix = "linkyoyo")
public class LinkyoyoProperties {

    /**
     * python相关配置
     */
    private PythonProperties py = new PythonProperties();

    /**
     * 企业微信相关配置
     */
    private WxProperties wx = new WxProperties();
}

@Data
public class PythonProperties {

    private String pyExplainerPath;

    private String pythonPath;

    private String wordPath;

    private String filePath;

    private String imagePath;

    private String wordTemplate;

    private String designWordScriptName;

    private String postEvaluationWordScriptName;

    private String doc2PdfScriptName;
}

@Data
public class WxProperties {

    //企业Id
    private String corpId;

    //应用Id
    private String agentId;

    //应用密钥
    private String secret;

    //企业微信群机器人 url
    private String url;

    //企业微信群机器人 key
    private String key;
}



在这里插入图片描述

2.2编写接收类,主要是用于调用企业微信API后,转换为实体类用
package com.linkyoyo.weatherpredict.info;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public 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
@Slf4j
public 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

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小轩爱编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值