Skip to content
This repository was archived by the owner on Nov 8, 2022. It is now read-only.

Commit 9305265

Browse files
committed
refactor: extract collect functions
1 parent 143d51b commit 9305265

File tree

2 files changed

+224
-7
lines changed

2 files changed

+224
-7
lines changed

lib/groupher_server/cms/cms.ex

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ defmodule GroupherServer.CMS do
1414
ArticleEmotion,
1515
ArticleReaction,
1616
ArticleComment,
17+
ArticleCollect,
1718
ArticleCommentAction,
1819
ArticleCommentEmotion,
1920
CommentCURD,
@@ -87,15 +88,15 @@ defmodule GroupherServer.CMS do
8788

8889
defdelegate upvoted_users(thread, article_id, filter), to: ArticleReaction
8990

90-
defdelegate collect_article(thread, article_id, user), to: ArticleReaction
91-
defdelegate collect_article_ifneed(thread, article_id, user), to: ArticleReaction
91+
defdelegate collect_article(thread, article_id, user), to: ArticleCollect
92+
defdelegate collect_article_ifneed(thread, article_id, user), to: ArticleCollect
9293

93-
defdelegate undo_collect_article(thread, article_id, user), to: ArticleReaction
94-
defdelegate undo_collect_article_ifneed(thread, article_id, user), to: ArticleReaction
95-
defdelegate collected_users(thread, article_id, filter), to: ArticleReaction
94+
defdelegate undo_collect_article(thread, article_id, user), to: ArticleCollect
95+
defdelegate undo_collect_article_ifneed(thread, article_id, user), to: ArticleCollect
96+
defdelegate collected_users(thread, article_id, filter), to: ArticleCollect
9697

97-
defdelegate set_collect_folder(collect, folder), to: ArticleReaction
98-
defdelegate undo_set_collect_folder(collect, folder), to: ArticleReaction
98+
defdelegate set_collect_folder(collect, folder), to: ArticleCollect
99+
defdelegate undo_set_collect_folder(collect, folder), to: ArticleCollect
99100

