修复 AI 分类完全不可用 — classifyWithChoice 和 classifyWithChat 均使用了 GenerateTextRequest.Builder.system() 方法,而该方法在当前 AI Foundation 版本中不被支持或导致运行时错误,导致所有评论均被拦截并显示"AI分类服务不可用,安全拦截"。现改为将 system prompt 合并到 user prompt 中,与可用的 chat() 方法保持一致的调用方式
AI Foundation 隔离加载 — 将 AI Foundation API 引用隔离到 AiFoundationDelegate 类,AiFoundationClient 不再直接引用 AI Foundation 类,修复未安装 AI Foundation 时插件无法启动的问题(NoClassDefFoundError)
评论内容 HTML 剥离 — 前置过滤检测前自动剥离评论 HTML 标签,提升 AI 分类准确性
对话场景精准处罚 — AI 对话场景下违规内容来自 Reply 时,仅取消通过该 Reply 而非父级 Comment,避免误伤
更新日志
v1.4.0
新增
languageRequirement,可自定义多语言回复规则改进
移除
Bug 修复
v1.3.0
新增
MomentsIntegrationService,通过SchemeManager检测 Moment 扩展注册状态,避免直接引用导致的NoClassDefFoundErrorContextExtractor适配 Moment 上下文:使用 moment name 作为关联标识,通过Unstructured单次 fetch 获取瞬间实际内容(spec.content.raw/html)和发布时间(spec.releaseTime)作为 AI 上下文FilterService对 Moment 评论读取momentsEnabled配置决定是否触发 AI 回复CommentPreFilterService.check()新增commentOwner参数,将昵称纳入 AI 分类输入改进
、、三个占位符,将输出规范、情感提示、语言要求拆分为独立模块占位符原位注入;旧模板不含该占位符时自动降级为末尾追加,保持向后兼容buildPrompt重载的代码重复,统一委托给单一核心组装方法/moments列表页改为/moments/{name}具体瞬间页ReviewService在审核服务不可用或异常时不再自动通过,改为返回 FAIL 并拦截发布,避免未经审核的 AI 回复被自动发布Bug 修复
renderContent仅移除<script>和<iframe>标签,未过滤on*事件处理器和javascript:协议。现已全面清理所有事件处理器、危险协议和嵌入标签:disabled="loading"防止重复请求falsePositiveTarget,可能导致重开时显示旧数据AiReplyOrchestrator指数退避无上限 —retryCount较高时延迟可达 43 分钟,超过处理锁 TTL 导致锁提前过期。新增 300 秒上限ContextExtractor空指针风险 —extractCommentContent/extractCommentOwner/extractReplyContent未检查spec == null,畸形数据会触发 NPE 中断整个处理链SettingsView邮箱防抖定时器未清理 — 组件卸载时emailDebounce定时器仍在运行,导致内存泄漏。新增onUnmounted清理HomeView刷新数据 Toast 提前弹出 —refreshData未等待异步请求完成就提示成功。改为await Promise.all()后再提示PromptBuilder安全提示词可被绕过 — 自定义模板若遗漏占位符,安全约束会被静默丢弃。新增安全网:检测到遗漏时强制前置注入安全规范AiReplyOrchestrator.processFalsePositive无去重锁 — 误报处理流程未使用处理锁,重复触发会创建重复 AI 回复。新增processingLocks机制AiReplyOrchestrator.processFalsePositive失败后记录卡在 PENDING — 处理失败时记录未被标记为 FAIL,用户无法重试。新增onErrorResume将记录标记为 FAILAiReplyOrchestrator.hasExistingReply错误时静默放行 — 数据库异常时去重检查返回 false 导致重复创建记录。改为返回 true(失败关闭,宁可跳过也不重复)AiReplyCleanupService删除处理中记录 — 清理逻辑未过滤 PENDING/REVIEWING 状态记录,可能破坏正在进行的 AI 回复流程。新增状态过滤AiReplyCleanupServicenull subscribe 消费者 —.subscribe(null, ...)传入 null 成功消费者,可能导致 NPE。改为空 lambdaAiReplyCleanupService清理开关默认值不一致 — ConfigMap 存在但 data 为 null 时返回 false(禁用),与其他情况返回 true 不一致。统一为 trueInteger.parseInt对非数字参数抛出 500 错误。新增parseIntSafely安全解析toLowerCase()不区分大小写flatMap默认并发 256,大批量操作可能压垮数据库。限制为 10autoRefreshing标志位ReplyReconciler.isAiReply空指针风险 — 未检查spec == null,畸形 Reply 数据会触发 NPEContextExtractor瞬间内容重复 fetch —getMomentContent和getMomentReleaseDate各自独立 fetch 同一个瞬间扩展,产生 2 次重复查询。合并为getMomentContentAndDate单次 fetchContextExtractor瞬间分支缺少容错 —buildContext的 Moment 分支缺少onErrorResume和defaultIfEmpty,异常时静默跳过而非降级处理。已补齐与 Post/SinglePage 一致的容错AiReplyOrchestrator.processFalsePositive锁竞态条件 — 锁值存储过期时间(未来时间戳),过期后putIfAbsent不覆盖旧值导致去重失效。改为存储获取时间,与processComment一致cleanupStaleLocks无法清理误报处理锁 — 误报处理锁值是未来时间戳,cleanupStaleLocks计算 age 为负数永远不清理。统一为存储获取时间ContextExtractor.getCommentCount空指针风险 —reply.getSpec()可能为 null 时直接调用getCommentName()触发 NPE。fetchConversationHistory同样问题已一并修复v1.2.1
Bug 修复
AiReplyOrchestrator.retryOrFail重试计数失效 —.then()丢弃了更新后的记录导致retryCount始终为 0,AI 生成失败时陷入无限重试。改为.flatMap()传递更新后的记录.subscribe()在异步流程中过早触发,导致 AI 回复生成完成后被空字符串覆盖。改为在.doOnSuccess()中触发异步生成PersonaResolver在响应式上下文中使用.block()— 调用阻塞方法会阻塞 Reactor 线程。改为返回Mono<String>并使用Flux.concatMap().next()替代 for 循环penalizeComment/penalizeReply缺少乐观锁重试 — 并发更新 Comment/Reply 时可能静默失败。添加Retry.backoff(3, 100ms)重试approveOriginalComment缺少乐观锁重试 — 同上,添加Retry.backoff(3, 100ms)重试FAIL状态记录 — 仅接受FILTERED和FALSE_POSITIVE状态,AI 生成失败的记录无法重试。现接受FAIL状态tag-NEUTRAL缺少 CSS 样式 — 中性情感标签无样式显示。补充样式定义handleTriggerAiReply缺少加载保护 — 触发 AI 回复按钮可被重复点击导致重复提交。添加 loading 状态filterKeyword输入未做防抖 — 每次按键都触发搜索,性能开销大。添加 300ms 防抖performCleanup逻辑错误 — 清理逻辑存在判断错误改进
extractChoice分类匹配优先级 — 优先匹配违规类别(advertising/abuse/sensitive/meaningless),再匹配normal,避免正常评论被误判为违规类别v1.2.0
新增
FALSE_POSITIVE状态,"仅通过"的记录显示为"误报通过",不显示"通过/拒绝"按钮Bug 修复
processComment()始终调用preFilterService.check(),用户已确认为误报的评论会被再次拦截。新增processFalsePositive()方法跳过前置过滤和去重检查hasExistingReply()找到已有的 FILTERED→PENDING 记录导致 AI 回复无法生成。processFalsePositive()复用已有记录,不经过去重检查processComment()同步等待 AI 生成完成,HTTP 请求长时间不返回。改为.subscribe()异步执行,API 立即返回status=PASS, published=false, reply="",导致显示"通过/拒绝"按钮且内容为空。改为status=FALSE_POSITIVEextractChoice无匹配时返回原始文本 — AI 返回非预期文本时被误判为违规类别。改为返回空字符串触发安全拦截approveOriginalComment缺少乐观锁重试 — 并发更新 Comment/Reply 时可能静默失败。添加Retry.backoff(3, 100ms)重试改进
checkBlockedCommenters重复代码 —FilterService新增isCommenterBlocked(commentName)公共方法,AiReplyOrchestrator改为调用它batchLoading状态,操作期间禁用按钮v1.1.2
Bug 修复
classifyWithChoice和classifyWithChat均使用了GenerateTextRequest.Builder.system()方法,而该方法在当前 AI Foundation 版本中不被支持或导致运行时错误,导致所有评论均被拦截并显示"AI分类服务不可用,安全拦截"。现改为将 system prompt 合并到 user prompt 中,与可用的chat()方法保持一致的调用方式classifyWithChoiceNPE —.map()返回null时触发 Reactor 内部 NullPointerException,改为.flatMap()+Mono.empty()正确触发 fallback改进
AiFoundationDelegate、AiFoundationClient、CommentPreFilterService中增加关键诊断日志(分类开始、fallback 触发、分类结果、异常详情),便于排查分类链路问题v1.1.1
改进
OutputSpec.choice结构化输出不被模型支持时,自动退回到普通 chat 调用并从响应文本中提取分类值(classifyWithChatfallback)v1.1.0
新增
approved置为false,进入待审核队列,前端不再展示该评论改进
AiFoundationDelegate类,AiFoundationClient不再直接引用 AI Foundation 类,修复未安装 AI Foundation 时插件无法启动的问题(NoClassDefFoundError)preFilterEnabled从false迁移为true(新默认值)Bug 修复
BeanDefinitionStoreException: Failed to parse AiFoundationClient,将 AI Foundation API 引用隔离到委托类preFilterEnabled默认值从false改为true,新安装和升级用户均默认启用penalize()遗漏approved=null— Halo 评论创建时approved可能为null,原代码仅处理approved=true的情况classify()失败时放行违规评论 —defaultIfEmpty和onErrorResume改为拦截而非放行v1.0.4
改进
Bug 修复
getConversation重写,构建 Reply 映射字典正确溯源引用关系quoteOwner/quoteContent字段支持引用摘要展示extractOwnerEmail方法,正确从 CommentOwner 提取邮箱用于头像生成v1.0.3
改进
v1.0.2
改进
<style scoped>原生 CSSv1.0.1
改进
v1.0.0
新功能
改进
new ObjectMapper()改为 Spring 构造函数注入Queries.equal()服务端过滤 status/sentiment,减少内存过滤开销spec.sentiment、spec.published、spec.postKind索引Bug 修复
v1.0.0-beta.2
改进
ExtensionGetter.getEnabledExtension(AiModelService.class)获取 AI 服务,替换原先的跨 ClassLoader 反射调用方式(Issue #1)plugin.yaml中声明可选插件依赖ai-foundation?: "*",建立正确的插件依赖关系,插件在未安装 AI Foundation 时仍可正常加载store.halo.run/recommended-apps注解,安装本插件后可在应用市场推荐安装 AI Foundation 插件OutputSpec.choice结构化输出,替换原先的字符串匹配解析,分类更可靠GenerateTextRequest并设置maxRetries=2,由 SDK 自动重试瞬时错误→)、GitHub 链接(nxxy335→sunny-335)、Cravatar 链接(cravatar.cn→cn.cravatar.com)等错误,补充缺失的配置项文档Bug 修复
generateAndPublish中Mono<Void>的 empty 信号触发switchIfEmpty导致publishReply被调用两次,第二次覆盖published=falseDisposableBean正确释放资源:deep(.btn-content)设置 inline-flex 布局,图标和文字并排显示px-3改为pl-3 pr-8为下拉箭头预留空间v1.0.0-beta.1
新功能
Bug 修复
PluginManager无法通过 Spring 依赖注入获取)DefaultSpringPlugin包级私有类反射访问权限问题gradlew缺少执行权限)改进
AiFoundationConfiguration配置类