基于SpringBoot+WebSocket的前后端连接,并接入文心一言大模子API ...

打印 上一主题 下一主题

主题 831|帖子 831|积分 2493

前言:

本片博客只讲述了操作的大致流程,具体实现步骤并不标准,请以参考为准。
本文前提:认识使用webSocket
 假如大家还不了解什么是WebSocket,可以参考我的这篇博客:
rWebSocket 详解:全双工通信的实现与应用-CSDN博客文章欣赏阅读214次。WebSocket 是一种在单个 TCP 连接上举行全双工通信的协议。它使得客户端和服务器之间的数据交换变得更加简单,允许服务器主动向客户端推送数据。通过 WebSocket API,欣赏器和服务器只需完成一次握手,即可建立持久性连接,开始双向数据传输。https://blog.csdn.net/Future_yzx/article/details/145359356?spm=1001.2014.3001.5502文章欣赏阅读214次。WebSocket 是一种在单个 TCP 连接上举行全双工通信的协议。它使得客户端和服务器之间的数据交换变得更加简单,允许服务器主动向客户端推送数据。通过 WebSocket API,欣赏器和服务器只需完成一次握手,即可建立持久性连接,开始双向数据传输。https://blog.csdn.net/Future_yzx/article/details/145359356?spm=1001.2014.3001.5502
https://blog.csdn.net/Future_yzx/article/details/145359356?spm=1001.2014.3001.5502
要使用Java的Springboot+WebSocket连接前后端并接入文心一言大模子API来实现实时聊天,可以分为几个主要步骤:

  • 搭建WebSocket服务端
  • 与文心一言大模子API对接
  • 前端WebSocket连接
一、 搭建WebSocket服务端(使用Java)

我们可以使用Spring Boot来搭建一个WebSocket服务端。
(1)添加依赖: 在pom.xml中添加Spring Boot WebSocket的依赖:
  1. <dependency>
  2.     <groupId>org.springframework.boot</groupId>
  3.     <artifactId>spring-boot-starter-websocket</artifactId>
  4. </dependency>
复制代码
(2)配置WebSocket连接: 创建一个WebSocket配置类来启用WebSocket功能:
  1. package com.qcby.byspringbootdemo.config;
  2. import org.springframework.context.annotation.Bean;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.web.socket.server.standard.ServerEndpointExporter;
  5. @Configuration
  6. public class WebSocketConfig {
  7.     @Bean
  8.     public ServerEndpointExporter serverEndpointExporter() {
  9.         return new ServerEndpointExporter();
  10.     }
  11. }
复制代码
 作用:这个类负责配置Spring WebSocket。通过使用 ServerEndpointExporter 来实现 WebSocket 的启用

