ASP.NET教师电子化信息库的设计与实现

摘    要

系统在基于信息管理系统的设计与实现技术上,结合高校教师信息管理的特点,进行总体结构设计、数据库表的设计以及前台界面和后台功能的具体实现,最终完成了一个以ASP.NET 2.0技术和SQL Server2005为基础的基于B/S架构的教师电子化信息库的设计与实现。系统基本实现了用户角色管理、用户密码管理、用户名/口令认证、教师基本信息、教师详细信息、科研论文信息、获奖信息、证书信息的管理及报表统计等功能。

关键词档案管理;用户名/口令认证;ASP.NET;SQL Server 2005、

2.1  系统开发环境

本系统的开发环境是:

技术平台:微软的.NET框架2.0版本

开发语言:C#.NET

开发工具:Visual Studio .NET 2005版本

数据库:SQL Server 2005

2.2  Sql Server 2005

在数据库开发方面SQL Server 2005的10个最重要的特点:

1.XML技术

在使用本地网络和互联网的情况下,在不同应用软件之间散布数据的时候,可扩展标记语言(XML)是一个重要的标准。SQL Server 2005将会自身支持存储和查询可扩展标记语言文件。

2.ADO.NET 2.0版本

从对SQL类的新的支持,到多活动结果集(MARS),SQL Server 2005中的ADO.NET将推动数据集的存取和操纵,实现更大的可升级性和灵活性。

3.增强的安全性

SQL Server 2005中的新安全模式将用户和对象分开,提供fine-grainaccess存取、并允许对数据存取进行更大的控制。另外,所有系统表格将作为视图得到实施,对数据库系统对象进行了更大程度的控制。

4.Transact-SQL的增强性能

SQL Server 2005为开发可升级的数据库应用软件,提供了新的语言功能。这些增强的性能包括处理错误、递归查询功能、关系运算符PIVOT,APPLY,ROW_NUMBER和其他数据列排行功能,等等。

5.SQL服务中介

SQL服务中介将为大型、营业范围内的应用软件,提供一个分布式的、异步应用框架。

6.通告服务

通告服务使得业务可以建立丰富的通知应用软件,向任何设备,提供个人化的及时的信息,例如股市警报、新闻订阅、包裹递送警报、航空公司票价等。在SQL Server 2005中,通告服务和其他技术更加紧密地融合在了一起,这些技术包括分析服务、SQL Server Management Studio。

7.Web服务

使用SQL Server 2005,开发人员将能够在数据库层开发Web服务,将SQL Server当作一个超文本传输协议(HTTP)侦听器,并且为网络服务中心应用软件提供一个新型的数据存取功能。

8.报表服务

使用SQL Server 2005,报表服务可以提供报表控制,可以通过Visual Studio 2005发行。

9.全文搜索功能的增强

SQL Server 2005将支持丰富的全文应用软件。服务器的编目功能将得到增强,对编目的对象提供更大的灵活性。查询性能和可升级性将大幅得到改进,同时新的管理工具将为有关全文功能的运行,提供更深入的了解。

2.3  ASP.NET技术

ASP. NET所采用的技术大致有如下几种:

 ( 1 ) 服务器端控件。页面使用了新的服务器端控件,使页面的状态管理自动化,并减少所写代码的数量。ASP. NET页面有一个与VB表单类似的编程模型,提供了在服务器上执行组件的可能,而且这些组件可以生成页面的某一部分并返回给用户。

( 2 ) HTML服务器端控件。HTML服务器端控件可以用来在页面输出中生成HTML组件,并允许在运行中使用代码设置这些控件的属性。它们还允许检测由这些组件激发的事件,允许在服务器上执行适当的代码来响应这些事件。  

( 3 )页面控件。这些控件可以在服务器上执行,在页面输出中生成更复杂的HTML组件和对象。

