feat: Implement core backend API server, S2A integration, auto-add service, and a new monitoring page.
This commit is contained in:
@@ -78,9 +78,8 @@ func main() {
|
|||||||
}
|
}
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
|
|
||||||
// 启动自动补号服务
|
// 启动自动补号检查器(需在前端开启开关才会实际补号)
|
||||||
api.StartAutoAddService()
|
api.StartAutoAddService()
|
||||||
fmt.Printf("%s[服务]%s 自动补号服务已启动\n", colorGreen, colorReset)
|
|
||||||
|
|
||||||
// 启动服务器
|
// 启动服务器
|
||||||
startServer(cfg)
|
startServer(cfg)
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ var (
|
|||||||
lastAutoAddTime time.Time
|
lastAutoAddTime time.Time
|
||||||
)
|
)
|
||||||
|
|
||||||
// StartAutoAddService 启动自动补号服务
|
// StartAutoAddService 启动自动补号服务(后台检查器)
|
||||||
func StartAutoAddService() {
|
func StartAutoAddService() {
|
||||||
autoAddMu.Lock()
|
autoAddMu.Lock()
|
||||||
if autoAddRunning {
|
if autoAddRunning {
|
||||||
@@ -30,7 +30,8 @@ func StartAutoAddService() {
|
|||||||
autoAddStopChan = make(chan struct{})
|
autoAddStopChan = make(chan struct{})
|
||||||
autoAddMu.Unlock()
|
autoAddMu.Unlock()
|
||||||
|
|
||||||
logger.Info("自动补号服务已启动", "", "auto-add")
|
// 注意:这只是启动后台检查器,实际补号需要前端开启开关
|
||||||
|
logger.Info("自动补号检查器已启动(需在前端开启开关)", "", "auto-add")
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
ticker := time.NewTicker(60 * time.Second) // 每分钟检查一次
|
ticker := time.NewTicker(60 * time.Second) // 每分钟检查一次
|
||||||
@@ -39,7 +40,7 @@ func StartAutoAddService() {
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-autoAddStopChan:
|
case <-autoAddStopChan:
|
||||||
logger.Info("自动补号服务已停止", "", "auto-add")
|
logger.Info("自动补号检查器已停止", "", "auto-add")
|
||||||
return
|
return
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
checkAndAutoAdd()
|
checkAndAutoAdd()
|
||||||
|
|||||||
@@ -45,20 +45,34 @@ export function useS2AApi() {
|
|||||||
|
|
||||||
// 兼容不同的响应格式
|
// 兼容不同的响应格式
|
||||||
// 格式1: { data: [...], total: N }
|
// 格式1: { data: [...], total: N }
|
||||||
// 格式2: { list: [...], total: N }
|
// 格式2: { items: [...], total: N } ← S2A 实际使用这个
|
||||||
// 格式3: 直接是数组 [...]
|
// 格式3: { list: [...], total: N }
|
||||||
|
// 格式4: 直接是数组 [...]
|
||||||
let accounts: S2AAccount[] = []
|
let accounts: S2AAccount[] = []
|
||||||
let total = 0
|
let total = 0
|
||||||
|
|
||||||
|
const res = response as unknown as {
|
||||||
|
data?: S2AAccount[]
|
||||||
|
items?: S2AAccount[]
|
||||||
|
list?: S2AAccount[]
|
||||||
|
total?: number
|
||||||
|
page?: number
|
||||||
|
page_size?: number
|
||||||
|
}
|
||||||
|
|
||||||
if (Array.isArray(response)) {
|
if (Array.isArray(response)) {
|
||||||
accounts = response as unknown as S2AAccount[]
|
accounts = response as unknown as S2AAccount[]
|
||||||
total = accounts.length
|
total = accounts.length
|
||||||
} else if (response.data && Array.isArray(response.data)) {
|
} else if (res.items && Array.isArray(res.items)) {
|
||||||
accounts = response.data
|
// S2A API 使用 items 字段
|
||||||
total = response.total || accounts.length
|
accounts = res.items
|
||||||
} else if ((response as unknown as { list: S2AAccount[] }).list) {
|
total = res.total || accounts.length
|
||||||
accounts = (response as unknown as { list: S2AAccount[] }).list
|
} else if (res.data && Array.isArray(res.data)) {
|
||||||
total = response.total || accounts.length
|
accounts = res.data
|
||||||
|
total = res.total || accounts.length
|
||||||
|
} else if (res.list && Array.isArray(res.list)) {
|
||||||
|
accounts = res.list
|
||||||
|
total = res.total || accounts.length
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Parsed accounts:', accounts.length, 'total:', total)
|
console.log('Parsed accounts:', accounts.length, 'total:', total)
|
||||||
@@ -66,9 +80,9 @@ export function useS2AApi() {
|
|||||||
return {
|
return {
|
||||||
data: accounts,
|
data: accounts,
|
||||||
total: total,
|
total: total,
|
||||||
page: response.page || 1,
|
page: res.page || 1,
|
||||||
page_size: response.page_size || 20,
|
page_size: res.page_size || 20,
|
||||||
total_pages: Math.ceil(total / (response.page_size || 20)),
|
total_pages: Math.ceil(total / (res.page_size || 20)),
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const message = err instanceof Error ? err.message : '获取账号列表失败'
|
const message = err instanceof Error ? err.message : '获取账号列表失败'
|
||||||
|
|||||||
@@ -419,10 +419,12 @@ export default function Monitor() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{deficit > 0 && (
|
{deficit > 0 && (
|
||||||
<p className="mt-2 text-xs text-orange-500 flex items-center gap-1">
|
<div className="mt-2 space-y-1">
|
||||||
|
<p className="text-xs text-orange-500 flex items-center gap-1">
|
||||||
<AlertTriangle className="h-3 w-3" />
|
<AlertTriangle className="h-3 w-3" />
|
||||||
低于目标
|
低于目标,需要约 {Math.ceil(deficit / 4)} 个 Team
|
||||||
</p>
|
</p>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
Reference in New Issue
Block a user