LIVE_YE 3 月之前
父节点
当前提交
f512fc8831
共有 37 个文件被更改,包括 2103 次插入10 次删除
  1. 7 0
      ruoyi-admin/resources/conf/acs/AddCardInfoParam.json
  2. 5 0
      ruoyi-admin/resources/conf/acs/AddFaceInfoParam.json
  3. 9 0
      ruoyi-admin/resources/conf/acs/AddFingerPrintInfo.json
  4. 22 0
      ruoyi-admin/resources/conf/acs/AddUserInfoParam.json
  5. 5 0
      ruoyi-admin/resources/conf/acs/CaptureFingerInfo.xml
  6. 9 0
      ruoyi-admin/resources/conf/acs/DeleteCardInfo.json
  7. 5 0
      ruoyi-admin/resources/conf/acs/DeleteFaceInfoParam.json
  8. 8 0
      ruoyi-admin/resources/conf/acs/DeleteFingerPrintInfo.json
  9. 10 0
      ruoyi-admin/resources/conf/acs/DeleteUserInfoParam.json
  10. 12 0
      ruoyi-admin/resources/conf/acs/SearchCardInfoParam.json
  11. 7 0
      ruoyi-admin/resources/conf/acs/SearchFaceInfoParam.json
  12. 8 0
      ruoyi-admin/resources/conf/acs/SearchFingerPrintParam.json
  13. 8 0
      ruoyi-admin/resources/conf/acs/SearchUserInfoParam.json
  14. 115 0
      ruoyi-admin/src/main/java/com/ruoyi/Common/CommonMethod.java
  15. 83 0
      ruoyi-admin/src/main/java/com/ruoyi/Common/ConfigFileUtil.java
  16. 43 0
      ruoyi-admin/src/main/java/com/ruoyi/alarm/Alarm.java
  17. 388 0
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/appointment/BomanReservatController.java
  18. 348 0
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/camera/CameraController.java
  19. 104 0
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/clock/ClockEquipRecordController.java
  20. 4 0
      ruoyi-common/src/main/java/com/ruoyi/common/constant/CommonConstants.java
  21. 22 0
      ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtils.java
  22. 1 1
      ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
  23. 238 0
      ruoyi-system/src/main/java/com/ruoyi/system/domain/ClockEquipRecord.java
  24. 15 4
      ruoyi-system/src/main/java/com/ruoyi/system/domain/ClockRecord.java
  25. 12 0
      ruoyi-system/src/main/java/com/ruoyi/system/domain/DutySchedule.java
  26. 66 0
      ruoyi-system/src/main/java/com/ruoyi/system/mapper/ClockEquipRecordMapper.java
  27. 3 0
      ruoyi-system/src/main/java/com/ruoyi/system/mapper/ClockUserInfoMapper.java
  28. 2 0
      ruoyi-system/src/main/java/com/ruoyi/system/service/CameraService.java
  29. 61 0
      ruoyi-system/src/main/java/com/ruoyi/system/service/IClockEquipRecordService.java
  30. 214 2
      ruoyi-system/src/main/java/com/ruoyi/system/service/impl/CameraServiceImpl.java
  31. 96 0
      ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ClockEquipRecordServiceImpl.java
  32. 3 0
      ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ClockRecordServiceImpl.java
  33. 151 0
      ruoyi-system/src/main/resources/mapper/system/ClockEquipRecordMapper.xml
  34. 6 1
      ruoyi-system/src/main/resources/mapper/system/ClockRecordMapper.xml
  35. 6 1
      ruoyi-system/src/main/resources/mapper/system/ClockUserInfoMapper.xml
  36. 6 1
      ruoyi-system/src/main/resources/mapper/system/DutyScheduleMapper.xml
  37. 1 0
      ruoyi-system/src/main/resources/mapper/system/OpeningDoorRecordMapper.xml

+ 7 - 0
ruoyi-admin/resources/conf/acs/AddCardInfoParam.json

@@ -0,0 +1,7 @@
+{
+  "CardInfo" : {
+    "employeeNo":"${employeeNo}",
+    "cardNo":"${cardNo}",
+    "cardType":"normalCard"
+  }
+}

+ 5 - 0
ruoyi-admin/resources/conf/acs/AddFaceInfoParam.json

@@ -0,0 +1,5 @@
+{
+  "faceLibType":"blackFD",
+  "FDID":"1",
+  "FPID":"${employeeNo}"
+}

+ 9 - 0
ruoyi-admin/resources/conf/acs/AddFingerPrintInfo.json

@@ -0,0 +1,9 @@
+{
+  "FingerPrintCfg": {
+    "employeeNo":  "${employeeNo}",
+    "enableCardReader": [1],
+    "fingerPrintID":  1,
+    "fingerType":  "normalFP",
+    "fingerData":  "${fingerData}"
+  }
+}

+ 22 - 0
ruoyi-admin/resources/conf/acs/AddUserInfoParam.json

@@ -0,0 +1,22 @@
+{
+  "UserInfo":{
+    "employeeNo": "${employeeNo}",
+    "name": "${name}",
+    "userType":"normal",
+    "Valid":{
+      "enable": ${enable},
+      "beginTime":"2023-01-01T00:00:00",
+      "endTime":"2033-12-30T23:59:59",
+      "timeType":"local"
+    },
+    "doorRight":"1",
+    "RightPlan":[
+      {
+        "doorNo": ${doorNo},
+        "planTemplateNo":"1"
+      }
+    ],
+    "gender":"male",
+    "localUIRight":false
+  }
+}

+ 5 - 0
ruoyi-admin/resources/conf/acs/CaptureFingerInfo.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<CaptureFingerPrintCond xmlns="http://www.isapi.org/ver20/XMLSchema" version="2.0">
+<fingerNo>${fingerNo}</fingerNo>
+<readerID>1</readerID>
+</CaptureFingerPrintCond>

+ 9 - 0
ruoyi-admin/resources/conf/acs/DeleteCardInfo.json

@@ -0,0 +1,9 @@
+{
+  "CardInfoDelCond":{
+    "CardNoList":[
+      {
+        "cardNo":"${cardNo}"
+      }
+    ]
+  }
+}

+ 5 - 0
ruoyi-admin/resources/conf/acs/DeleteFaceInfoParam.json

@@ -0,0 +1,5 @@
+{
+    "FPID": [{
+        "value": "${employeeNo}"
+    }]
+}

+ 8 - 0
ruoyi-admin/resources/conf/acs/DeleteFingerPrintInfo.json

@@ -0,0 +1,8 @@
+{
+  "FingerPrintCfg": {
+    "employeeNo": "${employeeNo}",
+    "enableCardReader": [1],
+    "fingerPrintID": 1,
+    "deleteFingerPrint": true
+  }
+}

+ 10 - 0
ruoyi-admin/resources/conf/acs/DeleteUserInfoParam.json

@@ -0,0 +1,10 @@
+{
+  "UserInfoDetail": {
+    "mode":  "${mode}",
+    "EmployeeNoList": [
+      {
+        "employeeNo":  "${employeeNo}"
+      }
+    ]
+  }
+}

+ 12 - 0
ruoyi-admin/resources/conf/acs/SearchCardInfoParam.json

@@ -0,0 +1,12 @@
+{
+  "CardInfoSearchCond": {
+    "searchID": "${searchID}",
+    "searchResultPosition": 0,
+    "maxResults": 30,
+    "EmployeeNoList" : [
+      {
+        "employeeNo": "${employeeNo}"
+      }
+    ]
+  }
+}

+ 7 - 0
ruoyi-admin/resources/conf/acs/SearchFaceInfoParam.json

@@ -0,0 +1,7 @@
+{
+  "searchResultPosition":0,
+  "maxResults":30,
+  "faceLibType":"blackFD",
+  "FDID":"1",
+  "FPID":"${employeeNo}"
+}

+ 8 - 0
ruoyi-admin/resources/conf/acs/SearchFingerPrintParam.json

@@ -0,0 +1,8 @@
+{
+  "FingerPrintCond": {
+    "searchID": "${searchID}",
+    "employeeNo": "${employeeNo}",
+    "cardReaderNo": 1,
+    "fingerPrintID": 1
+  }
+}

+ 8 - 0
ruoyi-admin/resources/conf/acs/SearchUserInfoParam.json

@@ -0,0 +1,8 @@
+
+{
+  "UserInfoSearchCond":{
+    "searchID":"${searchID}",
+    "searchResultPosition":0,
+    "maxResults":${maxResults}
+  }
+}

+ 115 - 0
ruoyi-admin/src/main/java/com/ruoyi/Common/CommonMethod.java

