yangdamao vor 2 Jahren
Ursprung
Commit
5826629ff2
24 geänderte Dateien mit 1273 neuen und 147 gelöschten Zeilen
  1. 84 21
      zhongzheng-admin/src/main/java/com/zhongzheng/controller/exam/ExamApplyController.java
  2. 5 0
      zhongzheng-common/pom.xml
  3. 24 6
      zhongzheng-common/src/main/java/com/zhongzheng/common/utils/DateUtils.java
  4. 46 0
      zhongzheng-common/src/main/java/com/zhongzheng/common/utils/JavaMailUtils.java
  5. 147 0
      zhongzheng-common/src/main/java/com/zhongzheng/common/utils/file/FileUtils.java
  6. 49 1
      zhongzheng-common/src/main/java/com/zhongzheng/common/utils/poi/ExcelUtil.java
  7. 33 19
      zhongzheng-system/src/main/java/com/zhongzheng/modules/course/service/impl/CourseServiceImpl.java
  8. 7 0
      zhongzheng-system/src/main/java/com/zhongzheng/modules/exam/bo/ExamApplyAddBo.java
  9. 8 2
      zhongzheng-system/src/main/java/com/zhongzheng/modules/exam/bo/ExamApplyEditBo.java
  10. 4 5
      zhongzheng-system/src/main/java/com/zhongzheng/modules/exam/bo/ExamApplyQueryBo.java
  11. 39 0
      zhongzheng-system/src/main/java/com/zhongzheng/modules/exam/bo/ExamApplySendmailBo.java
  12. 26 0
      zhongzheng-system/src/main/java/com/zhongzheng/modules/exam/bo/ExamApplyUserImportBo.java
  13. 2 0
      zhongzheng-system/src/main/java/com/zhongzheng/modules/exam/domain/ExamApply.java
  14. 7 5
      zhongzheng-system/src/main/java/com/zhongzheng/modules/exam/domain/ExamApplySiteTime.java
  15. 5 0
      zhongzheng-system/src/main/java/com/zhongzheng/modules/exam/mapper/ExamApplyMapper.java
  16. 10 0
      zhongzheng-system/src/main/java/com/zhongzheng/modules/exam/service/IExamApplyService.java
  17. 553 65
      zhongzheng-system/src/main/java/com/zhongzheng/modules/exam/service/impl/ExamApplyServiceImpl.java
  18. 22 20
      zhongzheng-system/src/main/java/com/zhongzheng/modules/exam/service/impl/ExamApplySiteServiceImpl.java
  19. 57 0
      zhongzheng-system/src/main/java/com/zhongzheng/modules/exam/vo/ExamApplyPlaceVo.java
  20. 11 2
      zhongzheng-system/src/main/java/com/zhongzheng/modules/exam/vo/ExamApplyVo.java
  21. 4 0
      zhongzheng-system/src/main/java/com/zhongzheng/modules/system/domain/SysTenant.java
  22. 72 0
      zhongzheng-system/src/main/java/com/zhongzheng/modules/user/vo/UserStudyRecordExport.java
  23. 57 0
      zhongzheng-system/src/main/resources/mapper/modules/exam/ExamApplyMapper.xml
  24. 1 1
      zhongzheng-system/src/main/resources/mapper/modules/user/UserMockSubscribeMapper.xml

+ 84 - 21
zhongzheng-admin/src/main/java/com/zhongzheng/controller/exam/ExamApplyController.java

@@ -1,33 +1,30 @@
 package com.zhongzheng.controller.exam;
 
-import java.util.List;
-import java.util.Arrays;
-
-import com.zhongzheng.modules.exam.bo.*;
-import com.zhongzheng.modules.exam.service.IExamApplyGoodsService;
-import com.zhongzheng.modules.exam.service.IExamApplySiteService;
-import com.zhongzheng.modules.exam.service.IExamApplyUserService;
-import com.zhongzheng.modules.exam.vo.*;
-import lombok.RequiredArgsConstructor;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.PutMapping;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
 import com.zhongzheng.common.annotation.Log;
 import com.zhongzheng.common.core.controller.BaseController;
 import com.zhongzheng.common.core.domain.AjaxResult;
+import com.zhongzheng.common.core.page.TableDataInfo;
 import com.zhongzheng.common.enums.BusinessType;
+import com.zhongzheng.common.exception.CustomException;
+import com.zhongzheng.common.utils.poi.EasyPoiUtil;
+import com.zhongzheng.modules.exam.bo.*;
+import com.zhongzheng.modules.exam.service.IExamApplyGoodsService;
 import com.zhongzheng.modules.exam.service.IExamApplyService;
-import com.zhongzheng.common.utils.poi.ExcelUtil;
-import com.zhongzheng.common.core.page.TableDataInfo;
+import com.zhongzheng.modules.exam.service.IExamApplySiteService;
+import com.zhongzheng.modules.exam.service.IExamApplyUserService;
+import com.zhongzheng.modules.exam.vo.*;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.List;
 
 /**
  * 考试安排Controller
@@ -61,6 +58,36 @@ public class ExamApplyController extends BaseController {
         return getDataTable(list);
     }
 
+    /**
+     * 查询考场列表
+     */
+    @ApiOperation("查询考场列表")
+    @GetMapping("/place")
+    public TableDataInfo<ExamApplyPlaceVo> placeList(ExamApplyQueryBo bo) {
+        startPage();
+        List<ExamApplyPlaceVo> list = iExamApplyService.getPlaceList(bo);
+        return getDataTable(list);
+    }
+
+    /**
+     * 考场邮件信息
+     */
+    @ApiOperation("考场邮件地址")
+    @GetMapping("/sendmail")
+    public AjaxResult getSendmailUrl(ExamApplyQueryBo bo) {
+        String url = iExamApplyService.getSendmailUrl(bo.getIds());
+        return AjaxResult.success(url);
+    }
+
+    /**
+     * 考场邮件发送
+     */
+    @ApiOperation("考场邮件发送")
+    @PostMapping("/send")
+    public AjaxResult<Void> sendmail(@RequestBody ExamApplySendmailBo bo) {
+        return toAjax(iExamApplyService.sendmail(bo)? 1 : 0);
+    }
+
 
     /**
      * 获取考试安排详细信息
@@ -94,6 +121,24 @@ public class ExamApplyController extends BaseController {
         return toAjax(iExamApplyService.insertByAddBo(bo) ? 1 : 0);
     }
 
+    /**
+     * 新增考试安排
+     */
+    @ApiOperation("新增考试安排")
+    @PostMapping("/save")
+    public AjaxResult<Void> saveExamApply(MultipartFile file, String param) {
+        if (StringUtils.isBlank(param)){
+            throw new CustomException("参数有误!");
+        }
+        ExamApplyAddBo addBo = JSONObject.parseObject(param, ExamApplyAddBo.class);
+        if (ObjectUtils.isNotNull(file)){
+            //学员资料解析
+            List<ExamApplyUserImportBo> applyUsers = EasyPoiUtil.importExcel(file,0,1,ExamApplyUserImportBo.class);
+            addBo.setApplyUsers(applyUsers);
+        }
+        return toAjax(iExamApplyService.saveExamApply(addBo) ? 1 : 0);
+    }
+
     /**
      * 修改考试安排
      */
@@ -105,6 +150,24 @@ public class ExamApplyController extends BaseController {
         return toAjax(iExamApplyService.updateByEditBo(bo) ? 1 : 0);
     }
 
+    /**
+     * 修改考试安排
+     */
+    @ApiOperation("修改考试安排")
+    @PostMapping("/editApply")
+    public AjaxResult<Void> editExamApply(MultipartFile file, String param) {
+        if (StringUtils.isBlank(param)){
+            throw new CustomException("参数有误!");
+        }
+        ExamApplyEditBo editBo = JSONObject.parseObject(param, ExamApplyEditBo.class);
+        if (ObjectUtils.isNotNull(file)){
+            //学员资料解析
+            List<ExamApplyUserImportBo> applyUsers = EasyPoiUtil.importExcel(file,0,1,ExamApplyUserImportBo.class);
+            editBo.setApplyUsers(applyUsers);
+        }
+        return toAjax(iExamApplyService.editExamApply(editBo) ? 1 : 0);
+    }
+
     /**
      * 新增考试安排地点
      */

+ 5 - 0
zhongzheng-common/pom.xml

@@ -237,6 +237,11 @@
             <groupId>io.undertow</groupId>
             <artifactId>undertow-core</artifactId>
         </dependency>
+        <dependency>
+            <groupId>javax.mail</groupId>
+            <artifactId>mail</artifactId>
+            <version>1.4.7</version>
+        </dependency>
 
     </dependencies>
 

+ 24 - 6
zhongzheng-common/src/main/java/com/zhongzheng/common/utils/DateUtils.java

@@ -1,21 +1,19 @@
 package com.zhongzheng.common.utils;
 
+import cn.hutool.core.lang.Validator;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.commons.lang3.time.DateFormatUtils;
+
 import java.lang.management.ManagementFactory;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
-import java.time.LocalDate;
 import java.time.LocalDateTime;
-import java.time.ZoneOffset;
 import java.time.format.DateTimeFormatter;
 import java.util.Calendar;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
 
-import cn.hutool.core.lang.Validator;
-import org.apache.commons.lang3.RandomStringUtils;
-import org.apache.commons.lang3.time.DateFormatUtils;
-
 /**
  * 时间工具类
  *
@@ -123,6 +121,26 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
         return DateFormatUtils.format(date, "yyyy/MM/dd");
     }
 
+    public static String timestampToDateFormatMonth(Long times){
+        if(Validator.isEmpty(times)){
+            return "";
+        }
+        long t = times.longValue();
+        t = t * 1000;
+        Date date = new Date(t);
+        return DateFormatUtils.format(date, "MM月dd号");
+    }
+
+    public static String timestampToDateFormatMonthTwo(Long times){
+        if(Validator.isEmpty(times)){
+            return "";
+        }
+        long t = times.longValue();
+        t = t * 1000;
+        Date date = new Date(t);
+        return DateFormatUtils.format(date, "MM.dd");
+    }
+
     public static String timestampToDateFormat(Long times,String patternStr){
         if(Validator.isEmpty(times)){
             return "";

+ 46 - 0
zhongzheng-common/src/main/java/com/zhongzheng/common/utils/JavaMailUtils.java

@@ -0,0 +1,46 @@
+package com.zhongzheng.common.utils;
+
+
+import javax.mail.Authenticator;
+import javax.mail.PasswordAuthentication;
+import javax.mail.Session;
+import java.util.Properties;
+
+/**
+ * @author yangdamao
+ * @date 2023年06月19日 10:11
+ */
+public class JavaMailUtils {
+
+    public static Session createsession(String postAccount,String postPassword,String STMPserver,String post) {
+        // SMTP服务器地址
+        String smtp = STMPserver;
+
+        // 邮箱账号和密码(授权密码)
+        String userName = postAccount;
+        String password = postPassword;
+
+        // SMTP服务器的连接信息
+        Properties props = new Properties();
+        props.put("mail.smtp.host", smtp); // SMTP主机号
+        props.put("mail.smtp.port", post); // 主机端口号
+        props.put("mail.smtp.auth", "true"); // 是否需要认证
+        props.put("mail.smtp.starttls.enable", "true"); // 启用TLS加密
+        props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
+
+        // 创建Session
+        // 参数1:SMTP服务器的连接信息
+        // 参数2:用户认证对象(Authenticator接口的匿名实现类)
+        Session session = Session.getInstance(props, new Authenticator() {
+            @Override
+            protected PasswordAuthentication getPasswordAuthentication() {
+                return new PasswordAuthentication(userName, password);
+            }
+        });
+
+        // 开启调试模式
+        session.setDebug(true);
+
+        return session;
+    }
+}

