Skip to content

Commit

Permalink
feat(account-service): add use giftcode (labring#5013)
Browse files Browse the repository at this point in the history
* feat(account-service): add use giftcode

* update swagger doc and complete test
  • Loading branch information
HUAHUAI23 authored and zjy365 committed Sep 3, 2024
1 parent 67eea59 commit c188b28
Show file tree
Hide file tree
Showing 11 changed files with 472 additions and 1 deletion.
45 changes: 45 additions & 0 deletions controllers/pkg/database/cockroach/accountv2.go
Original file line number Diff line number Diff line change
Expand Up @@ -1054,3 +1054,48 @@ func (c *Cockroach) Close() error {
}
return db.Close()
}

func (c *Cockroach) GetGiftCodeWithCode(code string) (*types.GiftCode, error) {
var giftCode types.GiftCode
if err := c.DB.Where(&types.GiftCode{Code: code}).First(&giftCode).Error; err != nil {
return nil, fmt.Errorf("failed to get gift code: %w", err)
}
return &giftCode, nil
}

func (c *Cockroach) UseGiftCode(giftCode *types.GiftCode, userID string) error {
return c.DB.Transaction(func(tx *gorm.DB) error {
ops := &types.UserQueryOpts{ID: userID}
// Update the user's balance
if err := c.updateBalance(tx, ops, giftCode.CreditAmount, false, true); err != nil {
return fmt.Errorf("failed to update user balance: %w", err)
}

message := "created by use gift code"
// Create an AccountTransaction record
accountTransaction := &types.AccountTransaction{
ID: uuid.New(),
Type: "GiftCode",
UserUID: ops.UID,
DeductionBalance: 0,
Balance: giftCode.CreditAmount,
Message: &message,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
BillingID: giftCode.ID,
}
if err := tx.Create(accountTransaction).Error; err != nil {
return fmt.Errorf("failed to create account transaction: %w", err)
}

// Mark the gift code as used
giftCode.Used = true
giftCode.UsedBy = ops.UID
giftCode.UsedAt = time.Now()
if err := tx.Save(giftCode).Error; err != nil {
return fmt.Errorf("failed to update gift code: %w", err)
}

return nil
})
}
32 changes: 32 additions & 0 deletions controllers/pkg/types/global.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,3 +280,35 @@ func (Invoice) TableName() string {
func (InvoicePayment) TableName() string {
return "InvoicePayment"
}

type GiftCode struct {
ID uuid.UUID `gorm:"column:id;type:uuid;default:gen_random_uuid();primary_key"`
Code string `gorm:"column:code;type:text;not null;unique"`
CreditAmount int64 `gorm:"column:creditAmount;type:bigint;default:0;not null"`
Used bool `gorm:"column:used;type:boolean;default:false;not null"`
UsedBy uuid.UUID `gorm:"column:usedBy;type:uuid"`
UsedAt time.Time `gorm:"column:usedAt;type:timestamp(3) with time zone"`
CreatedAt time.Time `gorm:"column:createdAt;type:timestamp(3) with time zone;default:current_timestamp()"`
ExpiredAt time.Time `gorm:"column:expiredAt;type:timestamp(3) with time zone"`
Comment string `gorm:"column:comment;type:text"`
}

func (GiftCode) TableName() string {
return "GiftCode"
}

type AccountTransaction struct {
ID uuid.UUID `gorm:"column:id;type:uuid;default:gen_random_uuid();primary_key"`
Type string `gorm:"column:type;type:text"`
UserUID uuid.UUID `gorm:"column:userUid;type:uuid"`
DeductionBalance int64 `gorm:"column:deduction_balance;type:bigint"`
Balance int64 `gorm:"column:balance;type:bigint"`
Message *string `gorm:"column:message;type:text"`
CreatedAt time.Time `gorm:"column:created_at;type:timestamp(3) with time zone;default:current_timestamp()"`
UpdatedAt time.Time `gorm:"column:updated_at;type:timestamp(3) with time zone;default:current_timestamp()"`
BillingID uuid.UUID `gorm:"column:billing_id;type:uuid"`
}

func (AccountTransaction) TableName() string {
return "AccountTransaction"
}
41 changes: 41 additions & 0 deletions service/account/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -874,3 +874,44 @@ func GetInvoicePayment(c *gin.Context) {
"data": payments,
})
}

