Переглянути джерело

优化门禁代码相关逻辑

fanghuisheng 1 день тому
батько
коміт
bfe9261a48

+ 18 - 3
service-eg/service-eg-biz/src/main/java/com/usky/eg/controller/web/EgDeviceController.java

@@ -81,6 +81,20 @@ public class EgDeviceController {
         return ApiResult.success();
     }
 
+    /**
+     * 设备绑定人员
+     * @param params 包含 deviceId, personIds, isLoginNotify 的参数对象
+     * @return
+     */
+    @PostMapping("/bindPerson")
+    public ApiResult<Void> bindPerson(@RequestBody Map<String, Object> params){
+        Integer deviceId = (Integer) params.get("deviceId");
+        String personIds = (String) params.get("personIds");
+        Integer isLoginNotify = params.get("isLoginNotify") != null ? (Integer) params.get("isLoginNotify") : 0;
+        egDeviceService.bindPerson(deviceId, personIds, isLoginNotify);
+        return ApiResult.success();
+    }
+
     /**
      * 下发设备控制命令
      */
@@ -90,12 +104,13 @@ public class EgDeviceController {
                                                  @RequestParam("deviceUuid") String deviceUuid,
                                                  @RequestParam("commandCode") String commandCode,
                                                  @RequestParam("commandValue") String commandValue,
-                                                 @RequestParam(value = "domain",required = false) String domain,
                                                  @RequestParam(value = "userId",required = false) Long userId,
                                                  @RequestParam(value = "userName",required = false) String userName,
                                                  @RequestParam(value = "categoryType",required = false , defaultValue = "1") Integer categoryType,
-                                                 @RequestParam(value = "gatewayUuid",required = false) String gatewayUuid){
-        return ApiResult.success(egDeviceService.control(productCode,deviceUuid,commandCode,commandValue,domain,userId,userName,categoryType,gatewayUuid));
+                                                 @RequestParam(value = "gatewayUuid",required = false) String gatewayUuid,
+                                                 @RequestParam(value = "skipCheck",required = false, defaultValue = "false") Boolean skipCheck,
+                                                 @RequestParam(value = "passType",required = false) Integer passType){
+        return ApiResult.success(egDeviceService.control(productCode,deviceUuid,commandCode,commandValue,userId,userName,categoryType,gatewayUuid,skipCheck,passType));
     }
 
 }

+ 6 - 0
service-eg/service-eg-biz/src/main/java/com/usky/eg/domain/EgDevice.java

@@ -98,6 +98,12 @@ public class EgDevice implements Serializable {
      */
     private Integer tenantId;
 
+    /**
+     * 绑定人员信息(从 eg_device_person_bind 表汇总的 person_id 列表,以逗号分隔)
+     */
+    @TableField("bind_person")
+    private String bindPerson;
+
     /**
      * 屏保
      */

+ 34 - 0
service-eg/service-eg-biz/src/main/java/com/usky/eg/domain/EgDevicePersonBind.java

@@ -0,0 +1,34 @@
+package com.usky.eg.domain;
+
+import java.io.Serializable;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * <p>
+ * 门禁设备人员绑定表 eg_device_person_bind
+ * </p>
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class EgDevicePersonBind implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 设备主键ID
+     */
+    private Integer deviceId;
+
+    /**
+     * 人员ID
+     */
+    private Integer personId;
+
+    /**
+     * 是否打开登录通知(1 表示是,0 表示否,默认0)
+     */
+    private Integer isLoginNotify;
+}
+

+ 1 - 1
service-eg/service-eg-biz/src/main/java/com/usky/eg/domain/EgRecord.java