+ 147 - 0
zhongzheng-common/src/main/java/com/zhongzheng/common/utils/file/FileUtils.java

@@ -7,6 +7,7 @@ import com.zhongzheng.common.exception.CustomException;
 import org.apache.commons.fileupload.FileItem;
 import org.apache.commons.fileupload.disk.DiskFileItemFactory;
 import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.http.MediaType;
 import org.springframework.web.multipart.MultipartFile;
 import org.springframework.web.multipart.commons.CommonsMultipartFile;
@@ -22,8 +23,11 @@ import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.security.MessageDigest;
 import java.util.Comparator;
+import java.util.List;
 import java.util.UUID;
 import java.util.stream.Stream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
 
 /**
  * 文件处理工具类
@@ -32,6 +36,8 @@ import java.util.stream.Stream;
  */
 public class FileUtils extends org.apache.commons.io.FileUtils
 {
+
+    private static final byte[] buf = new byte[1024];
     public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+";
 
     /**
@@ -297,4 +303,145 @@ public class FileUtils extends org.apache.commons.io.FileUtils
             throw new CustomException(e.getMessage());
         }
     }
+
+    /**
+     * 压缩成ZIP 方法1
+     *
+     * @param zipFileName       压缩文件夹路径
+     * @param sourceFileName    要压缩的文件路径
+     * @param KeepDirStructure 是否保留原来的目录结构,true:保留目录结构;
+     *                         false:所有文件跑到压缩包根目录下(注意:不保留目录结构可能会出现同名文件,会压缩失败)
+     * @throws RuntimeException 压缩失败会抛出运行时异常
+     */
+    public static Boolean toZip(String zipFileName, String sourceFileName, boolean KeepDirStructure) {
+        Boolean result = true;
+        long start = System.currentTimeMillis();//开始
+        ZipOutputStream zos = null;
+        try {
+            FileOutputStream fileOutputStream = new FileOutputStream(zipFileName);
+            zos = new ZipOutputStream(fileOutputStream);
+            File sourceFile = new File(sourceFileName);
+            compress(sourceFile, zos, sourceFile.getName(), KeepDirStructure);
+            long end = System.currentTimeMillis();//结束
+            System.out.println("压缩完成,耗时:" + (end - start) + " 毫秒");
+        } catch (Exception e) {
+            result = false;
+            e.printStackTrace();
+        } finally {
+            if (zos != null) {
+                try {
+                    zos.close();
+                } catch (IOException e) {
+                    e.getStackTrace();
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
+     * @Title: compress
+     * @Description: TODO
+     * @param filePaths 需要压缩的文件地址列表(绝对路径)
+     * @param zipFilePath 需要压缩到哪个zip文件(无需创建这样一个zip,只需要指定一个全路径)
+     * @param keepDirStructure 压缩后目录是否保持原目录结构
+     * @throws IOException
+     * @return int   压缩成功的文件个数
+     */
+    public static Boolean compressList(List<String> filePaths, String zipFilePath, Boolean keepDirStructure){
+        byte[] buf = new byte[1024];
+        File zipFile = new File(zipFilePath);
+        int fileCount = 0;//记录压缩了几个文件?
+        try {
+            //zip文件不存在,则创建文件,用于压缩
+            if(!zipFile.exists()){
+                zipFile.createNewFile();
+            }
+            ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFile));
+            for(int i = 0; i < filePaths.size(); i++){
+                String relativePath = filePaths.get(i);
+                if(StringUtils.isEmpty(relativePath)){
+                    continue;
+                }
+                File sourceFile = new File(relativePath);//绝对路径找到file
+                if(sourceFile == null || !sourceFile.exists()){
+                    continue;
+                }
+
+                FileInputStream fis = new FileInputStream(sourceFile);
+                if(keepDirStructure!=null && keepDirStructure){
+                    //保持目录结构
+                    zos.putNextEntry(new ZipEntry(relativePath));
+                }else{
+                    //直接放到压缩包的根目录
+                    zos.putNextEntry(new ZipEntry(sourceFile.getName()));
+                }
+                //System.out.println("压缩当前文件:"+sourceFile.getName());
+                int len;
+                while((len = fis.read(buf)) > 0){
+                    zos.write(buf, 0, len);
+                }
+                zos.closeEntry();
+                fis.close();
+                fileCount++;
+            }
+            zos.close();
+            //System.out.println("压缩完成");
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return fileCount > 0;
+    }
+
+
+    /**
+     * 递归压缩方法
+     *
+     * @param sourceFile       源文件
+     * @param zos              zip输出流
+     * @param name             压缩后的名称
+     * @param KeepDirStructure 是否保留原来的目录结构,true:保留目录结构;
+     *                         false:所有文件跑到压缩包根目录下(注意:不保留目录结构可能会出现同名文件,会压缩失败)
+     * @throws Exception
+     */
+    public static void compress(File sourceFile, ZipOutputStream zos, String name,
+                                boolean KeepDirStructure) throws Exception {
+
+        if (sourceFile.isFile()) {
+            // 向zip输出流中添加一个zip实体,构造器中name为zip实体的文件的名字
+            zos.putNextEntry(new ZipEntry(name));
+            // copy文件到zip输出流中
+            int len;
+            FileInputStream in = new FileInputStream(sourceFile);
+            while ((len = in.read(buf)) != -1) {
+                zos.write(buf, 0, len);
+            }
+            // Complete the entry
+            zos.closeEntry();
+            in.close();
+        } else {
+            File[] listFiles = sourceFile.listFiles();
+            if (listFiles == null || listFiles.length == 0) {
+                // 需要保留原来的文件结构时,需要对空文件夹进行处理
+                if (KeepDirStructure) {
+                    // 空文件夹的处理
+                    zos.putNextEntry(new ZipEntry(name + "/"));
+                    // 没有文件,不需要文件的copy
+                    zos.closeEntry();
+                }
+            } else {
+                for (File file : listFiles) {
+                    // 判断是否需要保留原来的文件结构
+                    if (KeepDirStructure) {
+                        // 注意:file.getName()前面需要带上父文件夹的名字加一斜杠,
+                        // 不然最后压缩包中就不能保留原来的文件结构,即:所有文件都跑到压缩包根目录下了
+                        compress(file, zos, name + "/" + file.getName(), KeepDirStructure);
+                    } else {
+                        compress(file, zos, file.getName(), KeepDirStructure);
+                    }
+                }
+            }
+        }
+    }
+
 }

+ 49 - 1
zhongzheng-common/src/main/java/com/zhongzheng/common/utils/poi/ExcelUtil.java

@@ -30,7 +30,6 @@ import java.io.*;
 import java.lang.reflect.Field;
 import java.math.BigDecimal;
 import java.text.DecimalFormat;
-import java.text.SimpleDateFormat;
 import java.util.*;
 import java.util.stream.Collectors;
 
@@ -388,6 +387,55 @@ public class ExcelUtil<T>
         }
     }
 
+    public void exportEasyExcelStudy(List<Map<String, Object>> sheetsList,String path)
+    {
+        OutputStream out = null;
+        //     Workbook workbook = null;
+        try
+        {
+            Workbook workbook = ExcelExportUtil.exportExcel(sheetsList, ExcelType.HSSF);
+            File desc = new File(path);
+            if (!desc.getParentFile().exists())
+            {
+                desc.getParentFile().mkdirs();
+            }
+            out = new FileOutputStream(path);
+            workbook.write(out);
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+            log.error("导出Excel异常{}", e.getMessage());
+            throw new CustomException("导出Excel失败,请联系网站管理员!");
+        }
+        finally
+        {
+
+            if (wb != null)
+            {
+                try
+                {
+                    wb.close();
+                }
+                catch (IOException e1)
+                {
+                    e1.printStackTrace();
+                }
+            }
+            if (out != null)
+            {
+                try
+                {
+                    out.close();
+                }
+                catch (IOException e1)
+                {
+                    e1.printStackTrace();
+                }
+            }
+        }
+    }
+
     /**
      * 对list数据源将其里面的数据导入到excel表单
      *

+ 33 - 19
zhongzheng-system/src/main/java/com/zhongzheng/modules/course/service/impl/CourseServiceImpl.java

@@ -3,8 +3,6 @@ package com.zhongzheng.modules.course.service.impl;
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.lang.Validator;
 import cn.hutool.core.util.StrUtil;
-
-
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
@@ -24,13 +22,11 @@ import com.zhongzheng.modules.course.service.ICoursePhotoLogService;
 import com.zhongzheng.modules.course.service.ICourseService;
 import com.zhongzheng.modules.course.vo.CourseUserVo;
 import com.zhongzheng.modules.course.vo.CourseVo;
+import com.zhongzheng.modules.exam.domain.ExamApply;
 import com.zhongzheng.modules.exam.domain.ExamApplyUser;
 import com.zhongzheng.modules.exam.domain.ExamBefore;
 import com.zhongzheng.modules.exam.domain.ExamBeforeApply;
-import com.zhongzheng.modules.exam.service.IExamApplyGoodsService;
-import com.zhongzheng.modules.exam.service.IExamApplyUserService;
-import com.zhongzheng.modules.exam.service.IExamBeforeApplyService;
-import com.zhongzheng.modules.exam.service.IExamBeforeService;
+import com.zhongzheng.modules.exam.service.*;
 import com.zhongzheng.modules.exam.vo.ExamApplyGoodsVo;
 import com.zhongzheng.modules.goods.bo.GoodsBatchDelBo;
 import com.zhongzheng.modules.goods.domain.GoodsCourse;
@@ -110,6 +106,9 @@ public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course> impleme
     @Autowired
     private IExamApplyGoodsService iExamApplyGoodsService;
 
+    @Autowired
+    private IExamApplyService iExamApplyService;
+
     @Autowired
     private IExamApplyUserService iExamApplyUserService;
 
@@ -496,19 +495,34 @@ public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course> impleme
                 List<ExamApplyGoodsVo> examApplyGoodsVos = new ArrayList<>();
                 List<ExamApplyGoodsVo> examApplyGoodsVos2 = new ArrayList<>();
                 examApplyGoodsVoList.forEach(examApplyGoodsVo -> {
-                    LambdaQueryWrapper<ExamApplyUser> lqw = Wrappers.lambdaQuery();
-                    lqw.eq(ExamApplyUser::getApplyId, examApplyGoodsVo.getApplyId());
-                    List<ExamApplyUser> examApplyUserList = iExamApplyUserService.list(lqw);
-                    if (examApplyUserList != null && examApplyUserList.size() > 0) {
-                        ArrayList<Long> userIds = new ArrayList<>();
-                        examApplyUserList.forEach(examApplyUser -> {
-                            userIds.add(examApplyUser.getUserId());
-                        });
-                        if (userIds.contains(bo.getUserId())) {
-                            examApplyGoodsVos.add(examApplyGoodsVo);
-                        }
-                    }else {
-                        examApplyGoodsVos2.add(examApplyGoodsVo);
+                    ExamApply examApply = iExamApplyService.getById(examApplyGoodsVo.getApplyId());
+                    switch (examApply.getApplyNature()){
+                        case 1: //普通场
+                            LambdaQueryWrapper<ExamApplyUser> lqw = Wrappers.lambdaQuery();
+                            lqw.eq(ExamApplyUser::getApplyId, examApplyGoodsVo.getApplyId());
+                            List<ExamApplyUser> examApplyUserList = iExamApplyUserService.list(lqw);
+                            if (examApplyUserList != null && examApplyUserList.size() > 0) {
+                                ArrayList<Long> userIds = new ArrayList<>();
+                                examApplyUserList.forEach(examApplyUser -> {
+                                    userIds.add(examApplyUser.getUserId());
+                                });
+                                if (userIds.contains(bo.getUserId())) {
+                                    examApplyGoodsVos.add(examApplyGoodsVo);
+                                }
+                            }else {
+                                examApplyGoodsVos2.add(examApplyGoodsVo);
+                            }
+                            break;
+                        case 2://专场
+                            List<ExamApplyUser> applyUsers = iExamApplyUserService
+                                    .list(new LambdaQueryWrapper<ExamApplyUser>().eq(ExamApplyUser::getApplyId, examApply.getApplyId()));
+                            if (CollectionUtils.isNotEmpty(applyUsers) &&
+                                    applyUsers.stream().anyMatch(x -> x.getUserId().equals(bo.getUserId()))){
+                                examApplyGoodsVos.add(examApplyGoodsVo);
+                            }
+                            break;
+                        default:
+                            break;
                     }
                 });
                 examApplyGoodsVos.addAll(examApplyGoodsVos2);

+ 7 - 0
zhongzheng-system/src/main/java/com/zhongzheng/modules/exam/bo/ExamApplyAddBo.java

@@ -4,6 +4,7 @@ import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
+import java.util.List;
 
 
 /**
@@ -70,5 +71,11 @@ public class ExamApplyAddBo {
     @ApiModelProperty("考场性质:1普通场,2专场 ")
     private Integer applyNature;
 
+    @ApiModelProperty("专场学员资料地址 ")
+    private String natureUrl;
+
+    @ApiModelProperty("专场预约学员")
+    private List<ExamApplyUserImportBo> applyUsers;
+
 
 }

+ 8 - 2
zhongzheng-system/src/main/java/com/zhongzheng/modules/exam/bo/ExamApplyEditBo.java

@@ -2,9 +2,9 @@ package com.zhongzheng.modules.exam.bo;
 
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
-import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;
-import java.util.Date;
+
+import java.util.List;
 
 
 /**
@@ -77,4 +77,10 @@ public class ExamApplyEditBo {
     @ApiModelProperty("考场性质:1普通场,2专场 ")
     private Integer applyNature;
 
+    @ApiModelProperty("专场学员资料地址 ")
+    private String natureUrl;
+
+    @ApiModelProperty("专场学员")
+    private List<ExamApplyUserImportBo> applyUsers;
+
 }

+ 4 - 5
zhongzheng-system/src/main/java/com/zhongzheng/modules/exam/bo/ExamApplyQueryBo.java

@@ -1,17 +1,13 @@
 package com.zhongzheng.modules.exam.bo;
 
 import com.zhongzheng.common.annotation.Excel;
+import com.zhongzheng.common.core.domain.BaseEntity;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 
-import java.util.Date;
 import java.util.List;
-import java.util.Map;
-import java.util.HashMap;
-
-import com.zhongzheng.common.core.domain.BaseEntity;
 
 /**
  * 考试安排分页查询对象 exam_apply
@@ -116,4 +112,7 @@ public class ExamApplyQueryBo extends BaseEntity {
 
 	@ApiModelProperty("关键词")
 	private String searchKey;
+
+	@ApiModelProperty("数据IDs")
+	private List<Long> ids;
 }

+ 39 - 0
zhongzheng-system/src/main/java/com/zhongzheng/modules/exam/bo/ExamApplySendmailBo.java

@@ -0,0 +1,39 @@
+package com.zhongzheng.modules.exam.bo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.List;
+
+
+/**
+ * 考试安排添加对象 exam_apply
+ *
+ * @author ruoyi
+ * @date 2021-12-07
+ */
+@Data
+@ApiModel("考试安排添加对象")
+public class ExamApplySendmailBo {
+
+    @ApiModelProperty("ids")
+    private List<Long> ids;
+
+    @ApiModelProperty("邮件标题")
+    private String mailName;
+
+    @ApiModelProperty("发送人地址")
+    private List<String> mailAttrList;
+
+    @ApiModelProperty("附件地址")
+    private String mailUrl;
+
+    @ApiModelProperty("附件标题")
+    private String mailUrlName;
+
+    @ApiModelProperty("内容")
+    private String mailText;
+
+
+}

+ 26 - 0
zhongzheng-system/src/main/java/com/zhongzheng/modules/exam/bo/ExamApplyUserImportBo.java

@@ -0,0 +1,26 @@
+package com.zhongzheng.modules.exam.bo;
+
+import cn.afterturn.easypoi.excel.annotation.Excel;
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+
+import java.io.Serializable;
+
+
+/**
+ * 考试安排添加对象 exam_apply
+ *
+ * @author ruoyi
+ * @date 2021-12-07
+ */
+@Data
+@ApiModel("考试安排添加对象")
+public class ExamApplyUserImportBo implements Serializable {
+
+    @Excel(name = "姓名")
+    private String userName;
+
+    @Excel(name = "身份证号")
+    private String idCard;
+
+}

+ 2 - 0
zhongzheng-system/src/main/java/com/zhongzheng/modules/exam/domain/ExamApply.java

@@ -61,4 +61,6 @@ private static final long serialVersionUID=1L;
     private Integer reportStatus;
     /** 考场性质:1普通场,2专场 */
     private Integer applyNature;
+    /** 专场学员资料地址 */
+    private String natureUrl;
 }

