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
Show all changes
22 commits
Select commit Hold shift + click to select a range
b2328ff
refactor(articles): add active_at timestamp
mydearxym May 31, 2021
284f987
refactor(articles): fix timestamp fmt
mydearxym May 31, 2021
43d8a3a
refactor(articles): test active_at field in gq workflow
mydearxym May 31, 2021
7e53708
refactor(articles): update active_at after create comment
mydearxym May 31, 2021
f64d72d
refactor(articles): active_period_days config & logic
mydearxym May 31, 2021
bd6f3a0
feat(active_at): use active_at as default sort
mydearxym Jun 1, 2021
f92783e
feat(active_at): add sort args in PageSizeProof
mydearxym Jun 1, 2021
53a51d5
feat(active_at): add sort args in PageSizeProof
mydearxym Jun 1, 2021
65be412
feat(active_at): fix active_at filter tests
mydearxym Jun 1, 2021
2861d9f
fix(active_at): some test error
mydearxym Jun 1, 2021
73b8320
fix(active_at): some test error
mydearxym Jun 1, 2021
0d292b2
fix(active_at): some test error
mydearxym Jun 1, 2021
e8709fd
fix(active_at): adjust should_add_pin logic by use active_at
mydearxym Jun 1, 2021
9d82cfb
fix(active_at): filter time shift test error
mydearxym Jun 1, 2021
2192204
fix(active_at): clean up
mydearxym Jun 1, 2021
5894b24
fix(active_at): paged filter test
mydearxym Jun 1, 2021
14f5f4e
fix(active_at): basic sink/undo_sink
mydearxym Jun 1, 2021
7f8770b
fix(active_at): wip
mydearxym Jun 1, 2021
14303d4
fix(active_at): fix test
mydearxym Jun 1, 2021
0efe9f5
fix(active_at): last_active_at concept
mydearxym Jun 1, 2021
70cf4e3
fix(active_at): adjust lock_article_comment arg & fmt
mydearxym Jun 1, 2021
bd7d0b9
fix(active_at): sink gq workflow
mydearxym Jun 1, 2021
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
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,8 @@ test.watch:
mix test.watch
test.watch.wip:
# work around, see: https://elixirforum.com/t/mix-test-file-watch/12298/2
mix test --listen-on-stdin --stale --trace --only wip
# mix test --listen-on-stdin --stale --trace --only wip
mix test --listen-on-stdin --stale --only wip
# test.watch not work now, see: https://github.com/lpil/mix-test.watch/issues/116
# mix test.watch --only wip --stale
test.watch.wip2:
Expand Down
8 changes: 8 additions & 0 deletions config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ config :groupher_server, :customization,
config :groupher_server, :article,
# NOTE: do not change unless you know what you are doing
article_threads: [:post, :job, :repo],
# in this period, paged articles will sort front if non-article-author commented
# 在此时间段内,一旦有非文章作者的用户评论,该文章就会排到前面
active_period_days: %{
post: 10,
job: 10,
repo: 10
},

