134 lines
4.2 KiB
Go
134 lines
4.2 KiB
Go
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 // HOURLY / DAILY / MONTHLY
|
|
QueryType string // COST / USAGE
|
|
GroupBy string // 单维度(service / skuName / region 等),或逗号分隔的复合维度(如 "service,skuName",主+子)
|
|
}
|
|
|
|
// CostItem 是一个时间桶内某分组维度的成本或用量。
|
|
type CostItem struct {
|
|
TimeStart *time.Time `json:"timeStart"`
|
|
GroupValue string `json:"groupValue"`
|
|
SubValue string `json:"subValue,omitempty"` // 复合分组的第二维取值(如 service 下的 skuName)
|
|
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: splitGroupBy(q.GroupBy),
|
|
}, nil
|
|
}
|
|
|
|
// splitGroupBy 把 "service,skuName" 复合维度拆成 Usage API 的 groupBy 列表。
|
|
func splitGroupBy(groupBy string) []string {
|
|
parts := strings.Split(groupBy, ",")
|
|
out := make([]string, 0, len(parts))
|
|
for _, p := range parts {
|
|
if p = strings.TrimSpace(p); p != "" {
|
|
out = append(out, p)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// dimValue 按维度名取响应条目上的分组值。
|
|
func dimValue(item usageapi.UsageSummary, dim string) string {
|
|
switch strings.ToLower(strings.TrimSpace(dim)) {
|
|
case "skuname":
|
|
return deref(item.SkuName)
|
|
case "region":
|
|
return deref(item.Region)
|
|
case "compartmentname":
|
|
return deref(item.CompartmentName)
|
|
default:
|
|
return deref(item.Service)
|
|
}
|
|
}
|
|
|
|
// toCostItem 把响应条目转为本地 DTO;首维进 GroupValue,复合分组的次维进 SubValue。
|
|
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
|
|
}
|
|
dims := splitGroupBy(groupBy)
|
|
if len(dims) == 0 {
|
|
dims = []string{"service"}
|
|
}
|
|
out.GroupValue = dimValue(item, dims[0])
|
|
if len(dims) > 1 {
|
|
out.SubValue = dimValue(item, dims[1])
|
|
}
|
|
return out
|
|
}
|