From ecf56905054b4665d29f922143687679976957ec Mon Sep 17 00:00:00 2001 From: admin Date: Sun, 3 May 2026 06:07:11 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=94=A8=E6=88=B7=E5=88=9B=E5=BB=BA?= =?UTF-8?q?=E5=85=85=E5=80=BC=E4=BC=98=E5=8C=96=E3=80=81=E4=BE=A7=E8=BE=B9?= =?UTF-8?q?=E6=A0=8F=E5=AF=BC=E8=88=AA=E4=BF=AE=E5=A4=8D=E3=80=81=E5=9C=A8?= =?UTF-8?q?=E7=BA=BF=E5=AE=9E=E4=BE=8B=E7=BB=9F=E8=AE=A1=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/internal/router/admin/cards.go | 4 +- backend/internal/router/admin/users.go | 145 +++++++++++++++++- .../src/components/admin-sidebar/index.vue | 12 +- .../components/app-sidebar/team-switcher.vue | 4 +- frontend/src/pages/admin/sessions/index.vue | 9 +- frontend/src/pages/admin/users/create.vue | 143 +++++++++++++++-- frontend/src/plugins/i18n/en.json | 49 +++--- frontend/src/plugins/i18n/zh.json | 49 +++--- 8 files changed, 331 insertions(+), 84 deletions(-) diff --git a/backend/internal/router/admin/cards.go b/backend/internal/router/admin/cards.go index d316342..ad775d4 100644 --- a/backend/internal/router/admin/cards.go +++ b/backend/internal/router/admin/cards.go @@ -1,4 +1,4 @@ -package admin +package admin import ( "fmt" @@ -135,7 +135,9 @@ func handleGetCardTypes(c *gin.Context) { "user_id": ct.UserID, "application_id": ct.ApplicationID, "name": ct.Name, + "recharge_type": ct.RechargeType, "value": ct.Value, + "value_unit": ct.ValueUnit, "price": ct.Price, "description": ct.Description, "status": ct.Status, diff --git a/backend/internal/router/admin/users.go b/backend/internal/router/admin/users.go index 84ba39d..cbea848 100644 --- a/backend/internal/router/admin/users.go +++ b/backend/internal/router/admin/users.go @@ -1,4 +1,4 @@ -package admin +package admin import ( "fmt" @@ -8,6 +8,7 @@ import ( "verification-platform-backend/internal/model" "verification-platform-backend/internal/service" "verification-platform-backend/pkg/response" + "verification-platform-backend/pkg/utils" "github.com/gin-gonic/gin" ) @@ -233,6 +234,8 @@ func handleCreateUser(c *gin.Context) { Email string `json:"email"` Password string `json:"password"` ApplicationID uint `json:"application_id"` + CardTypeID *uint `json:"card_type_id"` + CardQuantity int `json:"card_quantity"` } if err := c.ShouldBindJSON(&req); err != nil { response.Error(c, 400, "参数错误") @@ -254,6 +257,14 @@ func handleCreateUser(c *gin.Context) { return } + if req.CardQuantity < 1 { + req.CardQuantity = 1 + } + if req.CardQuantity > 100 { + response.Error(c, 400, "卡密数量不能超过100") + return + } + var app model.Application if err := database.DB.First(&app, req.ApplicationID).Error; err != nil { response.Error(c, 404, "应用不存在") @@ -274,6 +285,18 @@ func handleCreateUser(c *gin.Context) { return } + var cardType *model.CardType + if req.CardTypeID != nil && *req.CardTypeID > 0 { + var ct model.CardType + if err := database.DB.Where("id = ? AND application_id = ?", *req.CardTypeID, req.ApplicationID).First(&ct).Error; err != nil { + response.Error(c, 400, "卡密类型不存在或不属于该应用") + return + } + cardType = &ct + } + + tx := database.DB.Begin() + user := model.AppUser{ Username: req.Username, Email: req.Email, @@ -283,14 +306,128 @@ func handleCreateUser(c *gin.Context) { ApplicationID: app.ID, } - if err := database.DB.Create(&user).Error; err != nil { + if err := tx.Create(&user).Error; err != nil { + tx.Rollback() response.Error(c, 500, "创建用户失败") return } - service.LogOperation(c, "create", "app_user", &user.ID, fmt.Sprintf("创建用户: %s (应用: %s)", user.Username, app.Name), nil) + var cards []model.Card + if cardType != nil { + now := time.Now() - response.Success(c, user) + for i := 0; i < req.CardQuantity; i++ { + cardKey := "CK" + utils.GenerateRandomString(16) + card := model.Card{ + ApplicationID: req.ApplicationID, + CardTypeID: cardType.ID, + CardKey: cardKey, + CreatorID: userID, + AppUserID: &user.ID, + Status: "used", + } + card.UsedAt = &now + + if err := tx.Create(&card).Error; err != nil { + tx.Rollback() + response.Error(c, 500, "生成卡密失败") + return + } + + user.IsTrialUser = false + + if cardType.Value == -1 { + if cardType.RechargeType == "subscription" { + permanentExpiry := time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC) + user.ExpiryAt = &permanentExpiry + user.Balance = -1 + } else { + user.Balance = -1 + user.ExpiryAt = nil + } + } else { + switch cardType.RechargeType { + case "subscription": + var baseTime time.Time + if user.ExpiryAt != nil && user.ExpiryAt.After(now) { + baseTime = *user.ExpiryAt + } else { + baseTime = now + } + var duration time.Duration + switch cardType.ValueUnit { + case "minute": + duration = time.Duration(cardType.Value) * time.Minute + case "hour": + duration = time.Duration(cardType.Value) * time.Hour + case "day": + duration = time.Duration(cardType.Value) * 24 * time.Hour + case "month": + duration = time.Duration(cardType.Value) * 30 * 24 * time.Hour + case "year": + duration = time.Duration(cardType.Value) * 365 * 24 * time.Hour + default: + duration = time.Duration(cardType.Value) * time.Second + } + newExpiry := baseTime.Add(duration) + user.ExpiryAt = &newExpiry + case "balance": + fallthrough + default: + user.Balance += cardType.Value + } + } + + rechargeRecord := model.RechargeRecord{ + UserID: user.ID, + OrderNo: generateUserOrderNo("R"), + CardID: &card.ID, + CardCode: card.CardKey, + Amount: cardType.Price, + Status: "success", + PaymentType: "card", + Remark: "创建用户充值 - " + cardType.Name, + } + + if err := tx.Create(&rechargeRecord).Error; err != nil { + tx.Rollback() + response.Error(c, 500, "创建充值记录失败") + return + } + + cards = append(cards, card) + } + + if err := tx.Save(&user).Error; err != nil { + tx.Rollback() + response.Error(c, 500, "充值失败") + return + } + } + + if err := tx.Commit().Error; err != nil { + response.Error(c, 500, "创建用户失败") + return + } + + logDesc := fmt.Sprintf("创建用户: %s (应用: %s)", user.Username, app.Name) + if cardType != nil { + logDesc += fmt.Sprintf(",充值卡密: %s x%d", cardType.Name, req.CardQuantity) + } + service.LogOperation(c, "create", "app_user", &user.ID, logDesc, nil) + + result := gin.H{ + "user": user, + } + if len(cards) > 0 { + result["cards"] = cards + } + + response.Success(c, result) +} + +func generateUserOrderNo(prefix string) string { + return prefix + time.Now().Format("20060102150405") + utils.GenerateRandomString(6) } func handleGetUser(c *gin.Context) { diff --git a/frontend/src/components/admin-sidebar/index.vue b/frontend/src/components/admin-sidebar/index.vue index 61c98ff..10e6a9b 100644 --- a/frontend/src/components/admin-sidebar/index.vue +++ b/frontend/src/components/admin-sidebar/index.vue @@ -1,5 +1,5 @@