chore(init): 初始化解忧商行项目并完成 Phase 0 基座
This commit is contained in:
@@ -0,0 +1,380 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user