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

Commit 50692b4

Browse files
authored
refactor(article-comments): gq workflow for comment emotions && re-org (#340)
* refactor(article-comment): wip * refactor(article-comment): add middleware for update & delete * refactor(article-comment): wip * refactor(article-comment): wip * refactor(article-comment): wip * refactor(article-comment): wip * refactor(article-comment): wip
1 parent 2fdccee commit 50692b4

File tree

19 files changed

+516
-122
lines changed

19 files changed

+516
-122
lines changed

lib/groupher_server/cms/article_comment.ex

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ defmodule GroupherServer.CMS.ArticleComment do
5353

5454
@type t :: %ArticleComment{}
5555
schema "articles_comments" do
56+
belongs_to(:author, Accounts.User, foreign_key: :author_id)
57+
5658
field(:body_html, :string)
5759
# 是否被折叠
5860
field(:is_folded, :boolean, default: false)
@@ -71,7 +73,6 @@ defmodule GroupherServer.CMS.ArticleComment do
7173
field(:is_pinned, :boolean, default: false)
7274
field(:viewer_has_upvoted, :boolean, default: false, virtual: true)
7375

74-
belongs_to(:author, Accounts.User, foreign_key: :author_id)
7576
belongs_to(:post, Post, foreign_key: :post_id)
7677
belongs_to(:job, Job, foreign_key: :job_id)
7778
belongs_to(:reply_to, ArticleComment, foreign_key: :reply_to_id)

lib/groupher_server/cms/article_comment_user_emotion.ex

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,13 @@ defmodule GroupherServer.CMS.ArticleCommentUserEmotion do
5050
|> foreign_key_constraint(:user_id)
5151
|> foreign_key_constraint(:recived_user_id)
5252
end
53+
54+
def update_changeset(%ArticleCommentUserEmotion{} = struct, attrs) do
55+
struct
56+
|> cast(attrs, @required_fields ++ @optional_fields)
57+
|> validate_required(@required_fields)
58+
|> foreign_key_constraint(:article_comment_id)
59+
|> foreign_key_constraint(:user_id)
60+
|> foreign_key_constraint(:recived_user_id)
61+
end
5362
end

lib/groupher_server/cms/cms.ex

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ defmodule GroupherServer.CMS do
1313
ArticleOperation,
1414
ArticleReaction,
1515
ArticleComment,
16+
ArticleCommentEmotion,
1617
CommentCURD,
1718
CommunitySync,
18-
CommentReaction,
1919
CommunityCURD,
2020
CommunityOperation,
2121
PassportCURD,
@@ -126,15 +126,18 @@ defmodule GroupherServer.CMS do
126126
defdelegate list_article_comments_participators(thread, content_id, filters), to: ArticleComment
127127

128128
defdelegate create_article_comment(thread, article_id, args, user), to: ArticleComment
129+
defdelegate update_article_comment(comment, content), to: ArticleComment
130+
defdelegate delete_article_comment(comment), to: ArticleComment
131+
129132
defdelegate upvote_article_comment(comment_id, user), to: ArticleComment
130133
defdelegate undo_upvote_article_comment(comment_id, user), to: ArticleComment
131-
defdelegate delete_article_comment(comment_id, user), to: ArticleComment
132134
defdelegate reply_article_comment(comment_id, args, user), to: ArticleComment
133135

134136
defdelegate pin_article_comment(comment_id), to: ArticleComment
135137
defdelegate undo_pin_article_comment(comment_id), to: ArticleComment
136138

137-
defdelegate emotion_to_comment(comment_id, args, user), to: ArticleComment
139+
defdelegate emotion_to_comment(comment_id, args, user), to: ArticleCommentEmotion
140+
defdelegate undo_emotion_to_comment(comment_id, args, user), to: ArticleCommentEmotion
138141
defdelegate fold_article_comment(comment_id, user), to: ArticleComment
139142
defdelegate unfold_article_comment(comment_id, user), to: ArticleComment
140143
defdelegate report_article_comment(comment_id, user), to: ArticleComment

lib/groupher_server/cms/delegates/article_comment.ex

Lines changed: 24 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,13 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do
2020
ArticlePinedComment,
2121
ArticleCommentUpvote,
2222
ArticleCommentReply,
23-
ArticleCommentUserEmotion,
2423
Embeds,
2524
Post,
2625
Job
2726
}
2827

2928
alias Ecto.Multi
3029