@@ -46,7 +46,7 @@ public class EgRecord implements Serializable {
     private Integer egDeviceId;
 
     /**
-     * 通行方式(1、人脸 2、刷卡 3、手机)
+     * 通行方式(0、其它 1、人脸 2、刷卡 3、手机 4、电脑)
      */
     private Integer passType;
 

+ 44 - 0
service-eg/service-eg-biz/src/main/java/com/usky/eg/mapper/EgDevicePersonBindMapper.java

@@ -0,0 +1,44 @@
+package com.usky.eg.mapper;
+
+import com.usky.eg.domain.EgDevicePersonBind;
+import org.apache.ibatis.annotations.Param;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+@Repository
+public interface EgDevicePersonBindMapper {
+
+    /**
+     * 根据多个设备ID查询绑定关系
+     */
+    List<EgDevicePersonBind> selectByDeviceIds(@Param("deviceIds") List<Integer> deviceIds);
+
+    /**
+     * 统计某个设备下的绑定人数
+     */
+    Integer countByDeviceId(@Param("deviceId") Integer deviceId);
+
+    /**
+     * 根据用户ID和设备ID统计绑定关系
+     * 通过 sys_user_person -> sys_person -> eg_device_person_bind 进行关联
+     */
+    Integer countByUserIdAndDeviceId(@Param("userId") Long userId, @Param("deviceId") Integer deviceId);
+
+    /**
+     * 根据用户ID查询其绑定的设备ID列表
+     * 通过 sys_user_person -> sys_person -> eg_device_person_bind 进行关联
+     */
+    List<Integer> selectDeviceIdsByUserId(@Param("userId") Long userId);
+
+    /**
+     * 根据设备ID删除绑定关系
+     */
+    void deleteByDeviceId(@Param("deviceId") Integer deviceId);
+
+    /**
+     * 新增绑定关系
+     */
+    void insert(EgDevicePersonBind bind);
+}
+

+ 0 - 18
service-eg/service-eg-biz/src/main/java/com/usky/eg/mapper/MeetingFaceDeviceMapper.java

@@ -1,18 +0,0 @@
-package com.usky.eg.mapper;
-
-import com.usky.eg.domain.MeetingFaceDevice;
-import com.usky.common.mybatis.core.CrudMapper;
-import org.springframework.stereotype.Repository;
-
-/**
- * <p>
- * 人脸设备关联表 Mapper 接口
- * </p>
- *
- * @author zyj
- * @since 2024-11-27
- */
-@Repository
-public interface MeetingFaceDeviceMapper extends CrudMapper<MeetingFaceDevice> {
-
-}

+ 0 - 18
service-eg/service-eg-biz/src/main/java/com/usky/eg/mapper/MeetingFaceMapper.java

@@ -1,18 +0,0 @@
-package com.usky.eg.mapper;
-
-import com.usky.eg.domain.MeetingFace;
-import com.usky.common.mybatis.core.CrudMapper;
-import org.springframework.stereotype.Repository;
-
-/**
- * <p>
- *  Mapper 接口
- * </p>
- *
- * @author zyj
- * @since 2024-11-27
- */
-@Repository
-public interface MeetingFaceMapper extends CrudMapper<MeetingFace> {
-
-}

+ 10 - 1
service-eg/service-eg-biz/src/main/java/com/usky/eg/service/EgDeviceService.java

@@ -33,5 +33,14 @@ public interface EgDeviceService extends CrudService<EgDevice> {
 
     boolean checkDeviceNameUnique(EgDevice egDevice);
 
-    Map<String,Object> control(String productCode, String deviceUuid, String commandCode, String commandValue, String domain, Long userId, String userName, Integer categoryType, String gatewayUuid);
+    Map<String,Object> control(String productCode, String deviceUuid, String commandCode, String commandValue, Long userId, String userName, Integer categoryType, String gatewayUuid, Boolean skipCheck, Integer passType);
+
+    /**
+     * 设备与人员绑定
+     *
+     * @param deviceId      设备主键ID
+     * @param personIds     人员ID,多个以逗号分隔
+     * @param isLoginNotify 是否打开登录通知(1 表示是,0 表示否)
+     */
+    void bindPerson(Integer deviceId, String personIds, Integer isLoginNotify);
 }

+ 11 - 47
service-eg/service-eg-biz/src/main/java/com/usky/eg/service/impl/EgDeviceHeartbeatServiceImpl.java

@@ -4,21 +4,16 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.usky.common.core.exception.BusinessException;
-import com.usky.eg.domain.EgDevice;
 import com.usky.eg.domain.EgDeviceHeartbeat;
 import com.usky.eg.mapper.EgDeviceHeartbeatMapper;
-import com.usky.eg.mapper.EgDeviceMapper;
 import com.usky.eg.service.EgDeviceHeartbeatService;
 import com.usky.common.mybatis.core.AbstractCrudService;
 import org.apache.commons.lang3.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.time.LocalDateTime;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 /**
  * <p>
@@ -31,9 +26,6 @@ import java.util.Map;
 @Service
 public class EgDeviceHeartbeatServiceImpl extends AbstractCrudService<EgDeviceHeartbeatMapper, EgDeviceHeartbeat> implements EgDeviceHeartbeatService {
 
-    @Autowired
-    private EgDeviceMapper egDeviceMapper;
-
     @Transactional(rollbackFor = Exception.class)
     @Override
     public void heartbeatEscalation(EgDeviceHeartbeat heartbeat) {
@@ -75,48 +67,20 @@ public class EgDeviceHeartbeatServiceImpl extends AbstractCrudService<EgDeviceHe
         try {
             LocalDateTime now = LocalDateTime.now();
             
-            // 1. 查询所有心跳记录,按deviceCode分组,获取最新的心跳记录
+            // 1. 查询所有当前状态为在线的心跳记录
             LambdaQueryWrapper<EgDeviceHeartbeat> heartbeatQueryWrapper = Wrappers.lambdaQuery();
-            heartbeatQueryWrapper.orderByDesc(EgDeviceHeartbeat::getCreateTime);
+            heartbeatQueryWrapper.eq(EgDeviceHeartbeat::getDeviceStatus, 1);
             List<EgDeviceHeartbeat> heartbeatList = baseMapper.selectList(heartbeatQueryWrapper);
-            
-            // 2. 构建deviceCode到最新心跳记录的映射
-            Map<String, EgDeviceHeartbeat> deviceHeartbeatMap = new HashMap<>();
+
+            // 2. 遍历在线记录,若心跳时间超过5分钟则更新为离线
             for (EgDeviceHeartbeat heartbeat : heartbeatList) {
-                String deviceCode = heartbeat.getDeviceCode();
-                if (StringUtils.isNotBlank(deviceCode)) {
-                    // 如果已存在,保留最新的(因为已按createTime降序排列)
-                    deviceHeartbeatMap.putIfAbsent(deviceCode, heartbeat);
-                }
-            }
-            
-            // 3. 查询所有有deviceCode的设备
-            LambdaQueryWrapper<EgDevice> deviceQueryWrapper = Wrappers.lambdaQuery();
-            deviceQueryWrapper.isNotNull(EgDevice::getDeviceCode);
-            List<EgDevice> deviceList = egDeviceMapper.selectList(deviceQueryWrapper);
-            
-            // 4. 根据心跳表中的deviceStatus更新心跳记录的状态(如果需要根据时间判断)
-            // 或者同步设备状态到其他服务
-            for (EgDevice device : deviceList) {
-                String deviceCode = device.getDeviceCode();
-                if (StringUtils.isNotBlank(deviceCode) && deviceHeartbeatMap.containsKey(deviceCode)) {
-                    EgDeviceHeartbeat heartbeat = deviceHeartbeatMap.get(deviceCode);
-                    LocalDateTime heartbeatTime = heartbeat.getCreateTime();
-                    Integer deviceStatus = heartbeat.getDeviceStatus();
-                    
-                    // 如果心跳时间超过5分钟,且当前状态为在线,则更新为离线
-                    if (heartbeatTime != null && deviceStatus != null && deviceStatus == 1) {
-                        if (!heartbeatTime.isAfter(now.minusMinutes(5))) {
-                            // 心跳超时,更新心跳表中的状态为离线
-                            LambdaUpdateWrapper<EgDeviceHeartbeat> updateWrapper = Wrappers.lambdaUpdate();
-                            updateWrapper.set(EgDeviceHeartbeat::getDeviceStatus, 0)
-                                    .eq(EgDeviceHeartbeat::getId, heartbeat.getId());
-                            baseMapper.update(null, updateWrapper);
-                        }
-                    }
-                    
-                    // 这里可以根据实际需求添加其他业务逻辑
-                    // 例如:同步到IoT服务、发送告警等
+                LocalDateTime heartbeatTime = heartbeat.getCreateTime();
+                if (heartbeatTime != null && !heartbeatTime.isAfter(now.minusMinutes(5))) {
+                    // 心跳超时,更新心跳表中的状态为离线
+                    LambdaUpdateWrapper<EgDeviceHeartbeat> updateWrapper = Wrappers.lambdaUpdate();
+                    updateWrapper.set(EgDeviceHeartbeat::getDeviceStatus, 0)
+                            .eq(EgDeviceHeartbeat::getId, heartbeat.getId());
+                    baseMapper.update(null, updateWrapper);
                 }
             }
         } catch (Exception e) {

+ 195 - 130
service-eg/service-eg-biz/src/main/java/com/usky/eg/service/impl/EgDeviceServiceImpl.java

@@ -14,12 +14,12 @@ import com.usky.common.core.util.UUIDUtils;
 import com.usky.common.security.utils.SecurityUtils;
 import com.usky.eg.domain.EgDevice;
 import com.usky.eg.domain.EgDeviceHeartbeat;
-import com.usky.eg.domain.MeetingFace;
-import com.usky.eg.domain.MeetingFaceDevice;
+import com.usky.eg.domain.EgDevicePersonBind;
+import com.usky.eg.domain.EgRecord;
 import com.usky.eg.mapper.EgDeviceMapper;
 import com.usky.eg.mapper.EgDeviceHeartbeatMapper;
-import com.usky.eg.mapper.MeetingFaceDeviceMapper;
-import com.usky.eg.mapper.MeetingFaceMapper;
+import com.usky.eg.mapper.EgDevicePersonBindMapper;
+import com.usky.eg.mapper.EgRecordMapper;
 import com.usky.eg.service.EgDeviceService;
 import com.usky.common.mybatis.core.AbstractCrudService;
 import com.usky.eg.service.vo.EgDeviceRequestVO;
@@ -43,19 +43,18 @@ import java.util.concurrent.CompletableFuture;
  */
 @Service
 public class EgDeviceServiceImpl extends AbstractCrudService<EgDeviceMapper, EgDevice> implements EgDeviceService {
-    @Autowired
-    private MeetingFaceDeviceMapper meetingFaceDeviceMapper;
-    @Autowired
-    private MeetingFaceMapper meetingFaceMapper;
     @Autowired
     private EgDeviceMapper egDeviceMapper;
     @Autowired
     private EgDeviceHeartbeatMapper egDeviceHeartbeatMapper;
     @Autowired
     private RemoteIotTaskService remoteIotTaskService;
-
     @Autowired
     private RemoteTransferService remoteTransferService;
+    @Autowired
+    private EgDevicePersonBindMapper egDevicePersonBindMapper;
+    @Autowired
+    private EgRecordMapper egRecordMapper;
 
     @Override
     public CommonPage<EgDevice> page(EgDeviceRequestVO requestVO){
@@ -95,6 +94,9 @@ public class EgDeviceServiceImpl extends AbstractCrudService<EgDeviceMapper, EgD
                 if (StringUtils.isNotBlank(deviceCode)) {
                     validDeviceCodes.add(deviceCode);
                     deviceCodeMap.put(deviceCode, device);
+                } else {
+                    // deviceCode为空,直接设置为离线
+                    device.setDeviceStatus(0);
                 }
             }
 
@@ -137,28 +139,11 @@ public class EgDeviceServiceImpl extends AbstractCrudService<EgDeviceMapper, EgD
                 }
             }
 
