【新增】 rabbitmq手动连接,推送数据,接收数据并保存在数据库内
This commit is contained in:
@@ -0,0 +1,27 @@
|
|||||||
|
package pc.exam.pp.framework.mybatis.core.dataobject;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonParser;
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||||
|
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author REN
|
||||||
|
*/
|
||||||
|
public class CustomLocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
|
||||||
|
@Override
|
||||||
|
public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, IOException {
|
||||||
|
// 读取时间戳(毫秒)
|
||||||
|
long timestamp = p.getLongValue();
|
||||||
|
return LocalDateTime.ofInstant(
|
||||||
|
Instant.ofEpochMilli(timestamp),
|
||||||
|
// 使用系统默认时区
|
||||||
|
ZoneId.systemDefault()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@@ -90,6 +90,17 @@ public class SecurityFrameworkUtils {
|
|||||||
return loginUser != null ? loginUser.getId() : null;
|
return loginUser != null ? loginUser.getId() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得当前用户的租户ID,从上下文中
|
||||||
|
*
|
||||||
|
* @return 租户ID
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static Long getLoginTenantId() {
|
||||||
|
LoginUser loginUser = getLoginUser();
|
||||||
|
return loginUser != null ? loginUser.getTenantId() : null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得当前用户的昵称,从上下文中
|
* 获得当前用户的昵称,从上下文中
|
||||||
*
|
*
|
||||||
|
@@ -17,6 +17,11 @@
|
|||||||
</description>
|
</description>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-amqp</artifactId>
|
||||||
|
<version>2.7.18</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>pc.exam.gg</groupId>
|
<groupId>pc.exam.gg</groupId>
|
||||||
<artifactId>exam-module-system-api</artifactId>
|
<artifactId>exam-module-system-api</artifactId>
|
||||||
|
@@ -0,0 +1,71 @@
|
|||||||
|
package pc.exam.pp.module.exam.controller.admin.rabbitmq;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import pc.exam.pp.module.exam.controller.admin.rabbitmq.vo.RabbitMQSendInfoVO;
|
||||||
|
import pc.exam.pp.module.exam.dal.dataobject.ExamQuestion;
|
||||||
|
import pc.exam.pp.module.exam.service.question.ExamQuestionServiceImpl;
|
||||||
|
import pc.exam.pp.module.exam.utils.rabbitmq.RabbitmqUtils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/rabbitmq")
|
||||||
|
@Tag( name = "rabbitmq 消息队列")
|
||||||
|
public class RabbitMQController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private RabbitmqUtils rabbitMqService;
|
||||||
|
@Resource
|
||||||
|
private ExamQuestionServiceImpl examQuestionService;
|
||||||
|
/**
|
||||||
|
* 连接Rabbitmq
|
||||||
|
* @param queueName 队列名称
|
||||||
|
* @return 连接状态
|
||||||
|
*/
|
||||||
|
@PostMapping("/connect")
|
||||||
|
public String connect(@RequestParam String queueName) {
|
||||||
|
return rabbitMqService.connect(queueName,"name");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否连接
|
||||||
|
* @return 连接状态
|
||||||
|
*/
|
||||||
|
@GetMapping("/isConnected")
|
||||||
|
public boolean isConnected() {
|
||||||
|
return rabbitMqService.isConnected();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送消息
|
||||||
|
* @param rabbitMQSendInfoVO 对象
|
||||||
|
* @return 发送状态
|
||||||
|
*/
|
||||||
|
@PostMapping("/send")
|
||||||
|
public int sendMessage(@RequestBody RabbitMQSendInfoVO rabbitMQSendInfoVO) {
|
||||||
|
return examQuestionService.uploadExamQuestionToRabbitMQ(rabbitMQSendInfoVO);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全部接收消息
|
||||||
|
* @param queueName 队列名称-客户端代码
|
||||||
|
* @return 接收结果
|
||||||
|
*/
|
||||||
|
@GetMapping("/receiveAll")
|
||||||
|
public List<ExamQuestion> receiveMessage(@RequestParam String queueName) {
|
||||||
|
return examQuestionService.getExamQuestionToRabbitMQ(queueName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭
|
||||||
|
* @return 关闭结果
|
||||||
|
*/
|
||||||
|
@GetMapping("/disconnect")
|
||||||
|
public String disconnect() {
|
||||||
|
rabbitMqService.disconnect();
|
||||||
|
return "Disconnected";
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,15 @@
|
|||||||
|
package pc.exam.pp.module.exam.controller.admin.rabbitmq.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author REN
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class RabbitMQSendInfoVO {
|
||||||
|
private String queueName;
|
||||||
|
private String type;
|
||||||
|
private List<String> quIds;
|
||||||
|
}
|
@@ -110,9 +110,13 @@ public class ExamQuestion extends TenantBaseDO {
|
|||||||
@TableField(exist = false)
|
@TableField(exist = false)
|
||||||
private List<ExamQuestionKeyword> questionKeywords;
|
private List<ExamQuestionKeyword> questionKeywords;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
|
private Long source;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String createTeacher;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String type;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -6,6 +6,7 @@ import org.springframework.stereotype.Service;
|
|||||||
import pc.exam.pp.framework.common.pojo.PageResult;
|
import pc.exam.pp.framework.common.pojo.PageResult;
|
||||||
import pc.exam.pp.module.exam.controller.admin.question.dto.ExamQuestionDto;
|
import pc.exam.pp.module.exam.controller.admin.question.dto.ExamQuestionDto;
|
||||||
import pc.exam.pp.module.exam.controller.admin.question.vo.QuestionVo;
|
import pc.exam.pp.module.exam.controller.admin.question.vo.QuestionVo;
|
||||||
|
import pc.exam.pp.module.exam.controller.admin.rabbitmq.vo.RabbitMQSendInfoVO;
|
||||||
import pc.exam.pp.module.exam.dal.dataobject.*;
|
import pc.exam.pp.module.exam.dal.dataobject.*;
|
||||||
import pc.exam.pp.module.exam.dal.mysql.question.*;
|
import pc.exam.pp.module.exam.dal.mysql.question.*;
|
||||||
import pc.exam.pp.module.exam.utils.date.DateUtils;
|
import pc.exam.pp.module.exam.utils.date.DateUtils;
|
||||||
@@ -81,6 +82,7 @@ public class ExamQuestionServiceImpl implements IExamQuestionService
|
|||||||
* @return 试题(hyc)
|
* @return 试题(hyc)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新增试题(hyc)
|
* 新增试题(hyc)
|
||||||
*
|
*
|
||||||
@@ -333,29 +335,38 @@ public class ExamQuestionServiceImpl implements IExamQuestionService
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 上传试题至Rabbitmq
|
* 上传试题至Rabbitmq
|
||||||
* @param quId 试题内容 ID
|
* @param rabbitMQSendInfoVO 试题内容
|
||||||
* @param QUEUE_NAME 客户端ID
|
|
||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String uploadExamQuestionToRabbitMQ(String quId, String QUEUE_NAME) {
|
public int uploadExamQuestionToRabbitMQ(RabbitMQSendInfoVO rabbitMQSendInfoVO) {
|
||||||
|
// 1、判断上传题目数量
|
||||||
// 根据试题ID查找试题详情
|
int quCount = rabbitMQSendInfoVO.getQuIds().size();
|
||||||
|
for (String quId : rabbitMQSendInfoVO.getQuIds()) {
|
||||||
|
// 2、根据试题ID查找试题详情
|
||||||
ExamQuestion examQuestion_obj = selectExamQuestionByQuId(quId);
|
ExamQuestion examQuestion_obj = selectExamQuestionByQuId(quId);
|
||||||
|
examQuestion_obj.setType(rabbitMQSendInfoVO.getType());
|
||||||
// TODO 缺失逻辑
|
// 3、上传至Rabbitmq
|
||||||
|
rabbitMqService.sendMessage(rabbitMQSendInfoVO.getQueueName(), examQuestion_obj);
|
||||||
return rabbitMqService.sendMessage(QUEUE_NAME, examQuestion_obj);
|
}
|
||||||
|
return quCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 拉取数据Rabbitmq
|
* 拉取数据Rabbitmq
|
||||||
* @param QUEUE_NAME 客户端ID
|
* @param queueName 客户端ID
|
||||||
* @return 试题内容
|
* @return 试题内容
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public List<ExamQuestion> getExamQuestionToRabbitMQ(String QUEUE_NAME) {
|
public List<ExamQuestion> getExamQuestionToRabbitMQ(String queueName) {
|
||||||
|
// 最先判断类型
|
||||||
// TODO 1、拉取数据,保存至数据库 2、回调服务器是否拉取成功(中心服务器)
|
// TODO 1、拉取数据,保存至数据库 2、回调服务器是否拉取成功(中心服务器)
|
||||||
return rabbitMqService.receiveAllMessages(QUEUE_NAME);
|
// 1、获取上传的rabbit的试题数组
|
||||||
|
List<ExamQuestion> examQuestions = rabbitMqService.receiveAllMessages(queueName);
|
||||||
|
// 2、保存至数据库
|
||||||
|
for (ExamQuestion examQuestion : examQuestions) {
|
||||||
|
insertExamQuestion(examQuestion);
|
||||||
|
}
|
||||||
|
return rabbitMqService.receiveAllMessages(queueName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,6 +3,7 @@ package pc.exam.pp.module.exam.service.question;
|
|||||||
import pc.exam.pp.framework.common.pojo.PageResult;
|
import pc.exam.pp.framework.common.pojo.PageResult;
|
||||||
import pc.exam.pp.module.exam.controller.admin.question.dto.ExamQuestionDto;
|
import pc.exam.pp.module.exam.controller.admin.question.dto.ExamQuestionDto;
|
||||||
import pc.exam.pp.module.exam.controller.admin.question.vo.QuestionVo;
|
import pc.exam.pp.module.exam.controller.admin.question.vo.QuestionVo;
|
||||||
|
import pc.exam.pp.module.exam.controller.admin.rabbitmq.vo.RabbitMQSendInfoVO;
|
||||||
import pc.exam.pp.module.exam.dal.dataobject.EducationPaperTask;
|
import pc.exam.pp.module.exam.dal.dataobject.EducationPaperTask;
|
||||||
import pc.exam.pp.module.exam.dal.dataobject.ExamQuestion;
|
import pc.exam.pp.module.exam.dal.dataobject.ExamQuestion;
|
||||||
|
|
||||||
@@ -67,16 +68,15 @@ public interface IExamQuestionService
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 上传试题至Rabbitmq
|
* 上传试题至Rabbitmq
|
||||||
* @param quId 试题内容 ID
|
* @param rabbitMQSendInfoVO 试题内容
|
||||||
* @param QUEUE_NAME 客户端ID
|
|
||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
public String uploadExamQuestionToRabbitMQ(String quId, String QUEUE_NAME);
|
public int uploadExamQuestionToRabbitMQ(RabbitMQSendInfoVO rabbitMQSendInfoVO);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 拉取Rabbitmq内容
|
* 拉取Rabbitmq内容
|
||||||
* @param QUEUE_NAME 客户端ID
|
* @param queueName 客户端ID
|
||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
public List<ExamQuestion> getExamQuestionToRabbitMQ(String QUEUE_NAME);
|
public List<ExamQuestion> getExamQuestionToRabbitMQ(String queueName);
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package pc.exam.pp.module.exam.utils.rabbitmq;
|
package pc.exam.pp.module.exam.utils.rabbitmq;
|
||||||
|
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
import com.rabbitmq.client.Channel;
|
import com.rabbitmq.client.Channel;
|
||||||
import com.rabbitmq.client.Connection;
|
import com.rabbitmq.client.Connection;
|
||||||
import com.rabbitmq.client.ConnectionFactory;
|
import com.rabbitmq.client.ConnectionFactory;
|
||||||
@@ -8,9 +9,8 @@ import com.rabbitmq.client.GetResponse;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
|
||||||
import pc.exam.pp.framework.common.util.json.JsonUtils;
|
import pc.exam.pp.framework.security.core.util.SecurityFrameworkUtils;
|
||||||
import pc.exam.pp.module.exam.dal.dataobject.ExamQuestion;
|
import pc.exam.pp.module.exam.dal.dataobject.ExamQuestion;
|
||||||
import pc.exam.pp.module.exam.utils.json.JsonUtil ;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -44,11 +44,11 @@ public class RabbitmqUtils {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param QUEUE_NAME 客户端对应队列
|
* @param queueName 客户端对应队列
|
||||||
* @param deptName 客户端名称
|
* @param deptName 客户端名称
|
||||||
* @return 客户端连接状态
|
* @return 客户端连接状态
|
||||||
*/
|
*/
|
||||||
public String connect(String QUEUE_NAME, String deptName) {
|
public String connect(String queueName, String deptName) {
|
||||||
if (isConnected()) {
|
if (isConnected()) {
|
||||||
return "已经连接";
|
return "已经连接";
|
||||||
}
|
}
|
||||||
@@ -65,7 +65,7 @@ public class RabbitmqUtils {
|
|||||||
connection = factory.newConnection();
|
connection = factory.newConnection();
|
||||||
channel = connection.createChannel();
|
channel = connection.createChannel();
|
||||||
// 声明一个队列,如果已经存在就不会重复创建
|
// 声明一个队列,如果已经存在就不会重复创建
|
||||||
channel.queueDeclare(QUEUE_NAME, true, false, false, null);
|
channel.queueDeclare(queueName, true, false, false, null);
|
||||||
return deptName + "_连接成功";
|
return deptName + "_连接成功";
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@@ -77,12 +77,17 @@ public class RabbitmqUtils {
|
|||||||
* 向队列中发送一条消息
|
* 向队列中发送一条消息
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public String sendMessage(String QUEUE_NAME, ExamQuestion message) {
|
public String sendMessage(String queueName, ExamQuestion message) {
|
||||||
try {
|
try {
|
||||||
if (!isConnected()) return "未连接";
|
if (!isConnected()) {
|
||||||
|
return "未连接";
|
||||||
|
}
|
||||||
|
// 获取当前的租户ID
|
||||||
|
Long tenantId = SecurityFrameworkUtils.getLoginTenantId();
|
||||||
|
message.setSource(tenantId);
|
||||||
// 发送消息到默认交换机,绑定到队列
|
// 发送消息到默认交换机,绑定到队列
|
||||||
channel.basicPublish("", QUEUE_NAME, null, JsonUtil.toJson(message));
|
channel.basicPublish("", queueName, null, JSONUtil.toJsonStr(message).getBytes());
|
||||||
return "消息发送成功:" + QUEUE_NAME;
|
return "消息发送成功:" + queueName;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
return "发送失败: " + e.getMessage();
|
return "发送失败: " + e.getMessage();
|
||||||
@@ -91,14 +96,16 @@ public class RabbitmqUtils {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 只接收一条消息(点击一次,拉取一次)
|
* 只接收一条消息(点击一次,拉取一次)
|
||||||
* @param QUEUE_NAME 客户端对应队列
|
* @param queueName 客户端对应队列
|
||||||
* @return 消息
|
* @return 消息
|
||||||
*/
|
*/
|
||||||
public String receiveMessageOnce(String QUEUE_NAME) {
|
public String receiveMessageOnce(String queueName) {
|
||||||
try {
|
try {
|
||||||
if (!isConnected()) return "未连接";
|
if (!isConnected()) {
|
||||||
|
return "未连接";
|
||||||
|
}
|
||||||
// 拉取一条消息(非阻塞),自动确认
|
// 拉取一条消息(非阻塞),自动确认
|
||||||
GetResponse response = channel.basicGet(QUEUE_NAME, true);
|
GetResponse response = channel.basicGet(queueName, true);
|
||||||
if (response != null) {
|
if (response != null) {
|
||||||
return new String(response.getBody());
|
return new String(response.getBody());
|
||||||
} else {
|
} else {
|
||||||
@@ -113,16 +120,22 @@ public class RabbitmqUtils {
|
|||||||
/**
|
/**
|
||||||
* 一次性拉取所有消息(直到队列为空)
|
* 一次性拉取所有消息(直到队列为空)
|
||||||
*/
|
*/
|
||||||
public List<ExamQuestion> receiveAllMessages(String QUEUE_NAME) {
|
public List<ExamQuestion> receiveAllMessages(String queueName) {
|
||||||
List<ExamQuestion> messages = new ArrayList<>();
|
List<ExamQuestion> messages = new ArrayList<>();
|
||||||
try {
|
try {
|
||||||
if (!isConnected()) return messages;
|
if (!isConnected()) {
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
|
|
||||||
GetResponse response;
|
GetResponse response;
|
||||||
// 循环拉取,直到没有消息为止
|
// 循环拉取,直到没有消息为止
|
||||||
while ((response = channel.basicGet(QUEUE_NAME, true)) != null) {
|
while ((response = channel.basicGet(queueName, true)) != null) {
|
||||||
messages.add(JsonUtil.fromJson(response.getBody(), ExamQuestion.class));
|
byte[] body = response.getBody();
|
||||||
|
// 使用系统默认编码(可能因环境不同导致乱码)
|
||||||
|
String str = new String(body);
|
||||||
|
messages.add(JSONUtil.toBean(str, ExamQuestion.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
@@ -134,8 +147,12 @@ public class RabbitmqUtils {
|
|||||||
*/
|
*/
|
||||||
public void disconnect() {
|
public void disconnect() {
|
||||||
try {
|
try {
|
||||||
if (channel != null) channel.close();
|
if (channel != null) {
|
||||||
if (connection != null) connection.close();
|
channel.close();
|
||||||
|
}
|
||||||
|
if (connection != null) {
|
||||||
|
connection.close();
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
@@ -20,6 +20,8 @@ public interface TenantConvert {
|
|||||||
reqVO.setUsername(bean.getUsername());
|
reqVO.setUsername(bean.getUsername());
|
||||||
reqVO.setPassword(bean.getPassword());
|
reqVO.setPassword(bean.getPassword());
|
||||||
reqVO.setNickname(bean.getContactName()).setMobile(bean.getContactMobile());
|
reqVO.setNickname(bean.getContactName()).setMobile(bean.getContactMobile());
|
||||||
|
// 租户创建的用户,默认是管理员用户
|
||||||
|
reqVO.setUserType("0");
|
||||||
return reqVO;
|
return reqVO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -4,6 +4,8 @@ import pc.exam.pp.framework.common.core.KeyValue;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -12,9 +14,10 @@ import java.util.List;
|
|||||||
* @author 芋道源码
|
* @author 芋道源码
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
public class SmsSendMessage {
|
public class SmsSendMessage implements Serializable {
|
||||||
|
|
||||||
public static final String QUEUE = "SEND_MESSAGE_QUEUE"; // 重点:需要增加消息对应的 Queue
|
// 重点:需要增加消息对应的 Queue
|
||||||
|
public static final String QUEUE = "SEND_MESSAGE_QUEUE";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 短信日志编号
|
* 短信日志编号
|
||||||
|
Reference in New Issue
Block a user