31-
@max_latest_emotion_users_count ArticleComment.max_latest_emotion_users_count()
3230
@max_participator_count ArticleComment.max_participator_count()
3331
@max_parent_replies_count ArticleComment.max_parent_replies_count()
3432
@default_emotions Embeds.ArticleCommentEmotion.default_emotions()
@@ -157,24 +155,6 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do
157155
end
158156
end
159157

160-
@doc "delete article comment"
161-
def delete_article_comment(comment_id, %User{} = _user) do
162-
with {:ok, comment} <- ORM.find(ArticleComment, comment_id) do
163-
Multi.new()
164-
|> Multi.run(:update_article_comments_count, fn _, _ ->
165-
update_article_comments_count(comment, :dec)
166-
end)
167-
|> Multi.run(:remove_pined_comment, fn _, _ ->
168-
ORM.findby_delete(ArticlePinedComment, %{article_comment_id: comment.id})
169-
end)
170-
|> Multi.run(:delete_article_comment, fn _, _ ->
171-
ORM.update(comment, %{body_html: @delete_hint, is_deleted: true})
172-
end)
173-
|> Repo.transaction()
174-
|> upsert_comment_result()
175-
end
176-
end
177-
178158
def fold_article_comment(%ArticleComment{} = comment, %User{} = _user) do
179159
comment |> ORM.update(%{is_folded: true})
180160
end
@@ -224,68 +204,8 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do
224204
end
225205
end
226206

227-
@doc "make emotion to a comment"
228-
def emotion_to_comment(comment_id, emotion, %User{} = user) do
229-
with {:ok, comment} <-
230-
ORM.find(ArticleComment, comment_id) do
231-
Multi.new()
232-
|> Multi.run(:create_user_emotion, fn _, _ ->
233-
args =
234-
Map.put(
235-
%{
236-
article_comment_id: comment.id,
237-
recived_user_id: comment.author_id,
238-
user_id: user.id
239-
},
240-
:"#{emotion}",
241-
true
242-
)
243-
244-
{:ok, _} = ArticleCommentUserEmotion |> ORM.create(args)
245-
end)
246-
|> Multi.run(:query_emotion_status, fn _, _ ->
247-
# 每次被 emotion 动作触发后重新查询,主要原因
248-
# 1.并发下保证数据准确,类似 views 阅读数的统计
249-
# 2. 前端使用 nickname 而非 login 展示,如果用户改了 nickname, 可以"自动纠正"
250-
query =
251-
from(a in ArticleCommentUserEmotion,
252-
join: user in User,
253-
on: a.user_id == user.id,
254-
where: a.article_comment_id == ^comment.id,
255-
where: field(a, ^emotion) == true,
256-
select: %{login: user.login, nickname: user.nickname}
257-
)
258-
259-
emotioned_user_info_list = Repo.all(query) |> Enum.uniq()
260-
emotioned_user_count = length(emotioned_user_info_list)
261-
262-
{:ok, %{user_list: emotioned_user_info_list, user_count: emotioned_user_count}}
263-
end)
264-
|> Multi.run(:update_comment_emotion, fn _, %{query_emotion_status: status} ->
265-
updated_emotions =
266-
%{}
267-
|> Map.put(:"#{emotion}_count", status.user_count)
268-
|> Map.put(
269-
:"#{emotion}_user_logins",
270-
status.user_list |> Enum.map(& &1.login)
271-
)
272-
|> Map.put(
273-
:"latest_#{emotion}_users",
274-
Enum.slice(status.user_list, 0, @max_latest_emotion_users_count)
275-
)
276-
277-
comment
278-
|> Ecto.Changeset.change()
279-
|> Ecto.Changeset.put_embed(:emotions, updated_emotions)
280-
|> Repo.update()
281-
end)
282-
|> Repo.transaction()
283-
|> upsert_comment_result
284-
end
285-
end
286-
287207
@doc """
288-
Creates a comment for psot, job ...
208+
creates a comment for article like psot, job ...
289209
"""
290210
def create_article_comment(thread, article_id, content, %User{} = user) do
291211
with {:ok, info} <- match(thread),
@@ -307,6 +227,29 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do
307227
end
308228
end
309229

