feat: Redemption Code
This commit is contained in:
parent
5beff61e91
commit
518595b058
86
apis/admin/redemption.api
Normal file
86
apis/admin/redemption.api
Normal file
@ -0,0 +1,86 @@
|
||||
syntax = "v1"
|
||||
|
||||
info (
|
||||
title: "redemption API"
|
||||
desc: "API for redemption code management"
|
||||
author: "Tension"
|
||||
email: "tension@ppanel.com"
|
||||
version: "0.0.1"
|
||||
)
|
||||
|
||||
import "../types.api"
|
||||
|
||||
type (
|
||||
CreateRedemptionCodeRequest {
|
||||
TotalCount int64 `json:"total_count" validate:"required"`
|
||||
SubscribePlan int64 `json:"subscribe_plan" validate:"required"`
|
||||
UnitTime string `json:"unit_time" validate:"required,oneof=day month quarter half_year year"`
|
||||
Quantity int64 `json:"quantity" validate:"required"`
|
||||
BatchCount int64 `json:"batch_count" validate:"required,min=1"`
|
||||
}
|
||||
UpdateRedemptionCodeRequest {
|
||||
Id int64 `json:"id" validate:"required"`
|
||||
TotalCount int64 `json:"total_count,omitempty"`
|
||||
SubscribePlan int64 `json:"subscribe_plan,omitempty"`
|
||||
UnitTime string `json:"unit_time,omitempty" validate:"omitempty,oneof=day month quarter half_year year"`
|
||||
Quantity int64 `json:"quantity,omitempty"`
|
||||
}
|
||||
DeleteRedemptionCodeRequest {
|
||||
Id int64 `json:"id" validate:"required"`
|
||||
}
|
||||
BatchDeleteRedemptionCodeRequest {
|
||||
Ids []int64 `json:"ids" validate:"required"`
|
||||
}
|
||||
GetRedemptionCodeListRequest {
|
||||
Page int64 `form:"page" validate:"required"`
|
||||
Size int64 `form:"size" validate:"required"`
|
||||
SubscribePlan int64 `form:"subscribe_plan,omitempty"`
|
||||
UnitTime string `form:"unit_time,omitempty"`
|
||||
Code string `form:"code,omitempty"`
|
||||
}
|
||||
GetRedemptionCodeListResponse {
|
||||
Total int64 `json:"total"`
|
||||
List []RedemptionCode `json:"list"`
|
||||
}
|
||||
GetRedemptionRecordListRequest {
|
||||
Page int64 `form:"page" validate:"required"`
|
||||
Size int64 `form:"size" validate:"required"`
|
||||
UserId int64 `form:"user_id,omitempty"`
|
||||
CodeId int64 `form:"code_id,omitempty"`
|
||||
}
|
||||
GetRedemptionRecordListResponse {
|
||||
Total int64 `json:"total"`
|
||||
List []RedemptionRecord `json:"list"`
|
||||
}
|
||||
)
|
||||
|
||||
@server (
|
||||
prefix: v1/admin/redemption
|
||||
group: admin/redemption
|
||||
middleware: AuthMiddleware
|
||||
)
|
||||
service ppanel {
|
||||
@doc "Create redemption code"
|
||||
@handler CreateRedemptionCode
|
||||
post /code (CreateRedemptionCodeRequest)
|
||||
|
||||
@doc "Update redemption code"
|
||||
@handler UpdateRedemptionCode
|
||||
put /code (UpdateRedemptionCodeRequest)
|
||||
|
||||
@doc "Delete redemption code"
|
||||
@handler DeleteRedemptionCode
|
||||
delete /code (DeleteRedemptionCodeRequest)
|
||||
|
||||
@doc "Batch delete redemption code"
|
||||
@handler BatchDeleteRedemptionCode
|
||||
delete /code/batch (BatchDeleteRedemptionCodeRequest)
|
||||
|
||||
@doc "Get redemption code list"
|
||||
@handler GetRedemptionCodeList
|
||||
get /code/list (GetRedemptionCodeListRequest) returns (GetRedemptionCodeListResponse)
|
||||
|
||||
@doc "Get redemption record list"
|
||||
@handler GetRedemptionRecordList
|
||||
get /record/list (GetRedemptionRecordListRequest) returns (GetRedemptionRecordListResponse)
|
||||
}
|
||||
32
apis/public/redemption.api
Normal file
32
apis/public/redemption.api
Normal file
@ -0,0 +1,32 @@
|
||||
syntax = "v1"
|
||||
|
||||
info (
|
||||
title: "redemption API"
|
||||
desc: "API for redemption"
|
||||
author: "Tension"
|
||||
email: "tension@ppanel.com"
|
||||
version: "0.0.1"
|
||||
)
|
||||
|
||||
import "../types.api"
|
||||
|
||||
type (
|
||||
RedeemCodeRequest {
|
||||
Code string `json:"code" validate:"required"`
|
||||
}
|
||||
RedeemCodeResponse {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
)
|
||||
|
||||
@server (
|
||||
prefix: v1/public/redemption
|
||||
group: public/redemption
|
||||
jwt: JwtAuth
|
||||
middleware: AuthMiddleware,DeviceMiddleware
|
||||
)
|
||||
service ppanel {
|
||||
@doc "Redeem code"
|
||||
@handler RedeemCode
|
||||
post / (RedeemCodeRequest) returns (RedeemCodeResponse)
|
||||
}
|
||||
@ -448,6 +448,27 @@ type (
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at"`
|
||||
}
|
||||
RedemptionCode {
|
||||
Id int64 `json:"id"`
|
||||
Code string `json:"code"`
|
||||
TotalCount int64 `json:"total_count"`
|
||||
UsedCount int64 `json:"used_count"`
|
||||
SubscribePlan int64 `json:"subscribe_plan"`
|
||||
UnitTime string `json:"unit_time"`
|
||||
Quantity int64 `json:"quantity"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at"`
|
||||
}
|
||||
RedemptionRecord {
|
||||
Id int64 `json:"id"`
|
||||
RedemptionCodeId int64 `json:"redemption_code_id"`
|
||||
UserId int64 `json:"user_id"`
|
||||
SubscribeId int64 `json:"subscribe_id"`
|
||||
UnitTime string `json:"unit_time"`
|
||||
Quantity int64 `json:"quantity"`
|
||||
RedeemedAt int64 `json:"redeemed_at"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
}
|
||||
Announcement {
|
||||
Id int64 `json:"id"`
|
||||
Title string `json:"title"`
|
||||
|
||||
5
initialize/migrate/database/02126_redemption.down.sql
Normal file
5
initialize/migrate/database/02126_redemption.down.sql
Normal file
@ -0,0 +1,5 @@
|
||||
-- Drop redemption_record table
|
||||
DROP TABLE IF EXISTS `redemption_record`;
|
||||
|
||||
-- Drop redemption_code table
|
||||
DROP TABLE IF EXISTS `redemption_code`;
|
||||
31
initialize/migrate/database/02126_redemption.up.sql
Normal file
31
initialize/migrate/database/02126_redemption.up.sql
Normal file
@ -0,0 +1,31 @@
|
||||
-- Create redemption_code table
|
||||
CREATE TABLE IF NOT EXISTS `redemption_code` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT 'Primary Key',
|
||||
`code` VARCHAR(255) NOT NULL COMMENT 'Redemption Code',
|
||||
`total_count` BIGINT NOT NULL DEFAULT 0 COMMENT 'Total Redemption Count',
|
||||
`used_count` BIGINT NOT NULL DEFAULT 0 COMMENT 'Used Redemption Count',
|
||||
`subscribe_plan` BIGINT NOT NULL DEFAULT 0 COMMENT 'Subscribe Plan',
|
||||
`unit_time` VARCHAR(50) NOT NULL DEFAULT 'month' COMMENT 'Unit Time: day, month, quarter, half_year, year',
|
||||
`quantity` BIGINT NOT NULL DEFAULT 1 COMMENT 'Quantity',
|
||||
`created_at` DATETIME NOT NULL COMMENT 'Creation Time',
|
||||
`updated_at` DATETIME NOT NULL COMMENT 'Update Time',
|
||||
`deleted_at` DATETIME DEFAULT NULL COMMENT 'Deletion Time',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_code` (`code`),
|
||||
KEY `idx_deleted_at` (`deleted_at`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Redemption Code Table';
|
||||
|
||||
-- Create redemption_record table
|
||||
CREATE TABLE IF NOT EXISTS `redemption_record` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT 'Primary Key',
|
||||
`redemption_code_id` BIGINT NOT NULL DEFAULT 0 COMMENT 'Redemption Code Id',
|
||||
`user_id` BIGINT NOT NULL DEFAULT 0 COMMENT 'User Id',
|
||||
`subscribe_id` BIGINT NOT NULL DEFAULT 0 COMMENT 'Subscribe Id',
|
||||
`unit_time` VARCHAR(50) NOT NULL DEFAULT 'month' COMMENT 'Unit Time',
|
||||
`quantity` BIGINT NOT NULL DEFAULT 1 COMMENT 'Quantity',
|
||||
`redeemed_at` DATETIME NOT NULL COMMENT 'Redeemed Time',
|
||||
`created_at` DATETIME NOT NULL COMMENT 'Creation Time',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_redemption_code_id` (`redemption_code_id`),
|
||||
KEY `idx_user_id` (`user_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Redemption Record Table';
|
||||
@ -0,0 +1,26 @@
|
||||
package redemption
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/perfect-panel/server/internal/logic/admin/redemption"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/result"
|
||||
)
|
||||
|
||||
// Batch delete redemption code
|
||||
func BatchDeleteRedemptionCodeHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
|
||||
return func(c *gin.Context) {
|
||||
var req types.BatchDeleteRedemptionCodeRequest
|
||||
_ = c.ShouldBind(&req)
|
||||
validateErr := svcCtx.Validate(&req)
|
||||
if validateErr != nil {
|
||||
result.ParamErrorResult(c, validateErr)
|
||||
return
|
||||
}
|
||||
|
||||
l := redemption.NewBatchDeleteRedemptionCodeLogic(c.Request.Context(), svcCtx)
|
||||
err := l.BatchDeleteRedemptionCode(&req)
|
||||
result.HttpResult(c, nil, err)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
package redemption
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/perfect-panel/server/internal/logic/admin/redemption"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/result"
|
||||
)
|
||||
|
||||
// Create redemption code
|
||||
func CreateRedemptionCodeHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
|
||||
return func(c *gin.Context) {
|
||||
var req types.CreateRedemptionCodeRequest
|
||||
_ = c.ShouldBind(&req)
|
||||
validateErr := svcCtx.Validate(&req)
|
||||
if validateErr != nil {
|
||||
result.ParamErrorResult(c, validateErr)
|
||||
return
|
||||
}
|
||||
|
||||
l := redemption.NewCreateRedemptionCodeLogic(c.Request.Context(), svcCtx)
|
||||
err := l.CreateRedemptionCode(&req)
|
||||
result.HttpResult(c, nil, err)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
package redemption
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/perfect-panel/server/internal/logic/admin/redemption"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/result"
|
||||
)
|
||||
|
||||
// Delete redemption code
|
||||
func DeleteRedemptionCodeHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
|
||||
return func(c *gin.Context) {
|
||||
var req types.DeleteRedemptionCodeRequest
|
||||
_ = c.ShouldBind(&req)
|
||||
validateErr := svcCtx.Validate(&req)
|
||||
if validateErr != nil {
|
||||
result.ParamErrorResult(c, validateErr)
|
||||
return
|
||||
}
|
||||
|
||||
l := redemption.NewDeleteRedemptionCodeLogic(c.Request.Context(), svcCtx)
|
||||
err := l.DeleteRedemptionCode(&req)
|
||||
result.HttpResult(c, nil, err)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
package redemption
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/perfect-panel/server/internal/logic/admin/redemption"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/result"
|
||||
)
|
||||
|
||||
// Get redemption code list
|
||||
func GetRedemptionCodeListHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
|
||||
return func(c *gin.Context) {
|
||||
var req types.GetRedemptionCodeListRequest
|
||||
_ = c.ShouldBind(&req)
|
||||
validateErr := svcCtx.Validate(&req)
|
||||
if validateErr != nil {
|
||||
result.ParamErrorResult(c, validateErr)
|
||||
return
|
||||
}
|
||||
|
||||
l := redemption.NewGetRedemptionCodeListLogic(c.Request.Context(), svcCtx)
|
||||
resp, err := l.GetRedemptionCodeList(&req)
|
||||
result.HttpResult(c, resp, err)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
package redemption
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/perfect-panel/server/internal/logic/admin/redemption"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/result"
|
||||
)
|
||||
|
||||
// Get redemption record list
|
||||
func GetRedemptionRecordListHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
|
||||
return func(c *gin.Context) {
|
||||
var req types.GetRedemptionRecordListRequest
|
||||
_ = c.ShouldBind(&req)
|
||||
validateErr := svcCtx.Validate(&req)
|
||||
if validateErr != nil {
|
||||
result.ParamErrorResult(c, validateErr)
|
||||
return
|
||||
}
|
||||
|
||||
l := redemption.NewGetRedemptionRecordListLogic(c.Request.Context(), svcCtx)
|
||||
resp, err := l.GetRedemptionRecordList(&req)
|
||||
result.HttpResult(c, resp, err)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
package redemption
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/perfect-panel/server/internal/logic/admin/redemption"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/result"
|
||||
)
|
||||
|
||||
// Update redemption code
|
||||
func UpdateRedemptionCodeHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
|
||||
return func(c *gin.Context) {
|
||||
var req types.UpdateRedemptionCodeRequest
|
||||
_ = c.ShouldBind(&req)
|
||||
validateErr := svcCtx.Validate(&req)
|
||||
if validateErr != nil {
|
||||
result.ParamErrorResult(c, validateErr)
|
||||
return
|
||||
}
|
||||
|
||||
l := redemption.NewUpdateRedemptionCodeLogic(c.Request.Context(), svcCtx)
|
||||
err := l.UpdateRedemptionCode(&req)
|
||||
result.HttpResult(c, nil, err)
|
||||
}
|
||||
}
|
||||
26
internal/handler/public/redemption/redeemCodeHandler.go
Normal file
26
internal/handler/public/redemption/redeemCodeHandler.go
Normal file
@ -0,0 +1,26 @@
|
||||
package redemption
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/perfect-panel/server/internal/logic/public/redemption"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/result"
|
||||
)
|
||||
|
||||
// Redeem code
|
||||
func RedeemCodeHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
|
||||
return func(c *gin.Context) {
|
||||
var req types.RedeemCodeRequest
|
||||
_ = c.ShouldBind(&req)
|
||||
validateErr := svcCtx.Validate(&req)
|
||||
if validateErr != nil {
|
||||
result.ParamErrorResult(c, validateErr)
|
||||
return
|
||||
}
|
||||
|
||||
l := redemption.NewRedeemCodeLogic(c.Request.Context(), svcCtx)
|
||||
resp, err := l.RedeemCode(&req)
|
||||
result.HttpResult(c, resp, err)
|
||||
}
|
||||
}
|
||||
@ -16,6 +16,7 @@ import (
|
||||
adminMarketing "github.com/perfect-panel/server/internal/handler/admin/marketing"
|
||||
adminOrder "github.com/perfect-panel/server/internal/handler/admin/order"
|
||||
adminPayment "github.com/perfect-panel/server/internal/handler/admin/payment"
|
||||
adminRedemption "github.com/perfect-panel/server/internal/handler/admin/redemption"
|
||||
adminServer "github.com/perfect-panel/server/internal/handler/admin/server"
|
||||
adminSubscribe "github.com/perfect-panel/server/internal/handler/admin/subscribe"
|
||||
adminSystem "github.com/perfect-panel/server/internal/handler/admin/system"
|
||||
@ -30,6 +31,7 @@ import (
|
||||
publicOrder "github.com/perfect-panel/server/internal/handler/public/order"
|
||||
publicPayment "github.com/perfect-panel/server/internal/handler/public/payment"
|
||||
publicPortal "github.com/perfect-panel/server/internal/handler/public/portal"
|
||||
publicRedemption "github.com/perfect-panel/server/internal/handler/public/redemption"
|
||||
publicSubscribe "github.com/perfect-panel/server/internal/handler/public/subscribe"
|
||||
publicTicket "github.com/perfect-panel/server/internal/handler/public/ticket"
|
||||
publicUser "github.com/perfect-panel/server/internal/handler/public/user"
|
||||
@ -298,6 +300,29 @@ func RegisterHandlers(router *gin.Engine, serverCtx *svc.ServiceContext) {
|
||||
adminPaymentGroupRouter.GET("/platform", adminPayment.GetPaymentPlatformHandler(serverCtx))
|
||||
}
|
||||
|
||||
adminRedemptionGroupRouter := router.Group("/v1/admin/redemption")
|
||||
adminRedemptionGroupRouter.Use(middleware.AuthMiddleware(serverCtx))
|
||||
|
||||
{
|
||||
// Create redemption code
|
||||
adminRedemptionGroupRouter.POST("/code", adminRedemption.CreateRedemptionCodeHandler(serverCtx))
|
||||
|
||||
// Update redemption code
|
||||
adminRedemptionGroupRouter.PUT("/code", adminRedemption.UpdateRedemptionCodeHandler(serverCtx))
|
||||
|
||||
// Delete redemption code
|
||||
adminRedemptionGroupRouter.DELETE("/code", adminRedemption.DeleteRedemptionCodeHandler(serverCtx))
|
||||
|
||||
// Batch delete redemption code
|
||||
adminRedemptionGroupRouter.DELETE("/code/batch", adminRedemption.BatchDeleteRedemptionCodeHandler(serverCtx))
|
||||
|
||||
// Get redemption code list
|
||||
adminRedemptionGroupRouter.GET("/code/list", adminRedemption.GetRedemptionCodeListHandler(serverCtx))
|
||||
|
||||
// Get redemption record list
|
||||
adminRedemptionGroupRouter.GET("/record/list", adminRedemption.GetRedemptionRecordListHandler(serverCtx))
|
||||
}
|
||||
|
||||
adminServerGroupRouter := router.Group("/v1/admin/server")
|
||||
adminServerGroupRouter.Use(middleware.AuthMiddleware(serverCtx))
|
||||
|
||||
@ -748,6 +773,14 @@ func RegisterHandlers(router *gin.Engine, serverCtx *svc.ServiceContext) {
|
||||
publicPortalGroupRouter.GET("/subscribe", publicPortal.GetSubscriptionHandler(serverCtx))
|
||||
}
|
||||
|
||||
publicRedemptionGroupRouter := router.Group("/v1/public/redemption")
|
||||
publicRedemptionGroupRouter.Use(middleware.AuthMiddleware(serverCtx), middleware.DeviceMiddleware(serverCtx))
|
||||
|
||||
{
|
||||
// Redeem code
|
||||
publicRedemptionGroupRouter.POST("/", publicRedemption.RedeemCodeHandler(serverCtx))
|
||||
}
|
||||
|
||||
publicSubscribeGroupRouter := router.Group("/v1/public/subscribe")
|
||||
publicSubscribeGroupRouter.Use(middleware.AuthMiddleware(serverCtx), middleware.DeviceMiddleware(serverCtx))
|
||||
|
||||
|
||||
@ -0,0 +1,36 @@
|
||||
package redemption
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
"github.com/perfect-panel/server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type BatchDeleteRedemptionCodeLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// Batch delete redemption code
|
||||
func NewBatchDeleteRedemptionCodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *BatchDeleteRedemptionCodeLogic {
|
||||
return &BatchDeleteRedemptionCodeLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *BatchDeleteRedemptionCodeLogic) BatchDeleteRedemptionCode(req *types.BatchDeleteRedemptionCodeRequest) error {
|
||||
err := l.svcCtx.RedemptionCodeModel.BatchDelete(l.ctx, req.Ids)
|
||||
if err != nil {
|
||||
l.Errorw("[BatchDeleteRedemptionCode] Database Error", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseDeletedError), "batch delete redemption code error: %v", err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
119
internal/logic/admin/redemption/createRedemptionCodeLogic.go
Normal file
119
internal/logic/admin/redemption/createRedemptionCodeLogic.go
Normal file
@ -0,0 +1,119 @@
|
||||
package redemption
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"math/big"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/redemption"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
"github.com/perfect-panel/server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type CreateRedemptionCodeLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// Create redemption code
|
||||
func NewCreateRedemptionCodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateRedemptionCodeLogic {
|
||||
return &CreateRedemptionCodeLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
// generateUniqueCode generates a unique redemption code
|
||||
func (l *CreateRedemptionCodeLogic) generateUniqueCode() (string, error) {
|
||||
const charset = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789" // Removed confusing characters like I, O, 0, 1
|
||||
const codeLength = 16
|
||||
|
||||
maxRetries := 10
|
||||
for i := 0; i < maxRetries; i++ {
|
||||
code := make([]byte, codeLength)
|
||||
for j := 0; j < codeLength; j++ {
|
||||
num, err := rand.Int(rand.Reader, big.NewInt(int64(len(charset))))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
code[j] = charset[num.Int64()]
|
||||
}
|
||||
|
||||
codeStr := string(code)
|
||||
|
||||
// Check if code already exists
|
||||
_, err := l.svcCtx.RedemptionCodeModel.FindOneByCode(l.ctx, codeStr)
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return codeStr, nil
|
||||
} else if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// Code exists, try again
|
||||
}
|
||||
|
||||
return "", errors.New("failed to generate unique code after maximum retries")
|
||||
}
|
||||
|
||||
func (l *CreateRedemptionCodeLogic) CreateRedemptionCode(req *types.CreateRedemptionCodeRequest) error {
|
||||
// Check if subscribe plan is valid
|
||||
if req.SubscribePlan == 0 {
|
||||
l.Errorw("[CreateRedemptionCode] Subscribe plan cannot be empty")
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.InvalidParams), "subscribe plan cannot be empty")
|
||||
}
|
||||
|
||||
// Verify subscribe plan exists
|
||||
_, err := l.svcCtx.SubscribeModel.FindOne(l.ctx, req.SubscribePlan)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
l.Errorw("[CreateRedemptionCode] Subscribe plan not found", logger.Field("subscribe_plan", req.SubscribePlan))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.InvalidParams), "subscribe plan not found")
|
||||
}
|
||||
l.Errorw("[CreateRedemptionCode] Database Error", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find subscribe plan error: %v", err.Error())
|
||||
}
|
||||
|
||||
// Validate batch count
|
||||
if req.BatchCount < 1 {
|
||||
l.Errorw("[CreateRedemptionCode] Batch count must be at least 1")
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.InvalidParams), "batch count must be at least 1")
|
||||
}
|
||||
|
||||
// Generate redemption codes in batch
|
||||
var createdCodes []string
|
||||
for i := int64(0); i < req.BatchCount; i++ {
|
||||
code, err := l.generateUniqueCode()
|
||||
if err != nil {
|
||||
l.Errorw("[CreateRedemptionCode] Failed to generate unique code", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "generate unique code error: %v", err.Error())
|
||||
}
|
||||
|
||||
redemptionCode := &redemption.RedemptionCode{
|
||||
Code: code,
|
||||
TotalCount: req.TotalCount,
|
||||
UsedCount: 0,
|
||||
SubscribePlan: req.SubscribePlan,
|
||||
UnitTime: req.UnitTime,
|
||||
Quantity: req.Quantity,
|
||||
}
|
||||
|
||||
err = l.svcCtx.RedemptionCodeModel.Insert(l.ctx, redemptionCode)
|
||||
if err != nil {
|
||||
l.Errorw("[CreateRedemptionCode] Database Error", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "create redemption code error: %v", err.Error())
|
||||
}
|
||||
|
||||
createdCodes = append(createdCodes, code)
|
||||
}
|
||||
|
||||
l.Infow("[CreateRedemptionCode] Successfully created redemption codes",
|
||||
logger.Field("count", len(createdCodes)),
|
||||
logger.Field("codes", createdCodes))
|
||||
|
||||
return nil
|
||||
}
|
||||
36
internal/logic/admin/redemption/deleteRedemptionCodeLogic.go
Normal file
36
internal/logic/admin/redemption/deleteRedemptionCodeLogic.go
Normal file
@ -0,0 +1,36 @@
|
||||
package redemption
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
"github.com/perfect-panel/server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type DeleteRedemptionCodeLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// Delete redemption code
|
||||
func NewDeleteRedemptionCodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteRedemptionCodeLogic {
|
||||
return &DeleteRedemptionCodeLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *DeleteRedemptionCodeLogic) DeleteRedemptionCode(req *types.DeleteRedemptionCodeRequest) error {
|
||||
err := l.svcCtx.RedemptionCodeModel.Delete(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
l.Errorw("[DeleteRedemptionCode] Database Error", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseDeletedError), "delete redemption code error: %v", err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
package redemption
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
"github.com/perfect-panel/server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type GetRedemptionCodeListLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// Get redemption code list
|
||||
func NewGetRedemptionCodeListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetRedemptionCodeListLogic {
|
||||
return &GetRedemptionCodeListLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetRedemptionCodeListLogic) GetRedemptionCodeList(req *types.GetRedemptionCodeListRequest) (resp *types.GetRedemptionCodeListResponse, err error) {
|
||||
total, list, err := l.svcCtx.RedemptionCodeModel.QueryRedemptionCodeListByPage(
|
||||
l.ctx,
|
||||
int(req.Page),
|
||||
int(req.Size),
|
||||
req.SubscribePlan,
|
||||
req.UnitTime,
|
||||
req.Code,
|
||||
)
|
||||
if err != nil {
|
||||
l.Errorw("[GetRedemptionCodeList] Database Error", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "get redemption code list error: %v", err.Error())
|
||||
}
|
||||
|
||||
var redemptionCodes []types.RedemptionCode
|
||||
for _, item := range list {
|
||||
redemptionCodes = append(redemptionCodes, types.RedemptionCode{
|
||||
Id: item.Id,
|
||||
Code: item.Code,
|
||||
TotalCount: item.TotalCount,
|
||||
UsedCount: item.UsedCount,
|
||||
SubscribePlan: item.SubscribePlan,
|
||||
UnitTime: item.UnitTime,
|
||||
Quantity: item.Quantity,
|
||||
CreatedAt: item.CreatedAt.Unix(),
|
||||
UpdatedAt: item.UpdatedAt.Unix(),
|
||||
})
|
||||
}
|
||||
|
||||
return &types.GetRedemptionCodeListResponse{
|
||||
Total: total,
|
||||
List: redemptionCodes,
|
||||
}, nil
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
package redemption
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
"github.com/perfect-panel/server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type GetRedemptionRecordListLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// Get redemption record list
|
||||
func NewGetRedemptionRecordListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetRedemptionRecordListLogic {
|
||||
return &GetRedemptionRecordListLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetRedemptionRecordListLogic) GetRedemptionRecordList(req *types.GetRedemptionRecordListRequest) (resp *types.GetRedemptionRecordListResponse, err error) {
|
||||
total, list, err := l.svcCtx.RedemptionRecordModel.QueryRedemptionRecordListByPage(
|
||||
l.ctx,
|
||||
int(req.Page),
|
||||
int(req.Size),
|
||||
req.UserId,
|
||||
req.CodeId,
|
||||
)
|
||||
if err != nil {
|
||||
l.Errorw("[GetRedemptionRecordList] Database Error", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "get redemption record list error: %v", err.Error())
|
||||
}
|
||||
|
||||
var redemptionRecords []types.RedemptionRecord
|
||||
for _, item := range list {
|
||||
redemptionRecords = append(redemptionRecords, types.RedemptionRecord{
|
||||
Id: item.Id,
|
||||
RedemptionCodeId: item.RedemptionCodeId,
|
||||
UserId: item.UserId,
|
||||
SubscribeId: item.SubscribeId,
|
||||
UnitTime: item.UnitTime,
|
||||
Quantity: item.Quantity,
|
||||
RedeemedAt: item.RedeemedAt.Unix(),
|
||||
CreatedAt: item.CreatedAt.Unix(),
|
||||
})
|
||||
}
|
||||
|
||||
return &types.GetRedemptionRecordListResponse{
|
||||
Total: total,
|
||||
List: redemptionRecords,
|
||||
}, nil
|
||||
}
|
||||
65
internal/logic/admin/redemption/updateRedemptionCodeLogic.go
Normal file
65
internal/logic/admin/redemption/updateRedemptionCodeLogic.go
Normal file
@ -0,0 +1,65 @@
|
||||
package redemption
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
"github.com/perfect-panel/server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type UpdateRedemptionCodeLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// Update redemption code
|
||||
func NewUpdateRedemptionCodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateRedemptionCodeLogic {
|
||||
return &UpdateRedemptionCodeLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *UpdateRedemptionCodeLogic) UpdateRedemptionCode(req *types.UpdateRedemptionCodeRequest) error {
|
||||
redemptionCode, err := l.svcCtx.RedemptionCodeModel.FindOne(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
l.Errorw("[UpdateRedemptionCode] Find Redemption Code Error", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find redemption code error: %v", err.Error())
|
||||
}
|
||||
|
||||
// Code is not allowed to be modified
|
||||
if req.TotalCount != 0 {
|
||||
// Total count cannot be less than used count
|
||||
if req.TotalCount < redemptionCode.UsedCount {
|
||||
l.Errorw("[UpdateRedemptionCode] Total count cannot be less than used count",
|
||||
logger.Field("total_count", req.TotalCount),
|
||||
logger.Field("used_count", redemptionCode.UsedCount))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.InvalidParams),
|
||||
"total count cannot be less than used count: total_count=%d, used_count=%d",
|
||||
req.TotalCount, redemptionCode.UsedCount)
|
||||
}
|
||||
redemptionCode.TotalCount = req.TotalCount
|
||||
}
|
||||
if req.SubscribePlan != 0 {
|
||||
redemptionCode.SubscribePlan = req.SubscribePlan
|
||||
}
|
||||
if req.UnitTime != "" {
|
||||
redemptionCode.UnitTime = req.UnitTime
|
||||
}
|
||||
if req.Quantity != 0 {
|
||||
redemptionCode.Quantity = req.Quantity
|
||||
}
|
||||
|
||||
err = l.svcCtx.RedemptionCodeModel.Update(l.ctx, redemptionCode)
|
||||
if err != nil {
|
||||
l.Errorw("[UpdateRedemptionCode] Database Error", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "update redemption code error: %v", err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
254
internal/logic/public/redemption/redeemCodeLogic.go
Normal file
254
internal/logic/public/redemption/redeemCodeLogic.go
Normal file
@ -0,0 +1,254 @@
|
||||
package redemption
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/perfect-panel/server/internal/model/redemption"
|
||||
"github.com/perfect-panel/server/internal/model/user"
|
||||
"github.com/perfect-panel/server/pkg/constant"
|
||||
"github.com/perfect-panel/server/pkg/snowflake"
|
||||
"github.com/perfect-panel/server/pkg/uuidx"
|
||||
"github.com/perfect-panel/server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
)
|
||||
|
||||
type RedeemCodeLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// Redeem code
|
||||
func NewRedeemCodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RedeemCodeLogic {
|
||||
return &RedeemCodeLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *RedeemCodeLogic) RedeemCode(req *types.RedeemCodeRequest) (resp *types.RedeemCodeResponse, err error) {
|
||||
// Get user from context
|
||||
u, ok := l.ctx.Value(constant.CtxKeyUser).(*user.User)
|
||||
if !ok {
|
||||
logger.Error("current user is not found in context")
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidAccess), "Invalid Access")
|
||||
}
|
||||
|
||||
// Find redemption code by code
|
||||
redemptionCode, err := l.svcCtx.RedemptionCodeModel.FindOneByCode(l.ctx, req.Code)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
l.Errorw("[RedeemCode] Redemption code not found", logger.Field("code", req.Code))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidParams), "redemption code not found")
|
||||
}
|
||||
l.Errorw("[RedeemCode] Database Error", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find redemption code error: %v", err.Error())
|
||||
}
|
||||
|
||||
// Check if redemption code has remaining count
|
||||
if redemptionCode.TotalCount > 0 && redemptionCode.UsedCount >= redemptionCode.TotalCount {
|
||||
l.Errorw("[RedeemCode] Redemption code has been fully used",
|
||||
logger.Field("code", req.Code),
|
||||
logger.Field("total_count", redemptionCode.TotalCount),
|
||||
logger.Field("used_count", redemptionCode.UsedCount))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidParams), "redemption code has been fully used")
|
||||
}
|
||||
|
||||
// Check if user has already redeemed this code
|
||||
userRecords, err := l.svcCtx.RedemptionRecordModel.FindByUserId(l.ctx, u.Id)
|
||||
if err != nil {
|
||||
l.Errorw("[RedeemCode] Database Error", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find redemption records error: %v", err.Error())
|
||||
}
|
||||
for _, record := range userRecords {
|
||||
if record.RedemptionCodeId == redemptionCode.Id {
|
||||
l.Errorw("[RedeemCode] User has already redeemed this code",
|
||||
logger.Field("user_id", u.Id),
|
||||
logger.Field("code", req.Code))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidParams), "you have already redeemed this code")
|
||||
}
|
||||
}
|
||||
|
||||
// Find subscribe plan from redemption code
|
||||
subscribePlan, err := l.svcCtx.SubscribeModel.FindOne(l.ctx, redemptionCode.SubscribePlan)
|
||||
if err != nil {
|
||||
l.Errorw("[RedeemCode] Subscribe plan not found",
|
||||
logger.Field("subscribe_plan", redemptionCode.SubscribePlan),
|
||||
logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "subscribe plan not found")
|
||||
}
|
||||
|
||||
// Check if subscribe plan is available
|
||||
if !*subscribePlan.Sell {
|
||||
l.Errorw("[RedeemCode] Subscribe plan is not available",
|
||||
logger.Field("subscribe_plan", redemptionCode.SubscribePlan))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SubscribeNotAvailable), "subscribe plan is not available")
|
||||
}
|
||||
|
||||
// Start transaction
|
||||
err = l.svcCtx.RedemptionCodeModel.Transaction(l.ctx, func(tx *gorm.DB) error {
|
||||
// Find user's existing subscribe for this plan
|
||||
var existingSubscribe *user.SubscribeDetails
|
||||
userSubscribes, err := l.svcCtx.UserModel.QueryUserSubscribe(l.ctx, u.Id, 0, 1)
|
||||
if err == nil {
|
||||
for _, us := range userSubscribes {
|
||||
if us.SubscribeId == redemptionCode.SubscribePlan {
|
||||
existingSubscribe = us
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
|
||||
if existingSubscribe != nil {
|
||||
// Extend existing subscribe
|
||||
var newExpireTime time.Time
|
||||
if existingSubscribe.ExpireTime.After(now) {
|
||||
newExpireTime = existingSubscribe.ExpireTime
|
||||
} else {
|
||||
newExpireTime = now
|
||||
}
|
||||
|
||||
// Calculate duration based on redemption code
|
||||
duration, err := calculateDuration(redemptionCode.UnitTime, redemptionCode.Quantity)
|
||||
if err != nil {
|
||||
l.Errorw("[RedeemCode] Calculate duration error", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "calculate duration error: %v", err.Error())
|
||||
}
|
||||
newExpireTime = newExpireTime.Add(duration)
|
||||
|
||||
// Update subscribe
|
||||
existingSubscribe.ExpireTime = newExpireTime
|
||||
existingSubscribe.Status = 1
|
||||
|
||||
// Add traffic if needed
|
||||
if subscribePlan.Traffic > 0 {
|
||||
existingSubscribe.Traffic = subscribePlan.Traffic * 1024 * 1024 * 1024
|
||||
existingSubscribe.Download = 0
|
||||
existingSubscribe.Upload = 0
|
||||
}
|
||||
|
||||
err = l.svcCtx.UserModel.UpdateSubscribe(l.ctx, &user.Subscribe{
|
||||
Id: existingSubscribe.Id,
|
||||
UserId: existingSubscribe.UserId,
|
||||
ExpireTime: existingSubscribe.ExpireTime,
|
||||
Status: existingSubscribe.Status,
|
||||
Traffic: existingSubscribe.Traffic,
|
||||
Download: existingSubscribe.Download,
|
||||
Upload: existingSubscribe.Upload,
|
||||
}, tx)
|
||||
if err != nil {
|
||||
l.Errorw("[RedeemCode] Update subscribe error", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "update subscribe error: %v", err.Error())
|
||||
}
|
||||
} else {
|
||||
// Create new subscribe
|
||||
expireTime, traffic, err := calculateSubscribeTimeAndTraffic(redemptionCode.UnitTime, redemptionCode.Quantity, subscribePlan.Traffic)
|
||||
if err != nil {
|
||||
l.Errorw("[RedeemCode] Calculate subscribe time and traffic error", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "calculate subscribe time and traffic error: %v", err.Error())
|
||||
}
|
||||
|
||||
newSubscribe := &user.Subscribe{
|
||||
Id: snowflake.GetID(),
|
||||
UserId: u.Id,
|
||||
OrderId: 0,
|
||||
SubscribeId: redemptionCode.SubscribePlan,
|
||||
StartTime: now,
|
||||
ExpireTime: expireTime,
|
||||
FinishedAt: nil,
|
||||
Traffic: traffic,
|
||||
Download: 0,
|
||||
Upload: 0,
|
||||
Token: uuidx.SubscribeToken(fmt.Sprintf("redemption:%d:%d", u.Id, time.Now().UnixMilli())),
|
||||
UUID: uuid.New().String(),
|
||||
Status: 1,
|
||||
}
|
||||
|
||||
err = l.svcCtx.UserModel.InsertSubscribe(l.ctx, newSubscribe, tx)
|
||||
if err != nil {
|
||||
l.Errorw("[RedeemCode] Insert subscribe error", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "insert subscribe error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Increment redemption code used count
|
||||
err = l.svcCtx.RedemptionCodeModel.IncrementUsedCount(l.ctx, redemptionCode.Id)
|
||||
if err != nil {
|
||||
l.Errorw("[RedeemCode] Increment used count error", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "increment used count error: %v", err.Error())
|
||||
}
|
||||
|
||||
// Create redemption record
|
||||
redemptionRecord := &redemption.RedemptionRecord{
|
||||
Id: snowflake.GetID(),
|
||||
RedemptionCodeId: redemptionCode.Id,
|
||||
UserId: u.Id,
|
||||
SubscribeId: redemptionCode.SubscribePlan,
|
||||
UnitTime: redemptionCode.UnitTime,
|
||||
Quantity: redemptionCode.Quantity,
|
||||
RedeemedAt: now,
|
||||
CreatedAt: now,
|
||||
}
|
||||
|
||||
err = l.svcCtx.RedemptionRecordModel.Insert(l.ctx, redemptionRecord)
|
||||
if err != nil {
|
||||
l.Errorw("[RedeemCode] Insert redemption record error", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "insert redemption record error: %v", err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.RedeemCodeResponse{
|
||||
Message: "Redemption successful",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// calculateDuration calculates time duration based on unit time
|
||||
func calculateDuration(unitTime string, quantity int64) (time.Duration, error) {
|
||||
switch unitTime {
|
||||
case "month":
|
||||
return time.Duration(quantity*30*24) * time.Hour, nil
|
||||
case "quarter":
|
||||
return time.Duration(quantity*90*24) * time.Hour, nil
|
||||
case "half_year":
|
||||
return time.Duration(quantity*180*24) * time.Hour, nil
|
||||
case "year":
|
||||
return time.Duration(quantity*365*24) * time.Hour, nil
|
||||
case "day":
|
||||
return time.Duration(quantity*24) * time.Hour, nil
|
||||
default:
|
||||
return time.Duration(quantity*30*24) * time.Hour, nil
|
||||
}
|
||||
}
|
||||
|
||||
// calculateSubscribeTimeAndTraffic calculates expire time and traffic based on subscribe plan
|
||||
func calculateSubscribeTimeAndTraffic(unitTime string, quantity int64, traffic int64) (time.Time, int64, error) {
|
||||
duration, err := calculateDuration(unitTime, quantity)
|
||||
if err != nil {
|
||||
return time.Time{}, 0, err
|
||||
}
|
||||
|
||||
expireTime := time.Now().Add(duration)
|
||||
trafficBytes := int64(0)
|
||||
if traffic > 0 {
|
||||
trafficBytes = traffic * 1024 * 1024 * 1024
|
||||
}
|
||||
|
||||
return expireTime, trafficBytes, nil
|
||||
}
|
||||
279
internal/model/redemption/default.go
Normal file
279
internal/model/redemption/default.go
Normal file
@ -0,0 +1,279 @@
|
||||
package redemption
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/perfect-panel/server/pkg/cache"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var _ RedemptionCodeModel = (*customRedemptionCodeModel)(nil)
|
||||
var _ RedemptionRecordModel = (*customRedemptionRecordModel)(nil)
|
||||
|
||||
var (
|
||||
cacheRedemptionCodeIdPrefix = "cache:redemption_code:id:"
|
||||
cacheRedemptionCodeCodePrefix = "cache:redemption_code:code:"
|
||||
cacheRedemptionRecordIdPrefix = "cache:redemption_record:id:"
|
||||
)
|
||||
|
||||
type (
|
||||
RedemptionCodeModel interface {
|
||||
Insert(ctx context.Context, data *RedemptionCode) error
|
||||
FindOne(ctx context.Context, id int64) (*RedemptionCode, error)
|
||||
FindOneByCode(ctx context.Context, code string) (*RedemptionCode, error)
|
||||
Update(ctx context.Context, data *RedemptionCode) error
|
||||
Delete(ctx context.Context, id int64) error
|
||||
Transaction(ctx context.Context, fn func(db *gorm.DB) error) error
|
||||
customRedemptionCodeLogicModel
|
||||
}
|
||||
|
||||
RedemptionRecordModel interface {
|
||||
Insert(ctx context.Context, data *RedemptionRecord) error
|
||||
FindOne(ctx context.Context, id int64) (*RedemptionRecord, error)
|
||||
Update(ctx context.Context, data *RedemptionRecord) error
|
||||
Delete(ctx context.Context, id int64) error
|
||||
customRedemptionRecordLogicModel
|
||||
}
|
||||
|
||||
customRedemptionCodeLogicModel interface {
|
||||
QueryRedemptionCodeListByPage(ctx context.Context, page, size int, subscribePlan int64, unitTime string, code string) (total int64, list []*RedemptionCode, err error)
|
||||
BatchDelete(ctx context.Context, ids []int64) error
|
||||
IncrementUsedCount(ctx context.Context, id int64) error
|
||||
}
|
||||
|
||||
customRedemptionRecordLogicModel interface {
|
||||
QueryRedemptionRecordListByPage(ctx context.Context, page, size int, userId int64, codeId int64) (total int64, list []*RedemptionRecord, err error)
|
||||
FindByUserId(ctx context.Context, userId int64) ([]*RedemptionRecord, error)
|
||||
FindByCodeId(ctx context.Context, codeId int64) ([]*RedemptionRecord, error)
|
||||
}
|
||||
|
||||
customRedemptionCodeModel struct {
|
||||
*defaultRedemptionCodeModel
|
||||
}
|
||||
defaultRedemptionCodeModel struct {
|
||||
cache.CachedConn
|
||||
table string
|
||||
}
|
||||
|
||||
customRedemptionRecordModel struct {
|
||||
*defaultRedemptionRecordModel
|
||||
}
|
||||
defaultRedemptionRecordModel struct {
|
||||
cache.CachedConn
|
||||
table string
|
||||
}
|
||||
)
|
||||
|
||||
func newRedemptionCodeModel(db *gorm.DB, c *redis.Client) *defaultRedemptionCodeModel {
|
||||
return &defaultRedemptionCodeModel{
|
||||
CachedConn: cache.NewConn(db, c),
|
||||
table: "`redemption_code`",
|
||||
}
|
||||
}
|
||||
|
||||
func newRedemptionRecordModel(db *gorm.DB, c *redis.Client) *defaultRedemptionRecordModel {
|
||||
return &defaultRedemptionRecordModel{
|
||||
CachedConn: cache.NewConn(db, c),
|
||||
table: "`redemption_record`",
|
||||
}
|
||||
}
|
||||
|
||||
// RedemptionCode cache methods
|
||||
func (m *defaultRedemptionCodeModel) getCacheKeys(data *RedemptionCode) []string {
|
||||
if data == nil {
|
||||
return []string{}
|
||||
}
|
||||
codeIdKey := fmt.Sprintf("%s%v", cacheRedemptionCodeIdPrefix, data.Id)
|
||||
codeCodeKey := fmt.Sprintf("%s%v", cacheRedemptionCodeCodePrefix, data.Code)
|
||||
cacheKeys := []string{
|
||||
codeIdKey,
|
||||
codeCodeKey,
|
||||
}
|
||||
return cacheKeys
|
||||
}
|
||||
|
||||
func (m *defaultRedemptionCodeModel) Insert(ctx context.Context, data *RedemptionCode) error {
|
||||
err := m.ExecCtx(ctx, func(conn *gorm.DB) error {
|
||||
return conn.Create(data).Error
|
||||
}, m.getCacheKeys(data)...)
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *defaultRedemptionCodeModel) FindOne(ctx context.Context, id int64) (*RedemptionCode, error) {
|
||||
codeIdKey := fmt.Sprintf("%s%v", cacheRedemptionCodeIdPrefix, id)
|
||||
var resp RedemptionCode
|
||||
err := m.QueryCtx(ctx, &resp, codeIdKey, func(conn *gorm.DB, v interface{}) error {
|
||||
return conn.Model(&RedemptionCode{}).Where("`id` = ?", id).First(&resp).Error
|
||||
})
|
||||
switch {
|
||||
case err == nil:
|
||||
return &resp, nil
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultRedemptionCodeModel) FindOneByCode(ctx context.Context, code string) (*RedemptionCode, error) {
|
||||
codeCodeKey := fmt.Sprintf("%s%v", cacheRedemptionCodeCodePrefix, code)
|
||||
var resp RedemptionCode
|
||||
err := m.QueryCtx(ctx, &resp, codeCodeKey, func(conn *gorm.DB, v interface{}) error {
|
||||
return conn.Model(&RedemptionCode{}).Where("`code` = ?", code).First(&resp).Error
|
||||
})
|
||||
switch {
|
||||
case err == nil:
|
||||
return &resp, nil
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultRedemptionCodeModel) Update(ctx context.Context, data *RedemptionCode) error {
|
||||
old, err := m.FindOne(ctx, data.Id)
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return err
|
||||
}
|
||||
err = m.ExecCtx(ctx, func(conn *gorm.DB) error {
|
||||
db := conn
|
||||
return db.Save(data).Error
|
||||
}, m.getCacheKeys(old)...)
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *defaultRedemptionCodeModel) Delete(ctx context.Context, id int64) error {
|
||||
data, err := m.FindOne(ctx, id)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
err = m.ExecCtx(ctx, func(conn *gorm.DB) error {
|
||||
db := conn
|
||||
return db.Delete(&RedemptionCode{}, id).Error
|
||||
}, m.getCacheKeys(data)...)
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *defaultRedemptionCodeModel) Transaction(ctx context.Context, fn func(db *gorm.DB) error) error {
|
||||
return m.TransactCtx(ctx, fn)
|
||||
}
|
||||
|
||||
// RedemptionCode custom logic methods
|
||||
func (m *customRedemptionCodeModel) QueryRedemptionCodeListByPage(ctx context.Context, page, size int, subscribePlan int64, unitTime string, code string) (total int64, list []*RedemptionCode, err error) {
|
||||
err = m.QueryNoCacheCtx(ctx, &list, func(conn *gorm.DB, v interface{}) error {
|
||||
db := conn.Model(&RedemptionCode{})
|
||||
if subscribePlan != 0 {
|
||||
db = db.Where("subscribe_plan = ?", subscribePlan)
|
||||
}
|
||||
if unitTime != "" {
|
||||
db = db.Where("unit_time = ?", unitTime)
|
||||
}
|
||||
if code != "" {
|
||||
db = db.Where("code like ?", "%"+code+"%")
|
||||
}
|
||||
return db.Count(&total).Limit(size).Offset((page - 1) * size).Order("created_at DESC").Find(v).Error
|
||||
})
|
||||
return total, list, err
|
||||
}
|
||||
|
||||
func (m *customRedemptionCodeModel) BatchDelete(ctx context.Context, ids []int64) error {
|
||||
var err error
|
||||
for _, id := range ids {
|
||||
if err = m.Delete(ctx, id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *customRedemptionCodeModel) IncrementUsedCount(ctx context.Context, id int64) error {
|
||||
data, err := m.FindOne(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data.UsedCount++
|
||||
return m.Update(ctx, data)
|
||||
}
|
||||
|
||||
// RedemptionRecord cache methods
|
||||
func (m *defaultRedemptionRecordModel) getCacheKeys(data *RedemptionRecord) []string {
|
||||
if data == nil {
|
||||
return []string{}
|
||||
}
|
||||
recordIdKey := fmt.Sprintf("%s%v", cacheRedemptionRecordIdPrefix, data.Id)
|
||||
cacheKeys := []string{
|
||||
recordIdKey,
|
||||
}
|
||||
return cacheKeys
|
||||
}
|
||||
|
||||
func (m *defaultRedemptionRecordModel) Insert(ctx context.Context, data *RedemptionRecord) error {
|
||||
err := m.ExecCtx(ctx, func(conn *gorm.DB) error {
|
||||
return conn.Create(data).Error
|
||||
}, m.getCacheKeys(data)...)
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *defaultRedemptionRecordModel) FindOne(ctx context.Context, id int64) (*RedemptionRecord, error) {
|
||||
recordIdKey := fmt.Sprintf("%s%v", cacheRedemptionRecordIdPrefix, id)
|
||||
var resp RedemptionRecord
|
||||
err := m.QueryCtx(ctx, &resp, recordIdKey, func(conn *gorm.DB, v interface{}) error {
|
||||
return conn.Model(&RedemptionRecord{}).Where("`id` = ?", id).First(&resp).Error
|
||||
})
|
||||
switch {
|
||||
case err == nil:
|
||||
return &resp, nil
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultRedemptionRecordModel) Update(ctx context.Context, data *RedemptionRecord) error {
|
||||
err := m.ExecCtx(ctx, func(conn *gorm.DB) error {
|
||||
db := conn
|
||||
return db.Save(data).Error
|
||||
}, m.getCacheKeys(data)...)
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *defaultRedemptionRecordModel) Delete(ctx context.Context, id int64) error {
|
||||
err := m.ExecCtx(ctx, func(conn *gorm.DB) error {
|
||||
db := conn
|
||||
return db.Delete(&RedemptionRecord{}, id).Error
|
||||
}, m.getCacheKeys(nil)...)
|
||||
return err
|
||||
}
|
||||
|
||||
// RedemptionRecord custom logic methods
|
||||
func (m *customRedemptionRecordModel) QueryRedemptionRecordListByPage(ctx context.Context, page, size int, userId int64, codeId int64) (total int64, list []*RedemptionRecord, err error) {
|
||||
err = m.QueryNoCacheCtx(ctx, &list, func(conn *gorm.DB, v interface{}) error {
|
||||
db := conn.Model(&RedemptionRecord{})
|
||||
if userId != 0 {
|
||||
db = db.Where("user_id = ?", userId)
|
||||
}
|
||||
if codeId != 0 {
|
||||
db = db.Where("redemption_code_id = ?", codeId)
|
||||
}
|
||||
return db.Count(&total).Limit(size).Offset((page - 1) * size).Order("created_at DESC").Find(v).Error
|
||||
})
|
||||
return total, list, err
|
||||
}
|
||||
|
||||
func (m *customRedemptionRecordModel) FindByUserId(ctx context.Context, userId int64) ([]*RedemptionRecord, error) {
|
||||
var list []*RedemptionRecord
|
||||
err := m.QueryNoCacheCtx(ctx, &list, func(conn *gorm.DB, v interface{}) error {
|
||||
return conn.Model(&RedemptionRecord{}).Where("user_id = ?", userId).Order("created_at DESC").Find(v).Error
|
||||
})
|
||||
return list, err
|
||||
}
|
||||
|
||||
func (m *customRedemptionRecordModel) FindByCodeId(ctx context.Context, codeId int64) ([]*RedemptionRecord, error) {
|
||||
var list []*RedemptionRecord
|
||||
err := m.QueryNoCacheCtx(ctx, &list, func(conn *gorm.DB, v interface{}) error {
|
||||
return conn.Model(&RedemptionRecord{}).Where("redemption_code_id = ?", codeId).Order("created_at DESC").Find(v).Error
|
||||
})
|
||||
return list, err
|
||||
}
|
||||
20
internal/model/redemption/model.go
Normal file
20
internal/model/redemption/model.go
Normal file
@ -0,0 +1,20 @@
|
||||
package redemption
|
||||
|
||||
import (
|
||||
"github.com/redis/go-redis/v9"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// NewRedemptionCodeModel returns a model for the redemption_code table.
|
||||
func NewRedemptionCodeModel(conn *gorm.DB, c *redis.Client) RedemptionCodeModel {
|
||||
return &customRedemptionCodeModel{
|
||||
defaultRedemptionCodeModel: newRedemptionCodeModel(conn, c),
|
||||
}
|
||||
}
|
||||
|
||||
// NewRedemptionRecordModel returns a model for the redemption_record table.
|
||||
func NewRedemptionRecordModel(conn *gorm.DB, c *redis.Client) RedemptionRecordModel {
|
||||
return &customRedemptionRecordModel{
|
||||
defaultRedemptionRecordModel: newRedemptionRecordModel(conn, c),
|
||||
}
|
||||
}
|
||||
39
internal/model/redemption/redemption.go
Normal file
39
internal/model/redemption/redemption.go
Normal file
@ -0,0 +1,39 @@
|
||||
package redemption
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type RedemptionCode struct {
|
||||
Id int64 `gorm:"primaryKey"`
|
||||
Code string `gorm:"type:varchar(255);not null;unique;comment:Redemption Code"`
|
||||
TotalCount int64 `gorm:"type:int;not null;default:0;comment:Total Redemption Count"`
|
||||
UsedCount int64 `gorm:"type:int;not null;default:0;comment:Used Redemption Count"`
|
||||
SubscribePlan int64 `gorm:"type:bigint;not null;default:0;comment:Subscribe Plan"`
|
||||
UnitTime string `gorm:"type:varchar(50);not null;default:'month';comment:Unit Time: day, month, quarter, half_year, year"`
|
||||
Quantity int64 `gorm:"type:int;not null;default:1;comment:Quantity"`
|
||||
CreatedAt time.Time `gorm:"<-:create;comment:Create Time"`
|
||||
UpdatedAt time.Time `gorm:"comment:Update Time"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index;comment:Delete Time"`
|
||||
}
|
||||
|
||||
type RedemptionRecord struct {
|
||||
Id int64 `gorm:"primaryKey"`
|
||||
RedemptionCodeId int64 `gorm:"type:bigint;not null;default:0;comment:Redemption Code Id;index"`
|
||||
UserId int64 `gorm:"type:bigint;not null;default:0;comment:User Id;index"`
|
||||
SubscribeId int64 `gorm:"type:bigint;not null;default:0;comment:Subscribe Id"`
|
||||
UnitTime string `gorm:"type:varchar(50);not null;default:'month';comment:Unit Time"`
|
||||
Quantity int64 `gorm:"type:int;not null;default:1;comment:Quantity"`
|
||||
RedeemedAt time.Time `gorm:"<-:create;comment:Redeemed Time"`
|
||||
CreatedAt time.Time `gorm:"<-:create;comment:Create Time"`
|
||||
}
|
||||
|
||||
func (RedemptionCode) TableName() string {
|
||||
return "redemption_code"
|
||||
}
|
||||
|
||||
func (RedemptionRecord) TableName() string {
|
||||
return "redemption_record"
|
||||
}
|
||||
@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/client"
|
||||
"github.com/perfect-panel/server/internal/model/node"
|
||||
"github.com/perfect-panel/server/internal/model/redemption"
|
||||
"github.com/perfect-panel/server/pkg/device"
|
||||
|
||||
"github.com/perfect-panel/server/internal/config"
|
||||
@ -49,13 +50,15 @@ type ServiceContext struct {
|
||||
ClientModel client.Model
|
||||
TicketModel ticket.Model
|
||||
//ServerModel server.Model
|
||||
SystemModel system.Model
|
||||
CouponModel coupon.Model
|
||||
PaymentModel payment.Model
|
||||
DocumentModel document.Model
|
||||
SubscribeModel subscribe.Model
|
||||
TrafficLogModel traffic.Model
|
||||
AnnouncementModel announcement.Model
|
||||
SystemModel system.Model
|
||||
CouponModel coupon.Model
|
||||
RedemptionCodeModel redemption.RedemptionCodeModel
|
||||
RedemptionRecordModel redemption.RedemptionRecordModel
|
||||
PaymentModel payment.Model
|
||||
DocumentModel document.Model
|
||||
SubscribeModel subscribe.Model
|
||||
TrafficLogModel traffic.Model
|
||||
AnnouncementModel announcement.Model
|
||||
|
||||
Restart func() error
|
||||
TelegramBot *tgbotapi.BotAPI
|
||||
@ -110,13 +113,15 @@ func NewServiceContext(c config.Config) *ServiceContext {
|
||||
ClientModel: client.NewSubscribeApplicationModel(db),
|
||||
TicketModel: ticket.NewModel(db, rds),
|
||||
//ServerModel: server.NewModel(db, rds),
|
||||
SystemModel: system.NewModel(db, rds),
|
||||
CouponModel: coupon.NewModel(db, rds),
|
||||
PaymentModel: payment.NewModel(db, rds),
|
||||
DocumentModel: document.NewModel(db, rds),
|
||||
SubscribeModel: subscribe.NewModel(db, rds),
|
||||
TrafficLogModel: traffic.NewModel(db),
|
||||
AnnouncementModel: announcement.NewModel(db, rds),
|
||||
SystemModel: system.NewModel(db, rds),
|
||||
CouponModel: coupon.NewModel(db, rds),
|
||||
RedemptionCodeModel: redemption.NewRedemptionCodeModel(db, rds),
|
||||
RedemptionRecordModel: redemption.NewRedemptionRecordModel(db, rds),
|
||||
PaymentModel: payment.NewModel(db, rds),
|
||||
DocumentModel: document.NewModel(db, rds),
|
||||
SubscribeModel: subscribe.NewModel(db, rds),
|
||||
TrafficLogModel: traffic.NewModel(db),
|
||||
AnnouncementModel: announcement.NewModel(db, rds),
|
||||
}
|
||||
srv.DeviceManager = NewDeviceManager(srv)
|
||||
return srv
|
||||
|
||||
@ -146,6 +146,10 @@ type BatchDeleteDocumentRequest struct {
|
||||
Ids []int64 `json:"ids" validate:"required"`
|
||||
}
|
||||
|
||||
type BatchDeleteRedemptionCodeRequest struct {
|
||||
Ids []int64 `json:"ids" validate:"required"`
|
||||
}
|
||||
|
||||
type BatchDeleteSubscribeGroupRequest struct {
|
||||
Ids []int64 `json:"ids" validate:"required"`
|
||||
}
|
||||
@ -367,6 +371,14 @@ type CreateQuotaTaskRequest struct {
|
||||
GiftValue uint64 `json:"gift_value"`
|
||||
}
|
||||
|
||||
type CreateRedemptionCodeRequest struct {
|
||||
TotalCount int64 `json:"total_count" validate:"required"`
|
||||
SubscribePlan int64 `json:"subscribe_plan" validate:"required"`
|
||||
UnitTime string `json:"unit_time" validate:"required,oneof=day month quarter half_year year"`
|
||||
Quantity int64 `json:"quantity" validate:"required"`
|
||||
BatchCount int64 `json:"batch_count" validate:"required,min=1"`
|
||||
}
|
||||
|
||||
type CreateServerRequest struct {
|
||||
Name string `json:"name"`
|
||||
Country string `json:"country,omitempty"`
|
||||
@ -501,6 +513,10 @@ type DeletePaymentMethodRequest struct {
|
||||
Id int64 `json:"id" validate:"required"`
|
||||
}
|
||||
|
||||
type DeleteRedemptionCodeRequest struct {
|
||||
Id int64 `json:"id" validate:"required"`
|
||||
}
|
||||
|
||||
type DeleteServerRequest struct {
|
||||
Id int64 `json:"id"`
|
||||
}
|
||||
@ -934,6 +950,31 @@ type GetPreSendEmailCountResponse struct {
|
||||
Count int64 `json:"count"`
|
||||
}
|
||||
|
||||
type GetRedemptionCodeListRequest struct {
|
||||
Page int64 `form:"page" validate:"required"`
|
||||
Size int64 `form:"size" validate:"required"`
|
||||
SubscribePlan int64 `form:"subscribe_plan,omitempty"`
|
||||
UnitTime string `form:"unit_time,omitempty"`
|
||||
Code string `form:"code,omitempty"`
|
||||
}
|
||||
|
||||
type GetRedemptionCodeListResponse struct {
|
||||
Total int64 `json:"total"`
|
||||
List []RedemptionCode `json:"list"`
|
||||
}
|
||||
|
||||
type GetRedemptionRecordListRequest struct {
|
||||
Page int64 `form:"page" validate:"required"`
|
||||
Size int64 `form:"size" validate:"required"`
|
||||
UserId int64 `form:"user_id,omitempty"`
|
||||
CodeId int64 `form:"code_id,omitempty"`
|
||||
}
|
||||
|
||||
type GetRedemptionRecordListResponse struct {
|
||||
Total int64 `json:"total"`
|
||||
List []RedemptionRecord `json:"list"`
|
||||
}
|
||||
|
||||
type GetServerConfigRequest struct {
|
||||
ServerCommon
|
||||
}
|
||||
@ -1779,6 +1820,37 @@ type RechargeOrderResponse struct {
|
||||
OrderNo string `json:"order_no"`
|
||||
}
|
||||
|
||||
type RedeemCodeRequest struct {
|
||||
Code string `json:"code" validate:"required"`
|
||||
}
|
||||
|
||||
type RedeemCodeResponse struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type RedemptionCode struct {
|
||||
Id int64 `json:"id"`
|
||||
Code string `json:"code"`
|
||||
TotalCount int64 `json:"total_count"`
|
||||
UsedCount int64 `json:"used_count"`
|
||||
SubscribePlan int64 `json:"subscribe_plan"`
|
||||
UnitTime string `json:"unit_time"`
|
||||
Quantity int64 `json:"quantity"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at"`
|
||||
}
|
||||
|
||||
type RedemptionRecord struct {
|
||||
Id int64 `json:"id"`
|
||||
RedemptionCodeId int64 `json:"redemption_code_id"`
|
||||
UserId int64 `json:"user_id"`
|
||||
SubscribeId int64 `json:"subscribe_id"`
|
||||
UnitTime string `json:"unit_time"`
|
||||
Quantity int64 `json:"quantity"`
|
||||
RedeemedAt int64 `json:"redeemed_at"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
}
|
||||
|
||||
type RegisterConfig struct {
|
||||
StopRegister bool `json:"stop_register"`
|
||||
EnableTrial bool `json:"enable_trial"`
|
||||
@ -2416,6 +2488,14 @@ type UpdatePaymentMethodRequest struct {
|
||||
Enable *bool `json:"enable" validate:"required"`
|
||||
}
|
||||
|
||||
type UpdateRedemptionCodeRequest struct {
|
||||
Id int64 `json:"id" validate:"required"`
|
||||
TotalCount int64 `json:"total_count,omitempty"`
|
||||
SubscribePlan int64 `json:"subscribe_plan,omitempty"`
|
||||
UnitTime string `json:"unit_time,omitempty" validate:"omitempty,oneof=day month quarter half_year year"`
|
||||
Quantity int64 `json:"quantity,omitempty"`
|
||||
}
|
||||
|
||||
type UpdateServerRequest struct {
|
||||
Id int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
|
||||
@ -19,6 +19,7 @@ import (
|
||||
"apis/admin/subscribe.api"
|
||||
"apis/admin/payment.api"
|
||||
"apis/admin/coupon.api"
|
||||
"apis/admin/redemption.api"
|
||||
"apis/admin/order.api"
|
||||
"apis/admin/ticket.api"
|
||||
"apis/admin/announcement.api"
|
||||
@ -31,6 +32,7 @@ import (
|
||||
"apis/admin/application.api"
|
||||
"apis/public/user.api"
|
||||
"apis/public/subscribe.api"
|
||||
"apis/public/redemption.api"
|
||||
"apis/public/order.api"
|
||||
"apis/public/announcement.api"
|
||||
"apis/public/ticket.api"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user