武汉理工大学-Java面向对象与多线程综合实验-(6)网络编程

实验目标

本实验目标在实验 (5) 的基础上实现网络连接功能,在原有代码基础上新增加了 Client 客户端类和 Server 服务端类。

Client 类负责建立 Socket 对象,该对象会连接至特定 IP 地址与端口号,使用网络字节 IO 流向服务端传输操作信息、读取服务端回传信息;Server 类负责建立 ServerSocket 对象,利用 accept() 方法接收 Socket 对象的连接,也使用网络字节 IO 流与客户端进行信息交互。

模块解析

本次实验代码分为 common,frame,test,CS 共4个 package 。
1)common,frame 包模块结构功能与实验 (5) 相同;
2)test 包装有数据库连接测试类,客户端测试类,服务端测试类,仅用于单独地测试调试,不做详细描述;
3)详细介绍 CS 包,模块结构图如下:
在这里插入图片描述

准备工作

· Java 网络编程知识

推荐一个教学视频:https://www.bilibili.com/video/av63023329?p=1
建议读者首先了解 网络通信协议、IP地址、端口号 相关知识,之后重点学习 客户端、服务端代码实现 的内容。

源代码

test

test 包代码仅用于测试调试 。

·ClientTest

package test;

import java.net.*;
import java.io.*;

public class ClientTest {

	// 此类用于与ServerTest类测试连接
	public static void main(String[] args) {

		try {
			// 1.创建客户端对象Socket, 构造方法绑定服务器的IP地址和端口号
			Socket socket = new Socket("127.0.0.1", 8888);

			// 2.建立网络字节流InputStream与OutputStream的对象
			InputStream is = socket.getInputStream();
			OutputStream os = socket.getOutputStream();

			// 3.向服务端发送测试信息
			BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));
			bw.write("测试客户端和服务器通信,服务器接收到消息返回到客户端\n");
			bw.flush();

			// 4.读取服务器返回的消息
			BufferedReader br = new BufferedReader(new InputStreamReader(is));
			String mess = br.readLine();
			System.out.println("服务器:" + mess);

			// 5.释放资源
			socket.close();

		} catch (IOException e) {
			e.printStackTrace();
		}

	}

}

·ServerTest

package test;

import java.io.*;
import java.net.*;

public class ServerTest {

	// 此类用于与ClientTest类测试连接
	@SuppressWarnings("static-access")
	public static void main(String[] args) {

		try {
			// 1.创建服务端对象serverSocket和系统指定端口号
			ServerSocket serversocket = new ServerSocket(8888);
			System.out.println("启动服务器....");

			// 2.使用ServerSocket对象中的方法accept, 获取收到请求的客户端对象Socket
			Socket socket = serversocket.accept();
			System.out.println("客户端:" + socket.getInetAddress().getLocalHost() + "已连接到服务器");

			// 3.使用网络字节输入流读取客户端发送来的消息
			BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			String mess = br.readLine();
			System.out.println("客户端:" + mess);

			// 4.使用网络字节输出流向客户端回写信息
			BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
			bw.write(mess + "\n");
			bw.flush();

			// 5.释放资源
			serversocket.close();
			socket.close();

		} catch (IOException e) {
			e.printStackTrace();
		}

	}

}

ClientTest 与 ServerTest 作为测试客户端和服务端连接的范例代码。

·SQLtest

package test;

import java.sql.*;

public class SQLtest { // 该类用于测试数据库连接

	public static void main(String[] args) {

		Connection connection;
		Statement statement;
		ResultSet resultSet;

		// 加载数据库驱动类
		String driverName = "com.mysql.cj.jdbc.Driver"; // 新版本写法cj.
		// 声明数据库的URL
		String url = "jdbc:mysql://localhost:3306/document?useSSL=false&serverTimezone=UTC"; // 新版本显式关闭useSSL,添加时区

		// 数据库用户
		String user = "root";
		String password = "123456";

		try {

			// 1. 加载驱动
			Class.forName(driverName);
			// 2. 建立数据库连接
			connection = DriverManager.getConnection(url, user, password);
			// 3. 创建语句对象
			statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
			// 4. 执行SQL语句
			String sql = "select * from user_info ";
			// 5. 处理对象
			resultSet = statement.executeQuery(sql);
			while (resultSet.next()) {
				String username = resultSet.getString("UserName");
				String pwd = resultSet.getString("Password");
				String role = resultSet.getString("Role");
				System.out.println(username + ";" + pwd + ";" + role);
			}
			// 6. 释放资源
			resultSet.close();
			statement.close();
			connection.close();

		} catch (ClassNotFoundException e) {
			System.out.println("数据驱动错误");
		} catch (SQLException e) {
			System.out.println("数据库错误");
		}

	}

}

CS

本实验新增 CS 包代码,负责系统客户端和服务端连接,也是本次实验的重点内容。

·Client

package CS;

import java.io.*;
import java.net.*;

public class Client {

	// 用户Socket对象
	private static Socket client;
	// 网络字符输入输出流IO
	private static ObjectInputStream input;
	private static ObjectOutputStream output;
	// 从服务端接收的信息
	private static String messageFromServer;

	// 连接服务器
	public static void ConnectToServer() throws UnknownHostException, IOException {
		System.out.println("\n正在尝试连接服务器...\n");
		// Socket构造函数参数为IP地址与端口号
		client = new Socket("127.0.0.1", 8888);
		System.out.println("已连接至:" + client.getInetAddress().getHostName());
	}

	// 构造IO流
	public static void GetStreams() throws IOException {
		output = new ObjectOutputStream(client.getOutputStream());
		output.flush();
		input = new ObjectInputStream(client.getInputStream());
		System.out.println("IO构造完成\n");
	}

	// 断开连接
	public static void CloseConnection() throws IOException {
		output.close();
		input.close();
		client.close();
	}

	// 用户向服务器发送消息
	public static void SendMessage(String message) throws IOException {
		// 写入输出流
		output.writeObject(message);
		output.flush();
	}

	// 接收服务器回传的消息
	public static void ReceiveMessage() throws ClassNotFoundException, IOException {
		// 读入输入流
		messageFromServer = (String) input.readObject();
		System.out.println("SERVER>>> " + messageFromServer);
	}

}

·Server

package CS;

import java.io.*;
import java.net.*;

public class Server {

	// 服务器的ServerSocket对象
	private static ServerSocket server;
	// 网络字节输入输出流IO
	private static ObjectOutputStream output;
	private static ObjectInputStream input;
	// 接收连接用户的Socket对象connection
	private static Socket connection;
	// 从用户接收的信息
	private static String messageFromClient;
	// 连接用户的数量
	private static int counter = 1;

	// 等待用户连接
	public static void WaitForConnection() throws IOException {
		System.out.println("\n等待连接...\n");
		// 接收用户
		connection = server.accept();
		System.out.println("Connection " + counter + " 已连接:" + connection.getInetAddress().getHostName());
	}

	// 构造IO流
	public static void GetStreams() throws IOException {
		output = new ObjectOutputStream(connection.getOutputStream());
		output.flush();
		input = new ObjectInputStream(connection.getInputStream());
		System.out.println("IO构造完成\n");
	}

	// 保持消息监听
	public static void ProcessConnection() throws IOException, ClassNotFoundException {
		do {
			// 读入消息
			messageFromClient = (String) input.readObject();
			System.out.println("CLIENT>>> " + messageFromClient);
			// 回传消息
			output.writeObject(messageFromClient);
			output.flush();
		} while (!messageFromClient.equals("登出")); // 若消息不是登出,保持连接

		// 最后传出登出消息
		output.writeObject(messageFromClient);
		output.flush();
	}

	// 关闭服务器
	public static void CloseConnection() {
		try {
			output.close();
			input.close();
			connection.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	// 服务器主函数
	public static void main(String[] args) {

		try {
			// ServerSocket构造函数参数为端口号
			server = new ServerSocket(8888);
		} catch (IOException e) {
			e.printStackTrace();
		}

		try {
			// 保持循环,保证登出后再登录可再次连接
			while (true) {
				WaitForConnection();
				GetStreams();
				ProcessConnection();
			}

		} catch (IOException | ClassNotFoundException e) {
			e.printStackTrace();
		} finally {
			// 出现异常强制关闭服务器
			CloseConnection();
		}

	}
}

需要注意的一个小技巧在于使用 while 循环 保持 Server 和 Client 的连接状态。

common

·DataProcessing

package common;

import java.sql.*;
import java.util.Enumeration;
import java.util.Hashtable;

public class DataProcessing {

	private static boolean connectedToDatabase = false;

	private static Connection connection;
	private static Statement statement;
	private static ResultSet resultSet;

	public static void connectToDatabase(String driverName, String url, String user, String password)
			throws SQLException, ClassNotFoundException {
		// 加载驱动
		Class.forName(driverName);
		// 建立数据库连接
		connection = DriverManager.getConnection(url, user, password);
		connectedToDatabase = true;
	}

	public static void disconnectFromDatabase() throws SQLException {
		if (connectedToDatabase) {
			// 释放资源
			resultSet.close();
			statement.close();
			connection.close();
			connectedToDatabase = false;
		}
	}

	public static Doc searchDoc(String ID) throws SQLException {

		Doc doc = null;

		if (!connectedToDatabase)
			throw new SQLException("数据库未连接!");

		// 创建语句对象
		statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
		// 执行SQL语句
		String sql = "select * from doc_info where FileID='" + ID + "'"; // 获取FileID列中值为ID的数据,变量前后用+和'"、"'连接

		// 处理对象
		resultSet = statement.executeQuery(sql);
		// 获取数据库信息
		if (resultSet.next()) {

			String FileID = resultSet.getString("FileID");
			String Creator = resultSet.getString("Creator");
			Timestamp timestamp = Timestamp.valueOf(resultSet.getString("Timestamp")); // string转Timestamp
			String Description = resultSet.getString("Description");
			String FileName = resultSet.getString("FileName");

			doc = new Doc(FileID, Creator, timestamp, Description, FileName);
		}

		return doc;
	}

	public static boolean insertDoc(String ID, String creator, Timestamp timestamp, String description, String filename)
			throws SQLException {

		if (!connectedToDatabase)
			throw new SQLException("数据库未连接!");

		// 创建语句对象
		statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
		// 执行SQL语句
		String sql = "INSERT INTO doc_info VALUES('" + ID + "','" + creator + "','" + timestamp + "','" + description
				+ "','" + filename + "')";
		// 更新列表
		statement.executeUpdate(sql);

		return true;
	}

	public static Enumeration<Doc> getAllDocs() throws SQLException {

		if (!connectedToDatabase)
			throw new SQLException("数据库未连接!");

		// 建立哈希表
		Hashtable<String, Doc> docs = new Hashtable<String, Doc>();

		// 创建语句对象
		statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
		// 执行SQL语句
		String sql = "select * from doc_info "; // 获取doc_info所有值

		// 处理对象
		resultSet = statement.executeQuery(sql);
		// 获取数据库信息
		while (resultSet.next()) {

			// 遍历存放
			String FileID = resultSet.getString("FileID");
			String Creator = resultSet.getString("Creator");
			Timestamp timestamp = Timestamp.valueOf(resultSet.getString("Timestamp")); // string转Timestamp
			String Description = resultSet.getString("Description");
			String FileName = resultSet.getString("FileName");

			docs.put(FileID, new Doc(FileID, Creator, timestamp, Description, FileName));
		}

		// 返回枚举容器
		Enumeration<Doc> e = docs.elements();
		return e;
	}

	public static User searchUser(String name) throws SQLException {

		User user = null;

		if (!connectedToDatabase)
			throw new SQLException("未连接到数据库!");

		// 创建语句对象
		statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
		// 执行SQL语句
		String sql = "select * from user_info where UserName='" + name + "'"; // 获取UserName列中值为name的数据,变量前后用+和'"、"'连接

		// 处理对象
		resultSet = statement.executeQuery(sql);
		// 获取数据库信息
		if (resultSet.next()) {

			String UserName = resultSet.getString("UserName");
			String Password = resultSet.getString("Password");
			String Role = resultSet.getString("Role");

			user = new User(UserName, Password, Role);
		}

		return user;
	}

	public static User searchUser(String name, String password) throws SQLException {

		User user = null;

		if (!connectedToDatabase)
			throw new SQLException("未连接到数据库!");

		// 创建语句对象
		statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
		// 执行SQL语句
		String sql = "select * from user_info where UserName='" + name + "'"; // 获取UserName列中值为name的数据,变量前后用+和'"、"'连接

		// 处理对象
		resultSet = statement.executeQuery(sql);
		// 获取数据库信息
		if (resultSet.next()) {

			String UserName = resultSet.getString("UserName");
			String Password = resultSet.getString("Password");
			String Role = resultSet.getString("Role");

			user = new User(UserName, Password, Role);

			if (Password.equals(password)) {
				return user;
			} else {
				return null;
			}
		}

		return null;
	}

	public static Enumeration<User> getAllUser() throws SQLException {

		if (!connectedToDatabase)
			throw new SQLException("未连接到数据库!");

		// 建立哈希表
		Hashtable<String, User> users = new Hashtable<String, User>();

		// 创建语句对象
		statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
		// 执行SQL语句
		String sql = "select * from user_info "; // 获取user_info所有值

		// 处理对象
		resultSet = statement.executeQuery(sql);
		// 获取数据库信息
		while (resultSet.next()) {

			// 遍历存放
			String UserName = resultSet.getString("UserName");
			String Password = resultSet.getString("Password");
			String Role = resultSet.getString("Role");

			users.put(UserName, new User(UserName, Password, Role));
		}

		// 返回枚举容器
		Enumeration<User> e = users.elements();
		return e;
	}

	public static boolean updateUser(String name, String password, String role) throws SQLException {

		if (!connectedToDatabase)
			throw new SQLException("未连接到数据库!");

		// 创建语句对象
		statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
		// 执行SQL语句
		String sql = "UPDATE user_info SET Password='" + password + "',Role='" + role + "'WHERE UserName='" + name
				+ "'"; // 更改user_info相关数据
		// 更新列表
		statement.executeUpdate(sql);

		return true;
	}

	public static boolean insertUser(String name, String password, String role) throws SQLException {

		if (!connectedToDatabase)
			throw new SQLException("未连接到数据库!");

		// 创建语句对象
		statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
		// 执行SQL语句
		String sql = "INSERT INTO user_info VALUES('" + name + "','" + password + "','" + role + "')";
		// 更新列表
		statement.executeUpdate(sql);

		return true;
	}

	public static boolean deleteUser(String name) throws SQLException {

		if (!connectedToDatabase)
			throw new SQLException("未连接到数据库!");

		// 创建语句对象
		statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
		// 执行SQL语句
		String sql = "DELETE FROM user_info WHERE UserName='" + name + "'";
		// 更新列表
		statement.executeUpdate(sql);

		return true;
	}
}

·SQLconnection

package common;

import java.sql.SQLException;

public class SQLconnection {

	// 加载数据库驱动类
	static String DriverName = "com.mysql.cj.jdbc.Driver"; // 新版本写法cj.
	// 声明数据库的URL
	static String Url = "jdbc:mysql://localhost:3306/document?useSSL=false&serverTimezone=UTC"; // 新版本显式关闭useSSL,添加时区

	// 数据库用户
	static String User = "root";
	static String Password = "123456";

	// 连接数据库函数
	public static void Connect() throws ClassNotFoundException, SQLException {
		DataProcessing.connectToDatabase(DriverName, Url, User, Password);
	}

	// 断开数据库函数
	public static void Disconnect() throws SQLException {
		DataProcessing.disconnectFromDatabase();
	}

}

·Doc

package common;

import java.sql.Timestamp;

public class Doc {

	private String ID;
	private String creator;
	private Timestamp timestamp;
	private String description;
	private String filename;

	public Doc(String ID, String creator, Timestamp timestamp, String description, String filename) {
		this.setID(ID);
		this.setCreator(creator);
		this.setTimestamp(timestamp);
		this.setDescription(description);
		this.setFilename(filename);
	}

	public String getID() {
		return ID;
	}

	public void setID(String ID) {
		this.ID = ID;
	}

	public String getCreator() {
		return creator;
	}

	public void setCreator(String creator) {
		this.creator = creator;
	}

	public Timestamp getTimestamp() {
		return timestamp;
	}

	public void setTimestamp(Timestamp timestamp) {
		this.timestamp = timestamp;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public String getFilename() {
		return filename;
	}

	public void setFilename(String filename) {
		this.filename = filename;
	}

	public String toString() {
		return "ID:" + this.ID + " Creator:" + this.creator + " Time:" + this.timestamp + " Filename:" + this.filename
				+ " Description:" + this.description;
	}

}

·User

package common;

import java.io.*;
import java.sql.*;
import javax.swing.JOptionPane;

public class User {

	private String name;
	private String password;
	private String role;

	String uploadpath = "f:\\uploadfile\\";
	String downloadpath = "f:\\downloadfile\\";

	User(String name, String password, String role) {
		this.setName(name);
		this.setPassword(password);
		this.setRole(role);
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getRole() {
		return role;
	}

	public void setRole(String role) {
		this.role = role;
	}

	public String toString() {
		return "Name: " + this.name + " Password: " + this.password + " Role: " + this.role;
	}

	public boolean downloadFile(String fileID) {

		Doc doc;

		try {

			// 获取哈希表信息
			doc = DataProcessing.searchDoc(fileID);
			// 输入文件对象
			File input_file = new File(uploadpath + doc.getFilename());
			// 输入过滤器流,建立在文件流上
			BufferedInputStream input = new BufferedInputStream(new FileInputStream(input_file));

			// 输出文件对象
			File output_file = new File(downloadpath + doc.getFilename());
			// 创建文件
			output_file.createNewFile();
			// 输出过滤器流,建立在文件流上
			BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(output_file));

			// 用字节数组存取数据
			byte[] bytes = new byte[1024];
			// 文件写入操作
			int length = 0;
			while ((length = input.read(bytes)) != -1) {
				output.write(bytes, 0, length);
			}

			// 关闭流
			input.close();
			output.close();
			
			return true;

		} catch (SQLException | IOException e) {
			JOptionPane.showMessageDialog(null, e.getLocalizedMessage());
		}

		return false;
	}

	public boolean uploadFile(String fileID, String filepath, String filedescription) {

		// 输入文件对象
		File input_file = new File(filepath);
		// 获取文件名
		String filename = input_file.getName();
		// 获取当前时间
		Timestamp timestamp = new Timestamp(System.currentTimeMillis());

		try {

			if (DataProcessing.insertDoc(fileID, this.getName(), timestamp, filedescription, filename)) {

				// 输入过滤器流,建立在文件流上
				BufferedInputStream input = new BufferedInputStream(new FileInputStream(input_file));

				// 输出文件对象
				File output_file = new File(uploadpath + input_file.getName());
				// 创建文件
				output_file.createNewFile();
				// 输出过滤器流,建立在文件流上
				BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(output_file));

				// 用字节数组存取数据
				byte[] bytes = new byte[1024];
				// 文件写入操作
				int length = 0;
				while ((length = input.read(bytes)) != -1) {
					output.write(bytes, 0, length);
				}

				// 关闭流
				input.close();
				output.close();

				return true;

			} else
				return false;

		} catch (SQLException e) {
			JOptionPane.showMessageDialog(null, e.getLocalizedMessage());
		} catch (IOException e) {
			JOptionPane.showMessageDialog(null, e.getLocalizedMessage());
		}

		return false;
	}

	// 修改密码
	public boolean changeSelfInfo(String password) {

		try {
			if (DataProcessing.updateUser(name, password, role)) {
				this.password = password;
				return true;
			} else
				return false;
		} catch (SQLException e) {
			JOptionPane.showMessageDialog(null, e.getLocalizedMessage());
		}

		return false;
	}

}

·Administrator

package common;

public class Administrator extends User {

	public Administrator(String name, String password, String role) {
		super(name, password, role);
	}

}

·Browser

package common;

public class Browser extends User {

	public Browser(String name, String password, String role) {
		super(name, password, role);
	}

}

·Operator

package common;

public class Operator extends User {

	public Operator(String name, String password, String role) {
		super(name, password, role);
	}

}

·StringUtil

package common;

public class StringUtil {

	// 判断字符串是否为空
	// 去括号trim()
	public static boolean isEmpty(String str) {
		if (str == null || "".equals(str.trim())) {
			return true;
		} else {
			return false;
		}
	}

	// 判断字符串非空
	public static boolean isNotEmpty(String str) {
		if (str != null && !"".equals(str.trim())) {
			return true;
		} else {
			return false;
		}
	}

}

frame

·LoginFrame

package frame;

import java.awt.*;
import java.awt.event.*;
import java.io.IOException;
import java.sql.SQLException;
import javax.swing.*;
import javax.swing.border.*;
import CS.Client;
import common.*;

@SuppressWarnings("serial")
public class LoginFrame extends JFrame {

	private JPanel contentPane;
	private JLabel Username_Label;
	private JLabel Password_Label;
	private JTextField Username_Txt;
	private JPasswordField Password_Txt;
	private JButton Confirm_Button;
	private JButton Cancel_Button;

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					LoginFrame frame = new LoginFrame();
					frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the frame.
	 */
	public LoginFrame() {

		// 框架
		setTitle("\u7CFB\u7EDF\u767B\u5F55");
		setResizable(false);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(100, 100, 565, 372);

		// 中间容器
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		setContentPane(contentPane);

		// 用户名标签
		Username_Label = new JLabel("\u7528\u6237\u540D\uFF1A");
		Username_Label.setBounds(128, 89, 80, 40);
		Username_Label.setFont(new Font("黑体", Font.PLAIN, 20));

		// 密码标签
		Password_Label = new JLabel("\u5BC6\u7801\uFF1A");
		Password_Label.setBounds(148, 142, 60, 37);
		Password_Label.setFont(new Font("黑体", Font.PLAIN, 20));

		// 用户名文本域
		Username_Txt = new JTextField();
		Username_Txt.setBounds(222, 99, 176, 24);
		Username_Txt.setColumns(10);

		// 密码文本域
		Password_Txt = new JPasswordField();
		Password_Txt.setBounds(222, 150, 176, 24);

		// 确认按钮
		Confirm_Button = new JButton("\u786E\u5B9A");
		Confirm_Button.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				// 登录事件
				LoginInActionPerformed(e);
			}
		});
		Confirm_Button.setFont(new Font("黑体", Font.PLAIN, 20));
		Confirm_Button.setBounds(173, 216, 85, 27);

