Skip to content
This repository was archived by the owner on Nov 8, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 6 additions & 7 deletions lib/groupher_server/cms/cms.ex
Original file line number Diff line number Diff line change
Expand Up @@ -70,17 +70,16 @@ defmodule GroupherServer.CMS do
defdelegate subscribe_default_community_ifnot(user), to: CommunityOperation

# ArticleCURD
defdelegate read_content(thread, id, user), to: ArticleCURD
defdelegate paged_contents(queryable, filter, user), to: ArticleCURD
defdelegate paged_contents(queryable, filter), to: ArticleCURD
defdelegate read_article(thread, id), to: ArticleCURD
defdelegate read_article(thread, id, user), to: ArticleCURD

defdelegate paged_articles(queryable, filter), to: ArticleCURD
defdelegate paged_articles(queryable, filter, user), to: ArticleCURD

defdelegate create_content(community, thread, attrs, user), to: ArticleCURD
defdelegate update_content(content, attrs), to: ArticleCURD
defdelegate reaction_users(thread, react, id, filters), to: ArticleCURD

# ArticleReaction
defdelegate reaction(thread, react, content_id, user), to: ArticleReaction
defdelegate undo_reaction(thread, react, content_id, user), to: ArticleReaction

defdelegate upvote_article(thread, article_id, user), to: ArticleReaction
defdelegate undo_upvote_article(thread, article_id, user), to: ArticleReaction

Expand Down
141 changes: 97 additions & 44 deletions lib/groupher_server/cms/delegates/article_curd.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,55 +5,97 @@ defmodule GroupherServer.CMS.Delegate.ArticleCURD do
import Ecto.Query, warn: false

import GroupherServer.CMS.Utils.Matcher2
import GroupherServer.CMS.Utils.Matcher, only: [match_action: 2]

import GroupherServer.CMS.Utils.Matcher, only: [match_action: 2, dynamic_where: 2]
import Helper.Utils, only: [done: 1, pick_by: 2, integerfy: 1]
import Helper.ErrorCode
import ShortMaps

alias Helper.{Later, ORM}
alias GroupherServer.{Accounts, CMS, Delivery, Email, Repo, Statistics}

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

alias Delegate.ArticleOperation
alias Helper.{Later, ORM, QueryBuilder}

alias Ecto.Multi

@default_article_meta Embeds.ArticleMeta.default_meta()

@doc """
login user read cms content by add views count and viewer record
read articles for un-logined user
"""
def read_content(thread, id, %User{id: user_id}) do
condition = %{user_id: user_id} |> Map.merge(content_id(thread, id))

with {:ok, action} <- match_action(thread, :self),
{:ok, _viewer} <- action.viewer |> ORM.findby_or_insert(condition, condition) do
action.target |> ORM.read(id, inc: :views)
def read_article(thread, id) do
with {:ok, info} <- match(thread) do
ORM.read(info.model, id, inc: :views)
end
end

@doc """
get paged post / job ...
read articles for logined user
"""
def paged_contents(queryable, filter, user) do
queryable
|> domain_filter_query(filter)
|> community_with_flag_query(filter)
|> read_state_query(filter, user)
|> ORM.find_all(filter)
|> add_pin_contents_ifneed(queryable, filter)
def read_article(thread, id, %User{id: user_id}) do
with {:ok, info} <- match(thread) do
Multi.new()
|> Multi.run(:inc_views, fn _, _ ->
ORM.read(info.model, id, inc: :views)
end)
|> Multi.run(:add_viewed_user, fn _, %{inc_views: article} ->
update_viewed_user_list(article, user_id)
end)
|> Repo.transaction()
|> read_result()
end
end

def paged_contents(queryable, filter) do
queryable
|> domain_filter_query(filter)
|> community_with_flag_query(filter)
|> ORM.find_all(filter)
# TODO: if filter has when/sort/length/job... then don't
|> add_pin_contents_ifneed(queryable, filter)
def paged_articles(thread, filter) do
with {:ok, info} <- match(thread) do
info.model
|> domain_filter_query(filter)
|> community_with_flag_query(filter)
|> ORM.find_all(filter)
|> add_pin_contents_ifneed(info.model, filter)
end
end

