20 Commits

Author SHA1 Message Date
dyf
03f453f901 Merge pull request 'refactor(equipment): 将业务文件和操作视频功能从app模块迁移到equipment模块' (#25) from liwenlong/fys-Multi-tenant:jingquan into jingquan
Reviewed-on: dyf/fys-Multi-tenant#25
2026-02-27 15:29:26 +08:00
e23c5267ee refactor(equipment): 将业务文件和操作视频功能从app模块迁移到equipment模块
- 移动AppBusinessFile相关类到equipment模块
- 移动AppOperationVideo相关类到equipment模块
- 更新所有导入路径以指向新的equipment包结构
- 重构设备创建流程中的文件克隆逻辑
- 添加cloneFiles方法支持从设备类型复制文件和视频到新设备
- 优化DeviceXinghanBizService中的设备验证逻辑
- 更新Mapper XML命名空间和返回类型引用
- 调整设备导入Excel的必填字段验证规则
2026-02-27 15:13:53 +08:00
dyf
346792d5c1 Merge pull request 'feat(device): 新增阿里云语音通知功能并扩展设备查询接口' (#24) from liwenlong/fys-Multi-tenant:jingquan into jingquan
Reviewed-on: dyf/fys-Multi-tenant#24
2026-01-30 16:06:57 +08:00
cb87871982 feat(device): 新增阿里云语音通知功能并扩展设备查询接口
- 集成阿里云语音服务,实现TTS语音拨号功能
- 添加异步报警拨号机制,支持SOS紧急呼叫
- 新增按设备名称查询设备的接口和SQL
- 扩展设备类型统计和批量查询功能
- 添加租户ID字段支持多租户设备管理
- 配置阿里云语音API依赖和客户端单例模式
2026-01-30 16:03:13 +08:00
dyf
b280038502 Merge pull request 'fix(controller): 修复新增设备日志注解配置' (#23) from liwenlong/fys-Multi-tenant:jingquan into jingquan
Reviewed-on: dyf/fys-Multi-tenant#23
2026-01-28 16:33:25 +08:00
3e119b1dd8 feat(device): 添加设备图片字段映射
- 在设备服务实现中添加设备图片字段的赋值逻辑
- 将设备类型中的图片信息同步到设备对象中
2026-01-19 16:39:01 +08:00
cab0884d7f fix(controller): 修复新增设备日志注解配置
- 将 @Operation 注解替换为 @Log 注解以正确记录操作日志
- 保持新增设备功能的核心逻辑不变
- 确保日志标题正确显示为"新增设备"
2026-01-15 17:21:46 +08:00
dyf
ca11ef0e35 Merge pull request 'feat(equipment): 添加设备图片上传功能' (#22) from liwenlong/fys-Multi-tenant:jingquan into jingquan
Reviewed-on: dyf/fys-Multi-tenant#22
2026-01-12 11:15:56 +08:00
ef2dc6a6f6 feat(equipment): 添加设备图片上传功能
- 在DeviceType实体类中新增devicePic字段用于存储设备图片
- 修改控制器方法参数从RequestBody改为ModelAttribute以支持文件上传
- 更新DeviceTypeForm表单类添加MultipartFile类型的file字段
- 实现图片上传服务集成OSS存储和文件哈希处理
- 添加HTTP到HTTPS的URL强制转换机制
- 更新数据库操作逻辑以支持图片路径存储
- 在设备创建流程中集成设备类型图片信息传递
2026-01-12 11:06:44 +08:00
dff37b245a Merge branch 'dyf-device' into 6170 2025-12-26 16:22:00 +08:00
d7c4d22de3 该设备类型下已有设备,无法修改设备类型名称!!! 2025-12-26 16:21:33 +08:00
6a058318f2 设备记录列表显示问题修改 2025-12-26 15:54:14 +08:00
f2d74b8f17 Merge branch 'dyf-device' into 6170 2025-12-22 15:09:39 +08:00
af42a2199c 根据用户ID查询菜单 2025-12-22 15:09:07 +08:00
aaf142ca67 提交 2025-12-19 17:55:48 +08:00
c0dfe36b59 cn.idev.excel 2025-12-19 16:21:56 +08:00
c480bda112 围栏进出记录 2025-12-19 14:06:01 +08:00
8c636d0484 设备维修记录 2025-12-12 14:55:03 +08:00
0c474ae1f3 维修时间 2025-12-11 15:24:46 +08:00
b85664900e 分页查询围栏进出记录列表 2025-12-10 09:39:00 +08:00
73 changed files with 742 additions and 267 deletions

View File

@ -0,0 +1,64 @@
package com.fuyuanshen;
import com.fuyuanshen.equipment.utils.AlibabaTTSUtil;
import com.fuyuanshen.equipment.utils.AudioProcessUtil;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* @author: 默苍璃
* @date: 2025-12-1518:51
*/
public class Text {
public static void main(String[] args) throws IOException {
String text = "简述人生观的主要内容。\n" +
"人生观的主要内容包括以下三个方面:\n" +
"1.人生目的:回答“人为什么活着”的根本问题。它规定了人生的方向,是人生观的核心。\n" +
"2.人生态度:回答“人应该怎样活着”的问题。它是指人们通过生活实践形成的对人生问题的一种稳定的心理倾向和基本意图。\n" +
"3.人生价值:回答“什么样的人生才有意义”的问题。它是指人的生命及其实践活动对于社会和个人所具有的作用和意义。\n" +
"人生目的、人生态度和人生价值相互联系、相辅相成,共同构成一个有机整体。\n" +
"人生目的是人生观的核心,它决定人生态度和人生价值的方向;人生态度影响人生目的的实现和人生价值的创造;人生价值是衡量人生观正确与否的尺度。";
AlibabaTTSUtil alibabaTTSUtil = new AlibabaTTSUtil();
AudioProcessUtil audioProcessUtil = new AudioProcessUtil();
byte[] rawPcmData = alibabaTTSUtil.generateStandardPcmData(text);
// 使用AudioProcessUtil转换成带头44字节 PCM
byte[] pcmData = audioProcessUtil.rawPcmToStandardWav(rawPcmData);
// String savedPath = audioProcessUtil.saveWavToFile(pcmData, "test_output.wav");
// if (savedPath != null) {
// log.info("测试文件已保存: {}", savedPath);
// }
// 保存WAV文件到本地
String savedPath = saveByteArrayToFile(pcmData, "人生观.wav");
if (savedPath != null) {
System.out.println("WAV文件已保存: " + savedPath);
}
}
private static String saveByteArrayToFile(byte[] data, String filename) throws IOException {
// 确定保存路径(可以是临时目录或指定目录)
String directory = System.getProperty("java.io.tmpdir"); // 使用系统临时目录
File dir = new File(directory);
if (!dir.exists()) {
dir.mkdirs();
}
// 创建完整文件路径
File file = new File(dir, filename);
// 写入文件
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write(data);
}
return file.getAbsolutePath();
}
}

View File

@ -1,8 +1,8 @@
package com.fuyuanshen.app.controller;
import com.fuyuanshen.app.domain.bo.AppBusinessFileBo;
import com.fuyuanshen.equipment.domain.bo.AppBusinessFileBo;
import com.fuyuanshen.app.domain.dto.AppFileDto;
import com.fuyuanshen.app.domain.vo.AppFileVo;
import com.fuyuanshen.equipment.domain.vo.AppFileVo;
import com.fuyuanshen.app.service.AppFileService;
import com.fuyuanshen.common.core.domain.R;
import com.fuyuanshen.common.web.core.BaseController;

View File

@ -1,8 +1,8 @@
package com.fuyuanshen.app.controller;
import com.fuyuanshen.app.domain.bo.AppOperationVideoBo;
import com.fuyuanshen.app.domain.vo.AppOperationVideoVo;
import com.fuyuanshen.app.service.IAppOperationVideoService;
import com.fuyuanshen.equipment.domain.bo.AppOperationVideoBo;
import com.fuyuanshen.equipment.domain.vo.AppOperationVideoVo;
import com.fuyuanshen.equipment.service.IAppOperationVideoService;
import com.fuyuanshen.common.core.domain.R;
import com.fuyuanshen.common.core.domain.model.AppLoginUser;
import com.fuyuanshen.common.satoken.utils.AppLoginHelper;

View File

@ -64,4 +64,5 @@ public class AppVideoController extends BaseController {
public R<String> extract(@RequestParam("file") MultipartFile file) throws Exception {
return R.ok("Success",audioProcessService.extract(file));
}
}

View File

@ -26,6 +26,7 @@ import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
import java.util.Map;
/**
* HBY670设备控制类
@ -133,7 +134,7 @@ public class AppDeviceXinghanController extends BaseController {
}
// @Log("新增设备")
@Operation(summary = "新增设备")
@Log(title = "新增设备")
@PostMapping(value = "/add")
public R<Void> addDevice(@RequestBody DeviceForm deviceForm) {
try {
@ -144,4 +145,26 @@ public class AppDeviceXinghanController extends BaseController {
return R.ok();
}
@PostMapping(value = "/GetDeviceByName")
@Operation(summary = "通过蓝牙名/设备名称查询设备")
public R<Object> GetDeviceByName(@RequestBody DeviceForm deviceForm) {
Object device = appDeviceService.GetDeviceByName(deviceForm);
return R.ok(device);
}
@PostMapping(value = "/getEquipCountByType")
@Operation(summary = "查询某个类型下的设备总数量")
public R<Object> getEquipCountByType(@RequestBody DeviceForm deviceForm) {
Object device = appDeviceService.getEquipCountByType(deviceForm);
return R.ok(device);
}
@PostMapping(value = "/getEquipAllByType")
@Operation(summary = "查询某个类型下的设备")
public R<List<Map<String,Object>>> getEquipAllByType(@RequestBody DeviceForm deviceForm){
List<Map<String,Object>> list=appDeviceService.getEquipAllByType(deviceForm);
return R.ok(list);
}
}

View File

@ -227,7 +227,7 @@ public class AppDeviceShareService {
appDeviceShare.setPermission(bo.getPermission());
appDeviceShare.setCreateBy(userId);
return appDeviceShareMapper.insert(appDeviceShare);
}
}
}
public int remove(Long[] ids) {

View File

@ -1,13 +1,14 @@
package com.fuyuanshen.app.service;
import com.fuyuanshen.app.domain.bo.AppBusinessFileBo;
import com.fuyuanshen.equipment.domain.bo.AppBusinessFileBo;
import com.fuyuanshen.app.domain.dto.AppFileDto;
import com.fuyuanshen.app.domain.vo.AppBusinessFileVo;
import com.fuyuanshen.app.domain.vo.AppFileVo;
import com.fuyuanshen.equipment.domain.vo.AppBusinessFileVo;
import com.fuyuanshen.equipment.domain.vo.AppFileVo;
import com.fuyuanshen.common.core.exception.ServiceException;
import com.fuyuanshen.common.oss.core.OssClient;
import com.fuyuanshen.common.oss.factory.OssFactory;
import com.fuyuanshen.common.satoken.utils.AppLoginHelper;
import com.fuyuanshen.equipment.service.IAppBusinessFileService;
import com.fuyuanshen.equipment.utils.FileHashUtil;
import com.fuyuanshen.system.domain.vo.SysOssVo;
import com.fuyuanshen.system.service.ISysOssService;

View File

@ -10,22 +10,43 @@ import org.springframework.integration.mqtt.core.MqttPahoClientFactory;
@Configuration
public class MqttConfiguration {
@Autowired
private MqttPropertiesConfig mqttPropertiesConfig;
/** 创建连接工厂 **/
/**
* 创建连接工厂
**/
@Bean
public MqttPahoClientFactory mqttPahoClientFactory(){
public MqttPahoClientFactory mqttPahoClientFactory() {
DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
MqttConnectOptions options = new MqttConnectOptions();
options.setCleanSession(true); //设置新会话
options.setUserName(mqttPropertiesConfig.getUsername());
options.setPassword(mqttPropertiesConfig.getPassword().toCharArray());
options.setServerURIs(new String[]{mqttPropertiesConfig.getUrl()});
options.setCleanSession(true); // 设置新会话
// 修复用户名为null时的空指针异常
String username = mqttPropertiesConfig.getUsername();
if (username != null) {
options.setUserName(username);
}
// 修复密码为null时的空指针异常
String password = mqttPropertiesConfig.getPassword();
if (password != null) {
options.setPassword(password.toCharArray());
}
// 修复URL为null时的空指针异常
String url = mqttPropertiesConfig.getUrl();
if (url != null) {
options.setServerURIs(new String[]{url});
}
options.setAutomaticReconnect(true); // 启用自动重连
options.setConnectionTimeout(10); // 设置连接超时时间
options.setKeepAliveInterval(60); // 设置心跳间隔
factory.setConnectionOptions(options);
return factory;
}
}

View File

@ -39,8 +39,14 @@ public class MqttInboundConfiguration {
public MessageProducer messageProducer(){
// 生成一个不重复的随机数
String clientId = mqttPropertiesConfig.getSubClientId() + "_" + UUID.fastUUID();
// 修复URL为null时的空指针异常
String url = mqttPropertiesConfig.getUrl();
if (url == null) {
throw new IllegalStateException("MQTT服务器URL未配置");
}
MqttPahoMessageDrivenChannelAdapter mqttPahoMessageDrivenChannelAdapter = new MqttPahoMessageDrivenChannelAdapter(
mqttPropertiesConfig.getUrl(),
url,
clientId,
mqttPahoClientFactory,
mqttPropertiesConfig.getSubTopic().split(",")

View File

@ -15,11 +15,14 @@ import org.springframework.messaging.MessageHandler;
@Configuration
@Slf4j
public class MqttOutboundConfiguration {
@Autowired
private MqttPropertiesConfig mqttPropertiesConfig;
@Autowired
private MqttPahoClientFactory mqttPahoClientFactory;
// 消息通道
@Bean
public MessageChannel mqttOutboundChannel(){
@ -32,8 +35,14 @@ public class MqttOutboundConfiguration {
@ServiceActivator(inputChannel = "mqttOutboundChannel") // 指定处理器针对哪个通道的消息进行处理
public MessageHandler mqttOutboundMessageHandler(){
String clientId = mqttPropertiesConfig.getPubClientId() + "_" + UUID.fastUUID();
// 修复URL为null时的空指针异常
String url = mqttPropertiesConfig.getUrl();
if (url == null) {
throw new IllegalStateException("MQTT服务器URL未配置");
}
MqttPahoMessageHandler mqttPahoMessageHandler = new MqttPahoMessageHandler(
mqttPropertiesConfig.getUrl(),
url,
clientId,
mqttPahoClientFactory
);

View File

@ -1,6 +1,7 @@
package com.fuyuanshen.global.mqtt.constants;
public class DeviceRedisKeyConstants {
public static final String DEVICE_KEY_PREFIX = "device:";
// 设备上报状态
public static final String DEVICE_STATUS_KEY_PREFIX = ":status";
@ -52,4 +53,5 @@ public class DeviceRedisKeyConstants {
* 告警信息
*/
public static final String DEVICE_ALARM_MESSAGE_KEY_PREFIX = ":alarmMessage";
}

View File

@ -36,7 +36,6 @@ public class LightingCommandTypeConstants {
*/
public static final String SEND_MESSAGE = "Light_6";
/**
* 报警模式
*/

View File

@ -57,12 +57,12 @@ public class BjqAlarmRule implements MqttMessageRule {
if (StringUtils.isNotBlank(convertValue)) {
// 将设备状态信息存储到Redis中
String deviceRedisKey = GlobalConstants.GLOBAL_REDIS_KEY + DeviceRedisKeyConstants.DEVICE_KEY_PREFIX + context.getDeviceImei() + DEVICE_ALARM_KEY_PREFIX;
String sendMessageIng = GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + context.getDeviceImei() + ":messageSending";
String sendMessageIng = GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + context.getDeviceImei() + ":messageSending";
if ("1".equals(convertValue)) {
RedisUtils.setCacheObject(sendMessageIng, "1", Duration.ofDays(1));
// 存储到Redis
RedisUtils.setCacheObject(deviceRedisKey, "1");
}else if ("0".equals(convertValue)){
} else if ("0".equals(convertValue)) {
RedisUtils.deleteObject(sendMessageIng);
RedisUtils.deleteObject(deviceRedisKey);
}

View File

@ -71,5 +71,4 @@ public class BjqLaserModeSettingsRule implements MqttMessageRule {
}
}

View File

@ -45,6 +45,7 @@ public class BjqModeRule implements MqttMessageRule {
return LightingCommandTypeConstants.LIGHT_MODE;
}
@Override
public void execute(MqttRuleContext context) {
String functionAccess = FUNCTION_ACCESS_KEY + context.getDeviceImei();

View File

@ -1,32 +1,18 @@
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.*;
/**
* 定位数据命令处理
@ -55,4 +41,5 @@ public class BjqPersonnelInfoDataRule implements MqttMessageRule {
RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.FAILED.getCode(), Duration.ofSeconds(30));
}
}
}

View File

@ -27,11 +27,12 @@ public class MqttMessageConsumer {
@Autowired
private DeviceMapper deviceMapper;
// 创建两个线程池:一个用于消息获取,一个用于业务处理
private ExecutorService messageConsumerPool = Executors.newFixedThreadPool(3);
private ExecutorService messageProcessorPool = Executors.newFixedThreadPool(10);
// 初始化方法,启动消息监听
@PostConstruct
public void start() {
@ -130,4 +131,5 @@ public class MqttMessageConsumer {
log.error("业务处理线程 {} 处理消息时发生错误: {}", threadName, message, e);
}
}
}

View File

@ -22,6 +22,8 @@ public class OnlineStatusTask {
@Autowired
private DeviceMapper deviceMapper;
// 使用cron表达式每分钟的第0秒执行
@Scheduled(cron = "0 */3 * * * ?")
public void cronTask() {
@ -37,4 +39,5 @@ public class OnlineStatusTask {
}
});
}
}

View File

@ -157,4 +157,4 @@ public class CaptchaController {
return captchaVo;
}
}
}

View File

@ -98,6 +98,7 @@ public class DeviceControlCenterController extends BaseController {
return R.ok(appDeviceService.getDeviceInfo(deviceMac));
}
/**
* 指令下发记录
*/
@ -106,6 +107,7 @@ public class DeviceControlCenterController extends BaseController {
return appDeviceService.getInstructionRecord(dto, pageQuery);
}
/**
* 导出
*/

View File

@ -1,19 +1,15 @@
package com.fuyuanshen.web.controller.device;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.fuyuanshen.app.domain.bo.AppOperationVideoBo;
import com.fuyuanshen.app.domain.dto.AppDeviceLogoUploadDto;
import com.fuyuanshen.equipment.domain.bo.AppOperationVideoBo;
import com.fuyuanshen.app.domain.dto.AppFileDto;
import com.fuyuanshen.common.core.domain.R;
import com.fuyuanshen.common.core.exception.ServiceException;
import com.fuyuanshen.common.log.annotation.Log;
import com.fuyuanshen.common.log.enums.BusinessType;
import com.fuyuanshen.common.mybatis.core.page.PageQuery;
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
import com.fuyuanshen.common.ratelimiter.annotation.FunctionAccessAnnotation;
import com.fuyuanshen.common.web.core.BaseController;
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.domain.Dto.DeviceDebugEditDto;
import com.fuyuanshen.web.domain.Dto.DeviceDebugLogoUploadDto;
@ -28,8 +24,6 @@ import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* 联调中心
@ -125,7 +119,7 @@ public class DeviceDebugController extends BaseController {
}
deviceDebugService.delFile(bo.getFileIds());
// 修改操作视频
if (bo.getVideoUrl().isEmpty()) {
if (!bo.getVideoUrl().isEmpty()) {
AppOperationVideoBo appOperationVideoBo = new AppOperationVideoBo();
appOperationVideoBo.setDeviceIds(new Long[]{ bo.getDeviceId() });
appOperationVideoBo.setVideoUrl(bo.getVideoUrl());

View File

@ -59,6 +59,7 @@ public class DeviceFenceAccessRecordController extends BaseController {
ExcelUtil.exportExcel(list, "围栏进出记录", DeviceFenceAccessRecordVo.class, response);
}
/**
* 获取围栏进出记录详细信息
*

View File

@ -103,6 +103,7 @@ public class DeviceGeoFenceController extends BaseController {
return toAjax(deviceGeoFenceService.updateByBo(bo));
}
/**
* 删除电子围栏
*
@ -130,6 +131,7 @@ public class DeviceGeoFenceController extends BaseController {
return ResponseEntity.ok(response);
}
/**
* 添加电子围栏终端
*

View File

@ -1,10 +1,7 @@
package com.fuyuanshen.web.domain.vo;
import com.fuyuanshen.app.domain.vo.AppBusinessFileVo;
import com.fuyuanshen.app.domain.vo.AppFileVo;
import com.fuyuanshen.app.domain.vo.AppOperationVideoVo;
import com.fuyuanshen.equipment.domain.Device;
import com.fuyuanshen.equipment.domain.vo.AppDeviceVo;
import com.fuyuanshen.equipment.domain.vo.AppFileVo;
import com.fuyuanshen.equipment.domain.vo.AppOperationVideoVo;
import lombok.Data;
import java.util.List;

View File

@ -1,13 +1,13 @@
package com.fuyuanshen.web.service.device;
import cn.hutool.core.collection.CollUtil;
import com.fuyuanshen.app.domain.AppBusinessFile;
import com.fuyuanshen.app.domain.AppOperationVideo;
import com.fuyuanshen.app.domain.bo.AppBusinessFileBo;
import com.fuyuanshen.app.domain.bo.AppOperationVideoBo;
import com.fuyuanshen.equipment.domain.AppBusinessFile;
import com.fuyuanshen.equipment.domain.AppOperationVideo;
import com.fuyuanshen.equipment.domain.bo.AppBusinessFileBo;
import com.fuyuanshen.equipment.domain.bo.AppOperationVideoBo;
import com.fuyuanshen.app.domain.dto.AppFileDto;
import com.fuyuanshen.app.service.IAppBusinessFileService;
import com.fuyuanshen.app.service.IAppOperationVideoService;
import com.fuyuanshen.equipment.service.IAppBusinessFileService;
import com.fuyuanshen.equipment.service.IAppOperationVideoService;
import com.fuyuanshen.common.core.exception.ServiceException;
import com.fuyuanshen.common.satoken.utils.AppLoginHelper;
import com.fuyuanshen.equipment.service.DeviceService;

View File

@ -1,11 +1,11 @@
package com.fuyuanshen.web.service.device;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
@ -15,20 +15,17 @@ 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.AppDeviceLogoUploadDto;
import com.fuyuanshen.app.domain.dto.DeviceInstructDto;
import com.fuyuanshen.app.domain.vo.AppDeviceDetailVo;
import com.fuyuanshen.app.domain.vo.AppPersonnelInfoVo;
import com.fuyuanshen.app.mapper.AppPersonnelInfoMapper;
import com.fuyuanshen.app.mapper.AppPersonnelInfoRecordsMapper;
import com.fuyuanshen.equipment.service.IAppBusinessFileService;
import com.fuyuanshen.equipment.service.IAppOperationVideoService;
import com.fuyuanshen.common.core.constant.GlobalConstants;
import com.fuyuanshen.common.core.domain.R;
import com.fuyuanshen.common.core.domain.model.AppLoginUser;
import com.fuyuanshen.common.core.domain.model.LoginUser;
import com.fuyuanshen.common.core.exception.BadRequestException;
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;
@ -40,7 +37,6 @@ import com.fuyuanshen.equipment.domain.dto.AppDeviceSendMsgBo;
import com.fuyuanshen.equipment.domain.form.DeviceForm;
import com.fuyuanshen.equipment.domain.query.DeviceTypeQueryCriteria;
import com.fuyuanshen.equipment.enums.DeviceActiveStatusEnum;
import com.fuyuanshen.equipment.enums.LightModeEnum;
import com.fuyuanshen.equipment.mapper.DeviceLogMapper;
import com.fuyuanshen.equipment.mapper.DeviceMapper;
import com.fuyuanshen.equipment.mapper.DeviceTypeGrantsMapper;
@ -51,26 +47,23 @@ import com.fuyuanshen.global.mqtt.base.MqttXinghanJson;
import com.fuyuanshen.global.mqtt.config.MqttGateway;
import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants;
import com.fuyuanshen.global.mqtt.constants.MqttConstants;
import com.fuyuanshen.system.domain.vo.SysOssVo;
import com.fuyuanshen.system.domain.vo.SysRoleVo;
import com.fuyuanshen.web.domain.Dto.DeviceDebugLogoUploadDto;
import com.fuyuanshen.web.domain.Dto.DeviceXinghanInstructDto;
import com.fuyuanshen.web.domain.vo.DeviceXinghanDetailVo;
import com.fuyuanshen.web.enums.AlarmTypeEnum;
import com.fuyuanshen.web.util.AliyunVoiceUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import static com.fuyuanshen.common.core.constant.GlobalConstants.GLOBAL_REDIS_KEY;
import static com.fuyuanshen.common.core.utils.ImageToCArrayConverter.convertHexToDecimal;
@ -91,8 +84,11 @@ public class DeviceXinghanBizService {
private final IDeviceAlarmService deviceAlarmService;
private final DeviceTypeGrantsMapper deviceTypeGrantsMapper;
private final DeviceAssignmentsService deviceAssignmentsService;
private final IAppBusinessFileService appBusinessFileService;
private final IAppOperationVideoService appOperationVideoService;
@Autowired
private ObjectMapper objectMapper;
private final AliyunVoiceUtil voiceUtil;
/**
* 所有档位的描述表
@ -135,7 +131,6 @@ public class DeviceXinghanBizService {
public void upSOSGradeSettings(DeviceXinghanInstructDto dto) {
if(dto.getIsBluetooth()){
long deviceId = dto.getDeviceId();
// 1. 使用Optional简化空值检查使代码更简洁
Device device = Optional.ofNullable(deviceMapper.selectById(deviceId))
.orElseThrow(() -> new ServiceException("设备不存在"));
@ -147,6 +142,24 @@ public class DeviceXinghanBizService {
}
}
/**
* 触发异步报警
* Spring 会自动调用 AsyncConfig.getAsyncExecutor() 来执行此方法
*/
@Async
public void executeSosCall(String phone) {
log.info("[SOS业务] 准备发起语音拨号 -> 目标: {}", phone);
Map<String, String> params = Map.of("device", "670");
String callId = voiceUtil.sendTtsSync(phone, "TTS_328730104", params);
if (callId != null) {
log.info("[SOS业务] 拨号指令下发成功, callId: {}", callId);
// 这里可以记录拨打日志到数据库
} else {
log.error("[SOS业务] 拨号指令下发失败,请检查配置或余额");
}
}
/**
* 设置强制报警
*/
@ -701,20 +714,31 @@ public class DeviceXinghanBizService {
return deviceTypeMapper.findAll(criteria);
}
// @Log("新增设备")
public void addDevice(DeviceForm deviceForm) {
if (deviceForm.getDeviceMac() != null && deviceForm.getBluetoothName() == null) {
/**
* 校验唯一性约束
*/
private void validateDeviceUnique(DeviceForm form) {
if (form.getDeviceMac() != null && form.getBluetoothName() == null) {
throw new BadRequestException("请填写蓝牙名称!!!");
}
Device device1 = deviceMapper.selectOne(new QueryWrapper<Device>().eq("device_mac", deviceForm.getDeviceMac()));
if (device1 != null) {
// 使用 QueryWrapper 替代 lambdaQuery()
Long macCount = deviceMapper.selectCount(new LambdaQueryWrapper<Device>()
.eq(Device::getDeviceMac, form.getDeviceMac()));
if (macCount > 0) {
throw new BadRequestException("设备MAC已存在");
}
Device device2 = deviceMapper.selectOne(new QueryWrapper<Device>().eq("device_imei", deviceForm.getDeviceImei()));
if (device2 != null) {
Long imeiCount = deviceMapper.selectCount(new LambdaQueryWrapper<Device>()
.eq(Device::getDeviceImei, form.getDeviceImei()));
if (imeiCount > 0) {
throw new BadRequestException("设备IMEI已存在");
}
}
// @Log("新增设备")
public void addDevice(DeviceForm deviceForm) {
validateDeviceUnique(deviceForm);
DeviceTypeQueryCriteria queryCriteria = new DeviceTypeQueryCriteria();
queryCriteria.setDeviceTypeId(deviceForm.getDeviceType());
@ -739,6 +763,7 @@ public class DeviceXinghanBizService {
device.setCreateByName(loginUser.getNickname());
device.setTypeName(deviceTypes.getTypeName());
device.setDeviceType(deviceTypes.getId());
device.setDevicePic(deviceTypes.getDevicePic());
if (device.getDeviceImei() != null) {
device.setPubTopic("A/" + device.getDeviceImei());
device.setSubTopic("B/" + device.getDeviceImei());
@ -747,6 +772,14 @@ public class DeviceXinghanBizService {
device.setBindingStatus(0);
deviceMapper.insert(device);
Long deviceId = device.getDeviceId();
// 查询设备类型的文件列表
// 4. 核心优化:同步设备类型的文件列表 (一行代码)
appBusinessFileService.cloneFiles(deviceTypes.getId(), device.getId());
//同步设备类型的视频列表
appOperationVideoService.cloneFiles(deviceTypes.getId(), device.getId());
// 新增设备类型记录
DeviceAssignments assignments = new DeviceAssignments();
assignments.setDeviceId(device.getId());
@ -767,4 +800,23 @@ public class DeviceXinghanBizService {
return uuidStr.replaceAll("-", "");
}
public Map<String, Object> GetDeviceByName(DeviceForm deviceForm){
List<Map<String, Object>> list= deviceMapper.GetDeviceByName(deviceForm);
Map<String, Object> device=null;
if(list!=null && list.size()>0){
device=list.get(0);
}
return device;
}
public int getEquipCountByType(DeviceForm form){
var res=deviceMapper.getEquipCountByType(form);
return res;
}
public List<Map<String,Object>> getEquipAllByType(DeviceForm deviceForm){
List<Map<String, Object>> list= deviceMapper.getEquipAllByType(deviceForm);
return list;
}
}

View File

@ -0,0 +1,87 @@
package com.fuyuanshen.web.util;
import com.aliyun.dyvmsapi20170525.Client;
import com.aliyun.dyvmsapi20170525.models.SingleCallByTtsRequest;
import com.aliyun.dyvmsapi20170525.models.SingleCallByTtsResponse;
import com.aliyun.teaopenapi.models.Config;
import com.aliyun.teautil.models.RuntimeOptions;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.util.Map;
@Slf4j
@Component
public class AliyunVoiceUtil {
private static final ObjectMapper objectMapper = new ObjectMapper();
@Value("${alibaba.tts.akId}")
private String akId;
@Value("${alibaba.tts.akSecret}")
private String akSecret;
// @Value("${alibaba.tts.calledShowNumber:}")
private String calledShowNumber;
// ========== 核心:单例客户端(类似 OkHttpClient ==========
private volatile Client client;
/**
* 获取客户端(双重检查锁实现单例)
* 只有在第一次调用时才会根据配置实例化,后续直接返回复用
*/
private Client getClient() throws Exception {
if (client == null) {
synchronized (this) {
if (client == null) {
log.info("[AliyunVoice] 正在初始化阿里云语音客户端...");
Config config = new Config()
.setAccessKeyId(akId)
.setAccessKeySecret(akSecret)
.setEndpoint("dyvmsapi.aliyuncs.com");
this.client = new Client(config);
}
}
}
return client;
}
/**
* 同步发送方法:由异步架构调用
*/
public String sendTtsSync(String phone, String templateCode, Map<String, String> params) {
try {
// 1. 获取(或初始化)单例客户端
Client voiceClient = getClient();
SingleCallByTtsRequest request = new SingleCallByTtsRequest()
.setCalledNumber(phone)
.setTtsCode(templateCode)
.setTtsParam(objectMapper.writeValueAsString(params));
if (StringUtils.hasText(calledShowNumber)) {
request.setCalledShowNumber(calledShowNumber);
}
// 生产级超时配置
RuntimeOptions runtime = new RuntimeOptions();
runtime.setConnectTimeout(5000);
runtime.setReadTimeout(10000);
SingleCallByTtsResponse response = voiceClient.singleCallByTtsWithOptions(request, runtime);
if ("OK".equalsIgnoreCase(response.getBody().getCode())) {
return response.getBody().getCallId();
} else {
log.error("[AliyunVoice] 拨号失败: {}", response.getBody().getMessage());
}
} catch (Exception e) {
log.error("[AliyunVoice] 接口异常", e);
}
return null;
}
}

View File

@ -77,4 +77,9 @@ public interface SystemConstants {
*/
String ROOT_DEPT_ANCESTORS = "0";
/**
* 菜单ID
*/
public static final Long RESTRICTED_MENU_ID = 102L;
}

View File

@ -39,8 +39,8 @@ public class EncryptUtilsTest {
loginBody.setClientId("e5cd7e4891bf95d1d19206ce24a7b32e");
loginBody.setGrantType("password");
loginBody.setTenantId("894078");
loginBody.setCode("0");
loginBody.setUuid("1d6615668c7f410da77c4e002c601073");
loginBody.setCode("15");
loginBody.setUuid("28ecf3d396ce4e6db8eb414992235fad");
// loginBody.setUsername("admin");
// loginBody.setPassword("admin123");
loginBody.setUsername("dyf");

View File

@ -74,6 +74,7 @@ public class ExcelUtil {
return listener.getExcelResult();
}
/**
* 导出excel
*
@ -92,6 +93,7 @@ public class ExcelUtil {
}
}
/**
* 导出excel
*
@ -174,6 +176,7 @@ public class ExcelUtil {
public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, OutputStream os, List<DropDownOptions> options) {
exportExcel(list, sheetName, clazz, false, os, options);
}
/**
* 导出excel
@ -187,13 +190,13 @@ public class ExcelUtil {
public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, boolean merge,
OutputStream os, List<DropDownOptions> options) {
ExcelWriterSheetBuilder builder = FastExcel.write(os, clazz)
.autoCloseStream(false)
// 自动适配
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
// 大数值自动转换 防止失真
.registerConverter(new ExcelBigNumberConvert())
.registerWriteHandler(new DataWriteHandler(clazz))
.sheet(sheetName);
.autoCloseStream(false)
// 自动适配
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
// 大数值自动转换 防止失真
.registerConverter(new ExcelBigNumberConvert())
.registerWriteHandler(new DataWriteHandler(clazz))
.sheet(sheetName);
if (merge) {
// 合并处理器
builder.registerWriteHandler(new CellMergeStrategy(list, true));
@ -203,6 +206,7 @@ public class ExcelUtil {
builder.doWrite(list);
}
/**
* 单表多数据模板导出 模板格式为 {.属性}
*
@ -238,12 +242,12 @@ public class ExcelUtil {
public static <T> void exportTemplate(List<T> data, String templatePath, OutputStream os) {
ClassPathResource templateResource = new ClassPathResource(templatePath);
ExcelWriter excelWriter = FastExcel.write(os)
.withTemplate(templateResource.getStream())
.autoCloseStream(false)
// 大数值自动转换 防止失真
.registerConverter(new ExcelBigNumberConvert())
.registerWriteHandler(new DataWriteHandler(data.get(0).getClass()))
.build();
.withTemplate(templateResource.getStream())
.autoCloseStream(false)
// 大数值自动转换 防止失真
.registerConverter(new ExcelBigNumberConvert())
.registerWriteHandler(new DataWriteHandler(data.get(0).getClass()))
.build();
WriteSheet writeSheet = FastExcel.writerSheet().build();
FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
// 单表多数据导出 模板格式为 {.属性}
@ -311,11 +315,11 @@ public class ExcelUtil {
public static void exportTemplateMultiList(Map<String, Object> data, String templatePath, OutputStream os) {
ClassPathResource templateResource = new ClassPathResource(templatePath);
ExcelWriter excelWriter = FastExcel.write(os)
.withTemplate(templateResource.getStream())
.autoCloseStream(false)
// 大数值自动转换 防止失真
.registerConverter(new ExcelBigNumberConvert())
.build();
.withTemplate(templateResource.getStream())
.autoCloseStream(false)
// 大数值自动转换 防止失真
.registerConverter(new ExcelBigNumberConvert())
.build();
WriteSheet writeSheet = FastExcel.writerSheet().build();
for (Map.Entry<String, Object> map : data.entrySet()) {
// 设置列表后续还有数据
@ -342,11 +346,11 @@ public class ExcelUtil {
public static void exportTemplateMultiSheet(List<Map<String, Object>> data, String templatePath, OutputStream os) {
ClassPathResource templateResource = new ClassPathResource(templatePath);
ExcelWriter excelWriter = FastExcel.write(os)
.withTemplate(templateResource.getStream())
.autoCloseStream(false)
// 大数值自动转换 防止失真
.registerConverter(new ExcelBigNumberConvert())
.build();
.withTemplate(templateResource.getStream())
.autoCloseStream(false)
// 大数值自动转换 防止失真
.registerConverter(new ExcelBigNumberConvert())
.build();
for (int i = 0; i < data.size(); i++) {
WriteSheet writeSheet = FastExcel.writerSheet(i).build();
for (Map.Entry<String, Object> map : data.get(i).entrySet()) {

View File

@ -17,9 +17,9 @@ 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.AppBusinessFileVo;
import com.fuyuanshen.app.domain.bo.AppBusinessFileBo;
import com.fuyuanshen.app.service.IAppBusinessFileService;
import com.fuyuanshen.equipment.domain.vo.AppBusinessFileVo;
import com.fuyuanshen.equipment.domain.bo.AppBusinessFileBo;
import com.fuyuanshen.equipment.service.IAppBusinessFileService;
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
/**

View File

@ -17,9 +17,9 @@ 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.AppOperationVideoVo;
import com.fuyuanshen.app.domain.bo.AppOperationVideoBo;
import com.fuyuanshen.app.service.IAppOperationVideoService;
import com.fuyuanshen.equipment.domain.vo.AppOperationVideoVo;
import com.fuyuanshen.equipment.domain.bo.AppOperationVideoBo;
import com.fuyuanshen.equipment.service.IAppOperationVideoService;
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
/**

View File

@ -140,6 +140,18 @@
<version>3.3.1</version>
</dependency>
<!-- 电话语音通知 -->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>dyvmsapi20170525</artifactId>
<version>4.2.0</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>tea-openapi</artifactId>
<version>0.3.2</version>
</dependency>
<!-- fastjson2 -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>

View File

@ -6,7 +6,6 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fuyuanshen.common.core.domain.R;
import com.fuyuanshen.common.core.domain.model.LoginUser;
import com.fuyuanshen.common.core.utils.file.FileUtil;
import com.fuyuanshen.common.mybatis.core.page.PageQuery;
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
import com.fuyuanshen.common.satoken.utils.LoginHelper;
import com.fuyuanshen.common.web.core.BaseController;
@ -318,8 +317,7 @@ public class DeviceController extends BaseController {
// 定义必需的表头
Set<String> requiredHeaders = new HashSet<>(Arrays.asList(
"设备名称", "设备类型名称", "设备图片", "设备MAC", "蓝牙名称", "设备IMEI",
"备注", "是否支持蓝牙", "定位方式", "通讯方式",
"型号字典用于APP页面跳转", "型号字典用于PC页面跳转"
"备注"
));
// 检查必需的表头是否都存在

View File

@ -41,6 +41,7 @@ public class DeviceRepairRecordsController extends BaseController {
private final IDeviceRepairRecordsService deviceRepairRecordsService;
/**
* 查询设备维修记录列表
*/
@ -52,6 +53,7 @@ public class DeviceRepairRecordsController extends BaseController {
return deviceRepairRecordsService.queryPageList(criteria, page);
}
/**
* 导出设备维修记录列表
*/
@ -63,6 +65,7 @@ public class DeviceRepairRecordsController extends BaseController {
ExcelUtil.exportExcel(list, "设备维修记录", DeviceRepairRecordsVo.class, response);
}
/**
* 获取设备维修记录详细信息
*
@ -75,6 +78,7 @@ public class DeviceRepairRecordsController extends BaseController {
return R.ok(deviceRepairRecordsService.queryById(recordId));
}
/**
* 新增设备维修记录
*/
@ -86,6 +90,7 @@ public class DeviceRepairRecordsController extends BaseController {
return toAjax(deviceRepairRecordsService.insertByBo(bo));
}
/**
* 修改设备维修记录
*/
@ -109,4 +114,5 @@ public class DeviceRepairRecordsController extends BaseController {
@PathVariable Long[] recordIds) {
return toAjax(deviceRepairRecordsService.deleteWithValidByIds(List.of(recordIds), true));
}
}

View File

@ -15,6 +15,7 @@ import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.List;
/**
@ -50,7 +51,7 @@ public class DeviceTypeController {
// @Log("新增设备类型")
@Operation(summary = "新增设备类型")
@PostMapping(value = "/add")
public R<Void> createDeviceType(@Validated @RequestBody DeviceType resources) {
public R<Void> createDeviceType(@Validated @ModelAttribute DeviceTypeForm resources) throws IOException {
deviceTypeService.create(resources);
return R.ok();
}
@ -59,7 +60,7 @@ public class DeviceTypeController {
// @Log("修改设备类型")
@Operation(summary = "修改设备类型")
@PutMapping(value = "/update")
public R<Void> updateDeviceType(@Validated @RequestBody DeviceTypeForm resources) {
public R<Void> updateDeviceType(@Validated @ModelAttribute DeviceTypeForm resources) throws IOException {
deviceTypeService.update(resources);
return R.ok();
}
@ -79,7 +80,6 @@ public class DeviceTypeController {
public R<DeviceType> getCommunicationMode(@Parameter(name = "设备类型ID", required = true) Long id) {
DeviceType communicationMode = deviceTypeService.getCommunicationMode(id);
return R.ok(communicationMode);
}
}

View File

@ -44,7 +44,7 @@ public class TrackServiceController extends BaseController {
/**
* 查询轨迹服务列表
*/
@SaCheckPermission("equipment:trackService:list")
// @SaCheckPermission("equipment:trackService:list")
@GetMapping("/list")
public TableDataInfo<TrackServiceVo> list(TrackServiceBo bo, PageQuery pageQuery) {
return trackServiceService.queryPageList(bo, pageQuery);
@ -53,7 +53,7 @@ public class TrackServiceController extends BaseController {
/**
* 轨迹服务列表
*/
@SaCheckPermission("equipment:trackService:export")
// @SaCheckPermission("equipment:trackService:export")
@Log(title = "轨迹服务", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(TrackServiceBo bo, HttpServletResponse response) {
@ -75,7 +75,7 @@ public class TrackServiceController extends BaseController {
/**
* 新增轨迹服务
*/
@SaCheckPermission("equipment:trackService:add")
// @SaCheckPermission("equipment:trackService:add")
@Log(title = "轨迹服务", businessType = BusinessType.INSERT)
@PostMapping(value = "/add")
public R<Void> add(@Validated(AddGroup.class) @RequestBody TrackServiceBo bo) {
@ -85,7 +85,7 @@ public class TrackServiceController extends BaseController {
/**
* 修改轨迹服务
*/
@SaCheckPermission("equipment:trackService:edit")
// @SaCheckPermission("equipment:trackService:edit")
@Log(title = "轨迹服务", businessType = BusinessType.UPDATE)
@PostMapping(value = "/update")
public R<Void> edit(@Validated(EditGroup.class) @RequestBody TrackServiceBo bo) {
@ -97,7 +97,7 @@ public class TrackServiceController extends BaseController {
*
* @param ids 主键串
*/
@SaCheckPermission("equipment:trackService:remove")
// @SaCheckPermission("equipment:trackService:remove")
@Log(title = "轨迹服务", businessType = BusinessType.DELETE)
@DeleteMapping(value = "/delete")
public R<Void> remove(@NotEmpty(message = "主键不能为空")

View File

@ -1,4 +1,4 @@
package com.fuyuanshen.app.domain;
package com.fuyuanshen.equipment.domain;
import com.fuyuanshen.common.tenant.core.TenantEntity;
import com.baomidou.mybatisplus.annotation.*;

View File

@ -1,4 +1,4 @@
package com.fuyuanshen.app.domain;
package com.fuyuanshen.equipment.domain;
import com.fuyuanshen.common.tenant.core.TenantEntity;
import com.baomidou.mybatisplus.annotation.*;

View File

@ -85,5 +85,8 @@ public class DeviceType extends TenantEntity {
@Schema(title = "型号字典用于PC页面跳转")
private String pcModelDictionary;
@Schema(title = "设备图片")
private String devicePic;
}

View File

@ -1,6 +1,6 @@
package com.fuyuanshen.app.domain.bo;
package com.fuyuanshen.equipment.domain.bo;
import com.fuyuanshen.app.domain.AppBusinessFile;
import com.fuyuanshen.equipment.domain.AppBusinessFile;
import com.fuyuanshen.common.core.validate.EditGroup;
import com.fuyuanshen.common.mybatis.core.domain.BaseEntity;
import io.github.linpeilie.annotations.AutoMapper;

View File

@ -1,6 +1,6 @@
package com.fuyuanshen.app.domain.bo;
package com.fuyuanshen.equipment.domain.bo;
import com.fuyuanshen.app.domain.AppOperationVideo;
import com.fuyuanshen.equipment.domain.AppOperationVideo;
import com.fuyuanshen.common.core.validate.EditGroup;
import com.fuyuanshen.common.mybatis.core.domain.BaseEntity;
import io.github.linpeilie.annotations.AutoMapper;

View File

@ -54,6 +54,9 @@ public class DeviceForm {
@Schema(title = "备注")
private String remark;
@Schema(title = "商户号")
private Long tenant_id;
// 设备类型相关字段
@Schema(title = "设备类型名称")

View File

@ -2,6 +2,7 @@ package com.fuyuanshen.equipment.domain.form;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.web.multipart.MultipartFile;
/**
* @Description: 设备类型
@ -48,5 +49,10 @@ public class DeviceTypeForm {
*/
@Schema(title = "型号字典用于PC页面跳转")
private String pcModelDictionary;
@Schema(title = "设备图片")
private String devicePic;
@Schema(title = "设备图片")
private MultipartFile file;
}

View File

@ -1,17 +1,13 @@
package com.fuyuanshen.app.domain.vo;
package com.fuyuanshen.equipment.domain.vo;
import com.fuyuanshen.app.domain.AppBusinessFile;
import com.fuyuanshen.equipment.domain.AppBusinessFile;
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;
/**

View File

@ -1,4 +1,4 @@
package com.fuyuanshen.app.domain.vo;
package com.fuyuanshen.equipment.domain.vo;
import lombok.Data;

View File

@ -1,17 +1,13 @@
package com.fuyuanshen.app.domain.vo;
package com.fuyuanshen.equipment.domain.vo;
import com.fuyuanshen.app.domain.AppOperationVideo;
import com.fuyuanshen.equipment.domain.AppOperationVideo;
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;
/**

View File

@ -2,6 +2,7 @@ package com.fuyuanshen.equipment.domain.vo;
import java.util.Date;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.fasterxml.jackson.annotation.JsonFormat;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
@ -60,6 +61,13 @@ public class DeviceFenceAccessRecordVo implements Serializable {
@ExcelProperty(value = "设备名称")
private String deviceName;
/**
* 事件时间
*/
@ExcelProperty(value = "事件时间")
@ColumnWidth(120)
private String eventTime;
/**
* 用户ID
*/
@ -76,27 +84,21 @@ public class DeviceFenceAccessRecordVo implements Serializable {
/**
* 纬度
*/
@ExcelProperty(value = "纬度")
// @ExcelProperty(value = "纬度")
private Double latitude;
/**
* 经度
*/
@ExcelProperty(value = "经度")
// @ExcelProperty(value = "经度")
private Double longitude;
/**
* 定位精度
*/
@ExcelProperty(value = "定位精度")
// @ExcelProperty(value = "定位精度")
private Long accuracy;
/**
* 事件时间
*/
@ExcelProperty(value = "事件时间")
private Date eventTime;
/**
* 事件地址
*/
@ -106,8 +108,7 @@ public class DeviceFenceAccessRecordVo implements Serializable {
/**
* 记录创建时间
*/
@ExcelProperty(value = "记录创建时间")
// @ExcelProperty(value = "记录创建时间")
private Date createTime;
}

View File

@ -1,13 +1,11 @@
package com.fuyuanshen.equipment.domain.vo;
import java.util.Date;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import cn.idev.excel.annotation.write.style.ColumnWidth;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fuyuanshen.common.tenant.core.TenantEntity;
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;
@ -34,31 +32,39 @@ public class DeviceRepairRecordsVo extends TenantEntity implements Serializable
/**
* 维修记录ID
*/
@ExcelProperty(value = "维修记录ID")
// @ExcelProperty(value = "维修记录ID")
private Long recordId;
/**
* 设备ID
*/
@ExcelProperty(value = "设备ID")
// @ExcelProperty(value = "设备ID")
private String deviceId;
/**
* 设备名称
*/
@ExcelProperty(value = "设备名称")
private String deviceName;
/**
* 维修时间
*/
@ExcelProperty(value = "维修时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ColumnWidth(value = 20)
private Date repairTime;
/**
* 维修部位
* 损坏部位
*/
@ExcelProperty(value = "维修部位")
@ExcelProperty(value = "损坏部位")
private String repairPart;
/**
* 维修原因
* 损坏原因
*/
@ExcelProperty(value = "维修原因")
@ExcelProperty(value = "损坏原因")
private String repairReason;
/**
@ -66,12 +72,8 @@ public class DeviceRepairRecordsVo extends TenantEntity implements Serializable
*/
@ExcelProperty(value = "维修人员")
private String repairPerson;
/**
* 维修人员
*/
@ExcelProperty(value = "设备名称")
private String deviceName;
private List<DeviceRepairImagesVo> images;
}
}

View File

@ -12,6 +12,7 @@ import com.fuyuanshen.equipment.domain.DeviceType;
import com.fuyuanshen.equipment.domain.dto.DeviceExcelImportDTO;
import com.fuyuanshen.equipment.domain.dto.ImportResult;
import com.fuyuanshen.equipment.domain.form.DeviceForm;
import com.fuyuanshen.equipment.domain.form.DeviceTypeForm;
import com.fuyuanshen.equipment.handler.ImageWriteHandler;
import com.fuyuanshen.system.domain.vo.SysOssVo;
import lombok.extern.slf4j.Slf4j;
@ -437,8 +438,10 @@ public class UploadDeviceDataListener implements ReadListener<DeviceExcelImportD
newDeviceType.setAppModelDictionary(originalDto.getAppModelDictionary());
newDeviceType.setPcModelDictionary(originalDto.getPcModelDictionary());
DeviceTypeForm deviceTypeForm = new DeviceTypeForm();
BeanUtil.copyProperties(newDeviceType, deviceTypeForm, true);
// 创建新的设备类型
params.getDeviceTypeService().create(newDeviceType);
params.getDeviceTypeService().create(deviceTypeForm);
// 重新查询确保获取到正确的ID
deviceType = params.getDeviceTypeService().queryByName(device.getTypeName());

View File

@ -1,10 +1,10 @@
package com.fuyuanshen.app.mapper;
package com.fuyuanshen.equipment.mapper;
import com.fuyuanshen.app.domain.AppBusinessFile;
import com.fuyuanshen.app.domain.bo.AppBusinessFileBo;
import com.fuyuanshen.app.domain.vo.AppBusinessFileVo;
import com.fuyuanshen.app.domain.vo.AppFileVo;
import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus;
import com.fuyuanshen.equipment.domain.AppBusinessFile;
import com.fuyuanshen.equipment.domain.bo.AppBusinessFileBo;
import com.fuyuanshen.equipment.domain.vo.AppBusinessFileVo;
import com.fuyuanshen.equipment.domain.vo.AppFileVo;
import java.util.List;

View File

@ -1,8 +1,8 @@
package com.fuyuanshen.app.mapper;
package com.fuyuanshen.equipment.mapper;
import com.fuyuanshen.app.domain.AppOperationVideo;
import com.fuyuanshen.app.domain.vo.AppOperationVideoVo;
import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus;
import com.fuyuanshen.equipment.domain.AppOperationVideo;
import com.fuyuanshen.equipment.domain.vo.AppOperationVideoVo;
import org.apache.ibatis.annotations.Mapper;
/**

View File

@ -28,17 +28,19 @@ public interface DeviceFenceAccessRecordMapper extends BaseMapperPlus<DeviceFenc
*/
Page<DeviceFenceAccessRecordVo> selectVoPageWithFenceAndDeviceName(Page<DeviceFenceAccessRecord> page, @Param(Constants.WRAPPER) Wrapper<DeviceFenceAccessRecord> wrapper);
List<DeviceFenceAccessRecordVo> selectVoPageWithFenceAndDeviceName(@Param(Constants.WRAPPER) Wrapper<DeviceFenceAccessRecord> wrapper,@Param("fenceName") String fenceName);
List<DeviceFenceAccessRecordVo> selectVoPageWithFenceAndDeviceName(@Param(Constants.WRAPPER) Wrapper<DeviceFenceAccessRecord> wrapper, @Param("fenceName") String fenceName);
/**
* 分页查询围栏进出记录列表纯XML形式
*
* @param page 分页参数
* @param bo 查询条件
* @param bo 查询条件
* @return 围栏进出记录分页列表
*/
Page<DeviceFenceAccessRecordVo> selectVoPageByXml(Page<DeviceFenceAccessRecord> page, @Param("bo") DeviceFenceAccessRecordBo bo);
List<DeviceFenceAccessRecordVo> selectVoPageByXml(@Param("bo") DeviceFenceAccessRecordBo bo);
/**
* 查询设备最新的围栏记录

View File

@ -5,6 +5,7 @@ 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.dto.InstructionRecordDto;
import com.fuyuanshen.equipment.domain.form.DeviceForm;
import com.fuyuanshen.equipment.domain.query.DeviceQueryCriteria;
import com.fuyuanshen.equipment.domain.vo.*;
import org.apache.ibatis.annotations.Mapper;
@ -148,4 +149,10 @@ public interface DeviceMapper extends BaseMapper<Device> {
*/
int countByDeviceTypeId(@Param("deviceTypeId") Long deviceTypeId);
List<Map<String, Object>> GetDeviceByName(DeviceForm deviceForm);
int getEquipCountByType(DeviceForm deviceForm);
List<Map<String,Object>> getEquipAllByType(DeviceForm deviceForm);
}

View File

@ -8,6 +8,7 @@ import com.fuyuanshen.equipment.domain.DeviceType;
import com.fuyuanshen.equipment.domain.form.DeviceTypeForm;
import com.fuyuanshen.equipment.domain.query.DeviceTypeQueryCriteria;
import java.io.IOException;
import java.util.List;
/**
@ -63,14 +64,14 @@ public interface DeviceTypeService extends IService<DeviceType> {
*
* @param resources /
*/
void create(DeviceType resources);
void create(DeviceTypeForm resources) throws IOException;
/**
* 修改设备类型
*
* @param resources /
*/
void update(DeviceTypeForm resources);
void update(DeviceTypeForm resources) throws IOException;
/**
* 多选删除

View File

@ -1,11 +1,11 @@
package com.fuyuanshen.app.service;
package com.fuyuanshen.equipment.service;
import com.fuyuanshen.app.domain.AppBusinessFile;
import com.fuyuanshen.app.domain.vo.AppBusinessFileVo;
import com.fuyuanshen.app.domain.bo.AppBusinessFileBo;
import com.fuyuanshen.app.domain.vo.AppFileVo;
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
import com.fuyuanshen.common.mybatis.core.page.PageQuery;
import com.fuyuanshen.equipment.domain.AppBusinessFile;
import com.fuyuanshen.equipment.domain.bo.AppBusinessFileBo;
import com.fuyuanshen.equipment.domain.vo.AppBusinessFileVo;
import com.fuyuanshen.equipment.domain.vo.AppFileVo;
import java.util.Collection;
import java.util.List;
@ -57,7 +57,9 @@ public interface IAppBusinessFileService {
* @param bo 批量新增app业务文件
* @return 是否新增成功
*/
Boolean insertBatch(Collection<AppBusinessFile> bo,Boolean isBatch);
Boolean insertBatch(Collection<AppBusinessFile> bo, Boolean isBatch);
void cloneFiles(Long sourceId, Long targetId);
/**
* 修改app业务文件

View File

@ -1,11 +1,10 @@
package com.fuyuanshen.app.service;
package com.fuyuanshen.equipment.service;
import com.fuyuanshen.app.domain.AppBusinessFile;
import com.fuyuanshen.app.domain.AppOperationVideo;
import com.fuyuanshen.app.domain.vo.AppOperationVideoVo;
import com.fuyuanshen.app.domain.bo.AppOperationVideoBo;
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
import com.fuyuanshen.common.mybatis.core.page.PageQuery;
import com.fuyuanshen.equipment.domain.AppOperationVideo;
import com.fuyuanshen.equipment.domain.bo.AppOperationVideoBo;
import com.fuyuanshen.equipment.domain.vo.AppOperationVideoVo;
import java.util.Collection;
import java.util.List;
@ -59,6 +58,8 @@ public interface IAppOperationVideoService {
*/
Boolean insertBatch(Collection<AppOperationVideo> bo);
void cloneFiles(Long sourceId, Long targetId);
/**
* 修改操作视频
*

View File

@ -32,8 +32,8 @@ public interface IDeviceRepairRecordsService extends IService<DeviceRepairRecord
/**
* 分页查询设备维修记录列表
*
* @param bo 查询条件
* @param pageQuery 分页参数
* @param criteria 查询条件
* @param page 分页参数
* @return 设备维修记录分页列表
*/
TableDataInfo<DeviceRepairRecordsVo> queryPageList(DeviceRepairRecordsQueryCriteria criteria, Page<DeviceRepairRecords> page);
@ -41,7 +41,7 @@ public interface IDeviceRepairRecordsService extends IService<DeviceRepairRecord
/**
* 查询符合条件的设备维修记录列表
*
* @param bo 查询条件
* @param criteria 查询条件
* @return 设备维修记录列表
*/
List<DeviceRepairRecordsVo> queryList(DeviceRepairRecordsQueryCriteria criteria);
@ -70,4 +70,5 @@ public interface IDeviceRepairRecordsService extends IService<DeviceRepairRecord
* @return 是否删除成功
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
}

View File

@ -1,27 +1,29 @@
package com.fuyuanshen.app.service.impl;
package com.fuyuanshen.equipment.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fuyuanshen.app.domain.vo.AppFileVo;
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 com.fuyuanshen.equipment.domain.AppBusinessFile;
import com.fuyuanshen.equipment.domain.bo.AppBusinessFileBo;
import com.fuyuanshen.equipment.domain.vo.AppBusinessFileVo;
import com.fuyuanshen.equipment.domain.vo.AppFileVo;
import com.fuyuanshen.equipment.mapper.AppBusinessFileMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import com.fuyuanshen.app.domain.bo.AppBusinessFileBo;
import com.fuyuanshen.app.domain.vo.AppBusinessFileVo;
import com.fuyuanshen.app.domain.AppBusinessFile;
import com.fuyuanshen.app.mapper.AppBusinessFileMapper;
import com.fuyuanshen.app.service.IAppBusinessFileService;
import com.fuyuanshen.equipment.service.IAppBusinessFileService;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Collection;
import java.util.stream.Collectors;
/**
* app业务文件Service业务层处理
@ -117,6 +119,38 @@ public class AppBusinessFileServiceImpl implements IAppBusinessFileService {
return baseMapper.insertBatch(bo);
}
/**
* 克隆业务文件列表到新业务ID
* @param sourceId 源业务ID如设备类型ID
* @param targetId 目标业务ID如新设备ID
*/
@Transactional(rollbackFor = Exception.class)
@Override
public void cloneFiles(Long sourceId, Long targetId) {
// 1. 使用 Wrappers 替代 this.lambdaQuery()
List<AppBusinessFile> sourceFiles = baseMapper.selectList(
Wrappers.<AppBusinessFile>lambdaQuery().eq(AppBusinessFile::getBusinessId, sourceId)
);
if (CollUtil.isEmpty(sourceFiles)) {
return;
}
// 2. 批量转换并重置ID
List<AppBusinessFile> newFiles = sourceFiles.stream().map(file -> {
AppBusinessFile entity = new AppBusinessFile();
// 建议使用你代码中已有的 MapstructUtils BeanUtil
BeanUtil.copyProperties(file, entity);
entity.setId(null); // 确保主键自增
entity.setBusinessId(targetId); // 绑定到新设备ID
return entity;
}).collect(Collectors.toList());
// 3. 使用你已有的 insertBatch 替代 saveBatch
// 注意这里第二个参数传 false因为是新设备不需要执行你 insertBatch 里的删除逻辑
this.insertBatch(newFiles, false);
}
/**
* 修改app业务文件
*

View File

@ -1,7 +1,8 @@
package com.fuyuanshen.app.service.impl;
package com.fuyuanshen.equipment.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fuyuanshen.app.domain.AppBusinessFile;
import com.fuyuanshen.common.core.utils.MapstructUtils;
import com.fuyuanshen.common.core.utils.StringUtils;
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
@ -9,18 +10,20 @@ 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 com.fuyuanshen.equipment.domain.AppOperationVideo;
import com.fuyuanshen.equipment.domain.bo.AppOperationVideoBo;
import com.fuyuanshen.equipment.domain.vo.AppOperationVideoVo;
import com.fuyuanshen.equipment.mapper.AppOperationVideoMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import com.fuyuanshen.app.domain.bo.AppOperationVideoBo;
import com.fuyuanshen.app.domain.vo.AppOperationVideoVo;
import com.fuyuanshen.app.domain.AppOperationVideo;
import com.fuyuanshen.app.mapper.AppOperationVideoMapper;
import com.fuyuanshen.app.service.IAppOperationVideoService;
import com.fuyuanshen.equipment.service.IAppOperationVideoService;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Map;
import java.util.Collection;
import java.util.stream.Collectors;
/**
* 操作视频Service业务层处理
@ -125,6 +128,37 @@ public class AppOperationVideoServiceImpl implements IAppOperationVideoService {
return baseMapper.updateById(update) > 0;
}
/**
* 克隆业务文件列表到新业务ID
* @param sourceId 源业务ID如设备类型ID
* @param targetId 目标业务ID如新设备ID
*/
@Transactional(rollbackFor = Exception.class)
@Override
public void cloneFiles(Long sourceId, Long targetId) {
// 1. 使用 Wrappers 替代 this.lambdaQuery()
List<AppOperationVideo> sourceFiles = baseMapper.selectList(
Wrappers.<AppOperationVideo>lambdaQuery().eq(AppOperationVideo::getDeviceId, sourceId)
);
if (CollUtil.isEmpty(sourceFiles)) {
return;
}
// 2. 批量转换并重置ID
List<AppOperationVideo> newFiles = sourceFiles.stream().map(file -> {
AppOperationVideo entity = new AppOperationVideo();
// 建议使用你代码中已有的 MapstructUtils BeanUtil
BeanUtil.copyProperties(file, entity);
entity.setId(null); // 确保主键自增
entity.setDeviceId(targetId); // 绑定到新设备ID
return entity;
}).collect(Collectors.toList());
// 3. 使用你已有的 insertBatch 替代 saveBatch
this.insertBatch(newFiles);
}
/**
* 保存前的数据校验
*/

View File

@ -33,6 +33,7 @@ public class DeviceFenceAccessRecordServiceImpl implements IDeviceFenceAccessRec
private final DeviceFenceAccessRecordMapper baseMapper;
/**
* 查询围栏进出记录
*
@ -68,8 +69,8 @@ public class DeviceFenceAccessRecordServiceImpl implements IDeviceFenceAccessRec
*/
@Override
public List<DeviceFenceAccessRecordVo> queryList(DeviceFenceAccessRecordBo bo) {
LambdaQueryWrapper<DeviceFenceAccessRecord> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoPageWithFenceAndDeviceName(lqw, bo.getFenceName());
// LambdaQueryWrapper<DeviceFenceAccessRecord> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoPageByXml(bo);
}

View File

@ -275,6 +275,7 @@ public class DeviceGeoFenceServiceImpl extends ServiceImpl<DeviceGeoFenceMapper
return response;
}
@Override
public Boolean addFenceTerminal(FenceTerminalBo bo) {
// 新增围栏与终端关联信息

View File

@ -52,6 +52,7 @@ public class DeviceRepairRecordsServiceImpl extends ServiceImpl<DeviceRepairReco
private final ISysOssService ossService;
private final FileHashUtil fileHashUtil;
/**
* 查询设备维修记录
*
@ -76,6 +77,7 @@ public class DeviceRepairRecordsServiceImpl extends ServiceImpl<DeviceRepairReco
return vo;
}
/**
* 分页查询设备维修记录列表
*
@ -98,6 +100,7 @@ public class DeviceRepairRecordsServiceImpl extends ServiceImpl<DeviceRepairReco
return new TableDataInfo<DeviceRepairRecordsVo>(deviceRepairRecordsIPage.getRecords(), deviceRepairRecordsIPage.getTotal());
}
/**
* 查询符合条件的设备维修记录列表
*

View File

@ -30,10 +30,7 @@ import com.fuyuanshen.equipment.enums.BindingStatusEnum;
import com.fuyuanshen.equipment.enums.CommunicationModeEnum;
import com.fuyuanshen.equipment.enums.DeviceActiveStatusEnum;
import com.fuyuanshen.equipment.mapper.*;
import com.fuyuanshen.equipment.service.DeviceAssignmentsService;
import com.fuyuanshen.equipment.service.DeviceService;
import com.fuyuanshen.equipment.service.DeviceTypeGrantsService;
import com.fuyuanshen.equipment.service.IDeviceGeoFenceService;
import com.fuyuanshen.equipment.service.*;
import com.fuyuanshen.equipment.utils.FileHashUtil;
import com.fuyuanshen.system.domain.vo.SysOssVo;
import com.fuyuanshen.system.domain.vo.SysRoleVo;
@ -77,6 +74,8 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
private final DeviceFenceAccessRecordMapper deviceFenceAccessRecordMapper;
private final FileHashUtil fileHashUtil;
private final IAppBusinessFileService appBusinessFileService;
private final IAppOperationVideoService appOperationVideoService;
/**
@ -325,6 +324,7 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
device.setCreateByName(loginUser.getNickname());
device.setTypeName(deviceType.getTypeName());
device.setDeviceType(deviceType.getId());
device.setDevicePic(deviceType.getDevicePic());
if (device.getDeviceImei() != null) {
device.setPubTopic("A/" + device.getDeviceImei());
device.setSubTopic("B/" + device.getDeviceImei());
@ -333,6 +333,12 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
device.setBindingStatus(0);
deviceMapper.insert(device);
// 查询设备类型的文件列表
// 4. 核心优化:同步设备类型的文件列表 (一行代码)
appBusinessFileService.cloneFiles(deviceType.getId(), device.getId());
//同步设备类型的视频列表
appOperationVideoService.cloneFiles(deviceType.getId(), device.getId());
// 新增设备类型记录
DeviceAssignments assignments = new DeviceAssignments();
assignments.setDeviceId(device.getId());

View File

@ -19,12 +19,16 @@ import com.fuyuanshen.equipment.mapper.DeviceMapper;
import com.fuyuanshen.equipment.mapper.DeviceTypeGrantsMapper;
import com.fuyuanshen.equipment.mapper.DeviceTypeMapper;
import com.fuyuanshen.equipment.service.DeviceTypeService;
import com.fuyuanshen.equipment.utils.FileHashUtil;
import com.fuyuanshen.system.domain.vo.SysOssVo;
import com.fuyuanshen.system.domain.vo.SysRoleVo;
import com.fuyuanshen.system.service.ISysOssService;
import com.fuyuanshen.system.service.ISysRoleService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@ -46,6 +50,8 @@ public class DeviceTypeServiceImpl extends ServiceImpl<DeviceTypeMapper, DeviceT
private final DeviceAssignmentsMapper deviceAssignmentsMapper;
private final ISysRoleService roleService;
private final ISysOssService ossService;
private final FileHashUtil fileHashUtil;
/**
@ -181,24 +187,38 @@ public class DeviceTypeServiceImpl extends ServiceImpl<DeviceTypeMapper, DeviceT
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void create(DeviceType resources) {
public void create(DeviceTypeForm resources) throws IOException {
// 校验设备类型名称
List<DeviceType> typeName = deviceTypeMapper.selectList(new QueryWrapper<DeviceType>().eq("type_name", resources.getTypeName()));
if (CollectionUtil.isNotEmpty(typeName)) {
throw new RuntimeException("设备类型名称已存在,无法新增!!!");
}
// 保存图片并获取URL
if (resources.getFile() != null) {
String fileHash = fileHashUtil.hash(resources.getFile());
SysOssVo upload = ossService.updateHash(resources.getFile(), fileHash);
// 强制将HTTP替换为HTTPS
if (upload.getUrl() != null && upload.getUrl().startsWith("http://")) {
upload.setUrl(upload.getUrl().replaceFirst("^http://", "https://"));
}
// 设置图片路径
resources.setDevicePic(upload.getUrl());
}
DeviceType deviceType = new DeviceType();
BeanUtil.copyProperties(resources, deviceType, true);
LoginUser loginUser = LoginHelper.getLoginUser();
resources.setCustomerId(loginUser.getUserId());
resources.setOwnerCustomerId(loginUser.getUserId());
resources.setOriginalOwnerId(loginUser.getUserId());
resources.setCreateByName(loginUser.getNickname());
deviceTypeMapper.insert(resources);
deviceType.setCustomerId(loginUser.getUserId());
deviceType.setOwnerCustomerId(loginUser.getUserId());
deviceType.setOriginalOwnerId(loginUser.getUserId());
deviceType.setCreateByName(loginUser.getNickname());
deviceTypeMapper.insert(deviceType);
// 自动授权给自己
DeviceTypeGrants deviceTypeGrants = new DeviceTypeGrants();
deviceTypeGrants.setDeviceTypeId(resources.getId());
deviceTypeGrants.setDeviceTypeId(deviceType.getId());
deviceTypeGrants.setCustomerId(loginUser.getUserId());
deviceTypeGrants.setGrantorCustomerId(loginUser.getUserId());
deviceTypeGrants.setGrantedAt(new Date());
@ -213,7 +233,7 @@ public class DeviceTypeServiceImpl extends ServiceImpl<DeviceTypeMapper, DeviceT
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void update(DeviceTypeForm resources) {
public void update(DeviceTypeForm resources) throws IOException {
DeviceTypeGrants deviceTypeGrants = deviceTypeGrantsMapper.selectById(resources.getId());
if (deviceTypeGrants == null) {
throw new RuntimeException("设备类型不存在");
@ -224,22 +244,13 @@ public class DeviceTypeServiceImpl extends ServiceImpl<DeviceTypeMapper, DeviceT
throw new RuntimeException("设备类型不存在");
}
if (!deviceType.getTypeName().equals(resources.getTypeName())) {
int count = deviceMapper.countByDeviceTypeId(deviceType.getId());
if (count > 0) {
throw new RuntimeException("该设备类型下已有绑定设备,无法修改设备类型名称!!!");
throw new RuntimeException("该设备类型下已有设备,无法修改设备类型名称!!!");
}
}
// List<Device> devices = deviceMapper.selectList(new QueryWrapper<Device>()
// .eq("device_type", deviceTypeGrants.getDeviceTypeId()));
// if (CollectionUtil.isNotEmpty(devices)) {
// throw new RuntimeException("该设备类型已绑定设备,无法修改!!!");
// }
// 校验设备类型名称
DeviceType dt = deviceTypeMapper.selectOne(new QueryWrapper<DeviceType>().eq("type_name", resources.getTypeName()));
if (dt != null && !dt.getId().equals(deviceType.getId())) {
@ -253,6 +264,17 @@ public class DeviceTypeServiceImpl extends ServiceImpl<DeviceTypeMapper, DeviceT
throw new RuntimeException("无权修改该设备类型");
}
}
// 保存图片并获取URL
if (resources.getFile() != null) {
String fileHash = fileHashUtil.hash(resources.getFile());
SysOssVo upload = ossService.updateHash(resources.getFile(), fileHash);
// 强制将HTTP替换为HTTPS
if (upload.getUrl() != null && upload.getUrl().startsWith("http://")) {
upload.setUrl(upload.getUrl().replaceFirst("^http://", "https://"));
}
// 设置图片路径
resources.setDevicePic(upload.getUrl());
}
BeanUtil.copyProperties(resources, deviceType);
deviceTypeMapper.updateById(deviceType);

View File

@ -30,27 +30,45 @@ import static cn.dev33.satoken.SaManager.log;
@Component
public class AlibabaTTSUtil {
// ========== 常量配置 ==========
/** 阿里云TTS服务基础URL */
/**
* 阿里云TTS服务基础URL
*/
private static final String BASE_URL = "https://nls-gateway-cn-shanghai.aliyuncs.com/stream/v1/tts";
/** 音频内容类型标识 */
/**
* 音频内容类型标识
*/
private static final String CONTENT_TYPE_AUDIO = "audio/mpeg";
// ========== 默认参数值 ==========
/** 默认发音人 - 小云 */
/**
* 默认发音人 - 小云
*/
private static final String DEFAULT_VOICE = "xiaoyun";
/** 默认音量 50% */
/**
* 默认音量 50%
*/
private static final int DEFAULT_VOLUME = 50;
/** 默认语速 0正常 */
/**
* 默认语速 0正常
*/
private static final int DEFAULT_SPEECH_RATE = 1;
/** 默认语调 0正常 */
/**
* 默认语调 0正常
*/
private static final int DEFAULT_PITCH_RATE = 0;
/** 默认音频格式 pcm */
/**
* 默认音频格式 pcm
*/
private static final String DEFAULT_FORMAT = "pcm";
/** 默认采样率 16000Hz */
/**
* 默认采样率 16000Hz
*/
private static final int DEFAULT_SAMPLE_RATE = 16000;
// ========== Token管理配置 ==========
/** Token刷新缓冲时间提前5分钟刷新单位毫秒 */
/**
* Token刷新缓冲时间提前5分钟刷新单位毫秒
*/
private static final long TOKEN_REFRESH_BUFFER = 5 * 60 * 1000L;
// ========== 配置参数(从配置文件读取) ==========
@ -70,7 +88,8 @@ public class AlibabaTTSUtil {
/**
* 生成语音文件 - 简化版(使用默认参数)
* @param text 要转换的文本内容
*
* @param text 要转换的文本内容
* @param audioSaveFile 音频文件保存路径
* @return true-成功 false-失败
*/
@ -80,11 +99,12 @@ public class AlibabaTTSUtil {
/**
* 生成语音文件 - 标准版
* @param text 要转换的文本内容
*
* @param text 要转换的文本内容
* @param audioSaveFile 音频文件保存路径
* @param format 音频格式mp3, wav等
* @param sampleRate 采样率16000, 22050, 44100等
* @param voice 发音人xiaoyun, xiaoqian等
* @param format 音频格式mp3, wav等
* @param sampleRate 采样率16000, 22050, 44100等
* @param voice 发音人xiaoyun, xiaoqian等
* @return true-成功 false-失败
*/
public boolean generateSpeech(String text, String audioSaveFile, String format,
@ -95,14 +115,15 @@ public class AlibabaTTSUtil {
/**
* 生成语音文件 - 完整版(支持所有参数调节)
* @param text 要转换的文本内容
*
* @param text 要转换的文本内容
* @param audioSaveFile 音频文件保存路径
* @param format 音频格式
* @param sampleRate 采样率
* @param voice 发音人
* @param volume 音量0-100
* @param speechRate 语速(-500~500
* @param pitchRate 语调(-500~500
* @param format 音频格式
* @param sampleRate 采样率
* @param voice 发音人
* @param volume 音量0-100
* @param speechRate 语速(-500~500
* @param pitchRate 语调(-500~500
* @return true-成功 false-失败
*/
public boolean generateSpeech(String text, String audioSaveFile, String format,
@ -158,6 +179,7 @@ public class AlibabaTTSUtil {
/**
* 获取有效的访问令牌(优先从缓存获取,缓存不存在则重新生成)
*
* @return 访问令牌获取失败返回null
*/
private String getValidAccessToken() {
@ -181,6 +203,7 @@ public class AlibabaTTSUtil {
/**
* 刷新访问令牌调用阿里云API获取新令牌并缓存
*
* @return 新的访问令牌获取失败返回null
*/
private String refreshAccessToken() {
@ -202,6 +225,7 @@ public class AlibabaTTSUtil {
/**
* 参数验证
*
* @throws IllegalArgumentException 参数不合法时抛出异常
*/
private void validateParameters(String text, String audioSaveFile, String format, int sampleRate) {
@ -248,10 +272,10 @@ public class AlibabaTTSUtil {
"&text=" + encodedText +
"&format=" + format +
"&sample_rate=" + sampleRate;
//"&voice=" + actualVoice +
//"&volume=" + Math.max(0, Math.min(100, volume)) + // 音量范围限制
//"&speech_rate=" + Math.max(-500, Math.min(500, speechRate)) + // 语速范围限制
//"&pitch_rate=" + Math.max(-500, Math.min(500, pitchRate)); // 语调范围限制
//"&voice=" + actualVoice +
//"&volume=" + Math.max(0, Math.min(100, volume)) + // 音量范围限制
//"&speech_rate=" + Math.max(-500, Math.min(500, speechRate)) + // 语速范围限制
//"&pitch_rate=" + Math.max(-500, Math.min(500, pitchRate)); // 语调范围限制
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("UTF-8编码不支持", e);

View File

@ -2,9 +2,9 @@
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fuyuanshen.app.mapper.AppBusinessFileMapper">
<mapper namespace="com.fuyuanshen.equipment.mapper.AppBusinessFileMapper">
<select id="queryAppFileList" resultType="com.fuyuanshen.app.domain.vo.AppFileVo">
<select id="queryAppFileList" resultType="com.fuyuanshen.equipment.domain.vo.AppFileVo">
select a.id,a.business_id,a.file_id,a.file_type,b.file_name,b.url fileUrl from app_business_file a left join sys_oss b on a.file_id = b.oss_id
where 1=1
<if test="businessId != null">

View File

@ -2,6 +2,6 @@
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fuyuanshen.app.mapper.AppOperationVideoMapper">
<mapper namespace="com.fuyuanshen.equipment.mapper.AppOperationVideoMapper">
</mapper>

View File

@ -48,8 +48,8 @@
r.event_time, r.event_address,
r.create_time
FROM device_fence_access_record r
LEFT JOIN device_geo_fence f ON r.fence_id = f.id
LEFT JOIN device d ON r.device_id = d.id
INNER JOIN device_geo_fence f ON r.fence_id = f.id
INNER JOIN device d ON r.device_id = d.id
<where>
<if test="bo.fenceId != null">
AND r.fence_id = #{bo.fenceId}

View File

@ -13,8 +13,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
a.content,
a.create_time AS createTime
FROM
device_log a left join device b on a.device_id = b.id
left join device_type c on b.device_type = c.id
device_log a inner join device b on a.device_id = b.id
inner join device_type c on b.device_type = c.id
WHERE 1 = 1
<if test="bo.deviceType != null">
AND c.id = #{bo.deviceType}

View File

@ -264,6 +264,44 @@
</if>
</select>
<select id="GetDeviceByName" resultType="map" >
select a.id,
a.device_type,
a.device_name,
a.device_mac,
a.type_name,
a.bluetooth_name,
a.device_imei
from device a
<where>
a.tenant_id = #{tenant_id} and
a.device_type = #{deviceType}
AND (
a.device_name=#{deviceName} or
a.bluetooth_name=#{deviceName}
)
</where>
</select>
<select id="getEquipCountByType" resultType="int">
select count(1) cnt
from device a
<where>
a.tenant_id = #{tenant_id} and
a.device_type = #{deviceType}
</where>
</select>
<select id="getEquipAllByType" resultType="map">
select device_mac,bluetooth_name,device_name
from device a
<where>
a.tenant_id = #{tenant_id} and
a.device_type = #{deviceType}
</where>
</select>
<!-- 获取分配设备的客户 -->
<select id="getAssignCustomer" resultType="com.fuyuanshen.equipment.domain.Device">
SELECT *

View File

@ -36,6 +36,7 @@ public class SysMenuController extends BaseController {
private final ISysMenuService menuService;
/**
* 获取路由信息
*
@ -47,6 +48,7 @@ public class SysMenuController extends BaseController {
return R.ok(menuService.buildMenus(menus));
}
/**
* 获取菜单列表
*/

View File

@ -31,6 +31,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.stream.Collectors;
/**
* 菜单 业务层处理
@ -141,10 +142,17 @@ public class SysMenuServiceImpl implements ISysMenuService {
menus = baseMapper.selectMenuTreeAll();
} else {
menus = baseMapper.selectMenuTreeByUserId(userId);
// 如果不是超级管理员且不是租户管理员,则过滤掉受限制的菜单
// if (!LoginHelper.isAdmin()) {
menus = menus.stream()
.filter(menu -> !SystemConstants.RESTRICTED_MENU_ID.equals(menu.getMenuId()))
.collect(Collectors.toList());
// }
}
return getChildPerms(menus, 0);
}
/**
* 根据角色ID查询菜单树信息
*