From f1b05078bdac2142d4c16ffcc3814cf5f712353a Mon Sep 17 00:00:00 2001 From: ruanrongman Date: Mon, 26 Aug 2024 21:14:31 +0800 Subject: [PATCH] event engine and search tool --- pom.xml | 11 + .../java/top/rslly/iot/controllers/Auth.java | 45 +++- .../java/top/rslly/iot/controllers/Tool.java | 19 +- ...epository.java => DataTimeRepository.java} | 2 +- .../rslly/iot/dao/EventDataRepository.java | 35 +++ .../rslly/iot/dao/EventStorageRepository.java | 47 ++++ .../iot/dao/EventStorageTimeRepository.java | 53 +++++ .../rslly/iot/dao/ProductEventRepository.java | 40 ++++ .../top/rslly/iot/models/EventDataEntity.java | 100 +++++++++ .../rslly/iot/models/EventStorageEntity.java | 119 ++++++++++ .../rslly/iot/models/ProductEventEntity.java | 89 ++++++++ .../influxdb/EventStorageTimeEntity.java | 37 +++ .../prompt/ProductDeviceDescription.java | 1 + .../rslly/iot/param/request/EventData.java | 34 +++ .../rslly/iot/param/request/ProductEvent.java | 30 +++ .../top/rslly/iot/param/request/ReadData.java | 1 + .../top/rslly/iot/services/DataService.java | 3 +- .../rslly/iot/services/DataServiceImpl.java | 23 +- .../rslly/iot/services/EventDataService.java | 38 ++++ .../iot/services/EventDataServiceImpl.java | 92 ++++++++ .../iot/services/EventStorageService.java | 40 ++++ .../iot/services/EventStorageServiceImpl.java | 98 ++++++++ .../iot/services/HardWareServiceImpl.java | 6 + .../iot/services/ProductDataServiceImpl.java | 2 + .../services/ProductDeviceServiceImpl.java | 9 +- .../iot/services/ProductEventService.java | 38 ++++ .../iot/services/ProductEventServiceImpl.java | 83 +++++++ .../iot/services/ProductModelServiceImpl.java | 3 + .../iot/services/ProductServiceImpl.java | 3 + .../services/WxProductBindServiceImpl.java | 4 + .../rslly/iot/transfer/DealThingsEvent.java | 110 +++++++++ .../rslly/iot/transfer/DealThingsModel.java | 7 +- .../rslly/iot/transfer/HookProviderImpl.java | 9 +- .../top/rslly/iot/utility/DataCleanAuto.java | 4 +- .../java/top/rslly/iot/utility/ai/Prompt.java | 13 +- .../rslly/iot/utility/ai/chain/Router.java | 12 +- .../rslly/iot/utility/ai/llm/DeepSeek.java | 8 + .../utility/ai/prompts/ChatToolPrompt.java | 51 +++++ .../ai/prompts/ClassifierToolPrompt.java | 134 +++++++++++ .../utility/ai/prompts/ControlToolPrompt.java | 84 +++++++ .../utility/ai/prompts/MusicToolPrompt.java | 64 ++++++ .../iot/utility/ai/prompts/ReactPrompt.java | 103 +++++++++ .../ai/prompts/ScheduleToolPrompt.java | 131 +++++++++++ .../utility/ai/prompts/SearchToolPrompt.java | 44 ++++ .../utility/ai/prompts/WeatherToolPrompt.java | 82 +++++++ .../ai/prompts/WxBoundProductToolPrompt.java | 67 ++++++ .../ai/prompts/WxProductActiveToolPrompt.java | 64 ++++++ .../rslly/iot/utility/ai/toolAgent/Agent.java | 5 +- .../rslly/iot/utility/ai/tools/ChatTool.java | 12 +- .../iot/utility/ai/tools/ClassifierTool.java | 8 +- .../iot/utility/ai/tools/ControlTool.java | 9 +- .../rslly/iot/utility/ai/tools/MusicTool.java | 6 +- .../iot/utility/ai/tools/ScheduleTool.java | 8 +- .../iot/utility/ai/tools/SearchTool.java | 211 ++++++++++++++++++ .../iot/utility/ai/tools/WeatherTool.java | 8 +- .../utility/ai/tools/WxBoundProductTool.java | 12 +- .../utility/ai/tools/WxProductActiveTool.java | 8 +- .../iot/utility/ai/voice/DashScopeVoice.java | 9 +- .../rslly/iot/utility/result/ResultCode.java | 2 +- .../top/rslly/iot/utility/wx/SmartRobot.java | 6 +- src/main/resources/application.yaml | 7 +- .../top/rslly/iot/DemoApplicationTests.java | 24 +- .../java/top/rslly/iot/searchToolTests.java | 36 +++ 63 files changed, 2360 insertions(+), 103 deletions(-) rename src/main/java/top/rslly/iot/dao/{TimeDataRepository.java => DataTimeRepository.java} (97%) create mode 100644 src/main/java/top/rslly/iot/dao/EventDataRepository.java create mode 100644 src/main/java/top/rslly/iot/dao/EventStorageRepository.java create mode 100644 src/main/java/top/rslly/iot/dao/EventStorageTimeRepository.java create mode 100644 src/main/java/top/rslly/iot/dao/ProductEventRepository.java create mode 100644 src/main/java/top/rslly/iot/models/EventDataEntity.java create mode 100644 src/main/java/top/rslly/iot/models/EventStorageEntity.java create mode 100644 src/main/java/top/rslly/iot/models/ProductEventEntity.java create mode 100644 src/main/java/top/rslly/iot/models/influxdb/EventStorageTimeEntity.java create mode 100644 src/main/java/top/rslly/iot/param/request/EventData.java create mode 100644 src/main/java/top/rslly/iot/param/request/ProductEvent.java create mode 100644 src/main/java/top/rslly/iot/services/EventDataService.java create mode 100644 src/main/java/top/rslly/iot/services/EventDataServiceImpl.java create mode 100644 src/main/java/top/rslly/iot/services/EventStorageService.java create mode 100644 src/main/java/top/rslly/iot/services/EventStorageServiceImpl.java create mode 100644 src/main/java/top/rslly/iot/services/ProductEventService.java create mode 100644 src/main/java/top/rslly/iot/services/ProductEventServiceImpl.java create mode 100644 src/main/java/top/rslly/iot/transfer/DealThingsEvent.java create mode 100644 src/main/java/top/rslly/iot/utility/ai/prompts/ChatToolPrompt.java create mode 100644 src/main/java/top/rslly/iot/utility/ai/prompts/ClassifierToolPrompt.java create mode 100644 src/main/java/top/rslly/iot/utility/ai/prompts/ControlToolPrompt.java create mode 100644 src/main/java/top/rslly/iot/utility/ai/prompts/MusicToolPrompt.java create mode 100644 src/main/java/top/rslly/iot/utility/ai/prompts/ReactPrompt.java create mode 100644 src/main/java/top/rslly/iot/utility/ai/prompts/ScheduleToolPrompt.java create mode 100644 src/main/java/top/rslly/iot/utility/ai/prompts/SearchToolPrompt.java create mode 100644 src/main/java/top/rslly/iot/utility/ai/prompts/WeatherToolPrompt.java create mode 100644 src/main/java/top/rslly/iot/utility/ai/prompts/WxBoundProductToolPrompt.java create mode 100644 src/main/java/top/rslly/iot/utility/ai/prompts/WxProductActiveToolPrompt.java create mode 100644 src/main/java/top/rslly/iot/utility/ai/tools/SearchTool.java create mode 100644 src/test/java/top/rslly/iot/searchToolTests.java diff --git a/pom.xml b/pom.xml index 105ecbc..06a5858 100644 --- a/pom.xml +++ b/pom.xml @@ -96,6 +96,17 @@ dom4j 2.0.0 + + + org.seleniumhq.selenium + selenium-java + 4.23.1 + + + com.google.guava + guava + 31.1-jre + org.nutz diff --git a/src/main/java/top/rslly/iot/controllers/Auth.java b/src/main/java/top/rslly/iot/controllers/Auth.java index 6a2d39c..49d7f42 100644 --- a/src/main/java/top/rslly/iot/controllers/Auth.java +++ b/src/main/java/top/rslly/iot/controllers/Auth.java @@ -24,10 +24,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; -import top.rslly.iot.param.request.Product; -import top.rslly.iot.param.request.ProductData; -import top.rslly.iot.param.request.ProductDevice; -import top.rslly.iot.param.request.ProductModel; +import top.rslly.iot.param.request.*; import top.rslly.iot.services.*; import top.rslly.iot.utility.result.JsonResult; import top.rslly.iot.utility.result.ResultTool; @@ -45,6 +42,10 @@ public class Auth { @Autowired ProductDataServiceImpl productDataService; @Autowired + EventDataServiceImpl eventDataService; + @Autowired + ProductEventServiceImpl productEventService; + @Autowired MqttUserServiceImpl mqttUserService; @@ -100,6 +101,24 @@ public JsonResult ProductModel(@RequestParam("id") int id) { return productModelService.deleteProductModel(id); } + @Operation(summary = "获取事件", description = "获取物模型的事件") + @RequestMapping(value = "/ProductEvent", method = RequestMethod.GET) + public JsonResult ProductEvent() { + return productEventService.getProductEvent(); + } + + @Operation(summary = "提交事件", description = "提交物模型的事件") + @RequestMapping(value = "/ProductEvent", method = RequestMethod.POST) + public JsonResult ProductEvent(@RequestBody ProductEvent productEvent) { + return productEventService.postProductEvent(productEvent); + } + + @Operation(summary = "删除事件", description = "删除物模型的事件") + @RequestMapping(value = "/ProductEvent", method = RequestMethod.DELETE) + public JsonResult ProductEvent(@RequestParam("id") int id) { + return productEventService.deleteProductEvent(id); + } + @Operation(summary = "获取设备", description = "获取物模型的设备") @RequestMapping(value = "/ProductDevice", method = RequestMethod.GET) public JsonResult ProductDevice() { @@ -136,6 +155,24 @@ public JsonResult ProductData(@RequestParam("id") int id) { return productDataService.deleteProductData(id); } + @Operation(summary = "获取事件入参", description = "获取事件入参") + @RequestMapping(value = "/EventData", method = RequestMethod.GET) + public JsonResult EventData() { + return eventDataService.getEventData(); + } + + @Operation(summary = "提交事件入参", description = "提交事件入参") + @RequestMapping(value = "/EventData", method = RequestMethod.POST) + public JsonResult EventData(@RequestBody EventData eventData) { + return eventDataService.postEventData(eventData); + } + + @Operation(summary = "删除事件入参", description = "删除事件入参") + @RequestMapping(value = "/EventData", method = RequestMethod.DELETE) + public JsonResult EventData(@RequestParam("id") int id) { + return eventDataService.deleteEventData(id); + } + } diff --git a/src/main/java/top/rslly/iot/controllers/Tool.java b/src/main/java/top/rslly/iot/controllers/Tool.java index d7869ee..6596a16 100644 --- a/src/main/java/top/rslly/iot/controllers/Tool.java +++ b/src/main/java/top/rslly/iot/controllers/Tool.java @@ -31,6 +31,7 @@ import top.rslly.iot.param.request.MetaData; import top.rslly.iot.param.request.ReadData; import top.rslly.iot.services.DataServiceImpl; +import top.rslly.iot.services.EventStorageServiceImpl; import top.rslly.iot.services.HardWareServiceImpl; import top.rslly.iot.services.OtaServiceImpl; import top.rslly.iot.utility.RedisUtil; @@ -55,6 +56,8 @@ public class Tool { @Autowired private DataServiceImpl dataService; @Autowired + private EventStorageServiceImpl eventStorageService; + @Autowired private HardWareServiceImpl hardWareService; @Autowired private OtaServiceImpl otaService; @@ -77,8 +80,17 @@ public JsonResult control(@RequestBody ControlParam controlParam) throws Mqtt @Operation(summary = "用于获取物联网一段时间的设备数据", description = "时间参数请使用两个毫秒时间戳") @RequestMapping(value = "/readData", method = RequestMethod.POST) public JsonResult readData(@RequestBody ReadData readData) { - return dataService.findAllByTimeBetweenAndDeviceName(readData.getTime1(), readData.getTime2(), - readData.getName()); + return dataService.findAllByTimeBetweenAndDeviceNameAndJsonKey(readData.getTime1(), + readData.getTime2(), + readData.getName(), readData.getJsonKey()); + } + + @Operation(summary = "用于获取物联网一段时间的设备事件数据", description = "时间参数请使用两个毫秒时间戳") + @RequestMapping(value = "/readEvent", method = RequestMethod.POST) + public JsonResult readEvent(@RequestBody ReadData readData) { + return eventStorageService.findAllByTimeBetweenAndDeviceNameAndJsonKey(readData.getTime1(), + readData.getTime2(), + readData.getName(), readData.getJsonKey()); } @Operation(summary = "获取属性实时数据", description = "高性能接口(带redis缓存)") @@ -91,8 +103,7 @@ public JsonResult metaData(@RequestBody MetaData metaData) { @RequestMapping(value = "/aiControl", method = RequestMethod.POST) public JsonResult aiControl(@RequestBody AiControl aiControl) { var answer = - router.response(aiControl.getContent(), aiControl.getChatId(), aiControl.getProductId(), - "1234"); + router.response(aiControl.getContent(), aiControl.getChatId(), aiControl.getProductId()); return ResultTool.success(answer); } diff --git a/src/main/java/top/rslly/iot/dao/TimeDataRepository.java b/src/main/java/top/rslly/iot/dao/DataTimeRepository.java similarity index 97% rename from src/main/java/top/rslly/iot/dao/TimeDataRepository.java rename to src/main/java/top/rslly/iot/dao/DataTimeRepository.java index cd23ba4..7f8aa36 100644 --- a/src/main/java/top/rslly/iot/dao/TimeDataRepository.java +++ b/src/main/java/top/rslly/iot/dao/DataTimeRepository.java @@ -27,7 +27,7 @@ import java.util.List; -public interface TimeDataRepository extends InfluxDBBaseMapper { +public interface DataTimeRepository extends InfluxDBBaseMapper { @Select(value = "SELECT * FROM \"data\" where time >=#{time1}ms and time <=#{time2}ms", resultType = DataEntity.class) List findAllByTimeBetween(@Param("time1") Long time1, @Param("time2") Long time2); diff --git a/src/main/java/top/rslly/iot/dao/EventDataRepository.java b/src/main/java/top/rslly/iot/dao/EventDataRepository.java new file mode 100644 index 0000000..08a6acf --- /dev/null +++ b/src/main/java/top/rslly/iot/dao/EventDataRepository.java @@ -0,0 +1,35 @@ +/** + * Copyright © 2023-2030 The ruanrongman Authors + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package top.rslly.iot.dao; + +import org.springframework.data.jpa.repository.JpaRepository; +import top.rslly.iot.models.EventDataEntity; + +import javax.transaction.Transactional; +import java.util.List; + +public interface EventDataRepository extends JpaRepository { + List findAllByModelId(int modelId); + + List findAllByModelIdAndJsonKey(int modelId, String jsonKey); + + @Transactional + List deleteById(int id); +} diff --git a/src/main/java/top/rslly/iot/dao/EventStorageRepository.java b/src/main/java/top/rslly/iot/dao/EventStorageRepository.java new file mode 100644 index 0000000..0b7affe --- /dev/null +++ b/src/main/java/top/rslly/iot/dao/EventStorageRepository.java @@ -0,0 +1,47 @@ +/** + * Copyright © 2023-2030 The ruanrongman Authors + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package top.rslly.iot.dao; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.QueryByExampleExecutor; +import org.springframework.transaction.annotation.Transactional; +import top.rslly.iot.models.EventStorageEntity; + +import java.util.List; + +public interface EventStorageRepository extends JpaRepository, + JpaSpecificationExecutor, QueryByExampleExecutor { + List findAllByTimeBetweenAndDeviceIdAndJsonKey(long time, long time2, + int deviceId, + String jsonKey); + + List findAllByTimeBetweenAndDeviceId(long time, long time2, int deviceId); + + @Query( + value = "SELECT * FROM event_storage WHERE device_id =?1 And json_key=?2 ORDER BY time DESC LIMIT 1", + nativeQuery = true) + List findAllBySort(int deviceId, String jsonKey); + + @Transactional + List deleteAllByTimeBeforeAndDeviceIdAndJsonKey(long time, int deviceId, + String jsonKey); +} diff --git a/src/main/java/top/rslly/iot/dao/EventStorageTimeRepository.java b/src/main/java/top/rslly/iot/dao/EventStorageTimeRepository.java new file mode 100644 index 0000000..c1ddeed --- /dev/null +++ b/src/main/java/top/rslly/iot/dao/EventStorageTimeRepository.java @@ -0,0 +1,53 @@ +/** + * Copyright © 2023-2030 The ruanrongman Authors + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package top.rslly.iot.dao; + +import top.rslly.iot.models.EventStorageEntity; +import top.rslly.iot.utility.influxdb.InfluxDBBaseMapper; +import top.rslly.iot.utility.influxdb.ano.Delete; +import top.rslly.iot.utility.influxdb.ano.Param; +import top.rslly.iot.utility.influxdb.ano.Select; + +import java.util.List; + +public interface EventStorageTimeRepository extends InfluxDBBaseMapper { + @Select( + value = "SELECT * FROM \"event_storage\" where time >=#{time}ms and time <=#{time2}ms and deviceId='#{deviceId}'", + resultType = EventStorageEntity.class) + List findAllByTimeBetweenAndDeviceId(@Param("time") long time, + @Param("time2") long time2, @Param("deviceId") int deviceId); + + @Select( + value = "SELECT * FROM \"event_storage\" where time >=#{time}ms and time <=#{time2}ms and deviceId='#{deviceId}' and jsonKey=#{jsonKey}", + resultType = EventStorageEntity.class) + List findAllByTimeBetweenAndDeviceIdAndJsonKey(@Param("time") long time, + @Param("time2") long time2, @Param("deviceId") int deviceId, + @Param("jsonKey") String jsonKey); + + @Select( + value = "SELECT * FROM \"event_storage\" WHERE deviceId='#{deviceId}' and jsonKey=#{jsonKey} ORDER BY time DESC LIMIT 1", + resultType = EventStorageEntity.class) + List findAllBySort(int deviceId, String jsonKey); + + @Delete( + value = "DELETE FROM \"event_storage\" where time <=#{time}ms and deviceId='#{deviceId}' and jsonKey=#{jsonKey}") + void deleteAllByTimeBeforeAndDeviceIdAndJsonKey(@Param("time") long time, + @Param("deviceId") int deviceId, @Param("jsonKey") String jsonKey); +} diff --git a/src/main/java/top/rslly/iot/dao/ProductEventRepository.java b/src/main/java/top/rslly/iot/dao/ProductEventRepository.java new file mode 100644 index 0000000..1b7b6b9 --- /dev/null +++ b/src/main/java/top/rslly/iot/dao/ProductEventRepository.java @@ -0,0 +1,40 @@ +/** + * Copyright © 2023-2030 The ruanrongman Authors + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package top.rslly.iot.dao; + +import org.springframework.data.jpa.repository.JpaRepository; +import top.rslly.iot.models.ProductEventEntity; +import top.rslly.iot.models.ProductModelEntity; + +import javax.transaction.Transactional; +import java.util.List; + +public interface ProductEventRepository extends JpaRepository { + + List findAllByModelIdAndName(int modelId, String name); + + List findAllByName(String name); + + + List findAllById(int id); + + @Transactional + List deleteById(int id); +} diff --git a/src/main/java/top/rslly/iot/models/EventDataEntity.java b/src/main/java/top/rslly/iot/models/EventDataEntity.java new file mode 100644 index 0000000..f18527e --- /dev/null +++ b/src/main/java/top/rslly/iot/models/EventDataEntity.java @@ -0,0 +1,100 @@ +/** + * Copyright © 2023-2030 The ruanrongman Authors + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package top.rslly.iot.models; + +import javax.persistence.*; +import java.util.Objects; + +@Entity +@Table(name = "event_data", schema = "cwliot1.8", catalog = "") +public class EventDataEntity { + private int id; + private String jsonKey; + private int modelId; + private String description; + private String type; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + @Basic + @Column(name = "json_key") + public String getJsonKey() { + return jsonKey; + } + + public void setJsonKey(String jsonKey) { + this.jsonKey = jsonKey; + } + + @Basic + @Column(name = "model_id") + public int getModelId() { + return modelId; + } + + public void setModelId(int modelId) { + this.modelId = modelId; + } + + @Basic + @Column(name = "description") + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + @Basic + @Column(name = "type") + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + EventDataEntity that = (EventDataEntity) o; + return id == that.id && modelId == that.modelId && Objects.equals(jsonKey, that.jsonKey) + && Objects.equals(description, that.description) && Objects.equals(type, that.type); + } + + @Override + public int hashCode() { + return Objects.hash(id, jsonKey, modelId, description, type); + } +} diff --git a/src/main/java/top/rslly/iot/models/EventStorageEntity.java b/src/main/java/top/rslly/iot/models/EventStorageEntity.java new file mode 100644 index 0000000..5ffa460 --- /dev/null +++ b/src/main/java/top/rslly/iot/models/EventStorageEntity.java @@ -0,0 +1,119 @@ +/** + * Copyright © 2023-2030 The ruanrongman Authors + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package top.rslly.iot.models; + +import org.influxdb.annotation.Measurement; +import top.rslly.iot.utility.influxdb.ano.Tag; +import top.rslly.iot.utility.influxdb.ano.TimeColumn; + +import javax.persistence.*; +import java.util.Objects; + +@Entity +@Measurement(name = "event_storage") +@Table(name = "event_storage", schema = "cwliot1.8", catalog = "") +public class EventStorageEntity { + @TimeColumn + private long time; + @Tag + private int deviceId; + @Tag + private String jsonKey; + private String value; + private int eventId; + private String characteristic; + + @Id + @Column(name = "time") + public long getTime() { + return time; + } + + public void setTime(long time) { + this.time = time; + } + + @Basic + @Column(name = "device_id") + public int getDeviceId() { + return deviceId; + } + + public void setDeviceId(int deviceId) { + this.deviceId = deviceId; + } + + @Basic + @Column(name = "json_key") + public String getJsonKey() { + return jsonKey; + } + + public void setJsonKey(String jsonKey) { + this.jsonKey = jsonKey; + } + + @Basic + @Column(name = "value") + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + @Basic + @Column(name = "event_id") + public int getEventId() { + return eventId; + } + + public void setEventId(int eventId) { + this.eventId = eventId; + } + + @Basic + @Column(name = "characteristic") + public String getCharacteristic() { + return characteristic; + } + + public void setCharacteristic(String characteristic) { + this.characteristic = characteristic; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + EventStorageEntity that = (EventStorageEntity) o; + return time == that.time && deviceId == that.deviceId && eventId == that.eventId + && Objects.equals(jsonKey, that.jsonKey) && Objects.equals(value, that.value) + && Objects.equals(characteristic, that.characteristic); + } + + @Override + public int hashCode() { + return Objects.hash(time, deviceId, jsonKey, value, eventId, characteristic); + } +} diff --git a/src/main/java/top/rslly/iot/models/ProductEventEntity.java b/src/main/java/top/rslly/iot/models/ProductEventEntity.java new file mode 100644 index 0000000..d5f4e70 --- /dev/null +++ b/src/main/java/top/rslly/iot/models/ProductEventEntity.java @@ -0,0 +1,89 @@ +/** + * Copyright © 2023-2030 The ruanrongman Authors + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package top.rslly.iot.models; + +import javax.persistence.*; +import java.util.Objects; + +@Entity +@Table(name = "product_event", schema = "cwliot1.8", catalog = "") +public class ProductEventEntity { + private int id; + private String name; + private String description; + private int modelId; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + @Basic + @Column(name = "name") + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Basic + @Column(name = "description") + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + @Basic + @Column(name = "model_id") + public int getModelId() { + return modelId; + } + + public void setModelId(int modelId) { + this.modelId = modelId; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + ProductEventEntity that = (ProductEventEntity) o; + return id == that.id && modelId == that.modelId && Objects.equals(name, that.name) + && Objects.equals(description, that.description); + } + + @Override + public int hashCode() { + return Objects.hash(id, name, description, modelId); + } +} diff --git a/src/main/java/top/rslly/iot/models/influxdb/EventStorageTimeEntity.java b/src/main/java/top/rslly/iot/models/influxdb/EventStorageTimeEntity.java new file mode 100644 index 0000000..01b2c53 --- /dev/null +++ b/src/main/java/top/rslly/iot/models/influxdb/EventStorageTimeEntity.java @@ -0,0 +1,37 @@ +/** + * Copyright © 2023-2030 The ruanrongman Authors + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package top.rslly.iot.models.influxdb; + +import lombok.Data; +import org.influxdb.annotation.Measurement; +import top.rslly.iot.utility.influxdb.ano.Tag; +import top.rslly.iot.utility.influxdb.ano.TimeColumn; + +@Data +@Measurement(name = "event_storage") +public class EventStorageTimeEntity { + @TimeColumn + private Long time; + @Tag + private int deviceId; + private String jsonKey; + private String value; + private String characteristic; +} diff --git a/src/main/java/top/rslly/iot/param/prompt/ProductDeviceDescription.java b/src/main/java/top/rslly/iot/param/prompt/ProductDeviceDescription.java index 40c79ff..10032b1 100644 --- a/src/main/java/top/rslly/iot/param/prompt/ProductDeviceDescription.java +++ b/src/main/java/top/rslly/iot/param/prompt/ProductDeviceDescription.java @@ -30,4 +30,5 @@ public class ProductDeviceDescription { private String description; private List properties; private List values; + private String allow; } diff --git a/src/main/java/top/rslly/iot/param/request/EventData.java b/src/main/java/top/rslly/iot/param/request/EventData.java new file mode 100644 index 0000000..3c37607 --- /dev/null +++ b/src/main/java/top/rslly/iot/param/request/EventData.java @@ -0,0 +1,34 @@ +/** + * Copyright © 2023-2030 The ruanrongman Authors + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package top.rslly.iot.param.request; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +@AllArgsConstructor +@Getter +@Setter +public class EventData { + private String jsonKey; + private int modelId; + private String description; + private String type; +} diff --git a/src/main/java/top/rslly/iot/param/request/ProductEvent.java b/src/main/java/top/rslly/iot/param/request/ProductEvent.java new file mode 100644 index 0000000..d8b3e97 --- /dev/null +++ b/src/main/java/top/rslly/iot/param/request/ProductEvent.java @@ -0,0 +1,30 @@ +/** + * Copyright © 2023-2030 The ruanrongman Authors + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package top.rslly.iot.param.request; + +import lombok.Data; + +@Data +public class ProductEvent { + private String name; + private String description; + private int modelId; + +} diff --git a/src/main/java/top/rslly/iot/param/request/ReadData.java b/src/main/java/top/rslly/iot/param/request/ReadData.java index 6d30f6c..8731514 100644 --- a/src/main/java/top/rslly/iot/param/request/ReadData.java +++ b/src/main/java/top/rslly/iot/param/request/ReadData.java @@ -28,4 +28,5 @@ public class ReadData { Long time1; Long time2; String name; + String jsonKey; } diff --git a/src/main/java/top/rslly/iot/services/DataService.java b/src/main/java/top/rslly/iot/services/DataService.java index f299f60..49ef308 100644 --- a/src/main/java/top/rslly/iot/services/DataService.java +++ b/src/main/java/top/rslly/iot/services/DataService.java @@ -27,7 +27,8 @@ public interface DataService { void insert(DataEntity dataEntity); - JsonResult findAllByTimeBetweenAndDeviceName(long time, long time2, String name); + JsonResult findAllByTimeBetweenAndDeviceNameAndJsonKey(long time, long time2, String name, + String jsonKey); List findAllByTimeBetweenAndDeviceIdAndJsonKey(long time, long time2, int deviceId, String jsonKey); diff --git a/src/main/java/top/rslly/iot/services/DataServiceImpl.java b/src/main/java/top/rslly/iot/services/DataServiceImpl.java index 826426f..b2ba11a 100644 --- a/src/main/java/top/rslly/iot/services/DataServiceImpl.java +++ b/src/main/java/top/rslly/iot/services/DataServiceImpl.java @@ -23,7 +23,7 @@ import org.springframework.stereotype.Service; import top.rslly.iot.dao.DataRepository; import top.rslly.iot.dao.ProductDeviceRepository; -import top.rslly.iot.dao.TimeDataRepository; +import top.rslly.iot.dao.DataTimeRepository; import top.rslly.iot.models.DataEntity; import top.rslly.iot.utility.Cast; import top.rslly.iot.utility.RedisUtil; @@ -45,20 +45,21 @@ public class DataServiceImpl implements DataService { @Resource private ProductDeviceRepository deviceRepository; @Resource - private TimeDataRepository timeDataRepository; + private DataTimeRepository dataTimeRepository; @Resource private RedisUtil redisUtil; @Override public void insert(DataEntity dataEntity) { if (database.equals("influxdb")) { - timeDataRepository.insert(dataEntity); + dataTimeRepository.insert(dataEntity); } else dataRepository.save(dataEntity); } @Override - public JsonResult findAllByTimeBetweenAndDeviceName(long time, long time2, String name) { + public JsonResult findAllByTimeBetweenAndDeviceNameAndJsonKey(long time, long time2, + String name, String jsonKey) { var productDeviceEntities = deviceRepository.findAllByName(name); if (productDeviceEntities.isEmpty()) { return ResultTool.fail(ResultCode.PARAM_NOT_VALID); @@ -66,9 +67,11 @@ public JsonResult findAllByTimeBetweenAndDeviceName(long time, long time2, St int deviceId = productDeviceEntities.get(0).getId(); List res; if (database.equals("influxdb")) { - res = timeDataRepository.findAllByTimeBetweenAndDeviceId(time, time2, deviceId); + res = dataTimeRepository.findAllByTimeBetweenAndDeviceIdAndJsonKey(time, time2, deviceId, + jsonKey); } else { - res = dataRepository.findAllByTimeBetweenAndDeviceId(time, time2, deviceId); + res = + dataRepository.findAllByTimeBetweenAndDeviceIdAndJsonKey(time, time2, deviceId, jsonKey); } if (res.isEmpty()) return ResultTool.fail(ResultCode.PARAM_NOT_VALID); @@ -79,7 +82,7 @@ public JsonResult findAllByTimeBetweenAndDeviceName(long time, long time2, St public List findAllByTimeBetweenAndDeviceIdAndJsonKey(long time, long time2, int deviceId, String jsonKey) { if (database.equals("influxdb")) { - return timeDataRepository.findAllByTimeBetweenAndDeviceIdAndJsonKey(time, time2, deviceId, + return dataTimeRepository.findAllByTimeBetweenAndDeviceIdAndJsonKey(time, time2, deviceId, jsonKey); } else return dataRepository.findAllByTimeBetweenAndDeviceIdAndJsonKey(time, time2, deviceId, @@ -92,7 +95,7 @@ public List findAllBySort(int deviceId, String jsonKey) { var memory = redisUtil.get(deviceId + jsonKey); if (memory == null) { if (database.equals("influxdb")) { - res = timeDataRepository.findAllBySort(deviceId, jsonKey); + res = dataTimeRepository.findAllBySort(deviceId, jsonKey); } else { res = dataRepository.findAllBySort(deviceId, jsonKey); } @@ -107,7 +110,7 @@ public List findAllBySort(int deviceId, String jsonKey) { @Override public void deleteAllByTimeBeforeAndDeviceIdAndJsonKey(long time, int deviceId, String jsonKey) { if (database.equals("influxdb")) { - timeDataRepository.deleteAllByTimeBeforeAndDeviceIdAndJsonKey(time, deviceId, jsonKey); + dataTimeRepository.deleteAllByTimeBeforeAndDeviceIdAndJsonKey(time, deviceId, jsonKey); } else dataRepository.deleteAllByTimeBeforeAndDeviceIdAndJsonKey(time, deviceId, jsonKey); } @@ -118,7 +121,7 @@ public JsonResult metaData(int deviceId, String jsonKey) { var memory = redisUtil.get(deviceId + jsonKey); if (memory == null) { if (database.equals("influxdb")) { - res = timeDataRepository.findAllBySort(deviceId, jsonKey); + res = dataTimeRepository.findAllBySort(deviceId, jsonKey); } else { res = dataRepository.findAllBySort(deviceId, jsonKey); } diff --git a/src/main/java/top/rslly/iot/services/EventDataService.java b/src/main/java/top/rslly/iot/services/EventDataService.java new file mode 100644 index 0000000..cf72d73 --- /dev/null +++ b/src/main/java/top/rslly/iot/services/EventDataService.java @@ -0,0 +1,38 @@ +/** + * Copyright © 2023-2030 The ruanrongman Authors + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package top.rslly.iot.services; + +import jdk.jfr.Event; +import top.rslly.iot.models.EventDataEntity; +import top.rslly.iot.param.request.EventData; +import top.rslly.iot.param.request.ProductData; +import top.rslly.iot.utility.result.JsonResult; + +import java.util.List; + +public interface EventDataService { + List findAllByModelId(int modelId); + + JsonResult getEventData(); + + JsonResult postEventData(EventData eventData); + + JsonResult deleteEventData(int id); +} diff --git a/src/main/java/top/rslly/iot/services/EventDataServiceImpl.java b/src/main/java/top/rslly/iot/services/EventDataServiceImpl.java new file mode 100644 index 0000000..ad10224 --- /dev/null +++ b/src/main/java/top/rslly/iot/services/EventDataServiceImpl.java @@ -0,0 +1,92 @@ +/** + * Copyright © 2023-2030 The ruanrongman Authors + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package top.rslly.iot.services; + +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import top.rslly.iot.dao.EventDataRepository; +import top.rslly.iot.dao.ProductModelRepository; +import top.rslly.iot.models.EventDataEntity; +import top.rslly.iot.models.ProductModelEntity; +import top.rslly.iot.param.request.EventData; +import top.rslly.iot.utility.result.JsonResult; +import top.rslly.iot.utility.result.ResultCode; +import top.rslly.iot.utility.result.ResultTool; + +import javax.annotation.Resource; +import java.util.HashMap; +import java.util.List; + +@Service +public class EventDataServiceImpl implements EventDataService { + @Resource + private ProductModelRepository productModelRepository; + @Resource + private EventDataRepository eventDataRepository; + + @Override + public List findAllByModelId(int modelId) { + return eventDataRepository.findAllByModelId(modelId); + } + + @Override + public JsonResult getEventData() { + var result = eventDataRepository.findAll(); + if (result.isEmpty()) { + return ResultTool.fail(ResultCode.COMMON_FAIL); + } else + return ResultTool.success(result); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public JsonResult postEventData(EventData eventData) { + EventDataEntity eventDataEntity = new EventDataEntity(); + BeanUtils.copyProperties(eventData, eventDataEntity); + List result = + productModelRepository.findAllById(eventDataEntity.getModelId()); + List p1 = eventDataRepository + .findAllByModelIdAndJsonKey(eventDataEntity.getModelId(), eventDataEntity.getJsonKey()); + // List p2 = productDataRepository.findAllByType(productData.getType()); + HashMap typeMap = new HashMap<>(); + typeMap.put("string", 1); + typeMap.put("int", 2); + typeMap.put("float", 3); + var p2 = typeMap.get(eventDataEntity.getType()); + if (result.isEmpty() || !p1.isEmpty() || p2 == null) + return ResultTool.fail(ResultCode.COMMON_FAIL); + else { + EventDataEntity eventDataEntity1 = eventDataRepository.save(eventDataEntity); + return ResultTool.success(eventDataEntity1); + } + } + + @Override + public JsonResult deleteEventData(int id) { + List result = eventDataRepository.deleteById(id); + if (result.isEmpty()) + return ResultTool.fail(ResultCode.PARAM_NOT_VALID); + else { + return ResultTool.success(result); + } + + } +} diff --git a/src/main/java/top/rslly/iot/services/EventStorageService.java b/src/main/java/top/rslly/iot/services/EventStorageService.java new file mode 100644 index 0000000..916047d --- /dev/null +++ b/src/main/java/top/rslly/iot/services/EventStorageService.java @@ -0,0 +1,40 @@ +/** + * Copyright © 2023-2030 The ruanrongman Authors + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package top.rslly.iot.services; + +import top.rslly.iot.models.DataEntity; +import top.rslly.iot.models.EventStorageEntity; +import top.rslly.iot.models.influxdb.EventStorageTimeEntity; +import top.rslly.iot.utility.result.JsonResult; + +import java.util.List; + +public interface EventStorageService { + void insert(EventStorageEntity eventStorageTimeEntity); + + JsonResult findAllByTimeBetweenAndDeviceNameAndJsonKey(long time, long time2, String name, + String jsonKey); + + List findAllByTimeBetweenAndDeviceIdAndJsonKey(long time, long time2, + int deviceId, String jsonKey); + + + void deleteAllByTimeBeforeAndDeviceIdAndJsonKey(long time, int deviceId, String jsonKey); +} diff --git a/src/main/java/top/rslly/iot/services/EventStorageServiceImpl.java b/src/main/java/top/rslly/iot/services/EventStorageServiceImpl.java new file mode 100644 index 0000000..f1289a7 --- /dev/null +++ b/src/main/java/top/rslly/iot/services/EventStorageServiceImpl.java @@ -0,0 +1,98 @@ +/** + * Copyright © 2023-2030 The ruanrongman Authors + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package top.rslly.iot.services; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import top.rslly.iot.dao.EventStorageRepository; +import top.rslly.iot.dao.EventStorageTimeRepository; +import top.rslly.iot.dao.ProductDeviceRepository; +import top.rslly.iot.models.EventStorageEntity; +import top.rslly.iot.utility.result.JsonResult; +import top.rslly.iot.utility.result.ResultCode; +import top.rslly.iot.utility.result.ResultTool; + +import javax.annotation.Resource; +import java.util.List; + +@Service +public class EventStorageServiceImpl implements EventStorageService { + @Value("${storage.database}") + private String database; + + @Resource + private EventStorageRepository eventStorageRepository; + @Resource + private ProductDeviceRepository deviceRepository; + @Resource + private EventStorageTimeRepository eventStorageTimeRepository; + + @Override + public void insert(EventStorageEntity eventStorageEntity) { + if (database.equals("influxdb")) { + eventStorageTimeRepository.insert(eventStorageEntity); + } else + eventStorageRepository.save(eventStorageEntity); + } + + @Override + public JsonResult findAllByTimeBetweenAndDeviceNameAndJsonKey(long time, long time2, + String name, String jsonKey) { + var productDeviceEntities = deviceRepository.findAllByName(name); + if (productDeviceEntities.isEmpty()) { + return ResultTool.fail(ResultCode.PARAM_NOT_VALID); + } + int deviceId = productDeviceEntities.get(0).getId(); + List res; + if (database.equals("influxdb")) { + res = eventStorageTimeRepository.findAllByTimeBetweenAndDeviceIdAndJsonKey(time, time2, + deviceId, jsonKey); + } else { + res = eventStorageRepository.findAllByTimeBetweenAndDeviceIdAndJsonKey(time, time2, deviceId, + jsonKey); + } + if (res.isEmpty()) + return ResultTool.fail(ResultCode.PARAM_NOT_VALID); + return ResultTool.success(res); + } + + @Override + public List findAllByTimeBetweenAndDeviceIdAndJsonKey(long time, long time2, + int deviceId, String jsonKey) { + if (database.equals("influxdb")) { + return eventStorageTimeRepository.findAllByTimeBetweenAndDeviceIdAndJsonKey(time, time2, + deviceId, + jsonKey); + } else + return eventStorageRepository.findAllByTimeBetweenAndDeviceIdAndJsonKey(time, time2, deviceId, + jsonKey); + } + + + + @Override + public void deleteAllByTimeBeforeAndDeviceIdAndJsonKey(long time, int deviceId, String jsonKey) { + if (database.equals("influxdb")) { + eventStorageTimeRepository.deleteAllByTimeBeforeAndDeviceIdAndJsonKey(time, deviceId, + jsonKey); + } else + eventStorageRepository.deleteAllByTimeBeforeAndDeviceIdAndJsonKey(time, deviceId, jsonKey); + } +} diff --git a/src/main/java/top/rslly/iot/services/HardWareServiceImpl.java b/src/main/java/top/rslly/iot/services/HardWareServiceImpl.java index 9e32816..1dda587 100644 --- a/src/main/java/top/rslly/iot/services/HardWareServiceImpl.java +++ b/src/main/java/top/rslly/iot/services/HardWareServiceImpl.java @@ -21,6 +21,7 @@ import org.eclipse.paho.client.mqttv3.MqttException; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import top.rslly.iot.dao.ProductDataRepository; import top.rslly.iot.dao.ProductDeviceRepository; import top.rslly.iot.param.request.ControlParam; @@ -45,6 +46,7 @@ public class HardWareServiceImpl implements HardWareService { private ProductDeviceRepository productDeviceRepository; @Override + @Transactional(rollbackFor = Exception.class) public JsonResult control(ControlParam controlParam) throws MqttException { var deviceEntityList = productDeviceRepository.findAllByName(controlParam.getName()); if (deviceEntityList.isEmpty()) { @@ -52,6 +54,10 @@ public JsonResult control(ControlParam controlParam) throws MqttException { } if (controlParam.getQos() < 0 || controlParam.getQos() > 2) return ResultTool.fail(ResultCode.PARAM_NOT_VALID); + int allow = deviceEntityList.get(0).getAllow(); + if (allow == 0) { + return ResultTool.fail(ResultCode.DEVICE_ABANDON); + } int modelId = deviceEntityList.get(0).getModelId(); var productDataEntities = productDataRepository.findAllByModelId(modelId); if (productDataEntities.isEmpty()) { diff --git a/src/main/java/top/rslly/iot/services/ProductDataServiceImpl.java b/src/main/java/top/rslly/iot/services/ProductDataServiceImpl.java index 1f05384..571a1db 100644 --- a/src/main/java/top/rslly/iot/services/ProductDataServiceImpl.java +++ b/src/main/java/top/rslly/iot/services/ProductDataServiceImpl.java @@ -22,6 +22,7 @@ import org.springframework.beans.BeanUtils; import org.springframework.security.core.parameters.P; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import top.rslly.iot.dao.ProductDataRepository; import top.rslly.iot.dao.ProductModelRepository; import top.rslly.iot.models.ProductDataEntity; @@ -84,6 +85,7 @@ public JsonResult getProductData() { } @Override + @Transactional(rollbackFor = Exception.class) public JsonResult postProductData(ProductData productData) { ProductDataEntity productDataEntity = new ProductDataEntity(); BeanUtils.copyProperties(productData, productDataEntity); diff --git a/src/main/java/top/rslly/iot/services/ProductDeviceServiceImpl.java b/src/main/java/top/rslly/iot/services/ProductDeviceServiceImpl.java index 4305a61..2b78eba 100644 --- a/src/main/java/top/rslly/iot/services/ProductDeviceServiceImpl.java +++ b/src/main/java/top/rslly/iot/services/ProductDeviceServiceImpl.java @@ -21,13 +21,12 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; -import org.springframework.security.core.parameters.P; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import top.rslly.iot.dao.ProductDataRepository; import top.rslly.iot.dao.ProductDeviceRepository; import top.rslly.iot.dao.ProductModelRepository; import top.rslly.iot.models.ProductDeviceEntity; -import top.rslly.iot.models.ProductEntity; import top.rslly.iot.models.ProductModelEntity; import top.rslly.iot.param.prompt.ProductDeviceDescription; import top.rslly.iot.param.request.ProductDevice; @@ -84,6 +83,7 @@ public List deleteById(int id) { } @Override + @Transactional(rollbackFor = Exception.class) public List getDescription(int modelId) { var devices = productDeviceRepository.findAllByModelId(modelId); var productDataEntities = productDataRepository.findAllByModelId(modelId); @@ -99,6 +99,10 @@ public List getDescription(int modelId) { ProductDeviceDescription productDeviceDescription = new ProductDeviceDescription(); productDeviceDescription.setDevice_name(s.getName()); productDeviceDescription.setOnline(s.getOnline()); + if (s.getAllow() == 0) + productDeviceDescription.setAllow("false"); + else + productDeviceDescription.setAllow("true"); for (var jsonKey : properties) { var dataServiceAllBySort = dataService.findAllBySort(s.getId(), jsonKey); if (dataServiceAllBySort == null) @@ -135,6 +139,7 @@ public JsonResult getProductDevice() { } @Override + @Transactional(rollbackFor = Exception.class) public JsonResult postProductDevice(ProductDevice productDevice) { ProductDeviceEntity productDeviceEntity = new ProductDeviceEntity(); productDeviceEntity.setOnline("disconnected"); diff --git a/src/main/java/top/rslly/iot/services/ProductEventService.java b/src/main/java/top/rslly/iot/services/ProductEventService.java new file mode 100644 index 0000000..9e9090d --- /dev/null +++ b/src/main/java/top/rslly/iot/services/ProductEventService.java @@ -0,0 +1,38 @@ +/** + * Copyright © 2023-2030 The ruanrongman Authors + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package top.rslly.iot.services; + +import top.rslly.iot.models.ProductEventEntity; +import top.rslly.iot.param.request.EventData; +import top.rslly.iot.param.request.ProductEvent; +import top.rslly.iot.utility.result.JsonResult; + +import java.util.List; + +public interface ProductEventService { + + List findAllByModelIdAndName(int modelId, String name); + + JsonResult getProductEvent(); + + JsonResult postProductEvent(ProductEvent productEvent); + + JsonResult deleteProductEvent(int id); +} diff --git a/src/main/java/top/rslly/iot/services/ProductEventServiceImpl.java b/src/main/java/top/rslly/iot/services/ProductEventServiceImpl.java new file mode 100644 index 0000000..9894630 --- /dev/null +++ b/src/main/java/top/rslly/iot/services/ProductEventServiceImpl.java @@ -0,0 +1,83 @@ +/** + * Copyright © 2023-2030 The ruanrongman Authors + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package top.rslly.iot.services; + +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; +import top.rslly.iot.dao.ProductEventRepository; +import top.rslly.iot.dao.ProductModelRepository; +import top.rslly.iot.models.ProductEventEntity; +import top.rslly.iot.models.ProductModelEntity; +import top.rslly.iot.param.request.ProductEvent; +import top.rslly.iot.utility.result.JsonResult; +import top.rslly.iot.utility.result.ResultCode; +import top.rslly.iot.utility.result.ResultTool; + +import javax.annotation.Resource; +import java.util.List; + +@Service +public class ProductEventServiceImpl implements ProductEventService { + @Resource + private ProductModelRepository productModelRepository; + @Resource + private ProductEventRepository productEventRepository; + + @Override + public List findAllByModelIdAndName(int modelId, String name) { + return productEventRepository.findAllByModelIdAndName(modelId, name); + } + + @Override + public JsonResult getProductEvent() { + var result = productEventRepository.findAll(); + if (result.isEmpty()) { + return ResultTool.fail(ResultCode.COMMON_FAIL); + } else + return ResultTool.success(result); + } + + @Override + public JsonResult postProductEvent(ProductEvent productEvent) { + ProductEventEntity productEventEntity = new ProductEventEntity(); + BeanUtils.copyProperties(productEvent, productEventEntity); + List result = productModelRepository.findAllById(productEvent.getModelId()); + List p1 = productEventRepository + .findAllByModelIdAndName(productEvent.getModelId(), productEvent.getName()); + if (result.isEmpty() || !p1.isEmpty()) + return ResultTool.fail(ResultCode.COMMON_FAIL); + else { + ProductEventEntity productEventEntity1 = productEventRepository.save(productEventEntity); + return ResultTool.success(productEventEntity1); + } + } + + @Override + public JsonResult deleteProductEvent(int id) { + + List result = productEventRepository.deleteById(id); + if (result.isEmpty()) + return ResultTool.fail(ResultCode.PARAM_NOT_VALID); + else { + return ResultTool.success(result); + } + + } +} diff --git a/src/main/java/top/rslly/iot/services/ProductModelServiceImpl.java b/src/main/java/top/rslly/iot/services/ProductModelServiceImpl.java index 1f95cbf..3877624 100644 --- a/src/main/java/top/rslly/iot/services/ProductModelServiceImpl.java +++ b/src/main/java/top/rslly/iot/services/ProductModelServiceImpl.java @@ -21,6 +21,7 @@ import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import top.rslly.iot.dao.ProductDeviceRepository; import top.rslly.iot.dao.ProductModelRepository; import top.rslly.iot.dao.ProductRepository; @@ -66,6 +67,7 @@ public JsonResult getProductModel(int productId) { } @Override + @Transactional(rollbackFor = Exception.class) public JsonResult postProductModel(ProductModel productModel) { ProductModelEntity productModelEntity = new ProductModelEntity(); BeanUtils.copyProperties(productModel, productModelEntity); @@ -107,6 +109,7 @@ public List findAllByProductIdAndName(int productId, String @Override + @Transactional(rollbackFor = Exception.class) public JsonResult deleteProductModel(int id) { List productDeviceEntityList = productDeviceRepository.findAllByModelId(id); diff --git a/src/main/java/top/rslly/iot/services/ProductServiceImpl.java b/src/main/java/top/rslly/iot/services/ProductServiceImpl.java index 1abd30f..1e51a40 100644 --- a/src/main/java/top/rslly/iot/services/ProductServiceImpl.java +++ b/src/main/java/top/rslly/iot/services/ProductServiceImpl.java @@ -21,6 +21,7 @@ import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import top.rslly.iot.dao.MqttUserRepository; import top.rslly.iot.dao.ProductModelRepository; import top.rslly.iot.dao.ProductRepository; @@ -59,6 +60,7 @@ public JsonResult getProduct() { } @Override + @Transactional(rollbackFor = Exception.class) public JsonResult postProduct(Product product) { ProductEntity productEntity = new ProductEntity(); BeanUtils.copyProperties(product, productEntity); @@ -73,6 +75,7 @@ public JsonResult postProduct(Product product) { } @Override + @Transactional(rollbackFor = Exception.class) public JsonResult deleteProduct(int id) { List productModelEntityList = productModelRepository.findAllByProductId(id); List wxProductBindEntityList = diff --git a/src/main/java/top/rslly/iot/services/WxProductBindServiceImpl.java b/src/main/java/top/rslly/iot/services/WxProductBindServiceImpl.java index 3ff20ba..88ec630 100644 --- a/src/main/java/top/rslly/iot/services/WxProductBindServiceImpl.java +++ b/src/main/java/top/rslly/iot/services/WxProductBindServiceImpl.java @@ -20,6 +20,7 @@ package top.rslly.iot.services; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import top.rslly.iot.dao.ProductRepository; import top.rslly.iot.dao.WxProductActiveRepository; import top.rslly.iot.dao.WxProductBindRepository; @@ -44,6 +45,7 @@ public class WxProductBindServiceImpl implements WxProductBindService { private ProductRepository productRepository; @Override + @Transactional(rollbackFor = Exception.class) public JsonResult wxBindProduct(WxProductBindEntity wxProductBindEntity) { if (wxUserRepository.findAllByOpenid(wxProductBindEntity.getOpenid()).isEmpty() || productRepository.findAllById(wxProductBindEntity.getProductId()).isEmpty()) @@ -54,6 +56,7 @@ public JsonResult wxBindProduct(WxProductBindEntity wxProductBindEntity) { } @Override + @Transactional(rollbackFor = Exception.class) public boolean wxBindProduct(String openid, String productName, String productKey) { if (productName == null || productKey == null) return false; @@ -69,6 +72,7 @@ public boolean wxBindProduct(String openid, String productName, String productKe } @Override + @Transactional(rollbackFor = Exception.class) public boolean wxUnBindProduct(String openid, String productName, String productKey) { if (productName == null || productKey == null) return false; diff --git a/src/main/java/top/rslly/iot/transfer/DealThingsEvent.java b/src/main/java/top/rslly/iot/transfer/DealThingsEvent.java new file mode 100644 index 0000000..19b32af --- /dev/null +++ b/src/main/java/top/rslly/iot/transfer/DealThingsEvent.java @@ -0,0 +1,110 @@ +/** + * Copyright © 2023-2030 The ruanrongman Authors + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package top.rslly.iot.transfer; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONException; +import com.alibaba.fastjson.JSONObject; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import top.rslly.iot.models.EventStorageEntity; +import top.rslly.iot.services.EventDataServiceImpl; +import top.rslly.iot.services.EventStorageServiceImpl; +import top.rslly.iot.services.ProductDeviceServiceImpl; +import top.rslly.iot.services.ProductEventServiceImpl; +import top.rslly.iot.transfer.mqtt.MqttConnectionUtils; + +import java.util.UUID; + +@Component +@Slf4j +public class DealThingsEvent { + @Autowired + private ProductDeviceServiceImpl productDeviceService; + @Autowired + private ProductEventServiceImpl productEventService; + @Autowired + private EventDataServiceImpl eventDataService; + @Autowired + private EventStorageServiceImpl eventStorageService; + + public boolean deal(String clientId, String topic, String message) { + // this clientId is delivered id ,not the sender + log.info("clientId:{},topic:{},message:{}", clientId, topic, message); + String characteristic = UUID.randomUUID().toString(); + // var deviceEntityList = productDeviceService.findAllBySubscribeTopic(topic); + // if (deviceEntityList.isEmpty()||!clientId.equals(MqttConnectionUtils.clientId)) + // return; + var deviceEntityList = productDeviceService.findAllByClientId(clientId); + if (deviceEntityList.isEmpty()) + return false; + String event_topic = "/oc/devices/" + deviceEntityList.get(0).getName() + + "/sys/" + "properties/event"; + if (!event_topic.equals(topic)) + return false; + int allow = deviceEntityList.get(0).getAllow(); + if (allow == 0) { + return false; + } + int modelId = deviceEntityList.get(0).getModelId(); + var eventDataEntities = eventDataService.findAllByModelId(modelId); + JSONObject mes; + int event_id; + try { + mes = JSON.parseObject(message); + var eventEntities = + productEventService.findAllByModelIdAndName(modelId, mes.get("event").toString()); + event_id = eventEntities.get(0).getId();// event_id is the id of event + } catch (Exception e) { + log.error("json error{}", e.getMessage()); + return false; + } + String reply_topic = "/oc/devices/" + deviceEntityList.get(0).getName() + + "/sys/" + "properties/event_reply"; + for (var s : eventDataEntities) { + var result = mes.get(s.getJsonKey()); + if (result == null) + continue;// Automatically ignore values + EventStorageEntity eventStorageEntity = new EventStorageEntity(); + eventStorageEntity.setCharacteristic(characteristic); + eventStorageEntity.setEventId(event_id); + eventStorageEntity.setJsonKey(s.getJsonKey()); + eventStorageEntity.setValue(result.toString()); + eventStorageEntity.setDeviceId(deviceEntityList.get(0).getId()); + try { + long time = System.currentTimeMillis(); + eventStorageEntity.setTime(time); + log.info("{}", eventStorageEntity); + eventStorageService.insert(eventStorageEntity); + } catch (Exception e) { + log.error("DealThingsEvent save error:{}", e.getMessage()); + } + } + try { + MqttConnectionUtils.publish(reply_topic, "{\"code\":\"" + 200 + "\",\"status\":\"ok\"}", 0); + } catch (MqttException e) { + log.error("DealThingsModel error:{}", e.getMessage()); + } + return true; + } + +} diff --git a/src/main/java/top/rslly/iot/transfer/DealThingsModel.java b/src/main/java/top/rslly/iot/transfer/DealThingsModel.java index ff45239..efda4f0 100644 --- a/src/main/java/top/rslly/iot/transfer/DealThingsModel.java +++ b/src/main/java/top/rslly/iot/transfer/DealThingsModel.java @@ -27,6 +27,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; import top.rslly.iot.models.DataEntity; import top.rslly.iot.services.DataServiceImpl; import top.rslly.iot.services.ProductDataServiceImpl; @@ -54,8 +55,6 @@ public class DealThingsModel { private RedisUtil redisUtil; // private final Lock lock = new ReentrantLock(); - - public boolean deal(String clientId, String topic, String message) { // this clientId is delivered id ,not the sender log.info("clientId:{},topic:{},message:{}", clientId, topic, message); @@ -66,6 +65,10 @@ public boolean deal(String clientId, String topic, String message) { var deviceEntityList = productDeviceService.findAllByClientId(clientId); if (deviceEntityList.isEmpty() || !deviceEntityList.get(0).getSubscribeTopic().equals(topic)) return false; + int allow = deviceEntityList.get(0).getAllow(); + if (allow == 0) { + return false; + } int modelId = deviceEntityList.get(0).getModelId(); var productDataEntities = productDataService.findAllByModelId(modelId); JSONObject mes; diff --git a/src/main/java/top/rslly/iot/transfer/HookProviderImpl.java b/src/main/java/top/rslly/iot/transfer/HookProviderImpl.java index 319a91a..3cbe8c6 100644 --- a/src/main/java/top/rslly/iot/transfer/HookProviderImpl.java +++ b/src/main/java/top/rslly/iot/transfer/HookProviderImpl.java @@ -46,6 +46,8 @@ public class HookProviderImpl extends HookProviderGrpc.HookProviderImplBase { @Autowired private DealThingsModel dealThingsModel; @Autowired + private DealThingsEvent dealThingsEvent; + @Autowired private EmqTransfer emqTransfer; @PostConstruct @@ -121,11 +123,14 @@ public void onClientDisconnected(ClientDisconnectedRequest request, @Override public void onMessagePublish(MessagePublishRequest request, StreamObserver responseObserver) { - boolean isDeal = + boolean isDealModel = dealThingsModel.deal(request.getMessage().getFrom(), request.getMessage().getTopic(), request.getMessage().getPayload().toStringUtf8()); + boolean isDealEvent = + dealThingsEvent.deal(request.getMessage().getFrom(), request.getMessage().getTopic(), + request.getMessage().getPayload().toStringUtf8()); Message nmsg; - if (isDeal) { + if (isDealModel || isDealEvent) { ByteString bstr = ByteString.copyFromUtf8("You don't have permissions"); nmsg = Message.newBuilder() diff --git a/src/main/java/top/rslly/iot/utility/DataCleanAuto.java b/src/main/java/top/rslly/iot/utility/DataCleanAuto.java index f042784..9a41173 100644 --- a/src/main/java/top/rslly/iot/utility/DataCleanAuto.java +++ b/src/main/java/top/rslly/iot/utility/DataCleanAuto.java @@ -50,8 +50,8 @@ public void task() { // 当前时间日历 Calendar calendar = Calendar.getInstance(); calendar.add(Calendar.DATE, -7); - // long lastTime = calendar.getTime().getTime(); - long lastTime = System.currentTimeMillis(); + long lastTime = calendar.getTime().getTime(); + // long lastTime = System.currentTimeMillis(); // System.out.println(lastTime); List productDataEntityList = productDataService.findAllByStorageType(DataSave.week.getStorageType()); diff --git a/src/main/java/top/rslly/iot/utility/ai/Prompt.java b/src/main/java/top/rslly/iot/utility/ai/Prompt.java index 3e0890a..fe54961 100644 --- a/src/main/java/top/rslly/iot/utility/ai/Prompt.java +++ b/src/main/java/top/rslly/iot/utility/ai/Prompt.java @@ -31,6 +31,7 @@ import java.util.Map; @Component +@Deprecated public class Prompt { @Autowired private DescriptionUtil descriptionUtil; @@ -45,7 +46,7 @@ public class Prompt { The electrical properties and value that can be controlled by each electrical:{properties_value} The device of current electrical type and the latest status:{equipment_status} value ["null"] means devices information is not exist - If you find the user wants to control is not in the above range or the device is disconnected, + If you find the user wants to control is not in the above range ,the device is disconnected or allow is false, you can not control it,and you should warn the user in the answer param ## Output Format To answer the question, Use the following JSON format. JSON only, no explanation. Otherwise, you will be punished. @@ -71,7 +72,7 @@ public class Prompt { ## Attention - Your output is JSON only and no explanation. - Please respond to the user's request immediately, rather than using terms like "later" or "then" - - If device cannot be controlled, such as disconnected, please inform the user + - If device cannot be controlled, such as not allow or disconnected, please inform the user """; private static final String musicPrompt = """ @@ -98,7 +99,6 @@ public class Prompt { """ As a smart speaker, your name is {agent_name}, developed by the {team_name} team. You are good at helping people doing the following task {task_map} - reference information: The current time is {time} You now need to classify based on user input ## Output Format To answer the question, Use the following JSON format. JSON only, no explanation. Otherwise, you will be punished. @@ -155,6 +155,7 @@ public class Prompt { } } ``` + reference information: The current time is {time} ## Attention - Your output is JSON only and no explanation. ## Current Conversation @@ -460,14 +461,14 @@ public String getClassifierTool() { classifierMap.put("1", "Query weather"); classifierMap.put("2", "Get the current time"); classifierMap.put("3", - "Control electrical and query electrical status(Excluding playing music.)"); + "Operate electrical and query electrical status(Excluding playing music.)"); classifierMap.put("4", "Request a song or play music.(including Recommend music.)"); classifierMap.put("5", "Composite tasks(like according to weather control electrical)"); classifierMap.put("6", "Other tasks (including chatting,coding,write paper,Get news or unknown events,translate and etc.)"); - classifierMap.put("7", "bind or Unbinding the product"); - classifierMap.put("8", "Set up controlled products"); + classifierMap.put("7", "Bind or Unbinding the product"); + classifierMap.put("8", "handoff controlled products"); classifierMap.put("9", "Scheduled tasks or reminder tasks"); String classifierJson = JSON.toJSONString(classifierMap); Map params = new HashMap<>(); diff --git a/src/main/java/top/rslly/iot/utility/ai/chain/Router.java b/src/main/java/top/rslly/iot/utility/ai/chain/Router.java index d9bc07f..86a9fc9 100644 --- a/src/main/java/top/rslly/iot/utility/ai/chain/Router.java +++ b/src/main/java/top/rslly/iot/utility/ai/chain/Router.java @@ -59,15 +59,11 @@ public class Router { @Autowired private WxUserServiceImpl wxUserService; @Autowired - private WxProductBindServiceImpl wxProductBindService; - @Autowired private WxProductActiveTool wxProductActiveTool; @Autowired - private WxProductActiveServiceImpl wxProductActiveService; - @Autowired - private ProductServiceImpl productService; - @Autowired private ScheduleTool scheduleTool; + @Autowired + private SearchTool searchTool; // chatId in wechat module need to use openid public String response(String content, String chatId, int productId, String... microappid) { @@ -134,6 +130,10 @@ public String response(String content, String chatId, int productId, String... m } answer = "以下是定时任务插件的结果:" + toolResult; } + case "10" -> { + toolResult = searchTool.run(args); + answer = "以下是百度搜索插件的结果:" + toolResult; + } default -> answer = resultMap.get("answer").toString(); } } else diff --git a/src/main/java/top/rslly/iot/utility/ai/llm/DeepSeek.java b/src/main/java/top/rslly/iot/utility/ai/llm/DeepSeek.java index 9e780da..cfd3282 100644 --- a/src/main/java/top/rslly/iot/utility/ai/llm/DeepSeek.java +++ b/src/main/java/top/rslly/iot/utility/ai/llm/DeepSeek.java @@ -31,12 +31,14 @@ import com.unfbx.chatgpt.entity.chat.Message; import com.zhipu.oapi.service.v4.model.ChatMessageRole; import lombok.extern.slf4j.Slf4j; +import okhttp3.OkHttpClient; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import top.rslly.iot.utility.ai.ModelMessage; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.TimeUnit; @Slf4j @Component @@ -49,9 +51,15 @@ public class DeepSeek implements LLM { @Value("${ai.deepSeek-key}") public void setApiKey(String apiKey) { + OkHttpClient okHttpClient = new OkHttpClient.Builder() + .connectTimeout(3000, TimeUnit.SECONDS)// 自定义超时时间 + .writeTimeout(3000, TimeUnit.SECONDS)// 自定义超时时间 + .readTimeout(3000, TimeUnit.SECONDS)// 自定义超时时间 + .build(); // 填写自己的api key openAiClient = OpenAiClient.builder() .apiKey(List.of(apiKey)) + .okHttpClient(okHttpClient) .apiHost(URL) .build(); } diff --git a/src/main/java/top/rslly/iot/utility/ai/prompts/ChatToolPrompt.java b/src/main/java/top/rslly/iot/utility/ai/prompts/ChatToolPrompt.java new file mode 100644 index 0000000..c6ab476 --- /dev/null +++ b/src/main/java/top/rslly/iot/utility/ai/prompts/ChatToolPrompt.java @@ -0,0 +1,51 @@ +/** + * Copyright © 2023-2030 The ruanrongman Authors + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package top.rslly.iot.utility.ai.prompts; + + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import top.rslly.iot.utility.ai.promptTemplate.StringUtils; + +import java.util.HashMap; +import java.util.Map; + +@Component +public class ChatToolPrompt { + @Value("${ai.robot-name}") + private String robotName; + @Value("${ai.team-name}") + private String teamName; + private static final String chatPrompt = """ + You are a smart speaker, your name is {agent_name}, developed by the {team_name} team. + 你可以回答新闻内容和用户的各种合法请求,你回答的每句话都尽量口语化、简短,总是喜欢使用表情符号 + ## Output Format + Please do not output \\n and try to limit the word count to 100 words or less + ## Current Conversation + Below is the current conversation consisting of interleaving human and assistant history. + """; + + public String getChatTool() { + Map params = new HashMap<>(); + params.put("agent_name", robotName); + params.put("team_name", teamName); + return StringUtils.formatString(chatPrompt, params); + } +} diff --git a/src/main/java/top/rslly/iot/utility/ai/prompts/ClassifierToolPrompt.java b/src/main/java/top/rslly/iot/utility/ai/prompts/ClassifierToolPrompt.java new file mode 100644 index 0000000..8e80781 --- /dev/null +++ b/src/main/java/top/rslly/iot/utility/ai/prompts/ClassifierToolPrompt.java @@ -0,0 +1,134 @@ +/** + * Copyright © 2023-2030 The ruanrongman Authors + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package top.rslly.iot.utility.ai.prompts; + +import com.alibaba.fastjson.JSON; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import top.rslly.iot.utility.ai.DescriptionUtil; +import top.rslly.iot.utility.ai.promptTemplate.StringUtils; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +@Component +public class ClassifierToolPrompt { + @Value("${ai.robot-name}") + private String robotName; + @Value("${ai.team-name}") + private String teamName; + private static final String classifierPrompt = + """ + As a smart speaker, your name is {agent_name}, developed by the {team_name} team. You are good at helping people doing the following task + {task_map} + You now need to classify based on user input + ## Output Format + To answer the question, Use the following JSON format. JSON only, no explanation. Otherwise, you will be punished. + The output should be formatted as a JSON instance that conforms to the format below. JSON only, no explanation. + ```json + { + "thought": "The thought of what to do and why.(use Chinese)", + "action": # the action to take, must be one of provided tools + { + "code": "if success output 200,If it doesn't match any task,output 400", + "answer": "Task 1,3,4,5,6 just need to answer "yes",no explanation.other Answer vivid,lively,kind and amiable", + "value": "one of task No., json list data like [1],If it doesn't match, please output []", + "args": "task input parameters(Combined with Current Conversation,Summarize the context,not null)" + } + } + ``` + ## few shot + if user input: What's the weather like today ? + ```json + { + "thought": "用户想要查询天气", + "action": + { + "code": "200", + "answer": "yes", + "value": "[1]", + "args": "What's the weather like today" + } + } + ``` + if user input: what time is it ? + ```json + { + "thought": "用户希望查询时间", + "action": + { + "code": "200", + "answer": "现在时间是 XXX", + "value": "[2]", + "args": "ok" + } + } + ``` + if user input: Bundle the lamp product,key is XXX + ```json + { + "thought": "用户想要绑定产品", + "action": + { + "code": "200", + "answer": "yes", + "value": "[7]", + "args": "Bundle the lamp product,key is XXX" + } + } + ``` + reference information: The current time is {time} + ## Attention + - Your output is JSON only and no explanation. + ## Current Conversation + Below is the current conversation consisting of interleaving human and assistant history. + """; + + public String getClassifierTool() { + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + Date date = new Date(); + String formattedDate = formatter.format(date); + Map classifierMap = new HashMap<>(); + classifierMap.put("1", "Query weather"); + classifierMap.put("2", "Get the current time"); + classifierMap.put("3", + "Operate electrical and query electrical status(Excluding playing music.)"); + classifierMap.put("4", "Request a song or play music.(including Recommend music.)"); + classifierMap.put("5", + "Composite tasks(like according to weather control electrical)"); + classifierMap.put("6", + "Other tasks (including chatting,coding,write paper,Get news or unknown events,translate and etc.)"); + classifierMap.put("7", "Bind or Unbinding the product"); + classifierMap.put("8", "handoff controlled products"); + classifierMap.put("9", "Scheduled tasks or reminder tasks"); + classifierMap.put("10", + "Search for online(Please simplify user input to meet the needs of search engines)"); + String classifierJson = JSON.toJSONString(classifierMap); + Map params = new HashMap<>(); + params.put("agent_name", robotName); + params.put("team_name", teamName); + params.put("task_map", classifierJson); + params.put("time", formattedDate); + return StringUtils.formatString(classifierPrompt, params); + } +} diff --git a/src/main/java/top/rslly/iot/utility/ai/prompts/ControlToolPrompt.java b/src/main/java/top/rslly/iot/utility/ai/prompts/ControlToolPrompt.java new file mode 100644 index 0000000..8f8e959 --- /dev/null +++ b/src/main/java/top/rslly/iot/utility/ai/prompts/ControlToolPrompt.java @@ -0,0 +1,84 @@ +/** + * Copyright © 2023-2030 The ruanrongman Authors + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package top.rslly.iot.utility.ai.prompts; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import top.rslly.iot.utility.ai.DescriptionUtil; +import top.rslly.iot.utility.ai.promptTemplate.StringUtils; + +import java.util.HashMap; +import java.util.Map; + +@Component +public class ControlToolPrompt { + @Autowired + private DescriptionUtil descriptionUtil; + @Value("${ai.robot-name}") + private String robotName; + @Value("${ai.team-name}") + private String teamName; + private static final String controlPrompt = + """ + As a smart speaker, your name is {agent_name}, developed by the {team_name} team. You are good at helping people operate various appliances, + You can only control the following electrical type:{electrical_name} + The electrical properties and value that can be controlled by each electrical:{properties_value} + The device of current electrical type and the latest status:{equipment_status} + value ["null"] means devices information is not exist + If you find the user wants to control is not in the above range ,the device is disconnected or allow is false, + you can not control it,and you should warn the user in the answer param + ## Output Format + To answer the question, Use the following JSON format. JSON only, no explanation. Otherwise, you will be punished. + The output should be formatted as a JSON instance that conforms to the format below. JSON only, no explanation. + ```json + { + "thought": "The thought of what to do and why.(use Chinese)", + "action": # the action to take + { + "answer": "Answer vivid,lively,kind to user requests immediately based on data(use Chinese)", + "controlParameters":[ + { + "name": "device name,If it doesn't match, please output null", + "code": "if device connect output 200,else output 400", + "taskType": "If it is a control task, output control; otherwise, output 'query'." + "properties": "electronic input parameters, json list data,If there is no such properties, please output []", + "value": "electronic input parameters, json list data like ["on",30],If it doesn't match Or the user is queried, please output []" + } + ] + } + } + ``` + ## Attention + - Your output is JSON only and no explanation. + - Please respond to the user's request immediately, rather than using terms like "later" or "then" + - If device cannot be controlled, such as not allow or disconnected, please inform the user + """; + + public String getControlTool(int productId) { + Map params = new HashMap<>(); + params.put("agent_name", robotName); + params.put("team_name", teamName); + params.put("electrical_name", descriptionUtil.getElectricalName(productId)); + params.put("properties_value", descriptionUtil.getPropertiesAndValue(productId)); + params.put("equipment_status", descriptionUtil.getCurrentValue(productId)); + return StringUtils.formatString(controlPrompt, params); + } +} diff --git a/src/main/java/top/rslly/iot/utility/ai/prompts/MusicToolPrompt.java b/src/main/java/top/rslly/iot/utility/ai/prompts/MusicToolPrompt.java new file mode 100644 index 0000000..33303db --- /dev/null +++ b/src/main/java/top/rslly/iot/utility/ai/prompts/MusicToolPrompt.java @@ -0,0 +1,64 @@ +/** + * Copyright © 2023-2030 The ruanrongman Authors + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package top.rslly.iot.utility.ai.prompts; + + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import top.rslly.iot.utility.ai.promptTemplate.StringUtils; + +import java.util.HashMap; +import java.util.Map; + +@Component +public class MusicToolPrompt { + @Value("${ai.robot-name}") + private String robotName; + @Value("${ai.team-name}") + private String teamName; + private static final String musicPrompt = + """ + You are a smart speaker, your name is {agent_name}, developed by the {team_name} team.Translate the user input into a music or singer name, or a combination of both, ensuring that the name is unique. + ## Output Format + To answer the question, Use the following JSON format. JSON only, no explanation. Otherwise, you will be punished. + The output should be formatted as a JSON instance that conforms to the format below. JSON only, no explanation. + ```json + { + "thought": "The thought of what to do and why.(use Chinese)", + "action": # the action to take + { + "code": "If this is related to playing music output 200,else output 400", + "answer": "Answer vivid,lively,kind and amiable(use Chinese)", + "singer": "singer name,if you not found,output null", + "music": "music name,if you not found,output null" + } + } + ``` + ## Attention + - Your output is JSON only and no explanation. + """; + + public String getMusicTool() { + Map params = new HashMap<>(); + params.put("agent_name", robotName); + params.put("team_name", teamName); + return StringUtils.formatString(musicPrompt, params); + } +} diff --git a/src/main/java/top/rslly/iot/utility/ai/prompts/ReactPrompt.java b/src/main/java/top/rslly/iot/utility/ai/prompts/ReactPrompt.java new file mode 100644 index 0000000..ce173f1 --- /dev/null +++ b/src/main/java/top/rslly/iot/utility/ai/prompts/ReactPrompt.java @@ -0,0 +1,103 @@ +/** + * Copyright © 2023-2030 The ruanrongman Authors + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package top.rslly.iot.utility.ai.prompts; + + +import org.springframework.stereotype.Component; +import top.rslly.iot.utility.ai.promptTemplate.StringUtils; + +import java.util.HashMap; +import java.util.Map; + +@Component +public class ReactPrompt { + private static final String ReactSystem = + """ + As a diligent Task Agent, you goal is to effectively accomplish the provided task or question as best as you can. + + ## Tools + You have access to the following tools, the tools information is provided by the following schema: + {tool_descriptions} + + ## Output Format + To answer the question, Use the following JSON format. JSON only, no explanation. Otherwise, you will be punished. + The output should be formatted as a JSON instance that conforms to the format below. JSON only, no explanation. + + ```json + { + "thought": "The thought of what to do and why.", + "action": # the action to take, must be one of provided tools + { + "name": "tool name", + "args": "tool input parameters, json type data" + } + } + ``` + + If this format is used, the user will respond in the following format: + + ``` + Observation: tool response + ``` + + You should keep repeating the above format until you have enough information + to answer the question without using any more tools. At that point, you MUST respond + in the one of the following two formats: + + ```json + { + "thought": "The thought of what to do and why.", + "action": { + "name": "finish", + "args": {"content": "You answer here.(use chinese)"} + } + } + ``` + + ```json + { + "thought": "The thought of what to do and why.", + "action": { + "name": "finish", + "args": {"content": "Sorry, I cannot answer your query, because (Summary all the upper steps, and explain)"} + } + } + ``` + + ## Attention + - Your output is JSON only and no explanation. + - Choose only ONE tool and you can't do without using any tools in one step. + - Your final answer output language should be consistent with the language used by the user. Middle step output is English. + - Whether the action input is JSON or str depends on the definition of the tool. + + ## User question + {question} + + ## Current Conversation + Below is the current conversation consisting of interleaving human and assistant history. + """; + + public String getReact(String toolDescriptions, String question) { + Map params = new HashMap<>(); + params.put("tool_descriptions", toolDescriptions); + params.put("question", question); + return StringUtils.formatString(ReactSystem, params); + } +} diff --git a/src/main/java/top/rslly/iot/utility/ai/prompts/ScheduleToolPrompt.java b/src/main/java/top/rslly/iot/utility/ai/prompts/ScheduleToolPrompt.java new file mode 100644 index 0000000..1f13f04 --- /dev/null +++ b/src/main/java/top/rslly/iot/utility/ai/prompts/ScheduleToolPrompt.java @@ -0,0 +1,131 @@ +/** + * Copyright © 2023-2030 The ruanrongman Authors + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package top.rslly.iot.utility.ai.prompts; + + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import top.rslly.iot.utility.ai.DescriptionUtil; +import top.rslly.iot.utility.ai.promptTemplate.StringUtils; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +@Component +public class ScheduleToolPrompt { + @Autowired + private DescriptionUtil descriptionUtil; + @Value("${ai.robot-name}") + private String robotName; + @Value("${ai.team-name}") + private String teamName; + + private static final String schedulePrompt = + """ + You are a smart speaker, your name is {agent_name}, developed by the {team_name} team. + Identify the time when the user wants to be reminded and convert it to a cron expression. + reference information: The current time is {time} + Arranged reminder tasks:{schedule_map} + ## Output Format + To answer the question, Use the following JSON format. JSON only, no explanation. Otherwise, you will be punished. + The output should be formatted as a JSON instance that conforms to the format below. JSON only, no explanation. + ```json + { + "thought": "The thought of what to do and why.(use Chinese)", + "action": # the action to take + { + "code": "If this is related to Time reminder task output 200,else output 400", + "answer": "Answer vivid,lively,kind and amiable(use Chinese),you must tell people the Arranged reminder tasks", + "repeat": "If it is a periodic time task output true,else output false", + "task_name": "The name of the scheduled task", + "time" : "execution time,Here is the response formatted:yyyy-MM-dd HH:mm:ss", + "cron": "Linux Crontab expression", + "cancel":"If it is to cancel the task output ture else false" + } + } + ``` + ## few shot + if user input: Remind me in 5 seconds + if Arranged reminder tasks is {} + The current time is 2024-06-06 10:40:05 + ```json + { + "thought": "用户想要在5分钟后提醒", + "action": + { + "code": "200", + "answer": "好的我将会在5秒种后提醒你,你当前没有设定任何提醒任务", + "task_name": "5 seconds task", + "repeat": "false", + "time": "2024-06-06 10:40:10", + "cron": null, + "cancel":"false" + } + } + ``` + if user input: Remind me at 12:00 noon every day. + if Arranged reminder tasks is {"1300秒后提醒任务":"36 59 11 19 06 ? 2024"} + The current time is 2024-06-06 10:40:05 + ```json + { + "thought": "用户想要每天中午12点提醒", + "action": + { + "code": "200", + "answer": "好的我将会在每天中午12点提醒你,你已经设定了以下提醒任务 '1300秒后提醒任务'", + "task_name": "12:00 noon every day task", + "repeat": "true", + "time": null, + "cron": "0 0 12 * * ?", + "cancel":"false" + } + } + ``` + ## Attention + - Your output is JSON only and no explanation. + - Linux Crontab expression example + "0 0 8 * * *" Indicates that tasks are executed at 8 am every day. + + "0 0/30 9-17 * * *" The task is executed every 30 minutes between 9am and 17pm every day. + + "0 0 12 ? * WED" The task is executed every Wednesday at 12:00 noon. + + "0 0 10 L * ?" The task is executed at 10am on the last day of each month. + + "0 0 3-5 * * *" Execute tasks every hour between 3am and 5am every day. + + "0 15 10 L * ?" The task is executed at 10:15 am on the last day of each month. + """; + + public String getScheduleTool(String openid) { + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + Date date = new Date(); + String formattedDate = formatter.format(date); + Map params = new HashMap<>(); + params.put("agent_name", robotName); + params.put("team_name", teamName); + params.put("schedule_map", descriptionUtil.getSchedule(openid)); + params.put("time", formattedDate); + return StringUtils.formatString(schedulePrompt, params); + } +} diff --git a/src/main/java/top/rslly/iot/utility/ai/prompts/SearchToolPrompt.java b/src/main/java/top/rslly/iot/utility/ai/prompts/SearchToolPrompt.java new file mode 100644 index 0000000..420d3e5 --- /dev/null +++ b/src/main/java/top/rslly/iot/utility/ai/prompts/SearchToolPrompt.java @@ -0,0 +1,44 @@ +/** + * Copyright © 2023-2030 The ruanrongman Authors + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package top.rslly.iot.utility.ai.prompts; + +import org.springframework.stereotype.Component; +import top.rslly.iot.utility.ai.promptTemplate.StringUtils; + +import java.util.HashMap; +import java.util.Map; + +@Component +public class SearchToolPrompt { + private static final String prompt = + """ + You are a search tool, you can search for information on the Internet.You can search for everything that is legal + Please extract and summarize the search content provided based on the user's request,always provide titles and links to the reference content. + The total word count should not exceed 500 words. + ## Search content + {search_content} + """; + + public String getSearchTool(String search_content) { + Map params = new HashMap<>(); + params.put("search_content", search_content); + return StringUtils.formatString(prompt, params); + } +} diff --git a/src/main/java/top/rslly/iot/utility/ai/prompts/WeatherToolPrompt.java b/src/main/java/top/rslly/iot/utility/ai/prompts/WeatherToolPrompt.java new file mode 100644 index 0000000..aab7c38 --- /dev/null +++ b/src/main/java/top/rslly/iot/utility/ai/prompts/WeatherToolPrompt.java @@ -0,0 +1,82 @@ +/** + * Copyright © 2023-2030 The ruanrongman Authors + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package top.rslly.iot.utility.ai.prompts; + +import com.zhipu.oapi.utils.StringUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; + +@Component +public class WeatherToolPrompt { + @Value("${ai.robot-name}") + private String robotName; + @Value("${ai.team-name}") + private String teamName; + private static final String weatherPrompt = + """ + You are a smart speaker, your name is {agent_name}, developed by the {team_name} team.Simplify user input to city names. + ## Output Format + To answer the question, Use the following JSON format. JSON only, no explanation. Otherwise, you will be punished. + The output should be formatted as a JSON instance that conforms to the format below. JSON only, no explanation. + ```json + { + "thought": "The thought of what to do and why.(use Chinese)", + "action": # the action to take + { + "code": "If this is related to weather output 200,else output 400", + "answer": "Answer vivid,lively,kind and amiable(use Chinese)", + "address": "Structured addresses" + } + } + ``` + ## Attention + - Your output is JSON only and no explanation. + - First of all, the address must be a string of characters, containing the names of buildings such as country, province, city, district, town, village, street, house number, housing estate, building, etc. + Characters grouped together from the name of the large region to the name of the small region. A valid address should be unique. + Note: The country information can be selectively ignored during the geocoding conversion for the mainland, Hong Kong, and Macao, but the address composition at the provincial, municipal, + and urban levels cannot be ignored. + ## Current Conversation + Below is the current conversation consisting of interleaving human and assistant history. + """; + private static final String weatherArrangePrompt = """ + 请根据用户的请求,合理使用下列信息,使用人类友善的语言来回答,并推荐用户穿衣以及出行建议,可以配合使用一些表情符号来丰富表达。 + ## weather information + 当前天气信息{lives} + 未来几天的天气信息{forecast} + """; + + public String getWeatherTool() { + Map params = new HashMap<>(); + params.put("agent_name", robotName); + params.put("team_name", teamName); + return StringUtils.formatString(weatherPrompt, params); + } + + public String getWeatherResponse(String lives, String forecast) { + Map params = new HashMap<>(); + params.put("lives", lives); + params.put("forecast", forecast); + return StringUtils.formatString(weatherArrangePrompt, params); + } + +} diff --git a/src/main/java/top/rslly/iot/utility/ai/prompts/WxBoundProductToolPrompt.java b/src/main/java/top/rslly/iot/utility/ai/prompts/WxBoundProductToolPrompt.java new file mode 100644 index 0000000..9aca275 --- /dev/null +++ b/src/main/java/top/rslly/iot/utility/ai/prompts/WxBoundProductToolPrompt.java @@ -0,0 +1,67 @@ +/** + * Copyright © 2023-2030 The ruanrongman Authors + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package top.rslly.iot.utility.ai.prompts; + +import org.springframework.stereotype.Component; + +@Component +public class WxBoundProductToolPrompt { + private static final String wxProductBoundPrompt = + """ + Identify the product name and key to be bind or unbind based on the user's request + ## Output Format + To answer the question, Use the following JSON format. JSON only, no explanation. Otherwise, you will be punished. + The output should be formatted as a JSON instance that conforms to the format below. JSON only, no explanation. + ```json + { + "thought": "The thought of what to do and why.(use Chinese)", + "action": # the action to take + { + "code": "If this is related to bind product output 200,else output 400", + "answer": "Answer vivid,lively,kind and amiable(use Chinese)", + "bind" : "if user want to bound output true,else output false", + "productName": "The user needs to bind the product name", + "productKey": "The product key" + } + } + ``` + ## few shot + if user input: help me bound the air product,the key is 12345 + ```json + { + "thought": "用户想要绑定空调产品", + "action": + { + "code": "200", + "answer": "Ok, I've understood, your product name is air and the key is 12345", + "bind" : "true", + "productName": "air", + "productKey": "12345" + } + } + ``` + ## Attention + - Your output is JSON only and no explanation. + """; + + public String getWxProductBoundTool() { + return wxProductBoundPrompt; + } +} diff --git a/src/main/java/top/rslly/iot/utility/ai/prompts/WxProductActiveToolPrompt.java b/src/main/java/top/rslly/iot/utility/ai/prompts/WxProductActiveToolPrompt.java new file mode 100644 index 0000000..1e06ad0 --- /dev/null +++ b/src/main/java/top/rslly/iot/utility/ai/prompts/WxProductActiveToolPrompt.java @@ -0,0 +1,64 @@ +/** + * Copyright © 2023-2030 The ruanrongman Authors + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package top.rslly.iot.utility.ai.prompts; + +import org.springframework.stereotype.Component; + +@Component +public class WxProductActiveToolPrompt { + private static final String wxProductActivePrompt = + """ + Identify the products name that the user wants to control + ## Output Format + To answer the question, Use the following JSON format. JSON only, no explanation. Otherwise, you will be punished. + The output should be formatted as a JSON instance that conforms to the format below. JSON only, no explanation. + ```json + { + "thought": "The thought of what to do and why.(use Chinese)", + "action": # the action to take + { + "code": "If this is related to bind product output 200,else output 400", + "answer": "Answer vivid,lively,kind and amiable(use Chinese)", + "productName": "The name of the product that the user wants to control" + } + } + ``` + ## few shot + if user input: 设置控制lamp产品 + ```json + { + "thought": "用户想要设置控制产品", + "action": + { + "code": "200", + "answer": "Ok, I've understood, your want to control product name is lamp", + "productName": "lamp" + } + } + ``` + ## Attention + - Your output is JSON only and no explanation. + - The product name should be based solely on the user's input and should not be translated into other languages + """; + + public String getWxProductActiveTool() { + return wxProductActivePrompt; + } +} diff --git a/src/main/java/top/rslly/iot/utility/ai/toolAgent/Agent.java b/src/main/java/top/rslly/iot/utility/ai/toolAgent/Agent.java index 6735617..a341c07 100644 --- a/src/main/java/top/rslly/iot/utility/ai/toolAgent/Agent.java +++ b/src/main/java/top/rslly/iot/utility/ai/toolAgent/Agent.java @@ -32,6 +32,7 @@ import top.rslly.iot.utility.ai.llm.LLM; import top.rslly.iot.utility.ai.llm.LLMFactory; import top.rslly.iot.utility.ai.Manage; +import top.rslly.iot.utility.ai.prompts.ReactPrompt; import java.util.ArrayList; import java.util.HashMap; @@ -46,7 +47,7 @@ public class Agent { @Value("${ai.agent-llm}") private String llmName; @Autowired - private Prompt prompt; + private ReactPrompt reactPrompt; @Autowired private DescriptionUtil descriptionUtil; @Value("${ai.agent-epoch-limit}") @@ -55,7 +56,7 @@ public class Agent { public String run(String question, Map globalMessage) { LLM llm = LLMFactory.getLLM(llmName); - String system = prompt.getReact(descriptionUtil.getTools(), question); + String system = reactPrompt.getReact(descriptionUtil.getTools(), question); List messages = new ArrayList<>(); String toolResult = ""; conversationPrompt.append(system); diff --git a/src/main/java/top/rslly/iot/utility/ai/tools/ChatTool.java b/src/main/java/top/rslly/iot/utility/ai/tools/ChatTool.java index d58555d..18a47a5 100644 --- a/src/main/java/top/rslly/iot/utility/ai/tools/ChatTool.java +++ b/src/main/java/top/rslly/iot/utility/ai/tools/ChatTool.java @@ -19,20 +19,16 @@ */ package top.rslly.iot.utility.ai.tools; -import com.zhipu.oapi.service.v4.model.ChatMessage; -import com.zhipu.oapi.service.v4.model.ChatMessageRole; import lombok.Data; -import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import top.rslly.iot.utility.HttpRequestUtils; import top.rslly.iot.utility.ai.ModelMessage; import top.rslly.iot.utility.ai.ModelMessageRole; -import top.rslly.iot.utility.ai.llm.Glm; -import top.rslly.iot.utility.ai.Prompt; import top.rslly.iot.utility.ai.llm.LLM; import top.rslly.iot.utility.ai.llm.LLMFactory; +import top.rslly.iot.utility.ai.prompts.ChatToolPrompt; import java.util.ArrayList; import java.util.List; @@ -41,7 +37,7 @@ @Component public class ChatTool { @Autowired - private Prompt prompt; + private ChatToolPrompt chatToolPrompt; @Autowired private HttpRequestUtils httpRequestUtils; @Value("${ai.chatTool-llm}") @@ -57,11 +53,11 @@ public String run(String question, List memory) { List messages = new ArrayList<>(); ModelMessage systemMessage = - new ModelMessage(ModelMessageRole.SYSTEM.value(), prompt.getChatTool()); + new ModelMessage(ModelMessageRole.SYSTEM.value(), chatToolPrompt.getChatTool()); ModelMessage userMessage = new ModelMessage(ModelMessageRole.USER.value(), question); if (!memory.isEmpty()) { // messages.addAll(memory); - systemMessage.setContent(prompt.getChatTool() + memory); + systemMessage.setContent(chatToolPrompt.getChatTool() + memory); } messages.add(systemMessage); messages.add(userMessage); diff --git a/src/main/java/top/rslly/iot/utility/ai/tools/ClassifierTool.java b/src/main/java/top/rslly/iot/utility/ai/tools/ClassifierTool.java index 6806682..92d2cd9 100644 --- a/src/main/java/top/rslly/iot/utility/ai/tools/ClassifierTool.java +++ b/src/main/java/top/rslly/iot/utility/ai/tools/ClassifierTool.java @@ -28,9 +28,9 @@ import top.rslly.iot.utility.ai.IcAiException; import top.rslly.iot.utility.ai.ModelMessage; import top.rslly.iot.utility.ai.ModelMessageRole; -import top.rslly.iot.utility.ai.Prompt; import top.rslly.iot.utility.ai.llm.LLM; import top.rslly.iot.utility.ai.llm.LLMFactory; +import top.rslly.iot.utility.ai.prompts.ClassifierToolPrompt; import java.util.ArrayList; import java.util.HashMap; @@ -41,7 +41,7 @@ @Component public class ClassifierTool { @Autowired - private Prompt prompt; + private ClassifierToolPrompt classifierToolPrompt; @Autowired private HttpRequestUtils httpRequestUtils; @Value("${ai.classifierTool-llm}") @@ -58,11 +58,11 @@ public Map run(String question, List memory) { Map resultMap = new HashMap<>(); ModelMessage systemMessage = - new ModelMessage(ModelMessageRole.SYSTEM.value(), prompt.getClassifierTool()); + new ModelMessage(ModelMessageRole.SYSTEM.value(), classifierToolPrompt.getClassifierTool()); ModelMessage userMessage = new ModelMessage(ModelMessageRole.USER.value(), question); if (!memory.isEmpty()) { // messages.addAll(memory); - systemMessage.setContent(prompt.getClassifierTool() + memory); + systemMessage.setContent(classifierToolPrompt.getClassifierTool() + memory); } messages.add(systemMessage); messages.add(userMessage); diff --git a/src/main/java/top/rslly/iot/utility/ai/tools/ControlTool.java b/src/main/java/top/rslly/iot/utility/ai/tools/ControlTool.java index 20df409..84e832f 100644 --- a/src/main/java/top/rslly/iot/utility/ai/tools/ControlTool.java +++ b/src/main/java/top/rslly/iot/utility/ai/tools/ControlTool.java @@ -26,7 +26,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; -import top.rslly.iot.models.ProductModelEntity; import top.rslly.iot.param.request.ControlParam; import top.rslly.iot.services.HardWareServiceImpl; import top.rslly.iot.services.ProductDeviceServiceImpl; @@ -35,21 +34,20 @@ import top.rslly.iot.utility.ai.IcAiException; import top.rslly.iot.utility.ai.ModelMessage; import top.rslly.iot.utility.ai.ModelMessageRole; -import top.rslly.iot.utility.ai.Prompt; import top.rslly.iot.utility.ai.llm.LLM; import top.rslly.iot.utility.ai.llm.LLMFactory; +import top.rslly.iot.utility.ai.prompts.ControlToolPrompt; import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; @Data @Component @Slf4j public class ControlTool implements BaseTool { @Autowired - private Prompt prompt; + private ControlToolPrompt controlToolPrompt; @Autowired private ProductDeviceServiceImpl productDeviceService; @Autowired @@ -84,7 +82,8 @@ public String run(String question, Map globalMessage) { List messages = new ArrayList<>(); ModelMessage systemMessage = - new ModelMessage(ModelMessageRole.SYSTEM.value(), prompt.getControlTool(productId)); + new ModelMessage(ModelMessageRole.SYSTEM.value(), + controlToolPrompt.getControlTool(productId)); // log.info("systemMessage: " + systemMessage.getContent()); ModelMessage userMessage = new ModelMessage(ModelMessageRole.USER.value(), question); messages.add(systemMessage); diff --git a/src/main/java/top/rslly/iot/utility/ai/tools/MusicTool.java b/src/main/java/top/rslly/iot/utility/ai/tools/MusicTool.java index 1ea0cdc..f46d168 100644 --- a/src/main/java/top/rslly/iot/utility/ai/tools/MusicTool.java +++ b/src/main/java/top/rslly/iot/utility/ai/tools/MusicTool.java @@ -29,9 +29,9 @@ import top.rslly.iot.utility.ai.IcAiException; import top.rslly.iot.utility.ai.ModelMessage; import top.rslly.iot.utility.ai.ModelMessageRole; -import top.rslly.iot.utility.ai.Prompt; import top.rslly.iot.utility.ai.llm.LLM; import top.rslly.iot.utility.ai.llm.LLMFactory; +import top.rslly.iot.utility.ai.prompts.MusicToolPrompt; import java.io.IOException; import java.util.*; @@ -40,7 +40,7 @@ @Component public class MusicTool implements BaseTool> { @Autowired - private Prompt prompt; + private MusicToolPrompt musicToolPrompt; @Autowired private HttpRequestUtils httpRequestUtils; @Value("${ai.musicTool-llm}") @@ -58,7 +58,7 @@ public Map run(String question) { Map responseMap = new HashMap<>(); ModelMessage systemMessage = - new ModelMessage(ModelMessageRole.SYSTEM.value(), prompt.getMusicTool()); + new ModelMessage(ModelMessageRole.SYSTEM.value(), musicToolPrompt.getMusicTool()); ModelMessage userMessage = new ModelMessage(ModelMessageRole.USER.value(), question); messages.add(systemMessage); messages.add(userMessage); diff --git a/src/main/java/top/rslly/iot/utility/ai/tools/ScheduleTool.java b/src/main/java/top/rslly/iot/utility/ai/tools/ScheduleTool.java index a5ce25e..2825ac6 100644 --- a/src/main/java/top/rslly/iot/utility/ai/tools/ScheduleTool.java +++ b/src/main/java/top/rslly/iot/utility/ai/tools/ScheduleTool.java @@ -32,22 +32,21 @@ import top.rslly.iot.utility.ai.IcAiException; import top.rslly.iot.utility.ai.ModelMessage; import top.rslly.iot.utility.ai.ModelMessageRole; -import top.rslly.iot.utility.ai.Prompt; import top.rslly.iot.utility.ai.llm.LLM; import top.rslly.iot.utility.ai.llm.LLMFactory; +import top.rslly.iot.utility.ai.prompts.ScheduleToolPrompt; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; -import java.util.UUID; @Component @Data @Slf4j public class ScheduleTool { @Autowired - private Prompt prompt; + private ScheduleToolPrompt scheduleToolPrompt; @Value("${ai.scheduleTool-llm}") private String llmName; private String name = "scheduleTool"; @@ -63,7 +62,8 @@ public String run(String question, String openid, String appid) { List messages = new ArrayList<>(); ModelMessage systemMessage = - new ModelMessage(ModelMessageRole.SYSTEM.value(), prompt.getScheduleTool(openid)); + new ModelMessage(ModelMessageRole.SYSTEM.value(), + scheduleToolPrompt.getScheduleTool(openid)); // log.info("systemMessage: " + systemMessage.getContent()); ModelMessage userMessage = new ModelMessage(ModelMessageRole.USER.value(), question); messages.add(systemMessage); diff --git a/src/main/java/top/rslly/iot/utility/ai/tools/SearchTool.java b/src/main/java/top/rslly/iot/utility/ai/tools/SearchTool.java new file mode 100644 index 0000000..ec5e378 --- /dev/null +++ b/src/main/java/top/rslly/iot/utility/ai/tools/SearchTool.java @@ -0,0 +1,211 @@ +/** + * Copyright © 2023-2030 The ruanrongman Authors + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package top.rslly.iot.utility.ai.tools; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.openqa.selenium.By; +import org.openqa.selenium.Keys; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.chrome.ChromeDriver; +import org.openqa.selenium.chrome.ChromeOptions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import top.rslly.iot.utility.ai.ModelMessage; +import top.rslly.iot.utility.ai.ModelMessageRole; +import top.rslly.iot.utility.ai.llm.LLM; +import top.rslly.iot.utility.ai.llm.LLMFactory; +import top.rslly.iot.utility.ai.prompts.SearchToolPrompt; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.*; + +@Component +@Data +@Slf4j +public class SearchTool implements BaseTool { + @Autowired + private SearchToolPrompt searchToolPrompt; + @Value("${ai.searchTool-llm}") + private String llmName; + @Value("${ai.chromeDrive-path}") + private String chromeDrivePath; + @Value("${ai.chrome-path}") + private String chromePath; + private String name = "searchTool"; + private String description = """ + Tools for online search + Args: you want to search for online(str) + """; + + public static WebDriver getDriver(String chromeDrivePath, String chromePath) { + // 指定驱动路径 + System.setProperty("webdriver.chrome.driver", chromeDrivePath); + System.setProperty("webdriver.chrome.silentOutput", "true"); + // 谷歌驱动 + ChromeOptions options = new ChromeOptions(); + // 允许所有请求 + options.addArguments("--remote-allow-origins=*"); + + // 设置谷歌浏览器路径 + options.setBinary(chromePath); + options.addArguments( + "user-agent='Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36'"); + options.addArguments("--disable-gpu"); + options.addArguments("--headless"); + options.addArguments("--no-sandbox"); + options.addArguments("--disable-dev-shm-usage"); + options.addArguments("--disable-extensions"); + options.addArguments("--disable-images"); + options.addArguments("--silent"); + // options.setExperimentalOption("excludeSwitches", new String[]{"enable-logging"}); + + return new ChromeDriver(options); + } + + public static String fetchPage(String url, String chromeDrivePath, String chromePath) { + WebDriver driver = getDriver(chromeDrivePath, chromePath); + String text; + try { + driver.get(url); + text = driver.findElement(By.tagName("body")).getText(); + } catch (Exception e) { + text = "搜索失败"; + } + driver.quit(); + return text; + } + + public static List> getResult(String question, String chromeDrivePath, + String chromePath) { + List> results = new ArrayList<>(); + WebDriver driver = getDriver(chromeDrivePath, chromePath); + driver.manage().timeouts().implicitlyWait(2, TimeUnit.SECONDS); // 隐式等待5秒 + // 最大化窗口 + driver.manage().window().maximize(); // 最大化窗口 + // 设置隐性等待时间 + // 启动需要打开的网页 + driver.get("https://www.baidu.com"); + var el = driver.findElement(By.id("kw")); + el.sendKeys(question); + el.sendKeys(Keys.ENTER); + var searchResult = driver.findElements(By.cssSelector(".new-pmd.c-container")); + int i = 0; + for (var result : searchResult) { + Map resultMap = new HashMap<>(); + if (i != 0) { + var titleElement = result.findElement(By.cssSelector("a")); + String title = titleElement.getText(); + String link = result.findElement(By.cssSelector("a")).getAttribute("href"); + resultMap.put("title", title); + resultMap.put("url", link); + results.add(resultMap); + } + ++i; + if (i == 6) + break; + } + // System.out.println(resultMap); + driver.quit(); + return results; + } + + public static String process_search_result(String question, String chromeDrivePath, + String chromePath) { + ExecutorService executor = Executors.newFixedThreadPool(10); + var results = getResult(question, chromeDrivePath, chromePath); + + Map, Map> futureToUrl = new HashMap<>(); + JSONObject resultObject = new JSONObject(); + // 提交任务 + List> futures = new ArrayList<>(); + for (Map result : results) { + try { + CompletableFuture future = + CompletableFuture.supplyAsync( + () -> fetchPage(result.get("url"), chromeDrivePath, chromePath), executor); + futureToUrl.put(future, result); + futures.add(future); + } catch (Exception ignored) { + + } + } + + // 等待所有任务完成 + CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join(); + + // 处理结果 + for (CompletableFuture future : futures) { + try { + Map urlInfo = futureToUrl.get(future); + String pageText = future.join(); // 或者使用 future.get() + JSONObject jsonObject = new JSONObject(); + // System.out.printf("Title: %s\nURL: %s\nText: %s...\n", + // urlInfo.get("title"), urlInfo.get("url"), + // pageText.substring(0, Math.min(pageText.length(), 1000))); + + // 注意这里只存储一次即可 + jsonObject.put("link", urlInfo.get("url")); + jsonObject.put("text", pageText.substring(0, Math.min(pageText.length(), 800))); + resultObject.put("title:" + urlInfo.get("title"), jsonObject); + } catch (Exception e) { + Map urlInfo = futureToUrl.get(future); + System.out.printf("%s generated an exception: %s\n", urlInfo.get("url"), e.getMessage()); + } + } + + executor.shutdown(); // 关闭线程池 + return resultObject.toJSONString(); + } + + @Override + public String run(String question) { + LLM llm = LLMFactory.getLLM(llmName); + List messages = new ArrayList<>(); + try { + ModelMessage systemMessage = + new ModelMessage(ModelMessageRole.SYSTEM.value(), + searchToolPrompt + .getSearchTool(process_search_result(question, chromeDrivePath, chromePath))); + log.info("systemMessage: " + systemMessage.getContent()); + ModelMessage userMessage = new ModelMessage(ModelMessageRole.USER.value(), question); + messages.add(systemMessage); + messages.add(userMessage); + + // process_llm_result(obj, openid, appid); + return llm.commonChat(question, messages, false); + } catch (Exception e) { + // e.printStackTrace(); + log.info("LLM error: " + e.getMessage()); + return "不好意思,查询失败,请重新尝试!"; + } + } + + @Override + public String run(String question, Map globalMessage) { + return null; + } +} diff --git a/src/main/java/top/rslly/iot/utility/ai/tools/WeatherTool.java b/src/main/java/top/rslly/iot/utility/ai/tools/WeatherTool.java index 17d2017..bb4833a 100644 --- a/src/main/java/top/rslly/iot/utility/ai/tools/WeatherTool.java +++ b/src/main/java/top/rslly/iot/utility/ai/tools/WeatherTool.java @@ -29,9 +29,9 @@ import top.rslly.iot.utility.ai.IcAiException; import top.rslly.iot.utility.ai.ModelMessage; import top.rslly.iot.utility.ai.ModelMessageRole; -import top.rslly.iot.utility.ai.Prompt; import top.rslly.iot.utility.ai.llm.LLM; import top.rslly.iot.utility.ai.llm.LLMFactory; +import top.rslly.iot.utility.ai.prompts.WeatherToolPrompt; import java.io.IOException; import java.util.*; @@ -40,7 +40,7 @@ @Component public class WeatherTool implements BaseTool { @Autowired - private Prompt prompt; + private WeatherToolPrompt weatherToolPrompt; @Value("${weather.key}") private String AmapKey; @Autowired @@ -59,7 +59,7 @@ public String run(String question) { List messages = new ArrayList<>(); ModelMessage systemMessage = - new ModelMessage(ModelMessageRole.SYSTEM.value(), prompt.getWeatherTool()); + new ModelMessage(ModelMessageRole.SYSTEM.value(), weatherToolPrompt.getWeatherTool()); ModelMessage userMessage = new ModelMessage(ModelMessageRole.USER.value(), question); messages.add(systemMessage); messages.add(userMessage); @@ -68,7 +68,7 @@ public String run(String question) { Map answer = process_llm_result(obj); ModelMessage responseSystemMessage = new ModelMessage(ModelMessageRole.SYSTEM.value(), - prompt.getWeatherResponse(answer.get("lives"), answer.get("forecasts"))); + weatherToolPrompt.getWeatherResponse(answer.get("lives"), answer.get("forecasts"))); messages.clear(); messages.add(responseSystemMessage); messages.add(userMessage); diff --git a/src/main/java/top/rslly/iot/utility/ai/tools/WxBoundProductTool.java b/src/main/java/top/rslly/iot/utility/ai/tools/WxBoundProductTool.java index c4cb703..39f19b6 100644 --- a/src/main/java/top/rslly/iot/utility/ai/tools/WxBoundProductTool.java +++ b/src/main/java/top/rslly/iot/utility/ai/tools/WxBoundProductTool.java @@ -24,16 +24,17 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; -import top.rslly.iot.services.*; +import top.rslly.iot.services.ProductServiceImpl; +import top.rslly.iot.services.WxProductBindServiceImpl; +import top.rslly.iot.services.WxUserServiceImpl; import top.rslly.iot.utility.ai.IcAiException; import top.rslly.iot.utility.ai.ModelMessage; import top.rslly.iot.utility.ai.ModelMessageRole; -import top.rslly.iot.utility.ai.Prompt; import top.rslly.iot.utility.ai.llm.LLM; import top.rslly.iot.utility.ai.llm.LLMFactory; +import top.rslly.iot.utility.ai.prompts.WxBoundProductToolPrompt; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -41,7 +42,7 @@ @Component public class WxBoundProductTool implements BaseTool { @Autowired - private Prompt prompt; + private WxBoundProductToolPrompt wxBoundProductToolPrompt; @Autowired private WxUserServiceImpl wxUserService; @Autowired @@ -68,7 +69,8 @@ public String run(String question, Map globalMessage) { LLM llm = LLMFactory.getLLM(llmName); List messages = new ArrayList<>(); ModelMessage systemMessage = - new ModelMessage(ModelMessageRole.SYSTEM.value(), prompt.getWxProductBoundTool()); + new ModelMessage(ModelMessageRole.SYSTEM.value(), + wxBoundProductToolPrompt.getWxProductBoundTool()); ModelMessage userMessage = new ModelMessage(ModelMessageRole.USER.value(), question); messages.add(systemMessage); messages.add(userMessage); diff --git a/src/main/java/top/rslly/iot/utility/ai/tools/WxProductActiveTool.java b/src/main/java/top/rslly/iot/utility/ai/tools/WxProductActiveTool.java index 80f6e0a..e918373 100644 --- a/src/main/java/top/rslly/iot/utility/ai/tools/WxProductActiveTool.java +++ b/src/main/java/top/rslly/iot/utility/ai/tools/WxProductActiveTool.java @@ -32,12 +32,11 @@ import top.rslly.iot.utility.ai.IcAiException; import top.rslly.iot.utility.ai.ModelMessage; import top.rslly.iot.utility.ai.ModelMessageRole; -import top.rslly.iot.utility.ai.Prompt; import top.rslly.iot.utility.ai.llm.LLM; import top.rslly.iot.utility.ai.llm.LLMFactory; +import top.rslly.iot.utility.ai.prompts.WxProductActiveToolPrompt; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -45,7 +44,7 @@ @Component public class WxProductActiveTool implements BaseTool { @Autowired - private Prompt prompt; + private WxProductActiveToolPrompt wxProductActiveToolPrompt; @Autowired private ProductServiceImpl productService; @Autowired @@ -74,7 +73,8 @@ public String run(String question, Map globalMessage) { LLM llm = LLMFactory.getLLM(llmName); List messages = new ArrayList<>(); ModelMessage systemMessage = - new ModelMessage(ModelMessageRole.SYSTEM.value(), prompt.getWxProductActiveTool()); + new ModelMessage(ModelMessageRole.SYSTEM.value(), + wxProductActiveToolPrompt.getWxProductActiveTool()); ModelMessage userMessage = new ModelMessage(ModelMessageRole.USER.value(), question); messages.add(systemMessage); messages.add(userMessage); diff --git a/src/main/java/top/rslly/iot/utility/ai/voice/DashScopeVoice.java b/src/main/java/top/rslly/iot/utility/ai/voice/DashScopeVoice.java index 8cc43d2..87ab5e6 100644 --- a/src/main/java/top/rslly/iot/utility/ai/voice/DashScopeVoice.java +++ b/src/main/java/top/rslly/iot/utility/ai/voice/DashScopeVoice.java @@ -24,16 +24,10 @@ import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversationResult; import com.alibaba.dashscope.common.MultiModalMessage; import com.alibaba.dashscope.common.Role; -import com.alibaba.dashscope.exception.ApiException; -import com.alibaba.dashscope.exception.NoApiKeyException; -import com.alibaba.dashscope.exception.UploadFileException; import com.alibaba.dashscope.utils.Constants; -import com.zhipu.oapi.ClientV4; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; -import top.rslly.iot.utility.ai.Prompt; import java.util.Arrays; import java.util.Collections; @@ -43,8 +37,7 @@ @Component @Slf4j public class DashScopeVoice { - @Autowired - private Prompt prompt; + private static MultiModalConversation conv; private static final Pattern pattern = Pattern.compile("\"(.*?)\""); diff --git a/src/main/java/top/rslly/iot/utility/result/ResultCode.java b/src/main/java/top/rslly/iot/utility/result/ResultCode.java index 2524197..fcf9a0d 100644 --- a/src/main/java/top/rslly/iot/utility/result/ResultCode.java +++ b/src/main/java/top/rslly/iot/utility/result/ResultCode.java @@ -41,7 +41,7 @@ public enum ResultCode { "账号已存在"), USER_ACCOUNT_USE_BY_OTHERS(2009, "账号下线"), /* 业务错误 */ - NO_PERMISSION(3001, "没有权限"), HAS_DEPENDENCIES(3002, "存在依赖关系,无法删除"); + NO_PERMISSION(3001, "没有权限"), HAS_DEPENDENCIES(3002, "存在依赖关系,无法删除"), DEVICE_ABANDON(3003, "设备被禁用"); private Integer code; private String message; diff --git a/src/main/java/top/rslly/iot/utility/wx/SmartRobot.java b/src/main/java/top/rslly/iot/utility/wx/SmartRobot.java index 7cb3372..c83c5d0 100644 --- a/src/main/java/top/rslly/iot/utility/wx/SmartRobot.java +++ b/src/main/java/top/rslly/iot/utility/wx/SmartRobot.java @@ -70,7 +70,11 @@ public void smartSendContent(String openid, String msg, String microappid) throw } } String content = router.response(msg, openid, productId, microappid); - dealWx.sendContent(openid, content, microappid); + if (content.length() > 800) { + dealWx.sendContent(openid, content.substring(0, 800), microappid); + dealWx.sendContent(openid, content.substring(800), microappid); + } else + dealWx.sendContent(openid, content, microappid); } @Async("taskExecutor") diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index d244f27..e47f7d5 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -85,7 +85,11 @@ js: ota: bin: path: D://temp-rainy// +weather: + key: XXX ai: + chromeDrive-path: XXX + chrome-path: XXX glm-key: XXX deepSeek-key: XXX dashscope-key: XXX @@ -100,9 +104,10 @@ ai: wxBoundProductTool-llm: deepSeek wxProductActiveTool-llm: deepSeek scheduleTool-llm: deepSeek + searchTool-llm: deepSeek agent-llm: deepSeek emq: api: url: http://192.168.49.129:18083/api/v5/clients key: XXX - secret: XXX + secret: XXX \ No newline at end of file diff --git a/src/test/java/top/rslly/iot/DemoApplicationTests.java b/src/test/java/top/rslly/iot/DemoApplicationTests.java index c310761..63d2728 100644 --- a/src/test/java/top/rslly/iot/DemoApplicationTests.java +++ b/src/test/java/top/rslly/iot/DemoApplicationTests.java @@ -32,7 +32,7 @@ import org.springframework.context.ApplicationContext; import org.springframework.test.context.junit4.SpringRunner; import top.rslly.iot.dao.ProductDeviceRepository; -import top.rslly.iot.dao.TimeDataRepository; +import top.rslly.iot.dao.DataTimeRepository; import top.rslly.iot.models.DataEntity; import top.rslly.iot.models.influxdb.DataTimeEntity; import top.rslly.iot.param.request.ControlParam; @@ -43,7 +43,6 @@ import top.rslly.iot.utility.ai.Manage; import top.rslly.iot.utility.ai.Prompt; import top.rslly.iot.utility.ai.chain.Router; -import top.rslly.iot.utility.ai.llm.Glm; import top.rslly.iot.utility.ai.toolAgent.Agent; import top.rslly.iot.utility.ai.tools.*; import top.rslly.iot.utility.ai.voice.DashScopeVoice; @@ -66,9 +65,9 @@ class DemoApplicationTests { @Autowired private DataCleanAuto dataCleanAuto; @Autowired - private ExecutorImpl dataTimeRepository; + private ExecutorImpl executor; @Autowired(required = false) - private TimeDataRepository timeDataRepository; + private DataTimeRepository dataTimeRepository; @Autowired private ControlTool controlTool; @Autowired @@ -91,8 +90,7 @@ class DemoApplicationTests { ChatTool chatTool; @Autowired private ScheduleTool scheduleTool; - @Autowired - private Prompt prompt; + @Test void testDataSave() { @@ -120,7 +118,7 @@ void testDataAutoClean() throws IOException { @Test public void ping() { - if (dataTimeRepository.ping()) { + if (executor.ping()) { log.info("连接成功"); } else { log.info("连接失败"); @@ -137,13 +135,13 @@ void insert() { dataTimeEntity.setJsonKey("temp"); dataTimeEntity.setValue("30"); dataTimeEntity.setCharacteristic(UUID.randomUUID().toString()); - dataTimeRepository.insert(dataTimeEntity); + executor.insert(dataTimeEntity); } @Test public void list() { String sql = "SELECT * FROM \"data\" tz('Asia/Shanghai')"; - List locations = dataTimeRepository.query(DataTimeEntity.class, sql); + List locations = executor.query(DataTimeEntity.class, sql); if (locations == null || locations.isEmpty()) { log.info("暂无数据"); } @@ -158,7 +156,7 @@ public void delete() { // 默认使用配置文件中数据库 // influxDao.deleteDataBase(); // 使用指定数据库 - dataTimeRepository.deleteDataBase("data"); + executor.deleteDataBase("data"); } @Test @@ -171,18 +169,18 @@ public void insert2() { dataTimeEntity.setJsonKey("temp"); dataTimeEntity.setValue("90"); dataTimeEntity.setCharacteristic(UUID.randomUUID().toString()); - timeDataRepository.insert(dataTimeEntity); + executor.insert(dataTimeEntity); } @Test public void time() { - var res = timeDataRepository.findAllByTimeBetweenAndDeviceId(0L, System.currentTimeMillis(), 1); + var res = dataTimeRepository.findAllByTimeBetweenAndDeviceId(0L, System.currentTimeMillis(), 1); System.out.println(res.get(0)); } @Test public void influxdbMetaData() { - var res = timeDataRepository.findAllBySort(1, "color"); + var res = dataTimeRepository.findAllBySort(1, "color"); System.out.println(res.toString()); } diff --git a/src/test/java/top/rslly/iot/searchToolTests.java b/src/test/java/top/rslly/iot/searchToolTests.java new file mode 100644 index 0000000..ba02243 --- /dev/null +++ b/src/test/java/top/rslly/iot/searchToolTests.java @@ -0,0 +1,36 @@ +/** + * Copyright © 2023-2030 The ruanrongman Authors + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package top.rslly.iot; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import top.rslly.iot.utility.ai.tools.SearchTool; + +@SpringBootTest +public class searchToolTests { + @Autowired + private SearchTool searchTool; + + @Test + public void searchTool() { + searchTool.run("中国队奥运会拿了多少奖牌,详细介绍每块金牌的故事"); + } +}