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>