Files
aiops/backend/cloud/huawei_billing.go
T
Logan.Li 9b895efee3 fix: 完善多云同步验证与实例统计
- 修复华为云签名、账单字段与区域映射缓存
- 按真实云实例更新账号服务器数并补充并发测试
2026-07-29 06:35:54 +08:00

134 lines
4.4 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"
)
type huaweiBillRecord struct {
ServiceTypeCode string `json:"service_type_code"`
ServiceTypeName string `json:"service_type_name"`
CloudServiceType string `json:"cloud_service_type"`
CloudServiceTypeName string `json:"cloud_service_type_name"`
ResourceTypeName string `json:"resource_type_name"`
RegionCode string `json:"region_code"`
Region string `json:"region"`
RegionName string `json:"region_name"`
Amount json.RawMessage `json:"amount"`
ConsumeAmount json.RawMessage `json:"consume_amount"`
OfficialAmount json.RawMessage `json:"official_amount"`
DiscountAmount json.RawMessage `json:"discount_amount"`
}
type huaweiBillPage struct {
TotalCount int `json:"total_count"`
Currency string `json:"currency"`
Records []huaweiBillRecord `json:"records"`
FeeRecords []huaweiBillRecord `json:"fee_records"`
}
// 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{}
currency := "CNY"
const pageSize = 1000
for offset := 0; ; offset += pageSize {
query := url.Values{
"bill_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
}
page, err := decodeHuaweiBillPage(body)
if err != nil {
return nil, err
}
if page.Currency != "" {
currency = page.Currency
}
records := page.Records
if len(records) == 0 {
records = page.FeeRecords
}
for _, item := range records {
cost := huaweiBillRecordCost(item)
if cost == 0 {
continue
}
service := firstNonEmpty(item.ServiceTypeName, item.CloudServiceTypeName, item.ResourceTypeName, item.ServiceTypeCode, item.CloudServiceType, "其他")
region := firstNonEmpty(item.RegionName, item.RegionCode, item.Region, "全局")
aggregation[aggregationKey{service: service, region: region}] += cost
}
if len(records) < pageSize || (page.TotalCount > 0 && offset+pageSize >= page.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: currency, Service: key.service, Region: key.region,
Description: "云 API 自动同步(华为云 BSS)",
})
}
return records, nil
}
// decodeHuaweiBillPage 兼容华为云 records 与旧版 fee_records 响应。 // Logan.Li
func decodeHuaweiBillPage(body []byte) (huaweiBillPage, error) {
var page huaweiBillPage
if err := json.Unmarshal(body, &page); err != nil {
return page, fmt.Errorf("解析华为云 BSS 响应失败: %w", err)
}
return page, nil
}
// huaweiBillRecordCost 依次读取实际消费、流水金额和官网价减优惠。 // Logan.Li
func huaweiBillRecordCost(item huaweiBillRecord) float64 {
if cost := huaweiBillAmount(item.ConsumeAmount); cost != 0 {
return cost
}
if cost := huaweiBillAmount(item.Amount); cost != 0 {
return cost
}
return huaweiBillAmount(item.OfficialAmount) - huaweiBillAmount(item.DiscountAmount)
}
// 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
}