day25综合案例-学员管理系统【C/S版】

day25综合案例-学员管理系统【C/S版】

反馈和复习
1.建议下午也上课(9-12 2-3)
2."正则反向原理"
复习:
1.枚举
    public enum 枚举名{
    	枚举项1,枚举项2,...枚举项n;
	}
	===>
    public enum Sex{
     	BOY,GIRL;   
    }
	本质:
	public final class Sex extends java.lang.Enum<Sex>{
        //枚举 项,其实就是当前类的一个静态对象
        public static final Sex BOY = new Sex();
        public static final Sex GIRL = new Sex();
        //类中其实也可以添加我们自己的成员变量/成员方法/构造方法
    }
    
2.JDK剩下的2个特性
    方法引用: 把已经存在的方法,直接拿过来,给函数式接口类型的变量赋值
    		System.out::println
            集合.foreach(System.out::println).foreach(System.out::println)
    Base64编码:
			Decoder(解码器)  Encoder(编码器)
        
3.正则表达式   
    普通字符串.matches(正则表达式);  
	普通字符串.split(正则表达式);
	普通字符串.replaceAll(正则表达式,新的字符串);
	正则的语法:
		字符类: [abc][^abc][0-9][a-z][A-Z][0-9a-zA-Z][a-em-q]
		逻辑运算符类: && |
        预定义字符: \d   \w 	.
        数量词: A* A? A+  A{n} A{n,} A{n,m}  
		分组: (..){2}   
今日内容
学生管理系统(C/S)
    用户在客户端操作(增删改查) 
    通过TCP编程把数据发送给服务器
    服务器获取数据后要把数据保存到本地
    通过TCP编程把结果反馈给客户端    

一 项目演示

1.1 打开项目

在这里插入图片描述

1.2 运行项目
  • 运行服务器

在这里插入图片描述

  • 运行客户端

在这里插入图片描述

1.3 增删改查演示
  • 添加学生

    在这里插入图片描述

  • 根据id查询学生

在这里插入图片描述

  • 查询所有学生

在这里插入图片描述

  • 修改学生

在这里插入图片描述

  • 删除学生

在这里插入图片描述

二 项目说明

2.1 所采用的知识点要求
  • IO流技术

    服务器读写本地文件时,我们采用普通字符流/缓冲流技术,
    要求一个学生一行,并且学生的属性之间使用","间隔
    比如:
    1,张三,,18
    2,李四,,28    
    
  • 网络编程TCP技术

    客户端和服务器采用TCP"短连接",每个功能当需要与服务器连接时,才建立连接,功能完毕,连接立即 断开,即客户端使用一个功能连接服务器一次,使用完毕断开连接
    
  • 多线程技术

    为了同时支持多个客户端,可以连接服务器,要求每个客户端来了,服务器都要开启一个新的线程处理
    
  • 序列化和反序列化技术

    服务器给客户端返回一个学生对象或者一个学生集合时,我们采用序列化流发送给客户端,客户端采用反序列化流读取数据
    
2.2 客户端与服务器交互
  • 交互过程图解

在这里插入图片描述

  • 数据格式说明

    添加:"[1]数据",例如:"[1]张三,男,22"
    根据id查询一条数据:"[2]id",例如:"[2]1",意思:查询id为1的学员信息  
    修改一条数据:"[3]新数据"。例如:"[3]1,张三2,女,19",意思:将id=1的学员改为后面的新数据。
    查询所有数据:"[4]"。例如:"[4]",意思:后面不用带任何数据。  
    删除一条数据:"[5]id"。例如:"[5]1",意思:删除id为1的记录    
    

三 客户端代码实现

