From 8024c38c3285fd793a8971b66318ee8e1585f22e Mon Sep 17 00:00:00 2001 From: yuliang_guo Date: Mon, 2 Feb 2026 13:09:03 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E9=92=89=E9=92=89?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=E8=AF=AF=E5=88=A0=E7=94=A8=E6=88=B7=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题原因:钉钉应用缺少手机号读取权限,导致返回的员工手机号全为空, 同步逻辑认为"钉钉没有这些员工"而错误删除了系统中的用户。 修复方案: 1. 增加安全检查:如果钉钉返回员工但手机号全为空,跳过删除操作 2. 使用双重匹配:同时考虑手机号和钉钉ID进行员工匹配 3. 增强日志:记录有手机号和钉钉ID的员工数量 4. 增加保护:只有手机号和钉钉ID都不匹配时才删除 --- backend/app/services/employee_sync_service.py | 63 +++++++++++++++---- 1 file changed, 52 insertions(+), 11 deletions(-) diff --git a/backend/app/services/employee_sync_service.py b/backend/app/services/employee_sync_service.py index 906ae4d..8f54994 100644 --- a/backend/app/services/employee_sync_service.py +++ b/backend/app/services/employee_sync_service.py @@ -611,7 +611,7 @@ class EmployeeSyncService: """ 增量同步员工数据 - 新增钉钉有但系统没有的员工 - - 删除系统有但钉钉没有的员工(物理删除) + - 删除系统有但钉钉没有的员工(软删除) - 跳过两边都存在的员工(不做任何修改) Returns: @@ -634,8 +634,22 @@ class EmployeeSyncService: try: # 1. 获取钉钉在职员工数据 dingtalk_employees = await self.fetch_employees_from_dingtalk() + + # 使用手机号和钉钉ID双重匹配 dingtalk_phones = {emp.get('phone') for emp in dingtalk_employees if emp.get('phone')} - logger.info(f"钉钉在职员工数量: {len(dingtalk_phones)}") + dingtalk_ids = {emp.get('dingtalk_id') for emp in dingtalk_employees if emp.get('dingtalk_id')} + + logger.info(f"钉钉在职员工数量: {len(dingtalk_employees)}") + logger.info(f"有手机号的员工: {len(dingtalk_phones)}") + logger.info(f"有钉钉ID的员工: {len(dingtalk_ids)}") + + # 安全检查:如果钉钉返回了员工但手机号全为空,可能是权限问题,跳过删除操作 + skip_delete = False + if len(dingtalk_employees) > 0 and len(dingtalk_phones) == 0: + logger.warning("⚠️ 钉钉返回员工数据但手机号全为空,可能是钉钉应用缺少手机号读取权限!") + logger.warning("⚠️ 跳过离职员工处理,避免误删") + skip_delete = True + stats['errors'].append("钉钉应用可能缺少手机号读取权限,跳过删除操作") # 2. 获取系统现有用户(排除admin和已软删除的) stmt = select(User).where( @@ -645,16 +659,26 @@ class EmployeeSyncService: result = await self.db.execute(stmt) system_users = result.scalars().all() system_phones = {user.phone for user in system_users if user.phone} - logger.info(f"系统现有员工数量(排除admin): {len(system_phones)}") + system_dingtalk_ids = {user.dingtalk_id for user in system_users if user.dingtalk_id} + logger.info(f"系统现有员工数量(排除admin): {len(system_users)}") + logger.info(f"系统有手机号的员工: {len(system_phones)}") + logger.info(f"系统有钉钉ID的员工: {len(system_dingtalk_ids)}") - # 3. 计算需要新增、删除、跳过的员工 + # 3. 计算需要新增、删除、跳过的员工(同时考虑手机号和钉钉ID) + # 新增: 钉钉有但系统没有(手机号或钉钉ID都不存在) phones_to_add = dingtalk_phones - system_phones - phones_to_delete = system_phones - dingtalk_phones + ids_to_add = dingtalk_ids - system_dingtalk_ids + + # 删除: 系统有但钉钉没有(手机号和钉钉ID都不在钉钉列表中) + phones_to_delete = system_phones - dingtalk_phones if not skip_delete else set() + + # 跳过: 两边都存在 phones_to_skip = dingtalk_phones & system_phones + ids_to_skip = dingtalk_ids & system_dingtalk_ids - logger.info(f"待新增: {len(phones_to_add)}, 待删除: {len(phones_to_delete)}, 跳过: {len(phones_to_skip)}") + logger.info(f"待新增(手机号): {len(phones_to_add)}, 待删除(手机号): {len(phones_to_delete)}, 跳过(手机号): {len(phones_to_skip)}") - stats['skipped_count'] = len(phones_to_skip) + stats['skipped_count'] = len(phones_to_skip) + len(ids_to_skip) # 4. 新增员工 for employee in dingtalk_employees: @@ -724,12 +748,29 @@ class EmployeeSyncService: # 先flush之前的新增操作 await self.db.flush() - # 标记离职员工 - for user in system_users: - if user.phone and user.phone in phones_to_delete: + # 如果跳过删除,则不处理 + if skip_delete: + logger.info("⚠️ 由于安全检查未通过,跳过离职员工处理") + else: + # 标记离职员工(需要手机号和钉钉ID都不在钉钉列表中才删除) + for user in system_users: # 双重保护:确保不删除admin if user.username == 'admin' or user.role == 'admin': - logger.warning(f"⚠️ 跳过处理管理员账户: {user.username}") + continue + + # 检查用户是否在钉钉列表中(手机号或钉钉ID匹配任一即视为在职) + in_dingtalk = False + if user.phone and user.phone in dingtalk_phones: + in_dingtalk = True + if user.dingtalk_id and user.dingtalk_id in dingtalk_ids: + in_dingtalk = True + + if in_dingtalk: + continue # 在钉钉中,跳过 + + # 额外安全检查:如果钉钉没有返回有效数据,不删除 + if len(dingtalk_phones) == 0 and len(dingtalk_ids) == 0: + logger.warning(f"⚠️ 钉钉数据为空,跳过删除用户: {user.full_name}") continue try: