浏览代码

针对明夷私有化 考勤同步的相关开发

Min 1 年之前
父节点
当前提交
dd4cde5fba
共有 49 个文件被更改,包括 5391 次插入3 次删除
  1. 二进制
      fhKeeper/formulahousekeeper/attendance-data/opencv/opencv-420.jar
  2. 二进制
      fhKeeper/formulahousekeeper/attendance-data/opencv/x64/opencv_java420.dll
  3. 二进制
      fhKeeper/formulahousekeeper/attendance-data/opencv/x86/opencv_java420.dll
  4. 228 0
      fhKeeper/formulahousekeeper/attendance-data/pom.xml
  5. 23 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/PlatformStartApplication.java
  6. 33 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/controller/HrmScheduleSignController.java
  7. 56 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/entity/HrmScheduleSign.java
  8. 16 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/mapper/HrmScheduleSignMapper.java
  9. 18 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/service/HrmScheduleSignService.java
  10. 58 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/service/impl/HrmScheduleSignServiceImpl.java
  11. 73 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/CRC16Util.java
  12. 217 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/CodeGenerator.java
  13. 42 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/CodeUtil.java
  14. 203 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/ColorUtil.java
  15. 80 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/DateTimeUtil.java
  16. 344 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/DingCallbackCrypto.java
  17. 38 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/DocumentTypeUtil.java
  18. 586 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/ExcelUtil.java
  19. 72 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/FileCopyToFolderUtil.java
  20. 138 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/FileUtil.java
  21. 28 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/GsonUtils.java
  22. 293 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/HttpKit.java
  23. 69 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/HttpRespMsg.java
  24. 77 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/HttpUtil.java
  25. 292 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/ImageCompare.java
  26. 126 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/ImageReconizeUtil.java
  27. 157 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/ListUtil.java
  28. 133 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/MD5Util.java
  29. 18 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/MathUtil.java
  30. 28 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/MessageUtils.java
  31. 61 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/PageUtil.java
  32. 217 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/ProcessUtil.java
  33. 79 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/Sha1Util.java
  34. 51 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/SmsUtil.java
  35. 124 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/SnowFlake.java
  36. 158 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/SplitDateUtil.java
  37. 145 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/SpringUtils.java
  38. 54 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/Tess4jDemo.java
  39. 42 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/TestSample.java
  40. 24 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/TimeZoneUtil.java
  41. 54 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/UploadFileToFileNameUtil.java
  42. 361 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/UserAgentUtils.java
  43. 7 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/UserNotFoundException.java
  44. 298 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/WorkDayCalculateUtils.java
  45. 56 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/resources/application.yml
  46. 18 0
      fhKeeper/formulahousekeeper/attendance-data/src/main/resources/mapper/HrmScheduleSignMapper.xml
  47. 83 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/UserFvTimeController.java
  48. 55 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/task/TimingTask.java
  49. 58 3
      fhKeeper/formulahousekeeper/timesheet/src/views/workReport/daily.vue

二进制
fhKeeper/formulahousekeeper/attendance-data/opencv/opencv-420.jar


二进制
fhKeeper/formulahousekeeper/attendance-data/opencv/x64/opencv_java420.dll


二进制
fhKeeper/formulahousekeeper/attendance-data/opencv/x86/opencv_java420.dll


+ 228 - 0
fhKeeper/formulahousekeeper/attendance-data/pom.xml

@@ -0,0 +1,228 @@
+<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>formulahousekeeper</artifactId>
+        <groupId>com.hssx.parent</groupId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>org.example</groupId>
+    <artifactId>attendance-data</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-actuator</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.github.pagehelper</groupId>
+            <artifactId>pagehelper-spring-boot-starter</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.microsoft.sqlserver</groupId>
+            <artifactId>mssql-jdbc</artifactId>
+            <version>11.2.1.jre8</version>
+        </dependency>
+
+        <!--easyExcel-->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>easyexcel</artifactId>
+            <version>2.2.4</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi-ooxml</artifactId>
+        </dependency>
+        <!--Map依赖 -->
+        <dependency>
+            <groupId>net.jodah</groupId>
+            <artifactId>expiringmap</artifactId>
+            <version>0.5.10</version>
+        </dependency>
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter</artifactId>
+        </dependency>
+        <!-- mybatis-plus代码生成器依赖 -->
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-generator</artifactId>
+        </dependency>
+
+        <!-- velocity模板引擎 -->
+        <dependency>
+            <groupId>org.apache.velocity</groupId>
+            <artifactId>velocity-engine-core</artifactId>
+        </dependency>
+
+        <!-- freemarker 模板引擎-->
+        <dependency>
+            <groupId>org.freemarker</groupId>
+            <artifactId>freemarker</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+
+        <!-- fastjson -->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+        </dependency>
+
+        <!-- openoffice -->
+        <dependency>
+            <groupId>com.artofsolving</groupId>
+            <artifactId>jodconverter</artifactId>
+            <version>2.2.1</version>
+        </dependency>
+
+
+        <dependency>
+            <groupId>com.baidu.aip</groupId>
+            <artifactId>java-sdk</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+        </dependency>
+        <!--开启aop支持-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-aop</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-logging</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.aspectj</groupId>
+            <artifactId>aspectjweaver</artifactId>
+        </dependency>
+
+
+        <!--     tess4j相关依赖   -->
+        <!-- https://mvnrepository.com/artifact/net.sourceforge.tess4j/tess4j -->
+        <dependency>
+            <groupId>net.sourceforge.tess4j</groupId>
+            <artifactId>tess4j</artifactId>
+        </dependency>
+
+        <!--手动引入opencv 否则无法maven打包-->
+        <dependency>
+            <groupId>org.opencv</groupId>
+            <artifactId>opencv</artifactId>
+            <version>4.2.0</version>
+            <scope>system</scope>
+            <systemPath>${basedir}/opencv/opencv-420.jar</systemPath>
+        </dependency>
+
+        <dependency>
+            <groupId>com.jcraft</groupId>
+            <artifactId>jsch</artifactId>
+            <version>0.1.55</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.aliyun</groupId>
+            <artifactId>dysmsapi20170525</artifactId>
+            <version>2.0.4</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+            <optional>true</optional>
+        </dependency>
+
+        <dependency>
+            <groupId>com.microsoft.sqlserver</groupId>
+            <artifactId>sqljdbc4</artifactId>
+            <version>4.0</version>
+        </dependency>
+
+        <!-- 获取客户端信息 -->
+        <!-- https://mvnrepository.com/artifact/eu.bitwalker/UserAgentUtils -->
+        <dependency>
+            <groupId>eu.bitwalker</groupId>
+            <artifactId>UserAgentUtils</artifactId>
+            <version>1.21</version>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <includeSystemScope>true</includeSystemScope>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <!--配置阿里云仓库-->
+    <repositories>
+        <repository>
+            <id>public</id>
+            <name>aliyun nexus</name>
+            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
+            <releases>
+                <enabled>true</enabled>
+            </releases>
+            <snapshots>
+                <enabled>false</enabled>
+            </snapshots>
+        </repository>
+        <repository>
+            <id>bintray-qcloud-maven-repo</id>
+            <name>qcloud-maven-repo</name>
+            <url>https://dl.bintray.com/qcloud/maven-repo/</url>
+            <layout>default</layout>
+            <releases>
+                <enabled>true</enabled>
+            </releases>
+            <snapshots>
+                <enabled>false</enabled>
+            </snapshots>
+        </repository>
+    </repositories>
+
+    <pluginRepositories>
+        <pluginRepository>
+            <id>public</id>
+            <name>aliyun nexus</name>
+            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
+            <releases>
+                <enabled>true</enabled>
+            </releases>
+            <snapshots>
+                <enabled>false</enabled>
+            </snapshots>
+        </pluginRepository>
+    </pluginRepositories>
+
+
+</project>

+ 23 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/PlatformStartApplication.java

@@ -0,0 +1,23 @@
+package com.attendance;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+/**
+ * Author: 吴涛涛
+ * Date : 2019 - 12 - 31 9:23
+ * Description:<描述>
+ * Version: 1.0
+ */
+@SpringBootApplication
+@MapperScan("com.attendance.mapper")
+@EnableTransactionManagement //开启事务支持
+@EnableAsync
+public class PlatformStartApplication {
+    public static void main(String[] args) {
+        SpringApplication.run(com.attendance.PlatformStartApplication.class, args);
+    }
+}

+ 33 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/controller/HrmScheduleSignController.java

@@ -0,0 +1,33 @@
+package com.attendance.controller;
+
+
+import com.attendance.service.HrmScheduleSignService;
+import com.attendance.util.HttpRespMsg;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+
+/**
+ * <p>
+ *  前端控制器
+ * </p>
+ *
+ * @author Yurk
+ * @since 2023-07-10
+ */
+@RestController
+@RequestMapping("/attendance")
+public class HrmScheduleSignController {
+
+    @Resource
+    private HrmScheduleSignService hrmScheduleSignService;
+
+    @RequestMapping("getAttendanceList")
+    public HttpRespMsg getAttendanceList(@RequestBody String json){
+       return hrmScheduleSignService.getAttendanceList(json);
+    }
+
+}
+

+ 56 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/entity/HrmScheduleSign.java

@@ -0,0 +1,56 @@
+package com.attendance.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import java.time.LocalDate;
+import com.baomidou.mybatisplus.annotation.TableId;
+import java.time.LocalTime;
+import com.baomidou.mybatisplus.annotation.TableField;
+import java.io.Serializable;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+/**
+ * <p>
+ * 
+ * </p>
+ *
+ * @author Yurk
+ * @since 2023-07-10
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class HrmScheduleSign extends Model<HrmScheduleSign> {
+
+    private static final long serialVersionUID=1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @TableField("userId")
+    private String userId;
+
+    @TableField("signDate")
+    private LocalDate signDate;
+
+    @TableField("signTime")
+    private LocalTime signTime;
+
+    @TableField(exist = false)
+    private String clockInTime;
+
+    @TableField(exist = false)
+    private String afterWorkTime;
+
+
+    @Override
+    protected Serializable pkVal() {
+        return this.id;
+    }
+
+}

+ 16 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/mapper/HrmScheduleSignMapper.java

@@ -0,0 +1,16 @@
+package com.attendance.mapper;
+
+import com.attendance.entity.HrmScheduleSign;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * <p>
+ *  Mapper 接口
+ * </p>
+ *
+ * @author Yurk
+ * @since 2023-07-10
+ */
+public interface HrmScheduleSignMapper extends BaseMapper<HrmScheduleSign> {
+
+}

+ 18 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/service/HrmScheduleSignService.java

@@ -0,0 +1,18 @@
+package com.attendance.service;
+
+import com.attendance.entity.HrmScheduleSign;
+import com.attendance.util.HttpRespMsg;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * <p>
+ *  服务类
+ * </p>
+ *
+ * @author Yurk
+ * @since 2023-07-10
+ */
+public interface HrmScheduleSignService extends IService<HrmScheduleSign> {
+
+    HttpRespMsg getAttendanceList(String json);
+}

+ 58 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/service/impl/HrmScheduleSignServiceImpl.java

@@ -0,0 +1,58 @@
+package com.attendance.service.impl;
+
+import com.alibaba.fastjson.JSONObject;
+import com.attendance.entity.HrmScheduleSign;
+import com.attendance.mapper.HrmScheduleSignMapper;
+import com.attendance.service.HrmScheduleSignService;
+import com.attendance.util.HttpRespMsg;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * <p>
+ *  服务实现类
+ * </p>
+ *
+ * @author Yurk
+ * @since 2023-07-10
+ */
+@Service
+public class HrmScheduleSignServiceImpl extends ServiceImpl<HrmScheduleSignMapper, HrmScheduleSign> implements HrmScheduleSignService {
+
+    @Resource
+    private HrmScheduleSignMapper hrmScheduleSignMapper;
+
+    @Override
+    public HttpRespMsg getAttendanceList(String json) {
+        HttpRespMsg httpRespMsg=new HttpRespMsg();
+        Map map = JSONObject.parseObject(json, Map.class);
+        DateTimeFormatter date=DateTimeFormatter.ofPattern("yyyy-MM-dd");
+        List<HrmScheduleSign> attendanceList=new ArrayList<>();
+        if(map.get("startDate")!=null&&map.get("endDate")!=null){
+            LocalDate startDate = LocalDate.parse(String.valueOf(map.get("startDate")),date);
+            LocalDate endDate = LocalDate.parse(String.valueOf(map.get("endDate")),date);
+            if(map.get("userId")!=null){
+                attendanceList = hrmScheduleSignMapper.selectList(new QueryWrapper<HrmScheduleSign>()
+                        .select("userId","signDate","substring(CONVERT(varchar(100),MIN(signTime), 8),1,5) as clockInTime","substring(CONVERT(varchar(100),MAX(signTime), 8),1,5) as afterWorkTime")
+                        .between("signDate", startDate, endDate)
+                        .eq("userId",map.get("userId"))
+                        .groupBy("userId","signDate"));
+            }else {
+                attendanceList = hrmScheduleSignMapper.selectList(new QueryWrapper<HrmScheduleSign>()
+                        .select("userId","signDate","substring(CONVERT(varchar(100),MIN(signTime), 8),1,5) as clockInTime","substring(CONVERT(varchar(100),MAX(signTime), 8),1,5) as afterWorkTime")
+                        .between("signDate", startDate, endDate)
+                        .groupBy("userId","signDate"));
+            }
+        }
+        httpRespMsg.setData(attendanceList);
+        return httpRespMsg;
+    }
+}

+ 73 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/CRC16Util.java

@@ -0,0 +1,73 @@
+package com.attendance.util;
+public class CRC16Util {
+    /**
+     * 计算CRC16校验码
+     *
+     * @param data 需要校验的字符串
+     * @return 校验码
+     */
+    public static String getCRC(String data) {
+        data = data.replace(" ", "");
+        int len = data.length();
+        if (!(len % 2 == 0)) {
+            return "0000";
+        }
+        int num = len / 2;
+        byte[] para = new byte[num];
+        for (int i = 0; i < num; i++) {
+            int value = Integer.valueOf(data.substring(i * 2, 2 * (i + 1)), 16);
+            para[i] = (byte) value;
+        }
+        return getCRC(para);
+    }
+ 
+ 
+    /**
+     * 计算CRC16校验码
+     *
+     * @param bytes 字节数组
+     * @return {@link String} 校验码
+     * @since 1.0
+     */
+    public static String getCRC(byte[] bytes) {
+        //CRC寄存器全为1
+        int CRC = 0x0000ffff;
+        //多项式校验值
+        int POLYNOMIAL = 0x0000a001;
+        int i, j;
+        for (i = 0; i < bytes.length; i++) {
+            CRC ^= ((int) bytes[i] & 0x000000ff);
+            for (j = 0; j < 8; j++) {
+                if ((CRC & 0x00000001) != 0) {
+                    CRC >>= 1;
+                    CRC ^= POLYNOMIAL;
+                } else {
+                    CRC >>= 1;
+                }
+            }
+        }
+        System.out.println("CRC"+CRC);
+        //结果转换为16进制
+        String result = Integer.toHexString(CRC).toUpperCase();
+        System.out.println("result"+result);
+        if (result.length() != 4) {
+            StringBuffer sb = new StringBuffer("0000");
+            result = sb.replace(4 - result.length(), 4, result).toString();
+        }
+//        return result;
+//        交换高低位
+        return result.substring(2, 4) + result.substring(0, 2);//高位在前,低位在后
+    }
+ 
+ 
+    public static void main(String[] args) {
+        //01 03 20 7F FF 7F FF 7F FF 7F FF 7F FF 7F FF 7F FF 7F FF 7F FF 7F FF 7F FF 7F FF 7F FF 7F FF 7F FF 7F FF 8C 45
+        //01 03 00 00 00 08 44 0C
+        //01 03 10 00 8F 02 4E 00 91 02 44 00 92 02 5A 00 8B 02 47 40 D8
+//        System.out.println(getCRC("01 03 20 7F FF 7F FF 7F FF 7F FF 7F FF 7F FF 7F FF 7F FF 7F FF 7F FF 7F FF 7F FF 7F FF 7F FF 7F FF 7F FF"));
+//        System.out.println(getCRC("01 03 00 00 00 08"));
+//        System.out.println(getCRC("01 03 10 00 8F 02 4E 00 91 02 44 00 92 02 5A 00 8B 02 47"));
+    	String crc = getCRC("FA AF 00 07 01 1e 78 1e 50 00 3C");
+        System.out.println(crc);
+    }
+}

+ 217 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/CodeGenerator.java

@@ -0,0 +1,217 @@
+package com.attendance.util;
+
+import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
+import com.baomidou.mybatisplus.core.toolkit.StringPool;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import com.baomidou.mybatisplus.generator.AutoGenerator;
+import com.baomidou.mybatisplus.generator.InjectionConfig;
+import com.baomidou.mybatisplus.generator.config.*;
+import com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;
+import com.baomidou.mybatisplus.generator.config.po.TableInfo;
+import com.baomidou.mybatisplus.generator.config.rules.FileType;
+import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Scanner;
+
+/**
+ * mybatis-plus代码生成器
+ *  使用该类需要添加以下依赖,在此之前请移除所有与mybatis有关的其他依赖,防止冲突
+ *   <dependency>
+ *      <groupId>com.baomidou</groupId>
+ *       <artifactId>mybatis-plus-generator</artifactId>
+ *       <version>3.1.2</version>
+ *  </dependency>
+ *
+ *  <dependency>
+ *        <groupId>com.baomidou</groupId>
+ *        <artifactId>mybatis-plus-boot-starter</artifactId>
+ *        <version>3.1.2</version>
+ *   </dependency>
+ *
+ */
+// 演示例子,执行 main 方法控制台输入模块表名回车自动生成对应项目目录中
+public class CodeGenerator {
+
+    /**
+     * <p>
+     * 读取控制台内容
+     * </p>
+     */
+    public static String scanner(String tip) {
+        Scanner scanner = new Scanner(System.in);
+        StringBuilder help = new StringBuilder();
+        help.append("请输入" + tip + ":");
+        System.out.println(help.toString());
+        if (scanner.hasNext()) {
+            String ipt = scanner.next();
+            if (StringUtils.isNotEmpty(ipt)) {
+                return ipt;
+            }
+        }
+        throw new MybatisPlusException("请输入正确的" + tip + "!");
+    }
+
+    public static void main(String[] args) {
+        // 代码生成器
+        AutoGenerator mpg = new AutoGenerator();
+
+        // 全局配置
+        GlobalConfig gc = new GlobalConfig();
+        // 全局配置
+
+
+        // 自定义文件命名,注意 %s 会自动填充表实体属性!
+//        gc.setMapperName("%sDao");
+//        gc.setXmlName("%sMapper");
+//        gc.setServiceName("%sService");
+//        gc.setServiceImplName("%sServiceImap");
+//        gc.setControllerName("%sController");
+        //生成的代码存放到某个路径下,这里是E盘,
+//        gc.setOutputDir("E://");
+        //生成的代码位置为当前项目
+        String projectPath = System.getProperty("user.dir");
+        gc.setOutputDir(projectPath + "/src/main/java");
+        gc.setAuthor("Yurk");
+        gc.setOpen(false);
+        gc.setFileOverride(true);
+        gc.setActiveRecord(true);
+        //%s是实体类类名占位符,不配置这行的话,对于User会生成IUserService,配置后即可生成UserService;
+        gc.setServiceName("%sService");
+        // XML 二级缓存
+//      gc.setEnableCache(true);
+        // XML ResultMap
+        gc.setBaseResultMap(true);
+        // XML columList
+        gc.setBaseColumnList(true);
+        //
+        // gc.setSwagger2(true); 实体属性 Swagger2 注解
+        mpg.setGlobalConfig(gc);
+
+        // 数据源配置
+        DataSourceConfig dsc = new DataSourceConfig();
+        dsc.setUrl("jdbc:sqlserver://localhost:1433;database=atten_dev;encrypt=false");
+//        dsc.setSchemaName("public");
+        dsc.setDriverName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
+        dsc.setUsername("sa");
+        dsc.setPassword("Sqlserver_2023");
+        mpg.setDataSource(dsc);
+
+        // 包配置
+        PackageConfig pc = new PackageConfig();
+        //若果需要在Parent(此处即com.example.plus)下新建模块时打开下面注释,后续在控制台提示输入模块时,输入想要新建的模块名就可以
+//        pc.setModuleName(scanner("模块名"));
+        pc.setParent("com.attendance");
+        mpg.setPackageInfo(pc);
+
+        // 自定义配置
+        InjectionConfig cfg = new InjectionConfig() {
+            @Override
+            public void initMap() {
+                // to do nothing
+            }
+        };
+        //以下为两种模板来生成*mapper.xml文件,任选一种即可,不同的模板对应不同的依赖
+        // 如果模板引擎是 freemarker,请添加以下依赖。
+        /**
+         *         <dependency>
+         *             <groupId>org.freemarker</groupId>
+         *             <artifactId>freemarker</artifactId>
+         *             <version>2.3.23</version>
+         *         </dependency>
+         */
+//        String templatePath = "/templates/mapper.xml.ftl";
+        // 如果模板引擎是 velocity 请添加以下依赖。
+        /**
+         *         <dependency>
+         *             <groupId>org.apache.velocity</groupId>
+         *             <artifactId>velocity-engine-core</artifactId>
+         *             <version>2.0</version>
+         *         </dependency>
+         */
+         String templatePath = "/templates/mapper.xml.vm";
+
+        // 自定义输出配置
+        List<FileOutConfig> focList = new ArrayList<>();
+        // 自定义配置会被优先输出
+        focList.add(new FileOutConfig(templatePath) {
+            @Override
+            public String outputFile(TableInfo tableInfo) {
+                if(pc.getModuleName() == null){
+                    return projectPath + "/src/main/resources/mapper/"
+                            + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
+                }else{
+                    // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
+                    return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
+                            + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
+                }
+
+            }
+
+        });
+
+        cfg.setFileCreate(new IFileCreate() {
+            @Override
+            public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) {
+                // 判断自定义文件夹是否需要创建,这里调用默认的方法
+                checkDir(filePath);
+                //对于已存在的文件,只需重复生成 entity 和 mapper.xml
+                File file = new File(filePath);
+                boolean exist = file.exists();
+                if(exist){
+                    if (filePath.endsWith("Mapper.xml")|| FileType.ENTITY==fileType){
+                        return true;
+                    }else {
+                        return false;
+                    }
+                }
+                //不存在的文件都需要创建
+                return  true;
+            }
+        });
+
+        cfg.setFileOutConfigList(focList);
+        mpg.setCfg(cfg);
+        mpg.setTemplate(new TemplateConfig().setXml(null));
+
+        // 配置模板
+//        TemplateConfig templateConfig = new TemplateConfig();
+//
+//        // 配置自定义输出模板
+//        //指定自定义模板路径,注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别
+//        // templateConfig.setEntity("templates/entity2.java");
+//        // templateConfig.setService();
+//        // templateConfig.setController();
+//
+//        templateConfig.setXml(null);
+//        mpg.setTemplate(templateConfig);
+
+        // 策略配置
+        StrategyConfig strategy = new StrategyConfig();
+        strategy.setNaming(NamingStrategy.underline_to_camel);
+        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
+        //若想要生成的实体类继承某个类,则可打开下面注释。写上需要继承的类的位置即可
+//        strategy.setSuperEntityClass("com.baomidou.ant.common.BaseEntity");
+        //【实体】是否为lombok模型(默认 false)
+        strategy.setEntityLombokModel(true);
+        //对控制器生成 @RestController 注解
+        strategy.setRestControllerStyle(true);
+        //是否生成实体时,生成字段注解
+        strategy.setEntityTableFieldAnnotationEnable(true);
+//        strategy.setEntitySerialVersionUID(false)//加此行不生成生成实体类序列化编号,不加默认生成
+        //若想要生成的实体类继承某个Controller,则可打开下面注释。写上需要继承的Controller的位置即可
+//        strategy.setSuperControllerClass("com.baomidou.ant.common.BaseController");
+        //此处user是表名,多个英文逗号分割
+//        strategy.setInclude("project,project_docfolder,project_document,stages,task,task_comment,task_file,task_log");
+//        strategy.setExclude();//数据库表全生成
+        strategy.setInclude(scanner("请输入表名").split(","));//表名,多个英文逗号分割
+        strategy.setControllerMappingHyphenStyle(true);
+        //数据库表前缀,不配置这行的话,生成的类会带有T如:TUser,配置后即可将前缀去掉
+//        strategy.setTablePrefix("tb_");
+        mpg.setStrategy(strategy);
+//        mpg.setTemplateEngine(new FreemarkerTemplateEngine());
+        mpg.execute();
+    }
+}