@@ -0,0 +1,115 @@
+package com.ruoyi.Common;
+
+import com.sun.jna.Pointer;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Scanner;
+
+public class CommonMethod {
+    public static void WriteBuffToPointer(byte[] byData, Pointer pInBuffer) {
+        pInBuffer.write(0, byData, 0, byData.length);
+    }
+
+    public static String byteToString(byte[] bytes) {
+        if (null == bytes || bytes.length == 0) {
+            return "";
+        }
+        int iLengthOfBytes = 0;
+        for (byte st : bytes) {
+            if (st != 0) {
+                iLengthOfBytes++;
+            } else
+                break;
+        }
+        String strContent = "";
+        try {
+            strContent = new String(bytes, 0, iLengthOfBytes, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        return strContent;
+    }
+
+    /**
+     * utf8字节数组转gbk字节数组
+     *
+     * @param utf8Bytes
+     * @return
+     */
+    public static byte[] UTF8toGBK(byte[] utf8Bytes) {
+        String utf8Str = new String(utf8Bytes, StandardCharsets.UTF_8);
+        byte[] gbkBytes = utf8Str.getBytes(Charset.forName("GBK"));
+        return gbkBytes;
+    }
+
+    /**
+     * utf8字节数组转gbk字符串
+     *
+     * @param utf8Bytes
+     * @return
+     */
+    public static String UTF8toGBKStr(byte[] utf8Bytes) {
+        return new String(UTF8toGBK(utf8Bytes), Charset.forName("GBK"));
+    }
+
+    /**
+     * 获取resource文件夹下的文件绝对路径
+     *
+     * @param filePath 文件相对于resources文件夹的相对路径, 格式描述举例为 conf/XX/XX.json
+     * @return
+     */
+    public static String getResFileAbsPath(String filePath) {
+        if (filePath == null) {
+            throw new RuntimeException("filePath null error!");
+        }
+        return System.getProperty("user.dir") + "\\ruoyi-admin\\resources\\" + filePath;
+    }
+
+    /**
+     * 获取控制台的一行输入
+     *
+     * @param inputTip 输入提示
+     * @return 控制台的输入信息
+     */
+    public static String getConsoleInput(String inputTip) {
+        System.out.println(inputTip);
+        Scanner scanner = new Scanner(System.in);
+        return scanner.nextLine();
+    }
+
+    /**
+     * 输出信息到文件中
+     *
+     * @param fileName    文件名
+     * @param postFix     文件后缀
+     * @param fileContent 文件内容
+     */
+    public static void outputToFile(String fileName, String postFix, String fileContent) {
+        SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss_SSS");
+
+        String folder = System.getProperty("user.dir") + "\\outputFiles\\event\\";
+        File directory = new File(folder);
+        if (!directory.exists()) {
+            boolean created = directory.mkdirs();
+            if (!created) {
+                System.out.println(folder + "_文件夹创建失败!");
+            }
+        }
+
+        String filePath = folder + fileName + "_" + format.format(new Date()) + postFix;
+        try {
+            FileOutputStream fos = new FileOutputStream(filePath);
+            fos.write(fileContent.getBytes());
+            fos.close();
+        } catch (IOException e) {
+            System.out.println("输出到文件出现异常:" + e.getMessage());
+        }
+    }
+}

+ 83 - 0
ruoyi-admin/src/main/java/com/ruoyi/Common/ConfigFileUtil.java

@@ -0,0 +1,83 @@
+package com.ruoyi.Common;
+
+
+import org.apache.commons.lang3.text.StrSubstitutor;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Map;
+
+/**
+ * @author zhengxiaohui
+ * @date 2023/8/15 19:07
+ * @desc 配置文件处理工具
+ */
+public class ConfigFileUtil {
+
+    /**
+     * 获取请求数据报文内容
+     * @param templateFilePath 报文模板格式文件位置,位于resources文件夹下面(conf/--/--.xx)
+     * @param parameter 模板中可以替换的占位参数信息
+     * @return
+     */
+    public static String getReqBodyFromTemplate(String templateFilePath, Map<String, Object> parameter) {
+        String templateContent = ConfigFileUtil.readFileContent(templateFilePath);
+        return ConfigFileUtil.replace(templateContent, parameter);
+    }
+
+    /**
+     * 读取xml配置文件
+     *
+     * @param filePath 文件相对于resources文件夹的相对路径
+     * @return
+     */
+    public static String readFileContent(String filePath) {
+        String resourcePath = CommonMethod.getResFileAbsPath(filePath);
+
+        // 读取指定文件路径的文件内容
+        String contentStr = "";
+        try {
+            Path path = Paths.get(resourcePath);
+            contentStr = new String(Files.readAllBytes(path));
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return contentStr;
+    }
+
+    /**
+     * 替换 占位符变量固定为 ${}格式
+     *
+     * @param source    源内容
+     * @param parameter 占位符参数
+     *                  <p>
+     *                  转义符默认为'$'。如果这个字符放在一个变量引用之前,这个引用将被忽略,不会被替换 如$${a}将直接输出${a}
+     * @return
+     */
+    public static String replace(String source, Map<String, Object> parameter) {
+        return replace(source, parameter, "${", "}", false);
+    }
+
+    /**
+     * 替换
+     *
+     * @param source                        源内容
+     * @param parameter                     占位符参数
+     * @param prefix                        占位符前缀 例如:${
+     * @param suffix                        占位符后缀 例如:}
+     * @param enableSubstitutionInVariables 是否在变量名称中进行替换 例如:${system-${版本}}
+     *                                      <p>
+     *                                      转义符默认为'$'。如果这个字符放在一个变量引用之前,这个引用将被忽略,不会被替换 如$${a}将直接输出${a}
+     * @return
+     */
+    public static String replace(String source, Map<String, Object> parameter, String prefix, String suffix, boolean enableSubstitutionInVariables) {
+        //StrSubstitutor不是线程安全的类
+        StrSubstitutor strSubstitutor = new StrSubstitutor(parameter, prefix, suffix);
+        //是否在变量名称中进行替换
+        strSubstitutor.setEnableSubstitutionInVariables(enableSubstitutionInVariables);
+        return strSubstitutor.replace(source);
+    }
+
+}

+ 43 - 0
ruoyi-admin/src/main/java/com/ruoyi/alarm/Alarm.java

@@ -8,6 +8,7 @@ import com.ruoyi.Common.osSelect;
 import com.ruoyi.hksdk.HCNetSDK;
 import com.sun.jna.Native;
 import com.sun.jna.Pointer;
+import com.sun.jna.ptr.IntByReference;
 import org.springframework.boot.CommandLineRunner;
 import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Component;
@@ -350,6 +351,48 @@ public class Alarm implements CommandLineRunner {
         return hCNetSDK.NET_DVR_ControlGateway(lUserID, dwCommand,  dwInBufferSize);
     }
 
+
+    /**
+     * 下发人员信息建立长连接
+     *  hCNetSDK.NET_DVR_StartRemoteConfig(lUserID, HCNetSDK.NET_DVR_JSON_CONFIG, ptrByteArray.getPointer(), strInBuffer.length()
+     * @return
+     */
+    public static int startRemote(int userID, int type,Pointer pointer, int length){
+        return hCNetSDK.NET_DVR_StartRemoteConfig(lUserID, type, pointer, length, null, null);
+    }
+
+    /**
+     * 下发人员信息
+     *  hCNetSDK.NET_DVR_SendWithRecvRemoteConfig(lHandler, pbPointer, length, poPointer, bt, pInt)
+     * @return
+     */
+    public static int sendWith(int lHandler, Pointer pbPointer, int length, Pointer poPointer,int bt, IntByReference pInt){
+        return hCNetSDK.NET_DVR_SendWithRecvRemoteConfig(lHandler, pbPointer, length, poPointer, bt, pInt);
+
+    }
+
+    /**
+     * 获取查找的结果信息
+     *  hCNetSDK.NET_DVR_GetNextRemoteConfig(m_lSearchEventHandle, ptrStruEventCfg, struAcsEventCfg.size());
+     * @return
+     */
+    public static int nextRemote(int lHandler, Pointer pbPointer, int size){
+        return hCNetSDK.NET_DVR_GetNextRemoteConfig(lHandler, pbPointer, size);
+
+    }
+
+
+    /**
+     * 下发人员信息结束长连接
+     *  hCNetSDK.NET_DVR_StopRemoteConfig(lHandler)
+     * @return
+     */
+    public static boolean stopRemote(int lHandler){
+        return hCNetSDK.NET_DVR_StopRemoteConfig(lHandler);
+
+    }
+
+
     /**
      * 控制道闸失败
      *

+ 388 - 0
ruoyi-admin/src/main/java/com/ruoyi/web/controller/appointment/BomanReservatController.java

@@ -1,18 +1,41 @@
 package com.ruoyi.web.controller.appointment;
 
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONException;
+import com.alibaba.fastjson2.JSONObject;
+import com.ruoyi.Common.ConfigFileUtil;
+import com.ruoyi.alarm.Alarm;
 import com.ruoyi.common.annotation.Log;
 import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.core.page.TableDataInfo;
+import com.ruoyi.common.core.redis.RedisCache;
 import com.ruoyi.common.enums.BusinessType;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.hksdk.HCNetSDK;
 import com.ruoyi.system.domain.BomanReservat;
+import com.ruoyi.system.domain.EquipmentConfiguration;
 import com.ruoyi.system.service.IBomanReservatService;
+import com.ruoyi.system.service.IEquipmentConfigurationService;
+import com.ruoyi.system.service.IOpeningDoorRecordService;
+import com.sun.jna.ptr.IntByReference;
+import org.apache.commons.lang3.ObjectUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 import javax.servlet.http.HttpServletResponse;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+
+import static com.ruoyi.common.constant.CommonConstants.LOGIN_USER_SMS;
+import static com.ruoyi.common.constant.CommonConstants.OPEN_DOOR;
 
 /**
  * 预约Controller
@@ -26,6 +49,14 @@ public class BomanReservatController extends BaseController {
     @Autowired
     private IBomanReservatService bomanReservatService;
 
+    @Autowired
+    private IEquipmentConfigurationService equipmentConfigurationService;
+    @Autowired
+    private IOpeningDoorRecordService openingDoorRecordService;
+
+    @Autowired
+    private RedisCache redisCache;
+
     /**
      * 查询预约列表
      */
@@ -120,6 +151,363 @@ public class BomanReservatController extends BaseController {
     @PreAuthorize("@ss.hasPermi('system:reservat:sh')")
     @PostMapping("/sh")
     public AjaxResult examine(@RequestBody BomanReservat bomanReservat) {
+
+        //审核通过,下发信息
+        if("3".equals(bomanReservat.getVisitType())){
+            BomanReservat bomanReservat1 = bomanReservatService.selectBomanReservatByReservatId(bomanReservat.getReservatId());
+
+            EquipmentConfiguration equipmentConfiguration = new EquipmentConfiguration();
+            List<EquipmentConfiguration> equipmentConfigurations = equipmentConfigurationService.selectEquipmentConfigurationList(equipmentConfiguration);
+            for (EquipmentConfiguration configuration : equipmentConfigurations) {
+                issueUser(configuration,bomanReservat1);
+            }
+        }
         return bomanReservatService.examine(bomanReservat);
     }
+
+    /**
+     * 下发人员信息
+     * @param equipmentConfiguration 下发的设备信息
+     * @param bomanReservat 下发的人员信息
+     * @return
+     */
+    public void issueUser(EquipmentConfiguration equipmentConfiguration, BomanReservat bomanReservat) {
+        String employeeNo = "tem"+bomanReservat.getReservatId();
+
+        String ip = equipmentConfiguration.getEquipmentIp();
+        short prot = (short) Short.parseShort(equipmentConfiguration.getEquipmentPort());
+        String login = equipmentConfiguration.getLoginName();
+        String pwd = equipmentConfiguration.getLoginPwd();
+
+        int lUserID = Alarm.login_V40( ip, prot, login, pwd);;  //登录设备
+        //Alarm.setAlarm();
+        //String ret="起竿失败!";
+        try {
+            HCNetSDK.BYTE_ARRAY ptrByteArray = new HCNetSDK.BYTE_ARRAY(1024);    //数组
+            String strInBuffer = "POST /ISAPI/AccessControl/UserInfo/Record?format=json"; //此URL也是下发人员
+//        String strInBuffer = "PUT /ISAPI/AccessControl/UserInfo/SetUp?format=json";
+            System.arraycopy(strInBuffer.getBytes(), 0, ptrByteArray.byValue, 0, strInBuffer.length());//字符串拷贝到数组中
+            ptrByteArray.write();
+            //长连接
+            int lHandler = Alarm.startRemote(lUserID,HCNetSDK.NET_DVR_JSON_CONFIG, ptrByteArray.getPointer(), strInBuffer.length());
+            //int lHandler = Alarm.hCNetSDK.NET_DVR_StartRemoteConfig(lUserID, HCNetSDK.NET_DVR_JSON_CONFIG, ptrByteArray.getPointer(), strInBuffer.length(), null, null);
+            if (lHandler < 0) {
+                System.out.println("AddUserInfo NET_DVR_StartRemoteConfig 失败1,错误码为" + Alarm.controlsDzError());
+                return;
+            } else {
+                System.out.println("AddUserInfo NET_DVR_StartRemoteConfig 成功1!");
+
+                //输入参数,XML或者JSON数据,下发人员信息json报文,其他参数设置参考conf/acs/AddUserInfoParam.json中报文参数
+                Map<String, Object> parameter = new HashMap<>();
+                parameter.put("employeeNo", employeeNo); // 员工ID
+                parameter.put("name", bomanReservat.getVisitName()); // 员工名称
+                parameter.put("enable", true); // 是否启用
+                parameter.put("doorNo", 1); // 门编号  ruoyi-admin/resources/conf/acs/AddUserInfoParam.json
+                String input = ConfigFileUtil.getReqBodyFromTemplate("conf/acs/AddUserInfoParam.json", parameter);
+                System.out.println("下发人员参数:"+input);
+
+                byte[] byInbuffer = input.getBytes("utf-8");
+                int iInBufLen = byInbuffer.length;
+
+                HCNetSDK.BYTE_ARRAY ptrInBuffer = new HCNetSDK.BYTE_ARRAY(iInBufLen);
+                ptrInBuffer.read();
+                System.arraycopy(byInbuffer,0,ptrInBuffer.byValue,0, iInBufLen);
+                ptrInBuffer.write();
+                //下发人员信息
+                HCNetSDK.BYTE_ARRAY ptrOutuff = new HCNetSDK.BYTE_ARRAY(1024);
+//            Pointer ptr =new PointerByReference()
+                IntByReference pInt = new IntByReference(0);
+                while (true) {
+                    int dwState = Alarm.sendWith(lHandler, ptrInBuffer.getPointer(), iInBufLen, ptrOutuff.getPointer(), 1024, pInt);
+                    if (dwState == -1) {
+                        System.out.println("NET_DVR_SendWithRecvRemoteConfig接口调用失败1,错误码:" + Alarm.controlsDzError());
+                        break;
+                    }
+                    //读取返回的json并解析
+                    ptrOutuff.read();
+                    String strResult = new String(ptrOutuff.byValue).trim();
+                    System.out.println("dwState:" + dwState + ",strResult:" + strResult);
+                    JSONObject jsonResult = JSON.parseObject(strResult);
+                    int statusCode = (int) jsonResult.get("statusCode");
+                    String statusString = jsonResult.getString("statusString");
+                    if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_NEED_WAIT) {
+                        System.out.println("配置等待");
+                        Thread.sleep(10);
+                        continue;
+                    } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_FAILED) {
+                        System.out.println("下发人员失败1, json retun:" + jsonResult.toString());
+                        break;
+                    } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_EXCEPTION) {
+                        System.out.println("下发人员异常1, json retun:" + jsonResult.toString());
+                        break;
+                    } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_SUCCESS) {//返回NET_SDK_CONFIG_STATUS_SUCCESS代表流程走通了,但并不代表下发成功,比如有些设备可能因为人员已存在等原因下发失败,所以需要解析Json报文
+                        if (statusCode != 1) {
+                            System.out.println("下发人员成功,但是有异常情况1:" + jsonResult.toString());
+                        } else {
+                            System.out.println("下发人员成功: json retun1:" + jsonResult.toString());
+                        }
+                        //按URL方式下发人脸图片
+                        //addFaceByUrl(lUserID,employeeNo,bomanReservat.getHumanFaceData());
+                        addFaceByBinary(lUserID,employeeNo,bomanReservat.getHumanFaceData());
+                        break;
+                    } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_FINISH) {
+                        //下发人员时:dwState其实不会走到这里,因为设备不知道我们会下发多少个人,所以长连接需要我们主动关闭
+                        System.out.println("下发人员完成1");
+                        break;
+                    }
+                }
+                if (!Alarm.stopRemote(lHandler)) {
+                    System.out.println("NET_DVR_StopRemoteConfig接口调用失败,错误码2:" + Alarm.controlsDzError());
+                } else {
+                    System.out.println("NET_DVR_StopRemoteConfig接口成功2");
+                }
+            }
+            //注销用户
+            boolean b=Alarm.logout(lUserID);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+
+    /**
+     * 按URL方式下发人脸图片
+     * @param userID 用户注销ID
+     * @param employeeNo 人员工号
+     * @throws
+     */
+    public static void addFaceByUrl(int userID,String employeeNo,String url) throws JSONException {
+        HCNetSDK.BYTE_ARRAY ptrByteArray = new HCNetSDK.BYTE_ARRAY(1024);    //数组
+        String strInBuffer = "PUT /ISAPI/Intelligent/FDLib/FDSetUp?format=json";
+        System.arraycopy(strInBuffer.getBytes(), 0, ptrByteArray.byValue, 0, strInBuffer.length());//字符串拷贝到数组中
+        ptrByteArray.write();
+        int lHandler = Alarm.startRemote(userID,HCNetSDK.NET_DVR_FACE_DATA_RECORD, ptrByteArray.getPointer(), strInBuffer.length());
+        if (lHandler < 0)
+        {
+            System.out.println("Addface NET_DVR_StartRemoteConfig 失败,错误码为2"+Alarm.controlsDzError());
+            return;
+        }
+        else{
+            System.out.println("Addface NET_DVR_StartRemoteConfig 成功2!");
+
+            HCNetSDK.NET_DVR_JSON_DATA_CFG struAddFaceDataCfg = new HCNetSDK.NET_DVR_JSON_DATA_CFG();
+            struAddFaceDataCfg.read();
+
+            JSONObject jsonObject = new JSONObject();
+            //jsonObject.put("faceURL","http://10.17.34.106:6011/pic?7DD9D70207A9D7F576F99AC197B2D6CAface.jpg"); //人脸图片URL,平台自己实现,需要保证设备可以正常访问
+            jsonObject.put("faceURL","http://10.90.90.53:8065"+url); //人脸图片URL,平台自己实现,需要保证设备可以正常访问
+            jsonObject.put("faceLibType", "blackFD");
+            jsonObject.put("FDID", "fce"+employeeNo);
+            jsonObject.put("FPID", employeeNo);//人脸下发关联的工号
+
+            String strJsonData = jsonObject.toString();
+            System.arraycopy(strJsonData.getBytes(), 0, ptrByteArray.byValue, 0, strJsonData.length());//字符串拷贝到数组中
+            ptrByteArray.write();
+            struAddFaceDataCfg.dwSize = struAddFaceDataCfg.size();
+            struAddFaceDataCfg.lpJsonData = ptrByteArray.getPointer();
+            struAddFaceDataCfg.dwJsonDataSize = strJsonData.length();
+            struAddFaceDataCfg.lpPicData = null;
+            struAddFaceDataCfg.dwPicDataSize=0;
+            struAddFaceDataCfg.write();
+            HCNetSDK.BYTE_ARRAY ptrOutuff = new HCNetSDK.BYTE_ARRAY(1024);
+
+            IntByReference pInt = new IntByReference(0);
+
+            while(true){
+                int dwState = Alarm.sendWith(lHandler, struAddFaceDataCfg.getPointer(), struAddFaceDataCfg.dwSize ,ptrOutuff.getPointer(), 1024,  pInt);
+
+                if(dwState == -1){
+                    System.out.println("NET_DVR_SendWithRecvRemoteConfig接口调用失败,错误码3:" + Alarm.controlsDzError());
+                    break;
+                }
+                //读取返回的json并解析
+                ptrOutuff.read();
+                String strResult = new String(ptrOutuff.byValue).trim();
+                System.out.println("dwState:" + dwState + ",strResult:" + strResult);
+
+                JSONObject jsonResult = JSON.parseObject(strResult);
+                int statusCode = (int) jsonResult.get("statusCode");
+                String statusString = jsonResult.getString("statusString");
+                if(dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_NEED_WAIT)
+                {
+                    System.out.println("配置等待");
+                    try {
+                        Thread.sleep(10);
+                    } catch (InterruptedException e) {
+                        e.printStackTrace();
+                    }
+                    continue;
+                }
+                else if(dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_FAILED)
+                {
+                    System.out.println("下发人脸失败, json retun3:" + jsonResult.toString());
+                    break;
+                }
+                else if(dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_EXCEPTION)
+                {
+                    System.out.println("下发人脸异常, json retun3:" + jsonResult.toString());
+                    break;
+                }
+                else if(dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_SUCCESS)
+                {//返回NET_SDK_CONFIG_STATUS_SUCCESS代表流程走通了,但并不代表下发成功,比如人脸图片不符合设备规范等原因,所以需要解析Json报文
+                    if (statusCode != 1){
+                        System.out.println("下发人脸成功,但是有异常情况3:" + jsonResult.toString());
+                    }
+                    else{
+                        System.out.println("下发人脸成功,  json retun3:" + jsonResult.toString());
+                    }
+                    break;
+                }
+                else if(dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_FINISH) {
+                    //下发人脸时:dwState其实不会走到这里,因为设备不知道我们会下发多少个人,所以长连接需要我们主动关闭
+                    System.out.println("下发人脸完成3");
+                    break;
+                }
+            }
+            if(!Alarm.stopRemote(lHandler)){
+                System.out.println("NET_DVR_StopRemoteConfig接口调用失败,错误码3:" + Alarm.controlsDzError());
+            }
+            else{
+                System.out.println("NET_DVR_StopRemoteConfig接口成功3");
+            }
+        }
+    }
+
+
+    /**
+     * 功能:按照二进制方式下发人脸图片
+     * @param userID  用户注册ID
+     * @param employeeNo 人员工号
+     * @throws JSONException
+     * @throws InterruptedException
+     */
+    public static void addFaceByBinary(int userID,String employeeNo,String url ) throws JSONException, InterruptedException{
+        HCNetSDK.BYTE_ARRAY ptrByteArray = new HCNetSDK.BYTE_ARRAY(1024);    //数组
+        String strInBuffer = "PUT /ISAPI/Intelligent/FDLib/FDSetUp?format=json";
+        System.arraycopy(strInBuffer.getBytes(), 0, ptrByteArray.byValue, 0, strInBuffer.length());//字符串拷贝到数组中
+        ptrByteArray.write();
+
+        int lHandler = Alarm.startRemote(userID,HCNetSDK.NET_DVR_FACE_DATA_RECORD, ptrByteArray.getPointer(), strInBuffer.length());
+        if (lHandler < 0)
+        {
+            System.out.println("Addface NET_DVR_StartRemoteConfig 失败,错误码为"+Alarm.controlsDzError());
+            return;
+        }
+        else{
+            System.out.println("Addface NET_DVR_StartRemoteConfig 成功!");
+
+            HCNetSDK.NET_DVR_JSON_DATA_CFG struAddFaceDataCfg = new HCNetSDK.NET_DVR_JSON_DATA_CFG();
+            struAddFaceDataCfg.read();
+
+            //输入参数,XML或者JSON数据,添加人脸图片json报文
+            Map<String, Object> parameter = new HashMap<>();
+            parameter.put("employeeNo", employeeNo); // 员工ID号
+            String strJsonData = ConfigFileUtil.getReqBodyFromTemplate("conf/acs/AddFaceInfoParam.json", parameter);
+            System.out.println("下个人员参数:"+strJsonData);
+
+            System.arraycopy(strJsonData.getBytes(), 0, ptrByteArray.byValue, 0, strJsonData.length());//字符串拷贝到数组中
+            ptrByteArray.write();
+            struAddFaceDataCfg.dwSize = struAddFaceDataCfg.size();
+            struAddFaceDataCfg.lpJsonData = ptrByteArray.getPointer();
+            struAddFaceDataCfg.dwJsonDataSize = strJsonData.length();
+
+            /*****************************************
+             * 从本地文件里面读取JPEG图片二进制数据
+             *****************************************/
+            FileInputStream picfile = null;
+            int picdataLength = 0;
+            try{
+
+                picfile = new FileInputStream(new File("D:\\ruoyi\\uploadPath\\upload\\2025\\03\\20\\0000000019.jpg"));
+            }
+            catch(FileNotFoundException e)
+            {
+                e.printStackTrace();
+            }
+
+            try{
+                picdataLength = picfile.available();
+            }
+            catch(IOException e1)
+            {
+                e1.printStackTrace();
+            }
+            if(picdataLength < 0)
+            {
+                System.out.println("input file dataSize < 0");
+                return;
+            }
+
+            HCNetSDK.BYTE_ARRAY ptrpicByte = new HCNetSDK.BYTE_ARRAY(picdataLength);
+            try {
+                picfile.read(ptrpicByte.byValue);
+            } catch (IOException e2) {
+                e2.printStackTrace();
+            }
+            ptrpicByte.write();
+            struAddFaceDataCfg.dwPicDataSize  = picdataLength;
+            struAddFaceDataCfg.lpPicData  = ptrpicByte.getPointer();
+            struAddFaceDataCfg.write();
+
+            HCNetSDK.BYTE_ARRAY ptrOutuff = new HCNetSDK.BYTE_ARRAY(1024);
+
+            IntByReference pInt = new IntByReference(0);
+
+            while(true){
+                int dwState = Alarm.sendWith(lHandler, struAddFaceDataCfg.getPointer(), struAddFaceDataCfg.dwSize ,ptrOutuff.getPointer(), 1024,  pInt);
+
+                if(dwState == -1){
+                    System.out.println("NET_DVR_SendWithRecvRemoteConfig接口调用失败,错误码:" + Alarm.controlsDzError());
+                    break;
+                }
+                //读取返回的json并解析
+                ptrOutuff.read();
+                String strResult = new String(ptrOutuff.byValue).trim();
+                System.out.println("dwState:" + dwState + ",strResult:" + strResult);
+
+                JSONObject jsonResult = JSON.parseObject(strResult);
+                int statusCode = (int) jsonResult.get("statusCode");
+                String statusString = jsonResult.getString("statusString");
+                if(dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_NEED_WAIT)
+                {
+                    System.out.println("配置等待");
+                    Thread.sleep(10);
+                    continue;
+                }
+                else if(dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_FAILED)
+                {
+                    System.out.println("下发人脸失败, json retun:" + jsonResult.toString());
+                    break;
+                }
+                else if(dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_EXCEPTION)
+                {
+                    System.out.println("下发人脸异常, json retun:" + jsonResult.toString());
+                    break;
+                }
+                else if(dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_SUCCESS)
+                {//返回NET_SDK_CONFIG_STATUS_SUCCESS代表流程走通了,但并不代表下发成功,比如人脸图片不符合设备规范等原因,所以需要解析Json报文
+                    if (statusCode != 1){
+                        System.out.println("下发人脸成功,但是有异常情况:" + jsonResult.toString());
+                    }
+                    else{
+                        System.out.println("下发人脸成功,  json retun:" + jsonResult.toString());
+                    }
+                    break;
+                }
+                else if(dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_FINISH) {
+                    //下发人脸时:dwState其实不会走到这里,因为设备不知道我们会下发多少个人,所以长连接需要我们主动关闭
+                    System.out.println("下发人脸完成");
+                    break;
+                }
+            }
+            if(!Alarm.stopRemote(lHandler)){
+                System.out.println("NET_DVR_StopRemoteConfig接口调用失败,错误码3:" + Alarm.controlsDzError());
+            }
+            else{
+                System.out.println("NET_DVR_StopRemoteConfig接口成功");
+            }
+        }
+
+    }
+
 }

