| @@ -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} | |||
| """ | |||
| } | |||
| } | |||
| @@ -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 "镜像推送成功" | |||
| //} | |||
| } | |||
| } | |||
| @@ -180,6 +180,20 @@ services: | |||
| depends_on: | |||
| - xueyi-nacos | |||
| - xueyi-redis | |||
| links: | |||
| - xueyi-nacos | |||
| - xueyi-redis | |||
| - | |||
| xueyi-nlt: | |||
| container_name: xueyi-nlt | |||
| build: | |||
| context: xueyi-modules/xueyi-nlt | |||
| dockerfile: ./Dockerfile | |||
| ports: | |||
| - "9910:9910" | |||
| depends_on: | |||
| - xueyi-nacos | |||
| - xueyi-redis | |||
| links: | |||
| - xueyi-nacos | |||
| - xueyi-redis | |||
| @@ -46,6 +46,8 @@ | |||
| <sonar.java.checkstyle.reportPaths>./target/checkstyle-result.xml</sonar.java.checkstyle.reportPaths> | |||
| <sonar.login>admin</sonar.login> | |||
| <sonar.password>Digimeta@2023</sonar.password> | |||
| <jakarta-json.version>2.0.1</jakarta-json.version> | |||
| <elasticsearch.version>8.3.3</elasticsearch.version> | |||
| </properties> | |||
| <!-- 依赖声明 --> | |||
| @@ -163,6 +165,11 @@ | |||
| <version>${fastjson2.version}</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>net.logstash.logback</groupId> | |||
| <artifactId>logstash-logback-encoder</artifactId> | |||
| <version>7.4</version> | |||
| </dependency> | |||
| <!-- JWT --> | |||
| <dependency> | |||
| <groupId>io.jsonwebtoken</groupId> | |||
| @@ -301,6 +308,12 @@ | |||
| <version>${xueyi.version}</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.xueyi</groupId> | |||
| <artifactId>xueyi-api-nlt</artifactId> | |||
| <version>${xueyi.version}</version> | |||
| </dependency> | |||
| <!-- huTool 工具类库 --> | |||
| <dependency> | |||
| <groupId>cn.hutool</groupId> | |||
| @@ -310,6 +323,17 @@ | |||
| <scope>import</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>co.elastic.clients</groupId> | |||
| <artifactId>elasticsearch-java</artifactId> | |||
| <version>${elasticsearch.version}</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>jakarta.json</groupId> | |||
| <artifactId>jakarta.json-api</artifactId> | |||
| <version>${jakarta-json.version}</version> | |||
| </dependency> | |||
| </dependencies> | |||
| </dependencyManagement> | |||
| @@ -14,6 +14,7 @@ | |||
| <module>xueyi-api-tenant</module> | |||
| <module>xueyi-api-file</module> | |||
| <module>xueyi-api-job</module> | |||
| <module>xueyi-api-nlt</module> | |||
| <module>xueyi-api-modules-auth</module> | |||
| </modules> | |||
| @@ -0,0 +1,16 @@ | |||
| package com.xueyi.message.api.transfer.domain.vo; | |||
| import com.fasterxml.jackson.annotation.JsonIgnoreProperties; | |||
| import lombok.*; | |||
| import java.io.Serializable; | |||
| @Data | |||
| @ToString | |||
| @Builder | |||
| @NoArgsConstructor | |||
| @JsonIgnoreProperties(ignoreUnknown = true) | |||
| public class Message implements Serializable,Cloneable{ | |||
| } | |||
| @@ -1,15 +1,14 @@ | |||
| package com.xueyi.message.api.transfer.feign; | |||
| import com.xueyi.common.core.constant.basic.SecurityConstants; | |||
| import com.xueyi.common.core.constant.basic.ServiceConstants; | |||
| import com.xueyi.common.core.web.result.AjaxResult; | |||
| import com.xueyi.common.core.web.result.R; | |||
| import com.xueyi.message.api.transfer.feign.factory.RemoteTransferFallbackFactory; | |||
| import org.springframework.cloud.openfeign.FeignClient; | |||
| import org.springframework.web.bind.annotation.*; | |||
| import javax.servlet.http.HttpServletResponse; | |||
| import org.springframework.web.bind.annotation.RequestMapping; | |||
| import org.springframework.web.bind.annotation.RequestMethod; | |||
| import org.springframework.web.bind.annotation.RequestParam; | |||
| import org.springframework.web.bind.annotation.ResponseBody; | |||
| /** | |||
| * 数据源服务 | |||
| @@ -21,4 +20,10 @@ public interface RemoteTransferService { | |||
| @RequestMapping(value = "/api/device_online_status/{devId}", method = {RequestMethod.GET}) | |||
| @ResponseBody | |||
| public AjaxResult getDeviceOnlineStatus(@RequestParam(value = "devId") String devId); | |||
| @RequestMapping(value = "/api/broadcast", method = {RequestMethod.GET}) | |||
| @ResponseBody | |||
| public R broadcast(@RequestParam(value = "channel") String channel); | |||
| } | |||
| @@ -13,7 +13,7 @@ import javax.validation.constraints.NotNull; | |||
| public class IntentionReqDto { | |||
| @NotNull(message = "staffId不能为空") | |||
| private String staffId; | |||
| @NotNull(message = "staffType不能为空") | |||
| private String staffType; | |||
| @NotNull(message = "skillCode不能为空") | |||
| private String skillCode; | |||
| @@ -0,0 +1,33 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
| xmlns="http://maven.apache.org/POM/4.0.0" | |||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
| <parent> | |||
| <groupId>com.xueyi</groupId> | |||
| <artifactId>xueyi-api</artifactId> | |||
| <version>2.5.0</version> | |||
| </parent> | |||
| <modelVersion>4.0.0</modelVersion> | |||
| <artifactId>xueyi-api-nlt</artifactId> | |||
| <description> | |||
| xueyi-api-nlt模型接入管理模块 | |||
| </description> | |||
| <dependencies> | |||
| <!-- XueYi Common Core --> | |||
| <dependency> | |||
| <groupId>com.xueyi</groupId> | |||
| <artifactId>xueyi-common-core</artifactId> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.xueyi</groupId> | |||
| <artifactId>xueyi-api-system</artifactId> | |||
| </dependency> | |||
| </dependencies> | |||
| </project> | |||
| @@ -0,0 +1,15 @@ | |||
| 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; | |||
| String template; | |||
| String operator; | |||
| } | |||
| @@ -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); | |||
| } | |||
| @@ -0,0 +1,13 @@ | |||
| package com.xueyi.nlt.api.nlt.domain.vo; | |||
| import com.alibaba.fastjson2.JSONObject; | |||
| import lombok.Data; | |||
| import lombok.NoArgsConstructor; | |||
| @Data | |||
| @NoArgsConstructor | |||
| public class CoversationSessionVo { | |||
| protected String category; | |||
| protected String id; | |||
| protected JSONObject format; | |||
| } | |||
| @@ -0,0 +1,18 @@ | |||
| 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; | |||
| /** 模式类型 1:工作模式 2:接待模式 3:闲聊模式 */ | |||
| String mode; | |||
| } | |||
| @@ -0,0 +1,29 @@ | |||
| package com.xueyi.nlt.api.nlt.domain.vo; | |||
| import lombok.Data; | |||
| import lombok.NoArgsConstructor; | |||
| import java.util.ArrayList; | |||
| import java.util.List; | |||
| @Data | |||
| @NoArgsConstructor | |||
| public class DmLandingLlmVo { | |||
| private String category; | |||
| private List<DmLlm> message = new ArrayList<>(); | |||
| @Data | |||
| @NoArgsConstructor | |||
| class DmLlm { | |||
| private String role; | |||
| private String content; | |||
| } | |||
| // 定义创建DmLlm的对象方法,并添加到message中 | |||
| public void addDmLlm(String role, String content) { | |||
| DmLlm dmLlm = new DmLlm(); | |||
| dmLlm.setRole(role); | |||
| dmLlm.setContent(content); | |||
| message.add(dmLlm); | |||
| } | |||
| } | |||
| @@ -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; | |||
| } | |||
| @@ -0,0 +1,13 @@ | |||
| package com.xueyi.nlt.api.nlt.domain.vo; | |||
| import lombok.Data; | |||
| import lombok.NoArgsConstructor; | |||
| @Data | |||
| @NoArgsConstructor | |||
| public class KnowledgeVo { | |||
| private String man_code; | |||
| private String question; | |||
| private Long tenant_id; | |||
| private String trace_id; | |||
| } | |||
| @@ -0,0 +1,17 @@ | |||
| package com.xueyi.nlt.api.nlt.domain.vo; | |||
| import lombok.AllArgsConstructor; | |||
| import lombok.Data; | |||
| import lombok.NoArgsConstructor; | |||
| @Data | |||
| @NoArgsConstructor | |||
| public class TaskKnowledgeVo { | |||
| private Long knowledgeId; | |||
| private String content; | |||
| private Integer count; | |||
| private Long manId; | |||
| private Long tenantId; | |||
| private int split; | |||
| private Long createBy; | |||
| } | |||
| @@ -0,0 +1,27 @@ | |||
| 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<JSONObject> 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); | |||
| @PostMapping("/intent/inner/taskGenerativeKnowledge") | |||
| public R taskGenerativeKnowledge(); | |||
| } | |||
| @@ -0,0 +1,28 @@ | |||
| package com.xueyi.nlt.api.nlt.feign; | |||
| import com.alibaba.fastjson2.JSONObject; | |||
| import com.xueyi.common.core.web.result.AjaxResult; | |||
| import com.xueyi.nlt.api.nlt.domain.vo.DmLandingLlmVo; | |||
| import com.xueyi.nlt.api.nlt.feign.factory.RemoteLandingLlmFallbackFactory; | |||
| import com.xueyi.nlt.api.nlt.feign.factory.RemoteQAFallbackFactory; | |||
| import org.springframework.cloud.openfeign.FeignClient; | |||
| import org.springframework.web.bind.annotation.*; | |||
| /** | |||
| * 问答服务 | |||
| * @Param man_id 机器人id | |||
| * @Param question 问题 | |||
| * @Param tenant_id 租户id | |||
| * @author yrx | |||
| */ | |||
| @FeignClient(url = "${notification.landing-llm.url}",name = "landing-llm", fallbackFactory = RemoteLandingLlmFallbackFactory.class) | |||
| public interface RemoteLandingLlmService { | |||
| @PostMapping("/search/questionAnswer") | |||
| @ResponseBody | |||
| JSONObject query(@RequestBody DmLandingLlmVo vo); | |||
| @PostMapping("/search/questionAnswer") | |||
| @ResponseBody | |||
| JSONObject query(@RequestBody JSONObject jsonObject); | |||
| } | |||
| @@ -0,0 +1,30 @@ | |||
| package com.xueyi.nlt.api.nlt.feign; | |||
| import com.alibaba.fastjson.JSONObject; | |||
| import com.xueyi.common.core.web.result.AjaxResult; | |||
| import com.xueyi.nlt.api.nlt.domain.vo.KnowledgeVo; | |||
| import com.xueyi.nlt.api.nlt.feign.factory.RemoteQAFallbackFactory; | |||
| import com.xueyi.system.api.sms.domain.vo.SmsReqEntity; | |||
| import com.xueyi.system.api.sms.feign.factory.RemoteSmsFallbackFactory; | |||
| import org.springframework.cloud.openfeign.FeignClient; | |||
| import org.springframework.web.bind.annotation.GetMapping; | |||
| import org.springframework.web.bind.annotation.PostMapping; | |||
| import org.springframework.web.bind.annotation.RequestBody; | |||
| import org.springframework.web.bind.annotation.RequestParam; | |||
| /** | |||
| * 问答服务 | |||
| * @Param man_id 机器人id | |||
| * @Param question 问题 | |||
| * @Param tenant_id 租户id | |||
| * @author yrx | |||
| */ | |||
| @FeignClient(url = "${notification.qa.url}",name = "qa", fallbackFactory = RemoteQAFallbackFactory.class) | |||
| public interface RemoteQAService { | |||
| @GetMapping("/knowledge") | |||
| AjaxResult query(@RequestParam(value = "man_code") String manCode, @RequestParam(value = "question") String question, @RequestParam(value = "tenant_id") Long tenantId); | |||
| @PostMapping ("/knowledge") | |||
| AjaxResult query(@RequestBody KnowledgeVo vo); | |||
| } | |||
| @@ -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<RemoteIntentService> { | |||
| @Override | |||
| public RemoteIntentService create(Throwable cause) { | |||
| return null; | |||
| } | |||
| } | |||
| @@ -0,0 +1,42 @@ | |||
| package com.xueyi.nlt.api.nlt.feign.factory; | |||
| import com.alibaba.fastjson2.JSONObject; | |||
| import com.xueyi.common.core.web.result.AjaxResult; | |||
| import com.xueyi.nlt.api.nlt.domain.vo.DmLandingLlmVo; | |||
| import com.xueyi.nlt.api.nlt.feign.RemoteLandingLlmService; | |||
| import com.xueyi.nlt.api.nlt.feign.RemoteQAService; | |||
| import lombok.extern.slf4j.Slf4j; | |||
| import org.springframework.cloud.openfeign.FallbackFactory; | |||
| import org.springframework.stereotype.Component; | |||
| /** | |||
| * 会议室服务 降级处理 | |||
| * | |||
| * @author xueyi | |||
| */ | |||
| @Slf4j | |||
| @Component | |||
| public class RemoteLandingLlmFallbackFactory implements FallbackFactory<RemoteLandingLlmService> { | |||
| @Override | |||
| public RemoteLandingLlmService create(Throwable throwable) { | |||
| log.error("短信服务调用失败:{}", throwable.getMessage()); | |||
| return new RemoteLandingLlmService() { | |||
| @Override | |||
| public JSONObject query(DmLandingLlmVo vo) { | |||
| JSONObject jsonObject = new JSONObject(); | |||
| jsonObject.put("status","fail"); | |||
| jsonObject.put("data",""); | |||
| return jsonObject; | |||
| } | |||
| @Override | |||
| public JSONObject query(JSONObject jsonObject) { | |||
| JSONObject jResult = new JSONObject(); | |||
| jResult.put("status","fail2"); | |||
| jResult.put("data",""); | |||
| return jResult; | |||
| } | |||
| }; | |||
| } | |||
| } | |||
| @@ -0,0 +1,34 @@ | |||
| package com.xueyi.nlt.api.nlt.feign.factory; | |||
| import com.xueyi.common.core.web.result.AjaxResult; | |||
| import com.xueyi.nlt.api.nlt.domain.vo.KnowledgeVo; | |||
| import com.xueyi.nlt.api.nlt.feign.RemoteQAService; | |||
| import lombok.extern.slf4j.Slf4j; | |||
| import org.springframework.cloud.openfeign.FallbackFactory; | |||
| import org.springframework.stereotype.Component; | |||
| /** | |||
| * 会议室服务 降级处理 | |||
| * | |||
| * @author xueyi | |||
| */ | |||
| @Slf4j | |||
| @Component | |||
| public class RemoteQAFallbackFactory implements FallbackFactory<RemoteQAService> { | |||
| @Override | |||
| public RemoteQAService create(Throwable throwable) { | |||
| log.error("短信服务调用失败:{}", throwable.getMessage()); | |||
| return new RemoteQAService() { | |||
| @Override | |||
| public AjaxResult query(String manCode, String question, Long tenantId) { | |||
| return AjaxResult.error("查询失败"); | |||
| } | |||
| @Override | |||
| public AjaxResult query(KnowledgeVo vo) { | |||
| return AjaxResult.error("查询失败"); | |||
| } | |||
| }; | |||
| } | |||
| } | |||
| @@ -0,0 +1,3 @@ | |||
| com.xueyi.nlt.api.nlt.feign.factory.RemoteIntentFallbackFactory | |||
| com.xueyi.nlt.api.nlt.feign.factory.RemoteQAFallbackFactory | |||
| com.xueyi.nlt.api.nlt.feign.factory.RemoteLandingLlmFallbackFactory | |||
| @@ -9,6 +9,7 @@ import lombok.Data; | |||
| import lombok.EqualsAndHashCode; | |||
| import java.io.Serial; | |||
| import java.util.List; | |||
| @Data | |||
| @EqualsAndHashCode(callSuper = true) | |||
| @@ -23,6 +24,6 @@ public class DmSyncDigitalmanDto extends DmDigitalmanPo { | |||
| protected DmDigitalmanWorktimePo worktime; | |||
| protected SysDeptExt workdayHour; | |||
| protected String iconPos; | |||
| protected DmBroadcastVo broadcast; | |||
| protected List<DmBroadcastVo> broadcast; | |||
| } | |||
| @@ -22,4 +22,6 @@ public class DmVisitorsDto extends DmVisitorsPo { | |||
| public static final Long TYPE_VIP_VISITOR = 9L; | |||
| public static final Long TYPE_SPECIAL_VISITOR = 10L; | |||
| public String imgUrl; | |||
| } | |||
| @@ -0,0 +1,14 @@ | |||
| package com.xueyi.system.api.digitalmans.domain.vo; | |||
| import com.alibaba.fastjson2.JSONArray; | |||
| import lombok.Data; | |||
| import lombok.NoArgsConstructor; | |||
| import org.springframework.web.bind.annotation.RequestParam; | |||
| @Data | |||
| @NoArgsConstructor | |||
| public class DmBatchQuestionsVo { | |||
| private Long knowledgeId; | |||
| private JSONArray questions; | |||
| private Long createId; | |||
| } | |||
| @@ -11,5 +11,6 @@ import java.util.List; | |||
| public class DmBroadcastVo { | |||
| Integer recycle; | |||
| Integer speed; | |||
| Long id; | |||
| List<JSONObject> resource; | |||
| } | |||
| @@ -11,12 +11,14 @@ import org.springframework.web.bind.annotation.GetMapping; | |||
| import org.springframework.web.bind.annotation.RequestHeader; | |||
| import org.springframework.web.bind.annotation.RequestParam; | |||
| import java.util.List; | |||
| @FeignClient(contextId = "remoteBroadcastService", value = ServiceConstants.SYSTEM_SERVICE, fallbackFactory = RemoteUserFallbackFactory.class) | |||
| public interface RemoteBroadcastService { | |||
| @GetMapping("/broadcast/inner/list") | |||
| public R<DmBroadcastVo> innerlist(@RequestParam("manCode") String manCode, | |||
| @RequestHeader(SecurityConstants.ENTERPRISE_ID) Long enterpriseId, @RequestHeader(SecurityConstants.SOURCE_NAME) String sourceName, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); | |||
| public R<List<DmBroadcastVo>> innerlist(@RequestParam("manCode") String manCode, | |||
| @RequestHeader(SecurityConstants.ENTERPRISE_ID) Long enterpriseId, @RequestHeader(SecurityConstants.SOURCE_NAME) String sourceName, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); | |||
| } | |||
| @@ -1,5 +1,6 @@ | |||
| package com.xueyi.system.api.digitalmans.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; | |||
| @@ -8,7 +9,11 @@ import com.xueyi.system.api.digitalmans.domain.dto.DmSyncDigitalmanDto; | |||
| import com.xueyi.system.api.digitalmans.domain.po.DmDigitalmanExtPo; | |||
| import com.xueyi.system.api.organize.feign.factory.RemoteUserFallbackFactory; | |||
| import org.springframework.cloud.openfeign.FeignClient; | |||
| import org.springframework.web.bind.annotation.*; | |||
| import org.springframework.web.bind.annotation.GetMapping; | |||
| 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.RequestParam; | |||
| import java.util.List; | |||
| @@ -44,4 +49,11 @@ public interface RemoteDigitalmanService { | |||
| @GetMapping("/man/inner-api/devInfo/{devId}") | |||
| public R<DmDigitalmanExtPo> devInfo(@RequestParam(value = "devId") String devId, @RequestHeader(SecurityConstants.ENTERPRISE_ID) Long enterpriseId, @RequestHeader(SecurityConstants.SOURCE_NAME) String sourceName, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); | |||
| @GetMapping("/man/api/mansInfo") | |||
| public R<JSONObject> mansInfo(); | |||
| @GetMapping("/man/delRecognizedRecords") | |||
| public R<JSONObject> delRecognizedRecords(@RequestParam(value = "day") Integer days); | |||
| } | |||
| @@ -0,0 +1,22 @@ | |||
| package com.xueyi.system.api.digitalmans.feign; | |||
| import com.alibaba.fastjson2.JSONArray; | |||
| 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.system.api.digitalmans.domain.vo.DmBatchQuestionsVo; | |||
| import com.xueyi.system.api.digitalmans.domain.vo.DmReceptionVo; | |||
| import com.xueyi.system.api.organize.feign.factory.RemoteUserFallbackFactory; | |||
| import org.springframework.cloud.openfeign.FeignClient; | |||
| import org.springframework.web.bind.annotation.*; | |||
| @FeignClient(contextId = "remoteQuestionanswersService", value = ServiceConstants.SYSTEM_SERVICE, fallbackFactory = RemoteUserFallbackFactory.class) | |||
| public interface RemoteQuestionanswersService { | |||
| @PostMapping("/questionanswers/inner/batch_insert") | |||
| @ResponseBody | |||
| public R<DmReceptionVo> batchInsertInner(@RequestBody DmBatchQuestionsVo dmBatchQuestionsVo, | |||
| @RequestHeader(SecurityConstants.ENTERPRISE_ID) Long enterpriseId, @RequestHeader(SecurityConstants.SOURCE_NAME) String sourceName, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); | |||
| } | |||
| @@ -9,10 +9,12 @@ import com.xueyi.system.api.meeting.domain.dto.DmMeetingRoomsDto; | |||
| import com.xueyi.system.api.meeting.feign.factory.RemoteMeetingFallbackFactory; | |||
| import org.springframework.cloud.openfeign.FeignClient; | |||
| import org.springframework.web.bind.annotation.GetMapping; | |||
| import org.springframework.web.bind.annotation.PathVariable; | |||
| 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.RequestParam; | |||
| import org.springframework.web.bind.annotation.ResponseBody; | |||
| import java.util.List; | |||
| @@ -50,4 +52,18 @@ public interface RemoteMeetingService { | |||
| @PostMapping(value = "/meeting/inner-api/lists-all") | |||
| JSONObject listAllInner(@RequestParam("dateStr") String dateStr, @RequestParam("spaceId") Long spaceId, @RequestHeader(SecurityConstants.ENTERPRISE_ID) Long enterpriseId, @RequestHeader(SecurityConstants.SOURCE_NAME) String sourceName, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); | |||
| @GetMapping("/meeting/inner-api/recent/{deptId}/{dateStr}") | |||
| @ResponseBody | |||
| public List<JSONObject> 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("/meeting/inner-api/recent/{deptId}") | |||
| @ResponseBody | |||
| public List<JSONObject> recent(@PathVariable(value = "deptId") Long deptId, @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<List<JSONObject>> ableOrderList(@PathVariable(value = "devId") String devId, @PathVariable(value = "dateStr",required = false) String dateStr, @RequestParam(value = "roomId", required = false) Long roomId, @RequestParam(value = "startTime", required = false) String startTime); | |||
| @GetMapping(value = "/meeting/inner-api/enableOrder/{roomId}/{dateStr}/{startTime}") | |||
| JSONObject queryExist(@PathVariable(name = "roomId") Long roomId, @PathVariable(name = "dateStr") String dateStr, @PathVariable(name = "startTime") String startTime, @RequestHeader(SecurityConstants.ENTERPRISE_ID) Long enterpriseId, @RequestHeader(SecurityConstants.SOURCE_NAME) String sourceName, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); | |||
| } | |||
| @@ -9,6 +9,7 @@ import lombok.extern.slf4j.Slf4j; | |||
| import org.springframework.cloud.openfeign.FallbackFactory; | |||
| import org.springframework.stereotype.Component; | |||
| import java.util.ArrayList; | |||
| import java.util.List; | |||
| /** | |||
| @@ -66,6 +67,30 @@ public class RemoteMeetingFallbackFactory implements FallbackFactory<RemoteMeeti | |||
| public JSONObject listAllInner(String dateStr, Long spaceId, Long enterpriseId, String sourceName, String source) { | |||
| return R.fail("获取会议室预约列表失败:" + throwable.getMessage()).toJson(); | |||
| } | |||
| @Override | |||
| public List<JSONObject> recent(Long deptId, String dateStr, Long roomId, String startTime, Long enterpriseId, String sourceName, String source) { | |||
| List js = new ArrayList<>(); | |||
| js.add(R.fail("获取会议室可预约列表失败:" + throwable.getMessage()).toJson()); | |||
| return js; | |||
| } | |||
| @Override | |||
| public List<JSONObject> recent(Long deptId, Long enterpriseId, String sourceName, String source) { | |||
| List js = new ArrayList<>(); | |||
| js.add(R.fail("获取会议室可预约列表失败:" + throwable.getMessage()).toJson()); | |||
| return js; | |||
| } | |||
| @Override | |||
| public R<List<JSONObject>> ableOrderList(String devId, String dateStr, Long roomId, String startTime) { | |||
| return null; | |||
| } | |||
| @Override | |||
| public JSONObject queryExist(Long roomId, String dateStr, String startTime, Long enterpriseId, String sourceName, String source) { | |||
| return null; | |||
| } | |||
| }; | |||
| } | |||
| } | |||
| @@ -0,0 +1,28 @@ | |||
| package com.xueyi.system.api.organize.feign; | |||
| 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.system.api.organize.domain.dto.SysDeptDto; | |||
| import com.xueyi.system.api.organize.domain.dto.SysEnterpriseDto; | |||
| import com.xueyi.system.api.organize.feign.factory.RemoteDeptFallbackFactory; | |||
| import com.xueyi.system.api.organize.feign.factory.RemoteEnterpriseFallbackFactory; | |||
| import org.springframework.cloud.openfeign.FeignClient; | |||
| import org.springframework.web.bind.annotation.*; | |||
| /** | |||
| * 部门服务 | |||
| * | |||
| * @author xueyi | |||
| */ | |||
| @FeignClient(contextId = "remoteEnterpriseService", value = ServiceConstants.SYSTEM_SERVICE, fallbackFactory = RemoteEnterpriseFallbackFactory.class) | |||
| public interface RemoteEnterpriseService { | |||
| /** | |||
| * 新增部门 | |||
| * | |||
| * @return 结果 | |||
| */ | |||
| @GetMapping("/enterprise/info") | |||
| R<SysEnterpriseDto> getInfo(@RequestParam(value = "enterpriseId") Long enterpriseId); | |||
| } | |||
| @@ -0,0 +1,31 @@ | |||
| package com.xueyi.system.api.organize.feign.factory; | |||
| import com.xueyi.common.core.web.result.R; | |||
| import com.xueyi.system.api.organize.domain.dto.SysDeptDto; | |||
| import com.xueyi.system.api.organize.domain.dto.SysEnterpriseDto; | |||
| import com.xueyi.system.api.organize.feign.RemoteDeptService; | |||
| import com.xueyi.system.api.organize.feign.RemoteEnterpriseService; | |||
| import lombok.extern.slf4j.Slf4j; | |||
| import org.springframework.cloud.openfeign.FallbackFactory; | |||
| import org.springframework.stereotype.Component; | |||
| /** | |||
| * 部门服务 降级处理 | |||
| * | |||
| * @author xueyi | |||
| */ | |||
| @Slf4j | |||
| @Component | |||
| public class RemoteEnterpriseFallbackFactory implements FallbackFactory<RemoteEnterpriseService> { | |||
| @Override | |||
| public RemoteEnterpriseService create(Throwable throwable) { | |||
| log.error("部门服务调用失败:{}", throwable.getMessage()); | |||
| return new RemoteEnterpriseService() { | |||
| @Override | |||
| public R<SysEnterpriseDto> getInfo(Long enterpriseId) { | |||
| return R.fail("获取企业信息失败:" + throwable.getMessage()); | |||
| } | |||
| }; | |||
| } | |||
| } | |||
| @@ -0,0 +1,14 @@ | |||
| package com.xueyi.system.api.resource.feign; | |||
| import com.xueyi.common.core.constant.basic.ServiceConstants; | |||
| import com.xueyi.common.core.web.result.R; | |||
| import com.xueyi.system.api.resource.feign.factory.RemoteH5ConfigFallbackFactory; | |||
| import org.springframework.cloud.openfeign.FeignClient; | |||
| import org.springframework.web.bind.annotation.*; | |||
| @FeignClient(contextId = "remoteH5ConfigService", value = ServiceConstants.SYSTEM_SERVICE, fallbackFactory = RemoteH5ConfigFallbackFactory.class) | |||
| public interface RemoteH5ConfigService { | |||
| @GetMapping("/h5config/inner/syncH5Config") | |||
| R<String> syncH5Config(@RequestParam(value = "tId",required = false) String tId); | |||
| } | |||
| @@ -0,0 +1,21 @@ | |||
| package com.xueyi.system.api.resource.feign.factory; | |||
| import com.xueyi.common.core.web.result.R; | |||
| import com.xueyi.system.api.resource.feign.RemoteH5ConfigService; | |||
| import lombok.extern.slf4j.Slf4j; | |||
| import org.springframework.cloud.openfeign.FallbackFactory; | |||
| import org.springframework.stereotype.Component; | |||
| @Slf4j | |||
| @Component | |||
| public class RemoteH5ConfigFallbackFactory implements FallbackFactory<RemoteH5ConfigService> { | |||
| @Override | |||
| public RemoteH5ConfigService create(Throwable cause) { | |||
| return new RemoteH5ConfigService() { | |||
| @Override | |||
| public R<String> syncH5Config(String tId) { | |||
| return R.fail("同步配置文件失败"); | |||
| } | |||
| }; | |||
| } | |||
| } | |||
| @@ -87,4 +87,12 @@ public class DmStaffPo extends TBaseEntity { | |||
| @Excel(name = "所属组织ID") | |||
| protected Long resourceId; | |||
| /** 生日日期 */ | |||
| @Excel(name = "生日日期") | |||
| protected Date birthDate; | |||
| /** 入职日期 */ | |||
| @Excel(name = "入职日期") | |||
| protected Date hireDate; | |||
| } | |||
| @@ -1,8 +1,11 @@ | |||
| com.xueyi.system.api.organize.feign.factory.RemoteDeptFallbackFactory | |||
| com.xueyi.system.api.organize.feign.factory.RemotePostFallbackFactory | |||
| com.xueyi.system.api.organize.feign.factory.RemoteUserFallbackFactory | |||
| com.xueyi.system.api.organize.feign.factory.RemoteEnterpriseFallbackFactory | |||
| com.xueyi.system.api.authority.feign.factory.RemoteLoginFallbackFactory | |||
| com.xueyi.system.api.authority.feign.factory.RemoteMenuFallbackFactory | |||
| com.xueyi.system.api.authority.feign.factory.RemoteAuthFallbackFactory | |||
| com.xueyi.system.api.dict.feign.factory.RemoteConfigFallbackFactory | |||
| com.xueyi.system.api.log.feign.factory.RemoteLogFallbackFactory | |||
| com.xueyi.system.api.meeting.feign.factory.RemoteMeetingFallbackFactory | |||
| com.xueyi.system.api.resource.feign.factory.RemoteH5ConfigFallbackFactory | |||
| @@ -77,6 +77,12 @@ | |||
| <artifactId>jackson-databind</artifactId> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.fasterxml.jackson.datatype</groupId> | |||
| <artifactId>jackson-datatype-jsr310</artifactId> | |||
| <version>2.13.0</version> | |||
| </dependency> | |||
| <!-- Alibaba Fastjson --> | |||
| <dependency> | |||
| <groupId>com.alibaba.fastjson2</groupId> | |||
| @@ -149,6 +155,13 @@ | |||
| <artifactId>hutool-crypto</artifactId> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>co.elastic.clients</groupId> | |||
| <artifactId>elasticsearch-java</artifactId> | |||
| <version>8.3.3</version> | |||
| </dependency> | |||
| </dependencies> | |||
| </project> | |||
| @@ -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"; | |||
| @@ -16,6 +16,13 @@ public class MessageConstants { | |||
| /** 数字人同步 */ | |||
| public static final Long MODEL_SYNC = 2L; | |||
| public static final Long H5_CONFIG_SYNC = 4L; | |||
| /** 数字人场景 */ | |||
| public static final String MODE_WORK = "1"; // 工作模式 | |||
| public static final String MODE_RECEPTION = "2"; // 接待模式 | |||
| public static final String MODE_FREE_CHAT = "3"; // 闲聊模式 | |||
| /** 缓存数据头 */ | |||
| public static final String REDIS_GROUP_HEADER = "group:dgman:"; | |||
| public static final String REDIS_GROUP_DEVICE_HEADER = "group:dgman:device:"; | |||
| @@ -32,6 +39,7 @@ public class MessageConstants { | |||
| public static final Integer SYS_DICT_DATA_RELEASE_UPDATE_UPDATING = 7; | |||
| public static final Integer SYS_DICT_DATA_RESTART_DEVICE = 0; | |||
| public static final Integer SYS_DICT_DATA_RESTART_APP = 1; | |||
| public static final Integer SYS_DICT_DATA_SPLITED = 1; | |||
| public enum RestartEnum { | |||
| RESTART_DEVICE(0, "重启设备"), | |||
| @@ -0,0 +1,29 @@ | |||
| package com.xueyi.common.core.constant.digitalman; | |||
| import lombok.AllArgsConstructor; | |||
| import lombok.Getter; | |||
| /** | |||
| * 技能配置通用常量 | |||
| * | |||
| * @author yinruoxi | |||
| */ | |||
| public class SkillConstants { | |||
| /** 技能配置 */ | |||
| @Getter | |||
| @AllArgsConstructor | |||
| public enum SkillType { | |||
| GENERATION("0", "通用问答"), | |||
| BOOK_MEETING_ROOM("1", "会议室预定"), | |||
| CREATE_VISITOR_INFO("2", "访客预约登记"), | |||
| REGISTER_VISITOR("3", "访客到访登记"), | |||
| INTRODUCE_STRANGER("24", "熟人介绍生人"), | |||
| BROADCAST_DISPLAY("25", "播报展示"), | |||
| OPEN_DOOR("26", "开门"); | |||
| private final String code; | |||
| private final String info; | |||
| } | |||
| } | |||
| @@ -0,0 +1,6 @@ | |||
| package com.xueyi.common.core.constant.nacos; | |||
| public class NacosConstants { | |||
| public static final String DEFAULT_CONFIG_PATH = "config.json"; | |||
| public static final String DEFAULT_GROUP = "DEFAULT_GROUP"; | |||
| } | |||
| @@ -5,7 +5,12 @@ import org.apache.commons.lang3.time.DateFormatUtils; | |||
| import java.lang.management.ManagementFactory; | |||
| import java.text.ParseException; | |||
| import java.text.SimpleDateFormat; | |||
| import java.time.*; | |||
| import java.time.LocalDate; | |||
| import java.time.LocalDateTime; | |||
| import java.time.LocalTime; | |||
| import java.time.ZoneId; | |||
| import java.time.ZonedDateTime; | |||
| import java.time.format.DateTimeFormatter; | |||
| import java.util.Date; | |||
| /** | |||
| @@ -25,11 +30,14 @@ public class DateUtil extends org.apache.commons.lang3.time.DateUtils { | |||
| public static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"; | |||
| private static final String[] parsePatterns = { | |||
| "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", | |||
| "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", | |||
| "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"}; | |||
| public static final DateTimeFormatter DEFAULT_DATETIME_PATTERN = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); | |||
| /** | |||
| * 获取当前Date型日期 | |||
| * | |||
| @@ -0,0 +1,54 @@ | |||
| package com.xueyi.common.core.utils; | |||
| import java.net.InetAddress; | |||
| import java.net.NetworkInterface; | |||
| import java.net.UnknownHostException; | |||
| import java.util.Enumeration; | |||
| /** | |||
| * @author yinruoxi | |||
| * @version V1.0 | |||
| * @className InetAddressUtils | |||
| * @description TO DO | |||
| * @Date 2023/8/10 14:19 PM | |||
| */ | |||
| public class InetAddressUtils { | |||
| public static InetAddress getLocalHostLANAddress() throws UnknownHostException { | |||
| try { | |||
| InetAddress candidateAddress = null; | |||
| // 遍历所有的网络接口 | |||
| for (Enumeration ifaces = NetworkInterface.getNetworkInterfaces(); ifaces.hasMoreElements();) { | |||
| NetworkInterface iface = (NetworkInterface) ifaces.nextElement(); | |||
| // 在所有的接口下再遍历IP | |||
| for (Enumeration inetAddrs = iface.getInetAddresses(); inetAddrs.hasMoreElements();) { | |||
| InetAddress inetAddr = (InetAddress) inetAddrs.nextElement(); | |||
| if (!inetAddr.isLoopbackAddress()) {// 排除loopback类型地址 | |||
| if (inetAddr.isSiteLocalAddress()) { | |||
| // 如果是site-local地址,就是它了 | |||
| return inetAddr; | |||
| } else if (candidateAddress == null) { | |||
| // site-local类型的地址未被发现,先记录候选地址 | |||
| candidateAddress = inetAddr; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| if (candidateAddress != null) { | |||
| return candidateAddress; | |||
| } | |||
| // 如果没有发现 non-loopback地址.只能用最次选的方案 | |||
| InetAddress jdkSuppliedAddress = InetAddress.getLocalHost(); | |||
| if (jdkSuppliedAddress == null) { | |||
| throw new UnknownHostException("The JDK InetAddress.getLocalHost() method unexpectedly returned null."); | |||
| } | |||
| return jdkSuppliedAddress; | |||
| } catch (Exception e) { | |||
| UnknownHostException unknownHostException = new UnknownHostException( | |||
| "Failed to determine LAN address: " + e); | |||
| unknownHostException.initCause(e); | |||
| throw unknownHostException; | |||
| } | |||
| } | |||
| } | |||
| @@ -167,4 +167,27 @@ public class StrUtil extends cn.hutool.core.util.StrUtil implements StrPool { | |||
| return matcher.match(pattern, url); | |||
| } | |||
| /** | |||
| * @Author yangkai | |||
| * @Description 驼峰转下划线形式,如 helloWorldTest->hello_world_test | |||
| * @Date 2023/9/4 | |||
| * @Param | |||
| * @return | |||
| **/ | |||
| public static String camelCaseToUnderscore(String input) { | |||
| StringBuilder result = new StringBuilder(); | |||
| for (int i = 0; i < input.length(); i++) { | |||
| char currentChar = input.charAt(i); | |||
| if (Character.isUpperCase(currentChar)) { | |||
| if (i > 0) { | |||
| result.append("_"); | |||
| } | |||
| result.append(Character.toLowerCase(currentChar)); | |||
| } else { | |||
| result.append(currentChar); | |||
| } | |||
| } | |||
| return result.toString(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,40 @@ | |||
| package com.xueyi.common.core.utils.sql; | |||
| import co.elastic.clients.elasticsearch.ElasticsearchClient; | |||
| import co.elastic.clients.json.jackson.JacksonJsonpMapper; | |||
| import co.elastic.clients.transport.ElasticsearchTransport; | |||
| import co.elastic.clients.transport.rest_client.RestClientTransport; | |||
| import lombok.Data; | |||
| import org.apache.http.HttpHost; | |||
| import org.elasticsearch.client.RestClient; | |||
| import org.springframework.beans.factory.annotation.Value; | |||
| import org.springframework.context.annotation.Bean; | |||
| import org.springframework.context.annotation.Configuration; | |||
| /** | |||
| * @author yk | |||
| * @description | |||
| * @date 2023-08-29 15:55 | |||
| */ | |||
| @Data | |||
| @Configuration | |||
| public class EsClient { | |||
| @Value("${spring.elasticsearch.host}") | |||
| public String esHost; | |||
| @Value("${spring.elasticsearch.port}") | |||
| public String esPort; | |||
| @Bean | |||
| public ElasticsearchClient getEsClient() { | |||
| RestClient restClient = RestClient.builder( | |||
| new HttpHost(esHost, Integer.valueOf(esPort))).build(); | |||
| ElasticsearchTransport transport = new RestClientTransport( | |||
| restClient, new JacksonJsonpMapper()); | |||
| ElasticsearchClient client = new ElasticsearchClient(transport); | |||
| return client; | |||
| } | |||
| } | |||
| @@ -0,0 +1,30 @@ | |||
| package com.xueyi.common.core.utils.time; | |||
| import com.fasterxml.jackson.core.JsonGenerator; | |||
| import com.fasterxml.jackson.databind.JsonSerializer; | |||
| import com.fasterxml.jackson.databind.SerializerProvider; | |||
| import com.xueyi.common.core.utils.DateUtil; | |||
| import java.io.IOException; | |||
| import java.time.LocalDateTime; | |||
| import java.time.format.DateTimeParseException; | |||
| /** | |||
| * @author yk | |||
| * @description | |||
| * @date 2023-08-26 16:19 | |||
| */ | |||
| public class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> { | |||
| @Override | |||
| public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider provider) | |||
| throws IOException { | |||
| try { | |||
| String s = value.format(DateUtil.DEFAULT_DATETIME_PATTERN); | |||
| gen.writeString(s); | |||
| } catch (DateTimeParseException e) { | |||
| System.err.println(e); | |||
| gen.writeString(""); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,30 @@ | |||
| package com.xueyi.common.core.utils.time; | |||
| import com.fasterxml.jackson.core.JsonParser; | |||
| import com.fasterxml.jackson.databind.DeserializationContext; | |||
| import com.fasterxml.jackson.databind.JsonDeserializer; | |||
| import com.xueyi.common.core.utils.DateUtil; | |||
| import java.io.IOException; | |||
| import java.time.LocalDateTime; | |||
| import java.time.format.DateTimeParseException; | |||
| /** | |||
| * @author yk | |||
| * @description | |||
| * @date 2023-08-26 16:05 | |||
| */ | |||
| public class LocalDatetimeDeserializer extends JsonDeserializer<LocalDateTime> { | |||
| @Override | |||
| public LocalDateTime deserialize(JsonParser p, DeserializationContext ctx) | |||
| throws IOException { | |||
| String str = p.getText(); | |||
| try { | |||
| return LocalDateTime.parse(str, DateUtil.DEFAULT_DATETIME_PATTERN); | |||
| } catch (DateTimeParseException e) { | |||
| System.err.println(e); | |||
| return null; | |||
| } | |||
| } | |||
| } | |||
| @@ -1,8 +1,16 @@ | |||
| package com.xueyi.common.core.web.entity.base; | |||
| import com.baomidou.mybatisplus.annotation.*; | |||
| import com.baomidou.mybatisplus.annotation.FieldFill; | |||
| import com.baomidou.mybatisplus.annotation.FieldStrategy; | |||
| import com.baomidou.mybatisplus.annotation.OrderBy; | |||
| import com.baomidou.mybatisplus.annotation.TableField; | |||
| import com.baomidou.mybatisplus.annotation.TableLogic; | |||
| import com.fasterxml.jackson.annotation.JsonFormat; | |||
| import com.fasterxml.jackson.annotation.JsonIgnore; | |||
| import com.fasterxml.jackson.databind.annotation.JsonDeserialize; | |||
| import com.fasterxml.jackson.databind.annotation.JsonSerialize; | |||
| import com.xueyi.common.core.utils.time.LocalDateTimeSerializer; | |||
| import com.xueyi.common.core.utils.time.LocalDatetimeDeserializer; | |||
| import lombok.Data; | |||
| import lombok.EqualsAndHashCode; | |||
| @@ -46,6 +54,8 @@ public class BaseEntity extends BasisEntity { | |||
| @OrderBy(sort = 20) | |||
| @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") | |||
| @TableField(insertStrategy = FieldStrategy.NEVER, updateStrategy = FieldStrategy.NEVER) | |||
| @JsonDeserialize(using = LocalDatetimeDeserializer.class) // 反序列化 | |||
| @JsonSerialize(using = LocalDateTimeSerializer.class) | |||
| protected LocalDateTime createTime; | |||
| /** 更新者Id */ | |||
| @@ -55,6 +65,8 @@ public class BaseEntity extends BasisEntity { | |||
| /** 更新时间 */ | |||
| @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") | |||
| @TableField(insertStrategy = FieldStrategy.NEVER, updateStrategy = FieldStrategy.NEVER) | |||
| @JsonDeserialize(using = LocalDatetimeDeserializer.class) // 反序列化 | |||
| @JsonSerialize(using = LocalDateTimeSerializer.class) | |||
| protected LocalDateTime updateTime; | |||
| /** 删除标志 */ | |||
| @@ -1,2 +1,3 @@ | |||
| com.xueyi.common.core.utils.core.SpringUtil | |||
| com.xueyi.common.core.utils.core.SpringUtils | |||
| com.xueyi.common.core.utils.core.SpringUtils | |||
| com.xueyi.common.core.utils.sql.EsClient | |||
| @@ -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"; | |||
| } | |||
| @@ -26,6 +26,7 @@ public class SmsProperties { | |||
| private String meetingQuitTemplate; | |||
| private String meetingRemindTemplate; | |||
| private String defaultRemindTemplate; | |||
| private String warningTemplate; | |||
| private String visitorTemplate; | |||
| private String robotName; | |||
| private String receptionPhones; | |||
| @@ -40,6 +40,11 @@ | |||
| <artifactId>xueyi-common-datasource</artifactId> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.xueyi</groupId> | |||
| <artifactId>xueyi-common-core</artifactId> | |||
| </dependency> | |||
| <!-- XueYi Common DataSource --> | |||
| <dependency> | |||
| <groupId>com.xueyi</groupId> | |||
| @@ -457,12 +457,12 @@ public class ResponseCode { | |||
| public static final Integer NOT_FOUND_VISITOR = 5404; | |||
| @ResponseMessage( | |||
| msg = {"预订失败,会议室预订时间点有冲突"} | |||
| msg = {"会议室预订时间点有冲突"} | |||
| ) | |||
| public static final Integer MEETING_ORDER_CONFLICT = 5501; | |||
| @ResponseMessage( | |||
| msg = {"预订失败,会议室预订时间错误"} | |||
| msg = {"会议室预订时间错误"} | |||
| ) | |||
| public static final Integer MEETING_ORDER_TIME_ERROR = 5502; | |||
| @@ -123,4 +123,8 @@ public interface IBaseService<Q extends BaseEntity, D extends BaseEntity> { | |||
| * 更新缓存数据 | |||
| */ | |||
| void refreshCache(); | |||
| void createEsIndex(D dto); | |||
| void saveToEs(D dto); | |||
| } | |||
| @@ -1,5 +1,7 @@ | |||
| package com.xueyi.common.web.entity.service.impl; | |||
| import co.elastic.clients.elasticsearch.ElasticsearchClient; | |||
| import co.elastic.clients.elasticsearch.indices.CreateIndexResponse; | |||
| import com.baomidou.dynamic.datasource.annotation.DSTransactional; | |||
| import com.xueyi.common.core.constant.basic.BaseConstants; | |||
| import com.xueyi.common.core.constant.basic.OperateConstants; | |||
| @@ -11,7 +13,12 @@ import com.xueyi.common.redis.constant.RedisConstants; | |||
| import com.xueyi.common.web.entity.manager.IBaseManager; | |||
| import com.xueyi.common.web.entity.service.IBaseService; | |||
| import com.xueyi.common.web.entity.service.impl.handle.BaseHandleServiceImpl; | |||
| import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.beans.factory.annotation.Value; | |||
| import java.io.IOException; | |||
| import java.io.Serializable; | |||
| import java.util.Collection; | |||
| import java.util.List; | |||
| @@ -27,6 +34,7 @@ import java.util.stream.Collectors; | |||
| */ | |||
| public class BaseServiceImpl<Q extends BaseEntity, D extends BaseEntity, IDG extends IBaseManager<Q, D>> extends BaseHandleServiceImpl<Q, D, IDG> implements IBaseService<Q, D> { | |||
| private Logger log = LoggerFactory.getLogger(BaseServiceImpl.class); | |||
| /** | |||
| * 查询数据对象列表 | |||
| * | |||
| @@ -214,4 +222,53 @@ public class BaseServiceImpl<Q extends BaseEntity, D extends BaseEntity, IDG ext | |||
| throw new UtilException("未正常配置缓存,无法使用!"); | |||
| refreshCache(null, RedisConstants.OperateType.REFRESH_ALL, null, null); | |||
| } | |||
| @Autowired | |||
| private ElasticsearchClient client; | |||
| @Value("${spring.profiles.active}") | |||
| private String activeProfile; | |||
| @Override | |||
| public void createEsIndex(D dto) { | |||
| boolean exists = false; | |||
| String indexName = activeProfile+"_"+StrUtil.camelCaseToUnderscore(dto.getClass().getSimpleName()); | |||
| try { | |||
| String finalIndexName = indexName; | |||
| log.info("indexName:{}", indexName); | |||
| exists = client.indices().exists(query -> query.index(finalIndexName)).value(); | |||
| System.out.println(exists); | |||
| if (exists) { | |||
| log.info("索引已存在"); | |||
| } else { | |||
| String finalIndexName1 = indexName; | |||
| log.info("exists:false, indexName:{}", indexName); | |||
| final CreateIndexResponse products = client.indices().create(builder -> builder.index(finalIndexName1)); | |||
| log.info(products.acknowledged()?"索引创建成功" : "索引创建失败"); | |||
| } | |||
| } catch (IOException e) { | |||
| e.printStackTrace(); | |||
| throw new RuntimeException(e); | |||
| } | |||
| } | |||
| @Override | |||
| public void saveToEs(BaseEntity dto) { | |||
| try { | |||
| if (null != dto.getId()) { | |||
| String indexName = activeProfile+"_"+StrUtil.camelCaseToUnderscore(dto.getClass().getSimpleName()); | |||
| log.info("indexName:{}", indexName); | |||
| createEsIndex((D)dto); | |||
| String finalIndexName = indexName; | |||
| client.index(i -> i | |||
| .index(finalIndexName) | |||
| .id(dto.getId().toString()) | |||
| .document(dto)); | |||
| } | |||
| } catch (IOException e) { | |||
| e.printStackTrace(); | |||
| throw new RuntimeException(e); | |||
| } | |||
| } | |||
| } | |||
| @@ -8,6 +8,7 @@ import java.text.ParseException; | |||
| import java.text.ParsePosition; | |||
| import java.text.SimpleDateFormat; | |||
| import java.time.LocalDateTime; | |||
| import java.time.LocalTime; | |||
| import java.time.ZoneId; | |||
| import java.util.Calendar; | |||
| import java.util.Date; | |||
| @@ -334,4 +335,15 @@ public class DateUtils extends org.apache.commons.lang.time.DateUtils { | |||
| return ldt; | |||
| } | |||
| public static String isAmPm(){ | |||
| //判断当前时间是上午还是下午,上午返回am,下午返回pm | |||
| LocalTime currentTime = LocalTime.now(); | |||
| if (currentTime.isBefore(LocalTime.NOON)) { | |||
| return "am"; | |||
| } else { | |||
| return "pm"; | |||
| } | |||
| } | |||
| } | |||
| @@ -16,6 +16,7 @@ | |||
| <module>xueyi-file</module> | |||
| <module>xueyi-message</module> | |||
| <module>xueyi-modules-auth</module> | |||
| <module>xueyi-nlt</module> | |||
| </modules> | |||
| <artifactId>xueyi-modules</artifactId> | |||
| @@ -8,6 +8,8 @@ import org.slf4j.LoggerFactory; | |||
| import org.springframework.web.multipart.MultipartFile; | |||
| import javax.imageio.ImageIO; | |||
| import javax.imageio.ImageWriteParam; | |||
| import javax.imageio.ImageWriter; | |||
| import javax.servlet.http.HttpServletResponse; | |||
| import java.awt.image.BufferedImage; | |||
| import java.io.ByteArrayOutputStream; | |||
| @@ -18,6 +20,7 @@ import java.nio.file.Files; | |||
| import java.nio.file.Path; | |||
| import java.nio.file.Paths; | |||
| import java.util.ArrayList; | |||
| import java.util.Iterator; | |||
| import java.util.List; | |||
| import java.util.zip.ZipEntry; | |||
| import java.util.zip.ZipOutputStream; | |||
| @@ -70,8 +73,11 @@ public class PdfToImageUtil { | |||
| BufferedImage image = pdfRenderer.renderImageWithDPI(pageIdx, 300); // Set the DPI as needed | |||
| // 保存图像到本地临时目录 | |||
| String imageName = "page_" + (pageIdx + 1) + ".png"; | |||
| File imageFile = saveImageToFile(image, imageName); | |||
| /*String imageName = "page_" + (pageIdx + 1) + ".png"; | |||
| File imageFile = saveImageToFile(image, imageName);*/ | |||
| String imageName = "page_" + (pageIdx + 1) + ".jpg"; | |||
| File imageFile = saveCompressedImage(image, imageName, 0.8f); | |||
| imageFiles.add(imageFile); | |||
| } | |||
| } | |||
| @@ -131,6 +137,26 @@ public class PdfToImageUtil { | |||
| } | |||
| private static File saveCompressedImage(BufferedImage image, String imageName, float compressionQuality) throws IOException { | |||
| File tempDir = new File(TEMP_DIR); | |||
| if (!tempDir.exists()) { | |||
| tempDir.mkdirs(); | |||
| } | |||
| File imageFile = new File(tempDir, imageName); | |||
| Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("jpg"); | |||
| ImageWriter writer = writers.next(); | |||
| ImageWriteParam param = writer.getDefaultWriteParam(); | |||
| param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); | |||
| param.setCompressionQuality(compressionQuality); | |||
| ImageIO.write(image, "jpg", imageFile); | |||
| return imageFile; | |||
| } | |||
| /** | |||
| @@ -65,6 +65,11 @@ | |||
| <artifactId>xueyi-api-job</artifactId> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.xueyi</groupId> | |||
| <artifactId>xueyi-api-nlt</artifactId> | |||
| </dependency> | |||
| </dependencies> | |||
| <build> | |||
| @@ -0,0 +1,23 @@ | |||
| package com.xueyi.job.task; | |||
| import com.xueyi.message.api.transfer.feign.RemoteTransferService; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.stereotype.Component; | |||
| /** | |||
| * 定时任务调度测试 | |||
| * | |||
| * @author xueyi | |||
| */ | |||
| @Component("dmBroadDataTask") | |||
| public class DmBroadDataTask { | |||
| @Autowired | |||
| RemoteTransferService remoteTransferService; | |||
| /** | |||
| * 触发条件:* 0/1 * * * * | |||
| * 每分钟执行一次 | |||
| */ | |||
| public void syncData() { | |||
| remoteTransferService.broadcast("broadData"); | |||
| } | |||
| } | |||
| @@ -0,0 +1,24 @@ | |||
| package com.xueyi.job.task; | |||
| import com.xueyi.message.api.transfer.feign.RemoteTransferService; | |||
| import com.xueyi.nlt.api.nlt.feign.RemoteIntentService; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.stereotype.Component; | |||
| /** | |||
| * 定时任务调度测试 | |||
| * | |||
| * @author xueyi | |||
| */ | |||
| @Component("dmGenerativeKnowledgeTask") | |||
| public class DmGenerativeKnowledgeTask { | |||
| @Autowired | |||
| RemoteIntentService remoteIntentService; | |||
| /** | |||
| * 触发条件:* 0/1 * * * * | |||
| * 每分钟执行一次 | |||
| */ | |||
| public void generativeKnowledge() { | |||
| remoteIntentService.taskGenerativeKnowledge(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,25 @@ | |||
| package com.xueyi.job.task; | |||
| import com.xueyi.system.api.digitalmans.feign.RemoteDigitalmanService; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.stereotype.Component; | |||
| /** | |||
| * 定时任务调度测试 | |||
| * | |||
| * @author xueyi | |||
| */ | |||
| @Component("dmRecognizedRecordsCleanTask") | |||
| public class DmRecognizedRecordsCleanTask { | |||
| @Autowired | |||
| RemoteDigitalmanService remoteDigitalmanService; | |||
| /** | |||
| * 触发条件:* 0/1 * * * * | |||
| * 每分钟执行一次 | |||
| */ | |||
| public void delOutDays() { | |||
| remoteDigitalmanService.delRecognizedRecords(5); | |||
| } | |||
| } | |||
| @@ -43,7 +43,8 @@ public abstract class AbstractQuartzJob implements Job { | |||
| doExecute(context, sysJob); | |||
| after(context, sysJob, null); | |||
| } catch (Exception e) { | |||
| log.error("任务执行异常 - :", e); | |||
| log.error("任务执行异常 - :", e.getMessage()); | |||
| e.printStackTrace(); | |||
| after(context, sysJob, e); | |||
| } | |||
| } | |||
| @@ -47,7 +47,16 @@ | |||
| <artifactId>spring-integration-mqtt</artifactId> | |||
| <version>5.5.14</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.corundumstudio.socketio</groupId> | |||
| <artifactId>netty-socketio</artifactId> | |||
| <version>1.7.13</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>io.socket</groupId> | |||
| <artifactId>socket.io-client</artifactId> | |||
| <version>1.0.0</version> | |||
| </dependency> | |||
| <!-- XueYi Common Log --> | |||
| <dependency> | |||
| @@ -55,6 +64,7 @@ | |||
| <artifactId>xueyi-common-log</artifactId> | |||
| </dependency> | |||
| <!-- XueYi Common Web --> | |||
| <dependency> | |||
| <groupId>com.xueyi</groupId> | |||
| @@ -1,7 +1,6 @@ | |||
| package com.xueyi.message.transfer.controller; | |||
| import com.alibaba.fastjson2.JSONArray; | |||
| import com.alibaba.fastjson2.JSONObject; | |||
| import com.xueyi.common.cache.utils.SourceUtil; | |||
| import com.xueyi.common.core.constant.basic.SecurityConstants; | |||
| @@ -11,27 +10,34 @@ import com.xueyi.common.core.web.result.AjaxResult; | |||
| import com.xueyi.common.core.web.result.R; | |||
| import com.xueyi.message.api.transfer.domain.vo.DmActiveVo; | |||
| import com.xueyi.message.api.transfer.domain.vo.DmDeviceVo; | |||
| import com.xueyi.message.transfer.service.impl.MessageQueueServiceImpl; | |||
| import com.xueyi.system.api.authority.feign.RemoteLoginService; | |||
| import com.xueyi.system.api.device.domain.vo.DeviceTenantSourceMergeVo; | |||
| import com.xueyi.system.api.device.feign.RemoteDeviceTenantMergeService; | |||
| import com.xueyi.system.api.digitalmans.domain.dto.DmManDeviceDto; | |||
| import com.xueyi.system.api.digitalmans.domain.dto.DmSkillDto; | |||
| import com.xueyi.system.api.digitalmans.domain.dto.DmSyncDigitalmanDto; | |||
| import com.xueyi.system.api.digitalmans.domain.po.DmManDevicePo; | |||
| import com.xueyi.system.api.digitalmans.domain.vo.DmBroadcastVo; | |||
| import com.xueyi.system.api.digitalmans.domain.vo.DmReceptionVo; | |||
| import com.xueyi.system.api.digitalmans.feign.*; | |||
| import com.xueyi.system.api.digitalmans.feign.RemoteBroadcastService; | |||
| import com.xueyi.system.api.digitalmans.feign.RemoteDigitalmanService; | |||
| import com.xueyi.system.api.digitalmans.feign.RemoteManDeviceService; | |||
| import com.xueyi.system.api.digitalmans.feign.RemoteReceptionService; | |||
| import com.xueyi.system.api.digitalmans.feign.RemoteSkillService; | |||
| import com.xueyi.system.api.model.Source; | |||
| import com.xueyi.system.api.organize.domain.dto.SysEnterpriseDto; | |||
| import com.xueyi.system.api.resource.feign.RemoteH5ConfigService; | |||
| import com.xueyi.system.api.staff.domain.vo.DmStaffFeature; | |||
| import com.xueyi.system.api.staff.feign.RemoteStaffService; | |||
| import org.springframework.beans.BeanUtils; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.data.redis.core.RedisTemplate; | |||
| import org.springframework.data.redis.core.StringRedisTemplate; | |||
| import org.springframework.util.MimeTypeUtils; | |||
| import org.springframework.util.StringUtils; | |||
| import org.springframework.web.bind.annotation.*; | |||
| import org.springframework.web.bind.annotation.PathVariable; | |||
| import org.springframework.web.bind.annotation.RequestBody; | |||
| import org.springframework.web.bind.annotation.RequestMapping; | |||
| import org.springframework.web.bind.annotation.RequestMethod; | |||
| import org.springframework.web.bind.annotation.RequestParam; | |||
| import org.springframework.web.bind.annotation.ResponseBody; | |||
| import org.springframework.web.bind.annotation.RestController; | |||
| import javax.servlet.http.HttpServletResponse; | |||
| import java.util.Arrays; | |||
| @@ -70,6 +76,13 @@ public class ApiController { | |||
| @Autowired | |||
| RemoteBroadcastService remoteBroadcastService; | |||
| @Autowired | |||
| MessageQueueServiceImpl messageQueueService; | |||
| @Autowired | |||
| RemoteH5ConfigService remoteH5ConfigService; | |||
| @RequestMapping(value = "/heartbeat", method = {RequestMethod.POST}) | |||
| @ResponseBody | |||
| @@ -105,6 +118,10 @@ public class ApiController { | |||
| if (timestamp != null && !timestamp.equalsIgnoreCase(vo.getTimestamp()) && Long.parseLong(timestamp) > Long.parseLong(vo.getTimestamp())) { | |||
| dex_res += 0x100; | |||
| } | |||
| timestamp = redisTemplate.opsForValue().get("group:dgman:" + enterpriseDto.getData().getId() + ":" + MessageConstants.H5_CONFIG_SYNC); | |||
| if (timestamp != null && !timestamp.equalsIgnoreCase(vo.getTimestamp()) && Long.parseLong(timestamp) > Long.parseLong(vo.getTimestamp())) { | |||
| dex_res += 0x10000; | |||
| } | |||
| // if (!timestamp.equalsIgnoreCase(vo.getTimestamp()) && Long.parseLong(timestamp) > Long.parseLong(vo.getTimestamp())) { | |||
| // dex_res += 0x10; | |||
| // } | |||
| @@ -169,7 +186,7 @@ public class ApiController { | |||
| if (syncDmDto.isFail()) | |||
| return AjaxResult.warn("新增失败,请检查"); | |||
| // 添加播报信息 | |||
| R<DmBroadcastVo> broadcastVoR = remoteBroadcastService.innerlist(manDeviceDtoR.getData().getManCode(), manDeviceDtoR.getData().getTId(), source.getMaster(), SecurityConstants.INNER); | |||
| R<List<DmBroadcastVo>> broadcastVoR = remoteBroadcastService.innerlist(manDeviceDtoR.getData().getManCode(), manDeviceDtoR.getData().getTId(), source.getMaster(), SecurityConstants.INNER); | |||
| if (!broadcastVoR.isFail()) { | |||
| syncDmDto.getData().setBroadcast(broadcastVoR.getData()); | |||
| } | |||
| @@ -180,6 +197,8 @@ public class ApiController { | |||
| if (receptionVo.isFail()){ | |||
| return AjaxResult.warn("接待模式任务获取失败,请检查"); | |||
| } | |||
| // JSONObject jsObject = new JSONObject(); | |||
| // jsObject.put("code",200); | |||
| // JSONObject data = new JSONObject(); | |||
| @@ -199,6 +218,12 @@ public class ApiController { | |||
| // jsObject.put("data",data); | |||
| // jsObject.put("msg","success"); | |||
| return AjaxResult.success(receptionVo.getData()); | |||
| case 4: | |||
| R<String> propertyR = remoteH5ConfigService.syncH5Config(manDeviceDtoR.getData().getTId().toString()); | |||
| if (propertyR.isFail()) { | |||
| return AjaxResult.warn("配置同步失败,请检查"); | |||
| } | |||
| return AjaxResult.success(propertyR.getData()); | |||
| } | |||
| } | |||
| return AjaxResult.success(); | |||
| @@ -264,6 +289,55 @@ public class ApiController { | |||
| return AjaxResult.success(System.currentTimeMillis() - Long.parseLong(timestamp) >10 * 60 * 1000 ? InitConstants.DEVICE_ACTIVATE_STATUS_OFFLINE : InitConstants.DEVICE_ACTIVATE_STATUS_ONLINE); | |||
| } | |||
| /** | |||
| * @Author yangkai | |||
| * @Description //TODO | |||
| * @Date 2023/8/11 | |||
| * @Param | |||
| * @return | |||
| * | |||
| * | |||
| * manCount | |||
| * serviceTimeCount | |||
| * chatTimes | |||
| * chatDurationCount | |||
| * skillExecuteTimes | |||
| * | |||
| * recognizedPersonCount | |||
| * servicePerCount | |||
| * | |||
| * receptionServiceCount | |||
| * meetingServiceCount | |||
| * visitServiceCount | |||
| * largeModelKnowledgeBaseGroupChangeNum | |||
| * largeModelKnowledgeBaseChangeNum | |||
| **/ | |||
| @RequestMapping(value = "/broadcast", method = {RequestMethod.GET}) | |||
| @ResponseBody | |||
| public R broadcast(@RequestParam(value = "channel") String channel){ | |||
| try { | |||
| JSONObject json = new JSONObject(); | |||
| /*json.put("chatTimes", 865531); | |||
| json.put("chatDurationCount", 10068); | |||
| // json.put("skillExecuteTimes", 10); | |||
| json.put("recognizedPersonCount", 13899); | |||
| json.put("receptionServiceCount", 1824); | |||
| json.put("meetingServiceCount", 1762); | |||
| json.put("visitServiceCount", 523);*/ | |||
| R<JSONObject> objectR = remoteDigitalmanService.mansInfo(); | |||
| JSONObject jsonObj = objectR.getData(); | |||
| jsonObj.keySet().forEach(key -> json.put(key, jsonObj.get(key))); | |||
| String str = json.toJSONString(); | |||
| messageQueueService.broadcast(channel, str); | |||
| return R.ok(str); | |||
| }catch (Exception e){ | |||
| e.printStackTrace(); | |||
| return R.fail(e.getMessage()); | |||
| } | |||
| } | |||
| @RequestMapping(value = "/get_activate/{snCode}", method = {RequestMethod.GET}) | |||
| @ResponseBody | |||
| public AjaxResult activate(@PathVariable(value = "snCode") String snCode, HttpServletResponse response) { | |||
| @@ -275,4 +349,20 @@ public class ApiController { | |||
| System.out.println(manDeviceDtR.getData().toString()); | |||
| return AjaxResult.success("true"); | |||
| } | |||
| @RequestMapping(value = "/get_config_info/{deviceId}", method = {RequestMethod.GET}) | |||
| @ResponseBody | |||
| public AjaxResult getConfigInfo(@PathVariable(value = "deviceId") String deviceId, HttpServletResponse response) { | |||
| // 根据设备id获取租户信息 | |||
| R<DmManDeviceDto> manDeviceDtoR = manDeviceService.manDeviceInfoInner(deviceId); | |||
| String tenantId = null; | |||
| if (manDeviceDtoR.isOk() && manDeviceDtoR.getData() != null) { | |||
| tenantId = manDeviceDtoR.getData().getTId().toString(); | |||
| } | |||
| R<String> propertyR = remoteH5ConfigService.syncH5Config(tenantId); | |||
| if (propertyR.isFail()) { | |||
| return AjaxResult.warn("配置同步失败,请检查"); | |||
| } | |||
| return AjaxResult.success(propertyR.getData()); | |||
| } | |||
| } | |||
| @@ -0,0 +1,19 @@ | |||
| package com.xueyi.message.transfer.service; | |||
| import com.corundumstudio.socketio.SocketIOClient; | |||
| import com.xueyi.message.api.transfer.domain.vo.Message; | |||
| import java.util.Collection; | |||
| import java.util.List; | |||
| public interface IMessageQueueService { | |||
| // public List<Message> getAll(); | |||
| // | |||
| // public boolean add(Message message); | |||
| public void broadcast(String channel, String message); | |||
| public Collection<SocketIOClient> getAllClients(); | |||
| } | |||
| @@ -0,0 +1,167 @@ | |||
| package com.xueyi.message.transfer.service.impl; | |||
| import com.alibaba.fastjson.JSON; | |||
| import com.alibaba.fastjson.JSONObject; | |||
| import com.corundumstudio.socketio.AckRequest; | |||
| import com.corundumstudio.socketio.Configuration; | |||
| import com.corundumstudio.socketio.SocketIOClient; | |||
| import com.corundumstudio.socketio.SocketIOServer; | |||
| import com.corundumstudio.socketio.listener.ConnectListener; | |||
| import com.corundumstudio.socketio.listener.DataListener; | |||
| import com.corundumstudio.socketio.listener.DisconnectListener; | |||
| import com.corundumstudio.socketio.listener.ExceptionListener; | |||
| import com.xueyi.common.core.utils.InetAddressUtils; | |||
| import com.xueyi.message.api.transfer.domain.vo.Message; | |||
| import com.xueyi.message.transfer.service.IMessageQueueService; | |||
| import io.netty.channel.ChannelHandlerContext; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.beans.factory.annotation.Value; | |||
| import org.springframework.data.redis.core.RedisTemplate; | |||
| import org.springframework.stereotype.Component; | |||
| import java.io.Serializable; | |||
| import java.util.*; | |||
| import java.util.concurrent.ConcurrentHashMap; | |||
| @Component | |||
| public class MessageQueueServiceImpl implements IMessageQueueService { | |||
| @Autowired | |||
| RedisTemplate<String, Serializable> redisTemplate; | |||
| private Integer port = 9901; | |||
| private SocketIOServer server; | |||
| private ConcurrentHashMap<String, Message> messageMap = new ConcurrentHashMap<>(); | |||
| MessageQueueServiceImpl(){ | |||
| startServer(port); | |||
| } | |||
| // @Override | |||
| // public List<Message> getAll(){ | |||
| // List<Message> list = new ArrayList<Message>(); | |||
| // Iterator<Map.Entry<String, Message>> entries = messageMap.entrySet().iterator(); | |||
| // while(entries.hasNext()){ | |||
| // Map.Entry<String, Message> entry = entries.next(); | |||
| // Message value = entry.getValue(); | |||
| // if(System.currentTimeMillis()- value.getTimestamp() > 10000){ | |||
| // messageMap.remove(entry.getKey()); | |||
| // }else{ | |||
| // list.add(value); | |||
| // } | |||
| // } | |||
| // if(list.size()>0){ | |||
| // return list; | |||
| // } | |||
| // return null; | |||
| // } | |||
| // | |||
| // @Override | |||
| // public boolean add(Message message){ | |||
| // | |||
| // messageMap.put(message.getVid(),message); | |||
| // return true; | |||
| // } | |||
| @Override | |||
| public void broadcast(String channel, String message){ | |||
| server.getBroadcastOperations().sendEvent(channel,message); | |||
| } | |||
| @Override | |||
| public Collection<SocketIOClient> getAllClients(){ | |||
| return server.getAllClients(); | |||
| } | |||
| private void startServer(int port){ | |||
| Configuration config = new Configuration(); | |||
| ExceptionListener exceptionListener = new ExceptionListener() { | |||
| @Override | |||
| public void onEventException(Exception e, List<Object> list, SocketIOClient socketIOClient) { | |||
| } | |||
| @Override | |||
| public void onDisconnectException(Exception e, SocketIOClient socketIOClient) { | |||
| } | |||
| @Override | |||
| public void onConnectException(Exception e, SocketIOClient socketIOClient) { | |||
| } | |||
| @Override | |||
| public boolean exceptionCaught(ChannelHandlerContext channelHandlerContext, Throwable throwable) throws Exception { | |||
| channelHandlerContext.close(); | |||
| return true; | |||
| } | |||
| }; | |||
| try { | |||
| System.out.println("启动"); | |||
| config.setHostname(InetAddressUtils.getLocalHostLANAddress().getHostAddress()); | |||
| } catch (Exception e) { | |||
| e.printStackTrace(); | |||
| } | |||
| config.setPort(port); | |||
| config.setExceptionListener(exceptionListener); | |||
| server = new SocketIOServer(config); | |||
| server.addConnectListener(new ConnectListener() { | |||
| // 添加客户端连接监听器 | |||
| @Override | |||
| public void onConnect(SocketIOClient client) { | |||
| System.out.println(client.getRemoteAddress().toString()); | |||
| if(client.getRemoteAddress().toString().contains("100.117") || client.getRemoteAddress().toString().contains("100.171")){ | |||
| client.disconnect(); | |||
| return; | |||
| } | |||
| client.sendEvent("connected", "hello"); | |||
| } | |||
| }); | |||
| server.addEventListener("client_info", String.class, new DataListener<String>(){ | |||
| @Override | |||
| public void onData(SocketIOClient client, String data, AckRequest ackRequest) throws ClassNotFoundException { | |||
| //客户端推送advert_info事件时,onData接受数据,这里是string类型的json数据,还可以为Byte[],object其他类型 | |||
| String sa = client.getRemoteAddress().toString(); | |||
| String clientIp = sa.substring(1,sa.indexOf(":"));//获取客户端连接的ip | |||
| Map params = client.getHandshakeData().getUrlParams();//获取客户端url参数 | |||
| System.out.println(clientIp+":客户端:************"+data); | |||
| } | |||
| }); | |||
| // server.addEventListener("remove_vinfromrsu", String.class, new DataListener<String>() { | |||
| // @Override | |||
| // public void onData(SocketIOClient socketIOClient, String s, AckRequest ackRequest) throws Exception { | |||
| // System.out.println("remove_vinfromrsu : " + s); | |||
| // List<RsuEventSimulatorMessage> list = JSON.parseArray(s, RsuEventSimulatorMessage.class); | |||
| // for (RsuEventSimulatorMessage item : list) { | |||
| // for (String vout : item.getVinouts()) { | |||
| // redisTemplate.opsForSet().remove("rsu_" + item.getRsuId(),vout); | |||
| // } | |||
| // } | |||
| // } | |||
| // }); | |||
| // server.addEventListener("screen_info", String.class, new DataListener<String>(){ | |||
| // @Override | |||
| // public void onData(SocketIOClient client, String data, AckRequest ackRequest) throws ClassNotFoundException { | |||
| // VehicleGridCountItem item = ((JSONObject)JSON.parse(data)).toJavaObject(VehicleGridCountItem.class); | |||
| // gridCountItemMap.put(client,item); | |||
| // } | |||
| // }); | |||
| //添加客户端断开连接事件 | |||
| server.addDisconnectListener(new DisconnectListener(){ | |||
| @Override | |||
| public void onDisconnect(SocketIOClient client) { | |||
| String sa = client.getRemoteAddress().toString(); | |||
| String clientIp = sa.substring(1,sa.indexOf(":"));//获取设备ip | |||
| System.out.println(clientIp+"-------------------------"+"客户端已断开连接"); | |||
| //给客户端发送消息 | |||
| client.sendEvent("advert_info",clientIp+"客户端你好,我是服务端,期待下次和你见面"); | |||
| } | |||
| }); | |||
| server.start(); | |||
| } | |||
| } | |||
| @@ -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"] | |||
| @@ -0,0 +1,112 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
| xmlns="http://maven.apache.org/POM/4.0.0" | |||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
| <parent> | |||
| <groupId>com.xueyi</groupId> | |||
| <artifactId>xueyi-modules</artifactId> | |||
| <version>2.5.0</version> | |||
| </parent> | |||
| <modelVersion>4.0.0</modelVersion> | |||
| <artifactId>xueyi-modules-nlt</artifactId> | |||
| <description> | |||
| xueyi-modules-nlt大模型处理模块 | |||
| </description> | |||
| <dependencies> | |||
| <!-- SpringCloud Alibaba Nacos --> | |||
| <dependency> | |||
| <groupId>com.alibaba.cloud</groupId> | |||
| <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> | |||
| </dependency> | |||
| <!-- SpringCloud Alibaba Nacos Config --> | |||
| <dependency> | |||
| <groupId>com.alibaba.cloud</groupId> | |||
| <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> | |||
| </dependency> | |||
| <!-- https://mvnrepository.com/artifact/com.squareup.okhttp/okhttp --> | |||
| <dependency> | |||
| <groupId>com.squareup.okhttp3</groupId> | |||
| <artifactId>okhttp</artifactId> | |||
| <version>3.9.1</version> | |||
| </dependency> | |||
| <!-- SpringCloud Alibaba Sentinel --> | |||
| <dependency> | |||
| <groupId>com.alibaba.cloud</groupId> | |||
| <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> | |||
| </dependency> | |||
| <!-- SpringBoot Actuator --> | |||
| <dependency> | |||
| <groupId>org.springframework.boot</groupId> | |||
| <artifactId>spring-boot-starter-actuator</artifactId> | |||
| </dependency> | |||
| <!-- XueYi Common Log --> | |||
| <dependency> | |||
| <groupId>com.xueyi</groupId> | |||
| <artifactId>xueyi-common-log</artifactId> | |||
| </dependency> | |||
| <!-- XueYi Common Web --> | |||
| <dependency> | |||
| <groupId>com.xueyi</groupId> | |||
| <artifactId>xueyi-common-web</artifactId> | |||
| </dependency> | |||
| <!-- XueYi Common Swagger --> | |||
| <dependency> | |||
| <groupId>com.xueyi</groupId> | |||
| <artifactId>xueyi-common-swagger</artifactId> | |||
| </dependency> | |||
| <!-- XueYi Api File --> | |||
| <dependency> | |||
| <groupId>com.xueyi</groupId> | |||
| <artifactId>xueyi-api-nlt</artifactId> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.xueyi</groupId> | |||
| <artifactId>xueyi-api-system</artifactId> | |||
| </dependency> | |||
| </dependencies> | |||
| <build> | |||
| <finalName>${project.artifactId}</finalName> | |||
| <plugins> | |||
| <plugin> | |||
| <groupId>org.springframework.boot</groupId> | |||
| <artifactId>spring-boot-maven-plugin</artifactId> | |||
| <executions> | |||
| <execution> | |||
| <goals> | |||
| <goal>repackage</goal> | |||
| </goals> | |||
| </execution> | |||
| </executions> | |||
| </plugin> | |||
| <plugin> | |||
| <artifactId>maven-resources-plugin</artifactId> | |||
| <groupId>org.apache.maven.plugins</groupId> | |||
| <configuration> | |||
| <delimiters>@</delimiters> | |||
| <useDefaultDelimiters>false</useDefaultDelimiters> | |||
| </configuration> | |||
| </plugin> | |||
| </plugins> | |||
| <resources> | |||
| <resource> | |||
| <directory>src/main/resources</directory> | |||
| <filtering>true</filtering> | |||
| </resource> | |||
| </resources> | |||
| </build> | |||
| </project> | |||
| @@ -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 | |||
| @@ -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" + | |||
| " '--' '----'`-..-' "); | |||
| } | |||
| } | |||
| @@ -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<SocketChannel>() { | |||
| @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(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,374 @@ | |||
| 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.baomidou.mybatisplus.core.toolkit.StringUtils; | |||
| import com.xueyi.nlt.api.netty.domain.vo.DmWebSocketMessageVo; | |||
| import okhttp3.*; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.beans.factory.annotation.Value; | |||
| 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.*; | |||
| @Component | |||
| public class WebSocketClient extends WebSocketListener { | |||
| public static WebSocketClient INSTANCE; | |||
| @Autowired | |||
| private RedisTemplate redisTemplate; | |||
| @Autowired | |||
| private StringRedisTemplate stringRedisTemplate; | |||
| @Value("${secret.spark.appId}") | |||
| private String appId; | |||
| @Value("${secret.spark.apiSecret}") | |||
| private String apiSecret; | |||
| @Value("${secret.spark.apiKey}") | |||
| private String apiKey; | |||
| public final static Object LOCK = new Object(); | |||
| public static String hostUrl = "https://spark-api.xf-yun.com/v2.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 static List<String> questions = new ArrayList<>();//可以修改question 内容,来向模型提问 | |||
| public String answer = ""; | |||
| @PostConstruct | |||
| public void init() { | |||
| INSTANCE = this; | |||
| INSTANCE.redisTemplate = this.redisTemplate; | |||
| INSTANCE.stringRedisTemplate = this.stringRedisTemplate; | |||
| INSTANCE.appId = this.appId; | |||
| } | |||
| public static void main(String[] args) { | |||
| synchronized (LOCK) { | |||
| try { | |||
| //构建鉴权httpurl | |||
| String authUrl = getAuthorizationUrl(hostUrl, INSTANCE.apiKey, INSTANCE.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(); | |||
| } | |||
| } | |||
| public void sendMsg(List<String> messages){ | |||
| questions = messages; | |||
| question = null; | |||
| 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",INSTANCE.appId); | |||
| header.addProperty("uid","123456789"); | |||
| //填充parameter | |||
| // chat.addProperty("domain","general"); //1.0版本 | |||
| chat.addProperty("domain","generalv2"); // 2.0版本 | |||
| chat.addProperty("random_threshold",0.5); | |||
| chat.addProperty("max_tokens",1024); | |||
| chat.addProperty("auditing","default"); | |||
| parameter.add("chat",chat); | |||
| if (!StringUtils.isEmpty(question)) { | |||
| text = new JsonObject(); | |||
| //填充payload | |||
| text.addProperty("role","user"); | |||
| text.addProperty("content",question); | |||
| ja.add(text); | |||
| }else { | |||
| for (int i = 0;i < questions.size();i++) { | |||
| text = new JsonObject(); | |||
| if (i % 2 == 0) { | |||
| text.addProperty("role","user"); | |||
| } else { | |||
| text.addProperty("role","assistant"); | |||
| } | |||
| text.addProperty("content",questions.get(i)); | |||
| System.out.println(text.toString()); | |||
| 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"); | |||
| if (StringUtils.isNotEmpty(message.getTemplate()) && message.getTemplate().equals("birthday")) { | |||
| JSONObject birthdayJo = new JSONObject(); | |||
| birthdayJo.put("content", answer); | |||
| birthdayJo.put("timestamp", message.getFormat().get("timestamp")); | |||
| INSTANCE.stringRedisTemplate.opsForHash().put("group:nlp" + ":" + message.getFormat().getString("orderId"), "birthday", birthdayJo.toString()); | |||
| INSTANCE.redisTemplate.delete("gpt:websocket:1"); | |||
| return; | |||
| } | |||
| if (StringUtils.isNotEmpty(message.getTemplate()) && message.getTemplate().equals("hireDate")) { | |||
| JSONObject birthdayJo = new JSONObject(); | |||
| birthdayJo.put("content", answer); | |||
| birthdayJo.put("timestamp", message.getFormat().get("timestamp")); | |||
| INSTANCE.stringRedisTemplate.opsForHash().put("group:nlp" + ":" + message.getFormat().getString("orderId"), "hireDate", birthdayJo.toString()); | |||
| INSTANCE.redisTemplate.delete("gpt:websocket:1"); | |||
| return; | |||
| } | |||
| 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; | |||
| } | |||
| } | |||
| } | |||
| @@ -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<String, Object> 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<String, Object> getSuffixParams() { | |||
| return suffixParams; | |||
| } | |||
| public void setSuffixParams(Map<String, Object> 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; | |||
| } | |||
| } | |||
| @@ -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); | |||
| } | |||
| } | |||
| @@ -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<String> { | |||
| // 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(); | |||
| } | |||
| } | |||
| @@ -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<TextWebSocketFrame> { | |||
| // 定义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"); | |||
| } | |||
| } | |||
| @@ -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<Object> { | |||
| 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<String, String> 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; | |||
| // } | |||
| } | |||
| @@ -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<TextWebSocketFrame> { | |||
| 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关联起来,就可以做定向推送,而不是广播 | |||
| } | |||
| } | |||
| @@ -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); | |||
| } | |||
| @@ -0,0 +1,79 @@ | |||
| package com.xueyi.nlt.netty.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.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"); | |||
| if (StringUtils.isNotEmpty(message.getTemplate()) && message.getTemplate().equals("birthday")) { | |||
| String prefix = "假设你是一名公司前台,你看到" + message.getFormat().get("name")+ "已知今天是他的生日。请你从个人角度输出给他的生日贺词。要求待人平和,具有人情味,用词正式,内容与工作无关。输出只包含你要对他说的话,在20字以内。"; | |||
| webSocketClient.sendMsg(prefix); | |||
| redisTemplate.opsForValue().set("gpt:websocket" + ":" + "1", message); | |||
| return R.ok(); | |||
| } | |||
| if (StringUtils.isNotEmpty(message.getTemplate()) && message.getTemplate().equals("hiredate")) { | |||
| String prefix = "假设你是一名公司前台,你看到"+ message.getFormat().get("name")+ ",已知今天是他入职1周年,请你从个人角度说出对他入职周年的祝贺。要求具有人情味,有特色,字数在25字左右,不要提到生日,要带人名。输出只包含你要对他说的话。"; | |||
| webSocketClient.sendMsg(prefix); | |||
| redisTemplate.opsForValue().set("gpt:websocket" + ":" + "1", message); | |||
| return R.ok(); | |||
| } | |||
| 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(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,603 @@ | |||
| package com.xueyi.nlt.nlt.controller; | |||
| import co.elastic.clients.elasticsearch.ElasticsearchClient; | |||
| import co.elastic.clients.elasticsearch._types.query_dsl.MatchAllQuery; | |||
| import co.elastic.clients.elasticsearch._types.query_dsl.MatchQuery; | |||
| import co.elastic.clients.elasticsearch.core.SearchResponse; | |||
| import co.elastic.clients.elasticsearch.core.search.Hit; | |||
| import com.alibaba.fastjson2.JSONArray; | |||
| 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.constant.digitalman.MessageConstants; | |||
| import com.xueyi.common.core.context.SecurityContextHolder; | |||
| 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.security.utils.SecurityUtils; | |||
| 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.*; | |||
| import com.xueyi.nlt.api.nlt.feign.RemoteIntentService; | |||
| import com.xueyi.nlt.api.nlt.feign.RemoteLandingLlmService; | |||
| import com.xueyi.nlt.api.nlt.feign.RemoteQAService; | |||
| import com.xueyi.nlt.netty.client.WebSocketClient; | |||
| import com.xueyi.nlt.nlt.domain.dto.DmIntentDto; | |||
| import com.xueyi.nlt.nlt.domain.query.DmIntentQuery; | |||
| import com.xueyi.nlt.nlt.domain.vo.IntentTemplateVo; | |||
| import com.xueyi.nlt.nlt.service.IDmIntentService; | |||
| import com.xueyi.nlt.nlt.template.FreeChatTemplate; | |||
| import com.xueyi.nlt.nlt.template.GenerativeKnowledgeTemplate; | |||
| import com.xueyi.nlt.nlt.template.MeetingOrderTemplate; | |||
| import com.xueyi.system.api.digitalmans.domain.dto.DmManDeviceDto; | |||
| import com.xueyi.system.api.digitalmans.domain.vo.DmBatchQuestionsVo; | |||
| import com.xueyi.system.api.digitalmans.feign.RemoteDigitalmanService; | |||
| import com.xueyi.system.api.digitalmans.feign.RemoteManDeviceService; | |||
| import com.xueyi.system.api.digitalmans.feign.RemoteQuestionanswersService; | |||
| import com.xueyi.system.api.model.Source; | |||
| import com.xueyi.system.api.organize.domain.dto.SysEnterpriseDto; | |||
| import com.xueyi.system.api.organize.feign.RemoteEnterpriseService; | |||
| import com.xueyi.tenant.api.tenant.domain.dto.TeTenantDto; | |||
| import com.xueyi.tenant.api.tenant.feign.RemoteTenantService; | |||
| import org.apache.commons.lang.time.DateUtils; | |||
| 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 com.xueyi.common.core.constant.digitalman.SkillConstants.SkillType; | |||
| import java.io.IOException; | |||
| import java.io.Serializable; | |||
| import java.text.DateFormat; | |||
| import java.text.SimpleDateFormat; | |||
| import java.util.Arrays; | |||
| import java.time.Instant; | |||
| import java.time.LocalDateTime; | |||
| import java.time.ZoneId; | |||
| import java.util.Date; | |||
| import java.util.List; | |||
| import java.util.Map; | |||
| import java.util.regex.Matcher; | |||
| import java.util.regex.Pattern; | |||
| import java.util.concurrent.TimeUnit; | |||
| import java.util.stream.Collectors; | |||
| import static com.xueyi.common.core.constant.digitalman.MessageConstants.SYS_DICT_DATA_SPLITED; | |||
| @RestController | |||
| @RequestMapping("/intent") | |||
| public class DmIntentController extends BaseController<DmIntentQuery, DmIntentDto, IDmIntentService> { | |||
| 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<Object,Object> redisTemplate2; | |||
| @Autowired | |||
| private RemoteManDeviceService manDeviceService; | |||
| @Autowired | |||
| private RemoteIntentService remoteIntentService; | |||
| @Autowired | |||
| private MeetingOrderTemplate meetingOrderTemplate; | |||
| @Autowired | |||
| private GenerativeKnowledgeTemplate generativeKnowledgeTemplate; | |||
| @Autowired | |||
| private FreeChatTemplate freeChatTemplate; | |||
| @Autowired | |||
| private RemoteQuestionanswersService questionanswersService; | |||
| @Autowired | |||
| RemoteEnterpriseService enterpriseService; | |||
| @Autowired | |||
| RemoteLandingLlmService remoteLandingLlmService; | |||
| @Autowired | |||
| RemoteQAService remoteQAService; | |||
| @Autowired | |||
| RemoteDigitalmanService remoteDigitalmanService; | |||
| @Autowired | |||
| private ElasticsearchClient esClient; | |||
| /** | |||
| * 意图请求 | |||
| 列表 | |||
| */ | |||
| @PostMapping("/api/conversation") | |||
| @ResponseBody | |||
| public AjaxResult conversationApi(@RequestBody DmIntentVo intent) { | |||
| log.info("对话详情:{}", intent.toString()); | |||
| log.info("交互对象:{}", intent.toString()); | |||
| redisTemplate.opsForValue().increment("dashboard:server", 1); | |||
| // 获取今天日期,并格式化成yyyy-MM-dd | |||
| SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); | |||
| String today = sdf.format(new Date()); | |||
| redisTemplate.opsForValue().increment(("dashboard:server-chart:" + today), 1); | |||
| R<DmManDeviceDto> manDeviceDtoR = manDeviceService.manDeviceInfoInner(intent.getDevId()); | |||
| Source source = SourceUtil.getSourceCache(manDeviceDtoR.getData().getStrategyId()); | |||
| R<JSONObject> jsonObjectR = remoteIntentService.conversationInner(intent, manDeviceDtoR.getData().getTId(), source.getMaster(), SecurityConstants.INNER); | |||
| IntentTemplateVo voResult = new IntentTemplateVo(); | |||
| voResult.setMsg(""); | |||
| if (jsonObjectR.getData() != null) { | |||
| if (jsonObjectR.getData().containsKey("result") && StringUtils.isEmpty(jsonObjectR.getData().getString("result")) ) { | |||
| voResult.setTarget(0); | |||
| } else { | |||
| voResult.setTarget(1); | |||
| } | |||
| } else { | |||
| voResult.setTarget(0); | |||
| } | |||
| if (intent.getMode() != null && intent.getMode().equals(MessageConstants.MODE_FREE_CHAT)) { | |||
| voResult.setMsg(jsonObjectR.getData().get("msg").toString()); | |||
| } | |||
| else { | |||
| voResult.setFormat(jsonObjectR.getData()); | |||
| } | |||
| return AjaxResult.success(voResult); | |||
| } | |||
| @PostMapping("/api/skill-intent") | |||
| @ResponseBody | |||
| public AjaxResult skillIntentApi(@RequestBody DmIntentVo intent) { | |||
| log.info("交互对象:{}", intent.toString()); | |||
| redisTemplate.opsForValue().increment("dashboard:server", 1); | |||
| R<DmManDeviceDto> manDeviceDtoR = manDeviceService.manDeviceInfoInner(intent.getDevId()); | |||
| Source source = SourceUtil.getSourceCache(manDeviceDtoR.getData().getStrategyId()); | |||
| IntentTemplateVo voResult = new IntentTemplateVo(); | |||
| //查询es,将满足条件的数据调用后续处理,不满足条件的返回原始数据 | |||
| SearchResponse<JSONObject> dateResponse = null; | |||
| List<Hit<JSONObject>> question_list = null; | |||
| try { | |||
| MatchQuery byName = MatchQuery.of(m->m.field("name").query(intent.getContent())); | |||
| dateResponse = esClient.search(builder -> builder | |||
| .index("regular_expression").query(q->q.match(byName)), | |||
| JSONObject.class); | |||
| question_list = dateResponse.hits().hits(); | |||
| log.info("esClient search result: {}", dateResponse); | |||
| }catch (IOException ie) { | |||
| log.error("esClient search error", ie); | |||
| return null; | |||
| } | |||
| for (Hit<JSONObject> jsonObjectHit : question_list) { | |||
| JSONObject jo = jsonObjectHit.source(); | |||
| Pattern pat = Pattern.compile(jo.get("name").toString()); | |||
| Matcher m = pat.matcher(intent.getContent()); | |||
| if (m.find()) { | |||
| // R<JSONObject> jsonObjectR = remoteIntentService.conversationInner(intent, manDeviceDtoR.getData().getTId(), source.getMaster(), SecurityConstants.INNER); | |||
| voResult.setMsg(jo.get("answer").toString()); | |||
| // 如果包含Json数据并且json的skillCode字段不等于空 | |||
| if (jo.containsKey("json") && StringUtils.isNotEmpty(jo.getString("json"))) { | |||
| String skillCode = jo.getJSONObject("json").getString("skillCode"); | |||
| intent.setSkillCode(skillCode); | |||
| voResult.setJsonFormat(jo.getJSONObject("json")); | |||
| voResult.setType("1"); | |||
| } | |||
| // return AjaxResult.success(voResult); | |||
| } | |||
| } | |||
| //走知识库 | |||
| R<JSONObject> jsonObjectR = remoteIntentService.conversationInner(intent, manDeviceDtoR.getData().getTId(), source.getMaster(), SecurityConstants.INNER); | |||
| if (jsonObjectR.getData() != null) { | |||
| if (jsonObjectR.getData().containsKey("result") && StringUtils.isEmpty(jsonObjectR.getData().getString("result")) ) { | |||
| voResult.setTarget(0); | |||
| } else { | |||
| voResult.setTarget(1); | |||
| } | |||
| } else { | |||
| voResult.setTarget(0); | |||
| } | |||
| if (intent.getMode() != null && intent.getMode().equals(MessageConstants.MODE_FREE_CHAT)) { | |||
| voResult.setMsg(jsonObjectR.getData().get("msg").toString()); | |||
| } | |||
| else { | |||
| 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); | |||
| redisTemplate.opsForValue().increment("dashboard:conversation-times", 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"); | |||
| redisTemplate.opsForValue().increment("dashboard:conversation-duration", jo.get("content").toString().length() / 4); | |||
| return AjaxResult.success(joResult); | |||
| } else if (Double.valueOf(String.valueOf(jo.get("timestamp"))) - System.currentTimeMillis() < 0) { | |||
| redisTemplate.opsForHash().delete("group:nlp" + ":" + recognition.getPersonId(), "meeting"); | |||
| } | |||
| } else { | |||
| JSONObject jo = JSONObject.parseObject(value); | |||
| JSONObject joresult = addNotificaion(String.valueOf(key),recognition.getPersonId(),jo); | |||
| if (joresult!= null) { | |||
| return AjaxResult.success(joresult); | |||
| } | |||
| joResult.put("msg",value); | |||
| redisTemplate.opsForValue().increment("dashboard:conversation-duration", value.toString().length() / 4); | |||
| joResult.put("target",1); | |||
| redisTemplate.opsForHash().delete("group:nlp" + ":" + recognition.getPersonId(),key); | |||
| return AjaxResult.success(joResult); | |||
| } | |||
| } | |||
| } | |||
| redisTemplate.opsForValue().increment("dashboard:conversation-duration", 3); | |||
| return AjaxResult.success(joResult); | |||
| } | |||
| /** | |||
| * 知识库问答请求 | |||
| */ | |||
| @PostMapping("/api/searchQA") | |||
| @ResponseBody | |||
| public AjaxResult searchQA(@RequestBody DmIntentVo intent) { | |||
| log.info("交互对象:{}",intent.toString()); | |||
| R<DmManDeviceDto> manDeviceDtoR = manDeviceService.manDeviceInfoInner(intent.getDevId()); | |||
| Source source = SourceUtil.getSourceCache(manDeviceDtoR.getData().getStrategyId()); | |||
| KnowledgeVo knowledgeVo = new KnowledgeVo(); | |||
| knowledgeVo.setMan_code(manDeviceDtoR.getData().getManCode()); | |||
| knowledgeVo.setTenant_id(manDeviceDtoR.getData().getTId()); | |||
| knowledgeVo.setQuestion(intent.getContent()); | |||
| // return remoteQAService.query(manDeviceDtoR.getData().getManCode(),intent.getContent(),manDeviceDtoR.getData().getTId()); | |||
| return remoteQAService.query(knowledgeVo); | |||
| } | |||
| /** | |||
| * 联动北方大模型请求 | |||
| */ | |||
| @PostMapping("/api/searchQuestionAnswer") | |||
| @ResponseBody | |||
| public AjaxResult searchQuestionAnswer(@RequestBody DmIntentVo intent) { | |||
| log.info("交互对象:{}",intent.toString()); | |||
| redisTemplate.opsForValue().increment("dashboard:recognition", 1); | |||
| JSONObject joResult = new JSONObject(); | |||
| joResult.put("msg",""); | |||
| joResult.put("target",0); | |||
| DmLandingLlmVo vo = new DmLandingLlmVo(); | |||
| vo.setCategory("bj_unicom"); | |||
| vo.addDmLlm("user", intent.getContent()); | |||
| JSONObject testJ = new JSONObject(); | |||
| testJ.put("category","bj_unicom"); | |||
| JSONArray ja1 = new JSONArray(); | |||
| JSONObject jo = new JSONObject(); | |||
| jo.put("role","user"); | |||
| jo.put("content",intent.getContent()); | |||
| ja1.add(jo); | |||
| testJ.put("messages",ja1); | |||
| JSONObject resultJson = remoteLandingLlmService.query(testJ); | |||
| JSONArray ja = resultJson.getJSONArray("data"); | |||
| if (ja.size() > 0) { | |||
| joResult.put("msg",ja.getJSONObject(0).get("text")); | |||
| joResult.put("target",1); | |||
| } | |||
| return AjaxResult.success(joResult); | |||
| } | |||
| /** | |||
| * 意图请求列表 | |||
| */ | |||
| @PostMapping("/inner/conversation") | |||
| @ResponseBody | |||
| public R<JSONObject> conversationInner(@RequestBody DmIntentVo intent) { | |||
| JSONObject joResult = null; | |||
| if (intent.getMode() !=null && intent.getMode().equals(MessageConstants.MODE_FREE_CHAT)) { | |||
| // 闲聊 | |||
| joResult = freeChatTemplate.handle(intent.getDevId(),intent.getContent()); | |||
| return R.ok(joResult); | |||
| } | |||
| // 获取设备号对应缓存 | |||
| CoversationSessionVo sessionObject = (CoversationSessionVo) redisTemplate2.opsForValue().get("group:device" + ":" + intent.getDevId() + ":" +"session"); //category | |||
| if (sessionObject != null) { | |||
| switch (sessionObject.getCategory()) { | |||
| case "meeting": | |||
| joResult = meetingOrderTemplate.handle(intent.getDevId(),intent.getContent(), Long.parseLong(SecurityContextHolder.getLocalMap().get("enterprise_id").toString())); | |||
| return R.ok(joResult); | |||
| } | |||
| } | |||
| if (StringUtils.isEmpty(intent.getSkillCode())) { | |||
| // 调用知识库问答 | |||
| AjaxResult qaAjax = searchQA(intent); | |||
| return R.ok(JSONObject.from(qaAjax.get("data"))); | |||
| } | |||
| SkillType.BOOK_MEETING_ROOM.getCode(); | |||
| // 判断skill code的值 | |||
| if (SkillType.BOOK_MEETING_ROOM.getCode().equals(intent.getSkillCode())) { | |||
| // 获取名称为"meeting-order"的BaseTemplate的实例 | |||
| redisTemplate.opsForValue().increment("dashboard:meeting", 1); | |||
| joResult = meetingOrderTemplate.handle(intent.getDevId(),intent.getContent(), Long.parseLong(SecurityContextHolder.getLocalMap().get("enterprise_id").toString())); | |||
| } else if (SkillType.CREATE_VISITOR_INFO.getCode().equals(intent.getSkillCode())) { | |||
| // 访客预定 | |||
| redisTemplate.opsForValue().increment("dashboard:create_visitor_info", 1); | |||
| } else if (SkillType.REGISTER_VISITOR.getCode().equals(intent.getSkillCode())) { | |||
| // 访客登记 | |||
| redisTemplate.opsForValue().increment("dashboard:register_visitor", 1); | |||
| } else if (SkillType.BROADCAST_DISPLAY.getCode().equals(intent.getSkillCode())) { | |||
| // 播报展示 | |||
| redisTemplate.opsForValue().increment("dashboard:broadcast_display", 1); | |||
| } else if (SkillType.OPEN_DOOR.getCode().equals(intent.getSkillCode())) { | |||
| // 开门记录 | |||
| redisTemplate.opsForValue().increment("dashboard:open_door", 1); | |||
| } | |||
| return R.ok(joResult); | |||
| } | |||
| /** | |||
| * 意图请求 | |||
| 列表 | |||
| */ | |||
| @PostMapping("/inner/sendMessage") | |||
| @ResponseBody | |||
| public R<Object> 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"); | |||
| SimpleDateFormat dateFormat4 = new SimpleDateFormat("M月d日"); | |||
| Double timestamp = Double.valueOf((String)jo.get("timestamp")); | |||
| String meetingRoom = jo.getString("meetingRoom"); | |||
| if (StringUtils.isNotEmpty(message.getTemplate()) && message.getTemplate().equals("birthday")) { | |||
| String prefix = "假设你是一名公司前台,你看到" + message.getFormat().get("name")+ "已知今天是他的生日。请你从个人角度输出给他的生日贺词。要求待人平和,具有人情味,用词正式,内容与工作无关。输出只包含你要对他说的话,在20字以内。"; | |||
| webSocketClient.sendMsg(prefix); | |||
| redisTemplate2.opsForValue().set("gpt:websocket" + ":" + "1", message); | |||
| return R.ok(); | |||
| } | |||
| if (StringUtils.isNotEmpty(message.getTemplate()) && message.getTemplate().equals("hireDate")) { | |||
| String prefix = "假设你是一名公司前台,你看到"+ message.getFormat().get("name")+ ",已知今天是他入职" + message.getFormat().get("years")+"周年,请你从个人角度说出对他入职周年的祝贺。要求具有人情味,有特色,字数在25字左右,不要提到生日,要带人名。输出只包含你要对他说的话。"; | |||
| webSocketClient.sendMsg(prefix); | |||
| redisTemplate2.opsForValue().set("gpt:websocket" + ":" + "1", message); | |||
| return R.ok(); | |||
| } | |||
| Date date = new Date(timestamp.longValue()); | |||
| if (message.getSkillCode().equals("1")) { | |||
| String prefix = "假设你是一名公司前台,你看到在你们公司工作的\\"+ jo.getString("orderName")+ "\\,请你从个人角度提醒他参加\\" + | |||
| dateFormat4.format(timestamp) + "\\在\\" + meetingRoom + "\\的会,要求语气友好。输出只包含你要对他说的话,在20字左右。"; | |||
| webSocketClient.sendMsg(prefix); | |||
| // 设置缓存 | |||
| redisTemplate2.opsForValue().set("gpt:websocket" + ":" + "1", message); | |||
| } | |||
| return R.ok(); | |||
| } | |||
| /** | |||
| * 意图请求 | |||
| 列表 | |||
| */ | |||
| @PostMapping("/inner/taskGenerativeKnowledge") | |||
| @ResponseBody | |||
| public R<Object> taskGenerativeKnowledge() { | |||
| // 从列表中获取知识库任务 | |||
| TaskKnowledgeVo vo = (TaskKnowledgeVo) redisTemplate2.opsForList().leftPop("group:task"); | |||
| log.info("从缓存中获取对象:{}",vo.toString()); | |||
| if (vo == null) { | |||
| return R.ok(null,"没有任务"); | |||
| } | |||
| if (vo.getSplit() != SYS_DICT_DATA_SPLITED) { | |||
| // 请开始你的表演 | |||
| String contents = vo.getContent(); | |||
| List<String> results = Arrays.stream(contents.split("\n")).filter(StringUtils::isNotBlank).collect(Collectors.toList()); | |||
| results.forEach(item->{ | |||
| TaskKnowledgeVo temp = new TaskKnowledgeVo(); | |||
| temp.setContent(item); | |||
| temp.setCount(item.length()); | |||
| temp.setManId(vo.getManId()); | |||
| temp.setKnowledgeId(vo.getKnowledgeId()); | |||
| temp.setTenantId(vo.getTenantId()); | |||
| temp.setSplit(SYS_DICT_DATA_SPLITED); | |||
| temp.setCreateBy(vo.getCreateBy()); | |||
| redisTemplate2.opsForList().rightPush("group:task", temp); | |||
| }); | |||
| return R.ok(null,"处理成功"); | |||
| } | |||
| log.info("交互对象:{}",vo.toString()); | |||
| JSONObject joResult = generativeKnowledgeTemplate.handle(null,vo.getContent()); | |||
| if (joResult == null) { | |||
| redisTemplate2.opsForList().rightPush("group:task", vo); | |||
| return R.ok(null,"任务处理失败,重新放入队列"); | |||
| } | |||
| R<SysEnterpriseDto> enterpriseDtoR = enterpriseService.getInfo(vo.getTenantId()); | |||
| if (enterpriseDtoR.isFail()) { | |||
| log.warn("任务执行失败:{}",vo); | |||
| } | |||
| Source source = SourceUtil.getSourceCache(enterpriseDtoR.getData().getStrategyId()); | |||
| DmBatchQuestionsVo batchQuestionsVo = new DmBatchQuestionsVo(); | |||
| batchQuestionsVo.setKnowledgeId(vo.getKnowledgeId()); | |||
| batchQuestionsVo.setQuestions(JSONArray.from(joResult.get("questions"))); | |||
| batchQuestionsVo.setCreateId(vo.getCreateBy()); | |||
| questionanswersService.batchInsertInner(batchQuestionsVo,vo.getTenantId(),source.getMaster(),SecurityConstants.INNER); | |||
| return R.ok(null,"处理成功"); | |||
| } | |||
| /** | |||
| * 查询意图管理 | |||
| 列表 | |||
| */ | |||
| @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<Long> idList) { | |||
| return super.batchRemove(idList); | |||
| } | |||
| /** | |||
| * 获取意图管理 | |||
| 选择框列表 | |||
| */ | |||
| @Override | |||
| @GetMapping("/option") | |||
| public AjaxResult option() { | |||
| return super.option(); | |||
| } | |||
| private JSONObject addNotificaion(String key,Long personId,JSONObject jo) { | |||
| JSONObject joResult = new JSONObject(); | |||
| if (String.valueOf(key).equals("birthday") || String.valueOf(key).equals("hireDate")) { | |||
| // 获取value中的timestamp,判断是否是当天 | |||
| if (jo.containsKey("timestamp")) { | |||
| // 判断jo.get("timestamp")的月份是否与当天相同 | |||
| DateFormat dateFormat = new SimpleDateFormat("MM-dd"); | |||
| if (dateFormat.format(new Date(Long.valueOf(String.valueOf(jo.get("timestamp"))))).equals(dateFormat.format(new Date()))) { | |||
| joResult.put("msg", jo.get("content")); | |||
| joResult.put("target", 1); | |||
| redisTemplate.opsForHash().delete("group:nlp" + ":" + personId, key); | |||
| redisTemplate.opsForValue().increment("dashboard:conversation-duration", jo.get("content").toString().length() / 4); | |||
| return joResult; | |||
| } | |||
| } | |||
| } | |||
| return joResult; | |||
| } | |||
| 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"; | |||
| } | |||
| } | |||
| @@ -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; | |||
| } | |||
| @@ -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<DmIntentQuery, DmIntentDto, DmIntentPo> { | |||
| } | |||
| @@ -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; | |||
| } | |||
| @@ -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; | |||
| } | |||
| @@ -0,0 +1,19 @@ | |||
| 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; | |||
| private String skillCode; | |||
| private JSONObject jsonFormat; | |||
| private String type ="0"; | |||
| } | |||
| @@ -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<DmIntentQuery, DmIntentDto> { | |||
| } | |||
| @@ -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<DmIntentQuery, DmIntentDto, DmIntentPo, DmIntentMapper, DmIntentConverter> implements IDmIntentManager { | |||
| } | |||
| @@ -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<DmIntentQuery, DmIntentDto, DmIntentPo> { | |||
| } | |||
| @@ -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<DmIntentQuery, DmIntentDto> { | |||
| } | |||
| @@ -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<DmIntentQuery, DmIntentDto, IDmIntentManager> implements IDmIntentService { | |||
| } | |||
| @@ -0,0 +1,8 @@ | |||
| package com.xueyi.nlt.nlt.template; | |||
| import com.alibaba.fastjson2.JSONObject; | |||
| public interface BaseTemplate { | |||
| JSONObject handle(String dev, String content); | |||
| JSONObject handle(String dev, String content,Long tenantId); | |||
| } | |||
| @@ -0,0 +1,61 @@ | |||
| package com.xueyi.nlt.nlt.template; | |||
| import com.alibaba.fastjson2.JSONArray; | |||
| import com.alibaba.fastjson2.JSONException; | |||
| import com.alibaba.fastjson2.JSONObject; | |||
| import com.xueyi.nlt.netty.client.WebSocketClient; | |||
| 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.stereotype.Service; | |||
| import java.util.List; | |||
| @Service("free-chat") | |||
| public class FreeChatTemplate implements BaseTemplate{ | |||
| private static final Logger log = LoggerFactory.getLogger(FreeChatTemplate.class); | |||
| @Autowired | |||
| WebSocketClient webSocketClient; | |||
| @Autowired | |||
| private RedisTemplate<String,String> redisTemplate; | |||
| @Override | |||
| public JSONObject handle(String devId, String content) { | |||
| // 根据content内容调用模版并返回结果 | |||
| synchronized (WebSocketClient.LOCK) { | |||
| // 通过redis获取数字人上下文信息 | |||
| Long size = redisTemplate.opsForList().size("group:device:" + devId); | |||
| if (size > 8) { | |||
| redisTemplate.opsForList().leftPop("group:device:" + devId,2); | |||
| } | |||
| redisTemplate.opsForList().rightPush("group:device:" + devId,content); | |||
| List<String> context = redisTemplate.opsForList().range("group:device:" + devId,0,size); | |||
| String prefix = "你的任务是[针对给定的文段提出" + (content.length() /100 + 1 ) + "个问题并回答]。文段为:[\""; | |||
| String suffix = "\"]。输出为一个JSON数组[{}],每个元素是一个JSON:{“question”:,”answer”:}。不要给出任何解释说明。"; | |||
| log.info(prefix + content + suffix); | |||
| // webSocketClient.sendMsg(prefix + content + suffix); | |||
| webSocketClient.sendMsg(context); | |||
| try { | |||
| WebSocketClient.LOCK.wait(); | |||
| String result = (String)redisTemplate.opsForValue().get("group:websocket:content"); | |||
| redisTemplate.opsForList().rightPush("group:device:" + devId,result); | |||
| JSONObject resultJson = new JSONObject(); | |||
| resultJson.put("msg",result); | |||
| return resultJson; | |||
| } catch (InterruptedException e) { | |||
| log.warn(e.getMessage()); | |||
| } | |||
| } | |||
| return null; | |||
| } | |||
| @Override | |||
| public JSONObject handle(String dev, String content, Long tenantId) { | |||
| return null; | |||
| } | |||
| } | |||
| @@ -0,0 +1,59 @@ | |||
| package com.xueyi.nlt.nlt.template; | |||
| import com.alibaba.fastjson2.JSONArray; | |||
| import com.alibaba.fastjson2.JSONException; | |||
| import com.alibaba.fastjson2.JSONObject; | |||
| import com.xueyi.nlt.netty.client.WebSocketClient; | |||
| 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.stereotype.Service; | |||
| @Service("generative-knowledge") | |||
| public class GenerativeKnowledgeTemplate implements BaseTemplate{ | |||
| private static final Logger log = LoggerFactory.getLogger(GenerativeKnowledgeTemplate.class); | |||
| @Autowired | |||
| WebSocketClient webSocketClient; | |||
| @Autowired | |||
| private RedisTemplate<String,String> redisTemplate; | |||
| @Override | |||
| public JSONObject handle(String devId, String content) { | |||
| // 根据content内容调用模版并返回结果 | |||
| synchronized (WebSocketClient.LOCK) { | |||
| String prefix = "你的任务是[针对给定的文段提出" + (content.length() /100 + 1 ) + "个问题并回答]。文段为:[\""; | |||
| String suffix = "\"]。输出为一个JSON数组[{}],每个元素是一个JSON:{“question”:,”answer”:}。不要给出任何解释说明。"; | |||
| log.info(prefix + content + suffix); | |||
| webSocketClient.sendMsg(prefix + content + suffix); | |||
| try { | |||
| WebSocketClient.LOCK.wait(); | |||
| String result = (String)redisTemplate.opsForValue().get("group:websocket:content"); | |||
| try { | |||
| JSONArray jsonArray = JSONArray.parseArray(result); | |||
| JSONObject jsonObject = new JSONObject(); | |||
| jsonObject.put("questions",jsonArray); | |||
| return jsonObject; | |||
| } catch (JSONException je) { | |||
| // 返回结果错误,计日志,存log,返回空结果 | |||
| log.error(je.getMessage(),je); | |||
| return new JSONObject(); | |||
| } | |||
| } catch (InterruptedException e) { | |||
| log.warn(e.getMessage()); | |||
| } | |||
| } | |||
| return new JSONObject(); | |||
| } | |||
| @Override | |||
| public JSONObject handle(String dev, String content, Long tenantId) { | |||
| return null; | |||
| } | |||
| } | |||
| @@ -0,0 +1,245 @@ | |||
| package com.xueyi.nlt.nlt.template; | |||
| import co.elastic.clients.elasticsearch.ElasticsearchClient; | |||
| import co.elastic.clients.elasticsearch._types.query_dsl.MatchQuery; | |||
| import co.elastic.clients.elasticsearch._types.query_dsl.Query; | |||
| import co.elastic.clients.elasticsearch.core.SearchResponse; | |||
| import co.elastic.clients.elasticsearch.core.search.Hit; | |||
| import com.alibaba.fastjson2.JSONArray; | |||
| import com.alibaba.fastjson2.JSONException; | |||
| 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.constant.digitalman.SkillConstants; | |||
| import com.xueyi.common.core.web.result.R; | |||
| import com.xueyi.nlt.api.nlt.domain.vo.CoversationSessionVo; | |||
| import com.xueyi.nlt.netty.client.WebSocketClient; | |||
| import com.xueyi.nlt.nlt.controller.DmIntentController; | |||
| import com.xueyi.system.api.digitalmans.domain.po.DmDigitalmanExtPo; | |||
| import com.xueyi.system.api.digitalmans.feign.RemoteDigitalmanService; | |||
| import com.xueyi.system.api.meeting.domain.dto.DmMeetingRoomsDto; | |||
| import com.xueyi.system.api.meeting.feign.RemoteMeetingService; | |||
| import com.xueyi.system.api.model.Source; | |||
| import com.xueyi.system.api.organize.domain.dto.SysEnterpriseDto; | |||
| import com.xueyi.system.api.organize.feign.RemoteEnterpriseService; | |||
| import com.xueyi.system.api.pass.domain.po.DmRecognizedRecordsPo; | |||
| 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.stereotype.Component; | |||
| import org.springframework.stereotype.Service; | |||
| import java.io.IOException; | |||
| import java.time.LocalDate; | |||
| import java.time.LocalDateTime; | |||
| import java.time.format.DateTimeFormatter; | |||
| import java.util.List; | |||
| import java.util.concurrent.TimeUnit; | |||
| import java.util.stream.Collectors; | |||
| @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 RemoteDigitalmanService digitalmanService; | |||
| @Autowired | |||
| RemoteEnterpriseService enterpriseService; | |||
| @Autowired | |||
| private StringRedisTemplate redisTemplate; | |||
| @Autowired | |||
| private RedisTemplate<Object,Object> objectRedisTemplate; | |||
| @Autowired | |||
| private ElasticsearchClient esClient; | |||
| @Override | |||
| public JSONObject handle(String devId, String content,Long tenantId) { | |||
| LocalDateTime date = LocalDateTime.now(); | |||
| DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); | |||
| DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd 00:00"); | |||
| DateTimeFormatter scheduleFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); | |||
| DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm"); | |||
| R<SysEnterpriseDto> enterpriseDtoR = enterpriseService.getInfo(tenantId); | |||
| Source source = SourceUtil.getSourceCache(enterpriseDtoR.getData().getStrategyId()); | |||
| R<DmDigitalmanExtPo> extR = digitalmanService.selectExtByDeviceId(devId,tenantId,source.getMaster(), SecurityConstants.INNER); | |||
| DmDigitalmanExtPo extPo = null; | |||
| if (null != extR) { | |||
| extPo = extR.getData(); | |||
| } | |||
| // 获取房间号 | |||
| R<List<DmMeetingRoomsDto>> roomListR = remoteMeetingService.roomListInner(extPo.getDeptId(),tenantId,source.getMaster(), SecurityConstants.INNER); | |||
| // 缓存获取会议室session信息 | |||
| CoversationSessionVo session = (CoversationSessionVo) objectRedisTemplate.opsForValue().get("group:device" + ":" + devId + ":" +"session"); | |||
| if (session == null) { | |||
| session = new CoversationSessionVo(); | |||
| session.setCategory("meeting"); | |||
| session.setFormat(new JSONObject()); | |||
| session.getFormat().put("skillCode",SkillConstants.SkillType.BOOK_MEETING_ROOM.getCode()); | |||
| } | |||
| synchronized (WebSocketClient.LOCK) { | |||
| // 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 = "当前时间是"+ dateFormatter.format(date)+ " 00:00。我将提供一些用户输入的信息,请提取输入的内容进行结构化转换并输出对应 json 格式的代码。下面是结构的描述:date(格式为YYYY-MM-DD);start_time(格式HH:MM);location; 下面是用户的输入信息:开个会"; | |||
| String suffix = "\"。请开始信息提取,你回复的内容必须是一个json结构,如果没提取到的信息赋值为null,我不需要代码。"; | |||
| log.info(prefix + content + suffix); | |||
| // 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字符串 | |||
| if (!result.contains("{") || !result.contains("}")) { | |||
| // 生成内容无效 | |||
| return session.getFormat(); | |||
| } | |||
| result = result.substring(result.indexOf("{"), result.lastIndexOf("}") + 1); | |||
| System.out.println(result); | |||
| JSONObject jo = JSONObject.parseObject(result); | |||
| String dateStr = jo.getString("date"); | |||
| String timeStr = jo.getString("start_time"); | |||
| String locationStr = jo.getString("location"); | |||
| if (StringUtils.isNotEmpty(locationStr)) { | |||
| roomListR.getData().forEach(item -> { | |||
| if(item.getName().equals(jo.get("location"))){ | |||
| jo.put("locationId",item.getId()); | |||
| } | |||
| }); | |||
| if (jo.get("locationId") != null) { | |||
| session.getFormat().put("location",locationStr); | |||
| session.getFormat().put("locationId",jo.get("locationId")); | |||
| } | |||
| } | |||
| //设置时间参数 | |||
| if (StringUtils.isNotEmpty(dateStr) && dateStr.split(":").length == 3) { | |||
| if (!jo.containsKey("locationId") || !session.getFormat().containsKey("date")) { | |||
| session.getFormat().put("date",dateStr); | |||
| } | |||
| } | |||
| if (StringUtils.isNotEmpty(timeStr) && !timeStr.equals("00:00")) { | |||
| //判断timeStr的格式是否是HH:mm | |||
| if (timeStr.split(":").length == 2) { | |||
| session.getFormat().put("start_time",timeStr); | |||
| } | |||
| } | |||
| // R<List<JSONObject>> 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")); | |||
| // } | |||
| // } | |||
| // 判断内容是否击中es | |||
| CoversationSessionVo dateSession = processEsDate(content,session); | |||
| if (dateSession != null ){ | |||
| session = dateSession; | |||
| } | |||
| //判断会议室是否冲突,如果冲突,删除时间 | |||
| JSONObject checkObject = session.getFormat(); | |||
| if (checkObject.containsKey("date") && checkObject.containsKey("time") && checkObject.containsKey("location")) { | |||
| JSONObject ret = remoteMeetingService.queryExist(checkObject.getLong("locationId"), checkObject.getString("date"), checkObject.getString("time"),tenantId,source.getMaster(), SecurityConstants.INNER); | |||
| if (StringUtils.isNotEmpty(ret.getString("err"))) { | |||
| // 会议室冲突,删除时间 | |||
| session.getFormat().remove("time"); | |||
| } | |||
| } | |||
| // redis缓存更新session | |||
| objectRedisTemplate.opsForValue().set("group:device" + ":" + devId + ":" +"session", session, 1,TimeUnit.MINUTES); | |||
| session.getFormat().put("skillCode", SkillConstants.SkillType.BOOK_MEETING_ROOM.getCode()); | |||
| return session.getFormat(); | |||
| } catch (JSONException je) { | |||
| // 返回结果错误,计日志,存log,返回空结果 | |||
| log.error(je.getMessage(),je); | |||
| return null; | |||
| } | |||
| } catch (InterruptedException e) { | |||
| log.warn(e.getMessage()); | |||
| Thread.currentThread().interrupt(); | |||
| } | |||
| // JSONArray ja = new JSONArray(); | |||
| // R<List<JSONObject>> recentListR = null; | |||
| // recentListR = remoteMeetingService.ableOrderList(devId,null,null,null); | |||
| // if (recentListR.isOk()) { | |||
| // recentListR.getData().forEach(item->{ | |||
| // JSONObject tJson = new JSONObject(); | |||
| // tJson.put("start_time",item.get("startTime")); | |||
| // tJson.put("location",item.get("roomId")); | |||
| // tJson.put("date",item.get(("orderDate"))); | |||
| // tJson.put("duration_hours",0.5); | |||
| // ja.add(tJson); | |||
| // }); | |||
| // JSONObject result = new JSONObject(); | |||
| // result.put("data",ja); | |||
| // return result; | |||
| // } | |||
| } | |||
| return null; | |||
| } | |||
| private CoversationSessionVo processEsDate(String content,CoversationSessionVo session) { | |||
| LocalDateTime date = LocalDateTime.now(); | |||
| DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); | |||
| SearchResponse<JSONObject> dateResponse = null; | |||
| try { | |||
| MatchQuery byName = MatchQuery.of(m->m.field("name").query(content)); | |||
| dateResponse = esClient.search(builder -> builder | |||
| .index("meeting_date").query(q->q.match(byName)).minScore(3.0), | |||
| JSONObject.class); | |||
| log.info("esClient search result: {}", dateResponse); | |||
| }catch (IOException ie) { | |||
| log.error("esClient search error", ie); | |||
| return null; | |||
| } | |||
| List<JSONObject> dates = dateResponse.hits().hits().stream().map(f->f.source()).collect( | |||
| Collectors.toList() | |||
| ); | |||
| if (dates.size() > 0) { | |||
| Integer dateInt = date.getDayOfWeek().getValue(); | |||
| date = dates.get(0).get("type").equals("week")? date.plusDays(dates.get(0).getLong("content") - (long)dateInt) : date.plusDays(dates.get(0).getLong("content")); | |||
| session.getFormat().put("date",dateFormatter.format(date)); | |||
| } | |||
| return session; | |||
| } | |||
| @Override | |||
| public JSONObject handle(String dev, String content) { | |||
| return null; | |||
| } | |||
| } | |||
| @@ -0,0 +1,10 @@ | |||
| Spring Boot Version: ${spring-boot.version} | |||
| Spring Application Name: ${spring.application.name} | |||
| _______ _________ _ _________ | |||
| |\ /||\ /|( ____ \|\ /|\__ __/ ( ( /||\ \__ __/ | |||
| ( \ / )| ) ( || ( \/( \ / ) ) ( | \ ( || ) ) ( | |||
| \ (_) / | | | || (__ \ (_) / | | _____ | \ | || | | | | |||
| ) _ ( | | | || __) \ / | |(_____)| (\ \) || | | | | |||
| / ( ) \ | | | || ( ) ( | | | | \ || | | | | |||
| ( / \ )| (___) || (____/\ | | ___) (___ | ) \ || (____/\ | | | |||
| |/ \|(_______)(_______/ \_/ \_______/ |/ )_)(_______/ )_( | |||