DateUtils
代码:
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
/**
* @author miaohuanju001
* @date 2021-06-18
* @description
*/
public class DateUtils {
public static final String DATE_PATTERN_1 = "yyyy-MM-dd";
public static final String DATE_PATTERN_2 = "yyyy-MM-dd HH:mm:ss";
public static final String DATE_PATTERN_3 = "yyyyMMdd";
public static final int WHOLE_DAY_MILLIS = 24 * 60 * 60 * 1000;
public static final int WHOLE_DAY_SECONDS = 24 * 60 * 60;
/**
* 获取当前时间戳
*/
public static long getCurrentTimestamp() {
return System.currentTimeMillis();
}
/**
* 获取当前日期的格式化形式
* @param pattern "yyyy-MM-dd" / "yyyy-MM-dd HH:mm:ss" / ...
* @return 当前日期的格式化形式
*/
public static String now(String pattern){
DateTime dateTime = DateTime.now();
DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(pattern);
return dateTimeFormatter.print(dateTime);
}
/**
* 获取当前日期 -1d 的格式化形式
* @param pattern "yyyy-MM-dd" / "yyyy-MM-dd HH:mm:ss" / ...
*/
public static String yesterday(String pattern){
String now = now(pattern);
return datePlus(now, -1, pattern);
}
/**
* 获取当前日期 +1d 的格式化形式
* @param pattern "yyyy-MM-dd" / "yyyy-MM-dd HH:mm:ss" / ...
*/
public static String tomorrow(String pattern){
String now = now(pattern);
return datePlus(now, 1, pattern);
}
/**
* 获取当前日期的格式化形式 ,格式为 "yyyy-MM-dd HH:mm:ss"
*/
public static String now(){
return now(DATE_PATTERN_2);
}
/**
* 获取当前时间的指定格式化格式
*
* @param pattern "yyyy-MM-dd" / "yyyy-MM-dd HH:mm:ss" / ...
*/
public static String getFormatCurrentTime(String pattern) {
DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(pattern);
DateTime dateTime = DateTime.now();
return dateTimeFormatter.print(dateTime);
}
/**
* 格式化的日期转换为时间戳
*
* @param formatTime 格式化的日期
* 如 2021-06-19 或 2021-06-19T00:00:00
* @param pattern "yyyy-MM-dd" / "yyyy-MM-dd HH:mm:ss" / ...
* @return 时间戳
*/
public static long formatTime2Timestamp(String formatTime, String pattern) {
DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(pattern);
return DateTime.parse(formatTime, dateTimeFormatter).getMillis();
}
/**
* 时间戳转换为格式化的日期
*
* @param timestamp 时间戳
* @param pattern "yyyy-MM-dd" / "yyyy-MM-dd HH:mm:ss" 等
* @return 格式化的日期
*/
public static String timestamp2FormatTime(long timestamp, String pattern) {
DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(pattern);
return dateTimeFormatter.print(timestamp);
}
/**
* 获取一天的最后一毫秒对应的时间戳
* @param formatTime 格式化的日期
* @param pattern "yyyy-MM-dd" / "yyyy-MM-dd HH:mm:ss" / ...
* @return 传入日期当天的最后一毫秒对应的时间戳
*/
public static long getLastMillisOfDay(String formatTime, String pattern){
DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(pattern);
DateTime dateTime = DateTime.parse(formatTime, dateTimeFormatter);
return dateTime.millisOfDay().withMaximumValue().getMillis();
}
/**
* 获取一天的第一毫秒对应的时间戳
* @param formatTime 格式化的日期
* @param pattern "yyyy-MM-dd" / "yyyy-MM-dd HH:mm:ss" / ...
* @return 传入日期当天的第一毫秒对应的时间戳
*/
public static long getFirstMillisOfDay(String formatTime, String pattern){
DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(pattern);
DateTime dateTime = DateTime.parse(formatTime, dateTimeFormatter);
return dateTime.millisOfDay().withMinimumValue().getMillis();
}
/**
* 判断 当前时间 是否达到(≥) 传入日期当日的开始时间
* @param formatTime 格式化的日期
* @param pattern "yyyy-MM-dd" / "yyyy-MM-dd HH:mm:ss" / ...
* @return true/false
*/
public static boolean nowReachDayStart(String formatTime, String pattern){
return getCurrentTimestamp() >= getFirstMillisOfDay(formatTime, pattern);
}
/**
* 判断 当前时间 是否达到(≥) 传入日期当日的结束时间
* @param formatTime 格式化的日期
* @param pattern "yyyy-MM-dd" / "yyyy-MM-dd HH:mm:ss" / ...
* @return true/false
*/
public static boolean nowReachDayEnd(String formatTime, String pattern){
return getCurrentTimestamp() >= getLastMillisOfDay(formatTime, pattern);
}
/**
* 判断当前时间是否达到指定时间
* @param formatTime 格式化的日期
* @param pattern "yyyy-MM-dd" / "yyyy-MM-dd HH:mm:ss" / ...
* @return true/false
*/
public static boolean nowReachSpecificTime(String formatTime, String pattern){
return getCurrentTimestamp() >= formatTime2Timestamp(formatTime, pattern);
}
/**
* formatTime2 - formatTime1 ,计算两者之间间隔多少天
* @param formatTime1 格式化的日期1
* @param formatTime2 格式化的日期2
* @param pattern "yyyy-MM-dd" / "yyyy-MM-dd HH:mm:ss" / ...
* @return 间隔天数
*/
public static long dateDiff(String formatTime1, String formatTime2, String pattern){
DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(pattern);
DateTime dateTime1 = DateTime.parse(formatTime1, dateTimeFormatter);
DateTime dateTime2 = DateTime.parse(formatTime2, dateTimeFormatter);
long millisDiff = dateTime2.getMillis() - dateTime1.getMillis();
return millisDiff / WHOLE_DAY_MILLIS;
}
/**
*
* @param formatTime 格式化的基础日期
* @param plusDays 在基础日期上 增加(+)/减去(-) 的天数
* @param pattern 输入及输出的日期格式 "yyyy-MM-dd" / "yyyy-MM-dd HH:mm:ss" / ...
* @return 基础日期增加后的日期
*/
public static String datePlus(String formatTime, int plusDays, String pattern){
DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(pattern);
DateTime dateTime = DateTime.parse(formatTime, dateTimeFormatter);
DateTime newDateTime = dateTime.plusDays(plusDays);
return dateTimeFormatter.print(newDateTime);
}
/**
* 展示时间类的一些用法
*/
private void demo(){
// 2021-06-19T09:38:08.963+08:00
DateTime dateTime_Now = DateTime.now();
/*
2021-06-19T00:00:00.000+08:00
如果不加pattern,日期之间必须用"-"分割
*/
DateTime dateTime_0619_000000 = DateTime.parse("2021-06-19");
/*
2021-06-19T15:00:00.000+08:00
如果不加pattern,日期和时间之间必须有T
*/
DateTime dateTime_0619_150000 = DateTime.parse("2021-06-19T15:00:00");
DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern("yyyy/MM/dd HH_mm_ss");
// 2021-06-19T22:22:22.000+08:00
DateTime dateTime_0619_222222 = DateTime.parse("2021/06/19 22_22_22", dateTimeFormatter);
// 2021-06-20T00:00:00.000+08:00
DateTime dateTime_0620_000000 = DateTime.parse("2021-06-19").plusDays(1);
// 2021-06-18T00:00:00.000+08:00
DateTime dateTime_0618_000000 = DateTime.parse("2021-06-19").minusDays(1);
// 获取长度为12个小时的duration
Duration duration_12h = Duration.standardHours(12);
// 获取长度为120分钟的duration
Duration duration_120m = Duration.standardMinutes(120);
// 获取 两个dateTime之间的duration
Duration duration_betweenDateTime = new Duration(dateTime_0618_000000, dateTime_0619_000000);
// 将duration转换为seconds 【86400】
long duration_seconds = duration_betweenDateTime.getStandardSeconds();
// 2021-06-19T12:00:00.000+08:00
DateTime dateTime_0619_120000 = DateTime.parse("2021-06-19").plus(duration_12h);
// 2021-06-19T02:00:00.000+08:00
DateTime dateTime_0619_020000 = DateTime.parse("2021-06-19").plus(duration_120m);
// 将 DateTime 转换为 时间戳
// 2021-06-19 00:00:00 对应的时间戳 【1624032000000】
long millis_0619_000000 = dateTime_0619_000000.getMillis();
// 将 时间戳 转换为 DateTime
// 时间戳 【1624032000000】 对应的 DateTime 为 2021-06-19T00:00:00.000+08:00
dateTime_0619_000000 = new DateTime(millis_0619_000000);
// 2021-06-19 00:00:00 对应的当天的时间戳 【0】
// 相当于 dateTime_0619_000000.millisOfDay().get()
int dayMillis_0619_000000 = dateTime_0619_000000.getMillisOfDay();
// 2021-06-19T23:59:59.999+08:00 , 一天的最晚时间
DateTime dateTime_0619_235959 = dateTime_0619_000000.millisOfDay().withMaximumValue();
// 2021-06-19T00:00:00.000+08:00 , 一天的最早时间
dateTime_0619_000000 = dateTime_0619_120000.millisOfDay().withMinimumValue();
dateTimeFormatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss:SSS");
// 2021-06-19 22:22:22:000
String str_0618_000000 = dateTimeFormatter.print(dateTime_0618_000000);
// 2021-06-19 00:00:00:000
String str_1624032000000L = dateTimeFormatter.print(1624032000000L);
}
}
pom依赖:
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.9.4</version>
</dependency>
HttpUtils
代码:
import com.alibaba.fastjson.JSONObject;
import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;
import java.util.Map;
/**
* @author miaohuanju001
* @date 2021-06-20
* @description 依赖fastJSON 和 spring-web
*/
public class HTTPUtils {
private static final JSONObject EMPTY_JSON_OBJECT = new JSONObject();
/**
* 发送request请求
*
* @param domain 域名,例:http://localhost:8080
* @param path 路径, 例:/api/addUser
* @param paramMap 请求参数
* @param httpMethod HttpMethod.POST/HttpMethod.GET/...
* @return res
*/
public static JSONObject sendRequest(String domain, String path, Map<String, Object> paramMap, HttpMethod httpMethod) {
if (domain == null || "".equals(domain)){
return EMPTY_JSON_OBJECT;
}
if (path == null){
path = "";
}
String url = domain + path;
return sendRequest(url, paramMap, httpMethod);
}
/**
* 发送request请求
*
* @param url 请求url,例: http://localhost:8080/api/addUser
* @param paramMap 请求参数
* @param httpMethod HttpMethod.POST/HttpMethod.GET/...
* @return res
*/
public static JSONObject sendRequest(String url, Map<String, Object> paramMap, HttpMethod httpMethod) {
if (url == null || "".equals(url)){
return EMPTY_JSON_OBJECT;
}
RestTemplate restTemplate = new RestTemplate();
// 请求头设置
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8); // 表示发送请求内容的格式
httpHeaders.set("ucid", String.valueOf(1000000026751020L)); // 想请求头中添加一些自定义参数
httpHeaders.set(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_UTF8_VALUE); // 设置接收的内容格式
HttpEntity<Map<String, Object>> httpEntity;
String requestUrl;
JSONObject resJson = null;
if (HttpMethod.GET == httpMethod) {
httpEntity = new HttpEntity<>(httpHeaders);
StringBuilder requestUrlBuilder = new StringBuilder(url);
requestUrlBuilder.append('?');
for (String key : paramMap.keySet()) {
requestUrlBuilder.append(key).append('=').append(paramMap.get(key)).append('&');
}
requestUrlBuilder.deleteCharAt(requestUrlBuilder.length() - 1);
requestUrl =requestUrlBuilder.toString();
ResponseEntity<JSONObject> responseEntity = restTemplate.exchange(requestUrl, httpMethod, httpEntity, JSONObject.class);
resJson = responseEntity.getBody();
} else if (HttpMethod.POST == httpMethod) {
requestUrl = url;
httpEntity = new HttpEntity<>(paramMap, httpHeaders);
ResponseEntity<JSONObject> responseEntity = restTemplate.exchange(requestUrl, httpMethod, httpEntity, JSONObject.class);
resJson = responseEntity.getBody();
}
return resJson == null ? EMPTY_JSON_OBJECT : resJson;
}
}
pom依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.28</version>
</dependency>
JSONUtils
代码:
import com.alibaba.fastjson.JSONObject;
/**
* @author miaohuanju001
* @date 2021-07-07
* @description
*/
public class JSONUtils {
public static final JSONObject EMPTY_JSON_OBJECT = new JSONObject();
public static JSONObject javaObject2JsonObject(Object object){
return (JSONObject)JSONObject.toJSON(object);
}
public static <T> T jsonObject2JavaObject(JSONObject jsonObject, Class<T> clazz) {
return jsonObject.toJavaObject(clazz);
}
public static String object2JsonStr(Object object){
return JSONObject.toJSONString(object);
}
public static JSONObject jsonStr2JsonObject(String jsonStr){
return (JSONObject)(JSONObject.parse(jsonStr));
}
}
pom依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.28</version>
</dependency>
OfficialAccountsUtils
发送企微公众号,只适用于**贝壳找房**
代码:
import com.alibaba.fastjson.JSONObject;
import lombok.Data;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.util.DigestUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author miaohuanju001
* @date 2021-07-10
* @description 发送消息到企微公众号
* https://wiki.lianjia.com/pages/viewpage.action?pageId=499305440
*/
public class OfficialAccountsUtils {
private static final String OFFICIAL_ACCOUNTS_URL = "https://wxwork.ke.com/api/v1/wechat/message/news";
private static final String ODIN_APPID = "odin_notice";
private static final String ODIN_TOKEN = "F6_Ibms5bx63rIBnDz5_dVhrQDGFAzo1G28lcwXopwQ=";
/**
* 发送到自定义企微公众号
*
* @param title title
* @param usercodeList 工号list
* @param appId 公众号唯一appId
* @param token 公众号token
* @param url 点击图片后的跳转链接
* @param picurl 图片链接
* @return 成功/错误信息
*/
public static String sendToCustomOfficialAccounts(String title, List<String> usercodeList,
String appId, String token,
String url, String picurl) {
StringBuilder sb = new StringBuilder();
String touser;
for (String usercode : usercodeList) {
if (usercode.startsWith("10000000")) {
String usercode1 = usercode.replace("10000000", "");
sb.append(usercode1).append('|');
} else {
sb.append(usercode).append('|');
}
}
if (sb.length() > 0) {
touser = sb.deleteCharAt(sb.length() - 1).toString();
} else {
return "touser is invalid";
}
OfficialAccountsMessage officialAccountsMessage = createParam(title, touser, appId, token, url, picurl);
MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
JSONObject messageJson = JSONObject.parseObject(JSONObject.toJSONString(officialAccountsMessage));
paramMap.setAll(messageJson.toJavaObject(Map.class));
JSONObject jsonObject = HttpUtils2.sendRequest(OFFICIAL_ACCOUNTS_URL, paramMap, HttpMethod.POST, MediaType.APPLICATION_FORM_URLENCODED);
String errorMsg = jsonObject.getString("errmsg");
if (!Constants.OK.equals(errorMsg)) {
return errorMsg;
}
return Constants.SUCCESS;
}
/**
* 发送到奥丁企微公众号
*/
public static String sendToOdinOfficialAccounts(String title, List<String> usercodeList, String url, String picurl) {
return sendToCustomOfficialAccounts(title, usercodeList, ODIN_APPID, ODIN_TOKEN, url, picurl);
}
/**
* 构建 参数
* 公众号 发送图文消息
*
* @param url 点击图片后的跳转链接
* @param picurl 图片地址链接
* @param touser 用 | 分割的系统号
*/
private static OfficialAccountsMessage createParam(String title, String touser,
String appId, String token,
String url, String picurl) {
if (title == null || "".equals(title) ) {
title = "title";
}
OfficialAccountsMessage officialAccountsMessage = new OfficialAccountsMessage();
StringBuilder builder = new StringBuilder();
long timestamp = DateUtils.getCurrentTimestamp(DateUtils.UNIT_SECOND);
builder.append(timestamp);
builder.append(appId);
builder.append(token);
String signature = DigestUtils.md5DigestAsHex(builder.toString().getBytes());
officialAccountsMessage.setTouser(touser);
officialAccountsMessage.setAppId(appId);
officialAccountsMessage.setTimestamp(timestamp);
officialAccountsMessage.setSignature(signature);
officialAccountsMessage.setUrl(url);
officialAccountsMessage.setTitle(title);
officialAccountsMessage.setPicurl(picurl);
return officialAccountsMessage;
}
/**
* 企微公众号的消息内容
*/
@Data
public static class OfficialAccountsMessage {
/**
* 企微公众号唯一标识
* 必填
*/
private String appId;
/**
* 时间戳到秒
* 必填
*/
private long timestamp;
/**
* 签名:MD5(时间戳+appId+messageToken)
* 必填
*/
private String signature;
/**
* 接收人的链家系统号,比如:23009448,多个接收者用"|"分隔,最多支持1000个,特殊情况:指定为@all,则向该企业应用的全部成员发送
* 非必填
*/
private String touser;
/**
* 部门ID列表,多个用"|"分隔,最多支持100个,消息会发送给该部门及其所有子部门的员工。当touser为@all时忽略本参数
* 非必填
*/
private String toparty;
/**
* 标签ID列表,多个用"|"分隔,最多支持100个。当touser为@all时忽略本参数
* 非必填
*/
private String totag;
/**
* 点击图片后的跳转链接
* 必填
*/
private String url;
/**
* 图片链接
* 必填
*/
private String picurl;
/**
* title
* 必填
*/
private String title;
}
}
WeChatRobotUtils
发送企微群机器人消息
代码:
import com.alibaba.fastjson.JSONObject;
import lombok.Data;
import org.codehaus.plexus.util.Base64;
import org.springframework.http.*;
import org.springframework.util.DigestUtils;
import org.springframework.web.client.RestTemplate;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.List;
/**
* @author miaohuanju001
* @date 2021-07-06
* @description
/**
* 纯文本类型:
* {
* "msgtype": "text",
* "text": {
* "content": "广州今日天气:29度,大部分多云,降雨概率:60%",
* "mentioned_list":["wangqing","@all"],
* "mentioned_mobile_list":["13800001111","@all"]
* }
* }
*
* markdown类型:
* {
* "msgtype": "markdown",
* "markdown": {
* "content": "实时新增用户反馈<font color=\"warning\">132例</font>,请相关同事注意。\n
* >类型:<font color=\"comment\">用户反馈</font>
* >普通用户反馈:<font color=\"comment\">117例</font>
* >VIP用户反馈:<font color=\"comment\">15例</font>"
* }
* }
*
* 图文类型:
* {
* "msgtype": "news",
* "news": {
* "articles" : [
* {
* "title" : "中秋节礼品领取",
* "description" : "今年中秋节公司有豪礼相送",
* "url" : "www.qq.com",
* "picurl" : "http://res.mail.qq.com/node/ww/wwopenmng/images/independent/doc/test_pic_msg1.png"
* }
* ]
* }
* }
*
* 图片类型
* {
* "msgtype": "image",
* "image": {
* "base64": "DATA",
* "md5": "MD5"
* }
* }
*/
public class WeChatRobotUtils {
private static RestTemplate restTemplate = new RestTemplate();
/**
* 发送图片
*/
public static String sendPicture(File file, String webhookurl) {
RobotMessage robotMessage = generatePicture(file);
if (robotMessage == null){
return "error";
}
return send(robotMessage,webhookurl);
}
/**
* 发送图文类消息
*/
public static String sendRobotNews(List<Article> articleList, String webhookurl) {
RobotMessage robotMessage = generateNews(articleList);
return send(robotMessage, webhookurl);
}
/**
* 发送图文类消息
*/
public static String sendRobotNews(Article article, String webhookurl) {
RobotMessage robotMessage = generateNews(article);
return send(robotMessage, webhookurl);
}
/**
* 发送图文类消息
*/
public static String sendRobotNews(String title, String description, String url, String picturl, String webhookurl) {
Article article = new Article();
article.setTitle(title);
article.setDescription(description);
article.setUrl(url);
article.setPicurl(picturl);
RobotMessage robotMessage = generateNews(article);
return send(robotMessage, webhookurl);
}
/**
* 发送markdown消息
*/
public static String sendRobotMarkdown(String content, String webhookurl) {
RobotMessage robotMessage = generateMarkdown(content);
return send(robotMessage, webhookurl);
}
/**
* 发送text消息, 不@任何人
*/
public static String sendRobotText(String content, String webhookurl) {
RobotMessage robotMessage = generateText(content);
return send(robotMessage, webhookurl);
}
/**
* 发送text消息,@人
*/
public static String sendRobotText(String content, List<String> mentionedList, List<String> mentionedMobileList, String webhookurl) {
RobotMessage robotMessage = generateText(content, mentionedList, mentionedMobileList);
return send(robotMessage, webhookurl);
}
/**
* 生成 图文类型的robot消息
*/
private static RobotMessage generateNews(List<Article> articleList) {
RobotMessage robotMessage = new RobotMessage();
robotMessage.setMsgtype(RobotMessage.TYPE_NEWS);
JSONObject jsonObject = new JSONObject();
jsonObject.put("articles", articleList);
robotMessage.setNews(jsonObject);
return robotMessage;
}
/**
* 生成 图文类型的robot消息
*/
private static RobotMessage generateNews(Article article) {
List<Article> articleList = new ArrayList<>();
articleList.add(article);
return generateNews(articleList);
}
/**
* 生成 text消息
*
* @param content 消息内容
* @param mentionedList @哪些人,传miaohuanju001这种
* @param mentionedMobileList @哪些人,传手机号
*/
private static RobotMessage generateText(String content, List<String> mentionedList, List<String> mentionedMobileList) {
RobotMessage robotMessage = new RobotMessage();
robotMessage.setMsgtype(RobotMessage.TYPE_TEXT);
JSONObject jsonObject = new JSONObject();
jsonObject.put("content", content);
jsonObject.put("mentioned_list", mentionedList);
jsonObject.put("mentioned_mobile_list", mentionedMobileList);
robotMessage.setText(jsonObject);
return robotMessage;
}
/**
* 生成 text消息
*
* @param content 消息内容
*/
private static RobotMessage generateText(String content) {
return generateText(content, null, null);
}
/**
* 生成 markdown消息
*
* @param content 消息内容
*/
private static RobotMessage generateMarkdown(String content) {
RobotMessage robotMessage = new RobotMessage();
robotMessage.setMsgtype(RobotMessage.TYPE_TEXT);
JSONObject jsonObject = new JSONObject();
jsonObject.put("content", content);
robotMessage.setMarkdown(jsonObject);
return robotMessage;
}
private static RobotMessage generatePicture(File file){
byte[] data;
try {
FileInputStream fis = new FileInputStream(file);
ByteArrayOutputStream bos = new ByteArrayOutputStream(1024*1000*1000);
byte[] b = new byte[1024*200];
int n;
while ((n = fis.read(b)) != -1) {
bos.write(b, 0, n);
}
fis.close();
data = bos.toByteArray();
bos.close();
String md5 = DigestUtils.md5DigestAsHex(data);
String base64 = new String(Base64.encodeBase64(data));
RobotMessage robotMessage = new RobotMessage();
robotMessage.setMsgtype(RobotMessage.TYPE_IMAGE);
JSONObject imageJson = new JSONObject();
imageJson.put("md5",md5);
imageJson.put("base64",base64);
robotMessage.setImage(imageJson);
return robotMessage;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 发送robot消息
*/
private static String send(RobotMessage robotMessage, String webhookurl) {
String jsonMessage = JSONObject.toJSONString(robotMessage);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8); //发送内容的类型为 json
HttpEntity<String> entity = new HttpEntity<>(jsonMessage, httpHeaders);
ResponseEntity<String> resp = restTemplate.exchange(webhookurl, HttpMethod.POST, entity, String.class);
String body = resp.getBody();
JSONObject bodyJson = (JSONObject) (JSONObject.parse(body));
String errorMsg = bodyJson.getString("errmsg");
if (!"ok".equals(errorMsg)) {
return errorMsg;
}
return "success";
}
@Data
public static class Article {
private String title;
private String description;
/**
* 点击图片后的跳转链接
*/
private String url;
/**
* 图片链接
*/
private String picurl;
}
/**
* 纯文本类型:
* {
* "msgtype": "text",
* "text": {
* "content": "广州今日天气:29度,大部分多云,降雨概率:60%",
* "mentioned_list":["wangqing","@all"],
* "mentioned_mobile_list":["13800001111","@all"]
* }
* }
*
* markdown类型:
* {
* "msgtype": "markdown",
* "markdown": {
* "content": "实时新增用户反馈<font color=\"warning\">132例</font>,请相关同事注意。\n
* >类型:<font color=\"comment\">用户反馈</font>
* >普通用户反馈:<font color=\"comment\">117例</font>
* >VIP用户反馈:<font color=\"comment\">15例</font>"
* }
* }
*
* 图文类型:
* {
* "msgtype": "news",
* "news": {
* "articles" : [
* {
* "title" : "中秋节礼品领取",
* "description" : "今年中秋节公司有豪礼相送",
* "url" : "www.qq.com",
* "picurl" : "http://res.mail.qq.com/node/ww/wwopenmng/images/independent/doc/test_pic_msg1.png"
* }
* ]
* }
* }
*
* 图片类型
* {
* "msgtype": "image",
* "image": {
* "base64": "DATA",
* "md5": "MD5"
* }
* }
*/
@Data
public static class RobotMessage {
public static final String TYPE_TEXT = "text";
public static final String TYPE_MARKDOWN = "markdown";
/**
* 图文类型
*/
public static final String TYPE_NEWS = "news";
public static final String TYPE_IMAGE = "image";
/**
* 消息类型,
* 文本:text
* markdown
* 图文:news
*/
private String msgtype;
private JSONObject text;
private JSONObject markdown;
/**
* "base64"
* "md5"
*/
private JSONObject image;
/**
* "news":{"articles": [{article1},{article1}] }
*/
private JSONObject news;
public void makeNews(List<Article> articleList) {
this.news = new JSONObject();
news.put("articles", articleList);
}
}
}
ZKUtils
代码:
package zk;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.imps.CuratorFrameworkState;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
/**
* @author miaohuanju001
* @date 2021-07-20
* @description zk对节点操作的api封装
*/
public class ZKUtils {
private static final Logger logger = LoggerFactory.getLogger(ZKUtils.class);
public static int baseSleepTimeMs = 10; // 初始sleep时间
public static int maxRetry = 3; // 最大重试次数
public static String zkUrl = "zk01-test.xxx.com:2181"; // zk集群url
public static String scheme = "digest"; // addauth digest #{auth}
public static String auth = "xxxxxxxx"; // 为节点增加权限
public static int sessionTimeoutMs = 20000; // 会话超时时间 20000ms
public static int connectTimeoutMs = 15000; // 连接超时时间 15000ms
public static CuratorFramework zkClient;
static {
RetryPolicy retryPolicy = new ExponentialBackoffRetry(baseSleepTimeMs, maxRetry);
zkClient = CuratorFrameworkFactory.builder()
.connectString(zkUrl)
.authorization(scheme, auth.getBytes())
.sessionTimeoutMs(sessionTimeoutMs)
.connectionTimeoutMs(connectTimeoutMs)
.retryPolicy(retryPolicy).build();
zkClient.start();
}
/**
* 获取 zk客户端的状态
*
* @return LATENT、STARTED、STOPPED
*/
public static CuratorFrameworkState getZKClientState() {
return zkClient.getState();
}
/**
* 获取指定路径对应节点的信息
*
* @param path 节点路径,例如 /testZK/push/resource/10.224.65.216
* @return 节点信息
*/
public static String getNodeMessage(final String path) {
try {
byte[] bytes = zkClient.getData().forPath(path);
return new String(bytes, StandardCharsets.UTF_8);
} catch (Exception ex) {
logger.error("get key : {}", path, ex);
}
return null;
}
/**
* 获取指定路径下的子节点
*
* @param path 节点路径,例如 /testZK/push/resource
* @return 指定节点的子节点信息
*/
public static List<String> getChildrenNode(final String path) {
try {
return zkClient.getChildren().forPath(path);
} catch (KeeperException.NoNodeException ex) {
return new ArrayList<>();
} catch (InterruptedException ex) {
logger.error("getChildrenKeys path : {} InterruptedException", path);
throw new IllegalStateException(ex);
} catch (Exception ex) {
logger.error("getChildrenKeys path : {}", path, ex);
throw new RuntimeException(ex);
}
}
/**
* 判断指定路径下是否有节点
*
* @param path 指定路径
*/
public static boolean hasChildren(final String path) {
Stat stat;
try {
stat = zkClient.checkExists().forPath(path);
return stat.getNumChildren() >= 1;
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
}
/**
* 判断指定路径对应的节点是否存在
*
* @param path 指定的节点路径
*/
public static boolean isExisted(final String path) {
try {
return zkClient.checkExists().forPath(path) != null;
} catch (Exception ex) {
logger.error("isExisted key : {}", path, ex);
}
return false;
}
/**
* 创建永久节点,如果节点已存在则用输入信息覆盖
*
* @param path 指定的节点路径
* @param nodeMessage 节点信息
*/
public static void createPersistNode(final String path, final String nodeMessage) {
try {
if (!isExisted(path)) {
zkClient.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath(path, nodeMessage.getBytes(StandardCharsets.UTF_8));
} else {
update(path, nodeMessage);
}
} catch (Exception ex) {
logger.error("persist path : {} , nodeMessage : {}", path, nodeMessage, ex);
}
}
/**
* 创建临时节点
*
* @param path 指定的节点路径
* @param nodeMessage 节点信息
* @param overwrite 如果指定路径已存在,是否覆盖原有节点 true-覆盖 / false-不覆盖
*/
public static void createEphemeralNode(final String path, final String nodeMessage, final boolean overwrite) {
try {
if (overwrite) {
createEphemeralNode(path, nodeMessage);
} else {
if (!isExisted(path)) {
zkClient.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(path, nodeMessage.getBytes(StandardCharsets.UTF_8));
}
}
} catch (final Exception ex) {
logger.error("persistEphemeral path : {} , nodeMessage : {}, overwrite : {}", path, nodeMessage, overwrite, ex);
}
}
/**
* 更新节点信息,只更新节点本身存储的信息,不改变节点的子节点
* 如果节点不存在就会抓到exception
*
* @param path 指定的节点路径
* @param nodeMessage 节点信息
*/
public static void update(final String path, final String nodeMessage) {
try {
zkClient.inTransaction().check().forPath(path).and().setData().forPath(path, nodeMessage.getBytes(StandardCharsets.UTF_8)).and().commit();
} catch (Exception ex) {
logger.error("update path : {} , nodeMessage : {}", path, nodeMessage, ex);
}
}
/**
* 创建带序列号的临时节点
* <p>
* exam: ("/zk/testNode", "this is a serial Ephemeral node") 以该入参执行3次会创建如下节点
* [/zk/testNode0000000001, /zk/testNode0000000002, /zk/testNode0000000003]
*
* @param path 指定的节点路径
* @param nodeMessage 节点信息
*/
public static void persistEphemeralSequential(final String path, String nodeMessage) {
try {
zkClient.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(path, nodeMessage.getBytes(StandardCharsets.UTF_8));
} catch (final Exception ex) {
logger.error("persistEphemeralSequential path : {}", path, ex);
}
}
/**
* 删除指定节点
*
* @param path 指定的节点路径
*/
public static void remove(final String path) {
try {
if (isExisted(path)) {
zkClient.delete().deletingChildrenIfNeeded().forPath(path);
}
} catch (KeeperException.NoNodeException ignore) {
//NOP
} catch (final Exception ex) {
logger.error("remove path : {}", path, ex);
}
}
/**
* 创建临时节点
*
* @param path 指定的节点路径
* @param nodeMessage 节点信息
*/
private static void createEphemeralNode(final String path, final String nodeMessage) {
try {
if (isExisted(path)) {
try {
zkClient.delete().deletingChildrenIfNeeded().forPath(path);
} catch (KeeperException.NoNodeException ignore) {
//NOP
}
}
zkClient.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(path, nodeMessage.getBytes(StandardCharsets.UTF_8));
} catch (final Exception ex) {
logger.error("persistEphemeral path : {} , nodeMessage : {}", path, nodeMessage, ex);
}
}
/**
* 在指定节点下生成serial节点的方式获取锁,
* 持有锁的线程可重入,其余抢锁线程会在 mutex.acquire() 后阻塞
* 调用该方法后需要在finally中 mutex.release()释放锁
*
* @param path 指定的节点路径
* @return InterProcessMutex mutex
*/
public static InterProcessMutex getMasterBlockMutex(String path){
InterProcessMutex mutex = null;
try {
mutex = new InterProcessMutex(zkClient, path);
mutex.acquire();
} catch (Exception e) {
e.printStackTrace();
}
return mutex;
}
public static void main(String[] args) {
remove("/testZK/asdfasfasdf");
int x = 0;
}
}
pom依赖:
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.13.0</version>
</dependency>