当前位置: 首页 > news >正文

spring boot 集成科大讯飞星火认知大模型

首先到官网https://console.xfyun.cn/services/aidoc申请key
一、安装依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.6.13</version><relativePath/> <!-- lookup parent from repository --></parent><!-- Generated by https://start.springboot.io --><!-- 优质的 spring/boot/data/security/cloud 框架中文文档尽在 => https://springdoc.cn --><groupId>com.example</groupId><artifactId>xunfeigpt</artifactId><version>0.0.1-SNAPSHOT</version><name>xunfeigpt</name><description>xunfeigpt</description><properties><java.version>1.8</java.version><netty.verson>4.1.45.Final</netty.verson></properties><dependencies><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId><version>2.8.2</version></dependency><dependency><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp</artifactId><version>4.9.0</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.47</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId></dependency><dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>${netty.verson}</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.12.0</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build></project>

二、配置文件
在科大讯飞官网申请key

server:port: 1644xf:config:hostUrl: https://spark-api.xf-yun.com/v3.5/chatappId: xxxxxxxapiSecret: xxxxxxxxapiKey: xxxxxxxmaxResponseTime: 30

三、项目结构
在这里插入图片描述
四、各个模块代码
1、在bean目录下
1)Choices类

package com.example.xunfeigpt.bean;import lombok.Data;import java.util.List;@Data
public class Choices {private List<Text> text;
}

2)Header类型

import lombok.Data;@Data
public class Header {private int code;private int status;private String sid;
}

3)JsonParse类型

import lombok.Data;@Data
public class JsonParse {private Header header;private Payload payload;
}

4)NettyGroup类型

package com.example.xunfeigpt.bean;import io.netty.channel.Channel;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;import java.util.concurrent.ConcurrentHashMap;public class NettyGroup {private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);/*** 存放用户与Chanel的对应信息,用于给指定用户发送消息*/private static ConcurrentHashMap<String, Channel> channelMap = new ConcurrentHashMap<>();private NettyGroup() {}/*** 获取channel组*/public static ChannelGroup getChannelGroup() {return channelGroup;}/*** 获取连接channel map*/public static ConcurrentHashMap<String, Channel> getUserChannelMap() {return channelMap;}
}

5)Payload类型

import lombok.Data;@Data
public class Payload {private Choices choices;
}

6)ResultBean类型

package com.example.xunfeigpt.bean;import lombok.*;/*** @Author: ChengLiang* @CreateTime: 2023-05-22  11:04* @Description: TODO* @Version: 1.0*/
@Getter
@Setter
@ToString(callSuper = true)
@AllArgsConstructor
@NoArgsConstructor
public class ResultBean<T> {private String errorCode;private String message;private T data;public ResultBean(T data) {this.errorCode = ErrorMessage.SUCCESS.getErrorCode();this.message = ErrorMessage.SUCCESS.getMessage();this.data = data;}public ResultBean(ErrorMessage errorMessage, T data) {this.errorCode = errorMessage.getErrorCode();this.message = errorMessage.getMessage();this.data = data;}public static <T> ResultBean success(T data) {ResultBean resultBean = new ResultBean(data);return resultBean;}public static <T> ResultBean fail(T data) {ResultBean resultBean = new ResultBean(ErrorMessage.FAIL.getErrorCode(), ErrorMessage.FAIL.getMessage(), data);return resultBean;}public enum ErrorMessage {SUCCESS("0", "success"),FAIL("001", "fail"),NOAUTH("1001", "非法访问");private String errorCode;private String message;ErrorMessage(String errorCode, String message) {this.errorCode = errorCode;this.message = message;}public String getErrorCode() {return errorCode;}public void setErrorCode(String errorCode) {this.errorCode = errorCode;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}}
}

7)RoleContent类型

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@AllArgsConstructor
@NoArgsConstructor
public class RoleContent {public static final String ROLE_USER = "user";public static final String ROLE_ASSISTANT = "assistant";private String role;private String content;public static RoleContent createUserRoleContent(String content) {return new RoleContent(ROLE_USER, content);}public static RoleContent createAssistantRoleContent(String content) {return new RoleContent(ROLE_ASSISTANT, content);}
}

8)Text类型

import lombok.Data;@Data
public class Text {private String role;private String content;
}

