deason 9 달 전
부모
커밋
eb1c36bba4

+ 144 - 0
src/main/java/cn/com/qmth/examcloud/tool/service/export_exam_capture/ExportExamCaptureTask.java

@@ -0,0 +1,144 @@
+package cn.com.qmth.examcloud.tool.service.export_exam_capture;
+
+import cn.com.qmth.examcloud.tool.config.SysProperty;
+import cn.com.qmth.examcloud.tool.entity.TaskEntity;
+import cn.com.qmth.examcloud.tool.service.CommonService;
+import cn.com.qmth.examcloud.tool.service.TaskService;
+import cn.com.qmth.examcloud.tool.service.export_exam_capture.vo.ExamCaptureInfo;
+import cn.com.qmth.examcloud.tool.utils.FileHelper;
+import cn.com.qmth.examcloud.tool.utils.FssHelper;
+import cn.com.qmth.examcloud.tool.vo.user.User;
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.context.AnalysisContext;
+import com.alibaba.excel.event.AnalysisEventListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicInteger;
+
+@Component
+public class ExportExamCaptureTask implements TaskService {
+
+    private final static Logger log = LoggerFactory.getLogger(ExportExamCaptureTask.class);
+
+    @Autowired
+    private SysProperty sysProperty;
+
+    @Autowired
+    private CommonService commonService;
+
+    @Override
+    public void start(TaskEntity task) {
+        // this.execute(loginUser, task);
+    }
+
+    public void execute(User loginUser, String exportDir) {
+        // List<ExamCaptureInfo> captures = this.loadDataFromExcel(exportDir + "/ec_oe_exam_sync_capture.xlsx");
+        List<ExamCaptureInfo> captures = this.loadDataFromExcel(exportDir + "/ec_oe_exam_capture.xlsx");
+        this.downloadImages(captures, exportDir);
+    }
+
+    private List<ExamCaptureInfo> loadDataFromExcel(String dataFilePath) {
+        /*
+            select d.id as exam_record_data_id,d.exam_id,d.course_id,c.code as course_code,c.name as course_name,
+            d.student_id,d.student_code,s.identity_number,d.student_name,p.file_url
+            from ec_oe_exam_sync_capture p
+            -- from ec_oe_exam_capture p
+            inner join ec_oe_exam_record_data d on p.exam_record_data_id=d.id
+            inner join ec_b_course c on c.id=d.course_id
+            inner join ec_b_student s on s.id=d.student_id
+            where d.exam_id in (676);
+         */
+
+        long startTime = System.currentTimeMillis();
+        List<ExamCaptureInfo> list = new ArrayList<>();
+        try {
+            EasyExcel.read(dataFilePath, ExamCaptureInfo.class, new AnalysisEventListener<ExamCaptureInfo>() {
+                        @Override
+                        public void invoke(ExamCaptureInfo data, AnalysisContext analysisContext) {
+                            list.add(data);
+                        }
+
+                        @Override
+                        public void doAfterAllAnalysed(AnalysisContext analysisContext) {
+                            // ignore
+                        }
+                    }
+            ).sheet().doRead();
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            throw new RuntimeException("Excel内容解析错误,请使用标准模板!");
+        }
+
+        long cost = System.currentTimeMillis() - startTime;
+        log.info("【获取抓拍照】 总数:{} 耗时:{}ms", list.size(), cost);
+        return list;
+    }
+
+    private void downloadImages(List<ExamCaptureInfo> list, String exportDir) {
+        long startTime = System.currentTimeMillis();
+        int totalCount = list.size();
+        AtomicInteger finishCount = new AtomicInteger(), successCount = new AtomicInteger(), failCount = new AtomicInteger();
+        ExecutorService executorService = Executors.newFixedThreadPool(5);
+
+        for (ExamCaptureInfo info : list) {
+            executorService.execute(() -> {
+                // 路径规则:/capture/考试ID/科目代码_科目名称/考试记录ID/证件号_考生姓名/抓拍照片名称.jpg
+                String savePhotoPath = String.format("%s/capture/%s/%s_%s/%s/%s_%s/%s", exportDir, info.getExamId(),
+                        info.getCourseCode(), info.getCourseName(), info.getExamRecordDataId(),
+                        info.getIdentityNumber(), info.getStudentName(),
+                        FileHelper.getFileName(info.getFileUrl()));
+                this.doDownload(info.getFileUrl(), savePhotoPath, successCount, failCount);
+                finishCount.incrementAndGet();
+
+                if (finishCount.get() % 100 == 0) {
+                    float finishRate = finishCount.get() * 100f / totalCount;
+                    long cost = Math.max((System.currentTimeMillis() - startTime) / 1000, 1);
+                    float speed = (float) finishCount.get() / cost;
+                    log.info("【图片下载】 总数:{} 成功数:{} 失败数:{} 进度:{}% 速度:约{}个每秒 已耗时:{}秒",
+                            totalCount, successCount.get(), failCount.get(), finishRate, speed, cost);
+                }
+            });
+        }
+
+        // 停止接受新任务,并等待所有任务完成
+        executorService.shutdown();
+        while (!executorService.isTerminated()) {
+            // ignore
+        }
+
+        float finishRate = finishCount.get() * 100f / totalCount;
+        long cost = Math.max((System.currentTimeMillis() - startTime) / 1000, 1);
+        float speed = (float) finishCount.get() / cost;
+        log.info("【图片下载】 总数:{} 成功数:{} 失败数:{} 进度:{}% 速度:约{}个每秒 已耗时:{}秒",
+                totalCount, successCount.get(), failCount.get(), finishRate, speed, cost);
+    }
+
+    private void doDownload(String photoUrl, String photoPath, AtomicInteger successCount, AtomicInteger failCount) {
+        // String urlPrefix = "https://ecs-test-static.qmth.com.cn";
+        String urlPrefix = "https://ecs-static.qmth.com.cn";
+        photoUrl = FssHelper.finalFileUrl(photoUrl, urlPrefix);
+
+        if (new File(photoPath).exists()) {
+            log.debug("【已下载】 {} {}", photoPath, photoUrl);
+            successCount.incrementAndGet();
+            return;
+        }
+
+        try {
+            FileHelper.saveImageToFile(photoUrl, photoPath);
+            successCount.incrementAndGet();
+        } catch (Exception e) {
+            log.error("【下载错误】 {} err:{}", photoPath, e.getMessage());
+            failCount.incrementAndGet();
+        }
+    }
+
+}

