liusuyi
2025-12-30 d873828ae4b9d4fc6ad510513c6c3f0c33118fd8
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
package com.ard.device.camera.service.impl;
 
import com.ard.common.annotation.DataScope;
import com.ard.common.constant.CacheConstants;
import com.ard.common.constant.DeviceConstants;
import com.ard.common.core.domain.entity.SysUser;
import com.ard.common.core.redis.RedisCache;
import com.ard.common.exception.ServiceException;
import com.ard.common.utils.DateUtils;
import com.ard.common.utils.SecurityUtils;
import com.ard.common.utils.StringUtils;
import com.ard.common.utils.bean.BeanValidators;
import com.ard.common.utils.spring.SpringUtils;
import com.ard.common.utils.uuid.IdUtils;
import com.ard.device.camera.domain.ArdCamera;
import com.ard.device.camera.domain.CameraCmd;
import com.ard.device.camera.mapper.ArdCameraMapper;
import com.ard.device.camera.service.IArdCameraService;
import com.ard.device.channel.domain.ArdChannel;
import com.ard.device.channel.service.IArdChannelService;
import com.ard.device.component.sdk.common.GlobalVariable;
import com.ard.device.component.sdk.event.LoginEvent;
import com.ard.device.component.sdk.service.CameraSDKService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
 
import javax.annotation.Resource;
import javax.validation.Validator;
import java.util.*;
import java.util.stream.Collectors;
 
/**
 * 相机设备Service业务层处理
 *
 * @author 刘苏义
 * @date 2023-02-11
 */
@Service
@Slf4j
public class ArdCameraServiceImpl implements IArdCameraService, ApplicationRunner {
    @Resource
    private ArdCameraMapper ardCameraMapper;
 
    @Resource
    private CameraSDKService cameraSDKService;
 
    @Resource
    private RedisCache redisService;
 
    @Resource
    private IArdChannelService ardChannelService;
 
    @Resource
    protected Validator validator;
 
 
    @Override
    public void run(ApplicationArguments args) {
        initCamera();
    }
 
    public void initCamera() {
        //清空相机缓存
        redisService.deleteObject(redisService.keys(getCacheKey("*")));
        //重新加载相机到缓存
        ardCameraMapper.selectArdCameraList(new ArdCamera()).forEach(ardCamera -> {
            ardCameraMapper.resetCameraLoginId(ardCamera);
            redisService.setCacheObject(getCacheKey(ardCamera.getId()), ardCamera);
            cameraSDKService.login(ardCamera);
        });
    }
 
    /**
     * 设置cache key
     *
     * @param configKey 参数键
     * @return 缓存键key
     */
    private String getCacheKey(String configKey) {
        return CacheConstants.CAMERA_LIST_KEY + configKey;
    }
 
 
    /**
     * 查询相机设备
     *
     * @param id 相机设备主键
     * @return 相机设备
     */
    @Override
    public ArdCamera selectArdCameraById(String id) {
        return ardCameraMapper.selectArdCameraById(id);
    }
 
    /**
     * 查询相机设备
     *
     * @param name 相机设备名称
     * @return 相机设备
     */
    @Override
    public ArdCamera selectArdCameraByName(String name) {
        return ardCameraMapper.selectArdCameraByName(name);
    }
 
    /**
     * 查询相机设备列表
     *
     * @param ardCamera 相机设备
     * @return 相机设备
     */
    @Override
    @DataScope(deptAlias = "d", userAlias = "u")
    public List<ArdCamera> selectArdCameraList(ArdCamera ardCamera) {
        return ardCameraMapper.selectArdCameraList(ardCamera);
    }
 
    /**
     * 查询所有相机
     *
     * @param ardCamera 相机设备
     * @return 相机设备
     */
    @Override
    public List<ArdCamera> selectArdCameraListAll(ArdCamera ardCamera) {
        return ardCameraMapper.selectArdCameraList(ardCamera);
    }
 
    /**
     * 新增相机设备
     *
     * @param ardCamera 相机设备
     * @return 结果
     */
    @Override
    public int insertArdCamera(ArdCamera ardCamera) {
        ardCamera.setId(IdUtils.simpleUUID());
        ardCamera.setCreateBy(SecurityUtils.getUsername());
        ardCamera.setCreateTime(DateUtils.getNowDate());
        ardCamera.setUserId(SecurityUtils.getUserId());
        int res = ardCameraMapper.insertArdCamera(ardCamera);
        if (res > 0) {
            cameraSDKService.login(ardCamera);//sdk登录获取通道信息
        }
        return res;
    }
 
