Просмотр исходного кода

1、创建代码仓库,备份代码

Bingo 3 лет назад
Родитель
Сommit
3c298f260a
69 измененных файлов с 9411 добавлено и 0 удалено
  1. 238 0
      pom.xml
  2. 13 0
      src/main/java/com/chuanghai/attendance/AttendanceApplication.java
  3. 66 0
      src/main/java/com/chuanghai/attendance/common/exception/BizCodeEnume.java
  4. 129 0
      src/main/java/com/chuanghai/attendance/common/exception/MyExceptionHandler.java
  5. 66 0
      src/main/java/com/chuanghai/attendance/common/exception/RRException.java
  6. 35 0
      src/main/java/com/chuanghai/attendance/common/utils/BaseResult.java
  7. 51 0
      src/main/java/com/chuanghai/attendance/common/utils/CommonResult.java
  8. 70 0
      src/main/java/com/chuanghai/attendance/controller/CampusTimeController.java
  9. 49 0
      src/main/java/com/chuanghai/attendance/controller/LateClockController.java
  10. 172 0
      src/main/java/com/chuanghai/attendance/controller/MonthlySummaryController.java
  11. 49 0
      src/main/java/com/chuanghai/attendance/controller/OriginalDataController.java
  12. 48 0
      src/main/java/com/chuanghai/attendance/controller/WorkerIdentityController.java
  13. 15 0
      src/main/java/com/chuanghai/attendance/dao/CampusTimeDao.java
  14. 15 0
      src/main/java/com/chuanghai/attendance/dao/DayDetailsDao.java
  15. 15 0
      src/main/java/com/chuanghai/attendance/dao/LateClockDao.java
  16. 15 0
      src/main/java/com/chuanghai/attendance/dao/MonthlySummaryDao.java
  17. 15 0
      src/main/java/com/chuanghai/attendance/dao/OriginalDataDao.java
  18. 15 0
      src/main/java/com/chuanghai/attendance/dao/WorkerIdentityDao.java
  19. 24 0
      src/main/java/com/chuanghai/attendance/dto/CampusTimeLateClockDto.java
  20. 102 0
      src/main/java/com/chuanghai/attendance/dto/MonthlyDto.java
  21. 53 0
      src/main/java/com/chuanghai/attendance/entity/CampusTime.java
  22. 159 0
      src/main/java/com/chuanghai/attendance/entity/DayDetails.java
  23. 336 0
      src/main/java/com/chuanghai/attendance/entity/DownLoadExcel.java
  24. 37 0
      src/main/java/com/chuanghai/attendance/entity/LateClock.java
  25. 42 0
      src/main/java/com/chuanghai/attendance/entity/MonitorFoldLineTable.java
  26. 128 0
      src/main/java/com/chuanghai/attendance/entity/MonthlySummary.java
  27. 58 0
      src/main/java/com/chuanghai/attendance/entity/OriginalData.java
  28. 44 0
      src/main/java/com/chuanghai/attendance/entity/WorkerIdentity.java
  29. 20 0
      src/main/java/com/chuanghai/attendance/service/CampusTimeService.java
  30. 18 0
      src/main/java/com/chuanghai/attendance/service/DayDetailsService.java
  31. 18 0
      src/main/java/com/chuanghai/attendance/service/LateClockService.java
  32. 25 0
      src/main/java/com/chuanghai/attendance/service/MonthlySummaryService.java
  33. 22 0
      src/main/java/com/chuanghai/attendance/service/OriginalDataService.java
  34. 16 0
      src/main/java/com/chuanghai/attendance/service/WorkerIdentityService.java
  35. 45 0
      src/main/java/com/chuanghai/attendance/service/impl/CampusTimeServiceImpl.java
  36. 77 0
      src/main/java/com/chuanghai/attendance/service/impl/DayDetailsServiceImpl.java
  37. 37 0
      src/main/java/com/chuanghai/attendance/service/impl/LateClockServiceImpl.java
  38. 1129 0
      src/main/java/com/chuanghai/attendance/service/impl/MonthlySummaryServiceImpl.java
  39. 109 0
      src/main/java/com/chuanghai/attendance/service/impl/OriginalDataServiceImpl.java
  40. 81 0
      src/main/java/com/chuanghai/attendance/service/impl/WorkerIdentityServiceImpl.java
  41. 304 0
      src/main/java/com/chuanghai/attendance/utils/DateUtil.java
  42. 453 0
      src/main/java/com/chuanghai/attendance/utils/ExcelImportXLSXUtil.java
  43. 207 0
      src/main/java/com/chuanghai/attendance/utils/ExcelUtil.java
  44. 569 0
      src/main/java/com/chuanghai/attendance/utils/FileExportUtil.java
  45. 33 0
      src/main/java/com/chuanghai/attendance/utils/excel/CellResult.java
  46. 263 0
      src/main/java/com/chuanghai/attendance/utils/excel/EachCommand.java
  47. 326 0
      src/main/java/com/chuanghai/attendance/utils/excel/EachMergeCommand.java
  48. 597 0
      src/main/java/com/chuanghai/attendance/utils/excel/ExcelBase.java
  49. 470 0
      src/main/java/com/chuanghai/attendance/utils/excel/ExcelExportUtil.java
  50. 160 0
      src/main/java/com/chuanghai/attendance/utils/excel/ExcelImportXLSUtil.java
  51. 464 0
      src/main/java/com/chuanghai/attendance/utils/excel/ExcelImportXLSXUtil.java
  52. 373 0
      src/main/java/com/chuanghai/attendance/utils/excel/ExcelUtil.java
  53. 184 0
      src/main/java/com/chuanghai/attendance/utils/excel/FileTools.java
  54. 121 0
      src/main/java/com/chuanghai/attendance/utils/excel/ImportExcelUtil.java
  55. 118 0
      src/main/java/com/chuanghai/attendance/utils/excel/ImportResult.java
  56. 198 0
      src/main/java/com/chuanghai/attendance/utils/excel/XwpfTUtil.java
  57. 55 0
      src/main/java/com/chuanghai/attendance/vo/CampusTimeLateClockVO.java
  58. 29 0
      src/main/java/com/chuanghai/attendance/vo/ClockTimeVo.java
  59. 37 0
      src/main/resources/application.yml
  60. 28 0
      src/main/resources/smart-doc.json
  61. 7 0
      src/main/resources/static/doc/AllInOne.css
  62. 249 0
      src/main/resources/static/doc/index.adoc
  63. 46 0
      src/main/resources/static/doc/index.html
  64. 7 0
      src/main/resources/static/doc/rpc/AllInOne.css
  65. 10 0
      src/main/resources/static/doc/rpc/rpc-index.adoc
  66. 183 0
      src/main/resources/static/doc/rpc/rpc-index.html
  67. 105 0
      src/main/resources/static/doc/rpc/search.js
  68. 144 0
      src/main/resources/static/doc/search.js
  69. 15 0
      src/test/java/com/chuanghai/attendance/AttendanceApplicationTests.java

+ 238 - 0
pom.xml

@@ -0,0 +1,238 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>2.7.2</version>
+        <relativePath/> <!-- lookup parent from repository -->
+    </parent>
+    <groupId>com.chuanghai</groupId>
+    <artifactId>attendance</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+    <name>attendance</name>
+    <description>Demo project for Spring Boot</description>
+    <properties>
+        <java.version>1.8</java.version>
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- 数据校验 -->
+        <dependency>
+            <groupId>org.hibernate</groupId>
+            <artifactId>hibernate-validator</artifactId>
+            <version>6.1.5.Final</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter</artifactId>
+            <version>3.3.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <version>1.18.22</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-web</artifactId>
+            <version>5.3.22</version>
+        </dependency>
+
+        <!--easypoi  execl导入导出-->
+<!--        <dependency>-->
+<!--            <groupId>cn.afterturn</groupId>-->
+<!--            <artifactId>easypoi-base</artifactId>-->
+<!--            <version>3.2.0</version>-->
+<!--        </dependency>-->
+<!--        <dependency>-->
+<!--            <groupId>cn.afterturn</groupId>-->
+<!--            <artifactId>easypoi-web</artifactId>-->
+<!--            <version>3.2.0</version>-->
+<!--        </dependency>-->
+<!--        <dependency>-->
+<!--            <groupId>cn.afterturn</groupId>-->
+<!--            <artifactId>easypoi-annotation</artifactId>-->
+<!--            <version>3.2.0</version>-->
+<!--        </dependency>-->
+
+
+<!--        &lt;!&ndash; Apache poi&ndash;&gt;-->
+<!--        <dependency>-->
+<!--            <groupId>org.apache.poi</groupId>-->
+<!--            <artifactId>poi</artifactId>-->
+<!--            <version>3.8</version>-->
+<!--        </dependency>-->
+<!--        <dependency>-->
+<!--            <groupId>org.apache.poi</groupId>-->
+<!--            <artifactId>poi-ooxml</artifactId>-->
+<!--            <version>4.1.1</version>-->
+<!--        </dependency>-->
+
+<!--                <dependency>-->
+<!--                    <groupId>org.apache.poi</groupId>-->
+<!--                    <artifactId>poi</artifactId>-->
+<!--                    <version>5.2.2</version>-->
+<!--                </dependency>-->
+
+<!--                <dependency>-->
+<!--                    <groupId>org.apache.poi</groupId>-->
+<!--                    <artifactId>poi-ooxml</artifactId>-->
+<!--                    <version>3.10-FINAL</version>-->
+<!--                </dependency>-->
+<!--                <dependency>-->
+<!--                    <groupId>com.alibaba</groupId>-->
+<!--                    <artifactId>easyexcel</artifactId>-->
+<!--                    <version>2.2.10</version>-->
+<!--                </dependency>-->
+
+
+        <!-- excel导入导出 -->
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi</artifactId>
+            <version>3.16</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>2.4</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi-ooxml</artifactId>
+            <version>3.16</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>xml-apis</groupId>
+                    <artifactId>xml-apis</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>commons-fileupload</groupId>
+            <artifactId>commons-fileupload</artifactId>
+            <version>1.3.1</version>
+        </dependency>
+        <dependency>
+            <groupId>net.sf.jxls</groupId>
+            <artifactId>jxls-core</artifactId>
+            <version>1.0.6</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.apache.poi</groupId>
+                    <artifactId>poi</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.apache.poi</groupId>
+                    <artifactId>poi-ooxml</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.jxls</groupId>
+            <artifactId>jxls-poi</artifactId>
+            <version>1.0.13</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.apache.poi</groupId>
+                    <artifactId>poi</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.apache.poi</groupId>
+                    <artifactId>poi-ooxml</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>ch.qos.logback</groupId>
+                    <artifactId>logback-core</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+<!--        <dependency>-->
+<!--            <groupId>com.finger.tailor.common</groupId>-->
+<!--            <artifactId>tailor-common</artifactId>-->
+<!--        </dependency>-->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>easyexcel</artifactId>
+            <version>2.2.10</version>
+        </dependency>
+        <dependency>
+            <groupId>cn.afterturn</groupId>
+            <artifactId>easypoi-annotation</artifactId>
+            <version>4.4.0</version>
+        </dependency>
+
+    </dependencies>
+
+
+    <build>
+        <plugins>
+            <!-- smart doc -->
+            <plugin>
+                <groupId>com.github.shalousun</groupId>
+                <artifactId>smart-doc-maven-plugin</artifactId>
+                <version>2.2.2</version>
+                <configuration>
+                    <!--指定生成文档的使用的配置文件,配置文件放在自己的项目中-->
+                    <configFile>./src/main/resources/smart-doc.json</configFile>
+                    <!--指定项目名称-->
+                    <projectName>考勤数据分析</projectName>
+                </configuration>
+                <executions>
+                    <execution>
+                        <!--如果不需要在执行编译时启动smart-doc,则将phase注释掉-->
+                        <phase>compile</phase>
+                        <goals>
+                            <!--smart-doc提供了html、openapi、markdown等goal,可按需配置-->
+                            <goal>html</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+
+
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <excludes>
+                        <exclude>
+                            <groupId>org.projectlombok</groupId>
+                            <artifactId>lombok</artifactId>
+                        </exclude>
+                    </excludes>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 13 - 0
src/main/java/com/chuanghai/attendance/AttendanceApplication.java

@@ -0,0 +1,13 @@
+package com.chuanghai.attendance;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class AttendanceApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(AttendanceApplication.class, args);
+    }
+}

+ 66 - 0
src/main/java/com/chuanghai/attendance/common/exception/BizCodeEnume.java

@@ -0,0 +1,66 @@
+package com.chuanghai.attendance.common.exception;
+
+
+/***
+ * 错误码和错误信息定义类
+ * 1. 错误码定义规则为5为数字
+ * 2. 前两位表示业务场景,最后三位表示错误码。例如:100001。10:通用 001:系统未知异常
+ * 3. 维护错误码后需要维护错误描述,将他们定义为枚举形式
+ * 错误码列表:
+ *  10: 通用
+ *      001:参数格式校验
+ *  11: 用户
+ *  12: 权限
+ *  13: 参数异常
+ *  14: 文件
+ *  15: 积分模块
+ *
+ *
+ */
+public enum BizCodeEnume {
+    UNKNOW_EXCEPTION(10000,"系统未知异常"),
+    VAILD_EXCEPTION(10001,"参数格式校验失败"),
+    BODY_IS_EMPTY(10002, "body为空"),
+    METHOD_NOT_SUPPORT(10003, "请求方法不支持"),
+    DATA_IS_EXIST(10004, "数据已存在"),
+    DATA_IS_NOT_EXIST(10005, "数据不存在"),
+    TOKET_INVALID(10006, "无效的token"),
+    PARAMETER_ERROR(10007, "参数异常"),
+    NO_PERMISSION(10008, "没有操作权限"),
+    ADMIN_LOGIN_FAIL(10009, "管理员登录失败"),
+    REQUEST_HEADER_MISSING(10012, "缺少必要的请求头"),
+    PAYABLE_INFO_IS_NOT_EXIT(10013, "缴费信息不存在"),
+    ORDER_IS_NOT_EXIT(10014, "订单不存在"),
+    GET_JSAPI_PAY_PARAM_ERROR(10015, "获取微信jsapi支付参数失败"),
+    CCB_ORDER_QUERY_ERROR(10016, "建行订单状态查询失败"),
+    NOT_CCB_PAY_ORDER(10017, "非建行支付订单"),
+    JXNXS_ORDER_QUERY_ERROR(10018, "农商行订单状态查询失败"),
+    PAY_GRADE_IS_NOT_EXIT(10019, "缴费年级不存在"),
+    FILE_IMPORT_ERROR(10020, "文件导入失败"),
+    BATCH_EXECUTOR_ERROR(10100, "批量操作数据库失败"),
+    PAY_AMOUNT_ERROR(10021,"付款金额错误"),
+    PAY_ITEM_EXIST(10022,"缴费项目已存在"),
+    PAY_ITEM_NOT_EXIST(10025,"缴费项目不存在"),
+    ID_IS_NULL(10023,"id为空"),
+    PAY_ITEM_NOT_SUPPORT(10024,"该缴费项目不支持导出TXT"),
+    IS_PAY(10025,"该缴费宿舍已缴费"),
+    TOO_LONG(10026,"内容超长"),
+    ERROR_DE(10027,"AES加密失败"),
+    DORM_EXIST(10028,"宿舍已缴费"),
+    ORDER_IS_PAY(10029,"该订单的部分项目已支付,请前往首页重新支付");
+
+    private int code;
+    private String msg;
+    BizCodeEnume(int code,String msg){
+        this.code = code;
+        this.msg = msg;
+    }
+
+    public int getCode() {
+        return code;
+    }
+
+    public String getMsg() {
+        return msg;
+    }
+}

+ 129 - 0
src/main/java/com/chuanghai/attendance/common/exception/MyExceptionHandler.java

@@ -0,0 +1,129 @@
+package com.chuanghai.attendance.common.exception;
+
+
+import com.alibaba.excel.exception.ExcelAnalysisException;
+import com.chuanghai.attendance.common.utils.CommonResult;
+import org.springframework.dao.DuplicateKeyException;
+import org.springframework.http.converter.HttpMessageNotReadableException;
+import org.springframework.util.StringUtils;
+import org.springframework.web.HttpRequestMethodNotSupportedException;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.MissingRequestHeaderException;
+import org.springframework.web.bind.MissingServletRequestParameterException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @Author: codingliang
+ * @Description: 统一异常处理
+ * @Date: 2021-01-06 10:07
+ * @Version: V1.0
+ **/
+@RestControllerAdvice
+public class MyExceptionHandler {
+
+    /**
+     * 处理参数校验异常
+     * @param e
+     * @return
+     */
+    @ExceptionHandler(MethodArgumentNotValidException.class)
+    public CommonResult handleValidException(MethodArgumentNotValidException e) {
+        Map<String, String> map = new HashMap<>();
+        e.getBindingResult().getFieldErrors().forEach(item -> {
+            String errMessage = item.getDefaultMessage();
+            String errField = item.getField();
+            map.put(errField, errMessage);
+        });
+        return CommonResult.fail(Integer.toString(BizCodeEnume.VAILD_EXCEPTION.getCode()), BizCodeEnume.VAILD_EXCEPTION.getMsg()).setResult(map);
+    }
+
+    /**
+     * 数据库索引重复
+     * @return
+     */
+    @ExceptionHandler(DuplicateKeyException.class)
+    public CommonResult handleSQLIntegrityConstraintViolationException() {
+        return CommonResult.fail(Integer.toString(BizCodeEnume.DATA_IS_EXIST.getCode()), BizCodeEnume.DATA_IS_EXIST.getMsg());
+    }
+
+    /**
+     * 请求方法不支持
+     * @return
+     */
+    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
+    public CommonResult handleRequestMethodNotSupported() {
+        return CommonResult.fail(Integer.toString(BizCodeEnume.METHOD_NOT_SUPPORT.getCode()), BizCodeEnume.METHOD_NOT_SUPPORT.getMsg());
+    }
+
+    /**
+     * 缺少请求头
+     * @return
+     */
+    @ExceptionHandler(MissingRequestHeaderException.class)
+    public CommonResult handleRequestHeaderMissing() {
+        return CommonResult.fail(Integer.toString(BizCodeEnume.REQUEST_HEADER_MISSING.getCode()), BizCodeEnume.REQUEST_HEADER_MISSING.getMsg());
+    }
+
+    /**
+     * 参数缺失
+     * @return
+     */
+    @ExceptionHandler(MissingServletRequestParameterException.class)
+    public CommonResult handleMissingServletRequestParameterException() {
+        return CommonResult.fail(Integer.toString(BizCodeEnume.PARAMETER_ERROR.getCode()), BizCodeEnume.PARAMETER_ERROR.getMsg());
+    }
+
+    /**
+     * body为空
+     * @return
+     */
+    @ExceptionHandler(HttpMessageNotReadableException.class)
+    public CommonResult handleMessageNotReadableException() {
+        return CommonResult.fail(Integer.toString(BizCodeEnume.BODY_IS_EMPTY.getCode()), BizCodeEnume.BODY_IS_EMPTY.getMsg());
+    }
+
+    /**
+     * easyExcel 文件读取失败
+     * @return
+     */
+    @ExceptionHandler(ExcelAnalysisException.class)
+    public CommonResult handleExcelAnalysisException(ExcelAnalysisException e) {
+        String errorMessage = e.getMessage();
+        System.out.println(errorMessage);
+        String errMsg = "文件导入失败";
+        if (StringUtils.hasText(errorMessage) && errorMessage.contains("doesn't have a default value")) {
+            errMsg =  "文件导入失败,请检查模板header是否正确";
+        } else if (StringUtils.hasText(errorMessage) && errorMessage.contains("Duplicate entity")) {
+            errMsg = "文件导入失败,含有重复数据";
+        }else if (StringUtils.hasText(errorMessage) && errorMessage.contains("数据已存在") ){
+            errMsg = "文件导入失败,含有重复数据";
+        }
+        return CommonResult.fail(Integer.toString(BizCodeEnume.FILE_IMPORT_ERROR.getCode()), errMsg);
+    }
+
+    /**
+     * 自定义异常
+     * @param e
+     * @return
+     */
+    @ExceptionHandler(RRException.class)
+    public CommonResult handleRRException(RRException e) {
+        return CommonResult.fail(Integer.toString(e.getCode()), e.getMsg());
+    }
+
+    /**
+     * 未知异常
+     * @param throwable
+     * @return
+     */
+    @ExceptionHandler(Throwable.class)
+    public CommonResult handleException(Throwable throwable) {
+        throwable.printStackTrace();
+        return CommonResult.fail(Integer.toString(BizCodeEnume.UNKNOW_EXCEPTION.getCode()), BizCodeEnume.UNKNOW_EXCEPTION.getMsg());
+    }
+}
+

+ 66 - 0
src/main/java/com/chuanghai/attendance/common/exception/RRException.java

@@ -0,0 +1,66 @@
+package com.chuanghai.attendance.common.exception;
+
+
+/**
+ * 自定义异常
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public class RRException extends RuntimeException {
+    private static final long serialVersionUID = 1L;
+
+    private String msg;
+    private int code = 500;
+
+    public RRException(String msg) {
+        super(msg);
+        this.msg = msg;
+    }
+
+    public RRException(String msg, Throwable e) {
+        super(msg, e);
+        this.msg = msg;
+    }
+
+    public RRException(String msg, int code) {
+        super(msg);
+        this.msg = msg;
+        this.code = code;
+    }
+
+    public RRException(BizCodeEnume bizCodeEnume) {
+        super(bizCodeEnume.getMsg());
+        this.msg = bizCodeEnume.getMsg();
+        this.code = bizCodeEnume.getCode();
+    }
+
+    public RRException(BizCodeEnume bizCodeEnume, String msg) {
+        super(bizCodeEnume.getMsg());
+        this.msg = msg;
+        this.code = bizCodeEnume.getCode();
+    }
+
+    public RRException(String msg, int code, Throwable e) {
+        super(msg, e);
+        this.msg = msg;
+        this.code = code;
+    }
+
+    public String getMsg() {
+        return msg;
+    }
+
+    public void setMsg(String msg) {
+        this.msg = msg;
+    }
+
+    public int getCode() {
+        return code;
+    }
+
+    public void setCode(int code) {
+        this.code = code;
+    }
+
+
+}

+ 35 - 0
src/main/java/com/chuanghai/attendance/common/utils/BaseResult.java

@@ -0,0 +1,35 @@
+package com.chuanghai.attendance.common.utils;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * @Author: codingliang
+ * @Description: 接口统一返回基类
+ * @Date: 2021-04-29 12:09
+ * @Version: V1.0
+ **/
+@Data
+public abstract class BaseResult<T> implements Serializable {
+
+    /**
+     * 接口调用结果标识
+     */
+    private boolean success = false;
+
+    /**
+     * 接口调用结果信息
+     */
+    private String message;
+
+    /**
+     * 接口调用业务码
+     */
+    private String code;
+
+    /**
+     * 接口调用返回数据
+     */
+    private T data;
+}

+ 51 - 0
src/main/java/com/chuanghai/attendance/common/utils/CommonResult.java

@@ -0,0 +1,51 @@
+package com.chuanghai.attendance.common.utils;
+
+import java.io.Serializable;
+
+/**
+ * @Author: codingliang
+ * @Description: 接口统一返回
+ * @Date: 2021-04-29 12:10
+ * @Version: V1.0
+ **/
+public class CommonResult<T> extends BaseResult implements Serializable {
+
+    private static final long serialVersionUID = 3616484754899974346L;
+
+    public static CommonResult ok() {
+        return ok("1", "执行成功");
+    }
+
+    public static <T> CommonResult<T> ok(String code, String msg) {
+        return baseCreate(code, msg, true);
+    }
+
+    public static CommonResult fail() {
+        return fail("-1", "执行失败");
+    }
+
+    public static CommonResult fail(String code, String msg) {
+        return baseCreate(code, msg, false);
+    }
+
+    private static <T> CommonResult<T> baseCreate(String code, String msg, boolean success) {
+        CommonResult result = new CommonResult();
+        result.setCode(code);
+        result.setSuccess(success);
+        result.setMessage(msg);
+        return result;
+    }
+
+    public CommonResult<T> setResult(T data) {
+        this.setData(data);
+        return this;
+    }
+
+    public T getData() {
+        return (T) super.getData();
+    }
+
+    public static CommonResult result(String code, String msg, Boolean b) {
+        return baseCreate(code, msg, b);
+    }
+}

+ 70 - 0
src/main/java/com/chuanghai/attendance/controller/CampusTimeController.java

@@ -0,0 +1,70 @@
+package com.chuanghai.attendance.controller;
+
+import com.chuanghai.attendance.common.utils.CommonResult;
+import com.chuanghai.attendance.dto.CampusTimeLateClockDto;
+import com.chuanghai.attendance.entity.CampusTime;
+import com.chuanghai.attendance.entity.LateClock;
+import com.chuanghai.attendance.service.CampusTimeService;
+import com.chuanghai.attendance.service.LateClockService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 校区打卡设置
+ *
+ * @Author: binguo
+ * @Date: 2022/9/20 星期二 17:01
+ * @Description: com.chuanghai.attendance.controller
+ * @Version: 1.0
+ */
+@RestController
+@RequestMapping("campus")
+public class CampusTimeController {
+
+    @Autowired
+    private CampusTimeService campusTimeService;
+
+    @Autowired
+    private LateClockService lateClockService;
+
+    /**
+     * 查询校区打卡规则
+     *
+     * @param campus 规则校区
+     * @return
+     */
+    @GetMapping("queryCampusTime")
+    private CommonResult<List<CampusTime>> queryCampusTime(@RequestParam("campus") String campus) {
+        List<CampusTime> campusTimes = campusTimeService.queryCampusTime(campus);
+        return CommonResult.ok().setResult(campusTimes);
+    }
+
+    /**
+     * 新增修改校区打卡规则
+     *
+     * @param campusTimeLateClockDto
+     * @return
+     */
+    @PostMapping("saveOrUpdateCampusTime")
+    private CommonResult saveOrUpdateCampusTime(@RequestBody CampusTimeLateClockDto campusTimeLateClockDto) {
+        List<CampusTime> campusTimeList = campusTimeLateClockDto.getCampusTimeList();
+        //修改前先删除对应的校区打开时间规则
+        Boolean aBoolean = campusTimeService.removeCampusTime(campusTimeList.get(0).getCampus());
+        List<LateClock> lateClockList = new ArrayList<>();
+        lateClockList.add(campusTimeLateClockDto.getLateClock());
+        Boolean cBoolean = campusTimeService.saveOrUpdateCampusTime(campusTimeList);
+        Boolean lBoolean = Boolean.FALSE;
+        //在时间段规则修改成功之后,再执行早退规则修改
+        if (cBoolean) {
+            lBoolean = lateClockService.saveOrUpdateLateClock(lateClockList);
+        }
+        if (cBoolean && lBoolean) {
+            return CommonResult.ok();
+        } else {
+            return CommonResult.fail();
+        }
+    }
+}

+ 49 - 0
src/main/java/com/chuanghai/attendance/controller/LateClockController.java

@@ -0,0 +1,49 @@
+package com.chuanghai.attendance.controller;
+
+import com.chuanghai.attendance.common.utils.CommonResult;
+import com.chuanghai.attendance.entity.LateClock;
+import com.chuanghai.attendance.service.LateClockService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 迟到早退
+ *
+ * @Author: binguo
+ * @Date: 2022/9/20 星期二 17:01
+ * @Description: com.chuanghai.attendance.controller
+ * @Version: 1.0
+ */
+@RestController
+@RequestMapping("late")
+public class LateClockController {
+
+    @Autowired
+    private LateClockService lateClockService;
+
+    /**
+     * 查询迟到早退规则
+     *
+     * @param campus 规则校区
+     * @return
+     */
+    @GetMapping("queryLateClock")
+    private CommonResult<LateClock> queryLateClock(@RequestParam("campus") String campus) {
+        LateClock lateClock = lateClockService.queryCampusTime(campus);
+        return CommonResult.ok().setResult(lateClock);
+    }
+
+//    /**
+//     * 新增迟到早退规则
+//     * @param lateClock
+//     * @return
+//     */
+//    @PostMapping("saveOrUpdateLateClock")
+//    private CommonResult saveOrUpdateLateClock(@RequestBody LateClock lateClock){
+//        lateClockService.saveOrUpdateLateClock(lateClock);
+//        return CommonResult.ok();
+//    }
+}

+ 172 - 0
src/main/java/com/chuanghai/attendance/controller/MonthlySummaryController.java