-            LambdaQueryWrapper<MeetingFace> meetingFaceQuery = Wrappers.lambdaQuery();
-            meetingFaceQuery.select(MeetingFace::getFid,MeetingFace::getCreateTime,MeetingFace::getVefNum,MeetingFace::getFaceName,MeetingFace::getRemark,MeetingFace::getFaceStatus,MeetingFace::getCardNum,MeetingFace::getBindDevice,MeetingFace::getDeptId,MeetingFace::getTenantId,MeetingFace::getUserId)
-                    .eq(MeetingFace::getTenantId,tenantId);
-            List<MeetingFace> meetingAllFaceList = meetingFaceMapper.selectList(meetingFaceQuery);
-
-            for (int i = 0; i < page.getRecords().size(); i++) {
-                if(Objects.nonNull(page.getRecords().get(i).getBindFace()) ||StringUtils.isNotBlank(page.getRecords().get(i).getBindFace())){
-                    String[] fidListStr = page.getRecords().get(i).getBindFace().split(",");
-                    Integer[] fidList = Arrays.stream(fidListStr).map(Integer::parseInt).toArray(Integer[]::new);
-
-                    List<MeetingFace> meetingFaceList = new ArrayList<>();
-                    for (int j = 0; j < fidList.length; j++) {
-                        for (int k = 0; k < meetingAllFaceList.size(); k++) {
-                            if(fidList[j] == meetingAllFaceList.get(k).getFid()){
-                                meetingFaceList.add(meetingAllFaceList.get(k));
-                                break;
-                            }
-                        }
-                    }
-                    page.getRecords().get(i).setMeetingFaceList(meetingFaceList);
-                }
-            }
+        }
+
+        // 查询并填充绑定人员信息(来自 eg_device_person_bind,person_id 逗号分隔后写入 bindPerson 字段)
+        if (!page.getRecords().isEmpty()) {
+            fillBindPerson(page.getRecords());
         }
 
         return new CommonPage<>(page.getRecords(),page.getTotal(),requestVO.getSize(),requestVO.getCurrent());