    /**
     * 修改相机设备
     *
     * @param ardCamera 相机设备
     * @return 结果
     */
    @Override
    public int updateArdCamera(ArdCamera ardCamera) {
        ardCamera.setUpdateTime(DateUtils.getNowDate());
        ardCamera.setUpdateBy(SecurityUtils.getUsername());
        int res = ardCameraMapper.updateArdCamera(ardCamera);
        if (res > 0) {
            cameraSDKService.logout(ardCamera);//sdk注销
            cameraSDKService.login(ardCamera);//sdk登录获取通道信息
        }
        return res;
    }
 
 
    /**
     * 批量删除相机设备
     *
     * @param ids 需要删除的相机设备主键
     * @return 结果
     */
    @Override
    public int deleteArdCameraByIds(String[] ids) {
        for (String id : ids) {
            ArdCamera camera = ardCameraMapper.selectArdCameraById(id);
            if (camera != null) {
                //redis中删除
                redisService.deleteObject(getCacheKey(id));
                //删除当前相机的所有通道
                ardChannelService.deleteArdChannelByDeviceId(id);
                //sdk注销
                cameraSDKService.logout(camera);
            }
        }
        return ardCameraMapper.deleteArdCameraByIds(ids);
    }
 
    /**
     * 删除相机设备信息
     *
     * @param id 相机设备主键
     * @return 结果
     */
    @Override
    public int deleteArdCameraById(String id) {
        int i = ardCameraMapper.deleteArdCameraById(id);
        if (i > 0) {
            //redis中删除
            redisService.deleteObject(getCacheKey(id));
            //删除当前相机的所有通道
            ardChannelService.deleteArdChannelByDeviceId(id);
            //sdk注销
            cameraSDKService.logout(ArdCamera.builder().id(id).build());
        }
        return i;
    }
 
    @Override
    public String importCamera(List<ArdCamera> ardCameraList, Boolean isUpdateSupport, String operatorName) {
        if (StringUtils.isNull(ardCameraList) || ardCameraList.isEmpty()) {
            throw new ServiceException("导入相机数据不能为空!");
        }
        int successNum = 0;
        int failureNum = 0;
        StringBuilder successMsg = new StringBuilder();
        StringBuilder failureMsg = new StringBuilder();
        for (ArdCamera camera : ardCameraList) {
            try {
                //获取当前登录用户id
                Long userId = SecurityUtils.getUserId();
                camera.setUserId(userId);
                // 验证是否存在这个用户
                ArdCamera u = ardCameraMapper.selectArdCameraById(camera.getId());
                if (StringUtils.isNull(u)) {
                    BeanValidators.validateWithException(validator, camera);
                    camera.setCreateBy(operatorName);
                    this.insertArdCamera(camera);
                    successNum++;
                    successMsg.append("<br/>" + successNum + "、相机ID " + camera.getId() + " 导入成功");
                } else if (isUpdateSupport) {
                    BeanValidators.validateWithException(validator, camera);
                    checkCameraDataScope(camera.getUserId());
                    camera.setUpdateBy(operatorName);
                    this.updateArdCamera(camera);
                    successNum++;
                    successMsg.append("<br/>" + successNum + "、相机ID " + camera.getId() + " 更新成功");
                } else {
                    failureNum++;
                    failureMsg.append("<br/>" + failureNum + "、相机ID " + camera.getId() + " 已存在");
                }
            } catch (Exception e) {
                failureNum++;
                String msg = "<br/>" + failureNum + "、相机ID " + camera.getId() + " 导入失败:";
                failureMsg.append(msg + e.getMessage());
                log.error(msg, e);
            }
        }
        if (failureNum > 0) {
            failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:");
            throw new ServiceException(failureMsg.toString());
        } else {
            successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:");
        }
        return successMsg.toString();
    }
 
    /**
     * 校验用户是否有数据权限
     *
     * @param userId 用户id
     */
    @Override
    public void checkCameraDataScope(Long userId) {
        if (!SysUser.isAdmin(SecurityUtils.getUserId())) {
            ArdCamera camera = new ArdCamera();
            camera.setUserId(userId);
            List<ArdCamera> cameras = SpringUtils.getAopProxy(this).selectArdCameraList(camera);
            if (StringUtils.isEmpty(cameras)) {
                throw new ServiceException("没有权限访问井数据!");
            }
        }
    }
 
    /**
     * 校验相机是否唯一
     *
     * @param camera 相机
     * @return 结果
     */
    @Override
    public String checkCameraIpAndPortUnique(ArdCamera camera) {
        String id = camera.getId();
        String ip = camera.getIp();
        Integer port = camera.getPort();
        ArdCamera info = ardCameraMapper.checkCameraIpAndPortUnique(ip, port);
        if (StringUtils.isNotNull(info) && !info.getId().equals(id)) {
            return DeviceConstants.NOT_UNIQUE;
        }
        return DeviceConstants.UNIQUE;
    }
 
    /**
     * 校验机场是否已关联相机
     *
     * @param camera 相机
     * @return 结果
     */
    @Override
    public ArdCamera checkAirportHasCamera(ArdCamera camera) {
        ArdCamera info = ardCameraMapper.checkAirportHasCamera(camera);
        if (StringUtils.isNotNull(info)&& !info.getId().equals(camera.getId())) {
            return info;
        }
        return null;
    }
 