@@ -0,0 +1,172 @@
+package com.chuanghai.attendance.controller;
+
+import com.chuanghai.attendance.common.exception.BizCodeEnume;
+import com.chuanghai.attendance.common.exception.RRException;
+import com.chuanghai.attendance.common.utils.CommonResult;
+import com.chuanghai.attendance.dto.MonthlyDto;
+import com.chuanghai.attendance.entity.DayDetails;
+import com.chuanghai.attendance.entity.DownLoadExcel;
+import com.chuanghai.attendance.entity.MonitorFoldLineTable;
+import com.chuanghai.attendance.entity.MonthlySummary;
+import com.chuanghai.attendance.service.*;
+import com.chuanghai.attendance.utils.FileExportUtil;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 月度汇总
+ *
+ * @Author: binguo
+ * @Date: 2022/10/9 星期日 16:00
+ * @Description: com.chuanghai.attendance.controller
+ * @Version: 1.0
+ */
+@RestController
+@RequestMapping("month")
+public class MonthlySummaryController {
+
+    @Autowired
+    private MonthlySummaryService monthlySummaryService;
+
+    @Autowired
+    private OriginalDataService originalDataService;
+
+    @Autowired
+    private WorkerIdentityService workerIdentityService;
+
+    @Autowired
+    private CampusTimeService campusTimeService;
+
+    @Autowired
+    private DayDetailsService dayDetailsService;
+
+    private static Boolean analyseStatu = Boolean.FALSE;
+
+
+    /**
+     * 读取钉钉考勤excel原始数据,添加至数据库
+     *
+     * @param file
+     * @throws Exception
+     */
+    @PostMapping("importExcel")
+    public CommonResult importMonthExcel(@RequestParam("file") MultipartFile file) {
+        Boolean aBoolean;
+        Boolean oBoolean;
+        Boolean wBoolean;
+        String msg = "执行失败";
+        try {
+            aBoolean = monthlySummaryService.importMonthDataExcel(file);
+            wBoolean = workerIdentityService.importWorkExcel(file);
+            oBoolean = originalDataService.importOriginalDataExcel(file);
+            if (aBoolean && oBoolean && wBoolean) {
+                msg = "执行成功";
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RRException(BizCodeEnume.FILE_IMPORT_ERROR);
+        }
+
+        return CommonResult.result("200", msg, aBoolean);
+    }
+
+
+    /**
+     * 分析钉钉原始数据
+     *
+     * @return
+     */
+    @GetMapping("analyse")
+    public CommonResult analyse() {
+        List<MonthlyDto> monthlyDtoList = monthlySummaryService.analyse();
+        monthlyDtoList.forEach(monthlyDto -> {
+            System.out.println(monthlyDto);
+        });
+        if (monthlyDtoList.size() > 0) {
+            analyseStatu = Boolean.TRUE;
+        }
+        return CommonResult.ok();
+    }
+
+    /**
+     * 查询解析是否完成
+     * @return
+     */
+    @GetMapping("queryAnalyseStatu")
+    public CommonResult queryAnalyseStatu() {
+        Boolean statu;
+        statu = analyseStatu;
+        if (statu) {
+            analyseStatu = Boolean.FALSE;
+        }
+        return CommonResult.ok().setResult(statu);
+    }
+
+
+    /**
+     * 下载excel数据
+     * @return
+     */
+    @GetMapping("downLoad")
+    public CommonResult<List<DownLoadExcel>> downLoad() {
+        List<MonthlyDto> MonthlyDtoList = monthlySummaryService.analyse();
+        List<MonthlySummary> monthlySummaryList = monthlySummaryService.queryAll();
+        List<DayDetails> dayDetailsList = dayDetailsService.list();
+        List<DownLoadExcel> downLoadExcelList = new ArrayList<>();
+        for (int m = 0; m < MonthlyDtoList.size(); m++) {
+            DownLoadExcel downLoadExcel = new DownLoadExcel();
+            MonthlyDto monthlyDto = MonthlyDtoList.get(m);
+
+            MonthlySummary monthlySummary = monthlySummaryList.get(m);
+            DayDetails dayDetails = dayDetailsList.get(m + 1);
+
+            BeanUtils.copyProperties(monthlyDto, downLoadExcel);
+            BeanUtils.copyProperties(monthlySummary, downLoadExcel);
+            BeanUtils.copyProperties(dayDetails, downLoadExcel);
+
+            downLoadExcelList.add(downLoadExcel);
+        }
+//        downLoadExcelList.forEach(downLoadExcel -> {
+//            System.out.println(downLoadExcel);
+//        });
+
+        return CommonResult.ok().setResult(downLoadExcelList);
+    }
+
+    @RequestMapping("te")
+    public void test(HttpServletResponse response) {
+        String[] titleStr = dayDetailsService.getTitleStr();
+
+        //假设这是需要导出的数据
+        List<DownLoadExcel> downLoadExcels = downLoad().getData();
+        List<MonitorFoldLineTable> data = new ArrayList();
+        //调用导出工具类
+        FileExportUtil.testExcelDemo(downLoadExcels, response, titleStr);
+
+    }
+
+    /**
+     * 获取月度 日标题
+     *
+     * @return
+     */
+    @GetMapping("title")
+    public CommonResult<DayDetails> getTitle() {
+        String[] titleStr = dayDetailsService.getTitleStr();
+
+        for (int m = 0; m < titleStr.length; m++) {
+            if (!StringUtils.hasText(titleStr[m])) {
+                titleStr[m] = "";
+            }
+        }
+
+        return CommonResult.ok().setResult(titleStr);
+    }
+}

+ 49 - 0
src/main/java/com/chuanghai/attendance/controller/OriginalDataController.java

@@ -0,0 +1,49 @@
+package com.chuanghai.attendance.controller;
+
+import com.chuanghai.attendance.common.exception.BizCodeEnume;
+import com.chuanghai.attendance.common.exception.RRException;
+import com.chuanghai.attendance.common.utils.CommonResult;
+import com.chuanghai.attendance.service.OriginalDataService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+/**
+ * 原始数据
+ *
+ * @Author: binguo
+ * @Date: 2022/10/8 星期六 16:01
+ * @Description: com.chuanghai.attendance.controller
+ * @Version: 1.0
+ */
+@RestController
+@RequestMapping("original")
+public class OriginalDataController {
+
+    @Autowired
+    private OriginalDataService originalDataService;
+
+    /**
+     * 读取钉钉考勤excel原始数据,添加至数据库
+     *
+     * @param file
+     * @throws Exception
+     */
+    @PostMapping("importOriginal")
+    public CommonResult importOriginalDataExcel(@RequestParam("file") MultipartFile file) {
+        Boolean aBoolean;
+        String msg;
+        try {
+            aBoolean = originalDataService.importOriginalDataExcel(file);
+            msg = aBoolean == Boolean.TRUE ? "执行成功" : "执行失败";
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RRException(BizCodeEnume.FILE_IMPORT_ERROR);
+        }
+        return CommonResult.result("200", msg, aBoolean);
+    }
+
+}

+ 48 - 0
src/main/java/com/chuanghai/attendance/controller/WorkerIdentityController.java

@@ -0,0 +1,48 @@
+package com.chuanghai.attendance.controller;
+
+import com.chuanghai.attendance.common.exception.BizCodeEnume;
+import com.chuanghai.attendance.common.exception.RRException;
+import com.chuanghai.attendance.common.utils.CommonResult;
+import com.chuanghai.attendance.service.WorkerIdentityService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+/**
+ * 员工身份
+ *
+ * @Author: binguo
+ * @Date: 2022/9/20 星期二 17:01
+ * @Description: com.chuanghai.attendance.controller
+ * @Version: 1.0
+ */
+@RestController
+@RequestMapping("work")
+public class WorkerIdentityController {
+
+    @Autowired
+    private WorkerIdentityService workerIdService;
+
+    /**
+     * 读取钉钉考勤excel原始数据,添加至数据库
+     *
+     * @param file
+     * @throws Exception
+     */
+    @PostMapping("importWork")
+    public CommonResult importWorkExcel(@RequestParam("file") MultipartFile file) {
+        Boolean aBoolean;
+        String msg;
+        try {
+            aBoolean = workerIdService.importWorkExcel(file);
+            msg = aBoolean == Boolean.TRUE ? "执行成功" : "执行失败";
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RRException(BizCodeEnume.FILE_IMPORT_ERROR);
+        }
+        return CommonResult.result("200", msg, aBoolean);
+    }
+}

+ 15 - 0
src/main/java/com/chuanghai/attendance/dao/CampusTimeDao.java

@@ -0,0 +1,15 @@
+package com.chuanghai.attendance.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.chuanghai.attendance.entity.CampusTime;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * @Author: binguo
+ * @Date: 2022/9/20 星期二 17:18
+ * @Description: com.chuanghai.attendance.dao
+ * @Version: 1.0
+ */
+@Mapper
+public interface CampusTimeDao extends BaseMapper<CampusTime> {
+}

+ 15 - 0
src/main/java/com/chuanghai/attendance/dao/DayDetailsDao.java

@@ -0,0 +1,15 @@
+package com.chuanghai.attendance.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.chuanghai.attendance.entity.DayDetails;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * @Author: binguo
+ * @Date: 2022/10/9 星期日 16:09
+ * @Description: com.chuanghai.attendance.dao
+ * @Version: 1.0
+ */
+@Mapper
+public interface DayDetailsDao extends BaseMapper<DayDetails> {
+}

+ 15 - 0
src/main/java/com/chuanghai/attendance/dao/LateClockDao.java

@@ -0,0 +1,15 @@
+package com.chuanghai.attendance.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.chuanghai.attendance.entity.LateClock;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * @Author: binguo
+ * @Date: 2022/9/20 星期二 17:18
+ * @Description: com.chuanghai.attendance.dao
+ * @Version: 1.0
+ */
+@Mapper
+public interface LateClockDao extends BaseMapper<LateClock> {
+}

+ 15 - 0
src/main/java/com/chuanghai/attendance/dao/MonthlySummaryDao.java

@@ -0,0 +1,15 @@
+package com.chuanghai.attendance.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.chuanghai.attendance.entity.MonthlySummary;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * @Author: binguo
+ * @Date: 2022/10/9 星期日 16:03
+ * @Description: com.chuanghai.attendance.dao
+ * @Version: 1.0
+ */
+@Mapper
+public interface MonthlySummaryDao extends BaseMapper<MonthlySummary> {
+}

+ 15 - 0
src/main/java/com/chuanghai/attendance/dao/OriginalDataDao.java

@@ -0,0 +1,15 @@
+package com.chuanghai.attendance.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.chuanghai.attendance.entity.OriginalData;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * @Author: binguo
+ * @Date: 2022/9/20 星期二 17:18
+ * @Description: com.chuanghai.attendance.dao
+ * @Version: 1.0
+ */
+@Mapper
+public interface OriginalDataDao extends BaseMapper<OriginalData> {
+}

+ 15 - 0
src/main/java/com/chuanghai/attendance/dao/WorkerIdentityDao.java

@@ -0,0 +1,15 @@
+package com.chuanghai.attendance.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.chuanghai.attendance.entity.WorkerIdentity;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * @Author: binguo
+ * @Date: 2022/9/20 星期二 17:18
+ * @Description: com.chuanghai.attendance.dao
+ * @Version: 1.0
+ */
+@Mapper
+public interface WorkerIdentityDao extends BaseMapper<WorkerIdentity> {
+}

+ 24 - 0
src/main/java/com/chuanghai/attendance/dto/CampusTimeLateClockDto.java

@@ -0,0 +1,24 @@
+package com.chuanghai.attendance.dto;
+
+import com.chuanghai.attendance.entity.CampusTime;
+import com.chuanghai.attendance.entity.LateClock;
+import lombok.Data;
+import lombok.ToString;
+
+import java.util.List;
+
+/**
+ * @Author: binguo
+ * @Date: 2022/10/12 星期三 9:24
+ * @Description: com.chuanghai.attendance.vo
+ * @Version: 1.0
+ */
+@Data
+@ToString
+public class CampusTimeLateClockDto {
+
+    private List<CampusTime> campusTimeList;
+
+    private LateClock lateClock;
+
+}

+ 102 - 0
src/main/java/com/chuanghai/attendance/dto/MonthlyDto.java

@@ -0,0 +1,102 @@
+package com.chuanghai.attendance.dto;
+
+import lombok.*;
+
+/**
+ *
+ * @Author: binguo
+ * @Date: 2022/10/11 星期二 16:30
+ * @Description: 用于接收月度汇总信息分析结果
+ * @Version: 1.0
+ */
+@Data
+@ToString
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class MonthlyDto {
+    /**
+     * 身份证
+     */
+    private String idCard;
+    /**
+     * 黄家湖出勤天数
+     */
+    private Integer daysOfAttendanceInHuangjiahu = 0;
+    /**
+     * 黄家湖缺卡天数
+     */
+    private String daysOfcardShortageInHuangjiahu = "";
+    /**
+     * 黄家湖缺卡记录
+     */
+    private String recondOfcardShortageInHuangjiahu = "";
+    /**
+     * 黄家湖总出勤天数 = 黄家湖出勤天数 + 黄家湖缺卡天数
+     */
+    private Integer sumAttendanceInHuangjiahu = 0;
+
+    /**
+     * 墨轩湖出勤天数
+     */
+    private Integer daysOfAttendanceInMoxuanhu = 0;
+    /**
+     * 墨轩湖缺卡天数
+     */
+    private String daysOfcardShortageInMoxuanhu = "";
+    /**
+     * 墨轩湖缺卡记录
+     */
+    private String recondOfcardShortageInMoxuanhu ="";
+    /**
+     * 墨轩湖总出勤天数 = 墨轩湖出勤天数 + 墨轩湖缺卡天数
+     */
+    private Integer sumAttendanceInMoxuanhu = 0;
+    /**
+     * 异常地点打卡天数
+     */
+    private Integer abnormalClockNumber = 0;
+    /**
+     * 异常地点打卡记录
+     */
+    private String abnormalClockRecond = "";
+    /**
+     * 0-20分钟迟到次数
+     */
+    private Integer leTwentyLateNumber = 0;
+    /**
+     * 20-30分钟迟到次数
+     */
+    private Integer lequalThirtyLateNumber = 0;
+    /**
+     * >30分钟迟到次数
+     */
+    private Integer geThirtyLateNumber = 0;
+    /**
+     * 迟到次数记录
+     */
+    private String lateOfRecond = "";
+    /**
+     * 0-20分钟早退次数
+     */
+    private Integer leTwentyLeaveNumber = 0;
+    /**
+     * 20-30分钟早退次数
+     */
+    private Integer lequalThirtyLeaveNumber = 0;
+    /**
+     * >30分钟早退次数
+     */
+    private Integer geThirtyLeaveNumber = 0;
+    /**
+     * 早退次数记录
+     */
+    private String leaveOfRecond = "";
+
+    /**
+     * 出勤天数=校区出勤天数+校区缺卡+校区异地
+     */
+    private Integer attendanceDays = 0;
+
+
+}

+ 53 - 0
src/main/java/com/chuanghai/attendance/entity/CampusTime.java

@@ -0,0 +1,53 @@
+package com.chuanghai.attendance.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import lombok.Data;
+import lombok.ToString;
+
+import java.io.Serializable;
+
+/**
+ * @Author: binguo
+ * @Date: 2022/10/11 星期二 9:16
+ * @Description: 用于接收系统设置中关于校区打卡时间段
+ * @Version: 1.0
+ */
+@Data
+@ToString
+public class CampusTime implements Serializable {
+
+    private static final long serialVersionUID = 5384247214018351416L;
+
+    /**
+     * 编号
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+    /**
+     * 校区
+     */
+    private String campus;
+    /**
+     * 考勤打卡时间段起始时间
+     */
+    private String startTime;
+    /**
+     * 考勤打卡时间段结束时间
+     */
+    private String endTime;
+    /**
+     * 考勤打卡时间段内打卡次数
+     */
+    private Integer count;
+    /**
+     * 考勤打卡时间段内打卡名称
+     */
+    private String clockName;
+    /**
+     * 控制打卡名称输入设置
+     */
+    private Boolean isShow;
+
+
+}

+ 159 - 0
src/main/java/com/chuanghai/attendance/entity/DayDetails.java

@@ -0,0 +1,159 @@
+package com.chuanghai.attendance.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import lombok.Data;
+import lombok.ToString;
+
+import java.io.Serializable;
+
+/**
+ * @Author: binguo
+ * @Date: 2022/10/9 星期日 14:13
+ * @Description: 用于记录钉钉考勤月度汇总的每日个人详情
+ * @Version: 1.0
+ */
+@Data
+@ToString
+public class DayDetails implements Serializable {
+    private static final long serialVersionUID = 355649617800991621L;
+    /**
+     * 编号
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+    /**
+     * userId
+     */
+    private String userId;
+    /**
+     * workName
+     */
+    private String workName;
+    /**
+     * 月度第1天
+     */
+    private String oneOfMonth;
+    /**
+     * 月度第2天
+     */
+    private String twoOfMonth;
+    /**
+     * 月度第3天
+     */
+    private String threeOfMonth;
+    /**
+     * 月度第4天
+     */
+    private String fourOfMonth;
+    /**
+     * 月度第5天
+     */
+    private String fiveOfMonth;
+    /**
+     * 月度第6天
+     */
+    private String sixOfMonth;
+    /**
+     * 月度第7天
+     */
+    private String sevenOfMonth;
+    /**
+     * 月度第8天
+     */
+    private String eightOfMonth;
+    /**
+     * 月度第9天
+     */
+    private String nineOfMonth;
+    /**
+     * 月度第10天
+     */
+    private String tenOfMonth;
+    /**
+     * 月度第11天
+     */
+    private String elevenOfMonth;
+    /**
+     * 月度第12天
+     */
+    private String twelveOfMonth;
+    /**
+     * 月度第13天
+     */
+    private String thirteenOfMonth;
+    /**
+     * 月度第14天
+     */
+    private String fourteenOfMonth;
+    /**
+     * 月度第15天
+     */
+    private String fifteenOfMonth;
+    /**
+     * 月度第16天
+     */
+    private String sixteenOfMonth;
+    /**
+     * 月度第17天
+     */
+    private String seventeenOfMonth;
+    /**
+     * 月度第18天
+     */
+    private String eighteenOfMonth;
+    /**
+     * 月度第19天
+     */
+    private String nineteenOfMonth;
+    /**
+     * 月度第20天
+     */
+    private String twentyOfMonth;
+    /**
+     * 月度第21天
+     */
+    private String twentyOneOfMonth;
+    /**
+     * 月度第22天
+     */
+    private String twentyTwoOfMonth;
+    /**
+     * 月度第23天
+     */
+    private String twentyThreeOfMonth;
+    /**
+     * 月度第24天
+     */
+    private String twentyFourOfMonth;
+    /**
+     * 月度第25天
+     */
+    private String twentyFiveOfMonth;
+    /**
+     * 月度第26天
+     */
+    private String twentySixOfMonth;
+    /**
+     * 月度第27天
+     */
+    private String twentySevenOfMonth;
+    /**
+     * 月度第28天
+     */
+    private String twentyEightOfMonth;
+    /**
+     * 月度第29天
+     */
+    private String twentyNineOfMonth;
+    /**
+     * 月度第30天
+     */
+    private String thirtyOfMonth;
+    /**
+     * 月度第31天
+     */
+    private String thirtyOneOfMonth;
+
+
+}

+ 336 - 0
src/main/java/com/chuanghai/attendance/entity/DownLoadExcel.java

@@ -0,0 +1,336 @@
+package com.chuanghai.attendance.entity;
+
+import lombok.Data;
+import lombok.ToString;
+
+import java.io.Serializable;
+
+/**
+ * @Author: binguo
+ * @Date: 2022/10/18 星期二 9:57
+ * @Description: com.chuanghai.attendance.entity
+ * @Version: 1.0
+ */
+@Data
+@ToString
+public class DownLoadExcel implements Serializable {
+
+    private static final long serialVersionUID = 1806225442801155417L;
+
+    /**
+     * 姓名
+     */
+    private String workName;
+    /**
+     * 考勤组
+     */
+    private String groupOfCheck;
+    /**
+     * 部门
+     */
+    private String department;
+    /**
+     * 工号
+     */
+    private String workNum;
+
+    /**
+     * 身份证
+     */
+    private String idCard;
+    /**
+     * 黄家湖出勤天数
+     */
+    private Integer daysOfAttendanceInHuangjiahu = 0;
+    /**
+     * 黄家湖缺卡天数
+     */
+    private String daysOfcardShortageInHuangjiahu = "";
+    /**
+     * 黄家湖缺卡记录
+     */
+    private String recondOfcardShortageInHuangjiahu = "";
+    /**
+     * 黄家湖总出勤天数 = 黄家湖出勤天数 + 黄家湖缺卡天数
+     */
+    private Integer sumAttendanceInHuangjiahu = 0;
+
+    /**
+     * 墨轩湖出勤天数
+     */
+    private Integer daysOfAttendanceInMoxuanhu = 0;
+    /**
+     * 墨轩湖缺卡天数
+     */
+    private String daysOfcardShortageInMoxuanhu = "";
+    /**
+     * 墨轩湖缺卡记录
+     */
+    private String recondOfcardShortageInMoxuanhu;
+    /**
+     * 墨轩湖总出勤天数 = 墨轩湖出勤天数 + 墨轩湖缺卡天数
+     */
+    private Integer sumAttendanceInMoxuanhu = 0;
+    /**
+     * 异常地点打卡天数
+     */
+    private Integer abnormalClockNumber = 0;
+    /**
+     * 异常地点打卡记录
+     */
+    private String abnormalClockRecond = "";
+    /**
+     * 0-20分钟迟到次数
+     */
+    private Integer leTwentyLateNumber = 0;
+    /**
+     * 20-30分钟迟到次数
+     */
+    private Integer lequalThirtyLateNumber = 0;
+    /**
+     * >30分钟迟到次数
+     */
+    private Integer geThirtyLateNumber = 0;
+    /**
+     * 迟到次数记录
+     */
+    private String lateOfRecond = "";
+    /**
+     * 0-20分钟早退次数
+     */
+    private Integer leTwentyLeaveNumber = 0;
+    /**
+     * 20-30分钟早退次数
+     */
+    private Integer lequalThirtyLeaveNumber = 0;
+    /**
+     * >30分钟早退次数
+     */
+    private Integer geThirtyLeaveNumber = 0;
+    /**
+     * 早退次数记录
+     */
+    private String leaveOfRecond = "";
+
+    /**
+     * 出勤天数=校区出勤天数+校区缺卡+校区异地
+     */
+    private Integer attendanceDays = 0;
+
+    /**
+     * userId
+     */
+    private String userId;
+    /**
+     * 出勤天数
+     */
+    private String attendanceDay;
+    /**
+     * 休息天数
+     */
+    private String restDay;
+    /**
+     * 工作时长
+     */
+    private String workHours;
+    /**
+     * 迟到次数
+     */
+    private String numberOfLate;
+    /**
+     * 迟到时长
+     */
+    private String numberOfLateTime;
+    /**
+     * 严重迟到次数
+     */
+    private String numberOfSeriousLateness;
+    /**
+     * 严重迟到时长
+     */
+    private String durationOfSevereLateness;
+    /**
+     * 旷工迟到天数
+     */
+    private String daysOfAbsenteeismAndLateness;
+    /**
+     * 早退次数
+     */
+    private String leaveEarlyNumber;
+    /**
+     * 早退时长
+     */
+    private String leaveEarlyTimes;
+    /**
+     * 上班缺卡次数
+     */
+    private String lackOfWorkCardTimes;
+    /**
+     * 下班缺卡次数
+     */
+    private String lackOfMissingWorkCardTimes;
+    /**
+     * 旷工天数
+     */
+    private String absenteeismDay;
+
+//    /**
+//     * 出差(天)
+//     */
+//    private String evectionDay;
+    /**
+     * 外出(天)
+     */
+    private String goOutDay;
+    /**
+     * 事假(天)
+     */
+    private String bereavementLeave;
+    /**
+     * 产假(天)
+     */
+    private String privateAffairLeaveDay;
+    /**
+     * 婚假(天)
+     */
+    private String maternityLeaveDay;
+    /**
+     * 病假(天)
+     */
+    private String sickLeaveDay;
+
+    /**
+     * 陪护假(天)
+     */
+    private String escortFalse;
+
+
+
+
+    /**
+     * 月度第1天
+     */
+    private String oneOfMonth;
+    /**
+     * 月度第2天
+     */
+    private String twoOfMonth;
+    /**
+     * 月度第3天
+     */
+    private String threeOfMonth;
+    /**
+     * 月度第4天
+     */
+    private String fourOfMonth;
+    /**
+     * 月度第5天
+     */
+    private String fiveOfMonth;
+    /**
+     * 月度第6天
+     */
+    private String sixOfMonth;
+    /**
+     * 月度第7天
+     */
+    private String sevenOfMonth;
+    /**
+     * 月度第8天
+     */
+    private String eightOfMonth;
+    /**
+     * 月度第9天
+     */
+    private String nineOfMonth;
+    /**
+     * 月度第10天
+     */
+    private String tenOfMonth;
+    /**
+     * 月度第11天
+     */
+    private String elevenOfMonth;
+    /**
+     * 月度第12天
+     */
+    private String twelveOfMonth;
+    /**
+     * 月度第13天
+     */
+    private String thirteenOfMonth;
+    /**
+     * 月度第14天
+     */
+    private String fourteenOfMonth;
+    /**
+     * 月度第15天
+     */
+    private String fifteenOfMonth;
+    /**
+     * 月度第16天
+     */
+    private String sixteenOfMonth;
+    /**
+     * 月度第17天
+     */
+    private String seventeenOfMonth;
+    /**
+     * 月度第18天
+     */
+    private String eighteenOfMonth;
+    /**
+     * 月度第19天
+     */
+    private String nineteenOfMonth;
+    /**
+     * 月度第20天
+     */
+    private String twentyOfMonth;
+    /**
+     * 月度第21天
+     */
+    private String twentyOneOfMonth;
+    /**
+     * 月度第22天
+     */
+    private String twentyTwoOfMonth;
+    /**
+     * 月度第23天
+     */
+    private String twentyThreeOfMonth;
+    /**
+     * 月度第24天
+     */
+    private String twentyFourOfMonth;
+    /**
+     * 月度第25天
+     */
+    private String twentyFiveOfMonth;
+    /**
+     * 月度第26天
+     */
+    private String twentySixOfMonth;
+    /**
+     * 月度第27天
+     */
+    private String twentySevenOfMonth;
+    /**
+     * 月度第28天
+     */
+    private String twentyEightOfMonth;
+    /**
+     * 月度第29天
+     */
+    private String twentyNineOfMonth;
+    /**
+     * 月度第30天
+     */
+    private String thirtyOfMonth;
+    /**
+     * 月度第31天
+     */
+    private String thirtyOneOfMonth;
+
+
+}

+ 37 - 0
src/main/java/com/chuanghai/attendance/entity/LateClock.java

@@ -0,0 +1,37 @@
+package com.chuanghai.attendance.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import lombok.Data;
+import lombok.ToString;
+
+/**
+ *
+ * @Author: binguo
+ * @Date: 2022/10/11 星期二 15:10
+ * @Description: 钉钉考勤迟到时间的设置
+ * @Version: 1.0
+ */
+@Data
+@ToString
+public class LateClock {
+
+    /**
+     * 编号
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+    /**
+     * 校区
+     */
+    private String campus;
+    /**
+     * 上班晚到时间分钟
+     */
+    private String lateOfWork;
+    /**
+     * 下班早走时间分钟
+     */
+    private String leaveEarlyOfWork ;
+
+}

+ 42 - 0
src/main/java/com/chuanghai/attendance/entity/MonitorFoldLineTable.java

@@ -0,0 +1,42 @@
+package com.chuanghai.attendance.entity;
+
+import lombok.Data;
+import lombok.ToString;
+
+/**
+ * @Author: binguo
+ * @Date: 2022/10/18 星期二 14:28
+ * @Description: com.chuanghai.attendance.entity
+ * @Version: 1.0
+ */
+@Data
+@ToString
+public class MonitorFoldLineTable {
+
+
+        private String cdbh;
+
+        private String sd;
+
+        private Double dcsg;
+
+        private Double dcdsf;
+
+        private Double dcc;
+
+        private Double ljsg;
+
+        private Double ljdsf;
+
+        private Double ljc;;
+
+        private Double bxsg;
+
+        private Double bxdsf;
+
+        private Double bxc;
+
+        private String time;
+
+
+}

+ 128 - 0
src/main/java/com/chuanghai/attendance/entity/MonthlySummary.java

@@ -0,0 +1,128 @@
+package com.chuanghai.attendance.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import lombok.Data;
+import lombok.ToString;
+
+import java.io.Serializable;
+
+/**
+ * @Author: binguo
+ * @Date: 2022/10/9 星期日 11:12
+ * @Description: 用于接收钉钉考勤月度总结的实体对象
+ * @Version: 1.0
+ */
+@Data
+@ToString
+public class MonthlySummary implements Serializable {
+
+    private static final long serialVersionUID = -7578594712649052700L;
+    /**
+     * 员工编号
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+    /**
+     * 姓名
+     */
+    private String workName;
+    /**
+     * 考勤组
+     */
+    private String groupOfCheck;
+    /**
+     * 部门
+     */
+    private String department;
+    /**
+     * 工号
+     */
+    private String workNum;
+    /**
+     * userId
+     */
+    private String userId;
+    /**
+     * 出勤天数
+     */
+    private String attendanceDay;
+    /**
+     * 休息天数
+     */
+    private String restDay;
+    /**
+     * 工作时长
+     */
+    private String workHours;
+    /**
+     * 迟到次数
+     */
+    private String numberOfLate;
+    /**
+     * 迟到时长
+     */
+    private String numberOfLateTime;
+    /**
+     * 严重迟到次数
+     */
+    private String numberOfSeriousLateness;
+    /**
+     * 严重迟到时长
+     */
+    private String durationOfSevereLateness;
+    /**
+     * 旷工迟到天数
+     */
+    private String daysOfAbsenteeismAndLateness;
+    /**
+     * 早退次数
+     */
+    private String leaveEarlyNumber;
+    /**
+     * 早退时长
+     */
+    private String leaveEarlyTimes;
+    /**
+     * 上班缺卡次数
+     */
+    private String lackOfWorkCardTimes;
+    /**
+     * 下班缺卡次数
+     */
+    private String lackOfMissingWorkCardTimes;
+    /**
+     * 旷工天数
+     */
+    private String absenteeismDay;
+//    /**
+//     * 出差(天)
+//     */
+//    private String evectionDay;
+    /**
+     * 外出(天)
+     */
+    private String goOutDay;
+    /**
+     * 事假(天)
+     */
+    private String bereavementLeave;
+    /**
+     * 产假(天)
+     */
+    private String privateAffairLeaveDay;
+    /**
+     * 婚假(天)
+     */
+    private String maternityLeaveDay;
+    /**
+     * 病假(天)
+     */
+    private String sickLeaveDay;
+    /**
+     * 陪护假(天)
+     */
+    private String escortFalse;
+
+
+}

+ 58 - 0
src/main/java/com/chuanghai/attendance/entity/OriginalData.java

@@ -0,0 +1,58 @@
+package com.chuanghai.attendance.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import lombok.Data;
+import lombok.ToString;
+
+import java.io.Serializable;
+
+/**
+ * @Author: binguo
+ * @Date: 2022/10/8 星期六 15:31
+ * @Description: 钉钉原始数据收集的实体对象
+ * @Version: 1.0
+ */
+@Data
+@ToString
+public class OriginalData implements Serializable {
+    private static final long serialVersionUID = -5054784515239058437L;
+    /**
+     * 编号
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+    /**
+     * 姓名
+     */
+    private String workName;
+    /**
+     * 工号
+     */
+    private String workNum;
+    /**
+     * userId
+     */
+    private String userId;
+    /**
+     * 考勤日期
+     */
+    private String dateOfAttendance;
+//    /**
+//     * 考勤时间
+//     */
+//    private String timeOfAttendance;
+    /**
+     * 打卡时间
+     */
+    private String clockTime;
+    /**
+     * 打卡地址
+     */
+    private String clockAddress;
+    /**
+     * 打卡设备
+     */
+    private String clockEquipment;
+
+}

+ 44 - 0
src/main/java/com/chuanghai/attendance/entity/WorkerIdentity.java

@@ -0,0 +1,44 @@
+package com.chuanghai.attendance.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import lombok.Data;
+import lombok.ToString;
+
+import java.io.Serializable;
+
+/**
+ * @Author: binguo
+ * @Date: 2022/10/10 星期一 17:02
+ * @Description: 员工身份证信息
+ * @Version: 1.0
+ */
+@Data
+@ToString
+public class WorkerIdentity implements Serializable {
+
+    private static final long serialVersionUID = 5369496806164220442L;
+    /**
+     * 编号
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+    /**
+     * 员工工号
+     */
+    private String workNum;
+
+    /**
+     * 员工姓名
+     */
+    private String workName;
+    /**
+     * 身份证
+     */
+    private String idCard;
+    /**
+     * userId
+     */
+    private String userId;
+
+}

+ 20 - 0
src/main/java/com/chuanghai/attendance/service/CampusTimeService.java

@@ -0,0 +1,20 @@
+package com.chuanghai.attendance.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.chuanghai.attendance.entity.CampusTime;
+
+import java.util.List;
+
+/**
+ * @Author: binguo
+ * @Date: 2022/9/20 星期二 17:15
+ * @Description: com.chuanghai.attendance.service.impl
+ * @Version: 1.0
+ */
+public interface CampusTimeService extends IService<CampusTime> {
+    List<CampusTime> queryCampusTime( String campus);
+
+    Boolean saveOrUpdateCampusTime(List<CampusTime> campusTimeList);
+
+    Boolean removeCampusTime(String campus);
+}

+ 18 - 0
src/main/java/com/chuanghai/attendance/service/DayDetailsService.java

@@ -0,0 +1,18 @@
+package com.chuanghai.attendance.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.chuanghai.attendance.entity.DayDetails;
+
+/**
+ * @Author: binguo
+ * @Date: 2022/10/9 星期日 16:09
+ * @Description: com.chuanghai.attendance.service
+ * @Version: 1.0
+ */
+public interface DayDetailsService extends IService<DayDetails> {
+
+     Boolean removeDayDetails();
+
+     String[] getTitleStr();
+
+}

+ 18 - 0
src/main/java/com/chuanghai/attendance/service/LateClockService.java

@@ -0,0 +1,18 @@
+package com.chuanghai.attendance.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.chuanghai.attendance.entity.LateClock;
+
+import java.util.List;
+
+/**
+ * @Author: binguo
+ * @Date: 2022/9/20 星期二 17:15
+ * @Description: com.chuanghai.attendance.service.impl
+ * @Version: 1.0
+ */
+public interface LateClockService extends IService<LateClock> {
+    LateClock queryCampusTime(String campus);
+
+    Boolean saveOrUpdateLateClock(List<LateClock> lateClockList);
+}

+ 25 - 0
src/main/java/com/chuanghai/attendance/service/MonthlySummaryService.java

@@ -0,0 +1,25 @@
+package com.chuanghai.attendance.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.chuanghai.attendance.dto.MonthlyDto;
+import com.chuanghai.attendance.entity.MonthlySummary;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.List;
+
+/**
+ * @Author: binguo
+ * @Date: 2022/10/9 星期日 16:05
+ * @Description: com.chuanghai.attendance.service
+ * @Version: 1.0
+ */
+public interface MonthlySummaryService extends IService<MonthlySummary> {
+
+    Boolean importMonthDataExcel(MultipartFile file) throws Exception;
+
+    List<MonthlySummary> queryByWorkName(String workName);
+
+    List<MonthlySummary> queryAll();
+
+    List<MonthlyDto> analyse();
+}

+ 22 - 0
src/main/java/com/chuanghai/attendance/service/OriginalDataService.java

@@ -0,0 +1,22 @@
+package com.chuanghai.attendance.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.chuanghai.attendance.entity.OriginalData;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.List;
+
+/**
+ * @Author: binguo
+ * @Date: 2022/9/20 星期二 17:15
+ * @Description: com.chuanghai.attendance.service.impl
+ * @Version: 1.0
+ */
+public interface OriginalDataService extends IService<OriginalData> {
+
+    Boolean importOriginalDataExcel(MultipartFile file) throws Exception;
+
+    List<OriginalData> queryByUserId(String userId, String workNum);
+
+    List<OriginalData> queryByTimeAndUserId(String date, String userId, String workNum);
+}

+ 16 - 0
src/main/java/com/chuanghai/attendance/service/WorkerIdentityService.java

@@ -0,0 +1,16 @@
+package com.chuanghai.attendance.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.chuanghai.attendance.entity.WorkerIdentity;
+import org.springframework.web.multipart.MultipartFile;
+
+/**
+ * @Author: binguo
+ * @Date: 2022/9/20 星期二 17:15
+ * @Description: com.chuanghai.attendance.service.impl
+ * @Version: 1.0
+ */
+public interface WorkerIdentityService extends IService<WorkerIdentity> {
+    Boolean importWorkExcel(MultipartFile file) throws Exception;
+    String queryByuserId(String userId, String workNum);
+}

+ 45 - 0
src/main/java/com/chuanghai/attendance/service/impl/CampusTimeServiceImpl.java

@@ -0,0 +1,45 @@
+package com.chuanghai.attendance.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.chuanghai.attendance.common.exception.BizCodeEnume;
+import com.chuanghai.attendance.common.exception.RRException;
+import com.chuanghai.attendance.dao.CampusTimeDao;
+import com.chuanghai.attendance.entity.CampusTime;
+import com.chuanghai.attendance.service.CampusTimeService;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * @Author: binguo
+ * @Date: 2022/9/20 星期二 17:16
+ * @Description: com.chuanghai.attendance.service.impl
+ * @Version: 1.0
+ */
+@Service("campusTimeService")
+public class CampusTimeServiceImpl extends ServiceImpl<CampusTimeDao, CampusTime> implements CampusTimeService {
+
+    @Override
+    public List<CampusTime> queryCampusTime(String campus) {
+        QueryWrapper<CampusTime> wrapper = new QueryWrapper<>();
+        wrapper.eq("campus",campus);
+        List<CampusTime> list = this.list(wrapper);
+        if (list.size() == 0) {
+            throw new RRException(BizCodeEnume.DATA_IS_NOT_EXIST,campus+"不存在");
+        }
+        return list;
+    }
+
+    @Override
+    public Boolean saveOrUpdateCampusTime(List<CampusTime> campusTimeList) {
+        return this.saveOrUpdateBatch(campusTimeList);
+    }
+
+    @Override
+    public Boolean removeCampusTime(String campus) {
+        QueryWrapper<CampusTime> wrapper = new QueryWrapper<>();
+        wrapper.eq("campus",campus);
+        return this.remove(wrapper);
+    }
+}

+ 77 - 0
src/main/java/com/chuanghai/attendance/service/impl/DayDetailsServiceImpl.java

@@ -0,0 +1,77 @@
+package com.chuanghai.attendance.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.chuanghai.attendance.dao.DayDetailsDao;
+import com.chuanghai.attendance.entity.DayDetails;
+import com.chuanghai.attendance.service.DayDetailsService;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @Author: binguo
+ * @Date: 2022/10/9 星期日 16:10
+ * @Description: com.chuanghai.attendance.service.impl
+ * @Version: 1.0
+ */
+@Service("dayDetailsService")
+public class DayDetailsServiceImpl extends ServiceImpl<DayDetailsDao, DayDetails> implements DayDetailsService {
+
+    @Override
+    public Boolean removeDayDetails(){
+        List<Long> idList = new ArrayList<>();
+        List<DayDetails> dayDetailsList = this.list();
+        if (dayDetailsList.size() == 0) {
+            return Boolean.TRUE;
+        }
+        dayDetailsList.forEach(dayDetails -> {
+            idList.add(dayDetails.getId());
+        });
+        return this.removeByIds(idList);
+    }
+
+    @Override
+    public String[] getTitleStr() {
+        List<DayDetails> list = this.list();
+        DayDetails dayDetails = list.get(0);
+        String[] titleStr = new String[31];
+        titleStr[0] = dayDetails.getOneOfMonth();
+        titleStr[1] = dayDetails.getTwoOfMonth();
+        titleStr[2] = dayDetails.getThreeOfMonth();
+        titleStr[3] = dayDetails.getFourOfMonth();
+        titleStr[4] = dayDetails.getFiveOfMonth();
+        titleStr[5] = dayDetails.getSixOfMonth();
+        titleStr[6] = dayDetails.getSevenOfMonth();
+        titleStr[7] = dayDetails.getEightOfMonth();
+        titleStr[8] = dayDetails.getNineOfMonth();
+        titleStr[9] = dayDetails.getTenOfMonth();
+        titleStr[10] = dayDetails.getElevenOfMonth();
+        titleStr[11] = dayDetails.getTwelveOfMonth();
+        titleStr[12] = dayDetails.getThirteenOfMonth();
+        titleStr[13] = dayDetails.getFourteenOfMonth();
+        titleStr[14] = dayDetails.getFifteenOfMonth();
+        titleStr[15] = dayDetails.getSixteenOfMonth();
+        titleStr[16] = dayDetails.getSeventeenOfMonth();
+        titleStr[17] = dayDetails.getEighteenOfMonth();
+        titleStr[18] = dayDetails.getNineteenOfMonth();
+        titleStr[19] = dayDetails.getTwentyOfMonth();
+        titleStr[20] = dayDetails.getTwentyOneOfMonth();
+        titleStr[21] = dayDetails.getTwentyTwoOfMonth();
+        titleStr[22] = dayDetails.getTwentyThreeOfMonth();
+        titleStr[23] = dayDetails.getTwentyFourOfMonth();
+        titleStr[24] = dayDetails.getTwentyFiveOfMonth();
+        titleStr[25] = dayDetails.getTwentySixOfMonth();
+        titleStr[26] = dayDetails.getTwentySevenOfMonth();
+        titleStr[27] = dayDetails.getTwentyEightOfMonth();
+        titleStr[28] = dayDetails.getTwentyNineOfMonth();
+        titleStr[29] = dayDetails.getThirtyOfMonth();
+        titleStr[30] = dayDetails.getThirtyOneOfMonth();
+
+        return titleStr;
+    }
+
+
+
+
+}

+ 37 - 0
src/main/java/com/chuanghai/attendance/service/impl/LateClockServiceImpl.java

@@ -0,0 +1,37 @@
+package com.chuanghai.attendance.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.chuanghai.attendance.common.exception.BizCodeEnume;
+import com.chuanghai.attendance.common.exception.RRException;
+import com.chuanghai.attendance.dao.LateClockDao;
+import com.chuanghai.attendance.entity.LateClock;
+import com.chuanghai.attendance.service.LateClockService;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * @Author: binguo
+ * @Date: 2022/9/20 星期二 17:16
+ * @Description: com.chuanghai.attendance.service.impl
+ * @Version: 1.0
+ */
+@Service("lateClockService")
+public class LateClockServiceImpl extends ServiceImpl<LateClockDao, LateClock> implements LateClockService {
+    @Override
+    public LateClock queryCampusTime(String campus) {
+        QueryWrapper<LateClock> wrapper = new QueryWrapper<>();
+        wrapper.eq("campus",campus);
+        LateClock lateClock = this.getOne(wrapper);
+        if (lateClock == null) {
+            throw new RRException(BizCodeEnume.DATA_IS_NOT_EXIST,campus+"不存在");
+        }
+        return lateClock;
+    }
+
+    @Override
+    public Boolean saveOrUpdateLateClock(List<LateClock> lateClockList) {
+       return this.saveOrUpdateBatch(lateClockList);
+    }
+}

Разница между файлами не показана из-за своего большого размера
+ 1129 - 0
src/main/java/com/chuanghai/attendance/service/impl/MonthlySummaryServiceImpl.java


+ 109 - 0
src/main/java/com/chuanghai/attendance/service/impl/OriginalDataServiceImpl.java

@@ -0,0 +1,109 @@
+package com.chuanghai.attendance.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.chuanghai.attendance.dao.OriginalDataDao;
+import com.chuanghai.attendance.entity.OriginalData;
+import com.chuanghai.attendance.service.OriginalDataService;
+import com.chuanghai.attendance.utils.excel.ExcelImportXLSXUtil;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @Author: binguo
+ * @Date: 2022/9/20 星期二 17:16
+ * @Description: com.chuanghai.attendance.service.impl
+ * @Version: 1.0
+ */
+@Service("originalDataService")
+public class OriginalDataServiceImpl extends ServiceImpl<OriginalDataDao, OriginalData> implements OriginalDataService {
+
+    /**
+     * 导入钉钉考勤原始数据信息
+     *
+     * @param file
+     * @throws Exception
+     */
+    public Boolean importOriginalDataExcel(MultipartFile file) throws Exception {
+        Boolean aBoolean = removeOriginalDataIds();
+        List<OriginalData> originalDataList = new ArrayList<>();
+        if (aBoolean) {
+            List<String[]> list = ExcelImportXLSXUtil.readerExcel(file.getInputStream(), "原始记录", 20);
+
+
+            int count = 0;
+            for (String[] strings : list) {
+                count++;
+                if (count < 4) {
+                    continue;
+                }
+                //System.out.println(strings[9]);
+                OriginalData originalData = new OriginalData();
+                if (StringUtils.hasText(strings[9]) && !strings[9].contains("打卡无效")) {
+
+
+                    originalData.setWorkName(strings[0]);
+                    originalData.setWorkNum(strings[3]);
+                    originalData.setUserId(strings[5]);
+                    originalData.setDateOfAttendance(strings[6]);
+                    //originalData.setTimeOfAttendance(strings[7]);
+                    originalData.setClockTime(strings[8]);
+                    originalData.setClockAddress(strings[10]);
+                    originalData.setClockEquipment(strings[15]);
+
+                    originalDataList.add(originalData);
+                }
+            }
+        }
+
+        return this.saveBatch(originalDataList, originalDataList.size());
+    }
+
+    @Override
+    public List<OriginalData> queryByUserId(String userId, String workNum) {
+        QueryWrapper<OriginalData> wrapper = new QueryWrapper<>();
+        wrapper.eq("user_id", userId);
+        List<OriginalData> list = this.list(wrapper);
+        if (list.size() == 0) {
+            QueryWrapper<OriginalData> wrapper1 = new QueryWrapper<>();
+            wrapper1.eq("work_num", workNum);
+            list = this.list(wrapper1);
+        }
+        if (list.size() == 0) {
+            //  throw new RRException(BizCodeEnume.DATA_IS_NOT_EXIST, userId + "userId workNum不存在");
+        }
+        return list;
+    }
+
+    @Override
+    public List<OriginalData> queryByTimeAndUserId(String date, String userId, String workNum) {
+        QueryWrapper<OriginalData> wrapper = new QueryWrapper<>();
+        wrapper.eq("date_of_attendance", date)
+                .eq("user_id", userId);
+        List<OriginalData> list = this.list(wrapper);
+        if (list.size() == 0) {
+            QueryWrapper<OriginalData> wrapper1 = new QueryWrapper<>();
+            wrapper1.eq("date_of_attendance", date)
+                    .eq("work_num", workNum);
+            list = this.list(wrapper1);
+        }
+        return list;
+    }
+
+
+    public Boolean removeOriginalDataIds() {
+        List<OriginalData> list = this.list();
+        if (list.size() == 0) {
+            return Boolean.TRUE;
+        }
+        List<Long> originalDataIds = new ArrayList<>();
+        list.forEach(originalData -> {
+            originalDataIds.add(originalData.getId());
+        });
+        return this.removeByIds(originalDataIds);
+    }
+}

+ 81 - 0
src/main/java/com/chuanghai/attendance/service/impl/WorkerIdentityServiceImpl.java

@@ -0,0 +1,81 @@
+package com.chuanghai.attendance.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.chuanghai.attendance.common.exception.BizCodeEnume;
+import com.chuanghai.attendance.common.exception.RRException;
+import com.chuanghai.attendance.dao.WorkerIdentityDao;
+import com.chuanghai.attendance.entity.WorkerIdentity;
+import com.chuanghai.attendance.service.WorkerIdentityService;
+import com.chuanghai.attendance.utils.excel.ExcelImportXLSXUtil;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @Author: binguo
+ * @Date: 2022/9/20 星期二 17:16
+ * @Description: com.chuanghai.attendance.service.impl
+ * @Version: 1.0
+ */
+@Service("workerIdentityService")
+public class WorkerIdentityServiceImpl extends ServiceImpl<WorkerIdentityDao, WorkerIdentity> implements WorkerIdentityService {
+    @Override
+    public Boolean importWorkExcel(MultipartFile file) throws Exception {
+        List<WorkerIdentity> workerIdList = new ArrayList<>();
+
+        List<String[]> list = ExcelImportXLSXUtil.readerExcel(file.getInputStream(), "员工身份", 4);
+        Integer count = 0;
+        for (String[] strings : list) {
+            count++;
+            if (count < 2) {
+                continue;
+            }
+            WorkerIdentity workerIdentity = new WorkerIdentity();
+            workerIdentity.setWorkNum(strings[0]);
+            workerIdentity.setWorkName(strings[1]);
+            workerIdentity.setIdCard(strings[2]);
+            workerIdentity.setUserId(strings[3]);
+            WorkerIdentity queryByIdCard = queryByIdCard(strings[2]);
+            if (queryByIdCard == null) {
+                workerIdList.add(workerIdentity);
+            }
+
+        }
+        if (workerIdList.size() > 0 ) {
+            return this.saveBatch(workerIdList, workerIdList.size());
+        }else {
+            return Boolean.TRUE;
+        }
+
+    }
+
+    @Override
+    public String queryByuserId(String userId, String workNum) {
+        QueryWrapper<WorkerIdentity> wrapper = new QueryWrapper<>();
+        wrapper.eq("user_id",userId);
+      //  System.out.println(userId);
+        WorkerIdentity one = this.getOne(wrapper);
+        if (one == null) {
+            if (StringUtils.hasText(workNum)) {
+                QueryWrapper<WorkerIdentity> wrapper1 = new QueryWrapper<>();
+                wrapper1.eq("work_num",workNum);
+                one = this.getOne(wrapper1);
+            }
+        }
+        if (one == null) {
+            throw new RRException(BizCodeEnume.DATA_IS_NOT_EXIST,userId+" 用户ID不存在");
+        }
+        String idCard = one.getIdCard();
+        return idCard;
+    }
+
+    public WorkerIdentity queryByIdCard(String idCard) {
+        QueryWrapper<WorkerIdentity> wrapper = new QueryWrapper<>();
+        wrapper.eq("id_card", idCard);
+        return this.getOne(wrapper);
+    }
+}

+ 304 - 0
src/main/java/com/chuanghai/attendance/utils/DateUtil.java

@@ -0,0 +1,304 @@
+package com.chuanghai.attendance.utils;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.List;
+
+/**
+ * @Author: binguo
+ * @Date: 2022/9/21 星期三 9:59
+ * @Description: com.chuanghai.attendance.utils
+ * @Version: 1.0
+ */
+public class DateUtil {
+
+
+    /**
+     * 获取当月的日期标识出周末
+     *
+     * @param timeStamp
+     * @return
+     */
+    public static List getMonthDate1(String timeStamp) {
+        List list = new ArrayList();
+        String[] split = timeStamp.split("-");
+        String year = split[0];
+        String month = split[1];
+        Calendar calendar = Calendar.getInstance();
+        calendar.set(Calendar.YEAR, Integer.parseInt(year));
+        calendar.set(Calendar.MONTH, Integer.parseInt(month) - 1);
+        int day = calendar.getActualMaximum(Calendar.DATE);
+
+        for (int i = 1; i <= day; i++) {
+            String days = "";
+            if (i < 10) {
+                days = "0" + i;
+            } else {
+                days = String.valueOf(i);
+            }
+            String days1 = "";
+            //在第一天的基础上加1
+            calendar.add(Calendar.DATE, 1);
+            int week = calendar.get(Calendar.DAY_OF_WEEK);
+            // 1代表周日,7代表周六 判断这是一个星期的第几天从而判断是否是周末
+            if (week == Calendar.SATURDAY) {
+                list.add(days + "-六");
+            } else if (week == Calendar.SUNDAY) {
+                list.add(days + "-日");
+            } else {
+                String aDate = days;
+                list.add(aDate);
+            }
+        }
+        return list;
+    }
+
+    /**
+     * 获取当月的日期和周末
+     *
+     * @param timeStamp
+     * @return
+     */
+    public static List getMonthDate(String timeStamp) {
+        List list = new ArrayList();
+        String[] split = timeStamp.split("-");
+        String year = split[0];
+        String month = split[1];
+        Calendar calendar = Calendar.getInstance();
+        calendar.set(Calendar.YEAR, Integer.parseInt(year));
+        calendar.set(Calendar.MONTH, Integer.parseInt(month) - 1);
+        int day = calendar.getActualMaximum(Calendar.DATE);
+        for (int i = 1; i <= day; i++) {
+            String days = "";
+            if (i < 10) {
+                days = "0" + i;
+            } else {
+                days = String.valueOf(i);
+            }
+            String aDate = year + "-" + month + "-" + days;
+            list.add(aDate);
+        }
+        return list;
+    }
+
+    /**
+     * 获当月的所有周末
+     *
+     * @param timeStamp
+     * @return
+     */
+    public static List getWeekend(String timeStamp) {
+        List list = new ArrayList();
+        String[] split = timeStamp.split("-");
+        String year = split[0];
+        String month = split[1];
+        Calendar calendar = Calendar.getInstance();
+        calendar.set(Calendar.YEAR, Integer.parseInt(year));
+        calendar.set(Calendar.MONTH, Integer.parseInt(month) - 1);
+        // 设置为当月第一天
+        calendar.set(Calendar.DAY_OF_MONTH, 1);
+        // 当月最大天数
+        int daySize = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
+        for (int i = 0; i < daySize - 1; i++) {
+            String days = "";
+            //在第一天的基础上加1
+            calendar.add(Calendar.DATE, 1);
+            int week = calendar.get(Calendar.DAY_OF_WEEK);
+            // 1代表周日,7代表周六 判断这是一个星期的第几天从而判断是否是周末
+            if (week == Calendar.SATURDAY || week == Calendar.SUNDAY) {
+                int ct = calendar.get(Calendar.DAY_OF_MONTH);
+                if (ct < 10) {
+                    days = "0" + ct;
+                } else {
+                    days = String.valueOf(ct);
+                }
+                // 得到当天是一个月的第几天
+                list.add(year + "-" + month + "-" + days);
+            }
+        }
+        return list;
+    }
+
+    static int  m= 0;
+    public static String getCount(String str, Integer shortageCount) {
+        m++;
+
+        String[] split = str.split("\r\n");
+        int indexT = split[shortageCount - 1].indexOf("天");
+        int indexC = split[shortageCount - 1].indexOf("次");
+
+        String count = split[shortageCount - 1].substring(indexC + 1, indexT);
+        Integer countNum = Integer.parseInt(count) + 1;
+        String oneStr = split[shortageCount - 1];
+        String replaceAll = oneStr.replaceAll(count, "yy");
+        //判断重复出现的123
+        if (replaceAll.length() - oneStr.length() >1) {
+            oneStr = oneStr.replaceAll("yy", count);
+            oneStr = split[shortageCount - 1].replaceFirst(count, "HHHH");
+        }
+
+        String twoStr = oneStr.replace(count, String.valueOf(countNum));
+
+        String result = twoStr.replace("HHHH", count);
+        String replace = str.replace(split[shortageCount - 1], result);
+        int c = shortageCount-1;
+        return replace;
+    }
+
+
+
+    public static void main(String[] args) {
+
+//        String[] strings = new String[2];
+//        strings[0] = "1";
+//        strings[1] = "2";
+//        String[] strings1 = new String[3];
+//        strings1[0]="q";
+//        strings1[1]="w";
+//        strings1[2]="e";
+//
+//        String[] s = new String[strings.length+strings1.length];
+//
+//        for(int m = 0;m<strings.length;m++){
+//            s[m] = strings[m];
+//        }
+//        for(int m = 0;m<strings1.length;m++){
+//            s[strings.length+m] = strings1[m];
+//        }
+//        for(String s1:s){
+//            System.out.println(s1);
+//        }
+
+
+        String count = getCount("缺卡1次0天\r\n" +
+
+                "缺卡2次0天\r\n" +
+                "缺卡3次0天", 1);
+        System.out.println(count);
+
+
+//        Integer compareTo = 0;
+//        Long csTime = 0l;
+//        try {
+//
+//            String clockTime = "2021-11-30 07:31:00";
+//
+//
+//            String clockTimeStart = "2021-11-30 00:00:00";
+//            String clockTimeEnd = "2021-11-30 08:00:00";
+//
+//            Integer o = Integer.parseInt(clockTimeEnd.split(":")[2])- 40;
+//            String ss = clockTimeEnd.split(":")[0]+":" + clockTimeEnd.split(":")[1] +":"+ o;
+//
+//
+//            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+//            String clockTimeNow = format(format.parse(clockTime));
+//            System.out.println(clockTimeNow + " clockTimeNow");
+//            System.out.println(ss);
+//            String ssss = format(format.parse(ss));
+//            System.out.println(ssss);
+//
+//            String checkStartTime = format(format.parse(clockTimeStart));
+//            long clockTimeLong = format.parse(clockTime).getTime();
+//            long startTimeLong = format.parse(checkStartTime).getTime();
+//            //打卡时间 - 考勤时间
+//            csTime = (clockTimeLong - startTimeLong)/1000/60;
+//            // checkStartTime 早于 clockTimeNow    compareTo>0    1 true
+//           compareTo = clockTimeNow.compareTo(checkStartTime);
+//            System.out.println(compareTo + " 判断迟到");
+//            System.out.println(checkStartTime + " checkStartTime");
+//            if (compareTo  > 0) {
+//                //当前时间已经计算了早退时间
+//                String checkEndTime = format(format.parse(clockTimeEnd));
+//                long endTimeLong = format.parse(checkEndTime).getTime();
+//                //打卡时间 - 考勤时间
+//                csTime = (clockTimeLong - endTimeLong)/1000/60;
+//                // checkEndTime 晚于 clockTimeNow    compareTo>0  迟到    1  true
+//                compareTo = checkEndTime.compareTo(clockTimeNow);
+//                System.out.println(checkEndTime + " checkEndTime");
+//                System.out.println(compareTo + " 判断迟到");
+//
+//            }
+//        }catch (Exception e){
+//
+//        }
+//
+//        Boolean statu = compareTo > 0 ? Boolean.TRUE : Boolean.FALSE;
+//        System.out.println(statu  +" " +csTime);
+
+
+//        System.err.println(getWeekend("2022-09"));
+//        System.err.println(getMonthDate("2022-09"));
+//        System.err.println(getMonthDate1("2022-10"));
+//        int m = 200;
+//        int addPerson = (int) (m / 52);
+//        System.out.println(addPerson + "-----------");
+//        int intoNum = 0;
+//        int leaveNum = 0;
+//        int count1 = 0;
+//        int hh = 0;
+//        if ((int) Math.random() * 100 > 55) {
+//            hh = 8;
+//        } else {
+//            hh = 6;
+//        }
+//        ArrayList<Integer> objects = new ArrayList<>();
+//
+//        while (count1 < 53) {
+//
+//
+//            count1++;
+//            String format = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+//            if (Integer.parseInt(format.split(" ")[1].split(":")[0]) > 8) {
+//
+//
+////
+////                int count = (int) (Math.random() * 10);
+////                if (count == 0 || count == 5) {
+////                    intoNum += (addPerson * 4);
+////                } else if (count == 10) {
+////                    intoNum += (addPerson * 2);
+////                } else if (count > 0 && count < 4) {
+////                    intoNum += (int) (Math.random() * 5);
+////                } else {
+////                    intoNum += (int) (Math.random() * addPerson * 2);
+////
+////                }
+//
+//                intoNum = intoNum + 2 + (int) (Math.random() * (addPerson * 2));
+//
+//                leaveNum = intoNum - 1 - (int) (Math.random() * 6);
+////        	leaveNum = intoNum - 1;
+//                if (leaveNum < 0) {
+//                    leaveNum = intoNum;
+//                }
+//
+//
+////             intoNum =intoNum + 2 + (int) (Math.random() * (addPerson * 2));
+////
+////	        leaveNum = intoNum - 1 - (int) (Math.random()* 6);
+//////        	leaveNum = intoNum - 1;
+////	        if (leaveNum < 0 ) {
+////	        	leaveNum = intoNum;
+////	        }
+////                if (intoNum < m/2) {
+////                    intoNum += m /4;
+////                }else {
+////                    intoNum -= m /4;
+////                }
+////
+////                if ((int) (Math.random() * 10) > hh) {
+////                    System.out.println("111111111111111111111   "+ hh);
+////                    num -= (int) (Math.random() * 5);
+////                }else {
+////                    num += (int) (Math.random() * 10);
+////                }
+//
+//                System.out.println(intoNum + "    ---- -  --     " + leaveNum);
+//            }
+
+        //   System.out.println((int) (Math.random() * (addPerson * 2)));
+        //System.out.println(count);
+//        }
+    }
+}

+ 453 - 0
src/main/java/com/chuanghai/attendance/utils/ExcelImportXLSXUtil.java

@@ -0,0 +1,453 @@
+package com.chuanghai.attendance.utils;
+
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+import org.apache.poi.hssf.usermodel.HSSFDateUtil;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.opc.OPCPackage;
+import org.apache.poi.openxml4j.opc.PackageAccess;
+import org.apache.poi.ss.usermodel.BuiltinFormats;
+import org.apache.poi.ss.usermodel.DataFormatter;
+import org.apache.poi.xssf.eventusermodel.ReadOnlySharedStringsTable;
+import org.apache.poi.xssf.eventusermodel.XSSFReader;
+import org.apache.poi.xssf.model.StylesTable;
+import org.apache.poi.xssf.usermodel.XSSFCellStyle;
+import org.apache.poi.xssf.usermodel.XSSFRichTextString;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.DefaultHandler;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.net.URL;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 使用CVS模式解决XLSX文件,可以有效解决用户模式内存溢出的问题
+ * 该模式是POI官方推荐的读取大数据的模式,在用户模式下,数据量较大、Sheet较多、或者是有很多无用的空行的情况
+ * ,容易出现内存溢出,用户模式读取Excel的典型代码如下: FileInputStream file=new
+ * FileInputStream("c:\\test.xlsx"); Workbook wb=new XSSFWorkbook(file);
+ *
+ * @author 山人
+ */
+public class ExcelImportXLSXUtil {
+
+	/**
+	 * The type of the data value is indicated by an attribute on the cell. The
+	 * value is usually in a "v" element within the cell.
+	 */
+	enum xssfDataType {
+		BOOL, ERROR, FORMULA, INLINESTR, SSTINDEX, NUMBER,
+	}
+
+	/**
+	 * 使用xssf_sax_API处理Excel,请参考: http://poi.apache.org/spreadsheet/how-to.html#xssf_sax_api
+	 * <p/>
+	 * Also see Standard ECMA-376, 1st edition, part 4, pages 1928ff, at
+	 * http://www.ecma-international.org/publications/standards/Ecma-376.htm
+	 * <p/>
+	 * A web-friendly version is http://openiso.org/Ecma/376/Part4
+	 */
+	class MyXSSFSheetHandler extends DefaultHandler {
+
+		/**
+		 * Table with styles
+		 */
+		private StylesTable stylesTable;
+
+		/**
+		 * Table with unique strings
+		 */
+		private ReadOnlySharedStringsTable sharedStringsTable;
+
+		/**
+		 * Destination for data
+		 */
+		private final PrintStream output;
+
+		/**
+		 * Number of columns to read starting with leftmost
+		 */
+		private final int minColumnCount;
+
+		// Set when V start element is seen
+		private boolean vIsOpen;
+
+		// Set when cell start element is seen;
+		// used when cell close element is seen.
+		private xssfDataType nextDataType;
+
+		// Used to format numeric cell values.
+		private short formatIndex;
+		private String formatString;
+		private final DataFormatter formatter;
+
+		private int thisColumn = -1;
+		// The last column printed to the output stream
+		private int lastColumnNumber = -1;
+
+		// Gathers characters as they are seen.
+		private StringBuffer value;
+		private String[] record;
+		private List<String[]> rows = new ArrayList<String[]>();
+		private boolean isCellNull = false;
+
+		/**
+		 * Accepts objects needed while parsing.
+		 *
+		 * @param styles  Table of styles
+		 * @param strings Table of shared strings
+		 * @param cols    Minimum number of columns to show
+		 * @param target  Sink for output
+		 */
+		public MyXSSFSheetHandler(StylesTable styles,
+								  ReadOnlySharedStringsTable strings, int cols, PrintStream target) {
+			this.stylesTable = styles;
+			this.sharedStringsTable = strings;
+			this.minColumnCount = cols;
+			this.output = target;
+			this.value = new StringBuffer();
+			this.nextDataType = xssfDataType.NUMBER;
+			this.formatter = new DataFormatter();
+			record = new String[this.minColumnCount];
+			rows.clear();// 每次读取都清空行集合
+		}
+
+		/*
+		 * (non-Javadoc)
+		 *
+		 * @see
+		 * org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String,
+		 * java.lang.String, java.lang.String, org.xml.sax.Attributes)
+		 */
+		public void startElement(String uri, String localName, String name,
+								 Attributes attributes) throws SAXException {
+
+			if ("inlineStr".equals(name) || "v".equals(name)) {
+				vIsOpen = true;
+				// Clear contents cache
+				value.setLength(0);
+			}
+			// c => cell
+			else if ("c".equals(name)) {
+				// Get the cell reference
+				String r = attributes.getValue("r");
+				int firstDigit = -1;
+				for (int c = 0; c < r.length(); ++c) {
+					if (Character.isDigit(r.charAt(c))) {
+						firstDigit = c;
+						break;
+					}
+				}
+				thisColumn = nameToColumn(r.substring(0, firstDigit));
+
+				// Set up defaults.
+				this.nextDataType = xssfDataType.NUMBER;
+				this.formatIndex = -1;
+				this.formatString = null;
+				String cellType = attributes.getValue("t");
+				String cellStyleStr = attributes.getValue("s");
+				if ("b".equals(cellType))
+					nextDataType = xssfDataType.BOOL;
+				else if ("e".equals(cellType))
+					nextDataType = xssfDataType.ERROR;
+				else if ("inlineStr".equals(cellType))
+					nextDataType = xssfDataType.INLINESTR;
+				else if ("s".equals(cellType))
+					nextDataType = xssfDataType.SSTINDEX;
+				else if ("str".equals(cellType))
+					nextDataType = xssfDataType.FORMULA;
+				else if (cellStyleStr != null) {
+					// It's a number, but almost certainly one
+					// with a special style or format
+					int styleIndex = Integer.parseInt(cellStyleStr);
+					XSSFCellStyle style = stylesTable.getStyleAt(styleIndex);
+					this.formatIndex = style.getDataFormat();
+					this.formatString = style.getDataFormatString();
+					if (this.formatString == null)
+						this.formatString = BuiltinFormats
+								.getBuiltinFormat(this.formatIndex);
+				}
+			}
+
+		}
+
+		/*
+		 * (non-Javadoc)
+		 *
+		 * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String,
+		 * java.lang.String, java.lang.String)
+		 */
+		public void endElement(String uri, String localName, String name)
+				throws SAXException {
+
+			String thisStr = null;
+
+			// v => contents of a cell
+			if ("v".equals(name)) {
+				// Process the value contents as required.
+				// Do now, as characters() may be called more than once
+				switch (nextDataType) {
+
+					case BOOL:
+						char first = value.charAt(0);
+						thisStr = first == '0' ? "FALSE" : "TRUE";
+						break;
+
+					case ERROR:
+						thisStr = "\"ERROR:" + value.toString() + '"';
+						break;
+
+					case FORMULA:
+						// A formula could result in a string value,
+						// so always add double-quote characters.
+						thisStr = value.toString();
+						break;
+
+					case INLINESTR:
+						XSSFRichTextString rtsi = new XSSFRichTextString(
+								value.toString());
+						thisStr = rtsi.toString();
+						break;
+
+					case SSTINDEX:
+						String sstIndex = value.toString();
+						try {
+							int idx = Integer.parseInt(sstIndex);
+							XSSFRichTextString rtss = new XSSFRichTextString(
+									sharedStringsTable.getEntryAt(idx));
+							thisStr = rtss.toString();
+						} catch (NumberFormatException ex) {
+							output.println("Failed to parse SST index '" + sstIndex
+									+ "': " + ex.toString());
+						}
+						break;
+
+					case NUMBER:
+						String n = value.toString();
+						// 判断是否是日期格式
+						if (HSSFDateUtil.isADateFormat(this.formatIndex, n)) {
+							Double d = Double.parseDouble(n);
+							Date date = HSSFDateUtil.getJavaDate(d);
+							thisStr = formateDateToString(date);
+						} else if (this.formatString != null) {
+							if(this.formatString.equals("reserved-0x1F") ){
+								this.formatString = "yyyy\"年\"m\"月\"d\"日\";@";
+							}
+							thisStr = formatter.formatRawCellContents(
+									Double.parseDouble(n), this.formatIndex,
+									this.formatString);
+						} else {
+							thisStr = n;
+						}
+						break;
+
+					default:
+						thisStr = "(TODO: Unexpected type: " + nextDataType + ")";
+						break;
+				}
+
+				// Output after we've seen the string contents
+				// Emit commas for any fields that were missing on this row
+				if (lastColumnNumber == -1) {
+					lastColumnNumber = 0;
+				}
+				//判断单元格的值是否为空
+				if (thisStr == null || "".equals(isCellNull)) {
+					isCellNull = true;// 设置单元格是否为空值
+				}
+				record[thisColumn] = thisStr;
+				// Update column
+				if (thisColumn > -1)
+					lastColumnNumber = thisColumn;
+
+			} else if ("row".equals(name)) {
+
+				// Print out any missing commas if needed
+				if (minColumns > 0) {
+					// Columns are 0 based
+					if (lastColumnNumber == -1) {
+						lastColumnNumber = 0;
+					}
+					boolean recordflag = record[0] != null;
+					for(int i = 1 ; i < minColumns ; i++){
+						recordflag = recordflag || (record[i] != null);
+					}
+					if (isCellNull == false && recordflag)// 判断是否空行
+					{
+						rows.add(record.clone());
+						isCellNull = false;
+						for (int i = 0; i < record.length; i++) {
+							record[i] = null;
+						}
+					}
+				}
+				lastColumnNumber = -1;
+			}
+
+		}
+
+		public List<String[]> getRows() {
+			return rows;
+		}
+
+		public void setRows(List<String[]> rows) {
+			this.rows = rows;
+		}
+
+		/**
+		 * Captures characters only if a suitable element is open. Originally
+		 * was just "v"; extended for inlineStr also.
+		 */
+		public void characters(char[] ch, int start, int length)
+				throws SAXException {
+			if (vIsOpen)
+				value.append(ch, start, length);
+		}
+
+		/**
+		 * Converts an Excel column name like "C" to a zero-based index.
+		 *
+		 * @return Index corresponding to the specified name
+		 */
+		private int nameToColumn(String name) {
+			int column = -1;
+			for (int i = 0; i < name.length(); ++i) {
+				int c = name.charAt(i);
+				column = (column + 1) * 26 + c - 'A';
+			}
+			return column;
+		}
+
+		private String formateDateToString(Date date) {
+			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//格式化日期
+			return sdf.format(date);
+		}
+
+	}
+
+	// /////////////////////////////////////
+
+	private OPCPackage xlsxPackage;
+	private int minColumns;
+	private PrintStream output;
+	private String sheetName;
+
+	/**
+	 * Creates a new XLSX -> CSV converter
+	 *
+	 * @param pkg        The XLSX package to process
+	 * @param output     The PrintStream to output the CSV to
+	 * @param minColumns The minimum number of columns to output, or -1 for no minimum
+	 */
+	public ExcelImportXLSXUtil(OPCPackage pkg, PrintStream output,
+							   String sheetName, int minColumns) {
+		this.xlsxPackage = pkg;
+		this.output = output;
+		this.minColumns = minColumns;
+		this.sheetName = sheetName;
+	}
+
+	/**
+	 * Parses and shows the content of one sheet using the specified styles and
+	 * shared-strings tables.
+	 */
+	public List<String[]> processSheet(StylesTable styles,
+									   ReadOnlySharedStringsTable strings, InputStream sheetInputStream)
+			throws IOException, ParserConfigurationException, SAXException {
+
+		InputSource sheetSource = new InputSource(sheetInputStream);
+		SAXParserFactory saxFactory = SAXParserFactory.newInstance();
+		SAXParser saxParser = saxFactory.newSAXParser();
+		XMLReader sheetParser = saxParser.getXMLReader();
+		MyXSSFSheetHandler handler = new MyXSSFSheetHandler(styles, strings,
+				this.minColumns, this.output);
+		sheetParser.setContentHandler(handler);
+		sheetParser.parse(sheetSource);
+		return handler.getRows();
+	}
+
+	/**
+	 * 初始化这个处理程序 将
+	 */
+	public List<String[]> process() throws IOException, OpenXML4JException,
+			ParserConfigurationException, SAXException {
+
+		ReadOnlySharedStringsTable strings = new ReadOnlySharedStringsTable(this.xlsxPackage);
+		XSSFReader xssfReader = new XSSFReader(this.xlsxPackage);
+		List<String[]> list = null;
+		StylesTable styles = xssfReader.getStylesTable();
+		XSSFReader.SheetIterator iter = (XSSFReader.SheetIterator) xssfReader
+				.getSheetsData();
+		while (iter.hasNext()) {
+			InputStream stream = iter.next();
+			String sheetNameTemp = iter.getSheetName();
+			if (this.sheetName.equals(sheetNameTemp)) {
+				list = processSheet(styles, strings, stream);
+				stream.close();
+			}
+		}
+		return list;
+	}
+
+	/**
+	 * 读取Excel
+	 *
+	 * @param path       文件路径
+	 * @param sheetName  sheet名称
+	 * @param minColumns 列总数
+	 */
+	public static List<String[]> readerExcel(String path, String sheetName, int minColumns) throws IOException, OpenXML4JException,
+			ParserConfigurationException, SAXException {
+		OPCPackage p = OPCPackage.open(path, PackageAccess.READ);
+		ExcelImportXLSXUtil xlsx2csv = new ExcelImportXLSXUtil(p, System.out, sheetName, minColumns);
+		List<String[]> list = xlsx2csv.process();
+		p.close();
+		return list;
+	}
+
+	public static List<String[]> readerExcel(InputStream i, String sheetName, int minColumns) throws IOException, OpenXML4JException,
+			ParserConfigurationException, SAXException {
+		OPCPackage p = OPCPackage.open(i);
+		ExcelImportXLSXUtil xlsx2csv = new ExcelImportXLSXUtil(p, System.out, sheetName, minColumns);
+		List<String[]> list = xlsx2csv.process();
+		p.close();
+		return list;
+	}
+
+
+	public static void main(String[] args) throws IOException, OpenXML4JException, ParserConfigurationException, SAXException {
+		URL httpurl = new URL("http://192.168.0.149/1.xlsx");
+		List<String[]> list = ExcelImportXLSXUtil.readerExcel(httpurl.openStream(), "Sheet1", 50);
+		for (String[] strings : list) {
+			for (String string : strings) {
+				System.out.println(string + "  |  ");
+			}
+			System.out.println("\n");
+		}
+	}
+}

+ 207 - 0
src/main/java/com/chuanghai/attendance/utils/ExcelUtil.java

@@ -0,0 +1,207 @@
+package com.chuanghai.attendance.utils;
+
+
+import org.apache.poi.hssf.usermodel.*;
+import org.apache.poi.hssf.util.HSSFColor;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.springframework.util.StringUtils;
+
+import javax.swing.filechooser.FileSystemView;
+import java.io.*;
+import java.text.DecimalFormat;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * @Author: binguo
+ * @Date: 2022/9/22 星期四 10:02
+ * @Description: com.chuanghai.attendance.utils
+ * @Version: 1.0
+ */
+public class ExcelUtil {
+    public static void createExcel() throws IOException {
+        // 获取桌面路径
+        FileSystemView fsv = FileSystemView.getFileSystemView();
+        String desktop = fsv.getHomeDirectory().getPath();
+        String filePath = desktop + "/template.xls";
+
+        File file = new File(filePath);
+        OutputStream outputStream = new FileOutputStream(file);
+        HSSFWorkbook workbook = new HSSFWorkbook();
+        HSSFSheet sheet = workbook.createSheet("Sheet1");
+        HSSFRow row = sheet.createRow(0);
+        row.createCell(0).setCellValue("id");
+        row.createCell(1).setCellValue("订单号");
+        row.createCell(2).setCellValue("下单时间");
+        row.createCell(3).setCellValue("个数");
+        row.createCell(4).setCellValue("单价");
+        row.createCell(5).setCellValue("订单金额");
+        row.setHeightInPoints(30); // 设置行的高度
+
+        HSSFRow row1 = sheet.createRow(1);
+        row1.createCell(0).setCellValue("1");
+        row1.createCell(1).setCellValue("NO00001");
+
+        // 日期格式化
+        HSSFCellStyle cellStyle2 = workbook.createCellStyle();
+        HSSFCreationHelper creationHelper = workbook.getCreationHelper();
+        cellStyle2.setDataFormat(creationHelper.createDataFormat().getFormat("yyyy-MM-dd HH:mm:ss"));
+        sheet.setColumnWidth(2, 20 * 256); // 设置列的宽度
+
+        HSSFCell cell2 = row1.createCell(2);
+        cell2.setCellStyle(cellStyle2);
+        cell2.setCellValue(new Date());
+
+        row1.createCell(3).setCellValue(2);
+
+
+        // 保留两位小数
+        HSSFCellStyle cellStyle3 = workbook.createCellStyle();
+        cellStyle3.setDataFormat(HSSFDataFormat.getBuiltinFormat("0.00"));
+        HSSFCell cell4 = row1.createCell(4);
+        cell4.setCellStyle(cellStyle3);
+        cell4.setCellValue(29.5);
+
+
+        // 货币格式化
+        HSSFCellStyle cellStyle4 = workbook.createCellStyle();
+        HSSFFont font = workbook.createFont();
+        font.setFontName("华文行楷");
+        font.setFontHeightInPoints((short) 15);
+        font.setColor(HSSFColor.RED.index);
+        cellStyle4.setFont(font);
+
+        HSSFCell cell5 = row1.createCell(5);
+        cell5.setCellFormula("D2*E2");  // 设置计算公式
+
+        // 获取计算公式的值
+        HSSFFormulaEvaluator e = new HSSFFormulaEvaluator(workbook);
+        cell5 = e.evaluateInCell(cell5);
+        System.out.println(cell5.getNumericCellValue());
+
+
+        workbook.setActiveSheet(0);
+        workbook.write(outputStream);
+        outputStream.close();
+    }
+
+    /**
+     * 读取解析execl
+     *
+     * @throws IOException
+     */
+    public static void readExcel() throws IOException {
+        FileSystemView fsv = FileSystemView.getFileSystemView();
+        String desktop = fsv.getHomeDirectory().getPath();
+        String filePath = desktop + "/template.xls";
+
+        FileInputStream fileInputStream = new FileInputStream(filePath);
+        BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
+        POIFSFileSystem fileSystem = new POIFSFileSystem(bufferedInputStream);
+        HSSFWorkbook workbook = new HSSFWorkbook(fileSystem);
+        HSSFSheet sheet = workbook.getSheet("Sheet1");
+
+        int lastRowIndex = sheet.getLastRowNum();
+        System.out.println(lastRowIndex);
+        for (int i = 0; i <= lastRowIndex; i++) {
+            HSSFRow row = sheet.getRow(i);
+            if (row == null) {
+                break;
+            }
+
+            short lastCellNum = row.getLastCellNum();
+            for (int j = 0; j < lastCellNum; j++) {
+
+                //sheet.getRow(j).getCell(j).setCellValue(Cell.CELL_TYPE_STRING);
+                // String cellValue = row.getCell(j).getStringCellValue();
+                String cellValue = getCellValue(row.getCell(j));
+                System.out.println(cellValue);
+            }
+        }
+
+
+        bufferedInputStream.close();
+    }
+
+
+    /**
+     * 获取 Excel 文件表头信息
+     *
+     * @param fileUrl
+     * @return
+     * @throws Exception
+     */
+    public Set<String> getExcelHeaders(String fileUrl) throws Exception {
+        File file = new File(fileUrl);
+        InputStream is = new FileInputStream(file);
+        Workbook workbook = new XSSFWorkbook(is);
+        Sheet sheet = workbook.getSheetAt(0);
+        System.out.println(sheet.getLastRowNum());
+        //获取 excel 第一行数据(表头)
+        Row row = sheet.getRow(0);
+        //存放表头信息
+        Set<String> set = new HashSet<>();
+        //算下有多少列
+        int colCount = sheet.getRow(0).getLastCellNum();
+        System.out.println(colCount);
+        for (int j = 0; j < colCount; j++) {
+            Cell cell = row.getCell(j);
+            String cellValue = cell.getStringCellValue().trim();
+            set.add(cellValue);
+        }
+        return set;
+    }
+
+
+    /**
+     * 拿到不同类型单元格中的值
+     * 1. 字符串: 字符串
+     * 2. 布尔: toString
+     * 3. 数值(double): 格式化后的字符串
+     *
+     * @param cell 获取的单元格
+     * @return 单元格中的值
+     */
+    private static String getCellValue(Cell cell) {
+        String resultValue = "";
+        // 判空
+        if (Objects.isNull(cell)) {
+            return resultValue;
+        }
+
+        // 拿到单元格类型
+        int cellType = cell.getCellType();
+        switch (cellType) {
+            // 字符串类型
+            case Cell.CELL_TYPE_STRING:
+                resultValue = StringUtils.isEmpty(cell.getStringCellValue()) ? "" : cell.getStringCellValue().trim();
+                break;
+            // 布尔类型
+            case Cell.CELL_TYPE_BOOLEAN:
+                resultValue = String.valueOf(cell.getBooleanCellValue());
+                break;
+            // 数值类型
+            case Cell.CELL_TYPE_NUMERIC:
+                resultValue = new DecimalFormat("#.######").format(cell.getNumericCellValue());
+                break;
+            // 取空串
+            default:
+                break;
+        }
+        return resultValue;
+    }
+
+
+    public static void main(String[] args) throws IOException {
+//           ExcelUtil.createExcel();
+        ExcelUtil.readExcel();
+    }
+
+}

+ 569 - 0
src/main/java/com/chuanghai/attendance/utils/FileExportUtil.java

@@ -0,0 +1,569 @@
+package com.chuanghai.attendance.utils;
+
+
+import com.chuanghai.attendance.entity.DownLoadExcel;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.util.IOUtils;
+import org.apache.poi.xssf.usermodel.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.util.List;
+
+/**
+ * Created with IntelliJ IDEA.
+ *
+ * @Description:
+ */
+public class FileExportUtil {
+
+    /**
+     * @param list 需要写入excel的数据 从数据库或者其他途径读取
+     * @Description: 导出监测对比数据的工具类
+     * @return:
+     */
+    public static void testExcelDemo(List<DownLoadExcel> list, HttpServletResponse response, String[] titleStr) {
+        /** 第一步,创建一个Workbook,对应一个Excel文件  */
+        XSSFWorkbook wb = new XSSFWorkbook();
+
+        /** 第二步,在Workbook中添加一个sheet,对应Excel文件中的sheet  */
+        XSSFSheet sheet = wb.createSheet("excel导出标题");
+        /** 第三步,设置样式以及字体样式*/
+        XSSFCellStyle titleStyle = createTitleCellStyle(wb);
+        XSSFCellStyle headerStyle = createHeadCellStyle(wb);
+        XSSFCellStyle contentStyle = createContentCellStyle(wb);
+        /** 第四步,创建标题 ,合并标题单元格 */
+        // 行号
+        int rowNum = 0;
+        // 创建第一页的第一行,索引从0开始
+        XSSFRow row0 = sheet.createRow(rowNum++);
+        row0.setHeight((short) 600);// 设置行高
+
+        String[] row_Text = {"姓名", "考勤组", "部门", "工号", "身份证", "黄家湖出勤天数", "黄家湖缺卡天数", "黄家湖缺卡记录"
+                , "黄家湖总出勤天数", "墨轩湖出勤天数", "墨轩湖缺卡天数", "墨轩湖缺卡记录", "墨轩湖总出勤天数", "异常地点打卡天数", "异常地点打卡记录",
+                "0-20分钟迟到次数", "20-30分钟迟到次数", ">30分钟迟到次数", "迟到次数记录", "0-20分钟早退次数", "20-30分钟早退次数", ">30分钟早退次数",
+                "早退次数记录", "出勤天数", "userId", "出勤天数", "休息天数", "工作时长", "迟到次数", "迟到时长", "严重迟到次数", "严重迟到时长",
+                "旷工迟到天数", "早退次数", "早退时长", "上班缺卡次数", "下班缺卡次数", "旷工天数", "外出", "请假", "", "", "", ""};
+
+        String[] row_Text_new = new String[row_Text.length + titleStr.length];
+
+        for (int m = 0; m < row_Text.length; m++) {
+            row_Text_new[m] = row_Text[m];
+        }
+        for (int m = 0; m < titleStr.length; m++) {
+            row_Text_new[row_Text.length + m] = titleStr[m];
+        }
+        row_Text = row_Text_new;
+
+
+        for (int i = 0; i < row_Text.length; i++) {
+            XSSFCell c00 = row0.createCell(i);
+            c00.setCellValue(row_Text[i]);
+            c00.setCellStyle(headerStyle);
+        }
+        // 合并单元格,参数依次为起始列,结束列,起始行,结束行 (索引0开始)
+
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 0, 0));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 1, 1));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 2, 2));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 3, 3));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 4, 4));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 5, 5));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 6, 6));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 7, 7));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 8, 8));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 9, 9));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 10, 10));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 11, 11));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 12, 12));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 13, 13));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 14, 14));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 15, 15));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 16, 16));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 17, 17));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 18, 18));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 19, 19));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 20, 20));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 21, 21));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 22, 22));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 23, 23));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 24, 24));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 25, 25));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 26, 26));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 27, 27));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 28, 28));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 29, 29));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 30, 30));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 31, 31));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 32, 32));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 33, 33));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 34, 34));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 35, 35));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 36, 36));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 37, 37));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 38, 38));
+        sheet.addMergedRegion(new CellRangeAddress(0, 0, 39, 43));
+
+        //添加每日详情  31   ——————》75
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 44, 44));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 45, 45));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 46, 46));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 47, 47));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 48, 48));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 49, 49));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 50, 50));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 51, 51));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 52, 52));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 53, 53));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 54, 54));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 55, 55));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 56, 56));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 57, 57));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 58, 58));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 59, 59));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 60, 60));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 61, 61));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 62, 62));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 63, 63));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 64, 64));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 65, 65));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 66, 66));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 67, 67));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 68, 68));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 69, 69));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 70, 70));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 71, 71));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 72, 72));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 73, 73));
+        sheet.addMergedRegion(new CellRangeAddress(0, 1, 74, 74));
+
+
+
+        //第二行
+        XSSFRow row2 = sheet.createRow(rowNum++);
+        row2.setHeight((short) 700);
+
+        String[] row_third = {"", "", "", "", "", "", "", ""
+                , "", "", "", "", "", "", "",
+                "", "", "", "", "", "", "",
+                "", "", "", "", "", "", "", "", "", "",
+                "", "", "", "", "","", "(天)", "丧假(天)", "事假(天)", "产假(天)", "病假(天)", "陪护假(天)"};
+
+        String[] row_third_new = new String[row_third.length + titleStr.length];
+
+
+        for (int m = 0; m < row_third.length; m++) {
+            row_third_new[m] = row_third[m];
+        }
+        for (int m = 0; m < titleStr.length; m++) {
+            row_third_new[row_third.length + m] = titleStr[m];
+        }
+        row_third = row_third_new;
+
+        for (int i = 0; i < row_third.length; i++) {
+            XSSFCell tempCell = row2.createCell(i);
+            tempCell.setCellValue(row_third[i]);
+            tempCell.setCellStyle(headerStyle);
+        }
+
+
+        for (DownLoadExcel value : list) {
+
+            XSSFRow tempRow = sheet.createRow(rowNum++);
+            tempRow.setHeight((short) 500);
+            // 循环单元格填入数据
+//            for (int j = 0; j < (0 == type ? 12 : 11); j++) {
+            for (int j = 0; j < row_Text.length; j++) {
+                //列宽自适应,j为自适应的列,true就是自适应,false就是不自适应,默认不自适应
+                sheet.autoSizeColumn(j, true);
+                XSSFCell tempCell = tempRow.createCell(j);
+                tempCell.setCellStyle(contentStyle);
+                String tempValue = "";
+                switch (j) {
+                    case 0:
+                        //姓名
+                        tempValue = value.getWorkName();
+                        break;
+                    case 1:
+                        //考勤组
+                        tempValue = value.getGroupOfCheck();
+                        break;
+                    case 2:
+                        //部门
+                        tempValue = value.getDepartment();
+                        break;
+                    case 3:
+                        //工号
+                        tempValue = value.getWorkNum();
+                        break;
+                    case 4:
+                        //身份证
+                        tempValue = value.getIdCard();
+                        break;
+                    case 5:
+                        //黄家湖出勤天数
+                        tempValue = value.getDaysOfAttendanceInHuangjiahu().toString();
+                        break;
+                    case 6:
+                        //黄家湖缺卡天数
+                        tempValue = value.getDaysOfcardShortageInHuangjiahu();
+                        break;
+                    case 7:
+                        //黄家湖缺卡记录
+                        tempValue = value.getRecondOfcardShortageInHuangjiahu();
+                        break;
+                    case 8:
+                        //黄家湖总出勤天数
+                        tempValue = value.getSumAttendanceInHuangjiahu().toString();
+                        break;
+                    case 9:
+                        // 墨轩湖出勤天数
+                        tempValue = value.getDaysOfAttendanceInMoxuanhu().toString();
+                        break;
+                    case 10:
+                        //墨轩湖缺卡天数
+                        tempValue = value.getDaysOfcardShortageInMoxuanhu();
+                        break;
+                    case 11:
+                        //墨轩湖缺卡记录
+                        tempValue = value.getRecondOfcardShortageInMoxuanhu();
+                        break;
+                    case 12:
+                        //墨轩湖总出勤天数
+                        tempValue = value.getSumAttendanceInMoxuanhu().toString();
+                        break;
+                    case 13:
+                        //异常地点打卡天数
+                        tempValue = value.getAbnormalClockNumber().toString();
+                        break;
+                    case 14:
+                        //异常地点打卡记录
+                        tempValue = value.getAbnormalClockRecond();
+                        break;
+                    case 15:
+                        // 0-20分钟迟到次数
+                        tempValue = value.getLeTwentyLateNumber().toString();
+                        break;
+                    case 16:
+                        //20-30分钟迟到次数
+                        tempValue = value.getLequalThirtyLateNumber().toString();
+                        break;
+                    case 17:
+                        // >30分钟迟到次数
+                        tempValue = value.getGeThirtyLateNumber().toString();
+                        break;
+                    case 18:
+                        //迟到次数记录
+                        tempValue = value.getLateOfRecond();
+                        break;
+                    case 19:
+                        // 0-20分钟早退次数
+                        tempValue = value.getLeTwentyLeaveNumber().toString();
+                        break;
+                    case 20:
+                        //20-30分钟早退次数
+                        tempValue = value.getLequalThirtyLeaveNumber().toString();
+                        break;
+                    case 21:
+                        //>30分钟早退次数
+                        tempValue = value.getGeThirtyLeaveNumber().toString();
+                        break;
+                    case 22:
+                        //早退次数记录
+                        tempValue = value.getLeaveOfRecond();
+                        break;
+                    case 23:
+                        //出勤天数
+                        tempValue = value.getAttendanceDays().toString();
+                        break;
+                    case 24:
+                        //userId
+                        tempValue = value.getUserId();
+                        break;
+                    case 25:
+                        //出勤天数
+                        tempValue = value.getAttendanceDay();
+                        break;
+                    case 26:
+                        //休息天数
+                        tempValue = value.getRestDay();
+                        break;
+                    case 27:
+                        //工作时长
+                        tempValue = value.getWorkHours();
+                        break;
+                    case 28:
+                        //迟到次数
+                        tempValue = value.getNumberOfLate();
+                        break;
+                    case 29:
+                        //迟到时长
+                        tempValue = value.getNumberOfLateTime();
+                        break;
+                    case 30:
+                        //严重迟到次数
+                        tempValue = value.getNumberOfSeriousLateness();
+                        break;
+                    case 31:
+                        //严重迟到时长
+                        tempValue = value.getDurationOfSevereLateness();
+                        break;
+                    case 32:
+                        //旷工迟到天数
+                        tempValue = value.getDaysOfAbsenteeismAndLateness();
+                        break;
+                    case 33:
+                        //早退次数
+                        tempValue = value.getLeaveEarlyNumber();
+                        break;
+                    case 34:
+                        //早退时长
+                        tempValue = value.getLeaveEarlyTimes();
+                        break;
+                    case 35:
+                        //上班缺卡次数
+                        tempValue = value.getLackOfWorkCardTimes();
+                        break;
+                    case 36:
+                        //下班缺卡次数
+                        tempValue = value.getLackOfMissingWorkCardTimes();
+                        break;
+                    case 37:
+                        //旷工天数
+                        tempValue = value.getAbsenteeismDay();
+                        break;
+//                    case 38:
+//                        //出差(天)
+//                        tempValue = value.getEvectionDay();
+//                        break;
+                    case 38:
+                        // 外出(天)
+                        tempValue = value.getGoOutDay();
+                        break;
+                    case 39:
+                        // 事假(天)
+                        tempValue = value.getBereavementLeave();
+                        break;
+                    case 40:
+                        // 产假(天)
+                        tempValue = value.getPrivateAffairLeaveDay();
+                        break;
+                    case 41:
+                        //婚假(天)
+                        tempValue = value.getMaternityLeaveDay();
+                        break;
+                    case 42:
+                        //病假(天)
+                        tempValue = value.getSickLeaveDay();
+                        break;
+                    case 43:
+                        //陪护假(天)
+                        tempValue = value.getEscortFalse();
+                        break;
+
+                    case 44:
+                        tempValue = value.getOneOfMonth();
+                        break;
+                    case 45:
+                        tempValue = value.getTwoOfMonth();
+                        break;
+                    case 46:
+                        tempValue = value.getThreeOfMonth();
+                        break;
+                    case 47:
+                        tempValue = value.getFourOfMonth();
+                        break;
+                    case 48:
+                        tempValue = value.getFiveOfMonth();
+                        break;
+                    case 49:
+                        tempValue = value.getSixOfMonth();
+                        break;
+                    case 50:
+                        tempValue = value.getSevenOfMonth();
+                        break;
+                    case 51:
+                        tempValue = value.getEightOfMonth();
+                        break;
+                    case 52:
+                        tempValue = value.getNineOfMonth();
+                        break;
+                    case 53:
+                        tempValue = value.getTenOfMonth();
+                        break;
+                    case 54:
+                        tempValue = value.getElevenOfMonth();
+                        break;
+                    case 55:
+                        tempValue = value.getTwelveOfMonth();
+                        break;
+                    case 56:
+                        tempValue = value.getThirteenOfMonth();
+                        break;
+                    case 57:
+                        tempValue = value.getFourteenOfMonth();
+                        break;
+                    case 58:
+                        tempValue = value.getFifteenOfMonth();
+                        break;
+                    case 59:
+                        tempValue = value.getSixteenOfMonth();
+                        break;
+                    case 60:
+                        tempValue = value.getSeventeenOfMonth();
+                        break;
+                    case 61:
+                        tempValue = value.getEighteenOfMonth();
+                        break;
+                    case 62:
+                        tempValue = value.getNineteenOfMonth();
+                        break;
+                    case 63:
+                        tempValue = value.getTwentyOfMonth();
+                        break;
+                    case 64:
+                        tempValue = value.getTwentyOneOfMonth();
+                        break;
+                    case 65:
+                        tempValue = value.getTwentyTwoOfMonth();
+                        break;
+                    case 66:
+                        tempValue = value.getTwentyThreeOfMonth();
+                        break;
+                    case 67:
+                        tempValue = value.getTwentyFourOfMonth();
+                        break;
+                    case 68:
+                        tempValue = value.getTwentyFiveOfMonth();
+                        break;
+                    case 69:
+                        tempValue = value.getTwentySixOfMonth();
+                        break;
+                    case 70:
+                        tempValue = value.getTwentySevenOfMonth();
+                        break;
+                    case 71:
+                        tempValue = value.getTwentyEightOfMonth();
+                        break;
+                    case 72:
+                        tempValue = value.getTwentyNineOfMonth();
+                        break;
+                    case 73:
+                        tempValue = value.getThirtyOfMonth();
+                        break;
+                    case 74:
+                        tempValue = value.getThirtyOneOfMonth();
+                        break;
+
+                }
+                tempCell.setCellValue(tempValue);
+            }
+        }
+        //导出到浏览器下载
+        buildExcelDocument("钉钉分析汇总.xlsx", wb, response);
+    }
+
+    /**
+     * @Description: [导出到浏览器]
+     * @Param: [fileName, wb, response]
+     * @return: void
+     *
+     */
+    private static void buildExcelDocument(String fileName, Workbook wb, HttpServletResponse response) {
+        try {
+            response.setContentType("application/octet-stream");
+            // 可自行定义编码格式
+            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "utf-8"));
+
+            //清除jsp编译html文件的空白,防止excel出现空行
+            response.flushBuffer();
+            //写出
+            wb.write(response.getOutputStream());
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            IOUtils.closeQuietly(wb);
+        }
+    }
+//---------------------------------------以下是封装的样式-----------------------------
+
+    /**
+     * 创建标题样式
+     *
+     * @param wb
+     * @return
+     */
+    private static XSSFCellStyle createTitleCellStyle(XSSFWorkbook wb) {
+        XSSFCellStyle cellStyle = wb.createCellStyle();
+        cellStyle.setAlignment(HorizontalAlignment.CENTER);//水平居中
+        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);//垂直对齐
+        cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+//        cellStyle.setFillForegroundColor(IndexedColors.GREY_40_PERCENT.getIndex());//背景颜色
+
+        XSSFFont headerFont1 = (XSSFFont) wb.createFont(); // 创建字体样式
+        headerFont1.setBold(true); //字体加粗
+        headerFont1.setFontName("黑体"); // 设置字体类型
+        headerFont1.setFontHeightInPoints((short) 15); // 设置字体大小
+        cellStyle.setFont(headerFont1); // 为标题样式设置字体样式
+        return cellStyle;
+    }
+
+    /**
+     * 创建表头样式
+     *
+     * @param wb
+     * @return
+     */
+    private static XSSFCellStyle createHeadCellStyle(XSSFWorkbook wb) {
+        XSSFCellStyle cellStyle = wb.createCellStyle();
+        cellStyle.setWrapText(true);// 设置自动换行
+        cellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());//背景颜色
+        cellStyle.setAlignment(HorizontalAlignment.CENTER); //水平居中
+        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER); //垂直对齐
+        cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+//        cellStyle.setBottomBorderColor(IndexedColors.BLACK.index);
+        cellStyle.setBorderBottom(BorderStyle.THIN); //下边框
+        cellStyle.setBorderLeft(BorderStyle.THIN); //左边框
+        cellStyle.setBorderRight(BorderStyle.THIN); //右边框
+        cellStyle.setBorderTop(BorderStyle.THIN); //上边框
+
+        XSSFFont headerFont = (XSSFFont) wb.createFont(); // 创建字体样式
+        headerFont.setBold(true); //字体加粗
+        headerFont.setFontName("黑体"); // 设置字体类型
+        headerFont.setFontHeightInPoints((short) 12); // 设置字体大小
+        cellStyle.setFont(headerFont); // 为标题样式设置字体样式
+
+        return cellStyle;
+    }
+
+    /**
+     * 创建内容样式
+     *
+     * @param wb
+     * @return
+     */
+    private static XSSFCellStyle createContentCellStyle(XSSFWorkbook wb) {
+        XSSFCellStyle cellStyle = wb.createCellStyle();
+        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);// 垂直居中
+        cellStyle.setAlignment(HorizontalAlignment.CENTER);// 水平居中
+        cellStyle.setWrapText(true);// 设置自动换行
+        cellStyle.setBorderBottom(BorderStyle.THIN); //下边框
+        cellStyle.setBorderLeft(BorderStyle.THIN); //左边框
+        cellStyle.setBorderRight(BorderStyle.THIN); //右边框
+        cellStyle.setBorderTop(BorderStyle.THIN); //上边框
+
+        // 生成12号字体
+        XSSFFont font = wb.createFont();
+        font.setColor((short) 8);
+        font.setFontHeightInPoints((short) 12);
+        cellStyle.setFont(font);
+
+        return cellStyle;
+    }
+}
+
+
+
+

