摘 要
随着计算机及其网络在世界范围内的广泛应用,计算机的管理系统正在以难以置信的速度渗透到各个工作的领域和人们的日常生活。在传统进行管理系统方面的应用局限于人工的记录,不仅麻烦有时还出现错误,丢失重要的文件和信息等等。计算机管理系统的出现不仅解决了人工方面的错误,而且还大大的提高了工作效率,适应了现代化生活的速度快,效率高的特点,因此受到了广泛的欢迎。
在每年的每一所学校中,学生的收费信息管理是一项学校的重点工作,大量的学生收费信息管理的数目统计是很庞大的,由此,学校对学生的收费管理很可能出现混乱,一个完善的学生收费管理系统无论是对学生还是对学校的管理员来说是必不可少的,这也是做好各项工作的基础。“学校收费管理系统”是在解决目前大多数学校对学生收费管理工作方面所存在的种种问题的软件管理系统。“学校收费管理系统”能对学生交费情况的需求情况这一环节的信息进行及时,准确的反映,这将会大大的提高学校的工作效率,对学校信息的管理有一定的帮助。
关键字:SQL Server数据库,Visual C++开发语言,ADO,管理信息系统
ABSTRACT
With computers and networks in a wide range of applications around the world, the computer management system is the incredible speed of penetration into the various work areas and people's daily lives. In traditional management systems to limit the application of artificial records, not only troublesome and sometimes wrong, the loss of important documents and information and so on. The emergence of computer management system not only solved the problem of artificial errors, but also greatly enhance the work efficiency, adapt to the fast pace of modern life, the characteristics of high efficiency, it has been widely welcomed.
In each school year, students of information management fee is the focus of the work of a school, a large number of students in charge of information management is a very large number of statistics, thus, the school fees for the management of students is likely to there is some confusion in the students a complete charge management system, whether students or school administrators is essential, which is doing the work. "School fee management system" is the most schools in the settlement of fees for students with regard to the existence of the management problems of software management systems. "School fee management system" for students to pay the demand situation in this part of the information timely and accurate reflection that this will greatly improve the efficiency of the management of the school will certainly help.
Keywords: SQL Server database, Visual C++ development language, ADO, MIS
目录
第一章 绪论
1.1 开发的目的及意义
科技进步将人类带到了信息时代,计算机已成为各行各业在业务处理与管理工作中必不可少的辅助工具,并对各行业的发展起到越来越大的推动作用。计算机信息管理技术的应用,除了能在相当大的程度上代替人工作业,从而减少人员工作量,减轻工作负担,减少工作中因人为原因而产生的错误从而避免不必要的损失外,更重要的是能建立准确畅通、简便的信息流通渠道,为工作提供所需要的准确、即时的信息以帮助作出正确而及时的选择与决定,从而给采用这门技术的单位带来了巨大的可见或不可见的利益与效益。
随着信息时代来临,信息处理的利器—计算机应用于学校的日常管理为学校的现代化带来了从未有过的动力和机遇,为学校管理领域的飞速发展提供了无限潜力。采用计算机管理信息系统已成为学校管理科学化和现代化的重要标志,给学校带来了明显的经济效益和社会效益。为了加快学校系统的信息化步伐,提高学校的业务水平,建设和完善学校信息系已变得十分必要。与市场经济的接轨的过程中,每个学校都面临着强化内部管理、树立学校形象、上层次晋等级、进而提高社会效益和经济效益的艰巨任务。在学校管理过程中应用计算机系统,可随时进行经济核算,展开成本效益分析,使学校经营决策科学化;借据计算机数据处理严密可靠的特有机制,在改善学校人、财、物管理,降低学校财务人员工作强度,提高财务工作质量,创造学校良好信誉等诸多方面,益康学校管理信息系统都将成为您不可或缺的助手和工具。
1.2 背景以及现状
对于大多数的高等院校来说,利用计算机支持学校高效率完成学生收费管理的事务,是适应现代化高校制度要求、推动高校学生收费管理走向科学化、规范化的必要条件;而收费管理是一项琐碎、复杂而又十分细致的工作,学生学费的计算、统计、补交等工作信息量很大,是不允许出错的,如果实行手工操作,手工开票来完成,然后凭借发票,财务手工生成台帐。这样以来,开学时,学校的交费窗口总是学生拥挤不堪,财务人员手忙不停,做着大量重复而又不得不做的手工劳动。做台帐时更难,需手工将发票上的数据往电脑中填,这样的重复劳动,一个人需要十天半个月甚到个把月才能录入完,而且又容易填错、填漏,查询起来非常困难,面对大量学生的收费情况需要经过大量的计算和核对,这就会耗费工作人员大量的时间和精力,用计算机来进行学生年度收费的工作,不仅能够保证收费核算准确无误、快速输出,而且还可以利用计算机对有关收费的各种信息进行统计,服务于财务部门的核算和财务处理,同时计算机具有着手工管理所无法比拟的优点.例如:检索迅速、查找方便、可靠性高、存储量大、保密性好、寿命长、成本低等。这些优点能够极大地提高学生收费管理的效率,也是企业的科学化、正规化管理,与世界接轨的重要条件。
不同的高校具有不同的学生、收费管理制度,这就决定了不同的高校需要不同的学生收费管理系统。因此,设计一种通用化的学生收费管理系统是非常必要的。
本系统主要是对学生交费以及各专业部门收费项目情况进行系统管理。及时反映学生的交费、欠费情况。系统的开发主要包括后台数据库的建立、维护以及前端应用程序的开发两个方面。本系统采用目前比较流行的ADO数据访问技术,并将每个数据库表的字段和操作封装到类中,使应用程序的各个窗体都能够共享对表的操作,无需重复编码,使程序更加易于维护,从而成功地将面向对象的程序设计思想应用到数据库应用程序设计中。
1.3系统设计思想
一个方便用户使用的软件应该具备软件体积小,操作界面友好,基本功能稳定,运行速度较快,通过计算机技术及网络技术结合开发出客户端与服务器端,以便方便快捷清晰的进行数据传输,和简易通讯功能。
- 实用性:系统以用户需求为目标,以方便用户为原则。根据用户实际的需求情况,度身订造一套先进的局域网数据传输,从用户角度出发尽可能的方便用户使用,满足基本的用户需要,成为公司学校等通用的网络软件。
- 先进性:本设计将充分应用现有成熟的计算机技术、网络技术、软件开发技术。以VC++为主要开发环境,其优秀的编码体制和强大的编译器是此系统的强力支柱。
- 高可靠性:一个实用的系统同时必须是可靠的,本设计通过合理而先进的网络设计以及软、硬件的优化选型,可保证系统数据传输的正确性。
- 高安全性:在设计中,将充分利用网络软、硬件提供的各种安全措施,既可以保证用户共享资源,同时也可保证关键数据的安全性。
- 采用标准技术:本系统的所有设计遵循国际上现行的标准进行,以提高系统的开放性。
- 可维护性:系统的设计要求方便维护,包括硬件的维护,软件的维护(更改,升级等)和网络的维护。
- 可扩展性及灵活性:系统的设计以方便未来业务的扩展和系统扩充为目标,系统要求能够方便的升级,充分保护系统的投资。
- 智能性:系统在设计时,充分考虑系统运行的智能性,在使用系统一段时间后,系统可以适应于任何局域网。
1.4 本章小结
本章介绍了系统的开发目的、意义及类似系统的研究现状和开发背景,简略的介绍了系统的设计思想及预计达到的要求。
第二章 系统需求分析及其设计
学校收费管理系统是面向的是各学校负责收费工作的人员和相关领导,其目的是提高对各专业学生各种费用的收费效率及可靠性。本章针对各高校的管理现状和要求,从理论和应用的角度论述了学生收费信息管理系统的特点、功能、设计思路。本系统主要包括基本信息管理、学校收费管理和查询统计管理等四个模块,用户分为系统管理员和普通用户,用户角色的不同直接影响着用户的操作权限,比如对各模块的数据修改和增加都只限于管理员角色,而普通用户只能进行查询。
2.1 数据的需求
目前各高等院校的收费标准主要是以各专业学生的不同而产生差异的,当然还有些别的因素决定着各个学生的收费,如是否在校住宿、是否为特困学生等,因此向学生收费时需要一些学生的特定信息输入,即学生数据表。学生对应着某个专业,因此需要一个专门管理专业信息的专业表。一个院校的教学单位应该是以学院作为化分标准的,学院下才才对应着各个不同的系,在系的下一级才是专业,因此还需要建一个管理学院及系级单位的院系表。
有了上述的一些数据信息我们就可以设定各专业学生的收费标准了,在数据表中建一个管理收费标准的收费项目表,用户可以手动的设定各项收费标准。本系统的目的是为了管理学生的收费记录,因此最后还需要建一个学生收费表,用来记录各院系下学生的收费或欠费情况。至此,解决了收费系统的数据需求。
“学校收费管理信息系统”包括四个模块:基本信息管理、学校收费管理和查询统计管理。这四个模块既相互联系又相互独立,收费管理必须以学生信息和院系专业信息作入为基础和依据。
基本信息管理模块的主要功能是用来对学生和院系的基本信息进行收集和修改,具体包括对院系信息、专业信息和学生信息的添加、修改、删除和查询等功能。
学校收费管理模块是本系统的核心部分,根据学生的基本信息对学生进行收费,主要包括收费项目管理和学生收费管理等功能。
查询统计模块是为了给用户管理带来统计方便,主要包括包括按日详细查询和每月收费统计等功能。
系统的功能模块图如下图2.3所示。在功能模块示意图的树状结构中,每一个叶结点都是一个最小的功能模块。每一个功能模块都需要针对不同的表完成相同的数据库操作,即添加记录、修改记录、删除记录和查询显示记录信息。
在本系统中,用户管理模块的功能比较简单。在系统初始化时,有两个默认的用户。系统管理员用户为happyzero,普通用户为Users,是由程序设计时手动地添加到数据库中的,它们的默认密码均为123456。happyzero用户可以创建用户、修改用户信息以及删除用户;普通用户则只能修改自己的用户名和密码。
2.4 系统流程分析
运行系统后,首先会看到登录窗体,对用户的身份进行认证。如果需要对普通用户进行管理,则使用系统管理员用户登录;否则建议使用普通用户登录。如果还要创建其他用户,可以在用户管理模块中创建和管理。
在本系统中,应该首先增加基本信息,基本信息包括院系信息、专业信息和学生信息。院系信息包括院系名称、院系功能描述等。专业信息包括院系信息、专业名称和专业描述信息。学生信息包括学生姓名、性别、所属院系、专业、班级和入学年度等。基本信息是本系统管理的核心数据,它为学校收费管理提供各种数据。
在学生收费管理模块中,可以对收费项目和学生收费进行管理。收费项目信息包括专业信息、年度、学费、杂费、机时费、保险费、住宿费、存车费、书费和资料费等。学生收费信息包括学生信息、收费年度、收费日期、应交学费、实交学费、应交杂费、实交杂费、应交总计、实交总计等。
查询统计模块包括按日详细查询和每月收费统计等功能。按日详细查询信息包括学生、收费年度、收费日期、应交学费、实交学费、应交杂费、实交杂费、应交总计、实交总计、所欠费用合计等。每月收费统计信息用来统计各专业各月的各类收费总和,包括学生信息、收费月份、专业名称、学生总数、交费总额、学费总额等。
本系统的流程如图2.4所示。
在系统流程图中可以看到,每个用户有3次机会进行身份认证。如果3次输入的用户名和密码都无法与数据库中的用户数据匹配,则强制退出系统。
系统需求分析是开发软件前期过程中必不可少的一个环节,可以说直接影响到后期的系统的实现,程序编写,系统的维护,本章介绍了系统的需求分析及其设计,分析得出实现本系统功能的数据需求及功能需求,根据功能制作出系统用户操作的流程图。
第三章 开发技术
本次数据库管理信息系统的开发主要涉及到如下技术:
- 所选开发工具的基本编程方法。
- 基本的后台数据库管理方法,例如创建数据库、创建表、创建视图、备份和还原数据库等。
- 常用的SQL语句的使用。
- ADO数据库访问技术。
现在,市场上可以选购的应用开发产品很多,流行的也有数十种。目前在我国市场上最为流行、使用最多、最为先进的可用作企业级开发工具的产品有:Microsoft公司的Visual Basic,Microsoft公司的Visual C++,Borland公司的Delphi,Powersoft公司的PowerBulider,Java等等。在目前市场上这些众多的程序开发工具中,有些强调程语言的弹性与执行效率;有些则偏重于可视化程序开发工具所带来的便利性与效率的得高,各有各的优点和特色,也满足了不同用户的需求。然而,语言的弹性和工具的便利性是密不可分的,只有便利的工具,却没有弹性的语言作支持,许多特殊化的处理动作必需要耗费数倍的工夫来处理,使得原来所标榜的效率提高的优点失去了作用;相反,如果只强调程语言的弹性,却没有便利的工具作配合,会使一些即使非常简单的界面处理动作,也会严重地浪费程序设计师的宝贵时间。
与其他开发工具相比,Visual C++具有功能强大、通用性强和易于扩充等特点,越来越多地被用作客户/服务器(Client/Server,简称为C/S)应用程序的前端开发工具,与后端的SQL Server数据库相结合,能够提供一个高性能的管理信息系统 系统方案。因此,本次程序设计采用Visual C++作为开发工具,SQL Server作为后台数据库。
ADO (ActiveX Data Objects) 是一个用于存取数据源的COM组件。它提供了编程语言和统一数据访问方式OLE DB的一个中间层。允许开发人员编写访问数据的代码而不用关心数据库是如何实现的,而只用关心到数据库的连接。访问数据库的时候,关于SQL的知识不是必要的,但是特定数据库支持的SQL命令仍可以通过ADO中的命令对象来执行。
象Microsoft的其它系统接口一样,ADO是面向对象的。它是Microsoft全局数据访问(UDA)的一部分,Microsoft认为与其自己创建一个数据,不如利用UDA访问已有的数据库。为达到这一目的,Microsoft和其它数据库公司在它们的数据库和Microsoft的OLE数据库之间提供了一个“桥”程序,OLE数据库已经在使用ADO技术。ADO的一个特征(称为远程数据服务)支持网页中的数据相关的ActiveX控件和有效的客户端缓冲。作为ActiveX的一部分,ADO也是Microsoft的组件对象模式(COM)的一部分,它的面向组件的框架用以将程序组装在一起。
ADO从原来的Microsoft数据接口远程数据对象(RDO)而来。RDO与ODBC一起工作访问关系数据库,但不能访问如ISAM和VSAM的非关系数据库。
ADO 是对当前微软所支持的数据库进行操作的最有效和最简单直接的方法,它是一种功能强大的数据访问编程模式,从而使得大部分数据源可编程的属性得以直接扩展到你的Active Server 页面上。可以使用ADO 去编写紧凑简明的脚本以便连接到 Open Database Connectivity (ODBC) 兼容的数据库和 OLE DB 兼容的数据源,这样 ASP 程序员就可以访问任何与 ODBC 兼容的数据库,包括 MS SQL SERVER、Access、 Oracle 等等。
ADO是一种面向对象的编程接口,微软介绍说,与其同IBM和Oracle提倡的那样,创建一个统一数据库,不如提供一个能够访问不同数据库的统一接口,这样会更加实用一些。为实现这一目标,微软在数据库和微软的OLE DB中提供了一种“桥”程序,这种程序能够提供对数据库的连接。 开发人员在使用ADO时,其实就是在使用OLE DB,不过OLE DB更加接近底层。ADO的一项属性??远程数据服务,支持“数据仓库”ActiveX 组件以及高效的客户端缓存。作为ActiveX的一部分,ADO也是COM组件的一部分。ADO是由早期的微软数据接口??远程数据对象RDO演化而来的。RDO同微软的ODBC一同连接关系数据库,不过不能连接非关系数据库。
ADO向我们提供了一个熟悉的,高层的对OLE DB的Automation封装接口。对那些熟悉RDO的程序员来说,你可以把OLE DB比作是ODBC驱动程序。如同RDO对象是ODBC驱动程序接口一样,ADO对象是OLE DB的接口;如同不同的数据库系统需要它们自己的ODBC驱动程序一样,不同的数据源要求它们自己的OLE DB提供者(OLE DB provider)。
ADO提供执行以下操作的方式。
(1)连接到数据源。
(2)指定访问数据源的命令,同时可带变量参数或优化执行。通常涉及ADO的Command对象。
(3)执行命令,例如一个SELECT脚本。
(4)如果这个命令使数据按表中行的形式返回(例如SELECT命令),则将这些行存储在易于检查、操作或更改的缓存中。
(5)适当情况下,可以把缓存行的更改内容写回数据库中,更新数据源。
(6)提供常规方法检测错误(错误通常由建立连接或执行命令造成),涉及ADO的Error对象。
为了实现上面的功能,ADO编程模型提供了以下元素:
(1)连接。通过“连接”可以从应用程序中访问数据源。连接时必须指定要连接到的数据源以及连接所使用的用户名和口令等信息。
对象模型使用Connection对象完成连接功能。
(2)命令。可以通过已建立的连接发出命令,对数据源进行指定的操作。一般情况下,可以通过命令在数据源中添加、修改或删除数据,也可以检索满足指定条件的数据。
在对象模型中使用Command对象来体现命令的概念。
(3)参数。在执行命令时可以指定参数,参数可以在命令发布之前进行更改。例如,可以重复发出相同的数据检索命令,但是每一次指定的检索条件不同。
对象模型使用Parameter对象来体现参数概念。
(4)记录集。查询命令可以将查询结果存储在本地,这些数据以“行”(记录)为单位,返回数据的集合被称为记录集。
对象模型使用Recordset对象来体现记录集概念。
(5)字段。一个记录集行包含一个或多个字段。如果将记录集看作二维网格,字段将排列起来构成列。每一字段(列)都分别包含有名称、数据类型和值的属性,值中包含了来自数据源的真实数据。
对象模型使用Field对象来体现字段概念。
(6)错误。错误可能在应用程序中随时发生,通常是由于无法建立连接、执行命令或对某些状态的对象进行操作所引起的。
对象模型使用Error对象来体现错误。任何发生的错误都会产生一个或多个Error对象。
(7)属性。每个ADO对象都有一组惟一的“属性”来描述或控制对象的行为。属性有内置和动态两种类型。内置属性是ADO对象的一部份并且随时可用。动态属性则由特别的数据提供者添加到ADO对象的属性集合中,仅在提供者被使用时才能存在。
对象模型使用Property对象来体现属性。
(8)集合。ADO集合是一种可方便地包含其他特殊类型对象的对象类型。使用集合方法可按名称(文本字符串)或序号(整型数)对集合中的对象进行检索。
ADO支持4种类型的集合:
- Connection对象具有Errors集合,包含为响应与数据源有关的单一错误而创建的所有Error对象。
- Command对象具有Parameters集合,包含应用于Command对象的所有Parameter对象。
- Recordset对象具有Fields集合,包含Recordset对象中所有列的Field对象。
- Connection、Command、Recordset和Field对象都具有Properties集合,它包含各个对象的Property对象。
在使用Visual C++开发数据库应用程序时,会使用到一些常用的数据库访问控件,包括:
- ADO Data控件
- DataList控件
- DataCombo控件
- DataGrid控件
(1)ADO Data控件
- ADO Data控件是使用ADO技术访问数据库的具体实现,它主要有以下几个特点:
- 可以使用ADO Data控件快速地创建一个到数据库的连接;
- ADO Data控件使用Recordset对象提供储存在数据库中数据的访问,并允许从一个记录移动到另一个记录,显示和操纵记录数据;
- ADO Data控件可以执行大部分数据库访问操作,但是它并不能够显示数据库中数据的内容。ADO Data控件通常需要与其他控件使用。
这些与ADO Data控件结合使用的控件,可以称之为“数据觉察控件”。常用的数据觉察控件包括DataList、DataCombo和DataGrid等。当ADO Data控件中当前记录发生变化时,数据觉察控件的显示内容也会随之发生变化。在设计时,可以通过首先将 ConnectionString 属性设置为一个有效的连接字符串,然后将 RecordSource 属性设置为一个适合于数据库管理者的语句来创建一个连接。也可以将 ConnectionString 属性设置为定义连接的文件名。该文件是由“数据链接”对话框产生的,当单击“属性”窗口中的ConnectionString,然后单击“生成”或“选择”时,该对话框出现。可以通过将 DataSource 属性设置为 ADO Data 控件,把 ADO Data 控件连接到一个数据绑定的控件,例如 DataGrid,DataCombo或DataList控件。在运行时,可以动态地设置 ConnectionString 和 RecordSource 属性来更改数据库。
ADO Data控件的常用属性及说明如下:
- BOF。如果当前记录位于Recordset对象的第一个记录之前,则BOF等于TRUE,否则BOF等于FALSE。
- ConnectionString。指定连接字符串,用于建立到数据源的连接。
- ConnectionTimeout。指定连接数据库的等待时间,如果超过此时间还没有连接到数据库,则产生一个错误。
- EOF。如果当前记录位于Recordset对象的第一个记录之后,则EOF等于TRUE,否则EOF等于FALSE。通常可以用BOF和EOF属性判断记录集的当前游标位置是否存在有效数据。
- MaxRecords。返回Recordset对象中记录的数量。
- Mode。指定对数据源的访问权限。AdModeUnKnown是默认值,表明权限尚未设置或无法确定;AdModeRead表明权限为只读;AdModeWrite表明权限为只写;AdModeReadWrite表明权限为读/写;AdmodeShareDenyRead防止其他用户使用读杼退打开连接;AdModeShareExclusive防止其他用户打开连接;AdModeShareDenyNone防止其他用户使用任何权限打开连接。
- PassWord。设置连接到数据源时指定用户的口令。
- RecordSource。设置一个数据记录源,可以是表、视图或SELECT语句等。查询结果保存在Recordset属性中。
- UserName。设置连接到数据源的用户。
(2) DataGrid 控件
DataGrid控件以表格的形式显示结果集中的数据,它需要ADO Data控件提供的数据源。
DataGrid 控件的每一个单元格都可以包含文本值,但不能链接或内嵌对象。可以在代码中指定当前单元格,或者用户可以使用鼠标或箭头键在运行时改变它。通过在单元格中键入或编程的方式,单元格可以交互地编辑。单元格能够被单独地选定或按照行来选定。
如果一个单元格的文本太长,以致于不能在单元格中全部显示,则文本将在同一单元格内折行到下一行。要显示折行的文本,必须增加单元格的Column对象的Width属性和/或DataGrid控件的RowHeight属性。在设计时,可以通过调节列来交互地改变列宽度,或在Column对象的属性页中改变列宽度。
使用DataGrid控件的Columns集合的Count属性和Recordset对象的RecordCount属性,可以决定控件中行和列的数目。DataGrid控件的可包含的行数取决于系统的资源,而列数最多可达32,767列。
选择一个单元格,则ColIndex属性被设置,也就是选择了DataGrid对象的Columns集合中的一个Column对象。Column对象的Text和Value属性引用当前单元格的内容。使用Bookmark属性能够访问当前行的数据,它能够提供对下一级Recordset对象中记录的访问。DataGrid控件中的每一列都有自己的字体、边框、自动换行和另外一些与其他列无关的能够被设置的属性。在设计时,您可以设置列宽和行高,并且建立对用户不可见的列。您还能阻止用户在运行时改变格式。
(3) DataCombo控件
- DataCombo控件是数据绑定组合框,它需要指定一个ADO Data数据源,并使用数据源中的一个字段填充。该控件的常用属性如下。
- BoundColumn。返回或设置绑定数据源中的一个源字段名称,此字段的值会随着DataComo控件中当前值的变化而变化。
- BounText。返回或设置由BoundColumn属性指定的字段的值。可以把它看成是DataCombo控件当前值变化后的输出值。
- ListField。返回或设置Recordset对象中的字段名,这个对象由RowSource属性指定,用于填充DataCombo控件的列表部分。
- RowSource。设置一个指定ADO Data控件的值,DataCombo控件的列表由这个ADO Data控件填充。
- SelectedItem。返回DataCombo控件中选中记录的值。
(4) DataList控件
DataList控件和DataCombo控件的功能完全相同。
首先,要用#import语句来引用支持ADO的组件类型库(*.tlb),其中类型库可以作为可执行程序(DLL、EXE等)的一部分被定位在其自身程序中的附属资源里,如:被定位在msado15.dll的附属资源中,只需要直接用 #import引用它既可。可以直接在Stdafx.h文件中加入下面语句来实现:
#import "c:\program files\common files\system\ado\msado15.dll" no_namespace rename("EOF", "adoEOF")
其次,在程序初始过程中需要初始化组件,一般可以用CoInitialize(NULL);来实现,这种方法在结束时要关闭初始化的COM,可以用下面语句CoUnInitialize();来实现。在MFC中还可以采用另一种方法来实现初始化COM,这种方法只需要一条语句便可以自动为我们实现初始化COM和结束时关闭COM的操作,语句如下所示: AfxOleInit();
接着,就可以直接使用ADO的操作了。我们经常使用的只是前面用#import语句引用类型库时,生成的包装类.tlh中声明的智能指针中的三个:_ConnectionPtr、_RecordsetPtr和_CommandPtr。下面分别对它们的使用方法进行介绍:
1,_ConnectionPtr接口返回一个记录集或一个空指针。通常使用它来创建一个数据连接或执行一条不返回任何结果的SQL语句,如一个存储过程。使用 _ConnectionPtr接口返回一个记录集不是一个好的使用方法。对于要返回记录的操作通常用_RecordserPtr来实现。而用 _ConnectionPtr操作时要想得到记录条数得遍历所有记录,而用_RecordserPtr时不需要。
2,_CommandPtr接口返回一个记录集。它提供了一种简单的方法来执行返回记录集的存储过程和SQL语句。在使用_CommandPtr接口时,你可以利用全局 _ConnectionPtr接口,也可以在_CommandPtr接口里直接使用连接串。如果你只执行一次或几次数据访问操作,后者是比较好的选择。但如果你要频繁访问数据库,并要返回很多记录集,那么,你应该使用全局_ConnectionPtr接口创建一个数据连接,然后使用_CommandPtr 接口执行存储过程和SQL语句。
3,_RecordsetPtr是一个记录集对象。与以上两种对象相比,它对记录集提供了更多的控制功能,如记录锁定,游标控制等。同_CommandPtr接口一样,它不一定要使用一个已经创建的数据连接,可以用一个连接串代替连接指针赋给 _RecordsetPtr的connection成员变量,让它自己创建数据连接。如果你要使用多个记录集,最好的方法是同Command对象一样使用已经创建了数据连接的全局_ConnectionPtr接口,然后使用_RecordsetPtr执行存储过程和SQL语句。
开发一个信息管理系统,VC++6.0都提供了ODBC API、MFC DAO、MFC的OLE/DB 和ActiveX数据对象(ADO)类等一些编程接口。为开发本次学校收费管理系统,本章主要介绍了最为流行的ADO数据库开发技术,详细的讲解了ADO数据模型、数据访问控件及其使用步骤,为系统开发提供了技术保障。
第四章 数据库设计
数据库设计是数据库应用系统开发过程中首要的和基本的内容。数据库是信息系统的核心和基础。它把信息系统中的大量数据按照一定的模型组织起来,提供存储、维护、检索数据的功能,是信息系统可以方便、及时、准确从数据库中获取所需的信息。数据库结构设计是总体设计阶段非常重要的环节,好的数据库结构可以简化开发过程,使系统功能更加清晰明确。一个信息系统的各个部分能否紧密的结合在一起以及如何结合,关键在数据库。因此必须对数据库进行合理设计。
根据系统需求分析,数据库中应包含有6个表,即院系信息表DepInfo、专业信息表Speciality、学生信息表Student、收费项目表ChargeItem、收费信息表StuCharge和用户信息表UserInfo等。以下是这些表的结构。
表DepInfo的结构
编号 | 字段名称 | 数据类型 | 说明 |
1 | DepId | Int | 院系编号,主键 |
2 | DepName | Varchar50 | 院系名称 |
3 | Describes | Varchar250 | 院系描述 |
4 | UID | int | 上级院系编号 |
表Speciality的结构
编号 | 字段名称 | 数据类型 | 说明 |
1 | SpeId | Int | 专业编号,主键 |
2 | Name | Varchar50 | 专业名称 |
3 | DepId | Int | 院系编号 |
4 | Describes | Varchar200 | 专业描述 |
表Student的结构
编号 | 字段名称 | 数据类型 | 说明 |
1 | StuId | Int | 学生编号,主键 |
2 | Name | Varchar50 | 姓名 |
3 | Sex | Char2 | 性别 |
4 | DepId | Int | 院系编号 |
5 | SpeId | Int | 专业编号 |
6 | ClassName | Varchar20 | 班级名称 |
7 | iYear | Int | 入学年度 |
8 | Idcard | Varchar | 学生学号 |
9 | ZhuSu | Char2 | 是否住宿 |
10 | CunChe | Char2 | 是否存车 |
11 | PinKun | Char2 | 是否贫困生 |
12 | TeKun | Char2 | 是否特困生 |
13 | FillinPerson | Varchar30 | 填表用户 |
14 | FillininTime | Varchar20 | 填表日期和时间表 |
表ChargeItem的结构
编号 | 字段名称 | 数据类型 | 说明 |
1 | ItemId | Int | 项目编号,主键 |
2 | SpeId | Int | 专业编号 |
3 | iYear | Int | 年度 |
4 | Tuition | Float | 学费 |
5 | Incidental | Float | 杂费 |
6 | MacTimeFee | Float | 上机费 |
7 | Insurance | Float | 保险费 |
8 | DormFee | Float | 住宿费 |
9 | BicycleFee | Float | 存车费 |
10 | BookFee | Float | 书费 |
11 | FileFee | Float | 其它费用 |
表StuCharge的结构
编号 | 字段名称 | 数据类型 | 说明 |
1 | ChgId | int | 记录编号,主键 |
2 | StuId | Int | 学生编号 |
3 | iYear | Int | 费用年度 |
4 | ChargeDate | Datetime | 收费日期 |
5 | Should_Tuition | float | 应交学费 |
6 | Fact_Tuition | Float | 实交学费 |
7 | Should_Incidental | Float | 应交杂费 |
8 | Fact_ Incidental | Float | 实交杂费 |
9 | Should_MacTimeFee | Float | 应交机时费 |
10 | Fact_ MacTimeFee | Float | 实交机时费 |
11 | Should_Insurance | Float | 应交保险费 |
12 | Fact_ Insurance | Float | 实交保险费 |
13 | Should_DormFee | Float | 应交住宿费 |
14 | Fact_ DormFee | Float | 实交住宿费 |
15 | Should_BicycleFee | Float | 应交存车费 |
16 | Fact_ BicycleFee | Float | 实交存车费 |
17 | Should_BookFee | Float | 应交书费 |
18 | Fact_ BookFee | Float | 实交书费 |
19 | Should_FileFee | Float | 应交其它费用 |
20 | Fact_ FileFee | Float | 实交其它费用 |
21 | Should_Total | Float | 应交总计 |
22 | Fact_ Total | Float | 实交总计 |
23 | Memo | Varchar400 | 备注 |
表UserInfo的结构
编号 | 字段名称 | 数据类型 | 说明 |
1 | UserName | Varchar50 | 用户名,主键 |
2 | Passswd | Varchar50 | 密码 |
3 | UserType | Tinyint | 用户类型(1-系统管理员,2-普通用户) |
在上述的各表中都定义了相应的主键,为了规定各表之间的关系以及对数据库操作的约束限制,还定义了一组外键,具体如下表4.17所示:
外键定义
关系名 | 主键表 | 主键字段 | 外键表 | 外键字段 |
FK_Student_DepInfo | DepInfo | DepId | Student | DepId |
FK_Speciality_DepInfo | DepInfo | DepId | Speciality | DepId |
FK_Student_Speciality | Speciality | SpeId | Student | SpeId |
FK_ChargeItem_Speciality | Speciality | SpeId | ChargeItem | SpeId |
FK_StuCharge_Student | Student | StuId | StuCharge | StuId |
本次程序中为数据库的每一个表都创建一个类,类的成员变量对应表的列,类的成员函数是对成员变量和表的操作。这种设计思路完全符合了面向对象的程序设计方法,利用了类的封装和可重用性,使得程序中每一个窗体都能方便访问数据库,同时也便于程序的维护和扩展。具体编码说明如下:
4.2.1 CDepInfo类
CDepInfo类用来管理表DepInfo的数据库操作,它的主要成员如下表4.2.1所示。
函数名 | 具体说明 |
CDepInfo | 初始化成员变量。 |
LoadDep | 读取所有院系编号、名称和上级院系编号等信息到数组对象CStringArray。此函数在把院系装入到树状结构的过程中将起重要的作用。 |
HaveName | 判断指定的院系名称是否已经在数据库中,参数paraName表示指定的院系名称。返回-1表示不存在,返回1表示存在。 |
HaveSon | 判断指定院系中是否存在下级院系。参数paraId表示指定的院系的编号。返回-1表示不存在,返回1表示存在。此函数将在删除院系数据之前调用,如果返回1,则不能删除指定的院系。 |
HaveSpeciality | 判断指定的院系中是否包含专业信息。参paraId表示指定的院系的编制号。返回-1表示不存在,返回1表示存在。 |
GetInfo | 读取指定的院系记录。参数paraId表示要读取院系的编号。 |
SqlInsert | 插入新的院系记录,返回新插入记录的编号. |
SqlUpdate | 修改指定的院系记录。参数paraId表示要修改的院系编号。 |
SqlDelete | 删除指定的院系记录。参数paraId表示要删除的院系的编号. |
Load_dep()函数是一个比较重要的函数,其代码如下:
void CDepInfo::LoadDep()
{
ADOConn m_AdoConn;
m_AdoConn.OnInitADOConn();
_bstr_t vSQL;
vSQL = "SELECT * FROM DepInfo ORDER BY UID";
_RecordsetPtr m_pRecordset;
m_pRecordset = m_AdoConn.GetRecordSet(vSQL);
a_DepName.RemoveAll();
a_DepId.RemoveAll();
a_UID.RemoveAll();
while (m_pRecordset->adoEOF == 0)
{
a_DepId.Add((LPCTSTR)(_bstr_t)m_pRecordset->GetCollect("DepId"));
a_DepName.Add((LPCTSTR)(_bstr_t)m_pRecordset->GetCollect("DepName"));
a_UID.Add((LPCTSTR)(_bstr_t)m_pRecordset->GetCollect("UID"));
m_pRecordset->MoveNext();
}
m_AdoConn.ExitConnect();
}
这是一个批量读取表DepInfo中数据的过程,程序将院系数据读取到3个CStringArray数组对象中,即a_DepId、a_DepName和a_UId,它们被定义为类CDepInfo的成员变量。CStringArray对象是支持CString对象的数组。数组的定义在CDepInfo.h中实现,代码如下:
public:
CStringArray a_DepId;
CStringArray a_DepName;
CStringArray a_UID;
SqlInsert()函数的代码如下:
long CDepInfo::SqlInsert()
{
ADOConn m_AdoConn;
m_AdoConn.OnInitADOConn();
CString strUID;
strUID.Format("%d", UID);
_bstr_t vSQL;
vSQL = "INSERT INTO DepInfo (DepName, Describes, UID) VALUES('"
+ DepName + "','" + Describes + "'," + strUID + ")";
m_AdoConn.ExecuteSQL(vSQL);
//读取最大编号
long lDepId;
vSQL = "SELECT MAX(DepId) AS MaxId FROM DepInfo";
//执行SELETE语句
_RecordsetPtr m_pRecordset;
m_pRecordset = m_AdoConn.GetRecordSet(vSQL);
//返回各列的值
if (m_pRecordset->adoEOF)
lDepId = 0;
else
lDepId = atol((LPCTSTR)(_bstr_t)m_pRecordset->GetCollect("MaxId"));
m_AdoConn.ExitConnect();
return lDepId;
}
程序首先将新记录插入到表DepInfo中,然后使用select语句返回表DepInfo中最大的DepId字段值,并以此作为新记录的编号值返回。在添加院系记录时,程序需要得到新记录的院系编号值,并将其添加到Tree控件中。
4.2.2 CSpeciality类
CSpeciality类用来管理表Speciality的数据库操作,它的主要成员如下表所示。
函数名 | 具体说明 |
CSpeciality | 初始化成员变量。 |
LoadSpe | 装入专业信息到CStringArray数组对象中。 |
GetDepName | 返回专业信息所属的院系名称。 |
GetInfo | 读取指定的记录。参数paraId表示记录编号。 |
HaveName | 判断指定的专业名称是否存在,参数paraName表示专业名称。 |
HaveStu | 判断指定的专业中是否包含学生。参数paraId表示记录编号。 |
SqlDelete | 删除指定的记录。参数paraId表示要删除的记录编号。 |
SqlInsert | 插入新的记录。 |
SqlUpdate | 修改指定的记录。参数paraId表示记录编号。 |
HaveName()函数的代码如下:
int CSpeciality::HaveName(CString paraName)
{
ADOConn m_AdoConn;
m_AdoConn.OnInitADOConn();
_bstr_t vSQL;
vSQL = "SELECT * FROM Speciality WHERE Name = '" + paraName + "'";
_RecordsetPtr m_pRecordset;
m_pRecordset = m_AdoConn.GetRecordSet(vSQL);
if (m_pRecordset->adoEOF)
return -1;
else
return 1;
m_AdoConn.ExitConnect();
}
程序调用m_AdoConn.GetRecordSet(vSQL)函数执行select语句,判断指定的名称是否存在。如果返回的结果集为空,则表示指定的记录不存在;否则表示记录存在。
4.2.3 CStudent类
CStudent类用来管理表Student的数据库操作,它的主要成员如下表所示。
函数名 | 具体说明 |
CStudent | 初始化成员变量。 |
GetName | 根据指定的学生编号读取学生姓名。参数ParaId表示学生编号。 |
GetInfo | 读取指定的记录。参数ParaId表示记录编号。 |
SqlDelete | 删除指定的记录。参数ParaId表示要删除的记录编号。 |
SqlInsert | 插入新的记录。 |
SqlUpdate | 修改指定的记录。参数ParaId表示记录编号。 |
4.2.4 CChargeItem类
CChargeItem类用来管理表ChargeItem的数据库操作,它的主要成员如下表所示。
函数名 | 具体说明 |
CChargeItem | 初始化成员变量。 |
HaveSpe | 判断指定的记录是否存在。参数cItemName表示项目名称。 |
GetInfo | 读取指定的记录。参数paraId表示记录编号。 |
GetItemId | 返回项目编号。参数paraId表示专业编号,cYear表示年度。 |
GetSum | 返回指定专业的收费总客。参数paraId表示专业编号,cYear表示年度。 |
SqlDelete | 删除指定的记录。参数paraId表示要删除的记录编号。 |
SqlInsert | 插入新的记录。 |
SqlUpdate | 修改指定的记录。参数paraId表示记录编号。 |
4.2.5 CStuCharge类
CStuCharge类用来管理表StuCharge的数据库操作,它的主要成员如下表所示。
函数名 | 具体说明 |
CStuCharge | 初始化成员变量。 |
HaveChargeInfo | 判断是否存在交费信息。参数paraId表示学生编号,cYear表示缴费年度。 |
GetInfo | 读取指定的记录。参数paraId表示记录编号。 |
SqlDelete | 删除指定的记录。参数paraId表示要删除的记录。 |
SqlInsert | 插入新的记录。 |
SqlUpdate | 修改指定的记录。参数paraId表示记录编号。 |
4.2.6 CUserInfo类
CUserInfo类用来管理表UserInfo的数据库操作,它的主要成员如下表所示。
函数名 | 具体说明 |
CUserInfo | 初始化成员变量。 |
GetInfo | 读取指定的记录。参数表示要读取数据的用户名。 |
IsExistUser | 判断指定的用户名是否已经在数据库中,参数表示用户名。 |
SqlDelete | 删除指定的用户记录。参数表示要删除的用户名。 |
SqlInsert | 插入新用户记录。 |
SqlUpdate | 修改指定的用户记录。参数表示要修改的用户名。 |
数据库设计阶段,在需求分析的基础之上规划出了系统的功能模块,以及在此分析基础上设计出的具体的、可以满足本系统全部需求的数据库。由于数据库在设计中有非常重要的作用,所以在开发过程中,在考虑数据库上的时间较多。
本章介绍了系统的的数据库实现,详细列出了6个数据库表的具体定义,并将每年个数据库表的字段和操作都封装到类中,使应用程序的各个窗体都能够共享对表的操作,无需重复编码,成功的将面向对象的程序设计思想应用到数据库应用程序中。
第五章 详细模块设计及实现
用户要使用本系统,首先必须通过系统的身份认证,即登录过程。
当前用户的数据应该是全局有效的,在工程的任何位置都可以访问它,因此需要添加一个类CGlobal,用于保存全局变量。
在访问之前,还需要添加如下命令引入外部变量。
extern CUserInfo curUser;
添加一个对话框,并如下示图5.1添加相应的控件。
为登录对话框创建一个类,类名为CLoginDlg。
当用户单击“确定”按钮时,将发送BN_CLICKED消息,利用ClassWizard为其添加函数CLoginDlg::OnOK(),代码如下:
void CLoginDlg::OnOK()
{
UpdateData(TRUE);
if(m_User == "")
{
MessageBox("请输入用户名", "提示");
GetDlgItem(IDC_USER_EDIT)->SetFocus();
return;
}
CUserInfo User;
if(!User.IsExistUser(m_User))
{
count++;
if(count < 3)
{
MessageBox("用户名不存在,请重新输入!", "用户名错误", MB_ICONINFORMATION);
m_User = "";
m_Passwd = "";
UpdateData(FALSE);
GetDlgItem(IDC_USER_EDIT)->SetFocus();
return;
}
else
{
MessageBox("重试次数已到,不能再输入用户名和密码,即将退出系统!", "用户名错误", MB_ICONEXCLAMATION);
exit(0);
return;
}
}
// 判断密码是否正确
User.GetInfo(m_User);
if(User.Passwd != m_Passwd)
{
count++;
if(count < 3)
{
MessageBox("用户名或密码错误,请重新输入!", "用户名或密码错误", MB_ICONINFORMATION);
m_User = "";
m_Passwd = "";
UpdateData(FALSE);
GetDlgItem(IDC_USER_EDIT)->SetFocus();
return;
}
else
{
MessageBox("重试次数已到,不能再输入用户名和密码,即将退出系统!", "用户名或密码错误", MB_ICONEXCLAMATION);
exit(0);
return;
}
}
CDialog::OnOK();
}
因此,当用户点击程序运行时,程序首先打开的是登录对话框,如果用户不是通过单击“确定”按钮关闭登录对话框,则调用OnOK()函数关闭主对话框。在登录对话框的代码中,用户只有在通过身份认证后才能调用OnOK()关闭对话框。若验证通过后,则主对话框中将用户信息存入全局对象curUser 中,以便后续对用户权限的管理。
基本信息管理模块可以实现以下功能:
- 院系信息管理。
- 专业信息管理。
- 学生信息管理。
5.2.1 设计院系信息编辑对话框
编辑院系信息的对话框可以用来添加和修改院系信息。对话框ID属性为IDD_DEP_EDIT_DIALOG。对话框的布局如下图所示。
程序中用类CDepEditDlg来管理该对话框。为了便于交换,在DepEditDlg.h中定义3个公共变量,代码如下:
public:
int UpperId; //用于保存上级院系编号
int DepId; //用于保存当前编辑记录的院系编号
CString DepName; //用于保存当前编辑记录的院系名称
在打开对话框之前或者关闭对话框之后,用户可以通过设置和读取这3个变量与对话框交换数据。
用户单击“确定”按钮时,对应的OnOK()代码如下:
void CDepEditDlg::OnOK()
{
// TODO: Add extra validation here
UpdateData(TRUE);
CDepInfo dep;
//将用户输入的数据赋值到对象dep中,为更新数据库做准备
dep.DepName = m_DepName;
dep.Describes = m_Describe;
dep.UID = UpperId;
if (DepId == 0) //表示插入数据
//判断院系名称是否存在
if (dep.HaveName(m_DepName) == 1)
{
MessageBox("院系已经存在,请重新输入!");
return;
}
else
//返回插入记录的编号
DepId = dep.SqlInsert();
else
{
//如果用户修改了院系名称,则判断新院系名称是否存在
if (m_DepName != DepName)
{
if (dep.HaveName(m_DepName) == 1)
{
MessageBox("院系已经存在,请重新输入!");
return;
}
}
//根据DepId的值更新记录
CString cDepId;
cDepId.Format("%d", DepId);
dep.SqlUpdate(cDepId);
}
CDialog::OnOK();
}
程序将根据公共变量DepId的值决定插入数据还是修数据。当DepId为0时,将插入记录;否则将修改编号为DepId的记录。
5.2.2设计院系信息管理对话框
院系信息管理对话框的名称为IDD_DEP_MAN_DIALOG。因为本系统中使用树状结构来描述院系数据,所以要使用到Tree控件。Tree是控件显示Node(结点)对象的分层列表,每个Node对象均由一个标签和一个可选的位图组成。在对话框IDD_DEP_MAN_DIALOG中添加一个Tree控件,ID属性为IDC_TREE1。通过ClassWizard窗口,为Tree控件指定一个变量m_tree,数据类型为CTreeCtrl。对话框的布局如下:
以下是对话框IDD_DEP_MAN_DIALOG中部分过程的代码。
(1)定义公共变量。在DepManDlg.h中定义3个公共变量,代码如下:
public:
HTREEITEM m_root; //保存Tree控件的根节点
CDepInfo dep; //CDepInfo对象
CImageList m_treeImageList; //定义图像列表
- HTREEITEM是Win32简单数据类型,用于处理Tree控件中的一个节点,m_root变量表示Tree控件的根节点。
- Dep是一个CDepInfo对象,它用于从表DepInfo中读取院系数据到CStringArray数组中。为了提高效率,本实例中只在启动对话框中一次性装入院系数据,在其他函数中都通过访问dep对象得到院系记录。
- 类CImageList用来处理图像列表,此处为Tree控件提供节点图标。
(2)AddtoTree()函数。该函数的功能是将院系名称按照层次结构添加到Tree控件中,代码如下:
void CDepManDlg::AddtoTree(HTREEITEM m_node, long UpperId)
{
//使用递归方法将院系添加到TreeView控件中
long i;
HTREEITEM m_child;
for(i=0; i<dep.a_UID.GetSize(); i++)
{
if (UpperId == atoi(dep.a_UID.GetAt(i)))
{
m_child = m_tree.InsertItem(dep.a_DepName.GetAt(i), m_node);
m_tree.SetItemData(m_child, atol(dep.a_DepId.GetAt(i)));
AddtoTree(m_child, atol(dep.a_DepId.GetAt(i)));
}
}
}
这段代码使用了递归的方法装入院系数据。其中参数m_node为当前添加的根节点,参数UpperId表示当前添加根节点对应的院系编号。
(3)OnDelButton()函数。当用户单击“删除”按钮时触发OnDelButton()函数,对应的代码如下:
void CDepManDlg::OnDelButton()
{
// TODO: Add your control notification handler code here
HTREEITEM node;
node = m_tree.GetSelectedItem();
if (m_tree.GetItemData(node) == 0) //不能删除根节点
{
MessageBox("不能删除此项");
return;
}
//取得当前节点的值,并转换为CString类型
CString cDepId;
cDepId.Format("%d", m_tree.GetItemData(node));
if (dep.HaveSon(cDepId) == 1)
{
MessageBox("此院系包含下级院系,不能删除!");
return;
}
if (dep.HaveSpeciality(cDepId) == 1)
{
MessageBox("此院系包含专业,不能删除!");
return;
}
if (MessageBox("是否删除当前院系?", "请确认", MB_YESNO) == IDYES)
{
dep.SqlDelete(cDepId); //删除表DepInfo中的指定记录
m_tree.DeleteItem(node); //删除Tree控件中的当前节点
}
}
在删除院系前,应对院系信息进行判断。以下情况不能删除:
- 该结点为根结点;
- 包含下一级院系;
- 该院系包含学生;
5.2.3 设计院系信息选择对话框
为了方便用户选择院系,应该设计一个选择院系的对话框,对话框ID 属性为IDD_DEP_SEL_DIALOG,对应的类定义为CDepSelDlg。对话框布局如下所示:
为了保存选择院系的信息,在DepSelDlg.h中添加两个公共变量,代码如下:
public:
int DepId;
CString DepName;
当用户单击“确定”按钮时触发CDepSelDlg::OnOK()函数,代码如下:
void CDepSelDlg::OnOK()
{
// TODO: Add extra validation here
HTREEITEM node; //用于保存当前节点
node = m_tree.GetSelectedItem(); //取得当前节点值
if (m_tree.GetItemData(node) == 0) //不能选择根节点
{
MessageBox("请选择院系");
return;
}
DepId = m_tree.GetItemData(node);
DepName = m_tree.GetItemText(node);
CDialog::OnOK();
}
程序从Tree控件中读取选择的院系信息,然后将其赋值到公共变量DepIdDep和Name中,其他模块的程序可以通过访问此变量获取选择的院系信息。
5.2.4 设计专业信息编辑对话框
编辑专业信息的对话框可以用来添加和修改专业信息,对话框的ID属性为IDD_SEPCIALITY_EDIT_DIALOG。布局如下所示:
该对话框中的代码与院系编辑对话框相似。
5.2.5 设计专业信息管理对话框
专业信息管理对话框的名称为IDD_DEPCIALITY_MAN_DIALOG,对话框的属性与院系管理对话框相似,其布局如下:
专业信息管理与院系管理对话框比较相似,下面是一个比较重要的函数。
RefreshData()函数。该函数的功能是根据用户选择的院系信息设置SELECT语句,从而刷新DataGrid表格中的专业数据,代码如下:
void CSpecialityMan::RefreshData()
{
UpdateData(TRUE);
//从Tree控件中读取院系编号信息
int iDepId;
HTREEITEM node;
node = m_tree.GetSelectedItem();
iDepId = m_tree.GetItemData(node);
CString cDepId;
cDepId.Format("%d", iDepId);
CString cSource = "SELECT s.SpeId, s.Name AS 专业名称, d.DepName AS 院系,"
" s.Describes AS 描述 FROM Speciality s, DepInfo d"
" WHERE s.DepId = d.DepId";
if(m_tree.GetItemData(node) != 0) // 不是根节点
{
// 第2级节点
if(m_tree.GetItemData(m_tree.GetParentItem(node)) != 0)
{
cSource += " AND d.DepId = " + cDepId;
}
// 第1级节点
else
{
cSource += " AND (d.UID = " + cDepId + " OR d.DepId = " + cDepId + ")";
}
}
//刷新ADO Data控件的记录源
m_Adodc.SetRecordSource(cSource);
m_Adodc.Refresh();
_variant_t vIndex;
vIndex = long(0);
m_Datagrid.GetColumns().GetItem(vIndex).SetWidth(0);
vIndex = long(1);
m_Datagrid.GetColumns().GetItem(vIndex).SetWidth(110);
vIndex = long(2);
m_Datagrid.GetColumns().GetItem(vIndex).SetWidth(110);
vIndex = long(3);
m_Datagrid.GetColumns().GetItem(vIndex).SetWidth(230);
}
调用m_tree.GetSelectedItem()函数可以获取Tree控件中当前选择的节点,调用m_tree.GetItemData()函数可以获取当前节点对应的数据,即院系编号。以院系编号为查询条件设置SELECT语句,可以获取院系设置的所有专业。
调用m_Adodc.SetRecordSource(cSource)函数可以设置ADO Data控件要执行的SELECT语句,调用m_Adodc.Refresh()函数可以ADO Data控件中定义的数据源。调用m_Datagid.GetColumns().GetItem(vIndex).SetWidth(length)函数可以设置表格控件中当前选择记录的第vIndex+1列的宽度大小为length。
5.2.6 设计学生信息编辑对话框
编辑学生基本信息对话框可以用来添加和修改学生的基本信息,对话框的ID属性为IDD_STU_EDIT_DIALOG,对应的类为CStuEditDlg。其布局如下:
对话框中的代码与院系信息编辑对话框相似。
5.2.7设计学生信息管理对话框
学生信息管理对话框ID为IDD_STU_MAN_DIALOG,对应的类为CStuManDlg。其布局如下所示:
对话框中的代码与专业信息管理对话框相似。
5.2.8 设计学生信息选择对话框
在编辑学生收费信息时,要选择学生信息,所以需要设计一个选择学生对话框,对话框ID为IDD_STU_SEL_DIALOG。其布局如下所示:
当用户选择相应的院系时将调用RefreshList()函数,对应代码如下:
void CStuSelDlg::RefreshList()
{
UpdateData(TRUE);
// 从选择的节点中获取部门信息
long lDepId;
HTREEITEM node;
node = m_tree.GetSelectedItem();
lDepId = m_tree.GetItemData(node);
DepName = m_tree.GetItemText(node); // 院系名称
// 根据院系编号获取院系中的所有学生
CString cDepId;
cDepId.Format("%ld", lDepId);
CString cSource = "SELECT * FROM Student WHERE DepId = " + cDepId;
// 刷新数据源,从而更新学生列表
m_Adodc.SetRecordSource(cSource);
m_Adodc.Refresh();
}
学生收费管理是本系统的重要模块,它的主要功能包括收费项目管理和学生收费管理。
5.3.1 设计收费项目编辑对话框
收费项目对话框ID为IDD_CHARGE_ITEM_EDIT_DIALOG,其布局如下:
对话中的代码与院系信息编辑对话框相似。
5.3.2 设计收费项目管理对话框
收费项目管理对话框ID为ID_CHARGE_ITEM_MAN_DIALOG,对话框布局如下:
对话框中的代码与专业信息管理对话框相似。
5.3.3 设计学生收费编辑对话框
学生收费编辑对话框的名称为IDD_CHARGE_EDIT_DIALOG,其布局如下所示:
当用户单击“选择学生”时按钮按钮触发OnSelButton函数,代码如下:
void CChargeEdiDlg::OnSelButton()
{
// TODO: Add your control notification handler code here
UpdateData(TRUE);
CStuSelDlg dlg;
if(dlg.DoModal()==IDOK)
{
cStuId = dlg.cStuId;
m_name = dlg.Name;
CStudent stu;
stu.GetInfo(cStuId);
if(m_Year < stu.iYear)
{
MessageBox("缴费年度不能小于入学年度!");
return;
}
// 得到专业信息
CSpeciality spe;
CString cSpe;
cSpe.Format("%ld", stu.SpeId);
spe.GetInfo(cSpe);
m_speciality = spe.Name;
// 得到院系信息
m_dep = spe.GetDepName(cSpe);
CString cYear;
cYear.Format("%d",m_Year);
// 判断学生收费信息
if(sch.HaveChargeInfo(cStuId,cYear) == 1)
{
MessageBox("本学年已存在此学生收费信息,请选择补交费用!");
return;
}
// 得到专业收费项目信息
CChargeItem chi;
if (chi.HaveSpe(cSpe,cYear) == false)
{
MessageBox("无此学生所在专业收费信息,请在专业收费管理中补充收费信息!");
return;
}
// 得到此收费项目的总收费金额
m_total = chi.GetSum(cSpe,cYear);
// 得到项目编号
CString cItemId;
cItemId = chi.GetItemId(cSpe,cYear);
chi.GetInfo(cItemId);
m_pre = 0;
// 特困生的处理:免学费
if(stu.TeKun == "是")
{
m_total = m_total - chi.Tuition;
m_tuition = 0.0;
GetDlgItem(IDC_TUITION_EDIT)->EnableWindow(FALSE);
}
else
m_tuition = chi.Tuition;
// 不是住宿生处理:免住宿费
if(stu.ZhuSu == "否")
{
m_total = m_total - chi.DormFee;
m_DormFee = 0.0;
GetDlgItem(IDC_DORM_FEE_EDIT)->EnableWindow(FALSE);
}
else
m_DormFee = chi.DormFee;
// 不存车学生处理:免存车费
if(stu.CunChe == "否")
{
m_total = m_total - chi.BicycleFee;
m_BicycleFee = 0.0;
GetDlgItem(IDC_BICYCLE_FEE_EDIT)->EnableWindow(FALSE);
}
else
m_BicycleFee = chi.BicycleFee;
m_lack = m_total;
// 填写其他信息
m_incidental = chi.Incidental;
m_MacTimeFee = chi.MacTimeFee;
m_insurance = chi.Insurance;
m_BookFee = chi.BookFee;
m_FileFee = chi.FileFee;
m_cur = m_total;
UpdateData(FALSE);
GetDlgItem(IDOK)->EnableWindow(TRUE);
}
}
5.3.4 设计学生收费管理对话框
学生收费管理对话框ID为IDD_CHARGE_MAN_DIALOG,其布局如下:
当用户点击“补交”按钮时触发OnRechargeButton()函数,代码如下:
void CChargeManDlg::OnRechargeButton()
{
// TODO: Add your control notification handler code here
UpdateData(TRUE);
if (m_Adodc.GetRecordset().GetEof())
{
MessageBox("请选择要修改的记录!");
return;
}
// 如果费用都缴完,则不需要补交
if (atol(m_Datagrid.GetItem(31)) == 0)
{
MessageBox("费用已缴清,不用补交任何费用!");
return;
}
HTREEITEM node; // 用于保存当前节点
node = m_tree.GetSelectedItem(); //取得当前节点值
CString DepName;
DepName = m_tree.GetItemText(node);
CChargeEdiDlg dlg;
dlg.cId = m_Datagrid.GetItem(0);
dlg.cStuId = m_Datagrid.GetItem(1); // 学生编号
dlg.m_name = m_Datagrid.GetItem(2);
CSpeciality spe;
dlg.m_dep = spe.GetDepName(m_Datagrid.GetItem(1));
spe.GetInfo(m_Datagrid.GetItem(1));
dlg.m_speciality = spe.Name;
dlg.m_Year = atoi(m_Datagrid.GetItem(4));
dlg.m_total = atol(m_Datagrid.GetItem(29));
dlg.m_pre = atol(m_Datagrid.GetItem(30));
dlg.m_cur = atol(m_Datagrid.GetItem(31));
dlg.m_lack = atol(m_Datagrid.GetItem(31));
dlg.m_tuition = atol(m_Datagrid.GetItem(7));
dlg.m_incidental = atol(m_Datagrid.GetItem(10));
dlg.m_MacTimeFee = atol(m_Datagrid.GetItem(13));
dlg.m_insurance = atol(m_Datagrid.GetItem(16));
dlg.m_DormFee = atol(m_Datagrid.GetItem(19));
dlg.m_BicycleFee = atol(m_Datagrid.GetItem(22));
dlg.m_BookFee = atol(m_Datagrid.GetItem(25));
dlg.m_FileFee = atol(m_Datagrid.GetItem(28));
dlg.m_memo = m_Datagrid.GetItem(32);
if (dlg.DoModal() == IDOK)
RefreshData();
}
程序从表格中获取当前学生的已交费数据,赋值到学生收费编辑对话框中,供用户输入学生的剩余收费金额。
当用户点击“交费”按钮时,与补交的代码相似,只是这里要将学生收费编辑对话框的各项收费金额设置为0。
查询统计模块可以实现以下功能:
- 日详细查询表。
- 月收费统计表。
5.4.1 设计按日详细查询表对话框
日详细查询表可以按日查询所有学生的各项收费信息。对话框ID为IDD_TIME_SEL_DIALOG,对话框的布局如下:
函数RefreshData()的功能是根据用户选择的日期设置ADO Data控件的数据源,从而决定在DataGrid控件中显示的学生收费记录,代码如下:
void CChargeSelDlg::RefreshData()
{
UpdateData(TRUE);
CString cTime;
cTime = m_time.Format("%Y-%m-%d");
// 设置SELECT语句
CString cSource = "SELECT (d1.DepName+'-'+d.DepName) AS 院系名称,s.Name AS 学生姓名,"
"c.iYear AS 学年, c.Should_Tuition AS 应交学费, c.Fact_Tuition AS 实交学费, "
"(c.Should_Tuition-c.Fact_Tuition) AS 所欠学费,"
" c.Should_Incidental AS 应交杂费, c.Fact_Incidental AS 实交杂费,"
"(c.Should_Incidental-c.Fact_Incidental) AS 所欠杂费,"
" c.Should_MacTimeFee AS 应交机时费, c.Fact_MacTimeFee AS 实交机时费,"
"(c.Should_MacTimeFee-c.Fact_MacTimeFee) AS 所欠机时费,"
" c.Should_Insurance AS 应交保险费, c.Fact_Insurance AS 实交保险费,"
"(c.Should_Insurance-c.Fact_Insurance) AS 所欠保险费,"
" c.Should_DormFee AS 应交住宿费, c.Fact_DormFee AS 实交住宿费,"
"(c.Should_DormFee-c.Fact_DormFee) AS 所欠住宿费,"
" c.Should_BicycleFee AS 应交存车费, c.Fact_BicycleFee AS 实交存车费,"
"(c.Should_BicycleFee-c.Fact_BicycleFee) AS 所欠存车费,"
" c.Should_BookFee AS 应交书费, c.Fact_BookFee AS 实交书费,"
"(c.Should_BookFee-c.Fact_BookFee) AS 所欠书费,"
" c.Should_FileFee AS 应交其它费用, c.Fact_FileFee AS 实交其它费用, "
"(c.Should_FileFee-c.Fact_FileFee) AS 所欠其它费用,"
" c.Should_Total AS 应交合计, c.Fact_Total AS 实交合计,"
"(c.Should_Total-c.Fact_Total) AS 所欠合计"
" FROM StuCharge c, Student s ,DepInfo d,DepInfo d1 "
" WHERE c.StuId = s.StuId AND s.DepId=d.DepId And d.UID=d1.DepId "
" And c.ChargeDate = '" + cTime + "'";
CString cDepId;
cDepId.Format("%d", iDepId);
if(cDepId != "0")
cSource += " AND s.DepId = " + cDepId;
m_Adodc.SetRecordSource(cSource);
m_Adodc.Refresh();
_variant_t vIndex;
vIndex = long(0);
m_Datagrid.GetColumns().GetItem(vIndex).SetWidth(120);
for(int i=1; i<30; i++)
{
vIndex = long(i);
m_Datagrid.GetColumns().GetItem(vIndex).SetWidth(60);
}
}
程序根据选择的院系信息和收费日期设置SELECT语句,查询指定院系中所有学生在选择日期的收费项目、已收费信息和欠费信息。
5.4.2 设计按月收费统计表对话框
月收费统计表可以按月份统计各学生收费项的总金额。月收费统计表对话框的ID属性为IDD_CHARGE_STAT_DIALOG,其布局如下:
按月统计对话框与日详细查询表对话框相似。
根据用户类型的不同,用户管理模块的功能也不相同。可以包含以下情形:
- 系统管理员用户可以创建其他用户、修改用户密码和删除其他用户。(除happyzero用户不可修改外)
- 普通用户只能修改自身的用户信息。
5.5.1 设计用户编辑对话框
用户编辑对话框ID为IDD_USER_EDIT_DIALOG,其布局如图5.51所示:
编辑用户信息的对话框可以用来添加和修改用户信息,对应的类为CUserEditDlg,对话框中的代码与院系信息编辑相似。
5.5.2 设计用户管理对话框
用户管理对话框ID为IDD_USER_MAN_DIALOG,其布局如下:
该对话框与院系信息管理相似。
5.5.3设计修改密码对话框
修改密码对话框ID为IDD_USER_MAN_DIALOG,其布局如下:
当用户点击“确定”按钮时,触发OnOK()消息响应函数,代码如下:
void CPasswdDlg::OnOK()
{
// TODO: Add extra validation here
UpdateData(TRUE);
//检查数据有效性
if (m_old == "")
{
MessageBox("请输入旧密码");
GetDlgItem(IDC_OLD_EDIT)->SetFocus();
return;
}
if (m_new == "")
{
MessageBox("请输入新密码");
GetDlgItem(IDC_NEW_EDIT)->SetFocus();
return;
}
if (m_confirm != m_new)
{
MessageBox("两次输入的新密码不同");
m_new = "";
m_confirm = "";
UpdateData(FALSE);
GetDlgItem(IDC_NEW_EDIT)->SetFocus();
return;
}
CUserInfo cur;
cur.GetInfo(m_user);
if (cur.Passwd != m_old)
{
MessageBox("用户密码不正确!");
m_old = "";
UpdateData(FALSE);
GetDlgItem(IDC_OLD_EDIT)->SetFocus();
return;
}
cur.Passwd = m_new;
cur.SqlUpdate(m_user);
MessageBox("密码修改成功,下次登录请使用新密码");
CDialog::OnOK();
}
程序对用户输入的密码进行验证,包括:
- 旧密码是否为空;
- 新密码是否为空;
- 两次输入的新密码是否相同;
- 旧密码是否通过密码验证。
通过上述验证后,程序将更新用户密码。
本章介绍了系统各个模块的详细设计及程序实现,演示了各个子功能的具体程序界面及一些关键程序代码,主要涉及到一些MFC对话框的调用实现,具体代码可参看程序部分。
总 结
回顾该系统从选题,到调研、需求分析、设计、编码实现、测试等,论文的撰写,这系列连贯的过程,感想颇多,收获也很多。大致的总结几点,如下:
1.设计开发一个软件,首先要了解所涉及到行业的现状,一定要想办法进行行业调研,从不同途径获取该行业的专业知识。合理安排系统设计开发时间,进行实际开发会发现需求分析与系统设计占主要的时间,如果前面的工作做好了,编码实现占用的时间很短。所以我们要借助软件工程的思想---对软件开发过程进行控制。
2. 在开发一个系统时,前期的分析准备工作非常的重要。对于系统的需求要明确,这样才能做到有的放矢。特别是对于系统中数据的存储和重要算法的设计,设计的不好会出现大量的冗余,不仅浪费存储空间,还影响查询效率。
3.在设计的初期,对一个功能模块的设计,要想出两种或两种以上方案,这样可以进行比较,从而获得更佳的设计方案,设计开发过程中一定要吸取经验教训,避免返工。尤其是一些重要算法或功能。在对具体实现功能模块进行编程时,要养成良好的加注释的习惯,这样的话不仅能理清思路,而且可以为以后的调试程序带来很大的方便。
5. 懂得充分利用网络资源,因为实地调研的时间是非常有限的,所以需求分析中大部分分析结构都是在我阅读专业书籍,网络资料后得出的。由于在外面工作以及其他原因,我无法借到专业书籍,只好在浩瀚的网络中寻找我所需要的资料,这无形当中培养了我通过网络获取资料信息的能力。
由于本人是初次开发系统,在知识、经验方面都存在着不足。另外,在整个开发的过程中,时间也比较仓促。因此,该系统必然会存在一些缺陷和不足。谨请大家提出宝贵的意见。
参考文献
-
- 萨师煊,王珊.数据库系统概论[M].北京:高等教育出版社,1983
- 求是科技.SQL Server 2000数据库开发技术与工程实践[M].北京:人民邮电出版社,2005
- 侯太平,董爱红.Delphi数据库编程[M].北京:清华大学出版社,2004.6
- 周绪.SQL Server2000中文版入门提高[M].北京:清华大学出版社,2003
- 王华. Delphi 5编程实例与技巧[M].北京:机械工业出版社,2000
- 龙劲松,王俞. Delphi 6 开发人员指南[M].北京:机械工业出版社,2003
- 徐谡,张雷.Delphi应用与开发安全教程[M].北京:清华大学出版社,2005.1
- 王业龙,范朝辉,李育龙.Delphi软件工程案例精解[M]. 北京:电子工业出版社,2005.1
- 黄晓敏,陈强.Delphi程序设计基础[M].北京:清华大学出版社,2004.9
- 赵晓玲.可视化程序教程[M].北京:机械工业出版社,2005.1
- 乔林,杨志刚. MFC与多线程编程『M』. 北京: 中国铁道出版社, 2000.
- 吴金平. Visual C++6.0编程与实践『M』. 北京: 中国水利水电出版, 2004
- 谢歆,赵国华. Visual C++ 高级编程实例精解『M』. 北京: 国防工业出版社, 2001
- David J.Kruglinski Scot Wingo. Microsoft Visual C++ 6.0技术内幕『M』. 北京: 清华大学出版社, 2001
- 李松,汤庸,徐海水,等. 最新Visual C++6.0程序设计教程『M』. 北京: 冶金工业出版社, 2001
- 夏邦贵,刘凡鑫. Visual C++数据库开发实例精解『M』. 北京: 机械工业出版社, 2006
- 张力. Visual C++高级编程『M』. 北京: 人民邮电出版社, 2002
- 陆永祯,段慧坤,周芸.SQL Server 2000数据库管理和程序开发一周通『M』.北京:中国铁道出版社,2005
- Jon Bates,Tim Tompkins.实用Visual C++ 6.0『M』.北京:清华大学出版社,2000
- Charles Petzold. Programming Windows『M』. New York : Microsoft Press, 1998.
- (美)Marco Cantu.Delphi7从入门到精通[M]. 北京:电子工业出版社,2003.10
- (美)Clay Shannon.Delphi程序调试参考手册[M]. 北京:中国电力出版社,2003.9
致 谢
在论文即将完成之际,本人在此向所有关心我的及帮助我的老师和同学们致以最真诚的感谢。
在本次毕业设计中,我从指导老师--------*老师,身上学到了很多东西。她认真负责的工作态度,严谨的治学精神和深厚的理论水平都使我收益匪浅。他无论在理论上还是在实践中,都给与我很大的帮助,使我得到很大的提高,这对于我以后的工作和学习都有一种巨大的帮助,在此感谢她耐心的辅导。在撰写论文阶段,*老师审阅我们的论文,提出了许多宝贵意见,没有她的指导,我就不能较好的完成课题设计的任务。
另外,我还要感谢在这几年来对我有所教导的老师,他们孜孜不倦的教诲不但让我学到了很多知识,而且让我掌握了学习的方法,更教会了我做人处事的道理,在此表示感谢。同时,在系统开发过程中还有其他的同学也给了我不少帮助,这里一并表示感谢。