diff --git a/admin/dashboard/server.py b/admin/dashboard/server.py
index ed636b4..de59e56 100644
--- a/admin/dashboard/server.py
+++ b/admin/dashboard/server.py
@@ -247,24 +247,95 @@ class DashboardServer:
def api_contacts_statistics():
"""获取联系人统计信息API"""
try:
- contacts = self.contact_manager.get_contacts()
- group_contacts = {wxid: name for wxid, name in contacts.items()
- if '@@' in wxid or '@chatroom' in wxid}
- personal_contacts = {wxid: name for wxid, name in contacts.items()
- if '@@' not in wxid and '@chatroom' not in wxid}
+ # 使用新的联系人分类方法获取统计信息
+ total, groups, personal, public, official = self.contact_manager.get_contact_statistics()
return jsonify({
"success": True,
"data": {
- "total": len(contacts),
- "groups": len(group_contacts),
- "personal": len(personal_contacts)
+ "total": total,
+ "groups": groups,
+ "personal": personal,
+ "public": public,
+ "official": official
}
})
except Exception as e:
self.logger.error(f"获取联系人统计信息失败: {e}")
return jsonify({"success": False, "error": str(e)}), 500
+ # 修改群组联系人API,使用新的分类方法
+ @app.route('/api/contacts/groups', methods=['GET'])
+ @login_required
+ def api_contacts_groups():
+ """获取群组联系人信息API"""
+ try:
+ group_contacts = self.contact_manager.get_group_contacts()
+
+ return jsonify({
+ "success": True,
+ "data": {
+ "groups": group_contacts
+ }
+ })
+ except Exception as e:
+ self.logger.error(f"获取群组联系人信息失败: {e}")
+ return jsonify({"success": False, "error": str(e)}), 500
+
+ # 修改个人联系人API,使用新的分类方法
+ @app.route('/api/contacts/personal', methods=['GET'])
+ @login_required
+ def api_contacts_personal():
+ """获取个人联系人信息API"""
+ try:
+ personal_contacts = self.contact_manager.get_personal_contacts()
+
+ return jsonify({
+ "success": True,
+ "data": {
+ "personal": personal_contacts
+ }
+ })
+ except Exception as e:
+ self.logger.error(f"获取个人联系人信息失败: {e}")
+ return jsonify({"success": False, "error": str(e)}), 500
+
+ # 添加公众号联系人API
+ @app.route('/api/contacts/official', methods=['GET'])
+ @login_required
+ def api_contacts_official():
+ """获取公众号联系人信息API"""
+ try:
+ official_accounts = self.contact_manager.get_official_accounts()
+
+ return jsonify({
+ "success": True,
+ "data": {
+ "official": official_accounts
+ }
+ })
+ except Exception as e:
+ self.logger.error(f"获取公众号联系人信息失败: {e}")
+ return jsonify({"success": False, "error": str(e)}), 500
+
+ # 添加公共好友API
+ @app.route('/api/contacts/public', methods=['GET'])
+ @login_required
+ def api_contacts_public():
+ """获取公共好友信息API"""
+ try:
+ public_contacts = self.contact_manager.get_public_contacts()
+
+ return jsonify({
+ "success": True,
+ "data": {
+ "public": public_contacts
+ }
+ })
+ except Exception as e:
+ self.logger.error(f"获取公共好友信息失败: {e}")
+ return jsonify({"success": False, "error": str(e)}), 500
+
@app.route('/api/robot/groups')
@login_required
def api_robot_groups():
diff --git a/admin/dashboard/templates/contacts_management.html b/admin/dashboard/templates/contacts_management.html
index 34cca41..7b7cd95 100644
--- a/admin/dashboard/templates/contacts_management.html
+++ b/admin/dashboard/templates/contacts_management.html
@@ -27,24 +27,30 @@
-
+
总联系人数
{% raw %}{{ statistics.total }}{% endraw %}
-
+
群组数
{% raw %}{{ statistics.groups }}{% endraw %}
-
+
个人联系人数
{% raw %}{{ statistics.personal }}{% endraw %}
+
+
+ 公众号数
+ {% raw %}{{ statistics.official }}{% endraw %}
+
+
@@ -92,6 +98,50 @@
+
+
+
+
+
+
+
+
+
+
+ 查看详情
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 查看详情
+
+
+
+
+
@@ -118,6 +168,30 @@
{% raw %}:total="personalList.length"{% endraw %}>
+
+
+
+
@@ -139,6 +213,24 @@
+
+
+
+
+ {% raw %}{{ currentOfficial.wxid }}{% endraw %}
+ {% raw %}{{ currentOfficial.name }}{% endraw %}
+
+
+
+
+
+
+
+ {% raw %}{{ currentPublic.wxid }}{% endraw %}
+ {% raw %}{{ currentPublic.name }}{% endraw %}
+
+
+
{% endblock %}
@@ -155,15 +247,23 @@
pageSize: 10,
groupsList: [],
personalList: [],
+ officialList: [],
+ publicList: [],
statistics: {
total: 0,
groups: 0,
- personal: 0
+ personal: 0,
+ official: 0,
+ public: 0
},
groupDetailDialogVisible: false,
userDetailDialogVisible: false,
+ officialDetailDialogVisible: false,
+ publicDetailDialogVisible: false,
currentGroup: {},
- currentUser: {}
+ currentUser: {},
+ currentOfficial: {},
+ currentPublic: {}
};
},
computed: {
@@ -200,6 +300,40 @@
(this.currentPage - 1) * this.pageSize,
this.currentPage * this.pageSize
);
+ },
+ filteredOfficial() {
+ const query = this.searchQuery.toLowerCase();
+ if (!query) {
+ return this.officialList.slice(
+ (this.currentPage - 1) * this.pageSize,
+ this.currentPage * this.pageSize
+ );
+ }
+
+ return this.officialList.filter(official =>
+ official.wxid.toLowerCase().includes(query) ||
+ official.name.toLowerCase().includes(query)
+ ).slice(
+ (this.currentPage - 1) * this.pageSize,
+ this.currentPage * this.pageSize
+ );
+ },
+ filteredPublic() {
+ const query = this.searchQuery.toLowerCase();
+ if (!query) {
+ return this.publicList.slice(
+ (this.currentPage - 1) * this.pageSize,
+ this.currentPage * this.pageSize
+ );
+ }
+
+ return this.publicList.filter(item =>
+ item.wxid.toLowerCase().includes(query) ||
+ item.name.toLowerCase().includes(query)
+ ).slice(
+ (this.currentPage - 1) * this.pageSize,
+ this.currentPage * this.pageSize
+ );
}
},
mounted() {
@@ -251,6 +385,38 @@
console.error('加载个人联系人数据失败:', error);
this.$message.error('加载个人联系人数据失败');
});
+
+ // 加载公众号数据
+ axios.get('/api/contacts/official')
+ .then(response => {
+ if (response.data.success) {
+ const official = response.data.data.official;
+ this.officialList = Object.entries(official).map(([wxid, name]) => ({
+ wxid,
+ name
+ }));
+ }
+ })
+ .catch(error => {
+ console.error('加载公众号数据失败:', error);
+ this.$message.error('加载公众号数据失败');
+ });
+
+ // 加载公共好友数据
+ axios.get('/api/contacts/public')
+ .then(response => {
+ if (response.data.success) {
+ const publicFriends = response.data.data.public;
+ this.publicList = Object.entries(publicFriends).map(([wxid, name]) => ({
+ wxid,
+ name
+ }));
+ }
+ })
+ .catch(error => {
+ console.error('加载公共好友数据失败:', error);
+ this.$message.error('加载公共好友数据失败');
+ });
},
refreshContacts() {
this.loadContactsData();
@@ -272,6 +438,14 @@
viewUserDetails(user) {
this.currentUser = user;
this.userDetailDialogVisible = true;
+ },
+ viewOfficialDetails(official) {
+ this.currentOfficial = official;
+ this.officialDetailDialogVisible = true;
+ },
+ viewPublicDetails(publicFriend) {
+ this.currentPublic = publicFriend;
+ this.publicDetailDialogVisible = true;
}
}
});
diff --git a/utils/wechat/contact_manager.py b/utils/wechat/contact_manager.py
index fda1dee..bcfba39 100644
--- a/utils/wechat/contact_manager.py
+++ b/utils/wechat/contact_manager.py
@@ -12,8 +12,19 @@ class ContactManager:
_contacts: Dict[str, str] = {}
_group_contacts: Dict[str, str] = {} # 群组联系人
_personal_contacts: Dict[str, str] = {} # 个人联系人
+ _public_contacts: Dict[str, str] = {} # 公共好友
+ _official_accounts: Dict[str, str] = {} # 公众号
_initialized = False
_logger = logging.getLogger("ContactManager")
+
+ # 定义公共好友列表
+ _PUBLIC_FRIENDS = {
+ 'fmessage': '朋友推荐消息',
+ 'medianote': '语音记事本',
+ 'floatbottle': '漂流瓶',
+ 'mphelper': '公众平台安全助手',
+ 'filehelper': '文件传输助手'
+ }
def __new__(cls):
if cls._instance is None:
@@ -40,24 +51,35 @@ class ContactManager:
contacts: 联系人字典,格式为 {"wxid": "NickName"}
"""
self._contacts = contacts
- self._logger.info(f"联系人信息:contacts={contacts}")
self._logger.info(f"联系人信息已更新,共 {len(contacts)} 个联系人")
# 分类联系人
self._classify_contacts()
def _classify_contacts(self) -> None:
- """将联系人分类为群组和个人联系人"""
+ """将联系人分类为群组、个人联系人、公共好友和公众号"""
self._group_contacts = {}
self._personal_contacts = {}
+ self._public_contacts = {}
+ self._official_accounts = {}
for wxid, nickname in self._contacts.items():
- # 微信群ID通常以@@开头
- if wxid.startswith('@@'):
+ # 判断是否为公共好友
+ if wxid in self._PUBLIC_FRIENDS:
+ self._public_contacts[wxid] = self._PUBLIC_FRIENDS.get(wxid, nickname)
+ # 判断是否为公众号(wxid以gh_开头)
+ elif wxid.startswith('gh_'):
+ self._official_accounts[wxid] = nickname
+ # 判断是否为群组(wxid以@chatroom结尾)
+ elif wxid.endswith('@chatroom'):
self._group_contacts[wxid] = nickname
+ # 其他为普通好友和群成员
else:
self._personal_contacts[wxid] = nickname
- self._logger.info(f"联系人分类完成: {len(self._group_contacts)} 个群组, {len(self._personal_contacts)} 个个人联系人")
+ self._logger.info(f"联系人分类完成: {len(self._group_contacts)} 个群组, "
+ f"{len(self._personal_contacts)} 个个人联系人, "
+ f"{len(self._public_contacts)} 个公共好友, "
+ f"{len(self._official_accounts)} 个公众号")
def get_contacts(self) -> Dict[str, str]:
"""获取所有联系人
@@ -82,6 +104,22 @@ class ContactManager:
个人联系人字典,格式为 {"wxid": "NickName"}
"""
return self._personal_contacts
+
+ def get_public_contacts(self) -> Dict[str, str]:
+ """获取所有公共好友
+
+ Returns:
+ 公共好友字典,格式为 {"wxid": "NickName"}
+ """
+ return self._public_contacts
+
+ def get_official_accounts(self) -> Dict[str, str]:
+ """获取所有公众号
+
+ Returns:
+ 公众号字典,格式为 {"wxid": "NickName"}
+ """
+ return self._official_accounts
def get_nickname(self, wxid: str) -> str:
"""根据微信ID获取昵称
@@ -103,7 +141,11 @@ class ContactManager:
"""
self._contacts[wxid] = nickname
# 更新分类
- if wxid.endswith('@chatroom'):
+ if wxid in self._PUBLIC_FRIENDS:
+ self._public_contacts[wxid] = self._PUBLIC_FRIENDS.get(wxid, nickname)
+ elif wxid.startswith('gh_'):
+ self._official_accounts[wxid] = nickname
+ elif wxid.endswith('@chatroom'):
self._group_contacts[wxid] = nickname
else:
self._personal_contacts[wxid] = nickname
@@ -120,10 +162,12 @@ class ContactManager:
# 重新分类联系人
self._classify_contacts()
- def get_contact_statistics(self) -> Tuple[int, int, int]:
+ def get_contact_statistics(self) -> Tuple[int, int, int, int, int]:
"""获取联系人统计信息
Returns:
- 包含总联系人数、群组数和个人联系人数的元组
+ 包含总联系人数、群组数、个人联系人数、公共好友数和公众号数的元组
"""
- return len(self._contacts), len(self._group_contacts), len(self._personal_contacts)
\ No newline at end of file
+ return (len(self._contacts), len(self._group_contacts),
+ len(self._personal_contacts), len(self._public_contacts),
+ len(self._official_accounts))
\ No newline at end of file