需求:做一个部门授权,可以把所选择部门下面的所有人的人脸信息传到设备组里(多个设备),问题在于图片是通过Base64处理之后的,会导致文件名非常长,如果一次性传很多数据就会超过设备的最长请求长度,如果不用Base64处理的话让设备自己去minio下载就会导致特别慢,设备容易掉线,所以就用多线程发送。先看一下全部的代码,再看一下多线程的方法。
全部代码:
@Overridepublic 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()返回插入记录的IDids.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();//授权idauthorizeLogDO.setAuthorizeId(deptAuthorize.getId());//授权类型(0:部门 1:特殊 2:个人 3:临时)authorizeLogDO.setAuthorizeType(0);//用户idauthorizeLogDO.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();//授权idauthorizeLogDO.setAuthorizeId(deptAuthorize.getId());//授权类型(0:部门 1:特殊 2:个人 3:临时)authorizeLogDO.setAuthorizeType(0);//用户idauthorizeLogDO.setUserId(adminUserDO.getId());//备注authorizeLogDO.setRemark("创建部门授权成功");//是否成功(0:成功 1:失败)authorizeLogDO.setIsSuccess(0);//插入authorizeLogMapper.insert(authorizeLogDO);} else {//记录在授权日志里//插入授权日志(失败)AuthorizeLogDO authorizeLogDO = new AuthorizeLogDO();//授权idauthorizeLogDO.setAuthorizeId(deptAuthorize.getId());//授权类型(0:部门 1:特殊 2:个人 3:临时)authorizeLogDO.setAuthorizeType(0);//用户idauthorizeLogDO.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;}