( 4 )  Web服务。Web服务允许开发者创建不生成可视输出的类,但是为客户机提供了一些服务。例如,可以调用函数并在对请求的响应中返回特定的值。也就是说,Web服务使ASP.NET开发者可快速方便地创建自定义的商务服务对象。客户机可以使用HTTP-GET,  HTTP-POST或者HTTP-S OAP程序同步或异步地访问它们。

 ( 5 )错误处理、调试和跟踪特性有了很大的扩展和改进。原来的ASP在错误处理和调试方面一直不如其他开发环境(Visual Basic)。而现在,ASP. NET的每一个页面都有自己的“错误页面”,在执行的时候还可以显示页面代码中使用的值,从而提供了一个“跟踪”工具;另外调试也可在不同语言之间执行,从而使用户可以无间隙地从一种语言转到另一种语言进行单步调试。

 (6 )  ASP.NET提供了许多有用的组件,如SendMail组件、加密/解密组件、定义用户性能计数器的组件、读写事件日志的组件、网络访问组件数据访问组件等,这些类库可以使Web应用程序的编写变得更容易。

2.4  B/S模式

浏览器/服务器(Browser/Server)结构,简称 B/S 结构。是 对 C/S 结构的一种变化或者改进的结构。B/S模式无需像C/S模式那样在不同的客户机上安装不同的客户应用程序,而只需安装通用的浏览器软件。这样不但可以节省客户机的硬盘空间与内存,而且使安装过程更加简便、网络结构更加灵活。对于系统的开发者来说,他们无须再为不同级别的用户设计开发不同的客户应用程序了,只需把所有的功能都实现在Web服务器上,并就不同的功能为各个组别的用户设置权限就可以了。各个用户通过HTTP请求在权限范围内调用Web服务器上不同处理程序,从而完成对数据的查询或修改。现代企业面临着日新月异的竞争环境,对企业内部运作机制的更新与调整也变得逐渐频繁。相对于C/S,B/S的维护具有更大的灵活性。当形势变化时,它无须再为每一个现有的客户应用程序升级,而只需对Web服务器上的服务处理程序进行修订。这样不但可以提高公司的运作效率,还省去了维护时协调工作的不少麻烦。如果一个公司有上千台客户机,并且分布在不同的地点,那么便于维护将会显得更加重要。而采用B/S模式时,客户端只是一个简单易用的浏览器软件。无论是决策层还是操作层的人员都无需培训,就可以直接使用。再者,B/S模式适用于网上信息发布,扩展传统的MIS的功能。而这种新增的网上信息发布功能恰是现代企业所需的。这使得企业的大部分书面文件可以被电子文件取代,从而提高了企业的工作效率,使企业行政手续简化,节省人力物力。

4.1  登录模块

        4.1.1 分级用户

本系统采用了分级用户原则,所谓分级用户,就是将不同的用户划分成不同的等级,他们的权限不同,功能也各不相同。系统一共设定了三类用户,按权限级别由高到低依次是:系统管理员、院系领导、教师用户。用户首次登录时使用管理员为其分配的账号和密码进入系统,系统通过登录页面获取用户输入的用户名和口令,然后用户输入的用户名和口令与数据表中存放的数据(经过加密的数据)进行比较。以此判断用户的合法性和级别。

​​​​​​​4.1.2  口令的安全性

由于系统的入口是比较容易被黑客攻击的地方,为了提高系统的安全性,本系统采用了两种安全机制:HASH算法(加强口令安全性)与验证码(防止暴力破解口令)。

(1) HASH算法

为保证用户口令在网络传输过程当中的安全性和完整性,系统采用了MD5摘要算法,即Message-Digest Algorithm 5(信息-摘要算法),MD5的最大作用在于,将不同格式的大容量文件信息在用数字签名软件来签署私人密钥前"压缩"成一种保密的格式,关键之处在于——这种"压缩"是不可逆的。 

MD5将任意长度的“字节串”变换成一个128bit的大整数,并且它是一个不可逆的字符串变换算法,换句话说就是,即使你看到源程序和算法描述,也无法将一个MD5的值变换回原始的字符串,从数学原理上说,是因为原始的字符串有无穷多个,这有点像不存在反函数的数学函数。

MD5还广泛用于加密和解密技术上,在很多操作系统中,用户的密码是以MD5值(或类似的其它算法)的方式保存的, 用户Login的时候,系统是把用户输入的密码计算成MD5值,然后再去和系统中保存的MD5值进行比较,而系统并不“知道”用户的密码是什么。

(2)所谓验证码,就是一串随机产生的数字或符号。系统生成一幅包含验证码图片,图片里加上一些干扰象素(防止OCR),由用户肉眼识别其中的验证码信息,输入表单提交网站验证,验证成功后才能使用某项功能。

