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:
@@ -83,6 +83,7 @@ func main() {
|
|||||||
&marketingmodel.MarketingTemplate{},
|
&marketingmodel.MarketingTemplate{},
|
||||||
&marketingmodel.MarketingDownload{},
|
&marketingmodel.MarketingDownload{},
|
||||||
&marketingmodel.UserLogo{},
|
&marketingmodel.UserLogo{},
|
||||||
|
&marketingmodel.AdPlacement{},
|
||||||
&quitcheckinmodel.Profile{},
|
&quitcheckinmodel.Profile{},
|
||||||
&quitcheckinmodel.DailyStatus{},
|
&quitcheckinmodel.DailyStatus{},
|
||||||
&quitcheckinmodel.RelapseEvent{},
|
&quitcheckinmodel.RelapseEvent{},
|
||||||
@@ -166,6 +167,8 @@ func main() {
|
|||||||
marketingTemplateHandler := marketinghandler.NewTemplateHandler(templateSvc)
|
marketingTemplateHandler := marketinghandler.NewTemplateHandler(templateSvc)
|
||||||
marketingDownloadHandler := marketinghandler.NewDownloadHandler(downloadSvc)
|
marketingDownloadHandler := marketinghandler.NewDownloadHandler(downloadSvc)
|
||||||
marketingUserLogoHandler := marketinghandler.NewUserLogoHandler(userLogoSvc)
|
marketingUserLogoHandler := marketinghandler.NewUserLogoHandler(userLogoSvc)
|
||||||
|
adPlacementRepo := marketingrepo.NewAdPlacementRepository(database.DB)
|
||||||
|
marketingAdPlacementHandler := marketinghandler.NewAdPlacementHandler(adPlacementRepo)
|
||||||
|
|
||||||
adminService := adminmodule.NewService(
|
adminService := adminmodule.NewService(
|
||||||
database.DB,
|
database.DB,
|
||||||
@@ -199,6 +202,7 @@ func main() {
|
|||||||
marketingTemplateHandler,
|
marketingTemplateHandler,
|
||||||
marketingDownloadHandler,
|
marketingDownloadHandler,
|
||||||
marketingUserLogoHandler,
|
marketingUserLogoHandler,
|
||||||
|
marketingAdPlacementHandler,
|
||||||
quitCheckinHandler,
|
quitCheckinHandler,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"wx_service/internal/common/auth/service"
|
"wx_service/internal/common/auth/service"
|
||||||
|
"wx_service/internal/middleware"
|
||||||
"wx_service/internal/model"
|
"wx_service/internal/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -148,3 +149,35 @@ func (h *AuthHandler) DevLogin(c *gin.Context) {
|
|||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type updateProfileRequest struct {
|
||||||
|
Nickname string `json:"nickname"`
|
||||||
|
AvatarURL string `json:"avatar_url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *AuthHandler) UpdateProfile(c *gin.Context) {
|
||||||
|
user := middleware.MustCurrentUser(c)
|
||||||
|
|
||||||
|
var req updateProfileRequest
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "参数错误"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if req.Nickname == "" && req.AvatarURL == "" {
|
||||||
|
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "请提供昵称或头像"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
updated, err := h.authService.UpdateProfile(c.Request.Context(), user.ID, req.Nickname, req.AvatarURL)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[update_profile] error: %v", err)
|
||||||
|
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "更新失败"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, model.Success(gin.H{
|
||||||
|
"id": updated.ID,
|
||||||
|
"nickname": updated.NickName,
|
||||||
|
"avatar_url": updated.AvatarURL,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|||||||
@@ -192,6 +192,31 @@ func (s *AuthService) DevLogin(ctx context.Context, miniProgramID uint) (*LoginR
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateProfile 更新用户昵称和头像。
|
||||||
|
func (s *AuthService) UpdateProfile(ctx context.Context, userID uint, nickname, avatarURL string) (*model.User, error) {
|
||||||
|
tx := s.db.WithContext(ctx)
|
||||||
|
var user model.User
|
||||||
|
if err := tx.First(&user, userID).Error; err != nil {
|
||||||
|
return nil, fmt.Errorf("find user: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
updates := map[string]interface{}{}
|
||||||
|
if nickname != "" {
|
||||||
|
updates["nick_name"] = nickname
|
||||||
|
user.NickName = nickname
|
||||||
|
}
|
||||||
|
if avatarURL != "" {
|
||||||
|
updates["avatar_url"] = avatarURL
|
||||||
|
user.AvatarURL = avatarURL
|
||||||
|
}
|
||||||
|
if len(updates) > 0 {
|
||||||
|
if err := tx.Model(&user).Updates(updates).Error; err != nil {
|
||||||
|
return nil, fmt.Errorf("update profile: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &user, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *AuthService) getSmokeMode(ctx context.Context, uid int) (string, error) {
|
func (s *AuthService) getSmokeMode(ctx context.Context, uid int) (string, error) {
|
||||||
var profile smokemodel.SmokeUserProfile
|
var profile smokemodel.SmokeUserProfile
|
||||||
err := s.db.WithContext(ctx).
|
err := s.db.WithContext(ctx).
|
||||||
|
|||||||
@@ -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)
|
logo, err := h.svc.Save(user.ID, req)
|
||||||
if err != nil {
|
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()))
|
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type AdPlacement struct {
|
||||||
|
ID uint `json:"id" gorm:"primaryKey;comment:主键ID"`
|
||||||
|
MiniProgramID uint `json:"mini_program_id" gorm:"not null;index;comment:小程序ID"`
|
||||||
|
Name string `json:"name" gorm:"size:100;not null;comment:广告位名称"`
|
||||||
|
AdType string `json:"ad_type" gorm:"size:50;not null;default:rewarded_video;comment:广告类型(rewarded_video/banner/interstitial)"`
|
||||||
|
AdUnitID string `json:"ad_unit_id" gorm:"size:200;comment:微信广告单元ID"`
|
||||||
|
Status int `json:"status" gorm:"default:1;comment:状态(0禁用/1启用)"`
|
||||||
|
Description string `json:"description" gorm:"size:500;comment:备注说明"`
|
||||||
|
CreatedAt time.Time `json:"created_at" gorm:"comment:创建时间"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at" gorm:"comment:更新时间"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (AdPlacement) TableName() string {
|
||||||
|
return "marketing_ad_placements"
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
|
||||||
|
"wx_service/internal/marketing/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrAdPlacementNotFound = errors.New("ad placement not found")
|
||||||
|
|
||||||
|
type AdPlacementRepository struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAdPlacementRepository(db *gorm.DB) *AdPlacementRepository {
|
||||||
|
return &AdPlacementRepository{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *AdPlacementRepository) Create(p *model.AdPlacement) error {
|
||||||
|
if err := r.db.Create(p).Error; err != nil {
|
||||||
|
return fmt.Errorf("create ad placement: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *AdPlacementRepository) Update(p *model.AdPlacement) error {
|
||||||
|
if err := r.db.Save(p).Error; err != nil {
|
||||||
|
return fmt.Errorf("update ad placement: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *AdPlacementRepository) Delete(id uint) error {
|
||||||
|
tx := r.db.Delete(&model.AdPlacement{}, id)
|
||||||
|
if tx.Error != nil {
|
||||||
|
return fmt.Errorf("delete ad placement: %w", tx.Error)
|
||||||
|
}
|
||||||
|
if tx.RowsAffected == 0 {
|
||||||
|
return ErrAdPlacementNotFound
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *AdPlacementRepository) FindByID(id uint) (*model.AdPlacement, error) {
|
||||||
|
var p model.AdPlacement
|
||||||
|
err := r.db.First(&p, id).Error
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, ErrAdPlacementNotFound
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("find ad placement: %w", err)
|
||||||
|
}
|
||||||
|
return &p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *AdPlacementRepository) ListAll() ([]model.AdPlacement, error) {
|
||||||
|
var list []model.AdPlacement
|
||||||
|
err := r.db.Order("id DESC").Find(&list).Error
|
||||||
|
return list, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *AdPlacementRepository) FindByMiniProgramAndType(miniProgramID uint, adType string) (*model.AdPlacement, error) {
|
||||||
|
var p model.AdPlacement
|
||||||
|
err := r.db.Where("mini_program_id = ? AND ad_type = ? AND status = 1", miniProgramID, adType).
|
||||||
|
First(&p).Error
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, ErrAdPlacementNotFound
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("find ad placement by type: %w", err)
|
||||||
|
}
|
||||||
|
return &p, nil
|
||||||
|
}
|
||||||
@@ -7,9 +7,13 @@ import (
|
|||||||
"wx_service/internal/marketing/repository"
|
"wx_service/internal/marketing/repository"
|
||||||
)
|
)
|
||||||
|
|
||||||
const MaxLogosPerUser = 10
|
const MaxLogosPerUser = 3
|
||||||
|
const MaxLogoFileSize = 2 * 1024 * 1024 // 2MB
|
||||||
|
|
||||||
var ErrLogoLimitReached = errors.New("Logo 数量已达上限")
|
var (
|
||||||
|
ErrLogoLimitReached = errors.New("Logo 数量已达上限")
|
||||||
|
ErrLogoTooLarge = errors.New("Logo 文件不能超过 2MB")
|
||||||
|
)
|
||||||
|
|
||||||
type UserLogoService struct {
|
type UserLogoService struct {
|
||||||
repo *repository.UserLogoRepository
|
repo *repository.UserLogoRepository
|
||||||
@@ -26,6 +30,10 @@ type SaveLogoRequest struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *UserLogoService) Save(userID uint, req SaveLogoRequest) (*model.UserLogo, error) {
|
func (s *UserLogoService) Save(userID uint, req SaveLogoRequest) (*model.UserLogo, error) {
|
||||||
|
if req.FileSize > MaxLogoFileSize {
|
||||||
|
return nil, ErrLogoTooLarge
|
||||||
|
}
|
||||||
|
|
||||||
count, err := s.repo.CountByUser(userID)
|
count, err := s.repo.CountByUser(userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ func registerAdminRoutes(
|
|||||||
categoryHandler *marketinghandler.CategoryHandler,
|
categoryHandler *marketinghandler.CategoryHandler,
|
||||||
templateHandler *marketinghandler.TemplateHandler,
|
templateHandler *marketinghandler.TemplateHandler,
|
||||||
downloadHandler *marketinghandler.DownloadHandler,
|
downloadHandler *marketinghandler.DownloadHandler,
|
||||||
|
adPlacementHandler *marketinghandler.AdPlacementHandler,
|
||||||
) {
|
) {
|
||||||
if handler == nil {
|
if handler == nil {
|
||||||
return
|
return
|
||||||
@@ -107,11 +108,21 @@ func registerAdminRoutes(
|
|||||||
marketing.PUT("/templates/:id", templateHandler.AdminUpdate)
|
marketing.PUT("/templates/:id", templateHandler.AdminUpdate)
|
||||||
marketing.DELETE("/templates/:id", templateHandler.AdminDelete)
|
marketing.DELETE("/templates/:id", templateHandler.AdminDelete)
|
||||||
|
|
||||||
marketing.GET("/stats", downloadHandler.AdminStats)
|
marketing.GET("/stats", downloadHandler.AdminStats)
|
||||||
marketing.POST("/upload/oss/token", downloadHandler.AdminUploadToken)
|
marketing.POST("/upload/oss/token", downloadHandler.AdminUploadToken)
|
||||||
marketing.POST("/upload", downloadHandler.AdminUploadFile)
|
marketing.POST("/upload", downloadHandler.AdminUploadFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
if adPlacementHandler != nil {
|
||||||
|
ads := protected.Group("/marketing/ad-placements")
|
||||||
|
{
|
||||||
|
ads.GET("", adPlacementHandler.AdminList)
|
||||||
|
ads.POST("", adPlacementHandler.AdminCreate)
|
||||||
|
ads.PUT("/:id", adPlacementHandler.AdminUpdate)
|
||||||
|
ads.DELETE("/:id", adPlacementHandler.AdminDelete)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ func registerMarketingRoutes(
|
|||||||
templateHandler *marketinghandler.TemplateHandler,
|
templateHandler *marketinghandler.TemplateHandler,
|
||||||
downloadHandler *marketinghandler.DownloadHandler,
|
downloadHandler *marketinghandler.DownloadHandler,
|
||||||
userLogoHandler *marketinghandler.UserLogoHandler,
|
userLogoHandler *marketinghandler.UserLogoHandler,
|
||||||
|
adPlacementHandler *marketinghandler.AdPlacementHandler,
|
||||||
) {
|
) {
|
||||||
if categoryHandler == nil || templateHandler == nil || downloadHandler == nil {
|
if categoryHandler == nil || templateHandler == nil || downloadHandler == nil {
|
||||||
return
|
return
|
||||||
@@ -24,6 +25,10 @@ func registerMarketingRoutes(
|
|||||||
marketing.GET("/categories", categoryHandler.ListEnabled)
|
marketing.GET("/categories", categoryHandler.ListEnabled)
|
||||||
marketing.GET("/templates", templateHandler.ListEnabled)
|
marketing.GET("/templates", templateHandler.ListEnabled)
|
||||||
marketing.GET("/templates/:id", templateHandler.GetDetail)
|
marketing.GET("/templates/:id", templateHandler.GetDetail)
|
||||||
|
|
||||||
|
if adPlacementHandler != nil {
|
||||||
|
marketing.GET("/ad-config", adPlacementHandler.GetAdConfig)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protectedMarketing := protected.Group("/marketing")
|
protectedMarketing := protected.Group("/marketing")
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ func Register(
|
|||||||
marketingTemplateHandler *marketinghandler.TemplateHandler,
|
marketingTemplateHandler *marketinghandler.TemplateHandler,
|
||||||
marketingDownloadHandler *marketinghandler.DownloadHandler,
|
marketingDownloadHandler *marketinghandler.DownloadHandler,
|
||||||
marketingUserLogoHandler *marketinghandler.UserLogoHandler,
|
marketingUserLogoHandler *marketinghandler.UserLogoHandler,
|
||||||
|
marketingAdPlacementHandler *marketinghandler.AdPlacementHandler,
|
||||||
quitCheckinHandler *quitcheckinhandler.Handler,
|
quitCheckinHandler *quitcheckinhandler.Handler,
|
||||||
) {
|
) {
|
||||||
// Register 用来集中注册所有 HTTP 路由,便于工程结构更清晰:
|
// Register 用来集中注册所有 HTTP 路由,便于工程结构更清晰:
|
||||||
@@ -65,13 +66,14 @@ func Register(
|
|||||||
protected.Use(middleware.AuthMiddleware(db, sessionCache))
|
protected.Use(middleware.AuthMiddleware(db, sessionCache))
|
||||||
protected.Use(middleware.RequireUserMiddleware())
|
protected.Use(middleware.RequireUserMiddleware())
|
||||||
{
|
{
|
||||||
|
protected.PUT("/auth/profile", authHandler.UpdateProfile)
|
||||||
registerCommonRoutes(protected, uploadHandler)
|
registerCommonRoutes(protected, uploadHandler)
|
||||||
registerRemoveWatermarkRoutes(api, protected, videoHandler)
|
registerRemoveWatermarkRoutes(api, protected, videoHandler)
|
||||||
registerMembershipRoutes(protected, redeemCodeHandler)
|
registerMembershipRoutes(protected, redeemCodeHandler)
|
||||||
registerSmokeRoutes(protected, smokeHandler, quitPlanHandler)
|
registerSmokeRoutes(protected, smokeHandler, quitPlanHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
registerMarketingRoutes(api, protected, adminToken, marketingCategoryHandler, marketingTemplateHandler, marketingDownloadHandler, marketingUserLogoHandler)
|
registerMarketingRoutes(api, protected, adminToken, marketingCategoryHandler, marketingTemplateHandler, marketingDownloadHandler, marketingUserLogoHandler, marketingAdPlacementHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
apiV2 := router.Group("/api/v2")
|
apiV2 := router.Group("/api/v2")
|
||||||
@@ -84,7 +86,7 @@ func Register(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
registerAdminRoutes(router, adminHandler, marketingCategoryHandler, marketingTemplateHandler, marketingDownloadHandler)
|
registerAdminRoutes(router, adminHandler, marketingCategoryHandler, marketingTemplateHandler, marketingDownloadHandler, marketingAdPlacementHandler)
|
||||||
|
|
||||||
// 保质期提醒模块使用独立前缀 /api/expiry,与现有 /api/v1 并存。
|
// 保质期提醒模块使用独立前缀 /api/expiry,与现有 /api/v1 并存。
|
||||||
expiryAPI := router.Group("/api/expiry")
|
expiryAPI := router.Group("/api/expiry")
|
||||||
|
|||||||
Reference in New Issue
Block a user