topfans/backend/gateway/router/router.go
zerosaturation 7ff2d5cdc4 fix(gateway): restore /api/v1/starbook/* via assetService (recovery from Task 2 over-deletion)
- 恢复 /api/v1/starbook/home + /api/v1/starbook/items 网关路由
  (owner 从被删的 starbookService 切到 assetService);
- assetService 加 GetAssetsByType RPC (type/category/grade 过滤), 仅改 asset.pb.go (不动 triple.go);
- gateway 新建 starbook_controller.go 薄壳 (auth + 参数解析 + RPC);
  使用 Task 1 扩展接口 AssetServiceGetAssetsTypeClient (零装配增量, 复用现有 assetClient);
- 响应结构与原端点完全一致 (前端 4 处调用零修改, 实测 home 2514 bytes JSON shape 一致);
- items 支持 type/category/grade/page/page_size 完整参数;
- 路由注入到 AuthMiddleware 保护的 v1 group;
- 5/5 starbook 测试 + 完整 workspace build/vet 通过。

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-24 14:04:01 +08:00

534 lines
26 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 router
import (
"net/http"
"time"
"dubbo.apache.org/dubbo-go/v3/client"
"github.com/gin-gonic/gin"
swaggerfiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
_ "github.com/topfans/backend/gateway/docs" // 导入 docs 包以初始化 Swagger
"github.com/topfans/backend/gateway/config"
"github.com/topfans/backend/gateway/controller"
"github.com/topfans/backend/gateway/middleware"
"github.com/topfans/backend/gateway/pkg/starcache"
"github.com/topfans/backend/gateway/repository"
"github.com/topfans/backend/gateway/service"
"github.com/topfans/backend/pkg/database"
"github.com/topfans/backend/gateway/socket"
pbModeration "github.com/topfans/backend/pkg/proto/moderation"
assetRepo "github.com/topfans/backend/services/assetService/repository"
assetSvc "github.com/topfans/backend/services/assetService/service"
)
// SetupRouter 设置路由
//
// ★ 批次 4.2:starbookService 已删除,SetupRouter 签名同步移除 starbookClient 参数。
// 同时移除 /api/v1/starbook/* 两个路由(GetStarbookHome / GetStarbookItems)。
// 星册数据已在 assetService.GetMyAssets 提供(批次 4.2 起 owner 是 assetService)。
func SetupRouter(userClient *client.Client, socialClient *client.Client, assetClient *client.Client, galleryClient *client.Client, activityClient *client.Client, taskClient *client.Client, aiChatClient *client.Client, statisticClient *client.Client, notificationClient *client.Client, modSvc pbModeration.ModerationService, aiChatPath string, activityHub *socket.ActivityHub, starCache *starcache.Cache) (*gin.Engine, error) {
r := gin.Default()
// 全局中间件
r.Use(middleware.CORSMiddleware())
r.Use(middleware.LoggerMiddleware())
// GRPCStatusInterceptor 必须放在所有路由组中间件之后执行(它包裹 c.Writer)
// 实际上是 pre 阶段 wrap,post 阶段根据 status 决定是否改写
// 注意: AuthMiddleware 写在 c.Abort() 之前会 set 401 header,本拦截器 post 阶段
// 检测到 4xx/5xx 不会改写(详见 grpc_status_interceptor.go 注释)
r.Use(middleware.GRPCStatusInterceptor())
// 健康检查
r.GET("/health", func(c *gin.Context) {
c.JSON(200, gin.H{
"status": "ok",
"service": "top-fans-gateway",
})
})
// Swagger UI - 使用默认配置,自动从 docs 包读取
swaggerHandler := ginSwagger.WrapHandler(swaggerfiles.Handler)
r.GET("/swagger/*any", swaggerHandler)
// AI Chat WebSocket 路由
aiChatHub := socket.NewHub(aiChatClient, aiChatPath)
r.GET(aiChatPath, gin.WrapF(func(w http.ResponseWriter, r *http.Request) {
aiChatHub.HandleWebSocket(w, r)
}))
// Activity 实时推送 WebSocket 路由
r.GET(activityHub.ActivityPath(), gin.WrapF(func(w http.ResponseWriter, r *http.Request) {
activityHub.HandleWebSocket(w, r)
}))
// 创建控制器
authCtrl, err := controller.NewAuthController(userClient, starCache)
if err != nil {
return nil, err
}
userCtrl, err := controller.NewUserController(userClient, starCache)
if err != nil {
return nil, err
}
socialCtrl, err := controller.NewSocialController(socialClient)
if err != nil {
return nil, err
}
assetCtrl, err := controller.NewAssetController(assetClient, userClient, galleryClient)
if err != nil {
return nil, err
}
AssetController = assetCtrl // 暴露给 main.go 启动 mintSuccessCh consumer
// 周边验真 + 加入藏品 controller
peripheralRepo := assetRepo.NewPeripheralRepository(database.GetDB())
peripheralLimiter := assetSvc.NewMintRateLimiter(database.GetRedis(), 10, 24*time.Hour)
peripheralSvc := assetSvc.NewPeripheralServiceWithLimiter(peripheralRepo, peripheralLimiter)
peripheralCtrl := controller.NewPeripheralController(peripheralSvc)
galleryCtrl, err := controller.NewGalleryController(galleryClient, socialClient)
if err != nil {
return nil, err
}
// ★ starbook-recovery:批次 4.2 误删路由,现用 assetClient 单实例支撑 /starbook/home + /starbook/items
// client 类型为扩展接口 AssetServiceGetAssetsByTypeClient(同时含 GetMyAssets 和 GetAssetsByType)
starbookCtrl, err := controller.NewStarbookController(assetClient)
if err != nil {
return nil, err
}
rankingCtrl, err := controller.NewRankingController(assetClient)
if err != nil {
return nil, err
}
castloveConfigCtrl, err := controller.NewCastloveConfigController(assetClient)
if err != nil {
return nil, err
}
activityCtrl, err := controller.NewActivityController(activityClient)
if err != nil {
return nil, err
}
taskCtrl, err := controller.NewTaskController(taskClient)
if err != nil {
return nil, err
}
statisticCtrl, err := controller.NewStatisticController(statisticClient)
if err != nil {
return nil, err
}
notificationCtrl, err := controller.NewNotificationController(notificationClient)
if err != nil {
return nil, err
}
segmentCtrl := controller.NewSegmentController()
laserGenCtrl := controller.NewLaserGenerateController(config.Load())
composeCtrl := controller.NewComposeController(service.NewOssHelper(config.Load().OSS))
aiChatCtrl, err := controller.NewAIChatController(aiChatClient)
if err != nil {
return nil, err
}
// ModerationController - service 注入由 main.go 在路由注册前完成spec §A.9.1
modCtrl := controller.NewModerationController()
modCtrl.SetService(modSvc)
// App 下载页 — gateway 直连 PG无 Dubbo RPC
appDownloadRepo := repository.NewAppDownloadRepository(database.GetDB())
appDownloadSvc := service.NewAppDownloadService(appDownloadRepo)
appDownloadCtrl := controller.NewAppDownloadController(appDownloadSvc)
// API v1 路由组
v1 := r.Group("/api/v1")
{
// 认证相关路由(公开)
// ★ 鉴权边界审计 §四 P2validate / refresh / logout 均需受 AuthMiddleware 保护,
// 不应公开。register / login / check-* / send-code / verify-code / reset-password
// 是注册/登录前置流程,保持公开。
auth := v1.Group("/auth")
{
auth.POST("/register", authCtrl.Register) // 注册
auth.POST("/login", authCtrl.Login) // 登录
auth.POST("/check-nickname", authCtrl.CheckNickname) // 检查昵称是否被注册
auth.POST("/check-mobile", authCtrl.CheckMobile) // 检查手机号是否被注册
auth.POST("/send-code", authCtrl.SendCode) // 发送验证码
auth.POST("/verify-code", authCtrl.VerifyCode) // 验证验证码
auth.POST("/reset-password", authCtrl.ResetPassword) // 匿名重置密码(忘记密码场景)
// 注意:/validate 不在公开组(已移到下方 authProtected)
}
// 数据看板路由(需要 JWT 鉴权)
dashboard := v1.Group("/dashboard")
dashboard.Use(middleware.AuthMiddleware())
{
dashboard.GET("/today-overview", statisticCtrl.GetTodayOverview) // 1. 今日概览
dashboard.GET("/income-curve", statisticCtrl.Get7DayIncomeCurve) // 2. 七日收益曲线
dashboard.GET("/exhibition-summary", statisticCtrl.GetExhibitionIncomeSummary) // 3. 展出收益中心
dashboard.GET("/like-income-by-level", statisticCtrl.GetLikeIncomeByLevel) // 4. 点赞等级收益
dashboard.GET("/top-assets", statisticCtrl.GetTopAssets) // 5. 藏品 TOP5
dashboard.GET("/level-distribution", statisticCtrl.GetLevelDistribution) // 6. 等级分布
dashboard.GET("/upgrade-progress", statisticCtrl.GetUpgradeProgress) // 7. 升级进度
}
// 认证相关路由(需要认证)
// ★ /validate 在此组(挂 AuthMiddleware):
// 验证 Token 是鉴权能力,本身需要调用方已持有有效 JWT。
// 公开 /validate 允许任意人尝试"试探 token 有效性",违反最小权限原则。
// 修复:从上方公开组移到此处(鉴权边界审计 §四 P2)。
// ★ /refresh 保持现状:
// 当前 RefreshToken 控制器依赖 AuthMiddleware 从 JWT 解析 user_id/star_id,
// 而 Login 响应里 refresh_token 暂未下发("", // refresh_token 暂时为空,
// 详见 auth_controller.go:175),即当前未实现"凭 refresh token 续命"机制。
// 强行把 /refresh 改成公开,会让 AuthMiddleware 拿不到 user_id,刷新失败。
// 后续若上线独立 refresh token 机制,需新增"refresh-token 专用中间件"以区分。
authProtected := v1.Group("/auth")
authProtected.Use(middleware.AuthMiddleware())
{
authProtected.GET("/me", userCtrl.GetCurrentUser) // 获取当前用户信息
authProtected.POST("/refresh", authCtrl.RefreshToken) // 刷新 Token
authProtected.POST("/logout", authCtrl.Logout) // 登出
authProtected.POST("/validate", authCtrl.ValidateToken) // 验证 Token(需已登录)
}
// 用户相关路由(公开)
users := v1.Group("/users")
users.Use(middleware.AuthMiddleware())
{
users.GET("/:user_id", userCtrl.GetUser) // 获取指定用户信息
users.GET("/:user_id/liked-assets", socialCtrl.GetUserLikedAssets) // 获取他人点赞的作品列表
users.GET("/:user_id/exhibited-assets", galleryCtrl.GetUserExhibitedAssets) // 获取他人展出的作品列表
}
// 粉丝档案相关路由(公开)
fanProfiles := v1.Group("/fan-profiles")
{
fanProfiles.GET("", userCtrl.GetFanProfile) // 获取指定粉丝档案query params
}
// 粉丝身份相关路由(公开)
fanIdentities := v1.Group("/fan-identities")
{
fanIdentities.GET("", userCtrl.GetFanIdentities) // 获取可选粉丝身份列表
}
// 公开 OSS 上传签名(用于注册等未登录场景的上传,如注册头像)
public := v1.Group("/public")
{
public.GET("/oss/upload-signature", assetCtrl.GetPublicOSSUploadSignature) // 公开 OSS 上传签名
}
// 周边验真(公开,H5 可访问,无需 JWT)
// 同 prefix 不同 middleware:Gin 允许两个 group 共用前缀,
// 验证接口挂在无 auth 的 publicAssets,其他 /assets/* 走 AuthMiddleware。
// 按周边验真(公开) —— stage 1 后仅保留 by-hash(详见 design §5.1.4)
peripheralInfo := v1.Group("/peripheral-info")
{
peripheralInfo.GET("/by-hash/:hash", peripheralCtrl.GetVerificationByHash)
}
// 加入藏品(需 JWT)
peripheralMint := v1.Group("/peripheral-info")
peripheralMint.Use(middleware.AuthMiddleware())
{
peripheralMint.POST("/mint-by-hash/:hash", peripheralCtrl.MintFromPeripheralByHash)
}
// 当前用户相关路由(需要认证)
me := v1.Group("/me")
me.Use(middleware.AuthMiddleware())
{
me.GET("/profile", userCtrl.GetMyProfile) // 获取当前用户粉丝档案
me.PUT("/nickname", userCtrl.UpdateNickname) // 更新昵称
me.PUT("/avatar", userCtrl.UpdateAvatar) // 更新头像
me.GET("/liked-assets", socialCtrl.GetMyLikedAssets) // 获取我点赞的作品列表
me.GET("/today-liked-assets", socialCtrl.GetMyTodayLikedAssets) // 获取我今日点赞的作品列表
me.GET("/week-liked-assets", socialCtrl.GetMyWeekLikedAssets) // 获取我本周点赞的作品列表
me.GET("/exhibited-assets", galleryCtrl.GetMyExhibitedAssets) // 获取我展出的作品列表
}
// 账户管理路由(需要认证)
account := v1.Group("/account")
account.Use(middleware.AuthMiddleware())
{
account.POST("/password", userCtrl.UpdatePassword) // 更新密码
}
// 用户相关路由(需要认证)
user := v1.Group("/user")
user.Use(middleware.AuthMiddleware())
{
user.POST("/delete-account", userCtrl.DeleteAccount) // 注销账号
}
// 我的粉丝身份管理路由(需要认证)
myFanIdentities := v1.Group("/my/fan-identities")
myFanIdentities.Use(middleware.AuthMiddleware())
{
myFanIdentities.GET("", userCtrl.GetMyFanIdentities) // 获取我的粉丝身份列表
myFanIdentities.POST("", userCtrl.AddIdentity) // 添加粉丝身份
myFanIdentities.POST("/switch", userCtrl.SwitchIdentity) // 切换粉丝身份
}
// 社交功能路由(需要认证)
social := v1.Group("/social")
social.Use(middleware.AuthMiddleware())
{
// 用户搜索(用于加好友)
social.GET("/search-user", socialCtrl.SearchUserForFriend) // 查找用户信息
social.GET("/random-users", socialCtrl.GetRandomUsers) // 获取随机用户
social.GET("/users", socialCtrl.GetUsersPaged) // 获取分页用户列表
// 好友请求相关
social.POST("/friend-requests", socialCtrl.SendFriendRequest) // 发送好友请求
social.GET("/friend-requests", socialCtrl.GetFriendRequests) // 获取好友请求列表
social.POST("/friend-requests/handle", socialCtrl.HandleFriendRequest) // 处理好友请求
// 好友管理相关
social.GET("/friends", socialCtrl.GetFriendList) // 获取好友列表
social.DELETE("/friends", socialCtrl.DeleteFriend) // 删除好友
social.PUT("/friends/remark", socialCtrl.SetFriendRemark) // 设置好友备注
// 好友关系查询
social.GET("/friendship/check", socialCtrl.CheckFriendship) // 检查好友关系
social.GET("/friends/count", socialCtrl.GetFriendCount) // 获取好友数量
// 资产点赞操作(保留操作接口,查询状态已集成到资产详情)
social.POST("/assets/:asset_id/like", socialCtrl.LikeAsset) // 点赞资产
social.DELETE("/assets/:asset_id/like", socialCtrl.UnlikeAsset) // 取消点赞
social.GET("/assets/:asset_id/likers", socialCtrl.GetAssetLikers) // 获取点赞用户列表
// 注意GET /api/v1/assets/:asset_id 会返回 is_liked 字段,无需单独查询
}
// 通知相关路由(需要认证)
notifications := v1.Group("/notifications")
notifications.Use(middleware.AuthMiddleware())
{
notifications.GET("", notificationCtrl.GetNotifications) // 获取通知列表
notifications.GET("/unread-count", notificationCtrl.GetUnreadCount) // 获取未读数
notifications.POST("/:id/read", notificationCtrl.MarkAsRead) // 标记单条已读
notifications.POST("/targets/:target_id/read", notificationCtrl.MarkAsReadByTarget) // 按 target 标记已读
notifications.POST("/read-all", notificationCtrl.MarkAllAsRead) // 全部已读
notifications.DELETE("/:id", notificationCtrl.DeleteNotification) // 删除单条
notifications.DELETE("/targets/:target_id", notificationCtrl.DeleteByTarget) // 按 target 删除
notifications.DELETE("/clear", notificationCtrl.ClearByType) // 按 type 全软删(站内邮箱聚合页"清空"按钮)
// 推送设备注册:App 启动后上报 cid,后端写入 user_devices。
// 注销接口放在静态子路径(/devices/unregister),避免与 :id 动态路由冲突。
notifications.POST("/devices", notificationCtrl.RegisterDevice) // 注册/更新 cid
notifications.POST("/devices/unregister", notificationCtrl.UnregisterDevice) // 注销 cid(空 cid = 全部)
}
// Admin 内部接口(无鉴权,内网部署)
// 调用方:Python admin (TopFans-activity-admin, 端口 8081)
// 部署侧必须用 Nginx/防火墙限制 8080 只接受 admin 段 IP 访问 /api/v1/admin/*。
// 用途:admin 后台发送系统/活动通知 → 走 Go 通知服务 → 触发手机推送。
// 之前 admin 直接写共享 PG 表,bypassed 通知服务的 triggerPush 链路。
admin := v1.Group("/admin")
{
admin.POST("/notifications", notificationCtrl.AdminCreateNotification) // 批量发送通知
admin.POST("/app/versions/sync", appDownloadCtrl.SyncVersion) // App 下载页 — 版本同步
}
// 人像抠图(镭射卡 thinking 阶段;密钥仅服务端)
v1.POST("/segment", middleware.AuthMiddleware(), segmentCtrl.Portrait)
// 人像抠图 — Dify 工作流调用内网专用JSON body { image_url, scene },无需 JWT
v1.POST("/segment/json", segmentCtrl.PortraitByURL)
// App 下载页 — HTML 分享页使用(公开,无需认证)
v1.GET("/app/download-urls", appDownloadCtrl.GetDownloadUrls)
// 镭射卡 AI 生成Dify 工作流编排)
laser := v1.Group("/laser")
laser.Use(middleware.AuthMiddleware())
{
laser.POST("/generate", laserGenCtrl.CreateGenerateJob)
laser.GET("/generate/:id", laserGenCtrl.GetGenerateJob)
laser.POST("/compose", composeCtrl.ComposeSingle)
}
// 资产相关路由(需要认证)
assets := v1.Group("/assets")
assets.Use(middleware.AuthMiddleware())
{
assets.GET("/oss/signature", assetCtrl.GetOSSUploadSignature) // 获取 OSS 上传签名(兼容旧接口)
assets.GET("/oss/upload-signature", assetCtrl.GetOSSUploadSignature) // 获取 OSS 上传签名(阶段一接口)
assets.GET("/oss/presigned-url", assetCtrl.GetOSSPresignedURL) // 获取 OSS 预签名URL用于读取单个文件
assets.GET("/oss/batch-presigned-urls", assetCtrl.GetOSSBatchPresignedURLs) // 批量获取 OSS 预签名URL用于读取目录下的图片
assets.POST("/upload", assetCtrl.UploadImage) // 上传图片
assets.POST("/mints/precreate", assetCtrl.PreCreateMintOrder) // 阶段一:预创建铸造订单(返回 order_id
assets.POST("/mints/cost-estimate", assetCtrl.EstimateMintCost) // 估算铸造费用(显示消耗和余额)
assets.POST("/mints", assetCtrl.CreateMintOrder) // 创建铸造订单
assets.GET("/mints/:order_id", assetCtrl.GetMintOrder) // 查询铸造订单状态
assets.DELETE("/mints/:order_id", assetCtrl.CancelMintOrder) // 取消铸造订单
assets.POST("/mints/image/generation", assetCtrl.ImageGeneration) // 图生图
assets.GET("/me/earnings-summary", assetCtrl.GetEarningsSummary) // 获取收益汇总
assets.GET("/me/items", assetCtrl.GetMyAssets) // 获取我的藏品列表
assets.GET("/:asset_id", assetCtrl.GetAsset) // 获取资产详情
assets.GET("/:asset_id/status", assetCtrl.GetAssetStatus) // 查询上链状态
// 素材管理
materials := assets.Group("/materials")
materials.POST("/upload", assetCtrl.UploadMaterial)
assets.POST("/:asset_id/materials", assetCtrl.BindAssetMaterials)
assets.GET("/:asset_id/materials", assetCtrl.GetAssetMaterials) // 获取资产素材列表(修复:原 handler 存在但未注册)
// 周边加入藏品(强制 JWT)
// 周边加入藏品 mint 用 /peripheral-info/mint-by-hash/:hash(详见 design §5.1)
}
// 分享相关路由(需要认证)— 用 assetCtrl(spec 规定 /api/v1/share/* 前缀)
share := v1.Group("/share")
share.Use(middleware.AuthMiddleware())
{
share.GET("/asset-qrcode/:assetId", assetCtrl.GetAssetQrcode)
share.POST("/track", assetCtrl.TrackShare)
}
// 举报与反馈spec §5.1 客户端路由,需要认证)
moderation := v1.Group("/moderation")
moderation.Use(middleware.AuthMiddleware())
{
moderation.GET("/report-categories", modCtrl.GetReportCategories)
moderation.GET("/feedback-categories", modCtrl.GetFeedbackCategories)
moderation.POST("/reports", modCtrl.SubmitReport)
moderation.GET("/reports", modCtrl.ListMyReports)
moderation.GET("/reports/:id", modCtrl.GetReportDetail)
moderation.POST("/feedbacks", modCtrl.SubmitFeedback)
moderation.GET("/feedbacks", modCtrl.ListMyFeedbacks)
moderation.GET("/feedbacks/:id", modCtrl.GetFeedbackDetail)
}
// 展馆相关路由(需要认证)
galleries := v1.Group("/galleries")
galleries.Use(middleware.AuthMiddleware())
{
galleries.GET("/me", galleryCtrl.GetMyGallery) // 获取我的展馆
galleries.GET("/random", galleryCtrl.GetRandomGallery) // 获取随机玩家展馆(静态路由必须在参数路由之前)
galleries.GET("/:target_uid", galleryCtrl.GetUserGallery) // 获取他人展馆
galleries.POST("/place", galleryCtrl.PlaceAsset) // 在展位展示藏品
galleries.DELETE("/slots/:slot_id/asset", galleryCtrl.RemoveFromSlot) // 从展位移除资产(统一接口)
galleries.POST("/slots_unlock", galleryCtrl.UnlockSlot) // 解锁/购买新展位
}
// 灵感瀑布相关路由(需要认证)
inspirationFlow := v1.Group("/inspiration-flow")
inspirationFlow.Use(middleware.AuthMiddleware())
{
inspirationFlow.GET("", galleryCtrl.GetInspirationFlow) // 获取灵感瀑布藏品列表
}
// 我的展馆路由(需要认证)
mygalleries := v1.Group("/mygalleries")
mygalleries.Use(middleware.AuthMiddleware())
{
mygalleries.GET("", galleryCtrl.GetMyGallery) // 获取我的展馆
}
// 排行榜相关路由(需要认证)
rankings := v1.Group("/rankings")
rankings.Use(middleware.AuthMiddleware())
{
rankings.GET("/hot", rankingCtrl.GetHotRanking) // 获取热度排行榜
rankings.GET("/original", rankingCtrl.GetOriginalRanking) // 获取自制排行榜
}
// 铸爱工艺配置(需要认证,强制 JWT
// 流程gateway → Dubbo RPC → assetService端口 20003→ 5s 进程内缓存 → DB
// 后台直连写库,本服务只读
castlove := v1.Group("/castlove")
castlove.Use(middleware.AuthMiddleware())
{
castlove.GET("/config", castloveConfigCtrl.GetCastloveConfig) // 获取分类+卡片配置
}
// 活动相关路由(需要认证)
activities := v1.Group("/activities")
activities.Use(middleware.AuthMiddleware())
{
activities.GET("", activityCtrl.GetActivityList) // 获取活动列表
activities.GET("/:id", activityCtrl.GetActivity) // 获取活动详情
activities.GET("/:id/items", activityCtrl.GetActivityItems) // 获取活动道具列表
activities.GET("/:id/progress", activityCtrl.GetProgress) // 获取活动进度
activities.POST("/:id/purchase", activityCtrl.PurchaseItem) // 购买道具
activities.POST("/:id/batch-purchase", activityCtrl.BatchPurchaseItem) // 批量购买道具
activities.GET("/:id/ranking", activityCtrl.GetContributionRanking) // 获取贡献点排名
activities.GET("/:id/top-ranking", activityCtrl.GetTopRanking) // 获取活动 TOP3 + 我的排名(专用轻量接口)
activities.GET("/:id/contributions/latest", activityCtrl.GetLatestContributions) // 获取最新贡献记录
activities.GET("/:id/messages", activityCtrl.ListActivityMessages) // 获取活动留言列表
activities.POST("/:id/messages", activityCtrl.CreateActivityMessage) // 发送一条活动留言
}
// 铸造活动相关路由运营banner- 公开接口,不需要认证
mintingActivities := v1.Group("/minting-activities")
{
mintingActivities.GET("", activityCtrl.GetMintingActivities) // 获取铸造活动列表
}
// 任务相关路由(需要认证)
tasks := v1.Group("/tasks")
tasks.Use(middleware.AuthMiddleware())
{
tasks.GET("/daily", taskCtrl.GetDailyTasks) // 获取每日任务列表
tasks.POST("/report-event", taskCtrl.ReportEvent) // 上报任务事件
tasks.POST("/daily/claim", taskCtrl.ClaimDailyTask) // 领取单个每日任务
tasks.POST("/daily/claim-all", taskCtrl.ClaimAllDailyTasks) // 一键领取所有每日任务
tasks.POST("/guide/complete", taskCtrl.CompleteGuide) // 完成引导任务
tasks.GET("/onboarding/status", taskCtrl.GetOnboardingStatus) // 获取引导状态
tasks.POST("/onboarding/advance-stage", taskCtrl.AdvanceStage) // 进入下一引导阶段
tasks.POST("/onboarding/claim-reward", taskCtrl.ClaimOnboardingReward) // 领取引导阶段奖励
tasks.GET("/exhibition-revenue", taskCtrl.GetExhibitionRevenue) // 获取展示收益
tasks.POST("/exhibition-revenue/claim", taskCtrl.ClaimExhibitionRevenue) // 领取单条展示收益
tasks.POST("/exhibition-revenue/claim-all", taskCtrl.ClaimAllExhibitionRevenue) // 一键领取所有展示收益
tasks.GET("/like-bet-revenue", taskCtrl.GetLikeBetRevenue) // 获取点赞押注收益
tasks.POST("/like-bet-revenue/claim", taskCtrl.ClaimLikeBetRevenue) // 领取单条点赞押注收益
tasks.POST("/like-bet-revenue/claim-all", taskCtrl.ClaimAllLikeBetRevenue) // 一键领取所有点赞押注收益
}
// ★ 批次 4.2:星册相关路由已删除(starbookService 孤儿代码、生产从未部署)。
// 原 /api/v1/starbook/home、/api/v1/starbook/items 由 assetService.GetMyAssets 取代。
//
// ★ starbook-recovery:批次 4.2 的"由 GetMyAssets 取代"实际未补 gateway 路由,
// 前端 GET /api/v1/starbook/home 和 /starbook/items 持续 404。
// 恢复 gateway → assetService 直连薄壳(参见 starbook_controller.go)。
// 星册相关路由(需要认证)
starbook := v1.Group("/starbook")
starbook.Use(middleware.AuthMiddleware())
{
starbook.GET("/home", starbookCtrl.GetStarbookHome) // 获取星册首页
starbook.GET("/items", starbookCtrl.GetStarbookItems) // 获取星册藏品列表(分页)
}
// AI Chat 相关路由(需要认证)
aiChat := v1.Group("/ai-chat")
aiChat.Use(middleware.AuthMiddleware())
{
aiChat.GET("/personas", aiChatCtrl.GetPersonas) // 获取人设列表
aiChat.GET("/history/:sessionId", aiChatCtrl.GetHistory) // 获取对话历史
}
}
return r, nil
}
// AssetController exposes the asset controller constructed by SetupRouter so that
// main.go can start background consumers (e.g. mintSuccessCh) on it.
// Exposed as a package-level var rather than extending the SetupRouter return
// tuple, which would force cascade updates to every main_test.go and any caller
// in other services.
var AssetController *controller.AssetController