Feat: vas for group / storage pack

This commit is contained in:
HFO4
2020-02-16 14:31:23 +08:00
parent faf46745bc
commit e38a60ea44
11 changed files with 504 additions and 12 deletions

86
pkg/payment/order.go Normal file
View File

@@ -0,0 +1,86 @@
package payment
import (
"fmt"
model "github.com/HFO4/cloudreve/models"
"github.com/HFO4/cloudreve/pkg/serializer"
)
var (
// ErrUnknownPaymentMethod 未知支付方式
ErrUnknownPaymentMethod = serializer.NewError(serializer.CodeNotFound, "未知支付方式", nil)
// ErrUnsupportedPaymentMethod 未知支付方式
ErrUnsupportedPaymentMethod = serializer.NewError(serializer.CodeNotFound, "此订单不支持此支付方式", nil)
// ErrInsertOrder 无法插入订单记录
ErrInsertOrder = serializer.NewError(serializer.CodeDBError, "无法插入订单记录", nil)
// ErrScoreNotEnough 积分不足
ErrScoreNotEnough = serializer.NewError(serializer.CodeNoPermissionErr, "积分不足", nil)
// ErrCreateStoragePack 无法创建容量包
ErrCreateStoragePack = serializer.NewError(serializer.CodeNoPermissionErr, "无法创建容量包", nil)
// ErrGroupConflict 用户组冲突
ErrGroupConflict = serializer.NewError(serializer.CodeNoPermissionErr, "当前用户组仍未过期,请前往个人设置手动解约后继续", nil)
// ErrGroupInvalid 用户组冲突
ErrGroupInvalid = serializer.NewError(serializer.CodeNoPermissionErr, "用户组不可用", nil)
// ErrUpgradeGroup 用户组冲突
ErrUpgradeGroup = serializer.NewError(serializer.CodeDBError, "无法升级用户组", nil)
)
// Pay 支付处理接口
type Pay interface {
Create(order *model.Order, pack *serializer.PackProduct, group *serializer.GroupProducts, user *model.User) (*OrderCreateRes, error)
}
// OrderCreateRes 订单创建结果
type OrderCreateRes struct {
Payment bool `json:"payment"` // 是否需要支付
}
// NewPaymentInstance 获取新的支付实例
func NewPaymentInstance(method string) (Pay, error) {
if method == "score" {
return &ScorePayment{}, nil
}
return nil, ErrUnknownPaymentMethod
}
// NewOrder 创建新订单
func NewOrder(pack *serializer.PackProduct, group *serializer.GroupProducts, num int, method string, user *model.User) (*OrderCreateRes, error) {
// 获取支付实例
pay, err := NewPaymentInstance(method)
if err != nil {
return nil, err
}
var (
orderType int
productID int64
title string
price int
)
if pack == nil {
orderType = model.GroupOrderType
productID = group.ID
title = group.Name
price = group.Price
} else {
orderType = model.PackOrderType
productID = pack.ID
title = pack.Name
price = pack.Price
}
// 创建订单记录
order := &model.Order{
UserID: user.ID,
Type: orderType,
Method: method,
ProductID: productID,
Num: num,
Name: fmt.Sprintf("%s - %s", model.GetSettingByName("siteName"), title),
Price: price,
Status: model.OrderUnpaid,
}
return pay.Create(order, pack, group, user)
}

64
pkg/payment/purchase.go Normal file
View File

@@ -0,0 +1,64 @@
package payment
import (
model "github.com/HFO4/cloudreve/models"
"github.com/HFO4/cloudreve/pkg/serializer"
"time"
)
// GivePack 创建容量包
func GivePack(user *model.User, packInfo *serializer.PackProduct, num int) error {
timeNow := time.Now()
expires := timeNow.Add(time.Duration(packInfo.Time*int64(num)) * time.Second)
pack := model.StoragePack{
Name: packInfo.Name,
UserID: user.ID,
ActiveTime: &timeNow,
ExpiredTime: &expires,
Size: packInfo.Size,
}
if _, err := pack.Create(); err != nil {
return ErrCreateStoragePack.WithError(err)
}
return nil
}
func checkGroupUpgrade(user *model.User, groupInfo *serializer.GroupProducts) error {
// 检查用户是否已有未过期用户
if user.PreviousGroupID != 0 {
return ErrGroupConflict
}
// 用户组不能相同
if user.GroupID == groupInfo.GroupID {
return ErrGroupInvalid
}
return nil
}
// GiveGroup 升级用户组
func GiveGroup(user *model.User, groupInfo *serializer.GroupProducts, num int) error {
if err := checkGroupUpgrade(user, groupInfo); err != nil {
return err
}
timeNow := time.Now()
expires := timeNow.Add(time.Duration(groupInfo.Time*int64(num)) * time.Second)
if err := user.UpgradeGroup(groupInfo.GroupID, &expires); err != nil {
return ErrUpgradeGroup.WithError(err)
}
return nil
}
// GiveProduct “发货”
func GiveProduct(user *model.User, pack *serializer.PackProduct, group *serializer.GroupProducts, num int) error {
if pack != nil {
return GivePack(user, pack, num)
} else if group != nil {
return GiveGroup(user, group, num)
}
return nil
}

