初始提交:OCI 面板后端(含 GenAI 网关一期)

This commit is contained in:
Wang Defa
2026-07-09 15:31:04 +08:00
commit b9a3e97e84
168 changed files with 31794 additions and 0 deletions
+108
View File
@@ -0,0 +1,108 @@
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
}