feat(desktop): add Wenxin monitoring adapter and surface platform risk control

Introduces the Wenxin (文心一言) monitoring adapter and registers it in
the runtime controller and adapter index. Adds a new risk_control
failure classification with custom classifiers for Doubao and Wenxin
that recognize rate-limit, 频控 and 访问环境异常 signals, and propagates
this state through account-health, runtime activity alerts, and the
renderer views so users see "触发风控" instead of a generic challenge
message on the Home, Accounts, and AI Platforms screens.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-23 15:26:24 +08:00
parent bd4ee8feef
commit ca40657c5a
12 changed files with 2552 additions and 21 deletions
@@ -21,8 +21,10 @@ function translatePlatform(platform: string) {
return map[platform?.toLowerCase()] || titleCaseToken(platform);
}
function expiredAccountLabel(authState: string) {
switch (authState) {
function blockedAccountLabel(account: { authState: string; authReason: string | null }) {
switch (account.authState) {
case "challenge_required":
return account.authReason === "risk_control" ? "触发风控" : "待验证";
case "revoked":
return "已停用";
case "expired":
@@ -83,8 +85,8 @@ const tasks = computed(() => snapshot.value?.tasks ?? []);
const accounts = computed(() => snapshot.value?.accounts ?? []);
const activity = computed(() => snapshot.value?.activity ?? []);
const expiredAccounts = computed(() =>
accounts.value.filter((item) => ["expired", "revoked"].includes(item.authState)),
const blockedAccounts = computed(() =>
accounts.value.filter((item) => ["expired", "revoked", "challenge_required"].includes(item.authState)),
);
const subsystemCards = computed(() => {
@@ -159,23 +161,23 @@ const subsystemCards = computed(() => {
<div class="card-header">
<div class="header-text">
<h3>账号健康看板</h3>
<p>这里只展示已经确认过期需要重新授权的账号</p>
<p>这里只展示已经确认过期被风控或需要人工处理的账号</p>
</div>
</div>
<div v-if="expiredAccounts.length > 0" class="info-list">
<div v-for="account in expiredAccounts" :key="account.id" class="info-row record-row">
<div v-if="blockedAccounts.length > 0" class="info-list">
<div v-for="account in blockedAccounts" :key="account.id" class="info-row record-row">
<div class="info-stack">
<span class="title">{{ account.displayName }}</span>
<span class="subtitle">{{ translatePlatform(account.platform) }} · <span class="mono-text">{{ formatUid(account.platformUid) }}</span></span>
</div>
<div class="info-stack" style="align-items: flex-end;">
<StatusBadge :tone="healthTone(account.health)" :label="expiredAccountLabel(account.authState)" />
<StatusBadge :tone="healthTone(account.health)" :label="blockedAccountLabel(account)" />
<span class="subtitle" style="margin-top: 6px;">{{ formatRelativeTime(account.lastVerifiedAt ?? account.lastSyncAt) }}</span>
</div>
</div>
</div>
<div v-else class="empty-state">
<p>当前没有已过期账号</p>
<p>当前没有需要人工处理的阻断账号</p>
</div>
</article>