Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3e9d0b4441 | |||
| 5a13068143 | |||
| b1f30b374f |
@@ -1,3 +1,23 @@
|
|||||||
|
# anian-plugin-friends
|
||||||
|
|
||||||
|
> 基于原作者v1.4.3代码修改
|
||||||
|
|
||||||
|
#### v1.4.3-3
|
||||||
|
1. title字段改为微博图片列表
|
||||||
|
2. 图片列表内文件将自动下载至/upload/image-host
|
||||||
|
|
||||||
|
#### v1.4.3-2
|
||||||
|
1. 新增URL参数author用于查询作者
|
||||||
|
2. 模板参数新增${authors}返回所有作者
|
||||||
|
|
||||||
|
#### v1.4.3-1
|
||||||
|
1. 大幅增加description字段的字符数上限
|
||||||
|
2. 去除description字段内的微博视频提示
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# plugin-friends
|
# plugin-friends
|
||||||
> 1.3.4版本重构朋友圈插件
|
> 1.3.4版本重构朋友圈插件
|
||||||
> 如果使用了之前版本,请清掉之前的所有数据,不然会有问题
|
> 如果使用了之前版本,请清掉之前的所有数据,不然会有问题
|
||||||
|
|||||||
+1
-1
@@ -16,7 +16,7 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation platform('run.halo.tools.platform:plugin:2.20.0-SNAPSHOT')
|
implementation platform('run.halo.tools.platform:plugin:2.20.11')
|
||||||
compileOnly 'run.halo.app:api'
|
compileOnly 'run.halo.app:api'
|
||||||
|
|
||||||
testImplementation 'run.halo.app:api'
|
testImplementation 'run.halo.app:api'
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
version=1.3.7
|
version=1.4.3-3
|
||||||
|
|||||||
Generated
+9982
File diff suppressed because it is too large
Load Diff
@@ -25,4 +25,6 @@ public interface FriendFinder {
|
|||||||
Mono<FriendPostVo> getByName(String friendPostName);
|
Mono<FriendPostVo> getByName(String friendPostName);
|
||||||
|
|
||||||
Flux<LinkGroupVo> linkGroupBy();
|
Flux<LinkGroupVo> linkGroupBy();
|
||||||
|
|
||||||
|
Flux<String> listAllAuthors();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,6 +96,22 @@ public class FriendFinderImpl implements FriendFinder {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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();
|
||||||
|
}
|
||||||
|
|
||||||
private Mono<ListResult<FriendPostVo>> pageFriendPost(ListOptions queryOptions, PageRequest page){
|
private Mono<ListResult<FriendPostVo>> pageFriendPost(ListOptions queryOptions, PageRequest page){
|
||||||
var listOptions = new ListOptions();
|
var listOptions = new ListOptions();
|
||||||
var query = all();
|
var query = all();
|
||||||
@@ -220,6 +236,8 @@ public class FriendFinderImpl implements FriendFinder {
|
|||||||
private Integer page;
|
private Integer page;
|
||||||
private Integer size;
|
private Integer size;
|
||||||
private String linkName;
|
private String linkName;
|
||||||
|
// 作者查询
|
||||||
|
private String author;
|
||||||
private List<String> sort;
|
private List<String> sort;
|
||||||
|
|
||||||
public ListOptions toListOptions() {
|
public ListOptions toListOptions() {
|
||||||
@@ -227,6 +245,10 @@ public class FriendFinderImpl implements FriendFinder {
|
|||||||
if (StringUtils.isNotBlank(linkName)) {
|
if (StringUtils.isNotBlank(linkName)) {
|
||||||
builder.andQuery(equal("spec.linkName", linkName));
|
builder.andQuery(equal("spec.linkName", linkName));
|
||||||
}
|
}
|
||||||
|
// 作者查询
|
||||||
|
if (StringUtils.isNotBlank(author)) {
|
||||||
|
builder.andQuery(equal("spec.author", author));
|
||||||
|
}
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,12 @@ public class FriendPostQuery extends SortableRequest {
|
|||||||
return queryParams.getFirst("linkName");
|
return queryParams.getFirst("linkName");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 作者查询
|
||||||
|
@Nullable
|
||||||
|
public String getAuthor() {
|
||||||
|
return StringUtils.defaultIfBlank(queryParams.getFirst("author"), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public ListOptions toListOptions() {
|
public ListOptions toListOptions() {
|
||||||
var builder = ListOptions.builder(super.toListOptions());
|
var builder = ListOptions.builder(super.toListOptions());
|
||||||
@@ -55,6 +61,11 @@ public class FriendPostQuery extends SortableRequest {
|
|||||||
.filter(StringUtils::isNotBlank)
|
.filter(StringUtils::isNotBlank)
|
||||||
.ifPresent(linkName -> builder.andQuery(equal("spec.linkName", linkName)));
|
.ifPresent(linkName -> builder.andQuery(equal("spec.linkName", linkName)));
|
||||||
|
|
||||||
|
// 作者查询
|
||||||
|
Optional.ofNullable(getAuthor())
|
||||||
|
.filter(StringUtils::isNotBlank)
|
||||||
|
.ifPresent(author -> builder.andQuery(equal("spec.author", author)));
|
||||||
|
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,6 +113,13 @@ public class FriendPostQuery extends SortableRequest {
|
|||||||
.name("keyword")
|
.name("keyword")
|
||||||
.description("CronFriendPost filtered by keyword.")
|
.description("CronFriendPost filtered by keyword.")
|
||||||
.implementation(String.class)
|
.implementation(String.class)
|
||||||
|
.required(false))
|
||||||
|
// 作者查询
|
||||||
|
.parameter(parameterBuilder()
|
||||||
|
.in(ParameterIn.QUERY)
|
||||||
|
.name("author")
|
||||||
|
.description("FriendPost filtered by exact author name.")
|
||||||
|
.implementation(String.class)
|
||||||
.required(false));
|
.required(false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -27,16 +27,30 @@ import run.halo.app.extension.ListResult;
|
|||||||
import run.halo.app.extension.Metadata;
|
import run.halo.app.extension.Metadata;
|
||||||
import run.halo.app.extension.PageRequestImpl;
|
import run.halo.app.extension.PageRequestImpl;
|
||||||
import run.halo.app.extension.Unstructured;
|
import run.halo.app.extension.Unstructured;
|
||||||
import run.halo.app.extension.controller.*;
|
import run.halo.app.extension.controller.Controller;
|
||||||
|
import run.halo.app.extension.controller.ControllerBuilder;
|
||||||
|
import run.halo.app.extension.controller.DefaultController;
|
||||||
|
import run.halo.app.extension.controller.DefaultQueue;
|
||||||
|
import run.halo.app.extension.controller.Reconciler;
|
||||||
|
import run.halo.app.extension.controller.RequestQueue;
|
||||||
import run.halo.app.extension.router.selector.FieldSelector;
|
import run.halo.app.extension.router.selector.FieldSelector;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import static org.springframework.data.domain.Sort.Order.asc;
|
import static org.springframework.data.domain.Sort.Order.asc;
|
||||||
import static run.halo.app.extension.MetadataUtil.nullSafeAnnotations;
|
import static run.halo.app.extension.MetadataUtil.nullSafeAnnotations;
|
||||||
@@ -45,10 +59,24 @@ import static run.halo.app.extension.index.query.QueryFactory.equal;
|
|||||||
import static run.halo.app.extension.index.query.QueryFactory.isNull;
|
import static run.halo.app.extension.index.query.QueryFactory.isNull;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class RssSyncReconciler implements Reconciler<RssSyncReconciler.Request>,SmartLifecycle {
|
public class RssSyncReconciler implements Reconciler<RssSyncReconciler.Request>, SmartLifecycle {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(RssSyncReconciler.class);
|
private static final Logger log = LoggerFactory.getLogger(RssSyncReconciler.class);
|
||||||
|
|
||||||
|
// 新增:正则匹配所有img标签的src属性
|
||||||
|
private static final Pattern IMG_SRC_PATTERN = Pattern.compile(
|
||||||
|
"<img[^>]*?src\\s*=\\s*(['\"])(.*?)\\1[^>]*?>",
|
||||||
|
Pattern.CASE_INSENSITIVE
|
||||||
|
);
|
||||||
|
|
||||||
|
private static final Pattern MW2000_IMAGE_FILE_PATTERN = Pattern.compile(
|
||||||
|
".*/mw2000/([^/?#]+)(?:[?#].*)?$",
|
||||||
|
Pattern.CASE_INSENSITIVE
|
||||||
|
);
|
||||||
|
|
||||||
|
private static final String IMAGE_CDN_BASE_URL = "https://cdn.img2ipfs.com/weibo/middle/";
|
||||||
|
private static final Path IMAGE_HOST_DIR = Path.of("/root/.halo2/attachments/upload/image-host/");
|
||||||
|
|
||||||
private volatile boolean running = false;
|
private volatile boolean running = false;
|
||||||
|
|
||||||
private final ExtensionClient client;
|
private final ExtensionClient client;
|
||||||
@@ -56,7 +84,6 @@ public class RssSyncReconciler implements Reconciler<RssSyncReconciler.Request>,
|
|||||||
private final Controller controller;
|
private final Controller controller;
|
||||||
private final ObjectMapper objectMapper = Unstructured.OBJECT_MAPPER;
|
private final ObjectMapper objectMapper = Unstructured.OBJECT_MAPPER;
|
||||||
|
|
||||||
|
|
||||||
public RssSyncReconciler(ExtensionClient client) {
|
public RssSyncReconciler(ExtensionClient client) {
|
||||||
this.client = client;
|
this.client = client;
|
||||||
queue = new DefaultQueue<>(Instant::now);
|
queue = new DefaultQueue<>(Instant::now);
|
||||||
@@ -82,12 +109,12 @@ public class RssSyncReconciler implements Reconciler<RssSyncReconciler.Request>,
|
|||||||
newRssFeedSyncLog.setLinkName(linkName);
|
newRssFeedSyncLog.setLinkName(linkName);
|
||||||
if (StringUtils.isNotEmpty(rssUrl) && !isContains) {
|
if (StringUtils.isNotEmpty(rssUrl) && !isContains) {
|
||||||
tryToSynchronizeFriendPost(newRssFeedSyncLog, link, sum);
|
tryToSynchronizeFriendPost(newRssFeedSyncLog, link, sum);
|
||||||
}else if (StringUtils.isEmpty(rssUrl)) {
|
} else if (StringUtils.isEmpty(rssUrl)) {
|
||||||
newRssFeedSyncLog.setSyncTime(Instant.now());
|
newRssFeedSyncLog.setSyncTime(Instant.now());
|
||||||
newRssFeedSyncLog.setState(RssFeedSyncLog.RssFeedSyncLogState.nolink);
|
newRssFeedSyncLog.setState(RssFeedSyncLog.RssFeedSyncLogState.nolink);
|
||||||
newRssFeedSyncLog.setFailureReason("No RSS link");
|
newRssFeedSyncLog.setFailureReason("No RSS link");
|
||||||
newRssFeedSyncLog.setFailureMessage("无 RSS 链接.");
|
newRssFeedSyncLog.setFailureMessage("无 RSS 链接.");
|
||||||
}else if (isContains) {
|
} else if (isContains) {
|
||||||
newRssFeedSyncLog.setSyncTime(Instant.now());
|
newRssFeedSyncLog.setSyncTime(Instant.now());
|
||||||
newRssFeedSyncLog.setState(RssFeedSyncLog.RssFeedSyncLogState.failed);
|
newRssFeedSyncLog.setState(RssFeedSyncLog.RssFeedSyncLogState.failed);
|
||||||
newRssFeedSyncLog.setFailureReason("admin disables sync");
|
newRssFeedSyncLog.setFailureReason("admin disables sync");
|
||||||
@@ -101,7 +128,7 @@ public class RssSyncReconciler implements Reconciler<RssSyncReconciler.Request>,
|
|||||||
rssFeedSyncLog.setFailureReason(newRssFeedSyncLog.getFailureReason());
|
rssFeedSyncLog.setFailureReason(newRssFeedSyncLog.getFailureReason());
|
||||||
rssFeedSyncLog.setFailureMessage(newRssFeedSyncLog.getFailureMessage());
|
rssFeedSyncLog.setFailureMessage(newRssFeedSyncLog.getFailureMessage());
|
||||||
client.update(rssFeedSyncLog);
|
client.update(rssFeedSyncLog);
|
||||||
}else {
|
} else {
|
||||||
client.create(newRssFeedSyncLog);
|
client.create(newRssFeedSyncLog);
|
||||||
}
|
}
|
||||||
return Result.doNotRetry();
|
return Result.doNotRetry();
|
||||||
@@ -121,10 +148,10 @@ public class RssSyncReconciler implements Reconciler<RssSyncReconciler.Request>,
|
|||||||
linkRss = rssUri;
|
linkRss = rssUri;
|
||||||
annotations.remove("rss_uri");
|
annotations.remove("rss_uri");
|
||||||
annotations.put("rss_url", rssUri);
|
annotations.put("rss_url", rssUri);
|
||||||
}else {
|
} else {
|
||||||
annotations.put("is_request","true");
|
annotations.put("is_request", "true");
|
||||||
if (StringUtils.isNotEmpty(linkRss)) {
|
if (StringUtils.isNotEmpty(linkRss)) {
|
||||||
annotations.put("rss_url",linkRss);
|
annotations.put("rss_url", linkRss);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateLink(link);
|
updateLink(link);
|
||||||
@@ -133,7 +160,6 @@ public class RssSyncReconciler implements Reconciler<RssSyncReconciler.Request>,
|
|||||||
return rssUrl;
|
return rssUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void updateLink(Link link) {
|
public void updateLink(Link link) {
|
||||||
Map extensionMap = objectMapper.convertValue(link, Map.class);
|
Map extensionMap = objectMapper.convertValue(link, Map.class);
|
||||||
var extension = new Unstructured(extensionMap);
|
var extension = new Unstructured(extensionMap);
|
||||||
@@ -158,12 +184,12 @@ public class RssSyncReconciler implements Reconciler<RssSyncReconciler.Request>,
|
|||||||
var friendPostListOptions = new ListOptions();
|
var friendPostListOptions = new ListOptions();
|
||||||
friendPostListOptions.setFieldSelector(FieldSelector.of(
|
friendPostListOptions.setFieldSelector(FieldSelector.of(
|
||||||
and(isNull("metadata.deletionTimestamp"),
|
and(isNull("metadata.deletionTimestamp"),
|
||||||
equal("spec.postLink",rssPost.getSpec().getPostLink())
|
equal("spec.postLink", rssPost.getSpec().getPostLink())
|
||||||
)
|
)
|
||||||
));
|
));
|
||||||
long total = client.listBy(FriendPost.class, friendPostListOptions,
|
long total = client.listBy(FriendPost.class, friendPostListOptions,
|
||||||
PageRequestImpl.ofSize(1)).getTotal();
|
PageRequestImpl.ofSize(1)).getTotal();
|
||||||
if (total==0) {
|
if (total == 0) {
|
||||||
FriendPost friendPost = new FriendPost();
|
FriendPost friendPost = new FriendPost();
|
||||||
// 设置元数据才能保存
|
// 设置元数据才能保存
|
||||||
friendPost.setMetadata(new Metadata());
|
friendPost.setMetadata(new Metadata());
|
||||||
@@ -175,21 +201,42 @@ public class RssSyncReconciler implements Reconciler<RssSyncReconciler.Request>,
|
|||||||
friendPostSpec.setAuthorUrl(link.getSpec().getUrl());
|
friendPostSpec.setAuthorUrl(link.getSpec().getUrl());
|
||||||
friendPostSpec.setLinkName(linkName);
|
friendPostSpec.setLinkName(linkName);
|
||||||
String description = friendPost.getSpec().getDescription();
|
String description = friendPost.getSpec().getDescription();
|
||||||
//解析html内容转换成文本
|
|
||||||
if (StringUtils.isNotEmpty(description)){
|
// 先提取图片文件名覆盖标题,再将 HTML 描述转换为纯文本
|
||||||
description = CommonUtils.parseAndTruncateHtml2Text(description, 200);
|
if (StringUtils.isNotEmpty(description)) {
|
||||||
|
List<String> imageFileNames = new ArrayList<>();
|
||||||
|
Matcher matcher = IMG_SRC_PATTERN.matcher(description);
|
||||||
|
while (matcher.find()) {
|
||||||
|
String imgSrc = matcher.group(2);
|
||||||
|
Matcher mw2000Matcher = MW2000_IMAGE_FILE_PATTERN.matcher(imgSrc);
|
||||||
|
if (mw2000Matcher.matches()) {
|
||||||
|
imageFileNames.add(mw2000Matcher.group(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!imageFileNames.isEmpty()) {
|
||||||
|
String imageTitle = "img:::" + String.join(",", imageFileNames);
|
||||||
|
friendPostSpec.setTitle(imageTitle);
|
||||||
|
// 通知图片处理服务
|
||||||
|
notifyImageService(imageTitle);
|
||||||
|
}
|
||||||
|
|
||||||
|
String pureText = CommonUtils.parseAndTruncateHtml2Text(description, 100000);
|
||||||
String regexp = "[ *|\\s*]*";
|
String regexp = "[ *|\\s*]*";
|
||||||
description = description.replaceFirst(regexp, "").trim();
|
pureText = pureText.replaceFirst(regexp, "").trim();
|
||||||
friendPostSpec.setDescription(description);
|
String weiboVideoRegex = "\\s[^\\s]*的微博视频 视频无法显示,请前往微博视频观看。";
|
||||||
|
pureText = pureText.replaceAll(weiboVideoRegex, "");
|
||||||
|
friendPostSpec.setDescription(pureText);
|
||||||
}
|
}
|
||||||
|
|
||||||
client.create(friendPost);
|
client.create(friendPost);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delFriendPost(linkName,sum);
|
delFriendPost(linkName, sum);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<FriendPost> fetchFriendPost(String rssAddress,int postsLimit, RssFeedSyncLog sync) {
|
public List<FriendPost> fetchFriendPost(String rssAddress, int postsLimit, RssFeedSyncLog sync) {
|
||||||
List<FriendPost> friendPostList = new ArrayList<>();
|
List<FriendPost> friendPostList = new ArrayList<>();
|
||||||
try {
|
try {
|
||||||
SyndFeed feed = new SyndFeedInput().build(new XmlReader(new URL(rssAddress)));
|
SyndFeed feed = new SyndFeedInput().build(new XmlReader(new URL(rssAddress)));
|
||||||
@@ -203,7 +250,7 @@ public class RssSyncReconciler implements Reconciler<RssSyncReconciler.Request>,
|
|||||||
SyndContent content = entry.getDescription();
|
SyndContent content = entry.getDescription();
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
description = content.getValue();
|
description = content.getValue();
|
||||||
}else {
|
} else {
|
||||||
List<SyndContent> contents = entry.getContents();
|
List<SyndContent> contents = entry.getContents();
|
||||||
if (!contents.isEmpty()) {
|
if (!contents.isEmpty()) {
|
||||||
description = contents.get(0).getValue();
|
description = contents.get(0).getValue();
|
||||||
@@ -233,10 +280,10 @@ public class RssSyncReconciler implements Reconciler<RssSyncReconciler.Request>,
|
|||||||
return friendPostList;
|
return friendPostList;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void delFriendPost(String linkName,Integer sum) {
|
public void delFriendPost(String linkName, Integer sum) {
|
||||||
var friendPostListOptions = new ListOptions();
|
var friendPostListOptions = new ListOptions();
|
||||||
friendPostListOptions.setFieldSelector(FieldSelector.of(
|
friendPostListOptions.setFieldSelector(FieldSelector.of(
|
||||||
equal("spec.linkName",linkName)
|
equal("spec.linkName", linkName)
|
||||||
));
|
));
|
||||||
long total = client.listBy(FriendPost.class, friendPostListOptions, PageRequestImpl.ofSize(1)).getTotal();
|
long total = client.listBy(FriendPost.class, friendPostListOptions, PageRequestImpl.ofSize(1)).getTotal();
|
||||||
if (total > sum) {
|
if (total > sum) {
|
||||||
@@ -251,6 +298,44 @@ public class RssSyncReconciler implements Reconciler<RssSyncReconciler.Request>,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通知图片处理服务有新的图片需要处理
|
||||||
|
* @param imageTitle 图片标题,格式为 "img:::file1,file2,..."
|
||||||
|
*/
|
||||||
|
private void notifyImageService(String imageTitle) {
|
||||||
|
if (StringUtils.isEmpty(imageTitle)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
new Thread(() -> downloadImagesByTitle(imageTitle)).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void downloadImagesByTitle(String imageTitle) {
|
||||||
|
if (!imageTitle.startsWith("img:::")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Files.createDirectories(IMAGE_HOST_DIR);
|
||||||
|
String[] fileNames = imageTitle.substring("img:::".length()).split(",");
|
||||||
|
for (String fileName : fileNames) {
|
||||||
|
String trimmedName = fileName.trim();
|
||||||
|
if (StringUtils.isEmpty(trimmedName)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String encodedFileName = URLEncoder.encode(trimmedName, StandardCharsets.UTF_8)
|
||||||
|
.replace("+", "%20");
|
||||||
|
String downloadUrl = IMAGE_CDN_BASE_URL + encodedFileName;
|
||||||
|
Path targetPath = IMAGE_HOST_DIR.resolve(trimmedName);
|
||||||
|
|
||||||
|
try (InputStream inputStream = new URL(downloadUrl).openStream()) {
|
||||||
|
Files.copy(inputStream, targetPath, StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("Failed to download images for title: {}", imageTitle, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int pageNullSafe(Integer page) {
|
static int pageNullSafe(Integer page) {
|
||||||
return ObjectUtils.defaultIfNull(page, 1);
|
return ObjectUtils.defaultIfNull(page, 1);
|
||||||
}
|
}
|
||||||
@@ -259,7 +344,6 @@ public class RssSyncReconciler implements Reconciler<RssSyncReconciler.Request>,
|
|||||||
return ObjectUtils.defaultIfNull(size, 10);
|
return ObjectUtils.defaultIfNull(size, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Controller setupWith(ControllerBuilder builder) {
|
public Controller setupWith(ControllerBuilder builder) {
|
||||||
return new DefaultController<>(
|
return new DefaultController<>(
|
||||||
@@ -291,10 +375,10 @@ public class RssSyncReconciler implements Reconciler<RssSyncReconciler.Request>,
|
|||||||
|
|
||||||
@EventListener(RssFeedSyncEvent.class)
|
@EventListener(RssFeedSyncEvent.class)
|
||||||
public void onReplyEvent(RssFeedSyncEvent event) {
|
public void onReplyEvent(RssFeedSyncEvent event) {
|
||||||
var request = new Request(event.getLink(),event.getSum(),event.getDisableSyncList());
|
var request = new Request(event.getLink(), event.getSum(), event.getDisableSyncList());
|
||||||
queue.addImmediately(request);
|
queue.addImmediately(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
public record Request(Link link,int sum,List<String> disableSyncList) {
|
public record Request(Link link, int sum, List<String> disableSyncList) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -49,6 +49,7 @@ public class FriendRouter {
|
|||||||
.flatMap( templateName -> ServerResponse.ok().render(templateName,
|
.flatMap( templateName -> ServerResponse.ok().render(templateName,
|
||||||
java.util.Map.of(ModelConst.TEMPLATE_ID, templateName,
|
java.util.Map.of(ModelConst.TEMPLATE_ID, templateName,
|
||||||
"friends", friendPostList(request),
|
"friends", friendPostList(request),
|
||||||
|
"authors", friendFinder.listAllAuthors().collectList(),
|
||||||
"title",getFriendTitle())
|
"title",getFriendTitle())
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -65,6 +66,24 @@ public class FriendRouter {
|
|||||||
String linkName = request.queryParam("linkName")
|
String linkName = request.queryParam("linkName")
|
||||||
.filter(StringUtils::isNotBlank)
|
.filter(StringUtils::isNotBlank)
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
|
// --- 新增:获取 author 参数 ---
|
||||||
|
String author = request.queryParam("author")
|
||||||
|
.filter(StringUtils::isNotBlank)
|
||||||
|
.orElse(null);
|
||||||
|
|
||||||
|
// --- 新增:构建查询参数字符串 ---
|
||||||
|
StringBuilder queryString = new StringBuilder();
|
||||||
|
if (StringUtils.isNotBlank(linkName)) {
|
||||||
|
queryString.append("linkName=").append(linkName);
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotBlank(author)) {
|
||||||
|
if (queryString.length() > 0) {
|
||||||
|
queryString.append("&");
|
||||||
|
}
|
||||||
|
queryString.append("author=").append(author);
|
||||||
|
}
|
||||||
|
final String finalQueryString = queryString.toString();
|
||||||
|
|
||||||
return this.settingFetcher.get("base")
|
return this.settingFetcher.get("base")
|
||||||
.map(item -> item.get("pageSize").asInt(10))
|
.map(item -> item.get("pageSize").asInt(10))
|
||||||
.defaultIfEmpty(10)
|
.defaultIfEmpty(10)
|
||||||
@@ -75,16 +94,40 @@ public class FriendRouter {
|
|||||||
if(StringUtils.isNotBlank(linkName)){
|
if(StringUtils.isNotBlank(linkName)){
|
||||||
params.put("linkName",linkName);
|
params.put("linkName",linkName);
|
||||||
}
|
}
|
||||||
|
// --- 新增:把 author 也放进 params ---
|
||||||
|
if(StringUtils.isNotBlank(author)){
|
||||||
|
params.put("author",author);
|
||||||
|
}
|
||||||
|
|
||||||
return friendFinder.list(params)
|
return friendFinder.list(params)
|
||||||
.map(list -> new UrlContextListResult.Builder<FriendPostVo>()
|
.map(list -> {
|
||||||
.listResult(list)
|
// --- 修改:生成链接时带上查询参数 ---
|
||||||
.nextUrl(PageUrlUtils.nextPageUrl(path, totalPage(list)))
|
String baseNextUrl = PageUrlUtils.nextPageUrl(path, totalPage(list));
|
||||||
.prevUrl(PageUrlUtils.prevPageUrl(path))
|
String basePrevUrl = PageUrlUtils.prevPageUrl(path);
|
||||||
.build()
|
|
||||||
);
|
String nextUrl = appendQueryString(baseNextUrl, finalQueryString);
|
||||||
|
String prevUrl = appendQueryString(basePrevUrl, finalQueryString);
|
||||||
|
|
||||||
|
return new UrlContextListResult.Builder<FriendPostVo>()
|
||||||
|
.listResult(list)
|
||||||
|
.nextUrl(nextUrl)
|
||||||
|
.prevUrl(prevUrl)
|
||||||
|
.build();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- 新增:辅助方法,用于拼接查询参数 ---
|
||||||
|
private String appendQueryString(String url, String queryString) {
|
||||||
|
if (StringUtils.isBlank(queryString)) {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
if (url.contains("?")) {
|
||||||
|
return url + "&" + queryString;
|
||||||
|
} else {
|
||||||
|
return url + "?" + queryString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private int pageNumInPathVariable(ServerRequest request) {
|
private int pageNumInPathVariable(ServerRequest request) {
|
||||||
String page = request.pathVariables().get("page");
|
String page = request.pathVariables().get("page");
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user