2 Commits 11aa9d7ac7 ... 67597ae172

Auteur SHA1 Bericht Datum
  LIVE_YE 67597ae172 Merge remote-tracking branch 'origin/master' 3 weken geleden
  LIVE_YE 1764e97d8f 上传视频 3 weken geleden

+ 10 - 2
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java

@@ -1,5 +1,6 @@
 package com.ruoyi.web.controller.system;
 
+import com.ruoyi.common.utils.VideoStreamCompressor;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PostMapping;
@@ -144,10 +145,17 @@ public class SysProfileController extends BaseController {
     @PostMapping("/face")
     public AjaxResult face(@RequestParam("file") MultipartFile file, @RequestParam("userId") Long userId,@RequestParam("nickName") String nickName) throws Exception {
         if (!file.isEmpty()) {
+            //压缩视频
             String newFileName = userId+"_"+nickName;
-            String face = FileUploadUtils.uploadFileName(RuoYiConfig.getFacePath(), file, MimeTypeUtils.IMAGE_EXTENSION,newFileName,ONE);
+            byte[] bytes = VideoStreamCompressor.compressVideoStream(file, newFileName);
+            //存本地
+            String face = VideoStreamCompressor.saveBytesToFile(bytes,RuoYiConfig.getFacePath(),newFileName+".mp4");
+            //磁盘路径
+            String path = VideoStreamCompressor.saveBytesToFile(bytes, RuoYiConfig.getUploadPath(), newFileName + ".mp4");
+            String faceBd = FileUploadUtils.getPathFileName(RuoYiConfig.getUploadPath(),newFileName+".mp4");
+            //String face = FileUploadUtils.uploadFileName(RuoYiConfig.getFacePath(), file, MimeTypeUtils.VIDEO_EXTENSION,newFileName,ONE);
 
-            String faceBd = FileUploadUtils.uploadFileName(RuoYiConfig.getUploadPath(), file, MimeTypeUtils.IMAGE_EXTENSION,newFileName,TWO);
+            //String faceBd = FileUploadUtils.uploadFileName(RuoYiConfig.getUploadPath(), file, MimeTypeUtils.VIDEO_EXTENSION,newFileName,TWO);
             //保存进数据库
             SysUser user = new SysUser();
             user.setUserId(userId);

+ 7 - 0
ruoyi-common/pom.xml

@@ -190,6 +190,13 @@
             <version>${paho.mqtt.version}</version>
         </dependency>
 
+
+        <dependency>
+            <groupId>org.bytedeco</groupId>
+            <artifactId>javacv-platform</artifactId>
+            <version>1.5.8</version>
+        </dependency>
+
     </dependencies>
 
 </project>

+ 111 - 0
ruoyi-common/src/main/java/com/ruoyi/common/utils/VideoStreamCompressor.java

@@ -0,0 +1,111 @@
+package com.ruoyi.common.utils;
+
+import lombok.SneakyThrows;
+import org.bytedeco.ffmpeg.global.avcodec;
+import org.bytedeco.ffmpeg.global.avutil;
+import org.bytedeco.javacv.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.*;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+
+public class VideoStreamCompressor {
+
+    /**
+     * 压缩视频流数据
+     * @param inputFile 输入视频流
+     * @param fileneame 压缩后的输出流文件路径
+     */
+    @SneakyThrows
+    public static byte[] compressVideoStream( MultipartFile inputFile,String fileneame)  {
+
+        // 1. 创建临时文件
+        File tempInputFile = File.createTempFile(fileneame, ".tmp");
+        File tempOutputFile = File.createTempFile(fileneame, ".mp4");
+
+        try {
+            // 2. 将MultipartFile写入临时文件
+            inputFile.transferTo(tempInputFile);
+
+            // 3. 配置视频抓取器
+            try (FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(tempInputFile)) {
+                grabber.start();
+
+                // 4. 配置视频录制器(压缩输出)
+                try (FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(
+                        tempOutputFile,
+                        grabber.getImageWidth(),
+                        grabber.getImageHeight(),
+                        grabber.getAudioChannels())) {
+
+                    // 设置压缩参数
+                    recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);  // H.264编码
+                    recorder.setFormat("mp4");                         // 输出格式
+                    recorder.setVideoBitrate(1000_000);                // 目标比特率 (1Mbps)
+                    recorder.setFrameRate(grabber.getFrameRate());    // 保持原始帧率
+                    recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P); // 通用像素格式
+                    recorder.setVideoQuality(23);                      // 质量参数 (0-51,值越小质量越高)
+                    recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);   // AAC音频编码
+                    recorder.setAudioBitrate(128000);                  // 128kbps音频比特率
+
+                    // 5. 开始录制
+                    recorder.start();
+
+                    // 6. 逐帧处理视频
+                    Frame frame;
+                    while ((frame = grabber.grabFrame()) != null) {
+                        // 跳过损坏帧
+                        if (frame.image == null && frame.samples == null) continue;
+
+                        // 处理视频/音频帧
+                        if (frame.image != null) {
+                            recorder.record(frame);
+                        }
+                        if (frame.samples != null) {
+                            recorder.record(frame);
+                        }
+                    }
+                }
+            }
+
+            // 7. 读取压缩后的文件到字节数组
+            return java.nio.file.Files.readAllBytes(tempOutputFile.toPath());
+
+        } finally {
+            // 8. 清理临时文件
+            tempInputFile.delete();
+            tempOutputFile.delete();
+        }
+    }
+
+    /**
+     * 将字节数组保存到指定路径的文件
+     *
+     * @param data      字节数组
+     * @param directory 目标目录(如不存在会自动创建)
+     * @param filename  文件名(包含扩展名)
+     * @return 保存后的文件绝对路径
+     * @throws IOException 文件操作异常
+     */
+    public static String saveBytesToFile(byte[] data, String directory, String filename) throws IOException {
+        // 确保目录存在
+        Path dirPath = Paths.get(directory);
+        if (!Files.exists(dirPath)) {
+            Files.createDirectories(dirPath);
+        }
+
+        // 拼接完整文件路径
+        Path filePath = dirPath.resolve(filename);
+
+        // 写入文件
+        try (FileOutputStream fos = new FileOutputStream(filePath.toFile())) {
+            fos.write(data);
+        }
+
+        return filePath.toAbsolutePath().toString();
+    }
+
+}