+ 42 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/CodeUtil.java

@@ -0,0 +1,42 @@
+package com.attendance.util;
+
+import java.text.DecimalFormat;
+import java.util.Random;
+
+/**
+ * Author: 吴涛涛 cuiyi@itany.com
+ * Date : 2019 - 10 - 24 16:23
+ * Description:验证码生成工具
+ * Version: 1.0
+ */
+public class CodeUtil {
+
+
+    public HttpRespMsg getVcode(String mobile) {
+        HttpRespMsg msg = new HttpRespMsg();
+        if (mobile != null) {
+            Random r = new Random();
+            int val = r.nextInt(10000);
+            if (val < 1000) {
+                val += 1000;
+            }
+            String codeValStr = "" + val;
+//            Vcode record = new Vcode();
+//            record.setMobile(mobile);
+//            record.setVcode("" + val);
+//            vcodeMapper.insertSelective(record);
+//            try {
+//                SendSmsResponse sendSmsResponse = SmsDemo.sendSms(mobile, record.getVcode());
+//            } catch (ClientException e) {
+//                e.printStackTrace();
+//            }
+        }
+        return msg;
+    }
+
+    public static void main(String[] args) {
+        double temp = 1.45;
+        DecimalFormat decimalFormat = new DecimalFormat("0.#");
+        System.out.println(decimalFormat.format(temp));
+    }
+}

+ 203 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/ColorUtil.java

@@ -0,0 +1,203 @@
+package com.attendance.util;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+
+public class ColorUtil {
+    public static String[] COLORS = {"#1E90FF","#FF1493", "#FF7F50", "#2E8B57", "#778899"};
+
+    public static String randomColor() {
+        int rand = new Random().nextInt(COLORS.length);
+        return COLORS[rand];
+    }
+
+    public static void main(String[] args) {
+        String[] str = new String[]{"18658127122",
+                "13958019678",
+                "13777870711",
+                "18857154912",
+                "15557161578",
+                "15397126535",
+                "13456906403",
+                "18868860034",
+                "13588345506",
+                "13777419005",
+                "15268555322",
+                "15397023915",
+                "18069703196",
+                "13456735736",
+                "13805726051",
+                "18698555982",
+                "18958046862",
+                "15868845002",
+                "13575738161",
+                "15990199772",
+                "18658872590",
+                "13867169056",
+                "18158182525",
+                "15088689713",
+                "13585961039",
+                "18668017529",
+                "18057128645",
+                "15257582246",
+                "13735844966",
+                "18106532040",
+                "13716269811",
+                "17682330355",
+                "15958035195",
+                "15874924072",
+                "15158005335",
+                "17858936083",
+                "18457061976",
+                "18518207024",
+                "15921722090",
+                "13575489658",
+                "15202181434",
+                "18621559970",
+                "13309236572",
+                "15617169920",
+                "13992062944",
+                "18792624855",
+                "18158771827",
+                "13022966965",
+                "18629391086",
+                "18857114810",
+                "17606869224",
+                "18609654280",
+                "15309252997",
+                "13666166105",
+                "18782259249",
+                "18066953451",
+                "15129233575",
+                "18011265989",
+                "15224092347",
+                "15829070933",
+                "18200376799",
+                "18601666475",
+                "13700279481",
+                "15319957089",
+                "18792475086",
+                "13572160817",
+                "15167118084",
+                "13281865290",
+                "18616761341",
+                "15897721636",
+                "17816891567",
+                "13214327825",
+                "13567105150",
+                "13596260933",
+                "15200744011",
+                "18857866308",
+                "19883270311",
+                "13891857170",
+                "18392138744",
+                "15924058876",
+                "13227892017",
+                "15715817791",
+                "15824304746",
+                "15209256108",
+                "15282690951",
+                "13208101965",
+                "13173612375",
+                "18069809196",
+                "18402868212",
+                "13588015853",
+                "15828344349",
+                "13758183719",
+                "15101135063",
+                "18767157952",
+                "15158172465",
+                "17682308747",
+                "17733397108",
+                "18767180985",
+                "18508233670",
+                "13758200824",
+                "17348510010",
+                "17705737070",
+                "15058125780",
+                "15669067889",
+                "13516713029",
+                "13223014721",
+                "18868875984",
+                "15158866871",
+                "13666699169",
+                "15937664749",
+                "15102756101",
+                "17858606393",
+                "13588905589",
+                "15267163086",
+                "17791776120",
+                "19857124399",
+                "15858297095",
+                "18279055251",
+                "17682300802",
+                "15228949530",
+                "13524587246",
+                "15620552019",
+                "18683616506",
+                "13408015748",
+                "13913730634",
+                "15332315975",
+                "18868802950",
+                "18230107901",
+                "18367821189",
+                "15651615092",
+                "15680573913",
+                "15328183610",
+                "18270824772",
+                "15757189113",
+                "18706805139",
+                "13424441897",
+                "15375687185",
+                "18482270702",
+                "15850561758",
+                "13957795236",
+                "19817711781",
+                "18092719836",
+                "13488477632",
+                "13895461198",
+                "18758271681",
+                "17858603330",
+                "15621023159",
+                "15738531696",
+                "13525071538",
+                "15858181363",
+                "15178725899",
+                "18768417808",
+                "13289815592",
+                "13508506578",
+                "17639411957",
+                "13757174079",
+                "15191024238",
+                "17764558635",
+                "15957152408",
+                "18888928715",
+                "15988879266",
+                "13572525540",
+                "17858955042",
+                "15868157451",
+                "13735485537",
+                "18368497686",
+                "13736912611",
+                "15168483201",
+                "15922337143",
+                "17396247720",
+                "15707722551",
+                "18768110676",
+                "13957983264",
+                "13588810732",
+                "15112445545",
+                "18868101349",
+                "18611395335",
+                "15814404625",
+                "18605227853",
+                "15757517964",
+                "18096049625"};
+        List<String> phoneList = Arrays.asList(str);
+        for (int i=0;i<phoneList.size(); i++) {
+
+        }
+
+    }
+}

+ 80 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/DateTimeUtil.java

@@ -0,0 +1,80 @@
+package com.attendance.util;
+
+import java.math.BigDecimal;
+import java.time.*;
+import java.time.format.DateTimeFormatter;
+import java.util.Date;
+
+public class DateTimeUtil {
+    public static final String[] WEEK_DAYS = {"周一","周二","周三","周四","周五","周六", "周日"};
+
+
+    //把秒转化为时间
+    public static String getTimeFromSeconds(int seconds) {
+        LocalDateTime now = LocalDateTime.now();
+        LocalDateTime localDateTime = now.withSecond(0).withHour(0).withMinute(0).withNano(0);
+        localDateTime = localDateTime.plusSeconds(seconds);
+        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm");
+        String format = dateTimeFormatter.format(localDateTime);
+        return format;
+    }
+
+    //把时间转为为秒,方便计算时长
+    public static int getSecondsFromTime(String time) {
+        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
+        LocalDateTime date = LocalDateTime.parse("2022-01-01 "+time, dateTimeFormatter);
+        int intSeconds = date.getHour() * 3600 + date.getMinute() * 60 + date.getSecond();
+        return intSeconds;
+    }
+
+    //把时长秒转化为小时
+    public static double getHoursFromSeconds(int seconds) {
+        double hours = 1.0f * seconds/3600;
+        return hours;
+    }
+
+    /**
+     * 返回四舍五入到整数的部分
+     */
+    public static double getHoursFromDouble(double time) {
+        BigDecimal decimal = new BigDecimal(time);
+        return decimal.setScale(0, BigDecimal.ROUND_HALF_UP).doubleValue();
+    }
+
+    public static double getHalfHoursFromDouble(double time) {
+        BigDecimal decimal = new BigDecimal(time*2);
+        return decimal.setScale(0, BigDecimal.ROUND_HALF_UP).doubleValue()/2;
+    }
+
+    public static LocalDate getLocalDateFromSeconds(long time) {
+        LocalDate localDate = LocalDateTime.ofInstant(Instant.ofEpochSecond(time), ZoneId.systemDefault()).toLocalDate();
+        return localDate;
+    }
+
+    public static String getWeekDayTxt(int day) {
+        return WEEK_DAYS[day-1];
+    }
+
+    public static Date localDateTimeToDate(final LocalDateTime localDateTime) {
+        if (null == localDateTime) {
+            return null;
+        }
+        final ZoneId zoneId = ZoneId.systemDefault();
+        final ZonedDateTime zdt = localDateTime.atZone(zoneId);
+        final Date date = Date.from(zdt.toInstant());
+        return date;
+    }
+
+    public static void main(String[] args) {
+        double d = getHalfHoursFromDouble(0.26);
+        System.out.println(d);
+        d = getHalfHoursFromDouble(1.2);
+        System.out.println(d);
+        d = getHalfHoursFromDouble(1.24);
+        System.out.println(d);
+        d = getHalfHoursFromDouble(1.25);
+        System.out.println(d);
+        d = getHalfHoursFromDouble(1.26);
+        System.out.println(d);
+    }
+}

+ 344 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/DingCallbackCrypto.java

@@ -0,0 +1,344 @@
+package com.attendance.util;
+
+import com.alibaba.fastjson.JSON;
+import org.apache.commons.codec.binary.Base64;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.ByteArrayOutputStream;
+import java.nio.charset.Charset;
+import java.security.MessageDigest;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+
+/**
+ * 钉钉开放平台加解密方法
+ * 在ORACLE官方网站下载JCE无限制权限策略文件
+ * JDK6的下载地址:http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html
+ * JDK7的下载地址: http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html
+ * JDK8的下载地址 https://www.oracle.com/java/technologies/javase-jce8-downloads.html
+ */
+public class DingCallbackCrypto {
+
+    private static final Charset CHARSET = Charset.forName("utf-8");
+    private static final Base64 base64 = new Base64();
+    private byte[] aesKey;
+    private String token;
+    private String corpId;
+    /**
+     * ask getPaddingBytes key固定长度
+     **/
+    private static final Integer AES_ENCODE_KEY_LENGTH = 43;
+    /**
+     * 加密随机字符串字节长度
+     **/
+    private static final Integer RANDOM_LENGTH = 16;
+
+    /**
+     * 构造函数
+     *
+     * @param token          钉钉开放平台上,开发者设置的token
+     * @param encodingAesKey 钉钉开放台上,开发者设置的EncodingAESKey
+     * @param corpId         企业自建应用-事件订阅, 使用appKey
+     *                       企业自建应用-注册回调地址, 使用corpId
+     *                       第三方企业应用, 使用suiteKey
+     *
+     * @throws DingTalkEncryptException 执行失败,请查看该异常的错误码和具体的错误信息
+     */
+    public DingCallbackCrypto(String token, String encodingAesKey, String corpId) throws DingTalkEncryptException {
+        if (null == encodingAesKey || encodingAesKey.length() != AES_ENCODE_KEY_LENGTH) {
+            throw new DingTalkEncryptException(DingTalkEncryptException.AES_KEY_ILLEGAL);
+        }
+        this.token = token;
+        this.corpId = corpId;
+        aesKey = Base64.decodeBase64(encodingAesKey + "=");
+    }
+
+    public Map<String, String> getEncryptedMap(String plaintext) throws DingTalkEncryptException {
+        return getEncryptedMap(plaintext, System.currentTimeMillis(), Utils.getRandomStr(16));
+    }
+
+    /**
+     * 将和钉钉开放平台同步的消息体加密,返回加密Map
+     *
+     * @param plaintext 传递的消息体明文
+     * @param timeStamp 时间戳
+     * @param nonce     随机字符串
+     * @return
+     * @throws DingTalkEncryptException
+     */
+    public Map<String, String> getEncryptedMap(String plaintext, Long timeStamp, String nonce)
+        throws DingTalkEncryptException {
+        if (null == plaintext) {
+            throw new DingTalkEncryptException(DingTalkEncryptException.ENCRYPTION_PLAINTEXT_ILLEGAL);
+        }
+        if (null == timeStamp) {
+            throw new DingTalkEncryptException(DingTalkEncryptException.ENCRYPTION_TIMESTAMP_ILLEGAL);
+        }
+        if (null == nonce || nonce.length() != 16) {
+            throw new DingTalkEncryptException(DingTalkEncryptException.ENCRYPTION_NONCE_ILLEGAL);
+        }
+        // 加密
+        String encrypt = encrypt(Utils.getRandomStr(RANDOM_LENGTH), plaintext);
+        String signature = getSignature(token, String.valueOf(timeStamp), nonce, encrypt);
+        Map<String, String> resultMap = new HashMap<String, String>();
+        resultMap.put("msg_signature", signature);
+        resultMap.put("encrypt", encrypt);
+        resultMap.put("timeStamp", String.valueOf(timeStamp));
+        resultMap.put("nonce", nonce);
+        return resultMap;
+    }
+
+    /**
+     * 密文解密
+     *
+     * @param msgSignature 签名串
+     * @param timeStamp    时间戳
+     * @param nonce        随机串
+     * @param encryptMsg   密文
+     * @return 解密后的原文
+     * @throws DingTalkEncryptException
+     */
+    public String getDecryptMsg(String msgSignature, String timeStamp, String nonce, String encryptMsg)
+        throws DingTalkEncryptException {
+        //校验签名
+        String signature = getSignature(token, timeStamp, nonce, encryptMsg);
+        if (!signature.equals(msgSignature)) {
+            throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_SIGNATURE_ERROR);
+        }
+        // 解密
+        String result = decrypt(encryptMsg);
+        return result;
+    }
+
+    /*
+     * 对明文加密.
+     * @param text 需要加密的明文
+     * @return 加密后base64编码的字符串
+     */
+    private String encrypt(String random, String plaintext) throws DingTalkEncryptException {
+        try {
+            byte[] randomBytes = random.getBytes(CHARSET);
+            byte[] plainTextBytes = plaintext.getBytes(CHARSET);
+            byte[] lengthByte = Utils.int2Bytes(plainTextBytes.length);
+            byte[] corpidBytes = corpId.getBytes(CHARSET);
+            ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+            byteStream.write(randomBytes);
+            byteStream.write(lengthByte);
+            byteStream.write(plainTextBytes);
+            byteStream.write(corpidBytes);
+            byte[] padBytes = PKCS7Padding.getPaddingBytes(byteStream.size());
+            byteStream.write(padBytes);
+            byte[] unencrypted = byteStream.toByteArray();
+            byteStream.close();
+            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
+            SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
+            IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16);
+            cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
+            byte[] encrypted = cipher.doFinal(unencrypted);
+            String result = base64.encodeToString(encrypted);
+            return result;
+        } catch (Exception e) {
+            throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_ENCRYPT_TEXT_ERROR);
+        }
+    }
+
+    /*
+     * 对密文进行解密.
+     * @param text 需要解密的密文
+     * @return 解密得到的明文
+     */
+    private String decrypt(String text) throws DingTalkEncryptException {
+        byte[] originalArr;
+        try {
+            // 设置解密模式为AES的CBC模式
+            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
+            SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
+            IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16));
+            cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
+            // 使用BASE64对密文进行解码
+            byte[] encrypted = Base64.decodeBase64(text);
+            // 解密
+            originalArr = cipher.doFinal(encrypted);
+        } catch (Exception e) {
+            throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_DECRYPT_TEXT_ERROR);
+        }
+
+        String plainText;
+        String fromCorpid;
+        try {
+            // 去除补位字符
+            byte[] bytes = PKCS7Padding.removePaddingBytes(originalArr);
+            // 分离16位随机字符串,网络字节序和corpId
+            byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20);
+            int plainTextLegth = Utils.bytes2int(networkOrder);
+            plainText = new String(Arrays.copyOfRange(bytes, 20, 20 + plainTextLegth), CHARSET);
+            fromCorpid = new String(Arrays.copyOfRange(bytes, 20 + plainTextLegth, bytes.length), CHARSET);
+        } catch (Exception e) {
+            throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_DECRYPT_TEXT_LENGTH_ERROR);
+        }
+
+        // corpid不相同的情况
+        if (!fromCorpid.equals(corpId)) {
+            throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_DECRYPT_TEXT_CORPID_ERROR);
+        }
+        return plainText;
+    }
+
+    /**
+     * 数字签名
+     *
+     * @param token     isv token
+     * @param timestamp 时间戳
+     * @param nonce     随机串
+     * @param encrypt   加密文本
+     * @return
+     * @throws DingTalkEncryptException
+     */
+    public String getSignature(String token, String timestamp, String nonce, String encrypt)
+        throws DingTalkEncryptException {
+        try {
+            String[] array = new String[] {token, timestamp, nonce, encrypt};
+            Arrays.sort(array);
+            System.out.println(JSON.toJSONString(array));
+            StringBuffer sb = new StringBuffer();
+            for (int i = 0; i < 4; i++) {
+                sb.append(array[i]);
+            }
+            String str = sb.toString();
+            System.out.println(str);
+            MessageDigest md = MessageDigest.getInstance("SHA-1");
+            md.update(str.getBytes());
+            byte[] digest = md.digest();
+
+            StringBuffer hexstr = new StringBuffer();
+            String shaHex = "";
+            for (int i = 0; i < digest.length; i++) {
+                shaHex = Integer.toHexString(digest[i] & 0xFF);
+                if (shaHex.length() < 2) {
+                    hexstr.append(0);
+                }
+                hexstr.append(shaHex);
+            }
+            return hexstr.toString();
+        } catch (Exception e) {
+            throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_SIGNATURE_ERROR);
+        }
+    }
+
+    public static class Utils {
+        public Utils() {
+        }
+
+        public static String getRandomStr(int count) {
+            String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+            Random random = new Random();
+            StringBuffer sb = new StringBuffer();
+
+            for (int i = 0; i < count; ++i) {
+                int number = random.nextInt(base.length());
+                sb.append(base.charAt(number));
+            }
+
+            return sb.toString();
+        }
+
+        public static byte[] int2Bytes(int count) {
+            byte[] byteArr = new byte[] {(byte)(count >> 24 & 255), (byte)(count >> 16 & 255), (byte)(count >> 8 & 255),
+                (byte)(count & 255)};
+            return byteArr;
+        }
+
+        public static int bytes2int(byte[] byteArr) {
+            int count = 0;
+
+            for (int i = 0; i < 4; ++i) {
+                count <<= 8;
+                count |= byteArr[i] & 255;
+            }
+
+            return count;
+        }
+    }
+
+    public static class PKCS7Padding {
+        private static final Charset CHARSET = Charset.forName("utf-8");
+        private static final int BLOCK_SIZE = 32;
+
+        public PKCS7Padding() {
+        }
+
+        public static byte[] getPaddingBytes(int count) {
+            int amountToPad = 32 - count % 32;
+            if (amountToPad == 0) {
+                amountToPad = 32;
+            }
+
+            char padChr = chr(amountToPad);
+            String tmp = new String();
+
+            for (int index = 0; index < amountToPad; ++index) {
+                tmp = tmp + padChr;
+            }
+
+            return tmp.getBytes(CHARSET);
+        }
+
+        public static byte[] removePaddingBytes(byte[] decrypted) {
+            int pad = decrypted[decrypted.length - 1];
+            if (pad < 1 || pad > 32) {
+                pad = 0;
+            }
+
+            return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);
+        }
+
+        private static char chr(int a) {
+            byte target = (byte)(a & 255);
+            return (char)target;
+        }
+    }
+
+    public static class DingTalkEncryptException extends Exception {
+        public static final int SUCCESS = 0;
+        public static final int ENCRYPTION_PLAINTEXT_ILLEGAL = 900001;
+        public static final int ENCRYPTION_TIMESTAMP_ILLEGAL = 900002;
+        public static final int ENCRYPTION_NONCE_ILLEGAL = 900003;
+        public static final int AES_KEY_ILLEGAL = 900004;
+        public static final int SIGNATURE_NOT_MATCH = 900005;
+        public static final int COMPUTE_SIGNATURE_ERROR = 900006;
+        public static final int COMPUTE_ENCRYPT_TEXT_ERROR = 900007;
+        public static final int COMPUTE_DECRYPT_TEXT_ERROR = 900008;
+        public static final int COMPUTE_DECRYPT_TEXT_LENGTH_ERROR = 900009;
+        public static final int COMPUTE_DECRYPT_TEXT_CORPID_ERROR = 900010;
+        private static Map<Integer, String> msgMap = new HashMap();
+        private Integer code;
+
+        static {
+            msgMap.put(0, "成功");
+            msgMap.put(900001, "加密明文文本非法");
+            msgMap.put(900002, "加密时间戳参数非法");
+            msgMap.put(900003, "加密随机字符串参数非法");
+            msgMap.put(900005, "签名不匹配");
+            msgMap.put(900006, "签名计算失败");
+            msgMap.put(900004, "不合法的aes key");
+            msgMap.put(900007, "计算加密文字错误");
+            msgMap.put(900008, "计算解密文字错误");
+            msgMap.put(900009, "计算解密文字长度不匹配");
+            msgMap.put(900010, "计算解密文字corpid不匹配");
+        }
+
+        public Integer getCode() {
+            return this.code;
+        }
+
+        public DingTalkEncryptException(Integer exceptionCode) {
+            super((String)msgMap.get(exceptionCode));
+            this.code = exceptionCode;
+        }
+    }
+
+}

+ 38 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/DocumentTypeUtil.java

@@ -0,0 +1,38 @@
+package com.attendance.util;
+
+public class DocumentTypeUtil {
+	/**
+	 * documentType文件类型
+	 * 0-图片文件,1-文本文件 ,2-压缩文件,3-txt,4-excle文件,5-压缩文件,6-视频文件,7-音频文件
+	 */
+
+	public static Integer DocumentType(String fileSuffix) {
+		if(".gif".equals(fileSuffix)) {
+			return 0;
+		}else if(".jpg".equals(fileSuffix)) {
+			return 0;
+		}else if(".png".equals(fileSuffix)) {
+			return 0;
+		}else if(".bmp".equals(fileSuffix)) {
+			return 0;
+		}else if(".doc".equals(fileSuffix) || ".docx".equals(fileSuffix)) {
+			return 1;
+		}else if(".txt".equals(fileSuffix)) {
+			return 2;
+		}else if(".xls".equals(fileSuffix) || ".xlsx".equals(fileSuffix)) {
+			return 3;
+		}else if(".zip".equals(fileSuffix) || ".rar".equals(fileSuffix)) {
+			return 4;
+		}else if(".avi".equals(fileSuffix) || ".rmvb".equals(fileSuffix)
+				|| ".3gp".equals(fileSuffix) || ".mp4".equals(fileSuffix)) {
+			return 5;
+		} else if(".mp3".equals(fileSuffix)) {
+			return 6;
+		} else if(".pdf".equals(fileSuffix)) {
+			return 7;
+		} else{
+			//其他文件
+			return -1;
+		}
+	}
+}

+ 586 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/ExcelUtil.java

