首页 > 基础资料 博客日记

图书信息管理系统(JAVA版)连接MySQL数据库+GUI

2024-01-07 21:32:29基础资料围观226

本篇文章分享图书信息管理系统(JAVA版)连接MySQL数据库+GUI,对你有帮助的话记得收藏一下,看Java资料网收获更多编程知识

本次项目是实现的一个图书信息管理系统,功能算不上太多,但也比较齐全。

1.1 项目来源及研究背景

随着社会的发展,人们对知识的需求也在不断增长。书籍作为人们获取并增长知识的主要途径,使得书城,书店在人们的生活中占有了一定的位置。但是近几年,随着书量的不断增长,造成了图书挤压,管理不善等等的问题。这就直接影响了图书管理员对书城或者书店图书的管理带来一定的难度。这时就需要开发一套图书管理系统,通过该系统提高书城或者书店的管理效率,从而减少管理方面的工作流程和成本。

1.2 研究目的及意义

本次阶段性作业设计旨在加深对数据库系统,软件工程,程序设计语言的理论知识的理解和应用水平,同时在理论和实验教学基础上进一步巩固已学基本理论及应用知识并加以综合提高,并学会将知识应用于实际的方法,提高分析和解决问题的能力,增强动手能力,为毕业设计和以后工作打下必要的基础。

1.3 研究内容

本次课题主要研究的就是图书管理系统在管理员的使用下所能达到的效果,而图书信息管理系统的研究内容主要包括以下几个方面:

系统架构设计:研究图书信息管理系统的整体架构设计,包括系统的模块划分、数据流程、技术选型等。

数据库设计:研究图书信息管理系统的数据库设计,包括数据表结构、数据关系、数据存储等。

系统功能设计:研究图书信息管理系统的功能设计,包括图书的录入、查询、借阅、归还等功能,以及读者的注册、登录、预约、续借等功能。\

系统性能优化:研究图书信息管理系统的性能优化,包括系统的响应速度、并发处理能力、数据安全性等方面。

1.4 实施计划

图书信息管理系统的实施计划应该包括以下几个方面:

  1. 需求分析:对图书馆的管理需求进行分析,确定系统的功能和特点,以及实施的目标和范围。
  2. 系统设计:根据需求分析的结果,设计系统的架构、模块、界面等,确定系统的技术方案和实现方式。
  3. 系统开发:根据系统设计的方案,进行系统的编码、测试、调试等工作,确保系统的稳定性和可靠性。
  4. 系统部署:将开发好的系统部署到图书馆的服务器上,并进行系统的安装、配置、测试等工作,确保系统能够正常运行。
  5. 培训和推广:对图书馆的工作人员进行系统的培训和指导,使其能够熟练掌握系统的使用方法和技巧,同时进行系统的推广和宣传,提高读者的使用率和满意度。
  6. 系统维护:对系统进行定期的维护和更新,确保系统的稳定性和安全性,同时对系统的性能和功能进行优化和改进,以满足图书馆的不断发展和变化的需求

2.1 可行性分析

可行性研究的目的是在尽可能短的时间内,消耗尽可能少的资源来判断系统问题是否能够解决。它只能客观地分析问题的存在,给出分析是否该问题值得花费任何的资源去解决。下面从市场上、经济、技术和安全四方面对系统的可行性进行了分析和研究。

2.1.1 市场可行性

现在人们对阅读的积极性不断提升,对图书进行管理起到了重要的作用,该项目可满足人们的阅读需求,所以该系统在市场上具有可行性。

2.1.2 经济可行性

对该图书信息管理系统的开发、部署、维护等成本进行评估,包括硬件、软件、人力等方面,使得在经济上本系统对于运营者与使用者来说都并不具有很大负担,所以本系统在经济上具有可行性。

2.1.3 技术可行性