+ 45 - 0
src/main/java/cn/com/qmth/examcloud/tool/service/export_exam_capture/vo/ExamCaptureInfo.java

@@ -0,0 +1,45 @@
+package cn.com.qmth.examcloud.tool.service.export_exam_capture.vo;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.io.Serializable;
+
+@Setter
+@Getter
+public class ExamCaptureInfo implements Serializable {
+
+    private static final long serialVersionUID = -4935465309942094150L;
+
+    @ExcelProperty(value = "exam_record_data_id", index = 0)
+    private Long examRecordDataId;
+
+    @ExcelProperty(value = "exam_id", index = 1)
+    private Long examId;
+
+    @ExcelProperty(value = "course_id", index = 2)
+    private Long courseId;
+
+    @ExcelProperty(value = "course_code", index = 3)
+    private String courseCode;
+
+    @ExcelProperty(value = "course_name", index = 4)
+    private String courseName;
+
+    @ExcelProperty(value = "student_id", index = 5)
+    private String studentId;
+
+    @ExcelProperty(value = "student_code", index = 6)
+    private String studentCode;
+
+    @ExcelProperty(value = "identity_number", index = 7)
+    private String identityNumber;
+
+    @ExcelProperty(value = "student_name", index = 8)
+    private String studentName;
+
+    @ExcelProperty(value = "file_url", index = 9)
+    private String fileUrl;
+
+}

+ 6 - 6
src/main/java/cn/com/qmth/examcloud/tool/service/export_student_photo/ExportStudentPhotoTask.java

