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() if granularity == "MONTHLY" { return time.Date(t.Year(), t.Month(), 1, 0, 0, 0, 0, time.UTC) } 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 } if granularity == "MONTHLY" { return down.AddDate(0, 1, 0) } return down.AddDate(0, 0, 1) }