本系统基于Java、GUI、MySQL等技术开发,这些技术十分成熟且易于使用,并在市面上具有广泛的应用且具有成熟的标准与诸多接口与扩展,在硬件方面,本系统对于服务器硬件的需求并不高,普通的服务器已经远能够满足本系统的需求,所以在技术上本系统具有可行性。

2.1.4 安全可行性

对图书信息管理系统的法律合规性进行评估,具备账号密码登入界面,包括系统的数据安全、隐私保护等方面,确保系统的法律可行性,所以在安全上本系统上具有可行性。

2.2 功能需求

基于实际调研受众人群分析,系统应满足以下的功能:

(1)书库管理功能:管理员可以对书库内所有图书进行管理;

(2)信息查询功能:管理员可以对所需检索的图书进行查询;

(3)图书销售功能:管理员可以对用户所需的图书进行下单处理。

图书信息系统管理员是整个系统的管理员,考虑到系统并不复杂,所以并不对管理员进行更进一步的划分,管理员可以进行的行为有:

1.图书管理员可以对图书信息进行增加,删除,修改,查询等操作。

2.系统有自动结算的功能,输入图书ISBN码,自动回填相关信息,包含价格,折扣等等,输入金额,自动计算找零。

3.图书管理员可以在系统中设置图书的折扣信息,以及标价标准。

4.图书查询,订单查询等等支持根据ISBN码,书名,模糊查询等多种方式。

2.3 非功能性需求

(1)数据的一致性:包括数据的编码和语言、冗余数据、数据库采用第二或第三范式设计、相同数据在不同表中的一致性等,当数据信息发生变更时,能够在多处记录同时修改数据以保证数据的一致性。

(2)系统的安全性:这里的安全性考虑的比较全面,小到如何防止和处理管理员未登录就可进入到主界面或是在输入框输入特殊字符来获得开发者未曾预料的结果,大到如何预防外部黑客对系统的攻击。

(3)人机交互性:包括易操作性和界面的美观性,该图书信息管理系统使用的都是我们生活中习惯使用的操作方式,所以用户可以很容易上手,操作界面的设计考虑到色彩的搭配。

2.4 开发工具及相关技术

(1)IDEA

IDEA是成熟的JAVA开发IDE,具有高度的可扩展性,且被广泛应用于各个领域,IDEA功能强大,且各种贴近使用的快捷方式十分便利于开发。

(2)MySQL

MySQL是一个开源的关系数据库,其服务器快速、可靠,易于使用。同时因为其本身规模较小,易于学生进行开发。且具有大量MySQL软件可供使用。

(3)GUI界面

GUI是Graphical User Interface的缩写,中文翻译为图形用户界面。它是一种通过图形化方式来显示和操作计算机系统的用户界面。与传统的命令行界面相比,GUI更加直观、易用、美观,可以通过鼠标、键盘等多种方式来进行操作。GUI通常包括窗口、菜单、按钮、文本框、图标等元素,可以通过拖拽、点击等方式来进行操作。GUI的出现极大地方便了计算机用户的操作,使得计算机系统更加普及和易用。常见的GUI操作系统包括Windows、macOS、Linux等。

本章主要介绍了图书信息管理系统的需求分析,包括需求分析的概念、目的、方法和步骤,以及需求分析的结果。在需求分析的过程中,我们对图书信息管理系统的功能、性能、界面、安全等方面进行了详细的分析和描述,明确了系统的需求和特点,为后续的系统设计和开发提供了基础和依据。同时,我们还对系统的用户群体、使用场景、需求特点等方面进行了分析,以确保系统能够满足用户的需求和期望。通过本章的学习,我们了解了需求分析的重要性和方法,掌握了需求分析的基本技能和流程,为后续的系统设计和开发打下了坚实的基础。

本章通过需求分析将系统分为五个功能模块,分别为登录模块、书库管理模块、信息查询模块、图书销售模块,系统设计模块,基于需求完成了系统数据库的设计。

3.1 系统总体设计

