package com.project.ding.utils; import com.project.ding.domain.dto.UserDTO; import com.project.ding.domain.entity.SyncLogEntity; import com.project.ding.domain.entity.UserEntity; import com.project.ding.domain.service.UserBaseService; import com.project.ding.mapper.SyncLogMapper; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component; import java.util.Date; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @Component @Slf4j public class DingUserSyncUtil { @Resource(name = "dingUserSyncExecutor") private ExecutorService executor; @Autowired private StringRedisTemplate redisTemplate; @Autowired private UserBaseService userBaseService; @Autowired private DingUtil dingUtil; @Autowired private SyncLogMapper syncLogMapper; private static final String LOCK_KEY = "lock:ding:user_sync"; private static final String LAST_SYNC_KEY = "cache:ding:last_sync_time"; /** * 触发同步任务 * @param force 是否强制触发(跳过120分钟冷却检查) */ public String triggerSync(boolean force) { // 1. 抢占 Redis 锁(防止多个实例同时跑全量) Boolean acquired = redisTemplate.opsForValue().setIfAbsent(LOCK_KEY, "true", 1, TimeUnit.HOURS); if (Boolean.FALSE.equals(acquired)) { return "同步任务已在运行中,请勿重复触发"; } // 2. 提交异步任务(不使用 @Async) CompletableFuture.runAsync(() -> { try { // 3. 冷却检查 if (!force) { String lastSync = redisTemplate.opsForValue().get(LAST_SYNC_KEY); if (lastSync != null && (System.currentTimeMillis() - Long.parseLong(lastSync) < 120 * 60 * 1000)) { log.info("处于冷却期,跳过全量同步"); return; } } log.info(">>> 开始钉钉用户全量同步 (force={})", force); runActualSyncTask(); } catch (Exception e) { log.error("全量同步发生严重异常", e); } finally { // 4. 释放锁 redisTemplate.delete(LOCK_KEY); } }, executor); return "同步任务已启动,请查看系统日志或更新时间"; } private void runActualSyncTask() { // 插入一条同步日志 SyncLogEntity syncLogEntity = new SyncLogEntity(); syncLogEntity.setStartTime(new Date()); syncLogEntity.setStatus(0); syncLogMapper.insert(syncLogEntity); // 开始同步 try { List allUserDTOList = dingUtil.getAllUserDTO(); userBaseService.saveOrUpdateBatch(allUserDTOList.stream() .map(dto -> dto.toEntity(UserEntity::new)) .collect(Collectors.toList()) , 300); syncLogEntity.setStatus(1); syncLogEntity.setEndTime(new Date()); syncLogMapper.updateById(syncLogEntity); redisTemplate.opsForValue().set(LAST_SYNC_KEY, String.valueOf(System.currentTimeMillis())); log.info(">>> 全量同步完成"); } catch (Exception e) { // 同步失败更新 syncLogEntity.setEndTime(new Date()); syncLogEntity.setStatus(2); // 失败 syncLogEntity.setErrorMsg(e.getMessage()); syncLogMapper.updateById(syncLogEntity); log.error(">>> 全量同步过程报错", e); } } }