完善通讯录群详情展示:补齐群公告/群主/群管理信息

变更项:
1. 数据层新增群资料聚合查询,直接复用 is_owner/is_admin 字段返回群主与管理员列表。
2. 同步群信息时兼容提取群公告并落库,历史库启动时自动补齐 chat_room_announcement 字段。
3. 新增 /contacts/api/group_profile/<roomid> 接口,统一返回群公告、群主、管理员、成员数。
4. 通讯录群详情弹窗新增群主/群成员数/群管理/群公告展示,并在打开详情时自动加载。
5. 补充群成员精简查询头像字段,更新初始化 SQL 中 t_chatrooms 公告字段定义。
This commit is contained in:
liuwei
2026-04-16 17:04:55 +08:00
parent d8bfdd54e3
commit 77fc15d1fd
4 changed files with 144 additions and 1 deletions

View File

@@ -427,6 +427,23 @@ def api_group_members(roomid):
return jsonify({"success": False, "error": str(e)}), 500
@contacts_bp.route('/api/group_profile/<roomid>', methods=['GET'])
@login_required
def api_group_profile(roomid):
"""获取指定群的资料信息(群公告、群主、管理员、成员数)"""
try:
server = current_app.dashboard_server
# 直接复用联系人库中已有身份字段,按群聚合成页面可展示的资料结构。
profile = server.contact_db.get_chatroom_profile(roomid)
return jsonify({
"success": True,
"data": profile
})
except Exception as e:
logger.error(f"获取群资料失败: {e}")
return jsonify({"success": False, "error": str(e)}), 500
@contacts_bp.route('/api/group_member_context/<roomid>/<wxid>', methods=['GET'])
@login_required
def api_group_member_context(roomid, wxid):

View File