通过对系统权限的分析,我们将系统分为书库管理模块,销售管理模块,信息查询模块,系统设置模块,数据全览模块共五个模块。

系统总体功能模块图如图3-1所示。

 

图3-1 系统整体功能模块图

3.2 系统功能模块划分

通过对系统总体的分析,划分了五个功能模块,分别是书库管理模块、销售管理模块、信息查询模块、系统设置模块和数据全览模块。

以下为分模块分析:

(1)书库管理:

新书入库

图书下架

图书信息修改

(2)销售管理:

销售图书

(3)信息查询:

库存:按书名查询、按ISBN查询

新书:按书名查询、按ISBN查询、按入库时间查询

售书:按书名查询、按ISBN查询、按售出时间查询

(4)系统:

修改密码

获得帮助

退出登录

返回桌面

(5)数据全览:

库存一览

新书一览

订单一览

3.3 数据库设计

3.3.1概念设计

本系统除管理员以外,主要有图书,书库,书店三个实体,三个实体相对独立,图书与书库之间具有管理、进书、浏览三种关系;图书与书店之间有查询、浏览、销售三种关系;书库与书店之间有供需、销售、浏览三种关系。

具体E-R图3-2、图3-3、图3-4所示:

图3-2 图书实体E-R图

 

图3-3 书库实体E-R图

 

图3-4 书店实体E-R图

3.3.2逻辑设计

本系统使用MySQL数据库,使用关系数据模型。数据库有图书,书库,书店三个实体以及管理员这一流程外实体,共有管理员表、库存表、进书表、图书销售订单表等4张表。

关系模式

管理员表(管理员编号,管理员账号,管理员密码)

库存表(ISBN码,书名,作者,数量,标价)

进书表(顺序码,ISBN码,书名,作者,进价,数量,进书时间)

图书销售订单表(顺序码,ISBN码,书名,售出数量,售价,折扣,应付,找零,收钱额,时间)

数据模型

1.管理员表:用来保存管理员的记录,如表3-1所示。

表3-1 管理员表(manager)

列名

中文含义

类型

宽度

允许空值

备注

ID

管理员ID

int

默认

not null

主键

user

管理员账号

var char

默认

not null

password

管理员密码

var char

默认

not null

2.库存表:用于保存书库中的图书信息,如表3-2所示。

表3-2库存表(book_stack)

列名

中文含义

类型

宽度

允许空值

备注

ISBN

ISBN码

var char

默认

not null

主键

bookname

书名

var char

默认

author

作者

var char

50

num

数量

int

11

markprice

标价

decimal

10

3.进书表:用于添加保存新书的信息,如表3-3所示。

表3-3进书表(new_book_in)

列名

中文含义

类型

宽度

允许空值

备注

ID

顺序码

int

11

not null

主键

ISBN

ISBN码

var char

默认

not null

bookname

书名

var char

默认

author

作者

var char

默认

price

进价

decimal

10

num

数量

int

11

time

进书时间

var char

30

4.图书销售订单表:用于保存购买图书的订单信息,如表3-4所示。

表3-4库存表(book_out)

列名

中文含义

类型

宽度

允许空值

备注

ID

顺序码

int

11

not null

主键

ISBN

ISBN码

var char

默认

bookname

书名

var char

20

out_num

售出数量

int

11

markprice

售价

decimal

10

zhekou

折扣

var char

默认

sholdpay

应付

var char

30

return

找零

var char

30

receive

收钱额

var char

30

time

进书时间

var char

30

4.1功能实现

(1)登录界面设计如图4-1所示。

 

图4-1 图书信息管理系统登录界面图

(2)书库管理模块界面如图4-2所示。

 

图4-2 图书管理模块界面图

(3)信息查询模块界面如图4-3所示。

 

图4-3 信息查询模块界面图

(4)图书销售模块界面如图4-4所示。

 

图4-4 图书销售模块界面图