2、在config目录下新建XFConfig类

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;@Data
@Component
@ConfigurationProperties("xf.config")
public class XFConfig {private String appId;private String apiSecret;private String apiKey;private String hostUrl;private Integer maxResponseTime;
}

3、在listener目录下
1)新建XFWebClient类

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.example.xunfeigpt.bean.RoleContent;import com.example.xunfeigpt.config.XFConfig;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;@Slf4j
@Component
public class XFWebClient {@Autowiredprivate XFConfig xfConfig;/*** 发送消息** @param uid       每个用户的id,用于区分不同用户* @param questions 发送给大模型的消息,可以包含上下文内容* @return 获取websocket连接,以便于我们在获取完整大模型回复后手动关闭连接*//*** @description: 发送请求至大模型方法* @author: ChengLiang* @date: 2023/10/19 16:27* @param: [用户id, 请求内容, 返回结果监听器listener]* @return: okhttp3.WebSocket**/public WebSocket sendMsg(String uid, List<RoleContent> questions, WebSocketListener listener) {// 获取鉴权urlString authUrl = null;try {authUrl = getAuthUrl(xfConfig.getHostUrl(), xfConfig.getApiKey(), xfConfig.getApiSecret());} catch (Exception e) {log.error("鉴权失败:{}", e);return null;}// 鉴权方法生成失败,直接返回 nullOkHttpClient okHttpClient = new OkHttpClient.Builder().build();// 将 https/http 连接替换为 ws/wss 连接String url = authUrl.replace("http://", "ws://").replace("https://", "wss://");Request request = new Request.Builder().url(url).build();// 建立 wss 连接WebSocket webSocket = okHttpClient.newWebSocket(request, listener);// 组装请求参数JSONObject requestDTO = createRequestParams(uid, questions);// 发送请求webSocket.send(JSONObject.toJSONString(requestDTO));return webSocket;}/*** @description: 鉴权方法* @author: ChengLiang* @date: 2023/10/19 16:25* @param: [讯飞大模型请求地址, apiKey, apiSecret]* @return: java.lang.String**/public static String getAuthUrl(String hostUrl, String apiKey, String apiSecret) throws Exception {URL url = new URL(hostUrl);// 时间SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);format.setTimeZone(TimeZone.getTimeZone("GMT"));String date = format.format(new Date());// 拼接String preStr = "host: " + url.getHost() + "\n" +"date: " + date + "\n" +"GET " + url.getPath() + " HTTP/1.1";// SHA256加密Mac mac = Mac.getInstance("hmacsha256");SecretKeySpec spec = new SecretKeySpec(apiSecret.getBytes(StandardCharsets.UTF_8), "hmacsha256");mac.init(spec);byte[] hexDigits = mac.doFinal(preStr.getBytes(StandardCharsets.UTF_8));// Base64加密String sha = Base64.getEncoder().encodeToString(hexDigits);// 拼接String authorization = String.format("api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"", apiKey, "hmac-sha256", "host date request-line", sha);// 拼接地址HttpUrl httpUrl = Objects.requireNonNull(HttpUrl.parse("https://" + url.getHost() + url.getPath())).newBuilder().//addQueryParameter("authorization", Base64.getEncoder().encodeToString(authorization.getBytes(StandardCharsets.UTF_8))).//addQueryParameter("date", date).//addQueryParameter("host", url.getHost()).//build();return httpUrl.toString();}/*** @description: 请求参数组装方法* @author: ChengLiang* @date: 2023/10/19 16:26* @param: [用户id, 请求内容]* @return: com.alibaba.fastjson.JSONObject**/public JSONObject createRequestParams(String uid, List<RoleContent> questions) {JSONObject requestJson = new JSONObject();// header参数JSONObject header = new JSONObject();header.put("app_id", xfConfig.getAppId());header.put("uid", uid);// parameter参数JSONObject parameter = new JSONObject();JSONObject chat = new JSONObject();chat.put("domain", "generalv2");chat.put("temperature", 0.5);chat.put("max_tokens", 4096);parameter.put("chat", chat);// payload参数JSONObject payload = new JSONObject();JSONObject message = new JSONObject();JSONArray jsonArray = new JSONArray();jsonArray.addAll(questions);message.put("text", jsonArray);payload.put("message", message);requestJson.put("header", header);requestJson.put("parameter", parameter);requestJson.put("payload", payload);return requestJson;}
}

2)新建XFWebSocketListener类

