package srv_viporder import ( "context" "strings" "time" "videoweb/pub/common/srvdef" "videoweb/pub/common/utils" "videoweb/pub/models/vipordermod" "videoweb/pub/pubdef" "go.mongodb.org/mongo-driver/bson" ) // RechargeSummaryReq 充值汇总请求,时间按北京时间解析。 type RechargeSummaryReq struct { StartTime string `form:"startTime" json:"startTime"` // 开始时间,北京时间:2006-01-02 / 2006-01-02 15:04:05 / RFC3339 EndTime string `form:"endTime" json:"endTime"` // 结束时间,北京时间:2006-01-02 / 2006-01-02 15:04:05 / RFC3339 } // RechargeSummaryResp 充值汇总结果,金额单位为分。 type RechargeSummaryResp struct { StartTime string `json:"startTime"` // 实际统计开始时间(北京时间) EndTime string `json:"endTime"` // 实际统计结束时间(北京时间) OrderCount int64 `json:"orderCount"` // 成功订单数 TotalAmount int64 `json:"totalAmount"` // 成功订单金额合计(分) VipOrderCount int64 `json:"vipOrderCount"` // 会员成功订单数 VipAmount int64 `json:"vipAmount"` // 会员成功订单金额(分) CoinOrderCount int64 `json:"coinOrderCount"` // 金币成功订单数 CoinAmount int64 `json:"coinAmount"` // 金币成功订单金额(分) UserCount int64 `json:"userCount"` // 成功充值用户数(去重) } type rechargeSummaryAgg struct { OrderCount int64 `bson:"orderCount"` TotalAmount int64 `bson:"totalAmount"` VipOrderCount int64 `bson:"vipOrderCount"` VipAmount int64 `bson:"vipAmount"` CoinOrderCount int64 `bson:"coinOrderCount"` CoinAmount int64 `bson:"coinAmount"` UserCount int64 `bson:"userCount"` } // GetRechargeSummary 按支付完成时间汇总成功充值订单。 func GetRechargeSummary(ctx context.Context, req *RechargeSummaryReq) (*RechargeSummaryResp, *srvdef.SrvErr) { if req == nil { return nil, srvdef.New(srvdef.ErrParamError, "参数不能为空") } start, end, err := parseBeijingTimeRange(req.StartTime, req.EndTime) if err != nil { return nil, err } resp := &RechargeSummaryResp{ StartTime: start.In(utils.LocBJ).Format(pubdef.TimeISO), EndTime: end.In(utils.LocBJ).Format(pubdef.TimeISO), } list := make([]rechargeSummaryAgg, 0) if serr := vipordermod.AggregateVipOrder(ctx, &list, rechargeSummaryPipeline(start, end)); serr != nil { return nil, serr } if len(list) == 0 { return resp, nil } item := list[0] resp.OrderCount = item.OrderCount resp.TotalAmount = item.TotalAmount resp.VipOrderCount = item.VipOrderCount resp.VipAmount = item.VipAmount resp.CoinOrderCount = item.CoinOrderCount resp.CoinAmount = item.CoinAmount resp.UserCount = item.UserCount return resp, nil } func rechargeSummaryPipeline(start, end time.Time) []bson.M { vipType := bson.M{"$eq": bson.A{bson.M{"$ifNull": bson.A{"$" + vipordermod.FN.VipOrder.OrderType, vipordermod.OrderTypeVip}}, vipordermod.OrderTypeVip}} return []bson.M{ { "$match": bson.M{ vipordermod.FN.VipOrder.Status: pubdef.VipOrderSuccess, vipordermod.FN.VipOrder.FinishedAt: bson.M{ "$gte": start, "$lte": end, }, }, }, { "$group": bson.M{ "_id": nil, "orderCount": bson.M{"$sum": 1}, "totalAmount": bson.M{"$sum": "$" + vipordermod.FN.VipOrder.Amount}, "vipOrderCount": bson.M{"$sum": bson.M{"$cond": bson.A{vipType, 1, 0}}}, "vipAmount": bson.M{"$sum": bson.M{"$cond": bson.A{ vipType, "$" + vipordermod.FN.VipOrder.Amount, 0, }}}, "coinOrderCount": bson.M{"$sum": bson.M{"$cond": bson.A{ bson.M{"$eq": bson.A{"$" + vipordermod.FN.VipOrder.OrderType, vipordermod.OrderTypeCoin}}, 1, 0, }}}, "coinAmount": bson.M{"$sum": bson.M{"$cond": bson.A{ bson.M{"$eq": bson.A{"$" + vipordermod.FN.VipOrder.OrderType, vipordermod.OrderTypeCoin}}, "$" + vipordermod.FN.VipOrder.Amount, 0, }}}, "userIds": bson.M{"$addToSet": "$" + vipordermod.FN.VipOrder.UserId}, }, }, { "$project": bson.M{ "_id": 0, "orderCount": 1, "totalAmount": 1, "vipOrderCount": 1, "vipAmount": 1, "coinOrderCount": 1, "coinAmount": 1, "userCount": bson.M{"$size": "$userIds"}, }, }, } } func parseBeijingTimeRange(startRaw, endRaw string) (time.Time, time.Time, *srvdef.SrvErr) { start, ok := parseBeijingTime(startRaw, true) if !ok { return time.Time{}, time.Time{}, srvdef.New(srvdef.ErrParamError, "开始时间无效") } end, ok := parseBeijingTime(endRaw, false) if !ok { return time.Time{}, time.Time{}, srvdef.New(srvdef.ErrParamError, "结束时间无效") } if end.Before(start) { return time.Time{}, time.Time{}, srvdef.New(srvdef.ErrParamError, "结束时间不能早于开始时间") } return start, end, nil } func parseBeijingTime(raw string, isStart bool) (time.Time, bool) { raw = strings.TrimSpace(raw) if raw == "" { return time.Time{}, false } layouts := []string{ pubdef.TimeISO, time.RFC3339, time.RFC3339Nano, "2006-01-02 15:04:05 -0700 MST", "2006-01-02 15:04:05.999999999 -0700 MST", } for _, layout := range layouts { if t, err := time.ParseInLocation(layout, raw, utils.LocBJ); err == nil { return t, true } } if t, err := time.ParseInLocation(pubdef.DateISO, raw, utils.LocBJ); err == nil { if isStart { return utils.BeginOfTime(t), true } return utils.EndOfTime(t), true } return time.Time{}, false }