优化定时器

This commit is contained in:
2026-04-22 13:53:42 +08:00
parent 9c3b014fde
commit a190e1bff6
4 changed files with 163 additions and 90 deletions

View File

@@ -212,7 +212,9 @@ const syncAutotkPermissionLimit = async () => {
if (!isElectron()) return if (!isElectron()) return
try { try {
const permissions = getPermissions() const permissions = getPermissions()
if (permissions?.autotk === 1) return const hasAutotkPermission = permissions?.autotk === 1
const hasWebAiPermission = permissions?.webAi === 1
if (hasAutotkPermission || hasWebAiPermission) return
await window.electronAPI.updateAutomationConfig({ await window.electronAPI.updateAutomationConfig({
filters: { filters: {
maxAnchorCount: 1 maxAnchorCount: 1

View File

@@ -270,6 +270,7 @@ import { ref, computed, watch, onMounted, onUnmounted } from 'vue'
import { isElectron } from '../utils/electronBridge' import { isElectron } from '../utils/electronBridge'
import { getPermissions } from '@/utils/storage' import { getPermissions } from '@/utils/storage'
import { usePythonBridge } from '@/utils/pythonBridge' import { usePythonBridge } from '@/utils/pythonBridge'
import { ensureHostListGateMonitorRunning, stopHostListGateMonitor, syncHostListGateDecision } from '@/utils/hostListGateMonitor'
const props = defineProps({ const props = defineProps({
visible: { type: Boolean, required: true } visible: { type: Boolean, required: true }
@@ -339,11 +340,12 @@ const maxCount = ref()
const checkTaskGateState = ref(null) const checkTaskGateState = ref(null)
const selectedLevels = ref(new Set()) const selectedLevels = ref(new Set())
const showLevelDropdown = ref(false) const showLevelDropdown = ref(false)
const hostListDialogInstanceId = `host-list-dialog-${Math.random().toString(36).slice(2)}`
const resolveRestrictedMaxAnchorCount = (fallbackValue = 9999999) => { const resolveRestrictedMaxAnchorCount = (fallbackValue = 9999999) => {
const permissions = getPermissions() const permissions = getPermissions()
return permissions?.webAi === 1 ? fallbackValue : 0 const hasAutotkPermission = permissions?.autotk === 1
const hasWebAiPermission = permissions?.webAi === 1
return hasAutotkPermission || hasWebAiPermission ? fallbackValue : 1
} }
// 添加主播弹窗状态 // 添加主播弹窗状态
@@ -402,23 +404,17 @@ watch(() => props.visible, (newVal) => {
if (newVal) { if (newVal) {
document.body.style.overflow = 'hidden' document.body.style.overflow = 'hidden'
loadLocalGateConfig() loadLocalGateConfig()
if (gateEnabled.value) { if (gateEnabled.value) ensureHostListGateMonitorRunning()
claimGateOwnership()
}
loadHosts() loadHosts()
loadConfig() loadConfig()
} else { } else {
document.body.style.overflow = '' document.body.style.overflow = ''
} }
refreshHostListMonitor()
}) })
onMounted(() => { onMounted(() => {
loadLocalGateConfig() loadLocalGateConfig()
if (gateEnabled.value) { if (gateEnabled.value) ensureHostListGateMonitorRunning()
claimGateOwnership()
}
loadConfig() loadConfig()
if (props.visible) { if (props.visible) {
@@ -426,11 +422,13 @@ onMounted(() => {
loadHosts() loadHosts()
} }
refreshHostListMonitor() if (gateEnabled.value) {
void syncHostListGateDecision(true)
}
}) })
onUnmounted(() => { onUnmounted(() => {
releaseGateOwnership() document.body.style.overflow = ''
}) })
// 监听过滤器变化,同步到后端配置 // 监听过滤器变化,同步到后端配置
@@ -501,76 +499,6 @@ const loadConfig = async () => {
} }
} }
const getGateRuntime = () => {
if (typeof window === 'undefined') {
return { ownerId: null, timer: null }
}
if (!window.__hostListGateRuntime) {
window.__hostListGateRuntime = {
ownerId: null,
timer: null,
}
}
return window.__hostListGateRuntime
}
const isGateOwner = () => {
return getGateRuntime().ownerId === hostListDialogInstanceId
}
const claimGateOwnership = () => {
const runtime = getGateRuntime()
if (runtime.ownerId === hostListDialogInstanceId) return
if (runtime.timer) {
clearInterval(runtime.timer)
runtime.timer = null
}
runtime.ownerId = hostListDialogInstanceId
}
const releaseGateOwnership = () => {
const runtime = getGateRuntime()
if (runtime.ownerId !== hostListDialogInstanceId) return
if (runtime.timer) {
clearInterval(runtime.timer)
runtime.timer = null
}
runtime.ownerId = null
}
const refreshHostListMonitor = () => {
if (!isElectron()) return
const runtime = getGateRuntime()
const shouldRun = gateEnabled.value && isGateOwner()
if (!shouldRun) {
if (runtime.timer && isGateOwner()) {
clearInterval(runtime.timer)
runtime.timer = null
}
return
}
if (runtime.timer) return
runtime.timer = setInterval(async () => {
if (!gateEnabled.value || !isGateOwner()) {
refreshHostListMonitor()
return
}
await loadHosts()
await syncControlCheckTaskGate()
}, HOST_LIST_MONITOR_INTERVAL_MS)
}
const loadLocalGateConfig = () => { const loadLocalGateConfig = () => {
try { try {
const savedGateEnabled = sessionStorage.getItem(HOST_LIST_GATE_ENABLED_SESSION_KEY) const savedGateEnabled = sessionStorage.getItem(HOST_LIST_GATE_ENABLED_SESSION_KEY)
@@ -634,7 +562,6 @@ const persistGateState = (value) => {
const syncControlCheckTaskGate = async (forceSync = false) => { const syncControlCheckTaskGate = async (forceSync = false) => {
if (!isElectron()) return if (!isElectron()) return
if (!gateEnabled.value) return if (!gateEnabled.value) return
if (!isGateOwner()) return
const hostCount = filteredHosts.value.length const hostCount = filteredHosts.value.length
const upperLimit = Number(maxCount.value) const upperLimit = Number(maxCount.value)
@@ -766,16 +693,15 @@ watch(gateEnabled, async (enabled) => {
persistGateEnabled() persistGateEnabled()
if (enabled) { if (enabled) {
claimGateOwnership() ensureHostListGateMonitorRunning()
refreshHostListMonitor() void syncHostListGateDecision(true)
void syncControlCheckTaskGate(true) void syncControlCheckTaskGate(true)
return return
} }
checkTaskGateState.value = null checkTaskGateState.value = null
persistGateState(null) persistGateState(null)
releaseGateOwnership() stopHostListGateMonitor()
refreshHostListMonitor()
if (!isElectron()) return if (!isElectron()) return
@@ -809,6 +735,8 @@ const filteredHosts = computed(() => {
watch( watch(
[() => filteredHosts.value.length, minCount, maxCount, gateEnabled], [() => filteredHosts.value.length, minCount, maxCount, gateEnabled],
() => { () => {
if (gateEnabled.value) ensureHostListGateMonitorRunning()
void syncHostListGateDecision()
void syncControlCheckTaskGate() void syncControlCheckTaskGate()
} }
) )

View File

@@ -30,12 +30,12 @@
<span class="text-base font-medium truncate">自动私信</span> <span class="text-base font-medium truncate">自动私信</span>
</button> </button>
<!-- <button @click="currentView = 'auto_dm_tk'" <button @click="currentView = 'auto_dm_tk'"
class="w-full rounded-xl flex items-center gap-2 px-3 py-2.5 transition-all duration-200" style="height: 6vh;" class="w-full rounded-xl flex items-center gap-2 px-3 py-2.5 transition-all duration-200" style="height: 6vh;"
:class="currentView === 'auto_dm_tk' ? 'bg-white text-blue-600 shadow shadow-blue-900/20' : 'text-slate-400 hover:bg-[rgba(21,96,250,0.06)]'"> :class="currentView === 'auto_dm_tk' ? 'bg-white text-blue-600 shadow shadow-blue-900/20' : 'text-slate-400 hover:bg-[rgba(21,96,250,0.06)]'">
<img :src="currentView === 'auto_dm_tk' ? nav33 : nav3" class="w-9 h-9 object-contain flex-shrink-0" /> <img :src="currentView === 'auto_dm_tk' ? nav33 : nav3" class="w-9 h-9 object-contain flex-shrink-0" />
<span class="text-base font-medium truncate">自动私信TK版</span> <span class="text-base font-medium truncate">自动私信TK版</span>
</button> --> </button>
<button @click="currentView = 'FanWorkbench'" <button @click="currentView = 'FanWorkbench'"
class="w-full rounded-xl flex items-center gap-2 px-3 py-2.5 transition-all duration-200" style="height: 6vh;" class="w-full rounded-xl flex items-center gap-2 px-3 py-2.5 transition-all duration-200" style="height: 6vh;"

View File

@@ -0,0 +1,143 @@
import { isElectron } from '@/utils/electronBridge'
const HOST_LIST_MIN_COUNT_KEY = 'host_list_dialog_min_count'
const HOST_LIST_GATE_ENABLED_SESSION_KEY = 'host_list_dialog_gate_enabled_session'
const HOST_LIST_GATE_STATE_SESSION_KEY = 'host_list_dialog_gate_state_session'
const HOST_LIST_MONITOR_INTERVAL_MS = 30000
let monitorTimer = null
let syncPromise = null
function getElectronAPI() {
if (typeof window === 'undefined') return null
return window.electronAPI || null
}
function getGateEnabled() {
if (typeof window === 'undefined') return false
return sessionStorage.getItem(HOST_LIST_GATE_ENABLED_SESSION_KEY) === 'true'
}
function getLowerLimit() {
if (typeof window === 'undefined') return 5
const raw = Number(localStorage.getItem(HOST_LIST_MIN_COUNT_KEY))
return Number.isFinite(raw) && raw >= 0 ? raw : 5
}
function getPersistedGateState() {
if (typeof window === 'undefined') return null
const raw = sessionStorage.getItem(HOST_LIST_GATE_STATE_SESSION_KEY)
if (raw === 'true') return true
if (raw === 'false') return false
return null
}
function persistGateState(value) {
if (typeof window === 'undefined') return
if (value === null || value === undefined) {
sessionStorage.removeItem(HOST_LIST_GATE_STATE_SESSION_KEY)
return
}
sessionStorage.setItem(HOST_LIST_GATE_STATE_SESSION_KEY, String(value))
}
function getFilteredHostCount(hosts, filters) {
const excludedLevels = Array.isArray(filters?.hostsLevelList) ? filters.hostsLevelList : []
const minOnlineFans = Number(filters?.minOnlineFans) || 0
const maxOnlineFans = Number(filters?.maxOnlineFans) || 0
return hosts.filter((host) => {
if (filters?.gold === false && host.invitationType === 2) return false
if (filters?.ordinary === false && host.invitationType === 1) return false
if (minOnlineFans > 0 && host.onlineFans !== undefined && host.onlineFans < minOnlineFans) {
return false
}
if (maxOnlineFans > 0 && host.onlineFans !== undefined && host.onlineFans > maxOnlineFans) {
return false
}
if (excludedLevels.length > 0 && host.hostsLevel && excludedLevels.includes(host.hostsLevel)) {
return false
}
return true
}).length
}
export async function syncHostListGateDecision(forceSync = false) {
if (!isElectron()) return
if (!getGateEnabled()) return
if (syncPromise) return syncPromise
const electronAPI = getElectronAPI()
if (!electronAPI?.getAutomationConfig || !electronAPI?.loadAnchorData || !electronAPI?.tk?.controlCheckTask) {
return
}
syncPromise = (async () => {
const config = await electronAPI.getAutomationConfig()
const hosts = await electronAPI.loadAnchorData()
const filters = config?.filters || {}
const hostCount = getFilteredHostCount(hosts || [], filters)
const upperLimit = Number(filters.maxAnchorCount)
const lowerLimit = getLowerLimit()
const lastGateState = getPersistedGateState()
let nextState = null
if (Number.isFinite(upperLimit) && upperLimit >= 0 && hostCount >= upperLimit) {
nextState = false
} else if (Number.isFinite(lowerLimit) && lowerLimit >= 0 && hostCount < lowerLimit) {
nextState = true
} else if (lastGateState !== null) {
nextState = lastGateState
} else if (forceSync) {
nextState = true
}
if (nextState === null) return
if (!forceSync && lastGateState === nextState) return
const result = await electronAPI.tk.controlCheckTask({
isRunning: Boolean(nextState),
model: true
})
if (result?.success === false) {
console.error('[HostListGateMonitor] controlCheckTask 调用失败:', result.error)
return
}
persistGateState(nextState)
console.log('[HostListGateMonitor] 门控状态已更新:', nextState, '当前数量:', hostCount)
})().catch((error) => {
console.error('[HostListGateMonitor] 同步门控状态失败:', error)
}).finally(() => {
syncPromise = null
})
return syncPromise
}
export function ensureHostListGateMonitorRunning() {
if (!isElectron()) return
if (monitorTimer) return
monitorTimer = setInterval(() => {
if (!getGateEnabled()) {
stopHostListGateMonitor()
return
}
void syncHostListGateDecision()
}, HOST_LIST_MONITOR_INTERVAL_MS)
}
export function stopHostListGateMonitor() {
if (!monitorTimer) return
clearInterval(monitorTimer)
monitorTimer = null
}