@@ -0,0 +1,586 @@
+package com.attendance.util;
+
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.xssf.streaming.SXSSFWorkbook;
+import org.apache.poi.xssf.usermodel.XSSFCellStyle;
+import org.apache.poi.xssf.usermodel.XSSFColor;
+import org.springframework.stereotype.Component;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.List;
+
+@Component
+public class ExcelUtil {
+    /**
+     * 简单Excel导出
+     * @param title     标题
+     * @param list      数据
+     * @return
+     */
+
+    public static String exportGeneralExcelByTitleAndList(String title, List<List<String>> list, String downloadPath) {
+        String result="系统提示:Excel文件导出成功!";
+        String fileName= title+".xlsx";
+        try {
+//            response.reset();
+//            response.setHeader("Content-disposition",
+//                "attachment;filename="+ URLEncoder.encode(fileName, "UTF-8"));
+//            //设置文件头编码格式
+//            response.setContentType("APPLICATION/OCTET-STREAM;charset=UTF-8");//设置类型
+//            response.setHeader("Cache-Control","no-cache");//设置头
+//            response.setDateHeader("Expires", 0);//设置日期头
+            // 创建工作簿, 换成XSSSF 来支持万以上的数据
+            SXSSFWorkbook workBook = new SXSSFWorkbook();
+//            HSSFWorkbook workBook = new HSSFWorkbook();
+            // 创建工作类
+            Sheet sheet = workBook.createSheet();
+            //设置首行冻结
+            sheet.createFreezePane(0, 1);
+            sheet.setDefaultColumnWidth(16);
+            //设置字体样式
+            Font headFont = workBook.createFont();
+            headFont.setBold(true);
+            headFont.setFontHeightInPoints((short) 10);
+            headFont.setFontName("黑体");
+
+            Font titleFont = workBook.createFont();
+            titleFont.setBold(true);
+            titleFont.setFontHeightInPoints((short) 10);
+            titleFont.setFontName("黑体");
+
+            Font font = workBook.createFont();
+            font.setFontHeightInPoints((short) 10);
+            font.setFontName("宋体");
+
+            //设置单元格样式
+            XSSFCellStyle headStyle = (XSSFCellStyle) workBook.createCellStyle();
+            headStyle.setFont(headFont);
+            headStyle.setAlignment(HorizontalAlignment.CENTER);
+            headStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+            headStyle.setWrapText(true);
+            headStyle.setBorderBottom(BorderStyle.THIN); //下边框
+            headStyle.setBorderLeft(BorderStyle.THIN);//左边框
+            headStyle.setBorderTop(BorderStyle.THIN);//上边框
+            headStyle.setBorderRight(BorderStyle.THIN);//右边框
+
+            String color = "c0c0c0";    //此处得到的color为16进制的字符串
+            //转为RGB码
+            int r = Integer.parseInt((color.substring(0,2)),16);   //转为16进制
+            int g = Integer.parseInt((color.substring(2,4)),16);
+            int b = Integer.parseInt((color.substring(4,6)),16);
+
+            //自定义cell颜色
+//            HSSFPalette palette = workBook.getCustomPalette();
+            //这里的9是索引
+//            palette.setColorAtIndex((short)9, (byte) r, (byte) g, (byte) b);
+
+            //设置自定义颜色
+            XSSFColor xssfColor = new XSSFColor();
+            byte[] colorRgb = { (short)9, (byte) r, (byte) g, (byte) b };
+            xssfColor.setRGB(colorRgb);
+
+            headStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.index);
+            headStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);// 填充模式(和背景颜色成对使用)
+            /*headStyle.setFillForegroundColor(IndexedColors.AQUA.getIndex());*/ //设置自带的颜色
+
+            CellStyle titleStyle = workBook.createCellStyle();
+            titleStyle.setFont(titleFont);
+            titleStyle.setAlignment(HorizontalAlignment.CENTER);
+            titleStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+            titleStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);  //填充单元格
+            titleStyle.setFillForegroundColor((short)9);    //填色
+            titleStyle.setWrapText(true);
+            titleStyle.setBorderBottom(BorderStyle.THIN); //下边框
+            titleStyle.setBorderLeft(BorderStyle.THIN);//左边框
+            titleStyle.setBorderTop(BorderStyle.THIN);//上边框
+            titleStyle.setBorderRight(BorderStyle.THIN);//右边框
+
+            CellStyle cellStyle = workBook.createCellStyle();
+            cellStyle.setFont(font);
+            cellStyle.setAlignment(HorizontalAlignment.CENTER);
+            cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+            cellStyle.setWrapText(true);
+            cellStyle.setBorderBottom(BorderStyle.THIN); //下边框
+            cellStyle.setBorderLeft(BorderStyle.THIN);//左边框
+            cellStyle.setBorderTop(BorderStyle.THIN);//上边框
+            cellStyle.setBorderRight(BorderStyle.THIN);//右边框
+
+            if(list.size() > 0) {
+                //标题(如果需要在EXCEL内容最上面加标题,请打开下面的注释,修改start)
+                /*
+                HSSFRow titleRow = sheet.createRow(0);
+                titleRow.setHeightInPoints(30);
+                HSSFCell titleCell = titleRow.createCell(0);
+                titleCell.setCellStyle(headStyle);
+                titleCell.setCellValue(title);
+                //合并单元格
+                CellRangeAddress cellRangeAddress = new CellRangeAddress(0,0,0, list.get(0).size() - 1);
+                //加入合并单元格对象
+                sheet.addMergedRegion(cellRangeAddress);
+                //使用RegionUtil类为合并后的单元格添加边框
+			    RegionUtil.setBorderBottom(BorderStyle.THIN, cellRangeAddress, sheet); // 下边框
+                RegionUtil.setBorderLeft(BorderStyle.THIN, cellRangeAddress, sheet); // 左边框
+                RegionUtil.setBorderRight(BorderStyle.THIN, cellRangeAddress, sheet); // 有边框
+                RegionUtil.setBorderTop(BorderStyle.THIN, cellRangeAddress, sheet); // 上边框
+                */
+                int start = 0;
+                for(List<String> rowList : list) {
+                    Row row = sheet.createRow(start);
+                    row.setHeightInPoints(24);
+
+                    for(int i = 0; i < rowList.size(); i++) {
+                        Cell cell = row.createCell(i);
+                        if(start == 0) {
+                            cell.setCellStyle(headStyle);
+                        }else {
+                            cell.setCellStyle(cellStyle);
+                        }
+                        cell.setCellValue(rowList.get(i));
+                    }
+                    start++;
+                }
+            }
+            //用于非传统ajax;
+//            String headStr = "attachment; filename=\"" + fileName + "\"";
+//            response.setContentType("APPLICATION/OCTET-STREAM");//返回格式为流
+//            response.setHeader("Content-Disposition", headStr);
+//            //普通下载不需要以上三行,注掉即可
+//            OutputStream os = response.getOutputStream();//在线下载
+            File dir = null;
+            dir = new File(downloadPath);
+            // D://cloud/upload 文件上传后所存储的位置,部署到服务器上时配置服务器地址即可
+            if (!dir.exists()) {
+                dir.mkdirs();
+            }
+            FileOutputStream os = new FileOutputStream(downloadPath+fileName);//保存到本地
+            workBook.write(os);
+            os.flush();
+            os.close();
+        }catch(Exception e) {
+            System.out.println(result);
+            e.printStackTrace();
+        }
+        return "/upload/"+fileName;
+//        return "";
+    }
+
+    public static String exportGeneralExcelByTitleAndList2(String title, List<List<String>> list, String downloadPath) {
+        String result="系统提示:Excel文件导出成功!";
+        String fileName= title+".xlsx";
+        try {
+//            response.reset();
+//            response.setHeader("Content-disposition",
+//                "attachment;filename="+ URLEncoder.encode(fileName, "UTF-8"));
+//            //设置文件头编码格式
+//            response.setContentType("APPLICATION/OCTET-STREAM;charset=UTF-8");//设置类型
+//            response.setHeader("Cache-Control","no-cache");//设置头
+//            response.setDateHeader("Expires", 0);//设置日期头
+            // 创建工作簿
+            SXSSFWorkbook workBook = new SXSSFWorkbook();
+//            XSSFWorkbook workBook = new XSSFWorkbook();
+            // 创建工作类
+            Sheet sheet = workBook.createSheet();
+            //设置首行冻结
+            sheet.createFreezePane(0, 1);
+            sheet.setDefaultColumnWidth(16);
+            //设置字体样式
+            Font headFont = workBook.createFont();
+            headFont.setBold(true);
+            headFont.setFontHeightInPoints((short) 10);
+            headFont.setFontName("黑体");
+
+            Font titleFont = workBook.createFont();
+            titleFont.setBold(true);
+            titleFont.setFontHeightInPoints((short) 10);
+            titleFont.setFontName("黑体");
+
+            Font font = workBook.createFont();
+            font.setFontHeightInPoints((short) 10);
+            font.setFontName("宋体");
+
+            //设置单元格样式
+            XSSFCellStyle headStyle = (XSSFCellStyle) workBook.createCellStyle();
+            headStyle.setFont(headFont);
+            headStyle.setAlignment(HorizontalAlignment.CENTER);
+            headStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+            headStyle.setWrapText(true);
+            headStyle.setBorderBottom(BorderStyle.THIN); //下边框
+            headStyle.setBorderLeft(BorderStyle.THIN);//左边框
+            headStyle.setBorderTop(BorderStyle.THIN);//上边框
+            headStyle.setBorderRight(BorderStyle.THIN);//右边框
+
+            String color = "c0c0c0";    //此处得到的color为16进制的字符串
+            //转为RGB码
+            int r = Integer.parseInt((color.substring(0,2)),16);   //转为16进制
+            int g = Integer.parseInt((color.substring(2,4)),16);
+            int b = Integer.parseInt((color.substring(4,6)),16);
+
+            //自定义cell颜色
+//            HSSFPalette palette = workBook.getCustomPalette();
+            //这里的9是索引
+//            palette.setColorAtIndex((short)9, (byte) r, (byte) g, (byte) b);
+
+            //设置自定义颜色
+            XSSFColor xssfColor = new XSSFColor();
+            byte[] colorRgb = { (byte)118, (byte)147, (byte)60 };
+            xssfColor.setRGB(colorRgb);
+            headStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.index);
+            headStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);// 填充模式(和背景颜色成对使用)
+            //cs.setFillForegroundColor(IndexedColors.AQUA.getIndex()); //设置自带的颜色
+
+
+            CellStyle titleStyle = workBook.createCellStyle();
+            titleStyle.setFont(titleFont);
+            titleStyle.setAlignment(HorizontalAlignment.CENTER);
+            titleStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+            titleStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);  //填充单元格
+            titleStyle.setFillForegroundColor((short)9);    //填色
+            titleStyle.setWrapText(true);
+            titleStyle.setBorderBottom(BorderStyle.THIN); //下边框
+            titleStyle.setBorderLeft(BorderStyle.THIN);//左边框
+            titleStyle.setBorderTop(BorderStyle.THIN);//上边框
+            titleStyle.setBorderRight(BorderStyle.THIN);//右边框
+
+            CellStyle cellStyle = workBook.createCellStyle();
+            cellStyle.setFont(font);
+            cellStyle.setAlignment(HorizontalAlignment.CENTER);
+            cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+            cellStyle.setWrapText(true);
+            cellStyle.setBorderBottom(BorderStyle.THIN); //下边框
+            cellStyle.setBorderLeft(BorderStyle.THIN);//左边框
+            cellStyle.setBorderTop(BorderStyle.THIN);//上边框
+            cellStyle.setBorderRight(BorderStyle.THIN);//右边框
+
+            if(list.size() > 0) {
+                //标题(如果需要在EXCEL内容最上面加标题,请打开下面的注释,修改start)
+                /*
+                HSSFRow titleRow = sheet.createRow(0);
+                titleRow.setHeightInPoints(30);
+                HSSFCell titleCell = titleRow.createCell(0);
+                titleCell.setCellStyle(headStyle);
+                titleCell.setCellValue(title);
+                //合并单元格
+                CellRangeAddress cellRangeAddress = new CellRangeAddress(0,0,0, list.get(0).size() - 1);
+                //加入合并单元格对象
+                sheet.addMergedRegion(cellRangeAddress);
+                //使用RegionUtil类为合并后的单元格添加边框
+			    RegionUtil.setBorderBottom(BorderStyle.THIN, cellRangeAddress, sheet); // 下边框
+                RegionUtil.setBorderLeft(BorderStyle.THIN, cellRangeAddress, sheet); // 左边框
+                RegionUtil.setBorderRight(BorderStyle.THIN, cellRangeAddress, sheet); // 有边框
+                RegionUtil.setBorderTop(BorderStyle.THIN, cellRangeAddress, sheet); // 上边框
+                */
+                int start = 0;
+                for(List<String> rowList : list) {
+                    Row row = sheet.createRow(start);
+                    row.setHeightInPoints(24);
+
+                    for(int i = 0; i < rowList.size(); i++) {
+                        Cell cell = row.createCell(i);
+                        if(start == 0) {
+                            cell.setCellStyle(headStyle);
+                        }else {
+                            cell.setCellStyle(cellStyle);
+                        }
+                        cell.setCellValue(rowList.get(i));
+                    }
+                    start++;
+                }
+            }
+            //用于非传统ajax;
+//            String headStr = "attachment; filename=\"" + fileName + "\"";
+//            response.setContentType("APPLICATION/OCTET-STREAM");//返回格式为流
+//            response.setHeader("Content-Disposition", headStr);
+//            //普通下载不需要以上三行,注掉即可
+//            OutputStream os = response.getOutputStream();//在线下载
+            File dir = null;
+            dir = new File(downloadPath);
+            // D://cloud/upload 文件上传后所存储的位置,部署到服务器上时配置服务器地址即可
+            if (!dir.exists()) {
+                dir.mkdirs();
+            }
+            FileOutputStream os = new FileOutputStream(downloadPath+fileName);//保存到本地
+            workBook.write(os);
+            os.flush();
+            os.close();
+        }catch(Exception e) {
+            System.out.println(result);
+            e.printStackTrace();
+        }
+        return "/upload/"+fileName;
+//        return "";
+    }
+
+    public static String exportTwoSheetGeneralExcelByTitleAndList(String title, List<List<String>> sheetOneList,List<List<String>> sheetTwoList, String downloadPath,String sheetOneName,String sheetTwoName) {
+        String result="系统提示:Excel文件导出成功!";
+        String fileName= title+".xlsx";
+        try {
+            // 创建工作簿
+            SXSSFWorkbook workBook = new SXSSFWorkbook();
+            // 创建工作类
+            Sheet sheetOne = workBook.createSheet();
+            workBook.setSheetName(0,sheetOneName);
+            Sheet sheetTwo = workBook.createSheet();
+            workBook.setSheetName(1,sheetTwoName);
+            sheetOne.setDefaultColumnWidth(16);
+            sheetTwo.setDefaultColumnWidth(16);
+            //设置字体样式
+            Font headFont = workBook.createFont();
+            headFont.setBold(true);
+            headFont.setFontHeightInPoints((short) 10);
+            headFont.setFontName("黑体");
+
+            Font titleFont = workBook.createFont();
+            titleFont.setBold(true);
+            titleFont.setFontHeightInPoints((short) 10);
+            titleFont.setFontName("黑体");
+
+            Font font = workBook.createFont();
+            font.setFontHeightInPoints((short) 10);
+            font.setFontName("宋体");
+
+            //设置单元格样式
+            XSSFCellStyle headStyle = (XSSFCellStyle) workBook.createCellStyle();
+            headStyle.setFont(headFont);
+            headStyle.setAlignment(HorizontalAlignment.CENTER);
+            headStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+            headStyle.setWrapText(true);
+            headStyle.setBorderBottom(BorderStyle.THIN); //下边框
+            headStyle.setBorderLeft(BorderStyle.THIN);//左边框
+            headStyle.setBorderTop(BorderStyle.THIN);//上边框
+            headStyle.setBorderRight(BorderStyle.THIN);//右边框
+
+            String color = "c0c0c0";    //此处得到的color为16进制的字符串
+            //转为RGB码
+            int r = Integer.parseInt((color.substring(0,2)),16);   //转为16进制
+            int g = Integer.parseInt((color.substring(2,4)),16);
+            int b = Integer.parseInt((color.substring(4,6)),16);
+
+            //自定义cell颜色
+//            HSSFPalette palette = workBook.getCustomPalette();
+            //这里的9是索引
+//            palette.setColorAtIndex((short)9, (byte) r, (byte) g, (byte) b);
+
+            //设置自定义颜色
+            XSSFColor xssfColor = new XSSFColor();
+            byte[] colorRgb = { (byte)118, (byte)147, (byte)60 };
+            xssfColor.setRGB(colorRgb);
+            headStyle.setFillForegroundColor(xssfColor);
+            headStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.index);
+            headStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);// 填充模式(和背景颜色成对使用)
+            //cs.setFillForegroundColor(IndexedColors.AQUA.getIndex()); //设置自带的颜色
+
+            CellStyle titleStyle = workBook.createCellStyle();
+            titleStyle.setFont(titleFont);
+            titleStyle.setAlignment(HorizontalAlignment.CENTER);
+            titleStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+            titleStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);  //填充单元格
+            titleStyle.setFillForegroundColor((short)9);    //填色
+            titleStyle.setWrapText(true);
+            titleStyle.setBorderBottom(BorderStyle.THIN); //下边框
+            titleStyle.setBorderLeft(BorderStyle.THIN);//左边框
+            titleStyle.setBorderTop(BorderStyle.THIN);//上边框
+            titleStyle.setBorderRight(BorderStyle.THIN);//右边框
+
+            CellStyle cellStyle = workBook.createCellStyle();
+            cellStyle.setFont(font);
+            cellStyle.setAlignment(HorizontalAlignment.CENTER);
+            cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+            cellStyle.setWrapText(true);
+            cellStyle.setBorderBottom(BorderStyle.THIN); //下边框
+            cellStyle.setBorderLeft(BorderStyle.THIN);//左边框
+            cellStyle.setBorderTop(BorderStyle.THIN);//上边框
+            cellStyle.setBorderRight(BorderStyle.THIN);//右边框
+
+            if(sheetOneList.size() > 0) {
+                //标题(如果需要在EXCEL内容最上面加标题,请打开下面的注释,修改start)
+                /*
+                HSSFRow titleRow = sheet.createRow(0);
+                titleRow.setHeightInPoints(30);
+                HSSFCell titleCell = titleRow.createCell(0);
+                titleCell.setCellStyle(headStyle);
+                titleCell.setCellValue(title);
+                //合并单元格
+                CellRangeAddress cellRangeAddress = new CellRangeAddress(0,0,0, list.get(0).size() - 1);
+                //加入合并单元格对象
+                sheet.addMergedRegion(cellRangeAddress);
+                //使用RegionUtil类为合并后的单元格添加边框
+			    RegionUtil.setBorderBottom(BorderStyle.THIN, cellRangeAddress, sheet); // 下边框
+                RegionUtil.setBorderLeft(BorderStyle.THIN, cellRangeAddress, sheet); // 左边框
+                RegionUtil.setBorderRight(BorderStyle.THIN, cellRangeAddress, sheet); // 有边框
+                RegionUtil.setBorderTop(BorderStyle.THIN, cellRangeAddress, sheet); // 上边框
+                */
+                int start = 0;
+                for(List<String> rowList : sheetOneList) {
+                    Row row = sheetOne.createRow(start);
+                    row.setHeightInPoints(24);
+                    for(int i = 0; i < rowList.size(); i++) {
+                        Cell cell = row.createCell(i);
+                        if(start == 0) {
+                            cell.setCellStyle(headStyle);
+                        }else {
+                            cell.setCellStyle(cellStyle);
+                        }
+                        cell.setCellValue(rowList.get(i));
+                    }
+                    start++;
+                }
+            }
+            if(sheetTwoList.size()>0){
+                int start = 0;
+                for(List<String> rowList : sheetTwoList) {
+                    Row row = sheetTwo.createRow(start);
+                    row.setHeightInPoints(24);
+                    for(int i = 0; i < rowList.size(); i++) {
+                        Cell cell = row.createCell(i);
+                        if(start == 0) {
+                            cell.setCellStyle(titleStyle);
+                        }else {
+                            cell.setCellStyle(cellStyle);
+                        }
+                        cell.setCellValue(rowList.get(i));
+                    }
+                    start++;
+                }
+            }
+            File dir = null;
+            dir = new File(downloadPath);
+            if (!dir.exists()) {
+                dir.mkdirs();
+            }
+            FileOutputStream os = new FileOutputStream(downloadPath+fileName);//保存到本地
+            workBook.write(os);
+            os.flush();
+            os.close();
+        }catch(Exception e) {
+            System.out.println(result);
+            e.printStackTrace();
+        }
+        return "/upload/"+fileName;
+    }
+
+    public static String exportMultiSheetGeneralExcelByTitleAndList(String title, List<List<String>>[] multiSheetList, String downloadPath,String[] sheetsName) {
+        String result="系统提示:Excel文件导出成功!";
+        String fileName= title+".xlsx";
+        try {
+            // 创建工作簿
+            SXSSFWorkbook workBook = new SXSSFWorkbook();
+            // 创建工作类
+            for (int sheetIndex=0;sheetIndex<multiSheetList.length; sheetIndex++) {
+                Sheet sheetOne = workBook.createSheet();
+                workBook.setSheetName(sheetIndex,sheetsName[sheetIndex]);
+                sheetOne.setDefaultColumnWidth(16);
+
+                //设置字体样式
+                Font headFont = workBook.createFont();
+                headFont.setBold(true);
+                headFont.setFontHeightInPoints((short) 10);
+                headFont.setFontName("黑体");
+
+                Font titleFont = workBook.createFont();
+                titleFont.setBold(true);
+                titleFont.setFontHeightInPoints((short) 10);
+                titleFont.setFontName("黑体");
+
+                Font font = workBook.createFont();
+                font.setFontHeightInPoints((short) 10);
+                font.setFontName("宋体");
+
+                //设置单元格样式
+                XSSFCellStyle headStyle = (XSSFCellStyle) workBook.createCellStyle();
+                headStyle.setFont(headFont);
+                headStyle.setAlignment(HorizontalAlignment.CENTER);
+                headStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+                headStyle.setWrapText(true);
+                headStyle.setBorderBottom(BorderStyle.THIN); //下边框
+                headStyle.setBorderLeft(BorderStyle.THIN);//左边框
+                headStyle.setBorderTop(BorderStyle.THIN);//上边框
+                headStyle.setBorderRight(BorderStyle.THIN);//右边框
+
+                String color = "c0c0c0";    //此处得到的color为16进制的字符串
+                //转为RGB码
+                int r = Integer.parseInt((color.substring(0,2)),16);   //转为16进制
+                int g = Integer.parseInt((color.substring(2,4)),16);
+                int b = Integer.parseInt((color.substring(4,6)),16);
+
+                //自定义cell颜色
+//            HSSFPalette palette = workBook.getCustomPalette();
+                //这里的9是索引
+//            palette.setColorAtIndex((short)9, (byte) r, (byte) g, (byte) b);
+
+                //设置自定义颜色
+                XSSFColor xssfColor = new XSSFColor();
+                byte[] colorRgb = { (byte)118, (byte)147, (byte)60 };
+                xssfColor.setRGB(colorRgb);
+                headStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.index);
+                headStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);// 填充模式(和背景颜色成对使用)
+                //cs.setFillForegroundColor(IndexedColors.AQUA.getIndex()); //设置自带的颜色
+
+                CellStyle titleStyle = workBook.createCellStyle();
+                titleStyle.setFont(titleFont);
+                titleStyle.setAlignment(HorizontalAlignment.CENTER);
+                titleStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+                titleStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);  //填充单元格
+                titleStyle.setFillForegroundColor((short)9);    //填色
+                titleStyle.setWrapText(true);
+                titleStyle.setBorderBottom(BorderStyle.THIN); //下边框
+                titleStyle.setBorderLeft(BorderStyle.THIN);//左边框
+                titleStyle.setBorderTop(BorderStyle.THIN);//上边框
+                titleStyle.setBorderRight(BorderStyle.THIN);//右边框
+
+                CellStyle cellStyle = workBook.createCellStyle();
+                cellStyle.setFont(font);
+                cellStyle.setAlignment(HorizontalAlignment.CENTER);
+                cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+                cellStyle.setWrapText(true);
+                cellStyle.setBorderBottom(BorderStyle.THIN); //下边框
+                cellStyle.setBorderLeft(BorderStyle.THIN);//左边框
+                cellStyle.setBorderTop(BorderStyle.THIN);//上边框
+                cellStyle.setBorderRight(BorderStyle.THIN);//右边框
+
+                if(multiSheetList[sheetIndex].size() > 0) {
+                    int start = 0;
+                    for(List<String> rowList : multiSheetList[sheetIndex]) {
+                        Row row = sheetOne.createRow(start);
+                        row.setHeightInPoints(24);
+                        for(int i = 0; i < rowList.size(); i++) {
+                            Cell cell = row.createCell(i);
+                            if(start == 0) {
+                                cell.setCellStyle(headStyle);
+                            }else {
+                                cell.setCellStyle(cellStyle);
+                            }
+                            cell.setCellValue(rowList.get(i));
+                        }
+                        start++;
+                    }
+                }
+            }
+
+
+            File dir = new File(downloadPath);
+            if (!dir.exists()) {
+                dir.mkdirs();
+            }
+            FileOutputStream os = new FileOutputStream(downloadPath+fileName);//保存到本地
+            workBook.write(os);
+            os.flush();
+            os.close();
+        }catch(Exception e) {
+            System.out.println(result);
+            e.printStackTrace();
+        }
+        return "/upload/"+fileName;
+    }
+
+    public static boolean isRowEmpty(Row row){
+        for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) {
+            Cell cell = row.getCell(i);
+            if (cell != null && cell.getCellTypeEnum() != CellType.BLANK){
+//                System.out.println(i+":"+cell.getCellTypeEnum());
+                return false;
+            }
+        }
+        return true;
+    }
+}

