汪亚菲
(湖南工业职业技术学院,湖南长沙,410208)
在线投票系统应用较普遍,难点在于怎样避免重复投票和按照要求在指定日期及指定范围内进行投票的方法。
以asp和asp.net平台为例,在常见的设计中,有一些应用Request对象的Cookies集合实现投票,可以限制多次投票;也有一些应用Request对象的Serve Variabrles集合获取IP地址,通过查询IP地址的方法实现一个IP在指定日期内进行投票的方法。
本人在制作企业内部网站的评选月度优秀员工在线投票模块设计中,要求员工根据每月确定的候选人在线进行差额投票评选优秀员工,每月至多投票一次,且不能重复给同一人投票。上述利用Request对象的方法不能满足设计要求,本人在Microsoft Visual Studio2010平台下使用数据库和vc#等组件编程完成了设计,网站及评选优秀员工的模块工作状况良好。下面将介绍本模块的设计和制作方法。
本系统数据库名称为db_OA.mdf,事务日志文件是db_OA_log.ldf。与评优投票程序设计相关的数据表有系统用户表sysUser和评优表yx,具体内容如下:
1.1.1 系统用户表sys User
用于储存具有登录权用户的信息及每月评优投票日期。表结构定义如表1-1所示。
表1-1 系统用户表sys User
1.1.2 评优表yx
用于储存月评优秀员工侯选人信息。表结构定义如表1-2所示。
表1-2 评优表yx
本网站将数据库保存在网站的App_Data 文件夹中。其路径存储在配置文件Web.confi 的(connection Strings) 配置节中,内容如下:
数据库连接串定义在一个常量conStr 中,其值为:数据源来自本地Sql Server 2005 EXPRESS 数据库服务器;数据库保存在“|Data Directory|”,即默认的文件夹App_Data 中,数据库名为db_OA.MDF,登录用户和密码使用系统默认值,使用系统的“System.Data.Sql Client”命名空间。此时,数据库路径在网站已经确定。
网站采用三层开发架构。将网站需经常使用的代码段定义为方法,保存在公共类文件Base Class.cs文件中,文件存放在系统文件夹App_Code中。Base Class.cs文件中定义了三个方法,前一个方法是调用JavaScript的alert方法弹出window风格消息框;后二个方法涉及有关ADO.NET访问数据库的操作。
1.3.1 Message Box方法:
#region弹出消息框
public string Message Box(string Txt Message)
{
string str;
str=("");
return str;
}
#endregion
定义一个全局字符串型的方法MessageBox,调用时的形参名为字符串类型的Txt Message,字符串变量str中包含使用JavaScript的alert方法的代码,返回时在页面弹出window风格消息框。定义方法后,在程序中可随时调用。
1.3.2 Exec SELECTE方法:
定义一个全局整数型方法Exec SELECTE,形参名为字符串类型的sQuery String,用于执行sQuery String表示的sql查询语句,其中Command对象采用Execute Scalar方法,执行单一结果的查询。返回结果中第一行的第一列,将该列值转换为一个int类型的值,用以判断操作是否成功。
#region 执行SQL 语句,select 读取单一查询结果,操作成功返回>0
public int ExecSELECTE(string sQueryString)
{
Sql Connection con = new Sql Connection(Configuration Manager.
Connection Strings["con Str"] .Connection String); // 使用new 运算符用来创建连接对象con
con.Open();
SqlCommand cmd=new Sql Command(sQuery String,con);
int Num=(int)cmd.Execute Scalar();//Execute Scalar方法
con.Close();
return Num;//返回第一行的第一列值}
#endregion
1.3.3 GetDataSet方法:
定义一个全局系统数据集类型(System.Data.DataSet)的方法GetDataSet,调用时有两个字符串类型实参,一个是sQuery String,为输入的SQL语句;另一个是Table Name,为输入的数据表名称。返回值为数据集类型,即返回一个表。
#region:带二个参数执行SQL语句,操作成功返回一个表
public System.Data.Data Set Get DataSet(strings Query String,string Table Name)
{
Sql Connection con=new Sql Connection(Configuration Manager.
Connection Strings["con Str"] .Connection String);//使用new运算符用来创建连接对象con
con.Open();
Sql Data Adapter db Adapter=new Sql Data Adapter(s Query String,con);//创建并实例化适配器
Data Setdataset=new Data Set();//创建数据集对象dataset
db Adapter.Fill(dataset,Table Name);//Fill填充
con.Close();
return dataset;//操作成功返回一个表
}
#endregion
在网页中直接调用Base Class.cs文件所定义的方法的语句为:
Base Classbc=new Base Class();
语句使用new运算符创建类Base Class的对象bc,并调用Base Class()对该对象进行初始化,这表示对象bc可调用公共类文件BaseClass.cs中定义的所有方法。
网站页面为母版页结构,评选优秀员工前台页yx-vote.aspx为一个内容页,使用asp.net的数据列表控件DataList,页面截图如图2-1所示。在每个候选人的选项栏中设置一个投票按钮,投票时若点击某个候选人选项栏的投票按钮,则触发Data List控件的Edit Command事件,将执行后台设置的按钮点击事件代码。
图2-1 评优投票页yx-vote.aspx截图
2.2.1 启动评优投票页面时,系统打开用户表sys User,查询该用户是否有本月的投票记录,如果有则不能再投票,给出提示并返回上级页面。
2.2.2 若无本月的投票记录,则首先从评优表yx中取出已知的侯选人数,按一定的条件确定优秀员工的人数,确定的人数值也就是本轮投票的次数。将由员工进行差额不重复投票,评选出本月优秀员工。
2.2.3 单击“投票”按钮开始投票,系统首先判断本轮投票是否完成,如果已完成则退出;退出前应在sysUser表写入当前日期,因为每个用户每月至多只能进行一次评优投票。下次投票时若在sys User表查询出已有相同月份,表明已投过票,则不能再投票。这样可有效阻止多次登录重复投票。
2.2.4 如果本轮投票没有完成,投票时应确定不重复投票,鼠标点击某候选人所在行的主键值赋给变量str,将变量str与已存在数组中的str值比较,如果相同则表示重复投票给同一人,本票无效;直到投票选择侯选人的str与数组中的str值不相同时,将不重复投票的主键值str存入数组中。
2.2.5 从yx表中找到与数组元素str相同主键所在记录的票数字段qty值,赋给变量yxqty,给yxqty加1,然后更新表中投票值。每月投票后,yx表中票数较多的前几人即为评选出的本月优秀员工。
2.2.6 考虑到用户可能在投票中途非正常退出,所以投票开始时对数组和下标清零。
图2-2 评优投票模块设计流程图
评优投票模块设计流程图如图2-2所示,评优投票后台页yx-vote.aspx.cs代码如下:
2.3.1 启动时自动执行开始部分的初始化,然后执行Page_Load事件,从sysUser表中查询用户是否有本月投票记录,有则退出评优页面,返回到yx-vote Result.aspx页面显示投票结果。
staticinti=0;//定义数组下标值i,必须为静态变量,否则下标值i不递增
staticintyxcoun=0;//定义yxcoun为优秀员工人数
staticintrencoun;//定义rencoun为优秀员工候选人数
staticint[] homestr=newint[8] ;//定义数组
Base Classbc=new BaseClass();//对象bc拥有类文件Base Class.cs中定义的所有方法
protectedvoid Page_Load(objectsender,Event Argse)
{
Stringcheckyx=("select*fromsysUserwhere(userName='"+Session["login Name"] .ToString()+"'andDatepart(yyyy,monthyx)='"+DateTime.Now.Year+"'and Datepart(mm,monthyx)='"+Date Time.Now.Month+"')");
intci;
ci=bc.ExecSELECTE(checkyx);//启动时查询
if(ci>0)
{
Response.Write(bc.MessageBox("每个用户每月只能进行一次评优投票,你本月已投过票!"));
Response.Write("");//返回
}
2.3.2 在Page_Load事件中引入Page对象属性Page.IsPost Back,当网页第一次启动时,!Page.IsPost Back获取回传,即在第一次启动时执行以下初始化页面操作:
首先从yx表求和,Exec SELECTE方法返回结果中第一行的第一列值,即为求和得到的优秀员工的候选人数rencoun,然后按60%比例自动计算优秀员工人数yxcoun,考虑到上一个用户可能在投票中途非正常退出,所以投票开始时对数组和下标清零;
最后绑定DataList1控件并显示候选人名单,启动页面如图2-1所示:
if(!IsPost Back)
{
rencoun=bc.Exec SELECTE("select COUNT(*)fromyx");//返回结果中第一行的第一列,返回表的总行数即候选人数给变量
yxcoun=Convert.ToInt16(rencoun*0.60);//按60%比例选优秀员工!
Response.Write("每个用户每月只能进行一次评优投票。共有"+rencoun.ToString()+"名候选人,可投票选出"+yxcoun.To String()+"名优秀员工!"+"
");
for(intj=0;j<=yxcoun;j++)homestr[j] =0;//本次投票前数组清零
i=0;//投票前下标i清零(清零放在回传中,只执行一次;放在回传外则每次清零)
}
Data List1.Data Source=bc.Get DataSet("select*fromyx","yx");
Data List1.Data Key Field="id";
Data List1.Data Bind();//显示并绑定DataList1控件
}//Page_Load结束
2.4.1 单击"投票 "按钮,触发DataList控件的EditCommand事件。首先判断是否已投完票,投完票则在sysUser表写入当前日期,以便下次投票时查询是否已投过票,然后返回到yx-voteResult.aspx页面显示投票结果。投票时应确定不重复投票,投票结果存入yx表。代码如下:
protectedvoid DataList1_Edit Command1(objectsource,DataList Command EventArgse)
{
If(i>=yxcoun)//首先判断是否已投完票,其中i是投票次数,yxcoun是优秀员工数
{
for(intj=0;j<=yxcoun;j++)homestr[j] =0;//投票后数组清零
i=0;//i清0
Response.Write(bc.Message Box(“投票选出本月“+yxcoun+”名优秀员工!,已完成投票。谢谢!”));
bc.Exec SELECTE("updatesys Usersetmonthyx='"+DateTime.Now+"'whereuserName='"+Session["loginName"] .ToString()+"'");//在sysUser表写入当前日期
Response.Write("");//返回
}
//将投票的主键值(查sysUser表可知,即id号)预置到变量str中
intyxqty=0;//定义变量yxqty存放投票数
intstr=0;//定义变量str存放所选主键值
str=(int)Data List1.DataKeys[e.Item.Item Index] ;//鼠标点击行主键值,即id赋给str
//将不重复投票的主键值str(即id号)存入数组中
foreach (int aa in homestr)
{
if(aa!=0)Response.Write("已投票="+aa.ToString()+"号,");//显示投票信息,如图2-3所示
图2-3 投票正在进行中
图2-4 阻止重复投票
if(aa==str)//新选行主键str与己存在数组中主键aa比较,相等则为重复
{
Response.Write("["+aa.ToString()+"="+str.ToString()+",重复投票!] ");//如图2-4所示
Response.Write(bc.MessageBox1("不能重复投票给同一人。谢谢!!"));//如图2-4所示
str=-1; //重复选择的主键str无效,以-1为标记
i--; //下标减1
break;
}}
if(str!=-1)homestr[i] =str;//将不重复投票的主键str存入数组元素中
Response.Write("本次是第["+(i+1)+"] 票投票="+homestr[i] .ToString()+"号?
");//显示投票信息,如图2-3所示
2.4.2 最后利用Data Set行对象Data Row从yx表中找到主键值str所在行的投票数字段qty值并赋给变量yxqty,给yxqty加1,然后更新表中投票数字段值,步骤如下:
2.4.2.1 首先从表中找到原投票数qty并赋给变量yxqty。
Data Setds=bc.GetDataSet("select*fromyxwhereid='"+str+"'","yx");
Data Row[] row=ds.Tables[0] .Select();
foreach(Data Rowrsinrow)
{
yxqty=Convert.To Int16(rs["qty"] );//将yx表id=str行的qty值赋给变量yxqty
}
yxqty=yxqty+1;//原有票+1
2.4.2.2 将新的yxqty值写入yx表,更新表中投票数字段值
intbi;
bi=bc.ExecSELECTE("updateyxsetqty='"+yxqty+"'where id='"+str+"'");
if(bi>0)
{
Response.Write(bc.MessageBox1("第"+(i+1)+"票投票完成?"));//如图2-3所示
i++;//数组下标加1
}
}//DataList1_Edit Command1事件结束
后台制作完毕,投票结果存入yx表的qty字段。网站启动时,首页文件Default.aspx将在表yx中找出票数高的员工为优秀员工,然后在前台的左侧显示优秀员工的姓名、照片等资料。
[1] 明日科技.ASP.net2.0开发技术大全[M] .北京:人民邮电出版社,2008.
[2] 程不功.龙跃进.ASP.NET2.0动态网站开发教程[Z] .北京:清华大学出版社,2008.
[3] 祝红涛.SQL Server2008数据库应用简明教程[Z] .北京:清华大学出版社,2010.
[4] 沈大林.SQL Server2005案例教程[Z] .北京:中国铁道出版社,2010.
[5] 汪亚菲.网站建设实例教程[Z] .北京:清华大学出版社,2013.