+ 348 - 0
ruoyi-admin/src/main/java/com/ruoyi/web/controller/camera/CameraController.java

@@ -1,6 +1,9 @@
 package com.ruoyi.web.controller.camera;
 
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONException;
 import com.alibaba.fastjson2.JSONObject;
+import com.ruoyi.Common.ConfigFileUtil;
 import com.ruoyi.alarm.Alarm;
 import com.ruoyi.common.annotation.Log;
 import com.ruoyi.common.core.controller.BaseController;
@@ -9,9 +12,11 @@ import com.ruoyi.common.core.page.TableDataInfo;
 import com.ruoyi.common.core.redis.RedisCache;
 import com.ruoyi.common.enums.BusinessType;
 import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.DateUtils;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.poi.ExcelUtil;
 import com.ruoyi.hksdk.HCNetSDK;
+import com.ruoyi.system.domain.BomanReservat;
 import com.ruoyi.system.domain.EquipmentConfiguration;
 import com.ruoyi.system.domain.OpeningDoorRecord;
 import com.ruoyi.system.domain.PersonnelManagement;
@@ -21,13 +26,21 @@ import com.ruoyi.system.service.IEquipmentConfigurationService;
 import com.ruoyi.system.service.IOpeningDoorRecordService;
 import com.ruoyi.system.service.IPersonnelManagementService;
 import com.sun.jna.Pointer;
+import com.sun.jna.ptr.IntByReference;
 import org.apache.commons.lang3.ObjectUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletResponse;
+import java.io.UnsupportedEncodingException;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import static com.ruoyi.common.constant.CommonConstants.LOGIN_USER_SMS;
 import static com.ruoyi.common.constant.CommonConstants.OPEN_DOOR;
@@ -38,6 +51,8 @@ import static com.ruoyi.common.constant.CommonConstants.OPEN_DOOR;
  * @author ruoyi
  * @date 2024-02-28
  */
