commit 543e41ba1eefec879ff1d4fef782fb0c91d002ef Author: 冯凯 <371894675@qq.com> Date: Tue Oct 3 20:55:48 2023 +0800 提交 数组和讯飞大模型 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..05d7c76 --- /dev/null +++ b/.gitignore @@ -0,0 +1,47 @@ +###################################################################### +# Build Tools + +.gradle +/build/ +!gradle/wrapper/gradle-wrapper.jar + +target/ +!.mvn/wrapper/maven-wrapper.jar + +###################################################################### +# IDE + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### JRebel ### +rebel.xml +### NetBeans ### +nbproject/private/ +build/* +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ +target/ + +###################################################################### +# Others +*.log +*.xml.versionsBackup +*.swp + +!*/build/*.java +!*/build/*.html +!*/build/*.xml diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..c57922e --- /dev/null +++ b/pom.xml @@ -0,0 +1,74 @@ + + + 4.0.0 + + com.durant + Durant + 1.0-SNAPSHOT + + + 17 + 17 + UTF-8 + + + org.springframework.boot + spring-boot-starter-parent + 2.7.15 + + + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + cn.hutool + hutool-all + 5.8.18 + + + org.springframework.boot + spring-boot-starter-web + + + com.alibaba + fastjson + 1.2.67 + + + org.java-websocket + Java-WebSocket + 1.3.8 + + + com.squareup.okhttp3 + okhttp + 4.10.0 + + + org.projectlombok + lombok + + + org.slf4j + slf4j-log4j12 + + + mysql + mysql-connector-java + 5.1.32 + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 2.3.1 + + + + + diff --git a/src/main/java/com/array/Test1.java b/src/main/java/com/array/Test1.java new file mode 100644 index 0000000..023207e --- /dev/null +++ b/src/main/java/com/array/Test1.java @@ -0,0 +1,49 @@ +package com.array; + +import java.util.Arrays; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +/** + * @author 冯凯 + * @version 1.0 + * @description: TODO + * @date 2023/10/1 18:50 + */ +public class Test1 { + + public static int stepNum(int[] arr){ + //定义length获取数组的长度 + int length=arr.length; + //初始化边界值end为0 + int end=0; + //初始化最后一步能达到的最大下标初始化为0 + int maxIndex=0; + //定义step为所跳步数初始化为0; + int step=0; + //循环遍历数组 + for (int i=0;i implements Serializable { + + private static final long serialVersionUID = 1L; + /** + * 成功 + */ + public static final int SUCCESS = 200; + /** + * 失败 + */ + public static final int FAIL = 500; + /** + * 返回状态码 + */ + private int code; + /** + * 响应信息 + */ + private String msg; + /** + * 响应数据 + */ + private T data; + + public static Result success() { + return restResult(null, SUCCESS, "操作成功"); + } + + public static Result success(T data) { + return restResult(data, SUCCESS, "操作成功"); + } + + public static Result success(T data, String msg) { + return restResult(data, SUCCESS, msg); + } + + public static Result error() { + return restResult(null, FAIL, "操作失败"); + } + + public static Result error(String msg) { + return restResult(null, FAIL, msg); + } + + public static Result error(T data) { + return restResult(data, FAIL, "操作失败"); + } + + public static Result error(T data, String msg) { + return restResult(data, FAIL, msg); + } + + public static Result error(int code, String msg) { + return restResult(null, code, msg); + } + + private static Result restResult(T data, int code, String msg) { + Result apiResult = new Result<>(); + apiResult.setCode(code); + apiResult.setData(data); + apiResult.setMsg(msg); + return apiResult; + } +} diff --git a/src/main/java/com/durant/XfXhApplication.java b/src/main/java/com/durant/XfXhApplication.java new file mode 100644 index 0000000..4a9aad5 --- /dev/null +++ b/src/main/java/com/durant/XfXhApplication.java @@ -0,0 +1,19 @@ +package com.durant; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @author 冯凯 + * @version 1.0 + * @description: TODO + * @date 2023/9/29 14:09 + */ +@SpringBootApplication +public class XfXhApplication { + + public static void main(String[] args) { + SpringApplication.run(XfXhApplication.class); + } + +} diff --git a/src/main/java/com/durant/component/XfXhStreamClient.java b/src/main/java/com/durant/component/XfXhStreamClient.java new file mode 100644 index 0000000..619e126 --- /dev/null +++ b/src/main/java/com/durant/component/XfXhStreamClient.java @@ -0,0 +1,156 @@ +package com.durant.component; + +import com.alibaba.fastjson.JSONObject; +import com.durant.config.XfXhConfig; +import com.durant.dto.MsgDTO; +import com.durant.dto.request.RequestDTO; +import lombok.extern.slf4j.Slf4j; +import okhttp3.*; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +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.*; + +/** + * @author 冯凯 + * @version 1.0 + * @description: TODO + * @date 2023/9/29 14:02 + */ +@Component +@Slf4j +public class XfXhStreamClient { + + /** + * @author 狐狸半面添 + * @create 2023-09-15 1:10 + */ + @Resource + private XfXhConfig xfXhConfig; + + @Value("${xfxh.QPS}") + private int connectionTokenCount; + + /** + * 获取令牌 + */ + public static int GET_TOKEN_STATUS = 0; + /** + * 归还令牌 + */ + public static int BACK_TOKEN_STATUS = 1; + + /** + * 操作令牌 + * + * @param status 0-获取令牌 1-归还令牌 + * @return 是否操作成功 + */ + public synchronized boolean operateToken(int status) { + if (status == GET_TOKEN_STATUS) { + // 获取令牌 + if (connectionTokenCount != 0) { + // 说明还有令牌,将令牌数减一 + connectionTokenCount -= 1; + return true; + } else { + return false; + } + } else { + // 放回令牌 + connectionTokenCount += 1; + return true; + } + } + + /** + * 发送消息 + * + * @param uid 每个用户的id,用于区分不同用户 + * @param msgList 发送给大模型的消息,可以包含上下文内容 + * @return 获取websocket连接,以便于我们在获取完整大模型回复后手动关闭连接 + */ + public WebSocket sendMsg(String uid, List msgList, WebSocketListener listener) { + // 获取鉴权url + String authUrl = this.getAuthUrl(); + // 鉴权方法生成失败,直接返回 null + if (authUrl == null) { + return null; + } + OkHttpClient 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); + // 组装请求参数 + RequestDTO requestDTO = getRequestParam(uid, msgList); + // 发送请求 + webSocket.send(JSONObject.toJSONString(requestDTO)); + return webSocket; + } + + /** + * 生成鉴权方法,具体实现不用关心,这是讯飞官方定义的鉴权方式 + * + * @return 鉴权访问大模型的路径 + */ + public String getAuthUrl() { + try { + URL url = new URL(xfXhConfig.getHostUrl()); + // 时间 + 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(xfXhConfig.getApiSecret().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 authorizationOrigin = String.format("api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"", xfXhConfig.getApiKey(), "hmac-sha256", "host date request-line", sha); + // 拼接地址 + HttpUrl httpUrl = Objects.requireNonNull(HttpUrl.parse("https://" + url.getHost() + url.getPath())).newBuilder(). + addQueryParameter("authorization", Base64.getEncoder().encodeToString(authorizationOrigin.getBytes(StandardCharsets.UTF_8))). + addQueryParameter("date", date). + addQueryParameter("host", url.getHost()). + build(); + + return httpUrl.toString(); + } catch (Exception e) { + log.error("鉴权方法中发生错误:" + e.getMessage()); + return null; + } + } + + /** + * 获取请求参数 + * + * @param uid 每个用户的id,用于区分不同用户 + * @param msgList 发送给大模型的消息,可以包含上下文内容 + * @return 请求DTO,该 DTO 转 json 字符串后生成的格式参考 resources/demo-json/request.json + */ + public RequestDTO getRequestParam(String uid, List msgList) { + RequestDTO requestDTO = new RequestDTO(); + requestDTO.setHeader(new RequestDTO.HeaderDTO(xfXhConfig.getAppId(), uid)); + requestDTO.setParameter(new RequestDTO.ParameterDTO(new RequestDTO.ParameterDTO.ChatDTO(xfXhConfig.getDomain(), xfXhConfig.getTemperature(), xfXhConfig.getMaxTokens()))); + requestDTO.setPayload(new RequestDTO.PayloadDTO(new RequestDTO.PayloadDTO.MessageDTO(msgList))); + return requestDTO; + } + + + +} diff --git a/src/main/java/com/durant/config/CorsConfig.java b/src/main/java/com/durant/config/CorsConfig.java new file mode 100644 index 0000000..052a96a --- /dev/null +++ b/src/main/java/com/durant/config/CorsConfig.java @@ -0,0 +1,24 @@ +package com.durant.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + + +/** + * @author 冯凯 + * @version 1.0 + * @description: TODO + * @date 2023/10/3 14:43 + */ +@Configuration +public class CorsConfig implements WebMvcConfigurer { + + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/test/sendQuestion") + .allowedOrigins("http://localhost:9528") + .allowedMethods("GET", "POST") + .allowCredentials(true) + .maxAge(3600); + } +} diff --git a/src/main/java/com/durant/config/XfXhConfig.java b/src/main/java/com/durant/config/XfXhConfig.java new file mode 100644 index 0000000..94ecd74 --- /dev/null +++ b/src/main/java/com/durant/config/XfXhConfig.java @@ -0,0 +1,54 @@ +package com.durant.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +/** + * @author 冯凯 + * @version 1.0 + * @description: TODO + * @date 2023/9/29 13:50 + */ +@Configuration +@ConfigurationProperties(prefix = "xfxh") +@Data +public class XfXhConfig { + + + /** + * 服务引擎使用 讯飞星火认知大模型V2.0,如果使用 V1.5 需要将 hostUrl 修改为 https://spark-api.xf-yun.com/v1.1/chat + */ + private String hostUrl; + /** + * 发送请求时指定的访问领域,如果是 V1.5版本 设置为 general,如果是 V2版本 设置为 generalv2 + */ + private String domain; + /** + * 核采样阈值。用于决定结果随机性,取值越高随机性越强即相同的问题得到的不同答案的可能性越高。取值 [0,1] + */ + private Float temperature; + /** + * 模型回答的tokens的最大长度,V1.5取值为[1,4096],V2.0取值为[1,8192]。 + */ + private Integer maxTokens; + /** + * 大模型回复问题的最大响应时长,单位 s + */ + private Integer maxResponseTime; + /** + * 用于权限验证,从服务接口认证信息中获取 + */ + private String appId; + /** + * 用于权限验证,从服务接口认证信息中获取 + */ + private String apiKey; + /** + * 用于权限验证,从服务接口认证信息中获取 + */ + private String apiSecret; + + + +} diff --git a/src/main/java/com/durant/controller/LoginController.java b/src/main/java/com/durant/controller/LoginController.java new file mode 100644 index 0000000..819f057 --- /dev/null +++ b/src/main/java/com/durant/controller/LoginController.java @@ -0,0 +1,43 @@ +package com.durant.controller; + +import com.durant.Result; +import com.durant.dto.request.LoginReq; +import com.durant.service.LoginService; +import org.springframework.beans.factory.annotation.Autowired; +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.RestController; + +/** + * @author 冯凯 + * @version 1.0 + * @description: TODO + * @date 2023/10/3 14:08 + */ +@RestController +public class LoginController { + @Autowired + private LoginService loginService; + + @PostMapping("/user/login") + + public Result userLogin(@RequestBody LoginReq loginReq){ + Result loginReqResult = loginService.userLogin(loginReq); + if (loginReqResult!=null){ + return Result.success(loginReq); + } + + return Result.error(); + } + + @GetMapping("/user/info") + + public Result userInfo(){ + LoginReq loginReq = new LoginReq(); + loginReq.setUsername("fengkai"); + loginReq.setPassword("123456"); + return Result.success(loginReq); + } + +} diff --git a/src/main/java/com/durant/controller/TestController.java b/src/main/java/com/durant/controller/TestController.java new file mode 100644 index 0000000..588f9dd --- /dev/null +++ b/src/main/java/com/durant/controller/TestController.java @@ -0,0 +1,97 @@ +package com.durant.controller; + +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSONObject; +import com.durant.component.XfXhStreamClient; +import com.durant.config.XfXhConfig; +import com.durant.dto.MsgDTO; +import com.durant.listener.XfXhWebSocketListener; +import lombok.extern.slf4j.Slf4j; +import okhttp3.WebSocket; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import java.util.Collections; +import java.util.UUID; + +/** + * @author 冯凯 + * @version 1.0 + * @description: TODO + * @date 2023/9/29 14:06 + */ +@RestController +@RequestMapping("/test") +@Slf4j +public class TestController { + + + @Resource + private XfXhStreamClient xfXhStreamClient; + + @Resource + private XfXhConfig xfXhConfig; + + + /** + * 发送问题 + * + * @param question 问题 + * @return 星火大模型的回答 + */ + @GetMapping("/sendQuestion") + public String sendQuestion(@RequestParam("question") String question) { + // 如果是无效字符串,则不对大模型进行请求 + if (StrUtil.isBlank(question)) { + return "无效问题,请重新输入"; + } + // 获取连接令牌 + if (!xfXhStreamClient.operateToken(XfXhStreamClient.GET_TOKEN_STATUS)) { + return "当前大模型连接数过多,请稍后再试"; + } + + // 创建消息对象 + MsgDTO msgDTO = MsgDTO.createUserMsg(question); + // 创建监听器 + XfXhWebSocketListener listener = new XfXhWebSocketListener(); + // 发送问题给大模型,生成 websocket 连接 + + WebSocket webSocket = xfXhStreamClient.sendMsg(UUID.randomUUID().toString().substring(0, 10), Collections.singletonList(msgDTO), listener); + if (webSocket == null) { + // 归还令牌 + xfXhStreamClient.operateToken(XfXhStreamClient.BACK_TOKEN_STATUS); + return "系统内部错误,请联系管理员"; + } + try { + int count = 0; + // 为了避免死循环,设置循环次数来定义超时时长 + int maxCount = xfXhConfig.getMaxResponseTime() * 5; + while (count <= maxCount) { + Thread.sleep(200); + if (listener.isWsCloseFlag()) { + break; + } + count++; + } + if (count > maxCount) { + return "大模型响应超时,请联系管理员"; + } + // 响应大模型的答案 + System.out.println(listener.getAnswer().toString()); + return listener.getAnswer().toString(); + } catch (InterruptedException e) { + log.error("错误:" + e.getMessage()); + return "系统内部错误,请联系管理员"; + } finally { + // 关闭 websocket 连接 + webSocket.close(1000, ""); + // 归还令牌 + xfXhStreamClient.operateToken(XfXhStreamClient.BACK_TOKEN_STATUS); + } + } + + +} diff --git a/src/main/java/com/durant/dto/MsgDTO.java b/src/main/java/com/durant/dto/MsgDTO.java new file mode 100644 index 0000000..7f132a7 --- /dev/null +++ b/src/main/java/com/durant/dto/MsgDTO.java @@ -0,0 +1,43 @@ +package com.durant.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author 冯凯 + * @version 1.0 + * @description: TODO + * @date 2023/9/29 13:53 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +@JsonInclude(JsonInclude.Include.NON_NULL) +public class MsgDTO { + + private String role; + /** + * 消息内容 + */ + private String content; + /** + * 响应结果字段:结果序号,取值为[0,10]; 当前为保留字段,开发者可忽略 + */ + private Integer index; + + public static final String ROLE_USER = "user"; + public static final String ROLE_ASSISTANT = "assistant"; + + public static MsgDTO createUserMsg(String content) { + return new MsgDTO(ROLE_USER, content, null); + } + + public static MsgDTO createAssistantMsg(String content) { + return new MsgDTO(ROLE_ASSISTANT, content, null); + } + +} diff --git a/src/main/java/com/durant/dto/request/LoginReq.java b/src/main/java/com/durant/dto/request/LoginReq.java new file mode 100644 index 0000000..beeff43 --- /dev/null +++ b/src/main/java/com/durant/dto/request/LoginReq.java @@ -0,0 +1,17 @@ +package com.durant.dto.request; + +import lombok.Data; + +/** + * @author 冯凯 + * @version 1.0 + * @description: TODO + * @date 2023/10/3 14:20 + */ +@Data +public class LoginReq { + + private String username; + + private String password; +} diff --git a/src/main/java/com/durant/dto/request/RequestDTO.java b/src/main/java/com/durant/dto/request/RequestDTO.java new file mode 100644 index 0000000..45142ff --- /dev/null +++ b/src/main/java/com/durant/dto/request/RequestDTO.java @@ -0,0 +1,97 @@ +package com.durant.dto.request; + +/** + * @author 冯凯 + * @version 1.0 + * @description: TODO + * @date 2023/9/29 13:56 + */ + +import com.alibaba.fastjson.annotation.JSONField; +import com.durant.dto.MsgDTO; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * 请求参数 + * 对应生成的 JSON 结构参考 resources/demo-json/request.json + * + * @author 狐狸半面添 + * @create 2023-09-15 0:42 + */ +@NoArgsConstructor +@Data +public class RequestDTO { + + @JsonProperty("header") + private HeaderDTO header; + @JsonProperty("parameter") + private ParameterDTO parameter; + @JsonProperty("payload") + private PayloadDTO payload; + + @NoArgsConstructor + @Data + @AllArgsConstructor + public static class HeaderDTO { + /** + * 应用appid,从开放平台控制台创建的应用中获取 + */ + @JSONField(name = "app_id") + private String appId; + /** + * 每个用户的id,用于区分不同用户,最大长度32 + */ + @JSONField(name = "uid") + private String uid; + } + + @NoArgsConstructor + @Data + @AllArgsConstructor + public static class ParameterDTO { + private ChatDTO chat; + + @NoArgsConstructor + @Data + @AllArgsConstructor + public static class ChatDTO { + /** + * 指定访问的领域,general指向V1.5版本 generalv2指向V2版本。注意:不同的取值对应的url也不一样! + */ + @JsonProperty("domain") + private String domain; + /** + * 核采样阈值。用于决定结果随机性,取值越高随机性越强即相同的问题得到的不同答案的可能性越高 + */ + @JsonProperty("temperature") + private Float temperature; + /** + * 模型回答的tokens的最大长度 + */ + @JSONField(name = "max_tokens") + private Integer maxTokens; + } + } + + @NoArgsConstructor + @Data + @AllArgsConstructor + public static class PayloadDTO { + @JsonProperty("message") + private MessageDTO message; + + @NoArgsConstructor + @Data + @AllArgsConstructor + public static class MessageDTO { + @JsonProperty("text") + private List text; + } + } +} + diff --git a/src/main/java/com/durant/dto/response/ResponseDTO.java b/src/main/java/com/durant/dto/response/ResponseDTO.java new file mode 100644 index 0000000..aa96715 --- /dev/null +++ b/src/main/java/com/durant/dto/response/ResponseDTO.java @@ -0,0 +1,123 @@ +package com.durant.dto.response; + +/** + * @author 冯凯 + * @version 1.0 + * @description: TODO + * @date 2023/9/29 13:57 + */ + +import com.durant.dto.MsgDTO; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * 返回参数 + * 对应生成的 JSON 结构参考 resources/demo-json/response.json + * + * @author 狐狸半面添 + * @create 2023-09-15 0:42 + */ +@NoArgsConstructor +@Data +public class ResponseDTO { + + @JsonProperty("header") + private HeaderDTO header; + @JsonProperty("payload") + private PayloadDTO payload; + + @NoArgsConstructor + @Data + public static class HeaderDTO { + /** + * 错误码,0表示正常,非0表示出错 + */ + @JsonProperty("code") + private Integer code; + /** + * 会话是否成功的描述信息 + */ + @JsonProperty("message") + private String message; + /** + * 会话的唯一id,用于讯飞技术人员查询服务端会话日志使用,出现调用错误时建议留存该字段 + */ + @JsonProperty("sid") + private String sid; + /** + * 会话状态,取值为[0,1,2];0代表首次结果;1代表中间结果;2代表最后一个结果 + */ + @JsonProperty("status") + private Integer status; + } + + @NoArgsConstructor + @Data + public static class PayloadDTO { + @JsonProperty("choices") + private ChoicesDTO choices; + /** + * 在最后一次结果返回 + */ + @JsonProperty("usage") + private UsageDTO usage; + + @NoArgsConstructor + @Data + public static class ChoicesDTO { + /** + * 文本响应状态,取值为[0,1,2]; 0代表首个文本结果;1代表中间文本结果;2代表最后一个文本结果 + */ + @JsonProperty("status") + private Integer status; + /** + * 返回的数据序号,取值为[0,9999999] + */ + @JsonProperty("seq") + private Integer seq; + /** + * 响应文本 + */ + @JsonProperty("text") + private List text; + + } + + @NoArgsConstructor + @Data + public static class UsageDTO { + @JsonProperty("text") + private TextDTO text; + + @NoArgsConstructor + @Data + public static class TextDTO { + /** + * 保留字段,可忽略 + */ + @JsonProperty("question_tokens") + private Integer questionTokens; + /** + * 包含历史问题的总tokens大小 + */ + @JsonProperty("prompt_tokens") + private Integer promptTokens; + /** + * 回答的tokens大小 + */ + @JsonProperty("completion_tokens") + private Integer completionTokens; + /** + * prompt_tokens和completion_tokens的和,也是本次交互计费的tokens大小 + */ + @JsonProperty("total_tokens") + private Integer totalTokens; + } + } + } +} + diff --git a/src/main/java/com/durant/listener/XfXhWebSocketListener.java b/src/main/java/com/durant/listener/XfXhWebSocketListener.java new file mode 100644 index 0000000..6c39f0a --- /dev/null +++ b/src/main/java/com/durant/listener/XfXhWebSocketListener.java @@ -0,0 +1,73 @@ +package com.durant.listener; + +import com.alibaba.fastjson.JSONObject; +import com.durant.dto.MsgDTO; +import com.durant.dto.response.ResponseDTO; +import lombok.extern.slf4j.Slf4j; +import okhttp3.Response; +import okhttp3.WebSocket; +import okhttp3.WebSocketListener; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author 冯凯 + * @version 1.0 + * @description: TODO + * @date 2023/9/29 13:58 + */ +@Slf4j +public class XfXhWebSocketListener extends WebSocketListener { + private StringBuilder answer = new StringBuilder(); + + private boolean wsCloseFlag = false; + + public StringBuilder getAnswer() { + return answer; + } + + public boolean isWsCloseFlag() { + return wsCloseFlag; + } + + @Override + public void onOpen(@NotNull WebSocket webSocket, @NotNull Response response) { + super.onOpen(webSocket, response); + } + + @Override + public void onMessage(@NotNull WebSocket webSocket, @NotNull String text) { + super.onMessage(webSocket, text); + // 将大模型回复的 JSON 文本转为 ResponseDTO 对象 + ResponseDTO responseData = JSONObject.parseObject(text, ResponseDTO.class); + // 如果响应数据中的 header 的 code 值不为 0,则表示响应错误 + if (responseData.getHeader().getCode() != 0) { + // 日志记录 + log.error("发生错误,错误码为:" + responseData.getHeader().getCode() + "; " + "信息:" + responseData.getHeader().getMessage()); + // 设置回答 + this.answer = new StringBuilder("大模型响应错误,请稍后再试"); + // 关闭连接标识 + wsCloseFlag = true; + return; + } + // 将回答进行拼接 + for (MsgDTO msgDTO : responseData.getPayload().getChoices().getText()) { + this.answer.append(msgDTO.getContent()); + } + // 对最后一个文本结果进行处理 + if (2 == responseData.getHeader().getStatus()) { + wsCloseFlag = true; + } + } + + @Override + public void onFailure(@NotNull WebSocket webSocket, @NotNull Throwable t, @Nullable Response response) { + super.onFailure(webSocket, t, response); + } + + @Override + public void onClosed(@NotNull WebSocket webSocket, int code, @NotNull String reason) { + super.onClosed(webSocket, code, reason); + } +} + diff --git a/src/main/java/com/durant/mapper/LoginMapper.java b/src/main/java/com/durant/mapper/LoginMapper.java new file mode 100644 index 0000000..d6cf16f --- /dev/null +++ b/src/main/java/com/durant/mapper/LoginMapper.java @@ -0,0 +1,15 @@ +package com.durant.mapper; + +import com.durant.dto.request.LoginReq; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author 冯凯 + * @version 1.0 + * @description: TODO + * @date 2023/10/3 14:10 + */ +@Mapper +public interface LoginMapper { + LoginReq userLogin(LoginReq loginReq); +} diff --git a/src/main/java/com/durant/service/LoginService.java b/src/main/java/com/durant/service/LoginService.java new file mode 100644 index 0000000..ad27515 --- /dev/null +++ b/src/main/java/com/durant/service/LoginService.java @@ -0,0 +1,14 @@ +package com.durant.service; + +import com.durant.Result; +import com.durant.dto.request.LoginReq; + +/** + * @author 冯凯 + * @version 1.0 + * @description: TODO + * @date 2023/10/3 14:09 + */ +public interface LoginService { + Result userLogin(LoginReq loginReq); +} diff --git a/src/main/java/com/durant/service/LoginServiceImpl.java b/src/main/java/com/durant/service/LoginServiceImpl.java new file mode 100644 index 0000000..c3d8b43 --- /dev/null +++ b/src/main/java/com/durant/service/LoginServiceImpl.java @@ -0,0 +1,27 @@ +package com.durant.service; + +import com.durant.Result; +import com.durant.dto.request.LoginReq; +import com.durant.mapper.LoginMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * @author 冯凯 + * @version 1.0 + * @description: TODO + * @date 2023/10/3 14:09 + */ +@Service +public class LoginServiceImpl implements LoginService { + + @Autowired + private LoginMapper loginMapper; + + @Override + public Result userLogin(LoginReq loginReq) { + LoginReq loginReq1=loginMapper.userLogin(loginReq); + + return Result.success(loginReq1); + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..6126b47 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,35 @@ + + +xfxh: + # 服务引擎使用 讯飞星火认知大模型V2.0,如果使用 V1.5 需要将 hostUrl 修改为 https://spark-api.xf-yun.com/v1.1/chat + hostUrl: https://spark-api.xf-yun.com/v2.1/chat + # 发送请求时指定的访问领域,如果是 V1.5版本 设置为 general,如果是 V2版本 设置为 generalv2 + domain: generalv2 + # 核采样阈值。用于决定结果随机性,取值越高随机性越强即相同的问题得到的不同答案的可能性越高。取值 [0,1] + temperature: 0.5 + # 模型回答的tokens的最大长度,V1.5取值为[1,4096],V2.0取值为[1,8192]。 + maxTokens: 2048 + # 大模型回复问题的最大响应时长,单位 s + maxResponseTime: 30 + # 允许同时连接大模型的 websocket 数,如果是普通(免费)用户为 2,超过这个数连接响应会报错,具体参考官网。 + QPS: 2 + # 用于权限验证,从服务接口认证信息中获取 + appId: 4c5ea24c + # 用于权限验证,从服务接口认证信息中获取 + apiKey: d53d6f5aae8cfee01420eeff6584057f + # 用于权限验证,从服务接口认证信息中获取 + apiSecret: Mzg3YTYxZjYyYTE0MTdiOTMyZTdlMTFi +server: + port: 8080 +spring: + datasource: + driver-class-name: com.mysql.jdbc.Driver + url: jdbc:mysql://localhost:3306/chat?useUnicode=true&characterEncoding=utf8 + username: root + password: root + name: defaultDataSource +mybatis: + mapper-locations: mappers/*xml + mybatis: + configuration: + map-underscore-to-camel-case: true diff --git a/src/main/resources/mappers/userLogin.xml b/src/main/resources/mappers/userLogin.xml new file mode 100644 index 0000000..e920ba4 --- /dev/null +++ b/src/main/resources/mappers/userLogin.xml @@ -0,0 +1,9 @@ + + + + + + +