diff --git a/aa.conf b/aa.conf new file mode 100644 index 00000000..383531e1 --- /dev/null +++ b/aa.conf @@ -0,0 +1,119 @@ + +#user nobody; +worker_processes 1; + +#error_log logs/error.log; +#error_log logs/error.log notice; +#error_log logs/error.log info; + +#pid logs/nginx.pid; + + +events { + worker_connections 1024; +} + + +http { + include mime.types; + default_type application/octet-stream; + + #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + # '$status $body_bytes_sent "$http_referer" ' + # '"$http_user_agent" "$http_x_forwarded_for"'; + + #access_log logs/access.log main; + + sendfile on; + #tcp_nopush on; + + #keepalive_timeout 0; + keepalive_timeout 65; + + #gzip on; + + server { + listen 80; + server_name cnxhyc.com; + + #charset koi8-r; + + #access_log logs/host.access.log main; + + location / { + root html; + index index.html index.htm; + } + + #error_page 404 /404.html; + + # redirect server error pages to the static page /50x.html + # + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root html; + } + + # proxy the PHP scripts to Apache listening on 127.0.0.1:80 + # + #location ~ \.php$ { + # proxy_pass http://127.0.0.1; + #} + + # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 + # + #location ~ \.php$ { + # root html; + # fastcgi_pass 127.0.0.1:9000; + # fastcgi_index index.php; + # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; + # include fastcgi_params; + #} + + # deny access to .htaccess files, if Apache's document root + # concurs with nginx's one + # + #location ~ /\.ht { + # deny all; + #} + } + + + # another virtual host using mix of IP-, name-, and port-based configuration + # + #server { + # listen 8000; + # listen somename:8080; + # server_name somename alias another.alias; + + # location / { + # root html; + # index index.html index.htm; + # } + #} + + + # HTTPS server + + server { + listen 443 ssl http2; + server_name cnxhyc.com; + + ssl_certificate /cert/cnxhyc.com.pem; + ssl_certificate_key /cert/cnxhyc.com.key; + + # 使用更现代的 SSL 配置 + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers TLS_AES_256_GCM_SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; + ssl_prefer_server_ciphers off; + + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 10m; + + location / { + root html; + index index.html index.htm; + } + } + +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/CorrectVCardGenerator.java b/fys-admin/src/main/java/com/fuyuanshen/CorrectVCardGenerator.java new file mode 100644 index 00000000..8c4b7199 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/CorrectVCardGenerator.java @@ -0,0 +1,96 @@ +package com.fuyuanshen; + +import java.io.BufferedWriter; +import java.io.FileWriter; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; + +public class CorrectVCardGenerator { + public static void main(String[] args) { + // // 定义江西上饶的134号段(共22个) + // String[] prefixes = { + // "1340703", "1340793", "1342650", "1342651", "1342663", + // "1342664", "1342665", "1343703", "1343793", "1347901", + // "1347902", "1347903", "1347930", "1347931", "1347932", + // "1347933", "1347934", "1347935", "1347936", "1347937", + // "1347938", "1347939" + // }; 1340700 号段(移动) + // 1340708 号段(移动) + // 1340709 号段(移动) + // 1340791 号段(移动) + // 1342668 号段(移动) + // 1343700 号段(移动) + // 1343708 号段(移动) + // 1343709 号段(移动) + // 1343790 号段(移动) + // 1343791 号段(移动) + // 1347910 号段(移动) + // 1347911 号段(移动) + // 1347912 号段(移动) + // 1347913 号段(移动) + // 1347914 号段(移动) + // 1347915 号段(移动) + // 1347916 号段(移动) + // 1347917 号段(移动) + // 1347918 号段(移动) + // 1347919 号段(移动) + + // 定义江西南昌的134号段(共22个) + String[] prefixes = { + "1340700", "1340708", "1340709", "1340791", "1342668", + "1343700", "1343708", "1343709", "1343790", "1343791", + "1347910", "1347911", "1347912", "1347913", "1347914", + "1347915", "1347916", "1347917", "1347918", "1347919" + }; + + // 创建.vcf文件 + String filename = "南昌联系人.vcf"; + + try (BufferedWriter writer = new BufferedWriter(new FileWriter(filename))) { + // 生成所有联系人 + int count = 0; + for (String prefix : prefixes) { + for (int i = 0; i < 100; i++) { + String middle = String.format("%02d", i); + String phoneNumber = prefix + middle + "51"; + + // 每个联系人以BEGIN:VCARD开始 + writer.write("BEGIN:VCARD"); + writer.newLine(); + writer.write("VERSION:3.0"); + writer.newLine(); + + // 中文姓名(用户+序号) + writer.write("FN:用户" + (count + 1)); + writer.newLine(); + + // 手机号码 + writer.write("TEL;TYPE=CELL;TYPE=VOICE:" + phoneNumber); + writer.newLine(); + + // 添加唯一标识符 + writer.write("UID:" + phoneNumber + "@shangrao"); + writer.newLine(); + + // 添加备注信息 + writer.write("NOTE:生成于" + new SimpleDateFormat("yyyy-MM-dd").format(new Date())); + writer.newLine(); + + // 每个联系人以END:VCARD结束 + writer.write("END:VCARD"); + writer.newLine(); + writer.newLine(); // 联系人之间空一行 + + count++; + } + } + + System.out.println("成功生成 " + count + " 个联系人"); + System.out.println("文件已保存为: " + filename); + + } catch (IOException e) { + System.err.println("文件写入错误: " + e.getMessage()); + } + } +} \ No newline at end of file diff --git a/fys-admin/src/main/java/com/fuyuanshen/app/controller/AppDeviceController.java b/fys-admin/src/main/java/com/fuyuanshen/app/controller/AppDeviceController.java index 1151c88e..d4b1695e 100644 --- a/fys-admin/src/main/java/com/fuyuanshen/app/controller/AppDeviceController.java +++ b/fys-admin/src/main/java/com/fuyuanshen/app/controller/AppDeviceController.java @@ -1,28 +1,20 @@ package com.fuyuanshen.app.controller; -import cn.hutool.json.JSON; -import com.alibaba.fastjson2.JSONObject; -import com.fuyuanshen.app.domain.bo.AppPersonnelInfoBo; import com.fuyuanshen.app.domain.dto.APPReNameDTO; -import com.fuyuanshen.app.domain.dto.AppDeviceLogoUploadDto; -import com.fuyuanshen.app.domain.dto.DeviceInstructDto; +import com.fuyuanshen.app.domain.dto.AppRealTimeStatusDto; import com.fuyuanshen.app.domain.vo.APPDeviceTypeVo; -import com.fuyuanshen.app.domain.vo.AppDeviceDetailVo; -import com.fuyuanshen.app.service.AppDeviceBizService; import com.fuyuanshen.common.core.domain.R; -import com.fuyuanshen.common.core.validate.AddGroup; import com.fuyuanshen.common.mybatis.core.page.PageQuery; import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.equipment.domain.Device; import com.fuyuanshen.equipment.domain.dto.AppDeviceBo; -import com.fuyuanshen.equipment.domain.dto.AppDeviceSendMsgBo; import com.fuyuanshen.equipment.domain.query.DeviceQueryCriteria; import com.fuyuanshen.equipment.domain.vo.AppDeviceVo; -import jakarta.validation.constraints.NotNull; +import com.fuyuanshen.web.service.device.DeviceBizService; import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; -import org.springframework.web.multipart.MultipartFile; import java.util.List; import java.util.Map; @@ -36,7 +28,7 @@ import java.util.Map; @RequestMapping("/app/device") public class AppDeviceController extends BaseController { - private final AppDeviceBizService appDeviceService; + private final DeviceBizService appDeviceService; /** @@ -84,94 +76,18 @@ public class AppDeviceController extends BaseController { return R.ok("重命名成功!!!"); } - /** - * 获取设备详细信息 - * - * @param id 主键 - */ - @GetMapping("/{id}") - public R getInfo(@NotNull(message = "主键不能为空") - @PathVariable Long id) { - return R.ok(appDeviceService.getInfo(id)); + + @GetMapping("/realTimeStatus") + public R> getRealTimeStatus(AppRealTimeStatusDto statusDto) { + Map status = appDeviceService.getRealTimeStatus(statusDto); + return R.ok(status); } /** - * 人员信息登记 + * 根据mac查询设备信息 */ - @PostMapping(value = "/registerPersonInfo") - public R registerPersonInfo(@Validated(AddGroup.class) @RequestBody AppPersonnelInfoBo bo) { - return toAjax(appDeviceService.registerPersonInfo(bo)); - } - - /** - * 发送信息 - */ - @PostMapping(value = "/sendMessage") - public R sendMessage(@RequestBody AppDeviceSendMsgBo bo) { - return toAjax(appDeviceService.sendMessage(bo)); - } - - /** - * 发送报警信息 - */ - @PostMapping(value = "/sendAlarmMessage") - public R sendAlarmMessage(@RequestBody AppDeviceSendMsgBo bo) { - return toAjax(appDeviceService.sendAlarmMessage(bo)); - } - - /** - * 上传设备logo图片 - */ - @PostMapping("/uploadLogo") - public R upload(@Validated @ModelAttribute AppDeviceLogoUploadDto bo) { - - MultipartFile file = bo.getFile(); - if(file.getSize()>1024*1024*2){ - return R.warn("图片不能大于2M"); - } - appDeviceService.uploadDeviceLogo(bo); - - return R.ok(); - } - - /** - * 灯光模式 - * 0(关灯),1(强光模式),2(弱光模式), 3(爆闪模式), 4(泛光模式) - */ - @PostMapping("/lightModeSettings") - public R lightModeSettings(@RequestBody DeviceInstructDto params) { - // params 转 JSONObject - appDeviceService.lightModeSettings(params); - return R.ok(); - } - - /** - * 灯光亮度设置 - * - */ - @PostMapping("/lightBrightnessSettings") - public R lightBrightnessSettings(@RequestBody DeviceInstructDto params) { - appDeviceService.lightBrightnessSettings(params); - return R.ok(); - } - - /** - * 激光模式设置 - * - */ - @PostMapping("/laserModeSettings") - public R laserModeSettings(@RequestBody DeviceInstructDto params) { - appDeviceService.laserModeSettings(params); - return R.ok(); - } - - /** - * 地图逆解析 - * - */ - @PostMapping("/mapReverseGeocoding") - public R mapReverseGeocoding(@RequestBody DeviceInstructDto params) { - String mapJson = appDeviceService.mapReverseGeocoding(params); - return R.ok(mapJson); + @GetMapping("/getDeviceInfoByDeviceMac") + public R getDeviceInfo(String deviceMac) { + return R.ok(appDeviceService.getDeviceInfo(deviceMac)); } } diff --git a/fys-admin/src/main/java/com/fuyuanshen/app/controller/AppDeviceShareController.java b/fys-admin/src/main/java/com/fuyuanshen/app/controller/AppDeviceShareController.java index 581c934d..613bb7ea 100644 --- a/fys-admin/src/main/java/com/fuyuanshen/app/controller/AppDeviceShareController.java +++ b/fys-admin/src/main/java/com/fuyuanshen/app/controller/AppDeviceShareController.java @@ -5,7 +5,6 @@ import cn.hutool.core.util.RandomUtil; import com.fuyuanshen.app.domain.bo.AppDeviceShareBo; import com.fuyuanshen.app.domain.vo.AppDeviceShareDetailVo; import com.fuyuanshen.app.domain.vo.AppDeviceShareVo; -import com.fuyuanshen.app.service.AppDeviceShareService; import com.fuyuanshen.app.service.IAppDeviceShareService; import com.fuyuanshen.common.core.constant.Constants; import com.fuyuanshen.common.core.domain.R; @@ -16,6 +15,7 @@ import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; import com.fuyuanshen.common.ratelimiter.annotation.RateLimiter; import com.fuyuanshen.common.redis.utils.RedisUtils; import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.web.service.DeviceShareService; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; @@ -45,14 +45,14 @@ public class AppDeviceShareController extends BaseController { private final IAppDeviceShareService deviceShareService; - private final AppDeviceShareService appDeviceShareService; + private final DeviceShareService appDeviceShareService; /** * 分享管理列表 */ @GetMapping("/deviceShareList") public TableDataInfo list(AppDeviceShareBo bo, PageQuery pageQuery) { - return deviceShareService.queryPageList(bo, pageQuery); + return appDeviceShareService.queryPageList(bo, pageQuery); } /** @@ -60,7 +60,7 @@ public class AppDeviceShareController extends BaseController { */ @GetMapping("/otherDeviceShareList") public TableDataInfo otherDeviceShareList(AppDeviceShareBo bo, PageQuery pageQuery) { - return deviceShareService.otherDeviceShareList(bo, pageQuery); + return appDeviceShareService.otherDeviceShareList(bo, pageQuery); } /** diff --git a/fys-admin/src/main/java/com/fuyuanshen/app/controller/device/AppDeviceBJQController.java b/fys-admin/src/main/java/com/fuyuanshen/app/controller/device/AppDeviceBJQController.java new file mode 100644 index 00000000..3cc71f8c --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/app/controller/device/AppDeviceBJQController.java @@ -0,0 +1,119 @@ +package com.fuyuanshen.app.controller.device; + +import com.fuyuanshen.app.domain.bo.AppPersonnelInfoBo; +import com.fuyuanshen.app.domain.dto.AppDeviceLogoUploadDto; +import com.fuyuanshen.app.domain.dto.DeviceInstructDto; +import com.fuyuanshen.app.domain.vo.AppDeviceDetailVo; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.core.validate.AddGroup; +import com.fuyuanshen.common.ratelimiter.annotation.FunctionAccessAnnotation; +import com.fuyuanshen.common.ratelimiter.annotation.FunctionAccessBatcAnnotation; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.equipment.domain.dto.AppDeviceSendMsgBo; +import com.fuyuanshen.web.service.device.DeviceBJQBizService; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +/** + * BJQ6170设备控制类 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/app/bjq/device") +public class AppDeviceBJQController extends BaseController { + + private final DeviceBJQBizService appDeviceService; + + /** + * 获取设备详细信息 + * + * @param id 主键 + */ + @GetMapping("/{id}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(appDeviceService.getInfo(id)); + } + + /** + * 人员信息登记 + */ + @PostMapping(value = "/registerPersonInfo") +// @FunctionAccessAnnotation("registerPersonInfo") + public R registerPersonInfo(@Validated(AddGroup.class) @RequestBody AppPersonnelInfoBo bo) { + return toAjax(appDeviceService.registerPersonInfo(bo)); + } + + /** + * 发送信息 + */ + @PostMapping(value = "/sendMessage") + @FunctionAccessBatcAnnotation(value = "sendMessage", timeOut = 30, batchMaxTimeOut = 40) + public R sendMessage(@RequestBody AppDeviceSendMsgBo bo) { + return toAjax(appDeviceService.sendMessage(bo)); + } + + /** + * 发送报警信息 + */ + @PostMapping(value = "/sendAlarmMessage") + @FunctionAccessBatcAnnotation(value = "sendAlarmMessage", timeOut = 5, batchMaxTimeOut = 10) + public R sendAlarmMessage(@RequestBody AppDeviceSendMsgBo bo) { + return toAjax(appDeviceService.sendAlarmMessage(bo)); + } + + /** + * 上传设备logo图片 + */ + @PostMapping("/uploadLogo") + @FunctionAccessAnnotation("uploadLogo") + public R upload(@Validated @ModelAttribute AppDeviceLogoUploadDto bo) { + + MultipartFile file = bo.getFile(); + if(file.getSize()>1024*1024*2){ + return R.warn("图片不能大于2M"); + } + appDeviceService.uploadDeviceLogo(bo); + + return R.ok(); + } + + /** + * 灯光模式 + * 0(关灯),1(强光模式),2(弱光模式), 3(爆闪模式), 4(泛光模式) + */ +// @FunctionAccessAnnotation("lightModeSettings") + @PostMapping("/lightModeSettings") + public R lightModeSettings(@RequestBody DeviceInstructDto params) { + // params 转 JSONObject + appDeviceService.lightModeSettings(params); + return R.ok(); + } + + /** + * 灯光亮度设置 + * + */ +// @FunctionAccessAnnotation("lightBrightnessSettings") + @PostMapping("/lightBrightnessSettings") + public R lightBrightnessSettings(@RequestBody DeviceInstructDto params) { + appDeviceService.lightBrightnessSettings(params); + return R.ok(); + } + + /** + * 激光模式设置 + * + */ + @PostMapping("/laserModeSettings") +// @FunctionAccessAnnotation("laserModeSettings") + public R laserModeSettings(@RequestBody DeviceInstructDto params) { + appDeviceService.laserModeSettings(params); + return R.ok(); + } + +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/app/controller/device/AppDeviceXinghanController.java b/fys-admin/src/main/java/com/fuyuanshen/app/controller/device/AppDeviceXinghanController.java new file mode 100644 index 00000000..b4f53c51 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/app/controller/device/AppDeviceXinghanController.java @@ -0,0 +1,96 @@ +package com.fuyuanshen.app.controller.device; + +import com.fuyuanshen.app.domain.bo.AppPersonnelInfoBo; +import com.fuyuanshen.app.domain.dto.AppDeviceLogoUploadDto; +import com.fuyuanshen.app.domain.dto.DeviceInstructDto; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.core.validate.AddGroup; +import com.fuyuanshen.common.ratelimiter.annotation.FunctionAccessAnnotation; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.web.service.device.DeviceBJQBizService; +import com.fuyuanshen.web.service.device.DeviceXinghanBizService; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +/** + * HBY670设备控制类 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/app/xinghan/device") +public class AppDeviceXinghanController extends BaseController { + + private final DeviceXinghanBizService appDeviceService; + /** + * 人员信息登记 + */ + @PostMapping(value = "/registerPersonInfo") +// @FunctionAccessAnnotation("registerPersonInfo") + public R registerPersonInfo(@Validated(AddGroup.class) @RequestBody AppPersonnelInfoBo bo) { + return toAjax(appDeviceService.registerPersonInfo(bo)); + } + + /** + * 上传设备logo图片 + */ + @PostMapping("/uploadLogo") + @FunctionAccessAnnotation("uploadLogo") + public R upload(@Validated @ModelAttribute AppDeviceLogoUploadDto bo) { + + MultipartFile file = bo.getFile(); + if(file.getSize()>1024*1024*2){ + return R.warn("图片不能大于2M"); + } + appDeviceService.uploadDeviceLogo(bo); + + return R.ok(); + } + + /** + * 静电预警档位 + * 3,2,1,0,分别表示高档/中档/低挡/关闭 + */ + @PostMapping("/DetectGradeSettings") + public R DetectGradeSettings(@RequestBody DeviceInstructDto params) { + // params 转 JSONObject + appDeviceService.upDetectGradeSettings(params); + return R.ok(); + } + + /** + * 照明档位 + * 照明档位,2,1,0,分别表示弱光/强光/关闭 + */ + @PostMapping("/LightGradeSettings") + public R LightGradeSettings(@RequestBody DeviceInstructDto params) { + // params 转 JSONObject + appDeviceService.upLightGradeSettings(params); + return R.ok(); + } + + /** + * SOS档位 + * SOS档位,2,1,0, 分别表示红蓝模式/爆闪模式/关闭 + */ + @PostMapping("/SOSGradeSettings") + public R SOSGradeSettings(@RequestBody DeviceInstructDto params) { + // params 转 JSONObject + appDeviceService.upSOSGradeSettings(params); + return R.ok(); + } + + /** + * 静止报警状态 + * 静止报警状态,0-未静止报警,1-正在静止报警。 + */ + @PostMapping("/ShakeBitSettings") + public R ShakeBitSettings(@RequestBody DeviceInstructDto params) { + // params 转 JSONObject + appDeviceService.upShakeBitSettings(params); + return R.ok(); + } + +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/app/controller/device/TestController.java b/fys-admin/src/main/java/com/fuyuanshen/app/controller/device/TestController.java new file mode 100644 index 00000000..a9afd170 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/app/controller/device/TestController.java @@ -0,0 +1,47 @@ +package com.fuyuanshen.app.controller.device; + +import com.fuyuanshen.app.domain.bo.AppPersonnelInfoBo; +import com.fuyuanshen.app.domain.dto.AppDeviceLogoUploadDto; +import com.fuyuanshen.app.domain.dto.DeviceInstructDto; +import com.fuyuanshen.app.domain.vo.AppDeviceDetailVo; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.core.validate.AddGroup; +import com.fuyuanshen.common.ratelimiter.annotation.FunctionAccessAnnotation; +import com.fuyuanshen.common.ratelimiter.annotation.FunctionAccessBatcAnnotation; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.equipment.domain.dto.AppDeviceSendMsgBo; +import com.fuyuanshen.web.service.device.DeviceBJQBizService; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +/** + * BJQ6170设备控制类 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/test") +public class TestController extends BaseController { + + private final DeviceBJQBizService appDeviceService; + + /** + * 上传设备logo图片 + */ + @PostMapping("/uploadLogo") + @FunctionAccessAnnotation("uploadLogo") + public R upload(@Validated @ModelAttribute AppDeviceLogoUploadDto bo) { + + MultipartFile file = bo.getFile(); + if(file.getSize()>1024*1024*2){ + return R.warn("图片不能大于2M"); + } + appDeviceService.uploadDeviceLogo2(bo); + + return R.ok(); + } + +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/app/domain/dto/AppDeviceLogoUploadDto.java b/fys-admin/src/main/java/com/fuyuanshen/app/domain/dto/AppDeviceLogoUploadDto.java index 76dc5e2c..1841dadb 100644 --- a/fys-admin/src/main/java/com/fuyuanshen/app/domain/dto/AppDeviceLogoUploadDto.java +++ b/fys-admin/src/main/java/com/fuyuanshen/app/domain/dto/AppDeviceLogoUploadDto.java @@ -8,9 +8,11 @@ public class AppDeviceLogoUploadDto { private Long deviceId; + private String deviceImei; /** * 文件 */ private MultipartFile file; + private Integer chunkSize; } diff --git a/fys-admin/src/main/java/com/fuyuanshen/app/domain/dto/AppRealTimeStatusDto.java b/fys-admin/src/main/java/com/fuyuanshen/app/domain/dto/AppRealTimeStatusDto.java new file mode 100644 index 00000000..01a13d3a --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/app/domain/dto/AppRealTimeStatusDto.java @@ -0,0 +1,30 @@ +package com.fuyuanshen.app.domain.dto; + +import lombok.Data; + +/** + * 设备实时状态 + */ +@Data +public class AppRealTimeStatusDto { + + /** + * 设备IMEI + */ + private String deviceImei; + + /** + * 设备类型 + */ + private String typeName; + + /** + * 功能类型 + */ + private String functionMode; + + /** + * 批次号 + */ + private String batchId; +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/app/service/AppDeviceShareService.java b/fys-admin/src/main/java/com/fuyuanshen/app/service/AppDeviceShareService.java deleted file mode 100644 index 996c97c8..00000000 --- a/fys-admin/src/main/java/com/fuyuanshen/app/service/AppDeviceShareService.java +++ /dev/null @@ -1,135 +0,0 @@ -package com.fuyuanshen.app.service; - -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; -import com.fuyuanshen.app.domain.AppDeviceShare; -import com.fuyuanshen.app.domain.AppPersonnelInfo; -import com.fuyuanshen.app.domain.bo.AppDeviceShareBo; -import com.fuyuanshen.app.domain.vo.AppDeviceShareDetailVo; -import com.fuyuanshen.app.domain.vo.AppDeviceShareVo; -import com.fuyuanshen.app.domain.vo.AppPersonnelInfoVo; -import com.fuyuanshen.app.mapper.AppDeviceShareMapper; -import com.fuyuanshen.app.mapper.AppPersonnelInfoMapper; -import com.fuyuanshen.common.core.constant.Constants; -import com.fuyuanshen.common.core.constant.GlobalConstants; -import com.fuyuanshen.common.core.exception.ServiceException; -import com.fuyuanshen.common.core.exception.user.CaptchaExpireException; -import com.fuyuanshen.common.core.utils.MessageUtils; -import com.fuyuanshen.common.core.utils.StringUtils; -import com.fuyuanshen.common.redis.utils.RedisUtils; -import com.fuyuanshen.common.satoken.utils.AppLoginHelper; -import com.fuyuanshen.equipment.domain.Device; -import com.fuyuanshen.equipment.domain.DeviceType; -import com.fuyuanshen.equipment.mapper.DeviceMapper; -import com.fuyuanshen.equipment.mapper.DeviceTypeMapper; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -import java.util.Arrays; -import java.util.Date; -import java.util.List; - - -@RequiredArgsConstructor -@Slf4j -@Service -public class AppDeviceShareService { - - private final AppDeviceShareMapper appDeviceShareMapper; - - private final DeviceMapper deviceMapper; - - private final DeviceTypeMapper deviceTypeMapper; - - private final AppPersonnelInfoMapper appPersonnelInfoMapper; - - public AppDeviceShareDetailVo getInfo(Long id) { - - LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(AppDeviceShare::getDeviceId, id); - List appDeviceShareVos = appDeviceShareMapper.selectVoList(queryWrapper); - if(appDeviceShareVos==null || appDeviceShareVos.isEmpty()){ - return null; - } - - AppDeviceShareVo shareVo = appDeviceShareVos.get(0); - AppDeviceShareDetailVo shareDetailVo = new AppDeviceShareDetailVo(); - shareDetailVo.setId(shareVo.getId()); - shareDetailVo.setDeviceId(shareVo.getDeviceId()); - shareDetailVo.setPhonenumber(shareVo.getPhonenumber()); - shareDetailVo.setPermission(shareVo.getPermission()); - - Device device = deviceMapper.selectById(shareVo.getDeviceId()); - shareDetailVo.setDeviceName(device.getDeviceName()); - shareDetailVo.setDeviceImei(device.getDeviceImei()); - shareDetailVo.setDeviceMac(device.getDeviceMac()); - - DeviceType deviceType = deviceTypeMapper.selectById(device.getDeviceType()); - if(deviceType!=null){ - shareDetailVo.setCommunicationMode(Integer.valueOf(deviceType.getCommunicationMode())); - } - shareDetailVo.setDevicePic(device.getDevicePic()); - shareDetailVo.setTypeName(deviceType.getTypeName()); - shareDetailVo.setBluetoothName(device.getBluetoothName()); - shareDetailVo.setDeviceStatus(device.getDeviceStatus()); - shareDetailVo.setSendMsg(device.getSendMsg()); - - LambdaQueryWrapper qw = new LambdaQueryWrapper<>(); - qw.eq(AppPersonnelInfo::getDeviceId, device.getId()); - List appPersonnelInfoVos = appPersonnelInfoMapper.selectVoList(qw); - if(appPersonnelInfoVos!=null && !appPersonnelInfoVos.isEmpty()){ - shareDetailVo.setPersonnelInfo(appPersonnelInfoVos.get(0)); - } - - return shareDetailVo; - } - /** - * 校验短信验证码 - */ - private boolean validateSmsCode(String tenantId, String phonenumber, String smsCode) { - String code = RedisUtils.getCacheObject(GlobalConstants.DEVICE_SHARE_CODES_KEY + phonenumber); - if (StringUtils.isBlank(code)) { - throw new ServiceException("验证码失效"); - } - return code.equals(smsCode); - } - public int deviceShare(AppDeviceShareBo bo) { - boolean flag = validateSmsCode(AppLoginHelper.getTenantId(), bo.getPhonenumber(), bo.getSmsCode()); - if(!flag){ - throw new ServiceException("验证码错误"); - } - - Device device = deviceMapper.selectById(bo.getDeviceId()); - if(device==null){ - throw new ServiceException("设备不存在"); - } - Long userId = AppLoginHelper.getUserId(); - LambdaQueryWrapper lqw = new LambdaQueryWrapper<>(); - lqw.eq(AppDeviceShare::getDeviceId, bo.getDeviceId()); - lqw.eq(AppDeviceShare::getPhonenumber, bo.getPhonenumber()); - Long count = appDeviceShareMapper.selectCount(lqw); - if(count>0){ - - UpdateWrapper uw = new UpdateWrapper<>(); - uw.eq("device_id", bo.getDeviceId()); - uw.eq("phonenumber", bo.getPhonenumber()); - uw.set("permission", bo.getPermission()); - uw.set("update_by", userId); - uw.set("update_time", new Date()); - - return appDeviceShareMapper.update(uw); - }else { - AppDeviceShare appDeviceShare = new AppDeviceShare(); - appDeviceShare.setDeviceId(bo.getDeviceId()); - appDeviceShare.setPhonenumber(bo.getPhonenumber()); - appDeviceShare.setPermission(bo.getPermission()); - appDeviceShare.setCreateBy(userId); - return appDeviceShareMapper.insert(appDeviceShare); - } - } - - public int remove(Long[] ids) { - return appDeviceShareMapper.deleteByIds(Arrays.asList(ids)); - } -} diff --git a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/base/MqttRuleEngine.java b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/base/MqttRuleEngine.java index 66ea4dc0..b27d57aa 100644 --- a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/base/MqttRuleEngine.java +++ b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/base/MqttRuleEngine.java @@ -1,5 +1,9 @@ package com.fuyuanshen.global.mqtt.base; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.core.task.TaskExecutor; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.stereotype.Component; import java.util.Comparator; @@ -11,7 +15,10 @@ import java.util.List; */ @Component public class MqttRuleEngine { - + + @Autowired + @Qualifier("threadPoolTaskExecutor") + private ThreadPoolTaskExecutor threadPoolTaskExecutor; private final LinkedHashMap rulesMap = new LinkedHashMap<>(); public MqttRuleEngine(List rules) { @@ -30,7 +37,7 @@ public class MqttRuleEngine { int commandType = context.getCommandType(); MqttMessageRule mqttMessageRule = rulesMap.get("Light_"+commandType); if (mqttMessageRule != null) { - mqttMessageRule.execute(context); + threadPoolTaskExecutor.execute(() -> mqttMessageRule.execute(context)); return true; } diff --git a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/base/MqttXinghanCommandType.java b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/base/MqttXinghanCommandType.java new file mode 100644 index 00000000..99e7bc78 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/base/MqttXinghanCommandType.java @@ -0,0 +1,65 @@ +package com.fuyuanshen.global.mqtt.base; + +import cn.hutool.core.lang.Dict; + +import java.util.Map; +import java.util.LinkedHashMap; +import java.util.Collections; +import org.springframework.stereotype.Component; + + +@Component +public final class MqttXinghanCommandType { + private MqttXinghanCommandType() {} + + public enum XinghanCommandTypeEnum { + /** + * 星汉设备主动上报数据 + */ + GRADE_INFO(101), + /** + * 星汉开机LOGO + */ + PIC_TRANS(102), + /** + * 星汉设备发送消息 (XingHan send msg) + */ + TEX_TRANS(103), + BREAK_NEWS(104), + UNKNOWN(0); + + private final int value; + XinghanCommandTypeEnum(int value) { this.value = value; } + public int getValue() { return value; } + } + + private static final Map KEY_TO_TYPE; + static { + LinkedHashMap map = new LinkedHashMap<>(); + map.put("sta_DetectGrade", XinghanCommandTypeEnum.GRADE_INFO); + map.put("sta_PowerTime", XinghanCommandTypeEnum.GRADE_INFO); + map.put("sta_longitude", XinghanCommandTypeEnum.GRADE_INFO); + map.put("sta_latitude", XinghanCommandTypeEnum.GRADE_INFO); + map.put("sta_PicTrans", XinghanCommandTypeEnum.PIC_TRANS); + map.put("sta_TexTrans", XinghanCommandTypeEnum.TEX_TRANS); + map.put("sta_BreakNews", XinghanCommandTypeEnum.BREAK_NEWS); + KEY_TO_TYPE = Collections.unmodifiableMap(map); + } + + public static int computeVirtualCommandType(Dict payloadDict) { + if (payloadDict == null) { + return XinghanCommandTypeEnum.UNKNOWN.getValue(); + } + try { + for (String key : KEY_TO_TYPE.keySet()) { + if (payloadDict.containsKey(key)) { + return KEY_TO_TYPE.get(key).getValue(); + } + } + } catch (Exception ex) { + return XinghanCommandTypeEnum.UNKNOWN.getValue(); + } + + return XinghanCommandTypeEnum.UNKNOWN.getValue(); + } +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/base/MqttXinghanJson.java b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/base/MqttXinghanJson.java new file mode 100644 index 00000000..2d49b468 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/base/MqttXinghanJson.java @@ -0,0 +1,65 @@ +package com.fuyuanshen.global.mqtt.base; + +import lombok.Data; +import com.fasterxml.jackson.annotation.JsonProperty; + +@Data +public class MqttXinghanJson { + + /** + * 第一键值对,静电预警档位:3,2,1,0,分别表示高档/中档/低挡/关闭. + */ + @JsonProperty("sta_DetectGrade") + private Integer staDetectGrade; + /** + * 第二键值对,照明档位,2,1,0,分别表示弱光/强光/关闭 + */ + @JsonProperty("sta_LightGrade") + private Integer staLightGrade; + /** + * 第三键值对,SOS档位,2,1,0, 分别表示红蓝模式/爆闪模式/关闭 + */ + @JsonProperty("sta_SOSGrade") + public Integer staSOSGrade; + /** + * 第四键值对,剩余照明时间,0-5999,单位分钟。 + */ + @JsonProperty("sta_PowerTime") + public Integer staPowerTime; + /** + * 第五键值对,剩余电量百分比,0-100 + */ + @JsonProperty("sta_PowerPercent") + public Integer staPowerPercent; + /** + * 第六键值对, 近电预警级别, 0-无预警,1-弱预警,2-中预警,3-强预警,4-非常强预警。 + */ + @JsonProperty("sta_DetectResult") + public Integer staDetectResult; + /** + * 第七键值对, 静止报警状态,0-未静止报警,1-正在静止报警。 + */ + @JsonProperty("staShakeBit") + public Integer sta_ShakeBit; + /** + * 第八键值对, 4G信号强度,0-32,数值越大,信号越强。 + */ + @JsonProperty("sta_4gSinal") + public Integer sta4gSinal; + /** + * 第九键值对,IMIE卡号 + */ + @JsonProperty("sta_imei") + public String staimei; + /** + * 第十键值对,经度 + */ + @JsonProperty("sta_longitude") + public String stalongitude; + /** + * 第十一键值对,纬度 + */ + @JsonProperty("sta_latitude") + public String stalatitude; + +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/config/MqttGateway.java b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/config/MqttGateway.java index a1b8eb24..822e5bf4 100644 --- a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/config/MqttGateway.java +++ b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/config/MqttGateway.java @@ -4,12 +4,6 @@ import org.springframework.integration.annotation.MessagingGateway; import org.springframework.integration.mqtt.support.MqttHeaders; import org.springframework.messaging.handler.annotation.Header; -/** - * @Author: HarryLin - * @Date: 2025/3/20 17:06 - * @Company: 北京红山信息科技研究院有限公司 - * @Email: linyun@***.com.cn - **/ @MessagingGateway(defaultRequestChannel = "mqttOutboundChannel") public interface MqttGateway { public abstract void sendMsgToMqtt(@Header(value = MqttHeaders.TOPIC) String topic, String payload); diff --git a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/constants/DeviceRedisKeyConstants.java b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/constants/DeviceRedisKeyConstants.java index d92ec38f..080debc7 100644 --- a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/constants/DeviceRedisKeyConstants.java +++ b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/constants/DeviceRedisKeyConstants.java @@ -1,12 +1,50 @@ package com.fuyuanshen.global.mqtt.constants; public class DeviceRedisKeyConstants { - // 将设备上报状态 - public static final String DEVICE_STATUS_KEY_PREFIX = "device:status:"; + public static final String DEVICE_KEY_PREFIX = "device:"; + // 设备上报状态 + public static final String DEVICE_STATUS_KEY_PREFIX = ":status"; // 在线状态 - public static final String DEVICE_ONLINE_STATUS_KEY_PREFIX = "device:onlineStatus:"; - // 将设备状态信息存储到Redis中 - public static final String DEVICE_LOCATION_KEY_PREFIX = "device:location:"; + public static final String DEVICE_ONLINE_STATUS_KEY_PREFIX = ":onlineStatus"; + // 设备状态信息存储到Redis中 + public static final String DEVICE_LOCATION_KEY_PREFIX = ":location"; // 存储到一个列表中,保留历史位置信息 - public static final String DEVICE_LOCATION_HISTORY_KEY_PREFIX = "device:location:history:"; + public static final String DEVICE_LOCATION_HISTORY_KEY_PREFIX = ":location:history"; + + // 存储设备活跃上报信息 + public static final String DEVICE_ACTIVE_REPORTING_KEY_PREFIX = ":activeReporting"; + + // 存储设备人员信息 + public static final String DEVICE_PERSONNEL_INFO_KEY_PREFIX = ":personnelInfo"; + + // 存储设备发送消息 + public static final String DEVICE_SEND_MESSAGE_KEY_PREFIX = ":sendMessage"; + + // 存储设备启动logo + public static final String DEVICE_BOOT_LOGO_KEY_PREFIX = ":bootLogo"; + + /** + * 灯模式 + */ + public static final String DEVICE_LIGHT_MODE_KEY_PREFIX = ":lightMode"; + + /** + * 亮度模式 + */ + public static final String DEVICE_LIGHT_BRIGHTNESS_KEY_PREFIX = ":lightBrightness"; + + /** + * 激光模式 + */ + public static final String DEVICE_LASER_MODE_KEY_PREFIX = ":laserMode"; + + /** + * 地图逆地理编码 + */ + public static final String DEVICE_MAP_REVERSE_GEOCODING_KEY_PREFIX = ":mapReverseGeocoding"; + + /** + * 告警 + */ + public static final String DEVICE_ALARM_KEY_PREFIX = ":alarm"; } diff --git a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/constants/LightingCommandTypeConstants.java b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/constants/LightingCommandTypeConstants.java index c0f1ee75..de4e6b3f 100644 --- a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/constants/LightingCommandTypeConstants.java +++ b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/constants/LightingCommandTypeConstants.java @@ -35,6 +35,12 @@ public class LightingCommandTypeConstants { * 设备发送消息 */ public static final String SEND_MESSAGE = "Light_6"; + + + /** + * 报警模式 + */ + public static final String ALARM_MESSAGE = "Light_7"; /** * 定位数据 (Location Data) diff --git a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/constants/XingHanCommandTypeConstants.java b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/constants/XingHanCommandTypeConstants.java new file mode 100644 index 00000000..0b6f3d7c --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/constants/XingHanCommandTypeConstants.java @@ -0,0 +1,20 @@ +package com.fuyuanshen.global.mqtt.constants; + +public class XingHanCommandTypeConstants { + /** + * 星汉设备主动上报数据 (XingHan Device Data) + */ + public static final String XingHan_DEVICE_DATA = "Light_101"; + /** + * 星汉开机LOGO (XingHan Boot Logo) + */ + public static final String XingHan_BOOT_LOGO = "Light_102"; + /** + * 星汉设备发送消息 (XingHan send msg) + */ + public static final String XingHan_ESEND_MSG = "Light_103"; + /** + * 星汉设备发送紧急通知 (XingHan break news) + */ + public static final String XingHan_BREAK_NEWS = "Light_104"; +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/listener/RedisKeyExpirationListener.java b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/listener/RedisKeyExpirationListener.java new file mode 100644 index 00000000..86e669a0 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/listener/RedisKeyExpirationListener.java @@ -0,0 +1,38 @@ +package com.fuyuanshen.global.mqtt.listener; + +import com.fuyuanshen.common.redis.utils.RedisUtils; +import com.fuyuanshen.global.mqtt.listener.domain.FunctionAccessStatus; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.connection.Message; +import org.springframework.data.redis.connection.MessageListener; +import org.springframework.stereotype.Component; + +import java.time.Duration; +import java.util.HashMap; +import java.util.Map; + +import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_KEY; +import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_TIMEOUT_KEY; + +@Component +@Slf4j +public class RedisKeyExpirationListener implements MessageListener { + + @Override + public void onMessage(Message message, byte[] pattern) { + String expiredKey = new String(message.getBody()); + + if (expiredKey.startsWith(FUNCTION_ACCESS_KEY)) { + String element = expiredKey.substring(FUNCTION_ACCESS_KEY.length()); + handleFunctionAccessExpired(element); + } + } + + /** + * 访问key过期事件 + * @param element 批次ID + */ + private void handleFunctionAccessExpired(String element) { + RedisUtils.setCacheObject(FUNCTION_ACCESS_TIMEOUT_KEY + element, FunctionAccessStatus.TIMEOUT.getCode(), Duration.ofSeconds(30L)); + } +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/listener/config/RedisListenerConfig.java b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/listener/config/RedisListenerConfig.java new file mode 100644 index 00000000..e6cea5b4 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/listener/config/RedisListenerConfig.java @@ -0,0 +1,25 @@ +package com.fuyuanshen.global.mqtt.listener.config; + +import com.fuyuanshen.global.mqtt.listener.RedisKeyExpirationListener; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.listener.PatternTopic; +import org.springframework.data.redis.listener.RedisMessageListenerContainer; + +import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_KEY; + +@Configuration +public class RedisListenerConfig { + + @Bean + RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory connectionFactory, + RedisKeyExpirationListener redisKeyExpirationListener) { + RedisMessageListenerContainer container = new RedisMessageListenerContainer(); + container.setConnectionFactory(connectionFactory); + + // 监听过期事件 + container.addMessageListener(redisKeyExpirationListener, new PatternTopic("__keyevent@*__:expired")); + return container; + } +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/listener/domain/FunctionAccessResult.java b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/listener/domain/FunctionAccessResult.java new file mode 100644 index 00000000..8e609624 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/listener/domain/FunctionAccessResult.java @@ -0,0 +1,96 @@ +package com.fuyuanshen.global.mqtt.listener.domain; + +import lombok.Data; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; + +/** + * 功能访问状态对象 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class FunctionAccessResult { + + /** + * 状态码 + */ + private String status; + + /** + * 消息描述 + */ + private String message; + + /** + * 时间戳 + */ + private Long timestamp; + + /** + * 设备IMEI(可选) + */ + private String deviceImei; + + /** + * 批次ID(可选) + */ + private String batchId; + + public FunctionAccessResult(String status, String message) { + this.status = status; + this.message = message; + this.timestamp = System.currentTimeMillis(); + } + + /** + * 创建失败状态对象 + * @param message 消息 + * @return FunctionAccessResult + */ + public static FunctionAccessResult failed(String message) { + return new FunctionAccessResult("FAILED", message); + } + + /** + * 创建成功状态对象 + * @param message 消息 + * @return FunctionAccessResult + */ + public static FunctionAccessResult ok(String message) { + return new FunctionAccessResult("OK", message); + } + + /** + * 创建超时状态对象 + * @param message 消息 + * @return FunctionAccessResult + */ + public static FunctionAccessResult timeout(String message) { + return new FunctionAccessResult("TIMEOUT", message); + } + + /** + * 判断是否为失败状态 + * @return boolean + */ + public boolean isFailed() { + return "FAILED".equals(this.status); + } + + /** + * 判断是否为成功状态 + * @return boolean + */ + public boolean isOk() { + return "OK".equals(this.status); + } + + /** + * 判断是否为超时状态 + * @return boolean + */ + public boolean isTimeout() { + return "TIMEOUT".equals(this.status); + } +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/listener/domain/FunctionAccessStatus.java b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/listener/domain/FunctionAccessStatus.java new file mode 100644 index 00000000..33596841 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/listener/domain/FunctionAccessStatus.java @@ -0,0 +1,62 @@ +package com.fuyuanshen.global.mqtt.listener.domain; + +/** + * 功能访问状态枚举 + */ +public enum FunctionAccessStatus { + + /** + * 失败状态 + */ + FAILED("FAILED", "失败"), + + /** + * 成功状态 + */ + OK("OK", "成功"), + + /** + * 激活中状态 + */ + ACTIVE("ACTIVE", "处理中"), + + /** + * 超时状态 + */ + TIMEOUT("TIMEOUT", "超时"); + + private final String code; + private final String description; + + FunctionAccessStatus(String code, String description) { + this.code = code; + this.description = description; + } + + public String getCode() { + return code; + } + + public String getDescription() { + return description; + } + + /** + * 根据代码获取状态枚举 + * @param code 状态代码 + * @return 对应的状态枚举 + */ + public static FunctionAccessStatus fromCode(String code) { + for (FunctionAccessStatus status : FunctionAccessStatus.values()) { + if (status.getCode().equals(code)) { + return status; + } + } + throw new IllegalArgumentException("未知的状态代码: " + code); + } + + @Override + public String toString() { + return code; + } +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/publish/MqttMessageSender.java b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/publish/MqttMessageSender.java index dcb429cd..bb001248 100644 --- a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/publish/MqttMessageSender.java +++ b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/publish/MqttMessageSender.java @@ -6,12 +6,7 @@ import org.springframework.integration.mqtt.support.MqttHeaders; import org.springframework.messaging.handler.annotation.Header; import org.springframework.stereotype.Service; -/** - * @Author: HarryLin - * @Date: 2025/3/20 16:16 - * @Company: 北京红山信息科技研究院有限公司 - * @Email: linyun@***.com.cn - **/ + @Service public class MqttMessageSender { @Autowired diff --git a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/receiver/ReceiverMessageHandler.java b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/receiver/ReceiverMessageHandler.java index 3cd36af5..4259cd18 100644 --- a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/receiver/ReceiverMessageHandler.java +++ b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/receiver/ReceiverMessageHandler.java @@ -8,6 +8,7 @@ import com.fuyuanshen.common.json.utils.JsonUtils; import com.fuyuanshen.common.redis.utils.RedisUtils; import com.fuyuanshen.global.mqtt.base.MqttRuleContext; import com.fuyuanshen.global.mqtt.base.MqttRuleEngine; +import com.fuyuanshen.global.mqtt.base.MqttXinghanCommandType; import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -20,6 +21,8 @@ import org.springframework.stereotype.Service; import java.time.Duration; import java.util.Objects; +import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX; + @Service @Slf4j public class ReceiverMessageHandler implements MessageHandler { @@ -46,8 +49,8 @@ public class ReceiverMessageHandler implements MessageHandler { String deviceImei = subStr[1]; if(StringUtils.isNotBlank(deviceImei)){ //在线状态 - String deviceOnlineStatusRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+ DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX + deviceImei; - RedisUtils.setCacheObject(deviceOnlineStatusRedisKey, "1", Duration.ofSeconds(60*15)); + String deviceOnlineStatusRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX+ deviceImei + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX ; + RedisUtils.setCacheObject(deviceOnlineStatusRedisKey, "1", Duration.ofSeconds(62)); } String state = payloadDict.getStr("state"); @@ -67,5 +70,19 @@ public class ReceiverMessageHandler implements MessageHandler { log.warn("未找到匹配的规则来处理命令类型: {}", val1); } } + + /* ===== 追加:根据报文内容识别格式并统一解析 ===== */ + int intType = MqttXinghanCommandType.computeVirtualCommandType(payloadDict); + if (intType > 0) { + MqttRuleContext newCtx = new MqttRuleContext(); + newCtx.setCommandType((byte) intType); + newCtx.setDeviceImei(deviceImei); + newCtx.setPayloadDict(payloadDict); + + boolean ok = ruleEngine.executeRule(newCtx); + if (!ok) { + log.warn("新规则引擎未命中, imei={}", deviceImei); + } + } } } diff --git a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/ActiveReportingDeviceDataRule.java b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/bjq/BjqActiveReportingDeviceDataRule.java similarity index 85% rename from fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/ActiveReportingDeviceDataRule.java rename to fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/bjq/BjqActiveReportingDeviceDataRule.java index 104bd699..bdc82373 100644 --- a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/ActiveReportingDeviceDataRule.java +++ b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/bjq/BjqActiveReportingDeviceDataRule.java @@ -1,13 +1,13 @@ -package com.fuyuanshen.global.mqtt.rule; +package com.fuyuanshen.global.mqtt.rule.bjq; import com.fuyuanshen.common.core.constant.GlobalConstants; import com.fuyuanshen.common.json.utils.JsonUtils; import com.fuyuanshen.common.redis.utils.RedisUtils; import com.fuyuanshen.global.mqtt.base.MqttMessageRule; import com.fuyuanshen.global.mqtt.base.MqttRuleContext; -import com.fuyuanshen.global.mqtt.config.MqttGateway; import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants; import com.fuyuanshen.global.mqtt.constants.LightingCommandTypeConstants; +import com.fuyuanshen.global.mqtt.listener.domain.FunctionAccessStatus; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -17,6 +17,9 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; +import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_KEY; +import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_STATUS_KEY_PREFIX; + /** * 主动上报设备数据命令处理 * "第1位为12表示设备主动上报设备硬件状态,第2为表示当时设备主灯档位,第3位表示当时激光灯档位,第4位电量百分比,第5位为充电状态(0没有充电,1正在充电,2为已充满) @@ -25,10 +28,7 @@ import java.util.concurrent.CompletableFuture; @Component @RequiredArgsConstructor @Slf4j -public class ActiveReportingDeviceDataRule implements MqttMessageRule { - - private final MqttGateway mqttGateway; - +public class BjqActiveReportingDeviceDataRule implements MqttMessageRule { @Override public String getCommandType() { @@ -47,16 +47,16 @@ public class ActiveReportingDeviceDataRule implements MqttMessageRule { String chargeState = convertArr[4].toString(); String batteryRemainingTime = convertArr[5].toString(); - // 异步发送设备状态和位置信息到Redis + // 发送设备状态和位置信息到Redis asyncSendDeviceDataToRedisWithFuture(context.getDeviceImei(), mainLightMode, laserLightMode, batteryPercentage, chargeState, batteryRemainingTime); } catch (Exception e) { - log.error("处理定位数据命令时出错", e); + log.error("处理上报数据命令时出错", e); } } /** - * 异步发送设备状态信息和位置信息到Redis(使用CompletableFuture) + * 发送设备状态信息和位置信息到Redis * * @param deviceImei 设备IMEI * @param mainLightMode 主灯档位 @@ -80,10 +80,10 @@ public class ActiveReportingDeviceDataRule implements MqttMessageRule { deviceInfo.put("timestamp", System.currentTimeMillis()); // 将设备状态信息存储到Redis中 - String deviceRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+DeviceRedisKeyConstants.DEVICE_STATUS_KEY_PREFIX + deviceImei; + String deviceRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+DeviceRedisKeyConstants.DEVICE_KEY_PREFIX + deviceImei + DEVICE_STATUS_KEY_PREFIX; String deviceInfoJson = JsonUtils.toJsonString(deviceInfo); - // 存储到Redis,设置过期时间(例如24小时) + // 存储到Redis RedisUtils.setCacheObject(deviceRedisKey, deviceInfoJson); log.info("设备状态信息已异步发送到Redis: device={}, mainLightMode={}, laserLightMode={}, batteryPercentage={}", diff --git a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/bjq/BjqAlarmRule.java b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/bjq/BjqAlarmRule.java new file mode 100644 index 00000000..759f15b9 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/bjq/BjqAlarmRule.java @@ -0,0 +1,56 @@ +package com.fuyuanshen.global.mqtt.rule.bjq; + +import com.fuyuanshen.common.core.constant.GlobalConstants; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.redis.utils.RedisUtils; +import com.fuyuanshen.global.mqtt.base.MqttMessageRule; +import com.fuyuanshen.global.mqtt.base.MqttRuleContext; +import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants; +import com.fuyuanshen.global.mqtt.constants.LightingCommandTypeConstants; +import com.fuyuanshen.global.mqtt.listener.domain.FunctionAccessStatus; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.time.Duration; +import java.util.concurrent.CompletableFuture; + +import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_KEY; +import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.*; + +/** + * 灯光模式订阅设备回传消息 + */ +@Component +@RequiredArgsConstructor +@Slf4j +public class BjqAlarmRule implements MqttMessageRule { + + @Override + public String getCommandType() { + return LightingCommandTypeConstants.ALARM_MESSAGE; + } + + @Override + public void execute(MqttRuleContext context) { + String functionAccess = FUNCTION_ACCESS_KEY + context.getDeviceImei(); + try { + Object[] convertArr = context.getConvertArr(); + + String convertValue = convertArr[1].toString(); + if(StringUtils.isNotBlank(convertValue)){ + // 将设备状态信息存储到Redis中 + String deviceRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+DeviceRedisKeyConstants.DEVICE_KEY_PREFIX + context.getDeviceImei() + DEVICE_ALARM_KEY_PREFIX; + + // 存储到Redis + RedisUtils.setCacheObject(deviceRedisKey, convertValue); + } + RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.OK.getCode(), Duration.ofSeconds(20)); + } catch (Exception e) { + log.error("处理告警命令时出错", e); + RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.FAILED.getCode(), Duration.ofSeconds(20)); + } + } + + +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/DeviceBootLogoRule.java b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/bjq/BjqBootLogoRule.java similarity index 66% rename from fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/DeviceBootLogoRule.java rename to fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/bjq/BjqBootLogoRule.java index af5bc109..2a95db74 100644 --- a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/DeviceBootLogoRule.java +++ b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/bjq/BjqBootLogoRule.java @@ -1,6 +1,5 @@ -package com.fuyuanshen.global.mqtt.rule; +package com.fuyuanshen.global.mqtt.rule.bjq; -import com.fuyuanshen.common.core.constant.GlobalConstants; import com.fuyuanshen.common.core.utils.ImageToCArrayConverter; import com.fuyuanshen.common.core.utils.StringUtils; import com.fuyuanshen.common.json.utils.JsonUtils; @@ -10,24 +9,29 @@ import com.fuyuanshen.global.mqtt.base.MqttRuleContext; import com.fuyuanshen.global.mqtt.config.MqttGateway; import com.fuyuanshen.global.mqtt.constants.LightingCommandTypeConstants; import com.fuyuanshen.global.mqtt.constants.MqttConstants; +import com.fuyuanshen.global.mqtt.listener.domain.FunctionAccessStatus; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; +import java.time.Duration; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_KEY; +import static com.fuyuanshen.common.core.constant.GlobalConstants.GLOBAL_REDIS_KEY; import static com.fuyuanshen.common.core.utils.ImageToCArrayConverter.convertHexToDecimal; +import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_BOOT_LOGO_KEY_PREFIX; +import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX; /** - * 人员信息命令处理 + * 上传开机图片命令处理 */ @Component @RequiredArgsConstructor @Slf4j -public class DeviceBootLogoRule implements MqttMessageRule { +public class BjqBootLogoRule implements MqttMessageRule { private final MqttGateway mqttGateway; @@ -38,21 +42,22 @@ public class DeviceBootLogoRule implements MqttMessageRule { @Override public void execute(MqttRuleContext context) { + String functionAccess = FUNCTION_ACCESS_KEY + context.getDeviceImei(); try { Byte val2 = (Byte) context.getConvertArr()[1]; if (val2 == 100) { + RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.OK.getCode(), Duration.ofSeconds(20)); return; } - String data = RedisUtils.getCacheObject(GlobalConstants.GLOBAL_REDIS_KEY+"app_logo_data:" + context.getDeviceImei()); + String data = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + context.getDeviceImei() +DEVICE_BOOT_LOGO_KEY_PREFIX); if (StringUtils.isEmpty(data)) { return; } byte[] arr = ImageToCArrayConverter.convertStringToByteArray(data); byte[] specificChunk = ImageToCArrayConverter.getChunk(arr, (val2 - 1), 512); - System.out.println("第" + val2 + "块数据大小: " + specificChunk.length + " 字节"); -// System.out.println("第" + val2 + "块数据: " + Arrays.toString(specificChunk)); + log.info("第{}块数据大小: {} 字节", val2, specificChunk.length); ArrayList intData = new ArrayList<>(); intData.add(3); @@ -70,8 +75,10 @@ public class DeviceBootLogoRule implements MqttMessageRule { log.info("发送开机LOGO点阵数据到设备消息=>topic:{},payload:{}", MqttConstants.GLOBAL_PUB_KEY + context.getDeviceImei(), JsonUtils.toJsonString(map)); + } catch (Exception e) { - log.error("处理人员信息命令时出错", e); + log.error("处理开机LOGO时出错", e); + RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.FAILED.getCode(), Duration.ofSeconds(20)); } } } diff --git a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/bjq/BjqLaserModeSettingsRule.java b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/bjq/BjqLaserModeSettingsRule.java new file mode 100644 index 00000000..b2bb3391 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/bjq/BjqLaserModeSettingsRule.java @@ -0,0 +1,80 @@ +package com.fuyuanshen.global.mqtt.rule.bjq; + +import com.fuyuanshen.common.core.constant.GlobalConstants; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.redis.utils.RedisUtils; +import com.fuyuanshen.global.mqtt.base.MqttMessageRule; +import com.fuyuanshen.global.mqtt.base.MqttRuleContext; +import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants; +import com.fuyuanshen.global.mqtt.constants.LightingCommandTypeConstants; +import com.fuyuanshen.global.mqtt.listener.domain.FunctionAccessStatus; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.time.Duration; + +import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_KEY; +import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX; +import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_LIGHT_MODE_KEY_PREFIX; + +/** + * 灯光模式订阅设备回传消息 + */ +@Component +@RequiredArgsConstructor +@Slf4j +public class BjqLaserModeSettingsRule implements MqttMessageRule { + + @Override + public String getCommandType() { + return LightingCommandTypeConstants.LASER_LIGHT; + } + + @Override + public void execute(MqttRuleContext context) { + String functionAccess = FUNCTION_ACCESS_KEY + context.getDeviceImei(); + try { + Object[] convertArr = context.getConvertArr(); + + String mainLightMode = convertArr[1].toString(); + if(StringUtils.isNotBlank(mainLightMode)){ + if("0".equals(mainLightMode)){ + String deviceOnlineStatusRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX+ context.getDeviceImei() + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX ; + RedisUtils.setCacheObject(deviceOnlineStatusRedisKey, "0", Duration.ofSeconds(60*15)); + } + // 发送设备状态和位置信息到Redis + syncSendDeviceDataToRedisWithFuture(context.getDeviceImei(),mainLightMode); + } + + RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.OK.getCode(), Duration.ofSeconds(30)); + } catch (Exception e) { + log.error("处理激光模式命令时出错", e); + RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.FAILED.getCode(), Duration.ofSeconds(30)); + } + } + + /** + * 发送设备状态信息和位置信息到Redis + * + * @param deviceImei 设备IMEI + */ + public void syncSendDeviceDataToRedisWithFuture(String deviceImei,Object convertValue) { +// CompletableFuture.runAsync(() -> { +// +// }); + try { + // 将设备状态信息存储到Redis中 + String deviceRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+DeviceRedisKeyConstants.DEVICE_KEY_PREFIX + deviceImei + DEVICE_LIGHT_MODE_KEY_PREFIX; + + // 存储到Redis + RedisUtils.setCacheObject(deviceRedisKey, convertValue.toString()); + + } catch (Exception e) { + log.error("异步发送设备信息到Redis时出错: device={}, error={}", deviceImei, e.getMessage(), e); + } + } + + + +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/bjq/BjqLightBrightnessRule.java b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/bjq/BjqLightBrightnessRule.java new file mode 100644 index 00000000..0fc6dd8e --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/bjq/BjqLightBrightnessRule.java @@ -0,0 +1,51 @@ +package com.fuyuanshen.global.mqtt.rule.bjq; + +import com.fuyuanshen.common.core.constant.GlobalConstants; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.redis.utils.RedisUtils; +import com.fuyuanshen.global.mqtt.base.MqttMessageRule; +import com.fuyuanshen.global.mqtt.base.MqttRuleContext; +import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants; +import com.fuyuanshen.global.mqtt.constants.LightingCommandTypeConstants; +import com.fuyuanshen.global.mqtt.listener.domain.FunctionAccessStatus; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.time.Duration; + +import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_KEY; +import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.*; + +/** + * 灯光模式订阅设备回传消息 + */ +@Component +@RequiredArgsConstructor +@Slf4j +public class BjqLightBrightnessRule implements MqttMessageRule { + + @Override + public String getCommandType() { + return LightingCommandTypeConstants.MAIN_LIGHT_BRIGHTNESS; + } + + @Override + public void execute(MqttRuleContext context) { + String functionAccess = FUNCTION_ACCESS_KEY + context.getDeviceImei(); + try { + Object[] convertArr = context.getConvertArr(); + + String convertValue = convertArr[1].toString(); + // 将设备状态信息存储到Redis中 + String deviceRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+DeviceRedisKeyConstants.DEVICE_KEY_PREFIX + context.getDeviceImei() + DEVICE_LIGHT_BRIGHTNESS_KEY_PREFIX; + + // 存储到Redis + RedisUtils.setCacheObject(deviceRedisKey, convertValue); + RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.OK.getCode(), Duration.ofSeconds(20)); + } catch (Exception e) { + log.error("处理灯光亮度命令时出错", e); + RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.FAILED.getCode(), Duration.ofSeconds(20)); + } + } +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/LocationDataRule.java b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/bjq/BjqLocationDataRule.java similarity index 89% rename from fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/LocationDataRule.java rename to fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/bjq/BjqLocationDataRule.java index 16dec3b5..0c3bda1f 100644 --- a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/LocationDataRule.java +++ b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/bjq/BjqLocationDataRule.java @@ -1,6 +1,5 @@ -package com.fuyuanshen.global.mqtt.rule; +package com.fuyuanshen.global.mqtt.rule.bjq; -import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; import com.fuyuanshen.common.core.constant.GlobalConstants; import com.fuyuanshen.common.core.utils.StringUtils; @@ -14,6 +13,7 @@ import com.fuyuanshen.global.mqtt.config.MqttGateway; import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants; import com.fuyuanshen.global.mqtt.constants.LightingCommandTypeConstants; import com.fuyuanshen.global.mqtt.constants.MqttConstants; +import com.fuyuanshen.global.mqtt.listener.domain.FunctionAccessStatus; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -25,13 +25,17 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; +import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_KEY; +import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX; +import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_LOCATION_KEY_PREFIX; + /** * 定位数据命令处理 */ @Component @RequiredArgsConstructor @Slf4j -public class LocationDataRule implements MqttMessageRule { +public class BjqLocationDataRule implements MqttMessageRule { private final MqttGateway mqttGateway; @@ -43,6 +47,7 @@ public class LocationDataRule implements MqttMessageRule { @Override public void execute(MqttRuleContext context) { + String functionAccess = FUNCTION_ACCESS_KEY + context.getDeviceImei(); try { Object[] convertArr = context.getConvertArr(); // Latitude, longitude @@ -57,8 +62,10 @@ public class LocationDataRule implements MqttMessageRule { log.info("发送定位数据到设备=>topic:{},payload:{}", MqttConstants.GLOBAL_PUB_KEY + context.getDeviceImei(), JsonUtils.toJsonString(map)); + RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.OK.getCode(), Duration.ofSeconds(20)); } catch (Exception e) { log.error("处理定位数据命令时出错", e); + RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.FAILED.getCode(), Duration.ofSeconds(20)); } } @@ -113,7 +120,7 @@ public class LocationDataRule implements MqttMessageRule { String[] latArr = latitude.split("\\."); String[] lonArr = longitude.split("\\."); // 将位置信息存储到Redis中 - String redisKey = GlobalConstants.GLOBAL_REDIS_KEY+DeviceRedisKeyConstants.DEVICE_LOCATION_KEY_PREFIX + deviceImei; + String redisKey = GlobalConstants.GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX+ deviceImei + DEVICE_LOCATION_KEY_PREFIX; String redisObj = RedisUtils.getCacheObject(redisKey); JSONObject jsonOBj = JSONObject.parseObject(redisObj); if(jsonOBj != null){ @@ -169,7 +176,7 @@ public class LocationDataRule implements MqttMessageRule { */ public void storeDeviceTrajectoryWithSortedSet(String deviceImei, String locationJson) { try { - String trajectoryKey = GlobalConstants.GLOBAL_REDIS_KEY+DeviceRedisKeyConstants.DEVICE_LOCATION_HISTORY_KEY_PREFIX + deviceImei; + String trajectoryKey = GlobalConstants.GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + deviceImei + DeviceRedisKeyConstants.DEVICE_LOCATION_HISTORY_KEY_PREFIX; // String trajectoryKey = "device:trajectory:zset:" + deviceImei; // String locationJson = JsonUtils.toJsonString(locationInfo); long timestamp = System.currentTimeMillis(); @@ -181,7 +188,7 @@ public class LocationDataRule implements MqttMessageRule { // RedisUtils.expire(trajectoryKey, Duration.ofDays(30)); // 清理30天前的数据(冗余保护) - long thirtyDaysAgo = System.currentTimeMillis() - (90L * 24 * 60 * 60 * 1000); + long thirtyDaysAgo = System.currentTimeMillis() - (7L * 24 * 60 * 60 * 1000); RedisUtils.zRemoveRangeByScore(trajectoryKey, 0, thirtyDaysAgo); } catch (Exception e) { log.error("存储设备轨迹到Redis(ZSet)失败: device={}, error={}", deviceImei, e.getMessage(), e); diff --git a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/bjq/BjqModeRule.java b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/bjq/BjqModeRule.java new file mode 100644 index 00000000..f16b77e4 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/bjq/BjqModeRule.java @@ -0,0 +1,84 @@ +package com.fuyuanshen.global.mqtt.rule.bjq; + +import com.fuyuanshen.common.core.constant.GlobalConstants; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.redis.utils.RedisUtils; +import com.fuyuanshen.global.mqtt.base.MqttMessageRule; +import com.fuyuanshen.global.mqtt.base.MqttRuleContext; +import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants; +import com.fuyuanshen.global.mqtt.constants.LightingCommandTypeConstants; +import com.fuyuanshen.global.mqtt.listener.domain.FunctionAccessStatus; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.time.Duration; +import java.util.concurrent.CompletableFuture; + +import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_KEY; +import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.*; + +/** + * 灯光模式订阅设备回传消息 + */ +@Component +@RequiredArgsConstructor +@Slf4j +public class BjqModeRule implements MqttMessageRule { + + @Override + public String getCommandType() { + return LightingCommandTypeConstants.LIGHT_MODE; + } + + @Override + public void execute(MqttRuleContext context) { + String functionAccess = FUNCTION_ACCESS_KEY + context.getDeviceImei(); + try { + Object[] convertArr = context.getConvertArr(); + + String mainLightMode = convertArr[1].toString(); + String batteryRemainingTime = convertArr[2].toString(); + if(StringUtils.isNotBlank(mainLightMode)){ + if("0".equals(mainLightMode)){ + String deviceOnlineStatusRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX+ context.getDeviceImei() + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX ; + RedisUtils.setCacheObject(deviceOnlineStatusRedisKey, "0", Duration.ofSeconds(60*15)); + } + // 发送设备状态和位置信息到Redis + syncSendDeviceDataToRedisWithFuture(context.getDeviceImei(),mainLightMode); + String deviceRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+DeviceRedisKeyConstants.DEVICE_KEY_PREFIX + context.getDeviceImei() + DEVICE_LIGHT_BRIGHTNESS_KEY_PREFIX; + + // 存储到Redis + RedisUtils.setCacheObject(deviceRedisKey, batteryRemainingTime); + } + RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.OK.getCode(), Duration.ofSeconds(20)); + } catch (Exception e) { + log.error("处理灯光模式命令时出错", e); + RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.FAILED.getCode(), Duration.ofSeconds(20)); + } + } + + /** + * 发送设备状态信息和位置信息到Redis + * + * @param deviceImei 设备IMEI + */ + public void syncSendDeviceDataToRedisWithFuture(String deviceImei,Object convertValue) { +// CompletableFuture.runAsync(() -> { +// +// }); + try { + // 将设备状态信息存储到Redis中 + String deviceRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+DeviceRedisKeyConstants.DEVICE_KEY_PREFIX + deviceImei + DEVICE_LIGHT_MODE_KEY_PREFIX; + + // 存储到Redis + RedisUtils.setCacheObject(deviceRedisKey, convertValue.toString()); + + } catch (Exception e) { + log.error("异步发送设备信息到Redis时出错: device={}, error={}", deviceImei, e.getMessage(), e); + } + } + + + +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/bjq/BjqPersonnelInfoDataRule.java b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/bjq/BjqPersonnelInfoDataRule.java new file mode 100644 index 00000000..2a1b6d65 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/bjq/BjqPersonnelInfoDataRule.java @@ -0,0 +1,58 @@ +package com.fuyuanshen.global.mqtt.rule.bjq; + +import com.alibaba.fastjson2.JSONObject; +import com.fuyuanshen.common.core.constant.GlobalConstants; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.json.utils.JsonUtils; +import com.fuyuanshen.common.redis.utils.RedisUtils; +import com.fuyuanshen.equipment.utils.map.GetAddressFromLatUtil; +import com.fuyuanshen.equipment.utils.map.LngLonUtil; +import com.fuyuanshen.global.mqtt.base.MqttMessageRule; +import com.fuyuanshen.global.mqtt.base.MqttRuleContext; +import com.fuyuanshen.global.mqtt.config.MqttGateway; +import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants; +import com.fuyuanshen.global.mqtt.constants.LightingCommandTypeConstants; +import com.fuyuanshen.global.mqtt.constants.MqttConstants; +import com.fuyuanshen.global.mqtt.listener.domain.FunctionAccessStatus; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_KEY; +import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.*; + +/** + * 定位数据命令处理 + */ +@Component +@RequiredArgsConstructor +@Slf4j +public class BjqPersonnelInfoDataRule implements MqttMessageRule { + + private final MqttGateway mqttGateway; + + + @Override + public String getCommandType() { + return LightingCommandTypeConstants.PERSONNEL_INFO; + } + + @Override + public void execute(MqttRuleContext context) { + String functionAccess = FUNCTION_ACCESS_KEY + context.getDeviceImei(); + try { + Object[] convertArr = context.getConvertArr(); + RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.OK.getCode(), Duration.ofSeconds(30)); + } catch (Exception e) { + log.error("处理定位数据命令时出错", e); + RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.FAILED.getCode(), Duration.ofSeconds(30)); + } + } +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/DeviceSendMessageRule.java b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/bjq/BjqSendMessageRule.java similarity index 69% rename from fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/DeviceSendMessageRule.java rename to fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/bjq/BjqSendMessageRule.java index fab1fdee..7dc50935 100644 --- a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/DeviceSendMessageRule.java +++ b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/bjq/BjqSendMessageRule.java @@ -1,6 +1,5 @@ -package com.fuyuanshen.global.mqtt.rule; +package com.fuyuanshen.global.mqtt.rule.bjq; -import com.fuyuanshen.common.core.constant.GlobalConstants; import com.fuyuanshen.common.core.utils.ImageToCArrayConverter; import com.fuyuanshen.common.core.utils.StringUtils; import com.fuyuanshen.common.json.utils.JsonUtils; @@ -10,15 +9,20 @@ import com.fuyuanshen.global.mqtt.base.MqttRuleContext; import com.fuyuanshen.global.mqtt.config.MqttGateway; import com.fuyuanshen.global.mqtt.constants.LightingCommandTypeConstants; import com.fuyuanshen.global.mqtt.constants.MqttConstants; +import com.fuyuanshen.global.mqtt.listener.domain.FunctionAccessStatus; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; +import java.time.Duration; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; +import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_KEY; +import static com.fuyuanshen.common.core.constant.GlobalConstants.GLOBAL_REDIS_KEY; import static com.fuyuanshen.common.core.utils.ImageToCArrayConverter.convertHexToDecimal; +import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX; /** * 人员信息命令处理 @@ -26,7 +30,7 @@ import static com.fuyuanshen.common.core.utils.ImageToCArrayConverter.convertHex @Component @RequiredArgsConstructor @Slf4j -public class DeviceSendMessageRule implements MqttMessageRule { +public class BjqSendMessageRule implements MqttMessageRule { private final MqttGateway mqttGateway; @@ -37,20 +41,22 @@ public class DeviceSendMessageRule implements MqttMessageRule { @Override public void execute(MqttRuleContext context) { + String functionAccess = FUNCTION_ACCESS_KEY + context.getDeviceImei(); try { Byte val2 = (Byte) context.getConvertArr()[1]; if (val2 == 100) { + RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.OK.getCode(), Duration.ofSeconds(20)); return; } - String data = RedisUtils.getCacheObject(GlobalConstants.GLOBAL_REDIS_KEY+"app_send_message_data:" + context.getDeviceImei()); + String data = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + context.getDeviceImei() + ":app_send_message_data"); if (StringUtils.isEmpty(data)) { return; } byte[] arr = ImageToCArrayConverter.convertStringToByteArray(data); byte[] specificChunk = ImageToCArrayConverter.getChunk(arr, (val2 - 1), 512); - System.out.println("第" + val2 + "块数据大小: " + specificChunk.length + " 字节"); + log.info("第{}块数据大小: {} 字节", val2, specificChunk.length); // System.out.println("第" + val2 + "块数据: " + Arrays.toString(specificChunk)); ArrayList intData = new ArrayList<>(); @@ -69,8 +75,10 @@ public class DeviceSendMessageRule implements MqttMessageRule { log.info("发送设备信息数据到设备消息=>topic:{},payload:{}", MqttConstants.GLOBAL_PUB_KEY + context.getDeviceImei(), JsonUtils.toJsonString(map)); + } catch (Exception e) { - log.error("处理人员信息命令时出错", e); + log.error("处理发送设备信息时出错", e); + RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.FAILED.getCode(), Duration.ofSeconds(20)); } } } diff --git a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/xinghan/XinghanBootLogoRule.java b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/xinghan/XinghanBootLogoRule.java new file mode 100644 index 00000000..96faf308 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/xinghan/XinghanBootLogoRule.java @@ -0,0 +1,143 @@ +package com.fuyuanshen.global.mqtt.rule.xinghan; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fuyuanshen.common.core.utils.ImageToCArrayConverter; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.json.utils.JsonUtils; +import com.fuyuanshen.common.redis.utils.RedisUtils; +import com.fuyuanshen.global.mqtt.base.MqttMessageRule; +import com.fuyuanshen.global.mqtt.base.MqttRuleContext; +import com.fuyuanshen.global.mqtt.config.MqttGateway; +import com.fuyuanshen.global.mqtt.constants.LightingCommandTypeConstants; +import com.fuyuanshen.global.mqtt.constants.MqttConstants; +import com.fuyuanshen.global.mqtt.constants.XingHanCommandTypeConstants; +import com.fuyuanshen.global.mqtt.listener.domain.FunctionAccessStatus; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.time.Duration; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.zip.CRC32; + +import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_KEY; +import static com.fuyuanshen.common.core.constant.GlobalConstants.GLOBAL_REDIS_KEY; +import static com.fuyuanshen.common.core.utils.ImageToCArrayConverter.convertHexToDecimal; +import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_BOOT_LOGO_KEY_PREFIX; +import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX; + +/** + * 星汉设备开机 LOGO 下发规则: + *