		// 取消按钮
		Cancel_Button = new JButton("\u53D6\u6D88");
		Cancel_Button.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				// 取消事件
				ResetValueActionPerformed(e);
			}
		});
		Cancel_Button.setFont(new Font("黑体", Font.PLAIN, 20));
		Cancel_Button.setBounds(313, 216, 85, 27);

		// 设置布局与添加部件
		// 绝对布局
		contentPane.setLayout(null);
		contentPane.add(Username_Label);
		contentPane.add(Password_Label);
		contentPane.add(Username_Txt);
		contentPane.add(Password_Txt);
		contentPane.add(Confirm_Button);
		contentPane.add(Cancel_Button);

		// 连接数据库
		// 应在构造函数中连接,不应在主函数连接,否则登出后无法连接
		try {
			SQLconnection.Connect();
		} catch (ClassNotFoundException | SQLException e) {
			JOptionPane.showMessageDialog(null, e.getLocalizedMessage());
		}

	}

	// 登录
	private void LoginInActionPerformed(ActionEvent evt) {

		String username = this.Username_Txt.getText();
		String password = new String(this.Password_Txt.getPassword()); // 获取输入内容

		if (StringUtil.isEmpty(username)) {
			JOptionPane.showMessageDialog(null, "未输入用户名!"); // 显示对话框
			return;
		}
		if (StringUtil.isEmpty(password)) {
			JOptionPane.showMessageDialog(null, "未输入密码!"); // 显示对话框
			return;
		}

		try {
			if (DataProcessing.searchUser(username, password) == null) {
				JOptionPane.showMessageDialog(null, "用户名与密码不匹配!"); // 显示对话框
				return;
			} else {

				// 连接服务器,发送登录消息
				Client.ConnectToServer();
				Client.GetStreams();
				Client.SendMessage(username + "登录");
				Client.ReceiveMessage();

				// 导入用户
				User user = DataProcessing.searchUser(username, password);
				// 令当前界面消失
				this.dispose();
				// 跳转至主界面,新建对象并传入用户参数
				MainFrame mainframe = new MainFrame(user);
				mainframe.setVisible(true);
			}

		} catch (HeadlessException | SQLException | IOException | ClassNotFoundException e) {
			JOptionPane.showMessageDialog(null, e.getLocalizedMessage());
			return;
		}

	}

	// 重置文本域
	private void ResetValueActionPerformed(ActionEvent evt) {
		// 设置为空
		this.Username_Txt.setText("");
		this.Password_Txt.setText("");
	}
}

·MainFrame

package frame;

import java.awt.*;
import java.awt.event.*;
import java.io.IOException;
import java.sql.SQLException;
import javax.swing.*;
import javax.swing.border.*;
import CS.Client;
import common.*;

@SuppressWarnings("serial")
public class MainFrame extends JFrame {

	private JPanel contentPane;
	private JMenuBar menuBar;

	private JMenu UserManager_Menu;
	private JMenu FileManager_Menu;
	private JMenu SelfInfo_Menu;
	private JMenu Others_Menu;

