98 lines
2.7 KiB
Go
98 lines
2.7 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"oci-portal/internal/oci"
|
|
)
|
|
|
|
// maxUsageWindowDays 限制流量/成本查询窗口,避免拉取超大区间。
|
|
const maxUsageWindowDays = 366
|
|
|
|
// InstanceTraffic 查询实例近 days 天的进出流量(按天聚合)。
|
|
func (s *OciConfigService) InstanceTraffic(ctx context.Context, id uint, region, instanceID string, days int) (oci.InstanceTraffic, error) {
|
|
if days <= 0 {
|
|
days = 30
|
|
}
|
|
if days > maxUsageWindowDays {
|
|
return oci.InstanceTraffic{}, fmt.Errorf("instance traffic: days must be within %d", maxUsageWindowDays)
|
|
}
|
|
cred, err := s.credentialsByID(ctx, id)
|
|
if err != nil {
|
|
return oci.InstanceTraffic{}, err
|
|
}
|
|
end := time.Now().UTC().Truncate(time.Hour).Add(time.Hour)
|
|
return s.client.SummarizeInstanceTraffic(ctx, cred, oci.TrafficQuery{
|
|
Region: region,
|
|
InstanceID: instanceID,
|
|
StartTime: end.AddDate(0, 0, -days),
|
|
EndTime: end,
|
|
})
|
|
}
|
|
|
|
// Costs 汇总租户成本或用量;时间缺省为最近 30 天,起止按粒度对齐。
|
|
func (s *OciConfigService) Costs(ctx context.Context, id uint, q oci.CostQuery) ([]oci.CostItem, error) {
|
|
applyCostDefaults(&q)
|
|
if q.EndTime.Sub(q.StartTime) <= 0 {
|
|
return nil, fmt.Errorf("costs: endTime must be after startTime")
|
|
}
|
|
if q.EndTime.Sub(q.StartTime) > maxUsageWindowDays*24*time.Hour {
|
|
return nil, fmt.Errorf("costs: window must be within %d days", maxUsageWindowDays)
|
|
}
|
|
cred, err := s.credentialsByID(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return s.client.SummarizeCosts(ctx, cred, q)
|
|
}
|
|
|
|
// applyCostDefaults 填充缺省值并把时间对齐到粒度边界(Usage API 要求)。
|
|
func applyCostDefaults(q *oci.CostQuery) {
|
|
if q.Granularity == "" {
|
|
q.Granularity = "DAILY"
|
|
}
|
|
if q.QueryType == "" {
|
|
q.QueryType = "COST"
|
|
}
|
|
if q.GroupBy == "" {
|
|
q.GroupBy = "service"
|
|
}
|
|
now := time.Now().UTC()
|
|
if q.EndTime.IsZero() {
|
|
q.EndTime = now
|
|
}
|
|
if q.StartTime.IsZero() {
|
|
q.StartTime = q.EndTime.AddDate(0, 0, -30)
|
|
}
|
|
q.StartTime = alignDown(q.StartTime, q.Granularity)
|
|
// endTime 为开区间,向上对齐到粒度边界以覆盖当天/当月
|
|
q.EndTime = alignUp(q.EndTime, q.Granularity)
|
|
}
|
|
|
|
func alignDown(t time.Time, granularity string) time.Time {
|
|
t = t.UTC()
|
|
switch granularity {
|
|
case "MONTHLY":
|
|
return time.Date(t.Year(), t.Month(), 1, 0, 0, 0, 0, time.UTC)
|
|
case "HOURLY":
|
|
return t.Truncate(time.Hour)
|
|
}
|
|
return t.Truncate(24 * time.Hour)
|
|
}
|
|
|
|
func alignUp(t time.Time, granularity string) time.Time {
|
|
down := alignDown(t, granularity)
|
|
if down.Equal(t.UTC()) {
|
|
return down
|
|
}
|
|
switch granularity {
|
|
case "MONTHLY":
|
|
return down.AddDate(0, 1, 0)
|
|
case "HOURLY":
|
|
return down.Add(time.Hour)
|
|
}
|
|
return down.AddDate(0, 0, 1)
|
|
}
|