package com.inspect.nvr.service; import com.alibaba.fastjson.JSONObject; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.RemovalCause; import com.inspect.nvr.daHuaCarme.jna.NetSDKLib; import com.inspect.nvr.daHuaCarme.jna.NetSDKLib.LLong; import com.inspect.nvr.domain.Infrared.NvrInfo; import com.inspect.nvr.utils.redis.RedisService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.concurrent.TimeUnit; /** * 大华登录服务 */ @Slf4j @Service public class DahuaLoginService { private static final String ERROR_LOGOUT_KEY = "dahua:error"; @Autowired private NetSDKLib dhNetSDK; @Resource private RedisService redisService; // 使用 Caffeine 缓存,10分钟未访问自动移除并登出 private final Cache sessionCache = Caffeine.newBuilder() // 10分钟未被get()就过期 .expireAfterAccess(10, TimeUnit.MINUTES) .removalListener((String ip, LLong loginID, RemovalCause cause) -> { if (loginID != null && (cause == RemovalCause.EXPIRED || cause == RemovalCause.SIZE)) { log.info("[大华]会话超时自动登出,ip: {},loginID: {}", ip, loginID); doLogout(ip, loginID); } }).build(); public synchronized LLong login(NvrInfo nvrInfo) { String ip = nvrInfo.getNvrIp(); LLong existLoginID = sessionCache.getIfPresent(ip); if (existLoginID != null) { log.info("[大华]登录命中缓存,ip: {},loginID: {}", ip, existLoginID); return existLoginID; } // 执行登录 NetSDKLib.NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY pstInParam = new NetSDKLib.NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY(); pstInParam.szIP = nvrInfo.getNvrIp().getBytes(); pstInParam.nPort = nvrInfo.getServerPort(); pstInParam.szUserName = nvrInfo.getAccount().getBytes(); pstInParam.szPassword = nvrInfo.getPassword().getBytes(); NetSDKLib.NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY pstOutParam = new NetSDKLib.NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY(); LLong loginID = dhNetSDK.CLIENT_LoginWithHighLevelSecurity(pstInParam, pstOutParam); if (loginID.intValue() == 0) { int errorCode = dhNetSDK.CLIENT_GetLastError(); throw new RuntimeException("登录失败,错误码:" + errorCode); } // 放入缓存,自动开始计时10分钟 sessionCache.put(nvrInfo.getNvrIp(), loginID); log.info("[大华]登录成功,ip:{},loginID:{}", ip, loginID); return loginID; } /** * 记录注销失败信息到 Redis */ private void recordLogoutError(String ip, LLong loginID, int errorCode) { JSONObject json = new JSONObject(); json.put("ip", ip); json.put("userId", loginID); json.put("errorCode", errorCode); json.put("time", System.currentTimeMillis()); redisService.redisTemplate.opsForZSet().add(ERROR_LOGOUT_KEY, json.toJSONString(), System.currentTimeMillis()); } /** * 登出具体实现 */ public void doLogout(String ip, LLong loginID) { if (loginID != null) { // 执行登出操作 boolean isLogout = dhNetSDK.CLIENT_Logout(loginID); if (isLogout) { log.info("[大华]自动注销成功,ip: {},loginID: {}", ip, loginID.longValue()); } else { int errorCode = dhNetSDK.CLIENT_GetLastError(); log.error("[大华]自动注销失败,ip: {},loginID: {},错误码: {}", ip, loginID.longValue(), errorCode); // 记录失败日志到 Redis recordLogoutError(ip, loginID, errorCode); } } } /** * 登出 */ public synchronized void logout(String ip) { LLong loginID = sessionCache.getIfPresent(ip); if (loginID != null) { sessionCache.invalidate(ip); } } /** * 登出所有用户 */ public void logoutAll() { // 获取所有缓存的IP和userID sessionCache.asMap().forEach((ip, loginID) -> { doLogout(ip, loginID); }); // 清空整个缓存 sessionCache.invalidateAll(); log.info("[大华]所有用户已登出"); } /** * 检查是否已登录(同时刷新过期时间) */ public boolean isLoggedIn(String ip) { return sessionCache.getIfPresent(ip) != null; } }