feat: 彻底重构后台 UI 与对话上下文引用模块,全面优化移动端适配 (v1.0.4) #6
@@ -63,6 +63,7 @@ lerna-debug.log*
|
|||||||
*.ctxt
|
*.ctxt
|
||||||
|
|
||||||
### Package Files
|
### Package Files
|
||||||
|
*.jar
|
||||||
*.war
|
*.war
|
||||||
*.nar
|
*.nar
|
||||||
*.ear
|
*.ear
|
||||||
@@ -70,6 +71,12 @@ lerna-debug.log*
|
|||||||
*.tar.gz
|
*.tar.gz
|
||||||
*.rar
|
*.rar
|
||||||
|
|
||||||
|
### UI build output
|
||||||
|
ui/dist/
|
||||||
|
ui/dist-ssr/
|
||||||
|
ui/*.local
|
||||||
|
ui/.eslintcache
|
||||||
|
|
||||||
### Local file
|
### Local file
|
||||||
application-local.yml
|
application-local.yml
|
||||||
application-local.yaml
|
application-local.yaml
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group 'top.nxxy335.commentaiautopilot'
|
group 'top.nxxy335.commentaiautopilot'
|
||||||
version '1.0.0'
|
version '1.0.4'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
|||||||
+37
-10
@@ -291,26 +291,51 @@ public class CommentAiAutopilotEndpoint implements CustomEndpoint {
|
|||||||
var commentTime = String.valueOf(comment.getMetadata().getCreationTimestamp());
|
var commentTime = String.valueOf(comment.getMetadata().getCreationTimestamp());
|
||||||
var isCommentAi = isAiOwner(comment.getSpec().getOwner());
|
var isCommentAi = isAiOwner(comment.getSpec().getOwner());
|
||||||
|
|
||||||
|
// 首条评论没有引用对象
|
||||||
var commentMsg = new ConversationMessage(
|
var commentMsg = new ConversationMessage(
|
||||||
"comment", commentOwner, commentContent, commentTime, isCommentAi
|
"comment", commentOwner, commentContent, commentTime, isCommentAi, null, null
|
||||||
);
|
);
|
||||||
|
|
||||||
return client.list(Reply.class,
|
return client.list(Reply.class,
|
||||||
reply -> commentName.equals(reply.getSpec().getCommentName()),
|
reply -> commentName.equals(reply.getSpec().getCommentName()),
|
||||||
null)
|
null)
|
||||||
.sort(Comparator.comparing(r -> r.getMetadata().getCreationTimestamp()))
|
.sort(Comparator.comparing(r -> r.getMetadata().getCreationTimestamp()))
|
||||||
.map(reply -> {
|
.collectList() // 收集为List以便统一处理引用映射
|
||||||
|
.map(replyList -> {
|
||||||
|
List<ConversationMessage> messages = new ArrayList<>();
|
||||||
|
messages.add(commentMsg);
|
||||||
|
|
||||||
|
// 构建 Reply 的映射字典,方便查找引用关系
|
||||||
|
Map<String, Reply> replyMap = new HashMap<>();
|
||||||
|
for (Reply r : replyList) {
|
||||||
|
replyMap.put(r.getMetadata().getName(), r);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Reply reply : replyList) {
|
||||||
var replyOwner = extractOwnerName(reply.getSpec().getOwner());
|
var replyOwner = extractOwnerName(reply.getSpec().getOwner());
|
||||||
var replyContent = extractContent(reply.getSpec().getRaw(), reply.getSpec().getContent());
|
var replyContent = extractContent(reply.getSpec().getRaw(), reply.getSpec().getContent());
|
||||||
var replyTime = String.valueOf(reply.getMetadata().getCreationTimestamp());
|
var replyTime = String.valueOf(reply.getMetadata().getCreationTimestamp());
|
||||||
var isAi = isAiOwner(reply.getSpec().getOwner());
|
var isAi = isAiOwner(reply.getSpec().getOwner());
|
||||||
return new ConversationMessage("reply", replyOwner, replyContent, replyTime, isAi);
|
|
||||||
})
|
String quoteOwner = null;
|
||||||
.collectList()
|
String quoteContent = null;
|
||||||
.map(replyList -> {
|
|
||||||
List<ConversationMessage> messages = new ArrayList<>();
|
// 获取引用的 Reply 名称 (Halo中如果为空,代表直接回复顶级 Comment)
|
||||||
messages.add(commentMsg);
|
String quoteReplyName = reply.getSpec().getQuoteReply();
|
||||||
messages.addAll(replyList);
|
if (quoteReplyName != null && !quoteReplyName.isBlank()) {
|
||||||
|
Reply quotedReply = replyMap.get(quoteReplyName);
|
||||||
|
if (quotedReply != null) {
|
||||||
|
quoteOwner = extractOwnerName(quotedReply.getSpec().getOwner());
|
||||||
|
quoteContent = extractContent(quotedReply.getSpec().getRaw(), quotedReply.getSpec().getContent());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 没有 quoteReply 表示直接回复首条评论
|
||||||
|
quoteOwner = commentOwner;
|
||||||
|
quoteContent = commentContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
messages.add(new ConversationMessage("reply", replyOwner, replyContent, replyTime, isAi, quoteOwner, quoteContent));
|
||||||
|
}
|
||||||
return messages;
|
return messages;
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
@@ -712,7 +737,9 @@ public class CommentAiAutopilotEndpoint implements CustomEndpoint {
|
|||||||
String owner,
|
String owner,
|
||||||
String content,
|
String content,
|
||||||
String time,
|
String time,
|
||||||
boolean isAi
|
boolean isAi,
|
||||||
|
String quoteOwner,
|
||||||
|
String quoteContent
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public record CommenterInfo(
|
public record CommenterInfo(
|
||||||
|
|||||||
@@ -80,7 +80,10 @@ public class CommentReplyPublisher {
|
|||||||
private Mono<Reply> doPublish(String parentCommentName, String replyContent,
|
private Mono<Reply> doPublish(String parentCommentName, String replyContent,
|
||||||
String postName, String quoteReplyName, boolean autoPublish,
|
String postName, String quoteReplyName, boolean autoPublish,
|
||||||
String personaName) {
|
String personaName) {
|
||||||
return resolvePersona(personaName).flatMap(persona -> {
|
|
||||||
|
// 解析 AI 角色并直接发布纯净的回复内容
|
||||||
|
return resolvePersona(personaName)
|
||||||
|
.flatMap(persona -> {
|
||||||
String displayName = persona.displayName();
|
String displayName = persona.displayName();
|
||||||
String email = persona.email();
|
String email = persona.email();
|
||||||
|
|
||||||
@@ -91,8 +94,11 @@ public class CommentReplyPublisher {
|
|||||||
|
|
||||||
var spec = reply.getSpec();
|
var spec = reply.getSpec();
|
||||||
spec.setCommentName(parentCommentName);
|
spec.setCommentName(parentCommentName);
|
||||||
|
|
||||||
|
// 直接存入纯净的 AI 回复内容,不加任何 Markdown 前缀
|
||||||
spec.setRaw(replyContent);
|
spec.setRaw(replyContent);
|
||||||
spec.setContent(replyContent);
|
spec.setContent(replyContent);
|
||||||
|
|
||||||
spec.setApproved(autoPublish);
|
spec.setApproved(autoPublish);
|
||||||
if (autoPublish) {
|
if (autoPublish) {
|
||||||
spec.setApprovedTime(Instant.now());
|
spec.setApprovedTime(Instant.now());
|
||||||
@@ -102,6 +108,7 @@ public class CommentReplyPublisher {
|
|||||||
spec.setAllowNotification(false);
|
spec.setAllowNotification(false);
|
||||||
spec.setHidden(false);
|
spec.setHidden(false);
|
||||||
|
|
||||||
|
// Halo 原生评论组件正是靠这个字段来渲染 "回复 @某人" 的
|
||||||
if (quoteReplyName != null && !quoteReplyName.isBlank()) {
|
if (quoteReplyName != null && !quoteReplyName.isBlank()) {
|
||||||
spec.setQuoteReply(quoteReplyName);
|
spec.setQuoteReply(quoteReplyName);
|
||||||
}
|
}
|
||||||
@@ -117,7 +124,6 @@ public class CommentReplyPublisher {
|
|||||||
|
|
||||||
Map<String, String> ownerAnnotations = new HashMap<>();
|
Map<String, String> ownerAnnotations = new HashMap<>();
|
||||||
ownerAnnotations.put("comment-ai-autopilot.nxxy335.top/is-ai", "true");
|
ownerAnnotations.put("comment-ai-autopilot.nxxy335.top/is-ai", "true");
|
||||||
// 使用Gravatar邮箱头像
|
|
||||||
if (email != null && !email.isBlank()) {
|
if (email != null && !email.isBlank()) {
|
||||||
String gravatarUrl = GravatarUtil.generateUrl(email);
|
String gravatarUrl = GravatarUtil.generateUrl(email);
|
||||||
ownerAnnotations.put(Comment.CommentOwner.AVATAR_ANNO, gravatarUrl);
|
ownerAnnotations.put(Comment.CommentOwner.AVATAR_ANNO, gravatarUrl);
|
||||||
@@ -125,16 +131,10 @@ public class CommentReplyPublisher {
|
|||||||
owner.setAnnotations(ownerAnnotations);
|
owner.setAnnotations(ownerAnnotations);
|
||||||
spec.setOwner(owner);
|
spec.setOwner(owner);
|
||||||
|
|
||||||
log.info("[Publisher] Creating reply for comment: {}, owner: kind={}, name={}, displayName={}, annotations={}",
|
log.info("[Publisher] Creating reply for comment: {}, content length: {}", parentCommentName, replyContent.length());
|
||||||
parentCommentName, owner.getKind(), owner.getName(), owner.getDisplayName(), ownerAnnotations);
|
|
||||||
|
|
||||||
return client.create(reply)
|
return client.create(reply)
|
||||||
.doOnSuccess(created -> {
|
.doOnSuccess(created -> log.info("[Publisher] AI Persona '{}' reply published for comment: {}", displayName, parentCommentName))
|
||||||
var createdOwner = created.getSpec().getOwner();
|
|
||||||
log.info("[Publisher] AI Persona '{}' reply published for comment: {}, quoteReply: {}, owner annotations after create: {}",
|
|
||||||
displayName, parentCommentName, quoteReplyName,
|
|
||||||
createdOwner != null ? createdOwner.getAnnotations() : "null");
|
|
||||||
})
|
|
||||||
.doOnError(e -> log.error("[Publisher] Failed to publish AI reply: {}", e.getMessage()));
|
.doOnError(e -> log.error("[Publisher] Failed to publish AI reply: {}", e.getMessage()));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,4 +30,4 @@ spec:
|
|||||||
url: "https://github.com/sunny-335/plugin-comment-ai-autopilot/blob/main/LICENSE"
|
url: "https://github.com/sunny-335/plugin-comment-ai-autopilot/blob/main/LICENSE"
|
||||||
settingName: "comment-ai-autopilot-settings"
|
settingName: "comment-ai-autopilot-settings"
|
||||||
configMapName: "comment-ai-autopilot-configmap"
|
configMapName: "comment-ai-autopilot-configmap"
|
||||||
version: "1.0.0"
|
version: "1.0.4"
|
||||||
|
|||||||
+202
-869
File diff suppressed because it is too large
Load Diff
+274
-1432
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user