feat(codex): 增强 OAuth 导入解析与账号信息提取,优化维护调度器线程模型

Codex OAuth:
- 导入解析支持附加账号字段(account_id/plan_type/user_id/email)
- enrich_codex 扩展从 access_token 和直接字段提取账号信息
- parse_codex_id_token 支持 JWT/JSON 字符串/dict 三种输入格式
- request patching 新增 openai:compact 格式支持
- codex_usage_parser 新增 credits_unlimited 字段解析

维护调度器:
- 同步 DB 操作迁移到线程池执行,避免阻塞事件循环
- 新增 request_candidates 定期清理任务
- 新增每周 VACUUM ANALYZE 数据库表维护任务
- 新增 enable_db_maintenance 配置项

前端:
- ElapsedTimeText 从 setInterval 改为 requestAnimationFrame
- 时间线过滤 available/unused 占位记录
- Usage 页面默认关闭全局自动刷新
- 移除号池管理中的配额更新时间显示
This commit is contained in:
fawney19
2026-03-04 10:00:38 +08:00
parent e181329a81
commit 82a9fb3c39
17 changed files with 822 additions and 317 deletions

View File

@@ -10,20 +10,18 @@ const props = withDefaults(defineProps<{
status?: string | null
responseTimeMs?: number | null
precision?: number
intervalMs?: number
}>(), {
createdAt: null,
status: null,
responseTimeMs: null,
precision: 2,
intervalMs: 200
})
const now = ref(Date.now())
const precision = computed(() => Math.max(0, props.precision))
const isActive = computed(() => props.status === 'pending' || props.status === 'streaming')
let timer: ReturnType<typeof setInterval> | null = null
let rafId: number | null = null
function parseCreatedAtMs(value: string | null | undefined): number {
if (!value) return Number.NaN
@@ -32,31 +30,33 @@ function parseCreatedAtMs(value: string | null | undefined): number {
return new Date(normalized).getTime()
}
function stopTimer() {
if (!timer) return
clearInterval(timer)
timer = null
function stopRaf() {
if (rafId == null) return
cancelAnimationFrame(rafId)
rafId = null
}
function startTimer() {
stopTimer()
const intervalMs = Math.max(100, props.intervalMs)
timer = setInterval(() => {
now.value = Date.now()
}, intervalMs)
function tick() {
now.value = Date.now()
rafId = requestAnimationFrame(tick)
}
watch([isActive, () => props.intervalMs], ([active]) => {
function startRaf() {
stopRaf()
now.value = Date.now()
rafId = requestAnimationFrame(tick)
}
watch(isActive, (active) => {
if (active) {
now.value = Date.now()
startTimer()
startRaf()
} else {
stopTimer()
stopRaf()
}
}, { immediate: true })
onUnmounted(() => {
stopTimer()
stopRaf()
})
const displayText = computed(() => {

View File

@@ -642,6 +642,10 @@ const normalizeTimelineStatus = (value: unknown): CandidateRecord['status'] => {
return 'failed'
}
const isPlannedOnlyStatus = (status: CandidateRecord['status']): boolean => {
return status === 'available' || status === 'unused'
}
// 候选时间线(按实际执行顺序排序)
const rawTimeline = computed<CandidateRecord[]>(() => {
if (!trace.value) return []
@@ -658,6 +662,11 @@ const rawTimeline = computed<CandidateRecord[]>(() => {
})
})
// 仅保留“已进入执行链路”的节点,过滤预创建但未执行的 available/unused 占位记录。
const executableTimeline = computed<CandidateRecord[]>(() => {
return rawTimeline.value.filter(candidate => !isPlannedOnlyStatus(candidate.status))
})
const schedulingAudit = computed<Record<string, unknown> | null>(() => {
const metadata = props.requestMetadata
if (!metadata || typeof metadata !== 'object' || Array.isArray(metadata)) return null
@@ -677,7 +686,7 @@ const extractPoolGroupId = (candidate: CandidateRecord): string | null => {
const poolAttemptCandidates = computed<CandidateRecord[]>(() => {
// 新链路:优先使用后端写入的 extra_data.pool_group_id。
const fromTrace = rawTimeline.value.filter((candidate) => extractPoolGroupId(candidate) !== null)
const fromTrace = executableTimeline.value.filter((candidate) => extractPoolGroupId(candidate) !== null)
if (fromTrace.length > 0) {
return fromTrace
}
@@ -765,8 +774,8 @@ const poolAttemptKeySet = computed<Set<string>>(() => {
})
const timeline = computed<CandidateRecord[]>(() => {
if (poolAttemptCandidates.value.length === 0) return rawTimeline.value
return rawTimeline.value.filter(
if (poolAttemptCandidates.value.length === 0) return executableTimeline.value
return executableTimeline.value.filter(
(candidate) => !poolAttemptKeySet.value.has(makeAttemptKey(candidate.candidate_index, candidate.retry_index)),
)
})

View File

@@ -373,12 +373,6 @@
/>
</Button>
</template>
<span
v-if="formatQuotaUpdatedAt(key)"
class="text-[10px] text-muted-foreground/70 whitespace-nowrap"
>
{{ formatQuotaUpdatedAt(key) }}
</span>
<Badge
v-if="key.oauth_plan_type"
variant="outline"
@@ -767,12 +761,6 @@
/>
</Button>
</template>
<span
v-if="formatQuotaUpdatedAt(key)"
class="text-[10px] text-muted-foreground/70 truncate"
>
{{ formatQuotaUpdatedAt(key) }}
</span>
<Badge
v-if="key.oauth_plan_type"
variant="outline"
@@ -2078,27 +2066,6 @@ function getOAuthStatusTitle(key: PoolKeyDetail): string {
return `Token 剩余有效期: ${status.text}`
}
function formatUpdatedAt(updatedAtSeconds: number | null | undefined): string {
if (!updatedAtSeconds) return ''
// 触发响应式更新保持“xx分钟前更新”实时变化
void countdownTick.value
const now = Math.floor(Date.now() / 1000)
const diff = now - updatedAtSeconds
if (diff <= 60) return '刚刚更新'
const minutes = Math.floor(diff / 60)
if (minutes < 60) return `${minutes}分钟前更新`
const hours = Math.floor(minutes / 60)
if (hours < 24) return `${hours}小时前更新`
const days = Math.floor(hours / 24)
return `${days}天前更新`
}
function formatQuotaUpdatedAt(key: PoolKeyDetail): string {
return formatUpdatedAt(key.quota_updated_at)
}
function normalizeQuotaLabel(label: string): string {
const normalized = label.trim()
if (!normalized) return '额度'

View File

@@ -164,7 +164,8 @@
<p>2. <strong>压缩日志阶段</strong>: body 字段被压缩存储节省空间</p>
<p>3. <strong>统计阶段</strong>: 仅保留 tokens成本等统计信息</p>
<p>4. <strong>归档删除</strong>: 超过保留期限后完全删除记录</p>
<p>5. <strong>审计日志</strong>: 独立清理记录用户登操作等安全事件</p>
<p>5. <strong>候选记录</strong>: 与详细记录同步清理请求候选记request_candidates</p>
<p>6. <strong>审计日志</strong>: 独立清理记录用户登录操作等安全事件</p>
</div>
</div>
</CardSection>

View File

@@ -250,7 +250,7 @@ let globalAutoRefreshTimer: ReturnType<typeof setInterval> | null = null
let refreshInFlight: Promise<void> | null = null
const AUTO_REFRESH_INTERVAL = 1000 // 1秒刷新一次用于活跃请求
const GLOBAL_AUTO_REFRESH_INTERVAL = 3000 // 3秒刷新一次全局自动刷新
const globalAutoRefresh = ref(true) // 全局自动刷新开关(默认开启
const globalAutoRefresh = ref(false) // 全局自动刷新开关(默认关闭
const isPageVisible = ref(typeof document === 'undefined' ? true : !document.hidden)
// 轮询活跃请求状态(轻量级,只更新状态变化的记录)