Преглед изворни кода

会议门禁数据表更换,接口调整

fanghuisheng пре 1 дан
родитељ
комит
20ff1dd379
57 измењених фајлова са 1257 додато и 1590 уклоњено
  1. 1 1
      service-eg/service-eg-biz/src/main/java/com/usky/eg/MybatisGenerator.java
  2. 1 2
      service-eg/service-eg-biz/src/main/java/com/usky/eg/adb/EgDeviceAdbConfiguration.java
  3. 69 0
      service-eg/service-eg-biz/src/main/java/com/usky/eg/config/mqtt/MqttBaseConfig.java
  4. 65 0
      service-eg/service-eg-biz/src/main/java/com/usky/eg/config/mqtt/MqttInConfig.java
  5. 57 0
      service-eg/service-eg-biz/src/main/java/com/usky/eg/config/mqtt/MqttOutConfig.java
  6. 8 7
      service-eg/service-eg-biz/src/main/java/com/usky/eg/controller/web/EgDeviceController.java
  7. 38 45
      service-eg/service-eg-biz/src/main/java/com/usky/eg/domain/EgDevice.java
  8. 17 21
      service-eg/service-eg-biz/src/main/java/com/usky/eg/domain/EgDeviceHeartbeat.java
  9. 5 14
      service-eg/service-eg-biz/src/main/java/com/usky/eg/domain/EgDevicePersonBind.java
  10. 23 65
      service-eg/service-eg-biz/src/main/java/com/usky/eg/domain/EgRecord.java
  11. 43 0
      service-eg/service-eg-biz/src/main/java/com/usky/eg/domain/SpaceDeviceAccess.java
  12. 33 0
      service-eg/service-eg-biz/src/main/java/com/usky/eg/listener/MqttListener.java
  13. 10 0
      service-eg/service-eg-biz/src/main/java/com/usky/eg/mapper/EgDeviceMapper.java
  14. 10 31
      service-eg/service-eg-biz/src/main/java/com/usky/eg/mapper/EgDevicePersonBindMapper.java
  15. 9 0
      service-eg/service-eg-biz/src/main/java/com/usky/eg/mapper/SpaceDeviceAccessMapper.java
  16. 0 67
      service-eg/service-eg-biz/src/main/java/com/usky/eg/mqtt/EgMqttOutboundConfig.java
  17. 0 81
      service-eg/service-eg-biz/src/main/java/com/usky/eg/mqtt/EgMqttProperties.java
  18. 3 5
      service-eg/service-eg-biz/src/main/java/com/usky/eg/service/EgDeviceService.java
  19. 3 0
      service-eg/service-eg-biz/src/main/java/com/usky/eg/service/impl/EgDeviceHeartbeatServiceImpl.java
  20. 9 9
      service-eg/service-eg-biz/src/main/java/com/usky/eg/service/impl/EgDeviceRemoteUpgradeMqttServiceImpl.java
  21. 188 232
      service-eg/service-eg-biz/src/main/java/com/usky/eg/service/impl/EgDeviceServiceImpl.java
  22. 8 8
      service-eg/service-eg-biz/src/main/java/com/usky/eg/service/impl/EgRecordServiceImpl.java
  23. 1 1
      service-eg/service-eg-biz/src/main/java/com/usky/eg/service/vo/EgDeviceAdbRemoteUpgradeVO.java
  24. 4 9
      service-eg/service-eg-biz/src/main/java/com/usky/eg/service/vo/EgDeviceRequestVO.java
  25. 3 3
      service-eg/service-eg-biz/src/main/resources/mapper/eg/EgDeviceHeartbeatMapper.xml
  26. 71 21
      service-eg/service-eg-biz/src/main/resources/mapper/eg/EgDeviceMapper.xml
  27. 24 29
      service-eg/service-eg-biz/src/main/resources/mapper/eg/EgDevicePersonBindMapper.xml
  28. 14 15
      service-eg/service-eg-biz/src/main/resources/mapper/eg/EgRecordMapper.xml
  29. 1 1
      service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/controller/web/MeetingDeviceController.java
  30. 108 60
      service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/domain/MeetingDevice.java
  31. 17 16
      service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/domain/MeetingDeviceHeartbeat.java
  32. 11 5
      service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/domain/MeetingDevicePersonBind.java
  33. 0 101
      service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/domain/MeetingFace.java
  34. 82 0
      service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/domain/SpaceDeviceMeeting.java
  35. 13 9
      service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/mapper/MeetingDeviceMapper.java
  36. 4 14
      service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/mapper/MeetingDevicePersonBindMapper.java
  37. 0 17
      service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/mapper/MeetingFaceMapper.java
  38. 18 0
      service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/mapper/SpaceDeviceMeetingMapper.java
  39. 3 26
      service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/repository/MeetingDeviceRepository.java
  40. 0 56
      service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/server/FaceContrastServer.java
  41. 1 1
      service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/service/MeetingDeviceService.java
  42. 0 25
      service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/service/MeetingFaceService.java
  43. 14 12
      service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/service/impl/MeetingDeviceHeartbeatServiceImpl.java
  44. 105 150
      service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/service/impl/MeetingDeviceServiceImpl.java
  45. 0 178
      service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/service/impl/MeetingFaceServiceImpl.java
  46. 1 1
      service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/service/impl/MeetingRoomServiceImpl.java
  47. 4 22
      service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/service/impl/MeetingTemplateServiceImpl.java
  48. 0 98
      service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/service/vo/FaceResultVO.java
  49. 7 5
      service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/service/vo/MeetingDeviceBindPersonRequest.java
  50. 5 0
      service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/service/vo/MeetingDeviceRequestVO.java
  51. 0 21
      service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/service/vo/MeetingFaceRequestVO.java
  52. 0 68
      service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/service/vo/MeetingFaceVO.java
  53. 3 2
      service-meeting/service-meeting-biz/src/main/resources/mapper/meeting/MeetingDeviceHeartbeatMapper.xml
  54. 104 10
      service-meeting/service-meeting-biz/src/main/resources/mapper/meeting/MeetingDeviceMapper.xml
  55. 11 5
      service-meeting/service-meeting-biz/src/main/resources/mapper/meeting/MeetingDevicePersonBindMapper.xml
  56. 0 21
      service-meeting/service-meeting-biz/src/main/resources/mapper/meeting/MeetingFaceMapper.xml
  57. 28 0
      service-meeting/service-meeting-biz/src/main/resources/mapper/meeting/SpaceDeviceMeetingMapper.xml

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

@@ -68,7 +68,7 @@ public class MybatisGenerator {
         // strategy.setTablePrefix("t_"); // 表名前缀
         strategy.setEntityLombokModel(true); //使用lombok
         //修改自己想要生成的表
-        strategy.setInclude("meeting_face");  // 逆向工程使用的表   如果要生成多个,这里可以传入String[]
+        strategy.setInclude("meeting_device");  // 逆向工程使用的表   如果要生成多个,这里可以传入String[]
         mpg.setStrategy(strategy);
 
         // 关闭默认 xml 生成,调整生成 至 根目录

+ 1 - 2
service-eg/service-eg-biz/src/main/java/com/usky/eg/adb/EgDeviceAdbConfiguration.java

@@ -1,10 +1,9 @@
 package com.usky.eg.adb;
 
-import com.usky.eg.mqtt.EgMqttProperties;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.context.annotation.Configuration;
 
 @Configuration
-@EnableConfigurationProperties({EgDeviceAdbProperties.class, EgMqttProperties.class})
+@EnableConfigurationProperties({EgDeviceAdbProperties.class})
 public class EgDeviceAdbConfiguration {
 }

+ 69 - 0
service-eg/service-eg-biz/src/main/java/com/usky/eg/config/mqtt/MqttBaseConfig.java

@@ -0,0 +1,69 @@
+package com.usky.eg.config.mqtt;
+
+import lombok.Data;
+import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory;
+import org.springframework.integration.mqtt.core.MqttPahoClientFactory;
+import org.springframework.stereotype.Component;
+
+/**
+ * MQTT 基础连接配置(对齐 service-iot {@code MqttBaseConfig})。
+ * <p>
+ * 配置前缀 {@code mqtt};远程升级业务字段(主题模板等)一并放在此前缀下。
+ */
+@ConditionalOnProperty(prefix = "mqtt", value = "enabled", havingValue = "true")
+@Data
+@Component
+@ConfigurationProperties(prefix = "mqtt")
+public class MqttBaseConfig {
+
+    private String username;
+
+    private String password;
+
+    /** 例如 tcp://47.98.201.73:1883 */
+    private String url;
+
+    private int keepAliveInterval = 60;
+
+    private int completionTimeout = 3000;
+
+    /**
+     * 入站订阅主题,多个用英文逗号分隔;支持 MQTT 通配符 {@code +}/{@code #}。
+     * 示例:usky/device/+/status,usky/device/+/event
+     */
+    private String subTopics = "usky/device/+/status";
+
+    /**
+     * 发布主题模板,须包含占位符 {@code {deviceId}}。
+     * 示例:usky/device/{deviceId}/command
+     */
+    private String commandTopicTemplate = "usky/device/{deviceId}/command";
+
+    /** 发布 QoS,0/1/2 */
+    private int qos = 1;
+
+    /**
+     * 下发 JSON 中 {@code type} 字段,终端可在 {@code onCommand} 里按类型分支。
+     */
+    private String commandType = "egRemoteUpgrade";
+
+    @Bean
+    public MqttPahoClientFactory mqttClientFactory() {
+        DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
+        MqttConnectOptions options = new MqttConnectOptions();
+        options.setServerURIs(new String[]{this.getUrl()});
+        if (username != null && !username.isEmpty()) {
+            options.setUserName(username);
+        }
+        if (password != null && !password.isEmpty()) {
+            options.setPassword(password.toCharArray());
+        }
+        options.setKeepAliveInterval(keepAliveInterval);
+        factory.setConnectionOptions(options);
+        return factory;
+    }
+}

+ 65 - 0
service-eg/service-eg-biz/src/main/java/com/usky/eg/config/mqtt/MqttInConfig.java

@@ -0,0 +1,65 @@
+package com.usky.eg.config.mqtt;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.integration.channel.DirectChannel;
+import org.springframework.integration.core.MessageProducer;
+import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter;
+import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter;
+import org.springframework.messaging.MessageChannel;
+import org.springframework.util.StringUtils;
+
+/**
+ * MQTT 入站配置(对齐 service-iot {@code MqttInConfig})。
+ */
+@ConditionalOnProperty(prefix = "mqtt", value = "enabled", havingValue = "true")
+@Configuration
+public class MqttInConfig {
+
+    @Autowired
+    private MqttBaseConfig mqttBaseConfig;
+
+    public static final String CHANNEL_NAME_INPUT = "mqttInputChannel";
+
+    @Bean(name = CHANNEL_NAME_INPUT)
+    public MessageChannel mqttInputChannel() {
+        return new DirectChannel();
+    }
+
+    /**
+     * 消息订阅绑定-消费者
+     */
+    @Bean
+    public MessageProducer inbound() {
+        String[] topics = resolveSubTopics();
+        String clientId = "service-eg-mqtt-in-" + System.currentTimeMillis();
+        MqttPahoMessageDrivenChannelAdapter adapter = new MqttPahoMessageDrivenChannelAdapter(
+                clientId, mqttBaseConfig.mqttClientFactory(), topics);
+        adapter.setCompletionTimeout(mqttBaseConfig.getCompletionTimeout());
+        adapter.setConverter(new DefaultPahoMessageConverter());
+        adapter.setQos(mqttBaseConfig.getQos() >= 0 && mqttBaseConfig.getQos() <= 2
+                ? mqttBaseConfig.getQos() : 1);
+        adapter.setOutputChannel(mqttInputChannel());
+        return adapter;
+    }
+
+    private String[] resolveSubTopics() {
+        String raw = mqttBaseConfig.getSubTopics();
+        if (!StringUtils.hasText(raw)) {
+            throw new IllegalStateException("mqtt.sub-topics 不能为空(MQTT 入站订阅主题,逗号分隔)");
+        }
+        String[] parts = raw.split(",");
+        java.util.List<String> topics = new java.util.ArrayList<>();
+        for (String part : parts) {
+            if (StringUtils.hasText(part)) {
+                topics.add(part.trim());
+            }
+        }
+        if (topics.isEmpty()) {
+            throw new IllegalStateException("mqtt.sub-topics 解析后无有效主题");
+        }
+        return topics.toArray(new String[0]);
+    }
+}

+ 57 - 0
service-eg/service-eg-biz/src/main/java/com/usky/eg/config/mqtt/MqttOutConfig.java

@@ -0,0 +1,57 @@
+package com.usky.eg.config.mqtt;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.integration.annotation.MessagingGateway;
+import org.springframework.integration.annotation.ServiceActivator;
+import org.springframework.integration.channel.DirectChannel;
+import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler;
+import org.springframework.integration.mqtt.support.MqttHeaders;
+import org.springframework.messaging.MessageChannel;
+import org.springframework.messaging.MessageHandler;
+import org.springframework.messaging.handler.annotation.Header;
+
+/**
+ * MQTT 出站配置(对齐 service-iot {@code MqttOutConfig})。
+ */
+@ConditionalOnProperty(prefix = "mqtt", value = "enabled", havingValue = "true")
+@Configuration
+public class MqttOutConfig {
+
+    @Autowired
+    public MqttBaseConfig mqttBaseConfig;
+
+    public static final String CHANNEL_NAME_OUT = "mqttOutboundChannel";
+
+    public static final String MESSAGE_NAME = "messageOut";
+
+    public static final String DEFAULT_TOPIC = "testTopic";
+
+    @Bean(name = CHANNEL_NAME_OUT)
+    public MessageChannel mqttOutboundChannel() {
+        return new DirectChannel();
+    }
+
+    @Bean(name = MESSAGE_NAME)
+    @ServiceActivator(inputChannel = CHANNEL_NAME_OUT)
+    public MessageHandler outbound() {
+        String clientId = "service-eg-mqtt-out-" + System.currentTimeMillis();
+        MqttPahoMessageHandler messageHandler =
+                new MqttPahoMessageHandler(clientId, mqttBaseConfig.mqttClientFactory());
+        messageHandler.setAsync(true);
+        messageHandler.setDefaultTopic(DEFAULT_TOPIC);
+        return messageHandler;
+    }
+
+    @MessagingGateway(defaultRequestChannel = CHANNEL_NAME_OUT)
+    public interface MqttGateway {
+
+        void sendToMqtt(String payload);
+
+        void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, String payload);
+
+        void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, @Header(MqttHeaders.QOS) int qos, String payload);
+    }
+}

+ 8 - 7
service-eg/service-eg-biz/src/main/java/com/usky/eg/controller/web/EgDeviceController.java

@@ -79,26 +79,27 @@ public class EgDeviceController {
 
     /**
      * 删除
-     * @param id
+     * @param deviceUuid
      * @return
      */
-    @DeleteMapping("/{id}")
-    public ApiResult<Void> remove(@PathVariable("id") Integer id){
-        egDeviceService.remove(id);
+    @DeleteMapping("/{deviceUuid}")
+    public ApiResult<Void> remove(@PathVariable("deviceUuid") String deviceUuid){
+        egDeviceService.remove(deviceUuid);
         return ApiResult.success();
     }
 
     /**
      * 设备绑定人员
-     * @param params 包含 deviceId, personIds, isLoginNotify 的参数对象
+     * @param params 包含 deviceUuid, personIds, isLoginNotify 的参数对象
      * @return
      */
     @PostMapping("/bindPerson")
     public ApiResult<Void> bindPerson(@RequestBody Map<String, Object> params){
-        Integer deviceId = (Integer) params.get("deviceId");
+        Object uuidObj = params.get("deviceUuid");
+        String deviceUuid = uuidObj == null ? null : uuidObj.toString();
         String personIds = (String) params.get("personIds");
         Integer isLoginNotify = params.get("isLoginNotify") != null ? (Integer) params.get("isLoginNotify") : 0;
-        egDeviceService.bindPerson(deviceId, personIds, isLoginNotify);
+        egDeviceService.bindPerson(deviceUuid, personIds, isLoginNotify);
         return ApiResult.success();
     }
 

+ 38 - 45
service-eg/service-eg-biz/src/main/java/com/usky/eg/domain/EgDevice.java

@@ -1,71 +1,70 @@
 package com.usky.eg.domain;
 
-import com.baomidou.mybatisplus.annotation.FieldStrategy;
 import com.baomidou.mybatisplus.annotation.IdType;
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
-import java.time.LocalDateTime;
-import java.io.Serializable;
-
+import com.baomidou.mybatisplus.annotation.TableName;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
-import rx.BackpressureOverflow;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
 
 /**
- * <p>
- * 门禁_设备信息表
- * </p>
- *
- * @author zyj
- * @since 2024-11-27
+ * 空间设备主表 space_device(门禁扩展字段来自 space_device_access,不落主表)
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
+@TableName("space_device")
 public class EgDevice implements Serializable {
 
     private static final long serialVersionUID = 1L;
 
-    /**
-     * 主键id
-     */
-    @TableId(value = "id", type = IdType.AUTO)
-    private Integer id;
+    /** 门禁设备 */
+    public static final int DEVICE_TYPE_ACCESS = 1;
+    /** 会议室空间设备 */
+    public static final int DEVICE_TYPE_MEETING = 2;
 
     /**
-     * 设备uuid
+     * 设备唯一编号(主键)
      */
+    @TableId(value = "device_uuid", type = IdType.INPUT)
     private String deviceUuid;
 
     /**
-     * 设备ID
+     * 设备类型:1门禁设备,2会议室空间设备
      */
-    private String deviceId;
+    private Integer deviceType;
 
     /**
      * 设备名称
      */
     private String deviceName;
 
+    /**
+     * 设备编码
+     */
+    private String deviceCode;
+
     /**
      * 安装位置
      */
     private String installAddress;
 
     /**
-     * 业务状态;1:未激活,2:已激活,3:禁用
+     * 设备IP
      */
-    private Integer serviceStatus;
+    private String deviceIp;
 
     /**
-     * IP
+     * 设备图片/屏保
      */
-    private String deviceIp;
+    private String imgPath;
 
     /**
-     * 绑定人脸信息
+     * 备注/标识语
      */
-    @TableField(updateStrategy = FieldStrategy.IGNORED)
-    private String bindFace;
+    private String remark;
 
     /**
      * 创建者
@@ -98,43 +97,37 @@ public class EgDevice implements Serializable {
     private Integer tenantId;
 
     /**
-     * 绑定人员信息(从 eg_device_person_bind 表汇总的 person_id 列表,以逗号分隔
+     * 业务状态;1:未激活,2:已激活,3:禁用(space_device_access
      */
-    @TableField("bind_person")
-    private String bindPerson;
-
-    /**
-     * 屏保
-     */
-    private String imgPath;
-
-    /**
-     * 标识语
-     */
-    private String remark;
+    @TableField(exist = false)
+    private Integer serviceStatus;
 
     /**
-     * 开门方式
+     * 开门方式(space_device_access)
      */
+    @TableField(exist = false)
     private String openMode;
 
     /**
-     * 密码
+     * 开门密码(space_device_access)
      */
+    @TableField(exist = false)
     private String password;
 
     /**
-     * 工作状态
+     * 工作状态;1:办公,2:出差,3:会客,4:空闲(space_device_access)
      */
+    @TableField(exist = false)
     private String workStatus;
 
     /**
-     * 设备code
+     * 绑定人员信息(非表字段;从 space_device_person_bind 汇总 person_id)
      */
-    private String deviceCode;
+    @TableField(exist = false)
+    private String bindPerson;
 
     /**
-     * 设备状态;1:在线,0:离线(从心跳表获取,不映射到数据库
+     * 设备状态;1:在线,0:离线(从心跳表获取)
      */
     @TableField(exist = false)
     private Integer deviceStatus;

+ 17 - 21
service-eg/service-eg-biz/src/main/java/com/usky/eg/domain/EgDeviceHeartbeat.java

@@ -2,30 +2,26 @@ package com.usky.eg.domain;
 
 import com.baomidou.mybatisplus.annotation.IdType;
 import com.baomidou.mybatisplus.annotation.TableId;
-
-import java.time.LocalDateTime;
-import java.io.Serializable;
-
+import com.baomidou.mybatisplus.annotation.TableName;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
 /**
- * <p>
- * 门禁设备心跳表
- * </p>
- *
- * @author fhs
- * @since 2026-02-03
+ * 空间设备心跳表(space_device_heartbeat)
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
+@TableName("space_device_heartbeat")
 public class EgDeviceHeartbeat implements Serializable {
 
     private static final long serialVersionUID = 1L;
 
     /**
-     * 门禁设备心跳表主键ID
+     * 空间设备心跳表主键ID
      */
     @TableId(value = "id", type = IdType.AUTO)
     private Integer id;
@@ -46,9 +42,14 @@ public class EgDeviceHeartbeat implements Serializable {
     private String macAddr;
 
     /**
-     * 设备类型(1.会议屏 2.信息发布屏 3.综合屏)
+     * 设备类型(1.会议屏 2.信息发布屏 3.门禁综合屏)
      */
-    private Boolean deviceType;
+    private Integer deviceType;
+
+    /**
+     * 设备状态(0:离线,1:在线)
+     */
+    private Integer deviceStatus;
 
     /**
      * 创建日期
@@ -62,23 +63,18 @@ public class EgDeviceHeartbeat implements Serializable {
     private String model;
 
     /**
-     *  设备厂商
+     * 厂商
      */
     private String manuFacturer;
 
     /**
-     * 设备版本号
+     * 系统版本
      */
     private String version;
 
     /**
-     * 设备sdk
+     * SDK版本
      */
     private String sdk;
 
-    /**
-     * 设备状态;1:在线,0:离线
-     */
-    private Integer deviceStatus;
-
 }

+ 5 - 14
service-eg/service-eg-biz/src/main/java/com/usky/eg/domain/EgDevicePersonBind.java

@@ -6,9 +6,7 @@ import lombok.Data;
 import lombok.EqualsAndHashCode;
 
 /**
- * <p>
- * 门禁设备人员绑定表 eg_device_person_bind
- * </p>
+ * 空间设备人员绑定表 space_device_person_bind
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
@@ -16,19 +14,12 @@ public class EgDevicePersonBind implements Serializable {
 
     private static final long serialVersionUID = 1L;
 
-    /**
-     * 设备主键ID
-     */
-    private Integer deviceId;
+    /** 设备唯一编号(space_device.device_uuid) */
+    private String deviceUuid;
 
-    /**
-     * 人员ID
-     */
+    /** 人员ID */
     private Integer personId;
 
-    /**
-     * 是否打开登录通知(1 表示是,0 表示否,默认0)
-     */
+    /** 是否打开登录通知(1 表示是,0 表示否,默认0) */
     private Integer isLoginNotify;
 }
-

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

@@ -3,113 +3,71 @@ package com.usky.eg.domain;
 import com.baomidou.mybatisplus.annotation.IdType;
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
-import java.time.LocalDateTime;
-import java.io.Serializable;
-import java.util.List;
-
+import com.baomidou.mybatisplus.annotation.TableName;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
 /**
- * <p>
- * 门禁_通行记录表
- * </p>
- *
- * @author zyj
- * @since 2024-11-27
+ * 空间设备门禁通行记录表 space_device_access_record
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
+@TableName("space_device_access_record")
 public class EgRecord implements Serializable {
 
     private static final long serialVersionUID = 1L;
 
-    /**
-     * 主键id
-     */
+    /** 主键id */
     @TableId(value = "id", type = IdType.AUTO)
     private Integer id;
 
-    /**
-     * 用户姓名
-     */
+    /** 用户姓名 */
     private String userName;
 
-    /**
-     * 通行时间
-     */
+    /** 通行时间 */
     private LocalDateTime passTime;
 
-    /**
-     * 门禁设备主键id
-     */
-    private Integer egDeviceId;
+    /** 空间设备唯一编号(space_device.device_uuid) */
+    private String deviceUuid;
 
-    /**
-     * 通行方式(0、其它 1、人脸 2、刷卡 3、手机 4、电脑)
-     */
+    /** 通行方式(1、人脸 2、刷卡 3、手机) */
     private Integer passType;
 
-    /**
-     * 人脸图片数据 base_64编码
-     */
+    /** 人脸图片数据 base_64编码 */
     private String faceBase;
 
-    /**
-     * 卡号
-     */
+    /** 卡号 */
     private String cardNum;
 
-    /**
-     * 通行结果
-     */
+    /** 通行结果 */
     private String passResult;
 
-    /**
-     * 创建者
-     */
+    /** 创建者 */
     private String createBy;
 
-    /**
-     * 更新者
-     */
+    /** 更新者 */
     private String updateBy;
 
-    /**
-     * 创建日期
-     */
+    /** 创建日期 */
     private LocalDateTime createTime;
 
-    /**
-     * 更新时间
-     */
+    /** 更新时间 */
     private LocalDateTime updateTime;
 
-    /**
-     * 组织机构ID
-     */
+    /** 组织机构ID */
     private Integer deptId;
 
-    /**
-     * 租户ID
-     */
+    /** 租户ID */
     private Integer tenantId;
 
-    /**
-     * 设备Uuid
-     */
-    @TableField(exist = false)
-    private String deviceUuid;
-
-    /**
-     * 域名
-     */
+    /** 租户域名(非表字段,设备端上报时用于解析租户) */
     @TableField(exist = false)
     private String domain;
 
-    /**
-     * 门禁设备信息集合
-     */
+    /** 关联设备信息(列表回填) */
     @TableField(exist = false)
     private EgDevice egDevice;
 }

+ 43 - 0
service-eg/service-eg-biz/src/main/java/com/usky/eg/domain/SpaceDeviceAccess.java

@@ -0,0 +1,43 @@
+package com.usky.eg.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * 门禁设备专属扩展表 space_device_access
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("space_device_access")
+public class SpaceDeviceAccess implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @TableId(value = "device_uuid", type = IdType.INPUT)
+    private String deviceUuid;
+
+    /** 门禁设备ID(门禁专用) */
+    private String deviceId;
+
+    /** 业务状态;1:未激活,2:已激活,3:禁用 */
+    private Integer serviceStatus;
+
+    /** 开门方式 */
+    private String openMode;
+
+    /** 开门密码 */
+    private String password;
+
+    /** 工作状态;1:办公,2:出差,3:会客,4:空闲 */
+    private String workStatus;
+
+    private LocalDateTime createTime;
+
+    private LocalDateTime updateTime;
+}

+ 33 - 0
service-eg/service-eg-biz/src/main/java/com/usky/eg/listener/MqttListener.java

@@ -0,0 +1,33 @@
+package com.usky.eg.listener;
+
+import com.usky.eg.config.mqtt.MqttInConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.integration.annotation.ServiceActivator;
+import org.springframework.messaging.MessageHandler;
+import org.springframework.stereotype.Component;
+
+/**
+ * MQTT 入站消息处理(对齐 service-iot {@code MqttListener})。
+ */
+@ConditionalOnProperty(prefix = "mqtt", value = "enabled", havingValue = "true")
+@Component
+public class MqttListener {
+
+    public static final String MESSAGE_NAME = "messageInput";
+
+    private static final Logger logger = LoggerFactory.getLogger(MqttListener.class);
+
+    @Bean(MESSAGE_NAME)
+    @ServiceActivator(inputChannel = MqttInConfig.CHANNEL_NAME_INPUT)
+    public MessageHandler handler() {
+        return message -> {
+            String payload = message.getPayload() != null ? message.getPayload().toString() : "";
+            Object mqttReceivedTopic = message.getHeaders().get("mqtt_receivedTopic");
+            String topic = mqttReceivedTopic != null ? mqttReceivedTopic.toString() : "";
+            logger.info("接收到 mqtt消息,主题:{} 消息:{}", topic, payload);
+        };
+    }
+}

+ 10 - 0
service-eg/service-eg-biz/src/main/java/com/usky/eg/mapper/EgDeviceMapper.java

@@ -1,10 +1,15 @@
 package com.usky.eg.mapper;
 
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.usky.eg.domain.EgDevice;
 import com.usky.common.mybatis.core.CrudMapper;
+import com.usky.eg.service.vo.EgDeviceRequestVO;
 import org.apache.ibatis.annotations.Param;
 import org.springframework.stereotype.Repository;
 
+import java.util.List;
+
 /**
  * <p>
  * 门禁_设备信息表 Mapper 接口
@@ -17,4 +22,9 @@ import org.springframework.stereotype.Repository;
 public interface EgDeviceMapper extends CrudMapper<EgDevice> {
 
     Integer sysTenantId(@Param("domain") String domain);
+
+    IPage<EgDevice> selectPageJoin(Page<EgDevice> page,
+                                   @Param("req") EgDeviceRequestVO req,
+                                   @Param("tenantId") Integer tenantId,
+                                   @Param("deviceUuids") List<String> deviceUuids);
 }

+ 10 - 31
service-eg/service-eg-biz/src/main/java/com/usky/eg/mapper/EgDevicePersonBindMapper.java

@@ -9,36 +9,15 @@ 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);
-
-    /**
-     * 新增绑定关系
-     */
+    List<EgDevicePersonBind> selectByDeviceUuids(@Param("deviceUuids") List<String> deviceUuids);
+
+    Integer countByDeviceUuid(@Param("deviceUuid") String deviceUuid);
+
+    Integer countByUserIdAndDeviceUuid(@Param("userId") Long userId, @Param("deviceUuid") String deviceUuid);
+
+    List<String> selectDeviceUuidsByUserId(@Param("userId") Long userId);
+
+    void deleteByDeviceUuid(@Param("deviceUuid") String deviceUuid);
+
     void insert(EgDevicePersonBind bind);
 }
-

+ 9 - 0
service-eg/service-eg-biz/src/main/java/com/usky/eg/mapper/SpaceDeviceAccessMapper.java

@@ -0,0 +1,9 @@
+package com.usky.eg.mapper;
+
+import com.usky.common.mybatis.core.CrudMapper;
+import com.usky.eg.domain.SpaceDeviceAccess;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface SpaceDeviceAccessMapper extends CrudMapper<SpaceDeviceAccess> {
+}

+ 0 - 67
service-eg/service-eg-biz/src/main/java/com/usky/eg/mqtt/EgMqttOutboundConfig.java

@@ -1,67 +0,0 @@
-package com.usky.eg.mqtt;
-
-import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.integration.annotation.MessagingGateway;
-import org.springframework.integration.annotation.ServiceActivator;
-import org.springframework.integration.channel.DirectChannel;
-import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory;
-import org.springframework.integration.mqtt.core.MqttPahoClientFactory;
-import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler;
-import org.springframework.integration.mqtt.support.MqttHeaders;
-import org.springframework.messaging.MessageChannel;
-import org.springframework.messaging.MessageHandler;
-import org.springframework.messaging.handler.annotation.Header;
-
-/**
- * 出站 MQTT(发布远程升级下行指令)。
- */
-@Configuration
-public class EgMqttOutboundConfig {
-
-    public static final String CHANNEL_NAME_OUT = "egMqttOutboundChannel";
-    public static final String DEFAULT_TOPIC = "eg/default";
-
-    @Autowired
-    private EgMqttProperties egMqttProperties;
-
-    @Bean
-    public MqttPahoClientFactory egMqttPahoClientFactory() {
-        DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
-        MqttConnectOptions options = new MqttConnectOptions();
-        options.setServerURIs(new String[]{egMqttProperties.getUrl()});
-        String user = egMqttProperties.getUsername();
-        if (user != null && !user.isEmpty()) {
-            options.setUserName(user);
-        }
-        String pwd = egMqttProperties.getPassword();
-        if (pwd != null && !pwd.isEmpty()) {
-            options.setPassword(pwd.toCharArray());
-        }
-        factory.setConnectionOptions(options);
-        return factory;
-    }
-
-    @Bean(name = CHANNEL_NAME_OUT)
-    public MessageChannel egMqttOutboundChannel() {
-        return new DirectChannel();
-    }
-
-    @Bean
-    @ServiceActivator(inputChannel = CHANNEL_NAME_OUT)
-    public MessageHandler egMqttOutbound(MqttPahoClientFactory egMqttPahoClientFactory) {
-        String clientId = "service-eg-mqtt-out-" + System.currentTimeMillis();
-        MqttPahoMessageHandler handler = new MqttPahoMessageHandler(clientId, egMqttPahoClientFactory);
-        handler.setAsync(true);
-        handler.setDefaultTopic(DEFAULT_TOPIC);
-        return handler;
-    }
-
-    @MessagingGateway(defaultRequestChannel = CHANNEL_NAME_OUT)
-    public interface EgMqttPublishGateway {
-
-        void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, @Header(MqttHeaders.QOS) int qos, String payload);
-    }
-}

+ 0 - 81
service-eg/service-eg-biz/src/main/java/com/usky/eg/mqtt/EgMqttProperties.java

@@ -1,81 +0,0 @@
-package com.usky.eg.mqtt;
-
-import org.springframework.boot.context.properties.ConfigurationProperties;
-
-/**
- * 远程升级 MQTT 下发(服务端发布,终端 {@code connectWithDevice} 订阅)。
- * <p>
- * 配置前缀 {@code eg.mqtt};{@link com.usky.eg.controller.web.EgDeviceAdbController#remoteUpgrade} 仅通过本配置连接 Broker 并发布。
- */
-@ConfigurationProperties(prefix = "eg.mqtt")
-public class EgMqttProperties {
-
-    /** 例如 tcp://47.98.201.73:1883 */
-    private String url = "tcp://47.98.201.73:1883";
-
-    private String username = "usky";
-
-    private String password = "usky";
-
-    /**
-     * 发布主题模板,须包含占位符 {@code {deviceId}},与终端 {@code deviceId: getSerial()} 一致。
-     * 示例:usky/device/{deviceId}/command
-     */
-    private String commandTopicTemplate = "usky/device/{deviceId}/command";
-
-    /** 发布 QoS,0/1/2 */
-    private int qos = 1;
-
-    /**
-     * 下发 JSON 中 {@code type} 字段,终端可在 {@code onCommand} 里按类型分支。
-     */
-    private String commandType = "egRemoteUpgrade";
-
-    public String getUrl() {
-        return url;
-    }
-
-    public void setUrl(String url) {
-        this.url = url;
-    }
-
-    public String getUsername() {
-        return username;
-    }
-
-    public void setUsername(String username) {
-        this.username = username;
-    }
-
-    public String getPassword() {
-        return password;
-    }
-
-    public void setPassword(String password) {
-        this.password = password;
-    }
-
-    public String getCommandTopicTemplate() {
-        return commandTopicTemplate;
-    }
-
-    public void setCommandTopicTemplate(String commandTopicTemplate) {
-        this.commandTopicTemplate = commandTopicTemplate;
-    }
-
-    public int getQos() {
-        return qos;
-    }
-
-    public void setQos(int qos) {
-        this.qos = qos;
-    }
-
-    public String getCommandType() {
-        return commandType;
-    }
-
-    public void setCommandType(String commandType) {
-        this.commandType = commandType;
-    }
-}

+ 3 - 5
service-eg/service-eg-biz/src/main/java/com/usky/eg/service/EgDeviceService.java

