package handler import ( "errors" "net/http" "strconv" "github.com/freefire/jiang13-bbs/middleware" "github.com/freefire/jiang13-bbs/service" "github.com/gin-gonic/gin" ) // CaptchaIssue GET /api/captcha func (h *Handlers) CaptchaIssue(c *gin.Context) { if h.Ads == nil || h.Ads.Captcha() == nil { c.JSON(http.StatusServiceUnavailable, gin.H{"error": "验证码服务不可用"}) return } id, img, err := h.Ads.Captcha().Issue() if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "生成验证码失败"}) return } c.JSON(http.StatusOK, gin.H{"id": id, "image": img}) } // AdsPublicConfig GET /api/ads/config func (h *Handlers) AdsPublicConfig(c *gin.Context) { cfg, err := h.Ads.PublicConfig() if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "读取配置失败"}) return } c.JSON(http.StatusOK, cfg) } type adSubmitReq struct { Kind string `json:"kind"` ContactEmail string `json:"contact_email"` LinkURL string `json:"link_url"` ImageURL string `json:"image_url"` Title string `json:"title"` TextColor string `json:"text_color"` BgColor string `json:"bg_color"` DurationDays int `json:"duration_days"` PaymentID string `json:"payment_id"` BuyerNote string `json:"buyer_note"` CaptchaID string `json:"captcha_id"` CaptchaCode string `json:"captcha_code"` } // AdsSubmit POST /api/ads func (h *Handlers) AdsSubmit(c *gin.Context) { var req adSubmitReq if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "请求格式错误"}) return } ad, err := h.Ads.Submit(service.AdSubmitInput{ Kind: req.Kind, ContactEmail: req.ContactEmail, LinkURL: req.LinkURL, ImageURL: req.ImageURL, Title: req.Title, TextColor: req.TextColor, BgColor: req.BgColor, DurationDays: req.DurationDays, PaymentID: req.PaymentID, BuyerNote: req.BuyerNote, CaptchaID: req.CaptchaID, CaptchaCode: req.CaptchaCode, IP: c.ClientIP(), }) if err != nil { status := http.StatusBadRequest switch { case errors.Is(err, service.ErrAdCaptcha): status = http.StatusForbidden case errors.Is(err, service.ErrAdDisabled): status = http.StatusServiceUnavailable } c.JSON(status, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"ad": ad, "message": "提交成功,请完成付款并等待审核"}) } // AdminGetAdsConfig GET /api/admin/ads/config func (h *Handlers) AdminGetAdsConfig(c *gin.Context) { cfg, err := h.Ads.GetConfig() if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "读取失败"}) return } c.JSON(http.StatusOK, cfg) } // AdminSaveAdsConfig PUT /api/admin/ads/config func (h *Handlers) AdminSaveAdsConfig(c *gin.Context) { var req service.AdConfig if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "请求格式错误"}) return } cfg, err := h.Ads.SaveConfig(req) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, cfg) } // AdminListAds GET /api/admin/ads func (h *Handlers) AdminListAds(c *gin.Context) { page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) size, _ := strconv.Atoi(c.DefaultQuery("size", "20")) list, total, err := h.Ads.AdminList(c.Query("status"), page, size) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "加载失败"}) return } c.JSON(http.StatusOK, gin.H{"ads": list, "total": total, "page": page, "size": size}) } // AdminApproveAd PUT /api/admin/ads/:id/approve func (h *Handlers) AdminApproveAd(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "无效 ID"}) return } actor := middleware.CurrentActor(c) ad, err := h.Ads.Approve(actor.ID, uint(id)) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"ad": ad}) } type adRejectReq struct { Reason string `json:"reason"` } // AdminRejectAd PUT /api/admin/ads/:id/reject func (h *Handlers) AdminRejectAd(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "无效 ID"}) return } var req adRejectReq _ = c.ShouldBindJSON(&req) actor := middleware.CurrentActor(c) ad, err := h.Ads.Reject(actor.ID, uint(id), req.Reason) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"ad": ad}) } // AdminDeleteAd DELETE /api/admin/ads/:id func (h *Handlers) AdminDeleteAd(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "无效 ID"}) return } if err := h.Ads.Delete(uint(id)); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"ok": true}) } type adSortReq struct { SortOrder int `json:"sort_order"` } // AdminSortAd PUT /api/admin/ads/:id/sort func (h *Handlers) AdminSortAd(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "无效 ID"}) return } var req adSortReq if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "请求格式错误"}) return } if err := h.Ads.SetSort(uint(id), req.SortOrder); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"ok": true}) } // AdminUploadAdQR POST /api/admin/upload/ad-qr — 收款码上传到 /uploads/ads/ func (h *Handlers) AdminUploadAdQR(c *gin.Context) { c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, service.AdAssetMaxBytes+4096) fh, err := c.FormFile("file") if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "请选择图片文件"}) return } f, err := fh.Open() if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "读取图片失败"}) return } defer f.Close() url, err := h.Upload.SaveAdAsset(f) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"url": url}) } // AdminGetSponsorsConfig GET /api/admin/sponsors/config func (h *Handlers) AdminGetSponsorsConfig(c *gin.Context) { if h.Ads == nil { c.JSON(http.StatusServiceUnavailable, gin.H{"error": "服务不可用"}) return } cfg, err := h.Ads.GetSponsorsConfig() if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, cfg) } // AdminSaveSponsorsConfig PUT /api/admin/sponsors/config func (h *Handlers) AdminSaveSponsorsConfig(c *gin.Context) { if h.Ads == nil { c.JSON(http.StatusServiceUnavailable, gin.H{"error": "服务不可用"}) return } var req service.SponsorsConfig if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "请求格式错误"}) return } cfg, err := h.Ads.SaveSponsorsConfig(req) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, cfg) }