Files
aiops/backend/cloud/huawei_billing.go
T
Logan.Li 132fe9ba97 feat: 补全多云资产与账单同步
- 接入 AWS、华为云和 Azure 资产及账单 API

- 按原始币种隔离统计并修正账号服务器数

- 修复侧栏菜单展开后的滚动行为
2026-07-29 06:15:00 +08:00

99 lines
3.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package cloud
import (
"encoding/json"
"fmt"
"math"
"net/url"
"sort"
"strconv"
"strings"
"time"
)
// FetchDailyBill 通过华为云 BSS 费用流水接口拉取单日账单。 // Logan.Li
func (p *HuaweiProvider) FetchDailyBill(billingDate string) ([]CloudBillingRecord, error) {
if _, err := time.Parse("2006-01-02", billingDate); err != nil {
return nil, fmt.Errorf("账单日期格式应为 YYYY-MM-DD")
}
type aggregationKey struct {
service string
region string
}
aggregation := map[aggregationKey]float64{}
const pageSize = 1000
for offset := 0; ; offset += pageSize {
query := url.Values{
"cycle": {billingDate[:7]},
"bill_date_begin": {billingDate},
"bill_date_end": {billingDate},
"offset": {strconv.Itoa(offset)},
"limit": {strconv.Itoa(pageSize)},
}
body, err := p.signedGet("https://bss.myhuaweicloud.com/v2/bills/customer-bills/fee-records?"+query.Encode(), "bss")
if err != nil {
return nil, err
}
var response struct {
TotalCount int `json:"total_count"`
FeeRecords []struct {
CloudServiceType string `json:"cloud_service_type"`
CloudServiceTypeName string `json:"cloud_service_type_name"`
ResourceTypeName string `json:"resource_type_name"`
Region string `json:"region"`
RegionName string `json:"region_name"`
Amount json.RawMessage `json:"amount"`
OfficialAmount json.RawMessage `json:"official_amount"`
DiscountAmount json.RawMessage `json:"discount_amount"`
} `json:"fee_records"`
}
if err = json.Unmarshal(body, &response); err != nil {
return nil, fmt.Errorf("解析华为云 BSS 响应失败: %w", err)
}
for _, item := range response.FeeRecords {
cost := huaweiBillAmount(item.Amount)
if cost == 0 {
cost = huaweiBillAmount(item.OfficialAmount) - huaweiBillAmount(item.DiscountAmount)
}
if cost == 0 {
continue
}
service := firstNonEmpty(item.CloudServiceTypeName, item.ResourceTypeName, item.CloudServiceType, "其他")
region := firstNonEmpty(item.RegionName, item.Region, "全局")
aggregation[aggregationKey{service: service, region: region}] += cost
}
if len(response.FeeRecords) < pageSize || (response.TotalCount > 0 && offset+pageSize >= response.TotalCount) {
break
}
}
keys := make([]aggregationKey, 0, len(aggregation))
for key := range aggregation {
keys = append(keys, key)
}
sort.Slice(keys, func(i, j int) bool {
if keys[i].service == keys[j].service {
return keys[i].region < keys[j].region
}
return keys[i].service < keys[j].service
})
records := make([]CloudBillingRecord, 0, len(keys))
for _, key := range keys {
records = append(records, CloudBillingRecord{
BillingDate: billingDate, Provider: "huawei", Cost: math.Round(aggregation[key]*100) / 100,
Currency: "CNY", Service: key.service, Region: key.region,
Description: "云 API 自动同步(华为云 BSS)",
})
}
return records, nil
}
// huaweiBillAmount 兼容华为云金额字段的字符串和数字格式。 // Logan.Li
func huaweiBillAmount(raw json.RawMessage) float64 {
value := strings.Trim(strings.TrimSpace(string(raw)), "\"")
amount, err := strconv.ParseFloat(value, 64)
if err != nil {
return 0
}
return amount
}