3.1 学生类和打印学生信息的工具类介绍
学生类:
public class Student implements Serializable{
    private int id;
    private String name;
    private String sex;
    private int age;
	//其他省略
}
工具类:
public class StudentUtils {
    //打印ArrayList<Student>的方法
    public static void printStudentList(ArrayList<Student> stuList) {
        System.out.println("--------------------------------------------------");
        System.out.println("编号\t\t姓名\t\t\t性别\t\t年龄");
        for (int i = 0; i < stuList.size(); i++) {
            Student stu = stuList.get(i);
            System.out.println(stu.getId() + "\t\t" + stu.getName() + "\t\t\t" + stu.getSex() + "\t\t" + stu.getAge());

        }
        System.out.println("--------------------------------------------------");
    }
    //打印单个学生的方法
    public static void printStudent(Student stu) {
        System.out.println("--------------------------------------------------");
        System.out.println("编号\t\t姓名\t\t\t性别\t\t年龄");
        System.out.println(stu.getId() + "\t\t" + stu.getName() + "\t\t\t" + stu.getSex() + "\t\t" + stu.getAge());
        System.out.println("--------------------------------------------------");
    }
}
3.2 编写客户端主程序
public class StudentClient {
    //定义一个静态的Scanner对象
    public static Scanner sc = new Scanner(System.in);
    public static void main(String[] args) {
        System.out.println("欢迎使用学生管理系统~~~");
        while (true) {
            //1.菜单
            System.out.println();//空行,为了美观
            System.out.println("1.添加学生  2.修改学生  3.删除学生  4.查询学生 5.查看所有学生  6.退出");
            //2.键盘录入
            System.out.println("请选择:");
            int user = sc.nextInt();
            //3.判断
            switch (user) {
                case 1:
                    //添加学生
                    addStudent();
                    break;
                case 2:
                    //修改学生
                    updateStudent();
                    break;
                case 3:
                    //删除学生
                    deleteStudent();
                    break;
                case 4:
                    //根据ID查询
                    findStudent();
                    break;
                case 5:
                    //查询所有学生
                    findStudents();
                    break;
                case 6:
                    //退出
                    System.out.println("欢迎下次光临~~");
                    System.exit(0);
                default:
                    //提示输入有误
                    System.out.println("您输入的功能有误~~");
                    break;
            }
        }
    }
}
3.3 添加学生功能
1.客户端得到数据(用户输入)
2.使用TCP连接将数据发送给服务器
3.读取服务器发送回来的反馈
4.释放资源 
//添加学生
public static void addStudent() {
	System.out.println("【添加学生】");
	//1.客户端得到数据(用户输入)
	System.out.println("请输入姓名:");
	String name = sc.next();

	System.out.println("请输入性别:");
	String sex = sc.next();

	System.out.println("请输入年龄:");
	int age = sc.nextInt();

	//2.使用TCP连接将数据发送给服务器
	Socket socket = getSocket();
	if (socket == null) {
		System.out.println("服务器暂时无法连接...");
		return;
	}
	try (OutputStream out = socket.getOutputStream();
		 InputStream in = socket.getInputStream();) {
		//给服务器发送数据的格式:"[1]数据",例如:"[1]张三,男,22"
		out.write(("[1]" + name + "," + sex + "," + age).getBytes());
		//3.读取服务器发送回来的反馈
		int result = in.read();
		if (result == 1) {
			System.out.println("【添加成功】");
		} else {
			System.out.println("【添加失败,请联系系统管理员~~~】");
		}
		//4.释放资源
		socket.close();
	} catch (IOException ie) {
		ie.printStackTrace();
	}
}    
3.4 修改学生功能
//修改学生
public static void updateStudent() {
	System.out.println("【修改学生】");
	//1.客户端得到数据(用户输入)
	System.out.println("请输入要修改学生的ID:");
	int ID = sc.nextInt();

	//我们修改学生之前,先要查询该学生,如果不存在,提示不存在,如果存在,再让用户输入其他修改后的信息
	//2.使用TCP连接将数据发送给服务器
	Socket socket = getSocket();
	if (socket == null) {
		System.out.println("服务器暂时无法连接...");
		return;
	}
	Student student = null;
	//获取输出流
	try (OutputStream out = socket.getOutputStream();
		 InputStream in = socket.getInputStream();) {
		//查询学生的格式:"[2]id",例如:"[2]1"
		out.write(("[2]" + ID).getBytes());
		//3.读取服务器发送回来的反馈
		ObjectInputStream ois = new ObjectInputStream(in);
		student = (Student) ois.readObject();
		if (student == null) {
			System.out.println("【查无此人】");
			return;
		} else {
			StudentUtils.printStudent(student);
		}
		//4.释放资源
		socket.close();
        ois.close();
	} catch (Exception ie) {
		ie.printStackTrace();
	}
	//我们修改学生之前,先要查询该学生,如果不存在,提示不存在,如果存在,再让用户输入其他修改后的信息

	//程序执行到这,说明有此学生,我们就可以修改
	//1.客户端得到数据(用户输入其他的信息)
	System.out.println("请输入新的姓名(如果不修改输入0):");
	String name = sc.next();
	System.out.println("请输入新的性别(如果不修改输入0):");
	String sex = sc.next();
	System.out.println("请输入新的年龄(如果不修改输入0):");
	int age = sc.nextInt();

	//用户输入完毕之后,我们先判断
	if (!"0".equals(name)) {
		student.setName(name);
	}
	if (!"0".equals(sex)) {
		student.setSex(sex);
	}
	if (age != 0) {
		student.setAge(age);
	}

	//2.使用TCP连接将数据发送给服务器
	Socket socketUpdate = getSocket();
	if (socketUpdate == null) {
		System.out.println("服务器暂时无法连接...");
		return;
	}
	try (OutputStream out = socketUpdate.getOutputStream();
		 InputStream in = socketUpdate.getInputStream();) {
		//发送修改信息数据格式: "[3]新数据"。例如:"[3]1,张三2,女,19"
		out.write(("[3]" + ID + "," + student.getName() + "," + student.getSex() + "," + student.getAge()).getBytes());
		//3.读取服务器发送回来的反馈
		int result = in.read();
		if (result == 1) {
			System.out.println("【修改成功】");
		} else {
			System.out.println("【修改失败,请联系管理员~~~】");
		}
		//4.释放资源
		socketUpdate.close();
	} catch (IOException ie) {
		ie.printStackTrace();
	}
}
3.5 根据id查询学生功能
//根据ID查询
public static void findStudent() {
	System.out.println("【根据ID查询】");
	//1.客户端得到数据(用户输入)
	System.out.println("请输入要查询的学生的ID:");
	int ID = sc.nextInt();
	//2.使用TCP连接将数据发送给服务器
	Socket socket = getSocket();
	if (socket == null) {
		System.out.println("服务器暂时无法连接...");
		return;
	}
	//获取输出流
	try (OutputStream out = socket.getOutputStream();
		 InputStream in = socket.getInputStream();) {
		//查询学生的格式:"[2]id",例如:"[2]1"
		out.write(("[2]" + ID).getBytes());
		//3.读取服务器发送回来的反馈
		ObjectInputStream ois = new ObjectInputStream(in);
		Student student = (Student) ois.readObject();
		if (student == null) {
			System.out.println("【查无此人】");
		} else {
			StudentUtils.printStudent(student);
		}
		//4.释放资源
		socket.close();
        ois.close();
	} catch (Exception ie) {
		ie.printStackTrace();
	}
}
3.6 查询所有学生功能
//查询所有学生
public static void findStudents() {
	System.out.println("【查询所有学生】");
	//1.客户端得到数据(用户输入)
	//客户端不需要输入任何东西
	//2.使用TCP连接将数据发送给服务器
	Socket socket = getSocket();
	if (socket == null) {
		System.out.println("服务器暂时无法连接...");
		return;
	}
	try (OutputStream out = socket.getOutputStream();
		 InputStream in = socket.getInputStream();) {
		//查询所有的格式:"[4]"。例如:"[4]"
		out.write("[4]".getBytes());

		//3.读取服务器发送回来的反馈
		ObjectInputStream ois = new ObjectInputStream(in);
		ArrayList<Student> students = (ArrayList<Student>) ois.readObject();
		if (students == null || students.size() == 0) {
			System.out.println("【服务器暂无学生信息】");
		} else {
			//调用工具类
			StudentUtils.printStudentList(students);
		}
		//4.释放资源
		socket.close();
	} catch (Exception ie) {
		ie.printStackTrace();
	}
}
3.6 删除学生功能
//删除学生
public static void deleteStudent() {
	System.out.println("【删除学生】");
	//1.客户端得到数据(用户输入)
	System.out.println("请输入您要删除学生的ID:");
	int ID = sc.nextInt();

	//用户输入完毕ID之后,我们不应该立刻发送给服务器进行删除
	//应该先把该ID的学生查询出来,展示给我用户,然后问他真的确认删除吗
	//2.使用TCP连接将数据发送给服务器
	Socket socket = getSocket();
	if (socket == null) {
		System.out.println("服务器暂时无法连接...");
		return;
	}
	Student student = null;
	//获取输出流
	try (OutputStream out = socket.getOutputStream();
		 InputStream in = socket.getInputStream();) {
		//查询学生的格式:"[2]id",例如:"[2]1"
		out.write(("[2]" + ID).getBytes());
		//3.读取服务器发送回来的反馈
		student = (Student) ois.readObject();
		if (student == null) {
			System.out.println("【查无此人】");
			return;
		} else {
			StudentUtils.printStudent(student);
		}
		//4.释放资源
		socket.close();
        ois.close();
	} catch (Exception ie) {
		ie.printStackTrace();
	}
	//用户输入完毕ID之后,我们不应该立刻发送给服务器进行删除
	//应该先把该ID的学生查询出来,展示给我用户,然后问他真的确认删除吗

	//程序执行到此处,说明有该ID的学生
	//1.客户端得到数据(用户输入确定信息)
	System.out.println("您确定要删除以上信息的学生吗(y/n)?");
	String user = sc.next();

	if (!"y".equals(user)) { //只要用户输入的不是y 我们就认为取消!!
		System.out.println("【删除操作已经取消】");
		return;
	}
	//2.使用TCP连接将数据发送给服务器
	Socket socketDelete = getSocket();
	if (socketDelete == null) {
		System.out.println("服务器暂时无法连接...");
		return;
	}
	try (OutputStream out = socketDelete.getOutputStream();
		 InputStream in = socketDelete.getInputStream();) {
		//发送删除数据格式:"[5]id"。例如:"[5]1"
		out.write(("[5]" + ID).getBytes());
		//3.读取服务器发送回来的反馈
		int result = in.read();
		if (result == 1) {
			System.out.println("【删除成功】");
		} else {
			System.out.println("【删除失败,请联系管理员~~】");
		}
		//4.释放资源
		socketDelete.close();
	} catch (IOException ie) {
		ie.printStackTrace();
	}
}

