diff --git a/controller/token.go b/controller/token.go index c57552c0..173fc22e 100644 --- a/controller/token.go +++ b/controller/token.go @@ -258,3 +258,32 @@ func UpdateToken(c *gin.Context) { }) return } + +type TokenBatch struct { + Ids []int `json:"ids"` +} + +func DeleteTokenBatch(c *gin.Context) { + tokenBatch := TokenBatch{} + if err := c.ShouldBindJSON(&tokenBatch); err != nil || len(tokenBatch.Ids) == 0 { + c.JSON(http.StatusOK, gin.H{ + "success": false, + "message": "参数错误", + }) + return + } + userId := c.GetInt("id") + count, err := model.BatchDeleteTokens(tokenBatch.Ids, userId) + if err != nil { + c.JSON(http.StatusOK, gin.H{ + "success": false, + "message": err.Error(), + }) + return + } + c.JSON(http.StatusOK, gin.H{ + "success": true, + "message": "", + "data": count, + }) +} diff --git a/model/token.go b/model/token.go index 2ed2c09a..7e68f185 100644 --- a/model/token.go +++ b/model/token.go @@ -327,3 +327,37 @@ func CountUserTokens(userId int) (int64, error) { err := DB.Model(&Token{}).Where("user_id = ?", userId).Count(&total).Error return total, err } + +// BatchDeleteTokens 删除指定用户的一组令牌,返回成功删除数量 +func BatchDeleteTokens(ids []int, userId int) (int, error) { + if len(ids) == 0 { + return 0, errors.New("ids 不能为空!") + } + + tx := DB.Begin() + + var tokens []Token + if err := tx.Where("user_id = ? AND id IN (?)", userId, ids).Find(&tokens).Error; err != nil { + tx.Rollback() + return 0, err + } + + if err := tx.Where("user_id = ? AND id IN (?)", userId, ids).Delete(&Token{}).Error; err != nil { + tx.Rollback() + return 0, err + } + + if err := tx.Commit().Error; err != nil { + return 0, err + } + + if common.RedisEnabled { + gopool.Go(func() { + for _, t := range tokens { + _ = cacheDeleteToken(t.Key) + } + }) + } + + return len(tokens), nil +} diff --git a/router/api-router.go b/router/api-router.go index badfa7bf..db4c3898 100644 --- a/router/api-router.go +++ b/router/api-router.go @@ -125,6 +125,7 @@ func SetApiRouter(router *gin.Engine) { tokenRoute.POST("/", controller.AddToken) tokenRoute.PUT("/", controller.UpdateToken) tokenRoute.DELETE("/:id", controller.DeleteToken) + tokenRoute.POST("/batch", controller.DeleteTokenBatch) } redemptionRoute := apiRouter.Group("/redemption") redemptionRoute.Use(middleware.AdminAuth()) diff --git a/web/src/components/table/TokensTable.js b/web/src/components/table/TokensTable.js index bc6c7607..d49a344d 100644 --- a/web/src/components/table/TokensTable.js +++ b/web/src/components/table/TokensTable.js @@ -435,6 +435,7 @@ const TokensTable = () => { const refresh = async () => { await loadTokens(1); + setSelectedKeys([]); }; const copyText = async (text) => { @@ -583,6 +584,29 @@ const TokensTable = () => { } }; + const batchDeleteTokens = async () => { + if (selectedKeys.length === 0) { + showError(t('请先选择要删除的令牌!')); + return; + } + setLoading(true); + try { + const ids = selectedKeys.map((token) => token.id); + const res = await API.post('/api/token/batch', { ids }); + if (res?.data?.success) { + const count = res.data.data || 0; + showSuccess(t('已删除 {{count}} 个令牌!', { count })); + await refresh(); + } else { + showError(res?.data?.message || t('删除失败')); + } + } catch (error) { + showError(error.message); + } finally { + setLoading(false); + } + }; + const renderHeader = () => (