	private JButton AddUser_Button;
	private JButton DelUser_Button;
	private JButton UpdateUser_Button;
	private JButton UploadFile_Button;
	private JButton DownloadFile_Button;
	private JButton ChangeSelfInfo_Button;
	private JButton Exit_Button;

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					// 实验6无法实现单独调用界面, 不能实现用户与服务器的消息传递!
					// 请从登录界面开始跳转

					// 单独调用连接数据库
					try {
						SQLconnection.Connect();
					} catch (ClassNotFoundException | SQLException e) {
						JOptionPane.showMessageDialog(null, e.getLocalizedMessage());
					}

					// 单独启动默认管理者
					MainFrame frame = new MainFrame(new Administrator("kate", "123", "Administrator"));
					frame.setVisible(true);

				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the frame.
	 */
	public MainFrame(User user) {
		// 传入角色参数
		// 框架
		setResizable(false);
		// 根据角色设置标题
		SetTitle(user.getRole());
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(100, 100, 1171, 699);

		// 中间容器
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		setContentPane(contentPane);
		contentPane.setLayout(null);

		// 菜单栏
		menuBar = new JMenuBar();
		menuBar.setBounds(0, 0, 1165, 33);
		contentPane.add(menuBar);

		// 用户管理下拉菜单
		UserManager_Menu = new JMenu("\u7528\u6237\u7BA1\u7406");
		UserManager_Menu.setFont(new Font("黑体", Font.PLAIN, 18));
		menuBar.add(UserManager_Menu);

		// 增添用户按钮
		AddUser_Button = new JButton("\u589E\u6DFB\u7528\u6237");
		AddUser_Button.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				// 增添用户事件
				AddUserActionPerformed(user, e);
			}
		});
		AddUser_Button.setFont(new Font("黑体", Font.PLAIN, 16));
		UserManager_Menu.add(AddUser_Button);

		// 删除用户按钮
		DelUser_Button = new JButton("\u5220\u9664\u7528\u6237");
		DelUser_Button.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				// 删除用户事件
				DelUserActionPerformed(user, e);
			}
		});
		DelUser_Button.setFont(new Font("黑体", Font.PLAIN, 16));
		UserManager_Menu.add(DelUser_Button);

		// 修改用户按钮
		UpdateUser_Button = new JButton("\u4FEE\u6539\u7528\u6237");
		UpdateUser_Button.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				// 修改用户事件
				UpdateUserActionPerformed(user, e);
			}
		});
		UpdateUser_Button.setFont(new Font("黑体", Font.PLAIN, 16));
		UserManager_Menu.add(UpdateUser_Button);

		// 档案管理下拉菜单
		FileManager_Menu = new JMenu("\u6863\u6848\u7BA1\u7406");
		FileManager_Menu.setFont(new Font("黑体", Font.PLAIN, 18));
		menuBar.add(FileManager_Menu);

		// 上传文件按钮
		UploadFile_Button = new JButton("\u4E0A\u4F20\u6587\u4EF6");
		UploadFile_Button.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				// 上传文件事件
				UploadFileActionPerformed(user, e);
			}
		});
		UploadFile_Button.setFont(new Font("黑体", Font.PLAIN, 16));
		FileManager_Menu.add(UploadFile_Button);

		// 下载文件按钮
		DownloadFile_Button = new JButton("\u4E0B\u8F7D\u6587\u4EF6");
		DownloadFile_Button.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				// 下载文件事件
				DownloadFileActionPerformed(user, e);
			}
		});
		DownloadFile_Button.setFont(new Font("黑体", Font.PLAIN, 16));
		FileManager_Menu.add(DownloadFile_Button);

		// 个人信息管理下拉菜单
		SelfInfo_Menu = new JMenu("\u4E2A\u4EBA\u4FE1\u606F\u7BA1\u7406");
		SelfInfo_Menu.setFont(new Font("黑体", Font.PLAIN, 18));
		menuBar.add(SelfInfo_Menu);

		// 修改密码按钮
		ChangeSelfInfo_Button = new JButton("\u5BC6\u7801\u4FEE\u6539");
		ChangeSelfInfo_Button.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				// 修改密码事件
				ChangeSelfActionPerformed(user, e);
			}
		});
		ChangeSelfInfo_Button.setFont(new Font("黑体", Font.PLAIN, 16));
		SelfInfo_Menu.add(ChangeSelfInfo_Button);

		// 其他类下拉菜单
		Others_Menu = new JMenu("\u5176\u4ED6");
		Others_Menu.setFont(new Font("黑体", Font.PLAIN, 18));
		menuBar.add(Others_Menu);

		// 退出按钮
		Exit_Button = new JButton("\u9000\u51FA");
		Exit_Button.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				// 退出事件
				ExitActionPerformed(e);
			}
		});
		Exit_Button.setFont(new Font("黑体", Font.PLAIN, 16));
		Others_Menu.add(Exit_Button);

		// 设置各按钮权限
		SetRights(user.getRole());
	}

	// 增添用户
	private void AddUserActionPerformed(User user, ActionEvent evt) {
		// 选项编号 0
		UserFrame userframe = new UserFrame(user, 0);
		userframe.setVisible(true);
	}

	// 删除用户
	private void DelUserActionPerformed(User user, ActionEvent evt) {
		// 选项编号 1
		UserFrame userframe = new UserFrame(user, 1);
		userframe.setVisible(true);
	}

	// 修改用户
	private void UpdateUserActionPerformed(User user, ActionEvent evt) {
		// 选项编号 2
		UserFrame userframe = new UserFrame(user, 2);
		userframe.setVisible(true);
	}

	// 上传文件
	private void UploadFileActionPerformed(User user, ActionEvent evt) {
		// 选项编号0
		FileFrame fileframe = new FileFrame(user, 0);
		fileframe.setVisible(true);
	}

	// 下载文件
	private void DownloadFileActionPerformed(User user, ActionEvent evt) {
		// 选项编号1
		FileFrame fileframe = new FileFrame(user, 1);
		fileframe.setVisible(true);
	}

	// 修改密码
	private void ChangeSelfActionPerformed(User user, ActionEvent evt) {
		SelfInfoFrame selfframe = new SelfInfoFrame(user);
		selfframe.setVisible(true);
	}

	// 设置标题
	private void SetTitle(String role) {
		if (role.equalsIgnoreCase("administrator")) {
			setTitle("档案管理员界面");
		} else if (role.equalsIgnoreCase("browser")) {
			setTitle("档案浏览员界面");
		} else if (role.equalsIgnoreCase("operator")) {
			setTitle("档案录入员界面");
		}
	}

	// 设置用户权限
	private void SetRights(String role) {

		if (role.equalsIgnoreCase("administrator")) {

			AddUser_Button.setEnabled(true);
			DelUser_Button.setEnabled(true);
			UpdateUser_Button.setEnabled(true);
			DownloadFile_Button.setEnabled(true);
			UploadFile_Button.setEnabled(false);
			ChangeSelfInfo_Button.setEnabled(true);
			Exit_Button.setEnabled(true);

		} else if (role.equalsIgnoreCase("browser")) {

			AddUser_Button.setEnabled(false);
			DelUser_Button.setEnabled(false);
			UpdateUser_Button.setEnabled(false);
			DownloadFile_Button.setEnabled(true);
			UploadFile_Button.setEnabled(false);
			ChangeSelfInfo_Button.setEnabled(true);
			Exit_Button.setEnabled(true);

		} else if (role.equalsIgnoreCase("operator")) {

			AddUser_Button.setEnabled(false);
			DelUser_Button.setEnabled(false);
			UpdateUser_Button.setEnabled(false);
			DownloadFile_Button.setEnabled(true);
			UploadFile_Button.setEnabled(true);
			ChangeSelfInfo_Button.setEnabled(true);
			Exit_Button.setEnabled(true);
		}
	}

	// 退出
	private void ExitActionPerformed(ActionEvent evt) {

		// 断开数据库连接
		try {
			SQLconnection.Disconnect();
		} catch (SQLException e) {
			JOptionPane.showMessageDialog(null, e.getLocalizedMessage());
		}

		// 发送登出消息
		try {
			Client.SendMessage("登出");
			// 最后再接收服务器登出消息
			Client.ReceiveMessage();
			Client.CloseConnection();
		} catch (IOException | ClassNotFoundException e) {
			JOptionPane.showMessageDialog(null, e.getLocalizedMessage());
			return;
		} finally {
			// 在finally里返回登录界面,无论是否断开服务器连接都能返回
			this.dispose();
			LoginFrame loginframe = new LoginFrame();
			loginframe.setVisible(true);
		}

	}

}

·UserFrame

package frame;

import java.awt.*;
import java.awt.event.*;
import java.io.IOException;
import java.sql.SQLException;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.table.DefaultTableModel;

import CS.Client;

import java.util.Enumeration;
import common.*;

@SuppressWarnings("serial")
public class UserFrame extends JFrame {

	// 中间容器
	private JPanel contentPane;
	// 多页面容器
	private JTabbedPane tabbedPane;

	// 增添用户页面及组件
	private JPanel AddUser_Panel;
	private JLabel Username_Label1;
	private JLabel Password_Label1;
	private JLabel Role_Label1;
	private JTextField Username_Txt1;
	private JPasswordField Password_Txt1;
	@SuppressWarnings("rawtypes")
	private JComboBox Role_ComboBox1;
	private JButton Confirm_Button1;
	private JButton Return_Button1;

	// 删除用户页面及组件
	private JPanel DelUser_Panel;
	private JScrollPane scrollPane;
	private JTable Users_table;
	private JButton Confirm_Button2;
	private JButton Return_Button2;

	// 修改用户页面及组件
	private JPanel UpdateUser_Panel;
	private JLabel Username_Label2;
	private JLabel Password_Label2;
	private JLabel Role_Label2;
	private JTextField Username_Txt2;
	private JPasswordField Password_Txt2;
	@SuppressWarnings("rawtypes")
	private JComboBox Role_ComboBox2;
	private JButton Confirm_Button3;
	private JButton Return_Button3;

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					// 实验6无法实现单独调用界面, 不能实现用户与服务器的消息传递!
					// 请从登录界面开始跳转

					// 单独调用连接数据库
					try {
						SQLconnection.Connect();
					} catch (ClassNotFoundException | SQLException e) {
						JOptionPane.showMessageDialog(null, e.getLocalizedMessage());
					}

					UserFrame frame = new UserFrame(new Administrator("kate", "123", "Administrator"), 0);
					frame.setVisible(true);

				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the frame.
	 */

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public UserFrame(User user, int choice) {
		// 传入用户及页面选项: 0 增添用户 1 删除用户 2 修改用户
		// 框架
		setResizable(false);
		setTitle("\u7528\u6237\u7BA1\u7406\u754C\u9762");
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(100, 100, 587, 441);

		// 中间容器
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		setContentPane(contentPane);
		contentPane.setLayout(null);

		// 多页面容器
		tabbedPane = new JTabbedPane(JTabbedPane.TOP);
		tabbedPane.setBounds(14, 49, 553, 309);
		contentPane.add(tabbedPane);

		// 添加用户页面
		AddUser_Panel = new JPanel();
		// 增添选项卡
		tabbedPane.addTab("\u65B0\u589E\u7528\u6237", null, AddUser_Panel, null);
		AddUser_Panel.setLayout(null);

		// 用户名标签
		Username_Label1 = new JLabel("\u7528\u6237\u540D");
		Username_Label1.setFont(new Font("黑体", Font.PLAIN, 18));
		Username_Label1.setBounds(114, 38, 61, 32);
		AddUser_Panel.add(Username_Label1);

		// 密码标签
		Password_Label1 = new JLabel("\u5BC6\u7801");
		Password_Label1.setFont(new Font("黑体", Font.PLAIN, 18));
		Password_Label1.setBounds(132, 93, 43, 32);
		AddUser_Panel.add(Password_Label1);

		// 角色标签
		Role_Label1 = new JLabel("\u89D2\u8272");
		Role_Label1.setFont(new Font("黑体", Font.PLAIN, 18));
		Role_Label1.setBounds(132, 150, 43, 32);
		AddUser_Panel.add(Role_Label1);

		// 用户名文本域
		Username_Txt1 = new JTextField();
		Username_Txt1.setBounds(197, 44, 181, 24);
		Username_Txt1.setColumns(10);
		AddUser_Panel.add(Username_Txt1);

		// 密码文本域
		Password_Txt1 = new JPasswordField();
		Password_Txt1.setBounds(197, 99, 181, 24);
		AddUser_Panel.add(Password_Txt1);

		// 角色选项栏
		Role_ComboBox1 = new JComboBox();
		Role_ComboBox1.setEditable(true);
		Role_ComboBox1.setModel(new DefaultComboBoxModel(new String[] { "", "Administrator", "Browser", "Operator" }));
		Role_ComboBox1.setBounds(197, 156, 181, 24);
		AddUser_Panel.add(Role_ComboBox1);

		// 增添按钮
		Confirm_Button1 = new JButton("\u589E\u6DFB");
		Confirm_Button1.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				// 增添用户事件
				AddUserActionPerformed(user, e);
			}
		});
		Confirm_Button1.setFont(new Font("黑体", Font.PLAIN, 18));
		Confirm_Button1.setBounds(132, 222, 113, 27);
		AddUser_Panel.add(Confirm_Button1);

		// 返回按钮
		Return_Button1 = new JButton("\u8FD4\u56DE");
		Return_Button1.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				// 返回事件
				ReturnActionPerformed(e);
			}
		});
		Return_Button1.setFont(new Font("黑体", Font.PLAIN, 18));
		Return_Button1.setBounds(329, 222, 113, 27);
		AddUser_Panel.add(Return_Button1);

		// 删除用户页面
		DelUser_Panel = new JPanel();
		tabbedPane.addTab("\u5220\u9664\u7528\u6237", null, DelUser_Panel, null);
		DelUser_Panel.setLayout(null);

		// 删除按钮
		Confirm_Button2 = new JButton("\u5220\u9664");
		Confirm_Button2.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				// 删除事件
				DelUserActionPerformed(user, e);
			}
		});
		Confirm_Button2.setBounds(132, 222, 113, 27);
		Confirm_Button2.setFont(new Font("黑体", Font.PLAIN, 18));
		DelUser_Panel.add(Confirm_Button2);

		// 返回按钮
		Return_Button2 = new JButton("\u8FD4\u56DE");
		Return_Button2.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				// 返回事件
				ReturnActionPerformed(e);
			}
		});
		Return_Button2.setBounds(329, 222, 113, 27);
		Return_Button2.setFont(new Font("黑体", Font.PLAIN, 18));
		DelUser_Panel.add(Return_Button2);

		// 可下拉容器域
		scrollPane = new JScrollPane();
		scrollPane.setBounds(62, 37, 432, 159);
		DelUser_Panel.add(scrollPane);

		// 用户列表
		Users_table = new JTable();
		// 构造表格
		ConstructUserTable();
		// 加入可下拉区域
		scrollPane.setViewportView(Users_table);

		// 修改用户页面
		UpdateUser_Panel = new JPanel();
		UpdateUser_Panel.setLayout(null);
		tabbedPane.addTab("\u4FEE\u6539\u7528\u6237", null, UpdateUser_Panel, null);

		// 用户名标签
		Username_Label2 = new JLabel("\u7528\u6237\u540D");
		Username_Label2.setFont(new Font("黑体", Font.PLAIN, 18));
		Username_Label2.setBounds(114, 38, 61, 32);
		UpdateUser_Panel.add(Username_Label2);

		// 密码标签
		Password_Label2 = new JLabel("\u5BC6\u7801");
		Password_Label2.setFont(new Font("黑体", Font.PLAIN, 18));
		Password_Label2.setBounds(132, 93, 43, 32);
		UpdateUser_Panel.add(Password_Label2);

		// 角色标签
		Role_Label2 = new JLabel("\u89D2\u8272");
		Role_Label2.setFont(new Font("黑体", Font.PLAIN, 18));
		Role_Label2.setBounds(132, 150, 43, 32);
		UpdateUser_Panel.add(Role_Label2);

		// 用户名文本域
		Username_Txt2 = new JTextField();
		Username_Txt2.setColumns(10);
		Username_Txt2.setBounds(197, 44, 181, 24);
		UpdateUser_Panel.add(Username_Txt2);

		// 密码文本域
		Password_Txt2 = new JPasswordField();
		Password_Txt2.setBounds(197, 99, 181, 24);
		UpdateUser_Panel.add(Password_Txt2);

		// 角色选项栏
		Role_ComboBox2 = new JComboBox();
		Role_ComboBox2.setModel(new DefaultComboBoxModel(new String[] { "", "Administrator", "Browser", "Operator" }));
		Role_ComboBox2.setEditable(true);
		Role_ComboBox2.setBounds(197, 156, 181, 24);
		UpdateUser_Panel.add(Role_ComboBox2);

		// 修改按钮
		Confirm_Button3 = new JButton("\u4FEE\u6539");
		Confirm_Button3.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				// 修改用户事件
				UpdateUserActionPerformed(user, e);
			}
		});
		Confirm_Button3.setFont(new Font("黑体", Font.PLAIN, 18));
		Confirm_Button3.setBounds(132, 222, 113, 27);
		UpdateUser_Panel.add(Confirm_Button3);

		// 返回按钮
		Return_Button3 = new JButton("\u8FD4\u56DE");
		Return_Button3.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				// 返回事件
				ReturnActionPerformed(e);
			}
		});
		Return_Button3.setFont(new Font("黑体", Font.PLAIN, 18));
		Return_Button3.setBounds(329, 222, 113, 27);
		UpdateUser_Panel.add(Return_Button3);

		// 设置页面
		SetPane(choice);
	}

	// 用户表格构造
	private void ConstructUserTable() {

		// 表头数据
		String[] columnNames = { "\u7528\u6237\u540D", "\u5BC6\u7801", "\u89D2\u8272" };
		// 表格数据
		String[][] rowData = new String[20][3];

		Enumeration<User> u;
		try {
			// 获取哈希表信息
			u = DataProcessing.getAllUser();
			// 行数
			int row = 0;
			// 将哈希表信息导入至表格数据
			while (u.hasMoreElements()) {
				User user = u.nextElement();
				rowData[row][0] = user.getName();
				rowData[row][1] = user.getPassword();
				rowData[row][2] = user.getRole();
				row++;
			}

		} catch (SQLException e) {
			JOptionPane.showMessageDialog(null, e.getLocalizedMessage());
		}

		// 构造表格
		Users_table.setModel(new DefaultTableModel(rowData, columnNames) {

			boolean[] columnEditables = new boolean[] { false, false, false };

			public boolean isCellEditable(int row, int column) {
				return columnEditables[column];
			}

		});

	}

	// 增添
	private void AddUserActionPerformed(User user, ActionEvent evt) {

		// 获取输入内容
		String username = this.Username_Txt1.getText();
		String password = new String(this.Password_Txt1.getPassword());
		String role = (String) this.Role_ComboBox1.getSelectedItem();

		if (StringUtil.isEmpty(username)) {
			JOptionPane.showMessageDialog(null, "未输入用户名!");
			return;
		}
		if (StringUtil.isEmpty(password)) {
			JOptionPane.showMessageDialog(null, "未输入密码!");
			return;
		}
		if (StringUtil.isEmpty(role)) {
			JOptionPane.showMessageDialog(null, "未选择身份!");
			return;
		}

		try {
			if (DataProcessing.insertUser(username, password, role)) {

				// 发送增添用户的消息
				try {
					Client.SendMessage("增添用户");
					Client.ReceiveMessage();
				} catch (IOException | ClassNotFoundException e) {
					JOptionPane.showMessageDialog(null, e.getLocalizedMessage());
					return;
				}

				// 更新表格数据
				ConstructUserTable();
				JOptionPane.showMessageDialog(null, "添加成功!");
				return;

			} else {
				JOptionPane.showMessageDialog(null, "添加失败!用户名已存在!");
				return;
			}

		} catch (HeadlessException | SQLException e) {
			JOptionPane.showMessageDialog(null, e.getLocalizedMessage());
		}

	}

	// 删除
	private void DelUserActionPerformed(User user, ActionEvent evt) {

		// 获取所选行序号,若未选择其值为-1
		int selectedrow = Users_table.getSelectedRow();

		// 未选择用户的情况
		if (selectedrow == -1) {
			JOptionPane.showMessageDialog(null, "未选择用户!");
			return;
		} else {

			// 获取所选行的用户名
			String username = (String) Users_table.getValueAt(selectedrow, 0);
			// 若选择空行
			if (StringUtil.isEmpty(username)) {
				return;
			}
			// 选择自身用户的情况
			if (username.equals(user.getName())) {
				JOptionPane.showMessageDialog(null, "不能删除自身用户!");
				return;
			}

			// 显示确认界面: 信息, 标题, 选项个数
			int value = JOptionPane.showConfirmDialog(null, "确定要删除用户吗?", "用户删除确认界面", 2);
			// Yes=0 No=1
			if (value == 0) {
				try {
					if (DataProcessing.deleteUser(username)) {

						// 发送删除用户消息
						try {
							Client.SendMessage("删除用户");
							Client.ReceiveMessage();
						} catch (IOException | ClassNotFoundException e) {
							JOptionPane.showMessageDialog(null, e.getLocalizedMessage());
							return;
						}

						// 更新表格数据
						ConstructUserTable();
						JOptionPane.showMessageDialog(null, "删除成功!");
						return;

					} else {
						JOptionPane.showMessageDialog(null, "删除失败!");
						return;
					}

				} catch (SQLException e) {
					JOptionPane.showMessageDialog(null, e.getLocalizedMessage());
				}

			} else if (value == 1) {
				return;
			}
		}
	}

	// 修改
	private void UpdateUserActionPerformed(User user, ActionEvent evt) {

		String username = this.Username_Txt2.getText();
		String password = new String(this.Password_Txt2.getPassword());
		String role = (String) this.Role_ComboBox2.getSelectedItem();

		if (StringUtil.isEmpty(username)) {
			JOptionPane.showMessageDialog(null, "未输入用户名!");
			return;
		}
		if (StringUtil.isEmpty(password)) {
			JOptionPane.showMessageDialog(null, "未输入密码!");
			return;
		}
		if (StringUtil.isEmpty(role)) {
			JOptionPane.showMessageDialog(null, "未选择身份!");
			return;
		}

		try {

			if (DataProcessing.searchUser(username, password) == null) {
				JOptionPane.showMessageDialog(null, "用户名与密码不匹配!");
				return;
			} else {
				// 显示确认界面:信息,标题,选项个数
				int value = JOptionPane.showConfirmDialog(null, "确定要修改信息吗?", "信息修改确认界面", 2);
				// Yes=0 No=1
				if (value == 0) {
					if (DataProcessing.updateUser(username, password, role)) {

						// 发送修改用户信息
						try {
							Client.SendMessage("修改用户");
							Client.ReceiveMessage();
						} catch (IOException | ClassNotFoundException e) {
							JOptionPane.showMessageDialog(null, e.getLocalizedMessage());
							return;
						}

						// 更新表格数据
						ConstructUserTable();
						JOptionPane.showMessageDialog(null, "修改成功!");
						return;

					} else {
						JOptionPane.showMessageDialog(null, "修改失败!");
						return;
					}

				} else if (value == 1) {
					return;
				}
			}

		} catch (HeadlessException | SQLException e) {
			e.printStackTrace();
			JOptionPane.showMessageDialog(null, e.getLocalizedMessage());
		}
	}

	// 设置页面
	private void SetPane(int value) {
		if (value == 0) {
			tabbedPane.setSelectedComponent(AddUser_Panel);
		} else if (value == 1) {
			tabbedPane.setSelectedComponent(DelUser_Panel);
		} else if (value == 2) {
			tabbedPane.setSelectedComponent(UpdateUser_Panel);
		}
	}

	// 返回
	private void ReturnActionPerformed(ActionEvent evt) {
		this.dispose();
	}

}