+ 33 - 0
src/main/java/com/chuanghai/attendance/utils/excel/CellResult.java

@@ -0,0 +1,33 @@
+package com.chuanghai.attendance.utils.excel;
+
+public class CellResult {
+	private Integer no;
+	private String status;// SUCCESS, ERROR, OTHER
+	private String info;
+	private String extInfo;
+	public Integer getNo() {
+		return no;
+	}
+	public void setNo(Integer no) {
+		this.no = no;
+	}
+	public String getStatus() {
+		return status;
+	}
+	public void setStatus(String status) {
+		this.status = status;
+	}
+	public String getInfo() {
+		return info;
+	}
+	public void setInfo(String info) {
+		this.info = info;
+	}
+	public String getExtInfo() {
+		return extInfo;
+	}
+	public void setExtInfo(String extInfo) {
+		this.extInfo = extInfo;
+	}
+
+}

+ 263 - 0
src/main/java/com/chuanghai/attendance/utils/excel/EachCommand.java

@@ -0,0 +1,263 @@
+package com.chuanghai.attendance.utils.excel;
+
+import org.jxls.area.Area;
+import org.jxls.command.AbstractCommand;
+import org.jxls.command.CellRefGenerator;
+import org.jxls.command.Command;
+import org.jxls.command.SheetNameGenerator;
+import org.jxls.common.CellRef;
+import org.jxls.common.Context;
+import org.jxls.common.JxlsException;
+import org.jxls.common.Size;
+import org.jxls.expression.JexlExpressionEvaluator;
+import org.jxls.util.Util;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 扩展jxls each命令
+ * 增加retainEmpty属性,当items为null或size为0时,也保留当前一行数据的格式
+ * 循环增加下标变量“var_index”。如var="item",获取下标方法:${item_index}
+ */
+public class EachCommand extends AbstractCommand {
+    public enum Direction {RIGHT, DOWN}
+
+    private String var;
+    private String items;
+    private String select;
+    private Area area;
+    private Direction direction = Direction.DOWN;
+    private CellRefGenerator cellRefGenerator;
+    private String multisheet;
+
+    private String retainEmpty; //当集合大小为0时,是否最少保留一行空行数据
+
+    public EachCommand() {
+    }
+
+    /**
+     * @param var       name of the key in the context to contain each collection items during iteration
+     * @param items     name of the collection bean in the context
+     * @param direction defines processing by rows (DOWN - default) or columns (RIGHT)
+     */
+    public EachCommand(String var, String items, Direction direction) {
+        this.var = var;
+        this.items = items;
+        this.direction = direction == null ? Direction.DOWN : direction;
+    }
+
+    public EachCommand(String var, String items, Area area) {
+        this(var, items, area, Direction.DOWN);
+    }
+
+    public EachCommand(String var, String items, Area area, Direction direction) {
+        this(var, items, direction);
+        if (area != null) {
+            this.area = area;
+            addArea(this.area);
+        }
+    }
+
+    /**
+     * @param var              name of the key in the context to contain each collection items during iteration
+     * @param items            name of the collection bean in the context
+     * @param area             body area for this command
+     * @param cellRefGenerator generates target cell ref for each collection item during iteration
+     */
+    public EachCommand(String var, String items, Area area, CellRefGenerator cellRefGenerator) {
+        this(var, items, area, (Direction) null);
+        this.cellRefGenerator = cellRefGenerator;
+    }
+
+    /**
+     * Gets iteration directino
+     *
+     * @return current direction for iteration
+     */
+    public Direction getDirection() {
+        return direction;
+    }
+
+    /**
+     * Sets iteration direction
+     *
+     * @param direction
+     */
+    public void setDirection(Direction direction) {
+        this.direction = direction;
+    }
+
+    public void setDirection(String direction) {
+        this.direction = Direction.valueOf(direction);
+    }
+
+    /**
+     * Gets defined cell ref generator
+     *
+     * @return current {@link CellRefGenerator} instance or null
+     */
+    public CellRefGenerator getCellRefGenerator() {
+        return cellRefGenerator;
+    }
+
+    public void setCellRefGenerator(CellRefGenerator cellRefGenerator) {
+        this.cellRefGenerator = cellRefGenerator;
+    }
+
+    public String getName() {
+        return "each";
+    }
+
+    /**
+     * Gets current variable name for collection item in the context during iteration
+     *
+     * @return collection item key name in the context
+     */
+    public String getVar() {
+        return var;
+    }
+
+    /**
+     * Sets current variable name for collection item in the context during iteration
+     *
+     * @param var
+     */
+    public void setVar(String var) {
+        this.var = var;
+    }
+
+    /**
+     * Gets collection bean name
+     *
+     * @return collection bean name in the context
+     */
+    public String getItems() {
+        return items;
+    }
+
+    /**
+     * Sets collection bean name
+     *
+     * @param items collection bean name in the context
+     */
+    public void setItems(String items) {
+        this.items = items;
+    }
+
+    /**
+     * Gets current 'select' expression for filtering out collection items
+     *
+     * @return current 'select' expression or null if undefined
+     */
+    public String getSelect() {
+        return select;
+    }
+
+    /**
+     * Sets current 'select' expression for filtering collection
+     *
+     * @param select filtering expression
+     */
+    public void setSelect(String select) {
+        this.select = select;
+    }
+
+    /**
+     * @return Context variable name holding a list of Excel sheet names to output the collection to
+     */
+    public String getMultisheet() {
+        return multisheet;
+    }
+
+    /**
+     * Sets name of context variable holding a list of Excel sheet names to output the collection to
+     * @param multisheet
+     */
+    public void setMultisheet(String multisheet) {
+        this.multisheet = multisheet;
+    }
+
+    @Override
+    public Command addArea(Area area) {
+        if (area == null) {
+            return this;
+        }
+        if (super.getAreaList().size() >= 1) {
+            throw new IllegalArgumentException("You can add only a single area to 'each' command");
+        }
+        this.area = area;
+        return super.addArea(area);
+    }
+
+    @SuppressWarnings("rawtypes")
+    public Size applyAt(CellRef cellRef, Context context) {
+        Collection itemsCollection = Util.transformToCollectionObject(getTransformationConfig().getExpressionEvaluator(), items, context);
+        int width = 0;
+        int height = 0;
+        int index = 0;
+        CellRefGenerator cellRefGenerator = this.cellRefGenerator;
+        if (cellRefGenerator == null && multisheet != null) {
+            List<String> sheetNameList = extractSheetNameList(context);
+            cellRefGenerator = new SheetNameGenerator(sheetNameList, cellRef);
+        }
+        CellRef currentCell = cellRefGenerator != null ? cellRefGenerator.generateCellRef(index, context) : cellRef;
+        JexlExpressionEvaluator selectEvaluator = null;
+        if (select != null) {
+            selectEvaluator = new JexlExpressionEvaluator(select);
+        }
+        for (Object obj : itemsCollection) {
+            context.putVar(var, obj);
+            context.putVar(var+"_index", index);
+            if (selectEvaluator != null && !Util.isConditionTrue(selectEvaluator, context)) {
+                context.removeVar(var);
+                context.removeVar(var+"_index");
+                continue;
+            }
+            Size size = area.applyAt(currentCell, context);
+            index++;
+            if (cellRefGenerator != null) {
+                width = Math.max(width, size.getWidth());
+                height = Math.max(height, size.getHeight());
+                if(index < itemsCollection.size()) {
+                    currentCell = cellRefGenerator.generateCellRef(index, context);
+                }
+            } else if (direction == Direction.DOWN) {
+                currentCell = new CellRef(currentCell.getSheetName(), currentCell.getRow() + size.getHeight(), currentCell.getCol());
+                width = Math.max(width, size.getWidth());
+                height += size.getHeight();
+            } else {
+                currentCell = new CellRef(currentCell.getSheetName(), currentCell.getRow(), currentCell.getCol() + size.getWidth());
+                width += size.getWidth();
+                height = Math.max(height, size.getHeight());
+            }
+            context.removeVar(var);
+            context.removeVar(var+"_index");
+        }
+        if("true".equalsIgnoreCase(retainEmpty) && width == 0 && height == 0){
+            return area.applyAt(currentCell, context);
+        }
+        return new Size(width, height);
+    }
+
+    @SuppressWarnings("unchecked")
+    private List<String> extractSheetNameList(Context context) {
+        try {
+            return (List<String>) context.getVar(multisheet);
+        } catch (Exception e) {
+            throw new JxlsException("Failed to get sheet names from " + multisheet, e);
+        }
+    }
+
+    public String getRetainEmpty() {
+        return retainEmpty;
+    }
+
+    public void setRetainEmpty(String retainEmpty) {
+        this.retainEmpty = retainEmpty;
+    }
+
+    public Area getArea() {
+        return area;
+    }
+}

