feat(invitation): 支持分销站点自主配置返利比例与权限隔离
- 后端新增分站专属配置及审计接口(/api/distribution/invitation-config 等),基于分站管理员严格鉴权隔离 - 分销独立后台新增「邀请返利设置」页面(Invitations.tsx),支持分站管理员自主开启/调整返利比例并查看本站流水 - 主站后台邀请接口路径补齐 /admin 前缀,解决 404/401 鉴权作用域问题 - 验证方式:backend 单元测试全数通过,distribution-admin 类型与构建检查通过
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package invitation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
@@ -13,14 +14,23 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type SiteScoper interface {
|
||||
ScopeSiteID(ctx context.Context, adminID, requested uint64) (uint64, error)
|
||||
}
|
||||
|
||||
type Handler struct {
|
||||
service *Service
|
||||
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"))
|
||||
@@ -225,3 +235,131 @@ func writeInvitationError(c *gin.Context, err error) {
|
||||
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user