feat(iot): 优化设备管理功能
- 添加附件预览功能 - 修复设备列表查询条件 - 优化产品和设备控制器 - 调整 WebSocket连接逻辑
This commit is contained in:
parent
182d4c7961
commit
790aed76ea
1
pom.xml
1
pom.xml
@ -145,7 +145,6 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||||
<version>3.4.0</version>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
|
@ -26,16 +26,14 @@ public class WebSocketInterceptor implements HandshakeInterceptor {
|
|||||||
private ProductService productService;
|
private ProductService productService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
|
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) {
|
||||||
if (request instanceof ServletServerHttpRequest) {
|
if (request instanceof ServletServerHttpRequest) {
|
||||||
HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest();
|
HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest();
|
||||||
// 获取路径变量
|
// 获取完整路径
|
||||||
String path = servletRequest.getRequestURI();
|
String path = servletRequest.getRequestURI();
|
||||||
String[] pathParts = path.split("/");
|
|
||||||
String pathVariable = pathParts[pathParts.length - 1];
|
|
||||||
|
|
||||||
// 根据路径变量设置不同的业务逻辑 Supplier
|
// 根据路径设置不同的业务逻辑 Supplier
|
||||||
switch (pathVariable) {
|
switch (path) {
|
||||||
case "/api/rest/device/ws/device/status":
|
case "/api/rest/device/ws/device/status":
|
||||||
attributes.put("dataSupplier", (Supplier<String>) () -> String.valueOf(deviceService.status()));
|
attributes.put("dataSupplier", (Supplier<String>) () -> String.valueOf(deviceService.status()));
|
||||||
break;
|
break;
|
||||||
@ -46,7 +44,7 @@ public class WebSocketInterceptor implements HandshakeInterceptor {
|
|||||||
attributes.put("dataSupplier", (Supplier<String>) () -> String.valueOf(productService.status()));
|
attributes.put("dataSupplier", (Supplier<String>) () -> String.valueOf(productService.status()));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
attributes.put("dataSupplier", (Supplier<String>) () -> "Unknown path: " + pathVariable);
|
attributes.put("dataSupplier", (Supplier<String>) () -> "Unknown path: " + path);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.zsc.edu.gateway.modules.attachment.controller;
|
package com.zsc.edu.gateway.modules.attachment.controller;
|
||||||
|
|
||||||
import com.zsc.edu.gateway.exception.StorageException;
|
import com.zsc.edu.gateway.exception.StorageException;
|
||||||
|
import com.zsc.edu.gateway.framework.storage.exception.StorageFileNotFoundException;
|
||||||
import com.zsc.edu.gateway.modules.attachment.entity.Attachment;
|
import com.zsc.edu.gateway.modules.attachment.entity.Attachment;
|
||||||
import com.zsc.edu.gateway.modules.attachment.service.AttachmentService;
|
import com.zsc.edu.gateway.modules.attachment.service.AttachmentService;
|
||||||
import com.zsc.edu.gateway.modules.operationLog.entity.OperationLogAnnotation;
|
import com.zsc.edu.gateway.modules.operationLog.entity.OperationLogAnnotation;
|
||||||
@ -8,6 +9,7 @@ import lombok.AllArgsConstructor;
|
|||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.http.ContentDisposition;
|
import org.springframework.http.ContentDisposition;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.security.core.parameters.P;
|
import org.springframework.security.core.parameters.P;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
@ -73,6 +75,43 @@ public class AttachmentController {
|
|||||||
}
|
}
|
||||||
return ResponseEntity.ok(wrapper.resource);
|
return ResponseEntity.ok(wrapper.resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预览附件
|
||||||
|
*
|
||||||
|
* @param id 附件ID
|
||||||
|
* @return 附件文件内容(直接预览)
|
||||||
|
*/
|
||||||
|
@GetMapping("/preview/{id}")
|
||||||
|
public ResponseEntity<?> preview(
|
||||||
|
@PathVariable("id") String id
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
Attachment.Wrapper wrapper = service.loadAsWrapper(id);
|
||||||
|
String mimeType = wrapper.attachment.mimeType;
|
||||||
|
|
||||||
|
// 如果是图片或 PDF,直接返回文件流
|
||||||
|
if (mimeType.startsWith("image/") || mimeType.equals("application/pdf")) {
|
||||||
|
ContentDisposition contentDisposition = ContentDisposition.builder("inline")
|
||||||
|
.filename(wrapper.attachment.fileName, StandardCharsets.UTF_8)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
return ResponseEntity.ok()
|
||||||
|
.header(HttpHeaders.CONTENT_DISPOSITION, contentDisposition.toString())
|
||||||
|
.header(HttpHeaders.CONTENT_TYPE, mimeType)
|
||||||
|
.body(wrapper.resource);
|
||||||
|
} else {
|
||||||
|
// 如果是文本文件,直接返回文本内容
|
||||||
|
String content = new String(wrapper.resource.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
|
||||||
|
return ResponseEntity.ok(content);
|
||||||
|
}
|
||||||
|
} catch (StorageFileNotFoundException e) {
|
||||||
|
return ResponseEntity.notFound().build();
|
||||||
|
} catch (IOException e) {
|
||||||
|
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("文件读取失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据附件ID获取附件信息
|
* 根据附件ID获取附件信息
|
||||||
* */
|
* */
|
||||||
|
@ -4,7 +4,6 @@ import com.alibaba.fastjson2.JSONException;
|
|||||||
import com.alibaba.fastjson2.JSONObject;
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.zsc.edu.gateway.framework.message.sse.SseConfig;
|
|
||||||
import com.zsc.edu.gateway.framework.mybatisplus.DataPermission;
|
import com.zsc.edu.gateway.framework.mybatisplus.DataPermission;
|
||||||
import com.zsc.edu.gateway.modules.iot.device.dto.BatchDeviceDto;
|
import com.zsc.edu.gateway.modules.iot.device.dto.BatchDeviceDto;
|
||||||
import com.zsc.edu.gateway.modules.iot.device.dto.DeviceDto;
|
import com.zsc.edu.gateway.modules.iot.device.dto.DeviceDto;
|
||||||
@ -38,8 +37,6 @@ public class DeviceController {
|
|||||||
DeviceService service;
|
DeviceService service;
|
||||||
|
|
||||||
RecordDataService recordService;
|
RecordDataService recordService;
|
||||||
@Resource
|
|
||||||
private SseConfig sseConfig;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建设备
|
* 创建设备
|
||||||
|
@ -64,4 +64,5 @@ public class DeviceDto {
|
|||||||
*/
|
*/
|
||||||
public String previewId;
|
public String previewId;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -121,7 +121,7 @@ public class Device extends BaseEntity {
|
|||||||
* 设备预览图附件ID
|
* 设备预览图附件ID
|
||||||
*/
|
*/
|
||||||
public String previewId;
|
public String previewId;
|
||||||
|
//TODO 设备运行状态
|
||||||
|
|
||||||
public enum Status implements IEnum<Integer>, IState<Status> {
|
public enum Status implements IEnum<Integer>, IState<Status> {
|
||||||
UNACTIVATED(0, "未激活"),
|
UNACTIVATED(0, "未激活"),
|
||||||
|
@ -43,10 +43,7 @@ public class DeviceQuery {
|
|||||||
* 设态是否在线
|
* 设态是否在线
|
||||||
*/
|
*/
|
||||||
public Boolean isOnline;
|
public Boolean isOnline;
|
||||||
/**
|
|
||||||
* 产品名称
|
|
||||||
*/
|
|
||||||
public String productName;
|
|
||||||
|
|
||||||
public LambdaQueryWrapper<Device> wrapper() {
|
public LambdaQueryWrapper<Device> wrapper() {
|
||||||
LambdaQueryWrapper<Device> queryWrapper = new LambdaQueryWrapper<>();
|
LambdaQueryWrapper<Device> queryWrapper = new LambdaQueryWrapper<>();
|
||||||
|
@ -32,4 +32,6 @@ public interface DeviceRepository extends BaseMapper<Device> {
|
|||||||
Device findByClientId(@Param("clientId") String clientId);
|
Device findByClientId(@Param("clientId") String clientId);
|
||||||
|
|
||||||
List<Device> selectListByName(@Param("name") String name);
|
List<Device> selectListByName(@Param("name") String name);
|
||||||
|
|
||||||
|
IPage<Device> selectPageByConditions(Page<Device> page, @Param("query") DeviceQuery query);
|
||||||
}
|
}
|
||||||
|
@ -172,9 +172,10 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceRepository, Device> imp
|
|||||||
/**
|
/**
|
||||||
* 查询设备列表
|
* 查询设备列表
|
||||||
*/
|
*/
|
||||||
|
//TODO 解决分页条件问题
|
||||||
@Override
|
@Override
|
||||||
public IPage<Device> query(Page<Device> page, DeviceQuery query) {
|
public IPage<Device> query(Page<Device> page, DeviceQuery query) {
|
||||||
return deviceRepo.selectPage(page, query.wrapper());
|
return baseMapper.selectPageByConditions(page, query);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -126,7 +126,6 @@ public class DeviceVo {
|
|||||||
*/
|
*/
|
||||||
public Attachment preview;
|
public Attachment preview;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 所属产品ID
|
* 所属产品ID
|
||||||
*/
|
*/
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
package com.zsc.edu.gateway.modules.iot.product.controller;
|
package com.zsc.edu.gateway.modules.iot.product.controller;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.zsc.edu.gateway.framework.message.sse.SseConfig;
|
|
||||||
import com.zsc.edu.gateway.framework.mybatisplus.DataPermission;
|
import com.zsc.edu.gateway.framework.mybatisplus.DataPermission;
|
||||||
import com.zsc.edu.gateway.modules.iot.product.dto.ProductDto;
|
import com.zsc.edu.gateway.modules.iot.product.dto.ProductDto;
|
||||||
import com.zsc.edu.gateway.modules.iot.product.entity.Product;
|
import com.zsc.edu.gateway.modules.iot.product.entity.Product;
|
||||||
import com.zsc.edu.gateway.modules.iot.product.query.ProductQuery;
|
import com.zsc.edu.gateway.modules.iot.product.query.ProductQuery;
|
||||||
import com.zsc.edu.gateway.modules.iot.product.service.ProductService;
|
import com.zsc.edu.gateway.modules.iot.product.service.ProductService;
|
||||||
import com.zsc.edu.gateway.modules.operationLog.entity.OperationLogAnnotation;
|
import com.zsc.edu.gateway.modules.operationLog.entity.OperationLogAnnotation;
|
||||||
import jakarta.annotation.Resource;
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
@ -25,8 +23,6 @@ public class ProductController {
|
|||||||
|
|
||||||
private final ProductService service;
|
private final ProductService service;
|
||||||
|
|
||||||
@Resource
|
|
||||||
SseConfig sseConfig;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建产品
|
* 创建产品
|
||||||
|
@ -82,10 +82,27 @@
|
|||||||
where d.id = #{id}
|
where d.id = #{id}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectList" resultType="com.zsc.edu.gateway.modules.iot.device.entity.Device">
|
<select id="selectPageByConditions" resultType="com.zsc.edu.gateway.modules.iot.device.entity.Device">
|
||||||
SELECT d.*, p.name AS productName
|
SELECT d.*, p.*
|
||||||
FROM iot_device d
|
FROM iot_device d
|
||||||
LEFT JOIN iot_product p ON d.product_id = p.id
|
LEFT JOIN iot_product p ON d.product_id = p.id
|
||||||
|
<where>
|
||||||
|
<if test="query.name != null and query.name != ''">
|
||||||
|
AND d.name LIKE concat('%', #{query.name}, '%')
|
||||||
|
</if>
|
||||||
|
<if test="query.clientId != null and query.clientId != ''">
|
||||||
|
AND d.client_id = #{query.clientId}
|
||||||
|
</if>
|
||||||
|
<if test="query.state != null and query.state != ''">
|
||||||
|
AND d.state = #{query.state}
|
||||||
|
</if>
|
||||||
|
<if test="query.isOnline != null and query.isOnline != ''">
|
||||||
|
AND d.online = #{query.isOnline}
|
||||||
|
</if>
|
||||||
|
<if test="query.productId != null and query.productId != ''">
|
||||||
|
AND d.product_id = #{query.productId}
|
||||||
|
</if>
|
||||||
|
</where>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectListByName" resultMap="BaseResultMap">
|
<select id="selectListByName" resultMap="BaseResultMap">
|
||||||
@ -105,6 +122,8 @@
|
|||||||
left join iot_param ip on p.id = ip.foreign_id and ip.foreign_type = 3
|
left join iot_param ip on p.id = ip.foreign_id and ip.foreign_type = 3
|
||||||
left join attachment ai on d.icon_id = ai.id
|
left join attachment ai on d.icon_id = ai.id
|
||||||
left join attachment ap on d.preview_id = ap.id
|
left join attachment ap on d.preview_id = ap.id
|
||||||
WHERE d.name LIKE concat('%', #{name}, '%')
|
<if test="name != null">
|
||||||
|
WHERE d.name LIKE concat('%', #{name}, '%')
|
||||||
|
</if>
|
||||||
</select>
|
</select>
|
||||||
</mapper>
|
</mapper>
|
Loading…
Reference in New Issue
Block a user