验证码能有效防止某一个用户用特定程序暴力破解方式进行不断的登录尝试,实际上验证码是现在很多网站通行的方式。虽然登录麻烦一点,但是这个功能还是很有必要,也很重要。且用户应尽量使用混杂了数字、字母、符号在内的6位以上密码,不要使用诸如1234之类的简单密码或者与用户名相同、类似的密码。以免个人账号被人盗用给自己带来不必要的麻烦。

​​​​​​​​​​​​​​4.1.3  服务器Web页面安全保护

由于基于Web的应用软件通常是由服务器端的相互关联的动态页面组成的,这种页面的访问一般是借助浏览器通过HTTP协议来实现的。如果对Web应用软件的页面不采取一定的保护措施,则任何人都可以通过得到页面的地址对页面进行访问,这样整个软件的安全性就非常差。为防止不合法的用户不经过软件的身份验证入口而直接访问软件的内部页面进入系统,必须对用户的页面访问进行合法性校验,本系统是采用如下措施来实现的:

(1)如果用户是通过软件系统的身份验证入口进入的合法用户,就在服务器端实施完成身份验证后为此用户建立会话标记,也即Session变量,它可以表明用户是通过系统校验的合法用户。

(2)当用户访问软件系统内的其它页面时,首先读取Session变量的值,如果是true的话,表明用户是经过身份验证的合法用户,否则为对此页面的非法请求。这种方法的安全性在于,如果用户未经身份验证系统进入软件,而是直接通过有关页面地址直接访问页面,则服务器一端就没有此次会话的状态变量,此页面的逻辑处理语句就得不到执行,浏览器仅仅显示“你没有权限访问此页”。由于采用服务器端会话机制进行控制,而且合法会话状态在用户关闭所有页面或超过一定的时间限制后,服务器自动进行清除,可以大大降低不法用户利用浏览器残余信息进入系统的可能性,具有较高的安全性。但是,上述解决方案依然存在漏洞,如果一个用户以合法身份进入他所拥有权限的子系统,此时一切检验都己经通过,Session变量的值,己经是true。这时如果该用户在浏览器中直接输入他所无权访问的页面地址,则该页中关于标记变量的值的检验都能通,也就是说该用户进入了他所无权访问的页面。为解决这个问题,可以在进行上述检查的同时对用户的类别进行检查,以判断用户所属的类别是否拥有对此页面的访问权限。

​​​​​​​4.1.4  具体实现

登录模块为本系统的唯一入口,所有用户均通过登录页面登录进入相应的模块,采用以上提及的安全技术,且本系统没有注册功能,而是由系统管理员为每一个校内的教师分配一个ID号和初始密码(888888),用户登录进入相应的模块后可以对自己的密码进行修改。

登录页面如图4-1所示:

图4-1  登录界面图

部分核心代码如下:                      

    protected void Page_Load(object sender, EventArgs e)

    {

        Session["ID"] = null;

        Session["NAME"] = null;

        Session["POWER"] = null;

    }

    protected void imgbtnEnter_Click(object sender, ImageClickEventArgs e)

    {

        string name = this.txtName.Text.Trim();

        string pwd = this.txtPwd.Text.Trim();

        string validate = this.txtValidate.Text.Trim();

        pwd = Encrypt.MD5(pwd);

        if ((Session["VALIDATECODE"] == null) ||                                (Session["VALIDATECODE"].ToString() != validate))

        {

            this.lblMessage.Text = "你输入的验证码不正确,请核对后重新输入!";

        }

        else if (!DataBaseAccess.User.NameExist(name))

        {

            this.lblMessage.Text = "你输入的用户名不存在,请核对后重新输入!";

        }

        else if (!DataBaseAccess.User.NameAndPwdExist(name, pwd))

        {

            this.lblMessage.Text = "你输入的密码不正确,请核对后重新输入!";

        }

        else

        {

            Session["ID"] =            DataBaseAccess.User.AllByName(name).Tables[0].Rows[0].ItemArray[0].ToString();

            Session["NAME"] =        DataBaseAccess.User.AllByName(name).Tables[0].Rows[0].ItemArray[1].ToString();

            Session["POWER"] =        DataBaseAccess.User.AllByName(name).Tables[0].Rows[0].ItemArray[3].ToString();

            if (Convert.ToInt32(Session["POWER"]) == 0)

            {

                Response.Redirect("~/Admin/Admin.aspx");

            }

            if ((Convert.ToInt32(Session["POWER"]) == 1) ||        (Convert.ToInt32(Session["POWER"]) == 2))

            {

                Response.Redirect("~/Common/Welcome.aspx");

            }

            else

            {

                Response.Redirect("~/Error.aspx");

            }

        }

}

