This course is designed to be written by a sophomore in the Department of Software Engineering. Please correct me with my clumsy pen.
Development tools: Eclipse 2020-12, Microsoft SQL server 2012
Program language: Java
Introduction
Topic of topic selection: library management system
Background of topic selection:
(1) There are various books in the library, with a total of more than 10,000 volumes.
(2) Each book has title, book number (ISBN), one or more authors (translators), publisher, pricing and brief introduction.
(3) The library card records the borrower's name, unit, occupation, etc.
(4) Borrowing books with a book certificate, up to 8 books can be borrowed each time. The maximum loan period is 30 days.
Required functions:
(1) Basic operations such as entry, modification, and deletion of the basic information of books.
(2) Realize the function of borrowing books.
(3) Realize the book returning function.
(4) Realize the classification query and classification statistics of all purchased books.
(5) The number of existing books can be inquired by classification such as book title and author.
(6) It can automatically give prompt information for the overdue situation.
(7) There is a distinction between librarians and borrowers.
Significance of topic selection: It can deepen our understanding and application of theoretical knowledge of database system principles and program design. By designing actual database system topics, we can further familiarize ourselves with the operation technology of database management systems, improve hands-on ability, analyze practical projects and solve practical problems. The ability to learn basic database programming methods.
- Database Conceptual Model
1.1 Requirements Analysis
1.1.1 Librarian:
⑴ Inquire and modify personal information, such as contact number.
⑵It can perform operations such as querying, modifying, deleting, and classifying statistics on books.
(3) Query and modify the personal information of borrowers, including borrowing information, etc.
⑷Increase and delete borrowers
1.1.2 Borrowers:
⑴Inquire and modify personal information, such as contact number.
⑵ Inquire about book information and book status, that is, it is free in the book or has been borrowed.
(3) Borrowing and returning books.
⑷If the borrowed books have exceeded the time limit, you need to ask the administrator to help you return the books.
Up to 8 books can be borrowed each time. The maximum loan period is 30 days.
1.2 Conceptual structure design
1.2.1 Abstract entities
According to the analysis, the library management system mainly includes three entities: librarians, borrowers, and books.
Librarian: job number, name, gender, title, contact number
Borrower: library card number, name, gender, contact number, occupation, unit
Books: book number, title, author, publisher, price, content Profile, Status, Category
Data dictionary:
(6 tables)
Table name: Borrower
(the function of this table is: when the user logs in successfully, write the logged-in account and login time into the "operation account" table of the database, indicating that the user is currently using the The system is a certain user; for example, when a user modifies personal information, the system screens the latest login time to ensure that the user modifies his own information.)
1.2.2 Partial ER diagram (figure omitted)
Librarians and borrowers: one librarian can manage multiple borrowers, and one borrower can be managed by multiple librarians. So there is a many-to-many relationship between the two.
Borrowers and books: A borrower can borrow up to 8 books at a time, and a book can be loaned to different borrowers multiple times. So there is a many-to-many relationship between the two.
Librarians and books: A librarian can manage multiple books, and a book can be managed by multiple librarians. So there is a many-to-many relationship between the two.
1.2.3 The overall ER diagram (the diagram is omitted, it can be obtained by combining the partial ER diagrams)
2 Database logic model
2.1 Transform ER diagram into relational model
Librarian (employee number, name, gender, title, contact number) is the relational model corresponding to the librarian entity, where the employee number is the primary key of the librarian relationship.
The borrower (library card number, name, gender, contact number, occupation, unit) is the relationship model corresponding to the borrower entity, and the library card number is the primary key of the borrower relationship.
Books (book number, title, author, publisher, pricing, content introduction, status, classification) are the relationship schemas corresponding to book entities, and the book number is the primary key of the book relationship.
Borrowing books (library card number, book number, borrowing time, name, title) is the relationship model corresponding to the connection "borrowing book". Because borrowing books is a many-to-many relationship between librarians, borrowers and books, the main attribute of librarians, borrowers and books and the attribute "borrowing time" of the borrowing book itself constitute the borrowing The attributes of the book relationship model, where the combination of the library card number, book number, and borrowing time is the primary key of the book loan relationship.
Returning books (library card number, book number, borrowing time, returning time, name, book title) is the relationship model corresponding to the connection "returning books", because returning books is the relationship between librarians, borrowers and books Many-to-many relationship, so the main attribute of the librarian, borrower and book and the attribute "return time" of the borrowed book itself constitute the attributes of the return relationship model, in which the library card number, book number, borrowed book The combination of time and book return time is the primary key of the book return relationship.
3 Database physical design and implementation
3.1 Create database
3.2 Create and manage basic tables
3.2.1 Create basic tables
4 Program design
4.1 Create forms
4.1.1 Administrator
Book Query
Book Add
1. Operation failed - the ISBN is duplicated with the data already stored in the database, the status is not "loaned" or "free in book", and there is an empty item 2. The operation is successful -
not the above situation.
Book Editing
1. Input data Normal modification
2. Enter an illegal or non-existing ISBN—"There is no such book in the database"
3. Leave the ISBN blank—"Required"
Borrowing management
1. The administrator enters a legal and existing ISBN, and the operation is successful. Change the status of the book to free and move it to the book return record
2. Enter an illegal, non-existent or empty book number, the operation fails
3. Click "Book return record" to view the debit table
Reader query and edit
Delete
text If the account number entered in the box is valid, it means "operation succeeded", otherwise "operation failed"
Modify/Add
Modification
1. Fill in the legal library card number to execute smoothly; if you do not fill in the library card number or fill in the illegal library card number, the label prompt will be displayed
2. The gender is limited to "male" or "female". If an error occurs, the label prompt cannot be executed.
Add
1. The operation is successful - each item is filled in, and the gender is limited to "male" or "female".
2. The operation fails - there is an empty item or Gender is not limited, display "Each item is required"
Modify personal information
Change password
1. Enter legal passwords in all three password boxes - "modified successfully"
2. Enter illegal passwords in any of the three password boxes - "Input error, please check"
4.1.2借书者
book query
Book Borrowing
Operation successful
1. Enter the ISBN of "Free Book", and there are no more than 8 books in borrowing
Operation Failed
1. Enter a "loaned", non-existent or empty ISBN
2. The number of books in borrowing has reached 8
Book return
Operation successful
1. Enter the book number that is being borrowed, and the borrowing time is not more than 30 days.
Operation failure
1. Enter the book number that is not in use, illegal or empty
2. Enter the book number that is being borrowed, but the borrowing time has exceeded 30 days sky
Modify Personal Information
change Password
4.2连接数据库
package Testv1;
import java.sql.*;
public class Testv10 {
public static void main(String[] args) {
// TODO Auto-generated method stub
{
String driverName="com.microsoft.sqlserver.jdbc.SQLServerDriver";
String dbURL="jdbc:sqlserver://localhost:1433;DatabaseName=LibraryManagement";//个人数据库名称
String userName="sa"; //个人用户名
String userPwd="QWEASDZXC000000"; //个人密码
try
{
Class.forName(driverName);
System.out.println("加载驱动成功!");
}catch(Exception e){
e.printStackTrace();
System.out.println("加载驱动失败!");
}
try{
Connection dbConn=DriverManager.getConnection(dbURL,userName,userPwd);
System.out.println("连接数据库成功!");
}catch(Exception e)
{
e.printStackTrace();
System.out.print("SQL Server连接失败!");
}
}
Full demo video
https://www.bilibili.com/video/BV1C34y1H7NH?share_source=copy_web&vd_source=532622d453c2953506dbf894f3498777
Add link description: System use demo
book query
package Testv1;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.border.EmptyBorder;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.awt.event.ActionEvent;
import javax.swing.JComboBox;
import java.awt.event.ItemListener;
import java.awt.event.ItemEvent;
public class BookFind extends JFrame {
private JPanel contentPane;
private JTextField textField_2;
private JTextField textField_3;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
BookFind frame = new BookFind();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public BookFind() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 581, 391);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
textField_2 = new JTextField();
textField_2.setBounds(23, 7, 139, 21);
contentPane.add(textField_2);
textField_2.setColumns(10);
JButton btnNewButton_2 = new JButton("\u8FD4\u56DE");
btnNewButton_2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
setVisible(false);
}
});
btnNewButton_2.setBounds(462, 319, 93, 23);
contentPane.add(btnNewButton_2);
JLabel lblNewLabel = new JLabel("\u5171\u6709\uFF08\u672C\uFF09");
lblNewLabel.setBounds(23, 323, 70, 15);
contentPane.add(lblNewLabel);
textField_3 = new JTextField();
textField_3.setBounds(82, 320, 66, 21);
contentPane.add(textField_3);
textField_3.setColumns(10);
JPanel panel = new JPanel();
panel.setBounds(23, 38, 532, 271);
contentPane.add(panel);
JScrollPane scrollPane = new JScrollPane();
scrollPane.setBounds(10, 46, 300, 150);
panel.add(scrollPane);
String []Name = {
"书号", "书名", "作者", "出版社","定价","内容简介","状态","分类"};
Object [][] rowData = new Object [100][8];
JTable table = new JTable(rowData, Name);
table.setBounds(297, 179, -279, -124);
table.setRowHeight(30); //设置行高
table.getColumnModel().getColumn(0).setPreferredWidth(110); //第一列列宽
table.setPreferredScrollableViewportSize(new Dimension(500 ,300)); //设置滚动面板视口大小(超过该大小的行数据需要拖动滚动条)
scrollPane.setViewportView(table);
//把数据库表的内容显示到页面的表格
try {
String dbURL="jdbc:sqlserver://localhost:1433;DatabaseName=LibraryManagement";
String userName="sa";
String userPwd="QWEASDZXC000000";
Connection dbConn = null;
Statement state = null;
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
dbConn = DriverManager.getConnection(dbURL,userName,userPwd);
state = dbConn.createStatement();
String sql0 = "select * from 图书";
ResultSet rs = state.executeQuery(sql0);
Statement stateCnt = dbConn.createStatement();
String sql1 = "select count(*) from 图书";
ResultSet rs_1 = stateCnt.executeQuery(sql1);
while(rs_1.next()) {
textField_3.setText(rs_1.getString(1));
}//统计
int i = 0;
while(rs.next() && i<rowData.length) {
rowData[i][0] = rs.getString(1);
rowData[i][1] = rs.getString(2);
rowData[i][2] = rs.getString(3);
rowData[i][3] = rs.getString(4);
rowData[i][4] = rs.getString(5);
rowData[i][5] = rs.getString(6);
rowData[i][6] = rs.getString(7);
rowData[i][7] = rs.getString(8);
i++;
}
dbConn.close();
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (ClassNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
String []item = new String[] {
"书号","书名","作者","出版社","定价","内容简介","状态","分类"};
JComboBox comboBox = new JComboBox(item);
comboBox.addItemListener(new ItemListener() {
//不必要监听器
public void itemStateChanged(ItemEvent e) {
//textField_2.setText(e.getItem());
}
});
comboBox.setBounds(172, 6, 139, 23);
contentPane.add(comboBox);
JButton btnNewButton = new JButton("\u67E5\u8BE2");
btnNewButton.addActionListener(new ActionListener() {
//查询
public void actionPerformed(ActionEvent e) {
String str = new String(textField_2.getText()); //列元素
String str_1 = new String((String) comboBox.getSelectedItem());//列名
//从文本框下拉列表读取要查询的分类
try {
String dbURL="jdbc:sqlserver://localhost:1433;DatabaseName=LibraryManagement";
String userName="sa";
String userPwd="QWEASDZXC000000";
Connection dbConn = null;
Statement state = null;
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
dbConn = DriverManager.getConnection(dbURL,userName,userPwd);
state = dbConn.createStatement();
String sql0 = "select * from 图书 where "+str_1+"='"+str+"'" ;
ResultSet rs = state.executeQuery(sql0);
Statement stateCnt = dbConn.createStatement();
String sql1 = "select count(*) from 图书 where "+str_1+"='"+str+"'";
//填入列名, 列元素
ResultSet rs_1 = stateCnt.executeQuery(sql1);
int n = 0;
while(rs_1.next()) {
textField_3.setText(rs_1.getString(1));
n= Integer.parseInt(rs_1.getString(1));
}//统计数量
int i = 0;
while(rs.next() && i<rowData.length) {
//显示到表格中
rowData[i][0] = rs.getString(1);
rowData[i][1] = rs.getString(2);
rowData[i][2] = rs.getString(3);
rowData[i][3] = rs.getString(4);
rowData[i][4] = rs.getString(5);
rowData[i][5] = rs.getString(6);
rowData[i][6] = rs.getString(7);
rowData[i][7] = rs.getString(8);
i++;
}
while(i>=n && i<rowData.length) {
rowData[i][0] = null;
rowData[i][1] = null;
rowData[i][2] = null;
rowData[i][3] = null;
rowData[i][4] = null;
rowData[i][5] = null;
rowData[i][6] = null;
rowData[i][7] = null;
i++;
}
//刷新
TableModel tml = new DefaultTableModel(rowData,Name);
table.setModel(tml);
dbConn.close();
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (ClassNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
});
btnNewButton.setBounds(363, 6, 93, 23);
contentPane.add(btnNewButton);
JButton btnNewButton_1 = new JButton("\u53D6\u6D88");
btnNewButton_1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
String dbURL="jdbc:sqlserver://localhost:1433;DatabaseName=LibraryManagement";
String userName="sa";
String userPwd="QWEASDZXC000000";
Connection dbConn = null;
Statement state = null;
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
dbConn = DriverManager.getConnection(dbURL,userName,userPwd);
state = dbConn.createStatement();
String sql0 = "select * from 图书";
ResultSet rs = state.executeQuery(sql0);
Statement stateCnt = dbConn.createStatement();
String sql1 = "select count(*) from 图书";
ResultSet rs_1 = stateCnt.executeQuery(sql1);
while(rs_1.next()) {
textField_3.setText(rs_1.getString(1));
}//统计
int i = 0;
while(rs.next() && i<rowData.length) {
rowData[i][0] = rs.getString(1);
rowData[i][1] = rs.getString(2);
rowData[i][2] = rs.getString(3);
rowData[i][3] = rs.getString(4);
rowData[i][4] = rs.getString(5);
rowData[i][5] = rs.getString(6);
rowData[i][6] = rs.getString(7);
rowData[i][7] = rs.getString(8);
i++;
}
TableModel tml = new DefaultTableModel(rowData,Name);
table.setModel(tml);
dbConn.close();
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (ClassNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
});
btnNewButton_1.setBounds(462, 7, 93, 23);
contentPane.add(btnNewButton_1);
}
}
**4.1.2出现的问题及做法**
1.用户通过个人账号使用该系统,则用户的操作都绑定在该账号,如修改信息时,只能修改本账号信息而不能修改他人信息。
对此,我的做法是:在用户登录成功时,将登录的账号、登录时间写入数据库的“操作账号”表,表示当前正在使用该系统的是某某用户。
Admin login listener
JButton btnNewButton_1 = new JButton("\u767B\u5F55");
btnNewButton_1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String strP = new String(passwordField.getPassword());//密码
String strT = new String(textField.getText());//工号
String dbURL="jdbc:sqlserver://localhost:1433;DatabaseName=LibraryManagement";
String userName="sa";
String userPwd="QWEASDZXC000000";
Connection dbConn = null;
String sql = "select 工号,密码 from 图书管理员";
try {
Statement state = null;
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
dbConn = DriverManager.getConnection(dbURL,userName,userPwd);
state = dbConn.createStatement();
ResultSet rs = state.executeQuery(sql);
while (rs.next()) {
//System.out.println(rs.getString(1)+" "+rs.getString(2));
if(strT.equals(rs.getString(1)) && strP.equals(rs.getString(2))) {
Calendar c = Calendar.getInstance();
M_mainPage mPage = new M_mainPage();
mPage.setVisible(true);
lblNewLabel_3.setVisible(false);
PreparedStatement pst = null;
pst = dbConn.prepareStatement("insert into 操作账号 values(?,?)");//记录当前正在使用的用户
pst.setString(1, strT);//插入账号
pst.setTimestamp(2, new Timestamp(c.getTimeInMillis()));//插入时间戳
pst.addBatch();
pst.executeBatch();
lblNewLabel_3.setVisible(false);
}
else {
lblNewLabel_3.setVisible(true);
}
}
dbConn.close();
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (ClassNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
});
Modify personal information listener
JButton btnNewButton_1 = new JButton("\u4FDD\u5B58"); //保存修改信息
btnNewButton_1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String name = new String(textField.getText());//姓名
String work = new String(textField_2.getText());//职称
String sex = new String(textField_1.getText());//性别
String phone = new String(textField_3.getText());//电话号码
String num = new String();//工号
String dbURL="jdbc:sqlserver://localhost:1433;DatabaseName=LibraryManagement";
String userName="sa";
String userPwd="QWEASDZXC000000";
Connection dbConn = null;
try {
Statement state = null;
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
dbConn = DriverManager.getConnection(dbURL,userName,userPwd);
state = dbConn.createStatement();
//通过获得“操作账号”表的最后一行(最新一行)得到当前正在使用系统的用户是......
String sql0 = "select top 1* from 操作账号 order by 时间 desc";
ResultSet rs = state.executeQuery(sql0); //最后一行
String s1 = new String();
while(rs.next()) {
s1 = rs.getString(1);
}
num = s1;//工号
PreparedStatement pst = null;
//如果文本框内容不为空,则据此修改数据库表内容
if (name.equals("") == false) {
String sql = "update 图书管理员 set 姓名=? where 工号=?";
pst = dbConn.prepareStatement(sql);
pst.setString(1, name);
pst.setString(2, num);//工号是当前登录的账号,来自“操作账号”表
pst.executeUpdate();
}
if(sex.equals("") == false) {
//性别限定在“男”、“女”,出错时通过标签显示告知使用者
try {
String sql = "update 图书管理员 set 性别=? where 工号=?";
pst = dbConn.prepareStatement(sql);
pst.setString(1, sex);
pst.setString(2, num);
pst.executeUpdate();
lblNewLabel_4.setVisible(false);
}
catch(Exception ee) {
// TODO Auto-generated catch block
ee.printStackTrace();
lblNewLabel_4.setVisible(true);
lblNewLabel_5.setVisible(false);
}
}
if(work.equals("") == false) {
String sql = "update 图书管理员 set 职称=? where 工号=?";
pst = dbConn.prepareStatement(sql);
pst.setString(1, work);
pst.setString(2, num);
pst.executeUpdate();
}
if(phone.equals("") == false) {
String sql = "update 图书管理员 set 联系电话=? where 工号=?";
pst = dbConn.prepareStatement(sql);
pst.setString(1, phone);
pst.setString(2, num);
pst.executeUpdate();
}
Statement stateCnt = dbConn.createStatement();
String sql1 = "select * from 图书管理员 where 工号='"+num+"'";
ResultSet rs_1 = stateCnt.executeQuery(sql1);
int i = 0;
while(rs_1.next() && i<rowData.length) {
rowData[i][0] = rs_1.getString(1);
rowData[i][1] = rs_1.getString(2);
rowData[i][2] = rs_1.getString(3);
rowData[i][3] = rs_1.getString(4);
rowData[i][4] = rs_1.getString(5);
i++;
}
//刷新表格
TableModel tml = new DefaultTableModel(rowData,Name);
table.setModel(tml);
pst.close();
dbConn.close();
if(lblNewLabel_4.isVisible()) {
lblNewLabel_5.setVisible(false);
}else {
lblNewLabel_5.setVisible(true);
}
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (ClassNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
});
**4.3.0增删改查**
increase
PreparedStatement pst = null;
pst = dbConn.prepareStatement("insert into 图书 values(?,?,?,?,?,?,?,?)");
pst.setString(1, num);//书号
pst.setString(2, name);//书名
pst.setString(3, writer);//作者
pst.setString(4, publish);//出版社
pst.setString(5, price);//定价
pst.setString(6, intro);//内容简介
pst.setString(7, status);//状态
pst.setString(8, cla);//分类
pst.addBatch();
pst.executeBatch();
dbConn.close();
delete
String sql0 = "delete from 图书 where 书号= ?";
PreparedStatement pst = dbConn.prepareStatement(sql0);
pst.setString(1, num);//书号
pst.addBatch();
pst.executeBatch();
lblNewLabel_4.setVisible(true);//显示删除成功
dbConn.close();
change
PreparedStatement pst = null;
if (name.equals("") == false) {
String sql = "update 图书 set 书名=? where 书号=?";
pst = dbConn.prepareStatement(sql);
pst.setString(1, name);
pst.setString(2, num);
pst.executeUpdate();
}
check
try {
String dbURL="jdbc:sqlserver://localhost:1433;DatabaseName=LibraryManagement";
String userName="sa";
String userPwd="QWEASDZXC000000";
Connection dbConn = null;
Statement state = null;
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
dbConn = DriverManager.getConnection(dbURL,userName,userPwd);
//连接数据库
state = dbConn.createStatement();
String sql0 = "select * from 图书";
ResultSet rs = state.executeQuery(sql0);
Statement stateCnt = dbConn.createStatement();
String sql1 = "select count(*) from 图书";
ResultSet rs_1 = stateCnt.executeQuery(sql1);
while(rs_1.next()) {
textField_3.setText(rs_1.getString(1));
}//统计总数
int i = 0;//查询结果显示到Java表格组件
while(rs.next() && i<rowData.length) {
rowData[i][0] = rs.getString(1);
rowData[i][1] = rs.getString(2);
rowData[i][2] = rs.getString(3);
rowData[i][3] = rs.getString(4);
rowData[i][4] = rs.getString(5);
rowData[i][5] = rs.getString(6);
rowData[i][6] = rs.getString(7);
rowData[i][7] = rs.getString(8);
i++;
}
dbConn.close();
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (ClassNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
full source code
Files shared through Baidu Netdisk: Book Management System V1...
Link: https://pan.baidu.com/s/1NQczvOX0n0_g0pDx3iiIXw Extraction code: po48 Copy this content and open "Baidu Netdisk APP to get it"