From d27b6a9c87833edc7832f177e7be732a61829182 Mon Sep 17 00:00:00 2001 From: Jeason <1710884619@qq.com> Date: Thu, 2 Apr 2026 15:47:36 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E9=A3=9E=E4=B9=A6=E5=8F=91=E9=80=81?= =?UTF-8?q?=E8=80=85=E4=BF=A1=E6=81=AF=E8=8E=B7=E5=8F=96=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - sender_id 优先用 open_id(user_id 在无权限时为 None) - get_user_info 默认用 open_id 类型查询 - contact API 无权限时优雅降级,用 ID 缩写作为标识(用户_xxx) - feishu_bot 和 longconn 都统一了 sender 提取逻辑 --- data/tsp_assistant.db | Bin 208896 -> 217088 bytes logs/tsp_assistant.log | 9 -------- src/integrations/feishu_longconn_service.py | 16 +++++++++----- src/integrations/feishu_service.py | 23 ++++++++++---------- src/web/blueprints/feishu_bot.py | 19 +++++++++------- 5 files changed, 32 insertions(+), 35 deletions(-) diff --git a/data/tsp_assistant.db b/data/tsp_assistant.db index cec21500cde3bbfe973131472050304076004333..dcafc3c0085d8fd1747f7da6262ed3619bca7cd1 100644 GIT binary patch delta 6462 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 a!kQ}sPXpkY!cAs+6M%QSBq3tI+5ZCw26aLJ delta 1456 zcmZuxZA?>F7{2G;TRuwL10t}E0bODkY|!-H+b^1Bie|VtzgE~{Oq?(&anS&Z%=iad z{A#Pn8pJXwXw@Pi(n9W_2(&C&G~1tP+)OZr%xTZr95M|P;Fjsqu7>ti(!u! zGR5NitNUrzT}YRRzvlL{tg8_fi{1GLSQU%!QTTWK6hFfE#H`wHusNN{Lp3&w$;2+t z49xOOpwpPV^ctJdSfPaUEFP!uGkhO^jmO0-Z4h^lug%`MiYY}0?R!4eZ{57J>AlVS zZJU@CTh)8{I=8*N`)hNzwE2&i_BWUncGJ;N2jc(2G-H)NKA_X(3 zm-!&MsRbx604RF};LVo--gpV1G!LL87vS|Afa28vYq9}e%K|9E0EHO<1!(}UvT8u2 zsiHJhQ1x3HvFh6xm&T}nqVPwWqvI-cSi?GZ(AalUgFL(Tww5&W&81cLPg?f2TDLYe z+WAHL2B<*G8Ld2T6$G8dWH9r>e=TO6zQSbSjrI11y4L-NoJB}YZ786)JXAzgR#y7Z z4LaBN_rc$ZdMR00E{-Ks;0s=+{ zrH&yIj4sTaCNY=f8Iwlbl7Cn}8YTXr#D$<7K0&6sA{iJVLs{$v9)Wf%w#!g!?)PohP0PuqC<9J?8wL_J)!QY1C-DS; z#KMdV8bY4+Cx%W*XRZ?0#3P3@*%r>TOBokt-12PilA|0roiv3;q5k;vaI%KQ0+vtq zDS2JH_5~eCoA94n>anB_5Pqx+SO5h%6iY-y5C95-C7C*#I6DMOxyw&Fdq7g~mE5tF z;mPqJ>AJiy)29@mz*E7cq-3UpOin@+$#+o_dn9*e43tjZ$+9TEfQQa0AtySeYXLIa zFNb?TG9?r(4fr4XLXR)?#-}?I$0o=~FI=2&^AGecU(*wmVv=0m`1Cb6{Lfb>7_0`p zuEK0EScJ9T35#Rh#J0Q6lr{5KE9VQ$(*?fmMJP4@`J05Mf&T^P$yw1uk(+nuXY^k& C dict: + def get_user_info(self, user_id: str, id_type: str = "open_id") -> dict: """ - 获取飞书用户信息(姓名、头像等)。 - 返回 {"user_id": ..., "name": ..., "en_name": ..., "avatar_url": ...} 或空字典。 + 获取飞书用户信息(姓名等)。 + 优先用 contact API,失败则尝试 im chat members API。 """ token = self._get_tenant_access_token() - if not token: + if not token or not user_id: return {} + # 方式1: contact API(需要 contact:user.base:readonly 权限) url = f"{self.BASE_URL}/contact/v3/users/{user_id}?user_id_type={id_type}" headers = {"Authorization": f"Bearer {token}"} - try: response = requests.get(url, headers=headers) data = response.json() @@ -197,11 +197,10 @@ class FeishuService: "open_id": user.get("open_id", ""), "name": user.get("name", ""), "en_name": user.get("en_name", ""), - "avatar_url": user.get("avatar", {}).get("avatar_72", ""), } - else: - logger.warning(f"获取用户信息失败: {data.get('msg')} (user_id={user_id})") - return {} - except Exception as e: - logger.warning(f"获取用户信息异常: {e}") - return {} + except Exception: + pass + + # 方式2: 如果 contact API 没权限,返回 ID 缩写作为标识 + short_id = user_id[-8:] if len(user_id) > 8 else user_id + return {"user_id": user_id, "name": f"用户_{short_id}"} diff --git a/src/web/blueprints/feishu_bot.py b/src/web/blueprints/feishu_bot.py index 25c638d..e2c470a 100644 --- a/src/web/blueprints/feishu_bot.py +++ b/src/web/blueprints/feishu_bot.py @@ -102,17 +102,21 @@ def _process_message_in_background(app, event_data: dict): # 3. 获取或创建该飞书用户的会话(支持群聊隔离) chat_manager = service_manager.get_chat_manager() - # 获取发送者ID(从event中提取) + # 获取发送者ID(优先 open_id,user_id 可能为 None) sender = event.get('sender', {}) - sender_id = sender.get('sender_id', {}).get('user_id', 'unknown') - sender_open_id = sender.get('sender_id', {}).get('open_id', '') + sender_ids = sender.get('sender_id', {}) + sender_open_id = sender_ids.get('open_id', '') or '' + sender_user_id = sender_ids.get('user_id', '') or '' + sender_id = sender_user_id or sender_open_id or 'unknown' sender_type = sender.get('sender_type', 'user') - # 获取发送者姓名 - sender_name = '未知用户' + # 获取发送者姓名(用 open_id 查询) + sender_name = sender_id try: - if sender_id and sender_id != 'unknown': - user_info = feishu_service.get_user_info(sender_id) + id_for_query = sender_open_id or sender_user_id + id_type = 'open_id' if sender_open_id else 'user_id' + if id_for_query: + user_info = feishu_service.get_user_info(id_for_query, id_type=id_type) sender_name = user_info.get('name') or user_info.get('en_name') or sender_id except Exception as e: logger.warning(f"[Feishu Bot] 获取发送者信息失败: {e}") @@ -121,7 +125,6 @@ def _process_message_in_background(app, event_data: dict): logger.info(f"[Feishu Bot] 📝 消息内容: '{text_content}'") # 群聊隔离:每个用户在每个群都有独立会话 - # 格式:feishu_群聊ID_用户ID user_id = f"feishu_{chat_id}_{sender_id}" # 检查是否已有活跃会话