Init
This commit is contained in:
1 parent
065bdb572b
commit
dcb918ddd6
13 files changed
+508
-9
No files matched your search
@@ -10,15 +10,6 @@
|
|||||||
# Mobile Tools for Java (J2ME)
|
# Mobile Tools for Java (J2ME)
|
||||||
.mtj.tmp/
|
.mtj.tmp/
|
||||||
|
|
||||||
# Package Files #
|
|
||||||
*.jar
|
|
||||||
*.war
|
|
||||||
*.nar
|
|
||||||
*.ear
|
|
||||||
*.zip
|
|
||||||
*.tar.gz
|
|
||||||
*.rar
|
|
||||||
|
|
||||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||||
hs_err_pid*
|
hs_err_pid*
|
||||||
replay_pid*
|
replay_pid*
|
||||||
Vendored
+9
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"java.project.sourcePaths": [
|
||||||
|
"src"
|
||||||
|
],
|
||||||
|
"java.project.outputPath": "bin",
|
||||||
|
"java.project.referencedLibraries": [
|
||||||
|
"lib/**/*.jar"
|
||||||
|
]
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,179 @@
|
|||||||
|
package client;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Scanner;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
|
||||||
|
import server.vo.User;
|
||||||
|
|
||||||
|
public class Client {
|
||||||
|
|
||||||
|
// 多个函数共享的数据可以定义为全局的
|
||||||
|
public static Scanner scan = new Scanner(System.in); // 控制台输入
|
||||||
|
public static Socket socket; // 建立连接后的套接字对象
|
||||||
|
public static BufferedReader buf; // 字符输入流
|
||||||
|
public static PrintWriter write; // 字符输出流
|
||||||
|
public static User currentUser; // 当前登录用户
|
||||||
|
|
||||||
|
// 主方法
|
||||||
|
public static void main(String[] args) throws Exception, IOException {
|
||||||
|
String ip = "127.0.0.1"; // 服务器IP
|
||||||
|
int port = 1234; // 服务器程序的端口
|
||||||
|
// 1.建立与服务器的连接
|
||||||
|
socket = new Socket(ip, port);
|
||||||
|
// 获取输入流
|
||||||
|
InputStream is = socket.getInputStream();
|
||||||
|
buf = new BufferedReader(new InputStreamReader(is, "utf-8"));
|
||||||
|
// 获取输出流
|
||||||
|
OutputStream os = socket.getOutputStream();
|
||||||
|
write = new PrintWriter(new OutputStreamWriter(os, "utf-8"));
|
||||||
|
// 2.用户登录
|
||||||
|
if (!login()) { // 登录失败,退出程序
|
||||||
|
System.out.println("登录失败,欢迎下次访问");
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
// 登录成功,显示程序主界面
|
||||||
|
int choose = menu();
|
||||||
|
while (choose != 8) { // 不是退出
|
||||||
|
switch (choose) {
|
||||||
|
case 1: // 修改密码
|
||||||
|
updatepassword();
|
||||||
|
break;
|
||||||
|
case 2:// 修改密码
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 3:// 增加数据:显示二级菜单
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
choose = menu(); // 执行完毕,重新显示主菜单,并选择
|
||||||
|
}
|
||||||
|
// 输入选择=8,退出
|
||||||
|
close();
|
||||||
|
System.out.println("您已退出程序,欢迎下次使用!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择退出时,先向服务器发送“退出请求:code=8”,然后关闭连接
|
||||||
|
public static void close() throws Exception {
|
||||||
|
Map<String, Object> map = new HashMap<>();
|
||||||
|
map.put("code", 8);
|
||||||
|
String sendData = new Gson().toJson(map); // 将java的map对象直接转换成json字符串
|
||||||
|
System.out.println("发送给服务器:" + sendData);
|
||||||
|
write.println(sendData);
|
||||||
|
write.flush();
|
||||||
|
if (write != null) {
|
||||||
|
write.close();
|
||||||
|
}
|
||||||
|
if (buf != null) {
|
||||||
|
buf.close();
|
||||||
|
}
|
||||||
|
if (socket != null) {
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绘制流程图,按流程图书写
|
||||||
|
public static boolean login() throws Exception {
|
||||||
|
for (int i = 1; i <= 3; i++) {
|
||||||
|
// 1.输入用户名,密码:所有输入均应该先给输入提示
|
||||||
|
System.out.println("请输入用户名:");
|
||||||
|
String userName = scan.nextLine();
|
||||||
|
System.out.println("请输入密码:");
|
||||||
|
String passsord = scan.nextLine();
|
||||||
|
// 可以使用GSON包非常方便的将java类型的数据转成json字符串,或者从json字符串中获取相关的数据,见教案p129
|
||||||
|
// 封装发送的数据:如登录请求的数据格式可以为"{code:0,data:{userName:***,password:***}}"
|
||||||
|
Map<String, Object> map = new HashMap<>();
|
||||||
|
map.put("code", 0);
|
||||||
|
User user = new User();
|
||||||
|
user.setUserName(userName);
|
||||||
|
user.setPassword(passsord);
|
||||||
|
Gson gson = new Gson();
|
||||||
|
map.put("data", gson.toJson(user)); // toJson方法可以将java的vo对象直接转换成json字符串
|
||||||
|
String sendData = gson.toJson(map); // 将java的map对象直接转换成json字符串
|
||||||
|
System.out.println("发送给服务器:" + sendData);
|
||||||
|
/**
|
||||||
|
* 2.发送数据并接收服务器返回的数据(返回数据也有格式,如可以是以下格式:
|
||||||
|
* code:0或1,data:****
|
||||||
|
* code=0表示登录失败,data中存放失败原因
|
||||||
|
* code=1表示登录成功,data中放当前用户相关的信息(用户名,姓名,角色等)
|
||||||
|
*/
|
||||||
|
String responseData = send(sendData);
|
||||||
|
System.out.println("接收到服务器端的响应:" + responseData);
|
||||||
|
// 4.解析返回的字符串
|
||||||
|
JsonElement element = JsonParser.parseString(responseData); // 使用parseString方法将json字符串转换为JsonElement对象
|
||||||
|
JsonObject obj = element.getAsJsonObject(); // 将JsonElement对象转换为JsonObject对象
|
||||||
|
int returnCode = obj.get("code").getAsInt(); // 获取code属性
|
||||||
|
String returnData = obj.get("data").getAsString(); // 获取data属性
|
||||||
|
if (returnCode == 0) { // 登录失败
|
||||||
|
System.out.println("登录失败:" + returnData);
|
||||||
|
} else { // 登录成功,returnData中存放的是当前用户的json字符串
|
||||||
|
currentUser = gson.fromJson(returnData, User.class); // 使用fromJson方法可以将json字符串直接转换为指定类型的对象
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示操作界面,返回用户的选择1,2,.....
|
||||||
|
public static int menu() {
|
||||||
|
System.out.println("======新闻管理系统=======");
|
||||||
|
System.out.println("作者:****&******");
|
||||||
|
System.out.println("当前用户:" + currentUser.getName());
|
||||||
|
System.out.println("1.密码修改");
|
||||||
|
System.out.println("2.增加数据"); // 需要增加二级菜单:【键盘录入,网页爬取,txt文件导入,xls文件导入】
|
||||||
|
System.out.println("3.修改数据"); // 按key修改其他字段
|
||||||
|
System.out.println("4.删除数据"); // 按key删除记录
|
||||||
|
System.out.println("5.查询数据"); // 需要增加二级菜单,【自动设计查询条件】
|
||||||
|
System.out.println("6.统计数据"); // 需要增加二级菜单,【自行设计统计条件】
|
||||||
|
System.out.println("7.导出数据至文件"); // 需要增加二级菜单:【导出至txt,导出至xls】
|
||||||
|
System.out.println("8.退出");
|
||||||
|
|
||||||
|
System.out.println("请输入选项(1-8):");
|
||||||
|
int choose = scan.nextInt();
|
||||||
|
|
||||||
|
return choose;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将指定的字符串发送给服务器,并接收服务器返回的数据
|
||||||
|
public static String send(String data) throws Exception {
|
||||||
|
// 发送数据
|
||||||
|
write.println(data);
|
||||||
|
write.flush();
|
||||||
|
// 接收数据
|
||||||
|
String response = buf.readLine();
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改密码
|
||||||
|
public static void updatepassword() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从键盘输入
|
||||||
|
public static void inputFromKeyboard() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将电子表格文件编码为base64形式的字符串进行传输
|
||||||
|
public static void importFromXLS() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package server;
|
||||||
|
|
||||||
|
import java.net.ServerSocket;
|
||||||
|
import java.net.Socket;
|
||||||
|
|
||||||
|
import server.thread.HandleThread;
|
||||||
|
|
||||||
|
public class Server {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
// 创建服务器
|
||||||
|
int port = 1234;
|
||||||
|
ServerSocket server = new ServerSocket(port);
|
||||||
|
System.out.println("服务器正在1234端口侦听客户端请求");
|
||||||
|
// 循环等待客户端请求
|
||||||
|
while (true) {
|
||||||
|
Socket socket = server.accept();
|
||||||
|
System.out.println("客户端" + socket.getInetAddress().getHostAddress() + "成功连接");
|
||||||
|
// 新建一个线程来处理该处理该请求(实现并发)
|
||||||
|
new HandleThread(socket).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package server.dao;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import server.tools.DBConnection;
|
||||||
|
import server.vo.User;
|
||||||
|
|
||||||
|
public class UserDAO {
|
||||||
|
// 按关键字进行查询,返回查询结果(vo对象,因为查询结果最多一条),没有查询到,则返回null
|
||||||
|
public static User get(String userName) throws Exception {
|
||||||
|
User user = null;
|
||||||
|
// 获取连接
|
||||||
|
Connection con = DBConnection.getConnection();
|
||||||
|
// 操作对应sql语句,支持参数
|
||||||
|
String sql = "select * from t_users where userName=?";
|
||||||
|
// 创建语句对象
|
||||||
|
PreparedStatement pst = con.prepareStatement(sql);
|
||||||
|
// 对sql语句的?参数进行赋值
|
||||||
|
pst.setString(1, userName);
|
||||||
|
// 执行select查询语句,如果sql语句是insert,update或delete,则需调用executeUpdate进行执行
|
||||||
|
ResultSet rs = pst.executeQuery();
|
||||||
|
// 将记录集转换为vo对象
|
||||||
|
while (rs.next()) {
|
||||||
|
user = new User();
|
||||||
|
user.setUserName(rs.getString("userName"));
|
||||||
|
user.setPassword(rs.getString("password"));
|
||||||
|
user.setName(rs.getString("name"));
|
||||||
|
user.setRole(rs.getString("role"));
|
||||||
|
}
|
||||||
|
// 关闭连接对象
|
||||||
|
DBConnection.close(con, pst);
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按任意条件组合查询,将查询结果存放在集合中【因为可能有多条】
|
||||||
|
public static ArrayList<User> query(User userCondition) throws Exception {
|
||||||
|
ArrayList<User> list = new ArrayList();
|
||||||
|
// 获取连接
|
||||||
|
Connection con = DBConnection.getConnection();
|
||||||
|
// 操作对应sql语句
|
||||||
|
String sql = "*******";
|
||||||
|
// 创建语句对象
|
||||||
|
PreparedStatement pst = con.prepareStatement(sql);
|
||||||
|
// 执行select查询语句,如果sql语句是insert,update或delete,则需调用executeUpdate进行执行
|
||||||
|
ResultSet rs = pst.executeQuery();
|
||||||
|
// 将记录集转换为vo对象
|
||||||
|
while (rs.next()) {
|
||||||
|
User user = new User();
|
||||||
|
user.setUserName(rs.getString("userName"));
|
||||||
|
user.setPassword(rs.getString("password"));
|
||||||
|
user.setName(rs.getString("name"));
|
||||||
|
user.setRole(rs.getString("role"));
|
||||||
|
list.add(user); // 将对象增加到集合中
|
||||||
|
}
|
||||||
|
// 关闭连接对象
|
||||||
|
DBConnection.close(con, pst);
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
package server.thread;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
|
||||||
|
import server.dao.UserDAO;
|
||||||
|
import server.tools.Encoding;
|
||||||
|
import server.vo.User;
|
||||||
|
|
||||||
|
public class HandleThread extends Thread {
|
||||||
|
private Socket socket;
|
||||||
|
private BufferedReader buf; // 字符输入流
|
||||||
|
private PrintWriter write; // 字符输出流
|
||||||
|
|
||||||
|
// 构造方法
|
||||||
|
public HandleThread(Socket socket) {
|
||||||
|
super();
|
||||||
|
this.socket = socket;
|
||||||
|
try {
|
||||||
|
// 获取输入流
|
||||||
|
InputStream is = socket.getInputStream();
|
||||||
|
buf = new BufferedReader(new InputStreamReader(is, "utf-8"));
|
||||||
|
// 获取输出流
|
||||||
|
OutputStream os = socket.getOutputStream();
|
||||||
|
write = new PrintWriter(new OutputStreamWriter(os, "utf-8"));
|
||||||
|
} catch (Exception e) {
|
||||||
|
// TODO: handle exception
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 具体的请求处理:线程执行start方法时自动调用
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
while (true) { // 循环接收客户端发送过来的数据
|
||||||
|
try {
|
||||||
|
// 接收客户端发送的json字符串
|
||||||
|
String receiveStr = buf.readLine();
|
||||||
|
System.out.println("接收到客户端的请求数据:" + receiveStr);
|
||||||
|
// 按协议解析字符串,如:code = 0,表示的是登录请求
|
||||||
|
JsonElement element = JsonParser.parseString(receiveStr); // 使用parseString方法将json字符串转换为JsonElement对象
|
||||||
|
JsonObject obj = element.getAsJsonObject(); // 将JsonElement对象转换为JsonObject对象
|
||||||
|
int code = obj.get("code").getAsInt(); // 获取code属性
|
||||||
|
if (code == 8) { // 客户端退出
|
||||||
|
close();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
String data = obj.get("data").getAsString(); // 获取data属性
|
||||||
|
String response = "";
|
||||||
|
switch (code) {
|
||||||
|
case 0: // 登录请求
|
||||||
|
response = loginHandle(data); // 登录请求处理,data中存放的是输入的用户名和密码
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
System.out.println("命令无效!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// 向客户端发送响应数据
|
||||||
|
write.println(response);
|
||||||
|
write.flush();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
// TODO: handle exception
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("客户端退出!");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 客户端选择退出时,关闭连接
|
||||||
|
public void close() throws Exception {
|
||||||
|
if (write != null) {
|
||||||
|
write.close();
|
||||||
|
}
|
||||||
|
if (buf != null) {
|
||||||
|
buf.close();
|
||||||
|
}
|
||||||
|
if (socket != null) {
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String loginHandle(String str) throws Exception {
|
||||||
|
// 解析json字符串,str中的存放的是输入的用户名,密码信息的json格式字符串,使用Gson包将其直接转换为User对象
|
||||||
|
User loginUser = new Gson().fromJson(str, User.class);
|
||||||
|
// 调用DAO,按用户名查询数据库,返回查询结果
|
||||||
|
User user = UserDAO.get(loginUser.getName());
|
||||||
|
/**
|
||||||
|
* 根据dao执行结果,给出相应的响应,需要设计响应字符串的协议格式 如可以这样设计:*{code:0
|
||||||
|
* 1,data:****}
|
||||||
|
* code=0表示登录失败,data中存放失败原因 code=
|
||||||
|
* 表示登录成功,data中放当前用户相关的信息(用户名,姓名,角色等)
|
||||||
|
*/
|
||||||
|
String responseData = "";
|
||||||
|
if (user == null) { // 登录失败,原因:表示的用户不存在
|
||||||
|
Map<String, Object> map = new HashMap<>();
|
||||||
|
map.put("code", 0);
|
||||||
|
map.put("data", "用户名不存在");
|
||||||
|
responseData = new Gson().toJson(map); // 将java的map对象直接转换成json字符串
|
||||||
|
} else { // 有该用户,进一步密码是否正确
|
||||||
|
if (Encoding.md5(loginUser.getPassword()).equals(user.getPassword())) { // 用户名密码正确
|
||||||
|
Map<String, Object> map = new HashMap<>();
|
||||||
|
map.put("code", 1);
|
||||||
|
map.put("data", new Gson().toJson(user)); // 使用toJson方法可以将java的vo对象直接转换成json字符串
|
||||||
|
responseData = new Gson().toJson(map); // 将java的map对象直接转换成json字符串
|
||||||
|
} else { // 密码不正确
|
||||||
|
Map<String, Object> map = new HashMap<>();
|
||||||
|
map.put("code", 0);
|
||||||
|
map.put("data", "密码错误");
|
||||||
|
responseData = new Gson().toJson(map); // 将java的map对象直接转换成json字符串
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return responseData;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package server.tools;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
|
||||||
|
public class DBConnection {
|
||||||
|
private String driverName;
|
||||||
|
private String url;
|
||||||
|
private String user;
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
// 加载驱动
|
||||||
|
static {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Connection getConnection() {
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void close(Connection con, PreparedStatement pst) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package server.tools;
|
||||||
|
|
||||||
|
public class Encoding {
|
||||||
|
// 对指定的字符串进行md5编码
|
||||||
|
public static String md5(String password) {
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将指定的文件进行base64编码,返回编码字符串
|
||||||
|
public static String base64FromFile(String filePath) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将base64编码字符串还原为文件,文件路径及文件名为filePath
|
||||||
|
public static String base64ToFile(String base64Str, String filePath) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package server.vo;
|
||||||
|
|
||||||
|
public class User {
|
||||||
|
private String userName;
|
||||||
|
private String password;
|
||||||
|
private String name;
|
||||||
|
private String role;
|
||||||
|
|
||||||
|
public User() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserName() {
|
||||||
|
return userName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserName(String userName) {
|
||||||
|
this.userName = userName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRole() {
|
||||||
|
return role;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRole(String role) {
|
||||||
|
this.role = role;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return userName + "\\t" + password + "\\t" + name + "\\t" + role;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in new issue
Block a user