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 @@