230+
@doc """
231+
update a comment for article like psot, job ...
232+
"""
233+
def update_article_comment(%ArticleComment{} = article_comment, content) do
234+
article_comment |> ORM.update(%{body_html: content})
235+
end
236+
237+
@doc "delete article comment"
238+
def delete_article_comment(%ArticleComment{} = comment) do
239+
Multi.new()
240+
|> Multi.run(:update_article_comments_count, fn _, _ ->
241+
update_article_comments_count(comment, :dec)
242+
end)
243+
|> Multi.run(:remove_pined_comment, fn _, _ ->
244+
ORM.findby_delete(ArticlePinedComment, %{article_comment_id: comment.id})
245+
end)
246+
|> Multi.run(:delete_article_comment, fn _, _ ->
247+
ORM.update(comment, %{body_html: @delete_hint, is_deleted: true})
248+
end)
249+
|> Repo.transaction()
250+
|> upsert_comment_result()
251+
end
252+
310253
@doc "reply to exsiting comment"
311254
def reply_article_comment(comment_id, content, %User{} = user) do
312255
with {:ok, target_comment} <-
@@ -704,7 +647,6 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do
704647
defp upsert_comment_result({:ok, %{add_reply_to: result}}), do: {:ok, result}
705648
defp upsert_comment_result({:ok, %{check_article_author_upvoted: result}}), do: {:ok, result}
706649
defp upsert_comment_result({:ok, %{update_report_flag: result}}), do: {:ok, result}
707-
defp upsert_comment_result({:ok, %{update_comment_emotion: result}}), do: {:ok, result}
708650
defp upsert_comment_result({:ok, %{update_comment_flag: result}}), do: {:ok, result}
709651
defp upsert_comment_result({:ok, %{delete_article_comment: result}}), do: {:ok, result}
710652

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
defmodule GroupherServer.CMS.Delegate.ArticleCommentEmotion do
2+
@moduledoc """
3+
CURD and operations for article comments
4+
"""
5+
import Ecto.Query, warn: false
6+
7+
alias Helper.ORM
8+
alias GroupherServer.{Accounts, CMS, Repo}
9+
10+
alias Accounts.User
11+
alias CMS.{ArticleComment, ArticleCommentUserEmotion}
12+
13+
alias Ecto.Multi
14+
15+
@type t_user_list :: [%{login: String.t()}]
16+
@type t_mention_status :: %{user_list: t_user_list, user_count: Integer.t()}
17+
18+
@max_latest_emotion_users_count ArticleComment.max_latest_emotion_users_count()
19+
20+
@doc "make emotion to a comment"
21+
def emotion_to_comment(comment_id, emotion, %User{} = user) do
22+
with {:ok, comment} <- ORM.find(ArticleComment, comment_id, preload: :author) do
23+
Multi.new()
24+
|> Multi.run(:create_user_emotion, fn _, _ ->
25+
target = %{
26+
article_comment_id: comment.id,
27+
recived_user_id: comment.author.id,
28+
user_id: user.id
29+
}
30+
31+
args = Map.put(target, :"#{emotion}", true)
32+
33+
case ORM.find_by(ArticleCommentUserEmotion, target) do
34+
{:ok, article_comment_user_emotion} -> article_comment_user_emotion |> ORM.update(args)
35+
{:error, _} -> ArticleCommentUserEmotion |> ORM.create(args)
36+
end
37+
end)
38+
|> Multi.run(:query_emotion_status, fn _, _ ->
39+
query_emotion_status(comment, emotion)
40+
end)
41+
|> Multi.run(:update_comment_emotion, fn _, %{query_emotion_status: status} ->
42+
update_comment_emotion(comment, emotion, status, user)
43+
end)
44+
|> Repo.transaction()
45+
|> update_emotion_result
46+
end
47+
end
48+
49+
def undo_emotion_to_comment(comment_id, emotion, %User{} = user) do
50+
with {:ok, comment} <- ORM.find(ArticleComment, comment_id, preload: :author) do
51+
Multi.new()
52+
|> Multi.run(:update_user_emotion, fn _, _ ->
53+
target = %{
54+
article_comment_id: comment.id,
55+
recived_user_id: comment.author.id,
56+
user_id: user.id
57+
}
58+
59+
{:ok, article_comment_user_emotion} = ORM.find_by(ArticleCommentUserEmotion, target)
60+
args = Map.put(target, :"#{emotion}", false)
61+
article_comment_user_emotion |> ORM.update(args)
62+
end)
63+
|> Multi.run(:query_emotion_status, fn _, _ ->
64+
query_emotion_status(comment, emotion)
65+
end)
66+
|> Multi.run(:update_comment_emotion, fn _, %{query_emotion_status: status} ->
67+
update_comment_emotion(comment, emotion, status, user)
68+
end)
69+
|> Repo.transaction()
70+
|> update_emotion_result
71+
end
72+
end
73+
74+
@spec query_emotion_status(ArticleComment.t(), Atom.t()) :: {:ok, t_mention_status}
75+
defp query_emotion_status(comment, emotion) do
76+
# 每次被 emotion 动作触发后重新查询,主要原因
77+
# 1.并发下保证数据准确,类似 views 阅读数的统计
78+
# 2. 前端使用 nickname 而非 login 展示,如果用户改了 nickname, 可以"自动纠正"
79+
query =
80+
from(a in ArticleCommentUserEmotion,
81+
join: user in User,
82+
on: a.user_id == user.id,
83+
where: a.article_comment_id == ^comment.id,
84+
where: field(a, ^emotion) == true,
85+
select: %{login: user.login, nickname: user.nickname}
86+
)
87+
88+
emotioned_user_info_list = Repo.all(query) |> Enum.uniq()
89+
emotioned_user_count = length(emotioned_user_info_list)
90+
91+
{:ok, %{user_list: emotioned_user_info_list, user_count: emotioned_user_count}}
92+
end
93+
94+
@spec update_comment_emotion(ArticleComment.t(), Atom.t(), t_mention_status, User.t()) ::
95+
{:ok, ArticleComment.t()} | {:error, any}
96+
defp update_comment_emotion(comment, emotion, status, user) do
97+
%{user_count: user_count, user_list: user_list} = status
98+
99+
emotions =
100+
%{}
101+
|> Map.put(:"#{emotion}_count", user_count)
102+
|> Map.put(:"#{emotion}_user_logins", user_list |> Enum.map(& &1.login))
103+
|> Map.put(
104+
:"latest_#{emotion}_users",
105+
Enum.slice(user_list, 0, @max_latest_emotion_users_count)
106+
)
107+
108+
viewer_has_emotioned = user.login in Map.get(emotions, :"#{emotion}_user_logins")
109+
emotions = emotions |> Map.put(:"viewer_has_#{emotion}ed", viewer_has_emotioned)
110+
111+
comment
112+
|> Ecto.Changeset.change()
113+
|> Ecto.Changeset.put_embed(:emotions, emotions)
114+
|> Repo.update()
115+
# virtual field can not be updated
116+
|> add_viewer_emotioned_ifneed(emotions)
117+
end
118+
119+
defp add_viewer_emotioned_ifneed({:error, error}, _), do: {:error, error}
120+
121+
defp add_viewer_emotioned_ifneed({:ok, comment}, emotions) do
122+
# Map.merge(comment, %{emotion: emotions})
123+
{:ok, Map.merge(comment, %{emotion: emotions})}
124+
end
125+
126+
defp update_emotion_result({:ok, %{update_comment_emotion: result}}), do: {:ok, result}
127+
128+
defp update_emotion_result({:error, _, result, _steps}) do
129+
{:error, result}
130+
end
131+
end

