com.kasinf.framework.rest.support.SearchRequest Maven / Gradle / Ivy
The newest version!
package com.kasinf.framework.rest.support;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import com.kasinf.framework.rest.config.SearchableConfiguration;
import com.kasinf.framework.rest.exception.InvalidSearchPropertyException;
import com.kasinf.framework.rest.exception.InvalidSearchValueException;
import com.kasinf.framework.rest.exception.SearchException;
import com.kasinf.framework.rest.support.filter.*;
import com.kasinf.framework.rest.util.SearchableConvertUtils;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.util.Assert;
import java.util.*;
/**
* @author lkhsh
* 查询接口实现,包括条件、分页、排序
*/
@Data
@NoArgsConstructor
@ToString(of = {"searchFilterMap", "page", "sort"})
public final class SearchRequest implements Searchable {
private final Map searchFilterMap = new HashMap<>();
/**
* 使用这个的目的是保证拼sql的顺序是按照添加时的顺序
*/
private final List searchFilters = new ArrayList<>();
/**
* 分页参数
*/
private Pageable page;
/**
* 排序参数
*/
private Sort sort;
/**
* 是否已转换
*/
private boolean converted;
/**
* 搜索参数
*/
private SearchableConfiguration config;
/**
* 投影对象
*/
private Class> projection;
@Override
public boolean hasProjection() {
return projection != null && projection != void.class;
}
@Override
public Searchable setProjection(Class projection) {
this.projection = projection;
return this;
}
public SearchRequest(final Map searchParams) {
this(searchParams, null, null);
}
public SearchRequest(final Map searchParams, final Pageable page) {
this(searchParams, page, null);
}
public SearchRequest(final Map searchParams, final Sort sort) {
this(searchParams, null, sort);
}
/**
* 根据查询参数拼Search
* 查询参数格式:property_op=value 或 customerProperty=value
* customerProperty查找规则是:
* 1、先查找domain的属性,
* 2、如果找不到查找domain上的SearchPropertyMappings映射规则
* 属性、操作符之间用_分割,op可省略/或custom,省略后值默认为custom,即程序中自定义
* 如果op=custom,property也可以自定义(即可以与domain的不一样)
*
* @param searchParams 查询参数组
* @param page 分页
* @param sort 排序
*/
public SearchRequest(final Map searchParams, final Pageable page, final Sort sort) throws SearchException {
toSearchFilters(searchParams);
merge(sort, page);
}
/**
* 组装查询条件
*
* @param searchParams 查询条件
*/
private void toSearchFilters(final Map searchParams) throws SearchException {
if (searchParams == null || searchParams.size() == 0) {
return;
}
for (Map.Entry entry : searchParams.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
addFilter(SearchFilterHelper.newCondition(key, value));
}
}
@Override
public void changeDataCondition() {
changeDataCondition(this.searchFilters);
}
private void changeDataCondition(List searchFilters) {
for (int i = 0; i < searchFilters.size(); i++) {
SearchFilter searchFilter = searchFilters.get(i);
if (searchFilter instanceof Condition) {
Condition condition = (Condition) searchFilter;
if (condition.isDateFilter()) {
Date today = DateUtil.beginOfDay(new Date()).toJdkDate();
Date begin = null;
Date end = null;
switch (condition.getOperator()) {
case yesterday:
// 操作符为昨天,取昨天0点-昨天23:59:59的区间
begin = DateUtil.offsetDay(today, -1).toJdkDate();
end = DateUtil.offsetSecond(today, -1).toJdkDate();
break;
case thisWeek:
//操作符为本周,获取本周一0点至上周日23:59:59
begin = DateUtil.beginOfWeek(today).toJdkDate();
end = DateUtil.endOfWeek(today).toJdkDate();
break;
case lastWeek:
//操作符为上周,获取上周一0点至上周日23:59:59
Date monday = DateUtil.beginOfWeek(today);
begin = DateUtil.offsetWeek(monday, -1).toJdkDate();
end = DateUtil.offsetSecond(monday, -1).toJdkDate();
break;
case thisMonth:
// 操作符为本月,获取本月1号0点至本月最后一天23:59:59
begin = DateUtil.beginOfMonth(today).toJdkDate();
end = DateUtil.endOfMonth(today).toJdkDate();
break;
case thisYear:
begin = DateUtil.beginOfYear(today).toJdkDate();
end = DateUtil.endOfYear(today).toJdkDate();
break;
case specific:
// 指定日期,生成当天0点至当天23:59:59的区间
Object value = condition.getValue();
if (value instanceof String) {
begin = DateUtil.parse((String) condition.getValue()).toJdkDate();
} else if (value instanceof Date) {
begin = DateUtil.beginOfDay((Date) value).toJdkDate();
}
Assert.notNull(begin, "指定的日期必须为 String 或 Date 类型");
end = DateUtil.endOfDay(begin).toJdkDate();
break;
}
condition.setOperator(SearchOperator.ge);
condition.setValue(begin);
condition.createKey();
// 为当前条件拼接一个and条件
SearchFilter newCondition = SearchFilterHelper.newCondition(condition.getSearchProperty(), SearchOperator.le, end);
searchFilter = SearchFilterHelper.and(condition, newCondition);
searchFilters.set(i, searchFilter);
}
return;
}
if (searchFilter instanceof OrCondition) {
changeDataCondition(((OrCondition) searchFilter).getOrFilters());
return;
}
if (searchFilter instanceof AndCondition) {
changeDataCondition(((AndCondition) searchFilter).getAndFilters());
}
}
}
/**
* 添加搜索参数
*
* @param key 如 name_like
* @param value 如果是in查询 多个值之间","分隔
* @return {@link Searchable}
* @throws SearchException SearchException
*/
@Override
public Searchable addParam(String key, Object value) throws SearchException {
addFilter(SearchFilterHelper.newCondition(key, value));
return this;
}
/**
* 添加一组查询参数
*
* @param searchParams 查询条件
* @return {@link Searchable}
* @throws SearchException SearchException
*/
@Override
public Searchable addParams(Map searchParams) throws SearchException {
toSearchFilters(searchParams);
return this;
}
/**
* 添加过滤条件
*
* @param searchProperty 查询的属性名
* @param operator 操作运算符
* @param value 值
* @return {@link Searchable}
* @throws SearchException SearchException
*/
@Override
public Searchable addFilter(String searchProperty, SearchOperator operator, Object value) throws SearchException {
SearchFilter searchFilter = SearchFilterHelper.newCondition(searchProperty, operator, value);
return addFilter(searchFilter);
}
/**
* 添加单目操作
*
* @param searchProperty 查询的属性名
* @param operator 单目运算操作
* @return {@link Searchable}
* @throws SearchException SearchException
*/
@Override
public Searchable addFilter(String searchProperty, SearchOperator operator) throws SearchException {
SearchFilter searchFilter = SearchFilterHelper.newCondition(searchProperty, operator, null);
return addFilter(searchFilter);
}
/**
* 添加过滤条件
*
* @param searchFilter 查询条件{@link SearchFilter}
* @return {@link Searchable}
*/
@Override
public Searchable addFilter(SearchFilter searchFilter) {
if (searchFilter == null) {
return this;
}
if (searchFilter instanceof Condition) {
Condition condition = (Condition) searchFilter;
String key = condition.getKey();
searchFilterMap.put(key, condition);
}
int index = searchFilters.indexOf(searchFilter);
if (index != -1) {
searchFilters.set(index, searchFilter);
} else {
searchFilters.add(searchFilter);
}
return this;
}
/**
* 添加多个过滤条件
*
* @param searchFilters 过滤条件{@link SearchFilter}
* @return {@link Searchable}
*/
@Override
public Searchable addFilters(Collection extends SearchFilter> searchFilters) {
if (CollectionUtil.isNotEmpty(searchFilters)) {
searchFilters.forEach(this::addFilter);
}
return this;
}
/**
* 添加多个or连接的过滤条件
*
* @param first 第一个
* @param others 其他
* @return {@link Searchable}
*/
@Override
public Searchable or(final SearchFilter first, final SearchFilter... others) {
addFilter(SearchFilterHelper.or(first, others));
return this;
}
/**
* 添加多个and连接的过滤条件
*
* @param first 第一个
* @param others 其他
* @return {@link Searchable}
*/
@Override
public Searchable and(final SearchFilter first, final SearchFilter... others) {
addFilter(SearchFilterHelper.and(first, others));
return this;
}
/**
* 移除指定key的过滤条件
*
* @param key 键
* @return {@link Searchable}
*/
@Override
public Searchable removeFilter(final String key) {
if (key == null) {
return this;
}
SearchFilter searchFilter = searchFilterMap.get(key);
if (searchFilter == null) {
searchFilter = searchFilterMap.remove(getCustomKey(key));
}
if (searchFilter == null) {
return this;
}
searchFilters.remove(searchFilter);
return this;
}
/**
* 获取自定义key
*
* @param key:
*/
private String getCustomKey(String key) {
return key + Condition.separator + SearchOperator.custom;
}
/**
* 移除指定属性 和 操作符的过滤条件
*
* @param searchProperty 查询属性
* @param operator 操作符
* @return {@link Searchable}
*/
@Override
public Searchable removeFilter(final String searchProperty, final SearchOperator operator) {
return removeFilter(searchProperty + Condition.separator + operator);
}
/**
* 将搜索的值转换为实体对应的值
*
* @param entityClass 实体类
* @param 实体对应的类型
* @return {@link Searchable}
* @throws InvalidSearchValueException 非法搜索值异常
* @throws InvalidSearchPropertyException 非法搜索属性异常
*/
@Override
public Searchable convert(Class entityClass) throws InvalidSearchValueException, InvalidSearchPropertyException {
SearchableConvertUtils.convertSearchValueToEntityValue(this, entityClass);
markConverted();
return this;
}
/**
* 获取所有搜索条件
*
* @return searchFilters
*/
@Override
public List getSearchFilters() {
return searchFilters;
}
/**
* 标识为已经转换过了 避免多次转换
*
* @return {@link Searchable}
*/
@Override
public Searchable markConverted() {
this.converted = true;
return this;
}
/**
* 设置分页
*
* @param page 分页信息
* @return 查询实现类 {@link SearchRequest}
*/
@Override
public Searchable setPage(final Pageable page) {
merge(sort, page);
return this;
}
/**
* 设置分页
*
* @param pageNumber 分页页码 索引从 0 开始
* @param pageSize 每页大小
* @return 查询实现类 {@link SearchRequest}
*/
@Override
public Searchable setPage(int pageNumber, int pageSize) {
merge(sort, PageRequest.of(pageNumber, pageSize));
return this;
}
/**
* 添加排序条件
*
* @param sort 排序
* @return 查询实现类 {@link SearchRequest}
*/
@Override
public Searchable addSort(Sort sort) {
merge(sort, page);
return this;
}
/**
* 添加排序条件
*
* @param direction 排序方向
* @param property 属性
* @return 查询实现类 {@link SearchRequest}
*/
@Override
public Searchable addSort(Sort.Direction direction, String property) {
merge(Sort.by(direction, property), page);
return this;
}
/**
* 是否有查询参数
*
* @return 标识符
*/
@Override
public boolean hasSearchFilter() {
return searchFilters.size() > 0;
}
/**
* 是否有排序
*
* @return 标识符
*/
@Override
public boolean hashSort() {
return this.sort != null && this.sort.iterator().hasNext();
}
/**
* 移除排序
*/
@Override
public void removeSort() {
this.sort = null;
if (this.page != null) {
this.page = PageRequest.of(page.getPageNumber(), page.getPageSize());
}
}
/**
* 是否有分页
*
* @return 标识符
*/
@Override
public boolean hasPageable() {
return this.page != null && this.page.getPageSize() > 0;
}
/**
* 移除分页
*/
@Override
public void removePageable() {
this.page = null;
}
/**
* 是否包含要搜索的键,如 name_like,包括and和or
*
* @param key 查询条件
* @return 查询实现类 {@link SearchRequest}
*/
@Override
public boolean containsSearchKey(String key) {
//先检查自身是否包含,否则检查其中的or 和 and
return searchFilterMap.containsKey(key) || searchFilterMap.containsKey(getCustomKey(key)) || containsSearchKey(searchFilters, key);
}
/**
* 是否存在查询条件
*
* @param searchFilters:
* @param key:
*/
private boolean containsSearchKey(List searchFilters, String key) {
boolean contains = false;
for (SearchFilter searchFilter : searchFilters) {
if (searchFilter instanceof OrCondition) {
OrCondition orCondition = (OrCondition) searchFilter;
contains = containsSearchKey(orCondition.getOrFilters(), key);
}
if (searchFilter instanceof AndCondition) {
AndCondition andCondition = (AndCondition) searchFilter;
contains = containsSearchKey(andCondition.getAndFilters(), key);
}
if (searchFilter instanceof Condition) {
Condition condition = (Condition) searchFilter;
contains = condition.getKey().equals(key) || condition.getSearchProperty().equals(key);
}
}
return contains;
}
@Override
public Object getValue(String key) {
SearchFilter searchFilter = searchFilterMap.get(key);
if (searchFilter == null) {
searchFilter = searchFilterMap.get(getCustomKey(key));
}
if (searchFilter == null) {
return null;
}
if (searchFilter instanceof Condition) {
Condition condition = (Condition) searchFilter;
return condition.getValue();
}
return null;
}
/**
* 合并
*
* @param sort 排序
* @param page 分页
*/
private void merge(Sort sort, Pageable page) {
if (sort == null) {
sort = this.sort;
}
if (page == null) {
page = this.page;
}
// 合并排序
if (sort == null) {
this.sort = page != null ? page.getSort() : null;
} else {
this.sort = (page != null ? sort.and(page.getSort()) : sort);
}
// 把排序合并到page中
if (page != null) {
this.page = PageRequest.of(page.getPageNumber(), page.getPageSize(), this.sort);
} else {
this.page = null;
}
}
}