diff --git a/models/migration.go b/models/migration.go index e9bb4d4..391d78a 100644 --- a/models/migration.go +++ b/models/migration.go @@ -30,7 +30,7 @@ func migration() { DB = DB.Set("gorm:table_options", "ENGINE=InnoDB") } DB.AutoMigrate(&User{}, &Setting{}, &Group{}, &Policy{}, &Folder{}, &File{}, &StoragePack{}, &Share{}, - &Task{}, &Download{}, &Tag{}, &Webdav{}, &Order{}) + &Task{}, &Download{}, &Tag{}, &Webdav{}, &Order{}, &Redeem{}) // 创建初始存储策略 addDefaultPolicy() diff --git a/models/order.go b/models/order.go index 22fd055..9800bf2 100644 --- a/models/order.go +++ b/models/order.go @@ -27,7 +27,7 @@ const ( type Order struct { gorm.Model UserID uint // 创建者ID - OrderNo string // 商户自定义订单编号 + OrderNo string `gorm:"index:order_number"` // 商户自定义订单编号 Type int // 订单类型 Method string // 支付类型 ProductID int64 // 商品ID diff --git a/models/redeem.go b/models/redeem.go new file mode 100644 index 0000000..4f8396d --- /dev/null +++ b/models/redeem.go @@ -0,0 +1,27 @@ +package model + +import "github.com/jinzhu/gorm" + +// Redeem 兑换码 +type Redeem struct { + gorm.Model + Type int // 订单类型 + ProductID int64 // 商品ID + Num int // 商品数量 + Code string `gorm:"size:64,index:redeem_code"` // 兑换码 + Used bool // 是否已被使用 +} + +// GetAvailableRedeem 根据code查找可用兑换码 +func GetAvailableRedeem(code string) (*Redeem, error) { + redeem := &Redeem{} + result := DB.Where("code = ? and used = ?", code, false).First(redeem) + return redeem, result.Error +} + +// Use 设定为已使用状态 +func (redeem *Redeem) Use() { + DB.Model(redeem).Updates(map[string]interface{}{ + "used": true, + }) +} diff --git a/routers/controllers/vas.go b/routers/controllers/vas.go index ed1f0cb..883b477 100644 --- a/routers/controllers/vas.go +++ b/routers/controllers/vas.go @@ -37,3 +37,25 @@ func NewOrder(c *gin.Context) { c.JSON(200, ErrorResponse(err)) } } + +// GetRedeemInfo 获取兑换码信息 +func GetRedeemInfo(c *gin.Context) { + var service vas.RedeemService + if err := c.ShouldBindUri(&service); err == nil { + res := service.Query(c) + c.JSON(200, res) + } else { + c.JSON(200, ErrorResponse(err)) + } +} + +// DoRedeem 获取兑换码信息 +func DoRedeem(c *gin.Context) { + var service vas.RedeemService + if err := c.ShouldBindUri(&service); err == nil { + res := service.Redeem(c, CurrentUser(c)) + c.JSON(200, res) + } else { + c.JSON(200, ErrorResponse(err)) + } +} diff --git a/routers/router.go b/routers/router.go index 62b837a..ee7d88f 100644 --- a/routers/router.go +++ b/routers/router.go @@ -363,6 +363,10 @@ func InitMasterRouter() *gin.Engine { vas.GET("product", controllers.GetProduct) // 新建支付订单 vas.POST("order", controllers.NewOrder) + // 获取兑换码信息 + vas.GET("redeem/:code", controllers.GetRedeemInfo) + // 执行兑换 + vas.POST("redeem/:code", controllers.DoRedeem) } } diff --git a/service/vas/purchase.go b/service/vas/purchase.go new file mode 100644 index 0000000..1bff342 --- /dev/null +++ b/service/vas/purchase.go @@ -0,0 +1,194 @@ +package vas + +import ( + "encoding/json" + model "github.com/HFO4/cloudreve/models" + "github.com/HFO4/cloudreve/pkg/payment" + "github.com/HFO4/cloudreve/pkg/serializer" + "github.com/gin-gonic/gin" +) + +// CreateOrderService 创建订单服务 +type CreateOrderService struct { + Action string `json:"action" binding:"required,eq=group|eq=pack|eq=score"` + Method string `json:"method" binding:"required,eq=alipay|eq=score|eq=payjs"` + ID int64 `json:"id" binding:"required"` + Num int `json:"num" binding:"required,min=1"` +} + +// RedeemService 兑换服务 +type RedeemService struct { + Code string `uri:"code" binding:"required,max=64"` +} + +// Redeem 开始兑换 +func (service *RedeemService) Redeem(c *gin.Context, user *model.User) serializer.Response { + redeem, err := model.GetAvailableRedeem(service.Code) + if err != nil { + return serializer.Err(serializer.CodeDBError, "兑换码无效", err) + } + + // 取得当前商品信息 + packs, groups, err := decodeProductInfo() + if err != nil { + return serializer.Err(serializer.CodeInternalSetting, "无法解析商品设置", err) + } + + // 查找要购买的商品 + var ( + pack *serializer.PackProduct + group *serializer.GroupProducts + ) + if redeem.Type == model.GroupOrderType { + for _, v := range groups { + if v.ID == redeem.ProductID { + group = &v + break + } + } + } else if redeem.Type == model.PackOrderType { + for _, v := range packs { + if v.ID == redeem.ProductID { + pack = &v + break + } + } + } + + err = payment.GiveProduct(user, pack, group, redeem.Num) + if err != nil { + return serializer.Err(serializer.CodeNotSet, "兑换失败", err) + } + + redeem.Use() + + return serializer.Response{} + +} + +// Query 检查兑换码信息 +func (service *RedeemService) Query(c *gin.Context) serializer.Response { + redeem, err := model.GetAvailableRedeem(service.Code) + if err != nil { + return serializer.Err(serializer.CodeDBError, "兑换码无效", err) + } + + var ( + name = "积分" + productTime int64 + ) + if redeem.Type != model.ScoreOrderType { + packs, groups, err := decodeProductInfo() + if err != nil { + return serializer.Err(serializer.CodeInternalSetting, "无法解析商品设置", err) + } + if redeem.Type == model.GroupOrderType { + for _, v := range groups { + if v.ID == redeem.ProductID { + name = v.Name + productTime = v.Time + break + } + } + } else { + for _, v := range packs { + if v.ID == redeem.ProductID { + name = v.Name + productTime = v.Time + break + } + } + } + + if name == "积分" { + return serializer.Err(serializer.CodeNotFound, "商品已失效", err) + } + + } + + return serializer.Response{ + Data: struct { + Name string `json:"name"` + Type int `json:"type"` + Num int `json:"num"` + Time int64 `json:"time"` + }{ + name, redeem.Type, redeem.Num, productTime, + }, + } +} + +// Create 创建新订单 +func (service *CreateOrderService) Create(c *gin.Context, user *model.User) serializer.Response { + // 取得当前商品信息 + packs, groups, err := decodeProductInfo() + if err != nil { + return serializer.Err(serializer.CodeInternalSetting, "无法解析商品设置", err) + } + + // 查找要购买的商品 + var ( + pack *serializer.PackProduct + group *serializer.GroupProducts + ) + if service.Action == "group" { + for _, v := range groups { + if v.ID == service.ID { + group = &v + break + } + } + } else if service.Action == "pack" { + for _, v := range packs { + if v.ID == service.ID { + pack = &v + break + } + } + } + + // 购买积分 + if pack == nil && group == nil { + if service.Method == "score" { + return serializer.Err(serializer.CodeNotFound, "不支持此支付方式", nil) + } + } + + // 创建订单 + res, err := payment.NewOrder(pack, group, service.Num, service.Method, user) + if err != nil { + return serializer.Err(serializer.CodeNotSet, err.Error(), err) + } + + return serializer.Response{Data: res} + +} + +// Products 获取商品信息 +func (service *GeneralVASService) Products(c *gin.Context, user *model.User) serializer.Response { + options := model.GetSettingByNames("alipay_enabled", "payjs_enabled", "score_price") + scorePrice := model.GetIntSetting("score_price", 0) + packs, groups, err := decodeProductInfo() + if err != nil { + return serializer.Err(serializer.CodeInternalSetting, "无法解析商品设置", err) + } + + return serializer.BuildProductResponse(groups, packs, options["alipay_enabled"] == "1", options["payjs_enabled"] == "1", scorePrice) +} + +func decodeProductInfo() ([]serializer.PackProduct, []serializer.GroupProducts, error) { + options := model.GetSettingByNames("pack_data", "group_sell_data", "alipay_enabled", "payjs_enabled") + + var ( + packs []serializer.PackProduct + groups []serializer.GroupProducts + ) + if err := json.Unmarshal([]byte(options["pack_data"]), &packs); err != nil { + return nil, nil, err + } + if err := json.Unmarshal([]byte(options["group_sell_data"]), &groups); err != nil { + return nil, nil, err + } + + return packs, groups, nil +} diff --git a/service/vas/quota.go b/service/vas/quota.go index 25cc1c8..294c0df 100644 --- a/service/vas/quota.go +++ b/service/vas/quota.go @@ -1,9 +1,7 @@ package vas import ( - "encoding/json" model "github.com/HFO4/cloudreve/models" - "github.com/HFO4/cloudreve/pkg/payment" "github.com/HFO4/cloudreve/pkg/serializer" "github.com/gin-gonic/gin" ) @@ -12,89 +10,6 @@ import ( type GeneralVASService struct { } -// CreateOrderService 创建订单服务 -type CreateOrderService struct { - Action string `json:"action" binding:"required,eq=group|eq=pack|eq=score"` - Method string `json:"method" binding:"required,eq=alipay|eq=score|eq=payjs"` - ID int64 `json:"id" binding:"required"` - Num int `json:"num" binding:"required,min=1"` -} - -// Create 创建新订单 -func (service *CreateOrderService) Create(c *gin.Context, user *model.User) serializer.Response { - // 取得当前商品信息 - packs, groups, err := decodeProductInfo() - if err != nil { - return serializer.Err(serializer.CodeInternalSetting, "无法解析商品设置", err) - } - - // 查找要购买的商品 - var ( - pack *serializer.PackProduct - group *serializer.GroupProducts - ) - if service.Action == "group" { - for _, v := range groups { - if v.ID == service.ID { - group = &v - break - } - } - } else if service.Action == "pack" { - for _, v := range packs { - if v.ID == service.ID { - pack = &v - break - } - } - } - - // 购买积分 - if pack == nil && group == nil { - if service.Method == "score" { - return serializer.Err(serializer.CodeNotFound, "不支持此支付方式", nil) - } - } - - // 创建订单 - res, err := payment.NewOrder(pack, group, service.Num, service.Method, user) - if err != nil { - return serializer.Err(serializer.CodeNotSet, err.Error(), err) - } - - return serializer.Response{Data: res} - -} - -// Products 获取商品信息 -func (service *GeneralVASService) Products(c *gin.Context, user *model.User) serializer.Response { - options := model.GetSettingByNames("alipay_enabled", "payjs_enabled", "score_price") - scorePrice := model.GetIntSetting("score_price", 0) - packs, groups, err := decodeProductInfo() - if err != nil { - return serializer.Err(serializer.CodeInternalSetting, "无法解析商品设置", err) - } - - return serializer.BuildProductResponse(groups, packs, options["alipay_enabled"] == "1", options["payjs_enabled"] == "1", scorePrice) -} - -func decodeProductInfo() ([]serializer.PackProduct, []serializer.GroupProducts, error) { - options := model.GetSettingByNames("pack_data", "group_sell_data", "alipay_enabled", "payjs_enabled") - - var ( - packs []serializer.PackProduct - groups []serializer.GroupProducts - ) - if err := json.Unmarshal([]byte(options["pack_data"]), &packs); err != nil { - return nil, nil, err - } - if err := json.Unmarshal([]byte(options["group_sell_data"]), &groups); err != nil { - return nil, nil, err - } - - return packs, groups, nil -} - // Quota 获取容量配额信息 func (service *GeneralVASService) Quota(c *gin.Context, user *model.User) serializer.Response { packs := user.GetAvailableStoragePacks()