+ 326 - 0
src/main/java/com/chuanghai/attendance/utils/excel/EachMergeCommand.java

@@ -0,0 +1,326 @@
+package com.chuanghai.attendance.utils.excel;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import org.apache.poi.ss.usermodel.BorderStyle;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.ss.util.RegionUtil;
+import org.jxls.area.Area;
+import org.jxls.command.CellRefGenerator;
+import org.jxls.common.AreaListener;
+import org.jxls.common.AreaRef;
+import org.jxls.common.CellRef;
+import org.jxls.common.Context;
+import org.jxls.common.Size;
+import org.jxls.transform.Transformer;
+import org.jxls.transform.poi.PoiTransformer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Extends {@link EachCommand} to support merge cells.
+ *
+ * @author RJ
+ */
+public class EachMergeCommand extends EachCommand {
+	private static Logger logger = LoggerFactory.getLogger(EachMergeCommand.class);
+
+	public static final String COMMAND_NAME = "each-merge";
+
+	public EachMergeCommand() {
+		super();
+	}
+
+	public EachMergeCommand(String var, String items, Direction direction) {
+		super(var, items, direction);
+	}
+
+	public EachMergeCommand(String var, String items, Area area) {
+		super(var, items, area);
+	}
+
+	public EachMergeCommand(String var, String items, Area area, Direction direction) {
+		super(var, items, area, direction);
+	}
+
+	public EachMergeCommand(String var, String items, Area area, CellRefGenerator cellRefGenerator) {
+		super(var, items, area, cellRefGenerator);
+	}
+
+	@Override
+	@SuppressWarnings("unchecked")
+	public Size applyAt(CellRef cellRef, Context context) {
+		// collect sub command areas
+		List<Area> childAreas = this.getAreaList().stream()
+				.flatMap(area1 -> area1.getCommandDataList().stream())
+				.flatMap(commandData -> commandData.getCommand().getAreaList().stream())
+				.collect(Collectors.toList());
+		List<AreaRef> childAreaRefs = childAreas.stream()
+				.map(Area::getAreaRef).collect(Collectors.toList());
+
+		// register AreaListener for parent command area
+		Area parentArea = getArea();
+		MergeCellListener listener = new MergeCellListener(getTransformer(), parentArea.getAreaRef(), childAreaRefs,
+				((Collection) context.getVar(this.getItems())).size(), getDirection()); // 总数据量
+		logger.info("register listener {} to {} from {}", listener, parentArea.getAreaRef(), cellRef);
+		parentArea.addAreaListener(listener);
+
+		// register AreaListener for all sub command area
+		childAreas.forEach(area -> {
+			logger.info("register listener {} to {} by parent", listener, area.getAreaRef());
+			area.addAreaListener(listener);
+		});
+
+		// standard dealing
+		return super.applyAt(cellRef, context);
+	}
+
+	/**
+	 * The {@link AreaListener} for merge cells.
+	 */
+	public static class MergeCellListener implements AreaListener {
+		private final PoiTransformer transformer;
+		private int parentStartColumn;                         // parent command start column
+		private int[] childStartColumns;                       // all sub command start column
+		private final int[] mergeColumns;                      // to merge columns
+		private final List<int[]> records = new ArrayList<>(); // 0 - start column, 1 - end column
+		private int parentCount;
+
+
+		private int parentProcessed;
+		private String sheetName;
+		private Direction direction;
+
+		private int childRowOrCol;
+		private int parentProRowOrCol;
+
+		MergeCellListener(Transformer transformer, AreaRef parent, List<AreaRef> children, int parentCount, Direction direction) {
+			this.transformer = (PoiTransformer) transformer;
+			this.parentCount = parentCount;
+			this.direction = direction;
+			int[] childCols;
+			if (direction == Direction.DOWN) {
+				this.parentStartColumn = parent.getFirstCellRef().getCol();
+				// find all sub command columns
+				childCols = children.stream()
+						.flatMapToInt(ref -> IntStream.range(ref.getFirstCellRef().getCol(), ref.getLastCellRef().getCol() + 1))
+						.distinct().sorted()
+						.toArray();
+
+				// find all sub command start column
+				this.childStartColumns = children.stream()
+						.mapToInt(ref -> ref.getFirstCellRef().getCol())
+						.distinct().sorted().toArray();
+
+				// get columns to merge by filter childCols
+				this.mergeColumns = IntStream.range(parent.getFirstCellRef().getCol(), parent.getLastCellRef().getCol() + 1)
+						.filter(parentCol -> IntStream.of(childCols).noneMatch(childCol -> childCol == parentCol))
+						.toArray();
+			} else if (direction == Direction.RIGHT) {
+				this.parentStartColumn = parent.getFirstCellRef().getRow();
+				childCols = children.stream()
+						.flatMapToInt(ref -> IntStream.range(ref.getFirstCellRef().getRow(), ref.getLastCellRef().getRow() + 1))
+						.distinct().sorted()
+						.toArray();
+
+				// find all sub command start column
+				this.childStartColumns = children.stream()
+						.mapToInt(ref -> ref.getFirstCellRef().getRow())
+						.distinct().sorted().toArray();
+
+				// get columns to merge by filter childCols
+				this.mergeColumns = IntStream.range(parent.getFirstCellRef().getRow(), parent.getLastCellRef().getRow() + 1)
+						.filter(parentCol -> IntStream.of(childCols).noneMatch(childCol -> childCol == parentCol))
+						.toArray();
+			} else {
+				childCols = new int[]{};
+				this.mergeColumns = new int[]{};
+			}
+
+			logger.debug("parentArea={}", parent);
+			logger.debug("parentStartColumn={}", parentStartColumn);
+			logger.debug("childStartColumns={}", childStartColumns);
+			logger.debug("mergeColumns={}", mergeColumns);
+			logger.debug("childCols={}", childCols);
+		}
+
+		@Override
+		public void beforeApplyAtCell(CellRef cellRef, Context context) {
+		}
+
+		@Override
+		public void afterApplyAtCell(CellRef cellRef, Context context) {
+		}
+
+		@Override
+		public void beforeTransformCell(CellRef srcCell, CellRef targetCell, Context context) {
+		}
+
+		// remember that all sub command AreaListener is invoked before parent command AreaListener.
+		// This class use this feature to do the merge work.
+		@Override
+		public void afterTransformCell(CellRef srcCell, CellRef targetCell, Context context) {
+			/*
+				col 2 row 4
+				col 2 row 5
+				col 3 row 5
+				col 4 row 4
+				col 4 row 5
+				col 5 row 5
+			 */
+			if (parentProcessed == 0) this.sheetName = targetCell.getSheetName();
+
+
+			if (this.direction == Direction.DOWN) {
+				if (targetCell.getCol() == parentStartColumn) { // main command process
+					this.parentProcessed++;
+					if (this.parentProcessed == 1) {
+						this.parentProRowOrCol = targetCell.getRow();
+					}
+					logger.debug("parent: srcCell={}, targetCell={} [{}, {}]", srcCell, targetCell,
+							targetCell.getRow(), targetCell.getCol());
+
+					//should be recorded just on necessary
+					if (targetCell.getRow() > this.childRowOrCol)
+						this.records.add(new int[]{this.parentProRowOrCol, this.childRowOrCol});
+
+					// merge work invoke on the last
+					if (this.parentProcessed == this.parentCount) {//
+						Workbook workbook = transformer.getWorkbook();
+						Sheet sheet = workbook.getSheet(sheetName);
+						doMerge(sheet, this.records, this.mergeColumns, srcCell, this.direction);
+					}
+					this.childRowOrCol = 0;
+					this.parentProRowOrCol = targetCell.getRow();
+
+					// record the current row number of sub command process
+				} else if (IntStream.of(childStartColumns).anyMatch(col -> col == targetCell.getCol())) {
+					this.childRowOrCol = Math.max(this.childRowOrCol, targetCell.getRow());
+
+					logger.debug("child: srcCell={}, targetCell={} [{}, {}]", srcCell, targetCell,
+							targetCell.getRow(), targetCell.getCol());
+				}
+			} else if (this.direction == Direction.RIGHT) {
+				if (targetCell.getRow() == parentStartColumn) { // main command process
+					this.parentProcessed++;
+					if (this.parentProcessed == 1) {
+						this.parentProRowOrCol = targetCell.getCol();
+					}
+
+					logger.debug("parent: srcCell={}, targetCell={} [{}, {}]", srcCell, targetCell,
+							targetCell.getRow(), targetCell.getCol());
+
+					//should be recorded just on necessary
+					if (targetCell.getCol() > this.childRowOrCol && this.parentProRowOrCol < this.childRowOrCol) {
+						this.records.add(new int[]{this.parentProRowOrCol, this.childRowOrCol});
+					}
+
+					// merge work invoke on the last
+					if (this.parentProcessed == this.parentCount) {
+						Workbook workbook = transformer.getWorkbook();
+						Sheet sheet = workbook.getSheet(sheetName);
+						doMerge(sheet, this.records, this.mergeColumns, srcCell, this.direction);
+					}
+					this.parentProRowOrCol = targetCell.getCol();
+					this.childRowOrCol = 0;
+
+					// record the current row number of sub command process
+				} else if (IntStream.of(childStartColumns).anyMatch(row -> row == targetCell.getRow())) {
+					this.childRowOrCol = Math.max(this.childRowOrCol, targetCell.getCol());
+
+					logger.debug("child: srcCell={}, targetCell={} [{}, {}]", srcCell, targetCell,
+							targetCell.getRow(), targetCell.getCol());
+				}
+			}
+		}
+
+		private static void doMerge(Sheet sheet, List<int[]> records, int[] mergeColumns, CellRef srcCell, Direction direction) {
+			if (logger.isDebugEnabled()) {
+				logger.debug("merge: sheetName={}, records={}", sheet.getSheetName(),
+						records.stream().map(startEnd -> "[" + startEnd[0] + "," + startEnd[1] + "]")
+								.collect(Collectors.joining(",")));
+			}
+			if (direction == Direction.DOWN) {
+				records.forEach(startEnd -> merge4Row(sheet, startEnd[0], startEnd[1], mergeColumns, srcCell));
+			} else {
+				records.forEach(startEnd -> merge4Col(sheet, startEnd[0], startEnd[1], mergeColumns, srcCell));
+			}
+		}
+
+		private static void merge4Row(Sheet sheet, int fromRow, int toRow, int[] mergeColumns, CellRef srcCell) {
+			if (fromRow >= toRow) {
+				logger.warn("No need to merge because same row:fromRow={}, toRow={}", fromRow, toRow);
+				return;
+			}
+			Cell originCell;
+			CellStyle originCellStyle;
+			CellRangeAddress region;
+			for (int col : mergeColumns) {
+				logger.debug("fromRow={}, toRow={}, col={}", fromRow, toRow, col);
+				region = new CellRangeAddress(fromRow, toRow, col, col);
+				sheet.addMergedRegion(region);
+
+				//firstCell = sheet.getRow(fromRow).getCell(col);
+				originCell = sheet.getRow(srcCell.getRow()).getCell(col);
+				if (originCell == null) {
+					logger.info("Missing cell: row={}, col={}", fromRow, col);
+				}
+				if (originCell != null) {
+					// copy originCell style to the merged cell
+					originCellStyle = originCell.getCellStyle();
+					RegionUtil.setBorderTop(originCellStyle.getBorderTopEnum(), region, sheet);
+					RegionUtil.setBorderRight(originCellStyle.getBorderRightEnum(), region, sheet);
+					RegionUtil.setBorderBottom(originCellStyle.getBorderBottomEnum(), region, sheet);
+					RegionUtil.setBorderLeft(originCellStyle.getBorderLeftEnum(), region, sheet);
+				} else {
+					RegionUtil.setBorderTop(BorderStyle.THIN, region, sheet);
+					RegionUtil.setBorderRight(BorderStyle.THIN, region, sheet);
+					RegionUtil.setBorderBottom(BorderStyle.THIN, region, sheet);
+					RegionUtil.setBorderLeft(BorderStyle.THIN, region, sheet);
+				}
+			}
+		}
+
+		private static void merge4Col(Sheet sheet, int fromCol, int toCol, int[] mergeColumns, CellRef srcCell) {
+			if (fromCol >= toCol) {
+				logger.warn("No need to merge because same row:fromCol={}, toCol={}", fromCol, toCol);
+				return;
+			}
+			Cell originCell;
+			CellStyle originCellStyle;
+			CellRangeAddress region;
+			for (int row : mergeColumns) {
+				logger.debug("fromCol={}, toCol={}, row={}", fromCol, toCol, row);
+				region = new CellRangeAddress(row, row, fromCol, toCol);
+				sheet.addMergedRegion(region);
+
+				//firstCell = sheet.getRow(fromRow).getCell(row);
+				originCell = sheet.getRow(srcCell.getRow()).getCell(row);
+				if (originCell == null) {
+					logger.info("Missing cell: row={}, row={}", fromCol, row);
+				}
+				if (originCell != null) {
+					// copy originCell style to the merged cell
+					originCellStyle = originCell.getCellStyle();
+					RegionUtil.setBorderTop(originCellStyle.getBorderTopEnum(), region, sheet);
+					RegionUtil.setBorderRight(originCellStyle.getBorderRightEnum(), region, sheet);
+					RegionUtil.setBorderBottom(originCellStyle.getBorderBottomEnum(), region, sheet);
+					RegionUtil.setBorderLeft(originCellStyle.getBorderLeftEnum(), region, sheet);
+				} else {
+					RegionUtil.setBorderTop(BorderStyle.THIN, region, sheet);
+					RegionUtil.setBorderRight(BorderStyle.THIN, region, sheet);
+					RegionUtil.setBorderBottom(BorderStyle.THIN, region, sheet);
+					RegionUtil.setBorderLeft(BorderStyle.THIN, region, sheet);
+				}
+			}
+		}
+	}
+}

