From 0cc3500ef5f51105ed2989865d0808afa875101a Mon Sep 17 00:00:00 2001 From: kira Date: Mon, 14 Aug 2023 21:41:23 +0800 Subject: [PATCH] =?UTF-8?q?yinruoxi:=20=E6=96=B0=E5=A2=9E=20=20=20=20=201.?= =?UTF-8?q?NLT=E6=A8=A1=E5=9D=97=E4=BB=A3=E7=A0=81=E4=B8=8A=E4=BC=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Jenkinsfile | 12 +- Jenkinsfile1 | 67 ++++ pom.xml | 11 + xueyi-api/pom.xml | 1 + xueyi-api/xueyi-api-nlt/pom.xml | 33 ++ .../netty/domain/vo/DmWebSocketMessageVo.java | 13 + .../netty/feign/RemoteWebsocketService.java | 20 ++ .../nlt/api/nlt/domain/vo/DmIntentVo.java | 16 + .../api/nlt/domain/vo/DmRecognitionVo.java | 13 + .../api/nlt/feign/RemoteIntentService.java | 24 ++ .../factory/RemoteIntentFallbackFactory.java | 15 + ...ot.autoconfigure.AutoConfiguration.imports | 1 + .../meeting/feign/RemoteMeetingService.java | 3 + .../factory/RemoteMeetingFallbackFactory.java | 5 + .../core/constant/basic/ServiceConstants.java | 1 + .../mqtt/constant/MqttTopicConstant.java | 15 + xueyi-modules/pom.xml | 1 + xueyi-modules/xueyi-nlt/Dockerfile | 17 + xueyi-modules/xueyi-nlt/pom.xml | 117 +++++++ .../xueyi-nlt/sonar-project.properties | 15 + .../com/xueyi/nlt/XueYiNltApplication.java | 29 ++ .../xueyi/nlt/netty/client/NettyClient.java | 195 +++++++++++ .../nlt/netty/client/WebSocketClient.java | 313 ++++++++++++++++++ .../nlt/netty/client/WebsocketConfig.java | 202 +++++++++++ .../client/codec/WsChannelInitializer.java | 46 +++ .../client/handler/MockClientHandler.java | 66 ++++ .../client/handler/NettyClientHandler.java | 108 ++++++ .../handler/NettyWebsocketClientHandler.java | 196 +++++++++++ .../netty/client/handler/WsClientHandler.java | 23 ++ .../netty/client/message/ReceiveMessage.java | 13 + .../controller/DmWebsocketController.java | 66 ++++ .../nlt/controller/DmIntentController.java | 296 +++++++++++++++++ .../xueyi/nlt/nlt/domain/dto/DmIntentDto.java | 14 + .../nlt/domain/model/DmIntentConverter.java | 13 + .../xueyi/nlt/nlt/domain/po/DmIntentPo.java | 61 ++++ .../nlt/nlt/domain/query/DmIntentQuery.java | 13 + .../nlt/nlt/domain/vo/IntentTemplateVo.java | 16 + .../nlt/nlt/manager/IDmIntentManager.java | 8 + .../nlt/nlt/manager/impl/DmIntentManager.java | 15 + .../xueyi/nlt/nlt/mapper/DmIntentMapper.java | 11 + .../nlt/nlt/service/IDmIntentService.java | 8 + .../nlt/service/impl/DmIntentServiceImpl.java | 12 + .../xueyi/nlt/nlt/template/BaseTemplate.java | 7 + .../nlt/template/MeetingOrderTemplate.java | 93 ++++++ .../xueyi-nlt/src/main/resources/banner.txt | 10 + .../src/main/resources/bootstrap.yml | 34 ++ .../xueyi-nlt/src/main/resources/logback.xml | 74 +++++ xueyi-modules/xueyi-system/pom.xml | 9 + .../api/DmMeetingApiController.java | 12 + .../api/DmMeetingInnerApiController.java | 41 +++ 50 files changed, 2399 insertions(+), 5 deletions(-) create mode 100644 Jenkinsfile1 create mode 100644 xueyi-api/xueyi-api-nlt/pom.xml create mode 100644 xueyi-api/xueyi-api-nlt/src/main/java/com/xueyi/nlt/api/netty/domain/vo/DmWebSocketMessageVo.java create mode 100644 xueyi-api/xueyi-api-nlt/src/main/java/com/xueyi/nlt/api/netty/feign/RemoteWebsocketService.java create mode 100644 xueyi-api/xueyi-api-nlt/src/main/java/com/xueyi/nlt/api/nlt/domain/vo/DmIntentVo.java create mode 100644 xueyi-api/xueyi-api-nlt/src/main/java/com/xueyi/nlt/api/nlt/domain/vo/DmRecognitionVo.java create mode 100644 xueyi-api/xueyi-api-nlt/src/main/java/com/xueyi/nlt/api/nlt/feign/RemoteIntentService.java create mode 100644 xueyi-api/xueyi-api-nlt/src/main/java/com/xueyi/nlt/api/nlt/feign/factory/RemoteIntentFallbackFactory.java create mode 100644 xueyi-api/xueyi-api-nlt/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 xueyi-common/xueyi-common-mqtt/src/main/java/com/xueyi/common/mqtt/constant/MqttTopicConstant.java create mode 100644 xueyi-modules/xueyi-nlt/Dockerfile create mode 100644 xueyi-modules/xueyi-nlt/pom.xml create mode 100644 xueyi-modules/xueyi-nlt/sonar-project.properties create mode 100644 xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/XueYiNltApplication.java create mode 100644 xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/client/NettyClient.java create mode 100644 xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/client/WebSocketClient.java create mode 100644 xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/client/WebsocketConfig.java create mode 100644 xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/client/codec/WsChannelInitializer.java create mode 100644 xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/client/handler/MockClientHandler.java create mode 100644 xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/client/handler/NettyClientHandler.java create mode 100644 xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/client/handler/NettyWebsocketClientHandler.java create mode 100644 xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/client/handler/WsClientHandler.java create mode 100644 xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/client/message/ReceiveMessage.java create mode 100644 xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/controller/DmWebsocketController.java create mode 100644 xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/controller/DmIntentController.java create mode 100644 xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/domain/dto/DmIntentDto.java create mode 100644 xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/domain/model/DmIntentConverter.java create mode 100644 xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/domain/po/DmIntentPo.java create mode 100644 xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/domain/query/DmIntentQuery.java create mode 100644 xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/domain/vo/IntentTemplateVo.java create mode 100644 xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/manager/IDmIntentManager.java create mode 100644 xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/manager/impl/DmIntentManager.java create mode 100644 xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/mapper/DmIntentMapper.java create mode 100644 xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/service/IDmIntentService.java create mode 100644 xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/service/impl/DmIntentServiceImpl.java create mode 100644 xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/template/BaseTemplate.java create mode 100644 xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/template/MeetingOrderTemplate.java create mode 100644 xueyi-modules/xueyi-nlt/src/main/resources/banner.txt create mode 100644 xueyi-modules/xueyi-nlt/src/main/resources/bootstrap.yml create mode 100644 xueyi-modules/xueyi-nlt/src/main/resources/logback.xml diff --git a/Jenkinsfile b/Jenkinsfile index 079e0d48..0ebb8c08 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -19,7 +19,7 @@ pipeline { sh "which java" echo "打包子目录并进行代码检查 ${project_name}" sh "mvn clean install" - sh "mvn -f ${project_name} clean package -e -U -Dmaven.test.skip=true -Dsonar.login=admin -Dsonar.password=Digimeta@2023 sonar:sonar" + sh "mvn -f ${project_name} clean package -e -U -Dmaven.test.skip=true -P${build_env} -Dsonar.login=admin -Dsonar.password=Digimeta@2023 sonar:sonar" } } } @@ -31,8 +31,10 @@ pipeline { def workspace=pwd() // 删除所有report报告 sh "find ${workspace} -name report-task.txt | xargs rm -f" + // 删除所有sonar锁 + sh "find ${workspace} -name .sonar_lock | xargs rm -f" } - timeout(time: 15, unit: 'MINUTES') { + timeout(time: 5, unit: 'MINUTES') { // Parameter indicates whether to set pipeline to UNSTABLE if Quality Gate fails // true = set pipeline to UNSTABLE, false = don't waitForQualityGates abortPipeline: true waitForQualityGate abortPipeline: true @@ -55,9 +57,9 @@ pipeline { sh """ cd ${workspace}/${project_name} docker login --username=缔智元2023 --password=digimeta@2023 ${ali_registry} - docker build --tag ${ali_registry}/digitalman-multisaas/${module_name}:${version} . - docker push ${ali_registry}/digitalman-multisaas/${module_name}:${version} - docker rmi ${ali_registry}/digitalman-multisaas/${module_name}:${version} + docker build --tag ${ali_registry}/digitalman-multisaas/${module_name}:${git_version}-${build_env} . + docker push ${ali_registry}/digitalman-multisaas/${module_name}:${git_version}-${build_env} + docker rmi ${ali_registry}/digitalman-multisaas/${module_name}:${git_version}-${build_env} """ } } diff --git a/Jenkinsfile1 b/Jenkinsfile1 new file mode 100644 index 00000000..b6f9d2ce --- /dev/null +++ b/Jenkinsfile1 @@ -0,0 +1,67 @@ +node { + def workspace=pwd() + // 版本 + def tag = "0.8" + def ali_registry = "registry.cn-beijing.aliyuncs.com" + // 镜像仓库的地址 + // def harbor_url = "192.168.81.102:85" + // 镜像仓库的项目,这里建议项目名称和jenkins的item项目名称、以及harbor的项目名称保持一致,否则用一下脚本会出问题 + // def harbor_project = "demo" + def mavenPath="/usr/share/maven" + // 拉取代码 + stage('pull code') { + checkout([$class: 'GitSCM', branches: [[name: '*/${branch}']], extensions: [], userRemoteConfigs: [[credentialsId: '0f6d6eaa8754e735262afa495fe2828d611fca17', url: 'http://39.105.23.186:3000/develop/digimeta-MultiSaas.git']]]) + } + // 代码静态检查 + stage('Maven Package and Sonar') { + if ("${project_name}" == 'digimeta-MultiSaas' ) { + echo '打包根目录' + sh 'mvn clean package sonar:sonar' + } else { + echo "打包子目录并进行代码检查 ${project_name}" + sh "mvn clean install" + sh "mvn -f ${project_name} clean package -e -U -Dmaven.test.skip=true sonar:sonar" + } + + //script { + //引入Jenkins SonarQube-Scanner全局工具 "全局配置中以SonarQube-Scanner命名的工具" + // scannerHome = tool 'SonarQube-Scanner' + //} + //引用SonarQube环境 "系统配置中配置的SonarQube servers的name值 " + //withSonarQubeEnv('Sonar') { + //执行sonar-scanner命令 + //sh "${scannerHome}/bin/sonar-scanner" + // $mavenPath/bin/mvn sonar:sonar + //} + } + // build Docker并推送镜像仓库 + stage('build project') { + if ("${project_name}" == 'digimeta-MultiSaas' ) { + echo '仅做代码检查,不打包目录' + } else { + echo "构件微服务 ${project_name},并推送到镜像仓库" + sh """ + cd ${workspace}/${project_name} + docker login --username=缔智元2023 ${ali_registry} + docker build --tag ${ali_registry}/digitalman-multisaas/${project_name}:${version} + docker push ${ali_registry}/digitalman-multisaas/${project_name}:${version} + """ + + } + //echo "把jar上传镜像仓库" + //def oldImageName = "${project_name}:latest" + //def newImageName = "${harbor_url}/${harbor_project}/${project_name}:${tag}" + // 改名称 做规范 + //sh "docker tag ${oldImageName} ${newImageName}" + // 删除之前的 镜像 + //sh "docker rmi ${oldImageName}" + // 推送到 dockers仓库 + //withCredentials([usernamePassword(credentialsId: '8a3d7ab1-4cd6-482c-86c9-a12aa6404d98', passwordVariable: 'harbor_password', usernameVariable: 'harbor_account')]) { + // 登录 + //sh "docker login -u ${harbor_account} -p ${harbor_password} ${harbor_url}" + // 上传 + //sh "docker push ${newImageName}" + //echo "镜像推送成功" + //} + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 1547fdc4..c8d4432b 100644 --- a/pom.xml +++ b/pom.xml @@ -163,6 +163,11 @@ ${fastjson2.version} + + net.logstash.logback + logstash-logback-encoder + 7.4 + io.jsonwebtoken @@ -301,6 +306,12 @@ ${xueyi.version} + + com.xueyi + xueyi-api-nlt + ${xueyi.version} + + cn.hutool diff --git a/xueyi-api/pom.xml b/xueyi-api/pom.xml index d5cec5ed..43a410fd 100644 --- a/xueyi-api/pom.xml +++ b/xueyi-api/pom.xml @@ -14,6 +14,7 @@ xueyi-api-tenant xueyi-api-file xueyi-api-job + xueyi-api-nlt xueyi-api-modules-auth diff --git a/xueyi-api/xueyi-api-nlt/pom.xml b/xueyi-api/xueyi-api-nlt/pom.xml new file mode 100644 index 00000000..b42fb186 --- /dev/null +++ b/xueyi-api/xueyi-api-nlt/pom.xml @@ -0,0 +1,33 @@ + + + + com.xueyi + xueyi-api + 2.5.0 + + 4.0.0 + + xueyi-api-nlt + + + xueyi-api-nlt模型接入管理模块 + + + + + + + com.xueyi + xueyi-common-core + + + + + com.xueyi + xueyi-api-system + + + + \ No newline at end of file diff --git a/xueyi-api/xueyi-api-nlt/src/main/java/com/xueyi/nlt/api/netty/domain/vo/DmWebSocketMessageVo.java b/xueyi-api/xueyi-api-nlt/src/main/java/com/xueyi/nlt/api/netty/domain/vo/DmWebSocketMessageVo.java new file mode 100644 index 00000000..2ad20b60 --- /dev/null +++ b/xueyi-api/xueyi-api-nlt/src/main/java/com/xueyi/nlt/api/netty/domain/vo/DmWebSocketMessageVo.java @@ -0,0 +1,13 @@ +package com.xueyi.nlt.api.netty.domain.vo; + +import com.alibaba.fastjson2.JSONObject; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +public class DmWebSocketMessageVo { + String devId; + String skillCode; + JSONObject format; +} diff --git a/xueyi-api/xueyi-api-nlt/src/main/java/com/xueyi/nlt/api/netty/feign/RemoteWebsocketService.java b/xueyi-api/xueyi-api-nlt/src/main/java/com/xueyi/nlt/api/netty/feign/RemoteWebsocketService.java new file mode 100644 index 00000000..119df79a --- /dev/null +++ b/xueyi-api/xueyi-api-nlt/src/main/java/com/xueyi/nlt/api/netty/feign/RemoteWebsocketService.java @@ -0,0 +1,20 @@ +package com.xueyi.nlt.api.netty.feign; + +import com.alibaba.fastjson2.JSONObject; +import com.xueyi.common.core.constant.basic.SecurityConstants; +import com.xueyi.common.core.constant.basic.ServiceConstants; +import com.xueyi.common.core.web.result.R; +import com.xueyi.nlt.api.netty.domain.vo.DmWebSocketMessageVo; +import com.xueyi.nlt.api.nlt.domain.vo.DmIntentVo; +import com.xueyi.nlt.api.nlt.feign.factory.RemoteIntentFallbackFactory; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; + +@FeignClient(contextId = "remoteWebsocketService", value = ServiceConstants.NLT_SERVICE, fallbackFactory = RemoteIntentFallbackFactory.class) +public interface RemoteWebsocketService { + + @PostMapping("websocket/inner/sendMessage") + R sendMessage(@RequestBody DmWebSocketMessageVo message, @RequestHeader(SecurityConstants.ENTERPRISE_ID) Long enterpriseId, @RequestHeader(SecurityConstants.SOURCE_NAME) String sourceName, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); +} diff --git a/xueyi-api/xueyi-api-nlt/src/main/java/com/xueyi/nlt/api/nlt/domain/vo/DmIntentVo.java b/xueyi-api/xueyi-api-nlt/src/main/java/com/xueyi/nlt/api/nlt/domain/vo/DmIntentVo.java new file mode 100644 index 00000000..6f75e538 --- /dev/null +++ b/xueyi-api/xueyi-api-nlt/src/main/java/com/xueyi/nlt/api/nlt/domain/vo/DmIntentVo.java @@ -0,0 +1,16 @@ +package com.xueyi.nlt.api.nlt.domain.vo; + +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +public class DmIntentVo { + String devId; + Long operator; + String skillCode; + String content; + String preIntent; + String sign; + String requestId; +} diff --git a/xueyi-api/xueyi-api-nlt/src/main/java/com/xueyi/nlt/api/nlt/domain/vo/DmRecognitionVo.java b/xueyi-api/xueyi-api-nlt/src/main/java/com/xueyi/nlt/api/nlt/domain/vo/DmRecognitionVo.java new file mode 100644 index 00000000..39098bc9 --- /dev/null +++ b/xueyi-api/xueyi-api-nlt/src/main/java/com/xueyi/nlt/api/nlt/domain/vo/DmRecognitionVo.java @@ -0,0 +1,13 @@ +package com.xueyi.nlt.api.nlt.domain.vo; + +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +public class DmRecognitionVo { + String devId; + Long personId; + String registered; + String sign; +} diff --git a/xueyi-api/xueyi-api-nlt/src/main/java/com/xueyi/nlt/api/nlt/feign/RemoteIntentService.java b/xueyi-api/xueyi-api-nlt/src/main/java/com/xueyi/nlt/api/nlt/feign/RemoteIntentService.java new file mode 100644 index 00000000..fbb95712 --- /dev/null +++ b/xueyi-api/xueyi-api-nlt/src/main/java/com/xueyi/nlt/api/nlt/feign/RemoteIntentService.java @@ -0,0 +1,24 @@ +package com.xueyi.nlt.api.nlt.feign; + +import com.alibaba.fastjson2.JSONObject; +import com.xueyi.common.core.constant.basic.SecurityConstants; +import com.xueyi.common.core.constant.basic.ServiceConstants; +import com.xueyi.common.core.web.result.R; +import com.xueyi.nlt.api.netty.domain.vo.DmWebSocketMessageVo; +import com.xueyi.nlt.api.nlt.domain.vo.DmIntentVo; +import com.xueyi.nlt.api.nlt.feign.factory.RemoteIntentFallbackFactory; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.ResponseBody; + +@FeignClient(contextId = "remoteIntentService", value = ServiceConstants.NLT_SERVICE, fallbackFactory = RemoteIntentFallbackFactory.class) +public interface RemoteIntentService { + + @PostMapping("/intent/inner/conversation") + R conversationInner(@RequestBody DmIntentVo intent, @RequestHeader(SecurityConstants.ENTERPRISE_ID) Long enterpriseId, @RequestHeader(SecurityConstants.SOURCE_NAME) String sourceName, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); + + @PostMapping("/intent/inner/sendMessage") + R sendMessage(@RequestBody DmWebSocketMessageVo message, @RequestHeader(SecurityConstants.ENTERPRISE_ID) Long enterpriseId, @RequestHeader(SecurityConstants.SOURCE_NAME) String sourceName, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); +} diff --git a/xueyi-api/xueyi-api-nlt/src/main/java/com/xueyi/nlt/api/nlt/feign/factory/RemoteIntentFallbackFactory.java b/xueyi-api/xueyi-api-nlt/src/main/java/com/xueyi/nlt/api/nlt/feign/factory/RemoteIntentFallbackFactory.java new file mode 100644 index 00000000..a48cb1f9 --- /dev/null +++ b/xueyi-api/xueyi-api-nlt/src/main/java/com/xueyi/nlt/api/nlt/feign/factory/RemoteIntentFallbackFactory.java @@ -0,0 +1,15 @@ +package com.xueyi.nlt.api.nlt.feign.factory; + +import com.xueyi.nlt.api.nlt.feign.RemoteIntentService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cloud.openfeign.FallbackFactory; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +public class RemoteIntentFallbackFactory implements FallbackFactory { + @Override + public RemoteIntentService create(Throwable cause) { + return null; + } +} diff --git a/xueyi-api/xueyi-api-nlt/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/xueyi-api/xueyi-api-nlt/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..fcd3ba65 --- /dev/null +++ b/xueyi-api/xueyi-api-nlt/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.xueyi.nlt.api.nlt.feign.factory.RemoteIntentFallbackFactory diff --git a/xueyi-api/xueyi-api-system/src/main/java/com/xueyi/system/api/meeting/feign/RemoteMeetingService.java b/xueyi-api/xueyi-api-system/src/main/java/com/xueyi/system/api/meeting/feign/RemoteMeetingService.java index a5653282..110630ad 100644 --- a/xueyi-api/xueyi-api-system/src/main/java/com/xueyi/system/api/meeting/feign/RemoteMeetingService.java +++ b/xueyi-api/xueyi-api-system/src/main/java/com/xueyi/system/api/meeting/feign/RemoteMeetingService.java @@ -56,4 +56,7 @@ public interface RemoteMeetingService { @GetMapping("/meeting/inner-api/recent/{deptId}/{dateStr}") @ResponseBody public List recent(@PathVariable(value = "deptId") Long deptId, @PathVariable(value = "dateStr") String dateStr, @RequestParam(value = "roomId", required = false) Long roomId,@RequestParam(value = "startTime", required = false) String startTime, @RequestHeader(SecurityConstants.ENTERPRISE_ID) Long enterpriseId, @RequestHeader(SecurityConstants.SOURCE_NAME) String sourceName, @RequestHeader(SecurityConstants.FROM_SOURCE) String source) ; + + @GetMapping(value = "/meeting/api/recent/{devId}/{dateStr}") + R> ableOrderList(@PathVariable(value = "devId") String devId, @PathVariable(value = "dateStr") String dateStr, @RequestParam(value = "roomId", required = false) Long roomId, @RequestParam(value = "startTime", required = false) String startTime); } \ No newline at end of file diff --git a/xueyi-api/xueyi-api-system/src/main/java/com/xueyi/system/api/meeting/feign/factory/RemoteMeetingFallbackFactory.java b/xueyi-api/xueyi-api-system/src/main/java/com/xueyi/system/api/meeting/feign/factory/RemoteMeetingFallbackFactory.java index 580a016f..bdd75b76 100644 --- a/xueyi-api/xueyi-api-system/src/main/java/com/xueyi/system/api/meeting/feign/factory/RemoteMeetingFallbackFactory.java +++ b/xueyi-api/xueyi-api-system/src/main/java/com/xueyi/system/api/meeting/feign/factory/RemoteMeetingFallbackFactory.java @@ -71,6 +71,11 @@ public class RemoteMeetingFallbackFactory implements FallbackFactory recent(Long deptId, String dateStr, Long roomId, String startTime, Long enterpriseId, String sourceName, String source) { return null; } + + @Override + public R> ableOrderList(String devId, String dateStr, Long roomId, String startTime) { + return null; + } }; } } \ No newline at end of file diff --git a/xueyi-common/xueyi-common-core/src/main/java/com/xueyi/common/core/constant/basic/ServiceConstants.java b/xueyi-common/xueyi-common-core/src/main/java/com/xueyi/common/core/constant/basic/ServiceConstants.java index 493e3e04..3c4ef333 100644 --- a/xueyi-common/xueyi-common-core/src/main/java/com/xueyi/common/core/constant/basic/ServiceConstants.java +++ b/xueyi-common/xueyi-common-core/src/main/java/com/xueyi/common/core/constant/basic/ServiceConstants.java @@ -26,6 +26,7 @@ public class ServiceConstants { public static final String MESSAGE_SERVICE = "xueyi-message"; public static final String MODULES_AUTH_SERVICE = "xueyi-modules-auth"; + public static final String NLT_SERVICE = "xueyi-nlt"; /** 定时任务模块的serviceId */ public static final String JOB_SERVICE = "xueyi-job"; diff --git a/xueyi-common/xueyi-common-mqtt/src/main/java/com/xueyi/common/mqtt/constant/MqttTopicConstant.java b/xueyi-common/xueyi-common-mqtt/src/main/java/com/xueyi/common/mqtt/constant/MqttTopicConstant.java new file mode 100644 index 00000000..d8d1551a --- /dev/null +++ b/xueyi-common/xueyi-common-mqtt/src/main/java/com/xueyi/common/mqtt/constant/MqttTopicConstant.java @@ -0,0 +1,15 @@ +package com.xueyi.common.mqtt.constant; + +public class MqttTopicConstant { + + /** + * 数字人常量 + */ + public static final String TOPIC_DIGITALMAN = "/digitalman"; + /** + * 通知重启 + */ + public static final String TOPIC_NOTIFY_RESTART = "notify/restart"; + + +} diff --git a/xueyi-modules/pom.xml b/xueyi-modules/pom.xml index 5f292c54..7610974a 100644 --- a/xueyi-modules/pom.xml +++ b/xueyi-modules/pom.xml @@ -16,6 +16,7 @@ xueyi-file xueyi-message xueyi-modules-auth + xueyi-nlt xueyi-modules diff --git a/xueyi-modules/xueyi-nlt/Dockerfile b/xueyi-modules/xueyi-nlt/Dockerfile new file mode 100644 index 00000000..a72da462 --- /dev/null +++ b/xueyi-modules/xueyi-nlt/Dockerfile @@ -0,0 +1,17 @@ +# 基础镜像 +FROM openjdk:17-oracle +# author +MAINTAINER xueyi + +# 挂载目录 +VOLUME /home/xueyi +# 创建目录 +RUN mkdir -p /home/xueyi +RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime +# 指定路径 +WORKDIR /home/xueyi +# 复制jar文件到路径 +COPY ./target/xueyi-modules-nlt.jar /home/xueyi/xueyi-modules-nlt.jar + +# 启动系统服务 +ENTRYPOINT ["java","-jar","xueyi-modules-nlt.jar"] \ No newline at end of file diff --git a/xueyi-modules/xueyi-nlt/pom.xml b/xueyi-modules/xueyi-nlt/pom.xml new file mode 100644 index 00000000..bdac8af7 --- /dev/null +++ b/xueyi-modules/xueyi-nlt/pom.xml @@ -0,0 +1,117 @@ + + + + com.xueyi + xueyi-modules + 2.5.0 + + 4.0.0 + + xueyi-modules-nlt + + + xueyi-modules-nlt大模型处理模块 + + + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + com.squareup.okhttp3 + okhttp + 3.9.1 + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + + + + + + com.xueyi + xueyi-common-log + + + + + com.xueyi + xueyi-common-web + + + + + com.xueyi + xueyi-common-swagger + + + + + com.xueyi + xueyi-api-nlt + + + + com.xueyi + xueyi-api-system + + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + maven-resources-plugin + org.apache.maven.plugins + + @ + false + + + + + + src/main/resources + true + + + + diff --git a/xueyi-modules/xueyi-nlt/sonar-project.properties b/xueyi-modules/xueyi-nlt/sonar-project.properties new file mode 100644 index 00000000..d554b7c0 --- /dev/null +++ b/xueyi-modules/xueyi-nlt/sonar-project.properties @@ -0,0 +1,15 @@ +sonar.projectKey=digimeta-MultiSaas-nlt + +sonar.projectName=digimeta-MultiSaas-nlt +sonar.sourceEncoding=UTF-8 +sonar.projectVersion=0.8 + + + +sonar.sources=. +sonar.exclusions=**/test/**,**/target/** +sonar.java.binaries=. + +sonar.java.source=1.8 +sonar.java.target=1.8 + diff --git a/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/XueYiNltApplication.java b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/XueYiNltApplication.java new file mode 100644 index 00000000..146b79fa --- /dev/null +++ b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/XueYiNltApplication.java @@ -0,0 +1,29 @@ +package com.xueyi.nlt; + + +import com.xueyi.common.security.annotation.EnableCustomConfig; +import com.xueyi.common.security.annotation.EnableRyFeignClients; +import com.xueyi.common.swagger.annotation.EnableCustomSwagger; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@EnableCustomConfig +@EnableCustomSwagger +@EnableRyFeignClients +@SpringBootApplication +public class XueYiNltApplication { + public static void main(String[] args) { + SpringApplication.run(XueYiNltApplication.class, args); + System.out.println("(♥◠‿◠)ノ゙ NLT模块启动成功 ლ(´ڡ`ლ)゙ \n" + + " _____ __ ____ __ \n" + + " \\ _\\ / / \\ \\ / / \n" + + " .-./ ). / ' \\ _. / ' \n" + + " \\ '_ .') .' _( )_ .' \n" + + " (_ (_) _) ' ___(_ o _)' \n" + + " / \\ \\ | |(_,_)' \n" + + " `-'`-' \\| `-' / \n" + + " / / \\ \\\\ / \n" + + " '--' '----'`-..-' "); + } + +} diff --git a/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/client/NettyClient.java b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/client/NettyClient.java new file mode 100644 index 00000000..82620267 --- /dev/null +++ b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/client/NettyClient.java @@ -0,0 +1,195 @@ +package com.xueyi.nlt.netty.client; + +import com.xueyi.nlt.netty.client.codec.WsChannelInitializer; +import com.xueyi.nlt.netty.client.handler.MockClientHandler; +import com.xueyi.nlt.netty.client.handler.NettyClientHandler; +import com.xueyi.nlt.netty.client.handler.NettyWebsocketClientHandler; +import com.xueyi.nlt.netty.client.message.ReceiveMessage; +import io.netty.bootstrap.Bootstrap; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.*; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.codec.http.DefaultHttpHeaders; +import io.netty.handler.codec.http.HttpClientCodec; +import io.netty.handler.codec.http.HttpObjectAggregator; +import io.netty.handler.codec.http.HttpServerCodec; +import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory; +import io.netty.handler.codec.http.websocketx.WebSocketVersion; +import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketClientCompressionHandler; +import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.SslContextBuilder; +import io.netty.handler.ssl.util.InsecureTrustManagerFactory; +import io.netty.handler.timeout.IdleStateHandler; +import okhttp3.HttpUrl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import java.net.SocketAddress; +import java.net.URI; +import java.net.URL; +import java.nio.charset.Charset; +import java.text.SimpleDateFormat; +import java.util.Base64; +import java.util.Date; +import java.util.Locale; +import java.util.TimeZone; + +@Component +public class NettyClient { + private static final Logger log = LoggerFactory.getLogger(NettyClient.class); + private final int PORT = 8000; + + + public static String hostUrl = "https://spark-api.xf-yun.com/v1.1/chat"; + public static String APPID = "3d9282da";//从开放平台控制台中获取 + public static String APIKEY = "7c217b3a313f4b66fcc14a8e97f85103";//从开放平台控制台中获取 + public static String APISecret = "ZTRiNDQwMTRlOTlmZDQwMDUwYTdjMDM0";//从开放平台控制台中获取 + public static NettyClient nettyClient; + + private String mHost; + + private int mPort; + + private NettyClientHandler mClientHandler; + + private ChannelFuture mChannelFuture; + + /** + * websocket配置 + */ + private WebsocketConfig websocketConfig; + + private Channel channel; + + /** + * 接口消息的接口 + */ + private ReceiveMessage receiveMessage; + + + public void connect() { + + + EventLoopGroup workerGroup = new NioEventLoopGroup(); + try { + HttpUrl authUrl = getAuthorizationUrl(hostUrl, APIKEY, APISecret); + String url = authUrl.toString().replace("https://","wss://").replace("http://","ws://"); + URI uri = new URI(url); + final boolean ssl = "wss".equalsIgnoreCase(url); + final SslContext sslCtx; + if (ssl) { + sslCtx = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build(); + } else { + sslCtx = null; + } + NettyClientHandler webSocketClientHandler = new NettyClientHandler( + WebSocketClientHandshakerFactory.newHandshaker(authUrl.uri() + , WebSocketVersion.V07 + , null + , false + , new DefaultHttpHeaders())); + + final NettyWebsocketClientHandler handler = + new NettyWebsocketClientHandler( + WebSocketClientHandshakerFactory.newHandshaker( + authUrl.uri(), WebSocketVersion.V13, null, true, new DefaultHttpHeaders()), receiveMessage); + + handler.setWebsocketConfig(websocketConfig); + + Bootstrap b = new Bootstrap(); +// mClientHandler = new NettyClientHandler(webSocketClientHandler); +// b.group(workerGroup).channel(NioSocketChannel.class) +// // KeepAlive +// .option(ChannelOption.SO_KEEPALIVE, true) +// // Handler +// .handler(new WsChannelInitializer(webSocketClientHandler)); + + + b.group(workerGroup) + .channel(NioSocketChannel.class) + .handler(new ChannelInitializer() { + @Override + protected void initChannel(SocketChannel ch) { + ChannelPipeline pipeline = ch.pipeline(); + //wss 连接 + if (sslCtx != null) { + pipeline.addLast(sslCtx.newHandler(ch.alloc(), authUrl.host(), authUrl.port())); + } + pipeline.addLast( + new HttpServerCodec(), + new HttpObjectAggregator(65536), + WebSocketClientCompressionHandler.INSTANCE, + //new LoggingHandler(LogLevel.INFO), // only for debug +// new IdleStateHandler(websocketConfig.getReaderIdleTimeSeconds(), +// websocketConfig.getWriterIdleTimeSeconds(), +// websocketConfig.getAllIdleTimeSeconds()), + handler); + } + }).option(ChannelOption.SO_KEEPALIVE, true) + + ; + +// mChannelFuture = b.connect(authUrl.host(),authUrl.port()).sync(); + mChannelFuture = b.connect("spark-api.xf-yun.com",443).sync(); + channel = mChannelFuture.channel(); + handler.handshakeFuture().sync(); + if (mChannelFuture.isSuccess()) { + log.info("Client,连接服务端成功"); + } + channel.writeAndFlush("介绍下自己"); +// mChannelFuture.channel().closeFuture().sync(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + workerGroup.shutdownGracefully(); + } + } + + //鉴权url + public static HttpUrl getAuthorizationUrl(String hostUrl , String apikey ,String apisecret) throws Exception { + //获取host + URL url = new URL(hostUrl); + //获取鉴权时间 date + SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US); + System.out.println("format:\n" + format ); + format.setTimeZone(TimeZone.getTimeZone("GMT")); + String date = format.format(new Date()); + //获取signature_origin字段 + StringBuilder builder = new StringBuilder("host: ").append(url.getHost()).append("\n"). +// append("date: ").append(date).append("\n"). + append("date: ").append("Wed, 02 Aug 2023 05:35:33 GMT").append("\n"). + append("GET ").append(url.getPath()).append(" HTTP/1.1"); + System.out.println("signature_origin:\n" + builder); + //获得signatue + Charset charset = Charset.forName("UTF-8"); + Mac mac = Mac.getInstance("hmacsha256"); + SecretKeySpec sp = new SecretKeySpec(apisecret.getBytes(charset),"hmacsha256"); + mac.init(sp); + byte[] basebefore = mac.doFinal(builder.toString().getBytes(charset)); + String signature = Base64.getEncoder().encodeToString(basebefore); + //获得 authorization_origin + String authorization_origin = String.format("api_key=\"%s\",algorithm=\"%s\",headers=\"%s\",signature=\"%s\"",apikey,"hmac-sha256","host date request-line",signature); + //获得authorization + String authorization = Base64.getEncoder().encodeToString(authorization_origin.getBytes(charset)); + //获取httpurl + HttpUrl httpUrl = HttpUrl.parse("https://" + url.getHost() + url.getPath()).newBuilder().// + addQueryParameter("authorization", authorization).// + addQueryParameter("date", date).// + addQueryParameter("host", url.getHost()).// + build(); + System.out.println("httpUrl:\n" + httpUrl); + return httpUrl; + } + + public static void main(String[] args) { + nettyClient = new NettyClient(); + nettyClient.connect(); + + } +} diff --git a/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/client/WebSocketClient.java b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/client/WebSocketClient.java new file mode 100644 index 00000000..05fc808a --- /dev/null +++ b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/client/WebSocketClient.java @@ -0,0 +1,313 @@ +package com.xueyi.nlt.netty.client; + +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.nacos.shaded.com.google.gson.Gson; +import com.alibaba.nacos.shaded.com.google.gson.JsonArray; +import com.alibaba.nacos.shaded.com.google.gson.JsonObject; +import com.xueyi.nlt.api.netty.domain.vo.DmWebSocketMessageVo; +import okhttp3.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import java.net.URL; +import java.nio.charset.Charset; +import java.text.SimpleDateFormat; +import java.util.Base64; +import java.util.Date; +import java.util.Locale; +import java.util.TimeZone; + +@Component +public class WebSocketClient extends WebSocketListener { + + public static WebSocketClient INSTANCE; + @Autowired + private RedisTemplate redisTemplate; + @Autowired + private StringRedisTemplate stringRedisTemplate; + + public final static Object LOCK = new Object(); + public static String hostUrl = "https://spark-api.xf-yun.com/v1.1/chat"; + public static String APPID = "3d9282da";//从开放平台控制台中获取 + public static String APIKEY = "7c217b3a313f4b66fcc14a8e97f85103";//从开放平台控制台中获取 + public static String APISecret = "ZTRiNDQwMTRlOTlmZDQwMDUwYTdjMDM0";//从开放平台控制台中获取 + + public static WebSocket webSocket; +// public static String APPID = "948cf4b6";//从开放平台控制台中获取 +// public static String APIKEY = "54f6e81f40a31d66d976496de895a7a4";//从开放平台控制台中获取 +// public static String APISecret = "ZDYyMjNmMTlkYTE0YWRmOWUwZTYxNjYz";//从开放平台控制台中获取 + public static final Gson json = new Gson(); +// public static String question = "假设你是一位前台,你需要通过与其他人对话来获取会议相关信息,已知今天是2023-7-19,你需要获取会议日期,开始时间,持续时间,会议地点,会议主题。时间用类似00:00的格式输出。对方的话中可能不包含全部信息,对于未知的信息填充为none。如果所有信息都已知那么commit为true。否则为false。将你获得的信息输出为json格式。对方的话是:“明天下午开个会。从两点开到下午三点,在大会议室开,主题是访客接待”,只输出最后的json。输出只有一行,输出格式为{date:,start_time:,duration:,location:,theme:commit:}。";//可以修改question 内容,来向模型提问 + public static String question = "请帮我安排五一出行计划";//可以修改question 内容,来向模型提问 + public String answer = ""; + + @PostConstruct + public void init() { + INSTANCE = this; + INSTANCE.redisTemplate = this.redisTemplate; + INSTANCE.stringRedisTemplate = this.stringRedisTemplate; + } + + public static void main(String[] args) { + synchronized (LOCK) { + try { + //构建鉴权httpurl + String authUrl = getAuthorizationUrl(hostUrl,APIKEY,APISecret); + OkHttpClient okHttpClient = new OkHttpClient.Builder().build(); + String url = authUrl.replace("https://","wss://").replace("http://","ws://"); + Request request = new Request.Builder().url(url).build(); + WebSocket webSocket = okHttpClient.newWebSocket(request,new WebSocketClient()); + LOCK.wait(); + System.out.println("查询完成"); + } catch (Exception e) { + e.printStackTrace(); + } + } + + + + // write your code here + + } + + public void sendMsg(String message){ + question = message; + + try { + //构建鉴权httpurl + String authUrl = getAuthorizationUrl(hostUrl,APIKEY,APISecret); + OkHttpClient okHttpClient = new OkHttpClient.Builder().build(); + String url = authUrl.replace("https://","wss://").replace("http://","ws://"); + Request request = new Request.Builder().url(url).build(); + webSocket = okHttpClient.newWebSocket(request,new WebSocketClient()); + + } catch (Exception e) { + e.printStackTrace(); + } + } + //鉴权url + public static String getAuthorizationUrl(String hostUrl , String apikey ,String apisecret) throws Exception { + //获取host + URL url = new URL(hostUrl); + //获取鉴权时间 date + SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US); + System.out.println("format:\n" + format ); + format.setTimeZone(TimeZone.getTimeZone("GMT")); + String date = format.format(new Date()); + //获取signature_origin字段 + StringBuilder builder = new StringBuilder("host: ").append(url.getHost()).append("\n"). + append("date: ").append(date).append("\n"). + append("GET ").append(url.getPath()).append(" HTTP/1.1"); + System.out.println("signature_origin:\n" + builder); + //获得signatue + Charset charset = Charset.forName("UTF-8"); + Mac mac = Mac.getInstance("hmacsha256"); + SecretKeySpec sp = new SecretKeySpec(apisecret.getBytes(charset),"hmacsha256"); + mac.init(sp); + byte[] basebefore = mac.doFinal(builder.toString().getBytes(charset)); + String signature = Base64.getEncoder().encodeToString(basebefore); + //获得 authorization_origin + String authorization_origin = String.format("api_key=\"%s\",algorithm=\"%s\",headers=\"%s\",signature=\"%s\"",apikey,"hmac-sha256","host date request-line",signature); + //获得authorization + String authorization = Base64.getEncoder().encodeToString(authorization_origin.getBytes(charset)); + //获取httpurl + HttpUrl httpUrl = HttpUrl.parse("https://" + url.getHost() + url.getPath()).newBuilder().// + addQueryParameter("authorization", authorization).// + addQueryParameter("date", date).// + addQueryParameter("host", url.getHost()).// + build(); + + return httpUrl.toString(); + } + + //重写onopen + @Override + public void onOpen(WebSocket webSocket, Response response) { + super.onOpen(webSocket, response); + new Thread(()->{ + JsonObject frame = new JsonObject(); + JsonObject header = new JsonObject(); + JsonObject chat = new JsonObject(); + JsonObject parameter = new JsonObject(); + JsonObject payload = new JsonObject(); + JsonObject message = new JsonObject(); + JsonObject text = new JsonObject(); + JsonArray ja = new JsonArray(); + + //填充header + header.addProperty("app_id",APPID); + header.addProperty("uid","123456789"); + //填充parameter + chat.addProperty("domain","general"); + chat.addProperty("random_threshold",0); + chat.addProperty("max_tokens",1024); + chat.addProperty("auditing","default"); + parameter.add("chat",chat); + //填充payload + text.addProperty("role","user"); + text.addProperty("content",question); + ja.add(text); +// message.addProperty("text",ja.getAsString()); + message.add("text",ja); + payload.add("message",message); + frame.add("header",header); + frame.add("parameter",parameter); + frame.add("payload",payload); + System.out.println("frame:\n" + frame.toString()); + webSocket.send(frame.toString()); + + + } + + + ).start(); + } + + //重写onmessage + + @Override + public void onMessage(WebSocket webSocket, String text) { + super.onMessage(webSocket, text); + System.out.println("text:\n" + text); + ResponseData responseData = json.fromJson(text,ResponseData.class); + synchronized (LOCK) { + try { + // System.out.println("code:\n" + responseData.getHeader().get("code")); + if (0 == responseData.getHeader().get("code").getAsInt()) { + System.out.println("###########"); + System.out.println("getStatus: " + responseData.getHeader().get("status").getAsInt()); + if (2 != responseData.getHeader().get("status").getAsInt()) { + System.out.println("****************"); + Payload pl = json.fromJson(responseData.getPayload(), Payload.class); + JsonArray temp = (JsonArray) pl.getChoices().get("text"); + JsonObject jo = (JsonObject) temp.get(0); + answer += jo.get("content").getAsString(); +// System.out.println(answer); + } else { + Payload pl1 = json.fromJson(responseData.getPayload(), Payload.class); + JsonObject jsonObject = (JsonObject) pl1.getUsage().get("text"); + int prompt_tokens = jsonObject.get("prompt_tokens").getAsInt(); + JsonArray temp1 = (JsonArray) pl1.getChoices().get("text"); + JsonObject jo = (JsonObject) temp1.get(0); + answer += jo.get("content").getAsString(); + System.out.println("返回结果为:\n" + answer); + + if (INSTANCE.redisTemplate.hasKey("gpt:websocket:1")) { + DmWebSocketMessageVo message = (DmWebSocketMessageVo) INSTANCE.redisTemplate.opsForValue().get("gpt:websocket:1"); + JSONObject preWebsocketJo = message.getFormat(); + JSONObject meetingJo = new JSONObject(); + meetingJo.put("timestamp",preWebsocketJo.get("timestamp")); + meetingJo.put("content",answer); + INSTANCE.stringRedisTemplate.opsForHash().put("group:nlp" + ":" + preWebsocketJo.getString("orderId"), "meeting", meetingJo.toString()); + INSTANCE.redisTemplate.delete("gpt:websocket:1"); + + }else { + // 添加缓存 + INSTANCE.stringRedisTemplate.opsForValue().set("group:websocket:content", answer); + LOCK.notifyAll(); + } + +// webSocket.close(3,"客户端主动断开链接"); + //webSocket.close(1000,"客户端主动断开链接"); + + } + + } else { + System.out.println("返回结果错误:\n" + responseData.getHeader().get("code") + responseData.getHeader().get("message")); + LOCK.notifyAll(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + + //重写onFailure + + @Override + public void onFailure(WebSocket webSocket, Throwable t, Response response) { + super.onFailure(webSocket, t, response); + System.out.println(response); + } + + + + class ResponseData{ + private JsonObject header; + private JsonObject payload; + + public JsonObject getHeader() { + return header; + } + + public JsonObject getPayload() { + return payload; + } + } + + class Header{ + private int code ; + private String message; + private String sid; + private String status; + + public int getCode() { + return code; + } + + public String getMessage() { + return message; + } + + public String getSid() { + return sid; + } + + public String getStatus() { + return status; + } + } + + class Payload{ + private JsonObject choices; + private JsonObject usage; + + public JsonObject getChoices() { + return choices; + } + + public JsonObject getUsage() { + return usage; + } + } + + class Choices{ + private int status; + private int seq; + private JsonArray text; + + public int getStatus() { + return status; + } + + public int getSeq() { + return seq; + } + + public JsonArray getText() { + return text; + } + } + + + + + + +} diff --git a/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/client/WebsocketConfig.java b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/client/WebsocketConfig.java new file mode 100644 index 00000000..6a10962b --- /dev/null +++ b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/client/WebsocketConfig.java @@ -0,0 +1,202 @@ +package com.xueyi.nlt.netty.client; + +import java.util.Map; + +public class WebsocketConfig { + + /** + * websocket url-------------(如果存在则以url为主) + */ + private String url; + + + /** + * websocket前缀 + */ + private String scheme = "ws"; + + /** + * 服务器IP + */ + private String host; + + /** + * 服务器端口 + */ + private int port; + + /** + * 默认的websocket地址 + */ + private String path = "websocket"; + + /** + * url参数 + */ + private Map suffixParams; + + + // Optional settings below + /** + * 检查 + */ + private Long checkLiveDuration; + + /** + * 自动重启客户端 + */ + private Boolean autoRebootClient; + + /** + * 保持连接状态 + */ + private Boolean keepAlive; + + /** + * 读空闲超时时间 + */ + private Integer readerIdleTimeSeconds = 60; + + /** + * 写空闲超时时间 + */ + private Integer writerIdleTimeSeconds = 60; + + /** + * 所有空闲超时时间 + */ + private Integer allIdleTimeSeconds = 0; + + /** + * 心跳实现 + */ +// private HeartBeat heartBeat; + + public WebsocketConfig(){ + } + + public WebsocketConfig(String host, int port){ + this.host = host; + this.port = port; + } + + + public WebsocketConfig(String host, int port, String path){ + this.host = host; + this.port = port; + this.path = path; + } + + public WebsocketConfig(String scheme, String host, int port, String path){ + this.scheme = scheme; + this.host = host; + this.port = port; + this.path = path; + } + + + public String getScheme() { + return scheme; + } + + public void setScheme(String scheme) { + this.scheme = scheme; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public Map getSuffixParams() { + return suffixParams; + } + + public void setSuffixParams(Map suffixParams) { + this.suffixParams = suffixParams; + } + + public Long getCheckLiveDuration() { + return checkLiveDuration; + } + + public void setCheckLiveDuration(Long checkLiveDuration) { + this.checkLiveDuration = checkLiveDuration; + } + + public Boolean getAutoRebootClient() { + return autoRebootClient; + } + + public void setAutoRebootClient(Boolean autoRebootClient) { + this.autoRebootClient = autoRebootClient; + } + + + public Boolean getKeepAlive() { + return keepAlive; + } + + public void setKeepAlive(Boolean keepAlive) { + this.keepAlive = keepAlive; + } + + public Integer getReaderIdleTimeSeconds() { + return readerIdleTimeSeconds; + } + + public void setReaderIdleTimeSeconds(Integer readerIdleTimeSeconds) { + this.readerIdleTimeSeconds = readerIdleTimeSeconds; + } + + public Integer getWriterIdleTimeSeconds() { + return writerIdleTimeSeconds; + } + + public void setWriterIdleTimeSeconds(Integer writerIdleTimeSeconds) { + this.writerIdleTimeSeconds = writerIdleTimeSeconds; + } + + public Integer getAllIdleTimeSeconds() { + return allIdleTimeSeconds; + } + + public void setAllIdleTimeSeconds(Integer allIdleTimeSeconds) { + this.allIdleTimeSeconds = allIdleTimeSeconds; + } + +// public HeartBeat getHeartBeat() { +// return heartBeat; +// } + +// public void setHeartBeat(HeartBeat heartBeat) { +// this.heartBeat = heartBeat; +// } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } +} diff --git a/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/client/codec/WsChannelInitializer.java b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/client/codec/WsChannelInitializer.java new file mode 100644 index 00000000..d6f05655 --- /dev/null +++ b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/client/codec/WsChannelInitializer.java @@ -0,0 +1,46 @@ +package com.xueyi.nlt.netty.client.codec; + +import com.xueyi.nlt.netty.client.handler.MockClientHandler; +import com.xueyi.nlt.netty.client.handler.NettyClientHandler; +import com.xueyi.nlt.netty.client.handler.WsClientHandler; +import io.netty.channel.Channel; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.handler.codec.http.HttpClientCodec; +import io.netty.handler.codec.http.HttpObjectAggregator; +import io.netty.handler.codec.http.HttpServerCodec; +import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; +import io.netty.handler.codec.string.StringDecoder; +import io.netty.handler.codec.string.StringEncoder; +import io.netty.handler.logging.LogLevel; +import io.netty.handler.logging.LoggingHandler; +import io.netty.handler.stream.ChunkedWriteHandler; + +public class WsChannelInitializer extends ChannelInitializer { + + private NettyClientHandler nettyClientHandler; + + public WsChannelInitializer(NettyClientHandler nettyClientHandler) { + this.nettyClientHandler = nettyClientHandler; + } + + @Override + protected void initChannel(Channel ch) { + ChannelPipeline pipeline = ch.pipeline(); + // websocket是基于http协议的,所以需要使用http编解码器 + pipeline.addLast(new HttpClientCodec()); + pipeline.addLast(new LoggingHandler(LogLevel.INFO)); + pipeline.addLast("decoder", new StringDecoder()); + pipeline.addLast("encoder", new StringEncoder()); + // 以上三个处理器是对http协议的支持 + + // websocket 服务器处理的协议,并用于指定客户端连接的路由(这里指定的是 /ws) + // 这里的URL就是 ws://ip:port/ws + // 该处理器为运行websocket服务器承担了所有繁重的工作 + // 它会负责websocket的握手以及处理控制帧 + // websocket的数据传输都是以frames进行的 + pipeline.addLast(new WebSocketServerProtocolHandler("/wss")); + // 自定义的处理器 + pipeline.addLast("handler", nettyClientHandler); + } +} diff --git a/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/client/handler/MockClientHandler.java b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/client/handler/MockClientHandler.java new file mode 100644 index 00000000..4feba9eb --- /dev/null +++ b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/client/handler/MockClientHandler.java @@ -0,0 +1,66 @@ +package com.xueyi.nlt.netty.client.handler; + +import com.xueyi.nlt.netty.client.NettyClient; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Slf4j +public class MockClientHandler extends SimpleChannelInboundHandler { + + // private MsgHandleService msgHandleService; + private static final Logger log = LoggerFactory.getLogger(NettyClient.class); + private final WebSocketClientHandshaker webSocketClientHandshaker; + + public MockClientHandler(WebSocketClientHandshaker webSocketClientHandshaker) { + this.webSocketClientHandshaker = webSocketClientHandshaker; +// this.msgHandleService = SpringContextHolder.getBean(MsgHandleService.class); + } + + /** + * 当客户端主动链接服务端的链接后,调用此方法 + * + * @param channelHandlerContext ChannelHandlerContext + */ + @Override + public void channelActive(ChannelHandlerContext channelHandlerContext) { + log.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" + + "\t├ [Mock 建立连接]\n" + + "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓"); + + Channel channel = channelHandlerContext.channel(); + // 握手 + webSocketClientHandshaker.handshake(channel); + } + + + @Override + protected void channelRead0(ChannelHandlerContext channelHandlerContext, String data) { + log.info("接收到客户端的响应为:{}", data); + //自定义处理消息 + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + log.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" + + "\t├ [exception]: {}\n" + + "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓", cause.getMessage()); + ctx.close(); + } + + @Override + public void handlerRemoved(ChannelHandlerContext ctx) { + System.out.println("与服务器端断开连接"); + } + + @Override + public void channelReadComplete(ChannelHandlerContext channelHandlerContext) { + channelHandlerContext.flush(); + } +} + + diff --git a/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/client/handler/NettyClientHandler.java b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/client/handler/NettyClientHandler.java new file mode 100644 index 00000000..c64694fc --- /dev/null +++ b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/client/handler/NettyClientHandler.java @@ -0,0 +1,108 @@ +package com.xueyi.nlt.netty.client.handler; + +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.handler.codec.http.DefaultHttpResponse; +import io.netty.handler.codec.http.websocketx.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.charset.Charset; + +import static com.xueyi.nlt.netty.client.NettyClient.APPID; + +public class NettyClientHandler extends SimpleChannelInboundHandler { + + + + // 定义log + private static final Logger log = LoggerFactory.getLogger(NettyClientHandler.class); + + private final WebSocketClientHandshaker webSocketClientHandshaker; + + public NettyClientHandler(WebSocketClientHandshaker webSocketClientHandshaker) { + this.webSocketClientHandshaker = webSocketClientHandshaker; + } + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + + Channel channel = ctx.channel(); + // 握手 + webSocketClientHandshaker.handshake(channel); + + String question = "你是能帮我指定一个五一出行的计划么?"; + log.info("Client,channelActive"); + JSONObject frame = new JSONObject(); + JSONObject header = new JSONObject(); + JSONObject chat = new JSONObject(); + JSONObject parameter = new JSONObject(); + JSONObject payload = new JSONObject(); + JSONObject message = new JSONObject(); + JSONObject text = new JSONObject(); + JSONArray ja = new JSONArray(); + + //填充header + header.put("app_id",APPID); + header.put("uid","123456789"); + //填充parameter + chat.put("domain","general"); + chat.put("random_threshold",0); + chat.put("max_tokens",1024); + chat.put("auditing","default"); + parameter.put("chat",chat); + //填充payload + text.put("role","user"); + text.put("content",question); + ja.add(text); +// message.addProperty("text",ja.getAsString()); + message.put("text",ja); + payload.put("message",message); + frame.put("header",header); + frame.put("parameter",parameter); + frame.put("payload",payload); + System.out.println("frame:\n" + frame.toString()); + ByteBuf byteBuf = Unpooled.copiedBuffer(frame.toString(), Charset.forName("utf-8")); + ctx.writeAndFlush(frame.toString()); + } + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { +// log.info("Client,接收到服务端发来的消息:" + msg); +// ByteBuf buf = (ByteBuf) msg; +// byte[] buffer = new byte[buf.readableBytes()]; +// buf.readBytes(buffer); +// String message = new String(buffer, "utf-8"); +// log.info("Client,接收到服务端发来的消息:" + message); + + Channel ch = ctx.channel(); + DefaultHttpResponse response = (DefaultHttpResponse) msg; + log.info(response.toString()); + } + + @Override + protected void channelRead0(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame textWebSocketFrame) throws Exception { + // 获取客户端传输来的文本消息 + String text = textWebSocketFrame.text(); + // 这个是自定义的日志工具类,可见其它文章 + log.info("收到的文本消息:[{}]", text); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + log.info("Client,exceptionCaught"); + cause.printStackTrace(); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + log.info("Client,channelInactive"); + } + +} diff --git a/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/client/handler/NettyWebsocketClientHandler.java b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/client/handler/NettyWebsocketClientHandler.java new file mode 100644 index 00000000..56c25b51 --- /dev/null +++ b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/client/handler/NettyWebsocketClientHandler.java @@ -0,0 +1,196 @@ +package com.xueyi.nlt.netty.client.handler; + +import com.xueyi.nlt.netty.client.message.ReceiveMessage; +import com.xueyi.nlt.netty.client.WebsocketConfig; +import io.netty.buffer.ByteBuf; +import io.netty.channel.*; +import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.websocketx.*; +import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler.HandshakeComplete; +import io.netty.handler.timeout.IdleStateEvent; +import io.netty.util.CharsetUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; + +public class NettyWebsocketClientHandler extends SimpleChannelInboundHandler { + private Logger logger = LoggerFactory.getLogger(NettyWebsocketClientHandler.class); + + private WebsocketConfig websocketConfig; + + private final WebSocketClientHandshaker handshaker; + + private ChannelPromise handshakeFuture; + + private ReceiveMessage receiveMessage; + +// private HeartBeat heartBeat; + + public NettyWebsocketClientHandler(WebSocketClientHandshaker handshaker) { + this.handshaker = handshaker; + } + + public NettyWebsocketClientHandler(WebSocketClientHandshaker handshaker, ReceiveMessage receiveMessage) { + this.handshaker = handshaker; + this.receiveMessage = receiveMessage; + } + + public ChannelFuture handshakeFuture() { + return handshakeFuture; + } + + @Override + public void handlerAdded(ChannelHandlerContext ctx) { + handshakeFuture = ctx.newPromise(); + } + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception{ + super.channelActive(ctx); + handshaker.handshake(ctx.channel()); + logger.info("channelActive"); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception{ + super.channelInactive(ctx); + logger.info("WebSocket Client disconnected!"); + } + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + super.channelRead(ctx, msg); + } + + @Override + public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { + Channel ch = ctx.channel(); + if (!handshaker.isHandshakeComplete()) { + try { + handshaker.finishHandshake(ch, (FullHttpResponse) msg); + logger.info("WebSocket Client connected!"); + handshakeFuture.setSuccess(); + } catch (WebSocketHandshakeException e) { + logger.info("WebSocket Client failed to connect, " + e.getMessage()); + handshakeFuture.setFailure(e); + } + return; + } + + if (msg instanceof FullHttpResponse) { + FullHttpResponse response = (FullHttpResponse) msg; + throw new IllegalStateException( + "Unexpected FullHttpResponse (getStatus=" + response.getStatus() + + ", content=" + response.content().toString(CharsetUtil.UTF_8) + ')'); + } + + WebSocketFrame frame = (WebSocketFrame) msg; + if (frame instanceof TextWebSocketFrame) { + /** + * 我们主要是用TextWebSocketFrame + */ + TextWebSocketFrame textFrame = (TextWebSocketFrame) frame; + receiveMessage.onMessage(ch, textFrame.text()); + } else if (frame instanceof PongWebSocketFrame) { + logger.info("WebSocket Client received pong"); + receiveMessage.onMessage(ch, frame); + } else if (frame instanceof CloseWebSocketFrame) { + logger.info("WebSocket Client received close Frame"); + //执行后将关闭 + receiveMessage.onMessage(ch, frame); + //receive a closing frame to shutdown the event loop + ch.eventLoop().shutdownGracefully(); + ch.close().sync(); + }else if(frame instanceof BinaryWebSocketFrame){ + BinaryWebSocketFrame binaryFrame = (BinaryWebSocketFrame)msg; + ByteBuf buf = binaryFrame.content(); + if (buf.isReadable()){ + int availableBytesNumber = buf.readableBytes(); + byte[] receivedBytes = new byte[availableBytesNumber]; + buf.readBytes(receivedBytes); + receiveMessage.onMessage(ch, receivedBytes); + } + //buf.release(); + // byte [] bytes = receivedBytes; + } + } + + @Override + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { +// if (evt instanceof IdleStateEvent) { +// IdleStateEvent idleStateEvent = (IdleStateEvent) evt; +// switch (idleStateEvent.state()) { +// case WRITER_IDLE: +// handlerWriterIdleEvent(ctx); +// break; +// case READER_IDLE: +// handlerReaderIdleEvent(ctx); +// break; +// case ALL_IDLE: +// handlerAllIdleEvent(ctx); +// break; +// default: +// break; +// } +// } else { +// super.userEventTriggered(ctx, evt); +// } + if(evt instanceof HandshakeComplete) { + HandshakeComplete handshake = (WebSocketServerProtocolHandler.HandshakeComplete)evt; + //http request header + HttpHeaders headers = handshake.requestHeaders(); + + //http request uri: /chat?accesskey=hello + String uri = handshake.requestUri(); + + //TODO: parse uri parameters to map ... + Map params ; + + //put to channel context +// ctx.channel().attr(RequestParams).set(params); + } + + } + + +// protected void handlerWriterIdleEvent(ChannelHandlerContext ctx){ +// heartBeat.ping(ctx); +// } +// +// protected void handlerReaderIdleEvent(ChannelHandlerContext ctx){ +// heartBeat.ping(ctx); +// } +// +// protected void handlerAllIdleEvent(ChannelHandlerContext ctx){ +// heartBeat.ping(ctx); +// } + + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + cause.printStackTrace(); + if (!handshakeFuture.isDone()) { + handshakeFuture.setFailure(cause); + } + logger.info("exceptionCaught "+ cause.getMessage()); + } + + + public WebsocketConfig getWebsocketConfig() { + return websocketConfig; + } + + public void setWebsocketConfig(WebsocketConfig websocketConfig) { + this.websocketConfig = websocketConfig; + } + +// public HeartBeat getHeartBeat() { +// return heartBeat; +// } + +// public void setHeartBeat(HeartBeat heartBeat) { +// this.heartBeat = heartBeat; +// } +} diff --git a/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/client/handler/WsClientHandler.java b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/client/handler/WsClientHandler.java new file mode 100644 index 00000000..adf47c8d --- /dev/null +++ b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/client/handler/WsClientHandler.java @@ -0,0 +1,23 @@ +package com.xueyi.nlt.netty.client.handler; + +import com.xueyi.nlt.netty.client.NettyClient; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class WsClientHandler extends SimpleChannelInboundHandler { + + private static final Logger log = LoggerFactory.getLogger(WsClientHandler.class); + @Override + protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception { + // 获取客户端传输来的文本消息 + String text = msg.text(); + // 这个是自定义的日志工具类,可见其它文章 + log.info("收到的文本消息:[{}]", text); + // 在这里可以判断消息类型(比如初始化连接、消息在客户端间传输等) + // 然后可以将客户端Channel与对应的唯一标识用Map关联起来,就可以做定向推送,而不是广播 + + } +} diff --git a/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/client/message/ReceiveMessage.java b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/client/message/ReceiveMessage.java new file mode 100644 index 00000000..98fa375c --- /dev/null +++ b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/client/message/ReceiveMessage.java @@ -0,0 +1,13 @@ +package com.xueyi.nlt.netty.client.message; + +import io.netty.channel.Channel; +import io.netty.handler.codec.http.websocketx.WebSocketFrame; + +public interface ReceiveMessage { + + public void onMessage(Channel channel, String text); + + public void onMessage(Channel channel, byte[] bytes); + + public void onMessage(Channel channel, WebSocketFrame frame); +} diff --git a/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/controller/DmWebsocketController.java b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/controller/DmWebsocketController.java new file mode 100644 index 00000000..33fbd77f --- /dev/null +++ b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/netty/controller/DmWebsocketController.java @@ -0,0 +1,66 @@ +package com.xueyi.nlt.netty.controller; + +import com.alibaba.fastjson2.JSONObject; +import com.xueyi.common.cache.utils.SourceUtil; +import com.xueyi.common.core.constant.basic.SecurityConstants; +import com.xueyi.common.core.web.result.AjaxResult; +import com.xueyi.common.core.web.result.R; +import com.xueyi.nlt.api.netty.domain.vo.DmWebSocketMessageVo; +import com.xueyi.nlt.api.nlt.domain.vo.DmIntentVo; +import com.xueyi.nlt.netty.client.WebSocketClient; +import com.xueyi.nlt.nlt.domain.vo.IntentTemplateVo; +import com.xueyi.system.api.digitalmans.domain.dto.DmManDeviceDto; +import com.xueyi.system.api.model.Source; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.web.bind.annotation.*; + +import java.text.SimpleDateFormat; +import java.util.Date; + +@RestController +@RequestMapping("/websocket") +public class DmWebsocketController { + + private static final Logger log = LoggerFactory.getLogger(DmWebsocketController.class); + + @Autowired + WebSocketClient webSocketClient; + + @Autowired + StringRedisTemplate stringRedisTemplate; + + @Autowired + RedisTemplate redisTemplate; + + + /** + * 意图请求 + 列表 + */ + @PostMapping("/inner/sendMessage") + @ResponseBody + public R sendMessage(@RequestBody DmWebSocketMessageVo message) { + + log.info("websocket sendMessage:{}", message); + if (message == null || message.getFormat() == null) { + return R.fail("参数为空"); + } + JSONObject jo = message.getFormat(); + SimpleDateFormat dateFormat3 = new SimpleDateFormat("MM-dd"); + Double timestamp = Double.valueOf((String)jo.get("timestamp")); + String meetingRoom = jo.getString("meetingRoom"); + Date date = new Date(timestamp.longValue()); + if (message.getSkillCode().equals("1")) { + String prefix = "假设你是一名公司前台,你看到在你们公司工作的\\"+ jo.getString("orderName")+ "\\,请你从个人角度提醒他参加\\" + + dateFormat3.format(timestamp) + "\\在\\" + meetingRoom + "\\的会,要求语气友好。输出只包含你要对他说的话,在20字左右。"; + webSocketClient.sendMsg(prefix); + // 设置缓存 + redisTemplate.opsForValue().set("gpt:websocket" + ":" + "1", message); + } + return R.ok(); + } +} diff --git a/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/controller/DmIntentController.java b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/controller/DmIntentController.java new file mode 100644 index 00000000..fbf67729 --- /dev/null +++ b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/controller/DmIntentController.java @@ -0,0 +1,296 @@ +package com.xueyi.nlt.nlt.controller; + + +import com.alibaba.fastjson2.JSONObject; +import com.baomidou.mybatisplus.core.toolkit.StringUtils; +import com.xueyi.common.cache.utils.SourceUtil; +import com.xueyi.common.core.constant.basic.SecurityConstants; +import com.xueyi.common.core.web.result.AjaxResult; +import com.xueyi.common.core.web.result.R; +import com.xueyi.common.core.web.validate.V_A; +import com.xueyi.common.core.web.validate.V_E; +import com.xueyi.common.log.annotation.Log; +import com.xueyi.common.log.enums.BusinessType; +import com.xueyi.common.security.annotation.Logical; +import com.xueyi.common.security.annotation.RequiresPermissions; +import com.xueyi.common.web.entity.controller.BaseController; +import com.xueyi.nlt.api.netty.domain.vo.DmWebSocketMessageVo; +import com.xueyi.nlt.api.nlt.domain.vo.DmRecognitionVo; +import com.xueyi.nlt.api.nlt.feign.RemoteIntentService; +import com.xueyi.nlt.netty.client.WebSocketClient; +import com.xueyi.nlt.netty.client.handler.NettyClientHandler; +import com.xueyi.nlt.nlt.domain.dto.DmIntentDto; +import com.xueyi.nlt.nlt.domain.query.DmIntentQuery; +import com.xueyi.nlt.api.nlt.domain.vo.DmIntentVo; +import com.xueyi.nlt.nlt.domain.vo.IntentTemplateVo; +import com.xueyi.nlt.nlt.service.IDmIntentService; +import com.xueyi.nlt.nlt.template.BaseTemplate; +import com.xueyi.nlt.nlt.template.MeetingOrderTemplate; +import com.xueyi.system.api.digitalmans.domain.dto.DmManDeviceDto; +import com.xueyi.system.api.digitalmans.feign.RemoteBroadcastService; +import com.xueyi.system.api.digitalmans.feign.RemoteManDeviceService; +import com.xueyi.system.api.meeting.feign.RemoteMeetingService; +import com.xueyi.system.api.model.Source; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.io.Serializable; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/intent") +public class DmIntentController extends BaseController { + + private static final Logger log = LoggerFactory.getLogger(DmIntentController.class); + @Autowired + IDmIntentService dmIntentService; + + @Autowired + WebSocketClient webSocketClient; + + /** 定义节点名称 */ + @Override + protected String getNodeName() { + return "意图管理 "; + } + + + @Autowired + private StringRedisTemplate redisTemplate; + + @Autowired + private RedisTemplate redisTemplate2; + + + + @Autowired + private RemoteManDeviceService manDeviceService; + + @Autowired + private RemoteIntentService remoteIntentService; + + @Autowired + private MeetingOrderTemplate meetingOrderTemplate; + + /** + * 意图请求 + 列表 + */ + @PostMapping("/api/conversation") + @ResponseBody + public AjaxResult conversationApi(@RequestBody DmIntentVo intent) { + redisTemplate.opsForValue().increment("dashboard:server", 1); + R manDeviceDtoR = manDeviceService.manDeviceInfoInner(intent.getDevId()); + Source source = SourceUtil.getSourceCache(manDeviceDtoR.getData().getStrategyId()); + R jsonObjectR = remoteIntentService.conversationInner(intent, manDeviceDtoR.getData().getTId(), source.getMaster(), SecurityConstants.INNER); + IntentTemplateVo voResult = new IntentTemplateVo(); + voResult.setMsg(""); + if (jsonObjectR.getData() != null) { + voResult.setTarget(1); + } else { + voResult.setTarget(0); + } + voResult.setFormat(jsonObjectR.getData()); + return AjaxResult.success(voResult); + } + + /** + * 意图请求 + 列表 + */ + @PostMapping("/api/say_hello") + @ResponseBody + public AjaxResult sayHelloApi(@RequestBody DmRecognitionVo recognition) { + log.info(recognition.toString()); + redisTemplate.opsForValue().increment("dashboard:recognition", 1); + JSONObject joResult = new JSONObject(); + joResult.put("msg",""); + joResult.put("target",0); + if (StringUtils.isNotEmpty(recognition.getRegistered()) && recognition.getRegistered().equals("1")) { + + Map cntMap = redisTemplate.opsForHash().entries("group:nlp" + ":" + recognition.getPersonId()); + //遍历key和value + for (Object key : cntMap.keySet()) { + String value = (String) cntMap.get(key); + if (String.valueOf(key).equals("meeting")) { + JSONObject jo = JSONObject.parseObject(value); + // 判断是否在30分钟内 + System.out.println("timestamp:" + jo.get("timestamp")); + System.out.println("currenttime:" + System.currentTimeMillis()); + if(jo.containsKey("timestamp") && + Double.valueOf(String.valueOf(jo.get("timestamp"))) - System.currentTimeMillis() < 1800000) { + joResult.put("msg",jo.get("content")); + joResult.put("target",1); + redisTemplate.opsForHash().delete("group:nlp" + ":" + recognition.getPersonId(), "meeting"); + break; + } else if (Double.valueOf(String.valueOf(jo.get("timestamp"))) - System.currentTimeMillis() < 0) { + redisTemplate.opsForHash().delete("group:nlp" + ":" + recognition.getPersonId(), "meeting"); + } + } else { + joResult.put("msg",value); + joResult.put("target",1); + redisTemplate.opsForHash().delete("group:nlp" + ":" + recognition.getPersonId(),key); + break; + } + } + } + return AjaxResult.success(joResult); + } + + /** + * 意图请求列表 + */ + @PostMapping("/inner/conversation") + @ResponseBody + public R conversationInner(@RequestBody DmIntentVo intent) { + JSONObject joResult = null; + // 判断skill code的值 + switch (intent.getSkillCode()) { + case "1": + // 获取名称为"meeting-order"的BaseTemplate的实例 + joResult = meetingOrderTemplate.handle(intent.getDevId(),intent.getContent()); + break; + default: + break; + } + return R.ok(joResult); + } + + /** + * 意图请求 + 列表 + */ + @PostMapping("/inner/sendMessage") + @ResponseBody + public R sendMessage(@RequestBody DmWebSocketMessageVo message) { + + log.info("websocket sendMessage:{}", message); + if (message == null || message.getFormat() == null) { + return R.fail("参数为空"); + } + JSONObject jo = message.getFormat(); + SimpleDateFormat dateFormat3 = new SimpleDateFormat("MM-dd"); + Double timestamp = Double.valueOf((String)jo.get("timestamp")); + String meetingRoom = jo.getString("meetingRoom"); + Date date = new Date(timestamp.longValue()); + if (message.getSkillCode().equals("1")) { + String prefix = "假设你是一名公司前台,你看到在你们公司工作的\\"+ jo.getString("orderName")+ "\\,请你从个人角度提醒他参加\\" + + dateFormat3.format(timestamp) + "\\在\\" + meetingRoom + "\\的会,要求语气友好。输出只包含你要对他说的话,在20字左右。"; + webSocketClient.sendMsg(prefix); + // 设置缓存 + redisTemplate2.opsForValue().set("gpt:websocket" + ":" + "1", message); + } + return R.ok(); + } + + /** + * 查询意图管理 + 列表 + */ + @Override + @GetMapping("/list") + @RequiresPermissions(Auth.DM_INTENT_LIST) + public AjaxResult list(DmIntentQuery intent) { + return super.list(intent); + } + + /** + * 查询意图管理 + 详细 + */ + @Override + @GetMapping(value = "/{id}") + @RequiresPermissions(Auth.DM_INTENT_SINGLE) + public AjaxResult getInfo(@PathVariable Serializable id) { + return super.getInfo(id); + } + + /** + * 意图管理 + 新增 + */ + @Override + @PostMapping + @RequiresPermissions(Auth.DM_INTENT_ADD) + @Log(title = "意图管理", businessType = BusinessType.INSERT) + public AjaxResult add(@Validated({V_A.class}) @RequestBody DmIntentDto intent) { + return super.add(intent); + } + + /** + * 意图管理 + 修改 + */ + @Override + @PutMapping + @RequiresPermissions(Auth.DM_INTENT_EDIT) + @Log(title = "意图管理", businessType = BusinessType.UPDATE) + public AjaxResult edit(@Validated({V_E.class}) @RequestBody DmIntentDto intent) { + return super.edit(intent); + } + + /** + * 意图管理 + 修改状态 + */ + @Override + @PutMapping("/status") + @RequiresPermissions(value = {Auth.DM_INTENT_EDIT, Auth.DM_INTENT_ES}, logical = Logical.OR) + @Log(title = "意图管理", businessType = BusinessType.UPDATE_STATUS) + public AjaxResult editStatus(@RequestBody DmIntentDto intent) { + return super.editStatus(intent); + } + + /** + * 意图管理 + 批量删除 + */ + @Override + @DeleteMapping("/batch/{idList}") + @RequiresPermissions(Auth.DM_INTENT_DEL) + @Log(title = "意图管理", businessType = BusinessType.DELETE) + public AjaxResult batchRemove(@PathVariable List idList) { + return super.batchRemove(idList); + } + + /** + * 获取意图管理 + 选择框列表 + */ + @Override + @GetMapping("/option") + public AjaxResult option() { + return super.option(); + } + + + + interface Auth { + /** 系统 - 意图管理 + 管理 - 列表 */ + String DM_INTENT_LIST = "nlt:intent:list"; + /** 系统 - 意图管理 + 管理 - 详情 */ + String DM_INTENT_SINGLE = "nlt:intent:single"; + /** 系统 - 意图管理 + 管理 - 新增 */ + String DM_INTENT_ADD = "nlt:intent:add"; + /** 系统 - 意图管理 + 管理 - 修改 */ + String DM_INTENT_EDIT = "nlt:intent:edit"; + /** 系统 - 意图管理 + 管理 - 修改状态 */ + String DM_INTENT_ES = "nlt:intent:es"; + /** 系统 - 意图管理 + 管理 - 删除 */ + String DM_INTENT_DEL = "nlt:intent:delete"; + } +} diff --git a/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/domain/dto/DmIntentDto.java b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/domain/dto/DmIntentDto.java new file mode 100644 index 00000000..09877e72 --- /dev/null +++ b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/domain/dto/DmIntentDto.java @@ -0,0 +1,14 @@ +package com.xueyi.nlt.nlt.domain.dto; + + +import com.xueyi.nlt.nlt.domain.po.DmIntentPo; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class DmIntentDto extends DmIntentPo { + + private static final long serialVersionUID = 1L; + +} diff --git a/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/domain/model/DmIntentConverter.java b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/domain/model/DmIntentConverter.java new file mode 100644 index 00000000..eff602bc --- /dev/null +++ b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/domain/model/DmIntentConverter.java @@ -0,0 +1,13 @@ +package com.xueyi.nlt.nlt.domain.model; + +import com.xueyi.common.core.web.entity.model.BaseConverter; +import com.xueyi.nlt.nlt.domain.dto.DmIntentDto; +import com.xueyi.nlt.nlt.domain.po.DmIntentPo; +import com.xueyi.nlt.nlt.domain.query.DmIntentQuery; +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; + + +@Mapper(componentModel = MappingConstants.ComponentModel.SPRING) +public interface DmIntentConverter extends BaseConverter { +} diff --git a/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/domain/po/DmIntentPo.java b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/domain/po/DmIntentPo.java new file mode 100644 index 00000000..4c256f86 --- /dev/null +++ b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/domain/po/DmIntentPo.java @@ -0,0 +1,61 @@ +package com.xueyi.nlt.nlt.domain.po; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.xueyi.common.core.annotation.Excel; +import com.xueyi.common.core.web.tenant.base.TBaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +import static com.xueyi.common.core.constant.basic.EntityConstants.REMARK; +import static com.xueyi.common.core.constant.basic.EntityConstants.SORT; + +/** + * 访客 持久化对象 + * + * @author xueyi + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName(value = "dm_skills", excludeProperty = { SORT, REMARK }) +public class DmIntentPo extends TBaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** 数字人id */ + @Excel(name = "数字人id") + protected Long manId; + + /** 技能id */ + @Excel(name = "技能id") + protected String skillCode; + + /** 技能名称 */ + @Excel(name = "技能名称") + protected String name; + + /** 技能信息 */ + @Excel(name = "技能信息") + protected String info; + + /** 访客公司 */ + @Excel(name = "访客公司") + protected String resp; + + /** 访客称呼 */ + @Excel(name = "访客称呼") + protected Long motionId; + + protected String motionName; + + protected Integer firstCall; + + protected String auth; + + protected String intent; + + protected String template; + +} \ No newline at end of file diff --git a/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/domain/query/DmIntentQuery.java b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/domain/query/DmIntentQuery.java new file mode 100644 index 00000000..d15cef80 --- /dev/null +++ b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/domain/query/DmIntentQuery.java @@ -0,0 +1,13 @@ +package com.xueyi.nlt.nlt.domain.query; + + +import com.xueyi.nlt.nlt.domain.po.DmIntentPo; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class DmIntentQuery extends DmIntentPo { + + private static final long serialVersionUID = 1L; +} diff --git a/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/domain/vo/IntentTemplateVo.java b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/domain/vo/IntentTemplateVo.java new file mode 100644 index 00000000..178f4c5c --- /dev/null +++ b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/domain/vo/IntentTemplateVo.java @@ -0,0 +1,16 @@ +package com.xueyi.nlt.nlt.domain.vo; + +import com.alibaba.fastjson2.JSONObject; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +public class IntentTemplateVo { + private String msg; + private JSONObject format; + /** + * 是否触发技能 + */ + private Integer target; +} diff --git a/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/manager/IDmIntentManager.java b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/manager/IDmIntentManager.java new file mode 100644 index 00000000..d02b1bdc --- /dev/null +++ b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/manager/IDmIntentManager.java @@ -0,0 +1,8 @@ +package com.xueyi.nlt.nlt.manager; + +import com.xueyi.common.web.entity.manager.IBaseManager; +import com.xueyi.nlt.nlt.domain.dto.DmIntentDto; +import com.xueyi.nlt.nlt.domain.query.DmIntentQuery; + +public interface IDmIntentManager extends IBaseManager { +} diff --git a/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/manager/impl/DmIntentManager.java b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/manager/impl/DmIntentManager.java new file mode 100644 index 00000000..77ea9f87 --- /dev/null +++ b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/manager/impl/DmIntentManager.java @@ -0,0 +1,15 @@ +package com.xueyi.nlt.nlt.manager.impl; + +import com.xueyi.common.web.entity.manager.impl.BaseManagerImpl; +import com.xueyi.nlt.nlt.domain.dto.DmIntentDto; +import com.xueyi.nlt.nlt.domain.model.DmIntentConverter; +import com.xueyi.nlt.nlt.domain.po.DmIntentPo; +import com.xueyi.nlt.nlt.domain.query.DmIntentQuery; +import com.xueyi.nlt.nlt.manager.IDmIntentManager; +import com.xueyi.nlt.nlt.mapper.DmIntentMapper; +import org.springframework.stereotype.Component; + +@Component +public class DmIntentManager extends BaseManagerImpl implements IDmIntentManager { + +} diff --git a/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/mapper/DmIntentMapper.java b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/mapper/DmIntentMapper.java new file mode 100644 index 00000000..1c71bcca --- /dev/null +++ b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/mapper/DmIntentMapper.java @@ -0,0 +1,11 @@ +package com.xueyi.nlt.nlt.mapper; + +import com.xueyi.common.datasource.annotation.Isolate; +import com.xueyi.common.web.entity.mapper.BaseMapper; +import com.xueyi.nlt.nlt.domain.dto.DmIntentDto; +import com.xueyi.nlt.nlt.domain.po.DmIntentPo; +import com.xueyi.nlt.nlt.domain.query.DmIntentQuery; + +@Isolate +public interface DmIntentMapper extends BaseMapper { +} diff --git a/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/service/IDmIntentService.java b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/service/IDmIntentService.java new file mode 100644 index 00000000..79981a70 --- /dev/null +++ b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/service/IDmIntentService.java @@ -0,0 +1,8 @@ +package com.xueyi.nlt.nlt.service; + +import com.xueyi.common.web.entity.service.IBaseService; +import com.xueyi.nlt.nlt.domain.dto.DmIntentDto; +import com.xueyi.nlt.nlt.domain.query.DmIntentQuery; + +public interface IDmIntentService extends IBaseService { +} diff --git a/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/service/impl/DmIntentServiceImpl.java b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/service/impl/DmIntentServiceImpl.java new file mode 100644 index 00000000..38c7ba34 --- /dev/null +++ b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/service/impl/DmIntentServiceImpl.java @@ -0,0 +1,12 @@ +package com.xueyi.nlt.nlt.service.impl; + +import com.xueyi.common.web.entity.service.impl.BaseServiceImpl; +import com.xueyi.nlt.nlt.domain.dto.DmIntentDto; +import com.xueyi.nlt.nlt.domain.query.DmIntentQuery; +import com.xueyi.nlt.nlt.manager.IDmIntentManager; +import com.xueyi.nlt.nlt.service.IDmIntentService; +import org.springframework.stereotype.Service; + +@Service +public class DmIntentServiceImpl extends BaseServiceImpl implements IDmIntentService { +} diff --git a/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/template/BaseTemplate.java b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/template/BaseTemplate.java new file mode 100644 index 00000000..b067e862 --- /dev/null +++ b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/template/BaseTemplate.java @@ -0,0 +1,7 @@ +package com.xueyi.nlt.nlt.template; + +import com.alibaba.fastjson2.JSONObject; + +public interface BaseTemplate { + JSONObject handle(String dev, String content); +} diff --git a/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/template/MeetingOrderTemplate.java b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/template/MeetingOrderTemplate.java new file mode 100644 index 00000000..8bcb7d40 --- /dev/null +++ b/xueyi-modules/xueyi-nlt/src/main/java/com/xueyi/nlt/nlt/template/MeetingOrderTemplate.java @@ -0,0 +1,93 @@ +package com.xueyi.nlt.nlt.template; + +import com.alibaba.fastjson2.JSONException; +import com.alibaba.fastjson2.JSONObject; +import com.xueyi.common.core.web.result.R; +import com.xueyi.nlt.netty.client.WebSocketClient; +import com.xueyi.nlt.nlt.controller.DmIntentController; +import com.xueyi.system.api.meeting.feign.RemoteMeetingService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; + + +@Service("meeting-order") +public class MeetingOrderTemplate implements BaseTemplate { + + private static final Logger log = LoggerFactory.getLogger(MeetingOrderTemplate.class); + @Autowired + WebSocketClient webSocketClient; + +// @Autowired +// RemoteMeetingService remoteMeetingService; + + @Autowired + private RemoteMeetingService remoteMeetingService; + @Autowired + private StringRedisTemplate redisTemplate; + + @Override + public JSONObject handle(String devId, String content) { + synchronized (WebSocketClient.LOCK) { + LocalDateTime date = LocalDateTime.now(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd 00:00"); + DateTimeFormatter scheduleFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); + DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm"); +// String prefix = "假设你是一位前台,你需要通过与其他人对话来获取会议相关信息,已知今天是" + +// date.format(formatter) + ",你需要获取会议日期,开始时间,持续时间,会议地点,会议主题。时间用类似00:00的格式输出。对方的话中可能不包含全部信息,对于未知的信息填充为none。如果所有信息都已知那么commit为true。否则为false。将你获得的信息输出为json格式。对方的话是:“"; +// String suffix = "”,只输出最后的json。输出只有一行,输出格式为{date:,start_time:,duration:,location:,theme:commit:}。"; +// remoteMeetingService.roomListInner() +// String prefix = "我想请你充当[企业前台], 已知现在是" + +// date.format(formatter) + "。我对你说:”"; +// String suffix = "”。会议地点:{" + "大会议室,小会议室" + "}。会议内容:{其他会议,例会,访客接待,面试}。未知的项目输出null。输出为{date:,start_time:,duration_hours:,location:,meeting_content:}"; +// String prefix = "我想请你充当[SQL数据库],我需要的数据格式为{date:,start_time:,location:,meeting_theme:},没有数据不返回字段,数据只能来自于如下文段:\"已知现在是" + +// date.format(formatter) + ","; +// String suffix = "\",只输出最后的JSON结果。不要输出任何解释说明或者代码。"; + String prefix = "当前时间是" + + date.format(formatter) + " " + date.getDayOfWeek() + "。我将提供一些用户输入的信息,请提取输入的内容进行结构化转换并输出对应 json 格式的代码。下面是结构的描述:会议日期date(格式为YYYY-MM-DD);具体时间start_time(格式HH:MM);会议室地点location; 下面是用户的输入信息:\""; + String suffix = "\"。请开始信息提取,你回复的内容必须是一个json结构,我只要json结果,不需要代码。"; + log.info(prefix + content + suffix); + webSocketClient.sendMsg(prefix + content + suffix); + try { + WebSocketClient.LOCK.wait(); + String result = redisTemplate.opsForValue().get("group:websocket:content"); + try { + //对result进行解析,获取字符串第一个与'{'和最后一个'}'之间的字符串,即为json字符串 + result = result.substring(result.indexOf("{"), result.lastIndexOf("}") + 1); + System.out.println(result); + JSONObject jo = JSONObject.parseObject(result); + R> recentListR = null; + if (jo.get("start_time") != null ) { + if (jo.get("start_time").equals("00:00")) { + recentListR = remoteMeetingService.ableOrderList(devId, jo.getString("date"), null, null); + } else { + recentListR = remoteMeetingService.ableOrderList(devId, jo.getString("date"), null, jo.getString("start_time")); + } + if (recentListR.isOk()) { + jo.put("start_time",recentListR.getData().get(0).get("startTime")); + jo.put("location",recentListR.getData().get(0).get("roomId")); + } + } + + return jo; + } catch (JSONException je) { + // 返回结果错误,计日志,存log,返回空结果 + log.error(je.getMessage(),je); + return null; + } + + } catch (InterruptedException e) { + log.warn(e.getMessage()); + } + } + return null; + } +} \ No newline at end of file diff --git a/xueyi-modules/xueyi-nlt/src/main/resources/banner.txt b/xueyi-modules/xueyi-nlt/src/main/resources/banner.txt new file mode 100644 index 00000000..56ec3d17 --- /dev/null +++ b/xueyi-modules/xueyi-nlt/src/main/resources/banner.txt @@ -0,0 +1,10 @@ +Spring Boot Version: ${spring-boot.version} +Spring Application Name: ${spring.application.name} + _______ _________ _ _________ +|\ /||\ /|( ____ \|\ /|\__ __/ ( ( /||\ \__ __/ +( \ / )| ) ( || ( \/( \ / ) ) ( | \ ( || ) ) ( + \ (_) / | | | || (__ \ (_) / | | _____ | \ | || | | | + ) _ ( | | | || __) \ / | |(_____)| (\ \) || | | | + / ( ) \ | | | || ( ) ( | | | | \ || | | | +( / \ )| (___) || (____/\ | | ___) (___ | ) \ || (____/\ | | +|/ \|(_______)(_______/ \_/ \_______/ |/ )_)(_______/ )_( \ No newline at end of file diff --git a/xueyi-modules/xueyi-nlt/src/main/resources/bootstrap.yml b/xueyi-modules/xueyi-nlt/src/main/resources/bootstrap.yml new file mode 100644 index 00000000..c7c2eea5 --- /dev/null +++ b/xueyi-modules/xueyi-nlt/src/main/resources/bootstrap.yml @@ -0,0 +1,34 @@ +# Tomcat +server: + port: 9901 + +# Spring +spring: + application: + # 应用名称 + name: xueyi-nlt + profiles: + # 环境配置 + active: @activatedProperties@ + servlet: + multipart: + max-request-size: 20MB + max-file-size: 100MB + cloud: + nacos: + discovery: + # 服务注册地址 + server-addr: @nacos.host@:@nacos.port@ + namespace: @nacos.namespace@ + + config: + # 配置中心地址 + server-addr: @nacos.host@:@nacos.port@ + namespace: @nacos.namespace@ + # 配置文件格式 + file-extension: yml + # 共享配置 + shared-configs: + - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} + - application-secret-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} + - application-datasource-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} \ No newline at end of file diff --git a/xueyi-modules/xueyi-nlt/src/main/resources/logback.xml b/xueyi-modules/xueyi-nlt/src/main/resources/logback.xml new file mode 100644 index 00000000..7c344af1 --- /dev/null +++ b/xueyi-modules/xueyi-nlt/src/main/resources/logback.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/xueyi-modules/xueyi-system/pom.xml b/xueyi-modules/xueyi-system/pom.xml index 39f7fd10..4f7dad40 100644 --- a/xueyi-modules/xueyi-system/pom.xml +++ b/xueyi-modules/xueyi-system/pom.xml @@ -41,6 +41,11 @@ spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-test + com.xueyi @@ -103,6 +108,10 @@ thumbnailator 0.4.8 + + com.xueyi + xueyi-api-nlt + diff --git a/xueyi-modules/xueyi-system/src/main/java/com/xueyi/system/meeting/controller/api/DmMeetingApiController.java b/xueyi-modules/xueyi-system/src/main/java/com/xueyi/system/meeting/controller/api/DmMeetingApiController.java index d9cac850..10625a12 100644 --- a/xueyi-modules/xueyi-system/src/main/java/com/xueyi/system/meeting/controller/api/DmMeetingApiController.java +++ b/xueyi-modules/xueyi-system/src/main/java/com/xueyi/system/meeting/controller/api/DmMeetingApiController.java @@ -15,6 +15,7 @@ import com.xueyi.system.api.pass.feign.RemoteRecognizedRecordsService; import com.xueyi.system.resource.controller.api.BaseApiController; import com.xueyi.system.utils.common.ImageUtil; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -46,6 +47,9 @@ public class DmMeetingApiController extends BaseApiController { @Autowired private SmsService smsService; + @Autowired + private StringRedisTemplate redisTemplate; + @GetMapping(value = "/room-lists/{devId}") public List roomList(@PathVariable(value = "devId") String devId){ DeviceTenantSourceMergeVo vo = super.getDeviceTenantSourceMergeVo(devId); @@ -91,6 +95,14 @@ public class DmMeetingApiController extends BaseApiController { return remoteMeetingService.delInner(id, vo.getTenantId(),vo.getSourceSlave(), SecurityConstants.INNER); } + @ResponseBody + @PostMapping(value = "/cancel-order") + public JSONObject cancel(@RequestParam(required = true) String devId) { + redisTemplate.delete("group:websocket" + ":" + devId); + + return outputSuccess().toJSON(); + } + @ResponseBody @PostMapping(value = "/lists") public JSONObject list(String dateStr, Long spaceId, String devId) { diff --git a/xueyi-modules/xueyi-system/src/main/java/com/xueyi/system/meeting/controller/api/DmMeetingInnerApiController.java b/xueyi-modules/xueyi-system/src/main/java/com/xueyi/system/meeting/controller/api/DmMeetingInnerApiController.java index 35eb265b..66bffce9 100644 --- a/xueyi-modules/xueyi-system/src/main/java/com/xueyi/system/meeting/controller/api/DmMeetingInnerApiController.java +++ b/xueyi-modules/xueyi-system/src/main/java/com/xueyi/system/meeting/controller/api/DmMeetingInnerApiController.java @@ -5,18 +5,26 @@ import com.alibaba.fastjson2.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.xueyi.common.cache.utils.SourceUtil; +import com.xueyi.common.core.constant.basic.SecurityConstants; import com.xueyi.common.core.constant.basic.SqlConstants; import com.xueyi.common.core.web.result.R; import com.xueyi.common.security.annotation.InnerAuth; import com.xueyi.common.sms.configure.SmsProperties; import com.xueyi.common.web.constant.ResponseCode; import com.xueyi.common.web.utils.DateUtils; +import com.xueyi.nlt.api.netty.domain.vo.DmWebSocketMessageVo; +import com.xueyi.nlt.api.netty.feign.RemoteWebsocketService; +import com.xueyi.nlt.api.nlt.feign.RemoteIntentService; +import com.xueyi.system.api.digitalmans.domain.dto.DmManDeviceDto; import com.xueyi.system.api.digitalmans.domain.po.DmDigitalmanExtPo; import com.xueyi.system.api.digitalmans.domain.po.DmDigitalmanPo; +import com.xueyi.system.api.digitalmans.feign.RemoteManDeviceService; import com.xueyi.system.api.meeting.domain.dto.DmMeetingOrdersDto; import com.xueyi.system.api.meeting.domain.dto.DmMeetingRoomsDto; import com.xueyi.system.api.meeting.domain.po.DmMeetingOrdersPo; import com.xueyi.system.api.meeting.domain.po.DmMeetingRoomsPo; +import com.xueyi.system.api.model.Source; import com.xueyi.system.api.sms.domain.vo.SmsReqEntity; import com.xueyi.system.api.sms.feign.RemoteSmsService; import com.xueyi.system.api.staff.domain.po.DmStaffPo; @@ -31,6 +39,7 @@ import com.xueyi.system.meeting.service.impl.DmMeetingRoomsServiceImpl; import com.xueyi.system.resource.controller.api.BaseApiController; import com.xueyi.system.staff.mapper.DmStaffMapper; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -94,6 +103,17 @@ public class DmMeetingInnerApiController extends BaseApiController { @Autowired private DmMeetingRoomsConverter dmMeetingRoomsConverter; + @Autowired + private StringRedisTemplate redisTemplate; + + @Autowired + private RemoteWebsocketService remoteWebsocketService; + + @Autowired + private RemoteIntentService remoteIntentService; + @Autowired + private RemoteManDeviceService manDeviceService; + @InnerAuth @GetMapping("/rooms/{deptId}") @@ -189,6 +209,27 @@ public class DmMeetingInnerApiController extends BaseApiController { System.err.println(e.getMessage()); e.printStackTrace(); } + // 埋点 预定会议室 + redisTemplate.opsForValue().increment("dashboard:meeting", 1); + // 创建会议提醒 + // 创建json + JSONObject jsonObject = new JSONObject(); + jsonObject.put("meetingRoom", meetingRoom.getName()); + jsonObject.put("orderName", dmStaffPo.getUserName()); + jsonObject.put("orderId", dmStaffPo.getId()); + jsonObject.put("timestamp", list.get(0).getStartTime().getTime()); +// redisTemplate.opsForValue().set("group:nlp" + ":" + dmStaffPo.getId() + ":" + "meeting",jsonObject.toString()); + // 将创建临时信息添加进缓存:用户会议提醒 + + DmWebSocketMessageVo vo = new DmWebSocketMessageVo(); + vo.setDevId(order.getDevId()); + vo.setSkillCode("1"); + vo.setFormat(jsonObject); + R manDeviceDtoR = manDeviceService.manDeviceInfoInner(order.getDevId()); + Source source = SourceUtil.getSourceCache(manDeviceDtoR.getData().getStrategyId()); + remoteIntentService.sendMessage(vo, manDeviceDtoR.getData().getTId(), source.getMaster(), SecurityConstants.INNER); + // 清除设备会议室模版session信息 + redisTemplate.delete("group:websocket" + ":" + order.getDevId()); return outputSuccess().toJSON(); }