(3)创建WebSocketServer : 在WebSocketServer 类中处理连接、消息接收和发送。
  1. package com.qcby.byspringbootdemo.server;
  2. import com.alibaba.fastjson2.JSONException;
  3. import com.alibaba.fastjson2.JSONObject;
  4. import com.fasterxml.jackson.databind.ObjectMapper;
  5. import com.qcby.byspringbootdemo.util.WenXinYiYanUtil;
  6. import lombok.extern.slf4j.Slf4j;
  7. import org.springframework.stereotype.Component;
  8. import org.springframework.stereotype.Service;
  9. import javax.websocket.*;
  10. import javax.websocket.server.PathParam;
  11. import javax.websocket.server.ServerEndpoint;
  12. import java.io.IOException;
  13. import java.util.HashMap;
  14. import java.util.Map;
  15. import java.util.concurrent.CopyOnWriteArraySet;
  16. /**
  17. * WebSocket 服务端
  18. */
  19. @Component
  20. @Slf4j
  21. @Service
  22. @ServerEndpoint("/api/websocket/{sid}")
  23. public class WebSocketServer {
  24.     //当前在线连接数
  25.     private static int onlineCount = 0;
  26.     //存放每个客户端对应的 WebSocketServer 对象
  27.     private static final CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<>();
  28.     //用户信息
  29.     private Session session;
  30.     //当前用户的 sid
  31.     private String sid = "";
  32.     //JSON解析工具
  33.     private static final ObjectMapper objectMapper = new ObjectMapper();
  34.     /**
  35.      * 连接建立成功调用的方法
  36.      */
  37.     @OnOpen
  38.     public void onOpen(Session session, @PathParam("sid") String sid) {
  39.         this.session = session;
  40.         this.sid = sid;
  41.         webSocketSet.add(this); //加入集合
  42.         addOnlineCount(); //在线数加1
  43.         try {
  44.             // 发送 JSON 格式的消息
  45.             String successMessage = "{"message": "conn_success"}";
  46.             sendMessage(successMessage);
  47.             log.info("有新窗口开始监听: " + sid + ", 当前在线人数为: " + getOnlineCount());
  48.         } catch (IOException e) {
  49.             log.error("WebSocket IO Exception", e);
  50.         }
  51.     }
  52.     /**
  53.      * 连接关闭调用的方法
  54.      */
  55.     @OnClose
  56.     public void onClose() {
  57.         webSocketSet.remove(this); //从集合中删除
  58.         subOnlineCount(); //在线数减1
  59.         log.info("释放的 sid 为:" + sid);
  60.         log.info("有一连接关闭!当前在线人数为 " + getOnlineCount());
  61.     }
  62.     /**
  63.      * 收到客户端消息后调用的方法
  64.      */
  65.     @OnMessage
  66.     public void onMessage(String message, Session session) {
  67.         log.info("收到来自窗口 " + sid + " 的信息: " + message);
  68.         String targetSid;
  69.         String msgContent;
  70.         try {
  71.             //解析接收到的 JSON 消息
  72.             Map<String, String> messageMap = objectMapper.readValue(message, Map.class);
  73.             targetSid = messageMap.get("targetSid");
  74.             msgContent = messageMap.get("message");
  75.         } catch (IOException e) {
  76.             log.error("消息解析失败", e);
  77.             return;
  78.         }
  79.         //判断目标用户是否为管理员且管理员不在线
  80.         boolean isTargetAdmin = isAdmin(targetSid);
  81.         if (isTargetAdmin && !isAdminOnline()) {
  82.             log.info("管理员不在线,调用文心一言进行自动回复");
  83.             String wenxinResponse = getWenxinResponse(msgContent);
  84.             if (wenxinResponse != null) {
  85.                 log.info("文心一言返回的回复: " + wenxinResponse);
  86.                 Map<String, String> responseMap = new HashMap<>();
  87.                 responseMap.put("sourceSid", sid);
  88.                 responseMap.put("message", wenxinResponse);
  89.                 String jsonResponse;
  90.                 try {
  91.                     //将回复消息转换为 JSON 格式
  92.                     jsonResponse = objectMapper.writeValueAsString(responseMap);
  93.                 } catch (IOException e) {
  94.                     log.error("JSON 序列化失败", e);
  95.                     return;
  96.                 }
  97.                 //发送自动回复消息给发送方
  98.                 try {
  99.                     sendMessage(jsonResponse);
  100.                     log.info("发送自动回复消息: " + jsonResponse);
  101.                 } catch (IOException e) {
  102.                     log.error("消息发送失败", e);
  103.                 }
  104.                 return;
  105.             }
  106.         }
  107.         //如果管理员在线或者不是管理员,按照正常逻辑发送消息
  108.         Map<String, String> responseMap = new HashMap<>();
  109.         responseMap.put("sourceSid", sid);
  110.         responseMap.put("message", msgContent);
  111.         String jsonResponse;
  112.         try {
  113.             //将消息转换为 JSON 格式
  114.             jsonResponse = objectMapper.writeValueAsString(responseMap);
  115.         } catch (IOException e) {
  116.             log.error("JSON 序列化失败", e);
  117.             return;
  118.         }
  119.         //将消息发送给目标 sid
  120.         for (WebSocketServer item : webSocketSet) {
  121.             try {
  122.                 if (targetSid.equals(item.sid)) {
  123.                     item.sendMessage(jsonResponse);
  124.                     break;
  125.                 }
  126.             } catch (IOException e) {
  127.                 log.error("消息发送失败", e);
  128.             }
  129.         }
  130.     }
  131.     /**
  132.      * 判断是否是管理员
  133.      */
  134.     private boolean isAdmin(String sid) {
  135.         return "admin".equals(sid);
  136.     }
  137.     /**
  138.      * 发生错误时调用的方法
  139.      */
  140.     @OnError
  141.     public void onError(Session session, Throwable error) {
  142.         log.error("发生错误", error);
  143.     }
  144.     /**
  145.      * 实现服务器主动推送
  146.      */
  147.     public void sendMessage(String message) throws IOException {
  148.         this.session.getBasicRemote().sendText(message);
  149.     }
  150.     /**
  151.      * 群发自定义消息
  152.      */
  153.     public static void sendInfo(String message, @PathParam("sid") String sid) throws IOException {
  154.         log.info("推送消息到窗口 " + sid + ",推送内容: " + message);
  155.         for (WebSocketServer item : webSocketSet) {
  156.             try {
  157.                 if (sid == null) {
  158.                     item.sendMessage(message); //推送给所有人
  159.                 } else if (item.sid.equals(sid)) {
  160.                     item.sendMessage(message); //推送给指定 sid
  161.                 }
  162.             } catch (IOException e) {
  163.                 log.error("推送消息失败", e);
  164.             }
  165.         }
  166.     }
  167.     public static synchronized int getOnlineCount() {
  168.         return onlineCount;
  169.     }
  170.     public static synchronized void addOnlineCount() {
  171.         WebSocketServer.onlineCount++;
  172.     }
  173.     public static synchronized void subOnlineCount() {
  174.         WebSocketServer.onlineCount--;
  175.     }
  176.     public static CopyOnWriteArraySet<WebSocketServer> getWebSocketSet() {
  177.         return webSocketSet;
  178.     }
  179.     public String getSid() {
  180.         return this.sid;
  181.     }
  182.     private boolean isAdminOnline() {
  183.         for (WebSocketServer item : webSocketSet) {
  184.             if (isAdmin(item.sid)) {
  185.                 log.info("管理员已在线: " + item.sid);
  186.                 return true;
  187.             }
  188.         }
  189.         log.info("管理员不在线");
  190.         return false;
  191.     }
  192.     private String getWenxinResponse(String query) {
  193.         try {
  194.             //调用WenXinYiYanUtil类的静态方法获取回复
  195.             String wenxinReplyJson = WenXinYiYanUtil.getWenxinReply(query);
  196.             //将文心一言回复的JSON字符串解析为JSONObject
  197.             JSONObject wenxinReplyObj = JSONObject.parseObject(wenxinReplyJson);
  198.             //提取出要展示给用户的回复内容
  199.             String result = wenxinReplyObj.getString("result");
  200.             return result;
  201.         } catch (IOException | JSONException e) {
  202.             log.error("调用文心一言失败", e);
  203.             return null;
  204.         }
  205.     }
  206. }