@@ -187,6 +187,31 @@
<el-descriptions :column="1" border>
<el-descriptions-item label="群ID">{% raw %}{{ currentGroup.wxid }}{% endraw %}</el-descriptions-item>
<el-descriptions-item label="群名称">{% raw %}{{ currentGroup.name }}{% endraw %}</el-descriptions-item>
<el-descriptions-item label="群主">
<span>{% raw %}{{ currentGroupProfile.owner_name || currentGroupProfile.owner_wxid || '未知' }}{% endraw %}</span>
<span class="detail-inline-note" v-if="currentGroupProfile.owner_wxid && currentGroupProfile.owner_name && currentGroupProfile.owner_name !== currentGroupProfile.owner_wxid">
({% raw %}{{ currentGroupProfile.owner_wxid }}{% endraw %})
</span>
</el-descriptions-item>
<el-descriptions-item label="群成员数">
{% raw %}{{ currentGroupProfile.member_count || 0 }}{% endraw %}
</el-descriptions-item>
<el-descriptions-item label="群管理">
<div class="feature-chip-list">
<el-tag
v-for="admin in (currentGroupProfile.admins || [])"
:key="admin.wxid"
size="small"
type="warning"
effect="plain">
{% raw %}{{ admin.display_name }}{% endraw %}
</el-tag>
<span v-if="!(currentGroupProfile.admins || []).length" class="empty-inline">暂无管理员数据</span>
</div>
</el-descriptions-item>
<el-descriptions-item label="群公告">
<span class="group-announcement">{% raw %}{{ currentGroupProfile.announcement || '暂无群公告' }}{% endraw %}</span>
</el-descriptions-item>
<el-descriptions-item label="机器人状态">
<el-tag :type="currentGroup.robot_status === 'enabled' ? 'success' : 'info'">
{% raw %}{{ currentGroup.robot_status === 'enabled' ? '已启用' : '未启用' }}{% endraw %}
@@ -751,6 +776,8 @@
managedGroupMap: {},
groupPermissions: [],
groupPermissionsLoading: false,
// 当前群基础资料:用于展示群主、群公告、管理员、成员数等信息。
currentGroupProfile: { owner_wxid: '', owner_name: '', announcement: '', member_count: 0, admin_count: 0, admins: [] },
groupInsight: null,
groupInsightLoading: false,
groupMembersList: [], groupMembersCurrentPage: 1, groupMembersPageSize: 10, groupMemberSearchQuery: '', groupMembersLoading: false,
@@ -866,6 +893,8 @@
viewGroupDetails(group) {
this.currentGroup = { ...group };
this.groupDetailDialogVisible = true;
// 进入群详情时先加载群资料,保证群主/公告/管理员信息第一时间可见。
this.loadGroupProfile(group.wxid);
this.loadGroupMembers(group.wxid);
this.loadGroupPermissions(group.wxid);
this.loadGroupInsights(group.wxid);
@@ -915,6 +944,22 @@
})
.finally(() => { this.groupInsightLoading = false; });
},
loadGroupProfile(groupId) {
// 每次进入详情都拉最新资料,避免“更新通讯录后页面仍是旧数据”的错觉。
this.currentGroupProfile = { owner_wxid: '', owner_name: '', announcement: '', member_count: 0, admin_count: 0, admins: [] };
axios.get(`/contacts/api/group_profile/${groupId}`)
.then(response => {
if (response.data.success) {
this.currentGroupProfile = response.data.data || this.currentGroupProfile;
} else {
this.$message.error('加载群资料失败');
}
})
.catch(error => {
console.error('加载群资料失败:', error);
this.$message.error('加载群资料失败');
});
},
toggleGroupPermission(permission) {
const newStatus = permission.statusBool ? 'enabled' : 'disabled';
axios.post(`/robot/api/group/${this.currentGroup.wxid}/permissions`, {
@@ -1494,6 +1539,7 @@
.detail-card-sub { font-size: 12px; color: #94a3b8; font-weight: 500; }
.feature-chip-list { display: flex; gap: 8px; flex-wrap: wrap; min-height: 60px; align-items: flex-start; }
.empty-inline, .detail-inline-note { font-size: 12px; color: #64748b; }
.group-announcement { white-space: pre-wrap; line-height: 1.7; color: #334155; }
.detail-inline-note { margin-top: 12px; line-height: 1.6; }
.suggestion-list { display: flex; flex-direction: column; gap: 12px; }
.suggestion-item { border-radius: 14px; }

View File

@@ -101,6 +101,7 @@ class ContactsDBOperator(BaseDBOperator):
remark_quan_pin VARCHAR(256) COMMENT '备注全拼',
chat_room_notify TINYINT COMMENT '群通知',
chat_room_owner VARCHAR(64) COMMENT '群主微信ID',
chat_room_announcement TEXT COMMENT '群公告内容',
small_head_img_url TEXT COMMENT '群头像URL',
member_list TEXT COMMENT '成员列表(JSON)',
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
@@ -108,6 +109,11 @@ class ContactsDBOperator(BaseDBOperator):
UNIQUE KEY `idx_chatroom_id` (`chatroom_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='微信群信息表';
""")
# 兼容历史库:老版本表可能没有群公告字段,这里启动时自动补齐。
self.execute_update("""
ALTER TABLE t_chatrooms
ADD COLUMN IF NOT EXISTS chat_room_announcement TEXT COMMENT '群公告内容'
""")
self.LOG.info("成功创建或确认微信群信息表存在")
except Exception as e:
self.LOG.error(f"创建微信联系人表或群成员表失败: {e}")
@@ -516,6 +522,15 @@ class ContactsDBOperator(BaseDBOperator):
return val.get("string", default)
return val if val is not None else default
# 群公告在不同协议/版本里字段名不一致,这里做一次兼容提取,避免前端拿不到内容。
announcement = (
get_str('ChatRoomAnnouncement')
or get_str('Announcement')
or get_str('Annoucement')
or get_str('AnnouncementContent')
or get_str('chatRoomAnnouncement')
)
data = {
'chatroom_id': get_str('UserName'),
'nick_name': get_str('NickName',"未知群名"),
@@ -527,6 +542,7 @@ class ContactsDBOperator(BaseDBOperator):
'remark_quan_pin': get_str('RemarkQuanPin'),
'chat_room_notify': chatroom_data.get('ChatRoomNotify', 0),
'chat_room_owner': chatroom_data.get('ChatRoomOwner', ''),
'chat_room_announcement': announcement,
'small_head_img_url': chatroom_data.get('SmallHeadImgUrl', ''),
# 成员列表可选存储为JSON字符串
'member_list': json.dumps(chatroom_data.get('NewChatroomData', {}).get('ChatRoomMember', []),
@@ -673,7 +689,11 @@ class ContactsDBOperator(BaseDBOperator):
def get_chatroom_small_member_list(self, chatroom_id: str) -> List[dict]:
"""获取群成员列表"""
try:
sql = "SELECT wxid,nick_name,display_name,status,latest_active_time FROM t_chatroom_member WHERE chatroom_id = %s"
sql = """
SELECT wxid, nick_name, display_name, status, latest_active_time, small_head_img_url
FROM t_chatroom_member
WHERE chatroom_id = %s
"""
results = self.execute_query(sql, (chatroom_id,))
for row in results:
@@ -685,6 +705,65 @@ class ContactsDBOperator(BaseDBOperator):
self.LOG.error(f"获取群{chatroom_id}成员列表失败: {e}")
return []
def get_chatroom_profile(self, chatroom_id: str) -> Dict[str, Any]:
"""获取群资料(群公告、群主、管理员、成员数)用于通讯录详情展示。"""
try:
# 先取群基础信息 + 群主展示名 + 成员数。
info_sql = """
SELECT
c.chatroom_id,
c.nick_name,
c.chat_room_owner,
c.chat_room_announcement,
COALESCE(NULLIF(owner_member.display_name, ''), owner_member.nick_name, c.chat_room_owner, '') AS owner_name,
(
SELECT COUNT(*)
FROM t_chatroom_member m
WHERE m.chatroom_id = c.chatroom_id AND m.status = 1
) AS member_count
FROM t_chatrooms c
LEFT JOIN t_chatroom_member owner_member
ON owner_member.chatroom_id = c.chatroom_id
AND owner_member.wxid = c.chat_room_owner
WHERE c.chatroom_id = %s
LIMIT 1
"""
info = self.execute_query(info_sql, (chatroom_id,), fetch_one=True) or {}
# 使用既有身份字段 is_admin 组装管理员列表,直接复用现有数据。
admin_sql = """
SELECT
wxid,
COALESCE(NULLIF(display_name, ''), nick_name, wxid) AS display_name
FROM t_chatroom_member
WHERE chatroom_id = %s AND is_admin = 1 AND status = 1
ORDER BY display_name
"""
admins = self.execute_query(admin_sql, (chatroom_id,)) or []
return {
"chatroom_id": chatroom_id,
"nick_name": info.get("nick_name", ""),
"owner_wxid": info.get("chat_room_owner", "") or "",
"owner_name": info.get("owner_name", "") or "",
"announcement": info.get("chat_room_announcement", "") or "",
"member_count": int(info.get("member_count") or 0),
"admin_count": len(admins),
"admins": admins,
}
except Exception as e:
self.LOG.error(f"获取群{chatroom_id}资料失败: {e}")
return {
"chatroom_id": chatroom_id,
"nick_name": "",
"owner_wxid": "",
"owner_name": "",
"announcement": "",
"member_count": 0,
"admin_count": 0,
"admins": [],
}
# 获取群成员的昵称信息
def get_chatroom_member_list_name_all(self) -> List[dict]:
"""获取群成员列表"""

View File

@@ -121,6 +121,7 @@ create or replace table message_archive.t_chatrooms
remark_quan_pin varchar(256) null comment '备注全拼',
chat_room_notify tinyint null comment '群通知',
chat_room_owner varchar(64) null comment '群主微信ID',
chat_room_announcement text null comment '群公告内容',
small_head_img_url text null comment '群头像URL',
member_list text null comment '成员列表(JSON)',
create_time datetime default current_timestamp() not null comment '创建时间',