Skip to content

Commit

Permalink
add cost service swagger (#4474)
Browse files Browse the repository at this point in the history
* fix

* add costs{GetRechargeAmount/GetConsumptionAmount/GetPropertiesUsedAmount} api
  • Loading branch information
bxy4543 authored Jan 10, 2024
1 parent e6bf29b commit e45e3b7
Show file tree
Hide file tree
Showing 12 changed files with 708 additions and 14 deletions.
2 changes: 1 addition & 1 deletion service/account/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ clean:
rm -f $(SERVICE_NAME)

.PHONY: build
build: clean ## Build service-hub binary.
build: ## Build service-hub binary.
CGO_ENABLED=$(CGO_ENABLED) GOOS=$(GOOS) go build $(GO_BUILD_FLAGS) -o bin/manager main.go

.PHONY: docker-build
Expand Down
95 changes: 94 additions & 1 deletion service/account/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,99 @@ func GetProperties(c *gin.Context) {
})
}

// GetConsumptionAmount
// @Summary Get user consumption amount
// @Description Get user consumption amount within a specified time range
// @Tags ConsumptionAmount
// @Accept json
// @Produce json
// @Param request body helper.UserCostsAmountReq true "User consumption amount request"
// @Success 200 {object} map[string]interface{} "successfully retrieved user consumption amount"
// @Failure 400 {object} map[string]interface{} "failed to parse user consumption amount request"
// @Failure 401 {object} map[string]interface{} "authenticate error"
// @Failure 500 {object} map[string]interface{} "failed to get user consumption amount"
// @Router /account/v1alpha1/costs/consumption [post]
func GetConsumptionAmount(c *gin.Context) {
req, err := helper.ParseUserCostsAmountReq(c)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("failed to parse user consumption amount request: %v", err)})
return
}
if err := helper.Authenticate(req.Auth); err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": fmt.Sprintf("authenticate error : %v", err)})
return
}
amount, err := dao.DBClient.GetConsumptionAmount(req.Owner, req.TimeRange.StartTime, req.TimeRange.EndTime)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("failed to get consumption amount : %v", err)})
}
c.JSON(http.StatusOK, gin.H{
"amount": amount,
})
}

// GetRechargeAmount
// @Summary Get user recharge amount
// @Description Get user recharge amount within a specified time range
// @Tags RechargeAmount
// @Accept json
// @Produce json
// @Param request body helper.UserCostsAmountReq true "User recharge amount request"
// @Success 200 {object} map[string]interface{} "successfully retrieved user recharge amount"
// @Failure 400 {object} map[string]interface{} "failed to parse user recharge amount request"
// @Failure 401 {object} map[string]interface{} "authenticate error"
// @Failure 500 {object} map[string]interface{} "failed to get user recharge amount"
// @Router /account/v1alpha1/costs/recharge [post]
func GetRechargeAmount(c *gin.Context) {
req, err := helper.ParseUserCostsAmountReq(c)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("failed to parse user recharge amount request: %v", err)})
return
}
if err := helper.Authenticate(req.Auth); err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": fmt.Sprintf("authenticate error : %v", err)})
return
}
amount, err := dao.DBClient.GetRechargeAmount(req.Owner, req.TimeRange.StartTime, req.TimeRange.EndTime)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("failed to get recharge amount : %v", err)})
}
c.JSON(http.StatusOK, gin.H{
"amount": amount,
})
}

