Files
wx_service/cmd/api/main.go
T
nepiedg ac49e1458c 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
2026-03-06 07:36:05 +00:00

156 lines
6.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package main
import (
"log"
"time"
"github.com/gin-gonic/gin"
"wx_service/config"
authhandler "wx_service/internal/common/auth/handler"
authservice "wx_service/internal/common/auth/service"
qiniuhandler "wx_service/internal/common/qiniu/handler"
qiniuservice "wx_service/internal/common/qiniu/service"
rediscache "wx_service/internal/common/redis/cache"
redisservice "wx_service/internal/common/redis/service"
oahandler "wx_service/internal/common/wechat_official/handler"
oaservice "wx_service/internal/common/wechat_official/service"
"wx_service/internal/database"
expiry "wx_service/internal/expiry"
lawyerhandler "wx_service/internal/lawyer/handler"
lawyerservice "wx_service/internal/lawyer/service"
marketinghandler "wx_service/internal/marketing/handler"
marketingmodel "wx_service/internal/marketing/model"
marketingrepo "wx_service/internal/marketing/repository"
marketingservice "wx_service/internal/marketing/service"
membershiphandler "wx_service/internal/membership/handler"
membershipmodel "wx_service/internal/membership/model"
membershipservice "wx_service/internal/membership/service"
"wx_service/internal/model"
"wx_service/internal/observability"
rmhandler "wx_service/internal/remove_watermark/handler"
rmmodel "wx_service/internal/remove_watermark/model"
rmservice "wx_service/internal/remove_watermark/service"
"wx_service/internal/routes"
smokehandler "wx_service/internal/smoke/handler"
smokemodel "wx_service/internal/smoke/model"
smokeservice "wx_service/internal/smoke/service"
)
func main() {
if loc, err := time.LoadLocation("Asia/Shanghai"); err == nil {
time.Local = loc
} else {
time.Local = time.FixedZone("CST", 8*3600)
}
// 1) 加载配置(通常来自环境变量 / .env)
config.LoadConfig()
// 2) 初始化数据库连接
if err := database.InitDB(); err != nil {
log.Fatalf("init database failed: %v", err)
}
// 3) 自动建表/迁移(开发阶段很方便;生产环境可改为手动迁移)
if err := database.AutoMigrate(
&model.MiniProgram{},
&model.User{},
&model.UserMembership{},
&expiry.ExpiryItem{},
&expiry.ExpiryUserSettings{},
&membershipmodel.MembershipRedeemCode{},
&membershipmodel.MembershipRedemption{},
&rmmodel.VideoParseLog{},
&rmmodel.VideoParseUnlock{},
&rmmodel.VideoDownloadFailure{},
&smokemodel.SmokeLog{},
&smokemodel.SmokeUserProfile{},
&smokemodel.SmokeAIAdvice{},
&smokemodel.SmokeAIAdviceUnlock{},
&smokemodel.SmokeAINextSmoke{},
&smokemodel.SmokeMotivationQuote{},
&marketingmodel.MarketingCategory{},
&marketingmodel.MarketingTemplate{},
&marketingmodel.MarketingDownload{},
); err != nil {
log.Fatalf("auto migrate failed: %v", err)
}
// 4) 初始化 HTTP 框架(Gin
gin.SetMode(config.AppConfig.Server.Mode)
router := gin.Default()
metricsCollector := observability.NewCollector()
router.Use(observability.RequestLogMiddleware(metricsCollector))
router.GET("/metrics/basic", observability.BasicMetricsHandler(metricsCollector))
// 5) 依赖注入:先创建 service,再创建 handlerhandler 只关心 HTTP 输入/输出)
miniProgramService := authservice.NewMiniProgramService(database.DB)
authService := authservice.NewAuthService(database.DB, miniProgramService)
authHandler := authhandler.NewAuthHandler(authService)
videoService, err := rmservice.NewVideoService(database.DB, config.AppConfig.ShortVideo)
if err != nil {
log.Fatalf("init video service failed: %v", err)
}
videoHandler := rmhandler.NewVideoHandler(videoService)
smokeLogService := smokeservice.NewSmokeLogService(database.DB)
smokeAIAdviceService := smokeservice.NewSmokeAIAdviceService(database.DB, config.AppConfig.AI)
smokeProfileService := smokeservice.NewSmokeProfileService(database.DB)
smokeNextService := smokeservice.NewSmokeNextService(database.DB)
smokeAINextService := smokeservice.NewSmokeAINextSmokeService(database.DB, config.AppConfig.AI)
smokeHandler := smokehandler.NewSmokeHandler(smokeLogService, smokeAIAdviceService, smokeProfileService, smokeNextService, smokeAINextService)
redeemCodeService := membershipservice.NewRedeemCodeService(database.DB, config.AppConfig.Admin.Token)
redeemCodeHandler := membershiphandler.NewRedeemCodeHandler(redeemCodeService)
qiniuService := qiniuservice.NewQiniuService(config.AppConfig.Qiniu)
uploadHandler := qiniuhandler.NewUploadHandler(qiniuService)
oaService := oaservice.NewWeChatOAService(config.AppConfig.WeChatOA)
oaOAuthHandler := oahandler.NewOAuthHandler(oaService)
var sessionCache *rediscache.SessionUserCache
redisClient, err := redisservice.NewClient(config.AppConfig.Redis)
if err != nil {
log.Printf("redis disabled: %v", err)
} else if redisClient != nil {
sessionCache = rediscache.NewSessionUserCache(redisClient.Redis(), redisClient.KeyPrefix(), redisClient.SessionTTL())
}
var lawyerHandler *lawyerhandler.LawyerHandler
if lawyerDB, ok := database.GetAdditionalDB("lawyer"); ok {
lawyerService := lawyerservice.NewService(lawyerDB)
lawyerHandler = lawyerhandler.NewLawyerHandler(lawyerService)
} else {
log.Println("lawyer 数据库未配置,/lawyers 接口已禁用")
}
expiryRepo := expiry.NewRepository(database.DB)
expiryService := expiry.NewService(expiryRepo)
if redisClient != nil {
expirySummaryCache := expiry.NewSummaryCache(redisClient.Redis(), redisClient.KeyPrefix(), 5*time.Minute)
expiryService.BindSummaryCache(expirySummaryCache)
}
expiryHandler := expiry.NewHandler(expiryService)
categoryRepo := marketingrepo.NewCategoryRepository(database.DB)
templateRepo := marketingrepo.NewTemplateRepository(database.DB)
downloadRepo := marketingrepo.NewDownloadRepository(database.DB)
categorySvc := marketingservice.NewCategoryService(categoryRepo)
templateSvc := marketingservice.NewTemplateService(templateRepo)
downloadSvc := marketingservice.NewDownloadService(downloadRepo, templateRepo)
marketingCategoryHandler := marketinghandler.NewCategoryHandler(categorySvc)
marketingTemplateHandler := marketinghandler.NewTemplateHandler(templateSvc)
marketingDownloadHandler := marketinghandler.NewDownloadHandler(downloadSvc)
// 6) 注册路由:把 URL 映射到 handler
routes.Register(router, database.DB, authHandler, videoHandler, smokeHandler, redeemCodeHandler, uploadHandler, oaOAuthHandler, sessionCache, lawyerHandler, expiryHandler, config.AppConfig.Admin.Token, marketingCategoryHandler, marketingTemplateHandler, marketingDownloadHandler)
// 7) 启动监听端口
addr := ":" + config.AppConfig.Server.Port
if err := router.Run(addr); err != nil {
log.Fatalf("server stopped: %v", err)
}
}