import logger from "../../dxmodules/dxLogger.js"
|
import std from "../../dxmodules/dxStd.js"
|
import config from "../../dxmodules/dxConfig.js"
|
import dxCommonUtils from "../../dxmodules/dxCommonUtils.js"
|
import dxos from "../../dxmodules/dxOs.js"
|
import map from "../../dxmodules/dxMap.js"
|
import bus from "../../dxmodules/dxEventBus.js"
|
import driver from "../driver.js"
|
import mqttService from "./mqttService.js"
|
import sqliteService from "./sqliteService.js"
|
import utils from '../common/utils/utils.js'
|
import grainService from './grainService.js'
|
const accessService = {}
|
const mqtt_map = map.get("MQTT")
|
|
// 双人认证相关变量
|
let dualAuthTimeout = null
|
const MAX_QUEUE_SIZE = 2 // 最大队列长度,用于双人认证
|
|
// 使用dxMap存储认证队列和超时定时器,确保所有worker线程都能访问同一个队列和定时器
|
const AUTH_QUEUE_TOPIC = "AUTH_QUEUE"
|
const AUTH_QUEUE_KEY = "queue"
|
const AUTH_TIMEOUT_KEY = "timeout"
|
|
const getAuthQueue = function() {
|
try {
|
// 获取AUTH_QUEUE主题的map实例
|
const authMap = map.get(AUTH_QUEUE_TOPIC)
|
// 从map中获取队列
|
let queue = authMap.get(AUTH_QUEUE_KEY)
|
// 确保返回的是一个数组
|
if (!queue || typeof queue !== 'object' || !Array.isArray(queue)) {
|
queue = []
|
// 保存到map中
|
authMap.put(AUTH_QUEUE_KEY, queue)
|
}
|
return queue
|
} catch (error) {
|
logger.error("获取认证队列失败: " + error.message)
|
// 出错时返回空数组
|
return []
|
}
|
}
|
|
const updateAuthQueue = function(queue) {
|
try {
|
// 获取AUTH_QUEUE主题的map实例
|
const authMap = map.get(AUTH_QUEUE_TOPIC)
|
// 确保存储的是一个数组
|
if (Array.isArray(queue)) {
|
authMap.put(AUTH_QUEUE_KEY, queue)
|
} else {
|
logger.error("更新认证队列失败: 队列必须是数组")
|
}
|
} catch (error) {
|
logger.error("更新认证队列失败: " + error.message)
|
}
|
}
|
|
const clearAuthQueue = function() {
|
try {
|
// 获取AUTH_QUEUE主题的map实例
|
const authMap = map.get(AUTH_QUEUE_TOPIC)
|
// 清空队列
|
authMap.put(AUTH_QUEUE_KEY, [])
|
} catch (error) {
|
logger.error("清空认证队列失败: " + error.message)
|
}
|
}
|
|
const getAuthTimeout = function() {
|
try {
|
// 获取AUTH_QUEUE主题的map实例
|
const authMap = map.get(AUTH_QUEUE_TOPIC)
|
// 从map中获取超时定时器
|
return authMap.get(AUTH_TIMEOUT_KEY)
|
} catch (error) {
|
logger.error("获取认证超时定时器失败: " + error.message)
|
// 出错时返回null
|
return null
|
}
|
}
|
|
const setAuthTimeout = function(timeoutId) {
|
try {
|
// 获取AUTH_QUEUE主题的map实例
|
const authMap = map.get(AUTH_QUEUE_TOPIC)
|
// 保存超时定时器
|
authMap.put(AUTH_TIMEOUT_KEY, timeoutId)
|
} catch (error) {
|
logger.error("设置认证超时定时器失败: " + error.message)
|
}
|
}
|
|
const clearAuthTimeout = function() {
|
try {
|
// 获取AUTH_QUEUE主题的map实例
|
const authMap = map.get(AUTH_QUEUE_TOPIC)
|
// 清空超时定时器
|
authMap.put(AUTH_TIMEOUT_KEY, null)
|
} catch (error) {
|
logger.error("清空认证超时定时器失败: " + error.message)
|
}
|
}
|
|
/**
|
* 人脸/密码白名单校验
|
* @param {object} data {type:码制(string),code:码内容(string)}
|
* @returns number:-1(参数错误),0(通行成功),1(在线验证),string:校验失败的原因
|
*/
|
accessService.access = function (data, fileName, similarity) {
|
let language = config.get("base.language") || "CN";
|
|
// 打印认证队列状态
|
logger.info("[access]: access函数开始执行,当前认证队列状态: " + JSON.stringify(getAuthQueue()))
|
logger.info("[access]: 当前认证用户: " + JSON.stringify(data))
|
|
// 通行加锁
|
let lockMap = map.get("access_lock")
|
if (lockMap.get("access_lock")) {
|
logger.error("[access]: 通行加锁,请稍后再试")
|
return false
|
}
|
lockMap.put("access_lock", true)
|
|
try {
|
data.timeStamp = Math.floor(Date.parse(new Date()) / 1000)
|
data.message = ""
|
// 先根据code查询凭证
|
let res
|
// 确保 type 是字符串类型
|
const typeStr = data.type.toString()
|
if (typeStr == "300") {
|
res = sqliteService.d1_voucher.findByUserIdAndType(data.code, typeStr)
|
} else {
|
res = sqliteService.d1_voucher.findByCodeAndType(data.code, typeStr)
|
}
|
// 认证结果
|
let ret = true
|
// 是否是陌生人
|
let isStranger = false
|
if (similarity === false) {
|
// 如果相似度验证失败,则不进行认证
|
ret = false
|
isStranger = true
|
} else {
|
|
if (res.length == 0) {
|
logger.error("[access]: 通行失败,没查询到凭证!")
|
ret = false
|
isStranger = true
|
data.message = "Voucher verification failed"
|
} else {
|
data.userId = res[0].userId
|
data.keyId = res[0].keyId
|
// 根据userId查人员
|
res = sqliteService.d1_person.findByUserId(data.userId)
|
if (res.length == 0) {
|
logger.error("[access]: 通行失败,没查询到人员!")
|
ret = false
|
isStranger = true
|
data.message = "Personnel verification failed"
|
} else {
|
let idCard
|
let userType = 0
|
try {
|
idCard = JSON.parse(res[0].extra).idCard
|
userType = JSON.parse(res[0].extra).type || 0
|
} catch (error) {
|
logger.error("无身份证号或类型")
|
}
|
data.extra = { name: res[0].name, idCard: idCard, type: userType }
|
data.permissionIds = res[0].permissionIds
|
|
}
|
}
|
|
// 处理双人认证信息
|
if (data.dualAuthInfo) {
|
logger.info("[access]: 处理双人认证信息: " + JSON.stringify(data.dualAuthInfo))
|
// 存储第一用户信息
|
let firstUserId = data.dualAuthInfo.firstUserId
|
// 查询第一用户的详细信息
|
let res1 = sqliteService.d1_person.findByUserId(firstUserId)
|
if (res1.length > 0) {
|
// 获取第一用户的姓名、身份证号和身份类型
|
let idCard1
|
let firstUserType = 0
|
try {
|
idCard1 = JSON.parse(res1[0].extra).idCard
|
firstUserType = JSON.parse(res1[0].extra).type || 0
|
} catch (error) {
|
logger.error("无第一用户身份证号或类型")
|
}
|
data.userId = firstUserId
|
data.extra = { name: res1[0].name, idCard: idCard1, type: firstUserType }
|
} else {
|
// 如果没有查询到第一用户信息,使用默认值
|
data.userId = firstUserId
|
data.extra = { name: data.dualAuthInfo.firstUserName, idCard: "", type: 0 }
|
}
|
// 存储第二用户信息
|
data.userId2 = data.dualAuthInfo.secondUserId
|
// 查询第二用户的详细信息
|
let res2 = sqliteService.d1_person.findByUserId(data.dualAuthInfo.secondUserId)
|
if (res2.length > 0) {
|
// 获取第二用户的姓名和身份证号
|
let idCard2
|
let secondUserType = 0
|
try {
|
idCard2 = JSON.parse(res2[0].extra).idCard
|
secondUserType = JSON.parse(res2[0].extra).type || 0
|
} catch (error) {
|
logger.error("无第二用户身份证号或类型")
|
}
|
data.extra2 = { name: res2[0].name, idCard: idCard2 }
|
// 存储第二用户的权限ID(身份类型)
|
data.permissionId2 = secondUserType.toString()
|
} else {
|
// 如果没有查询到第二用户信息,使用默认值
|
data.extra2 = { name: data.dualAuthInfo.secondUserName, idCard: "" }
|
data.permissionId2 = ""
|
}
|
// 处理第一用户的人脸图片
|
if (data.firstUserFileName) {
|
let firstUserSrc = `/data/passRecord/${firstUserId}_${data.timeStamp}_1.jpg`
|
std.ensurePathExists(firstUserSrc) // 确保目录存在
|
if (std.exist(data.firstUserFileName)) {
|
// 移动图片到指定目录
|
dxos.systemBrief(`mv ${data.firstUserFileName} ${firstUserSrc}`)
|
// 更新data中的code为第一用户的图片路径
|
data.code = firstUserSrc
|
}
|
}
|
// 处理第二用户的人脸图片
|
if (fileName) {
|
let secondUserSrc = `/data/passRecord/${data.userId2}_${data.timeStamp}_2.jpg`
|
std.ensurePathExists(secondUserSrc) // 确保目录存在
|
if (std.exist(fileName)) {
|
// 移动图片到指定目录
|
dxos.systemBrief(`mv ${fileName} ${secondUserSrc}`)
|
// 更新data中的code2为第二用户的图片路径
|
data.code2 = secondUserSrc
|
}
|
}
|
}
|
|
if (ret) {
|
// 根据userId查询权限
|
let permissionIds = data.permissionIds.split(",")
|
let permissions = []
|
for (let i = 0; i < permissionIds.length; i++) {
|
const element = permissionIds[i];
|
let res = sqliteService.d1_permission.findByPermissionId(element)
|
permissions.push(...res)
|
}
|
let permissionId = judgmentPermission(permissions)
|
if (permissions && permissions.length > 0 && permissionId) {
|
logger.info("[access]: 权限认证通过")
|
|
// 存储用户身份类型作为权限ID(本系统中身份即权限)
|
let userType = 0
|
try {
|
if (data.extra) {
|
userType = data.extra.type || 0
|
} else {
|
// 从数据库查询用户信息获取身份类型
|
let userRes = sqliteService.d1_person.findByUserId(data.userId)
|
if (userRes && userRes.length > 0) {
|
userType = JSON.parse(userRes[0].extra).type || 0
|
}
|
}
|
} catch (error) {
|
logger.error("解析用户类型失败")
|
}
|
data.permissionId = userType.toString()
|
|
// 检查是否是双人认证模式
|
if (!data.dualAuthInfo) {
|
// 单人通行:科长(type=1)权限直接通过
|
if (userType === 1) {
|
logger.info("[access]: 科长权限,单人通行认证通过")
|
ret = true
|
} else if (userType === 0) { // 保管员(type=0)需要双人认证
|
let authQueue = getAuthQueue()
|
|
// 检查认证队列长度
|
if (authQueue.length >= MAX_QUEUE_SIZE) {
|
// 队列已满,清除队列,重新开始认证
|
logger.info("[access]: 认证队列已满,重置认证流程")
|
clearAuthQueue()
|
authQueue = getAuthQueue()
|
}
|
|
// 检查当前用户是否已经在队列中(避免重复认证)
|
const isUserInQueue = authQueue.some(item => item.userId === data.userId)
|
if (isUserInQueue) {
|
logger.info("[access]: 用户已在认证队列中,跳过重复认证")
|
// 解锁
|
lockMap.put("access_lock", false)
|
logger.error("[access]: 解锁成功")
|
driver.finger.controlLed("BLUE")
|
return
|
}
|
|
// 打印当前队列状态
|
logger.info("[access]: 当前认证队列状态: " + JSON.stringify(authQueue))
|
|
// 将当前用户添加到认证队列
|
authQueue.push({
|
userId: data.userId,
|
name: data.extra.name,
|
type: userType,
|
fileName: fileName,
|
data: data
|
})
|
|
// 更新队列到dxMap
|
updateAuthQueue(authQueue)
|
|
logger.info("[access]: 用户添加到认证队列,当前队列长度: " + authQueue.length)
|
|
if (authQueue.length === 1) {
|
// 第一用户认证
|
logger.info("[access]: 保管员权限,需要双人认证")
|
// 触发第一用户认证成功事件,更新UI
|
bus.fire("accessSuccess", {
|
data: {
|
userId: data.userId,
|
extra: data.extra,
|
dualAuthInfo: { firstUserId: data.userId, firstUserName: data.extra.name }
|
},
|
fileName: fileName
|
})
|
// 语音提示第二用户进行认证
|
driver.audio.play(`/app/code/resource/${language}/wav/user2.wav`)
|
// 等待第二用户认证,设置60秒超时
|
let newTimeout = std.setTimeout(() => {
|
// 检查队列是否为空,如果为空则不触发超时错误
|
let authQueue = getAuthQueue()
|
if (authQueue.length === 0) {
|
logger.info("[access]: 认证队列为空,跳过超时处理")
|
return
|
}
|
logger.info("[access]: 双人认证超时,认证失败")
|
// 保存超时失败的通行记录
|
if (authQueue.length > 0) {
|
let firstUser = authQueue[0]
|
let timeoutData = {
|
type: firstUser.data.type,
|
code: firstUser.data.code,
|
timeStamp: Math.floor(Date.now() / 1000),
|
userId: firstUser.userId,
|
extra: { name: firstUser.name },
|
message: "双人认证超时"
|
}
|
savePassPic(timeoutData, firstUser.fileName)
|
reply(timeoutData, false)
|
}
|
// 清空认证队列
|
clearAuthQueue()
|
// 清空超时定时器
|
clearAuthTimeout()
|
// 触发失败弹窗
|
bus.fire("showAccessResult", {
|
faceAuth: true,
|
gasConcentration: false,
|
accessAllowed: false,
|
message: "*双人认证超时,禁止通行*"
|
})
|
driver.audio.play(`/app/code/resource/${language}/wav/recg_f.wav`)
|
}, 60000)
|
// 保存超时定时器
|
setAuthTimeout(newTimeout)
|
// 解锁
|
lockMap.put("access_lock", false)
|
logger.error("[access]: 解锁成功")
|
driver.finger.controlLed("BLUE")
|
// 第一用户认证成功,等待第二用户,不设置ret,直接返回
|
return
|
} else if (authQueue.length === 2) {
|
// 第二用户认证,进行双人认证
|
logger.info("[access]: 第二用户认证,进行双人认证")
|
|
// 打印队列中的用户信息
|
logger.info("[access]: 认证队列中的用户: " + JSON.stringify(authQueue))
|
|
const firstUser = authQueue[0]
|
const secondUser = authQueue[1]
|
|
// 合并第一用户和第二用户信息
|
data.dualAuthInfo = {
|
firstUserId: firstUser.userId,
|
firstUserName: firstUser.name,
|
secondUserId: secondUser.userId,
|
secondUserName: secondUser.name,
|
secondUserExtra: secondUser.data.extra
|
}
|
// 确保第二用户的信息包含身份证号
|
data.extra2 = secondUser.data.extra
|
// 将 data.userId 和 data.extra 设置为第一用户的信息
|
data.userId = firstUser.userId
|
// 尝试从数据库获取第一用户的完整信息
|
try {
|
let userRes = sqliteService.d1_person.findByUserId(firstUser.userId)
|
if (userRes && userRes.length > 0) {
|
// 获取第一用户的姓名、身份证号和身份类型
|
let idCard1
|
let firstUserType = 0
|
try {
|
idCard1 = JSON.parse(userRes[0].extra).idCard
|
firstUserType = JSON.parse(userRes[0].extra).type || 0
|
} catch (error) {
|
logger.error("无第一用户身份证号或类型")
|
}
|
data.extra = { name: userRes[0].name, idCard: idCard1, type: firstUserType }
|
} else {
|
data.extra = { name: firstUser.name, idCard: '', type: firstUser.type }
|
}
|
} catch (error) {
|
logger.error("解析第一用户信息失败")
|
data.extra = { name: firstUser.name, idCard: '', type: firstUser.type }
|
}
|
// 存储第二用户的人脸图片
|
if (secondUser.fileName) {
|
let secondUserSrc = `/data/passRecord/${secondUser.userId}_${data.timeStamp}_2.jpg`
|
std.ensurePathExists(secondUserSrc) // 确保目录存在
|
if (std.exist(secondUser.fileName)) {
|
// 移动图片到指定目录
|
dxos.systemBrief(`mv ${secondUser.fileName} ${secondUserSrc}`)
|
// 更新data中的code2为第二用户的图片路径
|
data.code2 = secondUserSrc
|
}
|
}
|
// 存储第一用户的人脸图片
|
if (firstUser.fileName) {
|
let firstUserSrc = `/data/passRecord/${firstUser.userId}_${data.timeStamp}_1.jpg`
|
std.ensurePathExists(firstUserSrc) // 确保目录存在
|
if (std.exist(firstUser.fileName)) {
|
// 移动图片到指定目录
|
dxos.systemBrief(`mv ${firstUser.fileName} ${firstUserSrc}`)
|
// 更新data中的code为第一用户的图片路径
|
data.code = firstUserSrc
|
data.firstUserFileName = firstUserSrc
|
}
|
}
|
// 双人通行:两个用户都有基本权限即可
|
logger.info("[access]: 双人认证通过")
|
ret = true
|
// 清除双人认证超时
|
clearAuthTimeout()
|
// 清空认证队列
|
clearAuthQueue()
|
}
|
} else {
|
logger.info("[access]: 无权限通行")
|
ret = false
|
}
|
} else {
|
// 双人通行:两个用户都有基本权限即可
|
logger.info("[access]: 双人认证通过")
|
ret = true
|
// 清除双人认证超时
|
clearAuthTimeout()
|
// 清空认证队列
|
clearAuthQueue()
|
}
|
|
if (ret) {
|
// 暂停人脸认证功能
|
driver.face.status(false)
|
logger.info("[access]: 暂停人脸认证功能")
|
}
|
} else {
|
logger.info("[access]: 无权限")
|
ret = false
|
data.message = "Permission verification failed"
|
}
|
}
|
|
if (!ret && config.get('mqtt.onlinecheck') == 1 && mqtt_map.get("MQTT_STATUS") == "connected") {
|
logger.info("[access]: 无权限,走在线验证")
|
let serialNo = std.genRandomStr(10)
|
driver.mqtt.send("access_device/v2/event/access_online", JSON.stringify(mqttService.mqttReply(serialNo, data, mqttService.CODE.S_000)))
|
driver.audio.play(`/app/code/resource/${language}/wav/recg.wav`)
|
|
// 等待在线验证结果
|
let payload = getOnlinecheck()
|
if (payload && payload.serialNo == serialNo && payload.code == '000000') {
|
ret = true
|
// 暂停人脸认证功能
|
driver.face.status(false)
|
logger.info("[access]: 暂停人脸认证功能")
|
} else {
|
logger.info("[access]: 在线验证失败")
|
ret = false
|
}
|
}
|
}
|
if (ret == true) {
|
if (data.type == 500) {
|
driver.finger.controlLed("GREEN")
|
} else if (data.type == 600) {
|
bleReply(data, true)
|
}
|
|
// 验证气体浓度
|
grainService.checkGasConcentration(function() {
|
// 从存储的气体数据中获取验证结果
|
const gasData = grainService.getGasData()
|
|
if(gasData && gasData.data && gasData.data.status === "0") {
|
logger.info("[access]: 气体浓度验证合格")
|
// 通行成功处理
|
driver.screen.accessSuccess()
|
logger.info("[access]: 通行成功")
|
|
// 显示通行成功结果
|
bus.fire("showAccessResult", {
|
faceAuth: true,
|
gasConcentration: true,
|
accessAllowed: true,
|
message: "*仓内气体浓度合格,允许通行*"
|
})
|
|
// 触发通行成功事件,通知UI更新
|
bus.fire("accessSuccess", { data: data, fileName: fileName })
|
driver.audio.play(`/app/code/resource/${language}/wav/access_s.wav`) // 播放语音
|
driver.gpio.open() // 开门
|
savePassPic(data, fileName) // 保存通行图片
|
reply(data, true) // 上报通行记录
|
|
// 60秒后重置用户UI
|
std.setTimeout(() => {
|
bus.fire("accessUnlockComplete")
|
}, 60000)
|
} else {
|
logger.info("[access]: 气体浓度验证不合格")
|
// 通行失败处理
|
driver.screen.accessFail()
|
logger.error("[access]: 通行失败")
|
// 触发失败弹窗
|
bus.fire("showAccessResult", {
|
faceAuth: true,
|
gasConcentration: false,
|
accessAllowed: false,
|
message: "*仓内气体浓度不合格,禁止通行*"
|
})
|
// 触发通行成功事件,更新用户UI
|
bus.fire("accessSuccess", { data: data, fileName: fileName })
|
if (utils.isEmpty(similarity)) {
|
driver.audio.play(`/app/code/resource/${language}/wav/access_f.wav`)
|
}
|
// 60秒后重置用户UI
|
std.setTimeout(() => {
|
bus.fire("accessUnlockComplete")
|
}, 60000)
|
savePassPic(data, fileName)
|
// 添加气体浓度失败信息
|
data.message = "气体浓度不合格"
|
reply(data, false) // 上报通行记录
|
}
|
})
|
|
} else {
|
if (data.type == 500) {
|
driver.finger.controlLed("RED")
|
} else if (data.type == 600) {
|
bleReply(data, false)
|
}
|
driver.screen.accessFail()
|
logger.error("[access]: 通行失败")
|
// 触发失败弹窗
|
bus.fire("showAccessResult", {
|
faceAuth: true,
|
gasConcentration: false,
|
accessAllowed: false,
|
message: "*权限认证失败,禁止通行*"
|
})
|
if (similarity !== false) {
|
driver.audio.play(`/app/code/resource/${language}/wav/access_f.wav`)
|
}
|
|
// 60秒后重置用户UI
|
std.setTimeout(() => {
|
bus.fire("accessUnlockComplete")
|
}, 60000)
|
|
if (isStranger && !config.get("sys.strangerImage")) {
|
// 陌生人不保存照片
|
} else {
|
savePassPic(data, fileName)
|
}
|
// 添加权限认证失败信息
|
data.message = "权限认证失败"
|
reply(data, false)
|
}
|
|
} catch (error) {
|
logger.error(error)
|
}
|
// 语音播报需要时间,所以延迟1秒解锁
|
std.sleep(1000)
|
lockMap.put("access_lock", false)
|
logger.error("[access]: 解锁成功")
|
// 触发通行解锁完成事件,通知UI重置
|
// bus.fire("accessUnlockComplete")
|
driver.finger.controlLed("BLUE")
|
}
|
|
// 保存通行图片
|
function savePassPic(data, fileName) {
|
// 处理单人认证的人脸图片
|
if (data.type == "300") { // 仅处理人脸类型
|
// 如果是双人认证,已经在处理双人认证信息时保存了图片,这里跳过
|
if (data.dualAuthInfo) {
|
return
|
}
|
let src = `/data/passRecord/${data.userId}_${data.timeStamp}.jpg`
|
std.ensurePathExists(src) // 确保目录存在
|
if (std.exist(fileName)) {
|
// 移动图片到指定目录
|
dxos.systemBrief(`mv ${fileName} ${src}`)
|
// 只清理原始临时图片文件(以时间戳命名的文件),保留调整后的图片文件
|
dxos.systemBrief(`rm -rf /data/user/temp/[0-9]*.jpg`)
|
// 更新data中的code为图片路径
|
data.code = src
|
} else {
|
logger.error("[access]: 通行失败,图片不存在!!!!!!!!!!!!!!!" + fileName)
|
}
|
}
|
// 处理双人认证的人脸图片
|
if (data.dualAuthInfo) {
|
// 保存第一用户的人脸图片
|
if (fileName) {
|
let src = `/data/passRecord/${data.userId}_${data.timeStamp}_1.jpg`
|
std.ensurePathExists(src) // 确保目录存在
|
if (std.exist(fileName)) {
|
// 移动图片到指定目录
|
dxos.systemBrief(`mv ${fileName} ${src}`)
|
// 只清理原始临时图片文件(以时间戳命名的文件),保留调整后的图片文件
|
dxos.systemBrief(`rm -rf /data/user/temp/[0-9]*.jpg`)
|
// 更新data中的code为图片路径
|
data.code = src
|
data.firstUserFileName = src
|
} else {
|
logger.error("[access]: 通行失败,图片不存在!!!!!!!!!!!!!!!" + fileName)
|
}
|
}
|
}
|
}
|
|
function getOnlinecheck() {
|
let timeout = config.get("mqtt.timeout")
|
timeout = utils.isEmpty(timeout) ? 2000 : timeout * 1000
|
return driver.sync.request("mqtt.getOnlinecheck", timeout)
|
}
|
|
/**
|
* 校验权限时间是否可以通行
|
* @param {array} permissions 权限记录数组
|
* @returns permissionId成功,false失败
|
*/
|
function judgmentPermission(permissions) {
|
let currentTime = Math.floor(Date.now() / 1000)
|
for (let permission of permissions) {
|
if (permission.timeType == 0) {
|
// 永久权限
|
return permission.permissionId
|
} else if (permission.beginTime <= currentTime && currentTime <= permission.endTime) {
|
if (permission.timeType == 1) {
|
// 时间段权限
|
return permission.permissionId
|
}
|
if (permission.timeType == 2 && permission.period) {
|
// 每日权限
|
let dayTimes = permission.period
|
if (dayTimes && dayTimes.split("|").some((dayTime) => isCurrentTimeInTimeRange(dayTime))) {
|
return permission.permissionId
|
}
|
}
|
if (permission.timeType == 3 && permission.period) {
|
// 周重复权限
|
let dayTimes = JSON.parse(permission.period)[new Date().getDay() == 0 ? 7 : new Date().getDay()]
|
if (dayTimes && dayTimes.split("|").some((dayTime) => isCurrentTimeInTimeRange(dayTime))) {
|
return permission.permissionId
|
}
|
}
|
}
|
}
|
return false
|
}
|
|
/**
|
* 判断当前时间是否在时间段内
|
* @param {string} time eg:15:00-19:00
|
* @returns
|
*/
|
function isCurrentTimeInTimeRange(time) {
|
// 分割开始时间和结束时间
|
let [startTime, endTime] = time.split('-');
|
// 解析开始时间的小时和分钟
|
let [startHour, startMinute] = startTime.split(':');
|
// 解析结束时间的小时和分钟
|
let [endHour, endMinute] = endTime.split(':');
|
|
// 获取当前时间
|
let currentTime = new Date();
|
|
// 创建开始时间的日期对象
|
let startDate = new Date();
|
startDate.setHours(parseInt(startHour, 10));
|
startDate.setMinutes(parseInt(startMinute, 10));
|
// 创建结束时间的日期对象
|
let endDate = new Date();
|
endDate.setHours(parseInt(endHour, 10));
|
endDate.setMinutes(parseInt(endMinute, 10));
|
|
// 检查当前时间是否在时间范围内
|
return currentTime >= startDate && currentTime < endDate;
|
}
|
|
// access通行上报
|
function reply(data, result) {
|
delete data.permissionIds
|
let record = {
|
id: std.genRandomStr(10),
|
keyId: data.keyId || "",
|
permissionId: data.permissionId || "",
|
userId: data.userId || "",
|
type: data.type || "",
|
code: data.code || "",
|
door: data.door || "",
|
timeStamp: data.timeStamp || Math.floor(Date.now() / 1000),
|
result: result ? 0 : 1,
|
extra: JSON.stringify(data.extra || {}),
|
message: data.message || "",
|
}
|
// 处理双人认证信息
|
if (data.dualAuthInfo && data.dualAuthInfo.secondUserId) {
|
record.userId2 = data.dualAuthInfo.secondUserId
|
// 保存第二用户的完整信息
|
if (data.dualAuthInfo.secondUserExtra) {
|
record.extra2 = JSON.stringify(data.dualAuthInfo.secondUserExtra)
|
} else if (data.extra2) {
|
record.extra2 = JSON.stringify(data.extra2)
|
} else {
|
record.extra2 = JSON.stringify({ name: data.dualAuthInfo.secondUserName })
|
}
|
if (data.code2) {
|
record.code2 = data.code2
|
}
|
}
|
// 存储通行记录,判断上限
|
let count = sqliteService.d1_pass_record.count()
|
let configNum = config.get("access.offlineAccessNum");
|
configNum = configNum ? configNum : 2000;
|
if (configNum > 0) {
|
if (count >= configNum) {
|
// 达到最大存储数量
|
// 删除最远的那条
|
let lastRecord = sqliteService.d1_pass_record.findAllOrderBytimeStampAsc({ page: 0, size: 1 })
|
if (lastRecord && lastRecord.length == 1) {
|
//判断下如果是人脸 去删除一下人脸照片
|
if (lastRecord[0].type == 300) {
|
dxos.systemBrief(`rm -rf ${lastRecord[0].code}`)
|
}
|
sqliteService.d1_pass_record.deleteByid(lastRecord[0].id)
|
}
|
}
|
sqliteService.d1_pass_record.save(record)
|
}
|
let accessRecord = {
|
userId: record.userId,
|
type: record.type,
|
result: record.result,
|
name: data.extra && data.extra.name ? data.extra.name : "",
|
timeStamp: record.timeStamp,
|
extra: {},
|
error: record.message
|
}
|
let serialNo = record.id
|
if (record.type == 300) {
|
if (config.get('sys.strangerImage') && config.get('access.uploadToCloud')) {
|
accessRecord.code = dxCommonUtils.fs.fileToBase64(record.code)
|
} else {
|
accessRecord.code = ""
|
}
|
}
|
let payload = mqttService.mqttReply(serialNo, [accessRecord], mqttService.CODE.S_000)
|
driver.mqtt.send("access_device/v2/event/access", JSON.stringify(payload))
|
}
|
|
// 蓝牙回复
|
function bleReply (data, result) {
|
let replyData = JSON.stringify({
|
serialNo: data.serialNo,
|
code: result ? "000000" : "100000",
|
time: Math.floor(Date.parse(new Date()) / 1000)
|
})
|
driver.uartBle.send("0101" + dxCommonUtils.codec.strToUtf8Hex(replyData))
|
}
|
|
|
export default accessService
|