+@Configuration
+@EnableScheduling
 @RestController
 @RequestMapping("/camera")
 public class CameraController extends BaseController {
@@ -51,6 +66,18 @@ public class CameraController extends BaseController {
     @Autowired
     private RedisCache redisCache;
 
+
+    /**
+     * 在考勤机器获取考勤数据 一小时一次
+     */
+    @Async
+    @Scheduled(cron = "0 0 * * * ?  ")
+    public void configureTasks() {
+        System.out.println(DateUtils.getNowDate() +"开始执行定时任务");
+        cameraService.configureTasks();
+        System.out.println(DateUtils.getNowDate() +"定时任务结束");
+    }
+
     /**
      * 查询安防设备列表
      */
@@ -248,4 +275,325 @@ public class CameraController extends BaseController {
         return AjaxResult.success(ret);
     }
 
+
+    /**
+     * 下发人员信息
+     * @param equipmentConfiguration 下发的设备信息
+     * @param bomanReservat 下发的人员信息
+     * @return
+     */
+    public void issueUser(EquipmentConfiguration equipmentConfiguration, BomanReservat bomanReservat) {
+        String employeeNo = "tem"+bomanReservat.getReservatId();
+        //验证码校验
+        Object cacheObject = redisCache.getCacheObject(OPEN_DOOR + equipmentConfiguration.getPhoneNumber());
+        if(ObjectUtils.isEmpty(cacheObject) || !equipmentConfiguration.getCode().equals(cacheObject)){
+            throw new ServiceException("验证码错误");
+        }
+        redisCache.deleteObject(LOGIN_USER_SMS + equipmentConfiguration.getPhoneNumber());
+        equipmentConfiguration = equipmentConfigurationService.selectEquipmentConfigurationByEquipmentName(equipmentConfiguration.getEquipmentName());
+        if(StringUtils.isEmpty(equipmentConfiguration.getEquipmentName())){
+            //return AjaxResult.error("设备列表暂无当前设备");
+            return;
+        }
+
+        String ip = equipmentConfiguration.getEquipmentIp();
+        short prot = (short) Short.parseShort(equipmentConfiguration.getEquipmentPort());
+        String login = equipmentConfiguration.getLoginName();
+        String pwd = equipmentConfiguration.getLoginPwd();
+
+        int lUserID = Alarm.login_V40( ip, prot, login, pwd); //登录设备
+        //Alarm.setAlarm();
+        //String ret="起竿失败!";
+        try {
+            HCNetSDK.BYTE_ARRAY ptrByteArray = new HCNetSDK.BYTE_ARRAY(1024);    //数组
+            String strInBuffer = "POST /ISAPI/AccessControl/UserInfo/Record?format=json"; //此URL也是下发人员
+//        String strInBuffer = "PUT /ISAPI/AccessControl/UserInfo/SetUp?format=json";
+            System.arraycopy(strInBuffer.getBytes(), 0, ptrByteArray.byValue, 0, strInBuffer.length());//字符串拷贝到数组中
+            ptrByteArray.write();
+            //长连接
+            int lHandler = Alarm.startRemote(lUserID,HCNetSDK.NET_DVR_JSON_CONFIG, ptrByteArray.getPointer(), strInBuffer.length());
+            //int lHandler = Alarm.hCNetSDK.NET_DVR_StartRemoteConfig(lUserID, HCNetSDK.NET_DVR_JSON_CONFIG, ptrByteArray.getPointer(), strInBuffer.length(), null, null);
+            if (lHandler < 0) {
+                System.out.println("AddUserInfo NET_DVR_StartRemoteConfig 失败,错误码为" + Alarm.controlsDzError());
+                return;
+            } else {
+                System.out.println("AddUserInfo NET_DVR_StartRemoteConfig 成功!");
+
+                //输入参数,XML或者JSON数据,下发人员信息json报文,其他参数设置参考conf/acs/AddUserInfoParam.json中报文参数
+                Map<String, Object> parameter = new HashMap<>();
+                parameter.put("employeeNo", employeeNo); // 员工ID
+                parameter.put("name", bomanReservat.getVisitName()); // 员工名称
+                parameter.put("enable", true); // 是否启用
+                parameter.put("doorNo", 1); // 门编号
+                String input = ConfigFileUtil.getReqBodyFromTemplate("conf/acs/AddUserInfoParam.json", parameter);
+                System.out.println("下发人员参数:"+input);
+
+                byte[] byInbuffer = input.getBytes("utf-8");
+                int iInBufLen = byInbuffer.length;
+
+                HCNetSDK.BYTE_ARRAY ptrInBuffer = new HCNetSDK.BYTE_ARRAY(iInBufLen);
+                ptrInBuffer.read();
+                System.arraycopy(byInbuffer,0,ptrInBuffer.byValue,0, iInBufLen);
+                ptrInBuffer.write();
+                //下发人员信息
+                HCNetSDK.BYTE_ARRAY ptrOutuff = new HCNetSDK.BYTE_ARRAY(1024);
+//            Pointer ptr =new PointerByReference()
+                IntByReference pInt = new IntByReference(0);
+                while (true) {
+                    int dwState = Alarm.sendWith(lHandler, ptrInBuffer.getPointer(), iInBufLen, ptrOutuff.getPointer(), 1024, pInt);
+                    if (dwState == -1) {
+                        System.out.println("NET_DVR_SendWithRecvRemoteConfig接口调用失败,错误码:" + Alarm.controlsDzError());
+                        break;
+                    }
+                    //读取返回的json并解析
+                    ptrOutuff.read();
+                    String strResult = new String(ptrOutuff.byValue).trim();
+                    System.out.println("dwState:" + dwState + ",strResult:" + strResult);
+                    JSONObject jsonResult = JSON.parseObject(strResult);
+                    int statusCode = (int) jsonResult.get("statusCode");
+                    String statusString = jsonResult.getString("statusString");
+                    if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_NEED_WAIT) {
+                        System.out.println("配置等待");
+                        Thread.sleep(10);
+                        continue;
+                    } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_FAILED) {
+                        System.out.println("下发人员失败, json retun:" + jsonResult.toString());
+                        break;
+                    } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_EXCEPTION) {
+                        System.out.println("下发人员异常, json retun:" + jsonResult.toString());
+                        break;
+                    } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_SUCCESS) {//返回NET_SDK_CONFIG_STATUS_SUCCESS代表流程走通了,但并不代表下发成功,比如有些设备可能因为人员已存在等原因下发失败,所以需要解析Json报文
+                        if (statusCode != 1) {
+                            System.out.println("下发人员成功,但是有异常情况:" + jsonResult.toString());
+                        } else {
+                            System.out.println("下发人员成功: json retun:" + jsonResult.toString());
+                        }
+                        //按URL方式下发人脸图片
+                        addFaceByUrl(lUserID,employeeNo,bomanReservat.getHumanFaceData());
+                        break;
+                    } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_FINISH) {
+                        //下发人员时:dwState其实不会走到这里,因为设备不知道我们会下发多少个人,所以长连接需要我们主动关闭
+                        System.out.println("下发人员完成");
+                        break;
+                    }
+                }
+                if (!Alarm.stopRemote(lHandler)) {
+                    System.out.println("NET_DVR_StopRemoteConfig接口调用失败,错误码:" + Alarm.controlsDzError());
+                } else {
+                    System.out.println("NET_DVR_StopRemoteConfig接口成功");
+                }
+            }
+            //注销用户
+            boolean b=Alarm.logout(lUserID);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+
+    /**
+     * 按URL方式下发人脸图片
+     * @param userID 用户注销ID
+     * @param employeeNo 人员工号
+     * @throws
+     */
+    public static void addFaceByUrl(int userID,String employeeNo,String url) throws JSONException {
+        HCNetSDK.BYTE_ARRAY ptrByteArray = new HCNetSDK.BYTE_ARRAY(1024);    //数组
+        String strInBuffer = "PUT /ISAPI/Intelligent/FDLib/FDSetUp?format=json";
+        System.arraycopy(strInBuffer.getBytes(), 0, ptrByteArray.byValue, 0, strInBuffer.length());//字符串拷贝到数组中
+        ptrByteArray.write();
+        int lHandler = Alarm.startRemote(userID,HCNetSDK.NET_DVR_FACE_DATA_RECORD, ptrByteArray.getPointer(), strInBuffer.length());
+        if (lHandler < 0)
+        {
+            System.out.println("Addface NET_DVR_StartRemoteConfig 失败,错误码为"+Alarm.controlsDzError());
+            return;
+        }
+        else{
+            System.out.println("Addface NET_DVR_StartRemoteConfig 成功!");
+
+            HCNetSDK.NET_DVR_JSON_DATA_CFG struAddFaceDataCfg = new HCNetSDK.NET_DVR_JSON_DATA_CFG();
+            struAddFaceDataCfg.read();
+
+            JSONObject jsonObject = new JSONObject();
+            //jsonObject.put("faceURL","http://10.17.34.106:6011/pic?7DD9D70207A9D7F576F99AC197B2D6CAface.jpg"); //人脸图片URL,平台自己实现,需要保证设备可以正常访问
+            jsonObject.put("faceURL",url); //人脸图片URL,平台自己实现,需要保证设备可以正常访问
+            jsonObject.put("faceLibType", "blackFD");
+            jsonObject.put("FDID", "fce"+employeeNo);
+            jsonObject.put("FPID", employeeNo);//人脸下发关联的工号
+
+            String strJsonData = jsonObject.toString();
+            System.arraycopy(strJsonData.getBytes(), 0, ptrByteArray.byValue, 0, strJsonData.length());//字符串拷贝到数组中
+            ptrByteArray.write();
+            struAddFaceDataCfg.dwSize = struAddFaceDataCfg.size();
+            struAddFaceDataCfg.lpJsonData = ptrByteArray.getPointer();
+            struAddFaceDataCfg.dwJsonDataSize = strJsonData.length();
+            struAddFaceDataCfg.lpPicData = null;
+            struAddFaceDataCfg.dwPicDataSize=0;
+            struAddFaceDataCfg.write();
+            HCNetSDK.BYTE_ARRAY ptrOutuff = new HCNetSDK.BYTE_ARRAY(1024);
+
+            IntByReference pInt = new IntByReference(0);
+
+            while(true){
+                int dwState = Alarm.sendWith(lHandler, struAddFaceDataCfg.getPointer(), struAddFaceDataCfg.dwSize ,ptrOutuff.getPointer(), 1024,  pInt);
+
+                if(dwState == -1){
+                    System.out.println("NET_DVR_SendWithRecvRemoteConfig接口调用失败,错误码:" + Alarm.controlsDzError());
+                    break;
+                }
+                //读取返回的json并解析
+                ptrOutuff.read();
+                String strResult = new String(ptrOutuff.byValue).trim();
+                System.out.println("dwState:" + dwState + ",strResult:" + strResult);
+
+                JSONObject jsonResult = JSON.parseObject(strResult);
+                int statusCode = (int) jsonResult.get("statusCode");
+                String statusString = jsonResult.getString("statusString");
+                if(dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_NEED_WAIT)
+                {
+                    System.out.println("配置等待");
+                    try {
+                        Thread.sleep(10);
+                    } catch (InterruptedException e) {
+                        e.printStackTrace();
+                    }
+                    continue;
+                }
+                else if(dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_FAILED)
+                {
+                    System.out.println("下发人脸失败, json retun:" + jsonResult.toString());
+                    break;
+                }
+                else if(dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_EXCEPTION)
+                {
+                    System.out.println("下发人脸异常, json retun:" + jsonResult.toString());
+                    break;
+                }
+                else if(dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_SUCCESS)
+                {//返回NET_SDK_CONFIG_STATUS_SUCCESS代表流程走通了,但并不代表下发成功,比如人脸图片不符合设备规范等原因,所以需要解析Json报文
+                    if (statusCode != 1){
+                        System.out.println("下发人脸成功,但是有异常情况:" + jsonResult.toString());
+                    }
+                    else{
+                        System.out.println("下发人脸成功,  json retun:" + jsonResult.toString());
+                    }
+                    break;
+                }
+                else if(dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_FINISH) {
+                    //下发人脸时:dwState其实不会走到这里,因为设备不知道我们会下发多少个人,所以长连接需要我们主动关闭
+                    System.out.println("下发人脸完成");
+                    break;
+                }
+            }
+            if(!Alarm.stopRemote(lHandler)){
+                System.out.println("NET_DVR_StopRemoteConfig接口调用失败,错误码:" + Alarm.controlsDzError());
+            }
+            else{
+                System.out.println("NET_DVR_StopRemoteConfig接口成功");
+            }
+        }
+    }
+
+
+    /**
+     * 门禁事件查询,基于起止时间,事件类型进行查询
+     * @param
+     * @throws UnsupportedEncodingException
+     * @throws InterruptedException
+     */
+    @GetMapping("/yzm/cs")
+    public void searchAllEvent() throws UnsupportedEncodingException, InterruptedException {
+
+
+        int lUserID = Alarm.login_V40( "60.171.161.56", (short) 18107, "admin", "zxy@7000"); //登录设备
+
+        int i = 0; //事件条数
+        HCNetSDK.NET_DVR_ACS_EVENT_COND struAcsEventCond = new HCNetSDK.NET_DVR_ACS_EVENT_COND();
+        struAcsEventCond.read();
+        struAcsEventCond.dwSize = struAcsEventCond.size();
+        //查询全部主次类型的报警
+        struAcsEventCond.dwMajor = 5; // 主次事件类型设为0,代表查询所有事件
+        struAcsEventCond.dwMinor = 15; //
+        //开始时间
+        struAcsEventCond.struStartTime.dwYear = 2025;
+        struAcsEventCond.struStartTime.dwMonth = 3;
+        struAcsEventCond.struStartTime.dwDay = 01;
+        struAcsEventCond.struStartTime.dwHour = 0;
+        struAcsEventCond.struStartTime.dwMinute = 0;
+        struAcsEventCond.struStartTime.dwSecond = 0;
+        //结束时间
+        struAcsEventCond.struEndTime.dwYear = 2025;
+        struAcsEventCond.struEndTime.dwMonth = 4;
+        struAcsEventCond.struEndTime.dwDay = 9;
+        struAcsEventCond.struEndTime.dwHour = 23;
+        struAcsEventCond.struEndTime.dwMinute = 59;
+        struAcsEventCond.struEndTime.dwSecond = 59;
+        struAcsEventCond.wInductiveEventType = 1;
+        struAcsEventCond.byPicEnable = 0; //是否带图片,0-不带图片,1-带图片
+        struAcsEventCond.write();
+        Pointer ptrStruEventCond = struAcsEventCond.getPointer();
+        int m_lSearchEventHandle = Alarm.startRemote(lUserID,HCNetSDK.NET_DVR_GET_ACS_EVENT, ptrStruEventCond, struAcsEventCond.size());
+        //int m_lSearchEventHandle = AcsMain.hCNetSDK.NET_DVR_StartRemoteConfig(lUserID, HCNetSDK.NET_DVR_GET_ACS_EVENT, ptrStruEventCond, struAcsEventCond.size(), null, null);
+        if (m_lSearchEventHandle<=-1)
+        {
+            System.out.println("NET_DVR_StartRemoteConfig调用失败,错误码:" + Alarm.controlsDzError());
+        }
+        HCNetSDK.NET_DVR_ACS_EVENT_CFG struAcsEventCfg = new HCNetSDK.NET_DVR_ACS_EVENT_CFG();
+        struAcsEventCfg.read();
+        struAcsEventCfg.dwSize = struAcsEventCfg.size();
+        struAcsEventCfg.write();
+        Pointer ptrStruEventCfg = struAcsEventCfg.getPointer();
+        while (true) {
+            System.out.println("i=" + i);
+            int dwEventSearch = Alarm.nextRemote(m_lSearchEventHandle, ptrStruEventCfg, struAcsEventCfg.size());
+            if (dwEventSearch <= -1) {
+                System.out.println("NET_DVR_GetNextRemoteConfig接口调用失败,错误码:" + Alarm.controlsDzError());
+                return;
+            }
+            if (dwEventSearch == HCNetSDK.NET_SDK_GET_NEXT_STATUS_NEED_WAIT) {
+                System.out.println("配置等待....");
+                Thread.sleep(10);
+                continue;
+            } else if (dwEventSearch == HCNetSDK.NET_SDK_NEXT_STATUS__FINISH) {
+                System.out.println("获取事件完成");
+                break;
+            } else if (dwEventSearch == HCNetSDK.NET_SDK_GET_NEXT_STATUS_FAILED) {
+                System.out.println("获取事件出现异常");
+                break;
+            } else if (dwEventSearch == HCNetSDK.NET_SDK_GET_NEXT_STATUS_SUCCESS) {
+                struAcsEventCfg.read();
+                //获取的事件信息通过struAcsEventCfg结构体进行解析,
+                System.out.println(i + "获取事件成功, 报警主类型:" + Integer.toHexString(struAcsEventCfg.dwMajor) + "报警次类型:" + Integer.toHexString(struAcsEventCfg.dwMinor) + "卡号:" + new String(struAcsEventCfg.struAcsEventInfo.byCardNo).trim());
+                /**
+                 * 工号有两个字段,dwEmployeeNo表示工号,当值为0时,表示无效,解析byEmployeeNo参数
+                 */
+/*                System.out.println("工号1:"+struAcsEventCfg.struAcsEventInfo.dwEmployeeNo);
+                System.out.println("工号2:"+new String(struAcsEventCfg.struAcsEventInfo.byEmployeeNo));
+                //人脸温度数据,需要设备和支持测温功能
+                System.out.println("人脸温度:" + struAcsEventCfg.struAcsEventInfo.fCurrTemperature + "是否异常:" + struAcsEventCfg.struAcsEventInfo.byIsAbnomalTemperature);
+                //口罩功能,需要设备支持此功能 0-保留,1-未知,2-不戴口罩,3-戴口罩
+                System.out.println("是否带口罩:"+struAcsEventCfg.struAcsEventInfo.byMask);*/
+                System.out.println("刷卡时间:年:"+struAcsEventCfg.struTime.dwYear+" 月:"+struAcsEventCfg.struTime.dwMonth+" 日:"+struAcsEventCfg.struTime.dwDay+
+                        " 时:"+struAcsEventCfg.struTime.dwHour+" 分:"+struAcsEventCfg.struTime.dwMinute+" 秒:"+struAcsEventCfg.struTime.dwSecond);
+                System.out.println("刷卡信息:"+new String(struAcsEventCfg.struAcsEventInfo.byCardNo));
+
+                //todo 人脸图片保存
+
+                i++;
+                continue;
+            }else{
+                break;
+            }
+        }
+        i = 0;
+        if (!Alarm.stopRemote(m_lSearchEventHandle)) {
+            System.out.println("NET_DVR_StopRemoteConfig接口调用失败,错误码:" + Alarm.controlsDzError());
+        } else {
+            System.out.println("NET_DVR_StopRemoteConfig接口成功");
+        }
+        //注销用户
+        boolean b=Alarm.logout(lUserID);
+        return;
+    }
+
 }