4.2  系统管理员模块

本模块的主要功能为修改管理员个人密码和用户(包括:教师用户和院系领导用户)添加、修改、删除及用户密码重置(为888888)的管理。

其界面如图4-2所示:

图4-2  管理员界面图

管理员可以修改自己的密码,部分核心代码如下:

    protected void btnOK_Click(object sender, EventArgs e)

    {

        int id = Convert.ToInt32(Session["ID"]);

        string oldPwd = this.txtOldPwd.Text.Trim();

        string newPwd = this.txtNewPwd.Text.Trim();

        oldPwd = Encrypt.MD5(oldPwd);

        newPwd = Encrypt.MD5(newPwd);

        if (!DataBaseAccess.User.IDAndPwdExist(id, oldPwd))

        {

            Response.Write("<script>alert('原密码输入错误!')</script>");

        }

        else if (!DataBaseAccess.User.UpdateUserInfor(id, newPwd))

        {

            Response.Write("<script>alert('更改密码未成功!')</script>");

        }

        else

        {

            Response.Write("<script>alert('已成功更改密码!');window.window.location.href='Admin.aspx'</script>");

        }

}

管理员可以对用户信息进行管理,可以添加、修改、删除用户。核心代码如下:

// 提交添加用户

    protected void btnOK_Click(object sender, EventArgs e)

    {

        string name = this.txtName.Text.Trim();

        string pwd = Encrypt.MD5("888888"); ;

        int power = Convert.ToInt32(this.ddlUserPower.SelectedValue);

        string powertype = "";

        if(power == 0)

        {

            powertype = "系统管理员";

        }

        else if (power == 1)

        {

            powertype = "院系领导";

        }

        else

        {

            powertype = "教师用户";

        }

        if (DataBaseAccess.User.NameExist(name))

        {

            this.lblMessage.Text = "对不起,该用户名已经存在,请选择别的用户名!";

        }

        else

        {

            if (DataBaseAccess.User.AddUser(name, pwd, power, powertype))

            {

                this.lblMessage.Text = "添加用户成功!";

            }

            else

            {

                this.lblMessage.Text = "操作失败,请重新添加用户!";

            }

        }

 }

// 提交修改信息

    protected void btnOK_Click(object sender, EventArgs e)

    {

        bool result = false;

        int id =Convert.ToInt32(Request.QueryString["UID"].ToString());

        int power = Convert.ToInt32(this.ddlPower.SelectedValue);

        string powertype = "";

        if (power == 0)

        {

            powertype = "系统管理员";

        }

        else if (power == 1)

        {

            powertype = "院系领导";

        }

        else

        {

            powertype = "教师用户";

        }

        if (this.ddlPwd.SelectedIndex == 0)

        {

            result = DataBaseAccess.User.UpdateUserInfor(id, power,powertype);

        }

        else

        {

            string pwd = Encrypt.MD5("888888");

            result = DataBaseAccess.User.UpdateUserInfor(id, pwd, power,powertype);

        }

        if (result)

        {

            Response.Write("<script>alert('已成功更改用户信息!');window.window.location.href='UserManage.aspx'</script>");

        }

        else

        {

            Response.Write("<script>alert('更改用户信息未成功!')</script>");

    }

}

4.3  院系领导模块

院系领导具有教师用户所拥有的所有功能,即添加、修改、删除、查看个人信息及查看所有教师信息。除此以外,院系领导用户还具有报表统计功能,可以对所有教师的信息(包括:基本信息、详细信息、获奖信息、证书信息)进行统计并导出,以便查看、打印等。

报表统计功能是采用ASP.NET 2.0自带的ReportViewer控件实现,无须手动编写任何代码,只需按照向导,生成所需数据集,然后将数据集与ReportViewer关联即可实现。

报表统计界面如图4-3所示:

图4-3  报表统计界面

