package order import ( logger "eta/eta_mini_ht_api/common/component/log" "eta/eta_mini_ht_api/common/exception" "eta/eta_mini_ht_api/common/utils/page" "eta/eta_mini_ht_api/controllers" "eta/eta_mini_ht_api/service/facade" "eta/eta_mini_ht_api/service/order" "eta/eta_mini_ht_api/service/product" "eta/eta_mini_ht_api/service/user" "golang.org/x/time/rate" "net/http" "sync" "time" ) type SubscribeController struct { controllers.ListController } var ( // 初始化限流器 orderRateLimiter = NewUserRateLimiter(rate.Every(5*time.Second), 1) orderNoRateLimiter = NewUserRateLimiter(rate.Every(time.Second), 1) ) type UserRateLimiter struct { limiters sync.Map // 存储每个用户的限流器 defaultLimiter *rate.Limiter // 默认限流器 } func NewUserRateLimiter(limit rate.Limit, burst int) *UserRateLimiter { return &UserRateLimiter{ defaultLimiter: rate.NewLimiter(limit, burst), } } func (url *UserRateLimiter) Allow(userID int) bool { if limiter, ok := url.limiters.Load(userID); ok { return limiter.(*rate.Limiter).Allow() } // 创建新的限流器并存储 newLimiter := rate.NewLimiter(url.defaultLimiter.Limit(), url.defaultLimiter.Burst()) url.limiters.Store(userID, newLimiter) return newLimiter.Allow() } func orderRateLimitFilter(userId int) (code int) { if userId <= 0 { code = http.StatusBadRequest return } if !orderRateLimiter.Allow(userId) { code = http.StatusTooManyRequests return } code = http.StatusOK return } func orderNoRateLimitFilter(userId int) (code int) { if userId <= 0 { code = http.StatusBadRequest return } if !orderNoRateLimiter.Allow(userId) { code = http.StatusTooManyRequests return } code = http.StatusOK return } const ( AccountNotOpen = 430 IDExpired = 431 RiskUnTest = 432 RiskNotMatch = 433 RiskExpired = 434 AccountOpening = 435 orderPending = "pending" orderPaid = "paid" orderRefund = "refund" orderClosed = "closed" ) // GetOrderNo 获取订单号 // @Summary 获取订单号 // @Description 获取订单号 // @Success 200 {object} controllers.BaseResponse // @router /orderNo [get] func (sc *SubscribeController) GetOrderNo() { controllers.Wrap(&sc.BaseController, func() (result *controllers.WrapData, err error) { result = sc.InitWrapData("获取订单号失败") userInfo := sc.Data["user"].(user.User) if code := orderNoRateLimitFilter(userInfo.Id); code != 200 { err = exception.New(exception.SubscribeFailed) sc.FailedResult("操作太频繁了,请稍后再试", result) return } orderNo := order.GenerateProductOrderNo() sc.SuccessResult("获取订单号成功", orderNo, result) return }) } // SubscribeProduct 订阅产品 // @Summary 订阅产品 // @Description 订阅产品 // @Success 200 {object} controllers.BaseResponse // @router /subscribe [post] func (sc *SubscribeController) SubscribeProduct() { controllers.Wrap(&sc.BaseController, func() (result *controllers.WrapData, err error) { result = sc.InitWrapData("订阅产品失败") subscribeReq := new(SubscribeRequest) sc.GetPostParams(subscribeReq) var userInfo user.User userInfo = sc.Data["user"].(user.User) if subscribeReq.ProductId <= 0 { err = exception.New(exception.IllegalProductId) sc.FailedResult("订阅产品失败,产品编号非法", result) return } if subscribeReq.OrderNo == "" { err = exception.New(exception.IllegalOrderNo) sc.FailedResult("订阅产品失败,订单编号非法", result) return } if code := orderRateLimitFilter(userInfo.Id); code != 200 { err = exception.New(exception.SubscribeFailed) sc.FailedResult("操作太频繁了,请稍后再试", result) return } //校验是否已经购买并且在有效期内 //是否开户 //未开户 officialUser, err := user.GetUserByTemplateUserId(userInfo.Id) if err != nil { if err.Error() == exception.GetMsg(exception.OfficialUserNotFound) { err = exception.New(exception.OfficialUserNotFound) result.Ret = AccountNotOpen sc.FailedResult("用户未开通账户", result) } else { sc.FailedResult("获取用户账户信息失败", result) } return } switch officialUser.AccountStatus { case "unopen": result.Ret = AccountNotOpen err = exception.New(exception.AccountNotOpen) sc.FailedResult("用户未开通账户", result) return case "opening": result.Ret = AccountOpening err = exception.New(exception.AccountNotOpen) sc.FailedResult("用户开户中", result) return case "opened": default: err = exception.New(exception.IllegalAccountStatus) sc.FailedResult(result.Msg, result) return } //证件信息是否过期 if !officialUser.IDValid { err = exception.New(exception.IDExpired) result.Ret = IDExpired sc.FailedResult("用户证件有效期过期", result) return } //主动发起查询最新风险测评(获取失败用系统中原有的测评信息) code, err := facade.CheckUserRiskLevel(userInfo.Id, subscribeReq.ProductId, officialUser) if err != nil { result.Ret = code sc.FailedResult("校验用户风险等级失败", result) return } //创单 //返回订单信息、商品信息 orderInfo, err := order.CreateProductOrder(userInfo, subscribeReq.ProductId, subscribeReq.OrderNo) if err != nil { if err.Error() == exception.GetMsg(exception.DuplicateSubscribe) { sc.FailedResult("请勿重复订阅", result) return } sc.FailedResult("创建订单失败", result) return } sc.SuccessResult("订阅产品成功", orderInfo, result) return }) } // SubscribeList 用户订阅列表 // @Summary 用户订阅列表 // @Description 用户订阅列表 // @Success 200 {object} controllers.BaseResponse // @router /subscribeList [get] func (sc *SubscribeController) SubscribeList(productType string) { controllers.Wrap(&sc.BaseController, func() (result *controllers.WrapData, err error) { result = sc.InitWrapData("分页查询订阅列表失败") userInfo := sc.Data["user"].(user.User) if productType == "" || !sc.CheckProductType(productType) { err = exception.New(exception.ProductTypeError) sc.FailedResult("分页查询订阅列表失败", result) return } pageRes := page.Page{ Current: sc.PageInfo.Current, PageSize: sc.PageInfo.PageSize, } pageRes.Total, pageRes.LatestId = product.GetTotalUserPageCountByProductType(productType, userInfo.Id) if err != nil { logger.Error("分页查询订阅列表失败:%v", err) sc.FailedResult("分页查询订阅列表失败", result) return } if sc.PageInfo.LatestId == 0 { sc.PageInfo.LatestId = pageRes.LatestId sc.PageInfo.Total = pageRes.Total } else { pageRes.LatestId = sc.PageInfo.LatestId pageRes.Total = sc.PageInfo.Total } pageRes.TotalPage = page.TotalPages(pageRes.Total, pageRes.PageSize) list, err := product.SubscribeList(userInfo.Id, productType, sc.PageInfo) if err != nil { sc.FailedResult("分页查询订阅列表失败", result) return } mediaList := new(page.PageResult) mediaList.Data = list mediaList.Page = pageRes sc.SuccessResult("分页查询订阅列表成功", mediaList, result) return }) } type SubscribeRequest struct { ProductId int `json:"productId"` OrderNo string `json:"orderNo"` }