需求分析
點擊同步數據時,要把華為云的數據拉取到我們的系統中
對于新增設備操作,實際上這些參數與華為云產品我們添加設備時的參數是一樣的
表結構設計
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);}