前言
不知道小伙伴们的公司组织架构通勤用的啥软件,我公司用的企业微信。然后业务销售部那边需要每天统计销售数据报表然后发在群里(我是开发,我不配在群里)。知道这个背景以后,产品给我们的需求是:直接统计数据按照业务那边的报表模板直接生成销售报表,然后定时每天晚上10点发送到各个门店的相关群(企业微信群)里。
效果
实现
第一步:肯定要先去看企业微信的开发文档啊
1、开发文档
2、稍微说一下我看文档后的理解
- 群机器人作用:
- 企业微信群机器人怎么发送消息:
这里的意思是说:
往群组推送文本消息,首先要知道这个群组的地址,而这个地址用在该群里的机器人webhook地址。地址知道以后,我们就能够访问它并向他发送消息。
怎么发送简单的文本消息:
根据文档提供的实例,我们知道这是一个post请求,参数为
{
"msgtype": "text",
"text": {
"content": "hello world"
}
}
msgtype: 消息类型 ;text: 文本 ;content: 内容
- 接下来理解一下群机器人的webhook地址
https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=693a91f6-7xxx-4bc4-97a0-0ec2sifa5aaa
看url能看得出来,怎么区分不同的群机器人,用url的参数key,key不同代表群机器人不同,只要在各个群组里添加对应的机器人,拿到key,我们就能根据key,发送文件到不同的企业微信群里了。
3、发送文件
根据前面的分析我们知道,有参数 msgtype 代表着消息类型,所以开发文档往下滑,找到文件类型
发送文件参数:
{
"msgtype": "file",
"file": {
"media_id": "3a8asd892asd8asd"
}
}
media_id:文件id,通过文件上传接口获取
4、文件上传接口
参数file里的media_id,只能通过企业微信提供的文件上传接口获取
第二步:Java代码实现
文档看完以后,我们就开始写代码,其实很简单,就post请求访问两个url,根据访问的顺序:
- 文件上传接口: https://qyapi.weixin.qq.com/cgi-bin/webhook/upload_media?key=群机器人的key&type=file
- 群机器人的webhook地址: https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=群机器人的key
这里推荐 hutool工具,使用其封装的post方法,不用自己写啦
hutool: 🍬小而全的Java工具类库,使Java拥有函数式语言般的优雅,让Java语言也可以“甜甜的”。
public class QywxMessageConstant {
/** 企业微信群上传文件url */
public static final String UPLOAD_FILE_URL = "https://qyapi.weixin.qq.com/cgi-bin/webhook/upload_media";
/** 发送群消息url */
public static final String SEND_MESSAGE_URL = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send";
}
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
/**
* description 上传文件并推送到企业微信群
*/
public void sendFileToWeChatGroup(File file, String groupKey) throws ServiceException {
// 上传文件
String urlString = QywxMessageConstant.UPLOAD_FILE_URL + "?key=" + groupKey + "&type=file";
HashMap<String, Object> sendMap = new HashMap<>();
sendMap.put("file", file);
String result = cn.hutool.http.HttpUtil.post(urlString, sendMap);
JSONObject jsonObject = JSON.parseObject(result);
Integer errcode = Integer.valueOf(jsonObject.get("errcode").toString());
if (errcode.equals(0)) {
// 推送消息
String mediaid = (String) jsonObject.get("media_id");
String sendUrl = QywxMessageConstant.SEND_MESSAGE_URL + "?key=" + groupKey;
Map<String,Object> mediaMap = new HashMap<>();
mediaMap.put("media_id",mediaid);
Map<String,Object> msgMap = new HashMap<>();
msgMap.put("msgtype","file");
msgMap.put("file",mediaMap);
cn.hutool.http.HttpUtil.post(sendUrl, JSON.toJSONString(msgMap));
} else {
throw new ServiceException("企业微信群上传文件失败");
}
}
定时任务(springboot 注解实现)
/**
* 定时任务
*
* @author Administrator
*/
@Component
@Slf4j
@ConditionalOnProperty(prefix = "spring.profiles", name = "active", havingValue = "prod")
public class StatisticsTask {
@Autowired
private SalesStatisticsService salesStatisticsService;
/**
* 每天22点发送每日销售的数据
*
* @throws Exception
*/
@Scheduled(cron = "0 0 22 * * ? ")
public void send() throws Exception {
try {
log.info("开始执行:发送每日销售任务");
salesStatisticsService.exportExcel();
log.info("执行完成:每日销售任务");
} catch (Exception e) {
log.error("执行异常:每日销售任务");
e.printStackTrace();
}
}
}
补充: EasyExcel 生成文件 File
TestExportController
package com.xxx.crm.controller;
import cn.hutool.core.date.DateUtil;
import com.XX.XX.base.util.DateTimeUtils;
import com.XX.XX.service.export.TestExportService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Date;
/**
* 导出报表测试
*/
@RestController
@RequestMapping("/exportTest")
public class TestExportController {
@Autowired
TestExportService testExportService;
// 给管理后台的导出
@RequestMapping("/exportTest2")
@ResponseBody
public void exportTest2(HttpServletRequest request, HttpServletResponse response){
try {
// 中文文件名必须使用此句话
String filename = new String(String.format("%s 测试报表.xlsx", "2022-05-06").getBytes(), StandardCharsets.ISO_8859_1);
response.setContentType("application/json;charset=UTF-8");
response.setHeader("Content-Disposition", "attachment;filename=" + filename);
OutputStream out = new BufferedOutputStream(response.getOutputStream());
// 导出文件
testExportService.export(out,filename);
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
// 发送到企业微信群的导出
@RequestMapping("/exportTest3")
@ResponseBody
public void exportTest3(HttpServletRequest request, HttpServletResponse response){
try {
String formatTDate = DateUtil.format(new Date(), DateTimeUtils.y4M2d2);
// 表数据
String fileName = "测试报表_" + formatTDate + "_每日销售数据" + ".xlsx";
OutputStream out = new ByteArrayOutputStream();
testExportService.exportToQYWX(testExportService.exportByCustom(out,fileName),fileName);
} catch (Exception e) {
e.printStackTrace();
}
}
}
TestExportServiceImpl:
// 调用接口即可获取文件
@Override
public OutputStream export(OutputStream outputStream, String fileName) throws Exception {
try {
// 示例数据
List<String> datalist= ListUtils.newArrayList();
for (int i = 0; i < 10; i++) {
String data = "ceshi" + i;
datalist.add(data);
}
// 把数据写入流里
EasyExcel.write(outputStream)
.sheet(fileName.split("\\.")[0])
.doWrite(datalist);
outputStream.close();
return outputStream;
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
// 把文件暂时存在本地,然后发送到企业微信群
@Override
public void exportToQYWX(OutputStream outputStream, String fileName) throws Exception {
try {
// 输出流转输入流 outputStream = new ByteArrayOutputStream();
ByteArrayInputStream swapStream = new ByteArrayInputStream(((ByteArrayOutputStream)outputStream).toByteArray());
InputStream inputStream = new BufferedInputStream(swapStream);
// 将流转成成文件
File file = weChatConfig.inputStreamToFile(inputStream, fileName);
// 企业微信发送报表
sendFileToWeChatGroup(file, 'groupKey');
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import java.io.File;
/**
* description 将流转换保存成文件
*/
public File inputStreamToFile(InputStream inputStream, String filename) {
String uploadFileSavePath = "/tmp/exportData/";
String savePath = uploadFileSavePath + DateUtil.format(new Date(), "yyyyMMdd") + "/";
File savePathFile = new File(savePath);
if (!savePathFile.exists()) {
savePathFile.mkdirs();
}
return FileUtil.writeFromStream(inputStream, savePath + filename);
}