package oci import ( "context" "fmt" "strings" "time" "github.com/oracle/oci-go-sdk/v65/common" "github.com/oracle/oci-go-sdk/v65/usageapi" ) // CostQuery 是一次成本/用量分析的条件。 type CostQuery struct { StartTime time.Time EndTime time.Time Granularity string // DAILY / MONTHLY QueryType string // COST / USAGE GroupBy string // service / skuName / region 等维度 } // CostItem 是一个时间桶内某分组维度的成本或用量。 type CostItem struct { TimeStart *time.Time `json:"timeStart"` GroupValue string `json:"groupValue"` ComputedAmount float32 `json:"computedAmount"` ComputedQuantity float32 `json:"computedQuantity"` Currency string `json:"currency"` Unit string `json:"unit,omitempty"` } // SummarizeCosts 实现 Client:调用 Usage API 汇总租户成本或用量。 func (c *RealClient) SummarizeCosts(ctx context.Context, cred Credentials, q CostQuery) ([]CostItem, error) { uc, err := usageapi.NewUsageapiClientWithConfigurationProvider(provider(cred)) if err != nil { return nil, fmt.Errorf("new usageapi client: %w", err) } applyProxy(&uc.BaseClient, cred) details, err := buildUsageDetails(cred.TenancyOCID, q) if err != nil { return nil, err } var items []CostItem var page *string for { resp, err := uc.RequestSummarizedUsages(ctx, usageapi.RequestSummarizedUsagesRequest{ RequestSummarizedUsagesDetails: details, Page: page, }) if err != nil { return nil, fmt.Errorf("request summarized usages: %w", err) } for _, item := range resp.Items { items = append(items, toCostItem(item, q.GroupBy)) } if resp.OpcNextPage == nil { return orEmpty(items), nil } page = resp.OpcNextPage } } func buildUsageDetails(tenancyOCID string, q CostQuery) (usageapi.RequestSummarizedUsagesDetails, error) { granularity, ok := usageapi.GetMappingRequestSummarizedUsagesDetailsGranularityEnum(strings.ToUpper(q.Granularity)) if !ok { return usageapi.RequestSummarizedUsagesDetails{}, fmt.Errorf("cost query: unsupported granularity %q", q.Granularity) } queryType, ok := usageapi.GetMappingRequestSummarizedUsagesDetailsQueryTypeEnum(strings.ToUpper(q.QueryType)) if !ok { return usageapi.RequestSummarizedUsagesDetails{}, fmt.Errorf("cost query: unsupported queryType %q", q.QueryType) } return usageapi.RequestSummarizedUsagesDetails{ TenantId: &tenancyOCID, TimeUsageStarted: &common.SDKTime{Time: q.StartTime}, TimeUsageEnded: &common.SDKTime{Time: q.EndTime}, Granularity: granularity, QueryType: queryType, GroupBy: []string{q.GroupBy}, }, nil } // toCostItem 把响应条目转为本地 DTO,分组值按 groupBy 维度取对应字段。 func toCostItem(item usageapi.UsageSummary, groupBy string) CostItem { out := CostItem{ Currency: deref(item.Currency), Unit: deref(item.Unit), } if item.TimeUsageStarted != nil { out.TimeStart = &item.TimeUsageStarted.Time } if item.ComputedAmount != nil { out.ComputedAmount = *item.ComputedAmount } if item.ComputedQuantity != nil { out.ComputedQuantity = *item.ComputedQuantity } switch strings.ToLower(groupBy) { case "skuname": out.GroupValue = deref(item.SkuName) case "region": out.GroupValue = deref(item.Region) case "compartmentname": out.GroupValue = deref(item.CompartmentName) default: out.GroupValue = deref(item.Service) } return out }