提交 e57adfc6 authored 作者: 李秋林's avatar 李秋林

活动记录上传飞书

上级 1ec437cd
package com.wangxiaolu.export; package com.wangxiaolu.export;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication @EnableAsync
@EnableConfigurationProperties
@EnableAspectJAutoProxy
@SpringBootApplication(scanBasePackages = {"com"})
@MapperScan("com.wangxiaolu.export.mapper")
public class WangxiaoluExportApplication { public class WangxiaoluExportApplication {
public static void main(String[] args) { public static void main(String[] args) {
......
package com.wangxiaolu.export.controller.feishu;
import com.wangxiaolu.export.mapper.entity.TemporaryActivityClockDO;
import com.wangxiaolu.export.mapper.entity.TemporaryActivityPhotoDO;
import com.wangxiaolu.export.mapper.entity.TemporaryActivityReportedDO;
import com.wangxiaolu.export.pojo.ActivityVo;
import com.wangxiaolu.export.service.ActivityToFeishuSheetService;
import com.wangxiaolu.export.service.PromotionActivityService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @author : liqiulin
* @date : 2024-06-14 11
* @describe :
*/
@Slf4j
@RestController
@RequestMapping("/promotion/activity")
public class ActivityToFeishuSheet {
@Autowired
PromotionActivityService promotionActivityService;
@Autowired
ActivityToFeishuSheetService activityToFeishuSheetService;
/**
* 飞书电子表格
* @param activityVo 查询活动创建日期范围
*/
@PostMapping("/feishu/sheet")
public void activityList(@RequestBody ActivityVo activityVo) {
/**
* 1>> 查询活动记录
* 查询当天的记录
*/
List<TemporaryActivityReportedDO> reportedDos = promotionActivityService.findActivityList(activityVo);
List<Long> activityId = reportedDos.stream().map(TemporaryActivityReportedDO::getId).collect(Collectors.toList());
Map<Long, List<TemporaryActivityPhotoDO>> activityPhotos = promotionActivityService.findActivityPhotos(activityId);
/**
* 2>> 查询打卡记录
*/
List<TemporaryActivityClockDO> clockPhoto = promotionActivityService.findClockPhoto(activityVo);
Map<String, List<TemporaryActivityPhotoDO>> clockPhotoMap = new HashMap<>();
clockPhoto.stream().forEach(cp -> {
clockPhotoMap.put(cp.getTemporaryId() + "-" + cp.getCreateDate(), cp.getPhotoList());
});
log.info("============== 活动记录上传飞书 start(" + System.currentTimeMillis() + ") ==============");
activityToFeishuSheetService.activityDataToFeishuSheet(reportedDos,activityPhotos,clockPhotoMap);
log.info("============== 活动记录上传飞书 end(" + System.currentTimeMillis() + ") ==============");
}
}
...@@ -211,7 +211,9 @@ public class PromotionActivityExport { ...@@ -211,7 +211,9 @@ public class PromotionActivityExport {
}); });
log.info("活动记录数据处理完成,开始导出"); log.info("活动记录数据处理完成,开始导出");
System.out.println("1:"+System.currentTimeMillis());
ExcelUtils.export(response, "活动数据导出_"+ DateUtil.today(), sheet); ExcelUtils.export(response, "活动数据导出_"+ DateUtil.today(), sheet);
System.out.println("7:"+System.currentTimeMillis());
log.info("============== 活动记录导出 end =============="); log.info("============== 活动记录导出 end ==============");
} }
......
package com.wangxiaolu.export.mapper;
import com.wangxiaolu.export.mapper.entity.FeishuSheetRecordDO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
/**
* @author a02200059
* @description 针对表【feishu_sheet_record】的数据库操作Mapper
* @createDate 2024-07-30 15:09:39
* @Entity com.wangxiaolu.export.mapper.entity.FeishuSheetRecordDO
*/
@Mapper
@Repository
public interface FeishuSheetRecordMapper extends BaseMapper<FeishuSheetRecordDO> {
FeishuSheetRecordDO selectOneByCreateMonth(String month);
}
package com.wangxiaolu.export.mapper.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.util.Date;
import lombok.Data;
/**
*
* @TableName feishu_sheet_record
*/
@TableName(value ="feishu_sheet_record")
@Data
public class FeishuSheetRecordDO implements Serializable {
/**
* 主键id
*/
@TableId(type = IdType.AUTO)
private Integer id;
/**
* 电子表格token
*/
private String sheetToken;
/**
* 工作表id
*/
private String sheetId;
/**
* 创建月份
*/
private String createMonth;
/**
* 创建时间
*/
private Date createTime;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
}
\ No newline at end of file
package com.wangxiaolu.export.pojo; package com.wangxiaolu.export.pojo;
import cn.hutool.core.date.DateTime;
import lombok.Data; import lombok.Data;
import java.util.Date;
/** /**
* @author : liqiulin * @author : liqiulin
* @date : 2024-07-03 10 * @date : 2024-07-03 10
......
package com.wangxiaolu.export.service;
import com.wangxiaolu.export.mapper.entity.TemporaryActivityPhotoDO;
import com.wangxiaolu.export.mapper.entity.TemporaryActivityReportedDO;
import java.util.List;
import java.util.Map;
/**
* @author : liqiulin
* @date : 2024-07-29 15
* @describe :
*/
public interface ActivityToFeishuSheetService {
void activityDataToFeishuSheet(List<TemporaryActivityReportedDO> reportedDos, Map<Long, List<TemporaryActivityPhotoDO>> activityPhotos, Map<String, List<TemporaryActivityPhotoDO>> clockPhotoMap);
}
...@@ -718,28 +718,39 @@ public class ExcelUtils { ...@@ -718,28 +718,39 @@ public class ExcelUtils {
Map<String, List<List<Object>>> sheetMap, Map<Integer, List<String>> selectMap) { Map<String, List<List<Object>>> sheetMap, Map<Integer, List<String>> selectMap) {
// 整个 Excel 表格 book 对象 // 整个 Excel 表格 book 对象
SXSSFWorkbook book = new SXSSFWorkbook(); SXSSFWorkbook book = new SXSSFWorkbook();
// 每个 Sheet 页 // 设置表头背景色(灰色)
CellStyle headStyle = book.createCellStyle();
headStyle.setFillForegroundColor(IndexedColors.GREY_80_PERCENT.index);
headStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
headStyle.setAlignment(HorizontalAlignment.CENTER);
headStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.index);
// 设置表身背景色(默认色)
CellStyle rowStyle = book.createCellStyle();
rowStyle.setAlignment(HorizontalAlignment.CENTER);
rowStyle.setVerticalAlignment(VerticalAlignment.CENTER);
/**
* 每个 Sheet 页
*/
System.out.println("2:"+System.currentTimeMillis());
Set<Entry<String, List<List<Object>>>> entries = sheetMap.entrySet(); Set<Entry<String, List<List<Object>>>> entries = sheetMap.entrySet();
for (Entry<String, List<List<Object>>> entry : entries) { for (Entry<String, List<List<Object>>> entry : entries) {
List<List<Object>> sheetDataList = entry.getValue(); List<List<Object>> sheetDataList = entry.getValue();
Sheet sheet = book.createSheet(entry.getKey()); Sheet sheet = book.createSheet(entry.getKey());
Drawing<?> patriarch = sheet.createDrawingPatriarch(); Drawing<?> patriarch = sheet.createDrawingPatriarch();
// 设置表头背景色(灰色)
CellStyle headStyle = book.createCellStyle();
headStyle.setFillForegroundColor(IndexedColors.GREY_80_PERCENT.index);
headStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
headStyle.setAlignment(HorizontalAlignment.CENTER);
headStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.index);
// 设置表身背景色(默认色)
CellStyle rowStyle = book.createCellStyle();
rowStyle.setAlignment(HorizontalAlignment.CENTER);
rowStyle.setVerticalAlignment(VerticalAlignment.CENTER);
// 设置表格列宽度(默认为15个字节) // 设置表格列宽度(默认为15个字节)
sheet.setDefaultColumnWidth(15); sheet.setDefaultColumnWidth(15);
// 创建合并算法数组 // 创建合并算法数组
int rowLength = sheetDataList.size(); int rowLength = sheetDataList.size();
int columnLength = sheetDataList.get(0).size(); int columnLength = sheetDataList.get(0).size();
int[][] mergeArray = new int[rowLength][columnLength]; int[][] mergeArray = new int[rowLength][columnLength];
/**
* 每一行
*/
System.out.println("3:"+System.currentTimeMillis());
for (int i = 0; i < sheetDataList.size(); i++) { for (int i = 0; i < sheetDataList.size(); i++) {
// 每个 Sheet 页中的行数据 // 每个 Sheet 页中的行数据
Row row = sheet.createRow(i); Row row = sheet.createRow(i);
...@@ -764,10 +775,12 @@ public class ExcelUtils { ...@@ -764,10 +775,12 @@ public class ExcelUtils {
mergeArray[i][j] = v; mergeArray[i][j] = v;
} }
} }
System.out.println("4:"+System.currentTimeMillis());
// 合并单元格 // 合并单元格
mergeCells(sheet, mergeArray); mergeCells(sheet, mergeArray);
// 设置下拉列表 // 设置下拉列表
setSelect(sheet, selectMap); setSelect(sheet, selectMap);
System.out.println("5:"+System.currentTimeMillis());
} }
// 写数据 // 写数据
if (response != null) { if (response != null) {
...@@ -790,6 +803,7 @@ public class ExcelUtils { ...@@ -790,6 +803,7 @@ public class ExcelUtils {
e.printStackTrace(); e.printStackTrace();
} }
} }
System.out.println("6:"+System.currentTimeMillis());
} }
/** /**
......
package com.wangxiaolu.export.util;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import com.wangxiaolu.promotion.common.util.NumberUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import javax.net.ssl.HttpsURLConnection;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author : liqiulin
* @date : 2024-07-29 14
* @describe :
*/
@Slf4j
@Component
public class FeishuSheetUtils {
private static final String CONTENT_TYPE = "application/json; charset=utf-8";
private static final String SHEET_URL = "https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/";
private static final String TENANT_ACCESS_TOKEN_URL = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal";
private static final String VALUES_APPEND = "/values_append";
private static final String VALUES_IMAGE = "/values_image";
/**
* 追加一条记录
*
* @return 修改的行号
*/
public Integer appendValueToSheet(String range, List<Object> datas, String sheetToken,String autoToken) {
// 组装参数
Map<String, Map<String, Object>> bodyMap = new HashMap<>();
Map<String, Object> valueRange = new HashMap<>();
valueRange.put("range", range);
valueRange.put("values", Arrays.asList(datas));
bodyMap.put("valueRange", valueRange);
String bodyJson = JSONObject.toJSONString(bodyMap);
// body上传
String result = HttpUtil.createPost(SHEET_URL + sheetToken + VALUES_APPEND)
.auth("Bearer "+ autoToken).contentType(CONTENT_TYPE)
.body(bodyJson)
.execute().body();
// 上传结果判断
JSONObject resultJson = JSONObject.parseObject(result);
int code = resultJson.getInteger("code");
if (code != 0) {
log.error("活动上报错误:==============\n{}", result);
log.error("请求参数:==============\n{}", bodyJson);
return -1;
}
// 此次上传数据范围
String tableRange = resultJson.getJSONObject("data").getString("tableRange");
return NumberUtils.matcherNum(tableRange.split(":")[1]);
}
public void valuesImageToSheet(String range, String imageUrl, String sheetToken,String autoToken) {
if (StringUtils.isBlank(imageUrl) || !imageUrl.startsWith("https")) {
return;
}
try {
// 获取图片二进制流
byte[] imageByte = imageToByte(imageUrl);
// 组装参数
Map<String, Object> bodyMap = new HashMap<>();
bodyMap.put("range", range);
bodyMap.put("image", imageByte);
bodyMap.put("name", imageUrl);
String bodyJson = JSONObject.toJSONString(bodyMap);
// 上传boday
String result = HttpUtil.createPost(SHEET_URL + sheetToken + VALUES_IMAGE)
.auth("Bearer "+ autoToken).contentType(CONTENT_TYPE)
.body(bodyJson)
.execute().body();
// 判断上传结果
JSONObject resultJson = JSONObject.parseObject(result);
int code = resultJson.getInteger("code");
if (code != 0) {
log.error("图片上传错误,上传表格位置:{},报错详情:\n{}", range, result);
}
} catch (Exception e) {
log.error("图片转换/上传异常,上传表格位置:{}", range);
}
}
public JSONObject getFeishuTenantToken(String appId,String appSecret) {
Map<String, Object> bodyMap = new HashMap<>();
bodyMap.put("app_id", appId);
bodyMap.put("app_secret", appSecret);
String bodyJson = JSONObject.toJSONString(bodyMap);
String result = HttpUtil.createPost(TENANT_ACCESS_TOKEN_URL)
.contentType(CONTENT_TYPE)
.body(bodyJson)
.execute().body();
JSONObject resultJson = JSONObject.parseObject(result);
int code = resultJson.getInteger("code");
if (code != 0) {
throw new RuntimeException("获取tenant_access_token错误");
}
return resultJson;
}
private byte[] imageToByte(String fileUrl) throws Exception {
URL url = new URL(fileUrl);
// 打开链接,并且跳过 SSL 证书验证
HttpsUtils.trustAllHttpsCertificates();
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5 * 1000);
// 通过输入流获取图片数据
InputStream inStream = conn.getInputStream();
// 得到文件的二进制数据,以二进制封装得到数据,具有通用性
byte[] data = readInputStream(inStream);
// 关闭流
inStream.close();
return data;
}
private byte[] readInputStream(InputStream inStream) throws Exception {
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
byte[] buffer = new byte[4096];
// 每次读取的字符串长度,如果为-1,代表全部读取完毕
int len = 0;
// 使用一个输入流从buffer里把数据读取出来
while ((len = inStream.read(buffer)) != -1) {
// 用输出流往buffer里写入数据,中间参数代表从哪个位置开始读,len代表读取的长度
outStream.write(buffer, 0, len);
}
// 关闭输入流
inStream.close();
// 把outStream里的数据写入内存
return outStream.toByteArray();
}
}
package com.wangxiaolu.export.util;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
/**
* @author : liqiulin
* @date : 2024-07-29 14
* @describe :
*/
public class HttpsUtils {
// 设置 https 请求
public static void trustAllHttpsCertificates() throws Exception {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String str, SSLSession session) {
return true;
}
});
javax.net.ssl.TrustManager[] trustAllCerts = new javax.net.ssl.TrustManager[1];
javax.net.ssl.TrustManager tm = new miTM();
trustAllCerts[0] = tm;
javax.net.ssl.SSLContext sc = javax.net.ssl.SSLContext
.getInstance("SSL");
sc.init(null, trustAllCerts, null);
javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(sc
.getSocketFactory());
}
// 设置 https 请求证书
static class miTM implements javax.net.ssl.TrustManager, javax.net.ssl.X509TrustManager {
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public boolean isServerTrusted(
java.security.cert.X509Certificate[] certs) {
return true;
}
public boolean isClientTrusted(
java.security.cert.X509Certificate[] certs) {
return true;
}
@Override
public void checkServerTrusted(
java.security.cert.X509Certificate[] certs, String authType)
throws java.security.cert.CertificateException {
return;
}
@Override
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs, String authType)
throws java.security.cert.CertificateException {
return;
}
}
}
...@@ -5,11 +5,11 @@ spring: ...@@ -5,11 +5,11 @@ spring:
url: jdbc:mysql://bj-cdb-j8ppdy86.sql.tencentcdb.com:63569/promotion_dev?autoReconnect=true url: jdbc:mysql://bj-cdb-j8ppdy86.sql.tencentcdb.com:63569/promotion_dev?autoReconnect=true
username: LnNDBM username: LnNDBM
password: fd0%bhD4@oO(% password: fd0%bhD4@oO(%
# redis: redis:
# port: 21101 port: 21101
# host: bj-crs-oyzhz3c6.sql.tencentcdb.com host: bj-crs-oyzhz3c6.sql.tencentcdb.com
# database: 0 database: 0
# password: u)R3jrHk(qwt~mv$Tg=U password: u)R3jrHk(qwt~mv$Tg=U
cloud: cloud:
nacos: nacos:
...@@ -24,4 +24,25 @@ logging: ...@@ -24,4 +24,25 @@ logging:
# mybatis-plus 控制台打印sql日志 # mybatis-plus 控制台打印sql日志
mybatis-plus: mybatis-plus:
configuration: configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
\ No newline at end of file
wx:
miniapp:
configs:
- appid:
secret:
token: #微信小程序消息服务器配置的token
aesKey: #微信小程序消息服务器配置的EncodingAESKey
msgDataFormat:
temporary:
token_secret:
employee:
token_secret:
feishu:
activity_robot_1:
app_id: cli_a622204c69e8100b
app_secret: qzezaWe7Kxd61quiU9zRIf7COb4piEkF
activity_robot_2:
app_id: cli_a6220d5dcadf900b
app_secret: WeVaZ6yGXFEON6Lkl53rFdhHc8beQF1Y
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wangxiaolu.export.mapper.FeishuSheetRecordMapper">
<resultMap id="BaseResultMap" type="com.wangxiaolu.export.mapper.entity.FeishuSheetRecordDO">
<id property="id" column="id" jdbcType="INTEGER"/>
<result property="sheetToken" column="sheet_token" jdbcType="VARCHAR"/>
<result property="sheetId" column="sheet_id" jdbcType="VARCHAR"/>
<result property="createMonth" column="create_month" jdbcType="VARCHAR"/>
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
</resultMap>
<sql id="Base_Column_List">
id,sheet_token,sheet_id,
create_month,create_time
</sql>
<select id="selectOneByCreateMonth" resultMap="BaseResultMap">
select sheet_token, sheet_id
from feishu_sheet_record
where create_month = #{month}
</select>
</mapper>
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论