·FileFrame

package frame;

import java.awt.*;
import java.awt.event.*;
import java.io.IOException;
import java.sql.SQLException;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.table.DefaultTableModel;

import CS.Client;

import java.text.SimpleDateFormat;
import java.util.Enumeration;
import common.*;

@SuppressWarnings("serial")
public class FileFrame extends JFrame {

	// 中间容器
	private JPanel contentPane;
	// 多页面容器
	private JTabbedPane tabbedPane;

	// 上传文件页面及组件
	private JPanel Upload_Panel;
	private JLabel FileID_Label;
	private JLabel Filedescription_Label;
	private JLabel Filename_Label;
	private JTextField FileID_Txt;
	private JTextArea Filedescription_Txt;
	private JTextField Filepath_Txt;
	private JButton Upload_Button;
	private JButton OpenFile_Button;
	private JButton Return_Button1;

	// 下载文件页面及组件
	private JPanel Download_Panel;
	private JButton Download_Button;
	private JButton Return_Button2;
	private JScrollPane scrollPane;
	private JTable Files_table;

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					// 实验6无法实现单独调用界面, 不能实现用户与服务器的消息传递!
					// 请从登录界面开始跳转

					// 单独调用连接数据库
					try {
						SQLconnection.Connect();
					} catch (ClassNotFoundException | SQLException e) {
						JOptionPane.showMessageDialog(null, e.getLocalizedMessage());
					}

