1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
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