update
This commit is contained in:
@ -19,8 +19,11 @@ import (
|
||||
"fashionapi/internal/config"
|
||||
"fashionapi/internal/database"
|
||||
"fashionapi/internal/handler"
|
||||
"fashionapi/internal/middleware"
|
||||
"fashionapi/internal/pkg/hashid"
|
||||
"fashionapi/internal/pkg/imgurl"
|
||||
"fashionapi/internal/pkg/jwt"
|
||||
"fashionapi/internal/pkg/storage"
|
||||
"fashionapi/internal/repository"
|
||||
"fashionapi/internal/router"
|
||||
"fashionapi/internal/service"
|
||||
@ -70,25 +73,61 @@ func main() {
|
||||
|
||||
// 5. 装配各层依赖
|
||||
var (
|
||||
articleRepo = repository.NewArticleRepository(db)
|
||||
brandRepo = repository.NewBrandRepository(db)
|
||||
userRepo = repository.NewUserRepository(db)
|
||||
snapRepo = repository.NewStreetSnapRepository(db)
|
||||
refreshRepo = repository.NewRefreshTokenRepository(db)
|
||||
favRepo = repository.NewFavoriteRepository(db)
|
||||
histRepo = repository.NewHistoryRepository(db)
|
||||
articleRepo = repository.NewArticleRepository(db)
|
||||
brandRepo = repository.NewBrandRepository(db)
|
||||
userRepo = repository.NewUserRepository(db)
|
||||
snapRepo = repository.NewStreetSnapRepository(db)
|
||||
refreshRepo = repository.NewRefreshTokenRepository(db)
|
||||
favRepo = repository.NewFavoriteRepository(db)
|
||||
histRepo = repository.NewHistoryRepository(db)
|
||||
ingestRepo = repository.NewIngestRepository(db)
|
||||
reviewRepo = repository.NewReviewRepository(db)
|
||||
// 跨表图片引用计数(删除图集时判定七牛孤儿文件用)
|
||||
mediaRepo = repository.NewMediaRepository(db)
|
||||
|
||||
jwtManager = jwt.NewManager(cfg.JWT.Secret, cfg.JWT.ExpireHours)
|
||||
|
||||
articleSvc = service.NewArticleService(articleRepo)
|
||||
// 图片 URL 拼装器:库里只存七牛 key,渲染时按 tier 拼 base_url + 样式。
|
||||
imgComposer = imgurl.New(cfg.Qiniu.BaseURL, cfg.Qiniu.StyleNormal, cfg.Qiniu.StyleVip, cfg.Qiniu.LocalBase, cfg.Qiniu.AK, cfg.Qiniu.SK, cfg.Qiniu.SignTTLNormal, cfg.Qiniu.SignTTLHD)
|
||||
|
||||
// 图片存储:七牛云优先(若启用),失败兜底本地 uploads。
|
||||
// localUp 同时实现 Uploader 与 Deleter(本地兜底删除);启用七牛时两者均为七牛实现。
|
||||
localUp *storage.LocalUploader = storage.NewLocalUploader(uploadDir, cfg.Upload.URLPrefix)
|
||||
imgUp storage.Uploader = localUp
|
||||
imgDel storage.Deleter = localUp
|
||||
|
||||
articleSvc service.ArticleService // 在 ingestSvc 创建后再装配(需注入清理入队器)
|
||||
brandSvc = service.NewBrandService(brandRepo)
|
||||
indexSvc = service.NewIndexService(brandRepo)
|
||||
snapSvc = service.NewStreetSnapService(snapRepo)
|
||||
indexSvc = service.NewIndexService(brandRepo, imgComposer)
|
||||
snapSvc service.StreetSnapService // 在 ingestSvc 创建后再装配(需注入清理入队器)
|
||||
authSvc = service.NewAuthService(userRepo, refreshRepo, jwtManager, cfg.JWT.ExpireHours, cfg.JWT.RefreshExpireHours)
|
||||
favSvc = service.NewFavoriteService(favRepo)
|
||||
histSvc = service.NewHistoryService(histRepo)
|
||||
// 审核:草稿列表 / 通过晋升正式表 / 驳回(后台预览始终高清)
|
||||
reviewSvc = service.NewReviewService(reviewRepo, mediaRepo, imgComposer, imgDel)
|
||||
)
|
||||
|
||||
// 爬虫入库:入队 + 异步 worker(去重 / 补季节码 / 下载图 / 写草稿表)
|
||||
// 图片存储:七牛云优先(若启用),失败兜底本地 uploads。
|
||||
// imgUp / imgDel 已在上方 var 块声明,这里仅在启用七牛时切换为七牛实现。
|
||||
if cfg.Qiniu.Enabled {
|
||||
// 注意:上传器不传 base_url(末参传空),Upload 只返回七牛 key;
|
||||
// 完整可访问 URL 由 imgComposer 在读取时按 tier 拼 base_url + 样式,
|
||||
// 这样既方便换 CDN 域名,又能做普通/高清分级(见 internal/pkg/imgurl)。
|
||||
qu := storage.NewQiniuUploader(cfg.Qiniu.AK, cfg.Qiniu.SK, cfg.Qiniu.Bucket, cfg.Qiniu.Zone, "")
|
||||
imgUp = qu
|
||||
imgDel = qu
|
||||
log.Printf("✓ 图片存储: 七牛云 bucket=%s zone=%s(库只存 key,渲染时拼 base_url=%s)", cfg.Qiniu.Bucket, cfg.Qiniu.Zone, cfg.Qiniu.BaseURL)
|
||||
} else {
|
||||
log.Printf("✓ 图片存储: 本地 %s", cfg.Upload.URLPrefix)
|
||||
}
|
||||
// 爬虫入库服务:注入 mediaRepo(引用计数删孤儿)+ imgDel(七牛/本地删除器)。
|
||||
// 必须在 articleSvc / snapSvc 之前创建——图集服务删除时需调用它把「清理七牛」异步入队。
|
||||
ingestSvc := service.NewIngestService(ingestRepo, brandRepo, mediaRepo, imgUp, imgDel, localUp)
|
||||
// 图集服务:注入 ingestSvc 作为「清理七牛孤儿图」的入队器,使下架/删除异步触发存储清理。
|
||||
articleSvc = service.NewArticleService(articleRepo, mediaRepo, imgComposer, imgDel, ingestSvc)
|
||||
snapSvc = service.NewStreetSnapService(snapRepo, mediaRepo, imgComposer, imgDel, ingestSvc)
|
||||
|
||||
engine := router.New(router.Options{
|
||||
Config: cfg,
|
||||
JWT: jwtManager,
|
||||
@ -101,6 +140,11 @@ func main() {
|
||||
Health: handler.NewHealthHandler(),
|
||||
})
|
||||
|
||||
// 5.5 装配管理后台(Backstage)handler,供下方独立引擎使用。
|
||||
backstageHandler := handler.NewBackstageHandler(authSvc, brandSvc, articleSvc, snapSvc, reviewSvc, ingestSvc)
|
||||
// 爬虫上报 handler(HMAC 验签入口)
|
||||
ingestHandler := handler.NewIngestHandler(ingestSvc, brandSvc)
|
||||
|
||||
// 6. 启动对外公开 HTTP 服务(8090),并支持优雅关闭
|
||||
srv := &http.Server{
|
||||
Addr: ":" + cfg.Server.Port,
|
||||
@ -133,6 +177,37 @@ func main() {
|
||||
}
|
||||
}()
|
||||
|
||||
// 7.5 启动管理后台(默认 8092):后台 UI + 爬虫 ingest 内部接口。
|
||||
backstageEngine := router.NewBackstage(router.BackstageOptions{
|
||||
Config: cfg,
|
||||
JWT: jwtManager,
|
||||
Backstage: backstageHandler,
|
||||
UploadURLPrefix: cfg.Upload.URLPrefix,
|
||||
UploadDir: uploadDir,
|
||||
Ingest: ingestHandler,
|
||||
IngestAuth: &middleware.IngestAuthConfig{
|
||||
Secret: cfg.Ingest.Secret,
|
||||
TTL: cfg.Ingest.TTLSeconds,
|
||||
Nonces: ingestRepo,
|
||||
},
|
||||
})
|
||||
|
||||
// 7.6 启动爬虫入库 worker(异步处理 ingest_jobs 队列)。
|
||||
// 用独立可取消 ctx,优雅关闭时随主流程一起退出。
|
||||
workerCtx, workerCancel := context.WithCancel(context.Background())
|
||||
go ingestSvc.Run(workerCtx, 2, 3*time.Second)
|
||||
log.Printf("⚙ 入库 worker 已启动(队列 ingest_jobs)")
|
||||
srvBackstage := &http.Server{
|
||||
Addr: ":" + cfg.Server.BackstagePort,
|
||||
Handler: backstageEngine,
|
||||
}
|
||||
go func() {
|
||||
log.Printf("🛠 管理后台已启动: http://localhost:%s/admin", cfg.Server.BackstagePort)
|
||||
if err := srvBackstage.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
log.Fatalf("✗ 管理后台启动失败: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// 收到中断信号后,给正在处理的请求留出完成时间再退出,
|
||||
// 避免直接杀进程导致请求被截断、连接池未正常释放。
|
||||
quit := make(chan os.Signal, 1)
|
||||
@ -143,11 +218,15 @@ func main() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(),
|
||||
time.Duration(cfg.Server.ShutdownTimeout)*time.Second)
|
||||
defer cancel()
|
||||
workerCancel() // 通知入库 worker 停止领取新任务
|
||||
if err := srv.Shutdown(ctx); err != nil {
|
||||
log.Printf("! 公开服务优雅关闭超时: %v", err)
|
||||
}
|
||||
if err := srvSSG.Shutdown(ctx); err != nil {
|
||||
log.Printf("! SSG 内部服务优雅关闭超时: %v", err)
|
||||
}
|
||||
if err := srvBackstage.Shutdown(ctx); err != nil {
|
||||
log.Printf("! 管理后台优雅关闭超时: %v", err)
|
||||
}
|
||||
log.Println("✓ 服务已停止")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user