(5)系统设置模块界面如图4-5所示。

 

图4-5 系统设置模块界面图

4.2代码展示

4.2.1 login

package com.lys.bms;

import java.awt.EventQueue;

import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.Document;

import com.lys.bms.frame.Jrame2;
import com.lys.bms.jdbc.ConnectionManager;
import com.lys.bms.model.Manager;


import java.awt.Toolkit;
import java.awt.Font;
import java.awt.Color;
import java.awt.SystemColor;
import java.awt.event.ActionListener;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.awt.event.ActionEvent;

public class Login extends JFrame {

	private JPanel contentPane;
	private JTextField jt_user;
	private JPasswordField jt_psw;
	public static Jrame2 jrame;
	/**
	 * @wbp.nonvisual
	 */
	private final JPanel panel_3 = new JPanel();

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

	/**
	 * Create the frame.
	 */
	public Login() {
		try {
			UIManager.setLookAndFeel(   UIManager.getSystemLookAndFeelClassName());
		} catch (Exception e) {
			// TODO: handle exception
		}
		setBackground(new Color(224, 255, 255));
		setIconImage(Toolkit.getDefaultToolkit().getImage(Login.class.getResource("/img/线性图书 (1).png")));
		setTitle("图书信息管理系统");
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(100, 100, 610, 380);
		contentPane = new JPanel();
		contentPane.setBackground(SystemColor.menu);
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		setContentPane(contentPane);
		contentPane.setLayout(null);
		
		JPanel panel = new JPanel();
		panel.setBackground(SystemColor.menu);
		panel.setBounds(10, 25, 576, 81);
		contentPane.add(panel);
		panel.setLayout(null);
		
		JLabel lblNewLabel = new JLabel("    Welcome to use    ");
		lblNewLabel.setIcon(new ImageIcon(Login.class.getResource("/img/welcome.png")));
		lblNewLabel.setBounds(73, 25, 435, 34);
		lblNewLabel.setForeground(new Color(0, 0, 0));
		lblNewLabel.setFont(new Font("宋体", Font.BOLD, 29));
		panel.add(lblNewLabel);
		
		JPanel panel_1 = new JPanel();
		panel_1.setBackground(SystemColor.menu);
		panel_1.setBounds(10, 116, 576, 60);
		contentPane.add(panel_1);
		panel_1.setLayout(null);
		
		JLabel lblNewLabel_1 = new JLabel("账号:");
		lblNewLabel_1.setBounds(96, 5, 110, 32);
		lblNewLabel_1.setIcon(new ImageIcon(Login.class.getResource("/img/账号 (1).png")));
		lblNewLabel_1.setFont(new Font("宋体", Font.BOLD, 20));
		panel_1.add(lblNewLabel_1);
		
		jt_user = new JTextField();
		jt_user.setBounds(199, 6, 281, 30);
		jt_user.setFont(new Font("宋体", Font.BOLD, 20));
		panel_1.add(jt_user);
		jt_user.setColumns(25);
		
		JLabel mess1 = new JLabel("");
		mess1.setFont(new Font("宋体", Font.PLAIN, 14));
		mess1.setForeground(Color.RED);
		mess1.setBounds(199, 38, 125, 24);
		panel_1.add(mess1);
		
		JPanel panel_1_1 = new JPanel();
		panel_1_1.setBackground(SystemColor.menu);
		panel_1_1.setBounds(10, 186, 576, 60);
		contentPane.add(panel_1_1);
		panel_1_1.setLayout(null);
		
		JLabel lblNewLabel_1_1 = new JLabel("密码:");
		lblNewLabel_1_1.setBounds(97, 10, 110, 32);
		lblNewLabel_1_1.setIcon(new ImageIcon(Login.class.getResource("/img/密码 (7).png")));
		lblNewLabel_1_1.setFont(new Font("宋体", Font.BOLD, 20));
		panel_1_1.add(lblNewLabel_1_1);
		
		jt_psw = new JPasswordField();
		jt_psw.setBounds(201, 5, 280, 32);
		jt_psw.setFont(new Font("宋体", Font.BOLD, 15));
		jt_psw.setColumns(25);
		panel_1_1.add(jt_psw);
		
		JLabel mess2 = new JLabel("");
		mess2.setForeground(Color.RED);
		mess2.setFont(new Font("宋体", Font.PLAIN, 14));
		mess2.setBounds(201, 36, 125, 24);
		panel_1_1.add(mess2);
		
		JPanel panel_2 = new JPanel();
		panel_2.setBackground(SystemColor.menu);
		panel_2.setBounds(10, 270, 576, 60);
		contentPane.add(panel_2);
		panel_2.setLayout(null);
		
		JButton jb_reset = new JButton("重置");
		jb_reset.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
//				重置输入框
				jt_user.setText("");
				jt_psw.setText("");
			}
		});
		jb_reset.setIcon(new ImageIcon(Login.class.getResource("/img/重置.png")));
		jb_reset.setFont(new Font("宋体", Font.BOLD, 17));
		jb_reset.setBounds(113, 10, 97, 23);
		panel_2.add(jb_reset);
		
		JButton jb_login = new JButton("登录");
		jb_login.setIcon(new ImageIcon(Login.class.getResource("/img/登录统计.png")));
		jb_login.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
