fix: 完善多云同步验证与实例统计

- 修复华为云签名、账单字段与区域映射缓存
- 按真实云实例更新账号服务器数并补充并发测试
This commit is contained in:
Logan.Li committed 2026-07-29 06:35:54 +08:00
1 parent 132fe9ba97
commit 9b895efee3
6 files changed
+226 -64

No files matched your search

-18
View File
@@ -33,24 +33,6 @@ func NewCloudAccountHandler(db *gorm.DB) *CloudAccountHandler {
func (h *CloudAccountHandler) ListCloudAccounts(c *gin.Context) {
var accounts []model.CloudAccount
h.db.Find(&accounts)
// 服务器数来自真实云账号关联,避免重复导入后被“本次新增数”覆盖。 // Logan.Li
type accountServerCount struct {
CloudAccountID uint
Total int
}
var serverCounts []accountServerCount
h.db.Model(&model.Server{}).
Select("cloud_account_id, COUNT(*) AS total").
Where("cloud_account_id IS NOT NULL").Group("cloud_account_id").Scan(&serverCounts)
countByAccount := make(map[uint]int, len(serverCounts))
for _, item := range serverCounts {
countByAccount[item.CloudAccountID] = item.Total
}
for index := range accounts {
if total, ok := countByAccount[accounts[index].ID]; ok {
accounts[index].ServerCount = total
}
}
c.JSON(http.StatusOK, model.Response{
Code: 200,
+44 -10
View File
@@ -3,6 +3,7 @@ package controller
import (
"net/http"
"strconv"
"sync"
"time"
"ezops/cloud"
@@ -73,22 +74,60 @@ func (h *CloudSyncHandler) SyncRegions(c *gin.Context) {
}
}
// 更新账号的 region_count 和 last_sync_at
h.db.Model(&account).Updates(map[string]interface{}{
// 并发统计所有可用区域实例数,卡片展示云账号真实资源量。 // Logan.Li
serverCount, countedRegions := countCloudInstances(provider, cloudRegions)
updates := map[string]interface{}{
"region_count": len(cloudRegions),
"last_sync_at": time.Now(),
})
}
if countedRegions > 0 {
updates["server_count"] = serverCount
}
// 更新账号区域、实例数量和同步时间。 // Logan.Li
h.db.Model(&account).Updates(updates)
c.JSON(http.StatusOK, model.Response{
Code: 200,
Message: "同步成功",
Data: gin.H{
"synced": synced,
"total": len(cloudRegions),
"synced": synced,
"total": len(cloudRegions),
"server_count": serverCount,
},
})
}
// countCloudInstances 以固定并发统计各区域实例数量,避免串行请求过慢。 // Logan.Li
func countCloudInstances(provider cloud.Provider, regions []cloud.CloudRegion) (total int, succeeded int) {
type countResult struct {
count int
ok bool
}
results := make(chan countResult, len(regions))
semaphore := make(chan struct{}, 4)
var waitGroup sync.WaitGroup
for _, region := range regions {
regionID := region.RegionID
waitGroup.Add(1)
go func() {
defer waitGroup.Done()
semaphore <- struct{}{}
defer func() { <-semaphore }()
instances, err := provider.ListInstances(regionID)
results <- countResult{count: len(instances), ok: err == nil}
}()
}
waitGroup.Wait()
close(results)
for result := range results {
if result.ok {
total += result.count
succeeded++
}
}
return total, succeeded
}
// GetAccountRegions 获取指定云账号的区域列表(按 provider 过滤)
// GET /cloud-accounts/:id/regions
func (h *CloudSyncHandler) GetAccountRegions(c *gin.Context) {
@@ -308,11 +347,6 @@ func (h *CloudSyncHandler) ImportInstances(c *gin.Context) {
}
}
// 保存该账号当前已关联的资产总数,而不是本次新增数。 // Logan.Li
var serverCount int64
h.db.Model(&model.Server{}).Where("cloud_account_id = ?", account.ID).Count(&serverCount)
h.db.Model(&account).UpdateColumn("server_count", serverCount)
c.JSON(http.StatusOK, model.Response{
Code: 200,
Message: "导入完成",
+52
View File
@@ -0,0 +1,52 @@
package controller
import (
"errors"
"sync/atomic"
"testing"
"time"
"ezops/cloud"
)
type countInstancesProvider struct {
counts map[string]int
failures map[string]bool
activeCalls atomic.Int32
maxCalls atomic.Int32
}
func (p *countInstancesProvider) Name() string { return "fake" }
func (p *countInstancesProvider) ListRegions() ([]cloud.CloudRegion, error) { return nil, nil }
func (p *countInstancesProvider) ListInstances(regionID string) ([]cloud.CloudInstance, error) {
active := p.activeCalls.Add(1)
defer p.activeCalls.Add(-1)
for {
maximum := p.maxCalls.Load()
if active <= maximum || p.maxCalls.CompareAndSwap(maximum, active) {
break
}
}
time.Sleep(5 * time.Millisecond)
if p.failures[regionID] {
return nil, errors.New("区域请求失败")
}
return make([]cloud.CloudInstance, p.counts[regionID]), nil
}
func TestCountCloudInstances(t *testing.T) {
provider := &countInstancesProvider{
counts: map[string]int{"a": 2, "b": 3, "c": 4, "d": 5, "e": 6},
failures: map[string]bool{"c": true},
}
regions := []cloud.CloudRegion{{RegionID: "a"}, {RegionID: "b"}, {RegionID: "c"}, {RegionID: "d"}, {RegionID: "e"}}
total, succeeded := countCloudInstances(provider, regions)
if total != 16 || succeeded != 4 {
t.Fatalf("实例统计错误: total=%d succeeded=%d", total, succeeded)
}
if maximum := provider.maxCalls.Load(); maximum > 4 {
t.Fatalf("并发数超过限制: %d", maximum)
}
}