134 lines
5.0 KiB
Go
134 lines
5.0 KiB
Go
package oci
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/oracle/oci-go-sdk/v65/common"
|
|
|
|
"oci-portal/internal/model"
|
|
)
|
|
|
|
// AccountProfile 是一次云端订阅查询得到的账户信息快照。
|
|
type AccountProfile struct {
|
|
AccountType string
|
|
SubscriptionID string
|
|
ServiceName string
|
|
PaymentModel string
|
|
SubscriptionTier string
|
|
PromotionStatus string
|
|
PromotionAmount float32
|
|
PromotionExpires *time.Time
|
|
}
|
|
|
|
// AccountCapabilities 是账户能力接口的关键字段。来源为 Limits 服务
|
|
// 20230301 版 `/compartments/{tenancy}/capabilities?accountInfoOnly=true`
|
|
// (与控制台同源;oci-go-sdk v65 未收录,自签名裸调)。
|
|
// 该接口对暂停/已终止租户仍可访问,借此把「暂停」从「失联」中区分出来。
|
|
type AccountCapabilities struct {
|
|
Suspended bool `json:"suspended"`
|
|
AccountStatus string `json:"accountStatus"`
|
|
FreeTierEnabled bool `json:"freeTierEnabled"`
|
|
FreeTierOnly bool `json:"freeTierOnly"`
|
|
PromotionStatus string `json:"promotionStatus"`
|
|
ProgramType string `json:"programType"`
|
|
HasSaasSubscription bool `json:"hasSaasSubscription"`
|
|
Deletable bool `json:"deletable"`
|
|
}
|
|
|
|
// capabilitiesEnvelope 对应接口原始响应:顶层与 accountInfo 各有一个
|
|
// suspended,任一为 true 即视为暂停。
|
|
type capabilitiesEnvelope struct {
|
|
Capabilities struct {
|
|
Suspended bool `json:"suspended"`
|
|
AccountInfo struct {
|
|
FreeTierEnabled bool `json:"freeTierEnabled"`
|
|
FreeTierOnly bool `json:"freeTierOnly"`
|
|
PromotionStatus string `json:"promotionStatus"`
|
|
Suspended bool `json:"suspended"`
|
|
ProgramType string `json:"programType"`
|
|
HasSaasSubscription bool `json:"hasSaasSubscription"`
|
|
AccountStatus string `json:"accountStatus"`
|
|
Deletable bool `json:"deletable"`
|
|
} `json:"accountInfo"`
|
|
} `json:"capabilities"`
|
|
}
|
|
|
|
// parseAccountCapabilities 把原始响应体压平为关键字段视图。
|
|
func parseAccountCapabilities(body []byte) (AccountCapabilities, error) {
|
|
var env capabilitiesEnvelope
|
|
if err := json.Unmarshal(body, &env); err != nil {
|
|
return AccountCapabilities{}, fmt.Errorf("decode account capabilities: %w", err)
|
|
}
|
|
info := env.Capabilities.AccountInfo
|
|
return AccountCapabilities{
|
|
Suspended: env.Capabilities.Suspended || info.Suspended,
|
|
AccountStatus: info.AccountStatus,
|
|
FreeTierEnabled: info.FreeTierEnabled,
|
|
FreeTierOnly: info.FreeTierOnly,
|
|
PromotionStatus: info.PromotionStatus,
|
|
ProgramType: info.ProgramType,
|
|
HasSaasSubscription: info.HasSaasSubscription,
|
|
Deletable: info.Deletable,
|
|
}, nil
|
|
}
|
|
|
|
// GetAccountCapabilities 实现 Client:查询账户能力(暂停/账户状态/免费层)。
|
|
// region 应传 home region;请求带 API Key 签名,并沿用租户出站代理。
|
|
func (c *RealClient) GetAccountCapabilities(ctx context.Context, cred Credentials, region string) (AccountCapabilities, error) {
|
|
url := fmt.Sprintf("https://limits.%s.oci.oraclecloud.com/20230301/compartments/%s/capabilities?accountInfoOnly=true",
|
|
normalizeRegion(region), cred.TenancyOCID)
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
|
if err != nil {
|
|
return AccountCapabilities{}, fmt.Errorf("new capabilities request: %w", err)
|
|
}
|
|
if err := common.DefaultRequestSigner(provider(cred)).Sign(req); err != nil {
|
|
return AccountCapabilities{}, fmt.Errorf("sign capabilities request: %w", err)
|
|
}
|
|
hc := proxyHTTPClient(cred.Proxy)
|
|
if hc == nil {
|
|
hc = &http.Client{Timeout: proxyClientTimeout}
|
|
}
|
|
resp, err := hc.Do(req)
|
|
if err != nil {
|
|
return AccountCapabilities{}, fmt.Errorf("get account capabilities: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
body, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
|
|
if err != nil {
|
|
return AccountCapabilities{}, fmt.Errorf("read account capabilities: %w", err)
|
|
}
|
|
if resp.StatusCode != http.StatusOK {
|
|
return AccountCapabilities{}, fmt.Errorf("get account capabilities: status %d", resp.StatusCode)
|
|
}
|
|
return parseAccountCapabilities(body)
|
|
}
|
|
|
|
// ClassifyAccount 按订阅字段判定账户类别:
|
|
// - paymentModel 非空且不是 FREE_TRIAL:付费;
|
|
// - paymentModel 为 FREE_TRIAL 时,promotion ACTIVE 或 tier FREE_AND_TRIAL
|
|
// 判为试用;promotion EXPIRED 或 tier FREE 判为免费(试用权益已结束);
|
|
// - 其余情况无法判定。
|
|
func ClassifyAccount(paymentModel, subscriptionTier, promotionStatus string) string {
|
|
pm := strings.ToUpper(strings.TrimSpace(paymentModel))
|
|
tier := strings.ToUpper(strings.TrimSpace(subscriptionTier))
|
|
promo := strings.ToUpper(strings.TrimSpace(promotionStatus))
|
|
if pm != "" && pm != "FREE_TRIAL" {
|
|
return model.AccountTypePaid
|
|
}
|
|
if pm == "FREE_TRIAL" {
|
|
switch {
|
|
case promo == "ACTIVE" || tier == "FREE_AND_TRIAL":
|
|
return model.AccountTypeTrial
|
|
case promo == "EXPIRED" || tier == "FREE":
|
|
return model.AccountTypeFree
|
|
}
|
|
}
|
|
return model.AccountTypeUnknown
|
|
}
|