//				获取账号和密码
				String userString=jt_user.getText();
				char[] a=jt_psw.getPassword();
				String pswString=String.valueOf(a);
//				查询是否匹配
				String sql="select * from manager where user=?";
				if (jt_user.getText().equals("")) {
					mess1.setText("请输入账号:");
				}else {
					try {
						ResultSet set= ConnectionManager.query(sql, new Object[] {userString});
						if(set.next()) {
//							找到用户
							String user=set.getString("user");
							String psw=set.getString("password");
						
							System.out.println(user+psw);
//							判断密码
							if (pswString.equals("")) {
								mess2.setText("请输入密码!");
							}else if (psw.equals(pswString)) {
//								登录成功
								System.out.println("登录成功!");
//								打开新窗口
							    jrame=new Jrame2(new Manager(userString, pswString));
//								关闭当前
								dispose();
								jrame.setVisible(true);
							}else {
								System.out.println("密码输入错误!");
								mess2.setText("密码输入错误!");
							}
						}else {
							System.out.println("账号不存在!");
							mess1.setText("该账号不存在!");
						}
					} catch (SQLException e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					}
				}
				
			}
		});
		
//		动态清零
		Document dt=jt_user.getDocument();
		dt.addDocumentListener(new DocumentListener() {
			
			@Override
			public void removeUpdate(DocumentEvent e) {
				// TODO Auto-generated method stub
				mess1.setText("");
				mess2.setText("");
				
			}
			
			@Override
			public void insertUpdate(DocumentEvent e) {
				// TODO Auto-generated method stub
				mess1.setText("");
				mess2.setText("");
			}
			
			@Override
			public void changedUpdate(DocumentEvent e) {
				// TODO Auto-generated method stub
				mess1.setText("");
				mess2.setText("");
			}
		});
		jb_login.setFont(new Font("宋体", Font.BOLD, 17));
		jb_login.setBounds(356, 10, 97, 23);
		panel_2.add(jb_login);
	}
}

4.2.2 jdbc

package com.lys.bms.jdbc;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.sql.*;
/**
 * 简述:
 *数据库管理类
 */
public class ConnectionManager {
    /**
     * 连接数据库的四大必需属性
     */
    private static final String driver = "com.mysql.cj.jdbc.Driver";
    private static final String url = "jdbc:mysql://localhost:3306/bm?useSSL=false&serverTimezone=Asia/Shanghai";
    private static final String user = "root";
    private static final String psd = "031617";
 