// GetPropertiesUsedAmount
// @Summary Get user properties used amount
// @Description Get user properties used amount within a specified time range
// @Tags PropertiesUsedAmount
// @Accept json
// @Produce json
// @Param request body helper.UserCostsAmountReq true "User properties used amount request"
// @Success 200 {object} map[string]interface{} "successfully retrieved user properties used amount"
// @Failure 400 {object} map[string]interface{} "failed to parse user properties used amount request"
// @Failure 401 {object} map[string]interface{} "authenticate error"
// @Failure 500 {object} map[string]interface{} "failed to get user properties used amount"
// @Router /account/v1alpha1/costs/properties [post]
func GetPropertiesUsedAmount(c *gin.Context) {
req, err := helper.ParseUserCostsAmountReq(c)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("failed to parse user properties used amount request: %v", err)})
return
}
if err := helper.Authenticate(req.Auth); err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": fmt.Sprintf("authenticate error : %v", err)})
return
}
amount, err := dao.DBClient.GetPropertiesUsedAmount(req.Owner, req.TimeRange.StartTime, req.TimeRange.EndTime)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("failed to get properties used amount : %v", err)})
}
c.JSON(http.StatusOK, gin.H{
"amount": amount,
})
}

type CostsResult struct {
Data CostsResultData `json:"data" bson:"data"`
Message string `json:"message" bson:"message"`
Expand Down Expand Up @@ -110,7 +203,7 @@ func GetCosts(c *gin.Context) {
c.JSON(http.StatusUnauthorized, gin.H{"error": fmt.Sprintf("authenticate error : %v", err)})
return
}
costs, err := dao.DBClient.GetCostAmount(req.Auth.Owner, req.TimeRange.StartTime, req.TimeRange.EndTime)
costs, err := dao.DBClient.GetCosts(req.Auth.Owner, req.TimeRange.StartTime, req.TimeRange.EndTime)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("failed to get cost : %v", err)})
}
Expand Down
4 changes: 4 additions & 0 deletions service/account/dao/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,9 @@ var DBClient Interface
func InitDB() error {
var err error
DBClient, err = NewMongoInterface(os.Getenv(helper.EnvMongoURI))
if err != nil {
return err
}
_, err = DBClient.GetProperties()
return err
}
94 changes: 91 additions & 3 deletions service/account/dao/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ import (
type Interface interface {
GetBillingHistoryNamespaceList(req *helper.NamespaceBillingHistoryReq) ([]string, error)
GetProperties() ([]common.PropertyQuery, error)
GetCostAmount(user string, startTime, endTime time.Time) (common.TimeCostsMap, error)
GetCosts(user string, startTime, endTime time.Time) (common.TimeCostsMap, error)
GetConsumptionAmount(user string, startTime, endTime time.Time) (int64, error)
GetRechargeAmount(user string, startTime, endTime time.Time) (int64, error)
GetPropertiesUsedAmount(user string, startTime, endTime time.Time) (map[string]int64, error)
}

type MongoDB struct {
Expand All @@ -41,9 +44,13 @@ func (m *MongoDB) GetProperties() ([]common.PropertyQuery, error) {
m.Properties = properties
}
for _, types := range m.Properties.Types {
price := types.ViewPrice
if price == 0 {
price = types.UnitPrice
}
property := common.PropertyQuery{
Name: types.Name,
UnitPrice: types.ViewPrice,
UnitPrice: price,
Unit: types.UnitString,
Alias: types.Alias,
}
Expand All @@ -52,7 +59,7 @@ func (m *MongoDB) GetProperties() ([]common.PropertyQuery, error) {
return propertiesQuery, nil
}

func (m *MongoDB) GetCostAmount(user string, startTime, endTime time.Time) (common.TimeCostsMap, error) {
func (m *MongoDB) GetCosts(user string, startTime, endTime time.Time) (common.TimeCostsMap, error) {
filter := bson.M{
"type": 0,
"time": bson.M{
Expand Down Expand Up @@ -84,6 +91,87 @@ func (m *MongoDB) GetCostAmount(user string, startTime, endTime time.Time) (comm
return costsMap, nil
}

func (m *MongoDB) GetConsumptionAmount(user string, startTime, endTime time.Time) (int64, error) {
return m.getAmountWithType(0, user, startTime, endTime)
}

func (m *MongoDB) GetRechargeAmount(user string, startTime, endTime time.Time) (int64, error) {
return m.getAmountWithType(1, user, startTime, endTime)
}

func (m *MongoDB) getAmountWithType(_type int64, user string, startTime, endTime time.Time) (int64, error) {
pipeline := bson.A{
bson.D{{Key: "$match", Value: bson.M{
"type": _type,
"time": bson.M{"$gte": startTime, "$lte": endTime},
"owner": user,
}}},
bson.D{{Key: "$group", Value: bson.M{
"_id": nil,
"total": bson.M{"$sum": "$amount"},
}}},
}

cursor, err := m.getBillingCollection().Aggregate(context.Background(), pipeline)
if err != nil {
return 0, fmt.Errorf("failed to aggregate billing collection: %v", err)
}
defer cursor.Close(context.Background())

var result struct {
Total int64 `bson:"total"`
}

if cursor.Next(context.Background()) {
if err := cursor.Decode(&result); err != nil {
return 0, fmt.Errorf("failed to decode result: %v", err)
}
}
return result.Total, nil
}

func (m *MongoDB) GetPropertiesUsedAmount(user string, startTime, endTime time.Time) (map[string]int64, error) {
propertiesUsedAmount := make(map[string]int64)
for _, property := range m.Properties.Types {
amount, err := m.getSumOfUsedAmount(property.Enum, user, startTime, endTime)
if err != nil {
return nil, fmt.Errorf("failed to get sum of used amount: %v", err)
}
propertiesUsedAmount[property.Name] = amount
}
return propertiesUsedAmount, nil
}

func (m *MongoDB) getSumOfUsedAmount(propertyType uint8, user string, startTime, endTime time.Time) (int64, error) {
pipeline := bson.A{
bson.D{{Key: "$match", Value: bson.M{
"time": bson.M{"$gte": startTime, "$lte": endTime},
"owner": user,
"app_costs.used_amount.0": bson.M{"$exists": true},
}}},
bson.D{{Key: "$unwind", Value: "$app_costs"}},
bson.D{{Key: "$group", Value: bson.M{
"_id": nil,
"totalAmount": bson.M{"$sum": "$app_costs.used_amount." + strconv.Itoa(int(propertyType))},
}}},
}
cursor, err := m.getBillingCollection().Aggregate(context.Background(), pipeline)
if err != nil {
return 0, fmt.Errorf("failed to get billing collection: %v", err)
}
defer cursor.Close(context.Background())
var result struct {
TotalAmount int64 `bson:"totalAmount"`
}

if cursor.Next(context.Background()) {
if err := cursor.Decode(&result); err != nil {
return 0, fmt.Errorf("failed to decode result: %v", err)
}
}
return result.TotalAmount, nil
}

func NewMongoInterface(url string) (Interface, error) {
client, err := mongo.Connect(context.Background(), options.Client().ApplyURI(url))
if err != nil {
Expand Down
52 changes: 52 additions & 0 deletions service/account/dao/interface_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package dao

import (
"testing"
"time"

"github.com/labring/sealos/controllers/pkg/resources"
"go.mongodb.org/mongo-driver/mongo"
)

func TestMongoDB_GetRechargeAmount(t *testing.T) {
type fields struct {
Client *mongo.Client
AccountDBName string
BillingConn string
PropertiesConn string
Properties *resources.PropertyTypeLS
}
type args struct {
user string
startTime time.Time
endTime time.Time
}
tests := []struct {
name string
fields fields
args args
want int64
wantErr bool
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
m := &MongoDB{
Client: tt.fields.Client,
AccountDBName: tt.fields.AccountDBName,
BillingConn: tt.fields.BillingConn,
PropertiesConn: tt.fields.PropertiesConn,
Properties: tt.fields.Properties,
}
got, err := m.GetRechargeAmount(tt.args.user, tt.args.startTime, tt.args.endTime)
if (err != nil) != tt.wantErr {
t.Errorf("GetRechargeAmount() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("GetRechargeAmount() got = %v, want %v", got, tt.want)
}
})
}
}
Loading

0 comments on commit e45e3b7

Please sign in to comment.