feat(marketing): user profile update, ad placement management, logo limits
- Add PUT /auth/profile endpoint for nickname and avatar updates - Add ad_placements table and CRUD admin API for managing ad units - Add GET /marketing/ad-config public API for mini-program to fetch ad config - Reduce logo limit from 10 to 3 per user, add 2MB file size validation Made-with: Cursor
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"wx_service/internal/marketing/model"
|
||||
"wx_service/internal/marketing/repository"
|
||||
commonmodel "wx_service/internal/model"
|
||||
)
|
||||
|
||||
type AdPlacementHandler struct {
|
||||
repo *repository.AdPlacementRepository
|
||||
}
|
||||
|
||||
func NewAdPlacementHandler(repo *repository.AdPlacementRepository) *AdPlacementHandler {
|
||||
return &AdPlacementHandler{repo: repo}
|
||||
}
|
||||
|
||||
func (h *AdPlacementHandler) AdminList(c *gin.Context) {
|
||||
list, err := h.repo.ListAll()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, commonmodel.Error(http.StatusInternalServerError, "获取广告位列表失败"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, commonmodel.Success(list))
|
||||
}
|
||||
|
||||
type adPlacementCreateReq struct {
|
||||
MiniProgramID uint `json:"mini_program_id" binding:"required"`
|
||||
Name string `json:"name" binding:"required"`
|
||||
AdType string `json:"ad_type" binding:"required"`
|
||||
AdUnitID string `json:"ad_unit_id"`
|
||||
Status *int `json:"status"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
func (h *AdPlacementHandler) AdminCreate(c *gin.Context) {
|
||||
var req adPlacementCreateReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, commonmodel.Error(http.StatusBadRequest, "参数错误"))
|
||||
return
|
||||
}
|
||||
|
||||
p := &model.AdPlacement{
|
||||
MiniProgramID: req.MiniProgramID,
|
||||
Name: req.Name,
|
||||
AdType: req.AdType,
|
||||
AdUnitID: req.AdUnitID,
|
||||
Description: req.Description,
|
||||
Status: 1,
|
||||
}
|
||||
if req.Status != nil {
|
||||
p.Status = *req.Status
|
||||
}
|
||||
|
||||
if err := h.repo.Create(p); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, commonmodel.Error(http.StatusInternalServerError, "创建失败"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, commonmodel.Success(p))
|
||||
}
|
||||
|
||||
type adPlacementUpdateReq struct {
|
||||
Name *string `json:"name"`
|
||||
AdUnitID *string `json:"ad_unit_id"`
|
||||
Status *int `json:"status"`
|
||||
Description *string `json:"description"`
|
||||
}
|
||||
|
||||
func (h *AdPlacementHandler) AdminUpdate(c *gin.Context) {
|
||||
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if err != nil || id == 0 {
|
||||
c.JSON(http.StatusBadRequest, commonmodel.Error(http.StatusBadRequest, "无效 ID"))
|
||||
return
|
||||
}
|
||||
|
||||
p, err := h.repo.FindByID(uint(id))
|
||||
if err != nil {
|
||||
if errors.Is(err, repository.ErrAdPlacementNotFound) {
|
||||
c.JSON(http.StatusNotFound, commonmodel.Error(http.StatusNotFound, "广告位不存在"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, commonmodel.Error(http.StatusInternalServerError, "查询失败"))
|
||||
return
|
||||
}
|
||||
|
||||
var req adPlacementUpdateReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, commonmodel.Error(http.StatusBadRequest, "参数错误"))
|
||||
return
|
||||
}
|
||||
|
||||
if req.Name != nil {
|
||||
p.Name = *req.Name
|
||||
}
|
||||
if req.AdUnitID != nil {
|
||||
p.AdUnitID = *req.AdUnitID
|
||||
}
|
||||
if req.Status != nil {
|
||||
p.Status = *req.Status
|
||||
}
|
||||
if req.Description != nil {
|
||||
p.Description = *req.Description
|
||||
}
|
||||
|
||||
if err := h.repo.Update(p); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, commonmodel.Error(http.StatusInternalServerError, "更新失败"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, commonmodel.Success(p))
|
||||
}
|
||||
|
||||
func (h *AdPlacementHandler) AdminDelete(c *gin.Context) {
|
||||
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if err != nil || id == 0 {
|
||||
c.JSON(http.StatusBadRequest, commonmodel.Error(http.StatusBadRequest, "无效 ID"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.repo.Delete(uint(id)); err != nil {
|
||||
if errors.Is(err, repository.ErrAdPlacementNotFound) {
|
||||
c.JSON(http.StatusNotFound, commonmodel.Error(http.StatusNotFound, "广告位不存在"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, commonmodel.Error(http.StatusInternalServerError, "删除失败"))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, commonmodel.Success(nil))
|
||||
}
|
||||
|
||||
func (h *AdPlacementHandler) GetAdConfig(c *gin.Context) {
|
||||
miniProgramIDStr := c.Query("mini_program_id")
|
||||
miniProgramID, _ := strconv.ParseUint(miniProgramIDStr, 10, 64)
|
||||
if miniProgramID == 0 {
|
||||
c.JSON(http.StatusBadRequest, commonmodel.Error(http.StatusBadRequest, "缺少 mini_program_id"))
|
||||
return
|
||||
}
|
||||
|
||||
p, err := h.repo.FindByMiniProgramAndType(uint(miniProgramID), "rewarded_video")
|
||||
if err != nil {
|
||||
if errors.Is(err, repository.ErrAdPlacementNotFound) {
|
||||
c.JSON(http.StatusOK, commonmodel.Success(gin.H{"ad_unit_id": "", "enabled": false}))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, commonmodel.Error(http.StatusInternalServerError, "查询失败"))
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, commonmodel.Success(gin.H{
|
||||
"ad_unit_id": p.AdUnitID,
|
||||
"enabled": p.Status == 1 && p.AdUnitID != "",
|
||||
}))
|
||||
}
|
||||
@@ -42,7 +42,7 @@ func (h *UserLogoHandler) Save(c *gin.Context) {
|
||||
|
||||
logo, err := h.svc.Save(user.ID, req)
|
||||
if err != nil {
|
||||
if errors.Is(err, service.ErrLogoLimitReached) {
|
||||
if errors.Is(err, service.ErrLogoLimitReached) || errors.Is(err, service.ErrLogoTooLarge) {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user