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

Commit dcd2cca

Browse files
authored
feat(reaction_user_list): upvote / collect / viewer is avalidble in article meta (#334)
* feat(reaction_user_list): upvote / collect is avalidble in article meta * refactor(viewer-solution): read-content -> read-article * refactor(viewer-solution): wip * refactor(viewer-solution): wip * refactor(viewer-solution): wip * refactor(viewer-solution): wip * refactor(viewer-solution): wip * refactor(viewer-solution): remove viewer states and test * refactor(viewer-solution): fix nil meta
1 parent 3b35013 commit dcd2cca

36 files changed

+552
-667
lines changed

lib/groupher_server/cms/cms.ex

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -70,17 +70,16 @@ defmodule GroupherServer.CMS do
7070
defdelegate subscribe_default_community_ifnot(user), to: CommunityOperation
7171

7272
# ArticleCURD
73-
defdelegate read_content(thread, id, user), to: ArticleCURD
74-
defdelegate paged_contents(queryable, filter, user), to: ArticleCURD
75-
defdelegate paged_contents(queryable, filter), to: ArticleCURD
73+
defdelegate read_article(thread, id), to: ArticleCURD
74+
defdelegate read_article(thread, id, user), to: ArticleCURD
75+
76+
defdelegate paged_articles(queryable, filter), to: ArticleCURD
77+
defdelegate paged_articles(queryable, filter, user), to: ArticleCURD
78+
7679
defdelegate create_content(community, thread, attrs, user), to: ArticleCURD
7780
defdelegate update_content(content, attrs), to: ArticleCURD
78-
defdelegate reaction_users(thread, react, id, filters), to: ArticleCURD
7981

8082
# ArticleReaction
81-
defdelegate reaction(thread, react, content_id, user), to: ArticleReaction
82-
defdelegate undo_reaction(thread, react, content_id, user), to: ArticleReaction
83-
8483
defdelegate upvote_article(thread, article_id, user), to: ArticleReaction
8584
defdelegate undo_upvote_article(thread, article_id, user), to: ArticleReaction
8685

lib/groupher_server/cms/delegates/article_curd.ex

Lines changed: 97 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -5,55 +5,97 @@ defmodule GroupherServer.CMS.Delegate.ArticleCURD do
55
import Ecto.Query, warn: false
66

77
import GroupherServer.CMS.Utils.Matcher2
8+
import GroupherServer.CMS.Utils.Matcher, only: [match_action: 2]
89

9-
import GroupherServer.CMS.Utils.Matcher, only: [match_action: 2, dynamic_where: 2]
1010
import Helper.Utils, only: [done: 1, pick_by: 2, integerfy: 1]
1111
import Helper.ErrorCode
12-
import ShortMaps
1312

13+
alias Helper.{Later, ORM}
1414
alias GroupherServer.{Accounts, CMS, Delivery, Email, Repo, Statistics}
1515

1616
alias Accounts.User
1717
alias CMS.{Author, Community, PinnedArticle, Embeds, Delegate, Tag}
1818

1919
alias Delegate.ArticleOperation
20-
alias Helper.{Later, ORM, QueryBuilder}
2120

2221
alias Ecto.Multi
2322

2423
@default_article_meta Embeds.ArticleMeta.default_meta()
2524

2625
@doc """
27-
login user read cms content by add views count and viewer record
26+
read articles for un-logined user
2827
"""
29-
def read_content(thread, id, %User{id: user_id}) do
30-
condition = %{user_id: user_id} |> Map.merge(content_id(thread, id))
31-
32-
with {:ok, action} <- match_action(thread, :self),
33-
{:ok, _viewer} <- action.viewer |> ORM.findby_or_insert(condition, condition) do
34-
action.target |> ORM.read(id, inc: :views)
28+
def read_article(thread, id) do
29+
with {:ok, info} <- match(thread) do
30+
ORM.read(info.model, id, inc: :views)
3531
end
3632
end
3733

3834
@doc """
39-
get paged post / job ...
35+
read articles for logined user
4036
"""
41-
def paged_contents(queryable, filter, user) do
42-
queryable
43-
|> domain_filter_query(filter)
44-
|> community_with_flag_query(filter)
45-
|> read_state_query(filter, user)
46-
|> ORM.find_all(filter)
47-
|> add_pin_contents_ifneed(queryable, filter)
37+
def read_article(thread, id, %User{id: user_id}) do
38+
with {:ok, info} <- match(thread) do
39+
Multi.new()
40+
|> Multi.run(:inc_views, fn _, _ ->
41+
ORM.read(info.model, id, inc: :views)
42+
end)
43+
|> Multi.run(:add_viewed_user, fn _, %{inc_views: article} ->
44+
update_viewed_user_list(article, user_id)
45+
end)
46+
|> Repo.transaction()
47+
|> read_result()
48+
end
4849
end
4950

50-
def paged_contents(queryable, filter) do
51-
queryable
52-
|> domain_filter_query(filter)
53-
|> community_with_flag_query(filter)
54-
|> ORM.find_all(filter)
55-
# TODO: if filter has when/sort/length/job... then don't
56-
|> add_pin_contents_ifneed(queryable, filter)
51+
def paged_articles(thread, filter) do
52+
with {:ok, info} <- match(thread) do
53+
info.model
54+
|> domain_filter_query(filter)
55+
|> community_with_flag_query(filter)
56+
|> ORM.find_all(filter)
57+
|> add_pin_contents_ifneed(info.model, filter)
58+
end
59+
end
60+
61+
def paged_articles(thread, filter, %User{} = user) do
62+
with {:ok, info} <- match(thread) do
63+
info.model
64+
|> domain_filter_query(filter)
65+
|> community_with_flag_query(filter)
66+
|> ORM.find_all(filter)
67+
|> add_pin_contents_ifneed(info.model, filter)
68+
|> mark_viewer_has_states(user)
69+
end
70+
end
71+
72+
defp mark_viewer_has_states({:ok, %{entries: []} = contents}, _), do: {:ok, contents}
73+
74+
defp mark_viewer_has_states({:ok, %{entries: entries} = contents}, user) do
75+
entries = Enum.map(entries, &Map.merge(&1, do_mark_viewer_has_states(&1.meta, user)))
76+
{:ok, Map.merge(contents, %{entries: entries})}
77+
end
78+
79+
defp mark_viewer_has_states({:error, reason}, _), do: {:error, reason}
80+
81+
defp do_mark_viewer_has_states(nil, _) do
82+
%{
83+
viewer_has_collected: false,
84+
viewer_has_upvoted: false,
85+
viewer_has_viewed: false,
86+
viewer_has_reported: false
87+
}
88+
end
89+
90+
defp do_mark_viewer_has_states(meta, %User{id: user_id}) do
91+
# TODO: 根据是否付费进一步判断
92+
# user_is_member = true
93+
%{
94+
viewer_has_collected: Enum.member?(meta.collected_user_ids, user_id),
95+
viewer_has_upvoted: Enum.member?(meta.upvoted_user_ids, user_id),
96+
viewer_has_viewed: Enum.member?(meta.viewed_user_ids, user_id),
97+
viewer_has_reported: Enum.member?(meta.reported_user_ids, user_id)
98+
}
5799
end
58100

59101
@doc """
@@ -246,19 +288,6 @@ defmodule GroupherServer.CMS.Delegate.ArticleCURD do
246288

247289
defp domain_filter_query(queryable, _filter), do: queryable
248290

249-
# query if user has viewed before
250-
defp read_state_query(queryable, %{read: true} = _filter, user) do
251-
queryable
252-
|> join(:inner, [content, f, c], viewers in assoc(content, :viewers))
253-
|> where([content, f, c, viewers], viewers.user_id == ^user.id)
254-
end
255-
256-
defp read_state_query(queryable, %{read: false} = _filter, _user) do
257-
queryable
258-
end
259-
260-
defp read_state_query(queryable, _, _), do: queryable
261-
262291
defp add_pin_contents_ifneed(contents, querable, %{community: _community} = filter) do
263292
with {:ok, _} <- should_add_pin?(filter),
264293
{:ok, info} <- match(querable),
@@ -285,10 +314,10 @@ defmodule GroupherServer.CMS.Delegate.ArticleCURD do
285314
defp add_pin_contents_ifneed(contents, _querable, _filter), do: contents
286315

287316
# if filter contains like: tags, sort.., then don't add pin content
288-
defp should_add_pin?(%{page: 1, tag: :all, sort: :desc_inserted, read: :all} = filter) do
317+
defp should_add_pin?(%{page: 1, tag: :all, sort: :desc_inserted} = filter) do
289318
filter
290319
|> Map.keys()
291-
|> Enum.reject(fn x -> x in [:community, :tag, :sort, :read, :page, :size] end)
320+
|> Enum.reject(fn x -> x in [:community, :tag, :sort, :page, :size] end)
292321
|> case do
293322
[] -> {:ok, :pass}
294323
_ -> {:error, :pass}
@@ -355,10 +384,6 @@ defmodule GroupherServer.CMS.Delegate.ArticleCURD do
355384
defp update_content_result({:error, :update_content, result, _steps}), do: {:error, result}
356385
defp update_content_result({:error, :update_tag, result, _steps}), do: {:error, result}
357386

358-
defp content_id(:post, id), do: %{post_id: id}
359-
defp content_id(:job, id), do: %{job_id: id}
360-
defp content_id(:repo, id), do: %{repo_id: id}
361-
362387
# for create content step in Multi.new
363388
defp do_create_content(target, attrs, %Author{id: aid}, %Community{id: cid}) do
364389
target
@@ -417,4 +442,32 @@ defmodule GroupherServer.CMS.Delegate.ArticleCURD do
417442
end
418443

419444
defp exec_update_tags(_content, _), do: {:ok, :pass}
445+
446+
defp update_viewed_user_list(%{meta: nil} = article, user_id) do
447+
new_ids = Enum.uniq([user_id] ++ @default_article_meta.viewed_user_ids)
448+
updated_meta = @default_article_meta |> Map.merge(%{viewed_user_ids: new_ids})
449+
450+
article
451+
|> Ecto.Changeset.change()
452+
|> Ecto.Changeset.put_embed(:meta, updated_meta)
453+
|> Repo.update()
454+
end
455+
456+
defp update_viewed_user_list(%{meta: meta} = article, user_id) do
457+
new_ids = Enum.uniq([user_id] ++ meta.viewed_user_ids)
458+
459+
updated_meta =
460+
meta |> Map.merge(%{viewed_user_ids: new_ids}) |> Map.from_struct() |> Map.delete(:id)
461+
462+
article
463+
|> Ecto.Changeset.change()
464+
|> Ecto.Changeset.put_embed(:meta, updated_meta)
465+
|> Repo.update()
466+
end
467+
468+
defp read_result({:ok, %{inc_views: result}}), do: result |> done()
469+
470+
defp read_result({:error, _, result, _steps}) do
471+
{:error, result}
472+
end
420473
end

lib/groupher_server/cms/delegates/article_reaction.ex

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,23 @@ defmodule GroupherServer.CMS.Delegate.ArticleReaction do
22
@moduledoc """
33
reaction[upvote, collect, watch ...] on article [post, job...]
44
"""
5-
import Helper.Utils, only: [done: 1, done: 2]
5+
import Helper.Utils, only: [done: 1]
66

77
import GroupherServer.CMS.Utils.Matcher2
8-
import GroupherServer.CMS.Utils.Matcher, only: [match_action: 2]
98
import Ecto.Query, warn: false
10-
import Helper.ErrorCode
9+
# import Helper.ErrorCode
1110
import ShortMaps
1211

1312
alias Helper.{ORM, QueryBuilder}
1413
alias GroupherServer.{Accounts, CMS, Repo}
1514

1615
alias Accounts.User
17-
alias CMS.{ArticleUpvote, ArticleCollect}
16+
alias CMS.{ArticleUpvote, ArticleCollect, Embeds}
1817

1918
alias Ecto.Multi
2019

20+
@default_article_meta Embeds.ArticleMeta.default_meta()
21+
2122
def upvoted_users(thread, article_id, filter) do
2223
load_reaction_users(ArticleUpvote, thread, article_id, filter)
2324
end
@@ -46,6 +47,9 @@ defmodule GroupherServer.CMS.Delegate.ArticleReaction do
4647
|> Multi.run(:inc_article_collects_count, fn _, _ ->
4748
update_article_upvotes_count(info, article, :collects_count, :inc)
4849
end)
50+
|> Multi.run(:update_article_reaction_user_list, fn _, _ ->
51+
update_article_reaction_user_list(:collect, article, user_id, :add)
52+
end)
4953
|> Multi.run(:create_collect, fn _, _ ->
5054
thread_upcase = thread |> to_string |> String.upcase()
5155
args = Map.put(%{user_id: user_id, thread: thread_upcase}, info.foreign_key, article.id)
@@ -82,6 +86,9 @@ defmodule GroupherServer.CMS.Delegate.ArticleReaction do
8286
|> Multi.run(:inc_article_collects_count, fn _, _ ->
8387
update_article_upvotes_count(info, article, :collects_count, :dec)
8488
end)
89+
|> Multi.run(:update_article_reaction_user_list, fn _, _ ->
90+
update_article_reaction_user_list(:collect, article, user_id, :remove)
91+
end)
8592
|> Multi.run(:undo_collect, fn _, _ ->
8693
args = Map.put(%{user_id: user_id}, info.foreign_key, article.id)
8794

@@ -142,6 +149,9 @@ defmodule GroupherServer.CMS.Delegate.ArticleReaction do
142149
|> Multi.run(:inc_article_upvotes_count, fn _, _ ->
143150
update_article_upvotes_count(info, article, :upvotes_count, :inc)
144151
end)
152+
|> Multi.run(:update_article_reaction_user_list, fn _, _ ->
153+
update_article_reaction_user_list(:upvot, article, user_id, :add)
154+
end)
145155
|> Multi.run(:add_achievement, fn _, _ ->
146156
achiever_id = article.author.user_id
147157
Accounts.achieve(%User{id: achiever_id}, :inc, :upvote)
@@ -167,6 +177,9 @@ defmodule GroupherServer.CMS.Delegate.ArticleReaction do
167177
|> Multi.run(:inc_article_upvotes_count, fn _, _ ->
168178
update_article_upvotes_count(info, article, :upvotes_count, :dec)
169179
end)
180+
|> Multi.run(:update_article_reaction_user_list, fn _, _ ->
181+
update_article_reaction_user_list(:upvot, article, user_id, :remove)
182+
end)
170183
|> Multi.run(:undo_upvote, fn _, _ ->
171184
args = Map.put(%{user_id: user_id}, info.foreign_key, article.id)
172185

@@ -199,6 +212,56 @@ defmodule GroupherServer.CMS.Delegate.ArticleReaction do
199212
end
200213
end
201214

215+
@doc """
216+
add or remove artilce's reaction users is list history
217+
e.g:
218+
add/remove user_id to upvoted_user_ids in article meta
219+
"""
220+
@spec update_article_reaction_user_list(
221+
:upvot | :collect,
222+
T.article_common(),
223+
String.t(),
224+
:add | :remove
225+
) :: T.article_common()
226+
defp update_article_reaction_user_list(action, %{meta: nil} = article, user_id, opt) do
227+
cur_user_ids = []
228+
229+
updated_user_ids =
230+
case opt do
231+
:add -> [user_id] ++ cur_user_ids
232+
:remove -> cur_user_ids -- [user_id]
233+
end
234+
235+
updated_meta = @default_article_meta |> Map.merge(%{"#{action}ed_user_ids": updated_user_ids})
236+
237+
do_update_article_meta(article, updated_meta)
238+
end
239+
240+
defp update_article_reaction_user_list(action, article, user_id, opt) do
241+
cur_user_ids = get_in(article, [:meta, :"#{action}ed_user_ids"])
242+
243+
updated_user_ids =
244+
case opt do
245+
:add -> [user_id] ++ cur_user_ids
246+
:remove -> cur_user_ids -- [user_id]
247+
end
248+
249+
meta =
250+
article.meta
251+
|> Map.merge(%{"#{action}ed_user_ids": updated_user_ids})
252+
|> Map.from_struct()
253+
|> Map.delete(:id)
254+
255+
do_update_article_meta(article, meta)
256+
end
257+
258+
defp do_update_article_meta(article, meta) do
259+
article
260+
|> Ecto.Changeset.change()
261+
|> Ecto.Changeset.put_embed(:meta, meta)
262+
|> Repo.update()
263+
end
264+
202265
defp reaction_result({:ok, %{create_upvote: result}}), do: result |> done()
203266
defp reaction_result({:ok, %{undo_upvote: result}}), do: result |> done()
204267

lib/groupher_server/cms/embeds/article_meta.ex

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,19 @@ defmodule GroupherServer.CMS.Embeds.ArticleMeta do
33
general article meta info for article-like content, like post, job, works ...
44
"""
55
use Ecto.Schema
6+
use Accessible
67
import Ecto.Changeset
78

8-
@optional_fields ~w(is_edited is_comment_locked is_reported)a
9+
@optional_fields ~w(is_edited is_comment_locked is_reported upvoted_user_ids collected_user_ids viewed_user_ids reported_user_ids)a
910

1011
@default_meta %{
1112
is_edited: false,
1213
is_comment_locked: false,
13-
is_reported: false
14+
is_reported: false,
15+
upvoted_user_ids: [],
16+
collected_user_ids: [],
17+
viewed_user_ids: [],
18+
reported_user_ids: []
1419
}
1520

1621
@doc "for test usage"
@@ -20,6 +25,11 @@ defmodule GroupherServer.CMS.Embeds.ArticleMeta do
2025
field(:is_edited, :boolean, default: false)
2126
field(:is_comment_locked, :boolean, default: false)
2227
field(:is_reported, :boolean, default: false)
28+
# reaction history
29+
field(:upvoted_user_ids, {:array, :integer}, default: [])
30+
field(:collected_user_ids, {:array, :integer}, default: [])
31+
field(:viewed_user_ids, {:array, :integer}, default: [])
32+
field(:reported_user_ids, {:array, :integer}, default: [])
2333
end
2434

2535
def changeset(struct, params) do

lib/groupher_server/cms/job.ex

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ defmodule GroupherServer.CMS.Job do
1414
Embeds,
1515
ArticleComment,
1616
Community,
17-
JobViewer,
1817
JobCommunityFlag,
1918
Tag,
2019
ArticleUpvote,
@@ -69,8 +68,6 @@ defmodule GroupherServer.CMS.Job do
6968
field(:article_comments_count, :integer, default: 0)
7069
field(:article_comments_participators_count, :integer, default: 0)
7170

72-
has_many(:viewers, {"jobs_viewers", JobViewer})
73-
7471
many_to_many(
7572
:tags,
7673
Tag,

0 commit comments

Comments
 (0)