feat(Talk): 群头像支持自定义上传与成员九宫格默认

This commit is contained in:
TermiNexus committed 2026-10-13 18:23:17 +08:00
1 parent 5707ab0371
commit 8abbe1eefc
5 files changed
+86 -1

No files matched your search

+43 -1
View File
@@ -209,7 +209,7 @@ def BroadcastConversation(ConversationUUID, Action, Extra=None):
"""广播「会话级变更」事件(事件名 talk:conversation)
用于群主/管理员操作后让所有在线成员的会话列表立即同步:
改名(renamed)、群公告(announcement)、转让群主(owner_transferred)、
改名(renamed)、群公告(announcement)、群头像(avatar)、转让群主(owner_transferred)、
成员角色变更(role_changed)、移出成员(members_removed)、解散(dissolved)。
事件名不带命名空间前缀,前端订阅的是 'talk:conversation'。
"""
@@ -791,6 +791,46 @@ class TalkManager:
'AnnouncementTime': conv.AnnouncementTime.isoformat() if conv.AnnouncementTime else None,
}
@staticmethod
def SetConversationAvatar(ConversationUUID, OperatorUUID, AvatarSHA1, IsTalkAdmin=False):
"""设置群头像(群主/群管理员/Talk 管理员;传空串即恢复默认九宫格)
AvatarSHA1 为空串表示恢复默认(成员九宫格);非空必须是 40 位十六进制
(由 /API/Talk/Upload 上传图片得到,指向 FileAsset)。
"""
conv = TalkManager.GetConversation(ConversationUUID)
operator = _ToBytes(OperatorUUID)
if conv is None or conv.Status != STATUS_NORMAL:
return False, '会话不存在或已解散', None
if conv.ConversationType != TYPE_GROUP:
return False, '仅群聊可设置群头像', None
member = TalkManager.GetMember(conv.ConversationUUID, operator)
if member is None:
return False, '您不是该会话成员', None
if not TalkManager._IsGroupManager(conv, member, operator) and not IsTalkAdmin:
return False, '仅群主或管理员可修改群头像', None
sha1 = (AvatarSHA1 or '').strip().lower()
if sha1 and (len(sha1) != 40 or any(c not in '0123456789abcdef' for c in sha1)):
return False, '无效的头像资源标识', None
if sha1 == (conv.AvatarSHA1 or ''):
return True, '', {
'ConversationUUID': _ToStr(conv.ConversationUUID),
'AvatarSHA1': conv.AvatarSHA1 or '',
}
conv.AvatarSHA1 = sha1
conv.save(update_fields=['AvatarSHA1'])
operator_name = TalkManager._DisplayName(operator)
TalkManager.AppendSystemMessage(
conv,
f'{operator_name} 更换了群头像' if sha1 else f'{operator_name} 恢复了默认群头像',
operator,
)
BroadcastConversation(conv.ConversationUUID, 'avatar', {'AvatarSHA1': sha1})
return True, '', {
'ConversationUUID': _ToStr(conv.ConversationUUID),
'AvatarSHA1': sha1,
}
@staticmethod
def DissolveConversation(ConversationUUID, OperatorUUID, IsTalkAdmin=False):
"""解散会话(仅群主或 Talk 管理员)"""
@@ -1163,6 +1203,8 @@ class TalkManager:
'LastMessagePreview': Conversation.LastMessagePreview or '',
'Announcement': Conversation.Announcement or '',
'AnnouncementTime': Conversation.AnnouncementTime.isoformat() if Conversation.AnnouncementTime else None,
# 群头像 SHA1(空串=使用成员九宫格默认头像;前端据此拼资源 URL)
'AvatarSHA1': Conversation.AvatarSHA1 or '',
'UnreadCount': unread_qs.count(),
'Members': members,
# 群主标记:前端据此对「非管理员群主」显示解散入口(管理员另有 Entry.IsAdmin)
+2
View File
@@ -33,6 +33,8 @@ class TalkConversation(QModel):
# 群公告(仅群聊使用;群主/群管理员可编辑,成员在会话顶部可见)
Announcement = models.TextField(default='', db_column='Announcement', verbose_name='群公告')
AnnouncementTime = models.DateTimeField(null=True, blank=True, db_column='AnnouncementTime', verbose_name='群公告更新时间')
# 群头像(仅群聊使用;存 FileAsset 的 SHA1,空串表示使用成员九宫格默认头像)
AvatarSHA1 = models.CharField(max_length=40, default='', db_column='AvatarSHA1', verbose_name='群头像SHA1')
# 会话状态:1=正常 0=已解散
Status = models.SmallIntegerField(default=1, db_column='Status', verbose_name='状态(1正常/0解散)')
CreateTime = models.DateTimeField(default=datetime.datetime.utcnow, db_column='CreateTime', verbose_name='创建时间')
+1
View File
@@ -13,6 +13,7 @@ urlpatterns = [
path('Conversation/Member/Remove', views.ConversationMemberRemoveView.as_view()),
path('Conversation/Transfer', views.ConversationTransferView.as_view()),
path('Conversation/Announcement', views.ConversationAnnouncementView.as_view()),
path('Conversation/Avatar', views.ConversationAvatarView.as_view()),
path('Conversation/Dissolve', views.ConversationDissolveView.as_view()),
path('Messages', views.MessageListView.as_view()),
path('Send', views.SendView.as_view()),
+21
View File
@@ -316,6 +316,27 @@ class ConversationAnnouncementView(TalkAPIView):
return Response({'code': 0, 'data': data})
class ConversationAvatarView(TalkAPIView):
"""POST API/Talk/Conversation/Avatar —— 设置群头像(空串即恢复默认九宫格)
请求体:{ConversationUUID, AvatarSHA1}
AvatarSHA1 由 /API/Talk/Upload 上传图片得到(指向 FileAsset)。
"""
def post(self, request):
user = _UserBytes(request)
conv_uuid = request.data.get('ConversationUUID')
avatar_sha1 = request.data.get('AvatarSHA1', '')
if not conv_uuid:
return Response({'code': 1, 'msg': '缺少 ConversationUUID'}, status=400)
is_admin = IsAdmin(request=request) or IsCompanyAdmin(request=request)
ok, msg, data = TalkManager.SetConversationAvatar(
conv_uuid, user, avatar_sha1, IsTalkAdmin=is_admin)
if not ok:
return Response({'code': 1, 'msg': msg}, status=400)
return Response({'code': 0, 'data': data})
class ConversationDissolveView(TalkAPIView):
"""POST API/Talk/Conversation/Dissolve —— 解散会话(仅群主或 Talk 管理员)"""
@@ -0,0 +1,19 @@
"""Talk 群头像(TalkConversation.AvatarSHA1)
"""
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('gerp', '0056_talk_conversation_announcement'),
]
operations = [
migrations.AddField(
model_name='talkconversation',
name='AvatarSHA1',
field=models.CharField(
default='', max_length=40, db_column='AvatarSHA1', verbose_name='群头像SHA1'),
),
]