liujie
2026-08-11 323764c813a09cd56cdab780155ef659dbe0b364
ruoyi-applet/src/main/java/com/ruoyi/web/controller/service/SysVisitorRecordService.java
@@ -5,11 +5,14 @@
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.system.domain.SysVisitorRecord;
import com.ruoyi.system.mapper.SysVisitorRecordMapper;
import com.ruoyi.web.integration.dahua.DahuaVisitorAccessService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import java.util.Date;
import java.util.List;
@@ -23,14 +26,27 @@
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private DahuaVisitorAccessService dahuaVisitorAccessService;
    private static final String REDIS_KEY_PREFIX = "visitor:expire:";
    /**
     * 登记访客时设置 Redis 过期 Key
     */
    public void registerVisitor(SysVisitorRecord record) {
        if (record.getValidEndTime() == null) {
            throw new IllegalArgumentException("访客有效期结束时间不能为空");
        }
        if (!record.getValidEndTime().after(new Date())) {
            log.warn("访客记录 {} 登记时已超期,不下发门禁权限", record.getRecordId());
            autoSignOut(record);
            return;
        }
        // 2. 设置 Redis 过期 Key
        // 下发门禁权限后设置过期监听,确保登记与到期撤权使用同一条业务链路。
        DahuaVisitorAccessService.OperationResult accessResult = dahuaVisitorAccessService.grant(record);
        registerGrantRollbackCompensation(record, accessResult);
        setRedisExpireKey(record);
        log.info("访客 {} 登记成功,有效期至:{}", record.getRecordId(), record.getValidEndTime());
@@ -42,6 +58,10 @@
    private void setRedisExpireKey(SysVisitorRecord record) {
        Date now = new Date();
        Date validEndTime = record.getValidEndTime();
        if (validEndTime == null) {
            throw new IllegalArgumentException("访客有效期结束时间不能为空");
        }
        // 如果有效期结束时间小于当前时间,直接签离
        if (validEndTime.before(now)) {
@@ -110,11 +130,42 @@
     */
    private void autoSignOut(SysVisitorRecord record) {
        Date now = new Date();
        revokeAccess(record);
        record.setStatus("PENDING_CONFIRM");
        record.setSignTime(now);
        record.setRemark("逾期滞留,等待确认");
        record.setUpdateTime(now);
        this.updateById(record);
    }
    /**
     * 撤销访客在大华门禁设备上的人脸及人员权限。
     */
    public void revokeAccess(SysVisitorRecord record) {
        dahuaVisitorAccessService.revoke(record);
    }
    private void registerGrantRollbackCompensation(SysVisitorRecord record,
                                                   DahuaVisitorAccessService.OperationResult accessResult) {
        if (accessResult.getSuccessCount() <= 0 || !TransactionSynchronizationManager.isSynchronizationActive()) {
            return;
        }
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
            @Override
            public void afterCompletion(int status) {
                if (status != TransactionSynchronization.STATUS_ROLLED_BACK) {
                    return;
                }
                redisTemplate.delete(REDIS_KEY_PREFIX + record.getRecordId());
                try {
                    dahuaVisitorAccessService.revoke(record);
                    log.info("访客登记事务回滚,大华门禁权限补偿撤销完成,recordId={}", record.getRecordId());
                } catch (Exception e) {
                    log.error("访客登记事务回滚,大华门禁权限补偿撤销失败,recordId={}",
                            record.getRecordId(), e);
                }
            }
        });
    }
    /**
@@ -164,6 +215,7 @@
        int count = 0;
        for (SysVisitorRecord record : overdueRecords) {
            try {
                revokeAccess(record);
                record.setStatus("OVERDUE_AUTO_SIGNED_OUT");
                record.setSignTime(now);
                record.setRemark("系统自动签离(启动时检查)");