From 23b5d5bef091e222e913a12084b457abcb71bcd1 Mon Sep 17 00:00:00 2001 From: liuwei Date: Thu, 30 Apr 2026 14:12:01 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=BE=A4=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E8=AF=A6=E6=83=85=E9=A1=B5=E5=B1=95=E7=A4=BA=E4=B8=8E=E7=BE=A4?= =?UTF-8?q?=E6=88=90=E5=91=98=E7=BB=9F=E8=AE=A1=E5=8F=A3=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 群成员列表仅展示仍在群内的成员,排除已退群用户,避免僵尸成员与成员明细口径不一致。 2. 群成员列表按最后发言时间倒序排序,最近活跃成员优先展示,未发言成员排在后面。 3. 群详情启用功能区域的最后消息改为紧凑预览,图片、视频、链接、表情、XML、系统消息统一显示标记,不再直接展示原始内容。 4. 群功能权限区域默认折叠,需手动展开后再查看和操作,降低详情弹窗的信息噪音。 5. 进群欢迎配置区域默认折叠,需手动展开后再查看和编辑群级差异化欢迎配置。 --- .../templates/contacts_management.html | 82 +++++++++++++++++-- db/contacts_db.py | 25 +++++- 2 files changed, 97 insertions(+), 10 deletions(-) diff --git a/admin/dashboard/templates/contacts_management.html b/admin/dashboard/templates/contacts_management.html index 8c4b8a1..a3f226d 100644 --- a/admin/dashboard/templates/contacts_management.html +++ b/admin/dashboard/templates/contacts_management.html @@ -405,11 +405,19 @@

群功能权限

