diff --git a/admin/dashboard/blueprints/contacts.py b/admin/dashboard/blueprints/contacts.py index 1f1da7d..5cf05cd 100644 --- a/admin/dashboard/blueprints/contacts.py +++ b/admin/dashboard/blueprints/contacts.py @@ -444,6 +444,73 @@ def api_group_profile(roomid): return jsonify({"success": False, "error": str(e)}), 500 +@contacts_bp.route('/api/group_profile//sync_announcement', methods=['POST']) +@login_required +def api_sync_group_announcement(roomid): + """手动同步指定群公告(调用 /Group/GetChatRoomInfoDetail)。""" + try: + server = current_app.dashboard_server + if not roomid: + return jsonify({"success": False, "error": "缺少群ID"}), 400 + if not getattr(server, "robot", None) or not getattr(server.robot, "ipad_bot", None): + return jsonify({"success": False, "error": "机器人实例未初始化"}), 503 + + async def fetch_and_merge(): + # 先拉基础群信息,再拉 Detail 信息,最后合并,避免只用 Detail 导致字段不完整。 + base_info = await server.robot.ipad_bot.get_chatroom_info(roomid) + detail_info = await server.robot.ipad_bot.get_chatroom_announce(roomid) + + merged_info = dict(base_info or {}) + detail_contact = None + if isinstance(detail_info, dict): + contact_list = detail_info.get("ContactList") + if isinstance(contact_list, list) and contact_list: + first = contact_list[0] + if isinstance(first, dict): + detail_contact = first + + if detail_contact: + merged_info.update(detail_contact) + if isinstance(detail_info, dict): + merged_info.update(detail_info) + + # 统一公告字段命名,供 contacts_db.save_chatroom_info 直接提取入库。 + announcement = ( + merged_info.get("ChatRoomAnnouncement") + or merged_info.get("Announcement") + or merged_info.get("Annoucement") + or merged_info.get("AnnouncementContent") + or merged_info.get("chatRoomAnnouncement") + ) + if announcement: + merged_info["ChatRoomAnnouncement"] = announcement + + # 保底补上群ID,避免少数字段缺失导致无法更新到对应群。 + if not merged_info.get("UserName"): + merged_info["UserName"] = roomid + return merged_info + + merged_info = asyncio.run(fetch_and_merge()) + if not merged_info: + return jsonify({"success": False, "error": "获取群详情失败"}), 500 + + save_ok = server.contact_db.save_chatroom_info(merged_info) + if not save_ok: + return jsonify({"success": False, "error": "保存群公告失败"}), 500 + + profile = server.contact_db.get_chatroom_profile(roomid) + return jsonify({ + "success": True, + "message": "群公告同步成功", + "data": { + "profile": profile + } + }) + except Exception as e: + logger.error(f"手动同步群公告失败: {e}") + return jsonify({"success": False, "error": str(e)}), 500 + + @contacts_bp.route('/api/group_member_context//', methods=['GET']) @login_required def api_group_member_context(roomid, wxid): diff --git a/admin/dashboard/templates/contacts_management.html b/admin/dashboard/templates/contacts_management.html index 7d31469..64058b7 100644 --- a/admin/dashboard/templates/contacts_management.html +++ b/admin/dashboard/templates/contacts_management.html @@ -210,7 +210,17 @@ - {% raw %}{{ currentGroupProfile.announcement || '暂无群公告' }}{% endraw %} +
+ {% raw %}{{ currentGroupProfile.announcement || '暂无群公告' }}{% endraw %} + + 同步群公告 + +
@@ -778,6 +788,8 @@ groupPermissionsLoading: false, // 当前群基础资料:用于展示群主、群公告、管理员、成员数等信息。 currentGroupProfile: { owner_wxid: '', owner_name: '', announcement: '', member_count: 0, admin_count: 0, admins: [] }, + // 群公告手动同步按钮的加载态,避免重复点击触发多次请求。 + groupAnnouncementSyncing: false, groupInsight: null, groupInsightLoading: false, groupMembersList: [], groupMembersCurrentPage: 1, groupMembersPageSize: 10, groupMemberSearchQuery: '', groupMembersLoading: false, @@ -960,6 +972,27 @@ this.$message.error('加载群资料失败'); }); }, + syncCurrentGroupAnnouncement() { + if (!this.currentGroup || !this.currentGroup.wxid) return; + this.groupAnnouncementSyncing = true; + // 手动触发群公告同步:调用后端 Detail 接口并落库,再刷新当前群资料展示。 + axios.post(`/contacts/api/group_profile/${this.currentGroup.wxid}/sync_announcement`) + .then(response => { + if (response.data.success) { + this.$message.success(response.data.message || '群公告同步成功'); + this.loadGroupProfile(this.currentGroup.wxid); + } else { + this.$message.error(response.data.error || '群公告同步失败'); + } + }) + .catch(error => { + console.error('同步群公告失败:', error); + this.$message.error('同步群公告失败'); + }) + .finally(() => { + this.groupAnnouncementSyncing = false; + }); + }, toggleGroupPermission(permission) { const newStatus = permission.statusBool ? 'enabled' : 'disabled'; axios.post(`/robot/api/group/${this.currentGroup.wxid}/permissions`, { @@ -1539,6 +1572,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-wrap { display: flex; gap: 12px; align-items: flex-start; justify-content: space-between; } .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; }