# NOTE: if you want to add/remove emotion, just edit the list below
# and migrate the field to table "articles_users_emotions"
supported_emotions: [
Expand Down
6 changes: 5 additions & 1 deletion lib/groupher_server/cms/cms.ex
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ defmodule GroupherServer.CMS do
defdelegate mark_delete_article(thread, id), to: ArticleCURD
defdelegate undo_mark_delete_article(thread, id), to: ArticleCURD

defdelegate update_active_timestamp(thread, article), to: ArticleCURD
defdelegate sink_article(thread, id), to: ArticleCURD
defdelegate undo_sink_article(thread, id), to: ArticleCURD

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

Expand All @@ -112,7 +116,7 @@ defmodule GroupherServer.CMS do
# >> set flag on article, like: pin / unpin article
defdelegate pin_article(thread, id, community_id), to: ArticleCommunity
defdelegate undo_pin_article(thread, id, community_id), to: ArticleCommunity
defdelegate lock_article_comment(article), to: ArticleCommunity
defdelegate lock_article_comment(thread, article_id), to: ArticleCommunity

# >> community: set / unset
defdelegate mirror_article(thread, article_id, community_id), to: ArticleCommunity
Expand Down
10 changes: 6 additions & 4 deletions lib/groupher_server/cms/delegates/article_comment.ex
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,12 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do
|> Multi.run(:add_participator, fn _, _ ->
add_participator_to_article(article, user)
end)
|> Multi.run(:update_article_active_timestamp, fn _, %{create_article_comment: comment} ->
case comment.author_id == article.author.user.id do
true -> {:ok, :pass}
false -> CMS.update_active_timestamp(thread, article)
end
end)
|> Repo.transaction()
|> result()
end
Expand Down Expand Up @@ -284,9 +290,5 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do
raise_error(:create_comment, result)
end

defp result({:error, :add_participator, result, _steps}) do
{:error, result}
end

defp result({:error, _, result, _steps}), do: {:error, result}
end
22 changes: 9 additions & 13 deletions lib/groupher_server/cms/delegates/article_community.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleCommunity do
import Ecto.Query, warn: false

import Helper.ErrorCode
import Helper.Utils, only: [strip_struct: 1, done: 1]
import Helper.Utils, only: [strip_struct: 1, done: 1, ensure: 2]
import GroupherServer.CMS.Helper.Matcher

alias Helper.Types, as: T
Expand All @@ -17,6 +17,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleCommunity do

alias Ecto.Multi

@default_article_meta Embeds.ArticleMeta.default_meta()
@max_pinned_article_count_per_thread Community.max_pinned_article_count_per_thread()

@spec pin_article(T.article_thread(), Integer.t(), Integer.t()) :: {:ok, PinnedArticle.t()}
Expand Down Expand Up @@ -144,21 +145,16 @@ defmodule GroupherServer.CMS.Delegate.ArticleCommunity do
def update_edit_status(content, _), do: {:ok, content}

@doc "lock comment of a article"
# TODO: record it to ArticleLog
def lock_article_comment(
%{meta: %Embeds.ArticleMeta{is_comment_locked: false} = meta} = content
) do
meta =
meta
|> Map.from_struct()
|> Map.delete(:id)
|> Map.merge(%{is_comment_locked: true})
def lock_article_comment(thread, id) do
with {:ok, info} <- match(thread),
{:ok, article} <- ORM.find(info.model, id) do
article_meta = ensure(article.meta, @default_article_meta)
meta = Map.merge(article_meta, %{is_comment_locked: true})

ORM.update_meta(content, meta)
ORM.update_meta(article, meta)
end
end

def lock_article_comment(content), do: {:ok, content}

# check if the thread has aready enough pinned articles
defp check_pinned_article_count(community_id, thread) do
thread_upcase = thread |> to_string |> String.upcase()
Expand Down
138 changes: 103 additions & 35 deletions lib/groupher_server/cms/delegates/article_curd.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,15 @@ defmodule GroupherServer.CMS.Delegate.ArticleCURD do
import GroupherServer.CMS.Helper.Matcher

import Helper.Utils,
only: [done: 1, pick_by: 2, integerfy: 1, strip_struct: 1, module_to_thread: 1]
only: [
done: 1,
pick_by: 2,
integerfy: 1,
strip_struct: 1,
module_to_thread: 1,
get_config: 2,
ensure: 2
]

import GroupherServer.CMS.Delegate.Helper, only: [mark_viewer_emotion_states: 2]
import Helper.ErrorCode
Expand All @@ -23,6 +31,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleCURD do

alias Ecto.Multi

@active_period get_config(:article, :active_period_days)
@default_emotions Embeds.ArticleEmotion.default_emotions()
@default_article_meta Embeds.ArticleMeta.default_meta()

Expand All @@ -31,7 +40,16 @@ defmodule GroupherServer.CMS.Delegate.ArticleCURD do
"""
def read_article(thread, id) do
with {:ok, info} <- match(thread) do
ORM.read(info.model, id, inc: :views)
Multi.new()
|> Multi.run(:inc_views, fn _, _ -> ORM.read(info.model, id, inc: :views) end)
|> Multi.run(:update_article_meta, fn _, %{inc_views: article} ->
article_meta = ensure(article.meta, @default_article_meta)
meta = Map.merge(article_meta, %{can_undo_sink: in_active_period?(thread, article)})

ORM.update_meta(article, meta)
end)
|> Repo.transaction()
|> result()
end
end

Expand All @@ -41,8 +59,12 @@ defmodule GroupherServer.CMS.Delegate.ArticleCURD do
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)
|> Multi.run(:inc_views, fn _, _ -> ORM.read(info.model, id, inc: :views) end)
|> Multi.run(:update_article_meta, fn _, %{inc_views: article} ->
article_meta = ensure(article.meta, @default_article_meta)
meta = Map.merge(article_meta, %{can_undo_sink: in_active_period?(thread, article)})

ORM.update_meta(article, meta)
end)
|> Multi.run(:add_viewed_user, fn _, %{inc_views: article} ->
update_viewed_user_list(article, user_id)
Expand Down Expand Up @@ -142,6 +164,9 @@ defmodule GroupherServer.CMS.Delegate.ArticleCURD do
|> Multi.run(:set_article_tags, fn _, %{create_article: article} ->
ArticleTag.set_article_tags(community, thread, article, attrs)
end)
|> Multi.run(:set_active_at_timestamp, fn _, %{create_article: article} ->
ORM.update(article, %{active_at: article.inserted_at})
end)
|> Multi.run(:update_community_article_count, fn _, _ ->
CommunityCURD.update_community_count_field(community, thread)
end)
Expand All @@ -156,7 +181,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleCURD do
Statistics.log_publish_action(%User{id: uid})
end)
|> Repo.transaction()
|> create_article_result()
|> result()
end
end

Expand Down Expand Up @@ -199,6 +224,55 @@ defmodule GroupherServer.CMS.Delegate.ArticleCURD do
|> result()
end

@doc """
update active at timestamp of an article
"""
def update_active_timestamp(thread, article) do
# @article_active_period
# 1. 超过时限不更新
# 2. 已经沉默的不更新, is_sinked
with true <- in_active_period?(thread, article) do
ORM.update(article, %{active_at: DateTime.utc_now()})
else
_ -> {:ok, :pass}
end
end

@doc """
sink article
"""
def sink_article(thread, id) do
with {:ok, info} <- match(thread),
{:ok, article} <- ORM.find(info.model, id) do
meta = Map.merge(article.meta, %{is_sinked: true, last_active_at: article.active_at})
ORM.update_meta(article, meta, changes: %{active_at: article.inserted_at})
end
end

@doc """
undo sink article
"""
def undo_sink_article(thread, id) do
with {:ok, info} <- match(thread),
{:ok, article} <- ORM.find(info.model, id),
true <- in_active_period?(thread, article) do
meta = Map.merge(article.meta, %{is_sinked: false})
ORM.update_meta(article, meta, changes: %{active_at: meta.last_active_at})
else
false -> raise_error(:undo_sink_old_article, "can not undo sink old article")
end
end

# check is an article's active_at is in active period
defp in_active_period?(thread, article) do
active_period_days = Map.get(@active_period, thread)

inserted_at = article.inserted_at
active_threshold = Timex.shift(Timex.now(), days: -active_period_days)

:gt == DateTime.compare(inserted_at, active_threshold)
end

@doc """
mark delete falst for an anticle
"""
Expand Down Expand Up @@ -300,7 +374,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleCURD do
defp add_pin_articles_ifneed(articles, _querable, _filter), do: articles

# if filter contains like: tags, sort.., then don't add pin article
defp should_add_pin?(%{page: 1, sort: :desc_inserted} = filter) do
defp should_add_pin?(%{page: 1, sort: :desc_active} = filter) do
skip_pinned_fields = [:article_tag, :article_tags]

not Enum.any?(Map.keys(filter), &(&1 in skip_pinned_fields))
Expand Down Expand Up @@ -331,35 +405,6 @@ defmodule GroupherServer.CMS.Delegate.ArticleCURD do
|> Map.put(:total_count, normal_count)
end

defp create_article_result({:ok, %{create_article: result}}) do
Later.exec({__MODULE__, :notify_admin_new_article, [result]})
{:ok, result}
end

defp create_article_result({:error, :create_article, %Ecto.Changeset{} = result, _steps}) do
{:error, result}
end

defp create_article_result({:error, :create_article, _result, _steps}) do
{:error, [message: "create cms article author", code: ecode(:create_fails)]}
end

defp create_article_result({:error, :mirror_article, _result, _steps}) do
{:error, [message: "set community", code: ecode(:create_fails)]}
end

defp create_article_result({:error, :set_community_flag, _result, _steps}) do
{:error, [message: "set community flag", code: ecode(:create_fails)]}
end

defp create_article_result({:error, :set_article_tags, result, _steps}) do
{:error, result}
end

defp create_article_result({:error, :log_action, _result, _steps}) do
{:error, [message: "log action", code: ecode(:create_fails)]}
end

# for create artilce step in Multi.new
defp do_create_article(target, attrs, %Author{id: aid}, %Community{id: cid}) do
target
Expand Down Expand Up @@ -396,9 +441,32 @@ defmodule GroupherServer.CMS.Delegate.ArticleCURD do
end
end

# create done
defp result({:ok, %{set_active_at_timestamp: result}}) do
Later.exec({__MODULE__, :notify_admin_new_article, [result]})
{:ok, result}
end

defp result({:ok, %{update_edit_status: result}}), do: {:ok, result}
defp result({:ok, %{update_article: result}}), do: {:ok, result}
defp result({:ok, %{set_viewer_has_states: result}}), do: result |> done()
defp result({:ok, %{update_article_meta: result}}), do: {:ok, result}

defp result({:error, :create_article, _result, _steps}) do
{:error, [message: "create cms article author", code: ecode(:create_fails)]}
end

defp result({:error, :mirror_article, _result, _steps}) do
{:error, [message: "set community", code: ecode(:create_fails)]}
end

defp result({:error, :set_community_flag, _result, _steps}) do
{:error, [message: "set community flag", code: ecode(:create_fails)]}
end

defp result({:error, :log_action, _result, _steps}) do
{:error, [message: "log action", code: ecode(:create_fails)]}
end

defp result({:error, _, result, _steps}), do: {:error, result}
end
32 changes: 20 additions & 12 deletions lib/groupher_server/cms/embeds/article_meta.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,23 @@ defmodule GroupherServer.CMS.Embeds.ArticleMeta do
use Accessible
import Ecto.Changeset

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

@default_meta %{
is_edited: false,
is_comment_locked: false,
upvoted_user_ids: [],
collected_user_ids: [],
viewed_user_ids: [],
reported_user_ids: [],
reported_count: 0
}
@optional_fields ~w(is_edited is_comment_locked upvoted_user_ids collected_user_ids viewed_user_ids reported_user_ids reported_count is_sinked can_undo_sink last_active_at)a

@doc "for test usage"
def default_meta(), do: @default_meta
def default_meta() do
%{
is_edited: false,
is_comment_locked: false,
upvoted_user_ids: [],
collected_user_ids: [],
viewed_user_ids: [],
reported_user_ids: [],
reported_count: 0,
is_sinked: false,
can_undo_sink: true,
last_active_at: nil
}
end

embedded_schema do
field(:is_edited, :boolean, default: false)
Expand All @@ -30,6 +33,11 @@ defmodule GroupherServer.CMS.Embeds.ArticleMeta do
field(:viewed_user_ids, {:array, :integer}, default: [])
field(:reported_user_ids, {:array, :integer}, default: [])
field(:reported_count, :integer, default: 0)

field(:is_sinked, :boolean, default: false)
field(:can_undo_sink, :boolean, default: false)
# if undo_sink, can recover last active_at from here
field(:last_active_at, :utc_datetime_usec)
end

def changeset(struct, params) do
Expand Down
4 changes: 3 additions & 1 deletion lib/groupher_server/cms/helper/macros.ex
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@ defmodule GroupherServer.CMS.Helper.Macros do
:article_comments_participators_count,
:upvotes_count,
:collects_count,
:mark_delete
:mark_delete,
:active_at
]
end

Expand Down Expand Up @@ -189,6 +190,7 @@ defmodule GroupherServer.CMS.Helper.Macros do
viewer_has_fields()
article_comment_fields()

field(:active_at, :utc_datetime_usec)
# TODO:
# reference_articles
# related_articles
Expand Down
Loading