+ 72 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/FileCopyToFolderUtil.java

@@ -0,0 +1,72 @@
+package com.attendance.util;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Author: 吴涛涛 cuiyi@itany.com
+ * Date : 2019 - 08 - 30 13:59
+ * Description:<描述>
+ * Version: 1.0
+ */
+public class FileCopyToFolderUtil {
+    public static void main(String[] args) throws IOException {
+        Date date = new Date();
+        long time1 = date.getTime();
+        List<String>  sourceFileUrls = new ArrayList<>();
+        sourceFileUrls.add("D:\\软件\\ideaIU-2018.1.5.exe");
+        copy(sourceFileUrls, "D:\\775");
+        date = new Date();
+        long time2 = date.getTime();
+        System.out.println("耗时===》"+(time2-time1)/1000);
+        File file = new File("D:\\776");
+        if (file.exists()) {
+            file.delete();
+            //创建文件夹
+            file.mkdirs();
+        } else {
+            file.mkdirs();
+        }
+        System.out.println(file.getPath());
+    }
+    public static String copy(List<String>  sourceFileUrls, String destinationFolder) throws IOException {
+        //新文件夾
+        File file = new File(destinationFolder);
+        if (file.exists()) {
+            file.delete();
+            //创建文件夹
+            file.mkdirs();
+        } else {
+            file.mkdirs();
+        }
+        //如果源文件存在就复制
+        for (String sourceFileUrl : sourceFileUrls) {
+            //目标源文件夹
+            File source = new File(sourceFileUrl);
+            if (source.exists()) {
+                //新文件夹的路径
+                File newFile = new File(file + File.separator + source.getName());
+                if (source.isFile()) {
+                    FileInputStream in = new FileInputStream(source);
+                    BufferedInputStream bis= new BufferedInputStream(in);
+                    FileOutputStream out = new FileOutputStream(newFile);
+                    BufferedOutputStream bos= new BufferedOutputStream(out);
+                    byte[] bs = new byte[4096*10];
+                    int count = 0;
+//循环把源文件的内容写入新文件
+                    while ((count = bis.read(bs, 0, bs.length)) != -1) {
+                        bos.write(bs, 0, count);
+                    }
+//关闭流
+                    out.flush();
+                    out.close();
+                    in.close();
+                }
+            }
+        }
+        return file.getPath();
+    }
+
+}

+ 138 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/FileUtil.java

@@ -0,0 +1,138 @@
+package com.attendance.util;
+
+import java.io.*;
+
+/**
+ * 文件读取工具类
+ */
+public class FileUtil {
+
+    /**
+     * 获取容易识别的文件大小,比如KB, MB, GB
+     * @param size
+     * @return
+     */
+    public static String getReadableFileSize(long size) {
+        if (size < 1024) {//1K以内
+            return size + "byte";
+        } else if (size < 1024 * 1024) {//1M以内
+            return String.format("%.1fKB", (size*1.0f/1024));
+        } else if (size < 1024 * 1024 * 1024) {//1G以内
+            return String.format("%.1fMB", (size*1.0f/1024/1024));
+        } else {
+            return String.format("%.1fGB", (size*1.0f/1024/1024/1024));
+        }
+    }
+
+
+    /**
+     * 读取文件内容,作为字符串返回
+     */
+    public static String readFileAsString(String filePath) throws IOException {
+        File file = new File(filePath);
+        if (!file.exists()) {
+            throw new FileNotFoundException(filePath);
+        }
+
+        if (file.length() > 1024 * 1024 * 1024) {
+            throw new IOException("File is too large");
+        }
+
+        StringBuilder sb = new StringBuilder((int) (file.length()));
+        // 创建字节输入流  
+        FileInputStream fis = new FileInputStream(filePath);
+        // 创建一个长度为10240的Buffer
+        byte[] bbuf = new byte[10240];
+        // 用于保存实际读取的字节数  
+        int hasRead = 0;
+        while ((hasRead = fis.read(bbuf)) > 0) {
+            sb.append(new String(bbuf, 0, hasRead));
+        }
+        fis.close();
+        return sb.toString();
+    }
+
+    /**
+     * 根据文件路径读取byte[] 数组
+     */
+    public static byte[] readFileByBytes(String filePath) throws IOException {
+        File file = new File(filePath);
+        if (!file.exists()) {
+            throw new FileNotFoundException(filePath);
+        } else {
+            ByteArrayOutputStream bos = new ByteArrayOutputStream((int) file.length());
+            BufferedInputStream in = null;
+
+            try {
+                in = new BufferedInputStream(new FileInputStream(file));
+                short bufSize = 1024;
+                byte[] buffer = new byte[bufSize];
+                int len1;
+                while (-1 != (len1 = in.read(buffer, 0, bufSize))) {
+                    bos.write(buffer, 0, len1);
+                }
+
+                byte[] var7 = bos.toByteArray();
+                return var7;
+            } finally {
+                try {
+                    if (in != null) {
+                        in.close();
+                    }
+                } catch (IOException var14) {
+                    var14.printStackTrace();
+                }
+
+                bos.close();
+            }
+        }
+    }
+
+    /**
+     * 根据文件路径读取byte[] 数组
+     */
+    public static byte[] readFileByBytes(File file) throws IOException {
+        if (file == null) {
+            throw new FileNotFoundException("file is not null");
+        } else {
+            ByteArrayOutputStream bos = new ByteArrayOutputStream((int) file.length());
+            BufferedInputStream in = null;
+            try {
+                in = new BufferedInputStream(new FileInputStream(file));
+                short bufSize = 1024;
+                byte[] buffer = new byte[bufSize];
+                int len1;
+                while (-1 != (len1 = in.read(buffer, 0, bufSize))) {
+                    bos.write(buffer, 0, len1);
+                }
+
+                byte[] var7 = bos.toByteArray();
+                return var7;
+            } finally {
+                try {
+                    if (in != null) {
+                        in.close();
+                    }
+                } catch (IOException var14) {
+                    var14.printStackTrace();
+                }
+
+                bos.close();
+            }
+        }
+    }
+
+
+    /**
+     * 删除
+     *
+     * @param files
+     */
+    private void deleteFile(File... files) {
+        for (File file : files) {
+            if (file.exists()) {
+                file.delete();
+            }
+        }
+    }
+}

+ 28 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/GsonUtils.java

@@ -0,0 +1,28 @@
+package com.attendance.util;///*
+// * Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
+// */
+//package com.management.platform.util;
+//
+//import com.google.gson.Gson;
+//import com.google.gson.GsonBuilder;
+//import com.google.gson.JsonParseException;
+//import java.lang.reflect.Type;
+//
+///**
+// * Json工具类.
+// */
+//public class GsonUtils {
+//    private static Gson gson = new GsonBuilder().create();
+//
+//    public static String toJson(Object value) {
+//        return gson.toJson(value);
+//    }
+//
+//    public static <T> T fromJson(String json, Class<T> classOfT) throws JsonParseException {
+//        return gson.fromJson(json, classOfT);
+//    }
+//
+//    public static <T> T fromJson(String json, Type typeOfT) throws JsonParseException {
+//        return (T) gson.fromJson(json, typeOfT);
+//    }
+//}

+ 293 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/HttpKit.java

@@ -0,0 +1,293 @@
+package com.attendance.util;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+
+import javax.net.ssl.*;
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Map;
+import java.util.Map.Entry;
+
+@Slf4j
+public class HttpKit {
+    
+    private static final String DEFAULT_CHARSET = "UTF-8";
+    /**
+     * 发送Get请求
+     * @param url
+     * @return
+     * @throws NoSuchProviderException 
+     * @throws NoSuchAlgorithmException 
+     * @throws IOException 
+     * @throws KeyManagementException 
+     */
+    public static String get(String url) throws NoSuchAlgorithmException, NoSuchProviderException, IOException, KeyManagementException {
+        StringBuffer bufferRes = null;
+        TrustManager[] tm = { new MyX509TrustManager() };  
+        SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");  
+        sslContext.init(null, tm, new java.security.SecureRandom());  
+        // 从上述SSLContext对象中得到SSLSocketFactory对象  
+        SSLSocketFactory ssf = sslContext.getSocketFactory();
+        
+        URL urlGet = new URL(url);
+        HttpsURLConnection http = (HttpsURLConnection) urlGet.openConnection();
+        // 连接超时
+        http.setConnectTimeout(25000);
+        // 读取超时 --服务器响应比较慢,增大时间
+        http.setReadTimeout(25000);
+        http.setRequestMethod("GET");
+        http.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
+        http.setSSLSocketFactory(ssf);
+        http.setDoOutput(true);
+        http.setDoInput(true);
+        http.connect();
+        
+        InputStream in = http.getInputStream();
+        BufferedReader read = new BufferedReader(new InputStreamReader(in, DEFAULT_CHARSET));
+        String valueString = null;
+        bufferRes = new StringBuffer();
+        while ((valueString = read.readLine()) != null){
+            bufferRes.append(valueString);
+        }
+        in.close();
+        if (http != null) {
+            // 关闭连接
+            http.disconnect();
+        }
+        return bufferRes.toString();
+    }
+    
+    /**
+     * 发送Get请求
+     * @param url
+     * @return
+     * @throws NoSuchProviderException 
+     * @throws NoSuchAlgorithmException 
+     * @throws IOException 
+     * @throws KeyManagementException 
+     */
+    public static String get(String url,Boolean https) throws NoSuchAlgorithmException, NoSuchProviderException, IOException, KeyManagementException {
+     if(https != null && https){
+      return get(url);
+     }else{
+      StringBuffer bufferRes = null;
+            URL urlGet = new URL(url);
+            HttpURLConnection http = (HttpURLConnection) urlGet.openConnection();
+            // 连接超时
+            http.setConnectTimeout(25000);
+            // 读取超时 --服务器响应比较慢,增大时间
+            http.setReadTimeout(25000);
+            http.setRequestMethod("GET");
+            http.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
+            http.setDoOutput(true);
+            http.setDoInput(true);
+            http.connect();
+            
+            InputStream in = http.getInputStream();
+            BufferedReader read = new BufferedReader(new InputStreamReader(in, DEFAULT_CHARSET));
+            String valueString = null;
+            bufferRes = new StringBuffer();
+            while ((valueString = read.readLine()) != null){
+                bufferRes.append(valueString);
+            }
+            in.close();
+            if (http != null) {
+                // 关闭连接
+                http.disconnect();
+            }
+            return bufferRes.toString();
+     }
+    }
+    /**
+     *  发送Get请求
+     * @param url
+     * @param params
+     * @return
+     * @throws IOException 
+     * @throws NoSuchProviderException 
+     * @throws NoSuchAlgorithmException 
+     * @throws KeyManagementException 
+     */
+    public static String get(String url, Map<String, String> params) throws KeyManagementException, NoSuchAlgorithmException, NoSuchProviderException, IOException {
+        return get(initParams(url, params));
+    }
+    /**
+     *  发送Post请求
+     * @param url
+     * @param params
+     * @return
+     * @throws IOException 
+     * @throws NoSuchProviderException 
+     * @throws NoSuchAlgorithmException 
+     * @throws KeyManagementException 
+     */
+    public static String post(String url, String params) throws IOException, NoSuchAlgorithmException, NoSuchProviderException, KeyManagementException {
+     StringBuffer bufferRes = null;
+        TrustManager[] tm = { new MyX509TrustManager() };
+        SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
+        sslContext.init(null, tm, new java.security.SecureRandom());
+        // 从上述SSLContext对象中得到SSLSocketFactory对象  
+        SSLSocketFactory ssf = sslContext.getSocketFactory();
+        URL urlGet = new URL(url);
+        HttpsURLConnection http = (HttpsURLConnection) urlGet.openConnection();
+        // 连接超时
+        http.setConnectTimeout(25000);
+        // 读取超时 --服务器响应比较慢,增大时间
+        http.setReadTimeout(25000);
+        http.setRequestMethod("POST");
+        http.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
+        http.setSSLSocketFactory(ssf);
+        http.setDoOutput(true);
+        http.setDoInput(true);
+        http.connect();
+        OutputStream out = http.getOutputStream();
+        out.write(params.getBytes("UTF-8"));
+        out.flush();
+        out.close();
+        InputStream in = http.getInputStream();
+        BufferedReader read = new BufferedReader(new InputStreamReader(in, DEFAULT_CHARSET));
+        String valueString = null;
+        bufferRes = new StringBuffer();
+        while ((valueString = read.readLine()) != null){
+            bufferRes.append(valueString);
+        }
+        in.close();
+        if (http != null) {
+            // 关闭连接
+            http.disconnect();
+        }
+        return bufferRes.toString();
+    }
+    
+    /**
+     * 上传媒体文件
+     * @param url
+     * @param file
+     * @return
+     * @throws IOException
+     * @throws NoSuchAlgorithmException
+     * @throws NoSuchProviderException
+     * @throws KeyManagementException
+     */
+    public static String upload(String url,File file) throws IOException, NoSuchAlgorithmException, NoSuchProviderException, KeyManagementException {
+        String BOUNDARY = "----WebKitFormBoundaryiDGnV9zdZA1eM1yL"; // 定义数据分隔线  
+        StringBuffer bufferRes = null;
+        URL urlGet = new URL(url);
+        HttpURLConnection conn = (HttpURLConnection) urlGet.openConnection();
+        conn.setDoOutput(true);  
+        conn.setDoInput(true);  
+        conn.setUseCaches(false);  
+        conn.setRequestMethod("POST");  
+        conn.setRequestProperty("connection", "Keep-Alive");  
+        conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.107 Safari/537.36");  
+        conn.setRequestProperty("Charsert", "UTF-8");   
+        conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);  
+          
+        OutputStream out = new DataOutputStream(conn.getOutputStream());  
+        byte[] end_data = ("\r\n--" + BOUNDARY + "--\r\n").getBytes();// 定义最后数据分隔线  
+        StringBuilder sb = new StringBuilder();    
+        sb.append("--");    
+        sb.append(BOUNDARY);    
+        sb.append("\r\n");    
+        sb.append("Content-Disposition: form-data;name=\"media\";filename=\""+ file.getName() + "\"\r\n");    
+        sb.append("Content-Type:application/octet-stream\r\n\r\n");    
+        byte[] data = sb.toString().getBytes();  
+        out.write(data);  
+        DataInputStream fs = new DataInputStream(new FileInputStream(file));  
+        int bytes = 0;  
+        byte[] bufferOut = new byte[1024];  
+        while ((bytes = fs.read(bufferOut)) != -1) {  
+            out.write(bufferOut, 0, bytes);  
+        }  
+        out.write("\r\n".getBytes()); //多个文件时,二个文件之间加入这个  
+        fs.close();  
+        out.write(end_data);  
+        out.flush();    
+        out.close();   
+          
+        // 定义BufferedReader输入流来读取URL的响应  
+        InputStream in = conn.getInputStream();
+        BufferedReader read = new BufferedReader(new InputStreamReader(in, DEFAULT_CHARSET));
+        String valueString = null;
+        bufferRes = new StringBuffer();
+        while ((valueString = read.readLine()) != null){
+            bufferRes.append(valueString);
+        }
+        in.close();
+        if (conn != null) {
+            // 关闭连接
+         conn.disconnect();
+        }
+        return bufferRes.toString();
+    }
+    
+    /**
+     * 构造url
+     * @param url
+     * @param params
+     * @return
+     */
+    private static String initParams(String url, Map<String, String> params){
+        if (null == params || params.isEmpty()) {
+            return url;
+        }
+        StringBuilder sb = new StringBuilder(url);
+        if (url.indexOf("?") == -1) {
+            sb.append("?");
+        } else {
+            sb.append("&");
+        }
+        boolean first = true;
+        for (Entry<String, String> entry : params.entrySet()) {
+            if (first) {
+                first = false;
+            } else {
+                sb.append("&");
+            }
+            String key = entry.getKey();
+            String value = entry.getValue();
+            sb.append(key).append("=");
+            if (StringUtils.isNotEmpty(value)) {
+                try {
+                    sb.append(URLEncoder.encode(value, DEFAULT_CHARSET));
+                } catch (UnsupportedEncodingException e) {
+                    e.printStackTrace();
+                    log.error(url,e);
+                }
+            }
+        }
+        return sb.toString();
+    }
+    
+    public static void main(String[] args) {
+     String fname = "dsasdas.mp4";
+     String s = fname.substring(0, fname.lastIndexOf("."));
+     String f = fname.substring(s.length()+1);
+  System.out.println(f);
+ }
+}
+
+/**
+ * 证书管理
+ */
+class MyX509TrustManager implements X509TrustManager {
+    public X509Certificate[] getAcceptedIssuers() {
+        return null;  
+    }
+    @Override
+    public void checkClientTrusted(X509Certificate[] chain, String authType)
+            throws CertificateException {
+    }
+    @Override
+    public void checkServerTrusted(X509Certificate[] chain, String authType)
+            throws CertificateException {
+    }
+}

+ 69 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/HttpRespMsg.java

@@ -0,0 +1,69 @@
+package com.attendance.util;
+
+import com.alibaba.fastjson.JSONObject;
+import com.fasterxml.jackson.annotation.JsonInclude;
+
+import java.io.Serializable;
+
+public class HttpRespMsg implements Serializable {
+
+    //status code, ok or error.
+    public String code;
+
+    //为code生成getter和setter方法
+    public String getCode() {
+        return code;
+    }
+    public void setCode(String code) {
+        this.code = code;
+    }
+
+    //为data生成getter和setter方法
+    public Object getData() {
+        return data;
+    }
+    public void setData(Object data) {
+        this.data = data;
+    }
+
+    //为msg生成getter和setter方法
+    public String getMsg() {
+        return msg;
+    }
+    public void setMsg(String msg) {
+        this.msg = msg;
+    }
+
+
+
+
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    public String msg;
+
+    //data content, in jsonformat, or zipped string when format is gzip
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    public Object data;
+
+    public HttpRespMsg() {
+        code = "ok";
+    }
+
+    public void setError(String errorMsg) {
+        code = "error";
+        msg = errorMsg;
+    }
+
+    public HttpRespMsg fail(String errorMsg) {
+        setError(errorMsg);
+        return this;
+    }
+
+    public String toJSONStr() {
+        JSONObject json = new JSONObject();
+        json.put("code", code);
+        json.put("data", data);
+        json.put("msg", msg);
+
+        return json.toJSONString();
+    }
+}

+ 77 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/HttpUtil.java

@@ -0,0 +1,77 @@
+package com.attendance.util;
+
+import java.io.BufferedReader;
+import java.io.DataOutputStream;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * http 工具类
+ */
+public class HttpUtil {
+
+    public static String post(String requestUrl, String accessToken, String params)
+            throws Exception {
+        String contentType = "application/x-www-form-urlencoded";
+        return HttpUtil.post(requestUrl, accessToken, contentType, params);
+    }
+
+    public static String post(String requestUrl, String accessToken, String contentType, String params)
+            throws Exception {
+        String encoding = "UTF-8";
+        if (requestUrl.contains("nlp")) {
+            encoding = "GBK";
+        }
+        return HttpUtil.post(requestUrl, accessToken, contentType, params, encoding);
+    }
+
+    public static String post(String requestUrl, String accessToken, String contentType, String params, String encoding)
+            throws Exception {
+        String url = requestUrl + "?access_token=" + accessToken;
+        return HttpUtil.postGeneralUrl(url, contentType, params, encoding);
+    }
+
+    public static String postGeneralUrl(String generalUrl, String contentType, String params, String encoding)
+            throws Exception {
+        URL url = new URL(generalUrl);
+        // 打开和URL之间的连接
+        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+        connection.setRequestMethod("POST");
+        // 设置通用的请求属性
+        connection.setRequestProperty("Content-Type", contentType);
+        connection.setRequestProperty("Connection", "Keep-Alive");
+        connection.setUseCaches(false);
+        connection.setDoOutput(true);
+        connection.setDoInput(true);
+
+        // 得到请求的输出流对象
+        DataOutputStream out = new DataOutputStream(connection.getOutputStream());
+        out.write(params.getBytes(encoding));
+        out.flush();
+        out.close();
+
+        // 建立实际的连接
+        connection.connect();
+        // 获取所有响应头字段
+        Map<String, List<String>> headers = connection.getHeaderFields();
+        // 遍历所有的响应头字段
+        for (String key : headers.keySet()) {
+            System.err.println(key + "--->" + headers.get(key));
+        }
+        // 定义 BufferedReader输入流来读取URL的响应
+        BufferedReader in = null;
+        in = new BufferedReader(
+                new InputStreamReader(connection.getInputStream(), encoding));
+        String result = "";
+        String getLine;
+        while ((getLine = in.readLine()) != null) {
+            result += getLine;
+        }
+        in.close();
+        System.err.println("result:" + result);
+        return result;
+    }
+}

+ 292 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/ImageCompare.java

