From 930526535ebfdb94d0e7bc15d13d7b6ad4290542 Mon Sep 17 00:00:00 2001 From: mydearxym Date: Fri, 14 May 2021 10:52:04 +0800 Subject: [PATCH 1/5] refactor: extract collect functions --- lib/groupher_server/cms/cms.ex | 15 +- .../cms/delegates/article_collect.ex | 216 ++++++++++++++++++ 2 files changed, 224 insertions(+), 7 deletions(-) create mode 100644 lib/groupher_server/cms/delegates/article_collect.ex diff --git a/lib/groupher_server/cms/cms.ex b/lib/groupher_server/cms/cms.ex index 43ead789f..f6e11e012 100644 --- a/lib/groupher_server/cms/cms.ex +++ b/lib/groupher_server/cms/cms.ex @@ -14,6 +14,7 @@ defmodule GroupherServer.CMS do ArticleEmotion, ArticleReaction, ArticleComment, + ArticleCollect, ArticleCommentAction, ArticleCommentEmotion, CommentCURD, @@ -87,15 +88,15 @@ defmodule GroupherServer.CMS do defdelegate upvoted_users(thread, article_id, filter), to: ArticleReaction - defdelegate collect_article(thread, article_id, user), to: ArticleReaction - defdelegate collect_article_ifneed(thread, article_id, user), to: ArticleReaction + defdelegate collect_article(thread, article_id, user), to: ArticleCollect + defdelegate collect_article_ifneed(thread, article_id, user), to: ArticleCollect - defdelegate undo_collect_article(thread, article_id, user), to: ArticleReaction - defdelegate undo_collect_article_ifneed(thread, article_id, user), to: ArticleReaction - defdelegate collected_users(thread, article_id, filter), to: ArticleReaction + defdelegate undo_collect_article(thread, article_id, user), to: ArticleCollect + defdelegate undo_collect_article_ifneed(thread, article_id, user), to: ArticleCollect + defdelegate collected_users(thread, article_id, filter), to: ArticleCollect - defdelegate set_collect_folder(collect, folder), to: ArticleReaction - defdelegate undo_set_collect_folder(collect, folder), to: ArticleReaction + defdelegate set_collect_folder(collect, folder), to: ArticleCollect + defdelegate undo_set_collect_folder(collect, folder), to: ArticleCollect # ArticleOperation # >> set flag on article, like: pin / unpin article diff --git a/lib/groupher_server/cms/delegates/article_collect.ex b/lib/groupher_server/cms/delegates/article_collect.ex new file mode 100644 index 000000000..4a374d0c5 --- /dev/null +++ b/lib/groupher_server/cms/delegates/article_collect.ex @@ -0,0 +1,216 @@ +defmodule GroupherServer.CMS.Delegate.ArticleCollect do + @moduledoc """ + reaction[upvote, collect, watch ...] on article [post, job...] + """ + import GroupherServer.CMS.Helper.Matcher2 + import Ecto.Query, warn: false + import Helper.Utils, only: [done: 1, strip_struct: 1] + # import Helper.ErrorCode + import ShortMaps + + alias Helper.{ORM, QueryBuilder} + alias GroupherServer.{Accounts, CMS, Repo} + + alias Accounts.User + alias CMS.{ArticleUpvote, ArticleCollect, Embeds} + + alias Ecto.Multi + + @default_article_meta Embeds.ArticleMeta.default_meta() + + @doc """ + get paged collected users + """ + def collected_users(thread, article_id, filter) do + load_reaction_users(ArticleCollect, thread, article_id, filter) + end + + @doc """ + collect an article + """ + def collect_article(thread, article_id, %User{id: user_id}) do + with {:ok, info} <- match(thread), + {:ok, article} <- ORM.find(info.model, article_id, preload: [author: :user]) do + Multi.new() + |> Multi.run(:inc_author_achieve, fn _, _ -> + Accounts.achieve(article.author.user, :inc, :collect) + end) + |> Multi.run(:inc_article_collects_count, fn _, _ -> + update_article_upvotes_count(info, article, :collects_count, :inc) + end) + |> Multi.run(:update_article_reaction_user_list, fn _, _ -> + update_article_reaction_user_list(:collect, article, user_id, :add) + end) + |> Multi.run(:create_collect, fn _, _ -> + thread_upcase = thread |> to_string |> String.upcase() + args = Map.put(%{user_id: user_id, thread: thread_upcase}, info.foreign_key, article.id) + + ORM.create(ArticleCollect, args) + end) + |> Repo.transaction() + |> result() + end + end + + # 用于在收藏时,用户添加文章到不同的收藏夹中的情况 + # 如果是同一篇文章,只创建一次,collect_article 不创建记录,只是后续设置不同的收藏夹即可 + # 如果是第一次收藏,那么才创建文章收藏记录 + # 避免因为同一篇文章在不同收藏夹内造成的统计和用户成就系统的混乱 + def collect_article_ifneed(thread, article_id, %User{id: user_id} = user) do + with findby_args <- collection_findby_args(thread, article_id, user_id) do + already_collected = ORM.find_by(ArticleCollect, findby_args) + + case already_collected do + {:ok, article_collect} -> {:ok, article_collect} + {:error, _} -> collect_article(thread, article_id, user) + end + end + end + + def undo_collect_article(thread, article_id, %User{id: user_id}) do + with {:ok, info} <- match(thread), + {:ok, article} <- ORM.find(info.model, article_id, preload: [author: :user]) do + Multi.new() + |> Multi.run(:dec_author_achieve, fn _, _ -> + Accounts.achieve(article.author.user, :dec, :collect) + end) + |> Multi.run(:inc_article_collects_count, fn _, _ -> + update_article_upvotes_count(info, article, :collects_count, :dec) + end) + |> Multi.run(:update_article_reaction_user_list, fn _, _ -> + update_article_reaction_user_list(:collect, article, user_id, :remove) + end) + |> Multi.run(:undo_collect, fn _, _ -> + args = Map.put(%{user_id: user_id}, info.foreign_key, article.id) + + ORM.findby_delete(ArticleCollect, args) + end) + |> Repo.transaction() + |> result() + end + end + + def undo_collect_article_ifneed(thread, article_id, %User{id: user_id} = user) do + with findby_args <- collection_findby_args(thread, article_id, user_id), + {:ok, article_collect} = ORM.find_by(ArticleCollect, findby_args) do + case article_collect.collect_folders |> length <= 1 do + true -> undo_collect_article(thread, article_id, user) + false -> {:ok, article_collect} + end + end + end + + def set_collect_folder(%ArticleCollect{} = collect, folder) do + collect_folders = (collect.collect_folders ++ [folder]) |> Enum.uniq() + + collect + |> Ecto.Changeset.change() + |> Ecto.Changeset.put_embed(:collect_folders, collect_folders) + |> Repo.update() + end + + def undo_set_collect_folder(%ArticleCollect{} = collect, folder) do + collect_folders = Enum.reject(collect.collect_folders, &(&1.id == folder.id)) + + case collect_folders do + # means collect already delete + [] -> + {:ok, :pass} + + _ -> + collect + |> Ecto.Changeset.change() + |> Ecto.Changeset.put_embed(:collect_folders, collect_folders) + |> Repo.update() + end + end + + defp collection_findby_args(thread, article_id, user_id) do + with {:ok, info} <- match(thread) do + thread_upcase = thread |> to_string |> String.upcase() + %{thread: thread_upcase, user_id: user_id} |> Map.put(info.foreign_key, article_id) + end + end + + ############# + ############# + ############# + + # TODO: put in header, it's for upvotes and collect users + defp load_reaction_users(schema, thread, article_id, %{page: page, size: size} = filter) do + with {:ok, info} <- match(thread) do + schema + |> where([u], field(u, ^info.foreign_key) == ^article_id) + |> QueryBuilder.load_inner_users(filter) + |> ORM.paginater(~m(page size)a) + |> done() + end + end + + # TODO: put in header, it's for upvotes and collect users + defp update_article_upvotes_count(info, article, field, opt) do + schema = + case field do + :upvotes_count -> ArticleUpvote + :collects_count -> ArticleCollect + end + + count_query = from(u in schema, where: field(u, ^info.foreign_key) == ^article.id) + cur_count = Repo.aggregate(count_query, :count) + + case opt do + :inc -> + new_count = Enum.max([0, cur_count]) + ORM.update(article, Map.put(%{}, field, new_count + 1)) + + :dec -> + new_count = Enum.max([1, cur_count]) + ORM.update(article, Map.put(%{}, field, new_count - 1)) + end + end + + # TODO: put in header, it's for upvotes and collect users + @doc """ + add or remove artilce's reaction users is list history + e.g: + add/remove user_id to upvoted_user_ids in article meta + """ + @spec update_article_reaction_user_list( + :upvot | :collect, + T.article_common(), + String.t(), + :add | :remove + ) :: T.article_common() + defp update_article_reaction_user_list(action, %{meta: nil} = article, user_id, opt) do + cur_user_ids = [] + + updated_user_ids = + case opt do + :add -> [user_id] ++ cur_user_ids + :remove -> cur_user_ids -- [user_id] + end + + meta = @default_article_meta |> Map.merge(%{"#{action}ed_user_ids": updated_user_ids}) + ORM.update_meta(article, meta) + end + + defp update_article_reaction_user_list(action, article, user_id, opt) do + cur_user_ids = get_in(article, [:meta, :"#{action}ed_user_ids"]) + + updated_user_ids = + case opt do + :add -> [user_id] ++ cur_user_ids + :remove -> cur_user_ids -- [user_id] + end + + meta = article.meta |> Map.merge(%{"#{action}ed_user_ids": updated_user_ids}) |> strip_struct + ORM.update_meta(article, meta) + end + + defp result({:ok, %{create_collect: result}}), do: result |> done() + defp result({:ok, %{undo_collect: result}}), do: result |> done() + + defp result({:error, _, result, _steps}) do + {:error, result} + end +end From 798f03423a3091b783567f540fb2a8bfd7b51e7c Mon Sep 17 00:00:00 2001 From: mydearxym Date: Fri, 14 May 2021 11:00:46 +0800 Subject: [PATCH 2/5] refactor: rm collect code from ArticleReaction --- .../cms/delegates/article_reaction.ex | 121 +----------------- 1 file changed, 5 insertions(+), 116 deletions(-) diff --git a/lib/groupher_server/cms/delegates/article_reaction.ex b/lib/groupher_server/cms/delegates/article_reaction.ex index a2cae6b03..84c806a85 100644 --- a/lib/groupher_server/cms/delegates/article_reaction.ex +++ b/lib/groupher_server/cms/delegates/article_reaction.ex @@ -22,10 +22,6 @@ defmodule GroupherServer.CMS.Delegate.ArticleReaction do load_reaction_users(ArticleUpvote, thread, article_id, filter) end - def collected_users(thread, article_id, filter) do - load_reaction_users(ArticleCollect, thread, article_id, filter) - end - defp load_reaction_users(schema, thread, article_id, %{page: page, size: size} = filter) do with {:ok, info} <- match(thread) do schema @@ -36,110 +32,6 @@ defmodule GroupherServer.CMS.Delegate.ArticleReaction do end end - def collect_article(thread, article_id, %User{id: user_id}) do - with {:ok, info} <- match(thread), - {:ok, article} <- ORM.find(info.model, article_id, preload: [author: :user]) do - Multi.new() - |> Multi.run(:inc_author_achieve, fn _, _ -> - Accounts.achieve(article.author.user, :inc, :collect) - end) - |> Multi.run(:inc_article_collects_count, fn _, _ -> - update_article_upvotes_count(info, article, :collects_count, :inc) - end) - |> Multi.run(:update_article_reaction_user_list, fn _, _ -> - update_article_reaction_user_list(:collect, article, user_id, :add) - end) - |> Multi.run(:create_collect, fn _, _ -> - thread_upcase = thread |> to_string |> String.upcase() - args = Map.put(%{user_id: user_id, thread: thread_upcase}, info.foreign_key, article.id) - - ORM.create(ArticleCollect, args) - end) - |> Repo.transaction() - |> reaction_result() - end - end - - # 用于在收藏时,用户添加文章到不同的收藏夹中的情况 - # 如果是同一篇文章,只创建一次,collect_article 不创建记录,只是后续设置不同的收藏夹即可 - # 如果是第一次收藏,那么才创建文章收藏记录 - # 避免因为同一篇文章在不同收藏夹内造成的统计和用户成就系统的混乱 - def collect_article_ifneed(thread, article_id, %User{id: user_id} = user) do - with findby_args <- collection_findby_args(thread, article_id, user_id) do - already_collected = ORM.find_by(ArticleCollect, findby_args) - - case already_collected do - {:ok, article_collect} -> {:ok, article_collect} - {:error, _} -> collect_article(thread, article_id, user) - end - end - end - - def undo_collect_article(thread, article_id, %User{id: user_id}) do - with {:ok, info} <- match(thread), - {:ok, article} <- ORM.find(info.model, article_id, preload: [author: :user]) do - Multi.new() - |> Multi.run(:dec_author_achieve, fn _, _ -> - Accounts.achieve(article.author.user, :dec, :collect) - end) - |> Multi.run(:inc_article_collects_count, fn _, _ -> - update_article_upvotes_count(info, article, :collects_count, :dec) - end) - |> Multi.run(:update_article_reaction_user_list, fn _, _ -> - update_article_reaction_user_list(:collect, article, user_id, :remove) - end) - |> Multi.run(:undo_collect, fn _, _ -> - args = Map.put(%{user_id: user_id}, info.foreign_key, article.id) - - ORM.findby_delete(ArticleCollect, args) - end) - |> Repo.transaction() - |> reaction_result() - end - end - - def undo_collect_article_ifneed(thread, article_id, %User{id: user_id} = user) do - with findby_args <- collection_findby_args(thread, article_id, user_id), - {:ok, article_collect} = ORM.find_by(ArticleCollect, findby_args) do - case article_collect.collect_folders |> length <= 1 do - true -> undo_collect_article(thread, article_id, user) - false -> {:ok, article_collect} - end - end - end - - defp collection_findby_args(thread, article_id, user_id) do - with {:ok, info} <- match(thread) do - thread_upcase = thread |> to_string |> String.upcase() - %{thread: thread_upcase, user_id: user_id} |> Map.put(info.foreign_key, article_id) - end - end - - def set_collect_folder(%ArticleCollect{} = collect, folder) do - collect_folders = (collect.collect_folders ++ [folder]) |> Enum.uniq() - - collect - |> Ecto.Changeset.change() - |> Ecto.Changeset.put_embed(:collect_folders, collect_folders) - |> Repo.update() - end - - def undo_set_collect_folder(%ArticleCollect{} = collect, folder) do - collect_folders = Enum.reject(collect.collect_folders, &(&1.id == folder.id)) - - case collect_folders do - # means collect already delete - [] -> - {:ok, :pass} - - _ -> - collect - |> Ecto.Changeset.change() - |> Ecto.Changeset.put_embed(:collect_folders, collect_folders) - |> Repo.update() - end - end - @doc "upvote to a article-like content" def upvote_article(thread, article_id, %User{id: user_id}) do with {:ok, info} <- match(thread), @@ -164,7 +56,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleReaction do end end) |> Repo.transaction() - |> reaction_result() + |> result() end end @@ -186,7 +78,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleReaction do ORM.find(info.model, article.id) end) |> Repo.transaction() - |> reaction_result() + |> result() end end @@ -248,13 +140,10 @@ defmodule GroupherServer.CMS.Delegate.ArticleReaction do ORM.update_meta(article, meta) end - defp reaction_result({:ok, %{create_upvote: result}}), do: result |> done() - defp reaction_result({:ok, %{undo_upvote: result}}), do: result |> done() - - defp reaction_result({:ok, %{create_collect: result}}), do: result |> done() - defp reaction_result({:ok, %{undo_collect: result}}), do: result |> done() + defp result({:ok, %{create_upvote: result}}), do: result |> done() + defp result({:ok, %{undo_upvote: result}}), do: result |> done() - defp reaction_result({:error, _, result, _steps}) do + defp result({:error, _, result, _steps}) do {:error, result} end end From 7091f2390cdd86f51b4e2eb584d5fa386242a278 Mon Sep 17 00:00:00 2001 From: mydearxym Date: Fri, 14 May 2021 11:18:14 +0800 Subject: [PATCH 3/5] refactor: extract helper to share upvote && collect logic --- lib/groupher_server/cms/cms.ex | 9 +- .../cms/delegates/article_collect.ex | 101 ++---------- .../cms/delegates/article_reaction.ex | 149 ------------------ .../cms/delegates/article_upvote.ex | 83 ++++++++++ lib/groupher_server/cms/delegates/helper.ex | 90 ++++++++++- 5 files changed, 189 insertions(+), 243 deletions(-) delete mode 100644 lib/groupher_server/cms/delegates/article_reaction.ex create mode 100644 lib/groupher_server/cms/delegates/article_upvote.ex diff --git a/lib/groupher_server/cms/cms.ex b/lib/groupher_server/cms/cms.ex index f6e11e012..5ec24cfeb 100644 --- a/lib/groupher_server/cms/cms.ex +++ b/lib/groupher_server/cms/cms.ex @@ -12,9 +12,9 @@ defmodule GroupherServer.CMS do ArticleCURD, ArticleOperation, ArticleEmotion, - ArticleReaction, ArticleComment, ArticleCollect, + ArticleUpvote, ArticleCommentAction, ArticleCommentEmotion, CommentCURD, @@ -82,11 +82,10 @@ defmodule GroupherServer.CMS do defdelegate create_content(community, thread, attrs, user), to: ArticleCURD defdelegate update_content(content, attrs), to: ArticleCURD - # ArticleReaction - defdelegate upvote_article(thread, article_id, user), to: ArticleReaction - defdelegate undo_upvote_article(thread, article_id, user), to: ArticleReaction + defdelegate upvote_article(thread, article_id, user), to: ArticleUpvote + defdelegate undo_upvote_article(thread, article_id, user), to: ArticleUpvote - defdelegate upvoted_users(thread, article_id, filter), to: ArticleReaction + defdelegate upvoted_users(thread, article_id, filter), to: ArticleUpvote defdelegate collect_article(thread, article_id, user), to: ArticleCollect defdelegate collect_article_ifneed(thread, article_id, user), to: ArticleCollect diff --git a/lib/groupher_server/cms/delegates/article_collect.ex b/lib/groupher_server/cms/delegates/article_collect.ex index 4a374d0c5..8b7177d85 100644 --- a/lib/groupher_server/cms/delegates/article_collect.ex +++ b/lib/groupher_server/cms/delegates/article_collect.ex @@ -4,20 +4,24 @@ defmodule GroupherServer.CMS.Delegate.ArticleCollect do """ import GroupherServer.CMS.Helper.Matcher2 import Ecto.Query, warn: false - import Helper.Utils, only: [done: 1, strip_struct: 1] - # import Helper.ErrorCode - import ShortMaps + import Helper.Utils, only: [done: 1] + + import GroupherServer.CMS.Delegate.Helper, + only: [ + load_reaction_users: 4, + update_article_reactions_count: 4, + update_article_reaction_user_list: 4 + ] - alias Helper.{ORM, QueryBuilder} + # import Helper.ErrorCode + alias Helper.{ORM} alias GroupherServer.{Accounts, CMS, Repo} alias Accounts.User - alias CMS.{ArticleUpvote, ArticleCollect, Embeds} + alias CMS.ArticleCollect alias Ecto.Multi - @default_article_meta Embeds.ArticleMeta.default_meta() - @doc """ get paged collected users """ @@ -36,7 +40,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleCollect do Accounts.achieve(article.author.user, :inc, :collect) end) |> Multi.run(:inc_article_collects_count, fn _, _ -> - update_article_upvotes_count(info, article, :collects_count, :inc) + update_article_reactions_count(info, article, :collect, :inc) end) |> Multi.run(:update_article_reaction_user_list, fn _, _ -> update_article_reaction_user_list(:collect, article, user_id, :add) @@ -75,7 +79,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleCollect do Accounts.achieve(article.author.user, :dec, :collect) end) |> Multi.run(:inc_article_collects_count, fn _, _ -> - update_article_upvotes_count(info, article, :collects_count, :dec) + update_article_reactions_count(info, article, :collect, :dec) end) |> Multi.run(:update_article_reaction_user_list, fn _, _ -> update_article_reaction_user_list(:collect, article, user_id, :remove) @@ -133,84 +137,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleCollect do end ############# - ############# - ############# - - # TODO: put in header, it's for upvotes and collect users - defp load_reaction_users(schema, thread, article_id, %{page: page, size: size} = filter) do - with {:ok, info} <- match(thread) do - schema - |> where([u], field(u, ^info.foreign_key) == ^article_id) - |> QueryBuilder.load_inner_users(filter) - |> ORM.paginater(~m(page size)a) - |> done() - end - end - - # TODO: put in header, it's for upvotes and collect users - defp update_article_upvotes_count(info, article, field, opt) do - schema = - case field do - :upvotes_count -> ArticleUpvote - :collects_count -> ArticleCollect - end - - count_query = from(u in schema, where: field(u, ^info.foreign_key) == ^article.id) - cur_count = Repo.aggregate(count_query, :count) - - case opt do - :inc -> - new_count = Enum.max([0, cur_count]) - ORM.update(article, Map.put(%{}, field, new_count + 1)) - - :dec -> - new_count = Enum.max([1, cur_count]) - ORM.update(article, Map.put(%{}, field, new_count - 1)) - end - end - - # TODO: put in header, it's for upvotes and collect users - @doc """ - add or remove artilce's reaction users is list history - e.g: - add/remove user_id to upvoted_user_ids in article meta - """ - @spec update_article_reaction_user_list( - :upvot | :collect, - T.article_common(), - String.t(), - :add | :remove - ) :: T.article_common() - defp update_article_reaction_user_list(action, %{meta: nil} = article, user_id, opt) do - cur_user_ids = [] - - updated_user_ids = - case opt do - :add -> [user_id] ++ cur_user_ids - :remove -> cur_user_ids -- [user_id] - end - - meta = @default_article_meta |> Map.merge(%{"#{action}ed_user_ids": updated_user_ids}) - ORM.update_meta(article, meta) - end - - defp update_article_reaction_user_list(action, article, user_id, opt) do - cur_user_ids = get_in(article, [:meta, :"#{action}ed_user_ids"]) - - updated_user_ids = - case opt do - :add -> [user_id] ++ cur_user_ids - :remove -> cur_user_ids -- [user_id] - end - - meta = article.meta |> Map.merge(%{"#{action}ed_user_ids": updated_user_ids}) |> strip_struct - ORM.update_meta(article, meta) - end - defp result({:ok, %{create_collect: result}}), do: result |> done() defp result({:ok, %{undo_collect: result}}), do: result |> done() - - defp result({:error, _, result, _steps}) do - {:error, result} - end + defp result({:error, _, result, _steps}), do: {:error, result} end diff --git a/lib/groupher_server/cms/delegates/article_reaction.ex b/lib/groupher_server/cms/delegates/article_reaction.ex deleted file mode 100644 index 84c806a85..000000000 --- a/lib/groupher_server/cms/delegates/article_reaction.ex +++ /dev/null @@ -1,149 +0,0 @@ -defmodule GroupherServer.CMS.Delegate.ArticleReaction do - @moduledoc """ - reaction[upvote, collect, watch ...] on article [post, job...] - """ - import GroupherServer.CMS.Helper.Matcher2 - import Ecto.Query, warn: false - import Helper.Utils, only: [done: 1, strip_struct: 1] - # import Helper.ErrorCode - import ShortMaps - - alias Helper.{ORM, QueryBuilder} - alias GroupherServer.{Accounts, CMS, Repo} - - alias Accounts.User - alias CMS.{ArticleUpvote, ArticleCollect, Embeds} - - alias Ecto.Multi - - @default_article_meta Embeds.ArticleMeta.default_meta() - - def upvoted_users(thread, article_id, filter) do - load_reaction_users(ArticleUpvote, thread, article_id, filter) - end - - defp load_reaction_users(schema, thread, article_id, %{page: page, size: size} = filter) do - with {:ok, info} <- match(thread) do - schema - |> where([u], field(u, ^info.foreign_key) == ^article_id) - |> QueryBuilder.load_inner_users(filter) - |> ORM.paginater(~m(page size)a) - |> done() - end - end - - @doc "upvote to a article-like content" - def upvote_article(thread, article_id, %User{id: user_id}) do - with {:ok, info} <- match(thread), - {:ok, article} <- ORM.find(info.model, article_id, preload: [author: :user]) do - Multi.new() - |> Multi.run(:inc_article_upvotes_count, fn _, _ -> - update_article_upvotes_count(info, article, :upvotes_count, :inc) - end) - |> Multi.run(:update_article_reaction_user_list, fn _, _ -> - update_article_reaction_user_list(:upvot, article, user_id, :add) - end) - |> Multi.run(:add_achievement, fn _, _ -> - achiever_id = article.author.user_id - Accounts.achieve(%User{id: achiever_id}, :inc, :upvote) - end) - |> Multi.run(:create_upvote, fn _, _ -> - thread_upcase = thread |> to_string |> String.upcase() - args = Map.put(%{user_id: user_id, thread: thread_upcase}, info.foreign_key, article.id) - - with {:ok, _} <- ORM.create(ArticleUpvote, args) do - ORM.find(info.model, article.id) - end - end) - |> Repo.transaction() - |> result() - end - end - - @doc "upvote to a article-like content" - def undo_upvote_article(thread, article_id, %User{id: user_id}) do - with {:ok, info} <- match(thread), - {:ok, article} <- ORM.find(info.model, article_id) do - Multi.new() - |> Multi.run(:inc_article_upvotes_count, fn _, _ -> - update_article_upvotes_count(info, article, :upvotes_count, :dec) - end) - |> Multi.run(:update_article_reaction_user_list, fn _, _ -> - update_article_reaction_user_list(:upvot, article, user_id, :remove) - end) - |> Multi.run(:undo_upvote, fn _, _ -> - args = Map.put(%{user_id: user_id}, info.foreign_key, article.id) - - ORM.findby_delete(ArticleUpvote, args) - ORM.find(info.model, article.id) - end) - |> Repo.transaction() - |> result() - end - end - - defp update_article_upvotes_count(info, article, field, opt) do - schema = - case field do - :upvotes_count -> ArticleUpvote - :collects_count -> ArticleCollect - end - - count_query = from(u in schema, where: field(u, ^info.foreign_key) == ^article.id) - cur_count = Repo.aggregate(count_query, :count) - - case opt do - :inc -> - new_count = Enum.max([0, cur_count]) - ORM.update(article, Map.put(%{}, field, new_count + 1)) - - :dec -> - new_count = Enum.max([1, cur_count]) - ORM.update(article, Map.put(%{}, field, new_count - 1)) - end - end - - @doc """ - add or remove artilce's reaction users is list history - e.g: - add/remove user_id to upvoted_user_ids in article meta - """ - @spec update_article_reaction_user_list( - :upvot | :collect, - T.article_common(), - String.t(), - :add | :remove - ) :: T.article_common() - defp update_article_reaction_user_list(action, %{meta: nil} = article, user_id, opt) do - cur_user_ids = [] - - updated_user_ids = - case opt do - :add -> [user_id] ++ cur_user_ids - :remove -> cur_user_ids -- [user_id] - end - - meta = @default_article_meta |> Map.merge(%{"#{action}ed_user_ids": updated_user_ids}) - ORM.update_meta(article, meta) - end - - defp update_article_reaction_user_list(action, article, user_id, opt) do - cur_user_ids = get_in(article, [:meta, :"#{action}ed_user_ids"]) - - updated_user_ids = - case opt do - :add -> [user_id] ++ cur_user_ids - :remove -> cur_user_ids -- [user_id] - end - - meta = article.meta |> Map.merge(%{"#{action}ed_user_ids": updated_user_ids}) |> strip_struct - ORM.update_meta(article, meta) - end - - defp result({:ok, %{create_upvote: result}}), do: result |> done() - defp result({:ok, %{undo_upvote: result}}), do: result |> done() - - defp result({:error, _, result, _steps}) do - {:error, result} - end -end diff --git a/lib/groupher_server/cms/delegates/article_upvote.ex b/lib/groupher_server/cms/delegates/article_upvote.ex new file mode 100644 index 000000000..2c0d37c8c --- /dev/null +++ b/lib/groupher_server/cms/delegates/article_upvote.ex @@ -0,0 +1,83 @@ +defmodule GroupherServer.CMS.Delegate.ArticleUpvote do + @moduledoc """ + reaction[upvote, collect, watch ...] on article [post, job...] + """ + import GroupherServer.CMS.Helper.Matcher2 + import Ecto.Query, warn: false + import Helper.Utils, only: [done: 1] + + import GroupherServer.CMS.Delegate.Helper, + only: [ + load_reaction_users: 4, + update_article_reactions_count: 4, + update_article_reaction_user_list: 4 + ] + + # import Helper.ErrorCode + + alias Helper.ORM + alias GroupherServer.{Accounts, CMS, Repo} + + alias Accounts.User + alias CMS.ArticleUpvote + + alias Ecto.Multi + + def upvoted_users(thread, article_id, filter) do + load_reaction_users(ArticleUpvote, thread, article_id, filter) + end + + @doc "upvote to a article-like content" + def upvote_article(thread, article_id, %User{id: user_id}) do + with {:ok, info} <- match(thread), + {:ok, article} <- ORM.find(info.model, article_id, preload: [author: :user]) do + Multi.new() + |> Multi.run(:inc_article_upvotes_count, fn _, _ -> + update_article_reactions_count(info, article, :upvote, :inc) + end) + |> Multi.run(:update_article_reaction_user_list, fn _, _ -> + update_article_reaction_user_list(:upvot, article, user_id, :add) + end) + |> Multi.run(:add_achievement, fn _, _ -> + achiever_id = article.author.user_id + Accounts.achieve(%User{id: achiever_id}, :inc, :upvote) + end) + |> Multi.run(:create_upvote, fn _, _ -> + thread_upcase = thread |> to_string |> String.upcase() + args = Map.put(%{user_id: user_id, thread: thread_upcase}, info.foreign_key, article.id) + + with {:ok, _} <- ORM.create(ArticleUpvote, args) do + ORM.find(info.model, article.id) + end + end) + |> Repo.transaction() + |> result() + end + end + + @doc "upvote to a article-like content" + def undo_upvote_article(thread, article_id, %User{id: user_id}) do + with {:ok, info} <- match(thread), + {:ok, article} <- ORM.find(info.model, article_id) do + Multi.new() + |> Multi.run(:inc_article_upvotes_count, fn _, _ -> + update_article_reactions_count(info, article, :upvote, :dec) + end) + |> Multi.run(:update_article_reaction_user_list, fn _, _ -> + update_article_reaction_user_list(:upvot, article, user_id, :remove) + end) + |> Multi.run(:undo_upvote, fn _, _ -> + args = Map.put(%{user_id: user_id}, info.foreign_key, article.id) + + ORM.findby_delete(ArticleUpvote, args) + ORM.find(info.model, article.id) + end) + |> Repo.transaction() + |> result() + end + end + + defp result({:ok, %{create_upvote: result}}), do: result |> done() + defp result({:ok, %{undo_upvote: result}}), do: result |> done() + defp result({:error, _, result, _steps}), do: {:error, result} +end diff --git a/lib/groupher_server/cms/delegates/helper.ex b/lib/groupher_server/cms/delegates/helper.ex index 93b5f3b7d..0351cd8cc 100644 --- a/lib/groupher_server/cms/delegates/helper.ex +++ b/lib/groupher_server/cms/delegates/helper.ex @@ -2,17 +2,28 @@ defmodule GroupherServer.CMS.Delegate.Helper do @moduledoc """ helpers for GroupherServer.CMS.Delegate """ - import Helper.Utils, only: [get_config: 2, done: 1] + import Helper.Utils, only: [get_config: 2, done: 1, strip_struct: 1] + import Ecto.Query, warn: false + import GroupherServer.CMS.Helper.Matcher2 + import ShortMaps - alias GroupherServer.{Accounts, Repo} + alias Helper.{ORM, QueryBuilder} + alias GroupherServer.{Accounts, Repo, CMS} + + alias CMS.{ArticleUpvote, ArticleCollect} alias Accounts.User + @default_article_meta CMS.Embeds.ArticleMeta.default_meta() + # TODO: # @max_latest_emotion_users_count ArticleComment.max_latest_emotion_users_count() @max_latest_emotion_users_count 4 @supported_emotions get_config(:article, :supported_emotions) @supported_comment_emotions get_config(:article, :comment_supported_emotions) + ####### + # emotion related + ####### defp get_supported_mentions(:comment), do: @supported_comment_emotions defp get_supported_mentions(_), do: @supported_emotions @@ -82,4 +93,79 @@ defmodule GroupherServer.CMS.Delegate.Helper do defp user_in_logins?([], _), do: false defp user_in_logins?(ids_list, %User{login: login}), do: Enum.member?(ids_list, login) + + ####### + # emotion related end + ####### + + ###### + # reaction related end, include upvote && collect + ####### + def load_reaction_users(schema, thread, article_id, %{page: page, size: size} = filter) do + with {:ok, info} <- match(thread) do + schema + |> where([u], field(u, ^info.foreign_key) == ^article_id) + |> QueryBuilder.load_inner_users(filter) + |> ORM.paginater(~m(page size)a) + |> done() + end + end + + def update_article_reactions_count(info, article, field, opt) do + schema = + case field do + :upvote -> ArticleUpvote + :collect -> ArticleCollect + end + + count_query = from(u in schema, where: field(u, ^info.foreign_key) == ^article.id) + cur_count = Repo.aggregate(count_query, :count) + + case opt do + :inc -> + new_count = Enum.max([0, cur_count]) + ORM.update(article, Map.put(%{}, field, new_count + 1)) + + :dec -> + new_count = Enum.max([1, cur_count]) + ORM.update(article, Map.put(%{}, field, new_count - 1)) + end + end + + @doc """ + add or remove artilce's reaction users is list history + e.g: + add/remove user_id to upvoted_user_ids in article meta + """ + @spec update_article_reaction_user_list( + :upvot | :collect, + T.article_common(), + String.t(), + :add | :remove + ) :: T.article_common() + def update_article_reaction_user_list(action, %{meta: nil} = article, user_id, opt) do + cur_user_ids = [] + + updated_user_ids = + case opt do + :add -> [user_id] ++ cur_user_ids + :remove -> cur_user_ids -- [user_id] + end + + meta = @default_article_meta |> Map.merge(%{"#{action}ed_user_ids": updated_user_ids}) + ORM.update_meta(article, meta) + end + + def update_article_reaction_user_list(action, article, user_id, opt) do + cur_user_ids = get_in(article, [:meta, :"#{action}ed_user_ids"]) + + updated_user_ids = + case opt do + :add -> [user_id] ++ cur_user_ids + :remove -> cur_user_ids -- [user_id] + end + + meta = article.meta |> Map.merge(%{"#{action}ed_user_ids": updated_user_ids}) |> strip_struct + ORM.update_meta(article, meta) + end end From 516dbdd2ce0abf536785f2bd154b033ded0f8dd9 Mon Sep 17 00:00:00 2001 From: mydearxym Date: Fri, 14 May 2021 11:38:28 +0800 Subject: [PATCH 4/5] refactor: wip --- lib/groupher_server/cms/delegates/article_collect.ex | 4 ++-- lib/groupher_server/cms/delegates/article_upvote.ex | 4 ++-- lib/groupher_server/cms/delegates/helper.ex | 4 ++-- test/groupher_server/cms/abuse_reports/post_report_test.exs | 1 - test/groupher_server/cms/upvotes/job_upvote_test.exs | 1 + test/groupher_server_web/query/cms/flags/posts_flags_test.exs | 1 - 6 files changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/groupher_server/cms/delegates/article_collect.ex b/lib/groupher_server/cms/delegates/article_collect.ex index 8b7177d85..7e158aaa5 100644 --- a/lib/groupher_server/cms/delegates/article_collect.ex +++ b/lib/groupher_server/cms/delegates/article_collect.ex @@ -40,7 +40,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleCollect do Accounts.achieve(article.author.user, :inc, :collect) end) |> Multi.run(:inc_article_collects_count, fn _, _ -> - update_article_reactions_count(info, article, :collect, :inc) + update_article_reactions_count(info, article, :collects_count, :inc) end) |> Multi.run(:update_article_reaction_user_list, fn _, _ -> update_article_reaction_user_list(:collect, article, user_id, :add) @@ -79,7 +79,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleCollect do Accounts.achieve(article.author.user, :dec, :collect) end) |> Multi.run(:inc_article_collects_count, fn _, _ -> - update_article_reactions_count(info, article, :collect, :dec) + update_article_reactions_count(info, article, :collects_count, :dec) end) |> Multi.run(:update_article_reaction_user_list, fn _, _ -> update_article_reaction_user_list(:collect, article, user_id, :remove) diff --git a/lib/groupher_server/cms/delegates/article_upvote.ex b/lib/groupher_server/cms/delegates/article_upvote.ex index 2c0d37c8c..247aa0986 100644 --- a/lib/groupher_server/cms/delegates/article_upvote.ex +++ b/lib/groupher_server/cms/delegates/article_upvote.ex @@ -33,7 +33,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleUpvote do {:ok, article} <- ORM.find(info.model, article_id, preload: [author: :user]) do Multi.new() |> Multi.run(:inc_article_upvotes_count, fn _, _ -> - update_article_reactions_count(info, article, :upvote, :inc) + update_article_reactions_count(info, article, :upvotes_count, :inc) end) |> Multi.run(:update_article_reaction_user_list, fn _, _ -> update_article_reaction_user_list(:upvot, article, user_id, :add) @@ -61,7 +61,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleUpvote do {:ok, article} <- ORM.find(info.model, article_id) do Multi.new() |> Multi.run(:inc_article_upvotes_count, fn _, _ -> - update_article_reactions_count(info, article, :upvote, :dec) + update_article_reactions_count(info, article, :upvotes_count, :dec) end) |> Multi.run(:update_article_reaction_user_list, fn _, _ -> update_article_reaction_user_list(:upvot, article, user_id, :remove) diff --git a/lib/groupher_server/cms/delegates/helper.ex b/lib/groupher_server/cms/delegates/helper.ex index 0351cd8cc..27fad2474 100644 --- a/lib/groupher_server/cms/delegates/helper.ex +++ b/lib/groupher_server/cms/delegates/helper.ex @@ -114,8 +114,8 @@ defmodule GroupherServer.CMS.Delegate.Helper do def update_article_reactions_count(info, article, field, opt) do schema = case field do - :upvote -> ArticleUpvote - :collect -> ArticleCollect + :upvotes_count -> ArticleUpvote + :collects_count -> ArticleCollect end count_query = from(u in schema, where: field(u, ^info.foreign_key) == ^article.id) diff --git a/test/groupher_server/cms/abuse_reports/post_report_test.exs b/test/groupher_server/cms/abuse_reports/post_report_test.exs index fcd638368..743fd1b0d 100644 --- a/test/groupher_server/cms/abuse_reports/post_report_test.exs +++ b/test/groupher_server/cms/abuse_reports/post_report_test.exs @@ -79,7 +79,6 @@ defmodule GroupherServer.Test.CMS.AbuseReports.PostReport do assert user.id not in post.meta.reported_user_ids end - @tag :wip2 test "can undo a report with other user report it too", ~m(community user user2 post_attrs)a do {:ok, post} = CMS.create_content(community, :post, post_attrs, user) diff --git a/test/groupher_server/cms/upvotes/job_upvote_test.exs b/test/groupher_server/cms/upvotes/job_upvote_test.exs index 8320df575..b7d5e4e24 100644 --- a/test/groupher_server/cms/upvotes/job_upvote_test.exs +++ b/test/groupher_server/cms/upvotes/job_upvote_test.exs @@ -27,6 +27,7 @@ defmodule GroupherServer.Test.Upvotes.JobUpvote do assert article.upvotes_count == 2 end + @tag :wip2 test "job can be undo upvote && upvotes_count should dec by 1", ~m(user user2 community job_attrs)a do {:ok, job} = CMS.create_content(community, :job, job_attrs, user) diff --git a/test/groupher_server_web/query/cms/flags/posts_flags_test.exs b/test/groupher_server_web/query/cms/flags/posts_flags_test.exs index 90671a222..9d71db79f 100644 --- a/test/groupher_server_web/query/cms/flags/posts_flags_test.exs +++ b/test/groupher_server_web/query/cms/flags/posts_flags_test.exs @@ -89,7 +89,6 @@ defmodule GroupherServer.Test.Query.Flags.PostsFlags do assert results["entries"] |> Enum.any?(&(&1["id"] !== random_id)) end - @tag :wip2 test "if have trashed posts, the trashed posts should not appears in result", ~m(guest_conn community)a do variables = %{filter: %{community: community.raw}} From cd570b51128b5dc76811d99d4902e64bc59c098b Mon Sep 17 00:00:00 2001 From: mydearxym Date: Fri, 14 May 2021 11:59:55 +0800 Subject: [PATCH 5/5] refactor: clean up --- .../cms/delegates/article_comment_action.ex | 30 +++++++++---------- lib/groupher_server/cms/delegates/helper.ex | 16 ++++++++-- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/lib/groupher_server/cms/delegates/article_comment_action.ex b/lib/groupher_server/cms/delegates/article_comment_action.ex index 644933c5c..c91d2115b 100644 --- a/lib/groupher_server/cms/delegates/article_comment_action.ex +++ b/lib/groupher_server/cms/delegates/article_comment_action.ex @@ -63,7 +63,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleCommentAction do ) end) |> Repo.transaction() - |> upsert_comment_result() + |> result() end end @@ -77,7 +77,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleCommentAction do ORM.findby_delete(ArticlePinedComment, %{article_comment_id: comment.id}) end) |> Repo.transaction() - |> upsert_comment_result() + |> result() end end @@ -141,7 +141,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleCommentAction do |> Repo.update() end) |> Repo.transaction() - |> upsert_comment_result() + |> result() end end @@ -166,7 +166,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleCommentAction do update_article_author_upvoted_info(comment, user_id) end) |> Repo.transaction() - |> upsert_comment_result() + |> result() end end @@ -194,7 +194,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleCommentAction do update_article_author_upvoted_info(updated_comment, user_id) end) |> Repo.transaction() - |> upsert_comment_result() + |> result() end end @@ -324,24 +324,24 @@ defmodule GroupherServer.CMS.Delegate.ArticleCommentAction do ORM.update_meta(comment, meta) end - defp upsert_comment_result({:ok, %{create_article_comment: result}}), do: {:ok, result} - defp upsert_comment_result({:ok, %{add_reply_to: result}}), do: {:ok, result} - defp upsert_comment_result({:ok, %{check_article_author_upvoted: result}}), do: {:ok, result} - defp upsert_comment_result({:ok, %{fold_comment_report_too_many: result}}), do: {:ok, result} - defp upsert_comment_result({:ok, %{update_comment_flag: result}}), do: {:ok, result} - defp upsert_comment_result({:ok, %{delete_article_comment: result}}), do: {:ok, result} + defp result({:ok, %{create_article_comment: result}}), do: {:ok, result} + defp result({:ok, %{add_reply_to: result}}), do: {:ok, result} + defp result({:ok, %{check_article_author_upvoted: result}}), do: {:ok, result} + defp result({:ok, %{fold_comment_report_too_many: result}}), do: {:ok, result} + defp result({:ok, %{update_comment_flag: result}}), do: {:ok, result} + defp result({:ok, %{delete_article_comment: result}}), do: {:ok, result} - defp upsert_comment_result({:error, :create_article_comment, result, _steps}) do + defp result({:error, :create_article_comment, result, _steps}) do raise_error(:create_comment, result) end - defp upsert_comment_result({:error, :add_participator, result, _steps}) do + defp result({:error, :add_participator, result, _steps}) do {:error, result} end - defp upsert_comment_result({:error, :create_abuse_report, result, _steps}) do + defp result({:error, :create_abuse_report, result, _steps}) do {:error, result} end - defp upsert_comment_result({:error, _, result, _steps}), do: {:error, result} + defp result({:error, _, result, _steps}), do: {:error, result} end diff --git a/lib/groupher_server/cms/delegates/helper.ex b/lib/groupher_server/cms/delegates/helper.ex index 27fad2474..2a1ce15f8 100644 --- a/lib/groupher_server/cms/delegates/helper.ex +++ b/lib/groupher_server/cms/delegates/helper.ex @@ -28,8 +28,8 @@ defmodule GroupherServer.CMS.Delegate.Helper do defp get_supported_mentions(_), do: @supported_emotions def mark_viewer_emotion_states(paged_contents, nil), do: paged_contents - def mark_viewer_emotion_states(paged_contents, nil, :comment), do: paged_contents def mark_viewer_emotion_states(%{entries: []} = paged_contents, _), do: paged_contents + def mark_viewer_emotion_states(paged_contents, nil, :comment), do: paged_contents @doc """ mark viewer emotions status for article or comment @@ -101,9 +101,14 @@ defmodule GroupherServer.CMS.Delegate.Helper do ###### # reaction related end, include upvote && collect ####### - def load_reaction_users(schema, thread, article_id, %{page: page, size: size} = filter) do + @doc """ + paged [reaction] users list + """ + def load_reaction_users(queryable, thread, article_id, filter) do + %{page: page, size: size} = filter + with {:ok, info} <- match(thread) do - schema + queryable |> where([u], field(u, ^info.foreign_key) == ^article_id) |> QueryBuilder.load_inner_users(filter) |> ORM.paginater(~m(page size)a) @@ -111,6 +116,11 @@ defmodule GroupherServer.CMS.Delegate.Helper do end end + @doc """ + update the [reaction]s_count for article + e.g: + inc/dec upvotes_count of article + """ def update_article_reactions_count(info, article, field, opt) do schema = case field do