复制代码
 作用:使用 @ServerEndpoint 注解声明了 WebSocket 端点,指定了路径 /api/websocket/{sid},其中 {sid} 是一个动态路径参数,代表每个连接的唯一标识。
WebSocket 变乱处理:
@OnOpen: 连接建立时调用的方法。每当一个新的 WebSocket 连接建立时,执行此方法,并将当前连接的 sid 和 session 保存到 webSocketSet 中,同时增长在线人数。
发送一个 JSON 格式的 "conn_success" 消息给客户端,表示连接成功。
@OnClose: 连接关闭时调用的方法。每当一个 WebSocket 连接关闭时,执行此方法,并从 webSocketSet 中移除该连接,同时淘汰在线人数。
@OnMessage: 收到客户端消息时调用的方法。该方法剖析接收到的消息并根据目的用户的 sid 将消息发送给目的客户端。假如目的用户是管理员且管理员不在线,体系会通过 WenXinYiYanUtil 获取主动复兴,举行主动响应。
@OnError: 发生错误时调用的方法,日记记录错误信息。
消息发送:
sendMessage: 该方法用于向客户端发送消息,使用 session.getBasicRemote().sendText() 实现。
sendInfo: 该方法用于群发消息,向所有连接的客户端发送自界说的消息。可以根据传入的 sid 举行定向推送。