- 一键启用 - 一键关闭 + + {% raw %}{{ groupPermissionsCollapsed ? '展开查看' : '收起' }}{% endraw %} + +
- +
+ 群功能权限默认折叠,点击“展开查看”后再进行配置。 +
+ @@ -434,9 +442,17 @@
进群欢迎配置(群级差异化) - 当前群:{% raw %}{{ currentGroup.name || currentGroup.wxid || '-' }}{% endraw %} +
+ 当前群:{% raw %}{{ currentGroup.name || currentGroup.wxid || '-' }}{% endraw %} + + {% raw %}{{ groupWelcomeCollapsed ? '展开查看' : '收起' }}{% endraw %} + +
- +
+ 进群欢迎配置默认折叠,点击“展开查看”后再编辑当前群的欢迎文案。 +
+ @@ -835,7 +851,11 @@ managedGroupMap: {}, groupPermissions: [], groupPermissionsLoading: false, + // 群权限区域默认折叠,避免详情弹窗一打开就被大量开关占满视线。 + groupPermissionsCollapsed: true, groupWelcomeConfigLoading: false, + // 欢迎配置表单默认折叠,需要用户主动展开后再查看或编辑。 + groupWelcomeCollapsed: true, groupWelcomeConfig: { welcome_text_enabled: true, welcome_text_template: '👏欢迎 {nickname} 加入群聊!🎉', @@ -990,6 +1010,9 @@ handleCurrentChange(page) { this.currentPage = page; }, viewGroupDetails(group) { this.currentGroup = { ...group }; + // 切换群时恢复默认折叠态,保持“手动展开再看”的交互习惯。 + this.groupPermissionsCollapsed = true; + this.groupWelcomeCollapsed = true; this.groupDetailDialogVisible = true; // 进入群详情时先加载群资料,保证群主/公告/管理员信息第一时间可见。 this.loadGroupProfile(group.wxid); @@ -1249,7 +1272,19 @@ if (response.data.success) { const members = response.data.data.members; this.memberContextEnabled = !!response.data.data.member_context_enabled; - this.groupMembersList = members.map(item => ({ wxid: item.wxid, name: item.nick_name, display_name: item.display_name, status: item.status, latest_active_time: item.latest_active_time, small_head_img_url: item.small_head_img_url, activity_level: item.activity_level, response_style_hint: item.response_style_hint, summary_text: item.summary_text, last_profiled_at: item.last_profiled_at })); + // 后端已按最后发言时间倒序返回,这里保持字段展开写法,方便后续继续扩展成员画像字段。 + this.groupMembersList = members.map(item => ({ + wxid: item.wxid, + name: item.nick_name, + display_name: item.display_name, + status: item.status, + latest_active_time: item.latest_active_time, + small_head_img_url: item.small_head_img_url, + activity_level: item.activity_level, + response_style_hint: item.response_style_hint, + summary_text: item.summary_text, + last_profiled_at: item.last_profiled_at + })); } else { this.$message.error('获取群成员失败'); } }).catch(error => { console.error('加载群成员数据失败:', error); this.$message.error('加载群成员数据失败'); }).finally(() => { this.groupMembersLoading = false; }); }, @@ -1416,9 +1451,37 @@ if (!lastMessage) return '暂无消息'; const sender = lastMessage.sender || '未知成员'; const time = lastMessage.timestamp || ''; - const content = lastMessage.content || '[非文本消息]'; + const content = this.getCompactLastMessageContent(lastMessage); return `${sender} · ${time} · ${content}`; }, + getCompactLastMessageContent(lastMessage) { + // 群详情顶部只需要紧凑预览,不需要把 XML、链接卡片原文或媒体原始内容完整展开。 + const messageType = String((lastMessage && lastMessage.message_type) || '').trim(); + const rawContent = String((lastMessage && lastMessage.content) || '').trim(); + const compactMap = { + '3': '[图片]', + '43': '[视频]', + '62': '[视频]', + '47': '[表情]', + '1048625': '[表情]', + '1090519089': '[表情]', + '49': '[链接]' + }; + if (compactMap[messageType]) { + return compactMap[messageType]; + } + if (rawContent.startsWith(']+>/.test(rawContent)) { + return '[XML消息]'; + } + if (!rawContent) { + return '[非文本消息]'; + } + const singleLineContent = rawContent.replace(/\s+/g, ' ').trim(); + return singleLineContent.length > 60 ? `${singleLineContent.slice(0, 60)}...` : singleLineContent; + }, openChatDialog(user) { this.currentChatUser = user; this.chatType = user && user.wxid && user.wxid.endsWith('@chatroom') ? 'group' : 'personal'; @@ -1715,6 +1778,11 @@ .group-permission-section { margin-top: 20px; } .welcome-config-card { margin-top: 14px; } .group-members-section { margin-top: 20px; } + .detail-card-header-actions { display: flex; align-items: center; gap: 10px; } + .collapsed-placeholder { + padding: 14px 16px; border-radius: 12px; background: #f8fafc; color: #64748b; + border: 1px dashed rgba(148, 163, 184, 0.45); font-size: 13px; + } .section-title { margin: 20px 0 15px 0; border-bottom: 1px solid rgba(148,163,184,0.12); padding-bottom: 10px; display: flex; justify-content: space-between; align-items: center; diff --git a/db/contacts_db.py b/db/contacts_db.py index b7245f7..24d78dc 100644 --- a/db/contacts_db.py +++ b/db/contacts_db.py @@ -732,12 +732,23 @@ class ContactsDBOperator(BaseDBOperator): return [] # 新增获取群成员列表接口 def get_chatroom_small_member_list(self, chatroom_id: str) -> List[dict]: - """获取群成员列表""" + """获取群成员列表。 + + 说明: + 1. 群详情页的成员列表只展示当前仍在群内的成员,已退群成员不再返回; + 2. 成员需要按最后发言时间倒序展示,方便后台优先看到最近活跃的人; + 3. 从未发言的成员排在后面,避免把“无活跃记录”的成员顶到前面。 + """ try: sql = """ SELECT wxid, nick_name, display_name, status, latest_active_time, small_head_img_url FROM t_chatroom_member WHERE chatroom_id = %s + AND status = 1 + ORDER BY + CASE WHEN latest_active_time IS NULL THEN 1 ELSE 0 END ASC, + latest_active_time DESC, + COALESCE(NULLIF(display_name, ''), nick_name, wxid) ASC """ results = self.execute_query(sql, (chatroom_id,)) @@ -916,6 +927,7 @@ class ContactsDBOperator(BaseDBOperator): , CASE WHEN latest_active_time IS NULL THEN 1 ELSE 0 END AS never_spoken FROM t_chatroom_member WHERE chatroom_id = %s + AND status = 1 AND (latest_active_time IS NULL OR latest_active_time <= DATE_SUB(NOW(), INTERVAL %s DAY)) ORDER BY inactivity_days DESC LIMIT %s @@ -931,12 +943,18 @@ class ContactsDBOperator(BaseDBOperator): return [] def get_group_member_summary(self, chatroom_id: str, inactive_days: int = 30) -> Dict[str, Any]: - """获取群成员概览摘要""" + """获取群成员概览摘要。 + + 说明: + 1. 群运营看板里的成员统计口径应当只基于“当前仍在群内”的成员; + 2. 已退群成员如果继续参与统计,会导致僵尸成员数、活跃覆盖率等指标失真; + 3. 因此这里统一过滤 status = 1,确保详情页数字与成员列表保持一致。 + """ try: sql = """ SELECT COUNT(*) AS total_members, - SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) AS in_group_members, + COUNT(*) AS in_group_members, SUM(CASE WHEN is_owner = 1 THEN 1 ELSE 0 END) AS owner_count, SUM(CASE WHEN is_admin = 1 THEN 1 ELSE 0 END) AS admin_count, SUM(CASE WHEN latest_active_time IS NOT NULL THEN 1 ELSE 0 END) AS spoken_members, @@ -964,6 +982,7 @@ class ContactsDBOperator(BaseDBOperator): ) AS inactive_members FROM t_chatroom_member WHERE chatroom_id = %s + AND status = 1 """ result = self.execute_query(sql, (inactive_days, chatroom_id), fetch_one=True) or {} return {