    /**
     * 同步通道
     *
     * @param oldArrayList 旧通道列表(库中)
     * @param newArrayList 新通道列表(sdk)
     * @author 刘苏义
     * @date 2024/8/23 16:28
     */
    @Override
    public void asyncChannel(List<ArdChannel> oldArrayList, List<ArdChannel> newArrayList) {
        // 将列表转换为映射以提高性能
        Map<Integer, ArdChannel> oldMap = oldArrayList.stream()
                .collect(Collectors.toMap(ArdChannel::getChanNo, channel -> channel));
        Map<Integer, ArdChannel> newMap = newArrayList.stream()
                .collect(Collectors.toMap(ArdChannel::getChanNo, channel -> channel));
 
        // 需要更新的数据
        newArrayList.stream()
                .filter(channel -> {
                    ArdChannel oldChannel = oldMap.get(channel.getChanNo());
                    return oldChannel != null && (!Objects.equals(channel.getName(), oldChannel.getName()) || !Objects.equals(channel.getVideoCode(), oldChannel.getVideoCode()));
                })
                .forEach(channel -> {
                    ArdChannel oldChannel = oldMap.get(channel.getChanNo());
                    channel.setId(oldChannel.getId());
                    ardChannelService.updateArdChannel(channel);
                });
 
        // 需要删除的数据
        oldArrayList.stream()
                .filter(channel -> !newMap.containsKey(channel.getChanNo()))
                .forEach(channel -> {
                    ardChannelService.deleteArdChannelById(channel.getId());
 
                });
 
        // 需要新增的数据
        newArrayList.stream()
                .filter(channel -> !oldMap.containsKey(channel.getChanNo()))
                .forEach(channel -> {
                    if (ardChannelService.checkArdChannelUnique(channel)) {
                        ardChannelService.insertArdChannel(channel);
                    }
                });
    }
 
    /**
     * 监听登录事件
     *
     * @param loginEvent 登录事件
     * @author 刘苏义
     * @date 2024/8/23 15:42
     */
    @Async
    @EventListener(LoginEvent.class)
    public void LoginEventListener(LoginEvent loginEvent) {
        ArdCamera camera = loginEvent.getCamera();
        ardCameraMapper.updateArdCamera(camera);
        // SDK 登录成功后写入 Redis
        redisService.setCacheObject(getCacheKey(camera.getId()), camera);
    }
 
    /**
     * 同步设备通道与本地通道库信息
     *
     * @author 刘苏义
     * @date 2024/8/22 14:37
     */
    @Scheduled(fixedDelay = 5000)
    public void syncChannel() {
        try {
            log.debug("定时执行同步通道任务");
            List<ArdCamera> ardCameraList = this.selectArdCameraList(new ArdCamera());
            if (!ardCameraList.isEmpty()) {
                ardCameraList
                        .forEach(ardCamera -> {
                            //SDK获取实际通道
                            List<ArdChannel> ardChannelListSDK = cameraSDKService.getChannels(ardCamera);
                            //数据库获取通道
                            ArdChannel ardChannel = new ArdChannel();
                            ardChannel.setDeviceId(ardCamera.getId());
                            List<ArdChannel> ardChannelListDb = ardChannelService.selectArdChannelList(ardChannel);
                            //同步通道
                            asyncChannel(ardChannelListDb, ardChannelListSDK);
                        });
            }
        } catch (Exception ex) {
            log.error("定时执行同步通道任务异常:{}", ex.getMessage());
        }
    }
 
    /**
     * 定时执行登录失败相机重新登录
     *
     * @author 刘苏义
     * @date 2024/8/22 14:37
     */
    @Scheduled(fixedDelay = 10000)
    public void failedReconnect() {
        try {
            log.debug("定时执行失败相机重连任务");
            // 从数据库取全部设备
            List<ArdCamera> cameraList = selectArdCameraList(new ArdCamera());
            cameraList.stream()
                    // 只处理登录失败或离线的设备
                    .filter(camera -> camera.getLoginId() == null || camera.getLoginId() < 0 || "0".equals(camera.getState()))
                    .forEach(camera -> {
                        log.info("尝试重新登录设备 {}:{}", camera.getIp(), camera.getPort());
                        cameraSDKService.login(camera);
                    });
 
        } catch (Exception ex) {
            log.error("定时执行失败相机重连任务异常:{}", ex.getMessage());
        }
    }
 
    /**
     * 同步相机在线状态
     * 刘苏义
     * 2023/10/8 14:54:41
     */
    @Scheduled(fixedDelay = 5000)
    public void cameraState() {
        try {
            log.debug("定时执行同步相机在线状态任务");
            //调用查询方法
            List<ArdCamera> ardCameraList = selectArdCameraList(new ArdCamera());
            ardCameraList
                    .forEach(camera -> {
                        boolean onLineNew = cameraSDKService.isOnLine(camera);
                        boolean onLineOld = "1".equals(camera.getState());
                        if (onLineNew != onLineOld) {
                            if (onLineNew) {
                                camera.setState("1");
                            } else {
                                camera.setState("0");
                            }
                            camera.setUpdateTime(DateUtils.getNowDate());
                            ardCameraMapper.updateArdCamera(camera);
                        }
                    });
        } catch (Exception ex) {
            log.error("同步相机在线状态异常:{}", ex.getMessage());
        }
    }
}