@@ -51,7 +51,7 @@ public class ExportStudentPhotoTask implements TaskService {
     public void execute(User loginUser, String exportDir) {
         // List<StudentVO> hasPhotoStudents = this.loadStudentFromUrl(loginUser);
         List<StudentVO> hasPhotoStudents = this.loadStudentFromExcel(exportDir + "/students.xlsx");
-        this.downloadStudents(hasPhotoStudents, exportDir, false);
+        this.downloadImages(hasPhotoStudents, exportDir, false);
     }
 
     private List<StudentVO> loadStudentFromUrl(User loginUser) {
@@ -140,7 +140,7 @@ public class ExportStudentPhotoTask implements TaskService {
         return hasPhotoStudents;
     }
 
-    private void downloadStudents(List<StudentVO> students, String exportDir, boolean fileNameByStudentCode) {
+    private void downloadImages(List<StudentVO> students, String exportDir, boolean fileNameByStudentCode) {
         long startTime = System.currentTimeMillis();
         int totalCount = students.size();
         AtomicInteger finishCount = new AtomicInteger(), successCount = new AtomicInteger(), failCount = new AtomicInteger();
@@ -156,16 +156,16 @@ public class ExportStudentPhotoTask implements TaskService {
                         }
                         for (String studentCode : student.getStudentCodeList()) {
                             String savePhotoPath = String.format("%s/%s/%s%s", exportDir, student.getRootOrgId(), studentCode, photoSuffix);
-                            this.downloadPhoto(student.getPhotoPath(), savePhotoPath, successCount, failCount);
+                            this.doDownload(student.getPhotoPath(), savePhotoPath, successCount, failCount);
                         }
                     } else {
                         log.warn("identityNumber:{} 无学号!", student.getIdentityNumber());
                         String savePhotoPath = String.format("%s/%s/%s%s", exportDir, student.getRootOrgId(), student.getIdentityNumber(), photoSuffix);
-                        this.downloadPhoto(student.getPhotoPath(), savePhotoPath, successCount, failCount);
+                        this.doDownload(student.getPhotoPath(), savePhotoPath, successCount, failCount);
                     }
                 } else {
                     String savePhotoPath = String.format("%s/%s/%s%s", exportDir, student.getRootOrgId(), student.getIdentityNumber(), photoSuffix);
-                    this.downloadPhoto(student.getPhotoPath(), savePhotoPath, successCount, failCount);
+                    this.doDownload(student.getPhotoPath(), savePhotoPath, successCount, failCount);
                 }
 
                 finishCount.incrementAndGet();
@@ -192,7 +192,7 @@ public class ExportStudentPhotoTask implements TaskService {
                 totalCount, successCount.get(), failCount.get(), finishRate, speed, cost);
     }
 
-    private void downloadPhoto(String photoUrl, String photoPath, AtomicInteger successCount, AtomicInteger failCount) {
+    private void doDownload(String photoUrl, String photoPath, AtomicInteger successCount, AtomicInteger failCount) {
         // String urlPrefix = "https://ecs-test-static.qmth.com.cn";
         String urlPrefix = "https://ecs-static.qmth.com.cn";
         photoUrl = FssHelper.finalFileUrl(photoUrl, urlPrefix);

+ 14 - 2
src/main/java/cn/com/qmth/examcloud/tool/utils/FileHelper.java

@@ -20,6 +20,18 @@ public class FileHelper {
 
     private static final Logger log = LoggerFactory.getLogger(FileHelper.class);
 
+    /**
+     * 获取文件名称
+     */
+    public static String getFileName(String filePath) {
+        if (StringUtils.isNotEmpty(filePath)) {
+            int index = filePath.lastIndexOf("/");
+            return filePath.substring(index + 1);
+        }
+
+        return "";
+    }
+
     /**
      * 获取文件后缀名,包括"."
      */
@@ -104,7 +116,7 @@ public class FileHelper {
         }
 
         try (OutputStream outStream = new FileOutputStream(zipFile);
-                ZipOutputStream zipStream = new ZipOutputStream(outStream, StandardCharsets.UTF_8);) {
+             ZipOutputStream zipStream = new ZipOutputStream(outStream, StandardCharsets.UTF_8);) {
             if (sourceFile.isDirectory()) {
                 File[] subFiles = sourceFile.listFiles();
                 if (subFiles.length == 0) {
@@ -205,7 +217,7 @@ public class FileHelper {
         }
 
         try (FileOutputStream fos = new FileOutputStream(targetFile);
-                BufferedOutputStream bos = new BufferedOutputStream(fos);) {
+             BufferedOutputStream bos = new BufferedOutputStream(fos);) {
             bos.write(bytes);
         } catch (IOException e) {
             log.error(e.getMessage(), e);

+ 8 - 3
src/test/java/cn/com/qmth/examcloud/tool/ToolTest.java

@@ -7,6 +7,7 @@ import cn.com.qmth.examcloud.tool.entity.TaskEntity;
 import cn.com.qmth.examcloud.tool.service.CommonService;
 import cn.com.qmth.examcloud.tool.service.batch_import_exam_student.BatchImportExamStudentTask;
 import cn.com.qmth.examcloud.tool.service.exam_record_data_audit.ExamRecordDataAuditTask;
+import cn.com.qmth.examcloud.tool.service.export_exam_capture.ExportExamCaptureTask;
 import cn.com.qmth.examcloud.tool.service.export_exam_paper_struct.ExportExamPaperStructTask;
 import cn.com.qmth.examcloud.tool.service.export_student_photo.ExportStudentPhotoTask;
 import cn.com.qmth.examcloud.tool.service.reset_student_password.ResetStudentPasswordTask;
@@ -38,10 +39,13 @@ public class ToolTest {
     private ExportStudentPhotoTask exportStudentPhotoTask;
 
     @Autowired
-    private ExportExamPaperStructTask exportExamPaperStructTask;
+    private ExamRecordDataAuditTask examRecordDataAuditTask;
 
     @Autowired
-    private ExamRecordDataAuditTask examRecordDataAuditTask;
+    private ExportExamCaptureTask exportExamCaptureTask;
+
+    @Autowired
+    private ExportExamPaperStructTask exportExamPaperStructTask;
 
     public static final String DATA_DIR = "D:/home/data";
 
@@ -52,8 +56,9 @@ public class ToolTest {
         // this.testTask(loginUser);
         // resetStudentPasswordTask.execute(loginUser, 0L);
         // exportExamPaperStructTask.execute(loginUser, 0L, DATA_DIR);
-        // exportStudentPhotoTask.execute(loginUser, DATA_DIR);
         // examRecordDataAuditTask.execute(loginUser);
+        // exportStudentPhotoTask.execute(loginUser, DATA_DIR);
+        // exportExamCaptureTask.execute(loginUser, DATA_DIR);
     }
 
     private void testTask(User loginUser) {