+ 597 - 0
src/main/java/com/chuanghai/attendance/utils/excel/ExcelBase.java

@@ -0,0 +1,597 @@
+package com.chuanghai.attendance.utils.excel;
+
+import org.apache.poi.hssf.usermodel.*;
+import org.apache.poi.hssf.util.HSSFColor;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.springframework.util.StringUtils;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.sql.Timestamp;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+
+/**
+ * <p>Description: EXCEL导出工具基础类</p>
+ *
+ * @author Jianwen Zhu
+ * @version 1.0
+ * <p>Company:XiPinTech</p>
+ * <p>Copyright:Copyright(c)2015</p>
+ * @date 2016年5月4日
+ */
+public class ExcelBase {
+
+	/**
+	 * 工作簿
+	 */
+	protected static HSSFWorkbook workbook;
+
+	/**
+	 * 工作表
+	 */
+	protected static HSSFSheet sheet;
+
+	/**
+	 * 画图管理器
+	 */
+	protected static HSSFPatriarch patriarch;
+
+	/**
+	 * Long正则表达式
+	 */
+	public static final String LONG_MATCHES_STRING = "^\\-?\\d+$";
+
+	/**
+	 * Double正则表达式
+	 */
+	public static final String DOUBLE_MATCHES_STRING = "^\\-?\\d+(\\.\\d+)?$";
+
+	/**
+	 * 列宽(默认:10)
+	 */
+	private static Integer columnWidth = 10;
+
+	/**
+	 * 行高(默认:20)
+	 */
+	private static short rowHeight = 400;
+
+	/**
+	 * 样式集合
+	 */
+	private static Map<String, CellStyle> cellStyleMap;
+
+	/**
+	 * 当前行号
+	 */
+	protected static int rowIndex = 0;
+
+	/**
+	 * 单个工作表最大行数
+	 */
+	public static final int MAX_ROW = 5000;
+
+	/**
+	 * 单个工作表最大工作表数
+	 */
+	public static final int MAX_SHEET = 5;
+
+	/**
+	 * 工作表的数量
+	 */
+	private static int sheetSize = 0;
+
+	/**
+	 * 临时文件集合
+	 */
+	private static LinkedList<File> fileList = new LinkedList<>();
+
+	/**
+	 * 临时文件根目录
+	 */
+	private static String fileRootPath = "";
+
+	/**
+	 * 请求对象
+	 */
+	private static HttpServletRequest request;
+
+	/**
+	 * 方法用途: 新增工作表<br>
+	 * 如果单个工作簿中工作表数量超过最大值,则将该工作簿写到临时文件中,并新建工作簿<br>
+	 * 实现步骤: <br>
+	 *
+	 * @param fileName 文件名
+	 * @param headers  表头
+	 */
+	protected static void addSheet(String fileName, Object[] headers) throws FileNotFoundException,
+			IOException {
+		if (sheetSize != 0 && sheetSize % MAX_SHEET == 0) {
+			writeFile(fileName);
+			initWorkbook();
+		}
+
+		sheetSize++;
+		sheet = workbook.createSheet(fileName + sheetSize);
+		sheet.setDefaultColumnWidth(columnWidth);
+		sheet.setDefaultRowHeight(rowHeight);
+		patriarch = sheet.createDrawingPatriarch();
+		cellStyleMap = createCellStyleMap(workbook);
+		rowIndex = 0;
+		setTitle(fileName, headers.length);
+		setHeader(headers);
+	}
+
+	/**
+	 * 方法用途: 将工作簿写入临时文件<br>
+	 * 实现步骤: <br>
+	 */
+	private static void writeFile(String fileName) throws FileNotFoundException, IOException {
+		String filePath = fileRootPath + File.separator + fileName + (fileList.size() + 1) + ".xls";
+		File file = new File(filePath);
+		file.createNewFile();
+		BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file));
+		workbook.write(out);
+		out.close();
+		fileList.add(file);
+	}
+
+	/**
+	 * 方法用途: 初始化工作簿<br>
+	 * 并以当前系统毫秒数新建临时文件夹<br>
+	 * 实现步骤: <br>
+	 */
+	protected static void initWorkbook() {
+		workbook = new HSSFWorkbook();
+		if (fileRootPath.isEmpty()) {
+			fileRootPath = request.getServletContext().getRealPath("/") + "temp" + File.separator
+					+ System.currentTimeMillis();
+		}
+		File file = new File(fileRootPath);
+		if (!file.exists()) {
+			file.mkdirs();
+		}
+	}
+
+	/**
+	 * 方法用途: 获取表头样式<br>
+	 * 水平居中 细边框,黑色 填充蓝色<br>
+	 * 实现步骤: <br>
+	 *
+	 * @return 表头样式
+	 */
+	protected static HSSFCellStyle getHeaderCellStyle() {
+		HSSFCellStyle cellStyle = workbook.createCellStyle();
+
+		// 设置表头样式
+		cellStyle.setFillForegroundColor(HSSFColor.GREEN.index);
+		cellStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
+
+		cellStyle.setBorderBottom(HSSFCellStyle.BORDER_THIN);
+		cellStyle.setBorderLeft(HSSFCellStyle.BORDER_THIN);
+		cellStyle.setBorderRight(HSSFCellStyle.BORDER_THIN);
+		cellStyle.setBorderTop(HSSFCellStyle.BORDER_THIN);
+		cellStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER);
+		// 设置标题字体
+		HSSFFont font = workbook.createFont();
+		font.setFontName("宋体");
+		font.setFontHeightInPoints((short) 12);
+		font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
+		cellStyle.setFont(font);
+
+		// 指定当单元格内容显示不下时自动换行
+		cellStyle.setWrapText(true);
+
+		return cellStyle;
+	}
+
+	/**
+	 * 方法用途: 获取标题样式<br>
+	 * 水平居中、垂直居中 字体:15,粗体<br>
+	 * 实现步骤: <br>
+	 */
+	protected static HSSFCellStyle getTitlecCellStyle() {
+		HSSFCellStyle cellStyle = workbook.createCellStyle();
+
+		// 设置标题样式
+		cellStyle.setBorderBottom(HSSFCellStyle.BORDER_THIN);
+		cellStyle.setBorderLeft(HSSFCellStyle.BORDER_THIN);
+		cellStyle.setBorderRight(HSSFCellStyle.BORDER_THIN);
+		cellStyle.setBorderTop(HSSFCellStyle.BORDER_THIN);
+		cellStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER_SELECTION);
+		cellStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
+		// 设置字体
+		HSSFFont titleFont = workbook.createFont();
+		titleFont.setFontName("宋体");
+		titleFont.setFontHeightInPoints((short) 20);
+		titleFont.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
+		cellStyle.setFont(titleFont);
+
+		return cellStyle;
+	}
+
+	/**
+	 * 方法用途: 获取合计样式<br>
+	 * 字体:10,粗体<br>
+	 * 实现步骤: <br>
+	 */
+	protected static HSSFCellStyle getCountCellStyle() {
+		HSSFCellStyle cellStyle = workbook.createCellStyle();
+		cellStyle.setBorderBottom(HSSFCellStyle.BORDER_THIN);
+		cellStyle.setBorderLeft(HSSFCellStyle.BORDER_THIN);
+		cellStyle.setBorderRight(HSSFCellStyle.BORDER_THIN);
+		cellStyle.setBorderTop(HSSFCellStyle.BORDER_THIN);
+		HSSFFont titleFont = workbook.createFont();
+		titleFont.setFontHeightInPoints((short) 10);
+		titleFont.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
+		cellStyle.setFont(titleFont);
+		return cellStyle;
+	}
+
+	/**
+	 * 方法用途: 设置标题行<br>
+	 * 实现步骤: <br>
+	 *
+	 * @param titel       标题
+	 * @param headerWidth 表头宽度
+	 */
+	protected static void setTitle(String titel, int headerWidth) {
+		sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, headerWidth - 1));
+		HSSFRow row = sheet.createRow(0);
+		row.setHeight((short) 500);
+		HSSFCell cellHeader = row.createCell(0);
+		cellHeader.setCellStyle(getTitlecCellStyle());
+		cellHeader.setCellValue(titel);
+		rowIndex++;
+	}
+
+	/**
+	 * 方法用途: 设置表头<br>
+	 * 实现步骤: <br>
+	 *
+	 * @param headers 表头数组
+	 */
+	protected static void setHeader(Object[] headers) {
+		HSSFRow row = sheet.createRow(1);
+		for (int i = 0; i < headers.length; i++) {
+			HSSFCell cell = row.createCell(i);
+			sheet.setColumnWidth(i, (headers[i].toString().length() / 4 + 1) * 2560);
+			cell.setCellStyle(getHeaderCellStyle());
+			cell.setCellValue(headers[i].toString());
+		}
+		rowIndex++;
+	}
+
+	/**
+	 * 方法用途: 输出工作簿<br>
+	 * 实现步骤: <br>
+	 *
+	 * @param fileName 文件名
+	 * @param response 回应对象
+	 *
+	 * @throws Exception 异常
+	 */
+	protected static void outWorkbook(String fileName, HttpServletResponse response)
+			throws Exception {
+		OutputStream out = response.getOutputStream();
+		response.reset();
+
+		String outFileName = "";
+		if (request.getHeader("User-Agent").indexOf("MSIE") != -1) {
+			outFileName = java.net.URLEncoder.encode(fileName, "UTF-8");
+		} else {
+			outFileName = new String(fileName.getBytes("UTF-8"), "ISO-8859-1");
+		}
+		if (fileList.size() == 0) {
+			response.setContentType("application/msexcel");
+			response.setHeader("Content-disposition", "attachment; filename=" + outFileName
+					+ ".xls");
+
+			workbook.write(out);
+		} else {
+			response.setContentType("application/octet-stream");
+			response.setHeader("Content-Disposition", "attachment;filename=" + outFileName + ".zip");
+			// 输出最后一张工作簿
+			writeFile(fileName);
+
+			String zipFilePath = fileRootPath + File.separator + fileName + ".zip";
+			createZipFile(zipFilePath);
+
+			// 输出ZIP文件
+			byte[] buffer = new byte[1024];
+			int len;
+			FileInputStream zipIn = new FileInputStream(zipFilePath);
+			while ((len = zipIn.read(buffer)) > 0) {
+				out.write(buffer, 0, len);
+			}
+			zipIn.close();
+
+			// 删除临时文件
+			deleteFile(fileRootPath);
+		}
+
+		reset();
+	}
+
+	/**
+	 * 方法用途: 将临时文件打包成ZIP压缩包<br>
+	 * 实现步骤: <br>
+	 *
+	 * @param zipFilePath 压缩包路径
+	 */
+	private static void createZipFile(String zipFilePath) throws IOException {
+		ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipFilePath));
+		byte[] buffer = new byte[1024];
+		int len;
+		for (File file : fileList) {
+			FileInputStream fileIn = new FileInputStream(file);
+			zipOut.putNextEntry(new ZipEntry(file.getName()));
+			while ((len = fileIn.read(buffer)) > 0) {
+				zipOut.write(buffer, 0, len);
+			}
+			zipOut.closeEntry();
+			fileIn.close();
+		}
+		zipOut.close();
+	}
+
+	/**
+	 * 方法用途: 重置参数<br>
+	 * 实现步骤: <br>
+	 */
+	private static void reset() {
+		fileList.clear();
+		fileRootPath = "";
+		sheetSize = 0;
+	}
+
+	/**
+	 * 方法用途: 删除文件或文件夹<br>
+	 * 实现步骤: <br>
+	 *
+	 * @param filePath 文件或文件夹路径
+	 */
+	public static void deleteFile(String filePath) throws IOException {
+		File file = new File(filePath);
+		if (!file.exists()) {
+			return;
+		}
+		if (file.isDirectory()) {
+			if (file.listFiles().length == 0) {
+				file.delete();
+			} else {
+				File delFile[] = file.listFiles();
+				for (File fileItem : delFile) {
+					if (fileItem.isDirectory()) {
+						deleteFile(fileItem.getAbsolutePath());
+					}
+					fileItem.delete();
+				}
+			}
+		}
+		file.delete();
+	}
+
+	/**
+	 * 方法用途: 将值对象格式化为字符串<br>
+	 * 实现步骤: <br>
+	 *
+	 * @param value       值对象
+	 * @param dateFormat  时间格式
+	 * @param rowIndex    行标
+	 * @param columnIndex 列标
+	 *
+	 * @return 值对象字符串
+	 */
+	protected static String getValueString(Object value, String dateFormat, int rowIndex,
+										   int columnIndex) {
+		String valueString = "";
+		if (value == null) {
+			return valueString;
+		} else if (value instanceof Date) {
+			SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
+			valueString = sdf.format((Date) value);
+		} else if (value instanceof Timestamp) {
+			SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
+			valueString = sdf.format((Timestamp) value);
+		} else if (value instanceof byte[]) {
+			// 有图片时,设置行高为60px;
+			HSSFRow row = sheet.getRow(rowIndex);
+			row.setHeightInPoints(60);
+			// 设置图片所在列宽度为80px,注意这里单位的一个换算
+			sheet.setColumnWidth(columnIndex, (short) (35.7 * 80));
+			byte[] valueByte = (byte[]) value;
+			HSSFClientAnchor anchor = new HSSFClientAnchor(0, 0, 1023, 255, (short) 6, rowIndex,
+					(short) 6, rowIndex);
+			anchor.setAnchorType(2);
+			patriarch.createPicture(anchor,
+					workbook.addPicture(valueByte, HSSFWorkbook.PICTURE_TYPE_JPEG));
+		} else if (value instanceof String[]) {
+			//字符串数组
+			for (String string : (String[]) value) {
+				valueString = valueString + string + ",";
+			}
+		} else {
+			valueString = value.toString();
+		}
+		return valueString;
+	}
+
+	/**
+	 * 方法用途: 获取值对象<br>
+	 * 实现步骤: <br>
+	 *
+	 * @param resultItem 行数据
+	 * @param fieldName  值名称
+	 *
+	 * @return 值对象
+	 *
+	 * @throws Exception 异常
+	 */
+	protected static <T> Object getValueObject(T resultItem, String fieldName) throws Exception {
+		Object value = new Object();
+		if (resultItem instanceof Map) {
+			value = ((Map<?, ?>) resultItem).get(fieldName);
+		} else {
+			Class<? extends Object> resultItemClass = resultItem.getClass();
+			String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase()
+					+ fieldName.substring(1);
+			Method getMethod = resultItemClass.getMethod(getMethodName, new Class[]{});
+			value = getMethod.invoke(resultItem, new Object[]{});
+		}
+		return value;
+	}
+
+	/**
+	 * 方法用途: 设置单元格值<br>
+	 * 实现步骤: <br>
+	 *
+	 * @param valueString 单元格值
+	 * @param cell        单元格
+	 */
+	protected static void setCellValue(String valueString, Cell cell) {
+		if (StringUtils.isEmpty(valueString)) {
+			cell.setCellStyle(getCellStyle("stringCellStyle"));
+			return;
+		}
+		if (valueString.matches(LONG_MATCHES_STRING)) {
+			cell.setCellStyle(getCellStyle("longCellStyle"));
+			cell.setCellValue(Long.valueOf(valueString));
+		} else if (valueString.matches(DOUBLE_MATCHES_STRING)) {
+			cell.setCellStyle(getCellStyle("doubleCellStyle"));
+			cell.setCellValue(Double.valueOf(valueString));
+		} else {
+			cell.setCellStyle(getCellStyle("stringCellStyle"));
+			cell.setCellValue(valueString);
+		}
+	}
+
+	/**
+	 * 方法用途: 创建单元格格式集合<br>
+	 * 实现步骤: <br>
+	 */
+	private static Map<String, CellStyle> createCellStyleMap(Workbook workbook) {
+		Map<String, CellStyle> cellStyleMap = new HashMap<>();
+
+		CellStyle longCellStyle = createCellStyle(workbook);
+		longCellStyle.setDataFormat(HSSFDataFormat.getBuiltinFormat("0"));
+		cellStyleMap.put("longCellStyle", longCellStyle);
+
+		CellStyle doubleCellStyle = createCellStyle(workbook);
+		doubleCellStyle.setDataFormat(HSSFDataFormat.getBuiltinFormat("0.00"));
+		cellStyleMap.put("doubleCellStyle", doubleCellStyle);
+
+		CellStyle stringCellStyle = createCellStyle(workbook);
+		stringCellStyle.setDataFormat(HSSFDataFormat.getBuiltinFormat("0.00"));
+		cellStyleMap.put("stringCellStyle", stringCellStyle);
+
+		return cellStyleMap;
+	}
+
+	/**
+	 * 获取通用单元格
+	 */
+	private static CellStyle createCellStyle(Workbook workbook) {
+		CellStyle cellStyle = workbook.createCellStyle();
+		cellStyle.setBorderBottom(HSSFCellStyle.BORDER_THIN);
+		cellStyle.setBorderLeft(HSSFCellStyle.BORDER_THIN);
+		cellStyle.setBorderRight(HSSFCellStyle.BORDER_THIN);
+		cellStyle.setBorderTop(HSSFCellStyle.BORDER_THIN);
+		return cellStyle;
+	}
+
+	/**
+	 * 方法用途: 获取单元格格式<br>
+	 * 如果没有抛出空指针异常<br>
+	 * 实现步骤: <br>
+	 *
+	 * @param styleName    格式名称
+	// * @param cellStyleMap 单元格格式集合
+	 *
+	 * @return 单元格样式
+	 */
+	protected static CellStyle getCellStyle(String styleName) {
+		if (cellStyleMap.containsKey(styleName)) {
+			return cellStyleMap.get(styleName);
+		} else {
+			throw new NullPointerException(styleName + "is null");
+		}
+	}
+
+	/**
+	 * 方法用途: 判断对象属性或集合中是否存在该元素<br>
+	 * 实现步骤: <br>
+	 *
+	 * @param t         对象或者集合
+	 * @param fieldName 元素
+	 *
+	 * @return 判断结果
+	 */
+	protected static <T> boolean hasField(T t, Object fieldName) {
+		if (t instanceof Map) {
+			if (((Map<?, ?>) t).containsKey(fieldName)) {
+				return true;
+			}
+		} else {
+			Field[] fields = t.getClass().getFields();
+			for (Field field : fields) {
+				if (field.getName().equals(fieldName)) {
+					return true;
+				}
+			}
+		}
+
+		return false;
+	}
+
+	/**
+	 * 方法用途: 创建数据行<br>
+	 * 当数据行超过行最大值时,新建一个工作表
+	 * 实现步骤: <br>
+	 *
+	 * @return 新数据行
+	 */
+	protected static HSSFRow createRow(String fileName, Object[] headers)
+			throws FileNotFoundException, IOException {
+		if (rowIndex >= MAX_ROW) {
+			addSheet(fileName, headers);
+		}
+		HSSFRow row = sheet.createRow(rowIndex);
+		row.setHeight((short) 400);
+		return row;
+	}
+
+	/**
+	 * {@linkplain #columnWidth}
+	 */
+	public static Integer getColumnWidth() {
+		return columnWidth;
+	}
+
+	/**
+	 * {@linkplain #columnWidth}
+	 */
+	public static void setColumnWidth(Integer columnWidth) {
+		ExcelBase.columnWidth = columnWidth;
+	}
+
+	/**
+	 * {@linkplain #request}
+	 */
+	public static void setRequest(HttpServletRequest request) {
+		ExcelBase.request = request;
+	}
+}