四 服务器代码实现

4.1 学生类和保存学生数据的工具类介绍
学生类:
public class Student implements Serializable{
    private int id;
    private String name;
    private String sex;
    private int age;
	//其他省略
}

保存学生数据的工具类
public class StudentDao {
    //将集合中所有学生对象,写入到文件中
    public static void writeAll(ArrayList<Student> stuList) {
        try (FileWriter out = new FileWriter("student.txt")) {
            for (Student stu : stuList) {
                //格式: id,姓名,性别,年龄
                out.write(stu.getId() + "," + stu.getName() + "," + stu.getSex() + "," + stu.getAge());
                //换行
                out.write("\r\n");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //从文件中读取所有学生的信息,返回学生集合
    public static ArrayList<Student> readAll() {
        ArrayList<Student> stuList = new ArrayList<>();
        //1.创建File对象
        File file = new File("student.txt");
        if (!file.exists()) {
            try {
                //2.如果不存在,则创建,否则读取会抛异常
                file.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //3.读数据,一次一行 格式: id,姓名,性别,年龄
        try (BufferedReader bufIn = new BufferedReader(
                new FileReader("student.txt"))) {
            String line = null;
            while ((line = bufIn.readLine()) != null) {
                //4.一行切割成一个数组,[id,姓名,性别,年龄]
                String[] rowArray = line.split(",");
                //5.创建学生对象,封装数据
                Student stu = new Student();
                stu.setId(Integer.parseInt(rowArray[0]));
                stu.setName(rowArray[1]);
                stu.setSex(rowArray[2]);
                stu.setAge(Integer.parseInt(rowArray[3]));
                //6.添加到集合中
                stuList.add(stu);
            }
        } catch (IOException e) {
            return null;
        }
        //7.返回整个集合
        return stuList;
    }

    //添加一个学生,返回boolean代表是否添加成功
    public static boolean addStudent(Student student) {
        //1.先读取所有学生
        ArrayList<Student> stuList = readAll();
        if (stuList == null) {//说明读取文件出错
            return false;
        }

        //2.获取最后一个学生的id,加1后作为新学生的id
        if (stuList.size() != 0) {
            student.setId(stuList.get(stuList.size() - 1).getId() + 1);//取最后一个对象的id + 1
        } else {
            //3.如果没有学生,说明是第一个,则id设置为1
            student.setId(1);//第一次添加,文件中没有内容
        }
        //4.添加到集合中
        stuList.add(student);
        //5.把集合重写写入到文件中
        writeAll(stuList);
        //6.返回添加成功
        return true;
    }
    //根据id删除一个学生,返回boolean代表是否删除成功
    public static boolean deleteById(int id) {
        //1.先读取所有学生
        ArrayList<Student> stuList = readAll();
        if (stuList == null) {//说明读取文件出错
            return false;
        }
        //2.遍历集合
        for (int i = 0; i < stuList.size(); i++) {
            Student stu = stuList.get(i);
            //3.判断学生的id是否和要删除的id相等
            if (stu.getId() == id) {
                //4.从集合中删除学生
                stuList.remove(i);
                //5.重写写入到文件中
                writeAll(stuList);
                //6.返回成功
                return true;
            }
        }
        //7.如果没找到学生返回失败
        return false;
    }
    //修改学生,返回boolean代表是否修改成功
    public static boolean updateStudent(Student student) {
        //1.先读取所有学生
        ArrayList<Student> stuList = readAll();
        if (stuList == null) {//说明读取文件出错
            return false;
        }
        System.out.println("修改的数据:" + student);
        //2.遍历集合
        for (int i = 0; i < stuList.size(); i++) {
            Student stu = stuList.get(i);
            //3.判断哪个学生id和要修改的学生id相同
            if (stu.getId() == student.getId()) {
                //4.将学生改为新的学生
                stuList.set(i, student);
                //5.重写将集合写入到文集中
                writeAll(stuList);//写回文件
                //6.返回成功
                return true;
            }
        }
        //7.返回失败
        return false;//没找到
    }
    //根据id查询学生,返回查询到的学生
    public static Student findById(int id) {
        //1.先读取所有学生
        ArrayList<Student> stuList = readAll();
        if (stuList == null) {//说明读取文件出错
            return null;
        }
        //2.遍历集合
        for (int i = 0; i < stuList.size(); i++) {
            Student stu = stuList.get(i);
            //3.比较id
            if (stu.getId() == id) {
                //4.找到返回学生对象
                return stu;
            }
        }
        //5.找不到返回null
        return null;
    }
}
    
4.2 编写服务器主程序
注意我们的需求: 每个客户端连接后要开启一个线程来处与这个客户端的数据交互!!
public class StudentServer {
    public static void main(String[] args) throws IOException {
        //1.启动
        ServerSocket server = new ServerSocket(8888);
        System.out.println("学生管理系统服务器启动...");
        //2.等待客户端连接
        while (true) {
            System.out.println("等待客户端...");
            Socket socket = server.accept();
            //3.开启一个线程,传入Socket对象
            new ServerThread(socket).start();
        }
    }
}

public class ServerThread extends Thread {
    public Socket socket;
    public ServerThread(Socket socket){
        this.socket = socket;
    }
    @Override
    public void run() {

    }
} 
4.3 编写线程的任务代码
public class ServerThread extends Thread {

    public Socket socket;

    public ServerThread(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        //1.读取客户端发送的数据
        try (InputStream in = socket.getInputStream();) {
            byte[] bs = new byte[1024];
            int len = in.read(bs);
            String msg = new String(bs, 0, len);
            System.out.println("客户端的数据:" + msg);
            //2.分析数据
            //合法性判断
            if (msg.charAt(0) != '[' || msg.charAt(2) != ']') {
                System.out.println("客户端数据格式有误~~~");
                socket.close();
                in.close();
                return;
            }
            //截取数据
            String num = msg.substring(1, 2);//[1]再赶,男,30
            //3.判断
            switch (num) {
                case "1":
                    //添加学生
                    addStudent(msg);
                    break;
                case "2":
                    //根据ID查询
                    findStudent(msg);
                    break;
                case "3":
                    //修改数据
                    updateStudent(msg);
                    break;
                case "4":
                    //查询所有
                    findStudents(msg);
                    break;
                case "5":
                    //删除一条
                    deleteStudent(msg);
                    break;
                default:
                    //客户端数据有误~~
                    System.out.println("该客户端数据有误~~");
                    socket.close();
                    break;
            }

        } catch (IOException ie) {
            ie.printStackTrace();
        }
    }
}
4.4 线程中的增删改查实现
思考:服务器增删改查的方法,有没有共同的步骤
1.封装数据
2.调用StudentDao的增删改查方法
3.反馈数据给客户端
4.释放资源    
  • 添加学生

    //添加学生
    public void addStudent(String msg) {
    	//msg  [1]再赶,男,30
    	//1.封装数据
    	String stuMsg = msg.substring(3);
    	String[] msgArr = stuMsg.split(",");//[再赶,男,30]
    	Student newStudent = new Student(msgArr[0],msgArr[1],Integer.parseInt(msgArr[2]));
    	//2.调用StudentDao的增删改查方法
    	StudentDao.addStudent(newStudent);
    	//3.反馈数据给客户端
    	try (OutputStream out = socket.getOutputStream();) {
    		out.write(1);
    		//4.释放资源
    		socket.close();
    	} catch (IOException ie) {
    		ie.printStackTrace();
    	}
    }
    
  • 查询所有学生

    //查询所有学生
    public void findStudents(String msg) {
    	//1.封装数据
    	//不需要封装任何数据
    	//2.调用StudentDao的增删改查方法
    	ArrayList<Student> stuList = StudentDao.readAll();
    	//3.反馈数据给客户端
    	try {
    		OutputStream out = socket.getOutputStream();
    		ObjectOutputStream oos = new ObjectOutputStream(out);
    		oos.writeObject(stuList);
    		//4.释放资源
    		oos.close();
    		out.close();
    		socket.close();
    	} catch (IOException ie) {
    		ie.printStackTrace();
    	}
    }
    
  • 根据id查询

    //根据ID查询学生
    public void findStudent(String msg) {
    	//msg: "[2]1"
    	//1.封装数据
    	int findID = Integer.parseInt(msg.substring(3));
    	//2.调用StudentDao的增删改查方法
    	Student student = StudentDao.findById(findID);
    	//3.反馈数据给客户端
    	try {
    		OutputStream out = socket.getOutputStream();
    		ObjectOutputStream oos = new ObjectOutputStream(out);
    		oos.writeObject(student);
    		//4.释放资源
    		oos.close();
    		out.close();
    		socket.close();
    	} catch (IOException ie) {
    		ie.printStackTrace();
    	}
    }
    
  • 修改学生

    //修改学生
    public void updateStudent(String msg) {
    	//msg: [3]1,张三2,女,19
    	//1.封装数据
    	String msgStu = msg.substring(3);
    	String[] msgArr = msgStu.split(",");//[1,张三2,女,19]
    	Student s = new Student();
    	s.setId(Integer.parseInt(msgArr[0]));
    	s.setName(msgArr[1]);
    	s.setSex(msgArr[2]);
    	s.setAge(Integer.parseInt(msgArr[3]));
    
    	//2.调用StudentDao的增删改查方法
    	StudentDao.updateStudent(s);
    	//3.反馈数据给客户端
    	try {
    		OutputStream out = socket.getOutputStream();
    		out.write(1);
    		//4.释放资源
    		out.close();
    		socket.close();
    	} catch (IOException ie) {
    		ie.printStackTrace();
    	}
    }
    
  • 根据id删除

    //根据ID删除学生
    public void deleteStudent(String msg) {
    	//msg: [5]1
    	//1.封装数据
    	int deleteID = Integer.parseInt(msg.substring(3));
    	//2.调用StudentDao的增删改查方法
    	StudentDao.deleteById(deleteID);
    	//3.反馈数据给客户端
    	try {
    		OutputStream out = socket.getOutputStream();
    		out.write(1);
    		//4.释放资源
    		out.close();
    		socket.close();
    	} catch (IOException ie) {
    		ie.printStackTrace();
    	}
    }
    
总结
能够完成客户端添加功能
    [1]name,sex,age
能够完成客户端修改功能
    [3]id,name,sex,age
能够完成客户端删除功能
    [5]id
能够完成客户端获取功能
    [2]id
    [4]
能够完成服务端功能
    根据客户端的编码,完成不同的操作
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章