package com.example.xunfeigpt.listener;import com.alibaba.fastjson.JSON;import com.example.xunfeigpt.bean.JsonParse;
import com.example.xunfeigpt.bean.Text;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;import java.io.IOException;
import java.util.*;@Slf4j
public class XFWebSocketListener extends WebSocketListener {//断开websocket标志位private boolean wsCloseFlag = false;//语句组装buffer,将大模型返回结果全部接收,在组装成一句话返回private StringBuilder answer = new StringBuilder();public String getAnswer() {return answer.toString();}public boolean isWsCloseFlag() {return wsCloseFlag;}@Overridepublic void onOpen(WebSocket webSocket, Response response) {super.onOpen(webSocket, response);log.info("大模型服务器连接成功!");}@Overridepublic void onMessage(WebSocket webSocket, String text) {super.onMessage(webSocket, text);JsonParse myJsonParse = JSON.parseObject(text, JsonParse.class);log.info("myJsonParse:{}", JSON.toJSONString(myJsonParse));if (myJsonParse.getHeader().getCode() != 0) {log.error("发生错误,错误信息为:{}", JSON.toJSONString(myJsonParse.getHeader()));this.answer.append("大模型响应异常,请联系管理员");// 关闭连接标识wsCloseFlag = true;return;}List<Text> textList = myJsonParse.getPayload().getChoices().getText();for (Text temp : textList) {log.info("返回结果信息为:【{}】", JSON.toJSONString(temp));this.answer.append(temp.getContent());}log.info("result:{}", this.answer.toString());if (myJsonParse.getHeader().getStatus() == 2) {wsCloseFlag = true;//todo 将问答信息入库进行记录,可自行实现}}@Overridepublic void onFailure(WebSocket webSocket, Throwable t, Response response) {super.onFailure(webSocket, t, response);try {if (null != response) {int code = response.code();log.error("onFailure body:{}", response.body().string());if (101 != code) {log.error("讯飞星火大模型连接异常");}}} catch (IOException e) {log.error("IO异常:{}", e);}}
}

4、在netty目录下
1)新建NettyServer类

package com.example.xunfeigpt.netty;import com.example.xunfeigpt.netty.handler.WebSocketHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
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.serialization.ObjectEncoder;
import io.netty.handler.stream.ChunkedWriteHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.net.InetSocketAddress;@Slf4j
@Component
public class NettyServer {/*** webSocket协议名*/private static final String WEBSOCKET_PROTOCOL = "WebSocket";/*** 端口号*/@Value("${webSocket.netty.port:62632}")private int port;/*** webSocket路径*/@Value("${webSocket.netty.path:/webSocket}")private String webSocketPath;@Autowiredprivate WebSocketHandler webSocketHandler;private EventLoopGroup bossGroup;private EventLoopGroup workGroup;/*** 启动** @throws InterruptedException*/private void start() throws InterruptedException {bossGroup = new NioEventLoopGroup();workGroup = new NioEventLoopGroup();ServerBootstrap bootstrap = new ServerBootstrap();// bossGroup辅助客户端的tcp连接请求, workGroup负责与客户端之前的读写操作bootstrap.group(bossGroup, workGroup);// 设置NIO类型的channelbootstrap.channel(NioServerSocketChannel.class);// 设置监听端口bootstrap.localAddress(new InetSocketAddress(port));// 连接到达时会创建一个通道bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {// 流水线管理通道中的处理程序(Handler),用来处理业务// webSocket协议本身是基于http协议的,所以这边也要使用http编解码器ch.pipeline().addLast(new HttpServerCodec());ch.pipeline().addLast(new ObjectEncoder());// 以块的方式来写的处理器ch.pipeline().addLast(new ChunkedWriteHandler());/*说明:1、http数据在传输过程中是分段的,HttpObjectAggregator可以将多个段聚合2、这就是为什么,当浏览器发送大量数据时,就会发送多次http请求*/ch.pipeline().addLast(new HttpObjectAggregator(8192));/*说明:1、对应webSocket,它的数据是以帧(frame)的形式传递2、浏览器请求时 ws://localhost:58080/xxx 表示请求的uri3、核心功能是将http协议升级为ws协议,保持长连接*/ch.pipeline().addLast(new WebSocketServerProtocolHandler(webSocketPath, WEBSOCKET_PROTOCOL, true, 65536 * 10));// 自定义的handler,处理业务逻辑ch.pipeline().addLast(webSocketHandler);}});// 配置完成,开始绑定server,通过调用sync同步方法阻塞直到绑定成功ChannelFuture channelFuture = bootstrap.bind().sync();log.info("Server started and listen on:{}", channelFuture.channel().localAddress());// 对关闭通道进行监听channelFuture.channel().closeFuture().sync();}/*** 释放资源** @throws InterruptedException*/@PreDestroypublic void destroy() throws InterruptedException {if (bossGroup != null) {bossGroup.shutdownGracefully().sync();}if (workGroup != null) {workGroup.shutdownGracefully().sync();}}@PostConstruct()public void init() {//需要开启一个新的线程来执行netty server 服务器new Thread(() -> {try {start();log.info("消息推送线程开启!");} catch (InterruptedException e) {e.printStackTrace();}}).start();}
}

2)在handler目录下新建handler类

