Compare commits

..

2 Commits

Author SHA1 Message Date
yuliang_guo
8024c38c32 fix: 修复钉钉同步误删用户的问题
All checks were successful
continuous-integration/drone/push Build is passing
问题原因:钉钉应用缺少手机号读取权限,导致返回的员工手机号全为空,
同步逻辑认为"钉钉没有这些员工"而错误删除了系统中的用户。

修复方案:
1. 增加安全检查:如果钉钉返回员工但手机号全为空,跳过删除操作
2. 使用双重匹配:同时考虑手机号和钉钉ID进行员工匹配
3. 增强日志:记录有手机号和钉钉ID的员工数量
4. 增加保护:只有手机号和钉钉ID都不匹配时才删除
2026-02-02 13:09:03 +08:00
yuliang_guo
8bfd5aa3de fix: 修复TrainingSession状态比较大小写问题
All checks were successful
continuous-integration/drone/push Build is passing
- COMPLETED -> completed (枚举值是小写)
2026-02-02 13:02:19 +08:00
2 changed files with 53 additions and 12 deletions

View File

@@ -103,7 +103,7 @@ class DashboardService:
try: try:
result = await self.db.execute( result = await self.db.execute(
select(func.coalesce(func.sum(TrainingSession.duration_seconds), 0)) select(func.coalesce(func.sum(TrainingSession.duration_seconds), 0))
.where(TrainingSession.status == 'COMPLETED') .where(TrainingSession.status == 'completed') # 修复: 使用小写
) )
training_hours = (result.scalar() or 0) / 3600 training_hours = (result.scalar() or 0) / 3600
except Exception as e: except Exception as e:

View File

@@ -611,7 +611,7 @@ class EmployeeSyncService:
""" """
增量同步员工数据 增量同步员工数据
- 新增钉钉有但系统没有的员工 - 新增钉钉有但系统没有的员工
- 删除系统有但钉钉没有的员工(物理删除) - 删除系统有但钉钉没有的员工(删除)
- 跳过两边都存在的员工(不做任何修改) - 跳过两边都存在的员工(不做任何修改)
Returns: Returns:
@@ -634,8 +634,22 @@ class EmployeeSyncService:
try: try:
# 1. 获取钉钉在职员工数据 # 1. 获取钉钉在职员工数据
dingtalk_employees = await self.fetch_employees_from_dingtalk() dingtalk_employees = await self.fetch_employees_from_dingtalk()
# 使用手机号和钉钉ID双重匹配
dingtalk_phones = {emp.get('phone') for emp in dingtalk_employees if emp.get('phone')} 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和已软删除的 # 2. 获取系统现有用户排除admin和已软删除的
stmt = select(User).where( stmt = select(User).where(
@@ -645,16 +659,26 @@ class EmployeeSyncService:
result = await self.db.execute(stmt) result = await self.db.execute(stmt)
system_users = result.scalars().all() system_users = result.scalars().all()
system_phones = {user.phone for user in system_users if user.phone} 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_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 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. 新增员工 # 4. 新增员工
for employee in dingtalk_employees: for employee in dingtalk_employees:
@@ -724,12 +748,29 @@ class EmployeeSyncService:
# 先flush之前的新增操作 # 先flush之前的新增操作
await self.db.flush() await self.db.flush()
# 标记离职员工 # 如果跳过删除,则不处理
if skip_delete:
logger.info("⚠️ 由于安全检查未通过,跳过离职员工处理")
else:
# 标记离职员工需要手机号和钉钉ID都不在钉钉列表中才删除
for user in system_users: for user in system_users:
if user.phone and user.phone in phones_to_delete:
# 双重保护确保不删除admin # 双重保护确保不删除admin
if user.username == 'admin' or user.role == '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 continue
try: try: