From b3352a2864d39c58a9224c48517d09a51208cf85 Mon Sep 17 00:00:00 2001 From: tongtongstudio Date: Sat, 13 Sep 2025 19:36:07 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=AE=9A=E6=97=B6=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E5=88=A0=E9=99=A4=E4=B8=8D=E5=AD=98=E5=9C=A8=E7=9A=84?= =?UTF-8?q?=E5=9B=BE=E6=A0=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../videotablet/VideoTabletApplication.java | 2 + .../videotablet/config/AsyncConfig.java | 23 ++++++++ .../videotablet/config/SchedulerConfig.java | 31 ++++++++++ .../controller/user/UserController.java | 22 ++++--- .../repository/ApkIconRepository.java | 25 ++++++++ .../videotablet/service/ApkIconService.java | 15 +++++ .../videotablet/task/AsyncScheduledTasks.java | 20 +++++++ .../videotablet/task/ScheduledTasks.java | 59 +++++++++++++++++++ 8 files changed, 189 insertions(+), 8 deletions(-) create mode 100644 src/main/java/com/onekeycall/videotablet/config/AsyncConfig.java create mode 100644 src/main/java/com/onekeycall/videotablet/config/SchedulerConfig.java create mode 100644 src/main/java/com/onekeycall/videotablet/task/AsyncScheduledTasks.java create mode 100644 src/main/java/com/onekeycall/videotablet/task/ScheduledTasks.java diff --git a/src/main/java/com/onekeycall/videotablet/VideoTabletApplication.java b/src/main/java/com/onekeycall/videotablet/VideoTabletApplication.java index d6e8f07..6b7d70e 100644 --- a/src/main/java/com/onekeycall/videotablet/VideoTabletApplication.java +++ b/src/main/java/com/onekeycall/videotablet/VideoTabletApplication.java @@ -3,9 +3,11 @@ package com.onekeycall.videotablet; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; @EnableScheduling +@EnableAsync // 启用异步支持 @MapperScan({"com.onekeycall.videotablet.mapper",}) @SpringBootApplication public class VideoTabletApplication { diff --git a/src/main/java/com/onekeycall/videotablet/config/AsyncConfig.java b/src/main/java/com/onekeycall/videotablet/config/AsyncConfig.java new file mode 100644 index 0000000..5f91367 --- /dev/null +++ b/src/main/java/com/onekeycall/videotablet/config/AsyncConfig.java @@ -0,0 +1,23 @@ +package com.onekeycall.videotablet.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.AsyncConfigurer; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import java.util.concurrent.Executor; + +@Configuration +@EnableAsync +public class AsyncConfig implements AsyncConfigurer { + + @Override + public Executor getAsyncExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(10); // 核心线程数 + executor.setMaxPoolSize(20); // 最大线程数 + executor.setQueueCapacity(100); // 队列容量 + executor.setThreadNamePrefix("async-task-"); + executor.initialize(); + return executor; + } +} diff --git a/src/main/java/com/onekeycall/videotablet/config/SchedulerConfig.java b/src/main/java/com/onekeycall/videotablet/config/SchedulerConfig.java new file mode 100644 index 0000000..c1cf020 --- /dev/null +++ b/src/main/java/com/onekeycall/videotablet/config/SchedulerConfig.java @@ -0,0 +1,31 @@ +package com.onekeycall.videotablet.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.SchedulingConfigurer; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; +import org.springframework.scheduling.config.ScheduledTaskRegistrar; +import java.util.concurrent.Executors; +import java.util.concurrent.Executor; + +@Configuration +public class SchedulerConfig implements SchedulingConfigurer { + + @Override + public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { +// taskRegistrar.setScheduler(taskExecutor()); + + ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); + scheduler.setPoolSize(10); // 核心线程数 + scheduler.setThreadNamePrefix("scheduled-task-"); // 线程名前缀 + scheduler.setAwaitTerminationSeconds(60); // 等待终止时间 + scheduler.setWaitForTasksToCompleteOnShutdown(true); // 关闭时等待任务完成 + scheduler.initialize(); // 初始化调度器 + taskRegistrar.setTaskScheduler(scheduler); + } + + public Executor taskExecutor() { + // 创建包含10个线程的线程池 + return Executors.newScheduledThreadPool(10); + } +} \ No newline at end of file diff --git a/src/main/java/com/onekeycall/videotablet/controller/user/UserController.java b/src/main/java/com/onekeycall/videotablet/controller/user/UserController.java index 06ac8a0..3dcd8cb 100644 --- a/src/main/java/com/onekeycall/videotablet/controller/user/UserController.java +++ b/src/main/java/com/onekeycall/videotablet/controller/user/UserController.java @@ -3,16 +3,10 @@ package com.onekeycall.videotablet.controller.user; import com.onekeycall.videotablet.config.PushIdConfig; import com.onekeycall.videotablet.controller.pub.LoginController; import com.onekeycall.videotablet.dto.TokenPair; -import com.onekeycall.videotablet.entity.DeviceApkInfo; -import com.onekeycall.videotablet.entity.DeviceInfo; -import com.onekeycall.videotablet.entity.DeviceLocation; -import com.onekeycall.videotablet.entity.User; +import com.onekeycall.videotablet.entity.*; import com.onekeycall.videotablet.gson.GsonUtils; import com.onekeycall.videotablet.result.Result; -import com.onekeycall.videotablet.service.DeviceApkInfoService; -import com.onekeycall.videotablet.service.DeviceLocationService; -import com.onekeycall.videotablet.service.DeviceSnService; -import com.onekeycall.videotablet.service.UserService; +import com.onekeycall.videotablet.service.*; import com.onekeycall.videotablet.utils.DevicePushUtils; import com.onekeycall.videotablet.utils.JwtUtil; import com.onekeycall.videotablet.utils.TextUtils; @@ -26,7 +20,9 @@ import org.springframework.web.bind.annotation.*; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; @RestController @RequestMapping("/user") @@ -45,6 +41,8 @@ public class UserController { private DeviceLocationService deviceLocationService; @Autowired private DeviceApkInfoService deviceApkInfoService; + @Autowired + private ApkIconService apkIconService; Logger logger = LoggerFactory.getLogger(LoginController.class); @@ -190,6 +188,14 @@ public class UserController { if (deviceApkInfo == null || deviceApkInfo.getApkList() == null) { return Result.notFound().message("未找到设备APK信息"); } + List apkList = deviceApkInfo.getApkList(); + apkList.stream().forEach(new Consumer() { + @Override + public void accept(ApkInfo apkInfo) { + Optional apkIconFileInfo = apkIconService.findMaxVersionCodeByPackageName(apkInfo.getPackageName()); + apkIconFileInfo.ifPresent(iconFileInfo -> apkInfo.setIconUrl(iconFileInfo.getFileName())); + } + }); return Result.ok().data(deviceApkInfo.getApkList()); } diff --git a/src/main/java/com/onekeycall/videotablet/repository/ApkIconRepository.java b/src/main/java/com/onekeycall/videotablet/repository/ApkIconRepository.java index caa75e3..94b9f59 100644 --- a/src/main/java/com/onekeycall/videotablet/repository/ApkIconRepository.java +++ b/src/main/java/com/onekeycall/videotablet/repository/ApkIconRepository.java @@ -1,9 +1,15 @@ package com.onekeycall.videotablet.repository; import com.onekeycall.videotablet.entity.ApkIconFileInfo; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import java.util.List; +import java.util.Optional; public interface ApkIconRepository extends JpaRepository { @@ -15,4 +21,23 @@ public interface ApkIconRepository extends JpaRepository List findByPackageName(String packageName); + List findAll(); + + Integer deleteApkIconFileInfosById(Long id); + + /** + * 通过packageName查找version_code最大的一条记录 + * 使用 JPQL 并限制结果数量,但JPQL不支持LIMIT,故用Pageable + */ + @Query("SELECT e FROM ApkIconFileInfo e WHERE e.packageName = :pkgName ORDER BY e.versionCode DESC") + Page findTopByPackageNameOrderByVersionCodeDesc(@Param("pkgName") String packageName, Pageable pageable); + + /** + * 在Service中调用该方法,获取第一条记录 + */ + default Optional findMaxVersionCodeByPackageName(String packageName) { + PageRequest pageRequest = PageRequest.of(0, 1); // 获取第一页的第一条记录 + Page page = findTopByPackageNameOrderByVersionCodeDesc(packageName, pageRequest); + return page.getContent().stream().findFirst(); + } } diff --git a/src/main/java/com/onekeycall/videotablet/service/ApkIconService.java b/src/main/java/com/onekeycall/videotablet/service/ApkIconService.java index 6ebd4ec..3e79eed 100644 --- a/src/main/java/com/onekeycall/videotablet/service/ApkIconService.java +++ b/src/main/java/com/onekeycall/videotablet/service/ApkIconService.java @@ -4,8 +4,10 @@ import com.onekeycall.videotablet.entity.ApkIconFileInfo; import com.onekeycall.videotablet.repository.ApkIconRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.List; +import java.util.Optional; @Service public class ApkIconService { @@ -27,4 +29,17 @@ public class ApkIconService { public List findByPackageName(String packageName) { return apkIconRepository.findByPackageName(packageName); } + + public Optional findMaxVersionCodeByPackageName(String packageName) { + return apkIconRepository.findMaxVersionCodeByPackageName(packageName); + } + + public List findAll() { + return apkIconRepository.findAll(); + } + + @Transactional + public Integer deleteApkIconFileInfosById(Long id) { + return apkIconRepository.deleteApkIconFileInfosById(id); + } } diff --git a/src/main/java/com/onekeycall/videotablet/task/AsyncScheduledTasks.java b/src/main/java/com/onekeycall/videotablet/task/AsyncScheduledTasks.java new file mode 100644 index 0000000..4177a45 --- /dev/null +++ b/src/main/java/com/onekeycall/videotablet/task/AsyncScheduledTasks.java @@ -0,0 +1,20 @@ +package com.onekeycall.videotablet.task; + +import org.springframework.scheduling.annotation.Async; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +@Component +public class AsyncScheduledTasks { + +// @Async // 异步执行 +// @Scheduled(fixedRate = 5000) +// public void asyncTask() { +// try { +// Thread.sleep(3000); // 模拟耗时操作 +// System.out.println("异步任务执行: " + Thread.currentThread().getName()); +// } catch (InterruptedException e) { +// e.printStackTrace(); +// } +// } +} \ No newline at end of file diff --git a/src/main/java/com/onekeycall/videotablet/task/ScheduledTasks.java b/src/main/java/com/onekeycall/videotablet/task/ScheduledTasks.java new file mode 100644 index 0000000..ea7c6a0 --- /dev/null +++ b/src/main/java/com/onekeycall/videotablet/task/ScheduledTasks.java @@ -0,0 +1,59 @@ +package com.onekeycall.videotablet.task; + +import com.onekeycall.videotablet.config.FilePath; +import com.onekeycall.videotablet.entity.ApkIconFileInfo; +import com.onekeycall.videotablet.repository.ApkIconRepository; +import com.onekeycall.videotablet.service.ApkIconService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Async; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.io.File; +import java.util.Date; +import java.util.List; + +@Component +public class ScheduledTasks { + @Autowired + private ApkIconService apkIconService; + + Logger logger = LoggerFactory.getLogger(ScheduledTasks.class); + +// // 固定速率:每隔5秒执行一次(从上一次任务开始时间计算) +// @Scheduled(fixedRate = 5000) +// public void taskWithFixedRate() { +// System.out.println("固定速率任务执行: " + new Date()); +// } +// +// // 固定延迟:上一次任务结束后延迟3秒执行 +// @Scheduled(fixedDelay = 3000) +// public void taskWithFixedDelay() { +// System.out.println("固定延迟任务执行: " + new Date()); +// } +// +// // 初始延迟:应用启动后延迟10秒开始,然后每隔5秒执行一次 +// @Scheduled(initialDelay = 10000, fixedRate = 5000) +// public void taskWithInitialDelay() { +// System.out.println("带初始延迟的任务执行: " + new Date()); +// } + + // 使用Cron表达式:每天中午12点执行 + @Scheduled(cron = "0 0 0 * * ?") +// @Scheduled(initialDelay = 10000) + @Async + public void taskWithCron() { + logger.info("Cron表达式任务执行: " + new Date()); + List apkIconFileInfoList = apkIconService.findAll(); + for (ApkIconFileInfo apkIconFileInfo : apkIconFileInfoList) { + logger.info("检查图标文件是否存在: " + apkIconFileInfo); + File file = new File(FilePath.getApkIconPath() + apkIconFileInfo.getFileName()); + if (!file.exists()) { + Integer i = apkIconService.deleteApkIconFileInfosById(apkIconFileInfo.getId()); + logger.info("删除图标数据: id = " + apkIconFileInfo.getId() + " Successful =" + i); + } + } + } +}