package com.example.xunfeigpt.netty.handler;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;import com.example.xunfeigpt.bean.NettyGroup;
import com.example.xunfeigpt.bean.ResultBean;
import com.example.xunfeigpt.service.PushService;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.AttributeKey;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;@Slf4j
@Component
@ChannelHandler.Sharable
public class WebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {@Autowiredprivate PushService pushService;@Overridepublic void handlerAdded(ChannelHandlerContext ctx) throws Exception {log.info("handlerAdded被调用,{}", JSON.toJSONString(ctx));//todo 添加校验功能,校验合法后添加到group中// 添加到channelGroup 通道组NettyGroup.getChannelGroup().add(ctx.channel());}@Overrideprotected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {log.info("服务器收到消息:{}", msg.text());// 获取用户ID,关联channelJSONObject jsonObject = JSON.parseObject(msg.text());String channelId = jsonObject.getString("uid");// 将用户ID作为自定义属性加入到channel中,方便随时channel中获取用户IDAttributeKey<String> key = AttributeKey.valueOf("userId");//String channelId = CharUtil.generateStr(uid);NettyGroup.getUserChannelMap().put(channelId, ctx.channel());boolean containsKey = NettyGroup.getUserChannelMap().containsKey(channelId);//通道已存在,请求信息返回if (containsKey) {//接收消息格式{"uid":"123456","text":"中华人民共和国成立时间"}String text = jsonObject.getString("text");//请求大模型服务器,获取结果ResultBean resultBean = pushService.pushMessageToXFServer(channelId, text);String data = (String) resultBean.getData();//推送pushService.pushToOne(channelId, JSON.toJSONString(data));} else {ctx.channel().attr(key).setIfAbsent(channelId);log.info("连接通道id:{}", channelId);// 回复消息ctx.channel().writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(ResultBean.success(channelId))));}}@Overridepublic void handlerRemoved(ChannelHandlerContext ctx) throws Exception {log.info("handlerRemoved被调用,{}", JSON.toJSONString(ctx));// 删除通道NettyGroup.getChannelGroup().remove(ctx.channel());removeUserId(ctx);}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {log.info("通道异常:{}", cause.getMessage());// 删除通道NettyGroup.getChannelGroup().remove(ctx.channel());removeUserId(ctx);ctx.close();}private void removeUserId(ChannelHandlerContext ctx) {AttributeKey<String> key = AttributeKey.valueOf("userId");String userId = ctx.channel().attr(key).get();NettyGroup.getUserChannelMap().remove(userId);}
}

5、在service目录下
1)新建PushService接口

import com.example.xunfeigpt.bean.ResultBean;public interface PushService {void pushToOne(String uid, String text);void pushToAll(String text);ResultBean pushMessageToXFServer(String uid, String text);
}

2)在impl文件夹下新建PushServiceImpl类

