package invitation import ( "context" "errors" "net/http" "strconv" "strings" "hfb_sys/backend/internal/middleware" "hfb_sys/backend/internal/sitecode" "hfb_sys/backend/pkg/response" "github.com/gin-gonic/gin" ) type SiteScoper interface { ScopeSiteID(ctx context.Context, adminID, requested uint64) (uint64, error) } type Handler struct { service *Service siteScoper SiteScoper } func NewHandler(service *Service) *Handler { return &Handler{service: service} } func (h *Handler) SetSiteScoper(scoper SiteScoper) { h.siteScoper = scoper } // GetInviteInfo 公开接口:根据邀请码查询邀请人简要信息(用于落地注册页) func (h *Handler) GetInviteInfo(c *gin.Context) { code := strings.TrimSpace(c.Param("code")) if code == "" { code = strings.TrimSpace(c.Query("code")) } info, err := h.service.GetInviteCodeInfo(c.Request.Context(), code) if err != nil { writeInvitationError(c, err) return } response.OK(c, info) } // UserSummary 个人中心邀请返利概览 func (h *Handler) UserSummary(c *gin.Context) { userID, ok := currentUserID(c) if !ok { response.Unauthorized(c, "缺少用户上下文") return } siteID := parseSiteID(c) summary, err := h.service.GetUserSummary(c.Request.Context(), userID, siteID) if err != nil { writeInvitationError(c, err) return } response.OK(c, summary) } // ListMyInvitedUsers 我的邀请好友列表 func (h *Handler) ListMyInvitedUsers(c *gin.Context) { userID, ok := currentUserID(c) if !ok { response.Unauthorized(c, "缺少用户上下文") return } page, pageSize := parsePagination(c) items, total, err := h.service.ListMyInvitedUsers(c.Request.Context(), userID, page, pageSize) if err != nil { writeInvitationError(c, err) return } response.OK(c, gin.H{ "items": items, "total": total, "page": page, "page_size": pageSize, }) } // ListMyRebates 我的返利流水 func (h *Handler) ListMyRebates(c *gin.Context) { userID, ok := currentUserID(c) if !ok { response.Unauthorized(c, "缺少用户上下文") return } page, pageSize := parsePagination(c) items, total, err := h.service.ListMyRebates(c.Request.Context(), userID, page, pageSize) if err != nil { writeInvitationError(c, err) return } response.OK(c, gin.H{ "items": items, "total": total, "page": page, "page_size": pageSize, }) } // AdminGetConfig 管理员获取返利比例配置 func (h *Handler) AdminGetConfig(c *gin.Context) { siteID := parseAdminSiteID(c) cfg, err := h.service.GetRebateConfig(c.Request.Context(), siteID) if err != nil { writeInvitationError(c, err) return } response.OK(c, cfg) } // AdminUpdateConfig 管理员更新返利比例配置 func (h *Handler) AdminUpdateConfig(c *gin.Context) { siteID := parseAdminSiteID(c) var req UpdateConfigRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, "比例参数格式不正确,必须在0到1之间") return } cfg, err := h.service.UpdateRebateConfig(c.Request.Context(), siteID, req) if err != nil { writeInvitationError(c, err) return } response.OK(c, cfg) } // AdminInvitations 管理员查询全量邀请关系 func (h *Handler) AdminInvitations(c *gin.Context) { var query AdminInvitationQuery if err := c.ShouldBindQuery(&query); err != nil { response.BadRequest(c, "查询参数不正确") return } items, total, err := h.service.ListAdminInvitations(c.Request.Context(), query) if err != nil { writeInvitationError(c, err) return } response.OK(c, gin.H{ "items": items, "total": total, "page": query.Page, "page_size": query.PageSize, }) } // AdminRebates 管理员查询全量返利流水 func (h *Handler) AdminRebates(c *gin.Context) { var query AdminRebateLedgerQuery if err := c.ShouldBindQuery(&query); err != nil { response.BadRequest(c, "查询参数不正确") return } items, total, err := h.service.ListAdminRebateLedgers(c.Request.Context(), query) if err != nil { writeInvitationError(c, err) return } response.OK(c, gin.H{ "items": items, "total": total, "page": query.Page, "page_size": query.PageSize, }) } // AdminRebateSummary 管理员查看返利统计汇总 func (h *Handler) AdminRebateSummary(c *gin.Context) { var query AdminRebateLedgerQuery if err := c.ShouldBindQuery(&query); err != nil { response.BadRequest(c, "查询参数不正确") return } summary, err := h.service.AdminRebateSummary(c.Request.Context(), query) if err != nil { writeInvitationError(c, err) return } response.OK(c, summary) } func currentUserID(c *gin.Context) (uint64, bool) { value, ok := c.Get(middleware.ContextUserID) if !ok { return 0, false } userID, ok := value.(uint64) return userID, ok } func parsePagination(c *gin.Context) (int, int) { page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) if page < 1 { page = 1 } if pageSize < 1 { pageSize = 20 } if pageSize > 100 { pageSize = 100 } return page, pageSize } func parseSiteID(c *gin.Context) uint64 { siteCode := sitecode.FromRequest(c) if strings.TrimSpace(siteCode) == "" { return 0 } // 可从上下文或按 siteCode 获取 siteID return 0 } func parseAdminSiteID(c *gin.Context) uint64 { raw := c.Query("distribution_site_id") if raw == "" { raw = c.Param("id") } if raw == "" { raw = c.Param("site_id") } if raw != "" { id, _ := strconv.ParseUint(raw, 10, 64) return id } return 0 } func writeInvitationError(c *gin.Context, err error) { response.RecordError(c, err) switch { case errors.Is(err, ErrDependencyUnavailable): response.ServiceUnavailable(c, "数据库服务不可用") case errors.Is(err, ErrInvalidInviteCode): response.BadRequest(c, "邀请码不存在或无效") case errors.Is(err, ErrSelfInviteNotAllowed): response.BadRequest(c, "不能自己邀请自己") case errors.Is(err, ErrAlreadyInvited): response.BadRequest(c, "用户已绑定过邀请人") case errors.Is(err, ErrInviterNotVerified): response.BadRequest(c, "邀请人尚未完成实名认证") default: response.Error(c, http.StatusInternalServerError, "invitation_error", err.Error()) } } // DistributionGetConfig 分销端获取本站邀请返利比例配置 func (h *Handler) DistributionGetConfig(c *gin.Context) { siteID, err := h.resolveDistributionSiteID(c) if err != nil { response.Forbidden(c, "无权访问该分销站点") return } cfg, err := h.service.GetRebateConfig(c.Request.Context(), siteID) if err != nil { writeInvitationError(c, err) return } response.OK(c, cfg) } // DistributionUpdateConfig 分销端更新本站邀请返利比例配置 func (h *Handler) DistributionUpdateConfig(c *gin.Context) { siteID, err := h.resolveDistributionSiteID(c) if err != nil { response.Forbidden(c, "无权操作该分销站点") return } if siteID == 0 { response.BadRequest(c, "未指定有效分销站点") return } var req UpdateConfigRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, "比例参数格式不正确,必须在0到1之间") return } cfg, err := h.service.UpdateRebateConfig(c.Request.Context(), siteID, req) if err != nil { writeInvitationError(c, err) return } response.OK(c, cfg) } // DistributionListInvitations 分销端查看本站邀请关系 func (h *Handler) DistributionListInvitations(c *gin.Context) { siteID, err := h.resolveDistributionSiteID(c) if err != nil { response.Forbidden(c, "无权访问该分销站点") return } var query AdminInvitationQuery if err := c.ShouldBindQuery(&query); err != nil { response.BadRequest(c, "查询参数不正确") return } query.DistributionSiteID = &siteID items, total, err := h.service.ListAdminInvitations(c.Request.Context(), query) if err != nil { writeInvitationError(c, err) return } response.OK(c, gin.H{ "items": items, "total": total, "page": query.Page, "page_size": query.PageSize, }) } // DistributionListRebates 分销端查看本站返利支出流水 func (h *Handler) DistributionListRebates(c *gin.Context) { siteID, err := h.resolveDistributionSiteID(c) if err != nil { response.Forbidden(c, "无权访问该分销站点") return } var query AdminRebateLedgerQuery if err := c.ShouldBindQuery(&query); err != nil { response.BadRequest(c, "查询参数不正确") return } query.DistributionSiteID = &siteID items, total, err := h.service.ListAdminRebateLedgers(c.Request.Context(), query) if err != nil { writeInvitationError(c, err) return } response.OK(c, gin.H{ "items": items, "total": total, "page": query.Page, "page_size": query.PageSize, }) } func (h *Handler) resolveDistributionSiteID(c *gin.Context) (uint64, error) { adminID, _ := currentAdminID(c) var requested uint64 if raw := c.Param("id"); raw != "" { if id, err := strconv.ParseUint(raw, 10, 64); err == nil && id > 0 { requested = id } } if requested == 0 { if raw := c.Query("site_id"); raw != "" { if id, err := strconv.ParseUint(raw, 10, 64); err == nil && id > 0 { requested = id } } } if requested == 0 { if raw := c.Query("distribution_site_id"); raw != "" { if id, err := strconv.ParseUint(raw, 10, 64); err == nil && id > 0 { requested = id } } } if h.siteScoper != nil { return h.siteScoper.ScopeSiteID(c.Request.Context(), adminID, requested) } return requested, nil } func currentAdminID(c *gin.Context) (uint64, bool) { value, ok := c.Get(middleware.ContextAdminID) if !ok { return 0, false } adminID, ok := value.(uint64) return adminID, ok }