540 lines
16 KiB
Go
540 lines
16 KiB
Go
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
|
|
|
//go:build plus
|
|
// +build plus
|
|
|
|
package services
|
|
|
|
import (
|
|
"context"
|
|
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
|
"github.com/TeaOSLab/EdgeAPI/internal/db/models/accounts"
|
|
"github.com/TeaOSLab/EdgeAPI/internal/errors"
|
|
"github.com/TeaOSLab/EdgeAPI/internal/utils"
|
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
|
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
|
|
"github.com/iwind/TeaGo/dbs"
|
|
"github.com/iwind/TeaGo/maps"
|
|
"github.com/iwind/TeaGo/types"
|
|
timeutil "github.com/iwind/TeaGo/utils/time"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// UserPlanService 用户购买的套餐
|
|
type UserPlanService struct {
|
|
BaseService
|
|
}
|
|
|
|
// BuyUserPlan 添加已购套餐
|
|
func (this *UserPlanService) BuyUserPlan(ctx context.Context, req *pb.BuyUserPlanRequest) (*pb.BuyUserPlanResponse, error) {
|
|
_, userId, err := this.ValidateAdminAndUser(ctx, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if userId > 0 {
|
|
req.UserId = userId
|
|
}
|
|
|
|
if req.CountPeriod <= 0 {
|
|
req.CountPeriod = 1
|
|
}
|
|
|
|
var userPlanId int64
|
|
err = this.RunTx(func(tx *dbs.Tx) error {
|
|
// 套餐
|
|
plan, err := models.SharedPlanDAO.FindEnabledPlan(tx, req.PlanId, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if plan == nil {
|
|
return errors.New("can not find plan with id '" + types.String(req.PlanId) + "'")
|
|
}
|
|
|
|
// 周期
|
|
var dayTo = req.DayTo
|
|
if plan.PriceType == serverconfigs.PlanPriceTypePeriod {
|
|
var cost float64
|
|
var periodDescription string
|
|
switch req.Period {
|
|
case "monthly":
|
|
dayTo = timeutil.Format("Y-m-d", time.Now().AddDate(0, int(req.CountPeriod), 0))
|
|
cost = plan.MonthlyPrice * float64(req.CountPeriod)
|
|
periodDescription = types.String(req.CountPeriod) + "个月"
|
|
case "seasonally":
|
|
dayTo = timeutil.Format("Y-m-d", time.Now().AddDate(0, int(req.CountPeriod*3), 0))
|
|
cost = plan.SeasonallyPrice * float64(req.CountPeriod)
|
|
periodDescription = types.String(req.CountPeriod) + "个季度"
|
|
case "yearly":
|
|
dayTo = timeutil.Format("Y-m-d", time.Now().AddDate(int(req.CountPeriod), 0, 0))
|
|
cost = plan.YearlyPrice * float64(req.CountPeriod)
|
|
periodDescription = types.String(req.CountPeriod) + "年"
|
|
default:
|
|
return errors.New("invalid period '" + req.Period + "'")
|
|
}
|
|
|
|
// 用户账户
|
|
account, err := accounts.SharedUserAccountDAO.FindUserAccountWithUserId(tx, req.UserId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if account == nil {
|
|
return errors.New("can not find account for user '" + types.String(req.UserId) + "'")
|
|
}
|
|
|
|
if account.Total < cost {
|
|
return errors.New("not enough quota to buy")
|
|
}
|
|
|
|
// 扣费
|
|
err = accounts.SharedUserAccountDAO.UpdateUserAccount(tx, int64(account.Id), -cost, userconfigs.AccountEventTypeBuyPlan, "购买套餐:"+types.String(plan.Name)+"/"+periodDescription+"/到期时间:"+dayTo, maps.Map{"planId": plan.Id})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else if plan.PriceType == serverconfigs.PlanPriceTypeTraffic {
|
|
// DO NOTHING
|
|
} else if plan.PriceType == serverconfigs.PlanPriceTypeBandwidth {
|
|
// DO NOTHING
|
|
} else {
|
|
return errors.New("price type '" + plan.PriceType + "' is not supported yet")
|
|
}
|
|
|
|
userPlanId, err = models.SharedUserPlanDAO.CreateUserPlan(tx, req.UserId, req.PlanId, req.Name, dayTo)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &pb.BuyUserPlanResponse{UserPlanId: userPlanId}, nil
|
|
}
|
|
|
|
// RenewUserPlan 续费套餐
|
|
func (this *UserPlanService) RenewUserPlan(ctx context.Context, req *pb.RenewUserPlanRequest) (*pb.RPCSuccess, error) {
|
|
_, userId, err := this.ValidateAdminAndUser(ctx, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if req.CountPeriod <= 0 {
|
|
req.CountPeriod = 1
|
|
}
|
|
|
|
var tx = this.NullTx()
|
|
|
|
if userId > 0 {
|
|
err = models.SharedUserPlanDAO.CheckUserPlan(tx, userId, req.UserPlanId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
userPlan, err := models.SharedUserPlanDAO.FindEnabledUserPlan(tx, req.UserPlanId, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if userPlan == nil || userPlan.State != models.UserPlanStateEnabled {
|
|
return nil, errors.New("can not find user plan to renew")
|
|
}
|
|
|
|
var planId = int64(userPlan.PlanId)
|
|
userId = int64(userPlan.UserId)
|
|
|
|
// 套餐
|
|
plan, err := models.SharedPlanDAO.FindEnabledPlan(tx, planId, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if plan == nil {
|
|
return nil, errors.New("can not find plan with id '" + types.String(planId) + "'")
|
|
}
|
|
|
|
if len(userPlan.DayTo) == 0 {
|
|
userPlan.DayTo = timeutil.Format("Y-m-d")
|
|
}
|
|
var pieces = strings.Split(userPlan.DayTo, "-")
|
|
if len(pieces) != 3 {
|
|
return nil, errors.New("invalid 'dayTo': " + userPlan.DayTo)
|
|
}
|
|
var year = types.Int(pieces[0])
|
|
var month = types.Int(pieces[1])
|
|
var day = types.Int(pieces[2])
|
|
var startTime = time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.Local).AddDate(0, 0, 1)
|
|
|
|
err = this.RunTx(func(tx *dbs.Tx) error {
|
|
// 周期
|
|
var dayTo = req.DayTo
|
|
if plan.PriceType == serverconfigs.PlanPriceTypePeriod {
|
|
var cost float64
|
|
var periodDescription string
|
|
switch req.Period {
|
|
case "monthly":
|
|
dayTo = timeutil.Format("Y-m-d", startTime.AddDate(0, int(req.CountPeriod), 0))
|
|
cost = plan.MonthlyPrice * float64(req.CountPeriod)
|
|
periodDescription = types.String(req.CountPeriod) + "个月"
|
|
case "seasonally":
|
|
dayTo = timeutil.Format("Y-m-d", startTime.AddDate(0, int(req.CountPeriod*3), 0))
|
|
cost = plan.SeasonallyPrice * float64(req.CountPeriod)
|
|
periodDescription = types.String(req.CountPeriod) + "个季度"
|
|
case "yearly":
|
|
dayTo = timeutil.Format("Y-m-d", startTime.AddDate(int(req.CountPeriod), 0, 0))
|
|
cost = plan.YearlyPrice * float64(req.CountPeriod)
|
|
periodDescription = types.String(req.CountPeriod) + "年"
|
|
default:
|
|
return errors.New("invalid period '" + req.Period + "'")
|
|
}
|
|
|
|
// 用户账户
|
|
account, err := accounts.SharedUserAccountDAO.FindUserAccountWithUserId(tx, userId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if account == nil {
|
|
return errors.New("can not find account for user '" + types.String(userId) + "'")
|
|
}
|
|
|
|
if account.Total < cost {
|
|
return errors.New("not enough quota to buy")
|
|
}
|
|
|
|
// 扣费
|
|
err = accounts.SharedUserAccountDAO.UpdateUserAccount(tx, int64(account.Id), -cost, userconfigs.AccountEventTypeBuyPlan, "续费套餐:"+types.String(plan.Name)+"/"+periodDescription+"/到期时间:"+dayTo, maps.Map{"planId": plan.Id})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else if plan.PriceType == serverconfigs.PlanPriceTypeTraffic {
|
|
// DO NOTHING
|
|
} else if plan.PriceType == serverconfigs.PlanPriceTypeBandwidth {
|
|
// DO NOTHING
|
|
} else {
|
|
return errors.New("price type '" + plan.PriceType + "' is not supported yet")
|
|
}
|
|
|
|
err = models.SharedUserPlanDAO.UpdateUserPlanDayTo(tx, req.UserPlanId, dayTo)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return this.Success()
|
|
}
|
|
|
|
// FindEnabledUserPlan 查找单个已购套餐信息
|
|
func (this *UserPlanService) FindEnabledUserPlan(ctx context.Context, req *pb.FindEnabledUserPlanRequest) (*pb.FindEnabledUserPlanResponse, error) {
|
|
_, userId, err := this.ValidateAdminAndUser(ctx, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var tx = this.NullTx()
|
|
|
|
if userId > 0 {
|
|
err = models.SharedUserPlanDAO.CheckUserPlan(tx, userId, req.UserPlanId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
userPlan, err := models.SharedUserPlanDAO.FindEnabledUserPlan(tx, req.UserPlanId, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if userPlan == nil {
|
|
return &pb.FindEnabledUserPlanResponse{UserPlan: nil}, nil
|
|
}
|
|
|
|
// user
|
|
var pbUser = &pb.User{Id: int64(userPlan.UserId)}
|
|
user, err := models.SharedUserDAO.FindEnabledUser(tx, int64(userPlan.UserId), nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if user != nil {
|
|
pbUser = &pb.User{
|
|
Id: int64(user.Id),
|
|
Username: user.Username,
|
|
Fullname: user.Fullname,
|
|
}
|
|
}
|
|
|
|
// plan
|
|
var pbPlan = &pb.Plan{Id: int64(userPlan.PlanId)}
|
|
plan, err := models.SharedPlanDAO.FindEnabledPlan(tx, int64(userPlan.PlanId), nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if plan != nil {
|
|
pbPlan = &pb.Plan{
|
|
Id: int64(plan.Id),
|
|
Name: plan.Name,
|
|
TotalServers: types.Int32(plan.TotalServers),
|
|
TotalServerNames: types.Int32(plan.TotalServerNames),
|
|
TotalServerNamesPerServer: types.Int32(plan.TotalServerNamesPerServer),
|
|
DailyRequests: int64(plan.DailyRequests),
|
|
MonthlyRequests: int64(plan.MonthlyRequests),
|
|
}
|
|
}
|
|
|
|
return &pb.FindEnabledUserPlanResponse{UserPlan: &pb.UserPlan{
|
|
Id: int64(userPlan.Id),
|
|
UserId: int64(userPlan.UserId),
|
|
PlanId: int64(userPlan.PlanId),
|
|
IsOn: userPlan.IsOn,
|
|
Name: userPlan.Name,
|
|
DayTo: userPlan.DayTo,
|
|
User: pbUser,
|
|
Plan: pbPlan,
|
|
}}, nil
|
|
}
|
|
|
|
// UpdateUserPlan 修改已购套餐
|
|
func (this *UserPlanService) UpdateUserPlan(ctx context.Context, req *pb.UpdateUserPlanRequest) (*pb.RPCSuccess, error) {
|
|
_, err := this.ValidateAdmin(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var tx = this.NullTx()
|
|
err = models.SharedUserPlanDAO.UpdateUserPlan(tx, req.UserPlanId, req.PlanId, req.Name, req.DayTo, req.IsOn)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return this.Success()
|
|
}
|
|
|
|
// DeleteUserPlan 删除已购套餐
|
|
func (this *UserPlanService) DeleteUserPlan(ctx context.Context, req *pb.DeleteUserPlanRequest) (*pb.RPCSuccess, error) {
|
|
_, userId, err := this.ValidateAdminAndUser(ctx, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var tx = this.NullTx()
|
|
|
|
if userId > 0 {
|
|
// 检查用户套餐
|
|
err = models.SharedUserPlanDAO.CheckUserPlan(tx, userId, req.UserPlanId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
err = models.SharedUserPlanDAO.DisableUserPlan(tx, req.UserPlanId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return this.Success()
|
|
}
|
|
|
|
// CountAllEnabledUserPlans 计算已购套餐数
|
|
func (this *UserPlanService) CountAllEnabledUserPlans(ctx context.Context, req *pb.CountAllEnabledUserPlansRequest) (*pb.RPCCountResponse, error) {
|
|
_, userId, err := this.ValidateAdminAndUser(ctx, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if userId > 0 {
|
|
req.UserId = userId
|
|
}
|
|
|
|
var tx = this.NullTx()
|
|
count, err := models.SharedUserPlanDAO.CountAllEnabledUserPlans(tx, req.UserId, req.IsAvailable, req.IsExpired, req.ExpiringDays)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return this.SuccessCount(count)
|
|
}
|
|
|
|
// ListEnabledUserPlans 列出单页已购套餐
|
|
func (this *UserPlanService) ListEnabledUserPlans(ctx context.Context, req *pb.ListEnabledUserPlansRequest) (*pb.ListEnabledUserPlansResponse, error) {
|
|
_, userId, err := this.ValidateAdminAndUser(ctx, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if userId > 0 {
|
|
req.UserId = userId
|
|
}
|
|
|
|
var tx = this.NullTx()
|
|
userPlans, err := models.SharedUserPlanDAO.ListEnabledUserPlans(tx, req.UserId, req.IsAvailable, req.IsExpired, req.ExpiringDays, req.Offset, req.Size)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var pbUserPlans = []*pb.UserPlan{}
|
|
var cacheMap = utils.NewCacheMap()
|
|
for _, userPlan := range userPlans {
|
|
// user
|
|
var pbUser = &pb.User{Id: int64(userPlan.UserId)}
|
|
user, err := models.SharedUserDAO.FindEnabledUser(tx, int64(userPlan.UserId), cacheMap)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if user != nil {
|
|
pbUser = &pb.User{
|
|
Id: int64(user.Id),
|
|
Username: user.Username,
|
|
Fullname: user.Fullname,
|
|
}
|
|
}
|
|
|
|
// plan
|
|
var pbPlan = &pb.Plan{Id: int64(userPlan.PlanId)}
|
|
plan, err := models.SharedPlanDAO.FindEnabledPlan(tx, int64(userPlan.PlanId), cacheMap)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if plan != nil {
|
|
pbPlan = &pb.Plan{
|
|
Id: int64(plan.Id),
|
|
Name: plan.Name,
|
|
PriceType: plan.PriceType,
|
|
TrafficLimitJSON: plan.TrafficLimit,
|
|
BandwidthLimitPerNodeJSON: plan.BandwidthLimitPerNode,
|
|
TrafficPriceJSON: plan.TrafficPrice,
|
|
BandwidthPriceJSON: plan.BandwidthPrice,
|
|
MonthlyPrice: plan.MonthlyPrice,
|
|
SeasonallyPrice: plan.SeasonallyPrice,
|
|
YearlyPrice: plan.YearlyPrice,
|
|
TotalServers: types.Int32(plan.TotalServers),
|
|
TotalServerNames: types.Int32(plan.TotalServerNames),
|
|
TotalServerNamesPerServer: types.Int32(plan.TotalServerNamesPerServer),
|
|
DailyRequests: int64(plan.DailyRequests),
|
|
MonthlyRequests: int64(plan.MonthlyRequests),
|
|
DailyWebsocketConnections: int64(plan.DailyWebsocketConnections),
|
|
MonthlyWebsocketConnections: int64(plan.MonthlyWebsocketConnections),
|
|
}
|
|
}
|
|
|
|
// server
|
|
var pbServer *pb.Server
|
|
var pbServers []*pb.Server
|
|
servers, err := models.SharedServerDAO.FindEnabledServersWithUserPlanId(tx, int64(userPlan.Id))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(servers) > 0 {
|
|
// 兼容以往版本
|
|
var firstServer = servers[0]
|
|
pbServer = &pb.Server{
|
|
Id: int64(firstServer.Id),
|
|
Name: firstServer.Name,
|
|
ServerNamesJSON: firstServer.ServerNames,
|
|
Type: firstServer.Type,
|
|
}
|
|
|
|
// 列表
|
|
for _, server := range servers {
|
|
pbServers = append(pbServers, &pb.Server{
|
|
Id: int64(server.Id),
|
|
Name: server.Name,
|
|
ServerNamesJSON: server.ServerNames,
|
|
Type: server.Type,
|
|
})
|
|
}
|
|
}
|
|
|
|
pbUserPlans = append(pbUserPlans, &pb.UserPlan{
|
|
Id: int64(userPlan.Id),
|
|
UserId: int64(userPlan.UserId),
|
|
PlanId: int64(userPlan.PlanId),
|
|
IsOn: userPlan.IsOn,
|
|
Name: userPlan.Name,
|
|
DayTo: userPlan.DayTo,
|
|
User: pbUser,
|
|
Plan: pbPlan,
|
|
Server: pbServer,
|
|
Servers: pbServers,
|
|
})
|
|
}
|
|
|
|
return &pb.ListEnabledUserPlansResponse{UserPlans: pbUserPlans}, nil
|
|
}
|
|
|
|
// FindAllEnabledUserPlansForServer 查找当前网站所有可用的套餐
|
|
func (this *UserPlanService) FindAllEnabledUserPlansForServer(ctx context.Context, req *pb.FindAllEnabledUserPlansForServerRequest) (*pb.FindAllEnabledUserPlansForServerResponse, error) {
|
|
_, userId, err := this.ValidateAdminAndUser(ctx, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var tx = this.NullTx()
|
|
|
|
if userId > 0 && req.ServerId > 0 {
|
|
// 检查服务
|
|
err = models.SharedServerDAO.CheckUserServer(tx, userId, req.ServerId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
req.UserId = userId
|
|
}
|
|
|
|
userPlans, err := models.SharedUserPlanDAO.FindAllEnabledPlansForServer(tx, req.UserId, req.ServerId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var pbUserPlans = []*pb.UserPlan{}
|
|
var cacheMap = utils.NewCacheMap()
|
|
for _, userPlan := range userPlans {
|
|
// user
|
|
var pbUser = &pb.User{Id: int64(userPlan.UserId)}
|
|
user, err := models.SharedUserDAO.FindEnabledUser(tx, int64(userPlan.UserId), cacheMap)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if user != nil {
|
|
pbUser = &pb.User{
|
|
Id: int64(user.Id),
|
|
Username: user.Username,
|
|
Fullname: user.Fullname,
|
|
}
|
|
}
|
|
|
|
// plan
|
|
var pbPlan = &pb.Plan{Id: int64(userPlan.PlanId)}
|
|
plan, err := models.SharedPlanDAO.FindEnabledPlan(tx, int64(userPlan.PlanId), cacheMap)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if plan != nil {
|
|
pbPlan = &pb.Plan{
|
|
Id: int64(plan.Id),
|
|
Name: plan.Name,
|
|
TotalServers: types.Int32(plan.TotalServers),
|
|
TotalServerNames: types.Int32(plan.TotalServerNames),
|
|
TotalServerNamesPerServer: types.Int32(plan.TotalServerNamesPerServer),
|
|
DailyRequests: int64(plan.DailyRequests),
|
|
MonthlyRequests: int64(plan.MonthlyRequests),
|
|
}
|
|
}
|
|
|
|
pbUserPlans = append(pbUserPlans, &pb.UserPlan{
|
|
Id: int64(userPlan.Id),
|
|
UserId: int64(userPlan.UserId),
|
|
PlanId: int64(userPlan.PlanId),
|
|
Name: userPlan.Name,
|
|
IsOn: userPlan.IsOn,
|
|
DayTo: userPlan.DayTo,
|
|
User: pbUser,
|
|
Plan: pbPlan,
|
|
})
|
|
}
|
|
return &pb.FindAllEnabledUserPlansForServerResponse{UserPlans: pbUserPlans}, nil
|
|
}
|