@@ -0,0 +1,292 @@
+package com.attendance.util;
+
+import org.opencv.core.Point;
+import org.opencv.core.*;
+import org.opencv.imgcodecs.Imgcodecs;
+import org.opencv.imgproc.Imgproc;
+import org.opencv.utils.Converters;
+
+import javax.imageio.ImageIO;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.*;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ImageCompare {
+    static {
+        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
+        //注意程序运行的时候需要在VM option添加该行 指明opencv的dll文件所在路径
+        //-Djava.library.path=$PROJECT_DIR$\opencv\x64
+    }
+    private boolean compareResult = false;
+    private String mark = "_compareResult";
+    /**
+     * 通过两张图对比,判断是否是游戏电影类型
+     * @param imagePath1 图片1的路径
+     * @param imagePath2 图片2的路径
+     */
+    public boolean isMoviePlay(String imagePath1, String imagePath2)
+    {
+        Mat mat1 = readMat(imagePath1);
+        Mat mat2 = readMat(imagePath2);
+        mat1 = Imgcodecs.imdecode(mat1, Imgcodecs.IMREAD_UNCHANGED);
+        mat2 = Imgcodecs.imdecode(mat2, Imgcodecs.IMREAD_UNCHANGED);
+        if(mat1.cols() == 0 || mat2.cols() == 0 || mat1.rows() == 0 || mat2.rows() == 0)
+        {
+            System.out.println("图片文件路径异常,获取的图片大小为0,无法读取");
+            return false;
+        }
+        if(mat1.cols() != mat2.cols() || mat1.rows() != mat2.rows())
+        {
+            System.out.println("两张图片大小不同,无法比较");
+            return false;
+        }
+        mat1.convertTo(mat1, CvType.CV_8UC1);
+        mat2.convertTo(mat2, CvType.CV_8UC1);
+        Mat mat1_gray = new Mat();
+        Imgproc.cvtColor(mat1, mat1_gray, Imgproc.COLOR_BGR2GRAY);
+        Mat mat2_gray = new Mat();
+        Imgproc.cvtColor(mat2, mat2_gray, Imgproc.COLOR_BGR2GRAY);
+        mat1_gray.convertTo(mat1_gray, CvType.CV_32F);
+        mat2_gray.convertTo(mat2_gray, CvType.CV_32F);
+        double result = Imgproc.compareHist(mat1_gray, mat2_gray, Imgproc.CV_COMP_CORREL);
+        if(result == 1)
+        {
+            return false;
+        }
+//        System.out.println("相似度数值为:"+result);
+        Mat mat_result = new Mat();
+        //计算两个灰度图的绝对差值,并输出到一个Mat对象中
+        Core.absdiff(mat1_gray, mat2_gray, mat_result);
+        //将灰度图按照阈值进行绝对值化
+        mat_result.convertTo(mat_result, CvType.CV_8UC1);
+        List<MatOfPoint> mat2_list = new ArrayList<MatOfPoint>();
+        Mat mat2_hi = new Mat();
+        //寻找轮廓图
+        Imgproc.findContours(mat_result, mat2_list, mat2_hi, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
+        Mat mat_result1 = mat1;
+        Mat mat_result2 = mat2;
+        //使用红色标记不同点
+        System.out.println(mat2_list.size());
+        List<Point> allPoints = new ArrayList<Point>();
+        //按照目标区域的面积大小, 长宽不低于480*480
+        Point leftTop = null;
+        Point rightTop = null;
+        Point leftBottom = null;
+        Point rightBottom = null;
+        for (MatOfPoint matOfPoint : mat2_list)
+        {
+            Rect rect = Imgproc.boundingRect(matOfPoint);
+            if ((rect.width >= 640 && rect.height >= 480) || (rect.width >= 480 && rect.height >= 640)) {
+//                System.out.println("位置==["+rect.x +","+ rect.y+"], 大小=" +rect.width+"x"+rect.height+", 包含点=" + matOfPoint.toList().size());
+                Imgproc.rectangle(mat_result1, rect.tl(), rect.br(), new Scalar(0, 0, 255),2);
+                Imgproc.rectangle(mat_result2, rect.tl(), rect.br(), new Scalar(0, 0, 255),2);
+                allPoints = matOfPoint.toList();
+                leftTop = new Point(rect.x, rect.y);
+                rightTop = new Point(rect.x + rect.width -1, rect.y);
+                leftBottom = new Point(rect.x, rect.y + rect.height -1);
+                rightBottom = new Point(rect.x + rect.width -1, rect.y + rect.height -1);
+                break;
+            }
+        }
+        for (Point p : allPoints) {
+            Imgproc.drawMarker(mat_result1, p, new Scalar(0, 255, 0),2);
+        }
+        //找到四个顶点
+        System.out.println("allPoints size=="+allPoints.size());
+        if (allPoints.size() == 0) {
+            return false;
+        }
+        System.out.println(leftTop);
+        System.out.println(rightTop);
+        System.out.println(leftBottom);
+        System.out.println(rightBottom);
+        //统计在矩形边上的点数量
+        int hitCount = 0;
+        for (Point p : allPoints) {
+            if ((Math.abs(p.x - leftTop.x) <= 6 || Math.abs(p.y - leftTop.y) <= 6)
+                    || (Math.abs(p.x - rightBottom.x) <= 6 || Math.abs(p.y - rightBottom.y) <= 6)) {
+                hitCount++;
+            }
+        }
+        System.out.println("hitCount="+hitCount+", percent="+(100*hitCount/allPoints.size())+"%");
+        int percent = (100*hitCount/allPoints.size());
+        //分析占比
+        if (percent > 80) {
+            return true;//
+        } else {
+            return false;
+        }
+    }
+
+    private void writeImage(Mat mat, String outPutFile)
+    {
+        MatOfByte matOfByte = new MatOfByte();
+        Imgcodecs.imencode(".png", mat, matOfByte);
+        byte[] byteArray = matOfByte.toArray();
+        BufferedImage bufImage = null;
+        try {
+            InputStream in = new ByteArrayInputStream(byteArray);
+            bufImage = ImageIO.read(in);
+            ImageIO.write(bufImage, "png", new File(outPutFile));
+        } catch (IOException | HeadlessException e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    private String getFileName(String filePath)
+    {
+        File f = new File(filePath);
+        return f.getName();
+    }
+
+    private String getParentDir(String filePath)
+    {
+        File f = new File(filePath);
+        return f.getParent();
+    }
+
+    private Mat readMat(String filePath)
+    {
+        try {
+            File file = new File(filePath);
+            FileInputStream inputStream = new FileInputStream(filePath);
+            byte[] byt = new byte[(int) file.length()];
+            int read = inputStream.read(byt);
+            List<Byte> bs = convert(byt);
+            Mat mat1 = Converters.vector_char_to_Mat(bs);
+            return mat1;
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return new Mat();
+    }
+
+    private List<Byte> convert(byte[] byt)
+    {
+        List<Byte> bs = new ArrayList<Byte>();
+        for (int i = 0; i < byt.length; i++)
+        {
+            bs.add(i, byt[i]);
+        }
+        return bs;
+    }
+
+    public boolean test(String imagePath1, String imagePath2)
+    {
+        Mat mat1 = readMat(imagePath1);
+        Mat mat2 = readMat(imagePath2);
+        mat1 = Imgcodecs.imdecode(mat1, Imgcodecs.IMREAD_UNCHANGED);
+        mat2 = Imgcodecs.imdecode(mat2, Imgcodecs.IMREAD_UNCHANGED);
+        /*Mat mat1 = Imgcodecs.imread(imagePath1, Imgcodecs.IMREAD_UNCHANGED);
+        Mat mat2 = Imgcodecs.imread(imagePath2, Imgcodecs.IMREAD_UNCHANGED);*/
+        if(mat1.cols() == 0 || mat2.cols() == 0 || mat1.rows() == 0 || mat2.rows() == 0)
+        {
+            System.out.println("图片文件路径异常,获取的图片大小为0,无法读取");
+            return false;
+        }
+        if(mat1.cols() != mat2.cols() || mat1.rows() != mat2.rows())
+        {
+            System.out.println("两张图片大小不同,无法比较");
+            return false;
+        }
+        mat1.convertTo(mat1, CvType.CV_8UC1);
+        mat2.convertTo(mat2, CvType.CV_8UC1);
+        Mat mat1_gray = new Mat();
+        Imgproc.cvtColor(mat1, mat1_gray, Imgproc.COLOR_BGR2GRAY);
+        Mat mat2_gray = new Mat();
+        Imgproc.cvtColor(mat2, mat2_gray, Imgproc.COLOR_BGR2GRAY);
+        mat1_gray.convertTo(mat1_gray, CvType.CV_32F);
+        mat2_gray.convertTo(mat2_gray, CvType.CV_32F);
+        double result = Imgproc.compareHist(mat1_gray, mat2_gray, Imgproc.CV_COMP_CORREL);
+        if(result == 1)
+        {
+            compareResult = true;//此处结果为1则为完全相同
+            return false;
+        }
+        System.out.println("相似度数值为:"+result);
+        Mat mat_result = new Mat();
+        //计算两个灰度图的绝对差值,并输出到一个Mat对象中
+        Core.absdiff(mat1_gray, mat2_gray, mat_result);
+        //将灰度图按照阈值进行绝对值化
+        mat_result.convertTo(mat_result, CvType.CV_8UC1);
+        List<MatOfPoint> mat2_list = new ArrayList<MatOfPoint>();
+        Mat mat2_hi = new Mat();
+        //寻找轮廓图
+        Imgproc.findContours(mat_result, mat2_list, mat2_hi, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
+        Mat mat_result1 = mat1;
+        Mat mat_result2 = mat2;
+        //使用红色标记不同点
+        System.out.println(mat2_list.size());
+        List<Point> allPoints = new ArrayList<Point>();
+        //按照目标区域的面积大小, 长宽不低于480*480
+        Point leftTop = null;
+        Point rightTop = null;
+        Point leftBottom = null;
+        Point rightBottom = null;
+        for (MatOfPoint matOfPoint : mat2_list)
+        {
+            Rect rect = Imgproc.boundingRect(matOfPoint);
+            if (rect.width >= 280 && rect.height >= 280) {
+                System.out.println("位置==["+rect.x +","+ rect.y+"], 大小=" +rect.width+"x"+rect.height+", 包含点=" + matOfPoint.toList().size());
+                Imgproc.rectangle(mat_result1, rect.tl(), rect.br(), new Scalar(0, 0, 255),2);
+                Imgproc.rectangle(mat_result2, rect.tl(), rect.br(), new Scalar(0, 0, 255),2);
+                allPoints = matOfPoint.toList();
+                leftTop = new Point(rect.x, rect.y);
+                rightTop = new Point(rect.x + rect.width -1, rect.y);
+                leftBottom = new Point(rect.x, rect.y + rect.height -1);
+                rightBottom = new Point(rect.x + rect.width -1, rect.y + rect.height -1);
+                break;
+            }
+        }
+        for (Point p : allPoints) {
+            Imgproc.drawMarker(mat_result1, p, new Scalar(0, 255, 0),2);
+        }
+        String fileName1 = getFileName(imagePath1);
+        String targetPath1 = getParentDir(imagePath2)+File.separator+fileName1.replace(".", mark+".");
+        String fileName2 = getFileName(imagePath2);
+        String targetPath2 = getParentDir(imagePath2)+File.separator+fileName2.replace(".", mark+".");
+        System.out.println(targetPath1);
+        System.out.println(targetPath2);
+        writeImage(mat_result1, targetPath1);
+        writeImage(mat_result2, targetPath2);
+        //找到四个顶点
+        System.out.println("allPoints size=="+allPoints.size());
+        System.out.println(leftTop);
+        System.out.println(rightTop);
+        System.out.println(leftBottom);
+        System.out.println(rightBottom);
+        //检测其他点,是否都在矩形边上
+        int hitCount = 0;
+        for (Point p : allPoints) {
+            if ((Math.abs(p.x - leftTop.x) <= 6 || Math.abs(p.y - leftTop.y) <= 6)
+                    || (Math.abs(p.x - rightBottom.x) <= 6 || Math.abs(p.y - rightBottom.y) <= 6)) {
+                hitCount++;
+            } else {
+//                System.out.println("失效点:"+p.x + "," + p.y);
+            }
+        }
+        System.out.println("hitCount="+hitCount+", percent="+(100*hitCount/allPoints.size())+"%");
+        int percent = (100*hitCount/allPoints.size());
+
+        if (percent > 80) {
+            return true;//
+        } else {
+            return false;
+        }
+    }
+
+    public static void main(String[] args) {
+        String img1 = "C:\\Users\\seya\\Desktop\\e.jpg";
+        String img2 = "C:\\Users\\seya\\Desktop\\d.jpg";
+        ImageCompare ip = new ImageCompare();
+        System.out.println("是电影娱乐吗?"+ip.test(img1, img2));
+    }
+
+
+}
+

+ 126 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/ImageReconizeUtil.java

@@ -0,0 +1,126 @@
+package com.attendance.util;
+
+import org.opencv.core.Core;
+import org.opencv.core.CvType;
+import org.opencv.core.Mat;
+import org.opencv.core.Rect;
+import org.opencv.imgcodecs.Imgcodecs;
+import org.opencv.imgproc.Imgproc;
+
+import java.io.File;
+
+import static org.opencv.imgproc.Imgproc.TM_CCOEFF_NORMED;
+
+public class ImageReconizeUtil {
+//    public static final double YUZHI_HIGH = 2*Math.pow(0.1, 11);
+//    public static final double YUZHI_NORMAL = 1*Math.pow(0.1, 10);
+    public static final double YUZHI = 0.89;
+    static {
+        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
+        //注意程序运行的时候需要在VM option添加该行 指明opencv的dll文件所在路径
+        //-Djava.library.path=$PROJECT_DIR$\opencv\x64
+    }
+    public static void main(String[] args) {
+        boolean match = isWholeTemplateMatch("C:\\Users\\seya\\Desktop\\wjw.jpg",
+                "C:\\picrecongnize\\im\\wechat.jpg");
+
+    }
+
+    public static boolean isTemplateMatch(String sourcePic, String targetPic) {
+        double matchVal = templete(TM_CCOEFF_NORMED, sourcePic, targetPic,true);
+        if (matchVal >= YUZHI) {
+            System.out.println("找到啦");
+            return true;
+        } else {
+            System.out.println("没匹配上");
+            return false;
+        }
+    }
+
+    public static boolean isWholeTemplateMatch(String sourcePic, String targetPic) {
+        double matchVal = templete(Imgproc.TM_CCOEFF_NORMED, sourcePic, targetPic, false);
+        if (matchVal >= YUZHI) {
+            System.out.println("找到啦"+targetPic);
+            return true;
+        } else {
+            System.out.println("没匹配上");
+            return false;
+        }
+    }
+
+    /**
+     * OpenCV-4.1.0 模板匹配
+     * <table border="1" cellpadding="8">
+     * <tr><th>输入参数</th><th>参数解释</th></tr>
+     * <tr><td align="left">TM_SQDIFF是平方差匹配、TM_SQDIFF_NORMED是标准平方差匹配</td><td>利用平方差来进行匹配,最好匹配为0.匹配越差,匹配值越大。</td></tr>
+     * <tr><td align="left">TM_CCORR是相关性匹配、TM_CCORR_NORMED是标准相关性匹配</td><td>采用模板和图像间的乘法操作,数越大表示匹配程度较高, 0表示最坏的匹配效果。</td></tr>
+     * <tr><td align="left">TM_CCOEFF是相关性系数匹配、TM_CCOEFF_NORMED是标准相关性系数匹配</td><td>将模版对其均值的相对值与图像对其均值的相关值进行匹配,1表示完美匹配,-1表示糟糕的匹配,0表示没有任何相关性(随机序列)。</td></tr>
+     * <tr><td colspan="2">随着从简单的测量(平方差)到更复杂的测量(相关系数),我们可获得越来越准确的匹配(同时也意味着越来越大的计算代价)。</td></tr>
+     * <tr><td colspan="2">相关性是越接近1越大越好,平方差是越小越好,所以TM_SQDIFF在使用时和其他的是有所区别的。</td></tr>
+     * <tr><td colspan="2">模板匹配结果Mat要是32位的。</td></tr>
+     * </table>
+     * @return: void
+     * @date: 2019年5月7日12:16:55
+     */
+    public static double templete(int method, String inFile, String templateFile, boolean cutMatch) {
+        // 1 获取待匹配图片
+//        System.out.println("sourcePic="+sourcePic+", targetPic="+targetPic);
+        Mat srcMat = Imgcodecs.imread(inFile);
+        if (cutMatch) {
+            //先裁减,左上角1/4的截图,提高比对速度。
+            Rect rect = new Rect(0,0,srcMat.width()/4, srcMat.height()/4);
+            Mat subMat = new Mat(srcMat, rect);
+            subMat.copyTo(srcMat);
+        }
+
+        //将rgb灰化处理
+//        Imgproc.cvtColor(templete, templete,Imgproc.COLOR_BGR2GRAY);
+//
+        // 2 获取匹配模板
+        Mat demo = Imgcodecs.imread(templateFile);
+//        Imgproc.cvtColor(demo, demo,Imgproc.COLOR_BGR2GRAY);
+
+        Core.MinMaxLocResult mmr = getLocResult(method, srcMat, demo);
+        // 7 绘制匹配到的结果
+        double x,y;
+        double matchVal = 0;
+        if (method== Imgproc.TM_SQDIFF_NORMED || method== Imgproc.TM_SQDIFF) {
+            x = mmr.minLoc.x;
+            y = mmr.minLoc.y;
+            matchVal = mmr.minVal;
+        } else {
+            x = mmr.maxLoc.x;
+            y = mmr.maxLoc.y;
+            matchVal = mmr.maxVal;
+        }
+
+        System.out.println("匹配度=="+matchVal+", target="+templateFile.substring(templateFile.lastIndexOf(File.separator)-10)+", inFile="+inFile.substring(inFile.lastIndexOf(File.separator)));
+        System.out.println("x="+x+", y=" + y);
+        //我们匹配的图像在左上角,考虑到用户可能拖动窗口,但是不应该偏差很大。 这里增加判断标准:坐标处在左上方。
+
+//        if (matchVal >= YUZHI) {
+//            Imgproc.rectangle(srcMat,new Point(x,y),new Point(x+demo.cols(),y+demo.rows()),new Scalar( 0, 0, 255),2,Imgproc.LINE_AA);
+//            Imgproc.putText(srcMat,"Match Success",new Point(x,y),Imgproc.FONT_HERSHEY_SCRIPT_COMPLEX, 1.0, new Scalar(0, 255, 0),1,Imgproc.LINE_AA);
+//            // 8 显示结果
+//            HighGui.imshow("模板匹配", srcMat);
+//            HighGui.waitKey(0);
+//        }
+
+        return matchVal;
+    }
+
+    private static Core.MinMaxLocResult getLocResult(int method, Mat templete, Mat demo) {
+        int width=templete.cols()-demo.cols()+1;
+        int height=templete.rows()-demo.rows()+1;
+        // 3 创建32位模板匹配结果Mat
+        Mat result=new Mat(width,height, CvType.CV_32FC1);
+        // 4 调用 模板匹配函数
+        Imgproc.matchTemplate(templete, demo, result, method);
+        // 5 归一化
+//        Core.normalize(result, result,0, 1, Core.NORM_MINMAX, -1, new Mat());
+        // 6 获取模板匹配结果
+        Core.MinMaxLocResult mmr = Core.minMaxLoc(result);
+        return mmr;
+    }
+
+}

+ 157 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/ListUtil.java

@@ -0,0 +1,157 @@
+package com.attendance.util;
+
+import java.lang.reflect.Field;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * Author: 吴涛涛 cuiyi@itany.com
+ * Date : 2019 - 07 - 25 16:56
+ * Description:<描述>处理字符串转成集合的
+ * Version: 1.0
+ */
+
+public class ListUtil {
+	/**
+	 * 
+	 * @param idStr 1,2,3,4,5字符串
+	 * @return List<Long>
+	 */
+	public static List<Long> convertIdsArrayToList(String idStr) {
+		String[] array = idStr.split(",");
+		List<Long> ids = new ArrayList<Long>();
+		for (String a : array) {
+			if (a != null && a.length() > 0) {
+				ids.add(Long.valueOf(a));
+			}
+		}
+		return ids;
+	}
+
+	/**
+	 * Long
+	 * @param idStr  1,2,3,4,5字符串
+	 * @return
+	 */
+	public static List<String> convertLongIdsArrayToList(String idStr) {
+		String[] array = idStr.split(",");
+		List<String> ids = new ArrayList<String>();
+		for (String a : array) {
+			if (a != null && a.length() > 0) {
+				ids.add(a);
+			}
+		}
+		return ids;
+	}
+	/**
+	 * Long
+	 * @param idStr  1,2,3,4,5字符串
+	 * @return
+	 */
+	public static List<Integer> convertIntegerIdsArrayToList(String idStr) {
+		String[] array = idStr.split(",");
+		List<Integer> ids = new ArrayList<Integer>();
+		for (String a : array) {
+			if (a != null && a.length() > 0) {
+				ids.add(Integer.parseInt(a));
+			}
+		}
+		return ids;
+	}
+
+	public static List<Integer> extractIdFromList(List object, String key) {
+		List<Integer> list = new ArrayList<Integer>();
+        for (Object obj : object) {
+            // 得到类对象
+            Class userCla = (Class) obj.getClass();
+            /* 得到类中的所有属性集合 */
+            Field[] fs = userCla.getDeclaredFields();
+            for (int i = 0; i < fs.length; i++) {
+                Field f = fs[i];
+                f.setAccessible(true); // 设置些属性是可以访问的
+                try {
+                    if (f.getName().equals(key)) {
+                        list.add((Integer)f.get(obj));
+                    }
+                } catch (IllegalArgumentException e) {
+                    e.printStackTrace();
+                } catch (IllegalAccessException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        return list;
+	}
+	
+	
+	public static List<String> extractNameFromList(List object, String key) {
+		List<String> list = new ArrayList<String>();
+		for (Object obj : object) {
+			// 得到类对象
+			Class userCla = (Class) obj.getClass();
+			/* 得到类中的所有属性集合 */
+			Field[] fs = userCla.getDeclaredFields();
+			for (int i = 0; i < fs.length; i++) {
+				Field f = fs[i];
+				f.setAccessible(true); // 设置些属性是可以访问的
+				try {
+					if (f.getName().equals(key)) {
+						list.add((String)f.get(obj));
+					}
+				} catch (IllegalArgumentException e) {
+					e.printStackTrace();
+				} catch (IllegalAccessException e) {
+					e.printStackTrace();
+				}
+			}
+		}
+		return list;
+	}
+	
+	public static List<Integer> addList(String ids,List<Integer> idList){
+		String[] joinStr = ids.split(",");
+		boolean isCf = false;
+		for(String id : joinStr){
+			if (id != null && id.length() > 0) {
+				for(int i = 0;i<idList.size();i++){
+					if(Integer.valueOf(id).intValue() != idList.get(i).intValue()){
+						isCf = false;
+					}else{
+						isCf = true;
+						break;
+					}
+				}
+				if(!isCf){
+					idList.add(Integer.valueOf(id));
+				}
+			}
+		}
+		
+		return idList;
+	}
+	
+	//去重
+	public static List removeDuplicateData(List list) {
+		HashSet set = new HashSet();
+		set.addAll(list);
+		list.clear();
+		list.addAll(set);
+		return list;
+	}
+	
+	public static void main(String[] args) {
+		DecimalFormat df = new DecimalFormat("#0.0");
+		double a = 100.483258435;
+		System.out.println(df.format(a));
+	}
+
+	public static List<Integer> fromIntegers(int[] data) {
+		List<Integer> ids = new ArrayList<>();
+		for (int i : data) {
+			ids.add(i);
+		}
+		return ids;
+	}
+}

+ 133 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/MD5Util.java

@@ -0,0 +1,133 @@
+package com.attendance.util;
+
+import org.springframework.util.DigestUtils;
+
+import java.security.MessageDigest;
+import java.text.ParseException;
+
+/**
+ * Author: 吴涛涛 cuiyi@itany.com
+ * Date : 2019 - 07 - 25 16:56
+ * Description:<描述>MD5加密工具
+ * Version: 1.0
+ */
+public class MD5Util {
+
+    private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",
+            "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
+
+    public static String getPassword(String password) {
+        return DigestUtils.md5DigestAsHex(password.getBytes());
+    }
+
+    public static void main(String[] args) throws ParseException {
+        System.out.println(getPassword("工时管家"));
+
+    }
+
+//    public void zipDemo() {
+//        //需要压缩的文件--包括文件地址和文件名
+//        String[] path = {"D:\\666.jpg", "D:\\777.jpg"};
+//        // 要生成的压缩文件地址和文件名称
+//        String desPath = "D:\\new.zip";
+//        File zipFile = new File(desPath);
+//        ZipOutputStream zipStream = null;
+//        FileInputStream zipSource = null;
+//        BufferedInputStream bufferStream = null;
+//        try {
+//            //构造最终压缩包的输出流
+//            zipStream = new ZipOutputStream(new FileOutputStream(zipFile));
+//            for (int i = 0; i < path.length; i++) {
+//                File file = new File(path[i]);
+//                //将需要压缩的文件格式化为输入流
+//                zipSource = new FileInputStream(file);
+//                //压缩条目不是具体独立的文件,而是压缩包文件列表中的列表项,称为条目,就像索引一样
+//                ZipEntry zipEntry = new ZipEntry(i + "2222.jpg");//"2222.jpg"是添加到压缩包里的源文件的名字加i是防止名字相同出错
+//                //定位该压缩条目位置,开始写入文件到压缩包中
+//                zipStream.putNextEntry(zipEntry);
+//                //输入缓冲流
+//                bufferStream = new BufferedInputStream(zipSource, 1024 * 10);
+//                int read = 0;
+//                //创建读写缓冲区
+//                byte[] buf = new byte[1024 * 10];
+//                while ((read = bufferStream.read(buf, 0, 1024 * 10)) != -1) {
+//                    zipStream.write(buf, 0, read);
+//                }
+//            }
+//
+//        } catch (Exception e) {
+//            e.printStackTrace();
+//        } finally {
+//            //关闭流
+//            try {
+//                if (null != bufferStream) bufferStream.close();
+//                if (null != zipStream) zipStream.close();
+//                if (null != zipSource) zipSource.close();
+//            } catch (IOException e) {
+//                e.printStackTrace();
+//            }
+//        }
+//    }
+//
+//    public static void zip4jDemo(){
+//// 生成的压缩文件
+//        ZipFile zipFile = null;
+//        try {
+//            zipFile = new ZipFile("D:\\aa.zip");
+//
+//        ZipParameters parameters = new ZipParameters();
+//        // 压缩方式
+//        parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE);
+//        // 压缩级别
+//        parameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL);
+//        // 要打包的文件夹
+//        File currentFile = new File("D:\\666");
+//        File[] fs = currentFile.listFiles();
+//        // 遍历test文件夹下所有的文件、文件夹
+//        for (File f : fs) {
+//            if (f.isDirectory()) {
+//                zipFile.addFolder(f.getPath(), parameters);
+//            } else {
+//                zipFile.addFile(f, parameters);
+//            }
+//        }
+//            zipFile.addFolder("D:\\666", parameters);
+//        } catch (Exception e) {
+//            e.printStackTrace();
+//        }
+//    }
+
+
+    private static String byteArrayToHexString(byte b[]) {
+        StringBuffer resultSb = new StringBuffer();
+        for (int i = 0; i < b.length; i++)
+            resultSb.append(byteToHexString(b[i]));
+
+        return resultSb.toString();
+    }
+
+    private static String byteToHexString(byte b) {
+        int n = b;
+        if (n < 0)
+            n += 256;
+        int d1 = n / 16;
+        int d2 = n % 16;
+        return hexDigits[d1] + hexDigits[d2];
+    }
+
+    public static String MD5Encode(String origin, String charsetname) {
+        String resultString = null;
+        try {
+            resultString = new String(origin);
+            MessageDigest md = MessageDigest.getInstance("MD5");
+            if (charsetname == null || "".equals(charsetname))
+                resultString = byteArrayToHexString(md.digest(resultString
+                        .getBytes()));
+            else
+                resultString = byteArrayToHexString(md.digest(resultString
+                        .getBytes(charsetname)));
+        } catch (Exception exception) {
+        }
+        return resultString;
+    }
+}

+ 18 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/MathUtil.java

@@ -0,0 +1,18 @@
+package com.attendance.util;
+
+import java.util.regex.Pattern;
+
+public class MathUtil {
+    public static boolean isIntegerOrDigital(String str) {
+        if (str == null || str.trim().length() == 0) {
+            return false;
+        }
+        Pattern pattern = Pattern.compile("^[-\\+]?[\\d]*[\\.]?[\\d]*$");
+        return pattern.matcher(str).matches();
+    }
+
+    public static void main(String[] args) {
+        String str = "";
+        System.out.println(isIntegerOrDigital(str));
+    }
+}

+ 28 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/MessageUtils.java

@@ -0,0 +1,28 @@
+package com.attendance.util;
+
+
+import org.springframework.context.MessageSource;
+import org.springframework.context.i18n.LocaleContextHolder;
+
+import java.util.Locale;
+
+/**
+ * 获取i18n资源文件
+ *
+ */
+public class MessageUtils {
+
+    /**
+     * 根据消息键和参数 获取消息 委托给spring messageSource
+     * @param code 消息键
+     * @param args 参数
+     * @return 获取国际化翻译值
+     */
+    public static String message(String code, Object... args) {
+        MessageSource messageSource = SpringUtils.getBean(MessageSource.class);
+        Locale locale = LocaleContextHolder.getLocale();
+//        System.out.println("本地为:" +locale.getDisplayName()+", code="+code);
+        return messageSource.getMessage(code, args, locale);
+    }
+
+}

+ 61 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/PageUtil.java

@@ -0,0 +1,61 @@
+package com.attendance.util;
+
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Author: 吴涛涛 cuiyi@itany.com
+ * Date : 2019 - 07 - 27 9:25
+ * Description:<描述> 模仿pagehelper参数的分页封装类
+ * Version: 1.0
+ */
+@Data
+public class PageUtil<T> {
+    private Integer pageNum = 1;
+    private Integer total;
+    private Integer pageSize = 10;
+    private Integer pages;
+    private List<T> list = new ArrayList<>();
+
+    public Integer getPageNum() {
+        return pageNum;
+    }
+
+    public void setPageNum(Integer pageNum) {
+        this.pageNum = pageNum;
+    }
+
+    public Integer getTotal() {
+        return total;
+    }
+
+    public void setTotal(Integer total) {
+        this.total = total;
+    }
+
+    public Integer getPageSize() {
+        return pageSize;
+    }
+
+    public void setPageSize(Integer pageSize) {
+        this.pageSize = pageSize;
+    }
+
+    public Integer getPages() {
+        return pages;
+    }
+
+    public void setPages(Integer total) {
+        this.pages = total % this.pageSize == 0 ? total / this.pageSize : total / this.pageSize + 1;;
+    }
+
+    public List<T> getList() {
+        return list;
+    }
+
+    public void setList(List<T> list) {
+        this.list = list;
+    }
+}

文件差异内容过多而无法显示
+ 217 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/ProcessUtil.java


+ 79 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/Sha1Util.java

@@ -0,0 +1,79 @@
+package com.attendance.util;
+
+
+import java.security.MessageDigest;
+import java.util.*;
+
+/*
+'============================================================================
+'api说明:
+'createSHA1Sign创建签名SHA1
+'getSha1()Sha1签名
+'============================================================================
+'*/
+public class Sha1Util {
+
+    public static void main(String[] args) {
+//        String str = "jsapi_ticket=kgt8ON7yVITDhtdwci0qecaNzS34qhbQACy88uRm_S0XvT3hcWiBONbLzKnA5o5uPukt2sA3a8bBfETg4TyaQg&noncestr=10907813b97e249163587e6246612e21&timestamp=1516590573&url=http://www.dzjy.com/payHtml.html?num=1&code=001EjC0a2q6qoO0fn2Z92E6D0a2EjC0D&state=1";
+//        String sign = getSha1(str);
+//        System.out.println(sign);
+        String string = "10/30 下午~04/26 上午";
+        String[] arr = string.split(" |\\~");
+        for (String s : arr) {
+            System.out.println(s);
+        }
+
+    }
+
+    public static String getNonceStr() {
+        Random random = new Random();
+        return MD5Util.MD5Encode(String.valueOf(random.nextInt(10000)), "UTF-8");
+    }
+    public static String getTimeStamp() {
+        return String.valueOf(System.currentTimeMillis() / 1000);
+    }
+
+    //创建签名SHA1
+    public static String createSHA1Sign(SortedMap<String, String> signParams) throws Exception {
+        StringBuffer sb = new StringBuffer();
+        Set es = signParams.entrySet();
+        Iterator it = es.iterator();
+        while (it.hasNext()) {
+            Map.Entry entry = (Map.Entry) it.next();
+            String k = (String) entry.getKey();
+            String v = (String) entry.getValue();
+            sb.append(k + "=" + v + "&");
+            //要采用URLENCODER的原始值!
+        }
+        String params = sb.substring(0, sb.lastIndexOf("&"));
+//        System.out.println("sha1之前:" + params);
+//        System.out.println("SHA1签名为:"+getSha1(params));
+        return getSha1(params);
+    }
+    //Sha1签名
+    public static String getSha1(String str) {
+        if (str == null || str.length() == 0) {
+            return null;
+        }
+        char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+                'a', 'b', 'c', 'd', 'e', 'f' };
+
+        try {
+            MessageDigest mdTemp = MessageDigest.getInstance("SHA1");
+            mdTemp.update(str.getBytes("UTF-8"));
+
+            byte[] md = mdTemp.digest();
+            int j = md.length;
+            char buf[] = new char[j * 2];
+            int k = 0;
+            for (int i = 0; i < j; i++) {
+                byte byte0 = md[i];
+                buf[k++] = hexDigits[byte0 >>> 4 & 0xf];
+                buf[k++] = hexDigits[byte0 & 0xf];
+            }
+            return new String(buf);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+}

+ 51 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/SmsUtil.java

@@ -0,0 +1,51 @@
+package com.attendance.util;
+
+import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
+import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
+import com.aliyun.teaopenapi.models.Config;
+
+public class SmsUtil {
+    private static String accessKeyId = "LTAI5tQ2uwLE9T6Zrq1Jh3mr";
+    private static String accessKeySecret = "eGUxd8ipnLLCF6XqurKrGiMoyqC69u";
+    /**
+     * 使用AK&SK初始化账号Client
+     * @param accessKeyId
+     * @param accessKeySecret
+     * @return Client
+     * @throws Exception
+     */
+    public static com.aliyun.dysmsapi20170525.Client createClient(String accessKeyId, String accessKeySecret) throws Exception {
+        Config config = new Config()
+                // 您的AccessKey ID
+                .setAccessKeyId(accessKeyId)
+                // 您的AccessKey Secret
+                .setAccessKeySecret(accessKeySecret);
+        // 访问的域名
+        config.endpoint = "dysmsapi.aliyuncs.com";
+        return new com.aliyun.dysmsapi20170525.Client(config);
+    }
+
+    public static SendSmsResponse sendSms(String mobile, String code) {
+        com.aliyun.dysmsapi20170525.Client client = null;
+        try {
+            client = SmsUtil.createClient(accessKeyId, accessKeySecret);
+            System.out.println("发送短信");
+            SendSmsRequest sendSmsRequest = new SendSmsRequest()
+                    .setPhoneNumbers(mobile)
+                    .setSignName("火石闪信")
+                    .setTemplateCode("SMS_218575084")
+                    .setTemplateParam("{\"code\":\""+code+"\"}");
+            // 复制代码运行请自行打印 API 的返回值
+            SendSmsResponse sendSmsResponse = client.sendSms(sendSmsRequest);
+            System.out.println(sendSmsResponse.getBody().getCode()+":"+sendSmsResponse.getBody().message);
+            return sendSmsResponse;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    public static void main(String[] args_) throws Exception {
+        SmsUtil.sendSms("15895914665", "1234");
+    }
+}

+ 124 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/SnowFlake.java

@@ -0,0 +1,124 @@
+package com.attendance.util;
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.RuntimeMXBean;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.util.Enumeration;
+
+public class SnowFlake {
+    private final static long twepoch = 12888349746579L;
+    // 机器标识位数
+    private final static long workerIdBits = 5L;
+    // 数据中心标识位数
+    private final static long datacenterIdBits = 5L;
+
+    // 毫秒内自增位数
+    private final static long sequenceBits = 12L;
+    // 机器ID偏左移12位
+    private final static long workerIdShift = sequenceBits;
+    // 数据中心ID左移17位
+    private final static long datacenterIdShift = sequenceBits + workerIdBits;
+    // 时间毫秒左移22位
+    private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
+    //sequence掩码,确保sequnce不会超出上限
+    private final static long sequenceMask = -1L ^ (-1L << sequenceBits);
+    //上次时间戳
+    private static long lastTimestamp = -1L;
+    //序列
+    private long sequence = 0L;
+    //服务器ID
+    private long workerId = 1L;
+    private static long workerMask = -1L ^ (-1L << workerIdBits);
+    //进程编码
+    private long processId = 1L;
+    private static long processMask = -1L ^ (-1L << datacenterIdBits);
+
+    private static SnowFlake snowFlake = null;
+
+    static{
+        snowFlake = new SnowFlake();
+    }
+    public static synchronized long nextId(){
+        return snowFlake.getNextId();
+    }
+
+    private SnowFlake() {
+
+        //获取机器编码
+        this.workerId=this.getMachineNum();
+        //获取进程编码
+        RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
+        this.processId=Long.valueOf(runtimeMXBean.getName().split("@")[0]).longValue();
+
+        //避免编码超出最大值
+        this.workerId=workerId & workerMask;
+        this.processId=processId & processMask;
+    }
+
+    public synchronized long getNextId() {
+        //获取时间戳
+        long timestamp = timeGen();
+        //如果时间戳小于上次时间戳则报错
+        if (timestamp < lastTimestamp) {
+            try {
+                throw new Exception("Clock moved backwards.  Refusing to generate id for " + (lastTimestamp - timestamp) + " milliseconds");
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        //如果时间戳与上次时间戳相同
+        if (lastTimestamp == timestamp) {
+            // 当前毫秒内,则+1,与sequenceMask确保sequence不会超出上限
+            sequence = (sequence + 1) & sequenceMask;
+            if (sequence == 0) {
+                // 当前毫秒内计数满了,则等待下一秒
+                timestamp = tilNextMillis(lastTimestamp);
+            }
+        } else {
+            sequence = 0;
+        }
+        lastTimestamp = timestamp;
+        // ID偏移组合生成最终的ID,并返回ID
+        long nextId = ((timestamp - twepoch) << timestampLeftShift) | (processId << datacenterIdShift) | (workerId << workerIdShift) | sequence;
+        return nextId;
+    }
+
+    /**
+     * 再次获取时间戳直到获取的时间戳与现有的不同
+     * @param lastTimestamp
+     * @return 下一个时间戳
+     */
+    private long tilNextMillis(final long lastTimestamp) {
+        long timestamp = this.timeGen();
+        while (timestamp <= lastTimestamp) {
+            timestamp = this.timeGen();
+        }
+        return timestamp;
+    }
+
+    private long timeGen() {
+        return System.currentTimeMillis();
+    }
+
+    /**
+     * 获取机器编码
+     * @return
+     */
+    private long getMachineNum(){
+        long machinePiece;
+        StringBuilder sb = new StringBuilder();
+        Enumeration<NetworkInterface> e = null;
+        try {
+            e = NetworkInterface.getNetworkInterfaces();
+        } catch (SocketException e1) {
+            e1.printStackTrace();
+        }
+        while (e.hasMoreElements()) {
+            NetworkInterface ni = e.nextElement();
+            sb.append(ni.toString());
+        }
+        machinePiece = sb.toString().hashCode();
+        return machinePiece;
+    }
+}

+ 158 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/SplitDateUtil.java

@@ -0,0 +1,158 @@
+package com.attendance.util;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.time.*;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.TemporalAdjusters;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+
+public class SplitDateUtil {
+    /**
+     * 根据传入的参数,来对日期区间进行拆分,返回拆分后的日期List
+     * @param statisticsType
+     * @param startDate
+     * @param endDate
+     * @return
+     * @throws ParseException
+     * @author lihq 2019-6-24
+     * @editor
+     * @editcont
+     */
+    public static List<String> doDateByStatisticsType(String statisticsType,String startDate,String endDate) throws ParseException {
+        List<String> listWeekOrMonth = new ArrayList<String>();
+        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
+        Date sDate = dateFormat.parse(startDate);
+        Calendar sCalendar = Calendar.getInstance();
+        sCalendar.setFirstDayOfWeek(Calendar.MONDAY);
+        sCalendar.setTime(sDate);
+        Date eDate = dateFormat.parse(endDate);
+        Calendar eCalendar = Calendar.getInstance();
+        eCalendar.setFirstDayOfWeek(Calendar.MONDAY);
+        eCalendar.setTime(eDate);
+        boolean bool =true;
+        if(statisticsType.equals("week")){
+            while(sCalendar.getTime().getTime()<eCalendar.getTime().getTime()){
+                if(bool||sCalendar.get(Calendar.DAY_OF_WEEK)==2||sCalendar.get(Calendar.DAY_OF_WEEK)==1){
+                    listWeekOrMonth.add(dateFormat.format(sCalendar.getTime()));
+                    bool = false;
+                }
+                sCalendar.add(Calendar.DAY_OF_MONTH, 1);
+            }
+        }else{
+            while(sCalendar.getTime().getTime()<eCalendar.getTime().getTime()){
+                if(bool||sCalendar.get(Calendar.DAY_OF_MONTH)==1||sCalendar.get(Calendar.DAY_OF_MONTH)==sCalendar.getActualMaximum(Calendar.DAY_OF_MONTH)){
+                    listWeekOrMonth.add(dateFormat.format(sCalendar.getTime()));
+                    bool = false;
+                }
+                sCalendar.add(Calendar.DAY_OF_MONTH, 1);
+            }
+        }
+        listWeekOrMonth.add(dateFormat.format(eCalendar.getTime()));
+        if(listWeekOrMonth.size()%2!=0){
+            listWeekOrMonth.add(dateFormat.format(eCalendar.getTime()));
+        }
+        return listWeekOrMonth;
+    }
+
+    /**
+     * 获取当前日期是本月的第几周(通过本月有几个周三来判断)
+     * @param dateStr 日期(格式:yyyy-MM-dd)
+     * @return  第n周
+     * @throws Exception
+     */
+    public static String  getWeekValueByDate(String dateStr) throws Exception {
+            String monthNoAndWeekNo;
+            //获取当月的第一天
+            //获取月第一个周一,从当月第一天开始找
+            DateTimeFormatter df=DateTimeFormatter.ofPattern("yyyy-MM-dd");
+            LocalDate sourceTime=LocalDate.parse(dateStr,df);
+            LocalDateTime firstMondayOfMonth = getFirstMonday(sourceTime.atTime(LocalTime.MIN));
+
+            //比较当月的第一个星期一 < = 参数时间
+            if (firstMondayOfMonth.isBefore(sourceTime.atTime(LocalTime.MIN)) || firstMondayOfMonth.isEqual(sourceTime.atTime(LocalTime.MIN))) {
+                //当月第一个周一在当前时间之前 firstMondayOfMonth<=sourceTime
+                //计算两个时间间隔天数
+                int dayOfMonthFirstMonday = firstMondayOfMonth.getDayOfMonth();
+                int dayOfMonthSourceTime = sourceTime.getDayOfMonth();
+
+                int diffDays = dayOfMonthSourceTime - dayOfMonthFirstMonday;
+                //第几周weekNo
+                int weekNo = (diffDays / 7) + 1;
+                //月份
+                int monthNo = sourceTime.getMonth().getValue();
+
+                monthNoAndWeekNo = monthNo + "月份" + "第" + weekNo + "周";
+            } else {
+                //如果当月的第一个周一大于参数时间,则要计算到上个月份去
+                //获取上一个月的第一个周一
+                LocalDateTime lastMontLocalDateTime = sourceTime.atTime(LocalTime.MIN).minusMonths(1);
+                //上个月的第一天
+                //从上个月的第一天开始找周一
+                LocalDateTime firstMondayOfMonthLast = getFirstMonday(lastMontLocalDateTime);
+
+                //  计算两个时间间隔天数 (上月第一个周一 减去 当前时间)
+                Duration duration = Duration.between(firstMondayOfMonthLast, sourceTime.atTime(LocalTime.MIN));
+                long diffDays = duration.toDays(); //相差的天数
+                //第几周weekNo
+                long weekNo = (diffDays / 7) + 1;
+                //月份
+                int monthNo = firstMondayOfMonthLast.getMonth().getValue();//汉字版月份
+
+                monthNoAndWeekNo = monthNo + "月份" + "第" + weekNo + "周";
+            }
+
+            return monthNoAndWeekNo;
+    }
+
+
+    /**
+     *获取当月第一天
+     */
+    public static LocalDateTime getFirstLocalDayOfMonth(LocalDateTime localDateTime) {
+        return localDateTime.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
+    }
+
+    /**
+     *获取当月第一周  以第一个周一为准
+     */
+    private static LocalDateTime getFirstMonday(LocalDateTime sourceTime) {
+        LocalDateTime firstMondayOfMonth = getFirstLocalDayOfMonth(sourceTime);
+        for (int i = 0; i < 6; i++) {
+            DayOfWeek dayOfWeekTemp = firstMondayOfMonth.getDayOfWeek();
+            if (dayOfWeekTemp.equals(DayOfWeek.MONDAY)) {
+                break;
+            }
+            //往后推一天
+            firstMondayOfMonth = firstMondayOfMonth.plusDays(1);
+        }
+        return firstMondayOfMonth;
+    }
+
+
+    public static void main(String[] args) {
+        try {
+            List<String> list = doDateByStatisticsType("month", "2022-01-01", "2022-03-30");
+            for (int i = 0; i < list.size(); i++) {
+                String sDate = list.get(i);
+                String eDate = list.get(i + 1);
+                i++;
+                System.out.println(sDate+"-----"+eDate);
+                List<String> twoList = doDateByStatisticsType("week", sDate, eDate);
+                for (int i1 = 0; i1 < twoList.size(); i1++) {
+                    String sDate1 = twoList.get(i1);
+                    String eDate1 = twoList.get(i1 + 1);
+                    i1++;
+                    System.out.println(sDate1+"-----"+eDate1);
+                }
+            }
+        } catch (ParseException e) {
+            e.printStackTrace();
+        }
+    }
+}
+

+ 145 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/SpringUtils.java

@@ -0,0 +1,145 @@
+package com.attendance.util;
+
+import org.springframework.aop.framework.AopContext;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+
+/**
+ * spring工具类 方便在非spring管理环境中获取bean
+ *
+ * @author xq0136
+ */
+@Component
+public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware
+{
+    /** Spring应用上下文环境 */
+    private static ConfigurableListableBeanFactory beanFactory;
+
+    private static ApplicationContext applicationContext;
+
+    @Override
+    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
+    {
+        SpringUtils.beanFactory = beanFactory;
+    }
+
+    @Override
+    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
+    {
+        SpringUtils.applicationContext = applicationContext;
+    }
+
+    /**
+     * 获取对象
+     *
+     * @param name
+     * @return Object 一个以所给名字注册的bean的实例
+     * @throws BeansException
+     *
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T getBean(String name) throws BeansException
+    {
+        return (T) beanFactory.getBean(name);
+    }
+
+    /**
+     * 获取类型为requiredType的对象
+     *
+     * @param clz
+     * @return
+     * @throws BeansException
+     *
+     */
+    public static <T> T getBean(Class<T> clz) throws BeansException
+    {
+        T result = (T) beanFactory.getBean(clz);
+        return result;
+    }
+
+    /**
+     * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
+     *
+     * @param name
+     * @return boolean
+     */
+    public static boolean containsBean(String name)
+    {
+        return beanFactory.containsBean(name);
+    }
+
+    /**
+     * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
+     *
+     * @param name
+     * @return boolean
+     * @throws NoSuchBeanDefinitionException
+     *
+     */
+    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException
+    {
+        return beanFactory.isSingleton(name);
+    }
+
+    /**
+     * @param name
+     * @return Class 注册对象的类型
+     * @throws NoSuchBeanDefinitionException
+     *
+     */
+    public static Class<?> getType(String name) throws NoSuchBeanDefinitionException
+    {
+        return beanFactory.getType(name);
+    }
+
+    /**
+     * 如果给定的bean名字在bean定义中有别名,则返回这些别名
+     *
+     * @param name
+     * @return
+     * @throws NoSuchBeanDefinitionException
+     *
+     */
+    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException
+    {
+        return beanFactory.getAliases(name);
+    }
+
+    /**
+     * 获取aop代理对象
+     *
+     * @param invoker
+     * @return
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T getAopProxy(T invoker)
+    {
+        return (T) AopContext.currentProxy();
+    }
+
+    /**
+     * 获取当前的环境配置,无配置返回null
+     *
+     * @return 当前的环境配置
+     */
+    public static String[] getActiveProfiles()
+    {
+        return applicationContext.getEnvironment().getActiveProfiles();
+    }
+
+    /**
+     * 获取当前的环境配置,当有多个环境配置时,只获取第一个
+     *
+     * @return 当前的环境配置
+     */
+    public static String getActiveProfile()
+    {
+        final String[] activeProfiles = getActiveProfiles();
+        return (activeProfiles != null && activeProfiles.length > 0) ? activeProfiles[0] : null;
+    }
+}

+ 54 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/Tess4jDemo.java

@@ -0,0 +1,54 @@
+package com.attendance.util;
+
+import net.sourceforge.tess4j.TesseractException;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * tess4j测试类
+ */
+
+public class Tess4jDemo {
+    public static void main(String[] args) throws TesseractException {
+        String associateDegrees = "6,3";
+        String[] id = associateDegrees.split("\\,");
+//        String[] n = names.split("\\,");
+        List list = new ArrayList();
+        for (int i=0;i<id.length; i++) {
+            HashMap map = new HashMap();
+            map.put("id", id[i]);
+//            map.put("name", n[i]);
+            list.add(map);
+        }
+        System.out.println(list);
+
+
+//        LocalDate now = LocalDate.now();
+//        now = now.minusMonths(2-1);
+//        now = now.withDayOfMonth(1);
+//        System.out.println(now.toString());
+
+//        //创建ITesseract接口的实现实例对象
+//        ITesseract iTesseract = new Tesseract();
+//        //设置tessdata训练库语言包地址,项目根目录下为默认地址可不设置
+//        //获取项目路径
+////        String projectPath = System.getProperty("user.dir");
+////        System.out.println(projectPath);
+////        iTesseract.setDatapath(projectPath+"\\src\\main\\java\\com.management.platform.tessdata");
+//        //默认识别英文
+////                如果需要识别英文之外的语种,需要指定识别语种,并且需要将对应的语言包放进项目中
+//        iTesseract.setLanguage("chi_sim");
+//        // 指定本地图片
+//        File img = new File("D:\\360MoveData\\Users\\Administrator\\Desktop\\book1.png");
+//        //开始识别时间
+//        long startTime = System.currentTimeMillis();
+//        //识别结果
+//        String ocrResult = iTesseract.doOCR(img);
+//        // 输出识别结果
+//        System.out.println("耗时:" + (System.currentTimeMillis() - startTime) + "ms");
+//        System.out.println("识别结果: \n" + ocrResult);
+
+    }
+}

+ 42 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/TestSample.java

@@ -0,0 +1,42 @@
+package com.attendance.util;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class TestSample {
+    public static void main(String[] args) {
+        String address = "张三18680419527湖南省岳阳市君山区许加派村";
+        String p = decodePhone(address);
+        System.out.println("电话号码是:"+p);
+        address = address.replaceAll(p, " ");
+        //按空格或者逗号分割地址
+        String[] addArray = address.split(" |,|,");
+        String add = "";
+        for (String s : addArray) {
+            //判断是否最小区单位
+            if (s.contains("省") || s.contains("市") || s.contains("区") || s.contains("自治区") || s.contains("自治州") || s.contains("县")) {
+                System.out.println("省市区地址是:" + s);
+                add = s;
+            } else {
+                System.out.println("姓名是:" + s);
+            }
+        }
+        System.out.println("==============");
+        //从数据库的区获取数据,去匹配add, 找到后进行拆分
+        String[] detailAdd = add.split("区|自治区|自治州|县");
+        System.out.println("省市区地址是:" + detailAdd[0]);
+        System.out.println("详细地址是:" + detailAdd[1]);
+    }
+
+    public static String decodePhone(String address) {
+        //通过正则表达式解析电话
+        String regEx = "(?<phone>(?:(?:\\+|00)86)?1[3-9]\\d{9})";
+        Matcher m = Pattern.compile(regEx).matcher(address);
+        String phone = null;
+        if (m.find()) {
+            phone = m.group("phone");
+        }
+        System.out.println(phone);
+        return phone;
+    }
+}

+ 24 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/TimeZoneUtil.java

@@ -0,0 +1,24 @@
+package com.attendance.util;
+
+import java.util.Arrays;
+import java.util.Locale;
+import java.util.TimeZone;
+
+public class TimeZoneUtil {
+
+    public static void main(String[] args) {
+        System.out.println(Arrays.toString(TimeZone.getAvailableIDs()));
+        System.out.println(TimeZone.getDefault());
+        System.out.println(TimeZone.getTimeZone("GMT+08:00"));
+
+        TimeZone timeZone = TimeZone.getTimeZone("Europe/London");
+        System.out.println(1.0*timeZone.getRawOffset()/3600/1000);
+        double timeValue = 1.0*timeZone.getRawOffset()/3600/1000;
+        String hourValue = (timeValue>0?"+":"")+(int)timeValue;
+        int minInt = Math.abs((int)((timeValue - (int)timeValue)*60));
+        String minValue = (minInt<10?"0":"")+minInt;
+        System.out.println("GMT"+hourValue + ":"+ minValue);
+        System.out.println(timeZone.getDisplayName()); // 中国标准时间
+        System.out.println(timeZone.getDisplayName(Locale.ENGLISH)); // China Standard Time
+    }
+}

+ 54 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/UploadFileToFileNameUtil.java

@@ -0,0 +1,54 @@
+package com.attendance.util;
+
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * Author: 吴涛涛 cuiyi@itany.com
+ * Date : 2019 - 10 - 16 13:47
+ * Description:<描述>
+ * Version: 1.0
+ */
+public class UploadFileToFileNameUtil {
+
+    public static Map<String, Object> uploadFile(MultipartFile file, String path) {
+        Map<String,Object> map = new HashMap<String, Object>();
+        String afterUploadFileName = "";
+        if (file != null) {
+            File dir = null;
+            dir = new File(path);
+            // D://dolphin/upload 文件上传后所存储的位置,部署到服务器上时配置服务器地址即可
+            if (!dir.exists()) {
+                dir.mkdirs();
+            }
+            String fileName = "";
+            if (file != null) {
+                fileName = file.getOriginalFilename();
+                System.out.println("上传文件名称" + file.getName() + ", dir = " + dir.getAbsolutePath());
+                int pos = fileName.lastIndexOf(".");
+                String rand = UUID.randomUUID().toString().replaceAll("-", "");
+                String sufix = fileName.substring(pos);
+                fileName = rand + sufix;
+                afterUploadFileName = "/upload/" + fileName;
+                map.put("sqlFilePath",afterUploadFileName);
+                File saveFile = new File(dir, fileName);
+                try {
+                    saveFile.createNewFile();
+                    file.transferTo(saveFile);
+                    map.put("newFile",saveFile.getAbsolutePath());
+                } catch (IOException e) {
+                    e.printStackTrace();
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+
+        return map;
+    }
+}

+ 361 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/UserAgentUtils.java

@@ -0,0 +1,361 @@
+package com.attendance.util;
+import eu.bitwalker.useragentutils.Browser;
+import eu.bitwalker.useragentutils.OperatingSystem;
+import eu.bitwalker.useragentutils.UserAgent;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.http.HttpServletRequest;
+
+/*
+ * 工具类参照文章:https://blog.csdn.net/qq_23832313/article/details/82775316
+ *
+ * @author Tellsea
+ * @date 2020/10/27
+ */
+public class UserAgentUtils {
+
+    private static Logger logger = LoggerFactory.getLogger(UserAgentUtils.class);
+
+    /**
+     * 根据http获取userAgent信息
+     *
+     * @param request
+     * @return
+     */
+    public static String getUserAgent(HttpServletRequest request) {
+        String userAgent = request.getHeader("User-Agent");
+        return userAgent;
+    }
+
+    /**
+     * 根据request获取userAgent,然后解析出osVersion
+     *
+     * @param request
+     * @return
+     */
+    public static String getOsVersion(HttpServletRequest request) {
+        String userAgent = getUserAgent(request);
+        return getOsVersion(userAgent);
+    }
+
+    /**
+     * 根据userAgent解析出osVersion
+     *
+     * @param userAgent
+     * @return
+     */
+    public static String getOsVersion(String userAgent) {
+        String osVersion = "";
+        if (StringUtils.isBlank(userAgent)) {
+            return osVersion;
+        }
+        String[] strArr = userAgent.substring(userAgent.indexOf("(") + 1,
+                userAgent.indexOf(")")).split(";");
+        if (null == strArr || strArr.length == 0) {
+            return osVersion;
+        }
+
+        osVersion = strArr[1];
+        logger.info("osVersion is:{}", osVersion);
+        return osVersion;
+    }
+
+    /**
+     * 获取操作系统对象
+     *
+     * @param userAgent
+     * @return
+     */
+    private static OperatingSystem getOperatingSystem(String userAgent) {
+        UserAgent agent = UserAgent.parseUserAgentString(userAgent);
+        OperatingSystem operatingSystem = agent.getOperatingSystem();
+        return operatingSystem;
+    }
+
+
+    /**
+     * 获取os:Windows/ios/Android
+     *
+     * @param request
+     * @return
+     */
+    public static String getOs(HttpServletRequest request) {
+        String userAgent = getUserAgent(request);
+        return getOs(userAgent);
+    }
+
+    /**
+     * 获取os:Windows/ios/Android
+     *
+     * @param userAgent
+     * @return
+     */
+    public static String getOs(String userAgent) {
+        OperatingSystem operatingSystem = getOperatingSystem(userAgent);
+        String os = operatingSystem.getGroup().getName();
+        logger.info("os is:{}", os);
+        return os;
+    }
+
+
+    /**
+     * 获取deviceType
+     *
+     * @param request
+     * @return
+     */
+    public static String getDeviceType(HttpServletRequest request) {
+        String userAgent = getUserAgent(request);
+        return getDeviceType(userAgent);
+    }
+
+    /**
+     * 获取deviceType
+     *
+     * @param userAgent
+     * @return
+     */
+    public static String getDeviceType(String userAgent) {
+        OperatingSystem operatingSystem = getOperatingSystem(userAgent);
+        String deviceType = operatingSystem.getDeviceType().toString();
+        logger.info("deviceType is:{}", deviceType);
+        return deviceType;
+    }
+
+    /**
+     * 获取操作系统的名字
+     *
+     * @param request
+     * @return
+     */
+    public static String getOsName(HttpServletRequest request) {
+        String userAgent = getUserAgent(request);
+        return getOsName(userAgent);
+    }
+
+    /**
+     * 获取操作系统的名字
+     *
+     * @param userAgent
+     * @return
+     */
+    public static String getOsName(String userAgent) {
+        OperatingSystem operatingSystem = getOperatingSystem(userAgent);
+        String osName = operatingSystem.getName();
+        logger.info("osName is:{}", osName);
+        return osName;
+    }
+
+
+    /**
+     * 获取device的生产厂家
+     *
+     * @param request
+     */
+    public static String getDeviceManufacturer(HttpServletRequest request) {
+        String userAgent = getUserAgent(request);
+        return getDeviceManufacturer(userAgent);
+    }
+
+    /**
+     * 获取device的生产厂家
+     *
+     * @param userAgent
+     */
+    public static String getDeviceManufacturer(String userAgent) {
+        OperatingSystem operatingSystem = getOperatingSystem(userAgent);
+        String deviceManufacturer = operatingSystem.getManufacturer().toString();
+        logger.info("deviceManufacturer is:{}", deviceManufacturer);
+        return deviceManufacturer;
+    }
+
+    /**
+     * 获取浏览器对象
+     *
+     * @param agent
+     * @return
+     */
+    public static Browser getBrowser(String agent) {
+        UserAgent userAgent = UserAgent.parseUserAgentString(agent);
+        Browser browser = userAgent.getBrowser();
+        return browser;
+    }
+
+
+    /**
+     * 获取browser name
+     *
+     * @param request
+     * @return
+     */
+    public static String getBorderName(HttpServletRequest request) {
+        String userAgent = getUserAgent(request);
+        return getBorderName(userAgent);
+    }
+
+    /**
+     * 获取browser name
+     *
+     * @param userAgent
+     * @return
+     */
+    public static String getBorderName(String userAgent) {
+        Browser browser = getBrowser(userAgent);
+        String borderName = browser.getName();
+        logger.info("borderName is:{}", borderName);
+        return borderName;
+    }
+
+
+    /**
+     * 获取浏览器的类型
+     *
+     * @param request
+     * @return
+     */
+    public static String getBorderType(HttpServletRequest request) {
+        String userAgent = getUserAgent(request);
+        return getBorderType(userAgent);
+    }
+
+    /**
+     * 获取浏览器的类型
+     *
+     * @param userAgent
+     * @return
+     */
+    public static String getBorderType(String userAgent) {
+        Browser browser = getBrowser(userAgent);
+        String borderType = browser.getBrowserType().getName();
+        logger.info("borderType is:{}", borderType);
+        return borderType;
+    }
+
+    /**
+     * 获取浏览器组: CHROME、IE
+     *
+     * @param request
+     * @return
+     */
+    public static String getBorderGroup(HttpServletRequest request) {
+        String userAgent = getUserAgent(request);
+        return getBorderGroup(userAgent);
+    }
+
+    /**
+     * 获取浏览器组: CHROME、IE
+     *
+     * @param userAgent
+     * @return
+     */
+    public static String getBorderGroup(String userAgent) {
+        Browser browser = getBrowser(userAgent);
+        String browerGroup = browser.getGroup().getName();
+        logger.info("browerGroup is:{}", browerGroup);
+        return browerGroup;
+    }
+
+    /**
+     * 获取浏览器的生产厂商
+     *
+     * @param request
+     * @return
+     */
+    public static String getBrowserManufacturer(HttpServletRequest request) {
+        String userAgent = getUserAgent(request);
+        return getBrowserManufacturer(userAgent);
+    }
+
+
+    /**
+     * 获取浏览器的生产厂商
+     *
+     * @param userAgent
+     * @return
+     */
+    public static String getBrowserManufacturer(String userAgent) {
+        Browser browser = getBrowser(userAgent);
+        String browserManufacturer = browser.getManufacturer().getName();
+        logger.info("browserManufacturer is:{}", browserManufacturer);
+        return browserManufacturer;
+    }
+
+
+    /**
+     * 获取浏览器使用的渲染引擎
+     *
+     * @param request
+     * @return
+     */
+    public static String getBorderRenderingEngine(HttpServletRequest request) {
+        String userAgent = getUserAgent(request);
+        return getBorderRenderingEngine(userAgent);
+    }
+
+    /**
+     * 获取浏览器使用的渲染引擎
+     *
+     * @param userAgent
+     * @return
+     */
+    public static String getBorderRenderingEngine(String userAgent) {
+        Browser browser = getBrowser(userAgent);
+        String renderingEngine = browser.getRenderingEngine().name();
+        logger.info("renderingEngine is:{}", renderingEngine);
+        return renderingEngine;
+    }
+
+
+    /**
+     * 获取浏览器版本
+     *
+     * @param request
+     * @return
+     */
+    public static String getBrowserVersion(HttpServletRequest request) {
+        String userAgent = getUserAgent(request);
+        return getBrowserVersion(userAgent);
+    }
+
+    /**
+     * 获取浏览器版本
+     *
+     * @param userAgent
+     * @return
+     */
+    public static String getBrowserVersion(String userAgent) {
+        Browser browser = getBrowser(userAgent);
+        String borderVersion = browser.getVersion(userAgent).toString();
+        return borderVersion;
+    }
+
+
+    public static void main(String[] args) {
+		String winUserAgent = "Mozilla/5.0 (Linux; Android 8.0; LON-AL00 Build/HUAWEILON-AL00; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.132 MQQBrowser/6.2 TBS/044204 Mobile Safari/537.36 V1_AND_SQ_7.7.8_908_YYB_D QQ/7.7.8.3705 NetType/WIFI WebP/0.3.0 Pixel/1440";
+//		String iosUserAgent = "Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/16A366 QQ/7.7.8.421 V1_IPH_SQ_7.7.8_1_APP_A Pixel/750 Core/UIWebView Device/Apple(iPhone 6s) NetType/WIFI QBWebViewType/1";
+//        String winUserAgent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36";
+
+        System.out.println("浏览器组:" + getBorderGroup(winUserAgent));
+        System.out.println("浏览器名字:" + getBorderName(winUserAgent));
+        System.out.println("浏览器类型" + getBorderType(winUserAgent));
+        System.out.println("浏览器生产商:" + getBrowserManufacturer(winUserAgent));
+        System.out.println("浏览器版本:" + getBrowserVersion(winUserAgent));
+        System.out.println("设备生产厂商:" + getDeviceManufacturer(winUserAgent));
+        System.out.println("设备类型:" + getDeviceType(winUserAgent));
+        System.out.println("设备操作系统:" + getOs(winUserAgent));
+        System.out.println("操作系统的名字:" + getOsName(winUserAgent));
+        System.out.println("操作系统的版本号:" + getOsVersion(winUserAgent));
+        System.out.println("操作系统浏览器的渲染引擎:" + getBorderRenderingEngine(winUserAgent));
+        if (getOs(winUserAgent).contains("Windows")) {
+            System.out.println("是Windows");
+        } if (getOs(winUserAgent).contains("Linux")) {
+            System.out.println("是Linux");
+        } else {
+            System.out.println(getOs(winUserAgent));
+            System.out.println("无法匹配");
+        }
+    }
+}
+

+ 7 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/UserNotFoundException.java

@@ -0,0 +1,7 @@
+package com.attendance.util;
+
+public class UserNotFoundException extends Exception {
+    public UserNotFoundException(String msg) {
+        super(msg);
+    }
+}

+ 298 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/java/com/attendance/util/WorkDayCalculateUtils.java

@@ -0,0 +1,298 @@
+package com.attendance.util;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.time.DayOfWeek;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+
+public class WorkDayCalculateUtils {
+    public static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
+    public static DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+
+    private static final String KEY_SPECIAL_WORK_DAYS = "work";
+    private static final String KEY_SPECIAL_REST_DAYS = "rest";
+
+    private static final HashMap<String, HashMap> YEAR_DEFINE = new HashMap<>();
+    static {
+        HashMap<String, String[]> map2019 = new HashMap<>();
+        //2019年的除了周末的特殊工作日
+        map2019.put(KEY_SPECIAL_WORK_DAYS, new String[]{"2019-02-02","2019-02-03","2019-04-28","2019-05-05"});
+        //2019年除了周末的特殊休息日,例如国庆中秋春节
+        map2019.put(KEY_SPECIAL_REST_DAYS, new String[]{"2019-01-01",//元旦
+                "2019-02-04", "2019-02-05", "2019-02-06", "2019-02-07", "2021-02-08",//春节
+                "2019-04-05",//清明节
+                "2019-05-01","2019-05-02","2019-05-03",//劳动节
+                "2019-06-07",//端午节
+                "2019-10-01", "2019-10-02","2019-10-03","2019-10-04","2019-10-05","2019-10-06","2019-10-07",//国庆节
+                 });
+        YEAR_DEFINE.put("2019", map2019);
+
+        HashMap<String, String[]> map2020 = new HashMap<>();
+        //2020年的除了周末的特殊工作日
+        map2020.put(KEY_SPECIAL_WORK_DAYS, new String[]{"2020-01-19","2020-04-26","2020-05-09","2020-06-28","2020-10-10"});
+        //2020年除了周末的特殊休息日,例如国庆中秋春节
+        map2020.put(KEY_SPECIAL_REST_DAYS, new String[]{"2020-01-01",//元旦
+                "2020-01-24", "2020-01-27", "2020-02-28", "2020-02-29", "2020-02-30","2020-02-31",//春节
+                "2020-04-04",//清明节
+                "2020-05-01","2020-05-02","2020-05-03","2020-05-04","2020-05-05",//劳动节
+                "2020-06-25","2020-06-26",//端午节
+                "2020-10-01", "2020-10-02","2020-10-03","2020-10-04","2020-10-05","2020-10-06","2020-10-07","2020-10-08",//国庆节
+        });
+        YEAR_DEFINE.put("2020", map2020);
+
+        HashMap<String, String[]> map2021 = new HashMap<>();
+        //除了周末的特殊工作日
+        map2021.put(KEY_SPECIAL_WORK_DAYS, new String[]{"2021-02-07","2021-02-20","2021-04-25","2021-05-08","2021-09-18","2021-09-26","2021-10-09"});
+        //除了周末的特殊休息日,例如国庆中秋春节
+        map2021.put(KEY_SPECIAL_REST_DAYS, new String[]{
+                "2021-01-01",//元旦
+                "2021-02-11", "2021-02-12", "2021-02-15", "2021-02-16", "2021-02-17",//春节
+                "2021-04-05",//清明节
+                "2021-05-03","2021-05-04","2021-05-05",//劳动节
+                "2021-06-14",//端午节
+                "2021-09-20","2021-09-21",//中秋节
+                "2021-10-01", "2021-10-02","2021-10-03","2021-10-04","2021-10-05","2021-10-06","2021-10-07",//国庆节
+        });
+        YEAR_DEFINE.put("2021", map2021);
+
+        HashMap<String, String[]> map2022 = new HashMap<>();
+        //除了周末的特殊工作日
+        map2022.put(KEY_SPECIAL_WORK_DAYS, new String[]{"2022-01-29","2022-01-30","2022-04-02","2022-04-24","2022-05-07","2022-10-08","2022-10-09",});
+        //除了周末的特殊休息日,例如国庆中秋春节
+        map2022.put(KEY_SPECIAL_REST_DAYS, new String[]{
+                "2022-01-03",//元旦
+                "2022-01-31", "2022-02-01", "2022-02-02", "2022-02-03", "2022-02-04",//春节
+                "2022-04-04","2022-04-05",//清明节
+                "2022-05-02","2022-05-03","2022-05-04",//劳动节
+                "2022-06-03",//端午节
+                "2022-09-12",//中秋节
+                "2022-10-01", "2022-10-02","2022-10-03","2022-10-04","2022-10-05","2022-10-06","2022-10-07",//国庆节
+        });
+        YEAR_DEFINE.put("2022", map2022);
+
+//        HashMap<String, String[]> map2023 = new HashMap<>();
+//        //除了周末的特殊工作日
+//        map2023.put(KEY_SPECIAL_WORK_DAYS, new String[]{"2022-01-29","2022-01-30","2022-04-02","2022-04-24","2022-05-07","2022-10-08","2022-10-09",});
+//        //除了周末的特殊休息日,例如国庆中秋春节
+//        map2023.put(KEY_SPECIAL_REST_DAYS, new String[]{
+//                "2022-01-03",//元旦
+//                "2022-01-31", "2022-02-01", "2022-02-02", "2022-02-03", "2022-02-04",//春节
+//                "2022-04-04","2022-04-05",//清明节
+//                "2022-05-02","2022-05-03","2022-05-04",//劳动节
+//                "2022-06-03",//端午节
+//                "2022-09-20","2022-09-21",//中秋节
+//                "2022-10-01", "2022-10-02","2022-10-03","2022-10-04","2022-10-05","2022-10-06","2022-10-07",//国庆节
+//        });
+//        YEAR_DEFINE.put("2023", map2023);
+
+        HashMap<String, String[]> map2023 = new HashMap<>();
+        //除了周末的特殊工作日
+        map2023.put(KEY_SPECIAL_WORK_DAYS, new String[]{"2023-01-28","2023-01-29","2023-04-23","2023-05-06","2023-06-25","2023-10-07","2023-10-08",});
+        //除了周末的特殊休息日,例如国庆中秋春节
+        map2023.put(KEY_SPECIAL_REST_DAYS, new String[]{
+                "2023-01-02",//元旦
+                "2023-01-23", "2023-01-24", "2023-01-25", "2023-01-26", "2023-01-27",//春节
+                "2023-04-05",//清明节
+                "2023-05-01","2023-05-02","2023-05-03",//劳动节
+                "2023-06-22","2023-06-23",//端午节
+                "2023-09-29","2023-10-02","2023-10-03","2023-10-04","2023-10-05","2023-10-06",//中秋国庆节
+        });
+        YEAR_DEFINE.put("2023", map2023);
+
+    }
+
+    /**
+     * 计算给定时间范围内的工作日列表
+     * @param startDate
+     * @param endDate
+     * @return
+     */
+    public static List<LocalDate> getWorkDaysListInRange(String startDate, String endDate, int includeWeekends) {
+        int daysOffset = 0;
+        LocalDate localStartDate = LocalDate.parse(startDate, dateTimeFormatter);
+        LocalDate localEndDate = LocalDate.parse(endDate, dateTimeFormatter);
+        List<LocalDate> list = new ArrayList<>();
+        while(true) {
+            localStartDate = localStartDate.plusDays(daysOffset);
+            if (includeWeekends == 1 || isWorkDay(localStartDate)) {
+                list.add(localStartDate);
+            }
+            //到达结束日期,结束计算
+            if (localStartDate.isEqual(localEndDate)) {
+                break;
+            }
+            //每次加一天
+            if (daysOffset == 0) {
+                daysOffset = 1;
+            }
+        }
+        return list;
+    }
+
+    /**
+     * 获取期间的非工作日
+     * @param startDate
+     * @param endDate
+     * @return
+     */
+    public static List<LocalDate> getNonWorkDaysListInRange(String startDate, String endDate) {
+        int daysOffset = 0;
+        LocalDate localStartDate = LocalDate.parse(startDate, dateTimeFormatter);
+        LocalDate localEndDate = LocalDate.parse(endDate, dateTimeFormatter);
+        if (localEndDate.isBefore(localStartDate)) {
+            System.err.println("结束日期"+endDate+"不得小于开始日期"+startDate);
+        }
+        List<LocalDate> list = new ArrayList<>();
+        int i = 0;
+        while(true) {
+            localStartDate = localStartDate.plusDays(daysOffset);
+            if (!isWorkDay(localStartDate)) {
+                list.add(localStartDate);
+            }
+            //到达结束日期,结束计算
+            if (localStartDate.isEqual(localEndDate)) {
+                break;
+            }
+            //每次加一天
+            if (daysOffset == 0) {
+                daysOffset = 1;
+            }
+        }
+        return list;
+    }
+
+
+    /**
+     * 计算给定时间范围内的工作日天数
+     * @param startDate
+     * @param endDate
+     * @return
+     */
+    public static int getWorkDaysCountInRange(String startDate, String endDate, int includeWeekend) {
+        int count = 0;
+        LocalDate localStartDate = LocalDate.parse(startDate, dateTimeFormatter);
+        LocalDate localEndDate = LocalDate.parse(endDate, dateTimeFormatter);
+
+        if (includeWeekend == 0) {
+            //不含周末节假日
+            int daysOffset = 0;
+            if (startDate.compareTo(endDate) > 0) {
+                return -1;
+            }
+
+            while(true) {
+                localStartDate = localStartDate.plusDays(daysOffset);
+                if (isWorkDay(localStartDate)) {
+                    count++;
+                }
+                //到达结束日期,结束计算
+                if (localStartDate.isEqual(localEndDate)) {
+                    break;
+                }
+                //每次加一天
+                if (daysOffset == 0) {
+                    daysOffset = 1;
+                }
+            }
+        } else {
+            //全部日期
+            count = (int)(localEndDate.toEpochDay() - localStartDate.toEpochDay() + 1);
+        }
+
+        return count;
+    }
+
+    private static boolean isInArray(String key, String[] array) {
+        for (int i=0;i<array.length; i++) {
+            if (array[i].equals(key)) {
+                return true;
+            }
+        }
+        return false;
+    }
+    //一周6天工作制,周六也算工作日
+    public static boolean isWorkDayExceptSaturday(LocalDate date) {
+        int year = date.getYear();
+        try {
+            String dateStr = dateTimeFormatter.format(date);
+            Date d = simpleDateFormat.parse(dateStr);
+            boolean isWeekend = date.getDayOfWeek() == DayOfWeek.SUNDAY;
+            if (isWeekend) {
+                //判断周末是否是特殊工作日
+                HashMap<String, String[]> map = YEAR_DEFINE.get(year + "");
+                if (map == null) {
+                    //特殊日期年份尚未录入,周末不算是工作日
+                    return false;
+                }
+                String[] list = map.get(KEY_SPECIAL_WORK_DAYS);
+                if (isInArray(dateStr, list)) {
+                    //存在特殊工作日
+                    return true;
+                } else {
+                    return false;
+                }
+            } else {
+                //判断常规周一到周六,是否是特殊节假日
+                HashMap<String, String[]> map = YEAR_DEFINE.get(year+"");
+                if (map == null) {
+                    //特殊日期年份尚未录入,工作日正常上班
+                    return true;
+                }
+                String[] list = map.get(KEY_SPECIAL_REST_DAYS);
+                if (isInArray(dateStr, list)) {
+                    return false;
+                } else {
+                    return true;
+                }
+            }
+        } catch (ParseException e) {
+            e.printStackTrace();
+            System.out.println(e.getMessage());
+        }
+        return true;
+    }
+
+    public static boolean isWorkDay(LocalDate date) {
+        int year = date.getYear();
+        String dateStr = dateTimeFormatter.format(date);
+        boolean isWeekend = date.getDayOfWeek() == DayOfWeek.SATURDAY || date.getDayOfWeek() == DayOfWeek.SUNDAY;
+        if (isWeekend) {
+            //判断周末是否是特殊工作日
+            HashMap<String, String[]> map = YEAR_DEFINE.get(year + "");
+            if (map == null) {
+                //特殊日期年份尚未录入,周末不算是工作日
+                return false;
+            }
+            String[] list = map.get(KEY_SPECIAL_WORK_DAYS);
+            if (isInArray(dateStr, list)) {
+                //存在特殊工作日
+                return true;
+            } else {
+                return false;
+            }
+        } else {
+            //判断常规周一到周五,是否是特殊节假日
+            HashMap<String, String[]> map = YEAR_DEFINE.get(year+"");
+            if (map == null) {
+                //特殊日期年份尚未录入,工作日正常上班
+                return true;
+            }
+            String[] list = map.get(KEY_SPECIAL_REST_DAYS);
+            if (isInArray(dateStr, list)) {
+                return false;
+            } else {
+                return true;
+            }
+        }
+    }
+
+
+    public static void main(String[] args) {
+        System.out.println(getWorkDaysCountInRange("2022-08-06","2022-08-08", 1));
+    }
+}

+ 56 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/resources/application.yml

@@ -0,0 +1,56 @@
+server:
+  port: 10020
+  tomcat:
+    uri-encoding: utf-8
+    max-http-form-post-size: -1
+    connection-timeout: 18000000s
+spring:
+  servlet:
+    multipart:
+      # 配置上传文件的大小设置
+
+      # Single file max size  即单个文件大小
+      max-file-size: 100MB
+      max-request-size: 100MB
+      location: C:/upload/
+  datasource:
+    driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
+    url: jdbc:sqlserver://localhost:1433;database=atten_dev;encrypt=false
+    username: sa
+    password: Sqlserver_2023
+
+##########日志配置
+logging:
+  level:
+    root: info
+    org.mybatis: debug
+    java.sql: debug
+    org.springframework.web: trace
+    #打印sql语句
+    com.attendance.mapper: debug
+  path: C:/
+##########
+mybatis-plus:
+#  mapper-locations: classpath:mapper/*/*.xml
+#  #实体扫描,多个package用逗号或者分号分隔
+#  typeAliasesPackage: com.hssx.cloudmodel
+  global-config:
+    #主键类型  0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID";
+    id-type: 0
+    #字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断"
+    field-strategy: 2
+    db-column-underline: true
+    refresh-mapper:
+#################插入和更新非null判断
+    db-config:
+      insert-strategy: not_null
+      update-strategy: not_null
+      table-underline: false # 设置表明不使用下划线方式
+  configuration:
+    map-underscore-to-camel-case: false # 设置字段不使用下划线方式
+    cache-enabled: false
+######mybstis配置#######
+mybatis:
+  type-aliases-package: com.attendance.entity
+  mapper-locations: mappers/*Mapper.xml
+

+ 18 - 0
fhKeeper/formulahousekeeper/attendance-data/src/main/resources/mapper/HrmScheduleSignMapper.xml

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.attendance.mapper.HrmScheduleSignMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.attendance.entity.HrmScheduleSign">
+        <id column="id" property="id" />
+        <result column="userId" property="userId" />
+        <result column="signDate" property="signDate" />
+        <result column="signTime" property="signTime" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id, userId, signDate, signTime
+    </sql>
+
+</mapper>

+ 83 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/UserFvTimeController.java

@@ -1,6 +1,7 @@
 package com.management.platform.controller;
 
 
+import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.management.platform.entity.*;
@@ -12,12 +13,17 @@ import com.management.platform.service.LeaveSheetService;
 import com.management.platform.service.UserFvTimeService;
 import com.management.platform.util.DockWithMLD;
 import com.management.platform.util.HttpRespMsg;
+import org.springframework.http.*;
+import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 
 import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.client.RestTemplate;
 
 import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
 import java.math.BigDecimal;
+import java.text.DecimalFormat;
 import java.time.Duration;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
@@ -53,6 +59,10 @@ public class UserFvTimeController {
     UserMapper userMapper;
     @Resource
     TimeAutoExcludeMapper timeAutoExcludeMapper;
+    @Resource
+    private RestTemplate restTemplate;
+
+    private DecimalFormat df = new DecimalFormat("#.0");
 
     @RequestMapping("/get")
     public HttpRespMsg get(){
@@ -312,5 +322,78 @@ public class UserFvTimeController {
         return result;
     }
 
+    @RequestMapping("syncAttendanceForMingYi")
+    public HttpRespMsg getAttendanceForMingYi( String startDate,String endDate,String userId, HttpServletRequest request) throws Exception {
+        HttpRespMsg httpRespMsg = new HttpRespMsg();
+        JSONObject reqParam = new JSONObject();
+        Integer companyId = userMapper.selectById(request.getHeader("token")).getCompanyId();
+        List<User> userList = userMapper.selectList(new QueryWrapper<User>().eq("company_id", companyId).select("id", "job_number"));
+        List<UserFvTime> allList = userFvTimeService.list(new QueryWrapper<UserFvTime>().between("work_date", startDate, endDate).eq("company_id", companyId));
+        DateTimeFormatter date=DateTimeFormatter.ofPattern("yyyy-MM-dd");
+        DateTimeFormatter time=DateTimeFormatter.ofPattern("HH:mm");
+        reqParam.put("startDate",startDate);
+        reqParam.put("endDate",endDate);
+        reqParam.put("userId",userId);
+        HttpHeaders headers = new HttpHeaders();
+        String url="http://localhost:10020/attendance/getAttendanceList";
+        headers.setContentType(MediaType.APPLICATION_JSON);
+        HttpEntity<String> requestEntity = new HttpEntity<String>(reqParam.toJSONString(), headers);
+        ResponseEntity<String> responseEntity = this.restTemplate.exchange(url,
+                HttpMethod.POST, requestEntity, String.class);
+        List<Map> list=new ArrayList<>();
+        if (responseEntity.getStatusCode() == HttpStatus.OK) {
+            String resp = responseEntity.getBody();
+            System.out.println(resp);
+            JSONObject dataJson = JSONObject.parseObject(resp);
+            if (dataJson.getString("code").equals("ok")) {
+                JSONArray data = dataJson.getJSONArray("data");
+                String s = data.toJSONString();
+                list = JSONArray.parseArray(s, Map.class);
+            } else {
+                throw new Exception(dataJson.toJSONString());
+            }
+        }
+        List<UserFvTime> userFvTimeList=new ArrayList<>();
+        for (Map item : list) {
+            Optional<User> user = userList.stream().filter(ul -> ul.getJobNumber() != null && ul.getJobNumber().equals(item.get("userId"))).findFirst();
+            if(user.isPresent()){
+                UserFvTime userFvTime=new UserFvTime();
+                userFvTime.setWorkDate(LocalDate.parse(String.valueOf(item.get("signDate")),date));
+                userFvTime.setUserId(user.get().getId());
+                userFvTime.setCompanyId(companyId);
+                userFvTime.setStartTime(LocalTime.parse(String.valueOf(item.get("clockInTime")),time).format(time));
+                userFvTime.setEndTime(LocalTime.parse(String.valueOf(item.get("afterWorkTime")),time).format(time));
+                Duration between = Duration.between(LocalTime.parse(String.valueOf(item.get("clockInTime")), time), LocalTime.parse(String.valueOf(item.get("afterWorkTime")), time));
+                BigDecimal bigDecimal=new BigDecimal(between.toHours());
+                bigDecimal=bigDecimal.subtract(new BigDecimal(1.5));
+                userFvTime.setWorkHours(bigDecimal.floatValue());
+                if(!allList.stream().anyMatch(al->al.getUserId().equals(user.get().getId())&&al.getWorkDate().equals(LocalDate.parse(String.valueOf(item.get("signDate")),date)))){
+                    userFvTimeList.add(userFvTime);
+                }
+            }
+        }
+        if(!userFvTimeService.saveBatch(userFvTimeList)){
+            httpRespMsg.setError("验证失败");
+            return httpRespMsg;
+        }
+        User user = userMapper.selectById(request.getHeader("token"));
+        List<UserFvTime> timeList = userFvTimeService.list(new QueryWrapper<UserFvTime>().between("work_date", startDate, endDate).eq("company_id", user.getCompanyId()).eq("user_id", user.getId()));
+        double sum = timeList.stream().mapToDouble(UserFvTime::getWorkHours).sum();
+        httpRespMsg.setData(df.format(sum));
+        return httpRespMsg;
+    }
+
+    @RequestMapping("/getMinYiWorkHour")
+    public HttpRespMsg getMinYiWorkHour(HttpServletRequest request, String startDate,String endDate){
+        HttpRespMsg httpRespMsg=new HttpRespMsg();
+        DecimalFormat df = new DecimalFormat("#.0");
+        User user = userMapper.selectById(request.getHeader("token"));
+        List<UserFvTime> userFvTimeList = userFvTimeService.list(new QueryWrapper<UserFvTime>().between("work_date", startDate, endDate).eq("company_id", user.getCompanyId()).eq("user_id", user.getId()));
+        double sum=0;
+        sum = userFvTimeList.stream().mapToDouble(UserFvTime::getWorkHours).sum();
+        httpRespMsg.setData(df.format(sum));
+        return httpRespMsg;
+    }
+
 }
 

+ 55 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/task/TimingTask.java

@@ -672,6 +672,61 @@ public class TimingTask {
     }
 
 
+    //专门给明夷的定时任务
+    @Scheduled(cron = "0 0 1 ? * *")
+    private void synAttendanceDate() {
+        JSONObject reqParam = new JSONObject();
+        DateTimeFormatter date=DateTimeFormatter.ofPattern("yyyy-MM-dd");
+        DateTimeFormatter time=DateTimeFormatter.ofPattern("HH:mm");
+        List<Company> companyList = companyMapper.selectList(new QueryWrapper<Company>().eq("company_name", "明夷"));
+        if(companyList.size()>0){
+            Company company = companyList.get(0);
+            Integer companyId = company.getId();
+            String dateString = date.format(LocalDate.now().minusDays(1));
+            List<User> userList = userMapper.selectList(new QueryWrapper<User>().eq("company_id", companyId).select("id", "job_number"));
+            List<UserFvTime> allList = userFvTimeService.list(new QueryWrapper<UserFvTime>().between("work_date", dateString, dateString).eq("company_id", companyId));
+            reqParam.put("startDate",dateString);
+            reqParam.put("endDate",dateString);
+            HttpHeaders headers = new HttpHeaders();
+            String url="http://localhost:10020/attendance/getAttendanceList";
+            headers.setContentType(MediaType.APPLICATION_JSON);
+            HttpEntity<String> requestEntity = new HttpEntity<String>(reqParam.toJSONString(), headers);
+            ResponseEntity<String> responseEntity = this.restTemplate.exchange(url,
+                    HttpMethod.POST, requestEntity, String.class);
+            List<Map> list=new ArrayList<>();
+            if (responseEntity.getStatusCode() == HttpStatus.OK) {
+                String resp = responseEntity.getBody();
+                System.out.println(resp);
+                JSONObject dataJson = JSONObject.parseObject(resp);
+                if (dataJson.getString("code").equals("ok")) {
+                    JSONArray data = dataJson.getJSONArray("data");
+                    String s = data.toJSONString();
+                    list = JSONArray.parseArray(s, Map.class);
+                }
+            }
+            List<UserFvTime> userFvTimeList=new ArrayList<>();
+            for (Map item : list) {
+                Optional<User> user = userList.stream().filter(ul -> ul.getJobNumber() != null && ul.getJobNumber().equals(item.get("userId"))).findFirst();
+                if(user.isPresent()){
+                    UserFvTime userFvTime=new UserFvTime();
+                    userFvTime.setWorkDate(LocalDate.parse(String.valueOf(item.get("signDate")),date));
+                    userFvTime.setUserId(user.get().getId());
+                    userFvTime.setCompanyId(companyId);
+                    userFvTime.setStartTime(LocalTime.parse(String.valueOf(item.get("clockInTime")),time).format(time));
+                    userFvTime.setEndTime(LocalTime.parse(String.valueOf(item.get("afterWorkTime")),time).format(time));
+                    Duration between = Duration.between(LocalTime.parse(String.valueOf(item.get("clockInTime")), time), LocalTime.parse(String.valueOf(item.get("afterWorkTime")), time));
+                    BigDecimal bigDecimal=new BigDecimal(between.toHours());
+                    bigDecimal=bigDecimal.subtract(new BigDecimal(1.5));
+                    userFvTime.setWorkHours(bigDecimal.floatValue());
+                    if(!allList.stream().anyMatch(al->al.getUserId().equals(user.get().getId())&&al.getWorkDate().equals(LocalDate.parse(String.valueOf(item.get("signDate")),date)))){
+                        userFvTimeList.add(userFvTime);
+                    }
+                }
+            }
+            userFvTimeService.saveBatch(userFvTimeList);
+        }
+    }
+
     @Scheduled(cron = "0 10 1 ? * *")
     private void synAd() throws Exception {
         if (isDev) return;

+ 58 - 3
fhKeeper/formulahousekeeper/timesheet/src/views/workReport/daily.vue

@@ -346,7 +346,7 @@
                     </el-form-item>
                     <el-form-item :label="$t('screening.workingDate')" prop="createDate">
                         <el-date-picker v-model="workForm.createDate" :editable="false" format="yyyy-MM-dd" value-format="yyyy-MM-dd" 
-                        :style="'width:'+(isBatch==0?'200':'280')+'px;'" :type="isBatch==0?'date':'daterange'"
+                        :style="'width:'+(isBatch==0?'200':'320')+'px;'" :type="isBatch==0?'date':'daterange'"
                         @change="changeMonth()" @blur="iptBlur(reportTimeType.type)" :clearable="false" :placeholder="$t('defaultText.selectWorkDate')" :disabled="isDisable"></el-date-picker>
                         <div v-if="falsss" style="display: inline-block;margin-left:10px">{{jsDay}} {{$t('time.day')}}, {{jsTime}} {{$t('time.hour')}}</div>
                         <div v-if="isBatch!=0" style="display: inline-block;margin-left:10px">{{jsDay}} {{$t('time.day')}}</div>
@@ -359,9 +359,13 @@
                         <span v-if="workForm.time">{{$t('other.attendancePunch')}}: {{workForm.time.startTime}}-{{workForm.time.endTime}}, {{workForm.time.workHours}}{{$t('time.hour')}}</span>
                         <!--批量填报和批量代填不显示考勤记录-->
                         <span v-if="!isBatch && (user.timeType.syncDingding==1 || user.timeType.syncCorpwxTime==1)&&!workForm.time" >{{$t('other.noAttendanceRecord')}}</span>
+                        <span v-if="isBatch && user.company.companyName==='明夷'">{{',考勤时长'}}:{{this.workTimeForMinYi}}{{$t('time.hour')}}</span>
                         <el-button type="default" style="margin-left:5px;" size="small" :loading="syncTimeLoading" 
                         v-if="!isBatch && user.timeType.syncCorpwxTime==1" icon="el-icon-refresh" 
                                 @click="refreshWXCardTime(workForm.createDate)"></el-button>
+                        <el-button type="default" style="margin-left:5px;" size="small" :loading="syncTimeLoading" 
+                        v-if="isBatch && user.company.companyName==='明夷'" icon="el-icon-refresh" 
+                                @click="refreshAttendance(workForm.createDate)"></el-button>
                         <!-- AI智能填报 -->
                         <el-button type="primary" @click="getAIReport()" v-if="!hasWrittenReport" style="margin-left:5px;" >智能填报</el-button>
                     </el-form-item>
@@ -2096,6 +2100,8 @@
                 integrationProjectList: [],
 
                 totalReportHours: 0, // 合计的工时
+
+                workTimeForMinYi:0,
             };
         },
         watch: {
@@ -2445,6 +2451,31 @@
                         type: 'error'
                     })
                 })
+            },
+             refreshAttendance(workdate) {
+                 if (!workdate) return;
+                 if(this.workTimeForMinYi>0) return;
+                this.syncTimeLoading = true;
+                this.http.post('/user-fv-time/syncAttendanceForMingYi',{
+                    startDate: workdate[0],
+                    endDate: workdate[1]
+                },res => {
+                    this.syncTimeLoading = false;
+                    if(res.code == 'ok'){
+                        this.workTimeForMinYi= res.data;
+                    }else {
+                        this.$message({
+                            message: res.msg,
+                            type: 'error'
+                        })
+                    }
+                },err => {
+                    this.syncTimeLoading = false;
+                    this.$message({
+                        message: err,
+                        type: 'error'
+                    })
+                })
             },
             weeklyFilledTimeClick(parameterDate){ //按周填报-已填工时-点击
                 this.weeklyFilledTimeDialog = true
@@ -2934,6 +2965,30 @@
                     if (res.code == "ok") {
                         this.jsDay = res.data
                         this.chuji(i)
+                    } else {
+                        this.$message({
+                            message: res.msg,
+                            type: "error"
+                        });
+                    }
+                },
+                error => {
+                    this.$message({
+                        message: error,
+                        type: "error"
+                    });
+                    }
+                );
+                this.getWorkTimeForMinYi();
+            },
+            getWorkTimeForMinYi(){
+                this.http.post('/user-fv-time/getMinYiWorkHour',{ 
+                    startDate: this.workForm.createDate[0],
+                    endDate: this.workForm.createDate[1]
+                },
+                res => {
+                    if (res.code == "ok") {
+                        this.workTimeForMinYi = res.data
                     } else {
                         this.$message({
                             message: res.msg,
@@ -2949,7 +3004,6 @@
                     });
                     }
                 );
-
                 var sl = this.workForm.domains
                 this.chuji(i)
             },
@@ -5129,7 +5183,8 @@
                 });
             },
             guanbi() {
-                this.falsss = false
+                this.falsss = false,
+                this.workTimeForMinYi=0
             },
 
             // 打开日报填写