+ 104 - 0
ruoyi-admin/src/main/java/com/ruoyi/web/controller/clock/ClockEquipRecordController.java

@@ -0,0 +1,104 @@
+package com.ruoyi.web.controller.clock;
+
+import java.util.List;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.ruoyi.common.annotation.Log;
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.enums.BusinessType;
+import com.ruoyi.system.domain.ClockEquipRecord;
+import com.ruoyi.system.service.IClockEquipRecordService;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.common.core.page.TableDataInfo;
+
+/**
+ * 设备-打卡记录Controller
+ *
+ * @author boman
+ * @date 2025-03-26
+ */
+@RestController
+@RequestMapping("/record/equip")
+public class ClockEquipRecordController extends BaseController
+{
+    @Autowired
+    private IClockEquipRecordService clockEquipRecordService;
+
+/**
+ * 查询设备-打卡记录列表
+ */
+@PreAuthorize("@ss.hasPermi('system:record:list')")
+@GetMapping("/list")
+    public TableDataInfo list(ClockEquipRecord clockEquipRecord)
+    {
+        startPage();
+        List<ClockEquipRecord> list = clockEquipRecordService.selectClockEquipRecordList(clockEquipRecord);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出设备-打卡记录列表
+     */
+    @PreAuthorize("@ss.hasPermi('system:record:export')")
+    @Log(title = "设备-打卡记录", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(HttpServletResponse response, ClockEquipRecord clockEquipRecord)
+    {
+        List<ClockEquipRecord> list = clockEquipRecordService.selectClockEquipRecordList(clockEquipRecord);
+        ExcelUtil<ClockEquipRecord> util = new ExcelUtil<ClockEquipRecord>(ClockEquipRecord.class);
+        util.exportExcel(response, list, "设备-打卡记录数据");
+    }
+
+    /**
+     * 获取设备-打卡记录详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('system:record:query')")
+    @GetMapping(value = "/{clockId}")
+    public AjaxResult getInfo(@PathVariable("clockId") Long clockId)
+    {
+        return success(clockEquipRecordService.selectClockEquipRecordByClockId(clockId));
+    }
+
+    /**
+     * 新增设备-打卡记录
+     */
+    @PreAuthorize("@ss.hasPermi('system:record:add')")
+    @Log(title = "设备-打卡记录", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody ClockEquipRecord clockEquipRecord)
+    {
+        return toAjax(clockEquipRecordService.insertClockEquipRecord(clockEquipRecord));
+    }
+
+    /**
+     * 修改设备-打卡记录
+     */
+    @PreAuthorize("@ss.hasPermi('system:record:edit')")
+    @Log(title = "设备-打卡记录", businessType = BusinessType.UPDATE)
+    @PostMapping("/put")
+    public AjaxResult edit(@RequestBody ClockEquipRecord clockEquipRecord)
+    {
+        return toAjax(clockEquipRecordService.updateClockEquipRecord(clockEquipRecord));
+    }
+
+    /**
+     * 删除设备-打卡记录
+     */
+    @PreAuthorize("@ss.hasPermi('system:record:remove')")
+    @Log(title = "设备-打卡记录", businessType = BusinessType.DELETE)
+    @GetMapping("/delete/{clockIds}")
+    public AjaxResult remove(@PathVariable Long[] clockIds)
+    {
+        return toAjax(clockEquipRecordService.deleteClockEquipRecordByClockIds(clockIds));
+    }
+}

+ 4 - 0
ruoyi-common/src/main/java/com/ruoyi/common/constant/CommonConstants.java

@@ -58,5 +58,9 @@ public class CommonConstants {
      * 百度ACCESS_TOKEN
      */
     public static final String BAI_DU_ACCESS_TOKEN = "BAI_DU_ACCESS_TOKEN:";
+    /**
+     * 考勤开始时间
+     */
+    public static final String KQ_START_TIME = "kq_start_time:";
 
 }

+ 22 - 0
ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtils.java

@@ -4,6 +4,7 @@ import java.lang.management.ManagementFactory;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.time.*;
+import java.time.format.DateTimeFormatter;
 import java.time.temporal.TemporalAdjusters;
 import java.time.*;
 import java.time.temporal.TemporalAdjuster;
@@ -390,4 +391,25 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
 
     }
 
+    /**
+     * BISO8601时间格式转yyyy-mm-dd HH:mm:ss
+     * @param biso BISO8601时间格式
+     * @return
+     */
+    public static String bDateToString(String biso)
+    {
+        // 定义ISO 8601的日期时间格式器
+        DateTimeFormatter isoFormatter = DateTimeFormatter.ISO_DATE_TIME;
+
+        // 使用ISO格式器解析字符串为LocalDateTime
+        LocalDateTime dateTime = LocalDateTime.parse(biso, isoFormatter);
+
+        // 定义目标日期时间格式器(yyyy-MM-dd HH:mm:ss)
+        DateTimeFormatter customFormatter = DateTimeFormatter.ofPattern(YYYY_MM_DD_HH_MM_SS);
+
+        // 格式化日期时间为目标格式
+        String formattedDateTime = dateTime.format(customFormatter);
+        return formattedDateTime;
+    }
+
 }

+ 1 - 1
ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java

@@ -112,7 +112,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
                 .authorizeRequests()
                 // 对于登录login 注册register 验证码captchaImage 允许匿名访问
                 .antMatchers("/login", "/register", "/captchaImage","/camera/yzm/openDz","/sendSms/sendRegisterSms").permitAll()
-                .antMatchers("/camera/open/video/**","/camera/open/list").permitAll()
+                .antMatchers("/camera/open/video/**","/camera/open/list","/camera/yzm/cs").permitAll()
                 // 静态资源,可匿名访问
                 .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
                 .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**","/system/app/new").permitAll()

+ 238 - 0
ruoyi-system/src/main/java/com/ruoyi/system/domain/ClockEquipRecord.java

