You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现投票会话开启1分钟后自动将setIsSessionOpen设为FALSE?

投票会话自动关闭的实现方案

当前你的openSession方法仅完成了投票会话的开启逻辑,缺少1分钟后自动调用setIsSessionOpen(false)关闭会话的核心逻辑,以下是几种可行的实现方案:


方案1:Spring异步定时任务(轻量快捷)

如果是Spring项目,可利用@Async结合延迟任务实现,无需额外依赖:

代码实现

// 启动类需添加@EnableAsync注解开启异步支持
@Service
public class SessionVoteService {

    private final SessionVoteRepository sessionVoteRepository;
    private final AgendaRepository agendaRepository;
    private final Logger LOGGER = LoggerFactory.getLogger(SessionVoteService.class);

    // 构造注入依赖

    public SessionVote openSession(String agendaSubject, Integer sessionId){
        SessionVote sessionVote = findBySessionVoteId(sessionId);
        Optional<Agenda> agenda = getAgendaBySubject(agendaSubject);
        
        if (agenda.isPresent()) {
            sessionVote.setAgenda(agenda.get());
            sessionVote.setIsSessionOpen(true);
            sessionVote.setStartedTime(System.currentTimeMillis());
            // 必须先保存会话状态到数据库
            sessionVoteRepository.save(sessionVote);
            
            LOGGER.info("The session ID {} is opened and started at {}. The subject for voting is: {}", 
                        sessionVote.getSessionVoteId(), sessionVote.getStartedTime(), sessionVote.getAgenda().getSubject());
            
            // 启动1分钟后关闭会话的异步任务
            scheduleSessionClose(sessionId);
            
            return sessionVote;
        } else {
            LOGGER.error("No subject was found");
            throw new NoSubjectFoundException(Constants.NO_SUBJECT_FOUND);
        }
    }

    @Async
    @Transactional
    void scheduleSessionClose(Integer sessionId) {
        try {
            // 延迟1分钟执行关闭逻辑
            Thread.sleep(60000);
            
            SessionVote sessionVote = findBySessionVoteId(sessionId);
            if (sessionVote != null && sessionVote.getIsSessionOpen()) {
                sessionVote.setIsSessionOpen(false);
                sessionVoteRepository.save(sessionVote);
                LOGGER.info("Session ID {} has been automatically closed after 1 minute", sessionId);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            LOGGER.error("Failed to schedule session close for ID {}", sessionId, e);
        }
    }

    // 省略findBySessionVoteId、getAgendaBySubject等方法
}

注意事项

  • 启动类必须添加@EnableAsync注解
  • 异步任务需添加@Transactional保证数据库操作的一致性
  • 避免主线程阻塞,任务逻辑需独立异步执行

方案2:Java原生ScheduledExecutorService(无框架依赖)

如果不想依赖Spring特性,可使用Java原生线程池实现延迟任务:

代码实现

@Service
public class SessionVoteService {

    // 单线程定时任务池,可根据需求调整线程数
    private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
    private final SessionVoteRepository sessionVoteRepository;
    // 其他依赖注入...

    public SessionVote openSession(String agendaSubject, Integer sessionId){
        // 原有会话开启逻辑...
        sessionVoteRepository.save(sessionVote);
        
        // 提交延迟1分钟的关闭任务
        scheduler.schedule(() -> closeSession(sessionId), 1, TimeUnit.MINUTES);
        
        return sessionVote;
    }

    @Transactional
    void closeSession(Integer sessionId) {
        SessionVote sessionVote = findBySessionVoteId(sessionId);
        if (sessionVote != null && sessionVote.getIsSessionOpen()) {
            sessionVote.setIsSessionOpen(false);
            sessionVoteRepository.save(sessionVote);
            LOGGER.info("Session ID {} closed automatically", sessionId);
        }
    }

    // 应用关闭时销毁线程池,避免资源泄漏
    @PreDestroy
    void shutdownScheduler() {
        scheduler.shutdown();
    }
}

注意事项

  • 需在应用关闭时调用shutdown()销毁线程池
  • 线程池大小可根据业务量调整,例如使用newScheduledThreadPool(3)

方案3:数据库定时扫描(分布式场景适配)

如果是分布式系统,内存定时任务可能因节点故障丢失任务,可采用数据库+定时扫描的方案:

步骤1:扩展SessionVote实体

添加expireTime字段存储会话到期时间:

@Entity
public class SessionVote {
    // 原有字段:votedYes、votedNo、isSessionOpen等
    private long startedTime;
    private long expireTime; // 会话到期时间(毫秒级时间戳)
}

步骤2:修改openSession方法

设置会话到期时间:

public SessionVote openSession(String agendaSubject, Integer sessionId){
    SessionVote sessionVote = findBySessionVoteId(sessionId);
    Optional<Agenda> agenda = getAgendaBySubject(agendaSubject);
    
    if (agenda.isPresent()) {
        long startTime = System.currentTimeMillis();
        sessionVote.setAgenda(agenda.get());
        sessionVote.setIsSessionOpen(true);
        sessionVote.setStartedTime(startTime);
        sessionVote.setExpireTime(startTime + 60000); // 设置1分钟后到期
        sessionVoteRepository.save(sessionVote);
        
        LOGGER.info("The session ID {} is opened and started at {}. The subject for voting is: {}", 
                    sessionVote.getSessionVoteId(), startTime, sessionVote.getAgenda().getSubject());
        
        return sessionVote;
    } else {
        // 异常处理逻辑...
    }
}

步骤3:编写定时扫描任务

每分钟检查并关闭到期会话:

@Service
public class SessionCleanupService {

    private final SessionVoteRepository sessionVoteRepository;
    private final Logger LOGGER = LoggerFactory.getLogger(SessionCleanupService.class);

    // 构造注入依赖

    @Scheduled(fixedRate = 60000) // 每分钟执行一次扫描
    @Transactional
    public void closeExpiredSessions() {
        long currentTime = System.currentTimeMillis();
        // 查询所有已到期且未关闭的会话
        List<SessionVote> expiredSessions = sessionVoteRepository.findByIsSessionOpenTrueAndExpireTimeLessThan(currentTime);
        
        for (SessionVote session : expiredSessions) {
            session.setIsSessionOpen(false);
            sessionVoteRepository.save(session);
            LOGGER.info("Expired session ID {} closed", session.getSessionVoteId());
        }
    }
}

注意事项

  • 启动类需添加@EnableScheduling开启定时任务
  • 可调整扫描频率(比如每10秒一次)提高实时性
  • 适合分布式环境,避免单点故障导致的任务丢失

通用注意事项

  • 并发校验:投票接口需额外校验会话状态(isSessionOpen)和当前时间是否在有效期内,避免延迟任务未执行完导致的无效投票
  • 事务一致性:所有修改会话状态的操作需添加@Transactional注解
  • 日志监控:完善会话开启、关闭、投票的日志记录,方便问题排查

内容的提问来源于stack exchange,提问作者dcm50

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 08:57:17