feat: Implement batch team owner processing with dedicated upload, configuration, and monitoring pages and backend services.
This commit is contained in:
@@ -89,29 +89,6 @@ export default function Monitor() {
|
||||
return json
|
||||
}
|
||||
|
||||
// 获取号池状态 - 使用 S2A 的 dashboard/stats 接口
|
||||
const fetchPoolStatus = useCallback(async () => {
|
||||
try {
|
||||
const data = await requestS2A('/dashboard/stats')
|
||||
if (data) {
|
||||
// 从 dashboard/stats 构建 pool status
|
||||
setPoolStatus({
|
||||
current: data.normal_accounts || 0,
|
||||
target: targetInput,
|
||||
deficit: Math.max(0, targetInput - (data.normal_accounts || 0)),
|
||||
auto_add: autoAdd,
|
||||
min_interval: minInterval,
|
||||
polling_enabled: pollingEnabled,
|
||||
polling_interval: pollingInterval,
|
||||
})
|
||||
// 同时更新统计数据
|
||||
setStats(data)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('获取号池状态失败:', e)
|
||||
}
|
||||
}, [targetInput, autoAdd, minInterval, pollingEnabled, pollingInterval])
|
||||
|
||||
// 刷新 S2A 统计 - 使用 dashboard/stats
|
||||
const refreshStats = useCallback(async () => {
|
||||
setRefreshing(true)
|
||||
@@ -277,27 +254,59 @@ export default function Monitor() {
|
||||
const json = await res.json()
|
||||
if (json.code === 0 && json.data) {
|
||||
const s = json.data
|
||||
setTargetInput(s.target || 50)
|
||||
setAutoAdd(s.auto_add || false)
|
||||
setMinInterval(s.min_interval || 300)
|
||||
setPollingEnabled(s.polling_enabled || false)
|
||||
const target = s.target || 50
|
||||
const autoAddVal = s.auto_add || false
|
||||
const minIntervalVal = s.min_interval || 300
|
||||
const pollingEnabledVal = s.polling_enabled || false
|
||||
const interval = s.polling_interval || 60
|
||||
|
||||
setTargetInput(target)
|
||||
setAutoAdd(autoAddVal)
|
||||
setMinInterval(minIntervalVal)
|
||||
setPollingEnabled(pollingEnabledVal)
|
||||
setPollingInterval(interval)
|
||||
savedPollingIntervalRef.current = interval // 同步更新 ref
|
||||
savedPollingIntervalRef.current = interval
|
||||
setCountdown(interval)
|
||||
|
||||
// 返回加载的配置用于后续刷新
|
||||
return { target, autoAdd: autoAddVal, minInterval: minIntervalVal, pollingEnabled: pollingEnabledVal, pollingInterval: interval }
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('加载监控设置失败:', e)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
// 初始化 - 只在组件挂载时执行一次
|
||||
useEffect(() => {
|
||||
loadMonitorSettings()
|
||||
fetchPoolStatus()
|
||||
refreshStats()
|
||||
fetchAutoAddLogs()
|
||||
const init = async () => {
|
||||
// 先加载设置
|
||||
const settings = await loadMonitorSettings()
|
||||
|
||||
// 然后刷新状态(使用加载的设置值)
|
||||
try {
|
||||
const data = await requestS2A('/dashboard/stats')
|
||||
if (data) {
|
||||
const target = settings?.target || 50
|
||||
setStats(data)
|
||||
setPoolStatus({
|
||||
current: data.normal_accounts || 0,
|
||||
target: target,
|
||||
deficit: Math.max(0, target - (data.normal_accounts || 0)),
|
||||
auto_add: settings?.autoAdd || false,
|
||||
min_interval: settings?.minInterval || 300,
|
||||
polling_enabled: settings?.pollingEnabled || false,
|
||||
polling_interval: settings?.pollingInterval || 60,
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('获取号池状态失败:', e)
|
||||
}
|
||||
|
||||
fetchAutoAddLogs()
|
||||
}
|
||||
init()
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
|
||||
|
||||
@@ -72,6 +72,7 @@ export default function Upload() {
|
||||
const [browserType, setBrowserType] = useState<'chromedp' | 'rod'>('chromedp')
|
||||
const [proxy, setProxy] = useState('')
|
||||
const [includeOwner, setIncludeOwner] = useState(false) // 母号也入库
|
||||
const [processCount, setProcessCount] = useState(0) // 处理数量,0表示全部
|
||||
|
||||
const hasConfig = config.s2a.apiBase && config.s2a.adminKey
|
||||
|
||||
@@ -177,6 +178,7 @@ export default function Upload() {
|
||||
headless: true, // 始终使用无头模式
|
||||
proxy,
|
||||
include_owner: includeOwner, // 母号也入库
|
||||
process_count: processCount, // 处理数量,0表示全部
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -192,7 +194,7 @@ export default function Upload() {
|
||||
alert('启动失败')
|
||||
}
|
||||
setLoading(false)
|
||||
}, [stats, membersPerTeam, concurrentTeams, browserType, proxy, includeOwner, fetchStatus])
|
||||
}, [stats, membersPerTeam, concurrentTeams, browserType, proxy, includeOwner, processCount, fetchStatus])
|
||||
|
||||
// 停止处理
|
||||
const handleStop = useCallback(async () => {
|
||||
@@ -371,6 +373,37 @@ export default function Upload() {
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
{/* 处理数量设置 */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">
|
||||
处理母号数量
|
||||
</label>
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
type="number"
|
||||
min={1}
|
||||
max={stats?.valid || 100}
|
||||
value={processCount || ''}
|
||||
onChange={(e) => setProcessCount(Number(e.target.value) || 0)}
|
||||
disabled={isRunning}
|
||||
placeholder="输入数量"
|
||||
className="flex-1"
|
||||
/>
|
||||
<Button
|
||||
variant={processCount === 0 ? 'primary' : 'outline'}
|
||||
size="sm"
|
||||
onClick={() => setProcessCount(0)}
|
||||
disabled={isRunning}
|
||||
className="whitespace-nowrap"
|
||||
>
|
||||
全部 ({stats?.valid || 0})
|
||||
</Button>
|
||||
</div>
|
||||
<p className="text-xs text-slate-500 mt-1">
|
||||
{processCount > 0 ? `将处理 ${Math.min(processCount, stats?.valid || 0)} 个母号` : `将处理全部 ${stats?.valid || 0} 个母号`}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<Input
|
||||
label="每个 Team 成员数"
|
||||
|
||||
Reference in New Issue
Block a user