Compare commits
39 Commits
fcbde4322d
...
5f36c25550
| Author | SHA1 | Date | |
|---|---|---|---|
| 5f36c25550 | |||
| 9058dcfcb1 | |||
| 3e19cd9db2 | |||
| 397f227bfb | |||
| 1b2bf542fa | |||
| 7d4bc1f8ad | |||
| 4d9038567f | |||
| 630c6a8a35 | |||
| 11e48acc81 | |||
| 540a6fc1da | |||
| aeea8f9072 | |||
| b463e97d28 | |||
| e1b147b6be | |||
| 23b48cfb06 | |||
| 4106260e5f | |||
| 07449e4a1e | |||
| b278a3087e | |||
| dca5a6c48d | |||
| 15b3348989 | |||
| 9fbb0aefcf | |||
| 4004aa1090 | |||
| 39c8375f08 | |||
| 64c81ac44e | |||
| 41eb3d9a33 | |||
| 8597dc5a9f | |||
| 01a1a6e25b | |||
| 1722f92328 | |||
| ee8840d657 | |||
| 9b84296419 | |||
| d48c7b389e | |||
| ccdc25595d | |||
| f1aad91421 | |||
| e2274bdf09 | |||
| 8a127b9d2d | |||
| b8af6b511c | |||
| 6bc1d5b20b | |||
| 228e26df7f | |||
| 801b0267e6 | |||
| d5a9a92f9f |
@ -123,6 +123,17 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>javacv-platform</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>javacv</artifactId>
|
||||
<version>1.5.7</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- skywalking 整合 logback -->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>org.apache.skywalking</groupId>-->
|
||||
|
||||
@ -4,22 +4,33 @@ import cn.dev33.satoken.annotation.SaIgnore;
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import com.fuyuanshen.app.model.AppRegisterBody;
|
||||
import com.fuyuanshen.app.model.AppSmsLoginBody;
|
||||
import com.fuyuanshen.app.service.AppLoginService;
|
||||
import com.fuyuanshen.app.service.AppRegisterService;
|
||||
import com.fuyuanshen.common.core.constant.Constants;
|
||||
import com.fuyuanshen.common.core.constant.GlobalConstants;
|
||||
import com.fuyuanshen.common.core.constant.SystemConstants;
|
||||
import com.fuyuanshen.common.core.domain.R;
|
||||
import com.fuyuanshen.common.core.domain.model.AppPasswordLoginBody;
|
||||
import com.fuyuanshen.common.core.domain.model.RegisterBody;
|
||||
import com.fuyuanshen.common.core.domain.model.SmsLoginBody;
|
||||
import com.fuyuanshen.common.core.enums.LoginType;
|
||||
import com.fuyuanshen.common.core.utils.*;
|
||||
import com.fuyuanshen.common.encrypt.annotation.ApiEncrypt;
|
||||
import com.fuyuanshen.common.json.utils.JsonUtils;
|
||||
import com.fuyuanshen.common.ratelimiter.annotation.RateLimiter;
|
||||
import com.fuyuanshen.common.redis.utils.RedisUtils;
|
||||
import com.fuyuanshen.common.satoken.utils.LoginHelper;
|
||||
import com.fuyuanshen.common.tenant.helper.TenantHelper;
|
||||
import com.fuyuanshen.system.domain.bo.SysTenantBo;
|
||||
import com.fuyuanshen.system.domain.vo.SysClientVo;
|
||||
import com.fuyuanshen.system.domain.vo.SysDictDataVo;
|
||||
import com.fuyuanshen.system.domain.vo.SysTenantVo;
|
||||
import com.fuyuanshen.system.service.ISysClientService;
|
||||
import com.fuyuanshen.system.service.ISysConfigService;
|
||||
import com.fuyuanshen.system.service.ISysDictTypeService;
|
||||
import com.fuyuanshen.system.service.ISysTenantService;
|
||||
import com.fuyuanshen.web.domain.vo.LoginTenantVo;
|
||||
import com.fuyuanshen.web.domain.vo.LoginVo;
|
||||
@ -27,6 +38,7 @@ import com.fuyuanshen.web.domain.vo.TenantListVo;
|
||||
import com.fuyuanshen.web.service.IAuthStrategy;
|
||||
import com.fuyuanshen.web.service.SysRegisterService;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.api.SmsBlend;
|
||||
@ -36,8 +48,13 @@ import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.net.URL;
|
||||
import java.time.Duration;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.fuyuanshen.common.core.constant.GlobalConstants.DEVICE_SHARE_CODES_KEY;
|
||||
|
||||
/**
|
||||
* APP认证
|
||||
*
|
||||
@ -51,10 +68,11 @@ import java.util.List;
|
||||
public class AppAuthController {
|
||||
|
||||
private final AppLoginService loginService;
|
||||
private final SysRegisterService registerService;
|
||||
private final AppRegisterService registerService;
|
||||
private final ISysConfigService configService;
|
||||
private final ISysTenantService tenantService;
|
||||
private final ISysClientService clientService;
|
||||
private final ISysDictTypeService dictTypeService;
|
||||
|
||||
|
||||
/**
|
||||
@ -66,16 +84,34 @@ public class AppAuthController {
|
||||
@PostMapping("/login")
|
||||
public R<LoginVo> login(@RequestBody AppSmsLoginBody appSmsLoginBody) {
|
||||
// SmsLoginBody loginBody = JsonUtils.parseObject(body, SmsLoginBody.class);
|
||||
ValidatorUtils.validate(appSmsLoginBody);
|
||||
SmsLoginBody loginBody = new SmsLoginBody();
|
||||
loginBody.setPhonenumber(appSmsLoginBody.getPhonenumber());
|
||||
loginBody.setSmsCode(appSmsLoginBody.getSmsCode());
|
||||
loginBody.setTenantId(appSmsLoginBody.getTenantId());
|
||||
loginBody.setClientId("ca839698e245d60aa2f0e59bd52b34f8");
|
||||
loginBody.setGrantType("appSms");
|
||||
String loginType = appSmsLoginBody.getLoginType();
|
||||
String body = "";
|
||||
// 授权类型和客户端id
|
||||
String clientId = loginBody.getClientId();
|
||||
String grantType = loginBody.getGrantType();
|
||||
String clientId = "";
|
||||
String grantType = "";
|
||||
String tenantId = appSmsLoginBody.getTenantId();
|
||||
ValidatorUtils.validate(appSmsLoginBody);
|
||||
if("2".equals(loginType)){
|
||||
AppPasswordLoginBody loginBody = new AppPasswordLoginBody();
|
||||
loginBody.setUsername(appSmsLoginBody.getPhonenumber());
|
||||
loginBody.setPassword(appSmsLoginBody.getLoginPassword());
|
||||
loginBody.setClientId("835b15335d389c9fcfdf99421fa8019b");
|
||||
loginBody.setGrantType("appPassword");
|
||||
clientId = loginBody.getClientId();
|
||||
grantType = loginBody.getGrantType();
|
||||
body = JsonUtils.toJsonString(loginBody);
|
||||
}else{
|
||||
SmsLoginBody loginBody = new SmsLoginBody();
|
||||
loginBody.setPhonenumber(appSmsLoginBody.getPhonenumber());
|
||||
loginBody.setSmsCode(appSmsLoginBody.getSmsCode());
|
||||
loginBody.setTenantId(appSmsLoginBody.getTenantId());
|
||||
loginBody.setClientId("ca839698e245d60aa2f0e59bd52b34f8");
|
||||
loginBody.setGrantType("appSms");
|
||||
clientId = loginBody.getClientId();
|
||||
grantType = loginBody.getGrantType();
|
||||
body = JsonUtils.toJsonString(loginBody);
|
||||
}
|
||||
|
||||
SysClientVo client = clientService.queryByClientId(clientId);
|
||||
// 查询不到 client 或 client 内不包含 grantType
|
||||
if (ObjectUtil.isNull(client) || !StringUtils.contains(client.getGrantType(), grantType)) {
|
||||
@ -85,25 +121,14 @@ public class AppAuthController {
|
||||
return R.fail(MessageUtils.message("auth.grant.type.blocked"));
|
||||
}
|
||||
// 校验租户
|
||||
loginService.checkTenant(loginBody.getTenantId());
|
||||
// loginService.checkTenant(loginBody.getTenantId());
|
||||
loginService.checkTenant(tenantId);
|
||||
// 登录
|
||||
String body = JsonUtils.toJsonString(loginBody);
|
||||
LoginVo loginVo = IAuthStrategy.login(body, client, grantType);
|
||||
return R.ok(loginVo);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 退出登录
|
||||
*/
|
||||
@PostMapping("/logout")
|
||||
public R<Void> logout() {
|
||||
loginService.logout();
|
||||
return R.ok("退出成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户注销
|
||||
*/
|
||||
@ -113,16 +138,68 @@ public class AppAuthController {
|
||||
return R.ok("用户注销成功");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 用户注册
|
||||
*/
|
||||
@ApiEncrypt
|
||||
@PostMapping("/register")
|
||||
public R<Void> register(@Validated @RequestBody RegisterBody user) {
|
||||
if (!configService.selectRegisterEnabled(user.getTenantId())) {
|
||||
return R.fail("当前系统没有开启注册功能!");
|
||||
public R<Void> register(@Validated @RequestBody AppRegisterBody body) {
|
||||
registerService.register(body);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 找回密码
|
||||
*/
|
||||
@PostMapping("/forgetPassword")
|
||||
public R<Void> forgetPassword(@Validated @RequestBody AppRegisterBody body) {
|
||||
registerService.forgetPassword(body);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 用户注册短信验证码
|
||||
*
|
||||
* @param phonenumber 用户手机号
|
||||
*/
|
||||
@SaIgnore
|
||||
@RateLimiter(key = "#phonenumber", time = 60, count = 1)
|
||||
@GetMapping("/registerSmsCode")
|
||||
public R<Void> registerSmsCode(@NotBlank(message = "{user.phonenumber.not.blank}") String phonenumber) {
|
||||
String key = GlobalConstants.REGISTER_CODE_KEY + phonenumber;
|
||||
String code = RandomUtil.randomNumbers(4);
|
||||
RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
|
||||
LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
|
||||
map.put("code", code);
|
||||
SmsBlend smsBlend = SmsFactory.getSmsBlend("config1");
|
||||
SmsResponse smsResponse = smsBlend.sendMessage(phonenumber, map);
|
||||
if (!smsResponse.isSuccess()) {
|
||||
return R.fail(smsResponse.getData().toString());
|
||||
}
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 找回密码短信验证码
|
||||
*
|
||||
* @param phonenumber 用户手机号
|
||||
*/
|
||||
@SaIgnore
|
||||
@RateLimiter(key = "#phonenumber", time = 60, count = 1)
|
||||
@GetMapping("/forgetPasswordSmsCode")
|
||||
public R<Void> forgetPasswordSmsCode(@NotBlank(message = "{user.phonenumber.not.blank}") String phonenumber) {
|
||||
String key = GlobalConstants.FORGET_PASSWORD_CODE_KEY + phonenumber;
|
||||
String code = RandomUtil.randomNumbers(4);
|
||||
RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
|
||||
LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
|
||||
map.put("code", code);
|
||||
SmsBlend smsBlend = SmsFactory.getSmsBlend("config1");
|
||||
SmsResponse smsResponse = smsBlend.sendMessage(phonenumber, map);
|
||||
if (!smsResponse.isSuccess()) {
|
||||
return R.fail(smsResponse.getData().toString());
|
||||
}
|
||||
registerService.register(user);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@ -179,4 +256,26 @@ public class AppAuthController {
|
||||
SmsResponse smsResponse = smsBlend.sendMessage("18656573389", "123");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取app版本
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/version")
|
||||
public R<List<SysDictDataVo>> getAppVersion() {
|
||||
List<SysDictDataVo> list = dictTypeService.selectDictDataByType("app_version");
|
||||
list.forEach(d -> {
|
||||
String[] arr = d.getRemark().split("\\|");
|
||||
d.setDictLabel(d.getDictLabel()); // ios/android
|
||||
d.setDictValue(arr[0]); // 版本号
|
||||
d.setRemark(arr[1]); // 下载地址
|
||||
});
|
||||
// 只保留方法体:筛选 label=ios 且版本号 ≥ 2.5.0 的列表
|
||||
// List<SysDictDataVo> result = list.stream()
|
||||
// .peek(d -> { String[] a = d.getRemark().split("\\|"); d.setDictValue(a[0]); d.setRemark(a[1]); })
|
||||
// .filter(d -> "ios".equalsIgnoreCase(d.getDictLabel()))
|
||||
// .filter(d -> VersionComparator.INSTANCE.compare(d.getDictValue(), "2.5.0") >= 0)
|
||||
// .toList();
|
||||
return R.ok(list);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,55 @@
|
||||
package com.fuyuanshen.app.controller;
|
||||
|
||||
import com.fuyuanshen.app.model.AppRegisterBody;
|
||||
import com.fuyuanshen.app.model.AppUpdatePasswordBody;
|
||||
import com.fuyuanshen.app.service.AppLoginService;
|
||||
import com.fuyuanshen.app.service.AppRegisterService;
|
||||
import com.fuyuanshen.common.core.domain.R;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* APP我的
|
||||
*
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/app/userCenter")
|
||||
public class AppUserCenterController {
|
||||
|
||||
private final AppLoginService loginService;
|
||||
private final AppRegisterService registerService;
|
||||
/**
|
||||
* 用户注销
|
||||
*/
|
||||
@PostMapping("/cancelAccount")
|
||||
public R<Void> cancelAccount() {
|
||||
loginService.cancelAccount();
|
||||
return R.ok("用户注销成功");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 修改密码
|
||||
*/
|
||||
@PostMapping("/updatePassword")
|
||||
public R<Void> updatePassword(@Validated @RequestBody AppUpdatePasswordBody body) {
|
||||
registerService.updatePassword(body);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出登录
|
||||
*/
|
||||
@PostMapping("/logout")
|
||||
public R<Void> logout() {
|
||||
loginService.logout();
|
||||
return R.ok("退出成功");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,249 @@
|
||||
package com.fuyuanshen.app.controller;
|
||||
|
||||
import com.fuyuanshen.common.core.domain.R;
|
||||
import com.fuyuanshen.common.web.core.BaseController;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.bytedeco.javacv.FFmpegFrameGrabber;
|
||||
import org.bytedeco.javacv.Frame;
|
||||
import org.bytedeco.javacv.Java2DFrameUtils;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* APP 视频处理
|
||||
* @date 2025-09-15
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/app/video")
|
||||
public class AppVideoController extends BaseController {
|
||||
// 可配置项:建议从 application.yml 中读取
|
||||
private static final int MAX_VIDEO_SIZE = 10 * 1024 * 1024; // 10 MB
|
||||
private static final int FRAME_RATE = 15; // 每秒抽15帧
|
||||
private static final int DURATION = 2; // 抽2秒
|
||||
private static final int TOTAL_FRAMES = FRAME_RATE * DURATION;
|
||||
private static final int WIDTH = 160;
|
||||
private static final int HEIGHT = 80;
|
||||
private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
|
||||
|
||||
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||
public R<List<String>> upload(@RequestParam("file") MultipartFile file) {
|
||||
if (file == null || file.isEmpty()) {
|
||||
return R.fail("上传文件不能为空");
|
||||
}
|
||||
|
||||
if (!isVideo(file.getOriginalFilename())) {
|
||||
return R.fail("只允许上传视频文件");
|
||||
}
|
||||
|
||||
if (file.getSize() > MAX_VIDEO_SIZE) {
|
||||
return R.fail("视频大小不能超过10MB");
|
||||
}
|
||||
|
||||
File tempFile = null;
|
||||
try {
|
||||
// 创建临时文件保存上传的视频
|
||||
tempFile = createTempVideoFile(file);
|
||||
|
||||
|
||||
List<BufferedImage> frames = extractFramesFromVideo(tempFile);
|
||||
if (frames.isEmpty()) {
|
||||
return R.fail("无法提取任何帧");
|
||||
}
|
||||
|
||||
// ✅ 新增:保存帧为图片
|
||||
//saveFramesToLocal(frames, "extracted_frame");
|
||||
|
||||
byte[] binaryData = convertFramesToRGB565(frames);
|
||||
// String base64Data = Base64.getEncoder().encodeToString(binaryData);
|
||||
//
|
||||
// return R.ok(base64Data);
|
||||
// 构造响应头
|
||||
// 将二进制数据转为 Hex 字符串
|
||||
// 转换为 Hex 字符串列表
|
||||
List<String> hexList = bytesToHexList(binaryData);
|
||||
|
||||
return R.ok(hexList);
|
||||
|
||||
} catch (Exception e) {
|
||||
return R.fail("视频处理失败:" + e.getMessage());
|
||||
} finally {
|
||||
deleteTempFile(tempFile);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* rgb565 转 hex
|
||||
*/
|
||||
private List<String> bytesToHexList(byte[] bytes) {
|
||||
List<String> hexList = new ArrayList<>();
|
||||
for (byte b : bytes) {
|
||||
int value = b & 0xFF;
|
||||
char high = HEX_ARRAY[value >>> 4];
|
||||
char low = HEX_ARRAY[value & 0x0F];
|
||||
hexList.add(String.valueOf(high) + low);
|
||||
}
|
||||
return hexList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建临时文件并保存上传的视频
|
||||
*/
|
||||
private File createTempVideoFile(MultipartFile file) throws Exception {
|
||||
File tempFile = Files.createTempFile("upload-", ".mp4").toFile();
|
||||
file.transferTo(tempFile);
|
||||
return tempFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从视频中按时间均匀提取指定数量的帧
|
||||
*/
|
||||
private List<BufferedImage> extractFramesFromVideo(File videoFile) throws Exception {
|
||||
List<BufferedImage> frames = new ArrayList<>();
|
||||
|
||||
try (FFmpegFrameGrabber grabber = FFmpegFrameGrabber.createDefault(videoFile)) {
|
||||
grabber.start();
|
||||
|
||||
// 获取视频总帧数和帧率
|
||||
long totalFramesInVideo = grabber.getLengthInFrames();
|
||||
int fps = (int) Math.round(grabber.getFrameRate());
|
||||
if (fps <= 0) fps = 30;
|
||||
|
||||
double durationSeconds = (double) totalFramesInVideo / fps;
|
||||
|
||||
if (durationSeconds < DURATION) {
|
||||
throw new IllegalArgumentException("视频太短,至少需要 " + DURATION + " 秒");
|
||||
}
|
||||
|
||||
// 计算每帧之间的间隔(浮点以实现更精确跳转)
|
||||
double frameInterval = (double) totalFramesInVideo / TOTAL_FRAMES;
|
||||
|
||||
for (int i = 0; i < TOTAL_FRAMES; i++) {
|
||||
int targetFrameNumber = (int) Math.round(i * frameInterval);
|
||||
|
||||
// 避免设置无效帧号
|
||||
if (targetFrameNumber >= totalFramesInVideo) {
|
||||
throw new IllegalArgumentException("目标帧超出范围: " + targetFrameNumber + " ");
|
||||
}
|
||||
|
||||
grabber.setFrameNumber(targetFrameNumber);
|
||||
Frame frame = grabber.grab();
|
||||
|
||||
if (frame != null && frame.image != null) {
|
||||
BufferedImage bufferedImage = Java2DFrameUtils.toBufferedImage(frame);
|
||||
frames.add(cropImage(bufferedImage, WIDTH, HEIGHT));
|
||||
} else {
|
||||
throw new IllegalArgumentException("无法获取第 " + targetFrameNumber + "帧 ");
|
||||
}
|
||||
}
|
||||
|
||||
grabber.stop();
|
||||
}
|
||||
|
||||
return frames;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将抽取的帧保存到本地,用于调试
|
||||
*/
|
||||
private void saveFramesToLocal(List<BufferedImage> frames, String prefix) {
|
||||
// 指定输出目录
|
||||
File outputDir = new File("output_frames");
|
||||
if (!outputDir.exists()) {
|
||||
outputDir.mkdirs();
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
for (BufferedImage frame : frames) {
|
||||
try {
|
||||
File outputImage = new File(outputDir, prefix + "_" + (index++) + ".png");
|
||||
ImageIO.write(frame, "png", outputImage);
|
||||
System.out.println("保存帧图片成功: " + outputImage.getAbsolutePath());
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("保存帧图片失败 " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将所有帧转换为 RGB565 格式字节数组
|
||||
*/
|
||||
private byte[] convertFramesToRGB565(List<BufferedImage> frames) throws Exception {
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
|
||||
for (BufferedImage image : frames) {
|
||||
byte[] rgb565Bytes = convertToRGB565(image);
|
||||
byteArrayOutputStream.write(rgb565Bytes);
|
||||
}
|
||||
|
||||
return byteArrayOutputStream.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否是支持的视频格式
|
||||
*/
|
||||
private boolean isVideo(String filename) {
|
||||
String ext = filename.substring(filename.lastIndexOf('.')).toLowerCase();
|
||||
return Arrays.asList(".mp4", ".avi", ".mov", ".mkv").contains(ext);
|
||||
}
|
||||
|
||||
/**
|
||||
* 裁剪图像到目标尺寸
|
||||
*/
|
||||
private BufferedImage cropImage(BufferedImage img, int targetWidth, int targetHeight) {
|
||||
int w = Math.min(img.getWidth(), targetWidth);
|
||||
int h = Math.min(img.getHeight(), targetHeight);
|
||||
return img.getSubimage(0, 0, w, h);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 BufferedImage 转换为 RGB565 格式的字节数组
|
||||
*/
|
||||
private byte[] convertToRGB565(BufferedImage image) {
|
||||
int width = image.getWidth();
|
||||
int height = image.getHeight();
|
||||
byte[] result = new byte[width * height * 2]; // RGB565: 2 bytes per pixel
|
||||
int index = 0;
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
int rgb = image.getRGB(x, y);
|
||||
int r = ((rgb >> 16) & 0xFF) >> 3;
|
||||
int g = ((rgb >> 8) & 0xFF) >> 2;
|
||||
int b = (rgb & 0xFF) >> 3;
|
||||
short pixel = (short) ((r << 11) | (g << 5) | b);
|
||||
|
||||
result[index++] = (byte) (pixel >> 8); // High byte first
|
||||
result[index++] = (byte) pixel;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除临时文件
|
||||
*/
|
||||
private void deleteTempFile(File file) {
|
||||
if (file != null && file.exists()) {
|
||||
if (!file.delete()) {
|
||||
throw new IllegalArgumentException("无法删除临时文件: " + file.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5,6 +5,7 @@ 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.log.annotation.Log;
|
||||
import com.fuyuanshen.common.ratelimiter.annotation.FunctionAccessAnnotation;
|
||||
import com.fuyuanshen.common.ratelimiter.annotation.FunctionAccessBatcAnnotation;
|
||||
import com.fuyuanshen.common.web.core.BaseController;
|
||||
@ -29,6 +30,7 @@ public class AppDeviceXinghanController extends BaseController {
|
||||
/**
|
||||
* 人员信息登记
|
||||
*/
|
||||
@Log(title = "xinghan指令-人员信息登记")
|
||||
@PostMapping(value = "/registerPersonInfo")
|
||||
// @FunctionAccessAnnotation("registerPersonInfo")
|
||||
public R<Void> registerPersonInfo(@Validated(AddGroup.class) @RequestBody AppPersonnelInfoBo bo) {
|
||||
@ -38,6 +40,7 @@ public class AppDeviceXinghanController extends BaseController {
|
||||
/**
|
||||
* 发送紧急通知
|
||||
*/
|
||||
@Log(title = "xinghan指令-发送紧急通知")
|
||||
@PostMapping(value = "/sendAlarmMessage")
|
||||
@FunctionAccessBatcAnnotation(value = "sendAlarmMessage", timeOut = 5, batchMaxTimeOut = 10)
|
||||
public R<Void> sendAlarmMessage(@RequestBody AppDeviceSendMsgBo bo) {
|
||||
@ -47,6 +50,7 @@ public class AppDeviceXinghanController extends BaseController {
|
||||
/**
|
||||
* 上传设备logo图片
|
||||
*/
|
||||
@Log(title = "xinghan指令-上传设备logo图片")
|
||||
@PostMapping("/uploadLogo")
|
||||
@FunctionAccessAnnotation("uploadLogo")
|
||||
public R<Void> upload(@Validated @ModelAttribute AppDeviceLogoUploadDto bo) {
|
||||
@ -64,6 +68,7 @@ public class AppDeviceXinghanController extends BaseController {
|
||||
* 静电预警档位
|
||||
* 3,2,1,0,分别表示高档/中档/低挡/关闭
|
||||
*/
|
||||
@Log(title = "xinghan指令-静电预警档位")
|
||||
@PostMapping("/DetectGradeSettings")
|
||||
public R<Void> DetectGradeSettings(@RequestBody DeviceInstructDto params) {
|
||||
// params 转 JSONObject
|
||||
@ -75,6 +80,7 @@ public class AppDeviceXinghanController extends BaseController {
|
||||
* 照明档位
|
||||
* 照明档位,2,1,0,分别表示弱光/强光/关闭
|
||||
*/
|
||||
@Log(title = "xinghan指令-照明档位")
|
||||
@PostMapping("/LightGradeSettings")
|
||||
public R<Void> LightGradeSettings(@RequestBody DeviceInstructDto params) {
|
||||
// params 转 JSONObject
|
||||
@ -86,6 +92,7 @@ public class AppDeviceXinghanController extends BaseController {
|
||||
* SOS档位s
|
||||
* SOS档位,2,1,0, 分别表示红蓝模式/爆闪模式/关闭
|
||||
*/
|
||||
@Log(title = "xinghan指令-SOS档位s")
|
||||
@PostMapping("/SOSGradeSettings")
|
||||
public R<Void> SOSGradeSettings(@RequestBody DeviceInstructDto params) {
|
||||
// params 转 JSONObject
|
||||
@ -97,6 +104,7 @@ public class AppDeviceXinghanController extends BaseController {
|
||||
* 静止报警状态
|
||||
* 静止报警状态,0-未静止报警,1-正在静止报警。
|
||||
*/
|
||||
@Log(title = "xinghan指令-静止报警状态")
|
||||
@PostMapping("/ShakeBitSettings")
|
||||
public R<Void> ShakeBitSettings(@RequestBody DeviceInstructDto params) {
|
||||
// params 转 JSONObject
|
||||
|
||||
@ -18,4 +18,9 @@ public class AppFileDto {
|
||||
*/
|
||||
private MultipartFile[] files;
|
||||
|
||||
/**
|
||||
* 多设备id
|
||||
*/
|
||||
private Long[] deviceIds;
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,30 @@
|
||||
package com.fuyuanshen.app.model;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AppRegisterBody {
|
||||
/**
|
||||
* 手机号
|
||||
*/
|
||||
@NotBlank(message = "{user.phonenumber.not.blank}")
|
||||
private String phonenumber;
|
||||
|
||||
/**
|
||||
* 短信code
|
||||
*/
|
||||
private String smsCode;
|
||||
|
||||
/**
|
||||
* 租户ID
|
||||
*/
|
||||
@NotBlank(message = "租户ID不能为空")
|
||||
private String tenantId;
|
||||
|
||||
|
||||
/**
|
||||
* 登录密码
|
||||
*/
|
||||
private String password;
|
||||
}
|
||||
@ -14,7 +14,6 @@ public class AppSmsLoginBody {
|
||||
/**
|
||||
* 短信code
|
||||
*/
|
||||
@NotBlank(message = "{sms.code.not.blank}")
|
||||
private String smsCode;
|
||||
|
||||
/**
|
||||
@ -23,4 +22,14 @@ public class AppSmsLoginBody {
|
||||
@NotBlank(message = "租户ID不能为空")
|
||||
private String tenantId;
|
||||
|
||||
/**
|
||||
* 登录方式 1:手机验证码登录 2:密码登录
|
||||
*/
|
||||
@NotBlank(message = "登录方式不能为空")
|
||||
private String loginType;
|
||||
|
||||
/**
|
||||
* 登录密码
|
||||
*/
|
||||
private String loginPassword;
|
||||
}
|
||||
|
||||
@ -0,0 +1,12 @@
|
||||
package com.fuyuanshen.app.model;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AppUpdatePasswordBody {
|
||||
|
||||
/**
|
||||
* 登录密码
|
||||
*/
|
||||
private String password;
|
||||
}
|
||||
@ -0,0 +1,156 @@
|
||||
package com.fuyuanshen.app.service;
|
||||
|
||||
import cn.hutool.crypto.digest.BCrypt;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||
import com.fuyuanshen.app.domain.AppUser;
|
||||
import com.fuyuanshen.app.mapper.AppUserMapper;
|
||||
import com.fuyuanshen.app.model.AppRegisterBody;
|
||||
import com.fuyuanshen.app.model.AppUpdatePasswordBody;
|
||||
import com.fuyuanshen.common.core.constant.Constants;
|
||||
import com.fuyuanshen.common.core.constant.GlobalConstants;
|
||||
import com.fuyuanshen.common.core.domain.model.RegisterBody;
|
||||
import com.fuyuanshen.common.core.enums.UserType;
|
||||
import com.fuyuanshen.common.core.exception.user.CaptchaException;
|
||||
import com.fuyuanshen.common.core.exception.user.CaptchaExpireException;
|
||||
import com.fuyuanshen.common.core.exception.user.UserException;
|
||||
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.StringUtils;
|
||||
import com.fuyuanshen.common.log.event.LogininforEvent;
|
||||
import com.fuyuanshen.common.redis.utils.RedisUtils;
|
||||
import com.fuyuanshen.common.satoken.utils.AppLoginHelper;
|
||||
import com.fuyuanshen.common.tenant.helper.TenantHelper;
|
||||
import com.fuyuanshen.common.web.config.properties.CaptchaProperties;
|
||||
import com.fuyuanshen.system.domain.SysUser;
|
||||
import com.fuyuanshen.system.domain.bo.SysUserBo;
|
||||
import com.fuyuanshen.system.mapper.SysUserMapper;
|
||||
import com.fuyuanshen.system.service.ISysUserService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 注册校验方法
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class AppRegisterService {
|
||||
|
||||
private final IAppUserService userService;
|
||||
private final AppUserMapper userMapper;
|
||||
|
||||
/**
|
||||
* 注册
|
||||
*/
|
||||
public void register(AppRegisterBody registerBody) {
|
||||
String tenantId = registerBody.getTenantId();
|
||||
String username = registerBody.getPhonenumber();
|
||||
String password = registerBody.getPassword();
|
||||
|
||||
boolean codeValidate = validateRegisterSmsCode(tenantId, username, registerBody.getSmsCode());
|
||||
if (!codeValidate) {
|
||||
throw new CaptchaException();
|
||||
}
|
||||
|
||||
// 校验用户类型是否存在
|
||||
String userType = UserType.APP_USER.getUserType();
|
||||
|
||||
boolean exist = TenantHelper.dynamic(tenantId, () -> {
|
||||
return userMapper.exists(new LambdaQueryWrapper<AppUser>()
|
||||
.eq(AppUser::getUserName, username));
|
||||
});
|
||||
if (exist) {
|
||||
throw new UserException("user.register.save.error", username);
|
||||
}
|
||||
AppUser appUser = new AppUser();
|
||||
appUser.setPhonenumber(username);
|
||||
appUser.setUserName(username);
|
||||
appUser.setStatus("0");
|
||||
appUser.setLoginDate(new Date());
|
||||
appUser.setLoginIp(ServletUtils.getClientIP());
|
||||
appUser.setTenantId(tenantId);
|
||||
appUser.setPassword(password);
|
||||
appUser.setUserType(userType);
|
||||
userMapper.insert(appUser);
|
||||
recordLogininfor(tenantId, username, Constants.REGISTER, MessageUtils.message("user.register.success"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册校验短信验证码
|
||||
*/
|
||||
private boolean validateRegisterSmsCode(String tenantId, String phonenumber, String smsCode) {
|
||||
String code = RedisUtils.getCacheObject(GlobalConstants.REGISTER_CODE_KEY + phonenumber);
|
||||
if (StringUtils.isBlank(code)) {
|
||||
recordLogininfor(tenantId, phonenumber, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
|
||||
throw new CaptchaExpireException();
|
||||
}
|
||||
return code.equals(smsCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 忘记密码校验验证码
|
||||
*/
|
||||
private boolean validateForgetPasswordCode(String tenantId, String phonenumber, String smsCode) {
|
||||
String code = RedisUtils.getCacheObject(GlobalConstants.FORGET_PASSWORD_CODE_KEY + phonenumber);
|
||||
if (StringUtils.isBlank(code)) {
|
||||
recordLogininfor(tenantId, phonenumber, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
|
||||
throw new CaptchaExpireException();
|
||||
}
|
||||
return code.equals(smsCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录登录信息
|
||||
*
|
||||
* @param tenantId 租户ID
|
||||
* @param username 用户名
|
||||
* @param status 状态
|
||||
* @param message 消息内容
|
||||
* @return
|
||||
*/
|
||||
private void recordLogininfor(String tenantId, String username, String status, String message) {
|
||||
LogininforEvent logininforEvent = new LogininforEvent();
|
||||
logininforEvent.setTenantId(tenantId);
|
||||
logininforEvent.setUsername(username);
|
||||
logininforEvent.setStatus(status);
|
||||
logininforEvent.setMessage(message);
|
||||
logininforEvent.setRequest(ServletUtils.getRequest());
|
||||
SpringUtils.context().publishEvent(logininforEvent);
|
||||
}
|
||||
|
||||
public void forgetPassword(AppRegisterBody registerBody) {
|
||||
String tenantId = registerBody.getTenantId();
|
||||
String username = registerBody.getPhonenumber();
|
||||
String password = registerBody.getPassword();
|
||||
boolean codeValidate = validateForgetPasswordCode(tenantId, username, registerBody.getSmsCode());
|
||||
if (!codeValidate) {
|
||||
throw new CaptchaException();
|
||||
}
|
||||
boolean exist = TenantHelper.dynamic(tenantId, () -> {
|
||||
return userMapper.exists(new LambdaQueryWrapper<AppUser>()
|
||||
.eq(AppUser::getUserName, username));
|
||||
});
|
||||
if (!exist) {
|
||||
throw new UserException("用户不存在");
|
||||
}
|
||||
|
||||
UpdateWrapper<AppUser> updateWrapper = new UpdateWrapper<>();
|
||||
updateWrapper.eq("user_name", username)
|
||||
.set("password", password);
|
||||
userMapper.update(updateWrapper);
|
||||
}
|
||||
|
||||
public void updatePassword(AppUpdatePasswordBody body) {
|
||||
String username = AppLoginHelper.getUsername();
|
||||
String password = body.getPassword();
|
||||
UpdateWrapper<AppUser> updateWrapper = new UpdateWrapper<>();
|
||||
updateWrapper.eq("user_name", username)
|
||||
.set("password", password);
|
||||
userMapper.update(updateWrapper);
|
||||
}
|
||||
}
|
||||
@ -39,8 +39,8 @@ public class MqttXinghanJson {
|
||||
/**
|
||||
* 第七键值对, 静止报警状态,0-未静止报警,1-正在静止报警。
|
||||
*/
|
||||
@JsonProperty("staShakeBit")
|
||||
public Integer sta_ShakeBit;
|
||||
@JsonProperty("sta_ShakeBit")
|
||||
public Integer staShakeBit;
|
||||
/**
|
||||
* 第八键值对, 4G信号强度,0-32,数值越大,信号越强。
|
||||
*/
|
||||
|
||||
@ -3,6 +3,7 @@ package com.fuyuanshen.global.mqtt.config;
|
||||
|
||||
import cn.hutool.core.lang.UUID;
|
||||
import com.fuyuanshen.global.mqtt.receiver.ReceiverMessageHandler;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@ -55,4 +56,10 @@ public class MqttInboundConfiguration {
|
||||
public MessageHandler messageHandler(){
|
||||
return receiverMessageHandler;
|
||||
}
|
||||
|
||||
// @Bean
|
||||
// @ServiceActivator(inputChannel = "messageInboundChannel") // 确保通道名称正确
|
||||
// public MessageHandler deviceAlarmMessageHandler() {
|
||||
// return new DeviceAlrmMessageHandler();
|
||||
// }
|
||||
}
|
||||
@ -4,11 +4,14 @@ import cn.hutool.core.thread.ThreadUtil;
|
||||
import com.baomidou.lock.LockInfo;
|
||||
import com.baomidou.lock.LockTemplate;
|
||||
import com.baomidou.lock.executor.RedissonLockExecutor;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||
import com.fuyuanshen.common.core.constant.GlobalConstants;
|
||||
import com.fuyuanshen.common.core.utils.StringUtils;
|
||||
import com.fuyuanshen.common.redis.utils.RedisUtils;
|
||||
import com.fuyuanshen.equipment.domain.Device;
|
||||
import com.fuyuanshen.equipment.mapper.DeviceMapper;
|
||||
import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants;
|
||||
import com.fuyuanshen.global.mqtt.listener.domain.FunctionAccessStatus;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@ -48,14 +51,6 @@ public class RedisKeyExpirationListener implements MessageListener {
|
||||
handleFunctionAccessExpired(element);
|
||||
}
|
||||
if(expiredKey.endsWith(DEVICE_ONLINE_STATUS_KEY_PREFIX)) {
|
||||
// threadPoolTaskExecutor.execute(() -> {
|
||||
// log.info("设备离线:{}", expiredKey);
|
||||
// String element = expiredKey.substring(GlobalConstants.GLOBAL_REDIS_KEY.length() + DEVICE_KEY_PREFIX.length(), expiredKey.length() - DEVICE_ONLINE_STATUS_KEY_PREFIX.length());
|
||||
// UpdateWrapper<Device> deviceUpdateWrapper = new UpdateWrapper<>();
|
||||
// deviceUpdateWrapper.eq("device_imei", element);
|
||||
// deviceUpdateWrapper.set("online_status", 0);
|
||||
// deviceMapper.update(deviceUpdateWrapper);
|
||||
// });
|
||||
|
||||
threadPoolTaskExecutor.execute(() -> {
|
||||
log.info("设备离线:{}", expiredKey);
|
||||
@ -69,10 +64,14 @@ public class RedisKeyExpirationListener implements MessageListener {
|
||||
|
||||
if (lockInfo != null) {
|
||||
try {
|
||||
UpdateWrapper<Device> deviceUpdateWrapper = new UpdateWrapper<>();
|
||||
deviceUpdateWrapper.eq("device_imei", element);
|
||||
deviceUpdateWrapper.set("online_status", 0);
|
||||
deviceMapper.update(deviceUpdateWrapper);
|
||||
String deviceOnlineStatusRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX+ message + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX ;
|
||||
String deviceOnlineStatusRedis = RedisUtils.getCacheObject(deviceOnlineStatusRedisKey);
|
||||
if(StringUtils.isBlank(deviceOnlineStatusRedis)){
|
||||
UpdateWrapper<Device> deviceUpdateWrapper = new UpdateWrapper<>();
|
||||
deviceUpdateWrapper.eq("device_imei", element);
|
||||
deviceUpdateWrapper.set("online_status", 0);
|
||||
deviceMapper.update(deviceUpdateWrapper);
|
||||
}
|
||||
} finally {
|
||||
//释放锁
|
||||
lockTemplate.releaseLock(lockInfo);
|
||||
|
||||
@ -49,12 +49,12 @@ public class ReceiverMessageHandler implements MessageHandler {
|
||||
String[] subStr = receivedTopic.split("/");
|
||||
String deviceImei = subStr[1];
|
||||
if(StringUtils.isNotBlank(deviceImei)){
|
||||
String queueKey = MqttMessageQueueConstants.MQTT_MESSAGE_QUEUE_KEY;
|
||||
String dedupKey = MqttMessageQueueConstants.MQTT_MESSAGE_DEDUP_KEY;
|
||||
RedisUtils.offerDeduplicated(queueKey,dedupKey,deviceImei, Duration.ofHours(24));
|
||||
//在线状态
|
||||
String deviceOnlineStatusRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX+ deviceImei + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX ;
|
||||
RedisUtils.setCacheObject(deviceOnlineStatusRedisKey, "1", Duration.ofSeconds(62));
|
||||
// String queueKey = MqttMessageQueueConstants.MQTT_MESSAGE_QUEUE_KEY;
|
||||
// String dedupKey = MqttMessageQueueConstants.MQTT_MESSAGE_DEDUP_KEY;
|
||||
// RedisUtils.offerDeduplicated(queueKey,dedupKey,deviceImei, Duration.ofHours(24));
|
||||
}
|
||||
|
||||
String state = payloadDict.getStr("state");
|
||||
|
||||
@ -1,8 +1,18 @@
|
||||
package com.fuyuanshen.global.mqtt.rule.bjq;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.fuyuanshen.common.core.constant.GlobalConstants;
|
||||
import com.fuyuanshen.common.core.domain.model.LoginUser;
|
||||
import com.fuyuanshen.common.core.utils.StringUtils;
|
||||
import com.fuyuanshen.common.core.utils.date.DurationUtils;
|
||||
import com.fuyuanshen.common.redis.utils.RedisUtils;
|
||||
import com.fuyuanshen.common.satoken.utils.LoginHelper;
|
||||
import com.fuyuanshen.equipment.domain.Device;
|
||||
import com.fuyuanshen.equipment.domain.bo.DeviceAlarmBo;
|
||||
import com.fuyuanshen.equipment.domain.vo.DeviceAlarmVo;
|
||||
import com.fuyuanshen.equipment.service.DeviceService;
|
||||
import com.fuyuanshen.equipment.service.IDeviceAlarmService;
|
||||
import com.fuyuanshen.global.mqtt.base.MqttMessageRule;
|
||||
import com.fuyuanshen.global.mqtt.base.MqttRuleContext;
|
||||
import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants;
|
||||
@ -13,9 +23,11 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
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.*;
|
||||
|
||||
/**
|
||||
@ -26,6 +38,10 @@ import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.*;
|
||||
@Slf4j
|
||||
public class BjqAlarmRule implements MqttMessageRule {
|
||||
|
||||
private final IDeviceAlarmService deviceAlarmService;
|
||||
private final DeviceService deviceService;
|
||||
|
||||
|
||||
@Override
|
||||
public String getCommandType() {
|
||||
return LightingCommandTypeConstants.ALARM_MESSAGE;
|
||||
@ -38,14 +54,21 @@ public class BjqAlarmRule implements MqttMessageRule {
|
||||
Object[] convertArr = context.getConvertArr();
|
||||
|
||||
String convertValue = convertArr[1].toString();
|
||||
if(StringUtils.isNotBlank(convertValue)){
|
||||
if (StringUtils.isNotBlank(convertValue)) {
|
||||
// 将设备状态信息存储到Redis中
|
||||
String deviceRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+DeviceRedisKeyConstants.DEVICE_KEY_PREFIX + context.getDeviceImei() + DEVICE_ALARM_KEY_PREFIX;
|
||||
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));
|
||||
|
||||
// 保存告警信息
|
||||
String deviceImei = context.getDeviceImei();
|
||||
// 设备告警状态 0:解除告警 1:报警产生
|
||||
Byte state = (Byte) convertArr[1];
|
||||
savaAlarm(deviceImei, state);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("处理告警命令时出错", e);
|
||||
RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.FAILED.getCode(), Duration.ofSeconds(20));
|
||||
@ -53,4 +76,51 @@ public class BjqAlarmRule implements MqttMessageRule {
|
||||
}
|
||||
|
||||
|
||||
public void savaAlarm(String deviceImei, Byte state) {
|
||||
DeviceAlarmVo deviceAlarmVo = deviceAlarmService.queryLatestByDeviceImei(deviceImei);
|
||||
DeviceAlarmBo deviceAlarmBo = new DeviceAlarmBo();
|
||||
|
||||
// 解除告警
|
||||
if (state == 0) {
|
||||
if (deviceAlarmVo != null) {
|
||||
if (deviceAlarmVo.getFinishTime() == null) {
|
||||
BeanUtil.copyProperties(deviceAlarmVo, deviceAlarmBo);
|
||||
deviceAlarmBo.setFinishTime(new Date());
|
||||
String durationBetween = DurationUtils.getDurationBetween(deviceAlarmVo.getStartTime(), deviceAlarmBo.getFinishTime());
|
||||
deviceAlarmBo.setDurationTime(durationBetween);
|
||||
// 0已处理,1未处理
|
||||
deviceAlarmBo.setTreatmentState(0);
|
||||
deviceAlarmService.updateByBo(deviceAlarmBo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 报警产生
|
||||
if (state == 1) {
|
||||
if (deviceAlarmVo == null || deviceAlarmVo.getFinishTime() != null) {
|
||||
|
||||
Device device = deviceService.selectDeviceByImei(deviceImei);
|
||||
deviceAlarmBo.setDeviceId(device.getId());
|
||||
deviceAlarmBo.setDeviceImei(deviceImei);
|
||||
// 0-强制报警,1-撞击闯入,2-自动报警,3-电子围栏告警
|
||||
deviceAlarmBo.setDeviceAction(0);
|
||||
deviceAlarmBo.setStartTime(new Date());
|
||||
// 0已处理,1未处理
|
||||
deviceAlarmBo.setTreatmentState(1);
|
||||
|
||||
// LoginUser loginUser = LoginHelper.getLoginUser();
|
||||
// deviceAlarmBo.setCreateBy(loginUser.getUserId());
|
||||
deviceAlarmBo.setTenantId(device.getTenantId());
|
||||
|
||||
String location = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + deviceImei + DEVICE_LOCATION_KEY_PREFIX);
|
||||
if (StringUtils.isNotBlank(location)) {
|
||||
JSONObject jsonObject = JSONObject.parseObject(location);
|
||||
deviceAlarmBo.setLocation(jsonObject.getString("address"));
|
||||
}
|
||||
deviceAlarmService.insertByBo(deviceAlarmBo);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -54,7 +54,7 @@ public class XinghanBootLogoRule implements MqttMessageRule {
|
||||
|
||||
@Override
|
||||
public void execute(MqttRuleContext ctx) {
|
||||
final String functionAccessKey = FUNCTION_ACCESS_KEY + ctx.getDeviceImei();
|
||||
final String functionAccessKey = FUNCTION_ACCESS_KEY + "LOGO:" + ctx.getDeviceImei();
|
||||
try {
|
||||
MqttXinghanLogoJson payload = objectMapper.convertValue(
|
||||
ctx.getPayloadDict(), MqttXinghanLogoJson.class);
|
||||
|
||||
@ -60,7 +60,7 @@ public class XinghanDeviceDataRule implements MqttMessageRule {
|
||||
|
||||
@Override
|
||||
public void execute(MqttRuleContext context) {
|
||||
String functionAccess = FUNCTION_ACCESS_KEY + context.getDeviceImei();
|
||||
String functionAccess = FUNCTION_ACCESS_KEY + "DATA:" + context.getDeviceImei();
|
||||
try {
|
||||
// Latitude, longitude
|
||||
//主灯档位,激光灯档位,电量百分比,充电状态,电池剩余续航时间
|
||||
|
||||
@ -48,7 +48,7 @@ public class XinghanSendAlarmMessageRule implements MqttMessageRule {
|
||||
|
||||
@Override
|
||||
public void execute(MqttRuleContext ctx) {
|
||||
String functionAccess = FUNCTION_ACCESS_KEY + ctx.getDeviceImei();
|
||||
String functionAccess = FUNCTION_ACCESS_KEY + "ALARM:" + ctx.getDeviceImei();
|
||||
try {
|
||||
XinghanSendAlarmMessageRule.MqttXinghanAlarmMsgJson payload = objectMapper.convertValue(
|
||||
ctx.getPayloadDict(), XinghanSendAlarmMessageRule.MqttXinghanAlarmMsgJson.class);
|
||||
|
||||
@ -47,7 +47,7 @@ public class XinghanSendMsgRule implements MqttMessageRule {
|
||||
|
||||
@Override
|
||||
public void execute(MqttRuleContext ctx) {
|
||||
String functionAccess = FUNCTION_ACCESS_KEY + ctx.getDeviceImei();
|
||||
String functionAccess = FUNCTION_ACCESS_KEY + "MSG:" + ctx.getDeviceImei();
|
||||
try {
|
||||
XinghanSendMsgRule.MqttXinghanMsgJson payload = objectMapper.convertValue(
|
||||
ctx.getPayloadDict(), XinghanSendMsgRule.MqttXinghanMsgJson.class);
|
||||
|
||||
@ -1,9 +1,13 @@
|
||||
package com.fuyuanshen.global.queue;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||
import com.fuyuanshen.common.core.constant.GlobalConstants;
|
||||
import com.fuyuanshen.common.core.utils.StringUtils;
|
||||
import com.fuyuanshen.common.redis.utils.RedisUtils;
|
||||
import com.fuyuanshen.equipment.domain.Device;
|
||||
import com.fuyuanshen.equipment.mapper.DeviceMapper;
|
||||
import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.annotation.PreDestroy;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@ -15,6 +19,8 @@ import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class MqttMessageConsumer {
|
||||
@ -27,7 +33,7 @@ public class MqttMessageConsumer {
|
||||
private ExecutorService messageProcessorPool = Executors.newFixedThreadPool(10);
|
||||
|
||||
// 初始化方法,启动消息监听
|
||||
// @PostConstruct
|
||||
@PostConstruct
|
||||
public void start() {
|
||||
log.info("启动MQTT消息消费者...");
|
||||
// 启动消息获取线程
|
||||
@ -62,7 +68,7 @@ public class MqttMessageConsumer {
|
||||
public void consumeMessages() {
|
||||
String queueKey = MqttMessageQueueConstants.MQTT_MESSAGE_QUEUE_KEY;
|
||||
String threadName = Thread.currentThread().getName();
|
||||
log.info("消息消费者线程 {} 开始监听队列: {}", threadName, queueKey);
|
||||
// log.info("消息消费者线程 {} 开始监听队列: {}", threadName, queueKey);
|
||||
|
||||
try {
|
||||
while (!Thread.currentThread().isInterrupted() && !messageConsumerPool.isShutdown()) {
|
||||
@ -75,7 +81,7 @@ public class MqttMessageConsumer {
|
||||
);
|
||||
|
||||
if (message != null) {
|
||||
log.info("线程 {} 从队列中获取到消息,提交到处理线程池: {}", threadName, message);
|
||||
// log.info("线程 {} 从队列中获取到消息,提交到处理线程池: {}", threadName, message);
|
||||
// 将消息处理任务提交到处理线程池
|
||||
messageProcessorPool.submit(() -> processMessage(message));
|
||||
}
|
||||
@ -92,15 +98,23 @@ public class MqttMessageConsumer {
|
||||
String threadName = Thread.currentThread().getName();
|
||||
try {
|
||||
log.info("业务处理线程 {} 开始处理消息: {}", threadName, message);
|
||||
|
||||
// 实现具体的业务逻辑
|
||||
// 例如更新数据库、发送通知等
|
||||
UpdateWrapper<Device> updateWrapper = new UpdateWrapper<>();
|
||||
updateWrapper.eq("device_imei", message)
|
||||
.set("online_status", 1);
|
||||
deviceMapper.update(updateWrapper);
|
||||
String deviceOnlineStatusRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX+ message + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX ;
|
||||
String deviceOnlineStatusRedis = RedisUtils.getCacheObject(deviceOnlineStatusRedisKey);
|
||||
if(StringUtils.isBlank(deviceOnlineStatusRedis)){
|
||||
UpdateWrapper<Device> updateWrapper = new UpdateWrapper<>();
|
||||
updateWrapper.eq("device_imei", message)
|
||||
.set("online_status", 1);
|
||||
deviceMapper.update(updateWrapper);
|
||||
}
|
||||
// QueryWrapper<Device> queryWrapper = new QueryWrapper<>();
|
||||
// queryWrapper.eq("device_imei", message);
|
||||
// queryWrapper.eq("online_status", 1);
|
||||
// Long count = deviceMapper.selectCount(queryWrapper);
|
||||
// if(count == 0){
|
||||
//
|
||||
// }
|
||||
// 模拟业务处理耗时
|
||||
Thread.sleep(200);
|
||||
// Thread.sleep(200);
|
||||
|
||||
log.info("业务处理线程 {} 完成消息处理: {}", threadName, message);
|
||||
} catch (Exception e) {
|
||||
|
||||
@ -0,0 +1,160 @@
|
||||
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.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;
|
||||
import com.fuyuanshen.web.domain.vo.DeviceInfoVo;
|
||||
import com.fuyuanshen.web.service.device.DeviceBizService;
|
||||
import com.fuyuanshen.web.service.device.DeviceDebugService;
|
||||
import com.fuyuanshen.web.service.device.DeviceXinghanBizService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 联调中心
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2025-08-28
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("api/device/debug")
|
||||
public class DeviceDebugController extends BaseController {
|
||||
|
||||
private final DeviceBizService appDeviceService;
|
||||
private final DeviceXinghanBizService deviceXinghanBizService;
|
||||
private final DeviceDebugService deviceDebugService;
|
||||
|
||||
/**
|
||||
* 查询设备列表
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<WebDeviceVo> list(DeviceQueryCriteria bo, PageQuery pageQuery) {
|
||||
return appDeviceService.queryWebDeviceList(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*/
|
||||
@Log(title = "批量上传文件")
|
||||
@PostMapping("/addFile")
|
||||
public R<Void> uploadFile(@Validated @ModelAttribute AppFileDto bo) throws IOException {
|
||||
return toAjax(deviceDebugService.addFileHash(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作视频添加
|
||||
*/
|
||||
@Log(title = "批量添加操作视频")
|
||||
@PostMapping("/addVideo")
|
||||
public R<Void> addOperationVideo(@RequestBody AppOperationVideoBo bo) {
|
||||
return toAjax(deviceDebugService.addVideoList(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传设备logo图片
|
||||
*/
|
||||
@Log(title = "批量上传设备logo图片")
|
||||
@PostMapping("/addLogo")
|
||||
@FunctionAccessAnnotation("uploadLogo")
|
||||
public R<Void> uploadLogo670(@Validated @ModelAttribute DeviceDebugLogoUploadDto bo) {
|
||||
|
||||
MultipartFile file = bo.getFile();
|
||||
if(file.getSize()>1024*1024*2){
|
||||
return R.warn("图片不能大于2M");
|
||||
}
|
||||
deviceXinghanBizService.uploadDeviceLogoBatch(bo);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设备详情
|
||||
*/
|
||||
@Operation(summary = "设备详情")
|
||||
@GetMapping(value = "/detail/{id}")
|
||||
public R<DeviceInfoVo> getDeviceInfo(@PathVariable Long id) {
|
||||
return R.ok(deviceDebugService.getDeviceInfo(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改设备联调信息
|
||||
*/
|
||||
@Log(title = "修改设备联调信息")
|
||||
@PostMapping("/editDebug")
|
||||
public R<Void> editDeviceDebug(@Validated @ModelAttribute DeviceDebugEditDto bo) throws Exception {
|
||||
// 1. 基础参数必填校验
|
||||
validateDeviceDebugEdit(bo);
|
||||
|
||||
// 修改上传设备说明
|
||||
if (bo.getExplanationFiles() != null) {
|
||||
AppFileDto appFileDto = new AppFileDto();
|
||||
appFileDto.setDeviceIds(new Long[]{ bo.getDeviceId() });
|
||||
appFileDto.setFileType(1L);
|
||||
appFileDto.setFiles(bo.getExplanationFiles());
|
||||
deviceDebugService.addFileHash(appFileDto);
|
||||
}
|
||||
// 修改上传设备参数
|
||||
if (bo.getParameterFiles() != null) {
|
||||
AppFileDto appFileDto = new AppFileDto();
|
||||
appFileDto.setDeviceIds(new Long[]{ bo.getDeviceId() });
|
||||
appFileDto.setFileType(2L);
|
||||
appFileDto.setFiles(bo.getParameterFiles());
|
||||
deviceDebugService.addFileHash(appFileDto);
|
||||
}
|
||||
// 修改操作视频
|
||||
if (bo.getVideoUrl().isEmpty()) {
|
||||
AppOperationVideoBo appOperationVideoBo = new AppOperationVideoBo();
|
||||
appOperationVideoBo.setDeviceIds(new Long[]{ bo.getDeviceId() });
|
||||
appOperationVideoBo.setVideoUrl(bo.getVideoUrl());
|
||||
deviceDebugService.addVideoList(appOperationVideoBo);
|
||||
}
|
||||
// 修改设备logo 每个型号设备走不同协议无法共用同一个上传
|
||||
// if(bo.getLogoFile() != null){
|
||||
// MultipartFile file = bo.getLogoFile();
|
||||
// if(file.getSize()>1024*1024*2){
|
||||
// return R.warn("图片不能大于2M");
|
||||
// }
|
||||
// AppDeviceLogoUploadDto bo1 = new AppDeviceLogoUploadDto();
|
||||
// bo1.setDeviceId(bo.getDeviceId());
|
||||
// bo1.setDeviceImei(bo.getDeviceImei());
|
||||
// bo1.setFile(file);
|
||||
// deviceXinghanBizService.uploadDeviceLogo(bo1);
|
||||
// }
|
||||
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/* ------------------ 私有复用 ------------------ */
|
||||
|
||||
private void validateDeviceDebugEdit(DeviceDebugEditDto bo) {
|
||||
if (bo.getDeviceId() == null || bo.getDeviceId() == 0L) {
|
||||
throw new ServiceException("请选择设备");
|
||||
}
|
||||
// if (bo.getDeviceImei().isEmpty()) {
|
||||
// throw new ServiceException("设备 IMEI 不能为空");
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
@ -67,7 +67,7 @@ public class DeviceShareController extends BaseController {
|
||||
@RepeatSubmit()
|
||||
@PostMapping("/permission")
|
||||
public R<Void> permission(@Validated(AddGroup.class) @RequestBody AppDeviceShareBo bo) {
|
||||
return toAjax(deviceShareService.deviceShare(bo));
|
||||
return toAjax(deviceShareService.permission(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -0,0 +1,121 @@
|
||||
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.domain.vo.DeviceXinghanDetailVo;
|
||||
import com.fuyuanshen.web.service.device.DeviceXinghanBizService;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 设备控制类 HBY670
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/api/xinghan/device")
|
||||
public class DeviceXinghanController extends BaseController {
|
||||
|
||||
private final DeviceXinghanBizService deviceXinghanBizService;
|
||||
|
||||
|
||||
/**
|
||||
* 获取设备详细信息
|
||||
*
|
||||
* @param id 主键
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
public R<DeviceXinghanDetailVo> getInfo(@NotNull(message = "主键不能为空")
|
||||
@PathVariable Long id) {
|
||||
return R.ok(deviceXinghanBizService.getInfo(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 人员信息登记
|
||||
*/
|
||||
@PostMapping(value = "/registerPersonInfo")
|
||||
// @FunctionAccessAnnotation("registerPersonInfo")
|
||||
public R<Void> registerPersonInfo(@Validated(AddGroup.class) @RequestBody AppPersonnelInfoBo bo) {
|
||||
return toAjax(deviceXinghanBizService.registerPersonInfo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送紧急通知
|
||||
*/
|
||||
@PostMapping(value = "/sendAlarmMessage")
|
||||
@FunctionAccessBatcAnnotation(value = "sendAlarmMessage", timeOut = 5, batchMaxTimeOut = 10)
|
||||
public R<Void> sendAlarmMessage(@RequestBody AppDeviceSendMsgBo bo) {
|
||||
return toAjax(deviceXinghanBizService.sendAlarmMessage(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传设备logo图片
|
||||
*/
|
||||
@PostMapping("/uploadLogo")
|
||||
@FunctionAccessAnnotation("uploadLogo")
|
||||
public R<Void> upload(@Validated @ModelAttribute AppDeviceLogoUploadDto bo) {
|
||||
|
||||
MultipartFile file = bo.getFile();
|
||||
if(file.getSize()>1024*1024*2){
|
||||
return R.warn("图片不能大于2M");
|
||||
}
|
||||
deviceXinghanBizService.uploadDeviceLogo(bo);
|
||||
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 静电预警档位
|
||||
* 3,2,1,0,分别表示高档/中档/低挡/关闭
|
||||
*/
|
||||
@PostMapping("/DetectGradeSettings")
|
||||
public R<Void> DetectGradeSettings(@RequestBody DeviceInstructDto params) {
|
||||
// params 转 JSONObject
|
||||
deviceXinghanBizService.upDetectGradeSettings(params);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 照明档位
|
||||
* 照明档位,2,1,0,分别表示弱光/强光/关闭
|
||||
*/
|
||||
@PostMapping("/LightGradeSettings")
|
||||
public R<Void> LightGradeSettings(@RequestBody DeviceInstructDto params) {
|
||||
// params 转 JSONObject
|
||||
deviceXinghanBizService.upLightGradeSettings(params);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* SOS档位
|
||||
* SOS档位,2,1,0, 分别表示红蓝模式/爆闪模式/关闭
|
||||
*/
|
||||
@PostMapping("/SOSGradeSettings")
|
||||
public R<Void> SOSGradeSettings(@RequestBody DeviceInstructDto params) {
|
||||
// params 转 JSONObject
|
||||
deviceXinghanBizService.upSOSGradeSettings(params);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 静止报警状态
|
||||
* 静止报警状态,0-未静止报警,1-正在静止报警。
|
||||
*/
|
||||
@PostMapping("/ShakeBitSettings")
|
||||
public R<Void> ShakeBitSettings(@RequestBody DeviceInstructDto params) {
|
||||
// params 转 JSONObject
|
||||
deviceXinghanBizService.upShakeBitSettings(params);
|
||||
return R.ok();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,110 @@
|
||||
package com.fuyuanshen.web.controller.device.fence;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.fuyuanshen.equipment.domain.bo.DeviceFenceAccessRecordBo;
|
||||
import com.fuyuanshen.equipment.domain.vo.DeviceFenceAccessRecordVo;
|
||||
import com.fuyuanshen.equipment.service.IDeviceFenceAccessRecordService;
|
||||
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.common.mybatis.core.page.TableDataInfo;
|
||||
|
||||
/**
|
||||
* 围栏进出记录
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2025-09-11
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/api/equipment/fenceAccessRecord")
|
||||
public class DeviceFenceAccessRecordController extends BaseController {
|
||||
|
||||
private final IDeviceFenceAccessRecordService deviceFenceAccessRecordService;
|
||||
|
||||
|
||||
/**
|
||||
* 查询围栏进出记录列表
|
||||
*/
|
||||
@SaCheckPermission("fys-equipment:fenceAccessRecord:list")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<DeviceFenceAccessRecordVo> list(DeviceFenceAccessRecordBo bo, PageQuery pageQuery) {
|
||||
return deviceFenceAccessRecordService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 导出围栏进出记录列表
|
||||
*/
|
||||
@SaCheckPermission("fys-equipment:fenceAccessRecord:export")
|
||||
@Log(title = "导出围栏进出记录列表", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(DeviceFenceAccessRecordBo bo, HttpServletResponse response) {
|
||||
List<DeviceFenceAccessRecordVo> list = deviceFenceAccessRecordService.queryList(bo);
|
||||
ExcelUtil.exportExcel(list, "围栏进出记录", DeviceFenceAccessRecordVo.class, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取围栏进出记录详细信息
|
||||
*
|
||||
* @param id 主键
|
||||
*/
|
||||
@SaCheckPermission("fys-equipment:fenceAccessRecord:query")
|
||||
@GetMapping("/{id}")
|
||||
public R<DeviceFenceAccessRecordVo> getInfo(@NotNull(message = "主键不能为空")
|
||||
@PathVariable Long id) {
|
||||
return R.ok(deviceFenceAccessRecordService.queryById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增围栏进出记录
|
||||
*/
|
||||
@SaCheckPermission("fys-equipment:fenceAccessRecord:add")
|
||||
@Log(title = "围栏进出记录", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping()
|
||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody DeviceFenceAccessRecordBo bo) {
|
||||
return toAjax(deviceFenceAccessRecordService.insertByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改围栏进出记录
|
||||
*/
|
||||
@SaCheckPermission("fys-equipment:fenceAccessRecord:edit")
|
||||
@Log(title = "围栏进出记录", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping()
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody DeviceFenceAccessRecordBo bo) {
|
||||
return toAjax(deviceFenceAccessRecordService.updateByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除围栏进出记录
|
||||
*
|
||||
* @param ids 主键串
|
||||
*/
|
||||
@SaCheckPermission("fys-equipment:fenceAccessRecord:remove")
|
||||
@Log(title = "围栏进出记录", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
||||
@PathVariable Long[] ids) {
|
||||
return toAjax(deviceFenceAccessRecordService.deleteWithValidByIds(List.of(ids), true));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,126 @@
|
||||
package com.fuyuanshen.web.controller.device.fence;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.fuyuanshen.equipment.domain.bo.DeviceGeoFenceBo;
|
||||
import com.fuyuanshen.equipment.domain.dto.FenceCheckResponse;
|
||||
import com.fuyuanshen.equipment.domain.query.FenceCheckRequest;
|
||||
import com.fuyuanshen.equipment.domain.vo.DeviceGeoFenceVo;
|
||||
import com.fuyuanshen.equipment.service.IDeviceGeoFenceService;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.*;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
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.common.mybatis.core.page.TableDataInfo;
|
||||
|
||||
/**
|
||||
* 电子围栏
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2025-09-11
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/api/equipment/geoFence")
|
||||
public class DeviceGeoFenceController extends BaseController {
|
||||
|
||||
private final IDeviceGeoFenceService deviceGeoFenceService;
|
||||
|
||||
|
||||
/**
|
||||
* 查询电子围栏列表
|
||||
*/
|
||||
@SaCheckPermission("fys-equipment:geoFence:list")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<DeviceGeoFenceVo> list(DeviceGeoFenceBo bo, PageQuery pageQuery) {
|
||||
return deviceGeoFenceService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 导出电子围栏列表
|
||||
*/
|
||||
@SaCheckPermission("fys-equipment:geoFence:export")
|
||||
@Log(title = "导出电子围栏列表", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(DeviceGeoFenceBo bo, HttpServletResponse response) {
|
||||
List<DeviceGeoFenceVo> list = deviceGeoFenceService.queryList(bo);
|
||||
ExcelUtil.exportExcel(list, "电子围栏", DeviceGeoFenceVo.class, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取电子围栏详细信息
|
||||
*
|
||||
* @param id 主键
|
||||
*/
|
||||
@SaCheckPermission("fys-equipment:geoFence:query")
|
||||
@GetMapping("/{id}")
|
||||
public R<DeviceGeoFenceVo> getInfo(@NotNull(message = "主键不能为空")
|
||||
@PathVariable Long id) {
|
||||
return R.ok(deviceGeoFenceService.queryById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增电子围栏
|
||||
*/
|
||||
@SaCheckPermission("fys-equipment:geoFence:add")
|
||||
@Log(title = "电子围栏", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping()
|
||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody DeviceGeoFenceBo bo) {
|
||||
return toAjax(deviceGeoFenceService.insertByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改电子围栏
|
||||
*/
|
||||
@SaCheckPermission("fys-equipment:geoFence:edit")
|
||||
@Log(title = "电子围栏", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping()
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody DeviceGeoFenceBo bo) {
|
||||
return toAjax(deviceGeoFenceService.updateByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除电子围栏
|
||||
*
|
||||
* @param ids 主键串
|
||||
*/
|
||||
@SaCheckPermission("fys-equipment:geoFence:remove")
|
||||
@Log(title = "电子围栏", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
||||
@PathVariable Long[] ids) {
|
||||
return toAjax(deviceGeoFenceService.deleteWithValidByIds(List.of(ids), true));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 电子围栏-位置检查
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/check")
|
||||
public ResponseEntity<FenceCheckResponse> checkPosition(
|
||||
@Valid @RequestBody FenceCheckRequest request) {
|
||||
FenceCheckResponse response = deviceGeoFenceService.checkPosition(request);
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
package com.fuyuanshen.web.domain.Dto;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class DeviceDebugEditDto {
|
||||
/**
|
||||
* 设备主键列表
|
||||
*/
|
||||
private Long deviceId;
|
||||
/**
|
||||
* 设备 IMEI
|
||||
*/
|
||||
//private String deviceImei;
|
||||
/**
|
||||
* 上传 logo 图片
|
||||
*/
|
||||
//private MultipartFile LogoFile; // 同一张图
|
||||
/**
|
||||
* 参数文件
|
||||
*/
|
||||
private MultipartFile[] parameterFiles;
|
||||
/**
|
||||
* 说明文件
|
||||
*/
|
||||
private MultipartFile[] explanationFiles;
|
||||
/**
|
||||
* 视频链接
|
||||
*/
|
||||
private String videoUrl;
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
package com.fuyuanshen.web.domain.Dto;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class DeviceDebugLogoUploadDto {
|
||||
/**
|
||||
* 设备主键列表
|
||||
*/
|
||||
private List<Long> deviceIds; // 设备主键列表
|
||||
/**
|
||||
* 上传 图片
|
||||
*/
|
||||
private MultipartFile file; // 同一张图
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
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 lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 设备信息 视图对象
|
||||
*
|
||||
* @author Michelle.Chung
|
||||
*/
|
||||
@Data
|
||||
public class DeviceInfoVo {
|
||||
/**
|
||||
* 设备业务文件
|
||||
*/
|
||||
private List<AppFileVo> appBusinessFileVoList;
|
||||
/**
|
||||
* 设备操作视频
|
||||
*/
|
||||
private List<AppOperationVideoVo> appOperationVideoVoList;
|
||||
}
|
||||
@ -0,0 +1,103 @@
|
||||
package com.fuyuanshen.web.domain.vo;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fuyuanshen.app.domain.vo.AppPersonnelInfoVo;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class DeviceXinghanDetailVo {
|
||||
/**
|
||||
* 设备ID
|
||||
*/
|
||||
@ExcelProperty(value = "设备ID")
|
||||
private Long deviceId;
|
||||
|
||||
|
||||
/**
|
||||
* 设备名称
|
||||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
* 设备状态
|
||||
* 0 失效
|
||||
* 1 正常
|
||||
*/
|
||||
private Integer deviceStatus;
|
||||
|
||||
|
||||
/**
|
||||
* 人员信息
|
||||
*/
|
||||
private AppPersonnelInfoVo personnelInfo;
|
||||
|
||||
/**
|
||||
* 在线状态(0离线,1在线)
|
||||
*/
|
||||
private Integer onlineStatus;
|
||||
|
||||
// 经度
|
||||
private String longitude;
|
||||
|
||||
// 纬度
|
||||
private String latitude;
|
||||
|
||||
// 逆解析地址
|
||||
private String address;
|
||||
|
||||
/**
|
||||
* 第一键值对,静电预警档位:3,2,1,0,分别表示高档/中档/低挡/关闭.
|
||||
*/
|
||||
private Integer staDetectGrade;
|
||||
/**
|
||||
* 第二键值对,照明档位,2,1,0,分别表示弱光/强光/关闭
|
||||
*/
|
||||
private Integer staLightGrade;
|
||||
/**
|
||||
* 第三键值对,SOS档位,2,1,0, 分别表示红蓝模式/爆闪模式/关闭
|
||||
*/
|
||||
public Integer staSOSGrade;
|
||||
/**
|
||||
* 第四键值对,剩余照明时间,0-5999,单位分钟。
|
||||
*/
|
||||
public Integer staPowerTime;
|
||||
/**
|
||||
* 第五键值对,剩余电量百分比,0-100
|
||||
*/
|
||||
public Integer staPowerPercent;
|
||||
/**
|
||||
* 第六键值对, 近电预警级别, 0-无预警,1-弱预警,2-中预警,3-强预警,4-非常强预警。
|
||||
*/
|
||||
public Integer staDetectResult;
|
||||
}
|
||||
@ -0,0 +1,85 @@
|
||||
// package com.fuyuanshen.web.handler.mqtt;
|
||||
//
|
||||
// import cn.hutool.core.lang.Dict;
|
||||
// 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.MqttRuleContext;
|
||||
// import com.fuyuanshen.global.mqtt.base.MqttRuleEngine;
|
||||
// import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants;
|
||||
// import lombok.extern.slf4j.Slf4j;
|
||||
// import org.apache.commons.lang3.StringUtils;
|
||||
// import org.springframework.beans.factory.annotation.Autowired;
|
||||
// import org.springframework.messaging.Message;
|
||||
// import org.springframework.messaging.MessageHandler;
|
||||
// import org.springframework.messaging.MessageHeaders;
|
||||
// import org.springframework.messaging.MessagingException;
|
||||
// import org.springframework.stereotype.Service;
|
||||
//
|
||||
// import java.time.Duration;
|
||||
// import java.util.Objects;
|
||||
//
|
||||
// import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX;
|
||||
//
|
||||
// /**
|
||||
// * @author: 默苍璃
|
||||
// * @date: 2025-09-1609:28
|
||||
// */
|
||||
// @Service
|
||||
// @Slf4j
|
||||
// public class DeviceAlrmMessageHandler implements MessageHandler {
|
||||
//
|
||||
// @Autowired
|
||||
// private MqttRuleEngine ruleEngine;
|
||||
//
|
||||
// @Override
|
||||
// public void handleMessage(Message<?> message) throws MessagingException {
|
||||
//
|
||||
// // 处理新类型的消息
|
||||
// Object payload = message.getPayload();
|
||||
// MessageHeaders headers = message.getHeaders();
|
||||
// String receivedTopic = Objects.requireNonNull(headers.get("mqtt_receivedTopic")).toString();
|
||||
//
|
||||
// log.info("设备强制报警消息处理器 - MQTT payload= {} \n receivedTopic = {}", payload, receivedTopic);
|
||||
//
|
||||
// // 解析消息并执行相应逻辑
|
||||
// Dict payloadDict = JsonUtils.parseMap(payload.toString());
|
||||
// if (payloadDict != null) {
|
||||
// // 根据主题或消息内容执行不同的处理逻辑
|
||||
// processMessage(receivedTopic, payloadDict);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private void processMessage(String topic, Dict payloadDict) {
|
||||
// // 实现具体的业务逻辑
|
||||
// // 可以根据不同的主题执行不同的操作
|
||||
// if (topic.contains("newTopic")) {
|
||||
// // 处理特定主题的消息
|
||||
// handleNewTopicMessage(payloadDict);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private void handleNewTopicMessage(Dict payloadDict) {
|
||||
// // 实现新主题消息的具体处理逻辑
|
||||
// String deviceImei = payloadDict.getStr("imei");
|
||||
// if (StringUtils.isNotBlank(deviceImei)) {
|
||||
// // 更新设备状态到Redis
|
||||
// String deviceOnlineStatusRedisKey = GlobalConstants.GLOBAL_REDIS_KEY +
|
||||
// DEVICE_KEY_PREFIX + deviceImei + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX;
|
||||
// RedisUtils.setCacheObject(deviceOnlineStatusRedisKey, "1", Duration.ofSeconds(62));
|
||||
//
|
||||
// // 执行规则引擎
|
||||
// MqttRuleContext context = new MqttRuleContext();
|
||||
// context.setDeviceImei(deviceImei);
|
||||
// context.setPayloadDict(payloadDict);
|
||||
// // 设置命令类型
|
||||
// context.setCommandType((byte) 0x02); // 根据实际需要设置命令类型
|
||||
//
|
||||
// boolean ruleExecuted = ruleEngine.executeRule(context);
|
||||
// if (!ruleExecuted) {
|
||||
// log.warn("未找到匹配的规则来处理新主题消息,设备IMEI: {}", deviceImei);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
@ -212,6 +212,46 @@ public class DeviceShareService {
|
||||
LambdaQueryWrapper<AppDeviceBindRecord> qw = new LambdaQueryWrapper<>();
|
||||
qw.eq(AppDeviceBindRecord::getDeviceId, bo.getDeviceId());
|
||||
AppDeviceBindRecord bindRecord = appDeviceBindRecordMapper.selectOne(qw);
|
||||
if (bindRecord == null) {
|
||||
throw new ServiceException("设备未绑定");
|
||||
}
|
||||
Long userId = bindRecord.getBindingUserId();
|
||||
|
||||
LambdaQueryWrapper<AppDeviceShare> lqw = new LambdaQueryWrapper<>();
|
||||
lqw.eq(AppDeviceShare::getDeviceId, bo.getDeviceId());
|
||||
lqw.eq(AppDeviceShare::getPhonenumber, bo.getPhonenumber());
|
||||
Long count = appDeviceShareMapper.selectCount(lqw);
|
||||
if (count > 0) {
|
||||
|
||||
UpdateWrapper<AppDeviceShare> 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 permission(AppDeviceShareBo bo) {
|
||||
Device device = deviceMapper.selectById(bo.getDeviceId());
|
||||
if (device == null) {
|
||||
throw new ServiceException("设备不存在");
|
||||
}
|
||||
LambdaQueryWrapper<AppDeviceBindRecord> qw = new LambdaQueryWrapper<>();
|
||||
qw.eq(AppDeviceBindRecord::getDeviceId, bo.getDeviceId());
|
||||
AppDeviceBindRecord bindRecord = appDeviceBindRecordMapper.selectOne(qw);
|
||||
if (bindRecord == null) {
|
||||
throw new ServiceException("设备未绑定");
|
||||
}
|
||||
Long userId = bindRecord.getBindingUserId();
|
||||
|
||||
LambdaQueryWrapper<AppDeviceShare> lqw = new LambdaQueryWrapper<>();
|
||||
|
||||
@ -36,6 +36,7 @@ import com.fuyuanshen.equipment.mapper.DeviceMapper;
|
||||
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 com.google.common.primitives.Ints;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -126,7 +127,7 @@ public class DeviceBizService {
|
||||
List<WebDeviceVo> records = result.getRecords();
|
||||
if(records != null && !records.isEmpty()){
|
||||
records.forEach(item -> {
|
||||
if(item.getCommunicationMode()!=null && item.getCommunicationMode() == 0){
|
||||
if(item.getCommunicationMode()!=null && Ints.asList(0, 2).contains(item.getCommunicationMode())){
|
||||
|
||||
//设备在线状态
|
||||
String onlineStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX+ item.getDeviceImei() + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX);
|
||||
@ -181,12 +182,14 @@ public class DeviceBizService {
|
||||
|
||||
QueryWrapper<AppDeviceBindRecord> bindRecordQueryWrapper = new QueryWrapper<>();
|
||||
bindRecordQueryWrapper.eq("device_id", device.getId());
|
||||
bindRecordQueryWrapper.eq("communication_mode", 0);
|
||||
AppDeviceBindRecord appDeviceBindRecord = appDeviceBindRecordMapper.selectOne(bindRecordQueryWrapper);
|
||||
if (appDeviceBindRecord != null) {
|
||||
UpdateWrapper<AppDeviceBindRecord> deviceUpdateWrapper = new UpdateWrapper<>();
|
||||
deviceUpdateWrapper.eq("device_id", device.getId())
|
||||
.set("binding_status", BindingStatusEnum.BOUND.getCode())
|
||||
.set("binding_user_id", userId)
|
||||
.set("communication_mode", 0)
|
||||
.set("update_time", new Date())
|
||||
.set("binding_time", new Date());
|
||||
return appDeviceBindRecordMapper.update(null, deviceUpdateWrapper);
|
||||
@ -195,6 +198,7 @@ public class DeviceBizService {
|
||||
bindRecord.setDeviceId(device.getId());
|
||||
bindRecord.setBindingUserId(userId);
|
||||
bindRecord.setBindingTime(new Date());
|
||||
bindRecord.setCommunicationMode(0);
|
||||
bindRecord.setCreateBy(userId);
|
||||
appDeviceBindRecordMapper.insert(bindRecord);
|
||||
}
|
||||
@ -224,6 +228,7 @@ public class DeviceBizService {
|
||||
deviceUpdateWrapper.eq("device_id", device.getId())
|
||||
.eq("binding_user_id", userId)
|
||||
.set("binding_user_id", userId)
|
||||
.set("communication_mode", 1)
|
||||
.set("binding_time", new Date());
|
||||
return appDeviceBindRecordMapper.update(null, deviceUpdateWrapper);
|
||||
} else {
|
||||
@ -231,6 +236,7 @@ public class DeviceBizService {
|
||||
bindRecord.setDeviceId(device.getId());
|
||||
bindRecord.setBindingUserId(userId);
|
||||
bindRecord.setBindingTime(new Date());
|
||||
bindRecord.setCommunicationMode(1);
|
||||
bindRecord.setCreateBy(userId);
|
||||
appDeviceBindRecordMapper.insert(bindRecord);
|
||||
}
|
||||
|
||||
@ -0,0 +1,150 @@
|
||||
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.app.domain.dto.AppFileDto;
|
||||
import com.fuyuanshen.app.service.IAppBusinessFileService;
|
||||
import com.fuyuanshen.app.service.IAppOperationVideoService;
|
||||
import com.fuyuanshen.common.core.exception.ServiceException;
|
||||
import com.fuyuanshen.common.satoken.utils.AppLoginHelper;
|
||||
import com.fuyuanshen.equipment.mapper.DeviceMapper;
|
||||
import com.fuyuanshen.equipment.service.DeviceService;
|
||||
import com.fuyuanshen.system.domain.vo.SysOssVo;
|
||||
import com.fuyuanshen.system.service.ISysOssService;
|
||||
import com.fuyuanshen.web.domain.vo.DeviceInfoVo;
|
||||
import com.fuyuanshen.web.util.FileHashUtil;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 设备调试服务
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class DeviceDebugService {
|
||||
private final ISysOssService sysOssService;
|
||||
|
||||
private final IAppBusinessFileService appBusinessFileService;
|
||||
private final IAppOperationVideoService appOperationVideoService;
|
||||
private final DeviceService deviceService;
|
||||
|
||||
/**
|
||||
* 文件上传并添加文件信息哈希去重
|
||||
* @param bo
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean addFileHash(AppFileDto bo) throws IOException {
|
||||
MultipartFile[] files = bo.getFiles();
|
||||
if (files == null || files.length == 0) {
|
||||
throw new ServiceException("请选择要上传的文件");
|
||||
}
|
||||
if (files.length > 5) {
|
||||
throw new ServiceException("最多只能上传5个文件");
|
||||
}
|
||||
if (bo.getDeviceIds() == null || bo.getDeviceIds().length == 0) {
|
||||
throw new ServiceException("请选择你要上传的设备");
|
||||
}
|
||||
|
||||
Map<String, Long> hash2OssId = new LinkedHashMap<>(files.length);
|
||||
for (MultipartFile file : files) {
|
||||
// 1. 计算文件哈希
|
||||
String hash = FileHashUtil.hash(file);
|
||||
|
||||
// 2. 先根据 hash 查库(秒传)
|
||||
SysOssVo exist = sysOssService.selectByHash(hash);
|
||||
Long ossId;
|
||||
if (exist != null) {
|
||||
// 2.1 已存在,直接复用
|
||||
ossId = exist.getOssId();
|
||||
hash2OssId.putIfAbsent(hash, ossId);
|
||||
} else {
|
||||
// 2.2 不存在,真正上传
|
||||
SysOssVo upload = sysOssService.upload(file);
|
||||
if (upload == null) {
|
||||
return false;
|
||||
}
|
||||
ossId = upload.getOssId();
|
||||
hash2OssId.putIfAbsent(hash, ossId);
|
||||
// 2.3 把 hash 写回记录(供下次去重)
|
||||
sysOssService.updateHashById(ossId, hash);
|
||||
}
|
||||
}
|
||||
// 4. 组装业务中间表
|
||||
List<AppBusinessFile> bizList = new ArrayList<>(bo.getDeviceIds().length * hash2OssId.size());
|
||||
Long userId = AppLoginHelper.getUserId();
|
||||
for (Long deviceId : bo.getDeviceIds()) {
|
||||
for (Long ossId : hash2OssId.values()) {
|
||||
// 3. 关联业务表
|
||||
AppBusinessFile appFile = new AppBusinessFile();
|
||||
appFile.setFileId(ossId);
|
||||
appFile.setBusinessId(deviceId);
|
||||
appFile.setFileType(bo.getFileType());
|
||||
appFile.setCreateBy(userId);
|
||||
bizList.add(appFile);
|
||||
}
|
||||
}
|
||||
if (CollUtil.isEmpty(bizList)) { // 空集合直接返回
|
||||
throw new ServiceException("请选择要上传的文件");
|
||||
}
|
||||
return appBusinessFileService.insertBatch(bizList);
|
||||
}
|
||||
|
||||
public Boolean addVideoList(AppOperationVideoBo bo){
|
||||
if (bo.getVideoUrl().isEmpty()) {
|
||||
throw new ServiceException("请输入视频地址");
|
||||
}
|
||||
if (bo.getDeviceIds() == null || bo.getDeviceIds().length == 0) {
|
||||
throw new ServiceException("请选择你要上传的设备");
|
||||
}
|
||||
List<AppOperationVideo> bizList = new ArrayList<>(bo.getDeviceIds().length);
|
||||
for (Long deviceId : bo.getDeviceIds()) {
|
||||
|
||||
AppOperationVideo appVideo = new AppOperationVideo();
|
||||
appVideo.setVideoName(bo.getVideoName());
|
||||
appVideo.setDeviceId(deviceId);
|
||||
appVideo.setVideoUrl(bo.getVideoUrl());
|
||||
bizList.add(appVideo);
|
||||
}
|
||||
if (CollUtil.isEmpty(bizList)) { // 空集合直接返回
|
||||
throw new ServiceException("请选择要上传的视频");
|
||||
}
|
||||
return appOperationVideoService.insertBatch(bizList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设备详情
|
||||
* @param deviceId
|
||||
* @return
|
||||
*/
|
||||
public DeviceInfoVo getDeviceInfo(Long deviceId) {
|
||||
if(deviceId == null || deviceId <= 0L) {
|
||||
throw new ServiceException("请选择设备");
|
||||
}
|
||||
DeviceInfoVo vo = new DeviceInfoVo();
|
||||
var device = deviceService.getById(deviceId);
|
||||
AppBusinessFileBo fileBo = new AppBusinessFileBo();
|
||||
fileBo.setBusinessId(deviceId);
|
||||
AppOperationVideoBo videoBo = new AppOperationVideoBo();
|
||||
videoBo.setDeviceId(deviceId);
|
||||
vo.setAppBusinessFileVoList(appBusinessFileService.queryAppFileList(fileBo));
|
||||
vo.setAppOperationVideoVoList(appOperationVideoService.queryList(videoBo));
|
||||
return vo;
|
||||
}
|
||||
}
|
||||
@ -1,14 +1,22 @@
|
||||
package com.fuyuanshen.web.service.device;
|
||||
|
||||
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.core.toolkit.CollectionUtils;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
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.common.core.constant.GlobalConstants;
|
||||
import com.fuyuanshen.common.core.exception.ServiceException;
|
||||
import com.fuyuanshen.common.core.utils.ImageToCArrayConverter;
|
||||
@ -19,21 +27,29 @@ 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.DeviceType;
|
||||
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.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.web.domain.Dto.DeviceDebugLogoUploadDto;
|
||||
import com.fuyuanshen.web.domain.vo.DeviceXinghanDetailVo;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
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.Bitmap80x12Generator.buildArr;
|
||||
@ -51,6 +67,9 @@ public class DeviceXinghanBizService {
|
||||
private final DeviceTypeMapper deviceTypeMapper;
|
||||
private final MqttGateway mqttGateway;
|
||||
private final DeviceLogMapper deviceLogMapper;
|
||||
private final AppPersonnelInfoRecordsMapper appPersonnelInfoRecordsMapper;
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
/**
|
||||
* 所有档位的描述表
|
||||
@ -101,6 +120,71 @@ public class DeviceXinghanBizService {
|
||||
sendCommand(dto, "ins_ShakeBit","强制报警");
|
||||
}
|
||||
|
||||
public DeviceXinghanDetailVo getInfo(Long id) {
|
||||
Device device = deviceMapper.selectById(id);
|
||||
if (device == null) {
|
||||
throw new RuntimeException("请先将设备入库!!!");
|
||||
}
|
||||
|
||||
DeviceXinghanDetailVo vo = new DeviceXinghanDetailVo();
|
||||
vo.setDeviceId(device.getId());
|
||||
vo.setDeviceName(device.getDeviceName());
|
||||
vo.setDevicePic(device.getDevicePic());
|
||||
vo.setDeviceImei(device.getDeviceImei());
|
||||
vo.setDeviceMac(device.getDeviceMac());
|
||||
vo.setDeviceStatus(device.getDeviceStatus());
|
||||
DeviceType deviceType = deviceTypeMapper.selectById(device.getDeviceType());
|
||||
if (deviceType != null) {
|
||||
vo.setCommunicationMode(Integer.valueOf(deviceType.getCommunicationMode()));
|
||||
vo.setTypeName(deviceType.getTypeName());
|
||||
}
|
||||
vo.setBluetoothName(device.getBluetoothName());
|
||||
|
||||
|
||||
QueryWrapper<AppPersonnelInfo> qw = new QueryWrapper<AppPersonnelInfo>()
|
||||
.eq("device_id", device.getId());
|
||||
AppPersonnelInfo appPersonnelInfo = appPersonnelInfoMapper.selectOne(qw);
|
||||
if (appPersonnelInfo != null) {
|
||||
AppPersonnelInfoVo personnelInfoVo = MapstructUtils.convert(appPersonnelInfo, AppPersonnelInfoVo.class);
|
||||
vo.setPersonnelInfo(personnelInfoVo);
|
||||
}
|
||||
//设备在线状态
|
||||
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+ DEVICE_KEY_PREFIX + device.getDeviceImei() + DeviceRedisKeyConstants.DEVICE_STATUS_KEY_PREFIX);
|
||||
// 获取设备上报的设备状态
|
||||
if(StringUtils.isNotBlank(deviceStatus)){
|
||||
try {
|
||||
MqttXinghanJson deviceJson = objectMapper.readValue(deviceStatus, MqttXinghanJson.class);
|
||||
vo.setStaLightGrade(deviceJson.getStaLightGrade());
|
||||
vo.setStaPowerPercent(deviceJson.getStaPowerPercent());
|
||||
vo.setStaSOSGrade(deviceJson.getStaSOSGrade());
|
||||
vo.setStaDetectGrade(deviceJson.getStaDetectGrade());
|
||||
vo.setStaPowerTime(deviceJson.getStaPowerTime());
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("设备状态缓存格式非法", e);
|
||||
}
|
||||
}else{
|
||||
vo.setStaPowerPercent(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);
|
||||
vo.setLongitude(jsonObject.get("longitude").toString());
|
||||
vo.setLatitude(jsonObject.get("latitude").toString());
|
||||
vo.setAddress((String)jsonObject.get("address"));
|
||||
}
|
||||
|
||||
return vo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传设备logo
|
||||
*/
|
||||
@ -142,22 +226,84 @@ public class DeviceXinghanBizService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量上传设备logo
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void uploadDeviceLogoBatch(DeviceDebugLogoUploadDto batchDto) {
|
||||
if (CollectionUtils.isEmpty(batchDto.getDeviceIds())) {
|
||||
throw new ServiceException("设备列表为空");
|
||||
}
|
||||
|
||||
// 1. 一次性把设备查出来(N -> 1)
|
||||
QueryWrapper<Device> query = new QueryWrapper<>();
|
||||
query.in("id", batchDto.getDeviceIds());
|
||||
List<Device> devices = deviceMapper.selectList(query);
|
||||
if (devices.size() != batchDto.getDeviceIds().size()) {
|
||||
throw new ServiceException("部分设备不存在");
|
||||
}
|
||||
|
||||
// 2. 图片只转换一次(160*80 固定尺寸)
|
||||
byte[] largeData;
|
||||
try {
|
||||
largeData = ImageToCArrayConverter.convertImageToCArray(
|
||||
batchDto.getFile().getInputStream(), 160, 80, 25600);
|
||||
} catch (IOException e) {
|
||||
throw new ServiceException("图片解析失败");
|
||||
}
|
||||
int[] picArray = convertHexToDecimal(largeData);
|
||||
|
||||
// 3. 过滤离线设备 & 组装指令
|
||||
List<Device> onlineDevices = devices.stream()
|
||||
.filter(d -> !isDeviceOffline(d.getDeviceImei()))
|
||||
.toList();
|
||||
onlineDevices.forEach(d -> {
|
||||
String redisKey = GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + d.getDeviceImei() + DEVICE_BOOT_LOGO_KEY_PREFIX;
|
||||
|
||||
// 如果 Redis 里已存在,直接跳过
|
||||
if (RedisUtils.getCacheObject(redisKey) != null) {
|
||||
return; // 跳过本次循环
|
||||
}
|
||||
|
||||
RedisUtils.setCacheObject(
|
||||
redisKey,
|
||||
Arrays.toString(picArray),
|
||||
Duration.ofSeconds(5 * 60L));
|
||||
// 3.2 MQTT 下发
|
||||
Map<String, Object> payload =
|
||||
Collections.singletonMap("ins_PicTrans", Collections.singletonList(0));
|
||||
String topic = MqttConstants.GLOBAL_PUB_KEY + d.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());
|
||||
}
|
||||
|
||||
recordDeviceLog(d.getId(), d.getDeviceName(), "上传开机画面", "上传开机画面", AppLoginHelper.getUserId());
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 人员登记
|
||||
* @param bo
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class) // 1. 事务
|
||||
public boolean registerPersonInfo(AppPersonnelInfoBo bo) {
|
||||
Long deviceId = bo.getDeviceId();
|
||||
Device deviceObj = deviceMapper.selectById(deviceId);
|
||||
if (deviceObj == null) {
|
||||
throw new RuntimeException("请先将设备入库!!!");
|
||||
}
|
||||
if (deviceObj.getDeviceImei() == null) {
|
||||
return saveOrUpdatePersonnelInfo(bo, deviceId);
|
||||
}
|
||||
if (isDeviceOffline(deviceObj.getDeviceImei())) {
|
||||
throw new ServiceException("设备已断开连接:" + deviceObj.getDeviceName());
|
||||
}
|
||||
QueryWrapper<AppPersonnelInfo> qw = new QueryWrapper<AppPersonnelInfo>()
|
||||
.eq("device_id", deviceId);
|
||||
List<AppPersonnelInfoVo> appPersonnelInfoVos = appPersonnelInfoMapper.selectVoList(qw);
|
||||
|
||||
List<String> list = new ArrayList<>();
|
||||
list.add(bo.getName());
|
||||
@ -181,18 +327,47 @@ public class DeviceXinghanBizService {
|
||||
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);
|
||||
|
||||
return saveOrUpdatePersonnelInfo(bo, deviceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 仅内部调用,已处于 @Transactional 内
|
||||
*/
|
||||
private Boolean saveOrUpdatePersonnelInfo(AppPersonnelInfoBo bo, Long deviceId) {
|
||||
// 1. 先查主表有没有
|
||||
AppPersonnelInfo main = appPersonnelInfoMapper.selectOne(
|
||||
Wrappers.<AppPersonnelInfo>lambdaQuery()
|
||||
.eq(AppPersonnelInfo::getDeviceId, deviceId));
|
||||
|
||||
if (main == null) {
|
||||
/* ====== 新增场景 ====== */
|
||||
main = MapstructUtils.convert(bo, AppPersonnelInfo.class);
|
||||
main.setDeviceId(deviceId);
|
||||
// 1.1 主表插入 -> 主键回写
|
||||
appPersonnelInfoMapper.insert(main); // 执行后 main.getId() 一定有值
|
||||
} else {
|
||||
UpdateWrapper<AppPersonnelInfo> 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;
|
||||
/* ====== 更新场景 ====== */
|
||||
// 1.2 把 bo 的新值刷到主表
|
||||
appPersonnelInfoMapper.update(null,
|
||||
Wrappers.<AppPersonnelInfo>update()
|
||||
.eq("device_id", deviceId)
|
||||
.set("name", bo.getName())
|
||||
.set("position", bo.getPosition())
|
||||
.set("unit_name", bo.getUnitName())
|
||||
.set("code", bo.getCode())
|
||||
.set("update_time", new Date()));
|
||||
}
|
||||
|
||||
// 2. 无论新增/更新,都插入一条履历
|
||||
AppPersonnelInfoRecords record = new AppPersonnelInfoRecords();
|
||||
// 只拷业务字段,不拷时间、id、操作人
|
||||
BeanUtil.copyProperties(main, record, "id", "createTime", "updateTime", "createBy", "updateBy");
|
||||
record.setId(null); // 让自增
|
||||
record.setPersonnelId(main.getId()); // 关键:主键已回写,不会为 null
|
||||
appPersonnelInfoRecordsMapper.insert(record);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -207,6 +382,10 @@ public class DeviceXinghanBizService {
|
||||
if (deviceIds == null || deviceIds.isEmpty()) {
|
||||
throw new ServiceException("请选择设备");
|
||||
}
|
||||
if(!StringUtils.isNotEmpty(bo.getSendMsg()))
|
||||
{
|
||||
throw new ServiceException("请输入发送内容");
|
||||
}
|
||||
QueryWrapper<Device> queryWrapper = new QueryWrapper<>();
|
||||
// 使用 in 语句根据id集合查询
|
||||
queryWrapper.in("id", deviceIds);
|
||||
@ -259,9 +438,11 @@ public class DeviceXinghanBizService {
|
||||
*/
|
||||
private void sendSingleAlarmMessage(Device device, String message) {
|
||||
String deviceImei = device.getDeviceImei();
|
||||
|
||||
int msgLen = message.length() / 2;
|
||||
StringBuilder msgBuilder = new StringBuilder(message);
|
||||
msgBuilder.insert(msgLen, '|');
|
||||
// 缓存告警消息到Redis
|
||||
RedisUtils.setCacheObject(GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + deviceImei + DEVICE_ALARM_MESSAGE_KEY_PREFIX, message, Duration.ofSeconds(5 * 60L));
|
||||
RedisUtils.setCacheObject(GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + deviceImei + DEVICE_ALARM_MESSAGE_KEY_PREFIX, msgBuilder.toString(), Duration.ofSeconds(5 * 60L));
|
||||
|
||||
// 构建并发送MQTT消息
|
||||
Map<String, Object> payload = Map.of("ins_BreakNews", Collections.singletonList(0));
|
||||
|
||||
@ -0,0 +1,138 @@
|
||||
package com.fuyuanshen.web.service.impl;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.stp.parameter.SaLoginParameter;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.crypto.digest.BCrypt;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.fuyuanshen.app.domain.AppUser;
|
||||
import com.fuyuanshen.app.domain.vo.AppUserVo;
|
||||
import com.fuyuanshen.app.mapper.AppUserMapper;
|
||||
import com.fuyuanshen.app.service.AppLoginService;
|
||||
import com.fuyuanshen.common.core.constant.Constants;
|
||||
import com.fuyuanshen.common.core.constant.GlobalConstants;
|
||||
import com.fuyuanshen.common.core.constant.SystemConstants;
|
||||
import com.fuyuanshen.common.core.domain.model.AppLoginUser;
|
||||
import com.fuyuanshen.common.core.domain.model.AppPasswordLoginBody;
|
||||
import com.fuyuanshen.common.core.domain.model.LoginUser;
|
||||
import com.fuyuanshen.common.core.domain.model.PasswordLoginBody;
|
||||
import com.fuyuanshen.common.core.enums.LoginType;
|
||||
import com.fuyuanshen.common.core.enums.UserType;
|
||||
import com.fuyuanshen.common.core.exception.user.CaptchaException;
|
||||
import com.fuyuanshen.common.core.exception.user.CaptchaExpireException;
|
||||
import com.fuyuanshen.common.core.exception.user.UserException;
|
||||
import com.fuyuanshen.common.core.utils.MessageUtils;
|
||||
import com.fuyuanshen.common.core.utils.StringUtils;
|
||||
import com.fuyuanshen.common.core.utils.ValidatorUtils;
|
||||
import com.fuyuanshen.common.json.utils.JsonUtils;
|
||||
import com.fuyuanshen.common.redis.utils.RedisUtils;
|
||||
import com.fuyuanshen.common.satoken.utils.AppLoginHelper;
|
||||
import com.fuyuanshen.common.satoken.utils.LoginHelper;
|
||||
import com.fuyuanshen.common.tenant.helper.TenantHelper;
|
||||
import com.fuyuanshen.common.web.config.properties.CaptchaProperties;
|
||||
import com.fuyuanshen.system.domain.SysUser;
|
||||
import com.fuyuanshen.system.domain.vo.SysClientVo;
|
||||
import com.fuyuanshen.system.domain.vo.SysUserVo;
|
||||
import com.fuyuanshen.system.mapper.SysUserMapper;
|
||||
import com.fuyuanshen.web.domain.vo.LoginVo;
|
||||
import com.fuyuanshen.web.service.IAuthStrategy;
|
||||
import com.fuyuanshen.web.service.SysLoginService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* APP密码认证策略
|
||||
*
|
||||
* @author Michelle.Chung
|
||||
*/
|
||||
@Slf4j
|
||||
@Service("appPassword" + IAuthStrategy.BASE_NAME)
|
||||
@RequiredArgsConstructor
|
||||
public class AppPasswordAuthStrategy implements IAuthStrategy {
|
||||
|
||||
private final CaptchaProperties captchaProperties;
|
||||
private final AppLoginService loginService;
|
||||
private final AppUserMapper appUserMapper;
|
||||
|
||||
@Override
|
||||
public LoginVo login(String body, SysClientVo client) {
|
||||
AppPasswordLoginBody loginBody = JsonUtils.parseObject(body, AppPasswordLoginBody.class);
|
||||
ValidatorUtils.validate(loginBody);
|
||||
String tenantId = loginBody.getTenantId();
|
||||
String username = loginBody.getUsername();
|
||||
String password = loginBody.getPassword();
|
||||
AppLoginUser loginUser = TenantHelper.dynamic(tenantId, () -> {
|
||||
AppUserVo user = loadUserByUsername(username);
|
||||
loginService.checkLogin(LoginType.PASSWORD, tenantId, username, () -> !password.equals(user.getPassword()));
|
||||
|
||||
// 此处可根据登录用户的数据不同 自行创建 loginUser
|
||||
return loginService.buildLoginUser(user);
|
||||
});
|
||||
|
||||
loginUser.setClientKey(client.getClientKey());
|
||||
loginUser.setDeviceType(client.getDeviceType());
|
||||
SaLoginParameter model = new SaLoginParameter();
|
||||
model.setDeviceType(client.getDeviceType());
|
||||
// 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
|
||||
// 例如: 后台用户30分钟过期 app用户1天过期
|
||||
model.setTimeout(client.getTimeout());
|
||||
model.setActiveTimeout(client.getActiveTimeout());
|
||||
model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
|
||||
// 生成token
|
||||
AppLoginHelper.login(loginUser, model);
|
||||
|
||||
LoginVo loginVo = new LoginVo();
|
||||
loginVo.setAccessToken(StpUtil.getTokenValue());
|
||||
loginVo.setExpireIn(StpUtil.getTokenTimeout());
|
||||
loginVo.setClientId(client.getClientId());
|
||||
return loginVo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验验证码
|
||||
*
|
||||
* @param username 用户名
|
||||
* @param code 验证码
|
||||
* @param uuid 唯一标识
|
||||
*/
|
||||
private void validateCaptcha(String tenantId, String username, String code, String uuid) {
|
||||
String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.blankToDefault(uuid, "");
|
||||
String captcha = RedisUtils.getCacheObject(verifyKey);
|
||||
RedisUtils.deleteObject(verifyKey);
|
||||
if (captcha == null) {
|
||||
loginService.recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
|
||||
throw new CaptchaExpireException();
|
||||
}
|
||||
if (!code.equalsIgnoreCase(captcha)) {
|
||||
loginService.recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"));
|
||||
throw new CaptchaException();
|
||||
}
|
||||
}
|
||||
|
||||
private AppUserVo loadUserByUsername(String username) {
|
||||
// SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUserName, username));
|
||||
// if (ObjectUtil.isNull(user)) {
|
||||
// log.info("登录用户:{} 不存在.", username);
|
||||
// throw new UserException("user.not.exists", username);
|
||||
// } else if (SystemConstants.DISABLE.equals(user.getStatus())) {
|
||||
// log.info("登录用户:{} 已被停用.", username);
|
||||
// throw new UserException("user.blocked", username);
|
||||
// }
|
||||
// return user;
|
||||
|
||||
AppUserVo user = appUserMapper.selectVoOne(new LambdaQueryWrapper<AppUser>()
|
||||
.eq(AppUser::getPhonenumber, username)
|
||||
.eq(AppUser::getUserType, UserType.APP_USER.getUserType()));
|
||||
if (ObjectUtil.isNull(user)) {
|
||||
log.info("登录用户:{} 不存在.", username);
|
||||
throw new UserException("user.not.exists", username);
|
||||
|
||||
} else if (SystemConstants.DISABLE.equals(user.getStatus())) {
|
||||
log.info("登录用户:{} 已被停用.", username);
|
||||
throw new UserException("user.blocked", username);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
}
|
||||
@ -54,7 +54,7 @@ public class AppSmsAuthStrategy implements IAuthStrategy {
|
||||
String phonenumber = loginBody.getPhonenumber();
|
||||
String smsCode = loginBody.getSmsCode();
|
||||
AppLoginUser loginUser = TenantHelper.dynamic(tenantId, () -> {
|
||||
loginService.checkLogin(LoginType.SMS, tenantId, phonenumber, () -> !validateSmsCode(tenantId, phonenumber, smsCode));
|
||||
// loginService.checkLogin(LoginType.SMS, tenantId, phonenumber, () -> !validateSmsCode(tenantId, phonenumber, smsCode));
|
||||
AppUserVo user = loadUserByPhonenumber(phonenumber);
|
||||
if (ObjectUtil.isNull(user)) {
|
||||
//新增Appuser
|
||||
|
||||
@ -0,0 +1,28 @@
|
||||
package com.fuyuanshen.web.util;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.HexFormat;
|
||||
|
||||
/**
|
||||
* 文件哈希工具类
|
||||
*/
|
||||
public class FileHashUtil {
|
||||
private static final String ALGORITHM = "SHA-256";
|
||||
|
||||
public static String hash(MultipartFile file) throws IOException {
|
||||
MessageDigest digest = DigestUtils.getDigest(ALGORITHM);
|
||||
try (InputStream in = file.getInputStream()) {
|
||||
byte[] buf = new byte[8192];
|
||||
int len;
|
||||
while ((len = in.read(buf)) != -1) {
|
||||
digest.update(buf, 0, len);
|
||||
}
|
||||
}
|
||||
return HexFormat.of().formatHex(digest.digest());
|
||||
}
|
||||
}
|
||||
@ -303,6 +303,6 @@ mqtt:
|
||||
password: #YtvpSfCNG
|
||||
url: tcp://47.120.79.150:2883
|
||||
subClientId: fys_subClient
|
||||
subTopic: worker/location/#
|
||||
subTopic: A/#
|
||||
pubTopic: B/#
|
||||
pubClientId: fys_pubClient
|
||||
@ -17,6 +17,16 @@ public interface GlobalConstants {
|
||||
*/
|
||||
String CAPTCHA_CODE_KEY = GLOBAL_REDIS_KEY + "captcha_codes:";
|
||||
|
||||
/**
|
||||
* 忘记密码验证码 redis key
|
||||
*/
|
||||
String FORGET_PASSWORD_CODE_KEY = GLOBAL_REDIS_KEY + "forget_password_codes:";
|
||||
|
||||
/**
|
||||
* 注册验证码 redis key
|
||||
*/
|
||||
String REGISTER_CODE_KEY = GLOBAL_REDIS_KEY + "register_codes:";
|
||||
|
||||
/**
|
||||
* 设备分享验证码 redis key
|
||||
*/
|
||||
|
||||
@ -0,0 +1,31 @@
|
||||
package com.fuyuanshen.common.core.domain.model;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
/**
|
||||
* 密码登录对象
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class AppPasswordLoginBody extends LoginBody {
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
@NotBlank(message = "{user.username.not.blank}")
|
||||
@Length(min = 2, max = 30, message = "{user.username.length.valid}")
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 用户密码
|
||||
*/
|
||||
@NotBlank(message = "{user.password.not.blank}")
|
||||
// @Length(min = 5, max = 30, message = "{user.password.length.valid}")
|
||||
private String password;
|
||||
|
||||
}
|
||||
@ -0,0 +1,108 @@
|
||||
package com.fuyuanshen.common.core.utils.date;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 持续时间工具类
|
||||
* 提供计算两个日期之间差值的方法,并以时分秒格式返回
|
||||
*
|
||||
* @author fys
|
||||
*/
|
||||
public class DurationUtils {
|
||||
|
||||
/**
|
||||
* 计算两个日期之间的差值,返回时分秒格式的字符串
|
||||
* 格式为 "X小时 Y分钟 Z秒"
|
||||
*
|
||||
* @param startDate 开始日期
|
||||
* @param endDate 结束日期
|
||||
* @return 时分秒格式的时间差字符串
|
||||
*/
|
||||
public static String getDurationBetween(Date startDate, Date endDate) {
|
||||
if (startDate == null || endDate == null) {
|
||||
return "0小时 0分钟 0秒";
|
||||
}
|
||||
|
||||
long diffInMillis = Math.abs(endDate.getTime() - startDate.getTime());
|
||||
long hours = TimeUnit.MILLISECONDS.toHours(diffInMillis);
|
||||
long minutes = TimeUnit.MILLISECONDS.toMinutes(diffInMillis) % 60;
|
||||
long seconds = TimeUnit.MILLISECONDS.toSeconds(diffInMillis) % 60;
|
||||
|
||||
return String.format("%d小时 %d分钟 %d秒", hours, minutes, seconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两个日期之间的差值,返回时分秒格式的字符串
|
||||
* 仅显示非零单位
|
||||
*
|
||||
* @param startDate 开始日期
|
||||
* @param endDate 结束日期
|
||||
* @return 时分秒格式的时间差字符串,仅显示非零单位
|
||||
*/
|
||||
public static String getDurationBetweenPretty(Date startDate, Date endDate) {
|
||||
if (startDate == null || endDate == null) {
|
||||
return "0秒";
|
||||
}
|
||||
|
||||
long diffInMillis = Math.abs(endDate.getTime() - startDate.getTime());
|
||||
long hours = TimeUnit.MILLISECONDS.toHours(diffInMillis);
|
||||
long minutes = TimeUnit.MILLISECONDS.toMinutes(diffInMillis) % 60;
|
||||
long seconds = TimeUnit.MILLISECONDS.toSeconds(diffInMillis) % 60;
|
||||
|
||||
StringBuilder result = new StringBuilder();
|
||||
if (hours > 0) {
|
||||
result.append(hours).append("小时 ");
|
||||
}
|
||||
if (minutes > 0) {
|
||||
result.append(minutes).append("分钟 ");
|
||||
}
|
||||
if (seconds > 0 || result.length() == 0) {
|
||||
result.append(seconds).append("秒");
|
||||
}
|
||||
|
||||
return result.toString().trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算指定毫秒数的持续时间,返回时分秒格式的字符串
|
||||
*
|
||||
* @param millis 毫秒数
|
||||
* @return 时分秒格式的持续时间字符串
|
||||
*/
|
||||
public static String getDurationFromMillis(long millis) {
|
||||
long absMillis = Math.abs(millis);
|
||||
long hours = TimeUnit.MILLISECONDS.toHours(absMillis);
|
||||
long minutes = TimeUnit.MILLISECONDS.toMinutes(absMillis) % 60;
|
||||
long seconds = TimeUnit.MILLISECONDS.toSeconds(absMillis) % 60;
|
||||
|
||||
return String.format("%d小时 %d分钟 %d秒", hours, minutes, seconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算指定毫秒数的持续时间,返回时分秒格式的字符串
|
||||
* 仅显示非零单位
|
||||
*
|
||||
* @param millis 毫秒数
|
||||
* @return 时分秒格式的持续时间字符串,仅显示非零单位
|
||||
*/
|
||||
public static String getDurationFromMillisPretty(long millis) {
|
||||
long absMillis = Math.abs(millis);
|
||||
long hours = TimeUnit.MILLISECONDS.toHours(absMillis);
|
||||
long minutes = TimeUnit.MILLISECONDS.toMinutes(absMillis) % 60;
|
||||
long seconds = TimeUnit.MILLISECONDS.toSeconds(absMillis) % 60;
|
||||
|
||||
StringBuilder result = new StringBuilder();
|
||||
if (hours > 0) {
|
||||
result.append(hours).append("小时 ");
|
||||
}
|
||||
if (minutes > 0) {
|
||||
result.append(minutes).append("分钟 ");
|
||||
}
|
||||
if (seconds > 0 || result.length() == 0) {
|
||||
result.append(seconds).append("秒");
|
||||
}
|
||||
|
||||
return result.toString().trim();
|
||||
}
|
||||
}
|
||||
@ -50,5 +50,6 @@ public class AppDeviceBindRecord extends TenantEntity {
|
||||
*/
|
||||
private Date bindingTime;
|
||||
|
||||
private Integer communicationMode;
|
||||
|
||||
}
|
||||
|
||||
@ -60,7 +60,7 @@ public class AppDeviceShareBo extends BaseEntity {
|
||||
/**
|
||||
* 分享时间
|
||||
*/
|
||||
private Date shareStartTime;
|
||||
private Date shareEndTime;
|
||||
private String shareStartTime;
|
||||
private String shareEndTime;
|
||||
|
||||
}
|
||||
|
||||
@ -45,5 +45,10 @@ public class AppOperationVideoBo extends BaseEntity {
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 多设备id
|
||||
*/
|
||||
private Long[] deviceIds;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -109,4 +109,9 @@ public class AppDeviceShareVo implements Serializable {
|
||||
* 告警状态(0解除告警,1告警)
|
||||
*/
|
||||
private String alarmStatus;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private String createTime;
|
||||
}
|
||||
|
||||
@ -19,6 +19,10 @@ public class AppFileVo {
|
||||
* 文件名称
|
||||
*/
|
||||
private String fileName;
|
||||
/**
|
||||
* 文件类型(1:操作说明,2:产品参数)
|
||||
*/
|
||||
private Long fileType;
|
||||
/**
|
||||
* 文件url
|
||||
*/
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package com.fuyuanshen.app.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;
|
||||
@ -50,6 +51,14 @@ public interface IAppBusinessFileService {
|
||||
*/
|
||||
Boolean insertByBo(AppBusinessFileBo bo);
|
||||
|
||||
/**
|
||||
* 批量新增app业务文件
|
||||
*
|
||||
* @param bo 批量新增app业务文件
|
||||
* @return 是否新增成功
|
||||
*/
|
||||
Boolean insertBatch(Collection<AppBusinessFile> bo);
|
||||
|
||||
/**
|
||||
* 修改app业务文件
|
||||
*
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
package com.fuyuanshen.app.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;
|
||||
@ -49,6 +51,14 @@ public interface IAppOperationVideoService {
|
||||
*/
|
||||
Boolean insertByBo(AppOperationVideoBo bo);
|
||||
|
||||
/**
|
||||
* 批量新增操作视频
|
||||
*
|
||||
* @param bo 批量新增操作视频
|
||||
* @return 是否新增成功
|
||||
*/
|
||||
Boolean insertBatch(Collection<AppOperationVideo> bo);
|
||||
|
||||
/**
|
||||
* 修改操作视频
|
||||
*
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package com.fuyuanshen.app.service.impl;
|
||||
|
||||
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;
|
||||
@ -100,6 +101,20 @@ public class AppBusinessFileServiceImpl implements IAppBusinessFileService {
|
||||
return flag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean insertBatch(Collection<AppBusinessFile> bo) {
|
||||
// 1. 去重后的 businessId 集合
|
||||
List<Long> businessIds = bo.stream()
|
||||
.map(AppBusinessFile::getBusinessId)
|
||||
.distinct()
|
||||
.toList();
|
||||
QueryWrapper<AppBusinessFile> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.in("business_id", businessIds);
|
||||
queryWrapper.eq("file_type", bo.stream().findFirst().orElseThrow().getFileType());
|
||||
baseMapper.delete(queryWrapper);
|
||||
return baseMapper.insertBatch(bo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改app业务文件
|
||||
*
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
package com.fuyuanshen.app.service.impl;
|
||||
|
||||
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;
|
||||
@ -97,6 +99,19 @@ public class AppOperationVideoServiceImpl implements IAppOperationVideoService {
|
||||
return flag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean insertBatch(Collection<AppOperationVideo> bo) {
|
||||
// 1. 去重后的 businessId 集合
|
||||
List<Long> deviceIds = bo.stream()
|
||||
.map(AppOperationVideo::getDeviceId)
|
||||
.distinct()
|
||||
.toList();
|
||||
QueryWrapper<AppOperationVideo> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.in("device_id", deviceIds);
|
||||
baseMapper.delete(queryWrapper);
|
||||
return baseMapper.insertBatch(bo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改操作视频
|
||||
*
|
||||
|
||||
@ -5,7 +5,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<mapper namespace="com.fuyuanshen.app.mapper.AppBusinessFileMapper">
|
||||
|
||||
<select id="queryAppFileList" resultType="com.fuyuanshen.app.domain.vo.AppFileVo">
|
||||
select a.id,a.business_id,a.file_id,b.file_name,b.url fileUrl from app_business_file a left join sys_oss b on a.file_id = b.oss_id
|
||||
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">
|
||||
and a.business_id = #{businessId}
|
||||
|
||||
@ -72,9 +72,12 @@
|
||||
and ad.device_id = #{bo.deviceId}
|
||||
</if>
|
||||
<if test="bo.shareUser != null">
|
||||
and u.user_name = #{bo.shareUser}
|
||||
and ad.phonenumber like concat('%',#{bo.shareUser},'%')
|
||||
</if>
|
||||
<if test="bo.shareStartTime != null and bo.hareEndTime != null">
|
||||
<if test="bo.phonenumber != null">
|
||||
and ad.phonenumber like concat('%',#{bo.phonenumber},'%')
|
||||
</if>
|
||||
<if test="bo.shareStartTime != null and bo.shareEndTime != null">
|
||||
and ad.create_time between #{bo.shareStartTime} and #{bo.shareEndTime}
|
||||
</if>
|
||||
</where>
|
||||
|
||||
@ -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.DeviceFenceStatusVo;
|
||||
import com.fuyuanshen.equipment.domain.bo.DeviceFenceStatusBo;
|
||||
import com.fuyuanshen.equipment.service.IDeviceFenceStatusService;
|
||||
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
|
||||
|
||||
/**
|
||||
* 设备进入围栏状态
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2025-09-15
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/equipment/fenceStatus")
|
||||
public class DeviceFenceStatusController extends BaseController {
|
||||
|
||||
private final IDeviceFenceStatusService deviceFenceStatusService;
|
||||
|
||||
/**
|
||||
* 查询设备进入围栏状态列表
|
||||
*/
|
||||
@SaCheckPermission("equipment:fenceStatus:list")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<DeviceFenceStatusVo> list(DeviceFenceStatusBo bo, PageQuery pageQuery) {
|
||||
return deviceFenceStatusService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出设备进入围栏状态列表
|
||||
*/
|
||||
@SaCheckPermission("equipment:fenceStatus:export")
|
||||
@Log(title = "设备进入围栏状态", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(DeviceFenceStatusBo bo, HttpServletResponse response) {
|
||||
List<DeviceFenceStatusVo> list = deviceFenceStatusService.queryList(bo);
|
||||
ExcelUtil.exportExcel(list, "设备进入围栏状态", DeviceFenceStatusVo.class, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备进入围栏状态详细信息
|
||||
*
|
||||
* @param id 主键
|
||||
*/
|
||||
@SaCheckPermission("equipment:fenceStatus:query")
|
||||
@GetMapping("/{id}")
|
||||
public R<DeviceFenceStatusVo> getInfo(@NotNull(message = "主键不能为空")
|
||||
@PathVariable Long id) {
|
||||
return R.ok(deviceFenceStatusService.queryById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增设备进入围栏状态
|
||||
*/
|
||||
@SaCheckPermission("equipment:fenceStatus:add")
|
||||
@Log(title = "设备进入围栏状态", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping()
|
||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody DeviceFenceStatusBo bo) {
|
||||
return toAjax(deviceFenceStatusService.insertByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改设备进入围栏状态
|
||||
*/
|
||||
@SaCheckPermission("equipment:fenceStatus:edit")
|
||||
@Log(title = "设备进入围栏状态", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping()
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody DeviceFenceStatusBo bo) {
|
||||
return toAjax(deviceFenceStatusService.updateByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除设备进入围栏状态
|
||||
*
|
||||
* @param ids 主键串
|
||||
*/
|
||||
@SaCheckPermission("equipment:fenceStatus:remove")
|
||||
@Log(title = "设备进入围栏状态", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
||||
@PathVariable Long[] ids) {
|
||||
return toAjax(deviceFenceStatusService.deleteWithValidByIds(List.of(ids), true));
|
||||
}
|
||||
}
|
||||
@ -2,6 +2,8 @@ package com.fuyuanshen.equipment.domain;
|
||||
|
||||
import com.fuyuanshen.common.tenant.core.TenantEntity;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import io.github.linpeilie.annotations.AutoMapping;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@ -66,12 +68,12 @@ public class DeviceAlarm extends TenantEntity {
|
||||
/**
|
||||
* 经度
|
||||
*/
|
||||
private Long longitude;
|
||||
private Double longitude;
|
||||
|
||||
/**
|
||||
* 纬度
|
||||
*/
|
||||
private Long latitude;
|
||||
private Double latitude;
|
||||
|
||||
/**
|
||||
* 报警位置
|
||||
@ -97,7 +99,15 @@ public class DeviceAlarm extends TenantEntity {
|
||||
* 0已处理,1未处理
|
||||
* treatment_state
|
||||
*/
|
||||
private Long treatmentState;
|
||||
private Integer treatmentState;
|
||||
|
||||
|
||||
/**
|
||||
* 设备IMEI
|
||||
* device_imei
|
||||
*/
|
||||
@Schema(title = "设备IMEI")
|
||||
@AutoMapping(target = "deviceImei")
|
||||
private String deviceImei;
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,72 @@
|
||||
package com.fuyuanshen.equipment.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.fuyuanshen.common.mybatis.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* 围栏进出记录对象 device_fence_access_record
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2025-09-11
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("device_fence_access_record")
|
||||
public class DeviceFenceAccessRecord extends BaseEntity {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 记录ID
|
||||
*/
|
||||
@TableId(value = "id")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 围栏ID
|
||||
*/
|
||||
private Long fenceId;
|
||||
|
||||
/**
|
||||
* 设备标识
|
||||
*/
|
||||
private String deviceId;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 事件类型
|
||||
*/
|
||||
private Long eventType;
|
||||
|
||||
/**
|
||||
* 纬度
|
||||
*/
|
||||
private Double latitude;
|
||||
|
||||
/**
|
||||
* 经度
|
||||
*/
|
||||
private Double longitude;
|
||||
|
||||
/**
|
||||
* 定位精度
|
||||
*/
|
||||
private Long accuracy;
|
||||
|
||||
/**
|
||||
* 事件时间
|
||||
*/
|
||||
private Date eventTime;
|
||||
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
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_fence_status
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2025-09-15
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("device_fence_status")
|
||||
public class DeviceFenceStatus extends TenantEntity {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@TableId(value = "id")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 设备ID
|
||||
*/
|
||||
private String deviceId;
|
||||
|
||||
/**
|
||||
* 围栏ID
|
||||
*/
|
||||
private Long fenceId;
|
||||
|
||||
/**
|
||||
* 状态: 0-在围栏外, 1-在围栏内
|
||||
*/
|
||||
private Long status;
|
||||
|
||||
/**
|
||||
* 上次检查时间
|
||||
*/
|
||||
private Date lastCheckTime;
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,63 @@
|
||||
package com.fuyuanshen.equipment.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.fuyuanshen.common.mybatis.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* 电子围栏对象 device_geo_fence
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2025-09-11
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("device_geo_fence")
|
||||
public class DeviceGeoFence extends BaseEntity {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 围栏唯一标识
|
||||
*/
|
||||
@TableId(value = "id")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 围栏名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 围栏描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 围栏区域类型, 0 POLYGON, 1 CIRCLE
|
||||
*/
|
||||
private Long areaType;
|
||||
|
||||
/**
|
||||
* 围栏坐标数据
|
||||
*/
|
||||
private String coordinates;
|
||||
|
||||
/**
|
||||
* 圆形围栏半径(米)
|
||||
*/
|
||||
private Long radius;
|
||||
|
||||
/**
|
||||
* 是否激活
|
||||
*/
|
||||
private Long isActive;
|
||||
|
||||
|
||||
}
|
||||
@ -59,8 +59,8 @@ public class DeviceType extends TenantEntity {
|
||||
@Schema(title = "联网方式", example = "0:无;1:4G;2:WIFI")
|
||||
private String networkWay;
|
||||
|
||||
@Schema(title = "通讯方式", example = "0:4G;1:蓝牙,2 4G&蓝牙")
|
||||
private String communicationMode;
|
||||
@Schema(title = "通讯方式", example = "通讯方式 0:4G;1:蓝牙,2 4G&蓝牙")
|
||||
private Integer communicationMode;
|
||||
|
||||
/**
|
||||
* 创建人名称
|
||||
|
||||
@ -2,6 +2,7 @@ package com.fuyuanshen.equipment.domain.bo;
|
||||
|
||||
import com.fuyuanshen.common.core.validate.EditGroup;
|
||||
import com.fuyuanshen.common.mybatis.core.domain.BaseEntity;
|
||||
import com.fuyuanshen.common.tenant.core.TenantEntity;
|
||||
import com.fuyuanshen.equipment.domain.DeviceAlarm;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
@ -19,7 +20,7 @@ import java.util.Date;
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@AutoMapper(target = DeviceAlarm.class, reverseConvertGenerate = false)
|
||||
public class DeviceAlarmBo extends BaseEntity {
|
||||
public class DeviceAlarmBo extends TenantEntity {
|
||||
|
||||
/**
|
||||
* ID
|
||||
@ -35,6 +36,7 @@ public class DeviceAlarmBo extends BaseEntity {
|
||||
/**
|
||||
* 报警事项
|
||||
* device_action
|
||||
* 0-强制报警,1-撞击闯入,2-自动报警,3-电子围栏告警
|
||||
*/
|
||||
private Integer deviceAction;
|
||||
|
||||
@ -61,7 +63,7 @@ public class DeviceAlarmBo extends BaseEntity {
|
||||
private String dataSource;
|
||||
|
||||
/**
|
||||
* 内容
|
||||
* 内容(报警信息)
|
||||
*/
|
||||
private String content;
|
||||
|
||||
@ -99,7 +101,7 @@ public class DeviceAlarmBo extends BaseEntity {
|
||||
/**
|
||||
* 报警持续时间
|
||||
*/
|
||||
private Long durationTime;
|
||||
private String durationTime;
|
||||
|
||||
/**
|
||||
* 报警查询时间
|
||||
|
||||
@ -6,6 +6,7 @@ import com.fuyuanshen.equipment.domain.DeviceChargeDischarge;
|
||||
import com.fuyuanshen.common.mybatis.core.domain.BaseEntity;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.util.Date;
|
||||
@ -44,13 +45,25 @@ public class DeviceChargeDischargeBo extends BaseEntity {
|
||||
* 开始时间
|
||||
*/
|
||||
@NotNull(message = "开始时间不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date startTime;
|
||||
|
||||
// 添加字符串类型的setter方法来处理前端传递的字符串日期
|
||||
public void setStartTime(String startTime) {
|
||||
this.startTime = DateUtil.parseDate(startTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date endTime;
|
||||
|
||||
// 添加字符串类型的setter方法来处理前端传递的字符串日期
|
||||
public void setEndTime(String endTime) {
|
||||
this.endTime = DateUtil.parseDate(endTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 起始电量百分比(0-100)
|
||||
*/
|
||||
|
||||
@ -0,0 +1,93 @@
|
||||
package com.fuyuanshen.equipment.domain.bo;
|
||||
|
||||
import com.fuyuanshen.common.core.validate.AddGroup;
|
||||
import com.fuyuanshen.common.core.validate.EditGroup;
|
||||
import com.fuyuanshen.common.mybatis.core.domain.BaseEntity;
|
||||
import com.fuyuanshen.equipment.domain.DeviceFenceAccessRecord;
|
||||
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_fence_access_record
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2025-09-11
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@AutoMapper(target = DeviceFenceAccessRecord.class, reverseConvertGenerate = false)
|
||||
public class DeviceFenceAccessRecordBo extends BaseEntity {
|
||||
|
||||
/**
|
||||
* 记录ID
|
||||
*/
|
||||
@NotNull(message = "记录ID不能为空", groups = { EditGroup.class })
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 围栏ID
|
||||
*/
|
||||
@NotNull(message = "围栏ID不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Long fenceId;
|
||||
|
||||
/**
|
||||
* 设备标识
|
||||
*/
|
||||
@NotBlank(message = "设备标识不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String deviceId;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 事件类型
|
||||
*/
|
||||
@NotNull(message = "事件类型不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Long eventType;
|
||||
|
||||
/**
|
||||
* 纬度
|
||||
*/
|
||||
@NotNull(message = "纬度不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Double latitude;
|
||||
|
||||
/**
|
||||
* 经度
|
||||
*/
|
||||
@NotNull(message = "经度不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Double longitude;
|
||||
|
||||
/**
|
||||
* 定位精度
|
||||
*/
|
||||
private Long accuracy;
|
||||
|
||||
/**
|
||||
* 事件时间
|
||||
*/
|
||||
@NotNull(message = "事件时间不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Date eventTime;
|
||||
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
private String beginTime;
|
||||
|
||||
/**
|
||||
* 结束时间
|
||||
*/
|
||||
private String endTime;
|
||||
|
||||
/**
|
||||
* 记录创建时间
|
||||
*/
|
||||
private Date createTime;
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
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.DeviceFenceStatus;
|
||||
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_fence_status
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2025-09-15
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@AutoMapper(target = DeviceFenceStatus.class, reverseConvertGenerate = false)
|
||||
public class DeviceFenceStatusBo extends BaseEntity {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@NotNull(message = "不能为空", groups = { EditGroup.class })
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 设备ID
|
||||
*/
|
||||
@NotBlank(message = "设备ID不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String deviceId;
|
||||
|
||||
/**
|
||||
* 围栏ID
|
||||
*/
|
||||
@NotNull(message = "围栏ID不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Long fenceId;
|
||||
|
||||
/**
|
||||
* 状态: 0-在围栏外, 1-在围栏内
|
||||
*/
|
||||
@NotNull(message = "状态: 0-在围栏外, 1-在围栏内不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Long status;
|
||||
|
||||
/**
|
||||
* 上次检查时间
|
||||
*/
|
||||
@NotNull(message = "上次检查时间不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Date lastCheckTime;
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,75 @@
|
||||
package com.fuyuanshen.equipment.domain.bo;
|
||||
|
||||
import com.fuyuanshen.common.core.validate.AddGroup;
|
||||
import com.fuyuanshen.common.core.validate.EditGroup;
|
||||
import com.fuyuanshen.common.mybatis.core.domain.BaseEntity;
|
||||
import com.fuyuanshen.equipment.domain.DeviceGeoFence;
|
||||
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_geo_fence
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2025-09-11
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@AutoMapper(target = DeviceGeoFence.class, reverseConvertGenerate = false)
|
||||
public class DeviceGeoFenceBo extends BaseEntity {
|
||||
|
||||
/**
|
||||
* 围栏唯一标识
|
||||
*/
|
||||
@NotNull(message = "围栏唯一标识不能为空", groups = { EditGroup.class })
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 围栏名称
|
||||
*/
|
||||
@NotBlank(message = "围栏名称不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 围栏描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 围栏区域类型, 0 POLYGON, 1 CIRCLE
|
||||
*/
|
||||
@NotNull(message = "围栏区域类型, 0 POLYGON, 1 CIRCLE不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Long areaType;
|
||||
|
||||
/**
|
||||
* 围栏坐标数据
|
||||
*/
|
||||
@NotBlank(message = "围栏坐标数据不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String coordinates;
|
||||
|
||||
/**
|
||||
* 圆形围栏半径(米)
|
||||
*/
|
||||
private Long radius;
|
||||
|
||||
/**
|
||||
* 是否激活
|
||||
*/
|
||||
private Long isActive;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private Date updateTime;
|
||||
|
||||
|
||||
}
|
||||
@ -23,7 +23,7 @@ public class AppDeviceBo {
|
||||
private String deviceMac;
|
||||
|
||||
/**
|
||||
* 通讯方式 0:4G; 1:蓝牙
|
||||
* 通讯方式 0:4G;1:蓝牙,2 4G&蓝牙
|
||||
*/
|
||||
@NotNull(message = "通讯方式不能为空", groups = { EditGroup.class })
|
||||
private Integer communicationMode;
|
||||
|
||||
@ -0,0 +1,63 @@
|
||||
package com.fuyuanshen.equipment.domain.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* 围栏位置检查响应结果
|
||||
*
|
||||
* @author: 默苍璃
|
||||
* @date: 2025-09-1110:11
|
||||
*/
|
||||
|
||||
@Data
|
||||
public class FenceCheckResponse {
|
||||
|
||||
/**
|
||||
* 设备ID
|
||||
*/
|
||||
private String deviceId;
|
||||
|
||||
/**
|
||||
* 检查时间
|
||||
*/
|
||||
private Long checkTime;
|
||||
|
||||
/**
|
||||
* 进入围栏列表
|
||||
*/
|
||||
private List<FenceInfo> enteredFences;
|
||||
|
||||
/**
|
||||
* 离开围栏列表
|
||||
*/
|
||||
private List<FenceInfo> exitedFences;
|
||||
|
||||
/**
|
||||
* 当前所在围栏列表
|
||||
*/
|
||||
private List<FenceInfo> currentFences;
|
||||
|
||||
/**
|
||||
* 围栏信息
|
||||
*/
|
||||
@Data
|
||||
public static class FenceInfo {
|
||||
/**
|
||||
* 围栏ID
|
||||
*/
|
||||
private Long fenceId;
|
||||
|
||||
/**
|
||||
* 围栏名称
|
||||
*/
|
||||
private String fenceName;
|
||||
|
||||
/**
|
||||
* 围栏类型
|
||||
*/
|
||||
private Integer fenceType;
|
||||
}
|
||||
}
|
||||
@ -21,13 +21,13 @@ public class DeviceTypeForm {
|
||||
private Boolean isSupportBle;
|
||||
|
||||
@Schema(title = "定位方式", example = "0:无;1:GPS;2:基站;3:wifi;4:北斗")
|
||||
private String locateMode;
|
||||
private Integer locateMode;
|
||||
|
||||
@Schema(title = "联网方式", example = "0:无;1:4G;2:WIFI")
|
||||
private String networkWay;
|
||||
private Integer networkWay;
|
||||
|
||||
@Schema(title = "通讯方式", example = "0:4G;1:蓝牙")
|
||||
private String communicationMode;
|
||||
@Schema(title = "通讯方式", example = "通讯方式 0:4G;1:蓝牙,2 4G&蓝牙")
|
||||
private Integer communicationMode;
|
||||
|
||||
/**
|
||||
* 型号字典用于APP页面跳转
|
||||
|
||||
@ -59,7 +59,7 @@ public class APPDeviceQueryCriteria1 {
|
||||
@Schema(name = "租户ID")
|
||||
private Long tenantId;
|
||||
|
||||
@Schema(name = "通讯方式", example = "0:4G;1:蓝牙")
|
||||
@Schema(name = "通讯方式", example = "通讯方式 0:4G;1:蓝牙,2 4G&蓝牙")
|
||||
private Integer communicationMode;
|
||||
|
||||
}
|
||||
|
||||
@ -82,9 +82,10 @@ public class DeviceQueryCriteria extends BaseEntity {
|
||||
private String tenantId;
|
||||
|
||||
/**
|
||||
* 通讯方式 0:4G;1:蓝牙
|
||||
* 通讯方式 0:4G;1:蓝牙,2 4G&蓝牙
|
||||
* communication_mode
|
||||
*/
|
||||
@Schema(title = "通讯方式", example = "0:4G;1:蓝牙,2 4G&蓝牙")
|
||||
private Integer communicationMode;
|
||||
|
||||
/* app绑定用户id */
|
||||
|
||||
@ -0,0 +1,40 @@
|
||||
package com.fuyuanshen.equipment.domain.query;
|
||||
|
||||
/**
|
||||
* @author: 默苍璃
|
||||
* @date: 2025-09-1110:10
|
||||
*/
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 围栏位置检查请求参数
|
||||
*/
|
||||
@Data
|
||||
public class FenceCheckRequest {
|
||||
|
||||
/**
|
||||
* 设备ID
|
||||
*/
|
||||
@NotNull(message = "设备ID不能为空")
|
||||
private String deviceId;
|
||||
|
||||
/**
|
||||
* 纬度
|
||||
*/
|
||||
@NotNull(message = "纬度不能为空")
|
||||
private Double latitude;
|
||||
|
||||
/**
|
||||
* 经度
|
||||
*/
|
||||
@NotNull(message = "经度不能为空")
|
||||
private Double longitude;
|
||||
|
||||
/**
|
||||
* 定位精度(米)
|
||||
*/
|
||||
private Long accuracy;
|
||||
|
||||
}
|
||||
@ -49,7 +49,7 @@ public class AlarmInformationVo {
|
||||
private Integer alarmManual = 0;
|
||||
|
||||
/**
|
||||
* 电子围栏
|
||||
*
|
||||
*/
|
||||
private Integer fenceElectronic = 0;
|
||||
|
||||
|
||||
@ -26,7 +26,7 @@ public class AppDeviceVo implements Serializable {
|
||||
private String deviceMac;
|
||||
|
||||
/**
|
||||
* 通讯方式 0:4G;1:蓝牙
|
||||
* 通讯方式 0:4G;1:蓝牙,2 4G&蓝牙
|
||||
*/
|
||||
private Integer communicationMode;
|
||||
|
||||
|
||||
@ -26,6 +26,11 @@ public class DataOverviewVo {
|
||||
*/
|
||||
private Integer bindingNew = 0;
|
||||
|
||||
/**
|
||||
* 已绑定设备
|
||||
*/
|
||||
private Integer binding = 0;
|
||||
|
||||
/**
|
||||
* 异常设备
|
||||
*/
|
||||
|
||||
@ -10,6 +10,7 @@ import com.fuyuanshen.common.excel.annotation.ExcelDictFormat;
|
||||
import com.fuyuanshen.common.excel.convert.ExcelDictConvert;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
@ -45,7 +46,7 @@ public class DeviceAlarmVo implements Serializable {
|
||||
|
||||
/**
|
||||
* 报警事项
|
||||
* 0-强制报警,1-撞击闯入,2-手动报警,3-电子围栏告警,4-强制告警
|
||||
* 0-强制报警,1-撞击闯入,2-自动报警,3-电子围栏告警
|
||||
*/
|
||||
@ExcelProperty(value = "报警事项")
|
||||
private Integer deviceAction;
|
||||
@ -134,4 +135,5 @@ public class DeviceAlarmVo implements Serializable {
|
||||
@Schema(name = "设备图片")
|
||||
private String devicePic;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,107 @@
|
||||
package com.fuyuanshen.equipment.domain.vo;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
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 com.fuyuanshen.equipment.domain.DeviceFenceAccessRecord;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
|
||||
/**
|
||||
* 围栏进出记录视图对象 device_fence_access_record
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2025-09-11
|
||||
*/
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
@AutoMapper(target = DeviceFenceAccessRecord.class)
|
||||
public class DeviceFenceAccessRecordVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 记录ID
|
||||
*/
|
||||
// @ExcelProperty(value = "记录ID")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 围栏ID
|
||||
*/
|
||||
// @ExcelProperty(value = "围栏ID")
|
||||
private Long fenceId;
|
||||
|
||||
/**
|
||||
* 围栏名称
|
||||
*/
|
||||
@ExcelProperty(value = "围栏名称")
|
||||
private String fenceName;
|
||||
|
||||
/**
|
||||
* 设备标识
|
||||
*/
|
||||
// @ExcelProperty(value = "设备标识")
|
||||
private Long deviceId;
|
||||
|
||||
/**
|
||||
* 设备名称
|
||||
*/
|
||||
@ExcelProperty(value = "设备名称")
|
||||
private String deviceName;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
// @ExcelProperty(value = "用户ID")
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 事件类型
|
||||
*/
|
||||
@ExcelProperty(value = "事件类型", converter = ExcelDictConvert.class)
|
||||
@ExcelDictFormat(readConverterExp = "1=进入围栏,2=离开围栏")
|
||||
private Long eventType;
|
||||
|
||||
/**
|
||||
* 纬度
|
||||
*/
|
||||
@ExcelProperty(value = "纬度")
|
||||
private Long latitude;
|
||||
|
||||
/**
|
||||
* 经度
|
||||
*/
|
||||
@ExcelProperty(value = "经度")
|
||||
private Long longitude;
|
||||
|
||||
/**
|
||||
* 定位精度
|
||||
*/
|
||||
@ExcelProperty(value = "定位精度")
|
||||
private Long accuracy;
|
||||
|
||||
/**
|
||||
* 事件时间
|
||||
*/
|
||||
@ExcelProperty(value = "事件时间")
|
||||
private Date eventTime;
|
||||
|
||||
/**
|
||||
* 记录创建时间
|
||||
*/
|
||||
@ExcelProperty(value = "记录创建时间")
|
||||
private Date createTime;
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,64 @@
|
||||
package com.fuyuanshen.equipment.domain.vo;
|
||||
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fuyuanshen.equipment.domain.DeviceFenceStatus;
|
||||
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_fence_status
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2025-09-15
|
||||
*/
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
@AutoMapper(target = DeviceFenceStatus.class)
|
||||
public class DeviceFenceStatusVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@ExcelProperty(value = "")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 设备ID
|
||||
*/
|
||||
@ExcelProperty(value = "设备ID")
|
||||
private String deviceId;
|
||||
|
||||
/**
|
||||
* 围栏ID
|
||||
*/
|
||||
@ExcelProperty(value = "围栏ID")
|
||||
private Long fenceId;
|
||||
|
||||
/**
|
||||
* 状态: 0-在围栏外, 1-在围栏内
|
||||
*/
|
||||
@ExcelProperty(value = "状态: 0-在围栏外, 1-在围栏内")
|
||||
private Long status;
|
||||
|
||||
/**
|
||||
* 上次检查时间
|
||||
*/
|
||||
@ExcelProperty(value = "上次检查时间")
|
||||
private Date lastCheckTime;
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,94 @@
|
||||
package com.fuyuanshen.equipment.domain.vo;
|
||||
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
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 com.fuyuanshen.equipment.domain.DeviceGeoFence;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 电子围栏视图对象 device_geo_fence
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2025-09-11
|
||||
*/
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
@AutoMapper(target = DeviceGeoFence.class)
|
||||
public class DeviceGeoFenceVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 围栏唯一标识
|
||||
*/
|
||||
@ExcelProperty(value = "围栏唯一标识")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 围栏名称
|
||||
*/
|
||||
@ExcelProperty(value = "围栏名称")
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 围栏描述
|
||||
*/
|
||||
@ExcelProperty(value = "围栏描述")
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 围栏区域类型, 0 POLYGON, 1 CIRCLE
|
||||
*/
|
||||
@ExcelProperty(value = "围栏区域类型, 0 POLYGON, 1 CIRCLE")
|
||||
private Integer areaType;
|
||||
|
||||
/**
|
||||
* 围栏坐标数据
|
||||
*/
|
||||
@ExcelProperty(value = "围栏坐标数据")
|
||||
private String coordinates;
|
||||
|
||||
/**
|
||||
* 圆形围栏半径(米)
|
||||
*/
|
||||
@ExcelProperty(value = "圆形围栏半径(米)")
|
||||
private Long radius;
|
||||
|
||||
/**
|
||||
* 是否激活
|
||||
*/
|
||||
@ExcelProperty(value = "是否激活")
|
||||
private Long isActive;
|
||||
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
@ExcelProperty(value = "创建人")
|
||||
private Long createBy;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@ExcelProperty(value = "创建时间")
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@ExcelProperty(value = "更新时间")
|
||||
private Date updateTime;
|
||||
|
||||
|
||||
}
|
||||
@ -27,7 +27,7 @@ public class WebDeviceVo implements Serializable {
|
||||
private String deviceMac;
|
||||
|
||||
/**
|
||||
* 通讯方式 0:4G;1:蓝牙
|
||||
* 通讯方式 0:4G;1:蓝牙,2 4G&蓝牙
|
||||
*/
|
||||
private Integer communicationMode;
|
||||
|
||||
|
||||
@ -25,4 +25,13 @@ public interface DeviceAlarmMapper extends BaseMapperPlus<DeviceAlarm, DeviceAla
|
||||
*/
|
||||
Page<DeviceAlarmVo> selectVoPage( Page pageQuery,@Param("bo") DeviceAlarmBo bo);
|
||||
|
||||
/**
|
||||
* 根据设备IMEI查询最新的一条告警数据
|
||||
*
|
||||
* @param deviceImei 设备IMEI
|
||||
* @return 设备告警
|
||||
*/
|
||||
DeviceAlarmVo selectLatestByDeviceImei(@Param("deviceImei") String deviceImei);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,33 @@
|
||||
package com.fuyuanshen.equipment.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Constants;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
import com.fuyuanshen.equipment.domain.DeviceFenceAccessRecord;
|
||||
import com.fuyuanshen.equipment.domain.vo.DeviceFenceAccessRecordVo;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 围栏进出记录Mapper接口
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2025-09-11
|
||||
*/
|
||||
public interface DeviceFenceAccessRecordMapper extends BaseMapperPlus<DeviceFenceAccessRecord, DeviceFenceAccessRecordVo> {
|
||||
|
||||
/**
|
||||
* 分页查询围栏进出记录列表(包含围栏名称和设备名称)
|
||||
*
|
||||
* @param page 分页参数
|
||||
* @param wrapper 查询条件
|
||||
* @return 围栏进出记录分页列表
|
||||
*/
|
||||
Page<DeviceFenceAccessRecordVo> selectVoPageWithFenceAndDeviceName(Page<DeviceFenceAccessRecord> page, @Param(Constants.WRAPPER) Wrapper<DeviceFenceAccessRecord> wrapper);
|
||||
|
||||
List<DeviceFenceAccessRecordVo> selectVoPageWithFenceAndDeviceName(@Param(Constants.WRAPPER) Wrapper<DeviceFenceAccessRecord> wrapper);
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
package com.fuyuanshen.equipment.mapper;
|
||||
|
||||
import com.fuyuanshen.equipment.domain.DeviceFenceStatus;
|
||||
import com.fuyuanshen.equipment.domain.vo.DeviceFenceStatusVo;
|
||||
import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
|
||||
/**
|
||||
* 设备进入围栏状态Mapper接口
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2025-09-15
|
||||
*/
|
||||
public interface DeviceFenceStatusMapper extends BaseMapperPlus<DeviceFenceStatus, DeviceFenceStatusVo> {
|
||||
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
package com.fuyuanshen.equipment.mapper;
|
||||
|
||||
import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
import com.fuyuanshen.equipment.domain.DeviceGeoFence;
|
||||
import com.fuyuanshen.equipment.domain.vo.DeviceGeoFenceVo;
|
||||
|
||||
/**
|
||||
* 电子围栏Mapper接口
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2025-09-11
|
||||
*/
|
||||
public interface DeviceGeoFenceMapper extends BaseMapperPlus<DeviceGeoFence, DeviceGeoFenceVo> {
|
||||
|
||||
}
|
||||
@ -109,4 +109,12 @@ public interface DeviceMapper extends BaseMapper<Device> {
|
||||
int getUsageDataForMonth(@Param("deviceId") Long deviceId,
|
||||
@Param("year") int year,
|
||||
@Param("month") int month);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据设备IMEI查询设备
|
||||
*
|
||||
* @param deviceImei 设备IMEI
|
||||
* @return 设备信息
|
||||
*/
|
||||
Device selectDeviceByImei(@Param("deviceImei") String deviceImei);
|
||||
}
|
||||
@ -144,4 +144,12 @@ public interface DeviceService extends IService<Device> {
|
||||
* @return
|
||||
*/
|
||||
List<Map<String, Object>> getEquipmentUsageData(Long deviceTypeId, Integer range);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据设备IMEI查询设备
|
||||
*
|
||||
* @param deviceImei 设备IMEI
|
||||
* @return 设备信息
|
||||
*/
|
||||
Device selectDeviceByImei(String deviceImei);
|
||||
}
|
||||
@ -65,4 +65,14 @@ public interface IDeviceAlarmService {
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
|
||||
/**
|
||||
* 根据设备IMEI查询最新的一条告警数据
|
||||
*
|
||||
* @param deviceImei 设备IMEI
|
||||
* @return 设备告警
|
||||
*/
|
||||
DeviceAlarmVo queryLatestByDeviceImei(String deviceImei);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,68 @@
|
||||
package com.fuyuanshen.equipment.service;
|
||||
|
||||
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
|
||||
import com.fuyuanshen.common.mybatis.core.page.PageQuery;
|
||||
import com.fuyuanshen.equipment.domain.bo.DeviceFenceAccessRecordBo;
|
||||
import com.fuyuanshen.equipment.domain.vo.DeviceFenceAccessRecordVo;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 围栏进出记录Service接口
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2025-09-11
|
||||
*/
|
||||
public interface IDeviceFenceAccessRecordService {
|
||||
|
||||
/**
|
||||
* 查询围栏进出记录
|
||||
*
|
||||
* @param id 主键
|
||||
* @return 围栏进出记录
|
||||
*/
|
||||
DeviceFenceAccessRecordVo queryById(Long id);
|
||||
|
||||
/**
|
||||
* 分页查询围栏进出记录列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @param pageQuery 分页参数
|
||||
* @return 围栏进出记录分页列表
|
||||
*/
|
||||
TableDataInfo<DeviceFenceAccessRecordVo> queryPageList(DeviceFenceAccessRecordBo bo, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 查询符合条件的围栏进出记录列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @return 围栏进出记录列表
|
||||
*/
|
||||
List<DeviceFenceAccessRecordVo> queryList(DeviceFenceAccessRecordBo bo);
|
||||
|
||||
/**
|
||||
* 新增围栏进出记录
|
||||
*
|
||||
* @param bo 围栏进出记录
|
||||
* @return 是否新增成功
|
||||
*/
|
||||
Boolean insertByBo(DeviceFenceAccessRecordBo bo);
|
||||
|
||||
/**
|
||||
* 修改围栏进出记录
|
||||
*
|
||||
* @param bo 围栏进出记录
|
||||
* @return 是否修改成功
|
||||
*/
|
||||
Boolean updateByBo(DeviceFenceAccessRecordBo bo);
|
||||
|
||||
/**
|
||||
* 校验并批量删除围栏进出记录信息
|
||||
*
|
||||
* @param ids 待删除的主键集合
|
||||
* @param isValid 是否进行有效性校验
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
}
|
||||
@ -0,0 +1,81 @@
|
||||
package com.fuyuanshen.equipment.service;
|
||||
|
||||
import com.fuyuanshen.equipment.domain.vo.DeviceFenceStatusVo;
|
||||
import com.fuyuanshen.equipment.domain.bo.DeviceFenceStatusBo;
|
||||
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-09-15
|
||||
*/
|
||||
public interface IDeviceFenceStatusService {
|
||||
|
||||
/**
|
||||
* 查询设备进入围栏状态
|
||||
*
|
||||
* @param id 主键
|
||||
* @return 设备进入围栏状态
|
||||
*/
|
||||
DeviceFenceStatusVo queryById(Long id);
|
||||
|
||||
/**
|
||||
* 分页查询设备进入围栏状态列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @param pageQuery 分页参数
|
||||
* @return 设备进入围栏状态分页列表
|
||||
*/
|
||||
TableDataInfo<DeviceFenceStatusVo> queryPageList(DeviceFenceStatusBo bo, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 查询符合条件的设备进入围栏状态列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @return 设备进入围栏状态列表
|
||||
*/
|
||||
List<DeviceFenceStatusVo> queryList(DeviceFenceStatusBo bo);
|
||||
|
||||
/**
|
||||
* 新增设备进入围栏状态
|
||||
*
|
||||
* @param bo 设备进入围栏状态
|
||||
* @return 是否新增成功
|
||||
*/
|
||||
Boolean insertByBo(DeviceFenceStatusBo bo);
|
||||
|
||||
/**
|
||||
* 修改设备进入围栏状态
|
||||
*
|
||||
* @param bo 设备进入围栏状态
|
||||
* @return 是否修改成功
|
||||
*/
|
||||
Boolean updateByBo(DeviceFenceStatusBo bo);
|
||||
|
||||
/**
|
||||
* 校验并批量删除设备进入围栏状态信息
|
||||
*
|
||||
* @param ids 待删除的主键集合
|
||||
* @param isValid 是否进行有效性校验
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
|
||||
|
||||
/**
|
||||
* 查询设备在特定围栏的最新状态
|
||||
*
|
||||
* @param deviceId 设备ID
|
||||
* @param fenceId 围栏ID
|
||||
* @return 最新状态记录,如果不存在则返回null
|
||||
*/
|
||||
DeviceFenceStatusVo getLatestStatusByDeviceAndFence(String deviceId, Long fenceId);
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,78 @@
|
||||
package com.fuyuanshen.equipment.service;
|
||||
|
||||
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
|
||||
import com.fuyuanshen.common.mybatis.core.page.PageQuery;
|
||||
import com.fuyuanshen.equipment.domain.bo.DeviceGeoFenceBo;
|
||||
import com.fuyuanshen.equipment.domain.dto.FenceCheckResponse;
|
||||
import com.fuyuanshen.equipment.domain.query.FenceCheckRequest;
|
||||
import com.fuyuanshen.equipment.domain.vo.DeviceGeoFenceVo;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 电子围栏Service接口
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2025-09-11
|
||||
*/
|
||||
public interface IDeviceGeoFenceService {
|
||||
|
||||
/**
|
||||
* 查询电子围栏
|
||||
*
|
||||
* @param id 主键
|
||||
* @return 电子围栏
|
||||
*/
|
||||
DeviceGeoFenceVo queryById(Long id);
|
||||
|
||||
/**
|
||||
* 分页查询电子围栏列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @param pageQuery 分页参数
|
||||
* @return 电子围栏分页列表
|
||||
*/
|
||||
TableDataInfo<DeviceGeoFenceVo> queryPageList(DeviceGeoFenceBo bo, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 查询符合条件的电子围栏列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @return 电子围栏列表
|
||||
*/
|
||||
List<DeviceGeoFenceVo> queryList(DeviceGeoFenceBo bo);
|
||||
|
||||
/**
|
||||
* 新增电子围栏
|
||||
*
|
||||
* @param bo 电子围栏
|
||||
* @return 是否新增成功
|
||||
*/
|
||||
Boolean insertByBo(DeviceGeoFenceBo bo);
|
||||
|
||||
/**
|
||||
* 修改电子围栏
|
||||
*
|
||||
* @param bo 电子围栏
|
||||
* @return 是否修改成功
|
||||
*/
|
||||
Boolean updateByBo(DeviceGeoFenceBo bo);
|
||||
|
||||
/**
|
||||
* 校验并批量删除电子围栏信息
|
||||
*
|
||||
* @param ids 待删除的主键集合
|
||||
* @param isValid 是否进行有效性校验
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
|
||||
/**
|
||||
* 检查设备位置与围栏的关系
|
||||
*
|
||||
* @param request 位置检查请求
|
||||
* @return 位置检查结果
|
||||
*/
|
||||
FenceCheckResponse checkPosition(FenceCheckRequest request);
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
package com.fuyuanshen.equipment.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import com.fuyuanshen.common.core.utils.MapstructUtils;
|
||||
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
|
||||
import com.fuyuanshen.common.mybatis.core.page.PageQuery;
|
||||
@ -102,11 +103,13 @@ public class DeviceAlarmServiceImpl implements IDeviceAlarmService {
|
||||
*/
|
||||
@Override
|
||||
public Boolean insertByBo(DeviceAlarmBo bo) {
|
||||
DeviceAlarm add = MapstructUtils.convert(bo, DeviceAlarm.class);
|
||||
validEntityBeforeSave(add);
|
||||
boolean flag = baseMapper.insert(add) > 0;
|
||||
DeviceAlarm deviceAlarm = new DeviceAlarm();
|
||||
// DeviceAlarm add = MapstructUtils.convert(bo, DeviceAlarm.class);
|
||||
BeanUtil.copyProperties(bo, deviceAlarm);
|
||||
validEntityBeforeSave(deviceAlarm);
|
||||
boolean flag = baseMapper.insert(deviceAlarm) > 0;
|
||||
if (flag) {
|
||||
bo.setId(add.getId());
|
||||
bo.setId(deviceAlarm.getId());
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
@ -145,4 +148,17 @@ public class DeviceAlarmServiceImpl implements IDeviceAlarmService {
|
||||
}
|
||||
return baseMapper.deleteByIds(ids) > 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据设备IMEI查询最新的一条告警数据
|
||||
*
|
||||
* @param deviceImei 设备IMEI
|
||||
* @return 设备告警
|
||||
*/
|
||||
@Override
|
||||
public DeviceAlarmVo queryLatestByDeviceImei(String deviceImei) {
|
||||
return baseMapper.selectLatestByDeviceImei(deviceImei);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,143 @@
|
||||
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 com.fuyuanshen.equipment.domain.DeviceFenceAccessRecord;
|
||||
import com.fuyuanshen.equipment.domain.bo.DeviceFenceAccessRecordBo;
|
||||
import com.fuyuanshen.equipment.domain.vo.DeviceFenceAccessRecordVo;
|
||||
import com.fuyuanshen.equipment.mapper.DeviceFenceAccessRecordMapper;
|
||||
import com.fuyuanshen.equipment.service.IDeviceFenceAccessRecordService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 围栏进出记录Service业务层处理
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2025-09-11
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class DeviceFenceAccessRecordServiceImpl implements IDeviceFenceAccessRecordService {
|
||||
|
||||
private final DeviceFenceAccessRecordMapper baseMapper;
|
||||
|
||||
/**
|
||||
* 查询围栏进出记录
|
||||
*
|
||||
* @param id 主键
|
||||
* @return 围栏进出记录
|
||||
*/
|
||||
@Override
|
||||
public DeviceFenceAccessRecordVo queryById(Long id) {
|
||||
return baseMapper.selectVoById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询围栏进出记录列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @param pageQuery 分页参数
|
||||
* @return 围栏进出记录分页列表
|
||||
*/
|
||||
@Override
|
||||
public TableDataInfo<DeviceFenceAccessRecordVo> queryPageList(DeviceFenceAccessRecordBo bo, PageQuery pageQuery) {
|
||||
LambdaQueryWrapper<DeviceFenceAccessRecord> lqw = buildQueryWrapper(bo);
|
||||
Page<DeviceFenceAccessRecordVo> result = baseMapper.selectVoPageWithFenceAndDeviceName(pageQuery.build(), lqw);
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 查询符合条件的围栏进出记录列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @return 围栏进出记录列表
|
||||
*/
|
||||
@Override
|
||||
public List<DeviceFenceAccessRecordVo> queryList(DeviceFenceAccessRecordBo bo) {
|
||||
LambdaQueryWrapper<DeviceFenceAccessRecord> lqw = buildQueryWrapper(bo);
|
||||
return baseMapper.selectVoPageWithFenceAndDeviceName(lqw);
|
||||
}
|
||||
|
||||
|
||||
private LambdaQueryWrapper<DeviceFenceAccessRecord> buildQueryWrapper(DeviceFenceAccessRecordBo bo) {
|
||||
Map<String, Object> params = bo.getParams();
|
||||
LambdaQueryWrapper<DeviceFenceAccessRecord> lqw = Wrappers.lambdaQuery();
|
||||
// lqw.orderByAsc(DeviceFenceAccessRecord::getId);
|
||||
lqw.eq(bo.getFenceId() != null, DeviceFenceAccessRecord::getFenceId, bo.getFenceId());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getDeviceId()), DeviceFenceAccessRecord::getDeviceId, bo.getDeviceId());
|
||||
lqw.eq(bo.getUserId() != null, DeviceFenceAccessRecord::getUserId, bo.getUserId());
|
||||
lqw.eq(bo.getEventType() != null, DeviceFenceAccessRecord::getEventType, bo.getEventType());
|
||||
lqw.eq(bo.getLatitude() != null, DeviceFenceAccessRecord::getLatitude, bo.getLatitude());
|
||||
lqw.eq(bo.getLongitude() != null, DeviceFenceAccessRecord::getLongitude, bo.getLongitude());
|
||||
lqw.eq(bo.getAccuracy() != null, DeviceFenceAccessRecord::getAccuracy, bo.getAccuracy());
|
||||
lqw.eq(bo.getEventTime() != null, DeviceFenceAccessRecord::getEventTime, bo.getEventTime());
|
||||
lqw.eq(bo.getCreateTime() != null, DeviceFenceAccessRecord::getCreateTime, bo.getCreateTime());
|
||||
return lqw;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 新增围栏进出记录
|
||||
*
|
||||
* @param bo 围栏进出记录
|
||||
* @return 是否新增成功
|
||||
*/
|
||||
@Override
|
||||
public Boolean insertByBo(DeviceFenceAccessRecordBo bo) {
|
||||
DeviceFenceAccessRecord add = MapstructUtils.convert(bo, DeviceFenceAccessRecord.class);
|
||||
validEntityBeforeSave(add);
|
||||
boolean flag = baseMapper.insert(add) > 0;
|
||||
if (flag) {
|
||||
bo.setId(add.getId());
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改围栏进出记录
|
||||
*
|
||||
* @param bo 围栏进出记录
|
||||
* @return 是否修改成功
|
||||
*/
|
||||
@Override
|
||||
public Boolean updateByBo(DeviceFenceAccessRecordBo bo) {
|
||||
DeviceFenceAccessRecord update = MapstructUtils.convert(bo, DeviceFenceAccessRecord.class);
|
||||
validEntityBeforeSave(update);
|
||||
return baseMapper.updateById(update) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存前的数据校验
|
||||
*/
|
||||
private void validEntityBeforeSave(DeviceFenceAccessRecord entity) {
|
||||
// TODO 做一些数据校验,如唯一约束
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验并批量删除围栏进出记录信息
|
||||
*
|
||||
* @param ids 待删除的主键集合
|
||||
* @param isValid 是否进行有效性校验
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
@Override
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||||
if (isValid) {
|
||||
// TODO 做一些业务上的校验,判断是否需要校验
|
||||
}
|
||||
return baseMapper.deleteByIds(ids) > 0;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,152 @@
|
||||
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.DeviceFenceStatusBo;
|
||||
import com.fuyuanshen.equipment.domain.vo.DeviceFenceStatusVo;
|
||||
import com.fuyuanshen.equipment.domain.DeviceFenceStatus;
|
||||
import com.fuyuanshen.equipment.mapper.DeviceFenceStatusMapper;
|
||||
import com.fuyuanshen.equipment.service.IDeviceFenceStatusService;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 设备进入围栏状态Service业务层处理
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2025-09-15
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class DeviceFenceStatusServiceImpl implements IDeviceFenceStatusService {
|
||||
|
||||
private final DeviceFenceStatusMapper baseMapper;
|
||||
|
||||
/**
|
||||
* 查询设备进入围栏状态
|
||||
*
|
||||
* @param id 主键
|
||||
* @return 设备进入围栏状态
|
||||
*/
|
||||
@Override
|
||||
public DeviceFenceStatusVo queryById(Long id){
|
||||
return baseMapper.selectVoById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询设备进入围栏状态列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @param pageQuery 分页参数
|
||||
* @return 设备进入围栏状态分页列表
|
||||
*/
|
||||
@Override
|
||||
public TableDataInfo<DeviceFenceStatusVo> queryPageList(DeviceFenceStatusBo bo, PageQuery pageQuery) {
|
||||
LambdaQueryWrapper<DeviceFenceStatus> lqw = buildQueryWrapper(bo);
|
||||
Page<DeviceFenceStatusVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询符合条件的设备进入围栏状态列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @return 设备进入围栏状态列表
|
||||
*/
|
||||
@Override
|
||||
public List<DeviceFenceStatusVo> queryList(DeviceFenceStatusBo bo) {
|
||||
LambdaQueryWrapper<DeviceFenceStatus> lqw = buildQueryWrapper(bo);
|
||||
return baseMapper.selectVoList(lqw);
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<DeviceFenceStatus> buildQueryWrapper(DeviceFenceStatusBo bo) {
|
||||
Map<String, Object> params = bo.getParams();
|
||||
LambdaQueryWrapper<DeviceFenceStatus> lqw = Wrappers.lambdaQuery();
|
||||
lqw.orderByAsc(DeviceFenceStatus::getId);
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getDeviceId()), DeviceFenceStatus::getDeviceId, bo.getDeviceId());
|
||||
lqw.eq(bo.getFenceId() != null, DeviceFenceStatus::getFenceId, bo.getFenceId());
|
||||
lqw.eq(bo.getStatus() != null, DeviceFenceStatus::getStatus, bo.getStatus());
|
||||
lqw.eq(bo.getLastCheckTime() != null, DeviceFenceStatus::getLastCheckTime, bo.getLastCheckTime());
|
||||
return lqw;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增设备进入围栏状态
|
||||
*
|
||||
* @param bo 设备进入围栏状态
|
||||
* @return 是否新增成功
|
||||
*/
|
||||
@Override
|
||||
public Boolean insertByBo(DeviceFenceStatusBo bo) {
|
||||
DeviceFenceStatus add = MapstructUtils.convert(bo, DeviceFenceStatus.class);
|
||||
validEntityBeforeSave(add);
|
||||
boolean flag = baseMapper.insert(add) > 0;
|
||||
if (flag) {
|
||||
bo.setId(add.getId());
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改设备进入围栏状态
|
||||
*
|
||||
* @param bo 设备进入围栏状态
|
||||
* @return 是否修改成功
|
||||
*/
|
||||
@Override
|
||||
public Boolean updateByBo(DeviceFenceStatusBo bo) {
|
||||
DeviceFenceStatus update = MapstructUtils.convert(bo, DeviceFenceStatus.class);
|
||||
validEntityBeforeSave(update);
|
||||
return baseMapper.updateById(update) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存前的数据校验
|
||||
*/
|
||||
private void validEntityBeforeSave(DeviceFenceStatus entity){
|
||||
//TODO 做一些数据校验,如唯一约束
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验并批量删除设备进入围栏状态信息
|
||||
*
|
||||
* @param ids 待删除的主键集合
|
||||
* @param isValid 是否进行有效性校验
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
@Override
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||||
if(isValid){
|
||||
//TODO 做一些业务上的校验,判断是否需要校验
|
||||
}
|
||||
return baseMapper.deleteByIds(ids) > 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 查询设备在特定围栏的最新状态
|
||||
*/
|
||||
@Override
|
||||
public DeviceFenceStatusVo getLatestStatusByDeviceAndFence(String deviceId, Long fenceId) {
|
||||
LambdaQueryWrapper<DeviceFenceStatus> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(DeviceFenceStatus::getDeviceId, deviceId);
|
||||
lqw.eq(DeviceFenceStatus::getFenceId, fenceId);
|
||||
lqw.orderByDesc(DeviceFenceStatus::getLastCheckTime);
|
||||
lqw.last("LIMIT 1");
|
||||
DeviceFenceStatus status = baseMapper.selectOne(lqw);
|
||||
return MapstructUtils.convert(status, DeviceFenceStatusVo.class);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,266 @@
|
||||
package com.fuyuanshen.equipment.service.impl;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
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 com.fuyuanshen.equipment.domain.DeviceGeoFence;
|
||||
import com.fuyuanshen.equipment.domain.bo.DeviceFenceAccessRecordBo;
|
||||
import com.fuyuanshen.equipment.domain.bo.DeviceFenceStatusBo;
|
||||
import com.fuyuanshen.equipment.domain.bo.DeviceGeoFenceBo;
|
||||
import com.fuyuanshen.equipment.domain.dto.FenceCheckResponse;
|
||||
import com.fuyuanshen.equipment.domain.query.FenceCheckRequest;
|
||||
import com.fuyuanshen.equipment.domain.vo.DeviceFenceStatusVo;
|
||||
import com.fuyuanshen.equipment.domain.vo.DeviceGeoFenceVo;
|
||||
import com.fuyuanshen.equipment.mapper.DeviceGeoFenceMapper;
|
||||
import com.fuyuanshen.equipment.service.IDeviceFenceAccessRecordService;
|
||||
import com.fuyuanshen.equipment.service.IDeviceFenceStatusService;
|
||||
import com.fuyuanshen.equipment.service.IDeviceGeoFenceService;
|
||||
import com.fuyuanshen.equipment.utils.map.GeoFenceChecker;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 电子围栏Service业务层处理
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2025-09-11
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class DeviceGeoFenceServiceImpl implements IDeviceGeoFenceService {
|
||||
|
||||
private final DeviceGeoFenceMapper baseMapper;
|
||||
|
||||
private final IDeviceFenceStatusService fenceStatusService; // 添加此行
|
||||
|
||||
private final IDeviceFenceAccessRecordService fenceAccessRecordService; // 添加此行
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 查询电子围栏
|
||||
*
|
||||
* @param id 主键
|
||||
* @return 电子围栏
|
||||
*/
|
||||
@Override
|
||||
public DeviceGeoFenceVo queryById(Long id) {
|
||||
return baseMapper.selectVoById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询电子围栏列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @param pageQuery 分页参数
|
||||
* @return 电子围栏分页列表
|
||||
*/
|
||||
@Override
|
||||
public TableDataInfo<DeviceGeoFenceVo> queryPageList(DeviceGeoFenceBo bo, PageQuery pageQuery) {
|
||||
LambdaQueryWrapper<DeviceGeoFence> lqw = buildQueryWrapper(bo);
|
||||
Page<DeviceGeoFenceVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询符合条件的电子围栏列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @return 电子围栏列表
|
||||
*/
|
||||
@Override
|
||||
public List<DeviceGeoFenceVo> queryList(DeviceGeoFenceBo bo) {
|
||||
LambdaQueryWrapper<DeviceGeoFence> lqw = buildQueryWrapper(bo);
|
||||
return baseMapper.selectVoList(lqw);
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<DeviceGeoFence> buildQueryWrapper(DeviceGeoFenceBo bo) {
|
||||
Map<String, Object> params = bo.getParams();
|
||||
LambdaQueryWrapper<DeviceGeoFence> lqw = Wrappers.lambdaQuery();
|
||||
lqw.orderByAsc(DeviceGeoFence::getId);
|
||||
lqw.like(StringUtils.isNotBlank(bo.getName()), DeviceGeoFence::getName, bo.getName());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getDescription()), DeviceGeoFence::getDescription, bo.getDescription());
|
||||
lqw.eq(bo.getAreaType() != null, DeviceGeoFence::getAreaType, bo.getAreaType());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getCoordinates()), DeviceGeoFence::getCoordinates, bo.getCoordinates());
|
||||
lqw.eq(bo.getRadius() != null, DeviceGeoFence::getRadius, bo.getRadius());
|
||||
lqw.eq(bo.getIsActive() != null, DeviceGeoFence::getIsActive, bo.getIsActive());
|
||||
lqw.eq(bo.getCreateBy() != null, DeviceGeoFence::getCreateBy, bo.getCreateBy());
|
||||
lqw.eq(bo.getCreateTime() != null, DeviceGeoFence::getCreateTime, bo.getCreateTime());
|
||||
lqw.eq(bo.getUpdateTime() != null, DeviceGeoFence::getUpdateTime, bo.getUpdateTime());
|
||||
return lqw;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 新增电子围栏
|
||||
*
|
||||
* @param bo 电子围栏
|
||||
* @return 是否新增成功
|
||||
*/
|
||||
@Override
|
||||
public Boolean insertByBo(DeviceGeoFenceBo bo) {
|
||||
DeviceGeoFence add = MapstructUtils.convert(bo, DeviceGeoFence.class);
|
||||
validEntityBeforeSave(add);
|
||||
boolean flag = baseMapper.insert(add) > 0;
|
||||
if (flag) {
|
||||
bo.setId(add.getId());
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改电子围栏
|
||||
*
|
||||
* @param bo 电子围栏
|
||||
* @return 是否修改成功
|
||||
*/
|
||||
@Override
|
||||
public Boolean updateByBo(DeviceGeoFenceBo bo) {
|
||||
DeviceGeoFence update = MapstructUtils.convert(bo, DeviceGeoFence.class);
|
||||
validEntityBeforeSave(update);
|
||||
return baseMapper.updateById(update) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存前的数据校验
|
||||
*/
|
||||
private void validEntityBeforeSave(DeviceGeoFence entity) {
|
||||
// TODO 做一些数据校验,如唯一约束
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验并批量删除电子围栏信息
|
||||
*
|
||||
* @param ids 待删除的主键集合
|
||||
* @param isValid 是否进行有效性校验
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
@Override
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||||
if (isValid) {
|
||||
// TODO 做一些业务上的校验,判断是否需要校验
|
||||
}
|
||||
return baseMapper.deleteByIds(ids) > 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 检查设备位置与围栏的关系
|
||||
*
|
||||
* @param request 位置检查请求
|
||||
* @return 位置检查结果
|
||||
*/
|
||||
@Override
|
||||
public FenceCheckResponse checkPosition(FenceCheckRequest request) {
|
||||
// 1. 获取所有激活的围栏
|
||||
DeviceGeoFenceBo bo = new DeviceGeoFenceBo();
|
||||
bo.setIsActive(1L); // 假设1表示激活状态
|
||||
List<DeviceGeoFenceVo> activeFences = queryList(bo);
|
||||
|
||||
// 2. 初始化响应对象
|
||||
FenceCheckResponse response = new FenceCheckResponse();
|
||||
response.setDeviceId(request.getDeviceId());
|
||||
response.setCheckTime(System.currentTimeMillis());
|
||||
response.setEnteredFences(new ArrayList<>());
|
||||
response.setExitedFences(new ArrayList<>());
|
||||
response.setCurrentFences(new ArrayList<>());
|
||||
|
||||
// 3. 遍历所有激活的围栏
|
||||
for (DeviceGeoFenceVo fence : activeFences) {
|
||||
// 解析围栏坐标
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
List<GeoFenceChecker.Coordinate> coordinateList = new ArrayList<>();
|
||||
try {
|
||||
coordinateList = objectMapper.readValue(fence.getCoordinates(),
|
||||
new TypeReference<List<GeoFenceChecker.Coordinate>>() {
|
||||
});
|
||||
} catch (Exception e) {
|
||||
log.error("坐标数据解析失败: {}", e.getMessage());
|
||||
continue; // 解析失败则跳过该围栏
|
||||
}
|
||||
|
||||
// 检查设备是否在围栏内
|
||||
boolean pointInFence = GeoFenceChecker.isPointInFence(
|
||||
request.getLatitude(),
|
||||
request.getLongitude(),
|
||||
fence.getAreaType(),
|
||||
coordinateList,
|
||||
fence.getRadius());
|
||||
|
||||
// 创建围栏信息对象
|
||||
FenceCheckResponse.FenceInfo fenceInfo = new FenceCheckResponse.FenceInfo();
|
||||
fenceInfo.setFenceId(fence.getId());
|
||||
fenceInfo.setFenceName(fence.getName());
|
||||
fenceInfo.setFenceType(fence.getAreaType());
|
||||
|
||||
// // 查询设备在该围栏的历史状态
|
||||
// DeviceFenceStatusBo statusQuery = new DeviceFenceStatusBo();
|
||||
// statusQuery.setDeviceId(request.getDeviceId());
|
||||
// statusQuery.setFenceId(fence.getId());
|
||||
// List<DeviceFenceStatusVo> statusHistory = fenceStatusService.queryList(statusQuery);
|
||||
//
|
||||
// // 获取最新的状态记录
|
||||
// DeviceFenceStatusVo latestStatus = statusHistory.stream()
|
||||
// .max(Comparator.comparing(DeviceFenceStatusVo::getLastCheckTime))
|
||||
// .orElse(null);
|
||||
|
||||
// 查询设备在该围栏的最新状态
|
||||
DeviceFenceStatusVo latestStatus = fenceStatusService.getLatestStatusByDeviceAndFence(
|
||||
request.getDeviceId(), fence.getId());
|
||||
|
||||
// 判断设备与围栏的关系变化 1=进入围栏,2=离开围栏
|
||||
Long previousStatus = latestStatus != null ? latestStatus.getStatus() : 2L; // 默认在围栏外
|
||||
Long currentStatus = pointInFence ? 1L : 2L; // 当前状态:1-在围栏内,2-在围栏外
|
||||
|
||||
// 如果状态发生变化,则记录
|
||||
if (!previousStatus.equals(currentStatus)) {
|
||||
// 创建新的状态记录
|
||||
DeviceFenceStatusBo newStatus = new DeviceFenceStatusBo();
|
||||
newStatus.setDeviceId(request.getDeviceId());
|
||||
newStatus.setFenceId(fence.getId());
|
||||
newStatus.setStatus(currentStatus);
|
||||
newStatus.setLastCheckTime(new Date());
|
||||
|
||||
// 保存状态记录
|
||||
fenceStatusService.insertByBo(newStatus);
|
||||
|
||||
// 添加围栏进出记录
|
||||
DeviceFenceAccessRecordBo recordBo = new DeviceFenceAccessRecordBo();
|
||||
recordBo.setDeviceId(request.getDeviceId());
|
||||
recordBo.setFenceId(fence.getId());
|
||||
recordBo.setLatitude(request.getLatitude());
|
||||
recordBo.setLongitude(request.getLongitude());
|
||||
recordBo.setEventTime(new Date());
|
||||
// 1表示进入围栏,2表示离开围栏
|
||||
recordBo.setEventType(currentStatus);
|
||||
fenceAccessRecordService.insertByBo(recordBo);
|
||||
|
||||
// 根据状态变化更新响应
|
||||
if (currentStatus == 1L) {
|
||||
// 设备进入围栏
|
||||
response.getEnteredFences().add(fenceInfo);
|
||||
} else {
|
||||
// 设备离开围栏
|
||||
response.getExitedFences().add(fenceInfo);
|
||||
}
|
||||
} else if (currentStatus == 1L) {
|
||||
// 状态未变,但设备仍在围栏内
|
||||
response.getCurrentFences().add(fenceInfo);
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -40,7 +40,9 @@ import com.fuyuanshen.equipment.service.DeviceAssignmentsService;
|
||||
import com.fuyuanshen.equipment.service.DeviceService;
|
||||
import com.fuyuanshen.equipment.service.DeviceTypeGrantsService;
|
||||
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 lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
@ -67,6 +69,8 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
|
||||
|
||||
public static final String USER_ID_SEPARATOR = ":";
|
||||
|
||||
private final ISysRoleService roleService;
|
||||
|
||||
@Value("${file.device.pic}")
|
||||
private String filePath;
|
||||
@Value("${file.device.ip}")
|
||||
@ -109,6 +113,16 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
|
||||
if (username.equals("admin")) {
|
||||
criteria.setIsAdmin(true);
|
||||
}
|
||||
// 角色管理员
|
||||
Long userId = LoginHelper.getUserId();
|
||||
List<SysRoleVo> roles = roleService.selectRolesAuthByUserId(userId);
|
||||
if (CollectionUtil.isNotEmpty(roles)) {
|
||||
for (SysRoleVo role : roles) {
|
||||
if (role.getRoleKey().equals("admin")) {
|
||||
criteria.setIsAdmin(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IPage<Device> devices = deviceMapper.findAll(criteria, page);
|
||||
|
||||
@ -238,7 +252,8 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
|
||||
|
||||
DeviceTypeQueryCriteria deviceTypeQueryCriteria = new DeviceTypeQueryCriteria();
|
||||
deviceTypeQueryCriteria.setDeviceTypeId(deviceAssignments.getDeviceId());
|
||||
deviceTypeQueryCriteria.setCustomerId(LoginHelper.getUserId());
|
||||
// 被授权的客户
|
||||
// deviceTypeQueryCriteria.setCustomerId(LoginHelper.getUserId());
|
||||
List<DeviceType> deviceTypes = deviceTypeMapper.findAll(deviceTypeQueryCriteria);
|
||||
if (deviceTypes.isEmpty()) {
|
||||
throw new Exception("设备类型不存在!!!");
|
||||
@ -252,9 +267,13 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
|
||||
|
||||
// 处理上传的图片
|
||||
if (deviceForm.getFile() != null) {
|
||||
SysOssVo upload = ossService.upload(deviceForm.getFile());
|
||||
// 设置图片路径
|
||||
deviceForm.setDevicePic(upload.getUrl());
|
||||
SysOssVo oss = ossService.upload(deviceForm.getFile());
|
||||
// 强制将HTTP替换为HTTPS
|
||||
if (oss.getUrl() != null && oss.getUrl().startsWith("http://")) {
|
||||
oss.setUrl(oss.getUrl().replaceFirst("^http://", "https://"));
|
||||
}
|
||||
deviceForm.setDevicePic(oss.getUrl());
|
||||
}
|
||||
|
||||
// 更新字段
|
||||
@ -650,7 +669,7 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
|
||||
* 获取设备使用数据
|
||||
*
|
||||
* @param deviceTypeId 设备ID
|
||||
* @param range 时间范围 1:半年 2:一年
|
||||
* @param range 时间范围 1:半年 2:一年
|
||||
* @return 每月使用数据列表
|
||||
*/
|
||||
@Override
|
||||
@ -660,4 +679,9 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Device selectDeviceByImei(String deviceImei) {
|
||||
return baseMapper.selectDeviceByImei(deviceImei);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -19,6 +19,8 @@ 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.system.domain.vo.SysRoleVo;
|
||||
import com.fuyuanshen.system.service.ISysRoleService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@ -43,6 +45,8 @@ public class DeviceTypeServiceImpl extends ServiceImpl<DeviceTypeMapper, DeviceT
|
||||
private final DeviceTypeGrantsMapper deviceTypeGrantsMapper;
|
||||
private final DeviceAssignmentsMapper deviceAssignmentsMapper;
|
||||
|
||||
private final ISysRoleService roleService;
|
||||
|
||||
|
||||
/**
|
||||
* 分页查询设备类型
|
||||
@ -54,8 +58,23 @@ public class DeviceTypeServiceImpl extends ServiceImpl<DeviceTypeMapper, DeviceT
|
||||
@Override
|
||||
public TableDataInfo<DeviceType> queryAll(DeviceTypeQueryCriteria criteria, Page<DeviceType> page) {
|
||||
// 管理员
|
||||
String username = LoginHelper.getUsername();
|
||||
if (!username.equals("admin")) {
|
||||
// String username = LoginHelper.getUsername();
|
||||
// if (!username.equals("admin")) {
|
||||
// criteria.setCustomerId(LoginHelper.getUserId());
|
||||
// }
|
||||
// 角色管理员
|
||||
Long userId = LoginHelper.getUserId();
|
||||
List<SysRoleVo> roles = roleService.selectRolesAuthByUserId(userId);
|
||||
boolean isAdmin = false;
|
||||
if (CollectionUtil.isNotEmpty(roles)) {
|
||||
for (SysRoleVo role : roles) {
|
||||
if (role.getRoleKey().contains("admin")) {
|
||||
isAdmin = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!isAdmin) {
|
||||
criteria.setCustomerId(LoginHelper.getUserId());
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,24 @@
|
||||
package com.fuyuanshen.equipment.utils.map;
|
||||
|
||||
/**
|
||||
* @author: 默苍璃
|
||||
* @date: 2025-09-1110:27
|
||||
*/
|
||||
public class Coordinate {
|
||||
private double lat;
|
||||
private double lng;
|
||||
|
||||
// 构造函数
|
||||
public Coordinate() {}
|
||||
|
||||
public Coordinate(double lat, double lng) {
|
||||
this.lat = lat;
|
||||
this.lng = lng;
|
||||
}
|
||||
|
||||
// getter和setter方法
|
||||
public double getLat() { return lat; }
|
||||
public void setLat(double lat) { this.lat = lat; }
|
||||
public double getLng() { return lng; }
|
||||
public void setLng(double lng) { this.lng = lng; }
|
||||
}
|
||||
@ -0,0 +1,135 @@
|
||||
package com.fuyuanshen.equipment.utils.map;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author: 默苍璃
|
||||
* @date: 2025-09-1110:13
|
||||
*/
|
||||
public class GeoFenceChecker {
|
||||
|
||||
/**
|
||||
* 点是否在围栏内
|
||||
*
|
||||
* @param pointLat 点的纬度
|
||||
* @param pointLng 点的经度
|
||||
* @param fenceType 围栏类型:0 POLYGON 或 1 CIRCLE
|
||||
* @param coordinates 围栏坐标点列表
|
||||
* @param radius 圆形围栏半径(米),仅对CIRCLE类型有效
|
||||
* @return true表示在围栏内,false表示在围栏外
|
||||
*/
|
||||
public static boolean isPointInFence(double pointLat, double pointLng,
|
||||
Integer fenceType,
|
||||
List<Coordinate> coordinates,
|
||||
Long radius) {
|
||||
if (fenceType == 0) {
|
||||
return isPointInPolygon(pointLat, pointLng, coordinates);
|
||||
} else if (fenceType == 1) {
|
||||
if (coordinates == null || coordinates.isEmpty() || radius == null) {
|
||||
return false;
|
||||
}
|
||||
// 圆形围栏只有一个坐标点(圆心)
|
||||
Coordinate center = coordinates.get(0);
|
||||
return isPointInCircle(pointLat, pointLng, center.lat, center.lng, radius);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用射线投射算法判断点是否在多边形内
|
||||
*
|
||||
* @param pointLat 点的纬度
|
||||
* @param pointLng 点的经度
|
||||
* @param polygon 多边形顶点坐标列表
|
||||
* @return true表示在多边形内,false表示在多边形外
|
||||
*/
|
||||
public static boolean isPointInPolygon(double pointLat, double pointLng,
|
||||
List<Coordinate> polygon) {
|
||||
if (polygon == null || polygon.size() < 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int intersectCount = 0;
|
||||
int vertexCount = polygon.size();
|
||||
|
||||
// 遍历多边形的每条边
|
||||
for (int i = 0; i < vertexCount; i++) {
|
||||
Coordinate p1 = polygon.get(i);
|
||||
Coordinate p2 = polygon.get((i + 1) % vertexCount);
|
||||
|
||||
// 检查点是否在边的y范围内
|
||||
if (p1.lat != p2.lat &&
|
||||
(pointLat >= Math.min(p1.lat, p2.lat)) &&
|
||||
(pointLat < Math.max(p1.lat, p2.lat))) {
|
||||
|
||||
// 计算边与从点发出的水平射线的交点经度
|
||||
double intersectLng = (pointLat - p1.lat) * (p2.lng - p1.lng) / (p2.lat - p1.lat) + p1.lng;
|
||||
|
||||
// 如果交点在点的右侧,则计数加1
|
||||
if (pointLng < intersectLng) {
|
||||
intersectCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 奇数个交点表示点在多边形内
|
||||
return intersectCount % 2 == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断点是否在圆形内
|
||||
*
|
||||
* @param pointLat 点的纬度
|
||||
* @param pointLng 点的经度
|
||||
* @param centerLat 圆心纬度
|
||||
* @param centerLng 圆心经度
|
||||
* @param radius 半径(米)
|
||||
* @return true表示在圆内,false表示在圆外
|
||||
*/
|
||||
public static boolean isPointInCircle(double pointLat, double pointLng,
|
||||
double centerLat, double centerLng,
|
||||
Long radius) {
|
||||
double distance = calculateDistance(pointLat, pointLng, centerLat, centerLng);
|
||||
return distance <= radius;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两点间距离(使用Haversine公式)
|
||||
*
|
||||
* @param lat1 点1纬度
|
||||
* @param lng1 点1经度
|
||||
* @param lat2 点2纬度
|
||||
* @param lng2 点2经度
|
||||
* @return 距离(米)
|
||||
*/
|
||||
public static double calculateDistance(double lat1, double lng1, double lat2, double lng2) {
|
||||
final double EARTH_RADIUS = 6371000; // 地球半径(米)
|
||||
|
||||
double dLat = Math.toRadians(lat2 - lat1);
|
||||
double dLng = Math.toRadians(lng2 - lng1);
|
||||
|
||||
double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
||||
Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) *
|
||||
Math.sin(dLng / 2) * Math.sin(dLng / 2);
|
||||
|
||||
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||
|
||||
return EARTH_RADIUS * c;
|
||||
}
|
||||
|
||||
/**
|
||||
* 坐标点类
|
||||
*/
|
||||
public static class Coordinate {
|
||||
public double lat;
|
||||
public double lng;
|
||||
|
||||
public Coordinate() {
|
||||
}
|
||||
|
||||
public Coordinate(double lat, double lng) {
|
||||
this.lat = lat;
|
||||
this.lng = lng;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6,17 +6,21 @@
|
||||
|
||||
<!-- 查询设备告警列表 -->
|
||||
<select id="selectVoPage" resultType="com.fuyuanshen.equipment.domain.vo.DeviceAlarmVo">
|
||||
select *, d.device_mac as deviceMac, d.device_imei as deviceImei,
|
||||
select da.*, d.device_mac as deviceMac, d.device_imei as deviceImei,d.device_name as deviceName,
|
||||
d.device_type as deviceType,
|
||||
d.type_name as deviceTypeName, d.device_pic as devicePic
|
||||
from device_alarm da
|
||||
left join device d on da.device_id = d.id
|
||||
left join device_type dt on dt.id = da.device_type
|
||||
left join device_type dt on dt.id = d.device_type
|
||||
<where>
|
||||
<if test="bo.content != null">
|
||||
and d.device_name like concat('%', #{bo.content}, '%') or dt.type_name like concat('%', #{bo.content}, '%')
|
||||
</if>
|
||||
<if test="bo.deviceName != null">
|
||||
and da.device_name = #{bo.deviceName}
|
||||
and d.device_name like concat('%', #{bo.deviceName}, '%')
|
||||
</if>
|
||||
<if test="bo.deviceType != null">
|
||||
and da.device_type = #{bo.deviceType}
|
||||
and dt.id = #{bo.deviceType}
|
||||
</if>
|
||||
<if test="bo.deviceAction != null">
|
||||
and da.device_action = #{bo.deviceAction}
|
||||
@ -30,4 +34,20 @@
|
||||
</where>
|
||||
</select>
|
||||
|
||||
|
||||
<!-- 根据设备IMEI查询最新的一条告警数据 -->
|
||||
<select id="selectLatestByDeviceImei" resultType="com.fuyuanshen.equipment.domain.vo.DeviceAlarmVo">
|
||||
select *, d.device_mac as deviceMac, d.device_imei as deviceImei,
|
||||
d.type_name as deviceTypeName, d.device_pic as devicePic
|
||||
from device_alarm da
|
||||
left join device d on da.device_id = d.id
|
||||
left join device_type dt on dt.id = da.device_type
|
||||
<where>
|
||||
da.device_imei = #{deviceImei}
|
||||
</where>
|
||||
order by da.create_time desc
|
||||
limit 1
|
||||
</select>
|
||||
|
||||
|
||||
</mapper>
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.fuyuanshen.equipment.mapper.DeviceFenceAccessRecordMapper">
|
||||
|
||||
<!-- 修改后的分页查询围栏进出记录列表 -->
|
||||
<select id="selectVoPageWithFenceAndDeviceName"
|
||||
resultType="com.fuyuanshen.equipment.domain.vo.DeviceFenceAccessRecordVo">
|
||||
SELECT r.id,
|
||||
r.fence_id,
|
||||
f.name AS fence_name,
|
||||
r.device_id,
|
||||
d.device_name,
|
||||
r.user_id,
|
||||
r.event_type,
|
||||
r.latitude,
|
||||
r.longitude,
|
||||
r.accuracy,
|
||||
r.event_time,
|
||||
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
|
||||
${ew.customSqlSegment}
|
||||
ORDER BY r.id ASC
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.fuyuanshen.equipment.mapper.DeviceFenceStatusMapper">
|
||||
|
||||
</mapper>
|
||||
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.fuyuanshen.equipment.mapper.DeviceGeoFenceMapper">
|
||||
|
||||
</mapper>
|
||||
@ -251,12 +251,13 @@
|
||||
dt.pc_model_dictionary detailPageUrl,
|
||||
ap.name personnelBy,
|
||||
d.device_status,
|
||||
d.online_status,
|
||||
c.binding_time
|
||||
from device d
|
||||
inner join device_type dt on d.device_type = dt.id
|
||||
inner join app_device_bind_record c on d.id = c.device_id
|
||||
inner join app_device_bind_record c on d.id = c.device_id and c.communication_mode = 0
|
||||
left join app_personnel_info ap on ap.device_id = d.id
|
||||
where dt.communication_mode = 0
|
||||
where dt.communication_mode in (0, 2)
|
||||
<if test="criteria.deviceType != null">
|
||||
and d.device_type = #{criteria.deviceType}
|
||||
</if>
|
||||
@ -325,6 +326,9 @@
|
||||
(SELECT COUNT(1) FROM device WHERE DATE (create_time) = CURDATE() AND binding_status = 1 ) AS bindingNew, (
|
||||
SELECT COUNT (1)
|
||||
FROM device
|
||||
WHERE binding_status = 1 ) AS binding, (
|
||||
SELECT COUNT (1)
|
||||
FROM device
|
||||
WHERE online_status = 2) AS equipmentAbnormal
|
||||
</select>
|
||||
|
||||
@ -347,13 +351,13 @@
|
||||
|
||||
<!-- 获取告警信息 -->
|
||||
<select id="getAlarmInformation" resultType="com.fuyuanshen.equipment.domain.vo.AlarmInformationVo">
|
||||
SELECT (SELECT COUNT(1) FROM device_alarm WHERE treatment_state = 0) AS alarmsTotal
|
||||
SELECT (SELECT COUNT(1) FROM device_alarm) AS alarmsTotal
|
||||
, (SELECT COUNT(1)
|
||||
FROM device_alarm
|
||||
WHERE treatment_state = 0) AS processingAlarm
|
||||
WHERE treatment_state = 0) AS processingAlarm
|
||||
, (SELECT COUNT(1)
|
||||
FROM device_alarm
|
||||
WHERE treatment_state = 0 AND
|
||||
WHERE
|
||||
DATE (create_time) = CURDATE()) AS alarmsTotalToday
|
||||
, (
|
||||
SELECT COUNT (1)
|
||||
@ -419,4 +423,11 @@
|
||||
AND MONTH (dl.create_time) = #{month}
|
||||
</select>
|
||||
|
||||
<!-- 根据设备IMEI查询设备 -->
|
||||
<select id="selectDeviceByImei" resultType="com.fuyuanshen.equipment.domain.Device">
|
||||
SELECT *
|
||||
FROM device
|
||||
WHERE device_imei = #{deviceImei}
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user