From fc32b577986ecc4478f901da2479972f84f7c553 Mon Sep 17 00:00:00 2001 From: IanShaw027 <131567472+IanShaw027@users.noreply.github.com> Date: Fri, 9 Jan 2026 20:59:33 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E5=9B=BD=E9=99=85=E5=8C=96):=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E8=BF=90=E7=BB=B4=E7=9B=91=E6=8E=A7=E5=A4=9A=E8=AF=AD?= =?UTF-8?q?=E8=A8=80=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加英文翻译(en.ts)包含 ops 监控所有文案 - 添加中文翻译(zh.ts)包含 ops 监控所有文案 --- frontend/src/i18n/locales/en.ts | 382 ++++++++++++++++++++++++++++++++ frontend/src/i18n/locales/zh.ts | 382 ++++++++++++++++++++++++++++++++ 2 files changed, 764 insertions(+) diff --git a/frontend/src/i18n/locales/en.ts b/frontend/src/i18n/locales/en.ts index 393641a7..f80a235f 100644 --- a/frontend/src/i18n/locales/en.ts +++ b/frontend/src/i18n/locales/en.ts @@ -131,6 +131,7 @@ export default { noData: 'No data', success: 'Success', error: 'Error', + critical: 'Critical', warning: 'Warning', info: 'Info', active: 'Active', @@ -145,6 +146,8 @@ export default { copiedToClipboard: 'Copied to clipboard', copyFailed: 'Failed to copy', contactSupport: 'Contact Support', + add: 'Add', + invalidEmail: 'Please enter a valid email address', selectOption: 'Select an option', searchPlaceholder: 'Search...', noOptionsFound: 'No options found', @@ -177,6 +180,7 @@ export default { accounts: 'Accounts', proxies: 'Proxies', redeemCodes: 'Redeem Codes', + ops: 'Ops', settings: 'Settings', myAccount: 'My Account', lightMode: 'Light Mode', @@ -1713,6 +1717,370 @@ export default { failedToLoad: 'Failed to load usage records' }, + // Ops Monitoring + ops: { + title: 'Ops Monitoring', + description: 'Operational monitoring and troubleshooting', + // Dashboard + systemHealth: 'System Health', + overview: 'Overview', + noSystemMetrics: 'No system metrics collected yet.', + collectedAt: 'Collected at:', + window: 'window', + cpu: 'CPU', + memory: 'Memory', + db: 'DB', + redis: 'Redis', + goroutines: 'Goroutines', + jobs: 'Jobs', + active: 'active', + idle: 'idle', + ok: 'ok', + lastRun: 'last_run:', + lastSuccess: 'last_success:', + lastError: 'last_error:', + noData: 'No data.', + loadingText: 'loading', + ready: 'ready', + requestsTotal: 'Requests (total)', + slaScope: 'SLA scope:', + tokens: 'Tokens', + tps: 'TPS:', + current: 'current', + peak: 'peak', + sla: 'SLA (excl business limits)', + businessLimited: 'business_limited:', + errors: 'Errors', + errorRate: 'error_rate:', + upstreamRate: 'upstream_rate:', + latencyDuration: 'Latency (duration_ms)', + ttftLabel: 'TTFT (first_token_ms)', + p50: 'p50:', + p90: 'p90:', + p95: 'p95:', + p99: 'p99:', + avg: 'avg:', + max: 'max:', + qps: 'QPS', + requests: 'Requests', + upstream: 'Upstream', + client: 'Client', + system: 'System', + other: 'Other', + errorsSla: 'Errors (SLA scope)', + upstreamExcl429529: 'Upstream (excl 429/529)', + failedToLoadData: 'Failed to load ops data.', + tpsK: 'TPS (K)', + top: 'Top:', + throughputTrend: 'Throughput Trend', + latencyHistogram: 'Latency Histogram', + errorTrend: 'Error Trend', + errorDistribution: 'Error Distribution', + // Error Log + errorLog: { + timeId: 'Time / ID', + context: 'Context', + status: 'Status', + message: 'Message', + latency: 'Latency', + action: 'Action', + noErrors: 'No errors in this window.', + grp: 'GRP:', + acc: 'ACC:', + details: 'Details', + phase: 'Phase' + }, + // Error Details Modal + errorDetails: { + upstreamErrors: 'Upstream Errors', + requestErrors: 'Request Errors', + total: 'Total:', + searchPlaceholder: 'Search request_id / client_request_id / message', + accountIdPlaceholder: 'account_id' + }, + // Error Detail Modal + errorDetail: { + loading: 'Loading…', + requestId: 'Request ID', + time: 'Time', + phase: 'Phase', + status: 'Status', + message: 'Message', + basicInfo: 'Basic Info', + platform: 'Platform', + model: 'Model', + latency: 'Latency', + ttft: 'TTFT', + businessLimited: 'Business Limited', + requestPath: 'Request Path', + timings: 'Timings', + auth: 'Auth', + routing: 'Routing', + upstream: 'Upstream', + response: 'Response', + retry: 'Retry', + retryClient: 'Retry (Client)', + retryUpstream: 'Retry (Upstream pinned)', + pinnedAccountId: 'Pinned account_id', + retryNotes: 'Retry Notes', + requestBody: 'Request Body', + errorBody: 'Error Body', + trimmed: 'trimmed', + confirmRetry: 'Confirm Retry', + retrySuccess: 'Retry succeeded', + retryFailed: 'Retry failed', + na: 'N/A', + retryHint: 'Retry will resend the request with the same parameters', + retryClientHint: 'Use client retry (no account pinning)', + retryUpstreamHint: 'Use upstream pinned retry (pin to the error account)', + pinnedAccountIdHint: '(auto from error log)', + retryNote1: 'Retry will use the same request body and parameters', + retryNote2: 'If the original request failed due to account issues, pinned retry may still fail', + retryNote3: 'Client retry will reselect an account', + confirmRetryMessage: 'Confirm retry this request?', + confirmRetryHint: 'Will resend with the same request parameters' + }, + requestDetails: { + title: 'Request Details', + details: 'Details', + rangeLabel: 'Window: {range}', + rangeMinutes: '{n} minutes', + rangeHours: '{n} hours', + empty: 'No requests in this window.', + emptyHint: 'Try a different time range or remove filters.', + failedToLoad: 'Failed to load request details', + requestIdCopied: 'Request ID copied', + copyFailed: 'Copy failed', + copy: 'Copy', + viewError: 'View Error', + kind: { + success: 'SUCCESS', + error: 'ERROR' + }, + table: { + time: 'Time', + kind: 'Kind', + platform: 'Platform', + model: 'Model', + duration: 'Duration', + status: 'Status', + requestId: 'Request ID', + actions: 'Actions' + } + }, + alertEvents: { + title: 'Alert Events', + description: 'Recent alert firing/resolution records (email-only)', + loading: 'Loading...', + empty: 'No alert events', + loadFailed: 'Failed to load alert events', + table: { + time: 'Time', + status: 'Status', + severity: 'Severity', + title: 'Title', + metric: 'Metric / Threshold', + email: 'Email Sent' + } + }, + alertRules: { + title: 'Alert Rules', + description: 'Create and manage threshold-based system alerts (email-only)', + loading: 'Loading...', + empty: 'No alert rules', + loadFailed: 'Failed to load alert rules', + saveFailed: 'Failed to save alert rule', + deleteFailed: 'Failed to delete alert rule', + create: 'Create Rule', + createTitle: 'Create Alert Rule', + editTitle: 'Edit Alert Rule', + deleteConfirmTitle: 'Delete this rule?', + deleteConfirmMessage: 'This will remove the rule and its related events. Continue?', + metrics: { + successRate: 'Success Rate (%)', + errorRate: 'Error Rate (%)', + p95: 'P95 Latency (ms)', + p99: 'P99 Latency (ms)', + cpu: 'CPU Usage (%)', + memory: 'Memory Usage (%)', + queueDepth: 'Concurrency Queue Depth' + }, + table: { + name: 'Name', + metric: 'Metric', + severity: 'Severity', + enabled: 'Enabled', + actions: 'Actions' + }, + form: { + name: 'Name', + description: 'Description', + metric: 'Metric', + operator: 'Operator', + threshold: 'Threshold', + severity: 'Severity', + window: 'Window (minutes)', + sustained: 'Sustained (samples)', + cooldown: 'Cooldown (minutes)', + enabled: 'Enabled', + notifyEmail: 'Send email notifications' + }, + validation: { + title: 'Please fix the following issues', + invalid: 'Invalid rule', + nameRequired: 'Name is required', + metricRequired: 'Metric is required', + operatorRequired: 'Operator is required', + thresholdRequired: 'Threshold must be a number', + windowRange: 'Window must be one of: 1, 5, 60 minutes', + sustainedRange: 'Sustained must be between 1 and 1440 samples', + cooldownRange: 'Cooldown must be between 0 and 1440 minutes' + } + }, + runtime: { + title: 'Ops Runtime Settings', + description: 'Stored in database; changes take effect without editing config files.', + loading: 'Loading...', + noData: 'No runtime settings available', + loadFailed: 'Failed to load runtime settings', + saveSuccess: 'Runtime settings saved', + saveFailed: 'Failed to save runtime settings', + alertTitle: 'Alert Evaluator', + groupAvailabilityTitle: 'Group Availability Monitor', + evalIntervalSeconds: 'Evaluation Interval (seconds)', + silencing: { + title: 'Alert Silencing (Maintenance Mode)', + enabled: 'Enable silencing', + globalUntil: 'Silence until (RFC3339)', + untilPlaceholder: '2026-01-05T00:00:00Z', + untilHint: 'Leave empty to only toggle silencing without an expiry (not recommended).', + reason: 'Reason', + reasonPlaceholder: 'e.g., planned maintenance', + entries: { + title: 'Advanced: targeted silencing', + hint: 'Optional: silence only certain rules or severities. Leave fields empty to match all.', + add: 'Add Entry', + empty: 'No targeted entries', + entryTitle: 'Entry #{n}', + ruleId: 'Rule ID (optional)', + ruleIdPlaceholder: 'e.g., 1', + severities: 'Severities (optional)', + severitiesPlaceholder: 'e.g., P0,P1 (empty = all)', + until: 'Until (RFC3339)', + reason: 'Reason', + validation: { + untilRequired: 'Entry until time is required', + untilFormat: 'Entry until time must be a valid RFC3339 timestamp', + ruleIdPositive: 'Entry rule_id must be a positive integer', + severitiesFormat: 'Entry severities must be a comma-separated list of P0..P3' + } + }, + validation: { + timeFormat: 'Silence time must be a valid RFC3339 timestamp' + } + }, + lockEnabled: 'Distributed Lock Enabled', + lockKey: 'Distributed Lock Key', + lockTTLSeconds: 'Distributed Lock TTL (seconds)', + showAdvancedDeveloperSettings: 'Show advanced developer settings (Distributed Lock)', + advancedSettingsSummary: 'Advanced settings (Distributed Lock)', + evalIntervalHint: 'How often the evaluator runs. Keeping the default is recommended.', + validation: { + title: 'Please fix the following issues', + invalid: 'Invalid settings', + evalIntervalRange: 'Evaluation interval must be between 1 and 86400 seconds', + lockKeyRequired: 'Distributed lock key is required when lock is enabled', + lockKeyPrefix: 'Distributed lock key must start with "{prefix}"', + lockKeyHint: 'Recommended: start with "{prefix}" to avoid conflicts', + lockTtlRange: 'Distributed lock TTL must be between 1 and 86400 seconds' + } + }, + email: { + title: 'Email Notification', + description: 'Configure alert/report email notifications (stored in database).', + loading: 'Loading...', + noData: 'No email notification config', + loadFailed: 'Failed to load email notification config', + saveSuccess: 'Email notification config saved', + saveFailed: 'Failed to save email notification config', + alertTitle: 'Alert Emails', + reportTitle: 'Report Emails', + recipients: 'Recipients', + recipientsHint: 'If empty, the system may fallback to the first admin email.', + minSeverity: 'Min Severity', + minSeverityAll: 'All severities', + rateLimitPerHour: 'Rate limit per hour', + batchWindowSeconds: 'Batch window (seconds)', + includeResolved: 'Include resolved alerts', + dailySummary: 'Daily summary', + weeklySummary: 'Weekly summary', + errorDigest: 'Error digest', + errorDigestMinCount: 'Min errors for digest', + accountHealth: 'Account health', + accountHealthThreshold: 'Error rate threshold (%)', + cronPlaceholder: 'Cron expression', + reportHint: 'Schedules use cron syntax; leave empty to use defaults.', + validation: { + title: 'Please fix the following issues', + invalid: 'Invalid email notification config', + alertRecipientsRequired: 'Alert emails are enabled but no recipients are configured', + reportRecipientsRequired: 'Report emails are enabled but no recipients are configured', + invalidRecipients: 'One or more recipient emails are invalid', + rateLimitRange: 'Rate limit per hour must be a number ≥ 0', + batchWindowRange: 'Batch window must be between 0 and 86400 seconds', + cronRequired: 'A cron expression is required when schedule is enabled', + cronFormat: 'Cron expression format looks invalid (expected at least 5 parts)', + digestMinCountRange: 'Min errors for digest must be a number ≥ 0', + accountHealthThresholdRange: 'Account health threshold must be between 0 and 100' + } + }, + concurrency: { + title: 'Concurrency / Queue', + byPlatform: 'By Platform', + byGroup: 'By Group', + byAccount: 'By Account', + totalRows: '{count} rows', + disabledHint: 'Realtime monitoring is disabled in settings.', + empty: 'No data', + queued: 'Queue {count}', + rateLimited: 'Rate-limited {count}', + errorAccounts: 'Errors {count}', + loadFailed: 'Failed to load concurrency data' + }, + realtime: { + connected: 'Realtime connected', + connecting: 'Realtime connecting', + reconnecting: 'Realtime reconnecting', + offline: 'Realtime offline', + closed: 'Realtime closed', + reconnectIn: 'retry in {seconds}s' + }, + queryMode: { + auto: 'Auto', + raw: 'Raw', + preagg: 'Preagg' + }, + accountAvailability: { + available: 'Available', + unavailable: 'Unavailable', + accountError: 'Error' + }, + tooltips: { + throughputTrend: 'Requests/QPS + Tokens/TPS in the selected window.', + latencyHistogram: 'Latency distribution (duration_ms) for successful requests.', + errorTrend: 'Error counts over time (SLA scope excludes business limits; upstream excludes 429/529).', + errorDistribution: 'Error distribution by status code.' + }, + charts: { + emptyRequest: 'No requests in this window.', + emptyError: 'No errors in this window.', + resetZoom: 'Reset', + resetZoomHint: 'Reset zoom (if enabled)', + downloadChart: 'Download', + downloadChartHint: 'Download chart as image' + } + }, + // Settings settings: { title: 'System Settings', @@ -1803,6 +2171,20 @@ export default { sending: 'Sending...', enterRecipientHint: 'Please enter a recipient email address' }, + opsMonitoring: { + title: 'Ops Monitoring', + description: 'Enable ops monitoring for troubleshooting and health visibility', + disabled: 'Ops monitoring is disabled', + enabled: 'Enable Ops Monitoring', + enabledHint: 'Enable the ops monitoring module (admin only)', + realtimeEnabled: 'Enable Realtime Monitoring', + realtimeEnabledHint: 'Enable realtime QPS/metrics push (WebSocket)', + queryMode: 'Default Query Mode', + queryModeHint: 'Default query mode for Ops Dashboard (auto/raw/preagg)', + queryModeAuto: 'Auto (recommended)', + queryModeRaw: 'Raw (most accurate, slower)', + queryModePreagg: 'Preagg (fastest, requires aggregation)' + }, adminApiKey: { title: 'Admin API Key', description: 'Global API key for external system integration with full admin access', diff --git a/frontend/src/i18n/locales/zh.ts b/frontend/src/i18n/locales/zh.ts index fb46bbbe..646511f4 100644 --- a/frontend/src/i18n/locales/zh.ts +++ b/frontend/src/i18n/locales/zh.ts @@ -128,6 +128,7 @@ export default { noData: '暂无数据', success: '成功', error: '错误', + critical: '严重', warning: '警告', info: '提示', active: '启用', @@ -142,6 +143,8 @@ export default { copiedToClipboard: '已复制到剪贴板', copyFailed: '复制失败', contactSupport: '联系客服', + add: '添加', + invalidEmail: '请输入有效的邮箱地址', selectOption: '请选择', searchPlaceholder: '搜索...', noOptionsFound: '无匹配选项', @@ -175,6 +178,7 @@ export default { accounts: '账号管理', proxies: 'IP管理', redeemCodes: '兑换码', + ops: '运维监控', settings: '系统设置', myAccount: '我的账户', lightMode: '浅色模式', @@ -1858,6 +1862,370 @@ export default { failedToLoad: '加载使用记录失败' }, + // Ops Monitoring + ops: { + title: '运维监控', + description: '运维监控与排障', + // Dashboard + systemHealth: '系统健康', + overview: '概览', + noSystemMetrics: '尚未收集系统指标。', + collectedAt: '采集时间:', + window: '窗口', + cpu: 'CPU', + memory: '内存', + db: '数据库', + redis: 'Redis', + goroutines: '协程', + jobs: '后台任务', + active: '活跃', + idle: '空闲', + ok: '正常', + lastRun: '最近运行', + lastSuccess: '最近成功', + lastError: '最近错误', + noData: '暂无数据', + loadingText: '加载中...', + ready: '就绪', + requestsTotal: '请求(总计)', + slaScope: 'SLA 范围:', + tokens: 'Token', + tps: 'TPS', + current: '当前', + peak: '峰值', + sla: 'SLA(排除业务限制)', + businessLimited: '业务限制:', + errors: '错误', + errorRate: '错误率:', + upstreamRate: '上游错误率:', + latencyDuration: '延迟 (duration_ms)', + ttftLabel: 'TTFT (first_token_ms)', + p50: 'p50', + p90: 'p90', + p95: 'p95', + p99: 'p99', + avg: 'avg', + max: 'max', + qps: 'QPS', + requests: '请求', + upstream: '上游', + client: '客户端', + system: '系统', + other: '其他', + errorsSla: '错误(SLA范围)', + upstreamExcl429529: '上游(排除429/529)', + failedToLoadData: '加载运维数据失败', + tpsK: 'TPS (K)', + top: '最高:', + throughputTrend: '吞吐趋势', + latencyHistogram: '延迟分布', + errorTrend: '错误趋势', + errorDistribution: '错误分布', + // Error Log + errorLog: { + timeId: '时间 / ID', + context: '上下文', + status: '状态码', + message: '消息', + latency: '延迟', + action: '操作', + noErrors: '该窗口内暂无错误。', + grp: 'GRP:', + acc: 'ACC:', + details: '详情', + phase: '阶段' + }, + // Error Details Modal + errorDetails: { + upstreamErrors: '上游错误', + requestErrors: '请求错误', + total: '总计:', + searchPlaceholder: '搜索 request_id / client_request_id / message', + accountIdPlaceholder: 'account_id' + }, + // Error Detail Modal + errorDetail: { + loading: '加载中…', + requestId: '请求 ID', + time: '时间', + phase: '阶段', + status: '状态码', + message: '消息', + basicInfo: '基本信息', + platform: '平台', + model: '模型', + latency: '延迟', + ttft: 'TTFT', + businessLimited: '业务限制', + requestPath: '请求路径', + timings: '时序信息', + auth: '认证', + routing: '路由', + upstream: '上游', + response: '响应', + retry: '重试', + retryClient: '重试(客户端)', + retryUpstream: '重试(上游固定)', + pinnedAccountId: '固定 account_id', + retryNotes: '重试说明', + requestBody: '请求体', + errorBody: '错误体', + trimmed: '已截断', + confirmRetry: '确认重试', + retrySuccess: '重试成功', + retryFailed: '重试失败', + na: 'N/A', + retryHint: '重试将使用相同的请求参数重新发送请求', + retryClientHint: '使用客户端重试(不固定账号)', + retryUpstreamHint: '使用上游固定重试(固定到错误的账号)', + pinnedAccountIdHint: '(自动从错误日志获取)', + retryNote1: '重试会使用相同的请求体和参数', + retryNote2: '如果原请求失败是因为账号问题,固定重试可能仍会失败', + retryNote3: '客户端重试会重新选择账号', + confirmRetryMessage: '确认要重试该请求吗?', + confirmRetryHint: '将使用相同的请求参数重新发送' + }, + requestDetails: { + title: '请求明细', + details: '明细', + rangeLabel: '窗口:{range}', + rangeMinutes: '{n} 分钟', + rangeHours: '{n} 小时', + empty: '该窗口内暂无请求。', + emptyHint: '可尝试调整时间范围或取消部分筛选。', + failedToLoad: '加载请求明细失败', + requestIdCopied: '请求ID已复制', + copyFailed: '复制失败', + copy: '复制', + viewError: '查看错误', + kind: { + success: '成功', + error: '失败' + }, + table: { + time: '时间', + kind: '类型', + platform: '平台', + model: '模型', + duration: '耗时', + status: '状态码', + requestId: '请求ID', + actions: '操作' + } + }, + alertEvents: { + title: '告警事件', + description: '最近的告警触发/恢复记录(仅邮件通知)', + loading: '加载中...', + empty: '暂无告警事件', + loadFailed: '加载告警事件失败', + table: { + time: '时间', + status: '状态', + severity: '级别', + title: '标题', + metric: '指标 / 阈值', + email: '邮件已发送' + } + }, + alertRules: { + title: '告警规则', + description: '创建与管理系统阈值告警(仅邮件通知)', + loading: '加载中...', + empty: '暂无告警规则', + loadFailed: '加载告警规则失败', + saveFailed: '保存告警规则失败', + deleteFailed: '删除告警规则失败', + create: '新建规则', + createTitle: '新建告警规则', + editTitle: '编辑告警规则', + deleteConfirmTitle: '确认删除该规则?', + deleteConfirmMessage: '将删除该规则及其关联的告警事件,是否继续?', + metrics: { + successRate: '成功率 (%)', + errorRate: '错误率 (%)', + p95: 'P95 延迟 (ms)', + p99: 'P99 延迟 (ms)', + cpu: 'CPU 使用率 (%)', + memory: '内存使用率 (%)', + queueDepth: '并发排队深度' + }, + table: { + name: '名称', + metric: '指标', + severity: '级别', + enabled: '启用', + actions: '操作' + }, + form: { + name: '名称', + description: '描述', + metric: '指标', + operator: '运算符', + threshold: '阈值', + severity: '级别', + window: '统计窗口(分钟)', + sustained: '连续样本数(每分钟)', + cooldown: '冷却期(分钟)', + enabled: '启用', + notifyEmail: '发送邮件通知' + }, + validation: { + title: '请先修正以下问题', + invalid: '规则不合法', + nameRequired: '名称不能为空', + metricRequired: '指标不能为空', + operatorRequired: '运算符不能为空', + thresholdRequired: '阈值必须为数字', + windowRange: '统计窗口必须为 1 / 5 / 60 分钟之一', + sustainedRange: '连续样本数必须在 1 到 1440 之间', + cooldownRange: '冷却期必须在 0 到 1440 分钟之间' + } + }, + runtime: { + title: '运维监控运行设置', + description: '配置存储在数据库中,无需修改 config 文件即可生效。', + loading: '加载中...', + noData: '暂无运行设置', + loadFailed: '加载运行设置失败', + saveSuccess: '运行设置已保存', + saveFailed: '保存运行设置失败', + alertTitle: '告警评估器', + groupAvailabilityTitle: '分组可用性监控', + evalIntervalSeconds: '评估间隔(秒)', + silencing: { + title: '告警静默(维护模式)', + enabled: '启用静默', + globalUntil: '静默截止时间(RFC3339)', + untilPlaceholder: '2026-01-05T00:00:00Z', + untilHint: '建议填写截止时间,避免忘记关闭静默。', + reason: '原因', + reasonPlaceholder: '例如:计划维护', + entries: { + title: '高级:定向静默', + hint: '可选:仅静默特定规则或特定级别。字段留空表示匹配全部。', + add: '新增条目', + empty: '暂无定向静默条目', + entryTitle: '条目 #{n}', + ruleId: '规则ID(可选)', + ruleIdPlaceholder: '例如:1', + severities: '级别(可选)', + severitiesPlaceholder: '例如:P0,P1(留空=全部)', + until: '截止时间(RFC3339)', + reason: '原因', + validation: { + untilRequired: '条目截止时间不能为空', + untilFormat: '条目截止时间必须为合法的 RFC3339 时间戳', + ruleIdPositive: '条目 rule_id 必须为正整数', + severitiesFormat: '条目级别必须为 P0..P3 的逗号分隔列表' + } + }, + validation: { + timeFormat: '静默时间必须为合法的 RFC3339 时间戳' + } + }, + lockEnabled: '启用分布式锁', + lockKey: '分布式锁 Key', + lockTTLSeconds: '分布式锁 TTL(秒)', + showAdvancedDeveloperSettings: '显示高级开发者设置 (Distributed Lock)', + advancedSettingsSummary: '高级设置 (分布式锁)', + evalIntervalHint: '检测任务的执行频率,建议保持默认。', + validation: { + title: '请先修正以下问题', + invalid: '设置不合法', + evalIntervalRange: '评估间隔必须在 1 到 86400 秒之间', + lockKeyRequired: '启用分布式锁时必须填写 Lock Key', + lockKeyPrefix: '分布式锁 Key 必须以「{prefix}」开头', + lockKeyHint: '建议以「{prefix}」开头以避免冲突', + lockTtlRange: '分布式锁 TTL 必须在 1 到 86400 秒之间' + } + }, + email: { + title: '邮件通知配置', + description: '配置告警/报告邮件通知(存储在数据库中)。', + loading: '加载中...', + noData: '暂无邮件通知配置', + loadFailed: '加载邮件通知配置失败', + saveSuccess: '邮件通知配置已保存', + saveFailed: '保存邮件通知配置失败', + alertTitle: '告警邮件', + reportTitle: '报告邮件', + recipients: '收件人', + recipientsHint: '若为空,系统可能会回退使用第一个管理员邮箱。', + minSeverity: '最低级别', + minSeverityAll: '全部级别', + rateLimitPerHour: '每小时限额', + batchWindowSeconds: '合并窗口(秒)', + includeResolved: '包含恢复通知', + dailySummary: '每日摘要', + weeklySummary: '每周摘要', + errorDigest: '错误摘要', + errorDigestMinCount: '错误摘要最小数量', + accountHealth: '账号健康报告', + accountHealthThreshold: '错误率阈值(%)', + cronPlaceholder: 'Cron 表达式', + reportHint: '发送时间使用 Cron 语法;留空将使用默认值。', + validation: { + title: '请先修正以下问题', + invalid: '邮件通知配置不合法', + alertRecipientsRequired: '已启用告警邮件,但未配置任何收件人', + reportRecipientsRequired: '已启用报告邮件,但未配置任何收件人', + invalidRecipients: '存在不合法的收件人邮箱', + rateLimitRange: '每小时限额必须为 ≥ 0 的数字', + batchWindowRange: '合并窗口必须在 0 到 86400 秒之间', + cronRequired: '启用定时任务时必须填写 Cron 表达式', + cronFormat: 'Cron 表达式格式可能不正确(至少应包含 5 段)', + digestMinCountRange: '错误摘要最小数量必须为 ≥ 0 的数字', + accountHealthThresholdRange: '账号健康错误率阈值必须在 0 到 100 之间' + } + }, + concurrency: { + title: '并发 / 排队', + byPlatform: '按平台', + byGroup: '按分组', + byAccount: '按账号', + totalRows: '共 {count} 项', + disabledHint: '已在设置中关闭实时监控。', + empty: '暂无数据', + queued: '队列 {count}', + rateLimited: '限流 {count}', + errorAccounts: '异常 {count}', + loadFailed: '加载并发数据失败' + }, + realtime: { + connected: '实时已连接', + connecting: '实时连接中', + reconnecting: '实时重连中', + offline: '实时离线', + closed: '实时已关闭', + reconnectIn: '重连 {seconds}s' + }, + queryMode: { + auto: 'Auto(自动)', + raw: 'Raw(不聚合)', + preagg: 'Preagg(聚合)' + }, + accountAvailability: { + available: '可用', + unavailable: '不可用', + accountError: '异常' + }, + tooltips: { + throughputTrend: '当前窗口内的请求/QPS 与 token/TPS 趋势。', + latencyHistogram: '成功请求的延迟分布(duration_ms)。', + errorTrend: '错误趋势(SLA 口径排除业务限制;上游错误率排除 429/529)。', + errorDistribution: '按状态码统计的错误分布。' + }, + charts: { + emptyRequest: '该时间窗口内暂无请求。', + emptyError: '该时间窗口内暂无错误。', + resetZoom: '重置', + resetZoomHint: '重置缩放(若启用)', + downloadChart: '下载', + downloadChartHint: '下载图表图片' + } + }, + // Settings settings: { title: '系统设置', @@ -1947,6 +2315,20 @@ export default { sending: '发送中...', enterRecipientHint: '请输入收件人邮箱地址' }, + opsMonitoring: { + title: '运维监控', + description: '启用运维监控模块,用于排障与健康可视化', + disabled: '运维监控已关闭', + enabled: '启用运维监控', + enabledHint: '启用 Ops 运维监控模块(仅管理员可见)', + realtimeEnabled: '启用实时监控', + realtimeEnabledHint: '启用实时 QPS/指标推送(WebSocket)', + queryMode: '默认查询模式', + queryModeHint: 'Ops Dashboard 默认查询模式(auto/raw/preagg)', + queryModeAuto: '自动(推荐)', + queryModeRaw: 'Raw(最准,但较慢)', + queryModePreagg: 'Preagg(最快,需预聚合)' + }, adminApiKey: { title: '管理员 API Key', description: '用于外部系统集成的全局 API Key,拥有完整的管理员权限',