					FileFrame frame = new FileFrame(new Administrator("jack", "123", "operator"), 0);
					frame.setVisible(true);

				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the frame.
	 */
	public FileFrame(User user, int choice) {
		// 传入用户及页面选项: 0上传文件 1下载文件
		// 框架
		setTitle("\u6587\u4EF6\u7BA1\u7406\u754C\u9762");
		setResizable(false);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(100, 100, 802, 581);

		// 中间容器
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		setContentPane(contentPane);
		contentPane.setLayout(null);

		// 多页面容器
		tabbedPane = new JTabbedPane(JTabbedPane.TOP);
		tabbedPane.setBounds(38, 35, 713, 469);
		contentPane.add(tabbedPane);

		// 上传页面
		Upload_Panel = new JPanel();
		tabbedPane.addTab("\u6587\u4EF6\u4E0A\u4F20", null, Upload_Panel, null);
		Upload_Panel.setLayout(null);

		// 档案号标签
		FileID_Label = new JLabel("\u6863\u6848\u53F7");
		FileID_Label.setFont(new Font("黑体", Font.PLAIN, 20));
		FileID_Label.setBounds(125, 33, 60, 36);
		Upload_Panel.add(FileID_Label);

		// 文件描述标签
		Filedescription_Label = new JLabel("\u6863\u6848\u63CF\u8FF0");
		Filedescription_Label.setFont(new Font("黑体", Font.PLAIN, 20));
		Filedescription_Label.setBounds(105, 90, 80, 36);
		Upload_Panel.add(Filedescription_Label);

		// 文件名标签
		Filename_Label = new JLabel("\u6863\u6848\u6587\u4EF6\u540D");
		Filename_Label.setFont(new Font("黑体", Font.PLAIN, 20));
		Filename_Label.setBounds(85, 314, 100, 36);
		Upload_Panel.add(Filename_Label);

		// 档案号文本域
		FileID_Txt = new JTextField();
		FileID_Txt.setBounds(215, 40, 272, 27);
		Upload_Panel.add(FileID_Txt);
		FileID_Txt.setColumns(10);

		// 文件描述文本域
		Filedescription_Txt = new JTextArea();
		Filedescription_Txt.setBounds(215, 96, 272, 199);
		Upload_Panel.add(Filedescription_Txt);
		Filedescription_Txt.setColumns(10);

		// 文件名文本域
		Filepath_Txt = new JTextField();
		Filepath_Txt.setColumns(10);
		Filepath_Txt.setBounds(215, 321, 272, 27);
		Upload_Panel.add(Filepath_Txt);

		// 上传按钮
		Upload_Button = new JButton("\u4E0A\u4F20");
		Upload_Button.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				// 上传文件事件
				UploadActionPerformed(user, e);
			}
		});
		Upload_Button.setBounds(215, 380, 95, 27);
		Upload_Button.setFont(new Font("黑体", Font.PLAIN, 20));
		Upload_Panel.add(Upload_Button);

		// 返回按钮
		Return_Button1 = new JButton("\u8FD4\u56DE");
		Return_Button1.setBounds(395, 380, 95, 27);
		Return_Button1.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				// 返回事件
				ReturnActionPerformed(e);
			}
		});

		// 打开按钮
		OpenFile_Button = new JButton("\u6253\u5F00");
		OpenFile_Button.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				// 打开文件事件
				OpenFileActionPerformed(e);
			}
		});
		OpenFile_Button.setFont(new Font("黑体", Font.PLAIN, 18));
		OpenFile_Button.setBounds(532, 319, 95, 27);
		Upload_Panel.add(OpenFile_Button);
		Return_Button1.setFont(new Font("黑体", Font.PLAIN, 20));
		Upload_Panel.add(Return_Button1);

		// 下载页面
		Download_Panel = new JPanel();
		tabbedPane.addTab("\u6587\u4EF6\u4E0B\u8F7D", null, Download_Panel, null);
		tabbedPane.setEnabledAt(1, true);
		Download_Panel.setLayout(null);

		// 下载按钮
		Download_Button = new JButton("\u4E0B\u8F7D");
		Download_Button.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				// 下载文件事件
				DownloadActionPerformed(user, e);
			}
		});
		Download_Button.setFont(new Font("黑体", Font.PLAIN, 20));
		Download_Button.setBounds(215, 380, 95, 27);
		Download_Panel.add(Download_Button);

		// 返回按钮
		Return_Button2 = new JButton("\u8FD4\u56DE");
		Return_Button2.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				// 返回事件
				ReturnActionPerformed(e);
			}
		});
		Return_Button2.setFont(new Font("黑体", Font.PLAIN, 20));
		Return_Button2.setBounds(395, 380, 95, 27);
		Download_Panel.add(Return_Button2);

		// 可下拉容器
		scrollPane = new JScrollPane();
		scrollPane.setBounds(35, 32, 637, 322);
		Download_Panel.add(scrollPane);

		// 下载文件列表
		Files_table = new JTable();
		// 构造表格
		ConstructFileTable();
		// 加入可下拉区域
		scrollPane.setViewportView(Files_table);

		// 设置权限及页面
		setPane(user, choice);
	}

	// 表格构造
	private void ConstructFileTable() {

		// 表头数据
		String[] columnNames = { "\u6863\u6848\u53F7", "\u521B\u5EFA\u8005", "\u65F6\u95F4", "\u6587\u4EF6\u540D",
				"\u6587\u4EF6\u63CF\u8FF0" };
		// 表格数据
		String[][] rowData = new String[20][5];

		Enumeration<Doc> f;
		try {
			// 获取哈希表信息
			f = DataProcessing.getAllDocs();

			// 行数
			int row = 0;
			// 将哈希表信息导入至表格
			while (f.hasMoreElements()) {
				Doc doc = f.nextElement();
				rowData[row][0] = doc.getID();
				rowData[row][1] = doc.getCreator();
				rowData[row][2] = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(doc.getTimestamp()); // Time转String
				rowData[row][3] = doc.getFilename();
				rowData[row][4] = doc.getDescription();
				row++;
			}

		} catch (SQLException e) {
			JOptionPane.showMessageDialog(null, e.getLocalizedMessage());
		}

		// 构造表格
		Files_table.setModel(new DefaultTableModel(rowData, columnNames) {

			boolean[] columnEditables = new boolean[] { false, false, false, false, false };

			public boolean isCellEditable(int row, int column) {
				return columnEditables[column];
			}

		});

	}

	// 打开文件
	private void OpenFileActionPerformed(ActionEvent evt) {

		// 弹出文件选择框
		FileDialog OpenFileDialog = new FileDialog(this, "选择上传文件");
		OpenFileDialog.setVisible(true);

		// 获取文件路径
		String filepath = OpenFileDialog.getDirectory() + OpenFileDialog.getFile();
		Filepath_Txt.setText(filepath);

	}

	// 上传文件
	private void UploadActionPerformed(User user, ActionEvent evt) {

		String filepath = Filepath_Txt.getText();
		String fileID = FileID_Txt.getText();
		String filedescription = Filedescription_Txt.getText();

		if (StringUtil.isEmpty(filepath)) {
			JOptionPane.showMessageDialog(null, "未选择文件!");
			return;
		}
		if (StringUtil.isEmpty(fileID)) {
			JOptionPane.showMessageDialog(null, "未输入档案号!");
			return;
		}
		if (StringUtil.isEmpty(filedescription)) {
			JOptionPane.showMessageDialog(null, "未输入文件描述!");
			return;
		}

		if (user.uploadFile(fileID, filepath, filedescription)) {

			// 发送上传文件消息
			try {
				Client.SendMessage("上传文件");
				Client.ReceiveMessage();
			} catch (IOException | ClassNotFoundException e) {
				JOptionPane.showMessageDialog(null, e.getLocalizedMessage());
				return;
			}

			// 更新表格数据
			ConstructFileTable();
			JOptionPane.showMessageDialog(null, "上传成功!");
			return;

		} else {
			JOptionPane.showMessageDialog(null, "上传失败!");
			return;
		}

	}

	// 下载文件
	private void DownloadActionPerformed(User user, ActionEvent evt) {

		// 获取所选行序号, 若未选择其值为-1
		int selectedrow = Files_table.getSelectedRow();

		// 未选择文件的情况
		if (selectedrow == -1) {
			JOptionPane.showMessageDialog(null, "未选择文件!");
			return;
		} else {

			// 获取档案号
			String fileID = (String) Files_table.getValueAt(selectedrow, 0);
			// 若选择空行
			if (StringUtil.isEmpty(fileID)) {
				return;
			}

			// 显示确认界面: 信息, 标题, 选项个数
			int value = JOptionPane.showConfirmDialog(null, "确定要下载文件吗?", "文件下载确认界面", 2);
			// Yes=0 No=1
			if (value == 0) {
				if (user.downloadFile(fileID)) {

					// 发送下载文件消息
					try {
						Client.SendMessage("下载文件");
						Client.ReceiveMessage();
					} catch (IOException | ClassNotFoundException e) {
						JOptionPane.showMessageDialog(null, e.getLocalizedMessage());
						return;
					}

					JOptionPane.showMessageDialog(null, "下载成功!");
					return;
				} else {
					JOptionPane.showMessageDialog(null, "下载失败!");
					return;
				}

			} else if (value == 1) {
				return;
			}
		}
	}

	// 设置页面
	private void setPane(User user, int choice) {

		if (!user.getRole().equalsIgnoreCase("operator")) {
			FileID_Txt.setEditable(false);
			Filedescription_Txt.setEditable(false);
			Filepath_Txt.setEditable(false);
			Upload_Button.setEnabled(false);
			OpenFile_Button.setEnabled(false);
		}

		if (choice == 0) {
			tabbedPane.setSelectedComponent(Upload_Panel);
		} else if (choice == 1) {
			tabbedPane.setSelectedComponent(Download_Panel);
		}

	}

	// 返回
	private void ReturnActionPerformed(ActionEvent evt) {
		this.dispose();
	}
}