+ * 1. 设备上行 sta_PicTarns=great! => 仅标记成功
+ * 2. 设备上行 sta_PicTarns=数字 => 下发第 N 块数据(256B/块,带 CRC32) + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class XinghanBootLogoRule implements MqttMessageRule { + + private final MqttGateway mqttGateway; + private final ObjectMapper objectMapper; + + @Override + public String getCommandType() { + return XingHanCommandTypeConstants.XingHan_BOOT_LOGO; + } + + @Override + public void execute(MqttRuleContext ctx) { + final String functionAccessKey = FUNCTION_ACCESS_KEY + ctx.getDeviceImei(); + try { + MqttXinghanLogoJson payload = objectMapper.convertValue( + ctx.getPayloadDict(), MqttXinghanLogoJson.class); + + String respText = payload.getStaPicTrans(); + log.warn("设备上报LOGO:{}", respText); + + // 1. great! —— 成功标记 + if ("great!".equalsIgnoreCase(respText)) { + RedisUtils.setCacheObject(functionAccessKey, + FunctionAccessStatus.OK.getCode(), Duration.ofSeconds(20)); + log.info("设备 {} 开机 LOGO 写入成功", ctx.getDeviceImei()); + return; + } + + // 2. 数字 —— 下发数据块 + int blockIndex; + try { + blockIndex = Integer.parseInt(respText); + } catch (NumberFormatException ex) { + log.warn("设备 {} LOGO 上报非法块号:{}", ctx.getDeviceImei(), respText); + return; + } + String hexImage = RedisUtils.getCacheObject( + GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + ctx.getDeviceImei() + DEVICE_BOOT_LOGO_KEY_PREFIX); + if (StringUtils.isEmpty(hexImage)) { + return; + } + + byte[] fullBin = ImageToCArrayConverter.convertStringToByteArray(hexImage); + byte[] chunk = ImageToCArrayConverter.getChunk(fullBin, blockIndex - 1, CHUNK_SIZE); + log.info("设备 {} 第 {} 块数据长度: {} bytes", ctx.getDeviceImei(), blockIndex, chunk.length); + + // 组装下发数据 + ArrayList dataFrame = new ArrayList<>(); + dataFrame.add(blockIndex); // 块号 + ImageToCArrayConverter.buildArr(convertHexToDecimal(chunk), dataFrame); + dataFrame.addAll(crc32AsList(chunk)); // CRC32 + + Map pub = new HashMap<>(); + pub.put("ins_PicTrans", dataFrame); + + String topic = MqttConstants.GLOBAL_PUB_KEY + ctx.getDeviceImei(); + String json = JsonUtils.toJsonString(pub); + mqttGateway.sendMsgToMqtt(topic, 1, json); + + log.info("下发开机 LOGO 数据 => topic:{}, payload:{}", topic, json); + + } catch (Exception e) { + log.error("处理设备 {} 开机 LOGO 失败", ctx.getDeviceImei(), e); + RedisUtils.setCacheObject(functionAccessKey, + FunctionAccessStatus.FAILED.getCode(), Duration.ofSeconds(20)); + } + } + + /* ---------- 内部工具 ---------- */ + + private static final int CHUNK_SIZE = 256; + + private static ArrayList crc32AsList(byte[] data) { + CRC32 crc = new CRC32(); + crc.update(data); + byte[] crcBytes = ByteBuffer.allocate(4) + .order(ByteOrder.BIG_ENDIAN) + .putInt((int) crc.getValue()) + .array(); + ArrayList list = new ArrayList<>(4); + for (byte b : crcBytes) { + list.add(Byte.toUnsignedInt(b)); + } + return list; + } + + /* ---------- DTO ---------- */ + + @Data + private static class MqttXinghanLogoJson { + /** + * 设备上行: + * 数字 -> 请求对应块号 + * great! -> 写入成功 + */ + @JsonProperty("sta_PicTrans") + private String staPicTrans; + } +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/xinghan/XinghanDeviceDataRule.java b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/xinghan/XinghanDeviceDataRule.java new file mode 100644 index 00000000..e12c0665 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/xinghan/XinghanDeviceDataRule.java @@ -0,0 +1,212 @@ +package com.fuyuanshen.global.mqtt.rule.xinghan; + +import com.alibaba.fastjson2.JSONObject; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fuyuanshen.common.core.constant.GlobalConstants; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.json.utils.JsonUtils; +import com.fuyuanshen.common.redis.utils.RedisUtils; +import com.fuyuanshen.equipment.utils.map.GetAddressFromLatUtil; +import com.fuyuanshen.equipment.utils.map.LngLonUtil; +import com.fuyuanshen.global.mqtt.base.MqttMessageRule; +import com.fuyuanshen.global.mqtt.base.MqttRuleContext; +import com.fuyuanshen.global.mqtt.base.MqttXinghanJson; +import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants; +import com.fuyuanshen.global.mqtt.constants.LightingCommandTypeConstants; +import com.fuyuanshen.global.mqtt.constants.XingHanCommandTypeConstants; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_KEY; +import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.*; +import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX; + +/** + * 主动上报设备数据命令处理 + * 第一键值对,静电预警档位:3,2,1,0,分别表示高档/中档/低挡/关闭. + * 第二键值对,照明档位,2,1,0,分别表示弱光/强光/关闭 + * 第三键值对,SOS档位,2,1,0, 分别表示红蓝模式/爆闪模式/关闭 + * 第四键值对,剩余照明时间,0-5999,单位分钟。 + * 第五键值对, 剩余电量百分比,0-100。 + * 第六键值对, 近电预警级别, 0-无预警,1-弱预警,2-中预警,3-强预警,4-非常强预警。 + * 第七键值对, 静止报警状态,0-未静止报警,1-正在静止报警。 + * 第八键值对, 4G信号强度,0-32,数值越大,信号越强。 + * 第九键值对,IMIE卡号 + * 第十键值对,经度 + * 第十一键值对,纬度 + */ +@Component +@RequiredArgsConstructor +@Slf4j +public class XinghanDeviceDataRule implements MqttMessageRule { + + @Override + public String getCommandType() { + return XingHanCommandTypeConstants.XingHan_DEVICE_DATA; + } + + @Autowired + private ObjectMapper objectMapper; + + @Override + public void execute(MqttRuleContext context) { + try { + // Latitude, longitude + //主灯档位,激光灯档位,电量百分比,充电状态,电池剩余续航时间 + MqttXinghanJson deviceStatus = objectMapper.convertValue(context.getPayloadDict(), MqttXinghanJson.class); + + // 发送设备状态和位置信息到Redis + asyncSendDeviceDataToRedisWithFuture(context.getDeviceImei(),deviceStatus); + } catch (Exception e) { + log.error("处理上报数据命令时出错", e); + } + } + + /** + * 发送设备状态信息和位置信息到Redis + * + * @param deviceImei 设备IMEI + * @param deviceStatus 机器主动上报的状态信息 + */ + public void asyncSendDeviceDataToRedisWithFuture(String deviceImei, MqttXinghanJson deviceStatus) { + CompletableFuture.runAsync(() -> { + try { + + // 将设备状态信息存储到Redis中 + String deviceRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+ DeviceRedisKeyConstants.DEVICE_KEY_PREFIX + deviceImei + DEVICE_STATUS_KEY_PREFIX; + String deviceInfoJson = JsonUtils.toJsonString(deviceStatus); + // 存储到Redis + RedisUtils.setCacheObject(deviceRedisKey, deviceInfoJson); + + log.info("设备状态信息已异步发送到Redis: device={}, deviceInfoJson={}", + deviceImei, deviceInfoJson); + + //设备坐标缓存KEY + String functionAccess = FUNCTION_ACCESS_KEY + deviceImei; + // 异步发送经纬度到Redis + asyncSendLocationToRedisWithFuture(deviceImei, deviceStatus.getStalatitude(), deviceStatus.getStalongitude()); + + } catch (Exception e) { + log.error("异步发送设备信息到Redis时出错: device={}, error={}", deviceImei, e.getMessage(), e); + } + }); + } + + /** + * 异步发送位置信息到Redis(使用CompletableFuture) + * + * @param deviceImei 设备IMEI + * @param latitude 纬度 + * @param longitude 经度 + */ + public void asyncSendLocationToRedisWithFuture(String deviceImei, String latitude, String longitude) { + CompletableFuture.runAsync(() -> { + try { + if(StringUtils.isBlank(latitude) || StringUtils.isBlank(longitude)){ + return; + } + String[] latArr = latitude.split("\\."); + String[] lonArr = longitude.split("\\."); + // 将位置信息存储到Redis中 + String redisKey = GlobalConstants.GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX+ deviceImei + DEVICE_LOCATION_KEY_PREFIX; + String redisObj = RedisUtils.getCacheObject(redisKey); + JSONObject jsonOBj = JSONObject.parseObject(redisObj); + if(jsonOBj != null){ + String str1 = latArr[0] +"."+ latArr[1].substring(0,4); + String str2 = lonArr[0] +"."+ lonArr[1].substring(0,4); + + String cacheLatitude = jsonOBj.getString("wgs84_latitude"); + String cacheLongitude = jsonOBj.getString("wgs84_longitude"); + String[] latArr1 = cacheLatitude.split("\\."); + String[] lonArr1 = cacheLongitude.split("\\."); + + String cacheStr1 = latArr1[0] +"."+ latArr1[1].substring(0,4); + String cacheStr2 = lonArr1[0] +"."+ lonArr1[1].substring(0,4); + if(str1.equals(cacheStr1) && str2.equals(cacheStr2)){ + log.info("位置信息未发生变化: device={}, lat={}, lon={}", deviceImei, latitude, longitude); + return; + } + } + + // 构造位置信息对象 + Map locationInfo = new LinkedHashMap<>(); + double[] doubles = LngLonUtil.gps84_To_Gcj02(Double.parseDouble(latitude), Double.parseDouble(longitude)); + locationInfo.put("deviceImei", deviceImei); + locationInfo.put("latitude", doubles[0]); + locationInfo.put("longitude", doubles[1]); + locationInfo.put("wgs84_latitude", latitude); + locationInfo.put("wgs84_longitude", longitude); + String address = GetAddressFromLatUtil.getAdd(String.valueOf(doubles[1]), String.valueOf(doubles[0])); + locationInfo.put("address", address); + locationInfo.put("timestamp", System.currentTimeMillis()); + + + + String locationJson = JsonUtils.toJsonString(locationInfo); + + // 存储到Redis + RedisUtils.setCacheObject(redisKey, locationJson); + + // 存储到一个列表中,保留历史位置信息 +// String locationHistoryKey = GlobalConstants.GLOBAL_REDIS_KEY+DeviceRedisKeyConstants.DEVICE_LOCATION_HISTORY_KEY_PREFIX + deviceImei; +// RedisUtils.addCacheList(locationHistoryKey, locationJson); +// RedisUtils.expire(locationHistoryKey, Duration.ofDays(90)); + storeDeviceTrajectoryWithSortedSet(deviceImei, locationJson); + log.info("位置信息已异步发送到Redis: device={}, lat={}, lon={}", deviceImei, latitude, longitude); + } catch (Exception e) { + log.error("异步发送位置信息到Redis时出错: device={}, error={}", deviceImei, e.getMessage(), e); + } + }); + } + + /** + * 存储设备30天历史轨迹到Redis (使用Sorted Set) + */ + public void storeDeviceTrajectoryWithSortedSet(String deviceImei, String locationJson) { + try { + String trajectoryKey = GlobalConstants.GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + deviceImei + DeviceRedisKeyConstants.DEVICE_LOCATION_HISTORY_KEY_PREFIX; +// String trajectoryKey = "device:trajectory:zset:" + deviceImei; +// String locationJson = JsonUtils.toJsonString(locationInfo); + long timestamp = System.currentTimeMillis(); + + // 添加到Sorted Set,使用时间戳作为score + RedisUtils.zAdd(trajectoryKey, locationJson, timestamp); + +// // 设置30天过期时间 +// RedisUtils.expire(trajectoryKey, Duration.ofDays(30)); + + // 清理30天前的数据(冗余保护) + long thirtyDaysAgo = System.currentTimeMillis() - (7L * 24 * 60 * 60 * 1000); + RedisUtils.zRemoveRangeByScore(trajectoryKey, 0, thirtyDaysAgo); + } catch (Exception e) { + log.error("存储设备轨迹到Redis(ZSet)失败: device={}, error={}", deviceImei, e.getMessage(), e); + } + } + + private Map buildLocationDataMap(String latitude, String longitude) { + String[] latArr = latitude.split("\\."); + String[] lonArr = longitude.split("\\."); + + ArrayList intData = new ArrayList<>(); + intData.add(11); + intData.add(Integer.parseInt(latArr[0])); + String str1 = latArr[1]; + intData.add(Integer.parseInt(str1.substring(0,4))); + String str2 = lonArr[1]; + intData.add(Integer.parseInt(lonArr[0])); + intData.add(Integer.parseInt(str2.substring(0,4))); + + Map map = new HashMap<>(); + map.put("instruct", intData); + return map; + } + +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/xinghan/XinghanSendMsgRule.java b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/xinghan/XinghanSendMsgRule.java new file mode 100644 index 00000000..68acb099 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/xinghan/XinghanSendMsgRule.java @@ -0,0 +1,117 @@ +package com.fuyuanshen.global.mqtt.rule.xinghan; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fuyuanshen.common.json.utils.JsonUtils; +import com.fuyuanshen.common.redis.utils.RedisUtils; +import com.fuyuanshen.global.mqtt.base.MqttMessageRule; +import com.fuyuanshen.global.mqtt.base.MqttRuleContext; +import com.fuyuanshen.global.mqtt.config.MqttGateway; +import com.fuyuanshen.global.mqtt.constants.MqttConstants; +import com.fuyuanshen.global.mqtt.constants.XingHanCommandTypeConstants; +import com.fuyuanshen.global.mqtt.listener.domain.FunctionAccessStatus; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.nio.charset.Charset; +import java.time.Duration; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_KEY; +import static com.fuyuanshen.common.core.constant.GlobalConstants.GLOBAL_REDIS_KEY; +import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX; + +/** + * 星汉设备发送消息 下发规则: + *

+ * 1. 设备上行 sta_TexTarns=genius! => 仅标记成功
+ * 2. 设备上行 sta_TexTarns=数字 => GBK编码,每行文字为一包,一共4包,第一字节为包序号 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class XinghanSendMsgRule implements MqttMessageRule { + + private final MqttGateway mqttGateway; + private final ObjectMapper objectMapper; + + @Override + public String getCommandType() { + return XingHanCommandTypeConstants.XingHan_ESEND_MSG; + } + + @Override + public void execute(MqttRuleContext ctx) { + String functionAccess = FUNCTION_ACCESS_KEY + ctx.getDeviceImei(); + try { + XinghanSendMsgRule.MqttXinghanMsgJson payload = objectMapper.convertValue( + ctx.getPayloadDict(), XinghanSendMsgRule.MqttXinghanMsgJson.class); + + String respText = payload.getStaTexTrans(); + log.info("设备上报人员信息: {} ", respText); + + // 1. genius! —— 成功标记 + if ("genius!".equalsIgnoreCase(respText)) { + RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.FAILED.getCode(), Duration.ofSeconds(20)); + log.info("设备 {} 发送消息完成", ctx.getDeviceImei()); + return; + } + // 2. 数字 —— 下发数据块 + int blockIndex; + try { + blockIndex = Integer.parseInt(respText); + } catch (NumberFormatException ex) { + log.warn("设备 {} 消息上报非法块号:{}", ctx.getDeviceImei(), respText); + return; + } + // 将发送的信息原文本以List形式存储在Redis中 + List data = RedisUtils.getCacheList(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + ctx.getDeviceImei() + ":app_send_message_data"); + if (data.isEmpty()) { + return; + } + // + ArrayList intData = new ArrayList<>(); + intData.add(blockIndex); + // 获取块原内容 转成GBK 再转成无符号十进制整数 + String blockTxt = data.get(blockIndex-1); + // 再按 GBK 编码把字符串转成字节数组,并逐个转为无符号十进制整数 + for (byte b : blockTxt.getBytes(GBK)) { + intData.add(b & 0xFF); // b & 0xFF 得到 0~255 的整数 + } + + Map map = new HashMap<>(); + map.put("ins_TexTrans", intData); + + String topic = MqttConstants.GLOBAL_PUB_KEY + ctx.getDeviceImei(); + String json = JsonUtils.toJsonString(map); + mqttGateway.sendMsgToMqtt(topic, 1, json); + log.info("发送设备信息数据到设备消息=>topic:{},payload:{}", + MqttConstants.GLOBAL_PUB_KEY + ctx.getDeviceImei(), + JsonUtils.toJsonString(map)); + + } catch (Exception e) { + log.error("处理发送设备信息时出错", e); + RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.FAILED.getCode(), Duration.ofSeconds(20)); + } + } + + private static final Charset GBK = Charset.forName("GBK"); + + /* ---------- DTO ---------- */ + + @Data + private static class MqttXinghanMsgJson { + /** + * 设备上行: + * 数字 -> 请求对应块号 + * genius! -> 写入成功 + */ + @JsonProperty("sta_TexTrans") + private String staTexTrans; + } +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/ChineseVCardGenerator.java b/fys-admin/src/main/java/com/fuyuanshen/web/ChineseVCardGenerator.java new file mode 100644 index 00000000..d856a735 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/web/ChineseVCardGenerator.java @@ -0,0 +1,64 @@ +package com.fuyuanshen.web; + +import java.io.BufferedWriter; +import java.io.FileWriter; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; + +public class ChineseVCardGenerator { + public static void main(String[] args) { + // 定义江西上饶的134号段(共22个) + String[] prefixes = { + "1340703", "1340793", "1342650", "1342651", "1342663", + "1342664", "1342665", "1343703", "1343793", "1347901", + "1347902", "1347903", "1347930", "1347931", "1347932", + "1347933", "1347934", "1347935", "1347936", "1347937", + "1347938", "1347939" + }; + + // 创建.vcf文件 + String filename = "上饶联系人.vcf"; + + try (BufferedWriter writer = new BufferedWriter(new FileWriter(filename, true))) { + // 添加文件头信息(包含中文) + writer.write("BEGIN:VCARD"); + writer.newLine(); + writer.write("VERSION:3.0"); + writer.newLine(); + writer.write("X-GENERATOR:Java VCard Generator"); + writer.newLine(); + writer.write("PRODID:-//Apple Inc.//iPhone OS 15.6//EN"); + writer.newLine(); + writer.newLine(); + + // 生成所有联系人 + int count = 0; + for (String prefix : prefixes) { + for (int i = 0; i < 100; i++) { + String middle = String.format("%02d", i); + String phoneNumber = prefix + middle + "51"; + + // 写入单个联系人(使用中文) + writer.write("FN;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:用户" + (count + 1)); // 中文姓名 + writer.newLine(); + writer.write("TEL;TYPE=CELL;CHARSET=UTF-8:" + phoneNumber); // 手机号 + writer.newLine(); + writer.write("NOTE;CHARSET=UTF-8:生成时间 " + new SimpleDateFormat("yyyy-MM-dd").format(new Date())); + writer.newLine(); + writer.write("END:VCARD"); + writer.newLine(); + writer.newLine(); + + count++; + } + } + + System.out.println("成功生成 " + count + " 个中文联系人"); + System.out.println("文件已保存为: " + filename); + + } catch (IOException e) { + System.err.println("文件写入错误: " + e.getMessage()); + } + } +} \ No newline at end of file diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/PhoneNumberGenerator.java b/fys-admin/src/main/java/com/fuyuanshen/web/PhoneNumberGenerator.java new file mode 100644 index 00000000..56536d84 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/web/PhoneNumberGenerator.java @@ -0,0 +1,41 @@ +package com.fuyuanshen.web; + +import java.io.BufferedWriter; +import java.io.FileWriter; +import java.io.IOException; + +public class PhoneNumberGenerator { + public static void main(String[] args) { + // 定义江西上饶的134号段(共22个) + String[] prefixes = { + "1340703", "1340793", "1342650", "1342651", "1342663", + "1342664", "1342665", "1343703", "1343793", "1347901", + "1347902", "1347903", "1347930", "1347931", "1347932", + "1347933", "1347934", "1347935", "1347936", "1347937", + "1347938", "1347939" + }; + + // 输出到控制台 + System.out.println("江西上饶134号段完整手机号码(共2200个):"); + for (String prefix : prefixes) { + for (int i = 0; i < 100; i++) { + String middle = String.format("%02d", i); // 生成00-99的中间数字 + System.out.println(prefix + middle + "51"); + } + } + + // 同时输出到文件(可选) + try (BufferedWriter writer = new BufferedWriter(new FileWriter("shangrao_phones.txt"))) { + for (String prefix : prefixes) { + for (int i = 0; i < 100; i++) { + String middle = String.format("%02d", i); + writer.write(prefix + middle + "51"); + writer.newLine(); + } + } + System.out.println("\n同时已保存到文件:shangrao_phones.txt"); + } catch (IOException e) { + System.err.println("文件写入错误:" + e.getMessage()); + } + } +} \ No newline at end of file diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/VCardGenerator.java b/fys-admin/src/main/java/com/fuyuanshen/web/VCardGenerator.java new file mode 100644 index 00000000..9a1f507a --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/web/VCardGenerator.java @@ -0,0 +1,59 @@ +package com.fuyuanshen.web; + +import java.io.BufferedWriter; +import java.io.FileWriter; +import java.io.IOException; + +public class VCardGenerator { + public static void main(String[] args) { + // 定义江西上饶的134号段(共22个) + String[] prefixes = { + "1340703", "1340793", "1342650", "1342651", "1342663", + "1342664", "1342665", "1343703", "1343793", "1347901", + "1347902", "1347903", "1347930", "1347931", "1347932", + "1347933", "1347934", "1347935", "1347936", "1347937", + "1347938", "1347939" + }; + + // 创建.vcf文件 + String filename = "shangrao_contacts.vcf"; + + try (BufferedWriter writer = new BufferedWriter(new FileWriter(filename))) { + // 写入文件头 + writer.write("BEGIN:VCARD"); + writer.newLine(); + writer.write("VERSION:3.0"); + writer.newLine(); + writer.newLine(); + + // 生成所有联系人 + int count = 0; + for (String prefix : prefixes) { + for (int i = 0; i < 100; i++) { + String middle = String.format("%02d", i); + String phoneNumber = prefix + middle + "51"; + + // 写入单个联系人 + writer.write("FN:" + phoneNumber); // 姓名字段使用手机号 + writer.newLine(); + writer.write("TEL;TYPE=CELL:" + phoneNumber); // 电话字段使用手机号 + writer.newLine(); + writer.write("END:VCARD"); + writer.newLine(); + writer.newLine(); + + count++; + } + } + + // 写入文件尾 + writer.write("END:VCARD"); + + System.out.println("成功生成 " + count + " 个联系人"); + System.out.println("文件已保存为: " + filename); + + } catch (IOException e) { + System.err.println("文件写入错误: " + e.getMessage()); + } + } +} \ No newline at end of file diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/config/CustomMqttInboundConfiguration.java b/fys-admin/src/main/java/com/fuyuanshen/web/config/CustomMqttInboundConfiguration.java index faa8cb59..afefa342 100644 --- a/fys-admin/src/main/java/com/fuyuanshen/web/config/CustomMqttInboundConfiguration.java +++ b/fys-admin/src/main/java/com/fuyuanshen/web/config/CustomMqttInboundConfiguration.java @@ -1,61 +1,61 @@ -package com.fuyuanshen.web.config; - -import cn.hutool.core.lang.UUID; -import com.fuyuanshen.global.mqtt.config.MqttPropertiesConfig; -import com.fuyuanshen.web.handler.mqtt.DeviceReceiverMessageHandler; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.integration.annotation.ServiceActivator; -import org.springframework.integration.channel.DirectChannel; -import org.springframework.integration.core.MessageProducer; -import org.springframework.integration.mqtt.core.MqttPahoClientFactory; -import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter; -import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter; -import org.springframework.messaging.MessageChannel; -import org.springframework.messaging.MessageHandler; - -/** - * @author: 默苍璃 - * @date: 2025-08-0110:46 - */ -@Configuration -public class CustomMqttInboundConfiguration { - - @Autowired - private MqttPropertiesConfig mqttPropertiesConfig; - @Autowired - private MqttPahoClientFactory mqttPahoClientFactory; - @Autowired - private DeviceReceiverMessageHandler deviceReceiverMessageHandler; - - - @Bean - public MessageChannel customMqttChannel(){ - return new DirectChannel(); - } - - - @Bean - public MessageProducer customMessageProducer(){ - String clientId = "custom_client_" + UUID.fastUUID(); - MqttPahoMessageDrivenChannelAdapter adapter = new MqttPahoMessageDrivenChannelAdapter( - mqttPropertiesConfig.getUrl(), - clientId, - mqttPahoClientFactory, - "A/#", "B/#" // 直接指定这两个主题 - ); - adapter.setQos(1); - adapter.setConverter(new DefaultPahoMessageConverter()); - adapter.setOutputChannel(customMqttChannel()); - return adapter; - } - - - @Bean - @ServiceActivator(inputChannel = "customMqttChannel") - public MessageHandler customMessageHandler(){ - return deviceReceiverMessageHandler; - } - -} +//package com.fuyuanshen.web.config; +// +//import cn.hutool.core.lang.UUID; +//import com.fuyuanshen.global.mqtt.config.MqttPropertiesConfig; +//import com.fuyuanshen.web.handler.mqtt.DeviceReceiverMessageHandler; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.context.annotation.Bean; +//import org.springframework.context.annotation.Configuration; +//import org.springframework.integration.annotation.ServiceActivator; +//import org.springframework.integration.channel.DirectChannel; +//import org.springframework.integration.core.MessageProducer; +//import org.springframework.integration.mqtt.core.MqttPahoClientFactory; +//import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter; +//import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter; +//import org.springframework.messaging.MessageChannel; +//import org.springframework.messaging.MessageHandler; +// +///** +// * @author: 默苍璃 +// * @date: 2025-08-0110:46 +// */ +//@Configuration +//public class CustomMqttInboundConfiguration { +// +// @Autowired +// private MqttPropertiesConfig mqttPropertiesConfig; +// @Autowired +// private MqttPahoClientFactory mqttPahoClientFactory; +// @Autowired +// private DeviceReceiverMessageHandler deviceReceiverMessageHandler; +// +// +// @Bean +// public MessageChannel customMqttChannel(){ +// return new DirectChannel(); +// } +// +// +// @Bean +// public MessageProducer customMessageProducer(){ +// String clientId = "custom_client_" + UUID.fastUUID(); +// MqttPahoMessageDrivenChannelAdapter adapter = new MqttPahoMessageDrivenChannelAdapter( +// mqttPropertiesConfig.getUrl(), +// clientId, +// mqttPahoClientFactory, +// "A/#", "B/#" // 直接指定这两个主题 +// ); +// adapter.setQos(1); +// adapter.setConverter(new DefaultPahoMessageConverter()); +// adapter.setOutputChannel(customMqttChannel()); +// return adapter; +// } +// +// +// @Bean +// @ServiceActivator(inputChannel = "customMqttChannel") +// public MessageHandler customMessageHandler(){ +// return deviceReceiverMessageHandler; +// } +// +//} diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/controller/device/DeviceBJQController.java b/fys-admin/src/main/java/com/fuyuanshen/web/controller/device/DeviceBJQController.java new file mode 100644 index 00000000..e0536306 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/web/controller/device/DeviceBJQController.java @@ -0,0 +1,119 @@ +package com.fuyuanshen.web.controller.device; + +import com.fuyuanshen.app.domain.bo.AppPersonnelInfoBo; +import com.fuyuanshen.app.domain.dto.AppDeviceLogoUploadDto; +import com.fuyuanshen.app.domain.dto.DeviceInstructDto; +import com.fuyuanshen.app.domain.vo.AppDeviceDetailVo; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.core.validate.AddGroup; +import com.fuyuanshen.common.ratelimiter.annotation.FunctionAccessAnnotation; +import com.fuyuanshen.common.ratelimiter.annotation.FunctionAccessBatcAnnotation; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.equipment.domain.dto.AppDeviceSendMsgBo; +import com.fuyuanshen.web.service.device.DeviceBJQBizService; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +/** + * web后台:设备控制类 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/api/bjq/device") +public class DeviceBJQController extends BaseController { + + private final DeviceBJQBizService appDeviceService; + + /** + * 获取设备详细信息 + * + * @param id 主键 + */ + @GetMapping("/{id}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(appDeviceService.getInfo(id)); + } + + /** + * 人员信息登记 + */ + @PostMapping(value = "/registerPersonInfo") +// @FunctionAccessAnnotation("registerPersonInfo") + public R registerPersonInfo(@Validated(AddGroup.class) @RequestBody AppPersonnelInfoBo bo) { + return toAjax(appDeviceService.registerPersonInfo(bo)); + } + + /** + * 发送信息 + */ + @PostMapping(value = "/sendMessage") + @FunctionAccessBatcAnnotation(value = "sendMessage", timeOut = 30, batchMaxTimeOut = 40) + public R sendMessage(@RequestBody AppDeviceSendMsgBo bo) { + return toAjax(appDeviceService.sendMessage(bo)); + } + + /** + * 发送报警信息 + */ + @PostMapping(value = "/sendAlarmMessage") + @FunctionAccessBatcAnnotation(value = "sendAlarmMessage", timeOut = 5, batchMaxTimeOut = 10) + public R sendAlarmMessage(@RequestBody AppDeviceSendMsgBo bo) { + return toAjax(appDeviceService.sendAlarmMessage(bo)); + } + + /** + * 上传设备logo图片 + */ + @PostMapping("/uploadLogo") + @FunctionAccessAnnotation("uploadLogo") + public R upload(@Validated @ModelAttribute AppDeviceLogoUploadDto bo) { + + MultipartFile file = bo.getFile(); + if(file.getSize()>1024*1024*2){ + return R.warn("图片不能大于2M"); + } + appDeviceService.uploadDeviceLogo(bo); + + return R.ok(); + } + + /** + * 灯光模式 + * 0(关灯),1(强光模式),2(弱光模式), 3(爆闪模式), 4(泛光模式) + */ +// @FunctionAccessAnnotation("lightModeSettings") + @PostMapping("/lightModeSettings") + public R lightModeSettings(@RequestBody DeviceInstructDto params) { + // params 转 JSONObject + appDeviceService.lightModeSettings(params); + return R.ok(); + } + + /** + * 灯光亮度设置 + * + */ +// @FunctionAccessAnnotation("lightBrightnessSettings") + @PostMapping("/lightBrightnessSettings") + public R lightBrightnessSettings(@RequestBody DeviceInstructDto params) { + appDeviceService.lightBrightnessSettings(params); + return R.ok(); + } + + /** + * 激光模式设置 + * + */ + @PostMapping("/laserModeSettings") +// @FunctionAccessAnnotation("laserModeSettings") + public R laserModeSettings(@RequestBody DeviceInstructDto params) { + appDeviceService.laserModeSettings(params); + return R.ok(); + } + +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/controller/device/DeviceControlCenterController.java b/fys-admin/src/main/java/com/fuyuanshen/web/controller/device/DeviceControlCenterController.java new file mode 100644 index 00000000..76e39357 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/web/controller/device/DeviceControlCenterController.java @@ -0,0 +1,98 @@ +package com.fuyuanshen.web.controller.device; + +import com.fuyuanshen.app.domain.dto.APPReNameDTO; +import com.fuyuanshen.app.domain.dto.AppRealTimeStatusDto; +import com.fuyuanshen.app.domain.vo.APPDeviceTypeVo; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.equipment.domain.dto.AppDeviceBo; +import com.fuyuanshen.equipment.domain.query.DeviceQueryCriteria; +import com.fuyuanshen.equipment.domain.vo.AppDeviceVo; +import com.fuyuanshen.equipment.domain.vo.WebDeviceVo; +import com.fuyuanshen.web.service.device.DeviceBizService; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * web后台:设备控制中心 + */ +@Slf4j +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/device/controlCenter") +public class DeviceControlCenterController extends BaseController { + + private final DeviceBizService appDeviceService; + /** + * 查询设备列表 + */ + @GetMapping("/list") + public TableDataInfo list(DeviceQueryCriteria bo, PageQuery pageQuery) { + return appDeviceService.queryWebDeviceList(bo, pageQuery); + } + + /** + * 绑定设备 + */ + @PostMapping("/bind") + public R bind(@RequestBody AppDeviceBo bo) { + return toAjax(appDeviceService.bindDevice(bo)); + } + + + /** + * 解绑设备 + */ + @DeleteMapping("/unBind") + public R unBind(Long id) { + return toAjax(appDeviceService.unBindDevice(id)); + } + + /** + * 查询设备类型列表 + */ + @GetMapping(value = "/typeList") + public R> getTypeList() { + List typeList = appDeviceService.getTypeList(); + return R.ok(typeList); + } + + /** + * 重命名设备 + * + * @param reNameDTO + * @return + */ + @PostMapping(value = "/reName") + public R reName(@Validated @RequestBody APPReNameDTO reNameDTO) { + appDeviceService.reName(reNameDTO); + return R.ok("重命名成功!!!"); + } + + + @GetMapping("/realTimeStatus") + public R> getRealTimeStatus(AppRealTimeStatusDto statusDto) { + Map status = appDeviceService.getRealTimeStatus(statusDto); + return R.ok(status); + } + + /** + * 根据mac查询设备信息 + */ + @GetMapping("/getDeviceInfoByDeviceMac") + public R getDeviceInfo(String deviceMac) { + return R.ok(appDeviceService.getDeviceInfo(deviceMac)); + } + +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/controller/device/DeviceGroupController.java b/fys-admin/src/main/java/com/fuyuanshen/web/controller/device/DeviceGroupController.java new file mode 100644 index 00000000..28601a1a --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/web/controller/device/DeviceGroupController.java @@ -0,0 +1,117 @@ +package com.fuyuanshen.web.controller.device; + +import java.util.List; + +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.*; +import cn.dev33.satoken.annotation.SaCheckPermission; +import org.springframework.web.bind.annotation.*; +import org.springframework.validation.annotation.Validated; +import com.fuyuanshen.common.idempotent.annotation.RepeatSubmit; +import com.fuyuanshen.common.log.annotation.Log; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.core.validate.AddGroup; +import com.fuyuanshen.common.core.validate.EditGroup; +import com.fuyuanshen.common.log.enums.BusinessType; +import com.fuyuanshen.common.excel.utils.ExcelUtil; +import com.fuyuanshen.equipment.domain.vo.DeviceGroupVo; +import com.fuyuanshen.equipment.domain.bo.DeviceGroupBo; +import com.fuyuanshen.equipment.service.IDeviceGroupService; + +/** + * 设备分组 + * + * @author Lion Li + * @date 2025-08-08 + */ +@Tag(name = "web:设备分组", description = "web:设备分组") +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/api/device/group") +public class DeviceGroupController extends BaseController { + + private final IDeviceGroupService deviceGroupService; + + + /** + * 查询设备分组列表 + */ + @Operation(summary = "查询设备分组列表") + @SaCheckPermission("fys-equipment:group:list") + @GetMapping("/list") + public R> list(DeviceGroupBo bo) { + List list = deviceGroupService.queryList(bo); + return R.ok(list); + } + + + /** + * 导出设备分组列表 + */ + @SaCheckPermission("fys-equipment:group:export") + @Log(title = "设备分组", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(DeviceGroupBo bo, HttpServletResponse response) { + List list = deviceGroupService.queryList(bo); + ExcelUtil.exportExcel(list, "设备分组", DeviceGroupVo.class, response); + } + + + /** + * 获取设备分组详细信息 + * + * @param id 主键 + */ + @Operation(summary = "获取设备分组详细信息") + @SaCheckPermission("fys-equipment:group:query") + @GetMapping("/{id}") + public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) { + return R.ok(deviceGroupService.queryById(id)); + } + + + /** + * 新增设备分组 + */ + @Operation(summary = "新增设备分组") + @SaCheckPermission("fys-equipment:group:add") + @Log(title = "设备分组", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody DeviceGroupBo bo) { + return toAjax(deviceGroupService.insertByBo(bo)); + } + + + /** + * 修改设备分组 + */ + @Operation(summary = "修改设备分组") + @SaCheckPermission("fys-equipment:group:edit") + @Log(title = "设备分组", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody DeviceGroupBo bo) { + return toAjax(deviceGroupService.updateByBo(bo)); + } + + /** + * 删除设备分组 + * + * @param ids 主键串 + */ + @Operation(summary = "删除设备分组") + @SaCheckPermission("fys-equipment:group:remove") + @Log(title = "设备分组", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) { + return toAjax(deviceGroupService.deleteWithValidByIds(List.of(ids), true)); + } + +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/controller/device/WEBDeviceController.java b/fys-admin/src/main/java/com/fuyuanshen/web/controller/device/WEBDeviceController.java index eadb1579..86ec7ed6 100644 --- a/fys-admin/src/main/java/com/fuyuanshen/web/controller/device/WEBDeviceController.java +++ b/fys-admin/src/main/java/com/fuyuanshen/web/controller/device/WEBDeviceController.java @@ -1,16 +1,29 @@ package com.fuyuanshen.web.controller.device; +import com.fuyuanshen.app.domain.dto.APPReNameDTO; +import com.fuyuanshen.app.domain.dto.AppRealTimeStatusDto; +import com.fuyuanshen.app.domain.vo.APPDeviceTypeVo; import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.equipment.domain.Device; +import com.fuyuanshen.equipment.domain.dto.AppDeviceBo; +import com.fuyuanshen.equipment.domain.query.DeviceQueryCriteria; +import com.fuyuanshen.equipment.domain.vo.AppDeviceVo; +import com.fuyuanshen.equipment.domain.vo.WebDeviceVo; import com.fuyuanshen.web.service.WEBDeviceService; +import com.fuyuanshen.web.service.device.DeviceBizService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; /** * @Description: @@ -41,6 +54,20 @@ public class WEBDeviceController extends BaseController { } + /** + * 设备详情 + * + * @param id + * @return + */ + @Operation(summary = "设备详情") + @GetMapping(value = "/pc/detail/{id}") + public R getDevice(@PathVariable Long id) { + WebDeviceVo device = deviceService.getDevice(id); + return R.ok(device); + } + + } diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/handler/mqtt/DeviceReceiverMessageHandler.java b/fys-admin/src/main/java/com/fuyuanshen/web/handler/mqtt/DeviceReceiverMessageHandler.java index 3548e13f..ad080ac1 100644 --- a/fys-admin/src/main/java/com/fuyuanshen/web/handler/mqtt/DeviceReceiverMessageHandler.java +++ b/fys-admin/src/main/java/com/fuyuanshen/web/handler/mqtt/DeviceReceiverMessageHandler.java @@ -1,270 +1,318 @@ -package com.fuyuanshen.web.handler.mqtt; - -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fuyuanshen.common.satoken.utils.LoginHelper; -import com.fuyuanshen.equipment.domain.Device; -import com.fuyuanshen.equipment.domain.DeviceLog; -import com.fuyuanshen.equipment.mapper.DeviceLogMapper; -import com.fuyuanshen.equipment.mapper.DeviceMapper; -import com.fuyuanshen.equipment.utils.map.GetAddressFromLatUtil; -import com.fuyuanshen.equipment.utils.map.LngLonUtil; -import com.fuyuanshen.global.mqtt.constants.TenantsConstant; -import com.fuyuanshen.web.enums.InstructType6170; -import com.fuyuanshen.web.enums.LightModeEnum6170; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.extern.slf4j.Slf4j; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHandler; -import org.springframework.messaging.MessageHeaders; -import org.springframework.messaging.MessagingException; -import org.springframework.stereotype.Component; - -/** - * 定义监听主题消息的处理器 - * - * @author: 默苍璃 - * @date: 2025-08-0110:19 - */ -@Component -@Data -@AllArgsConstructor -@Slf4j -public class DeviceReceiverMessageHandler implements MessageHandler { - - private final DeviceMapper deviceMapper; - private final DeviceLogMapper deviceLogMapper; - - // 使用Jackson解析JSON - private static final ObjectMapper objectMapper = new ObjectMapper(); - - - /** - * 处理接收的消息 - * - * @param message - * @throws MessagingException - */ - @Override - public void handleMessage(Message message) throws MessagingException { - // System.out.println("接收到的消息:" + message.getPayload()); - MessageHeaders headers = message.getHeaders(); - String receivedTopicName = (String) headers.get("mqtt_receivedTopic"); - System.out.println("消息来自主题:" + receivedTopicName); - - // String tenantId = LoginHelper.getTenantId(); - String tenantId = TenantsConstant.FU_YUAN_SHENG; - String payload = message.getPayload().toString(); - - if (receivedTopicName != null) { - // 1. 提取设备ID (从主题中获取) - String deviceImei = extractDeviceId(receivedTopicName); - Device device = deviceMapper.selectOne(new QueryWrapper() - .eq("tenant_id", tenantId) - .eq("device_imei", deviceImei)); - if (device == null) { - log.info("不存在的设备IMEI: {}", deviceImei); - } else { - - try { - JsonNode root = objectMapper.readTree(payload); - - DeviceLog record = new DeviceLog(); - // 手动设置租户ID - record.setTenantId(device.getTenantId()); // 从设备信息中获取租户ID - // 设备ID - record.setDeviceId(device.getId()); - // 设备名称 - record.setDeviceName(device.getDeviceName()); - - // 2. 处理instruct消息 - if (root.has("instruct")) { - JsonNode instructNode = root.get("instruct"); - if (instructNode.isArray()) { - boolean b = receivedTopicName.startsWith("B/"); - record = parseInstruct(device, instructNode, b); - // 根据不同主题进行不同处理 - if (receivedTopicName.startsWith("A/")) { - // 处理A主题的消息(设备上传) - record.setDataSource("设备上报"); - } else if (receivedTopicName.startsWith("B/")) { - // 处理B主题的消息 (手动上传) - record.setDataSource("客户端操作"); - } - } - deviceLogMapper.insert(record); - } - - if (root.has("imei")) { - // 设备行为 - record.setDeviceAction(InstructType6170.fromCode(0).getDescription()); - record.setDataSource("设备上报"); - record.setContent("设备启动"); - deviceLogMapper.insert(record); - } - - - // 3. 处理state消息 - // else if (root.has("state")) { - // JsonNode stateNode = root.get("state"); - // if (stateNode.isArray()) { - // StateRecord record = parseState(device, stateNode); - // stateRepo.save(record); - // } - // } - } catch (Exception e) { - log.error("消息解析失败: {}", payload, e); - } - - } - - } - } - - - /** - * 从主题中提取设备ID(IMEI) - * - * @param topic - * @return - */ - private String extractDeviceId(String topic) { - // 处理 A/# 或 B/# 格式的主题,例如 B/861556078765285 或 A/861556078765285 - String[] segments = topic.split("/"); - if (segments.length >= 2) { - // 返回第二个段,即 / 后面的部分 - return segments[1]; - } - // 如果格式不符合预期,返回原主题 - return topic; - } - - - /** - * 解析instruct消息 - * - * @param device - * @param array - * @param b - * @return - */ - private DeviceLog parseInstruct(Device device, JsonNode array, boolean b) { - DeviceLog record = new DeviceLog(); - record.setDeviceName(device.getDeviceName()); - // 设备行为 - record.setDeviceAction(InstructType6170.fromCode(array.get(0).asInt()).getDescription()); - - switch (array.get(0).asInt()) { - case 1: // 灯光模式 - LightModeEnum6170 lightModeEnum6170 = LightModeEnum6170.fromCode(array.get(1).asInt()); - record.setContent(lightModeEnum6170.getDescription()); - break; - - case 2: // 单位/姓名/职位 - byte[] unitBytes = new byte[480]; - for (int i = 1; i <= 480; i++) { - unitBytes[i - 1] = (byte) array.get(i).asInt(); - } - // record.setUnitData(unitBytes); - break; - - case 3: // 开机图片 - // record.setImagePage(array.get(1).asInt()); - byte[] imageBytes = new byte[512]; - for (int i = 2; i <= 513; i++) { - imageBytes[i - 2] = (byte) array.get(i).asInt(); - } - // record.setImageData(imageBytes); - break; - - case 4: // 激光灯 - int anInt = array.get(1).asInt(); - if (anInt == 0) { - record.setContent("关闭激光灯"); - } else if (anInt == 1) { - record.setContent("开启激光灯"); - } else { - record.setContent("未知操作"); - } - break; - - case 5: // 亮度调节 - record.setContent(+array.get(1).asInt() + "%"); - break; - - case 11: // 定位数据 - if (b) { - break; - } - int i1 = array.get(1).asInt(); - int i2 = array.get(2).asInt(); - int i3 = array.get(3).asInt(); - int i4 = array.get(4).asInt(); - - // 优雅的转换方式 Longitude and latitude - double latitude = i1 + i2 / 10.0; - double Longitude = i3 + i4 / 10.0; - // 84 ==》 高德 - double[] doubles = LngLonUtil.gps84_To_Gcj02(latitude, Longitude); - String address = GetAddressFromLatUtil.getAdd(String.valueOf(doubles[1]), String.valueOf(doubles[0])); - record.setContent(address); - break; - } - return record; - } - - - /** - * 解析state消息 - * - * @param device - * @param array - * @return - */ - // private StateRecord parseState(Device device, JsonNode array) { - // StateRecord record = new StateRecord(); - // record.setDevice(device); - // record.setStateType(array.get(0).asInt()); - // - // switch (record.getStateType()) { - // case 1: // 灯光状态 - // record.setLightMode(array.get(1).asInt()); - // record.setBrightness(array.get(2).asInt()); - // break; - // - // case 2: // 设置结果 - // record.setSetResult(array.get(1).asInt() == 1); - // break; - // - // case 3: // 图片更新状态 - // record.setImagePage(array.get(1).asInt()); - // break; - // - // case 4: // 激光灯状态 - // record.setLaserStatus(array.get(1).asInt() == 1); - // break; - // - // case 5: // 亮度状态 - // record.setBrightness(array.get(1).asInt()); - // break; - // - // case 11: // 定位上报 - // record.setLatitude(array.get(1).asDouble()); - // record.setLongitude(array.get(2).asDouble()); - // break; - // - // case 12: // 设备状态 - // record.setMainLightGear(array.get(1).asInt()); - // record.setLaserLightGear(array.get(2).asInt()); - // record.setBattery(array.get(3).asInt()); - // record.setChargeStatus(array.get(4).asInt()); - // record.setDuration(array.get(5).asInt()); - // break; - // } - // return record; - // } - - -} +//package com.fuyuanshen.web.handler.mqtt; +// +//import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +//import com.fasterxml.jackson.databind.JsonNode; +//import com.fasterxml.jackson.databind.ObjectMapper; +//import com.fuyuanshen.common.satoken.utils.LoginHelper; +//import com.fuyuanshen.equipment.domain.Device; +//import com.fuyuanshen.equipment.domain.DeviceLog; +//import com.fuyuanshen.equipment.mapper.DeviceLogMapper; +//import com.fuyuanshen.equipment.mapper.DeviceMapper; +//import com.fuyuanshen.equipment.utils.map.GetAddressFromLatUtil; +//import com.fuyuanshen.equipment.utils.map.LngLonUtil; +//import com.fuyuanshen.global.mqtt.constants.TenantsConstant; +//import com.fuyuanshen.web.enums.InstructType6170; +//import com.fuyuanshen.web.enums.LightModeEnum6170; +//import lombok.AllArgsConstructor; +//import lombok.Data; +//import lombok.extern.slf4j.Slf4j; +//import org.springframework.messaging.Message; +//import org.springframework.messaging.MessageHandler; +//import org.springframework.messaging.MessageHeaders; +//import org.springframework.messaging.MessagingException; +//import org.springframework.stereotype.Component; +// +///** +// * 定义监听主题消息的处理器 +// * +// * @author: 默苍璃 +// * @date: 2025-08-0110:19 +// */ +//@Component +//@Data +//@AllArgsConstructor +//@Slf4j +//public class DeviceReceiverMessageHandler implements MessageHandler { +// +// private final DeviceMapper deviceMapper; +// private final DeviceLogMapper deviceLogMapper; +// +// // 使用Jackson解析JSON +// private static final ObjectMapper objectMapper = new ObjectMapper(); +// +// +// /** +// * 处理接收的消息 +// * +// * @param message +// * @throws MessagingException +// */ +// @Override +// public void handleMessage(Message message) throws MessagingException { +// // System.out.println("接收到的消息:" + message.getPayload()); +// MessageHeaders headers = message.getHeaders(); +// String receivedTopicName = (String) headers.get("mqtt_receivedTopic"); +// System.out.println("消息来自主题:" + receivedTopicName); +// +// // String tenantId = LoginHelper.getTenantId(); +// String tenantId = TenantsConstant.FU_YUAN_SHENG; +// String payload = message.getPayload().toString(); +// +// if (receivedTopicName != null) { +// // 1. 提取设备ID (从主题中获取) +// String deviceImei = extractDeviceId(receivedTopicName); +// Device device = deviceMapper.selectOne(new QueryWrapper() +// .eq("tenant_id", tenantId) +// .eq("device_imei", deviceImei)); +// if (device == null) { +// log.info("不存在的设备IMEI: {}", deviceImei); +// } else { +// +// try { +// JsonNode root = objectMapper.readTree(payload); +// +// DeviceLog record = new DeviceLog(); +// // 手动设置租户ID +// record.setTenantId(device.getTenantId()); // 从设备信息中获取租户ID +// // 设备ID +// record.setDeviceId(device.getId()); +// // 设备名称 +// record.setDeviceName(device.getDeviceName()); +// +// // 2. 处理instruct消息 +// if (root.has("instruct")) { +// JsonNode instructNode = root.get("instruct"); +// if (instructNode.isArray()) { +// boolean b = receivedTopicName.startsWith("B/"); +// record = parseInstruct(device, instructNode, b); +// // 根据不同主题进行不同处理 +// if (receivedTopicName.startsWith("A/")) { +// // 处理A主题的消息(设备上传) +// record.setDataSource("设备上报"); +// } else if (receivedTopicName.startsWith("B/")) { +// // 处理B主题的消息 (手动上传) +// record.setDataSource("客户端操作"); +// } +// } +// // 确保在插入前设置tenantId和deviceId +// record.setTenantId(device.getTenantId()); +// record.setDeviceId(device.getId()); +// deviceLogMapper.insert(record); +// } +// +// // 2. 处理 state 消息 +// if (root.has("state")) { +// JsonNode instructNode = root.get("state"); +// if (instructNode.isArray()) { +// boolean b = receivedTopicName.startsWith("B/"); +// record = parseState(device, instructNode, b); +// // 根据不同主题进行不同处理 +// if (receivedTopicName.startsWith("A/")) { +// // 处理A主题的消息(设备上传) +// record.setDataSource("设备上报"); +// } else if (receivedTopicName.startsWith("B/")) { +// // 处理B主题的消息 (手动上传) +// record.setDataSource("客户端操作"); +// } +// } +// // 确保在插入前设置tenantId和deviceId +// record.setTenantId(device.getTenantId()); +// record.setDeviceId(device.getId()); +// deviceLogMapper.insert(record); +// } +// +// if (root.has("imei")) { +// // 设备行为 +// record.setDeviceAction(InstructType6170.fromCode(0).getDescription()); +// record.setDataSource("设备上报"); +// record.setContent("设备启动"); +// // 确保在插入前设置tenantId和deviceId +// record.setTenantId(device.getTenantId()); +// record.setDeviceId(device.getId()); +// deviceLogMapper.insert(record); +// } +// +// +// // 3. 处理state消息 +// // else if (root.has("state")) { +// // JsonNode stateNode = root.get("state"); +// // if (stateNode.isArray()) { +// // StateRecord record = parseState(device, stateNode); +// // stateRepo.save(record); +// // } +// // } +// } catch (Exception e) { +// log.error("消息解析失败: {}", payload, e); +// } +// +// } +// +// } +// } +// +// +// /** +// * 从主题中提取设备ID(IMEI) +// * +// * @param topic +// * @return +// */ +// private String extractDeviceId(String topic) { +// // 处理 A/# 或 B/# 格式的主题,例如 B/861556078765285 或 A/861556078765285 +// String[] segments = topic.split("/"); +// if (segments.length >= 2) { +// // 返回第二个段,即 / 后面的部分 +// return segments[1]; +// } +// // 如果格式不符合预期,返回原主题 +// return topic; +// } +// +// +// /** +// * 解析instruct消息 +// * +// * @param device +// * @param array +// * @param b +// * @return +// */ +// private DeviceLog parseInstruct(Device device, JsonNode array, boolean b) { +// DeviceLog record = new DeviceLog(); +// record.setDeviceName(device.getDeviceName()); +// // 设备行为 +// record.setDeviceAction(InstructType6170.fromCode(array.get(0).asInt()).getDescription()); +// +// switch (array.get(0).asInt()) { +// case 1: // 灯光模式 +// LightModeEnum6170 lightModeEnum6170 = LightModeEnum6170.fromCode(array.get(1).asInt()); +// record.setContent(lightModeEnum6170.getDescription()); +// break; +// +// case 2: // 单位/姓名/职位 +// byte[] unitBytes = new byte[480]; +// for (int i = 1; i <= 480; i++) { +// unitBytes[i - 1] = (byte) array.get(i).asInt(); +// } +// // record.setUnitData(unitBytes); +// break; +// +// case 3: // 开机图片 +// // record.setImagePage(array.get(1).asInt()); +// byte[] imageBytes = new byte[512]; +// for (int i = 2; i <= 513; i++) { +// imageBytes[i - 2] = (byte) array.get(i).asInt(); +// } +// // record.setImageData(imageBytes); +// break; +// +// case 4: // 激光灯 +// int anInt = array.get(1).asInt(); +// if (anInt == 0) { +// record.setContent("关闭激光灯"); +// } else if (anInt == 1) { +// record.setContent("开启激光灯"); +// } else { +// record.setContent("未知操作"); +// } +// break; +// +// case 5: // 亮度调节 +// record.setContent(+array.get(1).asInt() + "%"); +// break; +// +// case 11: // 定位数据 +// if (b) { +// break; +// } +// int i1 = array.get(1).asInt(); +// int i2 = array.get(2).asInt(); +// int i3 = array.get(3).asInt(); +// int i4 = array.get(4).asInt(); +// +// // 优雅的转换方式 Longitude and latitude +// double latitude = i1 + i2 / 10.0; +// double Longitude = i3 + i4 / 10.0; +// // 84 ==》 高德 +// double[] doubles = LngLonUtil.gps84_To_Gcj02(latitude, Longitude); +// String address = GetAddressFromLatUtil.getAdd(String.valueOf(doubles[1]), String.valueOf(doubles[0])); +// record.setContent(address); +// break; +// } +// return record; +// } +// +// +// /** +// * 解析 state 消息 +// * +// * @param device +// * @param array +// * @return +// */ +// private DeviceLog parseState(Device device, JsonNode array, boolean b) { +// DeviceLog record = new DeviceLog(); +// record.setDeviceName(device.getDeviceName()); +// // 设备行为 +// record.setDeviceAction(InstructType6170.fromCode(array.get(0).asInt()).getDescription()); +// +// switch (array.get(0).asInt()) { +// case 1: // 灯光模式 +// LightModeEnum6170 lightModeEnum6170 = LightModeEnum6170.fromCode(array.get(1).asInt()); +// record.setContent(lightModeEnum6170.getDescription()); +// break; +// +// case 2: // 单位/姓名/职位 +// byte[] unitBytes = new byte[480]; +// for (int i = 1; i <= 480; i++) { +// unitBytes[i - 1] = (byte) array.get(i).asInt(); +// } +// // record.setUnitData(unitBytes); +// break; +// +// case 3: // 开机图片 +// // record.setImagePage(array.get(1).asInt()); +// byte[] imageBytes = new byte[512]; +// for (int i = 2; i <= 513; i++) { +// imageBytes[i - 2] = (byte) array.get(i).asInt(); +// } +// // record.setImageData(imageBytes); +// break; +// +// case 4: // 激光灯 +// int anInt = array.get(1).asInt(); +// if (anInt == 0) { +// record.setContent("关闭激光灯"); +// } else if (anInt == 1) { +// record.setContent("开启激光灯"); +// } else { +// record.setContent("未知操作"); +// } +// break; +// +// case 5: // 亮度调节 +// record.setContent(+array.get(1).asInt() + "%"); +// break; +// +// case 11: // 定位数据 +// if (b) { +// break; +// } +// int i1 = array.get(1).asInt(); +// int i2 = array.get(2).asInt(); +// int i3 = array.get(3).asInt(); +// int i4 = array.get(4).asInt(); +// +// // 优雅的转换方式 Longitude and latitude +// double latitude = i1 + i2 / 10.0; +// double Longitude = i3 + i4 / 10.0; +// // 84 ==》 高德 +// double[] doubles = LngLonUtil.gps84_To_Gcj02(latitude, Longitude); +// String address = GetAddressFromLatUtil.getAdd(String.valueOf(doubles[1]), String.valueOf(doubles[0])); +// record.setContent(address); +// break; +// } +// return record; +// } +// +//} diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/listener/UserActionListener.java b/fys-admin/src/main/java/com/fuyuanshen/web/listener/UserActionListener.java index 9105a5da..49762cbc 100644 --- a/fys-admin/src/main/java/com/fuyuanshen/web/listener/UserActionListener.java +++ b/fys-admin/src/main/java/com/fuyuanshen/web/listener/UserActionListener.java @@ -1,163 +1,163 @@ -package com.fuyuanshen.web.listener; - -import cn.dev33.satoken.listener.SaTokenListener; -import cn.dev33.satoken.stp.StpUtil; -import cn.dev33.satoken.stp.parameter.SaLoginParameter; -import cn.hutool.core.convert.Convert; -import cn.hutool.http.useragent.UserAgent; -import cn.hutool.http.useragent.UserAgentUtil; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import com.fuyuanshen.common.core.constant.CacheConstants; -import com.fuyuanshen.common.core.constant.Constants; -import com.fuyuanshen.common.core.domain.dto.UserOnlineDTO; -import com.fuyuanshen.common.core.utils.MessageUtils; -import com.fuyuanshen.common.core.utils.ServletUtils; -import com.fuyuanshen.common.core.utils.SpringUtils; -import com.fuyuanshen.common.core.utils.ip.AddressUtils; -import com.fuyuanshen.common.log.event.LogininforEvent; -import com.fuyuanshen.common.redis.utils.RedisUtils; -import com.fuyuanshen.common.satoken.utils.LoginHelper; -import com.fuyuanshen.common.tenant.helper.TenantHelper; -import com.fuyuanshen.web.service.SysLoginService; -import org.springframework.stereotype.Component; - -import java.time.Duration; - -/** - * 用户行为 侦听器的实现 - * - * @author Lion Li - */ -@RequiredArgsConstructor -@Component -@Slf4j -public class UserActionListener implements SaTokenListener { - - private final SysLoginService loginService; - - /** - * 每次登录时触发 - */ - @Override - public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginParameter loginParameter) { - UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent")); - String ip = ServletUtils.getClientIP(); - UserOnlineDTO dto = new UserOnlineDTO(); - dto.setIpaddr(ip); - dto.setLoginLocation(AddressUtils.getRealAddressByIP(ip)); - dto.setBrowser(userAgent.getBrowser().getName()); - dto.setOs(userAgent.getOs().getName()); - dto.setLoginTime(System.currentTimeMillis()); - dto.setTokenId(tokenValue); - String username = (String) loginParameter.getExtra(LoginHelper.USER_NAME_KEY); - String tenantId = (String) loginParameter.getExtra(LoginHelper.TENANT_KEY); - dto.setUserName(username); - dto.setClientKey((String) loginParameter.getExtra(LoginHelper.CLIENT_KEY)); - dto.setDeviceType(loginParameter.getDeviceType()); - dto.setDeptName((String) loginParameter.getExtra(LoginHelper.DEPT_NAME_KEY)); - TenantHelper.dynamic(tenantId, () -> { - if(loginParameter.getTimeout() == -1) { - RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto); - } else { - RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(loginParameter.getTimeout())); - } - }); - // 记录登录日志 - LogininforEvent logininforEvent = new LogininforEvent(); - logininforEvent.setTenantId(tenantId); - logininforEvent.setUsername(username); - logininforEvent.setStatus(Constants.LOGIN_SUCCESS); - logininforEvent.setMessage(MessageUtils.message("user.login.success")); - logininforEvent.setRequest(ServletUtils.getRequest()); - SpringUtils.context().publishEvent(logininforEvent); - // 更新登录信息 - loginService.recordLoginInfo((Long) loginParameter.getExtra(LoginHelper.USER_KEY), ip); - log.info("user doLogin, userId:{}, token:{}", loginId, tokenValue); - } - - /** - * 每次注销时触发 - */ - @Override - public void doLogout(String loginType, Object loginId, String tokenValue) { - String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY)); - TenantHelper.dynamic(tenantId, () -> { - RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); - }); - log.info("user doLogout, userId:{}, token:{}", loginId, tokenValue); - } - - /** - * 每次被踢下线时触发 - */ - @Override - public void doKickout(String loginType, Object loginId, String tokenValue) { - String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY)); - TenantHelper.dynamic(tenantId, () -> { - RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); - }); - log.info("user doKickout, userId:{}, token:{}", loginId, tokenValue); - } - - /** - * 每次被顶下线时触发 - */ - @Override - public void doReplaced(String loginType, Object loginId, String tokenValue) { - String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY)); - TenantHelper.dynamic(tenantId, () -> { - RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); - }); - log.info("user doReplaced, userId:{}, token:{}", loginId, tokenValue); - } - - /** - * 每次被封禁时触发 - */ - @Override - public void doDisable(String loginType, Object loginId, String service, int level, long disableTime) { - } - - /** - * 每次被解封时触发 - */ - @Override - public void doUntieDisable(String loginType, Object loginId, String service) { - } - - /** - * 每次打开二级认证时触发 - */ - @Override - public void doOpenSafe(String loginType, String tokenValue, String service, long safeTime) { - } - - /** - * 每次创建Session时触发 - */ - @Override - public void doCloseSafe(String loginType, String tokenValue, String service) { - } - - /** - * 每次创建Session时触发 - */ - @Override - public void doCreateSession(String id) { - } - - /** - * 每次注销Session时触发 - */ - @Override - public void doLogoutSession(String id) { - } - - /** - * 每次Token续期时触发 - */ - @Override - public void doRenewTimeout(String tokenValue, Object loginId, long timeout) { - } -} +//package com.fuyuanshen.web.listener; +// +//import cn.dev33.satoken.listener.SaTokenListener; +//import cn.dev33.satoken.stp.StpUtil; +//import cn.dev33.satoken.stp.parameter.SaLoginParameter; +//import cn.hutool.core.convert.Convert; +//import cn.hutool.http.useragent.UserAgent; +//import cn.hutool.http.useragent.UserAgentUtil; +//import lombok.RequiredArgsConstructor; +//import lombok.extern.slf4j.Slf4j; +//import com.fuyuanshen.common.core.constant.CacheConstants; +//import com.fuyuanshen.common.core.constant.Constants; +//import com.fuyuanshen.common.core.domain.dto.UserOnlineDTO; +//import com.fuyuanshen.common.core.utils.MessageUtils; +//import com.fuyuanshen.common.core.utils.ServletUtils; +//import com.fuyuanshen.common.core.utils.SpringUtils; +//import com.fuyuanshen.common.core.utils.ip.AddressUtils; +//import com.fuyuanshen.common.log.event.LogininforEvent; +//import com.fuyuanshen.common.redis.utils.RedisUtils; +//import com.fuyuanshen.common.satoken.utils.LoginHelper; +//import com.fuyuanshen.common.tenant.helper.TenantHelper; +//import com.fuyuanshen.web.service.SysLoginService; +//import org.springframework.stereotype.Component; +// +//import java.time.Duration; +// +///** +// * 用户行为 侦听器的实现 +// * +// * @author Lion Li +// */ +//@RequiredArgsConstructor +//@Component +//@Slf4j +//public class UserActionListener implements SaTokenListener { +// +// private final SysLoginService loginService; +// +// /** +// * 每次登录时触发 +// */ +// @Override +// public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginParameter loginParameter) { +// UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent")); +// String ip = ServletUtils.getClientIP(); +// UserOnlineDTO dto = new UserOnlineDTO(); +// dto.setIpaddr(ip); +// dto.setLoginLocation(AddressUtils.getRealAddressByIP(ip)); +// dto.setBrowser(userAgent.getBrowser().getName()); +// dto.setOs(userAgent.getOs().getName()); +// dto.setLoginTime(System.currentTimeMillis()); +// dto.setTokenId(tokenValue); +// String username = (String) loginParameter.getExtra(LoginHelper.USER_NAME_KEY); +// String tenantId = (String) loginParameter.getExtra(LoginHelper.TENANT_KEY); +// dto.setUserName(username); +// dto.setClientKey((String) loginParameter.getExtra(LoginHelper.CLIENT_KEY)); +// dto.setDeviceType(loginParameter.getDeviceType()); +// dto.setDeptName((String) loginParameter.getExtra(LoginHelper.DEPT_NAME_KEY)); +// TenantHelper.dynamic(tenantId, () -> { +// if(loginParameter.getTimeout() == -1) { +// RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto); +// } else { +// RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(loginParameter.getTimeout())); +// } +// }); +// // 记录登录日志 +// LogininforEvent logininforEvent = new LogininforEvent(); +// logininforEvent.setTenantId(tenantId); +// logininforEvent.setUsername(username); +// logininforEvent.setStatus(Constants.LOGIN_SUCCESS); +// logininforEvent.setMessage(MessageUtils.message("user.login.success")); +// logininforEvent.setRequest(ServletUtils.getRequest()); +// SpringUtils.context().publishEvent(logininforEvent); +// // 更新登录信息 +// loginService.recordLoginInfo((Long) loginParameter.getExtra(LoginHelper.USER_KEY), ip); +// log.info("user doLogin, userId:{}, token:{}", loginId, tokenValue); +// } +// +// /** +// * 每次注销时触发 +// */ +// @Override +// public void doLogout(String loginType, Object loginId, String tokenValue) { +// String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY)); +// TenantHelper.dynamic(tenantId, () -> { +// RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); +// }); +// log.info("user doLogout, userId:{}, token:{}", loginId, tokenValue); +// } +// +// /** +// * 每次被踢下线时触发 +// */ +// @Override +// public void doKickout(String loginType, Object loginId, String tokenValue) { +// String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY)); +// TenantHelper.dynamic(tenantId, () -> { +// RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); +// }); +// log.info("user doKickout, userId:{}, token:{}", loginId, tokenValue); +// } +// +// /** +// * 每次被顶下线时触发 +// */ +// @Override +// public void doReplaced(String loginType, Object loginId, String tokenValue) { +// String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY)); +// TenantHelper.dynamic(tenantId, () -> { +// RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); +// }); +// log.info("user doReplaced, userId:{}, token:{}", loginId, tokenValue); +// } +// +// /** +// * 每次被封禁时触发 +// */ +// @Override +// public void doDisable(String loginType, Object loginId, String service, int level, long disableTime) { +// } +// +// /** +// * 每次被解封时触发 +// */ +// @Override +// public void doUntieDisable(String loginType, Object loginId, String service) { +// } +// +// /** +// * 每次打开二级认证时触发 +// */ +// @Override +// public void doOpenSafe(String loginType, String tokenValue, String service, long safeTime) { +// } +// +// /** +// * 每次创建Session时触发 +// */ +// @Override +// public void doCloseSafe(String loginType, String tokenValue, String service) { +// } +// +// /** +// * 每次创建Session时触发 +// */ +// @Override +// public void doCreateSession(String id) { +// } +// +// /** +// * 每次注销Session时触发 +// */ +// @Override +// public void doLogoutSession(String id) { +// } +// +// /** +// * 每次Token续期时触发 +// */ +// @Override +// public void doRenewTimeout(String tokenValue, Object loginId, long timeout) { +// } +//} diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/service/DeviceShareService.java b/fys-admin/src/main/java/com/fuyuanshen/web/service/DeviceShareService.java new file mode 100644 index 00000000..d73eaa7f --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/web/service/DeviceShareService.java @@ -0,0 +1,235 @@ +package com.fuyuanshen.web.service; + +import com.alibaba.fastjson2.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fuyuanshen.app.domain.AppDeviceShare; +import com.fuyuanshen.app.domain.AppPersonnelInfo; +import com.fuyuanshen.app.domain.bo.AppDeviceShareBo; +import com.fuyuanshen.app.domain.vo.AppDeviceShareDetailVo; +import com.fuyuanshen.app.domain.vo.AppDeviceShareVo; +import com.fuyuanshen.app.domain.vo.AppPersonnelInfoVo; +import com.fuyuanshen.app.mapper.AppDeviceShareMapper; +import com.fuyuanshen.app.mapper.AppPersonnelInfoMapper; +import com.fuyuanshen.common.core.constant.GlobalConstants; +import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.redis.utils.RedisUtils; +import com.fuyuanshen.common.satoken.utils.AppLoginHelper; +import com.fuyuanshen.equipment.domain.Device; +import com.fuyuanshen.equipment.domain.DeviceType; +import com.fuyuanshen.equipment.mapper.DeviceMapper; +import com.fuyuanshen.equipment.mapper.DeviceTypeMapper; +import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +import static com.fuyuanshen.common.core.constant.GlobalConstants.GLOBAL_REDIS_KEY; +import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.*; + + +@RequiredArgsConstructor +@Slf4j +@Service +public class DeviceShareService { + + private final AppDeviceShareMapper appDeviceShareMapper; + + private final DeviceMapper deviceMapper; + + private final DeviceTypeMapper deviceTypeMapper; + + private final AppPersonnelInfoMapper appPersonnelInfoMapper; + + public TableDataInfo queryPageList(AppDeviceShareBo bo, PageQuery pageQuery) { + Long userId = AppLoginHelper.getUserId(); + bo.setCreateBy(userId); + Page page = new Page<>(pageQuery.getPageNum(), pageQuery.getPageSize()); + Page result = appDeviceShareMapper.selectAppDeviceShareList(bo, page); + List records = result.getRecords(); + records.forEach(DeviceShareService::buildDeviceStatus); + return TableDataInfo.build(result); + } + + private static void buildDeviceStatus(AppDeviceShareVo item) { + //设备在线状态 + String onlineStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX+ item.getDeviceImei() + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX); + if(StringUtils.isNotBlank(onlineStatus)){ + + item.setOnlineStatus(1); + }else{ + item.setOnlineStatus(0); + } + String deviceStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX+ item.getDeviceImei() + DEVICE_STATUS_KEY_PREFIX); + // 获取电量 + if(StringUtils.isNotBlank(deviceStatus)){ + JSONObject jsonObject = JSONObject.parseObject(deviceStatus); + item.setBattery(jsonObject.getString("batteryPercentage")); + }else{ + item.setBattery("0"); + } + + String location = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY +DEVICE_KEY_PREFIX+ item.getDeviceImei()+ DEVICE_LOCATION_KEY_PREFIX); + if(StringUtils.isNotBlank(location)){ + JSONObject jsonObject = JSONObject.parseObject(location); + item.setLatitude(jsonObject.getString("latitude")); + item.setLongitude(jsonObject.getString("longitude")); + } + + String alarmStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY +DEVICE_KEY_PREFIX+ item.getDeviceImei()+ DEVICE_ALARM_KEY_PREFIX); + if(StringUtils.isNotBlank(alarmStatus)){ + item.setAlarmStatus(alarmStatus); + } + } + + public AppDeviceShareDetailVo getInfo(Long id) { + + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(AppDeviceShare::getId, id); + List appDeviceShareVos = appDeviceShareMapper.selectVoList(queryWrapper); + if(appDeviceShareVos==null || appDeviceShareVos.isEmpty()){ + return null; + } + + AppDeviceShareVo shareVo = appDeviceShareVos.get(0); + AppDeviceShareDetailVo shareDetailVo = new AppDeviceShareDetailVo(); + shareDetailVo.setId(shareVo.getId()); + shareDetailVo.setDeviceId(shareVo.getDeviceId()); + shareDetailVo.setPhonenumber(shareVo.getPhonenumber()); + shareDetailVo.setPermission(shareVo.getPermission()); + + Device device = deviceMapper.selectById(shareVo.getDeviceId()); + shareDetailVo.setDeviceName(device.getDeviceName()); + shareDetailVo.setDeviceImei(device.getDeviceImei()); + shareDetailVo.setDeviceMac(device.getDeviceMac()); + + DeviceType deviceType = deviceTypeMapper.selectById(device.getDeviceType()); + if(deviceType!=null){ + shareDetailVo.setCommunicationMode(Integer.valueOf(deviceType.getCommunicationMode())); + } + shareDetailVo.setDevicePic(device.getDevicePic()); + shareDetailVo.setTypeName(deviceType.getTypeName()); + shareDetailVo.setBluetoothName(device.getBluetoothName()); + shareDetailVo.setDeviceStatus(device.getDeviceStatus()); + shareDetailVo.setSendMsg(device.getSendMsg()); + + LambdaQueryWrapper qw = new LambdaQueryWrapper<>(); + qw.eq(AppPersonnelInfo::getDeviceId, device.getId()); + List appPersonnelInfoVos = appPersonnelInfoMapper.selectVoList(qw); + if(appPersonnelInfoVos!=null && !appPersonnelInfoVos.isEmpty()){ + shareDetailVo.setPersonnelInfo(appPersonnelInfoVos.get(0)); + } + //设备在线状态 + String onlineStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + device.getDeviceImei()+ DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX); + if(StringUtils.isNotBlank(onlineStatus)){ + shareDetailVo.setOnlineStatus(1); + }else{ + shareDetailVo.setOnlineStatus(0); + } + String deviceStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + device.getDeviceImei() + DeviceRedisKeyConstants.DEVICE_STATUS_KEY_PREFIX); + // 获取电量 + if(StringUtils.isNotBlank(deviceStatus)){ + JSONObject jsonObject = JSONObject.parseObject(deviceStatus); + shareDetailVo.setMainLightMode(jsonObject.getString("mainLightMode")); + shareDetailVo.setLaserLightMode(jsonObject.getString("laserLightMode")); + shareDetailVo.setBatteryPercentage(jsonObject.getString("batteryPercentage")); + shareDetailVo.setChargeState(jsonObject.getString("chargeState")); + shareDetailVo.setBatteryRemainingTime(jsonObject.getString("batteryRemainingTime")); + }else{ + shareDetailVo.setBatteryPercentage("0"); + } + + // 获取经度纬度 + String locationKey = GlobalConstants.GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + device.getDeviceImei() + DeviceRedisKeyConstants.DEVICE_LOCATION_KEY_PREFIX; + String locationInfo = RedisUtils.getCacheObject(locationKey); + if(StringUtils.isNotBlank(locationInfo)){ + JSONObject jsonObject = JSONObject.parseObject(locationInfo); + shareDetailVo.setLongitude(jsonObject.get("longitude").toString()); + shareDetailVo.setLatitude(jsonObject.get("latitude").toString()); + shareDetailVo.setAddress((String)jsonObject.get("address")); + } + + + String alarmStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY +DEVICE_KEY_PREFIX+ device.getDeviceImei()+ DEVICE_ALARM_KEY_PREFIX); + if(StringUtils.isNotBlank(alarmStatus)){ + shareDetailVo.setAlarmStatus(alarmStatus); + } + + String lightBrightness = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY +DEVICE_KEY_PREFIX+ device.getDeviceImei()+ DEVICE_LIGHT_BRIGHTNESS_KEY_PREFIX); + if(StringUtils.isNotBlank(lightBrightness)){ + shareDetailVo.setLightBrightness(lightBrightness); + } + + return shareDetailVo; + } + /** + * 校验短信验证码 + */ + private boolean validateSmsCode(String tenantId, String phonenumber, String smsCode) { + String code = RedisUtils.getCacheObject(GlobalConstants.DEVICE_SHARE_CODES_KEY + phonenumber); + if (StringUtils.isBlank(code)) { + throw new ServiceException("验证码失效"); + } + return code.equals(smsCode); + } + public int deviceShare(AppDeviceShareBo bo) { + boolean flag = validateSmsCode(AppLoginHelper.getTenantId(), bo.getPhonenumber(), bo.getSmsCode()); + if(!flag){ + throw new ServiceException("验证码错误"); + } + + Device device = deviceMapper.selectById(bo.getDeviceId()); + if(device==null){ + throw new ServiceException("设备不存在"); + } + Long userId = AppLoginHelper.getUserId(); + LambdaQueryWrapper lqw = new LambdaQueryWrapper<>(); + lqw.eq(AppDeviceShare::getDeviceId, bo.getDeviceId()); + lqw.eq(AppDeviceShare::getPhonenumber, bo.getPhonenumber()); + Long count = appDeviceShareMapper.selectCount(lqw); + if(count>0){ + + UpdateWrapper uw = new UpdateWrapper<>(); + uw.eq("device_id", bo.getDeviceId()); + uw.eq("phonenumber", bo.getPhonenumber()); + uw.set("permission", bo.getPermission()); + uw.set("update_by", userId); + uw.set("update_time", new Date()); + + return appDeviceShareMapper.update(uw); + }else { + AppDeviceShare appDeviceShare = new AppDeviceShare(); + appDeviceShare.setDeviceId(bo.getDeviceId()); + appDeviceShare.setPhonenumber(bo.getPhonenumber()); + appDeviceShare.setPermission(bo.getPermission()); + appDeviceShare.setCreateBy(userId); + return appDeviceShareMapper.insert(appDeviceShare); + } + } + + public int remove(Long[] ids) { + return appDeviceShareMapper.deleteByIds(Arrays.asList(ids)); + } + + public TableDataInfo otherDeviceShareList(AppDeviceShareBo bo, PageQuery pageQuery) { + String username = AppLoginHelper.getUsername(); + bo.setPhonenumber(username); + Page page = new Page<>(pageQuery.getPageNum(), pageQuery.getPageSize()); + IPage result = appDeviceShareMapper.otherDeviceShareList(bo, page); + List records = result.getRecords(); + + records.forEach(DeviceShareService::buildDeviceStatus); + return TableDataInfo.build(result); + } + + +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/service/WEBDeviceService.java b/fys-admin/src/main/java/com/fuyuanshen/web/service/WEBDeviceService.java index 02747174..fe84f3b9 100644 --- a/fys-admin/src/main/java/com/fuyuanshen/web/service/WEBDeviceService.java +++ b/fys-admin/src/main/java/com/fuyuanshen/web/service/WEBDeviceService.java @@ -10,6 +10,7 @@ import com.fuyuanshen.equipment.domain.form.DeviceForm; import com.fuyuanshen.equipment.domain.query.DeviceQueryCriteria; import com.fuyuanshen.equipment.domain.vo.AppDeviceVo; import com.fuyuanshen.equipment.domain.vo.CustomerVo; +import com.fuyuanshen.equipment.domain.vo.WebDeviceVo; import java.io.IOException; import java.util.List; @@ -29,4 +30,12 @@ public interface WEBDeviceService extends IService { */ int webUnBindDevice(Long id, Long userId); + /** + * WEB端设备详情 + * + * @param id + * @return + */ + WebDeviceVo getDevice(Long id); + } diff --git a/fys-admin/src/main/java/com/fuyuanshen/app/service/AppDeviceBizService.java b/fys-admin/src/main/java/com/fuyuanshen/web/service/device/DeviceBJQBizService.java similarity index 57% rename from fys-admin/src/main/java/com/fuyuanshen/app/service/AppDeviceBizService.java rename to fys-admin/src/main/java/com/fuyuanshen/web/service/device/DeviceBJQBizService.java index ac36e82f..9ee8236e 100644 --- a/fys-admin/src/main/java/com/fuyuanshen/app/service/AppDeviceBizService.java +++ b/fys-admin/src/main/java/com/fuyuanshen/web/service/device/DeviceBJQBizService.java @@ -1,41 +1,31 @@ -package com.fuyuanshen.app.service; +package com.fuyuanshen.web.service.device; -import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.bean.BeanUtil; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import com.fuyuanshen.app.domain.AppDeviceBindRecord; import com.fuyuanshen.app.domain.AppPersonnelInfo; +import com.fuyuanshen.app.domain.AppPersonnelInfoRecords; import com.fuyuanshen.app.domain.bo.AppPersonnelInfoBo; -import com.fuyuanshen.app.domain.dto.APPReNameDTO; import com.fuyuanshen.app.domain.dto.AppDeviceLogoUploadDto; import com.fuyuanshen.app.domain.dto.DeviceInstructDto; -import com.fuyuanshen.app.domain.vo.APPDeviceTypeVo; import com.fuyuanshen.app.domain.vo.AppDeviceDetailVo; import com.fuyuanshen.app.domain.vo.AppPersonnelInfoVo; -import com.fuyuanshen.app.mapper.AppDeviceBindRecordMapper; import com.fuyuanshen.app.mapper.AppPersonnelInfoMapper; -import com.fuyuanshen.app.mapper.equipment.APPDeviceMapper; +import com.fuyuanshen.app.mapper.AppPersonnelInfoRecordsMapper; import com.fuyuanshen.common.core.constant.GlobalConstants; import com.fuyuanshen.common.core.exception.ServiceException; import com.fuyuanshen.common.core.utils.*; -import com.fuyuanshen.common.mybatis.core.page.PageQuery; -import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; import com.fuyuanshen.common.redis.utils.RedisUtils; import com.fuyuanshen.common.satoken.utils.AppLoginHelper; import com.fuyuanshen.equipment.domain.Device; import com.fuyuanshen.equipment.domain.DeviceType; -import com.fuyuanshen.equipment.domain.dto.AppDeviceBo; import com.fuyuanshen.equipment.domain.dto.AppDeviceSendMsgBo; -import com.fuyuanshen.equipment.domain.query.DeviceQueryCriteria; -import com.fuyuanshen.equipment.domain.vo.AppDeviceVo; -import com.fuyuanshen.equipment.enums.BindingStatusEnum; -import com.fuyuanshen.equipment.enums.CommunicationModeEnum; +import com.fuyuanshen.equipment.enums.LightModeEnum; +import com.fuyuanshen.equipment.mapper.DeviceLogMapper; import com.fuyuanshen.equipment.mapper.DeviceMapper; import com.fuyuanshen.equipment.mapper.DeviceTypeMapper; -import com.fuyuanshen.equipment.utils.c.ReliableTextToBitmap; import com.fuyuanshen.global.mqtt.config.MqttGateway; import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants; import com.fuyuanshen.global.mqtt.constants.MqttConstants; @@ -45,7 +35,6 @@ import org.springframework.core.io.ClassPathResource; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; -import java.io.IOException; import java.io.InputStream; import java.time.Duration; import java.util.*; @@ -54,34 +43,20 @@ import static com.fuyuanshen.common.core.constant.GlobalConstants.GLOBAL_REDIS_K import static com.fuyuanshen.common.core.utils.Bitmap80x12Generator.buildArr; import static com.fuyuanshen.common.core.utils.Bitmap80x12Generator.generateFixedBitmapData; import static com.fuyuanshen.common.core.utils.ImageToCArrayConverter.convertHexToDecimal; +import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.*; @Slf4j @Service @RequiredArgsConstructor -public class AppDeviceBizService { +public class DeviceBJQBizService { - private final APPDeviceMapper appDeviceMapper; private final DeviceMapper deviceMapper; private final AppPersonnelInfoMapper appPersonnelInfoMapper; + private final AppPersonnelInfoRecordsMapper appPersonnelInfoRecordsMapper; private final DeviceTypeMapper deviceTypeMapper; private final MqttGateway mqttGateway; - private final AppDeviceBindRecordMapper appDeviceBindRecordMapper; - - - public List getTypeList() { - Long userId = AppLoginHelper.getUserId(); - return appDeviceMapper.getTypeList(userId); - } - - public void reName(APPReNameDTO reNameDTO) { - UpdateWrapper updateWrapper = new UpdateWrapper<>(); - updateWrapper.eq("id", reNameDTO.getId()) - .eq("binding_user_id", AppLoginHelper.getUserId()) - .set("device_name", reNameDTO.getDeviceName()); - deviceMapper.update(updateWrapper); - } - + private final DeviceLogMapper deviceLogMapper; public int sendMessage(AppDeviceSendMsgBo bo) { List deviceIds = bo.getDeviceIds(); @@ -93,22 +68,22 @@ public class AppDeviceBizService { if (device == null) { throw new ServiceException("设备不存在" + deviceId); } - + if(getDeviceStatus(device.getDeviceImei())){ + throw new ServiceException(device.getDeviceName()+",设备已断开连接"); + } try { ClassPathResource resource = new ClassPathResource("image/background.png"); InputStream inputStream = resource.getInputStream(); -// String backgroundImagePath = "D:\\background.png"; // 替换为实际背景图片路径 byte[] largeData = ImageWithTextGenerate.generate160x80ImageWithText2(bo.getSendMsg(), inputStream, 25600); int[] ints = convertHexToDecimal(largeData); - RedisUtils.setCacheObject(GLOBAL_REDIS_KEY+"app_send_message_data:" + device.getDeviceImei(), Arrays.toString(ints), Duration.ofSeconds(30 * 60L)); + RedisUtils.setCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + device.getDeviceImei() + ":app_send_message_data" , Arrays.toString(ints), Duration.ofSeconds(5 * 60L)); - String data = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+"app_send_message_data:" + device.getDeviceImei()); + String data = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + device.getDeviceImei() + ":app_send_message_data"); byte[] arr = ImageToCArrayConverter.convertStringToByteArray(data); byte[] specificChunk = ImageToCArrayConverter.getChunk(arr, 0, 512); - System.out.println("第0块数据大小: " + specificChunk.length + " 字节"); - System.out.println("第0块数据: " + Arrays.toString(specificChunk)); + log.info("发送信息第0块数据大小: {} 字节",specificChunk.length); ArrayList intData = new ArrayList<>(); intData.add(6); @@ -121,189 +96,48 @@ public class AppDeviceBizService { Map map = new HashMap<>(); map.put("instruct", intData); mqttGateway.sendMsgToMqtt(MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(), 1 , JSON.toJSONString(map)); - log.info("发送点阵数据到设备消息=>topic:{},payload:{}", MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(),JSON.toJSONString(map)); + log.info("发送信息点阵数据到设备消息=>topic:{},payload:{}", MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(),JSON.toJSONString(map)); UpdateWrapper updateWrapper = new UpdateWrapper<>(); updateWrapper.eq("id", deviceId) .eq("binding_user_id", AppLoginHelper.getUserId()) .set("send_msg", bo.getSendMsg()); deviceMapper.update(updateWrapper); + + recordDeviceLog(deviceId, device.getDeviceName(), "发送信息", bo.getSendMsg(), AppLoginHelper.getUserId()); } catch (Exception e) { - log.info("设备发送信息失败:{}" ,deviceId); + log.info("发送信息设备发送信息失败:{}" ,deviceId); + throw new ServiceException("发送指令失败"); } } + + return 1; } + /** + * 记录设备操作日志 + * @param deviceId 设备ID + * @param content 日志内容 + * @param operator 操作人 + */ + private void recordDeviceLog(Long deviceId,String deviceName, String deviceAction, String content, Long operator) { + try { + // 创建设备日志实体 + com.fuyuanshen.equipment.domain.DeviceLog deviceLog = new com.fuyuanshen.equipment.domain.DeviceLog(); + deviceLog.setDeviceId(deviceId); + deviceLog.setDeviceAction(deviceAction); + deviceLog.setContent(content); + deviceLog.setCreateBy(operator); + deviceLog.setDeviceName(deviceName); + deviceLog.setCreateTime(new Date()); - public TableDataInfo queryAppDeviceList(DeviceQueryCriteria bo, PageQuery pageQuery) { - if (bo.getBindingUserId() == null) { - Long userId = AppLoginHelper.getUserId(); - bo.setBindingUserId(userId); + // 插入日志记录 + deviceLogMapper.insert(deviceLog); + } catch (Exception e) { + log.error("记录设备操作日志失败: {}", e.getMessage(), e); } - Page result = deviceMapper.queryAppBindDeviceList(pageQuery.build(), bo); - List records = result.getRecords(); - if(records != null && !records.isEmpty()){ - records.forEach(item -> { - if(item.getCommunicationMode()!=null && item.getCommunicationMode() == 0){ - - //设备在线状态 - String onlineStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX + item.getDeviceImei()); - if(StringUtils.isNotBlank(onlineStatus)){ - - item.setOnlineStatus(1); - }else{ - item.setOnlineStatus(0); - } - String deviceStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+DeviceRedisKeyConstants.DEVICE_STATUS_KEY_PREFIX + item.getDeviceImei()); - // 获取电量 - if(StringUtils.isNotBlank(deviceStatus)){ - JSONObject jsonObject = JSONObject.parseObject(deviceStatus); - item.setBattery(jsonObject.getString("batteryPercentage")); - }else{ - item.setBattery("0"); - } - - String location = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+DeviceRedisKeyConstants.DEVICE_LOCATION_KEY_PREFIX + item.getDeviceImei()); - // 获取电量 - if(StringUtils.isNotBlank(location)){ - JSONObject jsonObject = JSONObject.parseObject(location); - item.setLatitude(jsonObject.getString("latitude")); - item.setLongitude(jsonObject.getString("longitude")); - }else{ - item.setBattery("0"); - } - } - }); - } - return TableDataInfo.build(result); - } - - public int bindDevice(AppDeviceBo bo) { - Integer mode = bo.getCommunicationMode(); - Long userId = AppLoginHelper.getUserId(); - if (mode == CommunicationModeEnum.FOUR_G.getValue()) { - - String deviceImei = bo.getDeviceImei(); - QueryWrapper qw = new QueryWrapper() - .eq("device_imei", deviceImei); - List devices = deviceMapper.selectList(qw); - if (devices.isEmpty()) { - throw new RuntimeException("请先将设备入库!!!"); - } - Device device = devices.get(0); - if (device.getBindingStatus() != null && device.getBindingStatus() == BindingStatusEnum.BOUND.getCode()) { - throw new RuntimeException("设备已绑定"); - } - - QueryWrapper bindRecordQueryWrapper = new QueryWrapper<>(); - bindRecordQueryWrapper.eq("device_id", device.getId()); - AppDeviceBindRecord appDeviceBindRecord = appDeviceBindRecordMapper.selectOne(bindRecordQueryWrapper); - if (appDeviceBindRecord != null) { - UpdateWrapper deviceUpdateWrapper = new UpdateWrapper<>(); - deviceUpdateWrapper.eq("device_id", device.getId()) - .set("binding_status", BindingStatusEnum.BOUND.getCode()) - .set("binding_user_id", userId) - .set("update_time", new Date()) - .set("binding_time", new Date()); - return appDeviceBindRecordMapper.update(null, deviceUpdateWrapper); - } else { - AppDeviceBindRecord bindRecord = new AppDeviceBindRecord(); - bindRecord.setDeviceId(device.getId()); - bindRecord.setBindingUserId(userId); - bindRecord.setBindingTime(new Date()); - bindRecord.setCreateBy(userId); - appDeviceBindRecordMapper.insert(bindRecord); - } - - UpdateWrapper deviceUpdateWrapper = new UpdateWrapper<>(); - deviceUpdateWrapper.eq("id", device.getId()) - .set("binding_status", BindingStatusEnum.BOUND.getCode()) - .set("binding_user_id", userId) - .set("binding_time", new Date()); - return deviceMapper.update(null, deviceUpdateWrapper); - } else if (mode == CommunicationModeEnum.BLUETOOTH.getValue()) { - String deviceMac = bo.getDeviceMac(); - QueryWrapper qw = new QueryWrapper() - .eq("device_mac", deviceMac); - List devices = deviceMapper.selectList(qw); - if (devices.isEmpty()) { - throw new RuntimeException("请先将设备入库!!!"); - } - Device device = devices.get(0); - - QueryWrapper bindRecordQueryWrapper = new QueryWrapper<>(); - bindRecordQueryWrapper.eq("device_id", device.getId()); - bindRecordQueryWrapper.eq("binding_user_id", userId); - AppDeviceBindRecord appDeviceBindRecord = appDeviceBindRecordMapper.selectOne(bindRecordQueryWrapper); - if (appDeviceBindRecord != null) { - UpdateWrapper deviceUpdateWrapper = new UpdateWrapper<>(); - deviceUpdateWrapper.eq("device_id", device.getId()) - .eq("binding_user_id", userId) - .set("binding_user_id", userId) - .set("binding_time", new Date()); - return appDeviceBindRecordMapper.update(null, deviceUpdateWrapper); - } else { - AppDeviceBindRecord bindRecord = new AppDeviceBindRecord(); - bindRecord.setDeviceId(device.getId()); - bindRecord.setBindingUserId(userId); - bindRecord.setBindingTime(new Date()); - bindRecord.setCreateBy(userId); - appDeviceBindRecordMapper.insert(bindRecord); - } - - UpdateWrapper deviceUpdateWrapper = new UpdateWrapper<>(); - deviceUpdateWrapper.eq("id", device.getId()) - .set("binding_status", BindingStatusEnum.BOUND.getCode()) - .set("binding_user_id", userId) - .set("binding_time", new Date()); - return deviceMapper.update(null, deviceUpdateWrapper); - } else { - throw new RuntimeException("通讯方式错误"); - } - - } - - - public int unBindDevice(Long id) { - return unBindDevice(id, null, 1); - } - - public int unBindDevice(Long id, Long userId, int type) { - Device device = deviceMapper.selectById(id); - if (device == null) { - throw new RuntimeException("请先将设备入库!!!"); - } - UpdateWrapper deviceUpdateWrapper = new UpdateWrapper<>(); - deviceUpdateWrapper.eq("id", device.getId()) - .set("binding_user_id", null) - .set("binding_status", BindingStatusEnum.UNBOUND.getCode()) - .set("binding_time", null); - deviceMapper.update(null, deviceUpdateWrapper); - - if (userId == null) { - userId = AppLoginHelper.getUserId(); - } - QueryWrapper bindRecordQueryWrapper = new QueryWrapper<>(); - bindRecordQueryWrapper.eq("device_id", device.getId()); - // 设备端解绑 0:设备端解绑 1:web端解绑 - if (type == 1) { - bindRecordQueryWrapper.eq("binding_user_id", userId); - } - - // AppDeviceBindRecord appDeviceBindRecord = appDeviceBindRecordMapper.selectOne(bindRecordQueryWrapper); - // if (appDeviceBindRecord != null) { - // return appDeviceBindRecordMapper.deleteById(appDeviceBindRecord.getId()); - // } - - List appDeviceBindRecordList = appDeviceBindRecordMapper.selectList(bindRecordQueryWrapper); - if (CollectionUtil.isNotEmpty(appDeviceBindRecordList)) { - appDeviceBindRecordList.forEach(appDeviceBindRecord -> - appDeviceBindRecordMapper.deleteById(appDeviceBindRecord.getId())); - } - - return 1; } @@ -312,6 +146,7 @@ public class AppDeviceBizService { if (device == null) { throw new RuntimeException("请先将设备入库!!!"); } + AppDeviceDetailVo vo = new AppDeviceDetailVo(); vo.setDeviceId(device.getId()); vo.setDeviceName(device.getDeviceName()); @@ -336,13 +171,13 @@ public class AppDeviceBizService { vo.setPersonnelInfo(personnelInfoVo); } //设备在线状态 - String onlineStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX + device.getDeviceImei()); + String onlineStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + device.getDeviceImei()+ DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX); if(StringUtils.isNotBlank(onlineStatus)){ vo.setOnlineStatus(1); }else{ vo.setOnlineStatus(0); } - String deviceStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+DeviceRedisKeyConstants.DEVICE_STATUS_KEY_PREFIX + device.getDeviceImei()); + String deviceStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + device.getDeviceImei() + DeviceRedisKeyConstants.DEVICE_STATUS_KEY_PREFIX); // 获取电量 if(StringUtils.isNotBlank(deviceStatus)){ JSONObject jsonObject = JSONObject.parseObject(deviceStatus); @@ -356,7 +191,7 @@ public class AppDeviceBizService { } // 获取经度纬度 - String locationKey = GlobalConstants.GLOBAL_REDIS_KEY+DeviceRedisKeyConstants.DEVICE_LOCATION_KEY_PREFIX + device.getDeviceImei(); + String locationKey = GlobalConstants.GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + device.getDeviceImei() + DeviceRedisKeyConstants.DEVICE_LOCATION_KEY_PREFIX; String locationInfo = RedisUtils.getCacheObject(locationKey); if(StringUtils.isNotBlank(locationInfo)){ JSONObject jsonObject = JSONObject.parseObject(locationInfo); @@ -365,16 +200,30 @@ public class AppDeviceBizService { vo.setAddress((String)jsonObject.get("address")); } + String alarmStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY +DEVICE_KEY_PREFIX+ device.getDeviceImei()+ DEVICE_ALARM_KEY_PREFIX); + if(StringUtils.isNotBlank(alarmStatus)){ + vo.setAlarmStatus(alarmStatus); + } + + String lightBrightness = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY +DEVICE_KEY_PREFIX+ device.getDeviceImei()+ DEVICE_LIGHT_BRIGHTNESS_KEY_PREFIX); + if(StringUtils.isNotBlank(lightBrightness)){ + vo.setLightBrightness(lightBrightness); + } + return vo; } + public boolean registerPersonInfo(AppPersonnelInfoBo bo) { Long deviceId = bo.getDeviceId(); Device deviceObj = deviceMapper.selectById(deviceId); if (deviceObj == null) { throw new RuntimeException("请先将设备入库!!!"); } + if(getDeviceStatus(deviceObj.getDeviceImei())){ + throw new ServiceException(deviceObj.getDeviceName()+",设备已断开连接"); + } QueryWrapper qw = new QueryWrapper() .eq("device_id", deviceId); List appPersonnelInfoVos = appPersonnelInfoMapper.selectVoList(qw); @@ -397,10 +246,18 @@ public class AppDeviceBizService { map.put("instruct", intData); mqttGateway.sendMsgToMqtt(MqttConstants.GLOBAL_PUB_KEY + deviceObj.getDeviceImei(), 1, JSON.toJSONString(map)); log.info("发送点阵数据到设备消息=>topic:{},payload:{}", MqttConstants.GLOBAL_PUB_KEY + deviceObj.getDeviceImei(), bo); - + String logContent = "单位:"+bo.getUnitName()+",职位:"+bo.getPosition()+",姓名:"+bo.getName()+",ID:"+bo.getCode(); + recordDeviceLog(deviceId, deviceObj.getDeviceName(), "人员信息登记", logContent, AppLoginHelper.getUserId()); if (ObjectUtils.length(appPersonnelInfoVos) == 0) { AppPersonnelInfo appPersonnelInfo = MapstructUtils.convert(bo, AppPersonnelInfo.class); - return appPersonnelInfoMapper.insertOrUpdate(appPersonnelInfo); + appPersonnelInfoMapper.insertOrUpdate(appPersonnelInfo); + + AppPersonnelInfoRecords appPersonnelInfoRecords = new AppPersonnelInfoRecords(); + BeanUtil.copyProperties(appPersonnelInfo, appPersonnelInfoRecords); + appPersonnelInfoRecords.setId(null); + appPersonnelInfoRecords.setPersonnelId(appPersonnelInfo.getId()); + + appPersonnelInfoRecordsMapper.insert(appPersonnelInfoRecords); } else { UpdateWrapper uw = new UpdateWrapper<>(); uw.eq("device_id", deviceId) @@ -408,23 +265,19 @@ public class AppDeviceBizService { .set("position", bo.getPosition()) .set("unit_name", bo.getUnitName()) .set("code", bo.getCode()); - return appPersonnelInfoMapper.update(null, uw) > 0; + appPersonnelInfoMapper.update(null, uw); + + AppPersonnelInfoVo personnelInfoVo = appPersonnelInfoVos.get(0); + AppPersonnelInfo appPersonnelInfo = MapstructUtils.convert(bo, AppPersonnelInfo.class); + AppPersonnelInfoRecords appPersonnelInfoRecords = new AppPersonnelInfoRecords(); + BeanUtil.copyProperties(appPersonnelInfo, appPersonnelInfoRecords); + appPersonnelInfoRecords.setId(null); + appPersonnelInfoRecords.setPersonnelId(personnelInfoVo.getId()); + appPersonnelInfoRecordsMapper.insert(appPersonnelInfoRecords); } - - + return true; } - - public static void main(String[] args) throws IOException { - byte[] largeData = ImageToCArrayConverter.convertImageToCArray("E:\\workspace\\demo.png", 160, 80, 25600); - System.out.println("长度:" + largeData.length); - - System.out.println("原始数据大小: " + largeData.length + " 字节"); - - int[] ints = convertHexToDecimal(largeData); - System.out.println("转换后的数据: " + Arrays.toString(ints)); - } - - public void uploadDeviceLogo(AppDeviceLogoUploadDto bo) { + public void uploadDeviceLogo2(AppDeviceLogoUploadDto bo) { try { Device device = deviceMapper.selectById(bo.getDeviceId()); if (device == null) { @@ -433,19 +286,56 @@ public class AppDeviceBizService { MultipartFile file = bo.getFile(); byte[] largeData = ImageToCArrayConverter.convertImageToCArray(file.getInputStream(), 160, 80, 25600); - System.out.println("长度:" + largeData.length); - System.out.println("原始数据大小: " + largeData.length + " 字节"); + log.info("长度:" + largeData.length); + // 在获取 largeData 后,将其与前缀合并 + byte[] prefix = new byte[]{0x50, 0x49, 0x43, 0x54, 0x55, 0x52, 0x45}; // "PICTURE" + byte[] combinedData = new byte[prefix.length + largeData.length]; + System.arraycopy(prefix, 0, combinedData, 0, prefix.length); + System.arraycopy(largeData, 0, combinedData, prefix.length, largeData.length); + + // 将 combinedData 转换为十六进制表示 + String[] hexArray = new String[combinedData.length]; + for (int i = 0; i < combinedData.length; i++) { + hexArray[i] = String.format("0x%02X", combinedData[i]); + } +// Map map = new HashMap<>(); +// map.put("instruct", combinedData); + String[] specificChunk = ImageToCArrayConverter.getChunk2(hexArray, 0, bo.getChunkSize()); + mqttGateway.sendMsgToMqtt(MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(), 1 , Arrays.toString(specificChunk)); + log.info("发送点阵数据到设备消息=>topic:{},payload:{}", MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(),Arrays.toString(specificChunk)); + + recordDeviceLog(device.getId(), device.getDeviceName(), "上传开机画面", "上传开机画面", AppLoginHelper.getUserId()); + } catch (Exception e){ + e.printStackTrace(); + throw new ServiceException("发送指令失败"); + } + } + public void uploadDeviceLogo(AppDeviceLogoUploadDto bo) { + try { + Device device = deviceMapper.selectById(bo.getDeviceId()); + if (device == null) { + throw new ServiceException("设备不存在"); + } + if(getDeviceStatus(device.getDeviceImei())){ + throw new ServiceException(device.getDeviceName()+",设备已断开连接"); + } + MultipartFile file = bo.getFile(); + + byte[] largeData = ImageToCArrayConverter.convertImageToCArray(file.getInputStream(), 160, 80, 25600); + log.info("长度:" + largeData.length); + + log.info("原始数据大小: {} 字节", largeData.length); int[] ints = convertHexToDecimal(largeData); - RedisUtils.setCacheObject(GLOBAL_REDIS_KEY+"app_logo_data:" + device.getDeviceImei(), Arrays.toString(ints), Duration.ofSeconds(30 * 60L)); + RedisUtils.setCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + device.getDeviceImei() +DEVICE_BOOT_LOGO_KEY_PREFIX, Arrays.toString(ints), Duration.ofSeconds(5 * 60L)); - String data = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+"app_logo_data:" + device.getDeviceImei()); + String data = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + device.getDeviceImei() + DEVICE_BOOT_LOGO_KEY_PREFIX); byte[] arr = ImageToCArrayConverter.convertStringToByteArray(data); byte[] specificChunk = ImageToCArrayConverter.getChunk(arr, 0, 512); - System.out.println("第0块数据大小: " + specificChunk.length + " 字节"); - System.out.println("第0块数据: " + Arrays.toString(specificChunk)); + log.info("第0块数据大小: {} 字节", specificChunk.length); +// log.info("第0块数据: {}", Arrays.toString(specificChunk)); ArrayList intData = new ArrayList<>(); intData.add(3); @@ -459,8 +349,11 @@ public class AppDeviceBizService { map.put("instruct", intData); mqttGateway.sendMsgToMqtt(MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(), 1 , JSON.toJSONString(map)); log.info("发送点阵数据到设备消息=>topic:{},payload:{}", MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(),JSON.toJSONString(map)); + + recordDeviceLog(device.getId(), device.getDeviceName(), "上传开机画面", "上传开机画面", AppLoginHelper.getUserId()); } catch (Exception e){ e.printStackTrace(); + throw new ServiceException("发送指令失败"); } } @@ -468,6 +361,7 @@ public class AppDeviceBizService { * 灯光模式 * 0(关灯),1(强光模式),2(弱光模式), 3(爆闪模式), 4(泛光模式) */ + public void lightModeSettings(DeviceInstructDto params) { try { Long deviceId = params.getDeviceId(); @@ -475,6 +369,9 @@ public class AppDeviceBizService { if(device == null){ throw new ServiceException("设备不存在"); } + if(getDeviceStatus(device.getDeviceImei())){ + throw new ServiceException(device.getDeviceName()+",设备已断开连接"); + } Integer instructValue = Integer.parseInt(params.getInstructValue()); ArrayList intData = new ArrayList<>(); intData.add(1); @@ -486,8 +383,11 @@ public class AppDeviceBizService { map.put("instruct", intData); mqttGateway.sendMsgToMqtt(MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(), 1 , JSON.toJSONString(map)); log.info("发送点阵数据到设备消息=>topic:{},payload:{}", MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(),JSON.toJSONString(map)); + LightModeEnum modeEnum = LightModeEnum.getByCode(instructValue); + recordDeviceLog(device.getId(), device.getDeviceName(), "灯光模式", modeEnum!=null?modeEnum.getName():null, AppLoginHelper.getUserId()); } catch (Exception e){ e.printStackTrace(); + throw new ServiceException("发送指令失败"); } } @@ -499,6 +399,9 @@ public class AppDeviceBizService { if(device == null){ throw new ServiceException("设备不存在"); } + if(getDeviceStatus(device.getDeviceImei())){ + throw new ServiceException(device.getDeviceName()+",设备已断开连接"); + } String instructValue = params.getInstructValue(); ArrayList intData = new ArrayList<>(); intData.add(5); @@ -518,8 +421,10 @@ public class AppDeviceBizService { map.put("instruct", intData); mqttGateway.sendMsgToMqtt(MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(), 1 , JSON.toJSONString(map)); log.info("发送点阵数据到设备消息=>topic:{},payload:{}", MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(),JSON.toJSONString(map)); + } catch (Exception e){ e.printStackTrace(); + throw new ServiceException("发送指令失败"); } } @@ -531,6 +436,9 @@ public class AppDeviceBizService { if(device == null){ throw new ServiceException("设备不存在"); } + if(getDeviceStatus(device.getDeviceImei())){ + throw new ServiceException(device.getDeviceName()+",设备已断开连接"); + } Integer instructValue = Integer.parseInt(params.getInstructValue()); ArrayList intData = new ArrayList<>(); intData.add(4); @@ -542,14 +450,19 @@ public class AppDeviceBizService { map.put("instruct", intData); mqttGateway.sendMsgToMqtt(MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(), 1 , JSON.toJSONString(map)); log.info("发送点阵数据到设备消息=>topic:{},payload:{}", MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(),JSON.toJSONString(map)); + // 1代表开启激光灯,此时主灯关闭,主灯控件为关机状态,为0代表关闭激光灯 + if("1".equals(params.getInstructValue())){ + recordDeviceLog(device.getId(), device.getDeviceName(), "激光模式设置", "开启激光灯", AppLoginHelper.getUserId()); + }else{ + recordDeviceLog(device.getId(), device.getDeviceName(), "激光模式设置", "关闭激光灯", AppLoginHelper.getUserId()); + } } catch (Exception e){ e.printStackTrace(); + throw new ServiceException("发送指令失败"); } } public String mapReverseGeocoding(DeviceInstructDto params) { -// Long deviceId = params.getDeviceId(); -// Device device = deviceMapper.selectById(deviceId); QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("device_imei", params.getDeviceImei()); List devices = deviceMapper.selectList(queryWrapper); @@ -565,13 +478,17 @@ public class AppDeviceBizService { if (deviceIds == null || deviceIds.isEmpty()) { throw new ServiceException("请选择设备"); } + for (Long deviceId : deviceIds) { Device device = deviceMapper.selectById(deviceId); if (device == null) { throw new ServiceException("设备不存在" + deviceId); } - + if(getDeviceStatus(device.getDeviceImei())){ + throw new ServiceException(device.getDeviceName()+",设备已断开连接"); + } try { + ArrayList intData = new ArrayList<>(); intData.add(7); intData.add(Integer.parseInt(bo.getInstructValue())); @@ -589,14 +506,22 @@ public class AppDeviceBizService { .eq("binding_user_id", AppLoginHelper.getUserId()) .set("send_msg", bo.getSendMsg()); deviceMapper.update(updateWrapper); + recordDeviceLog(device.getId(), device.getDeviceName(), "发送告警信息", bo.getSendMsg(), AppLoginHelper.getUserId()); } catch (Exception e) { - log.info("设备发送信息失败:{}" ,deviceId); + log.info("设备发送告警信息信息失败:{}" ,deviceId); + throw new ServiceException("设备发送告警信息信息失败"); } } } catch (Exception e){ e.printStackTrace(); + throw new ServiceException("发送告警信息指令失败"); } return 1; } + + private boolean getDeviceStatus(String deviceImei) { + String deviceOnlineStatusRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX+ deviceImei + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX ; + return StringUtils.isBlank(deviceOnlineStatusRedisKey); + } } diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/service/device/DeviceBizService.java b/fys-admin/src/main/java/com/fuyuanshen/web/service/device/DeviceBizService.java new file mode 100644 index 00000000..90114467 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/web/service/device/DeviceBizService.java @@ -0,0 +1,335 @@ +package com.fuyuanshen.web.service.device; + +import cn.hutool.core.collection.CollectionUtil; +import com.alibaba.fastjson2.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fuyuanshen.app.domain.AppDeviceBindRecord; +import com.fuyuanshen.app.domain.AppDeviceShare; +import com.fuyuanshen.app.domain.dto.APPReNameDTO; +import com.fuyuanshen.app.domain.dto.AppRealTimeStatusDto; +import com.fuyuanshen.app.domain.dto.DeviceInstructDto; +import com.fuyuanshen.app.domain.vo.APPDeviceTypeVo; +import com.fuyuanshen.app.domain.vo.AppUserVo; +import com.fuyuanshen.app.mapper.AppDeviceBindRecordMapper; +import com.fuyuanshen.app.mapper.AppDeviceShareMapper; +import com.fuyuanshen.app.mapper.AppPersonnelInfoMapper; +import com.fuyuanshen.app.mapper.AppUserMapper; +import com.fuyuanshen.app.mapper.equipment.APPDeviceMapper; +import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.utils.ObjectUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.redis.utils.RedisUtils; +import com.fuyuanshen.common.satoken.utils.AppLoginHelper; +import com.fuyuanshen.equipment.domain.Device; +import com.fuyuanshen.equipment.domain.dto.AppDeviceBo; +import com.fuyuanshen.equipment.domain.query.DeviceQueryCriteria; +import com.fuyuanshen.equipment.domain.vo.AppDeviceVo; +import com.fuyuanshen.equipment.domain.vo.WebDeviceVo; +import com.fuyuanshen.equipment.enums.BindingStatusEnum; +import com.fuyuanshen.equipment.enums.CommunicationModeEnum; +import com.fuyuanshen.equipment.mapper.DeviceLogMapper; +import com.fuyuanshen.equipment.mapper.DeviceMapper; +import com.fuyuanshen.equipment.mapper.DeviceTypeMapper; +import com.fuyuanshen.global.mqtt.config.MqttGateway; +import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants; +import com.fuyuanshen.web.service.device.status.base.DeviceStatusRule; +import com.fuyuanshen.web.service.device.status.base.RealTimeStatusEngine; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +import static com.fuyuanshen.common.core.constant.GlobalConstants.GLOBAL_REDIS_KEY; +import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.*; + + +@Slf4j +@Service +@RequiredArgsConstructor +public class DeviceBizService { + + private final APPDeviceMapper appDeviceMapper; + private final DeviceMapper deviceMapper; + private final AppDeviceBindRecordMapper appDeviceBindRecordMapper; + private final RealTimeStatusEngine realTimeStatusEngine; + private final DeviceLogMapper deviceLogMapper; + private final AppDeviceShareMapper appDeviceShareMapper; + private final AppUserMapper appUserMapper;; + + + + public List getTypeList() { + Long userId = AppLoginHelper.getUserId(); + return appDeviceMapper.getTypeList(userId); + } + + public void reName(APPReNameDTO reNameDTO) { + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + updateWrapper.eq("id", reNameDTO.getId()) + .eq("binding_user_id", AppLoginHelper.getUserId()) + .set("device_name", reNameDTO.getDeviceName()); + deviceMapper.update(updateWrapper); + } + + + public TableDataInfo queryAppDeviceList(DeviceQueryCriteria bo, PageQuery pageQuery) { + if (bo.getBindingUserId() == null) { + Long userId = AppLoginHelper.getUserId(); + bo.setBindingUserId(userId); + } + Page result = deviceMapper.queryAppBindDeviceList(pageQuery.build(), bo); + List records = result.getRecords(); + if(records != null && !records.isEmpty()){ + records.forEach(item -> { + if(item.getCommunicationMode()!=null && item.getCommunicationMode() == 0){ + + //设备在线状态 + String onlineStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX+ item.getDeviceImei() + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX); + if(StringUtils.isNotBlank(onlineStatus)){ + + item.setOnlineStatus(1); + }else{ + item.setOnlineStatus(0); + } + String deviceStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX+ item.getDeviceImei() + DEVICE_STATUS_KEY_PREFIX); + // 获取电量 + if(StringUtils.isNotBlank(deviceStatus)){ + JSONObject jsonObject = JSONObject.parseObject(deviceStatus); + item.setBattery(jsonObject.getString("batteryPercentage")); + }else{ + item.setBattery("0"); + } + + String location = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY +DEVICE_KEY_PREFIX+ item.getDeviceImei()+ DEVICE_LOCATION_KEY_PREFIX); + if(StringUtils.isNotBlank(location)){ + JSONObject jsonObject = JSONObject.parseObject(location); + item.setLatitude(jsonObject.getString("latitude")); + item.setLongitude(jsonObject.getString("longitude")); + } + + String alarmStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY +DEVICE_KEY_PREFIX+ item.getDeviceImei()+ DEVICE_ALARM_KEY_PREFIX); + if(StringUtils.isNotBlank(alarmStatus)){ + item.setAlarmStatus(alarmStatus); + } + } + }); + } + return TableDataInfo.build(result); + } + + public TableDataInfo queryWebDeviceList(DeviceQueryCriteria bo, PageQuery pageQuery) { + Page result = deviceMapper.queryWebDeviceList(pageQuery.build(), bo); + List records = result.getRecords(); + if(records != null && !records.isEmpty()){ + records.forEach(item -> { + if(item.getCommunicationMode()!=null && item.getCommunicationMode() == 0){ + + //设备在线状态 + String onlineStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX+ item.getDeviceImei() + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX); + if(StringUtils.isNotBlank(onlineStatus)){ + + item.setOnlineStatus(1); + }else{ + item.setOnlineStatus(0); + } + String deviceStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX+ item.getDeviceImei() + DEVICE_STATUS_KEY_PREFIX); + // 获取电量 + if(StringUtils.isNotBlank(deviceStatus)){ + JSONObject jsonObject = JSONObject.parseObject(deviceStatus); + item.setBattery(jsonObject.getString("batteryPercentage")); + }else{ + item.setBattery("0"); + } + + String location = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY +DEVICE_KEY_PREFIX+ item.getDeviceImei()+ DEVICE_LOCATION_KEY_PREFIX); + if(StringUtils.isNotBlank(location)){ + JSONObject jsonObject = JSONObject.parseObject(location); + item.setLatitude(jsonObject.getString("latitude")); + item.setLongitude(jsonObject.getString("longitude")); + } + + String alarmStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY +DEVICE_KEY_PREFIX+ item.getDeviceImei()+ DEVICE_ALARM_KEY_PREFIX); + if(StringUtils.isNotBlank(alarmStatus)){ + item.setAlarmStatus(alarmStatus); + } + } + }); + } + return TableDataInfo.build(result); + } + + public int bindDevice(AppDeviceBo bo) { + Integer mode = bo.getCommunicationMode(); + Long userId = AppLoginHelper.getUserId(); + if (mode == CommunicationModeEnum.FOUR_G.getValue()) { + + String deviceImei = bo.getDeviceImei(); + QueryWrapper qw = new QueryWrapper() + .eq("device_imei", deviceImei); + List devices = deviceMapper.selectList(qw); + if (devices.isEmpty()) { + throw new RuntimeException("请先将设备入库!!!"); + } + Device device = devices.get(0); + if (device.getBindingStatus() != null && device.getBindingStatus() == BindingStatusEnum.BOUND.getCode()) { + throw new RuntimeException("设备已绑定"); + } + + QueryWrapper bindRecordQueryWrapper = new QueryWrapper<>(); + bindRecordQueryWrapper.eq("device_id", device.getId()); + AppDeviceBindRecord appDeviceBindRecord = appDeviceBindRecordMapper.selectOne(bindRecordQueryWrapper); + if (appDeviceBindRecord != null) { + UpdateWrapper deviceUpdateWrapper = new UpdateWrapper<>(); + deviceUpdateWrapper.eq("device_id", device.getId()) + .set("binding_status", BindingStatusEnum.BOUND.getCode()) + .set("binding_user_id", userId) + .set("update_time", new Date()) + .set("binding_time", new Date()); + return appDeviceBindRecordMapper.update(null, deviceUpdateWrapper); + } else { + AppDeviceBindRecord bindRecord = new AppDeviceBindRecord(); + bindRecord.setDeviceId(device.getId()); + bindRecord.setBindingUserId(userId); + bindRecord.setBindingTime(new Date()); + bindRecord.setCreateBy(userId); + appDeviceBindRecordMapper.insert(bindRecord); + } + + UpdateWrapper deviceUpdateWrapper = new UpdateWrapper<>(); + deviceUpdateWrapper.eq("id", device.getId()) + .set("binding_status", BindingStatusEnum.BOUND.getCode()) + .set("binding_user_id", userId) + .set("binding_time", new Date()); + return deviceMapper.update(null, deviceUpdateWrapper); + } else if (mode == CommunicationModeEnum.BLUETOOTH.getValue()) { + String deviceMac = bo.getDeviceMac(); + QueryWrapper qw = new QueryWrapper() + .eq("device_mac", deviceMac); + List devices = deviceMapper.selectList(qw); + if (devices.isEmpty()) { + throw new RuntimeException("请先将设备入库!!!"); + } + Device device = devices.get(0); + + QueryWrapper bindRecordQueryWrapper = new QueryWrapper<>(); + bindRecordQueryWrapper.eq("device_id", device.getId()); + bindRecordQueryWrapper.eq("binding_user_id", userId); + AppDeviceBindRecord appDeviceBindRecord = appDeviceBindRecordMapper.selectOne(bindRecordQueryWrapper); + if (appDeviceBindRecord != null) { + UpdateWrapper deviceUpdateWrapper = new UpdateWrapper<>(); + deviceUpdateWrapper.eq("device_id", device.getId()) + .eq("binding_user_id", userId) + .set("binding_user_id", userId) + .set("binding_time", new Date()); + return appDeviceBindRecordMapper.update(null, deviceUpdateWrapper); + } else { + AppDeviceBindRecord bindRecord = new AppDeviceBindRecord(); + bindRecord.setDeviceId(device.getId()); + bindRecord.setBindingUserId(userId); + bindRecord.setBindingTime(new Date()); + bindRecord.setCreateBy(userId); + appDeviceBindRecordMapper.insert(bindRecord); + } + + UpdateWrapper deviceUpdateWrapper = new UpdateWrapper<>(); + deviceUpdateWrapper.eq("id", device.getId()) + .set("binding_status", BindingStatusEnum.BOUND.getCode()) + .set("binding_user_id", userId) + .set("binding_time", new Date()); + return deviceMapper.update(null, deviceUpdateWrapper); + } else { + throw new RuntimeException("通讯方式错误"); + } + + } + + + public int unBindDevice(Long id) { + return unBindDevice(id, null, 1); + } + + public int unBindDevice(Long id, Long userId, int type) { + Device device = deviceMapper.selectById(id); + if (device == null) { + throw new RuntimeException("请先将设备入库!!!"); + } + UpdateWrapper deviceUpdateWrapper = new UpdateWrapper<>(); + deviceUpdateWrapper.eq("id", device.getId()) + .set("binding_user_id", null) + .set("binding_status", BindingStatusEnum.UNBOUND.getCode()) + .set("binding_time", null); + deviceMapper.update(null, deviceUpdateWrapper); + + if (userId == null) { + userId = AppLoginHelper.getUserId(); + } + QueryWrapper bindRecordQueryWrapper = new QueryWrapper<>(); + bindRecordQueryWrapper.eq("device_id", device.getId()); + // 设备端解绑 0:设备端解绑 1:web端解绑 + if (type == 1) { + bindRecordQueryWrapper.eq("binding_user_id", userId); + } + + // AppDeviceBindRecord appDeviceBindRecord = appDeviceBindRecordMapper.selectOne(bindRecordQueryWrapper); + // if (appDeviceBindRecord != null) { + // return appDeviceBindRecordMapper.deleteById(appDeviceBindRecord.getId()); + // } + + List appDeviceBindRecordList = appDeviceBindRecordMapper.selectList(bindRecordQueryWrapper); + if (CollectionUtil.isNotEmpty(appDeviceBindRecordList)) { + appDeviceBindRecordList.forEach(appDeviceBindRecord -> + appDeviceBindRecordMapper.deleteById(appDeviceBindRecord.getId())); + } + + AppUserVo appUserVo = appUserMapper.selectVoById(userId); + QueryWrapper appDeviceShareQueryWrapper = new QueryWrapper<>(); + appDeviceShareQueryWrapper.eq("device_id", device.getId()); + appDeviceShareQueryWrapper.eq("phonenumber", appUserVo.getPhonenumber()); + List appDeviceShareList = appDeviceShareMapper.selectList(appDeviceShareQueryWrapper); + if (CollectionUtil.isNotEmpty(appDeviceShareList)) { + appDeviceShareList.forEach(appDeviceShare -> + appDeviceShareMapper.deleteById(appDeviceShare.getId())); + } + return 1; + } + + public String mapReverseGeocoding(DeviceInstructDto params) { +// Long deviceId = params.getDeviceId(); +// Device device = deviceMapper.selectById(deviceId); + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("device_imei", params.getDeviceImei()); + List devices = deviceMapper.selectList(queryWrapper); + if(ObjectUtils.length( devices) ==0){ + throw new ServiceException("设备不存在"); + } + return RedisUtils.getCacheObject("device:location:" + devices.get(0).getDeviceImei()); + } + + public Map getRealTimeStatus(AppRealTimeStatusDto statusDto) { + try { + String commandType = statusDto.getTypeName()+"_" + statusDto.getFunctionMode(); + DeviceStatusRule rule = realTimeStatusEngine.getDeviceStatusRule(commandType); + if(rule == null){ + throw new ServiceException("未匹配到处理命令"); + } + return rule.getStatus(statusDto); + } catch (Exception e){ + e.printStackTrace(); + } + return null; + } + + public AppDeviceVo getDeviceInfo(String deviceMac) { +// QueryWrapper queryWrapper = new QueryWrapper<>(); +// queryWrapper.eq("device_mac", deviceMac); +// List devices = deviceMapper.selectList(queryWrapper); + return deviceMapper.getDeviceInfo(deviceMac); + } +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/service/device/DeviceXinghanBizService.java b/fys-admin/src/main/java/com/fuyuanshen/web/service/device/DeviceXinghanBizService.java new file mode 100644 index 00000000..40c2d7af --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/web/service/device/DeviceXinghanBizService.java @@ -0,0 +1,269 @@ +package com.fuyuanshen.web.service.device; + +import com.alibaba.fastjson2.JSON; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.fuyuanshen.app.domain.AppPersonnelInfo; +import com.fuyuanshen.app.domain.bo.AppPersonnelInfoBo; +import com.fuyuanshen.app.domain.dto.AppDeviceLogoUploadDto; +import com.fuyuanshen.app.domain.dto.DeviceInstructDto; +import com.fuyuanshen.app.domain.vo.AppPersonnelInfoVo; +import com.fuyuanshen.app.mapper.AppPersonnelInfoMapper; +import com.fuyuanshen.common.core.constant.GlobalConstants; +import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.utils.ImageToCArrayConverter; +import com.fuyuanshen.common.core.utils.MapstructUtils; +import com.fuyuanshen.common.core.utils.ObjectUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.json.utils.JsonUtils; +import com.fuyuanshen.common.redis.utils.RedisUtils; +import com.fuyuanshen.common.satoken.utils.AppLoginHelper; +import com.fuyuanshen.equipment.domain.Device; +import com.fuyuanshen.equipment.domain.dto.AppDeviceSendMsgBo; +import com.fuyuanshen.equipment.enums.LightModeEnum; +import com.fuyuanshen.equipment.mapper.DeviceLogMapper; +import com.fuyuanshen.equipment.mapper.DeviceMapper; +import com.fuyuanshen.equipment.mapper.DeviceTypeMapper; +import com.fuyuanshen.global.mqtt.config.MqttGateway; +import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants; +import com.fuyuanshen.global.mqtt.constants.MqttConstants; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.time.Duration; +import java.util.*; + +import static com.fuyuanshen.common.core.constant.GlobalConstants.GLOBAL_REDIS_KEY; +import static com.fuyuanshen.common.core.utils.Bitmap80x12Generator.buildArr; +import static com.fuyuanshen.common.core.utils.Bitmap80x12Generator.generateFixedBitmapData; +import static com.fuyuanshen.common.core.utils.ImageToCArrayConverter.convertHexToDecimal; +import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_BOOT_LOGO_KEY_PREFIX; +import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX; + +@Slf4j +@Service +@RequiredArgsConstructor +public class DeviceXinghanBizService { + + private final DeviceMapper deviceMapper; + private final AppPersonnelInfoMapper appPersonnelInfoMapper; + private final DeviceTypeMapper deviceTypeMapper; + private final MqttGateway mqttGateway; + private final DeviceLogMapper deviceLogMapper; + + /** + * 所有档位的描述表 + * key : 指令类型,如 "ins_DetectGrade"、"ins_LightGrade" …… + * value : Map 值 -> 描述 + */ + private static final Map> GRADE_DESC = Map.of( + "ins_DetectGrade", Map.of(1, "低档", 2, "中档", 3, "高档"), + "ins_LightGrade", Map.of(1, "强光", 2, "弱光"), + "ins_SOSGrade", Map.of(1, "爆闪模式", 2, "红蓝模式"), + "ins_ShakeBit", Map.of(0, "未静止报警", 1, "正在静止报警") + // 再加 4、5、6…… 档,直接往 Map 里塞即可 + ); + + /** + * 根据指令类型和值,返回中文描述 + */ + private static String resolveGradeDesc(String type, int value) { + return GRADE_DESC.getOrDefault(type, Map.of()) + .getOrDefault(value, "关闭"); + } + + /** + * 设置静电预警档位 + */ + public void upDetectGradeSettings(DeviceInstructDto dto) { + sendCommand(dto, "ins_DetectGrade","静电预警档位"); + } + + /** + * 设置照明档位 + */ + public void upLightGradeSettings(DeviceInstructDto dto) { + sendCommand(dto, "ins_LightGrade","照明档位"); + } + + /** + * 设置SOS档位 + */ + public void upSOSGradeSettings(DeviceInstructDto dto) { + sendCommand(dto, "ins_SOSGrade","SOS档位"); + } + + /** + * 设置强制报警 + */ + public void upShakeBitSettings(DeviceInstructDto dto) { + sendCommand(dto, "ins_ShakeBit","强制报警"); + } + + /** + * 上传设备logo + */ + public void uploadDeviceLogo(AppDeviceLogoUploadDto bo) { + try { + Device device = deviceMapper.selectById(bo.getDeviceId()); + if (device == null) { + throw new ServiceException("设备不存在"); + } + if (isDeviceOffline(device.getDeviceImei())) { + throw new ServiceException("设备已断开连接:" + device.getDeviceName()); + } + MultipartFile file = bo.getFile(); + + byte[] largeData = ImageToCArrayConverter.convertImageToCArray(file.getInputStream(), 160, 80, 25600); + log.info("长度:" + largeData.length); + + log.info("原始数据大小: {} 字节", largeData.length); + + int[] ints = convertHexToDecimal(largeData); + RedisUtils.setCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + device.getDeviceImei() +DEVICE_BOOT_LOGO_KEY_PREFIX, Arrays.toString(ints), Duration.ofSeconds(5 * 60L)); + + Map payload = Map.of("ins_PicTrans", + Collections.singletonList(0)); + String topic = MqttConstants.GLOBAL_PUB_KEY + device.getDeviceImei(); + String json = JsonUtils.toJsonString(payload); + + try { + mqttGateway.sendMsgToMqtt(topic, 1, json); + } catch (Exception e) { + log.error("上传开机画面失败, topic={}, payload={}", topic, json, e); + throw new ServiceException("上传LOGO失败:" + e.getMessage()); + } + log.info("发送上传开机画面到设备消息=>topic:{},payload:{}", MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(),json); + + recordDeviceLog(device.getId(), device.getDeviceName(), "上传开机画面", "上传开机画面", AppLoginHelper.getUserId()); + } catch (Exception e){ + throw new ServiceException("发送指令失败"); + } + } + + /** + * 人员登记 + * @param bo + */ + public boolean registerPersonInfo(AppPersonnelInfoBo bo) { + Long deviceId = bo.getDeviceId(); + Device deviceObj = deviceMapper.selectById(deviceId); + if (deviceObj == null) { + throw new RuntimeException("请先将设备入库!!!"); + } + if (isDeviceOffline(deviceObj.getDeviceImei())) { + throw new ServiceException("设备已断开连接:" + deviceObj.getDeviceName()); + } + QueryWrapper qw = new QueryWrapper() + .eq("device_id", deviceId); + List appPersonnelInfoVos = appPersonnelInfoMapper.selectVoList(qw); + + List list = new ArrayList<>(); + list.add(bo.getUnitName()); + list.add(bo.getName()); + list.add(bo.getPosition()); + list.add(bo.getCode()); + RedisUtils.setCacheList(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + deviceObj.getDeviceImei() + ":app_send_message_data", list); + + Map payload = Map.of("ins_TexTrans", + Collections.singletonList(0)); + String topic = MqttConstants.GLOBAL_PUB_KEY + deviceObj.getDeviceImei(); + String json = JsonUtils.toJsonString(payload); + + try { + mqttGateway.sendMsgToMqtt(topic, 1, json); + } catch (Exception e) { + log.error("人员信息登记失败, topic={}, payload={}", topic, json, e); + throw new ServiceException("人员信息登记失败:" + e.getMessage()); + } + log.info("发送人员信息登记到设备消息=>topic:{},payload:{}", MqttConstants.GLOBAL_PUB_KEY + deviceObj.getDeviceImei(), bo); + + recordDeviceLog(deviceId, deviceObj.getDeviceName(), "人员信息登记", JSON.toJSONString(bo), AppLoginHelper.getUserId()); + if (ObjectUtils.length(appPersonnelInfoVos) == 0) { + AppPersonnelInfo appPersonnelInfo = MapstructUtils.convert(bo, AppPersonnelInfo.class); + return appPersonnelInfoMapper.insertOrUpdate(appPersonnelInfo); + } else { + UpdateWrapper uw = new UpdateWrapper<>(); + uw.eq("device_id", deviceId) + .set("name", bo.getName()) + .set("position", bo.getPosition()) + .set("unit_name", bo.getUnitName()) + .set("code", bo.getCode()); + return appPersonnelInfoMapper.update(null, uw) > 0; + } + } + + /* ---------------------------------- 私有通用方法 ---------------------------------- */ + + private void sendCommand(DeviceInstructDto dto, + String payloadKey,String deviceAction) { + long deviceId = dto.getDeviceId(); + Device device = deviceMapper.selectById(deviceId); + if (device == null) { + throw new ServiceException("设备不存在"); + } + if (isDeviceOffline(device.getDeviceImei())) { + throw new ServiceException("设备已断开连接:" + device.getDeviceName()); + } + + Integer value = Integer.parseInt(dto.getInstructValue()); + + Map payload = Map.of(payloadKey, + Collections.singletonList(value)); + + String topic = MqttConstants.GLOBAL_PUB_KEY + device.getDeviceImei(); + String json = JsonUtils.toJsonString(payload); + + try { + mqttGateway.sendMsgToMqtt(topic, 1, json); + } catch (Exception e) { + log.error("发送指令失败, topic={}, payload={}", topic, json, e); + throw new ServiceException("发送指令失败:" + e.getMessage()); + } + + log.info("发送指令成功 => topic:{}, payload:{}", topic, json); + String content = resolveGradeDesc("ins_DetectGrade", value); + recordDeviceLog(device.getId(), + device.getDeviceName(), + deviceAction, + content, + AppLoginHelper.getUserId()); + } + + private boolean isDeviceOffline(String imei) { + // 原方法名语义相反,这里取反,使含义更清晰 + return getDeviceStatus(imei); + } + + /** + * 记录设备操作日志 + * @param deviceId 设备ID + * @param content 日志内容 + * @param operator 操作人 + */ + private void recordDeviceLog(Long deviceId,String deviceName, String deviceAction, String content, Long operator) { + try { + // 创建设备日志实体 + com.fuyuanshen.equipment.domain.DeviceLog deviceLog = new com.fuyuanshen.equipment.domain.DeviceLog(); + deviceLog.setDeviceId(deviceId); + deviceLog.setDeviceAction(deviceAction); + deviceLog.setContent(content); + deviceLog.setCreateBy(operator); + deviceLog.setDeviceName(deviceName); + deviceLog.setCreateTime(new Date()); + + // 插入日志记录 + deviceLogMapper.insert(deviceLog); + } catch (Exception e) { + log.error("记录设备操作日志失败: {}", e.getMessage(), e); + } + } + + private boolean getDeviceStatus(String deviceImei) { + String deviceOnlineStatusRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX+ deviceImei + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX ; + return StringUtils.isBlank(deviceOnlineStatusRedisKey); + } + +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/service/device/status/FunctionAccessBatchStatusRule.java b/fys-admin/src/main/java/com/fuyuanshen/web/service/device/status/FunctionAccessBatchStatusRule.java new file mode 100644 index 00000000..116b5c91 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/web/service/device/status/FunctionAccessBatchStatusRule.java @@ -0,0 +1,75 @@ +package com.fuyuanshen.web.service.device.status; + +import cn.hutool.json.JSONUtil; +import com.fuyuanshen.app.domain.dto.AppRealTimeStatusDto; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.redis.utils.RedisUtils; +import com.fuyuanshen.global.mqtt.listener.domain.FunctionAccessStatus; +import com.fuyuanshen.web.service.device.status.base.DeviceStatusRule; +import com.fuyuanshen.web.service.device.status.constants.DeviceTypeConstants; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_KEY; + + +@Slf4j +@Component +public class FunctionAccessBatchStatusRule implements DeviceStatusRule { + @Override + public String getCommandType() { + return DeviceTypeConstants.TYPE_BJQ6170+"_2"; + } + + @Override + public boolean supports(String deviceType) { + return true; // 适用于所有设备类型 + } + + @Override + public Map getStatus(AppRealTimeStatusDto dto) { + Map status = new HashMap<>(); + String functionAccess = RedisUtils.getCacheObject( + FUNCTION_ACCESS_KEY + dto.getBatchId()); + log.info("FunctionAccessBatchStatusRule:{}",functionAccess); + if(StringUtils.isBlank(functionAccess)){ + status.put("functionAccess", FunctionAccessStatus.OK.getCode()); + return status; + } + List cachedDeviceImeiList = JSONUtil.toList(functionAccess, String.class); + if(cachedDeviceImeiList.isEmpty()){ + status.put("functionAccess", FunctionAccessStatus.OK.getCode()); + return status; + } + for (String key : cachedDeviceImeiList) { + String item = RedisUtils.getCacheObject(FUNCTION_ACCESS_KEY + key); + if("ACTIVE".equals(item)){ + status.put("functionAccess", FunctionAccessStatus.ACTIVE.getCode()); + break; + }else { + status.put("functionAccess", FunctionAccessStatus.OK.getCode()); + } +// if (StringUtils.isBlank(item)) { +// String timeOut = RedisUtils.getCacheObject(FUNCTION_ACCESS_TIMEOUT_KEY + dto.getDeviceImei()); +// if ("TIMEOUT".equals(timeOut)) { +// status.put("functionAccess", FunctionAccessStatus.TIMEOUT.getCode()); +// break; +// } else { +// status.put("functionAccess", FunctionAccessStatus.OK.getCode()); +// } +// } else { +// if (!FunctionAccessStatus.OK.getCode().equals(item)) { +// status.put("functionAccess", FunctionAccessStatus.FAILED.getCode()); +// break; +// } else { +// status.put("functionAccess", FunctionAccessStatus.OK.getCode()); +// } +// } + } + return status; + } +} \ No newline at end of file diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/service/device/status/FunctionAccessStatusRule.java b/fys-admin/src/main/java/com/fuyuanshen/web/service/device/status/FunctionAccessStatusRule.java new file mode 100644 index 00000000..6b6f094d --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/web/service/device/status/FunctionAccessStatusRule.java @@ -0,0 +1,51 @@ +package com.fuyuanshen.web.service.device.status; + +import com.fuyuanshen.app.domain.dto.AppRealTimeStatusDto; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.redis.utils.RedisUtils; +import com.fuyuanshen.global.mqtt.listener.domain.FunctionAccessStatus; +import com.fuyuanshen.web.service.device.status.base.DeviceStatusRule; +import com.fuyuanshen.web.service.device.status.constants.DeviceTypeConstants; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; + +import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_KEY; +import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_TIMEOUT_KEY; + +// 上传开机图片 +@Slf4j +@Component +public class FunctionAccessStatusRule implements DeviceStatusRule { + @Override + public String getCommandType() { + return DeviceTypeConstants.TYPE_BJQ6170+"_1"; + } + + @Override + public boolean supports(String deviceType) { + return true; // 适用于所有设备类型 + } + + @Override + public Map getStatus(AppRealTimeStatusDto dto) { + Map status = new HashMap<>(); + String functionAccess = RedisUtils.getCacheObject( + FUNCTION_ACCESS_KEY + dto.getDeviceImei()); + log.info("FunctionAccessStatusRule:{}",functionAccess); + if(StringUtils.isBlank(functionAccess)){ + String timeOut = RedisUtils.getCacheObject(FUNCTION_ACCESS_TIMEOUT_KEY + dto.getDeviceImei()); + if("TIMEOUT".equals(timeOut)){ + status.put("functionAccess", FunctionAccessStatus.TIMEOUT.getCode()); + RedisUtils.deleteObject(FUNCTION_ACCESS_TIMEOUT_KEY + dto.getDeviceImei()); + }else{ + status.put("functionAccess", FunctionAccessStatus.OK.getCode()); + } + }else{ + status.put("functionAccess", functionAccess); + } + return status; + } +} \ No newline at end of file diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/service/device/status/base/DeviceStatusRule.java b/fys-admin/src/main/java/com/fuyuanshen/web/service/device/status/base/DeviceStatusRule.java new file mode 100644 index 00000000..57e1fd8e --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/web/service/device/status/base/DeviceStatusRule.java @@ -0,0 +1,19 @@ +package com.fuyuanshen.web.service.device.status.base; + +import com.fuyuanshen.app.domain.dto.AppRealTimeStatusDto; + +import java.util.Map; + +// 规则接口 +public interface DeviceStatusRule { + + /** + * 获取命令类型 + * @return 命令类型 + */ + String getCommandType(); + + boolean supports(String deviceType); + + Map getStatus(AppRealTimeStatusDto statusDto); +} \ No newline at end of file diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/service/device/status/base/RealTimeStatusEngine.java b/fys-admin/src/main/java/com/fuyuanshen/web/service/device/status/base/RealTimeStatusEngine.java new file mode 100644 index 00000000..d374eb52 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/web/service/device/status/base/RealTimeStatusEngine.java @@ -0,0 +1,23 @@ +package com.fuyuanshen.web.service.device.status.base; + +import org.springframework.stereotype.Component; + +import java.util.LinkedHashMap; +import java.util.List; + + +@Component +public class RealTimeStatusEngine { + + + private final LinkedHashMap rulesMap = new LinkedHashMap<>(); + public RealTimeStatusEngine(List rules) { + rules.forEach(rule -> rulesMap.put(rule.getCommandType(), rule) + ); + } + + + public DeviceStatusRule getDeviceStatusRule(String commandType) { + return rulesMap.get(commandType); + } +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/service/device/status/constants/DeviceTypeConstants.java b/fys-admin/src/main/java/com/fuyuanshen/web/service/device/status/constants/DeviceTypeConstants.java new file mode 100644 index 00000000..1e0f0ccf --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/web/service/device/status/constants/DeviceTypeConstants.java @@ -0,0 +1,6 @@ +package com.fuyuanshen.web.service.device.status.constants; + +public class DeviceTypeConstants { + public static final String TYPE_BJQ6170 = "BJQ6170"; + +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/service/impl/WEBDeviceServiceImpl.java b/fys-admin/src/main/java/com/fuyuanshen/web/service/impl/WEBDeviceServiceImpl.java index 9f8e0416..0d5097c1 100644 --- a/fys-admin/src/main/java/com/fuyuanshen/web/service/impl/WEBDeviceServiceImpl.java +++ b/fys-admin/src/main/java/com/fuyuanshen/web/service/impl/WEBDeviceServiceImpl.java @@ -1,15 +1,26 @@ package com.fuyuanshen.web.service.impl; +import cn.hutool.core.bean.BeanUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.fuyuanshen.app.service.AppDeviceBizService; +import com.fuyuanshen.app.domain.AppDeviceBindRecord; +import com.fuyuanshen.app.domain.AppDeviceShare; +import com.fuyuanshen.app.domain.vo.AppDeviceShareVo; +import com.fuyuanshen.app.mapper.AppDeviceBindRecordMapper; +import com.fuyuanshen.app.mapper.AppDeviceShareMapper; import com.fuyuanshen.equipment.domain.Device; import com.fuyuanshen.equipment.domain.DeviceAssignments; +import com.fuyuanshen.equipment.domain.vo.WebDeviceVo; +import com.fuyuanshen.equipment.enums.BindingStatusEnum; import com.fuyuanshen.equipment.mapper.DeviceAssignmentsMapper; import com.fuyuanshen.equipment.mapper.DeviceMapper; import com.fuyuanshen.web.service.WEBDeviceService; +import com.fuyuanshen.web.service.device.DeviceBizService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; /** * @Description: @@ -21,10 +32,13 @@ import org.springframework.stereotype.Service; @RequiredArgsConstructor public class WEBDeviceServiceImpl extends ServiceImpl implements WEBDeviceService { - private final AppDeviceBizService appDeviceService; - private final DeviceAssignmentsMapper deviceAssignmentsMapper; + private final AppDeviceBindRecordMapper appDeviceBindRecordMapper; + + private final DeviceMapper deviceMapper; + private final AppDeviceShareMapper appDeviceShareMapper; + /** * WEB端解绑设备 @@ -33,6 +47,7 @@ public class WEBDeviceServiceImpl extends ServiceImpl impl * @return */ @Override + @Transactional public int webUnBindDevice(Long id, Long userId) { // 设备端解绑 0:设备端解绑 1:web端解绑 int type = 1; @@ -44,7 +59,39 @@ public class WEBDeviceServiceImpl extends ServiceImpl impl id = deviceAssignments.getDeviceId(); type = 0; } - return appDeviceService.unBindDevice(id, userId, type); + + QueryWrapper deviceId = new QueryWrapper().eq("device_id", id); + + // appDeviceService.unBindDevice(id, userId, type); + UpdateWrapper deviceUpdateWrapper = new UpdateWrapper<>(); + deviceUpdateWrapper.eq("id", id) + .set("binding_user_id", null) + .set("binding_status", BindingStatusEnum.UNBOUND.getCode()) + .set("binding_time", null); + deviceMapper.update(null, deviceUpdateWrapper); + + return appDeviceBindRecordMapper.delete(deviceId); + } + + + /** + * WEB端设备详情 + * + * @param id + * @return + */ + @Override + public WebDeviceVo getDevice(Long id) { + Device device = deviceMapper.selectById(id); + if (device != null) { + WebDeviceVo webDeviceVo = new WebDeviceVo(); + BeanUtil.copyProperties(device, webDeviceVo); + // 查询分享用户数 + Long count = appDeviceShareMapper.selectCount(new QueryWrapper().eq("device_id", id)); + webDeviceVo.setShareUsersNumber(Math.toIntExact(count)); + return webDeviceVo; + } + return null; } diff --git a/fys-admin/src/main/resources/application-dev.yml b/fys-admin/src/main/resources/application-dev.yml index e586574e..64650383 100644 --- a/fys-admin/src/main/resources/application-dev.yml +++ b/fys-admin/src/main/resources/application-dev.yml @@ -1,7 +1,7 @@ --- # 监控中心配置 spring.boot.admin.client: # 增加客户端开关 - enabled: true + enabled: false url: http://localhost:9090/admin instance: service-host-type: IP @@ -303,6 +303,6 @@ mqtt: password: #YtvpSfCNG url: tcp://47.120.79.150:2883 subClientId: fys_subClient - subTopic: A/#,B/#,worker/location/# + subTopic: worker/location/# pubTopic: B/# pubClientId: fys_pubClient \ No newline at end of file diff --git a/fys-admin/src/main/resources/application-prod.yml b/fys-admin/src/main/resources/application-prod.yml index ae85aac6..7c6c62d7 100644 --- a/fys-admin/src/main/resources/application-prod.yml +++ b/fys-admin/src/main/resources/application-prod.yml @@ -4,7 +4,7 @@ spring.servlet.multipart.location: /fys/server/temp --- # 监控中心配置 spring.boot.admin.client: # 增加客户端开关 - enabled: true + enabled: false url: http://localhost:9090/admin instance: service-host-type: IP @@ -16,7 +16,7 @@ spring.boot.admin.client: --- # snail-job 配置 snail-job: - enabled: true + enabled: false # 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务 group: "fys_group" # SnailJob 接入验证令牌 详见 script/sql/ry_job.sql `sj_group_config`表 @@ -101,13 +101,13 @@ spring: spring.data: redis: # 地址 - host: localhost + host: 47.120.79.150 # 端口,默认为6379 port: 6379 # 数据库索引 database: 1 # redis 密码必须配置 - password: re_fs_11520631 + password: xhYc_djkl382^#780! # 连接超时时间 timeout: 10s # 是否开启ssl @@ -177,11 +177,14 @@ sms: # 框架定义的厂商名称标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分 supplier: alibaba # 有些称为accessKey有些称之为apiKey,也有称为sdkKey或者appId。 - access-key-id: 您的accessKey + access-key-id: LTAI5tJdDNpZootsPQ5hdELx # 称为accessSecret有些称之为apiSecret - access-key-secret: 您的accessKeySecret - signature: 您的短信签名 - sdk-app-id: 您的sdkAppId + access-key-secret: mU4WtffcCXpHPz5tLwQpaGtLsJXONt + #模板ID 非必须配置,如果使用sendMessage的快速发送需此配置 + template-id: SMS_322180518 + #模板变量 上述模板的变量 + templateName: code + signature: 湖北星汉研创科技 config2: # 厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分 supplier: tencent @@ -280,8 +283,8 @@ mqtt: password: #YtvpSfCNG url: tcp://47.120.79.150:2883 subClientId: fys_subClient - subTopic: worker/alert/#,worker/location/# - pubTopic: worker/location + subTopic: A/#,B/#,worker/location/# + pubTopic: B/# pubClientId: fys_pubClient diff --git a/fys-admin/src/main/resources/application.yml b/fys-admin/src/main/resources/application.yml index 60c91061..21924a61 100644 --- a/fys-admin/src/main/resources/application.yml +++ b/fys-admin/src/main/resources/application.yml @@ -64,7 +64,7 @@ spring: # 国际化资源文件路径 basename: i18n/messages profiles: - active: @profiles.active@ + active: ${profiles.active} # 文件上传 servlet: multipart: @@ -219,6 +219,8 @@ springdoc: packages-to-scan: com.fuyuanshen.customer - group: APP模块 packages-to-scan: com.fuyuanshen.app + - group: 设备分组 + packages-to-scan: com.fuyuanshen.web.controller.device # 防止XSS攻击 xss: @@ -232,7 +234,7 @@ xss: # 如使用JDK21请直接使用虚拟线程 不要开启此配置 thread-pool: # 是否开启线程池 - enabled: false + enabled: true # 队列最大长度 queueCapacity: 128 # 线程池维护线程所允许的空闲时间 diff --git a/fys-admin/src/main/resources/image/background.png b/fys-admin/src/main/resources/image/background.png index 1670e7f0..f7c168d4 100644 Binary files a/fys-admin/src/main/resources/image/background.png and b/fys-admin/src/main/resources/image/background.png differ diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/constant/GlobalConstants.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/constant/GlobalConstants.java index ccec3718..3eaad963 100644 --- a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/constant/GlobalConstants.java +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/constant/GlobalConstants.java @@ -36,4 +36,9 @@ public interface GlobalConstants { * 三方认证 redis key */ String SOCIAL_AUTH_CODE_KEY = GLOBAL_REDIS_KEY + "social_auth_codes:"; + + + String FUNCTION_ACCESS_KEY = GLOBAL_REDIS_KEY + "device:function_access:"; + + String FUNCTION_ACCESS_TIMEOUT_KEY = GLOBAL_REDIS_KEY + "device:function_access_timeout:"; } diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/ImageToCArrayConverter.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/ImageToCArrayConverter.java index 8823ad08..eedfead6 100644 --- a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/ImageToCArrayConverter.java +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/ImageToCArrayConverter.java @@ -88,6 +88,24 @@ public class ImageToCArrayConverter { return chunk; } + public static String[] getChunk2(String[] data, int chunkIndex, int chunkSize) { + if (data == null || chunkSize <= 0 || chunkIndex < 0) { + return new String[0]; + } + + int start = chunkIndex * chunkSize; + if (start >= data.length) { + return new String[0]; // 索引超出范围 + } + + int end = Math.min(start + chunkSize, data.length); + int length = end - start; + + String[] chunk = new String[length]; + System.arraycopy(data, start, chunk, 0, length); + return chunk; + } + public static void buildArr(int[] data,List intData){ for (int datum : data) { intData.add(datum); diff --git a/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/utils/EncryptUtilsTest.java b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/utils/EncryptUtilsTest.java index c81f3538..08b352ac 100644 --- a/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/utils/EncryptUtilsTest.java +++ b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/utils/EncryptUtilsTest.java @@ -39,8 +39,8 @@ public class EncryptUtilsTest { loginBody.setClientId("e5cd7e4891bf95d1d19206ce24a7b32e"); loginBody.setGrantType("password"); loginBody.setTenantId("894078"); - loginBody.setCode("0"); - loginBody.setUuid("1c285b27f516486f9535face77023aeb"); + loginBody.setCode("9"); + loginBody.setUuid("d5be31eac1244cee851a9903f358bc6a"); // loginBody.setUsername("admin"); // loginBody.setPassword("admin123"); loginBody.setUsername("dyf"); diff --git a/fys-common/fys-common-ratelimiter/src/main/java/com/fuyuanshen/common/ratelimiter/annotation/FunctionAccessAnnotation.java b/fys-common/fys-common-ratelimiter/src/main/java/com/fuyuanshen/common/ratelimiter/annotation/FunctionAccessAnnotation.java new file mode 100644 index 00000000..ab211b22 --- /dev/null +++ b/fys-common/fys-common-ratelimiter/src/main/java/com/fuyuanshen/common/ratelimiter/annotation/FunctionAccessAnnotation.java @@ -0,0 +1,12 @@ +package com.fuyuanshen.common.ratelimiter.annotation;// DeviceRedisKeyAnnotation.java +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface FunctionAccessAnnotation { + String value() default ""; + long timeOut() default 30; +} diff --git a/fys-common/fys-common-ratelimiter/src/main/java/com/fuyuanshen/common/ratelimiter/annotation/FunctionAccessBatcAnnotation.java b/fys-common/fys-common-ratelimiter/src/main/java/com/fuyuanshen/common/ratelimiter/annotation/FunctionAccessBatcAnnotation.java new file mode 100644 index 00000000..ef0934ee --- /dev/null +++ b/fys-common/fys-common-ratelimiter/src/main/java/com/fuyuanshen/common/ratelimiter/annotation/FunctionAccessBatcAnnotation.java @@ -0,0 +1,13 @@ +package com.fuyuanshen.common.ratelimiter.annotation;// DeviceRedisKeyAnnotation.java +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface FunctionAccessBatcAnnotation { + String value() default ""; + long timeOut() default 30; + long batchMaxTimeOut() default 40; +} diff --git a/fys-common/fys-common-ratelimiter/src/main/java/com/fuyuanshen/common/ratelimiter/aspectj/FunctionAccessAspect.java b/fys-common/fys-common-ratelimiter/src/main/java/com/fuyuanshen/common/ratelimiter/aspectj/FunctionAccessAspect.java new file mode 100644 index 00000000..41ecd7ea --- /dev/null +++ b/fys-common/fys-common-ratelimiter/src/main/java/com/fuyuanshen/common/ratelimiter/aspectj/FunctionAccessAspect.java @@ -0,0 +1,104 @@ +package com.fuyuanshen.common.ratelimiter.aspectj;// DeviceRedisKeyAspect.java +import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.ratelimiter.annotation.FunctionAccessAnnotation; +import com.fuyuanshen.common.redis.utils.RedisUtils; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.stereotype.Component; + +import java.time.Duration; + +import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_KEY; +import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_TIMEOUT_KEY; + +@Slf4j +@Aspect +@Component +public class FunctionAccessAspect { + + // 定义切点,拦截带有DeviceRedisKeyAnnotation注解的方法 + @Around("@annotation(functionAccessAnnotation)") + public Object addDeviceRedisKey(ProceedingJoinPoint joinPoint, FunctionAccessAnnotation functionAccessAnnotation) throws Throwable { + Object result; + String deviceImei = null; + + // 获取方法参数,查找设备ID + Object[] args = joinPoint.getArgs(); + deviceImei = extractDeviceImei(args); + long timeout = functionAccessAnnotation.timeOut(); + + if (StringUtils.isNotBlank(deviceImei)) { + // 生成全局Redis key + String redisKey = generateDeviceRedisKey(deviceImei); + String cacheKey = RedisUtils.getCacheObject(redisKey); + if(StringUtils.isNotBlank(cacheKey) && "ACTIVE".equals(cacheKey)){ + throw new ServiceException("设备已存在访问限制,请稍后再试", 500); + } + // + RedisUtils.setCacheObject(redisKey, "ACTIVE", Duration.ofSeconds(timeout)); + } + + // 执行原方法 + result = joinPoint.proceed(); + + return result; + } + + /** + * 从方法参数中提取设备ID + */ + private String extractDeviceImei(Object[] args) { + if (args == null || args.length == 0) { + return null; + } + + for (Object arg : args) { + if (arg == null) continue; + + // 如果参数本身就是设备ID (Long类型) + if (arg instanceof Long) { + return arg.toString(); + } + + // 如果参数是对象,尝试获取deviceId字段 + try { + // 使用反射获取deviceId字段 + java.lang.reflect.Field[] fields = arg.getClass().getDeclaredFields(); + for (java.lang.reflect.Field field : fields) { + if ("deviceImei".equalsIgnoreCase(field.getName()) || + "device_imei".equalsIgnoreCase(field.getName())) { + field.setAccessible(true); + Object value = field.get(arg); + if (value != null) { + return value.toString(); + } + } + } + + // 尝试获取getId方法 + try { + java.lang.reflect.Method getIdMethod = arg.getClass().getMethod("getDeviceImei"); + Object value = getIdMethod.invoke(arg); + if (value != null) { + return value.toString(); + } + } catch (Exception ignored) {} + + } catch (Exception e) { + log.debug("从参数中提取设备ID时出错: {}", e.getMessage()); + } + } + + return null; + } + + /** + * 生成设备的全局Redis key + */ + private String generateDeviceRedisKey(String deviceImei) { + return FUNCTION_ACCESS_KEY + deviceImei; + } +} diff --git a/fys-common/fys-common-ratelimiter/src/main/java/com/fuyuanshen/common/ratelimiter/aspectj/FunctionAccessBatchAspect.java b/fys-common/fys-common-ratelimiter/src/main/java/com/fuyuanshen/common/ratelimiter/aspectj/FunctionAccessBatchAspect.java new file mode 100644 index 00000000..5a901ff8 --- /dev/null +++ b/fys-common/fys-common-ratelimiter/src/main/java/com/fuyuanshen/common/ratelimiter/aspectj/FunctionAccessBatchAspect.java @@ -0,0 +1,183 @@ +package com.fuyuanshen.common.ratelimiter.aspectj;// DeviceRedisKeyAspect.java + +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONUtil; +import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.ratelimiter.annotation.FunctionAccessBatcAnnotation; +import com.fuyuanshen.common.redis.utils.RedisUtils; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.stereotype.Component; + +import java.time.Duration; +import java.util.List; + +import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_KEY; + +@Slf4j +@Aspect +@Component +public class FunctionAccessBatchAspect { + + // 定义切点,拦截带有DeviceRedisKeyAnnotation注解的方法 + @Around("@annotation(functionAccessBatchAspect)") + public Object addDeviceRedisKey(ProceedingJoinPoint joinPoint, FunctionAccessBatcAnnotation functionAccessBatchAspect) throws Throwable { + Object result; + String batchId = null; + List deviceImeiList = null; + + // 获取方法参数,查找设备ID + Object[] args = joinPoint.getArgs(); + batchId = extractDeviceBatchId(args); + deviceImeiList = extractDeviceImeiList(args); + if (StringUtils.isNotBlank(batchId)) { + // 生成全局Redis key + String redisKey = generateDeviceRedisKey(batchId); + String cacheKey = RedisUtils.getCacheObject(redisKey); + if(StringUtils.isNotBlank(cacheKey) && "ACTIVE".equals(cacheKey)){ + throw new ServiceException("设备已存在访问限制,请稍后再试", 500); + } + deviceImeiList.forEach(item->{ + RedisUtils.setCacheObject(FUNCTION_ACCESS_KEY + item, "ACTIVE", Duration.ofSeconds(functionAccessBatchAspect.timeOut())); + }); + String deviceImeiListStr = JSONUtil.toJsonStr(deviceImeiList); + RedisUtils.setCacheObject(redisKey, deviceImeiListStr , Duration.ofSeconds(functionAccessBatchAspect.batchMaxTimeOut())); + } + + // 执行原方法 + result = joinPoint.proceed(); + + return result; + } + + /** + * 从方法参数中提取设备IMEI列表 + */ + private List extractDeviceImeiList(Object[] args) { + if (args == null || args.length == 0) { + return null; + } + + for (Object arg : args) { + if (arg == null) continue; + + // 如果参数本身就是List类型 + if (arg instanceof List) { + List list = (List) arg; + if (!list.isEmpty() && list.get(0) instanceof String) { + // 检查是否为deviceImeiList + return (List) list; + } + } + + // 如果参数是对象,尝试获取deviceImeiList字段 + try { + // 使用反射获取deviceImeiList字段 + java.lang.reflect.Field[] fields = arg.getClass().getDeclaredFields(); + for (java.lang.reflect.Field field : fields) { + if ("deviceImeiList".equalsIgnoreCase(field.getName()) || + "device_imei_list".equalsIgnoreCase(field.getName()) || + "deviceImeis".equalsIgnoreCase(field.getName()) || + "device_imeis".equalsIgnoreCase(field.getName())) { + field.setAccessible(true); + Object value = field.get(arg); + if (value instanceof List) { + List list = (List) value; + if (!list.isEmpty() && list.get(0) instanceof String) { + return (List) list; + } + } + } + } + + // 尝试获取getDeviceImeiList方法 + try { + java.lang.reflect.Method getDeviceImeiListMethod = arg.getClass().getMethod("getDeviceImeiList"); + Object value = getDeviceImeiListMethod.invoke(arg); + if (value instanceof List) { + List list = (List) value; + if (!list.isEmpty() && list.get(0) instanceof String) { + return (List) list; + } + } + } catch (Exception ignored) {} + + // 尝试获取getDeviceImeis方法 + try { + java.lang.reflect.Method getDeviceImeisMethod = arg.getClass().getMethod("getDeviceImeis"); + Object value = getDeviceImeisMethod.invoke(arg); + if (value instanceof List) { + List list = (List) value; + if (!list.isEmpty() && list.get(0) instanceof String) { + return (List) list; + } + } + } catch (Exception ignored) {} + + } catch (Exception e) { + log.debug("从参数中提取设备IMEI列表时出错: {}", e.getMessage()); + } + } + + return null; + } + + + /** + * 从方法参数中提取设备ID + */ + private String extractDeviceBatchId(Object[] args) { + if (args == null || args.length == 0) { + return null; + } + + for (Object arg : args) { + if (arg == null) continue; + + // 如果参数本身就是设备ID (Long类型) + if (arg instanceof Long) { + return arg.toString(); + } + + // 如果参数是对象,尝试获取deviceId字段 + try { + // 使用反射获取deviceId字段 + java.lang.reflect.Field[] fields = arg.getClass().getDeclaredFields(); + for (java.lang.reflect.Field field : fields) { + if ("batchId".equalsIgnoreCase(field.getName()) || + "batch_id".equalsIgnoreCase(field.getName())) { + field.setAccessible(true); + Object value = field.get(arg); + if (value != null) { + return value.toString(); + } + } + } + + // 尝试获取getId方法 + try { + java.lang.reflect.Method getIdMethod = arg.getClass().getMethod("batchId"); + Object value = getIdMethod.invoke(arg); + if (value != null) { + return value.toString(); + } + } catch (Exception ignored) {} + + } catch (Exception e) { + log.debug("从参数中提取批次号时出错: {}", e.getMessage()); + } + } + + return null; + } + + /** + * 生成设备的全局Redis key + */ + private String generateDeviceRedisKey(String batchId) { + return FUNCTION_ACCESS_KEY + batchId; + } +} diff --git a/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/controller/AppPersonnelInfoRecordsController.java b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/controller/AppPersonnelInfoRecordsController.java new file mode 100644 index 00000000..b7f9dd62 --- /dev/null +++ b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/controller/AppPersonnelInfoRecordsController.java @@ -0,0 +1,105 @@ +package com.fuyuanshen.app.controller; + +import java.util.List; + +import lombok.RequiredArgsConstructor; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.*; +import cn.dev33.satoken.annotation.SaCheckPermission; +import org.springframework.web.bind.annotation.*; +import org.springframework.validation.annotation.Validated; +import com.fuyuanshen.common.idempotent.annotation.RepeatSubmit; +import com.fuyuanshen.common.log.annotation.Log; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.core.validate.AddGroup; +import com.fuyuanshen.common.core.validate.EditGroup; +import com.fuyuanshen.common.log.enums.BusinessType; +import com.fuyuanshen.common.excel.utils.ExcelUtil; +import com.fuyuanshen.app.domain.vo.AppPersonnelInfoRecordsVo; +import com.fuyuanshen.app.domain.bo.AppPersonnelInfoRecordsBo; +import com.fuyuanshen.app.service.IAppPersonnelInfoRecordsService; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; + +/** + * 人员信息登记记录 + * + * @author CYT + * @date 2025-08-22 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/app/personnelInfoRecords") +public class AppPersonnelInfoRecordsController extends BaseController { + + private final IAppPersonnelInfoRecordsService appPersonnelInfoRecordsService; + + /** + * 查询人员信息登记记录列表 + */ + @SaCheckPermission("app:personnelInfoRecords:list") + @GetMapping("/list") + public TableDataInfo list(AppPersonnelInfoRecordsBo bo, PageQuery pageQuery) { + return appPersonnelInfoRecordsService.queryPageList(bo, pageQuery); + } + + /** + * 导出人员信息登记记录列表 + */ + @SaCheckPermission("app:personnelInfoRecords:export") + @Log(title = "人员信息登记记录", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(AppPersonnelInfoRecordsBo bo, HttpServletResponse response) { + List list = appPersonnelInfoRecordsService.queryList(bo); + ExcelUtil.exportExcel(list, "人员信息登记记录", AppPersonnelInfoRecordsVo.class, response); + } + + /** + * 获取人员信息登记记录详细信息 + * + * @param id 主键 + */ + @SaCheckPermission("app:personnelInfoRecords:query") + @GetMapping("/{id}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(appPersonnelInfoRecordsService.queryById(id)); + } + + /** + * 新增人员信息登记记录 + */ + @SaCheckPermission("app:personnelInfoRecords:add") + @Log(title = "人员信息登记记录", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody AppPersonnelInfoRecordsBo bo) { + return toAjax(appPersonnelInfoRecordsService.insertByBo(bo)); + } + + /** + * 修改人员信息登记记录 + */ + @SaCheckPermission("app:personnelInfoRecords:edit") + @Log(title = "人员信息登记记录", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody AppPersonnelInfoRecordsBo bo) { + return toAjax(appPersonnelInfoRecordsService.updateByBo(bo)); + } + + /** + * 删除人员信息登记记录 + * + * @param ids 主键串 + */ + @SaCheckPermission("app:personnelInfoRecords:remove") + @Log(title = "人员信息登记记录", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(appPersonnelInfoRecordsService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/controller/AppUserController.java b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/controller/WebAppUserController.java similarity index 96% rename from fys-modules/fys-app/src/main/java/com/fuyuanshen/app/controller/AppUserController.java rename to fys-modules/fys-app/src/main/java/com/fuyuanshen/app/controller/WebAppUserController.java index 4c24feb5..34f6c959 100644 --- a/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/controller/AppUserController.java +++ b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/controller/WebAppUserController.java @@ -24,7 +24,7 @@ import com.fuyuanshen.app.service.IAppUserService; import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; /** - * APP用户信息 + * WebApp用户信息 * * @author Lion Li * @date 2025-06-27 @@ -32,8 +32,8 @@ import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; @Validated @RequiredArgsConstructor @RestController -@RequestMapping("/app/user") -public class AppUserController extends BaseController { +@RequestMapping("/WebApp/user") +public class WebAppUserController extends BaseController { private final IAppUserService appUserService; diff --git a/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/domain/AppPersonnelInfoRecords.java b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/domain/AppPersonnelInfoRecords.java new file mode 100644 index 00000000..848bd37e --- /dev/null +++ b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/domain/AppPersonnelInfoRecords.java @@ -0,0 +1,66 @@ +package com.fuyuanshen.app.domain; + +import com.fuyuanshen.common.tenant.core.TenantEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 人员信息登记记录对象 app_personnel_info_records + * + * @author CYT + * @date 2025-08-22 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("app_personnel_info_records") +public class AppPersonnelInfoRecords extends TenantEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 设备id + */ + private Long deviceId; + + /** + * 主键 + */ + private Long personnelId; + + /** + * 人员姓名 + */ + private String name; + + /** + * 职位 + */ + private String position; + + /** + * 单位名称 + */ + private String unitName; + + /** + * ID号 + */ + private String code; + + /** + * 发送信息 + */ + private String sendMsg; + + +} diff --git a/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/domain/bo/AppPersonnelInfoBo.java b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/domain/bo/AppPersonnelInfoBo.java index da469ac4..70a79188 100644 --- a/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/domain/bo/AppPersonnelInfoBo.java +++ b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/domain/bo/AppPersonnelInfoBo.java @@ -25,6 +25,11 @@ public class AppPersonnelInfoBo extends BaseEntity { */ private Long id; + /** + * 设备IMEI + */ + private String deviceImei; + /** * 设备id */ diff --git a/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/domain/bo/AppPersonnelInfoRecordsBo.java b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/domain/bo/AppPersonnelInfoRecordsBo.java new file mode 100644 index 00000000..70e24269 --- /dev/null +++ b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/domain/bo/AppPersonnelInfoRecordsBo.java @@ -0,0 +1,67 @@ +package com.fuyuanshen.app.domain.bo; + +import com.fuyuanshen.app.domain.AppPersonnelInfoRecords; +import com.fuyuanshen.common.core.validate.AddGroup; +import com.fuyuanshen.common.core.validate.EditGroup; +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +/** + * 人员信息登记记录业务对象 app_personnel_info_records + * + * @author CYT + * @date 2025-08-22 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = AppPersonnelInfoRecords.class, reverseConvertGenerate = false) +public class AppPersonnelInfoRecordsBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = { EditGroup.class }) + private Long id; + + /** + * 设备id + */ + @NotNull(message = "设备id不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long deviceId; + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long personnelId; + + /** + * 人员姓名 + */ + private String name; + + /** + * 职位 + */ + private String position; + + /** + * 单位名称 + */ + private String unitName; + + /** + * ID号 + */ + private String code; + + /** + * 发送信息 + */ + private String sendMsg; + + +} diff --git a/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/domain/vo/AppDeviceDetailVo.java b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/domain/vo/AppDeviceDetailVo.java index 6478ea44..c1728092 100644 --- a/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/domain/vo/AppDeviceDetailVo.java +++ b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/domain/vo/AppDeviceDetailVo.java @@ -99,4 +99,12 @@ public class AppDeviceDetailVo { // 逆解析地址 private String address; + + /** + * 告警状态(0解除告警,1告警) + */ + private String alarmStatus; + + // 灯光亮度 + private String lightBrightness; } diff --git a/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/domain/vo/AppDeviceShareDetailVo.java b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/domain/vo/AppDeviceShareDetailVo.java index d11d6576..f9e8483e 100644 --- a/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/domain/vo/AppDeviceShareDetailVo.java +++ b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/domain/vo/AppDeviceShareDetailVo.java @@ -104,4 +104,41 @@ public class AppDeviceShareDetailVo implements Serializable { * 发送信息 */ private String sendMsg; + + //设备主灯档位 + private String mainLightMode; + + //激光灯档位 + private String laserLightMode; + + //电量百分比 + private String batteryPercentage; + + //充电状态(0没有充电,1正在充电,2为已充满) + private String chargeState; + + //电池剩余续航时间200分钟 + private String batteryRemainingTime; + + /** + * 在线状态(0离线,1在线) + */ + private Integer onlineStatus; + + // 经度 + private String longitude; + + // 纬度 + private String latitude; + + // 逆解析地址 + private String address; + + /** + * 告警状态(0解除告警,1告警) + */ + private String alarmStatus; + + // 灯光亮度 + private String lightBrightness; } diff --git a/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/domain/vo/AppDeviceShareVo.java b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/domain/vo/AppDeviceShareVo.java index b9ea67d8..224ca8ce 100644 --- a/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/domain/vo/AppDeviceShareVo.java +++ b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/domain/vo/AppDeviceShareVo.java @@ -57,6 +57,12 @@ public class AppDeviceShareVo implements Serializable { @ExcelProperty(value = "手机号") private String phonenumber; + /** + * 他人分享手机号 + */ + @ExcelProperty(value = "手机号") + private String otherPhonenumber; + /** * 功能权限(1:灯光模式;2:激光模式;3:开机画面;4:人员信息登记;5:发送信息;6:产品信息) 以逗号分隔 @@ -65,6 +71,11 @@ public class AppDeviceShareVo implements Serializable { @ExcelDictFormat(readConverterExp = "1=:灯光模式;2:激光模式;3:开机画面;4:人员信息登记;5:发送信息;6:产品信息") private String permission; + /** + * 设备类型 + */ + private String typeName; + /** * 备注 */ @@ -73,4 +84,29 @@ public class AppDeviceShareVo implements Serializable { // 设备图片 private String devicePic; + + /** + * 在线状态(0离线,1在线) + */ + private Integer onlineStatus; + + /** + * 电量 百分比 + */ + private String battery; + + /** + * 纬度 + */ + private String latitude; + + /** + * 经度 + */ + private String longitude; + + /** + * 告警状态(0解除告警,1告警) + */ + private String alarmStatus; } diff --git a/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/domain/vo/AppPersonnelInfoRecordsVo.java b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/domain/vo/AppPersonnelInfoRecordsVo.java new file mode 100644 index 00000000..1d05a0ce --- /dev/null +++ b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/domain/vo/AppPersonnelInfoRecordsVo.java @@ -0,0 +1,80 @@ +package com.fuyuanshen.app.domain.vo; + +import com.fuyuanshen.app.domain.AppPersonnelInfoRecords; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.fuyuanshen.common.excel.annotation.ExcelDictFormat; +import com.fuyuanshen.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + + +/** + * 人员信息登记记录视图对象 app_personnel_info_records + * + * @author CYT + * @date 2025-08-22 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = AppPersonnelInfoRecords.class) +public class AppPersonnelInfoRecordsVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 设备id + */ + @ExcelProperty(value = "设备id") + private Long deviceId; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long personnelId; + + /** + * 人员姓名 + */ + @ExcelProperty(value = "人员姓名") + private String name; + + /** + * 职位 + */ + @ExcelProperty(value = "职位") + private String position; + + /** + * 单位名称 + */ + @ExcelProperty(value = "单位名称") + private String unitName; + + /** + * ID号 + */ + @ExcelProperty(value = "ID号") + private String code; + + /** + * 发送信息 + */ + @ExcelProperty(value = "发送信息") + private String sendMsg; + + +} diff --git a/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/mapper/AppDeviceShareMapper.java b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/mapper/AppDeviceShareMapper.java index 01dbf1a8..1de4c14a 100644 --- a/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/mapper/AppDeviceShareMapper.java +++ b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/mapper/AppDeviceShareMapper.java @@ -1,5 +1,6 @@ package com.fuyuanshen.app.mapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.fuyuanshen.app.domain.AppDeviceShare; @@ -16,4 +17,6 @@ import org.apache.ibatis.annotations.Param; */ public interface AppDeviceShareMapper extends BaseMapperPlus { IPage otherDeviceShareList(@Param("bo") AppDeviceShareBo bo, Page page); + + Page selectAppDeviceShareList(@Param("bo") AppDeviceShareBo bo,Page page); } diff --git a/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/mapper/AppPersonnelInfoRecordsMapper.java b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/mapper/AppPersonnelInfoRecordsMapper.java new file mode 100644 index 00000000..bffca130 --- /dev/null +++ b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/mapper/AppPersonnelInfoRecordsMapper.java @@ -0,0 +1,15 @@ +package com.fuyuanshen.app.mapper; + +import com.fuyuanshen.app.domain.AppPersonnelInfoRecords; +import com.fuyuanshen.app.domain.vo.AppPersonnelInfoRecordsVo; +import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 人员信息登记记录Mapper接口 + * + * @author CYT + * @date 2025-08-22 + */ +public interface AppPersonnelInfoRecordsMapper extends BaseMapperPlus { + +} diff --git a/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/service/IAppPersonnelInfoRecordsService.java b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/service/IAppPersonnelInfoRecordsService.java new file mode 100644 index 00000000..a845a858 --- /dev/null +++ b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/service/IAppPersonnelInfoRecordsService.java @@ -0,0 +1,68 @@ +package com.fuyuanshen.app.service; + +import com.fuyuanshen.app.domain.vo.AppPersonnelInfoRecordsVo; +import com.fuyuanshen.app.domain.bo.AppPersonnelInfoRecordsBo; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 人员信息登记记录Service接口 + * + * @author CYT + * @date 2025-08-22 + */ +public interface IAppPersonnelInfoRecordsService { + + /** + * 查询人员信息登记记录 + * + * @param id 主键 + * @return 人员信息登记记录 + */ + AppPersonnelInfoRecordsVo queryById(Long id); + + /** + * 分页查询人员信息登记记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 人员信息登记记录分页列表 + */ + TableDataInfo queryPageList(AppPersonnelInfoRecordsBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的人员信息登记记录列表 + * + * @param bo 查询条件 + * @return 人员信息登记记录列表 + */ + List queryList(AppPersonnelInfoRecordsBo bo); + + /** + * 新增人员信息登记记录 + * + * @param bo 人员信息登记记录 + * @return 是否新增成功 + */ + Boolean insertByBo(AppPersonnelInfoRecordsBo bo); + + /** + * 修改人员信息登记记录 + * + * @param bo 人员信息登记记录 + * @return 是否修改成功 + */ + Boolean updateByBo(AppPersonnelInfoRecordsBo bo); + + /** + * 校验并批量删除人员信息登记记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/service/impl/AppPersonnelInfoRecordsServiceImpl.java b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/service/impl/AppPersonnelInfoRecordsServiceImpl.java new file mode 100644 index 00000000..b330ded9 --- /dev/null +++ b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/service/impl/AppPersonnelInfoRecordsServiceImpl.java @@ -0,0 +1,138 @@ +package com.fuyuanshen.app.service.impl; + +import com.fuyuanshen.common.core.utils.MapstructUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import com.fuyuanshen.app.domain.bo.AppPersonnelInfoRecordsBo; +import com.fuyuanshen.app.domain.vo.AppPersonnelInfoRecordsVo; +import com.fuyuanshen.app.domain.AppPersonnelInfoRecords; +import com.fuyuanshen.app.mapper.AppPersonnelInfoRecordsMapper; +import com.fuyuanshen.app.service.IAppPersonnelInfoRecordsService; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * 人员信息登记记录Service业务层处理 + * + * @author CYT + * @date 2025-08-22 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class AppPersonnelInfoRecordsServiceImpl implements IAppPersonnelInfoRecordsService { + + private final AppPersonnelInfoRecordsMapper baseMapper; + + /** + * 查询人员信息登记记录 + * + * @param id 主键 + * @return 人员信息登记记录 + */ + @Override + public AppPersonnelInfoRecordsVo queryById(Long id){ + return baseMapper.selectVoById(id); + } + + /** + * 分页查询人员信息登记记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 人员信息登记记录分页列表 + */ + @Override + public TableDataInfo queryPageList(AppPersonnelInfoRecordsBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的人员信息登记记录列表 + * + * @param bo 查询条件 + * @return 人员信息登记记录列表 + */ + @Override + public List queryList(AppPersonnelInfoRecordsBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(AppPersonnelInfoRecordsBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(AppPersonnelInfoRecords::getId); + lqw.eq(bo.getDeviceId() != null, AppPersonnelInfoRecords::getDeviceId, bo.getDeviceId()); + lqw.eq(bo.getPersonnelId() != null, AppPersonnelInfoRecords::getPersonnelId, bo.getPersonnelId()); + lqw.like(StringUtils.isNotBlank(bo.getName()), AppPersonnelInfoRecords::getName, bo.getName()); + lqw.eq(StringUtils.isNotBlank(bo.getPosition()), AppPersonnelInfoRecords::getPosition, bo.getPosition()); + lqw.like(StringUtils.isNotBlank(bo.getUnitName()), AppPersonnelInfoRecords::getUnitName, bo.getUnitName()); + lqw.eq(StringUtils.isNotBlank(bo.getCode()), AppPersonnelInfoRecords::getCode, bo.getCode()); + lqw.eq(StringUtils.isNotBlank(bo.getSendMsg()), AppPersonnelInfoRecords::getSendMsg, bo.getSendMsg()); + return lqw; + } + + /** + * 新增人员信息登记记录 + * + * @param bo 人员信息登记记录 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(AppPersonnelInfoRecordsBo bo) { + AppPersonnelInfoRecords add = MapstructUtils.convert(bo, AppPersonnelInfoRecords.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改人员信息登记记录 + * + * @param bo 人员信息登记记录 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(AppPersonnelInfoRecordsBo bo) { + AppPersonnelInfoRecords update = MapstructUtils.convert(bo, AppPersonnelInfoRecords.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(AppPersonnelInfoRecords entity){ + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除人员信息登记记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if(isValid){ + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/fys-modules/fys-app/src/main/resources/mapper/app/AppDeviceShareMapper.xml b/fys-modules/fys-app/src/main/resources/mapper/app/AppDeviceShareMapper.xml index 2202eae4..39033bc1 100644 --- a/fys-modules/fys-app/src/main/resources/mapper/app/AppDeviceShareMapper.xml +++ b/fys-modules/fys-app/src/main/resources/mapper/app/AppDeviceShareMapper.xml @@ -5,6 +5,46 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + diff --git a/fys-modules/fys-app/src/main/resources/mapper/app/AppPersonnelInfoRecordsMapper.xml b/fys-modules/fys-app/src/main/resources/mapper/app/AppPersonnelInfoRecordsMapper.xml new file mode 100644 index 00000000..28b50d97 --- /dev/null +++ b/fys-modules/fys-app/src/main/resources/mapper/app/AppPersonnelInfoRecordsMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/controller/DeviceRepairRecordsController.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/controller/DeviceRepairRecordsController.java new file mode 100644 index 00000000..1304daa6 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/controller/DeviceRepairRecordsController.java @@ -0,0 +1,105 @@ +package com.fuyuanshen.equipment.controller; + +import java.util.List; + +import lombok.RequiredArgsConstructor; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.*; +import cn.dev33.satoken.annotation.SaCheckPermission; +import org.springframework.web.bind.annotation.*; +import org.springframework.validation.annotation.Validated; +import com.fuyuanshen.common.idempotent.annotation.RepeatSubmit; +import com.fuyuanshen.common.log.annotation.Log; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.core.validate.AddGroup; +import com.fuyuanshen.common.core.validate.EditGroup; +import com.fuyuanshen.common.log.enums.BusinessType; +import com.fuyuanshen.common.excel.utils.ExcelUtil; +import com.fuyuanshen.equipment.domain.vo.DeviceRepairRecordsVo; +import com.fuyuanshen.equipment.domain.bo.DeviceRepairRecordsBo; +import com.fuyuanshen.equipment.service.IDeviceRepairRecordsService; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; + +/** + * 设备维修记录 + * + * @author Lion Li + * @date 2025-08-08 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/equipment/repairRecords") +public class DeviceRepairRecordsController extends BaseController { + + private final IDeviceRepairRecordsService deviceRepairRecordsService; + + /** + * 查询设备维修记录列表 + */ + @SaCheckPermission("equipment:repairRecords:list") + @GetMapping("/list") + public TableDataInfo list(DeviceRepairRecordsBo bo, PageQuery pageQuery) { + return deviceRepairRecordsService.queryPageList(bo, pageQuery); + } + + /** + * 导出设备维修记录列表 + */ + @SaCheckPermission("equipment:repairRecords:export") + @Log(title = "设备维修记录", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(DeviceRepairRecordsBo bo, HttpServletResponse response) { + List list = deviceRepairRecordsService.queryList(bo); + ExcelUtil.exportExcel(list, "设备维修记录", DeviceRepairRecordsVo.class, response); + } + + /** + * 获取设备维修记录详细信息 + * + * @param recordId 主键 + */ + @SaCheckPermission("equipment:repairRecords:query") + @GetMapping("/{recordId}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long recordId) { + return R.ok(deviceRepairRecordsService.queryById(recordId)); + } + + /** + * 新增设备维修记录 + */ + @SaCheckPermission("equipment:repairRecords:add") + @Log(title = "设备维修记录", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody DeviceRepairRecordsBo bo) { + return toAjax(deviceRepairRecordsService.insertByBo(bo)); + } + + /** + * 修改设备维修记录 + */ + @SaCheckPermission("equipment:repairRecords:edit") + @Log(title = "设备维修记录", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody DeviceRepairRecordsBo bo) { + return toAjax(deviceRepairRecordsService.updateByBo(bo)); + } + + /** + * 删除设备维修记录 + * + * @param recordIds 主键串 + */ + @SaCheckPermission("equipment:repairRecords:remove") + @Log(title = "设备维修记录", businessType = BusinessType.DELETE) + @DeleteMapping("/{recordIds}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] recordIds) { + return toAjax(deviceRepairRecordsService.deleteWithValidByIds(List.of(recordIds), true)); + } +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/Device.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/Device.java index ae1a00d3..d166aacb 100644 --- a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/Device.java +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/Device.java @@ -1,9 +1,6 @@ package com.fuyuanshen.equipment.domain; -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.annotation.*; import com.fasterxml.jackson.annotation.JsonInclude; import com.fuyuanshen.common.tenant.core.TenantEntity; import io.swagger.v3.oas.annotations.media.Schema; @@ -25,80 +22,87 @@ public class Device extends TenantEntity { * id */ @TableId(value = "id", type = IdType.AUTO) - @Schema(name = "ID") + @Schema(title = "ID") private Long id; - @Schema(name = "设备记录ID") + @Schema(title = "设备记录ID") @TableField(exist = false) private Long assignId; + /** + * 设备分组 + * group_id + */ + @Schema(title = "设备分组") + private Long groupId; + /** * device_type */ - @Schema(name = "设备类型") + @Schema(title = "设备类型") private Long deviceType; - @Schema(name = "设备类型名称") + @Schema(title = "设备类型名称") private String typeName; - @Schema(name = "客户号") + @Schema(title = "客户号") private Long customerId; - @Schema(name = "所属客户") + @Schema(title = "所属客户") private String customerName; /** * 当前所有者 * current_owner_id */ - @Schema(name = "当前所有者") + @Schema(title = "当前所有者") private Long currentOwnerId; /** * 原始所有者(创建者) * original_owner_id */ - @Schema(name = "原始所有者(创建者)") + @Schema(title = "原始所有者(创建者)") private Long originalOwnerId; /** * 原始设备 */ - @Schema(name = "原始设备") + @Schema(title = "原始设备") private Long originalDeviceId; - @Schema(name = "设备编号") + @Schema(title = "设备编号") private String deviceNo; - @Schema(name = "设备名称") + @Schema(title = "设备名称") private String deviceName; - @Schema(name = "设备图片") + @Schema(title = "设备图片") private String devicePic; - @Schema(name = "设备MAC") + @Schema(title = "设备MAC") private String deviceMac; - @Schema(name = "蓝牙名称") + @Schema(title = "蓝牙名称") private String bluetoothName; /** * 设备IMEI * device_imei */ - @Schema(name = "设备IMEI") + @Schema(title = "设备IMEI") private String deviceImei; - @Schema(name = "设备SN") + @Schema(title = "设备SN") private String deviceSn; - @Schema(name = "经度") + @Schema(title = "经度") private String longitude; - @Schema(name = "纬度") + @Schema(title = "纬度") private String latitude; - @Schema(name = "备注") + @Schema(title = "备注") private String remark; /** @@ -106,7 +110,7 @@ public class Device extends TenantEntity { * 0 失效 * 1 正常 */ - @Schema(name = "设备状态") + @Schema(title = "设备状态") private Integer deviceStatus; /** @@ -114,7 +118,7 @@ public class Device extends TenantEntity { * 0 未绑定 * 1 已绑定 */ - @Schema(name = "绑定状态") + @Schema(title = "绑定状态") private Integer bindingStatus; /** @@ -143,4 +147,11 @@ public class Device extends TenantEntity { */ private String subTopic; + /** + * 出厂日期 + * production_date + */ + @Schema(title = "出厂日期") + private Date productionDate; + } diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/DeviceGroup.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/DeviceGroup.java new file mode 100644 index 00000000..16f84142 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/DeviceGroup.java @@ -0,0 +1,56 @@ +package com.fuyuanshen.equipment.domain; + +import com.fuyuanshen.common.tenant.core.TenantEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 设备分组对象 device_group + * + * @author Lion Li + * @date 2025-08-08 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("device_group") +public class DeviceGroup extends TenantEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * 分组名称 + */ + private String groupName; + + /** + * 状态:0-禁用,1-正常 + */ + private Long status; + + /** + * 父分组ID + */ + private Long parentId; + + /** + * 完整分组路径 + */ + private String fullPath; + + /** + * 删除标记:0-未删除,1-已删除 + */ + private Long isDeleted; + + +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/DeviceRepairRecords.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/DeviceRepairRecords.java new file mode 100644 index 00000000..3fca3c4a --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/DeviceRepairRecords.java @@ -0,0 +1,58 @@ +package com.fuyuanshen.equipment.domain; + +import com.fuyuanshen.common.tenant.core.TenantEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; + +import java.io.Serial; + +/** + * 设备维修记录对象 device_repair_records + * + * @author Lion Li + * @date 2025-08-08 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("device_repair_records") +public class DeviceRepairRecords extends TenantEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 维修记录ID + */ + @TableId(value = "record_id") + private Long recordId; + + /** + * 设备ID + */ + private String deviceId; + + /** + * 维修时间 + */ + private Date repairTime; + + /** + * 维修部位 + */ + private String repairPart; + + /** + * 维修原因 + */ + private String repairReason; + + /** + * 维修人员 + */ + private String repairPerson; + + +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/DeviceType.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/DeviceType.java index 76e554ed..d0ab5b0f 100644 --- a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/DeviceType.java +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/DeviceType.java @@ -18,42 +18,42 @@ import lombok.Data; public class DeviceType extends TenantEntity { @TableId(value = "id", type = IdType.AUTO) - @Schema(name = "ID", hidden = true) + @Schema(title = "ID", hidden = true) private Long id; - @Schema(name = "客户号") + @Schema(title = "客户号") private Long customerId; - @Schema(name = "创建该类型的客户") + @Schema(title = "创建该类型的客户") private Long ownerCustomerId; /** * 原始所有者(创建者) * original_owner_id */ - @Schema(name = "原始所有者(创建者)") + @Schema(title = "原始所有者(创建者)") private Long originalOwnerId; /** * 原始设备 */ - @Schema(name = "原始设备类型") + @Schema(title = "原始设备类型") private Long originalDeviceId; @NotBlank(message = "设备类型名称不能为空") - @Schema(name = "类型名称", required = true) + @Schema(title = "类型名称", required = true) private String typeName; - @Schema(name = "是否支持蓝牙") + @Schema(title = "是否支持蓝牙") private Boolean isSupportBle; - @Schema(name = "定位方式", example = "0:无;1:GPS;2:基站;3:wifi;4:北斗") + @Schema(title = "定位方式", example = "0:无;1:GPS;2:基站;3:wifi;4:北斗") private String locateMode; - @Schema(name = "联网方式", example = "0:无;1:4G;2:WIFI") + @Schema(title = "联网方式", example = "0:无;1:4G;2:WIFI") private String networkWay; - @Schema(name = "通讯方式", example = "0:4G;1:蓝牙") + @Schema(title = "通讯方式", example = "0:4G;1:蓝牙") private String communicationMode; /** @@ -65,4 +65,19 @@ public class DeviceType extends TenantEntity { BeanUtil.copyProperties(source, this, CopyOptions.create().setIgnoreNullValue(true)); } + /** + * 型号字典用于APP页面跳转 + * app_model_dictionary + */ + @Schema(title = "型号字典用于APP页面跳转") + private String appModelDictionary; + + /** + * 型号字典用于PC页面跳转 + * pc_model_dictionary + */ + @Schema(title = "型号字典用于PC页面跳转") + private String pcModelDictionary; + + } diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/bo/DeviceGroupBo.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/bo/DeviceGroupBo.java new file mode 100644 index 00000000..e0ac783e --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/bo/DeviceGroupBo.java @@ -0,0 +1,68 @@ +package com.fuyuanshen.equipment.domain.bo; + +import com.fuyuanshen.common.core.validate.AddGroup; +import com.fuyuanshen.common.core.validate.EditGroup; +import com.fuyuanshen.equipment.domain.DeviceGroup; +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; +import io.github.linpeilie.annotations.AutoMapper; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +/** + * 设备分组业务对象 device_group + * + * @author Lion Li + * @date 2025-08-08 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = DeviceGroup.class, reverseConvertGenerate = false) +public class DeviceGroupBo extends BaseEntity { + + /** + * 主键ID + */ + // @NotNull(message = "主键ID不能为空", groups = { EditGroup.class }) + private Long id; + + /** + * 分组名称 + */ + @Schema(title = "分组名称") + @NotBlank(message = "分组名称不能为空", groups = { AddGroup.class, EditGroup.class }) + private String groupName; + + /** + * 状态:0-禁用,1-正常 + */ + @Schema(title = "状态:0-禁用,1-正常") + // @NotNull(message = "状态:0-禁用,1-正常不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long status; + + /** + * 父分组ID + */ + @Schema(title = "父分组ID") + private Long parentId; + + /** + * 完整分组路径 + */ + private String fullPath; + + /** + * 删除标记:0-未删除,1-已删除 + */ + // @NotNull(message = "删除标记:0-未删除,1-已删除不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long isDeleted; + + + @Schema(title = "页码", example = "1") + private Integer pageNum = 1; + + @Schema(title = "每页数据量", example = "10") + private Integer pageSize = 10; + +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/bo/DeviceRepairRecordsBo.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/bo/DeviceRepairRecordsBo.java new file mode 100644 index 00000000..b40b9750 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/bo/DeviceRepairRecordsBo.java @@ -0,0 +1,62 @@ +package com.fuyuanshen.equipment.domain.bo; + +import com.fuyuanshen.common.core.validate.AddGroup; +import com.fuyuanshen.common.core.validate.EditGroup; +import com.fuyuanshen.equipment.domain.DeviceRepairRecords; +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; + +/** + * 设备维修记录业务对象 device_repair_records + * + * @author Lion Li + * @date 2025-08-08 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = DeviceRepairRecords.class, reverseConvertGenerate = false) +public class DeviceRepairRecordsBo extends BaseEntity { + + /** + * 维修记录ID + */ + @NotNull(message = "维修记录ID不能为空", groups = { EditGroup.class }) + private Long recordId; + + /** + * 设备ID + */ + @NotBlank(message = "设备ID不能为空", groups = { AddGroup.class, EditGroup.class }) + private String deviceId; + + /** + * 维修时间 + */ + @NotNull(message = "维修时间不能为空", groups = { AddGroup.class, EditGroup.class }) + private Date repairTime; + + /** + * 维修部位 + */ + @NotBlank(message = "维修部位不能为空", groups = { AddGroup.class, EditGroup.class }) + private String repairPart; + + /** + * 维修原因 + */ + @NotBlank(message = "维修原因不能为空", groups = { AddGroup.class, EditGroup.class }) + private String repairReason; + + /** + * 维修人员 + */ + @NotBlank(message = "维修人员不能为空", groups = { AddGroup.class, EditGroup.class }) + private String repairPerson; + + +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/dto/AppDeviceSendMsgBo.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/dto/AppDeviceSendMsgBo.java index 1b57ec6e..4010c05c 100644 --- a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/dto/AppDeviceSendMsgBo.java +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/dto/AppDeviceSendMsgBo.java @@ -14,9 +14,16 @@ public class AppDeviceSendMsgBo { private List deviceIds; + private List deviceImeiList; + /** * 下发指令 */ private String instructValue; + /** + * 批次号 + */ + private String batchId; + } diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/form/DeviceForm.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/form/DeviceForm.java index 2de4ca34..a51e13bd 100644 --- a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/form/DeviceForm.java +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/form/DeviceForm.java @@ -40,7 +40,7 @@ public class DeviceForm { @Schema(title = "设备MAC") private String deviceMac; - @Schema(name = "蓝牙名称") + @Schema(title = "蓝牙名称") private String bluetoothName; diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/form/DeviceTypeForm.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/form/DeviceTypeForm.java index d4167180..d5e1df70 100644 --- a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/form/DeviceTypeForm.java +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/form/DeviceTypeForm.java @@ -1,13 +1,6 @@ package com.fuyuanshen.equipment.domain.form; -import cn.hutool.core.bean.BeanUtil; -import cn.hutool.core.bean.copier.CopyOptions; -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import com.fuyuanshen.common.tenant.core.TenantEntity; import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; import lombok.Data; /** @@ -18,22 +11,28 @@ import lombok.Data; @Data public class DeviceTypeForm { - @Schema(name = "ID", hidden = true) + @Schema(title = "ID", hidden = true) private Long id; - @Schema(name = "类型名称", required = true) + @Schema(title = "类型名称", required = true) private String typeName; - @Schema(name = "是否支持蓝牙") + @Schema(title = "是否支持蓝牙") private Boolean isSupportBle; - @Schema(name = "定位方式", example = "0:无;1:GPS;2:基站;3:wifi;4:北斗") + @Schema(title = "定位方式", example = "0:无;1:GPS;2:基站;3:wifi;4:北斗") private String locateMode; - @Schema(name = "联网方式", example = "0:无;1:4G;2:WIFI") + @Schema(title = "联网方式", example = "0:无;1:4G;2:WIFI") private String networkWay; - @Schema(name = "通讯方式", example = "0:4G;1:蓝牙") + @Schema(title = "通讯方式", example = "0:4G;1:蓝牙") private String communicationMode; + /** + * 型号字典用于APP页面跳转 + */ + @Schema(title = "型号字典用于APP页面跳转") + private String modelDictionary; + } diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/query/DeviceQueryCriteria.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/query/DeviceQueryCriteria.java index 09f9815f..c9dcca99 100644 --- a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/query/DeviceQueryCriteria.java +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/query/DeviceQueryCriteria.java @@ -65,4 +65,29 @@ public class DeviceQueryCriteria extends BaseEntity { /* app绑定用户id */ private Long bindingUserId; + + + /** + * 使用人员 + */ + private String personnelBy; + + /** + * 是否为管理员 + */ + @Schema(name = "是否为管理员") + private Boolean isAdmin = false; + + /** + * 设备所属分组 + */ + @Schema(name = "设备所属分组") + private Long groupId; + + /** + * 设备地区 + */ + @Schema(name = "设备地区") + private String area; + } diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/query/DeviceTypeQueryCriteria.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/query/DeviceTypeQueryCriteria.java index a2a0ad09..5080edc6 100644 --- a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/query/DeviceTypeQueryCriteria.java +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/query/DeviceTypeQueryCriteria.java @@ -15,24 +15,30 @@ import java.util.Set; @Data public class DeviceTypeQueryCriteria extends BaseEntity implements Serializable { - @Schema(name = "设备类型id") + @Schema(title = "设备类型id") private Long deviceTypeId; - @Schema(name = "型号名称") + @Schema(title = "型号名称") private String typeName; - @Schema(name = "所属客户") + @Schema(title = "所属客户") private Set customerIds; - @Schema(name = "所属客户") + @Schema(title = "所属客户") private Long customerId; - @Schema(name = "com.fuyuanshen") + @Schema(title = "com.fuyuanshen") private Long tenantId; - @Schema(name = "页码", example = "1") + @Schema(title = "页码", example = "1") private Integer pageNum = 1; - @Schema(name = "每页数据量", example = "10") + @Schema(title = "每页数据量", example = "10") private Integer pageSize = 10; + + + /* 是否为管理员 */ + private Boolean isAdmin = false; + + } diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/vo/AppDeviceVo.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/vo/AppDeviceVo.java index 1642c7f7..aafc6fc2 100644 --- a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/vo/AppDeviceVo.java +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/vo/AppDeviceVo.java @@ -76,4 +76,14 @@ public class AppDeviceVo implements Serializable { * 经度 */ private String longitude; + + /** + * 告警状态(0解除告警,1告警) + */ + private String alarmStatus; + + /** + * 设备详情页面 + */ + private String detailPageUrl; } diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/vo/CustomerVo.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/vo/CustomerVo.java index b2ff0481..3debd670 100644 --- a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/vo/CustomerVo.java +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/vo/CustomerVo.java @@ -16,11 +16,11 @@ import java.util.List; @Validated public class CustomerVo { - @Schema(name = "客户ID") + @Schema(title = "客户ID") @NotNull(message = "客户ID不能为空") private Long customerId; - @Schema(name = "设备ID") + @Schema(title = "设备ID") @NotNull(message = "设备ID不能为空") private List deviceIds; diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/vo/DeviceGroupVo.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/vo/DeviceGroupVo.java new file mode 100644 index 00000000..58f6450d --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/vo/DeviceGroupVo.java @@ -0,0 +1,79 @@ +package com.fuyuanshen.equipment.domain.vo; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.fuyuanshen.equipment.domain.DeviceGroup; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.fuyuanshen.common.excel.annotation.ExcelDictFormat; +import com.fuyuanshen.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; +import java.util.List; + + +/** + * 设备分组视图对象 device_group + * + * @author Lion Li + * @date 2025-08-08 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = DeviceGroup.class) +public class DeviceGroupVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键ID + */ + @ExcelProperty(value = "主键ID") + private Long id; + + /** + * 分组名称 + */ + @ExcelProperty(value = "分组名称") + private String groupName; + + /** + * 状态:0-禁用,1-正常 + */ + @ExcelProperty(value = "状态:0-禁用,1-正常") + private String status; + + /** + * 父分组ID + */ + @ExcelProperty(value = "父分组ID") + private Long parentId; + + /** + * 完整分组路径 + */ + @ExcelProperty(value = "完整分组路径") + private String fullPath; + + /** + * 删除标记:0-未删除,1-已删除 + */ + @ExcelProperty(value = "删除标记:0-未删除,1-已删除") + private Long isDeleted; + + /** + * 嵌套子分组 + */ + private List children; + + /** + * 创建时间 + */ + private String createTime; + +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/vo/DeviceRepairRecordsVo.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/vo/DeviceRepairRecordsVo.java new file mode 100644 index 00000000..f66bb67f --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/vo/DeviceRepairRecordsVo.java @@ -0,0 +1,70 @@ +package com.fuyuanshen.equipment.domain.vo; + +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fuyuanshen.equipment.domain.DeviceRepairRecords; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.fuyuanshen.common.excel.annotation.ExcelDictFormat; +import com.fuyuanshen.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + + +/** + * 设备维修记录视图对象 device_repair_records + * + * @author Lion Li + * @date 2025-08-08 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = DeviceRepairRecords.class) +public class DeviceRepairRecordsVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 维修记录ID + */ + @ExcelProperty(value = "维修记录ID") + private Long recordId; + + /** + * 设备ID + */ + @ExcelProperty(value = "设备ID") + private String deviceId; + + /** + * 维修时间 + */ + @ExcelProperty(value = "维修时间") + private Date repairTime; + + /** + * 维修部位 + */ + @ExcelProperty(value = "维修部位") + private String repairPart; + + /** + * 维修原因 + */ + @ExcelProperty(value = "维修原因") + private String repairReason; + + /** + * 维修人员 + */ + @ExcelProperty(value = "维修人员") + private String repairPerson; + + +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/vo/WebDeviceVo.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/vo/WebDeviceVo.java new file mode 100644 index 00000000..94b20e80 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/vo/WebDeviceVo.java @@ -0,0 +1,100 @@ +package com.fuyuanshen.equipment.domain.vo; + +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +@Data +public class WebDeviceVo implements Serializable { + + private Long id; + + /** + * 设备名称 + */ + private String deviceName; + + /** + * 设备IMEI + */ + private String deviceImei; + + /** + * 设备MAC + */ + private String deviceMac; + + /** + * 通讯方式 0:4G;1:蓝牙 + */ + private Integer communicationMode; + + /** + * 设备图片 + */ + private String devicePic; + + /** + * 设备类型 + */ + private String typeName; + + /** + * 蓝牙名称 + */ + private String bluetoothName; + + /** + * 使用人员 + */ + private String personnelBy; + + /** + * 设备状态 + * 0 失效 + * 1 正常 + */ + private Integer deviceStatus; + + /** + * 绑定时间 + */ + private Date bindingTime; + + /** + * 在线状态(0离线,1在线) + */ + private Integer onlineStatus; + + /** + * 电量 百分比 + */ + private String battery; + + /** + * 纬度 + */ + private String latitude; + + /** + * 经度 + */ + private String longitude; + + /** + * 告警状态(0解除告警,1告警) + */ + private String alarmStatus; + + /** + * 设备详情页面 + */ + private String detailPageUrl; + + /** + * 分享用户数量 + */ + private Integer shareUsersNumber; + +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/enums/LightModeEnum.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/enums/LightModeEnum.java new file mode 100644 index 00000000..7394a082 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/enums/LightModeEnum.java @@ -0,0 +1,72 @@ +package com.fuyuanshen.equipment.enums; + +/** + * 灯光模式枚举 + */ +public enum LightModeEnum { + + /** + * 关灯模式 + */ + OFF(0, "关灯"), + + /** + * 强光模式 + */ + HIGH_BEAM(1, "开启强光模式"), + + /** + * 弱光模式 + */ + LOW_BEAM(2, "开启弱光模式"), + + /** + * 爆闪模式 + */ + STROBE(3, "开启爆闪模式"), + + /** + * 泛光模式 + */ + FLOOD(4, "开启泛光模式"); + + private final Integer code; + private final String name; + + LightModeEnum(Integer code, String name) { + this.code = code; + this.name = name; + } + + public Integer getCode() { + return code; + } + + public String getName() { + return name; + } + + /** + * 根据编号获取枚举 + * @param code 编号 + * @return 对应的枚举值 + */ + public static LightModeEnum getByCode(Integer code) { + for (LightModeEnum mode : LightModeEnum.values()) { + if (mode.getCode().equals(code)) { + return mode; + } + } + return null; + } + + /** + * 根据编号获取名称 + * @param code 编号 + * @return 对应的名称 + */ + public static String getNameByCode(Integer code) { + LightModeEnum mode = getByCode(code); + return mode != null ? mode.getName() : null; + } +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/mapper/DeviceGroupMapper.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/mapper/DeviceGroupMapper.java new file mode 100644 index 00000000..a681455c --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/mapper/DeviceGroupMapper.java @@ -0,0 +1,38 @@ +package com.fuyuanshen.equipment.mapper; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fuyuanshen.equipment.domain.Device; +import com.fuyuanshen.equipment.domain.DeviceGroup; +import com.fuyuanshen.equipment.domain.bo.DeviceGroupBo; +import com.fuyuanshen.equipment.domain.vo.DeviceGroupVo; +import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 设备分组Mapper接口 + * + * @author Lion Li + * @date 2025-08-08 + */ +public interface DeviceGroupMapper extends BaseMapperPlus { + + /** + * 查询设备分组列表 + * + * @param bo 设备分组 + * @return 设备分组 + */ + IPage selectRootGroups(@Param("bo") DeviceGroupBo bo, Page page); + + /** + * 查询子分组 + * + * @param id + * @return + */ + List selectByParentId(Long id); + +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/mapper/DeviceMapper.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/mapper/DeviceMapper.java index 57cbd4e7..39896531 100644 --- a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/mapper/DeviceMapper.java +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/mapper/DeviceMapper.java @@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.fuyuanshen.equipment.domain.Device; import com.fuyuanshen.equipment.domain.query.DeviceQueryCriteria; import com.fuyuanshen.equipment.domain.vo.AppDeviceVo; +import com.fuyuanshen.equipment.domain.vo.WebDeviceVo; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; @@ -66,4 +67,8 @@ public interface DeviceMapper extends BaseMapper { * @return */ List findByOriginalDeviceId(Long originalDeviceId); + + AppDeviceVo getDeviceInfo(@Param("deviceMac") String deviceMac); + + Page queryWebDeviceList(Page build, DeviceQueryCriteria bo); } diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/mapper/DeviceRepairRecordsMapper.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/mapper/DeviceRepairRecordsMapper.java new file mode 100644 index 00000000..eb3cec2d --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/mapper/DeviceRepairRecordsMapper.java @@ -0,0 +1,15 @@ +package com.fuyuanshen.equipment.mapper; + +import com.fuyuanshen.equipment.domain.DeviceRepairRecords; +import com.fuyuanshen.equipment.domain.vo.DeviceRepairRecordsVo; +import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 设备维修记录Mapper接口 + * + * @author Lion Li + * @date 2025-08-08 + */ +public interface DeviceRepairRecordsMapper extends BaseMapperPlus { + +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/IDeviceGroupService.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/IDeviceGroupService.java new file mode 100644 index 00000000..9662fef4 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/IDeviceGroupService.java @@ -0,0 +1,59 @@ +package com.fuyuanshen.equipment.service; + +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.equipment.domain.vo.DeviceGroupVo; +import com.fuyuanshen.equipment.domain.bo.DeviceGroupBo; + +import java.util.Collection; +import java.util.List; + +/** + * 设备分组Service接口 + * + * @author Lion Li + * @date 2025-08-08 + */ +public interface IDeviceGroupService { + + /** + * 查询设备分组 + * + * @param id 主键 + * @return 设备分组 + */ + DeviceGroupVo queryById(Long id); + + + /** + * 查询符合条件的设备分组列表 + * + * @param bo 查询条件 + * @return 设备分组列表 + */ + List queryList(DeviceGroupBo bo); + + /** + * 新增设备分组 + * + * @param bo 设备分组 + * @return 是否新增成功 + */ + Boolean insertByBo(DeviceGroupBo bo); + + /** + * 修改设备分组 + * + * @param bo 设备分组 + * @return 是否修改成功 + */ + Boolean updateByBo(DeviceGroupBo bo); + + /** + * 校验并批量删除设备分组信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/IDeviceRepairRecordsService.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/IDeviceRepairRecordsService.java new file mode 100644 index 00000000..8fb838c2 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/IDeviceRepairRecordsService.java @@ -0,0 +1,68 @@ +package com.fuyuanshen.equipment.service; + +import com.fuyuanshen.equipment.domain.vo.DeviceRepairRecordsVo; +import com.fuyuanshen.equipment.domain.bo.DeviceRepairRecordsBo; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 设备维修记录Service接口 + * + * @author Lion Li + * @date 2025-08-08 + */ +public interface IDeviceRepairRecordsService { + + /** + * 查询设备维修记录 + * + * @param recordId 主键 + * @return 设备维修记录 + */ + DeviceRepairRecordsVo queryById(Long recordId); + + /** + * 分页查询设备维修记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 设备维修记录分页列表 + */ + TableDataInfo queryPageList(DeviceRepairRecordsBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的设备维修记录列表 + * + * @param bo 查询条件 + * @return 设备维修记录列表 + */ + List queryList(DeviceRepairRecordsBo bo); + + /** + * 新增设备维修记录 + * + * @param bo 设备维修记录 + * @return 是否新增成功 + */ + Boolean insertByBo(DeviceRepairRecordsBo bo); + + /** + * 修改设备维修记录 + * + * @param bo 设备维修记录 + * @return 是否修改成功 + */ + Boolean updateByBo(DeviceRepairRecordsBo bo); + + /** + * 校验并批量删除设备维修记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/DeviceGroupServiceImpl.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/DeviceGroupServiceImpl.java new file mode 100644 index 00000000..51cf369b --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/DeviceGroupServiceImpl.java @@ -0,0 +1,172 @@ +package com.fuyuanshen.equipment.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.core.utils.MapstructUtils; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.satoken.utils.LoginHelper; +import com.fuyuanshen.equipment.domain.Device; +import com.fuyuanshen.equipment.domain.DeviceTypeGrants; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import com.fuyuanshen.equipment.domain.bo.DeviceGroupBo; +import com.fuyuanshen.equipment.domain.vo.DeviceGroupVo; +import com.fuyuanshen.equipment.domain.DeviceGroup; +import com.fuyuanshen.equipment.mapper.DeviceGroupMapper; +import com.fuyuanshen.equipment.service.IDeviceGroupService; + +import java.text.SimpleDateFormat; +import java.util.List; +import java.util.Map; +import java.util.Collection; +import java.util.stream.Collectors; + +/** + * 设备分组Service业务层处理 + * + * @author Lion Li + * @date 2025-08-08 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class DeviceGroupServiceImpl implements IDeviceGroupService { + + private final DeviceGroupMapper baseMapper; + + + /** + * 查询设备分组 + * + * @param id 主键 + * @return 设备分组 + */ + @Override + public DeviceGroupVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + + /** + * 查询符合条件的设备分组列表 + * + * @param bo 查询条件 + * @return 设备分组列表 + */ + @Override + public List queryList(DeviceGroupBo bo) { + Page page = new Page<>(bo.getPageNum(), bo.getPageSize()); + // 1. 查询顶级分组(parent_id为null) + IPage rootGroups = baseMapper.selectRootGroups(bo, page); + List records = rootGroups.getRecords(); + + // 2. 递归构建树形结构 + return records.stream() + .map(this::buildGroupTree) + .collect(Collectors.toList()); + } + + private DeviceGroupVo buildGroupTree(DeviceGroup group) { + DeviceGroupVo vo = convertToVO(group); + // 递归查询子分组 + List children = baseMapper.selectByParentId(group.getId()); + vo.setChildren(children.stream() + .map(this::buildGroupTree) + .collect(Collectors.toList())); + return vo; + } + + private DeviceGroupVo convertToVO(DeviceGroup group) { + DeviceGroupVo vo = new DeviceGroupVo(); + vo.setId(group.getId()); + vo.setGroupName(group.getGroupName()); + vo.setStatus(group.getStatus() == 1 ? "正常" : "禁用"); + vo.setCreateTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(group.getCreateTime())); + return vo; + } + + private LambdaQueryWrapper buildQueryWrapper(DeviceGroupBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(DeviceGroup::getId); + lqw.like(StringUtils.isNotBlank(bo.getGroupName()), DeviceGroup::getGroupName, bo.getGroupName()); + lqw.eq(bo.getStatus() != null, DeviceGroup::getStatus, bo.getStatus()); + lqw.eq(bo.getParentId() != null, DeviceGroup::getParentId, bo.getParentId()); + lqw.eq(StringUtils.isNotBlank(bo.getFullPath()), DeviceGroup::getFullPath, bo.getFullPath()); + lqw.eq(bo.getIsDeleted() != null, DeviceGroup::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + + /** + * 新增设备分组 + * + * @param bo 设备分组 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(DeviceGroupBo bo) { + + // 验证分组名称唯一性 + DeviceGroup deviceGroup = baseMapper.selectOne(new QueryWrapper().eq("group_name", bo.getGroupName())); + if (deviceGroup != null) { + throw new RuntimeException("分组名称已存在,请勿重复添加!!!"); + } + + // 验证父分组是否存在(如果提供了parentId) + DeviceGroup pDeviceGroup = baseMapper.selectById(bo.getParentId()); + if (bo.getParentId() != null && pDeviceGroup == null) { + throw new RuntimeException("父分组不存在!!!"); + } + + DeviceGroup add = MapstructUtils.convert(bo, DeviceGroup.class); + // validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + + /** + * 修改设备分组 + * + * @param bo 设备分组 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(DeviceGroupBo bo) { + DeviceGroup update = MapstructUtils.convert(bo, DeviceGroup.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(DeviceGroup entity) { + // TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除设备分组信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + // TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/DeviceRepairRecordsServiceImpl.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/DeviceRepairRecordsServiceImpl.java new file mode 100644 index 00000000..5eb4f89f --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/DeviceRepairRecordsServiceImpl.java @@ -0,0 +1,136 @@ +package com.fuyuanshen.equipment.service.impl; + +import com.fuyuanshen.common.core.utils.MapstructUtils; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import com.fuyuanshen.equipment.domain.bo.DeviceRepairRecordsBo; +import com.fuyuanshen.equipment.domain.vo.DeviceRepairRecordsVo; +import com.fuyuanshen.equipment.domain.DeviceRepairRecords; +import com.fuyuanshen.equipment.mapper.DeviceRepairRecordsMapper; +import com.fuyuanshen.equipment.service.IDeviceRepairRecordsService; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * 设备维修记录Service业务层处理 + * + * @author Lion Li + * @date 2025-08-08 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class DeviceRepairRecordsServiceImpl implements IDeviceRepairRecordsService { + + private final DeviceRepairRecordsMapper baseMapper; + + /** + * 查询设备维修记录 + * + * @param recordId 主键 + * @return 设备维修记录 + */ + @Override + public DeviceRepairRecordsVo queryById(Long recordId){ + return baseMapper.selectVoById(recordId); + } + + /** + * 分页查询设备维修记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 设备维修记录分页列表 + */ + @Override + public TableDataInfo queryPageList(DeviceRepairRecordsBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的设备维修记录列表 + * + * @param bo 查询条件 + * @return 设备维修记录列表 + */ + @Override + public List queryList(DeviceRepairRecordsBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(DeviceRepairRecordsBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(DeviceRepairRecords::getRecordId); + lqw.eq(StringUtils.isNotBlank(bo.getDeviceId()), DeviceRepairRecords::getDeviceId, bo.getDeviceId()); + lqw.eq(bo.getRepairTime() != null, DeviceRepairRecords::getRepairTime, bo.getRepairTime()); + lqw.eq(StringUtils.isNotBlank(bo.getRepairPart()), DeviceRepairRecords::getRepairPart, bo.getRepairPart()); + lqw.eq(StringUtils.isNotBlank(bo.getRepairReason()), DeviceRepairRecords::getRepairReason, bo.getRepairReason()); + lqw.eq(StringUtils.isNotBlank(bo.getRepairPerson()), DeviceRepairRecords::getRepairPerson, bo.getRepairPerson()); + return lqw; + } + + /** + * 新增设备维修记录 + * + * @param bo 设备维修记录 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(DeviceRepairRecordsBo bo) { + DeviceRepairRecords add = MapstructUtils.convert(bo, DeviceRepairRecords.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setRecordId(add.getRecordId()); + } + return flag; + } + + /** + * 修改设备维修记录 + * + * @param bo 设备维修记录 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(DeviceRepairRecordsBo bo) { + DeviceRepairRecords update = MapstructUtils.convert(bo, DeviceRepairRecords.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(DeviceRepairRecords entity){ + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除设备维修记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if(isValid){ + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/DeviceServiceImpl.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/DeviceServiceImpl.java index 92ba94ac..357a82d1 100644 --- a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/DeviceServiceImpl.java +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/DeviceServiceImpl.java @@ -106,6 +106,13 @@ public class DeviceServiceImpl extends ServiceImpl impleme criteria.setDeviceType(deviceTypeGrant.getDeviceTypeId()); } } + + // 管理员 + String username = LoginHelper.getUsername(); + if (username.equals("admin")) { + criteria.setIsAdmin(true); + } + IPage devices = deviceMapper.findAll(criteria, page); List records = devices.getRecords(); diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/DeviceTypeServiceImpl.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/DeviceTypeServiceImpl.java index 8f9d7f7a..cdeaacd4 100644 --- a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/DeviceTypeServiceImpl.java +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/DeviceTypeServiceImpl.java @@ -53,8 +53,12 @@ public class DeviceTypeServiceImpl extends ServiceImpl queryAll(DeviceTypeQueryCriteria criteria, Page page) { - criteria.setCustomerId(LoginHelper.getUserId()); - // return + // 管理员 + String username = LoginHelper.getUsername(); + if (!username.equals("admin")) { + criteria.setCustomerId(LoginHelper.getUserId()); + } + IPage deviceTypeIPage = deviceTypeMapper.findAll(criteria, page); return new TableDataInfo(deviceTypeIPage.getRecords(), deviceTypeIPage.getTotal()); } @@ -74,8 +78,16 @@ public class DeviceTypeServiceImpl extends ServiceImpl queryDeviceTypes() { DeviceTypeQueryCriteria criteria = new DeviceTypeQueryCriteria(); - Long userId = LoginHelper.getUserId(); - criteria.setCustomerId(userId); + + // 管理员 + String username = LoginHelper.getUsername(); + if (!username.equals("admin")) { + criteria.setCustomerId(LoginHelper.getUserId()); + + Long userId = LoginHelper.getUserId(); + criteria.setCustomerId(userId); + } + return deviceTypeMapper.findAll(criteria); } @@ -157,8 +169,12 @@ public class DeviceTypeServiceImpl extends ServiceImpl + + + + + + + + + + diff --git a/fys-modules/fys-equipment/src/main/resources/mapper/equipment/DeviceMapper.xml b/fys-modules/fys-equipment/src/main/resources/mapper/equipment/DeviceMapper.xml index 8af7215a..51f0a638 100644 --- a/fys-modules/fys-equipment/src/main/resources/mapper/equipment/DeviceMapper.xml +++ b/fys-modules/fys-equipment/src/main/resources/mapper/equipment/DeviceMapper.xml @@ -74,8 +74,11 @@ and da.create_time between #{criteria.params.beginTime} and #{criteria.params.endTime} - AND da.assignee_id = #{criteria.currentOwnerId} - AND dg.customer_id = #{criteria.currentOwnerId} + + + AND da.assignee_id = #{criteria.currentOwnerId} + AND dg.customer_id = #{criteria.currentOwnerId} + ) AS ranked WHERE rn = 1 @@ -148,6 +151,7 @@ dt.type_name, dt.communication_mode, d.bluetooth_name, + dt.model_dictionary detailPageUrl, c.binding_time from device d inner join device_type dt on d.device_type = dt.id @@ -177,6 +181,7 @@ d.device_pic, dt.type_name, dt.communication_mode, + dt.model_dictionary detailPageUrl, d.bluetooth_name from device d inner join device_type dt on d.device_type = dt.id @@ -210,5 +215,63 @@ FROM device WHERE original_device_id = #{originalDeviceId} + + \ No newline at end of file diff --git a/fys-modules/fys-equipment/src/main/resources/mapper/equipment/DeviceRepairRecordsMapper.xml b/fys-modules/fys-equipment/src/main/resources/mapper/equipment/DeviceRepairRecordsMapper.xml new file mode 100644 index 00000000..46af59f4 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/resources/mapper/equipment/DeviceRepairRecordsMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysDictDataController.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysDictDataController.java index ff3b6dcc..4ba67de4 100644 --- a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysDictDataController.java +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysDictDataController.java @@ -2,6 +2,7 @@ package com.fuyuanshen.system.controller.system; import cn.dev33.satoken.annotation.SaCheckPermission; import cn.hutool.core.util.ObjectUtil; +import com.fuyuanshen.common.core.constant.CacheNames; import com.fuyuanshen.common.log.annotation.Log; import com.fuyuanshen.common.web.core.BaseController; import com.fuyuanshen.common.mybatis.core.page.PageQuery; @@ -14,10 +15,12 @@ import com.fuyuanshen.system.domain.vo.SysDictDataVo; import com.fuyuanshen.system.service.ISysDictDataService; import com.fuyuanshen.system.service.ISysDictTypeService; import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.CacheEvict; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import jakarta.servlet.http.HttpServletResponse; + import java.util.ArrayList; import java.util.List; @@ -73,6 +76,8 @@ public class SysDictDataController extends BaseController { */ @GetMapping(value = "/type/{dictType}") public R> dictType(@PathVariable String dictType) { + // 使用时先清除缓存再查询 + dictTypeService.clearDictTypeCache(dictType); List data = dictTypeService.selectDictDataByType(dictType); if (ObjectUtil.isNull(data)) { data = new ArrayList<>(); @@ -94,6 +99,7 @@ public class SysDictDataController extends BaseController { return R.ok(); } + /** * 修改保存字典类型 */ @@ -105,6 +111,7 @@ public class SysDictDataController extends BaseController { return R.fail("修改字典数据'" + dict.getDictValue() + "'失败,字典键值已存在"); } dictDataService.updateDictData(dict); + return R.ok(); } diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysDictDataService.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysDictDataService.java index 1f8fc322..d5f03758 100644 --- a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysDictDataService.java +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysDictDataService.java @@ -73,4 +73,9 @@ public interface ISysDictDataService { */ boolean checkDictDataUnique(SysDictDataBo dict); + /** + * 清空字典缓存 + */ + void clearDictTypeCache(String dictType); + } diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysDictTypeService.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysDictTypeService.java index 004b6e58..183ceb59 100644 --- a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysDictTypeService.java +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysDictTypeService.java @@ -92,4 +92,9 @@ public interface ISysDictTypeService { * @return 结果 */ boolean checkDictTypeUnique(SysDictTypeBo dictType); + + /** + * 清空字典缓存 + */ + void clearDictTypeCache(String dictType); } diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysDictDataServiceImpl.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysDictDataServiceImpl.java index adcf47d1..19f55440 100644 --- a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysDictDataServiceImpl.java +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysDictDataServiceImpl.java @@ -18,6 +18,7 @@ import com.fuyuanshen.system.domain.vo.SysDictDataVo; import com.fuyuanshen.system.mapper.SysDictDataMapper; import com.fuyuanshen.system.service.ISysDictDataService; import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; import org.springframework.stereotype.Service; @@ -154,4 +155,12 @@ public class SysDictDataServiceImpl implements ISysDictDataService { return true; } + + // 清除指定dictType的缓存 + @CacheEvict(cacheNames = CacheNames.SYS_DICT, key = "#dictType") + public void clearDictTypeCache(String dictType) { + // 仅用于清除缓存 + } + + } diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysDictTypeServiceImpl.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysDictTypeServiceImpl.java index 822a2b87..692675c6 100644 --- a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysDictTypeServiceImpl.java +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysDictTypeServiceImpl.java @@ -28,6 +28,7 @@ import com.fuyuanshen.system.domain.vo.SysDictTypeVo; import com.fuyuanshen.system.mapper.SysDictDataMapper; import com.fuyuanshen.system.mapper.SysDictTypeMapper; import com.fuyuanshen.system.service.ISysDictTypeService; +import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; @@ -294,4 +295,11 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService return BeanUtil.copyToList(list, DictDataDTO.class); } + + // 清除指定dictType的缓存 + @CacheEvict(cacheNames = CacheNames.SYS_DICT, key = "#dictType") + public void clearDictTypeCache(String dictType) { + // 仅用于清除缓存 + } + }