@@ -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 "镜像推送成功" | |||
//} | |||
} | |||
} |
@@ -163,6 +163,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 +306,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> | |||
@@ -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); | |||
} |
@@ -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,13 @@ | |||
package com.xueyi.nlt.api.netty.domain.vo; | |||
import com.alibaba.fastjson2.JSONObject; | |||
import lombok.Data; | |||
import lombok.NoArgsConstructor; | |||
@Data | |||
@NoArgsConstructor | |||
public class DmWebSocketMessageVo { | |||
String devId; | |||
String skillCode; | |||
JSONObject format; | |||
} |
@@ -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,16 @@ | |||
package com.xueyi.nlt.api.nlt.domain.vo; | |||
import lombok.Data; | |||
import lombok.NoArgsConstructor; | |||
@Data | |||
@NoArgsConstructor | |||
public class DmIntentVo { | |||
String devId; | |||
Long operator; | |||
String skillCode; | |||
String content; | |||
String preIntent; | |||
String sign; | |||
String requestId; | |||
} |
@@ -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,24 @@ | |||
package com.xueyi.nlt.api.nlt.feign; | |||
import com.alibaba.fastjson2.JSONObject; | |||
import com.xueyi.common.core.constant.basic.SecurityConstants; | |||
import com.xueyi.common.core.constant.basic.ServiceConstants; | |||
import com.xueyi.common.core.web.result.R; | |||
import com.xueyi.nlt.api.netty.domain.vo.DmWebSocketMessageVo; | |||
import com.xueyi.nlt.api.nlt.domain.vo.DmIntentVo; | |||
import com.xueyi.nlt.api.nlt.feign.factory.RemoteIntentFallbackFactory; | |||
import org.springframework.cloud.openfeign.FeignClient; | |||
import org.springframework.web.bind.annotation.PostMapping; | |||
import org.springframework.web.bind.annotation.RequestBody; | |||
import org.springframework.web.bind.annotation.RequestHeader; | |||
import org.springframework.web.bind.annotation.ResponseBody; | |||
@FeignClient(contextId = "remoteIntentService", value = ServiceConstants.NLT_SERVICE, fallbackFactory = RemoteIntentFallbackFactory.class) | |||
public interface RemoteIntentService { | |||
@PostMapping("/intent/inner/conversation") | |||
R<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); | |||
} |
@@ -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 @@ | |||
com.xueyi.nlt.api.nlt.feign.factory.RemoteIntentFallbackFactory |
@@ -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; | |||
} |
@@ -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,8 @@ 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(); | |||
} |
@@ -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,11 @@ 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(value = "/meeting/api/recent/{devId}/{dateStr}") | |||
R<List<JSONObject>> ableOrderList(@PathVariable(value = "devId") String devId, @PathVariable(value = "dateStr") String dateStr, @RequestParam(value = "roomId", required = false) Long roomId, @RequestParam(value = "startTime", required = false) String startTime); | |||
} |
@@ -66,6 +66,16 @@ 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) { | |||
return null; | |||
} | |||
@Override | |||
public R<List<JSONObject>> ableOrderList(String devId, String dateStr, Long roomId, String startTime) { | |||
return null; | |||
} | |||
}; | |||
} | |||
} |
@@ -6,3 +6,4 @@ 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 |
@@ -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"; | |||
@@ -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; | |||
} | |||
} | |||
} |
@@ -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"; | |||
} |
@@ -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> | |||
@@ -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"); | |||
} | |||
} |
@@ -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,33 @@ 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.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 +75,10 @@ public class ApiController { | |||
@Autowired | |||
RemoteBroadcastService remoteBroadcastService; | |||
@Autowired | |||
MessageQueueServiceImpl messageQueueService; | |||
@RequestMapping(value = "/heartbeat", method = {RequestMethod.POST}) | |||
@ResponseBody | |||
@@ -264,6 +273,58 @@ 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("servicePerCount", 737521); | |||
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) { | |||
@@ -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,117 @@ | |||
<?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> | |||
<!-- <dependency>--> | |||
<!-- <groupId>org.springframework.boot</groupId>--> | |||
<!-- <artifactId>spring-boot-starter-data-elasticsearch</artifactId>--> | |||
<!-- <version>3.1.2</version>--> | |||
<!-- </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,313 @@ | |||
package com.xueyi.nlt.netty.client; | |||
import com.alibaba.fastjson2.JSONObject; | |||
import com.alibaba.nacos.shaded.com.google.gson.Gson; | |||
import com.alibaba.nacos.shaded.com.google.gson.JsonArray; | |||
import com.alibaba.nacos.shaded.com.google.gson.JsonObject; | |||
import com.xueyi.nlt.api.netty.domain.vo.DmWebSocketMessageVo; | |||
import okhttp3.*; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.data.redis.core.RedisTemplate; | |||
import org.springframework.data.redis.core.StringRedisTemplate; | |||
import org.springframework.stereotype.Component; | |||
import javax.annotation.PostConstruct; | |||
import javax.crypto.Mac; | |||
import javax.crypto.spec.SecretKeySpec; | |||
import java.net.URL; | |||
import java.nio.charset.Charset; | |||
import java.text.SimpleDateFormat; | |||
import java.util.Base64; | |||
import java.util.Date; | |||
import java.util.Locale; | |||
import java.util.TimeZone; | |||
@Component | |||
public class WebSocketClient extends WebSocketListener { | |||
public static WebSocketClient INSTANCE; | |||
@Autowired | |||
private RedisTemplate redisTemplate; | |||
@Autowired | |||
private StringRedisTemplate stringRedisTemplate; | |||
public final static Object LOCK = new Object(); | |||
public static String hostUrl = "https://spark-api.xf-yun.com/v1.1/chat"; | |||
public static String APPID = "3d9282da";//从开放平台控制台中获取 | |||
public static String APIKEY = "7c217b3a313f4b66fcc14a8e97f85103";//从开放平台控制台中获取 | |||
public static String APISecret = "ZTRiNDQwMTRlOTlmZDQwMDUwYTdjMDM0";//从开放平台控制台中获取 | |||
public static WebSocket webSocket; | |||
// public static String APPID = "948cf4b6";//从开放平台控制台中获取 | |||
// public static String APIKEY = "54f6e81f40a31d66d976496de895a7a4";//从开放平台控制台中获取 | |||
// public static String APISecret = "ZDYyMjNmMTlkYTE0YWRmOWUwZTYxNjYz";//从开放平台控制台中获取 | |||
public static final Gson json = new Gson(); | |||
// public static String question = "假设你是一位前台,你需要通过与其他人对话来获取会议相关信息,已知今天是2023-7-19,你需要获取会议日期,开始时间,持续时间,会议地点,会议主题。时间用类似00:00的格式输出。对方的话中可能不包含全部信息,对于未知的信息填充为none。如果所有信息都已知那么commit为true。否则为false。将你获得的信息输出为json格式。对方的话是:“明天下午开个会。从两点开到下午三点,在大会议室开,主题是访客接待”,只输出最后的json。输出只有一行,输出格式为{date:,start_time:,duration:,location:,theme:commit:}。";//可以修改question 内容,来向模型提问 | |||
public static String question = "请帮我安排五一出行计划";//可以修改question 内容,来向模型提问 | |||
public String answer = ""; | |||
@PostConstruct | |||
public void init() { | |||
INSTANCE = this; | |||
INSTANCE.redisTemplate = this.redisTemplate; | |||
INSTANCE.stringRedisTemplate = this.stringRedisTemplate; | |||
} | |||
public static void main(String[] args) { | |||
synchronized (LOCK) { | |||
try { | |||
//构建鉴权httpurl | |||
String authUrl = getAuthorizationUrl(hostUrl,APIKEY,APISecret); | |||
OkHttpClient okHttpClient = new OkHttpClient.Builder().build(); | |||
String url = authUrl.replace("https://","wss://").replace("http://","ws://"); | |||
Request request = new Request.Builder().url(url).build(); | |||
WebSocket webSocket = okHttpClient.newWebSocket(request,new WebSocketClient()); | |||
LOCK.wait(); | |||
System.out.println("查询完成"); | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
} | |||
} | |||
// write your code here | |||
} | |||
public void sendMsg(String message){ | |||
question = message; | |||
try { | |||
//构建鉴权httpurl | |||
String authUrl = getAuthorizationUrl(hostUrl,APIKEY,APISecret); | |||
OkHttpClient okHttpClient = new OkHttpClient.Builder().build(); | |||
String url = authUrl.replace("https://","wss://").replace("http://","ws://"); | |||
Request request = new Request.Builder().url(url).build(); | |||
webSocket = okHttpClient.newWebSocket(request,new WebSocketClient()); | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
} | |||
} | |||
//鉴权url | |||
public static String getAuthorizationUrl(String hostUrl , String apikey ,String apisecret) throws Exception { | |||
//获取host | |||
URL url = new URL(hostUrl); | |||
//获取鉴权时间 date | |||
SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US); | |||
System.out.println("format:\n" + format ); | |||
format.setTimeZone(TimeZone.getTimeZone("GMT")); | |||
String date = format.format(new Date()); | |||
//获取signature_origin字段 | |||
StringBuilder builder = new StringBuilder("host: ").append(url.getHost()).append("\n"). | |||
append("date: ").append(date).append("\n"). | |||
append("GET ").append(url.getPath()).append(" HTTP/1.1"); | |||
System.out.println("signature_origin:\n" + builder); | |||
//获得signatue | |||
Charset charset = Charset.forName("UTF-8"); | |||
Mac mac = Mac.getInstance("hmacsha256"); | |||
SecretKeySpec sp = new SecretKeySpec(apisecret.getBytes(charset),"hmacsha256"); | |||
mac.init(sp); | |||
byte[] basebefore = mac.doFinal(builder.toString().getBytes(charset)); | |||
String signature = Base64.getEncoder().encodeToString(basebefore); | |||
//获得 authorization_origin | |||
String authorization_origin = String.format("api_key=\"%s\",algorithm=\"%s\",headers=\"%s\",signature=\"%s\"",apikey,"hmac-sha256","host date request-line",signature); | |||
//获得authorization | |||
String authorization = Base64.getEncoder().encodeToString(authorization_origin.getBytes(charset)); | |||
//获取httpurl | |||
HttpUrl httpUrl = HttpUrl.parse("https://" + url.getHost() + url.getPath()).newBuilder().// | |||
addQueryParameter("authorization", authorization).// | |||
addQueryParameter("date", date).// | |||
addQueryParameter("host", url.getHost()).// | |||
build(); | |||
return httpUrl.toString(); | |||
} | |||
//重写onopen | |||
@Override | |||
public void onOpen(WebSocket webSocket, Response response) { | |||
super.onOpen(webSocket, response); | |||
new Thread(()->{ | |||
JsonObject frame = new JsonObject(); | |||
JsonObject header = new JsonObject(); | |||
JsonObject chat = new JsonObject(); | |||
JsonObject parameter = new JsonObject(); | |||
JsonObject payload = new JsonObject(); | |||
JsonObject message = new JsonObject(); | |||
JsonObject text = new JsonObject(); | |||
JsonArray ja = new JsonArray(); | |||
//填充header | |||
header.addProperty("app_id",APPID); | |||
header.addProperty("uid","123456789"); | |||
//填充parameter | |||
chat.addProperty("domain","general"); | |||
chat.addProperty("random_threshold",0); | |||
chat.addProperty("max_tokens",1024); | |||
chat.addProperty("auditing","default"); | |||
parameter.add("chat",chat); | |||
//填充payload | |||
text.addProperty("role","user"); | |||
text.addProperty("content",question); | |||
ja.add(text); | |||
// message.addProperty("text",ja.getAsString()); | |||
message.add("text",ja); | |||
payload.add("message",message); | |||
frame.add("header",header); | |||
frame.add("parameter",parameter); | |||
frame.add("payload",payload); | |||
System.out.println("frame:\n" + frame.toString()); | |||
webSocket.send(frame.toString()); | |||
} | |||
).start(); | |||
} | |||
//重写onmessage | |||
@Override | |||
public void onMessage(WebSocket webSocket, String text) { | |||
super.onMessage(webSocket, text); | |||
System.out.println("text:\n" + text); | |||
ResponseData responseData = json.fromJson(text,ResponseData.class); | |||
synchronized (LOCK) { | |||
try { | |||
// System.out.println("code:\n" + responseData.getHeader().get("code")); | |||
if (0 == responseData.getHeader().get("code").getAsInt()) { | |||
System.out.println("###########"); | |||
System.out.println("getStatus: " + responseData.getHeader().get("status").getAsInt()); | |||
if (2 != responseData.getHeader().get("status").getAsInt()) { | |||
System.out.println("****************"); | |||
Payload pl = json.fromJson(responseData.getPayload(), Payload.class); | |||
JsonArray temp = (JsonArray) pl.getChoices().get("text"); | |||
JsonObject jo = (JsonObject) temp.get(0); | |||
answer += jo.get("content").getAsString(); | |||
// System.out.println(answer); | |||
} else { | |||
Payload pl1 = json.fromJson(responseData.getPayload(), Payload.class); | |||
JsonObject jsonObject = (JsonObject) pl1.getUsage().get("text"); | |||
int prompt_tokens = jsonObject.get("prompt_tokens").getAsInt(); | |||
JsonArray temp1 = (JsonArray) pl1.getChoices().get("text"); | |||
JsonObject jo = (JsonObject) temp1.get(0); | |||
answer += jo.get("content").getAsString(); | |||
System.out.println("返回结果为:\n" + answer); | |||
if (INSTANCE.redisTemplate.hasKey("gpt:websocket:1")) { | |||
DmWebSocketMessageVo message = (DmWebSocketMessageVo) INSTANCE.redisTemplate.opsForValue().get("gpt:websocket:1"); | |||
JSONObject preWebsocketJo = message.getFormat(); | |||
JSONObject meetingJo = new JSONObject(); | |||
meetingJo.put("timestamp",preWebsocketJo.get("timestamp")); | |||
meetingJo.put("content",answer); | |||
INSTANCE.stringRedisTemplate.opsForHash().put("group:nlp" + ":" + preWebsocketJo.getString("orderId"), "meeting", meetingJo.toString()); | |||
INSTANCE.redisTemplate.delete("gpt:websocket:1"); | |||
}else { | |||
// 添加缓存 | |||
INSTANCE.stringRedisTemplate.opsForValue().set("group:websocket:content", answer); | |||
LOCK.notifyAll(); | |||
} | |||
// webSocket.close(3,"客户端主动断开链接"); | |||
//webSocket.close(1000,"客户端主动断开链接"); | |||
} | |||
} else { | |||
System.out.println("返回结果错误:\n" + responseData.getHeader().get("code") + responseData.getHeader().get("message")); | |||
LOCK.notifyAll(); | |||
} | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
} | |||
} | |||
} | |||
//重写onFailure | |||
@Override | |||
public void onFailure(WebSocket webSocket, Throwable t, Response response) { | |||
super.onFailure(webSocket, t, response); | |||
System.out.println(response); | |||
} | |||
class ResponseData{ | |||
private JsonObject header; | |||
private JsonObject payload; | |||
public JsonObject getHeader() { | |||
return header; | |||
} | |||
public JsonObject getPayload() { | |||
return payload; | |||
} | |||
} | |||
class Header{ | |||
private int code ; | |||
private String message; | |||
private String sid; | |||
private String status; | |||
public int getCode() { | |||
return code; | |||
} | |||
public String getMessage() { | |||
return message; | |||
} | |||
public String getSid() { | |||
return sid; | |||
} | |||
public String getStatus() { | |||
return status; | |||
} | |||
} | |||
class Payload{ | |||
private JsonObject choices; | |||
private JsonObject usage; | |||
public JsonObject getChoices() { | |||
return choices; | |||
} | |||
public JsonObject getUsage() { | |||
return usage; | |||
} | |||
} | |||
class Choices{ | |||
private int status; | |||
private int seq; | |||
private JsonArray text; | |||
public int getStatus() { | |||
return status; | |||
} | |||
public int getSeq() { | |||
return seq; | |||
} | |||
public JsonArray getText() { | |||
return text; | |||
} | |||
} | |||
} |
@@ -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,66 @@ | |||
package com.xueyi.nlt.netty.controller; | |||
import com.alibaba.fastjson2.JSONObject; | |||
import com.xueyi.common.cache.utils.SourceUtil; | |||
import com.xueyi.common.core.constant.basic.SecurityConstants; | |||
import com.xueyi.common.core.web.result.AjaxResult; | |||
import com.xueyi.common.core.web.result.R; | |||
import com.xueyi.nlt.api.netty.domain.vo.DmWebSocketMessageVo; | |||
import com.xueyi.nlt.api.nlt.domain.vo.DmIntentVo; | |||
import com.xueyi.nlt.netty.client.WebSocketClient; | |||
import com.xueyi.nlt.nlt.domain.vo.IntentTemplateVo; | |||
import com.xueyi.system.api.digitalmans.domain.dto.DmManDeviceDto; | |||
import com.xueyi.system.api.model.Source; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.data.redis.core.RedisTemplate; | |||
import org.springframework.data.redis.core.StringRedisTemplate; | |||
import org.springframework.web.bind.annotation.*; | |||
import java.text.SimpleDateFormat; | |||
import java.util.Date; | |||
@RestController | |||
@RequestMapping("/websocket") | |||
public class DmWebsocketController { | |||
private static final Logger log = LoggerFactory.getLogger(DmWebsocketController.class); | |||
@Autowired | |||
WebSocketClient webSocketClient; | |||
@Autowired | |||
StringRedisTemplate stringRedisTemplate; | |||
@Autowired | |||
RedisTemplate redisTemplate; | |||
/** | |||
* 意图请求 | |||
列表 | |||
*/ | |||
@PostMapping("/inner/sendMessage") | |||
@ResponseBody | |||
public R sendMessage(@RequestBody DmWebSocketMessageVo message) { | |||
log.info("websocket sendMessage:{}", message); | |||
if (message == null || message.getFormat() == null) { | |||
return R.fail("参数为空"); | |||
} | |||
JSONObject jo = message.getFormat(); | |||
SimpleDateFormat dateFormat3 = new SimpleDateFormat("MM-dd"); | |||
Double timestamp = Double.valueOf((String)jo.get("timestamp")); | |||
String meetingRoom = jo.getString("meetingRoom"); | |||
Date date = new Date(timestamp.longValue()); | |||
if (message.getSkillCode().equals("1")) { | |||
String prefix = "假设你是一名公司前台,你看到在你们公司工作的\\"+ jo.getString("orderName")+ "\\,请你从个人角度提醒他参加\\" + | |||
dateFormat3.format(timestamp) + "\\在\\" + meetingRoom + "\\的会,要求语气友好。输出只包含你要对他说的话,在20字左右。"; | |||
webSocketClient.sendMsg(prefix); | |||
// 设置缓存 | |||
redisTemplate.opsForValue().set("gpt:websocket" + ":" + "1", message); | |||
} | |||
return R.ok(); | |||
} | |||
} |
@@ -0,0 +1,296 @@ | |||
package com.xueyi.nlt.nlt.controller; | |||
import com.alibaba.fastjson2.JSONObject; | |||
import com.baomidou.mybatisplus.core.toolkit.StringUtils; | |||
import com.xueyi.common.cache.utils.SourceUtil; | |||
import com.xueyi.common.core.constant.basic.SecurityConstants; | |||
import com.xueyi.common.core.web.result.AjaxResult; | |||
import com.xueyi.common.core.web.result.R; | |||
import com.xueyi.common.core.web.validate.V_A; | |||
import com.xueyi.common.core.web.validate.V_E; | |||
import com.xueyi.common.log.annotation.Log; | |||
import com.xueyi.common.log.enums.BusinessType; | |||
import com.xueyi.common.security.annotation.Logical; | |||
import com.xueyi.common.security.annotation.RequiresPermissions; | |||
import com.xueyi.common.web.entity.controller.BaseController; | |||
import com.xueyi.nlt.api.netty.domain.vo.DmWebSocketMessageVo; | |||
import com.xueyi.nlt.api.nlt.domain.vo.DmRecognitionVo; | |||
import com.xueyi.nlt.api.nlt.feign.RemoteIntentService; | |||
import com.xueyi.nlt.netty.client.WebSocketClient; | |||
import com.xueyi.nlt.netty.client.handler.NettyClientHandler; | |||
import com.xueyi.nlt.nlt.domain.dto.DmIntentDto; | |||
import com.xueyi.nlt.nlt.domain.query.DmIntentQuery; | |||
import com.xueyi.nlt.api.nlt.domain.vo.DmIntentVo; | |||
import com.xueyi.nlt.nlt.domain.vo.IntentTemplateVo; | |||
import com.xueyi.nlt.nlt.service.IDmIntentService; | |||
import com.xueyi.nlt.nlt.template.BaseTemplate; | |||
import com.xueyi.nlt.nlt.template.MeetingOrderTemplate; | |||
import com.xueyi.system.api.digitalmans.domain.dto.DmManDeviceDto; | |||
import com.xueyi.system.api.digitalmans.feign.RemoteBroadcastService; | |||
import com.xueyi.system.api.digitalmans.feign.RemoteManDeviceService; | |||
import com.xueyi.system.api.meeting.feign.RemoteMeetingService; | |||
import com.xueyi.system.api.model.Source; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.data.redis.core.RedisTemplate; | |||
import org.springframework.data.redis.core.StringRedisTemplate; | |||
import org.springframework.validation.annotation.Validated; | |||
import org.springframework.web.bind.annotation.*; | |||
import java.io.Serializable; | |||
import java.text.SimpleDateFormat; | |||
import java.util.Date; | |||
import java.util.List; | |||
import java.util.Map; | |||
@RestController | |||
@RequestMapping("/intent") | |||
public class DmIntentController extends BaseController<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; | |||
/** | |||
* 意图请求 | |||
列表 | |||
*/ | |||
@PostMapping("/api/conversation") | |||
@ResponseBody | |||
public AjaxResult conversationApi(@RequestBody DmIntentVo intent) { | |||
redisTemplate.opsForValue().increment("dashboard:server", 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) { | |||
voResult.setTarget(1); | |||
} else { | |||
voResult.setTarget(0); | |||
} | |||
voResult.setFormat(jsonObjectR.getData()); | |||
return AjaxResult.success(voResult); | |||
} | |||
/** | |||
* 意图请求 | |||
列表 | |||
*/ | |||
@PostMapping("/api/say_hello") | |||
@ResponseBody | |||
public AjaxResult sayHelloApi(@RequestBody DmRecognitionVo recognition) { | |||
log.info(recognition.toString()); | |||
redisTemplate.opsForValue().increment("dashboard:recognition", 1); | |||
JSONObject joResult = new JSONObject(); | |||
joResult.put("msg",""); | |||
joResult.put("target",0); | |||
if (StringUtils.isNotEmpty(recognition.getRegistered()) && recognition.getRegistered().equals("1")) { | |||
Map cntMap = redisTemplate.opsForHash().entries("group:nlp" + ":" + recognition.getPersonId()); | |||
//遍历key和value | |||
for (Object key : cntMap.keySet()) { | |||
String value = (String) cntMap.get(key); | |||
if (String.valueOf(key).equals("meeting")) { | |||
JSONObject jo = JSONObject.parseObject(value); | |||
// 判断是否在30分钟内 | |||
System.out.println("timestamp:" + jo.get("timestamp")); | |||
System.out.println("currenttime:" + System.currentTimeMillis()); | |||
if(jo.containsKey("timestamp") && | |||
Double.valueOf(String.valueOf(jo.get("timestamp"))) - System.currentTimeMillis() < 1800000) { | |||
joResult.put("msg",jo.get("content")); | |||
joResult.put("target",1); | |||
redisTemplate.opsForHash().delete("group:nlp" + ":" + recognition.getPersonId(), "meeting"); | |||
break; | |||
} else if (Double.valueOf(String.valueOf(jo.get("timestamp"))) - System.currentTimeMillis() < 0) { | |||
redisTemplate.opsForHash().delete("group:nlp" + ":" + recognition.getPersonId(), "meeting"); | |||
} | |||
} else { | |||
joResult.put("msg",value); | |||
joResult.put("target",1); | |||
redisTemplate.opsForHash().delete("group:nlp" + ":" + recognition.getPersonId(),key); | |||
break; | |||
} | |||
} | |||
} | |||
return AjaxResult.success(joResult); | |||
} | |||
/** | |||
* 意图请求列表 | |||
*/ | |||
@PostMapping("/inner/conversation") | |||
@ResponseBody | |||
public R<JSONObject> conversationInner(@RequestBody DmIntentVo intent) { | |||
JSONObject joResult = null; | |||
// 判断skill code的值 | |||
switch (intent.getSkillCode()) { | |||
case "1": | |||
// 获取名称为"meeting-order"的BaseTemplate的实例 | |||
joResult = meetingOrderTemplate.handle(intent.getDevId(),intent.getContent()); | |||
break; | |||
default: | |||
break; | |||
} | |||
return R.ok(joResult); | |||
} | |||
/** | |||
* 意图请求 | |||
列表 | |||
*/ | |||
@PostMapping("/inner/sendMessage") | |||
@ResponseBody | |||
public R sendMessage(@RequestBody DmWebSocketMessageVo message) { | |||
log.info("websocket sendMessage:{}", message); | |||
if (message == null || message.getFormat() == null) { | |||
return R.fail("参数为空"); | |||
} | |||
JSONObject jo = message.getFormat(); | |||
SimpleDateFormat dateFormat3 = new SimpleDateFormat("MM-dd"); | |||
Double timestamp = Double.valueOf((String)jo.get("timestamp")); | |||
String meetingRoom = jo.getString("meetingRoom"); | |||
Date date = new Date(timestamp.longValue()); | |||
if (message.getSkillCode().equals("1")) { | |||
String prefix = "假设你是一名公司前台,你看到在你们公司工作的\\"+ jo.getString("orderName")+ "\\,请你从个人角度提醒他参加\\" + | |||
dateFormat3.format(timestamp) + "\\在\\" + meetingRoom + "\\的会,要求语气友好。输出只包含你要对他说的话,在20字左右。"; | |||
webSocketClient.sendMsg(prefix); | |||
// 设置缓存 | |||
redisTemplate2.opsForValue().set("gpt:websocket" + ":" + "1", message); | |||
} | |||
return R.ok(); | |||
} | |||
/** | |||
* 查询意图管理 | |||
列表 | |||
*/ | |||
@Override | |||
@GetMapping("/list") | |||
@RequiresPermissions(Auth.DM_INTENT_LIST) | |||
public AjaxResult list(DmIntentQuery intent) { | |||
return super.list(intent); | |||
} | |||
/** | |||
* 查询意图管理 | |||
详细 | |||
*/ | |||
@Override | |||
@GetMapping(value = "/{id}") | |||
@RequiresPermissions(Auth.DM_INTENT_SINGLE) | |||
public AjaxResult getInfo(@PathVariable Serializable id) { | |||
return super.getInfo(id); | |||
} | |||
/** | |||
* 意图管理 | |||
新增 | |||
*/ | |||
@Override | |||
@PostMapping | |||
@RequiresPermissions(Auth.DM_INTENT_ADD) | |||
@Log(title = "意图管理", businessType = BusinessType.INSERT) | |||
public AjaxResult add(@Validated({V_A.class}) @RequestBody DmIntentDto intent) { | |||
return super.add(intent); | |||
} | |||
/** | |||
* 意图管理 | |||
修改 | |||
*/ | |||
@Override | |||
@PutMapping | |||
@RequiresPermissions(Auth.DM_INTENT_EDIT) | |||
@Log(title = "意图管理", businessType = BusinessType.UPDATE) | |||
public AjaxResult edit(@Validated({V_E.class}) @RequestBody DmIntentDto intent) { | |||
return super.edit(intent); | |||
} | |||
/** | |||
* 意图管理 | |||
修改状态 | |||
*/ | |||
@Override | |||
@PutMapping("/status") | |||
@RequiresPermissions(value = {Auth.DM_INTENT_EDIT, Auth.DM_INTENT_ES}, logical = Logical.OR) | |||
@Log(title = "意图管理", businessType = BusinessType.UPDATE_STATUS) | |||
public AjaxResult editStatus(@RequestBody DmIntentDto intent) { | |||
return super.editStatus(intent); | |||
} | |||
/** | |||
* 意图管理 | |||
批量删除 | |||
*/ | |||
@Override | |||
@DeleteMapping("/batch/{idList}") | |||
@RequiresPermissions(Auth.DM_INTENT_DEL) | |||
@Log(title = "意图管理", businessType = BusinessType.DELETE) | |||
public AjaxResult batchRemove(@PathVariable List<Long> idList) { | |||
return super.batchRemove(idList); | |||
} | |||
/** | |||
* 获取意图管理 | |||
选择框列表 | |||
*/ | |||
@Override | |||
@GetMapping("/option") | |||
public AjaxResult option() { | |||
return super.option(); | |||
} | |||
interface Auth { | |||
/** 系统 - 意图管理 | |||
管理 - 列表 */ | |||
String DM_INTENT_LIST = "nlt:intent:list"; | |||
/** 系统 - 意图管理 | |||
管理 - 详情 */ | |||
String DM_INTENT_SINGLE = "nlt:intent:single"; | |||
/** 系统 - 意图管理 | |||
管理 - 新增 */ | |||
String DM_INTENT_ADD = "nlt:intent:add"; | |||
/** 系统 - 意图管理 | |||
管理 - 修改 */ | |||
String DM_INTENT_EDIT = "nlt:intent:edit"; | |||
/** 系统 - 意图管理 | |||
管理 - 修改状态 */ | |||
String DM_INTENT_ES = "nlt:intent:es"; | |||
/** 系统 - 意图管理 | |||
管理 - 删除 */ | |||
String DM_INTENT_DEL = "nlt:intent:delete"; | |||
} | |||
} |
@@ -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,16 @@ | |||
package com.xueyi.nlt.nlt.domain.vo; | |||
import com.alibaba.fastjson2.JSONObject; | |||
import lombok.Data; | |||
import lombok.NoArgsConstructor; | |||
@Data | |||
@NoArgsConstructor | |||
public class IntentTemplateVo { | |||
private String msg; | |||
private JSONObject format; | |||
/** | |||
* 是否触发技能 | |||
*/ | |||
private Integer target; | |||
} |
@@ -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,7 @@ | |||
package com.xueyi.nlt.nlt.template; | |||
import com.alibaba.fastjson2.JSONObject; | |||
public interface BaseTemplate { | |||
JSONObject handle(String dev, String content); | |||
} |
@@ -0,0 +1,93 @@ | |||
package com.xueyi.nlt.nlt.template; | |||
import com.alibaba.fastjson2.JSONException; | |||
import com.alibaba.fastjson2.JSONObject; | |||
import com.xueyi.common.core.web.result.R; | |||
import com.xueyi.nlt.netty.client.WebSocketClient; | |||
import com.xueyi.nlt.nlt.controller.DmIntentController; | |||
import com.xueyi.system.api.meeting.feign.RemoteMeetingService; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.data.redis.core.StringRedisTemplate; | |||
import org.springframework.stereotype.Component; | |||
import org.springframework.stereotype.Service; | |||
import java.time.LocalDate; | |||
import java.time.LocalDateTime; | |||
import java.time.format.DateTimeFormatter; | |||
import java.util.List; | |||
@Service("meeting-order") | |||
public class MeetingOrderTemplate implements BaseTemplate { | |||
private static final Logger log = LoggerFactory.getLogger(MeetingOrderTemplate.class); | |||
@Autowired | |||
WebSocketClient webSocketClient; | |||
// @Autowired | |||
// RemoteMeetingService remoteMeetingService; | |||
@Autowired | |||
private RemoteMeetingService remoteMeetingService; | |||
@Autowired | |||
private StringRedisTemplate redisTemplate; | |||
@Override | |||
public JSONObject handle(String devId, String content) { | |||
synchronized (WebSocketClient.LOCK) { | |||
LocalDateTime date = LocalDateTime.now(); | |||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd 00:00"); | |||
DateTimeFormatter scheduleFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); | |||
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm"); | |||
// String prefix = "假设你是一位前台,你需要通过与其他人对话来获取会议相关信息,已知今天是" + | |||
// date.format(formatter) + ",你需要获取会议日期,开始时间,持续时间,会议地点,会议主题。时间用类似00:00的格式输出。对方的话中可能不包含全部信息,对于未知的信息填充为none。如果所有信息都已知那么commit为true。否则为false。将你获得的信息输出为json格式。对方的话是:“"; | |||
// String suffix = "”,只输出最后的json。输出只有一行,输出格式为{date:,start_time:,duration:,location:,theme:commit:}。"; | |||
// remoteMeetingService.roomListInner() | |||
// String prefix = "我想请你充当[企业前台], 已知现在是" + | |||
// date.format(formatter) + "。我对你说:”"; | |||
// String suffix = "”。会议地点:{" + "大会议室,小会议室" + "}。会议内容:{其他会议,例会,访客接待,面试}。未知的项目输出null。输出为{date:,start_time:,duration_hours:,location:,meeting_content:}"; | |||
// String prefix = "我想请你充当[SQL数据库],我需要的数据格式为{date:,start_time:,location:,meeting_theme:},没有数据不返回字段,数据只能来自于如下文段:\"已知现在是" + | |||
// date.format(formatter) + ","; | |||
// String suffix = "\",只输出最后的JSON结果。不要输出任何解释说明或者代码。"; | |||
String prefix = "当前时间是" + | |||
date.format(formatter) + " " + date.getDayOfWeek() + "。我将提供一些用户输入的信息,请提取输入的内容进行结构化转换并输出对应 json 格式的代码。下面是结构的描述:会议日期date(格式为YYYY-MM-DD);具体时间start_time(格式HH:MM);会议室地点location; 下面是用户的输入信息:\""; | |||
String suffix = "\"。请开始信息提取,你回复的内容必须是一个json结构,我只要json结果,不需要代码。"; | |||
log.info(prefix + content + suffix); | |||
webSocketClient.sendMsg(prefix + content + suffix); | |||
try { | |||
WebSocketClient.LOCK.wait(); | |||
String result = redisTemplate.opsForValue().get("group:websocket:content"); | |||
try { | |||
//对result进行解析,获取字符串第一个与'{'和最后一个'}'之间的字符串,即为json字符串 | |||
result = result.substring(result.indexOf("{"), result.lastIndexOf("}") + 1); | |||
System.out.println(result); | |||
JSONObject jo = JSONObject.parseObject(result); | |||
R<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")); | |||
} | |||
} | |||
return jo; | |||
} catch (JSONException je) { | |||
// 返回结果错误,计日志,存log,返回空结果 | |||
log.error(je.getMessage(),je); | |||
return null; | |||
} | |||
} catch (InterruptedException e) { | |||
log.warn(e.getMessage()); | |||
} | |||
} | |||
return null; | |||
} | |||
} |
@@ -0,0 +1,10 @@ | |||
Spring Boot Version: ${spring-boot.version} | |||
Spring Application Name: ${spring.application.name} | |||
_______ _________ _ _________ | |||
|\ /||\ /|( ____ \|\ /|\__ __/ ( ( /||\ \__ __/ | |||
( \ / )| ) ( || ( \/( \ / ) ) ( | \ ( || ) ) ( | |||
\ (_) / | | | || (__ \ (_) / | | _____ | \ | || | | | | |||
) _ ( | | | || __) \ / | |(_____)| (\ \) || | | | | |||
/ ( ) \ | | | || ( ) ( | | | | \ || | | | | |||
( / \ )| (___) || (____/\ | | ___) (___ | ) \ || (____/\ | | | |||
|/ \|(_______)(_______/ \_/ \_______/ |/ )_)(_______/ )_( |
@@ -0,0 +1,34 @@ | |||
# Tomcat | |||
server: | |||
port: 9901 | |||
# Spring | |||
spring: | |||
application: | |||
# 应用名称 | |||
name: xueyi-nlt | |||
profiles: | |||
# 环境配置 | |||
active: @activatedProperties@ | |||
servlet: | |||
multipart: | |||
max-request-size: 20MB | |||
max-file-size: 100MB | |||
cloud: | |||
nacos: | |||
discovery: | |||
# 服务注册地址 | |||
server-addr: @nacos.host@:@nacos.port@ | |||
namespace: @nacos.namespace@ | |||
config: | |||
# 配置中心地址 | |||
server-addr: @nacos.host@:@nacos.port@ | |||
namespace: @nacos.namespace@ | |||
# 配置文件格式 | |||
file-extension: yml | |||
# 共享配置 | |||
shared-configs: | |||
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} | |||
- application-secret-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} | |||
- application-datasource-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} |
@@ -0,0 +1,74 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<configuration scan="true" scanPeriod="60 seconds" debug="false"> | |||
<!-- 日志存放路径 --> | |||
<property name="log.path" value="logs/xueyi-nlt" /> | |||
<!-- 日志输出格式 --> | |||
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" /> | |||
<!-- 控制台输出 --> | |||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender"> | |||
<encoder> | |||
<pattern>${log.pattern}</pattern> | |||
</encoder> | |||
</appender> | |||
<!-- 系统日志输出 --> | |||
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender"> | |||
<file>${log.path}/info.log</file> | |||
<!-- 循环政策:基于时间创建日志文件 --> | |||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> | |||
<!-- 日志文件名格式 --> | |||
<fileNamePattern>${log.path}/info.%d{yyyy-MM-dd}.log</fileNamePattern> | |||
<!-- 日志最大的历史 60天 --> | |||
<maxHistory>60</maxHistory> | |||
</rollingPolicy> | |||
<encoder> | |||
<pattern>${log.pattern}</pattern> | |||
</encoder> | |||
<filter class="ch.qos.logback.classic.filter.LevelFilter"> | |||
<!-- 过滤的级别 --> | |||
<level>INFO</level> | |||
<!-- 匹配时的操作:接收(记录) --> | |||
<onMatch>ACCEPT</onMatch> | |||
<!-- 不匹配时的操作:拒绝(不记录) --> | |||
<onMismatch>DENY</onMismatch> | |||
</filter> | |||
</appender> | |||
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender"> | |||
<file>${log.path}/error.log</file> | |||
<!-- 循环政策:基于时间创建日志文件 --> | |||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> | |||
<!-- 日志文件名格式 --> | |||
<fileNamePattern>${log.path}/error.%d{yyyy-MM-dd}.log</fileNamePattern> | |||
<!-- 日志最大的历史 60天 --> | |||
<maxHistory>60</maxHistory> | |||
</rollingPolicy> | |||
<encoder> | |||
<pattern>${log.pattern}</pattern> | |||
</encoder> | |||
<filter class="ch.qos.logback.classic.filter.LevelFilter"> | |||
<!-- 过滤的级别 --> | |||
<level>ERROR</level> | |||
<!-- 匹配时的操作:接收(记录) --> | |||
<onMatch>ACCEPT</onMatch> | |||
<!-- 不匹配时的操作:拒绝(不记录) --> | |||
<onMismatch>DENY</onMismatch> | |||
</filter> | |||
</appender> | |||
<!-- 系统模块日志级别控制 --> | |||
<logger name="com.xueyi" level="info" /> | |||
<!-- Spring日志级别控制 --> | |||
<logger name="org.springframework" level="warn" /> | |||
<root level="info"> | |||
<appender-ref ref="console" /> | |||
</root> | |||
<!--系统操作日志--> | |||
<root level="info"> | |||
<appender-ref ref="file_info" /> | |||
<appender-ref ref="file_error" /> | |||
</root> | |||
</configuration> |
@@ -41,6 +41,11 @@ | |||
<artifactId>spring-boot-starter-actuator</artifactId> | |||
</dependency> | |||
<!-- SpringBoot Mock --> | |||
<dependency> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-starter-test</artifactId> | |||
</dependency> | |||
<!-- XueYi Common Log --> | |||
<dependency> | |||
<groupId>com.xueyi</groupId> | |||
@@ -103,6 +108,10 @@ | |||
<artifactId>thumbnailator</artifactId> | |||
<version>0.4.8</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.xueyi</groupId> | |||
<artifactId>xueyi-api-nlt</artifactId> | |||
</dependency> | |||
</dependencies> | |||
@@ -11,6 +11,7 @@ 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.datasource.annotation.Master; | |||
import com.xueyi.common.log.annotation.Log; | |||
import com.xueyi.common.log.enums.BusinessType; | |||
import com.xueyi.common.security.annotation.InnerAuth; | |||
@@ -32,11 +33,13 @@ import com.xueyi.system.digitalmans.domain.dto.DmDigitalmanExtDto; | |||
import com.xueyi.system.digitalmans.domain.model.DmDigitalmanExtConverter; | |||
import com.xueyi.system.digitalmans.domain.query.DmDigitalmanExtQuery; | |||
import com.xueyi.system.digitalmans.domain.query.DmDigitalmanQuery; | |||
import com.xueyi.system.digitalmans.domain.query.DmManDeviceQuery; | |||
import com.xueyi.system.digitalmans.mapper.DmManDeviceMapper; | |||
import com.xueyi.system.digitalmans.service.IDmDigitalmanExtService; | |||
import com.xueyi.system.digitalmans.service.IDmDigitalmanService; | |||
import com.xueyi.system.digitalmans.service.IDmModelService; | |||
import com.xueyi.system.digitalmans.service.IDmSkillService; | |||
import com.xueyi.system.digitalmans.service.impl.DmManDeviceServiceImpl; | |||
import com.xueyi.system.emcs.constant.EmcsUploadType; | |||
import com.xueyi.system.emcs.domain.dto.DmDeviceLogFileDto; | |||
import com.xueyi.system.emcs.domain.dto.DmExceptionLogDto; | |||
@@ -63,6 +66,7 @@ import java.io.Serializable; | |||
import java.text.ParseException; | |||
import java.time.Duration; | |||
import java.util.List; | |||
import java.util.stream.Collectors; | |||
/** | |||
* 数字人基础管理 业务处理 | |||
@@ -382,6 +386,40 @@ public class DmDigitalmanController extends BaseController<DmDigitalmanQuery, Dm | |||
return R.fail("请检查请求参数"); | |||
} | |||
@Autowired | |||
DmManDeviceServiceImpl dmManDeviceService; | |||
@GetMapping("/api/mansInfo") | |||
@Master | |||
public R<JSONObject> mansInfo() { | |||
List<DmManDeviceDto> dtos = dmManDeviceService.selectList(new DmManDeviceQuery()); | |||
List<DmManDeviceDto> dtos2 = dtos.stream().filter(dto -> StringUtils.isNotEmpty(dto.getDeviceId())).collect(Collectors.toList());; | |||
System.err.println(dtos2.size()); | |||
Long serviceTimeCount = 0L; | |||
//当前时间和activateTime时间的差值,activateTime类型是Date,差值返回的单位是小时 | |||
for (DmManDeviceDto dto : dtos2) { | |||
serviceTimeCount += (System.currentTimeMillis() - dto.getActivateTime().getTime())/3600000; | |||
} | |||
Integer meetingServiceCount = (Integer) redisTemplate.opsForValue().get("dashboard:meeting"); | |||
Integer serverTimes = (Integer) redisTemplate.opsForValue().get("dashboard:server"); | |||
log.info("meetingServiceCount:{}",meetingServiceCount); | |||
log.info("serverTimes:{}",serverTimes); | |||
JSONObject json = new JSONObject(); | |||
json.put("manCount",dtos2.size()); | |||
json.put("serviceTimeCount",serviceTimeCount); | |||
json.put("servicePerCount",serverTimes); | |||
json.put("meetingServiceCount",meetingServiceCount); | |||
System.err.println(json.toJSONString()); | |||
return R.ok(json); | |||
} | |||
@GetMapping("/api/devInfo/{devId}") | |||
public R<DmDigitalmanExtPo> devInfo(@PathVariable(required = true) String devId) { | |||
if (StringUtils.isNotEmpty(devId)){ | |||
@@ -15,6 +15,7 @@ import com.xueyi.system.api.pass.feign.RemoteRecognizedRecordsService; | |||
import com.xueyi.system.resource.controller.api.BaseApiController; | |||
import com.xueyi.system.utils.common.ImageUtil; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.data.redis.core.StringRedisTemplate; | |||
import org.springframework.web.bind.annotation.GetMapping; | |||
import org.springframework.web.bind.annotation.PathVariable; | |||
import org.springframework.web.bind.annotation.PostMapping; | |||
@@ -46,6 +47,9 @@ public class DmMeetingApiController extends BaseApiController { | |||
@Autowired | |||
private SmsService smsService; | |||
@Autowired | |||
private StringRedisTemplate redisTemplate; | |||
@GetMapping(value = "/room-lists/{devId}") | |||
public List<DmMeetingRoomsDto> roomList(@PathVariable(value = "devId") String devId){ | |||
DeviceTenantSourceMergeVo vo = super.getDeviceTenantSourceMergeVo(devId); | |||
@@ -91,6 +95,14 @@ public class DmMeetingApiController extends BaseApiController { | |||
return remoteMeetingService.delInner(id, vo.getTenantId(),vo.getSourceSlave(), SecurityConstants.INNER); | |||
} | |||
@ResponseBody | |||
@PostMapping(value = "/cancel-order") | |||
public JSONObject cancel(@RequestParam(required = true) String devId) { | |||
redisTemplate.delete("group:websocket" + ":" + devId); | |||
return outputSuccess().toJSON(); | |||
} | |||
@ResponseBody | |||
@PostMapping(value = "/lists") | |||
public JSONObject list(String dateStr, Long spaceId, String devId) { | |||
@@ -141,4 +153,25 @@ public class DmMeetingApiController extends BaseApiController { | |||
return ImageUtil.urlImageToBase64(url); | |||
} | |||
@GetMapping(value = "/recent/{devId}/{dateStr}") | |||
@ResponseBody | |||
public R<List<JSONObject>> ableOrderList(@PathVariable(value = "devId") String devId, @PathVariable(value = "dateStr") String dateStr, @RequestParam(value = "roomId", required = false) Long roomId, @RequestParam(value = "startTime", required = false) String startTime){ | |||
DeviceTenantSourceMergeVo vo = super.getDeviceTenantSourceMergeVo(devId); | |||
R<DmDigitalmanExtPo> extR = digitalmanService.selectExtByDeviceId(devId,vo.getTenantId(),vo.getSourceSlave(), SecurityConstants.INNER); | |||
DmDigitalmanExtPo extPo = null; | |||
if (null != extR) { | |||
extPo = extR.getData(); | |||
} | |||
if (extPo != null) { | |||
List<JSONObject> result = remoteMeetingService.recent(extPo.getDeptId(),dateStr, roomId, startTime, vo.getTenantId(),vo.getSourceSlave(), SecurityConstants.INNER); | |||
return R.ok(result); | |||
} else { | |||
return R.fail("未找到设备信息"); | |||
} | |||
} | |||
} |
@@ -5,18 +5,26 @@ import com.alibaba.fastjson2.JSONObject; | |||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; | |||
import com.baomidou.mybatisplus.core.toolkit.StringUtils; | |||
import com.baomidou.mybatisplus.core.toolkit.Wrappers; | |||
import com.xueyi.common.cache.utils.SourceUtil; | |||
import com.xueyi.common.core.constant.basic.SecurityConstants; | |||
import com.xueyi.common.core.constant.basic.SqlConstants; | |||
import com.xueyi.common.core.web.result.R; | |||
import com.xueyi.common.security.annotation.InnerAuth; | |||
import com.xueyi.common.sms.configure.SmsProperties; | |||
import com.xueyi.common.web.constant.ResponseCode; | |||
import com.xueyi.common.web.utils.DateUtils; | |||
import com.xueyi.nlt.api.netty.domain.vo.DmWebSocketMessageVo; | |||
import com.xueyi.nlt.api.netty.feign.RemoteWebsocketService; | |||
import com.xueyi.nlt.api.nlt.feign.RemoteIntentService; | |||
import com.xueyi.system.api.digitalmans.domain.dto.DmManDeviceDto; | |||
import com.xueyi.system.api.digitalmans.domain.po.DmDigitalmanExtPo; | |||
import com.xueyi.system.api.digitalmans.domain.po.DmDigitalmanPo; | |||
import com.xueyi.system.api.digitalmans.feign.RemoteManDeviceService; | |||
import com.xueyi.system.api.meeting.domain.dto.DmMeetingOrdersDto; | |||
import com.xueyi.system.api.meeting.domain.dto.DmMeetingRoomsDto; | |||
import com.xueyi.system.api.meeting.domain.po.DmMeetingOrdersPo; | |||
import com.xueyi.system.api.meeting.domain.po.DmMeetingRoomsPo; | |||
import com.xueyi.system.api.model.Source; | |||
import com.xueyi.system.api.sms.domain.vo.SmsReqEntity; | |||
import com.xueyi.system.api.sms.feign.RemoteSmsService; | |||
import com.xueyi.system.api.staff.domain.po.DmStaffPo; | |||
@@ -31,18 +39,27 @@ import com.xueyi.system.meeting.service.impl.DmMeetingRoomsServiceImpl; | |||
import com.xueyi.system.resource.controller.api.BaseApiController; | |||
import com.xueyi.system.staff.mapper.DmStaffMapper; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.data.redis.core.StringRedisTemplate; | |||
import org.springframework.web.bind.annotation.GetMapping; | |||
import org.springframework.web.bind.annotation.PathVariable; | |||
import org.springframework.web.bind.annotation.PostMapping; | |||
import org.springframework.web.bind.annotation.RequestBody; | |||
import org.springframework.web.bind.annotation.RequestMapping; | |||
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.HttpServletRequest; | |||
import java.text.SimpleDateFormat; | |||
import java.time.LocalTime; | |||
import java.time.format.DateTimeFormatter; | |||
import java.util.ArrayList; | |||
import java.util.Comparator; | |||
import java.util.Date; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.stream.Collectors; | |||
/** | |||
* 会议室预约管理 API | |||
@@ -86,6 +103,17 @@ public class DmMeetingInnerApiController extends BaseApiController { | |||
@Autowired | |||
private DmMeetingRoomsConverter dmMeetingRoomsConverter; | |||
@Autowired | |||
private StringRedisTemplate redisTemplate; | |||
@Autowired | |||
private RemoteWebsocketService remoteWebsocketService; | |||
@Autowired | |||
private RemoteIntentService remoteIntentService; | |||
@Autowired | |||
private RemoteManDeviceService manDeviceService; | |||
@InnerAuth | |||
@GetMapping("/rooms/{deptId}") | |||
@@ -181,6 +209,27 @@ public class DmMeetingInnerApiController extends BaseApiController { | |||
System.err.println(e.getMessage()); | |||
e.printStackTrace(); | |||
} | |||
// 埋点 预定会议室 | |||
redisTemplate.opsForValue().increment("dashboard:meeting", 1); | |||
// 创建会议提醒 | |||
// 创建json | |||
JSONObject jsonObject = new JSONObject(); | |||
jsonObject.put("meetingRoom", meetingRoom.getName()); | |||
jsonObject.put("orderName", dmStaffPo.getUserName()); | |||
jsonObject.put("orderId", dmStaffPo.getId()); | |||
jsonObject.put("timestamp", list.get(0).getStartTime().getTime()); | |||
// redisTemplate.opsForValue().set("group:nlp" + ":" + dmStaffPo.getId() + ":" + "meeting",jsonObject.toString()); | |||
// 将创建临时信息添加进缓存:用户会议提醒 | |||
DmWebSocketMessageVo vo = new DmWebSocketMessageVo(); | |||
vo.setDevId(order.getDevId()); | |||
vo.setSkillCode("1"); | |||
vo.setFormat(jsonObject); | |||
R<DmManDeviceDto> manDeviceDtoR = manDeviceService.manDeviceInfoInner(order.getDevId()); | |||
Source source = SourceUtil.getSourceCache(manDeviceDtoR.getData().getStrategyId()); | |||
remoteIntentService.sendMessage(vo, manDeviceDtoR.getData().getTId(), source.getMaster(), SecurityConstants.INNER); | |||
// 清除设备会议室模版session信息 | |||
redisTemplate.delete("group:websocket" + ":" + order.getDevId()); | |||
return outputSuccess().toJSON(); | |||
} | |||
@@ -292,7 +341,7 @@ public class DmMeetingInnerApiController extends BaseApiController { | |||
@GetMapping(value = "/lists") | |||
public JSONObject listAllInner() { | |||
List<DmMeetingOrdersPo> list = dmMeetingOrdersMapper.findAllList(); | |||
List<DmMeetingOrdersDto> res = new ArrayList<>(); | |||
List<DmMeetingOrdersPo> res = new ArrayList<>(); | |||
list.forEach(item -> { | |||
DmMeetingOrdersDto dto = dmMeetingOrdersConverter.mapperDto(item); | |||
DmMeetingRoomsPo mr = dmMeetingRoomsMapper.findById(dto.getSpaceId()); | |||
@@ -303,4 +352,140 @@ public class DmMeetingInnerApiController extends BaseApiController { | |||
return outputSuccess(res).toJSON(); | |||
} | |||
List freeTimePart (Long roomId, List<DmMeetingOrdersPo> objects, String currentTime) { | |||
List<JSONObject> freeTimeList = new ArrayList<>(); | |||
if (null == objects || objects.size() ==0) { | |||
JSONObject json = new JSONObject(); | |||
json.put("startTime", currentTime); | |||
json.put("endTime", 0); | |||
json.put("roomId", roomId); | |||
freeTimeList.add(json); | |||
return freeTimeList; | |||
} | |||
if (objects.size() ==1) { | |||
DmMeetingOrdersPo current = objects.get(0); | |||
if (DateUtils.formatDate(current.getStartTime(), "HH:mm").compareTo(currentTime) > 0 ) { | |||
JSONObject json = new JSONObject(); | |||
json.put("startTime", currentTime); | |||
json.put("endTime", DateUtils.formatDate(current.getStartTime(), "HH:mm")); | |||
json.put("roomId", roomId); | |||
freeTimeList.add(json); | |||
} | |||
JSONObject json = new JSONObject(); | |||
json.put("startTime", DateUtils.formatDate(current.getEndTime(), "HH:mm")); | |||
json.put("endTime", 0); | |||
json.put("roomId", roomId); | |||
freeTimeList.add(json); | |||
return freeTimeList; | |||
} | |||
// DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm"); | |||
objects = objects.stream().sorted(Comparator.comparing(po -> DateUtils.formatDate(po.getStartTime(), "HH:mm"))).collect(Collectors.toList()); | |||
if (objects.size() > 1) | |||
for (int i = 0; i < objects.size() - 1; i++) { | |||
DmMeetingOrdersPo current = objects.get(i); | |||
DmMeetingOrdersPo next = objects.get(i + 1); | |||
Date freeStartTime = current.getEndTime(); | |||
Date freeEndTime = next.getStartTime(); | |||
String freeStartTimeStr = DateUtils.formatDate(current.getEndTime(), "HH:mm"); | |||
String freeEndTimeStr = DateUtils.formatDate(next.getStartTime(), "HH:mm"); | |||
if (i==0 && DateUtils.formatDate(current.getStartTime(), "HH:mm").compareTo(currentTime) > 0 ) { | |||
JSONObject json = new JSONObject(); | |||
json.put("startTime", currentTime); | |||
json.put("endTime", DateUtils.formatDate(current.getStartTime(), "HH:mm")); | |||
json.put("roomId", roomId); | |||
freeTimeList.add(json); | |||
} | |||
if (freeStartTime.before(freeEndTime)) { | |||
JSONObject json = new JSONObject(); | |||
json.put("startTime", freeStartTimeStr); | |||
json.put("endTime", freeEndTimeStr); | |||
json.put("roomId", roomId); | |||
freeTimeList.add(json); | |||
} | |||
if (i==objects.size() - 2) { | |||
JSONObject json = new JSONObject(); | |||
if (DateUtils.formatDate(next.getEndTime(), "HH:mm").compareTo(currentTime)>0) { | |||
json.put("startTime", DateUtils.formatDate(next.getEndTime(), "HH:mm")); | |||
} else { | |||
json.put("startTime", currentTime); | |||
} | |||
json.put("endTime", 0); | |||
json.put("roomId", current.getSpaceId()); | |||
freeTimeList.add(json); | |||
} | |||
} | |||
return freeTimeList; | |||
} | |||
@InnerAuth | |||
@GetMapping("/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, HttpServletRequest request) { | |||
List<DmMeetingOrdersPo> list = new ArrayList<>(); | |||
List<Long> ids = new ArrayList<>(); | |||
Map<Long, String> rooms = new HashMap<>(); | |||
if (null == roomId) { | |||
DmMeetingRoomsPo dm = new DmMeetingRoomsPo(); | |||
dm.setDeptId(deptId); | |||
List<DmMeetingRoomsPo> pos = dmMeetingRoomsMapper.selectRoomList(dm); | |||
ids = pos.stream().map(DmMeetingRoomsPo::getId).collect(Collectors.toList()); | |||
rooms = pos.stream().collect(Collectors.toMap(DmMeetingRoomsPo::getId, DmMeetingRoomsPo::getName)); | |||
list = dmMeetingOrdersMapper.findListByDateStr(dateStr); | |||
} else { | |||
DmMeetingRoomsPo po = dmMeetingRoomsMapper.findById(roomId); | |||
list = dmMeetingOrdersMapper.findListByDate(dateStr, po.getId()); | |||
ids.add(roomId); | |||
rooms.put(po.getId(), po.getName()); | |||
} | |||
//过滤掉今天的已经过去的预约记录,并按开始时间进行排序 | |||
list = list.stream().filter(t->DateUtils.formatDate(t.getOrderDate(), "yyyy-MM-dd").compareTo(DateUtils.formatDate(new Date(), "yyyy-MM-dd"))>0 || (DateUtils.formatDate(t.getOrderDate(), "yyyy-MM-dd").compareTo(DateUtils.formatDate(new Date(), "yyyy-MM-dd"))==0 && DateUtils.formatDate(t.getStartTime(), "HH:mm").compareTo(DateUtils.formatDate(new Date(), "HH:mm"))>0)).sorted(Comparator.comparing(po -> DateUtils.formatDate(po.getStartTime(), "HH:mm"))).collect(Collectors.toList()); | |||
//获得当前时间开始最近的整点,或者半点 | |||
String currentStr = "08:00"; | |||
if (StringUtils.isNotEmpty(startTime)) { | |||
currentStr = startTime; | |||
} | |||
if (dateStr.compareTo(DateUtils.formatDate(new Date(), "yyyy-MM-dd")) == 0) { | |||
LocalTime currentTime = LocalTime.now(); | |||
if (currentTime.getMinute() >= 30) { | |||
currentTime = currentTime.plusHours(1).withMinute(0).withSecond(0); | |||
} else { | |||
currentTime = currentTime.withMinute(30).withSecond(0); | |||
} | |||
currentStr = currentTime.format(DateTimeFormatter.ofPattern("HH:mm")); | |||
} | |||
Map<Long, List<DmMeetingOrdersPo>> groupedByRoom = list.stream() | |||
.collect(Collectors.groupingBy(DmMeetingOrdersPo::getSpaceId)); | |||
Map<Long, List<JSONObject>> freeTime = new HashMap<>(); | |||
for(int i=0;i<ids.size();i++){ | |||
Long key = ids.get(i); | |||
//groupedByRoom.get(key)可能为空 | |||
freeTime.put(key, freeTimePart(key, groupedByRoom.get(key), currentStr)); | |||
} | |||
List<JSONObject> arr = new ArrayList<>(); | |||
for (int i=0;i<freeTime.keySet().size();i++){ | |||
Long key = (Long) freeTime.keySet().toArray()[i]; | |||
List<JSONObject> value = freeTime.get(key); | |||
arr.addAll(freeTime.get(key)); | |||
} | |||
Map<Long, String> finalRooms = rooms; | |||
arr = arr.stream().map(j->{j.put("roomName", finalRooms.get(j.getLong("roomId")));return j;}).sorted(Comparator.comparing(po -> po.getString("startTime"))).collect(Collectors.toList()); | |||
return arr; | |||
} | |||
} |
@@ -90,6 +90,7 @@ public class DmStaffInnerApiController extends BaseApiController { | |||
if (r.getData() instanceof DmResourcesDto) { | |||
DmResourcesDto dmResourcesDto = (DmResourcesDto) r.getData(); | |||
v.setResourceId(dmResourcesDto.getId()); | |||
v.setAvatar(dmResourcesDto.getUrl()); | |||
} else { | |||
return output(ResponseCode.FILE_UPLOAD_FAIL, r.getMsg()).toJSON(); | |||
} | |||
@@ -108,6 +109,7 @@ public class DmStaffInnerApiController extends BaseApiController { | |||
if (r.getData() instanceof DmResourcesDto) { | |||
DmResourcesDto dmResourcesDto = (DmResourcesDto) r.getData(); | |||
staffPo.setResourceId(dmResourcesDto.getId()); | |||
staffPo.setAvatar(dmResourcesDto.getUrl()); | |||
} else { | |||
return output(ResponseCode.FILE_UPLOAD_FAIL, r.getMsg()).toJSON(); | |||
} | |||