@@ -167,29 +152,28 @@ public class EgDeviceServiceImpl extends AbstractCrudService<EgDeviceMapper, EgD
     @Override
     public CommonPage<EgDevice> wePage(EgDeviceRequestVO requestVO){
         long userId = SecurityUtils.getUserId();
-        //人员设备权限校验,校验通过,可以下发命令控制设备
-        Integer fid = baseMapper.getMeetingFaceData(userId);
-//        if(fid == null){
-//            throw new BusinessException("人脸卡号信息未注册");
-//        }
-        Integer[] deviceFid = baseMapper.getMeetingFaceDeviceList(fid);
-//        if(deviceFid.length == 0){
-//            throw new BusinessException("人员未绑定设备,请检查");
-//        }
+        // 通过 sys_user_person -> sys_person -> eg_device_person_bind -> eg_device 查询当前用户绑定的设备ID列表
+        List<Integer> deviceIds = egDevicePersonBindMapper.selectDeviceIdsByUserId(userId);
+        if (CollectionUtils.isEmpty(deviceIds)) {
+            // 未绑定任何设备,返回空分页
+            return new CommonPage<>(new ArrayList<>(), 0L, requestVO.getSize(), requestVO.getCurrent());
+        }
 
         IPage<EgDevice> page = new Page<>(requestVO.getCurrent(),requestVO.getSize());
-        if(deviceFid.length > 0){
-            LambdaQueryWrapper<EgDevice> queryWrapper = Wrappers.lambdaQuery();
-            queryWrapper.like(StringUtils.isNotBlank(requestVO.getDeviceName()),EgDevice::getDeviceName,requestVO.getDeviceName())
-                    .like(StringUtils.isNotBlank(requestVO.getInstallAddress()),EgDevice::getInstallAddress,requestVO.getInstallAddress())
-                    .eq(null != requestVO.getServiceStatus(),EgDevice::getServiceStatus,requestVO.getServiceStatus())
-                    .eq(null != requestVO.getId(),EgDevice::getId,requestVO.getId())
-                    .in(EgDevice::getId, (Object[]) deviceFid)
-                    .eq(EgDevice::getTenantId,SecurityUtils.getTenantId())
-                    .orderByDesc(EgDevice::getId);
-            page = this.page(page,queryWrapper);
-        }
+        LambdaQueryWrapper<EgDevice> queryWrapper = Wrappers.lambdaQuery();
+        queryWrapper.like(StringUtils.isNotBlank(requestVO.getDeviceName()),EgDevice::getDeviceName,requestVO.getDeviceName())
+                .like(StringUtils.isNotBlank(requestVO.getInstallAddress()),EgDevice::getInstallAddress,requestVO.getInstallAddress())
+                .eq(null != requestVO.getServiceStatus(),EgDevice::getServiceStatus,requestVO.getServiceStatus())
+                .eq(null != requestVO.getId(),EgDevice::getId,requestVO.getId())
+                .in(EgDevice::getId, deviceIds)
+                .eq(EgDevice::getTenantId,SecurityUtils.getTenantId())
+                .orderByDesc(EgDevice::getId);
+        page = this.page(page,queryWrapper);
 
+        // 查询并填充绑定人员信息
+        if (!page.getRecords().isEmpty()) {
+            fillBindPerson(page.getRecords());
+        }
 
         return new CommonPage<>(page.getRecords(),page.getTotal(),requestVO.getSize(),requestVO.getCurrent());
     }
