1、结构调整

2、抽象工厂优化
This commit is contained in:
2026-03-31 19:35:21 +08:00
parent 87757b352c
commit ebdbdbeb41
667 changed files with 1240 additions and 50173 deletions

View File

@@ -2,36 +2,45 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.njcn</groupId>
<artifactId>msgpush-framework</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>msgpush-common</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>定义基础 pojo 类、枚举、工具类等等</description>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- Spring 核心 -->
<!-- Spring 核心工具类,提供资源加载、类型转换等基础功能 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 -->
</dependency>
<!-- Spring 表达式语言(SpEL),用于动态表达式解析 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 -->
</dependency>
<!-- Spring AOP 支持,提供面向切面编程能力 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 -->
</dependency>
<!-- AspectJ 织入器,实现 AOP 切面逻辑 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
@@ -46,25 +55,28 @@
</dependency>
<!-- Web 相关 -->
<!-- Spring Web 工具类,提供 HTTP 请求处理、文件上传等功能 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 -->
</dependency>
<!-- Jakarta Servlet API,定义 HTTP 请求响应接口规范 -->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 -->
</dependency>
<!-- Swagger 注解,用于 API 文档生成(@Schema、@Operation 等) -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<scope>provided</scope> <!-- 设置为 provided主要是 PageParam 使用到 -->
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations</artifactId>
</dependency>
<!-- RPC 远程调用相关 -->
<!-- OpenFeign 核心,用于声明式 HTTP 客户端(@FeignClient 注解) -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign-core</artifactId>
@@ -72,6 +84,7 @@
</dependency>
<!-- 监控相关 -->
<!-- SkyWalking 链路追踪工具包,用于分布式追踪(@Trace 注解) -->
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-trace</artifactId>
@@ -79,78 +92,92 @@
<!-- 工具类相关 -->
<dependency>
<!-- Lombok,自动生成 getter/setter/构造器等样板代码 -->
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<!-- MapStruct 核心,用于对象映射转换(DTO/VO/DO 互转) -->
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
</dependency>
<dependency>
<!-- MapStruct JDK8+ 支持,提供 Java 8 时间类型等映射 -->
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId> <!-- use mapstruct-jdk8 for Java 8 or higher -->
<artifactId>mapstruct-jdk8</artifactId>
</dependency>
<dependency>
<!-- MapStruct 注解处理器,编译期生成映射实现代码 -->
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
</dependency>
<dependency>
<!-- Google Guava,提供集合、缓存、并发等增强工具类 -->
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 -->
</dependency>
<dependency>
<!-- Jackson 数据绑定,用于 JSON 序列化/反序列化 -->
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 -->
</dependency>
<dependency>
<!-- Jackson 核心,提供 JSON 解析底层能力 -->
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 -->
</dependency>
<dependency>
<!-- Jackson JSR310 支持,用于 LocalDateTime 等 Java 8 时间类型序列化 -->
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 -->
</dependency>
<dependency>
<!-- SLF4J 日志门面,提供统一的日志接口 -->
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 -->
</dependency>
<dependency>
<!-- Jakarta Validation API,用于参数校验(@NotNull、@Valid 等注解) -->
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
<scope>provided</scope> <!-- 设置为 provided主要是 PageParam 使用到 -->
</dependency>
<dependency>
<!-- Hutool 工具包,提供日期、加密、HTTP、Excel 等常用工具类 -->
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<dependency>
<!-- 阿里 TTL,用于线程池场景下 ThreadLocal 值的传递 -->
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
</dependency>
<dependency>
<groupId>com.fhs-opensource</groupId> <!-- VO 数据翻译 -->
<!-- Easy-Trans 注解包,用于 VO 数据字典翻译(@Trans 注解) -->
<groupId>com.fhs-opensource</groupId>
<artifactId>easy-trans-anno</artifactId> <!-- 默认引入的原因,方便 xxx-module-api 包使用 -->
</dependency>
<!-- Test 测试相关 -->
<dependency>
<!-- Spring Boot 测试启动器,集成 JUnit、Mockito 等测试框架 -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
</project>

View File