+ 7 - 5
zhongzheng-system/src/main/java/com/zhongzheng/modules/exam/domain/ExamApplySiteTime.java

@@ -1,14 +1,14 @@
 package com.zhongzheng.modules.exam.domain;
 
-import com.baomidou.mybatisplus.annotation.*;
-import io.swagger.annotations.ApiModelProperty;
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 import lombok.experimental.Accessors;
+
 import java.io.Serializable;
-import java.util.Date;
-import java.math.BigDecimal;
-import com.zhongzheng.common.annotation.Excel;
 
 /**
  * 考试安排地点绑定时间对象 exam_apply_site_time
@@ -41,4 +41,6 @@ private static final long serialVersionUID=1L;
     /** 更新时间 */
     @TableField(fill = FieldFill.INSERT_UPDATE)
     private Long updateTime;
+    /** 是否发送邮件:0未发送 1已发送 */
+    private Integer sendmail;
 }

+ 5 - 0
zhongzheng-system/src/main/java/com/zhongzheng/modules/exam/mapper/ExamApplyMapper.java

@@ -5,6 +5,7 @@ import com.zhongzheng.modules.exam.bo.ExamNumberGoodsQueryBo;
 import com.zhongzheng.modules.exam.domain.ExamApply;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.zhongzheng.modules.exam.vo.*;
+import com.zhongzheng.modules.user.vo.UserStudyRecordExport;
 import org.apache.ibatis.annotations.Param;
 
 import java.util.List;
@@ -70,4 +71,8 @@ public interface ExamApplyMapper extends BaseMapper<ExamApply> {
     Long sitePeopleNum(ExamApplyQueryBo bo);
 
     String siteTimeJson(ExamApplyQueryBo bo);
+
+    List<ExamApplyPlaceVo> getPlaceList(ExamApplyQueryBo bo);
+
+    List<UserStudyRecordExport> getUserStudyRecord(@Param("orderGoodsId") Long orderGoodsId,@Param("userId") Long userId);
 }

+ 10 - 0
zhongzheng-system/src/main/java/com/zhongzheng/modules/exam/service/IExamApplyService.java

@@ -74,4 +74,14 @@ public interface IExamApplyService extends IService<ExamApply> {
 	 * @return java.util.List<com.zhongzheng.modules.goods.vo.GoodsVo>
 	 */
 	TableDataInfo<GoodsVo> getRecommendGoodsList(ExamRecommendGoodsQueryBo bo);
+
+	boolean saveExamApply(ExamApplyAddBo addBo);
+
+	boolean editExamApply(ExamApplyEditBo editBo);
+
+    List<ExamApplyPlaceVo> getPlaceList(ExamApplyQueryBo bo);
+
+    String getSendmailUrl(List<Long> ids);
+
+    Boolean sendmail(ExamApplySendmailBo bo);
 }

+ 553 - 65
zhongzheng-system/src/main/java/com/zhongzheng/modules/exam/service/impl/ExamApplyServiceImpl.java

@@ -2,45 +2,74 @@ package com.zhongzheng.modules.exam.service.impl;
 
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.lang.Validator;
-import cn.hutool.core.util.StrUtil;
-import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
 import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.github.pagehelper.Page;
 import com.zhongzheng.common.core.page.TableDataInfo;
 import com.zhongzheng.common.exception.CustomException;
+import com.zhongzheng.common.type.EncryptHandler;
 import com.zhongzheng.common.utils.DateUtils;
+import com.zhongzheng.common.utils.JavaMailUtils;
 import com.zhongzheng.common.utils.ServletUtils;
+import com.zhongzheng.common.utils.file.FileUtils;
+import com.zhongzheng.common.utils.poi.EasyPoiUtil;
+import com.zhongzheng.common.utils.poi.ExcelUtil;
 import com.zhongzheng.modules.activity.domain.ActivityRecommend;
-import com.zhongzheng.modules.activity.domain.ActivityRecommendGoods;
 import com.zhongzheng.modules.activity.service.IActivityRecommendGoodsService;
 import com.zhongzheng.modules.activity.service.IActivityRecommendService;
-import com.zhongzheng.modules.activity.vo.ActivityRecommendGoodsVo;
+import com.zhongzheng.modules.alioss.bo.OssRequest;
+import com.zhongzheng.modules.alioss.service.OssService;
 import com.zhongzheng.modules.bank.domain.Exam;
 import com.zhongzheng.modules.bank.domain.QuestionBusiness;
 import com.zhongzheng.modules.bank.service.IExamService;
 import com.zhongzheng.modules.bank.service.IQuestionBusinessService;
-import com.zhongzheng.modules.course.domain.Course;
 import com.zhongzheng.modules.exam.bo.*;
-import com.zhongzheng.modules.exam.domain.ExamBefore;
+import com.zhongzheng.modules.exam.domain.ExamApply;
+import com.zhongzheng.modules.exam.domain.ExamApplySite;
+import com.zhongzheng.modules.exam.domain.ExamApplySiteTime;
+import com.zhongzheng.modules.exam.domain.ExamApplyUser;
+import com.zhongzheng.modules.exam.mapper.ExamApplyMapper;
+import com.zhongzheng.modules.exam.service.IExamApplyService;
+import com.zhongzheng.modules.exam.service.IExamApplySiteService;
+import com.zhongzheng.modules.exam.service.IExamApplySiteTimeService;
+import com.zhongzheng.modules.exam.service.IExamApplyUserService;
 import com.zhongzheng.modules.exam.vo.*;