@@ -215,7 +199,18 @@ public class EgDeviceServiceImpl extends AbstractCrudService<EgDeviceMapper, EgD
             }
         }
 
-        // 3. 校验设备编码是否已被绑定
+        // 3. 若未传deviceCode但传了deviceIp,尝试从心跳表中获取deviceCode
+        if (StringUtils.isBlank(egDevice.getDeviceCode()) && StringUtils.isNotBlank(egDevice.getDeviceIp())) {
+            LambdaQueryWrapper<EgDeviceHeartbeat> heartbeatCheckWrapper = Wrappers.lambdaQuery();
+            heartbeatCheckWrapper.eq(EgDeviceHeartbeat::getIpAddr, egDevice.getDeviceIp())
+                    .eq(EgDeviceHeartbeat::getDeviceStatus, 1);
+            EgDeviceHeartbeat heartbeat = egDeviceHeartbeatMapper.selectOne(heartbeatCheckWrapper);
+            if (heartbeat != null && StringUtils.isNotBlank(heartbeat.getDeviceCode())) {
+                egDevice.setDeviceCode(heartbeat.getDeviceCode());
+            }
+        }
+
+        // 4. 校验设备编码是否已被绑定
         if (StringUtils.isNotBlank(egDevice.getDeviceCode())) {
             LambdaQueryWrapper<EgDevice> codeCheckWrapper = Wrappers.lambdaQuery();
             codeCheckWrapper.eq(EgDevice::getDeviceCode, egDevice.getDeviceCode())
@@ -227,7 +222,7 @@ public class EgDeviceServiceImpl extends AbstractCrudService<EgDeviceMapper, EgD
             }
         }
 
-        // 4. 设置设备基本信息
+        // 5. 设置设备基本信息
         egDevice.setDeviceUuid(UUIDUtils.uuid());
         egDevice.setCreateBy(SecurityUtils.getUsername());
         egDevice.setCreateTime(now);
@@ -235,20 +230,9 @@ public class EgDeviceServiceImpl extends AbstractCrudService<EgDeviceMapper, EgD
         egDevice.setOpenMode("人脸");
         egDevice.setWorkStatus("4");
 
-        // 5. 保存设备
+        // 6. 保存设备
         this.save(egDevice);
 
-        // 6. 处理绑定人脸信息
-        String bindFace = egDevice.getBindFace();
-        if (StringUtils.isNotBlank(bindFace)) {
-            String[] fids = bindFace.split(",");
-            for (String fid : fids) {
-                if (StringUtils.isNotBlank(fid)) {
-                    egDeviceMapper.insertMeetingFaceDevice(Integer.parseInt(fid.trim()), egDevice.getId());
-                }
-            }
-        }
-
         // 7. 异步调用远程服务添加设备信息,静默处理异常,不影响设备保存成功
         String deviceUuid = egDevice.getDeviceUuid();
         String deviceName = egDevice.getDeviceName();
@@ -273,22 +257,6 @@ public class EgDeviceServiceImpl extends AbstractCrudService<EgDeviceMapper, EgD
 
     @Override
     public void update(EgDevice egDevice) {
-
-        String[] fids = new String[0];
-        if(Objects.nonNull(egDevice.getBindFace()) || StringUtils.isNotBlank(egDevice.getBindFace())){
-            fids = egDevice.getBindFace().split(",");
-
-            egDeviceMapper.deleteMeetingFaceDevice(egDevice.getId());
-        }else{
-            EgDevice one = this.getById(egDevice.getId());
-            egDevice.setBindFace(one.getBindFace());
-        }
-        if(fids.length > 0){
-            for (int i = 0; i < fids.length; i++) {
-                egDeviceMapper.insertMeetingFaceDevice(Integer.parseInt(fids[i]),egDevice.getId());
-            }
-        }
-
         if(checkDeviceNameUnique(egDevice)){
             throw new BusinessException("修改门禁设备'"+egDevice.getDeviceName()+"'失败,设备名称已存在");
         }
@@ -306,6 +274,17 @@ public class EgDeviceServiceImpl extends AbstractCrudService<EgDeviceMapper, EgD
             }
         }
 