@@ -28,9 +28,7 @@ public interface EgDeviceService extends CrudService<EgDevice> {
 
     void attachUpdate(EgDevice egDevice);
 
-    void remove(Integer id);
-
-    boolean checkNameUnique(EgDevice egDevice);
+    void remove(String deviceUuid);
 
     boolean checkDeviceNameUnique(EgDevice egDevice);
 
@@ -39,11 +37,11 @@ public interface EgDeviceService extends CrudService<EgDevice> {
     /**
      * 设备与人员绑定
      *
-     * @param deviceId      设备主键ID
+     * @param deviceUuid    设备唯一编号
      * @param personIds     人员ID,多个以逗号分隔
      * @param isLoginNotify 是否打开登录通知(1 表示是,0 表示否)
      */
-    void bindPerson(Integer deviceId, String personIds, Integer isLoginNotify);
+    void bindPerson(String deviceUuid, String personIds, Integer isLoginNotify);
 
     /** 绑定表人员分页(可选姓名、人脸状态) */
     CommonPage<EgDeviceBindFacePersonVO> pageBindFacePersons(EgDeviceBindFacePersonQueryVO queryVO);

+ 3 - 0
service-eg/service-eg-biz/src/main/java/com/usky/eg/service/impl/EgDeviceHeartbeatServiceImpl.java

@@ -41,6 +41,9 @@ public class EgDeviceHeartbeatServiceImpl extends AbstractCrudService<EgDeviceHe
         if (heartbeat.getDeviceType() == null) {
             throw new BusinessException("设备类型不能为空!");
         }
+        if (heartbeat.getDeviceStatus() == null) {
+            heartbeat.setDeviceStatus(1);
+        }
 
         // 查询是否存在该设备的心跳记录
         LambdaQueryWrapper<EgDeviceHeartbeat> queryWrapper = Wrappers.lambdaQuery();

+ 9 - 9
service-eg/service-eg-biz/src/main/java/com/usky/eg/service/impl/EgDeviceRemoteUpgradeMqttServiceImpl.java

@@ -3,8 +3,8 @@ package com.usky.eg.service.impl;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.toolkit.StringUtils;
 import com.usky.common.core.exception.BusinessException;
-import com.usky.eg.mqtt.EgMqttOutboundConfig.EgMqttPublishGateway;
-import com.usky.eg.mqtt.EgMqttProperties;
+import com.usky.eg.config.mqtt.MqttBaseConfig;
+import com.usky.eg.config.mqtt.MqttOutConfig;
 import com.usky.eg.service.EgDeviceRemoteUpgradeMqttService;
 import com.usky.eg.service.vo.EgDeviceAdbRemoteUpgradeBatchItemVO;
 import com.usky.eg.service.vo.EgDeviceAdbRemoteUpgradeBatchResultVO;
@@ -24,10 +24,10 @@ import java.util.UUID;
 public class EgDeviceRemoteUpgradeMqttServiceImpl implements EgDeviceRemoteUpgradeMqttService {
 
     @Autowired
-    private EgMqttPublishGateway mqttPublishGateway;
+    private MqttOutConfig.MqttGateway mqttGateway;
 
     @Autowired
-    private EgMqttProperties mqttProperties;
+    private MqttBaseConfig mqttBaseConfig;
 
     @Override
     public EgDeviceAdbResultVO publishRemoteUpgrade(EgDeviceAdbRemoteUpgradeVO vo) {
@@ -112,8 +112,8 @@ public class EgDeviceRemoteUpgradeMqttServiceImpl implements EgDeviceRemoteUpgra
 
     private EgDeviceAdbResultVO publishOne(String topic, String payload, String cmdId) {
         try {
-            int qos = clampQos(mqttProperties.getQos());
-            mqttPublishGateway.sendToMqtt(topic, qos, payload);
+            int qos = clampQos(mqttBaseConfig.getQos());
+            mqttGateway.sendToMqtt(topic, qos, payload);
             log.info("mqtt remote upgrade published topic={} cmdId={}", topic, cmdId);
             return EgDeviceAdbResultVO.ok("topic=" + topic + "; cmdId=" + cmdId);
         } catch (Exception e) {
@@ -126,7 +126,7 @@ public class EgDeviceRemoteUpgradeMqttServiceImpl implements EgDeviceRemoteUpgra
                                 String deviceCode, String deviceIp, String devicePort) {
         JSONObject o = new JSONObject();
         o.put("id", cmdId);
-        o.put("type", mqttProperties.getCommandType() != null ? mqttProperties.getCommandType() : "egRemoteUpgrade");
+        o.put("type", mqttBaseConfig.getCommandType() != null ? mqttBaseConfig.getCommandType() : "egRemoteUpgrade");
         o.put("fileUrl", fileUrl.trim());
         if (StringUtils.isNotBlank(launchPackage)) {
             o.put("launchPackage", launchPackage.trim());
@@ -154,9 +154,9 @@ public class EgDeviceRemoteUpgradeMqttServiceImpl implements EgDeviceRemoteUpgra
     }
 
     private String buildTopic(String deviceId) {
-        String template = mqttProperties.getCommandTopicTemplate();
+        String template = mqttBaseConfig.getCommandTopicTemplate();
         if (StringUtils.isBlank(template) || !template.contains("{deviceId}")) {
-            throw new BusinessException("eg.mqtt.command-topic-template 须为非空且包含占位符 {deviceId}");
+            throw new BusinessException("mqtt.command-topic-template 须为非空且包含占位符 {deviceId}");
         }
         return template.replace("{deviceId}", deviceId);
     }

+ 188 - 232
service-eg/service-eg-biz/src/main/java/com/usky/eg/service/impl/EgDeviceServiceImpl.java

@@ -2,7 +2,6 @@ package com.usky.eg.service.impl;
 
 import com.alibaba.fastjson.JSON;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
 import com.baomidou.mybatisplus.core.toolkit.StringUtils;
@@ -16,11 +15,13 @@ import com.usky.eg.domain.EgDevice;
 import com.usky.eg.domain.EgDeviceHeartbeat;
 import com.usky.eg.domain.EgDevicePersonBind;
 import com.usky.eg.domain.EgRecord;
+import com.usky.eg.domain.SpaceDeviceAccess;
 import com.usky.eg.domain.SysPerson;
 import com.usky.eg.mapper.EgDeviceMapper;
 import com.usky.eg.mapper.EgDeviceHeartbeatMapper;
 import com.usky.eg.mapper.EgDevicePersonBindMapper;
 import com.usky.eg.mapper.EgRecordMapper;
+import com.usky.eg.mapper.SpaceDeviceAccessMapper;
 import com.usky.eg.mapper.SysPersonMapper;
 import com.usky.eg.service.EgDeviceService;
 import com.usky.common.mybatis.core.AbstractCrudService;
@@ -37,12 +38,7 @@ import java.util.*;
 import java.util.concurrent.CompletableFuture;
 
 /**
- * <p>
- * 门禁_设备信息表 服务实现类
- * </p>
- *
- * @author zyj
- * @since 2024-11-27
+ * 空间设备(门禁)服务实现
  */
 @Service
 public class EgDeviceServiceImpl extends AbstractCrudService<EgDeviceMapper, EgDevice> implements EgDeviceService {
@@ -60,151 +56,66 @@ public class EgDeviceServiceImpl extends AbstractCrudService<EgDeviceMapper, EgD
     private EgRecordMapper egRecordMapper;
     @Autowired
     private SysPersonMapper sysPersonMapper;
+    @Autowired
+    private SpaceDeviceAccessMapper spaceDeviceAccessMapper;
 
     @Override
-    public CommonPage<EgDevice> page(EgDeviceRequestVO requestVO){
-        IPage<EgDevice> page = new Page<>(requestVO.getCurrent(),requestVO.getSize());
+    public CommonPage<EgDevice> page(EgDeviceRequestVO requestVO) {
         Integer tenantId;
-
-        if(StringUtils.isNotBlank(requestVO.getDeviceCode())){
+        if (StringUtils.isNotBlank(requestVO.getDeviceCode())) {
             LambdaQueryWrapper<EgDevice> tempQueryWrapper = new LambdaQueryWrapper<>();
             tempQueryWrapper.eq(EgDevice::getDeviceCode, requestVO.getDeviceCode());
             EgDevice tempDevice = egDeviceMapper.selectOne(tempQueryWrapper);
-            if(tempDevice == null){
-                // 未查询到数据,返回空数组
+            if (tempDevice == null) {
                 return new CommonPage<>(new ArrayList<>(), 0L, requestVO.getSize(), requestVO.getCurrent());
             }
             tenantId = tempDevice.getTenantId();
-        }else{
+        } else {
             tenantId = SecurityUtils.getTenantId();
         }
+        applyDefaultDeviceType(requestVO);
 
-        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())
-                .eq(null != requestVO.getDeviceUuid(),EgDevice::getDeviceUuid, requestVO.getDeviceUuid())
-                .eq(StringUtils.isNotBlank(requestVO.getDeviceCode()),EgDevice::getDeviceCode, requestVO.getDeviceCode())
-                .eq(EgDevice::getTenantId,tenantId)
-                .orderByDesc(EgDevice::getId);
-        page = this.page(page,queryWrapper);
-
-        if(!page.getRecords().isEmpty()){
-            // 检查设备心跳
-            List<String> validDeviceCodes = new ArrayList<>();
-            Map<String, EgDevice> deviceCodeMap = new HashMap<>();
-            for (EgDevice device : page.getRecords()) {
-                String deviceCode = device.getDeviceCode();
-                if (StringUtils.isNotBlank(deviceCode)) {
-                    validDeviceCodes.add(deviceCode);
-                    deviceCodeMap.put(deviceCode, device);
-                } else {
-                    // deviceCode为空,直接设置为离线
-                    device.setDeviceStatus(0);
-                }
-            }
-
-            // 批量查询心跳数据
-            if (!validDeviceCodes.isEmpty()) {
-                LambdaQueryWrapper<EgDeviceHeartbeat> heartbeatQueryWrapper = Wrappers.lambdaQuery();
-                heartbeatQueryWrapper.in(EgDeviceHeartbeat::getDeviceCode, validDeviceCodes)
-                        .orderByDesc(EgDeviceHeartbeat::getCreateTime);
-                List<EgDeviceHeartbeat> heartbeatList = egDeviceHeartbeatMapper.selectList(heartbeatQueryWrapper);
-                
-                // 构建deviceCode到最新心跳记录的映射,并同时设置deviceStatus
-                Map<String, EgDeviceHeartbeat> deviceHeartbeatMap = new HashMap<>();
-                for (EgDeviceHeartbeat heartbeat : heartbeatList) {
-                    String deviceCode = heartbeat.getDeviceCode();
-                    if (StringUtils.isNotBlank(deviceCode)) {
-                        // 如果已存在,保留最新的(因为已按createTime降序排列)
-                        if (deviceHeartbeatMap.putIfAbsent(deviceCode, heartbeat) == null) {
-                            // 首次遇到该deviceCode,从心跳表直接获取deviceStatus并设置到EgDevice
-                            EgDevice device = deviceCodeMap.get(deviceCode);
-                            if (device != null && heartbeat.getDeviceStatus() != null) {
-                                device.setDeviceStatus(heartbeat.getDeviceStatus());
-                            }
-                        }
-                    }
-                }
-                
-                // 如果未找到对应的设备心跳记录,则设置DeviceStatus为0(离线)
-                for (String deviceCode : validDeviceCodes) {
-                    if (!deviceHeartbeatMap.containsKey(deviceCode)) {
-                        EgDevice device = deviceCodeMap.get(deviceCode);
-                        if (device != null) {
-                            device.setDeviceStatus(0);
-                        }
-                    }
-                }
-            } else {
-                // 如果没有有效的deviceCode,将所有设备的deviceStatus设置为0
-                for (EgDevice device : page.getRecords()) {
-                    device.setDeviceStatus(0);
-                }
-            }
-
-        }
-
-        // 查询并填充绑定人员信息(来自 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());
+        Page<EgDevice> page = new Page<>(requestVO.getCurrent(), requestVO.getSize());
+        IPage<EgDevice> result = egDeviceMapper.selectPageJoin(page, requestVO, tenantId, null);
+        fillOnlineStatus(result.getRecords());
+        fillBindPerson(result.getRecords());
+        return new CommonPage<>(result.getRecords(), result.getTotal(), requestVO.getSize(), requestVO.getCurrent());
     }
 
     @Override
-    public CommonPage<EgDevice> wePage(EgDeviceRequestVO requestVO){
+    public CommonPage<EgDevice> wePage(EgDeviceRequestVO requestVO) {
         long userId = SecurityUtils.getUserId();
-        // 通过 sys_user_person -> sys_person -> eg_device_person_bind -> eg_device 查询当前用户绑定的设备ID列表
-        List<Integer> deviceIds = egDevicePersonBindMapper.selectDeviceIdsByUserId(userId);
-        if (CollectionUtils.isEmpty(deviceIds)) {
-            // 未绑定任何设备,返回空分页
+        List<String> deviceUuids = egDevicePersonBindMapper.selectDeviceUuidsByUserId(userId);
+        if (CollectionUtils.isEmpty(deviceUuids)) {
             return new CommonPage<>(new ArrayList<>(), 0L, requestVO.getSize(), requestVO.getCurrent());
         }
+        applyDefaultDeviceType(requestVO);
 
-        IPage<EgDevice> page = new Page<>(requestVO.getCurrent(),requestVO.getSize());
-        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());
+        Page<EgDevice> page = new Page<>(requestVO.getCurrent(), requestVO.getSize());
+        IPage<EgDevice> result = egDeviceMapper.selectPageJoin(page, requestVO, SecurityUtils.getTenantId(), deviceUuids);
+        fillBindPerson(result.getRecords());
+        return new CommonPage<>(result.getRecords(), result.getTotal(), requestVO.getSize(), requestVO.getCurrent());
     }
 
     @Override
     public void add(EgDevice egDevice) {
         LocalDateTime now = LocalDateTime.now();
-        
-        // 1. 校验设备名称唯一性
+
         if (checkDeviceNameUnique(egDevice)) {
             throw new BusinessException("新增门禁设备'" + egDevice.getDeviceName() + "'失败,设备已存在");
         }
 
-        // 2. 校验设备IP是否已被绑定
         if (StringUtils.isNotBlank(egDevice.getDeviceIp())) {
             LambdaQueryWrapper<EgDevice> ipCheckWrapper = Wrappers.lambdaQuery();
             ipCheckWrapper.eq(EgDevice::getDeviceIp, egDevice.getDeviceIp())
                     .eq(EgDevice::getTenantId, SecurityUtils.getTenantId());
             EgDevice existingDeviceByIp = this.getOne(ipCheckWrapper);
             if (existingDeviceByIp != null) {
-                throw new BusinessException("设备IP'" + egDevice.getDeviceIp() + "'已被设备'" 
+                throw new BusinessException("设备IP'" + egDevice.getDeviceIp() + "'已被设备'"
                         + existingDeviceByIp.getDeviceName() + "'绑定,请更换IP或检查设备配置!");
             }
         }
 
-        // 3. 若未传deviceCode但传了deviceIp,尝试从心跳表中获取deviceCode
         if (StringUtils.isBlank(egDevice.getDeviceCode()) && StringUtils.isNotBlank(egDevice.getDeviceIp())) {
             LambdaQueryWrapper<EgDeviceHeartbeat> heartbeatCheckWrapper = Wrappers.lambdaQuery();
             heartbeatCheckWrapper.eq(EgDeviceHeartbeat::getIpAddr, egDevice.getDeviceIp())
@@ -215,46 +126,44 @@ public class EgDeviceServiceImpl extends AbstractCrudService<EgDeviceMapper, EgD
             }
         }
 
-        // 4. 校验设备编码是否已被绑定
         if (StringUtils.isNotBlank(egDevice.getDeviceCode())) {
             LambdaQueryWrapper<EgDevice> codeCheckWrapper = Wrappers.lambdaQuery();
             codeCheckWrapper.eq(EgDevice::getDeviceCode, egDevice.getDeviceCode())
                     .eq(EgDevice::getTenantId, SecurityUtils.getTenantId());
             EgDevice existingDeviceByCode = this.getOne(codeCheckWrapper);
             if (existingDeviceByCode != null) {
-                throw new BusinessException("设备编码'" + egDevice.getDeviceCode() + "'已被设备'" 
+                throw new BusinessException("设备编码'" + egDevice.getDeviceCode() + "'已被设备'"
                         + existingDeviceByCode.getDeviceName() + "'绑定,请更换设备编码或检查设备配置!");
             }
         }
 
-        // 5. 设置设备基本信息
         egDevice.setDeviceUuid(UUIDUtils.uuid());
+        egDevice.setDeviceType(EgDevice.DEVICE_TYPE_ACCESS);
         egDevice.setCreateBy(SecurityUtils.getUsername());
         egDevice.setCreateTime(now);
         egDevice.setTenantId(SecurityUtils.getTenantId());
-        egDevice.setOpenMode("人脸");
-        egDevice.setWorkStatus("4");
+        if (StringUtils.isBlank(egDevice.getOpenMode())) {
+            egDevice.setOpenMode("人脸");
+        }
+        if (StringUtils.isBlank(egDevice.getWorkStatus())) {
+            egDevice.setWorkStatus("4");
+        }
 
-        // 6. 保存设备
         this.save(egDevice);
+        saveAccess(egDevice, now);
 
-        // 7. 异步调用远程服务添加设备信息,静默处理异常,不影响设备保存成功
         String deviceUuid = egDevice.getDeviceUuid();
         String deviceName = egDevice.getDeviceName();
         if (StringUtils.isNotBlank(deviceUuid) && StringUtils.isNotBlank(deviceName)) {
-            String installAddress = StringUtils.isNotBlank(egDevice.getInstallAddress()) 
-                    ? egDevice.getInstallAddress() 
-                    : "";
-            Integer serviceStatus = egDevice.getServiceStatus() != null 
-                    ? egDevice.getServiceStatus() 
-                    : 1;
-            
+            String installAddress = StringUtils.isNotBlank(egDevice.getInstallAddress())
+                    ? egDevice.getInstallAddress() : "";
+            Integer serviceStatus = egDevice.getServiceStatus() != null ? egDevice.getServiceStatus() : 1;
             CompletableFuture.runAsync(() -> {
                 try {
-                    remoteIotTaskService.addDeviceInfo("502_USKY", deviceUuid, deviceUuid, 
+                    remoteIotTaskService.addDeviceInfo("502_USKY", deviceUuid, deviceUuid,
                             deviceName, installAddress, serviceStatus);
                 } catch (Exception e) {
-                    // 远程服务调用失败,静默处理,不影响设备保存成功
+                    // 远程服务调用失败,静默处理
                 }
             });
         }
@@ -262,24 +171,25 @@ public class EgDeviceServiceImpl extends AbstractCrudService<EgDeviceMapper, EgD
 
     @Override
     public void update(EgDevice egDevice) {
-        if(checkDeviceNameUnique(egDevice)){
-            throw new BusinessException("修改门禁设备'"+egDevice.getDeviceName()+"'失败,设备名称已存在");
+        if (StringUtils.isBlank(egDevice.getDeviceUuid())) {
+            throw new BusinessException("设备uuid不能为空");
+        }
+        if (checkDeviceNameUnique(egDevice)) {
+            throw new BusinessException("修改门禁设备'" + egDevice.getDeviceName() + "'失败,设备名称已存在");
         }
 
-        // 校验设备IP是否已被其他设备绑定(排除当前设备)
         if (StringUtils.isNotBlank(egDevice.getDeviceIp())) {
             LambdaQueryWrapper<EgDevice> ipCheckWrapper = Wrappers.lambdaQuery();
             ipCheckWrapper.eq(EgDevice::getDeviceIp, egDevice.getDeviceIp())
                     .eq(EgDevice::getTenantId, SecurityUtils.getTenantId())
-                    .ne(EgDevice::getId, egDevice.getId());
+                    .ne(EgDevice::getDeviceUuid, egDevice.getDeviceUuid());
             EgDevice existingDeviceByIp = this.getOne(ipCheckWrapper);
             if (existingDeviceByIp != null) {
-                throw new BusinessException("设备IP'" + egDevice.getDeviceIp() + "'已被设备'" 
+                throw new BusinessException("设备IP'" + egDevice.getDeviceIp() + "'已被设备'"
                         + existingDeviceByIp.getDeviceName() + "'绑定,请更换IP或检查设备配置!");
             }
         }
 
-        // 若传了deviceIp,尝试从心跳表中获取deviceCode
         if (StringUtils.isNotBlank(egDevice.getDeviceIp())) {
             LambdaQueryWrapper<EgDeviceHeartbeat> heartbeatCheckWrapper = Wrappers.lambdaQuery();
             heartbeatCheckWrapper.eq(EgDeviceHeartbeat::getIpAddr, egDevice.getDeviceIp())
@@ -290,102 +200,96 @@ public class EgDeviceServiceImpl extends AbstractCrudService<EgDeviceMapper, EgD
             }
         }
 
-        // 校验设备编码是否已被其他设备绑定(排除当前设备)
         if (StringUtils.isNotBlank(egDevice.getDeviceCode())) {
             LambdaQueryWrapper<EgDevice> codeCheckWrapper = Wrappers.lambdaQuery();
             codeCheckWrapper.eq(EgDevice::getDeviceCode, egDevice.getDeviceCode())
                     .eq(EgDevice::getTenantId, SecurityUtils.getTenantId())
-                    .ne(EgDevice::getId, egDevice.getId());
+                    .ne(EgDevice::getDeviceUuid, egDevice.getDeviceUuid());
             EgDevice existingDeviceByCode = this.getOne(codeCheckWrapper);
             if (existingDeviceByCode != null) {
-                throw new BusinessException("设备编码'" + egDevice.getDeviceCode() + "'已被设备'" 
+                throw new BusinessException("设备编码'" + egDevice.getDeviceCode() + "'已被设备'"
                         + existingDeviceByCode.getDeviceName() + "'绑定,请更换设备编码或检查设备配置!");
             }
         }
 
+        LocalDateTime now = LocalDateTime.now();
         egDevice.setUpdateBy(SecurityUtils.getUsername());
-        egDevice.setUpdateTime(LocalDateTime.now());
-
+        egDevice.setUpdateTime(now);
         this.updateById(egDevice);
+        saveAccess(egDevice, now);
     }
 
     @Override
-    public void attachUpdate(EgDevice egDevice){
-        if(checkDeviceNameUnique(egDevice)){
-            throw new BusinessException("新增门禁设备'"+egDevice.getDeviceName()+"'失败,设备已存在");
+    public void attachUpdate(EgDevice egDevice) {
+        if (StringUtils.isBlank(egDevice.getDeviceUuid())) {
+            throw new BusinessException("设备uuid不能为空");
         }
-
+        if (checkDeviceNameUnique(egDevice)) {
+            throw new BusinessException("新增门禁设备'" + egDevice.getDeviceName() + "'失败,设备已存在");
+        }
+        LocalDateTime now = LocalDateTime.now();
         egDevice.setUpdateBy(SecurityUtils.getUsername());
-        egDevice.setUpdateTime(LocalDateTime.now());
-
+        egDevice.setUpdateTime(now);
         this.updateById(egDevice);
+        saveAccess(egDevice, now);
     }
 
     @Override
-    public void remove(Integer id){
-        EgDevice egDevice = this.getById(id);
+    public void remove(String deviceUuid) {
+        if (StringUtils.isBlank(deviceUuid)) {
+            throw new BusinessException("设备uuid不能为空");
+        }
+        EgDevice egDevice = this.getById(deviceUuid);
         Optional.ofNullable(egDevice).orElseThrow(() -> new BusinessException("门禁设备信息不存在"));
 
-        // 校验 eg_device_person_bind 是否存在绑定人员
-        Integer personBindCount = egDevicePersonBindMapper.countByDeviceId(id);
+        Integer personBindCount = egDevicePersonBindMapper.countByDeviceUuid(deviceUuid);
         if (personBindCount != null && personBindCount > 0) {
             throw new BusinessException("已绑定人员不能删除");
         }
 
-        this.removeById(id);
+        spaceDeviceAccessMapper.deleteById(deviceUuid);
+        this.removeById(deviceUuid);
 
-        // 异步调用远程服务删除设备信息,静默处理异常,不影响设备删除
-        String deviceUuid = egDevice.getDeviceUuid();
-        if (StringUtils.isNotBlank(deviceUuid)) {
-            CompletableFuture.runAsync(() -> {
-                try {
-                    remoteIotTaskService.deleteDeviceInfo(deviceUuid);
-                } catch (Exception e) {
-                    // 远程服务调用失败,静默处理,不影响设备删除
-                }
-            });
-        }
+        CompletableFuture.runAsync(() -> {
+            try {
+                remoteIotTaskService.deleteDeviceInfo(deviceUuid);
+            } catch (Exception e) {
+                // 远程服务调用失败,静默处理
+            }
+        });
     }
 
-    /**
-     * 根据设备ID集合查询 eg_device_person_bind,将 person_id 按逗号拼接后写入设备的 bindPerson 字段
-     */
     private void fillBindPerson(List<EgDevice> devices) {
         if (CollectionUtils.isEmpty(devices)) {
             return;
         }
-
-        List<Integer> deviceIds = new ArrayList<>();
+        List<String> deviceUuids = new ArrayList<>();
         for (EgDevice device : devices) {
-            if (device != null && device.getId() != null) {
-                deviceIds.add(device.getId());
+            if (device != null && StringUtils.isNotBlank(device.getDeviceUuid())) {
+                deviceUuids.add(device.getDeviceUuid());
             }
         }
-
-        if (deviceIds.isEmpty()) {
+        if (deviceUuids.isEmpty()) {
             return;
         }
-
-        List<EgDevicePersonBind> bindList = egDevicePersonBindMapper.selectByDeviceIds(deviceIds);
+        List<EgDevicePersonBind> bindList = egDevicePersonBindMapper.selectByDeviceUuids(deviceUuids);
         if (CollectionUtils.isEmpty(bindList)) {
             return;
         }
-
-        Map<Integer, List<String>> devicePersonMap = new HashMap<>();
+        Map<String, List<String>> devicePersonMap = new HashMap<>();
         for (EgDevicePersonBind bind : bindList) {
-            if (bind.getDeviceId() == null || bind.getPersonId() == null) {
+            if (StringUtils.isBlank(bind.getDeviceUuid()) || bind.getPersonId() == null) {
                 continue;
             }
             devicePersonMap
-                    .computeIfAbsent(bind.getDeviceId(), k -> new ArrayList<>())
+                    .computeIfAbsent(bind.getDeviceUuid(), k -> new ArrayList<>())
                     .add(String.valueOf(bind.getPersonId()));
         }
-
         for (EgDevice device : devices) {
-            if (device == null || device.getId() == null) {
+            if (device == null || StringUtils.isBlank(device.getDeviceUuid())) {
                 continue;
             }
-            List<String> personIds = devicePersonMap.get(device.getId());
+            List<String> personIds = devicePersonMap.get(device.getDeviceUuid());
             if (personIds != null && !personIds.isEmpty()) {
                 device.setBindPerson(String.join(",", personIds));
             } else {
@@ -395,30 +299,19 @@ public class EgDeviceServiceImpl extends AbstractCrudService<EgDeviceMapper, EgD
     }
 
     @Override
-    public boolean checkNameUnique(EgDevice egDevice){
-        Integer id = null == egDevice.getId() ? -1 : egDevice.getId();
-        LambdaQueryWrapper<EgDevice> queryWrapper = Wrappers.lambdaQuery();
-        queryWrapper.eq(EgDevice::getDeviceId,egDevice.getDeviceId())
-                .eq(EgDevice::getTenantId, SecurityUtils.getTenantId());
-        EgDevice one = this.getOne(queryWrapper);
-        return null != one && !Objects.equals(one.getId(),id);
-    }
-
-    @Override
-    public boolean checkDeviceNameUnique(EgDevice egDevice){
-        Integer id = null == egDevice.getId() ? -1 : egDevice.getId();
+    public boolean checkDeviceNameUnique(EgDevice egDevice) {
+        String uuid = egDevice.getDeviceUuid() == null ? "" : egDevice.getDeviceUuid();
         LambdaQueryWrapper<EgDevice> queryWrapper = Wrappers.lambdaQuery();
-        queryWrapper.eq(EgDevice::getDeviceName,egDevice.getDeviceName())
+        queryWrapper.eq(EgDevice::getDeviceName, egDevice.getDeviceName())
                 .eq(EgDevice::getTenantId, SecurityUtils.getTenantId());
         EgDevice one = this.getOne(queryWrapper);
-        return null != one && !Objects.equals(one.getId(),id);
+        return one != null && !Objects.equals(one.getDeviceUuid(), uuid);
     }
 
     @Override
-    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. 查询设备
+    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) {
         EgDevice device = this.getOne(Wrappers.<EgDevice>lambdaQuery()
-                .select(EgDevice::getId, EgDevice::getTenantId)
+                .select(EgDevice::getDeviceUuid, EgDevice::getTenantId)
                 .eq(EgDevice::getDeviceUuid, deviceUuid));
         if (device == null) {
             throw new BusinessException("设备未注册,请先注册");
@@ -428,27 +321,24 @@ public class EgDeviceServiceImpl extends AbstractCrudService<EgDeviceMapper, EgD
         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());
+            Integer bindCount = egDevicePersonBindMapper.countByUserIdAndDeviceUuid(commandUserId, deviceUuid);
             if (bindCount == null || bindCount == 0) {
-                saveRecord(device.getId(), tenantId, commandUserName, passType, "失败:暂无权限");
+                saveRecord(deviceUuid, tenantId, commandUserName, passType, "失败:暂无权限");
                 throw new BusinessException("暂无权限");
             }
         }
 
-        // 3. 构建下发命令参数
-        Map<String,Object> params = new HashMap<>();
+        Map<String, Object> params = new HashMap<>();
         params.put("commandCode", commandCode);
         params.put("commandValue", commandValue);
-        Map<String,Object> map = new HashMap<>();
+        Map<String, Object> map = new HashMap<>();
         map.put("method", "control");
         map.put("deviceUuid", deviceUuid);
         map.put("params", params);
 
-        // 4. 下发命令并记录结果
         String targetUuid = categoryType == 3 ? gatewayUuid : deviceUuid;
-        Map<String,Object> result = null;
+        Map<String, Object> result = null;
         String passResult = "下发命令失败";
         try {
             result = remoteTransferService.deviceControl(productCode, targetUuid, JSON.toJSONString(map), tenantId, commandUserId, commandUserName);
@@ -459,17 +349,14 @@ public class EgDeviceServiceImpl extends AbstractCrudService<EgDeviceMapper, EgD
             passResult = result.get("message") != null ? result.get("message").toString() : "下发命令成功";
             return result;
         } finally {
-            saveRecord(device.getId(), tenantId, commandUserName, passType, passResult);
+            saveRecord(deviceUuid, tenantId, commandUserName, passType, passResult);
         }
     }
 
-    /**
-     * 插入通行记录,失败不影响主流程
-     */
-    private void saveRecord(Integer deviceId, Integer tenantId, String userName, Integer passType, String passResult) {
+    private void saveRecord(String deviceUuid, Integer tenantId, String userName, Integer passType, String passResult) {
         try {
             EgRecord record = new EgRecord();
-            record.setEgDeviceId(deviceId);
+            record.setDeviceUuid(deviceUuid);
             record.setTenantId(tenantId);
             record.setPassTime(LocalDateTime.now());
             record.setCreateTime(LocalDateTime.now());
@@ -477,7 +364,8 @@ public class EgDeviceServiceImpl extends AbstractCrudService<EgDeviceMapper, EgD
             record.setUserName(userName);
             record.setPassType(passType);
             egRecordMapper.insert(record);
-        } catch (Exception ignored) {}
+        } catch (Exception ignored) {
+        }
     }
 
     @Override
@@ -486,7 +374,7 @@ public class EgDeviceServiceImpl extends AbstractCrudService<EgDeviceMapper, EgD
         Integer sz = queryVO.getSize();
         IPage<SysPerson> page = new Page<>(cur, sz);
 
-        List<Integer> deviceIds = new ArrayList<>();
+        List<String> deviceUuids = new ArrayList<>();
         if (StringUtils.isNotBlank(queryVO.getDeviceCode())) {
             EgDevice tempDevice = egDeviceMapper.selectOne(Wrappers.<EgDevice>lambdaQuery()
                     .eq(EgDevice::getDeviceCode, queryVO.getDeviceCode()));
@@ -496,27 +384,28 @@ public class EgDeviceServiceImpl extends AbstractCrudService<EgDeviceMapper, EgD
             EgDevice device = egDeviceMapper.selectOne(Wrappers.<EgDevice>lambdaQuery()
                     .eq(EgDevice::getDeviceCode, queryVO.getDeviceCode())
                     .eq(EgDevice::getTenantId, tempDevice.getTenantId()));
-            if (device == null || device.getId() == null) {
+            if (device == null || StringUtils.isBlank(device.getDeviceUuid())) {
                 return new CommonPage<>(new ArrayList<>(), 0L, sz, cur);
             }
-            deviceIds.add(device.getId());
+            deviceUuids.add(device.getDeviceUuid());
         } else {
             List<EgDevice> devices = egDeviceMapper.selectList(Wrappers.<EgDevice>lambdaQuery()
-                    .eq(EgDevice::getTenantId, SecurityUtils.getTenantId()));
+                    .eq(EgDevice::getTenantId, SecurityUtils.getTenantId())
+                    .eq(EgDevice::getDeviceType, EgDevice.DEVICE_TYPE_ACCESS));
             if (!CollectionUtils.isEmpty(devices)) {
                 for (EgDevice d : devices) {
-                    if (d != null && d.getId() != null) {
-                        deviceIds.add(d.getId());
+                    if (d != null && StringUtils.isNotBlank(d.getDeviceUuid())) {
+                        deviceUuids.add(d.getDeviceUuid());
                     }
                 }
             }
         }
 
-        if (deviceIds.isEmpty()) {
+        if (deviceUuids.isEmpty()) {
             return new CommonPage<>(new ArrayList<>(), 0L, sz, cur);
         }
 
-        List<EgDevicePersonBind> bindList = egDevicePersonBindMapper.selectByDeviceIds(deviceIds);
+        List<EgDevicePersonBind> bindList = egDevicePersonBindMapper.selectByDeviceUuids(deviceUuids);
         LinkedHashSet<Integer> personIdSet = new LinkedHashSet<>();
         if (!CollectionUtils.isEmpty(bindList)) {
             for (EgDevicePersonBind b : bindList) {
@@ -592,19 +481,14 @@ public class EgDeviceServiceImpl extends AbstractCrudService<EgDeviceMapper, EgD
     }
 
     @Override
-    public void bindPerson(Integer deviceId, String personIds, Integer isLoginNotify) {
-        if (deviceId == null) {
-            throw new BusinessException("设备ID不能为空");
+    public void bindPerson(String deviceUuid, String personIds, Integer isLoginNotify) {
+        if (StringUtils.isBlank(deviceUuid)) {
+            throw new BusinessException("设备uuid不能为空");
         }
-
-        // 先校验设备是否存在
-        EgDevice device = this.getById(deviceId);
+        EgDevice device = this.getById(deviceUuid);
         Optional.ofNullable(device).orElseThrow(() -> new BusinessException("门禁设备信息不存在"));
 
-        // 先清空原有绑定关系
-        egDevicePersonBindMapper.deleteByDeviceId(deviceId);
-
-        // 为空则视为解绑所有人员
+        egDevicePersonBindMapper.deleteByDeviceUuid(deviceUuid);
         if (!StringUtils.isNotBlank(personIds)) {
             return;
         }
@@ -616,10 +500,82 @@ public class EgDeviceServiceImpl extends AbstractCrudService<EgDeviceMapper, EgD
                 continue;
             }
             EgDevicePersonBind bind = new EgDevicePersonBind();
-            bind.setDeviceId(deviceId);
+            bind.setDeviceUuid(deviceUuid);
             bind.setPersonId(Integer.parseInt(personIdStr.trim()));
             bind.setIsLoginNotify(loginNotify);
             egDevicePersonBindMapper.insert(bind);
         }
     }
+
+    private void applyDefaultDeviceType(EgDeviceRequestVO requestVO) {
+        if (requestVO.getDeviceType() == null) {
+            requestVO.setDeviceType(EgDevice.DEVICE_TYPE_ACCESS);
+        }
+    }
+
+    private void saveAccess(EgDevice egDevice, LocalDateTime now) {
+        SpaceDeviceAccess access = spaceDeviceAccessMapper.selectById(egDevice.getDeviceUuid());
+        if (access == null) {
+            access = new SpaceDeviceAccess();
+            access.setDeviceUuid(egDevice.getDeviceUuid());
+            access.setCreateTime(now);
+            access.setServiceStatus(egDevice.getServiceStatus());
+            access.setOpenMode(egDevice.getOpenMode());
+            access.setPassword(egDevice.getPassword());
+            access.setWorkStatus(egDevice.getWorkStatus());
+            spaceDeviceAccessMapper.insert(access);
+            return;
+        }
+        access.setServiceStatus(egDevice.getServiceStatus());
+        access.setOpenMode(egDevice.getOpenMode());
+        access.setPassword(egDevice.getPassword());
+        access.setWorkStatus(egDevice.getWorkStatus());
+        access.setUpdateTime(now);
+        spaceDeviceAccessMapper.updateById(access);
+    }
+
+    private void fillOnlineStatus(List<EgDevice> records) {
+        if (CollectionUtils.isEmpty(records)) {
+            return;
+        }
+        List<String> validDeviceCodes = new ArrayList<>();
+        Map<String, EgDevice> deviceCodeMap = new HashMap<>();
+        for (EgDevice device : records) {
+            String deviceCode = device.getDeviceCode();
+            if (StringUtils.isNotBlank(deviceCode)) {
+                validDeviceCodes.add(deviceCode);
+                deviceCodeMap.put(deviceCode, device);
+            } else {
+                device.setDeviceStatus(0);
+            }
+        }
+        if (validDeviceCodes.isEmpty()) {
+            for (EgDevice device : records) {
+                device.setDeviceStatus(0);
+            }
+            return;
+        }
+        LambdaQueryWrapper<EgDeviceHeartbeat> heartbeatQueryWrapper = Wrappers.lambdaQuery();
+        heartbeatQueryWrapper.in(EgDeviceHeartbeat::getDeviceCode, validDeviceCodes)
+                .orderByDesc(EgDeviceHeartbeat::getCreateTime);
+        List<EgDeviceHeartbeat> heartbeatList = egDeviceHeartbeatMapper.selectList(heartbeatQueryWrapper);
+        Map<String, EgDeviceHeartbeat> deviceHeartbeatMap = new HashMap<>();
+        for (EgDeviceHeartbeat heartbeat : heartbeatList) {
+            String deviceCode = heartbeat.getDeviceCode();
+            if (StringUtils.isNotBlank(deviceCode) && deviceHeartbeatMap.putIfAbsent(deviceCode, heartbeat) == null) {
+                EgDevice device = deviceCodeMap.get(deviceCode);
+                if (device != null && heartbeat.getDeviceStatus() != null) {
+                    device.setDeviceStatus(heartbeat.getDeviceStatus());
+                }
+            }
+        }
+        for (String deviceCode : validDeviceCodes) {
+            if (!deviceHeartbeatMap.containsKey(deviceCode)) {
+                EgDevice device = deviceCodeMap.get(deviceCode);
+                if (device != null) {
+                    device.setDeviceStatus(0);
+                }
+            }
+        }
+    }
 }

+ 8 - 8
service-eg/service-eg-biz/src/main/java/com/usky/eg/service/impl/EgRecordServiceImpl.java

@@ -53,14 +53,14 @@ public class EgRecordServiceImpl extends AbstractCrudService<EgRecordMapper, EgR
         if(StringUtils.isBlank(egRecord.getDeviceUuid())){
             throw new BusinessException("设备Uuid不能为空");
         }
-        queryWrapper.select(EgDevice::getId, EgDevice::getTenantId)
+        queryWrapper.select(EgDevice::getDeviceUuid, EgDevice::getTenantId)
                 .eq(EgDevice::getDeviceUuid,egRecord.getDeviceUuid());
         EgDevice one = egDeviceService.getOne(queryWrapper);
         if(one == null){
             throw new BusinessException("设备未注册,请先注册");
         }
         tenantId = one.getTenantId();
-        egRecord.setEgDeviceId(one.getId());
+        egRecord.setDeviceUuid(one.getDeviceUuid());
         LocalDateTime now = LocalDateTime.now();
         egRecord.setPassTime(now);
         egRecord.setCreateTime(now);
@@ -84,7 +84,7 @@ public class EgRecordServiceImpl extends AbstractCrudService<EgRecordMapper, EgR
         IPage<EgRecord> page = new Page<>(requestVO.getCurrent(),requestVO.getSize());
         String deviceName = requestVO.getDeviceName();
 
-        List<Integer> egDeviceIdList = new ArrayList<>();
+        List<String> deviceUuidList = new ArrayList<>();
         List<EgDevice> egDeviceList ;
         LambdaQueryWrapper<EgDevice> egDeviceQuery = Wrappers.lambdaQuery();
         if(StringUtils.isNotBlank(deviceName)){
@@ -92,7 +92,7 @@ public class EgRecordServiceImpl extends AbstractCrudService<EgRecordMapper, EgR
             egDeviceList = egDeviceMapper.selectList(egDeviceQuery);
             if(egDeviceList.size() > 0){
                 for (int i = 0; i < egDeviceList.size(); i++) {
-                    egDeviceIdList.add(egDeviceList.get(i).getId());
+                    deviceUuidList.add(egDeviceList.get(i).getDeviceUuid());
                 }
             }
         }else{
@@ -105,17 +105,17 @@ public class EgRecordServiceImpl extends AbstractCrudService<EgRecordMapper, EgR
         LambdaQueryWrapper<EgRecord> queryWrapper = Wrappers.lambdaQuery();
         queryWrapper.like(StringUtils.isNotBlank(requestVO.getUserName()),EgRecord::getUserName,requestVO.getUserName())
                 .eq(null != requestVO.getPassType(),EgRecord::getPassType,requestVO.getPassType())
-                .in(CollectionUtils.isNotEmpty(egDeviceIdList),EgRecord::getEgDeviceId,egDeviceIdList)
+                .in(CollectionUtils.isNotEmpty(deviceUuidList),EgRecord::getDeviceUuid,deviceUuidList)
                 .between(StringUtils.isNotBlank(requestVO.getStartTime())&&StringUtils.isNotBlank(requestVO.getEndTime()),EgRecord::getPassTime,requestVO.getStartTime(),requestVO.getEndTime())
                 .apply(Objects.nonNull(dataScopeSql), dataScopeSql)
                 .orderByDesc(EgRecord::getId);
         page = this.page(page,queryWrapper);
         if((page.getRecords().size() > 0) && (egDeviceList.size() > 0)){
             for (int i = 0; i < page.getRecords().size(); i++) {
-                if(Objects.nonNull(page.getRecords().get(i).getEgDeviceId())){
-                    Integer egDeviceId = page.getRecords().get(i).getEgDeviceId();
+                if(Objects.nonNull(page.getRecords().get(i).getDeviceUuid())){
+                    String deviceUuid = page.getRecords().get(i).getDeviceUuid();
                     for (int j = 0; j < egDeviceList.size(); j++) {
-                        if(egDeviceId.equals(egDeviceList.get(j).getId())){
+                        if(deviceUuid.equals(egDeviceList.get(j).getDeviceUuid())){
                             page.getRecords().get(i).setEgDevice(egDeviceList.get(j));
                             break;
                         }

+ 1 - 1
service-eg/service-eg-biz/src/main/java/com/usky/eg/service/vo/EgDeviceAdbRemoteUpgradeVO.java

@@ -8,7 +8,7 @@ import java.util.List;
  * MQTT 远程升级请求体:向 Broker 发布下行 JSON(终端 {@code onCommand} 接收后自行下载安装)。
  * <p>
  * {@code fileUrl} 必填(http/https)。须指定 {@code deviceId} 或与终端序列号一致的 {@code deviceCode},用于主题
- * {@code eg.mqtt.command-topic-template} 中的占位符 {@code {deviceId}}。
+ * {@code mqtt.command-topic-template} 中的占位符 {@code {deviceId}}。
  * {@code deviceIp}、{@code devicePort} 可选,会一并写入下行 JSON 供终端展示或日志。
  * <p>
  * 批量:当 {@code items} 非空时,与本对象同级的 {@code fileUrl}、{@code launchPackage} 对所有设备共用;

+ 4 - 9
service-eg/service-eg-biz/src/main/java/com/usky/eg/service/vo/EgDeviceRequestVO.java

@@ -32,19 +32,14 @@ public class EgDeviceRequestVO implements Serializable {
     private Integer serviceStatus;
 
     /**
-     * 表主键Id
+     * 设备uuid(主键)
      */
-    private Integer id;
-
-    /**
-     * 域名
-     */
-    private String domain;
+    private String deviceUuid;
 
     /**
-     * 设备uuid
+     * 设备类型:1门禁设备,2会议室空间设备
      */
-    private String deviceUuid;
+    private Integer deviceType;
 
     /**
      * 设备编码

+ 3 - 3
service-eg/service-eg-biz/src/main/resources/mapper/eg/EgDeviceHeartbeatMapper.xml

@@ -2,19 +2,19 @@
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="com.usky.eg.mapper.EgDeviceHeartbeatMapper">
 
-    <!-- 通用查询映射结果 -->
+    <!-- 通用查询映射结果:space_device_heartbeat -->
     <resultMap id="BaseResultMap" type="com.usky.eg.domain.EgDeviceHeartbeat">
         <id column="id" property="id"/>
         <result column="device_code" property="deviceCode"/>
         <result column="ip_addr" property="ipAddr"/>
         <result column="mac_addr" property="macAddr"/>
         <result column="device_type" property="deviceType"/>
+        <result column="device_status" property="deviceStatus"/>
         <result column="create_time" property="createTime"/>
         <result column="model" property="model"/>
-        <result column="manu_facturer"  property="manuFacturer"/>
+        <result column="manu_facturer" property="manuFacturer"/>
         <result column="version" property="version"/>
         <result column="sdk" property="sdk"/>
-        <result column="device_status" property="deviceStatus"/>
     </resultMap>
 
 </mapper>

+ 71 - 21
service-eg/service-eg-biz/src/main/resources/mapper/eg/EgDeviceMapper.xml

@@ -2,29 +2,27 @@
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="com.usky.eg.mapper.EgDeviceMapper">
 
-    <!-- 通用查询映射结果 -->
     <resultMap id="BaseResultMap" type="com.usky.eg.domain.EgDevice">
-        <id column="id" property="id" />
-        <result column="device_uuid" property="deviceUuid" />
-        <result column="device_id" property="deviceId" />
-        <result column="device_name" property="deviceName" />
-        <result column="install_address" property="installAddress" />
-        <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" />
-        <result column="update_time" property="updateTime" />
-        <result column="dept_id" property="deptId" />
-        <result column="tenant_id" property="tenantId" />
-        <result column="img_path" property="imgPath" />
-        <result column="remark" property="remark" />
-        <result column="open_mode" property="openMode" />
-        <result column="password" property="password" />
-        <result column="work_status" property="workStatus" />
+        <id column="device_uuid" property="deviceUuid"/>
+        <result column="device_type" property="deviceType"/>
+        <result column="device_name" property="deviceName"/>
+        <result column="device_code" property="deviceCode"/>
+        <result column="install_address" property="installAddress"/>
+        <result column="device_ip" property="deviceIp"/>
+        <result column="img_path" property="imgPath"/>
+        <result column="remark" property="remark"/>
+        <result column="create_by" property="createBy"/>
+        <result column="update_by" property="updateBy"/>
+        <result column="create_time" property="createTime"/>
+        <result column="update_time" property="updateTime"/>
+        <result column="dept_id" property="deptId"/>
+        <result column="tenant_id" property="tenantId"/>
+        <result column="service_status" property="serviceStatus"/>
+        <result column="open_mode" property="openMode"/>
+        <result column="password" property="password"/>
+        <result column="work_status" property="workStatus"/>
     </resultMap>
+
     <select id="sysTenantId" resultType="integer">
         select id
         from sys_tenant
@@ -35,4 +33,56 @@
         </where>
     </select>
 
+    <select id="selectPageJoin" resultMap="BaseResultMap">
+        select
+            d.device_uuid,
+            d.device_type,
+            d.device_name,
+            d.device_code,
+            d.install_address,
+            d.device_ip,
+            d.img_path,
+            d.remark,
+            d.create_by,
+            d.update_by,
+            d.create_time,
+            d.update_time,
+            d.dept_id,
+            d.tenant_id,
+            a.service_status,
+            a.open_mode,
+            a.password,
+            a.work_status
+        from space_device d
+        left join space_device_access a on a.device_uuid = d.device_uuid
+        <where>
+            d.tenant_id = #{tenantId}
+            <if test="req.deviceType != null">
+                and d.device_type = #{req.deviceType}
+            </if>
+            <if test="req.deviceName != null and req.deviceName != ''">
+                and d.device_name like concat('%', #{req.deviceName}, '%')
+            </if>
+            <if test="req.installAddress != null and req.installAddress != ''">
+                and d.install_address like concat('%', #{req.installAddress}, '%')
+            </if>
+            <if test="req.serviceStatus != null">
+                and a.service_status = #{req.serviceStatus}
+            </if>
+            <if test="req.deviceUuid != null and req.deviceUuid != ''">
+                and d.device_uuid = #{req.deviceUuid}
+            </if>
+            <if test="req.deviceCode != null and req.deviceCode != ''">
+                and d.device_code = #{req.deviceCode}
+            </if>
+            <if test="deviceUuids != null and deviceUuids.size() > 0">
+                and d.device_uuid in
+                <foreach collection="deviceUuids" item="uuid" open="(" separator="," close=")">
+                    #{uuid}
+                </foreach>
+            </if>
+        </where>
+        order by d.create_time desc
+    </select>
+
 </mapper>

+ 24 - 29
service-eg/service-eg-biz/src/main/resources/mapper/eg/EgDevicePersonBindMapper.xml

@@ -3,60 +3,55 @@
 <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" />
+        <result column="device_uuid" property="deviceUuid"/>
+        <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
+    <select id="selectByDeviceUuids" resultMap="BaseResultMap">
+        select device_uuid, person_id, is_login_notify
+        from space_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}
+            <if test="deviceUuids != null and deviceUuids.size() > 0">
+                and device_uuid in
+                <foreach collection="deviceUuids" item="uuid" open="(" separator="," close=")">
+                    #{uuid}
                 </foreach>
             </if>
         </where>
     </select>
 
-    <select id="countByDeviceId" resultType="java.lang.Integer">
+    <select id="countByDeviceUuid" resultType="java.lang.Integer">
         select count(1)
-        from eg_device_person_bind
-        where device_id = #{deviceId}
+        from space_device_person_bind
+        where device_uuid = #{deviceUuid}
     </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 id="countByUserIdAndDeviceUuid" resultType="java.lang.Integer">
         select count(1)
-        from eg_device_person_bind b
+        from space_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}
+          and b.device_uuid = #{deviceUuid}
     </select>
 
-    <!-- 根据用户ID查询其绑定的设备ID列表 -->
-    <select id="selectDeviceIdsByUserId" resultType="java.lang.Integer">
-        select distinct b.device_id
-        from eg_device_person_bind b
+    <select id="selectDeviceUuidsByUserId" resultType="java.lang.String">
+        select distinct b.device_uuid
+        from space_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 id="deleteByDeviceUuid">
+        delete from space_device_person_bind
+        where device_uuid = #{deviceUuid}
     </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 into space_device_person_bind (device_uuid, person_id, is_login_notify)
+        values (#{deviceUuid}, #{personId}, #{isLoginNotify})
     </insert>
 
 </mapper>

+ 14 - 15
service-eg/service-eg-biz/src/main/resources/mapper/eg/EgRecordMapper.xml

@@ -2,22 +2,21 @@
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="com.usky.eg.mapper.EgRecordMapper">
 
-    <!-- 通用查询映射结果 -->
     <resultMap id="BaseResultMap" type="com.usky.eg.domain.EgRecord">
-        <id column="id" property="id" />
-        <result column="user_name" property="userName" />
-        <result column="pass_time" property="passTime" />
-        <result column="eg_device_id" property="egDeviceId" />
-        <result column="pass_type" property="passType" />
-        <result column="face_base" property="faceBase" />
-        <result column="card_num" property="cardNum" />
-        <result column="pass_result" property="passResult" />
-        <result column="create_by" property="createBy" />
-        <result column="update_by" property="updateBy" />
-        <result column="create_time" property="createTime" />
-        <result column="update_time" property="updateTime" />
-        <result column="dept_id" property="deptId" />
-        <result column="tenant_id" property="tenantId" />
+        <id column="id" property="id"/>
+        <result column="user_name" property="userName"/>
+        <result column="pass_time" property="passTime"/>
+        <result column="device_uuid" property="deviceUuid"/>
+        <result column="pass_type" property="passType"/>
+        <result column="face_base" property="faceBase"/>
+        <result column="card_num" property="cardNum"/>
+        <result column="pass_result" property="passResult"/>
+        <result column="create_by" property="createBy"/>
+        <result column="update_by" property="updateBy"/>
+        <result column="create_time" property="createTime"/>
+        <result column="update_time" property="updateTime"/>
+        <result column="dept_id" property="deptId"/>
+        <result column="tenant_id" property="tenantId"/>
     </resultMap>
 
 </mapper>

+ 1 - 1
service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/controller/web/MeetingDeviceController.java

@@ -81,7 +81,7 @@ public class MeetingDeviceController {
      */
     @Log(title = "删除-会议设备", businessType = BusinessType.DELETE)
     @DeleteMapping
-    public ApiResult<Void> remove(@RequestParam Long deviceUuid){
+    public ApiResult<Void> remove(@RequestParam String deviceUuid){
         meetingDeviceService.remove(deviceUuid);
         return ApiResult.success();
     }

+ 108 - 60
service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/domain/MeetingDevice.java

@@ -1,9 +1,6 @@
 package com.usky.meeting.domain;
 
-import java.time.LocalDateTime;
-import java.io.Serializable;
-import java.util.List;
-
+import com.baomidou.mybatisplus.annotation.IdType;
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
@@ -11,82 +8,75 @@ import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 
+import javax.persistence.Column;
 import javax.persistence.Entity;
 import javax.persistence.Id;
+import javax.persistence.Table;
 import javax.persistence.Transient;
+import java.io.Serializable;
+import java.time.LocalDateTime;
 
 /**
- * <p>
- * 设备表
- * </p>
- *
- * @author zyj
- * @since 2024-03-08
+ * 空间设备主表 space_device(会议扩展字段来自 space_device_meeting)
  */
 @Data
 @Entity
+@Table(name = "space_device")
+@TableName("space_device")
 @EqualsAndHashCode(callSuper = false)
 public class MeetingDevice implements Serializable {
 
     private static final long serialVersionUID = 1L;
 
-    @Id
-    @TableId
-    @JsonFormat(shape = JsonFormat.Shape.STRING)
-    private Long deviceUuid;
+    /** 门禁设备 */
+    public static final int DEVICE_TYPE_ACCESS = 1;
+    /** 会议室空间设备 */
+    public static final int DEVICE_TYPE_MEETING = 2;
 
     /**
-     * 设备名称
-     */
-    private String deviceName;
-
-    /**
-     * 文件存储id(tool_local_storage)
+     * 设备唯一编号(主键,space_device.device_uuid)
      */
-    @JsonFormat(shape = JsonFormat.Shape.STRING)
-    private Long storageId;
+    @Id
+    @TableId(value = "device_uuid", type = IdType.INPUT)
+    private String deviceUuid;
 
     /**
-     * 设备图片
+     * 设备类型:1门禁设备,2会议室空间设备
      */
-    private String imgPath;
+    private Integer deviceType;
 
     /**
-     * 设备铭牌
+     * 设备名称
      */
-    private String nameplate;
+    private String deviceName;
 
     /**
-     * 所属会议室
+     * 设备编码
      */
-    @JsonFormat(shape = JsonFormat.Shape.STRING)
-    private Long roomId;
+    private String deviceCode;
 
     /**
-     * 登记时间
+     * 安装位置
      */
-    private LocalDateTime registerDate;
+    private String installAddress;
 
     /**
-     * 维护人
+     * 设备IP(space_device.device_ip)
      */
-    private String maintainer;
+    @Column(name = "device_ip")
+    @TableField("device_ip")
+    private String ipAddr;
 
     /**
-     * 联系方式
+     * 设备图片/屏保
      */
-    private String contacts;
+    private String imgPath;
 
     /**
-     * 备注
+     * 备注/标识语
      */
     private String remark;
 
-    /**
-     * 安装位置
-     */
-    private String installAddress;
-
     /**
      * 创建者
      */
@@ -118,58 +108,116 @@ public class MeetingDevice implements Serializable {
     private Integer tenantId;
 
     /**
-     * 门禁设备主键id
+     * 文件存储id(tool_local_storage)(space_device_meeting)
      */
-    private Integer egDeviceId;
+    @Transient
+    @TableField(exist = false)
+    @JsonFormat(shape = JsonFormat.Shape.STRING)
+    private Long storageId;
 
     /**
-     * 会议室信息
+     * 设备铭牌(space_device_meeting)
      */
     @Transient
     @TableField(exist = false)
-    private MeetingRoom meetingRoom;
+    private String nameplate;
 
     /**
-     * 门禁设备信息
+     * 所属会议室ID(space_device_meeting)
      */
     @Transient
     @TableField(exist = false)
-    private EgDevice egDevice;
+    @JsonFormat(shape = JsonFormat.Shape.STRING)
+    private Long roomId;
 
     /**
-     * 设备编号
+     * 注册日期(space_device_meeting)
      */
-    private String deviceCode;
+    @Transient
+    @TableField(exist = false)
+    private LocalDateTime registerDate;
 
     /**
-     * 设备IP
+     * 模板id(space_device_meeting)
      */
-    private String ipAddr;
+    @Transient
+    @TableField(exist = false)
+    private Integer templateId;
 
     /**
-     * 模板id
+     * 设备状态(0:离线,1:在线)(space_device_meeting,列表会按心跳覆盖)
      */
-    private Integer templateId;
+    @Transient
+    @TableField(exist = false)
+    private Integer deviceStatus;
 
     /**
-     * 模板信息
+     * 设备方向(1:横屏,2竖屏)(space_device_meeting)
      */
+    @Transient
     @TableField(exist = false)
-    private MeetingTemplate meetingTemplate;
+    private Integer deviceDirection;
 
     /**
-     * 设备状态 0离线 1在线
+     * 签到方式(0.人工签到 1.人脸签到)(space_device_meeting)
      */
-    private Integer deviceStatus;
+    @Transient
+    @TableField(exist = false)
+    private Integer signType;
 
     /**
-     * 设备方向 1横屏 2竖屏
+     * 签退方式(0.人工签退 1.人脸签退)(space_device_meeting)
      */
-    private Integer deviceDirection;
+    @Transient
+    @TableField(exist = false)
+    private Integer signOutType;
+
+    /**
+     * 维护人(新表已无此列,仅兼容返回)
+     */
+    @Transient
+    @TableField(exist = false)
+    private String maintainer;
+
+    /**
+     * 联系方式(新表已无此列,仅兼容返回)
+     */
+    @Transient
+    @TableField(exist = false)
+    private String contacts;
 
     /**
-     * 绑定人员ID(逗号分隔,来自 meeting_device_person_bind)
+     * 门禁设备主键(新表已无此列,仅兼容返回
      */
+    @Transient
+    @TableField(exist = false)
+    private Integer egDeviceId;
+
+    /**
+     * 会议室信息(非表字段)
+     */
+    @Transient
+    @TableField(exist = false)
+    private MeetingRoom meetingRoom;
+
+    /**
+     * 门禁设备信息(非表字段,新表已无关联,仅兼容返回)
+     */
+    @Transient
+    @TableField(exist = false)
+    private EgDevice egDevice;
+
+    /**
+     * 模板信息(非表字段)
+     */
+    @Transient
+    @TableField(exist = false)
+    private MeetingTemplate meetingTemplate;
+
+    /**
+     * 绑定人员ID(逗号分隔,来自 space_device_person_bind)
+     */
+    @Transient
     @TableField(exist = false)
     private String bindPerson;
 }

+ 17 - 16
service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/domain/MeetingDeviceHeartbeat.java

@@ -2,30 +2,26 @@ package com.usky.meeting.domain;
 
 import com.baomidou.mybatisplus.annotation.IdType;
 import com.baomidou.mybatisplus.annotation.TableId;
-
-import java.time.LocalDateTime;
-import java.io.Serializable;
-
+import com.baomidou.mybatisplus.annotation.TableName;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
 /**
- * <p>
- * 会议设备心跳表
- * </p>
- *
- * @author fu
- * @since 2025-11-13
+ * 空间设备心跳表(space_device_heartbeat)
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
+@TableName("space_device_heartbeat")
 public class MeetingDeviceHeartbeat implements Serializable {
 
     private static final long serialVersionUID = 1L;
 
     /**
-     * 会议设备心跳表主键ID
+     * 空间设备心跳表主键ID
      */
     @TableId(value = "id", type = IdType.AUTO)
     private Integer id;
@@ -46,9 +42,14 @@ public class MeetingDeviceHeartbeat implements Serializable {
     private String macAddr;
 
     /**
-     * 设备类型(1.会议屏 2.信息发布屏 3.综合屏)
+     * 设备类型(1.会议屏 2.信息发布屏 3.门禁综合屏)
+     */
+    private Integer deviceType;
+
+    /**
+     * 设备状态(0:离线,1:在线)
      */
-    private Boolean deviceType;
+    private Integer deviceStatus;
 
     /**
      * 创建日期
@@ -62,17 +63,17 @@ public class MeetingDeviceHeartbeat implements Serializable {
     private String model;
 
     /**
-     *  设备厂商
+     * 厂商
      */
     private String manuFacturer;
 
     /**
-     * 设备版本号
+     * 系统版本
      */
     private String version;
 
     /**
-     * 设备sdk
+     * SDK版本
      */
     private String sdk;
 

+ 11 - 5
service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/domain/MeetingDevicePersonBind.java

@@ -6,7 +6,7 @@ import lombok.EqualsAndHashCode;
 import java.io.Serializable;
 
 /**
- * 会议设备人员绑定表 meeting_device_person_bind
+ * 空间设备人员绑定表 space_device_person_bind
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
@@ -14,12 +14,18 @@ public class MeetingDevicePersonBind implements Serializable {
 
     private static final long serialVersionUID = 1L;
 
-    /** 设备主键ID(meeting_device.device_uuid) */
-    private Long deviceUuid;
+    /**
+     * 设备唯一编号(space_device.device_uuid)
+     */
+    private String deviceUuid;
 
-    /** 人员ID */
+    /**
+     * 人员ID
+     */
     private Integer personId;
 
-    /** 是否打开登录通知(1 是 0 否 默认0) */
+    /**
+     * 是否打开登录通知(1 是 0 否 默认0)
+     */
     private Integer isLoginNotify;
 }

+ 0 - 101
service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/domain/MeetingFace.java

@@ -1,101 +0,0 @@
-package com.usky.meeting.domain;
-
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableField;
-import com.baomidou.mybatisplus.annotation.TableId;
-import java.time.LocalDateTime;
-import java.io.Serializable;
-import java.util.List;
-
-import com.fasterxml.jackson.annotation.JsonFormat;
-import com.usky.meeting.service.vo.DmpDeviceInfoVO;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-
-/**
- * <p>
- * 
- * </p>
- *
- * @author zyj
- * @since 2024-03-08
- */
-@Data
-@EqualsAndHashCode(callSuper = false)
-public class MeetingFace implements Serializable {
-
-    private static final long serialVersionUID = 1L;
-
-    /**
-     * 主键
-     */
-    @TableId(value = "fid", type = IdType.AUTO)
-    private Integer fid;
-
-    /**
-     * 图片数据 base_64编码
-     */
-    private String faceBase;
-
-    /**
-     * 插入时间
-     */
-    private LocalDateTime createTime;
-
-    /**
-     * 验证次数
-     */
-    private Integer vefNum;
-
-    /**
-     * 人脸名称
-     */
-    private String faceName;
-
-    /**
-     * 人脸备注
-     */
-    private String remark;
-
-    /**
-     * 人脸是否可用,(0==可用,1,不可用)
-     */
-    private Integer faceStatus;
-
-    /**
-     * 卡号
-     */
-    private String cardNum;
-
-    /**
-     * 绑定设备
-     */
-    private String bindDevice;
-
-    /**
-     * 组织机构ID
-     */
-    private Integer deptId;
-
-    /**
-     * 租户ID
-     */
-    private Integer tenantId;
-
-    /**
-     * 用户ID
-     */
-    private Long userId;
-
-    /**
-     * 设备对象
-     */
-    @TableField(exist = false)
-    private List<DmpDeviceInfoVO> deviceInfos;
-
-    /**
-     * 部门信息
-     */
-    @TableField(exist = false)
-    private SysDept sysDept;
-}

+ 82 - 0
service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/domain/SpaceDeviceMeeting.java

@@ -0,0 +1,82 @@
+package com.usky.meeting.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * 会议室空间设备专属扩展表 space_device_meeting
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("space_device_meeting")
+public class SpaceDeviceMeeting implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 关联 space_device.device_uuid【物理主键】
+     */
+    @TableId(value = "device_uuid", type = IdType.INPUT)
+    private String deviceUuid;
+
+    /**
+     * 文件存储id(tool_local_storage)
+     */
+    private Long storageId;
+
+    /**
+     * 设备铭牌
+     */
+    private String nameplate;
+
+    /**
+     * 所属会议室ID
+     */
+    private Long roomId;
+
+    /**
+     * 注册日期
+     */
+    private LocalDateTime registerDate;
+
+    /**
+     * 模板id
+     */
+    private Integer templateId;
+
+    /**
+     * 设备状态(0:离线,1:在线)
+     */
+    private Integer deviceStatus;
+
+    /**
+     * 设备方向(1:横屏,2竖屏)
+     */
+    private Integer deviceDirection;
+
+    /**
+     * 签退方式(0.人工签退 1.人脸签退)
+     */
+    private Integer signOutType;
+
+    /**
+     * 签到方式(0.人工签到 1.人脸签到)
+     */
+    private Integer signType;
+
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createTime;
+
+    /**
+     * 更新时间
+     */
+    private LocalDateTime updateTime;
+}

+ 13 - 9
service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/mapper/MeetingDeviceMapper.java

@@ -1,16 +1,20 @@
 package com.usky.meeting.mapper;
 
-import com.usky.meeting.domain.MeetingDevice;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.usky.common.mybatis.core.CrudMapper;
+import com.usky.meeting.domain.MeetingDevice;
+import com.usky.meeting.service.vo.MeetingDeviceRequestVO;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
 
-/**
- * <p>
- * 设备表 Mapper 接口
- * </p>
- *
- * @author zyj
- * @since 2024-03-08
- */
 public interface MeetingDeviceMapper extends CrudMapper<MeetingDevice> {
 
+    IPage<MeetingDevice> selectPageJoin(Page<MeetingDevice> page,
+                                        @Param("req") MeetingDeviceRequestVO req,
+                                        @Param("tenantId") Integer tenantId,
+                                        @Param("roomIdList") List<Long> roomIdList);
+
+    List<MeetingDevice> selectListByRoomId(@Param("roomId") Long roomId);
 }

+ 4 - 14
service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/mapper/MeetingDevicePersonBindMapper.java

@@ -6,24 +6,14 @@ import org.springframework.stereotype.Repository;
 
 import java.util.List;
 
-/**
- * 会议设备人员绑定 Mapper
- */
 @Repository
 public interface MeetingDevicePersonBindMapper {
 
-    /**
-     * 根据设备UUID删除绑定关系
-     */
-    void deleteByDeviceUuid(@Param("deviceUuid") Long deviceUuid);
+    void deleteByDeviceUuid(@Param("deviceUuid") String deviceUuid);
 
-    /**
-     * 根据多个设备UUID查询绑定关系
-     */
-    List<MeetingDevicePersonBind> selectByDeviceUuids(@Param("deviceUuids") List<Long> deviceUuids);
+    List<MeetingDevicePersonBind> selectByDeviceUuids(@Param("deviceUuids") List<String> deviceUuids);
+
+    Integer countByDeviceUuid(@Param("deviceUuid") String deviceUuid);
 
-    /**
-     * 新增绑定关系
-     */
     void insert(MeetingDevicePersonBind bind);
 }

+ 0 - 17
service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/mapper/MeetingFaceMapper.java

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

+ 18 - 0
service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/mapper/SpaceDeviceMeetingMapper.java

@@ -0,0 +1,18 @@
+package com.usky.meeting.mapper;
+
+import com.usky.common.mybatis.core.CrudMapper;
+import com.usky.meeting.domain.SpaceDeviceMeeting;
+import org.apache.ibatis.annotations.Param;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+@Repository
+public interface SpaceDeviceMeetingMapper extends CrudMapper<SpaceDeviceMeeting> {
+
+    void clearTemplateId(@Param("templateId") Integer templateId, @Param("tenantId") Integer tenantId);
+
+    void updateTemplateId(@Param("templateId") Integer templateId,
+                          @Param("tenantId") Integer tenantId,
+                          @Param("deviceCodes") List<String> deviceCodes);
+}

+ 3 - 26
service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/repository/MeetingDeviceRepository.java

@@ -1,38 +1,15 @@
-/*
-*  Copyright 2019-2020 Zheng Jie
-*
-*  Licensed under the Apache License, Version 2.0 (the "License");
-*  you may not use this file except in compliance with the License.
-*  You may obtain a copy of the License at
-*
-*  http://www.apache.org/licenses/LICENSE-2.0
-*
-*  Unless required by applicable law or agreed to in writing, software
-*  distributed under the License is distributed on an "AS IS" BASIS,
-*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-*  See the License for the specific language governing permissions and
-*  limitations under the License.
-*/
 package com.usky.meeting.repository;
 
 import com.usky.meeting.domain.MeetingDevice;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
-import org.springframework.data.jpa.repository.Modifying;
 import org.springframework.data.jpa.repository.Query;
 import org.springframework.data.repository.query.Param;
 
 import java.util.List;
-import java.util.Map;
 
-/**
-* @website https://el-admin.vip
-* @author shiguangji
-* @date 2021-03-12
-**/
-public interface MeetingDeviceRepository extends JpaRepository<MeetingDevice, Long>, JpaSpecificationExecutor<MeetingDevice> {
+public interface MeetingDeviceRepository extends JpaRepository<MeetingDevice, String>, JpaSpecificationExecutor<MeetingDevice> {
 
-    @Query(value = "select * from meeting_device where room_id = :roomId",nativeQuery = true)
+    @Query(value = "select d.* from space_device d inner join space_device_meeting m on m.device_uuid = d.device_uuid where m.room_id = :roomId", nativeQuery = true)
     List<MeetingDevice> getDeviceListByRoomId(@Param("roomId") Long roomId);
-
-}
+}

+ 0 - 56
service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/server/FaceContrastServer.java

@@ -1,56 +0,0 @@
-package com.usky.meeting.server;
-
-import com.tencentcloudapi.common.Credential;
-import com.tencentcloudapi.common.exception.TencentCloudSDKException;
-import com.tencentcloudapi.common.profile.ClientProfile;
-import com.tencentcloudapi.common.profile.HttpProfile;
-import com.tencentcloudapi.iai.v20180301.IaiClient;
-import com.tencentcloudapi.iai.v20180301.models.CompareFaceRequest;
-import com.tencentcloudapi.iai.v20180301.models.CompareFaceResponse;
-import com.usky.meeting.service.vo.FaceResultVO;
-import lombok.Data;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Component;
-
-/**
- * @author tanyongpeng
- * <p>调用腾讯接口</p>
- **/
-@Component
-@Data
-@Slf4j
-public class FaceContrastServer {
-
-    @Value("${tencentcloudapi.secretId}")
-    private String secretId;
-    @Value("${tencentcloudapi.secretKey}")
-    private String secretKey;
-    @Value("${tencentcloudapi.endpoint}")
-    private String endpoint;
-    @Value("${tencentcloudapi.region}")
-    private String region;
-
-    public FaceResultVO faceContrast(String imageA, String imageB){
-        FaceResultVO faceResult = new FaceResultVO();
-        try{
-            Credential cred = new Credential(secretId, secretKey);
-            HttpProfile httpProfile = new HttpProfile();
-            httpProfile.setEndpoint(endpoint);
-            ClientProfile clientProfile = new ClientProfile();
-            clientProfile.setHttpProfile(httpProfile);
-            IaiClient client = new IaiClient(cred, region, clientProfile);
-            CompareFaceRequest req = new CompareFaceRequest();
-            req.setImageA(imageA);
-            req.setImageB(imageB);
-            CompareFaceResponse resp = client.CompareFace(req);
-            faceResult.setScore(resp.getScore());
-            faceResult.setCode(FaceResultVO.SUCCESS_CODE);
-        } catch (TencentCloudSDKException e) {
-            faceResult.setCode(FaceResultVO.FACE_ERROR);
-            faceResult.setMsg(e.getMessage());
-            System.out.println("e.getMessage() "+e.getMessage());
-        }
-        return faceResult;
-    }
-}

+ 1 - 1
service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/service/MeetingDeviceService.java

@@ -24,7 +24,7 @@ public interface MeetingDeviceService extends CrudService<MeetingDevice> {
 
     void edit(MeetingDevice meetingDevice);
 
-    void remove(Long deviceId);
+    void remove(String deviceUuid);
 
     /**
      * 设备人员授权(覆盖式):先清空该设备绑定,再按 personIds 重新绑定;空字符串表示解除全部授权

+ 0 - 25
service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/service/MeetingFaceService.java

@@ -1,25 +0,0 @@
-package com.usky.meeting.service;
-
-import com.usky.meeting.domain.MeetingFace;
-import com.usky.common.mybatis.core.CrudService;
-import com.usky.meeting.service.vo.FaceResultVO;
-import com.usky.meeting.service.vo.MeetingFaceRequestVO;
-import com.usky.meeting.service.vo.MeetingFaceVO;
-
-/**
- * <p>
- *  服务类
- * </p>
- *
- * @author zyj
- * @since 2024-03-08
- */
-public interface MeetingFaceService extends CrudService<MeetingFace> {
-
-    /**
-     * 人脸校验
-     * @param imageBase
-     * @return
-     */
-    MeetingFaceVO vef(MeetingFaceRequestVO requestVO);
-}

+ 14 - 12
service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/service/impl/MeetingDeviceHeartbeatServiceImpl.java

@@ -14,12 +14,7 @@ import org.springframework.transaction.annotation.Transactional;
 import java.time.LocalDateTime;
 
 /**
- * <p>
- * 会议设备心跳表 服务实现类
- * </p>
- *
- * @author fu
- * @since 2025-11-13
+ * 空间设备心跳表 服务实现类
  */
 @Service
 public class MeetingDeviceHeartbeatServiceImpl extends AbstractCrudService<MeetingDeviceHeartbeatMapper, MeetingDeviceHeartbeat> implements MeetingDeviceHeartbeatService {
@@ -39,12 +34,19 @@ public class MeetingDeviceHeartbeatServiceImpl extends AbstractCrudService<Meeti
         if (heartbeat.getDeviceType() == null) {
             throw new BusinessException("设备类型不能为空!");
         }
+        if (heartbeat.getDeviceStatus() == null) {
+            heartbeat.setDeviceStatus(1);
+        }
 
-        // 删除旧数据
-        LambdaQueryWrapper<MeetingDeviceHeartbeat> deleteWrapper = Wrappers.lambdaQuery();
-        deleteWrapper.eq(MeetingDeviceHeartbeat::getDeviceCode, heartbeat.getDeviceCode());
-        baseMapper.delete(deleteWrapper);
-
-        baseMapper.insert(heartbeat);
+        // 按 deviceCode 更新或插入(共用 space_device_heartbeat)
+        LambdaQueryWrapper<MeetingDeviceHeartbeat> queryWrapper = Wrappers.lambdaQuery();
+        queryWrapper.eq(MeetingDeviceHeartbeat::getDeviceCode, heartbeat.getDeviceCode());
+        MeetingDeviceHeartbeat existing = baseMapper.selectOne(queryWrapper);
+        if (existing != null) {
+            heartbeat.setId(existing.getId());
+            baseMapper.updateById(heartbeat);
+        } else {
+            baseMapper.insert(heartbeat);
+        }
     }
 }

+ 105 - 150
service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/service/impl/MeetingDeviceServiceImpl.java

@@ -1,7 +1,5 @@
 package com.usky.meeting.service.impl;
 
-import cn.hutool.core.lang.Snowflake;
-import cn.hutool.core.util.IdUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
@@ -12,48 +10,27 @@ import com.usky.common.core.bean.CommonPage;
 import com.usky.common.core.exception.BusinessException;
 import com.usky.common.core.util.UUIDUtils;
 import com.usky.common.security.utils.SecurityUtils;
-import com.usky.iot.RemoteIotTaskService;
 import com.usky.meeting.domain.*;
 import com.usky.meeting.mapper.*;
-import com.usky.meeting.repository.MeetingRoomRepository;
 import com.usky.meeting.service.MeetingDeviceService;
 import com.usky.common.mybatis.core.AbstractCrudService;
-import com.usky.meeting.service.MeetingRoomService;
 import com.usky.meeting.service.vo.MeetingDeviceBindFacePersonQueryVO;
 import com.usky.meeting.service.vo.MeetingDeviceBindFacePersonVO;
 import com.usky.meeting.service.vo.MeetingDeviceBindPersonRequest;
 import com.usky.meeting.service.vo.MeetingDeviceRequestVO;
-import net.bytebuddy.implementation.bytecode.Throw;
-import org.aspectj.weaver.patterns.ThisOrTargetAnnotationPointcut;
-import org.bouncycastle.jcajce.provider.symmetric.AES;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.data.util.Optionals;
-import org.springframework.stereotype.Repository;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
 import java.time.LocalDateTime;
 import java.util.*;
 import java.util.stream.Collectors;
 
-/**
- * <p>
- * 设备表 服务实现类
- * </p>
- *
- * @author zyj
- * @since 2024-03-08
- */
 @Service
 public class MeetingDeviceServiceImpl extends AbstractCrudService<MeetingDeviceMapper, MeetingDevice> implements MeetingDeviceService {
     @Autowired
     private MeetingRoomMapper meetingRoomMapper;
     @Autowired
-    private EgDeviceMapper egDeviceMapper;
-    // @Autowired
-    // private RemoteIotTaskService remoteIotTaskService;
-    @Autowired
-    private MeetingRoomRepository dmMeetingRoomRepository;
-    @Autowired
     private MeetingDeviceMapper deviceMapper;
     @Autowired
     private MeetingDeviceHeartbeatMapper heartbeatMapper;
@@ -63,86 +40,61 @@ public class MeetingDeviceServiceImpl extends AbstractCrudService<MeetingDeviceM
     private MeetingDevicePersonBindMapper meetingDevicePersonBindMapper;
     @Autowired
     private SysPersonMapper sysPersonMapper;
+    @Autowired
+    private SpaceDeviceMeetingMapper spaceDeviceMeetingMapper;
 
     @Override
     public CommonPage<MeetingDevice> meetingDeviceList(MeetingDeviceRequestVO requestVO) {
         LocalDateTime now = LocalDateTime.now();
         Integer current = requestVO.getCurrent();
         Integer size = requestVO.getSize();
-        IPage<MeetingDevice> page = new Page<>(current, size);
 
         String deviceCode = requestVO.getDeviceCode();
         Integer tenantId;
-
         if (StringUtils.isNotBlank(deviceCode)) {
             LambdaQueryWrapper<MeetingDevice> queryWrapper = new LambdaQueryWrapper<>();
             queryWrapper.eq(MeetingDevice::getDeviceCode, requestVO.getDeviceCode());
-            tenantId = deviceMapper.selectOne(queryWrapper).getTenantId();
+            MeetingDevice one = deviceMapper.selectOne(queryWrapper);
+            if (one == null) {
+                return new CommonPage<>(new ArrayList<>(), 0L, size, current);
+            }
+            tenantId = one.getTenantId();
         } else {
             tenantId = SecurityUtils.getTenantId();
         }
+        if (requestVO.getDeviceType() == null) {
+            requestVO.setDeviceType(MeetingDevice.DEVICE_TYPE_MEETING);
+        }
 
-        // 根据输入所属会议室名称刷选
         List<Long> roomIdList = new ArrayList<>();
-        List<MeetingRoom> meetingRoomsList = null;
+        List<MeetingRoom> meetingRoomsList;
         LambdaQueryWrapper<MeetingRoom> queryWrapper = Wrappers.lambdaQuery();
         if (StringUtils.isNotBlank(requestVO.getRoomName())) {
             queryWrapper.like(MeetingRoom::getRoomName, requestVO.getRoomName()).eq(MeetingRoom::getTenantId, tenantId);
             meetingRoomsList = meetingRoomMapper.selectList(queryWrapper);
             if (CollectionUtils.isNotEmpty(meetingRoomsList)) {
-                for (int i = 0; i < meetingRoomsList.size(); i++) {
-                    roomIdList.add(meetingRoomsList.get(i).getRoomId());
+                for (MeetingRoom meetingRoom : meetingRoomsList) {
+                    roomIdList.add(meetingRoom.getRoomId());
                 }
             }
         } else {
             queryWrapper.eq(MeetingRoom::getTenantId, tenantId);
             meetingRoomsList = meetingRoomMapper.selectList(queryWrapper);
         }
-        // 根据输入所属门禁设备名称刷选
-        List<Integer> egDeviceIdList = new ArrayList<>();
-        List<EgDevice> egDeviceList = null;
-        LambdaQueryWrapper<EgDevice> queryWrapper1 = Wrappers.lambdaQuery();
-        if (StringUtils.isNotBlank(requestVO.getEgDeviceName())) {
-            queryWrapper1.like(EgDevice::getDeviceName, requestVO.getEgDeviceName()).eq(EgDevice::getTenantId, tenantId);
-            egDeviceList = egDeviceMapper.selectList(queryWrapper1);
-            if (CollectionUtils.isNotEmpty(egDeviceList)) {
-                for (EgDevice egDevice : egDeviceList) {
-                    egDeviceIdList.add(egDevice.getId());
-                }
-            }
-        } else {
-            queryWrapper1.eq(EgDevice::getTenantId, tenantId);
-            egDeviceList = egDeviceMapper.selectList(queryWrapper1);
-        }
-
-        LambdaQueryWrapper<MeetingDevice> queryWrapper3 = Wrappers.lambdaQuery();
-        queryWrapper3.eq(requestVO.getDeviceCode() != null, MeetingDevice::getDeviceCode, requestVO.getDeviceCode())
-                .eq(requestVO.getDeviceStatus() != null, MeetingDevice::getDeviceStatus, requestVO.getDeviceStatus())
-                .eq(requestVO.getDeviceDirection() != null, MeetingDevice::getDeviceDirection, requestVO.getDeviceDirection())
-                .like(StringUtils.isNotBlank(requestVO.getDeviceName()), MeetingDevice::getDeviceName, requestVO.getDeviceName())
-                .like(StringUtils.isNotBlank(requestVO.getNameplate()), MeetingDevice::getNameplate, requestVO.getNameplate())
-                .like(StringUtils.isNotBlank(requestVO.getMaintainer()), MeetingDevice::getMaintainer, requestVO.getMaintainer())
-                .like(StringUtils.isNotBlank(requestVO.getContacts()), MeetingDevice::getContacts, requestVO.getContacts())
-                .like(StringUtils.isNotBlank(requestVO.getRemark()), MeetingDevice::getRemark, requestVO.getRemark())
-                .like(requestVO.getRoomId() != null, MeetingDevice::getRoomId, requestVO.getRoomId())
-                .in(CollectionUtils.isNotEmpty(roomIdList), MeetingDevice::getRoomId, roomIdList)
-                .in(CollectionUtils.isNotEmpty(egDeviceIdList), MeetingDevice::getEgDeviceId, egDeviceIdList)
-                .eq(MeetingDevice::getTenantId, tenantId)
-                .orderByAsc(MeetingDevice::getDeviceUuid);
-        page = this.page(page, queryWrapper3);
+
+        Page<MeetingDevice> page = new Page<>(current, size);
+        IPage<MeetingDevice> result = deviceMapper.selectPageJoin(page, requestVO, tenantId, roomIdList);
 
         LambdaQueryWrapper<MeetingTemplate> queryWrapper4 = Wrappers.lambdaQuery();
         queryWrapper4.eq(MeetingTemplate::getTenantId, tenantId);
         List<MeetingTemplate> meetingTemplateList = meetingTemplateMapper.selectList(queryWrapper4);
 
-        if (!page.getRecords().isEmpty()) {
-            // 1. 提取设备编码并过滤空值
-            List<String> validDeviceCodes = page.getRecords().stream()
+        if (!result.getRecords().isEmpty()) {
+            List<String> validDeviceCodes = result.getRecords().stream()
                     .map(MeetingDevice::getDeviceCode)
                     .filter(Objects::nonNull)
                     .collect(Collectors.toList());
 
-            // 2. 批量查询心跳信息并构建Map
             Map<String, LocalDateTime> deviceHeartbeatMap;
             if (CollectionUtils.isEmpty(validDeviceCodes)) {
                 deviceHeartbeatMap = Collections.emptyMap();
@@ -157,8 +109,7 @@ public class MeetingDeviceServiceImpl extends AbstractCrudService<MeetingDeviceM
                         ));
             }
 
-            // 3. 设备状态赋值
-            page.getRecords().forEach(meetingDevice -> {
+            result.getRecords().forEach(meetingDevice -> {
                 String deviceCode1 = meetingDevice.getDeviceCode();
                 boolean isHeartbeatValid = Objects.nonNull(deviceCode1)
                         && deviceHeartbeatMap.containsKey(deviceCode1)
@@ -166,37 +117,27 @@ public class MeetingDeviceServiceImpl extends AbstractCrudService<MeetingDeviceM
                 meetingDevice.setDeviceStatus(isHeartbeatValid ? 1 : 0);
             });
 
-            // 4. 通用映射关联数据
-            mapAssociatedData(page.getRecords(),
+            mapAssociatedData(result.getRecords(),
                     MeetingDevice::getRoomId,
                     buildIdMap(meetingRoomsList, MeetingRoom::getRoomId),
                     MeetingDevice::setMeetingRoom);
 
-            mapAssociatedData(page.getRecords(),
-                    MeetingDevice::getEgDeviceId,
-                    buildIdMap(egDeviceList, EgDevice::getId),
-                    MeetingDevice::setEgDevice);
-
-            mapAssociatedData(page.getRecords(),
+            mapAssociatedData(result.getRecords(),
                     MeetingDevice::getTemplateId,
                     buildIdMap(meetingTemplateList, MeetingTemplate::getId),
                     MeetingDevice::setMeetingTemplate);
 
-            // 5. 填充绑定人员
-            fillBindPerson(page.getRecords());
+            fillBindPerson(result.getRecords());
         }
 
-        return new CommonPage<>(page.getRecords(), page.getTotal(), size, current);
+        return new CommonPage<>(result.getRecords(), result.getTotal(), size, current);
     }
 
-    /**
-     * 根据设备UUID集合查询 meeting_device_person_bind,将 person_id 按逗号拼接后写入 bindPerson
-     */
     private void fillBindPerson(List<MeetingDevice> devices) {
         if (CollectionUtils.isEmpty(devices)) {
             return;
         }
-        List<Long> deviceUuids = devices.stream()
+        List<String> deviceUuids = devices.stream()
                 .map(MeetingDevice::getDeviceUuid)
                 .filter(Objects::nonNull)
                 .distinct()
@@ -205,10 +146,10 @@ public class MeetingDeviceServiceImpl extends AbstractCrudService<MeetingDeviceM
             return;
         }
         List<MeetingDevicePersonBind> bindList = meetingDevicePersonBindMapper.selectByDeviceUuids(deviceUuids);
-        Map<Long, List<String>> devicePersonMap = new HashMap<>();
+        Map<String, List<String>> devicePersonMap = new HashMap<>();
         if (!CollectionUtils.isEmpty(bindList)) {
             for (MeetingDevicePersonBind bind : bindList) {
-                if (bind.getDeviceUuid() == null || bind.getPersonId() == null) {
+                if (StringUtils.isBlank(bind.getDeviceUuid()) || bind.getPersonId() == null) {
                     continue;
                 }
                 devicePersonMap
@@ -217,7 +158,7 @@ public class MeetingDeviceServiceImpl extends AbstractCrudService<MeetingDeviceM
             }
         }
         for (MeetingDevice device : devices) {
-            if (device == null || device.getDeviceUuid() == null) {
+            if (device == null || StringUtils.isBlank(device.getDeviceUuid())) {
                 continue;
             }
             List<String> personIds = devicePersonMap.get(device.getDeviceUuid());
@@ -229,32 +170,15 @@ public class MeetingDeviceServiceImpl extends AbstractCrudService<MeetingDeviceM
         }
     }
 
-    /**
-     * 构建ID到对象的Map,用于快速查找
-     * @param dataList 待处理的列表
-     * @param idExtractor ID提取器
-     * @return ID到对象的Map
-     */
     private <T, ID> Map<ID, T> buildIdMap(List<T> dataList, java.util.function.Function<T, ID> idExtractor) {
         if (CollectionUtils.isEmpty(dataList)) {
             return Collections.emptyMap();
         }
         return dataList.stream()
                 .filter(Objects::nonNull)
-                .collect(Collectors.toMap(
-                        idExtractor,
-                        t -> t,
-                        (existing, replacement) -> existing
-                ));
+                .collect(Collectors.toMap(idExtractor, t -> t, (existing, replacement) -> existing));
     }
 
-    /**
-     * 通用关联数据映射方法
-     * @param mainList 主列表(MeetingDevice列表)
-     * @param keyExtractor 主对象的关联ID提取器
-     * @param dataMap 关联数据的ID->对象Map
-     * @param setter 主对象的关联数据设置器
-     */
     private <T, ID, D> void mapAssociatedData(List<T> mainList,
                                               java.util.function.Function<T, ID> keyExtractor,
                                               Map<ID, D> dataMap,
@@ -271,85 +195,73 @@ public class MeetingDeviceServiceImpl extends AbstractCrudService<MeetingDeviceM
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public void add(MeetingDevice meetingDevice) {
         if (checkNameUnique(meetingDevice)) {
             throw new BusinessException("新增会议室设备失败," + meetingDevice.getDeviceName() + "已经存在");
         }
-//        Snowflake snowflake = IdUtil.createSnowflake(1,1);
-//        meetingDevice.setDeviceId(snowflake.nextId());
         LocalDateTime now = LocalDateTime.now();
         meetingDevice.setCreateBy(SecurityUtils.getUsername());
         meetingDevice.setCreateTime(now);
         meetingDevice.setTenantId(SecurityUtils.getTenantId());
+        meetingDevice.setDeviceType(MeetingDevice.DEVICE_TYPE_MEETING);
+        meetingDevice.setDeviceUuid(UUIDUtils.uuid());
 
-        String ipAddr = meetingDevice.getIpAddr();
-        LambdaQueryWrapper<MeetingDeviceHeartbeat> queryWrapper = Wrappers.lambdaQuery();
-        queryWrapper.eq(MeetingDeviceHeartbeat::getIpAddr, ipAddr);
-        MeetingDeviceHeartbeat meetingDeviceHeartbeat = heartbeatMapper.selectOne(queryWrapper);
-        if (meetingDeviceHeartbeat == null) {
-            throw new BusinessException("设备离线!请重新输入");
-        } else {
-            LocalDateTime createTime = meetingDeviceHeartbeat.getCreateTime();
-            if (createTime.isAfter(now) || !createTime.plusMinutes(6).isAfter(now)) {
-                throw new BusinessException("设备离线!请重新输入");
-            }
-        }
-        meetingDevice.setDeviceCode(meetingDeviceHeartbeat.getDeviceCode());
+        fillDeviceCodeFromHeartbeat(meetingDevice, now);
 
         this.save(meetingDevice);
-
-        // remoteIotTaskService.addDeviceInfo("505_USKY", meetingDevice.getDeviceUuid().toString(), "", meetingDevice.getDeviceName(), meetingDevice.getInstallAddress(), 1);
+        saveMeetingExt(meetingDevice, now);
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public void edit(MeetingDevice meetingDevice) {
+        if (StringUtils.isBlank(meetingDevice.getDeviceUuid())) {
+            throw new BusinessException("设备uuid不能为空");
+        }
         LocalDateTime now = LocalDateTime.now();
         if (checkNameUnique(meetingDevice)) {
             throw new BusinessException("修改会议室设备失败," + meetingDevice.getDeviceName() + "已经存在");
         }
         meetingDevice.setUpdateBy(SecurityUtils.getUsername());
         meetingDevice.setUpdateTime(now);
+        meetingDevice.setDeviceType(MeetingDevice.DEVICE_TYPE_MEETING);
 
-        String ipAddr = meetingDevice.getIpAddr();
-        LambdaQueryWrapper<MeetingDeviceHeartbeat> queryWrapper = Wrappers.lambdaQuery();
-        queryWrapper.eq(MeetingDeviceHeartbeat::getIpAddr, ipAddr);
-        MeetingDeviceHeartbeat meetingDeviceHeartbeat = heartbeatMapper.selectOne(queryWrapper);
-        if (meetingDeviceHeartbeat == null) {
-            throw new BusinessException("设备离线!请重新输入");
-        } else {
-            LocalDateTime createTime = meetingDeviceHeartbeat.getCreateTime();
-            if (createTime.isAfter(now) || !createTime.plusMinutes(6).isAfter(now)) {
-                throw new BusinessException("设备离线!请重新输入");
-            }
-        }
-        meetingDevice.setDeviceCode(meetingDeviceHeartbeat.getDeviceCode());
+        fillDeviceCodeFromHeartbeat(meetingDevice, now);
 
         this.updateById(meetingDevice);
+        saveMeetingExt(meetingDevice, now);
     }
 
     @Override
-    public void remove(Long deviceId) {
-        MeetingDevice meetingDevice = this.getById(deviceId);
+    @Transactional(rollbackFor = Exception.class)
+    public void remove(String deviceUuid) {
+        if (StringUtils.isBlank(deviceUuid)) {
+            throw new BusinessException("设备uuid不能为空");
+        }
+        MeetingDevice meetingDevice = this.getById(deviceUuid);
         Optional.ofNullable(meetingDevice).orElseThrow(() -> new BusinessException("该会议室设备不存在"));
-        this.removeById(deviceId);
-
-        // remoteIotTaskService.deleteDeviceInfo(deviceId.toString());
+        Integer bindCount = meetingDevicePersonBindMapper.countByDeviceUuid(deviceUuid);
+        if (bindCount != null && bindCount > 0) {
+            throw new BusinessException("已绑定人员不能删除");
+        }
+        spaceDeviceMeetingMapper.deleteById(deviceUuid);
+        this.removeById(deviceUuid);
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public void bindPerson(MeetingDeviceBindPersonRequest request) {
-        if (request == null || request.getDeviceUuid() == null) {
+        if (request == null || StringUtils.isBlank(request.getDeviceUuid())) {
             throw new BusinessException("设备UUID不能为空");
         }
-        Long deviceUuid = request.getDeviceUuid();
+        String deviceUuid = request.getDeviceUuid();
         MeetingDevice device = this.getById(deviceUuid);
         Optional.ofNullable(device).orElseThrow(() -> new BusinessException("会议设备信息不存在"));
 
-        // 覆盖式:先清空原有绑定
         meetingDevicePersonBindMapper.deleteByDeviceUuid(deviceUuid);
 
         String personIds = request.getPersonIds();
-        // 空字符串视为解除全部授权
         if (!StringUtils.isNotBlank(personIds)) {
             return;
         }
@@ -373,7 +285,7 @@ public class MeetingDeviceServiceImpl extends AbstractCrudService<MeetingDeviceM
         Integer sz = queryVO.getSize();
         IPage<SysPerson> page = new Page<>(cur, sz);
 
-        List<Long> deviceUuids = new ArrayList<>();
+        List<String> deviceUuids = new ArrayList<>();
         if (StringUtils.isNotBlank(queryVO.getDeviceCode())) {
             MeetingDevice tempDevice = deviceMapper.selectOne(Wrappers.<MeetingDevice>lambdaQuery()
                     .eq(MeetingDevice::getDeviceCode, queryVO.getDeviceCode()));
@@ -383,16 +295,17 @@ public class MeetingDeviceServiceImpl extends AbstractCrudService<MeetingDeviceM
             MeetingDevice device = deviceMapper.selectOne(Wrappers.<MeetingDevice>lambdaQuery()
                     .eq(MeetingDevice::getDeviceCode, queryVO.getDeviceCode())
                     .eq(MeetingDevice::getTenantId, tempDevice.getTenantId()));
-            if (device == null || device.getDeviceUuid() == null) {
+            if (device == null || StringUtils.isBlank(device.getDeviceUuid())) {
                 return new CommonPage<>(new ArrayList<>(), 0L, sz, cur);
             }
             deviceUuids.add(device.getDeviceUuid());
         } else {
             List<MeetingDevice> devices = deviceMapper.selectList(Wrappers.<MeetingDevice>lambdaQuery()
-                    .eq(MeetingDevice::getTenantId, SecurityUtils.getTenantId()));
+                    .eq(MeetingDevice::getTenantId, SecurityUtils.getTenantId())
+                    .eq(MeetingDevice::getDeviceType, MeetingDevice.DEVICE_TYPE_MEETING));
             if (!CollectionUtils.isEmpty(devices)) {
                 for (MeetingDevice d : devices) {
-                    if (d != null && d.getDeviceUuid() != null) {
+                    if (d != null && StringUtils.isNotBlank(d.getDeviceUuid())) {
                         deviceUuids.add(d.getDeviceUuid());
                     }
                 }
@@ -479,11 +392,53 @@ public class MeetingDeviceServiceImpl extends AbstractCrudService<MeetingDeviceM
     }
 
     public boolean checkNameUnique(MeetingDevice meetingDevice) {
-        Long deviceId = null == meetingDevice.getDeviceUuid() ? -1 : meetingDevice.getDeviceUuid();
+        String uuid = meetingDevice.getDeviceUuid() == null ? "" : meetingDevice.getDeviceUuid();
         LambdaQueryWrapper<MeetingDevice> queryWrapper = Wrappers.lambdaQuery();
         queryWrapper.eq(MeetingDevice::getDeviceName, meetingDevice.getDeviceName())
                 .eq(MeetingDevice::getTenantId, SecurityUtils.getTenantId());
         MeetingDevice one = this.getOne(queryWrapper);
-        return null != one && !Objects.equals(one.getDeviceUuid(), deviceId);
+        return one != null && !Objects.equals(one.getDeviceUuid(), uuid);
+    }
+
+    private void fillDeviceCodeFromHeartbeat(MeetingDevice meetingDevice, LocalDateTime now) {
+        String ipAddr = meetingDevice.getIpAddr();
+        LambdaQueryWrapper<MeetingDeviceHeartbeat> queryWrapper = Wrappers.lambdaQuery();
+        queryWrapper.eq(MeetingDeviceHeartbeat::getIpAddr, ipAddr);
+        MeetingDeviceHeartbeat meetingDeviceHeartbeat = heartbeatMapper.selectOne(queryWrapper);
+        if (meetingDeviceHeartbeat == null) {
+            throw new BusinessException("设备离线!请重新输入");
+        }
+        LocalDateTime createTime = meetingDeviceHeartbeat.getCreateTime();
+        if (createTime.isAfter(now) || !createTime.plusMinutes(6).isAfter(now)) {
+            throw new BusinessException("设备离线!请重新输入");
+        }
+        meetingDevice.setDeviceCode(meetingDeviceHeartbeat.getDeviceCode());
+    }
+
+    private void saveMeetingExt(MeetingDevice meetingDevice, LocalDateTime now) {
+        SpaceDeviceMeeting ext = spaceDeviceMeetingMapper.selectById(meetingDevice.getDeviceUuid());
+        if (ext == null) {
+            ext = new SpaceDeviceMeeting();
+            ext.setDeviceUuid(meetingDevice.getDeviceUuid());
+            ext.setCreateTime(now);
+            fillExt(ext, meetingDevice);
+            spaceDeviceMeetingMapper.insert(ext);
+            return;
+        }
+        fillExt(ext, meetingDevice);
+        ext.setUpdateTime(now);
+        spaceDeviceMeetingMapper.updateById(ext);
+    }
+
+    private void fillExt(SpaceDeviceMeeting ext, MeetingDevice meetingDevice) {
+        ext.setStorageId(meetingDevice.getStorageId());
+        ext.setNameplate(meetingDevice.getNameplate());
+        ext.setRoomId(meetingDevice.getRoomId());
+        ext.setRegisterDate(meetingDevice.getRegisterDate());
+        ext.setTemplateId(meetingDevice.getTemplateId());
+        ext.setDeviceStatus(meetingDevice.getDeviceStatus());
+        ext.setDeviceDirection(meetingDevice.getDeviceDirection());
+        ext.setSignType(meetingDevice.getSignType());
+        ext.setSignOutType(meetingDevice.getSignOutType());
     }
 }

+ 0 - 178
service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/service/impl/MeetingFaceServiceImpl.java

@@ -1,178 +0,0 @@
-package com.usky.meeting.service.impl;
-
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.toolkit.StringUtils;
-import com.usky.common.security.utils.SecurityUtils;
-import com.usky.meet.utils.TimeUtils;
-import com.usky.meeting.domain.MeetingDevice;
-import com.usky.meeting.domain.MeetingFace;
-import com.usky.meeting.mapper.MeetingDeviceMapper;
-import com.usky.meeting.mapper.MeetingFaceMapper;
-import com.usky.meeting.repository.MeetingRoomRepository;
-import com.usky.meeting.server.FaceContrastServer;
-import com.usky.meeting.service.MeetingFaceService;
-import com.usky.common.mybatis.core.AbstractCrudService;
-import com.usky.meeting.service.vo.FaceResultVO;
-import com.usky.meeting.service.vo.MeetingFaceRequestVO;
-import com.usky.meeting.service.vo.MeetingFaceVO;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
-import java.util.List;
-
-/**
- * <p>
- *  服务实现类
- * </p>
- *
- * @author zyj
- * @since 2024-03-08
- */
-@Service
-public class MeetingFaceServiceImpl extends AbstractCrudService<MeetingFaceMapper, MeetingFace> implements MeetingFaceService {
-
-    @Autowired
-    FaceContrastServer faceContrastServer;
-    @Autowired
-    private MeetingRoomRepository dmMeetingRoomRepository;
-    @Autowired
-    private MeetingDeviceMapper deviceMapper;
-
-    @Override
-    public MeetingFaceVO vef(MeetingFaceRequestVO requestVO) {
-
-        String deviceCode = requestVO.getDeviceCode();
-        Integer tenantId;
-        if(StringUtils.isNotBlank(deviceCode)){
-            LambdaQueryWrapper<MeetingDevice> queryWrapper = new LambdaQueryWrapper<>();
-            queryWrapper.eq(MeetingDevice::getDeviceCode,requestVO.getDeviceCode());
-            tenantId = deviceMapper.selectOne(queryWrapper).getTenantId();
-        }else{
-            tenantId = SecurityUtils.getTenantId();
-        }
-
-        List<MeetingFace> faceList = lambdaQuery().eq(MeetingFace::getTenantId,tenantId).orderByDesc(MeetingFace::getVefNum).list();
-        MeetingFaceVO faceState = new MeetingFaceVO();
-        MeetingFaceVO faceState1 = new MeetingFaceVO();
-        faceState.setTenantId(tenantId);
-        faceState1.setTenantId(tenantId);
-        // 如果人脸库为空,则第一次登录为录入人脸
-        if (!faceList.isEmpty()){
-            //人脸校验
-            if(StringUtils.isNotBlank(requestVO.getImageBase())){
-                int faceLength = faceList.size();
-                for (MeetingFace face : faceList) {
-                    FaceResultVO faceResult = faceContrastServer.faceContrast(face.getFaceBase(), requestVO.getImageBase());
-                    // 是否比对成功
-                    if (faceResult.getCode() == MeetingFaceVO.SUCCESS_CODE ){
-                        // 相似度是否大于80
-                        if (faceResult.getScore() > MeetingFaceVO.SATISFY_SCORE){
-                            if (face.getFaceStatus() == 0){
-                                // 成功
-                                lambdaUpdate().set(MeetingFace::getVefNum,face.getVefNum()+1).eq(MeetingFace::getFid,face.getFid()).update();
-                                faceState.setMsg(TimeUtils.timeQuantum()+"好,"+face.getFaceName());
-                                faceState.setFaceName(face.getFaceName());
-                                faceState.setUserId(face.getUserId());
-                                faceState.setCode(MeetingFaceVO.SUCCESS_CODE);
-                                faceState.setFaceBase(requestVO.getImageBase());
-                                faceState.setVefType(1);
-                                return faceState;
-                            }else {
-                                // 失败 人脸被禁用
-                                lambdaUpdate().set(MeetingFace::getVefNum,face.getVefNum()+1).eq(MeetingFace::getFid,face.getFid()).update();
-                                faceState1.setMsg("当前人脸被禁用");
-                                faceState1.setFaceName(face.getFaceName());
-                                faceState1.setUserId(face.getUserId());
-                                faceState1.setCode(MeetingFaceVO.FORBIDDEN_FACE);
-                                faceState1.setFaceBase(requestVO.getImageBase());
-                                faceState1.setVefType(1);
-                                // 就算上一张人脸被禁用还得往下执行
-                                // 可能当前用户存在多张人脸,
-                                if (faceLength == 1){
-                                    return faceState1;
-                                }
-                                faceLength --;
-                            }
-                        }else {
-                            // 人脸库没有检测到人脸
-                            if (faceLength == 1){
-                                // 判断当前人脸是否被禁用,如被禁用,提示被禁用
-                                // 禁用优先级大于 没有检测到人脸
-                                faceState.setMsg("人脸不匹配,请联系管理员");
-                                faceState.setCode(MeetingFaceVO.NOT_FOUND_FACE);
-                                faceState.setFaceBase(requestVO.getImageBase());
-                                faceState.setVefType(1);
-                                return faceState;
-                            }
-                            faceLength --;
-                        }
-                    }else {
-                        // 接口返回异常
-                        faceState.setMsg("人脸验证接口返回异常");
-                        faceState.setCode(MeetingFaceVO.FACE_ERROR);
-                        return faceState;
-                    }
-                }
-            }else if(StringUtils.isNotBlank(requestVO.getCardNum())){  //卡号校验
-                int faceLength = faceList.size();
-                String cardNum = requestVO.getCardNum();
-                for (MeetingFace face : faceList) {
-                    if(cardNum.equals(face.getCardNum())){
-                        // 成功
-                        lambdaUpdate().set(MeetingFace::getVefNum,face.getVefNum()+1).eq(MeetingFace::getFid,face.getFid()).update();
-                        faceState.setMsg(TimeUtils.timeQuantum()+"好,"+face.getFaceName());
-                        faceState.setFaceName(face.getFaceName());
-                        faceState.setUserId(face.getUserId());
-                        faceState.setCode(MeetingFaceVO.SUCCESS_CODE);
-                        faceState.setCardNum(requestVO.getCardNum());
-                        faceState.setVefType(2);
-                        return faceState;
-                    }else {
-                        // 失败 卡号不存在
-                        lambdaUpdate().set(MeetingFace::getVefNum,face.getVefNum()+1).eq(MeetingFace::getFid,face.getFid()).update();
-                        faceState1.setMsg("当前卡号不存在");
-                        faceState1.setCode(MeetingFaceVO.NOT_FOUND_FACE);
-                        faceState1.setCardNum(requestVO.getCardNum());
-                        faceState1.setVefType(2);
-                        // 就算上一张卡号没有找到还得往下执行
-                        if (faceLength == 1){
-                            return faceState1;
-                        }
-                        faceLength --;
-                    }
-                }
-            }
-
-
-        }
-        // 空异常
-        if(StringUtils.isNotBlank(requestVO.getImageBase())){
-            faceState.setMsg("人脸不存在,请联系管理员");
-            faceState.setCode(MeetingFaceVO.NULL_ERROR);
-            faceState.setFaceBase(requestVO.getImageBase());
-            faceState.setVefType(1);
-        }else if(StringUtils.isNotBlank(requestVO.getCardNum())){
-            faceState.setMsg("卡号不存在,请联系管理员");
-            faceState.setCode(MeetingFaceVO.NULL_ERROR);
-            faceState.setFaceBase(requestVO.getCardNum());
-            faceState.setVefType(2);
-        }
-
-        return faceState;
-    }
-
-
-//    public MeetingFaceVO initFace(String imageBase){
-//        MeetingFaceVO faceResult = new MeetingFaceVO();
-//        MeetingFace face = new MeetingFace();
-//        face.setFaceBase(imageBase);
-//        face.setCreateTime(LocalDateTime.now());
-//        face.setVefNum(0);
-//        face.setFaceName("admin");
-//        face.setFaceStatus(0);
-//        boolean save = save(face);
-//        faceResult.setMsg("人脸初始化"+(save?"成功":"失败")+","+(save?"请验证登录":"请稍后再试"));
-//        faceResult.setFaceName(face.getFaceName());
-//        return faceResult;
-//    }
-}

+ 1 - 1
service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/service/impl/MeetingRoomServiceImpl.java

@@ -328,7 +328,7 @@ public class MeetingRoomServiceImpl extends AbstractCrudService<MeetingRoomMappe
         MeetingFloor meetingFloor = dmFloorService.getById(floorId);
 
         // 会议室设备列表
-        List<MeetingDevice> deviceList = meetingDeviceRepository.getDeviceListByRoomId(roomId);
+        List<MeetingDevice> deviceList = deviceMapper.selectListByRoomId(roomId);
         // 查询今天所有的会议
         List<MeetingInfo> meetingList = meetingInfoRepository.queryTodayMeetingByRoomId(roomId);
 

+ 4 - 22
service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/service/impl/MeetingTemplateServiceImpl.java

@@ -1,30 +1,23 @@
 package com.usky.meeting.service.impl;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.usky.common.core.bean.CommonPage;
 import com.usky.common.core.exception.BusinessException;
 import com.usky.common.security.utils.SecurityUtils;
-import com.usky.meeting.domain.MeetingDevice;
 import com.usky.meeting.domain.MeetingTemplate;
-import com.usky.meeting.mapper.MeetingDeviceMapper;
 import com.usky.meeting.mapper.MeetingTemplateMapper;
+import com.usky.meeting.mapper.SpaceDeviceMeetingMapper;
 import com.usky.meeting.service.MeetingTemplateService;
 import com.usky.common.mybatis.core.AbstractCrudService;
 import com.usky.meeting.service.vo.MeetingTemplateRequestVO;
 import com.usky.system.domain.SysUserVO;
-import com.usky.system.model.LoginUser;
 import org.apache.commons.lang3.StringUtils;
-import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 
-import java.security.Security;
 import java.time.LocalDateTime;
-import java.util.Collection;
-import java.util.Collections;
 import java.util.List;
 
 /**
@@ -39,7 +32,7 @@ import java.util.List;
 public class MeetingTemplateServiceImpl extends AbstractCrudService<MeetingTemplateMapper, MeetingTemplate> implements MeetingTemplateService {
 
     @Autowired
-    private MeetingDeviceMapper meetingDeviceMapper;
+    private SpaceDeviceMeetingMapper spaceDeviceMeetingMapper;
 
     @Override
     @Transactional(rollbackFor = Exception.class)
@@ -81,11 +74,7 @@ public class MeetingTemplateServiceImpl extends AbstractCrudService<MeetingTempl
             throw new BusinessException("会议模板不存在!");
         }
 
-        LambdaUpdateWrapper<MeetingDevice> deviceWrap = new LambdaUpdateWrapper<>();
-        deviceWrap.set(MeetingDevice::getTemplateId, null)
-                .eq(MeetingDevice::getTemplateId, templateId)
-                .eq(MeetingDevice::getTenantId, tenantId);
-        meetingDeviceMapper.update(null, deviceWrap);
+        spaceDeviceMeetingMapper.clearTemplateId(templateId.intValue(), tenantId);
     }
 
     @Override
@@ -121,13 +110,6 @@ public class MeetingTemplateServiceImpl extends AbstractCrudService<MeetingTempl
             throw new BusinessException("模板不存在!");
         }
 
-        LambdaUpdateWrapper<MeetingDevice> unwrap = new LambdaUpdateWrapper<>();
-        unwrap.eq(MeetingDevice::getTenantId, tenantId);
-        if (deviceCodes != null && !deviceCodes.isEmpty()) {
-            unwrap.in(MeetingDevice::getDeviceCode, deviceCodes);
-        }
-        MeetingDevice updater = new MeetingDevice();
-        updater.setTemplateId(templateId);
-        meetingDeviceMapper.update(updater, unwrap);
+        spaceDeviceMeetingMapper.updateTemplateId(templateId, tenantId, deviceCodes);
     }
 }

+ 0 - 98
service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/service/vo/FaceResultVO.java

@@ -1,98 +0,0 @@
-package com.usky.meeting.service.vo;
-
-import lombok.Data;
-
-/**
- * @author tanyongpeng
- * <p>des</p>
- **/
-@Data
-public class FaceResultVO {
-
-    /**
-     * 返回消息
-     */
-    private String msg;
-
-    /**
-     * 状态码
-     */
-    private int code;
-
-    /**
-     * 验证人姓名
-     */
-    private String name;
-
-    /**
-     * 返回token
-     */
-    private String token;
-
-    /**
-     * 数据集
-     */
-    private Object data;
-
-    /**
-     * 相似度
-     */
-    private Float score;
-
-    /*成功*/
-    public static final Integer SUCCESS_CODE = 200;
-    /*相似匹配度*/
-    public static final Integer SATISFY_SCORE = 80;
-    /*调用腾讯云接口发生的异常*/
-    public static final Integer FACE_ERROR = -1;
-    /*数据库没有该人脸*/
-    public static final Integer NOT_FOUND_FACE = -2;
-    /*人脸数据为空*/
-    public static final Integer NULL_ERROR = -3;
-    /*当前人脸被禁用*/
-    public static final Integer FORBIDDEN_FACE = -4;
-    /*初始化人脸*/
-    public static final Integer INIT_FACE = 201;
-
-    public FaceResultVO setCode(int code) {
-        this.code = code;
-        return this;
-    }
-
-    public FaceResultVO setData(Object data) {
-        this.data = data;
-        return this;
-    }
-
-    public FaceResultVO setMsg(String msg) {
-        this.msg = msg;
-        return this;
-    }
-
-    public FaceResultVO setScore(Float score) {
-        this.score = score;
-        return this;
-    }
-
-    public static FaceResultVO error(int code, String msg){
-        return new FaceResultVO().setCode(code).setMsg(msg);
-    }
-
-    public static FaceResultVO error(int code, String msg, Float score){
-        return new FaceResultVO().setCode(code).setMsg(msg).setScore(score);
-    }
-
-    public static FaceResultVO error(int code){
-        return new FaceResultVO().setCode(code);
-    }
-
-    public static FaceResultVO success(){
-        return new FaceResultVO().setCode(SUCCESS_CODE);
-    }
-    public static FaceResultVO success(Object data){
-        return new FaceResultVO().setCode(SUCCESS_CODE).setData(data);
-    }
-    public static FaceResultVO success(String msg){
-        return new FaceResultVO().setCode(SUCCESS_CODE).setMsg(msg);
-    }
-}

+ 7 - 5
service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/service/vo/MeetingDeviceBindPersonRequest.java

@@ -1,6 +1,5 @@
 package com.usky.meeting.service.vo;
 
-import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;
 
 /**
@@ -9,10 +8,13 @@ import lombok.Data;
 @Data
 public class MeetingDeviceBindPersonRequest {
 
-    /** 会议设备 UUID(meeting_device.device_uuid) */
-    @JsonFormat(shape = JsonFormat.Shape.STRING)
-    private Long deviceUuid;
+    /**
+     * 设备唯一编号(space_device.device_uuid)
+     */
+    private String deviceUuid;
 
-    /** 人员 ID,逗号分隔;空字符串表示清空授权 */
+    /**
+     * 人员ID,逗号分隔;空字符串表示清空授权
+     */
     private String personIds;
 }

+ 5 - 0
service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/service/vo/MeetingDeviceRequestVO.java

@@ -73,4 +73,9 @@ public class MeetingDeviceRequestVO {
      * 设备方向 1横屏 2竖屏
      */
     private Integer deviceDirection;
+
+    /**
+     * 设备类型:1门禁设备,2会议室空间设备
+     */
+    private Integer deviceType;
 }

+ 0 - 21
service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/service/vo/MeetingFaceRequestVO.java

@@ -1,21 +0,0 @@
-package com.usky.meeting.service.vo;
-
-import lombok.Data;
-
-@Data
-public class MeetingFaceRequestVO {
-    /**
-     * 图片编码
-     */
-    private String imageBase;
-
-    /**
-     * 设备编码
-     */
-    private String deviceCode;
-
-    /**
-     * 卡号
-     */
-    private String cardNum;
-}

+ 0 - 68
service-meeting/service-meeting-biz/src/main/java/com/usky/meeting/service/vo/MeetingFaceVO.java

@@ -1,68 +0,0 @@
-package com.usky.meeting.service.vo;
-
-import lombok.Data;
-
-@Data
-public class MeetingFaceVO {
-
-    /**
-     * 返回消息
-     */
-    private String msg;
-
-    /**
-     * 人脸名称
-     */
-    private String faceName;
-
-    /**
-     * 用户ID
-     */
-    private Long userId;
-
-    /**
-     * 状态码
-     */
-    private int code;
-
-    /**
-     * 图片数据 base_64编码
-     */
-    private String faceBase;
-
-    /**
-     * 卡号
-     */
-    private String cardNum;
-
-    /**
-     * 验证类型(1、人脸 2、卡号)
-     */
-    private Integer vefType;
-
-    /**
-     * 组织机构ID
-     */
-    private Integer deptId;
-
-    /**
-     * 租户ID
-     */
-    private Integer tenantId;
-
-
-    /*成功*/
-    public static final Integer SUCCESS_CODE = 200;
-    /*相似匹配度*/
-    public static final Integer SATISFY_SCORE = 80;
-    /*调用腾讯云接口发生的异常*/
-    public static final Integer FACE_ERROR = -1;
-    /*数据库没有该人脸*/
-    public static final Integer NOT_FOUND_FACE = -2;
-    /*人脸数据为空*/
-    public static final Integer NULL_ERROR = -3;
-    /*当前人脸被禁用*/
-    public static final Integer FORBIDDEN_FACE = -4;
-    /*初始化人脸*/
-    public static final Integer INIT_FACE = 201;
-}

+ 3 - 2
service-meeting/service-meeting-biz/src/main/resources/mapper/meeting/MeetingDeviceHeartbeatMapper.xml

@@ -2,16 +2,17 @@
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="com.usky.meeting.mapper.MeetingDeviceHeartbeatMapper">
 
-    <!-- 通用查询映射结果 -->
+    <!-- 通用查询映射结果:space_device_heartbeat -->
     <resultMap id="BaseResultMap" type="com.usky.meeting.domain.MeetingDeviceHeartbeat">
         <id column="id" property="id"/>
         <result column="device_code" property="deviceCode"/>
         <result column="ip_addr" property="ipAddr"/>
         <result column="mac_addr" property="macAddr"/>
         <result column="device_type" property="deviceType"/>
+        <result column="device_status" property="deviceStatus"/>
         <result column="create_time" property="createTime"/>
         <result column="model" property="model"/>
-        <result column="manu_facturer"  property="manuFacturer"/>
+        <result column="manu_facturer" property="manuFacturer"/>
         <result column="version" property="version"/>
         <result column="sdk" property="sdk"/>
     </resultMap>

+ 104 - 10
service-meeting/service-meeting-biz/src/main/resources/mapper/meeting/MeetingDeviceMapper.xml

@@ -2,18 +2,14 @@
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="com.usky.meeting.mapper.MeetingDeviceMapper">
 
-    <!-- 通用查询映射结果 -->
     <resultMap id="BaseResultMap" type="com.usky.meeting.domain.MeetingDevice">
         <id column="device_uuid" property="deviceUuid"/>
+        <result column="device_type" property="deviceType"/>
         <result column="device_name" property="deviceName"/>
-        <result column="storage_id" property="storageId"/>
-        <result column="img_path" property="imgPath"/>
-        <result column="nameplate" property="nameplate"/>
-        <result column="room_id" property="roomId"/>
-        <result column="register_date" property="registerDate"/>
-        <result column="maintainer" property="maintainer"/>
-        <result column="contacts" property="contacts"/>
+        <result column="device_code" property="deviceCode"/>
         <result column="install_address" property="installAddress"/>
+        <result column="device_ip" property="ipAddr"/>
+        <result column="img_path" property="imgPath"/>
         <result column="remark" property="remark"/>
         <result column="create_by" property="createBy"/>
         <result column="update_by" property="updateBy"/>
@@ -21,11 +17,109 @@
         <result column="update_time" property="updateTime"/>
         <result column="dept_id" property="deptId"/>
         <result column="tenant_id" property="tenantId"/>
-        <result column="device_code" property="deviceCode"/>
-        <result column="ip_addr" property="ipAddr"/>
+        <result column="storage_id" property="storageId"/>
+        <result column="nameplate" property="nameplate"/>
+        <result column="room_id" property="roomId"/>
+        <result column="register_date" property="registerDate"/>
         <result column="template_id" property="templateId"/>
         <result column="device_status" property="deviceStatus"/>
         <result column="device_direction" property="deviceDirection"/>
+        <result column="sign_type" property="signType"/>
+        <result column="sign_out_type" property="signOutType"/>
     </resultMap>
 
+    <select id="selectPageJoin" resultMap="BaseResultMap">
+        select
+            d.device_uuid,
+            d.device_type,
+            d.device_name,
+            d.device_code,
+            d.install_address,
+            d.device_ip,
+            d.img_path,
+            d.remark,
+            d.create_by,
+            d.update_by,
+            d.create_time,
+            d.update_time,
+            d.dept_id,
+            d.tenant_id,
+            m.storage_id,
+            m.nameplate,
+            m.room_id,
+            m.register_date,
+            m.template_id,
+            m.device_status,
+            m.device_direction,
+            m.sign_type,
+            m.sign_out_type
+        from space_device d
+        left join space_device_meeting m on m.device_uuid = d.device_uuid
+        <where>
+            d.tenant_id = #{tenantId}
+            <if test="req.deviceType != null">
+                and d.device_type = #{req.deviceType}
+            </if>
+            <if test="req.deviceCode != null and req.deviceCode != ''">
+                and d.device_code = #{req.deviceCode}
+            </if>
+            <if test="req.deviceName != null and req.deviceName != ''">
+                and d.device_name like concat('%', #{req.deviceName}, '%')
+            </if>
+            <if test="req.remark != null and req.remark != ''">
+                and d.remark like concat('%', #{req.remark}, '%')
+            </if>
+            <if test="req.nameplate != null and req.nameplate != ''">
+                and m.nameplate like concat('%', #{req.nameplate}, '%')
+            </if>
+            <if test="req.deviceStatus != null">
+                and m.device_status = #{req.deviceStatus}
+            </if>
+            <if test="req.deviceDirection != null">
+                and m.device_direction = #{req.deviceDirection}
+            </if>
+            <if test="req.roomId != null">
+                and m.room_id = #{req.roomId}
+            </if>
+            <if test="roomIdList != null and roomIdList.size() > 0">
+                and m.room_id in
+                <foreach collection="roomIdList" item="rid" open="(" separator="," close=")">
+                    #{rid}
+                </foreach>
+            </if>
+        </where>
+        order by d.create_time desc
+    </select>
+
+    <select id="selectListByRoomId" resultMap="BaseResultMap">
+        select
+            d.device_uuid,
+            d.device_type,
+            d.device_name,
+            d.device_code,
+            d.install_address,
+            d.device_ip,
+            d.img_path,
+            d.remark,
+            d.create_by,
+            d.update_by,
+            d.create_time,
+            d.update_time,
+            d.dept_id,
+            d.tenant_id,
+            m.storage_id,
+            m.nameplate,
+            m.room_id,
+            m.register_date,
+            m.template_id,
+            m.device_status,
+            m.device_direction,
+            m.sign_type,
+            m.sign_out_type
+        from space_device d
+        inner join space_device_meeting m on m.device_uuid = d.device_uuid
+        where m.room_id = #{roomId}
+        order by d.create_time desc
+    </select>
+
 </mapper>

+ 11 - 5
service-meeting/service-meeting-biz/src/main/resources/mapper/meeting/MeetingDevicePersonBindMapper.xml

@@ -9,25 +9,31 @@
     </resultMap>
 
     <delete id="deleteByDeviceUuid">
-        delete from meeting_device_person_bind
+        delete from space_device_person_bind
         where device_uuid = #{deviceUuid}
     </delete>
 
     <select id="selectByDeviceUuids" resultMap="BaseResultMap">
         select device_uuid, person_id, is_login_notify
-        from meeting_device_person_bind
+        from space_device_person_bind
         <where>
             <if test="deviceUuids != null and deviceUuids.size() > 0">
                 and device_uuid in
-                <foreach collection="deviceUuids" item="id" open="(" separator="," close=")">
-                    #{id}
+                <foreach collection="deviceUuids" item="uuid" open="(" separator="," close=")">
+                    #{uuid}
                 </foreach>
             </if>
         </where>
     </select>
 
+    <select id="countByDeviceUuid" resultType="java.lang.Integer">
+        select count(1)
+        from space_device_person_bind
+        where device_uuid = #{deviceUuid}
+    </select>
+
     <insert id="insert" parameterType="com.usky.meeting.domain.MeetingDevicePersonBind">
-        insert into meeting_device_person_bind (device_uuid, person_id, is_login_notify)
+        insert into space_device_person_bind (device_uuid, person_id, is_login_notify)
         values (#{deviceUuid}, #{personId}, #{isLoginNotify})
     </insert>
 

+ 0 - 21
service-meeting/service-meeting-biz/src/main/resources/mapper/meeting/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.meeting.mapper.MeetingFaceMapper">
-
-    <!-- 通用查询映射结果 -->
-    <resultMap id="BaseResultMap" type="com.usky.meeting.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>

+ 28 - 0
service-meeting/service-meeting-biz/src/main/resources/mapper/meeting/SpaceDeviceMeetingMapper.xml

@@ -0,0 +1,28 @@
+<?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.meeting.mapper.SpaceDeviceMeetingMapper">
+
+    <update id="clearTemplateId">
+        update space_device_meeting m
+            inner join space_device d on d.device_uuid = m.device_uuid
+        set m.template_id = null,
+            m.update_time = now()
+        where m.template_id = #{templateId}
+          and d.tenant_id = #{tenantId}
+    </update>
+
+    <update id="updateTemplateId">
+        update space_device_meeting m
+            inner join space_device d on d.device_uuid = m.device_uuid
+        set m.template_id = #{templateId},
+            m.update_time = now()
+        where d.tenant_id = #{tenantId}
+        <if test="deviceCodes != null and deviceCodes.size() > 0">
+            and d.device_code in
+            <foreach collection="deviceCodes" item="code" open="(" separator="," close=")">
+                #{code}
+            </foreach>
+        </if>
+    </update>
+
+</mapper>