第29章 B2C网上购物系统实战
本章视频教学录像:23分钟
以练促学,通过实际项目开发,巩固加深对.Net的掌握。本章通过开发一个完整的B2C网上购物系统,以掌握Web系统开发的流程。
本章要点(已掌握的在方框中打钩)
□ 开发背景
□ 需求及功能分析
□ 系统功能实现
□ 系统运行
□ 开发过程常见问题及解决
29.1 开发背景
本节视频教学录像:2分钟
我们已经系统地学习了.Net程序设计的基本概念、方法和一般的应用技巧,但是编程的目的是应用,而不是死记硬背,不会灵活使用,知识永远也无法转化成能力。本章通过建立一个较为完整的B2C网上购物系统,让大家全面地掌握.Net的基本知识,并熟练掌握Web网站系统开发的基本流程。
结合本系统,一个Web网站的开发流程通常需要经历以下几个步骤。
⑴知道为什么做,也就是编写程序的目的是什么。我们要开发的是具有网上购物功能的一个系统。
⑵要做哪些事情,做这些事情需要达到什么程度。网上购物,通常就是要完成顾客注册、商品录入与展示、购物车、订单查询与处理等功能。
⑶数据库的设计,这是建立在对项目需求明确、功能清晰基础上的,因为数据库的设计是项目的基石,必须牢固。本系统的数据库是用来存储信息数据的,鉴于本系统的重点不是数据库,故实现的方法略微简单。
⑷编写代码。在达到目的的前提下,兼顾代码的运行效率,也就是体现出代码的功能化、模块化等。
⑸运行系统,查漏补缺,总结经验和教训。
29.2 需求及功能分析
本节视频教学录像:6分钟
所谓磨刀不误砍柴工,在接到项目任务时,不能盲目地进行开发。在开发之前,要对项目的开发背景、客户的需求以及项目的可行性等进行分析,然后再根据分析的结果做出合理的项目规划,使项目能够按部就班地进行,不至于出现顾此失彼的情况。
提示
在实际应用中,需求不是非常明确的,需要与客户长期接洽才能达成共识。
29.2.1 需求分析
通常,网上购物系统的功能可以很复杂很强大,也可以很简单很明了,但是最主要的需求是必须满足的,比如顾客的注册,商品的查询、维护、销售,订单的查询与处理等。经过这样一个调查分析的过程,我们设计的B2C网上购物系统的需求才能明确。
下面,把B2C网上购物系统的主要需求一一列举出来。
⑴设计数据库,用来存储顾客、商品、购物车、订单等各种信息,这样顾客、商品、购物信息才不至于程序结束导致据丢失,并且能对各种数据信息进行有效管理,方便查询与存储操作。本次.NET系统的开发,将采用SQL Server作为数据库。
⑵顾客的注册。
⑶顾客、管理人员的登录。
⑷商品呈现,实现顾客对商品的浏览与查找。
⑸购物车,实现顾客可以方便地多次购物。
⑹商品管理,实现对商品信息的录入与管理功能。
⑺销售订单管理,实现对顾客的订单进行查询和处理。
这就是我们设计的系统的基本需求,在需求定义阶段,设计人员和需方人员应协调沟通,确认需求且彼此理解无误,形成书面文档,就可以进行下一步的工作了。
下面对系统进行功能分析。
29.2.2 总体功能设计
总体功能模块设计,是在需求的基础上,对系统的架构做一个总体的规划。开发一个项目,特别是复杂的项目,总体设计方案是由大家集思广益,多次商讨之后决定的。我们这样做,也是按照程序设计的指导思想进行的,即由上至下、逐步细化。
可以把本系统分为以下几大模块:人员信息管理、商品呈现、购物车、商品管理、订单管理。如图所示。
为了充分发挥.NET的特性,我们建立一个购物明细类OrderItem 和订单信息类OrderInfo,为购物信息的传递实现提供方便。类声明如下。
01 //购物明细项目类
02 public class OrderItem
03 {
04 public string ID; //商品编号
05 public float Price; //商品价格
06 public int Num; //商品数量
07 public float SumPrice; //小计
08 public OrderItem()
09 {
10 }
11 }
12 //订单信息类
13 public class OrderInfo
14 {
15 public string OrderNo; //订单编号
16 public DateTime OrderTime; //下单时间
17 public float TotalPrice; //订单总金额
18 public string BuyerName; //购货人姓名
19 public string BuyerPhone; //联系人电话
20 public string BuyerEmail; //Email地址
21 public string ReceiverName; //收货人姓名
22 public string ReceiverPhone; //联系人电话
23 public string ReceiverAddress; //收货人地址
24 public string ReceiverPostalcode; //邮政编码
25 public string State; //订单状态
注意
在这里声明类对象,目的是为了程序在传值的时候简单一些,表达方便,不一定都要把信息封装成类。
29.2.3 各功能模块设计
下面依次介绍人员信息管理、商品呈现、购物车、商品管理和订单管理5大模块的实现。
1. 人员信息管理
人员信息管理是用来实现B2C网上购物系统的顾客在线注册、顾客信息管理和管理员信息管理等3个功能。
人员信息管理模块的流程如图所示。
2. 商品呈现模块
商品呈现模块的功能是将系统中的商品在网站首页显示出来,提供多种供用户查询自己所需商品的方法。
流程如图所示。
提示
我们对按各种条件检索出来的商品,如种类、价格区间或名称,在展示商品信息时,可以将商品的图片信息显示出来,这样会给顾客呈现非常直观的效果。
3. 购物车模块
网上购物车模块实现顾客的购物过程,提供类似于超市中小购物车的功能,用来搜集顾客选中的商品,一块结账。并实现比商场的传统购物车更方便的功能:顾客可以多次购物,购物车会记录下每次顾客选中的商品,下次登录时可以继续购物,多次一块结账,而不需要像逛超市一样,每次要重新推一个空的购物车重新选购。
网上购物车模块流程如图所示。
4. 商品管理模块
商品管理模块的功能是录入要销售的商品信息,如名称、种类、价格、图片等信息,然后对录入的商品进行修改、删除等维护操作。
商品管理模块流程如图所示。
5. 订单管理模块
订单管理模块的功能是查询客户订单,核对订单信息,对订单进行处理。
订单管理模块流程如图所示。
29.3 系统功能实现
本节视频教学录像:7分钟
本节从以下几个方面详细讲解B2C网上购物系统是如何实现的,分为系统目录框架、顾客注册功能、人员信息管理、商品呈现、购物车、商品管理、订单管理等。
29.3.1 系统目录框架的搭建
本小节介绍系统目录框架的搭建。B2C系统和一般的Web网站略有不同,其权限管理和逻辑流程较一般网站更为复杂。系统框架的搭建也有别于网站的开发。下面具体介绍本系统搭建的流程。
1. 创建项目
系统搭建的第1步是创建一个站点。打开Visual Studio 2008,选择文件→新建→网站,在弹出的新建网站窗口中选择代码文件的存放路径和开发语言,系统将在选定的文件路径下写入Web开发所需的文件,并生成一个默认的Web网页“Default.aspx”,这样系统项目就建好了。
以后需要添加页面时,只需要在解决方案面板中,在网站需要添加页面的目录单击右键,在弹出的快捷菜单中选择【添加新项】菜单项,然后在弹出的【新建文件】对话框中选择需要添加的文件类型和文件即可。
2. 附加数据库
⑴在SQL Server 2008中,右击【对象资源管理器】中的【数据库】,在弹出的快捷菜单中选择【附加】菜单项。
⑵单击【添加】按钮,添加“随书光盘ch29\db_EShop.mdf”数据库文件。
⑶修改Web.config文件的<connectionStrings>标签对为以下代码。
01 <connectionStrings>
02 <add name="db_EShopConnectionString" connectionString="Data Source=,\sqlexpress;InitialCatalog=DB_ESHOP;User ID=sa;Password=123"/>
03 < /connectionStrings>
提示
“Data Source=.\sqlexpress”表示数据库在本地计算机中,开发完成后,可将该地址改为数据库的具体地址。“User ID”和“Password”可改为自己计算机中SQL Server 2008的登录账户名和密码。
3. 页面框架
接下来的工作就是决定系统页面的框架和页面的风格。系统采用如图所示的框架,依次为页头、菜单、导航、主功能区、页脚。因为除了登录和重设密码外,所有的页面布局基本一致,所以我们可采用模板页以保证系统具有统一的风格,从而提高代码的重用度。
模板页是在项目里选择【添加新项】,在弹出窗口中选择【添加母版页】,然后再进行排版布局。
29.3.2 顾客注册功能的实现
在Web项目上右击,选择【添加新项】,选择新建一个Web窗体,即可创建一个Web网页,我们把这个页面的文件命名为“AddUser”,用来实现注册用户的功能。可以看到,对于一个Web窗体, Visual Studio .Net 实际上创建了两个文件,一个是aspx前台页面文件,负责页面布局与控件的声明;一个是.cs后台程序文件,用来实现逻辑处理。在后面其他页面创建时也都采用这种方法。
顾客注册包括顾客的基本信息录入,以及确认提交,在前台页面提供了注册信息的输入,及非空等验证,单击【提交】按钮后,服务器端将会对提交的注册信息进行逻辑处理,合法信息将会保存到数据库中。后台程序单击【提交】按钮的代码如下(代码29-1.txt)。
01 protected void btnSubmit_Click(object sender,EventArgs e)
02 {
03 stringsql=@"insertinto[USER](ID,NAME,PASSWORD,PHONECODE,EMAILS,ADVANCEPAYMENT,ROLE) values (@ID,@NAME,@PASSWORD,@PHONECODE,@EMAILS,@ADVANCEPAYMENT,@ROLE)";
04 SqlParameter[]parameters= newSqlParameter[7];
05
06 SqlParameter id= new SqlParameter("@ID",SqlDbType.Char);
07 DateTime tempTime=DateTime.Now;
08 id.Value= tempTime.ToString("yyyyMMddhhmmss");
09 parameters[0]= id;
10 SqlParameter name= new SqlParameter("@NAME",SqlDbType.VarChar);
11 name.Value= this.txtUserName.Text.Trim();
12 parameters[1]= name;
13
14 SqlParameter password= new SqlParameter("@PASSWORD",SqlDbType.VarChar);
15 password.Value= this.txtPassword.Text.Trim();
16 parameters[2]=password;
17
18 SqlParameter phoneCode= new SqlParameter("@PHONECODE",SqlDbType.VarChar);
19 phoneCode.Value= this.txtPhone.Text.Trim();
20 parameters[3]=phoneCode;
21
22 SqlParameter email= new SqlParameter("@EMAILS",SqlDbType.VarChar);
23 email.Value= this.txtEmail.Text.Trim();
24 parameters[4]= email;
25
26 SqlParameter adven= new SqlParameter("@ADVANCEPAYMENT",SqlDbType.Float);
27 adven.Value= 0;
28 parameters[5]= adven;
29
30 SqlParameter role= new SqlParameter("@ROLE",SqlDbType.Bit);
31 role.Value= false;
32 parameters[6]= role;
33 try
34 {
35 int result=dbHelper.ExecuteNonQuery(sql,parameters);
36 if (result== 1)
37 {
38 Response.Write("<script type='text/javascript'> alert('添加成功 ');< /script>");
39
40 }
41 else
42 throw new Exception();
43 }
44 catch
45 {
46 Response.Write("<script type='text/javascript'> alert('用户名已存在,请修改用户名! ');< /script>");
47 }
48
49 }
此处代码中调用了公共类DataBaseHelper,其代码如下(代码29-2.txt)。
01 using System;
02 using System.Data;
03 using System.Data.SqlClient;
04 using System.Configuration;
05 public class DataBaseHelper
06 {
07 //声明数据连接对象和数据访问对象
08 private SqlConnection connection;
09 private SqlCommand command;
10 private SqlDataAdapter adapter;
11 public DataBaseHelper()
12 {
13 this.connection = new SqlConnection(ConfigurationManager.ConnectionStrings["Database ConnectionString"].ConnectionString); //取得连接字符处 ;
14 this.command= new SqlCommand();
15 this.command.Connection= this.connection;
16 this.adapter= new SqlDataAdapter(command);
17 }
18
19 public int ExecuteNonQuery(string sql)
20 {
21 return this.ExecuteNonQuery(sql, new SqlParameter[0]);
22 }
23
24 public int ExecuteNonQuery(string sql,SqlParameter[]param)
25 {
26 this.command.CommandText= sql;
27 this.command.Parameters.Clear();
28 for (int i= 0; i<param.Length; i++)
29 {
30 if(param[i] != null)
31 this.command.Parameters.Add(param[i]);
32 }
33 int result= 0;
34 try
35 {
36 this.connection.Open();
37 result= this.command.ExecuteNonQuery();
38 }
39 catch (Exception ex)
40 {
41 throw ex;
42 }
43 finally
44 {
45 this.connection.Close();
46 }
47 return result ;
48 }
49
50 public DataTable Select(string sql,SqlParameter[]param)
51 {
52 this.command.CommandText= sql;
53 this.command.Parameters.Clear();
54 for (int i= 0; i<param.Length; i++)
55 {
56 if(param[i] != null)
57 this.command.Parameters.Add(param[i]);
58 }
59 DataTable dtData= new DataTable();
60 //连接数据库取得数据 ,出现异常则向上级抛出;
61 try
62 {
63 this.adapter.Fill(dtData);
64 }
65 catch (Exception ex)
66 {
67 throw ex;
68 }
69 return dtData;
70 }
71
72 public DataTable Select(String sql)
73 {
74 return this.Select(sql, new SqlParameter[0]);
75 }
76
77 public static SqlParameter[]AddParameter(SqlParameter[]paramArray,SqlParameter param)
78 {
79 Array.Resize<SqlParameter>(refparamArray,paramArray.Length+ 1);
80 paramArray[paramArray.Length - 1]=param;
81 return paramArray;
82 }
83 }
以后各页面调用的DataBaseHelper类皆为此类,后面不再赘述。
29.3.3 人员信息的维护
对录入的顾客信息,管理人员可进行删除等操作,以将无效用户从系统中清除,所以只需在人员列表中将要删除的用户名称取出,然后提交数据库进行删除即可。操作较为简单,我们重点学习一下GridView的扩展用法,注意绑定列BoundField与命令列CommandField的使用。下面建立UserManage页面,如图所示。
前台页面使用了GridView列出用户,通过提交行删除事件,后台程序可对选中的行进行删除操作。后台.cs代码如下(代码29-3.txt)。
01 protected void Page_Load(object sender,EventArgs e)
02 {
03 if (!IsPostBack)
04 {
05 gvMemberBind();
06 }
07 }
08 public void gvMemberBind()
09 {
10 DataTable dt = new DataBaseHelper().Select("select * from [user] where role='0' order byname");
11 gvUser.DataSource=dt;
12 gvUser.DataBind();
13
14 }
15 protected void gvUser_PageIndexChanging(object sender,GridViewPageEventArgs e)
16 {
17 gvUser.PageIndex= e.NewPageIndex;
18 gvMemberBind();
19 }
20 protected void gvUser_RowDeleting(object sender,GridViewDeleteEventArgs e)
21 {
22 string name=gvUser.DataKeys[e.RowIndex].Value.ToString();
23 string sql="delete from [user]where name='"+name+"' ";
24 int i= new DataBaseHelper().ExecuteNonQuery(sql);
25 if (i== 1)
26 {
27 gvMemberBind();
28 }
29 }
29.3.4 商品呈现功能实现
我们采用DataList控件将查询得到的商品信息以直观的形式展示给顾客,这样可以方便商品图片的布局显示。大家要重点学习一下DataList与GridView有什么不同,在绑定数据时,DataList控件有什么灵活性。下面建立商品展示Default.aspx页面,如图所示。
在前面的布局中我们设置了检索条件,下面展示商品信息,以满足顾客的购物需求,后台代码主要是根据传入的检索条件,进行商品的查询、数据的绑定,在这里大家可以看一下,是如何与DataList控件进行数据绑定的。后台代码如下(代码29-4.txt)。
01 private void query()
02 {
03
04 string sql="select top 20 * from Goods where 1=1 ";
05 if (!IsPostBack)
06 {
07 if (Request["Class"] != null&&Request["Class"].ToString().Trim().Length> 0)
08 {
09 sql+= "and class like'%"+Server.HtmlDecode(Request["Class"].ToString().Trim())+ "%' ";
10 }
11 }
12 else if (ddlKind.SelectedIndex> 0)
13 {
14 sql+= " and class like'%='"+ddlKind.SelectedValue.Trim()+ "%' ";
15 }
16 if(txtName.Text.Trim().Length>0)
17 {
18 sql+= " and name like '%"+ txtName.Text.Trim()+ "%' ";
19 }
20 if (txtPriceMin.Text.Trim().Length> 0)
21 {
22 sql+= " and price>="+ txtPriceMin.Text.Trim();
23 }
24 if (txtPriceMax.Text.Trim().Length> 0)
25 {
26 sql+= " and price<="+ txtPriceMax.Text.Trim();
27 }
28 sql+= " order by class,name ";
29 DataTable dt= new DataBaseHelper().Select(sql);
30 dlResult.DataSource=dt;
31 dlResult.DataBind();
32 }
29.3.5 购物车功能实现
本模块将检索到的要采购的商品,通过单击【购买】按钮,实现将此商品信息收集在本人的购物车中。前台页面即为商品呈现页面,只是在呈现每个商品的信息时,加载了购买事件。在事件被激发时,后台程序通过绑定信息,分析是购买的哪个商品,将此人的购买信息存到数据库的购物车表中,并以购物车的形式展现给顾客。实现代码如下(代码29-5.txt)。
01 //当购买商品时,获取商品信息
02 public OrderItem GetSubGoodsInformation(DataListCommandEventArgs e,DataList DLName)
03 {
04 //获取购物车中的信息
05 OrderItem Goods= new OrderItem();
06 Goods.ID=DLName.DataKeys[e.Item.ItemIndex].ToString();
07 string GoodsInfo= e.CommandArgument.ToString();
08 Goods.Price= float.Parse(GoodsInfo);
09 return (Goods);
10 }
11 public void AddShopCartItem(DataListCommandEventArgs e,DataList DLName)
12 {
13 if (Session["USERNAME"] != null)
14 {
15 OrderItem Goods= null;
16 Goods=GetSubGoodsInformation(e,DLName);
17 if (Goods== null)
18 {
19 //显示错误信息
20 Response.Write("<script>alert('没有可用的数据 ');location='index.aspx';< /script>");
21 return;
22 }
23 else
24 {
25 //取得当前购物车有无此已购商品
26 string sql ="select * from ShopCart where GoodsID=@GoodsID and UserName=@ UserName";
27 SqlParameter[]parameters={ newSqlParameter("@GoodsID",SqlDbType.Char, 14),
28 new SqlParameter("@UserName",SqlDb Type.Char, 50)};
29 parameters[0].Value=Goods.ID;
30 parameters[1].Value=Session["USERNAME"].ToString().Trim();
31 int i=new DataBaseHelper().Select(sql,parameters).Rows.Count;
32 if(i>0 )
33 {
34 sql=@"updateShopCart
35 set Num=(Num+1),
36 SumPrice=(SumPrice+@Price)
37 where GoodsID=@GoodsID and UserName=@UserName";
38 }
39 else
40 {
41 sql=@"Insert into ShopCart(GoodsID,Num,SumPrice,UserName)
42 values(@GoodsID,1,@Price,@UserName)";
43
44 }
45 SqlParameter[]parameters1={ new SqlParameter("@GoodsID",SqlDbType.Char, 14),
46 new SqlParameter("@Price",SqlDbType.Float, 8),
47 new SqlParameter("@UserName",SqlDbType.Char, 50)};
48
49 parameters1[0].Value=Goods.ID;
50 parameters1[1].Value=Goods.Price;
51 parameters1[2].Value=Session["USERNAME"].ToString().Trim();
52
53 //执行
54 int s= new DataBaseHelper().ExecuteNonQuery(sql,parameters1);
55 if (s> 0)
56 {
57 GlobleClass.PopInfo(this.Page, "恭喜您,添加成功! ");
58 }
59 else
60 {
61 GlobleClass.PopInfo(this.Page, "操作不成功! ");
62 }
63 }
64 }
65 else
66 {
67 GlobleClass.PopInfo(this.Page, "请先登录,谢谢! ");
68 }
69 }
需要注意的是,在添加购物信息时,一定要注意判断用户是否已登录,如果未登录要给以提醒登录,因为只有登录了,才知道商品是卖给哪位用户。购物结束,我们要能查看购物车的采购信息,并进行结账。为此在主页建立“我的购物车”链接,建立购物车页面,新建一个窗口文件“UserDetail. aspx”。前台模板内设计如图所示。
后台读取绑定购物车的商品信息,并进行“结账”,即提交订单的操作。后台文件UserDetail.aspx. cs代码如下(代码29-6.txt)。
01 //<summary>
02 ///显示购物车中的信息
03 ///< /summary>
04 private void LoadShopCar()
05 {
06 string sql=@"select GoodsID,Name,Price,Num,SumPrice,UserName
07 from ShopCart S, Goods G where S.GoodsID=G.ID and UserName='" +Session["USERNAME"].ToString().Trim()+ "' ";
08 DataTable dt= new DataBaseHelper().Select(sql);
09 gvShopCart.DataSource=dt;
10 gvShopCart.DataBind();
11 }
12 ///<summary>
13 ///显示购物车中的商品合计金额
14 ///< /summary>
15 private void TotalDs()
16 {
17 string sql= "select Sum(SumPrice) from ShopCart "+
18 "where UserName='"+Session["USERNAME"].ToString().Trim()+ "' ";
19 DataTabledt= new DataBaseHelper().Select(sql);
20 if (dt.Rows.Count> 0)
21 {
22 lblTotal .Text=GlobleClass.VarStr(dt.Rows[0][0].ToString(), 2);
23 }
24 }
25 protected void gvShopCart_PageIndexChanging(object sender,GridViewPageEventArgs e)
26 {
27 gvShopCart.PageIndex= e.NewPageIndex;
28 LoadShopCar();
29 }
30 protected void gvShopCart_RowDeleting(object sender,GridViewDeleteEventArgs e)
31 {
32 string goodsID=gvShopCart.DataKeys[e.RowIndex]["GoodsID"].ToString();
33 string userName=gvShopCart.DataKeys[e.RowIndex]["UserName"].ToString();
34 string sql=@"delete from ShopCart
35 where UserName='"+userName+"' and GoodsID='"+goodsID+"' ";
36 int i= new DataBaseHelper().ExecuteNonQuery(sql);
37 LoadShopCar();
38 TotalDs();
39 }
40 protected void gvShopCart_RowCancelingEdit(object sender,GridViewCancelEditEventArgs e)
41 {
42 gvShopCart.EditIndex= -1;
43 LoadShopCar();
44 TotalDs();
45 }
46 protected void gvShopCart_RowUpdating(object sender,GridViewUpdateEventArgs e)
47 {
48 string goodsID=gvShopCart.DataKeys[e.RowIndex]["GoodsID"].ToString();
49 string userName=gvShopCart.DataKeys[e.RowIndex]["UserName"].ToString();
50 string num= ((TextBox)(gvShopCart.Rows[e.RowIndex].Cells[2].Controls[0])).Text.ToString();
51 if (GlobleClass.IsNumber(num)== true)
52 {
53 string sql=@"update ShopCart set Num="+ num+ ", "+
54 "SumPrice=("+ num+ "*(Select Price from Goods "+
55 "where ID='"+goodsID+ "')) "+
56 "where UserName='"+ userName+ "' and GoodsID='"+goodsID+ "'";
57 if (new DataBaseHelper().ExecuteNonQuery(sql)== 1)
58 {
59 gvShopCart.EditIndex= -1;
60 LoadShopCar();
61 TotalDs();
62 }
63 }
64 else
65 {
66 GlobleClass.PopInfo(this.Page, "请输入正确的物品数量! ");
67 }
68 }
69
70 protected void gvShopCart_RowEditing(object sender,GridViewEditEventArgs e)
71 {
72 gvShopCart.EditIndex= e.NewEditIndex;
73 LoadShopCar();
74 TotalDs();
75 }
< >
76 protected void btnReckoning_Click(object sender,EventArgs e)
77 {
78 Response.Redirect("CheckOut.aspx?Total="+lblTotal.Text.Trim());
79 }
单击“结账”按钮的事件btnReckoning_Click,将会把购物信息以及收件人的地址电话等信息传到后台,生成订单,由卖家对订单进行发货等处理。
29.3.6 商品信息管理功能实现
商品信息管理将实现新商品的录入,并支持商品图片的上传,以及对系统内已有商品的修改和删除。通过本小节用户应学会简单的添加上传附件的功能,将文件图片上传到服务器并加以保存。要对商品进行管理,有必要做一个商品的查询功能,以方便查找要修改或删除的商品,为此新建Product窗口,在这里实现对已有商品的查询显示功能,如图所示。
后台代码将按照前台设置的检索条件,进行数据库信息的查询,并将结果绑定到GridView控件上显示,同时进行商品的删除操作。后台代码如下(代码29-7.txt)。
01 ///<summary>
02 ///绑定商品的信息
03 ///< /summary>
04 public void gvBind()
05 {
06 string sql= "select * from Good swhere 1=1 ";
07 if (ddlKind.SelectedIndex> 0)
08 {
09 sql+= " and class='"+ddlKind.SelectedValue.Trim()+ "' ";
10 }
11 if (txtName.Text.Trim().Length> 0)
12 {
13 sql+= " and Name like '%"+ txtName.Text.Trim()+ "%' ";
14 }
15 sql+= " order by class,name ";
16 DataTable dt= new DataBaseHelper().Select(sql);
17
18 gvGoodsInfo.DataSource=dt;
19 gvGoodsInfo.DataBind();
20 }
21
22 protected void gvGoodsInfo_PageIndexChanging(object sender,GridViewPageEventArgs e)
23 {
24 gvGoodsInfo.PageIndex= e.NewPageIndex;
25 gvBind();
26 }
27
28 protected void gvGoodsInfo_RowDeleting(object sender,GridViewDeleteEventArgs e)
29 {
30 string ID=gvGoodsInfo.DataKeys[e.RowIndex].Value.ToString();
31 string sql= "delete fromgoodswhere id='"+ ID+ "' ";
32 int i= newDataBaseHelper().ExecuteNonQuery(sql);
33 gvBind();
34 }
35 protected void btnSearch_Click(object sender,EventArgs e)
36 {
37 gvBind();
38 }
39 protected void gvGoodsInfo_RowDataBound(object sender,GridViewRowEventArgs e)
40 {
41 if (e.Row.RowIndex>-1)
42 {
43 string id=gvGoodsInfo.DataKeys[e.Row.RowIndex]["ID"].ToString();
44 string myUrl= "EditProduct.aspx?ID="+ id;
45 ((HyperLink)e.Row.FindControl("hlDetail")).NavigateUrl = "javascript:OpenWindow('" +myUrl+ "')";
46
47 }
48 }
对于要修改或者新增加的商品,鉴于信息较多,大家要在这一小节中学会简单地添加附件功能。我们专门建立了一个商品信息窗口进行维护,商品信息维护窗口EditProduct如图所示。
值得注意的是,为了增加代码的可重用性,我们在此将新增商品和修改已有商品做在了一个页面中,那么在保存商品信息时,就一定要区别,方法是通过判断窗口有无传入商品的ID信息,来作为是保存修改还是新增商品,为此一定要做好页面传入参数的判断。本页面用了一个HiddenField控件hdID来保存传入商品的ID,HiddenField控件常用来保存存储页面不可见的信息。后台代码如下(代码29-8.txt)。
01 protected void Page_Load(object sender,EventArgs e)
02 {
03 GlobleClass.ExecBeforPageLoad(this.Page);
04 if (!IsPostBack)
05 {
06 if (Request["ID"] != null&&Request["ID"].ToString().Trim().Length> 0)
07 {
08 hdID.Value=Request["ID"].ToString().Trim();
09 GetGoodsInfo(hdID.Value);
10 }
11 }
12 }
13 ///<summary>
14 ///获取指定商品的信息,并将其显示在界面上
15 ///< /summary>
16 public void GetGoodsInfo(string id)
17 {
18 string sql= "SELECT * FROM GOODSWHERE ID='"+id+"' ";
19 DataTable dt= new DataBaseHelper().Select(sql);
20 if (dt.Rows.Count> 0)
21 {
22 txtName.Text=dt.Rows[0]["NAME"].ToString();
23 ddlKind.SelectedValue=dt.Rows[0]["CLASS"].ToString();
24 txtUnit.Text=dt.Rows[0]["UNIT"].ToString();
25 txtPrice.Text=GlobleClass.VarStr(dt.Rows[0]["Price"].ToString(), 2);
26 hdImageUrl.Value=dt.Rows[0]["ImageUrl"].ToString();
27 ImageMapPhoto.ImageUrl=dt.Rows[0]["ImageUrl"].ToString();
28 txtShortDesc.Text=dt.Rows[0]["Introduce"].ToString();
29 }
30 }
31 protected void btnUpdate_Click(object sender,EventArgs e)
32 {
33 if (txtName.Text== "" || txtUnit.Text== "" || txtPrice.Text== "" ||ddlKind.SelectedIndex==0)
34 {
35 GlobleClass.PopInfo(this.Page, "请输入必要的信息! ");
36 }
37 else if (GlobleClass.IsNumber(txtPrice.Text.Trim())== false)
38 {
39 GlobleClass.PopInfo("<script>alert('请输入正确价格(格式:1.00)! ");
40 }
41 else
42 {
43 string sql= "";
44 if (hdID.Value.Trim().Length> 0)
45 {
46 sql=@"update Goods
47 set Class=@Class,
48 Name=@Name,
49 Introduce=@Introduce,
50 Unit=@Unit,
51 ImageUrl=@ImageUrl,
52 Price=@Price
53 where ID=@ID";
54 }
55 else
56 {
57 sql=@"insert Goods
58 (Class,Name,Introduce,Unit,ImageUrl,Price,ID)
59 values(@Class,@Name,@Introduce,@Unit,@ImageUrl,@Price,@ID)";
60 }
61 SqlParameter[]Parameters= newSqlParameter[7];
62 SqlParameter ClassID= new SqlParameter("@Class",SqlDbType.VarChar, 50);
63 ClassID.Value=ddlKind.SelectedValue;
64 Parameters[0]=ClassID;
65 SqlParameter name= new SqlParameter("@Name",SqlDbType.VarChar, 50);
66 name.Value= txtName.Text.Trim();
67 Parameters[1]= name;
68 SqlParameter GoodsIntroduce= new SqlParameter("@Introduce",SqlDbType.NText, 16);
69 GoodsIntroduce.Value= txtShortDesc.Text.Trim();
70 Parameters[2]=GoodsIntroduce;
71 SqlParameter GoodsUnit= new SqlParameter("@Unit",SqlDbType.VarChar, 10);
72 GoodsUnit.Value= txtUnit.Text.Trim();
73 Parameters[3]=GoodsUnit;
74 SqlParameter GoodsUrl= new SqlParameter("@ImageUrl",SqlDbType.VarChar, 50);
75 GoodsUrl.Value= hdImageUrl.Value.Trim();
76 Parameters[4]=GoodsUrl;
77 SqlParameter MarketPrice= new SqlParameter("@Price",SqlDbType.Float, 8);
78 MarketPrice.Value= txtPrice.Text.Trim();
79 Parameters[5]=MarketPrice;
80 SqlParameter ID= new SqlParameter("@ID",SqlDbType.BigInt, 8);
81 if (hdID.Value.Trim().Length> 0)
82 {
83 ID.Value= hdID.Value;
84 }
85 else
86 {
87 ID.Value=System.DateTime.Now.ToString("yyyyMMddHHmm");
88 }
89 Parameters[6]= ID;
90 //执行
91 int i= new DataBaseHelper().ExecuteNonQuery(sql,Parameters);
92 if (i== 1)
93 {
94 GlobleClass.PopInfo(this.Page, "操作成功! ");
95 }
96 }
97 }
98 protected void UploadImage_OnClick(object sender,EventArgs e)
99 {
100 try
101 {
102 if (imageUpload.PostedFile.FileName== "")
103 {
104 GlobleClass.PopInfo(this.Page, "要上传的文件不允许为空! ");
105 return;
106 }
107 else
108 {
109 string filePath= imageUpload.PostedFile.FileName;
110 string filename= filePath.Substring(filePath.LastIndexOf("\\")+ 1);
111 string fileSn=System.DateTime.Now.ToString("yyyyMMddHHmmssfff");
112 string serverpath = Server.MapPath(@"~\Images\Goods\") + fileSn + filename.Substring(filename.LastIndexOf("."));
113 string relativepath =@"~\Images\Goods\" + fileSn + filename.Substring(filename.LastIndexOf("."));
119 imageUpload.PostedFile.SaveAs(serverpath);
115 hdImageUrl.Value= relativepath;
116 ImageMapPhoto.ImageUrl= relativepath;
117 }
118 }
119 catch (Exception error)
120 {
121 GlobleClass.PopInfo(this.Page,"处理发生错误!原因:"+ error.ToString());
122 }
123 }
124 protected void btnNew_Click(object sender,EventArgs e)
125 {
126 hdID.Value= "";
127 txtName.Text= "";
128 txtUnit.Text= "";
129 txtPrice.Text= "";
120 txtShortDesc.Text= "";
121 hdImageUrl.Value= "";
122 }
29.3.7 订单信息管理功能实现
订单信息管理,将实现对客户提交到系统内的已有订单进行查询和处理的功能,我们在本小节还要学习简单的票据打印功能。
程序要人性化设计,查询订单要符合实际业务需要,这就要提供按客户查询和按订单信息查询等多种检索条件,以方便查找需要处理的订单。为此新建了一个OrderList窗口,在这里实现对已有订单的查询,显示各个订单当前处理状态,如图所示。
在这里我们仍然使用了GridView作为数据显示的控件,对于规整的二维表格式数据,GridView确实是一个很好的控件。下面看一下后台代码,是如何查询订单数据,并绑定到GridView控件显示的。很简单,代码如下(代码29-9.txt)。
01 //绑定总金额
02 public string GetVarTP(string strTotalPrice)
03 {
04 return GlobleClass.VarStr(strTotalPrice, 2);
05 }
06 ///<summary>
07 ///获取符合条件的订单信息
08 ///< /summary>
09 public void pageBind()
10 {
11 string sql= "select * from [Order]where 1=1 ";
12
13 if (txtKeyword.Text.Trim().Length> 0)
14 {
15 if (ddlKeyType.SelectedIndex== 0)
16 {
17 sql+= " and ORDERID='"+ txtKeyword.Text.Trim()+ "' ";
18 }
19 else if (ddlKeyType.SelectedIndex== 1)
20 {
21 sql+= " andUserName='"+ txtKeyword.Text.Trim()+ "' ";
22 }
23 }
24 if (ddlState.SelectedIndex> 0)
25 {
26 sql+= " and state='"+ddlState.SelectedValue.Trim()+ "' ";
27 }
28 sql+= " order by orderdate desc ";
29 DataTable dt= new DataBaseHelper().Select(sql);
30
31 gvOrderList.DataSource=dt;
32 gvOrderList.DataBind();
33 }
34
35 protected void gvOrderList_PageIndexChanging(object sender,GridViewPageEventArgs e)
36 {
37 gvOrderList.PageIndex= e.NewPageIndex;
38 pageBind();
39 }
40 protected void btnSearch_Click(object sender,EventArgs e)
41 {
42 pageBind();
43 }
44 protected void gvOrderList_RowDeleting(object sender,GridViewDeleteEventArgs e)
45 {
46 string orderID=gvOrderList.DataKeys[e.RowIndex].Value.ToString();
47 string sql= "delete * from [Order]where ORDERID='"+ txtKeyword.Text.Trim()+ "' ";
48 int i= new DataBaseHelper().ExecuteNonQuery(sql);
49 pageBind();
50
51 }
到这一步,我们通过检索找到了要处理的订单,如何处理呢?首先要能看到订单的详细信息,比如订单里都订购了哪些商品,数量和价格是多少。为此新建一个订单明细窗口OrderModify,在这个窗口中将显示上一步检索到的订单的详细信息,并能够对订单进行处理,即修改订单的执行状态,如图所示。
前台页面为了打印时页面美观,进行了平铺式的布局,通过调用Windows的打印程序,实现了本小节要学习的打印功能,页面后台代码如下(代码29-10.txt)。
01 public OrderInfo order= new OrderInfo();
02 protected void Page_Load(object sender,EventArgs e)
03 {
04 if (!IsPostBack)
05 {
06 order=GetOrderInformation();
07 OrderItemBind();
08 }
09 }
10
11 private void OrderItemBind()
12 {
13 string sql=@"select GoodsID,Name,Price,Num,SumPrice,UserName
14 from OrderItem S,GoodsG
15 where S.GoodsID=G.ID and s.orderid='"+Request["OrderID"].ToString().Trim()+"' ";
16 DataTable dt= new DataBaseHelper().Select(sql);
17 rptOrderItems.DataSource=dt;
18 rptOrderItems.DataBind();
19 }
20 ///<summary>
21 ///获取指定订单信息
22 ///< /summary>
23 ///<returns>返回OrderInfo类的实例对像< /returns>
24 public OrderInfo GetOrderInformation()
25 {
26 OrderInfo order= new OrderInfo();
27 //获取订单基本信息
28 DataTable dt = new DataBaseHelper().Select( "select * from [Order]where ORDERID='" +Request["OrderID"].ToString().Trim()+ "' ");
29 if(dt.Rows.Count>0)
30 {
31 DataRow dr=dt.Rows[0];
32 order.OrderNo=dr["ORDERID"].ToString();
33 order.OrderTime=Convert.ToDateTime(dr["orderdate"].ToString());
34 order.TotalPrice= float.Parse(dr["TotalPrice"].ToString());
35 order.ReceiverName=dr["ReceiverName"].ToString();
36 order.ReceiverPhone=dr["ReceiverPhone"].ToString();
37 order.ReceiverPostalcode=dr["ReceiverPostCode"].ToString();
38 order.ReceiverAddress=dr["ReceiverAddress"].ToString();
39 order.State=dr["State"].ToString();
40 lblState.Text=dr["State"].ToString();
41 //ddlState.SelectedValue=dr["State"].ToString();
42 }
43 //获取订单购买人信息
44 DataTable dtBuyer= new DataBaseHelper().Select("select [User].* from [Order] INNER JOIN "+
45 "[USER] ON [Order].UserName = [USER].Name where [Order].ORDERID='" +Request["OrderID"].ToString().Trim()+ "' ");
46 if (dt.Rows.Count> 0)
47 {
48 DataRow drBuyer=dtBuyer.Rows[0];
49 order.BuyerEmail=drBuyer["Emails"].ToString();
50 order.BuyerName=drBuyer["Name"].ToString();
51 order.BuyerPhone=drBuyer["Phonecode"].ToString();
52 }
53 return (order);
54 }
55
56 protected void btnSave_Click(object sender,EventArgs e)
57 {
58 if(ddlState.SelectedIndex>0)
59 {
60 string sql = "update [order] set state='" + ddlState.SelectedValue.Trim() + "' where OrderID='"+Request["OrderID"].ToString().Trim()+ "' ";
61 int i= new DataBaseHelper().ExecuteNonQuery(sql);
62 if (i== 1)
63 {
64 GlobleClass.PopInfo(this.Page, "修改成功,订单状态变更为:"+ddlState.SelectedValue.Trim());
65 order=GetOrderInformation();
66 }
67 }
68 }
在本次窗口代码中,大家有没有发现在绑定显示数据时有什么不同?除了直接将后台变量的值直接输出到前台显示,更关键的是不是多了一个对象order ?这就是我们在项目开发初期介绍的OrderInfo类的对象实例。在这个页面我们对它进行了初始化,赋值和前台输出绑定,希望大家仔细研究,对将某些事物的基本信息封装的好处多加体会。
到本节为止,我们已经将B2C网上购物系统从顾客注册与登录、商品呈现、购物车、商品管理,一直到订单管理等最基本的流程实现了。在项目实现的过程中,用到了最常用的Web服务器控件的用法,相信大家对GridView显示二维表数据、在DataList里定义Tale表灵活展示数据、图片附件上传至服务器、简单打印订单等功能都有了一定的了解,更重要的是对.Net进行Web系统开发的流程有了一个完整的认识。
29.4 系统运行
本节视频教学录像:6分钟
系统设计开发完成后,现在就将商品展示页Default页设为起始页,来看看系统的运行效果。按【F5】键调试运行,会在浏览器中打开系统的默认页面,本系统默认为登录页面。
29.4.1 普通用户登录
⑴在左侧登录窗口中单击【注册】按钮,填写注册信息(如用户名为123456,密码为123456),完成后单击【提交】按钮,提示“添加成功”。
⑵使用注册的信息登录,即可登录网站,可以购买本站中的商品。
⑶单击商品的【详细】链接可查看到商品明细信息。
⑷单击商品的【购买】链接,即可提示“恭喜你,添加成功!”,购买完成,单击【我的购物车】,即可查看所购买的商品,并且可以结账,结账后会生成订单,购物车内的物品自动清空。
29.4.2 管理员登录
⑴使用管理员的用户名“admin”、密码“000”登录本站,系统会自动转到网站管理的界面。
⑵在管理界面中,管理员可以实现订单管理、商品管理以及人员管理等。
29.5 开发过程中的常见问题及解决方式
本节视频教学录像:2分钟
本章主要通过一个不太复杂的综合范例——B2C网上购物系统的开发,具体讲解了如果开发一个.NET Web网站,包括开发的流程和一些开发技巧。
如果你是初次开发这样的综合性程序,在开发过程中,你肯定会遇到这样或者那样的问题,不要急于求成,要按照软件设计的原理一步步完成。
⑴明确系统的需求,做到有的放矢。
⑵讨论、思考系统的总体框架,在此基础上完成各部门功能模块的框架。
⑶建立合理高效的数据库,尽可能做到满足现有功能,还能进行下一步扩展的需求。
⑷编写代码,运行调试,逐渐完善。
⑸总结开发过程当中遇到的问题和解决的方法,为以后的编程提供宝贵的经验。
注意
更改数据库地址、名称或SQL Server 2008的登录名和密码后,对Web.config文件的<connectionStrings>标签对中的连接字符串,需改成新数据库的地址名称及账号和密码。