+ 470 - 0
src/main/java/com/chuanghai/attendance/utils/excel/ExcelExportUtil.java

@@ -0,0 +1,470 @@
+package com.chuanghai.attendance.utils.excel;
+
+import com.chuanghai.attendance.common.exception.BizCodeEnume;
+import com.chuanghai.attendance.common.exception.RRException;
+import net.sf.jxls.exception.ParsePropertyException;
+import net.sf.jxls.transformer.XLSTransformer;
+import org.apache.poi.POIXMLDocument;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.usermodel.WorkbookFactory;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.jxls.area.Area;
+import org.jxls.builder.AreaBuilder;
+import org.jxls.builder.xls.XlsCommentAreaBuilder;
+import org.jxls.common.CellRef;
+import org.jxls.common.Context;
+import org.jxls.transform.Transformer;
+import org.jxls.transform.poi.PoiTransformer;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * <p>Description: EXCEL模板导出工具类</p>
+ *
+ * @author Jianwen Zhu
+ * @version 1.0
+ * <p>Company:XiPinTech</p>
+ * <p>Copyright:Copyright(c)2015</p>
+ * @date 2016年5月10日
+ */
+public class ExcelExportUtil {
+
+	static {
+		XlsCommentAreaBuilder.addCommandMapping("each", EachCommand.class);
+		XlsCommentAreaBuilder.addCommandMapping(EachMergeCommand.COMMAND_NAME, EachMergeCommand.class);
+	}
+
+	/**
+	 * 方法用途: 输出Excel工作簿<br>
+	 * 实现步骤: <br>
+	 *
+	 * @param request  请求对象
+	 * @param response 回复对象
+	 * @param workbook 工作簿
+	 * @param fileName 文件名
+	 *
+	 * @throws UnsupportedEncodingException 字符编码异常
+	 * @throws IOException                  IO异常
+	 */
+	private static void export(HttpServletRequest request, HttpServletResponse response, Workbook workbook, String fileName) throws UnsupportedEncodingException, IOException {
+
+		OutputStream out = response.getOutputStream();
+		response.reset();
+
+		String outFileName = "";
+		if (request.getHeader("User-Agent").indexOf("MSIE") != -1) {
+			outFileName = URLEncoder.encode(fileName, "UTF-8");
+		} else {
+			outFileName = new String(fileName.getBytes("UTF-8"), "ISO-8859-1");
+		}
+		response.setContentType("application/msexcel");
+		response.setHeader("Content-disposition", "attachment; filename=" + outFileName);
+
+		workbook.write(out);
+	}
+
+	/**
+	 * 方法用途: 输出Excel工作簿<br>
+	 * 实现步骤: <br>
+	 *
+	 * @param request  请求对象
+	 * @param response 回复对象
+	 * @param workbook 工作簿
+	 * @param fileName 文件名
+	 * @param filePath 指定文件名
+	 *
+	 * @throws UnsupportedEncodingException 字符编码异常
+	 * @throws IOException                  IO异常
+	 */
+	private static void export(HttpServletRequest request, HttpServletResponse response, Workbook workbook, String fileName, String filePath) throws UnsupportedEncodingException, IOException {
+
+		FileOutputStream out = new FileOutputStream(filePath);
+		String outFileName = "";
+		if (request.getHeader("User-Agent").indexOf("MSIE") != -1) {
+			outFileName = URLEncoder.encode(fileName, "UTF-8");
+		} else {
+			outFileName = new String(fileName.getBytes("UTF-8"), "ISO-8859-1");
+		}
+		response.setContentType("application/msexcel");
+		response.setHeader("Content-disposition", "attachment; filename=" + outFileName);
+
+		workbook.write(out);
+	}
+
+	private static OutputStream getOutPut(HttpServletRequest request, HttpServletResponse response, Workbook workbook, String fileName) throws UnsupportedEncodingException, IOException {
+		OutputStream out = response.getOutputStream();
+		response.reset();
+
+		String outFileName = "";
+		if (request.getHeader("User-Agent").indexOf("MSIE") != -1) {
+			outFileName = URLEncoder.encode(fileName, "UTF-8");
+		} else {
+			outFileName = new String(fileName.getBytes("UTF-8"), "ISO-8859-1");
+		}
+		response.setContentType("application/msexcel");
+		response.setHeader("Content-disposition", "attachment; filename=" + outFileName);
+		return out;
+	}
+
+	/**
+	 * 方法用途: <br>
+	 * 实现步骤: <br>
+	 *
+	 * @param request      请求对象
+	 * @param response     回复对象
+	 * @param templateName Excel模板名称
+	 * @param fileName     导出文件名
+	 * @param beanParams   导出数据
+	 *
+	 * @throws Exception 异常
+	 */
+	public static void exportExcel(HttpServletRequest request, HttpServletResponse response, String templateName, String fileName, Map<String, Object> beanParams) throws Exception {
+
+		XLSTransformer transformer = new XLSTransformer();
+		InputStream in = getTemplateInputSteam(templateName);
+		String suffix = "";
+		if (!in.markSupported()) {
+			in = new PushbackInputStream(in, 8);
+		}
+		if (POIFSFileSystem.hasPOIFSHeader(in)) {
+			suffix = ".xls";
+		} else if (POIXMLDocument.hasOOXMLHeader(in)) {
+			suffix = ".xlsx";
+		}
+
+		Workbook workbook = transformer.transformXLS(in, beanParams);
+
+		export(request, response, workbook, fileName + suffix);
+
+
+	}
+
+	private static InputStream getTemplateInputSteam(String templateName) {
+		return ExcelExportUtil.class.getClassLoader().getResourceAsStream("static" + File.separator + "excel" + File.separator + templateName);
+	}
+
+	/**
+	 * 方法用途: <br>
+	 * 实现步骤: <br>
+	 *
+	 * @param request      请求对象
+	 * @param response     回复对象
+	 * @param templateName Excel模板名称
+	 * @param fileName     导出文件名
+	 * @param beanParams   导出数据
+	 *
+	 * @throws Exception 异常
+	 */
+	public static Workbook getWorkBook(HttpServletRequest request, HttpServletResponse response, String templateName, String fileName, Map<String, Object> beanParams) throws Exception {
+
+		XLSTransformer transformer = new XLSTransformer();
+		InputStream in = getTemplateInputSteam(templateName);
+
+		if (!in.markSupported()) {
+			in = new PushbackInputStream(in, 8);
+		}
+
+		/*		export(request, response, workbook, fileName + suffix);
+		 */
+		return transformer.transformXLS(in, beanParams);
+	}
+
+	/**
+	 * 方法用途: <br>
+	 * 实现步骤: <br>
+	 *
+	 * @param request      请求对象
+	 * @param response     回复对象
+	 * @param templateName Excel模板名称
+	 * @param fileName     导出文件名
+	 * @param filePath     导出文件路径
+	 * @param beanParams   导出数据
+	 *
+	 * @throws Exception 异常
+	 */
+	public static void exportExcel(HttpServletRequest request, HttpServletResponse response, String templateName, String fileName, Map<String, Object> beanParams, String filePath) throws Exception {
+		XLSTransformer transformer = new XLSTransformer();
+		String realPath = request.getServletContext().getRealPath("/WEB-INF/excel");
+		InputStream in = new FileInputStream(realPath + File.separator + templateName);
+
+		String suffix = "";
+		if (!in.markSupported()) {
+			in = new PushbackInputStream(in, 8);
+		}
+		if (POIFSFileSystem.hasPOIFSHeader(in)) {
+			suffix = ".xls";
+		} else if (POIXMLDocument.hasOOXMLHeader(in)) {
+			suffix = ".xlsx";
+		}
+		Workbook workbook = transformer.transformXLS(in, beanParams);
+		export(request, response, workbook, fileName + suffix, filePath);
+	}
+
+	public static void exportExcelLarge(HttpServletRequest request, HttpServletResponse response, String templateName, String fileName, Map<String, Object> beanParams, String sheetName) throws Exception {
+		InputStream in = getTemplateInputSteam(templateName);
+		Workbook workbook = WorkbookFactory.create(in);
+		Transformer transformer = PoiTransformer.createSxssfTransformer(workbook, 5000, false);
+		AreaBuilder areaBuilder = new XlsCommentAreaBuilder(transformer);
+		List<Area> xlsAreaList = areaBuilder.build();
+		Area xlsArea = xlsAreaList.get(0);
+		Context context = new Context(beanParams);
+		xlsArea.applyAt(new CellRef(sheetName + "结果!A1"), context);
+		xlsArea.processFormulas();
+		PoiTransformer poiTransformer = (PoiTransformer) transformer;
+		poiTransformer.getWorkbook().removeSheetAt(0);
+		poiTransformer.getWorkbook().write(getOutPut(request, response, workbook, fileName));
+	}
+
+//	public static void main(String[] args) throws Exception {
+//		InputStream in = new FileInputStream("D:\\拖车费率导出.xlsx");
+//        Workbook workbook = WorkbookFactory.create(in);
+//        Transformer transformer = PoiTransformer.createSxssfTransformer(workbook, 5000, false);
+//        AreaBuilder areaBuilder = new XlsCommentAreaBuilder(transformer);
+//        List<Area> xlsAreaList = areaBuilder.build();
+//        Area xlsArea = xlsAreaList.get(0);
+//        System.out.println(1);
+//	}
+
+	public static void uploadExcel(HttpServletRequest request, HttpServletResponse response, String templateName, Map<String, Object> beanParams, String uploadPaths) throws Exception {
+
+		XLSTransformer transformer = new XLSTransformer();
+		String realPath = request.getServletContext().getRealPath("/WEB-INF/excel");
+		InputStream in = new FileInputStream(realPath + File.separator + templateName);
+
+		String suffix = "";
+		if (!in.markSupported()) {
+			in = new PushbackInputStream(in, 8);
+		}
+		if (POIFSFileSystem.hasPOIFSHeader(in)) {
+			suffix = ".xls";
+		} else if (POIXMLDocument.hasOOXMLHeader(in)) {
+			suffix = ".xlsx";
+		}
+
+		Workbook workbook = transformer.transformXLS(in, beanParams);
+
+		File loadFile = new File(uploadPaths);
+		if (!loadFile.getParentFile().exists()) {
+			if (!loadFile.getParentFile().mkdirs()) {
+
+				throw new RRException(BizCodeEnume.BODY_IS_EMPTY,"创建目标文件所在目录失败!");
+			}
+		}
+		FileOutputStream fout = new FileOutputStream(uploadPaths);
+		workbook.write(fout);
+
+	}
+
+	/**
+	 * 通过文件服务器下载excel模板,并生成workbook对象及后缀名
+	 */
+	public static Map<String, Object> generateWorkbook(HttpServletRequest request, String templatePath, Map<String, Object> beanParams) throws IOException, ParsePropertyException, InvalidFormatException, URISyntaxException {
+
+		XLSTransformer transformer = new XLSTransformer();
+		//String realPath = request.getSession().getServletContext().getRealPath("/WEB-INF/excel");
+		//InputStream in = new FileInputStream(realPath + File.separator + templateName);
+		URL url = new URL(templatePath);
+		InputStream in = url.openStream();
+		String suffix = "";
+		if (!in.markSupported()) {
+			in = new PushbackInputStream(in, 8);
+		}
+		if (POIFSFileSystem.hasPOIFSHeader(in)) {
+			suffix = ".xls";
+		} else if (POIXMLDocument.hasOOXMLHeader(in)) {
+			suffix = ".xlsx";
+		}
+
+		Workbook workbook = transformer.transformXLS(in, beanParams);
+
+		Map<String, Object> result = new HashMap<String, Object>();
+		result.put("suffix", suffix);
+		result.put("workbook", workbook);
+
+		if (in != null)
+			in.close();
+
+		return result;
+	}
+
+
+	/**
+	 * 通过文件服务器下载excel模板,并生成workbook对象及后缀名
+	 */
+	public static Map<String, Object> newGenerateWorkbook(HttpServletRequest request, String templatePath, Map<String, Object> beanParams) throws IOException, ParsePropertyException, InvalidFormatException, URISyntaxException {
+
+		URL url = new URL(templatePath);
+		InputStream in = url.openStream();
+		String suffix = "";
+		if (!in.markSupported()) {
+			in = new PushbackInputStream(in, 8);
+		}
+		if (POIFSFileSystem.hasPOIFSHeader(in)) {
+			suffix = ".xls";
+		} else if (POIXMLDocument.hasOOXMLHeader(in)) {
+			suffix = ".xlsx";
+		}
+
+		Workbook workbook = WorkbookFactory.create(in);
+		Transformer transformer = PoiTransformer.createSxssfTransformer(workbook, 5000, true);
+		AreaBuilder areaBuilder = new XlsCommentAreaBuilder(transformer);
+		List<Area> xlsAreaList = areaBuilder.build();
+		Area xlsArea = xlsAreaList.get(0);
+		Context context = new Context(beanParams);
+		xlsArea.applyAt(new CellRef("结果!A1"), context);
+		xlsArea.processFormulas();
+		PoiTransformer poiTransformer = (PoiTransformer) transformer;
+		poiTransformer.getWorkbook().removeSheetAt(0);
+		Workbook test = ((PoiTransformer) transformer).getWorkbook();
+		Map<String, Object> result = new HashMap<String, Object>();
+		result.put("suffix", suffix);
+		result.put("workbook", test);
+
+		if (in != null)
+			in.close();
+
+		return result;
+	}
+
+	/**
+	 * @param mergeList 合并位置 int[](firstRow,lastRow,firstCol,lastCol)(行从最后一行开始数)
+	 */
+	public static void exportMergeExcel(HttpServletRequest request, HttpServletResponse response, String templateName, String fileName, Map<String, Object> beanParams, List<int[]> mergeList) throws Exception {
+
+		XLSTransformer transformer = new XLSTransformer();
+		String realPath = request.getServletContext().getRealPath("/WEB-INF/excel");
+		InputStream in = new FileInputStream(realPath + File.separator + templateName);
+
+		String suffix = "";
+		if (!in.markSupported()) {
+			in = new PushbackInputStream(in, 8);
+		}
+		if (POIFSFileSystem.hasPOIFSHeader(in)) {
+			suffix = ".xls";
+		} else if (POIXMLDocument.hasOOXMLHeader(in)) {
+			suffix = ".xlsx";
+		}
+
+		Workbook workbook = transformer.transformXLS(in, beanParams);
+		Sheet sheet = workbook.getSheetAt(0);
+		int rowNum = sheet.getLastRowNum();
+
+
+		for (int[] i : mergeList) {
+			if (i.length < 4) {
+//				throw new BusinessException("合并格式错误!");
+			}
+			sheet.addMergedRegion(new CellRangeAddress(rowNum - i[0], rowNum - i[1], i[2], i[3]));
+		}
+//		sheet.addMergedRegion(new CellRangeAddress(rowNum-4,rowNum-4,0,12));
+//		sheet.addMergedRegion(new CellRangeAddress(rowNum-3,rowNum-3,0,12));
+//		sheet.addMergedRegion(new CellRangeAddress(rowNum-5,rowNum-5,0,12));
+		export(request, response, workbook, fileName + suffix);
+	}
+
+	/**
+	 * @param mergeList 合并位置 int[](firstRow,lastRow,firstCol,lastCol)(row行从最后一行开始数)
+	 */
+	public static void exportMergeExcel(HttpServletRequest request, HttpServletResponse response, String templateName, String fileName, Map<String, Object> beanParams, String filePath, List<int[]> mergeList) throws Exception {
+		XLSTransformer transformer = new XLSTransformer();
+		String realPath = request.getServletContext().getRealPath("/WEB-INF/excel");
+		InputStream in = new FileInputStream(realPath + File.separator + templateName);
+
+		String suffix = "";
+		if (!in.markSupported()) {
+			in = new PushbackInputStream(in, 8);
+		}
+		if (POIFSFileSystem.hasPOIFSHeader(in)) {
+			suffix = ".xls";
+		} else if (POIXMLDocument.hasOOXMLHeader(in)) {
+			suffix = ".xlsx";
+		}
+		Workbook workbook = transformer.transformXLS(in, beanParams);
+		Sheet sheet = workbook.getSheetAt(0);
+		int rowNum = sheet.getLastRowNum();
+
+		for (int[] i : mergeList) {
+			if (i.length < 4) {
+	//			throw new BusinessException("合并格式错误!");
+			}
+			sheet.addMergedRegion(new CellRangeAddress(rowNum - i[0], rowNum - i[1], i[2], i[3]));
+		}
+//		sheet.addMergedRegion(new CellRangeAddress(rowNum-4,rowNum-4,0,12));
+//		sheet.addMergedRegion(new CellRangeAddress(rowNum-3,rowNum-3,0,12));
+//		sheet.addMergedRegion(new CellRangeAddress(rowNum-5,rowNum-5,0,12));
+		export(request, response, workbook, fileName + suffix, filePath);
+	}
+
+	public static void exportDeadlineExcel(HttpServletRequest request, HttpServletResponse response, String templateName, String fileName, Map<String, Object> beanParams, String filePath) throws Exception {
+		XLSTransformer transformer = new XLSTransformer();
+		String realPath = request.getServletContext().getRealPath("/WEB-INF/excel");
+		InputStream in = new FileInputStream(realPath + File.separator + templateName);
+
+		String suffix = "";
+		if (!in.markSupported()) {
+			in = new PushbackInputStream(in, 8);
+		}
+		if (POIFSFileSystem.hasPOIFSHeader(in)) {
+			suffix = ".xls";
+		} else if (POIXMLDocument.hasOOXMLHeader(in)) {
+			suffix = ".xlsx";
+		}
+		Workbook workbook = transformer.transformXLS(in, beanParams);
+		Sheet sheet = workbook.getSheetAt(0);
+		int rowNum = sheet.getLastRowNum();
+
+//		sheet.addMergedRegion(new CellRangeAddress(rowNum-4,rowNum-4,0,12));
+//		sheet.addMergedRegion(new CellRangeAddress(rowNum-3,rowNum-3,0,12));
+//		sheet.addMergedRegion(new CellRangeAddress(rowNum-5,rowNum-5,0,12));
+		FileOutputStream out = new FileOutputStream(filePath);
+		String outFileName = "";
+//		if (request.getHeader("User-Agent").indexOf("MSIE") != -1) {
+//			outFileName = URLEncoder.encode(fileName, "UTF-8");
+//		} else {
+//			outFileName = new String(fileName.getBytes("UTF-8"), "ISO-8859-1");
+//		}
+//		response.setContentType("application/msexcel");
+//		response.setHeader("Content-disposition", "attachment; filename=" + outFileName);
+
+		workbook.write(out);
+	}
+
+	public static Map<String, Object> generateWorkbookFromLocal(HttpServletRequest request, String templateName, Map<String, Object> beanParams) throws IOException, ParsePropertyException, InvalidFormatException, URISyntaxException {
+
+		XLSTransformer transformer = new XLSTransformer();
+		String realPath = request.getServletContext().getRealPath("/WEB-INF/excel");
+		InputStream in = new FileInputStream(realPath + File.separator + templateName);
+		String suffix = "";
+		if (!in.markSupported()) {
+			in = new PushbackInputStream(in, 8);
+		}
+		if (POIFSFileSystem.hasPOIFSHeader(in)) {
+			suffix = ".xls";
+		} else if (POIXMLDocument.hasOOXMLHeader(in)) {
+			suffix = ".xlsx";
+		}
+
+		Workbook workbook = transformer.transformXLS(in, beanParams);
+
+		Map<String, Object> result = new HashMap<String, Object>();
+		result.put("suffix", suffix);
+		result.put("workbook", workbook);
+
+		if (in != null)
+			in.close();
+
+		return result;
+	}
+}

+ 160 - 0
src/main/java/com/chuanghai/attendance/utils/excel/ExcelImportXLSUtil.java

@@ -0,0 +1,160 @@
+package com.chuanghai.attendance.utils.excel;
+
+import org.apache.poi.hssf.eventusermodel.*;
+import org.apache.poi.hssf.record.*;
+import org.apache.poi.hssf.usermodel.HSSFDateUtil;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+
+import java.io.*;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+public class ExcelImportXLSUtil implements HSSFListener {
+	private SSTRecord sstrec;
+
+	private List<String[]> list = new ArrayList<String[]>();
+
+	private int totalCol = 0;
+
+	private int sheetNum = 0;
+
+	private FormatTrackingHSSFListener formatListener;
+
+	private boolean isComplete = false;
+
+	public ExcelImportXLSUtil(int totalCol){
+		this.totalCol = totalCol;
+	}
+	/**
+	 * This method listens for incoming records and handles them as required.
+	 *
+	 * @param record
+	 *            The record that was found while reading.
+	 */
+	public void processRecord(Record record) {
+		if(sheetNum > 1){
+			return;
+		}
+		if(isComplete){
+			return;
+		}
+		switch (record.getSid()) {
+		// the BOFRecord can represent either the beginning of a sheet or the
+		// workbook
+		case BOFRecord.sid:
+			BOFRecord bof = (BOFRecord) record;
+			if (bof.getType() == bof.TYPE_WORKBOOK) {
+				System.out.println("Encountered workbook");
+				// assigned to the class level member
+			} else if (bof.getType() == bof.TYPE_WORKSHEET) {
+				System.out.println("Encountered s");
+				sheetNum++;
+			}
+			break;
+		case NumberRecord.sid:
+			NumberRecord numrec = (NumberRecord) record;
+			if(get(numrec.getRow()).length <= numrec.getColumn()){
+				break;
+			}
+			if("m/d/yy".equals(formatListener.getFormatString(numrec))){
+				Date date=HSSFDateUtil.getJavaDate(numrec.getValue());
+				get(numrec.getRow())[numrec.getColumn()] = formateDateToString(date);
+			} else{
+				int val = new Double(numrec.getValue()).intValue();
+				if(numrec.getValue() == val){
+					get(numrec.getRow())[numrec.getColumn()] = val+"";
+				} else{
+					get(numrec.getRow())[numrec.getColumn()] = numrec.getValue()+"";
+				}
+			}
+			break;
+		case SSTRecord.sid:
+			sstrec = (SSTRecord) record;
+			break;
+		case BlankRecord.sid:
+			BlankRecord b = (BlankRecord) record;
+			if(b.getColumn() == 0){
+				isComplete = true;
+				return;
+			}
+			if(get(b.getRow()).length <= b.getColumn()){
+				break;
+			}
+			get(b.getRow())[b.getColumn()] = "";
+			break;
+		case LabelSSTRecord.sid:
+			LabelSSTRecord lrec = (LabelSSTRecord) record;
+			if(get(lrec.getRow()).length <= lrec.getColumn()){
+				break;
+			}
+			get(lrec.getRow())[lrec.getColumn()] = sstrec.getString(lrec.getSSTIndex()).toString();
+			break;
+		}
+	}
+
+	private String formateDateToString(Date date) {
+		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//格式化日期
+		return sdf.format(date);
+	}
+
+	private String[] get(int i){
+		if(this.list.size() < i + 1){
+			System.out.println(i);
+			this.list.add(new String[this.totalCol]);
+		}
+		return this.list.get(i);
+	}
+
+	/**
+	 * Read an excel file and spit out what we find.
+	 *
+//	 * @param args
+	 *            Expect one argument that is the file to read.
+	 * @throws IOException
+	 *             When there is an error processing the file.
+	 */
+	public static List<String[]> readerExcel(InputStream fin, int totalCol) throws IOException {
+		// create a new file input stream with the input file specified
+		// at the command line
+		// create a new org.apache.poi.poifs.filesystem.Filesystem
+		ExcelImportXLSUtil lsnr = new ExcelImportXLSUtil(totalCol);
+
+        MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(lsnr);
+        lsnr.formatListener = new FormatTrackingHSSFListener(listener);
+		POIFSFileSystem poifs = new POIFSFileSystem(fin);
+		// get the Workbook (excel part) stream in a InputStream
+		InputStream din = poifs.createDocumentInputStream("Workbook");
+		// construct out HSSFRequest object
+		HSSFRequest req = new HSSFRequest();
+		// lazy listen for ALL records with the listener shown above
+
+		{
+			req.addListenerForAllRecords(lsnr.formatListener);
+		}
+		// create our event factory
+		HSSFEventFactory factory = new HSSFEventFactory();
+		// process our events based on the document input stream
+		factory.processEvents(req, din);
+		// once all the events are processed close our file input stream
+		fin.close();
+		// and our document input stream (don't want to leak these!)
+		din.close();
+		return lsnr.list;
+	}
+
+	public static void print(List<String[]> list){
+		for(String[] l : list){
+			for(String s : l){
+				System.out.print(s + " ->");
+			}
+			System.out.println();
+		}
+	}
+
+	public static void main(String[] args) throws FileNotFoundException, IOException {
+		List<String[]> list = ExcelImportXLSUtil.readerExcel(new FileInputStream(new File("C:\\Users\\Administrator\\Desktop\\项目空间\\111.xlsx")), 9);
+		print(list);
+	}
+}

+ 464 - 0
src/main/java/com/chuanghai/attendance/utils/excel/ExcelImportXLSXUtil.java

