需求:做一个部门授权,可以把所选择部门下面的所有人的人脸信息传到设备组里(多个设备),问题在于图片是通过Base64处理之后的,会导致文件名非常长,如果一次性传很多数据就会超过设备的最长请求长度,如果不用Base64处理的话让设备自己去minio下载就会导致特别慢,设备容易掉线,所以就用多线程发送。先看一下全部的代码,再看一下多线程的方法。
全部代码:
@Override public List<Long> createDeptAuthorize(DeptAuthorizeSaveReqVO createReqVO) { List<Long> ids = new ArrayList<>(); List<Long> userIds = new ArrayList<>(); //遍历部门表,查看该部门是否已有授权 List<Long> deptAuthorizeExits = new ArrayList<>(); List<Long> deptAuthorizeNotExits = new ArrayList<>(); //设备数组 List<BaseDeviceDo> baseDeviceDos = new ArrayList<>(); //把待更新授权的人员id存在这个数组里 List<Long> userUpdateIds = new ArrayList<>(); //把待更新授权的人员存在这个数组里 List<AdminUserRespDTO> userUpdateList = new ArrayList<>(); // 设置用户信息 List<User> userList = new ArrayList<>(); for (Long l : createReqVO.getDeptId()) { QueryWrapper<DeptAuthorizeDO> queryWrapperDept = new QueryWrapper<>(); queryWrapperDept.eq("dept_id", l); queryWrapperDept.eq("deleted", 0); DeptAuthorizeDO deptAuthorizeDO1 = deptAuthorizeMapper.selectOne(queryWrapperDept); if (deptAuthorizeDO1 != null) { //表里存在这条数据就代表已有部门授权,所以要修改部门授权 deptAuthorizeExits.add(deptAuthorizeDO1.getDeptId()); }else{ deptAuthorizeNotExits.add(l); } } //先遍历不存在的部门授权,即新增授权并发到设备上 for (Long l : deptAuthorizeNotExits) { // 插入 DeptAuthorizeDO deptAuthorize = new DeptAuthorizeDO(); deptAuthorize.setDeptId(l); deptAuthorize.setAuthorizeWay(createReqVO.getAuthorizeWay()); deptAuthorize.setDoorId(createReqVO.getDoorId()); deptAuthorize.setDoorGroupId(createReqVO.getDoorGroupId()); deptAuthorize.setEffectMethod(createReqVO.getEffectMethod()); deptAuthorize.setInPeriodId(createReqVO.getInPeriodId()); deptAuthorize.setOutPeriodId(createReqVO.getOutPeriodId()); deptAuthorizeMapper.insert(deptAuthorize); // 假设deptAuthorizeMapper.insert()返回插入记录的ID ids.add(deptAuthorize.getId()); //查找这个部门下面有多少人 List<AdminUserRespDTO> adminUserDOList = adminUserApi.getUserListByDeptId(l); for (AdminUserRespDTO adminUserDO : adminUserDOList) { //遍历人员数组看看是否在授权生效表里 QueryWrapper queryWrapper = new QueryWrapper(); queryWrapper.eq("user_id", adminUserDO.getId()); queryWrapper.eq("deleted", 0); AuthorizeEffectDO authorizeEffectDO = authorizeEffectMapper.selectOne(queryWrapper); if (authorizeEffectDO == null) { //如果不存在就插入这条数据 AuthorizeEffectDO authorizeEffectDO1 = new AuthorizeEffectDO(); authorizeEffectDO1.setUserId(adminUserDO.getId()); //0代表部门授权 authorizeEffectDO1.setAuthorizeEffectType(0); //0代表非临时授权 authorizeEffectDO1.setIsTemporary(0); authorizeEffectMapper.insert(authorizeEffectDO1); userIds.add(adminUserDO.getId()); //插入授权日志(生效表里没有这条数据则代表这人没有授权所以肯定会授权成功) AuthorizeLogDO authorizeLogDO = new AuthorizeLogDO(); //授权id authorizeLogDO.setAuthorizeId(deptAuthorize.getId()); //授权类型(0:部门 1:特殊 2:个人 3:临时) authorizeLogDO.setAuthorizeType(0); //用户id authorizeLogDO.setUserId(adminUserDO.getId()); //备注 authorizeLogDO.setRemark("创建部门授权成功"); //是否成功(0:成功 1:失败) authorizeLogDO.setIsSuccess(0); //插入 authorizeLogMapper.insert(authorizeLogDO); } else { //如果不存在这条数据则把生效设置为1 //临时>人>特殊>部门 if (authorizeEffectDO.getAuthorizeEffectType() != 1 && authorizeEffectDO.getAuthorizeEffectType() != 2) { //0代表部门授权 authorizeEffectDO.setAuthorizeEffectType(0); authorizeEffectMapper.updateById(authorizeEffectDO); userIds.add(adminUserDO.getId()); //插入授权日志(生效表里没有这条数据则代表这人没有授权所以肯定会授权成功) AuthorizeLogDO authorizeLogDO = new AuthorizeLogDO(); //授权id authorizeLogDO.setAuthorizeId(deptAuthorize.getId()); //授权类型(0:部门 1:特殊 2:个人 3:临时) authorizeLogDO.setAuthorizeType(0); //用户id authorizeLogDO.setUserId(adminUserDO.getId()); //备注 authorizeLogDO.setRemark("创建部门授权成功"); //是否成功(0:成功 1:失败) authorizeLogDO.setIsSuccess(0); //插入 authorizeLogMapper.insert(authorizeLogDO); } else { //记录在授权日志里 //插入授权日志(失败) AuthorizeLogDO authorizeLogDO = new AuthorizeLogDO(); //授权id authorizeLogDO.setAuthorizeId(deptAuthorize.getId()); //授权类型(0:部门 1:特殊 2:个人 3:临时) authorizeLogDO.setAuthorizeType(0); //用户id authorizeLogDO.setUserId(adminUserDO.getId()); //备注 authorizeLogDO.setRemark("创建部门授权失败,已有更高授权"); //是否成功(0:成功 1:失败) authorizeLogDO.setIsSuccess(1); //插入 authorizeLogMapper.insert(authorizeLogDO); } } } List<AdminUserRespDTO> adminUserDOS =adminUserApi.getUserListByDeptId(l); for (AdminUserRespDTO adminUserDO : adminUserDOS) { //更新这个部门下面人的权限 //用户列表 User user = new User(); AdminUserRespDTO user1 = adminUserApi.getUser(adminUserDO.getId()); user.setI(user1.getUserSn()); user.setN(user1.getNickname()); user.setU(user1.getUserSn()); user.setC(""); user.setB(""); user.setW(user1.getPassword()); user.setD(user1.getDeptName()); //出门规则 if (createReqVO.getAuthorizeWay()==0) { //门授权,判断这个门下的设备是进门还是出门 List<DoorDeviceRelateRespDTO> doorDeviceRelateList = doorDeviceRelateApi.getDoorDeviceRelateList(createReqVO.getDoorId()); for (DoorDeviceRelateRespDTO doorDeviceRelateRespDTO : doorDeviceRelateList) { if (doorDeviceRelateRespDTO != null && doorDeviceRelateRespDTO.getInOutDirection() == 0){ //存在并且为进门(0进门 1出门) // Long inPeriodId = accessPeriodMapper.selectById(createReqVO.getInPeriodId()).getParentRuleId(); user.setM(createReqVO.getInPeriodId().toString()); }else if (doorDeviceRelateRespDTO != null && doorDeviceRelateRespDTO.getInOutDirection() == 1){ //存在并且为出门(0进门 1出门) // AccessPeriodDO accessPeriodDO = accessPeriodMapper.selectById(createReqVO.getOutPeriodId()); //// Long outPeriodId = accessPeriodMapper.selectById(createReqVO.getOutPeriodId()).getParentRuleId(); user.setM(createReqVO.getOutPeriodId().toString()); } } }else if (createReqVO.getAuthorizeWay() == 1){ //获取这个门组下面的门 QueryWrapper queryWrapper = new QueryWrapper(); queryWrapper.eq("group_id",createReqVO.getDoorGroupId()); queryWrapper.eq("deleted",0); List<GroupDoorRelateDO> doorRelateDOS = doorRelateMapper.selectList(queryWrapper); for (GroupDoorRelateDO doorRelateDO : doorRelateDOS) { List<DoorDeviceRelateRespDTO> doorDeviceRelateList = doorDeviceRelateApi.getDoorDeviceRelateList(doorRelateDO.getDoorId()); for (DoorDeviceRelateRespDTO doorDeviceRelateRespDTO : doorDeviceRelateList) { if (doorDeviceRelateRespDTO != null && doorDeviceRelateRespDTO.getInOutDirection() == 0){ //存在并且为进门(0进门 1出门) user.setM(createReqVO.getOutPeriodId().toString()); }else if (doorDeviceRelateRespDTO != null && doorDeviceRelateRespDTO.getInOutDirection() == 1){ //存在并且为出门(0进门 1出门) user.setM(createReqVO.getOutPeriodId().toString()); } } } } //照片 FaceImageRespDTO faceImageDO = faceImageApi.getFaceImage(user1.getId()); if (faceImageDO != null) { if (faceImageDO.getImage() != null && !faceImageDO.getImage().isEmpty()) { // minio路径转换为文件路径 String objectName = faceImageDO.getImage().split("/")[faceImageDO.getImage().split("/").length - 1]; // 从 MinIO 下载文件并转换为字节数组 byte[] fileBytes = minioToBase64.downloadFileFromMinio(objectName); // 转换为 Base64 字符串 String base6String4 = minioToBase64.convertToBase64(fileBytes); user.setF(base6String4); } } //放到user列表里传给设备 userList.add(user); } } //遍历已有部门授权列表,即修改现有的部门授权 for (Long deptAuthorizeExit : deptAuthorizeExits) { QueryWrapper queryWrapperDeptAuthorize = new QueryWrapper(); queryWrapperDeptAuthorize.eq("dept_id",deptAuthorizeExit); queryWrapperDeptAuthorize.eq("deleted",0); //修改部门授权 DeptAuthorizeDO deptAuthorizeDO = deptAuthorizeMapper.selectOne(queryWrapperDeptAuthorize); //更新部门授权列表的基础性徐 deptAuthorizeDO.setAuthorizeWay(createReqVO.getAuthorizeWay()); deptAuthorizeDO.setDoorId(createReqVO.getDoorId()); deptAuthorizeDO.setDoorGroupId(createReqVO.getDoorGroupId()); deptAuthorizeDO.setEffectMethod(createReqVO.getEffectMethod()); deptAuthorizeDO.setInPeriodId(createReqVO.getInPeriodId()); deptAuthorizeDO.setOutPeriodId(createReqVO.getOutPeriodId()); deptAuthorizeDO.setUpdateTime(LocalDateTime.now()); deptAuthorizeMapper.updateById(deptAuthorizeDO); //查找这个部门下面有多少人 List<AdminUserRespDTO> adminUserDOList = adminUserApi.getUserListByDeptId(deptAuthorizeExit); for (AdminUserRespDTO adminUserDO : adminUserDOList) { //遍历人员数组看看是否在授权生效表里有高于部门授权的授权,如果有则不授权 QueryWrapper queryWrapper = new QueryWrapper(); queryWrapper.eq("user_id", adminUserDO.getId()); //目前生效类型为部门授权 queryWrapper.eq("authorize_effect_type",0); queryWrapper.eq("deleted",0); AuthorizeEffectDO authorizeEffectDO = authorizeEffectMapper.selectOne(queryWrapper); if (authorizeEffectDO != null) { //把符合规定的人插入到待更新的userId数组中 userUpdateIds.add(authorizeEffectDO.getUserId()); userUpdateList.add(adminUserApi.getUser(authorizeEffectDO.getUserId())); } } for (AdminUserRespDTO adminUserRespDTO : userUpdateList) { //更新这个部门下面人的权限 //用户列表 User user = new User(); AdminUserRespDTO user1 = adminUserApi.getUser(adminUserRespDTO.getId()); user.setI(user1.getUserSn()); user.setN(user1.getNickname()); user.setU(user1.getUserSn()); user.setC(""); user.setB(""); user.setW(user1.getPassword()); user.setD(user1.getDeptName()); //出门规则 if (createReqVO.getAuthorizeWay()==0) { //门授权,判断这个门下的设备是进门还是出门 List<DoorDeviceRelateRespDTO> doorDeviceRelateList = doorDeviceRelateApi.getDoorDeviceRelateList(createReqVO.getDoorId()); for (DoorDeviceRelateRespDTO doorDeviceRelateRespDTO : doorDeviceRelateList) { if (doorDeviceRelateRespDTO != null && doorDeviceRelateRespDTO.getInOutDirection() == 0){ //存在并且为进门(0进门 1出门) // Long inPeriodId = accessPeriodMapper.selectById(createReqVO.getInPeriodId()).getParentRuleId(); user.setM(createReqVO.getInPeriodId().toString()); }else if (doorDeviceRelateRespDTO != null && doorDeviceRelateRespDTO.getInOutDirection() == 1){ //存在并且为出门(0进门 1出门) // AccessPeriodDO accessPeriodDO = accessPeriodMapper.selectById(createReqVO.getOutPeriodId()); //// Long outPeriodId = accessPeriodMapper.selectById(createReqVO.getOutPeriodId()).getParentRuleId(); user.setM(createReqVO.getOutPeriodId().toString()); } } }else if (createReqVO.getAuthorizeWay() == 1){ //获取这个门组下面的门 QueryWrapper queryWrapper = new QueryWrapper(); queryWrapper.eq("group_id",createReqVO.getDoorGroupId()); queryWrapper.eq("deleted",0); List<GroupDoorRelateDO> doorRelateDOS = doorRelateMapper.selectList(queryWrapper); for (GroupDoorRelateDO doorRelateDO : doorRelateDOS) { List<DoorDeviceRelateRespDTO> doorDeviceRelateList = doorDeviceRelateApi.getDoorDeviceRelateList(doorRelateDO.getDoorId()); for (DoorDeviceRelateRespDTO doorDeviceRelateRespDTO : doorDeviceRelateList) { if (doorDeviceRelateRespDTO != null && doorDeviceRelateRespDTO.getInOutDirection() == 0){ //存在并且为进门(0进门 1出门) user.setM(createReqVO.getOutPeriodId().toString()); }else if (doorDeviceRelateRespDTO != null && doorDeviceRelateRespDTO.getInOutDirection() == 1){ //存在并且为出门(0进门 1出门) user.setM(createReqVO.getOutPeriodId().toString()); } } } } //照片 FaceImageRespDTO faceImageDO = faceImageApi.getFaceImage(user1.getId()); if (faceImageDO != null) { if (faceImageDO.getImage() != null && !faceImageDO.getImage().isEmpty()) { // minio路径转换为文件路径 String objectName = faceImageDO.getImage().split("/")[faceImageDO.getImage().split("/").length - 1]; // 从 MinIO 下载文件并转换为字节数组 byte[] fileBytes = minioToBase64.downloadFileFromMinio(objectName); // 转换为 Base64 字符串 String base6String4 = minioToBase64.convertToBase64(fileBytes); user.setF(base6String4); } } //放到user列表里传给设备 userList.add(user); } } //判断相关联的门找到要传输的设备 if (createReqVO.getAuthorizeWay() == 0){ //门授权 //获得这个门下面的所有设备 List<DoorDeviceRelateRespDTO> doorDeviceRelateList = doorDeviceRelateApi.getDoorDeviceRelateList(createReqVO.getDoorId()); for (DoorDeviceRelateRespDTO doorDeviceRelateRespDTO : doorDeviceRelateList) { OnsiteEquipDO onsiteEquipDO = onsiteEquipMapper.selectById(doorDeviceRelateRespDTO.getDeviceId()); //插入设备列表 BaseDeviceDo baseDeviceDo = new BaseDeviceDo(); baseDeviceDo.setDeviceType(onsiteEquipDO.getDeviceModel()); baseDeviceDo.setIpAddr(onsiteEquipDO.getDeviceIp()); baseDeviceDo.setPassword(onsiteEquipDO.getDevicePassword()); baseDeviceDos.add(baseDeviceDo); } }else if (createReqVO.getAuthorizeWay() == 1){ //门组授权 //获得门组下的所有设备 List<OnsiteEquipDO> onsiteEquipDOS = groupDoorRelateService.listByAccessGroupTableId(createReqVO.getDoorGroupId()); onsiteEquipDOS.stream().forEach(onsiteEquipDO -> { BaseDeviceDo baseDeviceDo = new BaseDeviceDo(); baseDeviceDo.setDeviceType(onsiteEquipDO.getDeviceModel()); baseDeviceDo.setIpAddr(onsiteEquipDO.getDeviceIp()); baseDeviceDo.setPassword(onsiteEquipDO.getDevicePassword()); baseDeviceDos.add(baseDeviceDo); }); } // 创建线程池 ExecutorService executorService = Executors.newFixedThreadPool(3); // 分批处理用户列表 int batchSize = 5; int totalTasks = (userList.size() + batchSize - 1) / batchSize; for (int i = 0; i < totalTasks; i++) { int start = i * batchSize; int end = Math.min(start + batchSize, userList.size()); List<User> usersBatch = userList.subList(start, end); executorService.submit(() -> { control.setDeviceUser(baseDeviceDos, usersBatch); }); } // 关闭线程池 executorService.shutdown(); try { // 等待所有任务完成 executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); } catch (InterruptedException e) { e.printStackTrace(); } return ids; }
多线程方法:
// 创建线程池 ExecutorService executorService = Executors.newFixedThreadPool(3); // 分批处理用户列表 int batchSize = 5; int totalTasks = (userList.size() + batchSize - 1) / batchSize; for (int i = 0; i < totalTasks; i++) { int start = i * batchSize; int end = Math.min(start + batchSize, userList.size()); List<User> usersBatch = userList.subList(start, end); executorService.submit(() -> { control.setDeviceUser(baseDeviceDos, usersBatch); }); } // 关闭线程池 executorService.shutdown(); try { // 等待所有任务完成 executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); } catch (InterruptedException e) { e.printStackTrace(); }
1. 创建线程池
ExecutorService executorService = Executors.newFixedThreadPool(3);
- 使用
Executors.newFixedThreadPool(3)创建了一个固定大小为 3 的线程池。 - 线程池的作用是管理线程的生命周期,避免频繁创建和销毁线程带来的性能开销。
- 在这个线程池中,最多可以同时运行 3 个线程。
每次运行三个线程可以解决掉请求头过长的问题。
2. 计算分批处理的批次数量
int batchSize = 5;
int totalTasks = (userList.size() + batchSize - 1) / batchSize;
batchSize定义了每一批次处理的用户数量,这里设置为 5。totalTasks计算总共需要处理的批次数量。通过公式(userList.size() + batchSize - 1) / batchSize,确保即使用户数量不能被batchSize整除,也能正确计算出需要的批次数量。
3. 分批处理用户列表
for (int i = 0; i < totalTasks; i++) {
int start = i * batchSize;
int end = Math.min(start + batchSize, userList.size());
List<User> usersBatch = userList.subList(start, end);
executorService.submit(() -> {
control.setDeviceUser(baseDeviceDos, usersBatch);
});
}
- 循环逻辑:
- 外层循环
for (int i = 0; i < totalTasks; i++)遍历所有批次。
- 计算每一批次的范围:
int start = i * batchSize;计算当前批次的起始索引。int end = Math.min(start + batchSize, userList.size());计算当前批次的结束索引,确保不会超出userList的范围。
- 提取当前批次的用户子列表:
List<User> usersBatch = userList.subList(start, end);使用subList方法从userList中提取当前批次的用户子列表。
- 提交任务到线程池:
executorService.submit(() -> { control.setDeviceUser(baseDeviceDos, usersBatch); });将任务提交到线程池中执行。- 每个任务调用
control.setDeviceUser(baseDeviceDos, usersBatch)方法,处理当前批次的用户。
4. 关闭线程池并等待所有任务完成
executorService.shutdown();
try {
executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
- 关闭线程池:
executorService.shutdown();调用shutdown()方法,表示不再接受新的任务,但会等待已经提交的任务完成。
- 等待所有任务完成:
executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);调用awaitTermination方法,等待线程池中的所有任务完成。- 这里使用
Long.MAX_VALUE和TimeUnit.NANOSECONDS,表示等待时间非常长,几乎等同于无限等待。
- 异常处理:
- 如果线程被中断,会抛出
InterruptedException,捕获并打印堆栈信息。
这样就可以实现多线程向设备发送数据了。
另:附上设备发送数据的方法,也是多线程,可以参考。
/** * 设置设备用户(正式员工、常驻) */ public List<ReturnMessage> setDeviceUser(List<BaseDeviceDo> baseDeviceDos, List<User> list) { List<ReturnMessage> failureMessages = new ArrayList<>(); ExecutorService executor = Executors.newFixedThreadPool(baseDeviceDos.size()); List<Runnable> tasks = new ArrayList<>(); for (BaseDeviceDo baseDeviceDo : baseDeviceDos) { tasks.add(() -> { ReturnMessage returnMessage = null; try { switch (baseDeviceDo.getDeviceType()) { case M7: returnMessage = m7Control.setDeviceUser(baseDeviceDo, list); } if (returnMessage != null && !returnMessage.getCode().equals("0")) { failureMessages.add(returnMessage); } } catch (Exception e) { e.printStackTrace(); // 将异常信息封装到 ReturnMessage 中,或者创建一个新的 ReturnMessage 来表示失败 returnMessage.setCode("50100"); returnMessage.setMsg("处理设备类型 " + baseDeviceDo.getDeviceType() + "设备IP为:" + baseDeviceDo.getIpAddr() + " 时发生异常: " + e.getMessage()); failureMessages.add(returnMessage); } }); } tasks.forEach(executor::submit); executor.shutdown(); while (!executor.isTerminated()) { // 等待所有线程完成 } return failureMessages; }