From 402ac5340d473a7b2ac86ffc8833d50e7161629b Mon Sep 17 00:00:00 2001 From: DB <2502523450@qq.com> Date: Mon, 9 Oct 2023 10:45:41 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E9=87=8D=E6=9E=84=E5=88=9D?= =?UTF-8?q?=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 35 + Cpop-Common/pom.xml | 39 + .../com/cpop/common/constant/Constants.java | 196 ++++ .../com/cpop/common/constant/HttpStatus.java | 88 ++ .../com/cpop/common/constant/RedisKey.java | 19 + .../java/com/cpop/common/enums/CycleEnum.java | 56 ++ .../com/cpop/common/enums/ErrorCodeEnum.java | 87 ++ .../java/com/cpop/common/utils/DateUtils.java | 653 +++++++++++++ .../com/cpop/common/utils/ServletUtils.java | 155 ++++ .../com/cpop/common/utils/StringUtils.java | 534 +++++++++++ .../com/cpop/common/utils/bean/BeanUtils.java | 193 ++++ .../cpop/common/utils/html/EscapeUtil.java | 134 +++ .../cpop/common/utils/html/HTMLFilter.java | 499 ++++++++++ .../cpop/common/utils/http/HttpHelper.java | 44 + .../com/cpop/common/utils/http/HttpUtils.java | 322 +++++++ .../cpop/common/utils/ip/AddressUtils.java | 51 ++ .../com/cpop/common/utils/ip/IpUtils.java | 196 ++++ .../cpop/common/utils/text/CharsetKit.java | 91 ++ .../com/cpop/common/utils/text/Convert.java | 854 ++++++++++++++++++ .../cpop/common/utils/text/StrFormatter.java | 77 ++ README.md | 18 + pom.xml | 80 ++ 22 files changed, 4421 insertions(+) create mode 100644 .gitignore create mode 100644 Cpop-Common/pom.xml create mode 100644 Cpop-Common/src/main/java/com/cpop/common/constant/Constants.java create mode 100644 Cpop-Common/src/main/java/com/cpop/common/constant/HttpStatus.java create mode 100644 Cpop-Common/src/main/java/com/cpop/common/constant/RedisKey.java create mode 100644 Cpop-Common/src/main/java/com/cpop/common/enums/CycleEnum.java create mode 100644 Cpop-Common/src/main/java/com/cpop/common/enums/ErrorCodeEnum.java create mode 100644 Cpop-Common/src/main/java/com/cpop/common/utils/DateUtils.java create mode 100644 Cpop-Common/src/main/java/com/cpop/common/utils/ServletUtils.java create mode 100644 Cpop-Common/src/main/java/com/cpop/common/utils/StringUtils.java create mode 100644 Cpop-Common/src/main/java/com/cpop/common/utils/bean/BeanUtils.java create mode 100644 Cpop-Common/src/main/java/com/cpop/common/utils/html/EscapeUtil.java create mode 100644 Cpop-Common/src/main/java/com/cpop/common/utils/html/HTMLFilter.java create mode 100644 Cpop-Common/src/main/java/com/cpop/common/utils/http/HttpHelper.java create mode 100644 Cpop-Common/src/main/java/com/cpop/common/utils/http/HttpUtils.java create mode 100644 Cpop-Common/src/main/java/com/cpop/common/utils/ip/AddressUtils.java create mode 100644 Cpop-Common/src/main/java/com/cpop/common/utils/ip/IpUtils.java create mode 100644 Cpop-Common/src/main/java/com/cpop/common/utils/text/CharsetKit.java create mode 100644 Cpop-Common/src/main/java/com/cpop/common/utils/text/Convert.java create mode 100644 Cpop-Common/src/main/java/com/cpop/common/utils/text/StrFormatter.java create mode 100644 README.md create mode 100644 pom.xml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2867377 --- /dev/null +++ b/.gitignore @@ -0,0 +1,35 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ +**/src/main/resources/static/keyPair/* + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ +rebel.xml \ No newline at end of file diff --git a/Cpop-Common/pom.xml b/Cpop-Common/pom.xml new file mode 100644 index 0000000..6e34fb1 --- /dev/null +++ b/Cpop-Common/pom.xml @@ -0,0 +1,39 @@ + + + 4.0.0 + + com.cpop + Cpop-Union + 1.0.0 + ../pom.xml + + Cpop-Common + Cpop-Common + 公共包 + jar + + + + + org.apache.commons + commons-text + + + + commons-io + commons-io + + + + joda-time + joda-time + + + + com.alibaba + fastjson + + + + diff --git a/Cpop-Common/src/main/java/com/cpop/common/constant/Constants.java b/Cpop-Common/src/main/java/com/cpop/common/constant/Constants.java new file mode 100644 index 0000000..8920454 --- /dev/null +++ b/Cpop-Common/src/main/java/com/cpop/common/constant/Constants.java @@ -0,0 +1,196 @@ +package com.cpop.common.constant; + +/** + * 通用常量信息 + * + * @author DB + */ +public interface Constants { + /** + * UTF-8 字符集 + */ + String PC = "/pc"; + /** + * UTF-8 字符集 + */ + String APP = "/app"; + /** + * UTF-8 字符集 + */ + String APPLETS = "/applets"; + /** + * UTF-8 字符集 + */ + String UTF8 = "UTF-8"; + + /** + * contentType json/utf-8 + */ + String CONTENT_TYPE_JSON_UTF8 = "application/json;charset=utf-8"; + + /** + * GBK 字符集 + */ + String GBK = "GBK"; + + /** + * http请求 + */ + String HTTP = "http://"; + + /** + * https请求 + */ + String HTTPS = "https://"; + + /** + * 通用成功标识 + */ + Integer SUCCESS = 200; + + /** + * 通用失败标识 + */ + Integer FAIL = 400; + + /** + * 登录成功 + */ + String LOGIN_SUCCESS = "Success"; + + /** + * 注销 + */ + String LOGOUT = "Logout"; + + /** + * 注册 + */ + String REGISTER = "Register"; + + /** + * 登录失败 + */ + String LOGIN_FAIL = "Error"; + + /** + * 验证码 redis key + */ + String CAPTCHA_CODE_KEY = "captcha_codes:"; + + /** + * 登录用户 redis key + */ + String LOGIN_TOKEN_KEY = "login_tokens:"; + + /** + * 防重提交 redis key + */ + String REPEAT_SUBMIT_KEY = "repeat_submit:"; + + /** + * 限流 redis key + */ + String RATE_LIMIT_KEY = "rate_limit:"; + + /** + * 验证码有效期(分钟) + */ + Integer CAPTCHA_EXPIRATION = 2; + + /** + * 令牌 + */ + String TOKEN = "token"; + + /** + * 令牌前缀 + */ + String TOKEN_PREFIX = "Bearer "; + + /** + * 令牌前缀 + */ + String LOGIN_USER_KEY = "login_user_key"; + + /** + * 用户ID + */ + String JWT_USERID = "userid"; + + /** + * 用户名称 + */ + //String JWT_USERNAME = Claims.SUBJECT; + + /** + * 用户头像 + */ + String JWT_AVATAR = "avatar"; + + /** + * 创建时间 + */ + String JWT_CREATED = "created"; + + /** + * 用户权限 + */ + String JWT_AUTHORITIES = "authorities"; + + /** + * 参数管理 cache key + */ + String SYS_CONFIG_KEY = "sys_config:"; + + /** + * 字典管理 cache key + */ + String SYS_DICT_KEY = "sys_dict:"; + + /** + * 资源映射路径 前缀 + */ + String RESOURCE_PREFIX = "/profile"; + + /** + * RMI 远程方法调用 + */ + String LOOKUP_RMI = "rmi://"; + + /** + * LDAP 远程方法调用 + */ + String LOOKUP_LDAP = "ldap://"; + + /** + * 定时任务违规的字符 + */ + String[] JOB_ERROR_STR = {"java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml", + "org.springframework.jndi"}; + + /** + * 超级管理员 + */ + String SUPER_ADMIN = "PuPu"; + + /** + * 超级管理员 + */ + String SUPER_ADMIN_VALUE = "superAdmin"; + + /** + * 所有权限 + */ + String ALL_PERMISSION = "*:*:*"; + + /** + * 隐藏菜单 + */ + String HIDE_MENU = "Menu"; + + /** + * 用户类型 + */ + String USER_TYPE = "UserType"; +} diff --git a/Cpop-Common/src/main/java/com/cpop/common/constant/HttpStatus.java b/Cpop-Common/src/main/java/com/cpop/common/constant/HttpStatus.java new file mode 100644 index 0000000..574832d --- /dev/null +++ b/Cpop-Common/src/main/java/com/cpop/common/constant/HttpStatus.java @@ -0,0 +1,88 @@ +package com.cpop.common.constant; + +/** + * 返回状态码 + * + * @author DB + */ +public interface HttpStatus { + /** + * 操作成功 + */ + int SUCCESS = 200; + + /** + * 对象创建成功 + */ + int CREATED = 201; + + /** + * 请求已经被接受 + */ + int ACCEPTED = 202; + + /** + * 操作已经执行成功,但是没有返回数据 + */ + int NO_CONTENT = 204; + + /** + * 资源已被移除 + */ + int MOVED_PERM = 301; + + /** + * 重定向 + */ + int SEE_OTHER = 303; + + /** + * 资源没有被修改 + */ + int NOT_MODIFIED = 304; + + /** + * 参数列表错误(缺少,格式不匹配) + */ + int BAD_REQUEST = 400; + + /** + * 未授权 + */ + int UNAUTHORIZED = 401; + + /** + * 访问受限,授权过期 + */ + int FORBIDDEN = 403; + + /** + * 资源,服务未找到 + */ + int NOT_FOUND = 404; + + /** + * 不允许的http方法 + */ + int BAD_METHOD = 405; + + /** + * 资源冲突,或者资源被锁 + */ + int CONFLICT = 409; + + /** + * 不支持的数据,媒体类型 + */ + int UNSUPPORTED_TYPE = 415; + + /** + * 系统内部错误 + */ + int ERROR = 500; + + /** + * 接口未实现 + */ + int NOT_IMPLEMENTED = 501; +} diff --git a/Cpop-Common/src/main/java/com/cpop/common/constant/RedisKey.java b/Cpop-Common/src/main/java/com/cpop/common/constant/RedisKey.java new file mode 100644 index 0000000..cf93434 --- /dev/null +++ b/Cpop-Common/src/main/java/com/cpop/common/constant/RedisKey.java @@ -0,0 +1,19 @@ +package com.cpop.common.constant; + +/** + * @author DB + * @date 2022-09-23 23:28 + */ +public interface RedisKey { + + /** + * 验证码 + */ + String CAPTCHA_KEY = "sys:sysLogin:captcha:"; + + /** + * redis分布式锁key + */ + String DISTRIBUTED_LOCK = "sys:redis:distributedLock"; + +} diff --git a/Cpop-Common/src/main/java/com/cpop/common/enums/CycleEnum.java b/Cpop-Common/src/main/java/com/cpop/common/enums/CycleEnum.java new file mode 100644 index 0000000..1699bbb --- /dev/null +++ b/Cpop-Common/src/main/java/com/cpop/common/enums/CycleEnum.java @@ -0,0 +1,56 @@ +package com.cpop.common.enums; + +/** + * @author DB + */ +public enum CycleEnum { + /** + * 日 + */ + DAY(1, "每天"), + /** + * 月 + */ + MONTH(2, "月"), + /** + * 季 + */ + WEEK(3, "季"), + /** + * 年 + */ + YEAR(4, "年"); + + private int codeId; + private String message; + + CycleEnum(int codeId, String message) { + this.codeId = codeId; + this.message = message; + } + + public static CycleEnum getCycle(int code) { + for (CycleEnum c : CycleEnum.values()) { + if (c.codeId == code) { + return c; + } + } + return null; + } + + public int getCodeId() { + return codeId; + } + + public void setCodeId(int codeId) { + this.codeId = codeId; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} diff --git a/Cpop-Common/src/main/java/com/cpop/common/enums/ErrorCodeEnum.java b/Cpop-Common/src/main/java/com/cpop/common/enums/ErrorCodeEnum.java new file mode 100644 index 0000000..3313ffc --- /dev/null +++ b/Cpop-Common/src/main/java/com/cpop/common/enums/ErrorCodeEnum.java @@ -0,0 +1,87 @@ +package com.cpop.common.enums; + +/** + * @author LOST.yuan + */ +public enum ErrorCodeEnum { + + /** jwt凭据已过期 */ + HTTP_401(401, "权限不足"), + + NULL_TOKEN(402, "未登录!"), + + TOKEN_EXPIRED(403, "登录已失效!"), + + /** + * 登录异常 + */ + LOGIN_EXCEPTION(2000, "登录异常!"), + + TOKEN_CREATE_ERROR(2001, "TOKEN生成失败!"), + + WRONG_LOGIN_PASSWORD(2002, "登录密码错误!"), + + WRONG_LOGIN_USERNAME(2003, "登录用户名错误!"), + /** + * 获取请求信息失败 + */ + GET_REQUEST_INFO_FAILED(2004, "获取请求信息失败!"), + /** + * 验证码错误 + */ + CAPTCHA_ERROR(2005,"验证码错误"), + /** + * 验证码失效FileSizeLimitExceeded + */ + CAPTCHA_EXPIRE(2006,"验证码失效"), + /** + * 文件名称超长限制 + */ + FILE_NAME_TOO_LONG(2007,"文件名称超出限制,文件名长度为:"), + /** + * 文件名大小限制 + */ + FILE_SIZE_LIMIT_EXCEEDED(2008,"文件名大小超出限制,文件名大小为:"), + + //2500--内部服务异常 + /** + * 操作日志异常 + */ + FILE_UPLOAD_EXCEPTION(2500,"文件上传异常"), + /** + * 操作日志异常 + */ + OPERATION_LOG_EXCEPTION(2501,"操作日志异常"), + /** + * 系统邮箱异常 + */ + SYS_EMAIL_EXCEPTION(2502,"系统邮箱异常"), + ; + + private final Integer code; + + private final String info; + + ErrorCodeEnum(Integer code, String info) { + this.code = code; + this.info = info; + } + + public Integer getCode() { + return code; + } + + public String getInfo() { + return info; + } + + public static ErrorCodeEnum codeOf(Integer code) { + for (ErrorCodeEnum value : values()) { + if (value.getCode().equals(code)) { + return value; + } + } + return null; + } + +} diff --git a/Cpop-Common/src/main/java/com/cpop/common/utils/DateUtils.java b/Cpop-Common/src/main/java/com/cpop/common/utils/DateUtils.java new file mode 100644 index 0000000..fdf815e --- /dev/null +++ b/Cpop-Common/src/main/java/com/cpop/common/utils/DateUtils.java @@ -0,0 +1,653 @@ +package com.cpop.common.utils; + +import com.pupu.common.enums.CycleEnum; +import org.apache.commons.lang3.time.DateFormatUtils; +import org.joda.time.DateTime; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.management.ManagementFactory; +import java.net.HttpURLConnection; +import java.net.URL; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.DayOfWeek; +import java.time.Instant; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +/** + * 时间工具类 + * + * @author DB + */ +public class DateUtils extends org.apache.commons.lang3.time.DateUtils { + public static String YYYY = "yyyy"; + + public static String YYYY_MM = "yyyy-MM"; + + public static String YYYY_MM_DD = "yyyy-MM-dd"; + + public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss"; + + public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"; + + public static String YYYYMMDD = "yyyyMMdd"; + + public static String TIME = "HH:mm:ss"; + /** + * (yyyy-MM-dd HH:mm) + */ + public final static String DATE_TIME_HOUR_MINUTE = "yyyy-MM-dd HH:mm"; + + private static final String[] parsePatterns = { + "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", + "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", + "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"}; + + public final static String HOUR = "HH:mm"; + + /** + * 获取当前Date型日期 + * + * @return Date() 当前日期 + */ + public static Date getNowDate() { + return new Date(); + } + + /** + * 获取当前日期, 默认格式为yyyy-MM-dd + * + * @return String + */ + public static String getDate() { + return dateTimeNow(YYYY_MM_DD); + } + + public static String getTime() { + return dateTimeNow(YYYY_MM_DD_HH_MM_SS); + } + + public static String dateTimeNow() { + return dateTimeNow(YYYYMMDDHHMMSS); + } + + public static String dateTimeNow(final String format) { + return parseDateToStr(format, new Date()); + } + + public static String dateTime(final Date date) { + return parseDateToStr(YYYY_MM_DD, date); + } + + public static String parseDateToStr(final String format, final Date date) { + return new SimpleDateFormat(format).format(date); + } + + public static Date dateTime(final String format, final String ts) { + try { + return new SimpleDateFormat(format).parse(ts); + } catch (ParseException e) { + throw new RuntimeException(e); + } + } + + /** + * 日期路径 即年/月/日 如2018/08/08 + */ + public static String datePath() { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyy/MM/dd"); + } + + /** + * 日期路径 即年/月/日 如20180808 + */ + public static String dateTime() { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyyMMdd"); + } + + /** + * 日期型字符串转化为日期 格式 + */ + public static Date parseDate(Object str) { + if (str == null) { + return null; + } + try { + return parseDate(str.toString(), parsePatterns); + } catch (ParseException e) { + return null; + } + } + + /** + * 获取服务器启动时间 + */ + public static Date getServerStartDate() { + long time = ManagementFactory.getRuntimeMXBean().getStartTime(); + return new Date(time); + } + + /** + * 计算两个时间差 + */ + public static String getDatePoor(Date endDate, Date nowDate) { + long nd = 1000 * 24 * 60 * 60; + long nh = 1000 * 60 * 60; + long nm = 1000 * 60; + // long ns = 1000; + // 获得两个时间的毫秒时间差异 + long diff = endDate.getTime() - nowDate.getTime(); + // 计算差多少天 + long day = diff / nd; + // 计算差多少小时 + long hour = diff % nd / nh; + // 计算差多少分钟 + long min = diff % nd % nh / nm; + // 计算差多少秒//输出结果 + // long sec = diff % nd % nh % nm / ns; + return day + "天" + hour + "小时" + min + "分钟"; + } + + /** + * 对日期的【月】进行加/减 + * + * @param date 日期 + * @param months 月数,负数为减 + * @return 加/减几月后的日期 + */ + public static Date addDateMonths(Date date, int months) { + DateTime dateTime = new DateTime(date); + return dateTime.plusMonths(months).toDate(); + } + + /** + * 获取指定时间前一天的时间 + * + * @param date + * @return + * @Author: ZJ + */ + public static Date getLasDate(Date date) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + calendar.set(Calendar.DATE, calendar.get(Calendar.DATE) - 1); + return calendar.getTime(); + } + + /** + * 对日期的【天】进行加/减 + * + * @param date 日期 + * @param days 天数,负数为减 + * @return 加/减几天后的日期 + */ + public static Date addDateDays(Date date, int days) { + DateTime dateTime = new DateTime(date); + return dateTime.plusDays(days).toDate(); + } + + /** + * 对日期的【小时】进行加/减 + * + * @param date 日期 + * @param hours 小时数,负数为减 + * @return 加/减几小时后的日期 + */ + public static Date addDateHours(Date date, int hours) { + DateTime dateTime = new DateTime(date); + return dateTime.plusHours(hours).toDate(); + } + + /** + * 判断当前时间是周几 + * + * @param date + * @return + */ + public static String getWeek(Date date) { + String format = DateUtils.parseDateToStr("yyyy-MM-dd", date); + DateFormat format1 = new SimpleDateFormat("yyyy-MM-dd"); + Date now = null; + try { + now = format1.parse(format); + } catch (ParseException e) { + e.printStackTrace(); + } + assert now != null; + Instant instant = now.toInstant(); + ZoneId zoneId = ZoneId.systemDefault(); + java.time.LocalDate localDate = instant.atZone(zoneId).toLocalDate(); + DayOfWeek day = localDate.getDayOfWeek(); + String WeekDay = ""; + switch (day) { + case MONDAY: + WeekDay = "1"; + break; + case FRIDAY: + WeekDay = "5"; + break; + case SATURDAY: + WeekDay = "6"; + break; + case SUNDAY: + WeekDay = "7"; + break; + case THURSDAY: + WeekDay = "4"; + break; + case TUESDAY: + WeekDay = "2"; + break; + case WEDNESDAY: + WeekDay = "3"; + break; + } + return WeekDay; + } + + /** + * 获取指定时间前一个月的时间 + * + * @param date 指定时间如果传空为当前时间 + * @return + * @Author: 李性禄 + */ + public static Date getLastMon(Date date) { + Calendar calendar = Calendar.getInstance(); + if (date != null) { + calendar.setTime(date); + } + calendar.add(Calendar.MONTH, -1); + return calendar.getTime(); + } + + /** + * 获取指定时间前一年的时间 + * + * @param date 指定时间如果传空为当前时间 + * @return + * @Author: 李性禄 + */ + public static Date getLastYear(Date date) { + Calendar calendar = Calendar.getInstance(); + if (date != null) { + calendar.setTime(date); + } + calendar.add(Calendar.YEAR, -1); + return calendar.getTime(); + } + + /** + * 传入两个时间,比较两个时间相差年、月、日 + * + * @param startDate + * @param endxDate + * @return + */ + public static List getTimeList(String startDate, String endxDate) { + SimpleDateFormat sdf; + int calendarType; + + switch (startDate.length()) { + case 10: + sdf = new SimpleDateFormat("yyyy-MM-dd"); + calendarType = Calendar.DATE; + break; + case 7: + sdf = new SimpleDateFormat("yyyy-MM"); + calendarType = Calendar.MONTH; + break; + case 4: + sdf = new SimpleDateFormat("yyyy"); + calendarType = Calendar.YEAR; + break; + default: + return null; + } + + List result = new ArrayList<>(); + Calendar min = Calendar.getInstance(); + Calendar max = Calendar.getInstance(); + try { + min.setTime(sdf.parse(startDate)); + min.add(calendarType, 0); + max.setTime(sdf.parse(endxDate)); + max.add(calendarType, 1); + } catch (ParseException e) { + e.printStackTrace(); + } + Calendar curr = min; + while (curr.before(max)) { + result.add(sdf.format(min.getTime())); + curr.add(calendarType, 1); + } + return result; + } + + /** + * 日期转换字符串 + * + * @param date + * @param pattern 转换的类型 + * @return + */ + public static String format(Date date, String pattern) { + if (date != null) { + SimpleDateFormat df = new SimpleDateFormat(pattern); + return df.format(date); + } + return null; + } + + /** + * 根据相差毫秒值查询相差多少天 + * + * @param result + * @return + */ + public static long days(long result) { + return result / (1000 * 60 * 60 * 24); + } + + /** + * 根据相差毫秒值查询相差多少个钟 + * + * @param result + * @return + */ + public static long hour(long result) { + return (result % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60); + } + + /** + * 根据相差毫秒值查询相差多少分钟 + * + * @param result + * @return + */ + public static long minutes(long result) { + return (result % (1000 * 60 * 60)) / (1000 * 60); + } + + /** + * 字符串转换成日期 + * + * @param strDate 日期字符串 + * @param pattern 日期的格式,如:DateUtils.DATE_TIME_PATTERN + */ + public static Date stringToDate(String strDate, String pattern) { + if (StringUtils.isBlank(strDate)) { + return null; + } + DateTimeFormatter fmt = DateTimeFormat.forPattern(pattern); + return fmt.parseLocalDateTime(strDate).toDate(); + } + + /** + * 根据传入的时间获取当前月份的所有日期 + * + * @param date + * @return + */ + public static List getAllTheDateOfTheMonth(Date date) { + List list = new ArrayList(); + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + cal.set(Calendar.DATE, 1); + int month = cal.get(Calendar.MONTH); + while (cal.get(Calendar.MONTH) == month) { + list.add(cal.getTime()); + cal.add(Calendar.DAY_OF_YEAR, 1); + } + return list; + } + + /** + * 根据传入的时间获取是当月的第几天 + * + * @param date + * @return + */ + public static Integer dayOfMonth(Date date) { + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + return cal.get(Calendar.DAY_OF_MONTH); + } + + /** + * 根据当前时间yyyyMMdd 格式判断是否是节假日(私人api慎用,容易服务挂掉) + * + * @param date 请求参数 + * @return 返回结果 默认返回 0:工作1-5 1:周末 2:节假日 + */ + public static String holidays(String date) { + String httpUrl = "http://tool.bitefu.net/jiari/"; + BufferedReader reader = null; + String holidays = null; + StringBuffer sbf = new StringBuffer(); + httpUrl = httpUrl + "?d=" + date; + try { + URL url = new URL(httpUrl); + HttpURLConnection connection = (HttpURLConnection) url + .openConnection(); + connection.setRequestMethod("GET"); + connection.connect(); + InputStream is = connection.getInputStream(); + reader = new BufferedReader(new InputStreamReader(is, "UTF-8")); + String strRead = null; + while ((strRead = reader.readLine()) != null) { + sbf.append(strRead); + } + reader.close(); + holidays = sbf.toString(); + } catch (Exception e) { + e.printStackTrace(); + } + return holidays; + } + + /** + * 获取两个时间之前的日期 + * + * @return + */ + public static List getDatesBetweenTwoDatesForApprove(Date startTime, Date endTime) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + String date1 = sdf.format(startTime); + String date2 = sdf.format(endTime); + List dateList = new ArrayList(); + try { + Date dateOne = sdf.parse(date1); + Date dateTwo = sdf.parse(date2); + Calendar calendar = Calendar.getInstance(); + calendar.setTime(dateOne); + dateList.add(sdf.format(dateOne)); + while (calendar.getTime().before(dateTwo)) { + calendar.add(Calendar.DAY_OF_MONTH, +1); + dateList.add(sdf.format(calendar.getTime())); + } + } catch (Exception e) { + e.printStackTrace(); + } + return dateList; + } + + /** + * 计算两个时间是否为同一周期 + * + * @param d1 时间1 + * @param d2 时间2 + * @param type 周期类型 + */ + public static Boolean sameCycle(Date d1, Date d2, int type) { + CycleEnum cycle = CycleEnum.getCycle(type); + assert cycle != null; + if (cycle.equals(CycleEnum.YEAR)) { + return isSameYear(d1, d2); + } else if (cycle.equals(CycleEnum.MONTH)) { + return isSameMonth(d1, d2); + } else if (cycle.equals(CycleEnum.WEEK)) { + return isSameWeek(d1, d2); + } else if (cycle.equals(CycleEnum.DAY)) { + return isSameDate(d1, d2); + } + return false; + } + + /** + * 同一年 + * + * @param date1 + * @param date2 + */ + public static Boolean isSameYear(Date date1, Date date2) { + Calendar cal1 = Calendar.getInstance(); + cal1.setTime(date1); + Calendar cal2 = Calendar.getInstance(); + cal2.setTime(date2); + return cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR); + } + + /** + * 同一月 + * + * @param date1 + * @param date2 + * @return + */ + public static Boolean isSameMonth(Date date1, Date date2) { + if (!isSameYear(date1, date2)) { + return false; + } + Calendar cal1 = Calendar.getInstance(); + cal1.setTime(date1); + Calendar cal2 = Calendar.getInstance(); + cal2.setTime(date2); + return cal1.get(Calendar.MONTH) == cal2.get(Calendar.MONTH); + } + + /** + * 同一周 + * + * @param date1 + * @param date2 + * @return + */ + public static Boolean isSameWeek(Date date1, Date date2) { + Calendar cal1 = Calendar.getInstance(); + Calendar cal2 = Calendar.getInstance(); + //西方周日为一周的第一天,咱得将周一设为一周第一天 + cal1.setFirstDayOfWeek(Calendar.MONDAY); + cal1.setTime(date1); + cal2.setTime(date2); + return cal1.get(Calendar.WEEK_OF_YEAR) == cal2.get(Calendar.WEEK_OF_YEAR); + } + + /** + * 同一天 + * + * @param date1 + * @param date2 + * @return + */ + public static Boolean isSameDate(Date date1, Date date2) { + if (!isSameMonth(date1, date2)) { + return false; + } + Calendar cal1 = Calendar.getInstance(); + cal1.setTime(date1); + Calendar cal2 = Calendar.getInstance(); + cal2.setTime(date2); + return cal1.get(Calendar.DAY_OF_MONTH) == cal2.get(Calendar.DAY_OF_MONTH); + } + + /** + * 查看两个时间之间的日期(包含结束那一天) + * + * @param startTime + * @param endTime + * @return + */ + public static List getDatesBetweenTwoDatesT(Date startTime, Date endTime) { + List days = new ArrayList<>(); + Calendar startCal = Calendar.getInstance(); + startCal.setTime(startTime); + Calendar endCal = Calendar.getInstance(); + endCal.setTime(endTime); + endCal.add(Calendar.DATE, 1); + while (startCal.before(endCal)) { + days.add(DateUtils.format(startCal.getTime(), DateUtils.YYYY_MM_DD)); + startCal.add(Calendar.DAY_OF_YEAR, 1); + } + return days; + } + + + /** + * 获取指定时间内所有时间,所有天数转为字符串集合 + * + * @param startTime 开始时间, + * * @param endTime 结束时间 + * *@Author: 李性禄 + * @return + */ + public static List getDateList(Date startTime, Date endTime, String format) { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format); + List list = new ArrayList<>(); + list.add(simpleDateFormat.format(startTime)); + String endDay = simpleDateFormat.format(endTime); + + String end = null; + Date date = startTime; + while (!endDay.equals(end)) { + if ("yyyy-MM-dd".equals(format)) { + date = getLasDate(date); + } + if ("yyyy-MM".equals(format)) { + date = getLastMon(date); + } + end = simpleDateFormat.format(date); + list.add(end); + + } + return list; + } + + + public static String setTime(Date date) { + long millis = System.currentTimeMillis(); + String endTim = ""; + if (date != null) { + long time = date.getTime(); + long end = (millis - time) / 1000; + if (end < 60L) { + endTim = end + "秒前"; + } else if (end < 3600L) { + endTim = (end / 60) + "分前"; + } else if (end < (long) (60 * 60 * 24)) { + endTim = (end / (60 * 60)) + "小时前"; + } else if (end < (long) (60 * 60 * 24 * 30)) { + endTim = (end / (60 * 60 * 24)) + "天前"; + } else if (end < (long) (60 * 60 * 24 * 30 * 12)) { + endTim = (end / (60 * 60 * 24)) + "月前"; + } else { + endTim = (end / (60 * 60 * 24 * 30 * 12)) + "年前"; + } + } + return endTim; + } +} + + + diff --git a/Cpop-Common/src/main/java/com/cpop/common/utils/ServletUtils.java b/Cpop-Common/src/main/java/com/cpop/common/utils/ServletUtils.java new file mode 100644 index 0000000..0c09b8a --- /dev/null +++ b/Cpop-Common/src/main/java/com/cpop/common/utils/ServletUtils.java @@ -0,0 +1,155 @@ +package com.cpop.common.utils; + +import com.alibaba.fastjson.JSONObject; +import com.pupu.common.utils.text.Convert; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.BufferedReader; +import java.io.IOException; + +/** + * 客户端工具类 + * + * @author DB + */ +public class ServletUtils { + + /** + * 获取String参数 + */ + public static String getParameter(String name) { + return getRequest().getParameter(name); + } + + /** + * 获取String参数 + */ + public static String getParameter(String name, String defaultValue) { + return Convert.toStr(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取Integer参数 + */ + public static Integer getParameterToInt(String name) { + String parameter = getRequest().getParameter(name); + if (StringUtils.isBlank(parameter)){ + //尝试从请求体中获取 + BufferedReader reader; + try { + reader = getRequest().getReader(); + StringBuilder body = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + body.append(line); + } + String requestBody = body.toString(); + //转json + JSONObject json = JSONObject.parseObject(requestBody); + if (null == json) { + return null; + } + parameter = json.getString(name); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + return Convert.toInt(parameter); + } + + /** + * 获取Integer参数 + */ + public static Integer getParameterToInt(String name, Integer defaultValue) { + return Convert.toInt(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取Boolean参数 + */ + public static Boolean getParameterToBool(String name) { + return Convert.toBool(getRequest().getParameter(name)); + } + + /** + * 获取Boolean参数 + */ + public static Boolean getParameterToBool(String name, Boolean defaultValue) { + return Convert.toBool(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取request + */ + public static HttpServletRequest getRequest() { + return getRequestAttributes().getRequest(); + } + + /** + * 获取response + */ + public static HttpServletResponse getResponse() { + return getRequestAttributes().getResponse(); + } + + /** + * 获取session + */ + public static HttpSession getSession() { + return getRequest().getSession(); + } + + public static ServletRequestAttributes getRequestAttributes() { + RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); + return (ServletRequestAttributes) attributes; + } + + /** + * 将字符串渲染到客户端 + * + * @param response 渲染对象 + * @param string 待渲染的字符串 + * @return null + */ + public static String renderString(HttpServletResponse response, String string) { + try { + response.setStatus(200); + response.setContentType("application/json"); + response.setCharacterEncoding("utf-8"); + response.getWriter().print(string); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + /** + * 是否是Ajax异步请求 + * + * @param request + */ + public static boolean isAjaxRequest(HttpServletRequest request) { + String accept = request.getHeader("accept"); + if (accept != null && accept.contains("application/json")) { + return true; + } + + String xRequestedWith = request.getHeader("X-Requested-With"); + if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) { + return true; + } + + String uri = request.getRequestURI(); + if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml")) { + return true; + } + + String ajax = request.getParameter("__ajax"); + return StringUtils.inStringIgnoreCase(ajax, "json", "xml"); + } +} diff --git a/Cpop-Common/src/main/java/com/cpop/common/utils/StringUtils.java b/Cpop-Common/src/main/java/com/cpop/common/utils/StringUtils.java new file mode 100644 index 0000000..b41a228 --- /dev/null +++ b/Cpop-Common/src/main/java/com/cpop/common/utils/StringUtils.java @@ -0,0 +1,534 @@ +package com.cpop.common.utils; + +import com.pupu.common.constant.Constants; +import com.pupu.common.utils.text.StrFormatter; +import org.springframework.util.AntPathMatcher; + +import java.util.*; + +/** + * 字符串工具类 + * + * @author DB + */ +public class StringUtils extends org.apache.commons.lang3.StringUtils { + /** + * 空字符串 + */ + private static final String NULL_STR = ""; + + /** + * 下划线 + */ + private static final char SEPARATOR = '_'; + + /** + * 获取参数不为空值 + * + * @param value defaultValue 要判断的value + * @return value 返回值 + */ + public static T nvl(T value, T defaultValue) { + return value != null ? value : defaultValue; + } + + /** + * * 判断一个Collection是否为空, 包含List,Set,Queue + * + * @param coll 要判断的Collection + * @return true:为空 false:非空 + */ + public static boolean isEmpty(Collection coll) { + return isNull(coll) || coll.isEmpty(); + } + + /** + * * 判断一个Collection是否非空,包含List,Set,Queue + * + * @param coll 要判断的Collection + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Collection coll) { + return !isEmpty(coll); + } + + /** + * * 判断一个对象数组是否为空 + * + * @param objects 要判断的对象数组 + * * @return true:为空 false:非空 + */ + public static boolean isEmpty(Object[] objects) { + return isNull(objects) || (objects.length == 0); + } + + /** + * * 判断一个对象数组是否非空 + * + * @param objects 要判断的对象数组 + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Object[] objects) { + return !isEmpty(objects); + } + + /** + * * 判断一个Map是否为空 + * + * @param map 要判断的Map + * @return true:为空 false:非空 + */ + public static boolean isEmpty(Map map) { + return isNull(map) || map.isEmpty(); + } + + /** + * * 判断一个Map是否为空 + * + * @param map 要判断的Map + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Map map) { + return !isEmpty(map); + } + + /** + * * 判断一个字符串是否为空串 + * + * @param str String + * @return true:为空 false:非空 + */ + public static boolean isEmpty(String str) { + return isNull(str) || NULL_STR.equals(str.trim()); + } + + /** + * * 判断一个字符串是否为非空串 + * + * @param str String + * @return true:非空串 false:空串 + */ + public static boolean isNotEmpty(String str) { + return !isEmpty(str); + } + + /** + * * 判断一个对象是否为空 + * + * @param object Object + * @return true:为空 false:非空 + */ + public static boolean isNull(Object object) { + return object == null; + } + + /** + * * 判断一个对象是否非空 + * + * @param object Object + * @return true:非空 false:空 + */ + public static boolean isNotNull(Object object) { + return !isNull(object); + } + + /** + * * 判断一个对象是否是数组类型(Java基本型别的数组) + * + * @param object 对象 + * @return true:是数组 false:不是数组 + */ + public static boolean isArray(Object object) { + return isNotNull(object) && object.getClass().isArray(); + } + + /** + * 去空格 + */ + public static String trim(String str) { + return (str == null ? "" : str.trim()); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * @return 结果 + */ + public static String substring(final String str, int start) { + if (str == null) { + return NULL_STR; + } + + if (start < 0) { + start = str.length() + start; + } + + if (start < 0) { + start = 0; + } + if (start > str.length()) { + return NULL_STR; + } + + return str.substring(start); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * @param end 结束 + * @return 结果 + */ + public static String substring(final String str, int start, int end) { + if (str == null) { + return NULL_STR; + } + + if (end < 0) { + end = str.length() + end; + } + if (start < 0) { + start = str.length() + start; + } + + if (end > str.length()) { + end = str.length(); + } + + if (start > end) { + return NULL_STR; + } + + if (start < 0) { + start = 0; + } + if (end < 0) { + end = 0; + } + + return str.substring(start, end); + } + + /** + * 格式化文本, {} 表示占位符
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
+ * + * @param template 文本模板,被替换的部分用 {} 表示 + * @param params 参数值 + * @return 格式化后的文本 + */ + public static String format(String template, Object... params) { + if (isEmpty(params) || isEmpty(template)) { + return template; + } + return StrFormatter.format(template, params); + } + + /** + * 是否为http(s)://开头 + * + * @param link 链接 + * @return 结果 + */ + public static boolean isHttp(String link) { + return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS); + } + + /** + * 字符串转set + * + * @param str 字符串 + * @param sep 分隔符 + * @return set集合 + */ + public static Set str2Set(String str, String sep) { + return new HashSet(str2List(str, sep, true, false)); + } + + /** + * 字符串转list + * + * @param str 字符串 + * @param sep 分隔符 + * @param filterBlank 过滤纯空白 + * @param trim 去掉首尾空白 + * @return list集合 + */ + public static List str2List(String str, String sep, boolean filterBlank, boolean trim) { + List list = new ArrayList(); + if (StringUtils.isEmpty(str)) { + return list; + } + + // 过滤空白字符串 + if (filterBlank && StringUtils.isBlank(str)) { + return list; + } + String[] split = str.split(sep); + for (String string : split) { + if (filterBlank && StringUtils.isBlank(string)) { + continue; + } + if (trim) { + string = string.trim(); + } + list.add(string); + } + + return list; + } + + /** + * 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写 + * + * @param cs 指定字符串 + * @param searchCharSequences 需要检查的字符串数组 + * @return 是否包含任意一个字符串 + */ + public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences) { + if (isEmpty(cs) || isEmpty(searchCharSequences)) { + return false; + } + for (CharSequence testStr : searchCharSequences) { + if (containsIgnoreCase(cs, testStr)) { + return true; + } + } + return false; + } + + /** + * 驼峰转下划线命名 + */ + public static String toUnderScoreCase(String str) { + if (str == null) { + return null; + } + StringBuilder sb = new StringBuilder(); + // 前置字符是否大写 + boolean preCharIsUpperCase = true; + // 当前字符是否大写 + boolean curreCharIsUpperCase = true; + // 下一字符是否大写 + boolean nexteCharIsUpperCase = true; + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (i > 0) { + preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1)); + } else { + preCharIsUpperCase = false; + } + + curreCharIsUpperCase = Character.isUpperCase(c); + + if (i < (str.length() - 1)) { + nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1)); + } + + if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) { + sb.append(SEPARATOR); + } else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase) { + sb.append(SEPARATOR); + } + sb.append(Character.toLowerCase(c)); + } + + return sb.toString(); + } + + /** + * 是否包含字符串 + * + * @param str 验证字符串 + * @param strs 字符串组 + * @return 包含返回true + */ + public static boolean inStringIgnoreCase(String str, String... strs) { + if (str != null && strs != null) { + for (String s : strs) { + if (str.equalsIgnoreCase(trim(s))) { + return true; + } + } + } + return false; + } + + /** + * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld + * + * @param name 转换前的下划线大写方式命名的字符串 + * @return 转换后的驼峰式命名的字符串 + */ + public static String convertToCamelCase(String name) { + StringBuilder result = new StringBuilder(); + // 快速检查 + if (name == null || name.isEmpty()) { + // 没必要转换 + return ""; + } else if (!name.contains("_")) { + // 不含下划线,仅将首字母大写 + return name.substring(0, 1).toUpperCase() + name.substring(1); + } + // 用下划线将原始字符串分割 + String[] camels = name.split("_"); + for (String camel : camels) { + // 跳过原始字符串中开头、结尾的下换线或双重下划线 + if (camel.isEmpty()) { + continue; + } + // 首字母大写 + result.append(camel.substring(0, 1).toUpperCase()); + result.append(camel.substring(1).toLowerCase()); + } + return result.toString(); + } + + /** + * 驼峰式命名法 例如:user_name->userName + */ + public static String toCamelCase(String s) { + if (s == null) { + return null; + } + s = s.toLowerCase(); + StringBuilder sb = new StringBuilder(s.length()); + boolean upperCase = false; + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + + if (c == SEPARATOR) { + upperCase = true; + } else if (upperCase) { + sb.append(Character.toUpperCase(c)); + upperCase = false; + } else { + sb.append(c); + } + } + return sb.toString(); + } + + /** + * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串 + * + * @param str 指定字符串 + * @param strs 需要检查的字符串数组 + * @return 是否匹配 + */ + public static boolean matches(String str, List strs) { + if (isEmpty(str) || isEmpty(strs)) { + return false; + } + for (String pattern : strs) { + if (isMatch(pattern, str)) { + return true; + } + } + return false; + } + + /** + * 判断url是否与规则配置: + * ? 表示单个字符; + * * 表示一层路径内的任意字符串,不可跨层级; + * ** 表示任意层路径; + * + * @param pattern 匹配规则 + * @param url 需要匹配的url + * @return + */ + public static boolean isMatch(String pattern, String url) { + AntPathMatcher matcher = new AntPathMatcher(); + return matcher.match(pattern, url); + } + + @SuppressWarnings("unchecked") + public static T cast(Object obj) { + return (T) obj; + } + + + /** + * 首字母大写(进行字母的ascii编码前移,效率是最高的) + * + * @param fieldName 需要转化的字符串 + */ + public static String getMethodName(String fieldName) { + char[] chars = fieldName.toCharArray(); + chars[0] = toUpperCase(chars[0]); + return String.valueOf(chars); + } + + + /** + * 字符转成大写 + * + * @param c 需要转化的字符 + */ + public static char toUpperCase(char c) { + if (97 <= c && c <= 122) { + c ^= 32; + } + return c; + } + + /** + * 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。 + * + * @param num 数字对象 + * @param size 字符串指定长度 + * @return 返回数字的字符串格式,该字符串为指定长度。 + */ + public static String padL(final Number num, final int size) + { + return padL(num.toString(), size, '0'); + } + + /** + * 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。 + * + * @param s 原始字符串 + * @param size 字符串指定长度 + * @param c 用于补齐的字符 + * @return 返回指定长度的字符串,由原字符串左补齐或截取得到。 + */ + public static String padL(final String s, final int size, final char c) + { + final StringBuilder sb = new StringBuilder(size); + if (s != null) + { + final int len = s.length(); + if (s.length() <= size) + { + for (int i = size - len; i > 0; i--) + { + sb.append(c); + } + sb.append(s); + } + else + { + return s.substring(len - size, len); + } + } + else + { + for (int i = size; i > 0; i--) + { + sb.append(c); + } + } + return sb.toString(); + } + +} diff --git a/Cpop-Common/src/main/java/com/cpop/common/utils/bean/BeanUtils.java b/Cpop-Common/src/main/java/com/cpop/common/utils/bean/BeanUtils.java new file mode 100644 index 0000000..38c3d46 --- /dev/null +++ b/Cpop-Common/src/main/java/com/cpop/common/utils/bean/BeanUtils.java @@ -0,0 +1,193 @@ +package com.cpop.common.utils.bean; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Bean 工具类 + * + * @author DB + */ +public class BeanUtils extends org.springframework.beans.BeanUtils { + + private static final Logger LOGGER = LoggerFactory.getLogger(BeanUtils.class); + /** + * Bean方法名中属性名开始的下标 + */ + private static final int BEAN_METHOD_PROP_INDEX = 3; + + /** + * 匹配getter方法的正则表达式 + */ + private static final Pattern GET_PATTERN = Pattern.compile("get(\\p{javaUpperCase}\\w*)"); + + /** + * 匹配setter方法的正则表达式 + */ + private static final Pattern SET_PATTERN = Pattern.compile("set(\\p{javaUpperCase}\\w*)"); + + /** + * Bean属性复制工具方法。 + * + * @param dest 目标对象 + * @param src 源对象 + */ + public static void copyBeanProp(Object dest, Object src) { + try { + copyProperties(src, dest); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 实体类转换 + * @param source 资源 + * @param target 目标 + * @param 目标类型 + * @return 转换结果 + */ + public static T sourceToTarget(Object source, Class target){ + if(source == null){ + return null; + } + T targetObject = null; + try { + targetObject = target.newInstance(); + BeanUtils.copyProperties(source, targetObject); + } catch (Exception e) { + LOGGER.error("实体类转换失败: ", e); + } + + return targetObject; + } + + /** + * 获取对象的setter方法。 + * + * @param obj 对象 + * @return 对象的setter方法列表 + */ + public static List getSetterMethods(Object obj) { + // setter方法列表 + List setterMethods = new ArrayList(); + + // 获取所有方法 + Method[] methods = obj.getClass().getMethods(); + + // 查找setter方法 + + for (Method method : methods) { + Matcher m = SET_PATTERN.matcher(method.getName()); + if (m.matches() && (method.getParameterTypes().length == 1)) { + setterMethods.add(method); + } + } + // 返回setter方法列表 + return setterMethods; + } + + /** + * 获取对象的getter方法。 + * + * @param obj 对象 + * @return 对象的getter方法列表 + */ + + public static List getGetterMethods(Object obj) { + // getter方法列表 + List getterMethods = new ArrayList(); + // 获取所有方法 + Method[] methods = obj.getClass().getMethods(); + // 查找getter方法 + for (Method method : methods) { + Matcher m = GET_PATTERN.matcher(method.getName()); + if (m.matches() && (method.getParameterTypes().length == 0)) { + getterMethods.add(method); + } + } + // 返回getter方法列表 + return getterMethods; + } + + /** + * 检查Bean方法名中的属性名是否相等。
+ * 如getName()和setName()属性名一样,getName()和setAge()属性名不一样。 + * + * @param m1 方法名1 + * @param m2 方法名2 + * @return 属性名一样返回true,否则返回false + */ + + public static boolean isMethodPropEquals(String m1, String m2) { + return m1.substring(BEAN_METHOD_PROP_INDEX).equals(m2.substring(BEAN_METHOD_PROP_INDEX)); + } + + /** + * 对象转换 + * + * @param sourceObject 原Object + * @param destinationClass 转换class + * @param 转换实体类 + * @param 原实体类 + * @return T + */ + public static T mapToClass(E sourceObject, Class destinationClass) { + T targetObject = instantiateClass(destinationClass); + copyProperties(sourceObject, targetObject); + return targetObject; + } + + /** + * List转换 + * + * @param sourceList 原List + * @param destinationClass 转换class + * @param 转换实体类 + * @param 原实体类 + * @return List + */ + public static List mapToList(List sourceList, Class destinationClass) { + List destinationList = new ArrayList<>(sourceList.size()); + for (E sourceObject : sourceList) { + T targetObject = instantiateClass(destinationClass); + copyProperties(sourceObject, targetObject); + destinationList.add(targetObject); + } + return destinationList; + } + + /** + * @param origin + * @param t + * @author LOST.yuan + * @Description map转对象 + * @date 11:52 2022/8/23 + **/ + public static void mapToObj(Map origin, T t) { + Class clazz = t.getClass(); + Field[] fields = clazz.getDeclaredFields(); + for (Field field : fields) { + field.setAccessible(true); + String fieldName = field.getName(); + Object o = origin.get(fieldName); + if (o == null) { + continue; + } + String value = o.toString(); + try { + field.set(t, value); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + } +} diff --git a/Cpop-Common/src/main/java/com/cpop/common/utils/html/EscapeUtil.java b/Cpop-Common/src/main/java/com/cpop/common/utils/html/EscapeUtil.java new file mode 100644 index 0000000..e195e2b --- /dev/null +++ b/Cpop-Common/src/main/java/com/cpop/common/utils/html/EscapeUtil.java @@ -0,0 +1,134 @@ +package com.cpop.common.utils.html; + + +import com.pupu.common.utils.StringUtils; + +/** + * 转义和反转义工具类 + * + * @author DB + */ +public class EscapeUtil { + public static final String RE_HTML_MARK = "(<[^<]*?>)|(<[\\s]*?/[^<]*?>)|(<[^<]*?/[\\s]*?>)"; + + private static final char[][] TEXT = new char[64][]; + + static { + for (int i = 0; i < 64; i++) { + TEXT[i] = new char[]{(char) i}; + } + // special HTML characters + // 单引号 + TEXT['\''] = "'".toCharArray(); + // 双引号 + TEXT['"'] = """.toCharArray(); + // &符 + TEXT['&'] = "&".toCharArray(); + // 小于号 + TEXT['<'] = "<".toCharArray(); + // 大于号 + TEXT['>'] = ">".toCharArray(); + } + + /** + * 转义文本中的HTML字符为安全的字符 + * + * @param text 被转义的文本 + * @return 转义后的文本 + */ + public static String escape(String text) { + return encode(text); + } + + /** + * 还原被转义的HTML特殊字符 + * + * @param content 包含转义符的HTML内容 + * @return 转换后的字符串 + */ + public static String unescape(String content) { + return decode(content); + } + + /** + * 清除所有HTML标签,但是不删除标签内的内容 + * + * @param content 文本 + * @return 清除标签后的文本 + */ + public static String clean(String content) { + return new HTMLFilter().filter(content); + } + + /** + * Escape编码 + * + * @param text 被编码的文本 + * @return 编码后的字符 + */ + private static String encode(String text) { + if (StringUtils.isEmpty(text)) { + return StringUtils.EMPTY; + } + + final StringBuilder tmp = new StringBuilder(text.length() * 6); + char c; + for (int i = 0; i < text.length(); i++) { + c = text.charAt(i); + if (c < 256) { + tmp.append("%"); + if (c < 16) { + tmp.append("0"); + } + tmp.append(Integer.toString(c, 16)); + } else { + tmp.append("%u"); + if (c <= 0xfff) { + // issue#I49JU8@Gitee + tmp.append("0"); + } + tmp.append(Integer.toString(c, 16)); + } + } + return tmp.toString(); + } + + /** + * Escape解码 + * + * @param content 被转义的内容 + * @return 解码后的字符串 + */ + public static String decode(String content) { + if (StringUtils.isEmpty(content)) { + return content; + } + + StringBuilder tmp = new StringBuilder(content.length()); + int lastPos = 0, pos = 0; + char ch; + while (lastPos < content.length()) { + pos = content.indexOf("%", lastPos); + if (pos == lastPos) { + if (content.charAt(pos + 1) == 'u') { + ch = (char) Integer.parseInt(content.substring(pos + 2, pos + 6), 16); + tmp.append(ch); + lastPos = pos + 6; + } else { + ch = (char) Integer.parseInt(content.substring(pos + 1, pos + 3), 16); + tmp.append(ch); + lastPos = pos + 3; + } + } else { + if (pos == -1) { + tmp.append(content.substring(lastPos)); + lastPos = content.length(); + } else { + tmp.append(content.substring(lastPos, pos)); + lastPos = pos; + } + } + } + return tmp.toString(); + } +} diff --git a/Cpop-Common/src/main/java/com/cpop/common/utils/html/HTMLFilter.java b/Cpop-Common/src/main/java/com/cpop/common/utils/html/HTMLFilter.java new file mode 100644 index 0000000..57790b1 --- /dev/null +++ b/Cpop-Common/src/main/java/com/cpop/common/utils/html/HTMLFilter.java @@ -0,0 +1,499 @@ +package com.cpop.common.utils.html; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * HTML过滤器,用于去除XSS漏洞隐患。 + * + * @author DB + */ +public final class HTMLFilter { + /** + * regex flag union representing /si modifiers in php + **/ + private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL; + private static final Pattern P_COMMENTS = Pattern.compile("", Pattern.DOTALL); + private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI); + private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL); + private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI); + private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI); + private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI); + private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI); + private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI); + private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?"); + private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?"); + private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?"); + private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))"); + private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL); + private static final Pattern P_END_ARROW = Pattern.compile("^>"); + private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)"); + private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)"); + private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)"); + private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)"); + private static final Pattern P_AMP = Pattern.compile("&"); + private static final Pattern P_QUOTE = Pattern.compile("\""); + private static final Pattern P_LEFT_ARROW = Pattern.compile("<"); + private static final Pattern P_RIGHT_ARROW = Pattern.compile(">"); + private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>"); + + // @xxx could grow large... maybe use sesat's ReferenceMap + private static final ConcurrentMap P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>(); + private static final ConcurrentMap P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>(); + + /** + * set of allowed html elements, along with allowed attributes for each element + **/ + private final Map> vAllowed; + /** + * counts of open tags for each (allowable) html element + **/ + private final Map vTagCounts = new HashMap<>(); + + /** + * html elements which must always be self-closing (e.g. "") + **/ + private final String[] vSelfClosingTags; + /** + * html elements which must always have separate opening and closing tags (e.g. "") + **/ + private final String[] vNeedClosingTags; + /** + * set of disallowed html elements + **/ + private final String[] vDisallowed; + /** + * attributes which should be checked for valid protocols + **/ + private final String[] vProtocolAtts; + /** + * allowed protocols + **/ + private final String[] vAllowedProtocols; + /** + * tags which should be removed if they contain no content (e.g. "" or "") + **/ + private final String[] vRemoveBlanks; + /** + * entities allowed within html markup + **/ + private final String[] vAllowedEntities; + /** + * flag determining whether comments are allowed in input String. + */ + private final boolean stripComment; + private final boolean encodeQuotes; + /** + * flag determining whether to try to make tags when presented with "unbalanced" angle brackets (e.g. "" + * becomes " text "). If set to false, unbalanced angle brackets will be html escaped. + */ + private final boolean alwaysMakeTags; + + /** + * Default constructor. + */ + public HTMLFilter() { + vAllowed = new HashMap<>(); + + final ArrayList a_atts = new ArrayList<>(); + a_atts.add("href"); + a_atts.add("target"); + vAllowed.put("a", a_atts); + + final ArrayList img_atts = new ArrayList<>(); + img_atts.add("src"); + img_atts.add("width"); + img_atts.add("height"); + img_atts.add("alt"); + vAllowed.put("img", img_atts); + + final ArrayList no_atts = new ArrayList<>(); + vAllowed.put("b", no_atts); + vAllowed.put("strong", no_atts); + vAllowed.put("i", no_atts); + vAllowed.put("em", no_atts); + + vSelfClosingTags = new String[]{"img"}; + vNeedClosingTags = new String[]{"a", "b", "strong", "i", "em"}; + vDisallowed = new String[]{}; + // no ftp. + vAllowedProtocols = new String[]{"http", "mailto", "https"}; + vProtocolAtts = new String[]{"src", "href"}; + vRemoveBlanks = new String[]{"a", "b", "strong", "i", "em"}; + vAllowedEntities = new String[]{"amp", "gt", "lt", "quot"}; + stripComment = true; + encodeQuotes = true; + alwaysMakeTags = false; + } + + /** + * Map-parameter configurable constructor. + * + * @param conf map containing configuration. keys match field names. + */ + @SuppressWarnings("unchecked") + public HTMLFilter(final Map conf) { + + assert conf.containsKey("vAllowed") : "configuration requires vAllowed"; + assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags"; + assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags"; + assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed"; + assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols"; + assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts"; + assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks"; + assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities"; + + vAllowed = Collections.unmodifiableMap((HashMap>) conf.get("vAllowed")); + vSelfClosingTags = (String[]) conf.get("vSelfClosingTags"); + vNeedClosingTags = (String[]) conf.get("vNeedClosingTags"); + vDisallowed = (String[]) conf.get("vDisallowed"); + vAllowedProtocols = (String[]) conf.get("vAllowedProtocols"); + vProtocolAtts = (String[]) conf.get("vProtocolAtts"); + vRemoveBlanks = (String[]) conf.get("vRemoveBlanks"); + vAllowedEntities = (String[]) conf.get("vAllowedEntities"); + stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true; + encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true; + alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true; + } + + private void reset() { + vTagCounts.clear(); + } + + // --------------------------------------------------------------- + // my versions of some PHP library functions + public static String chr(final int decimal) { + return String.valueOf((char) decimal); + } + + public static String htmlSpecialChars(final String s) { + String result = s; + result = regexReplace(P_AMP, "&", result); + result = regexReplace(P_QUOTE, """, result); + result = regexReplace(P_LEFT_ARROW, "<", result); + result = regexReplace(P_RIGHT_ARROW, ">", result); + return result; + } + + // --------------------------------------------------------------- + + /** + * given a user submitted input String, filter out any invalid or restricted html. + * + * @param input text (i.e. submitted by a user) than may contain html + * @return "clean" version of input, with only valid, whitelisted html elements allowed + */ + public String filter(final String input) { + reset(); + String s = input; + + s = escapeComments(s); + + s = balanceHTML(s); + + s = checkTags(s); + + s = processRemoveBlanks(s); + + // s = validateEntities(s); + + return s; + } + + public boolean isAlwaysMakeTags() { + return alwaysMakeTags; + } + + public boolean isStripComments() { + return stripComment; + } + + private String escapeComments(final String s) { + final Matcher m = P_COMMENTS.matcher(s); + final StringBuffer buf = new StringBuffer(); + if (m.find()) { + // (.*?) + final String match = m.group(1); + m.appendReplacement(buf, Matcher.quoteReplacement("")); + } + m.appendTail(buf); + + return buf.toString(); + } + + private String balanceHTML(String s) { + if (alwaysMakeTags) { + // + // try and form html + // + s = regexReplace(P_END_ARROW, "", s); + // 不追加结束标签 + s = regexReplace(P_BODY_TO_END, "<$1>", s); + s = regexReplace(P_XML_CONTENT, "$1<$2", s); + + } else { + // + // escape stray brackets + // + s = regexReplace(P_STRAY_LEFT_ARROW, "<$1", s); + s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2><", s); + + // + // the last regexp causes '<>' entities to appear + // (we need to do a lookahead assertion so that the last bracket can + // be used in the next pass of the regexp) + // + s = regexReplace(P_BOTH_ARROWS, "", s); + } + + return s; + } + + private String checkTags(String s) { + Matcher m = P_TAGS.matcher(s); + + final StringBuffer buf = new StringBuffer(); + while (m.find()) { + String replaceStr = m.group(1); + replaceStr = processTag(replaceStr); + m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr)); + } + m.appendTail(buf); + + // these get tallied in processTag + // (remember to reset before subsequent calls to filter method) + final StringBuilder sBuilder = new StringBuilder(buf.toString()); + for (String key : vTagCounts.keySet()) { + for (int ii = 0; ii < vTagCounts.get(key); ii++) { + sBuilder.append(""); + } + } + s = sBuilder.toString(); + + return s; + } + + private String processRemoveBlanks(final String s) { + String result = s; + for (String tag : vRemoveBlanks) { + if (!P_REMOVE_PAIR_BLANKS.containsKey(tag)) { + P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?>")); + } + result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result); + if (!P_REMOVE_SELF_BLANKS.containsKey(tag)) { + P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>")); + } + result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result); + } + + return result; + } + + private static String regexReplace(final Pattern regex_pattern, final String replacement, final String s) { + Matcher m = regex_pattern.matcher(s); + return m.replaceAll(replacement); + } + + private String processTag(final String s) { + // ending tags + Matcher m = P_END_TAG.matcher(s); + if (m.find()) { + final String name = m.group(1).toLowerCase(); + if (allowed(name)) { + if (!inArray(name, vSelfClosingTags)) { + if (vTagCounts.containsKey(name)) { + vTagCounts.put(name, vTagCounts.get(name) - 1); + return ""; + } + } + } + } + + // starting tags + m = P_START_TAG.matcher(s); + if (m.find()) { + final String name = m.group(1).toLowerCase(); + final String body = m.group(2); + String ending = m.group(3); + + // debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" ); + if (allowed(name)) { + final StringBuilder params = new StringBuilder(); + + final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body); + final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body); + final List paramNames = new ArrayList<>(); + final List paramValues = new ArrayList<>(); + while (m2.find()) { + paramNames.add(m2.group(1)); // ([a-z0-9]+) + paramValues.add(m2.group(3)); // (.*?) + } + while (m3.find()) { + paramNames.add(m3.group(1)); // ([a-z0-9]+) + paramValues.add(m3.group(3)); // ([^\"\\s']+) + } + + String paramName, paramValue; + for (int ii = 0; ii < paramNames.size(); ii++) { + paramName = paramNames.get(ii).toLowerCase(); + paramValue = paramValues.get(ii); + + // debug( "paramName='" + paramName + "'" ); + // debug( "paramValue='" + paramValue + "'" ); + // debug( "allowed? " + vAllowed.get( name ).contains( paramName ) ); + + if (allowedAttribute(name, paramName)) { + if (inArray(paramName, vProtocolAtts)) { + paramValue = processParamProtocol(paramValue); + } + params.append(' ').append(paramName).append("=\\\"").append(paramValue).append("\""); + } + } + + if (inArray(name, vSelfClosingTags)) { + ending = " /"; + } + + if (inArray(name, vNeedClosingTags)) { + ending = ""; + } + + if (ending == null || ending.length() < 1) { + if (vTagCounts.containsKey(name)) { + vTagCounts.put(name, vTagCounts.get(name) + 1); + } else { + vTagCounts.put(name, 1); + } + } else { + ending = " /"; + } + return "<" + name + params + ending + ">"; + } else { + return ""; + } + } + + // comments + m = P_COMMENT.matcher(s); + if (!stripComment && m.find()) { + return "<" + m.group() + ">"; + } + + return ""; + } + + private String processParamProtocol(String s) { + s = decodeEntities(s); + final Matcher m = P_PROTOCOL.matcher(s); + if (m.find()) { + final String protocol = m.group(1); + if (!inArray(protocol, vAllowedProtocols)) { + // bad protocol, turn into local anchor link instead + s = "#" + s.substring(protocol.length() + 1); + if (s.startsWith("#//")) { + s = "#" + s.substring(3); + } + } + } + + return s; + } + + private String decodeEntities(String s) { + StringBuffer buf = new StringBuffer(); + + Matcher m = P_ENTITY.matcher(s); + while (m.find()) { + final String match = m.group(1); + final int decimal = Integer.decode(match); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + buf = new StringBuffer(); + m = P_ENTITY_UNICODE.matcher(s); + while (m.find()) { + final String match = m.group(1); + final int decimal = Integer.valueOf(match, 16); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + buf = new StringBuffer(); + m = P_ENCODE.matcher(s); + while (m.find()) { + final String match = m.group(1); + final int decimal = Integer.valueOf(match, 16); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + s = validateEntities(s); + return s; + } + + private String validateEntities(final String s) { + StringBuffer buf = new StringBuffer(); + + // validate entities throughout the string + Matcher m = P_VALID_ENTITIES.matcher(s); + while (m.find()) { + final String one = m.group(1); // ([^&;]*) + final String two = m.group(2); // (?=(;|&|$)) + m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two))); + } + m.appendTail(buf); + + return encodeQuotes(buf.toString()); + } + + private String encodeQuotes(final String s) { + if (encodeQuotes) { + StringBuffer buf = new StringBuffer(); + Matcher m = P_VALID_QUOTES.matcher(s); + while (m.find()) { + final String one = m.group(1); // (>|^) + final String two = m.group(2); // ([^<]+?) + final String three = m.group(3); // (<|$) + // 不替换双引号为",防止json格式无效 regexReplace(P_QUOTE, """, two) + m.appendReplacement(buf, Matcher.quoteReplacement(one + two + three)); + } + m.appendTail(buf); + return buf.toString(); + } else { + return s; + } + } + + private String checkEntity(final String preamble, final String term) { + + return ";".equals(term) && isValidEntity(preamble) ? '&' + preamble : "&" + preamble; + } + + private boolean isValidEntity(final String entity) { + return inArray(entity, vAllowedEntities); + } + + private static boolean inArray(final String s, final String[] array) { + for (String item : array) { + if (item != null && item.equals(s)) { + return true; + } + } + return false; + } + + private boolean allowed(final String name) { + return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed); + } + + private boolean allowedAttribute(final String name, final String paramName) { + return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName)); + } +} diff --git a/Cpop-Common/src/main/java/com/cpop/common/utils/http/HttpHelper.java b/Cpop-Common/src/main/java/com/cpop/common/utils/http/HttpHelper.java new file mode 100644 index 0000000..fef431e --- /dev/null +++ b/Cpop-Common/src/main/java/com/cpop/common/utils/http/HttpHelper.java @@ -0,0 +1,44 @@ +package com.cpop.common.utils.http; + +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.ServletRequest; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; + +/** + * 通用http工具封装 + * + * @author DB + */ +public class HttpHelper { + private static final Logger LOGGER = LoggerFactory.getLogger(HttpHelper.class); + + public static String getBodyString(ServletRequest request) { + StringBuilder sb = new StringBuilder(); + BufferedReader reader = null; + try (InputStream inputStream = request.getInputStream()) { + reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + String line = ""; + while ((line = reader.readLine()) != null) { + sb.append(line); + } + } catch (IOException e) { + LOGGER.warn("getBodyString出现问题!"); + } finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + LOGGER.error(ExceptionUtils.getMessage(e)); + } + } + } + return sb.toString(); + } +} diff --git a/Cpop-Common/src/main/java/com/cpop/common/utils/http/HttpUtils.java b/Cpop-Common/src/main/java/com/cpop/common/utils/http/HttpUtils.java new file mode 100644 index 0000000..541f267 --- /dev/null +++ b/Cpop-Common/src/main/java/com/cpop/common/utils/http/HttpUtils.java @@ -0,0 +1,322 @@ +package com.cpop.common.utils.http; + +import com.pupu.common.constant.Constants; +import com.pupu.common.utils.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.net.ssl.*; +import java.io.*; +import java.net.*; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.security.cert.X509Certificate; + +/** + * 通用http发送方法 + * + * @author DB + */ +public class HttpUtils { + private static final Logger log = LoggerFactory.getLogger(HttpUtils.class); + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url) { + return sendGet(url, StringUtils.EMPTY); + } + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url, String param) { + return sendGet(url, param, Constants.UTF8); + } + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @param contentType 编码类型 + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url, String param, String contentType) { + StringBuilder result = new StringBuilder(); + BufferedReader in = null; + try { + String urlNameString = StringUtils.isNotBlank(param) ? url + "?" + param : url; + log.info("sendGet - {}", urlNameString); + URL realUrl = new URL(urlNameString); + URLConnection connection = realUrl.openConnection(); + connection.setRequestProperty("accept", "*/*"); + connection.setRequestProperty("connection", "Keep-Alive"); + connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + connection.connect(); + in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType)); + String line; + while ((line = in.readLine()) != null) { + result.append(line); + } + log.info("recv - {}", result); + } catch (ConnectException e) { + log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e); + } catch (SocketTimeoutException e) { + log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e); + } catch (IOException e) { + log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e); + } catch (Exception e) { + log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e); + } finally { + try { + if (in != null) { + in.close(); + } + } catch (Exception ex) { + log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); + } + } + return result.toString(); + } + + /** + * 向指定 URL 发送POST方法的请求 + * + * @param url 发送请求的 URL + * @return 所代表远程资源的响应结果 + */ + public static String sendPost(String url) { + BufferedReader in = null; + StringBuilder result = new StringBuilder(); + try { + log.info("sendPost - {}", url); + URL realUrl = new URL(url); + URLConnection conn = realUrl.openConnection(); + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + conn.setRequestProperty("Accept-Charset", "utf-8"); + conn.setRequestProperty("contentType", "utf-8"); + conn.setDoOutput(true); + conn.setDoInput(true); + in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8)); + String line; + while ((line = in.readLine()) != null) { + result.append(line); + } + log.info("rev - {}", result); + } catch (ConnectException e) { + log.error("调用HttpUtils.sendPost ConnectException, url=" + url, e); + } catch (SocketTimeoutException e) { + log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url, e); + } catch (IOException e) { + log.error("调用HttpUtils.sendPost IOException, url=" + url, e); + } catch (Exception e) { + log.error("调用HttpsUtil.sendPost Exception, url=" + url, e); + } finally { + try { + if (in != null) { + in.close(); + } + } catch (IOException ex) { + log.error("调用in.close Exception, url=" + url, ex); + } + } + return result.toString(); + } + + /** + * 向指定 URL 发送POST方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @return 所代表远程资源的响应结果 + */ + public static String sendPost(String url, String param) { + PrintWriter out = null; + BufferedReader in = null; + StringBuilder result = new StringBuilder(); + try { + log.info("sendPost - {}", url); + URL realUrl = new URL(url); + URLConnection conn = realUrl.openConnection(); + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + conn.setRequestProperty("Accept-Charset", "utf-8"); + conn.setRequestProperty("contentType", "utf-8"); + conn.setDoOutput(true); + conn.setDoInput(true); + out = new PrintWriter(conn.getOutputStream()); + out.print(param); + out.flush(); + in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8)); + String line; + while ((line = in.readLine()) != null) { + result.append(line); + } + log.info("recv - {}", result); + } catch (ConnectException e) { + log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e); + } catch (SocketTimeoutException e) { + log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e); + } catch (IOException e) { + log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e); + } catch (Exception e) { + log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e); + } finally { + try { + if (out != null) { + out.close(); + } + if (in != null) { + in.close(); + } + } catch (IOException ex) { + log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); + } + } + return result.toString(); + } + + /** + * 向指定 URL 发送POST方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @return 所代表远程资源的响应结果 + */ + public static String sendPost(String url, String param, File file) throws IOException { + URL urlObj = new URL(url); + //连接 + HttpURLConnection con = (HttpURLConnection) urlObj.openConnection(); + String result = null; + con.setDoInput(true); + con.setDoOutput(true); + // post方式不能使用缓存 + con.setUseCaches(false); + // 设置请求头信息 + con.setRequestProperty("Connection", "Keep-Alive"); + con.setRequestProperty("Charset", "UTF-8"); + // 设置边界 + String BOUNDARY = "----------" + System.currentTimeMillis(); + con.setRequestProperty("Content-Type", + "multipart/form-data; boundary=" + + BOUNDARY); + // 请求正文信息 + // 第一部分: + StringBuilder sb = new StringBuilder(); + // 必须多两道线 + sb.append("--"); + sb.append(BOUNDARY); + sb.append("\r\n"); + sb.append("Content-Disposition: form-data;name=\"media\";filelength=\"").append(file.length()).append("\";filename=\"").append(file.getName()).append("\"\r\n"); + sb.append("Content-Type:application/octet-stream\r\n\r\n"); + byte[] head = sb.toString().getBytes(StandardCharsets.UTF_8); + // 获得输出流 + OutputStream out = new DataOutputStream(con.getOutputStream()); + // 输出表头 + out.write(head); + // 文件正文部分 + // 把文件已流文件的方式 推入到url中 + DataInputStream in = new DataInputStream(Files.newInputStream(file.toPath())); + int bytes = 0; + byte[] bufferOut = new byte[1024]; + while ((bytes = in.read(bufferOut)) != -1) { + out.write(bufferOut, 0, bytes); + } + in.close(); + // 结尾部分 + // 定义最后数据分隔线 + byte[] foot = ("\r\n--" + BOUNDARY + "--\r\n").getBytes(StandardCharsets.UTF_8); + out.write(foot); + out.flush(); + out.close(); + StringBuilder buffer = new StringBuilder(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(con.getInputStream()))) { + // 定义BufferedReader输入流来读取URL的响应 + String line = null; + while ((line = reader.readLine()) != null) { + buffer.append(line); + } + result = buffer.toString(); + } catch (IOException e) { + System.out.println("发送POST请求出现异常!" + e); + e.printStackTrace(); + } + return result; + } + + public static String sendSSLPost(String url, String param) { + StringBuilder result = new StringBuilder(); + String urlNameString = url + "?" + param; + try { + log.info("sendSSLPost - {}", urlNameString); + SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(null, new TrustManager[]{new TrustAnyTrustManager()}, new java.security.SecureRandom()); + URL console = new URL(urlNameString); + HttpsURLConnection conn = (HttpsURLConnection) console.openConnection(); + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + conn.setRequestProperty("Accept-Charset", "utf-8"); + conn.setRequestProperty("contentType", "utf-8"); + conn.setDoOutput(true); + conn.setDoInput(true); + + conn.setSSLSocketFactory(sc.getSocketFactory()); + conn.setHostnameVerifier(new TrustAnyHostnameVerifier()); + conn.connect(); + InputStream is = conn.getInputStream(); + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + String ret = ""; + while ((ret = br.readLine()) != null) { + if (!"".equals(ret.trim())) { + result.append(new String(ret.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8)); + } + } + log.info("recv - {}", result); + conn.disconnect(); + br.close(); + } catch (ConnectException e) { + log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e); + } catch (SocketTimeoutException e) { + log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e); + } catch (IOException e) { + log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e); + } catch (Exception e) { + log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e); + } + return result.toString(); + } + + private static class TrustAnyTrustManager implements X509TrustManager { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[]{}; + } + } + + private static class TrustAnyHostnameVerifier implements HostnameVerifier { + @Override + public boolean verify(String hostname, SSLSession session) { + return true; + } + } +} diff --git a/Cpop-Common/src/main/java/com/cpop/common/utils/ip/AddressUtils.java b/Cpop-Common/src/main/java/com/cpop/common/utils/ip/AddressUtils.java new file mode 100644 index 0000000..df242b0 --- /dev/null +++ b/Cpop-Common/src/main/java/com/cpop/common/utils/ip/AddressUtils.java @@ -0,0 +1,51 @@ +package com.cpop.common.utils.ip; + +import com.alibaba.fastjson.JSONObject; +import com.pupu.common.constant.Constants; +import com.pupu.common.utils.StringUtils; +import com.pupu.common.utils.http.HttpUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; + +/** + * 获取地址类 + * + * @author DB + */ +public class AddressUtils { + private static final Logger log = LoggerFactory.getLogger(AddressUtils.class); + + // IP地址查询 + public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp"; + + @Value("${rockBlade.addressEnabled}") + private static Boolean isAddressEnabled; + + // 未知地址 + public static final String UN_KNOW = "XX XX"; + + public static String getRealAddressByIP(String ip) { + String address = UN_KNOW; + // 内网不查询 + if (IpUtils.internalIp(ip)) { + return "内网IP"; + } + if (isAddressEnabled) { + try { + String rspStr = HttpUtils.sendGet(IP_URL, "ip=" + ip + "&json=true", Constants.GBK); + if (StringUtils.isEmpty(rspStr)) { + log.error("获取地理位置异常 {}", ip); + return UN_KNOW; + } + JSONObject obj = JSONObject.parseObject(rspStr); + String region = obj.getString("pro"); + String city = obj.getString("city"); + return String.format("%s %s", region, city); + } catch (Exception e) { + log.error("获取地理位置异常 {}", ip); + } + } + return address; + } +} diff --git a/Cpop-Common/src/main/java/com/cpop/common/utils/ip/IpUtils.java b/Cpop-Common/src/main/java/com/cpop/common/utils/ip/IpUtils.java new file mode 100644 index 0000000..ecaadef --- /dev/null +++ b/Cpop-Common/src/main/java/com/cpop/common/utils/ip/IpUtils.java @@ -0,0 +1,196 @@ +package com.cpop.common.utils.ip; + +import com.pupu.common.utils.StringUtils; +import com.pupu.common.utils.html.EscapeUtil; + +import javax.servlet.http.HttpServletRequest; +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * 获取IP方法 + * + * @author DB + */ +public class IpUtils +{ + public static String getIpAddr(HttpServletRequest request) + { + if (request == null) + { + return "unknown"; + } + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("X-Forwarded-For"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("X-Real-IP"); + } + + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getRemoteAddr(); + } + return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : EscapeUtil.clean(ip); + } + + public static boolean internalIp(String ip) + { + byte[] addr = textToNumericFormatV4(ip); + return internalIp(addr) || "127.0.0.1".equals(ip); + } + + private static boolean internalIp(byte[] addr) + { + if (StringUtils.isNull(addr) || addr.length < 2) + { + return true; + } + final byte b0 = addr[0]; + final byte b1 = addr[1]; + // 10.x.x.x/8 + final byte SECTION_1 = 0x0A; + // 172.16.x.x/12 + final byte SECTION_2 = (byte) 0xAC; + final byte SECTION_3 = (byte) 0x10; + final byte SECTION_4 = (byte) 0x1F; + // 192.168.x.x/16 + final byte SECTION_5 = (byte) 0xC0; + final byte SECTION_6 = (byte) 0xA8; + switch (b0) + { + case SECTION_1: + return true; + case SECTION_2: + if (b1 >= SECTION_3 && b1 <= SECTION_4) + { + return true; + } + case SECTION_5: + switch (b1) + { + case SECTION_6: + return true; + } + default: + return false; + } + } + + /** + * 将IPv4地址转换成字节 + * + * @param text IPv4地址 + * @return byte 字节 + */ + public static byte[] textToNumericFormatV4(String text) + { + if (text.length() == 0) + { + return null; + } + + byte[] bytes = new byte[4]; + String[] elements = text.split("\\.", -1); + try + { + long l; + int i; + switch (elements.length) + { + case 1: + l = Long.parseLong(elements[0]); + if ((l < 0L) || (l > 4294967295L)) { + return null; + } + bytes[0] = (byte) (int) (l >> 24 & 0xFF); + bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF); + bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 2: + l = Integer.parseInt(elements[0]); + if ((l < 0L) || (l > 255L)) { + return null; + } + bytes[0] = (byte) (int) (l & 0xFF); + l = Integer.parseInt(elements[1]); + if ((l < 0L) || (l > 16777215L)) { + return null; + } + bytes[1] = (byte) (int) (l >> 16 & 0xFF); + bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 3: + for (i = 0; i < 2; ++i) + { + l = Integer.parseInt(elements[i]); + if ((l < 0L) || (l > 255L)) { + return null; + } + bytes[i] = (byte) (int) (l & 0xFF); + } + l = Integer.parseInt(elements[2]); + if ((l < 0L) || (l > 65535L)) { + return null; + } + bytes[2] = (byte) (int) (l >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 4: + for (i = 0; i < 4; ++i) + { + l = Integer.parseInt(elements[i]); + if ((l < 0L) || (l > 255L)) { + return null; + } + bytes[i] = (byte) (int) (l & 0xFF); + } + break; + default: + return null; + } + } + catch (NumberFormatException e) + { + return null; + } + return bytes; + } + + public static String getHostIp() + { + try + { + return InetAddress.getLocalHost().getHostAddress(); + } + catch (UnknownHostException e) + { + } + return "127.0.0.1"; + } + + public static String getHostName() + { + try + { + return InetAddress.getLocalHost().getHostName(); + } + catch (UnknownHostException e) + { + } + return "未知"; + } +} diff --git a/Cpop-Common/src/main/java/com/cpop/common/utils/text/CharsetKit.java b/Cpop-Common/src/main/java/com/cpop/common/utils/text/CharsetKit.java new file mode 100644 index 0000000..e56e930 --- /dev/null +++ b/Cpop-Common/src/main/java/com/cpop/common/utils/text/CharsetKit.java @@ -0,0 +1,91 @@ +package com.cpop.common.utils.text; + +import com.pupu.common.utils.StringUtils; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + +/** + * 字符集工具类 + * + * @author DB + */ +public class CharsetKit { + /** + * ISO-8859-1 + */ + public static final String ISO_8859_1 = "ISO-8859-1"; + /** + * UTF-8 + */ + public static final String UTF_8 = "UTF-8"; + /** + * GBK + */ + public static final String GBK = "GBK"; + + /** + * ISO-8859-1 + */ + public static final Charset CHARSET_ISO_8859_1 = StandardCharsets.ISO_8859_1; + /** + * UTF-8 + */ + public static final Charset CHARSET_UTF_8 = StandardCharsets.UTF_8; + /** + * GBK + */ + public static final Charset CHARSET_GBK = Charset.forName(GBK); + + /** + * 转换为Charset对象 + * + * @param charset 字符集,为空则返回默认字符集 + * @return Charset + */ + public static Charset charset(String charset) { + return StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset); + } + + /** + * 转换字符串的字符集编码 + * + * @param source 字符串 + * @param srcCharset 源字符集,默认ISO-8859-1 + * @param destCharset 目标字符集,默认UTF-8 + * @return 转换后的字符集 + */ + public static String convert(String source, String srcCharset, String destCharset) { + return convert(source, Charset.forName(srcCharset), Charset.forName(destCharset)); + } + + /** + * 转换字符串的字符集编码 + * + * @param source 字符串 + * @param srcCharset 源字符集,默认ISO-8859-1 + * @param destCharset 目标字符集,默认UTF-8 + * @return 转换后的字符集 + */ + public static String convert(String source, Charset srcCharset, Charset destCharset) { + if (null == srcCharset) { + srcCharset = StandardCharsets.ISO_8859_1; + } + + if (null == destCharset) { + destCharset = StandardCharsets.UTF_8; + } + + if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset)) { + return source; + } + return new String(source.getBytes(srcCharset), destCharset); + } + + /** + * @return 系统字符集编码 + */ + public static String systemCharset() { + return Charset.defaultCharset().name(); + } +} diff --git a/Cpop-Common/src/main/java/com/cpop/common/utils/text/Convert.java b/Cpop-Common/src/main/java/com/cpop/common/utils/text/Convert.java new file mode 100644 index 0000000..a828bae --- /dev/null +++ b/Cpop-Common/src/main/java/com/cpop/common/utils/text/Convert.java @@ -0,0 +1,854 @@ +package com.cpop.common.utils.text; + +import com.pupu.common.utils.StringUtils; +import org.apache.commons.lang3.ArrayUtils; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.text.NumberFormat; +import java.util.Set; + +/** + * 类型转换器 + * + * @author DB + */ +public class Convert { + /** + * 转换为字符串
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static String toStr(Object value, String defaultValue) { + if (null == value) { + return defaultValue; + } + if (value instanceof String) { + return (String) value; + } + return value.toString(); + } + + /** + * 转换为字符串
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static String toStr(Object value) { + return toStr(value, null); + } + + /** + * 转换为字符
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Character toChar(Object value, Character defaultValue) { + if (null == value) { + return defaultValue; + } + if (value instanceof Character) { + return (Character) value; + } + + final String valueStr = toStr(value, null); + return StringUtils.isEmpty(valueStr) ? defaultValue : valueStr.charAt(0); + } + + /** + * 转换为字符
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Character toChar(Object value) { + return toChar(value, null); + } + + /** + * 转换为byte
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Byte toByte(Object value, Byte defaultValue) { + if (value == null) { + return defaultValue; + } + if (value instanceof Byte) { + return (Byte) value; + } + if (value instanceof Number) { + return ((Number) value).byteValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + return Byte.parseByte(valueStr); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * 转换为byte
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Byte toByte(Object value) { + return toByte(value, null); + } + + /** + * 转换为Short
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Short toShort(Object value, Short defaultValue) { + if (value == null) { + return defaultValue; + } + if (value instanceof Short) { + return (Short) value; + } + if (value instanceof Number) { + return ((Number) value).shortValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + return Short.parseShort(valueStr.trim()); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * 转换为Short
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Short toShort(Object value) { + return toShort(value, null); + } + + /** + * 转换为Number
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Number toNumber(Object value, Number defaultValue) { + if (value == null) { + return defaultValue; + } + if (value instanceof Number) { + return (Number) value; + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + return NumberFormat.getInstance().parse(valueStr); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * 转换为Number
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Number toNumber(Object value) { + return toNumber(value, null); + } + + /** + * 转换为int
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Integer toInt(Object value, Integer defaultValue) { + if (value == null) { + return defaultValue; + } + if (value instanceof Integer) { + return (Integer) value; + } + if (value instanceof Number) { + return ((Number) value).intValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + return Integer.parseInt(valueStr.trim()); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * 转换为int
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Integer toInt(Object value) { + return toInt(value, null); + } + + /** + * 转换为Integer数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static Integer[] toIntArray(String str) { + return toIntArray(",", str); + } + + /** + * 转换为Long数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static Long[] toLongArray(String str) { + return toLongArray(",", str); + } + + /** + * 转换为Integer数组
+ * + * @param split 分隔符 + * @param split 被转换的值 + * @return 结果 + */ + public static Integer[] toIntArray(String split, String str) { + if (StringUtils.isEmpty(str)) { + return new Integer[]{}; + } + String[] arr = str.split(split); + final Integer[] ints = new Integer[arr.length]; + for (int i = 0; i < arr.length; i++) { + final Integer v = toInt(arr[i], 0); + ints[i] = v; + } + return ints; + } + + /** + * 转换为Long数组
+ * + * @param split 分隔符 + * @param str 被转换的值 + * @return 结果 + */ + public static Long[] toLongArray(String split, String str) { + if (StringUtils.isEmpty(str)) { + return new Long[]{}; + } + String[] arr = str.split(split); + final Long[] longs = new Long[arr.length]; + for (int i = 0; i < arr.length; i++) { + final Long v = toLong(arr[i], null); + longs[i] = v; + } + return longs; + } + + /** + * 转换为String数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static String[] toStrArray(String str) { + return toStrArray(",", str); + } + + /** + * 转换为String数组
+ * + * @param split 分隔符 + * @param split 被转换的值 + * @return 结果 + */ + public static String[] toStrArray(String split, String str) { + return str.split(split); + } + + /** + * 转换为long
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Long toLong(Object value, Long defaultValue) { + if (value == null) { + return defaultValue; + } + if (value instanceof Long) { + return (Long) value; + } + if (value instanceof Number) { + return ((Number) value).longValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + // 支持科学计数法 + return new BigDecimal(valueStr.trim()).longValue(); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * 转换为long
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Long toLong(Object value) { + return toLong(value, null); + } + + /** + * 转换为double
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Double toDouble(Object value, Double defaultValue) { + if (value == null) { + return defaultValue; + } + if (value instanceof Double) { + return (Double) value; + } + if (value instanceof Number) { + return ((Number) value).doubleValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + // 支持科学计数法 + return new BigDecimal(valueStr.trim()).doubleValue(); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * 转换为double
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Double toDouble(Object value) { + return toDouble(value, null); + } + + /** + * 转换为Float
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Float toFloat(Object value, Float defaultValue) { + if (value == null) { + return defaultValue; + } + if (value instanceof Float) { + return (Float) value; + } + if (value instanceof Number) { + return ((Number) value).floatValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + return Float.parseFloat(valueStr.trim()); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * 转换为Float
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Float toFloat(Object value) { + return toFloat(value, null); + } + + /** + * 转换为boolean
+ * String支持的值为:true、false、yes、ok、no,1,0 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Boolean toBool(Object value, Boolean defaultValue) { + if (value == null) { + return defaultValue; + } + if (value instanceof Boolean) { + return (Boolean) value; + } + String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + valueStr = valueStr.trim().toLowerCase(); + switch (valueStr) { + case "true": + return true; + case "false": + return false; + case "yes": + return true; + case "ok": + return true; + case "no": + return false; + case "1": + return true; + case "0": + return false; + default: + return defaultValue; + } + } + + /** + * 转换为boolean
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Boolean toBool(Object value) { + return toBool(value, null); + } + + /** + * 转换为Enum对象
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * + * @param clazz Enum的Class + * @param value 值 + * @param defaultValue 默认值 + * @return Enum + */ + public static > E toEnum(Class clazz, Object value, E defaultValue) { + if (value == null) { + return defaultValue; + } + if (clazz.isAssignableFrom(value.getClass())) { + @SuppressWarnings("unchecked") + E myE = (E) value; + return myE; + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + return Enum.valueOf(clazz, valueStr); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * 转换为Enum对象
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * + * @param clazz Enum的Class + * @param value 值 + * @return Enum + */ + public static > E toEnum(Class clazz, Object value) { + return toEnum(clazz, value, null); + } + + /** + * 转换为BigInteger
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static BigInteger toBigInteger(Object value, BigInteger defaultValue) { + if (value == null) { + return defaultValue; + } + if (value instanceof BigInteger) { + return (BigInteger) value; + } + if (value instanceof Long) { + return BigInteger.valueOf((Long) value); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + return new BigInteger(valueStr); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * 转换为BigInteger
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static BigInteger toBigInteger(Object value) { + return toBigInteger(value, null); + } + + /** + * 转换为BigDecimal
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static BigDecimal toBigDecimal(Object value, BigDecimal defaultValue) { + if (value == null) { + return defaultValue; + } + if (value instanceof BigDecimal) { + return (BigDecimal) value; + } + if (value instanceof Long) { + return new BigDecimal((Long) value); + } + if (value instanceof Double) { + return new BigDecimal((Double) value); + } + if (value instanceof Integer) { + return new BigDecimal((Integer) value); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + return new BigDecimal(valueStr); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * 转换为BigDecimal
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static BigDecimal toBigDecimal(Object value) { + return toBigDecimal(value, null); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @return 字符串 + */ + public static String utf8Str(Object obj) { + return str(obj, CharsetKit.CHARSET_UTF_8); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @param charsetName 字符集 + * @return 字符串 + */ + public static String str(Object obj, String charsetName) { + return str(obj, Charset.forName(charsetName)); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @param charset 字符集 + * @return 字符串 + */ + public static String str(Object obj, Charset charset) { + if (null == obj) { + return null; + } + + if (obj instanceof String) { + return (String) obj; + } else if (obj instanceof byte[]) { + return str((byte[]) obj, charset); + } else if (obj instanceof Byte[]) { + byte[] bytes = ArrayUtils.toPrimitive((Byte[]) obj); + return str(bytes, charset); + } else if (obj instanceof ByteBuffer) { + return str((ByteBuffer) obj, charset); + } + return obj.toString(); + } + + /** + * 将byte数组转为字符串 + * + * @param bytes byte数组 + * @param charset 字符集 + * @return 字符串 + */ + public static String str(byte[] bytes, String charset) { + return str(bytes, StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset)); + } + + /** + * 解码字节码 + * + * @param data 字符串 + * @param charset 字符集,如果此字段为空,则解码的结果取决于平台 + * @return 解码后的字符串 + */ + public static String str(byte[] data, Charset charset) { + if (data == null) { + return null; + } + + if (null == charset) { + return new String(data); + } + return new String(data, charset); + } + + /** + * 将编码的byteBuffer数据转换为字符串 + * + * @param data 数据 + * @param charset 字符集,如果为空使用当前系统字符集 + * @return 字符串 + */ + public static String str(ByteBuffer data, String charset) { + if (data == null) { + return null; + } + + return str(data, Charset.forName(charset)); + } + + /** + * 将编码的byteBuffer数据转换为字符串 + * + * @param data 数据 + * @param charset 字符集,如果为空使用当前系统字符集 + * @return 字符串 + */ + public static String str(ByteBuffer data, Charset charset) { + if (null == charset) { + charset = Charset.defaultCharset(); + } + return charset.decode(data).toString(); + } + + // ----------------------------------------------------------------------- 全角半角转换 + + /** + * 半角转全角 + * + * @param input String. + * @return 全角字符串. + */ + public static String toSBC(String input) { + return toSBC(input, null); + } + + /** + * 半角转全角 + * + * @param input String + * @param notConvertSet 不替换的字符集合 + * @return 全角字符串. + */ + public static String toSBC(String input, Set notConvertSet) { + char c[] = input.toCharArray(); + for (int i = 0; i < c.length; i++) { + if (null != notConvertSet && notConvertSet.contains(c[i])) { + // 跳过不替换的字符 + continue; + } + + if (c[i] == ' ') { + c[i] = '\u3000'; + } else if (c[i] < '\177') { + c[i] = (char) (c[i] + 65248); + + } + } + return new String(c); + } + + /** + * 全角转半角 + * + * @param input String. + * @return 半角字符串 + */ + public static String toDBC(String input) { + return toDBC(input, null); + } + + /** + * 替换全角为半角 + * + * @param text 文本 + * @param notConvertSet 不替换的字符集合 + * @return 替换后的字符 + */ + public static String toDBC(String text, Set notConvertSet) { + char c[] = text.toCharArray(); + for (int i = 0; i < c.length; i++) { + if (null != notConvertSet && notConvertSet.contains(c[i])) { + // 跳过不替换的字符 + continue; + } + + if (c[i] == '\u3000') { + c[i] = ' '; + } else if (c[i] > '\uFF00' && c[i] < '\uFF5F') { + c[i] = (char) (c[i] - 65248); + } + } + String returnString = new String(c); + + return returnString; + } + + /** + * 数字金额大写转换 先写个完整的然后将如零拾替换成零 + * + * @param n 数字 + * @return 中文大写数字 + */ + public static String digitUppercase(double n) { + String[] fraction = {"角", "分"}; + String[] digit = {"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"}; + String[][] unit = {{"元", "万", "亿"}, {"", "拾", "佰", "仟"}}; + + String head = n < 0 ? "负" : ""; + n = Math.abs(n); + + String s = ""; + for (int i = 0; i < fraction.length; i++) { + s += (digit[(int) (Math.floor(n * 10 * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", ""); + } + if (s.length() < 1) { + s = "整"; + } + int integerPart = (int) Math.floor(n); + + for (int i = 0; i < unit[0].length && integerPart > 0; i++) { + String p = ""; + for (int j = 0; j < unit[1].length && n > 0; j++) { + p = digit[integerPart % 10] + unit[1][j] + p; + integerPart = integerPart / 10; + } + s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s; + } + return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$", "零元整"); + } +} diff --git a/Cpop-Common/src/main/java/com/cpop/common/utils/text/StrFormatter.java b/Cpop-Common/src/main/java/com/cpop/common/utils/text/StrFormatter.java new file mode 100644 index 0000000..e21486d --- /dev/null +++ b/Cpop-Common/src/main/java/com/cpop/common/utils/text/StrFormatter.java @@ -0,0 +1,77 @@ +package com.cpop.common.utils.text; + +import com.pupu.common.utils.StringUtils; + +/** + * 字符串格式化 + * + * @author DB + */ +public class StrFormatter { + public static final String EMPTY_JSON = "{}"; + public static final char C_BACKSLASH = '\\'; + public static final char C_DELIM_START = '{'; + + public static final char C_DELIM_END = '}'; + + /** + * 格式化字符串
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
+ * + * @param strPattern 字符串模板 + * @param argArray 参数列表 + * @return 结果 + */ + public static String format(final String strPattern, final Object... argArray) { + if (StringUtils.isEmpty(strPattern) || StringUtils.isEmpty(argArray)) { + return strPattern; + } + final int strPatternLength = strPattern.length(); + + // 初始化定义好的长度以获得更好的性能 + StringBuilder sbuf = new StringBuilder(strPatternLength + 50); + + int handledPosition = 0; + int delimIndex;// 占位符所在位置 + for (int argIndex = 0; argIndex < argArray.length; argIndex++) { + delimIndex = strPattern.indexOf(EMPTY_JSON, handledPosition); + if (delimIndex == -1) { + if (handledPosition == 0) { + return strPattern; + } else { // 字符串模板剩余部分不再包含占位符,加入剩余部分后返回结果 + sbuf.append(strPattern, handledPosition, strPatternLength); + return sbuf.toString(); + } + } else { + if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == C_BACKSLASH) { + if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == C_BACKSLASH) { + // 转义符之前还有一个转义符,占位符依旧有效 + sbuf.append(strPattern, handledPosition, delimIndex - 1); + sbuf.append(Convert.utf8Str(argArray[argIndex])); + handledPosition = delimIndex + 2; + } else { + // 占位符被转义 + argIndex--; + sbuf.append(strPattern, handledPosition, delimIndex - 1); + sbuf.append(C_DELIM_START); + handledPosition = delimIndex + 1; + } + } else { + // 正常占位符 + sbuf.append(strPattern, handledPosition, delimIndex); + sbuf.append(Convert.utf8Str(argArray[argIndex])); + handledPosition = delimIndex + 2; + } + } + } + // 加入最后一个占位符后所有的字符 + sbuf.append(strPattern, handledPosition, strPattern.length()); + + return sbuf.toString(); + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..30558c1 --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +# Pupu-Union + +#### 介绍 +深圳普普数字产业有限公司内部开发框架 + +#### 软件架构 +SpringBoot + Java8 + Mybatis + MybatisFlex + Security + Jwt + Redis + + +#### 安装教程 + +1. mvn clean package install + +#### 使用说明 + +1. 需要修改配置文件中核心密钥读取位置\keyPair\publicKey 修改为正式本地路径 +2. 可以修改配置文件中核心上传文件路径:/Cpop/uploadPath +3. 预设mongoDB,可以不用配置 \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..231cfba --- /dev/null +++ b/pom.xml @@ -0,0 +1,80 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.7.15 + + + com.cpop + Cpop-Union + 1.0.0 + Cpop-Union + 深圳普普数字产业有限公司内部开发框架 + pom + + + Cpop-Common + + + + 1.8 + 2.0 + 1.10.0 + 2.13.0 + 2.12.5 + 2.0.38 + + + + + + + org.apache.commons + commons-text + ${common-text.version} + + + + commons-io + commons-io + ${commons-IO.version} + + + + joda-time + joda-time + ${joda.time.version} + + + + com.alibaba + fastjson + ${fastjson.version} + + + + + + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.yaml + snakeyaml + ${snakeyaml.version} + + +