·SelfInfoFrame

package frame;

import java.awt.*;
import java.awt.event.*;
import java.io.IOException;
import java.sql.SQLException;
import javax.swing.*;
import javax.swing.border.EmptyBorder;

import CS.Client;
import common.*;

@SuppressWarnings("serial")
public class SelfInfoFrame extends JFrame {

	private JPanel contentPane;
	private JLabel Username_Label;
	private JLabel OldPassword_Label;
	private JLabel NewPassword_Label;
	private JLabel ConfirmPassword_Label;
	private JLabel Role_Label;

	private JTextField Username_Txt;
	private JPasswordField OldPassword_Txt;
	private JPasswordField NewPassword_Txt;
	private JPasswordField ConfirmPassword_Txt;
	private JTextField Role_Txt;

	private JButton Confirm_Button;
	private JButton Return_Button;

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					// 实验6无法实现单独调用界面, 不能实现用户与服务器的消息传递!
					// 请从登录界面开始跳转

					// 单独调用连接数据库
					try {
						SQLconnection.Connect();
					} catch (ClassNotFoundException | SQLException e) {
						JOptionPane.showMessageDialog(null, e.getLocalizedMessage());
					}

					SelfInfoFrame frame = new SelfInfoFrame(new Administrator("kate", "123", "Administrator"));
					frame.setVisible(true);

				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the frame.
	 */
	public SelfInfoFrame(User user) {
		// 传入用户参数
		// 框架
		setResizable(false);
		setTitle("\u4E2A\u4EBA\u4FE1\u606F\u7BA1\u7406");
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(100, 100, 502, 384);

		// 中间容器
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		setContentPane(contentPane);
		contentPane.setLayout(null);

		// 用户名标签
		Username_Label = new JLabel("\u7528\u6237\u540D:");
		Username_Label.setFont(new Font("黑体", Font.PLAIN, 18));
		Username_Label.setBounds(100, 51, 72, 30);
		contentPane.add(Username_Label);

		// 旧密码标签
		OldPassword_Label = new JLabel("\u539F\u5BC6\u7801:");
		OldPassword_Label.setFont(new Font("黑体", Font.PLAIN, 18));
		OldPassword_Label.setBounds(100, 93, 72, 30);
		contentPane.add(OldPassword_Label);

		// 新密码标签
		NewPassword_Label = new JLabel("\u65B0\u5BC6\u7801:");
		NewPassword_Label.setFont(new Font("黑体", Font.PLAIN, 18));
		NewPassword_Label.setBounds(100, 135, 72, 30);
		contentPane.add(NewPassword_Label);

		// 确认密码标签
		ConfirmPassword_Label = new JLabel("\u786E\u8BA4\u65B0\u5BC6\u7801:");
		ConfirmPassword_Label.setFont(new Font("黑体", Font.PLAIN, 18));
		ConfirmPassword_Label.setBounds(63, 178, 109, 30);
		contentPane.add(ConfirmPassword_Label);

		// 角色标签
		Role_Label = new JLabel("\u89D2\u8272:");
		Role_Label.setFont(new Font("黑体", Font.PLAIN, 18));
		Role_Label.setBounds(118, 221, 57, 30);
		contentPane.add(Role_Label);

		// 用户名文本域
		Username_Txt = new JTextField();
		// 自动设置文本为用户名
		Username_Txt.setText(user.getName());
		Username_Txt.setEditable(false);
		Username_Txt.setBounds(186, 56, 154, 24);
		contentPane.add(Username_Txt);
		Username_Txt.setColumns(10);

		// 旧密码文本域
		OldPassword_Txt = new JPasswordField();
		OldPassword_Txt.setBounds(186, 98, 154, 24);
		contentPane.add(OldPassword_Txt);

		// 新密码文本域
		NewPassword_Txt = new JPasswordField();
		NewPassword_Txt.setBounds(186, 140, 154, 24);
		contentPane.add(NewPassword_Txt);

		// 确认密码文本域
		ConfirmPassword_Txt = new JPasswordField();
		ConfirmPassword_Txt.setBounds(186, 183, 154, 24);
		contentPane.add(ConfirmPassword_Txt);

		// 角色文本域
		Role_Txt = new JTextField();
		// 自动设置用户身份
		Role_Txt.setText(user.getRole());
		Role_Txt.setEditable(false);
		Role_Txt.setColumns(10);
		Role_Txt.setBounds(186, 226, 154, 24);
		contentPane.add(Role_Txt);

		// 确认按钮
		Confirm_Button = new JButton("\u786E\u8BA4");
		Confirm_Button.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				// 修改密码事件
				ChangeSelfInfoActionPerformed(user, e);
			}
		});
		Confirm_Button.setFont(new Font("黑体", Font.PLAIN, 18));
		Confirm_Button.setBounds(118, 288, 113, 27);
		contentPane.add(Confirm_Button);

		// 返回按钮
		Return_Button = new JButton("\u8FD4\u56DE");
		Return_Button.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				// 返回事件
				ReturnActionPerformed(e);
			}
		});
		Return_Button.setFont(new Font("黑体", Font.PLAIN, 18));
		Return_Button.setBounds(278, 288, 113, 27);
		contentPane.add(Return_Button);
	}

	// 修改密码
	private void ChangeSelfInfoActionPerformed(User user, ActionEvent evt) {

		String oldpassword = new String(OldPassword_Txt.getPassword());
		String newpassword = new String(NewPassword_Txt.getPassword());
		String confirmpassword = new String(ConfirmPassword_Txt.getPassword());

		// 检查是否为空
		if (StringUtil.isEmpty(oldpassword)) {
			JOptionPane.showMessageDialog(null, "未输入旧密码!");
			return;
		}
		if (StringUtil.isEmpty(newpassword)) {
			JOptionPane.showMessageDialog(null, "未输入新密码!");
			return;
		}
		if (StringUtil.isEmpty(confirmpassword)) {
			JOptionPane.showMessageDialog(null, "请输入确认密码!");
			return;
		}

		// 密码匹配
		try {
			if (DataProcessing.searchUser(user.getName(), oldpassword) == null) {
				JOptionPane.showMessageDialog(null, "用户名与原密码不匹配!");
				return;
			}
			if (!newpassword.equals(confirmpassword)) {
				JOptionPane.showMessageDialog(null, "两次输入的新密码不相同!");
				return;
			}

			// 修改密码
			if (user.changeSelfInfo(newpassword)) {

				// 发送修改密码消息
				try {
					Client.SendMessage("修改密码");
					Client.ReceiveMessage();
				} catch (IOException | ClassNotFoundException e) {
					JOptionPane.showMessageDialog(null, e.getLocalizedMessage());
					return;
				}

				// 清空
				this.OldPassword_Txt.setText("");
				this.NewPassword_Txt.setText("");
				this.ConfirmPassword_Txt.setText("");

				JOptionPane.showMessageDialog(null, "修改成功!");
				return;

			} else {
				JOptionPane.showMessageDialog(null, "修改失败!");
				return;
			}

		} catch (HeadlessException | SQLException e) {
			JOptionPane.showMessageDialog(null, e.getLocalizedMessage());
		}

	}

	// 返回
	private void ReturnActionPerformed(ActionEvent evt) {
		this.dispose();
	}
}

先运行 Server,再运行 LoginFrame 启动系统
在这里插入图片描述
在这里插入图片描述

写在最后

声明:本文内容来源于武汉理工大学2019-2020学年Java编程实验,仅供学习参考。如有不足错误地方,还请指出。
代码不要无脑抄 ,很多细节没有详细讲解,读者需自行理解。祝愿读者在编程之路上不断进步!

  • 6
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值