报表统计的主要代码如下:(由Visual Studio.Net 按照向导自动生成)

</Body>

  <PageHeader>

    <ReportItems>

      <Textbox Name="textbox1">

        <Left>21.75cm</Left>

        <Top>0.75cm</Top>

        <Width>4.5cm</Width>

        <Style>

          <TextAlign>Center</TextAlign>

          <PaddingLeft>2pt</PaddingLeft>

          <PaddingBottom>2pt</PaddingBottom>

          <FontFamily>宋体</FontFamily>

          <FontWeight>700</FontWeight>

          <FontSize>18pt</FontSize>

          <Color>#55761c</Color>

          <PaddingRight>2pt</PaddingRight>

          <PaddingTop>2pt</PaddingTop>

        </Style>

        <CanGrow>true</CanGrow>

        <Value>教师信息报表</Value>

      </Textbox>

    </ReportItems>

    <Height>1.75cm</Height>

    <PrintOnLastPage>true</PrintOnLastPage>

    <PrintOnFirstPage>true</PrintOnFirstPage>

  </PageHeader>

  <rd:ReportID>e64ee096-85a6-4568-a6e5-325fe15d0572</rd:ReportID>

  <LeftMargin>2.5cm</LeftMargin>

  <DataSets>

    <DataSet Name="ReportDataSet_TeacherInforDataTable">

       …………省略

    </DataSet>

  </DataSets>

  <Width>48.85581cm</Width>

  <InteractiveHeight>29.7cm</InteractiveHeight>

  <Language>zh-CN</Language>

  <PageFooter>

    <Height>0.75cm</Height>

    <PrintOnLastPage>true</PrintOnLastPage>

    <PrintOnFirstPage>true</PrintOnFirstPage>

  </PageFooter>

  <TopMargin>2.5cm</TopMargin>

  <PageHeight>29.7cm</PageHeight>

4.4  教师用户模块

教师用户的主要功能是对自己个人信息进行管理,即添加、修改、删除、查看个人信息,以及查看所有教师的信息。教师用户将自己的信息添加进数据库,以便院系领导用户进行统计。

修改、删除个人信息的界面如图4-4所示:

图4-4  教师用户界面图

添加、查看个人信息的部分核心代码如下:

/// 添加用户基本信息

    protected void btnOK_Click(object sender, EventArgs e)

    {

        int id = Convert.ToInt32(Session["ID"]);

        string name = this.txtName.Text.Trim();

        string sex = this.rblSex.SelectedValue.ToString().Trim() ;

        string birthdate = this.ddlYear.SelectedValue.ToString().Trim() + "-" +        this.ddlMonth.SelectedValue.ToString().Trim() + "-" +        this.ddlDay.SelectedValue.ToString().Trim();

        string address = this.txtAddress.Text.Trim();

        string nation = this.ddlNation.SelectedValue.ToString().Trim();

        if (!DataBaseAccess.Baseinfor.AddBaseInfor(id,name,sex,birthdate,address,nation))

        {

            Response.Write("<script>alert('添加信息未成功!')</script>");

        }

        else

        {

            Response.Write("<script>alert('已成功添加信息!');window.window.location.href='./AddInfor.aspx'</script>");

        }

}

/// 显示所选用户基本信息

private void DisplayInfor()

    {

        int uid = Convert.ToInt32(Session["ViewUID"]);

        if (!DataBaseAccess.Baseinfor.IDExist(uid))

        {

            Response.Write("<script>alert('该用户尚未添加基本信息!');</script>");

        }

        else

        {

            DataRow dr = DataBaseAccess.Baseinfor.AllByUID(uid).Tables[0].Rows[0];

            string name = dr.ItemArray[2].ToString().Trim();

            string sex = dr.ItemArray[3].ToString().Trim();

            string birthdate = dr.ItemArray[4].ToString().Trim();

            string birthaddress = dr.ItemArray[5].ToString().Trim();

            string nation = dr.ItemArray[6].ToString().Trim();

…………省略

            this.txtShow.Text = text;

        }

}

查看所有教师信息,将显示所有教师的姓名,是采用GridView+ SqlDataSource实现的。

查看所有教师信息的界面如图4-5所示:

图4-5查看所有教师信息界面图