package com.example.xunfeigpt.service.impl;import com.alibaba.fastjson.JSON;import com.example.xunfeigpt.bean.NettyGroup;
import com.example.xunfeigpt.bean.ResultBean;
import com.example.xunfeigpt.bean.RoleContent;
import com.example.xunfeigpt.config.XFConfig;
import com.example.xunfeigpt.listener.XFWebClient;
import com.example.xunfeigpt.listener.XFWebSocketListener;
import com.example.xunfeigpt.service.PushService;
import io.netty.channel.Channel;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.extern.slf4j.Slf4j;
import okhttp3.WebSocket;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;@Slf4j
@Service
public class PushServiceImpl implements PushService {@Autowiredprivate XFConfig xfConfig;@Autowiredprivate XFWebClient xfWebClient;@Overridepublic void pushToOne(String uid, String text) {if (StringUtils.isEmpty(uid) || StringUtils.isEmpty(text)) {log.error("uid或text均不能为空");throw new RuntimeException("uid或text均不能为空");}ConcurrentHashMap<String, Channel> userChannelMap = NettyGroup.getUserChannelMap();for (String channelId : userChannelMap.keySet()) {if (channelId.equals(uid)) {Channel channel = userChannelMap.get(channelId);if (channel != null) {ResultBean success = ResultBean.success(text);channel.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(success)));log.info("信息发送成功:{}", JSON.toJSONString(success));} else {log.error("该id对于channelId不存在!");}return;}}log.error("该用户不存在!");}@Overridepublic void pushToAll(String text) {String trim = text.trim();ResultBean success = ResultBean.success(trim);NettyGroup.getChannelGroup().writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(success)));log.info("信息推送成功:{}", JSON.toJSONString(success));}//测试账号只有2个并发,此处只使用一个,若是生产环境允许多个并发,可以采用分布式锁@Overridepublic synchronized ResultBean pushMessageToXFServer(String uid, String text) {RoleContent userRoleContent = RoleContent.createUserRoleContent(text);ArrayList<RoleContent> questions = new ArrayList<>();questions.add(userRoleContent);XFWebSocketListener xfWebSocketListener = new XFWebSocketListener();WebSocket webSocket = xfWebClient.sendMsg(uid, questions, xfWebSocketListener);if (webSocket == null) {log.error("webSocket连接异常");ResultBean.fail("请求异常,请联系管理员");}try {int count = 0;int maxCount = xfConfig.getMaxResponseTime() * 5;while (count <= maxCount) {Thread.sleep(200);if (xfWebSocketListener.isWsCloseFlag()) {break;}count++;}if (count > maxCount) {return ResultBean.fail("响应超时,请联系相关人员");}return ResultBean.success(xfWebSocketListener.getAnswer());} catch (Exception e) {log.error("请求异常:{}", e);} finally {webSocket.close(1000, "");}return ResultBean.success("");}
}

6、测试controller

import com.example.xunfeigpt.bean.ResultBean;
import com.example.xunfeigpt.service.PushService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@Slf4j
@RestController
@RequestMapping("/xfModel")
public class XFMessageController {@Autowiredprivate PushService pushService;@GetMapping("/test")public ResultBean test(String uid, String text) {if (StringUtils.isEmpty(uid) || StringUtils.isEmpty(text)) {log.error("uid或text不能为空");return ResultBean.fail("uid或text不能为空");}return pushService.pushMessageToXFServer(uid, text);}
}

7、websocket测试
地址:ws://localhost:62632/webSocket

传参:

