From 89c89cbfcd43a45ad4f5412782eee78f5794e9ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BD=97=E6=88=90?= <2847920761@qq.com> Date: Sun, 14 Sep 2025 16:52:43 +0800 Subject: [PATCH] =?UTF-8?q?=E6=95=B0=E6=8D=AE=E8=BD=AC=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ruoyi-modules/ruoyi-system/pom.xml | 5 + .../EnterpriseBigScreenController.java | 21 +- .../ProjectBigScreenController.java | 128 ++++++---- .../dromara/project/domain/BusAttendance.java | 2 +- .../impl/BusProjectTeamServiceImpl.java | 1 + .../service/impl/SysOssServiceImpl.java | 14 +- .../controller/TransferDataController.java | 126 +++++----- .../transferData/domain/OldAttendance.java | 2 + .../mapper/TransferDataMapper.java | 21 +- .../service/TransferAsyncConfig.java | 29 +++ .../service/TransferDataService.java | 229 ++++++++++++++++++ 11 files changed, 466 insertions(+), 112 deletions(-) create mode 100644 xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/transferData/service/TransferAsyncConfig.java create mode 100644 xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/transferData/service/TransferDataService.java diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/pom.xml b/xinnengyuan/ruoyi-modules/ruoyi-system/pom.xml index 28edad17..5f707192 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/pom.xml +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/pom.xml @@ -18,6 +18,11 @@ + + + + + diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/bigscreen/controller/EnterpriseBigScreenController.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/bigscreen/controller/EnterpriseBigScreenController.java index 4ff7e489..9b3d1753 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/bigscreen/controller/EnterpriseBigScreenController.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/bigscreen/controller/EnterpriseBigScreenController.java @@ -20,11 +20,16 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import java.io.File; +import java.io.IOException; import java.math.BigDecimal; import java.math.RoundingMode; +import java.text.DecimalFormat; import java.time.LocalDate; import java.time.temporal.ChronoUnit; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * 企业级大屏 @@ -133,13 +138,17 @@ public class EnterpriseBigScreenController { .map(BusUserProjectRelevancy::getUserId) .distinct().count()); - peopleCountVo.setManagersCount(list.stream().filter(item -> "2".equals(item.getUserType())) - .map(BusUserProjectRelevancy::getUserId) - .distinct().count()); +// peopleCountVo.setManagersCount(list.stream().filter(item -> "2".equals(item.getUserType())) +// .map(BusUserProjectRelevancy::getUserId) +// .distinct().count()); + peopleCountVo.setManagersCount(69L); + +// peopleCountVo.setSubcontractorsCount(list.stream().filter(item -> "3".equals(item.getUserType())) +// .map(BusUserProjectRelevancy::getUserId) +// .distinct().count()); + + peopleCountVo.setManagersCount(9L); - peopleCountVo.setSubcontractorsCount(list.stream().filter(item -> "3".equals(item.getUserType())) - .map(BusUserProjectRelevancy::getUserId) - .distinct().count()); Integer projectUserCount = projectBigScreenMapper.getUserCount(); peopleCountVo.setConstructionPersonnelCount(Long.valueOf(projectUserCount)); return R.ok(peopleCountVo); diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/bigscreen/controller/ProjectBigScreenController.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/bigscreen/controller/ProjectBigScreenController.java index 17f039d0..f6f7a632 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/bigscreen/controller/ProjectBigScreenController.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/bigscreen/controller/ProjectBigScreenController.java @@ -358,41 +358,104 @@ public class ProjectBigScreenController { List> sxtChildrenMap = new ArrayList<>(); HashMap map1 = new HashMap<>(); map1.put("id", "55"); - map1.put("label", "1222222"); + map1.put("label", "那荷4号方阵-1"); map1.put("name", "22"); map1.put("type", "camera"); - map1.put("lng", 106.48349615411811); - map1.put("lat", 29.54856374364732); + map1.put("lng", 107.111325); + map1.put("lat", 23.820919); map1.put("alt", 0); HashMap map2 = new HashMap<>(); map2.put("id", "56"); - map2.put("label", "1222223"); + map2.put("label", "甫必 1号方阵"); map2.put("name", "23"); map2.put("type", "camera"); - map2.put("lng", 106.48442273257676); - map2.put("lat", 29.53841670498476); + map2.put("lng", 107.091297); + map2.put("lat", 23.813567); map2.put("alt", 0); HashMap map3 = new HashMap<>(); map3.put("id", "57"); map3.put("label", "1222224"); map3.put("name", "24"); map3.put("type", "camera"); - map3.put("lng", 106.49197896482423); - map3.put("lat", 29.52931974282576); + map3.put("lng", 107.085442); + map3.put("lat", 23.811958); map3.put("alt", 0); HashMap map4 = new HashMap<>(); map4.put("id", "58"); - map4.put("label", "1222225"); + map4.put("label", "甫必2号方阵-1"); map4.put("name", "25"); map4.put("type", "camera"); - map4.put("lng", 106.50293584930655); - map4.put("lat", 29.533025743929034); + map4.put("lng", 107.085181); + map4.put("lat", 23.810556); map4.put("alt", 0); + HashMap map5 = new HashMap<>(); + map5.put("id", "58"); + map5.put("label", "甫必 4号方阵"); + map5.put("name", "25"); + map5.put("type", "camera"); + map5.put("lng", 107.081747); + map5.put("lat", 23.808131); + map5.put("alt", 0); + HashMap map6 = new HashMap<>(); + map6.put("id", "58"); + map6.put("label", "甫必 7号方阵-1"); + map6.put("name", "25"); + map6.put("type", "camera"); + map6.put("lng", 107.077922); + map6.put("lat", 23.798344); + map6.put("alt", 0); + HashMap map7 = new HashMap<>(); + map7.put("id", "58"); + map7.put("label", "68甫必6"); + map7.put("name", "25"); + map7.put("type", "camera"); + map7.put("lng", 107.077333); + map7.put("lat", 23.797969); + map7.put("alt", 0); + HashMap map8 = new HashMap<>(); + map8.put("id", "58"); + map8.put("label", "甫必5号方阵"); + map8.put("name", "25"); + map8.put("type", "camera"); + map8.put("lng", 107.075853); + map8.put("lat", 23.796711); + map8.put("alt", 0); + HashMap map9 = new HashMap<>(); + map9.put("id", "58"); + map9.put("label", "西牛2号方阵"); + map9.put("name", "25"); + map9.put("type", "camera"); + map9.put("lng", 107.078942); + map9.put("lat", 23.789306); + map9.put("alt", 0); + HashMap map10 = new HashMap<>(); + map10.put("id", "58"); + map10.put("label", "福绿1号方阵"); + map10.put("name", "25"); + map10.put("type", "camera"); + map10.put("lng", 107.090061); + map10.put("lat", 23.790411); + map10.put("alt", 0); + HashMap map11 = new HashMap<>(); + map11.put("id", "58"); + map11.put("label", "福绿6号方阵-2"); + map11.put("name", "25"); + map11.put("type", "camera"); + map11.put("lng", 107.112883); + map11.put("lat", 23.771378); + map11.put("alt", 0); sxtChildrenMap.add(map1); sxtChildrenMap.add(map2); sxtChildrenMap.add(map3); sxtChildrenMap.add(map4); + sxtChildrenMap.add(map5); + sxtChildrenMap.add(map6); + sxtChildrenMap.add(map7); + sxtChildrenMap.add(map8); + sxtChildrenMap.add(map9); + sxtChildrenMap.add(map10); + sxtChildrenMap.add(map11); return sxtChildrenMap; } @@ -401,41 +464,22 @@ public class ProjectBigScreenController { List> sxtChildrenMap = new ArrayList<>(); HashMap map1 = new HashMap<>(); map1.put("id", "65"); - map1.put("label", "6222222"); + map1.put("label", "田东无人机"); map1.put("name", "32"); map1.put("type", "drone"); - map1.put("lng", 106.49556855602525); - map1.put("lat", 29.534393226355515); + map1.put("lng", 107.12744694624267); + map1.put("lat", 23.615965741917278); map1.put("alt", 0); - HashMap map2 = new HashMap<>(); - map2.put("id", "66"); - map2.put("label", "2222223"); - map2.put("name", "33"); - map2.put("type", "drone"); - map2.put("lng", 106.49142431645038); - map2.put("lat", 29.534472802500083); - map2.put("alt", 0); - HashMap map3 = new HashMap<>(); - map3.put("id", "67"); - map3.put("label", "2222224"); - map3.put("name", "34"); - map3.put("type", "drone"); - map3.put("lng", 106.49142125177437); - map3.put("lat", 29.541881138875755); - map3.put("alt", 0); - HashMap map4 = new HashMap<>(); - map4.put("id", "68"); - map4.put("label", "2222225"); - map4.put("name", "35"); - map4.put("type", "drone"); - map4.put("lng", 106.50256649933792); - map4.put("lat", 29.54260793685717); - map4.put("alt", 0); - +// HashMap map2 = new HashMap<>(); +// map2.put("id", "66"); +// map2.put("label", "长顺无人机"); +// map2.put("name", "33"); +// map2.put("type", "drone"); +// map2.put("lng", 106.49142431645038); +// map2.put("lat", 29.534472802500083); +// map2.put("alt", 0); sxtChildrenMap.add(map1); - sxtChildrenMap.add(map2); - sxtChildrenMap.add(map3); - sxtChildrenMap.add(map4); +// sxtChildrenMap.add(map2); return sxtChildrenMap; } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/BusAttendance.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/BusAttendance.java index a07a65c7..753e49f2 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/BusAttendance.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/BusAttendance.java @@ -21,7 +21,7 @@ import java.io.Serial; */ @Data @EqualsAndHashCode(callSuper = true) -@TableName("bus_attendance") +@TableName("bus_attendance_copy1") public class BusAttendance extends BaseEntity { @Serial diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusProjectTeamServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusProjectTeamServiceImpl.java index b41e0a94..46dbd7ac 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusProjectTeamServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusProjectTeamServiceImpl.java @@ -188,6 +188,7 @@ public class BusProjectTeamServiceImpl extends ServiceImpl queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(BusProjectTeam::getProjectId, entity.getProjectId()); queryWrapper.eq(BusProjectTeam::getTeamName, teamName); if (entity.getId() != null) { queryWrapper.ne(BusProjectTeam::getId, entity.getId()); diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java index 669f3ce6..e8f9175a 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java @@ -24,6 +24,7 @@ import org.dromara.common.oss.core.OssClient; import org.dromara.common.oss.entity.UploadResult; import org.dromara.common.oss.enumd.AccessPolicyType; import org.dromara.common.oss.factory.OssFactory; +import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.system.domain.SysOss; import org.dromara.system.domain.bo.SysOssBo; import org.dromara.system.domain.vo.SysOssUploadVo; @@ -44,10 +45,8 @@ import java.net.URI; import java.net.URL; import java.net.URLConnection; import java.time.Duration; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; +import java.time.LocalDateTime; +import java.util.*; /** * 文件上传 服务层实现 @@ -400,6 +399,13 @@ public class SysOssServiceImpl implements ISysOssService, OssService { oss.setFileName(uploadResult.getFilename()); oss.setOriginalName(originalfileName); oss.setService(configKey); + + //罗成负责删掉 + oss.setCreateBy(1L); + oss.setUpdateBy(1L); + oss.setCreateTime(new Date()); + oss.setUpdateTime(new Date()); + baseMapper.insert(oss); SysOssVo sysOssVo = MapstructUtils.convert(oss, SysOssVo.class); return this.matchingUrl(sysOssVo); diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/transferData/controller/TransferDataController.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/transferData/controller/TransferDataController.java index 652502d9..8ef71ba8 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/transferData/controller/TransferDataController.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/transferData/controller/TransferDataController.java @@ -1,10 +1,15 @@ package org.dromara.transferData.controller; +import cn.dev33.satoken.annotation.SaCheckPermission; import cn.hutool.core.collection.CollectionUtil; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import jakarta.activation.MimetypesFileTypeMap; import jakarta.annotation.Resource; +import jakarta.validation.constraints.NotNull; import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.domain.R; +import org.dromara.contractor.domain.vo.constructionuser.SubConstructionUserVo; +import org.dromara.project.domain.BusAttendanceRule; import org.dromara.project.domain.vo.BusAttendanceRuleVo; import org.dromara.transferData.domain.ConstructionUserCopy; import org.dromara.transferData.domain.OldAttendance; @@ -15,6 +20,9 @@ import org.dromara.project.service.IBusAttendanceRuleService; import org.dromara.project.service.IBusAttendanceService; import org.dromara.system.domain.vo.SysOssVo; import org.dromara.system.service.ISysOssService; +import org.dromara.transferData.service.TransferDataService; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -27,9 +35,15 @@ import java.nio.file.Paths; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; +import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.Map; +import java.util.concurrent.ScheduledExecutorService; +import java.util.stream.Collectors; @RestController @RequestMapping("/transferData") @@ -39,13 +53,10 @@ public class TransferDataController { @Resource private TransferDataMapper transferDataMapper; @Resource - private IBusAttendanceService attendanceService; - @Resource private IBusAttendanceRuleService attendanceRuleService; @Resource - private ISubConstructionUserService constructionUserService; - @Resource - private ISysOssService ossService; + private TransferDataService transferDataService; + // 两个候选基础URL private static final String[] BASE_URLS = { @@ -56,7 +67,14 @@ public class TransferDataController { @RequestMapping("/transferAttendance") public void transferAttendance() { + List arrs = new ArrayList<>(); + List data = transferDataMapper.getData(); + + List list = attendanceRuleService.list(Wrappers.lambdaQuery() + .in(BusAttendanceRule::getProjectId, Arrays.asList(1897160897167638529L, 1897161054676336641L))); + Map rules = list.stream().collect(Collectors.toMap(BusAttendanceRule::getProjectId, vo -> vo)); + for (OldAttendance oldAttendance : data) { ConstructionUserCopy constructionUserCopy = transferDataMapper.getConstructionUserCopy(oldAttendance.getOpenid()); @@ -64,27 +82,37 @@ public class TransferDataController { continue; } LocalDate clockDate = LocalDate.parse(oldAttendance.getPrintingDate()); - //判定是否重读 - List list = attendanceService.list(Wrappers.lambdaQuery(BusAttendance.class) - .eq(BusAttendance::getUserId, constructionUserCopy.getSysUserId()) - .eq(BusAttendance::getProjectId, constructionUserCopy.getProjectId()) - .eq(BusAttendance::getClockDate, clockDate) - .eq(BusAttendance::getClockType, oldAttendance.getCommuter()) - ); - if(CollectionUtil.isNotEmpty(list)){ - continue; +// //判定是否重读 +// List list = attendanceService.list(Wrappers.lambdaQuery(BusAttendance.class) +// .eq(BusAttendance::getUserId, constructionUserCopy.getSysUserId()) +// .eq(BusAttendance::getProjectId, constructionUserCopy.getProjectId()) +// .eq(BusAttendance::getClockDate, clockDate) +// .eq(BusAttendance::getClockType, oldAttendance.getCommuter()) +// ); +// if(CollectionUtil.isNotEmpty(list)){ +// continue; +// } + if (oldAttendance.getProjectId() == 60){ + oldAttendance.setProjectId(1897160897167638529L); + } + if (oldAttendance.getProjectId() == 59){ + oldAttendance.setProjectId(1897161054676336641L); } BusAttendance busAttendance = new BusAttendance(); - if(oldAttendance.getPacePhoto()!=null){ - Long l = handleFaceImage(oldAttendance.getPacePhoto()); - busAttendance.setFacePic(l==null?"":l.toString()); - } + //处理照片 + + busAttendance.setFacePic(oldAttendance.getPacePhoto()); + +// if(oldAttendance.getPacePhoto()!=null){ +// Long l = handleFaceImage(oldAttendance.getPacePhoto()); +// busAttendance.setFacePic(l==null?"":l.toString()); +// } busAttendance.setUserName(constructionUserCopy.getUserName()); busAttendance.setUserId(constructionUserCopy.getSysUserId()); - busAttendance.setProjectId(constructionUserCopy.getProjectId()); +// busAttendance.setProjectId(constructionUserCopy.getProjectId()); // 转换日期字段 busAttendance.setClockDate(clockDate); @@ -102,7 +130,7 @@ public class TransferDataController { busAttendance.setClockTime(parseClockOn(oldAttendance.getClockOn(), busAttendance)); } //规则和迟到早退时间计算 - BusAttendanceRuleVo busAttendanceRuleVo = attendanceRuleService.queryByProjectId(busAttendance.getProjectId()); + BusAttendanceRule busAttendanceRuleVo = rules.get(busAttendance.getProjectId()); if (busAttendanceRuleVo != null) { LocalTime clockInTime = busAttendanceRuleVo.getClockInTime(); LocalTime clockOutTime = busAttendanceRuleVo.getClockOutTime(); @@ -125,53 +153,35 @@ public class TransferDataController { } busAttendance.setHandle("5".equals(oldAttendance.getIsPinch())?"1":"0"); } - - } - - - } - - - public Long handleFaceImage(String relativePath) { - String normalizedPath = relativePath.replace("\\", "/"); - String filename = Paths.get(normalizedPath).getFileName().toString(); - - // 使用 MimetypesFileTypeMap 解析 - MimetypesFileTypeMap fileTypeMap = new MimetypesFileTypeMap(); - String contentType = fileTypeMap.getContentType(filename); - - for (String baseUrl : BASE_URLS) { - String fullUrl = baseUrl + normalizedPath; - try { - HttpClient client = HttpClient.newHttpClient(); - HttpRequest request = HttpRequest.newBuilder() - .uri(URI.create(fullUrl)) - .GET() - .build(); - - HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofInputStream()); - - if (response.statusCode() == 200) { - long contentLength = response.headers().firstValueAsLong("Content-Length").orElse(-1); - SysOssVo ossVo = ossService.upload(response.body(), filename, contentType, contentLength); - return ossVo.getOssId(); - } - } catch (Exception e) { - log.warn("尝试URL失败: {}", fullUrl, e); - continue; + arrs.add(busAttendance); + if(arrs.size() >= 1000){ + List batchList = new ArrayList<>(arrs); + // 提交异步任务:处理照片 + 批量保存 + transferDataService.handlePhotoAndSaveBatch(batchList); + arrs.clear(); } } - return null; + if (CollectionUtil.isNotEmpty(arrs)) { + List batchList = new ArrayList<>(arrs); + transferDataService.handlePhotoAndSaveBatch(batchList); + } +// attendanceService.saveBatch(arrs); + +// transferDataMapper.saveBatchCopy(arrs); } + + private LocalDateTime parseClockOn(String clockOn, BusAttendance busAttendance) { if (clockOn == null || "缺卡".equals(clockOn)) { return null; } - + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("yyyy-MM-dd H:mm:ss"); try { + // 1. 尝试完整日期时间解析(如 "2023-10-14 07:35:07") - return LocalDateTime.parse(clockOn); + return LocalDateTime.parse(clockOn,formatter); } catch (DateTimeParseException e) { try { // 2. 仅时间解析(如 "5:38:00")并结合已有的日期 @@ -179,7 +189,7 @@ public class TransferDataController { if (date == null) { return null; // 日期不存在时返回 null } - LocalTime time = LocalTime.parse(clockOn); + LocalTime time = LocalTime.parse(clockOn,formatter1); return LocalDateTime.of(date, time); } catch (DateTimeParseException ex) { log.warn("无法解析打卡时间: {}", clockOn); diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/transferData/domain/OldAttendance.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/transferData/domain/OldAttendance.java index 06a0d96f..1396aca5 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/transferData/domain/OldAttendance.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/transferData/domain/OldAttendance.java @@ -1,5 +1,7 @@ package org.dromara.transferData.domain; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; import lombok.Data; import java.math.BigDecimal; import java.time.LocalDateTime; diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/transferData/mapper/TransferDataMapper.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/transferData/mapper/TransferDataMapper.java index 3c14f34b..18b1e373 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/transferData/mapper/TransferDataMapper.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/transferData/mapper/TransferDataMapper.java @@ -1,9 +1,11 @@ package org.dromara.transferData.mapper; import com.baomidou.dynamic.datasource.annotation.DS; +import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; +import org.dromara.project.domain.BusAttendance; import org.dromara.transferData.domain.ConstructionUserCopy; import org.dromara.transferData.domain.OldAttendance; @@ -15,10 +17,27 @@ public interface TransferDataMapper { @DS("slave") - @Select("select * from bus_attendance") + @Select("select * from bus_attendance where project_id in (59,60)") List getData(); @Select("select id,sys_user_id,project_id,user_name from sub_construction_user_copy1 where go_openid = #{openId}") ConstructionUserCopy getConstructionUserCopy(@Param("openId") String openId); + + + @Insert("") + int saveBatchCopy(@Param("list") List busAttendanceList); } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/transferData/service/TransferAsyncConfig.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/transferData/service/TransferAsyncConfig.java new file mode 100644 index 00000000..f26ab71b --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/transferData/service/TransferAsyncConfig.java @@ -0,0 +1,29 @@ +package org.dromara.transferData.service; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import java.util.concurrent.Executor; +import java.util.concurrent.ThreadPoolExecutor; + +@Configuration +public class TransferAsyncConfig { + + /** + * 考勤异步任务专用线程池 + */ + @Bean("attendanceAsyncPool") + public Executor attendanceAsyncPool() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + int corePoolSize = Runtime.getRuntime().availableProcessors() * 2; // 核心线程数:CPU核心数*2 + executor.setCorePoolSize(corePoolSize); + executor.setMaxPoolSize(corePoolSize * 2); // 最大线程数 + executor.setQueueCapacity(1000); // 任务队列容量 + executor.setKeepAliveSeconds(60); // 空闲线程存活时间 + executor.setThreadNamePrefix("attendance-async-"); // 线程名称前缀(便于排查) + // 拒绝策略:任务满时,由提交任务的线程(如主线程)执行,避免任务丢失 + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + executor.initialize(); + return executor; + } +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/transferData/service/TransferDataService.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/transferData/service/TransferDataService.java new file mode 100644 index 00000000..8fa666f7 --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/transferData/service/TransferDataService.java @@ -0,0 +1,229 @@ +package org.dromara.transferData.service; + +import jakarta.activation.MimetypesFileTypeMap; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.SerializationUtils; +import org.dromara.common.core.service.OssService; +import org.dromara.contractor.service.ISubConstructionUserService; +import org.dromara.project.domain.BusAttendance; +import org.dromara.project.service.IBusAttendanceRuleService; +import org.dromara.project.service.IBusAttendanceService; +import org.dromara.system.domain.vo.SysOssVo; +import org.dromara.system.service.ISysOssService; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import java.io.InputStream; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.file.Paths; +import java.time.Duration; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.stream.Collectors; + +/** + * @Author 铁憨憨 + * @Date 2025/9/14 10:53 + * @Version 1.0 + */ +@Service +@Slf4j +public class TransferDataService { + // 1. 全局复用HttpClient(设置超时,线程安全) + private final HttpClient httpClient = HttpClient.newBuilder() + .connectTimeout(Duration.ofSeconds(5)) // 连接超时5秒 + .followRedirects(HttpClient.Redirect.NORMAL) // 跟随重定向 + .build(); + + // 2. 注入依赖(原依赖不变) + @Resource + private IBusAttendanceService attendanceService; + @Resource + private ISysOssService ossService; + @Resource + @Qualifier("attendanceAsyncPool") // 对应之前 AsyncConfig 中定义的线程池Bean名 + private Executor attendanceAsyncExecutor; + + + // 两个候选基础URL + private static final String[] BASE_URLS = { + "http://xny.yj-3d.com:7464", + "http://xny.yj-3d.com:7363" + }; + /** + * 异步处理照片并批量保存(修复线程安全 + 性能优化) + * @param batchList 原始批量数据(浅拷贝) + */ + // 2. 修复后的照片批量异步处理逻辑 + public CompletableFuture handlePhotoAndSaveBatch(List batchList) { + log.info("异步处理照片和保存开始,条数:{}", batchList.size()); + + try { + // 步骤1:深度拷贝集合(不变,确保线程安全) + List deepCopyList = batchList.stream() + .map(attendance -> { + // 深拷贝实现(二选一,根据实体类是否实现Serializable) + // 方式1:Spring SerializationUtils(需实体类实现Serializable) + BusAttendance copy = SerializationUtils.deserialize(SerializationUtils.serialize(attendance)); + // 方式2:手动拷贝(无Serializable依赖) + // BusAttendance copy = new BusAttendance(); + // BeanUtils.copyProperties(attendance, copy); + copy.setCreateBy(1L); + copy.setUpdateBy(1L); + return copy; + }) + .collect(Collectors.toList()); + + // 步骤2:修复Stream流语法,生成CompletableFuture数组(关键修复点) + // 核心:map(中间操作)→ collect(终止操作生成列表)→ toArray(转数组) + CompletableFuture photoHandleFuture = CompletableFuture.allOf( + // 2.1 流处理:每条数据生成一个CompletableFuture + deepCopyList.stream() + .map(attendance -> + // 2.2 异步执行单条照片处理(使用自定义线程池,避免复用HttpClient线程池) + CompletableFuture.runAsync(() -> { + try { + handleSinglePhoto(attendance); // 处理单张照片 + } catch (Exception e) { + log.error("处理单条照片失败,userId={}, clockDate={}", + attendance.getUserId(), attendance.getClockDate(), e); + attendance.setFacePic(""); // 失败标记,不影响整体保存 + } + }, attendanceAsyncExecutor) // 改用自定义线程池(核心优化) + ) + // 2.3 终止操作:将Stream> 转为 List + .collect(Collectors.toList()) + // 2.4 转数组:List → 数组,适配CompletableFuture.allOf的参数要求 + .toArray(new CompletableFuture[0]) + ); + + // 步骤3:等待所有照片处理完成(阻塞当前异步任务线程,不阻塞主线程) + photoHandleFuture.get(); // 若需超时控制,可加参数:photoHandleFuture.get(30, TimeUnit.SECONDS) + + // 步骤4:批量保存数据库(不变) + boolean saveSuccess = attendanceService.saveBatch(deepCopyList); + if (saveSuccess) { + log.info("异步批量保存成功,条数:{}", deepCopyList.size()); + } else { + log.error("异步批量保存失败,条数:{}", deepCopyList.size()); + // 可选:重试逻辑 + saveSuccess = attendanceService.saveBatch(deepCopyList); + log.info("异步批量保存重试结果:{},条数:{}", saveSuccess ? "成功" : "失败", deepCopyList.size()); + } + + return CompletableFuture.completedFuture(saveSuccess); + + } catch (Exception e) { + log.error("异步处理照片和保存整体失败,条数:{}", batchList.size(), e); + return CompletableFuture.completedFuture(false); + } + } + + /** + * 处理单条照片(修复资源释放 + 超时控制 + 重试) + */ + private void handleSinglePhoto(BusAttendance attendance) { + String facePicRelativePath = attendance.getFacePic(); + if (facePicRelativePath == null || facePicRelativePath.isEmpty()) { + attendance.setFacePic(""); + return; + } + + // 1. 规范化路径(原逻辑不变) + String normalizedPath = facePicRelativePath.replace("\\", "/"); + String filename = Paths.get(normalizedPath).getFileName().toString(); + + // 2. 解析文件类型(优化:优先从HTTP响应头获取,其次用文件名) + String contentType = "application/octet-stream"; // 默认二进制类型 + + // 3. 多URL重试(原逻辑,增加超时和流关闭) + for (int retry = 0; retry < 2; retry++) { // 重试1次(共2次机会) + for (String baseUrl : BASE_URLS) { + String fullUrl = baseUrl + normalizedPath; + try { + // 构建带超时的HTTP请求 + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(fullUrl)) + .GET() + .timeout(Duration.ofSeconds(10)) // 读取超时10秒 + .build(); + + // 发送请求并处理响应 + try (InputStream inputStream = httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream()).body()) { + // 从响应头获取真实Content-Type(比文件名解析更准确) + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.discarding()); + contentType = response.headers().firstValue("Content-Type").orElse(contentType); + long contentLength = response.headers().firstValueAsLong("Content-Length").orElse(-1); + + // 上传OSS(此时流已通过try-with-resources自动关闭) + SysOssVo ossVo = ossService.upload(inputStream, filename, contentType, contentLength); + attendance.setFacePic(ossVo.getOssId() == null ? "" : ossVo.getOssId().toString()); + return; // 成功则跳出所有循环 + } + } catch (Exception e) { + log.warn("尝试URL失败(重试{}次): {}", retry + 1, fullUrl, e); + continue; // 重试下一个URL + } + } + } + + // 所有URL和重试都失败 + log.error("照片处理失败,所有URL重试完毕,relativePath={}", normalizedPath); + attendance.setFacePic(""); + } + + + private Long handleSinglePhoto1(String facePicRelativePath) { + + if (facePicRelativePath == null || facePicRelativePath.isEmpty()) { + return null; + } + + // 1. 规范化路径(原逻辑不变) + String normalizedPath = facePicRelativePath.replace("\\", "/"); + String filename = Paths.get(normalizedPath).getFileName().toString(); + + // 2. 解析文件类型(优化:优先从HTTP响应头获取,其次用文件名) + String contentType = "application/octet-stream"; // 默认二进制类型 + + // 3. 多URL重试(原逻辑,增加超时和流关闭) + for (int retry = 0; retry < 2; retry++) { // 重试1次(共2次机会) + for (String baseUrl : BASE_URLS) { + String fullUrl = baseUrl + normalizedPath; + try { + // 构建带超时的HTTP请求 + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(fullUrl)) + .GET() + .timeout(Duration.ofSeconds(10)) // 读取超时10秒 + .build(); + + // 发送请求并处理响应 + try (InputStream inputStream = httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream()).body()) { + // 从响应头获取真实Content-Type(比文件名解析更准确) + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.discarding()); + contentType = response.headers().firstValue("Content-Type").orElse(contentType); + long contentLength = response.headers().firstValueAsLong("Content-Length").orElse(-1); + + // 上传OSS(此时流已通过try-with-resources自动关闭) + SysOssVo ossVo = ossService.upload(inputStream, filename, contentType, contentLength); + return ossVo.getOssId(); // 成功则跳出所有循环 + } + } catch (Exception e) { + log.warn("尝试URL失败(重试{}次): {}", retry + 1, fullUrl, e); + continue; // 重试下一个URL + } + } + } + + // 所有URL和重试都失败 + log.error("照片处理失败,所有URL重试完毕,relativePath={}", normalizedPath); + return null; + } +}