chore(build): 更新项目依赖版本并优化代码格式

- 升级 Spring Boot 父项目版本至 3.5.9
- 更新 MySQL 连接器版本至 9.4.0
- 更新 MyBatis-Plus 版本至 3.5.14
- 更新 Fastjson2 版本至 2.0.59
- 更新 Hutool 版本至 5.8.41
- 更新 JJWT 版本至 0.12.6
- 更新 Knife4j 版本至 4.5.0
- 更新 Undertow 版本至 2.3.20.Final
- 更新 Oshi 版本至 6.6.5
- 更新 Guava 版本至 33.5.0-jre
- 格式化注解文件中的代码缩进
- 移除多余的空行和注释格式调整
This commit is contained in:
2025-12-30 12:53:51 +08:00
parent 377b0989f8
commit 83f75831f0
207 changed files with 7424 additions and 6977 deletions
@@ -41,38 +41,38 @@ import org.springframework.web.bind.annotation.RequestMethod;
@RequestMapping(method = RequestMethod.DELETE)
public @interface AnonymousDeleteMapping {
/**
* Alias for {@link RequestMapping#name}.
*/
@AliasFor(annotation = RequestMapping.class) String name() default "";
/**
* Alias for {@link RequestMapping#name}.
*/
@AliasFor(annotation = RequestMapping.class) String name() default "";
/**
* Alias for {@link RequestMapping#value}.
*/
@AliasFor(annotation = RequestMapping.class) String[] value() default {};
/**
* Alias for {@link RequestMapping#value}.
*/
@AliasFor(annotation = RequestMapping.class) String[] value() default {};
/**
* Alias for {@link RequestMapping#path}.
*/
@AliasFor(annotation = RequestMapping.class) String[] path() default {};
/**
* Alias for {@link RequestMapping#path}.
*/
@AliasFor(annotation = RequestMapping.class) String[] path() default {};
/**
* Alias for {@link RequestMapping#params}.
*/
@AliasFor(annotation = RequestMapping.class) String[] params() default {};
/**
* Alias for {@link RequestMapping#params}.
*/
@AliasFor(annotation = RequestMapping.class) String[] params() default {};
/**
* Alias for {@link RequestMapping#headers}.
*/
@AliasFor(annotation = RequestMapping.class) String[] headers() default {};
/**
* Alias for {@link RequestMapping#headers}.
*/
@AliasFor(annotation = RequestMapping.class) String[] headers() default {};
/**
* Alias for {@link RequestMapping#consumes}.
*/
@AliasFor(annotation = RequestMapping.class) String[] consumes() default {};
/**
* Alias for {@link RequestMapping#consumes}.
*/
@AliasFor(annotation = RequestMapping.class) String[] consumes() default {};
/**
* Alias for {@link RequestMapping#produces}.
*/
@AliasFor(annotation = RequestMapping.class) String[] produces() default {};
/**
* Alias for {@link RequestMapping#produces}.
*/
@AliasFor(annotation = RequestMapping.class) String[] produces() default {};
}
@@ -39,40 +39,40 @@ import org.springframework.web.bind.annotation.RequestMethod;
@RequestMapping(method = RequestMethod.GET)
public @interface AnonymousGetMapping {
/**
* Alias for {@link RequestMapping#name}.
*/
@AliasFor(annotation = RequestMapping.class) String name() default "";
/**
* Alias for {@link RequestMapping#name}.
*/
@AliasFor(annotation = RequestMapping.class) String name() default "";
/**
* Alias for {@link RequestMapping#value}.
*/
@AliasFor(annotation = RequestMapping.class) String[] value() default {};
/**
* Alias for {@link RequestMapping#value}.
*/
@AliasFor(annotation = RequestMapping.class) String[] value() default {};
/**
* Alias for {@link RequestMapping#path}.
*/
@AliasFor(annotation = RequestMapping.class) String[] path() default {};
/**
* Alias for {@link RequestMapping#path}.
*/
@AliasFor(annotation = RequestMapping.class) String[] path() default {};
/**
* Alias for {@link RequestMapping#params}.
*/
@AliasFor(annotation = RequestMapping.class) String[] params() default {};
/**
* Alias for {@link RequestMapping#params}.
*/
@AliasFor(annotation = RequestMapping.class) String[] params() default {};
/**
* Alias for {@link RequestMapping#headers}.
*/
@AliasFor(annotation = RequestMapping.class) String[] headers() default {};
/**
* Alias for {@link RequestMapping#headers}.
*/
@AliasFor(annotation = RequestMapping.class) String[] headers() default {};
/**
* Alias for {@link RequestMapping#consumes}.
*
* @since 4.3.5
*/
@AliasFor(annotation = RequestMapping.class) String[] consumes() default {};
/**
* Alias for {@link RequestMapping#consumes}.
*
* @since 4.3.5
*/
@AliasFor(annotation = RequestMapping.class) String[] consumes() default {};
/**
* Alias for {@link RequestMapping#produces}.
*/
@AliasFor(annotation = RequestMapping.class) String[] produces() default {};
/**
* Alias for {@link RequestMapping#produces}.
*/
@AliasFor(annotation = RequestMapping.class) String[] produces() default {};
}
@@ -41,38 +41,38 @@ import org.springframework.web.bind.annotation.RequestMethod;
@RequestMapping(method = RequestMethod.PATCH)
public @interface AnonymousPatchMapping {
/**
* Alias for {@link RequestMapping#name}.
*/
@AliasFor(annotation = RequestMapping.class) String name() default "";
/**
* Alias for {@link RequestMapping#name}.
*/
@AliasFor(annotation = RequestMapping.class) String name() default "";
/**
* Alias for {@link RequestMapping#value}.
*/
@AliasFor(annotation = RequestMapping.class) String[] value() default {};
/**
* Alias for {@link RequestMapping#value}.
*/
@AliasFor(annotation = RequestMapping.class) String[] value() default {};
/**
* Alias for {@link RequestMapping#path}.
*/
@AliasFor(annotation = RequestMapping.class) String[] path() default {};
/**
* Alias for {@link RequestMapping#path}.
*/
@AliasFor(annotation = RequestMapping.class) String[] path() default {};
/**
* Alias for {@link RequestMapping#params}.
*/
@AliasFor(annotation = RequestMapping.class) String[] params() default {};
/**
* Alias for {@link RequestMapping#params}.
*/
@AliasFor(annotation = RequestMapping.class) String[] params() default {};
/**
* Alias for {@link RequestMapping#headers}.
*/
@AliasFor(annotation = RequestMapping.class) String[] headers() default {};
/**
* Alias for {@link RequestMapping#headers}.
*/
@AliasFor(annotation = RequestMapping.class) String[] headers() default {};
/**
* Alias for {@link RequestMapping#consumes}.
*/
@AliasFor(annotation = RequestMapping.class) String[] consumes() default {};
/**
* Alias for {@link RequestMapping#consumes}.
*/
@AliasFor(annotation = RequestMapping.class) String[] consumes() default {};
/**
* Alias for {@link RequestMapping#produces}.
*/
@AliasFor(annotation = RequestMapping.class) String[] produces() default {};
/**
* Alias for {@link RequestMapping#produces}.
*/
@AliasFor(annotation = RequestMapping.class) String[] produces() default {};
}
@@ -41,38 +41,38 @@ import org.springframework.web.bind.annotation.RequestMethod;
@RequestMapping(method = RequestMethod.POST)
public @interface AnonymousPostMapping {
/**
* Alias for {@link RequestMapping#name}.
*/
@AliasFor(annotation = RequestMapping.class) String name() default "";
/**
* Alias for {@link RequestMapping#name}.
*/
@AliasFor(annotation = RequestMapping.class) String name() default "";
/**
* Alias for {@link RequestMapping#value}.
*/
@AliasFor(annotation = RequestMapping.class) String[] value() default {};
/**
* Alias for {@link RequestMapping#value}.
*/
@AliasFor(annotation = RequestMapping.class) String[] value() default {};
/**
* Alias for {@link RequestMapping#path}.
*/
@AliasFor(annotation = RequestMapping.class) String[] path() default {};
/**
* Alias for {@link RequestMapping#path}.
*/
@AliasFor(annotation = RequestMapping.class) String[] path() default {};
/**
* Alias for {@link RequestMapping#params}.
*/
@AliasFor(annotation = RequestMapping.class) String[] params() default {};
/**
* Alias for {@link RequestMapping#params}.
*/
@AliasFor(annotation = RequestMapping.class) String[] params() default {};
/**
* Alias for {@link RequestMapping#headers}.
*/
@AliasFor(annotation = RequestMapping.class) String[] headers() default {};
/**
* Alias for {@link RequestMapping#headers}.
*/
@AliasFor(annotation = RequestMapping.class) String[] headers() default {};
/**
* Alias for {@link RequestMapping#consumes}.
*/
@AliasFor(annotation = RequestMapping.class) String[] consumes() default {};
/**
* Alias for {@link RequestMapping#consumes}.
*/
@AliasFor(annotation = RequestMapping.class) String[] consumes() default {};
/**
* Alias for {@link RequestMapping#produces}.
*/
@AliasFor(annotation = RequestMapping.class) String[] produces() default {};
/**
* Alias for {@link RequestMapping#produces}.
*/
@AliasFor(annotation = RequestMapping.class) String[] produces() default {};
}
@@ -41,38 +41,38 @@ import org.springframework.web.bind.annotation.RequestMethod;
@RequestMapping(method = RequestMethod.PUT)
public @interface AnonymousPutMapping {
/**
* Alias for {@link RequestMapping#name}.
*/
@AliasFor(annotation = RequestMapping.class) String name() default "";
/**
* Alias for {@link RequestMapping#name}.
*/
@AliasFor(annotation = RequestMapping.class) String name() default "";
/**
* Alias for {@link RequestMapping#value}.
*/
@AliasFor(annotation = RequestMapping.class) String[] value() default {};
/**
* Alias for {@link RequestMapping#value}.
*/
@AliasFor(annotation = RequestMapping.class) String[] value() default {};
/**
* Alias for {@link RequestMapping#path}.
*/
@AliasFor(annotation = RequestMapping.class) String[] path() default {};
/**
* Alias for {@link RequestMapping#path}.
*/
@AliasFor(annotation = RequestMapping.class) String[] path() default {};
/**
* Alias for {@link RequestMapping#params}.
*/
@AliasFor(annotation = RequestMapping.class) String[] params() default {};
/**
* Alias for {@link RequestMapping#params}.
*/
@AliasFor(annotation = RequestMapping.class) String[] params() default {};
/**
* Alias for {@link RequestMapping#headers}.
*/
@AliasFor(annotation = RequestMapping.class) String[] headers() default {};
/**
* Alias for {@link RequestMapping#headers}.
*/
@AliasFor(annotation = RequestMapping.class) String[] headers() default {};
/**
* Alias for {@link RequestMapping#consumes}.
*/
@AliasFor(annotation = RequestMapping.class) String[] consumes() default {};
/**
* Alias for {@link RequestMapping#consumes}.
*/
@AliasFor(annotation = RequestMapping.class) String[] consumes() default {};
/**
* Alias for {@link RequestMapping#produces}.
*/
@AliasFor(annotation = RequestMapping.class) String[] produces() default {};
/**
* Alias for {@link RequestMapping#produces}.
*/
@AliasFor(annotation = RequestMapping.class) String[] produces() default {};
}
@@ -27,15 +27,20 @@ import lombok.Setter;
* @date 2025-07-23
*/
public class KitArgs {
@Getter
@Setter
public static class FindByLongId extends KitObject {
@NotNull(message = "id必填") private Long id;
}
@Getter
@Setter
public static class DeleteById extends KitObject {
@NotNull(message = "id必填") private Long id;
}
@Getter
@Setter
public static class FindByLongId extends KitObject {
@NotNull(message = "id必填")
private Long id;
}
@Getter
@Setter
public static class DeleteById extends KitObject {
@NotNull(message = "id必填")
private Long id;
}
}
@@ -28,6 +28,9 @@ import lombok.Setter;
@Setter
@EqualsAndHashCode(callSuper = false)
public class KitBaseCreateTimeTb extends KitObject {
@Schema(name = "创建时间: yyyy-MM-dd HH:mm:ss", hidden = true) @TableField(fill = FieldFill.INSERT) private Date
createTime;
@Schema(name = "创建时间: yyyy-MM-dd HH:mm:ss", hidden = true)
@TableField(fill = FieldFill.INSERT)
private Date
createTime;
}
@@ -28,9 +28,13 @@ import lombok.Setter;
@Setter
@EqualsAndHashCode(callSuper = false)
public class KitBaseTimeTb extends KitObject {
@TableField(fill = FieldFill.INSERT) @Schema(name = "创建时间: yyyy-MM-dd HH:mm:ss", hidden = true) private Date
createTime;
@TableField(fill = FieldFill.INSERT_UPDATE) @Schema(name = "更新时间: yyyy-MM-dd HH:mm:ss", hidden = true)
private Date updateTime;
@TableField(fill = FieldFill.INSERT)
@Schema(name = "创建时间: yyyy-MM-dd HH:mm:ss", hidden = true)
private Date
createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
@Schema(name = "更新时间: yyyy-MM-dd HH:mm:ss", hidden = true)
private Date updateTime;
}
@@ -28,6 +28,8 @@ import lombok.Setter;
@Setter
@EqualsAndHashCode(callSuper = false)
public class KitBaseUpdateTimeTb extends KitObject {
@Schema(name = "更新时间: yyyy-MM-dd HH:mm:ss", hidden = true) @TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
@Schema(name = "更新时间: yyyy-MM-dd HH:mm:ss", hidden = true)
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
}
@@ -32,29 +32,38 @@ import org.springframework.data.annotation.CreatedBy;
@EqualsAndHashCode(callSuper = false)
public class KitBaseUserCreateTimeTb extends KitObject {
@CreatedBy @TableField(fill = FieldFill.INSERT) @Schema(name = "创建人", hidden = true) private String createBy;
@CreatedBy
@TableField(fill = FieldFill.INSERT)
@Schema(name = "创建人", hidden = true)
private String createBy;
@TableField(fill = FieldFill.INSERT) @Schema(name = "创建时间: yyyy-MM-dd HH:mm:ss", hidden = true) private Date
createTime;
@TableField(fill = FieldFill.INSERT)
@Schema(name = "创建时间: yyyy-MM-dd HH:mm:ss", hidden = true)
private Date
createTime;
@Override
public String toString() {
ToStringBuilder builder = new ToStringBuilder(this);
Field[] fields = this.getClass().getDeclaredFields();
try {
for (Field f : fields) {
f.setAccessible(true);
builder.append(f.getName(), f.get(this)).append("\n");
}
} catch (Exception e) {
builder.append("toString builder encounter an error");
}
return builder.toString();
@Override
public String toString() {
ToStringBuilder builder = new ToStringBuilder(this);
Field[] fields = this.getClass().getDeclaredFields();
try {
for (Field f : fields) {
f.setAccessible(true);
builder.append(f.getName(), f.get(this)).append("\n");
}
} catch (Exception e) {
builder.append("toString builder encounter an error");
}
return builder.toString();
}
/* 分组校验 */
public @interface Create {}
/* 分组校验 */
public @interface Create {
/* 分组校验 */
public @interface Update {}
}
/* 分组校验 */
public @interface Update {
}
}
@@ -35,13 +35,24 @@ import org.springframework.data.annotation.LastModifiedBy;
@Getter
@Setter
public class KitBaseUserTimeLogicTb extends KitObject {
@CreatedBy @TableField(value = "create_by", fill = FieldFill.INSERT) @Schema(name = "创建人", hidden = true)
private String createBy;
@LastModifiedBy @TableField(value = "update_by", fill = FieldFill.INSERT_UPDATE)
@Schema(name = "更新人", hidden = true) private String updateBy;
@TableField(value = "create_time", fill = FieldFill.INSERT) @Schema(name = "创建时间", hidden = true) private Date
createTime;
@TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE) @Schema(name = "更新时间", hidden = true)
private Date updateTime;
@Schema(name = "数据有效性", hidden = true) @TableLogic @TableField("available") private Integer available;
@CreatedBy
@TableField(value = "create_by", fill = FieldFill.INSERT)
@Schema(name = "创建人", hidden = true)
private String createBy;
@LastModifiedBy
@TableField(value = "update_by", fill = FieldFill.INSERT_UPDATE)
@Schema(name = "更新人", hidden = true)
private String updateBy;
@TableField(value = "create_time", fill = FieldFill.INSERT)
@Schema(name = "创建时间", hidden = true)
private Date
createTime;
@TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
@Schema(name = "更新时间", hidden = true)
private Date updateTime;
@Schema(name = "数据有效性", hidden = true)
@TableLogic
@TableField("available")
private Integer available;
}
@@ -33,35 +33,48 @@ import org.springframework.data.annotation.LastModifiedBy;
@EqualsAndHashCode(callSuper = false)
public class KitBaseUserTimeTb extends KitObject {
@CreatedBy @TableField(fill = FieldFill.INSERT) @Schema(name = "创建人", hidden = true) private String createBy;
@CreatedBy
@TableField(fill = FieldFill.INSERT)
@Schema(name = "创建人", hidden = true)
private String createBy;
@LastModifiedBy @TableField(fill = FieldFill.INSERT_UPDATE) @Schema(name = "更新人", hidden = true) private String
updateBy;
@LastModifiedBy
@TableField(fill = FieldFill.INSERT_UPDATE)
@Schema(name = "更新人", hidden = true)
private String
updateBy;
@TableField(fill = FieldFill.INSERT) @Schema(name = "创建时间: yyyy-MM-dd HH:mm:ss", hidden = true) private Date
createTime;
@TableField(fill = FieldFill.INSERT)
@Schema(name = "创建时间: yyyy-MM-dd HH:mm:ss", hidden = true)
private Date
createTime;
@TableField(fill = FieldFill.INSERT_UPDATE) @Schema(name = "更新时间: yyyy-MM-dd HH:mm:ss", hidden = true)
private Date updateTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
@Schema(name = "更新时间: yyyy-MM-dd HH:mm:ss", hidden = true)
private Date updateTime;
@Override
public String toString() {
ToStringBuilder builder = new ToStringBuilder(this);
Field[] fields = this.getClass().getDeclaredFields();
try {
for (Field f : fields) {
f.setAccessible(true);
builder.append(f.getName(), f.get(this)).append("\n");
}
} catch (Exception e) {
builder.append("toString builder encounter an error");
}
return builder.toString();
@Override
public String toString() {
ToStringBuilder builder = new ToStringBuilder(this);
Field[] fields = this.getClass().getDeclaredFields();
try {
for (Field f : fields) {
f.setAccessible(true);
builder.append(f.getName(), f.get(this)).append("\n");
}
} catch (Exception e) {
builder.append("toString builder encounter an error");
}
return builder.toString();
}
/* 分组校验 */
public @interface Create {}
/* 分组校验 */
public @interface Create {
/* 分组校验 */
public @interface Update {}
}
/* 分组校验 */
public @interface Update {
}
}
@@ -26,5 +26,7 @@ import java.io.Serializable;
* @date 2022-01-12
*/
public class KitObject implements Serializable {
@Serial private static final long serialVersionUID = 1L;
@Serial
private static final long serialVersionUID = 1L;
}
@@ -34,9 +34,14 @@ import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
public class KitPageArgs<T> implements Serializable {
@NotNull(message = "参数page不能为空") @Min(value = 1, message = "参数page最小值为1")
@Schema(name = "页码", example = "1") private Integer page;
@NotNull(message = "参数size不能为空") @Min(value = 1, message = "参数size最小值为1")
@Schema(name = "每页数据量", example = "10") private Integer size;
private T args;
@NotNull(message = "参数page不能为空")
@Min(value = 1, message = "参数page最小值为1")
@Schema(name = "页码", example = "1")
private Integer page;
@NotNull(message = "参数size不能为空")
@Min(value = 1, message = "参数size最小值为1")
@Schema(name = "每页数据量", example = "10")
private Integer size;
private T args;
}
@@ -32,20 +32,21 @@ import lombok.NoArgsConstructor;
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class KitPageResult<T> extends KitObject {
private List<T> content;
private long totalElements;
public static <T> KitPageResult<T> emptyListData() {
KitPageResult<T> result = new KitPageResult<>();
result.setTotalElements(0);
result.setContent(new ArrayList<>());
return result;
}
private List<T> content;
private long totalElements;
public static <T> KitPageResult<T> listData(IPage<T> page) {
KitPageResult<T> result = new KitPageResult<>();
result.setTotalElements(page.getTotal());
result.setContent(page.getRecords());
return result;
}
public static <T> KitPageResult<T> emptyListData() {
KitPageResult<T> result = new KitPageResult<>();
result.setTotalElements(0);
result.setContent(new ArrayList<>());
return result;
}
public static <T> KitPageResult<T> listData(IPage<T> page) {
KitPageResult<T> result = new KitPageResult<>();
result.setTotalElements(page.getTotal());
result.setContent(page.getRecords());
return result;
}
}
@@ -35,7 +35,8 @@ import lombok.NoArgsConstructor;
@NoArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class KitSelectOptionVo extends KitObject {
private String label;
private String value;
private Map<String, Object> ext;
private String label;
private String value;
private Map<String, Object> ext;
}
@@ -20,24 +20,24 @@ package cn.odboy.constant;
* 验证码配置枚举
*/
public enum CaptchaCodeEnum {
/**
* 算数
*/
ARITHMETIC,
/**
* 中文
*/
CHINESE,
/**
* 中文闪图
*/
CHINESE_GIF,
/**
* 闪图
*/
GIF,
/**
* 静态
*/
SPEC
/**
* 算数
*/
ARITHMETIC,
/**
* 中文
*/
CHINESE,
/**
* 中文闪图
*/
CHINESE_GIF,
/**
* 闪图
*/
GIF,
/**
* 静态
*/
SPEC
}
@@ -22,9 +22,9 @@ import lombok.Getter;
@Getter
@AllArgsConstructor
public enum FileTypeEnum {
/* 文件类型 */
IMAGE("image", "图片"), DOC("doc", "文档"), VOICE("voice", "音频"), VIDEO("video", "视频"), OTHER("other", "其他");
/* 文件类型 */
IMAGE("image", "图片"), DOC("doc", "文档"), VOICE("voice", "音频"), VIDEO("video", "视频"), OTHER("other", "其他");
private final String code;
private final String description;
private final String code;
private final String description;
}
@@ -23,47 +23,47 @@ import lombok.Getter;
@AllArgsConstructor
public enum RequestMethodEnum {
/**
* 搜寻 @AnonymousGetMapping
*/
GET("GET"),
/**
* 搜寻 @AnonymousGetMapping
*/
GET("GET"),
/**
* 搜寻 @AnonymousPostMapping
*/
POST("POST"),
/**
* 搜寻 @AnonymousPostMapping
*/
POST("POST"),
/**
* 搜寻 @AnonymousPutMapping
*/
PUT("PUT"),
/**
* 搜寻 @AnonymousPutMapping
*/
PUT("PUT"),
/**
* 搜寻 @AnonymousPatchMapping
*/
PATCH("PATCH"),
/**
* 搜寻 @AnonymousPatchMapping
*/
PATCH("PATCH"),
/**
* 搜寻 @AnonymousDeleteMapping
*/
DELETE("DELETE"),
/**
* 搜寻 @AnonymousDeleteMapping
*/
DELETE("DELETE"),
/**
* 否则就是所有 Request 接口都放行
*/
ALL("All");
/**
* 否则就是所有 Request 接口都放行
*/
ALL("All");
/**
* Request 类型
*/
private final String type;
/**
* Request 类型
*/
private final String type;
public static RequestMethodEnum find(String type) {
for (RequestMethodEnum value : RequestMethodEnum.values()) {
if (value.getType().equals(type)) {
return value;
}
}
return ALL;
public static RequestMethodEnum find(String type) {
for (RequestMethodEnum value : RequestMethodEnum.values()) {
if (value.getType().equals(type)) {
return value;
}
}
return ALL;
}
}
@@ -20,34 +20,35 @@ package cn.odboy.constant;
* 常用静态常量
*/
public interface SystemConst {
/**
* win 系统
*/
String WIN = "win";
/**
* mac 系统
*/
String MAC = "mac";
/**
* win 系统
*/
String WIN = "win";
/**
* 请求头名称
*/
String HEADER_NAME = "Authorization";
/**
* mac 系统
*/
String MAC = "mac";
/**
* Token前缀
*/
String TOKEN_PREFIX = "Bearer";
/**
* 请求头名称
*/
String HEADER_NAME = "Authorization";
/**
* 小数点
*/
String SYMBOL_DOT = ".";
String SYMBOL_EQUAL = "=";
String SYMBOL_ADD = "+";
String SYMBOL_SUBTRACT = "-";
String SYMBOL_AT = "@";
String PROPERTY_OS_NAME = "os.name";
String OS_NAME_WINDOWS = "Windows";
/**
* Token前缀
*/
String TOKEN_PREFIX = "Bearer";
/**
* 小数点
*/
String SYMBOL_DOT = ".";
String SYMBOL_EQUAL = "=";
String SYMBOL_ADD = "+";
String SYMBOL_SUBTRACT = "-";
String SYMBOL_AT = "@";
String PROPERTY_OS_NAME = "os.name";
String OS_NAME_WINDOWS = "Windows";
}
@@ -30,8 +30,9 @@ import org.springframework.stereotype.Component;
@Slf4j
@Component
public class BeanLoadFinishListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
log.info("所有的Bean都加载完毕了");
}
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
log.info("所有的Bean都加载完毕了");
}
}
@@ -32,23 +32,24 @@ import org.springframework.core.env.Environment;
*/
@Slf4j
public class BootApplication {
protected static void inited(ConfigurableApplicationContext application) throws UnknownHostException {
Environment env = application.getEnvironment();
String ip = InetAddress.getLocalHost().getHostAddress();
String port = env.getProperty("server.port");
String path = StrUtil.isEmpty(env.getProperty("config.servlet.context-path")) ? ""
: env.getProperty("config.servlet.context-path");
log.info("""
----------------------------------------------------------
\tApplication is running! Access URLs:
\tLocal: \t\thttp://localhost:{}{}
\tExternal: \thttp://{}:{}{}
\tSwagger文档: \thttp://{}:{}{}/doc.html
----------------------------------------------------------""", port, path, ip, port, path, ip, port, path);
}
@Bean
public KitSpringBeanHolder springBeanHolder() {
return new KitSpringBeanHolder();
}
protected static void inited(ConfigurableApplicationContext application) throws UnknownHostException {
Environment env = application.getEnvironment();
String ip = InetAddress.getLocalHost().getHostAddress();
String port = env.getProperty("server.port");
String path = StrUtil.isEmpty(env.getProperty("config.servlet.context-path")) ? ""
: env.getProperty("config.servlet.context-path");
log.info("""
----------------------------------------------------------
\tApplication is running! Access URLs:
\tLocal: \t\thttp://localhost:{}{}
\tExternal: \thttp://{}:{}{}
\tSwagger文档: \thttp://{}:{}{}/doc.html
----------------------------------------------------------""", port, path, ip, port, path, ip, port, path);
}
@Bean
public KitSpringBeanHolder springBeanHolder() {
return new KitSpringBeanHolder();
}
}
@@ -27,8 +27,8 @@ import org.springframework.web.context.request.ServletRequestAttributes;
*/
public class KitRequestHolder {
public static HttpServletRequest getHttpServletRequest() {
return ((ServletRequestAttributes)Objects.requireNonNull(
RequestContextHolder.getRequestAttributes())).getRequest();
}
public static HttpServletRequest getHttpServletRequest() {
return ((ServletRequestAttributes) Objects.requireNonNull(
RequestContextHolder.getRequestAttributes())).getRequest();
}
}
@@ -31,32 +31,33 @@ import org.springframework.stereotype.Component;
@Slf4j
@Component
public class KitScriptHelper {
public Object evalObj(String script, Map<String, Object> args) {
try (Context rhino = Context.enter()) {
Scriptable scope = rhino.initStandardObjects();
bindArgumentsToScope(scope, args);
Object result = rhino.evaluateString(scope, script, "JavaScript", 1, null);
log.info("执行结果: {}", Context.toString(result));
return result;
}
}
private void bindArgumentsToScope(Scriptable scope, Map<?, ?> args) {
if (args == null) {
return;
}
for (Map.Entry<?, ?> entry : args.entrySet()) {
Object jsValue = Context.javaToJS(entry.getValue(), scope);
scope.put(entry.getKey().toString(), scope, jsValue);
}
public Object evalObj(String script, Map<String, Object> args) {
try (Context rhino = Context.enter()) {
Scriptable scope = rhino.initStandardObjects();
bindArgumentsToScope(scope, args);
Object result = rhino.evaluateString(scope, script, "JavaScript", 1, null);
log.info("执行结果: {}", Context.toString(result));
return result;
}
}
private void bindArgumentsToScope(Scriptable scope, Object args) {
if (args == null) {
return;
}
// 单个对象参数
Object jsValue = Context.javaToJS(args, scope);
scope.put("args", scope, jsValue);
private void bindArgumentsToScope(Scriptable scope, Map<?, ?> args) {
if (args == null) {
return;
}
for (Map.Entry<?, ?> entry : args.entrySet()) {
Object jsValue = Context.javaToJS(entry.getValue(), scope);
scope.put(entry.getKey().toString(), scope, jsValue);
}
}
private void bindArgumentsToScope(Scriptable scope, Object args) {
if (args == null) {
return;
}
// 单个对象参数
Object jsValue = Context.javaToJS(args, scope);
scope.put("args", scope, jsValue);
}
}
@@ -31,140 +31,140 @@ import org.springframework.stereotype.Service;
@SuppressWarnings({"unchecked", "all"})
public class KitSpringBeanHolder implements ApplicationContextAware, DisposableBean {
private static final List<CallBack> CALL_BACKS = new ArrayList<>();
private static ApplicationContext applicationContext = null;
private static boolean addCallback = true;
private static final List<CallBack> CALL_BACKS = new ArrayList<>();
private static ApplicationContext applicationContext = null;
private static boolean addCallback = true;
/**
* 针对 某些初始化方法, 在SpringContextHolder 未初始化时 提交回调方法。 在SpringContextHolder 初始化后, 进行回调使用
*
* @param callBack 回调函数
*/
public synchronized static void addCallBacks(CallBack callBack) {
if (addCallback) {
KitSpringBeanHolder.CALL_BACKS.add(callBack);
} else {
log.warn("CallBack:{} 已无法添加!立即执行", callBack.getCallBackName());
callBack.executor();
}
/**
* 针对 某些初始化方法, 在SpringContextHolder 未初始化时 提交回调方法。 在SpringContextHolder 初始化后, 进行回调使用
*
* @param callBack 回调函数
*/
public synchronized static void addCallBacks(CallBack callBack) {
if (addCallback) {
KitSpringBeanHolder.CALL_BACKS.add(callBack);
} else {
log.warn("CallBack:{} 已无法添加!立即执行", callBack.getCallBackName());
callBack.executor();
}
}
/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
public static <T> T getBean(String name) {
assertContextInjected();
return (T)applicationContext.getBean(name);
/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
public static <T> T getBean(String name) {
assertContextInjected();
return (T) applicationContext.getBean(name);
}
/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
public static <T> T getBean(Class<T> requiredType) {
assertContextInjected();
return applicationContext.getBean(requiredType);
}
/**
* 获取SpringBoot 配置信息
*
* @param property 属性key
* @param defaultValue 默认值
* @param requiredType 返回类型
* @return /
*/
public static <T> T getProperties(String property, T defaultValue, Class<T> requiredType) {
T result = defaultValue;
try {
result = getBean(Environment.class).getProperty(property, requiredType);
} catch (Exception ignored) {
}
return result;
}
/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
public static <T> T getBean(Class<T> requiredType) {
assertContextInjected();
return applicationContext.getBean(requiredType);
/**
* 获取SpringBoot 配置信息
*
* @param property 属性key
* @return /
*/
public static String getProperties(String property) {
return getProperties(property, null, String.class);
}
/**
* 获取SpringBoot 配置信息
*
* @param property 属性key
* @param requiredType 返回类型
* @return /
*/
public static <T> T getProperties(String property, Class<T> requiredType) {
return getProperties(property, null, requiredType);
}
/**
* 检查ApplicationContext不为空.
*/
private static void assertContextInjected() {
if (applicationContext == null) {
throw new IllegalStateException("applicaitonContext属性未注入, 请在applicationContext" +
".xml中定义SpringContextHolder或在SpringBoot启动类中注册SpringContextHolder.");
}
}
/**
* 获取SpringBoot 配置信息
*
* @param property 属性key
* @param defaultValue 默认值
* @param requiredType 返回类型
* @return /
*/
public static <T> T getProperties(String property, T defaultValue, Class<T> requiredType) {
T result = defaultValue;
try {
result = getBean(Environment.class).getProperty(property, requiredType);
} catch (Exception ignored) {
}
return result;
/**
* 清除SpringContextHolder中的ApplicationContext为Null.
*/
private static void clearHolder() {
log.debug("清除SpringContextHolder中的ApplicationContext:" + applicationContext);
applicationContext = null;
}
/**
* 获取 @Service 的所有 bean 名称
*
* @return /
*/
public static List<String> getAllServiceBeanName() {
return new ArrayList<>(Arrays.asList(applicationContext.getBeanNamesForAnnotation(Service.class)));
}
@Override
public void destroy() {
KitSpringBeanHolder.clearHolder();
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (KitSpringBeanHolder.applicationContext != null) {
log.warn("SpringContextHolder中的ApplicationContext被覆盖, 原有ApplicationContext为:" +
KitSpringBeanHolder.applicationContext);
}
/**
* 获取SpringBoot 配置信息
*
* @param property 属性key
* @return /
*/
public static String getProperties(String property) {
return getProperties(property, null, String.class);
KitSpringBeanHolder.applicationContext = applicationContext;
if (addCallback) {
for (CallBack callBack : KitSpringBeanHolder.CALL_BACKS) {
callBack.executor();
}
CALL_BACKS.clear();
}
KitSpringBeanHolder.addCallback = false;
}
interface CallBack {
/**
* 获取SpringBoot 配置信息
*
* @param property 属性key
* @param requiredType 返回类型
* @return /
* 回调执行方法
*/
public static <T> T getProperties(String property, Class<T> requiredType) {
return getProperties(property, null, requiredType);
}
void executor();
/**
* 检查ApplicationContext不为空.
*/
private static void assertContextInjected() {
if (applicationContext == null) {
throw new IllegalStateException("applicaitonContext属性未注入, 请在applicationContext" +
".xml中定义SpringContextHolder或在SpringBoot启动类中注册SpringContextHolder.");
}
}
/**
* 清除SpringContextHolder中的ApplicationContext为Null.
*/
private static void clearHolder() {
log.debug("清除SpringContextHolder中的ApplicationContext:" + applicationContext);
applicationContext = null;
}
/**
* 获取 @Service 的所有 bean 名称
* 本回调任务名称
*
* @return /
*/
public static List<String> getAllServiceBeanName() {
return new ArrayList<>(Arrays.asList(applicationContext.getBeanNamesForAnnotation(Service.class)));
}
@Override
public void destroy() {
KitSpringBeanHolder.clearHolder();
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (KitSpringBeanHolder.applicationContext != null) {
log.warn("SpringContextHolder中的ApplicationContext被覆盖, 原有ApplicationContext为:" +
KitSpringBeanHolder.applicationContext);
}
KitSpringBeanHolder.applicationContext = applicationContext;
if (addCallback) {
for (CallBack callBack : KitSpringBeanHolder.CALL_BACKS) {
callBack.executor();
}
CALL_BACKS.clear();
}
KitSpringBeanHolder.addCallback = false;
}
interface CallBack {
/**
* 回调执行方法
*/
void executor();
/**
* 本回调任务名称
*
* @return /
*/
default String getCallBackName() {
return Thread.currentThread().getId() + ":" + this.getClass().getName();
}
default String getCallBackName() {
return Thread.currentThread().getId() + ":" + this.getClass().getName();
}
}
}
@@ -16,30 +16,30 @@
package cn.odboy.framework.exception;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
import lombok.Getter;
import org.springframework.http.HttpStatus;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
/**
* 统一异常处理
*/
@Getter
public class BadRequestException extends RuntimeException {
private Integer status = BAD_REQUEST.value();
private Integer status = BAD_REQUEST.value();
public BadRequestException(String msg) {
super(msg);
}
public BadRequestException(String msg) {
super(msg);
}
public BadRequestException(HttpStatus status, String msg) {
super(msg);
this.status = status.value();
}
public BadRequestException(HttpStatus status, String msg) {
super(msg);
this.status = status.value();
}
public BadRequestException(Throwable cause) {
super(cause.getMessage());
this.status = BAD_REQUEST.value();
}
public BadRequestException(Throwable cause) {
super(cause.getMessage());
this.status = BAD_REQUEST.value();
}
}
@@ -16,27 +16,27 @@
package cn.odboy.framework.exception;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
import lombok.Getter;
import org.springframework.http.HttpStatus;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
@Getter
public class ServerException extends RuntimeException {
private Integer status = INTERNAL_SERVER_ERROR.value();
private Integer status = INTERNAL_SERVER_ERROR.value();
public ServerException(String msg) {
super(msg);
}
public ServerException(String msg) {
super(msg);
}
public ServerException(HttpStatus status, String msg) {
super(msg);
this.status = status.value();
}
public ServerException(HttpStatus status, String msg) {
super(msg);
this.status = status.value();
}
public ServerException(Throwable cause) {
super(cause.getMessage());
this.status = INTERNAL_SERVER_ERROR.value();
}
public ServerException(Throwable cause) {
super(cause.getMessage());
this.status = INTERNAL_SERVER_ERROR.value();
}
}
@@ -21,26 +21,26 @@ import lombok.Data;
@Data
public final class ApiError {
private Integer status = 400;
private Long timestamp;
private String message;
private Integer status = 400;
private Long timestamp;
private String message;
private ApiError() {
timestamp = System.currentTimeMillis();
}
private ApiError() {
timestamp = System.currentTimeMillis();
}
public static ApiError error(final String message) {
ApiError apiError = new ApiError();
apiError.setMessage(message);
return apiError;
}
public static ApiError error(final String message) {
ApiError apiError = new ApiError();
apiError.setMessage(message);
return apiError;
}
public static ApiError error(final Integer status, final String message) {
ApiError apiError = new ApiError();
apiError.setStatus(status);
apiError.setMessage(message);
return apiError;
}
public static ApiError error(final Integer status, final String message) {
ApiError apiError = new ApiError();
apiError.setStatus(status);
apiError.setMessage(message);
return apiError;
}
}
@@ -37,56 +37,56 @@ import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理所有不可知的异常
*/
@ExceptionHandler(Throwable.class)
public ResponseEntity<ApiError> handleException(Throwable e) {
// 打印堆栈信息
log.error(ExceptionUtil.stacktraceToString(e));
return buildResponseEntity(ApiError.error(e.getMessage()));
}
/**
* 处理所有不可知的异常
*/
@ExceptionHandler(Throwable.class)
public ResponseEntity<ApiError> handleException(Throwable e) {
// 打印堆栈信息
log.error(ExceptionUtil.stacktraceToString(e));
return buildResponseEntity(ApiError.error(e.getMessage()));
}
/**
* BadCredentialsException
*/
@ExceptionHandler(BadCredentialsException.class)
public ResponseEntity<ApiError> badCredentialsException(BadCredentialsException e) {
// 打印堆栈信息
String message = "坏的凭证".equals(e.getMessage()) ? "用户名或密码不正确" : e.getMessage();
log.error(message);
return buildResponseEntity(ApiError.error(message));
}
/**
* BadCredentialsException
*/
@ExceptionHandler(BadCredentialsException.class)
public ResponseEntity<ApiError> badCredentialsException(BadCredentialsException e) {
// 打印堆栈信息
String message = "坏的凭证".equals(e.getMessage()) ? "用户名或密码不正确" : e.getMessage();
log.error(message);
return buildResponseEntity(ApiError.error(message));
}
/**
* 处理自定义异常
*/
@ExceptionHandler(value = BadRequestException.class)
public ResponseEntity<ApiError> badRequestException(BadRequestException e) {
// 打印堆栈信息
log.error(ExceptionUtil.stacktraceToString(e));
return buildResponseEntity(ApiError.error(e.getStatus(), e.getMessage()));
}
/**
* 处理自定义异常
*/
@ExceptionHandler(value = BadRequestException.class)
public ResponseEntity<ApiError> badRequestException(BadRequestException e) {
// 打印堆栈信息
log.error(ExceptionUtil.stacktraceToString(e));
return buildResponseEntity(ApiError.error(e.getStatus(), e.getMessage()));
}
/**
* 处理所有接口数据验证异常
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ApiError> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
// 打印堆栈信息
log.error(ExceptionUtil.stacktraceToString(e));
ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
String message = objectError.getDefaultMessage();
if (objectError instanceof FieldError fieldError) {
message = fieldError.getField() + ": " + message;
}
return buildResponseEntity(ApiError.error(message));
/**
* 处理所有接口数据验证异常
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ApiError> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
// 打印堆栈信息
log.error(ExceptionUtil.stacktraceToString(e));
ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
String message = objectError.getDefaultMessage();
if (objectError instanceof FieldError fieldError) {
message = fieldError.getField() + ": " + message;
}
return buildResponseEntity(ApiError.error(message));
}
/**
* 统一返回
*/
private ResponseEntity<ApiError> buildResponseEntity(ApiError apiError) {
return new ResponseEntity<>(apiError, HttpStatus.valueOf(apiError.getStatus()));
}
/**
* 统一返回
*/
private ResponseEntity<ApiError> buildResponseEntity(ApiError apiError) {
return new ResponseEntity<>(apiError, HttpStatus.valueOf(apiError.getStatus()));
}
}
@@ -33,13 +33,14 @@ import java.lang.annotation.Target;
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ExecutionTimeStatisticsFace {
/**
* 接口描述
*/
String description();
/**
* 是否展示异常日志
*/
boolean showErrLog() default true;
/**
* 接口描述
*/
String description();
/**
* 是否展示异常日志
*/
boolean showErrLog() default true;
}
@@ -33,21 +33,22 @@ import org.springframework.stereotype.Component;
@Aspect
@Component
public class ExecutionTimeStatisticsFaceAspect {
@Around("@annotation(executionTimeStatisticsFace)")
public Object aroundFace(ProceedingJoinPoint joinPoint, ExecutionTimeStatisticsFace executionTimeStatisticsFace)
throws Throwable {
TimeInterval timeInterval = new TimeInterval();
try {
Object proceed = joinPoint.proceed();
log.info("[{}] 接口执行成功,耗时: {} ms", executionTimeStatisticsFace.description(),
timeInterval.intervalMs());
return proceed;
} catch (Exception e) {
if (executionTimeStatisticsFace.showErrLog()) {
log.error("[{}] 接口执行失败,耗时: {} ms", executionTimeStatisticsFace.description(),
timeInterval.intervalMs(), e);
}
throw e;
}
@Around("@annotation(executionTimeStatisticsFace)")
public Object aroundFace(ProceedingJoinPoint joinPoint, ExecutionTimeStatisticsFace executionTimeStatisticsFace)
throws Throwable {
TimeInterval timeInterval = new TimeInterval();
try {
Object proceed = joinPoint.proceed();
log.info("[{}] 接口执行成功,耗时: {} ms", executionTimeStatisticsFace.description(),
timeInterval.intervalMs());
return proceed;
} catch (Exception e) {
if (executionTimeStatisticsFace.showErrLog()) {
log.error("[{}] 接口执行失败,耗时: {} ms", executionTimeStatisticsFace.description(),
timeInterval.intervalMs(), e);
}
throw e;
}
}
}
@@ -31,29 +31,31 @@ import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/healthCheck")
@Tag(name = "系统:健康检查接口")
public class HealthCheckController {
@Autowired private KitHealthCheckPointService kitHealthCheckPointService;
/**
* 就绪检查
*/
@AnonymousGetMapping(value = "/readiness")
public ResponseEntity<?> doReadiness() {
return kitHealthCheckPointService.doReadiness();
}
@Autowired
private KitHealthCheckPointService kitHealthCheckPointService;
/**
* 存活检查
*/
@AnonymousGetMapping(value = "/liveness")
public ResponseEntity<?> doLiveness() {
return kitHealthCheckPointService.doLiveness();
}
/**
* 就绪检查
*/
@AnonymousGetMapping(value = "/readiness")
public ResponseEntity<?> doReadiness() {
return kitHealthCheckPointService.doReadiness();
}
// /**
// * 访问首页提示
// */
// @AnonymousGetMapping("/")
// public String index() {
// return "success";
// }
/**
* 存活检查
*/
@AnonymousGetMapping(value = "/liveness")
public ResponseEntity<?> doLiveness() {
return kitHealthCheckPointService.doLiveness();
}
// /**
// * 访问首页提示
// */
// @AnonymousGetMapping("/")
// public String index() {
// return "success";
// }
}
@@ -19,13 +19,14 @@ package cn.odboy.framework.monitor.service;
import org.springframework.http.ResponseEntity;
public interface KitHealthCheckPointService {
/**
* 就绪检查
*/
ResponseEntity<?> doReadiness();
/**
* 存活检查
*/
ResponseEntity<?> doLiveness();
/**
* 就绪检查
*/
ResponseEntity<?> doReadiness();
/**
* 存活检查
*/
ResponseEntity<?> doLiveness();
}
@@ -25,129 +25,129 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
@SuppressWarnings({"unchecked", "all"})
public enum DataTypeEnum {
/**
* mysql
*/
MYSQL("mysql", "mysql", "com.mysql.jdbc.Driver", "`", "`", "'", "'"),
/**
* oracle
*/
ORACLE("oracle", "oracle", "oracle.jdbc.driver.OracleDriver", "\"", "\"", "\"", "\""),
/**
* sql config
*/
SQLSERVER("sqlserver", "sqlserver", "com.microsoft.sqlserver.jdbc.SQLServerDriver", "\"", "\"", "\"", "\""),
/**
* h2
*/
H2("h2", "h2", "org.h2.Driver", "`", "`", "\"", "\""),
/**
* phoenix
*/
PHOENIX("phoenix", "hbase phoenix", "org.apache.phoenix.jdbc.PhoenixDriver", "", "", "\"", "\""),
/**
* mongo
*/
MONGODB("mongo", "mongodb", "mongodb.jdbc.MongoDriver", "`", "`", "\"", "\""),
/**
* sql4es
*/
ELASTICSEARCH("sql4es", "elasticsearch", "nl.anchormen.sql4es.jdbc.ESDriver", "", "", "'", "'"),
/**
* presto
*/
PRESTO("presto", "presto", "com.facebook.presto.jdbc.PrestoDriver", "", "", "\"", "\""),
/**
* moonbox
*/
MOONBOX("moonbox", "moonbox", "moonbox.jdbc.MbDriver", "`", "`", "`", "`"),
/**
* cassandra
*/
CASSANDRA("cassandra", "cassandra", "com.github.adejanovski.cassandra.jdbc.CassandraDriver", "", "", "'", "'"),
/**
* click house
*/
CLICKHOUSE("clickhouse", "clickhouse", "ru.yandex.clickhouse.ClickHouseDriver", "", "", "\"", "\""),
/**
* kylin
*/
KYLIN("kylin", "kylin", "org.apache.kylin.jdbc.Driver", "\"", "\"", "\"", "\""),
/**
* vertica
*/
VERTICA("vertica", "vertica", "com.vertica.jdbc.Driver", "", "", "'", "'"),
/**
* sap
*/
HANA("sap", "sap hana", "com.sap.db.jdbc.Driver", "", "", "'", "'"),
/**
* impala
*/
IMPALA("impala", "impala", "com.cloudera.impala.jdbc41.Driver", "", "", "'", "'");
private static final String JDBC_URL_PREFIX = "jdbc:";
private String feature;
private String desc;
private String driver;
private String keywordPrefix;
private String keywordSuffix;
private String aliasPrefix;
private String aliasSuffix;
/**
* mysql
*/
MYSQL("mysql", "mysql", "com.mysql.jdbc.Driver", "`", "`", "'", "'"),
/**
* oracle
*/
ORACLE("oracle", "oracle", "oracle.jdbc.driver.OracleDriver", "\"", "\"", "\"", "\""),
/**
* sql config
*/
SQLSERVER("sqlserver", "sqlserver", "com.microsoft.sqlserver.jdbc.SQLServerDriver", "\"", "\"", "\"", "\""),
/**
* h2
*/
H2("h2", "h2", "org.h2.Driver", "`", "`", "\"", "\""),
/**
* phoenix
*/
PHOENIX("phoenix", "hbase phoenix", "org.apache.phoenix.jdbc.PhoenixDriver", "", "", "\"", "\""),
/**
* mongo
*/
MONGODB("mongo", "mongodb", "mongodb.jdbc.MongoDriver", "`", "`", "\"", "\""),
/**
* sql4es
*/
ELASTICSEARCH("sql4es", "elasticsearch", "nl.anchormen.sql4es.jdbc.ESDriver", "", "", "'", "'"),
/**
* presto
*/
PRESTO("presto", "presto", "com.facebook.presto.jdbc.PrestoDriver", "", "", "\"", "\""),
/**
* moonbox
*/
MOONBOX("moonbox", "moonbox", "moonbox.jdbc.MbDriver", "`", "`", "`", "`"),
/**
* cassandra
*/
CASSANDRA("cassandra", "cassandra", "com.github.adejanovski.cassandra.jdbc.CassandraDriver", "", "", "'", "'"),
/**
* click house
*/
CLICKHOUSE("clickhouse", "clickhouse", "ru.yandex.clickhouse.ClickHouseDriver", "", "", "\"", "\""),
/**
* kylin
*/
KYLIN("kylin", "kylin", "org.apache.kylin.jdbc.Driver", "\"", "\"", "\"", "\""),
/**
* vertica
*/
VERTICA("vertica", "vertica", "com.vertica.jdbc.Driver", "", "", "'", "'"),
/**
* sap
*/
HANA("sap", "sap hana", "com.sap.db.jdbc.Driver", "", "", "'", "'"),
/**
* impala
*/
IMPALA("impala", "impala", "com.cloudera.impala.jdbc41.Driver", "", "", "'", "'");
private static final String JDBC_URL_PREFIX = "jdbc:";
private String feature;
private String desc;
private String driver;
private String keywordPrefix;
private String keywordSuffix;
private String aliasPrefix;
private String aliasSuffix;
DataTypeEnum(String feature, String desc, String driver, String keywordPrefix, String keywordSuffix,
String aliasPrefix, String aliasSuffix) {
this.feature = feature;
this.desc = desc;
this.driver = driver;
this.keywordPrefix = keywordPrefix;
this.keywordSuffix = keywordSuffix;
this.aliasPrefix = aliasPrefix;
this.aliasSuffix = aliasSuffix;
}
DataTypeEnum(String feature, String desc, String driver, String keywordPrefix, String keywordSuffix,
String aliasPrefix, String aliasSuffix) {
this.feature = feature;
this.desc = desc;
this.driver = driver;
this.keywordPrefix = keywordPrefix;
this.keywordSuffix = keywordSuffix;
this.aliasPrefix = aliasPrefix;
this.aliasSuffix = aliasSuffix;
}
public static DataTypeEnum urlOf(String jdbcUrl) {
String url = jdbcUrl.toLowerCase().trim();
for (DataTypeEnum dataTypeEnum : values()) {
if (url.startsWith(JDBC_URL_PREFIX + dataTypeEnum.feature)) {
try {
Class<?> aClass = Class.forName(dataTypeEnum.getDriver());
if (null == aClass) {
throw new BadRequestException("Unable to get driver instance for jdbcUrl: " + jdbcUrl);
}
} catch (ClassNotFoundException e) {
throw new BadRequestException("Unable to get driver instance: " + jdbcUrl);
}
return dataTypeEnum;
}
public static DataTypeEnum urlOf(String jdbcUrl) {
String url = jdbcUrl.toLowerCase().trim();
for (DataTypeEnum dataTypeEnum : values()) {
if (url.startsWith(JDBC_URL_PREFIX + dataTypeEnum.feature)) {
try {
Class<?> aClass = Class.forName(dataTypeEnum.getDriver());
if (null == aClass) {
throw new BadRequestException("Unable to get driver instance for jdbcUrl: " + jdbcUrl);
}
} catch (ClassNotFoundException e) {
throw new BadRequestException("Unable to get driver instance: " + jdbcUrl);
}
return null;
return dataTypeEnum;
}
}
return null;
}
public String getFeature() {
return feature;
}
public String getFeature() {
return feature;
}
public String getDesc() {
return desc;
}
public String getDesc() {
return desc;
}
public String getDriver() {
return driver;
}
public String getDriver() {
return driver;
}
public String getKeywordPrefix() {
return keywordPrefix;
}
public String getKeywordPrefix() {
return keywordPrefix;
}
public String getKeywordSuffix() {
return keywordSuffix;
}
public String getKeywordSuffix() {
return keywordSuffix;
}
public String getAliasPrefix() {
return aliasPrefix;
}
public String getAliasPrefix() {
return aliasPrefix;
}
public String getAliasSuffix() {
return aliasSuffix;
}
public String getAliasSuffix() {
return aliasSuffix;
}
}
@@ -20,56 +20,56 @@ package cn.odboy.framework.mybatisplus.config;
* 动态查询 枚举
*/
public enum KitMpQTypeEnum {
/**
* 相等
*/
EQUAL
/**
* 大于等于
*/
, GREATER_THAN
/**
* 小于等于
*/
, LESS_THAN
/**
* 中模糊查询
*/
, INNER_LIKE
/**
* 左模糊查询
*/
, LEFT_LIKE
/**
* 右模糊查询
*/
, RIGHT_LIKE
/**
* 小于
*/
, LESS_THAN_NQ
/**
* 包含
*/
, IN
/**
* 不包含
*/
, NOT_IN
/**
* 不等于
*/
, NOT_EQUAL
/**
* between(注:被注解的属性一定得是数组或者集合, 长度大于等于2)
*/
, BETWEEN
/**
* 不为空
*/
, NOT_NULL
/**
* 为空
*/
, IS_NULL
/**
* 相等
*/
EQUAL
/**
* 大于等于
*/
, GREATER_THAN
/**
* 小于等于
*/
, LESS_THAN
/**
* 中模糊查询
*/
, INNER_LIKE
/**
* 左模糊查询
*/
, LEFT_LIKE
/**
* 右模糊查询
*/
, RIGHT_LIKE
/**
* 小于
*/
, LESS_THAN_NQ
/**
* 包含
*/
, IN
/**
* 不包含
*/
, NOT_IN
/**
* 不等于
*/
, NOT_EQUAL
/**
* between(注:被注解的属性一定得是数组或者集合, 长度大于等于2)
*/
, BETWEEN
/**
* 不为空
*/
, NOT_NULL
/**
* 为空
*/
, IS_NULL
}
@@ -29,18 +29,19 @@ import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface KitMpQuery {
/**
* 基本对象的属性名
*/
String propName() default "";
/**
* 查询方式
*/
KitMpQTypeEnum type() default KitMpQTypeEnum.EQUAL;
/**
* 基本对象的属性名
*/
String propName() default "";
/**
* 多字段模糊搜索, 仅支持String类型字段, 多个用逗号隔开, 如@MpQuery(blurry = "email,username")
*/
String blurry() default "";
/**
* 查询方式
*/
KitMpQTypeEnum type() default KitMpQTypeEnum.EQUAL;
/**
* 多字段模糊搜索, 仅支持String类型字段, 多个用逗号隔开, 如@MpQuery(blurry = "email,username")
*/
String blurry() default "";
}
@@ -25,44 +25,45 @@ import lombok.extern.slf4j.Slf4j;
*/
@Slf4j
public class MpCustomP6SpyLogger implements MessageFormattingStrategy {
/**
* 重置颜色
*/
private static final String RESET = "\u001B[0m";
/**
* 红色
*/
private static final String RED = "\u001B[31m";
/**
* 绿色
*/
private static final String GREEN = "\u001B[32m";
/**
* 蓝色
*/
private static final String BLUE = "\u001B[34m";
/**
* 格式化 sql
*
* @param connectionId 连接id
* @param now 当前时间
* @param elapsed 执行时长
* @param category sql分类
* @param prepared 预编译sql
* @param sql 执行sql
* @param url 数据库连接url
* @return 格式化后的sql
*/
@Override
public String formatMessage(int connectionId, String now, long elapsed, String category, String prepared,
String sql, String url) {
// 去掉换行和多余空格
if (StrUtil.isNotBlank(sql)) {
sql = sql.replaceAll("\\s+", " ").trim();
}
// 格式化并加上颜色
return String.format("CuteJava - %s%s%s %s[%s] %sREVIEW_SQL [Interval: %dms]%s -> %s;", RED, now, RESET, GREEN,
Thread.currentThread().getName(), BLUE, elapsed, RESET, sql);
/**
* 重置颜色
*/
private static final String RESET = "\u001B[0m";
/**
* 红色
*/
private static final String RED = "\u001B[31m";
/**
* 绿色
*/
private static final String GREEN = "\u001B[32m";
/**
* 蓝色
*/
private static final String BLUE = "\u001B[34m";
/**
* 格式化 sql
*
* @param connectionId 连接id
* @param now 当前时间
* @param elapsed 执行时长
* @param category sql分类
* @param prepared 预编译sql
* @param sql 执行sql
* @param url 数据库连接url
* @return 格式化后的sql
*/
@Override
public String formatMessage(int connectionId, String now, long elapsed, String category, String prepared,
String sql, String url) {
// 去掉换行和多余空格
if (StrUtil.isNotBlank(sql)) {
sql = sql.replaceAll("\\s+", " ").trim();
}
// 格式化并加上颜色
return String.format("CuteJava - %s%s%s %s[%s] %sREVIEW_SQL [Interval: %dms]%s -> %s;", RED, now, RESET, GREEN,
Thread.currentThread().getName(), BLUE, elapsed, RESET, sql);
}
}
@@ -32,13 +32,14 @@ import org.apache.ibatis.session.Configuration;
* @date 2025-01-11
*/
public class MpInsertBatchSqlInjector extends DefaultSqlInjector {
@Override
public List<AbstractMethod> getMethodList(Configuration configuration, Class<?> mapperClass, TableInfo tableInfo) {
List<AbstractMethod> methodList = super.getMethodList(configuration, mapperClass, tableInfo);
// 新增自定义方法, InsertBatchSomeColumn这个方法Mp没有开放出来
methodList.add(new InsertBatchSomeColumn(i -> i.getFieldFill() != FieldFill.UPDATE));
// 全量更新, 包括null字段
methodList.add(new AlwaysUpdateSomeColumnById(i -> i.getFieldFill() != FieldFill.INSERT));
return methodList;
}
@Override
public List<AbstractMethod> getMethodList(Configuration configuration, Class<?> mapperClass, TableInfo tableInfo) {
List<AbstractMethod> methodList = super.getMethodList(configuration, mapperClass, tableInfo);
// 新增自定义方法, InsertBatchSomeColumn这个方法Mp没有开放出来
methodList.add(new InsertBatchSomeColumn(i -> i.getFieldFill() != FieldFill.UPDATE));
// 全量更新, 包括null字段
methodList.add(new AlwaysUpdateSomeColumnById(i -> i.getFieldFill() != FieldFill.INSERT));
return methodList;
}
}
@@ -28,28 +28,29 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@EnableTransactionManagement
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor paginationInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// MySQL的分页拦截器
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
paginationInnerInterceptor.setMaxLimit(500L);
/// 这个配置会导致:当分页没数据时, 游标从0开始,会导致无论page为什么值,就算分页超出最大页数,依然能够查询到
/// paginationInnerInterceptor.setOverflow(true);
paginationInnerInterceptor.setOptimizeJoin(true);
interceptor.addInnerInterceptor(paginationInnerInterceptor);
// 乐观锁
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
// 防全表更新与删除
interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
return interceptor;
}
/**
* Mapper支持批量插入
*/
@Bean
public MpInsertBatchSqlInjector insertBatchSqlInjector() {
return new MpInsertBatchSqlInjector();
}
@Bean
public MybatisPlusInterceptor paginationInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// MySQL的分页拦截器
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
paginationInnerInterceptor.setMaxLimit(500L);
/// 这个配置会导致:当分页没数据时, 游标从0开始,会导致无论page为什么值,就算分页超出最大页数,依然能够查询到
/// paginationInnerInterceptor.setOverflow(true);
paginationInnerInterceptor.setOptimizeJoin(true);
interceptor.addInnerInterceptor(paginationInnerInterceptor);
// 乐观锁
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
// 防全表更新与删除
interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
return interceptor;
}
/**
* Mapper支持批量插入
*/
@Bean
public MpInsertBatchSqlInjector insertBatchSqlInjector() {
return new MpInsertBatchSqlInjector();
}
}
@@ -43,55 +43,55 @@ import org.springframework.context.annotation.Configuration;
matchIfMissing = true)
public class RemoveDruidAdConfig {
/**
* 方法名: removeDruidAdFilterRegistrationBean 方法描述 除去页面底部的广告
*
* @param properties com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties
* @return org.springframework.boot.web.servlet.FilterRegistrationBean
*/
@Bean
public FilterRegistrationBean removeDruidAdFilterRegistrationBean(DruidStatProperties properties) {
/**
* 方法名: removeDruidAdFilterRegistrationBean 方法描述 除去页面底部的广告
*
* @param properties com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties
* @return org.springframework.boot.web.servlet.FilterRegistrationBean
*/
@Bean
public FilterRegistrationBean removeDruidAdFilterRegistrationBean(DruidStatProperties properties) {
// 获取web监控页面的参数
DruidStatProperties.StatViewServlet config = properties.getStatViewServlet();
// 提取common.js的配置路径
String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*";
String commonJsPattern = pattern.replaceAll("\\*", "js/common.js");
// 获取web监控页面的参数
DruidStatProperties.StatViewServlet config = properties.getStatViewServlet();
// 提取common.js的配置路径
String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*";
String commonJsPattern = pattern.replaceAll("\\*", "js/common.js");
final String filePath = "support/http/resources/js/common.js";
final String filePath = "support/http/resources/js/common.js";
//创建filter进行过滤
Filter filter = new Filter() {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
//创建filter进行过滤
Filter filter = new Filter() {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest)request;
HttpServletResponse httpResponse = (HttpServletResponse)response;
if (httpRequest.getRequestURI().endsWith("js/common.js")) {
// 获取common.js
String text = Utils.readFromResource(filePath);
// 正则替换banner, 除去底部的广告信息
text = text.replaceAll("<a.*?druid_banner\"></a><br/>", "");
text = text.replaceAll("powered by.*?shrek.wang</a>", "");
httpResponse.setContentType("application/javascript");
httpResponse.setCharacterEncoding("UTF-8");
httpResponse.getWriter().write(text);
} else {
chain.doFilter(request, response);
}
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
if (httpRequest.getRequestURI().endsWith("js/common.js")) {
// 获取common.js
String text = Utils.readFromResource(filePath);
// 正则替换banner, 除去底部的广告信息
text = text.replaceAll("<a.*?druid_banner\"></a><br/>", "");
text = text.replaceAll("powered by.*?shrek.wang</a>", "");
httpResponse.setContentType("application/javascript");
httpResponse.setCharacterEncoding("UTF-8");
httpResponse.getWriter().write(text);
} else {
chain.doFilter(request, response);
}
}
@Override
public void destroy() {
}
};
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(filter);
registrationBean.addUrlPatterns(commonJsPattern);
return registrationBean;
}
@Override
public void destroy() {
}
};
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(filter);
registrationBean.addUrlPatterns(commonJsPattern);
return registrationBean;
}
}
@@ -30,53 +30,54 @@ import java.util.List;
* @date 2025-05-15
*/
public class KitMpAnyQUtil {
public static <T> T selectOne(BaseMapper<T> baseMapper, Object queryParams) {
QueryWrapper<T> queryWrapper = KitMpQUtil.build(queryParams);
return baseMapper.selectOne(queryWrapper);
}
public static <T, M> M selectOne(BaseMapper<T> baseMapper, Object queryParams, Class<M> mapperClazz) {
QueryWrapper<T> queryWrapper = KitMpQUtil.build(queryParams);
T mapperTarget = baseMapper.selectOne(queryWrapper);
if (mapperTarget == null) {
return null;
}
return BeanUtil.copyProperties(mapperTarget, mapperClazz);
}
public static <T> T selectOne(BaseMapper<T> baseMapper, Object queryParams) {
QueryWrapper<T> queryWrapper = KitMpQUtil.build(queryParams);
return baseMapper.selectOne(queryWrapper);
}
public static <T> List<T> selectList(BaseMapper<T> baseMapper, Object queryParams) {
QueryWrapper<T> queryWrapper = KitMpQUtil.build(queryParams);
return baseMapper.selectList(queryWrapper);
public static <T, M> M selectOne(BaseMapper<T> baseMapper, Object queryParams, Class<M> mapperClazz) {
QueryWrapper<T> queryWrapper = KitMpQUtil.build(queryParams);
T mapperTarget = baseMapper.selectOne(queryWrapper);
if (mapperTarget == null) {
return null;
}
return BeanUtil.copyProperties(mapperTarget, mapperClazz);
}
public static <T, M> List<M> selectList(BaseMapper<T> baseMapper, Object queryParams, Class<M> mapperClazz) {
QueryWrapper<T> queryWrapper = KitMpQUtil.build(queryParams);
List<T> mapperTarget = baseMapper.selectList(queryWrapper);
if (mapperTarget == null) {
return new ArrayList<>();
}
return BeanUtil.copyToList(mapperTarget, mapperClazz);
}
public static <T> List<T> selectList(BaseMapper<T> baseMapper, Object queryParams) {
QueryWrapper<T> queryWrapper = KitMpQUtil.build(queryParams);
return baseMapper.selectList(queryWrapper);
}
public static <T> Long selectCount(BaseMapper<T> baseMapper, Object queryParams) {
QueryWrapper<T> queryWrapper = KitMpQUtil.build(queryParams);
return baseMapper.selectCount(queryWrapper);
public static <T, M> List<M> selectList(BaseMapper<T> baseMapper, Object queryParams, Class<M> mapperClazz) {
QueryWrapper<T> queryWrapper = KitMpQUtil.build(queryParams);
List<T> mapperTarget = baseMapper.selectList(queryWrapper);
if (mapperTarget == null) {
return new ArrayList<>();
}
return BeanUtil.copyToList(mapperTarget, mapperClazz);
}
public static <T> Page<T> selectPage(BaseMapper<T> baseMapper, Page<T> page, Object queryParams) {
QueryWrapper<T> queryWrapper = KitMpQUtil.build(queryParams);
return baseMapper.selectPage(page, queryWrapper);
}
public static <T> Long selectCount(BaseMapper<T> baseMapper, Object queryParams) {
QueryWrapper<T> queryWrapper = KitMpQUtil.build(queryParams);
return baseMapper.selectCount(queryWrapper);
}
public static <T, M> Page<M> selectPage(BaseMapper<T> baseMapper, Page<T> page, Object queryParams,
Class<M> mapperClazz) {
QueryWrapper<T> queryWrapper = KitMpQUtil.build(queryParams);
Page<T> tPage = baseMapper.selectPage(page, queryWrapper);
Page<M> mapperTarget = new Page<>();
mapperTarget.setRecords(BeanUtil.copyToList(tPage.getRecords(), mapperClazz));
mapperTarget.setTotal(tPage.getTotal());
mapperTarget.setSize(tPage.getSize());
mapperTarget.setCurrent(tPage.getCurrent());
return mapperTarget;
}
public static <T> Page<T> selectPage(BaseMapper<T> baseMapper, Page<T> page, Object queryParams) {
QueryWrapper<T> queryWrapper = KitMpQUtil.build(queryParams);
return baseMapper.selectPage(page, queryWrapper);
}
public static <T, M> Page<M> selectPage(BaseMapper<T> baseMapper, Page<T> page, Object queryParams,
Class<M> mapperClazz) {
QueryWrapper<T> queryWrapper = KitMpQUtil.build(queryParams);
Page<T> tPage = baseMapper.selectPage(page, queryWrapper);
Page<M> mapperTarget = new Page<>();
mapperTarget.setRecords(BeanUtil.copyToList(tPage.getRecords(), mapperClazz));
mapperTarget.setTotal(tPage.getTotal());
mapperTarget.setSize(tPage.getSize());
mapperTarget.setCurrent(tPage.getCurrent());
return mapperTarget;
}
}
@@ -49,165 +49,166 @@ import org.apache.ibatis.annotations.Mapper;
*/
@Data
public class KitMpCmdGenUtil {
// 项目根路径。生成结果 C:\Users\Administrator\Documents\GitHub\cutejava\cutejava
private static final String projectRootPath = System.getProperty("user.dir") + "/";
/**
* 数据库类型 MySQL
*/
private static final IDbQuery DB_QUERY = new MySqlQuery();
/**
* 类型转换
*/
private static final String TYPE_TINY_INT = "tinyint";
private static final String TYPE_DATETIME = "datetime";
private static final String TYPE_DATE = "date";
private String databaseUrl;
private String databaseUsername;
private String databasePassword;
private String driverClassName;
private String parentPackageName = "cn.odboy";
private String parentModuleName;
private static Consumer<StrategyConfig.Builder> getEntityConfigConsumer() {
return strategyConfigBuilder -> strategyConfigBuilder.entityBuilder().enableLombok()
// 如果不需要生成注解, 去掉.enableTableFieldAnnotation()
.enableTableFieldAnnotation().enableFileOverride().naming(NamingStrategy.underline_to_camel)
.columnNaming(NamingStrategy.underline_to_camel).idType(IdType.AUTO);
}
// 项目根路径。生成结果 C:\Users\Administrator\Documents\GitHub\cutejava\cutejava
private static final String projectRootPath = System.getProperty("user.dir") + "/";
/**
* 数据库类型 MySQL
*/
private static final IDbQuery DB_QUERY = new MySqlQuery();
/**
* 类型转换
*/
private static final String TYPE_TINY_INT = "tinyint";
private static final String TYPE_DATETIME = "datetime";
private static final String TYPE_DATE = "date";
private String databaseUrl;
private String databaseUsername;
private String databasePassword;
private String driverClassName;
private String parentPackageName = "cn.odboy";
private String parentModuleName;
private static Consumer<StrategyConfig.Builder> getStrategyConfigConsumer(List<String> tableNames) {
return strategyConfigBuilder -> strategyConfigBuilder.enableCapitalMode().enableSkipView().disableSqlFilter()
.addInclude(tableNames)
// 实体类生成策略
.entityBuilder().superClass(KitBaseUserTimeTb.class)
.addIgnoreColumns("create_time", "update_time", "create_by", "update_by")
// 开启生成实体时生成字段注解。
// 会在实体类的属性前,添加[@TableField("nickname")]
.enableTableFieldAnnotation().disableSerialVersionUID()
// 阶段2:Mapper策略配置
.mapperBuilder()
// 开启 @Mapper 注解。
// 会在mapper接口上添加注解[@Mapper]
// .enableMapperAnnotation()
// 启用 BaseResultMap 生成。
// 会在mapper.xml文件生成[通用查询映射结果]配置。
// .enableBaseResultMap()
// 启用 BaseColumnList。
// 会在mapper.xml文件生成[通用查询结果列 ]配置
// .enableBaseColumnList()
// 阶段4:Controller策略配置
.controllerBuilder()
// 会在控制类中加[@RestController]注解。
.enableRestStyle()
// 开启驼峰转连字符
.enableHyphenStyle().build();
}
private static Consumer<StrategyConfig.Builder> getEntityConfigConsumer() {
return strategyConfigBuilder -> strategyConfigBuilder.entityBuilder().enableLombok()
// 如果不需要生成注解, 去掉.enableTableFieldAnnotation()
.enableTableFieldAnnotation().enableFileOverride().naming(NamingStrategy.underline_to_camel)
.columnNaming(NamingStrategy.underline_to_camel).idType(IdType.AUTO);
}
private static Consumer<GlobalConfig.Builder> getGlobalConfigConsumer() {
String outputDir = projectRootPath + "CodeGen";
return globalConfigBuilder -> globalConfigBuilder.enableSwagger().outputDir(outputDir).author("codegen")
.commentDate("yyyy-MM-dd").dateType(DateType.TIME_PACK);
}
private static Consumer<StrategyConfig.Builder> getStrategyConfigConsumer(List<String> tableNames) {
return strategyConfigBuilder -> strategyConfigBuilder.enableCapitalMode().enableSkipView().disableSqlFilter()
.addInclude(tableNames)
// 实体类生成策略
.entityBuilder().superClass(KitBaseUserTimeTb.class)
.addIgnoreColumns("create_time", "update_time", "create_by", "update_by")
// 开启生成实体时生成字段注解。
// 会在实体类的属性前,添加[@TableField("nickname")]
.enableTableFieldAnnotation().disableSerialVersionUID()
// 阶段2:Mapper策略配置
.mapperBuilder()
// 开启 @Mapper 注解。
// 会在mapper接口上添加注解[@Mapper]
// .enableMapperAnnotation()
// 启用 BaseResultMap 生成。
// 会在mapper.xml文件生成[通用查询映射结果]配置。
// .enableBaseResultMap()
// 启用 BaseColumnList。
// 会在mapper.xml文件生成[通用查询结果列 ]配置
// .enableBaseColumnList()
// 阶段4:Controller策略配置
.controllerBuilder()
// 会在控制类中加[@RestController]注解。
.enableRestStyle()
// 开启驼峰转连字符
.enableHyphenStyle().build();
}
private static Consumer<StrategyConfig.Builder> getStrategyConfigConsumer() {
return strategyConfigBuilder -> strategyConfigBuilder.mapperBuilder().mapperAnnotation(Mapper.class)
.formatMapperFileName("%sMapper").formatXmlFileName("%sMapper").entityBuilder().formatFileName("%sTb");
}
private static Consumer<GlobalConfig.Builder> getGlobalConfigConsumer() {
String outputDir = projectRootPath + "CodeGen";
return globalConfigBuilder -> globalConfigBuilder.enableSwagger().outputDir(outputDir).author("codegen")
.commentDate("yyyy-MM-dd").dateType(DateType.TIME_PACK);
}
private static Consumer<DataSourceConfig.Builder> getDataSourceConfigConsumer() {
return builder -> builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {
int typeCode = metaInfo.getJdbcType().TYPE_CODE;
if (typeCode == Types.SMALLINT) {
return DbColumnType.INTEGER;
}
if (typeCode == Types.DATE) {
return DbColumnType.DATE;
}
if (typeCode == Types.TIME) {
return DbColumnType.DATE;
}
if (typeCode == Types.TIMESTAMP) {
return DbColumnType.DATE;
}
return typeRegistry.getColumnType(metaInfo);
});
}
private static Consumer<StrategyConfig.Builder> getStrategyConfigConsumer() {
return strategyConfigBuilder -> strategyConfigBuilder.mapperBuilder().mapperAnnotation(Mapper.class)
.formatMapperFileName("%sMapper").formatXmlFileName("%sMapper").entityBuilder().formatFileName("%sTb");
}
/**
* 代码生成函数
*
* @param moduleName 模块名称
* @param tablePrefix 表前缀(可空)
* @param tableNames 表名称, 多个用逗号分隔
*/
public void gen(String moduleName, String tablePrefix, List<String> tableNames) {
this.parentModuleName = moduleName;
// 1.数据库配置
DataSourceConfig.Builder dataSourceConfigBuilder =
new DataSourceConfig.Builder(this.databaseUrl, this.databaseUsername,
this.databasePassword).driverClassName(this.driverClassName);
dataSourceConfigBuilder.dbQuery(DB_QUERY);
dataSourceConfigBuilder.keyWordsHandler(new MySqlKeyWordsHandler());
// 1.1.快速生成器
FastAutoGenerator fastAutoGenerator = FastAutoGenerator.create(dataSourceConfigBuilder);
// 2.全局配置
// 自定义类型转换
fastAutoGenerator.dataSourceConfig(getDataSourceConfigConsumer());
// 覆盖已生成文件
// 添加swagger注解
// 设置注释的作者
// 设置注释的日期格式
// 使用java8新的时间类型
fastAutoGenerator.globalConfig(getGlobalConfigConsumer());
// 3.包配置
// 设置父包名
// 设置父包模块名
// 设置MVC下各个模块的包名
// 设置XML资源文件的目录
fastAutoGenerator.packageConfig(getPackageConfigConsumer());
// 4.模板配置
// 使用Freemarker引擎模板, 默认的是Velocity引擎模板
AbstractTemplateEngine templateEngine = new FreemarkerTemplateEngine();
fastAutoGenerator.templateEngine(templateEngine);
// 5.策略配置
// 设置需要生成的表名
// 设置过滤表前缀
fastAutoGenerator.strategyConfig(getStrategyConfigConsumer(tableNames));
if (tablePrefix != null) {
// 表前缀, 可多个
fastAutoGenerator.strategyConfig(
strategyConfigBuilder -> strategyConfigBuilder.addTablePrefix(tablePrefix));
}
// 5.1.Entity策略配置
// 生成实体时生成字段的注解, 包括@TableId注解等
// 数据库表和字段映射到实体的命名策略, 为下划线转驼峰
// 全局主键类型为None
// 实体名称格式化为XXXEntity
fastAutoGenerator.strategyConfig(getEntityConfigConsumer());
// 5.2.Controller策略配置
// 开启生成@RestController控制器
fastAutoGenerator.strategyConfig(
strategyConfigBuilder -> strategyConfigBuilder.controllerBuilder().enableRestStyle());
// 如果不需要生成Controller, fastAutoGenerator.templateConfig(templateConfig -> templateConfig.controller(""))。5.3
// .Service策略配置:格式化service接口和实现类的文件名称, 去掉默认的ServiceName前面的I
fastAutoGenerator.strategyConfig(
strategyConfigBuilder -> strategyConfigBuilder.serviceBuilder().formatServiceFileName("%sService")
.formatServiceImplFileName("%sServiceImpl"));
// 5.4.Mapper策略配置
// 格式化 mapper文件名,格式化xml实现类文件名称
fastAutoGenerator.strategyConfig(getStrategyConfigConsumer());
// 6.生成代码
fastAutoGenerator.execute();
}
private static Consumer<DataSourceConfig.Builder> getDataSourceConfigConsumer() {
return builder -> builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {
int typeCode = metaInfo.getJdbcType().TYPE_CODE;
if (typeCode == Types.SMALLINT) {
return DbColumnType.INTEGER;
}
if (typeCode == Types.DATE) {
return DbColumnType.DATE;
}
if (typeCode == Types.TIME) {
return DbColumnType.DATE;
}
if (typeCode == Types.TIMESTAMP) {
return DbColumnType.DATE;
}
return typeRegistry.getColumnType(metaInfo);
});
}
private Consumer<PackageConfig.Builder> getPackageConfigConsumer() {
String mapperPath = projectRootPath + "CodeGen/resources/mapper";
if (StrUtil.isBlank(this.parentModuleName)) {
throw new BadRequestException("模块名称必填");
}
return packageConfigBuilder -> packageConfigBuilder.parent(this.parentPackageName)
.moduleName(this.parentModuleName).entity("dal.dataobject").mapper("dal.mysql").service("service")
.serviceImpl("service.impl").controller("controller")
.pathInfo(Collections.singletonMap(OutputFile.xml, mapperPath));
/**
* 代码生成函数
*
* @param moduleName 模块名称
* @param tablePrefix 表前缀(可空)
* @param tableNames 表名称, 多个用逗号分隔
*/
public void gen(String moduleName, String tablePrefix, List<String> tableNames) {
this.parentModuleName = moduleName;
// 1.数据库配置
DataSourceConfig.Builder dataSourceConfigBuilder =
new DataSourceConfig.Builder(this.databaseUrl, this.databaseUsername,
this.databasePassword).driverClassName(this.driverClassName);
dataSourceConfigBuilder.dbQuery(DB_QUERY);
dataSourceConfigBuilder.keyWordsHandler(new MySqlKeyWordsHandler());
// 1.1.快速生成器
FastAutoGenerator fastAutoGenerator = FastAutoGenerator.create(dataSourceConfigBuilder);
// 2.全局配置
// 自定义类型转换
fastAutoGenerator.dataSourceConfig(getDataSourceConfigConsumer());
// 覆盖已生成文件
// 添加swagger注解
// 设置注释的作者
// 设置注释的日期格式
// 使用java8新的时间类型
fastAutoGenerator.globalConfig(getGlobalConfigConsumer());
// 3.包配置
// 设置父包名
// 设置父包模块名
// 设置MVC下各个模块的包名
// 设置XML资源文件的目录
fastAutoGenerator.packageConfig(getPackageConfigConsumer());
// 4.模板配置
// 使用Freemarker引擎模板, 默认的是Velocity引擎模板
AbstractTemplateEngine templateEngine = new FreemarkerTemplateEngine();
fastAutoGenerator.templateEngine(templateEngine);
// 5.策略配置
// 设置需要生成的表名
// 设置过滤表前缀
fastAutoGenerator.strategyConfig(getStrategyConfigConsumer(tableNames));
if (tablePrefix != null) {
// 表前缀, 可多个
fastAutoGenerator.strategyConfig(
strategyConfigBuilder -> strategyConfigBuilder.addTablePrefix(tablePrefix));
}
// 5.1.Entity策略配置
// 生成实体时生成字段的注解, 包括@TableId注解等
// 数据库表和字段映射到实体的命名策略, 为下划线转驼峰
// 全局主键类型为None
// 实体名称格式化为XXXEntity
fastAutoGenerator.strategyConfig(getEntityConfigConsumer());
// 5.2.Controller策略配置
// 开启生成@RestController控制器
fastAutoGenerator.strategyConfig(
strategyConfigBuilder -> strategyConfigBuilder.controllerBuilder().enableRestStyle());
// 如果不需要生成Controller, fastAutoGenerator.templateConfig(templateConfig -> templateConfig.controller(""))。5.3
// .Service策略配置:格式化service接口和实现类的文件名称, 去掉默认的ServiceName前面的I
fastAutoGenerator.strategyConfig(
strategyConfigBuilder -> strategyConfigBuilder.serviceBuilder().formatServiceFileName("%sService")
.formatServiceImplFileName("%sServiceImpl"));
// 5.4.Mapper策略配置
// 格式化 mapper文件名,格式化xml实现类文件名称
fastAutoGenerator.strategyConfig(getStrategyConfigConsumer());
// 6.生成代码
fastAutoGenerator.execute();
}
private Consumer<PackageConfig.Builder> getPackageConfigConsumer() {
String mapperPath = projectRootPath + "CodeGen/resources/mapper";
if (StrUtil.isBlank(this.parentModuleName)) {
throw new BadRequestException("模块名称必填");
}
return packageConfigBuilder -> packageConfigBuilder.parent(this.parentPackageName)
.moduleName(this.parentModuleName).entity("dal.dataobject").mapper("dal.mysql").service("service")
.serviceImpl("service.impl").controller("controller")
.pathInfo(Collections.singletonMap(OutputFile.xml, mapperPath));
}
}
@@ -42,156 +42,163 @@ import lombok.extern.slf4j.Slf4j;
*/
@Slf4j
public class KitMpQUtil {
public static <R, Q> QueryWrapper<R> build(Q query) {
QueryWrapper<R> queryWrapper = new QueryWrapper<>();
if (query == null) {
return queryWrapper;
}
Class<?> clazz = query.getClass();
try {
List<Field> fields = getAllFields(clazz, new ArrayList<>());
for (Field field : fields) {
if (!field.canAccess(query)) {
field.setAccessible(true);
KitMpQuery q = field.getAnnotation(KitMpQuery.class);
if (q != null) {
String attributeName = getAttributeName(q, field);
Object fieldVal = field.get(query);
if (ObjectUtil.isNull(fieldVal) || "".equals(fieldVal)) {
continue;
}
if (StrUtil.isNotBlank(q.blurry())) {
handleBlurryQuery(queryWrapper, q.blurry(), fieldVal);
continue;
}
handleWrapper(q, queryWrapper, attributeName, fieldVal);
}
field.setAccessible(false);
}
}
} catch (Exception e) {
log.error("Failed to build query wrapper for class: {}", clazz.getName(), e);
}
return queryWrapper;
}
private static String getAttributeName(KitMpQuery q, Field field) {
String attributeName = StrUtil.isBlank(q.propName()) ? field.getName() : q.propName();
return StrUtil.toUnderlineCase(attributeName);
public static <R, Q> QueryWrapper<R> build(Q query) {
QueryWrapper<R> queryWrapper = new QueryWrapper<>();
if (query == null) {
return queryWrapper;
}
private static <R> void handleWrapper(KitMpQuery q, QueryWrapper<R> queryWrapper, String attributeName,
Object fieldVal) {
switch (q.type()) {
case EQUAL -> queryWrapper.eq(attributeName, fieldVal);
case GREATER_THAN -> queryWrapper.ge(attributeName, fieldVal);
case LESS_THAN -> queryWrapper.le(attributeName, fieldVal);
case LESS_THAN_NQ -> queryWrapper.lt(attributeName, fieldVal);
case INNER_LIKE -> queryWrapper.like(attributeName, fieldVal);
case LEFT_LIKE -> queryWrapper.likeLeft(attributeName, fieldVal);
case RIGHT_LIKE -> queryWrapper.likeRight(attributeName, fieldVal);
case IN -> handleInOrNotQuery(true, queryWrapper, attributeName, fieldVal);
case NOT_IN -> handleInOrNotQuery(false, queryWrapper, attributeName, fieldVal);
case NOT_EQUAL -> queryWrapper.ne(attributeName, fieldVal);
case NOT_NULL -> queryWrapper.isNotNull(attributeName);
case IS_NULL -> queryWrapper.isNull(attributeName);
case BETWEEN -> handleBetweenQuery(queryWrapper, fieldVal, attributeName);
default -> {
Class<?> clazz = query.getClass();
try {
List<Field> fields = getAllFields(clazz, new ArrayList<>());
for (Field field : fields) {
if (!field.canAccess(query)) {
field.setAccessible(true);
KitMpQuery q = field.getAnnotation(KitMpQuery.class);
if (q != null) {
String attributeName = getAttributeName(q, field);
Object fieldVal = field.get(query);
if (ObjectUtil.isNull(fieldVal) || "".equals(fieldVal)) {
continue;
}
if (StrUtil.isNotBlank(q.blurry())) {
handleBlurryQuery(queryWrapper, q.blurry(), fieldVal);
continue;
}
handleWrapper(q, queryWrapper, attributeName, fieldVal);
}
field.setAccessible(false);
}
}
} catch (Exception e) {
log.error("Failed to build query wrapper for class: {}", clazz.getName(), e);
}
return queryWrapper;
}
private static <R> void handleInOrNotQuery(boolean b, QueryWrapper<R> queryWrapper, String attributeName,
Object fieldVal) {
Collection<?> wrapNotInVal = (Collection<?>)fieldVal;
if (CollectionUtil.isNotEmpty(wrapNotInVal)) {
Optional<?> anyValOptional = wrapNotInVal.stream().findAny();
if (anyValOptional.isPresent()) {
Object o = anyValOptional.get();
if (o instanceof Long) {
if (b) {
queryWrapper.in(attributeName, fieldVal);
} else {
queryWrapper.notIn(attributeName, fieldVal);
}
} else if (o instanceof Integer) {
if (b) {
queryWrapper.in(attributeName, fieldVal);
} else {
queryWrapper.notIn(attributeName, fieldVal);
}
} else {
throw new BadRequestException("InOrNotIn查询参数类型的对象属性值必须是Long/Integer类型集合");
}
}
}
private static String getAttributeName(KitMpQuery q, Field field) {
String attributeName = StrUtil.isBlank(q.propName()) ? field.getName() : q.propName();
return StrUtil.toUnderlineCase(attributeName);
}
private static <R> void handleWrapper(KitMpQuery q, QueryWrapper<R> queryWrapper, String attributeName,
Object fieldVal) {
switch (q.type()) {
case EQUAL -> queryWrapper.eq(attributeName, fieldVal);
case GREATER_THAN -> queryWrapper.ge(attributeName, fieldVal);
case LESS_THAN -> queryWrapper.le(attributeName, fieldVal);
case LESS_THAN_NQ -> queryWrapper.lt(attributeName, fieldVal);
case INNER_LIKE -> queryWrapper.like(attributeName, fieldVal);
case LEFT_LIKE -> queryWrapper.likeLeft(attributeName, fieldVal);
case RIGHT_LIKE -> queryWrapper.likeRight(attributeName, fieldVal);
case IN -> handleInOrNotQuery(true, queryWrapper, attributeName, fieldVal);
case NOT_IN -> handleInOrNotQuery(false, queryWrapper, attributeName, fieldVal);
case NOT_EQUAL -> queryWrapper.ne(attributeName, fieldVal);
case NOT_NULL -> queryWrapper.isNotNull(attributeName);
case IS_NULL -> queryWrapper.isNull(attributeName);
case BETWEEN -> handleBetweenQuery(queryWrapper, fieldVal, attributeName);
default -> {
}
}
}
private static <R> void handleBetweenQuery(QueryWrapper<R> queryWrapper, Object fieldVal,
String finalAttributeName) {
if (fieldVal instanceof List) {
List<Object> between = new ArrayList<>((List<?>)fieldVal);
int minLength = 2;
if (CollectionUtil.isNotEmpty(between) && between.size() >= minLength) {
queryWrapper.between(finalAttributeName, between.get(0), between.get(1));
} else {
throw new BadRequestException("BETWEEN类型的对象列表长度必须 >= 2");
}
private static <R> void handleInOrNotQuery(boolean b, QueryWrapper<R> queryWrapper, String attributeName,
Object fieldVal) {
Collection<?> wrapNotInVal = (Collection<?>) fieldVal;
if (CollectionUtil.isNotEmpty(wrapNotInVal)) {
Optional<?> anyValOptional = wrapNotInVal.stream().findAny();
if (anyValOptional.isPresent()) {
Object o = anyValOptional.get();
if (o instanceof Long) {
if (b) {
queryWrapper.in(attributeName, fieldVal);
} else {
queryWrapper.notIn(attributeName, fieldVal);
}
} else if (o instanceof Integer) {
if (b) {
queryWrapper.in(attributeName, fieldVal);
} else {
queryWrapper.notIn(attributeName, fieldVal);
}
} else {
throw new BadRequestException("BETWEEN类型的对象必须是一个List子集合");
throw new BadRequestException("InOrNotIn查询参数类型的对象属性值必须是Long/Integer类型集合");
}
}
}
}
/**
* 模糊多字段
*
* @param queryWrapper /
* @param blurry /
* @param fieldVal /
* @param <R> /
*/
private static <R> void handleBlurryQuery(QueryWrapper<R> queryWrapper, String blurry, Object fieldVal) {
List<String> blurryList = Arrays.stream(blurry.split(",")).filter(StrUtil::isNotBlank).distinct().toList();
queryWrapper.and(wrapper -> {
for (String blurryItem : blurryList) {
String column = StrUtil.toUnderlineCase(blurryItem);
wrapper.or();
wrapper.like(column, fieldVal.toString());
}
});
private static <R> void handleBetweenQuery(QueryWrapper<R> queryWrapper, Object fieldVal,
String finalAttributeName) {
if (fieldVal instanceof List) {
List<Object> between = new ArrayList<>((List<?>) fieldVal);
int minLength = 2;
if (CollectionUtil.isNotEmpty(between) && between.size() >= minLength) {
queryWrapper.between(finalAttributeName, between.get(0), between.get(1));
} else {
throw new BadRequestException("BETWEEN类型的对象列表长度必须 >= 2");
}
} else {
throw new BadRequestException("BETWEEN类型的对象必须是一个List子集合");
}
}
public static List<Field> getAllFields(Class<?> clazz, List<Field> fields) {
if (clazz != null) {
fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
getAllFields(clazz.getSuperclass(), fields);
}
return fields;
}
/**
* 模糊多字段
*
* @param queryWrapper /
* @param blurry /
* @param fieldVal /
* @param <R> /
*/
private static <R> void handleBlurryQuery(QueryWrapper<R> queryWrapper, String blurry, Object fieldVal) {
List<String> blurryList = Arrays.stream(blurry.split(",")).filter(StrUtil::isNotBlank).distinct().toList();
queryWrapper.and(wrapper -> {
for (String blurryItem : blurryList) {
String column = StrUtil.toUnderlineCase(blurryItem);
wrapper.or();
wrapper.like(column, fieldVal.toString());
}
});
}
public static void main(String[] args) {
QueryWrapper<TestDomain> query = new QueryWrapper<>();
query.or(wrapper -> wrapper.eq("username", 1).or().eq("nickname", 2));
query.eq("id", 1);
query.orderByDesc("id");
System.err.println("getSqlSelect=================================");
System.err.println(query.getSqlSelect());
System.err.println("getSqlSegment=================================");
System.err.println(query.getSqlSegment());
System.err.println("getSqlComment=================================");
System.err.println(query.getSqlComment());
System.err.println("getSqlSet=================================");
System.err.println(query.getSqlSet());
System.err.println("getTargetSql=================================");
System.err.println(query.getTargetSql());
public static List<Field> getAllFields(Class<?> clazz, List<Field> fields) {
if (clazz != null) {
fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
getAllFields(clazz.getSuperclass(), fields);
}
return fields;
}
@TableName("test_domain")
private static class TestDomain {
@NotNull(groups = KitBaseUserTimeTb.Update.class) @TableId(value = "user_id", type = IdType.AUTO) private Long
id;
@TableField(value = "dept_id") private Long deptId;
@NotBlank private String username;
@NotBlank private String nickName;
}
public static void main(String[] args) {
QueryWrapper<TestDomain> query = new QueryWrapper<>();
query.or(wrapper -> wrapper.eq("username", 1).or().eq("nickname", 2));
query.eq("id", 1);
query.orderByDesc("id");
System.err.println("getSqlSelect=================================");
System.err.println(query.getSqlSelect());
System.err.println("getSqlSegment=================================");
System.err.println(query.getSqlSegment());
System.err.println("getSqlComment=================================");
System.err.println(query.getSqlComment());
System.err.println("getSqlSet=================================");
System.err.println(query.getSqlSet());
System.err.println("getTargetSql=================================");
System.err.println(query.getTargetSql());
}
@TableName("test_domain")
private static class TestDomain {
@NotNull(groups = KitBaseUserTimeTb.Update.class)
@TableId(value = "user_id", type = IdType.AUTO)
private Long
id;
@TableField(value = "dept_id")
private Long deptId;
@NotBlank
private String username;
@NotBlank
private String nickName;
}
}
@@ -42,146 +42,147 @@ import org.springframework.data.domain.Pageable;
* @date 2022-04-03
*/
public interface KitMpMapper<E> extends BaseMapper<E> {
/**
* 批量插入
*
* @param entityList List<E>
* @return 影响的行数
*/
int insertBatchSomeColumn(List<E> entityList);
/**
* 根据id全量更新
*
* @param entity E
* @return 影响的行数
*/
int alwaysUpdateSomeColumnById(@Param("et") E entity);
/**
* 批量插入
*
* @param entityList List<E>
* @return 影响的行数
*/
int insertBatchSomeColumn(List<E> entityList);
/**
* 获取字典
*
* @param wrapper LambdaQueryWrapper<E>
* @param keyName 键对应的列名称
* @param valueName 值对应的列名称
* @return Map<String, Object>
*/
default Map<String, Object> getMapWithKv(LambdaQueryWrapper<E> wrapper, String keyName, String valueName) {
Map<String, Object> result = new ConcurrentHashMap<>(1);
List<E> list = this.selectList(wrapper);
if (CollUtil.isEmpty(list)) {
return new HashMap<>(3);
}
E tempObj = list.stream().findFirst().orElse(null);
if (tempObj == null) {
return new HashMap<>(3);
}
Field[] fields = ReflectUtil.getFields(tempObj.getClass());
for (E e : list) {
Field keyField = null;
Field valueField = null;
for (Field field : fields) {
String fieldName = field.getName();
if (fieldName.equals(keyName)) {
keyField = field;
} else if (fieldName.equals(valueName)) {
valueField = field;
break;
}
}
String fieldKey = String.valueOf(ReflectUtil.getFieldValue(e, keyField));
Object fieldValue = ReflectUtil.getFieldValue(e, valueField);
if (fieldKey != null) {
result.put(fieldKey, fieldValue);
}
}
return result;
/**
* 根据id全量更新
*
* @param entity E
* @return 影响的行数
*/
int alwaysUpdateSomeColumnById(@Param("et") E entity);
/**
* 获取字典
*
* @param wrapper LambdaQueryWrapper<E>
* @param keyName 键对应的列名称
* @param valueName 值对应的列名称
* @return Map<String, Object>
*/
default Map<String, Object> getMapWithKv(LambdaQueryWrapper<E> wrapper, String keyName, String valueName) {
Map<String, Object> result = new ConcurrentHashMap<>(1);
List<E> list = this.selectList(wrapper);
if (CollUtil.isEmpty(list)) {
return new HashMap<>(3);
}
/**
* 条件查询列表, 并返回期望的对象
*
* @param wrapper LambdaQueryWrapper<E>
* @param clazz 期望的对象类型
* @return List<T>
*/
default <T> List<T> queryFeatureClazzList(LambdaQueryWrapper<E> wrapper, Class<T> clazz) {
List<E> list = this.selectList(wrapper);
if (CollUtil.isEmpty(list)) {
return new ArrayList<>();
}
return BeanUtil.copyToList(list, clazz);
E tempObj = list.stream().findFirst().orElse(null);
if (tempObj == null) {
return new HashMap<>(3);
}
/**
* 条件查询列表, 并返回期望的对象
*
* @param wrapper LambdaQueryWrapper<E>
* @param clazz 期望的对象类型
* @return List<T>
*/
default <T> List<T> queryFeatureClazzList(LambdaQueryChainWrapper<E> wrapper, Class<T> clazz) {
List<E> list = wrapper.list();
if (CollUtil.isEmpty(list)) {
return new ArrayList<>();
Field[] fields = ReflectUtil.getFields(tempObj.getClass());
for (E e : list) {
Field keyField = null;
Field valueField = null;
for (Field field : fields) {
String fieldName = field.getName();
if (fieldName.equals(keyName)) {
keyField = field;
} else if (fieldName.equals(valueName)) {
valueField = field;
break;
}
return BeanUtil.copyToList(list, clazz);
}
String fieldKey = String.valueOf(ReflectUtil.getFieldValue(e, keyField));
Object fieldValue = ReflectUtil.getFieldValue(e, valueField);
if (fieldKey != null) {
result.put(fieldKey, fieldValue);
}
}
return result;
}
/**
* 条件查询分页列表, 并返回期望的对象
*
* @param pageable 分页参数
* @param wrapper LambdaQueryWrapper<E> wrapper
* @param clazz 期望的对象类型
* @return IPage<T>
*/
default <T> KitPageResult<T> queryFeatureClazzPage(Pageable pageable, LambdaQueryWrapper<E> wrapper,
Class<T> clazz) {
int pageNumber = pageable.getPageNumber();
int pageSize = pageable.getPageSize();
pageNumber = pageNumber <= 0 ? 1 : pageNumber;
pageSize = pageSize <= 0 ? 10 : pageSize;
IPage<E> pageInfo = this.selectPage(new Page<>(pageNumber, pageSize), wrapper);
if (CollUtil.isEmpty(pageInfo.getRecords())) {
return new KitPageResult<>(new ArrayList<>(), pageInfo.getTotal());
}
return new KitPageResult<>(BeanUtil.copyToList(pageInfo.getRecords(), clazz), pageInfo.getTotal());
/**
* 条件查询列表, 并返回期望的对象
*
* @param wrapper LambdaQueryWrapper<E>
* @param clazz 期望的对象类型
* @return List<T>
*/
default <T> List<T> queryFeatureClazzList(LambdaQueryWrapper<E> wrapper, Class<T> clazz) {
List<E> list = this.selectList(wrapper);
if (CollUtil.isEmpty(list)) {
return new ArrayList<>();
}
return BeanUtil.copyToList(list, clazz);
}
/**
* 条件查询分页列表, 并返回期望的对象
*
* @param pageable 分页参数
* @param wrapper LambdaQueryWrapper<E> wrapper
* @param clazz 期望的对象类型
* @return IPage<T>
*/
default <T> KitPageResult<T> queryFeatureClazzPage(Pageable pageable, LambdaQueryChainWrapper<E> wrapper,
Class<T> clazz) {
int pageNumber = pageable.getPageNumber();
int pageSize = pageable.getPageSize();
pageNumber = pageNumber <= 0 ? 1 : pageNumber;
pageSize = pageSize <= 0 ? 10 : pageSize;
IPage<E> pageInfo = wrapper.page(new Page<>(pageNumber, pageSize));
if (CollUtil.isEmpty(pageInfo.getRecords())) {
return KitPageUtil.emptyListData();
}
return new KitPageResult<>(BeanUtil.copyToList(pageInfo.getRecords(), clazz), pageInfo.getTotal());
/**
* 条件查询列表, 并返回期望的对象
*
* @param wrapper LambdaQueryWrapper<E>
* @param clazz 期望的对象类型
* @return List<T>
*/
default <T> List<T> queryFeatureClazzList(LambdaQueryChainWrapper<E> wrapper, Class<T> clazz) {
List<E> list = wrapper.list();
if (CollUtil.isEmpty(list)) {
return new ArrayList<>();
}
return BeanUtil.copyToList(list, clazz);
}
/**
* 获取目标类
*
* @param wrapper LambdaQueryChainWrapper<E> wrapper
* @param clazz 期望的对象类型
* @return T
*/
default <T> T getFeatureClazz(LambdaQueryChainWrapper<E> wrapper, Class<T> clazz) {
E e = this.selectOne(wrapper);
if (e == null) {
return null;
}
return BeanUtil.copyProperties(e, clazz);
/**
* 条件查询分页列表, 并返回期望的对象
*
* @param pageable 分页参数
* @param wrapper LambdaQueryWrapper<E> wrapper
* @param clazz 期望的对象类型
* @return IPage<T>
*/
default <T> KitPageResult<T> queryFeatureClazzPage(Pageable pageable, LambdaQueryWrapper<E> wrapper,
Class<T> clazz) {
int pageNumber = pageable.getPageNumber();
int pageSize = pageable.getPageSize();
pageNumber = pageNumber <= 0 ? 1 : pageNumber;
pageSize = pageSize <= 0 ? 10 : pageSize;
IPage<E> pageInfo = this.selectPage(new Page<>(pageNumber, pageSize), wrapper);
if (CollUtil.isEmpty(pageInfo.getRecords())) {
return new KitPageResult<>(new ArrayList<>(), pageInfo.getTotal());
}
return new KitPageResult<>(BeanUtil.copyToList(pageInfo.getRecords(), clazz), pageInfo.getTotal());
}
/**
* 条件查询分页列表, 并返回期望的对象
*
* @param pageable 分页参数
* @param wrapper LambdaQueryWrapper<E> wrapper
* @param clazz 期望的对象类型
* @return IPage<T>
*/
default <T> KitPageResult<T> queryFeatureClazzPage(Pageable pageable, LambdaQueryChainWrapper<E> wrapper,
Class<T> clazz) {
int pageNumber = pageable.getPageNumber();
int pageSize = pageable.getPageSize();
pageNumber = pageNumber <= 0 ? 1 : pageNumber;
pageSize = pageSize <= 0 ? 10 : pageSize;
IPage<E> pageInfo = wrapper.page(new Page<>(pageNumber, pageSize));
if (CollUtil.isEmpty(pageInfo.getRecords())) {
return KitPageUtil.emptyListData();
}
return new KitPageResult<>(BeanUtil.copyToList(pageInfo.getRecords(), clazz), pageInfo.getTotal());
}
/**
* 获取目标类
*
* @param wrapper LambdaQueryChainWrapper<E> wrapper
* @param clazz 期望的对象类型
* @return T
*/
default <T> T getFeatureClazz(LambdaQueryChainWrapper<E> wrapper, Class<T> clazz) {
E e = this.selectOne(wrapper);
if (e == null) {
return null;
}
return BeanUtil.copyProperties(e, clazz);
}
}
@@ -32,40 +32,41 @@ import java.util.List;
* @date 2022-04-03
*/
public interface KitMpService<T> extends IService<T> {
<G> int saveFeatureClazz(G resources);
<G> int saveFeatureClazzList(List<G> resources);
<G> int saveFeatureClazz(G resources);
<G> int modifyFeatureClazzById(G resources);
<G> int saveFeatureClazzList(List<G> resources);
<G> boolean modifyFeatureClazzListById(Collection<G> resources, int batchSize);
<G> int modifyFeatureClazzById(G resources);
<G> G queryFeatureClazzById(Serializable id, Class<G> targetClazz);
<G> boolean modifyFeatureClazzListById(Collection<G> resources, int batchSize);
T queryClazzByArgs(LambdaQueryWrapper<T> wrapper, SFunction<T, ?> orderColumn);
<G> G queryFeatureClazzById(Serializable id, Class<G> targetClazz);
<G> G queryFeatureClazzByArgs(LambdaQueryWrapper<T> wrapper, SFunction<T, ?> orderColumn, Class<G> clazz);
T queryClazzByArgs(LambdaQueryWrapper<T> wrapper, SFunction<T, ?> orderColumn);
<G> List<G> queryFeatureClazzListByIds(List<Serializable> ids, Class<G> targetClazz);
<G> G queryFeatureClazzByArgs(LambdaQueryWrapper<T> wrapper, SFunction<T, ?> orderColumn, Class<G> clazz);
<Q> List<T> queryClazzListByArgs(Q criteria);
<G> List<G> queryFeatureClazzListByIds(List<Serializable> ids, Class<G> targetClazz);
List<T> queryClazzListByArgs(LambdaQueryWrapper<T> wrapper);
<Q> List<T> queryClazzListByArgs(Q criteria);
<G, Q> List<G> queryFeatureClazzListByArgs(Q criteria, Class<G> targetClazz);
List<T> queryClazzListByArgs(LambdaQueryWrapper<T> wrapper);
<G> List<G> queryFeatureClazzListByArgs(LambdaQueryWrapper<T> wrapper, Class<G> targetClazz);
<G, Q> List<G> queryFeatureClazzListByArgs(Q criteria, Class<G> targetClazz);
KitPageResult<T> queryClazzPageByArgs(LambdaQueryWrapper<T> wrapper, IPage<T> pageable);
<G> List<G> queryFeatureClazzListByArgs(LambdaQueryWrapper<T> wrapper, Class<G> targetClazz);
<G> KitPageResult<G> queryFeatureClazzPageByArgs(LambdaQueryWrapper<T> wrapper, IPage<T> pageable,
Class<G> targetClazz);
KitPageResult<T> queryClazzPageByArgs(LambdaQueryWrapper<T> wrapper, IPage<T> pageable);
<G, Q> KitPageResult<G> queryFeatureClazzPageByArgs(Q criteria, IPage<T> pageable, Class<G> targetClazz);
<G> KitPageResult<G> queryFeatureClazzPageByArgs(LambdaQueryWrapper<T> wrapper, IPage<T> pageable,
Class<G> targetClazz);
<Q> KitPageResult<T> queryClazzPageByArgs(Q criteria, IPage<T> pageable);
<G, Q> KitPageResult<G> queryFeatureClazzPageByArgs(Q criteria, IPage<T> pageable, Class<G> targetClazz);
int modifyClazzByArgs(LambdaQueryWrapper<T> wrapper, T entity);
<Q> KitPageResult<T> queryClazzPageByArgs(Q criteria, IPage<T> pageable);
int removeClazzByArgs(LambdaQueryWrapper<T> wrapper);
int modifyClazzByArgs(LambdaQueryWrapper<T> wrapper, T entity);
int removeClazzByArgs(LambdaQueryWrapper<T> wrapper);
}
@@ -39,221 +39,222 @@ import org.apache.ibatis.binding.MapperMethod;
* @date 2022-04-03
*/
public class KitMpServiceImpl<M extends KitMpMapper<T>, T> extends ServiceImpl<M, T> {
/**
* 快速插入
*
* @param resources /
*/
public <G> int saveFeatureClazz(G resources) {
return getBaseMapper().insert(BeanUtil.copyProperties(resources, this.getEntityClass()));
}
/**
* 快速批量插入
*
* @param resources /
*/
public <G> int saveFeatureClazzList(List<G> resources) {
return getBaseMapper().insertBatchSomeColumn(BeanUtil.copyToList(resources, this.getEntityClass()));
}
/**
* 快速插入
*
* @param resources /
*/
public <G> int saveFeatureClazz(G resources) {
return getBaseMapper().insert(BeanUtil.copyProperties(resources, this.getEntityClass()));
}
/**
* 快速更新
*
* @param resources /
*/
public <G> int modifyFeatureClazzById(G resources) {
return getBaseMapper().updateById(BeanUtil.copyProperties(resources, this.getEntityClass()));
}
/**
* 快速批量插入
*
* @param resources /
*/
public <G> int saveFeatureClazzList(List<G> resources) {
return getBaseMapper().insertBatchSomeColumn(BeanUtil.copyToList(resources, this.getEntityClass()));
}
/**
* 快速批量更新
*
* @param resources /
* @param batchSize 每批数量
*/
public <G> boolean modifyFeatureClazzListById(Collection<G> resources, int batchSize) {
String sqlStatement = this.getSqlStatement(SqlMethod.UPDATE_BY_ID);
return this.executeBatch(resources, batchSize, (sqlSession, entity) -> {
MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();
param.put("et", BeanUtil.copyProperties(entity, this.getEntityClass()));
sqlSession.update(sqlStatement, param);
});
}
/**
* 快速更新
*
* @param resources /
*/
public <G> int modifyFeatureClazzById(G resources) {
return getBaseMapper().updateById(BeanUtil.copyProperties(resources, this.getEntityClass()));
}
/**
* 通过id查询
*
* @param id /
* @param targetClazz 目标类型
*/
public <G> G queryFeatureClazzById(Serializable id, Class<G> targetClazz) {
T entity = this.getById(id);
return entity == null ? null : BeanUtil.copyProperties(entity, targetClazz);
}
/**
* 快速批量更新
*
* @param resources /
* @param batchSize 每批数量
*/
public <G> boolean modifyFeatureClazzListById(Collection<G> resources, int batchSize) {
String sqlStatement = this.getSqlStatement(SqlMethod.UPDATE_BY_ID);
return this.executeBatch(resources, batchSize, (sqlSession, entity) -> {
MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();
param.put("et", BeanUtil.copyProperties(entity, this.getEntityClass()));
sqlSession.update(sqlStatement, param);
});
}
/**
* 查一条数据
*
* @param wrapper 制造条件
*/
public T queryClazzByArgs(LambdaQueryWrapper<T> wrapper, SFunction<T, ?> orderColumn) {
if (orderColumn == null) {
throw new BadRequestException("参数orderColumn(排序的列)必填");
}
if (wrapper == null) {
wrapper = new LambdaQueryWrapper<>();
}
wrapper.last("limit 1");
wrapper.orderByDesc(orderColumn);
List<T> ts = baseMapper.selectList(wrapper);
if (ts.isEmpty()) {
return null;
}
return ts.stream().findFirst().get();
}
/**
* 通过id查询
*
* @param id /
* @param targetClazz 目标类型
*/
public <G> G queryFeatureClazzById(Serializable id, Class<G> targetClazz) {
T entity = this.getById(id);
return entity == null ? null : BeanUtil.copyProperties(entity, targetClazz);
}
public <G> G queryFeatureClazzByArgs(LambdaQueryWrapper<T> wrapper, SFunction<T, ?> orderColumn, Class<G> clazz) {
if (orderColumn == null) {
throw new BadRequestException("参数orderColumn(排序的列)必填");
}
if (wrapper == null) {
wrapper = new LambdaQueryWrapper<>();
}
wrapper.last("limit 1");
wrapper.orderByDesc(orderColumn);
List<T> ts = baseMapper.selectList(wrapper);
if (ts.isEmpty()) {
return null;
}
return BeanUtil.copyProperties(ts.stream().findFirst().get(), clazz);
/**
* 查一条数据
*
* @param wrapper 制造条件
*/
public T queryClazzByArgs(LambdaQueryWrapper<T> wrapper, SFunction<T, ?> orderColumn) {
if (orderColumn == null) {
throw new BadRequestException("参数orderColumn(排序的列)必填");
}
if (wrapper == null) {
wrapper = new LambdaQueryWrapper<>();
}
wrapper.last("limit 1");
wrapper.orderByDesc(orderColumn);
List<T> ts = baseMapper.selectList(wrapper);
if (ts.isEmpty()) {
return null;
}
return ts.stream().findFirst().get();
}
/**
* 通过id集合查询
*
* @param ids /
* @param targetClazz 目标类型
*/
public <G> List<G> queryFeatureClazzListByIds(List<Serializable> ids, Class<G> targetClazz) {
List<T> entitys = this.listByIds(ids);
return entitys == null || entitys.isEmpty() ? CollUtil.newArrayList()
: BeanUtil.copyToList(entitys, targetClazz);
public <G> G queryFeatureClazzByArgs(LambdaQueryWrapper<T> wrapper, SFunction<T, ?> orderColumn, Class<G> clazz) {
if (orderColumn == null) {
throw new BadRequestException("参数orderColumn(排序的列)必填");
}
if (wrapper == null) {
wrapper = new LambdaQueryWrapper<>();
}
wrapper.last("limit 1");
wrapper.orderByDesc(orderColumn);
List<T> ts = baseMapper.selectList(wrapper);
if (ts.isEmpty()) {
return null;
}
return BeanUtil.copyProperties(ts.stream().findFirst().get(), clazz);
}
/**
* 查有关的数据
*
* @param criteria 动态条件
*/
public <Q> List<T> queryClazzListByArgs(Q criteria) {
QueryWrapper<T> predicate = KitMpQUtil.build(criteria);
return baseMapper.selectList(predicate);
}
/**
* 通过id集合查询
*
* @param ids /
* @param targetClazz 目标类型
*/
public <G> List<G> queryFeatureClazzListByIds(List<Serializable> ids, Class<G> targetClazz) {
List<T> entitys = this.listByIds(ids);
return entitys == null || entitys.isEmpty() ? CollUtil.newArrayList()
: BeanUtil.copyToList(entitys, targetClazz);
}
/**
* 查有关的数据
*
* @param wrapper 制造条件
*/
public List<T> queryClazzListByArgs(LambdaQueryWrapper<T> wrapper) {
if (wrapper == null) {
wrapper = new LambdaQueryWrapper<>();
}
return baseMapper.selectList(wrapper);
}
/**
* 查有关的数据
*
* @param criteria 动态条件
*/
public <Q> List<T> queryClazzListByArgs(Q criteria) {
QueryWrapper<T> predicate = KitMpQUtil.build(criteria);
return baseMapper.selectList(predicate);
}
/**
* 查有关的数据并转成目标类型的数据
*
* @param criteria 动态条件
* @param targetClazz 目标类型
*/
public <G, Q> List<G> queryFeatureClazzListByArgs(Q criteria, Class<G> targetClazz) {
QueryWrapper<T> predicate = KitMpQUtil.build(criteria);
List<T> data = baseMapper.selectList(predicate);
return BeanUtil.copyToList(data, targetClazz);
/**
* 查有关的数据
*
* @param wrapper 制造条件
*/
public List<T> queryClazzListByArgs(LambdaQueryWrapper<T> wrapper) {
if (wrapper == null) {
wrapper = new LambdaQueryWrapper<>();
}
return baseMapper.selectList(wrapper);
}
/**
* 查有关的数据并转换为目标类型
*
* @param wrapper 制造条件
* @param targetClazz 目标类型
*/
public <G> List<G> queryFeatureClazzListByArgs(LambdaQueryWrapper<T> wrapper, Class<G> targetClazz) {
if (wrapper == null) {
wrapper = new LambdaQueryWrapper<>();
}
return BeanUtil.copyToList(baseMapper.selectList(wrapper), targetClazz);
}
/**
* 查有关的数据并转成目标类型的数据
*
* @param criteria 动态条件
* @param targetClazz 目标类型
*/
public <G, Q> List<G> queryFeatureClazzListByArgs(Q criteria, Class<G> targetClazz) {
QueryWrapper<T> predicate = KitMpQUtil.build(criteria);
List<T> data = baseMapper.selectList(predicate);
return BeanUtil.copyToList(data, targetClazz);
}
/**
* 查有关的分页数据
*
* @param wrapper 制造条件
* @param pageable 分页参数
*/
public KitPageResult<T> queryClazzPageByArgs(LambdaQueryWrapper<T> wrapper, IPage<T> pageable) {
if (wrapper == null) {
wrapper = new LambdaQueryWrapper<>();
}
IPage<T> originPageData = baseMapper.selectPage(pageable, wrapper);
return new KitPageResult<>(originPageData.getRecords(), originPageData.getTotal());
/**
* 查有关的数据并转换为目标类型
*
* @param wrapper 制造条件
* @param targetClazz 目标类型
*/
public <G> List<G> queryFeatureClazzListByArgs(LambdaQueryWrapper<T> wrapper, Class<G> targetClazz) {
if (wrapper == null) {
wrapper = new LambdaQueryWrapper<>();
}
return BeanUtil.copyToList(baseMapper.selectList(wrapper), targetClazz);
}
/**
* 查有关的分页数据
*
* @param wrapper 制造条件
* @param pageable 分页参数
* @param targetClazz 目标类型
*/
public <G> KitPageResult<G> queryFeatureClazzPageByArgs(LambdaQueryWrapper<T> wrapper, IPage<T> pageable,
Class<G> targetClazz) {
if (wrapper == null) {
wrapper = new LambdaQueryWrapper<>();
}
IPage<T> originPageData = baseMapper.selectPage(pageable, wrapper);
return new KitPageResult<>(BeanUtil.copyToList(originPageData.getRecords(), targetClazz),
originPageData.getTotal());
/**
* 查有关的分页数据
*
* @param wrapper 制造条件
* @param pageable 分页参数
*/
public KitPageResult<T> queryClazzPageByArgs(LambdaQueryWrapper<T> wrapper, IPage<T> pageable) {
if (wrapper == null) {
wrapper = new LambdaQueryWrapper<>();
}
IPage<T> originPageData = baseMapper.selectPage(pageable, wrapper);
return new KitPageResult<>(originPageData.getRecords(), originPageData.getTotal());
}
public <G, Q> KitPageResult<G> queryFeatureClazzPageByArgs(Q criteria, IPage<T> pageable, Class<G> targetClazz) {
QueryWrapper<T> wrapper = KitMpQUtil.build(criteria);
IPage<T> originPageData = baseMapper.selectPage(pageable, wrapper);
return new KitPageResult<>(BeanUtil.copyToList(originPageData.getRecords(), targetClazz),
originPageData.getTotal());
/**
* 查有关的分页数据
*
* @param wrapper 制造条件
* @param pageable 分页参数
* @param targetClazz 目标类型
*/
public <G> KitPageResult<G> queryFeatureClazzPageByArgs(LambdaQueryWrapper<T> wrapper, IPage<T> pageable,
Class<G> targetClazz) {
if (wrapper == null) {
wrapper = new LambdaQueryWrapper<>();
}
IPage<T> originPageData = baseMapper.selectPage(pageable, wrapper);
return new KitPageResult<>(BeanUtil.copyToList(originPageData.getRecords(), targetClazz),
originPageData.getTotal());
}
public <Q> KitPageResult<T> queryClazzPageByArgs(Q criteria, IPage<T> pageable) {
QueryWrapper<T> wrapper = KitMpQUtil.build(criteria);
IPage<T> originPageData = baseMapper.selectPage(pageable, wrapper);
return new KitPageResult<>(originPageData.getRecords(), originPageData.getTotal());
}
public <G, Q> KitPageResult<G> queryFeatureClazzPageByArgs(Q criteria, IPage<T> pageable, Class<G> targetClazz) {
QueryWrapper<T> wrapper = KitMpQUtil.build(criteria);
IPage<T> originPageData = baseMapper.selectPage(pageable, wrapper);
return new KitPageResult<>(BeanUtil.copyToList(originPageData.getRecords(), targetClazz),
originPageData.getTotal());
}
/**
* 条件更新
*
* @param wrapper 制造条件
* @param entity 更新内容
*/
public int modifyClazzByArgs(LambdaQueryWrapper<T> wrapper, T entity) {
if (wrapper == null) {
throw new BadRequestException("参数wrapper必填");
}
return baseMapper.update(entity, wrapper);
}
public <Q> KitPageResult<T> queryClazzPageByArgs(Q criteria, IPage<T> pageable) {
QueryWrapper<T> wrapper = KitMpQUtil.build(criteria);
IPage<T> originPageData = baseMapper.selectPage(pageable, wrapper);
return new KitPageResult<>(originPageData.getRecords(), originPageData.getTotal());
}
/**
* 条件删除
*
* @param wrapper 制造条件
*/
public int removeClazzByArgs(LambdaQueryWrapper<T> wrapper) {
if (wrapper == null) {
throw new BadRequestException("参数wrapper必填");
}
return baseMapper.delete(wrapper);
/**
* 条件更新
*
* @param wrapper 制造条件
* @param entity 更新内容
*/
public int modifyClazzByArgs(LambdaQueryWrapper<T> wrapper, T entity) {
if (wrapper == null) {
throw new BadRequestException("参数wrapper必填");
}
return baseMapper.update(entity, wrapper);
}
/**
* 条件删除
*
* @param wrapper 制造条件
*/
public int removeClazzByArgs(LambdaQueryWrapper<T> wrapper) {
if (wrapper == null) {
throw new BadRequestException("参数wrapper必填");
}
return baseMapper.delete(wrapper);
}
}
@@ -36,12 +36,13 @@ import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "app")
public class AppProperties {
private ContentRsaEncodeSettingModel rsa;
private JwtAuthSettingModel jwt;
private UserLoginSettingModel login;
private ThreadPoolSettingModel asyncTaskPool;
private CaptchaModel captcha;
private StorageOSSModel oss;
private ContentRsaEncodeSettingModel rsa;
private JwtAuthSettingModel jwt;
private UserLoginSettingModel login;
private ThreadPoolSettingModel asyncTaskPool;
private CaptchaModel captcha;
private StorageOSSModel oss;
}
@@ -23,8 +23,9 @@ import lombok.Setter;
@Getter
@Setter
public class CaptchaModel extends KitObject {
/**
* 验证码有效时间, 单位: 秒
*/
private Long expireTime;
/**
* 验证码有效时间, 单位: 秒
*/
private Long expireTime;
}
@@ -26,5 +26,6 @@ import lombok.Setter;
@Getter
@Setter
public class ContentRsaEncodeSettingModel extends KitObject {
private String privateKey;
private String privateKey;
}
@@ -26,23 +26,24 @@ import lombok.Setter;
@Getter
@Setter
public class JwtAuthSettingModel extends KitObject {
/**
* 必须使用最少88位的Base64对该令牌进行编码
*/
private String base64Secret;
/**
* 令牌过期时间 此处单位/毫秒
*/
private Long tokenValidityInSeconds;
/**
* 必须使用最少88位的Base64对该令牌进行编码
*/
private String base64Secret;
/**
* token 续期检查
*/
private Long detect;
/**
* 令牌过期时间 此处单位/毫秒
*/
private Long tokenValidityInSeconds;
/**
* 续期时间
*/
private Long renew;
/**
* token 续期检查
*/
private Long detect;
/**
* 续期时间
*/
private Long renew;
}
@@ -23,20 +23,21 @@ import lombok.Setter;
@Getter
@Setter
public class OSSConfigModel extends KitObject {
/**
* 服务地址
*/
private String endpoint;
/**
* 存储桶名称
*/
private String bucketName;
/**
* Access Key
*/
private String accessKey;
/**
* Secret Key
*/
private String secretKey;
/**
* 服务地址
*/
private String endpoint;
/**
* 存储桶名称
*/
private String bucketName;
/**
* Access Key
*/
private String accessKey;
/**
* Secret Key
*/
private String secretKey;
}
@@ -22,9 +22,10 @@ import lombok.Setter;
@Getter
@Setter
public class StorageOSSModel {
/**
* 上传大小, 单位: M
*/
private Long maxSize;
private OSSConfigModel minio;
/**
* 上传大小, 单位: M
*/
private Long maxSize;
private OSSConfigModel minio;
}
@@ -29,20 +29,21 @@ import lombok.Setter;
@Getter
@Setter
public class ThreadPoolSettingModel extends KitObject {
/**
* 核心线程池大小
*/
private int corePoolSize;
/**
* 最大线程数
*/
private int maxPoolSize;
/**
* 活跃时间
*/
private int keepAliveSeconds;
/**
* 队列容量
*/
private int queueCapacity;
/**
* 核心线程池大小
*/
private int corePoolSize;
/**
* 最大线程数
*/
private int maxPoolSize;
/**
* 活跃时间
*/
private int keepAliveSeconds;
/**
* 队列容量
*/
private int queueCapacity;
}
@@ -38,96 +38,98 @@ import lombok.Setter;
@Getter
@Setter
public class UserLoginCaptchaSettingModel extends KitObject {
private CaptchaCodeEnum codeType;
/**
* 验证码有效期 分钟
*/
private Long expiration = 5L;
private CaptchaCodeEnum codeType;
/**
* 验证码内容长度
*/
private int length = 4;
/**
* 验证码有效期 分钟
*/
private Long expiration = 5L;
/**
* 验证码宽度
*/
private int width = 111;
/**
* 验证码内容长度
*/
private int length = 4;
/**
* 验证码高度
*/
private int height = 36;
/**
* 验证码宽度
*/
private int width = 111;
/**
* 验证码字体
*/
private String fontName;
/**
* 验证码高度
*/
private int height = 36;
/**
* 字体大小
*/
private int fontSize = 25;
/**
* 验证码字体
*/
private String fontName;
/**
* 依据配置信息生产验证码
*
* @return /
*/
public Captcha getCaptcha() {
Captcha captcha;
switch (codeType) {
case ARITHMETIC -> {
// 算术类型 https://gitee.com/whvse/EasyCaptcha
captcha = new FixedArithmeticCaptcha(width, height);
// 几位数运算, 默认是两位
captcha.setLen(length);
}
case CHINESE -> {
captcha = new ChineseCaptcha(width, height);
captcha.setLen(length);
}
case CHINESE_GIF -> {
captcha = new ChineseGifCaptcha(width, height);
captcha.setLen(length);
}
case GIF -> {
captcha = new GifCaptcha(width, height);
captcha.setLen(length);
}
case SPEC -> {
captcha = new SpecCaptcha(width, height);
captcha.setLen(length);
}
default -> throw new BadRequestException("验证码配置信息错误!正确配置查看 LoginCodeEnum ");
}
if (StrUtil.isNotBlank(fontName)) {
captcha.setFont(new Font(fontName, Font.PLAIN, fontSize));
}
return captcha;
/**
* 字体大小
*/
private int fontSize = 25;
/**
* 依据配置信息生产验证码
*
* @return /
*/
public Captcha getCaptcha() {
Captcha captcha;
switch (codeType) {
case ARITHMETIC -> {
// 算术类型 https://gitee.com/whvse/EasyCaptcha
captcha = new FixedArithmeticCaptcha(width, height);
// 几位数运算, 默认是两位
captcha.setLen(length);
}
case CHINESE -> {
captcha = new ChineseCaptcha(width, height);
captcha.setLen(length);
}
case CHINESE_GIF -> {
captcha = new ChineseGifCaptcha(width, height);
captcha.setLen(length);
}
case GIF -> {
captcha = new GifCaptcha(width, height);
captcha.setLen(length);
}
case SPEC -> {
captcha = new SpecCaptcha(width, height);
captcha.setLen(length);
}
default -> throw new BadRequestException("验证码配置信息错误!正确配置查看 LoginCodeEnum ");
}
if (StrUtil.isNotBlank(fontName)) {
captcha.setFont(new Font(fontName, Font.PLAIN, fontSize));
}
return captcha;
}
static class FixedArithmeticCaptcha extends ArithmeticCaptcha {
public FixedArithmeticCaptcha(int width, int height) {
super(width, height);
}
static class FixedArithmeticCaptcha extends ArithmeticCaptcha {
public FixedArithmeticCaptcha(int width, int height) {
super(width, height);
}
@Override
protected char[] alphas() {
// 生成随机数字和运算符
int n1 = num(1, 10), n2 = num(1, 10);
int opt = num(3);
@Override
protected char[] alphas() {
// 生成随机数字和运算符
int n1 = num(1, 10), n2 = num(1, 10);
int opt = num(3);
// 计算结果
int res = new int[]{n1 + n2, n1 - n2, n1 * n2}[opt];
// 转换为字符运算符
char optChar = "+-x".charAt(opt);
// 计算结果
int res = new int[] {n1 + n2, n1 - n2, n1 * n2}[opt];
// 转换为字符运算符
char optChar = "+-x".charAt(opt);
this.setArithmeticString(String.format("%s%c%s=?", n1, optChar, n2));
this.chars = String.valueOf(res);
this.setArithmeticString(String.format("%s%c%s=?", n1, optChar, n2));
this.chars = String.valueOf(res);
return chars.toCharArray();
}
return chars.toCharArray();
}
}
}
@@ -28,9 +28,10 @@ import lombok.Setter;
@Getter
@Setter
public class UserLoginSettingModel extends KitObject {
/**
* 账号单用户 登录
*/
private boolean single = false;
private UserLoginCaptchaSettingModel captchaSetting;
/**
* 账号单用户 登录
*/
private boolean single = false;
private UserLoginCaptchaSettingModel captchaSetting;
}
@@ -33,31 +33,32 @@ import org.springframework.stereotype.Component;
@Configuration
@Scope("singleton")
public class QuartzConfig {
/**
* 解决Job中注入Spring Bean为null的问题
*/
@Component("quartzJobFactory")
public static class QuartzJobFactory extends AdaptableJobFactory {
private final AutowireCapableBeanFactory autowireCapableBeanFactory;
/**
* 解决Job中注入Spring Bean为null的问题
*/
@Component("quartzJobFactory")
public static class QuartzJobFactory extends AdaptableJobFactory {
@Autowired
public QuartzJobFactory(AutowireCapableBeanFactory autowireCapableBeanFactory) {
this.autowireCapableBeanFactory = autowireCapableBeanFactory;
}
private final AutowireCapableBeanFactory autowireCapableBeanFactory;
@NonNull
@Override
protected Object createJobInstance(@NonNull TriggerFiredBundle triggerFiredBundle) throws Exception {
try {
// 调用父类的方法, 把Job注入Spring中
Object jobInstance = super.createJobInstance(triggerFiredBundle);
autowireCapableBeanFactory.autowireBean(jobInstance);
return jobInstance;
} catch (Exception e) {
log.error("Job注入Spring失败, {}", triggerFiredBundle, e);
throw e;
}
}
@Autowired
public QuartzJobFactory(AutowireCapableBeanFactory autowireCapableBeanFactory) {
this.autowireCapableBeanFactory = autowireCapableBeanFactory;
}
@NonNull
@Override
protected Object createJobInstance(@NonNull TriggerFiredBundle triggerFiredBundle) throws Exception {
try {
// 调用父类的方法, 把Job注入Spring中
Object jobInstance = super.createJobInstance(triggerFiredBundle);
autowireCapableBeanFactory.autowireBean(jobInstance);
return jobInstance;
} catch (Exception e) {
log.error("Job注入Spring失败, {}", triggerFiredBundle, e);
throw e;
}
}
}
}
@@ -28,29 +28,30 @@ import org.springframework.util.ReflectionUtils;
*/
@Slf4j
public class QuartzRunnable implements Callable<Object> {
private final Object target;
private final Method method;
private final String params;
public QuartzRunnable(String beanName, String methodName, String params)
throws NoSuchMethodException, SecurityException {
this.target = KitSpringBeanHolder.getBean(beanName);
this.params = params;
if (StringUtils.isNotBlank(params)) {
this.method = target.getClass().getDeclaredMethod(methodName, String.class);
} else {
this.method = target.getClass().getDeclaredMethod(methodName);
}
}
private final Object target;
private final Method method;
private final String params;
@Override
@SuppressWarnings({"unchecked", "all"})
public Object call() throws Exception {
ReflectionUtils.makeAccessible(method);
if (StringUtils.isNotBlank(params)) {
return method.invoke(target, params);
} else {
return method.invoke(target);
}
public QuartzRunnable(String beanName, String methodName, String params)
throws NoSuchMethodException, SecurityException {
this.target = KitSpringBeanHolder.getBean(beanName);
this.params = params;
if (StringUtils.isNotBlank(params)) {
this.method = target.getClass().getDeclaredMethod(methodName, String.class);
} else {
this.method = target.getClass().getDeclaredMethod(methodName);
}
}
@Override
@SuppressWarnings({"unchecked", "all"})
public Object call() throws Exception {
ReflectionUtils.makeAccessible(method);
if (StringUtils.isNotBlank(params)) {
return method.invoke(target, params);
} else {
return method.invoke(target);
}
}
}
@@ -48,148 +48,149 @@ import org.springframework.data.redis.serializer.StringRedisSerializer;
@EnableCaching
@AutoConfigureBefore(RedisAutoConfiguration.class)
public class RedisConfiguration {
/**
* 自动识别json对象白名单配置(仅允许解析的包名, 范围越小越安全)<br/> 未配置可能导致, 登录失败, 反复登录等问题
*/
private static final String[] WHITELIST_STR =
{"org.springframework", "cn.odboy.system.dal.dataobject", "cn.odboy.system.dal.model",
"cn.odboy.task.dal.dataobject", "cn.odboy.task.dal.model",};
/**
* 设置 redis 数据默认过期时间,默认2小时 设置@cacheable 序列化方式
*/
@Bean
public RedisCacheConfiguration redisCacheConfiguration() {
FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig();
configuration = configuration.serializeValuesWith(
RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer))
.entryTtl(Duration.ofHours(2));
return configuration;
/**
* 自动识别json对象白名单配置(仅允许解析的包名, 范围越小越安全)<br/> 未配置可能导致, 登录失败, 反复登录等问题
*/
private static final String[] WHITELIST_STR =
{"org.springframework", "cn.odboy.system.dal.dataobject", "cn.odboy.system.dal.model",
"cn.odboy.task.dal.dataobject", "cn.odboy.task.dal.model",};
/**
* 设置 redis 数据默认过期时间,默认2小时 设置@cacheable 序列化方式
*/
@Bean
public RedisCacheConfiguration redisCacheConfiguration() {
FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig();
configuration = configuration.serializeValuesWith(
RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer))
.entryTtl(Duration.ofHours(2));
return configuration;
}
@Bean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
// 指定 key 和 value 的序列化方案
FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
// value值的序列化采用fastJsonRedisSerializer
template.setValueSerializer(fastJsonRedisSerializer);
template.setHashValueSerializer(fastJsonRedisSerializer);
// 设置fastJson的序列化白名单
for (String pack : WHITELIST_STR) {
JSONFactory.getDefaultObjectReaderProvider().addAutoTypeAccept(pack);
}
// key的序列化采用StringRedisSerializer
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
// 指定 key 和 value 的序列化方案
FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
// value值的序列化采用fastJsonRedisSerializer
template.setValueSerializer(fastJsonRedisSerializer);
template.setHashValueSerializer(fastJsonRedisSerializer);
// 设置fastJson的序列化白名单
for (String pack : WHITELIST_STR) {
JSONFactory.getDefaultObjectReaderProvider().addAutoTypeAccept(pack);
}
// key的序列化采用StringRedisSerializer
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setConnectionFactory(redisConnectionFactory);
return template;
/**
* 缓存管理器
*
* @param redisConnectionFactory /
* @return 缓存管理器
*/
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration config = redisCacheConfiguration();
return RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(config).build();
}
/**
* 自定义缓存key生成策略
*/
@Bean
public KeyGenerator keyGenerator() {
return (target, method, params) -> {
Map<String, Object> container = new HashMap<>(8);
Class<?> targetClassClass = target.getClass();
// 类地址
container.put("class", targetClassClass.toGenericString());
// 方法名称
container.put("methodName", method.getName());
// 包名称
container.put("package", targetClassClass.getPackage());
// 参数列表
for (int i = 0; i < params.length; i++) {
container.put(String.valueOf(i), params[i]);
}
// 转为JSON字符串
String jsonString = JSON.toJSONString(container);
// 使用 MurmurHash 生成 hash
return Integer.toHexString(MurmurHash3.hash32x86(jsonString.getBytes()));
};
}
@Bean
public CacheErrorHandler errorHandler() {
return new SimpleCacheErrorHandler() {
@Override
public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {
// 处理缓存读取错误
log.error("Cache Get Error: {}", exception.getMessage());
}
@Override
public void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) {
// 处理缓存写入错误
log.error("Cache Put Error: {}", exception.getMessage());
}
@Override
public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) {
// 处理缓存删除错误
log.error("Cache Evict Error: {}", exception.getMessage());
}
@Override
public void handleCacheClearError(RuntimeException exception, Cache cache) {
// 处理缓存清除错误
log.error("Cache Clear Error: {}", exception.getMessage());
}
};
}
/**
* Value 序列化
*
* @param <T>
*/
static class FastJsonRedisSerializer<T> implements RedisSerializer<T> {
private final Class<T> clazz;
FastJsonRedisSerializer(Class<T> clazz) {
super();
this.clazz = clazz;
}
/**
* 缓存管理器
*
* @param redisConnectionFactory /
* @return 缓存管理器
* 序列化
*/
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration config = redisCacheConfiguration();
return RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(config).build();
@Override
public byte[] serialize(T t) throws SerializationException {
if (t == null) {
return new byte[0];
}
return JSON.toJSONString(t, JSONWriter.Feature.WriteClassName).getBytes(StandardCharsets.UTF_8);
}
/**
* 自定义缓存key生成策略
* 反序列化
*/
@Bean
public KeyGenerator keyGenerator() {
return (target, method, params) -> {
Map<String, Object> container = new HashMap<>(8);
Class<?> targetClassClass = target.getClass();
// 类地址
container.put("class", targetClassClass.toGenericString());
// 方法名称
container.put("methodName", method.getName());
// 包名称
container.put("package", targetClassClass.getPackage());
// 参数列表
for (int i = 0; i < params.length; i++) {
container.put(String.valueOf(i), params[i]);
}
// 转为JSON字符串
String jsonString = JSON.toJSONString(container);
// 使用 MurmurHash 生成 hash
return Integer.toHexString(MurmurHash3.hash32x86(jsonString.getBytes()));
};
}
@Bean
public CacheErrorHandler errorHandler() {
return new SimpleCacheErrorHandler() {
@Override
public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {
// 处理缓存读取错误
log.error("Cache Get Error: {}", exception.getMessage());
}
@Override
public void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) {
// 处理缓存写入错误
log.error("Cache Put Error: {}", exception.getMessage());
}
@Override
public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) {
// 处理缓存删除错误
log.error("Cache Evict Error: {}", exception.getMessage());
}
@Override
public void handleCacheClearError(RuntimeException exception, Cache cache) {
// 处理缓存清除错误
log.error("Cache Clear Error: {}", exception.getMessage());
}
};
}
/**
* Value 序列化
*
* @param <T>
*/
static class FastJsonRedisSerializer<T> implements RedisSerializer<T> {
private final Class<T> clazz;
FastJsonRedisSerializer(Class<T> clazz) {
super();
this.clazz = clazz;
}
/**
* 序列化
*/
@Override
public byte[] serialize(T t) throws SerializationException {
if (t == null) {
return new byte[0];
}
return JSON.toJSONString(t, JSONWriter.Feature.WriteClassName).getBytes(StandardCharsets.UTF_8);
}
/**
* 反序列化
*/
@Override
public T deserialize(byte[] bytes) throws SerializationException {
if (bytes == null || bytes.length == 0) {
return null;
}
String str = new String(bytes, StandardCharsets.UTF_8);
// 移除对 SupportAutoType 的使用
return JSON.parseObject(str, clazz);
}
@Override
public T deserialize(byte[] bytes) throws SerializationException {
if (bytes == null || bytes.length == 0) {
return null;
}
String str = new String(bytes, StandardCharsets.UTF_8);
// 移除对 SupportAutoType 的使用
return JSON.parseObject(str, clazz);
}
}
}
@@ -38,62 +38,64 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@EnableAsync
@Configuration
public class AsyncTheadPoolConfig implements AsyncConfigurer {
@Autowired private AppProperties properties;
/**
* 自定义线程池, 用法 @Async
*
* @return Executor
*/
@Override
public Executor getAsyncExecutor() {
ThreadPoolSettingModel asyncTaskPool = properties.getAsyncTaskPool();
// 自定义工厂
ThreadFactory factory = r -> new Thread(r, "DefaultAsync-" + new AtomicInteger(1).getAndIncrement());
// 自定义线程池
return new ThreadPoolExecutor(asyncTaskPool.getCorePoolSize(), asyncTaskPool.getMaxPoolSize(),
asyncTaskPool.getKeepAliveSeconds(), TimeUnit.SECONDS,
new ArrayBlockingQueue<>(asyncTaskPool.getQueueCapacity()), factory,
new ThreadPoolExecutor.CallerRunsPolicy());
}
@Autowired
private AppProperties properties;
/**
* 自定义线程池, 用法, 注入到类中使用
*/
@Bean("taskAsync")
public ThreadPoolTaskExecutor taskAsync() {
ThreadPoolSettingModel asyncTaskPool = properties.getAsyncTaskPool();
// 用法 private ThreadPoolTaskExecutor taskExecutor
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(asyncTaskPool.getCorePoolSize());
executor.setMaxPoolSize(asyncTaskPool.getMaxPoolSize());
executor.setQueueCapacity(asyncTaskPool.getQueueCapacity());
executor.setThreadNamePrefix("TaskAsync-");
executor.setKeepAliveSeconds(asyncTaskPool.getKeepAliveSeconds());
// DiscardOldestPolicy,抛弃最早的任务,将新任务加入队列。
// AbortPolicy,拒绝执行新任务,并抛出异常。
// CallerRunsPolicy,交由调用者线程执行新任务,如果调用者线程已关闭,则抛弃任务。
// DiscardPolicy,直接抛弃新任务。
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
// 设置包装器
executor.setTaskDecorator(TtlRunnable::get);
executor.initialize();
return executor;
}
/**
* 自定义线程池, 用法 @Async
*
* @return Executor
*/
@Override
public Executor getAsyncExecutor() {
ThreadPoolSettingModel asyncTaskPool = properties.getAsyncTaskPool();
// 自定义工厂
ThreadFactory factory = r -> new Thread(r, "DefaultAsync-" + new AtomicInteger(1).getAndIncrement());
// 自定义线程池
return new ThreadPoolExecutor(asyncTaskPool.getCorePoolSize(), asyncTaskPool.getMaxPoolSize(),
asyncTaskPool.getKeepAliveSeconds(), TimeUnit.SECONDS,
new ArrayBlockingQueue<>(asyncTaskPool.getQueueCapacity()), factory,
new ThreadPoolExecutor.CallerRunsPolicy());
}
@Bean("longTimeTaskAsync")
public ThreadPoolTaskExecutor longTimeTaskAsync() {
ThreadPoolSettingModel asyncTaskPool = properties.getAsyncTaskPool();
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(asyncTaskPool.getCorePoolSize());
executor.setMaxPoolSize(asyncTaskPool.getMaxPoolSize());
executor.setQueueCapacity(asyncTaskPool.getQueueCapacity());
executor.setThreadNamePrefix("LongTimeTaskAsync-");
executor.setKeepAliveSeconds(asyncTaskPool.getKeepAliveSeconds());
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
// 设置包装器
executor.setTaskDecorator(TtlRunnable::get);
executor.initialize();
return executor;
}
/**
* 自定义线程池, 用法, 注入到类中使用
*/
@Bean("taskAsync")
public ThreadPoolTaskExecutor taskAsync() {
ThreadPoolSettingModel asyncTaskPool = properties.getAsyncTaskPool();
// 用法 private ThreadPoolTaskExecutor taskExecutor
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(asyncTaskPool.getCorePoolSize());
executor.setMaxPoolSize(asyncTaskPool.getMaxPoolSize());
executor.setQueueCapacity(asyncTaskPool.getQueueCapacity());
executor.setThreadNamePrefix("TaskAsync-");
executor.setKeepAliveSeconds(asyncTaskPool.getKeepAliveSeconds());
// DiscardOldestPolicy,抛弃最早的任务,将新任务加入队列。
// AbortPolicy,拒绝执行新任务,并抛出异常。
// CallerRunsPolicy,交由调用者线程执行新任务,如果调用者线程已关闭,则抛弃任务。
// DiscardPolicy,直接抛弃新任务。
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
// 设置包装器
executor.setTaskDecorator(TtlRunnable::get);
executor.initialize();
return executor;
}
@Bean("longTimeTaskAsync")
public ThreadPoolTaskExecutor longTimeTaskAsync() {
ThreadPoolSettingModel asyncTaskPool = properties.getAsyncTaskPool();
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(asyncTaskPool.getCorePoolSize());
executor.setMaxPoolSize(asyncTaskPool.getMaxPoolSize());
executor.setQueueCapacity(asyncTaskPool.getQueueCapacity());
executor.setThreadNamePrefix("LongTimeTaskAsync-");
executor.setKeepAliveSeconds(asyncTaskPool.getKeepAliveSeconds());
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
// 设置包装器
executor.setTaskDecorator(TtlRunnable::get);
executor.initialize();
return executor;
}
}
@@ -27,20 +27,21 @@ import org.springframework.stereotype.Component;
@Component
public class DateStringArraysToListDateConverter implements Converter<String[], List<Date>> {
private static final ThreadLocal<SimpleDateFormat> SDF =
ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
@Override
public List<Date> convert(String[] source) {
if (source.length == 0) {
return Collections.emptyList();
}
return Arrays.stream(source).map(s -> {
try {
return SDF.get().parse(s);
} catch (ParseException e) {
throw new IllegalArgumentException("Invalid date string: " + s);
}
}).toList();
private static final ThreadLocal<SimpleDateFormat> SDF =
ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
@Override
public List<Date> convert(String[] source) {
if (source.length == 0) {
return Collections.emptyList();
}
return Arrays.stream(source).map(s -> {
try {
return SDF.get().parse(s);
} catch (ParseException e) {
throw new IllegalArgumentException("Invalid date string: " + s);
}
}).toList();
}
}
@@ -25,18 +25,19 @@ import org.springframework.stereotype.Component;
@Component
public class DateStringToDateConverter implements Converter<String, Date> {
private static final ThreadLocal<SimpleDateFormat> SDF =
ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
@Override
public Date convert(String source) {
if (StrUtil.isBlank(source)) {
return null;
}
try {
return SDF.get().parse(source);
} catch (ParseException e) {
throw new IllegalArgumentException("Invalid date string: " + source);
}
private static final ThreadLocal<SimpleDateFormat> SDF =
ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
@Override
public Date convert(String source) {
if (StrUtil.isBlank(source)) {
return null;
}
try {
return SDF.get().parse(source);
} catch (ParseException e) {
throw new IllegalArgumentException("Invalid date string: " + source);
}
}
}
@@ -47,72 +47,76 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@EnableWebMvc
public class ConfigurerAdapter implements WebMvcConfigurer {
@Autowired private DateStringArraysToListDateConverter dateStringArraysToListDateConverter;
@Autowired private DateStringToDateConverter dateStringToDateConverter;
@Autowired private KitFileLocalUploadHelper fileUploadPathHelper;
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOriginPattern("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
@Autowired
private DateStringArraysToListDateConverter dateStringArraysToListDateConverter;
@Autowired
private DateStringToDateConverter dateStringToDateConverter;
@Autowired
private KitFileLocalUploadHelper fileUploadPathHelper;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
String path = fileUploadPathHelper.getPath();
String pathUtl = "file:" + path.replace("\\", "/");
registry.addResourceHandler("/avatar/**").addResourceLocations(pathUtl).setCachePeriod(0);
registry.addResourceHandler("/file/**").addResourceLocations(pathUtl).setCachePeriod(0);
registry.addResourceHandler("/**").addResourceLocations("classpath:/META-INF/resources/").setCachePeriod(0);
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOriginPattern("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
/**
* Http消息转换器
*/
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
// 添加默认的 StringHttpMessageConverter
converters.add(new StringHttpMessageConverter(StandardCharsets.UTF_8));
// 配置 FastJsonHttpMessageConverter
FastJsonHttpMessageConverter fastJsonConverter = new FastJsonHttpMessageConverter();
List<MediaType> supportMediaTypeList = new ArrayList<>();
supportMediaTypeList.add(MediaType.APPLICATION_JSON);
FastJsonConfig config = new FastJsonConfig();
config.setDateFormat("yyyy-MM-dd HH:mm:ss");
// 开启引用检测, 枚举支持
/// 是否输出值为null的字段
// config.setWriterFeatures(JSONWriter.Feature.WriteMapNullValue);
/// 字段如果为null,输出为false,而非null
// config.setWriterFeatures(JSONWriter.Feature.WriteNullBooleanAsFalse);
/// 字段如果为null,输出为[],而非null
// config.setWriterFeatures(JSONWriter.Feature.WriteNullListAsEmpty);
/// 字符类型字段如果为null,输出为"",而非null
// config.setWriterFeatures(JSONWriter.Feature.WriteNullStringAsEmpty);
/// 太具体的数值会直接影响逻辑本身, 所以不要这个
// config.setWriterFeatures(JSONWriter.Feature.WriteNullNumberAsZero);
/// 全局Long转字符串,会导致基础框架本身异常, 所以不要这个
// config.setWriterFeatures(JSONWriter.Feature.WriteLongAsString);
config.setWriterFeatures(JSONWriter.Feature.WriteEnumUsingToString);
config.setWriterFeatures(JSONWriter.Feature.ReferenceDetection);
fastJsonConverter.setFastJsonConfig(config);
fastJsonConverter.setSupportedMediaTypes(supportMediaTypeList);
fastJsonConverter.setDefaultCharset(StandardCharsets.UTF_8);
// 将 FastJsonHttpMessageConverter 添加到列表末尾
converters.add(fastJsonConverter);
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
String path = fileUploadPathHelper.getPath();
String pathUtl = "file:" + path.replace("\\", "/");
registry.addResourceHandler("/avatar/**").addResourceLocations(pathUtl).setCachePeriod(0);
registry.addResourceHandler("/file/**").addResourceLocations(pathUtl).setCachePeriod(0);
registry.addResourceHandler("/**").addResourceLocations("classpath:/META-INF/resources/").setCachePeriod(0);
}
/**
* 自定义时间转换器
*/
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(dateStringArraysToListDateConverter);
registry.addConverter(dateStringToDateConverter);
}
/**
* Http消息转换器
*/
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
// 添加默认的 StringHttpMessageConverter
converters.add(new StringHttpMessageConverter(StandardCharsets.UTF_8));
// 配置 FastJsonHttpMessageConverter
FastJsonHttpMessageConverter fastJsonConverter = new FastJsonHttpMessageConverter();
List<MediaType> supportMediaTypeList = new ArrayList<>();
supportMediaTypeList.add(MediaType.APPLICATION_JSON);
FastJsonConfig config = new FastJsonConfig();
config.setDateFormat("yyyy-MM-dd HH:mm:ss");
// 开启引用检测, 枚举支持
/// 是否输出值为null的字段
// config.setWriterFeatures(JSONWriter.Feature.WriteMapNullValue);
/// 字段如果为null,输出为false,而非null
// config.setWriterFeatures(JSONWriter.Feature.WriteNullBooleanAsFalse);
/// 字段如果为null,输出为[],而非null
// config.setWriterFeatures(JSONWriter.Feature.WriteNullListAsEmpty);
/// 字符类型字段如果为null,输出为"",而非null
// config.setWriterFeatures(JSONWriter.Feature.WriteNullStringAsEmpty);
/// 太具体的数值会直接影响逻辑本身, 所以不要这个
// config.setWriterFeatures(JSONWriter.Feature.WriteNullNumberAsZero);
/// 全局Long转字符串,会导致基础框架本身异常, 所以不要这个
// config.setWriterFeatures(JSONWriter.Feature.WriteLongAsString);
config.setWriterFeatures(JSONWriter.Feature.WriteEnumUsingToString);
config.setWriterFeatures(JSONWriter.Feature.ReferenceDetection);
fastJsonConverter.setFastJsonConfig(config);
fastJsonConverter.setSupportedMediaTypes(supportMediaTypeList);
fastJsonConverter.setDefaultCharset(StandardCharsets.UTF_8);
// 将 FastJsonHttpMessageConverter 添加到列表末尾
converters.add(fastJsonConverter);
}
/**
* 自定义时间转换器
*/
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(dateStringArraysToListDateConverter);
registry.addConverter(dateStringToDateConverter);
}
}
@@ -28,35 +28,37 @@ import org.springframework.stereotype.Component;
*/
@Component
public class KitFileLocalUploadHelper {
/**
* 单位MB
*/
private static final int AVATAR_MAX_SIZE = 5;
private static final String PATH_WINDOWS = "C:\\${appName}\\file\\";
private static final String PATH_MAC = "/Users${username}/${appName}/file/";
private static final String PATH_LINUX = "/home/${appName}/file/";
@Value("${spring.application.name}") private String appName;
public String getPath() {
String os = System.getProperty("os.name");
if (os == null) {
// 默认使用 Linux 路径作为兜底方案
return PATH_LINUX.replace("${appName}", appName);
}
String lowerOs = os.toLowerCase();
if (lowerOs.startsWith(SystemConst.WIN)) {
return PATH_WINDOWS.replace("${appName}", appName);
} else if (lowerOs.startsWith(SystemConst.MAC)) {
String currentUserName = KitSystemUtil.getCurrentUserName();
if (StrUtil.isBlank(currentUserName)) {
return PATH_MAC.replace("${appName}", appName).replace("${username}", "");
}
return PATH_MAC.replace("${appName}", appName).replace("${username}", currentUserName);
}
return PATH_LINUX.replace("${appName}", appName);
}
/**
* 单位MB
*/
private static final int AVATAR_MAX_SIZE = 5;
private static final String PATH_WINDOWS = "C:\\${appName}\\file\\";
private static final String PATH_MAC = "/Users${username}/${appName}/file/";
private static final String PATH_LINUX = "/home/${appName}/file/";
@Value("${spring.application.name}")
private String appName;
public int getAvatarMaxSize() {
return AVATAR_MAX_SIZE;
public String getPath() {
String os = System.getProperty("os.name");
if (os == null) {
// 默认使用 Linux 路径作为兜底方案
return PATH_LINUX.replace("${appName}", appName);
}
String lowerOs = os.toLowerCase();
if (lowerOs.startsWith(SystemConst.WIN)) {
return PATH_WINDOWS.replace("${appName}", appName);
} else if (lowerOs.startsWith(SystemConst.MAC)) {
String currentUserName = KitSystemUtil.getCurrentUserName();
if (StrUtil.isBlank(currentUserName)) {
return PATH_MAC.replace("${appName}", appName).replace("${username}", "");
}
return PATH_MAC.replace("${appName}", appName).replace("${username}", currentUserName);
}
return PATH_LINUX.replace("${appName}", appName);
}
public int getAvatarMaxSize() {
return AVATAR_MAX_SIZE;
}
}
@@ -7,38 +7,38 @@ import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.stereotype.Component;
/**
* 解决启动io.undertow.websockets.jsr UT026010: Buffer pool was not set on WebSocketDeploymentInfo, the default pool will be
* used的警告
* 解决启动io.undertow.websockets.jsr UT026010: Buffer pool was not set on WebSocketDeploymentInfo, the default pool will be used的警告
*
* @author odboy
* @date 2025-10-30
*/
@Component
public class UndertowWebSocketConfig implements WebServerFactoryCustomizer<UndertowServletWebServerFactory> {
private int calculateThreadLocalCacheSize(int processorCount) {
// 每个I/O线程缓存2-4个缓冲区
return Math.max(2, processorCount / 2);
}
private int calculateMaxPoolSize(int processorCount) {
// 基础值 + 根据CPU核心数调整
int baseSize = 100;
return baseSize + (processorCount * 20);
}
private int calculateThreadLocalCacheSize(int processorCount) {
// 每个I/O线程缓存2-4个缓冲区
return Math.max(2, processorCount / 2);
}
@Override
public void customize(UndertowServletWebServerFactory factory) {
int processorCount = Runtime.getRuntime().availableProcessors();
int bufferSize = 1024;
int maxPoolSize = calculateMaxPoolSize(processorCount);
int threadLocalCacheSize = calculateThreadLocalCacheSize(processorCount);
private int calculateMaxPoolSize(int processorCount) {
// 基础值 + 根据CPU核心数调整
int baseSize = 100;
return baseSize + (processorCount * 20);
}
factory.addDeploymentInfoCustomizers(deploymentInfo -> {
WebSocketDeploymentInfo webSocketDeploymentInfo = new WebSocketDeploymentInfo();
webSocketDeploymentInfo.setBuffers(
new DefaultByteBufferPool(false, bufferSize, maxPoolSize, threadLocalCacheSize));
deploymentInfo.addServletContextAttribute("io.undertow.websockets.jsr.WebSocketDeploymentInfo",
webSocketDeploymentInfo);
});
}
@Override
public void customize(UndertowServletWebServerFactory factory) {
int processorCount = Runtime.getRuntime().availableProcessors();
int bufferSize = 1024;
int maxPoolSize = calculateMaxPoolSize(processorCount);
int threadLocalCacheSize = calculateThreadLocalCacheSize(processorCount);
factory.addDeploymentInfoCustomizers(deploymentInfo -> {
WebSocketDeploymentInfo webSocketDeploymentInfo = new WebSocketDeploymentInfo();
webSocketDeploymentInfo.setBuffers(
new DefaultByteBufferPool(false, bufferSize, maxPoolSize, threadLocalCacheSize));
deploymentInfo.addServletContextAttribute("io.undertow.websockets.jsr.WebSocketDeploymentInfo",
webSocketDeploymentInfo);
});
}
}
@@ -29,8 +29,8 @@ import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketEndpointConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
@@ -29,35 +29,36 @@ import lombok.extern.slf4j.Slf4j;
*/
@Slf4j
public class KitWsClientManager {
/**
* concurrent包的线程安全Map, 用来存放每个客户端对应的MyWebSocket对象。(分布式必出问题)
*/
private static final Map<String, KitWsServer> clientMap = new ConcurrentHashMap<>();
public static void addClient(String sid, KitWsServer wsServer) {
// 如果存在就先删除一个, 防止重复推送消息
removeClient(sid);
clientMap.put(sid, wsServer);
}
/**
* concurrent包的线程安全Map, 用来存放每个客户端对应的MyWebSocket对象。(分布式必出问题)
*/
private static final Map<String, KitWsServer> clientMap = new ConcurrentHashMap<>();
public static void removeClient(String sid) {
KitWsServer wsServer = clientMap.get(sid);
if (wsServer != null) {
try {
log.info("关闭session, sid={}", sid);
wsServer.getSession().close();
} catch (Exception e) {
log.error("Close session failed, sid={}", sid, e);
}
clientMap.remove(sid);
}
}
public static void addClient(String sid, KitWsServer wsServer) {
// 如果存在就先删除一个, 防止重复推送消息
removeClient(sid);
clientMap.put(sid, wsServer);
}
public static Collection<KitWsServer> getAllClient() {
return clientMap.values();
public static void removeClient(String sid) {
KitWsServer wsServer = clientMap.get(sid);
if (wsServer != null) {
try {
log.info("关闭session, sid={}", sid);
wsServer.getSession().close();
} catch (Exception e) {
log.error("Close session failed, sid={}", sid, e);
}
clientMap.remove(sid);
}
}
public static KitWsServer getClientBySid(String sid) {
return clientMap.get(sid);
}
public static Collection<KitWsServer> getAllClient() {
return clientMap.values();
}
public static KitWsServer getClientBySid(String sid) {
return clientMap.get(sid);
}
}
@@ -23,11 +23,12 @@ import lombok.Setter;
@Getter
@Setter
public class KitWsMessage extends KitObject {
private String bizCode;
private String data;
public KitWsMessage(String bizCode, String data) {
this.bizCode = bizCode;
this.data = data;
}
private String bizCode;
private String data;
public KitWsMessage(String bizCode, String data) {
this.bizCode = bizCode;
this.data = data;
}
}
@@ -35,116 +35,117 @@ import org.springframework.stereotype.Component;
@ServerEndpoint("/websocket/{sid}")
@Getter
public class KitWsServer {
/**
* 与某个客户端的连接会话, 需要通过它来给客户端发送数据
*/
private Session session;
/**
* 接收sid
*/
private String sid = "";
/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session, @PathParam("sid") String sid) {
this.session = session;
// {username}#{bizCode}#{contextParams}
this.sid = sid;
KitWsClientManager.addClient(sid, this);
/**
* 与某个客户端的连接会话, 需要通过它来给客户端发送数据
*/
private Session session;
/**
* 接收sid
*/
private String sid = "";
/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session, @PathParam("sid") String sid) {
this.session = session;
// {username}#{bizCode}#{contextParams}
this.sid = sid;
KitWsClientManager.addClient(sid, this);
}
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息
*/
@OnMessage
public void onMessage(String message, Session session) {
KitWsMessage wsMessage = JSON.parseObject(message, KitWsMessage.class);
String bizCode = wsMessage.getBizCode();
Object data = wsMessage.getData();
log.info("收到来 sid={} 的信息: message={}, bizCode={}, data={}", sid, message, bizCode,
JSON.toJSONString(data));
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
try {
KitWsClientManager.removeClient(this.sid);
} catch (Exception e) {
log.error("WebSocket onClose error, sid={}", this.sid, e);
}
}
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息
*/
@OnMessage
public void onMessage(String message, Session session) {
KitWsMessage wsMessage = JSON.parseObject(message, KitWsMessage.class);
String bizCode = wsMessage.getBizCode();
Object data = wsMessage.getData();
log.info("收到来 sid={} 的信息: message={}, bizCode={}, data={}", sid, message, bizCode,
JSON.toJSONString(data));
@OnError
public void onError(Session session, Throwable error) {
// log.error("WebSocket sid={} 发生错误", this.sid, error);
try {
KitWsClientManager.removeClient(this.sid);
} catch (Exception e) {
log.error("WebSocket onClose error, sid={}", this.sid, e);
}
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
try {
KitWsClientManager.removeClient(this.sid);
} catch (Exception e) {
log.error("WebSocket onClose error, sid={}", this.sid, e);
/**
* 实现服务器主动推送
*/
private void innerSendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
}
/**
* 精准推送消息
*/
public void sendMessage(KitWsMessage message, @PathParam("sid") String sid) {
String body = JSON.toJSONString(message);
// log.info("推送消息到{}, 推送内容:{}", sid, message);
try {
if (sid == null) {
sendToAll(body);
} else {
KitWsServer wsClient = KitWsClientManager.getClientBySid(sid);
if (wsClient != null) {
wsClient.innerSendMessage(body);
}
}
} catch (Exception ignored) {
}
}
@OnError
public void onError(Session session, Throwable error) {
// log.error("WebSocket sid={} 发生错误", this.sid, error);
try {
KitWsClientManager.removeClient(this.sid);
} catch (Exception e) {
log.error("WebSocket onClose error, sid={}", this.sid, e);
}
/**
* 群发消息
*/
public void sendToAll(String message) {
for (KitWsServer item : KitWsClientManager.getAllClient()) {
try {
item.innerSendMessage(message);
} catch (IOException e) {
log.error("发送消息给 sid={} 失败", item.sid, e);
KitWsClientManager.removeClient(item.sid);
}
}
}
/**
* 实现服务器主动推送
*/
private void innerSendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
KitWsServer that = (KitWsServer) o;
return Objects.equals(session, that.session) && Objects.equals(sid, that.sid);
}
/**
* 精准推送消息
*/
public void sendMessage(KitWsMessage message, @PathParam("sid") String sid) {
String body = JSON.toJSONString(message);
// log.info("推送消息到{}, 推送内容:{}", sid, message);
try {
if (sid == null) {
sendToAll(body);
} else {
KitWsServer wsClient = KitWsClientManager.getClientBySid(sid);
if (wsClient != null) {
wsClient.innerSendMessage(body);
}
}
} catch (Exception ignored) {
}
}
/**
* 群发消息
*/
public void sendToAll(String message) {
for (KitWsServer item : KitWsClientManager.getAllClient()) {
try {
item.innerSendMessage(message);
} catch (IOException e) {
log.error("发送消息给 sid={} 失败", item.sid, e);
KitWsClientManager.removeClient(item.sid);
}
}
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
KitWsServer that = (KitWsServer)o;
return Objects.equals(session, that.session) && Objects.equals(sid, that.sid);
}
@Override
public int hashCode() {
return Objects.hash(session, sid);
}
@Override
public int hashCode() {
return Objects.hash(session, sid);
}
}
@@ -23,7 +23,8 @@ import lombok.Setter;
@Getter
@Setter
public class WsSidVo extends KitObject {
private String username;
private String bizCode;
private String param;
private String username;
private String bizCode;
private String param;
}
@@ -28,16 +28,17 @@ import lombok.experimental.UtilityClass;
*/
@UtilityClass
public class WsMessageUtil {
public static WsSidVo parseSid(String sid) {
// sid {username}_{bizCode}_{param}
String[] sids = sid.split("_");
if (sids.length != 3) {
throw new BadRequestException("sid格式异常");
}
WsSidVo sidVo = new WsSidVo();
sidVo.setUsername(sids[0]);
sidVo.setBizCode(sids[1]);
sidVo.setParam(sids[2]);
return sidVo;
public static WsSidVo parseSid(String sid) {
// sid {username}_{bizCode}_{param}
String[] sids = sid.split("_");
if (sids.length != 3) {
throw new BadRequestException("sid格式异常");
}
WsSidVo sidVo = new WsSidVo();
sidVo.setUsername(sids[0]);
sidVo.setBizCode(sids[1]);
sidVo.setParam(sids[2]);
return sidVo;
}
}
@@ -40,66 +40,66 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl
@UtilityClass
public class KitAnonTagUtil {
/**
* 获取匿名标记的URL
*
* @param applicationContext /
* @return /
*/
public static Map<String, Set<String>> getAnonymousUrl(final ApplicationContext applicationContext) {
RequestMappingHandlerMapping requestMappingHandlerMapping =
applicationContext.getBean(RequestMappingHandlerMapping.class);
Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = requestMappingHandlerMapping.getHandlerMethods();
Map<String, java.util.Set<String>> anonymousUrls = new HashMap<>(8);
// 获取匿名标记
Set<String> get = new HashSet<>();
Set<String> post = new HashSet<>();
Set<String> put = new HashSet<>();
Set<String> patch = new HashSet<>();
Set<String> delete = new HashSet<>();
Set<String> all = new HashSet<>();
for (Map.Entry<RequestMappingInfo, HandlerMethod> infoEntry : handlerMethodMap.entrySet()) {
HandlerMethod handlerMethod = infoEntry.getValue();
AnonymousAccess anonymousAccess = handlerMethod.getMethodAnnotation(AnonymousAccess.class);
if (null != anonymousAccess && infoEntry.getKey() != null) {
List<RequestMethod> requestMethods =
new ArrayList<>(infoEntry.getKey().getMethodsCondition().getMethods());
RequestMethodEnum request = RequestMethodEnum.find(
requestMethods.isEmpty() ? RequestMethodEnum.ALL.getType() : requestMethods.get(0).name());
PathPatternsRequestCondition pathPatternsCondition = infoEntry.getKey().getPathPatternsCondition();
if (pathPatternsCondition != null) {
switch (Objects.requireNonNull(request)) {
case GET -> get.addAll(pathPatternsCondition.getPatternValues());
case POST -> post.addAll(pathPatternsCondition.getPatternValues());
case PUT -> put.addAll(pathPatternsCondition.getPatternValues());
case PATCH -> patch.addAll(pathPatternsCondition.getPatternValues());
case DELETE -> delete.addAll(pathPatternsCondition.getPatternValues());
default -> all.addAll(pathPatternsCondition.getPatternValues());
}
}
}
/**
* 获取匿名标记的URL
*
* @param applicationContext /
* @return /
*/
public static Map<String, Set<String>> getAnonymousUrl(final ApplicationContext applicationContext) {
RequestMappingHandlerMapping requestMappingHandlerMapping =
applicationContext.getBean(RequestMappingHandlerMapping.class);
Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = requestMappingHandlerMapping.getHandlerMethods();
Map<String, java.util.Set<String>> anonymousUrls = new HashMap<>(8);
// 获取匿名标记
Set<String> get = new HashSet<>();
Set<String> post = new HashSet<>();
Set<String> put = new HashSet<>();
Set<String> patch = new HashSet<>();
Set<String> delete = new HashSet<>();
Set<String> all = new HashSet<>();
for (Map.Entry<RequestMappingInfo, HandlerMethod> infoEntry : handlerMethodMap.entrySet()) {
HandlerMethod handlerMethod = infoEntry.getValue();
AnonymousAccess anonymousAccess = handlerMethod.getMethodAnnotation(AnonymousAccess.class);
if (null != anonymousAccess && infoEntry.getKey() != null) {
List<RequestMethod> requestMethods =
new ArrayList<>(infoEntry.getKey().getMethodsCondition().getMethods());
RequestMethodEnum request = RequestMethodEnum.find(
requestMethods.isEmpty() ? RequestMethodEnum.ALL.getType() : requestMethods.get(0).name());
PathPatternsRequestCondition pathPatternsCondition = infoEntry.getKey().getPathPatternsCondition();
if (pathPatternsCondition != null) {
switch (Objects.requireNonNull(request)) {
case GET -> get.addAll(pathPatternsCondition.getPatternValues());
case POST -> post.addAll(pathPatternsCondition.getPatternValues());
case PUT -> put.addAll(pathPatternsCondition.getPatternValues());
case PATCH -> patch.addAll(pathPatternsCondition.getPatternValues());
case DELETE -> delete.addAll(pathPatternsCondition.getPatternValues());
default -> all.addAll(pathPatternsCondition.getPatternValues());
}
}
anonymousUrls.put(RequestMethodEnum.GET.getType(), get);
anonymousUrls.put(RequestMethodEnum.POST.getType(), post);
anonymousUrls.put(RequestMethodEnum.PUT.getType(), put);
anonymousUrls.put(RequestMethodEnum.PATCH.getType(), patch);
anonymousUrls.put(RequestMethodEnum.DELETE.getType(), delete);
anonymousUrls.put(RequestMethodEnum.ALL.getType(), all);
return anonymousUrls;
}
}
anonymousUrls.put(RequestMethodEnum.GET.getType(), get);
anonymousUrls.put(RequestMethodEnum.POST.getType(), post);
anonymousUrls.put(RequestMethodEnum.PUT.getType(), put);
anonymousUrls.put(RequestMethodEnum.PATCH.getType(), patch);
anonymousUrls.put(RequestMethodEnum.DELETE.getType(), delete);
anonymousUrls.put(RequestMethodEnum.ALL.getType(), all);
return anonymousUrls;
}
/**
* 获取所有匿名标记的URL
*
* @param applicationContext /
* @return /
*/
public static Set<String> getAllAnonymousUrl(final ApplicationContext applicationContext) {
Set<String> allUrl = new HashSet<>();
Map<String, Set<String>> anonymousUrls = getAnonymousUrl(applicationContext);
for (Map.Entry<String, Set<String>> entry : anonymousUrls.entrySet()) {
allUrl.addAll(anonymousUrls.get(entry.getKey()));
}
return allUrl;
/**
* 获取所有匿名标记的URL
*
* @param applicationContext /
* @return /
*/
public static Set<String> getAllAnonymousUrl(final ApplicationContext applicationContext) {
Set<String> allUrl = new HashSet<>();
Map<String, Set<String>> anonymousUrls = getAnonymousUrl(applicationContext);
for (Map.Entry<String, Set<String>> entry : anonymousUrls.entrySet()) {
allUrl.addAll(anonymousUrls.get(entry.getKey()));
}
return allUrl;
}
}
@@ -30,20 +30,20 @@ import lombok.experimental.UtilityClass;
@UtilityClass
public final class KitBase64SecretUtil {
public static String build() {
// 128位uuid
return Base64Encoder.encode(getLongUuid(4).getBytes(StandardCharsets.UTF_8));
}
public static String build() {
// 128位uuid
return Base64Encoder.encode(getLongUuid(4).getBytes(StandardCharsets.UTF_8));
}
public static String getLongUuid(int bit) {
StringBuilder result = new StringBuilder();
for (int i = 0; i < bit; i++) {
result.append(IdUtil.simpleUUID());
}
return result.toString();
public static String getLongUuid(int bit) {
StringBuilder result = new StringBuilder();
for (int i = 0; i < bit; i++) {
result.append(IdUtil.simpleUUID());
}
return result.toString();
}
public static void main(String[] args) {
System.out.println(build());
}
public static void main(String[] args) {
System.out.println(build());
}
}
@@ -27,127 +27,127 @@ import lombok.experimental.UtilityClass;
@UtilityClass
public final class KitBigDecimalUtil {
/**
* 将对象转换为 BigDecimal
*
* @param obj 输入对象
* @return 转换后的 BigDecimal
*/
private static BigDecimal toBigDecimal(Object obj) {
if (obj instanceof BigDecimal b) {
return b;
} else if (obj instanceof Long l) {
return BigDecimal.valueOf(l);
} else if (obj instanceof Integer i) {
return BigDecimal.valueOf(i);
} else if (obj instanceof Double d) {
return new BigDecimal(d);
} else {
throw new IllegalArgumentException("Unsupported type");
}
/**
* 将对象转换为 BigDecimal
*
* @param obj 输入对象
* @return 转换后的 BigDecimal
*/
private static BigDecimal toBigDecimal(Object obj) {
if (obj instanceof BigDecimal b) {
return b;
} else if (obj instanceof Long l) {
return BigDecimal.valueOf(l);
} else if (obj instanceof Integer i) {
return BigDecimal.valueOf(i);
} else if (obj instanceof Double d) {
return new BigDecimal(d);
} else {
throw new IllegalArgumentException("Unsupported type");
}
}
/**
* 加法
*
* @param a 加数
* @param b 加数
* @return 两个加数的和, 保留两位小数
*/
public static BigDecimal add(Object a, Object b) {
BigDecimal bdA = toBigDecimal(a);
BigDecimal bdB = toBigDecimal(b);
return bdA.add(bdB).setScale(2, RoundingMode.HALF_UP);
}
/**
* 加法
*
* @param a 加数
* @param b 加数
* @return 两个加数的和, 保留两位小数
*/
public static BigDecimal add(Object a, Object b) {
BigDecimal bdA = toBigDecimal(a);
BigDecimal bdB = toBigDecimal(b);
return bdA.add(bdB).setScale(2, RoundingMode.HALF_UP);
}
/**
* 减法
*
* @param a 被减数
* @param b 减数
* @return 两数的差, 保留两位小数
*/
public static BigDecimal subtract(Object a, Object b) {
BigDecimal bdA = toBigDecimal(a);
BigDecimal bdB = toBigDecimal(b);
return bdA.subtract(bdB).setScale(2, RoundingMode.HALF_UP);
}
/**
* 减法
*
* @param a 被减数
* @param b 减数
* @return 两数的差, 保留两位小数
*/
public static BigDecimal subtract(Object a, Object b) {
BigDecimal bdA = toBigDecimal(a);
BigDecimal bdB = toBigDecimal(b);
return bdA.subtract(bdB).setScale(2, RoundingMode.HALF_UP);
}
/**
* 乘法
*
* @param a 乘数
* @param b 乘数
* @return 两个乘数的积, 保留两位小数
*/
public static BigDecimal multiply(Object a, Object b) {
BigDecimal bdA = toBigDecimal(a);
BigDecimal bdB = toBigDecimal(b);
return bdA.multiply(bdB).setScale(2, RoundingMode.HALF_UP);
}
/**
* 乘法
*
* @param a 乘数
* @param b 乘数
* @return 两个乘数的积, 保留两位小数
*/
public static BigDecimal multiply(Object a, Object b) {
BigDecimal bdA = toBigDecimal(a);
BigDecimal bdB = toBigDecimal(b);
return bdA.multiply(bdB).setScale(2, RoundingMode.HALF_UP);
}
/**
* 除法
*
* @param a 被除数
* @param b 除数
* @return 两数的商,保留两位小数
*/
public static BigDecimal divide(Object a, Object b) {
BigDecimal bdA = toBigDecimal(a);
BigDecimal bdB = toBigDecimal(b);
return bdA.divide(bdB, 2, RoundingMode.HALF_UP);
}
/**
* 除法
*
* @param a 被除数
* @param b 除数
* @return 两数的商,保留两位小数
*/
public static BigDecimal divide(Object a, Object b) {
BigDecimal bdA = toBigDecimal(a);
BigDecimal bdB = toBigDecimal(b);
return bdA.divide(bdB, 2, RoundingMode.HALF_UP);
}
/**
* 除法
*
* @param a 被除数
* @param b 除数
* @param scale 保留小数位数
* @return 两数的商,保留两位小数
*/
public static BigDecimal divide(Object a, Object b, int scale) {
BigDecimal bdA = toBigDecimal(a);
BigDecimal bdB = toBigDecimal(b);
return bdA.divide(bdB, scale, RoundingMode.HALF_UP);
}
/**
* 除法
*
* @param a 被除数
* @param b 除数
* @param scale 保留小数位数
* @return 两数的商,保留两位小数
*/
public static BigDecimal divide(Object a, Object b, int scale) {
BigDecimal bdA = toBigDecimal(a);
BigDecimal bdB = toBigDecimal(b);
return bdA.divide(bdB, scale, RoundingMode.HALF_UP);
}
/**
* 分转元
*
* @param obj 分的金额
* @return 转换后的元,保留两位小数
*/
public static BigDecimal centsToYuan(Object obj) {
BigDecimal cents = toBigDecimal(obj);
return cents.divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
}
/**
* 分转元
*
* @param obj 分的金额
* @return 转换后的元,保留两位小数
*/
public static BigDecimal centsToYuan(Object obj) {
BigDecimal cents = toBigDecimal(obj);
return cents.divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
}
/**
* 元转分
*
* @param obj 元的金额
* @return 转换后的分
*/
public static Long yuanToCents(Object obj) {
BigDecimal yuan = toBigDecimal(obj);
return yuan.multiply(BigDecimal.valueOf(100)).setScale(0, RoundingMode.HALF_UP).longValue();
}
/**
* 元转分
*
* @param obj 元的金额
* @return 转换后的分
*/
public static Long yuanToCents(Object obj) {
BigDecimal yuan = toBigDecimal(obj);
return yuan.multiply(BigDecimal.valueOf(100)).setScale(0, RoundingMode.HALF_UP).longValue();
}
public static void main(String[] args) {
BigDecimal num1 = new BigDecimal("10.123");
BigDecimal num2 = new BigDecimal("2.456");
public static void main(String[] args) {
BigDecimal num1 = new BigDecimal("10.123");
BigDecimal num2 = new BigDecimal("2.456");
System.out.println("加法结果: " + add(num1, num2));
System.out.println("减法结果: " + subtract(num1, num2));
System.out.println("乘法结果: " + multiply(num1, num2));
System.out.println("除法结果: " + divide(num1, num2));
System.out.println("加法结果: " + add(num1, num2));
System.out.println("减法结果: " + subtract(num1, num2));
System.out.println("乘法结果: " + multiply(num1, num2));
System.out.println("除法结果: " + divide(num1, num2));
Long cents = 12345L;
System.out.println("分转元结果: " + centsToYuan(cents));
Long cents = 12345L;
System.out.println("分转元结果: " + centsToYuan(cents));
BigDecimal yuan = new BigDecimal("123.45");
System.out.println("元转分结果: " + yuanToCents(yuan));
}
BigDecimal yuan = new BigDecimal("123.45");
System.out.println("元转分结果: " + yuanToCents(yuan));
}
}
@@ -30,44 +30,45 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
@UtilityClass
public final class KitBrowserUtil {
private static final String UNKNOWN = "unknown";
/**
* 获取ip地址
*/
public static String getIp(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.isEmpty() || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.isEmpty() || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.isEmpty() || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
String comma = ",";
String localhost = "127.0.0.1";
if (ip.contains(comma)) {
ip = ip.split(",")[0];
}
if (localhost.equals(ip)) {
// 获取本机真正的ip地址
try {
ip = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
log.error("获取本机地址失败", e);
}
}
return ip;
}
private static final String UNKNOWN = "unknown";
/**
* 获取浏览器
*/
public static String getVersion(HttpServletRequest request) {
UserAgent ua = UserAgentUtil.parse(request.getHeader("User-Agent"));
String browser = ua.getBrowser().toString() + " " + ua.getVersion();
return browser.replace(".0.0.0", "");
/**
* 获取ip地址
*/
public static String getIp(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.isEmpty() || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.isEmpty() || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.isEmpty() || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
String comma = ",";
String localhost = "127.0.0.1";
if (ip.contains(comma)) {
ip = ip.split(",")[0];
}
if (localhost.equals(ip)) {
// 获取本机真正的ip地址
try {
ip = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
log.error("获取本机地址失败", e);
}
}
return ip;
}
/**
* 获取浏览器
*/
public static String getVersion(HttpServletRequest request) {
UserAgent ua = UserAgentUtil.parse(request.getHeader("User-Agent"));
String browser = ua.getBrowser().toString() + " " + ua.getVersion();
return browser.replace(".0.0.0", "");
}
}
@@ -27,12 +27,13 @@ import lombok.experimental.UtilityClass;
*/
@UtilityClass
public final class KitClassUtil {
@SuppressWarnings({"unchecked", "all"})
public static List<Field> getAllFields(Class clazz, List<Field> fields) {
if (clazz != null) {
fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
getAllFields(clazz.getSuperclass(), fields);
}
return fields;
@SuppressWarnings({"unchecked", "all"})
public static List<Field> getAllFields(Class clazz, List<Field> fields) {
if (clazz != null) {
fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
getAllFields(clazz.getSuperclass(), fields);
}
return fields;
}
}
@@ -26,23 +26,23 @@ import lombok.experimental.UtilityClass;
@UtilityClass
public final class KitCloseUtil {
public static void close(Closeable closeable) {
if (null != closeable) {
try {
closeable.close();
} catch (Exception e) {
// 静默关闭
}
}
public static void close(Closeable closeable) {
if (null != closeable) {
try {
closeable.close();
} catch (Exception e) {
// 静默关闭
}
}
}
public static void close(AutoCloseable closeable) {
if (null != closeable) {
try {
closeable.close();
} catch (Exception e) {
// 静默关闭
}
}
public static void close(AutoCloseable closeable) {
if (null != closeable) {
try {
closeable.close();
} catch (Exception e) {
// 静默关闭
}
}
}
}
@@ -40,183 +40,184 @@ import lombok.experimental.UtilityClass;
*/
@UtilityClass
public final class KitCollUtil extends cn.hutool.core.collection.CollUtil {
private static <T, K> Predicate<T> distinctPredicate(Function<? super T, ? extends K> keyMapper) {
Map<Object, Boolean> map = new HashMap<>(3);
return (t) -> null == map.putIfAbsent(keyMapper.apply(t), true);
}
/**
* 流根据对象属性去重
* <p>
* 使用方式: list = CollUtil.streamDistinct(list, Object::getXXX)
*/
public static <T, K> List<T> streamDistinct(List<T> data, Function<? super T, ? extends K> keyMapper) {
return data.stream().filter(distinctPredicate(keyMapper)).toList();
}
private static <T, K> Predicate<T> distinctPredicate(Function<? super T, ? extends K> keyMapper) {
Map<Object, Boolean> map = new HashMap<>(3);
return (t) -> null == map.putIfAbsent(keyMapper.apply(t), true);
}
/**
* 流根据对象属性自然排序
* <p>
* 使用方式: list = CollUtil.streamAscSort(list, Object::getXXX)
*
* @param data /
* @param keyExtractor /
* @param <T> /
* @param <U> /
* @return /
*/
public static <T, U extends Comparable<? super U>> List<T> streamAscSort(List<T> data,
Function<? super T, ? extends U> keyExtractor) {
return data.stream().sorted(Comparator.comparing(keyExtractor)).toList();
}
/**
* 流根据对象属性去重
* <p>
* 使用方式: list = CollUtil.streamDistinct(list, Object::getXXX)
*/
public static <T, K> List<T> streamDistinct(List<T> data, Function<? super T, ? extends K> keyMapper) {
return data.stream().filter(distinctPredicate(keyMapper)).toList();
}
/**
* 流根据对象属性倒转排序
* <p>
* 使用方式: list = CollUtil.streamDescSort(list, Object::getXXX)
*
* @param data /
* @param keyExtractor /
* @param <T> /
* @param <U> /
* @return /
*/
public static <T, U extends Comparable<? super U>> List<T> streamDescSort(List<T> data,
Function<? super T, ? extends U> keyExtractor) {
return data.stream().sorted(Comparator.comparing(keyExtractor).reversed()).toList();
}
/**
* 流根据对象属性自然排序
* <p>
* 使用方式: list = CollUtil.streamAscSort(list, Object::getXXX)
*
* @param data /
* @param keyExtractor /
* @param <T> /
* @param <U> /
* @return /
*/
public static <T, U extends Comparable<? super U>> List<T> streamAscSort(List<T> data,
Function<? super T, ? extends U> keyExtractor) {
return data.stream().sorted(Comparator.comparing(keyExtractor)).toList();
}
/**
* 流拆分对象为键值对
* <p>
* 使用方式: map = CollUtil.streamToMap(list, Object::getXXKey, Object::getXXValue)
*
* @param data /
* @param keyMapper /
* @param valueMapper /
* @param <T> /
* @param <K> /
* @param <U> /
* @return /
*/
public static <T, K, U> Map<K, U> streamToMap(List<T> data, Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper) {
// 根据key去重再并流
return streamDistinct(data, keyMapper).stream().collect(Collectors.toMap(keyMapper, valueMapper));
}
/**
* 流根据对象属性倒转排序
* <p>
* 使用方式: list = CollUtil.streamDescSort(list, Object::getXXX)
*
* @param data /
* @param keyExtractor /
* @param <T> /
* @param <U> /
* @return /
*/
public static <T, U extends Comparable<? super U>> List<T> streamDescSort(List<T> data,
Function<? super T, ? extends U> keyExtractor) {
return data.stream().sorted(Comparator.comparing(keyExtractor).reversed()).toList();
}
/**
* 流对List<Double>求和
* <p>
* 使用方式: total = CollUtil.streamDoubleAdd(doubleList)
*
* @param data /
* @return /
*/
public static Double streamDoubleAdd(List<Double> data) {
if (KitCollUtil.isNotEmpty(data)) {
return data.stream().filter(Objects::nonNull).reduce(Double::sum).orElse(0.0);
}
return 0.0;
}
/**
* 流拆分对象为键值对
* <p>
* 使用方式: map = CollUtil.streamToMap(list, Object::getXXKey, Object::getXXValue)
*
* @param data /
* @param keyMapper /
* @param valueMapper /
* @param <T> /
* @param <K> /
* @param <U> /
* @return /
*/
public static <T, K, U> Map<K, U> streamToMap(List<T> data, Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper) {
// 根据key去重再并流
return streamDistinct(data, keyMapper).stream().collect(Collectors.toMap(keyMapper, valueMapper));
}
/**
* 流对List<BigDecimal>求和
* <p>
* 使用方式: total = CollUtil.streamBigDecimalAdd(doubleList)
*
* @param data /
* @return /
*/
public static BigDecimal streamBigDecimalAdd(List<BigDecimal> data) {
if (KitCollUtil.isNotEmpty(data)) {
return data.stream().filter(Objects::nonNull).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
}
return BigDecimal.ZERO;
/**
* 流对List<Double>求和
* <p>
* 使用方式: total = CollUtil.streamDoubleAdd(doubleList)
*
* @param data /
* @return /
*/
public static Double streamDoubleAdd(List<Double> data) {
if (KitCollUtil.isNotEmpty(data)) {
return data.stream().filter(Objects::nonNull).reduce(Double::sum).orElse(0.0);
}
return 0.0;
}
/**
* 流对List<Object>中的某一BigDecimal属性求和
* <p>
* 使用方式: total = CollUtil.streamBigDecimalAdd(list, XXX)
*
* @param data /
* @return /
*/
public static <T> BigDecimal streamBigDecimalAdd(List<T> data, String elementName) {
return data.stream().filter(Objects::nonNull).map(m -> {
// 反射, 取集合中各元素的某属性
BigDecimal that;
try {
Field field = m.getClass().getDeclaredField(elementName);
field.setAccessible(true);
that = (BigDecimal)field.get(m);
} catch (Exception e) {
// 忽略异常
that = BigDecimal.ZERO;
}
return that;
}).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
/**
* 流对List<BigDecimal>求和
* <p>
* 使用方式: total = CollUtil.streamBigDecimalAdd(doubleList)
*
* @param data /
* @return /
*/
public static BigDecimal streamBigDecimalAdd(List<BigDecimal> data) {
if (KitCollUtil.isNotEmpty(data)) {
return data.stream().filter(Objects::nonNull).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
}
return BigDecimal.ZERO;
}
/**
* 流对List<BigDecimal>取最大值
* <p>
* 使用方式: max = CollUtil.streamBigDecimalMax(list)
*
* @param data /
* @return /
*/
public static BigDecimal streamBigDecimalMax(List<BigDecimal> data) {
return data.stream().filter(Objects::nonNull).reduce(BigDecimal::max).orElse(null);
}
/**
* 流对List<Object>中的某一BigDecimal属性求和
* <p>
* 使用方式: total = CollUtil.streamBigDecimalAdd(list, XXX)
*
* @param data /
* @return /
*/
public static <T> BigDecimal streamBigDecimalAdd(List<T> data, String elementName) {
return data.stream().filter(Objects::nonNull).map(m -> {
// 反射, 取集合中各元素的某属性
BigDecimal that;
try {
Field field = m.getClass().getDeclaredField(elementName);
field.setAccessible(true);
that = (BigDecimal) field.get(m);
} catch (Exception e) {
// 忽略异常
that = BigDecimal.ZERO;
}
return that;
}).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
}
/**
* 流对List<BigDecimal>取最小值
* <p>
* 使用方式: max = CollUtil.streamBigDecimalMin(list)
*
* @param data /
* @return /
*/
public static BigDecimal streamBigDecimalMin(List<BigDecimal> data) {
return data.stream().filter(Objects::nonNull).reduce(BigDecimal::min).orElse(null);
}
/**
* 流对List<BigDecimal>取最大值
* <p>
* 使用方式: max = CollUtil.streamBigDecimalMax(list)
*
* @param data /
* @return /
*/
public static BigDecimal streamBigDecimalMax(List<BigDecimal> data) {
return data.stream().filter(Objects::nonNull).reduce(BigDecimal::max).orElse(null);
}
/**
* 流对List<BigDecimal>取平均值
* <p>
* 使用方式: avg = CollUtil.streamBigDecimalAvg(list)
*
* @param data /
* @return /
*/
public static BigDecimal streamBigDecimalAvg(List<BigDecimal> data) {
return data.stream().filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add)
.divide(BigDecimal.valueOf(data.size()), 6, RoundingMode.FLOOR);
}
/**
* 流对List<BigDecimal>取最小值
* <p>
* 使用方式: max = CollUtil.streamBigDecimalMin(list)
*
* @param data /
* @return /
*/
public static BigDecimal streamBigDecimalMin(List<BigDecimal> data) {
return data.stream().filter(Objects::nonNull).reduce(BigDecimal::min).orElse(null);
}
public static void main(String[] args) {
List<KitSelectOptionVo> testData = new ArrayList<>();
KitSelectOptionVo selectOption1 = KitSelectOptionVo.builder().label("苹果").value("apple").build();
KitSelectOptionVo selectOption2 = KitSelectOptionVo.builder().label("苹果").value("apple").build();
KitSelectOptionVo selectOption3 = KitSelectOptionVo.builder().label("香蕉").value("banana").build();
KitSelectOptionVo selectOption4 = KitSelectOptionVo.builder().label("猫").value("cat").build();
testData.add(selectOption1);
testData.add(selectOption2);
testData.add(selectOption3);
testData.add(selectOption4);
List<KitSelectOptionVo> selectOptions1 = KitCollUtil.streamAscSort(testData, KitSelectOptionVo::getValue);
System.out.println(JSON.toJSONString(selectOptions1));
List<KitSelectOptionVo> selectOptions2 = KitCollUtil.streamDescSort(testData, KitSelectOptionVo::getValue);
System.out.println(JSON.toJSONString(selectOptions2));
List<KitSelectOptionVo> selectOptions3 = KitCollUtil.streamDistinct(testData, KitSelectOptionVo::getValue);
System.out.println(JSON.toJSONString(selectOptions3));
Map<String, String> selectMap1 =
KitCollUtil.streamToMap(testData, KitSelectOptionVo::getValue, KitSelectOptionVo::getLabel);
System.out.println(JSON.toJSONString(selectMap1));
// 这只是一个示范
BigDecimal total1 = KitCollUtil.streamBigDecimalAdd(testData, "value");
System.out.println(total1);
}
/**
* 流对List<BigDecimal>取平均值
* <p>
* 使用方式: avg = CollUtil.streamBigDecimalAvg(list)
*
* @param data /
* @return /
*/
public static BigDecimal streamBigDecimalAvg(List<BigDecimal> data) {
return data.stream().filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add)
.divide(BigDecimal.valueOf(data.size()), 6, RoundingMode.FLOOR);
}
public static void main(String[] args) {
List<KitSelectOptionVo> testData = new ArrayList<>();
KitSelectOptionVo selectOption1 = KitSelectOptionVo.builder().label("苹果").value("apple").build();
KitSelectOptionVo selectOption2 = KitSelectOptionVo.builder().label("苹果").value("apple").build();
KitSelectOptionVo selectOption3 = KitSelectOptionVo.builder().label("香蕉").value("banana").build();
KitSelectOptionVo selectOption4 = KitSelectOptionVo.builder().label("猫").value("cat").build();
testData.add(selectOption1);
testData.add(selectOption2);
testData.add(selectOption3);
testData.add(selectOption4);
List<KitSelectOptionVo> selectOptions1 = KitCollUtil.streamAscSort(testData, KitSelectOptionVo::getValue);
System.out.println(JSON.toJSONString(selectOptions1));
List<KitSelectOptionVo> selectOptions2 = KitCollUtil.streamDescSort(testData, KitSelectOptionVo::getValue);
System.out.println(JSON.toJSONString(selectOptions2));
List<KitSelectOptionVo> selectOptions3 = KitCollUtil.streamDistinct(testData, KitSelectOptionVo::getValue);
System.out.println(JSON.toJSONString(selectOptions3));
Map<String, String> selectMap1 =
KitCollUtil.streamToMap(testData, KitSelectOptionVo::getValue, KitSelectOptionVo::getLabel);
System.out.println(JSON.toJSONString(selectMap1));
// 这只是一个示范
BigDecimal total1 = KitCollUtil.streamBigDecimalAdd(testData, "value");
System.out.println(total1);
}
}
@@ -29,121 +29,122 @@ import lombok.experimental.UtilityClass;
*/
@UtilityClass
public class KitDateUtil {
/**
* 获取当前时间毫秒数
*
* @return /
*/
public static String getNowDateTimeMsStr() {
return DateUtil.format(new Date(), DatePattern.PURE_DATETIME_MS_PATTERN);
/**
* 获取当前时间毫秒数
*
* @return /
*/
public static String getNowDateTimeMsStr() {
return DateUtil.format(new Date(), DatePattern.PURE_DATETIME_MS_PATTERN);
}
/**
* 获取当前时间,但不包括毫秒数
*
* @return /
*/
public static String getNowDateTimeStr() {
return DateUtil.format(new Date(), DatePattern.PURE_DATETIME_PATTERN);
}
/**
* 获取当前时间,但不包括毫秒数
*
* @return /
*/
public static String getNowDateStr() {
return DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN);
}
/**
* 格式化到毫秒
*
* @param startTime 开始时间
* @param endTime 结束时间
* @return 格式化后的时间,X天X时X分X秒X毫秒
*/
public static String formatMillsDuration(Date startTime, Date endTime) {
if (startTime == null || endTime == null) {
// 什么都不返回
return "";
}
/**
* 获取当前时间,但不包括毫秒数
*
* @return /
*/
public static String getNowDateTimeStr() {
return DateUtil.format(new Date(), DatePattern.PURE_DATETIME_PATTERN);
long betweenSeconds = DateUtil.between(startTime, endTime, cn.hutool.core.date.DateUnit.SECOND);
long days = betweenSeconds / (24 * 60 * 60);
long hours = (betweenSeconds % (24 * 60 * 60)) / (60 * 60);
long minutes = (betweenSeconds % (60 * 60)) / 60;
long seconds = betweenSeconds % 60;
StringBuilder result = new StringBuilder();
if (days > 0) {
result.append(days).append("天");
}
/**
* 获取当前时间,但不包括毫秒数
*
* @return /
*/
public static String getNowDateStr() {
return DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN);
if (hours > 0) {
result.append(hours).append("时");
}
/**
* 格式化到毫秒
*
* @param startTime 开始时间
* @param endTime 结束时间
* @return 格式化后的时间,X天X时X分X秒X毫秒
*/
public static String formatMillsDuration(Date startTime, Date endTime) {
if (startTime == null || endTime == null) {
// 什么都不返回
return "";
}
long betweenSeconds = DateUtil.between(startTime, endTime, cn.hutool.core.date.DateUnit.SECOND);
long days = betweenSeconds / (24 * 60 * 60);
long hours = (betweenSeconds % (24 * 60 * 60)) / (60 * 60);
long minutes = (betweenSeconds % (60 * 60)) / 60;
long seconds = betweenSeconds % 60;
StringBuilder result = new StringBuilder();
if (days > 0) {
result.append(days).append("天");
}
if (hours > 0) {
result.append(hours).append("时");
}
if (minutes > 0) {
result.append(minutes).append("分");
}
if (seconds > 0 || result.isEmpty()) {
result.append(seconds).append("秒");
}
return result.toString();
if (minutes > 0) {
result.append(minutes).append("分");
}
/**
* 格式化到秒
*
* @param startTime 开始时间
* @param endTime 结束时间
* @return 格式化后的时间,X天X时X分X秒
*/
public static String formatSecondsDuration(Date startTime, Date endTime) {
if (startTime == null || endTime == null) {
// 什么都不返回
return "";
}
long betweenMs = DateUtil.betweenMs(startTime, endTime);
long days = betweenMs / (24 * 60 * 60 * 1000);
long hours = (betweenMs % (24 * 60 * 60 * 1000)) / (60 * 60 * 1000);
long minutes = (betweenMs % (60 * 60 * 1000)) / (60 * 1000);
long seconds = (betweenMs % (60 * 1000)) / 1000;
long milliseconds = betweenMs % 1000;
StringBuilder result = new StringBuilder();
if (days > 0) {
result.append(days).append("天");
}
if (hours > 0) {
result.append(hours).append("时");
}
if (minutes > 0) {
result.append(minutes).append("分");
}
if (seconds > 0) {
result.append(seconds).append("秒");
}
if (milliseconds > 0) {
result.append(milliseconds).append("毫秒");
}
// 如果所有单位都是0,则显示0毫秒
if (result.isEmpty()) {
result.append("0毫秒");
}
return result.toString();
if (seconds > 0 || result.isEmpty()) {
result.append(seconds).append("秒");
}
return result.toString();
}
/**
* 格式化到秒
*
* @param startTime 开始时间
* @param endTime 结束时间
* @return 格式化后的时间,X天X时X分X秒
*/
public static String formatSecondsDuration(Date startTime, Date endTime) {
if (startTime == null || endTime == null) {
// 什么都不返回
return "";
}
long betweenMs = DateUtil.betweenMs(startTime, endTime);
long days = betweenMs / (24 * 60 * 60 * 1000);
long hours = (betweenMs % (24 * 60 * 60 * 1000)) / (60 * 60 * 1000);
long minutes = (betweenMs % (60 * 60 * 1000)) / (60 * 1000);
long seconds = (betweenMs % (60 * 1000)) / 1000;
long milliseconds = betweenMs % 1000;
StringBuilder result = new StringBuilder();
if (days > 0) {
result.append(days).append("天");
}
if (hours > 0) {
result.append(hours).append("时");
}
if (minutes > 0) {
result.append(minutes).append("分");
}
if (seconds > 0) {
result.append(seconds).append("秒");
}
if (milliseconds > 0) {
result.append(milliseconds).append("毫秒");
}
// 如果所有单位都是0,则显示0毫秒
if (result.isEmpty()) {
result.append("0毫秒");
}
return result.toString();
}
}
@@ -30,67 +30,67 @@ import lombok.experimental.UtilityClass;
@UtilityClass
public final class KitDesEncryptUtil {
private static final String STR_PARAM = "Passw0rd";
private static final IvParameterSpec IV = new IvParameterSpec(STR_PARAM.getBytes(StandardCharsets.UTF_8));
private static final String STR_PARAM = "Passw0rd";
private static final IvParameterSpec IV = new IvParameterSpec(STR_PARAM.getBytes(StandardCharsets.UTF_8));
private static DESKeySpec getDesKeySpec(String source) throws Exception {
if (source == null || source.isEmpty()) {
return null;
}
return new DESKeySpec(STR_PARAM.getBytes(StandardCharsets.UTF_8));
private static DESKeySpec getDesKeySpec(String source) throws Exception {
if (source == null || source.isEmpty()) {
return null;
}
return new DESKeySpec(STR_PARAM.getBytes(StandardCharsets.UTF_8));
}
/**
* 对称加密
*/
public static String desEncrypt(String source) throws Exception {
Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
DESKeySpec desKeySpec = getDesKeySpec(source);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey secretKey = keyFactory.generateSecret(desKeySpec);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, IV);
return byte2hex(cipher.doFinal(source.getBytes(StandardCharsets.UTF_8))).toUpperCase();
}
/**
* 对称加密
*/
public static String desEncrypt(String source) throws Exception {
Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
DESKeySpec desKeySpec = getDesKeySpec(source);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey secretKey = keyFactory.generateSecret(desKeySpec);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, IV);
return byte2hex(cipher.doFinal(source.getBytes(StandardCharsets.UTF_8))).toUpperCase();
}
/**
* 对称解密
*/
public static String desDecrypt(String source) throws Exception {
Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
byte[] src = hex2byte(source.getBytes(StandardCharsets.UTF_8));
DESKeySpec desKeySpec = getDesKeySpec(source);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey secretKey = keyFactory.generateSecret(desKeySpec);
cipher.init(Cipher.DECRYPT_MODE, secretKey, IV);
byte[] retByte = cipher.doFinal(src);
return new String(retByte);
}
/**
* 对称解密
*/
public static String desDecrypt(String source) throws Exception {
Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
byte[] src = hex2byte(source.getBytes(StandardCharsets.UTF_8));
DESKeySpec desKeySpec = getDesKeySpec(source);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey secretKey = keyFactory.generateSecret(desKeySpec);
cipher.init(Cipher.DECRYPT_MODE, secretKey, IV);
byte[] retByte = cipher.doFinal(src);
return new String(retByte);
}
private static String byte2hex(byte[] inStr) {
String stmp;
StringBuilder out = new StringBuilder(inStr.length * 2);
for (byte b : inStr) {
stmp = Integer.toHexString(b & 0xFF);
if (stmp.length() == 1) {
out.append("0").append(stmp);
} else {
out.append(stmp);
}
}
return out.toString();
private static String byte2hex(byte[] inStr) {
String stmp;
StringBuilder out = new StringBuilder(inStr.length * 2);
for (byte b : inStr) {
stmp = Integer.toHexString(b & 0xFF);
if (stmp.length() == 1) {
out.append("0").append(stmp);
} else {
out.append(stmp);
}
}
return out.toString();
}
private static byte[] hex2byte(byte[] b) {
int size = 2;
if ((b.length % size) != 0) {
throw new IllegalArgumentException("长度不是偶数");
}
byte[] b2 = new byte[b.length / 2];
for (int n = 0; n < b.length; n += size) {
String item = new String(b, n, 2);
b2[n / 2] = (byte)Integer.parseInt(item, 16);
}
return b2;
private static byte[] hex2byte(byte[] b) {
int size = 2;
if ((b.length % size) != 0) {
throw new IllegalArgumentException("长度不是偶数");
}
byte[] b2 = new byte[b.length / 2];
for (int n = 0; n < b.length; n += size) {
String item = new String(b, n, 2);
b2[n / 2] = (byte) Integer.parseInt(item, 16);
}
return b2;
}
}
@@ -26,177 +26,179 @@ import java.util.concurrent.atomic.AtomicLong;
* @date 2025-04-05
*/
public final class KitDistributedIdUtil {
/**
* 起始毫秒时间戳, 2020-12-30
*/
private static final long START_TIMESTAMP = 1609314517000L;
/**
* 数据中心 ID 所占位数
*/
private final long dataCenterIdBits;
/**
* 机器 ID 所占位数
*/
private final long workerIdBits;
/**
* 序列号所占位数
*/
private final long sequenceBits;
/**
* 数据中心 ID 最大值
*/
private final long maxDataCenterId;
/**
* 机器 ID 最大值
*/
private final long maxWorkerId;
/**
* 序列号最大值
*/
private final long sequenceMask;
/**
* 起始毫秒时间戳, 2020-12-30
*/
private static final long START_TIMESTAMP = 1609314517000L;
/**
* 数据中心 ID 所占位数
*/
private final long dataCenterIdBits;
/**
* 机器 ID 所占位数
*/
private final long workerIdBits;
/**
* 序列号所占位数
*/
private final long sequenceBits;
/**
* 机器 ID 向左移位数
*/
private final long workerIdShift;
/**
* 数据中心 ID 向左移位数
*/
private final long dataCenterIdShift;
/**
* 时间戳向左移位数
*/
private final long timestampLeftShift;
/**
* 数据中心 ID 最大值
*/
private final long maxDataCenterId;
/**
* 机器 ID 最大值
*/
private final long maxWorkerId;
/**
* 序列号最大值
*/
private final long sequenceMask;
/**
* 数据中心 ID
*/
private final long dataCenterId;
/**
* 机器 ID
*/
private final long workerId;
/**
* 序列号
*/
private final AtomicLong sequence = new AtomicLong(0L);
/**
* 上一次生成 ID 的时间戳
*/
private final AtomicLong lastTimestamp = new AtomicLong(-1L);
/**
* 机器 ID 向左移位数
*/
private final long workerIdShift;
/**
* 数据中心 ID 向左移位数
*/
private final long dataCenterIdShift;
/**
* 时间戳向左移位数
*/
private final long timestampLeftShift;
/**
* 构造函数, 初始化数据中心 ID 和机器 ID
*
* @param dataCenterId 数据中心 ID
* @param workerId 机器 ID
* @param dataCenterIdBits 数据中心 ID 所占位数
* @param workerIdBits 机器 ID 所占位数
* @param sequenceBits 序列号所占位数
*/
public KitDistributedIdUtil(long dataCenterId, long workerId, long dataCenterIdBits, long workerIdBits,
long sequenceBits) {
this.dataCenterIdBits = dataCenterIdBits;
this.workerIdBits = workerIdBits;
this.sequenceBits = sequenceBits;
/**
* 数据中心 ID
*/
private final long dataCenterId;
/**
* 机器 ID
*/
private final long workerId;
/**
* 序列号
*/
private final AtomicLong sequence = new AtomicLong(0L);
/**
* 上一次生成 ID 的时间戳
*/
private final AtomicLong lastTimestamp = new AtomicLong(-1L);
this.maxDataCenterId = ~(-1L << dataCenterIdBits);
this.maxWorkerId = ~(-1L << workerIdBits);
this.sequenceMask = ~(-1L << sequenceBits);
/**
* 构造函数, 初始化数据中心 ID 和机器 ID
*
* @param dataCenterId 数据中心 ID
* @param workerId 机器 ID
* @param dataCenterIdBits 数据中心 ID 所占位数
* @param workerIdBits 机器 ID 所占位数
* @param sequenceBits 序列号所占位数
*/
public KitDistributedIdUtil(long dataCenterId, long workerId, long dataCenterIdBits, long workerIdBits,
long sequenceBits) {
this.dataCenterIdBits = dataCenterIdBits;
this.workerIdBits = workerIdBits;
this.sequenceBits = sequenceBits;
this.workerIdShift = sequenceBits;
this.dataCenterIdShift = sequenceBits + workerIdBits;
this.timestampLeftShift = sequenceBits + workerIdBits + dataCenterIdBits;
this.maxDataCenterId = ~(-1L << dataCenterIdBits);
this.maxWorkerId = ~(-1L << workerIdBits);
this.sequenceMask = ~(-1L << sequenceBits);
if (dataCenterId > maxDataCenterId || dataCenterId < 0) {
throw new IllegalArgumentException(
"Data center ID can't be greater than " + maxDataCenterId + " or less than 0");
this.workerIdShift = sequenceBits;
this.dataCenterIdShift = sequenceBits + workerIdBits;
this.timestampLeftShift = sequenceBits + workerIdBits + dataCenterIdBits;
if (dataCenterId > maxDataCenterId || dataCenterId < 0) {
throw new IllegalArgumentException(
"Data center ID can't be greater than " + maxDataCenterId + " or less than 0");
}
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException("Worker ID can't be greater than " + maxWorkerId + " or less than 0");
}
this.dataCenterId = dataCenterId;
this.workerId = workerId;
}
/**
* 获取单例实例的下一个分布式 ID
*
* @return 生成的分布式 ID
*/
public static long fastNextId() {
return InstanceHolder.INSTANCE.nextId();
}
/**
* 主方法,用于测试分布式 ID 生成
*
* @param args 命令行参数
*/
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
long id = Thread.currentThread().getId();
for (int j = 0; j < 20; j++) {
System.out.println(id + " - " + KitDistributedIdUtil.fastNextId());
}
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException("Worker ID can't be greater than " + maxWorkerId + " or less than 0");
}
this.dataCenterId = dataCenterId;
this.workerId = workerId;
}).start();
}
}
/**
* 生成下一个分布式 ID
*
* @return 生成的分布式 ID
*/
public long nextId() {
long currentTimestamp = System.currentTimeMillis();
// 如果当前时间戳小于上一次生成 ID 的时间戳, 说明时钟回拨,抛出异常
if (currentTimestamp < lastTimestamp.get()) {
throw new BadRequestException(
"Clock moved backwards. Refusing to generate id for " + (lastTimestamp.get() - currentTimestamp) +
" milliseconds");
}
/**
* 获取单例实例的下一个分布式 ID
*
* @return 生成的分布式 ID
*/
public static long fastNextId() {
return InstanceHolder.INSTANCE.nextId();
long lastTimestampValue = lastTimestamp.get();
if (currentTimestamp == lastTimestampValue) {
// 同一毫秒内,序列号自增
long seq = sequence.incrementAndGet() & sequenceMask;
if (seq == 0) {
// 序列号溢出,等待下一毫秒
currentTimestamp = waitNextMillis(lastTimestampValue);
}
return ((currentTimestamp - START_TIMESTAMP) << timestampLeftShift) | (dataCenterId << dataCenterIdShift) |
(workerId << workerIdShift) | seq;
} else {
// 时间戳改变,重置序列号
sequence.set(0L);
lastTimestamp.set(currentTimestamp);
return ((currentTimestamp - START_TIMESTAMP) << timestampLeftShift) | (dataCenterId << dataCenterIdShift) |
(workerId << workerIdShift);
}
}
/**
* 主方法,用于测试分布式 ID 生成
*
* @param args 命令行参数
*/
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
long id = Thread.currentThread().getId();
for (int j = 0; j < 20; j++) {
System.out.println(id + " - " + KitDistributedIdUtil.fastNextId());
}
}).start();
}
/**
* 等待下一毫秒
*
* @param lastTimestamp 上一次生成 ID 的时间戳
* @return 当前时间戳
*/
private long waitNextMillis(long lastTimestamp) {
long timestamp = System.currentTimeMillis();
while (timestamp <= lastTimestamp) {
timestamp = System.currentTimeMillis();
}
return timestamp;
}
/**
* 生成下一个分布式 ID
*
* @return 生成的分布式 ID
*/
public long nextId() {
long currentTimestamp = System.currentTimeMillis();
/**
* 单例实例持有者类
*/
private static class InstanceHolder {
// 如果当前时间戳小于上一次生成 ID 的时间戳, 说明时钟回拨,抛出异常
if (currentTimestamp < lastTimestamp.get()) {
throw new BadRequestException(
"Clock moved backwards. Refusing to generate id for " + (lastTimestamp.get() - currentTimestamp) +
" milliseconds");
}
long lastTimestampValue = lastTimestamp.get();
if (currentTimestamp == lastTimestampValue) {
// 同一毫秒内,序列号自增
long seq = sequence.incrementAndGet() & sequenceMask;
if (seq == 0) {
// 序列号溢出,等待下一毫秒
currentTimestamp = waitNextMillis(lastTimestampValue);
}
return ((currentTimestamp - START_TIMESTAMP) << timestampLeftShift) | (dataCenterId << dataCenterIdShift) |
(workerId << workerIdShift) | seq;
} else {
// 时间戳改变,重置序列号
sequence.set(0L);
lastTimestamp.set(currentTimestamp);
return ((currentTimestamp - START_TIMESTAMP) << timestampLeftShift) | (dataCenterId << dataCenterIdShift) |
(workerId << workerIdShift);
}
}
/**
* 等待下一毫秒
*
* @param lastTimestamp 上一次生成 ID 的时间戳
* @return 当前时间戳
*/
private long waitNextMillis(long lastTimestamp) {
long timestamp = System.currentTimeMillis();
while (timestamp <= lastTimestamp) {
timestamp = System.currentTimeMillis();
}
return timestamp;
}
/**
* 单例实例持有者类
*/
private static class InstanceHolder {
private static final KitDistributedIdUtil INSTANCE = new KitDistributedIdUtil(20, 12, 5, 5, 12);
}
private static final KitDistributedIdUtil INSTANCE = new KitDistributedIdUtil(20, 12, 5, 5, 12);
}
}
@@ -16,6 +16,11 @@
package cn.odboy.util;
import static cn.odboy.constant.SystemConst.SYMBOL_ADD;
import static cn.odboy.constant.SystemConst.SYMBOL_AT;
import static cn.odboy.constant.SystemConst.SYMBOL_EQUAL;
import static cn.odboy.constant.SystemConst.SYMBOL_SUBTRACT;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.poi.excel.BigExcelWriter;
@@ -45,11 +50,6 @@ import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.springframework.web.multipart.MultipartFile;
import static cn.odboy.constant.SystemConst.SYMBOL_ADD;
import static cn.odboy.constant.SystemConst.SYMBOL_AT;
import static cn.odboy.constant.SystemConst.SYMBOL_EQUAL;
import static cn.odboy.constant.SystemConst.SYMBOL_SUBTRACT;
/**
* File工具类, 扩展 hutool 工具包
*/
@@ -57,342 +57,342 @@ import static cn.odboy.constant.SystemConst.SYMBOL_SUBTRACT;
@UtilityClass
public final class KitFileUtil extends cn.hutool.core.io.FileUtil {
/**
* 系统临时目录
* <br>
* windows 包含路径分割符, 但Linux 不包含, 在windows \\==\ 前提下, 为安全起见 同意拼装 路径分割符,
* <pre>
* java.io.tmpdir
* windows : C:\Users/xxx\AppData\Local\Temp\
* linux: /temp
* </pre>
*/
public static final String SYS_TEM_DIR = System.getProperty("java.io.tmpdir") + File.separator;
/**
* 定义GB的计算常量
*/
private static final int GB = 1024 * 1024 * 1024;
/**
* 定义MB的计算常量
*/
private static final int MB = 1024 * 1024;
/**
* 定义KB的计算常量
*/
private static final int KB = 1024;
/**
* 系统临时目录
* <br>
* windows 包含路径分割符, 但Linux 不包含, 在windows \\==\ 前提下, 为安全起见 同意拼装 路径分割符,
* <pre>
* java.io.tmpdir
* windows : C:\Users/xxx\AppData\Local\Temp\
* linux: /temp
* </pre>
*/
public static final String SYS_TEM_DIR = System.getProperty("java.io.tmpdir") + File.separator;
/**
* 定义GB的计算常量
*/
private static final int GB = 1024 * 1024 * 1024;
/**
* 定义MB的计算常量
*/
private static final int MB = 1024 * 1024;
/**
* 定义KB的计算常量
*/
private static final int KB = 1024;
/**
* 格式化小数
*/
private static final DecimalFormat DF = new DecimalFormat("0.00");
/**
* 格式化小数
*/
private static final DecimalFormat DF = new DecimalFormat("0.00");
/**
* MultipartFile转File
*/
public static File toFile(MultipartFile multipartFile) {
// 获取文件名
String fileName = multipartFile.getOriginalFilename();
// 获取文件后缀
String prefix = "." + getSuffix(fileName);
File file = null;
/**
* MultipartFile转File
*/
public static File toFile(MultipartFile multipartFile) {
// 获取文件名
String fileName = multipartFile.getOriginalFilename();
// 获取文件后缀
String prefix = "." + getSuffix(fileName);
File file = null;
try {
// 用uuid作为文件名, 防止生成的临时文件重复
file = new File(SYS_TEM_DIR + IdUtil.simpleUUID() + prefix);
// MultipartFile to File
multipartFile.transferTo(file);
} catch (IOException e) {
log.error("保存临时文件失败", e);
}
return file;
}
/**
* 文件大小转换
*/
public static String getSize(long size) {
String resultSize;
if (size / GB >= 1) {
//如果当前Byte的值大于等于1GB
resultSize = DF.format(size / (float) GB) + " GB";
} else if (size / MB >= 1) {
//如果当前Byte的值大于等于1MB
resultSize = DF.format(size / (float) MB) + " MB";
} else if (size / KB >= 1) {
//如果当前Byte的值大于等于1KB
resultSize = DF.format(size / (float) KB) + " KB";
} else {
resultSize = size + " B";
}
return resultSize;
}
/**
* inputStream 转 File
*/
public static File inputStreamToFile(InputStream ins, String name) {
File file = new File(SYS_TEM_DIR + name);
if (file.exists()) {
return file;
}
OutputStream os = null;
try {
os = Files.newOutputStream(file.toPath());
int bytesRead;
int len = 8192;
byte[] buffer = new byte[len];
while ((bytesRead = ins.read(buffer, 0, len)) != -1) {
os.write(buffer, 0, bytesRead);
}
} catch (Exception e) {
log.error(e.getMessage(), e);
} finally {
KitCloseUtil.close(os);
KitCloseUtil.close(ins);
}
return file;
}
/**
* 将文件名解析成文件的上传路径
*/
public static File upload(MultipartFile file, String filePath) {
Date date = new Date();
SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmssS");
// 过滤非法文件名
String name = getPrefix(verifyFilename(file.getOriginalFilename()));
String suffix = getSuffix(file.getOriginalFilename());
String nowStr = "-" + format.format(date);
try {
String fileName = name + nowStr + "." + suffix;
String path = filePath + fileName;
// getCanonicalFile 可解析正确各种路径
File dest = new File(path).getCanonicalFile();
// 检测是否存在目录
if (!dest.getParentFile().exists()) {
if (!dest.getParentFile().mkdirs()) {
log.error("创建目录失败, {}", dest.getParentFile().getAbsolutePath());
}
}
// 文件写入
file.transferTo(dest);
return dest;
} catch (Exception e) {
log.error("文件上传失败", e);
}
return null;
}
/**
* 导出excel
*/
public static void downloadExcel(List<Map<String, Object>> list, HttpServletResponse response) throws IOException {
String tempPath = SYS_TEM_DIR + IdUtil.fastSimpleUUID() + ".xlsx";
File file = new File(tempPath);
BigExcelWriter writer = ExcelUtil.getBigWriter(file);
// 处理数据以防止CSV注入
List<Map<String, Object>> sanitizedList = list.parallelStream().map(map -> {
Map<String, Object> sanitizedMap = new LinkedHashMap<>();
map.forEach((key, value) -> {
if (value instanceof String strValue) {
// 检查并处理以特殊字符开头的值
if (strValue.startsWith(SYMBOL_EQUAL) || strValue.startsWith(SYMBOL_ADD) ||
strValue.startsWith(SYMBOL_SUBTRACT) || strValue.startsWith(SYMBOL_AT)) {
// 添加单引号前缀
strValue = "'" + strValue;
}
sanitizedMap.put(key, strValue);
} else {
sanitizedMap.put(key, value);
}
});
return sanitizedMap;
}).toList();
// 一次性写出内容, 使用默认样式,强制输出标题
writer.write(sanitizedList, true);
SXSSFSheet sheet = (SXSSFSheet) writer.getSheet();
//上面需要强转SXSSFSheet 不然没有trackAllColumnsForAutoSizing方法
sheet.trackAllColumnsForAutoSizing();
//列宽自适应
writer.autoSizeColumnAll();
//response为HttpServletResponse对象
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
//test.xls是弹出下载对话框的文件名,不能为中文,中文请自行编码
response.setHeader("Content-Disposition", "attachment;filename=file.xlsx");
ServletOutputStream out = response.getOutputStream();
// 终止后删除临时文件
file.deleteOnExit();
writer.flush(out, true);
//此处记得关闭输出Servlet流
IoUtil.close(out);
}
/**
* 获取文件类型
*/
public static String getFileType(String type) {
String documents = "txt doc pdf ppt pps xlsx xls docx";
String voice = "mp3 wav wma mpa ram ra aac aif m4a";
String video = "avi mpg mpe mpeg asf wmv mov qt rm mp4 flv m4v webm ogv ogg";
String image = "bmp dib pcp dif wmf gif jpg tif eps psd cdr iff tga pcd mpt png jpeg";
if (image.contains(type)) {
return FileTypeEnum.IMAGE.getCode();
} else if (documents.contains(type)) {
return FileTypeEnum.DOC.getCode();
} else if (voice.contains(type)) {
return FileTypeEnum.VOICE.getCode();
} else if (video.contains(type)) {
return FileTypeEnum.VIDEO.getCode();
} else {
return FileTypeEnum.OTHER.getCode();
}
}
/**
* 检查文件大小是否超出最大值
*/
public static void checkSize(long maxSize, long size) {
// 1M
int len = 1024 * 1024;
if (size > (maxSize * len)) {
throw new BadRequestException("文件超出规定大小:" + maxSize + "MB");
}
}
/**
* 判断两个文件是否相同
*/
public static boolean check(File file1, File file2) {
String img1Md5 = getMd5(file1);
String img2Md5 = getMd5(file2);
if (img1Md5 != null) {
return img1Md5.equals(img2Md5);
}
return false;
}
/**
* 判断两个文件是否相同
*/
public static boolean check(String file1Md5, String file2Md5) {
return file1Md5.equals(file2Md5);
}
/**
* 获取文件字节数组
*/
private static byte[] getByte(File file) {
byte[] b = new byte[(int) file.length()];
InputStream in = null;
try {
in = Files.newInputStream(file.toPath());
// 忽略read值
int read = in.read(b);
} catch (Exception e) {
log.error(e.getMessage(), e);
return null;
} finally {
KitCloseUtil.close(in);
}
return b;
}
/**
* 获取文件MD5值
*/
private static String getMd5(byte[] bytes) {
// 16进制字符
char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
try {
MessageDigest mdTemp = MessageDigest.getInstance("MD5");
mdTemp.update(bytes);
byte[] md = mdTemp.digest();
int j = md.length;
char[] str = new char[j * 2];
int k = 0;
// 移位 输出字符串
for (byte byte0 : md) {
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
return new String(str);
} catch (Exception e) {
log.error("获取文件MD5值失败", e);
}
return null;
}
/**
* 下载文件
*
* @param request /
* @param response /
* @param file /
*/
public static void downloadFile(HttpServletRequest request, HttpServletResponse response, File file,
boolean deleteOnExit) {
response.setCharacterEncoding(request.getCharacterEncoding());
response.setContentType("application/octet-stream");
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
response.setHeader("Content-Disposition", "attachment; filename=" + file.getName());
IOUtils.copy(fis, response.getOutputStream());
response.flushBuffer();
} catch (Exception e) {
log.error("下载文件失败", e);
} finally {
if (fis != null) {
try {
// 用uuid作为文件名, 防止生成的临时文件重复
file = new File(SYS_TEM_DIR + IdUtil.simpleUUID() + prefix);
// MultipartFile to File
multipartFile.transferTo(file);
fis.close();
if (deleteOnExit) {
file.deleteOnExit();
}
} catch (IOException e) {
log.error("保存临时文件失败", e);
log.error("关闭IO流失败", e);
}
return file;
}
}
}
/**
* 验证并过滤非法的文件名
*
* @param fileName 文件名
* @return 文件名
*/
public static String verifyFilename(String fileName) {
// 过滤掉特殊字符
fileName = fileName.replaceAll("[\\\\/:*?\"<>|~\\s]", "");
// 去掉文件名开头和结尾的空格和点
fileName = fileName.trim().replaceAll("^[. ]+|[. ]+$", "");
// 不允许文件名超过255(在Mac和Linux中)或260(在Windows中)个字符
int maxFileNameLength = 255;
if (System.getProperty(SystemConst.PROPERTY_OS_NAME).startsWith(SystemConst.OS_NAME_WINDOWS)) {
maxFileNameLength = 260;
}
if (fileName.length() > maxFileNameLength) {
fileName = fileName.substring(0, maxFileNameLength);
}
/**
* 文件大小转换
*/
public static String getSize(long size) {
String resultSize;
if (size / GB >= 1) {
//如果当前Byte的值大于等于1GB
resultSize = DF.format(size / (float)GB) + " GB";
} else if (size / MB >= 1) {
//如果当前Byte的值大于等于1MB
resultSize = DF.format(size / (float)MB) + " MB";
} else if (size / KB >= 1) {
//如果当前Byte的值大于等于1KB
resultSize = DF.format(size / (float)KB) + " KB";
} else {
resultSize = size + " B";
}
return resultSize;
}
// 过滤掉控制字符
fileName = fileName.replaceAll("[\\p{Cntrl}]", "");
/**
* inputStream 转 File
*/
public static File inputStreamToFile(InputStream ins, String name) {
File file = new File(SYS_TEM_DIR + name);
if (file.exists()) {
return file;
}
OutputStream os = null;
try {
os = Files.newOutputStream(file.toPath());
int bytesRead;
int len = 8192;
byte[] buffer = new byte[len];
while ((bytesRead = ins.read(buffer, 0, len)) != -1) {
os.write(buffer, 0, bytesRead);
}
} catch (Exception e) {
log.error(e.getMessage(), e);
} finally {
KitCloseUtil.close(os);
KitCloseUtil.close(ins);
}
return file;
}
// 过滤掉 ".." 路径
fileName = fileName.replaceAll("\\.{2,}", "");
/**
* 将文件名解析成文件的上传路径
*/
public static File upload(MultipartFile file, String filePath) {
Date date = new Date();
SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmssS");
// 过滤非法文件名
String name = getPrefix(verifyFilename(file.getOriginalFilename()));
String suffix = getSuffix(file.getOriginalFilename());
String nowStr = "-" + format.format(date);
try {
String fileName = name + nowStr + "." + suffix;
String path = filePath + fileName;
// getCanonicalFile 可解析正确各种路径
File dest = new File(path).getCanonicalFile();
// 检测是否存在目录
if (!dest.getParentFile().exists()) {
if (!dest.getParentFile().mkdirs()) {
log.error("创建目录失败, {}", dest.getParentFile().getAbsolutePath());
}
}
// 文件写入
file.transferTo(dest);
return dest;
} catch (Exception e) {
log.error("文件上传失败", e);
}
return null;
}
// 去掉文件名开头的 ".."
fileName = fileName.replaceAll("^\\.+/", "");
/**
* 导出excel
*/
public static void downloadExcel(List<Map<String, Object>> list, HttpServletResponse response) throws IOException {
String tempPath = SYS_TEM_DIR + IdUtil.fastSimpleUUID() + ".xlsx";
File file = new File(tempPath);
BigExcelWriter writer = ExcelUtil.getBigWriter(file);
// 处理数据以防止CSV注入
List<Map<String, Object>> sanitizedList = list.parallelStream().map(map -> {
Map<String, Object> sanitizedMap = new LinkedHashMap<>();
map.forEach((key, value) -> {
if (value instanceof String strValue) {
// 检查并处理以特殊字符开头的值
if (strValue.startsWith(SYMBOL_EQUAL) || strValue.startsWith(SYMBOL_ADD) ||
strValue.startsWith(SYMBOL_SUBTRACT) || strValue.startsWith(SYMBOL_AT)) {
// 添加单引号前缀
strValue = "'" + strValue;
}
sanitizedMap.put(key, strValue);
} else {
sanitizedMap.put(key, value);
}
});
return sanitizedMap;
}).toList();
// 一次性写出内容, 使用默认样式,强制输出标题
writer.write(sanitizedList, true);
SXSSFSheet sheet = (SXSSFSheet)writer.getSheet();
//上面需要强转SXSSFSheet 不然没有trackAllColumnsForAutoSizing方法
sheet.trackAllColumnsForAutoSizing();
//列宽自适应
writer.autoSizeColumnAll();
//response为HttpServletResponse对象
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
//test.xls是弹出下载对话框的文件名,不能为中文,中文请自行编码
response.setHeader("Content-Disposition", "attachment;filename=file.xlsx");
ServletOutputStream out = response.getOutputStream();
// 终止后删除临时文件
file.deleteOnExit();
writer.flush(out, true);
//此处记得关闭输出Servlet流
IoUtil.close(out);
}
// 保留文件名中最后一个 "." 字符,过滤掉其他 "."
fileName = fileName.replaceAll("^(.*)(\\.[^.]*)$", "$1").replaceAll("\\.", "") +
fileName.replaceAll("^(.*)(\\.[^.]*)$", "$2");
/**
* 获取文件类型
*/
public static String getFileType(String type) {
String documents = "txt doc pdf ppt pps xlsx xls docx";
String voice = "mp3 wav wma mpa ram ra aac aif m4a";
String video = "avi mpg mpe mpeg asf wmv mov qt rm mp4 flv m4v webm ogv ogg";
String image = "bmp dib pcp dif wmf gif jpg tif eps psd cdr iff tga pcd mpt png jpeg";
if (image.contains(type)) {
return FileTypeEnum.IMAGE.getCode();
} else if (documents.contains(type)) {
return FileTypeEnum.DOC.getCode();
} else if (voice.contains(type)) {
return FileTypeEnum.VOICE.getCode();
} else if (video.contains(type)) {
return FileTypeEnum.VIDEO.getCode();
} else {
return FileTypeEnum.OTHER.getCode();
}
}
return fileName;
}
/**
* 检查文件大小是否超出最大值
*/
public static void checkSize(long maxSize, long size) {
// 1M
int len = 1024 * 1024;
if (size > (maxSize * len)) {
throw new BadRequestException("文件超出规定大小:" + maxSize + "MB");
}
}
/**
* 判断两个文件是否相同
*/
public static boolean check(File file1, File file2) {
String img1Md5 = getMd5(file1);
String img2Md5 = getMd5(file2);
if (img1Md5 != null) {
return img1Md5.equals(img2Md5);
}
return false;
}
/**
* 判断两个文件是否相同
*/
public static boolean check(String file1Md5, String file2Md5) {
return file1Md5.equals(file2Md5);
}
/**
* 获取文件字节数组
*/
private static byte[] getByte(File file) {
byte[] b = new byte[(int)file.length()];
InputStream in = null;
try {
in = Files.newInputStream(file.toPath());
// 忽略read值
int read = in.read(b);
} catch (Exception e) {
log.error(e.getMessage(), e);
return null;
} finally {
KitCloseUtil.close(in);
}
return b;
}
/**
* 获取文件MD5值
*/
private static String getMd5(byte[] bytes) {
// 16进制字符
char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
try {
MessageDigest mdTemp = MessageDigest.getInstance("MD5");
mdTemp.update(bytes);
byte[] md = mdTemp.digest();
int j = md.length;
char[] str = new char[j * 2];
int k = 0;
// 移位 输出字符串
for (byte byte0 : md) {
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
return new String(str);
} catch (Exception e) {
log.error("获取文件MD5值失败", e);
}
return null;
}
/**
* 下载文件
*
* @param request /
* @param response /
* @param file /
*/
public static void downloadFile(HttpServletRequest request, HttpServletResponse response, File file,
boolean deleteOnExit) {
response.setCharacterEncoding(request.getCharacterEncoding());
response.setContentType("application/octet-stream");
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
response.setHeader("Content-Disposition", "attachment; filename=" + file.getName());
IOUtils.copy(fis, response.getOutputStream());
response.flushBuffer();
} catch (Exception e) {
log.error("下载文件失败", e);
} finally {
if (fis != null) {
try {
fis.close();
if (deleteOnExit) {
file.deleteOnExit();
}
} catch (IOException e) {
log.error("关闭IO流失败", e);
}
}
}
}
/**
* 验证并过滤非法的文件名
*
* @param fileName 文件名
* @return 文件名
*/
public static String verifyFilename(String fileName) {
// 过滤掉特殊字符
fileName = fileName.replaceAll("[\\\\/:*?\"<>|~\\s]", "");
// 去掉文件名开头和结尾的空格和点
fileName = fileName.trim().replaceAll("^[. ]+|[. ]+$", "");
// 不允许文件名超过255(在Mac和Linux中)或260(在Windows中)个字符
int maxFileNameLength = 255;
if (System.getProperty(SystemConst.PROPERTY_OS_NAME).startsWith(SystemConst.OS_NAME_WINDOWS)) {
maxFileNameLength = 260;
}
if (fileName.length() > maxFileNameLength) {
fileName = fileName.substring(0, maxFileNameLength);
}
// 过滤掉控制字符
fileName = fileName.replaceAll("[\\p{Cntrl}]", "");
// 过滤掉 ".." 路径
fileName = fileName.replaceAll("\\.{2,}", "");
// 去掉文件名开头的 ".."
fileName = fileName.replaceAll("^\\.+/", "");
// 保留文件名中最后一个 "." 字符,过滤掉其他 "."
fileName = fileName.replaceAll("^(.*)(\\.[^.]*)$", "$1").replaceAll("\\.", "") +
fileName.replaceAll("^(.*)(\\.[^.]*)$", "$2");
return fileName;
}
public static String getMd5(File file) {
return getMd5(getByte(file));
}
public static String getMd5(File file) {
return getMd5(getByte(file));
}
}
@@ -29,61 +29,62 @@ import net.dreamlu.mica.ip2region.core.IpInfo;
*/
@UtilityClass
public final class KitIPUtil {
/**
* 注入bean
*/
private final static Ip2regionSearcher IP_SEARCHER = KitSpringBeanHolder.getBean(Ip2regionSearcher.class);
/**
* 根据ip获取详细地址
*/
public static String getCityInfo(String ip) {
IpInfo ipInfo = IP_SEARCHER.memorySearch(ip);
if (ipInfo != null) {
return ipInfo.getAddress();
}
return null;
}
/**
* 注入bean
*/
private final static Ip2regionSearcher IP_SEARCHER = KitSpringBeanHolder.getBean(Ip2regionSearcher.class);
/**
* 获取当前机器的IP
*
* @return /
*/
public static String getLocalIp() {
try {
InetAddress candidateAddress = null;
// 遍历所有的网络接口
for (Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
interfaces.hasMoreElements(); ) {
NetworkInterface anInterface = interfaces.nextElement();
// 在所有的接口下再遍历IP
for (Enumeration<InetAddress> inetAddresses = anInterface.getInetAddresses();
inetAddresses.hasMoreElements(); ) {
InetAddress inetAddr = inetAddresses.nextElement();
// 排除loopback类型地址
if (!inetAddr.isLoopbackAddress()) {
if (inetAddr.isSiteLocalAddress()) {
// 如果是site-local地址, 就是它了
return inetAddr.getHostAddress();
} else if (candidateAddress == null) {
// site-local类型的地址未被发现, 先记录候选地址
candidateAddress = inetAddr;
}
}
}
}
if (candidateAddress != null) {
return candidateAddress.getHostAddress();
}
// 如果没有发现 non-loopback地址.只能用最次选的方案
InetAddress jdkSuppliedAddress = InetAddress.getLocalHost();
if (jdkSuppliedAddress == null) {
return "";
}
return jdkSuppliedAddress.getHostAddress();
} catch (Exception e) {
return "";
}
/**
* 根据ip获取详细地址
*/
public static String getCityInfo(String ip) {
IpInfo ipInfo = IP_SEARCHER.memorySearch(ip);
if (ipInfo != null) {
return ipInfo.getAddress();
}
return null;
}
/**
* 获取当前机器的IP
*
* @return /
*/
public static String getLocalIp() {
try {
InetAddress candidateAddress = null;
// 遍历所有的网络接口
for (Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
interfaces.hasMoreElements(); ) {
NetworkInterface anInterface = interfaces.nextElement();
// 在所有的接口下再遍历IP
for (Enumeration<InetAddress> inetAddresses = anInterface.getInetAddresses();
inetAddresses.hasMoreElements(); ) {
InetAddress inetAddr = inetAddresses.nextElement();
// 排除loopback类型地址
if (!inetAddr.isLoopbackAddress()) {
if (inetAddr.isSiteLocalAddress()) {
// 如果是site-local地址, 就是它了
return inetAddr.getHostAddress();
} else if (candidateAddress == null) {
// site-local类型的地址未被发现, 先记录候选地址
candidateAddress = inetAddr;
}
}
}
}
if (candidateAddress != null) {
return candidateAddress.getHostAddress();
}
// 如果没有发现 non-loopback地址.只能用最次选的方案
InetAddress jdkSuppliedAddress = InetAddress.getLocalHost();
if (jdkSuppliedAddress == null) {
return "";
}
return jdkSuppliedAddress.getHostAddress();
} catch (Exception e) {
return "";
}
}
}
@@ -30,18 +30,21 @@ import lombok.experimental.UtilityClass;
*/
@UtilityClass
public final class KitJsonUtil {
public static <K, V> Map<K, V> toMap(String jsonStr, Class<K> kClass, Class<V> vClass) {
return JSON.parseObject(jsonStr, new TypeReference<>() {});
}
public static <V> List<V> toList(String jsonStr, Class<V> vClass) {
return JSON.parseObject(jsonStr, new TypeReference<>() {});
}
public static <K, V> Map<K, V> toMap(String jsonStr, Class<K> kClass, Class<V> vClass) {
return JSON.parseObject(jsonStr, new TypeReference<>() {
});
}
public static void main(String[] args) {
String mapStr = "{\"key1\":\"value1\", \"key2\":\"value2\"}";
System.err.println(KitJsonUtil.toMap(mapStr, String.class, String.class));
String listStr = "[\"value1\", \"value2\"]";
System.err.println(KitJsonUtil.toList(listStr, String.class));
}
public static <V> List<V> toList(String jsonStr, Class<V> vClass) {
return JSON.parseObject(jsonStr, new TypeReference<>() {
});
}
public static void main(String[] args) {
String mapStr = "{\"key1\":\"value1\", \"key2\":\"value2\"}";
System.err.println(KitJsonUtil.toMap(mapStr, String.class, String.class));
String listStr = "[\"value1\", \"value2\"]";
System.err.println(KitJsonUtil.toList(listStr, String.class));
}
}
@@ -32,38 +32,38 @@ import lombok.experimental.UtilityClass;
*/
@UtilityClass
public final class KitListUtil {
/**
* 根据对象T的属性去重
*
* @param items 对象T集合
* @param classifier 对象T属性表达式, 比如: User::getName
*/
public static <T, K> List<T> distinctByArgs(List<T> items, Function<? super T, ? extends K> classifier) {
return items.stream().collect(Collectors.groupingBy(classifier)).values().stream()
.map(monitorItem -> monitorItem.iterator().next()).toList();
}
/**
* list转map, 保留新值 -> Map<String, User> userMap = items.stream().collect(Collectors.toMap(User::getName, user ->
* user)) <br> list转map, 保留旧值 -> Map<String, User> userMap = users.stream().collect(Collectors.toMap(User::getName,
* user -> user, (existing, replacement) -> existing));
*/
public static void main(String[] args) {
List<KitBaseUserTimeTb> entities = new ArrayList<>();
int total = 50;
for (int i = 0; i < total; i++) {
KitBaseUserTimeTb entity = new KitBaseUserTimeTb();
if (i % 2 == 0) {
entity.setCreateBy("odboy");
} else {
entity.setCreateBy("admin");
}
entity.setUpdateBy("admin");
entity.setCreateTime(DateTime.now().toTimestamp());
entity.setUpdateTime(DateTime.now().toTimestamp());
entities.add(entity);
}
List<KitBaseUserTimeTb> entities1 = KitListUtil.distinctByArgs(entities, KitBaseUserTimeTb::getCreateBy);
System.err.println(entities1);
/**
* 根据对象T的属性去重
*
* @param items 对象T集合
* @param classifier 对象T属性表达式, 比如: User::getName
*/
public static <T, K> List<T> distinctByArgs(List<T> items, Function<? super T, ? extends K> classifier) {
return items.stream().collect(Collectors.groupingBy(classifier)).values().stream()
.map(monitorItem -> monitorItem.iterator().next()).toList();
}
/**
* list转map, 保留新值 -> Map<String, User> userMap = items.stream().collect(Collectors.toMap(User::getName, user -> user)) <br> list转map, 保留旧值 -> Map<String,
* User> userMap = users.stream().collect(Collectors.toMap(User::getName, user -> user, (existing, replacement) -> existing));
*/
public static void main(String[] args) {
List<KitBaseUserTimeTb> entities = new ArrayList<>();
int total = 50;
for (int i = 0; i < total; i++) {
KitBaseUserTimeTb entity = new KitBaseUserTimeTb();
if (i % 2 == 0) {
entity.setCreateBy("odboy");
} else {
entity.setCreateBy("admin");
}
entity.setUpdateBy("admin");
entity.setCreateTime(DateTime.now().toTimestamp());
entity.setUpdateTime(DateTime.now().toTimestamp());
entities.add(entity);
}
List<KitBaseUserTimeTb> entities1 = KitListUtil.distinctByArgs(entities, KitBaseUserTimeTb::getCreateBy);
System.err.println(entities1);
}
}
@@ -26,26 +26,27 @@ import lombok.experimental.UtilityClass;
*/
@UtilityClass
public final class KitLogFmtUtil {
/**
* 替换字符串中的占位符 {} 为传入的参数
*
* @param message 消息模板
* @param args 替换占位符的参数
* @return 替换后的消息
*/
public static String format(String message, Object... args) {
if (message == null || args == null || args.length == 0) {
return message;
}
// 使用 String.format 替换占位符
return String.format(message.replace("{}", "%s"), args);
}
public static void main(String[] args) {
// 示例用法
String messageTemplate = "获取dingtalk accessToken失败, code={}, message={}";
String formattedMessage = format(messageTemplate, 400, "Bad Request");
// 输出: 获取dingtalk accessToken失败, code=400, message=Bad Request
System.out.println(formattedMessage);
/**
* 替换字符串中的占位符 {} 为传入的参数
*
* @param message 消息模板
* @param args 替换占位符的参数
* @return 替换后的消息
*/
public static String format(String message, Object... args) {
if (message == null || args == null || args.length == 0) {
return message;
}
// 使用 String.format 替换占位符
return String.format(message.replace("{}", "%s"), args);
}
public static void main(String[] args) {
// 示例用法
String messageTemplate = "获取dingtalk accessToken失败, code={}, message={}";
String formattedMessage = format(messageTemplate, 400, "Bad Request");
// 输出: 获取dingtalk accessToken失败, code=400, message=Bad Request
System.out.println(formattedMessage);
}
}
@@ -33,58 +33,58 @@ import lombok.experimental.UtilityClass;
@UtilityClass
public final class KitPageUtil extends cn.hutool.core.util.PageUtil {
/**
* List 分页
*/
public static <T> List<T> softPaging(long page, long size, List<T> list) {
int pageIndex = Math.toIntExact(page - 1);
int fromIndex = Math.toIntExact(pageIndex * size);
int toIndex = Math.toIntExact(pageIndex * size + size);
if (fromIndex > list.size()) {
return Collections.emptyList();
} else if (toIndex >= list.size()) {
return list.subList(fromIndex, list.size());
} else {
return list.subList(fromIndex, toIndex);
}
/**
* List 分页
*/
public static <T> List<T> softPaging(long page, long size, List<T> list) {
int pageIndex = Math.toIntExact(page - 1);
int fromIndex = Math.toIntExact(pageIndex * size);
int toIndex = Math.toIntExact(pageIndex * size + size);
if (fromIndex > list.size()) {
return Collections.emptyList();
} else if (toIndex >= list.size()) {
return list.subList(fromIndex, list.size());
} else {
return list.subList(fromIndex, toIndex);
}
}
/**
* Page 数据处理
*/
public static <T> KitPageResult<T> toPage(IPage<T> page) {
return new KitPageResult<>(page.getRecords(), page.getTotal());
}
/**
* Page 数据处理
*/
public static <T> KitPageResult<T> toPage(IPage<T> page) {
return new KitPageResult<>(page.getRecords(), page.getTotal());
}
/**
* 自定义分页
*/
public static <T> KitPageResult<T> toPage(List<T> list) {
return new KitPageResult<>(list, list.size());
}
/**
* 自定义分页
*/
public static <T> KitPageResult<T> toPage(List<T> list) {
return new KitPageResult<>(list, list.size());
}
/**
* 返回空数据
*/
public static <T> KitPageResult<T> emptyListData() {
return new KitPageResult<>(new ArrayList<>(), 0);
}
/**
* 返回空数据
*/
public static <T> KitPageResult<T> emptyListData() {
return new KitPageResult<>(new ArrayList<>(), 0);
}
/**
* 返回空数据
*/
public static <T> KitPageResult<T> emptyData() {
return new KitPageResult<>(null, 0);
}
/**
* 返回空数据
*/
public static <T> KitPageResult<T> emptyData() {
return new KitPageResult<>(null, 0);
}
/**
* 自定义分页
*/
public static <T> KitPageResult<T> toPage(List<T> list, long totalElements) {
return new KitPageResult<>(list, totalElements);
}
/**
* 自定义分页
*/
public static <T> KitPageResult<T> toPage(List<T> list, long totalElements) {
return new KitPageResult<>(list, totalElements);
}
public static <T> Page<T> getCount(BaseMapper<T> baseMapper) {
return new Page<>(1, baseMapper.selectCount(null));
}
public static <T> Page<T> getCount(BaseMapper<T> baseMapper) {
return new Page<>(1, baseMapper.selectCount(null));
}
}
@@ -32,11 +32,12 @@ import lombok.experimental.UtilityClass;
*/
@UtilityClass
public class KitResourceTemplateUtil {
public static String render(String moduleName, String filename, Dict params) {
String path = StrUtil.isBlank(moduleName) ? "template" : "template/" + moduleName;
TemplateEngine engine =
TemplateUtil.createEngine(new TemplateConfig(path, TemplateConfig.ResourceMode.CLASSPATH));
Template template = engine.getTemplate(filename);
return template.render(params);
}
public static String render(String moduleName, String filename, Dict params) {
String path = StrUtil.isBlank(moduleName) ? "template" : "template/" + moduleName;
TemplateEngine engine =
TemplateUtil.createEngine(new TemplateConfig(path, TemplateConfig.ResourceMode.CLASSPATH));
Template template = engine.getTemplate(filename);
return template.render(params);
}
}
@@ -32,41 +32,42 @@ import org.aspectj.lang.reflect.MethodSignature;
* @date 2025-05-08
*/
public class KitReturnValueHandleUtil {
public static Object getDefaultValue(ProceedingJoinPoint joinPoint) {
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
Method method = signature.getMethod();
// 获取目标方法的返回值类型
Class<?> returnType = method.getReturnType();
if (returnType.isArray()) {
return getEmptyArray(returnType);
} else if (List.class.isAssignableFrom(returnType)) {
return Collections.emptyList();
} else if (Set.class.isAssignableFrom(returnType)) {
return Collections.emptySet();
} else if (Collection.class.isAssignableFrom(returnType)) {
// 默认返回空 List
return Collections.emptyList();
} else {
// 普通对象返回 null
return null;
}
}
/**
* 根据数组类型返回空数组
*/
private static Object getEmptyArray(Class<?> arrayType) {
if (arrayType == int[].class) {
return new int[0];
} else if (arrayType == long[].class) {
return new long[0];
} else if (arrayType == double[].class) {
return new double[0];
} else if (arrayType == String[].class) {
return new String[0];
} else {
// 其他引用类型数组
return java.lang.reflect.Array.newInstance(arrayType.getComponentType(), 0);
}
public static Object getDefaultValue(ProceedingJoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
// 获取目标方法的返回值类型
Class<?> returnType = method.getReturnType();
if (returnType.isArray()) {
return getEmptyArray(returnType);
} else if (List.class.isAssignableFrom(returnType)) {
return Collections.emptyList();
} else if (Set.class.isAssignableFrom(returnType)) {
return Collections.emptySet();
} else if (Collection.class.isAssignableFrom(returnType)) {
// 默认返回空 List
return Collections.emptyList();
} else {
// 普通对象返回 null
return null;
}
}
/**
* 根据数组类型返回空数组
*/
private static Object getEmptyArray(Class<?> arrayType) {
if (arrayType == int[].class) {
return new int[0];
} else if (arrayType == long[].class) {
return new long[0];
} else if (arrayType == double[].class) {
return new double[0];
} else if (arrayType == String[].class) {
return new String[0];
} else {
// 其他引用类型数组
return java.lang.reflect.Array.newInstance(arrayType.getComponentType(), 0);
}
}
}
@@ -37,182 +37,182 @@ import org.apache.commons.codec.binary.Base64;
@UtilityClass
public final class KitRsaEncryptUtil {
private static final String SRC = "123456";
private static final String SRC = "123456";
public static void main(String[] args) throws Exception {
System.out.println("\n");
RsaKeyPair keyPair = generateKeyPair();
System.out.println("公钥:" + keyPair.getPublicKey());
System.out.println("私钥:" + keyPair.getPrivateKey());
System.out.println("\n");
test1(keyPair);
System.out.println("\n");
test2(keyPair);
System.out.println("\n");
public static void main(String[] args) throws Exception {
System.out.println("\n");
RsaKeyPair keyPair = generateKeyPair();
System.out.println("公钥:" + keyPair.getPublicKey());
System.out.println("私钥:" + keyPair.getPrivateKey());
System.out.println("\n");
test1(keyPair);
System.out.println("\n");
test2(keyPair);
System.out.println("\n");
}
/**
* 公钥加密私钥解密
*/
private static void test1(RsaKeyPair keyPair) throws Exception {
System.out.println("***************** 公钥加密私钥解密开始 *****************");
String text1 = encryptByPublicKey(keyPair.getPublicKey(), KitRsaEncryptUtil.SRC);
String text2 = decryptByPrivateKey(keyPair.getPrivateKey(), text1);
System.out.println("加密前:" + KitRsaEncryptUtil.SRC);
System.out.println("加密后:" + text1);
System.out.println("解密后:" + text2);
if (KitRsaEncryptUtil.SRC.equals(text2)) {
System.out.println("解密字符串和原始字符串一致, 解密成功");
} else {
System.out.println("解密字符串和原始字符串不一致,解密失败");
}
System.out.println("***************** 公钥加密私钥解密结束 *****************");
}
/**
* 私钥加密公钥解密
*
* @throws Exception /
*/
private static void test2(RsaKeyPair keyPair) throws Exception {
System.out.println("***************** 私钥加密公钥解密开始 *****************");
String text1 = encryptByPrivateKey(keyPair.getPrivateKey(), KitRsaEncryptUtil.SRC);
String text2 = decryptByPublicKey(keyPair.getPublicKey(), text1);
System.out.println("加密前:" + KitRsaEncryptUtil.SRC);
System.out.println("加密后:" + text1);
System.out.println("解密后:" + text2);
if (KitRsaEncryptUtil.SRC.equals(text2)) {
System.out.println("解密字符串和原始字符串一致,解密成功");
} else {
System.out.println("解密字符串和原始字符串不一致,解密失败");
}
System.out.println("***************** 私钥加密公钥解密结束 *****************");
}
/**
* 公钥解密
*
* @param publicKeyText 公钥
* @param text 待解密的信息
* @return /
* @throws Exception /
*/
public static String decryptByPublicKey(String publicKeyText, String text) throws Exception {
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyText));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, publicKey);
byte[] result = doLongerCipherFinal(Cipher.DECRYPT_MODE, cipher, Base64.decodeBase64(text));
return new String(result);
}
/**
* 私钥加密
*
* @param privateKeyText 私钥
* @param text 待加密的信息
* @return /
* @throws Exception /
*/
public static String encryptByPrivateKey(String privateKeyText, String text) throws Exception {
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyText));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] result = doLongerCipherFinal(Cipher.ENCRYPT_MODE, cipher, text.getBytes());
return Base64.encodeBase64String(result);
}
/**
* 私钥解密
*
* @param privateKeyText 私钥
* @param text 待解密的文本
* @return /
* @throws Exception /
*/
public static String decryptByPrivateKey(String privateKeyText, String text) throws Exception {
PKCS8EncodedKeySpec pkcs8EncodedKeySpec5 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyText));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec5);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] result = doLongerCipherFinal(Cipher.DECRYPT_MODE, cipher, Base64.decodeBase64(text));
return new String(result);
}
/**
* 公钥加密
*
* @param publicKeyText 公钥
* @param text 待加密的文本
* @return /
*/
public static String encryptByPublicKey(String publicKeyText, String text) throws Exception {
X509EncodedKeySpec x509EncodedKeySpec2 = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyText));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec2);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] result = doLongerCipherFinal(Cipher.ENCRYPT_MODE, cipher, text.getBytes());
return Base64.encodeBase64String(result);
}
private static byte[] doLongerCipherFinal(int opMode, Cipher cipher, byte[] source) throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
if (opMode == Cipher.DECRYPT_MODE) {
out.write(cipher.doFinal(source));
} else {
int offset = 0;
int totalSize = source.length;
while (totalSize - offset > 0) {
int size = Math.min(cipher.getOutputSize(0) - 11, totalSize - offset);
out.write(cipher.doFinal(source, offset, size));
offset += size;
}
}
out.close();
return out.toByteArray();
}
/**
* 构建RSA密钥对
*
* @return /
* @throws NoSuchAlgorithmException /
*/
public static RsaKeyPair generateKeyPair() throws NoSuchAlgorithmException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
String publicKeyString = Base64.encodeBase64String(rsaPublicKey.getEncoded());
String privateKeyString = Base64.encodeBase64String(rsaPrivateKey.getEncoded());
return new RsaKeyPair(publicKeyString, privateKeyString);
}
/**
* RSA密钥对对象
*/
public static class RsaKeyPair {
private final String publicKey;
private final String privateKey;
public RsaKeyPair(String publicKey, String privateKey) {
this.publicKey = publicKey;
this.privateKey = privateKey;
}
/**
* 公钥加密私钥解密
*/
private static void test1(RsaKeyPair keyPair) throws Exception {
System.out.println("***************** 公钥加密私钥解密开始 *****************");
String text1 = encryptByPublicKey(keyPair.getPublicKey(), KitRsaEncryptUtil.SRC);
String text2 = decryptByPrivateKey(keyPair.getPrivateKey(), text1);
System.out.println("加密前:" + KitRsaEncryptUtil.SRC);
System.out.println("加密后:" + text1);
System.out.println("解密后:" + text2);
if (KitRsaEncryptUtil.SRC.equals(text2)) {
System.out.println("解密字符串和原始字符串一致, 解密成功");
} else {
System.out.println("解密字符串和原始字符串不一致,解密失败");
}
System.out.println("***************** 公钥加密私钥解密结束 *****************");
public String getPublicKey() {
return publicKey;
}
/**
* 私钥加密公钥解密
*
* @throws Exception /
*/
private static void test2(RsaKeyPair keyPair) throws Exception {
System.out.println("***************** 私钥加密公钥解密开始 *****************");
String text1 = encryptByPrivateKey(keyPair.getPrivateKey(), KitRsaEncryptUtil.SRC);
String text2 = decryptByPublicKey(keyPair.getPublicKey(), text1);
System.out.println("加密前:" + KitRsaEncryptUtil.SRC);
System.out.println("加密后:" + text1);
System.out.println("解密后:" + text2);
if (KitRsaEncryptUtil.SRC.equals(text2)) {
System.out.println("解密字符串和原始字符串一致,解密成功");
} else {
System.out.println("解密字符串和原始字符串不一致,解密失败");
}
System.out.println("***************** 私钥加密公钥解密结束 *****************");
}
/**
* 公钥解密
*
* @param publicKeyText 公钥
* @param text 待解密的信息
* @return /
* @throws Exception /
*/
public static String decryptByPublicKey(String publicKeyText, String text) throws Exception {
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyText));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, publicKey);
byte[] result = doLongerCipherFinal(Cipher.DECRYPT_MODE, cipher, Base64.decodeBase64(text));
return new String(result);
}
/**
* 私钥加密
*
* @param privateKeyText 私钥
* @param text 待加密的信息
* @return /
* @throws Exception /
*/
public static String encryptByPrivateKey(String privateKeyText, String text) throws Exception {
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyText));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] result = doLongerCipherFinal(Cipher.ENCRYPT_MODE, cipher, text.getBytes());
return Base64.encodeBase64String(result);
}
/**
* 私钥解密
*
* @param privateKeyText 私钥
* @param text 待解密的文本
* @return /
* @throws Exception /
*/
public static String decryptByPrivateKey(String privateKeyText, String text) throws Exception {
PKCS8EncodedKeySpec pkcs8EncodedKeySpec5 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyText));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec5);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] result = doLongerCipherFinal(Cipher.DECRYPT_MODE, cipher, Base64.decodeBase64(text));
return new String(result);
}
/**
* 公钥加密
*
* @param publicKeyText 公钥
* @param text 待加密的文本
* @return /
*/
public static String encryptByPublicKey(String publicKeyText, String text) throws Exception {
X509EncodedKeySpec x509EncodedKeySpec2 = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyText));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec2);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] result = doLongerCipherFinal(Cipher.ENCRYPT_MODE, cipher, text.getBytes());
return Base64.encodeBase64String(result);
}
private static byte[] doLongerCipherFinal(int opMode, Cipher cipher, byte[] source) throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
if (opMode == Cipher.DECRYPT_MODE) {
out.write(cipher.doFinal(source));
} else {
int offset = 0;
int totalSize = source.length;
while (totalSize - offset > 0) {
int size = Math.min(cipher.getOutputSize(0) - 11, totalSize - offset);
out.write(cipher.doFinal(source, offset, size));
offset += size;
}
}
out.close();
return out.toByteArray();
}
/**
* 构建RSA密钥对
*
* @return /
* @throws NoSuchAlgorithmException /
*/
public static RsaKeyPair generateKeyPair() throws NoSuchAlgorithmException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
RSAPublicKey rsaPublicKey = (RSAPublicKey)keyPair.getPublic();
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey)keyPair.getPrivate();
String publicKeyString = Base64.encodeBase64String(rsaPublicKey.getEncoded());
String privateKeyString = Base64.encodeBase64String(rsaPrivateKey.getEncoded());
return new RsaKeyPair(publicKeyString, privateKeyString);
}
/**
* RSA密钥对对象
*/
public static class RsaKeyPair {
private final String publicKey;
private final String privateKey;
public RsaKeyPair(String publicKey, String privateKey) {
this.publicKey = publicKey;
this.privateKey = privateKey;
}
public String getPublicKey() {
return publicKey;
}
public String getPrivateKey() {
return privateKey;
}
public String getPrivateKey() {
return privateKey;
}
}
}
@@ -29,102 +29,99 @@ import lombok.extern.slf4j.Slf4j;
@UtilityClass
public final class KitStringUtil {
private static final char SEPARATOR = '_';
private static final char SEPARATOR = '_';
/**
* 驼峰命名法工具
*
* @return toCamelCase(" hello_world ") == "helloWorld" toCapitalizeCamelCase("hello_world") == "HelloWorld"
* toUnderScoreCase("helloWorld") = "hello_world"
*/
public static String toCamelCase(String s) {
if (s == null) {
return null;
}
s = s.toLowerCase();
StringBuilder sb = new StringBuilder(s.length());
boolean upperCase = false;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == SEPARATOR) {
upperCase = true;
} else if (upperCase) {
sb.append(Character.toUpperCase(c));
upperCase = false;
} else {
sb.append(c);
}
}
return sb.toString();
/**
* 驼峰命名法工具
*
* @return toCamelCase(" hello_world ") == "helloWorld" toCapitalizeCamelCase("hello_world") == "HelloWorld" toUnderScoreCase("helloWorld") = "hello_world"
*/
public static String toCamelCase(String s) {
if (s == null) {
return null;
}
/**
* 驼峰命名法工具
*
* @return toCamelCase(" hello_world ") == "helloWorld" toCapitalizeCamelCase("hello_world") == "HelloWorld"
* toUnderScoreCase("helloWorld") = "hello_world"
*/
public static String toCapitalizeCamelCase(String s) {
if (s == null) {
return null;
}
s = toCamelCase(s);
return s.substring(0, 1).toUpperCase() + s.substring(1);
s = s.toLowerCase();
StringBuilder sb = new StringBuilder(s.length());
boolean upperCase = false;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == SEPARATOR) {
upperCase = true;
} else if (upperCase) {
sb.append(Character.toUpperCase(c));
upperCase = false;
} else {
sb.append(c);
}
}
/**
* 驼峰命名法工具
*
* @return toCamelCase(" hello_world ") == "helloWorld" toCapitalizeCamelCase("hello_world") == "HelloWorld"
* toUnderScoreCase("helloWorld") = "hello_world"
*/
public static String toUnderScoreCase(String s) {
if (s == null) {
return null;
}
return sb.toString();
}
StringBuilder sb = new StringBuilder();
boolean upperCase = false;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
/**
* 驼峰命名法工具
*
* @return toCamelCase(" hello_world ") == "helloWorld" toCapitalizeCamelCase("hello_world") == "HelloWorld" toUnderScoreCase("helloWorld") = "hello_world"
*/
public static String toCapitalizeCamelCase(String s) {
if (s == null) {
return null;
}
s = toCamelCase(s);
return s.substring(0, 1).toUpperCase() + s.substring(1);
}
boolean nextUpperCase = true;
if (i < (s.length() - 1)) {
nextUpperCase = Character.isUpperCase(s.charAt(i + 1));
}
if ((i > 0) && Character.isUpperCase(c)) {
if (!upperCase || !nextUpperCase) {
sb.append(SEPARATOR);
}
upperCase = true;
} else {
upperCase = false;
}
sb.append(Character.toLowerCase(c));
}
return sb.toString();
/**
* 驼峰命名法工具
*
* @return toCamelCase(" hello_world ") == "helloWorld" toCapitalizeCamelCase("hello_world") == "HelloWorld" toUnderScoreCase("helloWorld") = "hello_world"
*/
public static String toUnderScoreCase(String s) {
if (s == null) {
return null;
}
/**
* 获得当天是周几
*/
public static String getWeekDay() {
// String[] weekDays = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
String[] weekDays = {"星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期天"};
Calendar cal = Calendar.getInstance();
cal.setTime(new Date());
int w = cal.get(Calendar.DAY_OF_WEEK) - 1;
if (w < 0) {
w = 0;
StringBuilder sb = new StringBuilder();
boolean upperCase = false;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
boolean nextUpperCase = true;
if (i < (s.length() - 1)) {
nextUpperCase = Character.isUpperCase(s.charAt(i + 1));
}
if ((i > 0) && Character.isUpperCase(c)) {
if (!upperCase || !nextUpperCase) {
sb.append(SEPARATOR);
}
return weekDays[w];
upperCase = true;
} else {
upperCase = false;
}
sb.append(Character.toLowerCase(c));
}
return sb.toString();
}
/**
* 获得当天是周几
*/
public static String getWeekDay() {
// String[] weekDays = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
String[] weekDays = {"星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期天"};
Calendar cal = Calendar.getInstance();
cal.setTime(new Date());
int w = cal.get(Calendar.DAY_OF_WEEK) - 1;
if (w < 0) {
w = 0;
}
return weekDays[w];
}
}
@@ -27,16 +27,17 @@ import oshi.hardware.CentralProcessor;
* @date 2025-08-11
*/
public class KitSystemUtil {
public static int getCpuCount() {
SystemInfo systemInfo = new SystemInfo();
CentralProcessor processor = systemInfo.getHardware().getProcessor();
return processor.getPhysicalProcessorCount();
}
/**
* 获取当前系统的用户名
*/
public static String getCurrentUserName() {
return System.getProperty("user.name");
}
public static int getCpuCount() {
SystemInfo systemInfo = new SystemInfo();
CentralProcessor processor = systemInfo.getHardware().getProcessor();
return processor.getPhysicalProcessorCount();
}
/**
* 获取当前系统的用户名
*/
public static String getCurrentUserName() {
return System.getProperty("user.name");
}
}
@@ -34,19 +34,20 @@ import lombok.experimental.UtilityClass;
*/
@UtilityClass
public final class KitValidUtil {
private static final Validator VALIDATOR;
static {
try (ValidatorFactory factory = Validation.buildDefaultValidatorFactory()) {
VALIDATOR = factory.getValidator();
}
}
private static final Validator VALIDATOR;
public static <T> void validate(T object) {
Set<ConstraintViolation<T>> violations = VALIDATOR.validate(object, Default.class);
if (!violations.isEmpty()) {
throw new BadRequestException(
violations.stream().map(ConstraintViolation::getMessage).collect(Collectors.joining(",")));
}
static {
try (ValidatorFactory factory = Validation.buildDefaultValidatorFactory()) {
VALIDATOR = factory.getValidator();
}
}
public static <T> void validate(T object) {
Set<ConstraintViolation<T>> violations = VALIDATOR.validate(object, Default.class);
if (!violations.isEmpty()) {
throw new BadRequestException(
violations.stream().map(ConstraintViolation::getMessage).collect(Collectors.joining(",")));
}
}
}