@@ -11,8 +11,6 @@ import java.util.List;
* 为什么要赋值粘贴到 msgpush-common 包下?
* 因为 AutoTransable 属于 easy-trans-service 下,无法方便的在 msgpush-module-xxx-api 模块下使用
*
* @author hongawen
* @since 2020-05-19 10:26:15
*/
public interface AutoTransable<V extends VO> {

View File

@@ -1,4 +0,0 @@
/**
* 针对 infra 模块的 api 包
*/
package com.njcn.msgpush.framework.common.biz.infra;

View File

@@ -12,7 +12,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
@FeignClient(name = RpcConstants.SYSTEM_NAME, primary = false) // TODO 芋艿fallbackFactory =
@FeignClient(name = RpcConstants.SYSTEM_NAME, primary = false)
@Tag(name = "RPC 服务 - 字典数据")
public interface DictDataCommonApi {

View File

@@ -10,7 +10,7 @@ public class DictDataRespDTO {
@Schema(description = "字典标签", requiredMode = Schema.RequiredMode.REQUIRED, example = "灿能")
private String label;
@Schema(description = "字典值", requiredMode = Schema.RequiredMode.REQUIRED, example = "iocoder")
@Schema(description = "字典值", requiredMode = Schema.RequiredMode.REQUIRED, example = "njcn")
private String value;
@Schema(description = "字典类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "sys_common_sex")

View File

@@ -11,7 +11,7 @@ import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@FeignClient(name = RpcConstants.SYSTEM_NAME, primary = false) // TODO 芋艿fallbackFactory =
@FeignClient(name = RpcConstants.SYSTEM_NAME, primary = false)
@Tag(name = "RPC 服务 - 操作日志")
public interface OperateLogCommonApi {

View File

@@ -1,20 +1,19 @@
package com.njcn.msgpush.framework.common.biz.system.oauth2;
import com.njcn.msgpush.framework.common.enums.RpcConstants;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenCheckRespDTO;
import com.njcn.msgpush.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenCreateReqDTO;
import com.njcn.msgpush.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenRespDTO;
import io.swagger.v3.oas.annotations.tags.Tag;
import com.njcn.msgpush.framework.common.enums.RpcConstants;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
import jakarta.validation.Valid;
@FeignClient(name = RpcConstants.SYSTEM_NAME) // TODO 芋艿fallbackFactory =
@FeignClient(name = RpcConstants.SYSTEM_NAME)
@Tag(name = "RPC 服务 - OAuth2.0 令牌")
public interface OAuth2TokenCommonApi {

View File

@@ -8,23 +8,23 @@ import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
@Schema(description = "RPC 服务 - OAuth2 访问令牌的校验 Response DTO")
@Schema(description = "RPC service - OAuth2 access token check response")
@Data
public class OAuth2AccessTokenCheckRespDTO implements Serializable {
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
@Schema(description = "User id", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
private Long userId;
@Schema(description = "用户类型,参见 UserTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@Schema(description = "User type", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer userType;
@Schema(description = "用户信息", example = "{\"nickname\": \"灿能\"}")
@Schema(description = "User info", example = "{\"nickname\": \"msgpush\"}")
private Map<String, String> userInfo;
@Schema(description = "授权范围的数组", example = "user_info")
@Schema(description = "Scopes", example = "user_info")
private List<String> scopes;
@Schema(description = "过期时间", requiredMode = Schema.RequiredMode.REQUIRED)
@Schema(description = "Expire time", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime expiresTime;
}

View File

@@ -3,9 +3,9 @@ package com.njcn.msgpush.framework.common.biz.system.oauth2.dto;
import com.njcn.msgpush.framework.common.enums.UserTypeEnum;
import com.njcn.msgpush.framework.common.validation.InEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import jakarta.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.List;

View File

@@ -1,6 +1,5 @@
package com.njcn.msgpush.framework.common.biz.system.permission;
import com.njcn.msgpush.framework.common.biz.system.permission.dto.DeptDataPermissionRespDTO;
import com.njcn.msgpush.framework.common.enums.RpcConstants;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import io.swagger.v3.oas.annotations.Operation;
@@ -11,7 +10,7 @@ import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(name = RpcConstants.SYSTEM_NAME, primary = false) // TODO 芋艿fallbackFactory =
@FeignClient(name = RpcConstants.SYSTEM_NAME, primary = false)
@Tag(name = "RPC 服务 - 权限")
public interface PermissionCommonApi {
@@ -35,9 +34,4 @@ public interface PermissionCommonApi {
CommonResult<Boolean> hasAnyRoles(@RequestParam("userId") Long userId,
@RequestParam("roles") String... roles);
@GetMapping(PREFIX + "/get-dept-data-permission")
@Operation(summary = "获得登陆用户的部门数据权限")
@Parameter(name = "userId", description = "用户编号", example = "2", required = true)
CommonResult<DeptDataPermissionRespDTO> getDeptDataPermission(@RequestParam("userId") Long userId);
}

View File

@@ -1,28 +0,0 @@
package com.njcn.msgpush.framework.common.biz.system.permission.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.HashSet;
import java.util.Set;
@Schema(description = "RPC 服务 - 部门的数据权限 Response DTO")
@Data
public class DeptDataPermissionRespDTO {
@Schema(description = "是否可查看全部数据", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
private Boolean all;
@Schema(description = "是否可查看自己的数据", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
private Boolean self;
@Schema(description = "可查看的部门编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1, 3]")
private Set<Long> deptIds;
public DeptDataPermissionRespDTO() {
this.all = false;
this.self = false;
this.deptIds = new HashSet<>();
}
}

View File

@@ -3,7 +3,7 @@ package com.njcn.msgpush.framework.common.core;
/**
* 可生成 T 数组的接口
*
* @author HUIHUI
* @author hongawen
*/
public interface ArrayValuable<T> {

View File

@@ -3,11 +3,6 @@ package com.njcn.msgpush.framework.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 文档地址
*
* @author hongawen
*/
@Getter
@AllArgsConstructor
public enum DocumentEnum {

View File

@@ -19,22 +19,12 @@ public interface RpcConstants {
*
* 注意,需要保证和 spring.application.name 保持一致
*/
String SYSTEM_NAME = "system-server";
String SYSTEM_NAME = "msgpush-system-server";
/**
* system 服务的前缀
*/
String SYSTEM_PREFIX = RPC_API_PREFIX + "/system";
/**
* infra 服务名
*
* 注意,需要保证和 spring.application.name 保持一致
*/
String INFRA_NAME = "infra-server";
/**
* infra 服务的前缀
*/
String INFRA_PREFIX = RPC_API_PREFIX + "/infra";
}

View File

@@ -9,13 +9,17 @@ import java.util.Arrays;
/**
* 全局用户类型枚举
*
* 用于区分不同访问端的用户类型:
* - MEMBER: 对应 /app-api/** 路径,通常用于移动端或 C 端用户
* - ADMIN: 对应 /admin-api/** 路径,通常用于 Web 管理端或 B 端用户
*/
@AllArgsConstructor
@Getter
public enum UserTypeEnum implements ArrayValuable<Integer> {
MEMBER(1, "会员"), // 面向 c 端,普通用户
ADMIN(2, "管理员"); // 面向 b 端,管理后台
MEMBER(1, "App端用户"), // 对应 /app-api移动端或 C 端
ADMIN(2, "Web端用户"); // 对应 /admin-apiWeb 管理端或 B 端
public static final Integer[] ARRAYS = Arrays.stream(values()).map(UserTypeEnum::getValue).toArray(Integer[]::new);

View File

@@ -29,12 +29,11 @@ public interface GlobalErrorCodeConstants {
// ========== 服务端错误段 ==========
ErrorCode INTERNAL_SERVER_ERROR = new ErrorCode(500, "系统异常");
ErrorCode NOT_IMPLEMENTED = new ErrorCode(501, "功能未实现/未开启");
ErrorCode TABLE_NOT_EXISTS = new ErrorCode(501, "表不存在");
ErrorCode ERROR_CONFIGURATION = new ErrorCode(502, "错误的配置项");
// ========== 自定义错误段 ==========
ErrorCode REPEATED_REQUESTS = new ErrorCode(900, "重复请求,请稍后重试"); // 重复请求
ErrorCode DEMO_DENY = new ErrorCode(901, "演示模式,禁止写操作");
ErrorCode UNKNOWN = new ErrorCode(999, "未知错误");

View File

@@ -1,9 +1,9 @@
package com.njcn.msgpush.framework.common.exception.util;
import com.google.common.annotations.VisibleForTesting;
import com.njcn.msgpush.framework.common.exception.ErrorCode;
import com.njcn.msgpush.framework.common.exception.ServiceException;
import com.njcn.msgpush.framework.common.exception.enums.GlobalErrorCodeConstants;
import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j;
/**

View File

@@ -1,6 +0,0 @@
/**
* 基础的通用类,和框架无关
*
* 例如说CommonResult 为通用返回
*/
package com.njcn.msgpush.framework.common;

View File

@@ -1,11 +1,11 @@
package com.njcn.msgpush.framework.common.pojo;
import cn.hutool.core.lang.Assert;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.njcn.msgpush.framework.common.exception.ErrorCode;
import com.njcn.msgpush.framework.common.exception.ServiceException;
import com.njcn.msgpush.framework.common.exception.enums.GlobalErrorCodeConstants;
import com.njcn.msgpush.framework.common.exception.util.ServiceExceptionUtil;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import java.io.Serializable;

View File

@@ -1,11 +1,11 @@
package com.njcn.msgpush.framework.common.pojo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.NotNull;
import java.io.Serializable;
@Schema(description="分页参数")

View File

@@ -40,7 +40,7 @@ public class CacheUtils {
// 只阻塞当前数据加载线程,其他线程返回旧值
.refreshAfterWrite(duration)
// 通过 asyncReloading 实现全异步加载,包括 refreshAfterWrite 被阻塞的加载线程
.build(CacheLoader.asyncReloading(loader, Executors.newCachedThreadPool())); // TODO 芋艿:可能要思考下,未来要不要做成可配置
.build(CacheLoader.asyncReloading(loader, Executors.newCachedThreadPool())); // TODO 可能要思考下,未来要不要做成可配置
}
/**

View File

@@ -3,8 +3,8 @@ package com.njcn.msgpush.framework.common.util.collection;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ArrayUtil;
import com.njcn.msgpush.framework.common.pojo.PageResult;
import com.google.common.collect.ImmutableMap;
import com.njcn.msgpush.framework.common.pojo.PageResult;
import java.util.*;
import java.util.function.*;

View File

@@ -3,10 +3,11 @@ package com.njcn.msgpush.framework.common.util.collection;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjUtil;
import com.njcn.msgpush.framework.common.core.KeyValue;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.njcn.msgpush.framework.common.core.KeyValue;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -65,4 +66,47 @@ public class MapUtils {
return map;
}
/**
* 从 Map 中获取 BigDecimal 值
*
* @param map Map 数据源
* @param key 键名
* @return BigDecimal 值,解析失败或值为 null 时返回 null
*/
public static BigDecimal getBigDecimal(Map<String, ?> map, String key) {
return getBigDecimal(map, key, null);
}
/**
* 从 Map 中获取 BigDecimal 值
*
* @param map Map 数据源
* @param key 键名
* @param defaultValue 默认值
* @return BigDecimal 值,解析失败或值为 null 时返回默认值
*/
public static BigDecimal getBigDecimal(Map<String, ?> map, String key, BigDecimal defaultValue) {
if (map == null) {
return defaultValue;
}
Object value = map.get(key);
if (value == null) {
return defaultValue;
}
if (value instanceof BigDecimal) {
return (BigDecimal) value;
}
if (value instanceof Number) {
return BigDecimal.valueOf(((Number) value).doubleValue());
}
if (value instanceof String) {
try {
return new BigDecimal((String) value);
} catch (NumberFormatException e) {
return defaultValue;
}
}
return defaultValue;
}
}

View File

@@ -17,7 +17,8 @@ import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.List;
import static cn.hutool.core.date.DatePattern.*;
import static cn.hutool.core.date.DatePattern.UTC_MS_WITH_XXX_OFFSET_PATTERN;
import static cn.hutool.core.date.DatePattern.createFormatter;
/**
* 时间工具类,用于 {@link LocalDateTime}

View File

@@ -7,7 +7,7 @@ import cn.hutool.core.util.StrUtil;
import java.io.InputStream;
/**
* IO 工具类,用于 {@link cn.hutool.core.io.IoUtil} 缺失的方法
* IO 工具类,用于 {@link IoUtil} 缺失的方法
*
* @author hongawen
*/

View File

@@ -3,8 +3,6 @@ package com.njcn.msgpush.framework.common.util.json;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.njcn.msgpush.framework.common.util.json.databind.TimestampLocalDateTimeDeserializer;
import com.njcn.msgpush.framework.common.util.json.databind.TimestampLocalDateTimeSerializer;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
@@ -13,6 +11,8 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.njcn.msgpush.framework.common.util.json.databind.TimestampLocalDateTimeDeserializer;
import com.njcn.msgpush.framework.common.util.json.databind.TimestampLocalDateTimeSerializer;
import lombok.Getter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
@@ -229,4 +229,53 @@ public class JsonUtils {
return JSONUtil.isTypeJSONObject(str);
}
/**
* 将 Object 转换为目标类型
* <p>
* 避免先转 jsonString 再 parseObject 的性能损耗
*
* @param obj 源对象(可以是 Map、POJO 等)
* @param clazz 目标类型
* @return 转换后的对象
*/
public static <T> T convertObject(Object obj, Class<T> clazz) {
if (obj == null) {
return null;
}
if (clazz.isInstance(obj)) {
return clazz.cast(obj);
}
return objectMapper.convertValue(obj, clazz);
}
/**
* 将 Object 转换为目标类型(支持泛型)
*
* @param obj 源对象
* @param typeReference 目标类型引用
* @return 转换后的对象
*/
public static <T> T convertObject(Object obj, TypeReference<T> typeReference) {
if (obj == null) {
return null;
}
return objectMapper.convertValue(obj, typeReference);
}
/**
* 将 Object 转换为 List 类型
* <p>
* 避免先转 jsonString 再 parseArray 的性能损耗
*
* @param obj 源对象(可以是 List、数组等
* @param clazz 目标元素类型
* @return 转换后的 List
*/
public static <T> List<T> convertList(Object obj, Class<T> clazz) {
if (obj == null) {
return new ArrayList<>();
}
return objectMapper.convertValue(obj, objectMapper.getTypeFactory().constructCollectionType(List.class, clazz));
}
}

View File

@@ -8,7 +8,7 @@ import java.math.BigDecimal;
import java.util.List;
/**
* 数字的工具类,补全 {@link cn.hutool.core.util.NumberUtil} 的功能
* 数字的工具类,补全 {@link NumberUtil} 的功能
*
* @author hongawen
*/

View File

@@ -10,7 +10,7 @@ import java.util.function.Consumer;
/**
* Bean 工具类
*
* 1. 默认使用 {@link cn.hutool.core.bean.BeanUtil} 作为实现类,虽然不同 bean 工具的性能有差别,但是对绝大多数同学的项目,不用在意这点性能
* 1. 默认使用 {@link BeanUtil} 作为实现类,虽然不同 bean 工具的性能有差别,但是对绝大多数同学的项目,不用在意这点性能
* 2. 针对复杂的对象转换,可以搜参考 AuthConvert 实现,通过 mapstruct + default 配合实现
*
* @author hongawen

View File

@@ -13,7 +13,7 @@ import org.springframework.util.Assert;
import static java.util.Collections.singletonList;
/**
* {@link com.njcn.msgpush.framework.common.pojo.PageParam} 工具类
* {@link PageParam} 工具类
*
* @author hongawen
*/

View File

@@ -2,12 +2,12 @@ package com.njcn.msgpush.framework.common.util.validation;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import org.springframework.util.StringUtils;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import org.springframework.util.StringUtils;
import java.util.Set;
import java.util.regex.Pattern;

View File

@@ -2,6 +2,7 @@ package com.njcn.msgpush.framework.common.validation;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.*;
@Target({

View File

@@ -2,7 +2,6 @@ package com.njcn.msgpush.framework.common.validation;
import cn.hutool.core.util.StrUtil;
import com.njcn.msgpush.framework.common.util.validation.ValidationUtils;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

View File

@@ -2,6 +2,7 @@ package com.njcn.msgpush.framework.common.validation;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.*;
@Target({

View File

@@ -2,7 +2,6 @@ package com.njcn.msgpush.framework.common.validation;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.PhoneUtil;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

View File

@@ -1,64 +0,0 @@
package com.njcn.msgpush.framework.common.util.collection;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.function.BiFunction;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* {@link CollectionUtils} 的单元测试
*/
public class CollectionUtilsTest {
@Data
@AllArgsConstructor
private static class Dog {
private Integer id;
private String name;
private String code;
}
@Test
public void testDiffList() {
// 准备参数
Collection<Dog> oldList = Arrays.asList(
new Dog(1, "花花", "hh"),
new Dog(2, "旺财", "wc")
);
Collection<Dog> newList = Arrays.asList(
new Dog(null, "花花2", "hh"),
new Dog(null, "小白", "xb")
);
BiFunction<Dog, Dog, Boolean> sameFunc = (oldObj, newObj) -> {
boolean same = oldObj.getCode().equals(newObj.getCode());
// 如果相等的情况下,需要设置下 id后续好更新
if (same) {
newObj.setId(oldObj.getId());
}
return same;
};
// 调用
List<List<Dog>> result = CollectionUtils.diffList(oldList, newList, sameFunc);
// 断言
assertEquals(result.size(), 3);
// 断言 create
assertEquals(result.get(0).size(), 1);
assertEquals(result.get(0).get(0), new Dog(null, "小白", "xb"));
// 断言 update
assertEquals(result.get(1).size(), 1);
assertEquals(result.get(1).get(0), new Dog(1, "花花2", "hh"));
// 断言 delete
assertEquals(result.get(2).size(), 1);
assertEquals(result.get(2).get(0), new Dog(2, "旺财", "wc"));
}
}