@@ -0,0 +1,238 @@
+package com.ruoyi.system.domain;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.ruoyi.common.annotation.Excel;
+import com.ruoyi.common.core.domain.BaseEntity;
+
+/**
+ * 设备-打卡记录对象 clock_equip_record
+ * 
+ * @author boman
+ * @date 2025-03-26
+ */
+public class ClockEquipRecord extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 打卡记录id */
+    private Long clockId;
+
+    /** 人员ID */
+    @Excel(name = "人员ID")
+    private Long userId;
+
+    /** 所属部门名称 */
+    @Excel(name = "所属部门名称")
+    private String deptName;
+
+    /** 人员姓名 */
+    @Excel(name = "人员姓名")
+    private String userName;
+
+    /** 手机号码 */
+    @Excel(name = "手机号码")
+    private String phonenumber;
+
+    /** 人员身份证号 */
+    @Excel(name = "人员身份证号")
+    private String idCard;
+
+    /** 周几(1:星期一,2:星期二,3:星期三,4:星期四,5:星期五,6:星期六,7:星期日) */
+    @Excel(name = "周几", readConverterExp = "1=:星期一,2:星期二,3:星期三,4:星期四,5:星期五,6:星期六,7:星期日")
+    private String week;
+
+    /** 打卡记录完整时间(yyyy-MM-dd HH:mm:ss) */
+    @Excel(name = "打卡记录完整时间", readConverterExp = "y=yyy-MM-dd,H=H:mm:ss")
+    private String recordDt;
+
+    /** 打卡记录日期(yyyy-mm-dd) */
+    @Excel(name = "打卡记录日期", readConverterExp = "y=yyy-mm-dd")
+    private String recordDate;
+
+    /** 打卡记录时间(HH:mm:ss) */
+    @Excel(name = "打卡记录时间", readConverterExp = "H=H:mm:ss")
+    private String recordTime;
+
+    /** 打卡地点 */
+    @Excel(name = "打卡地点")
+    private String recordLocations;
+
+    /** 打卡情况  1:正常打卡,2:外勤打卡,3:迟到,4:早退 */
+    @Excel(name = "打卡情况  1:正常打卡,2:外勤打卡,3:迟到,4:早退")
+    private String isOutwork;
+
+    /** 图片地址 */
+    @Excel(name = "图片地址")
+    private String pictureUrl;
+
+    /** 上班,下班打卡 1:上班,2:下班 */
+    @Excel(name = "上班,下班打卡 1:上班,2:下班")
+    private String type;
+
+    /** 是否有效, 1:有效,2:无效 */
+    @Excel(name = "是否有效, 1:有效,2:无效")
+    private String effective;
+
+    public void setClockId(Long clockId) 
+    {
+        this.clockId = clockId;
+    }
+
+    public Long getClockId() 
+    {
+        return clockId;
+    }
+    public void setUserId(Long userId) 
+    {
+        this.userId = userId;
+    }
+
+    public Long getUserId() 
+    {
+        return userId;
+    }
+    public void setDeptName(String deptName)
+    {
+        this.deptName = deptName;
+    }
+
+    public String getDeptName()
+    {
+        return deptName;
+    }
+    public void setUserName(String userName) 
+    {
+        this.userName = userName;
+    }
+
+    public String getUserName() 
+    {
+        return userName;
+    }
+    public void setPhonenumber(String phonenumber) 
+    {
+        this.phonenumber = phonenumber;
+    }
+
+    public String getPhonenumber() 
+    {
+        return phonenumber;
+    }
+    public void setIdCard(String idCard) 
+    {
+        this.idCard = idCard;
+    }
+
+    public String getIdCard() 
+    {
+        return idCard;
+    }
+    public void setWeek(String week) 
+    {
+        this.week = week;
+    }
+
+    public String getWeek() 
+    {
+        return week;
+    }
+    public void setRecordDt(String recordDt) 
+    {
+        this.recordDt = recordDt;
+    }
+
+    public String getRecordDt() 
+    {
+        return recordDt;
+    }
+    public void setRecordDate(String recordDate) 
+    {
+        this.recordDate = recordDate;
+    }
+
+    public String getRecordDate() 
+    {
+        return recordDate;
+    }
+    public void setRecordTime(String recordTime) 
+    {
+        this.recordTime = recordTime;
+    }
+
+    public String getRecordTime() 
+    {
+        return recordTime;
+    }
+    public void setRecordLocations(String recordLocations) 
+    {
+        this.recordLocations = recordLocations;
+    }
+
+    public String getRecordLocations() 
+    {
+        return recordLocations;
+    }
+    public void setIsOutwork(String isOutwork) 
+    {
+        this.isOutwork = isOutwork;
+    }
+
+    public String getIsOutwork() 
+    {
+        return isOutwork;
+    }
+    public void setPictureUrl(String pictureUrl) 
+    {
+        this.pictureUrl = pictureUrl;
+    }
+
+    public String getPictureUrl() 
+    {
+        return pictureUrl;
+    }
+    public void setType(String type) 
+    {
+        this.type = type;
+    }
+
+    public String getType() 
+    {
+        return type;
+    }
+    public void setEffective(String effective) 
+    {
+        this.effective = effective;
+    }
+
+    public String getEffective() 
+    {
+        return effective;
+    }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+            .append("clockId", getClockId())
+            .append("userId", getUserId())
+            .append("deptName", getDeptName())
+            .append("userName", getUserName())
+            .append("phonenumber", getPhonenumber())
+            .append("idCard", getIdCard())
+            .append("week", getWeek())
+            .append("recordDt", getRecordDt())
+            .append("recordDate", getRecordDate())
+            .append("recordTime", getRecordTime())
+            .append("recordLocations", getRecordLocations())
+            .append("isOutwork", getIsOutwork())
+            .append("pictureUrl", getPictureUrl())
+            .append("type", getType())
+            .append("effective", getEffective())
+            .append("createBy", getCreateBy())
+            .append("createTime", getCreateTime())
+            .append("updateBy", getUpdateBy())
+            .append("updateTime", getUpdateTime())
+            .append("remark", getRemark())
+            .toString();
+    }
+}

+ 15 - 4
ruoyi-system/src/main/java/com/ruoyi/system/domain/ClockRecord.java

@@ -24,7 +24,7 @@ public class ClockRecord extends BaseEntity
 
     /** 所属部门名称 */
     @Excel(name = "所属部门名称")
-    private Long deptName;
+    private String deptName;
 
     /** 人员姓名 */
     @Excel(name = "人员姓名")
@@ -73,7 +73,18 @@ public class ClockRecord extends BaseEntity
     @Excel(name = "上班,下班打卡", readConverterExp = "1=上班,2=下班")
     private String type;
 
-    public void setClockId(Long clockId) 
+    /** 数据来源*/
+    private String source;
+
+    public String getSource() {
+        return source;
+    }
+
+    public void setSource(String source) {
+        this.source = source;
+    }
+
+    public void setClockId(Long clockId)
     {
         this.clockId = clockId;
     }
@@ -91,12 +102,12 @@ public class ClockRecord extends BaseEntity
     {
         return userId;
     }
-    public void setDeptName(Long deptName) 
+    public void setDeptName(String deptName)
     {
         this.deptName = deptName;
     }
 
-    public Long getDeptName() 
+    public String getDeptName()
     {
         return deptName;
     }

+ 12 - 0
ruoyi-system/src/main/java/com/ruoyi/system/domain/DutySchedule.java

@@ -45,6 +45,10 @@ public class DutySchedule extends BaseEntity
     @Excel(name = "下班时间")
     private String endWorkTime;
 
+    /** 是否打卡机使用 N:否,Y:是 */
+    @Excel(name = "是否打卡机使用 N:否,Y:是")
+    private String isEquip;
+
     public void setDutyId(Long dutyId) 
     {
         this.dutyId = dutyId;
@@ -113,6 +117,14 @@ public class DutySchedule extends BaseEntity
         this.locations = locations;
     }
 
+    public String getIsEquip() {
+        return isEquip;
+    }
+
+    public void setIsEquip(String isEquip) {
+        this.isEquip = isEquip;
+    }
+
     @Override
     public String toString() {
         return "DutySchedule{" +

+ 66 - 0
ruoyi-system/src/main/java/com/ruoyi/system/mapper/ClockEquipRecordMapper.java

@@ -0,0 +1,66 @@
+package com.ruoyi.system.mapper;
+
+import java.util.List;
+import com.ruoyi.system.domain.ClockEquipRecord;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * 设备-打卡记录Mapper接口
+ * 
+ * @author boman
+ * @date 2025-03-26
+ */
+public interface ClockEquipRecordMapper 
+{
+    /**
+     * 查询设备-打卡记录
+     * 
+     * @param clockId 设备-打卡记录主键
+     * @return 设备-打卡记录
+     */
+    public ClockEquipRecord selectClockEquipRecordByClockId(Long clockId);
+
+    /**
+     * 查询设备-打卡记录列表
+     * 
+     * @param clockEquipRecord 设备-打卡记录
+     * @return 设备-打卡记录集合
+     */
+    public List<ClockEquipRecord> selectClockEquipRecordList(ClockEquipRecord clockEquipRecord);
+
+    /**
+     * 新增设备-打卡记录
+     * 
+     * @param clockEquipRecord 设备-打卡记录
+     * @return 结果
+     */
+    public int insertClockEquipRecord(ClockEquipRecord clockEquipRecord);
+
+    /**
+     * 修改设备-打卡记录
+     * 
+     * @param clockEquipRecord 设备-打卡记录
+     * @return 结果
+     */
+    public int updateClockEquipRecord(ClockEquipRecord clockEquipRecord);
+
+    /**
+     * 删除设备-打卡记录
+     * 
+     * @param clockId 设备-打卡记录主键
+     * @return 结果
+     */
+    public int deleteClockEquipRecordByClockId(Long clockId);
+
+    /**
+     * 批量删除设备-打卡记录
+     * 
+     * @param clockIds 需要删除的数据主键集合
+     * @return 结果
+     */
+    public int deleteClockEquipRecordByClockIds(Long[] clockIds);
+
+    void updateClockEquipRecordEffective(@Param("day")String day, @Param("personName")String personName);
+
+    ClockEquipRecord selectClockEquipRecordNew(Long userId);
+}

+ 3 - 0
ruoyi-system/src/main/java/com/ruoyi/system/mapper/ClockUserInfoMapper.java

@@ -2,6 +2,7 @@ package com.ruoyi.system.mapper;
 
 import java.util.List;
 import com.ruoyi.system.domain.ClockUserInfo;
+import org.apache.ibatis.annotations.Param;
 
 /**
  * 打卡人员信息Mapper接口
@@ -58,4 +59,6 @@ public interface ClockUserInfoMapper
      * @return 结果
      */
     public int deleteClockUserInfoByUserIds(Long[] userIds);
+
+    public ClockUserInfo selectClockUserInfoByName(@Param("userName")String userName);
 }

+ 2 - 0
ruoyi-system/src/main/java/com/ruoyi/system/service/CameraService.java

@@ -8,4 +8,6 @@ public interface CameraService {
     AjaxResult selectCameraList(CameraInfoVo cameraInfoVo);
 
     AjaxResult video(String cameraIndexCode);
+
+    void configureTasks();
 }

+ 61 - 0
ruoyi-system/src/main/java/com/ruoyi/system/service/IClockEquipRecordService.java

@@ -0,0 +1,61 @@
+package com.ruoyi.system.service;
+
+import java.util.List;
+import com.ruoyi.system.domain.ClockEquipRecord;
+
+/**
+ * 设备-打卡记录Service接口
+ * 
+ * @author boman
+ * @date 2025-03-26
+ */
+public interface IClockEquipRecordService 
+{
+    /**
+     * 查询设备-打卡记录
+     * 
+     * @param clockId 设备-打卡记录主键
+     * @return 设备-打卡记录
+     */
+    public ClockEquipRecord selectClockEquipRecordByClockId(Long clockId);
+
+    /**
+     * 查询设备-打卡记录列表
+     * 
+     * @param clockEquipRecord 设备-打卡记录
+     * @return 设备-打卡记录集合
+     */
+    public List<ClockEquipRecord> selectClockEquipRecordList(ClockEquipRecord clockEquipRecord);
+
+    /**
+     * 新增设备-打卡记录
+     * 
+     * @param clockEquipRecord 设备-打卡记录
+     * @return 结果
+     */
+    public int insertClockEquipRecord(ClockEquipRecord clockEquipRecord);
+
+    /**
+     * 修改设备-打卡记录
+     * 
+     * @param clockEquipRecord 设备-打卡记录
+     * @return 结果
+     */
+    public int updateClockEquipRecord(ClockEquipRecord clockEquipRecord);
+
+    /**
+     * 批量删除设备-打卡记录
+     * 
+     * @param clockIds 需要删除的设备-打卡记录主键集合
+     * @return 结果
+     */
+    public int deleteClockEquipRecordByClockIds(Long[] clockIds);
+
+    /**
+     * 删除设备-打卡记录信息
+     * 
+     * @param clockId 设备-打卡记录主键
+     * @return 结果
+     */
+    public int deleteClockEquipRecordByClockId(Long clockId);
+}

+ 214 - 2
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/CameraServiceImpl.java

@@ -5,19 +5,31 @@ import com.alibaba.fastjson2.JSONArray;
 import com.alibaba.fastjson2.JSONObject;
 import com.alibaba.fastjson2.TypeReference;
 import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.redis.RedisCache;
 import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.DateUtils;
 import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.hkws.GetCameraPreviewURL;
-import com.ruoyi.system.domain.EquipmentConfiguration;
+import com.ruoyi.system.domain.*;
 import com.ruoyi.system.domain.vo.CameraInfoVo;
-import com.ruoyi.system.mapper.EquipmentConfigurationMapper;
+import com.ruoyi.system.mapper.*;
 import com.ruoyi.system.service.CameraService;
+import com.ruoyi.system.service.IClockEquipRecordService;
+import lombok.SneakyThrows;
 import org.apache.poi.ss.usermodel.DateUtil;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import java.time.OffsetDateTime;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
 import java.util.*;