二、 调用文心一言大模子API

要与文心一言大模子API举行集成,你需要访问其官方文档并获取API密钥,通常通过HTTP请求与API交互。
步骤:
1.在百度智能云开放平台中注册成为开辟者
官网:全面解读 百度智能云 API 服务商 -- 服务商流派 -- 幂简集成
2.进入百度智能云官网举行登录,点击立刻体验

3.进入千帆ModelBuilder,点击左侧的应用接入并且点击创建应用

4.在页面上的应用名称输入自己想要的应用名称和应用描述

5.获取对应的API Key 和 Secret Key
6.配置文心一言ERNIE4.0 API并调用,选择一个想要使用的模子

7.添加依赖
  1. <dependency>
  2.     <groupId>com.squareup.okhttp3</groupId>
  3.     <artifactId>okhttp</artifactId>
  4.     <version>4.9.3</version>
  5. </dependency>
复制代码

8.我们可以参考官方提供的示例代码,编写自己的程序

代码实例

下面这段代码是一个 Java 示例程序,使用 OkHttp 库向文心一言 API 发送请求,并通过用户提供的输入获取模子的响应。 
  1. import okhttp3.*;
  2. import org.json.JSONException;
  3. import org.json.JSONObject;
  4. import java.io.*;
  5. import java.util.concurrent.TimeUnit;
  6. class Sample {
  7.     // 密钥
  8.     public static final String API_KEY = "";
  9.     public static final String SECRET_KEY = "";
  10.     // OkHttpClient 配置,设置连接超时和读取超时
  11.     static final OkHttpClient HTTP_CLIENT = new OkHttpClient().newBuilder()
  12.             .connectTimeout(60, TimeUnit.SECONDS)  // 设置连接超时为60秒
  13.             .readTimeout(60, TimeUnit.SECONDS)     // 设置读取超时为60秒
  14.             .build();
  15.     public static void main(String[] args) throws IOException, JSONException {
  16.         // 定义请求的媒体类型
  17.         MediaType mediaType = MediaType.parse("application/json");
  18.         // 构建请求体,消息内容包含了用户请求
  19.         RequestBody body = RequestBody.create(mediaType, "{"messages":["
  20.                 + "{"role":"user","content":"北京的天气是什么"}" // 用户输入的消息内容
  21.                 + "],"
  22.                 + ""temperature":0.95,"   // 设置温度参数,控制模型的输出多样性
  23.                 + ""top_p":0.8,"          // 设置 top_p 参数,控制模型输出的多样性
  24.                 + ""penalty_score":1,"    // 设置惩罚得分参数,影响模型对重复内容的惩罚
  25.                 + ""enable_system_memory":false," // 禁用系统内存
  26.                 + ""disable_search":false,"        // 禁用搜索功能
  27.                 + ""enable_citation":false}");    // 禁用引用功能
  28.         // 构建 HTTP 请求
  29.         Request request = new Request.Builder()
  30.                 .url("https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions_pro?access_token=" + getAccessToken())
  31.                 .method("POST", body)  // 设置请求方法为 POST
  32.                 .addHeader("Content-Type", "application/json")  // 设置请求头,表示发送的内容是 JSON 格式
  33.                 .build();
  34.         // 发送请求并获取响应
  35.         try (Response response = HTTP_CLIENT.newCall(request).execute()) {
  36.             // 输出响应内容,打印接口返回的数据
  37.             System.out.println(response.body().string());
  38.         } catch (IOException e) {
  39.             // 捕获 IO 异常(如网络错误、超时等),并打印异常信息
  40.             e.printStackTrace();
  41.         }
  42.     }
  43.     /**
  44.      * 从用户的 API 密钥(AK、SK)生成鉴权签名(Access Token)
  45.      *
  46.      * @return 鉴权签名(Access Token)
  47.      * @throws IOException 如果发生 I/O 异常
  48.      * @throws JSONException 如果发生 JSON 解析异常
  49.      */
  50.     static String getAccessToken() throws IOException, JSONException {
  51.         // 设置请求体的媒体类型为 x-www-form-urlencoded
  52.         MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
  53.         // 创建请求体,包含 API 的 client_id 和 client_secret
  54.         RequestBody body = RequestBody.create(mediaType, "grant_type=client_credentials&client_id=" + API_KEY
  55.                 + "&client_secret=" + SECRET_KEY);
  56.         // 构建请求,使用 POST 方法获取 Access Token
  57.         Request request = new Request.Builder()
  58.                 .url("https://aip.baidubce.com/oauth/2.0/token") // 请求 URL,获取 Access Token
  59.                 .method("POST", body)  // 使用 POST 方法发送请求
  60.                 .addHeader("Content-Type", "application/x-www-form-urlencoded") // 请求头
  61.                 .build();
  62.         // 发送请求并获取响应
  63.         try (Response response = HTTP_CLIENT.newCall(request).execute()) {
  64.             // 从响应中解析出 Access Token
  65.             return new JSONObject(response.body().string()).getString("access_token");
  66.         }
  67.     }
  68. }