["uid":"xxxxxx","text";"水泥的种类有哪些?}

在这里插入图片描述

相关文章:

spring boot 集成科大讯飞星火认知大模型

首先到官网https://console.xfyun.cn/services/aidoc申请key 一、安装依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance&…...

springboot/ssm高校宣讲会管理系统Java企业招聘宣讲系统web

演示视频&#xff1a;https://www.bilibili.com/video/BV1vz421R7cg/、 基于springboot(可改ssm)vue项目 开发语言&#xff1a;Java 框架&#xff1a;springboot/可改ssm vue JDK版本&#xff1a;JDK1.8&#xff08;或11&#xff09; 服务器&#xff1a;tomcat 数据库&am…...

2024.02.23作业

1. 尝试处理普通信号 #include "test.h"#define MAXSIZE 128void handler(int signo) {if (SIGINT signo){printf("用户按下了 ctrl c 键\n");} }int main(int argc, char const *argv[]) {if (signal(SIGINT, SIG_IGN) SIG_ERR){perror("signal …...

倒模专用制作耳机壳UV树脂:改性丙烯酸树脂

倒模专用制作耳机壳的UV树脂是经过改性的丙烯酸树脂&#xff0c;具有高透明度、高粘度、快速固化的特点。这种树脂可以通过紫外线光固化&#xff0c;快速形成坚硬的表面&#xff0c;并且具有较高的硬度和耐磨性&#xff0c;因此非常适合用于制作耳机壳。 此外&#xff0c;改性丙…...

chatgpt:还有哪些人工智能和科技值得关注?

今天&#xff0c;很多人的目光都被ChatGPT吸引&#xff0c;其实&#xff0c;人工智能的范围很大&#xff0c;远不止ChatGPT或者其他自然语言的处理工具。所以说不管ChatGPT的结果如何&#xff0c;人工智能依然是未来。 那么在ChatGPT之外&#xff0c;还有没有什么值得关注的人…...

LeetCode 2997.使数组异或和等于K的最少操作次数

给你一个下标从 0 开始的整数数组 nums 和一个正整数 k 。 你可以对数组执行以下操作 任意次 &#xff1a; 选择数组里的 任意 一个元素&#xff0c;并将它的 二进制 表示 翻转 一个数位&#xff0c;翻转数位表示将 0 变成 1 或者将 1 变成 0 。 你的目标是让数组里 所有 元素…...

计算机设计大赛 深度学习大数据物流平台 python

文章目录 0 前言1 课题背景2 物流大数据平台的架构与设计3 智能车货匹配推荐算法的实现**1\. 问题陈述****2\. 算法模型**3\. 模型构建总览 **4 司机标签体系的搭建及算法****1\. 冷启动**2\. LSTM多标签模型算法 5 货运价格预测6 总结7 部分核心代码8 最后 0 前言 &#x1f5…...

WPF 附加属性+控件模板,完成自定义控件。建议观看HandyControl源码

文章目录 相关连接前言需要实现的效果附加属性添加附加属性&#xff0c;以Test修改FontSize为例依赖属性使用触发器使用直接操控 结论 控件模板&#xff0c;在HandyControl的基础上面进行修改参考HandyControl的源码控件模板原型控件模板 控件模板触发器完整样式简单使用 结论 …...

编程笔记 Golang基础 040 defer、panic 和 recover

编程笔记 Golang基础 040 defer、panic 和 recover 一、defer二、panic三、recover小结 在Go语言中&#xff0c;defer、panic 和 recover 是一组用于错误处理和控制程序流程的关键字。它们之间的交互有助于实现异常处理机制&#xff0c;并确保资源的正确释放。 一、defer defe…...

通过redfish协议实现服务器固件升级、从虚拟光驱启动自检盘并等待完成,最后截图保存

通过redfish协议实现服务器固件升级、从虚拟光驱启动自检盘并等待完成,最后截图保存 版本信息代码新开发的PCIE设备在做服务器适配时,有时需要服务器厂家更新BMC或BIOS固件。同时,我们也希望对PCIE设备做一些检测,最后收集一些信息存档。如果需要处理的服务器很多,通过BMC的界面…...

ARM 版银河麒麟桌面系统下 Qt 开发环境搭建指南

目录 前言安装Linux ARM 版 QtCreator配置 Qt Creator配置构建套件 第一个麒麟 Qt 应用程序小结 前言 在上一篇文章信创ARM架构QT应用开发环境搭建中建议大家使用 Ubuntu X86 系统作为信创 ARM 架构 QT 应用的开发环境&#xff0c;里面使用了交叉编译的方式。这对于自己的 Qt …...

架构面试题汇总:缓存(二)

目录 1. 问题&#xff1a;什么是缓存&#xff0c;以及为什么我们需要缓存&#xff1f;2. 问题&#xff1a;你能解释一下缓存击穿、缓存雪崩和缓存预热是什么吗&#xff1f;3. 问题&#xff1a;如何在Java中实现缓存&#xff1f;4. 问题&#xff1a;你如何决定哪些数据应该被缓存…...

【docker入门】1-

文章目录 参考&#xff1a; Docker – 容器虚拟化平台。 参考&#xff1a; docker入门&#xff0c;这一篇就够了。【零基础入门Docker】Dockerfile中的USER指令以及dockerfile命令详解dockerfile copy命令...

微信小程序-全局配置

个人笔记&#xff0c;仅供参考。 1.entryPagePath 代码&#xff1a; "entryPagePath": "pages/index/index" 具体用法&#xff1a; 2.pages 小程序中新增/减少页面&#xff0c;都需要对 pages 数组进行修改。 代码&#xff1a; "pages": [&…...

【Android】性能优化之内存、网络、布局、卡顿、安装包、启动速度优化

欢迎来到 Android 开发老生常谈的性能优化篇&#xff0c;本文将性能优化划分为内存、网络、布局、卡顿、安装包、启动速度七块&#xff0c;从这七块优化出发&#xff0c;阐述优化的 Application 的方式。 目录 内存优化避免内存泄漏使用内存分析工具优化数据结构和算法数据缓存…...

第3.6章:StarRocks数据导入——DataX StarRocksWriter

一、Datax 1.1 DataX 3.0概述 DataX3.0是一个异构数据源离线同步工具&#xff0c;可以方便的对各种异构数据源进行高效的数据同步。 其github地址为&#xff1a; https://github.com/alibaba/DataX/blob/master/introduction.mdhttps://github.com/alibaba/DataX/blob/mast…...

【非递归版】归并排序算法(2)

目录 MergeSortNonR归并排序 非递归&归并排序VS快速排序 整体思想 图解分析​ 代码实现 时间复杂度 归并排序在硬盘上的应用&#xff08;外排序&#xff09; MergeSortNonR归并排序 前面的快速排序的非递归实现&#xff0c;我们借助栈实现。这里我们能否也借助栈去…...

[C++]C++实现本地TCP通讯的示例代码

这篇文章主要为大家详细介绍了C如何利用TCP技术,实现本地ROS1和ROS2的通讯,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下 概要服务端代码 头文件源代码客户端代码 概要 利用TCP技术&#xff0c;实现本地ROS1和ROS2的通讯。 服务端代码 头文件 #include &…...

Sora - 探索AI视频模型的无限可能

文章目录 每日一句正能量前言技术解析应用场景未来展望伦理与创意用户体验与互动后记 每日一句正能量 . 一个人&#xff0c;如果没有经受过投资失败的痛楚&#xff0c;又怎么会看到绝望之后的海阔天空。很多时候&#xff0c;经历了人生中最艰难的事&#xff0c;反而锻造了最坚强…...

【JavaScript 漫游】【022】事件模型

文章简介 本篇文章为【JavaScript 漫游】专栏的第 022 篇文章&#xff0c;对 JavaScript 中事件模型相关的知识点进行了总结。 监听函数 浏览器的事件模型&#xff0c;就是通过监听函数&#xff08;listener&#xff09;对事件做出反应。事件发生后&#xff0c;浏览器监听到…...

【加密算法】RSA非对称加密算法简介

目录 前言 工作原理 密钥生成 加密和解密 在Java中使用RSA 生成密钥对 加密和解密数据 加密数据 解密数据 注意事项和最佳实践 结论 前言 RSA&#xff08;Rivest-Shamir-Adleman&#xff09;是一种基于数论的非对称加密算法&#xff0c;广泛应用于数字签名、数据加密…...

深入理解 JavaScript 对象原型,解密原型链之谜(上)

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…...

产品经理学习-产品运营《什么是SOP》

目录 什么是SOP 如何执行SOP 执行SOP的重点 什么是SOP SOP就是项目流程操作的说明书 日常工作中的例行操作&#xff1a; 例行操作是指&#xff0c;在每一天&#xff0c;针对每一个用户&#xff0c;在每个项目之中&#xff0c;都必须完成的操作&#xff0c;这些必须完成的操…...

大数据Hadoop生态圈

存储&#xff1a; HDFS(namenode,datanode) 计算&#xff1a;MapReduce(mapreduce&#xff0c;基于磁盘) 便于用sql操作&#xff1a;Hive(核心 metastore&#xff0c;存储这些结构化的数据)&#xff0c;同类的还有Impala&#xff0c;hbase等 基于yaml的资源调度 hive &…...

算法简介:查找与算法运行时间

文章目录 1. 二分查找与简单查找1.1 运行时间 2. 旅行商问题 算法是一组完成任务的指令。任何代码片段都可以视为算法。 1. 二分查找与简单查找 二分查找是一种算法&#xff0c;其输入是一个有序的元素列表&#xff0c;如果要查找的元素包含在列表中&#xff0c;二分查找返回…...

零基础C++开发上位机--基于QT5.15的串口助手(三)

本系列教程本着实践的目的&#xff0c;争取每一节课都带大家做一个小项目&#xff0c;让大家多实践多试验&#xff0c;这样才能知道自己学会与否。 接下来我们这节课&#xff0c;主要学习一下QT的串口编程。做一款自己的串口助手&#xff0c;那么这里默认大家都是具备串口通信…...

Facebook的虚拟社交愿景:元宇宙时代的新起点

在当今数字化时代&#xff0c;社交媒体已经成为人们生活中不可或缺的一部分。而随着科技的不断进步和社会的发展&#xff0c;元宇宙已经成为了人们关注的热点话题之一。作为社交媒体的领军企业之一&#xff0c;Facebook也在积极探索虚拟社交的未来&#xff0c;将其视为元宇宙时…...

【深度学习笔记】4_6 模型的GPU计算

注&#xff1a;本文为《动手学深度学习》开源内容&#xff0c;部分标注了个人理解&#xff0c;仅为个人学习记录&#xff0c;无抄袭搬运意图 4.6 GPU计算 到目前为止&#xff0c;我们一直在使用CPU计算。对复杂的神经网络和大规模的数据来说&#xff0c;使用CPU来计算可能不够…...

留学申请过程中如何合理使用AI?大学招生官怎么看?

我们采访过的学生表示&#xff0c;他们在写essay的过程中会使用 ChatGPT&#xff0c;主要用于以下两个方面&#xff1a;第一&#xff0c;生成想法和头脑风暴&#xff1b;第二&#xff0c;拼写和语法检查。 纽约时报的娜塔莎辛格&#xff08;Natasha Singer&#xff09;在一篇文…...

vue2与vue3的diff算法有什么区别

在 Vue 中&#xff0c;虚拟 DOM 是一种重要的概念&#xff0c;它通过将真实的 DOM 操作转化为对虚拟 DOM 的操作&#xff0c;从而提高应用的性能。Vue 框架在虚拟 DOM 的更新过程中采用了 Diff 算法&#xff0c;用于比较新旧虚拟节点树&#xff0c;找出需要更新的部分&#xff…...

wordpress媒体库不能用云/站长工具最近查询

之前我们讨论过《Linux Oracle 11g dataguard物理standby 配置过程》&#xff0c; 但是在实际过程中会遇到不同的问题&#xff0c;首先我们讨论下&#xff2f;&#xff32;&#xff21;&#xff23;&#xff2c;&#xff25; &#xff24;&#xff21;&#xff34;&#xff21…...

广告平台源码/seo可以从哪些方面优化

PyInstaller的原理简介PyInstaller其实就是把python解析器和你自己的脚本打包成一个可执行的文件&#xff0c;和编译成真正的机器码完全是两回事&#xff0c;所以千万不要指望成打包成一个可执行文件会提高运行效率&#xff0c;相反可能会降低运行效率&#xff0c;好处就是在运…...

外贸都是在哪些网站做/seo排名优化方式

在《Server 层混杂信息字典表 | 全方位认识 information_schema》中&#xff0c;我们详细介绍了information_schema下的状态变量、系统变量、进程状态、字符集和校对规则等字典表&#xff0c;本期我们将为大家带来系列第五篇《InnoDB 层系统字典表 | 全方位认识 information_sc…...

汕头网站建设公司有哪些/做网站的步骤

⭐️这篇博客是对上一篇博客中进行扩展&#xff0c;我们应该知道&#xff0c;位图的效率确实很高&#xff0c;但是有一个致命的缺点——只能对整形数据进行处理。今天要介绍的布隆过滤器就是来解决这个缺点的&#xff0c;看完下面的内容&#xff0c;相信你会了解这个数据结构。…...

网站建设买服务器还是数据库/最新足球新闻头条

由于组件提供的方式不同&#xff0c;所以安装的方法也是不一样的&#xff0c;下面就目前常见的各种形式的组 件的安装方法介绍一下。 1. 只有一个DCU文件的组件。DCU文件是编译好的单元文件&#xff0c;这样的组件是作者不想把源 码公布。一般来说&#xff0c;作者必须说明此…...

天天网站建设/西安网站关键词排名

本来不想谈这种招人骂的话题的&#xff0c;哎&#xff0c;不过今天在一个&#xff31;&#xff31;群中遇到了类似的情况&#xff0c;站在现实的角度&#xff0c;说了几句自己学校的不好&#xff0c;然后遇到了一个喷子&#xff0c;而且还不是我们学校的。让我觉的这件事情很奇…...