+import java.util.stream.Collectors;
+
+import static com.ruoyi.common.constant.CommonConstants.KQ_START_TIME;
+import static com.ruoyi.common.constant.CommonConstants.OPEN_DOOR;
 
 
 /**
@@ -31,6 +43,19 @@ public class CameraServiceImpl implements CameraService {
 
     @Autowired
     private EquipmentConfigurationMapper equipmentConfigurationMapper;
+    @Autowired
+    private DutyScheduleMapper dutyScheduleMapper;
+    @Autowired
+    private IClockEquipRecordService clockEquipRecordService;
+    @Autowired
+    private ClockEquipRecordMapper clockEquipRecordMapper;
+    @Autowired
+    private ClockUserInfoMapper clockUserInfoMapper;
+    @Autowired
+    private RedisCache redisCache;
+
+    @Autowired
+    private ClockRecordMapper clockRecordMapper;
 
     @Override
     public AjaxResult selectCameraList(CameraInfoVo cameraInfoVo) {
@@ -100,4 +125,191 @@ public class CameraServiceImpl implements CameraService {
         JSONObject jsonObject = JSON.parseObject(result);
         return AjaxResult.success(jsonObject);
     }
+
+    @SneakyThrows
+    @Override
+    public void configureTasks() {
+
+        //查询设备  name -> 1号楼2F-
+
+        /**
+         * STEP3:查询门禁点事件v2
+         */
+        String url = "/api/acs/v2/door/events";
+
+        /**
+         * STEP5:组装请求参数
+         */
+        /**
+         * STEP3:设置接口的URI地址
+         * indexCode -> c9a07d45adc247908d73d4d3a41470a4  name -> 1号楼2F-2_门_1
+         * indexCode -> 86e6e58f29fe44ad80b18a0bc3a24080  name -> 1号楼2F-1_门_1
+         * indexCode -> 5490b0c100dd4367a8fb548b1de33043  name -> 5号楼1F_门_1
+         * indexCode -> 0e530bc2f5234929b6bf5562e1e4932c  name -> 后门_门_1
+         * */
+        String startTime = redisCache.getCacheObject(KQ_START_TIME);
+        if(StringUtils.isEmpty(startTime)){
+            startTime = "2025-01-01T00:00:00+08:00";
+        }
+
+        ZoneOffset offset = ZoneOffset.of("+08:00"); // 中国时区偏移量
+        ZonedDateTime now = ZonedDateTime.now(offset); // 注意这里用的是ZoneOffset而不是ZoneId
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX"); // XXX 表示时区偏移量,但不显示区域名称如Asia/Shanghai
+        String formatted = now.format(formatter);
+        int[] eventTypes = {196893};
+        String[] doorIndexCodes = {"c9a07d45adc247908d73d4d3a41470a4","86e6e58f29fe44ad80b18a0bc3a24080"};
+        JSONObject jsonBody = new JSONObject();
+        jsonBody.put("pageNo", 1);
+        jsonBody.put("pageSize", "1000");
+        jsonBody.put("doorIndexCodes", doorIndexCodes);
+        jsonBody.put("eventTypes", eventTypes);
+        jsonBody.put("startTime", startTime);
+        jsonBody.put("endTime", formatted);
+        //jsonBody.put("doorName", "考勤");
+        jsonBody.put("sort", "eventTime");
+        jsonBody.put("order", "asc");
+
+        String result = GetCameraPreviewURL.GetCameraPreviewURL(url,jsonBody.toString());
+        JSONObject jsonObject = JSON.parseObject(result);
+        JSONObject data = (JSONObject) jsonObject.get("data");
+        JSONArray itemsArray = data.getJSONArray("list");
+        List<JSONObject> itemsList = itemsArray.toJavaList(JSONObject.class);
+        List<JSONObject> accessList = new ArrayList<>();
+        for (JSONObject object : itemsList) {
+            String name = (String)object.get("personName");
+            if(StringUtils.isNotEmpty(name)){
+                accessList.add(object);
+            }
+        }
+        if(accessList==null){
+            return;
+        }
+
+        Map<String,List<JSONObject>> map = accessList.stream().collect(Collectors.groupingBy(obj -> obj.getString("personName")));
+        //查询打卡时间
+        DutySchedule dutySchedule = new DutySchedule();
+        dutySchedule.setIsEquip("Y");
+        List<DutySchedule> dutySchedules = dutyScheduleMapper.selectDutyScheduleList(dutySchedule);
+        if(dutySchedules==null || dutySchedules.size()<1){
+            return;
+        }
+        dutySchedule = dutySchedules.get(0);
+        for (String key : map.keySet()) {
+            //查询人员的信息
+            ClockUserInfo clockUserInfo = clockUserInfoMapper.selectClockUserInfoByName(key);
+
+            List<JSONObject> keyValue = map.get(key);
+            for (int i = 0; i < keyValue.size(); i++) {
+                JSONObject object = map.get(key).get(i);
+                if(object.get("eventTime").equals(startTime)){
+                    continue;
+                }
+                String dateTime = DateUtils.bDateToString((String) object.get("eventTime"));
+                ClockRecord clockEquipRecord = new ClockRecord();
+                if(clockUserInfo!=null){
+                    clockEquipRecord.setUserId(clockUserInfo.getUserId());
+                    clockEquipRecord.setDeptName(clockUserInfo.getDeptName());
+                    clockEquipRecord.setPhonenumber(clockUserInfo.getPhonenumber());
+                    clockEquipRecord.setIdCard(clockUserInfo.getIdCard());
+                }
+                //clockEquipRecord.setUserName((String) object.get("personId"));
+                clockEquipRecord.setUserName((String) object.get("personName"));
+                //clockEquipRecord.setIdCard((String) object.get("certNo"));
+                clockEquipRecord.setWeek(String.valueOf(DateUtils.dayForWeek(dateTime)));
+                clockEquipRecord.setRecordDt(dateTime);
+                String[] dt = dateTime.split(" ");
+                clockEquipRecord.setRecordDate(dt[0]);
+                clockEquipRecord.setRecordTime(dt[1]);
+                clockEquipRecord.setRecordLocations((String) object.get("readerDevName"));
+                //判断是否是正常打卡  1:正常打卡,2:外勤打卡,3:迟到,4:早退',
+
+                //查询库里是否有key的数据,有的话就说明不是上班卡,没有的话就是第一个卡
+                ClockRecord clockEquip = new ClockRecord();
+                clockEquip.setUserName((String) object.get("personName"));
+                clockEquip.setRecordDate(dt[0]);
+                List<ClockRecord> clockEquipRecords = clockRecordMapper.selectClockRecordList(clockEquip);
+                //里面存在数据
+                if(clockEquipRecords!=null && clockEquipRecords.size()>0){
+                    //先将之前的的下班卡改为无效(名字)
+                    //clockEquipRecordMapper.updateClockEquipRecordEffective(dt[0],(String) object.get("personName"));
+                    clockEquipRecord.setType("2");
+                    //clockEquipRecord.setEffective("1");
+                    //判断下班是否早退
+                    int res = dt[1].compareTo(dutySchedule.getEndWorkTime());
+                    if(res>0 || res==0){
+                        //正常打卡
+                        clockEquipRecord.setIsOutwork("1");
+                    }else{
+                        clockEquipRecord.setIsOutwork("4");
+                    }
+
+                }else{
+                    //不存在数据
+                    clockEquipRecord.setType("1");
+                    //clockEquipRecord.setEffective("1");
+                    //判断上班是否迟到
+                    int res = dt[1].compareTo(dutySchedule.getStartWorkTime());
+                    if(res>0 || res==0){
+                        //迟到
+                        clockEquipRecord.setIsOutwork("3");
+                    }else{
+                        clockEquipRecord.setIsOutwork("1");
+                    }
+                }
+                clockEquipRecord.setSource("设备");
+                clockEquipRecord.setCreateTime(DateUtils.getNowDate());
+                clockRecordMapper.insertClockRecord(clockEquipRecord);
+            }
+
+        }
+        //将最后一条有效数据时间存入redis
+        redisCache.setCacheObject(KQ_START_TIME,accessList.get(accessList.size()-1).get("eventTime"));
+
+
+
+    }
+
+    /*public static void main(String[] args) {
+
+        // 获取当前时间
+        ZoneOffset offset = ZoneOffset.of("+08:00"); // 中国时区偏移量
+        ZonedDateTime now = ZonedDateTime.now(offset); // 注意这里用的是ZoneOffset而不是ZoneId
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX"); // XXX 表示时区偏移量,但不显示区域名称如Asia/Shanghai
+        String formatted = now.format(formatter);
+        System.out.println(formatted);
+        // 打印ISO 8601格式的当前时间
+        //System.out.println(now);
+
+
+
+        //String url = "/api/resource/v2/door/search";
+        String url = "/api/acs/v2/door/events";
+
+
+
+        int[] eventTypes = {196893};
+        String[] doorIndexCodes = {"c9a07d45adc247908d73d4d3a41470a4","86e6e58f29fe44ad80b18a0bc3a24080"};
+        JSONObject jsonBody = new JSONObject();
+        jsonBody.put("pageNo", 1);
+        jsonBody.put("pageSize", "1000");
+        jsonBody.put("doorIndexCodes", doorIndexCodes);
+        jsonBody.put("eventTypes", eventTypes);
+        //jsonBody.put("doorName", "考勤");
+
+        String result = GetCameraPreviewURL.GetCameraPreviewURL(url,jsonBody.toString());
+        JSONObject jsonObject = JSON.parseObject(result);
+        JSONObject data = (JSONObject) jsonObject.get("data");
+        JSONArray itemsArray = data.getJSONArray("list");
+        List<JSONObject> itemsList = itemsArray.toJavaList(JSONObject.class);
+        Map<String,List<JSONObject>> map = itemsList.stream().collect(Collectors.groupingBy(obj -> obj.getString("personId")));
+        List<JSONObject> accessList = new ArrayList<>();
+        for (JSONObject object : itemsList) {
+            String name = (String)object.get("doorName");
+            if(name.contains("考勤")){
+                accessList.add(object);
+            }
+        }
+
+        System.out.println(accessList.toString());
+    }*/
 }

+ 96 - 0
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ClockEquipRecordServiceImpl.java

@@ -0,0 +1,96 @@
+package com.ruoyi.system.service.impl;
+
+import java.util.List;
+import com.ruoyi.common.utils.DateUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.ruoyi.system.mapper.ClockEquipRecordMapper;
+import com.ruoyi.system.domain.ClockEquipRecord;
+import com.ruoyi.system.service.IClockEquipRecordService;
+
+/**
+ * 设备-打卡记录Service业务层处理
+ * 
+ * @author boman
+ * @date 2025-03-26
+ */
+@Service
+public class ClockEquipRecordServiceImpl implements IClockEquipRecordService 
+{
+    @Autowired
+    private ClockEquipRecordMapper clockEquipRecordMapper;
+
+    /**
+     * 查询设备-打卡记录
+     * 
+     * @param clockId 设备-打卡记录主键
+     * @return 设备-打卡记录
+     */
+    @Override
+    public ClockEquipRecord selectClockEquipRecordByClockId(Long clockId)
+    {
+        return clockEquipRecordMapper.selectClockEquipRecordByClockId(clockId);
+    }
+
+    /**
+     * 查询设备-打卡记录列表
+     * 
+     * @param clockEquipRecord 设备-打卡记录
+     * @return 设备-打卡记录
+     */
+    @Override
+    public List<ClockEquipRecord> selectClockEquipRecordList(ClockEquipRecord clockEquipRecord)
+    {
+        return clockEquipRecordMapper.selectClockEquipRecordList(clockEquipRecord);
+    }
+
+    /**
+     * 新增设备-打卡记录
+     * 
+     * @param clockEquipRecord 设备-打卡记录
+     * @return 结果
+     */
+    @Override
+    public int insertClockEquipRecord(ClockEquipRecord clockEquipRecord)
+    {
+        clockEquipRecord.setCreateTime(DateUtils.getNowDate());
+        return clockEquipRecordMapper.insertClockEquipRecord(clockEquipRecord);
+    }
+
+    /**
+     * 修改设备-打卡记录
+     * 
+     * @param clockEquipRecord 设备-打卡记录
+     * @return 结果
+     */
+    @Override
+    public int updateClockEquipRecord(ClockEquipRecord clockEquipRecord)
+    {
+        clockEquipRecord.setUpdateTime(DateUtils.getNowDate());
+        return clockEquipRecordMapper.updateClockEquipRecord(clockEquipRecord);
+    }
+
+    /**
+     * 批量删除设备-打卡记录
+     * 
+     * @param clockIds 需要删除的设备-打卡记录主键
+     * @return 结果
+     */
+    @Override
+    public int deleteClockEquipRecordByClockIds(Long[] clockIds)
+    {
+        return clockEquipRecordMapper.deleteClockEquipRecordByClockIds(clockIds);
+    }
+
+    /**
+     * 删除设备-打卡记录信息
+     * 
+     * @param clockId 设备-打卡记录主键
+     * @return 结果
+     */
+    @Override
+    public int deleteClockEquipRecordByClockId(Long clockId)
+    {
+        return clockEquipRecordMapper.deleteClockEquipRecordByClockId(clockId);
+    }
+}

+ 3 - 0
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ClockRecordServiceImpl.java