+        // 若未传deviceCode但传了deviceIp,尝试从心跳表中获取deviceCode
+        if (StringUtils.isBlank(egDevice.getDeviceCode()) && StringUtils.isNotBlank(egDevice.getDeviceIp())) {
+            LambdaQueryWrapper<EgDeviceHeartbeat> heartbeatCheckWrapper = Wrappers.lambdaQuery();
+            heartbeatCheckWrapper.eq(EgDeviceHeartbeat::getIpAddr, egDevice.getDeviceIp())
+                    .eq(EgDeviceHeartbeat::getDeviceStatus, 1);
+            EgDeviceHeartbeat heartbeat = egDeviceHeartbeatMapper.selectOne(heartbeatCheckWrapper);
+            if (heartbeat != null && StringUtils.isNotBlank(heartbeat.getDeviceCode())) {
+                egDevice.setDeviceCode(heartbeat.getDeviceCode());
+            }
+        }
+
         // 校验设备编码是否已被其他设备绑定(排除当前设备)
         if (StringUtils.isNotBlank(egDevice.getDeviceCode())) {
             LambdaQueryWrapper<EgDevice> codeCheckWrapper = Wrappers.lambdaQuery();
@@ -347,10 +326,9 @@ public class EgDeviceServiceImpl extends AbstractCrudService<EgDeviceMapper, EgD
         EgDevice egDevice = this.getById(id);
         Optional.ofNullable(egDevice).orElseThrow(() -> new BusinessException("门禁设备信息不存在"));
 
-        LambdaQueryWrapper<MeetingFaceDevice> queryWrapper = Wrappers.lambdaQuery();
-        queryWrapper.eq(MeetingFaceDevice::getDeviceId,id);
-        Integer count = meetingFaceDeviceMapper.selectCount(queryWrapper);
-        if(count > 0){
+        // 校验 eg_device_person_bind 是否存在绑定人员
+        Integer personBindCount = egDevicePersonBindMapper.countByDeviceId(id);
+        if (personBindCount != null && personBindCount > 0) {
             throw new BusinessException("已绑定人员不能删除");
         }
 
@@ -369,6 +347,53 @@ public class EgDeviceServiceImpl extends AbstractCrudService<EgDeviceMapper, EgD
         }
     }
 
+    /**
+     * 根据设备ID集合查询 eg_device_person_bind,将 person_id 按逗号拼接后写入设备的 bindPerson 字段
+     */
+    private void fillBindPerson(List<EgDevice> devices) {
+        if (CollectionUtils.isEmpty(devices)) {
+            return;
+        }
+
+        List<Integer> deviceIds = new ArrayList<>();
+        for (EgDevice device : devices) {
+            if (device != null && device.getId() != null) {
+                deviceIds.add(device.getId());
+            }
+        }
+
+        if (deviceIds.isEmpty()) {
+            return;
+        }
+
+        List<EgDevicePersonBind> bindList = egDevicePersonBindMapper.selectByDeviceIds(deviceIds);
+        if (CollectionUtils.isEmpty(bindList)) {
+            return;
+        }
+
+        Map<Integer, List<String>> devicePersonMap = new HashMap<>();
+        for (EgDevicePersonBind bind : bindList) {
+            if (bind.getDeviceId() == null || bind.getPersonId() == null) {
+                continue;
+            }
+            devicePersonMap
+                    .computeIfAbsent(bind.getDeviceId(), k -> new ArrayList<>())
+                    .add(String.valueOf(bind.getPersonId()));
+        }
+
+        for (EgDevice device : devices) {
+            if (device == null || device.getId() == null) {
+                continue;
+            }
+            List<String> personIds = devicePersonMap.get(device.getId());
+            if (personIds != null && !personIds.isEmpty()) {
+                device.setBindPerson(String.join(",", personIds));
+            } else {
+                device.setBindPerson(null);
+            }
+        }
+    }
+
     @Override
     public boolean checkNameUnique(EgDevice egDevice){
         Integer id = null == egDevice.getId() ? -1 : egDevice.getId();
@@ -390,60 +415,100 @@ public class EgDeviceServiceImpl extends AbstractCrudService<EgDeviceMapper, EgD
     }
 
     @Override
