This commit is contained in:
2024-02-25 08:30:34 +08:00
commit 4947f39e74
273 changed files with 45396 additions and 0 deletions

235
service/vas/purchase.go Normal file
View File

@ -0,0 +1,235 @@
package vas
import (
"encoding/json"
model "github.com/cloudreve/Cloudreve/v3/models"
"github.com/cloudreve/Cloudreve/v3/pkg/payment"
"github.com/cloudreve/Cloudreve/v3/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|eq=wechat|eq=custom"`
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"`
}
// OrderService 订单查询
type OrderService struct {
ID string `uri:"id" binding:"required"`
}
// Status 查询订单状态
func (service *OrderService) Status(c *gin.Context, user *model.User) serializer.Response {
order, _ := model.GetOrderByNo(service.ID)
if order == nil || order.UserID != user.ID {
return serializer.Err(serializer.CodeNotFound, "", nil)
}
return serializer.Response{Data: order.Status}
}
// 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.CodeInvalidGiftCode, "", err)
}
// 取得当前商品信息
packs, groups, err := decodeProductInfo()
if err != nil {
return serializer.Err(serializer.CodeInternalSetting, "Failed to parse product settings", 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
}
}
if group == nil {
return serializer.Err(serializer.CodeNotFound, "", err)
}
} else if redeem.Type == model.PackOrderType {
for _, v := range packs {
if v.ID == redeem.ProductID {
pack = &v
break
}
}
if pack == nil {
return serializer.Err(serializer.CodeNotFound, "", err)
}
}
err = payment.GiveProduct(user, pack, group, redeem.Num)
if err != nil {
return serializer.Err(serializer.CodeNotSet, "Redeem failed", 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.CodeInvalidGiftCode, "", err)
}
var (
name = "积分"
productTime int64
)
if redeem.Type != model.ScoreOrderType {
packs, groups, err := decodeProductInfo()
if err != nil {
return serializer.Err(serializer.CodeInternalSetting, "Failed to parse product settings", 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, "Failed to parse product list", 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.ParamErr("Payment method not supported", 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(
"wechat_enabled",
"alipay_enabled",
"payjs_enabled",
"payjs_enabled",
"custom_payment_enabled",
"custom_payment_name",
)
scorePrice := model.GetIntSetting("score_price", 0)
packs, groups, err := decodeProductInfo()
if err != nil {
return serializer.Err(serializer.CodeInternalSetting, "Failed to parse product list", err)
}
return serializer.BuildProductResponse(
groups,
packs,
model.IsTrueVal(options["wechat_enabled"]),
model.IsTrueVal(options["alipay_enabled"]),
model.IsTrueVal(options["payjs_enabled"]),
model.IsTrueVal(options["custom_payment_enabled"]),
options["custom_payment_name"],
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
}

113
service/vas/qq.go Normal file
View File

@ -0,0 +1,113 @@
package vas
import (
model "github.com/cloudreve/Cloudreve/v3/models"
"github.com/cloudreve/Cloudreve/v3/pkg/qq"
"github.com/cloudreve/Cloudreve/v3/pkg/request"
"github.com/cloudreve/Cloudreve/v3/pkg/serializer"
"github.com/cloudreve/Cloudreve/v3/pkg/thumb"
"github.com/cloudreve/Cloudreve/v3/pkg/util"
"github.com/gin-gonic/gin"
)
// QQCallbackService QQ互联回调处理服务
type QQCallbackService struct {
Code string `json:"code" binding:"required"`
State string `json:"state" binding:"required"`
}
// Callback 处理QQ互联回调
func (service *QQCallbackService) Callback(c *gin.Context, user *model.User) serializer.Response {
state := util.GetSession(c, "qq_login_secret")
if stateStr, ok := state.(string); !ok || stateStr != service.State {
return serializer.Err(serializer.CodeSignExpired, "", nil)
}
util.DeleteSession(c, "qq_login_secret")
// 获取OpenID
credential, err := qq.Callback(service.Code)
if err != nil {
return serializer.Err(serializer.CodeInternalSetting, "Failed to get session status", err)
}
// 如果已登录,则绑定已有用户
if user != nil {
if user.OpenID != "" {
return serializer.Err(serializer.CodeQQBindConflict, "", nil)
}
// OpenID 是否重复
if _, err := model.GetActiveUserByOpenID(credential.OpenID); err == nil {
return serializer.Err(serializer.CodeQQBindOtherAccount, "", nil)
}
if err := user.Update(map[string]interface{}{"open_id": credential.OpenID}); err != nil {
return serializer.DBErr("Failed to update user open id", err)
}
return serializer.Response{
Data: "/setting",
}
}
// 未登录,尝试查找用户
if expectedUser, err := model.GetActiveUserByOpenID(credential.OpenID); err == nil {
// 用户绑定了此QQ设定为登录状态
util.SetSession(c, map[string]interface{}{
"user_id": expectedUser.ID,
})
res := serializer.BuildUserResponse(expectedUser)
res.Code = 203
return res
}
// 无匹配用户,创建新用户
if !model.IsTrueVal(model.GetSettingByName("qq_direct_login")) {
return serializer.Err(serializer.CodeQQNotLinked, "", nil)
}
// 获取用户信息
userInfo, err := qq.GetUserInfo(credential)
if err != nil {
return serializer.Err(serializer.CodeInternalSetting, "Failed to fetch user info", err)
}
// 生成邮箱地址
fakeEmail := util.RandStringRunes(16) + "@login.qq.com"
// 创建用户
defaultGroup := model.GetIntSetting("default_group", 2)
newUser := model.NewUser()
newUser.Email = fakeEmail
newUser.Nick = userInfo.Nick
newUser.SetPassword("")
newUser.Status = model.Active
newUser.GroupID = uint(defaultGroup)
newUser.OpenID = credential.OpenID
newUser.Avatar = "file"
// 创建用户
if err := model.DB.Create(&newUser).Error; err != nil {
return serializer.Err(serializer.CodeEmailExisted, "", err)
}
// 下载头像
r := request.NewClient()
rawAvatar := r.Request("GET", userInfo.Avatar, nil)
if avatar, err := thumb.NewThumbFromFile(rawAvatar.Response.Body, "avatar.jpg"); err == nil {
avatar.CreateAvatar(newUser.ID)
}
// 登录
util.SetSession(c, map[string]interface{}{"user_id": newUser.ID})
newUser, _ = model.GetActiveUserByID(newUser.ID)
res := serializer.BuildUserResponse(newUser)
res.Code = 203
return res
}

17
service/vas/quota.go Normal file
View File

@ -0,0 +1,17 @@
package vas
import (
model "github.com/cloudreve/Cloudreve/v3/models"
"github.com/cloudreve/Cloudreve/v3/pkg/serializer"
"github.com/gin-gonic/gin"
)
// GeneralVASService 通用增值服务
type GeneralVASService struct {
}
// Quota 获取容量配额信息
func (service *GeneralVASService) Quota(c *gin.Context, user *model.User) serializer.Response {
packs := user.GetAvailableStoragePacks()
return serializer.BuildUserQuotaResponse(user, packs)
}