@@ -0,0 +1,464 @@
+package com.chuanghai.attendance.utils.excel;
+
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+import org.apache.poi.hssf.usermodel.HSSFDateUtil;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.opc.OPCPackage;
+import org.apache.poi.openxml4j.opc.PackageAccess;
+import org.apache.poi.ss.usermodel.BuiltinFormats;
+import org.apache.poi.ss.usermodel.DataFormatter;
+import org.apache.poi.xssf.eventusermodel.ReadOnlySharedStringsTable;
+import org.apache.poi.xssf.eventusermodel.XSSFReader;
+import org.apache.poi.xssf.model.StylesTable;
+import org.apache.poi.xssf.usermodel.XSSFCellStyle;
+import org.apache.poi.xssf.usermodel.XSSFRichTextString;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.DefaultHandler;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 使用CVS模式解决XLSX文件,可以有效解决用户模式内存溢出的问题
+ * 该模式是POI官方推荐的读取大数据的模式,在用户模式下,数据量较大、Sheet较多、或者是有很多无用的空行的情况
+ * ,容易出现内存溢出,用户模式读取Excel的典型代码如下: FileInputStream file=new
+ * FileInputStream("c:\\test.xlsx"); Workbook wb=new XSSFWorkbook(file);
+ *
+ * @author 山人
+ */
+public class ExcelImportXLSXUtil {
+
+    /**
+     * The type of the data value is indicated by an attribute on the cell. The
+     * value is usually in a "v" element within the cell.
+     */
+    enum xssfDataType {
+        BOOL, ERROR, FORMULA, INLINESTR, SSTINDEX, NUMBER,
+    }
+
+    /**
+     * 使用xssf_sax_API处理Excel,请参考: http://poi.apache.org/spreadsheet/how-to.html#xssf_sax_api
+     * <p/>
+     * Also see Standard ECMA-376, 1st edition, part 4, pages 1928ff, at
+     * http://www.ecma-international.org/publications/standards/Ecma-376.htm
+     * <p/>
+     * A web-friendly version is http://openiso.org/Ecma/376/Part4
+     */
+    class MyXSSFSheetHandler extends DefaultHandler {
+
+        /**
+         * Table with styles
+         */
+        private StylesTable stylesTable;
+
+        /**
+         * Table with unique strings
+         */
+        private ReadOnlySharedStringsTable sharedStringsTable;
+
+        /**
+         * Destination for data
+         */
+        private final PrintStream output;
+
+        /**
+         * Number of columns to read starting with leftmost
+         */
+        private final int minColumnCount;
+
+        // Set when V start element is seen
+        private boolean vIsOpen;
+
+        // Set when cell start element is seen;
+        // used when cell close element is seen.
+        private xssfDataType nextDataType;
+
+        // Used to format numeric cell values.
+        private short formatIndex;
+        private String formatString;
+        private final DataFormatter formatter;
+
+        private int thisColumn = -1;
+        // The last column printed to the output stream
+        private int lastColumnNumber = -1;
+
+        // Gathers characters as they are seen.
+        private StringBuffer value;
+        private String[] record;
+        private List<String[]> rows = new ArrayList<String[]>();
+        private boolean isCellNull = false;
+
+        /**
+         * Accepts objects needed while parsing.
+         *
+         * @param styles  Table of styles
+         * @param strings Table of shared strings
+         * @param cols    Minimum number of columns to show
+         * @param target  Sink for output
+         */
+        public MyXSSFSheetHandler(StylesTable styles,
+                                  ReadOnlySharedStringsTable strings, int cols, PrintStream target) {
+            this.stylesTable = styles;
+            this.sharedStringsTable = strings;
+            this.minColumnCount = cols;
+            this.output = target;
+            this.value = new StringBuffer();
+            this.nextDataType = xssfDataType.NUMBER;
+            this.formatter = new DataFormatter();
+            record = new String[this.minColumnCount];
+            rows.clear();// 每次读取都清空行集合
+        }
+
+        /*
+         * (non-Javadoc)
+         *
+         * @see
+         * org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String,
+         * java.lang.String, java.lang.String, org.xml.sax.Attributes)
+         */
+        public void startElement(String uri, String localName, String name,
+                                 Attributes attributes) throws SAXException {
+
+            if ("inlineStr".equals(name) || "v".equals(name)) {
+                vIsOpen = true;
+                // Clear contents cache
+                value.setLength(0);
+            }
+            // c => cell
+            else if ("c".equals(name)) {
+                // Get the cell reference
+                String r = attributes.getValue("r");
+                int firstDigit = -1;
+                for (int c = 0; c < r.length(); ++c) {
+                    if (Character.isDigit(r.charAt(c))) {
+                        firstDigit = c;
+                        break;
+                    }
+                }
+                thisColumn = nameToColumn(r.substring(0, firstDigit));
+
+                // Set up defaults.
+                this.nextDataType = xssfDataType.NUMBER;
+                this.formatIndex = -1;
+                this.formatString = null;
+                String cellType = attributes.getValue("t");
+                String cellStyleStr = attributes.getValue("s");
+                if ("b".equals(cellType))
+                    nextDataType = xssfDataType.BOOL;
+                else if ("e".equals(cellType))
+                    nextDataType = xssfDataType.ERROR;
+                else if ("inlineStr".equals(cellType))
+                    nextDataType = xssfDataType.INLINESTR;
+                else if ("s".equals(cellType))
+                    nextDataType = xssfDataType.SSTINDEX;
+                else if ("str".equals(cellType))
+                    nextDataType = xssfDataType.FORMULA;
+                else if (cellStyleStr != null) {
+                    // It's a number, but almost certainly one
+                    // with a special style or format
+                    int styleIndex = Integer.parseInt(cellStyleStr);
+                    XSSFCellStyle style = stylesTable.getStyleAt(styleIndex);
+                    this.formatIndex = style.getDataFormat();
+                    this.formatString = style.getDataFormatString();
+                    if (this.formatString == null)
+                        this.formatString = BuiltinFormats
+                                .getBuiltinFormat(this.formatIndex);
+                }
+            }
+
+        }
+
+        /*
+         * (non-Javadoc)
+         *
+         * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String,
+         * java.lang.String, java.lang.String)
+         */
+        public void endElement(String uri, String localName, String name)
+                throws SAXException {
+
+            String thisStr = null;
+
+            // v => contents of a cell
+            if ("v".equals(name)) {
+                // Process the value contents as required.
+                // Do now, as characters() may be called more than once
+                switch (nextDataType) {
+
+                    case BOOL:
+                        char first = value.charAt(0);
+                        thisStr = first == '0' ? "FALSE" : "TRUE";
+                        break;
+
+                    case ERROR:
+                        thisStr = "\"ERROR:" + value.toString() + '"';
+                        break;
+
+                    case FORMULA:
+                        // A formula could result in a string value,
+                        // so always add double-quote characters.
+                        thisStr = value.toString();
+                        break;
+
+                    case INLINESTR:
+                        XSSFRichTextString rtsi = new XSSFRichTextString(
+                                value.toString());
+                        thisStr = rtsi.toString();
+                        break;
+
+                    case SSTINDEX:
+                        String sstIndex = value.toString();
+                        try {
+                            int idx = Integer.parseInt(sstIndex);
+                            XSSFRichTextString rtss = new XSSFRichTextString(
+                                    sharedStringsTable.getEntryAt(idx));
+                            thisStr = rtss.toString();
+                        } catch (NumberFormatException ex) {
+                            output.println("Failed to parse SST index '" + sstIndex
+                                    + "': " + ex.toString());
+                        }
+                        break;
+
+                    case NUMBER:
+                        String n = value.toString();
+                        // 判断是否是日期格式
+                        if (HSSFDateUtil.isADateFormat(this.formatIndex, n)) {
+                            Double d = Double.parseDouble(n);
+                            Date date = HSSFDateUtil.getJavaDate(d);
+                            thisStr = formateDateToString(date);
+                        } else if (this.formatString != null) {
+                            if (this.formatString.equals("reserved-0x1F")) {
+                                this.formatString = "yyyy\"年\"m\"月\"d\"日\";@";
+                            }
+                            thisStr = formatter.formatRawCellContents(
+                                    Double.parseDouble(n), this.formatIndex,
+                                    this.formatString);
+                        } else {
+                            thisStr = n;
+                        }
+                        break;
+
+                    default:
+                        thisStr = "(TODO: Unexpected type: " + nextDataType + ")";
+                        break;
+                }
+
+                // Output after we've seen the string contents
+                // Emit commas for any fields that were missing on this row
+                if (lastColumnNumber == -1) {
+                    lastColumnNumber = 0;
+                }
+                //判断单元格的值是否为空
+                if (thisStr == null || "".equals(isCellNull)) {
+                    isCellNull = true;// 设置单元格是否为空值
+                }
+                record[thisColumn] = thisStr;
+                // Update column
+                if (thisColumn > -1)
+                    lastColumnNumber = thisColumn;
+
+            } else if ("row".equals(name)) {
+
+                // Print out any missing commas if needed
+                if (minColumns > 0) {
+                    // Columns are 0 based
+                    if (lastColumnNumber == -1) {
+                        lastColumnNumber = 0;
+                    }
+                    boolean recordflag = record[0] != null;
+                    for (int i = 1; i < minColumns; i++) {
+                        recordflag = recordflag || (record[i] != null);
+                    }
+                    if (isCellNull == false && recordflag)// 判断是否空行
+                    {
+                        rows.add(record.clone());
+                        isCellNull = false;
+                        for (int i = 0; i < record.length; i++) {
+                            record[i] = null;
+                        }
+                    }
+                }
+                lastColumnNumber = -1;
+            }
+
+        }
+
+        public List<String[]> getRows() {
+            return rows;
+        }
+
+        public void setRows(List<String[]> rows) {
+            this.rows = rows;
+        }
+
+        /**
+         * Captures characters only if a suitable element is open. Originally
+         * was just "v"; extended for inlineStr also.
+         */
+        public void characters(char[] ch, int start, int length)
+                throws SAXException {
+            if (vIsOpen)
+                value.append(ch, start, length);
+        }
+
+        /**
+         * Converts an Excel column name like "C" to a zero-based index.
+         *
+         * @return Index corresponding to the specified name
+         */
+        private int nameToColumn(String name) {
+            int column = -1;
+            for (int i = 0; i < name.length(); ++i) {
+                int c = name.charAt(i);
+                column = (column + 1) * 26 + c - 'A';
+            }
+            return column;
+        }
+
+        private String formateDateToString(Date date) {
+            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//格式化日期
+            return sdf.format(date);
+        }
+
+    }
+
+    // /////////////////////////////////////
+
+    private OPCPackage xlsxPackage;
+    private int minColumns;
+    private PrintStream output;
+    private String sheetName;
+
+    /**
+     * Creates a new XLSX -> CSV converter
+     *
+     * @param pkg        The XLSX package to process
+     * @param output     The PrintStream to output the CSV to
+     * @param minColumns The minimum number of columns to output, or -1 for no minimum
+     */
+    public ExcelImportXLSXUtil(OPCPackage pkg, PrintStream output,
+                               String sheetName, int minColumns) {
+        this.xlsxPackage = pkg;
+        this.output = output;
+        this.minColumns = minColumns;
+        this.sheetName = sheetName;
+    }
+
+    /**
+     * Parses and shows the content of one sheet using the specified styles and
+     * shared-strings tables.
+     */
+    public List<String[]> processSheet(StylesTable styles,
+                                       ReadOnlySharedStringsTable strings, InputStream sheetInputStream)
+            throws IOException, ParserConfigurationException, SAXException {
+
+        InputSource sheetSource = new InputSource(sheetInputStream);
+        SAXParserFactory saxFactory = SAXParserFactory.newInstance();
+        SAXParser saxParser = saxFactory.newSAXParser();
+        XMLReader sheetParser = saxParser.getXMLReader();
+        MyXSSFSheetHandler handler = new MyXSSFSheetHandler(styles, strings,
+                this.minColumns, this.output);
+        sheetParser.setContentHandler(handler);
+        sheetParser.parse(sheetSource);
+        return handler.getRows();
+    }
+
+    /**
+     * 初始化这个处理程序 将
+     */
+    public List<String[]> process() throws IOException, OpenXML4JException,
+            ParserConfigurationException, SAXException {
+
+        ReadOnlySharedStringsTable strings = new ReadOnlySharedStringsTable(this.xlsxPackage);
+        XSSFReader xssfReader = new XSSFReader(this.xlsxPackage);
+        List<String[]> list = null;
+        StylesTable styles = xssfReader.getStylesTable();
+        XSSFReader.SheetIterator iter = (XSSFReader.SheetIterator) xssfReader
+                .getSheetsData();
+        while (iter.hasNext()) {
+            InputStream stream = iter.next();
+            String sheetNameTemp = iter.getSheetName();
+            if (this.sheetName.equals(sheetNameTemp)) {
+                list = processSheet(styles, strings, stream);
+                stream.close();
+            }
+        }
+        return list;
+    }
+
+    /**
+     * 读取Excel
+     *
+     * @param path       文件路径
+     * @param sheetName  sheet名称
+     * @param minColumns 列总数
+     */
+    public static List<String[]> readerExcel(String path, String sheetName, int minColumns) throws IOException, OpenXML4JException,
+            ParserConfigurationException, SAXException {
+        OPCPackage p = OPCPackage.open(path, PackageAccess.READ);
+        ExcelImportXLSXUtil xlsx2csv = new ExcelImportXLSXUtil(p, System.out, sheetName, minColumns);
+        List<String[]> list = xlsx2csv.process();
+        p.close();
+        return list;
+    }
+
+    public static List<String[]> readerExcel(InputStream i, String sheetName, int minColumns) throws IOException, OpenXML4JException,
+            ParserConfigurationException, SAXException {
+        OPCPackage p = OPCPackage.open(i);
+        ExcelImportXLSXUtil xlsx2csv = new ExcelImportXLSXUtil(p, System.out, sheetName, minColumns);
+        List<String[]> list = xlsx2csv.process();
+        p.close();
+        return list;
+    }
+
+
+    public static void main(String[] args) throws IOException, OpenXML4JException, ParserConfigurationException, SAXException {
+//		URL httpurl = new URL("D:\\Bingo\\Desktop\\1.xlsx");
+//        URL httpurl = new URL("D:\\Bingo\\Desktop\\南昌交通学院_考勤报表.xlsx");
+//        System.out.println(httpurl);
+//        List<String[]> list = ExcelImportXLSXUtil.readerExcel(httpurl.openStream(), "Sheet1", 50);
+		List<String[]> list = ExcelImportXLSXUtil.readerExcel("D:\\Bingo\\Desktop\\南昌交通学院_考勤报表.xlsx", "原始记录", 60);
+        System.out.println(list.size());
+
+//		for (String[] strings : list) {
+//			System.out.println(list);
+//			for (String string : strings) {
+//				System.out.println(string + "  |  ");
+//			}
+//			System.out.println("\n");
+//		}
+
+        String[] strings = list.get(4);
+        System.out.println(strings[0]);
+        for (String string : strings) {
+            System.out.println(string + "  |  ");
+        }
+    }
+}

+ 373 - 0
src/main/java/com/chuanghai/attendance/utils/excel/ExcelUtil.java

@@ -0,0 +1,373 @@
+package com.chuanghai.attendance.utils.excel;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFRow;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.springframework.util.StringUtils;
+
+/**
+ * <p>Description: EXCEL导出工具</p>
+ *
+ * @author Jianwen Zhu
+ * @version 1.0
+ * <p>Company:XiPinTech</p>
+ * <p>Copyright:Copyright(c)2015</p>
+ * @date 2016年4月22日
+ */
+public class ExcelUtil extends ExcelBase {
+
+	/**
+	 * 一维集合导出,普通
+	 *
+	 * @param fileName     文件名称
+	 * @param headers      表头数组
+	 * @param columns      列名数组
+	 * @param result       结果集
+	 * @param dateFormat   时间格式
+	 * @param response     回应对象
+	 * @param request      请求对象
+	 * @param totalColumns 需要计算合计的列标
+	 *
+	 * @throws Exception 抛出异常
+	 */
+	public static <T> void exportExcel(String fileName, String[] headers, String[] columns,
+									   Collection<T> result, String dateFormat, HttpServletResponse response,
+									   HttpServletRequest request, List<Integer> totalColumns) throws Exception {
+		if (result == null || result.isEmpty()) {
+			throw new NullPointerException("result is NULL");
+		}
+
+		setRequest(request);
+		initWorkbook();
+		addSheet(fileName, headers);
+
+		// 合计列计算值
+		Map<Integer, Double> totalColumnValues = new HashMap<>();
+		HSSFRow row = null;
+		// 遍历集合数据,产生数据行
+		for (T resultItem : result) {
+			row = createRow(fileName, headers);
+			for (int columnIndex = 0; columnIndex < columns.length; columnIndex++) {
+				HSSFCell cell = row.createCell(columnIndex);
+				String fieldName = columns[columnIndex];
+
+				Object valueObject = getValueObject(resultItem, fieldName);
+				String valueString = getValueString(valueObject, dateFormat, rowIndex, columnIndex);
+
+				setCellValue(valueString, cell);
+				if (StringUtils.isEmpty(valueString)) {
+					continue;
+				}
+
+				// 计算合计列的值
+				if (totalColumns == null || !totalColumns.contains(columnIndex)) {
+					continue;
+				}
+				// 验证该列是否有非数字值,有则从合计列中移除,空值不作为非数字值。
+				if (!valueString.matches(DOUBLE_MATCHES_STRING)) {
+					totalColumns.remove((Object) columnIndex);
+					continue;
+				}
+				Double valueDouble = Double.valueOf(valueString);
+				if (totalColumnValues.containsKey(columnIndex)) {
+					Double tempDouble = totalColumnValues.get(columnIndex);
+					tempDouble = tempDouble + valueDouble;
+					totalColumnValues.put(columnIndex, tempDouble);
+				} else {
+					totalColumnValues.put(columnIndex, valueDouble);
+				}
+			}
+			rowIndex++;
+		}
+
+		// 设置合计列的值
+		if (CollectionUtils.isNotEmpty(totalColumns)) {
+			row = createRow(fileName, headers);
+
+			HSSFCell cell = row.createCell(0);
+			cell.setCellStyle(getCountCellStyle());
+			cell.setCellValue("合计:");
+			for (Integer totalIndex : totalColumns) {
+				cell = row.createCell(totalIndex);
+				cell.setCellStyle(getCellStyle("doubleCellStyle"));
+				cell.setCellValue(totalColumnValues.get(totalIndex) == null ? 0 : totalColumnValues
+						.get(totalIndex));
+			}
+		}
+
+		outWorkbook(fileName, response);
+
+	}
+
+	/**
+	 * 二维集合导出,合并
+	 *
+	 * @param fileName       文件名
+	 * @param collectionName 一维集合中二维集合的列名
+	 * @param headers        工作表表头名称
+	 * @param firstColumns   一维集合列名,二维集合中的列名使用""替代,二维集合第一列列名使用collectionName参数值替代
+	 * @param secondColumns  二维集合列名
+	 * @param result         一维集合结果集,包含二维集合结果集
+	 * @param dateFormat     时间格式
+	 * @param response       请求对象
+	 * @param request        回复对象
+	 *
+	 * @throws Exception 抛出异常
+	 */
+	public static <T, V> void exportExcelForVerticalMerger(String fileName, String collectionName,
+														   String[] headers, String[] firstColumns, String[] secondColumns, Collection<T> result,
+														   String dateFormat, HttpServletResponse response, HttpServletRequest request)
+			throws Exception {
+		if (result == null || result.isEmpty()) {
+			throw new NullPointerException("result is NULL");
+		}
+
+		setRequest(request);
+		initWorkbook();
+		addSheet(fileName, headers);
+
+		// 遍历一维集合
+		HSSFRow row = null;
+		for (T firstResultItem : result) {
+			row = createRow(fileName, headers);
+			// 计算合并域
+			int rowBegin = rowIndex;
+			@SuppressWarnings("unchecked")
+			int rowEnd = rowIndex
+					+ ((Collection<V>) getValueObject(firstResultItem, collectionName)).size() - 1;
+
+			for (int columnIndex = 0; columnIndex < firstColumns.length; columnIndex++) {
+				String fieldName = firstColumns[columnIndex];
+				if (fieldName.equals(collectionName)) {// 是二维集合元素
+					@SuppressWarnings("unchecked")
+					Collection<V> secondResult = (Collection<V>) getValueObject(firstResultItem,
+							collectionName);
+					Iterator<V> it = secondResult.iterator();
+					// 遍历二维集合
+					while (it.hasNext()) {
+						V secondResultItem = it.next();
+						for (int j = 0; j < secondColumns.length; j++) {
+							HSSFCell secondCell = row.createCell(columnIndex + j);
+							fieldName = secondColumns[j];
+
+							Object valueObject = getValueObject(secondResultItem, fieldName);
+							String valueString = getValueString(valueObject, dateFormat, rowIndex,
+									columnIndex + j);
+
+							setCellValue(valueString, secondCell);
+						}
+						// 如果是二维集合非最后一行,则由二维集合移动行标,否则由外层移动行标。
+						if (it.hasNext()) {
+							rowIndex++;
+							row = sheet.createRow(rowIndex);
+						}
+					}
+
+					// 移动列标至二维集合列后一列
+					columnIndex = columnIndex + secondColumns.length - 1;
+				} else {// 普通元素
+					sheet.addMergedRegion(new CellRangeAddress(rowBegin, rowEnd, columnIndex,
+							columnIndex));
+					HSSFCell firstCell = sheet.getRow(rowBegin).createCell(columnIndex);
+
+					Object valueObject = getValueObject(firstResultItem, fieldName);
+					String valueString = getValueString(valueObject, dateFormat, rowIndex,
+							columnIndex);
+
+					setCellValue(valueString, firstCell);
+				}
+			}
+			rowIndex++;
+		}
+
+		outWorkbook(fileName, response);
+	}
+
+	/**
+	 * 二维集合导出,动态列
+	 *
+	 * @param fileName       文件名
+	 * @param headers        一维集合表头
+	 * @param headerIndex    动态列开始的列索引
+	 * @param columnKey      动态列表头对应的属性名
+	 * @param valueKey       动态列值对应的属性名
+	 * @param columns        一维集合列名
+	 * @param collectionName 一维集合中二维集合的列名
+	 * @param result         一维集合结果集,包含二维集合结果集
+	 * @param dateFormat     时间格式
+	 * @param response       回复对象
+	 * @param request        请求对象
+	 *
+	 * @throws Exception 抛出异常
+	 */
+	public static <T, V> void exportExcelDynamicColumn(String fileName, List<String> headers,
+													   int headerIndex, String columnKey, String valueKey, List<String> columns,
+													   String collectionName, Collection<T> result, String dateFormat,
+													   HttpServletResponse response, HttpServletRequest request) throws Exception {
+		if (result == null || result.isEmpty()) {
+			throw new NullPointerException("result is NULL");
+		}
+		// 构造动态表头
+		for (T firstResultItem : result) {
+			@SuppressWarnings("unchecked")
+			Collection<V> secondResult = (Collection<V>) getValueObject(firstResultItem,
+					collectionName);
+			for (V secondResultItem : secondResult) {
+				String headerTemp = getValueObject(secondResultItem, columnKey).toString();
+				// 如果没有该列,则添加该列
+				if (!headers.contains(headerTemp)) {
+					// 添加表头列名
+					headers.add(headerIndex, headerTemp);
+					// 添加列标索引
+					columns.add(headerIndex, headerTemp);
+				}
+			}
+		}
+
+		setRequest(request);
+		initWorkbook();
+		addSheet(fileName, headers.toArray());
+
+		HSSFRow row = null;
+		for (T firstResultItem : result) {
+			boolean isWrite = false;
+			row = createRow(fileName, headers.toArray());
+			for (int columnIndex = 0; columnIndex < columns.size(); columnIndex++) {
+				String fieldName = columns.get(columnIndex);
+				// 如果一维集合中有该元素则从一维集合取值,否则从二维集合取值(动态列)
+				if (hasField(firstResultItem, fieldName)) {
+					HSSFCell cell = row.createCell(columnIndex);
+					Object valueObject = getValueObject(firstResultItem, fieldName);
+
+					String valueString = getValueString(valueObject, dateFormat, rowIndex,
+							columnIndex);
+
+					setCellValue(valueString, cell);
+				} else if (!isWrite) {
+					// 防止同一一维集合下的二维集合被重复设值
+					isWrite = true;
+					@SuppressWarnings("unchecked")
+					Collection<V> secondResult = (Collection<V>) getValueObject(firstResultItem,
+							collectionName);
+					Iterator<V> it = secondResult.iterator();
+					// 动态列设值
+					while (it.hasNext()) {
+						V secondResultItem = it.next();
+						// 获取到对应的列标
+						String headerTemp = getValueObject(secondResultItem, columnKey).toString();
+						columnIndex = headers.indexOf(headerTemp);
+
+						HSSFCell cell = row.createCell(columnIndex);
+						Object valueObject = getValueObject(secondResultItem, valueKey);
+						String valueString = getValueString(valueObject, dateFormat, rowIndex,
+								columnIndex);
+						setCellValue(valueString, cell);
+					}
+				}
+			}
+			rowIndex++;
+		}
+
+		outWorkbook(fileName, response);
+	}
+
+	/**
+	 * 供应商出货汇总表导出(定制)
+	 *
+	 * @param fileName 文件名
+	 * @param result   数据
+	 * @param response 回应对象
+	 * @param request  请求对象
+	 *
+	 * @throws Exception 异常
+	 */
+	public static void exportExcelSupplierShipment(String fileName,
+												   List<Map<String, String>> result, HttpServletResponse response,
+												   HttpServletRequest request) throws Exception {
+		if (result == null || result.isEmpty()) {
+			throw new NullPointerException("result is NULL");
+		}
+
+		// 标题
+		int headerWidth = result.get(0).size();
+		// 表头
+		Map<String, String> headerData = result.get(0);
+		List<String> headers = new LinkedList<String>();
+		for (int i = 0; i < headerWidth; i++) {
+			String key = "c" + String.format("%02d", i + 1);
+			headers.add(headerData.get(key));
+		}
+
+		setRequest(request);
+		initWorkbook();
+		addSheet(fileName, headers.toArray());
+		// 数据
+		for (int i = 1; i < result.size(); i++) {
+			int columnIndex = 0;
+			Map<String, String> rowData = result.get(i);
+			HSSFRow row = createRow(fileName, headers.toArray());
+			for (; columnIndex < headerWidth; columnIndex++) {
+				String key = "c" + String.format("%02d", columnIndex + 1);
+				HSSFCell cell = row.createCell(columnIndex);
+				setCellValue(rowData.get(key), cell);
+			}
+			rowIndex++;
+		}
+
+		outWorkbook(fileName, response);
+	}
+
+
+	/**
+	 * 后台经营户基础资料表(定制)
+	 */
+	public static <T> void exportExcelMember(String fileName, String[] headers, String[] columns,
+											 Collection<T> result, String dateFormat, HttpServletResponse response,
+											 HttpServletRequest request) throws Exception {
+		if (result == null || result.isEmpty()) {
+			throw new NullPointerException("result is NULL");
+		}
+
+		setRequest(request);
+		initWorkbook();
+		addSheet(fileName, headers);
+
+		HSSFRow row;
+		// 遍历集合数据,产生数据行
+		for (T resultItem : result) {
+			row = createRow(fileName, headers);
+			for (int columnIndex = 0; columnIndex < columns.length; columnIndex++) {
+				HSSFCell cell = row.createCell(columnIndex);
+				String fieldName = columns[columnIndex];
+
+				Object valueObject = getValueObject(resultItem, fieldName);
+				if (Objects.equals(fieldName, "status")) {
+					valueObject = ((Byte) valueObject == 0) ? "正常" : "冻结";
+				}
+				String valueString = getValueString(valueObject, dateFormat, rowIndex, columnIndex);
+
+				if (StringUtils.isEmpty(valueString)) {
+					continue;
+				}
+
+				setCellValue(valueString, cell);
+			}
+			rowIndex++;
+		}
+
+		outWorkbook(fileName, response);
+	}
+
+}

+ 184 - 0
src/main/java/com/chuanghai/attendance/utils/excel/FileTools.java

@@ -0,0 +1,184 @@
+package com.chuanghai.attendance.utils.excel;
+
+
+import org.apache.commons.io.FileUtils;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.context.ContextLoader;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletResponse;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+
+/**
+ * @author zouxiaoliang
+ * @ClassName: FileUtils
+ * @Description: TODO(这里用一句话描述这个类的作用)
+ * @date 2016年8月4日 下午4:08:06
+ */
+public class FileTools {
+	public static void downTemplate(HttpServletResponse response, String filePath, String title) throws Exception {
+		if (title == null || title.isEmpty()) {
+		//	throw new BusinessException("文件名没有定义");
+		}
+		String path = ContextLoader.getCurrentWebApplicationContext().getServletContext().getRealPath("/" + filePath);
+		OutputStream out = null;
+		try {
+			String fileName = new String(title.getBytes("UTF-8"), "iso-8859-1");
+			response.reset();
+			response.setContentType("application/octet-stream; charset=utf-8");
+			response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
+			out = response.getOutputStream();
+			out.write(FileUtils.readFileToByteArray(new File(path)));
+			out.flush();
+		} catch (Exception e) {
+			throw e;
+		} finally {
+			if (out != null) {
+				try {
+					out.close();
+				} catch (Exception e) {
+					throw e;
+				}
+			}
+		}
+	}
+
+	public static void downFile(HttpServletResponse response, String filePath, String title) throws Exception {
+		if (title == null || title.isEmpty()) {
+		//	throw new BusinessException("文件名没有定义");
+		}
+		OutputStream out = null;
+		try {
+			String fileName = new String(title.getBytes("UTF-8"), "iso-8859-1");
+			response.reset();
+			response.setContentType("application/octet-stream; charset=utf-8");
+			response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
+			out = response.getOutputStream();
+			out.write(FileUtils.readFileToByteArray(new File(filePath)));
+			out.flush();
+		} catch (Exception e) {
+			throw e;
+		} finally {
+			if (out != null) {
+				try {
+					out.close();
+				} catch (Exception e) {
+					throw e;
+				}
+			}
+		}
+	}
+
+
+	/**
+	 * 模板导出方法二
+	 *
+	 * @throws Exception 费率上传模板说明.xlsx
+	 */
+	public static ResponseEntity<byte[]> downTemplate(String filePath, String title) throws Exception {
+		if (title == null || title.isEmpty()) {
+		//	throw new BusinessException("文件名没有定义");
+		}
+		String path = ContextLoader.getCurrentWebApplicationContext().getServletContext().getRealPath("/" + filePath);
+		String fileName = new String(title.getBytes("UTF-8"), "iso-8859-1");
+		HttpHeaders headers = new HttpHeaders();
+		headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
+		headers.setContentDispositionFormData("attachment", fileName);
+		return new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(new File(path)), headers, HttpStatus.CREATED);
+	}
+
+	public static String checkFile(MultipartFile file) {
+		String partten = ".*(.xls|.xlsx)$";
+		String filename = file.getOriginalFilename().toLowerCase();
+		Pattern pattern = Pattern.compile(partten);
+		Matcher matcher = pattern.matcher(filename);
+		if (!matcher.matches()) {
+	//		throw new BusinessException("文件类型不匹配");
+		}
+		if (file.getSize() / (1000 * 1000) > 22) {
+	//		throw new BusinessException("文件大小不能超过20M");
+		}
+		return filename.substring(filename.lastIndexOf(".") + 1);
+	}
+
+	public static void writeToTxt(HttpServletResponse response, String fileName, List<String> list) throws Exception {
+		fileName = new String(fileName.getBytes("UTF-8"), "iso-8859-1");
+		response.setContentType("text/plain");
+		response.addHeader("Content-Disposition", "attachment;filename=" + fileName + ".txt");// filename指定默认的名字
+		BufferedOutputStream buff = null;
+		StringBuffer write = new StringBuffer();
+		String enter = "\r\n";
+		ServletOutputStream outSTr = null;
+		try {
+			outSTr = response.getOutputStream();
+			buff = new BufferedOutputStream(outSTr);
+			for (String str : list) {
+				write.append(str).append(enter);
+			}
+			buff.write(write.toString().getBytes("UTF-8"));
+			buff.flush();
+			buff.close();
+		} catch (IOException e) {
+			e.printStackTrace();
+		} finally {
+			try {
+				if (buff != null) {
+					buff.close();
+				}
+				if (outSTr != null) {
+					outSTr.close();
+				}
+			} catch (Exception e) {
+				e.printStackTrace();
+			}
+		}
+
+	}
+
+
+	public static void writeToTxtANSI(HttpServletResponse response, String fileName, List<String> list) throws Exception {
+		fileName = new String(fileName.getBytes("UTF-8"), "iso-8859-1");
+		response.setContentType("text/plain");
+		response.addHeader("Content-Disposition", "attachment;filename=" + fileName + ".txt");// filename指定默认的名字
+		BufferedOutputStream buff = null;
+		StringBuffer write = new StringBuffer();
+		String enter = "\r\n";
+		ServletOutputStream outSTr = null;
+		try {
+			outSTr = response.getOutputStream();
+			buff = new BufferedOutputStream(outSTr);
+			for (String str : list) {
+				write.append(str).append(enter);
+			}
+			buff.write(write.toString().getBytes("GBK"));
+			buff.flush();
+			buff.close();
+		} catch (IOException e) {
+			e.printStackTrace();
+		} finally {
+			try {
+				if (buff != null) {
+					buff.close();
+				}
+				if (outSTr != null) {
+					outSTr.close();
+				}
+			} catch (Exception e) {
+				e.printStackTrace();
+			}
+		}
+
+	}
+
+}

+ 121 - 0
src/main/java/com/chuanghai/attendance/utils/excel/ImportExcelUtil.java

@@ -0,0 +1,121 @@
+package com.chuanghai.attendance.utils.excel;
+
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.DecimalFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class ImportExcelUtil {
+
+    private final static String excel2003L =".xls";    //2003- 版本的excel
+    private final static String excel2007U =".xlsx";   //2007+ 版本的excel
+
+    /**
+     * 描述:获取IO流中的数据,组装成List<List<Object>>对象
+     * @param in,fileName
+     * @return
+     * @throws IOException
+     */
+    public List<List<Object>> getBankListByExcel(InputStream in, String fileName) throws Exception {
+        List<List<Object>> list = null;
+
+        //创建Excel工作薄
+        Workbook work = this.getWorkbook(in,fileName);
+        if(null == work){
+            throw new Exception("创建Excel工作薄为空!");
+        }
+        Sheet sheet = null;
+        Row row = null;
+        Cell cell = null;
+
+        list = new ArrayList<List<Object>>();
+        //遍历Excel中所有的sheet
+        for (int i = 0; i < work.getNumberOfSheets(); i++) {
+            sheet = work.getSheetAt(i);
+            if(sheet==null){continue;}
+
+            //遍历当前sheet中的所有行
+            for (int j = sheet.getFirstRowNum(); j < sheet.getLastRowNum() + 1; j++) {
+                row = sheet.getRow(j);
+                if(row==null||row.getFirstCellNum()==j){continue;}
+
+                //遍历所有的列
+                List<Object> li = new ArrayList<Object>();
+                for (int y = row.getFirstCellNum(); y < row.getLastCellNum(); y++) {
+                    cell = row.getCell(y);
+                    li.add(this.getCellValue(cell));
+                }
+                list.add(li);
+            }
+        }
+       // ((InputStream) work).close();
+        return list;
+    }
+
+    /**
+     * 描述:根据文件后缀,自适应上传文件的版本
+     * @param inStr,fileName
+     * @return
+     * @throws Exception
+     */
+    public  Workbook getWorkbook(InputStream inStr, String fileName) throws Exception {
+        Workbook wb = null;
+        String fileType = fileName.substring(fileName.lastIndexOf("."));
+        if(excel2003L.equals(fileType)){
+            wb = new HSSFWorkbook(inStr);  //2003-
+        }else if(excel2007U.equals(fileType)){
+            wb = new XSSFWorkbook(inStr);  //2007+
+        }else{
+            throw new Exception("解析的文件格式有误!");
+        }
+        return wb;
+    }
+
+    /**
+     * 描述:对表格中数值进行格式化
+     * @param cell
+     * @return
+     */
+    public Object getCellValue(Cell cell){
+        Object value = null;
+        DecimalFormat df = new DecimalFormat("0");  //格式化number String字符
+        SimpleDateFormat sdf = new SimpleDateFormat("yyy-MM-dd");  //日期格式化
+        DecimalFormat df2 = new DecimalFormat("0.00");  //格式化数字
+
+        switch (cell.getCellType()) {
+        case Cell.CELL_TYPE_STRING:
+            value = cell.getRichStringCellValue().getString();
+            break;
+        case Cell.CELL_TYPE_NUMERIC:
+            if("General".equals(cell.getCellStyle().getDataFormatString())){
+                value = df.format(cell.getNumericCellValue());
+            }else if("m/d/yy".equals(cell.getCellStyle().getDataFormatString())){
+                value = sdf.format(cell.getDateCellValue());
+            }else{
+                value = df2.format(cell.getNumericCellValue());
+            }
+            break;
+        case Cell.CELL_TYPE_BOOLEAN:
+            value = cell.getBooleanCellValue();
+            break;
+        case Cell.CELL_TYPE_BLANK:
+            value = "";
+            break;
+        default:
+            break;
+        }
+        return value;
+    }
+
+
+}