100101
# ArticleOperation
101102
# >> set flag on article, like: pin / unpin article
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
defmodule GroupherServer.CMS.Delegate.ArticleCollect do
2+
@moduledoc """
3+
reaction[upvote, collect, watch ...] on article [post, job...]
4+
"""
5+
import GroupherServer.CMS.Helper.Matcher2
6+
import Ecto.Query, warn: false
7+
import Helper.Utils, only: [done: 1, strip_struct: 1]
8+
# import Helper.ErrorCode
9+
import ShortMaps
10+
11+
alias Helper.{ORM, QueryBuilder}
12+
alias GroupherServer.{Accounts, CMS, Repo}
13+
14+
alias Accounts.User
15+
alias CMS.{ArticleUpvote, ArticleCollect, Embeds}
16+
17+
alias Ecto.Multi
18+
19+
@default_article_meta Embeds.ArticleMeta.default_meta()
20+
21+
@doc """
22+
get paged collected users
23+
"""
24+
def collected_users(thread, article_id, filter) do
25+
load_reaction_users(ArticleCollect, thread, article_id, filter)
26+
end
27+
28+
@doc """
29+
collect an article
30+
"""
31+
def collect_article(thread, article_id, %User{id: user_id}) do
32+
with {:ok, info} <- match(thread),
33+
{:ok, article} <- ORM.find(info.model, article_id, preload: [author: :user]) do
34+
Multi.new()
35+
|> Multi.run(:inc_author_achieve, fn _, _ ->
36+
Accounts.achieve(article.author.user, :inc, :collect)
37+
end)
38+
|> Multi.run(:inc_article_collects_count, fn _, _ ->
39+
update_article_upvotes_count(info, article, :collects_count, :inc)
40+
end)
41+
|> Multi.run(:update_article_reaction_user_list, fn _, _ ->
42+
update_article_reaction_user_list(:collect, article, user_id, :add)
43+
end)
44+
|> Multi.run(:create_collect, fn _, _ ->
45+
thread_upcase = thread |> to_string |> String.upcase()
46+
args = Map.put(%{user_id: user_id, thread: thread_upcase}, info.foreign_key, article.id)
47+
48+
ORM.create(ArticleCollect, args)
49+
end)
50+
|> Repo.transaction()
51+
|> result()
52+
end
53+
end
54+
55+
# 用于在收藏时,用户添加文章到不同的收藏夹中的情况
56+
# 如果是同一篇文章,只创建一次,collect_article 不创建记录,只是后续设置不同的收藏夹即可
57+
# 如果是第一次收藏,那么才创建文章收藏记录
58+
# 避免因为同一篇文章在不同收藏夹内造成的统计和用户成就系统的混乱
59+
def collect_article_ifneed(thread, article_id, %User{id: user_id} = user) do
60+
with findby_args <- collection_findby_args(thread, article_id, user_id) do
61+
already_collected = ORM.find_by(ArticleCollect, findby_args)
62+
63+
case already_collected do
64+
{:ok, article_collect} -> {:ok, article_collect}
65+
{:error, _} -> collect_article(thread, article_id, user)
66+
end
67+
end
68+
end
69+
70+
def undo_collect_article(thread, article_id, %User{id: user_id}) do
71+
with {:ok, info} <- match(thread),
72+
{:ok, article} <- ORM.find(info.model, article_id, preload: [author: :user]) do
73+
Multi.new()
74+
|> Multi.run(:dec_author_achieve, fn _, _ ->
75+
Accounts.achieve(article.author.user, :dec, :collect)
76+
end)
77+
|> Multi.run(:inc_article_collects_count, fn _, _ ->
78+
update_article_upvotes_count(info, article, :collects_count, :dec)
79+
end)
80+
|> Multi.run(:update_article_reaction_user_list, fn _, _ ->
81+
update_article_reaction_user_list(:collect, article, user_id, :remove)
82+
end)
83+
|> Multi.run(:undo_collect, fn _, _ ->
84+
args = Map.put(%{user_id: user_id}, info.foreign_key, article.id)
85+
86+
ORM.findby_delete(ArticleCollect, args)
87+
end)
88+
|> Repo.transaction()
89+
|> result()
90+
end
91+
end
92+
93+
def undo_collect_article_ifneed(thread, article_id, %User{id: user_id} = user) do
94+
with findby_args <- collection_findby_args(thread, article_id, user_id),
95+
{:ok, article_collect} = ORM.find_by(ArticleCollect, findby_args) do
96+
case article_collect.collect_folders |> length <= 1 do
97+
true -> undo_collect_article(thread, article_id, user)
98+
false -> {:ok, article_collect}
99+
end
100+
end
101+
end
102+
103+
def set_collect_folder(%ArticleCollect{} = collect, folder) do
104+
collect_folders = (collect.collect_folders ++ [folder]) |> Enum.uniq()
105+
106+
collect
107+
|> Ecto.Changeset.change()
108+
|> Ecto.Changeset.put_embed(:collect_folders, collect_folders)
109+
|> Repo.update()
110+
end
111+
112+
def undo_set_collect_folder(%ArticleCollect{} = collect, folder) do
113+
collect_folders = Enum.reject(collect.collect_folders, &(&1.id == folder.id))
114+
115+
case collect_folders do
116+
# means collect already delete
117+
[] ->
118+
{:ok, :pass}
119+
120+
_ ->
121+
collect
122+
|> Ecto.Changeset.change()
123+
|> Ecto.Changeset.put_embed(:collect_folders, collect_folders)
124+
|> Repo.update()
125+
end
126+
end
127+
128+
defp collection_findby_args(thread, article_id, user_id) do
129+
with {:ok, info} <- match(thread) do
130+
thread_upcase = thread |> to_string |> String.upcase()
131+
%{thread: thread_upcase, user_id: user_id} |> Map.put(info.foreign_key, article_id)
132+
end
133+
end
134+
135+
#############
136+
#############
137+
#############
138+
139+
# TODO: put in header, it's for upvotes and collect users
140+
defp load_reaction_users(schema, thread, article_id, %{page: page, size: size} = filter) do
141+
with {:ok, info} <- match(thread) do
142+
schema
143+
|> where([u], field(u, ^info.foreign_key) == ^article_id)
144+
|> QueryBuilder.load_inner_users(filter)
145+
|> ORM.paginater(~m(page size)a)
146+
|> done()
147+
end
148+
end
149+
150+
# TODO: put in header, it's for upvotes and collect users
151+
defp update_article_upvotes_count(info, article, field, opt) do
152+
schema =
153+
case field do
154+
:upvotes_count -> ArticleUpvote
155+
:collects_count -> ArticleCollect
156+
end
157+
158+
count_query = from(u in schema, where: field(u, ^info.foreign_key) == ^article.id)
159+
cur_count = Repo.aggregate(count_query, :count)
160+
161+
case opt do
162+
:inc ->
163+
new_count = Enum.max([0, cur_count])
164+
ORM.update(article, Map.put(%{}, field, new_count + 1))
165+
166+
:dec ->
167+
new_count = Enum.max([1, cur_count])
168+
ORM.update(article, Map.put(%{}, field, new_count - 1))
169+
end
170+
end
171+
172+
# TODO: put in header, it's for upvotes and collect users
173+
@doc """
174+
add or remove artilce's reaction users is list history
175+
e.g:
176+
add/remove user_id to upvoted_user_ids in article meta
177+
"""
178+
@spec update_article_reaction_user_list(
179+
:upvot | :collect,
180+
T.article_common(),
181+
String.t(),
182+
:add | :remove
183+
) :: T.article_common()
184+
defp update_article_reaction_user_list(action, %{meta: nil} = article, user_id, opt) do
185+
cur_user_ids = []
186+
187+
updated_user_ids =
188+
case opt do
189+
:add -> [user_id] ++ cur_user_ids
190+
:remove -> cur_user_ids -- [user_id]
191+
end
192+
193+
meta = @default_article_meta |> Map.merge(%{"#{action}ed_user_ids": updated_user_ids})
194+
ORM.update_meta(article, meta)
195+
end
196+
197+
defp update_article_reaction_user_list(action, article, user_id, opt) do
198+
cur_user_ids = get_in(article, [:meta, :"#{action}ed_user_ids"])
199+
200+
updated_user_ids =
201+
case opt do
202+
:add -> [user_id] ++ cur_user_ids
203+
:remove -> cur_user_ids -- [user_id]
204+
end
205+
206+
meta = article.meta |> Map.merge(%{"#{action}ed_user_ids": updated_user_ids}) |> strip_struct
207+
ORM.update_meta(article, meta)
208+
end
209+
210+
defp result({:ok, %{create_collect: result}}), do: result |> done()
211+
defp result({:ok, %{undo_collect: result}}), do: result |> done()
212+
213+
defp result({:error, _, result, _steps}) do
214+
{:error, result}
215+
end
216+
end

0 commit comments

Comments
 (0)