-import com.zhongzheng.modules.goods.domain.Goods;
 import com.zhongzheng.modules.goods.service.IGoodsService;
 import com.zhongzheng.modules.goods.vo.GoodsVo;
-import com.zhongzheng.modules.user.vo.CalendarStudyVo;
-import io.swagger.annotations.ApiModelProperty;
+import com.zhongzheng.modules.system.domain.SysConfig;
+import com.zhongzheng.modules.system.domain.SysTenant;
+import com.zhongzheng.modules.system.service.ISysConfigService;
+import com.zhongzheng.modules.system.service.ISysTenantService;
+import com.zhongzheng.modules.user.domain.User;
+import com.zhongzheng.modules.user.domain.UserSubscribe;
+import com.zhongzheng.modules.user.service.IUserService;
+import com.zhongzheng.modules.user.service.IUserSubscribeService;
+import com.zhongzheng.modules.user.vo.UserStudyRecordExport;
+import org.apache.commons.io.IOUtils;
+import org.apache.http.entity.ContentType;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.mock.web.MockMultipartFile;
 import org.springframework.stereotype.Service;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.toolkit.Wrappers;
-import com.github.pagehelper.Page;
-import com.zhongzheng.modules.exam.domain.ExamApply;
-import com.zhongzheng.modules.exam.mapper.ExamApplyMapper;
-import com.zhongzheng.modules.exam.service.IExamApplyService;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
 
