273 lines
9.8 KiB
Java
273 lines
9.8 KiB
Java
package la.moony.friends.finders.impl;
|
|
|
|
import jakarta.annotation.Nonnull;
|
|
import la.moony.friends.extension.FriendPost;
|
|
import la.moony.friends.extension.Link;
|
|
import la.moony.friends.extension.LinkGroup;
|
|
import la.moony.friends.extension.RssFeedSyncLog;
|
|
import la.moony.friends.finders.FriendFinder;
|
|
import la.moony.friends.util.SortUtils;
|
|
import la.moony.friends.vo.FriendPostVo;
|
|
import la.moony.friends.vo.LinkGroupVo;
|
|
import la.moony.friends.vo.LinkVo;
|
|
import lombok.Data;
|
|
import lombok.RequiredArgsConstructor;
|
|
import org.apache.commons.lang3.ObjectUtils;
|
|
import org.apache.commons.lang3.StringUtils;
|
|
import org.springframework.data.domain.Sort;
|
|
import org.springframework.util.Assert;
|
|
import reactor.core.publisher.Flux;
|
|
import reactor.core.publisher.Mono;
|
|
import org.springframework.lang.NonNull;
|
|
import run.halo.app.extension.*;
|
|
import run.halo.app.extension.router.selector.FieldSelector;
|
|
import run.halo.app.infra.utils.JsonUtils;
|
|
import run.halo.app.theme.finders.Finder;
|
|
import java.util.Comparator;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Optional;
|
|
import java.util.stream.Collectors;
|
|
|
|
import static org.springframework.data.domain.Sort.Order.asc;
|
|
import static run.halo.app.extension.index.query.QueryFactory.*;
|
|
|
|
|
|
@Finder("friendFinder")
|
|
@RequiredArgsConstructor
|
|
public class FriendFinderImpl implements FriendFinder {
|
|
|
|
static final String UNGROUPED_NAME = "ungrouped";
|
|
|
|
private final ReactiveExtensionClient client;
|
|
|
|
|
|
|
|
@Override
|
|
public Flux<FriendPostVo> listAll() {
|
|
var listOptions = new ListOptions();
|
|
var query = all();
|
|
listOptions.setFieldSelector(FieldSelector.of(query));
|
|
return client.listAll(FriendPost.class, listOptions, defaultSort())
|
|
.flatMap(this::getFriendPostVo);
|
|
}
|
|
|
|
@Override
|
|
public Mono<ListResult<FriendPostVo>> list(Integer page, Integer size) {
|
|
return pageFriendPost(new ListOptions(), getPageRequest(page, size));
|
|
}
|
|
|
|
private PageRequestImpl getPageRequest(Integer page, Integer size) {
|
|
return PageRequestImpl.of(pageNullSafe(page), sizeNullSafe(size), defaultSort());
|
|
}
|
|
|
|
@Override
|
|
public Mono<ListResult<FriendPostVo>> list(Map<String, Object> params) {
|
|
var query = Optional.ofNullable(params)
|
|
.map(map -> JsonUtils.mapToObject(map, FriendPostQuery.class))
|
|
.orElseGet(FriendPostQuery::new);
|
|
return pageFriendPost(query.toListOptions(), query.toPageRequest());
|
|
}
|
|
|
|
|
|
@Override
|
|
public Mono<FriendPostVo> getByName(String friendPostName) {
|
|
return client.fetch(FriendPost.class, friendPostName)
|
|
.map(FriendPostVo::from);
|
|
}
|
|
|
|
@Override
|
|
public Flux<LinkGroupVo> linkGroupBy() {
|
|
return client.listAll(LinkGroup.class, new ListOptions(), defaultGroupSort())
|
|
.map(LinkGroupVo::from)
|
|
.concatMap(group -> linkListBy(group.getMetadata().getName())
|
|
.collectList()
|
|
.map(group::withLinks)
|
|
.defaultIfEmpty(group)
|
|
)
|
|
.mergeWith(Mono.defer(() -> linkListBy(UNGROUPED_NAME)
|
|
.collectList()
|
|
// do not return ungrouped group if no links
|
|
.filter(links -> !links.isEmpty())
|
|
.flatMap(links -> ungrouped()
|
|
.map(LinkGroupVo::from)
|
|
.map(group -> group.withLinks(links))
|
|
)
|
|
));
|
|
}
|
|
|
|
@Override
|
|
public Flux<String> listAllAuthors() {
|
|
var listOptions = new ListOptions();
|
|
// 只查有数据的,排除已删除的
|
|
var query = isNull("metadata.deletionTimestamp");
|
|
listOptions.setFieldSelector(FieldSelector.of(query));
|
|
|
|
return client.listAll(FriendPost.class, listOptions, defaultSort())
|
|
// 提取作者名
|
|
.map(friendPost -> friendPost.getSpec().getAuthor())
|
|
// 过滤掉空值
|
|
.filter(StringUtils::isNotBlank)
|
|
// 去重
|
|
.distinct();
|
|
}
|
|
|
|
@Override
|
|
public Flux<LinkVo> listAuthorLinks() {
|
|
var listOptions = new ListOptions();
|
|
listOptions.setFieldSelector(FieldSelector.of(isNull("metadata.deletionTimestamp")));
|
|
return client.listAll(Link.class, listOptions, defaultLinkSort())
|
|
.filter(link -> link.getSpec() != null
|
|
&& StringUtils.isNotBlank(link.getSpec().getDisplayName()))
|
|
.map(LinkVo::from);
|
|
}
|
|
|
|
private Mono<ListResult<FriendPostVo>> pageFriendPost(ListOptions queryOptions, PageRequest page){
|
|
var listOptions = new ListOptions();
|
|
var query = all();
|
|
listOptions.setFieldSelector(FieldSelector.of(query));
|
|
var fieldSelector = queryOptions.getFieldSelector();
|
|
if (fieldSelector != null) {
|
|
listOptions.setFieldSelector(listOptions.getFieldSelector()
|
|
.andQuery(fieldSelector.query()));
|
|
}
|
|
|
|
return client.listBy(FriendPost.class, listOptions, page)
|
|
.flatMap(list -> Flux.fromStream(list.get())
|
|
.concatMap(this::getFriendPostVo)
|
|
.collectList()
|
|
.map(friendPostVos -> new ListResult<>(list.getPage(), list.getSize(),
|
|
list.getTotal(), friendPostVos)
|
|
)
|
|
)
|
|
.defaultIfEmpty(ListResult.emptyResult());
|
|
|
|
}
|
|
|
|
public Flux<LinkVo> linkListBy(String groupName) {
|
|
var listOptions = new ListOptions();
|
|
var query = isNull("metadata.deletionTimestamp");
|
|
if (UNGROUPED_NAME.equals(groupName)) {
|
|
query = and(query, isNull("spec.groupName"));
|
|
} else {
|
|
query = and(query, equal("spec.groupName", groupName));
|
|
}
|
|
listOptions.setFieldSelector(FieldSelector.of(query));
|
|
return client.listAll(Link.class, listOptions, defaultLinkSort())
|
|
.flatMapSequential(this::convertToListedVo)
|
|
.collectList()
|
|
.map(links -> {
|
|
// 排序逻辑
|
|
return links.stream()
|
|
.sorted(Comparator.comparing(link -> {
|
|
List<FriendPostVo> friendPosts = link.getFriendPosts();
|
|
if (friendPosts != null && !friendPosts.isEmpty()) {
|
|
return friendPosts.get(0).getSpec().getPubDate();
|
|
}
|
|
return null;
|
|
}, Comparator.nullsLast(Comparator.reverseOrder())
|
|
)).collect(Collectors.toList());
|
|
})
|
|
.flatMapMany(Flux::fromIterable);
|
|
}
|
|
|
|
|
|
public Mono<LinkVo> convertToListedVo(@NonNull Link link) {
|
|
Assert.notNull(link, "Link must not be null");
|
|
LinkVo linkVo = LinkVo.from(link);
|
|
linkVo.setFriendPosts(List.of());
|
|
return Mono.just(linkVo)
|
|
.flatMap(lp -> friendPostListBy(lp.getMetadata().getName())
|
|
.doOnNext(lp::setFriendPosts)
|
|
.thenReturn(lp)
|
|
).flatMap(p -> {
|
|
String linkName = p.getMetadata().getName();
|
|
return client.fetch(RssFeedSyncLog.class,"sync-log-" + linkName)
|
|
.doOnNext(p::setRssFeedSyncLog)
|
|
.thenReturn(p);
|
|
})
|
|
.defaultIfEmpty(linkVo);
|
|
}
|
|
|
|
public Mono<List<FriendPostVo>> friendPostListBy(String linkName) {
|
|
var listOptions = new ListOptions();
|
|
var query = isNull("metadata.deletionTimestamp");
|
|
query = and(query, equal("spec.linkName", linkName));
|
|
listOptions.setFieldSelector(FieldSelector.of(query));
|
|
var pageRequest = PageRequestImpl.of(pageNullSafe(1), sizeNullSafe(2), defaultSort());
|
|
return client.listBy(FriendPost.class, listOptions, pageRequest)
|
|
.flatMap(friendPosts -> Flux.fromStream(friendPosts.get())
|
|
.map(FriendPostVo::from).collectList());
|
|
}
|
|
|
|
Mono<LinkGroup> ungrouped() {
|
|
LinkGroup linkGroup = new LinkGroup();
|
|
linkGroup.setMetadata(new Metadata());
|
|
linkGroup.getMetadata().setName(UNGROUPED_NAME);
|
|
linkGroup.setSpec(new LinkGroup.LinkGroupSpec());
|
|
linkGroup.getSpec().setDisplayName("");
|
|
linkGroup.getSpec().setPriority(0);
|
|
return Mono.just(linkGroup);
|
|
}
|
|
|
|
static Sort defaultGroupSort() {
|
|
return Sort.by(asc("spec.priority"),
|
|
asc("metadata.creationTimestamp"),
|
|
asc("metadata.name")
|
|
);
|
|
}
|
|
|
|
static Sort defaultLinkSort() {
|
|
return Sort.by(asc("spec.priority"),
|
|
asc("metadata.creationTimestamp"),
|
|
asc("metadata.name")
|
|
);
|
|
}
|
|
|
|
static Sort defaultSort() {
|
|
return Sort.by("spec.pubDate").descending();
|
|
}
|
|
|
|
private Mono<FriendPostVo> getFriendPostVo(@Nonnull FriendPost friendPost) {
|
|
FriendPostVo friendPostVo = FriendPostVo.from(friendPost);
|
|
return Mono.just(friendPostVo);
|
|
}
|
|
|
|
static int pageNullSafe(Integer page) {
|
|
return ObjectUtils.defaultIfNull(page, 1);
|
|
}
|
|
|
|
static int sizeNullSafe(Integer size) {
|
|
return ObjectUtils.defaultIfNull(size, 10);
|
|
}
|
|
|
|
@Data
|
|
public static class FriendPostQuery {
|
|
private Integer page;
|
|
private Integer size;
|
|
private String linkName;
|
|
// 作者查询
|
|
private String author;
|
|
private List<String> sort;
|
|
|
|
public ListOptions toListOptions() {
|
|
var builder = ListOptions.builder();
|
|
if (StringUtils.isNotBlank(linkName)) {
|
|
builder.andQuery(equal("spec.linkName", linkName));
|
|
}
|
|
// 作者查询
|
|
if (StringUtils.isNotBlank(author)) {
|
|
builder.andQuery(equal("spec.author", author));
|
|
}
|
|
return builder.build();
|
|
}
|
|
|
|
public PageRequest toPageRequest() {
|
|
return PageRequestImpl.of(pageNullSafe(getPage()),
|
|
sizeNullSafe(getSize()), SortUtils.resolve(sort).and(defaultSort()));
|
|
}
|
|
}
|
|
|
|
|
|
}
|