refactor(api): remove deprecated application-related endpoints and types

This commit is contained in:
Chang lue Tsen
2025-08-17 12:14:55 -04:00
parent 14a86c50b2
commit 8996a62b54
118 changed files with 6 additions and 7156 deletions
@@ -1,125 +0,0 @@
package system
import (
"context"
"github.com/perfect-panel/server/internal/model/application"
"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 CreateApplicationLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewCreateApplicationLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateApplicationLogic {
return &CreateApplicationLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *CreateApplicationLogic) CreateApplication(req *types.CreateApplicationRequest) error {
var ios []application.ApplicationVersion
if len(req.Platform.IOS) > 0 {
for _, ios_ := range req.Platform.IOS {
ios = append(ios, application.ApplicationVersion{
Url: ios_.Url,
Version: ios_.Version,
Platform: "ios",
IsDefault: ios_.IsDefault,
Description: ios_.Description,
})
}
}
var mac []application.ApplicationVersion
if len(req.Platform.MacOS) > 0 {
for _, mac_ := range req.Platform.MacOS {
mac = append(mac, application.ApplicationVersion{
Url: mac_.Url,
Version: mac_.Version,
Platform: "macos",
IsDefault: mac_.IsDefault,
Description: mac_.Description,
})
}
}
var linux []application.ApplicationVersion
if len(req.Platform.Linux) > 0 {
for _, linux_ := range req.Platform.Linux {
linux = append(linux, application.ApplicationVersion{
Url: linux_.Url,
Version: linux_.Version,
Platform: "linux",
IsDefault: linux_.IsDefault,
Description: linux_.Description,
})
}
}
var android []application.ApplicationVersion
if len(req.Platform.Android) > 0 {
for _, android_ := range req.Platform.Android {
android = append(android, application.ApplicationVersion{
Url: android_.Url,
Version: android_.Version,
Platform: "android",
IsDefault: android_.IsDefault,
Description: android_.Description,
})
}
}
var windows []application.ApplicationVersion
if len(req.Platform.Windows) > 0 {
for _, windows_ := range req.Platform.Windows {
windows = append(windows, application.ApplicationVersion{
Url: windows_.Url,
Version: windows_.Version,
Platform: "windows",
IsDefault: windows_.IsDefault,
Description: windows_.Description,
})
}
}
var harmony []application.ApplicationVersion
if len(req.Platform.Harmony) > 0 {
for _, harmony_ := range req.Platform.Harmony {
harmony = append(harmony, application.ApplicationVersion{
Url: harmony_.Url,
Version: harmony_.Version,
Platform: "harmony",
IsDefault: harmony_.IsDefault,
Description: harmony_.Description,
})
}
}
var applicationVersions []application.ApplicationVersion
applicationVersions = append(applicationVersions, ios...)
applicationVersions = append(applicationVersions, mac...)
applicationVersions = append(applicationVersions, linux...)
applicationVersions = append(applicationVersions, android...)
applicationVersions = append(applicationVersions, windows...)
applicationVersions = append(applicationVersions, harmony...)
app := application.Application{
Name: req.Name,
Icon: req.Icon,
SubscribeType: req.SubscribeType,
ApplicationVersions: applicationVersions,
}
err := l.svcCtx.ApplicationModel.Insert(l.ctx, &app)
if err != nil {
l.Errorw("[CreateApplicationLogic] create application error: ", logger.Field("error", err.Error()))
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "create application error: %v", err)
}
return nil
}
@@ -1,44 +0,0 @@
package system
import (
"context"
"github.com/perfect-panel/server/internal/model/application"
"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 CreateApplicationVersionLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// Create application version
func NewCreateApplicationVersionLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateApplicationVersionLogic {
return &CreateApplicationVersionLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *CreateApplicationVersionLogic) CreateApplicationVersion(req *types.CreateApplicationVersionRequest) error {
create := &application.ApplicationVersion{
Url: req.Url,
Platform: req.Platform,
Version: req.Version,
Description: req.Description,
IsDefault: req.IsDefault,
ApplicationId: req.ApplicationId,
}
err := l.svcCtx.ApplicationModel.InsertVersion(l.ctx, create)
if err != nil {
l.Errorw("[CreateApplicationVersion] create application version error: ", logger.Field("error", err.Error()))
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "create application version error: %v", err)
}
return nil
}
@@ -1,35 +0,0 @@
package system
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 DeleteApplicationLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewDeleteApplicationLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteApplicationLogic {
return &DeleteApplicationLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *DeleteApplicationLogic) DeleteApplication(req *types.DeleteApplicationRequest) error {
// delete application
err := l.svcCtx.ApplicationModel.Delete(l.ctx, req.Id)
if err != nil {
l.Errorw("[DeleteApplicationLogic] delete application error: ", logger.Field("error", err.Error()))
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseDeletedError), "delete application error: %v", err.Error())
}
return nil
}
@@ -1,36 +0,0 @@
package system
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 DeleteApplicationVersionLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// Delete application
func NewDeleteApplicationVersionLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteApplicationVersionLogic {
return &DeleteApplicationVersionLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *DeleteApplicationVersionLogic) DeleteApplicationVersion(req *types.DeleteApplicationVersionRequest) error {
// delete application
err := l.svcCtx.ApplicationModel.DeleteVersion(l.ctx, req.Id)
if err != nil {
l.Errorw("[DeleteApplicationVersion] delete application version error: ", logger.Field("error", err.Error()))
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseDeletedError), "delete application version error: %v", err.Error())
}
return nil
}
@@ -1,49 +0,0 @@
package system
import (
"context"
"strings"
"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 GetApplicationConfigLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// get application config
func NewGetApplicationConfigLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetApplicationConfigLogic {
return &GetApplicationConfigLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetApplicationConfigLogic) GetApplicationConfig() (resp *types.ApplicationConfig, err error) {
resp = &types.ApplicationConfig{}
appConfig, err := l.svcCtx.ApplicationModel.FindOneConfig(l.ctx, 1)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
err = nil
return
}
l.Errorw("[GetApplicationConfig] Database Error", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "get app config error: %v", err.Error())
}
resp.AppId = appConfig.AppId
resp.EncryptionKey = appConfig.EncryptionKey
resp.EncryptionMethod = appConfig.EncryptionMethod
resp.Domains = strings.Split(appConfig.Domains, ";")
resp.StartupPicture = appConfig.StartupPicture
resp.StartupPictureSkipTime = appConfig.StartupPictureSkipTime
return
}
@@ -1,113 +0,0 @@
package system
import (
"context"
"github.com/perfect-panel/server/internal/model/application"
"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 GetApplicationLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// Get application
func NewGetApplicationLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetApplicationLogic {
return &GetApplicationLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetApplicationLogic) GetApplication() (resp *types.ApplicationResponse, err error) {
resp = &types.ApplicationResponse{}
var applications []*application.Application
err = l.svcCtx.ApplicationModel.Transaction(l.ctx, func(tx *gorm.DB) (err error) {
return tx.Model(applications).Preload("ApplicationVersions").Find(&applications).Error
})
if err != nil {
l.Errorw("[GetApplicationLogic] get application error: ", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "get application error: %v", err.Error())
}
if len(applications) == 0 {
return resp, nil
}
for _, app := range applications {
applicationResponse := types.ApplicationResponseInfo{
Id: app.Id,
Name: app.Name,
Icon: app.Icon,
Description: app.Description,
SubscribeType: app.SubscribeType,
}
applicationVersions := app.ApplicationVersions
if len(applicationVersions) != 0 {
for _, applicationVersion := range applicationVersions {
switch applicationVersion.Platform {
case "ios":
applicationResponse.Platform.IOS = append(applicationResponse.Platform.IOS, &types.ApplicationVersion{
Id: applicationVersion.Id,
Url: applicationVersion.Url,
Version: applicationVersion.Version,
IsDefault: applicationVersion.IsDefault,
Description: applicationVersion.Description,
})
case "macos":
applicationResponse.Platform.MacOS = append(applicationResponse.Platform.MacOS, &types.ApplicationVersion{
Id: applicationVersion.Id,
Url: applicationVersion.Url,
Version: applicationVersion.Version,
IsDefault: applicationVersion.IsDefault,
Description: applicationVersion.Description,
})
case "linux":
applicationResponse.Platform.Linux = append(applicationResponse.Platform.Linux, &types.ApplicationVersion{
Id: applicationVersion.Id,
Url: applicationVersion.Url,
Version: applicationVersion.Version,
IsDefault: applicationVersion.IsDefault,
Description: applicationVersion.Description,
})
case "android":
applicationResponse.Platform.Android = append(applicationResponse.Platform.Android, &types.ApplicationVersion{
Id: applicationVersion.Id,
Url: applicationVersion.Url,
Version: applicationVersion.Version,
IsDefault: applicationVersion.IsDefault,
Description: applicationVersion.Description,
})
case "windows":
applicationResponse.Platform.Windows = append(applicationResponse.Platform.Windows, &types.ApplicationVersion{
Id: applicationVersion.Id,
Url: applicationVersion.Url,
Version: applicationVersion.Version,
IsDefault: applicationVersion.IsDefault,
Description: applicationVersion.Description,
})
case "harmony":
applicationResponse.Platform.Harmony = append(applicationResponse.Platform.Harmony, &types.ApplicationVersion{
Id: applicationVersion.Id,
Url: applicationVersion.Url,
Version: applicationVersion.Version,
IsDefault: applicationVersion.IsDefault,
Description: applicationVersion.Description,
})
}
}
}
resp.Applications = append(resp.Applications, applicationResponse)
}
return
}
@@ -1,45 +0,0 @@
package system
import (
"context"
"strings"
"github.com/perfect-panel/server/internal/model/application"
"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 UpdateApplicationConfigLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// update application config
func NewUpdateApplicationConfigLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateApplicationConfigLogic {
return &UpdateApplicationConfigLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *UpdateApplicationConfigLogic) UpdateApplicationConfig(req *types.ApplicationConfig) error {
err := l.svcCtx.ApplicationModel.UpdateConfig(l.ctx, &application.ApplicationConfig{
Id: 1,
AppId: req.AppId,
EncryptionKey: req.EncryptionKey,
EncryptionMethod: req.EncryptionMethod,
Domains: strings.Join(req.Domains, ";"),
StartupPicture: req.StartupPicture,
StartupPictureSkipTime: req.StartupPictureSkipTime,
})
if err != nil {
l.Errorw("[UpdateApplicationConfig] Database Error", logger.Field("error", err.Error()))
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "update app config error: %v", err.Error())
}
return nil
}
@@ -1,149 +0,0 @@
package system
import (
"context"
"github.com/perfect-panel/server/internal/model/application"
"github.com/perfect-panel/server/pkg/logger"
"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"
)
type UpdateApplicationLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewUpdateApplicationLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateApplicationLogic {
return &UpdateApplicationLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *UpdateApplicationLogic) UpdateApplication(req *types.UpdateApplicationRequest) error {
// find application
app, err := l.svcCtx.ApplicationModel.FindOne(l.ctx, req.Id)
if err != nil {
l.Errorw("[UpdateApplication] find application error", logger.Field("error", err.Error()))
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find application error: %v", err.Error())
}
app.Name = req.Name
app.Icon = req.Icon
app.SubscribeType = req.SubscribeType
app.Description = req.Description
var ios []application.ApplicationVersion
if len(req.Platform.IOS) > 0 {
for _, ios_ := range req.Platform.IOS {
ios = append(ios, application.ApplicationVersion{
Url: ios_.Url,
Version: ios_.Version,
Platform: "ios",
IsDefault: ios_.IsDefault,
Description: ios_.Description,
ApplicationId: app.Id,
})
}
}
var mac []application.ApplicationVersion
if len(req.Platform.MacOS) > 0 {
for _, mac_ := range req.Platform.MacOS {
mac = append(mac, application.ApplicationVersion{
Url: mac_.Url,
Version: mac_.Version,
Platform: "macos",
IsDefault: mac_.IsDefault,
Description: mac_.Description,
ApplicationId: app.Id,
})
}
}
var linux []application.ApplicationVersion
if len(req.Platform.Linux) > 0 {
for _, linux_ := range req.Platform.Linux {
linux = append(linux, application.ApplicationVersion{
Url: linux_.Url,
Version: linux_.Version,
Platform: "linux",
IsDefault: linux_.IsDefault,
Description: linux_.Description,
ApplicationId: app.Id,
})
}
}
var android []application.ApplicationVersion
if len(req.Platform.Android) > 0 {
for _, android_ := range req.Platform.Android {
android = append(android, application.ApplicationVersion{
Url: android_.Url,
Version: android_.Version,
Platform: "android",
IsDefault: android_.IsDefault,
Description: android_.Description,
ApplicationId: app.Id,
})
}
}
var windows []application.ApplicationVersion
if len(req.Platform.Windows) > 0 {
for _, windows_ := range req.Platform.Windows {
windows = append(windows, application.ApplicationVersion{
Url: windows_.Url,
Version: windows_.Version,
Platform: "windows",
IsDefault: windows_.IsDefault,
Description: windows_.Description,
ApplicationId: app.Id,
})
}
}
var harmony []application.ApplicationVersion
if len(req.Platform.Harmony) > 0 {
for _, harmony_ := range req.Platform.Harmony {
harmony = append(harmony, application.ApplicationVersion{
Url: harmony_.Url,
Version: harmony_.Version,
Platform: "harmony",
IsDefault: harmony_.IsDefault,
Description: harmony_.Description,
ApplicationId: app.Id,
})
}
}
var applicationVersions []application.ApplicationVersion
applicationVersions = append(applicationVersions, ios...)
applicationVersions = append(applicationVersions, mac...)
applicationVersions = append(applicationVersions, linux...)
applicationVersions = append(applicationVersions, android...)
applicationVersions = append(applicationVersions, windows...)
applicationVersions = append(applicationVersions, harmony...)
app.ApplicationVersions = applicationVersions
err = l.svcCtx.ApplicationModel.Transaction(l.ctx, func(db *gorm.DB) error {
if err = db.Where("application_id = ?", app.Id).Delete(&application.ApplicationVersion{}).Error; err != nil {
return err
}
if err = db.Create(&applicationVersions).Error; err != nil {
return err
}
return db.Save(app).Error
})
if err != nil {
l.Errorw("[UpdateApplication] update application error", logger.Field("error", err.Error()))
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "update application error: %v", err.Error())
}
return nil
}
@@ -1,45 +0,0 @@
package system
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 UpdateApplicationVersionLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// Update application version
func NewUpdateApplicationVersionLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateApplicationVersionLogic {
return &UpdateApplicationVersionLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *UpdateApplicationVersionLogic) UpdateApplicationVersion(req *types.UpdateApplicationVersionRequest) error {
// find application
app, err := l.svcCtx.ApplicationModel.FindOneVersion(l.ctx, req.Id)
if err != nil {
l.Errorw("[UpdateApplicationVersion] find application version error", logger.Field("error", err.Error()))
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find application error: %v", err.Error())
}
app.Url = req.Url
app.Version = req.Version
app.Description = req.Description
app.IsDefault = req.IsDefault
err = l.svcCtx.ApplicationModel.UpdateVersion(l.ctx, app)
if err != nil {
l.Errorw("[UpdateApplicationVersion] update application version error", logger.Field("error", err.Error()))
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "update application version error: %v", err.Error())
}
return nil
}
@@ -1,47 +0,0 @@
package announcement
import (
"context"
"github.com/perfect-panel/server/internal/model/announcement"
"github.com/perfect-panel/server/pkg/tool"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/logger"
)
type QueryAnnouncementLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// NewQueryAnnouncementLogic Query announcement
func NewQueryAnnouncementLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryAnnouncementLogic {
return &QueryAnnouncementLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *QueryAnnouncementLogic) QueryAnnouncement(req *types.QueryAnnouncementRequest) (resp *types.QueryAnnouncementResponse, err error) {
enable := true
total, list, err := l.svcCtx.AnnouncementModel.GetAnnouncementListByPage(l.ctx, req.Page, req.Size, announcement.Filter{
Show: &enable,
Pinned: req.Pinned,
Popup: req.Popup,
})
if err != nil {
l.Error("[QueryAnnouncementLogic] GetAnnouncementListByPage error", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "GetAnnouncementListByPage error: %v", err.Error())
}
resp = &types.QueryAnnouncementResponse{}
resp.Total = total
resp.List = make([]types.Announcement, 0)
tool.DeepCopy(&resp.List, list)
return
}
-41
View File
@@ -1,41 +0,0 @@
package auth
import (
"context"
"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 CheckLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// Check Account
func NewCheckLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CheckLogic {
return &CheckLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *CheckLogic) Check(req *types.AppAuthCheckRequest) (resp *types.AppAuthCheckResponse, err error) {
resp = &types.AppAuthCheckResponse{}
_, err = findUserByMethod(l.ctx, l.svcCtx, req.Method, req.Identifier, req.Account, req.AreaCode)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
resp.Status = false
return resp, nil
}
return resp, err
}
resp.Status = true
return
}
@@ -1,59 +0,0 @@
package auth
import (
"context"
"github.com/perfect-panel/server/pkg/authmethod"
"github.com/perfect-panel/server/internal/model/user"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/pkg/phone"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
"gorm.io/gorm"
)
func findUserByMethod(ctx context.Context, svcCtx *svc.ServiceContext, method, identifier, account, areaCode string) (userInfo *user.User, err error) {
var authMethods *user.AuthMethods
switch method {
case authmethod.Email:
authMethods, err = svcCtx.UserModel.FindUserAuthMethodByOpenID(ctx, authmethod.Email, account)
case authmethod.Mobile:
phoneNumber, err := phone.FormatToE164(areaCode, account)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.TelephoneError), "Invalid phone number")
}
authMethods, err = svcCtx.UserModel.FindUserAuthMethodByOpenID(ctx, authmethod.Mobile, phoneNumber)
if err != nil {
return nil, err
}
case authmethod.Device:
userDevice, err := svcCtx.UserModel.FindOneDeviceByIdentifier(ctx, identifier)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, err
}
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query user device imei error")
}
return svcCtx.UserModel.FindOne(ctx, userDevice.UserId)
default:
return nil, errors.Wrapf(xerr.NewErrCode(xerr.UserNotExist), "unknown method")
}
if err != nil {
return nil, err
}
return svcCtx.UserModel.FindOne(ctx, authMethods.UserId)
}
func existError(method string) error {
switch method {
case authmethod.Email:
return errors.Wrapf(xerr.NewErrCode(xerr.EmailExist), "")
case authmethod.Mobile:
return errors.Wrapf(xerr.NewErrCode(xerr.TelephoneExist), "")
case authmethod.Device:
return errors.Wrapf(xerr.NewErrCode(xerr.DeviceExist), "")
default:
return errors.New("unknown method")
}
}
@@ -1,119 +0,0 @@
package auth
import (
"context"
"encoding/json"
"fmt"
"strings"
"github.com/perfect-panel/server/internal/model/application"
"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 GetAppConfigLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// GetAppConfig
func NewGetAppConfigLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetAppConfigLogic {
return &GetAppConfigLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetAppConfigLogic) GetAppConfig(req *types.AppConfigRequest) (resp *types.AppConfigResponse, err error) {
resp = &types.AppConfigResponse{}
systems, err := l.svcCtx.SystemModel.GetSiteConfig(l.ctx)
if err != nil {
l.Errorw("[QueryApplicationConfig] GetSiteConfig error: ", logger.Field("error", err.Error()))
}
for _, sysVal := range systems {
if sysVal.Key == "CustomData" {
jsonStr := strings.ReplaceAll(sysVal.Value, "\\", "")
customData := make(map[string]interface{})
if err = json.Unmarshal([]byte(jsonStr), &customData); err != nil {
break
}
website := customData["website"]
if website != nil {
resp.OfficialWebsite = fmt.Sprintf("%v", website)
}
contacts := customData["contacts"]
if contacts != nil {
contactsJson, err := json.Marshal(contacts)
if err == nil {
contactsMap := make(map[string]string)
err = json.Unmarshal(contactsJson, &contactsMap)
if err == nil {
resp.OfficialEmail = fmt.Sprintf("%v", contactsMap["email"])
resp.OfficialTelegram = fmt.Sprintf("%v", contactsMap["telegram"])
resp.OfficialTelephone = fmt.Sprintf("%v", contactsMap["telephone"])
}
}
}
break
}
}
var applications []*application.Application
err = l.svcCtx.ApplicationModel.Transaction(l.ctx, func(tx *gorm.DB) (err error) {
return tx.Model(applications).Preload("ApplicationVersions").Find(&applications).Error
})
if err != nil {
l.Errorw("[QueryApplicationConfig] get application error: ", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "get application error: %v", err.Error())
}
if len(applications) == 0 {
return resp, nil
}
isOk := false
for _, app := range applications {
if isOk {
break
}
resp.Application.Name = app.Name
resp.Application.Description = app.Description
applicationVersions := app.ApplicationVersions
if len(applicationVersions) != 0 {
for _, applicationVersion := range applicationVersions {
if applicationVersion.Platform == req.UserAgent {
resp.Application.Id = applicationVersion.ApplicationId
resp.Application.Url = applicationVersion.Url
resp.Application.Version = applicationVersion.Version
resp.Application.VersionDescription = applicationVersion.Description
resp.Application.IsDefault = applicationVersion.IsDefault
isOk = true
break
}
}
}
}
configs, err := l.svcCtx.ApplicationModel.FindOneConfig(l.ctx, 1)
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
l.Logger.Error("[GetAppInfo] FindOneAppConfig error: ", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "GetAppInfo FindOneAppConfig error: %v", err.Error())
}
resp.EncryptionKey = configs.EncryptionKey
resp.EncryptionMethod = configs.EncryptionMethod
resp.Domains = strings.Split(configs.Domains, ";")
resp.StartupPicture = configs.StartupPicture
resp.StartupPictureSkipTime = configs.StartupPictureSkipTime
resp.InvitationLink = configs.InvitationLink
resp.KrWebsiteId = configs.KrWebsiteId
return
}
-194
View File
@@ -1,194 +0,0 @@
package auth
import (
"encoding/json"
"fmt"
"time"
"github.com/perfect-panel/server/pkg/authmethod"
"github.com/gin-gonic/gin"
"github.com/perfect-panel/server/pkg/constant"
"github.com/perfect-panel/server/pkg/phone"
"github.com/perfect-panel/server/internal/config"
"github.com/perfect-panel/server/internal/logic/common"
"github.com/perfect-panel/server/internal/model/user"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/jwt"
"github.com/perfect-panel/server/pkg/logger"
"github.com/perfect-panel/server/pkg/tool"
"github.com/perfect-panel/server/pkg/uuidx"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
"gorm.io/gorm"
)
type LoginLogic struct {
logger.Logger
ctx *gin.Context
svcCtx *svc.ServiceContext
}
// Login
func NewLoginLogic(ctx *gin.Context, svcCtx *svc.ServiceContext) *LoginLogic {
return &LoginLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *LoginLogic) Login(req *types.AppAuthRequest) (resp *types.AppAuthRespone, err error) {
loginStatus := false
var userInfo *user.User
// Record login status
defer func(svcCtx *svc.ServiceContext) {
if userInfo != nil && userInfo.Id != 0 {
if err := svcCtx.UserModel.InsertLoginLog(l.ctx, &user.LoginLog{
UserId: userInfo.Id,
LoginIP: l.ctx.ClientIP(),
UserAgent: l.ctx.Request.UserAgent(),
Success: &loginStatus,
}); err != nil {
l.Errorw("InsertLoginLog Error", logger.Field("error", err.Error()))
}
}
}(l.svcCtx)
resp = &types.AppAuthRespone{}
//query user
userInfo, err = findUserByMethod(l.ctx, l.svcCtx, req.Method, req.Identifier, req.Account, req.AreaCode)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.UserPasswordError), "user password")
}
return resp, err
}
switch req.Method {
case authmethod.Email:
if !l.svcCtx.Config.Email.Enable {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.EmailNotEnabled), "Email function is not enabled yet")
}
if req.Code != "" {
cacheKey := fmt.Sprintf("%s:%s:%s", config.AuthCodeCacheKey, constant.Security.String(), req.Account)
value, err := l.svcCtx.Redis.Get(l.ctx, cacheKey).Result()
if err != nil {
l.Errorw("Redis Error", logger.Field("error", err.Error()), logger.Field("cacheKey", cacheKey))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.VerifyCodeError), "code error")
}
var payload common.CacheKeyPayload
err = json.Unmarshal([]byte(value), &payload)
if err != nil {
l.Errorw("Unmarshal Error", logger.Field("error", err.Error()), logger.Field("value", value))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.VerifyCodeError), "code error")
}
if payload.Code != req.Code {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.VerifyCodeError), "code error")
}
l.svcCtx.Redis.Del(l.ctx, cacheKey)
} else {
// Verify password
if !tool.VerifyPassWord(req.Password, userInfo.Password) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.UserPasswordError), "user password")
}
}
case authmethod.Mobile:
if !l.svcCtx.Config.Mobile.Enable {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SmsNotEnabled), "sms login is not enabled")
}
phoneNumber, err := phone.FormatToE164(req.AreaCode, req.Account)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.TelephoneError), "Invalid phone number")
}
if req.Code != "" {
cacheKey := fmt.Sprintf("%s:%s:%s", config.AuthCodeTelephoneCacheKey, constant.Security, phoneNumber)
value, err := l.svcCtx.Redis.Get(l.ctx, cacheKey).Result()
if err != nil {
l.Errorw("Redis Error", logger.Field("error", err.Error()), logger.Field("cacheKey", cacheKey))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.VerifyCodeError), "code error")
}
if value == "" {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.VerifyCodeError), "code error")
}
var payload common.CacheKeyPayload
if err := json.Unmarshal([]byte(value), &payload); err != nil {
l.Errorw("[SendSmsCode]: Unmarshal Error", logger.Field("error", err.Error()), logger.Field("value", value))
}
if payload.Code != req.Code {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.VerifyCodeError), "code error")
}
l.svcCtx.Redis.Del(l.ctx, cacheKey)
} else {
// Verify password
if !tool.VerifyPassWord(req.Password, userInfo.Password) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.UserPasswordError), "user password")
}
}
case authmethod.Device:
default:
return nil, existError(req.Method)
}
device, err := l.svcCtx.UserModel.FindOneDeviceByIdentifier(l.ctx, req.Identifier)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
if req.Method == authmethod.Device {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.UserNotExist), "device not exist")
}
//Add User Device
userInfo.UserDevices = append(userInfo.UserDevices, user.Device{
UserAgent: req.UserAgent,
Identifier: req.Identifier,
Ip: l.ctx.ClientIP(),
})
err = l.svcCtx.UserModel.Update(l.ctx, userInfo)
if err != nil {
l.Errorw("[UpdateUserBindDevice] Fail", logger.Field("error", err.Error()))
}
}
} else {
//Change the user who owns the device
if device.UserId != userInfo.Id {
device.UserId = userInfo.Id
}
device.Ip = l.ctx.ClientIP()
err = l.svcCtx.UserModel.UpdateDevice(l.ctx, device)
if err != nil {
l.Errorw("[UpdateUserBindDevice] Fail", logger.Field("error", err.Error()))
}
}
// Generate session id
sessionId := uuidx.NewUUID().String()
// Generate token
token, err := jwt.NewJwtToken(
l.svcCtx.Config.JwtAuth.AccessSecret,
time.Now().Unix(),
l.svcCtx.Config.JwtAuth.AccessExpire,
jwt.WithOption("UserId", userInfo.Id),
jwt.WithOption("SessionId", sessionId),
)
if err != nil {
l.Logger.Error("[UserLogin] token generate error", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "token generate error: %v", err.Error())
}
sessionIdCacheKey := fmt.Sprintf("%v:%v", config.SessionIdKey, sessionId)
if err = l.svcCtx.Redis.Set(l.ctx, sessionIdCacheKey, userInfo.Id, time.Duration(l.svcCtx.Config.JwtAuth.AccessExpire)*time.Second).Err(); err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "set session id error: %v", err.Error())
}
resp.Token = token
return
}
-249
View File
@@ -1,249 +0,0 @@
package auth
import (
"encoding/json"
"fmt"
"time"
"github.com/perfect-panel/server/pkg/authmethod"
"github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/config"
"github.com/perfect-panel/server/internal/logic/common"
"github.com/perfect-panel/server/internal/model/user"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/constant"
"github.com/perfect-panel/server/pkg/jwt"
"github.com/perfect-panel/server/pkg/logger"
"github.com/perfect-panel/server/pkg/phone"
"github.com/perfect-panel/server/pkg/tool"
"github.com/perfect-panel/server/pkg/uuidx"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
"gorm.io/gorm"
)
type CacheKeyPayload struct {
Code string `json:"code"`
LastAt int64 `json:"lastAt"`
}
type RegisterLogic struct {
logger.Logger
ctx *gin.Context
svcCtx *svc.ServiceContext
}
// Register
func NewRegisterLogic(ctx *gin.Context, svcCtx *svc.ServiceContext) *RegisterLogic {
return &RegisterLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *RegisterLogic) Register(req *types.AppAuthRequest) (resp *types.AppAuthRespone, err error) {
resp = &types.AppAuthRespone{}
var referer *user.User
c := l.svcCtx.Config.Register
// Check if the registration is stopped
if c.StopRegister {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.StopRegister), "stop register")
}
if req.Invite == "" {
if l.svcCtx.Config.Invite.ForcedInvite {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.InviteCodeError), "invite code is required")
}
} else {
// Check if the invite code is valid
referer, err = l.svcCtx.UserModel.FindOneByReferCode(l.ctx, req.Invite)
if err != nil {
l.Errorw("FindOneByReferCode Error", logger.Field("error", err))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.InviteCodeError), "invite code is invalid")
}
}
if req.Password == "" {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.PasswordIsEmpty), "Password required")
}
userInfo, err := findUserByMethod(l.ctx, l.svcCtx, req.Method, req.Identifier, req.Account, req.AreaCode)
if err == nil && userInfo != nil {
return nil, existError(req.Method)
}
if !errors.Is(err, gorm.ErrRecordNotFound) {
return nil, err
}
// Generate password
pwd := tool.EncodePassWord(req.Password)
userInfo = &user.User{
Password: pwd,
}
if referer != nil {
userInfo.RefererId = referer.Id
}
switch req.Method {
case authmethod.Email:
if !l.svcCtx.Config.Email.Enable {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.EmailNotEnabled), "Email function is not enabled yet")
}
if l.svcCtx.Config.Email.EnableVerify {
cacheKey := fmt.Sprintf("%s:%s:%s", config.AuthCodeCacheKey, constant.Register.String(), req.Account)
value, err := l.svcCtx.Redis.Get(l.ctx, cacheKey).Result()
if err != nil {
l.Errorw("Redis Error", logger.Field("error", err.Error()), logger.Field("cacheKey", cacheKey))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.VerifyCodeError), "code error")
}
var payload common.CacheKeyPayload
err = json.Unmarshal([]byte(value), &payload)
if err != nil {
l.Errorw("Unmarshal Error", logger.Field("error", err.Error()), logger.Field("value", value))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.VerifyCodeError), "code error")
}
if payload.Code != req.Code {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.VerifyCodeError), "code error")
}
}
userInfo.AuthMethods = []user.AuthMethods{{
AuthType: authmethod.Email,
AuthIdentifier: req.Account,
}}
case authmethod.Mobile:
if req.AreaCode == "" {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.TelephoneAreaCodeIsEmpty), "area code required")
}
if !l.svcCtx.Config.Mobile.Enable {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SmsNotEnabled), "sms login is not enabled")
}
phoneNumber, err := phone.FormatToE164(req.AreaCode, req.Account)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.TelephoneError), "Invalid phone number")
}
cacheKey := fmt.Sprintf("%s:%s:%s", config.AuthCodeTelephoneCacheKey, constant.Register, phoneNumber)
value, err := l.svcCtx.Redis.Get(l.ctx, cacheKey).Result()
if err != nil {
l.Errorw("Redis Error", logger.Field("error", err.Error()), logger.Field("cacheKey", cacheKey))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.VerifyCodeError), "code error")
}
var payload CacheKeyPayload
_ = json.Unmarshal([]byte(value), &payload)
if payload.Code != req.Code {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.VerifyCodeError), "code error")
}
userInfo.AuthMethods = []user.AuthMethods{{
AuthType: authmethod.Mobile,
AuthIdentifier: phoneNumber,
Verified: true,
}}
case authmethod.Device:
oneDevice, err := l.svcCtx.UserModel.FindOneDeviceByIdentifier(l.ctx, req.Identifier)
if err == nil && oneDevice != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DeviceExist), "device exist")
}
default:
return nil, existError(req.Method)
}
device, err := l.svcCtx.UserModel.FindOneDeviceByIdentifier(l.ctx, req.Identifier)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
//Add User Device
userInfo.UserDevices = append(userInfo.UserDevices, user.Device{
Ip: l.ctx.ClientIP(),
Identifier: req.Identifier,
UserAgent: req.UserAgent,
})
} else {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query user info failed: %v", err.Error())
}
} else {
//Delete Other User Device
err = l.svcCtx.UserModel.DeleteDevice(l.ctx, device.Id)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "delete old user device failed: %v", err.Error())
} else {
//User Add Device
userInfo.UserDevices = append(userInfo.UserDevices, user.Device{
Ip: l.ctx.ClientIP(),
Identifier: req.Identifier,
UserAgent: req.UserAgent,
})
}
}
err = l.svcCtx.UserModel.Transaction(l.ctx, func(db *gorm.DB) error {
// Save user information
if err := db.Create(userInfo).Error; err != nil {
return err
}
// Generate ReferCode
userInfo.ReferCode = uuidx.UserInviteCode(userInfo.Id)
// Update ReferCode
if err := db.Model(&user.User{}).Where("id = ?", userInfo.Id).Update("refer_code", userInfo.ReferCode).Error; err != nil {
return err
}
if l.svcCtx.Config.Register.EnableTrial {
// Active trial
if err = l.activeTrial(userInfo.Id); err != nil {
return err
}
}
return nil
})
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "insert user info failed: %v", err.Error())
}
// Generate session id
sessionId := uuidx.NewUUID().String()
// Generate token
token, err := jwt.NewJwtToken(
l.svcCtx.Config.JwtAuth.AccessSecret,
time.Now().Unix(),
l.svcCtx.Config.JwtAuth.AccessExpire,
jwt.WithOption("UserId", userInfo.Id),
jwt.WithOption("SessionId", sessionId),
)
if err != nil {
l.Logger.Error("[UserLogin] token generate error", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "token generate error: %v", err.Error())
}
sessionIdCacheKey := fmt.Sprintf("%v:%v", config.SessionIdKey, sessionId)
if err := l.svcCtx.Redis.Set(l.ctx, sessionIdCacheKey, userInfo.Id, time.Duration(l.svcCtx.Config.JwtAuth.AccessExpire)*time.Second).Err(); err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "set session id error: %v", err.Error())
}
resp.Token = token
return
}
func (l *RegisterLogic) activeTrial(uid int64) error {
sub, err := l.svcCtx.SubscribeModel.FindOne(l.ctx, l.svcCtx.Config.Register.TrialSubscribe)
if err != nil {
return err
}
userSub := &user.Subscribe{
Id: 0,
UserId: uid,
OrderId: 0,
SubscribeId: sub.Id,
StartTime: time.Now(),
ExpireTime: tool.AddTime(l.svcCtx.Config.Register.TrialTimeUnit, l.svcCtx.Config.Register.TrialTime, time.Now()),
Traffic: sub.Traffic,
Download: 0,
Upload: 0,
Token: uuidx.SubscribeToken(fmt.Sprintf("Trial-%v", uid)),
UUID: uuidx.NewUUID().String(),
Status: 1,
}
return l.svcCtx.UserModel.InsertSubscribe(l.ctx, userSub)
}
@@ -1,161 +0,0 @@
package auth
import (
"encoding/json"
"fmt"
"time"
"github.com/perfect-panel/server/pkg/authmethod"
"github.com/gin-gonic/gin"
"github.com/perfect-panel/server/pkg/constant"
"github.com/perfect-panel/server/pkg/phone"
"github.com/perfect-panel/server/internal/config"
"github.com/perfect-panel/server/internal/logic/common"
"github.com/perfect-panel/server/internal/model/user"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/jwt"
"github.com/perfect-panel/server/pkg/logger"
"github.com/perfect-panel/server/pkg/tool"
"github.com/perfect-panel/server/pkg/uuidx"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
"gorm.io/gorm"
)
type ResetPasswordLogic struct {
logger.Logger
ctx *gin.Context
svcCtx *svc.ServiceContext
}
// Reset Password
func NewResetPasswordLogic(ctx *gin.Context, svcCtx *svc.ServiceContext) *ResetPasswordLogic {
return &ResetPasswordLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *ResetPasswordLogic) ResetPassword(req *types.AppAuthRequest) (resp *types.AppAuthRespone, err error) {
resp = &types.AppAuthRespone{}
userInfo, err := findUserByMethod(l.ctx, l.svcCtx, req.Method, req.Identifier, req.Account, req.AreaCode)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.UserNotExist), "query user info failed")
}
l.Errorw("FindOneByEmail Error", logger.Field("error", err))
return nil, err
}
switch req.Method {
case authmethod.Mobile:
if !l.svcCtx.Config.Mobile.Enable {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SmsNotEnabled), "sms login is not enabled")
}
phoneNumber, err := phone.FormatToE164(req.AreaCode, req.Account)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.TelephoneError), "Invalid phone number")
}
cacheKey := fmt.Sprintf("%s:%s:%s", config.AuthCodeTelephoneCacheKey, constant.Security, phoneNumber)
value, err := l.svcCtx.Redis.Get(l.ctx, cacheKey).Result()
if err != nil {
l.Errorw("Redis Error", logger.Field("error", err.Error()), logger.Field("cacheKey", cacheKey))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.VerifyCodeError), "code error")
}
var payload common.CacheKeyPayload
err = json.Unmarshal([]byte(value), &payload)
if err != nil {
l.Errorw("Unmarshal Error", logger.Field("error", err.Error()), logger.Field("value", value))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.VerifyCodeError), "code error")
}
if payload.Code != req.Code {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.VerifyCodeError), "code error")
}
case authmethod.Email:
if !l.svcCtx.Config.Email.Enable {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.EmailNotEnabled), "Email function is not enabled yet")
}
cacheKey := fmt.Sprintf("%s:%s:%s", config.AuthCodeCacheKey, constant.Security.String(), req.Account)
value, err := l.svcCtx.Redis.Get(l.ctx, cacheKey).Result()
if err != nil {
l.Errorw("Redis Error", logger.Field("error", err.Error()), logger.Field("cacheKey", cacheKey))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.VerifyCodeError), "code error")
}
var payload CacheKeyPayload
err = json.Unmarshal([]byte(value), &payload)
if err != nil {
l.Errorw("Unmarshal Error", logger.Field("error", err.Error()), logger.Field("value", value))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.VerifyCodeError), "code error")
}
if payload.Code != req.Code {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.VerifyCodeError), "code error")
}
default:
return nil, errors.New("unknown method")
}
userInfo.Password = tool.EncodePassWord(req.Password)
err = l.svcCtx.UserModel.Update(l.ctx, userInfo)
if err != nil {
l.Errorw("UpdateUser Error", logger.Field("error", err))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "update user info failed: %v", err.Error())
}
device, err := l.svcCtx.UserModel.FindOneDeviceByIdentifier(l.ctx, req.Identifier)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
//Add User Device
userInfo.UserDevices = append(userInfo.UserDevices, user.Device{
Ip: l.ctx.ClientIP(),
Identifier: req.Identifier,
UserAgent: req.UserAgent,
})
} else {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query user info failed: %v", err.Error())
}
} else {
if device.UserId != userInfo.Id {
//Change the user who owns the device
if device.UserId != userInfo.Id {
device.UserId = userInfo.Id
}
device.Ip = l.ctx.ClientIP()
err = l.svcCtx.UserModel.UpdateDevice(l.ctx, device)
if err != nil {
l.Errorw("[UpdateUserBindDevice] Fail", logger.Field("error", err.Error()))
}
}
}
// Generate session id
sessionId := uuidx.NewUUID().String()
// Generate token
token, err := jwt.NewJwtToken(
l.svcCtx.Config.JwtAuth.AccessSecret,
time.Now().Unix(),
l.svcCtx.Config.JwtAuth.AccessExpire,
jwt.WithOption("UserId", userInfo.Id),
jwt.WithOption("SessionId", sessionId),
)
if err != nil {
l.Logger.Error("[UserLogin] token generate error", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "token generate error: %v", err.Error())
}
sessionIdCacheKey := fmt.Sprintf("%v:%v", config.SessionIdKey, sessionId)
if err := l.svcCtx.Redis.Set(l.ctx, sessionIdCacheKey, userInfo.Id, time.Duration(l.svcCtx.Config.JwtAuth.AccessExpire)*time.Second).Err(); err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "set session id error: %v", err.Error())
}
resp.Token = token
return
}
@@ -1,39 +0,0 @@
package document
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/tool"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
)
type QueryDocumentDetailLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// NewQueryDocumentDetailLogic Get document detail
func NewQueryDocumentDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryDocumentDetailLogic {
return &QueryDocumentDetailLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *QueryDocumentDetailLogic) QueryDocumentDetail(req *types.QueryDocumentDetailRequest) (resp *types.Document, err error) {
// find document
data, err := l.svcCtx.DocumentModel.FindOne(l.ctx, req.Id)
if err != nil {
l.Error("[QueryDocumentDetailLogic] FindOne error", logger.Field("id", req.Id), logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "FindOne error: %s", err.Error())
}
resp = &types.Document{}
tool.DeepCopy(resp, data)
return
}
@@ -1,48 +0,0 @@
package document
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/tool"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
)
type QueryDocumentListLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// Get document list
func NewQueryDocumentListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryDocumentListLogic {
return &QueryDocumentListLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *QueryDocumentListLogic) QueryDocumentList() (resp *types.QueryDocumentListResponse, err error) {
total, data, err := l.svcCtx.DocumentModel.GetDocumentListByAll(l.ctx)
if err != nil {
l.Error("[QueryDocumentList] error", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "QueryDocumentList error: %v", err.Error())
}
resp = &types.QueryDocumentListResponse{
Total: total,
List: make([]types.Document, 0),
}
for _, item := range data {
resp.List = append(resp.List, types.Document{
Id: item.Id,
Title: item.Title,
Tags: tool.StringMergeAndRemoveDuplicates(item.Tags),
UpdatedAt: item.UpdatedAt.UnixMilli(),
})
}
return
}
@@ -1,82 +0,0 @@
package node
import (
"context"
"strconv"
"strings"
"github.com/perfect-panel/server/pkg/constant"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
"github.com/perfect-panel/server/internal/model/user"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/logger"
)
type GetNodeListLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// Get Node list
func NewGetNodeListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetNodeListLogic {
return &GetNodeListLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetNodeListLogic) GetNodeList(req *types.AppUserSubscbribeNodeRequest) (resp *types.AppUserSubscbribeNodeResponse, err error) {
resp = &types.AppUserSubscbribeNodeResponse{List: make([]types.AppUserSubscbribeNode, 0)}
userInfo := l.ctx.Value(constant.CtxKeyUser).(*user.User)
userSubscribe, err := l.svcCtx.UserModel.FindOneUserSubscribe(l.ctx, req.Id)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find user subscribe: %v", err.Error())
}
if userInfo.Id != userSubscribe.UserId {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidParams), "find user subscribe: %v", err.Error())
}
//拿到所有订阅下的服务组id
var ids []int64
for _, idStr := range strings.Split(userSubscribe.Subscribe.ServerGroup, ",") {
id, err := strconv.ParseInt(idStr, 10, 64)
if err != nil {
continue
}
ids = append(ids, id)
}
//根据服务组id拿到所有节点
servers, err := l.svcCtx.ServerModel.FindServerListByGroupIds(l.ctx, ids)
if err != nil {
return nil, err
}
for _, server := range servers {
resp.List = append(resp.List, types.AppUserSubscbribeNode{
Id: server.Id,
Uuid: userSubscribe.UUID,
Traffic: userSubscribe.Traffic,
Upload: userSubscribe.Upload,
Download: userSubscribe.Download,
RelayNode: server.RelayNode,
RelayMode: server.RelayMode,
Longitude: server.Longitude,
Latitude: server.Latitude,
Tags: strings.Split(server.Tags, ","),
Config: server.Config,
ServerAddr: server.ServerAddr,
Protocol: server.Protocol,
SpeedLimit: server.SpeedLimit,
City: server.City,
Country: server.Country,
Name: server.Name,
})
}
return
}
@@ -1,41 +0,0 @@
package node
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/tool"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
)
type GetRuleGroupListLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// Get rule group list
func NewGetRuleGroupListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetRuleGroupListLogic {
return &GetRuleGroupListLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetRuleGroupListLogic) GetRuleGroupList() (resp *types.AppRuleGroupListResponse, err error) {
nodeRuleGroupList, err := l.svcCtx.ServerModel.QueryAllRuleGroup(l.ctx)
if err != nil {
l.Logger.Error("[GetRuleGroupList] get subscribe rule group list failed: ", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "get subscribe rule group list failed: %v", err.Error())
}
nodeRuleGroups := make([]types.ServerRuleGroup, 0)
tool.DeepCopy(&nodeRuleGroups, nodeRuleGroupList)
return &types.AppRuleGroupListResponse{
Total: int64(len(nodeRuleGroups)),
List: nodeRuleGroups,
}, nil
}
@@ -1,13 +0,0 @@
package order
import (
"github.com/perfect-panel/server/internal/model/coupon"
)
func calculateCoupon(amount int64, couponInfo *coupon.Coupon) int64 {
if couponInfo.Type == 1 {
return int64(float64(amount) * (float64(couponInfo.Discount) / float64(100)))
} else {
return min(couponInfo.Discount, amount)
}
}
-20
View File
@@ -1,20 +0,0 @@
package order
import "github.com/perfect-panel/server/internal/model/payment"
func calculateFee(amount int64, config *payment.Payment) int64 {
var fee float64
switch config.FeeMode {
case 0:
return 0
case 1:
fee = float64(amount) * (float64(config.FeePercent) / float64(100))
case 2:
if amount > 0 {
fee = float64(config.FeeAmount)
}
case 3:
fee = float64(amount)*(float64(config.FeePercent)/float64(100)) + float64(config.FeeAmount)
}
return int64(fee)
}
@@ -1,325 +0,0 @@
package order
import (
"context"
"encoding/json"
"fmt"
"strconv"
paymentPlatform "github.com/perfect-panel/server/pkg/payment"
"github.com/perfect-panel/server/pkg/constant"
"github.com/hibiken/asynq"
"github.com/perfect-panel/server/internal/model/order"
"github.com/perfect-panel/server/internal/model/payment"
"github.com/perfect-panel/server/internal/model/user"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/exchangeRate"
"github.com/perfect-panel/server/pkg/logger"
"github.com/perfect-panel/server/pkg/payment/alipay"
"github.com/perfect-panel/server/pkg/payment/epay"
"github.com/perfect-panel/server/pkg/payment/stripe"
"github.com/perfect-panel/server/pkg/tool"
"github.com/perfect-panel/server/pkg/xerr"
queueType "github.com/perfect-panel/server/queue/types"
"github.com/pkg/errors"
"gorm.io/gorm"
)
type CheckoutOrderLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
type CurrencyConfig struct {
CurrencyUnit string
CurrencySymbol string
AccessKey string
}
const (
Stripe = "Stripe"
QR = "qr"
Link = "link"
)
// NewCheckoutOrderLogic Checkout order
func NewCheckoutOrderLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CheckoutOrderLogic {
return &CheckoutOrderLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *CheckoutOrderLogic) CheckoutOrder(req *types.CheckoutOrderRequest, requestHost string) (resp *types.CheckoutOrderResponse, err error) {
u, ok := l.ctx.Value(constant.CtxKeyUser).(*user.User)
if !ok {
l.Error("[CheckoutOrderLogic] Invalid access")
return nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidAccess), "Invalid access")
}
// find order
orderInfo, err := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, req.OrderNo)
if err != nil {
l.Error("[CheckoutOrderLogic] FindOneByOrderNo error", logger.Field("orderNo", req.OrderNo), logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "FindOneByOrderNo error: %s", err.Error())
}
if orderInfo.Status != 1 {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "Order status error")
}
paymentConfig, err := l.svcCtx.PaymentModel.FindOne(l.ctx, orderInfo.PaymentId)
if err != nil {
l.Error("[CheckoutOrderLogic] FindOneByPaymentMark error", logger.Field("paymentMark", orderInfo.Method), logger.Field("PaymentID", orderInfo.PaymentId), logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "FindOneByPaymentMark error: %s", err.Error())
}
var stripePayment *types.StripePayment = nil
var url, t string
// switch payment method
switch paymentPlatform.ParsePlatform(paymentConfig.Platform) {
case paymentPlatform.Stripe:
result, err := l.stripePayment(paymentConfig.Config, orderInfo, u)
if err != nil {
l.Error("[CheckoutOrderLogic] stripePayment error", logger.Field("error", err.Error()))
return nil, err
}
stripePayment = result
t = Stripe
case paymentPlatform.EPay:
// epay
url, err = l.epayPayment(paymentConfig, orderInfo, req.ReturnUrl, requestHost)
if err != nil {
l.Error("[CheckoutOrderLogic] epayPayment error", logger.Field("error", err.Error()))
return nil, err
}
t = Link
case paymentPlatform.AlipayF2F:
// alipay f2f
url, err = l.alipayF2fPayment(paymentConfig, orderInfo, requestHost)
if err != nil {
return nil, err
}
t = QR
case paymentPlatform.Balance:
// balance
if err = l.balancePayment(u, orderInfo); err != nil {
return nil, err
}
t = paymentPlatform.Balance.String()
default:
return nil, errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "Payment method not supported")
}
return &types.CheckoutOrderResponse{
Type: t,
CheckoutUrl: url,
Stripe: stripePayment,
}, nil
}
// Query exchange rate
func (l *CheckoutOrderLogic) queryExchangeRate(to string, src int64) (amount float64, err error) {
amount = float64(src) / float64(100)
// query system currency
currency, err := l.svcCtx.SystemModel.GetCurrencyConfig(l.ctx)
if err != nil {
l.Error("[CheckoutOrderLogic] GetCurrencyConfig error", logger.Field("error", err.Error()))
return 0, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "GetCurrencyConfig error: %s", err.Error())
}
configs := &CurrencyConfig{}
tool.SystemConfigSliceReflectToStruct(currency, configs)
if configs.AccessKey == "" {
return amount, nil
}
if configs.CurrencyUnit != to {
// query exchange rate
result, err := exchangeRate.GetExchangeRete(configs.CurrencyUnit, to, configs.AccessKey, 1)
if err != nil {
return 0, err
}
amount = result * amount
}
return amount, nil
}
// Stripe Payment
func (l *CheckoutOrderLogic) stripePayment(config string, info *order.Order, u *user.User) (*types.StripePayment, error) {
// stripe WeChat pay or stripe alipay
stripeConfig := payment.StripeConfig{}
if err := json.Unmarshal([]byte(config), &stripeConfig); err != nil {
l.Error("[CheckoutOrderLogic] Unmarshal error", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "Unmarshal error: %s", err.Error())
}
client := stripe.NewClient(stripe.Config{
SecretKey: stripeConfig.SecretKey,
PublicKey: stripeConfig.PublicKey,
WebhookSecret: stripeConfig.WebhookSecret,
})
// Calculate the amount with exchange rate
amount, err := l.queryExchangeRate("CNY", info.Amount)
if err != nil {
l.Error("[CheckoutOrderLogic] queryExchangeRate error", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "queryExchangeRate error: %s", err.Error())
}
convertAmount := int64(amount * 100)
// create payment
result, err := client.CreatePaymentSheet(&stripe.Order{
OrderNo: info.OrderNo,
Subscribe: strconv.FormatInt(info.SubscribeId, 10),
Amount: convertAmount,
Currency: "cny",
Payment: stripeConfig.Payment,
},
&stripe.User{
UserId: u.Id,
})
if err != nil {
l.Error("[CheckoutOrderLogic] CreatePaymentSheet error", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "CreatePaymentSheet error: %s", err.Error())
}
tradeNo := result.TradeNo
stripePayment := &types.StripePayment{
PublishableKey: stripeConfig.PublicKey,
ClientSecret: result.ClientSecret,
Method: stripeConfig.Payment,
}
// save payment
info.TradeNo = tradeNo
err = l.svcCtx.OrderModel.Update(l.ctx, info)
if err != nil {
l.Error("[CheckoutOrderLogic] Update error", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "Update error: %s", err.Error())
}
return stripePayment, nil
}
// epay payment
func (l *CheckoutOrderLogic) epayPayment(config *payment.Payment, info *order.Order, returnUrl, requestHost string) (string, error) {
epayConfig := payment.EPayConfig{}
if err := json.Unmarshal([]byte(config.Config), &epayConfig); err != nil {
l.Error("[CheckoutOrderLogic] Unmarshal error", logger.Field("error", err.Error()))
return "", errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "Unmarshal error: %s", err.Error())
}
client := epay.NewClient(epayConfig.Pid, epayConfig.Url, epayConfig.Key)
// Calculate the amount with exchange rate
amount, err := l.queryExchangeRate("CNY", info.Amount)
if err != nil {
return "", err
}
var domain string
if config.Domain != "" {
domain = config.Domain
} else {
domain = fmt.Sprintf("http://%s", requestHost)
}
// create payment
url := client.CreatePayUrl(epay.Order{
Name: l.svcCtx.Config.Site.SiteName,
Amount: amount,
OrderNo: info.OrderNo,
SignType: "MD5",
NotifyUrl: domain + "/v1/notify/epay",
ReturnUrl: returnUrl,
})
return url, nil
}
// alipay f2f payment
func (l *CheckoutOrderLogic) alipayF2fPayment(pay *payment.Payment, info *order.Order, requestHost string) (string, error) {
f2FConfig := payment.AlipayF2FConfig{}
if err := json.Unmarshal([]byte(pay.Config), &f2FConfig); err != nil {
l.Error("[CheckoutOrderLogic] Unmarshal error", logger.Field("error", err.Error()))
return "", errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "Unmarshal error: %s", err.Error())
}
var domain string
if pay.Domain != "" {
domain = pay.Domain
} else {
domain = fmt.Sprintf("http://%s", requestHost)
}
client := alipay.NewClient(alipay.Config{
AppId: f2FConfig.AppId,
PrivateKey: f2FConfig.PrivateKey,
PublicKey: f2FConfig.PublicKey,
InvoiceName: f2FConfig.InvoiceName,
NotifyURL: domain + "/notify/alipay",
})
// Calculate the amount with exchange rate
amount, err := l.queryExchangeRate("CNY", info.Amount)
if err != nil {
l.Error("[CheckoutOrderLogic] queryExchangeRate error", logger.Field("error", err.Error()))
return "", errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "queryExchangeRate error: %s", err.Error())
}
convertAmount := int64(amount * 100)
// create payment
QRCode, err := client.PreCreateTrade(l.ctx, alipay.Order{
OrderNo: info.OrderNo,
Amount: convertAmount,
})
if err != nil {
l.Error("[CheckoutOrderLogic] PreCreateTrade error", logger.Field("error", err.Error()))
return "", errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "PreCreateTrade error: %s", err.Error())
}
return QRCode, nil
}
// Balance payment
func (l *CheckoutOrderLogic) balancePayment(u *user.User, o *order.Order) error {
var userInfo user.User
err := l.svcCtx.UserModel.Transaction(l.ctx, func(db *gorm.DB) error {
err := db.Model(&user.User{}).Where("id = ?", u.Id).First(&userInfo).Error
if err != nil {
return err
}
if userInfo.Balance < o.Amount {
return errors.Wrapf(xerr.NewErrCode(xerr.InsufficientBalance), "Insufficient balance")
}
// deduct balance
userInfo.Balance -= o.Amount
err = l.svcCtx.UserModel.Update(l.ctx, &userInfo)
if err != nil {
return err
}
// create balance log
balanceLog := &user.BalanceLog{
Id: 0,
UserId: u.Id,
Amount: o.Amount,
Type: 3,
OrderId: o.Id,
Balance: userInfo.Balance,
}
err = db.Create(balanceLog).Error
if err != nil {
return err
}
return l.svcCtx.OrderModel.UpdateOrderStatus(l.ctx, o.OrderNo, 2)
})
if err != nil {
l.Error("[CheckoutOrderLogic] Transaction error", logger.Field("error", err.Error()), logger.Field("orderNo", o.OrderNo))
return err
}
// create activity order task
payload := queueType.ForthwithActivateOrderPayload{
OrderNo: o.OrderNo,
}
bytes, err := json.Marshal(payload)
if err != nil {
l.Error("[CheckoutOrderLogic] Marshal error", logger.Field("error", err.Error()))
return err
}
task := asynq.NewTask(queueType.ForthwithActivateOrder, bytes)
_, err = l.svcCtx.Queue.EnqueueContext(l.ctx, task)
if err != nil {
l.Error("[CheckoutOrderLogic] Enqueue error", logger.Field("error", err.Error()))
return err
}
l.Logger.Info("[CheckoutOrderLogic] Enqueue success", logger.Field("orderNo", o.OrderNo))
return nil
}
-186
View File
@@ -1,186 +0,0 @@
package order
import (
"context"
"encoding/json"
paymentPlatform "github.com/perfect-panel/server/pkg/payment"
"github.com/perfect-panel/server/internal/model/user"
"github.com/perfect-panel/server/pkg/payment/stripe"
"gorm.io/gorm"
"github.com/perfect-panel/server/internal/model/order"
"github.com/perfect-panel/server/internal/model/payment"
"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/payment/alipay"
)
type CloseOrderLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// NewCloseOrderLogic Close order
func NewCloseOrderLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CloseOrderLogic {
return &CloseOrderLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *CloseOrderLogic) CloseOrder(req *types.CloseOrderRequest) error {
// Find order information by order number
orderInfo, err := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, req.OrderNo)
if err != nil {
l.Error("[CloseOrder] Find order info failed",
logger.Field("error", err.Error()),
logger.Field("orderNo", req.OrderNo),
)
return nil
}
// If the order status is not 1, it means that the order has been closed or paid
if orderInfo.Status != 1 {
l.Info("[CloseOrder] Order status is not 1",
logger.Field("orderNo", req.OrderNo),
logger.Field("status", orderInfo.Status),
)
return nil
}
if l.confirmationPayment(orderInfo) {
l.Info("[CloseOrder] Order has been paid",
logger.Field("orderNo", req.OrderNo),
logger.Field("status", orderInfo.Status),
)
return nil
}
err = l.svcCtx.DB.Transaction(func(tx *gorm.DB) error {
// update order status
err := tx.Model(&order.Order{}).Where("order_no = ?", req.OrderNo).Update("status", 3).Error
if err != nil {
l.Error("[CloseOrder] Update order status failed",
logger.Field("error", err.Error()),
logger.Field("orderNo", req.OrderNo),
)
return err
}
// refund deduction amount to user deduction balance
if orderInfo.GiftAmount > 0 {
userInfo, err := l.svcCtx.UserModel.FindOne(l.ctx, orderInfo.UserId)
if err != nil {
l.Error("[CloseOrder] Find user info failed",
logger.Field("error", err.Error()),
logger.Field("user_id", orderInfo.UserId),
)
return err
}
deduction := userInfo.GiftAmount + orderInfo.GiftAmount
err = tx.Model(&user.User{}).Where("id = ?", orderInfo.UserId).Update("deduction", deduction).Error
if err != nil {
l.Error("[CloseOrder] Refund deduction amount failed",
logger.Field("error", err.Error()),
logger.Field("uid", orderInfo.UserId),
logger.Field("deduction", orderInfo.GiftAmount),
)
return err
}
// Record the deduction refund log
giftAmountLog := &user.GiftAmountLog{
UserId: orderInfo.UserId,
OrderNo: orderInfo.OrderNo,
Amount: orderInfo.GiftAmount,
Type: 1,
Balance: deduction,
Remark: "Order cancellation refund",
}
err = tx.Model(&user.GiftAmountLog{}).Create(giftAmountLog).Error
if err != nil {
l.Error("[CloseOrder] Record cancellation refund log failed",
logger.Field("error", err.Error()),
logger.Field("uid", orderInfo.UserId),
logger.Field("deduction", orderInfo.GiftAmount),
)
return err
}
// update user cache
return l.svcCtx.UserModel.UpdateUserCache(l.ctx, userInfo)
}
return nil
})
if err != nil {
return err
}
return nil
}
// confirmationPayment Determine whether the payment is successful
//
//nolint:unused
func (l *CloseOrderLogic) confirmationPayment(order *order.Order) bool {
paymentConfig, err := l.svcCtx.PaymentModel.FindOne(l.ctx, order.PaymentId)
if err != nil {
l.Error("[CloseOrder] Find payment config failed", logger.Field("error", err.Error()), logger.Field("paymentMark", order.Method))
return false
}
switch paymentPlatform.ParsePlatform(order.Method) {
case paymentPlatform.AlipayF2F:
if l.queryAlipay(paymentConfig, order.TradeNo) {
return true
}
case paymentPlatform.Stripe:
if l.queryStripe(paymentConfig, order.TradeNo) {
return true
}
default:
l.Info("[CloseOrder] Unsupported payment method", logger.Field("paymentMethod", order.Method))
}
return false
}
// queryAlipay Query Alipay payment status
func (l *CloseOrderLogic) queryAlipay(paymentConfig *payment.Payment, TradeNo string) bool {
config := payment.AlipayF2FConfig{}
if err := json.Unmarshal([]byte(paymentConfig.Config), &config); err != nil {
l.Error("[CloseOrder] Unmarshal payment config failed", logger.Field("error", err.Error()), logger.Field("config", paymentConfig.Config))
return false
}
client := alipay.NewClient(alipay.Config{
AppId: config.AppId,
PrivateKey: config.PrivateKey,
PublicKey: config.PublicKey,
InvoiceName: config.InvoiceName,
})
status, err := client.QueryTrade(l.ctx, TradeNo)
if err != nil {
l.Error("[CloseOrder] Query trade failed", logger.Field("error", err.Error()), logger.Field("TradeNo", TradeNo))
return false
}
if status == alipay.Success || status == alipay.Finished {
return true
}
return false
}
// queryStripe Query Stripe payment status
func (l *CloseOrderLogic) queryStripe(paymentConfig *payment.Payment, TradeNo string) bool {
config := payment.StripeConfig{}
if err := json.Unmarshal([]byte(paymentConfig.Config), &config); err != nil {
l.Error("[CloseOrder] Unmarshal payment config failed", logger.Field("error", err.Error()), logger.Field("config", paymentConfig.Config))
return false
}
client := stripe.NewClient(stripe.Config{
PublicKey: config.PublicKey,
SecretKey: config.SecretKey,
WebhookSecret: config.WebhookSecret,
})
status, err := client.QueryOrderStatus(TradeNo)
if err != nil {
l.Error("[CloseOrder] Query order status failed", logger.Field("error", err.Error()), logger.Field("TradeNo", TradeNo))
return false
}
return status
}
-14
View File
@@ -1,14 +0,0 @@
package order
import "github.com/perfect-panel/server/internal/types"
func getDiscount(discounts []types.SubscribeDiscount, inputMonths int64) float64 {
var finalDiscount int64 = 100
for _, discount := range discounts {
if inputMonths >= discount.Quantity && discount.Discount < finalDiscount {
finalDiscount = discount.Discount
}
}
return float64(finalDiscount) / float64(100)
}
@@ -1,123 +0,0 @@
package order
import (
"context"
"encoding/json"
"github.com/perfect-panel/server/internal/model/order"
"github.com/perfect-panel/server/pkg/tool"
"github.com/perfect-panel/server/pkg/constant"
"github.com/perfect-panel/server/internal/model/user"
"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 PreCreateOrderLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// Pre create order
func NewPreCreateOrderLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PreCreateOrderLogic {
return &PreCreateOrderLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *PreCreateOrderLogic) PreCreateOrder(req *types.PurchaseOrderRequest) (resp *types.PreOrderResponse, err error) {
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 subscribe plan
sub, err := l.svcCtx.SubscribeModel.FindOne(l.ctx, req.SubscribeId)
if err != nil {
l.Error("[PreCreateOrder] Database query error", logger.Field("error", err.Error()), logger.Field("subscribe_id", req.SubscribeId))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find subscribe error: %v", err.Error())
}
var discount float64 = 1
if sub.Discount != "" {
var dis []types.SubscribeDiscount
_ = json.Unmarshal([]byte(sub.Discount), &dis)
discount = getDiscount(dis, req.Quantity)
}
price := sub.UnitPrice * req.Quantity
amount := int64(float64(price) * discount)
discountAmount := price - amount
var coupon int64
if req.Coupon != "" {
couponInfo, err := l.svcCtx.CouponModel.FindOneByCode(l.ctx, req.Coupon)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.CouponNotExist), "coupon not found")
}
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find coupon error: %v", err.Error())
}
if couponInfo.Count > 0 && couponInfo.Count <= couponInfo.UsedCount {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.CouponInsufficientUsage), "coupon used")
}
couponSub := tool.StringToInt64Slice(couponInfo.Subscribe)
if len(couponSub) > 0 && !tool.Contains(couponSub, req.SubscribeId) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.CouponNotApplicable), "coupon not match")
}
var count int64
err = l.svcCtx.DB.Transaction(func(tx *gorm.DB) error {
return tx.Model(&order.Order{}).Where("user_id = ? and coupon = ?", u.Id, req.Coupon).Count(&count).Error
})
if err != nil {
l.Errorw("[Purchase] Database query error", logger.Field("error", err.Error()), logger.Field("user_id", u.Id), logger.Field("coupon", req.Coupon))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find coupon error: %v", err.Error())
}
if couponInfo.UserLimit > 0 && count >= couponInfo.UserLimit {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.CouponInsufficientUsage), "coupon limit exceeded")
}
coupon = calculateCoupon(amount, couponInfo)
}
amount -= coupon
var deductionAmount int64
// Check user deduction amount
if u.GiftAmount > 0 {
if u.GiftAmount >= amount {
deductionAmount = amount
amount = 0
} else {
deductionAmount = u.GiftAmount
amount -= u.GiftAmount
}
}
payment, err := l.svcCtx.PaymentModel.FindOne(l.ctx, req.Payment)
if err != nil {
l.Logger.Error("[PreCreateOrder] Database query error", logger.Field("error", err.Error()), logger.Field("payment", req.Payment))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find payment method error: %v", err.Error())
}
var feeAmount int64
// Calculate the handling fee
if amount > 0 {
feeAmount = calculateFee(amount, payment)
}
amount += feeAmount
resp = &types.PreOrderResponse{
Price: price,
Amount: amount,
Discount: discountAmount,
GiftAmount: deductionAmount,
Coupon: req.Coupon,
CouponDiscount: coupon,
FeeAmount: feeAmount,
}
return
}
-214
View File
@@ -1,214 +0,0 @@
package order
import (
"context"
"encoding/json"
"time"
"github.com/perfect-panel/server/pkg/constant"
"github.com/hibiken/asynq"
"github.com/perfect-panel/server/internal/model/order"
"github.com/perfect-panel/server/internal/model/user"
"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/tool"
"github.com/perfect-panel/server/pkg/xerr"
queue "github.com/perfect-panel/server/queue/types"
"github.com/pkg/errors"
"gorm.io/gorm"
)
type PurchaseLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
const CloseOrderTimeMinutes = 15
// purchase Subscription
func NewPurchaseLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PurchaseLogic {
return &PurchaseLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *PurchaseLogic) Purchase(req *types.PurchaseOrderRequest) (resp *types.PurchaseOrderResponse, err error) {
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 user subscription
if l.svcCtx.Config.Subscribe.SingleModel {
userSub, err := l.svcCtx.UserModel.QueryUserSubscribe(l.ctx, u.Id)
if err != nil {
l.Error("[Purchase] Database query error", logger.Field("error", err.Error()), logger.Field("user_id", u.Id))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find user subscription error: %v", err.Error())
}
if len(userSub) > 0 {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.UserSubscribeExist), "user has subscription")
}
}
// find subscribe plan
sub, err := l.svcCtx.SubscribeModel.FindOne(l.ctx, req.SubscribeId)
if err != nil {
l.Error("[Purchase] Database query error", logger.Field("error", err.Error()), logger.Field("subscribe_id", req.SubscribeId))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find subscribe error: %v", err.Error())
}
// check subscribe plan status
if !*sub.Sell {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "subscribe not sell")
}
var discount float64 = 1
if sub.Discount != "" {
var dis []types.SubscribeDiscount
_ = json.Unmarshal([]byte(sub.Discount), &dis)
discount = getDiscount(dis, req.Quantity)
}
price := sub.UnitPrice * req.Quantity
// discount amount
amount := int64(float64(price) * discount)
discountAmount := price - amount
var coupon int64 = 0
// Calculate the coupon deduction
if req.Coupon != "" {
couponInfo, err := l.svcCtx.CouponModel.FindOneByCode(l.ctx, req.Coupon)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.CouponNotExist), "coupon not found")
}
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find coupon error: %v", err.Error())
}
if couponInfo.Count <= couponInfo.UsedCount {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.CouponInsufficientUsage), "coupon used")
}
couponSub := tool.StringToInt64Slice(couponInfo.Subscribe)
if len(couponSub) > 0 && !tool.Contains(couponSub, req.SubscribeId) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.CouponNotApplicable), "coupon not match")
}
var count int64
err = l.svcCtx.DB.Transaction(func(tx *gorm.DB) error {
return tx.Model(&order.Order{}).Where("user_id = ? and coupon = ?", u.Id, req.Coupon).Count(&count).Error
})
if err != nil {
l.Errorw("[Purchase] Database query error", logger.Field("error", err.Error()), logger.Field("user_id", u.Id), logger.Field("coupon", req.Coupon))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find coupon error: %v", err.Error())
}
if count >= couponInfo.UserLimit {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.CouponInsufficientUsage), "coupon limit exceeded")
}
coupon = calculateCoupon(amount, couponInfo)
}
// Calculate the handling fee
amount -= coupon
var deductionAmount int64
// Check user deduction amount
if u.GiftAmount > 0 {
if u.GiftAmount >= amount {
deductionAmount = amount
amount = 0
u.GiftAmount -= amount
} else {
deductionAmount = u.GiftAmount
amount -= u.GiftAmount
u.GiftAmount = 0
}
}
// find payment method
payment, err := l.svcCtx.PaymentModel.FindOne(l.ctx, req.Payment)
if err != nil {
l.Logger.Error("[Purchase] Database query error", logger.Field("error", err.Error()), logger.Field("payment", req.Payment))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find payment method error: %v", err.Error())
}
var feeAmount int64
// Calculate the handling fee
if amount > 0 {
feeAmount = calculateFee(amount, payment)
}
// query user is new purchase or renewal
isNew, err := l.svcCtx.OrderModel.IsUserEligibleForNewOrder(l.ctx, u.Id)
if err != nil {
l.Error("[Purchase] Database query error", logger.Field("error", err.Error()), logger.Field("user_id", u.Id))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find user order error: %v", err.Error())
}
// create order
orderInfo := &order.Order{
UserId: u.Id,
OrderNo: tool.GenerateTradeNo(),
Type: 1,
Quantity: req.Quantity,
Price: price,
Amount: amount,
Discount: discountAmount,
GiftAmount: deductionAmount,
Coupon: req.Coupon,
CouponDiscount: coupon,
PaymentId: req.Payment,
Method: payment.Platform,
FeeAmount: feeAmount,
Status: 1,
IsNew: isNew,
SubscribeId: req.SubscribeId,
}
// Database transaction
err = l.svcCtx.DB.Transaction(func(db *gorm.DB) error {
// update user deduction && Pre deduction ,Return after canceling the order
if orderInfo.GiftAmount > 0 {
// update user deduction && Pre deduction ,Return after canceling the order
if e := l.svcCtx.UserModel.Update(l.ctx, u, db); err != nil {
l.Error("[Purchase] Database update error", logger.Field("error", err.Error()), logger.Field("user", u))
return e
}
// create deduction record
giftAmountLog := user.GiftAmountLog{
UserId: orderInfo.UserId,
OrderNo: orderInfo.OrderNo,
Amount: orderInfo.GiftAmount,
Type: 2,
Balance: u.GiftAmount,
Remark: "Purchase order deduction",
}
if e := db.Model(&user.GiftAmountLog{}).Create(&giftAmountLog).Error; e != nil {
l.Error("[Purchase] Database insert error",
logger.Field("error", err.Error()),
logger.Field("deductionLog", giftAmountLog),
)
return e
}
}
// insert order
return db.Model(&order.Order{}).Create(&orderInfo).Error
})
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "insert order error: %v", err.Error())
}
// Deferred task
payload := queue.DeferCloseOrderPayload{
OrderNo: orderInfo.OrderNo,
}
val, err := json.Marshal(payload)
if err != nil {
l.Error("[CreateOrder] Marshal payload error", logger.Field("error", err.Error()), logger.Field("payload", payload))
}
task := asynq.NewTask(queue.DeferCloseOrder, val, asynq.MaxRetry(3))
taskInfo, err := l.svcCtx.Queue.Enqueue(task, asynq.ProcessIn(CloseOrderTimeMinutes*time.Minute))
if err != nil {
l.Error("[CreateOrder] Enqueue task error", logger.Field("error", err.Error()), logger.Field("task", task))
} else {
l.Info("[CreateOrder] Enqueue task success", logger.Field("TaskID", taskInfo.ID))
}
return &types.PurchaseOrderResponse{
OrderNo: orderInfo.OrderNo,
}, nil
}
@@ -1,40 +0,0 @@
package order
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/tool"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
)
type QueryOrderDetailLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// Get order
func NewQueryOrderDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryOrderDetailLogic {
return &QueryOrderDetailLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *QueryOrderDetailLogic) QueryOrderDetail(req *types.QueryOrderDetailRequest) (resp *types.OrderDetail, err error) {
orderInfo, err := l.svcCtx.OrderModel.FindOneDetailsByOrderNo(l.ctx, req.OrderNo)
if err != nil {
l.Error("[QueryOrderDetail] Database query error", logger.Field("error", err.Error()), logger.Field("order_no", req.OrderNo))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find order error: %v", err.Error())
}
resp = &types.OrderDetail{}
tool.DeepCopy(resp, orderInfo)
// Prevent commission amount leakage
resp.Commission = 0
return
}
@@ -1,56 +0,0 @@
package order
import (
"context"
"github.com/perfect-panel/server/pkg/constant"
"github.com/perfect-panel/server/internal/model/user"
"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/tool"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
)
type QueryOrderListLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// Get order list
func NewQueryOrderListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryOrderListLogic {
return &QueryOrderListLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *QueryOrderListLogic) QueryOrderList(req *types.QueryOrderListRequest) (resp *types.QueryOrderListResponse, err error) {
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")
}
total, data, err := l.svcCtx.OrderModel.QueryOrderListByPage(l.ctx, req.Page, req.Size, 0, u.Id, 0, "")
if err != nil {
l.Error("[QueryOrderListLogic] Query order list failed", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "Query order list failed")
}
resp = &types.QueryOrderListResponse{
Total: total,
List: make([]types.OrderDetail, 0),
}
for _, item := range data {
var orderInfo types.OrderDetail
tool.DeepCopy(&orderInfo, item)
// Prevent commission amount leakage
orderInfo.Commission = 0
resp.List = append(resp.List, orderInfo)
}
return
}
-92
View File
@@ -1,92 +0,0 @@
package order
import (
"context"
"encoding/json"
"time"
"github.com/perfect-panel/server/pkg/constant"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/hibiken/asynq"
"github.com/perfect-panel/server/internal/model/order"
"github.com/perfect-panel/server/internal/model/user"
"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/tool"
queue "github.com/perfect-panel/server/queue/types"
"github.com/pkg/errors"
)
type RechargeLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// NewRechargeLogic Recharge
func NewRechargeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RechargeLogic {
return &RechargeLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *RechargeLogic) Recharge(req *types.RechargeOrderRequest) (resp *types.RechargeOrderResponse, err error) {
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 payment method
payment, err := l.svcCtx.PaymentModel.FindOne(l.ctx, req.Payment)
if err != nil {
l.Error("[Recharge] Database query error", logger.Field("error", err.Error()), logger.Field("payment", req.Payment))
return nil, errors.Wrapf(err, "find payment error: %v", err.Error())
}
// Calculate the handling fee
feeAmount := calculateFee(req.Amount, payment)
// query user is new purchase or renewal
isNew, err := l.svcCtx.OrderModel.IsUserEligibleForNewOrder(l.ctx, u.Id)
if err != nil {
l.Error("[Recharge] Database query error", logger.Field("error", err.Error()), logger.Field("user_id", u.Id))
return nil, errors.Wrapf(err, "query user error: %v", err.Error())
}
orderInfo := order.Order{
UserId: u.Id,
OrderNo: tool.GenerateTradeNo(),
Type: 4,
Price: req.Amount,
Amount: req.Amount + feeAmount,
FeeAmount: feeAmount,
PaymentId: req.Payment,
Method: payment.Platform,
Status: 1,
IsNew: isNew,
}
err = l.svcCtx.OrderModel.Insert(l.ctx, &orderInfo)
if err != nil {
l.Error("[Recharge] Database insert error", logger.Field("error", err.Error()), logger.Field("order", orderInfo))
return nil, errors.Wrapf(err, "insert order error: %v", err.Error())
}
// Deferred task
payload := queue.DeferCloseOrderPayload{
OrderNo: orderInfo.OrderNo,
}
val, err := json.Marshal(payload)
if err != nil {
l.Error("[Recharge] Marshal payload error", logger.Field("error", err.Error()), logger.Field("payload", payload))
}
task := asynq.NewTask(queue.DeferCloseOrder, val, asynq.MaxRetry(3))
taskInfo, err := l.svcCtx.Queue.Enqueue(task, asynq.ProcessIn(CloseOrderTimeMinutes*time.Minute))
if err != nil {
l.Error("[Recharge] Enqueue task error", logger.Field("error", err.Error()), logger.Field("task", task))
} else {
l.Info("[Recharge] Enqueue task success", logger.Field("TaskID", taskInfo.ID))
}
return &types.RechargeOrderResponse{
OrderNo: orderInfo.OrderNo,
}, nil
}
-178
View File
@@ -1,178 +0,0 @@
package order
import (
"context"
"encoding/json"
"time"
"github.com/perfect-panel/server/pkg/constant"
"github.com/hibiken/asynq"
"github.com/perfect-panel/server/internal/model/order"
"github.com/perfect-panel/server/internal/model/user"
"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/tool"
"github.com/perfect-panel/server/pkg/xerr"
queue "github.com/perfect-panel/server/queue/types"
"github.com/pkg/errors"
"gorm.io/gorm"
)
type RenewalLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// Renewal Subscription
func NewRenewalLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RenewalLogic {
return &RenewalLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *RenewalLogic) Renewal(req *types.RenewalOrderRequest) (resp *types.RenewalOrderResponse, err error) {
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")
}
orderNo := tool.GenerateTradeNo()
// find user subscribe
userSubscribe, err := l.svcCtx.UserModel.FindOneUserSubscribe(l.ctx, req.UserSubscribeID)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find user subscribe error: %v", err.Error())
}
// find subscription
sub, err := l.svcCtx.SubscribeModel.FindOne(l.ctx, userSubscribe.SubscribeId)
if err != nil {
l.Error("[Renewal] Database query error", logger.Field("error", err.Error()), logger.Field("subscribe_id", userSubscribe.SubscribeId))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find subscribe error: %v", err.Error())
}
// check subscribe plan status
if !*sub.Sell {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "subscribe not sell")
}
var discount float64 = 1
if sub.Discount != "" {
var dis []types.SubscribeDiscount
_ = json.Unmarshal([]byte(sub.Discount), &dis)
discount = getDiscount(dis, req.Quantity)
}
price := sub.UnitPrice * req.Quantity
amount := int64(float64(price) * discount)
discountAmount := price - amount
var coupon int64 = 0
if req.Coupon != "" {
couponInfo, err := l.svcCtx.CouponModel.FindOneByCode(l.ctx, req.Coupon)
if err != nil {
l.Error("[Renewal] Database query error", logger.Field("error", err.Error()), logger.Field("coupon", req.Coupon))
return nil, errors.Wrapf(err, "find coupon error: %v", err.Error())
}
if couponInfo.Count <= couponInfo.UsedCount {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.CouponInsufficientUsage), "coupon used")
}
coupon = calculateCoupon(amount, couponInfo)
}
payment, err := l.svcCtx.PaymentModel.FindOne(l.ctx, req.Payment)
if err != nil {
l.Error("[Renewal] Database query error", logger.Field("error", err.Error()), logger.Field("payment", req.Payment))
return nil, errors.Wrapf(err, "find payment error: %v", err.Error())
}
amount -= coupon
var deductionAmount int64
// Check user deduction amount
if u.GiftAmount > 0 {
if u.GiftAmount >= amount {
deductionAmount = amount
amount = 0
u.GiftAmount -= amount
} else {
deductionAmount = u.GiftAmount
amount -= u.GiftAmount
u.GiftAmount = 0
}
}
var feeAmount int64
// Calculate the handling fee
if amount > 0 {
feeAmount = calculateFee(amount, payment)
}
amount += feeAmount
// create order
orderInfo := order.Order{
UserId: u.Id,
ParentId: userSubscribe.OrderId,
OrderNo: orderNo,
Type: 2,
Quantity: req.Quantity,
Price: price,
Amount: amount,
GiftAmount: deductionAmount,
Discount: discountAmount,
Coupon: req.Coupon,
CouponDiscount: coupon,
PaymentId: payment.Id,
Method: payment.Platform,
FeeAmount: feeAmount,
Status: 1,
SubscribeId: userSubscribe.SubscribeId,
SubscribeToken: userSubscribe.Token,
}
// Database transaction
err = l.svcCtx.DB.Transaction(func(db *gorm.DB) error {
// update user deduction && Pre deduction ,Return after canceling the order
if orderInfo.GiftAmount > 0 {
// update user deduction && Pre deduction ,Return after canceling the order
if err := l.svcCtx.UserModel.Update(l.ctx, u, db); err != nil {
l.Error("[Purchase] Database update error", logger.Field("error", err.Error()), logger.Field("user", u))
return err
}
// create deduction record
deductionLog := user.GiftAmountLog{
UserId: orderInfo.UserId,
OrderNo: orderInfo.OrderNo,
Amount: orderInfo.GiftAmount,
Type: 2,
Balance: u.GiftAmount,
Remark: "Renewal order deduction",
}
if err := db.Model(&user.GiftAmountLog{}).Create(&deductionLog).Error; err != nil {
l.Error("[Renewal] Database insert error", logger.Field("error", err.Error()), logger.Field("deductionLog", deductionLog))
return err
}
}
// insert order
return db.Model(&order.Order{}).Create(&orderInfo).Error
})
if err != nil {
l.Error("[Renewal] Database insert error", logger.Field("error", err.Error()), logger.Field("order", orderInfo))
return nil, errors.Wrapf(err, "insert order error: %v", err.Error())
}
// Deferred task
payload := queue.DeferCloseOrderPayload{
OrderNo: orderInfo.OrderNo,
}
val, err := json.Marshal(payload)
if err != nil {
l.Error("[Renewal] Marshal payload error", logger.Field("error", err.Error()), logger.Field("payload", payload))
}
task := asynq.NewTask(queue.DeferCloseOrder, val, asynq.MaxRetry(3))
taskInfo, err := l.svcCtx.Queue.Enqueue(task, asynq.ProcessIn(CloseOrderTimeMinutes*time.Minute))
if err != nil {
l.Error("[Renewal] Enqueue task error", logger.Field("error", err.Error()), logger.Field("task", task))
} else {
l.Info("[Renewal] Enqueue task success", logger.Field("TaskID", taskInfo.ID))
}
return &types.RenewalOrderResponse{
OrderNo: orderInfo.OrderNo,
}, nil
}
@@ -1,146 +0,0 @@
package order
import (
"context"
"encoding/json"
"time"
"github.com/perfect-panel/server/pkg/constant"
"github.com/perfect-panel/server/pkg/xerr"
"gorm.io/gorm"
"github.com/hibiken/asynq"
"github.com/perfect-panel/server/internal/model/order"
"github.com/perfect-panel/server/internal/model/user"
"github.com/perfect-panel/server/pkg/tool"
queue "github.com/perfect-panel/server/queue/types"
"github.com/pkg/errors"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/logger"
)
type ResetTrafficLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// Reset traffic
func NewResetTrafficLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ResetTrafficLogic {
return &ResetTrafficLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *ResetTrafficLogic) ResetTraffic(req *types.ResetTrafficOrderRequest) (resp *types.ResetTrafficOrderResponse, err error) {
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 user subscription
userSubscribe, err := l.svcCtx.UserModel.FindOneUserSubscribe(l.ctx, req.UserSubscribeID)
if err != nil {
l.Error("[ResetTraffic] Database query error", logger.Field("error", err.Error()), logger.Field("UserSubscribeID", req.UserSubscribeID))
return nil, errors.Wrapf(err, "find user subscribe error: %v", err.Error())
}
if userSubscribe.Subscribe == nil {
l.Error("[ResetTraffic] subscribe not found", logger.Field("UserSubscribeID", req.UserSubscribeID))
return nil, errors.New("subscribe not found")
}
amount := userSubscribe.Subscribe.Replacement
var deductionAmount int64
// Check user deduction amount
if u.GiftAmount > 0 {
if u.GiftAmount >= amount {
deductionAmount = amount
amount = 0
u.GiftAmount -= amount
} else {
deductionAmount = u.GiftAmount
amount -= u.GiftAmount
u.GiftAmount = 0
}
}
// find payment method
payment, err := l.svcCtx.PaymentModel.FindOne(l.ctx, req.Payment)
if err != nil {
l.Error("[ResetTraffic] Database query error", logger.Field("error", err.Error()), logger.Field("payment", req.Payment))
return nil, errors.Wrapf(err, "find payment error: %v", err.Error())
}
var feeAmount int64
// Calculate the handling fee
if amount > 0 {
feeAmount = calculateFee(amount, payment)
}
// create order
orderInfo := order.Order{
Id: 0,
ParentId: userSubscribe.OrderId,
UserId: u.Id,
OrderNo: tool.GenerateTradeNo(),
Type: 3,
Price: userSubscribe.Subscribe.Replacement,
Amount: amount + feeAmount,
GiftAmount: deductionAmount,
FeeAmount: feeAmount,
PaymentId: req.Payment,
Method: payment.Platform,
Status: 1,
SubscribeId: userSubscribe.SubscribeId,
SubscribeToken: userSubscribe.Token,
}
// Database transaction
err = l.svcCtx.DB.Transaction(func(db *gorm.DB) error {
// update user deduction && Pre deduction ,Return after canceling the order
if orderInfo.GiftAmount > 0 {
// update user deduction && Pre deduction ,Return after canceling the order
if err := l.svcCtx.UserModel.Update(l.ctx, u, db); err != nil {
l.Error("[ResetTraffic] Database update error", logger.Field("error", err.Error()), logger.Field("user", u))
return err
}
// create deduction record
deductionLog := user.GiftAmountLog{
UserId: orderInfo.UserId,
OrderNo: orderInfo.OrderNo,
Amount: orderInfo.GiftAmount,
Type: 2,
Balance: u.GiftAmount,
Remark: "ResetTraffic order deduction",
}
if err := db.Model(&user.GiftAmountLog{}).Create(&deductionLog).Error; err != nil {
l.Error("[ResetTraffic] Database insert error", logger.Field("error", err.Error()), logger.Field("deductionLog", deductionLog))
return err
}
}
// insert order
return db.Model(&order.Order{}).Create(&orderInfo).Error
})
if err != nil {
l.Error("[ResetTraffic] Database insert error", logger.Field("error", err.Error()), logger.Field("order", orderInfo))
return nil, errors.Wrapf(err, "insert order error: %v", err.Error())
}
// Deferred task
payload := queue.DeferCloseOrderPayload{
OrderNo: orderInfo.OrderNo,
}
val, err := json.Marshal(payload)
if err != nil {
l.Error("[ResetTraffic] Marshal payload error", logger.Field("error", err.Error()), logger.Field("payload", payload))
}
task := asynq.NewTask(queue.DeferCloseOrder, val, asynq.MaxRetry(3))
taskInfo, err := l.svcCtx.Queue.Enqueue(task, asynq.ProcessIn(CloseOrderTimeMinutes*time.Minute))
if err != nil {
l.Error("[ResetTraffic] Enqueue task error", logger.Field("error", err.Error()), logger.Field("task", task))
} else {
l.Info("[ResetTraffic] Enqueue task success", logger.Field("TaskID", taskInfo.ID))
}
return &types.ResetTrafficOrderResponse{
OrderNo: orderInfo.OrderNo,
}, nil
}
@@ -1,40 +0,0 @@
package payment
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/tool"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
)
type GetAvailablePaymentMethodsLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// NewGetAvailablePaymentMethodsLogic Get available payment methods
func NewGetAvailablePaymentMethodsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetAvailablePaymentMethodsLogic {
return &GetAvailablePaymentMethodsLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetAvailablePaymentMethodsLogic) GetAvailablePaymentMethods() (resp *types.GetAvailablePaymentMethodsResponse, err error) {
data, err := l.svcCtx.PaymentModel.FindAvailableMethods(l.ctx)
if err != nil {
l.Error("[GetAvailablePaymentMethods] database error", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "GetAvailablePaymentMethods: %v", err.Error())
}
resp = &types.GetAvailablePaymentMethodsResponse{
List: make([]types.PaymentMethod, 0),
}
tool.DeepCopy(&resp.List, data)
return
}
@@ -1,115 +0,0 @@
package subscribe
import (
"context"
"github.com/perfect-panel/server/internal/model/application"
"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 QueryApplicationConfigLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// Get application config
func NewQueryApplicationConfigLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryApplicationConfigLogic {
return &QueryApplicationConfigLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *QueryApplicationConfigLogic) QueryApplicationConfig() (resp *types.ApplicationResponse, err error) {
resp = &types.ApplicationResponse{}
var applications []*application.Application
err = l.svcCtx.ApplicationModel.Transaction(l.ctx, func(tx *gorm.DB) (err error) {
return tx.Model(applications).Preload("ApplicationVersions").Find(&applications).Error
})
if err != nil {
l.Errorw("[QueryApplicationConfig] get application error: ", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "get application error: %v", err.Error())
}
if len(applications) == 0 {
return resp, nil
}
for _, app := range applications {
applicationResponse := types.ApplicationResponseInfo{
Id: app.Id,
Name: app.Name,
Icon: app.Icon,
Description: app.Description,
SubscribeType: app.SubscribeType,
}
applicationVersions := app.ApplicationVersions
if len(applicationVersions) != 0 {
for _, applicationVersion := range applicationVersions {
/*if !applicationVersion.IsDefault {
continue
}*/
switch applicationVersion.Platform {
case "ios":
applicationResponse.Platform.IOS = append(applicationResponse.Platform.IOS, &types.ApplicationVersion{
Id: applicationVersion.Id,
Url: applicationVersion.Url,
Version: applicationVersion.Version,
IsDefault: applicationVersion.IsDefault,
Description: applicationVersion.Description,
})
case "macos":
applicationResponse.Platform.MacOS = append(applicationResponse.Platform.MacOS, &types.ApplicationVersion{
Id: applicationVersion.Id,
Url: applicationVersion.Url,
Version: applicationVersion.Version,
IsDefault: applicationVersion.IsDefault,
Description: applicationVersion.Description,
})
case "linux":
applicationResponse.Platform.Linux = append(applicationResponse.Platform.Linux, &types.ApplicationVersion{
Id: applicationVersion.Id,
Url: applicationVersion.Url,
Version: applicationVersion.Version,
IsDefault: applicationVersion.IsDefault,
Description: applicationVersion.Description,
})
case "android":
applicationResponse.Platform.Android = append(applicationResponse.Platform.Android, &types.ApplicationVersion{
Id: applicationVersion.Id,
Url: applicationVersion.Url,
Version: applicationVersion.Version,
IsDefault: applicationVersion.IsDefault,
Description: applicationVersion.Description,
})
case "windows":
applicationResponse.Platform.Windows = append(applicationResponse.Platform.Windows, &types.ApplicationVersion{
Id: applicationVersion.Id,
Url: applicationVersion.Url,
Version: applicationVersion.Version,
IsDefault: applicationVersion.IsDefault,
Description: applicationVersion.Description,
})
case "harmony":
applicationResponse.Platform.Harmony = append(applicationResponse.Platform.Harmony, &types.ApplicationVersion{
Id: applicationVersion.Id,
Url: applicationVersion.Url,
Version: applicationVersion.Version,
IsDefault: applicationVersion.IsDefault,
Description: applicationVersion.Description,
})
}
}
}
resp.Applications = append(resp.Applications, applicationResponse)
}
return
}
@@ -1,44 +0,0 @@
package subscribe
import (
"context"
"github.com/perfect-panel/server/internal/model/subscribe"
"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/tool"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
)
type QuerySubscribeGroupListLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// Get subscribe group list
func NewQuerySubscribeGroupListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QuerySubscribeGroupListLogic {
return &QuerySubscribeGroupListLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *QuerySubscribeGroupListLogic) QuerySubscribeGroupList() (resp *types.QuerySubscribeGroupListResponse, err error) {
var list []*subscribe.Group
var total int64
err = l.svcCtx.DB.Model(&subscribe.Group{}).Count(&total).Find(&list).Error
if err != nil {
l.Logger.Error("[QuerySubscribeGroupListLogic] get subscribe group list failed: ", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "get subscribe group list failed: %v", err.Error())
}
groupList := make([]types.SubscribeGroup, 0)
tool.DeepCopy(&groupList, list)
return &types.QuerySubscribeGroupListResponse{
Total: total,
List: groupList,
}, nil
}
@@ -1,55 +0,0 @@
package subscribe
import (
"context"
"encoding/json"
"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/tool"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
)
type QuerySubscribeListLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// Get subscribe list
func NewQuerySubscribeListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QuerySubscribeListLogic {
return &QuerySubscribeListLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *QuerySubscribeListLogic) QuerySubscribeList() (resp *types.QuerySubscribeListResponse, err error) {
data, err := l.svcCtx.SubscribeModel.QuerySubscribeList(l.ctx)
if err != nil {
l.Errorw("[QuerySubscribeListLogic] Database Error", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "QuerySubscribeList error: %v", err.Error())
}
resp = &types.QuerySubscribeListResponse{
List: make([]types.Subscribe, 0),
Total: int64(len(data)),
}
for _, v := range data {
var sub types.Subscribe
tool.DeepCopy(&sub, v)
if v.Discount != "" {
if err = json.Unmarshal([]byte(v.Discount), &sub.Discount); err != nil {
l.Errorw("[QuerySubscribeListLogic] json.Unmarshal Error", logger.Field("error", err.Error()), logger.Field("value", v.Discount))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "json.Unmarshal error: %v", err.Error())
}
} else {
sub.Discount = make([]types.SubscribeDiscount, 0)
}
resp.List = append(resp.List, sub)
}
return
}
@@ -1,67 +0,0 @@
package subscribe
import (
"context"
"github.com/perfect-panel/server/internal/model/order"
"github.com/perfect-panel/server/internal/model/user"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/constant"
"github.com/perfect-panel/server/pkg/logger"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
"gorm.io/gorm"
)
type QueryUserAlreadySubscribeLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// Get Already subscribed to package
func NewQueryUserAlreadySubscribeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryUserAlreadySubscribeLogic {
return &QueryUserAlreadySubscribeLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *QueryUserAlreadySubscribeLogic) QueryUserAlreadySubscribe() (resp *types.QueryUserSubscribeResp, err error) {
resp = &types.QueryUserSubscribeResp{
Data: make([]types.UserSubscribeData, 0),
}
userInfo := l.ctx.Value(constant.CtxKeyUser).(*user.User)
var orderIds []int64
var subscribes []user.Subscribe
err = l.svcCtx.OrderModel.Transaction(context.Background(), func(tx *gorm.DB) error {
if err := tx.Model(&order.Order{}).Where("user_id = ? AND status in ?", userInfo.Id, []int64{2, 5}).Select("id").Find(&orderIds).Error; err != nil {
return err
}
if len(orderIds) == 0 {
return nil
}
return tx.Model(&user.Subscribe{}).Where("user_id = ? AND order_id in ?", userInfo.Id, orderIds).Order("created_at desc").Find(&subscribes).Error
})
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find order error: %v", err.Error())
}
if len(subscribes) == 0 {
return
}
userAlreadySubscribe := make(map[int64]int64)
for _, subscribe := range subscribes {
userAlreadySubscribe[subscribe.SubscribeId] = subscribe.Id
}
for k, v := range userAlreadySubscribe {
resp.Data = append(resp.Data, types.UserSubscribeData{
SubscribeId: k,
UserSubscribeId: v,
})
}
return
}
@@ -1,107 +0,0 @@
package subscribe
import (
"context"
"strconv"
"strings"
"time"
"github.com/perfect-panel/server/internal/model/user"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/constant"
"github.com/perfect-panel/server/pkg/logger"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
)
type QueryUserAvailableUserSubscribeLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// Get Available subscriptions for users
func NewQueryUserAvailableUserSubscribeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryUserAvailableUserSubscribeLogic {
return &QueryUserAvailableUserSubscribeLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *QueryUserAvailableUserSubscribeLogic) QueryUserAvailableUserSubscribe(req *types.AppUserSubscribeRequest) (resp *types.AppUserSubscbribeResponse, err error) {
resp = &types.AppUserSubscbribeResponse{List: make([]types.AppUserSubcbribe, 0)}
userInfo := l.ctx.Value(constant.CtxKeyUser).(*user.User)
//查询用户订阅
subscribeDetails, err := l.svcCtx.UserModel.QueryUserSubscribe(l.ctx, userInfo.Id, 1, 2)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "get query user subscribe error: %v", err.Error())
}
userSubscribeMap := make(map[int64]types.AppUserSubcbribe)
for _, sd := range subscribeDetails {
userSubscribeInfo := types.AppUserSubcbribe{
Id: sd.Id,
Name: sd.Subscribe.Name,
Traffic: sd.Traffic,
Upload: sd.Upload,
Download: sd.Download,
ExpireTime: sd.ExpireTime.Format(time.DateTime),
StartTime: sd.StartTime.Format(time.DateTime),
DeviceLimit: sd.Subscribe.DeviceLimit,
}
//不需要查询节点
if req.ContainsNodes == nil || !*req.ContainsNodes {
resp.List = append(resp.List, userSubscribeInfo)
continue
}
//拿到所有订阅下的服务组id
var ids []int64
for _, idStr := range strings.Split(sd.Subscribe.ServerGroup, ",") {
id, err := strconv.ParseInt(idStr, 10, 64)
if err != nil {
continue
}
ids = append(ids, id)
}
//根据服务组id拿到所有节点
servers, err := l.svcCtx.ServerModel.FindServerListByGroupIds(l.ctx, ids)
if err != nil {
l.Logger.Errorf("FindServerListByGroupIds error: %v", err.Error())
continue
}
for _, server := range servers {
userSubscribeInfo.List = append(userSubscribeInfo.List, types.AppUserSubscbribeNode{
Id: server.Id,
Uuid: sd.UUID,
Traffic: sd.Traffic,
Upload: sd.Upload,
Download: sd.Download,
RelayNode: server.RelayNode,
RelayMode: server.RelayMode,
Longitude: server.Longitude,
Latitude: server.Latitude,
Tags: strings.Split(server.Tags, ","),
Config: server.Config,
ServerAddr: server.ServerAddr,
Protocol: server.Protocol,
SpeedLimit: server.SpeedLimit,
City: server.City,
Country: server.Country,
Name: server.Name,
})
}
resp.List = append(resp.List, userSubscribeInfo)
userSubscribeMap[userSubscribeInfo.Id] = userSubscribeInfo
}
for _, userSubscribeInfo := range userSubscribeMap {
resp.List = append(resp.List, userSubscribeInfo)
}
return resp, nil
}
@@ -1,60 +0,0 @@
package subscribe
import (
"context"
"time"
"github.com/perfect-panel/server/internal/model/user"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/constant"
"github.com/perfect-panel/server/pkg/logger"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
)
type ResetUserSubscribePeriodLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewResetUserSubscribePeriodLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ResetUserSubscribePeriodLogic {
return &ResetUserSubscribePeriodLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *ResetUserSubscribePeriodLogic) ResetUserSubscribePeriod(req *types.UserSubscribeResetPeriodRequest) (resp *types.UserSubscribeResetPeriodResponse, err error) {
resp = &types.UserSubscribeResetPeriodResponse{}
userInfo := l.ctx.Value(constant.CtxKeyUser).(*user.User)
subscribe, err := l.svcCtx.UserModel.FindOneSubscribe(l.ctx, req.UserSubscribeId)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find order error: %v", err.Error())
}
if userInfo.Id != subscribe.UserId {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SubscribeNotAvailable), "user not authorized,subscribe not available")
}
if time.Now().After(subscribe.ExpireTime) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SubscribeExpired), "subscribe expired")
}
if subscribe.Traffic < 1 {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.ExistAvailableTraffic), "Unlimited data plan.")
}
if (subscribe.Download + subscribe.Upload + 10240) < subscribe.Traffic {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.ExistAvailableTraffic), "There is still available traffic.")
}
subscribe.ExpireTime = subscribe.ExpireTime.AddDate(0, -1, 0)
err = l.svcCtx.UserModel.UpdateSubscribe(l.ctx, subscribe)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "update subscribe error: %v", err.Error())
}
resp.Status = true
return
}
@@ -1,103 +0,0 @@
package user
import (
"context"
"encoding/json"
"fmt"
"github.com/perfect-panel/server/internal/config"
"github.com/perfect-panel/server/internal/logic/common"
"github.com/perfect-panel/server/internal/model/user"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/constant"
"github.com/perfect-panel/server/pkg/logger"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
)
type DeleteAccountLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// Delete Account
func NewDeleteAccountLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteAccountLogic {
return &DeleteAccountLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *DeleteAccountLogic) DeleteAccount(req *types.DeleteAccountRequest) error {
userInfo, exists := l.ctx.Value(constant.CtxKeyUser).(user.User)
if !exists {
return nil
}
var account string
for _, authMethod := range userInfo.AuthMethods {
if authMethod.AuthType == req.Method {
account = authMethod.AuthIdentifier
break
}
}
if account == "" {
return nil
}
if req.Method == "email" {
emailConfig := l.svcCtx.Config.Email
if !emailConfig.Enable {
return errors.Wrapf(xerr.NewErrCode(xerr.EmailNotEnabled), "Email function is not enabled yet")
}
if emailConfig.EnableVerify {
cacheKey := fmt.Sprintf("%s:%s:%s", config.AuthCodeCacheKey, constant.Security, account)
value, err := l.svcCtx.Redis.Get(l.ctx, cacheKey).Result()
if err != nil {
l.Errorw("Redis Error", logger.Field("error", err.Error()), logger.Field("cacheKey", cacheKey))
return errors.Wrapf(xerr.NewErrCode(xerr.VerifyCodeError), "code error")
}
var payload common.CacheKeyPayload
err = json.Unmarshal([]byte(value), &payload)
if err != nil {
l.Errorw("Unmarshal Error", logger.Field("error", err.Error()), logger.Field("value", value))
return errors.Wrapf(xerr.NewErrCode(xerr.VerifyCodeError), "code error")
}
if payload.Code != req.Code {
return errors.Wrapf(xerr.NewErrCode(xerr.VerifyCodeError), "code error")
}
}
} else {
cacheKey := fmt.Sprintf("%s:%s:%s", config.AuthCodeTelephoneCacheKey, constant.Security, account)
value, err := l.svcCtx.Redis.Get(l.ctx, cacheKey).Result()
if err != nil {
l.Errorw("Redis Error", logger.Field("error", err.Error()), logger.Field("cacheKey", cacheKey))
return errors.Wrapf(xerr.NewErrCode(xerr.VerifyCodeError), "code error")
}
if value == "" {
return errors.Wrapf(xerr.NewErrCode(xerr.VerifyCodeError), "code error")
}
var payload common.CacheKeyPayload
if err := json.Unmarshal([]byte(value), &payload); err != nil {
l.Errorw("[SendSmsCode]: Unmarshal Error", logger.Field("error", err.Error()), logger.Field("value", value))
}
if payload.Code != req.Code {
return errors.Wrapf(xerr.NewErrCode(xerr.VerifyCodeError), "code error")
}
}
err := l.svcCtx.UserModel.Delete(l.ctx, userInfo.Id)
if err != nil {
l.Errorw("update user password error", logger.Field("error", err.Error()))
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "update user password")
}
return nil
}
@@ -1,115 +0,0 @@
package user
import (
"context"
"sort"
"time"
"github.com/perfect-panel/server/internal/model/user"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/constant"
"github.com/perfect-panel/server/pkg/logger"
)
type GetUserOnlineTimeStatisticsLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// Get user online time total
func NewGetUserOnlineTimeStatisticsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserOnlineTimeStatisticsLogic {
return &GetUserOnlineTimeStatisticsLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetUserOnlineTimeStatisticsLogic) GetUserOnlineTimeStatistics() (resp *types.GetUserOnlineTimeStatisticsResponse, err error) {
u := l.ctx.Value(constant.CtxKeyUser).(*user.User)
//获取历史最长在线时间
var OnlineSeconds int64
if err := l.svcCtx.DB.Model(user.DeviceOnlineRecord{}).Where("user_id = ?", u.Id).Select("online_seconds").Order("online_seconds desc").Limit(1).Scan(&OnlineSeconds).Error; err != nil {
l.Logger.Error(err)
}
//获取历史连续最长在线天数
var DurationDays int64
if err := l.svcCtx.DB.Model(user.DeviceOnlineRecord{}).Where("user_id = ?", u.Id).Select("duration_days").Order("duration_days desc").Limit(1).Scan(&DurationDays).Error; err != nil {
l.Logger.Error(err)
}
//获取近七天在线情况
var userOnlineRecord []user.DeviceOnlineRecord
if err := l.svcCtx.DB.Model(&userOnlineRecord).Where("user_id = ? and created_at >= ?", u.Id, time.Now().AddDate(0, 0, -7).Format(time.DateTime)).Order("created_at desc").Find(&userOnlineRecord).Error; err != nil {
l.Logger.Error(err)
}
//获取当前连续在线天数
var currentContinuousDays int64
if len(userOnlineRecord) > 0 {
currentContinuousDays = userOnlineRecord[0].DurationDays
} else {
currentContinuousDays = 1
}
var dates []string
for i := 0; i < 7; i++ {
date := time.Now().AddDate(0, 0, -i).Format(time.DateOnly)
dates = append(dates, date)
}
onlineDays := make(map[string]types.WeeklyStat)
for _, record := range userOnlineRecord {
//获取近七天在线情况
onlineTime := record.OnlineTime.Format(time.DateOnly)
if weeklyStat, ok := onlineDays[onlineTime]; ok {
weeklyStat.Hours += float64(record.OnlineSeconds)
onlineDays[onlineTime] = weeklyStat
} else {
onlineDays[onlineTime] = types.WeeklyStat{
Hours: float64(record.OnlineSeconds),
//根据日期获取周几
DayName: record.OnlineTime.Weekday().String(),
}
}
}
//补全不存在的日期
for _, date := range dates {
if _, ok := onlineDays[date]; !ok {
onlineTime, _ := time.Parse(time.DateOnly, date)
onlineDays[date] = types.WeeklyStat{
DayName: onlineTime.Weekday().String(),
}
}
}
var keys []string
for key := range onlineDays {
keys = append(keys, key)
}
//排序
sort.Strings(keys)
var weeklyStats []types.WeeklyStat
for index, key := range keys {
weeklyStat := onlineDays[key]
weeklyStat.Day = index + 1
weeklyStat.Hours = weeklyStat.Hours / float64(3600)
weeklyStats = append(weeklyStats, weeklyStat)
}
resp = &types.GetUserOnlineTimeStatisticsResponse{
WeeklyStats: weeklyStats,
ConnectionRecords: types.ConnectionRecords{
CurrentContinuousDays: currentContinuousDays,
HistoryContinuousDays: DurationDays,
LongestSingleConnection: OnlineSeconds / 60,
},
}
return
}
@@ -1,85 +0,0 @@
package user
import (
"context"
"time"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
"github.com/perfect-panel/server/internal/model/traffic"
"github.com/perfect-panel/server/internal/model/user"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/constant"
"github.com/perfect-panel/server/pkg/logger"
"gorm.io/gorm"
)
type GetUserSubscribeTrafficLogsLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// Get user subcribe traffic logs
func NewGetUserSubscribeTrafficLogsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserSubscribeTrafficLogsLogic {
return &GetUserSubscribeTrafficLogsLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetUserSubscribeTrafficLogsLogic) GetUserSubscribeTrafficLogs(req *types.GetUserSubscribeTrafficLogsRequest) (resp *types.GetUserSubscribeTrafficLogsResponse, err error) {
resp = &types.GetUserSubscribeTrafficLogsResponse{}
u := l.ctx.Value(constant.CtxKeyUser).(*user.User)
var traffics []traffic.TrafficLog
err = l.svcCtx.UserModel.Transaction(l.ctx, func(db *gorm.DB) error {
return db.Model(traffic.TrafficLog{}).Where("user_id = ? and `timestamp` >= ? and `timestamp` < ?", u.Id, time.UnixMilli(req.StartTime), time.UnixMilli(req.EndTime)).Find(&traffics).Error
})
if err != nil {
l.Errorw("get user subscribe traffic logs failed", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), err.Error())
}
//合并多条记录为以天为单位
trafficMap := make(map[string]*traffic.TrafficLog)
for _, traf := range traffics {
key := traf.Timestamp.Format(time.DateOnly)
existTraf := trafficMap[key]
if existTraf == nil {
trafficMap[key] = &traf
} else {
existTraf.Upload = existTraf.Download + traf.Upload
existTraf.Download = existTraf.Download + traf.Download
trafficMap[key] = existTraf
}
}
startTime := time.UnixMilli(req.StartTime)
EndTime := time.UnixMilli(req.EndTime)
res := make(map[string]traffic.TrafficLog)
// 循环遍历每一天
for current := startTime; !current.After(EndTime); current = current.AddDate(0, 0, 1) {
dateStr := current.Format(time.DateOnly) // 格式化为日期字符串
if trafficMap[dateStr] == nil {
res[dateStr] = traffic.TrafficLog{
Timestamp: current,
}
} else {
res[dateStr] = *trafficMap[dateStr]
}
resp.List = append(resp.List, types.TrafficLog{
Id: res[dateStr].Id,
ServerId: res[dateStr].ServerId,
Upload: res[dateStr].Upload,
Download: res[dateStr].Download,
Timestamp: res[dateStr].Timestamp.UnixMilli(),
})
}
return
}
@@ -1,62 +0,0 @@
package user
import (
"context"
"github.com/perfect-panel/server/pkg/constant"
"github.com/perfect-panel/server/internal/model/user"
"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 QueryUserAffiliateListLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// Query User Affiliate List
func NewQueryUserAffiliateListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryUserAffiliateListLogic {
return &QueryUserAffiliateListLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *QueryUserAffiliateListLogic) QueryUserAffiliateList(req *types.QueryUserAffiliateListRequest) (resp *types.QueryUserAffiliateListResponse, err error) {
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")
}
var data []*user.User
var total int64
err = l.svcCtx.UserModel.Transaction(l.ctx, func(db *gorm.DB) error {
return db.Model(&user.User{}).Order("id desc").Where("referer_id = ?", u.Id).Count(&total).Limit(req.Size).Offset((req.Page - 1) * req.Size).Find(&data).Error
})
if err != nil {
l.Errorw("Query User Affiliate List failed: %v", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "Query User Affiliate List failed: %v", err.Error())
}
list := make([]types.UserAffiliate, 0)
for _, item := range data {
list = append(list, types.UserAffiliate{
//Email: tool.MaskEmail(item.Email),
Avatar: item.Avatar,
RegisteredAt: item.CreatedAt.UnixMilli(),
Enable: *item.Enable,
})
}
return &types.QueryUserAffiliateListResponse{
Total: total,
List: list,
}, nil
}
@@ -1,63 +0,0 @@
package user
import (
"context"
"github.com/perfect-panel/server/pkg/constant"
"github.com/perfect-panel/server/internal/model/user"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/logger"
)
type QueryUserInfoLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// query user info
func NewQueryUserInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryUserInfoLogic {
return &QueryUserInfoLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *QueryUserInfoLogic) QueryUserInfo() (resp *types.UserInfoResponse, err error) {
u := l.ctx.Value(constant.CtxKeyUser).(*user.User)
var devices []types.UserDevice
if len(u.UserDevices) != 0 {
for _, device := range u.UserDevices {
devices = append(devices, types.UserDevice{
Id: device.Id,
Identifier: device.Identifier,
Online: device.Online,
})
}
}
var authMeths []types.UserAuthMethod
authMethods, err := l.svcCtx.UserModel.FindUserAuthMethods(l.ctx, u.Id)
if err == nil && len(authMeths) != 0 {
for _, as := range authMethods {
authMeths = append(authMeths, types.UserAuthMethod{
AuthType: as.AuthType,
AuthIdentifier: as.AuthIdentifier,
})
}
}
resp = &types.UserInfoResponse{
Id: u.Id,
Balance: u.Balance,
Avatar: u.Avatar,
ReferCode: u.ReferCode,
RefererId: u.RefererId,
Devices: devices,
AuthMethods: authMeths,
}
return
}
@@ -1,60 +0,0 @@
package user
import (
"context"
"github.com/perfect-panel/server/internal/model/user"
"github.com/perfect-panel/server/pkg/constant"
"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 QueryUserAffiliateLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// Query User Affiliate Count
func NewQueryUserAffiliateLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryUserAffiliateLogic {
return &QueryUserAffiliateLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *QueryUserAffiliateLogic) QueryUserAffiliate() (resp *types.QueryUserAffiliateCountResponse, err error) {
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")
}
var sum int64
var total int64
err = l.svcCtx.UserModel.Transaction(l.ctx, func(db *gorm.DB) error {
return db.Model(&user.User{}).Where("referer_id = ?", u.Id).Count(&total).Find(&user.User{}).Error
})
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "Query User Affiliate failed: %v", err)
}
err = l.svcCtx.UserModel.Transaction(l.ctx, func(db *gorm.DB) error {
return db.Model(&user.CommissionLog{}).
Where("user_id = ?", u.Id).
Select("COALESCE(SUM(amount), 0)").
Scan(&sum).Error
})
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "Query User Affiliate failed: %v", err)
}
return &types.QueryUserAffiliateCountResponse{
Registers: total,
TotalCommission: sum,
}, nil
}
@@ -1,46 +0,0 @@
package user
import (
"context"
"github.com/perfect-panel/server/pkg/constant"
"github.com/perfect-panel/server/internal/model/user"
"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/tool"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
)
type UpdatePasswordLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// Update Password
func NewUpdatePasswordLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdatePasswordLogic {
return &UpdatePasswordLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *UpdatePasswordLogic) UpdatePassword(req *types.UpdatePasswordRequeset) error {
userInfo := l.ctx.Value(constant.CtxKeyUser).(*user.User)
// Verify password
if !tool.VerifyPassWord(req.Password, userInfo.Password) {
return errors.Wrapf(xerr.NewErrCode(xerr.UserPasswordError), "user password")
}
userInfo.Password = tool.EncodePassWord(req.NewPassword)
err := l.svcCtx.UserModel.Update(l.ctx, userInfo)
if err != nil {
l.Errorw("update user password error", logger.Field("error", err.Error()))
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "update user password")
}
return err
}
-81
View File
@@ -1,81 +0,0 @@
package ws
import (
"context"
"net/http"
"strconv"
"time"
"github.com/perfect-panel/server/internal/model/user"
"github.com/perfect-panel/server/pkg/constant"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/pkg/logger"
)
type AppWsLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// App heartbeat
func NewAppWsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AppWsLogic {
return &AppWsLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AppWsLogic) AppWs(w http.ResponseWriter, r *http.Request, userid, identifier string) error {
//获取设备号
if identifier == "" {
return xerr.NewErrCode(xerr.DeviceNotExist)
}
//获取用户id
userID, err := strconv.ParseInt(userid, 10, 64)
if err != nil {
return xerr.NewErrCode(xerr.UseridNotMatch)
}
////获取session
value := l.ctx.Value(constant.CtxKeySessionID)
if value == nil {
return xerr.NewErrCode(xerr.ErrorTokenInvalid)
}
session := value.(string)
//获取用户
userInfo := l.ctx.Value(constant.CtxKeyUser).(*user.User)
if userID != userInfo.Id {
return xerr.NewErrCode(xerr.UseridNotMatch)
}
_, err = l.svcCtx.UserModel.FindOneDeviceByIdentifier(l.ctx, identifier)
if err != nil {
return xerr.NewErrCode(xerr.DeviceNotExist)
}
//if device.UserId != userInfo.Id {
// return xerr.NewErrCode(xerr.DeviceNotExist)
//}
//默认在线设备1
maxDevice := 0
subscribe, err := l.svcCtx.UserModel.QueryUserSubscribe(l.ctx, userInfo.Id)
if err == nil {
for _, sub := range subscribe {
if time.Now().Before(sub.ExpireTime) {
deviceLimit := int(sub.Subscribe.DeviceLimit)
if deviceLimit > maxDevice {
maxDevice = deviceLimit
}
}
}
}
l.svcCtx.DeviceManager.AddDevice(w, r, session, userID, identifier, maxDevice)
return nil
}
@@ -1,136 +0,0 @@
package common
import (
"context"
"strings"
"github.com/perfect-panel/server/internal/model/application"
"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 GetApplicationLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// Get Tos Content
func NewGetApplicationLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetApplicationLogic {
return &GetApplicationLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetApplicationLogic) GetApplication() (resp *types.GetAppcationResponse, err error) {
resp = &types.GetAppcationResponse{}
cfg, err := l.svcCtx.ApplicationModel.FindOneConfig(l.ctx, 1)
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
l.Logger.Error("[GetAppInfo] FindOneAppConfig error: ", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "GetAppInfo FindOneAppConfig error: %v", err.Error())
}
if err != nil {
resp.Config = types.ApplicationConfig{}
} else {
resp.Config = types.ApplicationConfig{
AppId: cfg.AppId,
EncryptionKey: cfg.EncryptionKey,
EncryptionMethod: cfg.EncryptionMethod,
Domains: strings.Split(cfg.Domains, ";"),
StartupPicture: cfg.StartupPicture,
StartupPictureSkipTime: cfg.StartupPictureSkipTime,
}
}
var applications []*application.Application
err = l.svcCtx.ApplicationModel.Transaction(l.ctx, func(tx *gorm.DB) (err error) {
return tx.Model(applications).Preload("ApplicationVersions").Find(&applications).Error
})
if err != nil {
l.Errorw("[QueryApplicationConfig] get application error: ", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "get application error: %v", err.Error())
}
if len(applications) == 0 {
return resp, nil
}
for _, app := range applications {
applicationResponse := types.ApplicationResponseInfo{
Id: app.Id,
Name: app.Name,
Icon: app.Icon,
Description: app.Description,
SubscribeType: app.SubscribeType,
}
applicationVersions := app.ApplicationVersions
if len(applicationVersions) != 0 {
for _, applicationVersion := range applicationVersions {
/*if !applicationVersion.IsDefault {
continue
}*/
switch applicationVersion.Platform {
case "ios":
applicationResponse.Platform.IOS = append(applicationResponse.Platform.IOS, &types.ApplicationVersion{
Id: applicationVersion.Id,
Url: applicationVersion.Url,
Version: applicationVersion.Version,
IsDefault: applicationVersion.IsDefault,
Description: applicationVersion.Description,
})
case "macos":
applicationResponse.Platform.MacOS = append(applicationResponse.Platform.MacOS, &types.ApplicationVersion{
Id: applicationVersion.Id,
Url: applicationVersion.Url,
Version: applicationVersion.Version,
IsDefault: applicationVersion.IsDefault,
Description: applicationVersion.Description,
})
case "linux":
applicationResponse.Platform.Linux = append(applicationResponse.Platform.Linux, &types.ApplicationVersion{
Id: applicationVersion.Id,
Url: applicationVersion.Url,
Version: applicationVersion.Version,
IsDefault: applicationVersion.IsDefault,
Description: applicationVersion.Description,
})
case "android":
applicationResponse.Platform.Android = append(applicationResponse.Platform.Android, &types.ApplicationVersion{
Id: applicationVersion.Id,
Url: applicationVersion.Url,
Version: applicationVersion.Version,
IsDefault: applicationVersion.IsDefault,
Description: applicationVersion.Description,
})
case "windows":
applicationResponse.Platform.Windows = append(applicationResponse.Platform.Windows, &types.ApplicationVersion{
Id: applicationVersion.Id,
Url: applicationVersion.Url,
Version: applicationVersion.Version,
IsDefault: applicationVersion.IsDefault,
Description: applicationVersion.Description,
})
case "harmony":
applicationResponse.Platform.Harmony = append(applicationResponse.Platform.Harmony, &types.ApplicationVersion{
Id: applicationVersion.Id,
Url: applicationVersion.Url,
Version: applicationVersion.Version,
IsDefault: applicationVersion.IsDefault,
Description: applicationVersion.Description,
})
}
}
}
resp.Applications = append(resp.Applications, applicationResponse)
}
return
}
@@ -1,116 +0,0 @@
package subscribe
import (
"context"
"github.com/perfect-panel/server/internal/model/application"
"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 QueryApplicationConfigLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// Get application config
func NewQueryApplicationConfigLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryApplicationConfigLogic {
return &QueryApplicationConfigLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *QueryApplicationConfigLogic) QueryApplicationConfig() (resp *types.ApplicationResponse, err error) {
resp = &types.ApplicationResponse{}
var applications []*application.Application
err = l.svcCtx.ApplicationModel.Transaction(l.ctx, func(tx *gorm.DB) (err error) {
return tx.Model(applications).Preload("ApplicationVersions").Find(&applications).Error
})
if err != nil {
l.Errorw("[QueryApplicationConfig] get application error: ", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "get application error: %v", err.Error())
}
if len(applications) == 0 {
return resp, nil
}
for _, app := range applications {
applicationResponse := types.ApplicationResponseInfo{
Id: app.Id,
Name: app.Name,
Icon: app.Icon,
Description: app.Description,
SubscribeType: app.SubscribeType,
}
applicationVersions := app.ApplicationVersions
if len(applicationVersions) != 0 {
for _, applicationVersion := range applicationVersions {
/*if !applicationVersion.IsDefault {
continue
}*/
switch applicationVersion.Platform {
case "ios":
applicationResponse.Platform.IOS = append(applicationResponse.Platform.IOS, &types.ApplicationVersion{
Id: applicationVersion.Id,
Url: applicationVersion.Url,
Version: applicationVersion.Version,
IsDefault: applicationVersion.IsDefault,
Description: applicationVersion.Description,
})
case "macos":
applicationResponse.Platform.MacOS = append(applicationResponse.Platform.MacOS, &types.ApplicationVersion{
Id: applicationVersion.Id,
Url: applicationVersion.Url,
Version: applicationVersion.Version,
IsDefault: applicationVersion.IsDefault,
Description: applicationVersion.Description,
})
case "linux":
applicationResponse.Platform.Linux = append(applicationResponse.Platform.Linux, &types.ApplicationVersion{
Id: applicationVersion.Id,
Url: applicationVersion.Url,
Version: applicationVersion.Version,
IsDefault: applicationVersion.IsDefault,
Description: applicationVersion.Description,
})
case "android":
applicationResponse.Platform.Android = append(applicationResponse.Platform.Android, &types.ApplicationVersion{
Id: applicationVersion.Id,
Url: applicationVersion.Url,
Version: applicationVersion.Version,
IsDefault: applicationVersion.IsDefault,
Description: applicationVersion.Description,
})
case "windows":
applicationResponse.Platform.Windows = append(applicationResponse.Platform.Windows, &types.ApplicationVersion{
Id: applicationVersion.Id,
Url: applicationVersion.Url,
Version: applicationVersion.Version,
IsDefault: applicationVersion.IsDefault,
Description: applicationVersion.Description,
})
case "harmony":
applicationResponse.Platform.Harmony = append(applicationResponse.Platform.Harmony, &types.ApplicationVersion{
Id: applicationVersion.Id,
Url: applicationVersion.Url,
Version: applicationVersion.Version,
IsDefault: applicationVersion.IsDefault,
Description: applicationVersion.Description,
})
}
}
}
resp.Applications = append(resp.Applications, applicationResponse)
}
return
}