Initial commit

This commit is contained in:
jackyu66git
2026-03-23 11:52:51 +08:00
commit ea8d889fbe
1114 changed files with 92438 additions and 0 deletions
+119
View File
@@ -0,0 +1,119 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>oneone-common</artifactId>
<groupId>com.oneone.cloud</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>common-core</artifactId>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--hutool-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<!--mvc 相关配置-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<scope>provided</scope>
</dependency>
<!--server-api-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
<!--hibernate-validator-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!--aop-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-annotation</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId> <!-- use mapstruct-jdk8 for Java 8 or higher -->
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
</dependency>
<dependency>
<groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId>
</dependency>
<!-- 监控相关 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,54 @@
package com.oneone.common.base;
import com.oneone.common.enums.AreaTypeEnum;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* 区域节点,包括国家、省份、城市、地区等信息
*
* 数据可见 resources/area.csv 文件
*
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Area {
/**
* 编号 - 全球,即根目录
*/
public static final Integer ID_GLOBAL = 0;
/**
* 编号 - 中国
*/
public static final Integer ID_CHINA = 1;
/**
* 编号
*/
private Integer id;
/**
* 名字
*/
private String name;
/**
* 类型
*
* 枚举 {@link AreaTypeEnum}
*/
private Integer type;
/**
* 父节点
*/
private Area parent;
/**
* 子节点
*/
private List<Area> children;
}
@@ -0,0 +1,19 @@
package com.oneone.common.base;
import lombok.Data;
import lombok.ToString;
import java.io.Serializable;
/**
* @author oneone
* @desc VO 基类
* @email
* @date 2021/1/11
*/
@Data
@ToString
public class BaseVO implements Serializable {
private static final long serialVersionUID = 1L;
}
@@ -0,0 +1,22 @@
package com.oneone.common.base;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotNull;
/**
* @author mice
* @version 1.0
* @date 2022-03-30 20:59
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class IdParam {
@Schema(description = "id")
@NotNull
private Long id;
}
@@ -0,0 +1,17 @@
package com.oneone.common.base;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class KeyValue<K, V> implements Serializable {
private K key;
private V value;
}
@@ -0,0 +1,34 @@
package com.oneone.common.base;
import com.oneone.common.enums.ChannelEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
/**
* @author mice
* @version 1.0
* @date 2022-04-02 11:38
*/
@Data
public class LocalToken implements Serializable {
private static final long serialVersionUID = 1L;
private String accessToken;
private String refreshToken;
@Schema(description = "有效期")
private Long expiresIn;
@Schema(description = "授权方式: refresh, sms_code, wechat")
private String grantType;
@Schema(description = "用户id")
private Long memberId;
@Schema(description = "账号")
private String account;
@Schema(description = "账号类型")
private String accountType;
@Schema(description = "用户类型")
private Integer userType;
@Schema(description = "渠道")
private ChannelEnum channel = ChannelEnum.LOCAL;
}
@@ -0,0 +1,41 @@
package com.oneone.common.base;
import cn.hutool.core.map.MapUtil;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author mice
* @version 1.0
* @date 2022-04-02 14:35
*/
@Data
public class LocalUser {
private Long userId;
private Long memberId;
private int userType;
/**
* 上下文字段,不进行持久化
*
* 1. 用于基于 LocalUser 维度的临时缓存
*/
@JsonIgnore
private Map<String, Object> context;
public void setContext(String key, Object value) {
if (context == null) {
context = new HashMap<>();
}
context.put(key, value);
}
public <T> T getContext(String key, Class<T> type) {
return MapUtil.get(context, key, type);
}
}
@@ -0,0 +1,36 @@
package com.oneone.common.base;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
@Schema(description="分页参数")
@Data
public class PageParam implements Serializable {
private static final Integer PAGE_NO = 1;
private static final Integer PAGE_SIZE = 10;
/**
* 每页条数 - 不分页
*
* 例如说,导出接口,可以设置 {@link #pageSize} 为 -1 不分页,查询所有数据。
*/
public static final Integer PAGE_SIZE_NONE = -1;
@Schema(description = "页码,从 1 开始", requiredMode = Schema.RequiredMode.REQUIRED,example = "1")
@NotNull(message = "页码不能为空")
@Min(value = 1, message = "页码最小值为 1")
private Integer pageNo = PAGE_NO;
@Schema(description = "每页条数,最大值为 100", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
@NotNull(message = "每页条数不能为空")
@Min(value = 1, message = "每页条数最小值为 1")
@Max(value = 100, message = "每页条数最大值为 100")
private Integer pageSize = PAGE_SIZE;
}
@@ -0,0 +1,37 @@
package com.oneone.common.base;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* 排序字段 DTO
*
* 类名加了 ing 的原因是,避免和 ES SortField 重名。
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SortingField implements Serializable {
/**
* 顺序 - 升序
*/
public static final String ORDER_ASC = "asc";
/**
* 顺序 - 降序
*/
public static final String ORDER_DESC = "desc";
/**
* 字段
*/
private String field;
/**
* 顺序
*/
private String order;
}
@@ -0,0 +1,21 @@
package com.oneone.common.constant;
public class CommonConstant {
// 禁用 否
public static final int STATUS_NO = 0;
// 启用 是
public static final int STATUS_YES = 1;
// 失败 未知
public static final int STATUS_FAILED = -1;
/**
* log
*/
public static final String EVENT_LOG = "log";
/**
* request
*/
public static final String EVENT_REQUEST = "request";
}
@@ -0,0 +1,45 @@
package com.oneone.common.constant;
/**
* 全局常量
*
* @author oneone
* @date 2021/10/30 9:32
*/
public interface GlobalConstants {
/**
* 全局状态-是
*/
Integer STATUS_YES = 1;
/**
* 超级管理员角色编码
*/
String ROOT_ROLE_CODE = "ROOT";
/**
* 用户信息
*/
String USER_INFO = "user_info";
String HEX_PREFIX = "0x";
String POUND_SIGN = "#";
String SLASH = "/";
String FEIGN_TOKEN = "feign_token";
String INNER_RPC = "inner";
/**
* 数据源名称 master
*/
String DATASOURCE_MASTER = "master";
/**
* 数据源名称 slave
*/
String DATASOURCE_SLAVE= "slave";
}
@@ -0,0 +1,34 @@
package com.oneone.common.constant;
public interface RedisConstants {
String BUSINESS_NO_PREFIX = "business_no:";
/**
* 优惠券码KEY前缀
*/
String SMS_COUPON_TEMPLATE_CODE_KEY = "sms_coupon_template_code_";
/**
* 用户当前所有可用优惠券key
*/
String SMS_USER_COUPON_USABLE_KEY = "sms_user_coupon_usable_";
/**
* 用户当前所有已使用优惠券key
*/
String SMS_USER_COUPON_USED_KEY = "sms_user_coupon_used_";
/**
* 用户当前所有已过期优惠券key
*/
String SMS_USER_COUPON_EXPIRED_KEY = "sms_user_coupon_expired_";
/**
* 图片验证码
*/
public static final String IMAGE_VALIDATE_CODE = "image_validate_code:";
}
@@ -0,0 +1,154 @@
package com.oneone.common.constant;
public interface SecurityConstants {
/**
* 认证请求头key
*/
String AUTHORIZATION_KEY = "Authorization";
String AUTHORIZATION_BEARER = "Bearer";
/**
* JWT令牌前缀
*/
String JWT_PREFIX = "Bearer ";
/**
* Basic认证前缀
*/
String BASIC_PREFIX = "Basic ";
/**
* JWT载体key
*/
String JWT_PAYLOAD_KEY = "payload";
/**
* JWT ID 唯一标识
*/
String JWT_JTI = "jti";
/**
* JWT ID 唯一标识
*/
String JWT_EXP = "exp";
/**
* 黑名单token前缀
*/
String TOKEN_BLACKLIST_PREFIX = "auth:token:blacklist:";
String USER_ID_KEY = "userId";
String USER_NAME_KEY = "username";
String CLIENT_ID_KEY = "client_id";
/**
* JWT存储权限前缀
*/
String AUTHORITY_PREFIX = "ROLE_";
/**
* JWT存储权限属性
*/
String JWT_AUTHORITIES_KEY = "authorities";
String GRANT_TYPE_KEY = "grant_type";
String REFRESH_TOKEN_KEY = "refresh_token";
/**
* 认证身份标识
*/
String AUTHENTICATION_IDENTITY_KEY = "authenticationIdentity";
String APP_API_PATTERN = "/*/app-api/**";
String LOGOUT_PATH = "/oneone-auth/oauth/logout";
/**
* 新增菜单路径,新增不存在的路由会导致系统无法访问,线上禁止新增菜单的操作
*/
String SAVE_MENU_PATH = "/oneone-admin/api/v1/menus";
/**
* 验证码key前缀
*/
String VALIDATE_CODE_PREFIX = "VALIDATE_CODE:";
/**
* 短信验证码key前缀
*/
String SMS_CODE_PREFIX = "SMS_CODE:";
/**
* 接口文档 Knife4j 测试客户端ID
*/
String TEST_CLIENT_ID = "client";
/**
* 系统管理 web 客户端ID
*/
String ADMIN_CLIENT_ID = "oneone-admin-web";
/**
* 移动端(H5/Android/IOS)客户端ID
*/
String APP_CLIENT_ID = "oneone-app";
/**
* 小程序端(微信小程序、....) 客户端ID
*/
String WEAPP_CLIENT_ID = "oneone-weapp";
/**
* 用户ID字段
*/
public static final String DETAILS_USER_ID = "user_id";
/**
* 用户名字段
*/
public static final String DETAILS_USERNAME = "username";
/**
* 授权信息字段
*/
public static final String AUTHORIZATION_HEADER = "authorization";
/**
* 请求来源
*/
public static final String FROM_SOURCE = "from-source";
/**
* 内部请求
*/
public static final String INNER = "inner";
/**
* 用户标识
*/
public static final String USER_KEY = "user_key";
/**
* 登录用户
*/
public static final String LOGIN_USER = "login_user";
/**
* 角色权限
*/
public static final String ROLE_PERMISSION = "role_permission";
public static final String BACK_TOKEN = "back_token";
String MINI_APP_TOKEN_PREFIX = "mini ";
String OFFICIAL_ACCOUNT_TOKEN_PREFIX = "official ";
String APP_URI_PREFIX = "/app-api";
}
@@ -0,0 +1,28 @@
package com.oneone.common.constant;
/**
* 服务名称
*
* @author ruoyi
*/
public class ServiceNameConstants {
/**
* 认证服务的serviceid
*/
public static final String AUTH_SERVICE = "ruoyi-auth";
/**
* 系统模块的serviceid
*/
public static final String SYSTEM_SERVICE = "ruoyi-system";
/**
* 文件服务的serviceid
*/
public static final String FILE_SERVICE = "ruoyi-file";
public static final String VERIFY_SERVICE = "verification-system";
public static final String PATH_INNER = "inner";
}
@@ -0,0 +1,28 @@
package com.oneone.common.desensitize.base.annotation;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.oneone.common.desensitize.base.handler.DesensitizationHandler;
import com.oneone.common.desensitize.base.serializer.StringDesensitizeSerializer;
import java.lang.annotation.*;
/**
* 顶级脱敏注解,自定义注解需要使用此注解
*
* @author gaibu
*/
@Documented
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside // 此注解是其他所有 jackson 注解的元注解,打上了此注解的注解表明是 jackson 注解的一部分
@JsonSerialize(using = StringDesensitizeSerializer.class) // 指定序列化器
public @interface DesensitizeBy {
/**
* 脱敏处理器
*/
@SuppressWarnings("rawtypes")
Class<? extends DesensitizationHandler> handler();
}
@@ -0,0 +1,21 @@
package com.oneone.common.desensitize.base.handler;
import java.lang.annotation.Annotation;
/**
* 脱敏处理器接口
*
* @author gaibu
*/
public interface DesensitizationHandler<T extends Annotation> {
/**
* 脱敏
*
* @param origin 原始字符串
* @param annotation 注解信息
* @return 脱敏后的字符串
*/
String desensitize(String origin, T annotation);
}
@@ -0,0 +1,92 @@
package com.oneone.common.desensitize.base.serializer;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.lang.Singleton;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import com.oneone.common.desensitize.base.annotation.DesensitizeBy;
import com.oneone.common.desensitize.base.handler.DesensitizationHandler;
import lombok.Getter;
import lombok.Setter;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
/**
* 脱敏序列化器
*
* 实现 JSON 返回数据时,使用 {@link DesensitizationHandler} 对声明脱敏注解的字段,进行脱敏处理。
*
* @author gaibu
*/
@Getter
@SuppressWarnings("rawtypes")
public class StringDesensitizeSerializer extends StdSerializer<String> implements ContextualSerializer {
@Setter
private DesensitizationHandler desensitizationHandler;
protected StringDesensitizeSerializer() {
super(String.class);
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) {
DesensitizeBy annotation = beanProperty.getAnnotation(DesensitizeBy.class);
if (annotation == null) {
return this;
}
// 创建一个 StringDesensitizeSerializer 对象,使用 DesensitizeBy 对应的处理器
StringDesensitizeSerializer serializer = new StringDesensitizeSerializer();
serializer.setDesensitizationHandler(Singleton.get(annotation.handler()));
return serializer;
}
@Override
@SuppressWarnings("unchecked")
public void serialize(String value, JsonGenerator gen, SerializerProvider serializerProvider) throws IOException {
if (StrUtil.isBlank(value)) {
gen.writeNull();
return;
}
// 获取序列化字段
Field field = getField(gen);
// 自定义处理器
DesensitizeBy[] annotations = AnnotationUtil.getCombinationAnnotations(field, DesensitizeBy.class);
if (ArrayUtil.isEmpty(annotations)) {
gen.writeString(value);
return;
}
for (Annotation annotation : field.getAnnotations()) {
if (AnnotationUtil.hasAnnotation(annotation.annotationType(), DesensitizeBy.class)) {
value = this.desensitizationHandler.desensitize(value, annotation);
gen.writeString(value);
return;
}
}
gen.writeString(value);
}
/**
* 获取字段
*
* @param generator JsonGenerator
* @return 字段
*/
private Field getField(JsonGenerator generator) {
String currentName = generator.getOutputContext().getCurrentName();
Object currentValue = generator.getCurrentValue();
Class<?> currentValueClass = currentValue.getClass();
return ReflectUtil.getField(currentValueClass, currentName);
}
}
@@ -0,0 +1,32 @@
package com.oneone.common.desensitize.regex.annotation;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.oneone.common.desensitize.base.annotation.DesensitizeBy;
import com.oneone.common.desensitize.regex.handler.EmailDesensitizationHandler;
import java.lang.annotation.*;
/**
* 邮箱脱敏注解
*
* @author gaibu
*/
@Documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@DesensitizeBy(handler = EmailDesensitizationHandler.class)
public @interface EmailDesensitize {
/**
* 匹配的正则表达式
*/
String regex() default "(^.)[^@]*(@.*$)";
/**
* 替换规则,邮箱;
*
* 比如:example@gmail.com 脱敏之后为 e****@gmail.com
*/
String replacer() default "$1****$2";
}
@@ -0,0 +1,34 @@
package com.oneone.common.desensitize.regex.annotation;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.oneone.common.desensitize.base.annotation.DesensitizeBy;
import com.oneone.common.desensitize.regex.handler.DefaultRegexDesensitizationHandler;
import java.lang.annotation.*;
/**
* 正则脱敏注解
*
* @author gaibu
*/
@Documented
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@DesensitizeBy(handler = DefaultRegexDesensitizationHandler.class)
public @interface RegexDesensitize {
/**
* 匹配的正则表达式(默认匹配所有)
*/
String regex() default "^[\\s\\S]*$";
/**
* 替换规则,会将匹配到的字符串全部替换成 replacer
*
* 例如:regex=123; replacer=******
* 原始字符串 123456789
* 脱敏后字符串 ******456789
*/
String replacer() default "******";
}
@@ -0,0 +1,38 @@
package com.oneone.common.desensitize.regex.handler;
import com.oneone.common.desensitize.base.handler.DesensitizationHandler;
import java.lang.annotation.Annotation;
/**
* 正则表达式脱敏处理器抽象类,已实现通用的方法
*
* @author gaibu
*/
public abstract class AbstractRegexDesensitizationHandler<T extends Annotation>
implements DesensitizationHandler<T> {
@Override
public String desensitize(String origin, T annotation) {
String regex = getRegex(annotation);
String replacer = getReplacer(annotation);
return origin.replaceAll(regex, replacer);
}
/**
* 获取注解上的 regex 参数
*
* @param annotation 注解信息
* @return 正则表达式
*/
abstract String getRegex(T annotation);
/**
* 获取注解上的 replacer 参数
*
* @param annotation 注解信息
* @return 待替换的字符串
*/
abstract String getReplacer(T annotation);
}
@@ -0,0 +1,22 @@
package com.oneone.common.desensitize.regex.handler;
import com.oneone.common.desensitize.regex.annotation.RegexDesensitize;
/**
* {@link RegexDesensitize} 的正则脱敏处理器
*
* @author gaibu
*/
public class DefaultRegexDesensitizationHandler extends AbstractRegexDesensitizationHandler<RegexDesensitize> {
@Override
String getRegex(RegexDesensitize annotation) {
return annotation.regex();
}
@Override
String getReplacer(RegexDesensitize annotation) {
return annotation.replacer();
}
}
@@ -0,0 +1,22 @@
package com.oneone.common.desensitize.regex.handler;
import com.oneone.common.desensitize.regex.annotation.EmailDesensitize;
/**
* {@link EmailDesensitize} 的脱敏处理器
*
* @author gaibu
*/
public class EmailDesensitizationHandler extends AbstractRegexDesensitizationHandler<EmailDesensitize> {
@Override
String getRegex(EmailDesensitize annotation) {
return annotation.regex();
}
@Override
String getReplacer(EmailDesensitize annotation) {
return annotation.replacer();
}
}
@@ -0,0 +1,36 @@
package com.oneone.common.desensitize.slider.annotation;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.oneone.common.desensitize.base.annotation.DesensitizeBy;
import com.oneone.common.desensitize.slider.handler.BankCardDesensitization;
import java.lang.annotation.*;
/**
* 银行卡号
*
* @author gaibu
*/
@Documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@DesensitizeBy(handler = BankCardDesensitization.class)
public @interface BankCardDesensitize {
/**
* 前缀保留长度
*/
int prefixKeep() default 6;
/**
* 后缀保留长度
*/
int suffixKeep() default 2;
/**
* 替换规则,银行卡号; 比如:9988002866797031 脱敏之后为 998800********31
*/
String replacer() default "*";
}
@@ -0,0 +1,36 @@
package com.oneone.common.desensitize.slider.annotation;
import com.oneone.common.desensitize.base.annotation.DesensitizeBy;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.oneone.common.desensitize.slider.handler.CarLicenseDesensitization;
import java.lang.annotation.*;
/**
* 车牌号
*
* @author gaibu
*/
@Documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@DesensitizeBy(handler = CarLicenseDesensitization.class)
public @interface CarLicenseDesensitize {
/**
* 前缀保留长度
*/
int prefixKeep() default 3;
/**
* 后缀保留长度
*/
int suffixKeep() default 1;
/**
* 替换规则,车牌号;比如:粤A66666 脱敏之后为粤A6***6
*/
String replacer() default "*";
}
@@ -0,0 +1,36 @@
package com.oneone.common.desensitize.slider.annotation;
import com.oneone.common.desensitize.base.annotation.DesensitizeBy;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.oneone.common.desensitize.slider.handler.ChineseNameDesensitization;
import java.lang.annotation.*;
/**
* 中文名
*
* @author gaibu
*/
@Documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@DesensitizeBy(handler = ChineseNameDesensitization.class)
public @interface ChineseNameDesensitize {
/**
* 前缀保留长度
*/
int prefixKeep() default 1;
/**
* 后缀保留长度
*/
int suffixKeep() default 0;
/**
* 替换规则,中文名;比如:刘子豪脱敏之后为刘**
*/
String replacer() default "*";
}
@@ -0,0 +1,36 @@
package com.oneone.common.desensitize.slider.annotation;
import com.oneone.common.desensitize.base.annotation.DesensitizeBy;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.oneone.common.desensitize.slider.handler.FixedPhoneDesensitization;
import java.lang.annotation.*;
/**
* 固定电话
*
* @author gaibu
*/
@Documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@DesensitizeBy(handler = FixedPhoneDesensitization.class)
public @interface FixedPhoneDesensitize {
/**
* 前缀保留长度
*/
int prefixKeep() default 4;
/**
* 后缀保留长度
*/
int suffixKeep() default 2;
/**
* 替换规则,固定电话;比如:01086551122 脱敏之后为 0108*****22
*/
String replacer() default "*";
}
@@ -0,0 +1,36 @@
package com.oneone.common.desensitize.slider.annotation;
import com.oneone.common.desensitize.base.annotation.DesensitizeBy;
import com.oneone.common.desensitize.slider.handler.IdCardDesensitization;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import java.lang.annotation.*;
/**
* 身份证
*
* @author gaibu
*/
@Documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@DesensitizeBy(handler = IdCardDesensitization.class)
public @interface IdCardDesensitize {
/**
* 前缀保留长度
*/
int prefixKeep() default 6;
/**
* 后缀保留长度
*/
int suffixKeep() default 2;
/**
* 替换规则,身份证号码;比如:530321199204074611 脱敏之后为 530321**********11
*/
String replacer() default "*";
}
@@ -0,0 +1,36 @@
package com.oneone.common.desensitize.slider.annotation;
import com.oneone.common.desensitize.base.annotation.DesensitizeBy;
import com.oneone.common.desensitize.slider.handler.MobileDesensitization;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import java.lang.annotation.*;
/**
* 手机号
*
* @author gaibu
*/
@Documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@DesensitizeBy(handler = MobileDesensitization.class)
public @interface MobileDesensitize {
/**
* 前缀保留长度
*/
int prefixKeep() default 3;
/**
* 后缀保留长度
*/
int suffixKeep() default 4;
/**
* 替换规则,手机号;比如:13248765917 脱敏之后为 132****5917
*/
String replacer() default "*";
}
@@ -0,0 +1,38 @@
package com.oneone.common.desensitize.slider.annotation;
import com.oneone.common.desensitize.base.annotation.DesensitizeBy;
import com.oneone.common.desensitize.slider.handler.PasswordDesensitization;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import java.lang.annotation.*;
/**
* 密码
*
* @author gaibu
*/
@Documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@DesensitizeBy(handler = PasswordDesensitization.class)
public @interface PasswordDesensitize {
/**
* 前缀保留长度
*/
int prefixKeep() default 0;
/**
* 后缀保留长度
*/
int suffixKeep() default 0;
/**
* 替换规则,密码;
*
* 比如:123456 脱敏之后为 ******
*/
String replacer() default "*";
}
@@ -0,0 +1,39 @@
package com.oneone.common.desensitize.slider.annotation;
import com.oneone.common.desensitize.base.annotation.DesensitizeBy;
import com.oneone.common.desensitize.slider.handler.DefaultDesensitizationHandler;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import java.lang.annotation.*;
/**
* 滑动脱敏注解
*
* @author gaibu
*/
@Documented
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@DesensitizeBy(handler = DefaultDesensitizationHandler.class)
public @interface SliderDesensitize {
/**
* 后缀保留长度
*/
int suffixKeep() default 0;
/**
* 替换规则,会将前缀后缀保留后,全部替换成 replacer
*
* 例如:prefixKeep = 1; suffixKeep = 2; replacer = "*";
* 原始字符串 123456
* 脱敏后 1***56
*/
String replacer() default "*";
/**
* 前缀保留长度
*/
int prefixKeep() default 0;
}
@@ -0,0 +1,79 @@
package com.oneone.common.desensitize.slider.handler;
import com.oneone.common.desensitize.base.handler.DesensitizationHandler;
import java.lang.annotation.Annotation;
/**
* 滑动脱敏处理器抽象类,已实现通用的方法
*
* @author gaibu
*/
public abstract class AbstractDesensitizationHandler<T extends Annotation>
implements DesensitizationHandler<T> {
@Override
public String desensitize(String origin, T annotation) {
int prefixKeep = getPrefixKeep(annotation);
int suffixKeep = getSuffixKeep(annotation);
String replacer = getReplacer(annotation);
int length = origin.length();
// 情况一:原始字符串长度小于等于保留长度,则原始字符串全部替换
if (prefixKeep >= length || suffixKeep >= length) {
return buildReplacerByLength(replacer, length);
}
// 情况二:原始字符串长度小于等于前后缀保留字符串长度,则原始字符串全部替换
if ((prefixKeep + suffixKeep) >= length) {
return buildReplacerByLength(replacer, length);
}
// 情况三:原始字符串长度大于前后缀保留字符串长度,则替换中间字符串
int interval = length - prefixKeep - suffixKeep;
return origin.substring(0, prefixKeep) +
buildReplacerByLength(replacer, interval) +
origin.substring(prefixKeep + interval);
}
/**
* 根据长度循环构建替换符
*
* @param replacer 替换符
* @param length 长度
* @return 构建后的替换符
*/
private String buildReplacerByLength(String replacer, int length) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < length; i++) {
builder.append(replacer);
}
return builder.toString();
}
/**
* 前缀保留长度
*
* @param annotation 注解信息
* @return 前缀保留长度
*/
abstract Integer getPrefixKeep(T annotation);
/**
* 后缀保留长度
*
* @param annotation 注解信息
* @return 后缀保留长度
*/
abstract Integer getSuffixKeep(T annotation);
/**
* 替换符
*
* @param annotation 注解信息
* @return 替换符
*/
abstract String getReplacer(T annotation);
}
@@ -0,0 +1,78 @@
package com.oneone.common.desensitize.slider.handler;
import com.oneone.common.desensitize.base.handler.DesensitizationHandler;
import java.lang.annotation.Annotation;
/**
* 滑动脱敏处理器抽象类,已实现通用的方法
*
* @author gaibu
*/
public abstract class AbstractSliderDesensitizationHandler<T extends Annotation>
implements DesensitizationHandler<T> {
@Override
public String desensitize(String origin, T annotation) {
int prefixKeep = getPrefixKeep(annotation);
int suffixKeep = getSuffixKeep(annotation);
String replacer = getReplacer(annotation);
int length = origin.length();
// 情况一:原始字符串长度小于等于保留长度,则原始字符串全部替换
if (prefixKeep >= length || suffixKeep >= length) {
return buildReplacerByLength(replacer, length);
}
// 情况二:原始字符串长度小于等于前后缀保留字符串长度,则原始字符串全部替换
if ((prefixKeep + suffixKeep) >= length) {
return buildReplacerByLength(replacer, length);
}
// 情况三:原始字符串长度大于前后缀保留字符串长度,则替换中间字符串
int interval = length - prefixKeep - suffixKeep;
return origin.substring(0, prefixKeep) +
buildReplacerByLength(replacer, interval) +
origin.substring(prefixKeep + interval);
}
/**
* 根据长度循环构建替换符
*
* @param replacer 替换符
* @param length 长度
* @return 构建后的替换符
*/
private String buildReplacerByLength(String replacer, int length) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < length; i++) {
builder.append(replacer);
}
return builder.toString();
}
/**
* 前缀保留长度
*
* @param annotation 注解信息
* @return 前缀保留长度
*/
abstract Integer getPrefixKeep(T annotation);
/**
* 后缀保留长度
*
* @param annotation 注解信息
* @return 后缀保留长度
*/
abstract Integer getSuffixKeep(T annotation);
/**
* 替换符
*
* @param annotation 注解信息
* @return 替换符
*/
abstract String getReplacer(T annotation);
}
@@ -0,0 +1,27 @@
package com.oneone.common.desensitize.slider.handler;
import com.oneone.common.desensitize.slider.annotation.BankCardDesensitize;
/**
* {@link BankCardDesensitize} 的脱敏处理器
*
* @author gaibu
*/
public class BankCardDesensitization extends AbstractSliderDesensitizationHandler<BankCardDesensitize> {
@Override
Integer getPrefixKeep(BankCardDesensitize annotation) {
return annotation.prefixKeep();
}
@Override
Integer getSuffixKeep(BankCardDesensitize annotation) {
return annotation.suffixKeep();
}
@Override
String getReplacer(BankCardDesensitize annotation) {
return annotation.replacer();
}
}
@@ -0,0 +1,25 @@
package com.oneone.common.desensitize.slider.handler;
import com.oneone.common.desensitize.slider.annotation.CarLicenseDesensitize;
/**
* {@link CarLicenseDesensitize} 的脱敏处理器
*
* @author gaibu
*/
public class CarLicenseDesensitization extends AbstractSliderDesensitizationHandler<CarLicenseDesensitize> {
@Override
Integer getPrefixKeep(CarLicenseDesensitize annotation) {
return annotation.prefixKeep();
}
@Override
Integer getSuffixKeep(CarLicenseDesensitize annotation) {
return annotation.suffixKeep();
}
@Override
String getReplacer(CarLicenseDesensitize annotation) {
return annotation.replacer();
}
}
@@ -0,0 +1,28 @@
package com.oneone.common.desensitize.slider.handler;
import com.oneone.common.desensitize.slider.annotation.ChineseNameDesensitize;
/**
* {@link ChineseNameDesensitize} 的脱敏处理器
*
* @author gaibu
*/
public class ChineseNameDesensitization extends AbstractSliderDesensitizationHandler<ChineseNameDesensitize> {
@Override
Integer getPrefixKeep(ChineseNameDesensitize annotation) {
return annotation.prefixKeep();
}
@Override
Integer getSuffixKeep(ChineseNameDesensitize annotation) {
return annotation.suffixKeep();
}
@Override
String getReplacer(ChineseNameDesensitize annotation) {
return annotation.replacer();
}
}
@@ -0,0 +1,25 @@
package com.oneone.common.desensitize.slider.handler;
import com.oneone.common.desensitize.slider.annotation.SliderDesensitize;
/**
* {@link SliderDesensitize} 的脱敏处理器
*
* @author gaibu
*/
public class DefaultDesensitizationHandler extends AbstractSliderDesensitizationHandler<SliderDesensitize> {
@Override
Integer getPrefixKeep(SliderDesensitize annotation) {
return annotation.prefixKeep();
}
@Override
Integer getSuffixKeep(SliderDesensitize annotation) {
return annotation.suffixKeep();
}
@Override
String getReplacer(SliderDesensitize annotation) {
return annotation.replacer();
}
}
@@ -0,0 +1,25 @@
package com.oneone.common.desensitize.slider.handler;
import com.oneone.common.desensitize.slider.annotation.FixedPhoneDesensitize;
/**
* {@link FixedPhoneDesensitize} 的脱敏处理器
*
* @author gaibu
*/
public class FixedPhoneDesensitization extends AbstractSliderDesensitizationHandler<FixedPhoneDesensitize> {
@Override
Integer getPrefixKeep(FixedPhoneDesensitize annotation) {
return annotation.prefixKeep();
}
@Override
Integer getSuffixKeep(FixedPhoneDesensitize annotation) {
return annotation.suffixKeep();
}
@Override
String getReplacer(FixedPhoneDesensitize annotation) {
return annotation.replacer();
}
}
@@ -0,0 +1,25 @@
package com.oneone.common.desensitize.slider.handler;
import com.oneone.common.desensitize.slider.annotation.IdCardDesensitize;
/**
* {@link IdCardDesensitize} 的脱敏处理器
*
* @author gaibu
*/
public class IdCardDesensitization extends AbstractSliderDesensitizationHandler<IdCardDesensitize> {
@Override
Integer getPrefixKeep(IdCardDesensitize annotation) {
return annotation.prefixKeep();
}
@Override
Integer getSuffixKeep(IdCardDesensitize annotation) {
return annotation.suffixKeep();
}
@Override
String getReplacer(IdCardDesensitize annotation) {
return annotation.replacer();
}
}
@@ -0,0 +1,26 @@
package com.oneone.common.desensitize.slider.handler;
import com.oneone.common.desensitize.slider.annotation.MobileDesensitize;
/**
* {@link MobileDesensitize} 的脱敏处理器
*
* @author gaibu
*/
public class MobileDesensitization extends AbstractSliderDesensitizationHandler<MobileDesensitize> {
@Override
Integer getPrefixKeep(MobileDesensitize annotation) {
return annotation.prefixKeep();
}
@Override
Integer getSuffixKeep(MobileDesensitize annotation) {
return annotation.suffixKeep();
}
@Override
String getReplacer(MobileDesensitize annotation) {
return annotation.replacer();
}
}
@@ -0,0 +1,25 @@
package com.oneone.common.desensitize.slider.handler;
import com.oneone.common.desensitize.slider.annotation.PasswordDesensitize;
/**
* {@link PasswordDesensitize} 的码脱敏处理器
*
* @author gaibu
*/
public class PasswordDesensitization extends AbstractSliderDesensitizationHandler<PasswordDesensitize> {
@Override
Integer getPrefixKeep(PasswordDesensitize annotation) {
return annotation.prefixKeep();
}
@Override
Integer getSuffixKeep(PasswordDesensitize annotation) {
return annotation.suffixKeep();
}
@Override
String getReplacer(PasswordDesensitize annotation) {
return annotation.replacer();
}
}
@@ -0,0 +1,45 @@
package com.oneone.common.enums;
import lombok.Getter;
/**
* @author oneone
* @description
* @createTime 2021/6/5 17:57
*/
@Getter
public enum AccountTypeEnum {
MOBILE("mobile","手机"),
USERNAME("username","账号"),
EMAIL("email","邮件"),
TWITTER("twitter","twitter"),
WALLET("wallet","钱包登录"),
ALIPAY("alipay","支付宝登录"),
WECHAT("wechat","微信登录"),
MINI_APP("mini_app","微信小程序"),
OFFICIAL_ACCOUNT("official_account","微信公众号"),
APPLE_JWT("apple","苹果jwt"),
DEVICE("device","设备id,游客登录"),
FACEBOOK("facebook","脸书登录"),
GOOGLE("google","谷歌登录"),
;
public static AccountTypeEnum getByCode(String code){
for (AccountTypeEnum value : values()) {
if (value.getCode().equals(code)){
return value;
}
}
return null;
}
private final String code;
private final String desc;
AccountTypeEnum(String code, String desc){
this.code = code;
this.desc = desc;
}
}
@@ -0,0 +1,31 @@
package com.oneone.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* 区域类型枚举
*
*
*/
@AllArgsConstructor
@Getter
public enum AreaTypeEnum {
COUNTRY(1, "国家"),
PROVINCE(2, "省份"),
CITY(3, "城市"),
DISTRICT(4, "地区"), // 县、镇、区等
;
/**
* 类型
*/
private final Integer type;
/**
* 名字
*/
private final String name;
}
@@ -0,0 +1,27 @@
package com.oneone.common.enums;
import lombok.Getter;
@Getter
public enum ChannelEnum {
LOCAL("local","本地"),
;
public static ChannelEnum getByCode(String code){
for (ChannelEnum value : values()) {
if (value.getCode().equals(code)){
return value;
}
}
return null;
}
private final String code;
private final String desc;
ChannelEnum(String code, String desc){
this.code = code;
this.desc = desc;
}
}
@@ -0,0 +1,31 @@
package com.oneone.common.enums;
import cn.hutool.core.util.ObjUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum CommonStatusEnum {
ENABLE(0, "开启" ),
DISABLE(1, "关闭" );
/**
* 状态值
*/
private final Integer status;
/**
* 状态名
*/
private final String name;
public static boolean isEnable(Integer status) {
return ObjUtil.equal(ENABLE.status, status);
}
public static boolean isDisable(Integer status) {
return ObjUtil.equal(DISABLE.status, status);
}
}
@@ -0,0 +1,15 @@
package com.oneone.common.enums;
/**
* 可生成 Int 数组的接口
*
*
*/
public interface IntArrayValuable {
/**
* @return int 数组
*/
int[] array();
}
@@ -0,0 +1,89 @@
package com.oneone.common.exception;
import com.google.common.annotations.VisibleForTesting;
import com.oneone.common.result.BaseEnum;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Getter
@NoArgsConstructor
public class BusinessException extends RuntimeException {
public BaseEnum resultCode;
public BusinessException(String message){
super(message);
}
public BusinessException(String message, Throwable cause){
super(message, cause);
}
public BusinessException(Throwable cause){
super(cause);
}
@Setter
@Getter
private String code;
public BusinessException(String code, String message) {
super(message);
this.code = code;
}
public BusinessException(BaseEnum baseEnum) {
super(baseEnum.getMsg());
this.code = baseEnum.getCode();
}
public BusinessException(ErrorCode errorCode){
super(errorCode.getMsg());
this.code = errorCode.getCode()+"";
}
public static BusinessException exception(ErrorCode errorCode) {
return new BusinessException(errorCode.getCode()+"", errorCode.getMsg());
}
public static BusinessException exception(ErrorCode errorCode, Object... params) {
String message = doFormat(errorCode.getCode(), errorCode.getMsg(), params);
return new BusinessException(errorCode.getCode()+"", message);
}
@VisibleForTesting
public static String doFormat(int code, String messagePattern, Object... params) {
StringBuilder sbuf = new StringBuilder(messagePattern.length() + 50);
int i = 0;
int j;
int l;
for (l = 0; l < params.length; l++) {
j = messagePattern.indexOf("{}", i);
if (j == -1) {
log.error("[doFormat][参数过多:错误码({})|错误内容({})|参数({})", code, messagePattern, params);
if (i == 0) {
return messagePattern;
} else {
sbuf.append(messagePattern.substring(i));
return sbuf.toString();
}
} else {
sbuf.append(messagePattern, i, j);
sbuf.append(params[l]);
i = j + 2;
}
}
if (messagePattern.indexOf("{}", i) != -1) {
log.error("[doFormat][参数过少:错误码({})|错误内容({})|参数({})", code, messagePattern, params);
}
sbuf.append(messagePattern.substring(i));
return sbuf.toString();
}
}
@@ -0,0 +1,30 @@
package com.oneone.common.exception;
import lombok.Data;
/**
* 错误码对象
*
* 全局错误码,占用 [0, 999], 参见 {@link }
* 业务异常错误码,占用 [1 000 000 000, +∞),参见 {@link }
*
* TODO 错误码设计成对象的原因,为未来的 i18 国际化做准备
*/
@Data
public class ErrorCode {
/**
* 错误码
*/
private final Integer code;
/**
* 错误提示
*/
private final String msg;
public ErrorCode(Integer code, String message) {
this.code = code;
this.msg = message;
}
}
@@ -0,0 +1,26 @@
package com.oneone.common.exception;
public class IdempotentException extends RuntimeException {
public IdempotentException() {
super();
}
public IdempotentException(String message) {
super(message);
}
public IdempotentException(String message, Throwable cause) {
super(message, cause);
}
public IdempotentException(Throwable cause) {
super(cause);
}
protected IdempotentException(String message, Throwable cause, boolean enableSuppression,
boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
@@ -0,0 +1,30 @@
package com.oneone.common.exception;
import com.oneone.common.result.BaseEnum;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class LockFailedException extends RuntimeException {
public BaseEnum resultCode;
public LockFailedException(String message){
super(message);
}
public LockFailedException(String message, Throwable cause){
super(message, cause);
}
public LockFailedException(Throwable cause){
super(cause);
}
}
@@ -0,0 +1,26 @@
package com.oneone.common.exception;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
/**
*
* @Date: 2018/11/27 17:40
*/
@Slf4j
@Data
public class SocketBusinessException extends RuntimeException {
private static final long serialVersionUID = 2332608236621015980L;
private String code;
// 协议号
private String cmd;
public SocketBusinessException(String cmd, String code, String message) {
super(message);
this.code = code;
this.cmd = cmd;
}
}
@@ -0,0 +1,21 @@
package com.oneone.common.jsonserializer;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
public class LocalDateTimeToDateSerializer extends JsonSerializer<LocalDateTime> {
@Override
public void serialize(LocalDateTime date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
if(date!=null){
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
jsonGenerator.writeString(formatter.format(date));
}
}
}
@@ -0,0 +1,26 @@
package com.oneone.common.jsonserializer;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.oneone.common.util.Func;
import java.io.IOException;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
public class LocalDateTimeToLocalDateDeserializer extends JsonDeserializer<LocalDateTime> {
@Override
public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
String timestamp = jsonParser.getValueAsString();
if (Func.isEmpty(timestamp)) {
return null;
}
return LocalDate.parse(timestamp, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atStartOfDay();
}
}
@@ -0,0 +1,24 @@
package com.oneone.common.jsonserializer;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import java.io.IOException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
public class LocalDateTimeToLongDeserializer extends JsonDeserializer<LocalDateTime> {
@Override
public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
long timestamp = jsonParser.getValueAsLong();
if (timestamp == 0) {
return null;
}
Instant instant = Instant.ofEpochMilli(timestamp);
return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
}
}
@@ -0,0 +1,17 @@
package com.oneone.common.jsonserializer;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneId;
public class LocalDateTimeToLongSerializer extends JsonSerializer<LocalDateTime> {
@Override
public void serialize(LocalDateTime date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeNumber(date.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
}
}
@@ -0,0 +1,18 @@
package com.oneone.common.jsonserializer;
import cn.hutool.core.date.LocalDateTimeUtil;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneId;
public class LocalDateTimeToStringSerializer extends JsonSerializer<LocalDateTime> {
@Override
public void serialize(LocalDateTime date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString(LocalDateTimeUtil.formatNormal(date));
}
}
@@ -0,0 +1,12 @@
package com.oneone.common.result;
/**
* @author oneone
**/
public interface BaseEnum {
String getCode();
String getMsg();
}
@@ -0,0 +1,23 @@
package com.oneone.common.result;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.List;
/**
* @author mice
* @version 1.0
* @date 2022-04-01 15:44
*/
@Data
@AllArgsConstructor
public class BasePage<T> {
private int pageNum;
private int pageSize;
private long total;
private List<T> list;
}
@@ -0,0 +1,41 @@
package com.oneone.common.result;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@Schema(description = "分页结果")
@Data
public final class PageResult<T> implements Serializable {
@Schema(description = "数据", requiredMode = Schema.RequiredMode.REQUIRED)
private List<T> list;
@Schema(description = "总量", requiredMode = Schema.RequiredMode.REQUIRED)
private Long total;
public PageResult() {
}
public PageResult(List<T> list, Long total) {
this.list = list;
this.total = total;
}
public PageResult(Long total) {
this.list = new ArrayList<>();
this.total = total;
}
public static <T> PageResult<T> empty() {
return new PageResult<>(0L);
}
public static <T> PageResult<T> empty(Long total) {
return new PageResult<>(total);
}
}
@@ -0,0 +1,114 @@
package com.oneone.common.result;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.oneone.common.exception.BusinessException;
import lombok.Data;
import lombok.ToString;
import java.io.Serializable;
/**
* 统一响应结构体
*
* @author oneone
* @date 2022/1/30
**/
@Data
@ToString
public class Result<T> implements Serializable {
private String code;
private T data;
private String msg;
public static <T> Result<T> success() {
return success(null);
}
public static <T> Result<T> success(T data) {
Result<T> result = new Result<>();
result.setCode(ResultCode.SUCCESS.getCode());
result.setMsg(ResultCode.SUCCESS.getMsg());
result.setData(data);
return result;
}
public static <T> Result<T> failed() {
return result(ResultCode.SYSTEM_EXECUTION_ERROR.getCode(), ResultCode.SYSTEM_EXECUTION_ERROR.getMsg(), null);
}
public static <T> Result<T> failed(String msg) {
return result(ResultCode.SYSTEM_EXECUTION_ERROR.getCode(), msg, null);
}
public static <T> Result<T> judge(boolean status) {
if (status) {
return success();
} else {
return failed();
}
}
public static <T> Result<T> failed(BaseEnum resultCode) {
return result(resultCode.getCode(), resultCode.getMsg(), null);
}
public static <T> Result<T> failed(BaseEnum resultCode, String msg) {
return result(resultCode.getCode(), msg, null);
}
public static Result failed(String code, String msg) {
Result result = new Result();
result.setCode(code);
result.setMsg(msg);
return result;
}
private static <T> Result<T> result(BaseEnum resultCode, T data) {
return result(resultCode.getCode(), resultCode.getMsg(), data);
}
private static <T> Result<T> result(String code, String msg, T data) {
Result<T> result = new Result<>();
result.setCode(code);
result.setData(data);
result.setMsg(msg);
return result;
}
public static boolean isSuccess(Result<?> result) {
return result != null && (ResultCode.SUCCESS.getCode().equals(result.getCode())
|| ResultCode.SUCCESS_200.getCode().equals(result.getCode())
);
}
/**
* 判断是否有异常。如果有,则抛出
* 如果没有,则返回 {@link #data} 数据
*/
@JsonIgnore // 避免 jackson 序列化
public T getCheckedData() {
checkError();
return data;
}
/**
* 判断是否有异常。如果有,则抛出 {} 异常
*/
public void checkError() throws BusinessException {
if (isSuccess()) {
return;
}
// 业务异常
throw new BusinessException(code, msg);
}
@JsonIgnore // 避免 jackson 序列化
public boolean isSuccess() {
return (ResultCode.SUCCESS.getCode().equals(code)
|| ResultCode.SUCCESS_200.getCode().equals(code)
);
}
}
@@ -0,0 +1,60 @@
package com.oneone.common.result;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* @author oneone
* @date 2020-06-23
**/
@AllArgsConstructor
@NoArgsConstructor
public enum ResultCode implements BaseEnum, Serializable {
SUCCESS("0", ""),
SUCCESS_200("200", ""),
TOKEN_INVALID_OR_EXPIRED("401", "token无效或已过期"),
SYSTEM_EXECUTION_ERROR("1000", "系统太忙碌了,请稍后再试"),
VALIDATE_CODE_ERROR("1001", "验证码错误"),
IMAGE_VALIDATE_CODE_ERROR("1002", "图形验证码错误"),
TOKEN_ACCESS_FORBIDDEN("1003", "token已被禁止访问"),
PARAM_ERROR("1004", "用户请求参数错误"),
RESOURCE_NOT_FOUND("1005", "请求资源不存在"),
PARAM_IS_NULL("1006", "请求必填参数为空"),
;
@Override
public String getCode() {
return code;
}
@Override
public String getMsg() {
return msg;
}
private String code;
private String msg;
@Override
public String toString() {
return "{" +
"\"code\":\"" + code + '\"' +
", \"msg\":\"" + msg + '\"' +
'}';
}
public static ResultCode getValue(String code) {
for (ResultCode value : values()) {
if (value.getCode().equals(code)) {
return value;
}
}
return SYSTEM_EXECUTION_ERROR; // 默认系统执行错误
}
}
@@ -0,0 +1,42 @@
package com.oneone.common.server;
import com.oneone.common.util.INetUtil;
import lombok.Getter;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
/**
* 服务器信息
*
* @author Chill
*/
@Getter
@Configuration
@Component
public class ServerInfo implements SmartInitializingSingleton {
@Value("${spring.application.name}")
private String applicationName;
private final ServerProperties serverProperties;
private String hostName;
private String ip;
private Integer port;
private String ipWithPort;
@Autowired(required = false)
public ServerInfo(ServerProperties serverProperties) {
this.serverProperties = serverProperties;
}
@Override
public void afterSingletonsInstantiated() {
this.hostName = INetUtil.getHostName();
this.ip = INetUtil.getHostIp();
this.port = serverProperties.getPort();
this.ipWithPort = String.format("%s:%d", ip, port);
}
}
@@ -0,0 +1,120 @@
/*
* Copyright 2011 Google Inc.
* Copyright 2015 Andreas Schildbach
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.oneone.common.util;
@SuppressWarnings("serial")
public class AddressFormatException extends IllegalArgumentException {
public AddressFormatException() {
super();
}
public AddressFormatException(String message) {
super(message);
}
/**
* This exception is thrown by {@link Base58}, {@link Bech32} and the {@link PrefixedChecksummedBytes} hierarchy of
* classes when you try to decode data and a character isn't valid. You shouldn't allow the user to proceed in this
* case.
*/
public static class InvalidCharacter extends AddressFormatException {
public final char character;
public final int position;
public InvalidCharacter(char character, int position) {
super("Invalid character '" + Character.toString(character) + "' at position " + position);
this.character = character;
this.position = position;
}
}
/**
* This exception is thrown by {@link Base58}, {@link Bech32} and the {@link PrefixedChecksummedBytes} hierarchy of
* classes when you try to decode data and the data isn't of the right size. You shouldn't allow the user to proceed
* in this case.
*/
public static class InvalidDataLength extends AddressFormatException {
public InvalidDataLength() {
super();
}
public InvalidDataLength(String message) {
super(message);
}
}
/**
* This exception is thrown by {@link Base58}, {@link Bech32} and the {@link PrefixedChecksummedBytes} hierarchy of
* classes when you try to decode data and the checksum isn't valid. You shouldn't allow the user to proceed in this
* case.
*/
public static class InvalidChecksum extends AddressFormatException {
public InvalidChecksum() {
super("Checksum does not validate");
}
public InvalidChecksum(String message) {
super(message);
}
}
/**
* This exception is thrown by {@link SegwitAddress} when you try to decode data and the witness version doesn't
* match the Bech32 encoding as per BIP350. You shouldn't allow the user to proceed in this case.
*/
public static class UnexpectedWitnessVersion extends AddressFormatException {
public UnexpectedWitnessVersion() {
super("Unexpected witness version");
}
public UnexpectedWitnessVersion(String message) {
super(message);
}
}
/**
* This exception is thrown by the {@link PrefixedChecksummedBytes} hierarchy of classes when you try and decode an
* address or private key with an invalid prefix (version header or human-readable part). You shouldn't allow the
* user to proceed in this case.
*/
public static class InvalidPrefix extends AddressFormatException {
public InvalidPrefix() {
super();
}
public InvalidPrefix(String message) {
super(message);
}
}
/**
* This exception is thrown by the {@link PrefixedChecksummedBytes} hierarchy of classes when you try and decode an
* address with a prefix (version header or human-readable part) that used by another network (usually: mainnet vs
* testnet). You shouldn't allow the user to proceed in this case as they are trying to send money across different
* chains, an operation that is guaranteed to destroy the money.
*/
public static class WrongNetwork extends InvalidPrefix {
public WrongNetwork(int versionHeader) {
super("Version code of address did not match acceptable versions for network: " + versionHeader);
}
public WrongNetwork(String hrp) {
super("Human readable part of address did not match acceptable HRPs for network: " + hrp);
}
}
}
@@ -0,0 +1,221 @@
package com.oneone.common.util;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.text.csv.CsvRow;
import cn.hutool.core.text.csv.CsvUtil;
import com.oneone.common.base.Area;
import com.oneone.common.enums.AreaTypeEnum;
import lombok.NonNull;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.lionsoul.ip2region.xdb.Searcher;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import static com.oneone.common.util.collection.CollectionUtils.convertList;
/**
* 区域工具类
*
*/
@Slf4j
public class AreaUtils {
/**
* 初始化 SEARCHER
*/
@SuppressWarnings("InstantiationOfUtilityClass")
private final static AreaUtils INSTANCE = new AreaUtils();
/**
* IP 查询器,启动加载到内存中
*/
private static Searcher SEARCHER;
/**
* Area 内存缓存,提升访问速度
*/
private static Map<Integer, Area> areas;
private AreaUtils() {
long now = System.currentTimeMillis();
areas = new HashMap<>();
areas.put(Area.ID_GLOBAL, new Area(Area.ID_GLOBAL, "全球", 0,
null, new ArrayList<>()));
// 从 csv 中加载数据
List<CsvRow> rows = CsvUtil.getReader().read(ResourceUtil.getUtf8Reader("area.csv")).getRows();
rows.remove(0); // 删除 header
for (CsvRow row : rows) {
// 创建 Area 对象
Area area = new Area(Integer.valueOf(row.get(0)), row.get(1), Integer.valueOf(row.get(2)),
null, new ArrayList<>());
// 添加到 areas 中
areas.put(area.getId(), area);
}
// 构建父子关系:因为 Area 中没有 parentId 字段,所以需要重复读取
for (CsvRow row : rows) {
Area area = areas.get(Integer.valueOf(row.get(0))); // 自己
Area parent = areas.get(Integer.valueOf(row.get(3))); // 父
Assert.isTrue(area != parent, "{}:父子节点相同", area.getName());
area.setParent(parent);
parent.getChildren().add(area);
}
try {
byte[] bytes = ResourceUtil.readBytes("ip2region.xdb");
SEARCHER = Searcher.newWithBuffer(bytes);
} catch (IOException e) {
log.error("启动加载 AreaUtils 失败", e);
return;
}
log.info("启动加载 AreaUtils 成功,耗时 ({}) 毫秒", System.currentTimeMillis() - now);
}
/**
* 获得指定编号对应的区域
*
* @param id 区域编号
* @return 区域
*/
public static Area getArea(Integer id) {
return areas.get(id);
}
/**
* 格式化区域
*
* @param id 区域编号
* @return 格式化后的区域
*/
public static String format(Integer id) {
return format(id, " ");
}
/**
* 格式化区域
*
* 例如说:
* 1. id = “静安区”时:上海 上海市 静安区
* 2. id = “上海市”时:上海 上海市
* 3. id = “上海”时:上海
* 4. id = “美国”时:美国
* 当区域在中国时,默认不显示中国
*
* @param id 区域编号
* @param separator 分隔符
* @return 格式化后的区域
*/
public static String format(Integer id, String separator) {
// 获得区域
Area area = areas.get(id);
if (area == null) {
return null;
}
// 格式化
StringBuilder sb = new StringBuilder();
for (int i = 0; i < AreaTypeEnum.values().length; i++) { // 避免死循环
sb.insert(0, area.getName());
// “递归”父节点
area = area.getParent();
if (area == null
|| ObjectUtils.equalsAny(area.getId(), Area.ID_GLOBAL, Area.ID_CHINA)) { // 跳过父节点为中国的情况
break;
}
sb.insert(0, separator);
}
return sb.toString();
}
/**
* 获取指定类型的区域列表
*
* @param type 区域类型
* @param func 转换函数
* @param <T> 结果类型
* @return 区域列表
*/
public static <T> List<T> getByType(AreaTypeEnum type, Function<Area, T> func) {
return convertList(areas.values(), func, area -> type.getType().equals(area.getType()));
}
/**
* 根据区域编号、上级区域类型,获取上级区域编号
*
* @param id 区域编号
* @param type 区域类型
* @return 上级区域编号
*/
public static Integer getParentIdByType(Integer id, @NonNull AreaTypeEnum type) {
for (int i = 0; i < Byte.MAX_VALUE; i++) {
Area area = AreaUtils.getArea(id);
if (area == null) {
return null;
}
// 情况一:匹配到,返回它
if (type.getType().equals(area.getType())) {
return area.getId();
}
// 情况二:找到根节点,返回空
if (area.getParent() == null || area.getParent().getId() == null) {
return null;
}
// 其它:继续向上查找
id = area.getParent().getId();
}
return null;
}
/**
* 查询 IP 对应的地区编号
*
* @param ip IP 地址,格式为 127.0.0.1
* @return 地区id
*/
@SneakyThrows
public static Integer getAreaId(String ip) {
return Integer.parseInt(SEARCHER.search(ip.trim()));
}
/**
* 查询 IP 对应的地区编号
*
* @param ip IP 地址的时间戳,格式参考{@link Searcher#checkIP(String)} 的返回
* @return 地区编号
*/
@SneakyThrows
public static Integer getAreaId(long ip) {
return Integer.parseInt(SEARCHER.search(ip));
}
/**
* 查询 IP 对应的地区
*
* @param ip IP 地址,格式为 127.0.0.1
* @return 地区
*/
public static Area getArea(String ip) {
return AreaUtils.getArea(getAreaId(ip));
}
/**
* 查询 IP 对应的地区
*
* @param ip IP 地址的时间戳,格式参考{@link Searcher#checkIP(String)} 的返回
* @return 地区
*/
public static Area getArea(long ip) {
return AreaUtils.getArea(getAreaId(ip));
}
}
@@ -0,0 +1,233 @@
/*
* Copyright 2011 Google Inc.
* Copyright 2018 Andreas Schildbach
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.oneone.common.util;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
/**
* Base58 is a way to encode Bitcoin addresses (or arbitrary data) as alphanumeric strings.
* <p>
* Note that this is not the same base58 as used by Flickr, which you may find referenced around the Internet.
* <p>
* You may want to consider working with { PrefixedChecksummedBytes} instead, which
* adds support for testing the prefix and suffix bytes commonly found in addresses.
* <p>
* Satoshi explains: why base-58 instead of standard base-64 encoding?
* <ul>
* <li>Don't want 0OIl characters that look the same in some fonts and
* could be used to create visually identical looking account numbers.</li>
* <li>A string with non-alphanumeric characters is not as easily accepted as an account number.</li>
* <li>E-mail usually won't line-break if there's no punctuation to break at.</li>
* <li>Doubleclicking selects the whole number as one word if it's all alphanumeric.</li>
* </ul>
* <p>
* However, note that the encoding/decoding runs in O(n&sup2;) time, so it is not useful for large data.
* <p>
* The basic idea of the encoding is to treat the data bytes as a large number represented using
* base-256 digits, convert the number to be represented using base-58 digits, preserve the exact
* number of leading zeros (which are otherwise lost during the mathematical operations on the
* numbers), and finally represent the resulting base-58 digits as alphanumeric ASCII characters.
*/
public class Base58 {
public static final char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray();
private static final char ENCODED_ZERO = ALPHABET[0];
private static final int[] INDEXES = new int[128];
static {
Arrays.fill(INDEXES, -1);
for (int i = 0; i < ALPHABET.length; i++) {
INDEXES[ALPHABET[i]] = i;
}
}
/**
* Encodes the given bytes as a base58 string (no checksum is appended).
*
* @param input the bytes to encode
* @return the base58-encoded string
*/
public static String encode(byte[] input) {
if (input.length == 0) {
return "";
}
// Count leading zeros.
int zeros = 0;
while (zeros < input.length && input[zeros] == 0) {
++zeros;
}
// Convert base-256 digits to base-58 digits (plus conversion to ASCII characters)
input = Arrays.copyOf(input, input.length); // since we modify it in-place
char[] encoded = new char[input.length * 2]; // upper bound
int outputStart = encoded.length;
for (int inputStart = zeros; inputStart < input.length; ) {
encoded[--outputStart] = ALPHABET[divmod(input, inputStart, 256, 58)];
if (input[inputStart] == 0) {
++inputStart; // optimization - skip leading zeros
}
}
// Preserve exactly as many leading encoded zeros in output as there were leading zeros in input.
while (outputStart < encoded.length && encoded[outputStart] == ENCODED_ZERO) {
++outputStart;
}
while (--zeros >= 0) {
encoded[--outputStart] = ENCODED_ZERO;
}
// Return encoded string (including encoded leading zeros).
return new String(encoded, outputStart, encoded.length - outputStart);
}
/**
* Encodes the given version and bytes as a base58 string. A checksum is appended.
*
* @param version the version to encode
* @param payload the bytes to encode, e.g. pubkey hash
* @return the base58-encoded string
*/
public static String encodeChecked(int version, byte[] payload) {
if (version < 0 || version > 255)
throw new IllegalArgumentException("Version not in range.");
// A stringified buffer is:
// 1 byte version + data bytes + 4 bytes check code (a truncated hash)
byte[] addressBytes = new byte[1 + payload.length + 4];
addressBytes[0] = (byte) version;
System.arraycopy(payload, 0, addressBytes, 1, payload.length);
byte[] checksum = hashTwice(addressBytes, 0, payload.length + 1);
System.arraycopy(checksum, 0, addressBytes, payload.length + 1, 4);
return Base58.encode(addressBytes);
}
/**
* Decodes the given base58 string into the original data bytes.
*
* @param input the base58-encoded string to decode
* @return the decoded data bytes
* @throws AddressFormatException if the given string is not a valid base58 string
*/
public static byte[] decode(String input) throws AddressFormatException {
if (input.length() == 0) {
return new byte[0];
}
// Convert the base58-encoded ASCII chars to a base58 byte sequence (base58 digits).
byte[] input58 = new byte[input.length()];
for (int i = 0; i < input.length(); ++i) {
char c = input.charAt(i);
int digit = c < 128 ? INDEXES[c] : -1;
if (digit < 0) {
throw new AddressFormatException.InvalidCharacter(c, i);
}
input58[i] = (byte) digit;
}
// Count leading zeros.
int zeros = 0;
while (zeros < input58.length && input58[zeros] == 0) {
++zeros;
}
// Convert base-58 digits to base-256 digits.
byte[] decoded = new byte[input.length()];
int outputStart = decoded.length;
for (int inputStart = zeros; inputStart < input58.length; ) {
decoded[--outputStart] = divmod(input58, inputStart, 58, 256);
if (input58[inputStart] == 0) {
++inputStart; // optimization - skip leading zeros
}
}
// Ignore extra leading zeroes that were added during the calculation.
while (outputStart < decoded.length && decoded[outputStart] == 0) {
++outputStart;
}
// Return decoded data (including original number of leading zeros).
return Arrays.copyOfRange(decoded, outputStart - zeros, decoded.length);
}
public static BigInteger decodeToBigInteger(String input) throws AddressFormatException {
return new BigInteger(1, decode(input));
}
/**
* Decodes the given base58 string into the original data bytes, using the checksum in the
* last 4 bytes of the decoded data to verify that the rest are correct. The checksum is
* removed from the returned data.
*
* @param input the base58-encoded string to decode (which should include the checksum)
* @throws AddressFormatException if the input is not base 58 or the checksum does not validate.
*/
public static byte[] decodeChecked(String input) throws AddressFormatException {
byte[] decoded = decode(input);
if (decoded.length < 4)
throw new AddressFormatException.InvalidDataLength("Input too short: " + decoded.length);
byte[] data = Arrays.copyOfRange(decoded, 0, decoded.length - 4);
byte[] checksum = Arrays.copyOfRange(decoded, decoded.length - 4, decoded.length);
byte[] actualChecksum = Arrays.copyOfRange(hashTwice(data), 0, 4);
if (!Arrays.equals(checksum, actualChecksum))
throw new AddressFormatException.InvalidChecksum();
return data;
}
/**
* Divides a number, represented as an array of bytes each containing a single digit
* in the specified base, by the given divisor. The given number is modified in-place
* to contain the quotient, and the return value is the remainder.
*
* @param number the number to divide
* @param firstDigit the index within the array of the first non-zero digit
* (this is used for optimization by skipping the leading zeros)
* @param base the base in which the number's digits are represented (up to 256)
* @param divisor the number to divide by (up to 256)
* @return the remainder of the division operation
*/
private static byte divmod(byte[] number, int firstDigit, int base, int divisor) {
// this is just long division which accounts for the base of the input digits
int remainder = 0;
for (int i = firstDigit; i < number.length; i++) {
int digit = (int) number[i] & 0xFF;
int temp = remainder * base + digit;
number[i] = (byte) (temp / divisor);
remainder = temp % divisor;
}
return (byte) remainder;
}
public static byte[] hashTwice(byte[] input, int offset, int length) {
MessageDigest digest = newDigest();
digest.update(input, offset, length);
return digest.digest(digest.digest());
}
public static byte[] hashTwice(byte[] input) {
return hashTwice(input,0,input.length);
}
/**
* Returns a new SHA-256 MessageDigest instance.
*
* This is a convenience method which wraps the checked
* exception that can never occur with a RuntimeException.
*
* @return a new SHA-256 MessageDigest instance
*/
public static MessageDigest newDigest() {
try {
return MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e); // Can't happen.
}
}
}
@@ -0,0 +1,194 @@
package com.oneone.common.util;
import org.springframework.asm.ClassVisitor;
import org.springframework.asm.Type;
import org.springframework.cglib.core.*;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Modifier;
import java.security.ProtectionDomain;
import java.util.HashMap;
import java.util.Map;
/**
* spring cglib 魔改
*
* <p>
* 1. 支持链式 bean
* 2. 自定义的 BeanCopier 解决 spring boot 和 cglib ClassLoader classLoader 不一致的问题
* </p>
*
*/
public abstract class BaseBeanCopier {
private static final BeanCopierKey KEY_FACTORY = (BeanCopierKey) KeyFactory.create(BeanCopierKey.class);
private static final Type CONVERTER = TypeUtils.parseType("org.springframework.cglib.core.Converter");
private static final Type BEAN_COPIER = TypeUtils.parseType(BaseBeanCopier.class.getName());
private static final Signature COPY = new Signature("copy", Type.VOID_TYPE, new Type[]{Constants.TYPE_OBJECT, Constants.TYPE_OBJECT, CONVERTER});
private static final Signature CONVERT = TypeUtils.parseSignature("Object convert(Object, Class, Object)");
interface BeanCopierKey {
/**
* 实例化
* @param source 源
* @param target 目标
* @param useConverter 是否使用转换
* @return
*/
Object newInstance(String source, String target, boolean useConverter);
}
public static BaseBeanCopier create(Class source, Class target, boolean useConverter) {
return BaseBeanCopier.create(source, target, null, useConverter);
}
public static BaseBeanCopier create(Class source, Class target, ClassLoader classLoader, boolean useConverter) {
Generator gen;
if (classLoader == null) {
gen = new Generator();
} else {
gen = new Generator(classLoader);
}
gen.setSource(source);
gen.setTarget(target);
gen.setUseConverter(useConverter);
return gen.create();
}
/**
* 拷贝
* @param from 源
* @param to 目标
* @param converter 转换器
*/
abstract public void copy(Object from, Object to, Converter converter);
public static class Generator extends AbstractClassGenerator {
private static final Source SOURCE = new Source(BaseBeanCopier.class.getName());
private final ClassLoader classLoader;
private Class source;
private Class target;
private boolean useConverter;
Generator() {
super(SOURCE);
this.classLoader = null;
}
Generator(ClassLoader classLoader) {
super(SOURCE);
this.classLoader = classLoader;
}
public void setSource(Class source) {
if (!Modifier.isPublic(source.getModifiers())) {
setNamePrefix(source.getName());
}
this.source = source;
}
public void setTarget(Class target) {
if (!Modifier.isPublic(target.getModifiers())) {
setNamePrefix(target.getName());
}
this.target = target;
}
public void setUseConverter(boolean useConverter) {
this.useConverter = useConverter;
}
@Override
protected ClassLoader getDefaultClassLoader() {
return target.getClassLoader();
}
@Override
protected ProtectionDomain getProtectionDomain() {
return ReflectUtils.getProtectionDomain(source);
}
public BaseBeanCopier create() {
Object key = KEY_FACTORY.newInstance(source.getName(), target.getName(), useConverter);
return (BaseBeanCopier) super.create(key);
}
@Override
public void generateClass(ClassVisitor v) {
Type sourceType = Type.getType(source);
Type targetType = Type.getType(target);
ClassEmitter ce = new ClassEmitter(v);
ce.begin_class(Constants.V1_2,
Constants.ACC_PUBLIC,
getClassName(),
BEAN_COPIER,
null,
Constants.SOURCE_FILE);
EmitUtils.null_constructor(ce);
CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, COPY, null);
// 2018.12.27 by L.cm 支持链式 bean
PropertyDescriptor[] getters = BeanUtil.getBeanGetters(source);
PropertyDescriptor[] setters = BeanUtil.getBeanSetters(target);
Map<String, Object> names = new HashMap<String, Object>(16);
for (PropertyDescriptor getter : getters) {
names.put(getter.getName(), getter);
}
Local targetLocal = e.make_local();
Local sourceLocal = e.make_local();
e.load_arg(1);
e.checkcast(targetType);
e.store_local(targetLocal);
e.load_arg(0);
e.checkcast(sourceType);
e.store_local(sourceLocal);
for (int i = 0; i < setters.length; i++) {
PropertyDescriptor setter = setters[i];
PropertyDescriptor getter = (PropertyDescriptor) names.get(setter.getName());
if (getter != null) {
MethodInfo read = ReflectUtils.getMethodInfo(getter.getReadMethod());
MethodInfo write = ReflectUtils.getMethodInfo(setter.getWriteMethod());
if (useConverter) {
Type setterType = write.getSignature().getArgumentTypes()[0];
e.load_local(targetLocal);
e.load_arg(2);
e.load_local(sourceLocal);
e.invoke(read);
e.box(read.getSignature().getReturnType());
EmitUtils.load_class(e, setterType);
e.push(write.getSignature().getName());
e.invoke_interface(CONVERTER, CONVERT);
e.unbox_or_zero(setterType);
e.invoke(write);
} else if (compatible(getter, setter)) {
// 2018.12.27 by L.cm 支持链式 bean
e.load_local(targetLocal);
e.load_local(sourceLocal);
e.invoke(read);
e.invoke(write);
}
}
}
e.return_value();
e.end_method();
ce.end_class();
}
private static boolean compatible(PropertyDescriptor getter, PropertyDescriptor setter) {
return setter.getPropertyType().isAssignableFrom(getter.getPropertyType());
}
@Override
protected Object firstInstance(Class type) {
return ReflectUtils.newInstance(type);
}
@Override
protected Object nextInstance(Object instance) {
return instance;
}
}
}
@@ -0,0 +1,255 @@
package com.oneone.common.util;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.BeansException;
import org.springframework.cglib.beans.BeanGenerator;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.cglib.core.CodeGenerationException;
import org.springframework.util.Assert;
import java.beans.PropertyDescriptor;
import java.util.*;
/**
* 实体工具类
*
*/
public class BeanUtil extends org.springframework.beans.BeanUtils {
/**
* 实例化对象
* @param clazz 类
* @param <T> 泛型标记
* @return 对象
*/
@SuppressWarnings("unchecked")
public static <T> T newInstance(Class<?> clazz) {
return (T) instantiateClass(clazz);
}
/**
* 实例化对象
* @param clazzStr 类名
* @param <T> 泛型标记
* @return 对象
*/
public static <T> T newInstance(String clazzStr) {
try {
Class<?> clazz = Class.forName(clazzStr);
return newInstance(clazz);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
/**
* 获取Bean的属性
* @param bean bean
* @param propertyName 属性名
* @return 属性值
*/
public static Object getProperty(Object bean, String propertyName) {
Assert.notNull(bean, "bean Could not null");
return BeanMap.create(bean).get(propertyName);
}
/**
* 设置Bean属性
* @param bean bean
* @param propertyName 属性名
* @param value 属性值
*/
public static void setProperty(Object bean, String propertyName, Object value) {
Assert.notNull(bean, "bean Could not null");
BeanMap.create(bean).put(propertyName, value);
}
/**
* 深复制
*
* 注意:不支持链式Bean
*
* @param source 源对象
* @param <T> 泛型标记
* @return T
*/
@SuppressWarnings("unchecked")
public static <T> T clone(T source) {
return (T) BeanUtil.copy(source, source.getClass());
}
/**
* copy 对象属性到另一个对象,默认不使用Convert
*
* 注意:不支持链式Bean,链式用 copyProperties
*
* @param source 源对象
* @param clazz 类名
* @param <T> 泛型标记
* @return T
*/
public static <T> T copy(Object source, Class<T> clazz) {
BaseBeanCopier copier = BaseBeanCopier.create(source.getClass(), clazz, false);
T to = newInstance(clazz);
copier.copy(source, to, null);
return to;
}
/**
* 拷贝对象
*
* 注意:不支持链式Bean,链式用 copyProperties
*
* @param source 源对象
* @param targetBean 需要赋值的对象
*/
public static void copy(Object source, Object targetBean) {
BaseBeanCopier copier = BaseBeanCopier
.create(source.getClass(), targetBean.getClass(), false);
copier.copy(source, targetBean, null);
}
/**
* Copy the property values of the given source bean into the target class.
* <p>Note: The source and target classes do not have to match or even be derived
* from each other, as long as the properties match. Any bean properties that the
* source bean exposes but the target bean does not will silently be ignored.
* <p>This is just a convenience method. For more complex transfer needs,
* @param source the source bean
* @param target the target bean class
* @param <T> 泛型标记
* @throws BeansException if the copying failed
* @return T
*/
public static <T> T copyProperties(Object source, Class<T> target) throws BeansException {
T to = newInstance(target);
BeanUtil.copyProperties(source, to);
return to;
}
/**
* 两个对象间复制属性(忽略空值)
* @param source
* @param target
*/
public static void copyPropertiesIgnoreNull(Object source, Object target) {
final BeanWrapper src = new BeanWrapperImpl(source);
PropertyDescriptor[] pds = src.getPropertyDescriptors();
Set<String> emptyNames = new HashSet<>();
for(PropertyDescriptor pd : pds) {
Object srcValue = src.getPropertyValue(pd.getName());
if (srcValue == null) {
emptyNames.add(pd.getName());
}
}
String[] ignoreProperties = new String[emptyNames.size()];
emptyNames.toArray(ignoreProperties);
copyProperties(source, target, ignoreProperties);
}
/**
* 将对象装成map形式
* @param bean 源对象
* @return {Map}
*/
@SuppressWarnings("unchecked")
public static Map<String, Object> toMap(Object bean) {
return BeanMap.create(bean);
}
/**
* 将map 转为 bean
* @param beanMap map
* @param valueType 对象类型
* @param <T> 泛型标记
* @return {T}
*/
public static <T> T toBean(Map<String, Object> beanMap, Class<T> valueType) {
T bean = BeanUtil.newInstance(valueType);
BeanMap.create(bean).putAll(beanMap);
return bean;
}
/**
* 给一个Bean添加字段
* @param superBean 父级Bean
* @param props 新增属性
* @return {Object}
*/
public static Object generator(Object superBean, BeanProperty... props) {
Class<?> superclass = superBean.getClass();
Object genBean = generator(superclass, props);
BeanUtil.copy(superBean, genBean);
return genBean;
}
/**
* 给一个class添加字段
* @param superclass 父级
* @param props 新增属性
* @return {Object}
*/
public static Object generator(Class<?> superclass, BeanProperty... props) {
BeanGenerator generator = new BeanGenerator();
generator.setSuperclass(superclass);
generator.setUseCache(true);
for (BeanProperty prop : props) {
generator.addProperty(prop.getName(), prop.getType());
}
return generator.create();
}
/**
* 获取 Bean 的所有 get方法
* @param type 类
* @return PropertyDescriptor数组
*/
public static PropertyDescriptor[] getBeanGetters(Class type) {
return getPropertiesHelper(type, true, false);
}
/**
* 获取 Bean 的所有 set方法
* @param type 类
* @return PropertyDescriptor数组
*/
public static PropertyDescriptor[] getBeanSetters(Class type) {
return getPropertiesHelper(type, false, true);
}
private static PropertyDescriptor[] getPropertiesHelper(Class type, boolean read, boolean write) {
try {
PropertyDescriptor[] all = BeanUtil.getPropertyDescriptors(type);
if (read && write) {
return all;
} else {
List<PropertyDescriptor> properties = new ArrayList<PropertyDescriptor>(all.length);
for (PropertyDescriptor pd : all) {
if (read && pd.getReadMethod() != null) {
properties.add(pd);
} else if (write && pd.getWriteMethod() != null) {
properties.add(pd);
}
}
return properties.toArray(new PropertyDescriptor[0]);
}
} catch (BeansException ex) {
throw new CodeGenerationException(ex);
}
}
}
@Getter
@AllArgsConstructor
class BeanProperty {
private final String name;
private final Class<?> type;
}
@@ -0,0 +1,37 @@
package com.oneone.common.util;
import cn.hutool.core.bean.BeanUtil;
import com.oneone.common.result.PageResult;
import com.oneone.common.util.collection.CollectionUtils;
import java.util.List;
/**
* Bean 工具类
*
* 1. 默认使用 {@link BeanUtil} 作为实现类,虽然不同 bean 工具的性能有差别,但是对绝大多数同学的项目,不用在意这点性能
* 2. 针对复杂的对象转换,可以搜参考 AuthConvert 实现,通过 mapstruct + default 配合实现
*
*
*/
public class BeanUtils {
public static <T> T toBean(Object source, Class<T> targetClass) {
return BeanUtil.toBean(source, targetClass);
}
public static <S, T> List<T> toBean(List<S> source, Class<T> targetType) {
if (source == null) {
return null;
}
return CollectionUtils.convertList(source, s -> toBean(s, targetType));
}
public static <S, T> PageResult<T> toBean(PageResult<S> source, Class<T> targetType) {
if (source == null) {
return null;
}
return new PageResult<>(toBean(source.getList(), targetType), source.getTotal());
}
}
@@ -0,0 +1,19 @@
package com.oneone.common.util;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import java.time.Duration;
import java.util.concurrent.Executors;
public class CacheUtils {
public static <K, V> LoadingCache<K, V> buildAsyncReloadingCache(Duration duration, CacheLoader<K, V> loader) {
return CacheBuilder.newBuilder()
// 只阻塞当前数据加载线程,其他线程返回旧值
.refreshAfterWrite(duration)
// 通过 asyncReloading 实现全异步加载,包括 refreshAfterWrite 被阻塞的加载线程
.build(CacheLoader.asyncReloading(loader, Executors.newCachedThreadPool()));
}
}
@@ -0,0 +1,175 @@
package com.oneone.common.util;
import cn.hutool.core.date.LocalDateTimeUtil;
import java.time.*;
import java.util.Calendar;
import java.util.Date;
public class DateUtils {
/**
* 时区 - 默认
*/
public static final String TIME_ZONE_DEFAULT = "GMT+8";
/**
* 秒转换成毫秒
*/
public static final long SECOND_MILLIS = 1000;
public static final String FORMAT_YEAR_MONTH_DAY = "yyyy-MM-dd";
public static final String FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND = "yyyy-MM-dd HH:mm:ss";
public static final String FORMAT_HOUR_MINUTE_SECOND = "HH:mm:ss";
/**
* 将 LocalDateTime 转换成 Date
*
* @param date LocalDateTime
* @return LocalDateTime
*/
public static Date of(LocalDateTime date) {
if (date == null) {
return null;
}
// 将此日期时间与时区相结合以创建 ZonedDateTime
ZonedDateTime zonedDateTime = date.atZone(ZoneId.systemDefault());
// 本地时间线 LocalDateTime 到即时时间线 Instant 时间戳
Instant instant = zonedDateTime.toInstant();
// UTC时间(世界协调时间,UTC + 00:00)转北京(北京,UTC + 8:00)时间
return Date.from(instant);
}
/**
* 将 Date 转换成 LocalDateTime
*
* @param date Date
* @return LocalDateTime
*/
public static LocalDateTime of(Date date) {
if (date == null) {
return null;
}
// 转为时间戳
Instant instant = date.toInstant();
// UTC时间(世界协调时间,UTC + 00:00)转北京(北京,UTC + 8:00)时间
return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
}
public static Date addTime(Duration duration) {
return new Date(System.currentTimeMillis() + duration.toMillis());
}
public static boolean isExpired(Date time) {
return System.currentTimeMillis() > time.getTime();
}
public static boolean isExpired(LocalDateTime time) {
LocalDateTime now = LocalDateTime.now();
return now.isAfter(time);
}
public static long diff(Date endTime, Date startTime) {
return endTime.getTime() - startTime.getTime();
}
/**
* 创建指定时间
*
* @param year 年
* @param mouth 月
* @param day 日
* @return 指定时间
*/
public static Date buildTime(int year, int mouth, int day) {
return buildTime(year, mouth, day, 0, 0, 0);
}
/**
* 创建指定时间
*
* @param year 年
* @param mouth 月
* @param day 日
* @param hour 小时
* @param minute 分钟
* @param second 秒
* @return 指定时间
*/
public static Date buildTime(int year, int mouth, int day,
int hour, int minute, int second) {
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR, year);
calendar.set(Calendar.MONTH, mouth - 1);
calendar.set(Calendar.DAY_OF_MONTH, day);
calendar.set(Calendar.HOUR_OF_DAY, hour);
calendar.set(Calendar.MINUTE, minute);
calendar.set(Calendar.SECOND, second);
calendar.set(Calendar.MILLISECOND, 0); // 一般情况下,都是 0 毫秒
return calendar.getTime();
}
public static Date max(Date a, Date b) {
if (a == null) {
return b;
}
if (b == null) {
return a;
}
return a.compareTo(b) > 0 ? a : b;
}
public static LocalDateTime max(LocalDateTime a, LocalDateTime b) {
if (a == null) {
return b;
}
if (b == null) {
return a;
}
return a.isAfter(b) ? a : b;
}
/**
* 计算当期时间相差的日期
*
* @param field 日历字段.<br/>eg:Calendar.MONTH,Calendar.DAY_OF_MONTH,<br/>Calendar.HOUR_OF_DAY等.
* @param amount 相差的数值
* @return 计算后的日志
*/
public static Date addDate(int field, int amount) {
return addDate(null, field, amount);
}
/**
* 计算当期时间相差的日期
*
* @param date 设置时间
* @param field 日历字段 例如说,{@link Calendar#DAY_OF_MONTH} 等
* @param amount 相差的数值
* @return 计算后的日志
*/
public static Date addDate(Date date, int field, int amount) {
if (amount == 0) {
return date;
}
Calendar c = Calendar.getInstance();
if (date != null) {
c.setTime(date);
}
c.add(field, amount);
return c.getTime();
}
/**
* 是否今天
*
* @param date 日期
* @return 是否
*/
public static boolean isToday(LocalDateTime date) {
return LocalDateTimeUtil.isSameDay(date, LocalDateTime.now());
}
}
@@ -0,0 +1,48 @@
package com.oneone.common.util;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.List;
/**
* Excel 工具类
*
*
*/
public class ExcelUtils {
/**
* 将列表以 Excel 响应给前端
*
* @param response 响应
* @param filename 文件名
* @param sheetName Excel sheet 名
* @param head Excel head 头
* @param data 数据列表哦
* @param <T> 泛型,保证 head 和 data 类型的一致性
* @throws IOException 写入失败的情况
*/
public static <T> void write(HttpServletResponse response, String filename, String sheetName,
Class<T> head, List<T> data) throws IOException {
// 输出 Excel
EasyExcel.write(response.getOutputStream(), head)
.autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) // 基于 column 长度,自动适配。最大 255 宽度
.sheet(sheetName).doWrite(data);
// 设置 header 和 contentType。写在最后的原因是,避免报错时,响应 contentType 已经被修改了
response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
response.setContentType("application/vnd.ms-excel;charset=UTF-8");
}
public static <T> List<T> read(MultipartFile file, Class<T> head) throws IOException {
return EasyExcel.read(file.getInputStream(), head, null)
.autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理
.doReadAllSync();
}
}
@@ -0,0 +1,66 @@
package com.oneone.common.util;
import cn.hutool.core.io.FastStringWriter;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.UndeclaredThrowableException;
/**
* 异常处理工具类
*
*/
public class Exceptions {
/**
* 将CheckedException转换为UncheckedException.
*
* @param e Throwable
* @return {RuntimeException}
*/
public static RuntimeException unchecked(Throwable e) {
if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException
|| e instanceof NoSuchMethodException) {
return new IllegalArgumentException(e);
} else if (e instanceof InvocationTargetException) {
return new RuntimeException(((InvocationTargetException) e).getTargetException());
} else if (e instanceof RuntimeException) {
return (RuntimeException) e;
} else {
return new RuntimeException(e);
}
}
/**
* 代理异常解包
*
* @param wrapped 包装过得异常
* @return 解包后的异常
*/
public static Throwable unwrap(Throwable wrapped) {
Throwable unwrapped = wrapped;
while (true) {
if (unwrapped instanceof InvocationTargetException) {
unwrapped = ((InvocationTargetException) unwrapped).getTargetException();
} else if (unwrapped instanceof UndeclaredThrowableException) {
unwrapped = ((UndeclaredThrowableException) unwrapped).getUndeclaredThrowable();
} else {
return unwrapped;
}
}
}
/**
* 将ErrorStack转化为String.
*
* @param ex Throwable
* @return {String}
*/
public static String getStackTraceAsString(Throwable ex) {
FastStringWriter stringWriter = new FastStringWriter();
ex.printStackTrace(new PrintWriter(stringWriter));
return stringWriter.toString();
}
}
@@ -0,0 +1,110 @@
package com.oneone.common.util;
import cn.hutool.core.util.ObjectUtil;
import java.beans.PropertyDescriptor;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.BeansException;
import org.springframework.cglib.beans.BeanGenerator;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.cglib.core.CodeGenerationException;
import org.springframework.util.Assert;
import static cn.hutool.core.text.CharSequenceUtil.subPre;
public class Func extends org.springframework.beans.BeanUtils{
public static boolean isEmpty(Object obj) {
return ObjectUtil.isEmpty(obj);
}
public static boolean isNotEmpty(Object obj) {
return !ObjectUtil.isEmpty(obj);
}
public static Date localDateTimeToDate(LocalDateTime localDateTime) {
//d当前时间
//系统的默认时区
ZoneId zoneId = ZoneId.systemDefault();
//时区的日期和时间
ZonedDateTime zonedDateTime = localDateTime.atZone(zoneId);
//获取时刻
Date date = Date.from(zonedDateTime.toInstant());
//格式化LocalDateTime、Date
// DateTimeFormatter localDateTimeFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return date;
}
public static LocalDateTime dateToLocalDateTime(Date date ) {
ZoneId zoneId = ZoneId.systemDefault();
LocalDateTime localDateTime = date.toInstant().atZone(zoneId).toLocalDateTime();
return localDateTime;
}
/**
* resultMin=after-before
* @param after
* @param before
* @return
*/
public static int timeDifferenceOfMin(Date after ,Date before ) {
int minute = (int) ((after.getTime() - before.getTime()) / (1000 * 60));
return minute;
}
public static int timeDifferenceOfMin(LocalDateTime after ,LocalDateTime before ) {
int minute = (int) ((localDateTimeToDate(after).getTime() - localDateTimeToDate(before).getTime()) / (1000 * 60));
return minute;
}
public static int timeDifferenceOfMin(Date after ,LocalDateTime before ) {
int minute = (int) ((after.getTime() - localDateTimeToDate(before).getTime()) / (1000 * 60));
return minute;
}
public static int timeDifferenceOfMin(LocalDateTime after ,Date before ) {
int minute = (int) ((localDateTimeToDate(after).getTime() - before.getTime()) / (1000 * 60));
return minute;
}
/**
* 创建StringBuilder对象
*
* @param sb 初始StringBuilder
* @param strs 初始字符串列表
* @return StringBuilder对象
*/
public static StringBuilder appendBuilder(StringBuilder sb, CharSequence... strs) {
for (CharSequence str : strs) {
sb.append(str);
}
return sb;
}
/**
* 去掉指定后缀
*
* @param str 字符串
* @param suffix 后缀
* @return 切掉后的字符串,若后缀不是 suffix, 返回原字符串
*/
public static String removeSuffix(CharSequence str, CharSequence suffix) {
if (isEmpty(str) || isEmpty(suffix)) {
return "";
}
final String str2 = str.toString();
if (str2.endsWith(suffix.toString())) {
return subPre(str2, str2.length() - suffix.length());
}
return str2;
}
}
@@ -0,0 +1,87 @@
package com.oneone.common.util;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @author mice
* @version 1.0
* @date 2022-08-06 15:33
*/
public class IDCardUtil {
/**
* 根据身份证号判断性别
*
* @param idNumber
* @return
*/
public static String judgeGender(String idNumber) throws IllegalArgumentException {
System.out.println(idNumber.length());
if (idNumber.length() != 18 && idNumber.length() != 15) {
throw new IllegalArgumentException("身份证号长度错误");
}
int gender = 0;
if (idNumber.length() == 18) {
//如果身份证号18位,取身份证号倒数第二位
char c = idNumber.charAt(idNumber.length() - 2);
gender = Integer.parseInt(String.valueOf(c));
} else {
//如果身份证号15位,取身份证号最后一位
char c = idNumber.charAt(idNumber.length() - 1);
gender = Integer.parseInt(String.valueOf(c));
}
if (gender % 2 == 1) {
return "";
} else {
return "";
}
}
/**
* 根据身份证的号码算出当前身份证持有者的年龄
*
* @return
*/
public static int countAge(String idNumber) {
if (idNumber.length() != 18 && idNumber.length() != 15) {
throw new IllegalArgumentException("身份证号长度错误");
}
String year;
String yue;
String day;
if (idNumber.length() == 18) {
year = idNumber.substring(6).substring(0, 4);// 得到年份
yue = idNumber.substring(10).substring(0, 2);// 得到月份
day = idNumber.substring(12).substring(0, 2);//得到日
} else {
year = "19" + idNumber.substring(6, 8);// 年份
yue = idNumber.substring(8, 10);// 月份
day = idNumber.substring(10, 12);//日
}
Date date = new Date();// 得到当前的系统时间
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
String fyear = format.format(date).substring(0, 4);// 当前年份
String fyue = format.format(date).substring(5, 7);// 月份
String fday = format.format(date).substring(8, 10);//
int age = 0;
if (Integer.parseInt(yue) == Integer.parseInt(fyue)) {//如果月份相同
if (Integer.parseInt(day) <= Integer.parseInt(fday)) {//说明已经过了生日或者今天是生日
age = Integer.parseInt(fyear) - Integer.parseInt(year);
} else {
age = Integer.parseInt(fyear) - Integer.parseInt(year) - 1;
}
} else {
if (Integer.parseInt(yue) < Integer.parseInt(fyue)) {
//如果当前月份大于出生月份
age = Integer.parseInt(fyear) - Integer.parseInt(year);
} else {
//如果当前月份小于出生月份,说明生日还没过
age = Integer.parseInt(fyear) - Integer.parseInt(year) - 1;
}
}
return age;
}
}
@@ -0,0 +1,138 @@
package com.oneone.common.util;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.UnknownHostException;
import java.util.Enumeration;
/**
* INet 相关工具
*/
public class INetUtil {
public static final String LOCAL_HOST = "127.0.0.1";
/**
* 获取 服务器 hostname
*
* @return hostname
*/
public static String getHostName() {
String hostname;
try {
InetAddress address = InetAddress.getLocalHost();
// force a best effort reverse DNS lookup
hostname = address.getHostName();
if (hostname == null || "".equals(hostname)) {
hostname = address.toString();
}
} catch (UnknownHostException ignore) {
hostname = LOCAL_HOST;
}
return hostname;
}
/**
* 获取 服务器 HostIp
*
* @return HostIp
*/
public static String getHostIp() {
String hostAddress;
try {
InetAddress address = INetUtil.getLocalHostLANAddress();
// force a best effort reverse DNS lookup
hostAddress = address.getHostAddress();
if (hostAddress == null || "".equals(hostAddress)) {
hostAddress = address.toString();
}
} catch (UnknownHostException ignore) {
hostAddress = LOCAL_HOST;
}
return hostAddress;
}
/**
* https://stackoverflow.com/questions/9481865/getting-the-ip-address-of-the-current-machine-using-java
*
* <p>
* Returns an <code>InetAddress</code> object encapsulating what is most likely the machine's LAN IP address.
* <p/>
* This method is intended for use as a replacement of JDK method <code>InetAddress.getLocalHost</code>, because
* that method is ambiguous on Linux systems. Linux systems enumerate the loopback network interface the same
* way as regular LAN network interfaces, but the JDK <code>InetAddress.getLocalHost</code> method does not
* specify the algorithm used to select the address returned under such circumstances, and will often return the
* loopback address, which is not valid for network communication. Details
* <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4665037">here</a>.
* <p/>
* This method will scan all IP addresses on all network interfaces on the host machine to determine the IP address
* most likely to be the machine's LAN address. If the machine has multiple IP addresses, this method will prefer
* a site-local IP address (e.g. 192.168.x.x or 10.10.x.x, usually IPv4) if the machine has one (and will return the
* first site-local address if the machine has more than one), but if the machine does not hold a site-local
* address, this method will return simply the first non-loopback address found (IPv4 or IPv6).
* <p/>
* If this method cannot find a non-loopback address using this selection algorithm, it will fall back to
* calling and returning the result of JDK method <code>InetAddress.getLocalHost</code>.
* <p/>
*
* @throws UnknownHostException If the LAN address of the machine cannot be found.
*/
private static InetAddress getLocalHostLANAddress() throws UnknownHostException {
try {
InetAddress candidateAddress = null;
// Iterate all NICs (network interface cards)...
for (Enumeration ifaces = NetworkInterface.getNetworkInterfaces(); ifaces.hasMoreElements(); ) {
NetworkInterface iface = (NetworkInterface) ifaces.nextElement();
// Iterate all IP addresses assigned to each card...
for (Enumeration inetAddrs = iface.getInetAddresses(); inetAddrs.hasMoreElements(); ) {
InetAddress inetAddr = (InetAddress) inetAddrs.nextElement();
if (!inetAddr.isLoopbackAddress()) {
if (inetAddr.isSiteLocalAddress()) {
// Found non-loopback site-local address. Return it immediately...
return inetAddr;
} else if (candidateAddress == null) {
// Found non-loopback address, but not necessarily site-local.
// Store it as a candidate to be returned if site-local address is not subsequently found...
candidateAddress = inetAddr;
// Note that we don't repeatedly assign non-loopback non-site-local addresses as candidates,
// only the first. For subsequent iterations, candidate will be non-null.
}
}
}
}
if (candidateAddress != null) {
// We did not find a site-local address, but we found some other non-loopback address.
// Server might have a non-site-local address assigned to its NIC (or it might be running
// IPv6 which deprecates the "site-local" concept).
// Return this non-loopback candidate address...
return candidateAddress;
}
// At this point, we did not find a non-loopback address.
// Fall back to returning whatever InetAddress.getLocalHost() returns...
InetAddress jdkSuppliedAddress = InetAddress.getLocalHost();
if (jdkSuppliedAddress == null) {
throw new UnknownHostException("The JDK InetAddress.getLocalHost() method unexpectedly returned null.");
}
return jdkSuppliedAddress;
} catch (Exception e) {
UnknownHostException unknownHostException = new UnknownHostException("Failed to determine LAN address: " + e);
unknownHostException.initCause(e);
throw unknownHostException;
}
}
/**
* 尝试端口时候被占用
*
* @param port 端口号
* @return 没有被占用:true,被占用:false
*/
public static boolean tryPort(int port) {
try (ServerSocket ignore = new ServerSocket(port)) {
return true;
} catch (Exception e) {
return false;
}
}
}
@@ -0,0 +1,43 @@
package com.oneone.common.util;
import lombok.experimental.UtilityClass;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
/**
* @author mice
* @version 1.0
* @date 2023-02-22 17:33
*/
@UtilityClass
public class ImageUtil {
public static InputStream getImageInputStream(String imageUrl) {
InputStream inputStream = null;
HttpURLConnection httpURLConnection = null;
try {
URL url = new URL(imageUrl);
httpURLConnection = (HttpURLConnection) url.openConnection();
// 设置网络连接超时时间
httpURLConnection.setConnectTimeout(3000);
// 设置应用程序要从网络连接读取数据
httpURLConnection.setDoInput(true);
httpURLConnection.setRequestMethod("GET");
int responseCode = httpURLConnection.getResponseCode();
if (responseCode == 200) {
// 从服务器返回一个输入流
inputStream = httpURLConnection.getInputStream();
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return inputStream;
}
}
@@ -0,0 +1,158 @@
package com.oneone.common.util;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import java.io.IOException;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
/**
* jackson转换工具类
*
* @author mice
* @version 1.0
* @date 2021/7/16 17:30
*/
public class JsonUtils {
private static ObjectMapper mapper = new ObjectMapper();
static {
//序列化的时候序列对象的所有属性
mapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
//反序列化的时候如果多了其他属性,不抛出异常
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
//如果是空对象的时候,不抛异常
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
//取消时间的转化格式,默认是时间戳,可以取消,同时需要设置要表现的时间格式
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss" ));
mapper.registerModules(new JavaTimeModule());
}
/**
* 对象转为Json字符串
*
* @param data
* @return
*/
public static String toJSONString(Object data) {
String jsonStr = null;
try {
jsonStr = mapper.writeValueAsString(data);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return jsonStr;
}
/**
* 对象转为byte数组
*
* @param data
* @return
*/
public static byte[] transferToBytes(Object data) {
byte[] byteArr = null;
try {
byteArr = mapper.writeValueAsBytes(data);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return byteArr;
}
/**
* json字符串转为对象
*
* @param str
* @param valueType
* @return
*/
public static <T> T strToClass(String str, Class<T> valueType) {
try {
return mapper.readValue(str, valueType);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}
public static <T> T parseObject(String str, Class<T> valueType) {
try {
return mapper.readValue(str, valueType);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}
public static <T> T parseObject(String text, Type type) {
if (StrUtil.isEmpty(text)) {
return null;
}
try {
return mapper.readValue(text, mapper.getTypeFactory().constructType(type));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static <T> T parseObject(String text, TypeReference<T> typeReference) {
try {
return mapper.readValue(text, typeReference);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* byte数组转为对象
*
* @param byteArr
* @param valueType
* @return
*/
public static <T> T bytesToTransfer(byte[] byteArr, Class<T> valueType) {
T data = null;
try {
data = mapper.readValue(byteArr, valueType);
} catch (JsonParseException e) {
e.printStackTrace();
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return data;
}
/**
* 转指定对象
*
* @param o
* @param valueType
* @return
*/
public static <T> T objectToClass(Object o, Class<T> valueType) {
return mapper.convertValue(o, valueType);
}
public static boolean isJson(String text) {
return JSONUtil.isTypeJSON(text);
}
}
@@ -0,0 +1,124 @@
package com.oneone.common.util;
import cn.hutool.core.date.LocalDateTimeUtil;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.TemporalAdjusters;
/**
* 时间工具类,用于 {@link LocalDateTime}
*
*
*/
public class LocalDateTimeUtils {
/**
* 空的 LocalDateTime 对象,主要用于 DB 唯一索引的默认值
*/
public static LocalDateTime EMPTY = buildTime(1970, 1, 1);
public static LocalDateTime addTime(Duration duration) {
return LocalDateTime.now().plus(duration);
}
public static LocalDateTime minusTime(Duration duration) {
return LocalDateTime.now().minus(duration);
}
public static boolean beforeNow(LocalDateTime date) {
return date.isBefore(LocalDateTime.now());
}
public static boolean afterNow(LocalDateTime date) {
return date.isAfter(LocalDateTime.now());
}
/**
* 创建指定时间
*
* @param year 年
* @param mouth 月
* @param day 日
* @return 指定时间
*/
public static LocalDateTime buildTime(int year, int mouth, int day) {
return LocalDateTime.of(year, mouth, day, 0, 0, 0);
}
public static LocalDateTime[] buildBetweenTime(int year1, int mouth1, int day1,
int year2, int mouth2, int day2) {
return new LocalDateTime[]{buildTime(year1, mouth1, day1), buildTime(year2, mouth2, day2)};
}
/**
* 判断当前时间是否在该时间范围内
*
* @param startTime 开始时间
* @param endTime 结束时间
* @return 是否
*/
public static boolean isBetween(LocalDateTime startTime, LocalDateTime endTime) {
if (startTime == null || endTime == null) {
return false;
}
return LocalDateTimeUtil.isIn(LocalDateTime.now(), startTime, endTime);
}
/**
* 判断当前时间是否在该时间范围内
*
* @param startTime 开始时间
* @param endTime 结束时间
* @return 是否
*/
public static boolean isBetween(String startTime, String endTime) {
if (startTime == null || endTime == null) {
return false;
}
LocalDate nowDate = LocalDate.now();
return LocalDateTimeUtil.isIn(LocalDateTime.now(),
LocalDateTime.of(nowDate, LocalTime.parse(startTime)),
LocalDateTime.of(nowDate, LocalTime.parse(endTime)));
}
/**
* 判断时间段是否重叠
*
* @param startTime1 开始 time1
* @param endTime1 结束 time1
* @param startTime2 开始 time2
* @param endTime2 结束 time2
* @return 重叠:true 不重叠:false
*/
public static boolean isOverlap(LocalTime startTime1, LocalTime endTime1, LocalTime startTime2, LocalTime endTime2) {
LocalDate nowDate = LocalDate.now();
return LocalDateTimeUtil.isOverlap(LocalDateTime.of(nowDate, startTime1), LocalDateTime.of(nowDate, endTime1),
LocalDateTime.of(nowDate, startTime2), LocalDateTime.of(nowDate, endTime2));
}
/**
* 获取指定日期所在的月份的开始时间
* 例如:2023-09-30 00:00:00,000
*
* @param date 日期
* @return 月份的开始时间
*/
public static LocalDateTime beginOfMonth(LocalDateTime date) {
return date.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
}
/**
* 获取指定日期所在的月份的最后时间
* 例如:2023-09-30 23:59:59,999
*
* @param date 日期
* @return 月份的结束时间
*/
public static LocalDateTime endOfMonth(LocalDateTime date) {
return date.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX);
}
}
@@ -0,0 +1,62 @@
package com.oneone.common.util;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.function.Consumer;
/**
* Object 工具类
*
*/
public class ObjectUtils {
/**
* 复制对象,并忽略 Id 编号
*
* @param object 被复制对象
* @param consumer 消费者,可以二次编辑被复制对象
* @return 复制后的对象
*/
public static <T> T cloneIgnoreId(T object, Consumer<T> consumer) {
T result = ObjectUtil.clone(object);
// 忽略 id 编号
Field field = ReflectUtil.getField(object.getClass(), "id");
if (field != null) {
ReflectUtil.setFieldValue(result, field, null);
}
// 二次编辑
if (result != null) {
consumer.accept(result);
}
return result;
}
public static <T extends Comparable<T>> T max(T obj1, T obj2) {
if (obj1 == null) {
return obj2;
}
if (obj2 == null) {
return obj1;
}
return obj1.compareTo(obj2) > 0 ? obj1 : obj2;
}
@SafeVarargs
public static <T> T defaultIfNull(T... array) {
for (T item : array) {
if (item != null) {
return item;
}
}
return null;
}
@SafeVarargs
public static <T> boolean equalsAny(T obj, T... array) {
return Arrays.asList(array).contains(obj);
}
}
@@ -0,0 +1,102 @@
package com.oneone.common.util;
import cn.hutool.core.util.StrUtil;
import lombok.Data;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
/**
* @author mice
* @version 1.0
* @date 2022-04-09 17:49
*/
@Data
public class RandomUtils {
/**
* 用于随机选的数字
*/
public static final String BASE_NUMBER = "0123456789";
/**
* 用于随机选的字符
*/
public static final String BASE_CHAR = "abcdefghijklmnopqrstuvwxyz";
/**
* 用于随机选的字符
*/
public static final String BASE_UPPER_CHAR = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
/**
* 用于随机选的字符和数字
*/
public static final String BASE_CHAR_NUMBER = BASE_CHAR + BASE_NUMBER;
/**
* 用于随机选的字符和数字
*/
public static final String BASE_ALL_CHAR_NUMBER = BASE_CHAR + BASE_NUMBER + BASE_UPPER_CHAR;
public static final String BASE_ALL_CHAR_NUMBER2 = BASE_CHAR + BASE_NUMBER;
public static final String BASE_ALL_CHAR_NUMBER3 = BASE_NUMBER + BASE_UPPER_CHAR;
public static String randomAllStringUpper(int length) {
return randomString(BASE_ALL_CHAR_NUMBER3, length);
}
public static String randomAllStringLower(int length) {
return randomString(BASE_ALL_CHAR_NUMBER2, length);
}
public static String randomAllString(int length) {
return randomString(BASE_ALL_CHAR_NUMBER, length);
}
public static String randomAllNumberString(int length) {
return randomString(BASE_NUMBER, length);
}
public static String randomString(String baseString, int length) {
if (StrUtil.isEmpty(baseString)) {
return StrUtil.EMPTY;
}
final StringBuilder sb = new StringBuilder(length);
if (length < 1) {
length = 1;
}
int baseLength = baseString.length();
for (int i = 0; i < length; i++) {
int number = ThreadLocalRandom.current().nextInt(baseLength);
sb.append(baseString.charAt(number));
}
return sb.toString();
}
public static List getRandomList(List list){
if(list==null||list.size()==0) {
return null;
}
//1、建立一个随机数生成器对象
SecureRandom random = new SecureRandom();
//2、建立一个乱序的对象
List result = new ArrayList();
boolean flag = true;
int size = 0;
while(flag) {
size = list.size();
//随机生成一个下标
int index = random.nextInt(size);
//将内容添加入list中
result.add(list.get(index));
//从原有的list中移除
list.remove(index);
if(list.size()==0) {
flag=false;
}
}
return result;
}
}
@@ -0,0 +1,112 @@
package com.oneone.common.util;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import org.springframework.http.MediaType;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.Map;
/**
* 客户端工具类
*
*
*/
public class ServletUtils {
/**
* 返回 JSON 字符串
*
* @param response 响应
* @param object 对象,会序列化成 JSON 字符串
*/
@SuppressWarnings("deprecation") // 必须使用 APPLICATION_JSON_UTF8_VALUE,否则会乱码
public static void writeJSON(HttpServletResponse response, Object object) {
String content = JsonUtils.toJSONString(object);
ServletUtil.write(response, content, MediaType.APPLICATION_JSON_UTF8_VALUE);
}
/**
* 返回附件
*
* @param response 响应
* @param filename 文件名
* @param content 附件内容
*/
public static void writeAttachment(HttpServletResponse response, String filename, byte[] content) throws IOException {
// 设置 header 和 contentType
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
// 输出附件
IoUtil.write(response.getOutputStream(), false, content);
}
/**
* @param request 请求
* @return ua
*/
public static String getUserAgent(HttpServletRequest request) {
String ua = request.getHeader("User-Agent");
return ua != null ? ua : "";
}
/**
* 获得请求
*
* @return HttpServletRequest
*/
public static HttpServletRequest getRequest() {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (!(requestAttributes instanceof ServletRequestAttributes)) {
return null;
}
return ((ServletRequestAttributes) requestAttributes).getRequest();
}
public static String getUserAgent() {
HttpServletRequest request = getRequest();
if (request == null) {
return null;
}
return getUserAgent(request);
}
public static String getClientIP() {
HttpServletRequest request = getRequest();
if (request == null) {
return null;
}
return ServletUtil.getClientIP(request);
}
// TODO @疯狂:terminal 还是从 ServletUtils 里拿,更容易全局治理;
public static boolean isJsonRequest(ServletRequest request) {
return StrUtil.startWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE);
}
public static String getBody(HttpServletRequest request) {
return ServletUtil.getBody(request);
}
public static byte[] getBodyBytes(HttpServletRequest request) {
return ServletUtil.getBodyBytes(request);
}
public static String getClientIP(HttpServletRequest request) {
return ServletUtil.getClientIP(request);
}
public static Map<String, String> getParamMap(HttpServletRequest request) {
return ServletUtil.getParamMap(request);
}
}
@@ -0,0 +1,70 @@
package com.oneone.common.util;
public class SnowflakeIdGenerator {
private final long workerId;
private final static long START_TIMESTAMP = 1613036400000L; // 设置起始时间戳,这里以2021-02-11 00:00:00为起始时间
private long sequence = 0L;
private final static long WORKER_ID_BITS = 5L;
private final static long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);
private final static long SEQUENCE_BITS = 12L;
private final static long WORKER_ID_SHIFT = SEQUENCE_BITS;
private final static long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
private final static long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS);
private long lastTimestamp = -1L;
public SnowflakeIdGenerator(long workerId) {
if (workerId > MAX_WORKER_ID || workerId < 0) {
throw new IllegalArgumentException(String.format("Worker ID must be between 0 and %d", MAX_WORKER_ID));
}
this.workerId = workerId;
}
public synchronized long nextId() {
long timestamp = System.currentTimeMillis();
if (timestamp < lastTimestamp) {
throw new RuntimeException("Clock moved backwards. Refusing to generate id.");
}
if (timestamp == lastTimestamp) {
sequence = (sequence + 1) & SEQUENCE_MASK;
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0L;
}
lastTimestamp = timestamp;
return ((timestamp - START_TIMESTAMP) << TIMESTAMP_LEFT_SHIFT) | (workerId << WORKER_ID_SHIFT) | sequence;
}
private long tilNextMillis(long lastTimestamp) {
long timestamp = System.currentTimeMillis();
while (timestamp <= lastTimestamp) {
timestamp = System.currentTimeMillis();
}
return timestamp;
}
public static void main(String[] args) {
SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(1); // 设置工作机器ID
for (int i = 0; i < 50; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
long id = idGenerator.nextId();
System.out.println(id+"长度="+String.valueOf(id).length());
}
System.out.println("结束:"+System.currentTimeMillis());
}
}
@@ -0,0 +1,69 @@
package com.oneone.common.util;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
/**
* 字符串工具类
*
*
*/
public class StrUtils {
public static String maxLength(CharSequence str, int maxLength) {
return StrUtil.maxLength(str, maxLength - 3); // -3 的原因,是该方法会补充 ... 恰好
}
/**
* 给定字符串是否以任何一个字符串开始
* 给定字符串和数组为空都返回 false
*
* @param str 给定字符串
* @param prefixes 需要检测的开始字符串
* @since 3.0.6
*/
public static boolean startWithAny(String str, Collection<String> prefixes) {
if (StrUtil.isEmpty(str) || ArrayUtil.isEmpty(prefixes)) {
return false;
}
for (CharSequence suffix : prefixes) {
if (StrUtil.startWith(str, suffix, false)) {
return true;
}
}
return false;
}
public static List<Long> splitToLong(String value, CharSequence separator) {
long[] longs = StrUtil.splitToLong(value, separator);
return Arrays.stream(longs).boxed().collect(Collectors.toList());
}
public static List<Integer> splitToInteger(String value, CharSequence separator) {
int[] integers = StrUtil.splitToInt(value, separator);
return Arrays.stream(integers).boxed().collect(Collectors.toList());
}
/**
* 移除字符串中,包含指定字符串的行
*
* @param content 字符串
* @param sequence 包含的字符串
* @return 移除后的字符串
*/
public static String removeLineContains(String content, String sequence) {
if (StrUtil.isEmpty(content) || StrUtil.isEmpty(sequence)) {
return content;
}
return Arrays.stream(content.split("\n"))
.filter(line -> !line.contains(sequence))
.collect(Collectors.joining("\n"));
}
}
@@ -0,0 +1,28 @@
package com.oneone.common.util;
import org.slf4j.MDC;
/**
* 链路追踪工具类
* <p>
* 考虑到每个 starter 都需要用到该工具类,所以放到 common 模块下的 util 包下
*/
public class TracerUtils {
/**
* 私有化构造方法
*/
private TracerUtils() {
}
/**
* 获得链路追踪编号,直接返回 TraceId。
*
* @return 链路追踪编号
*/
public static String getTraceId() {
return MDC.get("traceId");
}
}
@@ -0,0 +1,50 @@
package com.oneone.common.util;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import org.springframework.util.StringUtils;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validation;
import javax.validation.Validator;
import java.util.Set;
import java.util.regex.Pattern;
public class ValidationUtils {
private static final Pattern PATTERN_MOBILE = Pattern.compile("^(?:(?:\\+|00)86)?1(?:(?:3[\\d])|(?:4[0,1,4-9])|(?:5[0-3,5-9])|(?:6[2,5-7])|(?:7[0-8])|(?:8[\\d])|(?:9[0-3,5-9]))\\d{8}$" );
private static final Pattern PATTERN_URL = Pattern.compile("^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]" );
private static final Pattern PATTERN_XML_NCNAME = Pattern.compile("[a-zA-Z_][\\-_.0-9_a-zA-Z$]*" );
public static boolean isMobile(String mobile) {
return StringUtils.hasText(mobile)
&& PATTERN_MOBILE.matcher(mobile).matches();
}
public static boolean isURL(String url) {
return StringUtils.hasText(url)
&& PATTERN_URL.matcher(url).matches();
}
public static boolean isXmlNCName(String str) {
return StringUtils.hasText(str)
&& PATTERN_XML_NCNAME.matcher(str).matches();
}
public static void validate(Object object, Class<?>... groups) {
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Assert.notNull(validator);
validate(validator, object, groups);
}
public static void validate(Validator validator, Object object, Class<?>... groups) {
Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
if (CollUtil.isNotEmpty(constraintViolations)) {
throw new ConstraintViolationException(constraintViolations);
}
}
}
@@ -0,0 +1,59 @@
package com.oneone.common.util.collection;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.util.ArrayUtil;
import java.util.Collection;
import java.util.function.Consumer;
import java.util.function.Function;
import static com.oneone.common.util.collection.CollectionUtils.convertList;
/**
* Array 工具类
*
*
*/
public class ArrayUtils {
/**
* 将 object 和 newElements 合并成一个数组
*
* @param object 对象
* @param newElements 数组
* @param <T> 泛型
* @return 结果数组
*/
@SafeVarargs
public static <T> Consumer<T>[] append(Consumer<T> object, Consumer<T>... newElements) {
if (object == null) {
return newElements;
}
Consumer<T>[] result = ArrayUtil.newArray(Consumer.class, 1 + newElements.length);
result[0] = object;
System.arraycopy(newElements, 0, result, 1, newElements.length);
return result;
}
public static <T, V> V[] toArray(Collection<T> from, Function<T, V> mapper) {
return toArray(convertList(from, mapper));
}
@SuppressWarnings("unchecked")
public static <T> T[] toArray(Collection<T> from) {
if (CollectionUtil.isEmpty(from)) {
return (T[]) (new Object[0]);
}
return ArrayUtil.toArray(from, (Class<T>) IterUtil.getElementType(from.iterator()));
}
public static <T> T get(T[] array, int index) {
if (null == array || index >= array.length) {
return null;
}
return array[index];
}
}
@@ -0,0 +1,291 @@
package com.oneone.common.util.collection;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ArrayUtil;
import com.google.common.collect.ImmutableMap;
import java.util.*;
import java.util.function.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.util.Arrays.asList;
/**
* Collection 工具类
*
*
*/
public class CollectionUtils {
public static boolean containsAny(Object source, Object... targets) {
return asList(targets).contains(source);
}
public static boolean isAnyEmpty(Collection<?>... collections) {
return Arrays.stream(collections).anyMatch(CollectionUtil::isEmpty);
}
public static <T> boolean anyMatch(Collection<T> from, Predicate<T> predicate) {
return from.stream().anyMatch(predicate);
}
public static <T> List<T> filterList(Collection<T> from, Predicate<T> predicate) {
if (CollUtil.isEmpty(from)) {
return new ArrayList<>();
}
return from.stream().filter(predicate).collect(Collectors.toList());
}
public static <T, R> List<T> distinct(Collection<T> from, Function<T, R> keyMapper) {
if (CollUtil.isEmpty(from)) {
return new ArrayList<>();
}
return distinct(from, keyMapper, (t1, t2) -> t1);
}
public static <T, R> List<T> distinct(Collection<T> from, Function<T, R> keyMapper, BinaryOperator<T> cover) {
if (CollUtil.isEmpty(from)) {
return new ArrayList<>();
}
return new ArrayList<>(convertMap(from, keyMapper, Function.identity(), cover).values());
}
public static <T, U> List<U> convertList(T[] from, Function<T, U> func) {
if (ArrayUtil.isEmpty(from)) {
return new ArrayList<>();
}
return convertList(Arrays.asList(from), func);
}
public static <T, U> List<U> convertList(Collection<T> from, Function<T, U> func) {
if (CollUtil.isEmpty(from)) {
return new ArrayList<>();
}
return from.stream().map(func).filter(Objects::nonNull).collect(Collectors.toList());
}
public static <T, U> List<U> convertList(Collection<T> from, Function<T, U> func, Predicate<T> filter) {
if (CollUtil.isEmpty(from)) {
return new ArrayList<>();
}
return from.stream().filter(filter).map(func).filter(Objects::nonNull).collect(Collectors.toList());
}
public static <K, V> List<V> mergeValuesFromMap(Map<K, List<V>> map) {
return map.values()
.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
}
public static <T, U> Set<U> convertSet(Collection<T> from, Function<T, U> func) {
if (CollUtil.isEmpty(from)) {
return new HashSet<>();
}
return from.stream().map(func).filter(Objects::nonNull).collect(Collectors.toSet());
}
public static <T, U> Set<U> convertSet(Collection<T> from, Function<T, U> func, Predicate<T> filter) {
if (CollUtil.isEmpty(from)) {
return new HashSet<>();
}
return from.stream().filter(filter).map(func).filter(Objects::nonNull).collect(Collectors.toSet());
}
public static <T, K> Map<K, T> convertMapByFilter(Collection<T> from, Predicate<T> filter, Function<T, K> keyFunc) {
if (CollUtil.isEmpty(from)) {
return new HashMap<>();
}
return from.stream().filter(filter).collect(Collectors.toMap(keyFunc, v -> v));
}
public static <T, K> Map<K, T> convertMap(Collection<T> from, Function<T, K> keyFunc) {
if (CollUtil.isEmpty(from)) {
return new HashMap<>();
}
return convertMap(from, keyFunc, Function.identity());
}
public static <T, K> Map<K, T> convertMap(Collection<T> from, Function<T, K> keyFunc, Supplier<? extends Map<K, T>> supplier) {
if (CollUtil.isEmpty(from)) {
return supplier.get();
}
return convertMap(from, keyFunc, Function.identity(), supplier);
}
public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) {
if (CollUtil.isEmpty(from)) {
return new HashMap<>();
}
return convertMap(from, keyFunc, valueFunc, (v1, v2) -> v1);
}
public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc, BinaryOperator<V> mergeFunction) {
if (CollUtil.isEmpty(from)) {
return new HashMap<>();
}
return convertMap(from, keyFunc, valueFunc, mergeFunction, HashMap::new);
}
public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc, Supplier<? extends Map<K, V>> supplier) {
if (CollUtil.isEmpty(from)) {
return supplier.get();
}
return convertMap(from, keyFunc, valueFunc, (v1, v2) -> v1, supplier);
}
public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc, BinaryOperator<V> mergeFunction, Supplier<? extends Map<K, V>> supplier) {
if (CollUtil.isEmpty(from)) {
return new HashMap<>();
}
return from.stream().collect(Collectors.toMap(keyFunc, valueFunc, mergeFunction, supplier));
}
public static <T, K> Map<K, List<T>> convertMultiMap(Collection<T> from, Function<T, K> keyFunc) {
if (CollUtil.isEmpty(from)) {
return new HashMap<>();
}
return from.stream().collect(Collectors.groupingBy(keyFunc, Collectors.mapping(t -> t, Collectors.toList())));
}
public static <T, K, V> Map<K, List<V>> convertMultiMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) {
if (CollUtil.isEmpty(from)) {
return new HashMap<>();
}
return from.stream()
.collect(Collectors.groupingBy(keyFunc, Collectors.mapping(valueFunc, Collectors.toList())));
}
// 暂时没想好名字,先以 2 结尾噶
public static <T, K, V> Map<K, Set<V>> convertMultiMap2(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) {
if (CollUtil.isEmpty(from)) {
return new HashMap<>();
}
return from.stream().collect(Collectors.groupingBy(keyFunc, Collectors.mapping(valueFunc, Collectors.toSet())));
}
public static <T, K> Map<K, T> convertImmutableMap(Collection<T> from, Function<T, K> keyFunc) {
if (CollUtil.isEmpty(from)) {
return Collections.emptyMap();
}
ImmutableMap.Builder<K, T> builder = ImmutableMap.builder();
from.forEach(item -> builder.put(keyFunc.apply(item), item));
return builder.build();
}
/**
* 对比老、新两个列表,找出新增、修改、删除的数据
*
* @param oldList 老列表
* @param newList 新列表
* @param sameFunc 对比函数,返回 true 表示相同,返回 false 表示不同
* 注意,same 是通过每个元素的“标识”,判断它们是不是同一个数据
* @return [新增列表、修改列表、删除列表]
*/
public static <T> List<List<T>> diffList(Collection<T> oldList, Collection<T> newList,
BiFunction<T, T, Boolean> sameFunc) {
List<T> createList = new LinkedList<>(newList); // 默认都认为是新增的,后续会进行移除
List<T> updateList = new ArrayList<>();
List<T> deleteList = new ArrayList<>();
// 通过以 oldList 为主遍历,找出 updateList 和 deleteList
for (T oldObj : oldList) {
// 1. 寻找是否有匹配的
T foundObj = null;
for (Iterator<T> iterator = createList.iterator(); iterator.hasNext(); ) {
T newObj = iterator.next();
// 1.1 不匹配,则直接跳过
if (!sameFunc.apply(oldObj, newObj)) {
continue;
}
// 1.2 匹配,则移除,并结束寻找
iterator.remove();
foundObj = newObj;
break;
}
// 2. 匹配添加到 updateList;不匹配则添加到 deleteList 中
if (foundObj != null) {
updateList.add(foundObj);
} else {
deleteList.add(oldObj);
}
}
return asList(createList, updateList, deleteList);
}
public static boolean containsAny(Collection<?> source, Collection<?> candidates) {
return org.springframework.util.CollectionUtils.containsAny(source, candidates);
}
public static <T> T getFirst(List<T> from) {
return !CollectionUtil.isEmpty(from) ? from.get(0) : null;
}
public static <T> T findFirst(List<T> from, Predicate<T> predicate) {
return findFirst(from, predicate, Function.identity());
}
public static <T, U> U findFirst(List<T> from, Predicate<T> predicate, Function<T, U> func) {
if (CollUtil.isEmpty(from)) {
return null;
}
return from.stream().filter(predicate).findFirst().map(func).orElse(null);
}
public static <T, V extends Comparable<? super V>> V getMaxValue(Collection<T> from, Function<T, V> valueFunc) {
if (CollUtil.isEmpty(from)) {
return null;
}
assert !from.isEmpty(); // 断言,避免告警
T t = from.stream().max(Comparator.comparing(valueFunc)).get();
return valueFunc.apply(t);
}
public static <T, V extends Comparable<? super V>> V getMinValue(List<T> from, Function<T, V> valueFunc) {
if (CollUtil.isEmpty(from)) {
return null;
}
assert from.size() > 0; // 断言,避免告警
T t = from.stream().min(Comparator.comparing(valueFunc)).get();
return valueFunc.apply(t);
}
public static <T, V extends Comparable<? super V>> V getSumValue(List<T> from, Function<T, V> valueFunc,
BinaryOperator<V> accumulator) {
if (CollUtil.isEmpty(from)) {
return null;
}
assert from.size() > 0; // 断言,避免告警
return from.stream().map(valueFunc).reduce(accumulator).get();
}
public static <T> void addIfNotNull(Collection<T> coll, T item) {
if (item == null) {
return;
}
coll.add(item);
}
public static <T> Collection<T> singleton(T deptId) {
return deptId == null ? Collections.emptyList() : Collections.singleton(deptId);
}
public static <T, U> List<U> convertListByFlatMap(Collection<T> from,
Function<T, ? extends Stream<? extends U>> func) {
if (CollUtil.isEmpty(from)) {
return new ArrayList<>();
}
return from.stream().flatMap(func).filter(Objects::nonNull).collect(Collectors.toList());
}
public static <T, U> Set<U> convertSetByFlatMap(Collection<T> from,
Function<T, ? extends Stream<? extends U>> func) {
if (CollUtil.isEmpty(from)) {
return new HashSet<>();
}
return from.stream().flatMap(func).filter(Objects::nonNull).collect(Collectors.toSet());
}
}
@@ -0,0 +1,66 @@
package com.oneone.common.util.collection;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.oneone.common.base.KeyValue;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
/**
* Map 工具类
*
*
*/
public class MapUtils {
/**
* 从哈希表表中,获得 keys 对应的所有 value 数组
*
* @param multimap 哈希表
* @param keys keys
* @return value 数组
*/
public static <K, V> List<V> getList(Multimap<K, V> multimap, Collection<K> keys) {
List<V> result = new ArrayList<>();
keys.forEach(k -> {
Collection<V> values = multimap.get(k);
if (CollectionUtil.isEmpty(values)) {
return;
}
result.addAll(values);
});
return result;
}
/**
* 从哈希表查找到 key 对应的 value,然后进一步处理
* 注意,如果查找到的 value 为 null 时,不进行处理
*
* @param map 哈希表
* @param key key
* @param consumer 进一步处理的逻辑
*/
public static <K, V> void findAndThen(Map<K, V> map, K key, Consumer<V> consumer) {
if (CollUtil.isEmpty(map)) {
return;
}
V value = map.get(key);
if (value == null) {
return;
}
consumer.accept(value);
}
public static <K, V> Map<K, V> convertMap(List<KeyValue<K, V>> keyValues) {
Map<K, V> map = Maps.newLinkedHashMapWithExpectedSize(keyValues.size());
keyValues.forEach(keyValue -> map.put(keyValue.getKey(), keyValue.getValue()));
return map;
}
}
@@ -0,0 +1,19 @@
package com.oneone.common.util.collection;
import cn.hutool.core.collection.CollUtil;
import java.util.Set;
/**
* Set 工具类
*
*
*/
public class SetUtils {
@SafeVarargs
public static <T> Set<T> asSet(T... objs) {
return CollUtil.newHashSet(objs);
}
}
@@ -0,0 +1,14 @@
package com.oneone.common.util.prob;
import lombok.Data;
/**
* @author mice
* @version 1.0
* @date 2023-02-10 16:16
*/
@Data
public class BaseProb {
private Integer index;
private Integer prob;
}
@@ -0,0 +1,5 @@
package com.oneone.common.util.prob;
public interface IBaseProb {
public int getProb();
}
@@ -0,0 +1,55 @@
package com.oneone.common.util.prob;
import cn.hutool.core.util.RandomUtil;
import com.oneone.common.exception.BusinessException;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
/**
* 概率生成器
* @author mice
* @date 2021/12/7 21:32
* @version 1.0
*/
@AllArgsConstructor
@Data
@NoArgsConstructor
public class ProbabilityGenerator<T extends BaseProb> {
protected int totalProbability;
protected List<ProbabilityInterval<T>> probabilityIntervals;
public T generateResult(){
int random = RandomUtil.randomInt(totalProbability);
for (ProbabilityInterval<T> probabilityInterval : probabilityIntervals){
if (random>=probabilityInterval.getStart() && random<probabilityInterval.getEnd()){
return probabilityInterval.getProb();
}
}
return null;
}
public ProbabilityGenerator initGenerator(List<T> list) {
if (CollectionUtils.isEmpty(list)) {
throw new BusinessException("数据不能为空");
}
List<ProbabilityInterval> probabilityIntervals = new ArrayList<>();
int start = 0;
int totalProbability = 0;
for (T obj : list) {
Integer probability = obj.getProb();
totalProbability += probability;
int end = start + probability;
ProbabilityInterval<T> probabilityInterval = new ProbabilityInterval(obj.getIndex(), start, end, obj);
probabilityIntervals.add(probabilityInterval);
start = end;
}
ProbabilityGenerator probabilityGenerator = new ProbabilityGenerator(totalProbability, probabilityIntervals);
return probabilityGenerator;
}
}
@@ -0,0 +1,63 @@
package com.oneone.common.util.prob;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSON;
import com.oneone.common.exception.BusinessException;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
/**
* 概率生成器
* @author mice
* @date 2021/12/7 21:32
* @version 1.0
*/
@AllArgsConstructor
@Data
@Slf4j
@NoArgsConstructor
public class ProbabilityGeneratorByInterface<T extends IBaseProb> {
protected int totalProbability;
protected List<ProbabilityInterval<T>> probabilityIntervals;
public T generateResult(){
if(totalProbability<=0){
return null;
}
int random = RandomUtil.randomInt(totalProbability);
for (ProbabilityInterval<T> probabilityInterval : probabilityIntervals){
if (random>=probabilityInterval.getStart() && random<probabilityInterval.getEnd()){
return probabilityInterval.getProb();
}
}
return null;
}
public ProbabilityGeneratorByInterface initGenerator(List<T> list) {
if (CollectionUtils.isEmpty(list)) {
throw new BusinessException("数据不能为空");
}
List<ProbabilityInterval> probabilityIntervals = new ArrayList<>();
int start = 0;
int totalProbability = 0;
for (T obj : list) {
Integer probability = obj.getProb();
totalProbability += probability;
int end = start + probability;
ProbabilityInterval<T> probabilityInterval = new ProbabilityInterval(start, end, obj);
probabilityIntervals.add(probabilityInterval);
start = end;
}
ProbabilityGeneratorByInterface probabilityGenerator = new ProbabilityGeneratorByInterface(totalProbability, probabilityIntervals);
log.info("随机器初始化:"+JSON.toJSONString(probabilityGenerator.probabilityIntervals)+" \n"
+"总权重:"+probabilityGenerator.totalProbability);
return probabilityGenerator;
}
}
@@ -0,0 +1,26 @@
package com.oneone.common.util.prob;
import lombok.AllArgsConstructor;
import lombok.Data;
/**
* 概率区间
* @author mice
* @date 2021/12/7 21:31
* @version 1.0
*/
@Data
@AllArgsConstructor
public class
ProbabilityInterval<T> {
private int index;
private int start;
private int end;
private T prob;
public ProbabilityInterval(int start, int end, T prob) {
this.start = start;
this.end = end;
this.prob = prob;
}
}
@@ -0,0 +1,28 @@
package com.oneone.common.validation;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
@Target({
ElementType.METHOD,
ElementType.FIELD,
ElementType.ANNOTATION_TYPE,
ElementType.CONSTRUCTOR,
ElementType.PARAMETER,
ElementType.TYPE_USE
})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(
validatedBy = MobileValidator.class
)
public @interface Mobile {
String message() default "手机号格式不正确";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
@@ -0,0 +1,25 @@
package com.oneone.common.validation;
import cn.hutool.core.util.StrUtil;
import com.oneone.common.util.ValidationUtils;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class MobileValidator implements ConstraintValidator<Mobile, String> {
@Override
public void initialize(Mobile annotation) {
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
// 如果手机号为空,默认不校验,即校验通过
if (StrUtil.isEmpty(value)) {
return true;
}
// 校验手机
return ValidationUtils.isMobile(value);
}
}
@@ -0,0 +1,11 @@
# \u901A\u7528\u5F02\u5E38\u4FE1\u606F
id.positive=id\u5FC5\u987B\u4E3A\u6B63\u6574\u6570
page.count.min=\u5206\u9875\u6570\u91CF\u5FC5\u987B\u4E3A\u6B63\u6574\u6570
page.count.max=\u5206\u9875\u6570\u91CF\u5FC5\u987B\u5C0F\u4E8E{value}
page.number.min=\u5206\u9875\u9875\u7801\u5FC5\u987B\u4E3A\u6B63\u6574\u6570
date.past=\u65E5\u671F\u5FC5\u987B\u662F\u8FC7\u53BB\u7684\u65F6\u95F4
date.interval=\u7ED3\u675F\u65E5\u671F\u5FC5\u987B\u65E9\u4E8E\u5F00\u59CB\u65E5\u671F
text.length.min=\u6700\u5C0F\u957F\u5EA6\u5FC5\u987B\u5927\u4E8E{min}
text.length.max=\u6700\u5927\u957F\u5EA6\u5FC5\u987B\u5C0F\u4E8E{max}
phone.valid=\u5FC5\u987B\u662F\u5927\u964611\u4F4D\u624B\u673A\u53F7
zipcode.valid=\u90AE\u7F16\u5FC5\u987B\u4E3A6\u4F4D\u6570\u5B57
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,21 @@
_____ _____ _____ _____ _____
/\ \ /\ \ /\ \ /\ \ /\ \
/::\ \ /::\ \ /::\ \ /::\ \ /::\ \
/::::\ \ /::::\ \ \:::\ \ /::::\ \ /::::\ \
/::::::\ \ /::::::\ \ \:::\ \ /::::::\ \ /::::::\ \
/:::/\:::\ \ /:::/\:::\ \ \:::\ \ /:::/\:::\ \ /:::/\:::\ \
/:::/__\:::\ \ /:::/__\:::\ \ \:::\ \ /:::/__\:::\ \ /:::/__\:::\ \
/::::\ \:::\ \ \:::\ \:::\ \ /::::\ \ /::::\ \:::\ \ /::::\ \:::\ \
/::::::\ \:::\ \ ___\:::\ \:::\ \ /::::::\ \ /::::::\ \:::\ \ /::::::\ \:::\ \
/:::/\:::\ \:::\ \ /\ \:::\ \:::\ \ /:::/\:::\ \ /:::/\:::\ \:::\ \ /:::/\:::\ \:::\____\
/:::/__\:::\ \:::\____\ /::\ \:::\ \:::\____\ /:::/ \:::\____\/:::/ \:::\ \:::\____\/:::/ \:::\ \:::| |
\:::\ \:::\ \::/ / \:::\ \:::\ \::/ / /:::/ \::/ /\::/ \:::\ /:::/ /\::/ |::::\ /:::|____|
\:::\ \:::\ \/____/ \:::\ \:::\ \/____/ /:::/ / \/____/ \/____/ \:::\/:::/ / \/____|:::::\/:::/ /
\:::\ \:::\ \ \:::\ \:::\ \ /:::/ / \::::::/ / |:::::::::/ /
\:::\ \:::\____\ \:::\ \:::\____\ /:::/ / \::::/ / |::|\::::/ /
\:::\ \::/ / \:::\ /:::/ / \::/ / /:::/ / |::| \::/____/
\:::\ \/____/ \:::\/:::/ / \/____/ /:::/ / |::| ~|
\:::\ \ \::::::/ / /:::/ / |::| |
\:::\____\ \::::/ / /:::/ / \::| |
\::/ / \::/ / \::/ / \:| |
\/____/ \/____/ \/____/ \|___|
File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More