    /**
     * 静态块加载驱动
     */
    static {
        try {
            Class.forName(driver);
            System.out.println("加载驱动成功!");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            System.out.println("加载驱动失败!");
        }
    }
    /**
     * 返回一个连接对象
     * @return
     */
    public static Connection getConnection() {
        Connection connection = null;
        try {
            connection = DriverManager.getConnection(url, user, psd);
//            System.out.println("连接数据库ok");
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }
    /**
     * 通用查询方法,返回结果集
     * @param sql
     * @param objects
     * @return
     * @throws SQLException
     */
    public static ResultSet query(String sql, Object[] objects) throws SQLException {
        Connection conn = ConnectionManager.getConnection();
        PreparedStatement pst = conn.prepareStatement(sql,ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);
        ResultSet resultSet=null;
//        判断Object是否为空,为空直接执行sql语句
        if (objects == null) {
            resultSet = pst.executeQuery();
        } else {
            for (int i = 0; i < objects.length; i++) {
                pst.setObject(i+1,objects[i]);
            }
            resultSet = pst.executeQuery();
        }
        return resultSet;
    }
    /**
     * 通用增删改方法
     * @param sql
     * @param objects
     * @return
     * @throws SQLException
     */
    public static int Update(String sql,Object[] objects) throws SQLException {
        Connection conn = ConnectionManager.getConnection();
        PreparedStatement pst = conn.prepareStatement(sql,ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);
//      判断数组是否为空
        try {
            if (objects == null || objects.equals("")) {
                return pst.executeUpdate();
            } else {
                for (int i = 0; i < objects.length; i++) {
                    pst.setObject(i + 1, objects[i]);
                }
                return pst.executeUpdate();
            }
        } finally {
            closeall(conn, pst);
        }
 
    }
    /**
     * 返回结果集的二维数组形式,这个在JavaGUI里创建了表格要调用显示数据库的数据时可以用到
     * @param set
     * @return
     * @throws SQLException
     */
    public static Object[][] getSetArrays(ResultSet set) throws SQLException {
        Object[][] objects;
        
        set.last();
        int rowcount = set.getRow();
        ResultSetMetaData rsm = set.getMetaData();
        int colcount = rsm.getColumnCount();//获取列数
//      创建二维数组
        objects = new Object[rowcount][colcount+1];
        set.first();
        for (int i = 0; i < rowcount; i++) {
            objects[i][0]=i+1;//给每一行的第一列添加序号
            for (int j = 1; j < colcount+1; j++) {
                objects[i][j] = set.getObject(j);
            }
            set.next();
        }
        return objects;
    }
 
    /**
     * 关闭资源
     * @param resultSet
     * @param statement
     * @param connection
     */
    public static void closeAll(ResultSet resultSet, Statement statement, Connection connection) {
        try {
            if (resultSet != null) {
                resultSet.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try {
            if (statement != null) {
                statement.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
 
        try {
            if (connection != null && (!connection.isClosed())) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    public static void closeAll(ResultSet resultSet, PreparedStatement preparedStatement, Connection connection) {
        try {
            if (resultSet != null) {
                resultSet.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try {
            if (preparedStatement != null) {
                preparedStatement.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
 
        try {
            if (connection != null && (!connection.isClosed())) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    public static void closeall(Connection c, PreparedStatement p) throws SQLException {
        c.close();
        p.close();
    }
    
//    获取时间
    public static String gettime() {
    	Calendar calendar = Calendar.getInstance();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        return simpleDateFormat.format(calendar.getTime());
	}
    
    public static String getday() {
    	Calendar calendar = Calendar.getInstance();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日");
        return simpleDateFormat.format(calendar.getTime());
	}

    public static void main(String[] args) {
        System.out.println(getConnection());
    }
    
}
 

总体来说本次项目实践体验蛮不错的。


文章来源:https://blog.csdn.net/qq_66020148/article/details/131154618
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!

标签:

相关文章

本站推荐

标签云