lib/groupher_server_web/middleware/passport_loader.ex

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ defmodule GroupherServerWeb.Middleware.PassportLoader do
99
import Helper.ErrorCode
1010
import ShortMaps
1111

12-
alias GroupherServer.CMS
1312
alias Helper.ORM
13+
alias GroupherServer.CMS
14+
15+
alias CMS.{ArticleComment}
1416

1517
def call(%{errors: errors} = resolution, _) when length(errors) > 0, do: resolution
1618

@@ -24,8 +26,20 @@ defmodule GroupherServerWeb.Middleware.PassportLoader do
2426
%{resolution | arguments: arguments}
2527

2628
{:error, err_msg} ->
29+
resolution |> handle_absinthe_error(err_msg, ecode(:passport))
30+
end
31+
end
32+
33+
@doc "load article comment"
34+
def call(%{context: %{cur_user: _}, arguments: ~m(id)a} = resolution, source: :article_comment) do
35+
case ORM.find(ArticleComment, id, preload: :author) do
36+
{:ok, article_comment} ->
2737
resolution
28-
|> handle_absinthe_error(err_msg, ecode(:passport))
38+
|> load_owner_info(:article_comment, article_comment)
39+
|> load_source(article_comment)
40+
41+
{:error, err_msg} ->
42+
resolution |> handle_absinthe_error(err_msg, ecode(:passport))
2943
end
3044
end
3145

@@ -83,6 +97,9 @@ defmodule GroupherServerWeb.Middleware.PassportLoader do
8397
def load_owner_info(%{context: %{cur_user: cur_user}} = resolution, react, content) do
8498
content_author_id =
8599
cond do
100+
react == :article_comment ->
101+
content.author.id
102+
86103
react == :comment ->
87104
content.author.id
88105

0 commit comments

Comments
 (0)