+ 118 - 0
src/main/java/com/chuanghai/attendance/utils/excel/ImportResult.java

@@ -0,0 +1,118 @@
+package com.chuanghai.attendance.utils.excel;
+
+import java.util.List;
+
+public class ImportResult {
+
+	private Integer successNum;
+
+	private String successInfo;
+
+	private Integer errorNum;
+
+	private String errorInfo;
+
+	private Integer otherNum;
+
+	private String otherInfo;
+
+	private String updateInfo;
+
+	private List<CellResult> updateList;
+
+	private List<CellResult> successList;
+
+	private List<CellResult> errorList;
+
+	private List<CellResult> otherList;
+
+	public Integer getSuccessNum() {
+		return successNum;
+	}
+
+	public void setSuccessNum(Integer successNum) {
+		this.successNum = successNum;
+	}
+
+	public String getSuccessInfo() {
+		return successInfo;
+	}
+
+	public void setSuccessInfo(String successInfo) {
+		this.successInfo = successInfo;
+	}
+
+	public Integer getErrorNum() {
+		return errorNum;
+	}
+
+	public void setErrorNum(Integer errorNum) {
+		this.errorNum = errorNum;
+	}
+
+	public String getErrorInfo() {
+		return errorInfo;
+	}
+
+	public void setErrorInfo(String errorInfo) {
+		this.errorInfo = errorInfo;
+	}
+
+	public Integer getOtherNum() {
+		return otherNum;
+	}
+
+	public void setOtherNum(Integer otherNum) {
+		this.otherNum = otherNum;
+	}
+
+	public String getOtherInfo() {
+		return otherInfo;
+	}
+
+	public void setOtherInfo(String otherInfo) {
+		this.otherInfo = otherInfo;
+	}
+
+	public List<CellResult> getSuccessList() {
+		return successList;
+	}
+
+	public void setSuccessList(List<CellResult> successList) {
+		this.successList = successList;
+	}
+
+	public List<CellResult> getErrorList() {
+		return errorList;
+	}
+
+	public void setErrorList(List<CellResult> errorList) {
+		this.errorList = errorList;
+	}
+
+	public List<CellResult> getOtherList() {
+		return otherList;
+	}
+
+	public void setOtherList(List<CellResult> otherList) {
+		this.otherList = otherList;
+	}
+
+	public String getUpdateInfo() {
+		return updateInfo;
+	}
+
+	public void setUpdateInfo(String updateInfo) {
+		this.updateInfo = updateInfo;
+	}
+
+	public List<CellResult> getUpdateList() {
+		return updateList;
+	}
+
+	public void setUpdateList(List<CellResult> updateList) {
+		this.updateList = updateList;
+	}
+
+}
+

+ 198 - 0
src/main/java/com/chuanghai/attendance/utils/excel/XwpfTUtil.java

@@ -0,0 +1,198 @@
+package com.chuanghai.attendance.utils.excel;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URLEncoder;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.poi.xwpf.usermodel.XWPFDocument;
+import org.apache.poi.xwpf.usermodel.XWPFParagraph;
+import org.apache.poi.xwpf.usermodel.XWPFRun;
+import org.apache.poi.xwpf.usermodel.XWPFTable;
+import org.apache.poi.xwpf.usermodel.XWPFTableCell;
+import org.apache.poi.xwpf.usermodel.XWPFTableRow;
+
+/**
+ * @Author Alan
+ * @Data 2017/4/12.
+ */
+public class XwpfTUtil {
+
+
+	private XwpfTUtil() {
+	}
+
+	;
+
+	/**
+	 * 替换段落里面的变量
+	 *
+	 * @param doc    要替换的文档
+	 * @param params 参数
+	 */
+	public static void replaceInPara(XWPFDocument doc, Map<String, Object> params, int fontSize, String fontFamily) {
+		Iterator<XWPFParagraph> iterator = doc.getParagraphsIterator();
+		XWPFParagraph para;
+		while (iterator.hasNext()) {
+			para = iterator.next();
+			replaceInPara(para, params, fontSize, fontFamily);
+		}
+	}
+
+	/**
+	 * 替换段落里面的变量
+	 *
+	 * @param para   要替换的段落
+	 * @param params 参数
+	 */
+	public static void replaceInPara(XWPFParagraph para, Map<String, Object> params, int fontSize, String fontFamily) {
+		List<XWPFRun> runs;
+		Matcher matcher;
+		if (matcher(para.getParagraphText()).find()) {
+			runs = para.getRuns();
+
+			int start = -1;
+			int end = -1;
+			String str = "";
+			for (int i = 0; i < runs.size(); i++) {
+				XWPFRun run = runs.get(i);
+				String runText = run.toString();
+				System.out.println("------>>>>>>>>>" + runText);
+				if ('$' == runText.charAt(0) && '{' == runText.charAt(1)) {
+					start = i;
+				}
+				if ((start != -1)) {
+					str += runText;
+				}
+				if ('}' == runText.charAt(runText.length() - 1)) {
+					if (start != -1) {
+						end = i;
+						break;
+					}
+				}
+			}
+			System.out.println("start--->" + start);
+			System.out.println("end--->" + end);
+
+			System.out.println("str---->>>" + str);
+
+			for (int i = start; i <= end; i++) {
+				para.removeRun(i);
+				i--;
+				end--;
+				System.out.println("remove i=" + i);
+			}
+			XWPFRun r1 = para.createRun();
+			r1.setFontSize(fontSize);
+			r1.setFontFamily(fontFamily);
+			for (String key : params.keySet()) {
+				if (str.equals(key)) {
+					r1.setText((String) params.get(key));
+					break;
+				}
+			}
+
+
+		}
+	}
+
+	/**
+	 * 替换表格里面的变量
+	 *
+	 * @param doc    要替换的文档
+	 * @param params 参数
+	 */
+	public static void replaceInTable(XWPFDocument doc, Map<String, Object> params) {
+		Iterator<XWPFTable> iterator = doc.getTablesIterator();
+		XWPFTable table;
+		List<XWPFTableRow> rows;
+		List<XWPFTableCell> cells;
+		List<XWPFParagraph> paras;
+		while (iterator.hasNext()) {
+			table = iterator.next();
+			rows = table.getRows();
+			for (XWPFTableRow row : rows) {
+				cells = row.getTableCells();
+				for (XWPFTableCell cell : cells) {
+					paras = cell.getParagraphs();
+					for (XWPFParagraph para : paras) {
+						//replaceInPara(para, params);
+					}
+				}
+			}
+		}
+	}
+
+	/**
+	 * 正则匹配字符串
+	 */
+	private static Matcher matcher(String str) {
+		Pattern pattern = Pattern.compile("\\$\\{(.+?)\\}", Pattern.CASE_INSENSITIVE);
+		Matcher matcher = pattern.matcher(str);
+		return matcher;
+	}
+
+	/**
+	 * 关闭输入流
+	 */
+	public static void close(InputStream is) {
+		if (is != null) {
+			try {
+				is.close();
+			} catch (IOException e) {
+				e.printStackTrace();
+			}
+		}
+	}
+
+	/**
+	 * 关闭输出流
+	 */
+	public static void close(OutputStream os) {
+		if (os != null) {
+			try {
+				os.close();
+			} catch (IOException e) {
+				e.printStackTrace();
+			}
+		}
+	}
+
+	public static void exportWord(HttpServletRequest request, HttpServletResponse response, Map<String, Object> params, String fileName, String exportName, int fontSize, String fontFamily) throws Exception {
+
+		XWPFDocument doc;
+		//String fileNameInResource = "sta.docx";
+		String realPath = request.getServletContext().getRealPath("/WEB-INF/word/");
+
+		InputStream is;
+		is = new FileInputStream(realPath + fileName);
+		//is = getClass().getClassLoader().getResourceAsStream(fileName);
+		doc = new XWPFDocument(is);
+
+
+		XwpfTUtil.replaceInPara(doc, params, fontSize, fontFamily);
+		//替换表格里面的变量
+		//XwpfTUtil.replaceInTable(doc, params);
+		OutputStream os = response.getOutputStream();
+
+		response.setContentType("application/msword;charset=UTF-8");
+		response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(exportName, "utf-8") + ".docx");
+
+		doc.write(os);
+
+		XwpfTUtil.close(os);
+		XwpfTUtil.close(is);
+
+		os.flush();
+		os.close();
+	}
+}

+ 55 - 0
src/main/java/com/chuanghai/attendance/vo/CampusTimeLateClockVO.java

@@ -0,0 +1,55 @@
+package com.chuanghai.attendance.vo;
+
+import lombok.Data;
+import lombok.ToString;
+
+/**
+ * @Author: binguo
+ * @Date: 2022/10/12 星期三 9:24
+ * @Description: com.chuanghai.attendance.vo
+ * @Version: 1.0
+ */
+@Data
+@ToString
+public class CampusTimeLateClockVO {
+
+    /**
+     * 编号
+     */
+    private Integer id;
+    /**
+     * 校区
+     */
+    private String campus;
+    /**
+     * 考勤打卡时间段起始时间
+     */
+    private String startTime;
+    /**
+     * 考勤打卡时间段结束时间
+     */
+    private String endTime;
+    /**
+     * 考勤打卡时间段内打卡次数
+     */
+    private Integer count;
+    /**
+     * 考勤打卡时间段内打卡名称
+     */
+    private String clockName;
+
+    /**
+     * 编号
+     */
+    private Integer lateId;
+
+    /**
+     * 上班晚到时间分钟
+     */
+    private String lateOfWork;
+    /**
+     * 下班早走时间分钟
+     */
+    private String leaveEarlyOfWork ;
+
+}

+ 29 - 0
src/main/java/com/chuanghai/attendance/vo/ClockTimeVo.java

@@ -0,0 +1,29 @@
+package com.chuanghai.attendance.vo;
+
+import lombok.Data;
+import lombok.ToString;
+
+/**
+ * @Author: binguo
+ * @Date: 2022/10/19 星期三 16:35
+ * @Description: com.chuanghai.attendance.vo
+ * @Version: 1.0
+ */
+@Data
+@ToString
+public class ClockTimeVo {
+    /**
+     * 打卡时间段起始
+     */
+    private String clockTimeStart = "";
+    /**
+     * 打卡时间段结束
+     */
+    private String clockTimeEnd = "";
+    /**
+     * 当前时间段的打卡次数
+     */
+    private Integer count = 0;
+
+    private String startTime;
+}

+ 37 - 0
src/main/resources/application.yml

@@ -0,0 +1,37 @@
+server:
+  port: 8087
+  servlet:
+    context-path: /attendance
+
+# 数据源
+spring:
+  application:
+    name: attendance
+  main:
+    allow-circular-references: true
+  datasource:
+    username: root
+    password: root
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    url: jdbc:mysql://127.0.0.1:3306/attendance?characterEncoding=utf-8&useSSL=FALSE&useAffectedRows=TRUE&allowMultiQueries=true&serverTimezone=GMT%2B8
+
+  #配置文件传输的大小
+  servlet:
+    multipart:
+      enabled: true
+      file-size-threshold: 0
+      max-file-size: 10MB
+      max-request-size: 10MB
+
+mybatis-plus:
+  mapper-locations: classpath:/mapper/attendance/*.xml
+#  global-config:
+#    db-config:
+#      id-type: assign_id
+
+
+
+logging:
+  level:
+    com.chuanghai: error   #debug
+

+ 28 - 0
src/main/resources/smart-doc.json

@@ -0,0 +1,28 @@
+{
+  "serverUrl": "http://192.168.161.34:8087/attendance",
+  "isStrict": false,
+  "allInOne": true,
+  "outPath": "./src/main/resources/static/doc",
+  "coverOld": true,
+  "style": "zenburn",
+  "createDebugPage": false,
+  "projectName": "考勤分析",
+  "inlineEnum": true,
+  "revisionLogs": [
+    {
+      "version": "0.0.1",
+      "revisionTime": "2022-09-20 09:50:00",
+      "status": "创建文档",
+      "author": "binguo",
+      "remarks": "初始化接口文档"
+    }
+  ],
+  "errorCodeDictionaries": [
+    {
+      "title": "业务异常代码",
+      "enumClassName": "com.chuanghai.attendance.common.exception.BizCodeEnume",
+      "codeField": "code",
+      "descField": "msg"
+    }
+  ]
+}

Разница между файлами не показана из-за своего большого размера
+ 7 - 0
src/main/resources/static/doc/AllInOne.css


+ 249 - 0
src/main/resources/static/doc/index.adoc

@@ -0,0 +1,249 @@
+= 考勤分析
+
+[width="100%",options="header"]
+[stripes=even]
+|====================
+|Version |  Update Time  | Status | Author |  Description
+|0.0.1|2022-09-20 09:50:00|创建文档|binguo|初始化接口文档
+|====================
+
+
+== 
+== 
+=== 读取钉钉考勤excel原始数据,添加至数据库
+*URL:* http://192.168.161.34:8087/attendance/month/importMonth
+
+*Type:* POST
+
+
+*Content-Type:* multipart/form-data
+
+
+
+*Query-parameters:*
+
+[width="100%",options="header"]
+[stripes=even]
+|====================
+|Parameter | Type|Description|Required|Since
+|file|file|No comments found.|true|-
+|====================
+
+
+*Request-example:*
+----
+curl -X POST -H 'Content-Type: multipart/form-data' -i http://192.168.161.34:8087/attendance/month/importMonth
+----
+*Response-fields:*
+
+[width="100%",options="header"]
+[stripes=even]
+|====================
+|Field | Type|Description|Since
+|success|boolean|接口调用结果标识|-
+|message|string|接口调用结果信息|-
+|code|string|接口调用业务码|-
+|data|object|接口调用返回数据|-
+|====================
+
+*Response-example:*
+----
+{
+  "success": true,
+  "message": "success",
+  "code": "4224",
+  "data": {
+    "waring": "You may have used non-display generics."
+  }
+}
+----
+
+== 
+=== 读取钉钉考勤excel原始数据,添加至数据库
+*URL:* http://192.168.161.34:8087/attendance/original/importOriginal
+
+*Type:* POST
+
+
+*Content-Type:* multipart/form-data
+
+
+
+*Query-parameters:*
+
+[width="100%",options="header"]
+[stripes=even]
+|====================
+|Parameter | Type|Description|Required|Since
+|file|file|No comments found.|true|-
+|====================
+
+
+*Request-example:*
+----
+curl -X POST -H 'Content-Type: multipart/form-data' -i http://192.168.161.34:8087/attendance/original/importOriginal
+----
+*Response-fields:*
+
+[width="100%",options="header"]
+[stripes=even]
+|====================
+|Field | Type|Description|Since
+|success|boolean|接口调用结果标识|-
+|message|string|接口调用结果信息|-
+|code|string|接口调用业务码|-
+|data|object|接口调用返回数据|-
+|====================
+
+*Response-example:*
+----
+{
+  "success": true,
+  "message": "success",
+  "code": "4224",
+  "data": {
+    "waring": "You may have used non-display generics."
+  }
+}
+----
+
+== 
+=== 
+*URL:* http://192.168.161.34:8087/attendance/test/export
+
+*Type:* GET
+
+
+*Content-Type:* application/x-www-form-urlencoded;charset=utf-8
+
+
+
+
+
+*Request-example:*
+----
+curl -X GET -i http://192.168.161.34:8087/attendance/test/export
+----
+
+*Response-example:*
+----
+Doesn't return a value.
+----
+
+=== 解析上传的.xls文件
+*URL:* http://192.168.161.34:8087/attendance/test/import
+
+*Type:* GET
+
+
+*Content-Type:* multipart/form-data
+
+
+
+*Query-parameters:*
+
+[width="100%",options="header"]
+[stripes=even]
+|====================
+|Parameter | Type|Description|Required|Since
+|file|file|No comments found.|true|-
+|====================
+
+
+*Request-example:*
+----
+curl -X GET -H 'Content-Type: multipart/form-data' -i http://192.168.161.34:8087/attendance/test/import
+----
+
+*Response-example:*
+----
+Doesn't return a value.
+----
+
+== 
+=== 读取钉钉考勤excel原始数据,添加至数据库
+*URL:* http://192.168.161.34:8087/attendance/work/importWork
+
+*Type:* POST
+
+
+*Content-Type:* multipart/form-data
+
+
+
+*Query-parameters:*
+
+[width="100%",options="header"]
+[stripes=even]
+|====================
+|Parameter | Type|Description|Required|Since
+|file|file|No comments found.|true|-
+|====================
+
+
+*Request-example:*
+----
+curl -X POST -H 'Content-Type: multipart/form-data' -i http://192.168.161.34:8087/attendance/work/importWork
+----
+*Response-fields:*
+
+[width="100%",options="header"]
+[stripes=even]
+|====================
+|Field | Type|Description|Since
+|success|boolean|接口调用结果标识|-
+|message|string|接口调用结果信息|-
+|code|string|接口调用业务码|-
+|data|object|接口调用返回数据|-
+|====================
+
+*Response-example:*
+----
+{
+  "success": true,
+  "message": "success",
+  "code": "4224",
+  "data": {
+    "waring": "You may have used non-display generics."
+  }
+}
+----
+
+== 错误码列表
+
+[width="100%",options="header"]
+[stripes=even]
+|====================
+|Error code |Description
+|10000|系统未知异常
+|10001|参数格式校验失败
+|10002|body为空
+|10003|请求方法不支持
+|10004|数据已存在
+|10005|数据不存在
+|10006|无效的token
+|10007|参数异常
+|10008|没有操作权限
+|10009|管理员登录失败
+|10012|缺少必要的请求头
+|10013|缴费信息不存在
+|10014|订单不存在
+|10015|获取微信jsapi支付参数失败
+|10016|建行订单状态查询失败
+|10017|非建行支付订单
+|10018|农商行订单状态查询失败
+|10019|缴费年级不存在
+|10020|文件导入失败
+|10100|批量操作数据库失败
+|10021|付款金额错误
+|10022|缴费项目已存在
+|10025|缴费项目不存在
+|10023|id为空
+|10024|该缴费项目不支持导出TXT
+|10025|该缴费宿舍已缴费
+|10026|内容超长
+|10027|AES加密失败
+|10028|宿舍已缴费
+|10029|该订单的部分项目已支付,请前往首页重新支付
+|====================
+

Разница между файлами не показана из-за своего большого размера
+ 46 - 0
src/main/resources/static/doc/index.html


Разница между файлами не показана из-за своего большого размера
+ 7 - 0
src/main/resources/static/doc/rpc/AllInOne.css


+ 10 - 0
src/main/resources/static/doc/rpc/rpc-index.adoc

@@ -0,0 +1,10 @@
+= 考勤分析
+
+[width="100%",options="header"]
+[stripes=even]
+|====================
+|Version |  Update Time  | Status | Author |  Description
+0.0.1|2022-09-20 09:50:00|创建文档|binguo|初始化接口文档
+|====================
+
+

+ 183 - 0
src/main/resources/static/doc/rpc/rpc-index.html

@@ -0,0 +1,183 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <meta name="generator" content="smart-doc">
+    <title>考勤分析</title>
+    <link rel="stylesheet"
+          href="https://fonts.googleapis.cnpmjs.org/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700">
+    <link rel="stylesheet" href="AllInOne.css?v=1663667322149"/>
+    <script src="https://cdn.bootcss.com/jquery/2.2.4/jquery.min.js"></script>
+</head>
+<body class="book toc2 toc-left">
+<div id="header"><h1>考勤分析</h1>
+    <div id="toc" class="toc2">
+        <div id="book-search-input"><input id="search" type="text" placeholder="Type to search"></div>
+        <div id="toctitle"><span>API Reference</span></div>
+        <ul id="accordion" class="sectlevel1">
+            <li><a href="#_add_dependency">1.&nbsp;Add dependency</a></li>
+            <li><a href="#_error_code_list">1.&nbsp;错误码列表</a></li>
+        </ul>
+    </div>
+</div>
+<div id="content">
+    <div id="preamble">
+        <div class="sectionbody">
+            <table class="tableblock frame-all grid-all spread">
+                <colgroup>
+                    <col style="width: 20%;">
+                    <col style="width: 20%;">
+                    <col style="width: 20%;">
+                    <col style="width: 20%;">
+                    <col style="width: 20%;">
+                </colgroup>
+                <thead>
+                <tr>
+                    <th class="tableblock halign-left valign-top">Version</th>
+                    <th class="tableblock halign-left valign-top">Update Time</th>
+                    <th class="tableblock halign-left valign-top">Status</th>
+                    <th class="tableblock halign-left valign-top">Author</th>
+                    <th class="tableblock halign-left valign-top">Description</th>
+                </tr>
+                </thead>
+                <tbody>
+                <tr>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">0.0.1</p></td>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">2022-09-20 09:50:00</p></td>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">创建文档</p></td>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">binguo</p></td>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">初始化接口文档</p></td>
+                </tr>
+                </tbody>
+            </table>
+        </div>
+    </div>
+    <div class="sect1"><h2 id="_error_code_list"><a class="anchor" href="#_error_code_list"></a><a class="link"
+                                                                                                   href="#_error_code_list">2.&nbsp;错误码列表</a>
+    </h2>
+        <div class="sectionbody">
+            <table class="tableblock frame-all grid-all spread">
+                <colgroup>
+                    <col style="width: 50%;">
+                    <col style="width: 50%;">
+                </colgroup>
+                <thead>
+                <tr>
+                    <th class="tableblock halign-left valign-top">Error code</th>
+                    <th class="tableblock halign-left valign-top">Description</th>
+                </tr>
+                </thead>
+                <tbody>
+                <tr>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">10000</p></td>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">系统未知异常</p></td>
+                </tr>
+                <tr>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">10001</p></td>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">请求方法不支持</p></td>
+                </tr>
+                <tr>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">10002</p></td>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">缺少必要的请求头</p></td>
+                </tr>
+                <tr>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">10003</p></td>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">无效的token</p></td>
+                </tr>
+                <tr>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">10004</p></td>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">token为空</p></td>
+                </tr>
+                <tr>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">10005</p></td>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">管理员登录失败</p></td>
+                </tr>
+                <tr>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">10006</p></td>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">权限不足</p></td>
+                </tr>
+                <tr>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">11001</p></td>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">参数格式校验失败</p></td>
+                </tr>
+                <tr>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">11002</p></td>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">body为空</p></td>
+                </tr>
+                <tr>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">11003</p></td>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">参数异常</p></td>
+                </tr>
+                <tr>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">11004</p></td>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">重复提交表单</p></td>
+                </tr>
+                <tr>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">11005</p></td>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">订单提交失败</p></td>
+                </tr>
+                <tr>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">11006</p></td>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">消费者消费异常</p></td>
+                </tr>
+                <tr>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">11007</p></td>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">第三方服务调用失败</p></td>
+                </tr>
+                <tr>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">11008</p></td>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">农商行订单状态查询失败</p></td>
+                </tr>
+                <tr>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">16000</p></td>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">数据已存在</p></td>
+                </tr>
+                <tr>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">16000</p></td>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">数据不存在</p></td>
+                </tr>
+                <tr>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">16006</p></td>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">临时文件不存在</p></td>
+                </tr>
+                <tr>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">16007</p></td>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">文件上传失败</p></td>
+                </tr>
+                <tr>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">16008</p></td>
+                    <td class="tableblock halign-left valign-top"><p class="tableblock">文件太大</p></td>
+                </tr>
+                </tbody>
+            </table>
+        </div>
+    </div>
+    <footer class="page-footer"><span class="copyright">Generated by smart-doc at 2022-09-20 17:48:42</span><span
+            class="footer-modification">Suggestions,contact,support and error reporting on<a
+            href="https://gitee.com/smart-doc-team/smart-doc" target="_blank">&nbsp;Gitee</a>&nbsp;or<a
+            href="https://github.com/shalousun/smart-doc" target="_blank">&nbsp;Github</a></span></footer>
+</div>
+<script src="search.js?v=1663667322149"></script>
+<script>
+    $(function () {
+        const Accordion = function (el, multiple) {
+            this.el = el || {};
+            this.multiple = multiple || false;
+            const links = this.el.find('.dd');
+            links.on('click', {el: this.el, multiple: this.multiple}, this.dropdown);
+        };
+        Accordion.prototype.dropdown = function (e) {
+            const $el = e.data.el;
+            $this = $(this), $next = $this.next();
+            $next.slideToggle();
+            $this.parent().toggleClass('open');
+            if (!e.data.multiple) {
+                $el.find('.submenu').not($next).slideUp("20").parent().removeClass('open');
+            }
+        };
+        new Accordion($('#accordion'), false);
+    });
+</script>
+</body>
+</html>

+ 105 - 0
src/main/resources/static/doc/rpc/search.js

@@ -0,0 +1,105 @@
+let api = [];
+api.push({
+    alias: 'Add dependency',
+    order: '1',
+    link: 'add_dependency',
+    desc: 'Add dependency',
+    list: []
+})
+api.push({
+    alias: '',
+    order: '2',
+    link: '错误码列表',
+    desc: '错误码列表',
+    list: []
+})
+document.onkeydown = keyDownSearch;
+
+function keyDownSearch(e) {
+    const theEvent = e;
+    const code = theEvent.keyCode || theEvent.which || theEvent.charCode;
+    if (code == 13) {
+        const search = document.getElementById('search');
+        const searchValue = search.value;
+        let searchArr = [];
+        for (let i = 0; i < api.length; i++) {
+            let apiData = api[i];
+            const desc = apiData.desc;
+            if (desc.indexOf(searchValue) > -1) {
+                searchArr.push({
+                    order: apiData.order,
+                    desc: apiData.desc,
+                    link: apiData.link,
+                    list: apiData.list
+                });
+            } else {
+                let methodList = apiData.list || [];
+                let methodListTemp = [];
+                for (let j = 0; j < methodList.length; j++) {
+                    const methodData = methodList[j];
+                    const methodDesc = methodData.desc;
+                    if (methodDesc.indexOf(searchValue) > -1) {
+                        methodListTemp.push(methodData);
+                        break;
+                    }
+                }
+                if (methodListTemp.length > 0) {
+                    const data = {
+                        order: apiData.order,
+                        desc: apiData.desc,
+                        link: apiData.link,
+                        list: methodListTemp
+                    };
+                    searchArr.push(data);
+                }
+            }
+        }
+        let html;
+        if (searchValue == '') {
+            const liClass = "";
+            const display = "display: none";
+            html = buildAccordion(api, liClass, display);
+            document.getElementById('accordion').innerHTML = html;
+        } else {
+            const liClass = "open";
+            const display = "display: block";
+            html = buildAccordion(searchArr, liClass, display);
+            document.getElementById('accordion').innerHTML = html;
+        }
+        const Accordion = function (el, multiple) {
+            this.el = el || {};
+            this.multiple = multiple || false;
+            const links = this.el.find('.dd');
+            links.on('click', {el: this.el, multiple: this.multiple}, this.dropdown);
+        };
+        Accordion.prototype.dropdown = function (e) {
+            const $el = e.data.el;
+            $this = $(this), $next = $this.next();
+            $next.slideToggle();
+            $this.parent().toggleClass('open');
+            if (!e.data.multiple) {
+                $el.find('.submenu').not($next).slideUp("20").parent().removeClass('open');
+            }
+        };
+        new Accordion($('#accordion'), false);
+    }
+}
+
+function buildAccordion(apiData, liClass, display) {
+    let html = "";
+    let doc;
+    if (apiData.length > 0) {
+        for (let j = 0; j < apiData.length; j++) {
+            html += '<li class="' + liClass + '">';
+            html += '<a class="dd" href="#_' + apiData[j].link + '">' + apiData[j].order + '.&nbsp;' + apiData[j].desc + '</a>';
+            html += '<ul class="sectlevel2" style="' + display + '">';
+            doc = apiData[j].list;
+            for (let m = 0; m < doc.length; m++) {
+                html += '<li><a href="#_' + apiData[j].order + '_' + doc[m].order + '_' + doc[m].desc + '">' + apiData[j].order + '.' + doc[m].order + '.&nbsp;' + doc[m].desc + '</a> </li>';
+            }
+            html += '</ul>';
+            html += '</li>';
+        }
+    }
+    return html;
+}

+ 144 - 0
src/main/resources/static/doc/search.js

@@ -0,0 +1,144 @@
+let api = [];
+api.push({
+    alias: 'CampusTimeController',
+    order: '1',
+    link: '校区打卡设置',
+    desc: '校区打卡设置',
+    list: []
+})
+api.push({
+    alias: 'MonthlySummaryController',
+    order: '2',
+    link: '',
+    desc: '',
+    list: []
+})
+api[1].list.push({
+    order: '1',
+    desc: '读取钉钉考勤excel原始数据,添加至数据库',
+});
+api.push({
+    alias: 'OriginalDataController',
+    order: '3',
+    link: '',
+    desc: '',
+    list: []
+})
+api[2].list.push({
+    order: '1',
+    desc: '读取钉钉考勤excel原始数据,添加至数据库',
+});
+api.push({
+    alias: 'WorkerIdentityController',
+    order: '4',
+    link: '',
+    desc: '',
+    list: []
+})
+api[3].list.push({
+    order: '1',
+    desc: '读取钉钉考勤excel原始数据,添加至数据库',
+});
+api.push({
+    alias: 'error',
+    order: '5',
+    link: 'error_code_list',
+    desc: '错误码列表',
+    list: []
+})
+api.push({
+    alias: 'dict',
+    order: '6',
+    link: 'dict_list',
+    desc: '数据字典',
+    list: []
+})
+document.onkeydown = keyDownSearch;
+function keyDownSearch(e) {
+    const theEvent = e;
+    const code = theEvent.keyCode || theEvent.which || theEvent.charCode;
+    if (code == 13) {
+        const search = document.getElementById('search');
+        const searchValue = search.value;
+        let searchArr = [];
+        for (let i = 0; i < api.length; i++) {
+            let apiData = api[i];
+            const desc = apiData.desc;
+            if (desc.indexOf(searchValue) > -1) {
+                searchArr.push({
+                    order: apiData.order,
+                    desc: apiData.desc,
+                    link: apiData.link,
+                    list: apiData.list
+                });
+            } else {
+                let methodList = apiData.list || [];
+                let methodListTemp = [];
+                for (let j = 0; j < methodList.length; j++) {
+                    const methodData = methodList[j];
+                    const methodDesc = methodData.desc;
+                    if (methodDesc.indexOf(searchValue) > -1) {
+                        methodListTemp.push(methodData);
+                        break;
+                    }
+                }
+                if (methodListTemp.length > 0) {
+                    const data = {
+                        order: apiData.order,
+                        desc: apiData.desc,
+                        link: apiData.link,
+                        list: methodListTemp
+                    };
+                    searchArr.push(data);
+                }
+            }
+        }
+        let html;
+        if (searchValue == '') {
+            const liClass = "";
+            const display = "display: none";
+            html = buildAccordion(api,liClass,display);
+            document.getElementById('accordion').innerHTML = html;
+        } else {
+            const liClass = "open";
+            const display = "display: block";
+            html = buildAccordion(searchArr,liClass,display);
+            document.getElementById('accordion').innerHTML = html;
+        }
+        const Accordion = function (el, multiple) {
+            this.el = el || {};
+            this.multiple = multiple || false;
+            const links = this.el.find('.dd');
+            links.on('click', {el: this.el, multiple: this.multiple}, this.dropdown);
+        };
+        Accordion.prototype.dropdown = function (e) {
+            const $el = e.data.el;
+            $this = $(this), $next = $this.next();
+            $next.slideToggle();
+            $this.parent().toggleClass('open');
+            if (!e.data.multiple) {
+                $el.find('.submenu').not($next).slideUp("20").parent().removeClass('open');
+            }
+        };
+        new Accordion($('#accordion'), false);
+    }
+}
+
+function buildAccordion(apiData, liClass, display) {
+    let html = "";
+    let doc;
+    if (apiData.length > 0) {
+        for (let j = 0; j < apiData.length; j++) {
+            html += '<li class="'+liClass+'">';
+            html += '<a class="dd" href="#_' + apiData[j].link + '">' + apiData[j].order + '.&nbsp;' + apiData[j].desc + '</a>';
+            html += '<ul class="sectlevel2" style="'+display+'">';
+            doc = apiData[j].list;
+            for (let m = 0; m < doc.length; m++) {
+                html += '<li><a href="#_' + apiData[j].order + '_' + doc[m].order + '_' + doc[m].desc + '">' + apiData[j].order + '.' + doc[m].order + '.&nbsp;' + doc[m].desc + '</a> </li>';
+            }
+            html += '</ul>';
+            html += '</li>';
+        }
+    }
+    return html;
+}

+ 15 - 0
src/test/java/com/chuanghai/attendance/AttendanceApplicationTests.java

@@ -0,0 +1,15 @@
+package com.chuanghai.attendance;
+
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class AttendanceApplicationTests {
+
+    @Test
+    void contextLoads() {
+        System.out.println("qqq");
+    }
+
+}