复制代码
解释如下:

这段代码是一个 Java 示例程序,使用 OkHttp 库向文心一言 API 发送请求,并通过用户提供的输入获取模子的响应。具体来说,它包括了两个主要部分:


  • 获取访问令牌(Access Token):通过 API_KEY 和 SECRET_KEY 获取用于访问文心一言 API 的 Access Token。
  • 发送请求到文心一言 API:使用获取到的 Access Token,向文心一言 API 发送一个包含用户输入的消息,请求模子生成响应。
具体步骤:

1. 获取 Access Token
getAccessToken() 方法的作用是通过 API_KEY 和 SECRET_KEY 获取一个 Access Token。这个令牌是调用文心一言 API 所必须的。步骤如下:
  1. static String getAccessToken() throws IOException, JSONException {
  2.     // 设置请求体的媒体类型为 x-www-form-urlencoded
  3.     MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
  4.     // 创建请求体,包含 API 的 client_id 和 client_secret
  5.     RequestBody body = RequestBody.create(mediaType, "grant_type=client_credentials&client_id=" + API_KEY
  6.             + "&client_secret=" + SECRET_KEY);
  7.     // 构建请求,使用 POST 方法获取 Access Token
  8.     Request request = new Request.Builder()
  9.             .url("https://aip.baidubce.com/oauth/2.0/token") // 请求 URL,获取 Access Token
  10.             .method("POST", body)  // 使用 POST 方法发送请求
  11.             .addHeader("Content-Type", "application/x-www-form-urlencoded") // 请求头
  12.             .build();
  13.     // 发送请求并获取响应
  14.     try (Response response = HTTP_CLIENT.newCall(request).execute()) {
  15.         // 从响应中解析出 Access Token
  16.         return new JSONObject(response.body().string()).getString("access_token");
  17.     }
  18. }
复制代码


  • 请求体:grant_type=client_credentials&client_id=API_KEY&client_secret=SECRET_KEY,请求体中的 client_id 和 client_secret 是用来鉴权的。
  • 响应剖析:在获取到响应后,程序剖析出其中的 access_token,这是后续访问文心一言 API 的凭证。
2. 发送请求到文心一言 API
接下来的部分是构建并发送请求到文心一言的聊天 API。
  1. RequestBody body = RequestBody.create(mediaType, "{"messages":["
  2.         + "{"role":"user","content":"北京的天气是什么"}" // 用户输入的消息内容
  3.         + "],"
  4.         + ""temperature":0.95,"   // 设置温度参数,控制模型的输出多样性
  5.         + ""top_p":0.8,"          // 设置 top_p 参数,控制模型输出的多样性
  6.         + ""penalty_score":1,"    // 设置惩罚得分参数,影响模型对重复内容的惩罚
  7.         + ""enable_system_memory":false," // 禁用系统内存
  8.         + ""disable_search":false,"        // 禁用搜索功能
  9.         + ""enable_citation":false}");    // 禁用引用功能
