还原 1.4.1 基线并修复回评开关状态不一致

- 还原工程:以 1.4.1 安装包为来源,补齐缺失的 filterOnly 配置/编排逻辑/UI 开关,gradle build 全绿(5 测试通过)
- 修复「开启回评」:annotation 改用字符串 on/off 契约,FilterService 归一化解析成为唯一判定入口,默认开启、显式关闭生效、重启不丢;新增 FilterServiceTest 锁定行为
- 整体健壮性:白名单/黑名单(兼容数组形态、正则预编译)、清理天数钳制 [1,365]、UI parseCfg 空值判定修正

Co-Authored-By: WorkBuddy <workbuddy@tencent.com>
This commit is contained in:
2026-09-27 22:39:38 +08:00
co-authored by WorkBuddy
parent 207a013239
commit 0959196c34
10 changed files with 363 additions and 47 deletions
+8
View File
@@ -20,6 +20,9 @@ importers:
axios:
specifier: ^1.13.5
version: 1.17.0
canvas-confetti:
specifier: ^1.9.3
version: 1.9.4
vue:
specifier: ^3.5.28
version: 3.5.38(typescript@5.8.3)
@@ -1031,6 +1034,9 @@ packages:
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
engines: {node: '>=6'}
canvas-confetti@1.9.4:
resolution: {integrity: sha512-yxQbJkAVrFXWNbTUjPqjF7G+g6pDotOUHGbkZq2NELZUMDpiJ85rIEazVb8GTaAptNW2miJAXbs1BtioA251Pw==}
chai@6.2.2:
resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==}
engines: {node: '>=18'}
@@ -3452,6 +3458,8 @@ snapshots:
callsites@3.1.0: {}
canvas-confetti@1.9.4: {}
chai@6.2.2: {}
chalk@4.1.2:
+10 -3
View File
@@ -118,6 +118,10 @@
<div class="form-row__label"><span class="form-label">启用前置过滤</span><span class="form-hint">AI回复前检测评论合规性,拦截广告/辱骂/敏感内容,节省Token</span></div>
<label class="toggle"><input type="checkbox" v-model="settings.basic.preFilterEnabled" /><span class="toggle__track"><span class="toggle__thumb"></span></span></label>
</div>
<div v-if="settings.basic.preFilterEnabled" class="form-row">
<div class="form-row__label"><span class="form-label">仅过滤不回评</span><span class="form-hint">开启后只进行AI评论过滤拦截,不生成AI回复。需先启用前置过滤</span></div>
<label class="toggle"><input type="checkbox" v-model="settings.basic.filterOnly" /><span class="toggle__track"><span class="toggle__thumb"></span></span></label>
</div>
<div class="form-row">
<div class="form-row__label"><span class="form-label">违规评论设为待审核</span><span class="form-hint">检测到违规评论时自动取消通过,需人工审核</span></div>
<label class="toggle"><input type="checkbox" v-model="settings.basic.preFilterPendingOnViolation" /><span class="toggle__track"><span class="toggle__thumb"></span></span></label>
@@ -442,11 +446,11 @@ const DEFAULT_LANGUAGE_REQUIREMENT = `【语言要求】请用评论所使用的
// 配置对象:basic/model/prompt/cleanup 四组
const settings = reactive({
basic: { autoReply: true, autoPublish: true, maxRetryCount: 3, blockedCommenters: "", allowedCommenters: "", maxConversationTurns: 10, rateLimitPerHour: 0, preFilterEnabled: true, preFilterPendingOnViolation: true, momentsEnabled: true, pagesEnabled: false, whitelistEnabled: true },
basic: { autoReply: true, autoPublish: true, maxRetryCount: 3, blockedCommenters: "", allowedCommenters: "", maxConversationTurns: 10, rateLimitPerHour: 0, preFilterEnabled: true, filterOnly: false, preFilterPendingOnViolation: true, momentsEnabled: true, pagesEnabled: false, whitelistEnabled: true },
model: { modelName: "" },
persona: { categoryPersonaMap: {} as Record<string, string> },
prompt: { personaIdentity: "", safetyReview: "", sentimentAdapter: "", outputGuidance: "", languageRequirement: "" },
cleanup: { cleanupEnabled: false, retentionDays: 30 }
cleanup: { cleanupEnabled: true, retentionDays: 30 }
})
// 分类角色映射:编辑行数据(与 settings.model.categoryPersonaMap 双向同步)
@@ -684,7 +688,8 @@ const onNumberChange = (field: 'maxConversationTurns' | 'rateLimitPerHour') => {
if (settings.basic[field] === 0) triggerZeroConfirm(field)
}
const parseCfg = (d:any, k:string) => { const v = d[k]; if(!v) return {}; if(typeof v === 'string') { try{ return JSON.parse(v) }catch{ return {} } } return v }
// 解析 ConfigMap 中的 JSON 字段:仅 null/undefined 视为缺失(false/0 是合法配置值)
const parseCfg = (d:any, k:string) => { const v = d[k]; if(v === undefined || v === null) return {}; if(typeof v === 'string') { try{ return JSON.parse(v) }catch{ return {} } } return v }
// 解析分类角色映射配置(兼容字符串/对象两种存储形式)
const parseCategoryPersonaMap = (raw:any): Record<string,string> => { if(!raw) return {}; if(typeof raw === 'string') { try { const o = JSON.parse(raw); return (o && typeof o === 'object') ? o : {} } catch { return {} } } return (typeof raw === 'object') ? raw : {} }
@@ -708,6 +713,8 @@ const fetchSettings = async () => {
}
if (b.pagesEnabled === undefined) settings.basic.pagesEnabled = false
if (b.whitelistEnabled === undefined) settings.basic.whitelistEnabled = true
// 兼容旧版本 ConfigMap:filterOnly 缺失时回填默认值,避免开关显示"未定义"
if (b.filterOnly === undefined) settings.basic.filterOnly = false
if (b.whitelistedCommenters !== undefined && b.allowedCommenters === undefined) {
settings.basic.allowedCommenters = b.whitelistedCommenters
}