feat(marketing): 新增营销图管理模块

- 新增 marketing 模块:model/repository/service/handler 四层架构
- 数据模型:marketing_categories、marketing_templates、marketing_user_downloads
- 小程序端接口:分类列表、模板列表/详情、下载记录、广告回调
- 管理后台接口:分类/模板 CRUD、下载统计(X-Admin-Token 鉴权)
- 路由注册:接入现有 AuthMiddleware,新增 AdminTokenMiddleware
- Web 管理后台:单页面 Vue3 + Element Plus(分类管理、模板管理、数据概览)

Closes #37, #38, #39, #40

Made-with: Cursor
This commit is contained in:
nepiedg
2026-03-06 07:36:05 +00:00
parent 5f492929df
commit ac49e1458c
17 changed files with 1599 additions and 1 deletions
+50
View File
@@ -0,0 +1,50 @@
package routes
import (
"github.com/gin-gonic/gin"
marketinghandler "wx_service/internal/marketing/handler"
)
func registerMarketingRoutes(
api *gin.RouterGroup,
protected *gin.RouterGroup,
adminToken string,
categoryHandler *marketinghandler.CategoryHandler,
templateHandler *marketinghandler.TemplateHandler,
downloadHandler *marketinghandler.DownloadHandler,
) {
if categoryHandler == nil || templateHandler == nil || downloadHandler == nil {
return
}
marketing := api.Group("/marketing")
{
marketing.GET("/categories", categoryHandler.ListEnabled)
marketing.GET("/templates", templateHandler.ListEnabled)
marketing.GET("/templates/:id", templateHandler.GetDetail)
}
protectedMarketing := protected.Group("/marketing")
{
protectedMarketing.POST("/downloads", downloadHandler.Create)
protectedMarketing.POST("/ad_callback", downloadHandler.AdCallback)
protectedMarketing.GET("/downloads", downloadHandler.ListByUser)
}
admin := api.Group("/admin/marketing")
admin.Use(marketinghandler.AdminTokenMiddleware(adminToken))
{
admin.GET("/categories", categoryHandler.AdminList)
admin.POST("/categories", categoryHandler.AdminCreate)
admin.PUT("/categories/:id", categoryHandler.AdminUpdate)
admin.DELETE("/categories/:id", categoryHandler.AdminDelete)
admin.GET("/templates", templateHandler.AdminList)
admin.POST("/templates", templateHandler.AdminCreate)
admin.PUT("/templates/:id", templateHandler.AdminUpdate)
admin.DELETE("/templates/:id", templateHandler.AdminDelete)
admin.GET("/stats", downloadHandler.AdminStats)
}
}
+7
View File
@@ -12,6 +12,7 @@ import (
oahandler "wx_service/internal/common/wechat_official/handler"
expiryhandler "wx_service/internal/expiry"
lawyerhandler "wx_service/internal/lawyer/handler"
marketinghandler "wx_service/internal/marketing/handler"
membershiphandler "wx_service/internal/membership/handler"
"wx_service/internal/middleware"
rmhandler "wx_service/internal/remove_watermark/handler"
@@ -30,6 +31,10 @@ func Register(
sessionCache *rediscache.SessionUserCache,
lawyerHandler *lawyerhandler.LawyerHandler,
expiryHandler *expiryhandler.Handler,
adminToken string,
marketingCategoryHandler *marketinghandler.CategoryHandler,
marketingTemplateHandler *marketinghandler.TemplateHandler,
marketingDownloadHandler *marketinghandler.DownloadHandler,
) {
// Register 用来集中注册所有 HTTP 路由,便于工程结构更清晰:
// - main 只负责初始化(配置/DB/依赖注入)
@@ -57,6 +62,8 @@ func Register(
registerMembershipRoutes(protected, redeemCodeHandler)
registerSmokeRoutes(protected, smokeHandler)
}
registerMarketingRoutes(api, protected, adminToken, marketingCategoryHandler, marketingTemplateHandler, marketingDownloadHandler)
}
// 保质期提醒模块使用独立前缀 /api/expiry,与现有 /api/v1 并存。