支持了传递deleted参数指定查询。
This commit is contained in:
parent
41d384f626
commit
5130c7593a
@ -7,4 +7,9 @@ query.setCreateTimeRange(
|
|||||||
);
|
);
|
||||||
query.setEnabled(true);
|
query.setEnabled(true);
|
||||||
query.setCreateBy("admin");
|
query.setCreateBy("admin");
|
||||||
|
|
||||||
|
|
||||||
|
@QueryField(type = QueryType.IN)
|
||||||
|
private String status; // 可以传入 "ACTIVE,PENDING,CLOSED"
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
package com.qqchen.deploy.backend.common.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface SoftDelete {
|
||||||
|
boolean value() default true; // 默认启用软删除
|
||||||
|
}
|
||||||
@ -10,18 +10,15 @@ import java.time.LocalDateTime;
|
|||||||
@Data
|
@Data
|
||||||
public abstract class BaseQuery implements Serializable {
|
public abstract class BaseQuery implements Serializable {
|
||||||
private Integer pageNum = 1;
|
private Integer pageNum = 1;
|
||||||
|
|
||||||
private Integer pageSize = 10;
|
private Integer pageSize = 10;
|
||||||
|
|
||||||
private String sortField = "createTime";
|
private String sortField = "createTime";
|
||||||
|
|
||||||
private String sortOrder = "desc";
|
private String sortOrder = "desc";
|
||||||
|
|
||||||
// 通用状态查询
|
// 通用状态查询
|
||||||
@QueryField(field = "enabled")
|
@QueryField(field = "enabled")
|
||||||
private Boolean enabled;
|
private Boolean enabled;
|
||||||
|
|
||||||
@QueryField(field = "deleted")
|
@QueryField(field = "deleted", type = QueryType.EQUAL)
|
||||||
private Boolean deleted;
|
private Boolean deleted;
|
||||||
|
|
||||||
// 创建时间范围查询
|
// 创建时间范围查询
|
||||||
|
|||||||
@ -11,8 +11,7 @@ import java.util.List;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@NoRepositoryBean
|
@NoRepositoryBean
|
||||||
public interface BaseRepository<T extends Entity<ID>, ID extends Serializable>
|
public interface BaseRepository<T extends Entity<ID>, ID extends Serializable> extends JpaRepository<T, ID>, QuerydslPredicateExecutor<T> {
|
||||||
extends JpaRepository<T, ID>, QuerydslPredicateExecutor<T> {
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Query("select e from #{#entityName} e where e.deleted = false")
|
@Query("select e from #{#entityName} e where e.deleted = false")
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
package com.qqchen.deploy.backend.common.service.impl;
|
package com.qqchen.deploy.backend.common.service.impl;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.stream.Collectors;
|
import java.time.LocalDateTime;
|
||||||
import java.util.Collection;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.qqchen.deploy.backend.common.annotation.SoftDelete;
|
||||||
import com.qqchen.deploy.backend.common.domain.Entity;
|
import com.qqchen.deploy.backend.common.domain.Entity;
|
||||||
import com.qqchen.deploy.backend.common.enums.QueryType;
|
import com.qqchen.deploy.backend.common.enums.QueryType;
|
||||||
import com.qqchen.deploy.backend.common.query.BaseQuery;
|
import com.qqchen.deploy.backend.common.query.BaseQuery;
|
||||||
@ -13,6 +14,7 @@ import com.qqchen.deploy.backend.common.query.Range;
|
|||||||
import com.qqchen.deploy.backend.common.repository.BaseRepository;
|
import com.qqchen.deploy.backend.common.repository.BaseRepository;
|
||||||
import com.qqchen.deploy.backend.common.annotation.QueryField;
|
import com.qqchen.deploy.backend.common.annotation.QueryField;
|
||||||
import com.qqchen.deploy.backend.common.service.BaseService;
|
import com.qqchen.deploy.backend.common.service.BaseService;
|
||||||
|
import com.qqchen.deploy.backend.common.utils.EntityPathResolver;
|
||||||
import com.querydsl.core.BooleanBuilder;
|
import com.querydsl.core.BooleanBuilder;
|
||||||
import com.querydsl.core.types.EntityPath;
|
import com.querydsl.core.types.EntityPath;
|
||||||
import com.querydsl.core.types.Path;
|
import com.querydsl.core.types.Path;
|
||||||
@ -30,17 +32,233 @@ import java.lang.reflect.Field;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public abstract class BaseServiceImpl<T extends Entity<ID>, ID extends Serializable> implements BaseService<T, ID> {
|
public abstract class BaseServiceImpl<T extends Entity<ID>, ID extends Serializable>
|
||||||
|
implements BaseService<T, ID> {
|
||||||
|
|
||||||
protected final BaseRepository<T, ID> repository;
|
protected final BaseRepository<T, ID> repository;
|
||||||
|
|
||||||
private final EntityPath<T> entityPath;
|
protected final EntityPath<T> entityPath;
|
||||||
|
|
||||||
protected BaseServiceImpl(BaseRepository<T, ID> repository) {
|
protected BaseServiceImpl(BaseRepository<T, ID> repository) {
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
this.entityPath = getEntityPath();
|
this.entityPath = getEntityPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private EntityPath<T> getEntityPath() {
|
||||||
|
Class<?>[] genericTypes = GenericTypeResolver.resolveTypeArguments(getClass(), BaseServiceImpl.class);
|
||||||
|
if (genericTypes == null || genericTypes.length < 2) {
|
||||||
|
throw new IllegalStateException("Could not resolve generic type arguments");
|
||||||
|
}
|
||||||
|
|
||||||
|
Class<T> entityClass = (Class<T>) genericTypes[0];
|
||||||
|
try {
|
||||||
|
String qClassName = entityClass.getPackageName() + ".Q" + entityClass.getSimpleName();
|
||||||
|
Class<?> qClass = Class.forName(qClassName);
|
||||||
|
Field instanceField = qClass.getDeclaredField(entityClass.getSimpleName().toLowerCase());
|
||||||
|
return (EntityPath<T>) instanceField.get(null);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Failed to get Q class for " + entityClass.getName(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Page<T> page(BaseQuery query) {
|
||||||
|
BooleanBuilder builder = new BooleanBuilder().and(Expressions.asBoolean(true).isTrue());
|
||||||
|
|
||||||
|
if (query != null) {
|
||||||
|
buildQueryPredicate(query, builder);
|
||||||
|
}
|
||||||
|
if (query == null || query.getDeleted() == null) {
|
||||||
|
Class<?>[] genericTypes = GenericTypeResolver.resolveTypeArguments(getClass(), BaseServiceImpl.class);
|
||||||
|
if (genericTypes != null && genericTypes.length > 0) {
|
||||||
|
Class<T> entityClass = (Class<T>) genericTypes[0];
|
||||||
|
SoftDelete softDelete = entityClass.getAnnotation(SoftDelete.class);
|
||||||
|
|
||||||
|
if (softDelete != null && softDelete.value()) {
|
||||||
|
Path<?> deletedPath = EntityPathResolver.getPath(entityPath, "deleted");
|
||||||
|
if (deletedPath instanceof BooleanPath) {
|
||||||
|
builder.and(((BooleanPath) deletedPath).eq(false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return repository.findAll(builder, createPageRequest(query));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void buildQueryPredicate(BaseQuery query, BooleanBuilder builder) {
|
||||||
|
// 处理当前类的字段
|
||||||
|
processClassFields(query, query.getClass(), builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processClassFields(Object query, Class<?> clazz, BooleanBuilder builder) {
|
||||||
|
// 如果到达Object类或null,则停止递归
|
||||||
|
if (clazz == null || clazz == Object.class) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理父类的字段
|
||||||
|
processClassFields(query, clazz.getSuperclass(), builder);
|
||||||
|
|
||||||
|
// 处理当前类的字段
|
||||||
|
Field[] fields = clazz.getDeclaredFields();
|
||||||
|
for (Field field : fields) {
|
||||||
|
field.setAccessible(true);
|
||||||
|
try {
|
||||||
|
Object value = field.get(query);
|
||||||
|
if (value != null && StringUtils.hasText(value.toString())) {
|
||||||
|
QueryField queryField = field.getAnnotation(QueryField.class);
|
||||||
|
if (queryField != null) {
|
||||||
|
String fieldName = StringUtils.hasText(queryField.field()) ?
|
||||||
|
queryField.field() : field.getName();
|
||||||
|
Path<?> path = EntityPathResolver.getPath(entityPath, fieldName);
|
||||||
|
if (path != null) {
|
||||||
|
addCondition(builder, path, value, queryField.type());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 忽略无法处理的字段
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addCondition(BooleanBuilder builder, Path<?> path, Object value, QueryType queryType) {
|
||||||
|
Predicate condition = null;
|
||||||
|
|
||||||
|
if (path instanceof StringPath) {
|
||||||
|
condition = createStringCondition((StringPath) path, value.toString(), queryType);
|
||||||
|
} else if (path instanceof BooleanPath) {
|
||||||
|
condition = ((BooleanPath) path).eq(Boolean.valueOf(value.toString()));
|
||||||
|
} else if (path instanceof NumberPath) {
|
||||||
|
condition = createNumberCondition((NumberPath<?>) path, value, queryType);
|
||||||
|
} else if (path instanceof DateTimePath) {
|
||||||
|
condition = createDateCondition((DateTimePath<?>) path, value, queryType);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (condition != null) {
|
||||||
|
builder.and(condition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Predicate createStringCondition(StringPath path, String value, QueryType queryType) {
|
||||||
|
switch (queryType) {
|
||||||
|
case EQUAL:
|
||||||
|
return path.eq(value);
|
||||||
|
case LIKE:
|
||||||
|
return path.containsIgnoreCase(value);
|
||||||
|
case START_WITH:
|
||||||
|
return path.startsWithIgnoreCase(value);
|
||||||
|
case END_WITH:
|
||||||
|
return path.endsWithIgnoreCase(value);
|
||||||
|
case IN:
|
||||||
|
if (value.contains(",")) {
|
||||||
|
List<String> values = Arrays.asList(value.split(","));
|
||||||
|
return path.in(values);
|
||||||
|
}
|
||||||
|
return path.eq(value);
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private <N extends Number & Comparable<?>> Predicate createNumberCondition(NumberPath<N> path, Object value, QueryType queryType) {
|
||||||
|
N numValue = (N) parseNumber(value, path.getType());
|
||||||
|
if (numValue != null) {
|
||||||
|
switch (queryType) {
|
||||||
|
case EQUAL:
|
||||||
|
return path.eq(numValue);
|
||||||
|
case GREATER_THAN:
|
||||||
|
return path.gt(numValue);
|
||||||
|
case LESS_THAN:
|
||||||
|
return path.lt(numValue);
|
||||||
|
case GREATER_EQUAL:
|
||||||
|
return path.goe(numValue);
|
||||||
|
case LESS_EQUAL:
|
||||||
|
return path.loe(numValue);
|
||||||
|
case BETWEEN:
|
||||||
|
if (value instanceof Range) {
|
||||||
|
Range<?> range = (Range<?>) value;
|
||||||
|
N from = (N) parseNumber(range.getFrom(), path.getType());
|
||||||
|
N to = (N) parseNumber(range.getTo(), path.getType());
|
||||||
|
if (from != null && to != null) {
|
||||||
|
return path.between(from, to);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private <T extends Comparable<?>> Predicate createDateCondition(DateTimePath<T> path, Object value, QueryType queryType) {
|
||||||
|
if (value instanceof DateRange range) {
|
||||||
|
LocalDateTime from = range.getFrom();
|
||||||
|
LocalDateTime to = range.getTo();
|
||||||
|
|
||||||
|
switch (queryType) {
|
||||||
|
case EQUAL:
|
||||||
|
return from != null ? path.eq((T) from) : null;
|
||||||
|
case GREATER_THAN:
|
||||||
|
return from != null ? path.gt((T) from) : null;
|
||||||
|
case LESS_THAN:
|
||||||
|
return to != null ? path.lt((T) to) : null;
|
||||||
|
case BETWEEN:
|
||||||
|
if (from != null && to != null) {
|
||||||
|
return path.between((T) from, (T) to);
|
||||||
|
} else if (from != null) {
|
||||||
|
return path.goe((T) from);
|
||||||
|
} else if (to != null) {
|
||||||
|
return path.loe((T) to);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Number parseNumber(Object value, Class<?> targetType) {
|
||||||
|
if (value == null) return null;
|
||||||
|
try {
|
||||||
|
String strValue = String.valueOf(value);
|
||||||
|
if (targetType == Long.class) {
|
||||||
|
return Long.valueOf(strValue);
|
||||||
|
} else if (targetType == Integer.class) {
|
||||||
|
return Integer.valueOf(strValue);
|
||||||
|
} else if (targetType == Double.class) {
|
||||||
|
return Double.valueOf(strValue);
|
||||||
|
} else if (targetType == Float.class) {
|
||||||
|
return Float.valueOf(strValue);
|
||||||
|
} else if (targetType == BigDecimal.class) {
|
||||||
|
return new BigDecimal(strValue);
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
// 忽略转换错误
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected PageRequest createPageRequest(BaseQuery query) {
|
||||||
|
if (query == null) {
|
||||||
|
return PageRequest.of(0, 10, Sort.by(Sort.Direction.DESC, "createTime"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Sort sort = StringUtils.hasText(query.getSortField()) ?
|
||||||
|
Sort.by(Sort.Direction.fromString(query.getSortOrder()), query.getSortField()) :
|
||||||
|
Sort.by(Sort.Direction.DESC, "createTime");
|
||||||
|
|
||||||
|
return PageRequest.of(
|
||||||
|
query.getPageNum() - 1,
|
||||||
|
query.getPageSize(),
|
||||||
|
sort
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public T create(T entity) {
|
public T create(T entity) {
|
||||||
return repository.save(entity);
|
return repository.save(entity);
|
||||||
@ -66,259 +284,4 @@ public abstract class BaseServiceImpl<T extends Entity<ID>, ID extends Serializa
|
|||||||
public List<T> findAll() {
|
public List<T> findAll() {
|
||||||
return repository.findAll();
|
return repository.findAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 自动获取Q类实例
|
|
||||||
// 自动获取Q类实例
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private EntityPath<T> getEntityPath() {
|
|
||||||
Class<?>[] genericTypes = GenericTypeResolver.resolveTypeArguments(getClass(), BaseServiceImpl.class);
|
|
||||||
if (genericTypes == null || genericTypes.length < 2) {
|
|
||||||
throw new IllegalStateException("Could not resolve generic type arguments for " + getClass().getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
Class<T> entityClass = (Class<T>) genericTypes[0];
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 获取Q类
|
|
||||||
String qClassName = entityClass.getPackageName() + ".Q" + entityClass.getSimpleName();
|
|
||||||
Class<?> qClass = Class.forName(qClassName);
|
|
||||||
|
|
||||||
// 获取静态实例字段
|
|
||||||
Field instanceField = qClass.getDeclaredField(entityClass.getSimpleName().toLowerCase());
|
|
||||||
return (EntityPath<T>) instanceField.get(null);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("Failed to get Q class for " + entityClass.getName(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Page<T> page(BaseQuery query) {
|
|
||||||
return repository.findAll(
|
|
||||||
createPredicate(query),
|
|
||||||
createPageRequest(query)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Predicate createPredicate(BaseQuery query) {
|
|
||||||
BooleanBuilder builder = new BooleanBuilder();
|
|
||||||
processFields(query, builder, entityPath, "");
|
|
||||||
return builder.getValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processFields(Object query, BooleanBuilder builder, EntityPath<?> currentPath, String prefix) {
|
|
||||||
Field[] fields = query.getClass().getDeclaredFields();
|
|
||||||
for (Field field : fields) {
|
|
||||||
field.setAccessible(true);
|
|
||||||
try {
|
|
||||||
Object value = field.get(query);
|
|
||||||
if (value != null) {
|
|
||||||
QueryField queryField = field.getAnnotation(QueryField.class);
|
|
||||||
if (queryField != null) {
|
|
||||||
String fieldName = StringUtils.hasText(queryField.field()) ?
|
|
||||||
queryField.field() : field.getName();
|
|
||||||
String fullPath = StringUtils.hasText(prefix) ? prefix + "." + fieldName : fieldName;
|
|
||||||
|
|
||||||
Path<?> path = EntityPathResolver.getPath(currentPath, fieldName);
|
|
||||||
if (path != null) {
|
|
||||||
addPredicate(builder, path, value, queryField.type());
|
|
||||||
}
|
|
||||||
} else if (!isBaseType(field.getType()) && !isCollection(field.getType())) {
|
|
||||||
// 处理嵌套对象
|
|
||||||
String nestedPrefix = StringUtils.hasText(prefix) ?
|
|
||||||
prefix + "." + field.getName() : field.getName();
|
|
||||||
Path<?> nestedPath = EntityPathResolver.getPath(currentPath, field.getName());
|
|
||||||
if (nestedPath instanceof EntityPath) {
|
|
||||||
processFields(value, builder, (EntityPath<?>) nestedPath, nestedPrefix);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
// 忽略无法处理的字段
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addPredicate(BooleanBuilder builder, Path<?> path, Object value, QueryType queryType) {
|
|
||||||
if (path instanceof StringPath) {
|
|
||||||
addStringPredicate(builder, (StringPath) path, value, queryType);
|
|
||||||
} else if (path instanceof BooleanPath) {
|
|
||||||
addBooleanPredicate(builder, (BooleanPath) path, value);
|
|
||||||
} else if (path instanceof NumberPath) {
|
|
||||||
addNumberPredicate(builder, (NumberPath<?>) path, value, queryType);
|
|
||||||
} else if (path instanceof DateTimePath) {
|
|
||||||
addDatePredicate(builder, (DateTimePath<?>) path, value, queryType);
|
|
||||||
} else if (path instanceof EnumPath) {
|
|
||||||
addEnumPredicate(builder, (EnumPath) path, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addStringPredicate(BooleanBuilder builder, StringPath path, Object value, QueryType queryType) {
|
|
||||||
switch (queryType) {
|
|
||||||
case EQUAL:
|
|
||||||
builder.and(path.eq(value.toString()));
|
|
||||||
break;
|
|
||||||
case LIKE:
|
|
||||||
builder.and(path.containsIgnoreCase(value.toString()));
|
|
||||||
break;
|
|
||||||
case START_WITH:
|
|
||||||
builder.and(path.startsWithIgnoreCase(value.toString()));
|
|
||||||
break;
|
|
||||||
case END_WITH:
|
|
||||||
builder.and(path.endsWithIgnoreCase(value.toString()));
|
|
||||||
break;
|
|
||||||
case IN:
|
|
||||||
if (value instanceof Collection<?>) {
|
|
||||||
Collection<?> collection = (Collection<?>) value;
|
|
||||||
List<String> strings = collection.stream()
|
|
||||||
.map(Object::toString)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
builder.and(path.in(strings));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private <N extends Number & Comparable<?>> void addNumberPredicate(BooleanBuilder builder,
|
|
||||||
NumberPath<N> path, Object value, QueryType queryType) {
|
|
||||||
N numValue = (N) parseNumber(value, path.getType());
|
|
||||||
if (numValue != null) {
|
|
||||||
switch (queryType) {
|
|
||||||
case EQUAL:
|
|
||||||
builder.and(path.eq(numValue));
|
|
||||||
break;
|
|
||||||
case GREATER_THAN:
|
|
||||||
builder.and(path.gt(numValue));
|
|
||||||
break;
|
|
||||||
case LESS_THAN:
|
|
||||||
builder.and(path.lt(numValue));
|
|
||||||
break;
|
|
||||||
case GREATER_EQUAL:
|
|
||||||
builder.and(path.goe(numValue));
|
|
||||||
break;
|
|
||||||
case LESS_EQUAL:
|
|
||||||
builder.and(path.loe(numValue));
|
|
||||||
break;
|
|
||||||
case BETWEEN:
|
|
||||||
if (value instanceof Range) {
|
|
||||||
Range<?> range = (Range<?>) value;
|
|
||||||
N from = (N) parseNumber(range.getFrom(), path.getType());
|
|
||||||
N to = (N) parseNumber(range.getTo(), path.getType());
|
|
||||||
if (from != null && to != null) {
|
|
||||||
builder.and(path.between(from, to));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private <T extends Comparable<?>> void addDatePredicate(BooleanBuilder builder, DateTimePath<T> path, Object value, QueryType queryType) {
|
|
||||||
if (value instanceof DateRange range) {
|
|
||||||
switch (queryType) {
|
|
||||||
case EQUAL:
|
|
||||||
if (range.getFrom() != null) {
|
|
||||||
builder.and(path.eq((T) range.getFrom()));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GREATER_THAN:
|
|
||||||
if (range.getFrom() != null) {
|
|
||||||
builder.and(path.gt((T) range.getFrom()));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case LESS_THAN:
|
|
||||||
if (range.getTo() != null) {
|
|
||||||
builder.and(path.lt((T) range.getTo()));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case BETWEEN:
|
|
||||||
if (range.isValid()) {
|
|
||||||
builder.and(path.between((T) range.getFrom(), (T) range.getTo()));
|
|
||||||
} else if (range.hasFrom()) {
|
|
||||||
builder.and(path.goe((T) range.getFrom()));
|
|
||||||
} else if (range.hasTo()) {
|
|
||||||
builder.and(path.loe((T) range.getTo()));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addBooleanPredicate(BooleanBuilder builder, BooleanPath path, Object value) {
|
|
||||||
if (value instanceof Boolean) {
|
|
||||||
builder.and(path.eq((Boolean) value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addEnumPredicate(BooleanBuilder builder, EnumPath path, Object value) {
|
|
||||||
if (value instanceof Enum) {
|
|
||||||
builder.and(path.eq(value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isBaseType(Class<?> clazz) {
|
|
||||||
return clazz.isPrimitive()
|
|
||||||
|| Number.class.isAssignableFrom(clazz)
|
|
||||||
|| String.class.equals(clazz)
|
|
||||||
|| Boolean.class.equals(clazz)
|
|
||||||
|| Date.class.isAssignableFrom(clazz)
|
|
||||||
|| clazz.isEnum();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isCollection(Class<?> clazz) {
|
|
||||||
return Collection.class.isAssignableFrom(clazz);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected PageRequest createPageRequest(BaseQuery query) {
|
|
||||||
if (query == null) {
|
|
||||||
// 如果query为null,使用默认值
|
|
||||||
return PageRequest.of(0, 10, Sort.by(Sort.Direction.DESC, "createTime"));
|
|
||||||
}
|
|
||||||
|
|
||||||
Sort sort = StringUtils.hasText(query.getSortField()) ?
|
|
||||||
Sort.by(Sort.Direction.fromString(query.getSortOrder()), query.getSortField()) :
|
|
||||||
Sort.by(Sort.Direction.DESC, "createTime");
|
|
||||||
|
|
||||||
return PageRequest.of(
|
|
||||||
query.getPageNum() - 1,
|
|
||||||
query.getPageSize(),
|
|
||||||
sort
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Number parseNumber(Object value, Class<?> targetType) {
|
|
||||||
if (value == null) return null;
|
|
||||||
try {
|
|
||||||
String strValue = String.valueOf(value);
|
|
||||||
if (targetType == Long.class) {
|
|
||||||
return Long.valueOf(strValue);
|
|
||||||
} else if (targetType == Integer.class) {
|
|
||||||
return Integer.valueOf(strValue);
|
|
||||||
} else if (targetType == Double.class) {
|
|
||||||
return Double.valueOf(strValue);
|
|
||||||
} else if (targetType == Float.class) {
|
|
||||||
return Float.valueOf(strValue);
|
|
||||||
} else if (targetType == BigDecimal.class) {
|
|
||||||
return new BigDecimal(strValue);
|
|
||||||
}
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
// 忽略转换错误
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// EntityPathResolver 用于解析实体路径
|
|
||||||
private static class EntityPathResolver {
|
|
||||||
public static Path<?> getPath(EntityPath<?> entityPath, String fieldName) {
|
|
||||||
try {
|
|
||||||
Field field = entityPath.getClass().getDeclaredField(fieldName);
|
|
||||||
field.setAccessible(true);
|
|
||||||
return (Path<?>) field.get(entityPath);
|
|
||||||
} catch (Exception e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
package com.qqchen.deploy.backend.common.utils;
|
||||||
|
|
||||||
|
import com.querydsl.core.types.EntityPath;
|
||||||
|
import com.querydsl.core.types.Path;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
public class EntityPathResolver {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(EntityPathResolver.class);
|
||||||
|
|
||||||
|
public static Path<?> getPath(EntityPath<?> entityPath, String fieldName) {
|
||||||
|
try {
|
||||||
|
// 首先尝试直接获取字段
|
||||||
|
try {
|
||||||
|
Field field = entityPath.getClass().getDeclaredField(fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return (Path<?>) field.get(entityPath);
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
// 如果找不到,尝试从_super中获取
|
||||||
|
Field superField = entityPath.getClass().getDeclaredField("_super");
|
||||||
|
superField.setAccessible(true);
|
||||||
|
Object superInstance = superField.get(entityPath);
|
||||||
|
|
||||||
|
if (superInstance != null) {
|
||||||
|
try {
|
||||||
|
Field field = superInstance.getClass().getDeclaredField(fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return (Path<?>) field.get(superInstance);
|
||||||
|
} catch (NoSuchFieldException ex) {
|
||||||
|
// 打印调试信息
|
||||||
|
log.debug("Field {} not found in _super, available fields:", fieldName);
|
||||||
|
for (Field field : superInstance.getClass().getDeclaredFields()) {
|
||||||
|
log.debug("Field name in _super: {}", field.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error resolving path for field {}: {}", fieldName, e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
package com.qqchen.deploy.backend.entity;
|
package com.qqchen.deploy.backend.entity;
|
||||||
|
|
||||||
|
import com.qqchen.deploy.backend.common.annotation.SoftDelete;
|
||||||
import com.qqchen.deploy.backend.common.domain.Entity;
|
import com.qqchen.deploy.backend.common.domain.Entity;
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
@ -10,6 +11,7 @@ import lombok.EqualsAndHashCode;
|
|||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
@jakarta.persistence.Entity
|
@jakarta.persistence.Entity
|
||||||
@Table(name = "t_user")
|
@Table(name = "t_user")
|
||||||
|
@SoftDelete
|
||||||
public class User extends Entity<Long> {
|
public class User extends Entity<Long> {
|
||||||
|
|
||||||
@Column(unique = true, nullable = false)
|
@Column(unique = true, nullable = false)
|
||||||
|
|||||||
@ -25,6 +25,7 @@ logging:
|
|||||||
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping: TRACE # \u6253\u5370\u6240\u6709\u6CE8\u518C\u7684\u63A5\u53E3\u8DEF\u5F84
|
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping: TRACE # \u6253\u5370\u6240\u6709\u6CE8\u518C\u7684\u63A5\u53E3\u8DEF\u5F84
|
||||||
org.hibernate.type.descriptor.sql.BasicBinder: TRACE # \u663E\u793ASQL\u53C2\u6570
|
org.hibernate.type.descriptor.sql.BasicBinder: TRACE # \u663E\u793ASQL\u53C2\u6570
|
||||||
org.hibernate.type.descriptor.sql: TRACE
|
org.hibernate.type.descriptor.sql: TRACE
|
||||||
|
com.qqchen.deploy.backend.common.utils.EntityPathResolver: DEBUG
|
||||||
jwt:
|
jwt:
|
||||||
secret: 'thisIsAVeryVerySecretKeyForJwtTokenGenerationAndValidation123456789'
|
secret: 'thisIsAVeryVerySecretKeyForJwtTokenGenerationAndValidation123456789'
|
||||||
expiration: 86400
|
expiration: 86400
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user