-    public Map<String,Object> control(String productCode, String deviceUuid, String commandCode, String commandValue, String domain, Long userId, String userName, Integer categoryType, String gatewayUuid){
-        Integer tenantId;
-        long commandUserId;
-        String commandUserName;
-        EgDevice device = null;
-        
-        // 通过deviceUuid查询设备获取tenantId
-        if(StringUtils.isNotBlank(deviceUuid)){
-            LambdaQueryWrapper<EgDevice> deviceQueryWrapper = Wrappers.lambdaQuery();
-            deviceQueryWrapper.select(EgDevice::getId, EgDevice::getTenantId)
-                    .eq(EgDevice::getDeviceUuid, deviceUuid);
-            device = this.getOne(deviceQueryWrapper);
-            if(device != null){
-                tenantId = device.getTenantId();
-                commandUserId = userId;
-                commandUserName = userName;
-            }else{
-                throw new BusinessException("设备未注册,请先注册");
-            }
-        }else{
-            tenantId = SecurityUtils.getTenantId();
-            commandUserId = SecurityUtils.getUserId();
-            commandUserName = SecurityUtils.getUsername();
+    public Map<String,Object> control(String productCode, String deviceUuid, String commandCode, String commandValue, Long userId, String userName, Integer categoryType, String gatewayUuid, Boolean skipCheck, Integer passType){
+        // 1. 查询设备
+        EgDevice device = this.getOne(Wrappers.<EgDevice>lambdaQuery()
+                .select(EgDevice::getId, EgDevice::getTenantId)
+                .eq(EgDevice::getDeviceUuid, deviceUuid));
+        if (device == null) {
+            throw new BusinessException("设备未注册,请先注册");
         }
 
-        //人员设备权限校验,校验通过,可以下发命令控制设备
-        if(device != null){
-            Integer fid = baseMapper.getMeetingFaceData(commandUserId);
-            if(fid == null){
-                throw new BusinessException("人脸卡号信息未注册");
-            }
-            Integer[] deviceFid = baseMapper.getMeetingFaceDeviceList(fid);
-            if(deviceFid.length == 0){
-                throw new BusinessException("人员未绑定设备,请检查");
-            }
-            
-            boolean exist = Arrays.asList(deviceFid).contains(device.getId());
-            if(!exist){
+        Integer tenantId = device.getTenantId();
+        long commandUserId = userId != null ? userId : SecurityUtils.getUserId();
+        String commandUserName = userName != null ? userName : SecurityUtils.getUsername();
+
+        // 2. 权限校验
+        if (!Boolean.TRUE.equals(skipCheck)) {
+            Integer bindCount = egDevicePersonBindMapper.countByUserIdAndDeviceId(commandUserId, device.getId());
+            if (bindCount == null || bindCount == 0) {
+                saveRecord(device.getId(), tenantId, commandUserName, passType, "失败:暂无权限");
                 throw new BusinessException("暂无权限");
             }
         }
 
+        // 3. 构建下发命令参数
+        Map<String,Object> params = new HashMap<>();
+        params.put("commandCode", commandCode);
+        params.put("commandValue", commandValue);
         Map<String,Object> map = new HashMap<>();
-        map.put("method","control");
+        map.put("method", "control");
         map.put("deviceUuid", deviceUuid);
-        Map<String,Object> map1 = new HashMap<>();
-        map1.put("commandCode",commandCode);
-        map1.put("commandValue",commandValue);
-        map.put("params",map1);
+        map.put("params", params);
+
+        // 4. 下发命令并记录结果
+        String targetUuid = categoryType == 3 ? gatewayUuid : deviceUuid;
+        Map<String,Object> result = null;
+        String passResult = "下发命令失败";
+        try {
+            result = remoteTransferService.deviceControl(productCode, targetUuid, JSON.toJSONString(map), tenantId, commandUserId, commandUserName);
+            if (result == null || !Integer.valueOf(200).equals(result.get("code"))) {
+                passResult = result != null && result.get("message") != null ? result.get("message").toString() : "下发命令失败";
+                throw new BusinessException(passResult);
+            }
+            passResult = result.get("message") != null ? result.get("message").toString() : "下发命令成功";
+            return result;
+        } finally {
+            saveRecord(device.getId(), tenantId, commandUserName, passType, passResult);
+        }
+    }
 
-        if(categoryType == 3){
-            return remoteTransferService.deviceControl(productCode, gatewayUuid, JSON.toJSONString(map), tenantId, commandUserId, commandUserName);
-        }else{
-            return remoteTransferService.deviceControl(productCode, deviceUuid, JSON.toJSONString(map), tenantId, commandUserId, commandUserName);
+    /**
+     * 插入通行记录,失败不影响主流程
+     */
+    private void saveRecord(Integer deviceId, Integer tenantId, String userName, Integer passType, String passResult) {
+        try {
+            EgRecord record = new EgRecord();
+            record.setEgDeviceId(deviceId);
+            record.setTenantId(tenantId);
+            record.setPassTime(LocalDateTime.now());
+            record.setCreateTime(LocalDateTime.now());
+            record.setPassResult(passResult);
+            record.setUserName(userName);
+            record.setPassType(passType);
+            egRecordMapper.insert(record);
+        } catch (Exception ignored) {}
+    }
+
+    @Override
+    public void bindPerson(Integer deviceId, String personIds, Integer isLoginNotify) {
+        if (deviceId == null) {
+            throw new BusinessException("设备ID不能为空");
+        }
+
+        // 先校验设备是否存在
+        EgDevice device = this.getById(deviceId);
+        Optional.ofNullable(device).orElseThrow(() -> new BusinessException("门禁设备信息不存在"));
+
+        // 先清空原有绑定关系
+        egDevicePersonBindMapper.deleteByDeviceId(deviceId);
+
+        // 为空则视为解绑所有人员
+        if (!StringUtils.isNotBlank(personIds)) {
+            return;
+        }
+
+        String[] personIdArr = personIds.split(",");
+        int loginNotify = (isLoginNotify == null) ? 0 : isLoginNotify;
+        for (String personIdStr : personIdArr) {
+            if (!StringUtils.isNotBlank(personIdStr)) {
+                continue;
+            }
+            EgDevicePersonBind bind = new EgDevicePersonBind();
+            bind.setDeviceId(deviceId);
+            bind.setPersonId(Integer.parseInt(personIdStr.trim()));
+            bind.setIsLoginNotify(loginNotify);
+            egDevicePersonBindMapper.insert(bind);
         }
     }
 }

+ 1 - 0
service-eg/service-eg-biz/src/main/resources/mapper/eg/EgDeviceMapper.xml

@@ -12,6 +12,7 @@
         <result column="service_status" property="serviceStatus" />
         <result column="device_ip" property="deviceIp" />
         <result column="bind_face" property="bindFace" />
+        <result column="bind_person" property="bindPerson" />
         <result column="create_by" property="createBy" />
         <result column="update_by" property="updateBy" />
         <result column="create_time" property="createTime" />

+ 62 - 0
service-eg/service-eg-biz/src/main/resources/mapper/eg/EgDevicePersonBindMapper.xml

@@ -0,0 +1,62 @@
+<?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.usky.eg.mapper.EgDevicePersonBindMapper">
+
+    <resultMap id="BaseResultMap" type="com.usky.eg.domain.EgDevicePersonBind">
+        <result column="device_id" property="deviceId" />
+        <result column="person_id" property="personId" />
+        <result column="is_login_notify" property="isLoginNotify" />
+    </resultMap>
+
+    <select id="selectByDeviceIds" resultMap="BaseResultMap">
+        select device_id, person_id, is_login_notify
+        from eg_device_person_bind
+        <where>
+            <if test="deviceIds != null and deviceIds.size() > 0">
+                and device_id in
+                <foreach collection="deviceIds" item="id" open="(" separator="," close=")">
+                    #{id}
+                </foreach>
+            </if>
+        </where>
+    </select>
+
+    <select id="countByDeviceId" resultType="java.lang.Integer">
+        select count(1)
+        from eg_device_person_bind
+        where device_id = #{deviceId}
+    </select>
+
+    <!--
+        通过 sys_user_person 关联 sys_person,最终关联到 eg_device_person_bind,判断某个用户是否对某设备有绑定关系
+        假定字段:sys_user_person(user_id, person_id),sys_person(id),eg_device_person_bind(person_id, device_id)
+    -->
+    <select id="countByUserIdAndDeviceId" resultType="java.lang.Integer">
+        select count(1)
+        from eg_device_person_bind b
+                 join sys_person p on b.person_id = p.id
+                 join sys_user_person up on up.person_id = p.id
+        where up.user_id = #{userId}
+          and b.device_id = #{deviceId}
+    </select>
+
+    <!-- 根据用户ID查询其绑定的设备ID列表 -->
+    <select id="selectDeviceIdsByUserId" resultType="java.lang.Integer">
+        select distinct b.device_id
+        from eg_device_person_bind b
+                 join sys_person p on b.person_id = p.id
+                 join sys_user_person up on up.person_id = p.id
+        where up.user_id = #{userId}
+    </select>
+
+    <delete id="deleteByDeviceId">
+        delete from eg_device_person_bind
+        where device_id = #{deviceId}
+    </delete>
+
+    <insert id="insert" parameterType="com.usky.eg.domain.EgDevicePersonBind">
+        insert into eg_device_person_bind (device_id, person_id, is_login_notify)
+        values (#{deviceId}, #{personId}, #{isLoginNotify})
+    </insert>
+
+</mapper>

+ 0 - 11
service-eg/service-eg-biz/src/main/resources/mapper/eg/MeetingFaceDeviceMapper.xml

@@ -1,11 +0,0 @@
-<?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.usky.eg.mapper.MeetingFaceDeviceMapper">
-
-    <!-- 通用查询映射结果 -->
-    <resultMap id="BaseResultMap" type="com.usky.eg.domain.MeetingFaceDevice">
-        <id column="face_id" property="faceId" />
-        <result column="device_id" property="deviceId" />
-    </resultMap>
-
-</mapper>

+ 0 - 21
service-eg/service-eg-biz/src/main/resources/mapper/eg/MeetingFaceMapper.xml

@@ -1,21 +0,0 @@
-<?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.usky.eg.mapper.MeetingFaceMapper">
-
-    <!-- 通用查询映射结果 -->
-    <resultMap id="BaseResultMap" type="com.usky.eg.domain.MeetingFace">
-        <id column="fid" property="fid" />
-        <result column="face_base" property="faceBase" />
-        <result column="create_time" property="createTime" />
-        <result column="vef_num" property="vefNum" />
-        <result column="face_name" property="faceName" />
-        <result column="remark" property="remark" />
-        <result column="face_status" property="faceStatus" />
-        <result column="card_num" property="cardNum" />
-        <result column="bind_device" property="bindDevice" />
-        <result column="dept_id" property="deptId" />
-        <result column="tenant_id" property="tenantId" />
-        <result column="user_id" property="userId" />
-    </resultMap>
-
-</mapper>