package admin import ( "net/http" "strconv" "bindbox-game/internal/code" "bindbox-game/internal/pkg/core" "bindbox-game/internal/pkg/validation" "bindbox-game/internal/repository/mysql/model" ) // ========== 黑名单管理 ========== type addBlacklistRequest struct { DouyinUserID string `json:"douyin_user_id" binding:"required"` Reason string `json:"reason"` } type blacklistResponse struct { ID int64 `json:"id"` DouyinUserID string `json:"douyin_user_id"` Reason string `json:"reason"` OperatorID int64 `json:"operator_id"` Status int32 `json:"status"` CreatedAt string `json:"created_at"` } type listBlacklistRequest struct { Page int `form:"page"` PageSize int `form:"page_size"` Keyword string `form:"keyword"` } type listBlacklistResponse struct { List []blacklistResponse `json:"list"` Total int64 `json:"total"` Page int `json:"page"` PageSize int `json:"page_size"` } // ListBlacklist 获取黑名单列表 // @Summary 获取黑名单列表 // @Description 获取抖音用户黑名单列表,支持分页和关键词搜索 // @Tags 管理端.黑名单 // @Accept json // @Produce json // @Param page query int false "页码" default(1) // @Param page_size query int false "每页数量" default(20) // @Param keyword query string false "搜索关键词(抖音ID)" // @Success 200 {object} listBlacklistResponse // @Failure 400 {object} code.Failure // @Router /api/admin/blacklist [get] // @Security LoginVerifyToken func (h *handler) ListBlacklist() core.HandlerFunc { return func(ctx core.Context) { req := new(listBlacklistRequest) if err := ctx.ShouldBindQuery(req); err != nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err))) return } if req.Page <= 0 { req.Page = 1 } if req.PageSize <= 0 { req.PageSize = 20 } db := h.repo.GetDbR().WithContext(ctx.RequestContext()). Table("douyin_blacklist"). Where("status = 1") if req.Keyword != "" { db = db.Where("douyin_user_id LIKE ?", "%"+req.Keyword+"%") } var total int64 if err := db.Count(&total).Error; err != nil { ctx.AbortWithError(core.Error(http.StatusInternalServerError, code.ServerError, err.Error())) return } var list []model.DouyinBlacklist if err := db.Order("id DESC"). Offset((req.Page - 1) * req.PageSize). Limit(req.PageSize). Find(&list).Error; err != nil { ctx.AbortWithError(core.Error(http.StatusInternalServerError, code.ServerError, err.Error())) return } rsp := &listBlacklistResponse{ List: make([]blacklistResponse, len(list)), Total: total, Page: req.Page, PageSize: req.PageSize, } for i, item := range list { rsp.List[i] = blacklistResponse{ ID: item.ID, DouyinUserID: item.DouyinUserID, Reason: item.Reason, OperatorID: item.OperatorID, Status: item.Status, CreatedAt: item.CreatedAt.Format("2006-01-02 15:04:05"), } } ctx.Payload(rsp) } } // AddBlacklist 添加黑名单 // @Summary 添加黑名单 // @Description 将抖音用户添加到黑名单 // @Tags 管理端.黑名单 // @Accept json // @Produce json // @Param body body addBlacklistRequest true "请求参数" // @Success 200 {object} blacklistResponse // @Failure 400 {object} code.Failure // @Router /api/admin/blacklist [post] // @Security LoginVerifyToken func (h *handler) AddBlacklist() core.HandlerFunc { return func(ctx core.Context) { req := new(addBlacklistRequest) if err := ctx.ShouldBindJSON(req); err != nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err))) return } // 检查是否已在黑名单 var existCount int64 h.repo.GetDbR().WithContext(ctx.RequestContext()). Table("douyin_blacklist"). Where("douyin_user_id = ? AND status = 1", req.DouyinUserID). Count(&existCount) if existCount > 0 { ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "该用户已在黑名单中")) return } operatorID := int64(0) if ctx.SessionUserInfo().Id > 0 { operatorID = int64(ctx.SessionUserInfo().Id) } blacklist := &model.DouyinBlacklist{ DouyinUserID: req.DouyinUserID, Reason: req.Reason, OperatorID: operatorID, Status: 1, } if err := h.repo.GetDbW().WithContext(ctx.RequestContext()).Create(blacklist).Error; err != nil { ctx.AbortWithError(core.Error(http.StatusInternalServerError, code.ServerError, err.Error())) return } ctx.Payload(&blacklistResponse{ ID: blacklist.ID, DouyinUserID: blacklist.DouyinUserID, Reason: blacklist.Reason, OperatorID: blacklist.OperatorID, Status: blacklist.Status, CreatedAt: blacklist.CreatedAt.Format("2006-01-02 15:04:05"), }) } } // RemoveBlacklist 移除黑名单 // @Summary 移除黑名单 // @Description 将用户从黑名单中移除(软删除,status设为0) // @Tags 管理端.黑名单 // @Accept json // @Produce json // @Param id path integer true "黑名单ID" // @Success 200 {object} simpleMessageResponse // @Failure 400 {object} code.Failure // @Router /api/admin/blacklist/{id} [delete] // @Security LoginVerifyToken func (h *handler) RemoveBlacklist() core.HandlerFunc { return func(ctx core.Context) { idStr := ctx.Param("id") id, err := strconv.ParseInt(idStr, 10, 64) if err != nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "无效的ID")) return } result := h.repo.GetDbW().WithContext(ctx.RequestContext()). Table("douyin_blacklist"). Where("id = ?", id). Update("status", 0) if result.Error != nil { ctx.AbortWithError(core.Error(http.StatusInternalServerError, code.ServerError, result.Error.Error())) return } if result.RowsAffected == 0 { ctx.AbortWithError(core.Error(http.StatusNotFound, code.ParamBindError, "黑名单记录不存在")) return } ctx.Payload(&simpleMessageResponse{Message: "移除成功"}) } } // CheckBlacklist 检查用户是否在黑名单 // @Summary 检查黑名单状态 // @Description 检查指定抖音用户是否在黑名单中 // @Tags 管理端.黑名单 // @Accept json // @Produce json // @Param douyin_user_id query string true "抖音用户ID" // @Success 200 {object} map[string]bool // @Failure 400 {object} code.Failure // @Router /api/admin/blacklist/check [get] // @Security LoginVerifyToken func (h *handler) CheckBlacklist() core.HandlerFunc { return func(ctx core.Context) { douyinUserID := ctx.RequestInputParams().Get("douyin_user_id") if douyinUserID == "" { ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "抖音用户ID不能为空")) return } var count int64 h.repo.GetDbR().WithContext(ctx.RequestContext()). Table("douyin_blacklist"). Where("douyin_user_id = ? AND status = 1", douyinUserID). Count(&count) ctx.Payload(map[string]any{ "douyin_user_id": douyinUserID, "is_blacklisted": count > 0, }) } } // BatchAddBlacklist 批量添加黑名单 // @Summary 批量添加黑名单 // @Description 批量将抖音用户添加到黑名单 // @Tags 管理端.黑名单 // @Accept json // @Produce json // @Param body body batchAddBlacklistRequest true "请求参数" // @Success 200 {object} batchAddBlacklistResponse // @Failure 400 {object} code.Failure // @Router /api/admin/blacklist/batch [post] // @Security LoginVerifyToken func (h *handler) BatchAddBlacklist() core.HandlerFunc { return func(ctx core.Context) { var req struct { DouyinUserIDs []string `json:"douyin_user_ids" binding:"required"` Reason string `json:"reason"` } if err := ctx.ShouldBindJSON(&req); err != nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err))) return } if len(req.DouyinUserIDs) == 0 { ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "抖音用户ID列表不能为空")) return } // 获取操作人ID operatorID := int64(0) if ctx.SessionUserInfo().Id > 0 { operatorID = int64(ctx.SessionUserInfo().Id) } // 查询已存在的黑名单 var existingIDs []string h.repo.GetDbR().WithContext(ctx.RequestContext()). Table("douyin_blacklist"). Where("douyin_user_id IN ? AND status = 1", req.DouyinUserIDs). Pluck("douyin_user_id", &existingIDs) existMap := make(map[string]bool) for _, id := range existingIDs { existMap[id] = true } // 过滤出需要新增的 var toAdd []model.DouyinBlacklist for _, uid := range req.DouyinUserIDs { if !existMap[uid] { toAdd = append(toAdd, model.DouyinBlacklist{ DouyinUserID: uid, Reason: req.Reason, OperatorID: operatorID, Status: 1, }) } } addedCount := 0 if len(toAdd) > 0 { if err := h.repo.GetDbW().WithContext(ctx.RequestContext()).Create(&toAdd).Error; err != nil { ctx.AbortWithError(core.Error(http.StatusInternalServerError, code.ServerError, err.Error())) return } addedCount = len(toAdd) } ctx.Payload(map[string]any{ "total_requested": len(req.DouyinUserIDs), "added": addedCount, "skipped": len(req.DouyinUserIDs) - addedCount, }) } }