package srv_viporder import ( "context" "strings" "time" "videoweb/pub/common/srvdef" "videoweb/pub/models/usermod" "videoweb/pub/models/vipordermod" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo/options" ) // GetVipOrders 分页查询会员购买记录(固定 OrderType=会员) func GetVipOrders(ctx context.Context, req *VipOrderReq) (*VipOrderResp, *srvdef.SrvErr) { orderType := vipordermod.OrderTypeVip reqCopy := *req reqCopy.OrderType = &orderType return GetOrders(ctx, &reqCopy) } // GetOrders 分页查询充值订单,支持按 OrderType 筛选(不传则查全部类型) func GetOrders(ctx context.Context, req *VipOrderReq) (*VipOrderResp, *srvdef.SrvErr) { filter := bson.M{} if req.OrderType != nil { filter[vipordermod.FN.VipOrder.OrderType] = *req.OrderType } if req.UserId != nil && *req.UserId > 0 { filter[vipordermod.FN.VipOrder.UserId] = *req.UserId } if req.Status != nil { filter[vipordermod.FN.VipOrder.Status] = *req.Status } if req.TradeNo != nil && *req.TradeNo != "" { filter[vipordermod.FN.VipOrder.TradeNo] = *req.TradeNo } if districtCode := strings.TrimSpace(req.DistrictCode); districtCode != "" { filter[vipordermod.FN.VipOrder.DistrictCode] = districtCode } timeFilter := bson.M{} if req.StartTime != nil && *req.StartTime != "" { if t, ok := parseTime(*req.StartTime); ok { timeFilter["$gte"] = t } } if req.EndTime != nil && *req.EndTime != "" { if t, ok := parseTime(*req.EndTime); ok { timeFilter["$lte"] = t } } if len(timeFilter) > 0 { filter[vipordermod.FN.VipOrder.CreatedAt] = timeFilter } total, serr := vipordermod.CountVipOrder(ctx, filter) if serr != nil { return nil, serr } resp := &VipOrderResp{Total: total} if total == 0 { resp.List = vipordermod.VipOrderList{} return resp, nil } opt := options.Find(). SetSort(bson.D{{Key: vipordermod.FN.VipOrder.CreatedAt, Value: -1}}). SetSkip((req.PageNum - 1) * req.PageSize). SetLimit(req.PageSize) list, serr := vipordermod.FindManyVipOrder(ctx, filter, opt) if serr != nil { return nil, serr } resp.List = list return resp, nil } const ( syncDistrictDefaultSize = int64(500) syncDistrictMaxSize = int64(2000) syncDistrictBatchSize = int64(200) ) // SyncDistrictCode 将历史订单中空的渠道码,按用户当前 districtCode 回填。 func SyncDistrictCode(ctx context.Context, req *SyncDistrictCodeReq) (*SyncDistrictCodeResp, *srvdef.SrvErr) { maxSize := syncDistrictDefaultSize if req != nil && req.MaxSize > 0 { maxSize = req.MaxSize } if maxSize > syncDistrictMaxSize { maxSize = syncDistrictMaxSize } resp := &SyncDistrictCodeResp{} lastId := int64(0) updatedUsers := make(map[int64]struct{}) skippedUsers := make(map[int64]struct{}) for resp.Scanned < maxSize { limit := syncDistrictBatchSize if remain := maxSize - resp.Scanned; remain < limit { limit = remain } filter := emptyDistrictSyncFilter(lastId, skippedUsers) list, serr := vipordermod.FindManyVipOrder(ctx, filter, options.Find(). SetProjection(bson.D{ {Key: vipordermod.FN.VipOrder.Id, Value: 1}, {Key: vipordermod.FN.VipOrder.UserId, Value: 1}, }). SetSort(bson.D{{Key: vipordermod.FN.VipOrder.Id, Value: 1}}). SetLimit(limit)) if serr != nil { return nil, serr } if len(list) == 0 { break } resp.Scanned += int64(len(list)) lastId = list[len(list)-1].Id if int64(len(list)) == limit && resp.Scanned >= maxSize { resp.HasMore = true } userIds := make([]int64, 0, len(list)) seen := make(map[int64]struct{}, len(list)) for _, order := range list { if order == nil || order.UserId <= 0 { continue } if _, ok := seen[order.UserId]; ok { continue } seen[order.UserId] = struct{}{} userIds = append(userIds, order.UserId) } if len(userIds) == 0 { continue } users, serr := usermod.FindManyUser(ctx, bson.M{ usermod.FN.User.Id: bson.M{"$in": userIds}, }, options.Find().SetProjection(bson.D{ {Key: usermod.FN.User.Id, Value: 1}, {Key: usermod.FN.User.DistrictCode, Value: 1}, })) if serr != nil { return nil, serr } userMap := make(map[int64]*usermod.User, len(users)) for _, user := range users { if user != nil { userMap[user.Id] = user } } now := time.Now() for _, userId := range userIds { user := userMap[userId] code := "" if user != nil { code = strings.TrimSpace(user.DistrictCode) } if code == "" { skippedUsers[userId] = struct{}{} continue } modified, serr := vipordermod.UpdateManyVipOrder(ctx, mergeEmptyDistrictFilter(bson.M{vipordermod.FN.VipOrder.UserId: userId}), bson.M{"$set": bson.M{ vipordermod.FN.VipOrder.DistrictCode: code, vipordermod.FN.VipOrder.UpdatedAt: now, }}, ) if serr != nil { return nil, serr } if modified > 0 { updatedUsers[userId] = struct{}{} resp.OrderUpdated += modified } } } resp.UserUpdated = int64(len(updatedUsers)) resp.UserSkipped = int64(len(skippedUsers)) if !resp.HasMore { remain, serr := vipordermod.FindManyVipOrder(ctx, emptyDistrictSyncFilter(lastId, skippedUsers), options.Find().SetProjection(bson.D{{Key: vipordermod.FN.VipOrder.Id, Value: 1}}).SetLimit(1)) if serr != nil { return nil, serr } resp.HasMore = len(remain) > 0 } return resp, nil } func emptyDistrictSyncFilter(lastId int64, skippedUsers map[int64]struct{}) bson.M { conds := []bson.M{vipordermod.EmptyDistrictFilter()} if lastId > 0 { conds = append(conds, bson.M{vipordermod.FN.VipOrder.Id: bson.M{"$gt": lastId}}) } if skipIds := mapKeys(skippedUsers); len(skipIds) > 0 { conds = append(conds, bson.M{vipordermod.FN.VipOrder.UserId: bson.M{"$nin": skipIds}}) } if len(conds) == 1 { return conds[0] } return bson.M{"$and": conds} } func mergeEmptyDistrictFilter(extra bson.M) bson.M { filter := vipordermod.EmptyDistrictFilter() if len(extra) == 0 { return filter } conds := []bson.M{filter} for key, value := range extra { conds = append(conds, bson.M{key: value}) } return bson.M{"$and": conds} } func mapKeys(m map[int64]struct{}) []int64 { if len(m) == 0 { return nil } keys := make([]int64, 0, len(m)) for id := range m { keys = append(keys, id) } return keys }