显示教师姓名的核心代码如下:

    <asp:GridView ID="GridView1" runat="server" Width="570px" AllowPaging="True" AllowSorting="True" DataSourceID="SqlDataSource1" AutoGenerateColumns="False">

        <PagerSettings FirstPageImageUrl="~/Images/First.gif" LastPageImageUrl="~/Images/Last.gif"

            Mode="NextPreviousFirstLast" NextPageImageUrl="~/Images/Next.gif" PreviousPageImageUrl="~/Images/Prev.gif" />

        <PagerStyle Height="20px" />

        <Columns>

            <asp:BoundField DataField="BaseID" HeaderText="BaseID" InsertVisible="False" ReadOnly="True"

                SortExpression="BaseID" Visible="False" />

            <asp:BoundField DataField="UID" HeaderText="UID" SortExpression="UID" Visible="False" />

            <asp:BoundField DataField="Name" HeaderText="姓名" SortExpression="Name" />

            <asp:BoundField DataField="Sex" HeaderText="Sex" SortExpression="Sex" Visible="False" />

            <asp:BoundField DataField="BirthDate" HeaderText="BirthDate" SortExpression="BirthDate"

                Visible="False" />

            <asp:BoundField DataField="BirthAddress" HeaderText="BirthAddress" SortExpression="BirthAddress"

                Visible="False" />

            <asp:BoundField DataField="Nation" HeaderText="Nation" SortExpression="Nation" Visible="False" />

            <asp:HyperLinkField DataNavigateUrlFields="UID" DataNavigateUrlFormatString="ViewBaseInfor.aspx?UID={0}"

                HeaderText="查看信息" Text="查看信息" />

        </Columns>

    </asp:GridView>

4.5  数据库安全保护

数据库安全在软件系统的安全保护中十分重要,因为现在的信息系统软件中大多都有各种数据库系统做支撑,而且涉及到的数据量比较庞大,数据本身具有一定保密性或机密性。数据的丢失或泄密对所属机构的影响很大,所以在网络化环境中数据库以及相应数据的保护非常重要。

避免SQL注入攻击:

所谓SQI注入攻击就是攻击者把SQL命令插入到web表单的输入域或页面请求的查询字符串,欺骗服务器执行恶意的SQL命令。在某些表单中,用户输入的内容被直接用来构造(或者影响)动态SQL命令,或作为存储过程的输入参数。这类表单特别容易受到SQL注入攻击。

系统所有操作数据库的地方都统一采用Parameters来防SQL注入,核心代码如下:

        // 添加基本信息

        public static bool AddBaseInfor(int uid, string name, string sex, string birthdate, string address, string nation)

        {

            int Count = 0;

            string cmdText = "INSERT INTO [Teacher](UID , Name , Sex , BirthDate , BirthAddress , Nation) VALUES(@UID , @Name , @Sex , @BirthDate , @BirthAddress , @Nation)";

            SqlConnection con = new SqlConnection(connstr);

            SqlCommand cmd = new SqlCommand(cmdText, con);

            SqlParameter param1 = new SqlParameter("@UID", SqlDbType.Int);

            SqlParameter param2 = new SqlParameter("@Name", SqlDbType.NVarChar, 50);

            SqlParameter param3 = new SqlParameter("@Sex", SqlDbType.NVarChar, 10);

            SqlParameter param4 = new SqlParameter("@BirthDate", SqlDbType.NVarChar, 50);

            SqlParameter param5 = new SqlParameter("@BirthAddress", SqlDbType.NVarChar, 100);

            SqlParameter param6 = new SqlParameter("@Nation", SqlDbType.NVarChar, 50);

            param1.Value = uid;

            param2.Value = name;

            param3.Value = sex;

            param4.Value = birthdate;

            param5.Value = address;

            param6.Value = nation;

            cmd.Parameters.Add(param1);

            cmd.Parameters.Add(param2);

            cmd.Parameters.Add(param3);

            cmd.Parameters.Add(param4);

            cmd.Parameters.Add(param5);

            cmd.Parameters.Add(param6);

            try

            {

                con.Open();

                Count = cmd.ExecuteNonQuery();

            }

            catch (Exception ex)

            {

                //做错误处理

                throw new Exception(ex.Message);

            }

            finally

            {

                con.Close();

            }

            if (Count == 1)

            {

                return true;

            }

            else

            {

                return false;

            }

        }

  • 34
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值