+import javax.activation.DataHandler;
+import javax.mail.*;
+import javax.mail.internet.*;
+import javax.mail.util.ByteArrayDataSource;
+import java.io.*;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.*;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 /**
  * 考试安排Service业务层处理
@@ -61,17 +90,33 @@ public class ExamApplyServiceImpl extends ServiceImpl<ExamApplyMapper, ExamApply
     private IActivityRecommendGoodsService iActivityRecommendGoodsService;
     @Autowired
     private IGoodsService iGoodsService;
+    @Autowired
+    private IUserService iUserService;
+    @Autowired
+    private ISysTenantService sysTenantService;
+    @Autowired
+    private IExamApplyUserService iExamApplyUserService;
+    @Autowired
+    private IExamApplySiteService iExamApplySiteService;
+    @Autowired
+    private IUserSubscribeService iUserSubscribeService;
+    @Autowired
+    private IExamApplySiteTimeService iExamApplySiteTimeService;
+    @Autowired
+    private OssService ossService;
+    @Autowired
+    private ISysConfigService iSysConfigService;
 
     @Override
-    public ExamApplyVo queryById(Long applyId){
+    public ExamApplyVo queryById(Long applyId) {
         ExamApplyVo examApplyVo = baseMapper.selectByApply(applyId);
         List<ExamNumberGoodsVo> examNumberGoodsVos = baseMapper.examNumberGoodsVos(applyId);
-        List<ExamApplySiteVo> examApplySite = baseMapper.addressExam(applyId,1L);
+        List<ExamApplySiteVo> examApplySite = baseMapper.addressExam(applyId, 1L);
         for (ExamApplySiteVo examApplySiteVo : examApplySite) {
             List<ExamApplySiteTimeVo> examApplySiteTime = baseMapper.addressTimeExam(examApplySiteVo.getId());
             examApplySiteVo.setExamApplySiteTime(examApplySiteTime);
         }
-        List<ExamApplySiteVo> examApplySiteTrain = baseMapper.addressExam(applyId,2L);
+        List<ExamApplySiteVo> examApplySiteTrain = baseMapper.addressExam(applyId, 2L);
         for (ExamApplySiteVo examApplySiteVo : examApplySiteTrain) {
             List<ExamApplySiteTimeVo> examApplySiteTime = baseMapper.addressTimeExam(examApplySiteVo.getId());
             examApplySiteVo.setExamApplySiteTime(examApplySiteTime);
@@ -79,6 +124,17 @@ public class ExamApplyServiceImpl extends ServiceImpl<ExamApplyMapper, ExamApply
         examApplyVo.setExamNumberGoods(examNumberGoodsVos);
         examApplyVo.setExamApplySite(examApplySite);
         examApplyVo.setExamApplySiteTrain(examApplySiteTrain);
+        List<ExamApplyUser> applyUsers = iExamApplyUserService.list(new LambdaQueryWrapper<ExamApplyUser>().eq(ExamApplyUser::getApplyId, applyId));
+        if (CollectionUtils.isNotEmpty(applyUsers)) {
+            List<ExamApplyUserImportBo> collect = applyUsers.stream().map(item -> {
+                User user = iUserService.getById(item.getUserId());
+                ExamApplyUserImportBo bo = new ExamApplyUserImportBo();
+                bo.setUserName(user.getRealname());
+                bo.setIdCard(EncryptHandler.decrypt(user.getIdCard()));
+                return bo;
+            }).collect(Collectors.toList());
+            examApplyVo.setApplyUsers(collect);
+        }
         return examApplyVo;
     }
 
@@ -90,7 +146,7 @@ public class ExamApplyServiceImpl extends ServiceImpl<ExamApplyMapper, ExamApply
             Calendar calendar = Calendar.getInstance();
             calendar.setTimeZone(TimeZone.getTimeZone("GMT+8:00"));
             calendar.setTimeInMillis(System.currentTimeMillis());
-            if ((examApplyVo.getApplyEndTime()) < calendar.getTimeInMillis()/1000){
+            if ((examApplyVo.getApplyEndTime()) < calendar.getTimeInMillis() / 1000) {
                 ExamApply update = new ExamApply();
                 update.setApplyId(examApplyVo.getApplyId());
                 update.setStatus(2);
@@ -103,19 +159,19 @@ public class ExamApplyServiceImpl extends ServiceImpl<ExamApplyMapper, ExamApply
     }
 
     /**
-    * 实体类转化成视图对象
-    *
-    * @param collection 实体类集合
-    * @return
-    */
+     * 实体类转化成视图对象
+     *
+     * @param collection 实体类集合
+     * @return
+     */
     private List<ExamApplyVo> entity2Vo(Collection<ExamApply> collection) {
         List<ExamApplyVo> voList = collection.stream()
                 .map(any -> BeanUtil.toBean(any, ExamApplyVo.class))
                 .collect(Collectors.toList());
         if (collection instanceof Page) {
-            Page<ExamApply> page = (Page<ExamApply>)collection;
+            Page<ExamApply> page = (Page<ExamApply>) collection;
             Page<ExamApplyVo> pageVo = new Page<>();
-            BeanUtil.copyProperties(page,pageVo);
+            BeanUtil.copyProperties(page, pageVo);
             pageVo.addAll(voList);
             voList = pageVo;
         }
@@ -129,21 +185,72 @@ public class ExamApplyServiceImpl extends ServiceImpl<ExamApplyMapper, ExamApply
         add.setCode(ServletUtils.getEncoded("KSAP"));
         add.setCreateTime(DateUtils.getNowTime());
         add.setUpdateTime(DateUtils.getNowTime());
-        return this.save(add);
+        this.save(add);
+
+        if (StringUtils.isNotBlank(bo.getNatureUrl())) {
+            try {
+                InputStream inputStream = ossService.getStreamByObject(bo.getNatureUrl());
+                //学员资料解析
+                List<ExamApplyUserImportBo> applyUsers = EasyPoiUtil.importExcel(createMultipartFile(inputStream), 0, 1, ExamApplyUserImportBo.class);
+                //专场预约学员
+                List<ExamApplyUser> applyUserList = applyUsers.stream().filter(x -> {
+                    int count = iUserService.count(new LambdaQueryWrapper<User>()
+                            .eq(User::getIdCard, EncryptHandler.encrypt(x.getIdCard())));
+                    return count > 0;
+                }).map(item -> {
+                    User user = iUserService.getOne(new LambdaQueryWrapper<User>()
+                            .eq(User::getIdCard, EncryptHandler.encrypt(item.getIdCard())));
+                    ExamApplyUser applyUser = new ExamApplyUser();
+                    applyUser.setApplyId(add.getApplyId());
+                    applyUser.setUserId(user.getUserId());
+                    applyUser.setCreateTime(DateUtils.getNowTime());
+                    applyUser.setUpdateTime(DateUtils.getNowTime());
+                    return applyUser;
+                }).collect(Collectors.toList());
+
+                iExamApplyUserService.saveBatch(applyUserList);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        return true;
+    }
+
+
+    private MultipartFile createMultipartFile(InputStream inputStream) throws IOException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        byte[] buffer = new byte[1024];
+        int len;
+        byte[] dataBytes;
+        while ((len = inputStream.read(buffer)) != -1) {
+            baos.write(buffer, 0, len);
+        }
+        baos.flush();
+        byte[] bytes = baos.toByteArray();
+
+        MultipartFile multipartFile = new MockMultipartFile(
+                "file",
+                "filename",
+                ContentType.APPLICATION_OCTET_STREAM.toString(),
+                new ByteArrayInputStream(bytes)
+        );
+
+        return multipartFile;
+
     }
 
     @Override
     public Boolean updateByEditBo(ExamApplyEditBo bo) {
         ExamApply update = BeanUtil.toBean(bo, ExamApply.class);
-        if (bo.getStatus() == 1){
+        if (bo.getStatus() == 1) {
             //判断是否有设置商品
             Long contGoodsId = baseMapper.countGoods(bo.getApplyId());
-            if (contGoodsId < 1){
+            if (contGoodsId < 1) {
                 throw new CustomException("请先设置适用商品");
             }
             //判断是否有设置考试地点和时间
-            List<ExamApplySiteVo> examApplySite = baseMapper.addressExam(bo.getApplyId(),1L);
-            if (CollectionUtils.isEmpty(examApplySite)){
+            List<ExamApplySiteVo> examApplySite = baseMapper.addressExam(bo.getApplyId(), 1L);
+            if (CollectionUtils.isEmpty(examApplySite)) {
                 throw new CustomException("请先设置考试地点和时间");
             }
             //考试计划内的商品是否被其他计划使用
@@ -161,16 +268,48 @@ public class ExamApplyServiceImpl extends ServiceImpl<ExamApplyMapper, ExamApply
                 throw new RuntimeException(goodsName+"已被其他进行中的考试计划使用,请修改,再启用");
             }*/
         }
-        if(bo.getStatus()==-1){
+        if (bo.getStatus() == -1) {
             ExamApplyQueryBo queryBo = new ExamApplyQueryBo();
             queryBo.setApplyId(bo.getApplyId());
-            if(countApplySubscribe(queryBo)>0){
+            if (countApplySubscribe(queryBo) > 0) {
                 throw new RuntimeException("该考试计划存在预约数据,无法删除");
             }
         }
         validEntityBeforeSave(update);
         update.setUpdateTime(DateUtils.getNowTime());
-        return this.updateById(update);
+        this.updateById(update);
+
+        if (update.getApplyNature() == 2) {
+            //专场预约学员
+            iExamApplyUserService.remove(new LambdaQueryWrapper<ExamApplyUser>().eq(ExamApplyUser::getApplyId, update.getApplyId()));
+            if (StringUtils.isNotBlank(bo.getNatureUrl())) {
+                try {
+                    InputStream inputStream = ossService.getStreamByObject(bo.getNatureUrl());
+                    //学员资料解析
+                    List<ExamApplyUserImportBo> applyUsers = EasyPoiUtil.importExcel(createMultipartFile(inputStream), 0, 1, ExamApplyUserImportBo.class);
+                    //专场预约学员
+                    List<ExamApplyUser> applyUserList = applyUsers.stream().filter(x -> {
+                        int count = iUserService.count(new LambdaQueryWrapper<User>()
+                                .eq(User::getIdCard, EncryptHandler.encrypt(x.getIdCard())));
+                        return count > 0;
+                    }).map(item -> {
+                        User user = iUserService.getOne(new LambdaQueryWrapper<User>()
+                                .eq(User::getIdCard, EncryptHandler.encrypt(item.getIdCard())));
+                        ExamApplyUser applyUser = new ExamApplyUser();
+                        applyUser.setApplyId(update.getApplyId());
+                        applyUser.setUserId(user.getUserId());
+                        applyUser.setCreateTime(DateUtils.getNowTime());
+                        applyUser.setUpdateTime(DateUtils.getNowTime());
+                        return applyUser;
+                    }).collect(Collectors.toList());
+
+                    iExamApplyUserService.saveBatch(applyUserList);
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        return true;
     }
 
     /**
@@ -178,16 +317,16 @@ public class ExamApplyServiceImpl extends ServiceImpl<ExamApplyMapper, ExamApply
      *
      * @param entity 实体类数据
      */
-    private void validEntityBeforeSave(ExamApply entity){
+    private void validEntityBeforeSave(ExamApply entity) {
         //TODO 做一些数据校验,如唯一约束
-        if(checkNameUnique(entity)){
+        if (checkNameUnique(entity)) {
             throw new CustomException("名称重复");
         }
     }
 
     @Override
     public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
-        if(isValid){
+        if (isValid) {
             //TODO 做一些业务上的校验,判断是否需要校验
         }
         return this.removeByIds(ids);
@@ -201,7 +340,7 @@ public class ExamApplyServiceImpl extends ServiceImpl<ExamApplyMapper, ExamApply
 
     @Override
     public List<ExamApplySiteVo> getSiteInfo(ExamApplyQueryBo bo) {
-        List<ExamApplySiteVo> examApplySite = baseMapper.addressExam(bo.getApplyId(),bo.getAddressStatus());
+        List<ExamApplySiteVo> examApplySite = baseMapper.addressExam(bo.getApplyId(), bo.getAddressStatus());
         for (ExamApplySiteVo examApplySiteVo : examApplySite) {
             List<ExamApplySiteTimeVo> examApplySiteTime = baseMapper.addressTimeExam(examApplySiteVo.getId());
             examApplySiteVo.setExamApplySiteTime(examApplySiteTime);
@@ -218,7 +357,7 @@ public class ExamApplyServiceImpl extends ServiceImpl<ExamApplyMapper, ExamApply
         //初始化需要得到的数组
         Long[] array = new Long[examNumberGoodsVos.size()];
         //使用for循环得到数组
-        for(int i = 0; i < examNumberGoodsVos.size();i++){
+        for (int i = 0; i < examNumberGoodsVos.size(); i++) {
             array[i] = examNumberGoodsVos.get(i).getGoodsId();
         }
         examApplyVo.setExamNumberGoods(examNumberGoodsVos);
@@ -230,22 +369,22 @@ public class ExamApplyServiceImpl extends ServiceImpl<ExamApplyMapper, ExamApply
     public ExamUserApplyVo subscribe(ExamApplyQueryBo bo) {
         //查询学时通过情况
         Integer countGradePeriod = baseMapper.countGradePeriod(bo);
-        if (countGradePeriod < 1){
+        if (countGradePeriod < 1) {
             throw new CustomException("学时审核未通过,不可以报名参加考试");
         }
         //查询考试通过情况
         Integer countSubscribe = baseMapper.countSubscribe(bo);
-        if (countSubscribe > 0){
+        if (countSubscribe > 0) {
             throw new CustomException("考试已通过,不需要重考");
         }
         //查询是否已预约考试
         Integer countHaveSubscribe = baseMapper.countHaveSubscribe(bo);
-        if (countHaveSubscribe > 0){
+        if (countHaveSubscribe > 0) {
             throw new CustomException("您所报考的专业,已经预约成功,您可在“我的-我的考试预约”中查询详情");
         }
         //查询是否有考试计划
         ExamUserApplyVo examUserApplyVo = baseMapper.selectExamUserApplyVo(bo);
-        if (examUserApplyVo == null){
+        if (examUserApplyVo == null) {
             throw new CustomException("商品无考试计划,无需预约考试");
         }
         return examUserApplyVo;
@@ -255,20 +394,20 @@ public class ExamApplyServiceImpl extends ServiceImpl<ExamApplyMapper, ExamApply
     public Long subscribeNext(ExamApplyQueryBo bo) {
         //查看是否有无考试记录
         Integer countHaveSubscribeNext = baseMapper.countHaveSubscribeNext(bo);
-        if (bo.getApplyStatus().equals(2) && countHaveSubscribeNext < 1){
+        if (bo.getApplyStatus().equals(2) && countHaveSubscribeNext < 1) {
             throw new CustomException("系统检索到您不符合【补考学员】的报名条件,请重新选择!");
         }
-        if (bo.getApplyStatus().equals(1) && countHaveSubscribeNext > 0){
+        if (bo.getApplyStatus().equals(1) && countHaveSubscribeNext > 0) {
             throw new CustomException("系统检索到您不符合【非补考学员】的报名条件,请重新选择!");
         }
         //查询剩余考试次数
         Integer residueSubscribeNext = baseMapper.residueSubscribeNext(bo);
-        if (residueSubscribeNext == null || residueSubscribeNext < 1){
+        if (residueSubscribeNext == null || residueSubscribeNext < 1) {
             return 3L;
         }
         //查询有无考培
         Integer beforeSubscribeNext = baseMapper.beforeSubscribeNext(bo);
-        if (beforeSubscribeNext > 0){
+        if (beforeSubscribeNext > 0) {
             return 1L;
         }
         return 2L;
@@ -277,7 +416,7 @@ public class ExamApplyServiceImpl extends ServiceImpl<ExamApplyMapper, ExamApply
     @Override
     public List<ExamUserApplySiteVo> subscribeApplySite(ExamApplyQueryBo bo) {
         //查看地址
-        List<ExamUserApplySiteVo> examUserApplySiteVos= baseMapper.subscribeApplySite(bo);
+        List<ExamUserApplySiteVo> examUserApplySiteVos = baseMapper.subscribeApplySite(bo);
         //获得详细地址查看是否满员
         for (ExamUserApplySiteVo examUserApplySiteVo : examUserApplySiteVos) {
             List<ExamUserApplySiteTimeVo> examApplySiteTime = baseMapper.subscribeApplySiteTime(examUserApplySiteVo.getId());
@@ -285,8 +424,8 @@ public class ExamApplyServiceImpl extends ServiceImpl<ExamApplyMapper, ExamApply
                 List<ExamApplySiteTimeTwoVo> examApplySiteTimeTwoVos = JSONObject.parseArray(examUserApplySiteTimeVo.getSiteTime(), ExamApplySiteTimeTwoVo.class);
                 for (ExamApplySiteTimeTwoVo examApplySiteTimeTwoVo : examApplySiteTimeTwoVos) {
                     examApplySiteTimeTwoVo.setRegistration(baseMapper.registration(examUserApplySiteVo.getApplyId(), examUserApplySiteTimeVo.getExamTime()
-                            ,examUserApplySiteVo.getSiteAddress(),examApplySiteTimeTwoVo.getStartTime(),examApplySiteTimeTwoVo.getEndTime()));
-                    examApplySiteTimeTwoVo.setStatus(baseMapper.userStatus(bo.getUserId(),examUserApplySiteTimeVo.getExamTime(),examApplySiteTimeTwoVo.getStartTime(),examApplySiteTimeTwoVo.getEndTime()));
+                            , examUserApplySiteVo.getSiteAddress(), examApplySiteTimeTwoVo.getStartTime(), examApplySiteTimeTwoVo.getEndTime()));
+                    examApplySiteTimeTwoVo.setStatus(baseMapper.userStatus(bo.getUserId(), examUserApplySiteTimeVo.getExamTime(), examApplySiteTimeTwoVo.getStartTime(), examApplySiteTimeTwoVo.getEndTime()));
                 }
                 examUserApplySiteTimeVo.setExamApplySiteTimeTwoVo(examApplySiteTimeTwoVos);
             }
@@ -298,7 +437,7 @@ public class ExamApplyServiceImpl extends ServiceImpl<ExamApplyMapper, ExamApply
     @Override
     public List<ExamUserApplySiteVo> subscribeApplySiteTrain(ExamApplyQueryBo bo) {
         //查看地址
-        List<ExamUserApplySiteVo> examUserApplySiteVos= baseMapper.subscribeApplySiteTrain(bo);
+        List<ExamUserApplySiteVo> examUserApplySiteVos = baseMapper.subscribeApplySiteTrain(bo);
         //获得详细地址查看是否满员
         for (ExamUserApplySiteVo examUserApplySiteVo : examUserApplySiteVos) {
             List<ExamUserApplySiteTimeVo> examApplySiteTime = baseMapper.subscribeApplySiteTime(examUserApplySiteVo.getId());
@@ -306,8 +445,8 @@ public class ExamApplyServiceImpl extends ServiceImpl<ExamApplyMapper, ExamApply
                 List<ExamApplySiteTimeTwoVo> examApplySiteTimeTwoVos = JSONObject.parseArray(examUserApplySiteTimeVo.getSiteTime(), ExamApplySiteTimeTwoVo.class);
                 for (ExamApplySiteTimeTwoVo examApplySiteTimeTwoVo : examApplySiteTimeTwoVos) {
                     examApplySiteTimeTwoVo.setRegistration(baseMapper.registrationTrain(examUserApplySiteVo.getApplyId(), examUserApplySiteTimeVo.getExamTime()
-                            ,examUserApplySiteVo.getSiteAddress(),examApplySiteTimeTwoVo.getStartTime(),examApplySiteTimeTwoVo.getEndTime()));
-                    examApplySiteTimeTwoVo.setStatus(baseMapper.userStatusTrain(bo.getUserId(),examUserApplySiteTimeVo.getExamTime(),examApplySiteTimeTwoVo.getStartTime(),examApplySiteTimeTwoVo.getEndTime()));
+                            , examUserApplySiteVo.getSiteAddress(), examApplySiteTimeTwoVo.getStartTime(), examApplySiteTimeTwoVo.getEndTime()));
+                    examApplySiteTimeTwoVo.setStatus(baseMapper.userStatusTrain(bo.getUserId(), examUserApplySiteTimeVo.getExamTime(), examApplySiteTimeTwoVo.getStartTime(), examApplySiteTimeTwoVo.getEndTime()));
                 }
                 examUserApplySiteTimeVo.setExamApplySiteTimeTwoVo(examApplySiteTimeTwoVos);
             }
@@ -325,7 +464,7 @@ public class ExamApplyServiceImpl extends ServiceImpl<ExamApplyMapper, ExamApply
     public TableDataInfo<GoodsVo> getRecommendGoodsList(ExamRecommendGoodsQueryBo bo) {
         TableDataInfo tableDataInfo = new TableDataInfo();
         Exam exam = iExamService.getById(bo.getExamId());
-        if (ObjectUtils.isNull(exam)){
+        if (ObjectUtils.isNull(exam)) {
 //            throw new CustomException("获取试卷信息失败");
             return new TableDataInfo<>();
         }
@@ -333,23 +472,23 @@ public class ExamApplyServiceImpl extends ServiceImpl<ExamApplyMapper, ExamApply
         QuestionBusiness business = iQuestionBusinessService.getOne(new LambdaQueryWrapper<QuestionBusiness>()
                 .eq(QuestionBusiness::getMajorId, bo.getExamId())
                 .eq(QuestionBusiness::getType, 2));
-        if (ObjectUtils.isNull(business)){
+        if (ObjectUtils.isNull(business)) {
             throw new CustomException("获取试卷业务层次信息失败");
         }
-        Integer platform = ObjectUtils.isNull(bo.getPlatform())?1:bo.getPlatform();
+        Integer platform = ObjectUtils.isNull(bo.getPlatform()) ? 1 : bo.getPlatform();
         //获取推荐位信息
         List<ActivityRecommend> list = iActivityRecommendService.list(new LambdaQueryWrapper<ActivityRecommend>()
                 .eq(ActivityRecommend::getEducationTypeId, business.getEducationTypeId())
                 .eq(ActivityRecommend::getBusinessId, business.getBusinessId())
-                .eq(ActivityRecommend::getPlatform,platform)
+                .eq(ActivityRecommend::getPlatform, platform)
                 .eq(ActivityRecommend::getType, 1)
-                .eq(ActivityRecommend::getStatus,1));
-        if (CollectionUtils.isEmpty(list)){
+                .eq(ActivityRecommend::getStatus, 1));
+        if (CollectionUtils.isEmpty(list)) {
             return tableDataInfo;
         }
         ActivityRecommend activityRecommend = list.stream().findFirst().get();
         com.baomidou.mybatisplus.extension.plugins.pagination.Page<GoodsVo> page =
-                iActivityRecommendGoodsService.getGoodsListByPage(new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(bo.getPageNum(), bo.getPageSize()),activityRecommend.getRecommendId());
+                iActivityRecommendGoodsService.getGoodsListByPage(new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(bo.getPageNum(), bo.getPageSize()), activityRecommend.getRecommendId());
 
 //        if (CollectionUtils.isEmpty(page.getRecords())){
 //            return tableDataInfo;
@@ -361,15 +500,364 @@ public class ExamApplyServiceImpl extends ServiceImpl<ExamApplyMapper, ExamApply
         return tableDataInfo;
     }
 
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean saveExamApply(ExamApplyAddBo bo) {
+        ExamApply add = BeanUtil.toBean(bo, ExamApply.class);
+        validEntityBeforeSave(add);
+        add.setCode(ServletUtils.getEncoded("KSAP"));
+        add.setCreateTime(DateUtils.getNowTime());
+        add.setUpdateTime(DateUtils.getNowTime());
+        this.save(add);
+        if (CollectionUtils.isNotEmpty(bo.getApplyUsers())) {
+            //专场预约学员
+            List<ExamApplyUser> applyUserList = bo.getApplyUsers().stream().filter(x -> {
+                int count = iUserService.count(new LambdaQueryWrapper<User>()
+                        .eq(User::getIdCard, EncryptHandler.encrypt(x.getIdCard())));
+                return count > 0;
+            }).map(item -> {
+                User user = iUserService.getOne(new LambdaQueryWrapper<User>()
+                        .eq(User::getIdCard, EncryptHandler.encrypt(item.getIdCard())));
+                ExamApplyUser applyUser = new ExamApplyUser();
+                applyUser.setApplyId(add.getApplyId());
+                applyUser.setUserId(user.getUserId());
+                applyUser.setCreateTime(DateUtils.getNowTime());
+                applyUser.setUpdateTime(DateUtils.getNowTime());
+                return applyUser;
+            }).collect(Collectors.toList());
+
+            iExamApplyUserService.saveBatch(applyUserList);
+        }
+        return true;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean editExamApply(ExamApplyEditBo bo) {
+        ExamApply update = BeanUtil.toBean(bo, ExamApply.class);
+        if (bo.getStatus() == 1) {
+            //判断是否有设置商品
+            Long contGoodsId = baseMapper.countGoods(bo.getApplyId());
+            if (contGoodsId < 1) {
+                throw new CustomException("请先设置适用商品");
+            }
+            //判断是否有设置考试地点和时间
+            List<ExamApplySiteVo> examApplySite = baseMapper.addressExam(bo.getApplyId(), 1L);
+            if (CollectionUtils.isEmpty(examApplySite)) {
+                throw new CustomException("请先设置考试地点和时间");
+            }
+        }
+        if (bo.getStatus() == -1) {
+            ExamApplyQueryBo queryBo = new ExamApplyQueryBo();
+            queryBo.setApplyId(bo.getApplyId());
+            if (countApplySubscribe(queryBo) > 0) {
+                throw new RuntimeException("该考试计划存在预约数据,无法删除");
+            }
+        }
+        validEntityBeforeSave(update);
+        update.setUpdateTime(DateUtils.getNowTime());
+        this.updateById(update);
+
+        if (update.getApplyNature() == 2) {
+            //专场预约学员
+            iExamApplyUserService.remove(new LambdaQueryWrapper<ExamApplyUser>().eq(ExamApplyUser::getApplyId, update.getApplyId()));
+            if (CollectionUtils.isNotEmpty(bo.getApplyUsers())) {
+                //专场预约学员
+                List<ExamApplyUser> applyUserList = bo.getApplyUsers().stream().filter(x -> {
+                    int count = iUserService.count(new LambdaQueryWrapper<User>()
+                            .eq(User::getIdCard, EncryptHandler.encrypt(x.getIdCard())));
+                    return count > 0;
+                }).map(item -> {
+                    User user = iUserService.getOne(new LambdaQueryWrapper<User>()
+                            .eq(User::getIdCard, EncryptHandler.encrypt(item.getIdCard())));
+                    ExamApplyUser applyUser = new ExamApplyUser();
+                    applyUser.setApplyId(update.getApplyId());
+                    applyUser.setUserId(user.getUserId());
+                    applyUser.setCreateTime(DateUtils.getNowTime());
+                    applyUser.setUpdateTime(DateUtils.getNowTime());
+                    return applyUser;
+                }).collect(Collectors.toList());
+                iExamApplyUserService.saveBatch(applyUserList);
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public List<ExamApplyPlaceVo> getPlaceList(ExamApplyQueryBo bo) {
+        List<ExamApplyPlaceVo> voList = baseMapper.getPlaceList(bo);
+        if (CollectionUtils.isEmpty(voList)) {
+            return new ArrayList<>();
+        }
+        voList.forEach(item -> {
+            List<ExamApplySiteTimeTwoVo> examApplySiteTimeTwoVos = JSONArray.parseArray(item.getSiteTime(), ExamApplySiteTimeTwoVo.class);
+            if (ObjectUtils.isNotNull(examApplySiteTimeTwoVos)) {
+                ExamApplySiteTimeTwoVo timeTwoVo = examApplySiteTimeTwoVos.stream().findFirst().orElse(null);
+                //预约人数
+                int count = iUserSubscribeService.count(new LambdaQueryWrapper<UserSubscribe>()
+                        .eq(UserSubscribe::getApplyId, item.getApplyId())
+                        .eq(UserSubscribe::getSiteId, item.getApplySiteId())
+                        .eq(UserSubscribe::getApplySiteStartTime, timeTwoVo.getStartTime())
+                        .eq(UserSubscribe::getApplySiteEndTime, timeTwoVo.getEndTime()));
+                item.setPeople(count);
+                item.setMayNum(timeTwoVo.getNum());
+                if (ObjectUtils.isNotNull(item.getApplyDate())) {
+                    String time = DateUtils.timestampToDateFormatMonth(item.getApplyDate());
+                    String format = String.format("%s(%s-%s)", time, timeTwoVo.getStartTime(), timeTwoVo.getEndTime());
+                    item.setApplyTime(format);
+                }
+            }
+        });
+        return voList;
+    }
+
+    @Override
+    public String getSendmailUrl(List<Long> ids) {
+        List<ExamApplySiteTime> siteTimes = iExamApplySiteTimeService.listByIds(ids);
+        if (CollectionUtils.isEmpty(siteTimes)) {
+            return null;
+        }
+        Map<Long, List<ExamApplySiteTime>> map = siteTimes.stream().collect(Collectors.groupingBy(ExamApplySiteTime::getExamTime));
+        String zhiyuan = System.getProperty("user.dir");
+        String path = zhiyuan + "/zhongzheng-admin/src/main/resources/学习记录";
+        File file = new File(path);
+        if (file.exists()) {
+            file.mkdirs();
+        }
+        map.forEach((k, v) -> {
+            if (CollectionUtils.isEmpty(v)) {
+                return;
+            }
+            //月份文件夹
+            String month = DateUtils.timestampToDateFormatMonthTwo(k);
+            String secondPath = path + "/" + month;
+            File secondFile = new File(secondPath);
+            if (secondFile.exists()) {
+                secondFile.mkdirs();
+            }
+            //时间文件夹
+            Map<String, List<ExamApplySiteTime>> timeMap = v.stream().filter(x -> StringUtils.isNotBlank(x.getSiteTime())).collect(Collectors.groupingBy(item -> {
+                List<ExamApplySiteTimeTwoVo> examApplySiteTimeTwoVos = JSONArray.parseArray(item.getSiteTime(), ExamApplySiteTimeTwoVo.class);
+                ExamApplySiteTimeTwoVo timeTwoVo = examApplySiteTimeTwoVos.stream().findFirst().orElse(null);
+                return timeTwoVo.getStartTime();
+            }));
+            timeMap.forEach((x, y) -> {
+                if (CollectionUtils.isEmpty(y)) {
+                    return;
+                }
+                String timePath = secondPath + "/" + x.replace(":", ".");
+                File timeFile = new File(timePath);
+                if (timeFile.exists()) {
+                    timeFile.mkdirs();
+                }
+                Set<Long> applyIds = new HashSet<>();
+                Set<Long> siteIds = new HashSet<>();
+                Set<String> startTimeList = new HashSet<>();
+                Set<String> endTimeList = new HashSet<>();
+                for (ExamApplySiteTime itme : y) {
+                    applyIds.add(itme.getApplyId());
+                    ExamApplySite site = iExamApplySiteService.getById(itme.getApplySiteId());
+                    siteIds.add(site.getSiteId());
+                    List<ExamApplySiteTimeTwoVo> examApplySiteTimeTwoVos = JSONArray.parseArray(itme.getSiteTime(), ExamApplySiteTimeTwoVo.class);
+                    ExamApplySiteTimeTwoVo timeTwoVo = examApplySiteTimeTwoVos.stream().findFirst().orElse(null);
+                    startTimeList.add(timeTwoVo.getStartTime());
+                    endTimeList.add(timeTwoVo.getEndTime());
+                }
+
+                //查询预约学员
+                List<UserSubscribe> userSubscribeList = iUserSubscribeService
+                        .list(new LambdaQueryWrapper<UserSubscribe>()
+                                .in(UserSubscribe::getApplyId, applyIds)
+                                .in(UserSubscribe::getSiteId, siteIds)
+                                .in(UserSubscribe::getApplySiteStartTime, startTimeList)
+                                .in(UserSubscribe::getApplySiteEndTime, endTimeList)
+                                .eq(UserSubscribe::getSubscribeStatus, 1));
+                if (CollectionUtils.isEmpty(userSubscribeList)) {
+                    return;
+                }
+                Map<Integer, List<UserSubscribe>> userMap = userSubscribeList.stream().collect(Collectors.groupingBy(UserSubscribe::getStudentType));
+                userMap.forEach((m, i) -> {
+                    switch (m) {
+                        case 1://新考人员
+                            String userPath = timePath + "/新考";
+                            File userFile = new File(userPath);
+                            if (userFile.exists()) {
+                                userFile.mkdirs();
+                            }
+                            //查询学员学时记录
+                            userStudyRecord(userPath, i);
+                            break;
+                        case 2://补考人员
+                            String userPathTwo = timePath + "/补考";
+                            File userTwoFile = new File(userPathTwo);
+                            if (userTwoFile.exists()) {
+                                userTwoFile.mkdirs();
+                            }
+                            //查询学员学时记录
+                            userStudyRecord(userPathTwo, i);
+                            break;
+                        default:
+                            break;
+                    }
+                });
+            });
+        });
+        String zipPath = zhiyuan + "/zhongzheng-admin/src/main/resources/xuexizip" + "/" + DateUtils.getNowTime() + ".zip";
+        File desc = new File(zipPath);
+        if (!desc.getParentFile().exists()) {
+            desc.getParentFile().mkdirs();
+        }
+        if (FileUtils.toZip(zipPath, path, true)) {
+            //压缩成功删除文件
+            Path pathStr = Paths.get(path);
+            try (Stream<Path> walk = Files.walk(pathStr)) {
+                walk.sorted(Comparator.reverseOrder())
+                        .forEach(FileUtils::deleteDirectoryStream);
+
+                //上传oss
+                OssRequest ossRequest = new OssRequest();
+                ossRequest.setGradeId(0L);
+                ossRequest.setUserId(0L);
+                ossRequest.setImageStatus(6);
+                File file1 = new File(zipPath);
+                ossRequest.setFile(FileUtils.getMultipartFile(file1));
+                String upload = ossService.upload(ossRequest);
+
+                //删除压缩包
+                Path zipPathStr = Paths.get(zipPath);
+                Stream<Path> zipWalk = Files.walk(zipPathStr);
+                zipWalk.sorted(Comparator.reverseOrder())
+                        .forEach(FileUtils::deleteDirectoryStream);
+                return upload;
+            } catch (Exception e) {
+                e.printStackTrace();
+                log.error("学员学习资料上传oss失败:" + e.getMessage());
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Boolean sendmail(ExamApplySendmailBo bo) {
+        if (CollectionUtils.isEmpty(bo.getMailAttrList())) {
+            throw new CustomException("收件箱地址为空!");
+        }
+        String tenantId = ServletUtils.getRequest().getHeader("TenantId");
+        SysConfig mailConfig = iSysConfigService.getSysConfigByKeyTenant("home.mail", Long.valueOf(tenantId));
+        if (ObjectUtils.isNull(mailConfig)) {
+            throw new CustomException("请先完成邮箱配置!");
+        }
+        JSONObject jsonObject = JSONObject.parseObject(mailConfig.getConfigValue());
+        String postAccount = jsonObject.get("postAccount").toString();
+        String postPassword = jsonObject.get("postPassword").toString();
+        String STMPserver = jsonObject.get("STMPserver").toString();
+        String post = jsonObject.get("post").toString();
+
+        List<String> mailAttrsList = bo.getMailAttrList();
+        try {
+            // 1.创建Session
+            Session session = JavaMailUtils.createsession(postAccount, postPassword, STMPserver, post);
+            // 2.创建邮件对象(Message抽象类的子类对象)
+            MimeMessage msg = new MimeMessage(session); // 传入session
+            msg.setFrom(new InternetAddress(postAccount)); // 发件人
+            msg.setRecipient(Message.RecipientType.TO, new InternetAddress(mailAttrsList.get(0))); // 收件人
+            if (mailAttrsList.size() > 1){
+                InternetAddress[] addresses = new InternetAddress[mailAttrsList.size() - 1];
+                for (int i = 0; i < mailAttrsList.size(); i++) {
+                    if (i == 0) {
+                        continue;
+                    }
+                    addresses[i-1] = new InternetAddress(mailAttrsList.get(i));
+                }
+                msg.setRecipients(Message.RecipientType.CC,addresses); //抄送人
+            }
+            msg.setSubject(bo.getMailName(),"utf-8"); // 标题
+
+            // 3.邮件内容"复合"对象
+            Multipart multipart = new MimeMultipart();
+            // 正文
+            BodyPart textPart = new MimeBodyPart();
+            // 参数1:正文内容
+            // 参数2:内容类型;字符编码集
+            textPart.setContent(bo.getMailText(), "text/html;charset=utf-8");
+
+            // 附件
+            BodyPart imagePart = new MimeBodyPart();
+            String filename = bo.getMailUrlName();
+            String fileNameNew= MimeUtility.encodeText(filename,"utf-8",null);
+            imagePart.setFileName(fileNameNew); // 设置附件文件的显示名称
+            InputStream in = ossService.getStreamByObject(bo.getMailUrl());
+
+            imagePart.setDataHandler(
+                    new DataHandler(
+                            new ByteArrayDataSource(
+                                    IOUtils.toByteArray(in),
+                                    "application/octet-stream")));
+
+            // 添加至邮件内容
+            multipart.addBodyPart(textPart); // 添加正文
+            multipart.addBodyPart(imagePart); // 添加附件
+            // 设置邮件内容
+            msg.setContent(multipart);
+            // 3.发送
+            Transport.send(msg);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+
+        return iExamApplySiteTimeService.update(new LambdaUpdateWrapper<ExamApplySiteTime>()
+                .in(ExamApplySiteTime::getId, bo.getIds())
+                .set(ExamApplySiteTime::getSendmail, 1));
+    }
+
+    private void userStudyRecord(String userPath, List<UserSubscribe> list) {
+        Map<Long, List<UserSubscribe>> map = list.stream().collect(Collectors.groupingBy(UserSubscribe::getUserId));
+        map.forEach((k, v) -> {
+            User user = iUserService.getById(k);
+            if (ObjectUtils.isNull(user)) {
+                return;
+            }
+            SysTenant tenant = sysTenantService.getById(user.getTenantId());
+            Set<Long> orderGoodsIds = v.stream().map(UserSubscribe::getOrderGoodsId).collect(Collectors.toSet());
+            List<UserStudyRecordExport> recordExports = new ArrayList<>();
+            for (Long orderGoodsId : orderGoodsIds) {
+                //学习记录
+                recordExports = baseMapper.getUserStudyRecord(orderGoodsId, k);
+                if (CollectionUtils.isEmpty(recordExports)) {
+                    continue;
+                }
+                recordExports.forEach(item -> {
+                    item.setUserName(user.getRealname());
+                    item.setIdCard(EncryptHandler.decrypt(user.getIdCard()));
+                    item.setCompanyName(tenant.getTenantName());
+                    item.setSectionTime(String.format("%s分钟", item.getDurationTime() / 60));
+                    if (item.getStatus() == 1) {
+                        //已学完
+                        item.setStudyTime(String.format("%s分钟", item.getDurationTime() / 60));
+                        item.setFinish("已完成");
+                    } else {
+                        item.setFinish("未完成");
+                    }
+                    item.setStudyStartTime(DateUtils.timestampToDateFormat(item.getStartTime(), "yyyy/MM/dd HH:mm:ss"));
+                    item.setStudyEndTime(DateUtils.timestampToDateFormat(item.getEndTime(), "yyyy/MM/dd HH:mm:ss"));
+                });
+            }
+            ExcelUtil<UserStudyRecordExport> util = new ExcelUtil<UserStudyRecordExport>(UserStudyRecordExport.class);
+            String path = userPath + "/" + user.getRealname() + ".xlsx";
+            util.exportEasyExcelStudy(util.exportEasyData(recordExports), path);
+        });
+    }
+
     private boolean checkNameUnique(ExamApply entity) {
         ExamApply info = getOne(new LambdaQueryWrapper<ExamApply>()
-                .eq(ExamApply::getApplyName,entity.getApplyName()).ne(ExamApply::getStatus,-1).last("limit 1"));
+                .eq(ExamApply::getApplyName, entity.getApplyName()).ne(ExamApply::getStatus, -1).last("limit 1"));
         if (Validator.isNotNull(info)) {
-            if(Validator.isNotEmpty(entity.getApplyId())){
-                if(entity.getApplyId().longValue() != info.getApplyId().longValue()){
+            if (Validator.isNotEmpty(entity.getApplyId())) {
+                if (entity.getApplyId().longValue() != info.getApplyId().longValue()) {
                     return true;
                 }
-            }else{
+            } else {
                 return true;
             }
         }

+ 22 - 20
zhongzheng-system/src/main/java/com/zhongzheng/modules/exam/service/impl/ExamApplySiteServiceImpl.java

@@ -1,28 +1,25 @@
 package com.zhongzheng.modules.exam.service.impl;
 
 import cn.hutool.core.bean.BeanUtil;
-import cn.hutool.core.util.StrUtil;
 import cn.hutool.json.JSONUtil;
-import com.zhongzheng.common.utils.DateUtils;
-import com.zhongzheng.modules.exam.bo.ExamApplySiteTimeAddBo;
-import com.zhongzheng.modules.exam.domain.ExamApplySiteTime;
-import com.zhongzheng.modules.exam.service.IExamApplySiteTimeService;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.github.pagehelper.Page;
-import com.zhongzheng.modules.exam.bo.ExamApplySiteAddBo;
-import com.zhongzheng.modules.exam.bo.ExamApplySiteQueryBo;
-import com.zhongzheng.modules.exam.bo.ExamApplySiteEditBo;
+import com.zhongzheng.common.utils.DateUtils;
+import com.zhongzheng.modules.exam.bo.*;
 import com.zhongzheng.modules.exam.domain.ExamApplySite;
+import com.zhongzheng.modules.exam.domain.ExamApplySiteTime;
 import com.zhongzheng.modules.exam.mapper.ExamApplySiteMapper;
-import com.zhongzheng.modules.exam.vo.ExamApplySiteVo;
 import com.zhongzheng.modules.exam.service.IExamApplySiteService;
+import com.zhongzheng.modules.exam.service.IExamApplySiteTimeService;
+import com.zhongzheng.modules.exam.vo.ExamApplySiteVo;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
 
+import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.List;
 import java.util.stream.Collectors;
 
@@ -114,6 +111,7 @@ public class ExamApplySiteServiceImpl extends ServiceImpl<ExamApplySiteMapper, E
         lqw.eq( ExamApplySite::getApplyId,bo.get(0).getApplyId());
         lqw.eq( ExamApplySite::getStatus,bo.get(0).getStatus());
         this.remove(lqw);
+        examApplySiteTimeService.remove(new LambdaQueryWrapper<ExamApplySiteTime>().eq(ExamApplySiteTime::getApplyId,bo.get(0).getApplyId()));
         //增加地点
         for (ExamApplySiteAddBo examApplySiteAddBo : bo) {
             ExamApplySite add = BeanUtil.toBean(examApplySiteAddBo, ExamApplySite.class);
@@ -122,13 +120,17 @@ public class ExamApplySiteServiceImpl extends ServiceImpl<ExamApplySiteMapper, E
             add.setUpdateTime(DateUtils.getNowTime());
             this.save(add);
             for (ExamApplySiteTimeAddBo examApplySiteTimeAddBo : examApplySiteAddBo.getExamApplySiteTime()) {
-                ExamApplySiteTime examApplySiteTime = BeanUtil.toBean(examApplySiteTimeAddBo, ExamApplySiteTime.class);
-                examApplySiteTime.setApplySiteId(add.getId());
-                examApplySiteTime.setApplyId(add.getApplyId());
-                examApplySiteTime.setSiteTime(JSONUtil.toJsonStr(examApplySiteTimeAddBo.getExamApplySiteTimeTwo()));
-                examApplySiteTime.setCreateTime(DateUtils.getNowTime());
-                examApplySiteTime.setUpdateTime(DateUtils.getNowTime());
-                examApplySiteTimeService.save(examApplySiteTime);
+                if (CollectionUtils.isNotEmpty(examApplySiteTimeAddBo.getExamApplySiteTimeTwo())){
+                    for (ExamApplySiteTimeTwoAddBo examApplySiteTimeTwoAddBo : examApplySiteTimeAddBo.getExamApplySiteTimeTwo()) {
+                        ExamApplySiteTime examApplySiteTime = BeanUtil.toBean(examApplySiteTimeAddBo, ExamApplySiteTime.class);
+                        examApplySiteTime.setApplySiteId(add.getId());
+                        examApplySiteTime.setApplyId(add.getApplyId());
+                        examApplySiteTime.setSiteTime(JSONUtil.toJsonStr(Arrays.asList(examApplySiteTimeTwoAddBo)));
+                        examApplySiteTime.setCreateTime(DateUtils.getNowTime());
+                        examApplySiteTime.setUpdateTime(DateUtils.getNowTime());
+                        examApplySiteTimeService.save(examApplySiteTime);
+                    }
+                }
             }
         }
         return true;

+ 57 - 0
zhongzheng-system/src/main/java/com/zhongzheng/modules/exam/vo/ExamApplyPlaceVo.java

@@ -0,0 +1,57 @@
+package com.zhongzheng.modules.exam.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+
+/**
+ * 考试安排视图对象 mall_package
+ *
+ * @author ruoyi
+ * @date 2021-12-07
+ */
+@Data
+@ApiModel("考场返回对象")
+public class ExamApplyPlaceVo {
+	private static final long serialVersionUID = 1L;
+
+	@ApiModelProperty("$pkColumn.columnComment")
+	private Long id;
+
+	/** $pkColumn.columnComment */
+	@ApiModelProperty("$pkColumn.columnComment")
+	private Long applyId;
+
+	@ApiModelProperty("考试日期")
+	private Long applyDate;
+
+	@ApiModelProperty("考试地点ID")
+	private Long applySiteId;
+
+	@ApiModelProperty("考试时间段")
+	private String applyTime;
+
+	private String siteTime;
+
+	@ApiModelProperty("报名开始时间")
+	private Long applyStartTime;
+
+	@ApiModelProperty("报名结束时间")
+	private Long applyEndTime;
+
+	@ApiModelProperty("已报人数")
+	private Integer people;
+
+	@ApiModelProperty("可报人数")
+	private Long mayNum;
+
+	@ApiModelProperty("1非补考学员 2补考学员 ")
+	private String applyStatus;
+
+	@ApiModelProperty("考场性质:1普通场,2专场")
+	private Integer applyNature;
+
+	@ApiModelProperty("是否发送邮件:0未发送 1已发送")
+	private Integer sendmail;
+}

+ 11 - 2
zhongzheng-system/src/main/java/com/zhongzheng/modules/exam/vo/ExamApplyVo.java

@@ -1,11 +1,11 @@
 package com.zhongzheng.modules.exam.vo;
 
 import com.zhongzheng.common.annotation.Excel;
-import com.fasterxml.jackson.annotation.JsonFormat;
+import com.zhongzheng.modules.exam.bo.ExamApplyUserImportBo;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
-import java.util.Date;
+
 import java.util.List;
 
 
@@ -103,4 +103,13 @@ public class ExamApplyVo {
 
 	@ApiModelProperty("智慧考场数量")
 	private Integer wisdomNum;
+
+	@ApiModelProperty("考场性质:1普通场,2专场")
+	private Integer applyNature;
+
+	@ApiModelProperty("专场学员资料地址 ")
+	private String natureUrl;
+
+	@ApiModelProperty("专场预约学员")
+	private List<ExamApplyUserImportBo> applyUsers;
 }

+ 4 - 0
zhongzheng-system/src/main/java/com/zhongzheng/modules/system/domain/SysTenant.java

@@ -103,5 +103,9 @@ private static final long serialVersionUID=1L;
 
     /** 教务电话 */
     private String eduPhone;
+    /** 邮箱账号 */
+    private String postAccount;
+    /** 邮箱密码 */
+    private String postPassword;
 
 }

+ 72 - 0
zhongzheng-system/src/main/java/com/zhongzheng/modules/user/vo/UserStudyRecordExport.java

@@ -0,0 +1,72 @@
+package com.zhongzheng.modules.user.vo;
+
+
+import cn.afterturn.easypoi.excel.annotation.Excel;
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+
+
+/**
+ * 用户预约考试视图对象 mall_package
+ *
+ * @author ruoyi
+ * @date 2021-12-07
+ */
+@Data
+@ApiModel("学员学时")
+public class UserStudyRecordExport {
+	private static final long serialVersionUID = 1L;
+
+	private Long orderGoodsId;
+	private Long goodsId;
+	private Long moduleId;
+	private Long chapterId;
+	private Long sectionId;
+	private Long startTime;
+	private Long endTime;
+	private Long durationTime;
+	private Integer status;
+
+	/** $column.columnComment */
+	@Excel(name = "学员姓名",width = 30)
+	private String userName;
+
+	@Excel(name = "证件号码" ,width = 30)
+	private String idCard;
+
+	/** 考试标题 */
+	@Excel(name = "所在公司",width = 30)
+	private String companyName;
+
+	/** 考试地点 */
+	@Excel(name = "学习课程",width = 30)
+	private String goodsName;
+
+	@Excel(name = "章节",width = 30)
+	private String chapterName;
+
+	/** 考试开始时间段 */
+	@Excel(name = "视频名称",width = 30)
+	private String sectionName;
+
+	/** 考试开始时间段 */
+	@Excel(name = "视频时长",width = 30)
+	private String sectionTime;
+
+	/** 考试开始时间段 */
+	@Excel(name = "学习开始时间",width = 30)
+	private String studyStartTime;
+
+	@Excel(name = "学习完成时间",width = 30)
+	private String studyEndTime;
+
+	@Excel(name = "已学时长",width = 30)
+	private String studyTime;
+
+	@Excel(name = "是否完成",width = 30)
+	private String finish;
+
+	@Excel(name = "ip",width = 30)
+	private String ip;
+
+}

+ 57 - 0
zhongzheng-system/src/main/resources/mapper/modules/exam/ExamApplyMapper.xml

@@ -34,6 +34,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="people" column="people"/>
         <result property="reportStatus" column="report_status"/>
         <result property="wisdomNum" column="wisdom_num"/>
+        <result property="applyNature" column="apply_nature"/>
+        <result property="natureUrl" column="nature_url"/>
     </resultMap>
 
     <resultMap type="com.zhongzheng.modules.exam.vo.ExamUserApplyVo" id="ExamUserApplyVo">
@@ -466,4 +468,59 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
           AND es.site_address = #{applySiteAddress}
           AND ea.`status` = 1
     </select>
+
+
+    <select id="getPlaceList" parameterType="com.zhongzheng.modules.exam.bo.ExamApplyQueryBo" resultType="com.zhongzheng.modules.exam.vo.ExamApplyPlaceVo">
+        SELECT
+        eat.id,
+        ea.apply_id,
+        eat.exam_time AS applyDate,
+        eat.site_time,
+        eat.apply_site_id,
+        ea.apply_status,
+        ea.apply_nature,
+        eat.sendmail,
+        ea.apply_start_time,
+        ea.apply_end_time
+        FROM
+        exam_apply_site_time eat
+        LEFT JOIN exam_apply ea ON eat.apply_id = ea.apply_id
+        WHERE 1=1
+        <if test="applyStartTime != null and applyStartTime != ''">
+            AND eat.exam_time <![CDATA[ >= ]]> #{applyStartTime}
+        </if>
+        <if test="applyEndTime != null and applyEndTime != ''">
+            AND eat.exam_time <![CDATA[ <= ]]> #{applyEndTime}
+        </if>
+        order by ea.create_time desc
+    </select>
+
+    <select id="getUserStudyRecord" parameterType="map" resultType="com.zhongzheng.modules.user.vo.UserStudyRecordExport">
+        SELECT
+            order_goods_id,
+            goods_id,
+            module_id,
+            chapter_id,
+            section_id,
+            (SELECT goods_name FROM goods WHERE goods_id = usr.goods_id) as goods_name,
+            (SELECT `name` FROM course_chapter WHERE chapter_id = usr.chapter_id) as chapter_name,
+            (SELECT `name` FROM course_section WHERE section_id = usr.section_id) as section_name,
+            (SELECT duration_time FROM course_section WHERE section_id = usr.section_id) as duration_time,
+            MIN(start_time) AS start_time,
+            MAX(end_time) AS end_time,
+            MAX(`status`) AS `status`,
+            any_value(ip) AS ip
+        FROM
+            user_study_record usr
+        WHERE
+            user_id = #{userId}
+          AND order_goods_id = #{orderGoodsId}
+        GROUP BY
+            order_goods_id,
+            goods_id,
+            course_id,
+            module_id,
+            chapter_id,
+            section_id
+    </select>
 </mapper>

+ 1 - 1
zhongzheng-system/src/main/resources/mapper/modules/user/UserMockSubscribeMapper.xml

@@ -159,7 +159,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             AND u.user_id = #{userId}
         </if>
         <if test="searchKey != null and searchKey != ''">
-            AND (u.realname like concat('%', #{searchKey}, '%') or u.id_card like concat('%', #{searchKey}, '%'))
+            AND (u.realname like concat('%', #{searchKey}, '%') or u.id_card like concat('%', #{searchKey,typeHandler=com.zhongzheng.common.type.EncryptHandler}, '%'))
         </if>
         <if test="majorId != null and majorId != ''">
             AND mm.major_id = #{majorId}