// UseGiftCode
// @Summary Use a gift code
// @Description Redeem a gift code and apply the credit to the user's account
// @Tags UseGiftCode
// @Accept json
// @Produce json
// @Param request body helper.UseGiftCodeReq true "Use gift code request"
// @Success 200 {object} helper.UseGiftCodeResp "Successfully redeemed gift code"
// @Failure 400 {object} helper.ErrorMessage "Failed to parse use gift code request"
// @Failure 401 {object} helper.ErrorMessage "Authentication error"
// @Failure 500 {object} helper.ErrorMessage "Failed to redeem gift code"
// @Router /account/v1alpha1/gift-code/use [post]
func UseGiftCode(c *gin.Context) {
// Parse the use gift code request
req, err := helper.ParseUseGiftCodeReq(c)
if err != nil {
c.JSON(http.StatusBadRequest, helper.ErrorMessage{Error: fmt.Sprintf("failed to parse use gift code request: %v", err)})
return
}

// Check authentication
if err := CheckAuthAndCalibrate(req.Auth); err != nil {
c.JSON(http.StatusUnauthorized, helper.ErrorMessage{Error: fmt.Sprintf("authenticate error: %v", err)})
return
}

// Use the gift code (to be implemented)
if _, err := dao.DBClient.UseGiftCode(req); err != nil {
c.JSON(http.StatusInternalServerError, helper.ErrorMessage{Error: fmt.Sprintf("failed to use gift code: %v", err)})
return
}

// Return success response
c.JSON(http.StatusOK, helper.UseGiftCodeResp{
Data: helper.UseGiftCodeRespData{
UserID: req.UserID,
},
Message: "Gift code successfully redeemed",
})
}
22 changes: 22 additions & 0 deletions service/account/dao/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ type Interface interface {
GetUserCrName(ops types.UserQueryOpts) (string, error)
GetRegions() ([]types.Region, error)
GetLocalRegion() types.Region
UseGiftCode(req *helper.UseGiftCodeReq) (*types.GiftCode, error)
}

type Account struct {
Expand Down Expand Up @@ -1358,3 +1359,24 @@ func (m *Account) GetInvoicePayments(invoiceID string) ([]types.Payment, error)
func (m *Account) SetStatusInvoice(req *helper.SetInvoiceStatusReq) error {
return m.ck.SetInvoiceStatus(req.InvoiceIDList, req.Status)
}

func (m *Account) UseGiftCode(req *helper.UseGiftCodeReq) (*types.GiftCode, error) {
giftCode, err := m.ck.GetGiftCodeWithCode(req.Code)
if err != nil {
return nil, fmt.Errorf("failed to get gift code: %v", err)
}

if !giftCode.ExpiredAt.IsZero() && time.Now().After(giftCode.ExpiredAt) {
return nil, fmt.Errorf("gift code has expired")
}

if giftCode.Used {
return nil, fmt.Errorf("gift code is already used")
}

if err = m.ck.UseGiftCode(giftCode, req.UserID); err != nil {
return nil, fmt.Errorf("failed to use gift code: %v", err)
}

return giftCode, nil
}
21 changes: 21 additions & 0 deletions service/account/dao/interface_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,27 @@ func TestAccount_SetStatusInvoice(t *testing.T) {
}
}

func TestAccount_UseGiftCode(t *testing.T) {
db, err := newAccountForTest("", os.Getenv("GLOBAL_COCKROACH_URI"), os.Getenv("LOCAL_COCKROACH_URI"))
if err != nil {
t.Fatalf("NewAccountInterface() error = %v", err)
return
}

giftcode, err := db.UseGiftCode(&helper.UseGiftCodeReq{
Code: "DfxAffaeEf",
Auth: &helper.Auth{
UserID: "E1xAJ0fy4k",
},
})

if err != nil {
t.Fatalf("UseGiftCode() error = %v", err)
return
}
t.Logf("giftcode = %+v", giftcode)
}

func init() {
// set env
os.Setenv("MONGO_URI", "")
Expand Down
101 changes: 101 additions & 0 deletions service/account/docs/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,58 @@ const docTemplate = `{
}
}
},
"/account/v1alpha1/gift-code/use": {
"post": {
"description": "Redeem a gift code and apply the credit to the user's account",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"UseGiftCode"
],
"summary": "Use a gift code",
"parameters": [
{
"description": "Use gift code request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/helper.UseGiftCodeReq"
}
}
],
"responses": {
"200": {
"description": "Successfully redeemed gift code",
"schema": {
"$ref": "#/definitions/helper.UseGiftCodeResp"
}
},
"400": {
"description": "Failed to parse use gift code request",
"schema": {
"$ref": "#/definitions/helper.ErrorMessage"
}
},
"401": {
"description": "Authentication error",
"schema": {
"$ref": "#/definitions/helper.ErrorMessage"
}
},
"500": {
"description": "Failed to redeem gift code",
"schema": {
"$ref": "#/definitions/helper.ErrorMessage"
}
}
}
}
},
"/account/v1alpha1/invoice/apply": {
"post": {
"description": "Apply invoice",
Expand Down Expand Up @@ -1858,6 +1910,55 @@ const docTemplate = `{
}
}
},
"helper.UseGiftCodeReq": {
"type": "object",
"required": [
"code"
],
"properties": {
"code": {
"description": "@Summary Gift code to be used\n@Description The code of the gift card to be redeemed\n@JSONSchema required",
"type": "string",
"example": "HAPPY2024"
},
"kubeConfig": {
"type": "string"
},
"owner": {
"type": "string",
"example": "admin"
},
"token": {
"type": "string",
"example": "token"
},
"userID": {
"type": "string",
"example": "admin"
}
}
},
"helper.UseGiftCodeResp": {
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/helper.UseGiftCodeRespData"
},
"message": {
"type": "string",
"example": "Gift code successfully redeemed"
}
}
},
"helper.UseGiftCodeRespData": {
"type": "object",
"properties": {
"userID": {
"type": "string",
"example": "user-123"
}
}
},
"helper.UserBaseReq": {
"type": "object",
"properties": {
Expand Down
Loading

0 comments on commit c188b28

Please sign in to comment.