def paged_articles(thread, filter, %User{} = user) do
with {:ok, info} <- match(thread) do
info.model
|> domain_filter_query(filter)
|> community_with_flag_query(filter)
|> ORM.find_all(filter)
|> add_pin_contents_ifneed(info.model, filter)
|> mark_viewer_has_states(user)
end
end

defp mark_viewer_has_states({:ok, %{entries: []} = contents}, _), do: {:ok, contents}

defp mark_viewer_has_states({:ok, %{entries: entries} = contents}, user) do
entries = Enum.map(entries, &Map.merge(&1, do_mark_viewer_has_states(&1.meta, user)))
{:ok, Map.merge(contents, %{entries: entries})}
end

defp mark_viewer_has_states({:error, reason}, _), do: {:error, reason}

defp do_mark_viewer_has_states(nil, _) do
%{
viewer_has_collected: false,
viewer_has_upvoted: false,
viewer_has_viewed: false,
viewer_has_reported: false
}
end

defp do_mark_viewer_has_states(meta, %User{id: user_id}) do
# TODO: 根据是否付费进一步判断
# user_is_member = true
%{
viewer_has_collected: Enum.member?(meta.collected_user_ids, user_id),
viewer_has_upvoted: Enum.member?(meta.upvoted_user_ids, user_id),
viewer_has_viewed: Enum.member?(meta.viewed_user_ids, user_id),
viewer_has_reported: Enum.member?(meta.reported_user_ids, user_id)
}
end

@doc """
Expand Down Expand Up @@ -246,19 +288,6 @@ defmodule GroupherServer.CMS.Delegate.ArticleCURD do

defp domain_filter_query(queryable, _filter), do: queryable

# query if user has viewed before
defp read_state_query(queryable, %{read: true} = _filter, user) do
queryable
|> join(:inner, [content, f, c], viewers in assoc(content, :viewers))
|> where([content, f, c, viewers], viewers.user_id == ^user.id)
end

defp read_state_query(queryable, %{read: false} = _filter, _user) do
queryable
end

defp read_state_query(queryable, _, _), do: queryable

defp add_pin_contents_ifneed(contents, querable, %{community: _community} = filter) do
with {:ok, _} <- should_add_pin?(filter),
{:ok, info} <- match(querable),
Expand All @@ -285,10 +314,10 @@ defmodule GroupherServer.CMS.Delegate.ArticleCURD do
defp add_pin_contents_ifneed(contents, _querable, _filter), do: contents

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

defp content_id(:post, id), do: %{post_id: id}
defp content_id(:job, id), do: %{job_id: id}
defp content_id(:repo, id), do: %{repo_id: id}

# for create content step in Multi.new
defp do_create_content(target, attrs, %Author{id: aid}, %Community{id: cid}) do
target
Expand Down Expand Up @@ -417,4 +442,32 @@ defmodule GroupherServer.CMS.Delegate.ArticleCURD do
end

defp exec_update_tags(_content, _), do: {:ok, :pass}

defp update_viewed_user_list(%{meta: nil} = article, user_id) do
new_ids = Enum.uniq([user_id] ++ @default_article_meta.viewed_user_ids)
updated_meta = @default_article_meta |> Map.merge(%{viewed_user_ids: new_ids})

article
|> Ecto.Changeset.change()
|> Ecto.Changeset.put_embed(:meta, updated_meta)
|> Repo.update()
end

defp update_viewed_user_list(%{meta: meta} = article, user_id) do
new_ids = Enum.uniq([user_id] ++ meta.viewed_user_ids)

updated_meta =
meta |> Map.merge(%{viewed_user_ids: new_ids}) |> Map.from_struct() |> Map.delete(:id)

article
|> Ecto.Changeset.change()
|> Ecto.Changeset.put_embed(:meta, updated_meta)
|> Repo.update()
end

defp read_result({:ok, %{inc_views: result}}), do: result |> done()

defp read_result({:error, _, result, _steps}) do
{:error, result}
end
end
71 changes: 67 additions & 4 deletions lib/groupher_server/cms/delegates/article_reaction.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,23 @@ defmodule GroupherServer.CMS.Delegate.ArticleReaction do
@moduledoc """
reaction[upvote, collect, watch ...] on article [post, job...]
"""
import Helper.Utils, only: [done: 1, done: 2]
import Helper.Utils, only: [done: 1]

import GroupherServer.CMS.Utils.Matcher2
import GroupherServer.CMS.Utils.Matcher, only: [match_action: 2]
import Ecto.Query, warn: false
import Helper.ErrorCode
# import Helper.ErrorCode
import ShortMaps

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

alias Accounts.User
alias CMS.{ArticleUpvote, ArticleCollect}
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
Expand Down Expand Up @@ -46,6 +47,9 @@ defmodule GroupherServer.CMS.Delegate.ArticleReaction do
|> 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)
Expand Down Expand Up @@ -82,6 +86,9 @@ defmodule GroupherServer.CMS.Delegate.ArticleReaction do
|> 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)

Expand Down Expand Up @@ -142,6 +149,9 @@ defmodule GroupherServer.CMS.Delegate.ArticleReaction do
|> 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)
Expand All @@ -167,6 +177,9 @@ defmodule GroupherServer.CMS.Delegate.ArticleReaction do
|> 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)

Expand Down Expand Up @@ -199,6 +212,56 @@ defmodule GroupherServer.CMS.Delegate.ArticleReaction do
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

updated_meta = @default_article_meta |> Map.merge(%{"#{action}ed_user_ids": updated_user_ids})

do_update_article_meta(article, updated_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})
|> Map.from_struct()
|> Map.delete(:id)

do_update_article_meta(article, meta)
end

defp do_update_article_meta(article, meta) do
article
|> Ecto.Changeset.change()
|> Ecto.Changeset.put_embed(:meta, meta)
|> Repo.update()
end

defp reaction_result({:ok, %{create_upvote: result}}), do: result |> done()
defp reaction_result({:ok, %{undo_upvote: result}}), do: result |> done()

Expand Down
14 changes: 12 additions & 2 deletions lib/groupher_server/cms/embeds/article_meta.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,19 @@ defmodule GroupherServer.CMS.Embeds.ArticleMeta do
general article meta info for article-like content, like post, job, works ...
"""
use Ecto.Schema
use Accessible
import Ecto.Changeset