@@ -37,6 +37,8 @@ public class ClockRecordServiceImpl implements IClockRecordService {
     private DutyScheduleMapper dutyScheduleMapper;
     @Autowired
     private ClockHolidaysMapper clockHolidaysMapper;
+    @Autowired
+    private ClockEquipRecordMapper clockEquipRecordMapper;
 
     /**
      * 查询打卡记录
@@ -98,6 +100,7 @@ public class ClockRecordServiceImpl implements IClockRecordService {
         } catch (Exception e) {
             e.printStackTrace();
         }
+        clockRecord.setSource("软件");
         clockRecord.setCreateTime(DateUtils.getNowDate());
         return clockRecordMapper.insertClockRecord(clockRecord);
     }

+ 151 - 0
ruoyi-system/src/main/resources/mapper/system/ClockEquipRecordMapper.xml

@@ -0,0 +1,151 @@
+<?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.ruoyi.system.mapper.ClockEquipRecordMapper">
+    
+    <resultMap type="ClockEquipRecord" id="ClockEquipRecordResult">
+        <result property="clockId"    column="clock_id"    />
+        <result property="userId"    column="user_id"    />
+        <result property="deptName"    column="dept_name"    />
+        <result property="userName"    column="user_name"    />
+        <result property="phonenumber"    column="phonenumber"    />
+        <result property="idCard"    column="id_card"    />
+        <result property="week"    column="week"    />
+        <result property="recordDt"    column="record_dt"    />
+        <result property="recordDate"    column="record_date"    />
+        <result property="recordTime"    column="record_time"    />
+        <result property="recordLocations"    column="record_locations"    />
+        <result property="isOutwork"    column="is_outwork"    />
+        <result property="pictureUrl"    column="picture_url"    />
+        <result property="type"    column="type"    />
+        <result property="effective"    column="effective"    />
+        <result property="createBy"    column="create_by"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="updateBy"    column="update_by"    />
+        <result property="updateTime"    column="update_time"    />
+        <result property="remark"    column="remark"    />
+    </resultMap>
+
+    <sql id="selectClockEquipRecordVo">
+        select clock_id, user_id, dept_name, user_name, phonenumber, id_card, week, record_dt, record_date, record_time, record_locations, is_outwork, picture_url, type, effective, create_by, create_time, update_by, update_time, remark from clock_equip_record
+    </sql>
+
+    <select id="selectClockEquipRecordList" parameterType="ClockEquipRecord" resultMap="ClockEquipRecordResult">
+        <include refid="selectClockEquipRecordVo"/>
+        <where>  
+            <if test="userId != null "> and user_id = #{userId}</if>
+            <if test="deptName != null "> and dept_name like concat('%', #{deptName}, '%')</if>
+            <if test="userName != null  and userName != ''"> and user_name like concat('%', #{userName}, '%')</if>
+            <if test="phonenumber != null  and phonenumber != ''"> and phonenumber = #{phonenumber}</if>
+            <if test="idCard != null  and idCard != ''"> and id_card = #{idCard}</if>
+            <if test="week != null  and week != ''"> and week = #{week}</if>
+            <if test="recordDt != null  and recordDt != ''"> and record_dt = #{recordDt}</if>
+            <if test="recordDate != null  and recordDate != ''"> and record_date = #{recordDate}</if>
+            <if test="recordTime != null  and recordTime != ''"> and record_time = #{recordTime}</if>
+            <if test="recordLocations != null  and recordLocations != ''"> and record_locations = #{recordLocations}</if>
+            <if test="isOutwork != null  and isOutwork != ''"> and is_outwork = #{isOutwork}</if>
+            <if test="pictureUrl != null  and pictureUrl != ''"> and picture_url = #{pictureUrl}</if>
+            <if test="type != null  and type != ''"> and type = #{type}</if>
+            <if test="effective != null  and effective != ''"> and effective = #{effective}</if>
+        </where>
+    </select>
+    
+    <select id="selectClockEquipRecordByClockId" parameterType="Long" resultMap="ClockEquipRecordResult">
+        <include refid="selectClockEquipRecordVo"/>
+        where clock_id = #{clockId}
+    </select>
+
+    <select id="selectClockEquipRecordNew" resultType="com.ruoyi.system.domain.ClockEquipRecord">
+        <include refid="selectClockEquipRecordVo"/>
+        where user_id = #{userId}
+        order by record_time desc limit 1
+    </select>
+
+    <insert id="insertClockEquipRecord" parameterType="ClockEquipRecord" useGeneratedKeys="true" keyProperty="clockId">
+        insert into clock_equip_record
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="userId != null">user_id,</if>
+            <if test="deptName != null">dept_name,</if>
+            <if test="userName != null and userName != ''">user_name,</if>
+            <if test="phonenumber != null">phonenumber,</if>
+            <if test="idCard != null">id_card,</if>
+            <if test="week != null">week,</if>
+            <if test="recordDt != null">record_dt,</if>
+            <if test="recordDate != null">record_date,</if>
+            <if test="recordTime != null">record_time,</if>
+            <if test="recordLocations != null">record_locations,</if>
+            <if test="isOutwork != null">is_outwork,</if>
+            <if test="pictureUrl != null">picture_url,</if>
+            <if test="type != null and type != ''">type,</if>
+            <if test="effective != null and effective != ''">effective,</if>
+            <if test="createBy != null">create_by,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="updateBy != null">update_by,</if>
+            <if test="updateTime != null">update_time,</if>
+            <if test="remark != null">remark,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="userId != null">#{userId},</if>
+            <if test="deptName != null">#{deptName},</if>
+            <if test="userName != null and userName != ''">#{userName},</if>
+            <if test="phonenumber != null">#{phonenumber},</if>
+            <if test="idCard != null">#{idCard},</if>
+            <if test="week != null">#{week},</if>
+            <if test="recordDt != null">#{recordDt},</if>
+            <if test="recordDate != null">#{recordDate},</if>
+            <if test="recordTime != null">#{recordTime},</if>
+            <if test="recordLocations != null">#{recordLocations},</if>
+            <if test="isOutwork != null">#{isOutwork},</if>
+            <if test="pictureUrl != null">#{pictureUrl},</if>
+            <if test="type != null and type != ''">#{type},</if>
+            <if test="effective != null and effective != ''">#{effective},</if>
+            <if test="createBy != null">#{createBy},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="updateBy != null">#{updateBy},</if>
+            <if test="updateTime != null">#{updateTime},</if>
+            <if test="remark != null">#{remark},</if>
+         </trim>
+    </insert>
+
+    <update id="updateClockEquipRecord" parameterType="ClockEquipRecord">
+        update clock_equip_record
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="userId != null">user_id = #{userId},</if>
+            <if test="deptName != null">dept_name = #{deptName},</if>
+            <if test="userName != null and userName != ''">user_name = #{userName},</if>
+            <if test="phonenumber != null">phonenumber = #{phonenumber},</if>
+            <if test="idCard != null">id_card = #{idCard},</if>
+            <if test="week != null">week = #{week},</if>
+            <if test="recordDt != null">record_dt = #{recordDt},</if>
+            <if test="recordDate != null">record_date = #{recordDate},</if>
+            <if test="recordTime != null">record_time = #{recordTime},</if>
+            <if test="recordLocations != null">record_locations = #{recordLocations},</if>
+            <if test="isOutwork != null">is_outwork = #{isOutwork},</if>
+            <if test="pictureUrl != null">picture_url = #{pictureUrl},</if>
+            <if test="type != null and type != ''">type = #{type},</if>
+            <if test="effective != null and effective != ''">effective = #{effective},</if>
+            <if test="createBy != null">create_by = #{createBy},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="updateBy != null">update_by = #{updateBy},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
+            <if test="remark != null">remark = #{remark},</if>
+        </trim>
+        where clock_id = #{clockId}
+    </update>
+
+    <update id="updateClockEquipRecordEffective">
+        update clock_equip_record set effective = '2' where record_date = #{day} and user_name = #{personName} and type = '2'
+    </update>
+
+    <delete id="deleteClockEquipRecordByClockId" parameterType="Long">
+        delete from clock_equip_record where clock_id = #{clockId}
+    </delete>
+
+    <delete id="deleteClockEquipRecordByClockIds" parameterType="String">
+        delete from clock_equip_record where clock_id in 
+        <foreach item="clockId" collection="array" open="(" separator="," close=")">
+            #{clockId}
+        </foreach>
+    </delete>
+</mapper>

+ 6 - 1
ruoyi-system/src/main/resources/mapper/system/ClockRecordMapper.xml

@@ -19,6 +19,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="isOutwork"    column="is_outwork"    />
         <result property="pictureUrl"    column="picture_url"    />
         <result property="type"    column="type"    />
+        <result property="source"    column="source"    />
         <result property="createBy"    column="create_by"    />
         <result property="createTime"    column="create_time"    />
         <result property="updateBy"    column="update_by"    />
@@ -27,7 +28,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     </resultMap>
 
     <sql id="selectClockRecordVo">
-        select clock_id, user_id, dept_name, user_name, phonenumber, id_card,record_dt,record_date, record_time,record_locations,week, is_outwork,picture_url,type, create_by, create_time, update_by, update_time, remark from clock_record
+        select clock_id, user_id, dept_name, user_name, phonenumber, id_card,record_dt,record_date, record_time,record_locations,week, is_outwork,picture_url,type,source, create_by, create_time, update_by, update_time, remark from clock_record
     </sql>
 
     <select id="selectClockRecordList" parameterType="ClockRecord" resultMap="ClockRecordResult">
@@ -42,6 +43,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="week != null  and week != ''"> and week = #{week}</if>
             <if test="isOutwork != null  and isOutwork != ''"> and is_outwork = #{isOutwork}</if>
             <if test="type != null  and type != ''"> and type = #{type}</if>
+            <if test="source != null  and source != ''"> and source = #{source}</if>
             <if test="startTime != null and startTime != ''"><!-- 开始时间检索 -->
                 AND record_date &gt;= #{startTime}
             </if>
@@ -98,6 +100,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="isOutwork != null  and isOutwork != ''"> is_outwork,</if>
             <if test="pictureUrl != null  and pictureUrl != ''"> picture_url,</if>
             <if test="type != null  and type != ''">type,</if>
+            <if test="source != null  and source != ''"> source ,</if>
             <if test="createBy != null">create_by,</if>
             <if test="createTime != null">create_time,</if>
             <if test="updateBy != null">update_by,</if>
@@ -118,6 +121,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="isOutwork != null  and isOutwork != ''">#{isOutwork},</if>
             <if test="pictureUrl != null  and pictureUrl != ''"> #{pictureUrl},</if>
             <if test="type != null  and type != ''">#{type},</if>
+            <if test="source != null  and source != ''"> #{source},</if>
             <if test="createBy != null">#{createBy},</if>
             <if test="createTime != null">#{createTime},</if>
             <if test="updateBy != null">#{updateBy},</if>
@@ -142,6 +146,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="isOutwork != null  and isOutwork != ''"> is_outwork = #{isOutwork},</if>
             <if test="pictureUrl != null  and pictureUrl != ''"> picture_url=#{pictureUrl},</if>
             <if test="type != null  and type != ''">type = #{type},</if>
+            <if test="source != null  and source != ''"> source = #{source},</if>
             <if test="createBy != null">create_by = #{createBy},</if>
             <if test="createTime != null">create_time = #{createTime},</if>
             <if test="updateBy != null">update_by = #{updateBy},</if>

+ 6 - 1
ruoyi-system/src/main/resources/mapper/system/ClockUserInfoMapper.xml

@@ -39,7 +39,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <include refid="selectClockUserInfoVo"/>
         where user_id = #{userId}
     </select>
-        
+
+    <select id="selectClockUserInfoByName" parameterType="ClockUserInfo" resultMap="ClockUserInfoResult">
+        <include refid="selectClockUserInfoVo"/>
+        where user_name=#{userName} and del_flag = '0' limit 1
+    </select>
+
     <insert id="insertClockUserInfo" parameterType="ClockUserInfo" useGeneratedKeys="true" keyProperty="userId">
         insert into clock_user_info
         <trim prefix="(" suffix=")" suffixOverrides=",">

+ 6 - 1
ruoyi-system/src/main/resources/mapper/system/DutyScheduleMapper.xml

@@ -13,6 +13,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="isPicture"    column="is_picture"    />
         <result property="startWorkTime"    column="start_work_time"    />
         <result property="endWorkTime"    column="end_work_time"    />
+        <result property="isEquip"    column="is_equip"    />
         <result property="createBy"    column="create_by"    />
         <result property="createTime"    column="create_time"    />
         <result property="updateBy"    column="update_by"    />
@@ -21,7 +22,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     </resultMap>
 
     <sql id="selectDutyScheduleVo">
-        select duty_id,lon,lat,locations,distance,is_picture, start_work_time, end_work_time, create_by, create_time, update_by, update_time, remark from duty_schedule
+        select duty_id,lon,lat,locations,distance,is_picture, start_work_time, end_work_time,is_equip, create_by, create_time, update_by, update_time, remark from duty_schedule
     </sql>
 
     <select id="selectDutyScheduleList" parameterType="DutySchedule" resultMap="DutyScheduleResult">
@@ -29,6 +30,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <where>  
             <if test="startWorkTime != null  and startWorkTime != ''"> and start_work_time = #{startWorkTime}</if>
             <if test="endWorkTime != null  and endWorkTime != ''"> and end_work_time = #{endWorkTime}</if>
+            <if test="isEquip != null  and isEquip != ''"> and is_equip = #{isEquip}</if>
         </where>
         order by create_time desc
     </select>
@@ -52,6 +54,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="isPicture != null">is_picture,</if>
             <if test="startWorkTime != null">start_work_time,</if>
             <if test="endWorkTime != null">end_work_time,</if>
+            <if test="isEquip != null ">is_equip,</if>
             <if test="createBy != null">create_by,</if>
             <if test="createTime != null">create_time,</if>
             <if test="updateBy != null">update_by,</if>
@@ -66,6 +69,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="isPicture != null">#{isPicture},</if>
             <if test="startWorkTime != null">#{startWorkTime},</if>
             <if test="endWorkTime != null">#{endWorkTime},</if>
+            <if test="isEquip != null ">#{isEquip},</if>
             <if test="createBy != null">#{createBy},</if>
             <if test="createTime != null">#{createTime},</if>
             <if test="updateBy != null">#{updateBy},</if>
@@ -84,6 +88,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="isPicture != null">is_picture = #{isPicture},</if>
             <if test="startWorkTime != null">start_work_time = #{startWorkTime},</if>
             <if test="endWorkTime != null">end_work_time = #{endWorkTime},</if>
+            <if test="isEquip != null ">is_equip= #{isEquip},</if>
             <if test="createBy != null">create_by = #{createBy},</if>
             <if test="createTime != null">create_time = #{createTime},</if>
             <if test="updateBy != null">update_by = #{updateBy},</if>

+ 1 - 0
ruoyi-system/src/main/resources/mapper/system/OpeningDoorRecordMapper.xml

@@ -39,6 +39,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                 AND controls_time &lt;= #{endTime}
             </if>
         </where>
+        order by controls_time desc
     </select>
     
     <select id="selectOpeningDoorRecordByRecordId" parameterType="Long" resultMap="OpeningDoorRecordResult">