Files
TermiNexus 99fd632bf1 refactor(custom-apps): doc/invite/sve/community 视图裸 ORM 收敛进各 app Manager
doc:DocManager 新增 QueryDocuments/QuerySeries 返回查询集,视图 get_queryset 改为委托;invite:新增 InviteManager(QuerySet/GetByInviteID/Create);sve:新增 SveManager(QuerySet/GetByCandidates/Upsert 支持 CreateDefaults);community:新增 CommunityManager(板块/分类/帖子/回复/点赞共 20 处,ToggleLike 封装点赞切换与计数同步,聚合查询保留原 values_list+annotate)。

视图层不再直接操作 ORM,类级 queryset 属性保留(DRF 必需);QueryPosts 收 Filters 字典以保持原边界行为。
2026-09-14 06:47:01 +08:00

151 lines
5.0 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""模块:Community 社区数据访问层
官网社区(discuss.gvsds.com)的板块/分类/帖子/回复/点赞数据访问收敛在此,
视图层不直接操作 ORM。所有方法遵循帕斯卡命名法。
"""
import uuid
from django.db.models import Count, F
from .models import (
CommunityBoard,
CommunityCategory,
CommunityLike,
CommunityPost,
CommunityReply,
)
class CommunityManager:
"""社区管理器"""
# ========== 板块 / 分类 ==========
@staticmethod
def ListActiveBoards():
"""启用板块列表"""
return list(CommunityBoard.objects.filter(BoardStatus=1))
@staticmethod
def CountPostsByBoards(BoardUUIDs):
"""各板块的已发布帖子数 {板块bytes: 帖子数}(BoardUUIDs 为空返回 {})"""
if not BoardUUIDs:
return {}
return {
BoardUUID: PostCount
for BoardUUID, PostCount in CommunityPost.objects.filter(
PostStatus=1, BoardUUID__in=BoardUUIDs,
).values_list('BoardUUID').annotate(c=Count('PostUUID'))
}
@staticmethod
def ListActiveCategories():
"""启用分类列表"""
return list(CommunityCategory.objects.filter(CategoryStatus=1))
@staticmethod
def GetBoard(BoardUUID):
"""按 UUID 取板块(不存在返回 None)"""
return CommunityBoard.objects.filter(BoardUUID=BoardUUID).first()
@staticmethod
def GetActiveBoard(BoardUUID):
"""按 UUID 取启用板块(不存在或已停用返回 None)"""
return CommunityBoard.objects.filter(BoardUUID=BoardUUID, BoardStatus=1).first()
@staticmethod
def GetCategory(CategoryUUID):
"""按 UUID 取分类(UUID 为空返回 None)"""
if not CategoryUUID:
return None
return CommunityCategory.objects.filter(CategoryUUID=CategoryUUID).first()
# ========== 帖子 ==========
@staticmethod
def QueryPosts(Filters=None):
"""已发布帖子查询集
Filters 为附加过滤条件(键为 ORM lookup、值为已转换好的 bytes/str),
由调用方按「参数存在才过滤」的语义构造;为空表示仅限已发布。
"""
return CommunityPost.objects.filter(PostStatus=1, **(Filters or {}))
@staticmethod
def GetPost(PostUUID, OnlyPublished=False):
"""按 UUID 取帖子(OnlyPublished=True 时排除已删除;不存在返回 None)"""
qs = CommunityPost.objects.filter(PostUUID=PostUUID)
if OnlyPublished:
qs = qs.filter(PostStatus=1)
return qs.first()
@staticmethod
def CreatePost(BoardUUID, Title, Content, AuthorUUID, AuthorName, CategoryUUID=None):
"""发帖"""
return CommunityPost.objects.create(
PostUUID=uuid.uuid4().bytes,
BoardUUID=BoardUUID,
CategoryUUID=CategoryUUID,
Title=Title,
Content=Content,
AuthorUUID=AuthorUUID,
AuthorName=AuthorName,
)
@staticmethod
def IncreaseViewCount(PostUUID):
"""浏览量 +1(DB 侧原子自增)"""
CommunityPost.objects.filter(PostUUID=PostUUID).update(ViewCount=F('ViewCount') + 1)
@staticmethod
def IncreaseReplyCount(PostUUID):
"""回复数 +1(DB 侧原子自增)"""
CommunityPost.objects.filter(PostUUID=PostUUID).update(ReplyCount=F('ReplyCount') + 1)
# ========== 回复 ==========
@staticmethod
def ListReplies(PostUUID):
"""帖子下未删除回复列表"""
return CommunityReply.objects.filter(PostUUID=PostUUID, ReplyStatus=1)
@staticmethod
def CreateReply(PostUUID, AuthorUUID, AuthorName, Content, ReplyToUUID=None):
"""新增回复"""
return CommunityReply.objects.create(
ReplyUUID=uuid.uuid4().bytes,
PostUUID=PostUUID,
AuthorUUID=AuthorUUID,
AuthorName=AuthorName,
Content=Content,
ReplyToUUID=ReplyToUUID,
)
# ========== 点赞 ==========
@staticmethod
def HasLiked(PostUUID, AuthorUUID):
"""判断用户是否已点赞该帖子"""
return CommunityLike.objects.filter(PostUUID=PostUUID, AuthorUUID=AuthorUUID).exists()
@staticmethod
def ToggleLike(PostUUID, AuthorUUID):
"""切换点赞状态并同步帖子点赞数
Returns:
True 表示本次为点赞,False 表示本次为取消点赞
"""
existing = CommunityLike.objects.filter(PostUUID=PostUUID, AuthorUUID=AuthorUUID).first()
if existing is not None:
existing.delete()
CommunityPost.objects.filter(PostUUID=PostUUID).update(LikeCount=F('LikeCount') - 1)
return False
CommunityLike.objects.create(
LikeUUID=uuid.uuid4().bytes,
PostUUID=PostUUID,
AuthorUUID=AuthorUUID,
)
CommunityPost.objects.filter(PostUUID=PostUUID).update(LikeCount=F('LikeCount') + 1)
return True