@optional_fields ~w(is_edited is_comment_locked is_reported)a
@optional_fields ~w(is_edited is_comment_locked is_reported upvoted_user_ids collected_user_ids viewed_user_ids reported_user_ids)a

@default_meta %{
is_edited: false,
is_comment_locked: false,
is_reported: false
is_reported: false,
upvoted_user_ids: [],
collected_user_ids: [],
viewed_user_ids: [],
reported_user_ids: []
}

@doc "for test usage"
Expand All @@ -20,6 +25,11 @@ defmodule GroupherServer.CMS.Embeds.ArticleMeta do
field(:is_edited, :boolean, default: false)
field(:is_comment_locked, :boolean, default: false)
field(:is_reported, :boolean, default: false)
# reaction history
field(:upvoted_user_ids, {:array, :integer}, default: [])
field(:collected_user_ids, {:array, :integer}, default: [])
field(:viewed_user_ids, {:array, :integer}, default: [])
field(:reported_user_ids, {:array, :integer}, default: [])
end

def changeset(struct, params) do
Expand Down
3 changes: 0 additions & 3 deletions lib/groupher_server/cms/job.ex
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ defmodule GroupherServer.CMS.Job do
Embeds,
ArticleComment,
Community,
JobViewer,
JobCommunityFlag,
Tag,
ArticleUpvote,
Expand Down Expand Up @@ -69,8 +68,6 @@ defmodule GroupherServer.CMS.Job do
field(:article_comments_count, :integer, default: 0)
field(:article_comments_participators_count, :integer, default: 0)

has_many(:viewers, {"jobs_viewers", JobViewer})

many_to_many(
:tags,
Tag,
Expand Down
Loading