需求:做一個部門授權,可以把所選擇部門下面的所有人的人臉信息傳到設備組里(多個設備),問題在于圖片是通過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;}