需求分析
点击同步数据时,要把华为云的数据拉取到我们的系统中
对于新增设备操作,实际上这些参数与华为云产品我们添加设备时的参数是一样的
表结构设计
E-R图
数据库字段
接口分析
对于设备中的数据,我们既要再IOT平台存储,又要在数据库中存储.之所以保存两份数据的原因:
IOT平台中只是维护了基础的设备信息,并没有跟业务数据进行绑定,比如,设备属于哪个位置,绑定了哪个老人。
对于我们要实现的功能,华为云提供了丰富的接口
环境集成
IOT平台目前已经给提供了完整的SDK,我们在conmon模块引入华为云的两个依赖
我们添加关于IOT的配置并修改
endpoint
测试客户端能否连接成功
package com.zzyl.test;import com.huaweicloud.sdk.iotda.v5.IoTDAClient;
import com.huaweicloud.sdk.iotda.v5.model.ListProductsRequest;
import com.huaweicloud.sdk.iotda.v5.model.ListProductsResponse;
import com.huaweicloud.sdk.iotda.v5.model.Page;
import com.huaweicloud.sdk.iotda.v5.model.ProductSummary;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.util.List;@SpringBootTest
public class IoTDeviceTest {@Autowiredprivate IoTDAClient client;/*** 查询公共实例下的所有产品* @throws Exception*/@Testpublic void selectProduceList() throws Exception {ListProductsRequest listProductsRequest = new ListProductsRequest();listProductsRequest.setLimit(50);ListProductsResponse response = client.listProducts(listProductsRequest);List<ProductSummary> products = response.getProducts();System.out.println(products);}}
功能开发
从物联网平台同步产品列表
Controller层就是一个简单的post请求
servic层,我们调用华为云的接口请求获得产品的列表存入到Redis中
/*** 同步产品列表*/@Overridepublic void syncProductList(){//请求参数ListProductsRequest listProductsRequest=new ListProductsRequest();//设置条数listProductsRequest.setLimit(50);//发起请求ListProductsResponse response=client.listProducts(listProductsRequest);if(response.getHttpStatusCode()!=200){throw new BaseException("物联网接口 - 查询产品,同步失败");}//存储到redisredisTemplate.opsForValue().set(CacheConstants.IOT_ALL_PRODUCT_LIST,JSONUtil.toJsonStr(response.getProducts()));}
此时打开redis图形化界面,所有数据均已经存储到redis中
查询所有产品列表
Controller
从redis中查询数据并返回
/*** 查询所有产品列表** @return*/@Overridepublic List<ProductVo> allProduct(){//从redis中查询数据String jsonStr=redisTemplate.opsForValue().get(CacheConstants.IOT_ALL_PRODUCT_LIST);//如果数据为空,则返回一个空集合if(StringUtils.isEmpty(jsonStr)){return Collections.emptyList();}//解析数据,并返回return JSONUtil.toList(jsonStr,ProductVo.class);}
前后端联调查看结果
查询已经入住的老人列表
接口文档
对于分页查询接口,传入分页数量和每页数量
通过传入的状态作为mp的条件查询我们想要的姓名,年龄等
/*** 老人分页查询* @param status* @param pageNum* @param pageSize* @return*/@Overridepublic TableDataInfo pageQuery(Integer status,Integer pageNum,Integer pageSize){//分页查询老人数据//创建条件构建器LambdaQueryWrapper<Elder> queryWrapper=new LambdaQueryWrapper<>();//创建分页对象Page<Elder> page=new Page<>(pageNum,pageSize);//按照状态查询if(ObjectUtil.isNotNull(status)){queryWrapper.eq(Elder::getStatus,status);}//sql返回结果,只展示主键、名字、身份照号、床位编号queryWrapper.select(Elder::getId,Elder::getName,Elder::getIdCardNo,Elder::getBedNumber);//执行查询page=page(page,queryWrapper);//返回分页结果return getDataTable(page);}/*** 封装分页结果* @param page* @return*/private TableDataInfo getDataTable(Page<Elder> page){//封装分页结果TableDataInfo tableDataInfo=new TableDataInfo();tableDataInfo.setTotal(page.getTotal());tableDataInfo.setCode(HttpStatus.SUCCESS);tableDataInfo.setMsg("请求成功");tableDataInfo.setRows(page.getRecords());return tableDataInfo;}
注册设备
在Controller层,我们传入设备信息
service层的逻辑
/*** 注册设备* @param deviceDto*/@Overridepublic void registerDevice(DeviceDto deviceDto){//判断设备名称是否重复LambdaQueryWrapper<Device> queryWrapper=new LambdaQueryWrapper<>();queryWrapper.eq(Device::getDeviceName,deviceDto.getDeviceName());if(count(queryWrapper)>0){throw new BaseException("设备名称已存在,请重新输入");}//检验设备标识码是否重复LambdaQueryWrapper<Device> queryWrapperNodeId=new LambdaQueryWrapper<>();queryWrapperNodeId.eq(Device::getNodeId,deviceDto.getNodeId());if(count(queryWrapperNodeId)>0){throw new BaseException("设备标识码已存在,请重新输入");}//校验同一位置是否绑定了同一类产品LambdaQueryWrapper<Device> condition=new LambdaQueryWrapper<>();condition.eq(Device::getProductKey,deviceDto.getProductKey()).eq(Device::getLocationType,deviceDto.getLocationType()).eq(Device::getPhysicalLocationType,deviceDto.getPhysicalLocationType()).eq(Device::getBindingLocation,deviceDto.getBindingLocation());if(count(condition)>0){throw new BaseException("该老人/位置已绑定该产品,请重新选择");}//iot中新增设备AddDeviceRequest request=new AddDeviceRequest();AddDevice body=new AddDevice();body.withProductId(deviceDto.getProductKey());body.withDeviceName(deviceDto.getDeviceName());body.withNodeId(deviceDto.getNodeId());request.withBody(body);AuthInfo authInfo=new AuthInfo();//秘钥String secret=UUID.randomUUID().toString().replaceAll("-","");authInfo.withSecret(secret);body.setAuthInfo(authInfo);AddDeviceResponse response;try{response=client.addDevice(request);}catch(Exception e){e.printStackTrace();throw new BaseException("物联网接口 - 注册设备,调用失败");}//设备数据保存到数据库//属性拷贝Device device=BeanUtil.toBean(deviceDto,Device.class);//设备id、设备绑定状态device.setIotId(response.getDeviceId());//秘钥device.setSecret(secret);//在数据库中新增设备try{save(device);}catch(Exception e){throw new BaseException("同一个位置不能绑定同类型的产品");}}
测试
查看设备上报的数据
前端传入Controller层物联网设备的id
/*** 获取设备详细信息*/@GetMapping("/{iotId}")@ApiOperation("获取设备详细信息")@ApiImplicitParams({@ApiImplicitParam(name = "iotId", value = "物联网设备id", required = true, dataTypeClass = String.class)})public AjaxResult getInfo(@PathVariable("iotId") String iotId){return success(deviceService.queryDeviceDetail(iotId));}/*** 查询设备上报数据*/@GetMapping("/queryServiceProperties/{iotId}")@ApiOperation("查询设备上报数据")@ApiImplicitParams({@ApiImplicitParam(name = "iotId", value = "物联网设备id", required = true, dataTypeClass = String.class)})public AjaxResult queryServiceProperties(@PathVariable("iotId") String iotId){AjaxResult ajaxResult=deviceService.queryServiceProperties(iotId);return ajaxResult;}
service层通过Controller层传入的iotId查询数据库中的设备信息,调用华为的物联网接口查询华为云中设备的实时信息,将数据封装到deviceVo中返回给前端
/*** 查询设备详情** @return*/@Overridepublic DeviceDetailVo queryDeviceDetail(String iotId){//查询数据库Device device=getOne(Wrappers.<Device>lambdaQuery().eq(Device::getIotId,iotId));if(ObjectUtil.isEmpty(device)){return null;}//调用华为云物联网接口ShowDeviceRequest request=new ShowDeviceRequest();request.setDeviceId(iotId);ShowDeviceResponse response;try{response=client.showDevice(request);}catch(Exception e){log.info("物联网接口 - 查询设备详情,调用失败:{}",e.getMessage());throw new BaseException("物联网接口 - 查询设备详情,调用失败");}//属性拷贝DeviceDetailVo deviceVo=BeanUtil.toBean(device,DeviceDetailVo.class);deviceVo.setDeviceStatus(response.getStatus());String activeTimeStr=response.getActiveTime();if(StringUtils.isNotEmpty(activeTimeStr)){LocalDateTime activeTime=LocalDateTimeUtil.parse(activeTimeStr,DatePattern.UTC_MS_PATTERN);//日期时区转换activeTime=activeTime.atZone(ZoneId.from(ZoneOffset.UTC)).withZoneSameInstant(ZoneId.of("Asia/Shanghai")).toLocalDateTime();deviceVo.setActiveTime(activeTime);}return deviceVo;}/*** 查询设备上报数据** @param iotId* @return*/@Overridepublic AjaxResult queryServiceProperties(String iotId){ShowDeviceShadowRequest request=new ShowDeviceShadowRequest();request.setDeviceId(iotId);ShowDeviceShadowResponse response=client.showDeviceShadow(request);if(response.getHttpStatusCode()!=200){throw new BaseException("物联网接口 - 查询设备上报数据,调用失败");}List<DeviceShadowData> shadow=response.getShadow();if(CollUtil.isEmpty(shadow)){List<Object> emptyList=Collections.emptyList();return AjaxResult.success(emptyList);}//返回数据JSONObject jsonObject=JSONUtil.parseObj(shadow.get(0).getReported().getProperties());List<Map<String, Object>>list=new ArrayList<>();//处理上报时间日期LocalDateTime activeTime=LocalDateTimeUtil.parse(shadow.get(0).getReported().getEventTime(),"yyyyMMdd'T'HHmmss'Z'");//日期时区转换LocalDateTime eventTime=activeTime.atZone(ZoneId.from(ZoneOffset.UTC)).withZoneSameInstant(ZoneId.of("Asia/Shanghai")).toLocalDateTime();jsonObject.forEach((k,v)->{Map<String, Object> map=new HashMap<>();map.put("functionId",k);map.put("value",v);map.put("eventTime",eventTime);list.add(map);});return AjaxResult.success(list);}