复制代码


  • 请求体:请求体是一个 JSON 字符串,其中包含了用户的消息(例如:"北京的天气是什么"),以及一些参数,如 temperature, top_p 等,控制生成的答复的多样性和随机性。

    • "messages": 包含一个消息对象,其中 "role" 为 "user",表示消息来源是用户,"content" 是用户的实际题目。
    • "temperature": 控制模子生成内容的多样性,值越高,生成的内容越随机,通常在 0 到 1 之间。0.95 是一个比力高的值,表示会有更多的随机性。
    • "top_p": 也是控制生成内容多样性的参数,决定了模子从哪些概率分布中选择内容。0.8 表示在一个给定的概率质量上选取词汇。
    • "penalty_score": 处罚得分,影响模子对重复内容的处罚,值越高,模子更倾向于避免重复。
    • "enable_system_memory", "disable_search", "enable_citation": 这些选项是与文心一言 API 功能相干的配置项,用于启用或禁用某些功能。

  1. Request request = new Request.Builder()
  2.         .url("https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions_pro?access_token=" + getAccessToken())
  3.         .method("POST", body)  // 设置请求方法为 POST
  4.         .addHeader("Content-Type", "application/json")  // 设置请求头,表示发送的内容是 JSON 格式
  5.         .build();
复制代码


  • 请求 URL:请求的 URL 中包含了获取到的 access_token,它会用来验证请求的合法性。
  • 请求方法:POST,表示发送的数据(如用户消息)需要上传到服务器。
  • 请求头:Content-Type 设置为 application/json,表示请求体的格式是 JSON。
  1. try (Response response = HTTP_CLIENT.newCall(request).execute()) {
  2.     // 输出响应内容,打印接口返回的数据
  3.     System.out.println(response.body().string());
  4. } catch (IOException e) {
  5.     // 捕获 IO 异常(如网络错误、超时等),并打印异常信息
  6.     e.printStackTrace();
  7. }
复制代码


  • 发送请求:调用 HTTP_CLIENT.newCall(request).execute() 发送请求,并获取服务器的响应。
  • 打印响应:假如请求成功,打印返回的响应内容,通常是文心一言的答复。
3. 整体流程


  • 程序启动后,调用 getAccessToken() 方法获取 Access Token。
  • 使用获取到的 Access Token,构建一个带有用户消息的请求,发送到文心一言 API。
  • 接收文心一言的响应(比如对用户题目的答复),并打印在控制台。
4. 需要的外部依赖



  • OkHttp:用于发送 HTTP 请求,处理网络连接。
  • JSON库:用于剖析 JSON 数据,这里使用的是 org.json 库(JSONObject 类)。


我们也可以使用RestTemplate或WebClient来调用文心一言API,具体怎么使用大家可以自行探索。


三、 前端WebSocket连接

在前端,你可以使用JavaScript的WebSocket API来连接后端WebSocket服务并举行通信。
前端代码示例:
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6.     <title>实时聊天</title>
  7. </head>
  8. <body>
  9.     <div>
  10.         <input type="text" id="inputMessage" placeholder="请输入消息">
  11.         <button onclick="sendMessage()">发送</button>
  12.     </div>
  13.     <div id="chatBox"></div>
  14.     <script>
  15.         // 创建WebSocket连接
  16.         const socket = new WebSocket("ws://localhost:8080/api/websocket/+sid");
  17.         // 监听消息
  18.         socket.onmessage = function(event) {
  19.             const chatBox = document.getElementById("chatBox");
  20.             const message = event.data;
  21.             chatBox.innerHTML += `<p>机器人: ${message}</p>`;
  22.         };
  23.         // 发送消息到WebSocket
  24.         function sendMessage() {
  25.             const inputMessage = document.getElementById("inputMessage").value;
  26.             socket.send(inputMessage);
  27.             const chatBox = document.getElementById("chatBox");
  28.             chatBox.innerHTML += `<p>你: ${inputMessage}</p>`;
  29.         }
  30.     </script>
  31. </body>
  32. </html>
复制代码



参考文章:
实现 WebSocket 接入文心一言_apipost websocket-CSDN博客

文心一言ERNIE-Bot 4.0模子流式和非流式API调用(SpringBoot+OkHttp3+SSE+WebSocket) - autunomy - 博客园


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

万万哇

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表