44
pkg/payment/score.go Normal file
View File

@@ -0,0 +1,44 @@
package payment
import (
model "github.com/HFO4/cloudreve/models"
"github.com/HFO4/cloudreve/pkg/serializer"
)
// ScorePayment 积分支付处理
type ScorePayment struct {
}
// Create 创建新订单
func (pay *ScorePayment) Create(order *model.Order, pack *serializer.PackProduct, group *serializer.GroupProducts, user *model.User) (*OrderCreateRes, error) {
if pack != nil {
order.Price = pack.Score
} else {
order.Price = group.Score
}
// 检查此订单是否可用积分支付
if order.Price == 0 {
return nil, ErrUnsupportedPaymentMethod
}
// 扣除用户积分
if !user.PayScore(order.Price * order.Num) {
return nil, ErrScoreNotEnough
}
// 商品“发货”
if err := GiveProduct(user, pack, group, order.Num); err != nil {
user.AddScore(order.Price * order.Num)
return nil, err
}
// 创建订单记录
if _, err := order.Create(); err != nil {
return nil, ErrInsertOrder.WithError(err)
}
return &OrderCreateRes{
Payment: false,
}, nil
}

84
pkg/serializer/vas.go Normal file
View File

@@ -0,0 +1,84 @@
package serializer
import (
model "github.com/HFO4/cloudreve/models"
)
type quota struct {
Base uint64 `json:"base"`
Pack uint64 `json:"pack"`
Used uint64 `json:"used"`
Total uint64 `json:"total"`
Packs []storagePacks `json:"packs"`
}
type storagePacks struct {
Name string `json:"name"`
Size uint64 `json:"size"`
ActivateDate string `json:"activate_date"`
Expiration int `json:"expiration"`
ExpirationDate string `json:"expiration_date"`
}
// BuildUserQuotaResponse 序列化用户存储配额概况响应
func BuildUserQuotaResponse(user *model.User, packs []model.StoragePack) Response {
packSize := user.GetAvailablePackSize()
res := quota{
Base: user.Group.MaxStorage,
Pack: packSize,
Used: user.Storage,
Total: packSize + user.Group.MaxStorage,
Packs: make([]storagePacks, 0, len(packs)),
}
for _, pack := range packs {
res.Packs = append(res.Packs, storagePacks{
Name: pack.Name,
Size: pack.Size,
ActivateDate: pack.ActiveTime.Format("2006-01-02 15:04:05"),
Expiration: int(pack.ExpiredTime.Sub(*pack.ActiveTime).Seconds()),
ExpirationDate: pack.ExpiredTime.Format("2006-01-02 15:04:05"),
})
}
return Response{
Data: res,
}
}
// PackProduct 容量包商品
type PackProduct struct {
ID int64 `json:"id"`
Name string `json:"name"`
Size uint64 `json:"size"`
Time int64 `json:"time"`
Price int `json:"price"`
Score int `json:"score"`
}
// GroupProducts 用户组商品
type GroupProducts struct {
ID int64 `json:"id"`
Name string `json:"name"`
GroupID uint `json:"group_id"`
Time int64 `json:"time"`
Price int `json:"price"`
Score int `json:"score"`
Des []string `json:"des"`
Highlight bool `json:"highlight"`
}
// BuildProductResponse 构建增值服务商品响应
func BuildProductResponse(groups []GroupProducts, packs []PackProduct, alipay, payjs bool) Response {
// 隐藏响应中的用户组ID
for i := 0; i < len(groups); i++ {
groups[i].GroupID = 0
}
return Response{
Data: map[string]interface{}{
"packs": packs,
"groups": groups,
"alipay": alipay,
"payjs": payjs,
},
}
}