设备心跳调整 app新增增设备时绑定联网设备
This commit is contained in:
@@ -10,15 +10,19 @@ import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.app.domain.bo.*;
|
||||
import org.dromara.app.domain.bo.AppDeviceBo;
|
||||
import org.dromara.app.domain.bo.AppScheduleBo;
|
||||
import org.dromara.app.domain.bo.AppSchedulingDeviceBo;
|
||||
import org.dromara.app.domain.bo.AppWateringLogBo;
|
||||
import org.dromara.app.domain.mqtt.DeviceCommand;
|
||||
import org.dromara.app.domain.vo.*;
|
||||
import org.dromara.app.service.*;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.app.service.impl.AppScheduleServiceImpl;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.core.domain.model.LoginUser;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.exception.user.UserException;
|
||||
import org.dromara.common.core.utils.MessageUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
import org.dromara.common.core.validate.EditGroup;
|
||||
@@ -77,16 +81,15 @@ public class AppController extends BaseController {
|
||||
*
|
||||
*/
|
||||
@ApiEncrypt
|
||||
@PostMapping("/addDevice")
|
||||
public R<Void> addDevice(@RequestBody String body) {
|
||||
AppDeviceBo appDevic = JsonUtils.parseObject(body, AppDeviceBo.class);
|
||||
appDevic.setUserId(LoginHelper.getUserId());
|
||||
Boolean b = appDeviceService.insertByBo(appDevic);
|
||||
if (b){
|
||||
return R.ok();
|
||||
}else {
|
||||
|
||||
return R.fail();
|
||||
@PostMapping("/bindDeviceStatus")
|
||||
public R<Map> bindDeviceStatus(@RequestBody String body) {
|
||||
try {
|
||||
AppDeviceBo appDevice = JsonUtils.parseObject(body, AppDeviceBo.class);
|
||||
appDevice.setUserId(LoginHelper.getUserId());
|
||||
Map<String, Object> map = appDeviceService.bindDeviceStatus(appDevice);
|
||||
return R.ok(map);
|
||||
} catch (Exception e) {
|
||||
return fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,11 +97,15 @@ public class AppController extends BaseController {
|
||||
* 绑定已通过MQTT注册上线的设备
|
||||
*/
|
||||
@ApiEncrypt
|
||||
@PostMapping("/bindDevice")
|
||||
@PostMapping("/addDevice")
|
||||
public R<AppDeviceVo> bindDevice(@RequestBody String body) {
|
||||
AppDeviceBo appDevice = JsonUtils.parseObject(body, AppDeviceBo.class);
|
||||
appDevice.setUserId(LoginHelper.getUserId());
|
||||
return R.ok(appDeviceService.bindRegisteredDevice(appDevice));
|
||||
try {
|
||||
AppDeviceBo appDevice = JsonUtils.parseObject(body, AppDeviceBo.class);
|
||||
appDevice.setUserId(LoginHelper.getUserId());
|
||||
return R.ok(appDeviceService.bindRegisteredDevice(appDevice));
|
||||
} catch (Exception e) {
|
||||
return fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -110,7 +117,11 @@ public class AppController extends BaseController {
|
||||
@GetMapping("/device/{deviceNo}")
|
||||
public R<AppDeviceVo> getDeviceInfo(@NotNull(message = "主键不能为空")
|
||||
@PathVariable String deviceNo) {
|
||||
return R.ok(appDeviceService.queryById(deviceNo));
|
||||
try {
|
||||
return R.ok(getOwnedDevice(deviceNo));
|
||||
} catch (Exception e) {
|
||||
return fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -123,9 +134,11 @@ public class AppController extends BaseController {
|
||||
@PutMapping("/updateDeviceInfo")
|
||||
public R<Void> updateDeviceInfo(@Validated @RequestBody AppDeviceBo bo) {
|
||||
try {
|
||||
assertDeviceOwned(bo.getDeviceNo());
|
||||
bo.setUserId(LoginHelper.getUserId());
|
||||
return toAjax(appDeviceService.updateByBo(bo));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
return fail(e);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -137,7 +150,14 @@ public class AppController extends BaseController {
|
||||
@DeleteMapping("/deleteDevice/{deviceNos}")
|
||||
public R<Void> removeDevice(@NotEmpty(message = "主键不能为空")
|
||||
@PathVariable String[] deviceNos) {
|
||||
return toAjax(appDeviceService.deleteWithValidByIds(List.of(deviceNos), true));
|
||||
try {
|
||||
for (String deviceNo : deviceNos) {
|
||||
assertDeviceOwned(deviceNo);
|
||||
}
|
||||
return toAjax(appDeviceService.deleteWithValidByIds(List.of(deviceNos), true));
|
||||
} catch (Exception e) {
|
||||
return fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -149,11 +169,11 @@ public class AppController extends BaseController {
|
||||
@RepeatSubmit()
|
||||
@PutMapping("/switchDevice")
|
||||
public R<Void> switchDevice(@RequestBody String body) {
|
||||
Map<String,String> map = JsonUtils.parseObject(body, Map.class);
|
||||
try {
|
||||
Map<String,String> map = JsonUtils.parseObject(body, Map.class);
|
||||
String deviceNo = map.get("deviceNo");
|
||||
String status = map.get("workStatus");
|
||||
Date date = new Date();
|
||||
String startTime = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
|
||||
String durationMinValue = map.get("durationMin");
|
||||
Integer durationMin = StringUtils.isBlank(durationMinValue) ? null : Integer.valueOf(durationMinValue);
|
||||
//更新设备工作状态
|
||||
@@ -161,7 +181,7 @@ public class AppController extends BaseController {
|
||||
// 记录信息到历史表
|
||||
return toAjax(flag);
|
||||
} catch (Exception e) {
|
||||
return R.fail(e.getMessage());
|
||||
return fail(e);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -173,28 +193,32 @@ public class AppController extends BaseController {
|
||||
*/
|
||||
@GetMapping("/scheduleDeviceList/{scheduleId}")
|
||||
public R<Map<String,Object>> scheduleDeviceList( @PathVariable Long scheduleId) {
|
||||
try {
|
||||
|
||||
Map retMap = new HashMap();
|
||||
//所有设备列表
|
||||
AppDeviceBo appDeviceBo = new AppDeviceBo();
|
||||
appDeviceBo.setUserId(LoginHelper.getUserId());
|
||||
List<AppDeviceVo> appDeviceVos = appDeviceService.queryList(appDeviceBo);
|
||||
Map retMap = new HashMap();
|
||||
//所有设备列表
|
||||
AppDeviceBo appDeviceBo = new AppDeviceBo();
|
||||
appDeviceBo.setUserId(LoginHelper.getUserId());
|
||||
List<AppDeviceVo> appDeviceVos = appDeviceService.queryList(appDeviceBo);
|
||||
|
||||
List<AppDeviceVo> appDeviceVoList = new ArrayList();
|
||||
AppSchedulingDeviceBo schedulingDeviceBo = new AppSchedulingDeviceBo();
|
||||
schedulingDeviceBo.setScheduleId(scheduleId);
|
||||
List<AppDeviceVo> appDeviceVoList = new ArrayList();
|
||||
AppSchedulingDeviceBo schedulingDeviceBo = new AppSchedulingDeviceBo();
|
||||
schedulingDeviceBo.setScheduleId(scheduleId);
|
||||
|
||||
|
||||
for (AppDeviceVo appDeviceVo:appDeviceVos){
|
||||
List<AppSchedulingDeviceVo> appSchedulingDeviceVos = appSchedulingDeviceService.findByDeviceNo(appDeviceVo.getDeviceNo());
|
||||
if(appSchedulingDeviceVos.size() == 0){
|
||||
appDeviceVoList.add(appDeviceVo);
|
||||
for (AppDeviceVo appDeviceVo:appDeviceVos){
|
||||
List<AppSchedulingDeviceVo> appSchedulingDeviceVos = appSchedulingDeviceService.findByDeviceNo(appDeviceVo.getDeviceNo());
|
||||
if(appSchedulingDeviceVos.size() == 0){
|
||||
appDeviceVoList.add(appDeviceVo);
|
||||
}
|
||||
}
|
||||
retMap.put("deviceList", appDeviceVoList);
|
||||
|
||||
|
||||
return R.ok(retMap);
|
||||
} catch (Exception e) {
|
||||
return fail(e);
|
||||
}
|
||||
retMap.put("deviceList", appDeviceVoList);
|
||||
|
||||
|
||||
return R.ok(retMap);
|
||||
}
|
||||
/**
|
||||
*绑定排程跟设备
|
||||
@@ -202,26 +226,32 @@ public class AppController extends BaseController {
|
||||
@RepeatSubmit()
|
||||
@PostMapping("/addScheduleDevice")
|
||||
public R<Void> addScheduleDevice( @RequestBody String body) {
|
||||
Map<String, Object> map = JsonUtils.parseObject(body, Map.class);
|
||||
if (map == null || map.get("scheduleId") == null) {
|
||||
throw new ServiceException("scheduleId不能为空");
|
||||
}
|
||||
Long scheduleId = Long.valueOf(map.get("scheduleId").toString());
|
||||
List<String> deviceNos = parseDeviceNos(map);
|
||||
if (deviceNos.isEmpty()) {
|
||||
throw new ServiceException("deviceNos不能为空");
|
||||
}
|
||||
|
||||
for (String deviceNo:deviceNos){
|
||||
if (StringUtils.isBlank(deviceNo)) {
|
||||
continue;
|
||||
try {
|
||||
Map<String, Object> map = JsonUtils.parseObject(body, Map.class);
|
||||
if (map == null || map.get("scheduleId") == null) {
|
||||
throw new ServiceException("app.schedule.id.not.blank.request");
|
||||
}
|
||||
AppSchedulingDeviceBo schedulingDeviceBo = new AppSchedulingDeviceBo();
|
||||
schedulingDeviceBo.setDeviceNo(deviceNo);
|
||||
schedulingDeviceBo.setScheduleId(scheduleId);
|
||||
appSchedulingDeviceService.insertByBo(schedulingDeviceBo);
|
||||
Long scheduleId = Long.valueOf(map.get("scheduleId").toString());
|
||||
assertScheduleOwned(scheduleId);
|
||||
List<String> deviceNos = parseDeviceNos(map);
|
||||
if (deviceNos.isEmpty()) {
|
||||
throw new ServiceException("app.schedule.devices.not.blank");
|
||||
}
|
||||
|
||||
for (String deviceNo:deviceNos){
|
||||
if (StringUtils.isBlank(deviceNo)) {
|
||||
continue;
|
||||
}
|
||||
assertDeviceOwned(deviceNo);
|
||||
AppSchedulingDeviceBo schedulingDeviceBo = new AppSchedulingDeviceBo();
|
||||
schedulingDeviceBo.setDeviceNo(deviceNo);
|
||||
schedulingDeviceBo.setScheduleId(scheduleId);
|
||||
appSchedulingDeviceService.insertByBo(schedulingDeviceBo);
|
||||
}
|
||||
return R.ok();
|
||||
} catch (Exception e) {
|
||||
return fail(e);
|
||||
}
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
private List<String> parseDeviceNos(Map<String, Object> map) {
|
||||
@@ -255,9 +285,22 @@ public class AppController extends BaseController {
|
||||
|
||||
@Log(title = "排程与设备关联", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/deleteScheduleDevice")
|
||||
public R<Void> deleteScheduleDevice(@NotEmpty(message = "主键不能为空")
|
||||
@RequestParam String scheduleId, @RequestParam String deviceNo) {
|
||||
return toAjax(appSchedulingDeviceService.deleteWithValidByScheduleIdAndDeviceNo(Long.valueOf(scheduleId), deviceNo));
|
||||
public R<Void> deleteScheduleDevice(@RequestParam(required = false) String scheduleId,
|
||||
@RequestParam(required = false) String deviceNo) {
|
||||
try {
|
||||
if (StringUtils.isBlank(scheduleId)) {
|
||||
throw new ServiceException("app.schedule.id.not.blank.request");
|
||||
}
|
||||
if (StringUtils.isBlank(deviceNo)) {
|
||||
throw new ServiceException("app.device.no.not.blank");
|
||||
}
|
||||
Long scheduleIdValue = Long.valueOf(scheduleId);
|
||||
assertScheduleOwned(scheduleIdValue);
|
||||
assertDeviceOwned(deviceNo);
|
||||
return toAjax(appSchedulingDeviceService.deleteWithValidByScheduleIdAndDeviceNo(scheduleIdValue, deviceNo));
|
||||
} catch (Exception e) {
|
||||
return fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -289,8 +332,8 @@ public class AppController extends BaseController {
|
||||
}
|
||||
return R.fail();
|
||||
// return toAjax(appScheduleService.insertByBo(bo));
|
||||
} catch (RuntimeException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (Exception e) {
|
||||
return fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,48 +346,52 @@ public class AppController extends BaseController {
|
||||
@GetMapping("/schedule/{id}")
|
||||
public R<AppScheduleVo> getScheduleInfo(@NotNull(message = "主键不能为空")
|
||||
@PathVariable Long id) {
|
||||
AppScheduleVo appScheduleVo = appScheduleService.queryById(id);
|
||||
if (ObjectUtil.isNotNull(appScheduleVo)){
|
||||
List<AppScheduleDetailVo> list = appScheduleDetailService.queryByScheduleIdByStatus(appScheduleVo.getId());
|
||||
try {
|
||||
AppScheduleVo appScheduleVo = getOwnedSchedule(id);
|
||||
if (ObjectUtil.isNotNull(appScheduleVo)){
|
||||
List<AppScheduleDetailVo> list = appScheduleDetailService.queryByScheduleIdByStatus(appScheduleVo.getId());
|
||||
|
||||
AppSchedulingDeviceBo appSchedulingDeviceBo = new AppSchedulingDeviceBo();
|
||||
appSchedulingDeviceBo.setScheduleId(appScheduleVo.getId());
|
||||
List<AppSchedulingDeviceVo> appSchedulingDeviceVos = appSchedulingDeviceService.queryList(appSchedulingDeviceBo);
|
||||
List deviceList = new ArrayList();
|
||||
for (AppSchedulingDeviceVo appSchedulingDeviceVo:appSchedulingDeviceVos){
|
||||
AppDeviceVo appDeviceVo = appDeviceService.queryById(appSchedulingDeviceVo.getDeviceNo());
|
||||
if (ObjectUtil.isNotNull(appDeviceVo)){
|
||||
deviceList.add(appDeviceVo);
|
||||
AppSchedulingDeviceBo appSchedulingDeviceBo = new AppSchedulingDeviceBo();
|
||||
appSchedulingDeviceBo.setScheduleId(appScheduleVo.getId());
|
||||
List<AppSchedulingDeviceVo> appSchedulingDeviceVos = appSchedulingDeviceService.queryList(appSchedulingDeviceBo);
|
||||
List deviceList = new ArrayList();
|
||||
for (AppSchedulingDeviceVo appSchedulingDeviceVo:appSchedulingDeviceVos){
|
||||
AppDeviceVo appDeviceVo = appDeviceService.queryById(appSchedulingDeviceVo.getDeviceNo());
|
||||
if (ObjectUtil.isNotNull(appDeviceVo)){
|
||||
deviceList.add(appDeviceVo);
|
||||
}
|
||||
|
||||
}
|
||||
List detailList = new ArrayList();
|
||||
|
||||
for (AppScheduleDetailVo detail:list){
|
||||
List timeSlots = new ArrayList();
|
||||
JSONArray mapArray = JSONUtil.parseArray(detail.getTimeData());
|
||||
for (Object timeSlot:mapArray){
|
||||
timeSlots.add(timeSlot);
|
||||
}
|
||||
|
||||
Map map = new HashMap();
|
||||
map.put("id",detail.getId() );
|
||||
map.put("weekday",detail.getWeekday() );
|
||||
map.put("timeData",timeSlots );
|
||||
map.put("triggerType",detail.getTriggerType() );
|
||||
map.put("status",detail.getStatus() );
|
||||
detailList.add(map);
|
||||
}
|
||||
|
||||
}
|
||||
List detailList = new ArrayList();
|
||||
|
||||
for (AppScheduleDetailVo detail:list){
|
||||
List timeSlots = new ArrayList();
|
||||
JSONArray mapArray = JSONUtil.parseArray(detail.getTimeData());
|
||||
for (Object timeSlot:mapArray){
|
||||
timeSlots.add(timeSlot);
|
||||
}
|
||||
|
||||
Map map = new HashMap();
|
||||
map.put("id",detail.getId() );
|
||||
map.put("weekday",detail.getWeekday() );
|
||||
map.put("timeData",timeSlots );
|
||||
map.put("triggerType",detail.getTriggerType() );
|
||||
map.put("status",detail.getStatus() );
|
||||
detailList.add(map);
|
||||
appScheduleVo.setDetails(detailList);
|
||||
appScheduleVo.setDeviceNos(deviceList);
|
||||
return R.ok(appScheduleVo);
|
||||
}
|
||||
|
||||
|
||||
|
||||
appScheduleVo.setDetails(detailList);
|
||||
appScheduleVo.setDeviceNos(deviceList);
|
||||
return R.ok(appScheduleVo);
|
||||
return R.fail(appScheduleVo);
|
||||
} catch (Exception e) {
|
||||
return fail(e);
|
||||
}
|
||||
|
||||
|
||||
return R.fail(appScheduleVo);
|
||||
}
|
||||
|
||||
|
||||
@@ -356,8 +403,13 @@ public class AppController extends BaseController {
|
||||
@PutMapping("/editScheduleStatus")
|
||||
public R<Void> editScheduleStatus(@NotNull(message = "主键不能为空")
|
||||
@RequestBody AppScheduleBo bo) {
|
||||
|
||||
return toAjax(appScheduleService.updateStatusByBo(bo));
|
||||
try {
|
||||
assertScheduleOwned(bo.getId());
|
||||
bo.setUserId(LoginHelper.getUserId());
|
||||
return toAjax(appScheduleService.updateStatusByBo(bo));
|
||||
} catch (Exception e) {
|
||||
return fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -367,11 +419,17 @@ public class AppController extends BaseController {
|
||||
@RepeatSubmit()
|
||||
@PutMapping("/updataschedule")
|
||||
public R<Object> editSchedule(@Validated(EditGroup.class) @RequestBody AppScheduleBo bo) {
|
||||
Map<String, Object> map = appScheduleService.updateByBo(bo);
|
||||
if (Boolean.valueOf(map.get("flag").toString())){
|
||||
return R.ok(map.get("appSchedule"));
|
||||
}else {
|
||||
return R.fail();
|
||||
try {
|
||||
assertScheduleOwned(bo.getId());
|
||||
bo.setUserId(LoginHelper.getUserId());
|
||||
Map<String, Object> map = appScheduleService.updateByBo(bo);
|
||||
if (Boolean.valueOf(map.get("flag").toString())){
|
||||
return R.ok(map.get("appSchedule"));
|
||||
}else {
|
||||
return R.fail();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return fail(e);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -386,7 +444,14 @@ public class AppController extends BaseController {
|
||||
@DeleteMapping("/deleteschedule/{ids}")
|
||||
public R<Void> removeSchedule(@NotEmpty(message = "主键不能为空")
|
||||
@PathVariable Long[] ids) {
|
||||
return toAjax(appScheduleService.deleteWithValidByIds(List.of(ids), true));
|
||||
try {
|
||||
for (Long id : ids) {
|
||||
assertScheduleOwned(id);
|
||||
}
|
||||
return toAjax(appScheduleService.deleteWithValidByIds(List.of(ids), true));
|
||||
} catch (Exception e) {
|
||||
return fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -427,7 +492,14 @@ public class AppController extends BaseController {
|
||||
@DeleteMapping("/waterLog/{ids}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
||||
@PathVariable Long[] ids) {
|
||||
return toAjax(appWateringLogService.deleteWithValidByIds(List.of(ids), true));
|
||||
try {
|
||||
for (Long id : ids) {
|
||||
assertWateringLogOwned(id);
|
||||
}
|
||||
return toAjax(appWateringLogService.deleteWithValidByIds(List.of(ids), true));
|
||||
} catch (Exception e) {
|
||||
return fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -463,7 +535,7 @@ public class AppController extends BaseController {
|
||||
map.put("averageTime",averageTime);
|
||||
return R.ok(map);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
return fail(e);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -475,16 +547,20 @@ public class AppController extends BaseController {
|
||||
*/
|
||||
@GetMapping("/getUserInfo")
|
||||
public R<UserInfoVo> getUserInfo() {
|
||||
UserInfoVo userInfoVo = new UserInfoVo();
|
||||
LoginUser loginUser = LoginHelper.getLoginUser();
|
||||
SysUserVo user = DataPermissionHelper.ignore(() -> userService.selectUserById(loginUser.getUserId()));
|
||||
if (ObjectUtil.isNull(user)) {
|
||||
return R.fail("没有权限访问用户数据!");
|
||||
try {
|
||||
UserInfoVo userInfoVo = new UserInfoVo();
|
||||
LoginUser loginUser = LoginHelper.getLoginUser();
|
||||
SysUserVo user = DataPermissionHelper.ignore(() -> userService.selectUserById(loginUser.getUserId()));
|
||||
if (ObjectUtil.isNull(user)) {
|
||||
return R.fail(MessageUtils.message("app.user.data.denied"));
|
||||
}
|
||||
userInfoVo.setUser(user);
|
||||
userInfoVo.setPermissions(loginUser.getMenuPermission());
|
||||
userInfoVo.setRoles(loginUser.getRolePermission());
|
||||
return R.ok(userInfoVo);
|
||||
} catch (Exception e) {
|
||||
return fail(e);
|
||||
}
|
||||
userInfoVo.setUser(user);
|
||||
userInfoVo.setPermissions(loginUser.getMenuPermission());
|
||||
userInfoVo.setRoles(loginUser.getRolePermission());
|
||||
return R.ok(userInfoVo);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -495,13 +571,17 @@ public class AppController extends BaseController {
|
||||
@RepeatSubmit()
|
||||
@PutMapping("/updataUser")
|
||||
public R<Void> editUser( @RequestBody SysUserBo user) {
|
||||
userService.checkUserAllowed(user.getUserId());
|
||||
userService.checkUserDataScope(user.getUserId());
|
||||
if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {
|
||||
return R.fail("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
|
||||
}
|
||||
try {
|
||||
userService.checkUserAllowed(user.getUserId());
|
||||
userService.checkUserDataScope(user.getUserId());
|
||||
if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {
|
||||
return R.fail(MessageUtils.message("app.user.phone.exists", user.getUserName()));
|
||||
}
|
||||
|
||||
return toAjax(userService.updateAppUser(user));
|
||||
return toAjax(userService.updateAppUser(user));
|
||||
} catch (Exception e) {
|
||||
return fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -510,15 +590,19 @@ public class AppController extends BaseController {
|
||||
@RepeatSubmit()
|
||||
@PutMapping("/updataPassword")
|
||||
public R<Void> updataPassword(@RequestBody SysUserVo vo) {
|
||||
Long userId = LoginHelper.getUserId();
|
||||
SysUserVo sysUserVo = userService.selectUserById(userId);
|
||||
if (!BCrypt.checkpw(vo.getOldPassword(), sysUserVo.getPassword())){
|
||||
throw new UserException("user.password.not.match");
|
||||
try {
|
||||
Long userId = LoginHelper.getUserId();
|
||||
SysUserVo sysUserVo = userService.selectUserById(userId);
|
||||
if (!BCrypt.checkpw(vo.getOldPassword(), sysUserVo.getPassword())){
|
||||
throw new UserException("user.password.not.match");
|
||||
}
|
||||
SysUserBo userBo = new SysUserBo();
|
||||
userBo.setUserId(userId);
|
||||
userBo.setPassword(vo.getPassword());
|
||||
return toAjax(userService.updateUserPas(userBo));
|
||||
} catch (Exception e) {
|
||||
return fail(e);
|
||||
}
|
||||
SysUserBo userBo = new SysUserBo();
|
||||
userBo.setUserId(userId);
|
||||
userBo.setPassword(vo.getPassword());
|
||||
return toAjax(userService.updateUserPas(userBo));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -527,35 +611,99 @@ public class AppController extends BaseController {
|
||||
@ApiEncrypt
|
||||
@PostMapping("/retrievePassword")
|
||||
public R<Void> forgot(@RequestBody String body) {
|
||||
SysUserBo bo = JsonUtils.parseObject(body, SysUserBo.class);
|
||||
bo.setUserId(LoginHelper.getUserId());
|
||||
try {
|
||||
SysUserBo bo = JsonUtils.parseObject(body, SysUserBo.class);
|
||||
bo.setUserId(LoginHelper.getUserId());
|
||||
|
||||
if(Validator.isEmail(bo.getUserName())){
|
||||
boolean checkPhoneFlag = userService.checkEmailUnique(bo);
|
||||
if (!checkPhoneFlag){
|
||||
throw new UserException("user.email.not.username");
|
||||
}
|
||||
}else if (Validator.isMobile(bo.getUserName())){
|
||||
boolean checkPhoneFlag = userService.checkPhoneUnique(bo);
|
||||
if (!checkPhoneFlag){
|
||||
throw new UserException("user.mobile.phone.number.not.username");
|
||||
if(Validator.isEmail(bo.getUserName())){
|
||||
boolean checkPhoneFlag = userService.checkEmailUnique(bo);
|
||||
if (!checkPhoneFlag){
|
||||
throw new UserException("user.email.not.username");
|
||||
}
|
||||
}else if (Validator.isMobile(bo.getUserName())){
|
||||
boolean checkPhoneFlag = userService.checkPhoneUnique(bo);
|
||||
if (!checkPhoneFlag){
|
||||
throw new UserException("user.mobile.phone.number.not.username");
|
||||
}
|
||||
}
|
||||
|
||||
return toAjax(userService.updateUserPas(bo));
|
||||
} catch (Exception e) {
|
||||
return fail(e);
|
||||
}
|
||||
|
||||
return toAjax(userService.updateUserPas(bo));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@GetMapping("/test")
|
||||
public R<Void> test (){
|
||||
try {
|
||||
|
||||
DeviceCommand deviceCommand = new DeviceCommand();
|
||||
DeviceCommand deviceCommand = new DeviceCommand();
|
||||
|
||||
String bindDevice = deviceCommandService.sendBindDeviceCommand("01");
|
||||
String bindDevice = deviceCommandService.sendBindDeviceCommand("01");
|
||||
|
||||
// String s = deviceCommandService.sendSwitchCommand("01", "1", "2026-06-10 14:35:31", 20);
|
||||
return R.ok(bindDevice);
|
||||
// String s = deviceCommandService.sendSwitchCommand("01", "1", "2026-06-10 14:35:31", 20);
|
||||
return R.ok(bindDevice);
|
||||
} catch (Exception e) {
|
||||
return fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
private <T> R<T> fail(Exception e) {
|
||||
return R.fail(toI18nErrorMessage(e));
|
||||
}
|
||||
|
||||
private String toI18nErrorMessage(Exception e) {
|
||||
String message = e.getMessage();
|
||||
if (StringUtils.isBlank(message)) {
|
||||
return MessageUtils.message("operation.fail");
|
||||
}
|
||||
String i18nMessage = MessageUtils.message(message);
|
||||
return message.equals(i18nMessage) ? message : i18nMessage;
|
||||
}
|
||||
|
||||
private AppDeviceVo getOwnedDevice(String deviceNo) {
|
||||
if (StringUtils.isBlank(deviceNo)) {
|
||||
throw new ServiceException("app.device.no.not.blank");
|
||||
}
|
||||
AppDeviceVo device = appDeviceService.queryById(deviceNo);
|
||||
Long userId = LoginHelper.getUserId();
|
||||
if (device == null || !Objects.equals(device.getUserId(), userId)) {
|
||||
throw new ServiceException("app.device.not.exists.or.denied");
|
||||
}
|
||||
return device;
|
||||
}
|
||||
|
||||
private void assertDeviceOwned(String deviceNo) {
|
||||
getOwnedDevice(deviceNo);
|
||||
}
|
||||
|
||||
private AppScheduleVo getOwnedSchedule(Long scheduleId) {
|
||||
if (scheduleId == null) {
|
||||
throw new ServiceException("app.schedule.id.not.blank");
|
||||
}
|
||||
AppScheduleVo schedule = appScheduleService.queryById(scheduleId);
|
||||
Long userId = LoginHelper.getUserId();
|
||||
if (schedule == null || !Objects.equals(schedule.getUserId(), userId)) {
|
||||
throw new ServiceException("app.schedule.not.exists.or.denied");
|
||||
}
|
||||
return schedule;
|
||||
}
|
||||
|
||||
private void assertScheduleOwned(Long scheduleId) {
|
||||
getOwnedSchedule(scheduleId);
|
||||
}
|
||||
|
||||
private void assertWateringLogOwned(Long id) {
|
||||
if (id == null) {
|
||||
throw new ServiceException("app.watering.log.id.not.blank");
|
||||
}
|
||||
AppWateringLogVo log = appWateringLogService.queryById(id);
|
||||
Long userId = LoginHelper.getUserId();
|
||||
if (log == null || !Objects.equals(log.getUserId(), userId)) {
|
||||
throw new ServiceException("app.watering.log.not.exists.or.denied");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
package org.dromara.app.handler;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.app.domain.AppDevice;
|
||||
import org.dromara.app.domain.bo.AppDeviceBo;
|
||||
import org.dromara.app.mapper.AppDeviceMapper;
|
||||
import org.dromara.app.mqtt.MqttTopicHandler;
|
||||
import org.dromara.app.service.IAppDeviceService;
|
||||
import org.dromara.common.json.utils.JsonUtils;
|
||||
import org.dromara.common.redis.utils.RedisUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -22,6 +30,13 @@ public class DeviceDataHandler implements MqttTopicHandler {
|
||||
|
||||
private static final Pattern PATTERN = Pattern.compile("^/([^/]+)/publish/power$");
|
||||
private final IAppDeviceService appDeviceService;
|
||||
private final AppDeviceMapper appDeviceMapper;
|
||||
|
||||
@Value("${mqtt.command-ack.device-status-cache-prefix}")
|
||||
private String deviceStatusCachePrefix;
|
||||
|
||||
@Value("${mqtt.command-ack.device-status-cache-ttl-seconds}")
|
||||
private int deviceStatusCacheTtlSeconds;
|
||||
|
||||
@Override
|
||||
public Pattern topicPattern() {
|
||||
@@ -32,14 +47,44 @@ public class DeviceDataHandler implements MqttTopicHandler {
|
||||
public void handle(String deviceNo, String payload) {
|
||||
try {
|
||||
Map<String, Object> dto = JsonUtils.parseObject(payload, Map.class);
|
||||
if (dto == null || dto.get("powerLevel") == null) {
|
||||
log.warn("[MQTT] 设备电量上报缺少电量字段 时间={} 设备编号={} 消息体={}", HandlerLogTime.now(), deviceNo, payload);
|
||||
refreshDeviceOnline(deviceNo);
|
||||
return;
|
||||
}
|
||||
// 更新设备电量 + 同步在线状态到数据库
|
||||
AppDeviceBo appDeviceBo = new AppDeviceBo();
|
||||
appDeviceBo.setDeviceNo(deviceNo);
|
||||
appDeviceBo.setPowerLevel(dto.get("powerLevel").toString());
|
||||
appDeviceBo.setPowerLevelUpdatatime(new Date());
|
||||
appDeviceBo.setStatus("1"); // 收到数据 = 在线
|
||||
appDeviceService.updateByBo(appDeviceBo);
|
||||
log.info("[MQTT] 设备电量更新 deviceNo={} payload={}", deviceNo, payload);
|
||||
// 同步刷新 Redis 在线缓存(定时任务离线检测依赖此 Key)
|
||||
refreshDeviceOnline(deviceNo);
|
||||
log.info("[MQTT] 设备电量更新 时间={} 设备编号={} 消息体={}", HandlerLogTime.now(), deviceNo, payload);
|
||||
} catch (Exception e) {
|
||||
log.error("[MQTT] 设备电量更新失败 deviceNo={} payload={}", deviceNo, payload, e);
|
||||
log.error("[MQTT] 设备电量更新失败 时间={} 设备编号={} 消息体={}", HandlerLogTime.now(), deviceNo, payload, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新设备在线状态到 Redis(与 MqttCommandAckService 逻辑一致)
|
||||
* 写入 Redis 后 TTL 到期自动过期 = 离线
|
||||
*/
|
||||
private void refreshDeviceOnline(String deviceNo) {
|
||||
Map<String, Object> statusCache = new HashMap<>();
|
||||
statusCache.put("deviceNo", deviceNo);
|
||||
statusCache.put("status", "1");
|
||||
statusCache.put("lastReportTime", Instant.now().toString());
|
||||
RedisUtils.setCacheObject(
|
||||
deviceStatusCachePrefix + deviceNo,
|
||||
statusCache,
|
||||
Duration.ofSeconds(deviceStatusCacheTtlSeconds)
|
||||
);
|
||||
appDeviceMapper.update(null,
|
||||
new LambdaUpdateWrapper<AppDevice>()
|
||||
.set(AppDevice::getStatus, "1")
|
||||
.eq(AppDevice::getDeviceNo, deviceNo)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
package org.dromara.app.handler;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.app.domain.AppDevice;
|
||||
import org.dromara.app.domain.bo.AppDeviceBo;
|
||||
import org.dromara.app.mapper.AppDeviceMapper;
|
||||
import org.dromara.app.mqtt.MqttTopicHandler;
|
||||
import org.dromara.app.service.IAppDeviceService;
|
||||
import org.dromara.common.json.utils.JsonUtils;
|
||||
import org.dromara.common.redis.utils.RedisUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -22,6 +30,13 @@ public class DeviceRegisterHandler implements MqttTopicHandler {
|
||||
private static final Pattern PATTERN = Pattern.compile("^/([^/]+)/publish/register$");
|
||||
|
||||
private final IAppDeviceService appDeviceService;
|
||||
private final AppDeviceMapper appDeviceMapper;
|
||||
|
||||
@Value("${mqtt.command-ack.device-status-cache-prefix}")
|
||||
private String deviceStatusCachePrefix;
|
||||
|
||||
@Value("${mqtt.command-ack.device-status-cache-ttl-seconds}")
|
||||
private int deviceStatusCacheTtlSeconds;
|
||||
|
||||
@Override
|
||||
public Pattern topicPattern() {
|
||||
@@ -34,15 +49,18 @@ public class DeviceRegisterHandler implements MqttTopicHandler {
|
||||
Map<String, Object> dto = JsonUtils.parseObject(payload, Map.class);
|
||||
AppDeviceBo device = buildRegisterDevice(deviceNo, dto);
|
||||
appDeviceService.registerByMqtt(device);
|
||||
log.info("[MQTT] 设备注册 deviceNo={} payload={}", deviceNo, payload);
|
||||
// 注册即在线
|
||||
refreshDeviceOnline(deviceNo);
|
||||
log.info("[MQTT] 设备注册 时间={} 设备编号={} 消息体={}", HandlerLogTime.now(), deviceNo, payload);
|
||||
} catch (Exception e) {
|
||||
log.error("[MQTT] 设备注册失败 deviceNo={} payload={}", deviceNo, payload, e);
|
||||
log.error("[MQTT] 设备注册失败 时间={} 设备编号={} 消息体={}", HandlerLogTime.now(), deviceNo, payload, e);
|
||||
}
|
||||
}
|
||||
|
||||
private AppDeviceBo buildRegisterDevice(String topicDeviceNo, Map<String, Object> dto) {
|
||||
AppDeviceBo device = new AppDeviceBo();
|
||||
device.setDeviceNo(topicDeviceNo);
|
||||
device.setStatus("1"); // 注册即在线
|
||||
if (dto == null) {
|
||||
return device;
|
||||
}
|
||||
@@ -67,4 +85,24 @@ public class DeviceRegisterHandler implements MqttTopicHandler {
|
||||
private String valueAsString(Object value) {
|
||||
return value == null ? null : String.valueOf(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新设备在线状态到 Redis
|
||||
*/
|
||||
private void refreshDeviceOnline(String deviceNo) {
|
||||
Map<String, Object> statusCache = new HashMap<>();
|
||||
statusCache.put("deviceNo", deviceNo);
|
||||
statusCache.put("status", "1");
|
||||
statusCache.put("lastReportTime", Instant.now().toString());
|
||||
RedisUtils.setCacheObject(
|
||||
deviceStatusCachePrefix + deviceNo,
|
||||
statusCache,
|
||||
Duration.ofSeconds(deviceStatusCacheTtlSeconds)
|
||||
);
|
||||
appDeviceMapper.update(null,
|
||||
new LambdaUpdateWrapper<AppDevice>()
|
||||
.set(AppDevice::getStatus, "1")
|
||||
.eq(AppDevice::getDeviceNo, deviceNo)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,9 +28,9 @@ public class ErromesHandler implements MqttTopicHandler {
|
||||
public void handle(String deviceNo, String payload) {
|
||||
try {
|
||||
Map<String, Object> dto = JsonUtils.parseObject(payload, Map.class);
|
||||
log.info("[MQTT] 设备异常告警 deviceNo={} payload={}", deviceNo, dto);
|
||||
log.info("[MQTT] 设备异常告警 时间={} 设备编号={} 消息体={}", HandlerLogTime.now(), deviceNo, dto);
|
||||
} catch (Exception e) {
|
||||
log.error("[MQTT] 设备异常处理失败 deviceNo={}", deviceNo, e);
|
||||
log.error("[MQTT] 设备异常处理失败 时间={} 设备编号={}", HandlerLogTime.now(), deviceNo, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package org.dromara.app.handler;
|
||||
|
||||
import org.dromara.common.core.utils.DateUtils;
|
||||
|
||||
final class HandlerLogTime {
|
||||
|
||||
private HandlerLogTime() {
|
||||
}
|
||||
|
||||
static String now() {
|
||||
return DateUtils.getTime();
|
||||
}
|
||||
}
|
||||
@@ -41,16 +41,16 @@ public class KeyFinishHandler implements MqttTopicHandler {
|
||||
Map<String, Object> dto = JsonUtils.parseObject(payload, Map.class);
|
||||
AppWateringLogBo logBo = buildWateringLog(deviceNo, dto);
|
||||
appWateringLogService.insertByBo(logBo);
|
||||
log.info("[MQTT] 按键浇水完成上报已保存 deviceNo={} payload={}", deviceNo, payload);
|
||||
log.info("[MQTT] 按键浇水完成上报已保存 时间={} 设备编号={} 消息体={}", HandlerLogTime.now(), deviceNo, payload);
|
||||
} catch (Exception e) {
|
||||
log.error("[MQTT] 按键浇水完成上报处理失败 deviceNo={} payload={}", deviceNo, payload, e);
|
||||
log.error("[MQTT] 按键浇水完成上报处理失败 时间={} 设备编号={} 消息体={}", HandlerLogTime.now(), deviceNo, payload, e);
|
||||
}
|
||||
}
|
||||
|
||||
private AppWateringLogBo buildWateringLog(String topicDeviceNo, Map<String, Object> dto) {
|
||||
String payloadDeviceNo = valueAsString(dto.get("deviceNo"));
|
||||
if (StringUtils.isNotBlank(payloadDeviceNo) && !topicDeviceNo.equals(payloadDeviceNo)) {
|
||||
log.warn("[MQTT] 按键浇水完成上报设备编号不一致 topicDeviceNo={} payloadDeviceNo={}", topicDeviceNo, payloadDeviceNo);
|
||||
log.warn("[MQTT] 按键浇水完成上报设备编号不一致 时间={} 主题设备编号={} 消息体设备编号={}", HandlerLogTime.now(), topicDeviceNo, payloadDeviceNo);
|
||||
}
|
||||
|
||||
Date receivedAt = new Date();
|
||||
@@ -127,7 +127,7 @@ public class KeyFinishHandler implements MqttTopicHandler {
|
||||
}
|
||||
long diffMillis = endTime.getTime() - startTime.getTime();
|
||||
if (diffMillis < 0) {
|
||||
log.warn("[MQTT] 按键浇水完成上报结束时间早于开始时间 startTime={} endTime={}", startTime, endTime);
|
||||
log.warn("[MQTT] 按键浇水完成上报结束时间早于开始时间 时间={} 开始时间={} 结束时间={}", HandlerLogTime.now(), startTime, endTime);
|
||||
return 0;
|
||||
}
|
||||
return (int) (diffMillis / 60000L);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package org.dromara.app.handler;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import org.dromara.app.domain.AppSchedulingDevice;
|
||||
import org.dromara.app.domain.bo.AppWateringLogBo;
|
||||
import org.dromara.app.domain.vo.AppDeviceVo;
|
||||
@@ -46,16 +46,16 @@ public class ScheduleFinishHandler implements MqttTopicHandler {
|
||||
Map<String, Object> dto = JsonUtils.parseObject(payload, Map.class);
|
||||
AppWateringLogBo logBo = buildWateringLog(deviceNo, dto);
|
||||
appWateringLogService.insertByBo(logBo);
|
||||
log.info("[MQTT] 浇水排程上报 saved deviceNo={} payload={}", deviceNo, payload);
|
||||
log.info("[MQTT] 浇水排程上报已保存 时间={} 设备编号={} 消息体={}", HandlerLogTime.now(), deviceNo, payload);
|
||||
} catch (Exception e) {
|
||||
log.error("[MQTT] 浇水排程上报 failed deviceNo={} payload={}", deviceNo, payload, e);
|
||||
log.error("[MQTT] 浇水排程上报处理失败 时间={} 设备编号={} 消息体={}", HandlerLogTime.now(), deviceNo, payload, e);
|
||||
}
|
||||
}
|
||||
|
||||
private AppWateringLogBo buildWateringLog(String topicDeviceNo, Map<String, Object> dto) {
|
||||
String payloadDeviceNo = valueAsString(dto.get("deviceNo"));
|
||||
if (StringUtils.isNotBlank(payloadDeviceNo) && !topicDeviceNo.equals(payloadDeviceNo)) {
|
||||
log.warn("[MQTT] 排程完成上报设备编号不一致 topicDeviceNo={} payloadDeviceNo={}", topicDeviceNo, payloadDeviceNo);
|
||||
log.warn("[MQTT] 排程完成上报设备编号不一致 时间={} 主题设备编号={} 消息体设备编号={}", HandlerLogTime.now(), topicDeviceNo, payloadDeviceNo);
|
||||
}
|
||||
|
||||
|
||||
@@ -96,11 +96,11 @@ public class ScheduleFinishHandler implements MqttTopicHandler {
|
||||
.eq(AppSchedulingDevice::getDeviceNo, deviceNo)
|
||||
);
|
||||
if (schedulingDevices == null || schedulingDevices.isEmpty()) {
|
||||
log.warn("[MQTT] 排程完成上报未找到设备绑定的排程 deviceNo={}", deviceNo);
|
||||
log.warn("[MQTT] 排程完成上报未找到设备绑定的排程 时间={} 设备编号={}", HandlerLogTime.now(), deviceNo);
|
||||
return 0L;
|
||||
}
|
||||
if (schedulingDevices.size() > 1) {
|
||||
log.warn("[MQTT] 排程完成上报匹配到多个排程 deviceNo={} count={}", deviceNo, schedulingDevices.size());
|
||||
log.warn("[MQTT] 排程完成上报匹配到多个排程 时间={} 设备编号={} 数量={}", HandlerLogTime.now(), deviceNo, schedulingDevices.size());
|
||||
}
|
||||
return schedulingDevices.get(0).getScheduleId();
|
||||
}
|
||||
@@ -148,7 +148,7 @@ public class ScheduleFinishHandler implements MqttTopicHandler {
|
||||
}
|
||||
long diffMillis = endTime.getTime() - startTime.getTime();
|
||||
if (diffMillis < 0) {
|
||||
log.warn("[MQTT] 排程完成上报结束时间早于开始时间 startTime={} endTime={}", startTime, endTime);
|
||||
log.warn("[MQTT] 排程完成上报结束时间早于开始时间 时间={} 开始时间={} 结束时间={}", HandlerLogTime.now(), startTime, endTime);
|
||||
return 0;
|
||||
}
|
||||
return (int) (diffMillis / 60000L);
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
package org.dromara.app.mapper;
|
||||
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.apache.ibatis.annotations.Update;
|
||||
import org.dromara.app.domain.AppDevice;
|
||||
import org.dromara.app.domain.vo.AppDeviceVo;
|
||||
import org.dromara.common.mybatis.annotation.DataColumn;
|
||||
import org.dromara.common.mybatis.annotation.DataPermission;
|
||||
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Update;
|
||||
|
||||
/**
|
||||
* 设备信息
|
||||
@@ -18,26 +17,38 @@ Mapper接口
|
||||
public interface AppDeviceMapper extends BaseMapperPlus<AppDevice, AppDeviceVo> {
|
||||
|
||||
@Update("""
|
||||
<script>
|
||||
update app_device
|
||||
<set>
|
||||
user_id = #{userId},
|
||||
bind_token_hash = null,
|
||||
<if test="deviceName != null and deviceName != ''">
|
||||
device_name = #{deviceName},
|
||||
</if>
|
||||
<if test="status != null and status != ''">
|
||||
status = #{status},
|
||||
</if>
|
||||
</set>
|
||||
where device_no = #{deviceNo}
|
||||
and (user_id is null or user_id = #{userId})
|
||||
</script>
|
||||
""")
|
||||
<script>
|
||||
update app_device
|
||||
<set>
|
||||
user_id = #{userId},
|
||||
bind_token_hash = null,
|
||||
<if test="deviceName != null and deviceName != ''">
|
||||
device_name = #{deviceName},
|
||||
</if>
|
||||
<if test="status != null and status != ''">
|
||||
status = #{status},
|
||||
</if>
|
||||
<if test="workStatus != null and workStatus != ''">
|
||||
work_status = #{workStatus},
|
||||
</if>
|
||||
<if test="wifiName != null and wifiName != ''">
|
||||
wifi_name = #{wifiName},
|
||||
</if>
|
||||
<if test="wifiPassword != null and wifiPassword != ''">
|
||||
wifi_password = #{wifiPassword},
|
||||
</if>
|
||||
</set>
|
||||
where device_no = #{deviceNo}
|
||||
and (user_id is null or user_id = #{userId})
|
||||
</script>
|
||||
""")
|
||||
int bindIfAvailable(@Param("deviceNo") String deviceNo,
|
||||
@Param("userId") Long userId,
|
||||
@Param("deviceName") String deviceName,
|
||||
@Param("status") String status);
|
||||
@Param("status") String status,
|
||||
@Param("workStatus") String workStatus,
|
||||
@Param("wifiName") String wifiName,
|
||||
@Param("wifiPassword") String wifiPassword);
|
||||
|
||||
@Update("""
|
||||
update app_device
|
||||
@@ -49,4 +60,11 @@ public interface AppDeviceMapper extends BaseMapperPlus<AppDevice, AppDeviceVo>
|
||||
@Param("userId") Long userId,
|
||||
@Param("workStatus") String workStatus);
|
||||
|
||||
@Select("""
|
||||
select *
|
||||
from app_device
|
||||
where mac_address = #{macAddress}
|
||||
""")
|
||||
AppDevice selectByMac(@Param("macAddress") String macAddress);
|
||||
|
||||
}
|
||||
|
||||
@@ -15,13 +15,13 @@ import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
public interface AppWateringLogMapper extends BaseMapperPlus<AppWateringLog, AppWateringLogVo> {
|
||||
|
||||
@Select("SELECT count(id) FROM app_watering_log\n" +
|
||||
"WHERE user_id=${userId} AND YEAR(create_time) = YEAR(CURDATE()) AND MONTH(create_time) = MONTH(CURDATE());")
|
||||
"WHERE user_id=#{userId} AND YEAR(create_time) = YEAR(CURDATE()) AND MONTH(create_time) = MONTH(CURDATE());")
|
||||
Long selectByMonCout(@Param("userId") Long userId);
|
||||
|
||||
|
||||
@Select("SELECT COUNT(id) \n" +
|
||||
"FROM app_watering_log \n" +
|
||||
"WHERE user_id=${userId} AND WEEK(create_time) = WEEK(CURDATE());")
|
||||
"WHERE user_id=#{userId} AND WEEK(create_time) = WEEK(CURDATE());")
|
||||
Long selectByWeekCount(@Param("userId") Long userId);
|
||||
|
||||
|
||||
@@ -29,11 +29,11 @@ public interface AppWateringLogMapper extends BaseMapperPlus<AppWateringLog, App
|
||||
|
||||
@Select("SELECT COUNT(id) \n" +
|
||||
"FROM app_watering_log \n" +
|
||||
"WHERE user_id=${userId} AND trigger_type =${triggerType}")
|
||||
Long selectByTriggerTypeCount(Long userId, String triggerType);
|
||||
"WHERE user_id=#{userId} AND trigger_type =#{triggerType}")
|
||||
Long selectByTriggerTypeCount(@Param("userId") Long userId, @Param("triggerType") String triggerType);
|
||||
|
||||
@Select("SELECT SUM(duration_min)\n" +
|
||||
"FROM app_watering_log \n" +
|
||||
"WHERE user_id=${userId}")
|
||||
Long selectByTotalWateringTime(Long userId);
|
||||
"WHERE user_id=#{userId}")
|
||||
Long selectByTotalWateringTime(@Param("userId") Long userId);
|
||||
}
|
||||
|
||||
@@ -20,9 +20,9 @@ public class MqttMessageDispatcher {
|
||||
|
||||
public MqttMessageDispatcher(List<MqttTopicHandler> handlers) {
|
||||
this.handlers = handlers;
|
||||
log.info("[MQTT] dispatcher initialized with {} handlers", handlers.size());
|
||||
log.info("[MQTT] 消息分发器初始化完成,处理器数量={}", handlers.size());
|
||||
for (MqttTopicHandler handler : handlers) {
|
||||
log.info("[MQTT] └ registered: {} -> {}", handler.getClass().getSimpleName(), handler.topicPattern());
|
||||
log.info("[MQTT] 已注册处理器:{} -> {}", handler.getClass().getSimpleName(), handler.topicPattern());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ public class MqttMessageDispatcher {
|
||||
* @param payload 消息体
|
||||
*/
|
||||
public void dispatch(String topic, String payload) {
|
||||
log.debug("[MQTT] received topic={} payload={}", topic, payload);
|
||||
log.debug("[MQTT] 收到消息 主题={} 消息体={}", topic, payload);
|
||||
|
||||
for (MqttTopicHandler handler : handlers) {
|
||||
Matcher matcher = handler.topicPattern().matcher(topic);
|
||||
@@ -44,6 +44,6 @@ public class MqttMessageDispatcher {
|
||||
}
|
||||
}
|
||||
|
||||
log.warn("[MQTT] no handler for topic: {} payload={}", topic, payload);
|
||||
log.warn("[MQTT] 未找到匹配的处理器 主题={} 消息体={}", topic, payload);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
package org.dromara.app.service;
|
||||
|
||||
import org.dromara.app.domain.vo.AppDeviceVo;
|
||||
import org.dromara.app.domain.bo.AppDeviceBo;
|
||||
import org.dromara.common.mybatis.annotation.DataColumn;
|
||||
import org.dromara.common.mybatis.annotation.DataPermission;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.app.domain.vo.AppDeviceVo;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 设备信息
|
||||
@@ -97,4 +96,6 @@ public interface IAppDeviceService {
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
Boolean deleteWithValidByIds(Collection<String> deviceNos, Boolean isValid);
|
||||
|
||||
Map<String, Object> bindDeviceStatus(AppDeviceBo appDevice);
|
||||
}
|
||||
|
||||
@@ -25,15 +25,12 @@ import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@@ -116,7 +113,7 @@ public class AppDeviceServiceImpl implements IAppDeviceService {
|
||||
device.setPowerLevelUpdatatime(new Date());
|
||||
// applyRegisterBindToken(device, bo, exists);
|
||||
if (StringUtils.isBlank(device.getStatus())) {
|
||||
device.setStatus("2");
|
||||
device.setStatus("1"); // 注册默认在线
|
||||
device.setWorkStatus("2");
|
||||
}
|
||||
|
||||
@@ -131,6 +128,45 @@ public class AppDeviceServiceImpl implements IAppDeviceService {
|
||||
return baseMapper.updateById(device) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> bindDeviceStatus(AppDeviceBo bo) {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("bindDeviceStatus", 200);
|
||||
params.put("bindDeviceStatusName", "");
|
||||
if (bo == null) {
|
||||
params.put("bindDeviceStatus", 300);
|
||||
params.put("bindDeviceStatusName", "设备信息不能为空");
|
||||
return params;
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(bo.getMacAddress())) {
|
||||
params.put("bindDeviceStatus", 301);
|
||||
params.put("bindDeviceStatusName", "MAC地址不能为空");
|
||||
return params;
|
||||
}
|
||||
params.put("bindDevice", null);
|
||||
Long userId = LoginHelper.getUserId();
|
||||
AppDevice exists = baseMapper.selectByMac(bo.getMacAddress());
|
||||
if (exists == null) {
|
||||
params.put("bindDeviceStatus", 302);
|
||||
params.put("bindDeviceStatusName", "设备未上线注册,请先完成配网");
|
||||
return params;
|
||||
}
|
||||
params.put("bindDevice", exists);
|
||||
if (exists.getUserId() != null && !exists.getUserId().equals(userId)) {
|
||||
params.put("bindDeviceStatus", 303);
|
||||
params.put("bindDeviceStatusName", "设备已被其他用户绑定");
|
||||
return params;
|
||||
}
|
||||
if (exists.getUserId() == null ) {
|
||||
params.put("bindDeviceStatus", 304);
|
||||
params.put("bindDeviceStatusName", "设备未绑定,请先绑定用户");
|
||||
return params;
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppDeviceVo bindRegisteredDevice(AppDeviceBo bo) {
|
||||
if (StringUtils.isBlank(bo.getDeviceNo())) {
|
||||
@@ -147,8 +183,10 @@ public class AppDeviceServiceImpl implements IAppDeviceService {
|
||||
// validBindToken(bo.getBindToken(), exists);
|
||||
|
||||
String status = StringUtils.isBlank(bo.getStatus()) ? exists.getStatus() : bo.getStatus();
|
||||
String wifiName = StringUtils.isBlank(bo.getWifiName()) ? exists.getWifiName() : bo.getWifiName();
|
||||
String wifiPassword = StringUtils.isBlank(bo.getWifiPassword()) ? exists.getWifiPassword() : bo.getWifiPassword();
|
||||
String deviceName = StringUtils.isBlank(bo.getDeviceName()) ? exists.getDeviceName() : bo.getDeviceName();
|
||||
int rows = baseMapper.bindIfAvailable(exists.getDeviceNo(), userId, deviceName, status);
|
||||
int rows = baseMapper.bindIfAvailable(exists.getDeviceNo(), userId, deviceName, status,"0",wifiName,wifiPassword);
|
||||
if (rows <= 0) {
|
||||
AppDevice current = baseMapper.selectById(bo.getDeviceNo());
|
||||
if (current == null) {
|
||||
@@ -188,7 +226,7 @@ public class AppDeviceServiceImpl implements IAppDeviceService {
|
||||
if (ObjectUtil.isNotNull(device) && StringUtils.isNotBlank(device.getDeviceNo())) {
|
||||
// 通过业务层统一下发命令(含浇水日志记录)
|
||||
String commandId = deviceCommandService.sendSwitchCommand(deviceNo, workStatus, startTime, durationMin);
|
||||
log.info("[DEVICE] switch command sent deviceNo={} commandId={}", deviceNo, commandId);
|
||||
log.info("[设备] 开关命令已下发 设备编号={} 命令编号={}", deviceNo, commandId);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -25,11 +25,7 @@ import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@@ -88,7 +84,13 @@ public class AppScheduleServiceImpl implements IAppScheduleService {
|
||||
@Override
|
||||
public Boolean updateStatusByBo(AppScheduleBo bo) {
|
||||
AppSchedule update = MapstructUtils.convert(bo, AppSchedule.class);
|
||||
return baseMapper.updateById(update) > 0;
|
||||
Long userId = bo.getUserId() == null ? LoginHelper.getUserId() : bo.getUserId();
|
||||
return baseMapper.update(
|
||||
update,
|
||||
Wrappers.<AppSchedule>lambdaQuery()
|
||||
.eq(AppSchedule::getId, bo.getId())
|
||||
.eq(AppSchedule::getUserId, userId)
|
||||
) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -98,7 +100,13 @@ public class AppScheduleServiceImpl implements IAppScheduleService {
|
||||
validEntityBeforeSave(update);
|
||||
|
||||
Map<String, Object> repMap = new HashMap<>();
|
||||
Boolean flag = baseMapper.updateById(update) > 0;
|
||||
Long userId = bo.getUserId() == null ? LoginHelper.getUserId() : bo.getUserId();
|
||||
Boolean flag = baseMapper.update(
|
||||
update,
|
||||
Wrappers.<AppSchedule>lambdaQuery()
|
||||
.eq(AppSchedule::getId, bo.getId())
|
||||
.eq(AppSchedule::getUserId, userId)
|
||||
) > 0;
|
||||
repMap.put("flag", flag);
|
||||
repMap.put("list", new ArrayList<>());
|
||||
repMap.put("appSchedule", "");
|
||||
|
||||
@@ -3,13 +3,13 @@ package org.dromara.app.service.impl;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.app.domain.bo.AppWateringLogBo;
|
||||
import org.dromara.app.domain.mqtt.DeviceCommand;
|
||||
import org.dromara.app.domain.vo.AppDeviceVo;
|
||||
import org.dromara.app.service.IAppDeviceService;
|
||||
import org.dromara.app.service.IAppWateringLogService;
|
||||
import org.dromara.app.service.IDeviceCommandPublisher;
|
||||
import org.dromara.app.service.IDeviceCommandService;
|
||||
import org.dromara.app.domain.bo.AppWateringLogBo;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
@@ -50,7 +50,7 @@ public class DeviceCommandServiceImpl implements IDeviceCommandService {
|
||||
public String sendCommand(DeviceCommand command) {
|
||||
validate(command);
|
||||
String commandId = commandPublisher.send(command);
|
||||
log.info("[CMD] command sent deviceNo={} commandType={} commandId={}",
|
||||
log.info("[命令] 命令已下发 设备编号={} 命令类型={} 命令编号={}",
|
||||
command.getDeviceNo(), command.getCommandType(), commandId);
|
||||
return commandId;
|
||||
}
|
||||
@@ -76,7 +76,7 @@ public class DeviceCommandServiceImpl implements IDeviceCommandService {
|
||||
}
|
||||
|
||||
String commandId = commandPublisher.send(command);
|
||||
log.info("[CMD] switch command sent deviceNo={} workStatus={} durationMin={} commandId={}",
|
||||
log.info("[命令] 开关命令已下发 设备编号={} 工作状态={} 持续分钟数={} 命令编号={}",
|
||||
deviceNo, workStatus, durationMin, commandId);
|
||||
|
||||
Long userId = LoginHelper.getUserId();
|
||||
@@ -86,7 +86,7 @@ public class DeviceCommandServiceImpl implements IDeviceCommandService {
|
||||
} else {
|
||||
boolean updated = wateringLogService.finishManualLog(deviceNo, userId, new Date());
|
||||
if (!updated) {
|
||||
log.warn("[CMD] no active manual watering log found deviceNo={} userId={} stopCommandId={}",
|
||||
log.warn("[命令] 未找到进行中的手动浇水记录 设备编号={} 用户ID={} 停止命令编号={}",
|
||||
deviceNo, userId, commandId);
|
||||
}
|
||||
}
|
||||
@@ -119,7 +119,7 @@ public class DeviceCommandServiceImpl implements IDeviceCommandService {
|
||||
command.setCommandType("bindDevice");
|
||||
Map<String, Object> objectObjectHashMap = new HashMap<>();
|
||||
objectObjectHashMap.put("bindStatus", true);
|
||||
objectObjectHashMap.put("deviceNo", "01");
|
||||
objectObjectHashMap.put("deviceNo", deviceNo);
|
||||
objectObjectHashMap.put("cmd", -1);
|
||||
command.getPayload().putAll(objectObjectHashMap);
|
||||
return sendCommand(command);
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
package org.dromara.app.task;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.app.domain.AppDevice;
|
||||
import org.dromara.app.mapper.AppDeviceMapper;
|
||||
import org.dromara.common.redis.utils.RedisUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 设备离线检测定时任务
|
||||
* <p>
|
||||
* 逻辑:Redis 中 mqtt:device:status:{deviceNo} Key 的 TTL 由 device-status-cache-ttl-seconds 控制。
|
||||
* 设备断电/断网后不再上报数据,TTL 到期 Key 自动消失。
|
||||
* 本任务定期扫描数据库中非离线状态的设备,如果对应 Redis Key 已不存在,
|
||||
* 则将数据库 status 更新为 0(离线)。
|
||||
* </p>
|
||||
*
|
||||
* <p>扫描间隔通过 {@code mqtt.offline-check.interval-ms} 配置,建议小于设备在线 Key 的 TTL。</p>
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class DeviceOfflineCheckTask {
|
||||
|
||||
private final AppDeviceMapper appDeviceMapper;
|
||||
|
||||
@Value("${mqtt.command-ack.device-status-cache-prefix}")
|
||||
private String deviceStatusCachePrefix;
|
||||
|
||||
@Value("${mqtt.offline-check.enabled:true}")
|
||||
private boolean enabled;
|
||||
|
||||
/**
|
||||
* 每 90 秒检查一次(Redis TTL 默认 300s,90s 间隔确保 1.5 个周期内同步)
|
||||
* 可通过 mqtt.offline-check.interval-ms 覆盖(单位 ms)
|
||||
*/
|
||||
@Scheduled(fixedDelayString = "${mqtt.offline-check.interval-ms:90000}")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void checkOfflineDevices() {
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 查询数据库中所有非离线状态的设备,避免状态不是 1 但仍需离线修正的设备被漏掉
|
||||
List<AppDevice> onlineDevices = appDeviceMapper.selectList(
|
||||
new LambdaQueryWrapper<AppDevice>()
|
||||
.ne(AppDevice::getStatus, "0")
|
||||
.select(AppDevice::getDeviceNo)
|
||||
);
|
||||
|
||||
if (onlineDevices.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 过滤出 Redis Key 已过期(不存在)的设备
|
||||
List<String> offlineDeviceNos = onlineDevices.stream()
|
||||
.map(AppDevice::getDeviceNo)
|
||||
.filter(deviceNo -> !RedisUtils.hasKey(deviceStatusCachePrefix + deviceNo))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (offlineDeviceNos.isEmpty()) {
|
||||
log.debug("[设备离线] 本次检测未发现离线设备,扫描设备数:{}", onlineDevices.size());
|
||||
return;
|
||||
}
|
||||
|
||||
// 批量更新为离线
|
||||
int updated = appDeviceMapper.update(null,
|
||||
new LambdaUpdateWrapper<AppDevice>()
|
||||
.set(AppDevice::getStatus, "0")
|
||||
.ne(AppDevice::getStatus, "0")
|
||||
.in(AppDevice::getDeviceNo, offlineDeviceNos)
|
||||
);
|
||||
|
||||
log.info("[设备离线] 检测到 {} 台设备离线,已更新数据库状态,设备编号:{}",
|
||||
updated, offlineDeviceNos);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user