This commit is contained in:
Sun
2023-11-08 21:53:07 +08:00
commit 211c3071dc
245 changed files with 39293 additions and 0 deletions
+11
View File
@@ -0,0 +1,11 @@
package system
type ApiSystem struct {
About About
LoginApi LoginApi
UserApi UserApi
FileApi FileApi
CaptchaApi CaptchaApi
RegisterApi RegisterApi
NoticeApi NoticeApi
}
+19
View File
@@ -0,0 +1,19 @@
package system
import (
"sun-panel/api/api_v1/common/apiReturn"
"sun-panel/lib/cmn"
"github.com/gin-gonic/gin"
)
type About struct {
}
func (a *About) Get(c *gin.Context) {
version := cmn.GetSysVersionInfo()
apiReturn.SuccessData(c, gin.H{
"versionName": version.Version,
"versionCode": version.Version_code,
})
}
+63
View File
@@ -0,0 +1,63 @@
package system
import (
"encoding/base64"
"strconv"
"sun-panel/lib/captcha"
"sun-panel/lib/cmn"
"github.com/gin-gonic/gin"
)
type CaptchaApi struct {
ErrMsg string // 错误信息
}
// 获取图像
func (c *CaptchaApi) GetImage(ctx *gin.Context) {
key := cmn.BuildRandCode(16, cmn.RAND_CODE_MODE2)
width, _ := strconv.Atoi(ctx.Param("width"))
height, _ := strconv.Atoi(ctx.Param("height"))
if width == 0 || width > 500 {
width = 120
}
if height == 0 || height > 500 {
height = 44
}
// 设置网页验证码的cookie
ctx.SetCookie("CaptchaId", key, 3600, "/", "", false, false)
base64Str := captcha.GenerateCaptchaHandler(key, width, height)
_ = base64Str
// base64 字符串一般会包含头部 data:image/xxx;base64, 需要去除
baseImg, _ := base64.StdEncoding.DecodeString(base64Str[22:])
_, _ = ctx.Writer.WriteString(string(baseImg))
}
// 获取图像根据验证器id,id从地址栏获取
func (c *CaptchaApi) GetImageByCaptchaId(ctx *gin.Context) {
// key := cmn.BuildRandCode(16, cmn.RAND_CODE_MODE2)
width, _ := strconv.Atoi(ctx.Param("width"))
height, _ := strconv.Atoi(ctx.Param("height"))
captchaId := ctx.Param("captchaId")
if width == 0 || width > 500 {
width = 120
}
if height == 0 || height > 500 {
height = 44
}
// 设置网页验证码的cookie
base64Str := captcha.GenerateCaptchaHandler(captchaId, width, height)
_ = base64Str
// base64 字符串一般会包含头部 data:image/xxx;base64, 需要去除
baseImg, _ := base64.StdEncoding.DecodeString(base64Str[22:])
_, _ = ctx.Writer.WriteString(string(baseImg))
}
func (c *CaptchaApi) CheckVCode(id, vcode string) {
// Captcha.Store = base64Captcha.DefaultMemStore
// if store.Verify(id, vcode, true) {
// body = map[string]interface{}{"code": 1001, "msg": "ok"}
// }
// w.Header().Set("Content-Type", "application/json; charset=utf-8")
// json.NewEncoder(w).Encode(body)
}
+87
View File
@@ -0,0 +1,87 @@
package system
import (
"fmt"
"os"
"path"
"strings"
"sun-panel/api/api_v1/common/apiReturn"
"sun-panel/api/api_v1/common/base"
"sun-panel/global"
"sun-panel/lib/cmn"
"sun-panel/models"
"time"
"github.com/gin-gonic/gin"
)
type FileApi struct{}
func (a *FileApi) UploadImg(c *gin.Context) {
userInfo, _ := base.GetCurrentUserInfo(c)
configUpload := global.Config.GetValueString("base", "source_path")
f, err := c.FormFile("imgfile")
if err != nil {
apiReturn.Error(c, "上传失败")
return
} else {
fileExt := strings.ToLower(path.Ext(f.Filename))
if fileExt != ".png" && fileExt != ".jpg" && fileExt != ".gif" && fileExt != ".jpeg" && fileExt != ".webp" && fileExt != ".svg" {
apiReturn.Error(c, "上传失败!只允许png,jpg,gif,jpeg,svg文件")
return
}
fileName := cmn.Md5(fmt.Sprintf("%s%s", f.Filename, time.Now().String()))
fildDir := fmt.Sprintf("%s/%d/%d/%d/", configUpload, time.Now().Year(), time.Now().Month(), time.Now().Day())
isExist, _ := cmn.PathExists(fildDir)
if !isExist {
os.MkdirAll(fildDir, os.ModePerm)
}
filepath := fmt.Sprintf("%s%s%s", fildDir, fileName, fileExt)
c.SaveUploadedFile(f, filepath)
// 像数据库添加记录
mFile := models.File{}
mFile.AddFile(userInfo.ID, f.Filename, fileExt, filepath)
apiReturn.SuccessData(c, gin.H{
"imageUrl": filepath[1:],
})
}
}
func (a *FileApi) UploadFiles(c *gin.Context) {
userInfo, _ := base.GetCurrentUserInfo(c)
configUpload := global.Config.GetValueString("base", "source_path")
form, err := c.MultipartForm()
if err != nil {
apiReturn.Error(c, "上传失败")
return
}
files := form.File["files[]"]
errFiles := []string{}
succMap := map[string]string{}
for _, f := range files {
fileExt := strings.ToLower(path.Ext(f.Filename))
fileName := cmn.Md5(fmt.Sprintf("%s%s", f.Filename, time.Now().String()))
fildDir := fmt.Sprintf("%s/%d/%d/%d/", configUpload, time.Now().Year(), time.Now().Month(), time.Now().Day())
isExist, _ := cmn.PathExists(fildDir)
if !isExist {
os.MkdirAll(fildDir, os.ModePerm)
}
filepath := fmt.Sprintf("%s%s%s", fildDir, fileName, fileExt)
if c.SaveUploadedFile(f, filepath) != nil {
errFiles = append(errFiles, f.Filename)
} else {
// 成功
// 像数据库添加记录
mFile := models.File{}
mFile.AddFile(userInfo.ID, f.Filename, fileExt, filepath)
succMap[f.Filename] = filepath[1:]
}
}
apiReturn.SuccessData(c, gin.H{
"succMap": succMap,
"errFiles": errFiles,
})
}
+215
View File
@@ -0,0 +1,215 @@
package system
import (
"strconv"
"sun-panel/api/api_v1/common/apiData/commonApiStructs"
"sun-panel/api/api_v1/common/apiReturn"
"sun-panel/api/api_v1/common/base"
"sun-panel/global"
"sun-panel/lib/captcha"
"sun-panel/lib/cmn"
"sun-panel/lib/cmn/systemSetting"
"sun-panel/lib/mail"
"sun-panel/models"
"time"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"gorm.io/gorm"
)
type LoginApi struct {
}
// 登录输入验证
type LoginLoginVerify struct {
Username string `json:"username" validate:"required,min=5"`
Password string `json:"password" validate:"required,min=5,max=20"`
VCode string `json:"vcode" validate:"max=6"`
Email string `json:"email"`
}
// @Summary 登录账号
// @Accept application/json
// @Produce application/json
// @Param LoginLoginVerify body LoginLoginVerify true "登陆验证信息"
// @Tags user
// @Router /login [post]
func (l LoginApi) Login(c *gin.Context) {
param := LoginLoginVerify{}
if err := c.ShouldBindJSON(&param); err != nil {
apiReturn.Error(c, global.Lang.Get("common.api_error_param_format"))
return
}
if errMsg, err := base.ValidateInputStruct(param); err != nil {
apiReturn.Error(c, errMsg)
return
}
settings := systemSetting.ApplicationSetting{}
global.SystemSetting.GetValueByInterface("system_application", &settings)
// 验证验证码
if settings.Login.LoginCaptcha {
var captchaId string
var err error
// 获取captchaId
if captchaId, err = captcha.CaptchaGetIdByCookieHeader(c, "CaptchaId"); err != nil {
apiReturn.Error(c, global.Lang.Get("login.err_captcha_check_fail"))
return
}
// 验证码错误
if !captcha.CaptchaVerifyHandle(captchaId, param.VCode) {
apiReturn.Error(c, global.Lang.Get("captcha.api_captcha_fail"))
return
}
}
mUser := models.User{}
var (
err error
info models.User
)
bToken := ""
if info, err = mUser.GetUserInfoByUsernameAndPassword(param.Username, cmn.PasswordEncryption(param.Password)); err != nil {
// 未找到记录 账号或密码错误
if err == gorm.ErrRecordNotFound {
apiReturn.Error(c, global.Lang.Get("login.err_username_password"))
return
} else {
// 未知错误
apiReturn.Error(c, err.Error())
return
}
}
// 停用或未激活
if info.Status != 1 {
apiReturn.Error(c, global.Lang.Get("login.err_username_deactivation"))
return
}
bToken = info.Token
if info.Token == "" {
// 生成token
buildTokenOver := false
for !buildTokenOver {
bToken = cmn.BuildRandCode(32, cmn.RAND_CODE_MODE2)
if _, err := mUser.GetUserInfoByToken(bToken); err != nil {
// 保存token
mUser.UpdateUserInfoByUserId(info.ID, map[string]interface{}{
"token": bToken,
})
buildTokenOver = true
}
}
info.Token = bToken
}
// global.UserToken.SetDefault(bToken, info)
cToken := uuid.NewString() + "-" + cmn.Md5(cmn.Md5("userId"+strconv.Itoa(int(info.ID))))
global.CUserToken.SetDefault(cToken, bToken)
global.Logger.Debug("token:", cToken, "|", bToken)
global.Logger.Debug(global.CUserToken.Get(cToken))
// 设置当前用户信息
c.Set("userInfo", info)
info.Token = cToken // 重要 采用cToken,隐藏真实token
apiReturn.SuccessData(c, info)
}
// 安全退出
func (l *LoginApi) Logout(c *gin.Context) {
// userInfo, _ := base.GetCurrentUserInfo(c)
cToken := c.GetHeader("token")
global.CUserToken.Delete(cToken)
apiReturn.Success(c)
}
// 获取重置密码的验证码
func (l *LoginApi) SendResetPasswordVCode(c *gin.Context) {
type ResstRequest struct {
LoginLoginVerify
Verification commonApiStructs.VerificationRequest `json:"verification"`
}
req := ResstRequest{}
if err := c.ShouldBindJSON(&req); err != nil {
apiReturn.Error(c, global.Lang.Get("common.api_error_param_format"))
return
}
// 验证码验证
{
errCode, verifcationId := base.VerificationCheck(req.Verification.CodeID, req.Verification.VCode)
if errCode != apiReturn.ERROR_CODE_SUCCESS {
apiReturn.ErrorVerification(c, errCode, verifcationId)
return
}
}
emailVCode := cmn.BuildRandCode(6, cmn.RAND_CODE_MODE2)
global.VerifyCodeCachePool.Set(req.Email, emailVCode, 10*time.Minute)
userCheck := &models.User{Mail: req.Email}
userInfo := userCheck.GetUserInfoByMail()
if userInfo == nil {
apiReturn.Error(c, "账号不存在")
return
}
emailInfoConfig := systemSetting.Email{}
global.SystemSetting.GetValueByInterface("system_email", &emailInfoConfig)
emailInfo := mail.EmailInfo{
Username: emailInfoConfig.Mail,
Password: emailInfoConfig.Password,
Host: emailInfoConfig.Host,
Port: emailInfoConfig.Port,
}
if err := mail.SendResetPasswordVCode(mail.NewEmailer(emailInfo), req.Email, emailVCode); err != nil {
apiReturn.Error(c, err.Error())
return
}
apiReturn.Success(c)
}
// 使用邮箱验证码重置密码
func (l *LoginApi) ResetPasswordByVCode(c *gin.Context) {
req := registerInfo{}
if err := c.ShouldBindJSON(&req); err != nil {
apiReturn.Error(c, global.Lang.Get("common.api_error_param_format"))
return
}
userCheck := &models.User{Mail: req.Email}
userInfo := userCheck.GetUserInfoByMail()
if userInfo == nil {
apiReturn.Error(c, "账号不存在")
return
}
// 校验验证码
{
if emailVCode, ok := global.VerifyCodeCachePool.Get(req.Email); !ok || req.EmailVCode != emailVCode {
apiReturn.Error(c, global.Lang.Get("common.captcha_code_error"))
return
}
global.VerifyCodeCachePool.Delete(req.Email)
}
updateData := map[string]interface{}{
"password": cmn.PasswordEncryption(req.Password),
"token": "",
}
global.UserToken.Delete(userInfo.Token) // 更新用户信息
if err := userInfo.UpdateUserInfoByUserId(userInfo.ID, updateData); err != nil {
apiReturn.ErrorDatabase(c, err.Error())
return
}
apiReturn.Success(c)
}
+28
View File
@@ -0,0 +1,28 @@
package system
import (
"sun-panel/api/api_v1/common/apiData/systemApiStructs"
"sun-panel/api/api_v1/common/apiReturn"
"sun-panel/global"
"sun-panel/models"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
)
type NoticeApi struct {
}
func (a *NoticeApi) GetListByDisplayType(c *gin.Context) {
req := systemApiStructs.NoticeGetListByDisplayTypeReq{}
if err := c.ShouldBindBodyWith(&req, binding.JSON); err != nil {
apiReturn.ErrorParamFomat(c, err.Error())
return
}
noticeList := []models.Notice{}
if err := global.Db.Find(&noticeList, "display_type in ?", req.DisplayType).Error; err != nil {
apiReturn.ErrorDatabase(c, err.Error())
return
}
apiReturn.SuccessListData(c, noticeList, 0)
}
@@ -0,0 +1,36 @@
package rateLimit
import (
"errors"
"sun-panel/global"
)
const (
ERROR_RATE_EXCEED_MINUTE = "minute exceed" // 分钟速率超出限制
ERROR_RATE_EXCEED_HOUR = "minute hour" // 小时速率超出限制
)
// 获取用户套餐的速率 此处正常根据用户套餐设定获取-暂时写死
func GetUserPackageRate(userId uint) (minuteRate, hourRate int) {
return 10, 200
}
func CheckRateLimit(userId uint) error {
minuteRate, hourRate := GetUserPackageRate(userId)
if minuteRate != 0 && minuteRate <= global.RateLimit.MinuteGet(userId) {
return errors.New(ERROR_RATE_EXCEED_MINUTE)
}
if hourRate != 0 && hourRate <= global.RateLimit.HourGet(userId) {
return errors.New(ERROR_RATE_EXCEED_HOUR)
}
return nil
}
// 速率+1次 同时增加小时和分钟的次数
func AddOnceRate(userId uint) error {
global.RateLimit.MinuteAddOnce(userId)
global.RateLimit.HourAddOnce(userId)
return nil
}
+183
View File
@@ -0,0 +1,183 @@
package system
import (
"fmt"
"math/rand"
"sun-panel/api/api_v1/common/apiData/commonApiStructs"
"sun-panel/api/api_v1/common/apiReturn"
"sun-panel/api/api_v1/common/base"
"sun-panel/global"
"sun-panel/lib/cmn"
"sun-panel/lib/cmn/systemSetting"
"sun-panel/lib/mail"
"sun-panel/models"
"time"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
type registerInfo struct {
Email string `json:"email"`
UserName string `json:"userName"`
Password string `json:"password"`
Vcode string `json:"vcode"`
EmailVCode string `json:"emailVCode"`
VCode string `json:"vCode"`
Verification commonApiStructs.VerificationRequest `json:"verification"`
ReferralCode string `json:"referralCode"`
}
const EmailCodeCapacity = 1000
type RegisterApi struct{}
// 获取注册验证码
func (l RegisterApi) SendRegisterVcode(c *gin.Context) {
req := registerInfo{}
err := c.ShouldBindJSON(&req)
req.Email = req.UserName
if err != nil {
apiReturn.ErrorParamFomat(c, err.Error())
return
}
errMsg, err := base.ValidateInputStruct(req)
if err != nil {
apiReturn.Error(c, errMsg)
return
}
// 验证码验证
{
errCode, verifcationId := base.VerificationCheck(req.Verification.CodeID, req.Verification.VCode)
if errCode != apiReturn.ERROR_CODE_SUCCESS {
apiReturn.ErrorVerification(c, errCode, verifcationId)
return
}
}
// 验证是否开启注册和后缀格式是否正确
{
systemSettingInfo := systemSetting.ApplicationSetting{}
if err := global.SystemSetting.GetValueByInterface("system_application", &systemSettingInfo); err != nil || !systemSettingInfo.Register.OpenRegister {
apiReturn.Error(c, global.Lang.Get("register.unopened_register"))
return
}
if systemSettingInfo.Register.EmailSuffix != "" && !cmn.VerifyFormat("^.*"+systemSettingInfo.Register.EmailSuffix+"$", req.Email) {
apiReturn.Error(c, global.Lang.GetWithFields("register.emailSuffix_error", map[string]string{"EmailSuffix": systemSettingInfo.Register.EmailSuffix}))
return
}
}
// 验证邮箱是否被注册
{
userCheck := &models.User{Mail: req.UserName}
if _, err := userCheck.GetUserInfoByUsername(req.UserName); err == nil && err != gorm.ErrRecordNotFound {
apiReturn.Error(c, global.Lang.Get("register.mail_exist"))
return
}
}
emailCode := generateEmailCode()
count, err := global.VerifyCodeCachePool.ItemCount()
if err != nil || count >= EmailCodeCapacity {
global.VerifyCodeCachePool.Flush()
}
global.VerifyCodeCachePool.Set(req.Email, emailCode, 0)
emailInfoConfig := systemSetting.Email{}
global.SystemSetting.GetValueByInterface("system_email", &emailInfoConfig)
emailInfo := mail.EmailInfo{
Username: emailInfoConfig.Mail,
Password: emailInfoConfig.Password,
Host: emailInfoConfig.Host,
Port: emailInfoConfig.Port,
}
err = mail.SendRegisterEmail(mail.NewEmailer(emailInfo), req.Email, emailCode)
if err != nil {
apiReturn.Error(c, global.Lang.Get("mail.send_mail_fail"))
global.Logger.Errorf("[register] fail to send email to%s", req.UserName)
return
}
apiReturn.Success(c)
}
// 注册提交(开始注册)
func (l *RegisterApi) Commit(c *gin.Context) {
req := registerInfo{}
err := c.ShouldBindJSON(&req)
req.Email = req.UserName
if err != nil {
apiReturn.ErrorParamFomat(c, err.Error())
return
}
errMsg, err := base.ValidateInputStruct(req)
if err != nil {
apiReturn.Error(c, errMsg)
return
}
// 验证是否开启注册和后缀格式是否正确
{
systemSettingInfo := systemSetting.ApplicationSetting{}
if err := global.SystemSetting.GetValueByInterface("system_application", &systemSettingInfo); err != nil || !systemSettingInfo.Register.OpenRegister {
apiReturn.Error(c, global.Lang.Get("register.unopened_register"))
return
}
if systemSettingInfo.Register.EmailSuffix != "" && !cmn.VerifyFormat("^.*"+systemSettingInfo.Register.EmailSuffix+"$", req.Email) {
apiReturn.Error(c, global.Lang.GetWithFields("register.emailSuffix_error", map[string]string{"EmailSuffix": systemSettingInfo.Register.EmailSuffix}))
return
}
}
// 验证邮箱是否被注册
{
userCheck := &models.User{Mail: req.UserName}
if _, err := userCheck.GetUserInfoByUsername(req.UserName); err == nil && err != gorm.ErrRecordNotFound {
apiReturn.Error(c, global.Lang.Get("register.mail_exist"))
return
}
}
// 验证码验证
{
vCode, ok := global.VerifyCodeCachePool.Get(req.Email)
if !ok {
apiReturn.Error(c, global.Lang.Get("common.captcha_code_error"))
//验证码不存在
return
}
if vCode != req.EmailVCode {
apiReturn.Error(c, global.Lang.Get("common.captcha_code_error"))
return
//验证码有误
}
}
// 自动生成用户昵称
name := "用户" + cmn.BuildRandCode(4, cmn.RAND_CODE_MODE3)
//验证通过,注册
user := &models.User{
Mail: req.UserName,
Name: name,
Username: req.UserName,
Password: cmn.PasswordEncryption(req.Password),
Status: 1,
Role: 2,
}
_, err = user.CreateOne()
if err != nil {
apiReturn.ErrorDatabase(c, err.Error())
return
}
//删除旧的验证码
global.VerifyCodeCachePool.Delete(req.Email)
apiReturn.Success(c)
}
func generateEmailCode() string {
return fmt.Sprintf("%06v", rand.New(rand.NewSource(time.Now().UnixNano())).Int31n(1000000))
}
+136
View File
@@ -0,0 +1,136 @@
package system
import (
"sun-panel/api/api_v1/common/apiData/systemApiStructs"
"sun-panel/api/api_v1/common/apiReturn"
"sun-panel/api/api_v1/common/base"
"sun-panel/global"
"sun-panel/lib/cmn"
"sun-panel/models"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
)
type UserApi struct{}
func (a *UserApi) GetInfo(c *gin.Context) {
userInfo, _ := base.GetCurrentUserInfo(c)
apiReturn.SuccessData(c, gin.H{
"userId": userInfo.ID,
"id": userInfo.ID,
"headImage": userInfo.HeadImage,
"name": userInfo.Name,
"role": userInfo.Role,
// "token": userInfo.Token,
})
}
// 修改资料
func (a *UserApi) UpdateInfo(c *gin.Context) {
userInfo, _ := base.GetCurrentUserInfo(c)
type UpdateUserInfoStruct struct {
HeadImage string `json:"headImage"`
Name string `json:"name" validate:"max=15,min=3,required"`
}
params := UpdateUserInfoStruct{}
err := c.ShouldBindBodyWith(&params, binding.JSON)
if err != nil {
apiReturn.ErrorParamFomat(c, err.Error())
return
}
if errMsg, err := base.ValidateInputStruct(&params); err != nil {
apiReturn.ErrorParamFomat(c, errMsg)
return
}
mUser := models.User{}
err = mUser.UpdateUserInfoByUserId(userInfo.ID, map[string]interface{}{
"head_image": params.HeadImage,
"name": params.Name,
})
// 删除缓存
global.UserToken.Delete(userInfo.Token)
if err != nil {
apiReturn.ErrorDatabase(c, err.Error())
}
apiReturn.Success(c)
}
// 修改密码
func (a *UserApi) UpdatePasssword(c *gin.Context) {
type UpdatePasssStruct struct {
OldPassword string `json:"oldPassword"`
NewPassword string `json:"newPassword"`
}
params := UpdatePasssStruct{}
err := c.ShouldBindBodyWith(&params, binding.JSON)
if err != nil {
apiReturn.ErrorParamFomat(c, err.Error())
return
}
userInfo, _ := base.GetCurrentUserInfo(c)
mUser := models.User{}
if v, err := mUser.GetUserInfoByUid(userInfo.ID); err != nil {
apiReturn.ErrorParamFomat(c, err.Error())
return
} else {
if v.Password != cmn.PasswordEncryption(params.OldPassword) {
// 旧密码不正确
apiReturn.Error(c, global.Lang.Get("user.api_old_pass_error"))
return
}
}
res := global.Db.Model(&models.User{}).Where("id", userInfo.ID).Updates(map[string]interface{}{
"password": cmn.PasswordEncryption(params.NewPassword),
"token": "",
})
if res.Error != nil {
apiReturn.ErrorDatabase(c, res.Error.Error())
return
}
// 删除token
global.UserToken.Delete(userInfo.Token)
apiReturn.Success(c)
}
// 获取推荐码
func (a *UserApi) GetReferralCode(c *gin.Context) {
currentUserInfo, _ := base.GetCurrentUserInfo(c)
mUser := models.User{}
userInfo, err := mUser.GetUserInfoByUid(currentUserInfo.ID)
if err != nil {
apiReturn.ErrorDatabase(c, err.Error())
return
}
// 为空生成一个
if userInfo.ReferralCode == "" {
for {
referralCode := cmn.BuildRandCode(8, cmn.RAND_CODE_MODE2)
global.Logger.Debug("referralCode:", referralCode)
// 查询是否有重复的
if row := global.Db.Find(&userInfo, "referral_code=?", referralCode).RowsAffected; row != 0 {
apiReturn.ErrorDatabase(c, err.Error())
continue
}
// 创建新的邀请码
if err := global.Db.Model(&models.User{}).Where("id=?", userInfo.ID).Update("referral_code", referralCode).Error; err != nil {
apiReturn.ErrorDatabase(c, err.Error())
return
} else {
userInfo.ReferralCode = referralCode
break
}
}
}
apiReturn.SuccessData(c, systemApiStructs.GetReferralCodeResp{ReferralCode: userInfo.ReferralCode})
}