0

我有一个应用程序,用户可以在其中获得游戏积分。我的代码中有一个错误,该错误旨在按 Game_Type 对过去一周的每个用户的积分进行小计和排名(在 GameTypeRank 表中)。如果用户在过去一周内有积分,这可以正常工作,但是,如果用户在过去一周内没有积分,则无法正常工作。如果用户没有积分,他们应该在 GameTypeRank 中更新为 0 分,否则,他们保留最后的排名直到更新(并且排名不正确)。

# user.rb:
def self.update_game_type_weekly_rank
  @game_types = GameType.all
  @game_types.each do |game_type|

  # this query is where the bug is since it is possible a User has no Points  
  @user_with_points = Point.where("game_type_id = ? and created_at >= ?", game_type.id, 1.week.ago).sum(:points, :group => :user_id, :order => 'sum(points) desc')
    rank = point_counter = 0

    @user_with_points.each do |user_id, points|
      @game_type_rank = GameTypeRank.find_or_create_by_user_id_and_game_type_id(user_id, game_type.id)
      if points != point_counter
        point_counter = points
        rank += 1
      end
      @game_type_rank.weekly_rank = rank
      @game_type_rank.weekly_points = points
      @game_type_rank.save
    end
  end
end

# Models

# game_type_rank.rb
# fields - user_id, game_type_id, weekly_points, weekly_rank
belongs_to :game_type
belongs_to :user

# point.rb
# fields - user_id, points, game_type_id
belongs_to :game
belongs_to :game_type
belongs_to :user

我可以创建一个每周运行的方法,并为每周没有任何记录的每个用户创建点记录(Point.points = 0),但这是一个糟糕的解决方案。

我也可以在方法开始时为每个 GameTypeRank 记录初始化weekly_pointsand weekly_rank,但这对我来说似乎效率低下(而且我不确定最好的方法)。

# Sample Data
Points
|user_id|points|game_type_id|created_at
|1      |   10 |           1|2013-05-07
|1      |   10 |           2|2013-05-07
|2      |   20 |           2|2012-12-31 
|1      |   5  |           2|2012-12-31 

before weekly_update - Game_Type_Ranks 
|user_id |game_type_id | weekly_points | weekly_rank|
|1       |2            |5              |2
|2       |2            |20             |1

after weekly_update - Game_Type_Ranks 
|user_id |game_type_id | weekly_points | weekly_rank|
|1       |1            |10             |1
|1       |2            |10             |1
|2       |2            |20             |1

what should happen after weekly_update - Game_Type_Ranks
|user_id |game_type_id | weekly_points | weekly_rank|
|1       |1            |10             |1
|1       |2            |10             |1
|2       |2            |0              |2   <== update for user_id 2
4

1 回答 1

0

主要问题是您无权访问具有现有排名但在@user_with_points给定的当前周内不活跃的用户GameType

为什么不保持原样,而是利用updated_at时间戳。调用您的GameTypeRank对象时,只需拉出上周更新的对象。

# some controller
GameTypeRank.where("id = ? and updated_at >= ?", id, 1.week.ago)

然而,这样你就失去了将老用户每周点数设置为 0 的能力。

另一种方法是循环遍历所有拥有给定 GameType 积分的用户,而不考虑时间,如下所示:

# user.rb:
def self.update_game_type_weekly_rank
  @game_types = GameType.all
  @game_types.each do |game_type|

    @user_with_points = Point.where("game_type_id = ?", game_type.id).sum(:points, :group => :user_id, :order => 'sum(points) desc')
      rank = point_counter = 0
      @user_with_points.each do |user_id, points|
        @game_type_rank = GameTypeRank.find_or_create_by_user_id_and_game_type_id(user_id, game_type.id)
        if points != point_counter
          point_counter = points
          rank += 1
        end
        @game_type_rank.weekly_rank = rank
        @game_type_rank.weekly_points = points
        @game_type_rank.save
      end
    end

  end
end

在此之后,您的桌子应该是您想要的方式。现在,正如您可能意识到的那样,这是有代价的。也许你可以从不活动中得到某种切断。比如说6个月。

# users.rb
....
@user_with_points = Point.where("game_type_id = ? and created_at >= ?", game_type.id, 6.months.ago).sum(:points, :group => :user_id, :order => 'sum(points) desc')
....

# some controller
GameTypeRank.where("id = ? and created_at >= ?", id, 6.months.ago)

这两种方法都有一些效率低下,但可以完成工作。另外,您可能正在使用 cron 作业和/或后台作业运行此方法,因此对您的用户的影响应该为零。

于 2013-05-08T17:02:11.627 回答