+ 23 - 11
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/KaoqinRecordServiceImpl.java

@@ -152,7 +152,7 @@ public class KaoqinRecordServiceImpl implements IKaoqinRecordService {
         //查询打卡信息
         KaoqinRecord kaoqinRecord = kaoqinRecordMapper.selectKaoqinRecordByRecordByUser(Long.parseLong(userId), date);
         //查询考勤规则
-        KaoqinConfig kaoqinConfig = kaoqinConfigMapper.selectKaoqinConfigBynew();
+        //KaoqinConfig kaoqinConfig = kaoqinConfigMapper.selectKaoqinConfigBynew();
         if (kaoqinRecord == null) {
             //根据id查询人员信息
             SysUser sysUser = sysUserMapper.selectUserById(Long.parseLong(userId));
@@ -171,14 +171,21 @@ public class KaoqinRecordServiceImpl implements IKaoqinRecordService {
             kaoqinRecords.setKaTime(date);
             kaoqinRecords.setCreateTime(DateUtils.getNowDate());
             //此次打卡为第一次打卡
+            //在上班之前打卡
+            // todo 学校逻辑,没有迟到,早退,只要打卡就行,只要两次卡
+            kaoqinRecords.setKaTimeAmIn(DateUtils.parseDate(time.split(" ")[1]));
+            kaoqinRecords.setKaTypeAmIn(ONE);
+            kaoqinRecords.setKaStatus(ONE);
+            //todo 正常逻辑
             //判断是否在上班卡之前打卡
-            if (DateUtils.compareTimes(time.split(" ")[1], DateUtils.parseDateToStr("HH:mm:ss", kaoqinConfig.getKaTimeAmIn()), "HH:mm:ss") <= 0) {
+            /*if (DateUtils.compareTimes(time.split(" ")[1], DateUtils.parseDateToStr("HH:mm:ss", kaoqinConfig.getKaTimeAmIn()), "HH:mm:ss") <= 0) {
                 //在上班之前打卡
                 kaoqinRecords.setKaTimeAmIn(DateUtils.parseDate(time.split(" ")[1]));
                 kaoqinRecords.setKaTypeAmIn(ONE);
                 kaoqinRecords.setKaStatus(ONE);
 
-            } else {
+            }
+            else {
                 //在上班之后打卡
                 //判断配置要打几次卡
                 if (TWO.equals(kaoqinConfig.getKaNum())) {
@@ -205,29 +212,34 @@ public class KaoqinRecordServiceImpl implements IKaoqinRecordService {
                         kaoqinRecords.setKaTypeAmIn(FIVE);
                         kaoqinRecords.setKaTimeAmOut(DateUtils.parseDate(time.split(" ")[1]));
                         kaoqinRecords.setKaTypeAmOut(ONE);
-                        kaoqinRecord.setKaTimePmIn(DateUtils.parseDate(time.split(" ")[1]));
-                        kaoqinRecord.setKaTypePmIn(ONE);
+                        kaoqinRecords.setKaTimePmIn(DateUtils.parseDate(time.split(" ")[1]));
+                        kaoqinRecords.setKaTypePmIn(ONE);
 
                     } else if (DateUtils.isInTimeRange(time.split(" ")[1], DateUtils.parseDateToStr("HH:mm:ss", kaoqinConfig.getKaTimePmIn()), DateUtils.parseDateToStr("HH:mm:ss", kaoqinConfig.getKaTimePmOut()))) {
                         //下午迟到 打卡类别-上午下班 1正常 2:迟到 3:外勤 4:早退
                         kaoqinRecords.setKaTypeAmIn(FIVE);
                         kaoqinRecords.setKaTypeAmOut(FIVE);
-                        kaoqinRecord.setKaTimePmIn(DateUtils.parseDate(time.split(" ")[1]));
-                        kaoqinRecord.setKaTypePmIn(TWO);
+                        kaoqinRecords.setKaTimePmIn(DateUtils.parseDate(time.split(" ")[1]));
+                        kaoqinRecords.setKaTypePmIn(TWO);
 
                     } else if (DateUtils.compareTimes(time.split(" ")[1], DateUtils.parseDateToStr("HH:mm:ss", kaoqinConfig.getKaTimePmOut()), "HH:mm:ss") >= 0) {
                         //下午正常下班
                         kaoqinRecords.setKaTypeAmIn(FIVE);
                         kaoqinRecords.setKaTypeAmOut(FIVE);
-                        kaoqinRecord.setKaTypePmIn(FIVE);
+                        kaoqinRecords.setKaTypePmIn(FIVE);
                         kaoqinRecords.setKaTimePmOut(DateUtils.parseDate(time.split(" ")[1]));
                         kaoqinRecords.setKaTypePmOut(ONE);
                     }
                 }
-            }
+            }*/
             kaoqinRecordMapper.insertKaoqinRecord(kaoqinRecords);
         } else {
-            if (DateUtils.compareTimes(time.split(" ")[1], DateUtils.parseDateToStr("HH:mm:ss", kaoqinConfig.getKaTimeAmIn()), "HH:mm:ss") <= 0) {
+            // todo 学校逻辑,没有迟到,早退,只要打卡就行,只要两次卡
+            kaoqinRecord.setKaTimePmOut(DateUtils.parseDate(time.split(" ")[1]));
+            kaoqinRecord.setKaTypePmOut(ONE);
+            kaoqinRecord.setKaStatus(ONE);
+            //todo 正常逻辑
+            /*if (DateUtils.compareTimes(time.split(" ")[1], DateUtils.parseDateToStr("HH:mm:ss", kaoqinConfig.getKaTimeAmIn()), "HH:mm:ss") <= 0) {
                 //在上班之前打卡
                 kaoqinRecord.setKaTimeAmIn(DateUtils.parseDate(time.split(" ")[1]));
                 kaoqinRecord.setKaTypeAmIn(ONE);
@@ -283,7 +295,7 @@ public class KaoqinRecordServiceImpl implements IKaoqinRecordService {
                         kaoqinRecord.setKaTypePmOut(ONE);
                     }
                 }
-            }
+            }*/
             kaoqinRecordMapper.updateKaoqinRecord(kaoqinRecord);
         }
     }