分类

课内:
不限
类型:
不限 毕业设计 课程设计 小学期 大作业
汇编语言 C语言 C++ JAVA C# JSP PYTHON PHP
数据结构与算法 操作系统 编译原理 数据库 计算机网络 软件工程 VC++程序设计
游戏 PC程序 APP 网站 其他
评分:
不限 10 9 8 7 6 5 4 3 2 1
年份:
不限 2018 2019 2020

资源列表

  • 基于rabbitMQ的微服务架构消息组件设计与实现

    摘要随着互联网的高速发展,淘宝、微信、12306和各类直播平台等各种规模庞大、高频率、高峰值的应用层出不穷,而且在一些特殊节点,比如淘宝的双十一购物节、12306的春运抢票高峰等,系统的访问量更是日常流量的几万甚至千万倍。为了支撑起如此庞大的性能需求,迫切需要各种层面的配合。
    传统的单体式架构,虽然易于开发、部署和扩展,但是存在很多的弊端,特别是应用变大或者团队扩大,这些弊端将更为明显和严重,对企业的长远发展是十分不利的。
    由此,技术人员在2012年提出了微服务的概念,指明组件化和服务化是系统架构发展的必然趋势,虽然在一定程度上增加了前期开发的复杂度,提高了部署的技术难度,但是可以为企业的长远发展建立坚实的基础,其利益远大于弊端。因此,研究微服务系统架构的优势和核心要点,探索rabbitMQ技术在大型业务系统中的应用,对于开发低延迟、高并发、高性能、高可用、高可靠的应用系统很有辅助作用。
    基于上述背景,本毕业设计在深入理解微服务架构的技术背景、实现原理与优缺点的前提下,对微服务架构中各个组件之间基于消息队列的异步通信模式进行了技术研究,以典型的消息队列rabbitMQ为主要技术手段,以时下流行的轻量级框架spring boot为容器,借助java开发语言,实现了微服务架构常用的消息中间件。
    论文首先对所用技术进行了理论研究与概述,然后利用以上技术实现了三种应用场景下的应用组件,分别是邮件服务器、复杂业务拆分和分布式任务处理;该组件可与业务应用集群通过消息队列进行通信,作为一个独立的模块帮助业务系统完成一些功能,减轻服务器压力。
    【关键词】rabbitMQ springboot 微服务 消息中间件
    AbstractWith the rapid development of the Internet,various large-scale, high-frequency, and high-peak applications such as Taobao,WeChat, 12306, and various types of live broadcast platforms emerge in anendless stream, and at certain special nodes, such as Taobao’s Double 11thShopping Festival and 12306 Spring Festival. To grab the peak of votes, thesystem’s traffic is tens of thousands or even ten million times the dailytraffic. In order to support such a large amount of performance requirements,there is an urgent need for various levels of cooperation.
    Although the traditional single-unitarchitecture is easy to develop, deploy, and expand, it has many drawbacks,especially if the application becomes large or the team expands. Thesedrawbacks will become more apparent and serious, which is very unfavorable tothe long-term development of the company.
    As a result, the technicians proposed theconcept of microservices in 2012, indicating that componentization and serviceare inevitable trends in the development of system architecture. Although thecomplexity of pre-development has been increased to some extent, and thetechnical difficulty of deployment has been improved, It can establish a solidfoundation for the long-term development of the company, and its benefits faroutweigh the disadvantages. Therefore, to study the advantages and core pointsof microservice system architecture, and to explore the application of rabbitMQtechnology in large-scale business systems, it is helpful to develop lowlatency, high concurrency, high performance, high availability, and highreliability of application systems.
    Based on the above background, under thepremise of in-depth understanding of the technical background, implementationprinciple, and advantages and disadvantages of the microservice architecture,this graduation design conducted a technical research on the asynchronouscommunication model based on the message queue among the various components inthe microservice architecture. The message queue rabbitMQ is the main technicalmeans. With the popular lightweight framework spring boot as a container, withthe help of the Java development language, the message middleware commonly usedin the microservice architecture is realized.
    The paper firstly studies and summarizes thetechnologies used, then uses the above technologies to implement theapplication components in three application scenarios, namely, mail server,complex service splitting, and distributed task processing; this component canbe used with business application clusters. The message queue communicates asan independent module to help the business system perform some functions andrelieve server pressure.
    [key words] rabbitMQ springboot message queue light server
    1 引言1.1 选题背景及论文意义近年来随着互联网的高速发展,淘宝、微信、12306和各类直播平台等各种规模庞大、高频率、高峰值的应用层出不穷,而且在一些特殊节点,比如淘宝的双十一购物节、12306的春运抢票高峰等,系统的访问量更是日常流量的几万甚至千万倍。为了支撑起如此庞大的性能需求,迫切需要各种层面的配合。自此微服务系统架构和作为中间件定位的消息队列应用应运而生,被广泛运用于消息分发、分布式服务调用、高峰值流量访问等场景。
    传统的单体式架构中,所有的核心和边缘业务均集中在一个工程中,依靠一个应用就能完成所有的业务和功能,其优势在于易于开发、易于部署、易于扩展,单体式应用的开发仅依靠IDE就能实现,开发完成后将工程打包为war或者jar即可在合适的环境中运行,使用负载均衡技术即可实现应用多副本的性能扩展。
    但是单体式应用在具备这些优势的同时,也有很多的弊端,一旦应用变大或者团队扩大,这些弊端将更为明显和严重。主要体现在以下几个方面:(1)代码量巨大,当团队成员变更时不利于新成员快速理解和修改代码;(2)资源消耗量大,启动时间长,特别是当系统业务功能扩展使得规模扩大时,仍作为一个整体在web容器中运行,资源消耗与启动时间尤为明显,且生产效率和部署效率大大降低;(3)持续部署与应用扩展困难,难以进行规模化开发,同时技术栈长时间不更新等各种问题,企业的长远发展是十分不利的。
    在上述背景下,技术人员在2012年提出了微服务的概念,指明组件化和服务化是系统架构发展的必然趋势,虽然在一定程度上增加了前期开发的复杂度,提高了部署的技术难度,但是可以为企业的长远发展建立坚实的基础,其利益远大于弊端。因此,研究微服务系统架构的优势和核心要点,探索rabbitMQ技术在大型业务系统中的应用,可以学习到如今的技术热点,对于开发低延迟、高并发、高性能、高可用、高可靠的应用系统很有辅助作用。
    1.2 系统现状与发展分析消息队列的概念在互联网尚未发展成熟的早期已经被提出,Microsoft用消息队列作为消息处理技术,实现两台计算机之间的通信。目前基于消息队列的技术产品非常丰富,具体包含以下几种类型:为企业应用而设计的企业型消息队列,具体产品有ActiveMQ、HornetQ、rabbitMQ;互联网型的消息队列,产品有Kafka,RocketMQ;轻便型消息队列,有memcacheq、Redis、zeroMQ等。随着微服务架构的流行,作为一种优秀的应用程序间通信技术,消息队列得到了更广泛的应用。
    作为一项在云中部署应用和服务的新技术,微服务已成为当下最新的热门话题。微服务这一概念出现于2012年,目前正处于发展初期,对于使用微服务架构的利与弊,各界IT技术人员各执一词,但无论存在多大的争议,越来越多的企业和开发者开始倾向于基于微服务思想去构建业务系统框架,并取得了良好的效果,诸如Amazon、Netflix以及Git等,都是应用微服务架构的成功案例。在国内,各大互联网巨头如阿里、百度、华为等,都是云服务和微服务架构的先驱,其中,阿里巴巴的电商平台——淘宝是最典型的微服务和中间件应用实例。其消息队列服务于整个阿里巴巴集团已超过10年,历经了阿里巴巴交易核心链路反复打磨与历年双十一购物狂欢节的严峻考验。在淘宝的整个微服务架构中,交易是系统的核心域,物流、支付等是系统的支撑子域,短信服务、会员系统、积分系统等是通用子域(如图1-1所示)。各个领域之间寻找到内聚和耦合的合理平衡点,所有的服务都在云中注册和互相发现,支撑起庞大的业务流量需求。

    总之,中间件和微服务作为新型的架构思想,将给IT行业带来巨大的冲击,虽然也存在诸多不足,但整体来看目前的发展趋势是十分乐观的,对于IT技术人员来说,无疑是一个必须研究的课题,也是一个挑战,是每个IT企业应该纳入技术库的一种架构。
    1.1 论文主要内容及组织结构本毕业设计题目是《基于rabbitMQ的微服务架构消息组件设计与实现》。在深入理解微服务架构的技术背景、实现原理与优缺点的前提下,对微服务架构中基于消息队列的异步通信模式进行了技术研究,以典型的消息队列rabbitMQ为主要技术手段,以时下流行的轻量级框架spring boot为容器,以java为主要开发语言,实现了微服务架构常用的消息中间件。论文的组织结构如下安排:
    本文第一部分对选题背景、论文意义、现状与发展分析、论文组织架构等进行概括,阐述使用微服务架构与传统架构各自的思想异同点与优缺点;第二部分对本设计所使用的关键技术进行介绍,包括编程语言、数据库、技术框架等,重点对rabbitMQ、Spring Boot和微服务架构的概念、应用场景、技术原理等进行了详细的说明;第三部分为系统的需求分析,本设计实现的是基于rabbitMQ的消息中间件,因此,我们以邮件服务器、复杂业务拆分和分布式任务处理等rabbitMQ常见的三个应用场景为模拟对象,分别对这三个场景进行需求描述;第四部分为系统功能设计,阐述消息中间件系统的整体功能模块、上述三个rabbitMQ应用场景的具体功能设计以及系统数据库结构设计;第五部分阐述具体的功能实现,并展示毕业设计成果,对其中的关键功能点进行功能描述,以页面截图和关键代码作为支撑。第六部分为总结,对本系统的优势与不足进行分析总结,并提出今后的优化方向与理论方案。
    论文的具体组织结构如下图1-2所示:

    2 关键技术介绍本章对消息中间件所采用的技术进行初步的介绍,组件采用java为主要开发语言,使用SpringBoot作为开发框架,mysql作为数据库,mybatis为持久层框架,消息中间件与应用集群中的其他组件之间的通讯使用消息队列rabbitMQ。
    作为目前使用最广泛的高级开发语言,java语言具备丰富API和成熟的企业应用开发框架;mysql是一个开源的RDBMS(即关系型数据库管理系统);SpringBoot是一个用于快速搭建Spring应用的全新框架;mybatis是一个优秀的持久层开源框架,支持普通的sql查询、存储过程和高级映射;rabbitMQ是一个基于AMQP的可复用的企业消息系统。这些在以下章节中将分别对其进行具体介绍。
    2.1 系统基础技术介绍2.1.1 mysql简介mysql是一个被广泛使用的开源RDBMS(关系型数据库管理系统),mysql自身所用的SQL语言语法是最常用的访问数据库的标准化语言。mysql采用了双授权政策,分为社区版和商业版,相比于oracle 和sql Server昂贵的费用和超大的内存占用,mysql由于总体拥有成本低、体积小、速度快,尤其是开放源码,是中小型web网站数据库的首选,尤其受个人开发者的青睐。
    2.1.2 spring mvc简介Spring MVC框架是当今在web开发中应用最广泛的开源Java框架,为开发强大、高性能的Web应用程序提供了坚固的全面的技术支持。并且非常容易上手开发。SpringMVC框架采用MVC(模型 - 视图 - 控制器)架构和松散耦合的Web应用程序组件。MVC模式使得应用程序的输入逻辑,业务逻辑和UI逻辑分离,同时让这些层次之间松散耦合。模型(Model)封装应用程序数据,视图(View)进行视图解析渲染模型数据,渲染出用户所能看到的页面,控制器(Controller)负责处理分发用户请求,并将数据传递给视图进行渲染。
    SpringMVC具备以下几个特点:明确的角色划分,高复用的代码,简单的配置方式,高定制的绑定、验证,灵活的model转换,可适配非侵入。其功能非常强大,能够设计出结构清晰的层次,同时其天生与Spring框架集成(具有IOC容器,AOP等特点),提供使用方便的格式、和数据绑定机制,强大的约定大于配置的契约式编程支持,灵活的数据验证,支持restful风格等。
    2.1.3 mybatis介绍MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJO映射成数据库中的记录。在开发过程中使用mybatis generator工具根据数据库内容生成实体对象和映射文件,大大减轻了手动书写类似代码的工作量。
    2.2 SpringBoot框架介绍2.2.1 SpringBoot简介Spring Boot是近年来非常流行的框架,它的设计目的是简化传统Spring项目繁琐的配置过程和开发过程。在以前传统的Spring项目中,一般来说都是使用applicationContext.xml来配置项目,对于小白性的开发者来说,常会因为配置的纰漏而导致项目无法启动,Spring Boot的出现则解决了这些传统弊病。该框架使用特定的方式来配置,从而让开发人员免除繁琐的配置工作。简单来说Spring Boot就是传统Spring 框架的进阶简介版,就像maven整合管理了大量的jar包,spring boot整合了spring 旗下的各种框架。使用此框架则可以让开发者将更多的时间放于业务开发上,而不是配置搭建上。
    2.2.2 框架应用场景与优势Spring Boot脱胎于传统的Spring框架,自然具备其优秀基因,而且具有快速搭建的特性,可以让开发者快速开发、搭建出一个Web应用。Spring由于其繁琐的配置,一直被开发者吐槽,各种繁琐的XML配置让人眼花,而且配置错误很难定位。因此使用spring boot自带的Java Config方式对Spring进行配置的Spring Boot是传统spring 框架的一大进化。一般被作为微服务的首选框架,只要是Web后端都可以考虑使用用SpringBoot框架。
    总体来说,SpringBoot框架使配置、编码、部署、监控变简单,如图2-1到图2-3所示。

    虽然Spring Boot优势这么亮眼,但是仍有着一些不足,SpringBoot如今仍是一个微框架,离健全的微服务还有一定的距离。譬如,没有提供相应的服务发现和注册的配套功能、监控功能不全。没有健全的相应的安全权限控制方案,对于REST风格的支持,还要自行结合项目实际情况进行规范化工作。
    通过以上对SpringBoot的简单分析,可以看到SpringBoot对于Java快速开发是有帮助的,因为其简化了开发过程配置过程部署过程和监控过程;但仅仅只有SpringBoot并不足以开发一个完整的大型应用,还需要与外围框架进行集成,尤其是在服务发现和注册,认证鉴权和监控等方面。
    2.3 rabbitMQ介绍2.3.1 概念、原理与特点RabbitMQ是一个由erlang开发的AMQP的开源实现,是消息队列的一种,可以实现应用程序之间的数据传输和通信。不同于传统的远程过程调用,通过使用消息队列MQ,应用程序通过读写出入队列的消息来进行通信。
    AMQP (AdvancedMessage Queue),即高级消息队列协议。它是应用层协议的一个开放标准,为面向消息的中间件设计,基于此协议的客户端与消息中间件可传递消息,并不受产品、开发语言等条件的限制。
    消息(message)是指在应用程序间传递的数据。消息可以是简单地只包含文本字符串,也可以包含复杂的实体嵌入对象。消息队列(Message Queue)是一种应用间的通信方式,消息写入消息队列后,应用程序可以立即返回,接下来交由消息系统来确保消息的可靠传递。也就是说,消息发布者只需要把消息发布到 MQ 中而不用管谁来取,消息使用者只管从 MQ 中取消息而不管是谁发布的。
    RabbitMQ在易用性、扩展性和高可用性等方便具备极大的优势,常用于在分布式系统中存储转发消息,其特点有可靠性(reliability)、灵活路由(flexible routing)、消息集群(clustering)、高可用(high available queues)、多种协议(muti-protocol)、多语言客户端(many-clients)、管理界面(management UI)、消息跟踪机制(tracing)、插件机制(plugin system)等。
    所有MQ产品从模型抽象上来说都是如图2-4所示的过程:生产者(producer)生成消息,发布到队列(queue)中,通过路由(exchange)机制将消息发送到监听的消费者,进行消息的下一步处理。

    如图2-5为RabbitMQ的内部实现,其中的专有名词解释如下:

    1.message
    消息,消息是不具名的,它由消息头和消息体组成,是消息队列传输的数据对象。
    2.publisher
    消息的生产者,向消息队列中写入消息的客户端应用程序。
    3.exchange
    交换器,用来接收生产者发送的消息并将这些消息路由给服务器中的队列
    4.binding
    绑定,用于消息队列和exchange之间的关联。一个binding就是基于路由键(routing key)将exchange和mq连接起来的路由规则,所以可以将exchange理解成一个由binding构成的路由表。
    5.queue
    消息队列,是消息的容器,也是消息的终点。一个消息可投入一个或多个队列。消息一直保留在队列中,直到消费者连接到这个队列将其取走。
    6.connecting
    网络连接,比如一个TCP连接。
    7.channel
    信道,多路复用连接中的一条独立的双向数据流通道。信道是建立在真实的TCP连接内的虚拟连接,AMQP 命令都是通过信道发出去的,不管是发布消息、订阅队列还是接收消息,都是通过信道完成。因为对于操作系统来说建立和销毁 TCP 连接开销是非常巨大的,所以引入信道的概念,以复用一条 TCP 连接。
    8.customer
    消息的消费者,从消息队列中读取消息的客户端应用程序。
    9.virtual host
    虚拟主机,表示一批交换器、消息队列和相关对象。虚拟主机是共享相同的身份认证和加密环境的独立服务器域。每个 vhost 本质上就是一个 mini 版的RabbitMQ 服务器,拥有自己的队列、交换器、绑定和权限机制。vhost 是 AMQP 概念的基础,必须在连接时指定,RabbitMQ 默认的 vhost 是 /。
    10.broker
    表示消息队列服务器实体,它提供一种传输服务,维护一条从生产者到消费者的路线,保证数据能按照指定的方式进行传输。
    2.3.2 应用场景与实例从上面的描述中可以看出,消息队列是一种实现应用间异步协作的通信机制,常见的应用场景有如下三种:
    1. 业务异步处理
    通常,用户登录注册成功需要发送短信和邮件通知,有如下三种方式,(1)串行方式,如图2-6所示,首先将注册信息写入数据库,然后发送注册邮件,最后发送注册短信,上述三个任务全部成功完成后返回客户端。显然,这种方式存在一个问题,相对注册功能而言,发送短信和邮件并不是必须的,它只是一个通知,不涉及注册业务,使得客户端等待没有必要等待的东西。(2)并行方式,如图2-7所示,注册信息写入数据库后,发送短信的同时发送邮件,同样上述三个任务完成后返回客户端,降低了客户端等待的时间。(3)消息队列处理通知消息方式,如图2-8所示,注册信息写入数据库之后立即返回,客户端的响应时间即为注册处理所需时间,而与注册功能无关的邮件短信通知直接写入消息队列,由消息队列消费者异步进行数据持久化和邮件短信发送等进一步处理。
    比较这三种处理方式,显然使用消息队列的方式,客户端相应速度是串行的三倍,是并行的两倍。
    串行方式

    并行方式

    使用消息队列处理通知消息

    2.应用解耦
    所谓应用解耦,是将系统庞大的业务拆分成各自独立的模块,每个模块在单独的进程中对外提供自身的服务,各模块之间以rabbitMQ等消息机制进行通信,合作完成系统整体功能。以某电商系统的双11购物狂欢节为例,用户下单之后,订单系统需要将订单信息通知库存系统以更新商品库存以及进行后续的物流配送,传统的做法要么采用串行方式,将订单处理和库存处理代码逻辑写在同一个系统中;要么如图2-9所示,让订单系统调用库存系统的接口。串行方式使得业务逻辑复杂,代码不易维护,而且一个环节出错将导致整体操作回滚;而订单系统调用库存接口的方法又使得订单系统和库存系统存在耦合现象,当订单系统出现故障时,无法访问库存服务,导致下单业务失败的局面。

    引入消息队列机制后,订单系统与库存系统使用MQ进行通信,如图2-10所示,订单系统作为生产者,库存系统作为消费者,当用户下单后,订单系统将消息写入消息队列,返回用户下单成功;而库存系统订阅订单系统消息,获取下单信息,进行库存信息处理,使用这种方式,即使库存系统出现故障,消息队列也能保证订单消息的可靠投递,不会导致消息丢失。

    3.流量削峰
    流量削峰即使用消息队列存取机制进行访问流量控制,减轻服务器压力,一般在定时活动如秒杀抢购中应用广泛。在秒杀活动中,一般会因为访问量过大导致系统因性能问题而发生故障,此时若使用消息队列,就可以达到控制流量的目的。如图2-11所示,服务器收到用户的请求之后,将数据写入消息队列,消息数量达到最大值之后则直接抛弃消息或跳转到错误页面,秒杀业务系统根据队列中的请求信息做后续的业务处理,如此一来,首先可以控制活动人数,超过一定阈值的的订单直接丢弃,其次可以缓解短时间内的超大流量压垮应用。

    2.4 微服务架构介绍2.4.1 微服务架构概念微服务构架是近年来提出的新构架。它最早由MartinFowler与James Lewis于2014年共同提出。微服务即是“微”的服务,相比单体式系统构架,将原本庞大的功能拆分为多个独立不同的微小服务,使整个系统划分成多个不同的互相独立的小颗粒点。
    目前,微服务概念并没有一个统一的说法,其范围比较广泛,以此为基础的各种框架也不都相同。即使现在微服务还是新兴概念,但已经成为软件开发的趋势,成为各大互联网公司、软件开发公司首选的基础构架。
    微服务有如下几大特性,服务组件化、按业务团队组织、智能端点与哑管道(服务调用方式、实时、异步中间件)、去中心化治理(组件能根据不同的业务特点选择不同的技术平台)、基础设施自动化、演进式设计、容错设计。
    2.4.2 微服务架构的优势根据微服务架构的概念描述,独立部署、扩展性强,是微服务架构的明显优势。具体来说,首先,通过将巨大单体式应用分解为多个服务,解决了复杂性问题。应用被分解为多个可管理的分支和服务,每个分支负责一个功能模块,各自有清晰的边界,整个系统模块化。不仅如此,单个服务更容易开发和维护,而且可以被复用,每个服务都可以由专门的团队来开发,使得开发者自由选择所用的技术。再者,微服务架构中的每个组件独立部署,不再需要协调其它服务部署对本服务的影响,可以使得部署更快速。最后,微服务架构模式使得每个服务独立扩展,可以灵活地根据需求的规模来部署每个服务,甚至硬件资源。
    3 系统需求分析3.1 系统简介本系统并不是实现一个具体的业务功能,而是作为一个组件,集成于各类应用中,通过rabbitMQ与其他应用通信,从队列中读取数据进行数据持久化和完成特定的功能。消息中间件的功能很多,本系统以其中的几种应用场景为模拟对象,来说明消息中间件的工作原理和能完成的功能。实现的应用场景包括“邮件服务器”、“复杂业务拆分”和“分布式任务处理”,将在以下章节进行具体的场景和功能介绍。
    3.2 应用场景描述3.2.1 邮件服务器消息中间件可用于邮件服务器,通过队列监听业务系统的邮件消息,进行本地数据解析(包括参数和模板的数据渲染)和邮件发送,并记录发送日志。同时,为了实时适配业务系统的需求变更,邮件服务器提供邮件模板管理(增删改查)和日志查询功能,便于监控和管理。
    3.2.2 复杂业务拆分当一个系统很复杂时,可以使用消息中间件技术进行拆分,使用消息队列进行通信,实现业务的异步处理,提高容错率。例如在电商系统中,用户购买商品涉及订单系统、库存系统和物流系统,这些系统如果全部在一套代码中实现业务处理,那么无论是效率、容错率,还是用户体验,都达到理想的效果。因此,可以将订单系统、库存系统、物流系统拆分成单个的业务组件,约定好数据的传输格式,订单系统仅处理订单信息,库存系统仅根据订单系统传递的客户下单数据更新商品库存,物流系统仅根据库存系统传递的收货数据处理配送信息,那么整个系统将形成一个业务流水线,各个组件各司其职。
    3.2.3 分布式任务处理在规模较大的系统中,由于用户量大,经常会出现流量高峰时系统性能瓶颈的问题,如果将所有的访问压力集中在一个系统中,无疑会带来性能的压力。此时将事物处理拆分出来,并复制出多个节点,使用各自的消息队列与主系统进行通信,主系统将用户请求随机地通过消息队列分发到各个处理系统节点,由处理系统进行分布式数据处理。
    3.3 系统需求分析为了更好地说明消息中间件的典型应用场景,本系统将通过简单的业务处理去模拟在实际业务系统中消息中间件需要实现的功能和扮演的角色,并提供各类前端页面便于演示和可视化。以下为上述应用场景中的具体需求描述,其中3.3.1是属于消息中间件系统整体的需求,3.3.2是数据消息中间件作为邮件服务器的需求,3.3.3是系统作为抽离库存系统的需求,3.3.4是多队列监听处理大量任务的需求。
    3.3.1 系统整体需求描述消息组件涉及较多的资源,因此消息中间件需要配置一个系统管理用户来维护,一方面以适应实时适配业务变更的需求,另一方面管理和监控整个系统资源。
    3.3.2 邮件服务器需求描述在邮件服务场景中,邮件服务器需要承担主业务系统中的邮件消息解析和发送功能,并记录发送日志,提供邮件模板的增删改查。具体需求如下:
    1.邮件消息监听
    邮件服务器中,需要使用rabbitMQ队列与其他业务系统进行通信,对业务系统的邮件消息进行监听,获取邮件发送相关数据,并持久化存入数据库。
    2.邮件内容解析
    rabbitMQ队列监听器接收到消息后,根据模板名找到模板内容,并使用视图解析技术将模板与数据进行渲染,合成邮件内容,持久化存入数据库,等待发送。
    3.邮件发送
    邮件服务器需要实现邮件发送功能,通过集成java mail API将解析后的邮件内容封装成真正的邮件消息实体,发送到应用系统在消息数据中封装的收件人邮箱地址。
    4.邮件模板管理
    邮件服务器中的模板数据是随着业务系统的需求变更而变更的,当业务系统中邮件内容发生变化时,邮件服务器中模板也要进行更新。因此邮件服务器中要提供邮件模板管理功能,系统管理员需要对邮件模板进行增删改查,适应业务变化。
    5.邮件发送日志监控
    为了方便运维人员对邮件发送情况进行监控,需要提供邮件发送日志查询功能。需要将所有的邮件发送流水持久化到数据库表中,同时数据库中需要记录发件箱地址、发件人昵称、收件人邮箱、发送时间、主题、模板名、传入消息参数、邮件内容解析结果、解析后邮件内容、发送结果等信息,从而便于系统管理员查看邮件发送情况,及时解决线上问题。
    3.3.3 复杂应用拆分需求描述复杂应用拆分是指将大型的业务系统拆分成独立运行的子系统,各个子系统各司其职完成完整的业务处理。以从电商系统抽离库存系统为例,将库存业务从大型的电商业务中抽离出来,作为一个独立的业务处理模块对其他业务模块提供服务。抽离的库存系统需提供商品库存查询的功能,并对订单系统中的订单消息进行监控,根据订单信息更新商品库存。当用户下单成功后,订单系统给库存系统发送一个消息,告知库存系统某商品成功售出多少,然后由库存系统进行库存数据处理。
    3.3.4 分布式任务处理需求描述分布式任务处理是指在处理大规模的任务时,将处理应用复制出多个应用实例,使用消息队列的消息分发机制实现任务的并行处理。每个应用实例在各自独立的进程中运行,通过rabbitMQ队列与任务生产者进行通信,因此一个生产者对应多个消费者。事务生产者将数据写入消息队列,多个消费者负载均衡地读取数据和进行数据处理。还需提供分布式任务处理结果查询界面,以便于查看队列处理事务的情况,在页面上展示事务内容、处理应用实例、处理时间等。
    4 系统功能设计4.1 总体功能概述基于前面的需求分析,我们对此系统需要实现的功能有了一定的了解。由于本系统通常作为一个应用集群中的组件使用,实现的功能是多样的,且多数功能都只是为了展示组件在应用集群中运作的方式和结果,因此不涉及具体完整的业务功能,其涉众范围也与该组件真正的应用场景有关。在以上选取的三个场景中,涉众严格来说只有管理员,登录之后可以对系统当前的数据变化方便地进行监控和查看,便于演示。如图4-1所示为系统整体功能模块图。

    4.2 详细功能设计4.2.1 整体功能1.管理账户登录
    系统管理员账号密码为系统初始化,账号密码在系统配置文件中进行配置。用户访问此系统时,进入到登录页面,输入账号密码,点击登录,后台读取配置文件进行身份校验。当系统页面一定时间段内无交互时,登录会话超时,需重新登录,超时时间可配。
    4.2.2 邮件服务器1.邮件消息监听
    在消息中间件中生成传输邮件发送消息的消息队列(mail_queue),监听该队列的程序段读取队列中的邮件消息数据后,对其进行数据校验、模板解析、并持久化存入数据库,记录处理日志。
    2.邮件内容解析
    传递到消息中间件的邮件消息不能直接作为邮件发送出去,需要将业务参数与邮件模板解析成完整的邮件内容才能进行发送。在此功能模块中,使用freemarker模板引擎技术,根据邮件消息数据中的模板名字段选择邮件模板,与业务参数一起进行数据渲染,生成邮件内容。
    3.邮件发送
    此功能将实现邮件发送。在消息中间件中,利用Spring提供的@Schedule注解定时任务调度每间隔一段时间从数据库中读取出待发送的邮件记录,集成javamail API,搭建一个邮件发送客户端,并在数据库中记录发送日志。
    4.邮件发送日志查询
    邮件发送日志有两种,一种是邮件数据解析日志,查询所有接收到的邮件消息邮件模板解析失败日志记录;另一种是邮件发送日志,查询解析成功的邮件数据的发送日志记录,包括发送成功和不成功的日志。
    5.邮件模板管理
    邮件模板管理实现对邮件模板的增删改查,支持业务变化时灵活地更新邮件模板的内容。
    4.2.3 复杂业务拆分此场景模拟电商平台的订单系统和库存系统之间的异步通信,消息中间件的角色为库存系统,此处不考虑两个系统间保持数据一致性的方案,默认两个系统的数据一直是同步的。
    1.订单生成消息监听
    在消息中间件中生成订单-库存消息队列(order_stock_queue),监听该队列的程序段接收到订单系统写入的订单信息后,根据消息中的商品编号和购买数量更新商品库存数量。
    2.更新商品库存
    商品的库存数据存入数据库库存表中,当接收到订单消息后,从消息数据中得到商品编号,购买数量,将库存表中对应的商品数据已售数量和库存数量更新。
    3.商品库存管理
    为方便查看系统处理订单数据和库存数据的结果,前端提供商品数量的查询页面,展示商品编号、商品已售数量、商品库存数量。
    4.2.4 分布式任务处理1.事务消息监听
    创建多个MQ消费者监听应用发送的事务消息,本系统以加减乘除模拟事务处理,消息中包含两个整数参数,监听该消息的程序段对这两个参数进行常规的数学运算,并将结果和处理该事务的对列明写入数据库便于监控。
    2.事务处理并写入数据库
    对接收的参数做加减乘除数学运算,将结果存入数据库。
    3.事务处理结果查询
    方便查看各个队列处理事务的情况,从数据库中读取数据,在前端页面展示。
    4.3 数据库设计在4.2章节的详细设计中提到了各个业务场景功能中大致的数据库存储数据内容,具体的数据库表结构设计如下
    4.3.1 邮件服务器mail_ask_for表存储所有接收到的邮件消息。监听邮件消息队列的程序段读取到邮件消息后,对消息进数据校验和模板解析,无论是否解析成功,结果均存入mail_ask_for表(表结构如表4.1所示)。



    column name
    datatype
    default value
    is nullable
    comment




    sn
    varchar

    NO
    sn


    create_time
    datetime

    YES
    创建时间


    update_time
    datetime

    YES
    更新时间


    version
    int
    0
    NO
    数据版本


    msg_from_nick
    varchar

    YES
    发件人标签


    msg_to
    varchar

    YES
    接受者邮件地址


    temp_enable
    tinyint
    0
    NO
    是否使用模板,0:不使用,1:使用


    param
    text

    YES
    参数(json格式)


    attachment
    text

    YES
    附件列表(json格式)


    temp_name
    varchar

    YES
    模板名称


    subject
    varchar

    YES
    邮件主题


    context
    text

    YES
    消息内容


    send_type
    tinyint
    0
    YES
    发送状态,0:实时发送 1:定时发送


    timing
    datetime

    YES
    定时发送时间


    dealwith_state
    tinyint
    0
    YES
    0处理成功,1处理失败!


    remark
    varchar

    YES
    备注



    mail_send_record存储解析成功的、进行发送处理的邮件消息记录。上述邮件消息解析成功后,存入mail_send_record表,由定时任务每间隔一段时间读取待发送的邮件记录,进行邮件发送,并更新发送次数和发送状态,表结构如表4.2所示。



    column name
    datatype
    default value
    is nullable
    comment




    sn
    varchar

    NO
    sn


    create_time
    datetime

    YES
    创建时间


    update_time
    datetime

    YES
    更新时间


    version
    int
    0
    NO
    数据版本


    msg_from_nick
    varchar

    YES
    发件人标签


    msg_to
    varchar

    YES
    接受者邮件地址


    subject
    varchar

    YES
    邮件主题


    context
    text

    YES
    消息内容


    send_status
    tinyint
    0
    NO
    发送状态,0:未发送,1:已发送 3 发送失败 4:处理中 5:记录已取出


    send_times
    int
    0
    NO
    发送次数



    mail_send_log表存储mail_send_record表中邮件发送日志流水,发送成功记录发送日志,发送失败记录发送日志和失败原因,表结构如表4.3所示。



    column name
    datatype
    default value
    is nullable
    comment




    sn
    varchar

    NO
    sn


    create_time
    datetime

    YES
    发送时间


    mail_sn
    varchar

    YES
    邮件sn


    send_status
    tinyint
    0
    YES
    发送状态,0:未发送,1:已发送 3 发送失败 4:处理中 5:记录已取出


    remark
    varchar

    YES
    备注



    mail_template表存储邮件模板数据,通过temp_name字段唯一确定模板记录,模板内容为freemarker语法,表结构如表4.4所示。



    column name
    datatype
    default value
    is nullable
    comment




    sn
    varchar

    NO
    sn


    create_time
    datetime

    YES
    创建时间


    update_time
    datetime

    YES
    更新时间


    version
    int
    0
    NO
    数据版本


    temp_name
    varchar

    NO
    模板名称


    temp_group_name
    varchar
    默认分组
    NO
    模板分组名


    temp_subject
    varchar

    YES
    模板标题


    temp_context
    text

    YES
    模板内容


    temp_enable
    tinyint
    0
    NO
    是否可用,0:不可用,1:可用



    mail_attachment存储每个邮件消息要发送的附件信息,从邮件消息队列中解析而来,表结构如表4.5所示。



    column name
    datatype
    default value
    is nullable
    comment




    sn
    varchar

    NO
    sn


    create_time
    datetime

    YES
    创建时间


    update_time
    datetime

    YES
    更新时间


    version
    int
    0
    NO
    数据版本


    msg_sn
    varchar

    NO
    邮件sn


    attachment_name
    varchar

    NO
    附件名字


    attachment_path
    varchar

    YES
    附件地址



    4.3.2 复杂业务拆分product_order表存储订单信息,当监听订单-库存消息队列的程序段读取到数据之后,持久化到product_order表,表结构如表4.6所示。



    column name
    datatype
    default value
    is nullable
    comment




    sn
    varchar

    NO
    商品编号sn


    create_time
    datetime
    CURRENT_TIMESTAMP
    NO
    创建时间


    update_time
    datetime
    CURRENT_TIMESTAMP
    NO
    修改时间


    product_sale
    bigint
    0
    YES
    购买数量



    product_stock表存储商品库存信息,表结构如表4.7所示。



    column name
    datatype
    default value
    is nullable
    comment




    sn
    varchar

    NO
    商品编号sn


    create_time
    datetime
    CURRENT_TIMESTAMP
    NO
    创建时间


    update_time
    datetime
    CURRENT_TIMESTAMP
    NO
    修改时间


    product_name
    varchar

    NO
    商品名称


    product_sale
    bigint
    0
    YES
    已售数量


    quantity
    bigint

    NO
    库存数量



    4.3.3 分布式任务处理distributed_result存储任务处理结果,监听分布式任务的消息队列读取消息后,将消息中的参数a、b和对这两个参数的数学运算结果写入此表,模拟任务处理,表结构如表4.8所示。



    column name
    datatype
    default value
    is nullable
    comment




    create_time
    datetime
    CURRENT_TIMESTAMP
    NO
    创建时间


    update_time
    datetime
    CURRENT_TIMESTAMP
    NO
    修改时间


    sn
    varchar

    NO
    事务编号


    deal_queue_name
    varchar

    NO
    处理应用


    parameter_a
    int

    NO
    参数a


    parameter_b
    int

    NO
    参数b


    result_add
    int

    NO
    a+b


    result_reduce
    int

    NO
    a-b


    result_multiply
    int

    NO
    a*b


    result_divide
    float

    NO
    a÷b



    5 系统功能实现为更好地说明rabbitMQ在实际应用系统中的应用机制,本系统将实现一个包含4个应用实例的应用集群,有1个消息生产者和3个消息消费者,通过MQ进行通信,拓扑图如图5-1所示。

    5.1 系统管理员登录生产者应用和消费者应用均提供管理员登录页面。管理员的账号密码在系统的配置文件中进行配置,本设计使用admin/a123456作为账号密码,运行系统,在浏览器中输入http://localhost:8080/login.action 显示系统登录页,如图5-2所示,输入账号密码,点击登录进入功能页。登录信息使用session进行会话管理,30分钟无操作则登录失效。
    由于生产者和消费者角色的不同,因此其包含的功能也不尽相同。生产者应用中登录成功后的首页如图5-3所示,包含发送邮件消息、创建批量任务消息、创建订单消息等功能;消费者应用登录成功后的首页如图5-4所示,包含邮件模板管理、邮件日志查询、库存查询、分布式任务处理结果等功能,具体功能将在下文进行详细介绍。

    5.2 邮件服务器5.2.1 邮件模板管理功能主页如图5-5所示,在邮件模板管理功能中,分页查询当前系统中的模板数据,使用列表展示,点击”添加模板”可进入模板添加页面,如图5-6所示;点击操作栏中的”更新”可进入模板编辑页面,当前模板信息在表单中回显,如图5-7所示。

    5.2.2 邮件消息监听和持久化系统使用rabbitMQ队列进行消息传输,在系统配置RabbitConfig中进行队列生成,关键代码和相关配置文件如图5-8,5-9所示,在监听方法中加入@RabbitHandler和@RabbitListener注解,以实现消费者事件监听。
    rabbitMQ服务搭建在远程服务器中,具体搭建过程与本设计无关,故省略。rabbitMQ提供了管理界面,可查看当前队列信息,启动消费者应用后,会生成三个队列,如图5-10所示。

    5.2.3 邮件发送在生产者应用中提供了邮件消息生成页面,如图5-11所示。点击各个按钮,将创建一个邮件消息到消息队列,消费者监听后进行发送处理。以”模板实时发送带参数带附件”为例,点击此按钮,将在mail_ask_for表生成一条邮件请求数据,模板解析成功后,在mail_ask_record中生成一条待发送数据。经过邮件发送定时任务处理后,使用java mail将邮件发送到收件人。邮件发件箱配置如图5-8所示,邮件发送关键代码如图5-12,5-13,5-14,5-15所示。收件人收到的邮件如图5-16所示。

    5.2.4 邮件日志查询在消费者应用中提供了邮件日志查询页面,如图5-17所示,接收的邮件消息将有四种处理结果,分别是内容解析成功、内容解析失败、发送成功和发送失败,且只有内容解析成功的邮件才会进行发送处理,点击列表上方的状态单选按钮可切换查询条件。

    5.3 复杂业务拆分本系统以简单的商品购买和库存查询功能来模拟大型电商平台中订单系统与库存系统分离的应用场景。在生产者应用中,提供了创建订单消息的商品购买页面,如图5-18所示,列表展示商品信息,点击操作栏中的”购买”按钮将生成一个订单消息,并发送到消息队列。

    在消费者应用中,提供商品库存查询页面,如图5-19所示。当消费者应用接收到来自生产者应用的订单消息时,会更新商品库存。在该页面中,点击操作栏中的”订单列表”可以看到该商品的订单记录,如图5-20所示。

    5.4 分布式任务处理在生产者应用中,提供了创建大量任务的按钮用于测试,如图5-21所示,点击该按钮,会批量生成50条消息放入消息队列供消费者处理。

    在上文的系统拓扑图中有介绍,将开启三个消费者实例来并行处理这些任务,分别是consumer、consumer1和consumer2,此时在rabbitMQ 管理界面可以看到,监听分布式任务消息的队列rookie_distributedqueue拥有了三个消费者,如图5-22所示。这些消费者同时监听发布到rookie_distributedqueue队列中的消息,负载均衡地进行任务处理。在消费者应用中提供了任务处理结果查询页面,如图5-23所示,通过列表中”处理实例”字段的数据,可以看出生成的50个任务消息大致均衡地被3个消费者实例处理了。

    6 总结与展望6.1 应用优势本系统作为消息中间件,模拟实现了rabbitMQ的三种典型的应用场景,可以方便地集成到各大应用集群中作为消息,只需更改其具体的业务实现即可。在邮件服务器中,使用java mail技术,真实地实现了邮件客户端,可以将邮件发送出去,且发件箱信息在配置文件中,更改发件人账号只需简单地更改配置文件。
    本系统使用spring boot,启动简单,配置方便,是一个轻量级的组件,且由于使用消息队列监听与其他应用进行通信,在本地进行数据持久化,数据稳定可靠,可扩展性强。
    6.2 优化方向与方案rabbitMQ的应用场景远不止本文所描述的三种,其队列有多种类型,可以灵活适配各种业务需求,当作为邮件服务器时,不仅可以将系统的邮件发送功能分离出来,还可以将短信、站内消息等均抽离到邮件服务器,将邮件服务器作为业务系统的消息中心,扩展出更丰富的消息服务。
    另外,由于时间等客观因素,本系统只能简单地模拟业务场景,展示出使用消息队列的异步通信原理和方式,起到示意作用,而应用在真实的业务系统中时,涉及到的数据处理复杂度远不止于此种程度,而且分布式业务处理所带来的数据不一致问题,在真实的业务场景中是务必要考虑的。
    参考文献[1] 鱼朝伟,詹舒波.基于RabbitMQ的异步全双工消息总线的实现[J].软件,2016,37(02):139-146.[2] 杨家炜.基于Spring Boot的web设计与实现[J].轻工科技,2016,32(07):86-89.[3] 欧阳荣彬,王倩宜,龙新征.基于微服务的数据服务框架设计[J].华中科技大学学报(自然科学版),2016,44(S1):126-130.[4] 王永和,张劲松,邓安明,周智勋.Spring Boot研究和应用[J].信息通信,2016(10):91-94.[5] 李华.基于Spring boot的高职院校实践教学管理系统的实现[J].绵阳师范学院学报,2016,35(11):70-74.[6] Spring boot集成RabbitMQ[J].电脑编程技巧与维护,2017(02):4.[7] 黄小锋,张晶.微服务框架介绍与实现[J].电脑与信息技术,2016,24(06):14-16.[8] 李春阳,刘迪,崔蔚,李晓珍,李春岐.基于微服务架构的统一应用开发平台[J].计算机系统应用,2017,26(04):43-48.[9] 马巍,武欣嵘,郑翔,张文强,童玮.RabbitMQ在实时监控系统中的应用[J].军事通信技术,2017,38(01):82-85.[10] 张晶,黄小锋,李春阳.微服务框架的设计与实现[J].计算机系统应用,2017,26(06):259-262.[11] 张峰.应用SpringBoot改变web应用开发模式[J].科技创新与应用,2017(23):193-194.[12] 温晓丽,苏浩伟,陈欢,邹大毕.基于SpringBoot微服务架构的城市一卡通手机充值支撑系统研究[J].电子产品世界,2017,24(10):59-62.[13] 王志勃,王麒森,毕艳茹.互联网环境下微服务框架分析与研究[J].信息与电脑(理论版),2017(22):23-25.[14] 谭一鸣. 基于微服务架构的平台化服务框架的设计与实现[D].北京交通大学,2017.[15] 马雄. 基于微服务架构的系统设计与开发[D].南京邮电大学,2017.[16] 荀崇崇. 分布式服务与服务集群技术研究[D].成都理工大学,2017.[17] 李楚畅. 基于MBaaS架构的移动后端服务框架的研究与实现[D].北京邮电大学,2017.[18] 宋鹏威. 开放式微服务框架的设计与应用[D].北京邮电大学,2017.[19] 程岚岚,田文涛,汪剑.基于Spring Boot的网页健康性评级系统的设计与实现[J].电脑与信息技术,2018,26(02):45-47.[20] 袁佳,郭燕慧.基于rabbitmq的海量日志的分布式处理[J].软件,2013,34(07):19-23.[21] 吴炜鑫,王宇,王兴伟.基于AMQP的校园消息总线系统的设计与实现[J].通信学报,2013,34(S2):180-183.[22] 李健. 基于RabbitMQ的核查地震交互分析软件的调度设计[A]. 中国地球物理学会国家安全地球物理专业委员会、陕西省地球物理学会军事地球物理专业委员会.国家安全地球物理丛书(十)——地球物理环境与国家安全[C].中国地球物理学会国家安全地球物理专业委员会、陕西省地球物理学会军事地球物理专业委员会:,2014:7.[23] 陈璞. 基于RabbitMQ的电子商务系统设计与实现[D].天津大学,2015.[24] Felipe Gutierrez. Spring Boot, Simplifying Everything[M].Apress:2014-06-15.[25] Chris Schaefer,Clarence Ho,Rob Harrop. Spring Projects: Batch, Integration, XD, and Boot[M].Apress:2014-06-15.[26] Jing Jing Yan,Yun Fang Ma. Learning-Sphere Curriculum and Course Development of “Java Web Application Development” Based on Working Process[J]. Applied Mechanics and Materials,2013,2388(321).[27] Akitoshi Yoshida. MOWS: distributed Web and cache server in Java[J]. Computer Networks and ISDN Systems,1997,29(8).[28] Gun Ho Lee,Junsu Jung. Web framework with Java and XML in multi-tiers for productivity[J]. Future Generation Computer Systems,2006,23(2).
    致 谢从开始选题,经历了近半年的资料收集、材料整理、论文写作和系统开发,且在此期间还坚持公司实习,今天终于完成了毕业设计和毕业论文,也算是给大学生活画上了一个完美的句点。
    论文得以完成,要感谢的人实在太多了,首先要感谢我的毕业设计指导老师,老师同时也是我大学四年的导师,无论是在专业素质的培养还是职业发展的选择上,都给予了我莫大的帮助。老师还要同时指导其他两位同学的论文,加上本来就有的教学任务,工作量之大可想而知,但在一次次的回稿中,精确到每一个字的批改给了我深刻的印象,使我在论文之外明白了做学问所应有的态度。同时,还要感谢实习公司的岗上导师,为我的毕业设计提供了技术支持,让作为本科生的我接触到了微服务架构和rabbitMQ等新的技术,为毕业设计增加亮点。
    论文的顺利完成,也离不开其它各位老师、同学和朋友的关心和帮助。在整个的论文写作中,各位老师、同学和朋友积极的帮助我查资料和提供有利于论文写作的建议和意见,在他们的帮助下,论文得以不断的完善。另外,要感谢在大学期间所有传授我知识的老师,是他们的悉心教导使我有了良好的专业课知识,这也是论文得以完成的基础。
    1 评论 6 下载 2018-09-26 16:42:27 下载需要20点积分
  • 基于C#实现的文件管理系统

    一、项目背景
    在内存中开辟一个空间作为文件存储器(虚拟磁盘),在其上实现一个简单的文件管理系统
    退出这个文件系统时,应将文件系统的内容保存在磁盘上,下次将其恢复到内存中
    文件目录采用多级目录结构

    二、开发/运行环境
    开发环境:Windows10 Pro 1803
    开发语言:C# 6.0/ .net Framework 4.7.03056
    开发工具:Microsoft Visual Studio Community 2017

    三、数据结构:块组由于模拟的磁盘较为简单,所以这里采用单块组,取消了逻辑扇区的数据结构。事实上,这一个块组对象就等价于一个虚拟磁盘。我使用了ext2的设计思想,并做了必要的简化,在细节上与真实的ext2文件系统有所不同。
    3.1 卷大小由于简化了操作,将单个块组等价于磁盘,所以将磁盘的容量记录在块组里面。同时这是一个不太严格的说法,这里的卷大小实际上是所有数据块的尺寸之和。
    3.2 超级块用于描述文件系统的整体信息

    inode数量
    空闲inode数量

    3.3 组描述符表记录所有的组描述符。
    组描述符
    记录块组中各个描述性质的数据结构的位置,这里用来记录数据块和inode的使用情况。
    块位图
    描述本块组中数据块的使用情况,是一个位向量,1表示数据块已被占用,0表示数据块未被占用。
    inode位图
    描述inode表中inode的使用情况,1表示已被使用,0表示未被使用。
    3.4 数据块表记录块组中所有的数据块。
    数据块data
    一个字符型数组,用于存储各种数据,数组大小为2048,实际占用4096字节,因为C#中字符为Unicode编码,每个字符占据2个字节。
    3.5 inode表记录块组中所有的inode,数量由超级块给出。
    inode
    表示文件系统树型结构节点:

    文件类型
    文件大小
    文件名
    文件创建时间
    文件修改时间
    文件占用数据块数量
    占用的数据块索引
    包含的子文件的inode索引
    包含的子文件的数量
    父目录的inode索引

    注意,这里的子文件均指第一级子文件,即树型结构中的儿子。
    3.6 当前目录索引用于记录用户当前所处的目录的inode索引。由于这是一个单用户系统,所以将这个信息保存在了块组对象中,便于编程实现。
    磁盘映像也是按照这个顺序写入的,保存在与可执行文件同一目录下,名为disk.img,请务必不要随意修改这个文件,否则会导致文件系统读取错误
    四、GUI设计及使用说明整个文件管理系统包含四个窗口:

    主窗口:展示当前目录下的文件和子目录列表,以及各种操作对应的按钮
    输入窗口:用于输入文件或子目录的名称
    文件查看和编辑窗口:用于进行文件查看和编辑
    文件详情窗口:用于查看文件各项详细信息

    其中,主窗体负责调用其它三个窗体,窗体之间通过委托和事件进行信息传递。
    4.1 GUI说明4.1.1 主窗口
    其中,蓝色标识的文件是目录文件,黑色标识的是普通文件,红色标识的是未知文件。未知文件一般不会出现,除非磁盘映像损坏造成错误。我们可以尝试一下:

    重新运行文件管理系统,可以看到:

    目录文件斗破苍穹已被损坏,成为了未知文件。保存此时的磁盘映像,可以看到:

    文件类型已经发生了改变。
    4.1.2 输入窗口这个窗口主要用于进行命名操作,以及在格式化时输入磁盘大小。
    重命名

    点击确定或按下回车键,文件名变为:
    新建文件
    也可以尝试新建目录和文件,并为它们命名:

    格式化
    在进行格式化时,务必注意输入的数字符合要求,否则无法进行格式化:

    磁盘空间大小必须为大于0小于等于100的整数。
    4.1.3 文件查看和编辑窗口首先,只有普通类型的文件可以被“打开”,目录文件和未知文件无法执行“打开”操作。
    “打开”文件分为两种类型:只读和读写模式。只读模式下,用户将无法对文件内容进行编辑,而读写模式下则可以。
    双击文件或按下回车键采用的是只读模式打开,编辑文件需要点击写文件按钮或按下W键。
    只读模式

    用户无法编辑文件内容,确认按钮也没有被激活。
    读写模式

    用户可以对文件进行编辑,编辑之后可以点击确认按钮或同时按下Control+S键保存修改的内容,也可以点击取消按钮或者右上角的叉号或者按下Esc键放弃更改。
    4.1.4 文件详情窗口当用户选中一个文件时,按下空格键可以查看其详细信息。操作时应当注意,当窗口焦点位于某一按钮时,即使文件看起来被选中了,此时按下空格键也无法看到文件详情。
    普通文件详情

    目录文件详情

    当然,目录文件详情中的文件大小是指目录文件本身的大小,不包含目录文件的子文件。
    在详情界面,再次按下空格键即可关闭详情页。
    4.2 使用说明


    主界面操作
    效果




    双击普通文件
    以只读模式打开文件


    双击目录
    进入该目录


    按F键
    格式化磁盘


    按N键
    新建普通文件


    选中普通文件,按回车键
    以只读模式打开文件


    选中普通文件,按W键
    以读写模式打开文件


    按退格键(Backspace)
    返回上一级目录


    选中目录,按回车键
    进入该目录


    按D键
    创建目录


    选中文件,按删除键(Delete)
    删除文件


    选中文件,按R键
    重命名


    按Control+W键
    退出系统,保存映像


    选中文件,按空格键
    查看文件详情






    文件编辑页面操作
    效果




    按退出键(Esc)
    关闭窗口,不保存修改


    按Control+S键
    关闭窗口,保存修改



    注:只读模式打开的文件无法使用Control+S按键关闭窗口



    文件详情页面操作
    效果




    按空格键
    关闭详情页面






    输入窗口页面操作
    效果




    按退出键(Esc)
    关闭窗口,输入不生效


    按回车键(Enter)
    关闭窗口,输入生效



    五、设计细节5.1 系统架构在我的理解中,文件管理系统的作用是:向下管理磁盘和文件,向上提供服务。基于此,我设计了如下架构:

    其中,蓝色部分为用户界面,是用户直接看到的图形化界面;绿色部分为文件管理系统向上对用户提供的功能,它们在用户界面中表现为一个个的按钮和鼠标键盘事件;黄色部分是文件管理系统向下对文件和磁盘进行管理的函数;灰色部分是虚拟磁盘,即管理的对象,文件也储存在这里面;红色部分处于真实的磁盘中,用于保存虚拟磁盘和文件管理系统的映像。
    在这个结构中,用户一般是无法直接对虚拟磁盘进行操作的,除了格式化操作和退出系统操作(需要覆写原有磁盘映像)。这一方面简化了用户的操作,一方面提供了安全性保障,防止用户直接操作黄色部分函数,而损坏管理系统。
    5.2 读取磁盘映像当运行文件管理系统时,系统会进行初始化,尝试读取disk.img文件。如果文件不存在,会进入如下界面:

    若此时不进行格式化,那么主界面上除了关闭系统按钮可以操作,其它功能都无法使用。
    如果磁盘映像读取成功,文件管理系统便会根据映像中的内容恢复磁盘结构和文件内容。磁盘映像并不是将磁盘中所有的内容都包含在内,它只包含了被占用的部分。
    5.3 删除文件对于文件的删除分为2种:删除目录文件和非目录文件。
    删除目录文件时,必须递归地删除目录文件中所有的子文件,这里的子文件指目录文件下所有文件,即树型结构中的所有子孙。删除时,系统会根据inode中子文件的inode索引执行深度优先遍历,自底向上删除目录文件的所有子孙,最终删除目录文件本身。即:对于目录文件A,它包含两个文件B和C,那么当用户删除A时,系统首先删除B和C,再删除A;在删除B和C的过程中,如果B或C也是目录文件,那么就要以相同的方式删除B或C。
    删除普通文件的过程比较简单,直接删除即可。
    具体的删除过程会将inode位图的数据块位图中的相应信息置为true,即未占用;同时为了数据安全,这些inode和数据块中的信息都会被抹去,用0元素覆盖。
    我在代码中留下了测试删除顺序的方法,具体位置位于deleteFile(String fileName)函数中:
    foreach (var index in deleteInode.dataBlockList) groupDescriptorList[0].blockBitmap[index] = true; // 释放占用的数据块groupDescriptorList[0].inodeBitmap[deleteFileIndex] = true; // 释放占用的inode块 //MessageBox.Show(inodeList[deleteFileIndex].fileName, "", MessageBoxButtons.OK, MessageBoxIcon.Error); // 检查删除顺序inodeList[currentInodeIndex].childInodeIndex.Remove(deleteFileIndex);// 抹去inode信息和数据块内容deleteInode.blockSize = 0;
    将注释为检查删除顺序一行开头的注释符去掉,重新编译运行,执行删除操作即可观察到删除顺序。
    5.4 Inode块和数据块分配5.4.1 Inode块分配当新建文件时,文件系统会从空闲的Inode块中选出一个分配给这个新文件,挑选过程使用Inode位图顺序检查每个Inode块是否已被分配,找到第一个空闲的Inode块后停止检查。如果所有的Inode块都已被占用,那么会返回错误。
    int indexOfInode = -1; foreach(KeyValuePair<int, bool> kvp in groupDescriptorList[0].inodeBitmap) { if(kvp.Value == true) { indexOfInode = kvp.Key; break; } } if (indexOfInode == -1) return false; groupDescriptorList[0].inodeBitmap[indexOfInode] = false; Inode inode = inodeList[indexOfInode];
    最后一行代码中取出的inode将被写入各种信息,最终写回到Inode列表中。Inode位图中的信息也应得到更新,这个inode对应的位图标志被置为false。
    5.4.2 数据块分配文件创建之初是不会被分配数据块的,直到文件被写入了信息,才会被分配数据块。在这其中,对目录文件和普通文件的写入还有所不同。由于我规定文件名称不得超过100个字符,所以对于目录文件的一次写入最多会额外申请一个数据块;而普通文件的写入没有限制(C#本身的文本框字符上限为32768个,这是一个上限),所以可能会申请多个数据块。
    当然,对数据块的申请过程都是一致的:
    int indexOfBlock = -1; foreach (KeyValuePair<int, bool> kvp in groupDescriptorList[0].blockBitmap) // 找到一个空闲数据块 { if (kvp.Value == true) { indexOfBlock = kvp.Key; break; } } if (indexOfBlock == -1) // 没有足够的数据块 { updateInodeInfo(ref inode); inodeList[commonIndex] = inode; return false; } groupDescriptorList[0].blockBitmap[indexOfBlock] = false; DataBlock dataBlock = dataBlockList[indexOfBlock];
    文件系统通过检查数据块位图取出相应的数据块,将位图中对应的信息置为false,在数据块被更新完毕后将其写回。
    5.4.3 Inode块和数据块回收当文件被删除或内容被删减时,它占用的inode块和数据块可能会变得“空闲”,这时文件管理系统就必须将空闲的块进行回收,抹掉块中的内容,更新块位图中的信息。
    删除文件时,会触发释放inode事件和数据块事件:
    foreach (var index in deleteInode.dataBlockList) groupDescriptorList[0].blockBitmap[index] = true; // 释放占用的数据块 groupDescriptorList[0].inodeBitmap[deleteFileIndex] = true; // 释放占用的inode块 //MessageBox.Show(inodeList[deleteFileIndex].fileName, "", MessageBoxButtons.OK, MessageBoxIcon.Error); // 检查删除顺序 inodeList[currentInodeIndex].childInodeIndex.Remove(deleteFileIndex); // 抹去inode信息和数据块内容 deleteInode.blockSize = 0; foreach(var index in deleteInode.dataBlockList) { DataBlock dataBlock = dataBlockList[index]; for (int i = 0; i < BLOCKSIZE / 2; i++) dataBlock.data[i] = '\0'; dataBlockList[index] = dataBlock; } deleteInode.dataBlockList.Clear(); deleteInode.fatherIndex = -1; deleteInode.fileSize = 0; superBlock.freeInodeNum++; inodeList[deleteFileIndex] = deleteInode; // 写回 Inode fatherInode = inodeList[tempCurrentInodeIndex]; fatherInode.childrenNum--; inodeList[tempCurrentInodeIndex] = fatherInode; // 写回 writeDirectoryFileToDisk(tempCurrentInodeIndex);
    改写文件时,只可能会触发释放数据块事件:
    int freeIndex = inode.dataBlockList[blockNum]; // 要释放的数据块的index groupDescriptorList[0].blockBitmap[freeIndex] = true; for (int i = 0; i < BLOCKSIZE / 2; i++) dataBlockList[freeIndex].data[i] = '\0'; inode.dataBlockList.Remove(freeIndex); // 从子列表中移除
    5.5 其它信息所有的代码都有必要的注释,读者可以参考注释阅读代码。
    2 评论 36 下载 2019-04-08 15:00:29 下载需要12点积分
  • 基于react框架和MYSQL数据库的社团管理系统

    一、技术简介1.1 ReactReact 可以非常轻松地创建用户交互界面。为你应用的每一个状态设计简洁的视图,在数据改变时 React 也可以高效地更新渲染界面。以声明式编写UI,可以让你的代码更加可靠,且方便调试。创建好拥有各自状态的组件,再由组件构成更加复杂的界面。无需再用模版代码,通过使用JavaScript编写的组件你可以更好地传递数据,将应用状态和DOM拆分开来。
    1.2 JSX一种 JavaScript 的语法扩展。 我们推荐在 React 中使用 JSX 来描述用户界面。
    JSX 是在 JavaScript 内部实现的。我们知道元素是构成 React 应用的最小单位,JSX 就是用来声明 React 当中的元素。与浏览器的 DOM 元素不同,React 当中的元素事实上是普通的对象,React DOM 可以确保 浏览器 DOM 的数据内容与 React 元素保持一致。
    1.3 webpackwebpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。
    1.4 npmnpm 是 JavaScript 世界的包管理工具,并且是 Node.js 平台的默认包管理工具。通过 npm 可以安装、共享、分发代码,管理项目依赖关系。
    1.5 YarnYarn 对代码来说是一个包管理器,可以通过它使用全世界开发者的代码, 或者分享自己的代码。Yarn 做这些快捷、安全、可靠。
    通过Yarn可以使用其他开发者针对不同问题的解决方案,使开发过程更简单。 使用过程中遇到问题,可以将其上报或者贡献解决方案。一旦问题被修复, Yarn会更新保持同步。
    1.6 iViewiView 是一套基于 Vue.js 的开源 UI 组件库,主要服务于 PC 界面的中后台产品。
    1.7 Ant Designantd服务于企业级产品的设计体系,基于确定和自然的设计价值观上的模块化解决方案,让设计者和开发者专注于更好的用户体验。
    1.8 Eggegg.js是阿里推出的基于koa的node开发框架。为企业级框架和应用而生。Egg 的插件机制有很高的可扩展性,一个插件只做一件事(比如 Nunjucks 模板封装成了 egg-view-nunjucks、MySQL 数据库封装成了 egg-mysql)。Egg 通过框架聚合这些插件,并根据自己的业务场景定制配置,这样应用的开发成本就变得很低。
    Egg 奉行『约定优于配置』,按照一套统一的约定进行应用开发,团队内部采用这种方式可以减少开发人员的学习成本,开发人员不再是『钉子』,可以流动起来。没有约定的团队,沟通成本是非常高的,比如有人会按目录分栈而其他人按目录分功能,开发者认知不一致很容易犯错。但约定不等于扩展性差,相反 Egg 有很高的扩展性,可以按照团队的约定定制框架。使用 Loader 可以让框架根据不同环境定义默认配置,还可以覆盖 Egg 的默认约定。
    1.9 JSON Web TokenJSON Web Token(JWT)是目前最流行的跨域身份验证解决方案。它是一个很长的字符串,中间用点(.)分隔成三个部分。客户端收到服务器返回的 JWT,可以储存在 Cookie 里面,也可以储存在 localStorage。此后,客户端每次与服务器通信,都要带上这个 JWT。你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求的头信息Authorization字段里面。
    1.10 KnexKnex.js是为Postgres,MSSQL,MySQL,MariaDB,SQLite3,Oracle和Amazon Redshift设计的“包含电池”SQL查询构建器,其设计灵活,便于携带并且使用起来非常有趣。它具有传统的节点样式回调以及用于清洁异步流控制的承诺接口,流接口,全功能查询和模式构建器,事务支持(带保存点),连接池 以及不同查询客户和方言之间的标准化响应。
    二、系统概要设计系统的设计主要前台和后台两个部分
    2.1 系统需求分析
    登录/登出
    社团信息管理

    新增/查询/编辑/注销社团移除社团成员关键字查询社团信息
    公告管理

    新增公告预览/查询/删除已创建的公告公告流量统计
    管理员管理

    添加/删除/编辑管理员权限验证
    个人信息管理

    用户名编辑修改密码

    2.2 可行性分析这里讲的可行性分析的任务是从技术上、经济上分析需解决的问题是否存在可行性。其目的是在尽可能短的时间内用尽可能小的代价确定问题是否有解。
    技术上的可行性分析主要分析技术条件能否顺利完成开发工作,硬、软件能否满足开发者的需要等。
    React是一个用于创建可重用且有吸引力的JavaScript框架。它非常适合代表经常变化的数据的组件。使用React,您可以通过将它们分解为组件而不是使用模板或HTML来构建可重用的用户界面。

    灵活性和响应性:它提供最大的灵活性和响应能力
    虚拟DOM:由于它基于文档对象模型,因此它允许浏览器友好地以HTML,XHTML或XML格式排列文档
    丰富的JavaScript库:来自世界各地的贡献者正在努力添加更多功能
    可扩展性:由于其灵活的结构和可扩展性,React已被证明对大型应用程序更好
    不断发展: React得到了Facebook专业开发人员的支持,他们不断寻找改进方法
    Web或移动平台: React提供React Native平台,可通过相同的React组件模型为iOS和Android开发本机呈现的应用程序

    无论是Web还是本机移动开发,React都是大多数用户界面设计平台的理想选择。
    本系统数据库使用的MySQL,MySQL是最流行的关系型数据库管理系统,在WEB应用方面MySQL是最好的关系型数据库,轻量、开源、简便易用,使用Navicat Premium 12做数据库图形化管理更高效率进行前后端开发。
    2.3 Server端模型
    由前台客户端发起请求来请求数据,Egg服务器判断是否在白名单或请求头中的token是否有效,若满足条件之一,则放行请求,根据router访问对应的controller,controller为数据的处理层,将数据处理好后通过service对数据库进行访问与操作,然后将service返回的结果在controller进行再次处理与封装,然后将响应数据和状态码(默认200)返回前台
    三、数据库设计3.1 数据概念结构设计E-R模型是数据进行第一层抽象的表示方法。它的主要成分包括:实体、联系和属性。我们可以用E-R图将内容表达出来,辅助设计的实现[6]。
    3.2 数据库关系设计该系统数据库关系图如下所示:

    3.3 数据字典管理员表(admin)



    字段
    类型
    主键
    允许空值
    备注




    id
    int(11)
    P

    自动递增


    login_name
    varchar(255)





    name
    varchar(255)





    password
    varchar(255)





    role
    int(1)


    1:超级管理员 2:管理员


    isDel
    int(1)


    默认0



    学生表(student)



    字段
    类型
    主键
    允许空值
    备注




    stu_id
    varchar(255)
    P




    stu_name
    varchar(255)





    password
    varchar(255)





    sex
    int(1)


    0:男 1:女


    role
    int(1)


    0:未审核 1:家庭成员 2:业主


    tel
    varchar(255)





    社团表(community)



    字段
    类型
    主键
    允许空值
    备注




    id
    int(11)
    P

    自动递增


    title
    varchar(255)





    desp
    varchar(255)





    belong_dept
    int(11)





    manage_dept
    int(11)





    chairman_stu_id
    int(11)





    type
    int(11)





    create_time
    varchar(13)





    status
    int(1)


    0:未审核 ; 1:已审核 ; 2:已注销



    社团-学生表(community_student)



    字段
    类型
    主键
    允许空值
    备注




    id
    int(11)
    P

    自动递增


    stu_id
    int(11)





    community_id
    int(11)





    role
    int(11)


    0:成员; 1:社团管理员


    isDel
    int(11)





    社团类型表(community_type)



    字段
    类型
    主键
    允许空值
    备注




    id
    int(11)
    P

    自动递增


    type_name
    varchar(255)





    isDel
    int(11)


    默认0



    部门表(dept)



    字段
    类型
    主键
    允许空值
    备注




    id
    int(11)
    P

    自动递增


    dept_name
    varchar(255)





    isDel
    int(11)


    默认0



    公告表(notice)



    字段
    类型
    主键
    允许空值
    备注




    id
    int(11)
    P

    自动递增


    title
    varchar(255)





    author
    varchar(255)





    content
    text





    create_time
    varchar(255)





    isDel
    int(11)





    公告日志表(notice_log)



    字段
    类型
    主键
    允许空值
    备注




    id
    int(11)
    P

    自动递增


    notice_id
    int(11)





    stu_id
    int(11)





    create_time
    varchar(255)





    四、后台设计在接口设计方面,使用restful API设计原则,访问同一资源的不同操作时,使用同一命名空间,使用不同的请求方式区分不同的操作。
    数据中均没有使用物理删除,只做了逻辑删除,保证数据可回溯。
    新增与更新,都使用同一个接口,根据是否有id去判断是更新还是新增
    4.1 Egg本系统后台采用egg.js编写,egg继承于koa,操作与配置更加简洁,每个请求发送至后台时,先通过编写好的中间件(middleware),然后根据访问的路由(router)去执行对应的控制器(controller),控制器中只做数据操作,不对数据库进行直接操作,将数据整理为服务(service)所需要的形式,直接发送给对应的服务,服务对数据库进行直接操作,然后将结果直接返回给控制器,控制器再将结果响应给前台。
    如此,完成了一次请求
    4.2 权限校验已登录的用户,在请求时的请求头会带Authorization字段,若请求在白名单内,则不需要通过jwt中间件,直接放行,否则要校验此字段,字段里是bare + 登录时生成的json web token。
    jwt中间件对token进行解码后,判断是否合法并且在有效期内,通过则放行,不通过则直接返回状态码401(无权限)。
    代码实现:

    4.3 连接数据库本系统通过knex对数据库进行操作,以函数式编程的方式,简化了手写sql语句的操作,使代码更具语义化与可读性。
    配置MySQL数据库

    4.4 上传图片前端将图片以表单提交的形式传至后台,后台接受一个文件流,将其重命名后存放到目的位置,之后将完整路径发送至前端。

    五、页面设计页面采用蚂蚁金服出品的Ant Design UI 组件库,提供了完善的设计原则、最佳实践和设计资源文件(Sketch 和 Axure),来帮助业务快速设计出高质量的产品原型。
    5.1 登录页面
    输入用户名密码,点击登录,在对密码进行MD5加密后将login_name和password发送至后台,后台进行校验,匹配后将Json Web Token响应回来,将这个token存至cookie中,设定7天的过期时间,当前端再次发起其他请求时,将这个token带到请求头的Authorization字段中,供后台解析。
    若cookie中无请求头,则中止请求,使页面跳转回login页面,重新登录。

    5.2 主页
    antd支持layout和grid两种页面布局,这里采用layout布局,侧边栏通过递归一个JSON对象进行渲染对应的列表内容。

    获取到用户信息时,判断用户的角色,只有超级管理员才可显示管理员管理页签。
    顶部header下拉菜单可以直接跳转到个人信息或者退出登录。

    侧边栏通过collapsed状态进行切换开启或关闭状态。


    主题内容部分采用react-router-dom路由切换,根据url变化切换content内容。

    当页面均无法匹配到对应路由的时候,则进入noFound页面
    5.3 社团管理
    社团列表页面,做了关键字查询与分页功能,分页功能默认每页10条,可更改。

    点击编辑按钮可对当前社团进行更改。

    对每一项均做了填写校验。

    在选择社团主席时,输入学号进行远程搜索,动态选择。
    此处使用Lodash中的防抖功能对输入进行了优化,减少了多次请求造成的性能问题,也避免因请求返回次序不同造成的数据错误。



    当点击详情按钮时,可显示社团内成员,并进行了分组,可对成员进行直接移除。
    5.4 公告管理5.4.1 新增公告
    此处使用react-draft-wysiwyg富文本编辑器制作新增公告功能。编辑好的内容会以HTML标签字符串的形式发送给后台。当发送空值时内容为 ’<p></p>’,此时发布会提示没有添加正文,是否确定提交。

    由于富文本中添加图片是一个比较复杂的操作,此富文本编辑器默认仅可选择在线图片,可增加选取本地图片的选项,但是需要自己后台支持。

    在此处选择图片时,图片限制不可超过500KB并且只可选择’jpg’, ‘jpeg’, ‘png’, ‘gif’这四种格式的图片,选择成功后,会自动将图片上传至服务器,服务器会将处理好的图片的url返回至前台并显示在虚框内,点击添加即可显示到富文本编辑器中,发布时此处会变为<img />标签。
    5.4.2 公告列表
    在此处使用了骨架屏,在数据未完整请求到时,显示骨架,并且为骨架添加必要的延迟,避免白页与页面闪烁。


    当骨架结束后,页面显示内容,对数据进行必要的处理后,点击预览按钮可以显示手机模拟器,预览本条公告。

    手机页面可以进行滚动,但由于在电脑浏览器端显示滚动会有滚动条,很难去掉,有兼容性问题,为了提高体验,显示滚动条,然后将内层盒子扩大出一个滚动条的宽高,再将外层盒子进行over-flow设置为hidden,超出部分不可见,更好地实现模拟手机的滚动功能。


    点击删除按钮时会弹出提示。

    确定后才会删除(逻辑删除)
    5.4.3 流量统计
    默认显示一周以内的公告流量数据,此处使用echarts制作环形图,点击右侧可进行过滤上部时间picker可以选择时间范围,默认为一周内。通过此功能,可以对公告的阅读量进行统计,以便推出更受欢迎的公告资讯。
    5.5 管理员管理只有超级管理员可进入此tab,普通管理员进入不显示这个菜单项,如果在url处输入对应路径,会进入no permission(无权限)页面。


    进入管理员管理页面后,可操作其他管理员但不可操作自己与其他超级管理员。


    此处填写表单均做了表单校验,新增与编辑同理。
    5.6 个人信息
    此处分为基本信息模块与密码模块,基本信息模块可修改用户基本信息,此处只设置了姓名,还可以增加手机号,性别,个性签名,身份证号等等。
    密码模块必须正确输入之前的密码,两次新密码必须一致,而且要满足以字母开头,长度在6~18之间,只能包含字母、数字和下划线才可提交。


    原密码错误会提示错误。
    1 评论 13 下载 2019-08-11 08:41:12 下载需要11点积分
  • 基于C++的通讯录系统

    一、实验的内容通讯录系统可帮助使用者管理归纳通讯录名单,达到添加,删除,修改,保存等需求。要求使用学习过的C/C++程序设计的知识完成通讯录系统的设计与实现。
    二、实验课题分析2.1 通讯录系统的主要功能通讯录主要功能为:添加通讯录成员,修改成员,删除成员,按需求搜索查看成员,保存为文档。

    系统各模块的功能具体描述为:
    添加成员模块
    提供界面让使用者输入希望加入的通讯录成员的各类信息(姓名,电话,住址,QQ,邮箱等),并检查格式是否有误。若格式无误,则将该通讯录信息通过二进制文件方式储存在./contact文件目录下。
    修改成员模块
    使用者可以重写已有的通讯录成员,增加或删除除姓名以外的各个信息。一条通讯录成员可以拥有多个电话号码或QQ。
    删除成员模块
    使用者可以选择某个不希望继续使用的通讯录成员并删除他们。
    搜索查看成员模块
    使用者通过各种方式查询已添加的通讯录成员,并决定是否修改或删除它们。提供的方法有:精准查询,模糊查询,按分类查询等。
    2.2 系统分析及设计系统开发使用Template Method设计模式和Strategy Patten 两种设计模式,较好的封装所需函数,使得主程序入口开发环节只需关注Contact.h头文件即可实现。具体类之间的耦合关系见下图:

    2.3 系统的实现2.3.1 类的编写系统工程名为:contacts。包含类:Person, Contact, ContactInterface, ContactInit等具体类结构声明如下:
    Person类
    class Person {Public: char name[MAXNAME]; char sex; char tel[MAXTEL]; char addr[MAXADDR]; char zip[MAXZIP]; char mail[MAXMAIL]; char qq[MAXQQ]; char category[MAXCTGY]; Person(); ~Person();};
    ContactInterface类
    class CheckInterface{public: bool check(Person&, const bool _check_repe) const; bool check_exact(const Person&, const string) const;virtual ~CheckInterface(){};private: vector<string> part_tq(const Person&, const char* const) const;};
    ContactInit类
    class ContactInit{public: virtual int refresh() const = 0; virtual void welcome() const= 0; virtual ~ContactInit(){};};
    Contact类
    class Contact : public ContactInterface, public ContactInit{private: MainStrategy* setMainStrategy(int); public: Contact(); ~Contact(); int refresh() const; void welcome() const;};
    MainStrategy类
    class MainStrategy : public CheckInterface{public: MainStrategy(); virtual ~MainStrategy(); virtual int doMainStrategy() = 0;protected: void printAll() const; void print_prsn(const Person&, const string, bool) const; bool delete_prsn(Person&) const; int modify_prsn(Person&) const; //Way to modify a spefic Person member, with 0->success, -1->fail};
    MainViewMenuInterface类
    class MainVewMenuInterface : public MainStrategy{public:private: ViewStrategy* viewStrategy; virtual ViewStrategy* setViewStrategy(int) = 0; virtual int view(Person* v_Person) const; public: MainVewMenuInterface(); virtual ~MainVewMenuInterface(); virtual int doMainStrategy();};
    类的关系设计

    各类的具体功能和说明如下:

    class Person; 提供基本的数据存储结构。class ContactInterface; 提供主函数菜单,策略选择方法。是Contact类的一个接口,MainStrategy的调用者。class ContactInit; 提供初始化程序所需函数。同样是Contact类的一个接口。class Contact; 具体实现了两个接口的方法。MainStrategy的决策者。同时面向调用者(main.cpp)。但注意Contact不提供任何public方法。需要通过两个接口调用。class CheckInterface; 提供检查函数。class MainStrategy; Strategy Patten设计模式。同时包含子类公用的方法。class MainNewMenu; class MainDelMenu; class MainMdfMenu; 分别override doMainStrategy()函数,实现新建,删除,修改功能。class MainVewMenuInterface; override doMainStrategy()函数,ViewStrategy的调用者。class MainVewMenu; ViewStrategy的决策者。class ViewStrategy; Strategy Patten。class ViewAllMenu; class ViewExactMenu;class ViewFuzzyMenu; class ViewCategoryMenu; 分别override doViewStrategy()函数,实现所有查找,精确查找,模糊查找,按类查找功能。

    通讯录实现流程图

    2.3.2 交互界面以及登录菜单的实现系统运行开始的界面如图所示

    主要通过选择结构和循环结构实现界面的前进和后退。例如,第一个登录界面出现5个选择:1.新建,2.删除,3.修改,4.浏览,5.退出
    三、实验调试、测试、运行记录及分析系统在调试测试过程中遇到若干问题,不过经过仔细反复的检查已经消除各种漏洞。
    主要的测试经过如下:

    在开始界面输入“1”即添加新的成员:

    若显示 Information Entry Success! 则录入数据成功。若显示Information Error! 则录入数据失败。如图则因为在电话(TEL)中出现了中文字符。随后将返回主界面。

    在主界面输入2可删除成员:

    如我们希望删除(2)数据,则键入2:

    就可以得到(2)的详细数据。输入y/n即可选择是否删除该成员。随后程序将返回主界面。
    在主界面下输入3可以修改已有的成员:我们希望修改刚刚加入的成员(1)的电话号码,同时加入他所有常用的QQ号码:

    键入1, 并按照需求修改信息:

    确认无误后即可修改信息。随后返回主界面
    输入4即可按需求分类查看搜索成员:

    键入1进行精准匹配,该模式下只匹配名字:如输入“严恩伟”后匹配信息如图:

    随后可以根据自身需求选择1-3选项。此处不再演示。
    在view模式下键入2进行模糊匹配,该模式匹配名字,电话,地址。只要出现匹配字符即给与显示。如输入“181”后显示:

    选择1-3即可进入其详细页面。此处不再演示。
    在view模式下键入3进行分类(category)匹配。该模式会列出所有的分类,并可根据用户选择的分类进行罗列(其中未设置的分类被标记为Unset):

    根据提示选择1-2即可进入相应页面。选择0即可退出。
    在view模式下键入4进行全局匹配。该模式会列出所有的成员:

    在view模式下键入5退出view模式。
    在主菜单中键入5退出程序。
    3 评论 32 下载 2019-02-01 17:54:46 下载需要10点积分
  • 基于TCP SOCKET实现的远程聊天系统

    一、实验目的本次实验需要完成一个实时聊天系统。该系统由聊天客户端和聊天服务器组成。客户端即可以给指定客户端发送信息,也可以把消息广播给所有客户端。通过这个实验,使得学生掌握socket API编程。
    二、实验内容该章节将会详细的列出服务器和客户端的功能要求。
    2.1 服务器的功能
    服务器能够并行处理客户端发送过来的消息或者命令
    服务器把从新连接的客户端中收到的第一条消息作为该客户端的用户名称
    服务器能够处理从客户端发送过来的两种类型的信息:

    广播信息,服务器收到信息后将其广播给其他客户端点对点信息,服务器将消息转发给指定客户端
    服务器需要把某个客户端发送的广播聊天信息,广播给和该客户端在同一个频道的所有客户端(但是不包括该用户端自己)这个广播消息的格式应为[<name>]:<message>。其中<name>为发送聊天信息的客户端名称,<message>为该客户端发送的消息
    当某个客户端离线时,服务器需要广播一个离线消息给所有的客户端
    容错处理。当客户端发送的数据和规定的格式不一致时,服务器能够处理这个错误并且返回错误信息

    2.2 客户端的功能
    从服务器返回的消息需要显示在控制台下,并且需要去除末尾多余的空格
    为了区别于其他人发送的消息,客户端自己发送的消息,需要在控制台中以“[me]:”显示

    三、操作环境
    操作系统:Mac OS
    编写语言:Java
    编译软件:Eclipse

    四、问题分析网络编程中两个主要的问题一个是如何准确的定位网络上一台或多台主机,另一个就是找到主机后如何可靠高效的进行数据传输。在TCP/IP协议中IP层主要负责网络主机的定位,数据传输的路由,由IP地址可以唯一地确定工nternet上的一台主机。而TCP层则提供面向应用的可靠(TCP)的或非可靠(UDP)的数据传输机制,这是网络编程的主要对象,一般不需要关心IP层是如何处理数据的。目前较为流行的网络编程模型是客户机/服务器(C/S)结构。即通信双方一方作为服务器等待客户提出请求并予以响应。客户则在需要服务时向服务器提出申请。服务器一般作为守护进程始终运行,监听网络端口,一旦有客户请求,就会启动一个服务进程来响应该客户,同时自己继续监听服务端口,使后来的客户也能及时得到服务。
    两类传输协议:TCP和UDP。TCP是Tranfer Control Protocol 的简称,是一种面向连接的保证可靠传输的协议。通过TCP协议传输,得到的是一个顺序的无差错的数据流。发送方和接收方的成对的两个socket之间必须建立连接,以便在TCP协议的基础上进行通信,当一个socket(通常都是server socket)等待建立连接时,另一个socket可以要求进行连接,一旦这两个socket 连接起来,它们就可以进行双向数据传输,双方都可以进行发送或接收操作。
    UDP是User Datagram Protocol的简称,是一种无连接的协议,每个数据报都是一个独立的信息,包括完整的源地址或目的地址,它在网络上以任何可能的路径传往目的地,因此能否到达目的地,到达目的地的时间以及内容的正确性都是不能被保证的。
    经过慎重的选择,本程序基于TCP传输协议,本人独立完成了整体设计,并部分借鉴了部分教材上使用的架构设计,逐项完成了实验要求的功能。

    五、详细设计5.1 总体设计思路单连接、单线程如果仅仅是客户端与服务器进行通信,则设计较为简单,仅需要在服务器开辟端口建立ServerSocket即可,用死等函数.accept()等待客户端Socket连接,一旦连接成功则在服务器建立Socket对象匹配客户端。具体描述如下:

    用指定的端口实例化一个Seve rSocket对象。服务器就可以用这个端口监听从客户端发来的连接请求
    调用ServerSocket的accept()方法,以在等待连接期间造成阻塞,监听连接从端口上发来的连接请求
    利用accept方法返回的客户端的Socket对象,进行读写I0的操作
    关闭打开的流和Socket对象

    其中,I0操作可以利用Socket提供的getlnputStream和getOutputStream方法得到输入和输出流,为方便起见,可以采用 Buffered Reader/PrintStream封装。

    客户端较为简单,只需要匹配到服务器的’P地址和相应的Port 即可。具体描述如下:

    用服务器的’P地址和端口号实例化Socket对象
    调用connect方法,连接到服务器上
    获得Socket上的流,把流封装进Buffered Reader/Pri ntStrea m的实例,以进行读写
    利用Socket提供的getlnputStream和getOutputStrea m方法,通过旧流对象,向服务器发送数据流

    关闭打开的流和Socket。

    单连接、多线程上述方法一个很致命缺点,这个连接没有开辟多线程,也就是说,服务器必须等待客户端发送来消息后才可以给一个回执消息,客户端操作过程也类似,但我们希望的应该是一个全双工的通信,因此必须开辟多线程。
    开辟多线程的方法很简单,只需要继承Runnable接口即可,然后将I0操作添加至run函数中。

    多连接、多线程现在基本的通讯问题己经解决了,服务器和客户端可以像发短信一样互相全双工的发送信息了,但这还远远不够,因为我们不能是针对两个用户的应用,我们的应用应该可以在服务器转发来自不同用户的消息,并且实现私聊和群聊的区别,因此,我们需要做一个概要设计,之后会详细讲述每部分的内容。
    如图,大体的设计架构如下,总共分为三个包:


    Client包:与客户端相关的一切信息,其中Login类实现客户端到服务器的登陆,Register类实现新用户信息的注册,ChatRoom为聊天主界面,ChatFra m为私聊窗口。
    Server包:完成服务器相关功能实现,FileOpen用于识别登录用户的各项信息,由于时间紧迫,暂未连接数据库,采用的是文本文件存储用户内容,Connection类用于开辟端口等待连接,同样采用多线程技术为每个链接进来的用户开辟线程,并保存所有己连接用户的信息,并保存他们的进程,ChatCu stomer类用于保存连接用户的全部信息,并完成登陆、注册消息的处理以及聊天内容、文件等转发操作。
    Util包:为服务器客户端通用内容,Customer保留了每个登陆用户的信息,Message定义了消息的类别、内容、发送方、接收方以及其他信息,Mtype类定义各种用到的常量。

    Util包具体内容Mtype类定义各种常量,主要分两类:消息类别和IP/Port

    Message类
    如图,主要定义了消息类型、消息内容、发送方、接收方以及其他信息(主要用于文件传送),类中用set函数和get函数为消息赋值和得到消息内容。
    Customer类
    同Message类,主要为得到己连接用户的各项基本信息。
    Server包Connection类由基本的ServerSocket功能扩展而来,主要为并行处理客户端发送过来的消息或者命令,并保存各个连接的客户线程。

    Vector可以更好的处理并发功能,在客户端窗口顶端显示在线的人数。

    采用多线程,并行处理多个连接。并可以返回在线用户列表。
    ChartCustomer类该类主要是处理每个连接到服务器的客户端线程。
    根据发送来的消息,可以分为:登陆消息、离线消息、注册消息、普通聊天消息、文件传送消息,以及应对错误错误消息格式的反馈。在此,如果一个线程断开,认为该用户已离线,发出离线消息给所有用户。

    对于每个消息,服务器都会解析消息的具体内容,对于登录的用户,需要判断该账号是否己经在线、用户名密码是否正确,如果成功登陆则为该用户保留线程资源的同时发送该用户上线的消息给所有在线用户;对于注册用户,注册时需要判断用户名是否己存在,存在则返回注册失败的消息,否则保留该用户的资料;处理文件消息和聊天消息在服务器端是相同的,只需要判断是发送给所有用户的还是发送给单独用户得即可,在服务器实现转发功能。

    FileOpen类保存用户资料。

    Client类由于此部分代码多为界面设计,在此不一一罗列。
    Login类登陆界面,输入用户名密码登陆,登录成功会显示聊天主界面,登录失败会返回失败原因
    Register类用户注册界面,注册失败会予以反馈。
    ChartRoom类聊天主界面,可选择在线用户进行私聊或广播消息,同样可以给用户传送文件或给所有用户群发文件。
    在系统消息一栏,用于显示各种系统消息,如:用户上线、下线提醒,新消息通知、文件传送提醒等。
    ChatFrame类类似腾讯00的聊天界面,可以从本地同步聊天记录,并允许发送消息、接收消息。
    六、测试登录界面

    聊天室主界面

    可以实现私聊功能

    可以实现广播功能

    可以实现文件传送

    离线有提醒
    4 评论 31 下载 2018-11-05 11:58:38 下载需要7点积分
  • 基于winform实现的仿windows画板绘图程序

    1、设计目标和要求
    用面向对象技术实现一个类似于Windows画板的程序
    要求实现生成图形的位图文件和JPEG文件保存和打开功能

    2、编译与运行说明
    Windows操作系统
    VS2008下编译
    装有.Net FrameWork 3.5及以上版本运行库
    运行时exe文件需与光标文件getcolor.cur、paint.cur、pen.cur在同一目录下,否则会报错

    3、算法原理介绍本程序图形界面采用.net framework 3.5中的winform制作而成,用bitmap类对象存储图像,用picturebox控件显示图像,用Graphics类对象修改图像,用鼠标事件响应鼠标操作,用绘图事件进行即时反馈(绘图交互)。
    实现了:调整颜色、打开保存(支持bmp、gif、exif、jpg、png、tiff格式)、铅笔、画刷、线段、圆形、矩形、取色器、油漆桶、橡皮等功能。其中,线段、圆形、矩形带有画图即时反馈,圆形、矩形支持空心于实心两种模式;油漆桶采用种子填充算法;画刷、橡皮支持调整大小;铅笔、画刷、橡皮由于鼠标事件MouseMove的响应频率不足使得取到的点断断续续,采用将相邻的点用直线联接起来,实际测试效果良好;所有工具均用光标自定义或绘图事件的方式实现了,光标在绘图区内的变化。
    4、系统设计和实现//Form1.h#pragma oncenamespace 关键计算机图形学课程设计{ using namespace System; using namespace System::ComponentModel; using namespace System::Collections; using namespace System::Windows::Forms; using namespace System::Data; using namespace System::Drawing; public ref class Form1 : public System::Windows::Forms::Form { public: Form1(void);//构造函数 protected: /// <summary> /// 清理所有正在使用的资源。 /// </summary> ~Form1()//析构函数//以下是各控件、对象及用到变量声明 private: System::Windows::Forms::ToolStripPanel^ BottomToolStripPanel; protected: private: System::Windows::Forms::ToolStripPanel^ TopToolStripPanel; private: System::Windows::Forms::ToolStripPanel^ RightToolStripPanel; private: System::Windows::Forms::ToolStripPanel^ LeftToolStripPanel; private: System::Windows::Forms::ToolStripContentPanel^ ContentPanel; private: System::Windows::Forms::MenuStrip^ menuStrip1; private: System::Windows::Forms::ToolStrip^ toolStrip1; private: System::Windows::Forms::ToolStripButton^ brush; private: System::Windows::Forms::ToolStripMenuItem^ 文件ToolStripMenuItem; private: System::Windows::Forms::ToolStripMenuItem^ 新建ToolStripMenuItem; private: System::Windows::Forms::ToolStripMenuItem^ 打开ToolStripMenuItem; private: System::Windows::Forms::ToolStripMenuItem^ 保存ToolStripMenuItem; private: System::Windows::Forms::PictureBox^ pictureBox1; private: System::Windows::Forms::ColorDialog^ colorDialog1; private: System::Windows::Forms::OpenFileDialog^ openFileDialog1; private: System::Windows::Forms::SaveFileDialog^ saveFileDialog1; private: System::Windows::Forms::ToolStrip^ toolStrip2; private: System::Windows::Forms::ToolStripButton^ circle; private: array<Bitmap^> ^b; private: Graphics ^gra; private: System::Windows::Forms::Panel^ panel1; private: System::Windows::Forms::TableLayoutPanel^ tableLayoutPanel1; private: System::Windows::Forms::PictureBox^ pictureBox2; private: Bitmap ^pre; private: Graphics ^gp; private: Pen ^p; private: Color ^c1,^c2; private: int fx,fy,tx,ty,no,f,t,size; static const int mo=100; bool ld,rd,cv; array<Int32> ^lx,^ly; private: System::Windows::Forms::ToolStripMenuItem^ 编辑ToolStripMenuItem; private: System::Windows::Forms::ToolStripMenuItem^ 撤销ToolStripMenuItem; private: System::Windows::Forms::ToolStripMenuItem^ 重复ToolStripMenuItem; private: System::Windows::Forms::ToolStripButton^ sqare; private: System::Windows::Forms::FlowLayoutPanel^ flowLayoutPanel1; private: System::Windows::Forms::PictureBox^ color2; private: System::Windows::Forms::PictureBox^ color1; private: System::Drawing::Printing::PrintDocument^ printDocument1; private: System::Windows::Forms::PictureBox^ pictureBox3; private: System::Windows::Forms::PictureBox^ pictureBox4; private: System::Windows::Forms::PictureBox^ pictureBox5; private: System::Windows::Forms::PictureBox^ pictureBox6; private: System::Windows::Forms::PictureBox^ pictureBox7; private: System::Windows::Forms::PictureBox^ pictureBox8; private: System::Windows::Forms::PictureBox^ pictureBox9; private: System::Windows::Forms::PictureBox^ pictureBox10; private: System::Windows::Forms::PictureBox^ pictureBox11; private: System::Windows::Forms::PictureBox^ pictureBox12; private: System::Windows::Forms::PictureBox^ pictureBox13; private: System::Windows::Forms::PictureBox^ pictureBox14; private: System::Windows::Forms::PictureBox^ pictureBox15; private: System::Windows::Forms::PictureBox^ pictureBox16; private: System::Windows::Forms::PictureBox^ pictureBox17; private: System::Windows::Forms::PictureBox^ pictureBox18; private: System::Windows::Forms::PictureBox^ pictureBox19; private: System::Windows::Forms::PictureBox^ pictureBox20; private: System::Windows::Forms::PictureBox^ pictureBox21; private: System::Windows::Forms::PictureBox^ pictureBox22; private: System::Windows::Forms::PictureBox^ pictureBox23; private: System::Windows::Forms::PictureBox^ pictureBox24; private: System::Windows::Forms::PictureBox^ pictureBox25; private: System::Windows::Forms::PictureBox^ pictureBox26; private: System::Windows::Forms::PictureBox^ pictureBox27; private: System::Windows::Forms::PictureBox^ pictureBox28; private: System::Windows::Forms::PictureBox^ pictureBox29; private: System::Windows::Forms::PictureBox^ pictureBox30; private: System::Windows::Forms::PictureBox^ pictureBox31; private: System::Windows::Forms::PictureBox^ pictureBox32; private: System::Windows::Forms::PictureBox^ pictureBox33; private: System::Windows::Forms::PictureBox^ pictureBox34; private: System::Windows::Forms::PictureBox^ pictureBox35; private: System::Windows::Forms::PictureBox^ pictureBox36; private: System::Windows::Forms::PictureBox^ pictureBox37; private: System::Windows::Forms::PictureBox^ pictureBox38; private: System::Windows::Forms::PictureBox^ pictureBox39; private: System::Windows::Forms::PictureBox^ pictureBox40; private: System::Windows::Forms::PictureBox^ pictureBox41; private: System::Windows::Forms::PictureBox^ pictureBox42; private: System::Windows::Forms::PictureBox^ pictureBox43; private: System::Windows::Forms::PictureBox^ pictureBox44; private: System::Windows::Forms::PictureBox^ pictureBox45; private: System::Windows::Forms::PictureBox^ pictureBox46;private: System::Windows::Forms::ToolStripButton^ pen;private: System::Windows::Forms::Panel^ panel2;private: System::Windows::Forms::Panel^ panel3;private: System::Windows::Forms::PictureBox^ pictureBox47;private: System::Windows::Forms::PictureBox^ pictureBox48;private: System::Windows::Forms::PictureBox^ pictureBox49;private: System::Windows::Forms::PictureBox^ pictureBox50;private: System::Windows::Forms::PictureBox^ pictureBox51;private: System::Windows::Forms::ToolStripMenuItem^ 清除ToolStripMenuItem; ToolStripButton ^mod1,^mod2;private: System::Windows::Forms::ToolStripButton^ fillin;private: System::Windows::Forms::ToolStripButton^ line;private: System::Windows::Forms::ToolStripButton^ erazer;private: System::Windows::Forms::ToolStripButton^ getcolor;private: System::Windows::Forms::ToolStripButton^ empty;private: System::Windows::Forms::ToolStripButton^ ssub;private: System::Windows::Forms::ToolStripButton^ filled;private: System::Windows::Forms::ToolStripButton^ sadd;private: System::Windows::Forms::ToolStripMenuItem^ 另存为ToolStripMenuItem; System::ComponentModel::Container ^components;void InitializeComponent(void)//各控件初始化private: System::Int32 add1(int x);private:System::Int32 sub1(int x);private:System::Void next();//重复的实现private:System::Void last();//撤销的实现private:System::Void update()//对pictureBox1上显示的图像进行更新private:System::Void newb()//创建新bitmap来保存修改(用来实现撤销与重复功能)private:System::Void newb(int x,int y)//在创建bitmap同时改变图象大小(可实现缩放)private: System::Void Form1_Load(System::Object^ sender, System::EventArgs^ e) //窗体加载事件各变量初始化private: System::Void pictureBox2_MouseUp(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e)//用来响应拖动右下角小白块时缩放的鼠标操作private: System::Void 撤销ToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e) //撤销菜单private: System::Void 重复ToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e) //重复菜单private: System::Void setcolor(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) //用来设置前景色或后景色private: System::Void pictureBox3_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) 以下是个眼色框的鼠标事件,用来调整颜色private: System::Void pictureBox4_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) private: System::Void pictureBox5_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) private: System::Void pictureBox6_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) private: System::Void pictureBox7_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e)private: System::Void pictureBox8_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) private: System::Void pictureBox9_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) private: System::Void pictureBox10_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) private: System::Void pictureBox11_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) private: System::Void pictureBox12_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e)private: System::Void pictureBox13_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e)private: System::Void pictureBox14_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) private: System::Void pictureBox15_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) private: System::Void pictureBox16_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) private: System::Void pictureBox17_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) private: System::Void pictureBox18_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) private: System::Void pictureBox19_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) private: System::Void pictureBox20_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) private: System::Void pictureBox21_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) private: System::Void pictureBox22_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e)private: System::Void pictureBox23_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) private: System::Void pictureBox24_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) private: System::Void pictureBox25_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e)private: System::Void pictureBox26_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) private: System::Void pictureBox27_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) private: System::Void pictureBox28_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) private: System::Void pictureBox29_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) private: System::Void pictureBox30_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e)private: System::Void pictureBox31_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) private: System::Void pictureBox32_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) private: System::Void pictureBox33_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) private: System::Void pictureBox34_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e)private: System::Void pictureBox35_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) private: System::Void pictureBox36_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e)private: System::Void pictureBox37_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e)private: System::Void pictureBox38_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) private: System::Void pictureBox39_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e)private: System::Void pictureBox40_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) private: System::Void pictureBox41_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) private: System::Void pictureBox42_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e)private: System::Void pictureBox43_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) private: System::Void pictureBox44_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) private: System::Void pictureBox45_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) private: System::Void pictureBox46_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) private: System::Void pictureBox47_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) private: System::Void pictureBox48_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e)private: System::Void pictureBox49_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) private: System::Void pictureBox50_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) private: System::Void color1_DoubleClick(System::Object^ sender, System::EventArgs^ e)//用来双击调出颜色选择对话框private: System::Void color2_DoubleClick(System::Object^ sender, System::EventArgs^ e) //用来双击调出颜色选择对话框private: System::Void 清除ToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e) //清除菜单实现private: System::Void pen_Click(System::Object^ sender, System::EventArgs^ e)//实现了铅笔按钮private: System::Void brush_Click(System::Object^ sender, System::EventArgs^ e) //刷子按钮private: System::Void circle_Click(System::Object^ sender, System::EventArgs^ e)//圆形工具private: System::Void sqare_Click(System::Object^ sender, System::EventArgs^ e)//矩形工具private: System::Void fillin_Click(System::Object^ sender, System::EventArgs^ e)油漆桶工具private: System::Void line_Click(System::Object^ sender, System::EventArgs^ e) //线段工具private: System::Void getcolor_Click(System::Object^ sender, System::EventArgs^ e) //取色器工具private: System::Void erazer_Click(System::Object^ sender, System::EventArgs^ e) //橡皮工具private: System::Void filled_Click(System::Object^ sender, System::EventArgs^ e) //实心模式private: System::Void empty_Click(System::Object^ sender, System::EventArgs^ e) //空心模式private: System::Int32 fitx(int x)//检查x坐标是否在范围内private: System::Int32 fity(int y) //检查y坐标是否在范围内private: System::Boolean fit(int x,int y) //检查x、y坐标是否在范围内private: System::Void mmove(System::Windows::Forms::MouseEventArgs^ e)//实现了各工具拖动鼠标时的响应private: System::Void pictureBox1_MouseMove(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e)//鼠标移动事件调用了mmove()private: System::Void pictureBox1_MouseDown(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) //鼠标按下事件实现了各工具按下鼠标时的响应private: System::Void pictureBox1_MouseUp(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) //鼠标放开事件实现了各工具放开鼠标时的相应private: System::Void pictureBox1_MouseEnter(System::Object^ sender, System::EventArgs^ e) //鼠标进入事件用来配合光标的改变(自己绘制光标时将光标隐藏)private: System::Void sadd_Click(System::Object^ sender, System::EventArgs^ e) //尺寸增大按钮的实现private: System::Void ssub_Click(System::Object^ sender, System::EventArgs^ e) //尺寸缩小按钮的实现private: System::Void pictureBox1_Paint(System::Object^ sender, System::Windows::Forms::PaintEventArgs^ e) //绘图事件用来绘制部分工具光标及绘制绘画即时响应private: System::Void pictureBox1_MouseLeave(System::Object^ sender, System::EventArgs^ e) //鼠标离开事件用来配合部分光标的绘制private: System::Void tchong(int ix,int iy,Color c)//4邻域种子填充private: System::Void 打开ToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e)// 打开菜单的实现private: System::Void 另存为ToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e)//另存为菜单的实现private: System::Void openFileDialog1_FileOk(System::Object^ sender, System::ComponentModel::CancelEventArgs^ e) //打开对话框确定的实现private: System::Void saveFileDialog1_FileOk(System::Object^ sender, System::ComponentModel::CancelEventArgs^ e) //保存对话框确定的实现}
    5、运行结果程序界面

    铅笔演示

    油漆桶演示

    矩形(实心)演示

    圆形(空心)演示

    各种尺寸的刷子、橡皮

    保存对话框

    6、设计总结这次设计令我受益匪浅,使我更加熟练的掌握了winform中有关图形和鼠标处理的部分,设计历时10天虽然比较累但是也充满了趣味和成就感,相信在这次设计中我获得的经验必然能在以后派上更大的用处。
    2 评论 16 下载 2019-03-20 16:11:39 下载需要13点积分
  • 基于C语言实现的旅店管理系统

    1 需求分析通过和指导老师交流,了解到本系统中的数据来源于标准输入设备(如键盘)或者来自某文件,操作主要有客人入住、客人续住、客人离开、客人查询。总之,可以实现对旅店系统的住宿管理、客房管理、查询统计。
    1.1 住宿管理客人来住宿时首先需要查找是否有空房,若有,则询问客人需要哪种类型的客房(住房价格,随行人数)。客人决定后需要输入自己的身份证号、姓名、性别、电话号码、入住时长等必要信息。客人如果对客房不满意,可以提出更换房间或是退房。客人住宿时间结束后可以办理退房手续。输入时需要对身份证号以及电话号码进行判断,若符合标准则继续,否则重新输入。
    1.2 客房管理考虑到实际需要,对客房的管理包括对客房的增加、删除、修改、查找空余房间、查找房间的设置等功能。
    1.3 查询统计输入客房编号能够查找到是否有人入住,如果有人入住,则输出客人信息,否则显示空房。输入客人的身份证号能查询到是否此人入住,若入住,输出客人信息,否则输出无人入住。可以查找即将欠费的客房。可以查找指定价格区间与几人间的住房。
    2 概要设计2.1 数据类型的定义2.1.1 定义客人的元素类型typedef struct{ int year; int mon; int day;}ruzhutime;//入住时间的结构体typedef struct patron//客官信息{ int guenum;//客人住房编号 char name[20];//客人姓名 char idnum[19];//客人身份证号 char phone[12];//电话号码 char sex[5];//客人性别 ruzhutime time;//入住时间 int timelong;//租房时长 long int s;//标记时间}parton;
    2.1.2 定义客房的元素类型typedef struct Guest//住房 { int guenum;//客房编号 int maxnum;//最大入住人数 int shifouruzhu;//是否有人入住 double zujin;//房租 int num;//已入住人数 }Guest;
    2.1.3 定义函数返回类型typedef int Index;//hash地址类型typedef void Status;//返回类型
    2.2 功能模块结构图根据需求分析,为了满足用户的功能需求,按照软件开发方法学中的模块划分原则,我将本系统主要划分为如下模块:住宿管理、客房管理、查询统计。各模块之间的关系如图所示。
    主菜单模块结构图

    住宿管理模块结构图

    客房管理模块结构图

    查询统计模块结构图

    为了实现上述功能模块,分别在各个模块物理结构上定义了多个函数,本系统定义的函数和功能如下:
    2.2.1 住宿管理部分Status ruzhu();功能:入住操作。 Status tuifang();功能:退房操作。 Status xufang();功能:续房操作。 Status findprice();功能:筛选符合需求的客房。Status findkefang();功能:查找空的客房。
    2.2.2 客房管理部分Status weihu();功能:客房信息的维护。 Status InputRoom();功能:初始化客房信息。 Status writetotxt();功能:将客房信息写入到kefangxinxi.txt文件中。 Status write1totxt();功能:将客人信息写入到kerenxinxi.txt文件中。 Status readfromtxt();功能:将客房信息从kefangxinxi.txt文件中读出到hash表中。 Status read1fromtxt();功能:将客人信息从kerenxinxi.txt读出到hash1表中。Status kefangxinxi();功能:输出客房信息。 Status xiugaikefang();功能:修改客房信息。 Status tainjiakefang();功能:添加客房。 Status shanchukefang();功能:删除客房。 Status zhusuguanli();功能:住宿管理菜单。
    2.2.3 查询统计模块Status findkefang();功能:查找空的客房。 Status findkeren();功能:查找客人。 Status findofroom();功能:按照客房查找客人。 Status findofname();功能:按照姓名查找客人。 Status kerenxinxi();功能:输出客人信息。 Index HashIndex(int num);功能:查找num在hash函数中的位置(取num的后三//位并返回后三位)。Status daoqitixing();功能:输出即将到期的客人
    2.2.4 其他辅助函数Status writetotxt();功能:将客房信息写入到kefangxinxi.txt文件中。Status write1totxt();功能:将客人信息写入到kerenxinxi.txt文件中。Status readfromtxt();功能:将客房信息从kefangxinxi.txt文件中读出到hash表中。 Status read1fromtxt();功能:将客人信息从kerenxinxi.txt读出到hash1表中。Index HashIndex(int num);功能:查找num在hash函数中的位置(取num的后三位并返回后三位)。
    3 运行环境
    硬件环境:PC机内存 256M;硬盘40G
    软件环境:操作系统:Windows 10

    4 开发工具和编程语言
    开发环境:DEV C++
    编程语言:C语言

    5 详细设计在概要设计的基础上,对每个模块进行内部逻辑处理部分详细设计。下面分别列出各个模块具体实现流程图。
    5.1 添加客房信息首先定义一个哈希表,其表长为1000,每输入一个客房编号num,进行取后三位数操作(以下出现num,凡是不做特殊说明,均认为取后三位),输入的客房信息为-1时结束。具体操作如下图:

    5.2 删除客房信息删除时首先输入需要删除的客房编号num,如果此房间存在则删除此房间,否则输出不存在此房间。具体操作如下图:

    5.3 修改客房信息输入需要修改的客房的编号num,当此客房存在时继续输入修改后的信息,否则输出不存在此房间,具体操作如下图:

    5.4 查询客房信息输入客房编号num不为0,则存在此房间,并输出此房间信息,否则输出查无此房,具体实现如下图:

    5.5 客人入住输入客人入住的编号num,当num不为0,并且入住人数不小于需要入住的人数时,将其信息置为客人信息,操作过程如下:

    5.6 客人退房输入客房编号num,当num不为0时,将客人信息的客房信息换为0,并将客房信息修改为无人入住。具体操作如下:

    5.7 客人续房输入需要续住的客房的编号num,判断其是否有人入住,并输出此房间的信息,以便于客人浏览并确认是否进行续费。若确认续费,则输入续住的天数。将客人的信息进行调整后再保存到文件中。具体实现过程如下图:

    5.8 筛选符合条件的客房首先输入需要查找的客房信息,包括价格区间,同行人数等必要信息,然后查找符合条件的信息并输出,具体过程如下:

    5.9 查找客人查找包括两部分。1.查找指定客房的客人。2. 查找指定姓名的客人。调用如下:

    5.10 按照房间号查找客人输入房间号num,如果房间号里的信息有人入住,则输出此人的信息。否则,输出此房间尚未租出。具体过程如下:

    5.11 按照姓名查找
    5.12 查询即将到期的客人遍历所有客人,判断其入住时间是否离到期时间小于三天,若是,则输出此客人,否则不输出。具体过程如下:

    5.13 哈希函数传递一个房间编号(整型变量),返回该数的后三位作为客房的存储地址。具体操作流程如下:

    5.14 将信息写入文件首先以需要打开的方式打开需要打开的文件,将非空信息写入文件,关闭文件。具体流程如下图所示:

    5.15 将信息从文件读出首先以需要打开的方式打开需要打开的文件,将信息读出到哈希表中,关闭文件。具体流程如下图所示:

    6 程序编码根据详细设计的流程图转化为如下代码,下面列出部分代码:
    /*住宿管理模块:包括客房预订、入住登记、客人续往、调房登记、退房结账客房管理模块:包括客房设置、客房查询、宿费提醒查询统计模块:包括预订房查询、住宿查询、退房查询、客房宿费统计*/#include<stdio.h>#include<stdlib.h>#include<string.h>#include<time.h> #define MAX 1000//哈希表的表长 typedef int Index;//hash地址类型 typedef void Status;//返回类型 Status weihu();//客房信息的维护 Status InputRoom();//初始化客房信息 Status writetotxt();//将客房信息写入到kefangxinxi.txt文件中 Status write1totxt();//将客人信息写入到kerenxinxi.txt文件中 Status readfromtxt();//将客房信息从kefangxinxi.txt文件中读出到hash表中 Status read1fromtxt();//将客人信息从kerenxinxi.txt读出到hash1表中Index HashIndex(int num);//查找num在hash函数中的位置(取num的后三位并返回后三位)Status kefangxinxi();//输出客房信息 Status xiugaikefang();//修改客房信息 Status tainjiakefang();//添加客房 Status shanchukefang();//删除客房 Status zhusuguanli();//住宿管理菜单 Status ruzhu();//入住操作 Status tuifang();//退房操作 Status xufang();//续房操作 Status findprice();// 筛选符合需求的客房Status findkefang();//查找空的客房 Status findkeren();//查找客人 Status findofroom();//按照客房查找客人 Status findofname();//按照姓名查找客人 Status kerenxinxi();//输出客人信息 Status daoqitixing();//输出即将到期的客人 /*住宿管理模块:包括客房预订、入住登记、客人续住、调房登记、退房结账*/typedef struct{ int year; int mon; int day;}ruzhutime;//入住时间的结构体 typedef struct patron//客官信息{ int guenum;//客人住房编号 char name[20];//客人姓名 char idnum[19];//客人身份证号 char phone[12];//电话号码 char sex[5];//客人性别 ruzhutime time;//入住时间 int timelong;//租房时长 long int s; }parton;parton hash1[MAX];//定义客人的哈希表 typedef struct Guest//住房 { int guenum;//客房编号 int maxnum;//最大入住人数 int shifouruzhu;//是否有人入住 double zujin;//房租 int num;//已入住人数 }Guest;Guest hash[MAX];//定义客房的哈希表 Index HashIndex(int num)//查找num在hash函数中的位置(取num的后三位并返回后三位){ return num%MAX;//取后三位 }Status InputRoom()//初始化房间信息 { int i; printf("注意!!!是否继续,此操作会将原客房信息覆盖且无法恢复(Y/N)\n"); char choice;//确认操作 scanf("%*c%c", &choice); if(choice!='Y'&&choice!='y') return; Guest room; printf("请输入房间编号,(编号为-1时结束输入)\n"); while(scanf("%d", &room.guenum),room.guenum!=-1)//输入信息 { i=room.guenum%1000; if(hash[i].guenum)//如果已经输入过此房间则重新输入 { printf("已存在此房间!请重新输入!\n"); continue; } printf("请输入最大入住人数\n"); scanf("%d", &room.maxnum); printf("请输入租金:\n"); scanf("%lf", &room.zujin); printf("请输入是否有人入住:(1=有;0=没有)\n"); scanf("%d",&room.shifouruzhu); printf("请输入入住人数:\n"); scanf("%d", &room.num); hash[i]=room; printf("请输入房间编号,(编号为-1时结束输入)\n"); } printf("房间编号 最大入住人数 租金 是否有人 入住人数\n"); for(i=0;i<MAX;i++)//输出操作后的客房信息 if(hash[i].guenum) printf("%d %d %.2f %d %d\n", hash[i].guenum,hash[i].maxnum,hash[i].zujin, hash[i].shifouruzhu,hash[i].num); writetotxt();//将客房信息写入文件 }Status weihu()//客房信息的维护 { while(1) { printf("************************************\n"); printf("**** 重新输入客房信息请输入 1: ***\n"); printf("**** 修改客房信息请输入 2: ***\n"); printf("**** 添加客房信息请输入 3: ***\n"); printf("**** 删除客房信息请输入 4: ***\n"); printf("**** 返回上一菜单请输入 0: ***\n"); printf("************************************\n"); int n; printf("请输入您的选择:\n"); if(scanf("%d", &n)!=1) { printf("输入错误!\n已退出!\n"); return; } switch(n)//对应的选择 { case 1:InputRoom();break;//初始化客房信息 case 2:xiugaikefang();break;//修改客房信息 case 3:tainjiakefang();break;//添加客房 case 4:shanchukefang();break;//删除客房 case 0:return;//返回上一菜单 } }}Status shanchukefang()//删除客房{ int i,num; printf("请输入要删除的客房编号:\n"); if(scanf("%d", &num)!=1) { printf("输入错误!\n已退出!\n"); return; } i=HashIndex(num);//哈希地址 if(!hash[i].guenum)//如果客房编号为0 { printf("此客房不存在!\n"); return ; } printf("删除不可恢复,是否要删除!Y/N\n");//确认操作 char c; if(scanf("%*c%c", &c)!=1) { printf("输入错误!\n已退出!\n"); return; } if(c!='Y'&&c!='y')//取消操作 return; hash[i].guenum=0;//将客房信息初始化为0 writetotxt();//写入文件 printf("删除成功!\n");}Status tainjiakefang()//添加客房信息 { int i,num; printf("请输入需要添加的客房编号:\n"); if(scanf("%d", &num)!=1) { printf("输入错误!\n已退出!\n"); return; } i=HashIndex(num);//哈希地址 if(hash[i].guenum)//房间号不为空 { printf("此房间已存在!\n"); return; } hash[i].guenum=num; printf("请输入最大入住人数\n"); scanf("%d", &hash[i].maxnum); printf("请输入租金:\n"); scanf("%lf", &hash[i].zujin); printf("请输入是否有人入住:(1=有;0=没有)\n"); scanf("%d",&hash[i].shifouruzhu); printf("请输入入住人数:\n"); scanf("%d", &hash[i].num); printf("添加成功!\n"); writetotxt();}Status xiugaikefang()//修改客房信息 { printf("请输入需要修改的客房的编号:\n"); int num,i,j; scanf("%d", &num); i=HashIndex(num);//哈希地址 if(!hash[i].guenum)//客房编号为空 { printf("不存在此房间!\n"); return; } printf("此房间信息为:\n");//修改客房之前输出此客房信息便于确认 printf("房间编号 最大入住人数 租金 是否有人 入住人数\n"); printf("%d %d %.2f %d %d\n", hash[i].guenum,hash[i].maxnum,hash[i].zujin, hash[i].shifouruzhu,hash[i].num); printf("是否要修改 是(Y)否(N)\n"); char c; scanf("%*c%c", &c); if(c!='y'&&c!='Y')//确认删除 { printf("修改取消!\n"); return; } printf("请输入最大入住人数\n"); scanf("%d", &hash[i].maxnum); printf("请输入租金:\n"); scanf("%lf", &hash[i].zujin); printf("请输入是否有人入住:(1=有;0=没有)\n"); scanf("%d",&hash[i].shifouruzhu); printf("请输入入住人数:\n"); scanf("%d", &hash[i].num); printf("修改成功!\n"); printf("修改后的信息为:\n"); printf("此房间信息为:\n"); printf("房间编号 最大入住人数 租金 是否有人 入住人数\n"); printf("%d %d %.2f %d %d\n", hash[i].guenum,hash[i].maxnum,hash[i].zujin, hash[i].shifouruzhu,hash[i].num); writetotxt();//将修改后的信息写入文件 }Status kefangxinxi()//输出客房信息{ int i; printf("房间编号 最大入住人数 租金 是否有人 入住人数\n"); for(i=0;i<MAX;i++) if(hash[i].guenum)//存在客房 printf("%d %d %.2f %d %d\n", hash[i].guenum,hash[i].maxnum,hash[i].zujin, hash[i].shifouruzhu,hash[i].num);}Status search()//查找模块 { while(1) { printf("************************************\n"); printf("**** 查找客房信息请输入 1: ***\n"); printf("**** 查找客人信息请输入 2: ***\n"); printf("**** 浏览所有住房信息请输入 3: ***\n"); printf("**** 浏览所有客人信息请输入 4: ***\n"); printf("**** 筛选客房请输入输入 5: ***\n"); printf("**** 输出快到期的客人请输入 6: ***\n"); printf("**** 返回上一菜单请输入 0: ***\n"); printf("************************************\n"); int n; printf("请输入您的选择:\n"); if(scanf("%d", &n)!=1) { printf("输入错误!\n已退出!\n"); return; } switch(n) { case 1:findkefang();break;//查找客房 case 2:findkeren();break;//查找客人 case 3:kefangxinxi();break;//客房信息输出 case 4:kerenxinxi();break;//客人信息输出 case 5:findprice();break;//筛选指定的客房 case 6:daoqitixing();break; case 0:return;//返回上一菜单 } }}Status daoqitixing(){ int i,j=0; time_t rawtime;//获取时间标记 printf("即将到期客人信息为:\n"); for(i=0;i<MAX;i++) if(hash1[i].guenum) if(hash1[i].s-time(&rawtime)<=hash1[i].timelong*24*60*60) { printf("%d %d %.2f %d %d\n", hash[i].guenum,hash[i].maxnum,hash[i].zujin, hash[i].shifouruzhu,hash[i].num); j=1; } if(!j) printf("不存在快到期的客人!\n");}Status findprice()//筛选指定客房 { printf("请输入指定的价格区间:\n"); printf("请输入最小的价格:\n"); double low; scanf("%lf", &low); printf("请输入最大的价格:\n"); double high; scanf("%lf", &high); printf("请输入人数:\n"); int s; scanf("%d",&s); int i; printf("房间编号 最大入住人数 租金 是否有人 入住人数\n"); for(i=0;i<MAX;i++) if(hash[i].zujin>=low&&hash[i].zujin<=high&&hash[i].maxnum>=s)//符合筛选条件 printf("%d %d %.2f %d %d\n", hash[i].guenum,hash[i].maxnum,hash[i].zujin, hash[i].shifouruzhu,hash[i].num);}Status kerenxinxi()//输出客人信息 { int i,j=0; for(i=0;i<MAX;i++) if(hash1[i].guenum)//如果有客人 { printf("住宿房间号:%d 姓名:%s 身份证号:%s 电话号码: %s 性别: %s \n",hash1[i].guenum,hash1[i].name, hash1[i].idnum,hash1[i].phone,hash1[i].sex); j=1;//标记有客人 } if(!j) printf("无人入住!\n");}Status findkeren()//查找客人 { int i,j=0; printf("*********************************\n"); printf("**** 按照房间查找请输入 1 ****\n"); printf("**** 按照姓名查找请输入 2 ****\n"); printf("*********************************\n"); printf("请输入您的选择:\n"); int n; scanf("%d", &n); switch(n) { case 1:findofroom();break;//按房间查找 case 2:findofname();break;//按姓名查找 }}Status findofname()//按姓名查找 { char name[20]; printf("请输入客人姓名:\n"); scanf("%s", name); int i,j=0; for(i=0;i<MAX;i++) if(!strcmp(hash1[i].name,name))//要查找的姓名与客人相同 { printf("住宿房间号:%d 姓名:%s 身份证号:%s 电话号码: %s 性别: %s \n",hash1[i].guenum,hash1[i].name, hash1[i].idnum,hash1[i].phone,hash1[i].sex); j=1;//找到标记 } if(!j )printf("查无此人!!\n");}Status findofroom()//按客房查找 { int i,num; printf("请输入房间编号:\n"); scanf("%d", &num); i=HashIndex(num);//哈希地址 if(!hash1[i].guenum)//房间号不存在 { printf("无人入住!!\n") ; return; } printf("住宿房间号:%d 姓名:%s 身份证号:%s 电话号码: %s 性别: %s \n",hash1[i].guenum,hash1[i].name, hash1[i].idnum,hash1[i].phone,hash1[i].sex);}Status findkefang()//查找空余客房 { int i,j=0; printf("空余客房为:\n"); printf("房间编号 最大入住人数 租金 是否有人 入住人数\n"); for(i=0;i<MAX;i++) if(!hash[i].shifouruzhu&&hash[i].guenum){ printf("%d %d %.2f %d %d\n", hash[i].guenum,hash[i].maxnum,hash[i].zujin, hash[i].shifouruzhu,hash[i].num); j=1;//找到标记 } if(!j) printf("客房爆满!!!\n");}Status zhusuguanli()//住宿管理 { printf("************************************\n"); printf("***** 入住请输入 1: ****\n"); printf("***** 退房请输入 2: ****\n"); printf("***** 续房请输入 3: ****\n"); printf("***** 退出请输入 0: ****\n"); printf("************************************\n"); int n; printf("请输入您的选择:\n"); scanf("%d", &n); switch(n) { case 1:ruzhu();break;//入住操作 case 2:tuifang();break;//退房操作 case 3:xufang();break;//续房操作 case 0:return;//返回上一菜单 }}Status xufang()//续房操作 { printf("请输入需要续房的房间号:\n"); int num,i; if(scanf("%d", &num)!=1) { printf("输入错误!\n已退出!\n"); return; } i=HashIndex(num);//哈希地址 if(!hash1[i].guenum)//若未租出 { printf("此房间尚未租出!!\n"); return; } printf("住宿房间号:%d 姓名:%s 入住时间:%d年%d月%d日 入住时长:%d天\n",hash1[i].guenum,hash1[i].name, hash1[i].time.year,hash1[i].time.mon,hash1[i].time.day,hash1[i].timelong); printf("是否续费? Y/N\n"); char c; if(scanf("%*c%c", &c)!=1) { printf("输入错误!\n已退出!\n"); return; } if(c!='y'&&c!='Y') return;//确认操作 printf("请输入续费的天数:\n"); int day; scanf("%d", &day) ; hash1[i].timelong+=day;//修改延长后住宿天数 printf("修改后的信息为:\n"); printf("住宿房间号:%d 姓名:%s 入住时间:%d年%d月%d日 入住时长:%d天\n",hash1[i].guenum,hash1[i].name, hash1[i].time.year,hash1[i].time.mon,hash1[i].time.day,hash1[i].timelong); write1totxt();//修改后写入文件 printf("续费成功!!\n");} Status tuifang()//退房操作 { printf("请输入将要退房的房间号:\n"); int num,i; if(scanf("%d", &num)!=1) { printf("输入错误!\n已退出!\n"); return; } i=HashIndex(num);//哈希地址 if(!hash1[i].guenum)//没有租出 { printf("此房间没有租出!!\n"); return; } hash1[i].guenum=0;//将此客人删除 write1totxt();//操作后的客人信息 写入文件 hash[i].shifouruzhu=0;//客房修改为没有人入住 hash[i].num=0;//入住人数修改为0 writetotxt();//将修改后的 客房信息写入文件 printf("退房成功,欢迎下次光临!!!\n");}Status ruzhu()//入住操作 { findprice(); printf("请输入需要预定的房间编号:\n"); parton keren; if(scanf("%d", &keren.guenum)!=1) { printf("输入错误!\n已退出!\n"); exit(0); } int i; i=HashIndex(keren.guenum);//哈希地址 if(!hash[i].guenum) { printf("查无此房!!\n"); return; } if(hash[i].shifouruzhu) { printf("此房间已经有人!\n"); return; } int n; printf("请输入几人入住:\n"); if(scanf("%d", &n)!=1) { printf("输入错误!\n已退出!\n"); return; } if(hash[i].maxnum<n)//超过入住人数上限 { printf("超过人数上限!!\n请重新输入:\n"); return; } printf("请输入客人姓名:\n"); scanf("%s", keren.name); printf("请输入客人身份证号:\n"); scanf("%s", keren.idnum); printf("请输入客人电话号码:\n"); scanf("%s", keren.phone); printf("请输入客人性别:\n"); scanf("%s", keren.sex); printf("请输入入住时间:(年月日),中间用空格隔开\n"); scanf("%d%d%d", &keren.time.year,&keren.time.mon,&keren.time.day); printf("请输入入住时长:\n"); scanf("%d", &keren.timelong); time_t rawtime;//获取时间标记 keren.s=time(&rawtime); hash1[i]=keren;//入住 hash[i].shifouruzhu=1;//客房信息改为有人入住 hash[i].num=n;//入住人数 writetotxt();//写入修改后的客人和客房信息 write1totxt();}int main()//主函数 { int i; for(i=0;i<MAX;i++) { hash[i].guenum=0; hash1[i].guenum=0; } readfromtxt(); read1fromtxt(); while(1) { printf("*************************************\n"); printf("**** 欢迎使用住宿管理系统 *****\n\n"); printf("**** 住宿管理请输入 1: *****\n"); printf("**** 客房管理请输入 2: *****\n"); printf("**** 查询统计请输入 3: *****\n"); printf("**** 退出请输入 0: *****\n\n"); printf("*************************************\n"); int choice; printf("请输入您的选择:\n"); if(scanf("%d",&choice)!=1) //如果输入不非法 { printf("输入错误!\n已退出!\n"); exit(0); } switch(choice) { case 1:zhusuguanli();break;//住宿管理模块 case 2: weihu();break;//客房维护模块 case 3:search();break;//查找信息模块 case 0: printf("\n感谢使用本旅店管理系统\n\nhaut,掌握先进科技!\n"); exit(0);//退出 } } return 0;}Status write1totxt()//将客人信息写入kerenxinxi.txt文件 { FILE *fp;//定义文件 fp=fopen("kerenxinxi.txt","w");//以写的方式打开 int i; for(i=0;i<MAX;i++) if(hash1[i].guenum) fprintf(fp,"%d %s %s %s %s %d %d %d %d %d\n",hash1[i].guenum,hash1[i].name,hash1[i].idnum,hash1[i].phone, hash1[i].sex,hash1[i].time.year,hash1[i].time.mon,hash1[i].time.day,hash1[i].timelong,hash1[i].s); fclose(fp);//关闭文件 }Status read1fromtxt()//从文件kerenxinxi.txt文件读出客人信息到hash1数组中 { parton keren;//暂存的客人 FILE *fp;//定义文件指针 fp=fopen("kerenxinxi.txt","r");//以读的方式打开文件 int i; while(!feof(fp))//不到文件尾 { fscanf(fp,"%d %s %s %s %s %d %d %d %d %d\n", &keren.guenum,keren.name,keren.idnum,keren.phone, keren.sex,&keren.time.year,&keren.time.mon,&keren.time.day,&keren.timelong,&keren.s); i=HashIndex(keren.guenum);//哈希地址 hash1[i]=keren;//赋值 } fclose(fp);//关闭文件 }Status readfromtxt()//从文件中读取客房信息 { Guest room; FILE *fp;//定义文件 if((fp=fopen("roomxinxi.txt","r"))==NULL) { printf("打开文件失败!!!\n"); exit(0); }//读的方式打开文件 int i; while(!feof(fp))//不到文件尾 { fscanf(fp,"%d %d %lf %d %d\n", &room.guenum,&room.maxnum,&room.zujin, &room.shifouruzhu,&room.num); i=HashIndex(room.guenum);//哈希地址 hash[i]=room; } if(fclose(fp)) { printf("关闭文件失败!!!\n"); exit(0); }//关闭文件 }Status writetotxt()//将客房信息写入文件 { int i; FILE *fp; if((fp=fopen("roomxinxi.txt","w"))==NULL)//读的方式打开文件 { printf("打开文件失败!!!\n"); exit(0); } for(i=0;i<MAX;i++) if(hash[i].guenum)//有客房 fprintf(fp,"%d %d %lf %d %d\n", hash[i].guenum,hash[i].maxnum,hash[i].zujin, hash[i].shifouruzhu,hash[i].num); fclose(fp);//关闭文件 }
    7 运行结果输入1时屏幕显示图

    输入的房间编号有人时取消预订

    预订时需要填写的信息

    退房时输入正确的客房编号即可

    续房时的操作图

    修改客房信息操作图

    添加客房时已存在此客房

    添加客房时需要输入的信息及其操作图

    删除客房时输入客房非法

    删除客房操作及其显示图

    查找空余客房时输入1的显示图

    按照房间号查找客人操作及其显示图

    按照姓名查找客人信息操作及其显示图

    所有客房信息浏览图

    所有客人信息浏览图

    筛选符合条件的客房信息的操作及其显示图

    输出将要到期的客人信息及其操作示意图

    退出旅店管理系统

    8 心得体会此次数据结构试验是我学习数据结构以来做过的与数据结构有关的最复杂的一个程序,定义了3个结构体、包含22个功能函数,共计617行代码。完成了实验的基本功能与基本要求。根据对数据的分析以及结合所学的知识,发现客房编号的后三位具有唯一性,而且旅馆客房一般具有几百个房间。据此,便联想到了学习过的哈希表存储和查找。所以,本程序主要采用了哈希结构的各个功能。
    在实验的过程中,我遇到了很多问题。例如在对客人的结构体定义时便犹豫不决,不能确定用于区分的关键字。但是随着将与客房信息有关的函数渐渐写好,我的心中忽然闪出一个念头,用客房编号区分客人不是很恰当吗?我于是便尝试着写,最后客人的操作也与哈希结构联系起来,有关操作也迎刃而解。又如,在进行哈希表查找的时候,模块化不够强。老师看过之后便提出了问题。因此,我便将哈希查找构造成了一个哈希函数,返回哈希表的地址。这样以来,程序的可读性便增强了,而且更有利于对地址的查找,给程序的修改提供了极大的方便。
    值得一提的是查找模块中的时间到期提醒,刚开始由于没有调用本地时间导致判断时间相当复杂。经过查阅百度文档中的C语言怎样调用本地时间,我学到了如何调用系统时间,将问题难度大幅度的降低。
    由于此次试验的完成是在将近期末考试的时候,时间有限。我不免有些急功近利的想法。因此,在某些设计方面可能考虑的不够周到,甚至在算法方面有些冗杂,尤其是在容错处理上,并没有经过大量数据的测试。在暑假期间有机会的话,我一定对其算法、数据结构、容错处理、界面设置进行优化和补充。
    3 评论 37 下载 2018-10-31 21:56:27 下载需要4点积分
  • 基于JAVA EE框架的在线考试系统平台的设计与实现

    摘要在线考试系统以java作为开发语言,Struts2、Hibernate实现前后台数据管理,Spring实现业务逻辑管理,以MyEclipse作为开发工具,MySQL实现数据持久化,Tomcat部署整个系统,实现了院系管理、专业管理、班级管理、用户管理、课程管理、题库管理、考试管理、成绩管理八大功能。
    关键字:在线考试系统、J2EE、JSP、Struts2、Hibernate、Spring、MySQL、Tomcat
    AbstractOnline examinationsystem with Java as a development language, struts 2, Hibernate implementationQianHouTai data management, Spring management, implement the business logicwith MyEclipse as development tools, M and SQL for data persistence, Tomcatdeployment of the entire system, realized the departments management,professional management, class management, user management, course management,question bank management, examination management, grade management eightfunctions.
    Key words: online examinationsystem; J2EE;JSP;struts 2;Hibernate;Spring;MySQL;Tomcat
    第一章 绪论当今社会是一个信息社会,一个知识经济时代。自世界上第一台计算机ENINC(Electronicnumerical integrator and calculator)于1946年在美国问世到现在,计算机业飞速发展,技术淘汰指标高的惊人,价格下降以及软件应用的快速扩展引发了以信息处理计算机化为标志的“微机革命”,随之而来的是以全球信息网络普及和全球信息共享为标志的“全球信息网络革命”的蓬勃兴起[1]。可见,世界已进入在计算机信息管理领域中激烈竞争的时代,计算机已经变得普通的不能再普通的工具,如同我们离不开的自行车、汽车一样。我们应该承认,谁掌握的知识多,信息量大,信息处理速度快,批量大,谁的效率就高,谁就能在各种竞争中立于不败之地。
    随着网络技术的飞速发展,现在很多国内外的大学和其他部门都已经开设了远程教育,通过计算机网络实现了异地教育和培训。
    远程教育包括很多环节,例如:教学系统、答疑系统和考试系统等等,在我国远程教育正在逐步地发展起来,在教育教学过程中考试又是一个很重要环节,它对学生知识的增长、能力的发展、兴趣爱好、思想品德以及对学校的教育措施等许多问题是一个良好的反应,通过对考试成绩的分析有利于学校更好的指导和教育学生。但是目前学校与社会上的考试大都采用传统的考试方式,而传统的考试方式组织一次考试至少需要经过五个步骤,即人工出题、考生考试、人工阅卷、成绩评估和试卷分析[2]。其中人工出题由老师将题目用笔写在纸上或者由老师以电子文档的形式出题,然后在将试题打印在纸质试卷上;而考生考试也必须从其他地方赶到同一个地方参加考试,考试过程中要准备好考试所用的各种工具;考试结束后老师将试卷收上去然后根据标准答案进行手工阅卷;阅卷完后还需要手工统计学生的成绩等等。显然,随着考试类型的不断增加及考试要求的不断提高,教师的工作量将会变得越来越大,十分的繁琐,并且非常容易出错。因此传统的考试方式已经不能适应现代化考试的需求了,人们迫切需要新的技术来改善这一状况。
    目前,利用计算机网络技术来进行考试的主要是社会上的培训认证考试(如微软的各种认证考试)和远程教育的考试。一个成功的基于WEB的考试需要一个好的网络环境和一个号的考试平台以及一个好的题库。但一个真正智能化的基于WEB的考试系统的实现是一个复杂的过程,考试平台和题库的研究要依靠软件开发专家、被测科学专家和精通考试理论和命题的专家三者的合作。
    在线考试系统具有降低考试成本、解决繁重的考务工作的优点。它可以免去老师大量的临考工作,试卷可以根据题库中的内容即时生成;老师也无须在考生提交答卷后批阅考生所有的答案,还可以免去阅卷后对成绩的统计与分析等工作,计算机可以自动统计、排序和汇总成绩,实现无纸考试,大大提高工作效率。
    一个完善的在线考试系统可以使学生在网上学习过后及时检验自己的学习效果,以发现自己的不足,使得学习效率得到很大提高[3]。在线考试系统中题目的生成、试卷的提交、成绩的批阅等都可以由系统自动完成。只要形成一套成熟的题库就可以实现考试的自动化。这样老师所要做的只是精心设计题目、维护题库,而不是组织考试,从而大大减轻了教师的负担。
    第二章 可行性分析本章主要从经济可行性、技术可行性和操作可行性三个方面阐述了在线考试系统的开发可行性,其中经济可行性主要是从传统考试方式所消耗的成本与在线考试系统开发与应用所消耗的成本来对比经济可行性,技术可行性主要是介绍系统开发所用到的技术,操作可行性则是介绍用户使用系统的能快速上手操作整个系统。
    2.1 经济可行性传统考试采用的是纯人工方式的出卷、阅卷、成绩统计,考生考试时也需要提前从各地集中在一个地点进行统一考试,因此,从经济角度分析,出卷时印刷所消耗的成本、考生赶往考试地点所消耗的交通费用、老师组织一次考试时消耗的工时等等都是比较大的。同样,在考生考试过后,老师阅卷也是采用纯人工的方式来完成,阅卷完成之后还需要制作报表、对数据进行综合分析等,这消耗的工时也是较多的,且效率低下。
    而利用计算机网络来组织考生在有网络的地方进行在线考试,不但可以为学校或者企业节省大量的人力物力,而且效率要比手工传统的考试方式高的多,还可以让人力资源得到充分的利用,由此可见,开发在线考试系统在经济上是完全可行的。
    2.2 技术可行性在线考试系统开发所利用的技术主要包括JSP、JAVASCRIPT、J2EE、STRUTS2、HIBERNATE、SPRING,以MySQL数据库实现数据的持久化,Tomcat部署整个系统的运行。
    其中JSP技术主要是有Sun公司倡导、许多公司参与一起建立的一种动态网页技术标准[4],而J2EE是Sun Microsystems针对Java开发人员的产品(JDK)的一个企业版[5],JSP与J2EE都可夸平台操作。STRUTS是一个使用MVC架构的框架,将设计模式中的模型、视图、控制器三者实现代码分离,从而使同一个应用程序可以使用不同的表现形式[6]。HIBERNATE是一个ORM(对象关系映射)框架,它对JDBC进行了轻量级的封装,通过使用HIBERNATE框架,开发人员能够以面向对象的思维方式来操作数据库[7]。SPRING是一个以IoC和AOP为核心的轻量级容器框架,它提供了一系列的JAVA EE开发解决方案[8]。MySQL是一个开放源码的小型关联式数据库管理系统,开发者为瑞典MySQL AB公司。目前MySQL被广泛地应用在Internet上的中小型网站中。由于其体积小、速度快、总体拥有成本低,尤其是开放源码这一特点,许多中小型网站为了降低网站总体拥有成本而选择了MySQL作为网站数据库[9]。Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选[10]。
    以上所提及到的技术都是开源或者免费的技术,同时也具有跨平台使用的效果,开发使用起来简单方便,所以在线考试系统的开发在技术上是可行的。
    2.3 操作可行性在线考试系统开发是所使用到的技术都是可跨平台的,所以我们首选的操作系统是windows操作系统。作为21世纪的学生,人人都会使用电脑、人人都能在windows环境下进行网页浏览操作。
    在线考试系统的业务逻辑简单,在懂的操作windows系统的前提下只要对用户进行简单的培训用户就能独立完成在线考试系统的各个功能使用,所以在线考试系统在操作上也是完全可行的。
    第三章 需求分析本章主要讲述的是系统的功能需求,同时也包含了系统的运行环境需求、性能需求和系统的E-R图。其中系统的功能需求是通过本人对考试各个环节的理解以及在网上收集资料等各种方法所获取的,运行环境需求是对运行本系统的一个最低PC配置,E-R图是对系统的各实体之间的关系的一个解说[11]。
    3.1 功能需求本系统主要包括登录功能、院系管理功能、专业管理功能、班级管理功能、用户管理功能、课程管理功能、题库管理功能、考试管理功能和成绩管理功能。登录功能是用于用户登录系统,院系管理功能是用于管理员管理院系信息,专业管理功能是用于管理员管理专业信息,班级功能是用于管理员管理班级信息,用户管理功能是用于管理员管理系统用户,课程管理功能是用于管理员管理课程信息,题库管理功能是用于教师管理题库信息,考试管理功能是用于管理员生成试卷、学生在线考试与教师阅卷,成绩管理功能是用于学生查询本人成绩和教师统计学生成绩。
    3.3.1 登录功能该功能是针对所有的用户必需的功能,用户在登录页面输入用户名和密码,选择相应的角色进行登录,其中学生的用户名为学号,老师与管理员的用户名为教师编号,登录成功后进去在线考试系统,失败则返回登录页面重新登录。
    操作流程如图3.1所示。

    3.3.2 院系管理功能该功能仅对管理员可见,管理员登录系统后可对院系的基本信息进行增删改查操作,其中修改和删除两项操作需要先将院系信息查询出来才可进行。点击添加院系信息时进入填写院系信息页面填写院系信息,提交后保存结束,点击查看院系信息直接将所有院系信息列出来显示在页面,在每条院系信息后面有编辑与删除按钮,点编辑按钮编辑所选院系,并在编辑页面显示所选院系的所有信息,修改提交后保存并结束,点删除按钮直接删除所选院系信息并结束。
    操作流程如图3.2所示。

    3.3.3 专业管理该功能仅对管理员可见,管理员登录系统后可对专业的基本信息进行增删改查操作,其中修改和删除两项操作需要先将院系信息查询出来才可进行。点击添加专业信息时进入填写专业信息页面填写专业信息,提交后保存结束,点击查看专业信息进入选择要查看的院系的页面,选择院系后点查看直接将所选院系的所有专业信息列出来显示在页面,在每条专业信息后面有编辑与删除按钮,点编辑按钮编辑所选专业,并在编辑页面显示所选专业的所有信息,修改提交后保存并结束,点删除按钮直接删除所选专业信息并结束。
    操作流程如图3.3所示。

    3.3.4 班级管理该功能仅对管理员可见,管理员登录系统后可对班级的基本信息进行增删改查操作,其中修改和删除两项操作需要先将班级信息查询出来才可进行。点击添加班级信息时进入填写班级信息页面填写班级信息,提交后保存结束,点击查看班级信息进入填写查询条件页面填写查询条件,填写完查询条件后点查看直接将所填条件下的所有班级信息列出来显示在页面,在每条班级信息后面有编辑与删除按钮,点编辑按钮编辑所选班级,并在编辑页面显示所选班级的所有信息,修改提交后保存并结束,点删除按钮直接删除所选班级信息并结束。
    操作流程如图3.4所示。

    3.3.5 用户管理该功能仅对管理员可见,管理员登录系统后可对用户的基本信息进行增删改查操作,其中用户包括学生与老师两种类别,老师也包括管理员,修改和删除两项操作需要先将用户信息查询出来才可进行。点击添加老师信息时进入填写老师信息页面填写老师信息,提交后保存结束,点击查看老师信息进入填写查询条件页面填写查询条件,填写完查询条件后点查看直接将所填条件下的所有老师信息列出来显示在页面,在每条老师信息后面有编辑与删除按钮,点编辑按钮编辑所选老师,并在编辑页面显示所选老师的所有信息,修改提交后保存并结束,点删除按钮直接删除所选老师信息并结束。对学生信息的管理与老师类似。
    操作流程如图3.5所示。

    3.3.6 课程管理该功能仅对管理员可见,管理员登录系统后可对课程的基本信息进行增删改查操作,其中修改和删除两项操作需要先将课程信息查询出来才可进行。点击添加课程信息时进入填写课程信息页面填写课程信息,提交后保存结束,点击查看课程信息直接将所有课程信息列出来显示在页面,在每条课程信息后面有编辑与删除按钮,点编辑按钮编辑所选课程,并在编辑页面显示所选课程的所有信息,修改提交后保存并结束,点删除按钮直接删除所选课程信息并结束。
    操作流程如图3.6所示。

    3.3.7 题库管理该功能对管理员与老师都可见,管理员或者老师登录系统后可对题库的基本信息进行增删改查操作,题库中的题型分为选择题、判断题、填空题、简答题四种。其中修改和删除两项操作需要先将题库信息查询出来才可进行。点击添选择题信息时进入填写选择题信息页面填写题目信息,提交后保存结束,点击查看选择题信息后进入查询条件填写页面,填写完查询条件后点查询后将列出所填查询条件下的所有选择题信息,在每条选择题信息后有编辑与删除按钮,点击编辑按钮编辑所选选择题,并在编辑页面显示所选选择题的所有信息,修改提交保存并结束,点删除按钮直接删除所选选择题信息并结束;类似选择题管理,点击添判断题信息时进入填写判断题信息页面填写题目信息,提交后保存结束,点击查看判断题信息后进入查询条件填写页面,填写完查询条件后点查询后将列出所填查询条件下的所有选择题信息,在每条判断题信息后有编辑与删除按钮,点击编辑按钮编辑所选判断题,并在编辑页面显示所选判断题的所有信息,修改提交保存并结束,点删除按钮直接删除所选判断题信息并结束;填空题与简答题的管理类似。
    选择题操作流程如图3.7所示,判断题操作流程如图3.8所示,填空题操作流程如图3.9所示,简答题操作流程如图3.10所示。

    3.3.8 考试管理该功能分为生成试卷、在线考试、老师阅卷三个功能,生成试卷仅对管理员可见,在线考试仅对学生可见、老师阅卷仅对老师可见。其中生生试卷功能是管理员登录系统后点考试管理,然后再点生成试卷进入生成试卷条件填写页面,在试卷条件填写页面填写生成试卷的条件后提交自动随机生成试卷并结束;在线考试功能在学生登录系统后点考试管理,然后再点在线考试进入试卷选择页面,选择要考试的试卷开始考试,考试完毕提交试卷结束。老师阅卷功能在老师登录系统后选择考试管理,点击老师阅卷进入学生答卷选择页面,选择学生考试试卷开始阅卷,阅卷完后提交结束。
    生成试卷的操作流程如图3.11所示,在线考试的操作流程如图3.12所示,老师阅卷的操作流程如图3.13所示。



    生成试卷
    在线考试
    老师阅卷










    3.3.9 成绩管理该功能主要包括成绩统计与成绩查询两个功能,其中成绩统计仅对老师可见,老师登录系统后选择成绩管理,然后点击成绩统计进入统计条件填写页面填写统计条件,点统计后将显示统计结果,点关闭结束。查看成绩仅对学生可见,学生登录系统后选择成绩管理,点击查询成绩将显示该学生所有考试科目的成绩,点关闭结束。
    成绩统计操作流程如图3.14所示,查看成绩操作流程如图3.15所示。



    成绩统计
    查询成绩









    3.2 运行环境需求为了保证系统运行的流畅性,我们对硬件有一定的需求,硬件配置不得低于32GB的硬盘容量、1GB的运行内存、1.8GHz的CUP处理速度。同样为了保证系统操作的简单性,我们要求该系统在windows操作系统下运行,浏览器的版本火狐浏览器,同时也要求各客户PC机与服务器所在PC机保存通信状态。
    3.2 性能需求由于在线考试系统最主要的功能是用于在线考试,而每场考试都是设定时间的,所对系统的性能有一定的需求,系统每个操作处理时间不能大于10S,正常处理时间必须低于5S。
    第四章 概要设计本章主要介绍在线考试系统的八个功能模块的总体设计以及数据库的设计。其中院系管理模块、专业管理模块、班级管理模块、用户管理模块、课程管理模块和题库管理模块都具有增删改查功能,而考试管理模块具有生成试卷、在线考试、老师阅卷三个功能,成绩管理模块具有成绩统计和查询成绩两个功能。
    4.1 功能模块设计该在线考试系统主要包括院系管理模块、专业管理模块、班级管理模块、用户管理模块、课程管理模块、题库管理模块、考试管理模块和成绩管理八大模块,如图4-1所示。

    4.1.1 院系信息管理模块该模块主要包括添加院系信息、查询院系信息、修改院系信息、删除院系信息四个功能模块,如图4.2所示。

    4.1.2 专业信息管理该模块主要包括添加专业信息、查询专业信息、修改专业信息、删除专业信息四个功能模块,如图4.3所示。

    4.1.3 班级信息管理]该模块主要包括添加班级信息、查询班级信息、修改班级信息、删除班级信息四个功能模块,如图4.4所示。

    4.1.4 用户信息管理该模块主要包括添加老师信息、查询老师信息、修改老师信息、删除老师信息以及添加学生信息、查询学生信息、修改学生信息、删除学生信息八个功能模块,如图4.5所示。

    4.1.5 课程管理该模块主要包括添加课程信息、查询课程信息、修改课程信息、删除课程信息四个功能模块,如图4.6所示。

    4.1.6 题库管理该模块主要包括添加选择题、查看选择题、修改选择题、删除选择题、添加判断题、查看判断题、修改判断题、删除判断题、添加填空题、查看填空题、修改填空题、删除填空题、添加简答题、查看简答题、修改简答题、删除简答题十六个功能模块,如图4.7所示。

    4.1.7 考试管理该模块主要包括添加生成试卷、在线考试、老师阅卷三个功能模块,如图4.8所示。

    4.1.8 成绩查询该模块主要包括成绩统计和查询成绩两个功能模块,如图4.9所示。

    4.2 E-R图 E-R(实体 - 关系)图是用来描述实体之间的关系的[12],一个专业属于一个院系,一个班级属于一个专业,一个学生属于一个班级,学生与老师都拥有权限,一个老师管理多个班级,学生与试卷的选择题、判断题、填空题、简答题存在考试关系,试卷的选择题、判断题、填空题与简答题分别是从题库的选择题、判断题、填空题和简答题表中抽取出来的,题库中的每一道选择题、判断题、填空题、简答题又隶属于一门课程,总体E-R图如图4.10、图4.11所示,实体E-R图如图4.12至图4.26所示。

    实体图见图4.12至图4.26。



    院系实体
    专业实体








    班级实体
    权限实体







    学生实体

    教师实体

    课程实体




    试卷选择题实体
    试卷选择题实体








    试卷填空题实体
    试卷简答题实体






    题库判断题实体
    题库填空题实体






    题库简答题实体
    题库选择题实体







    4.3 数据库设计该在线考试系统以院系、专业、班级、学生、老师、权限、课程、题库选择题、题库判断题、题库填空题、题库简答题、试卷选择题、试卷判断题、试卷填空题、试卷简答题15个实体作为数据库的表,表的设计如下。
    4.2.1 院系表院系表包含id、name、date、des四个字段,id为主键,没有实际意义,name用来存储院系名称,date用来存储院系创办日期,des是用来对存储院系做一个简单的描述。表的设计如表4.1所示。



    字段名
    字段类型
    字段长短
    字段描述




    id
    int
    8
    主键


    name
    varchar
    50
    名称


    date
    date

    创办日期


    des
    text

    描述



    4.2.2 专业表专业表包含id、name、deptId、date、des五个字段,id为主键,没有实际意义,name存储专业名称,deptId存储院系id,用来唯一标识专业属于哪个院系,date存储专业开始日期,des用来对存储的专业做一个简短的描述,如表4.2所示。



    字段名
    字段类型
    字段长度
    字段描述




    id
    int
    8
    主键


    name
    varchar
    50
    名称


    deptId
    int
    8
    所属院系


    date
    date

    创办日期


    des
    text

    描述



    4.2.3 班级表班级表包含id、name、majorId、teacherId、des五个字段,id为主键,没有实际意义,name存储班级名称,majorId存储专业id,用来唯一标识班级属于哪个专业,teacherId存储教师id,用来唯一标识负责老师,des是用来对存储班级做一个简短的描述,如表4.3所示。



    字段名
    字段类型
    字段长度
    字段描述




    id
    int
    8
    主键


    name
    varchar
    50
    名称


    majorId
    int
    8
    所属专业


    teacherId
    int
    8
    班主任


    des
    text

    描述



    4.2.4 学生表学生表包含id、sno、name、birthday、nation、tel、email、address、classId、powerId、password、des十二个字段,id为主键,没有实际意义,sno存储学生学号,name存储学生姓名,birthday存储学生出生日期,nation标识学生属于哪个民族,tel存储学生电话号码,email存储学生邮箱,address存储学生住址,classId存储班级id,用来唯一标识学生属于哪个班级,powerId存储学生权限id,用来唯一标识学生的权限,password存储学生登录系统的密码,des是用来对存储的学生做一个简短的描述,如表4.4所示。



    字段名
    字段类型
    字段长度
    字段描述




    id
    int
    8
    主键


    sno
    varchar
    11
    学号


    name
    varchar
    50
    姓名


    birthday
    date

    出生日期


    nation
    varchar
    50
    民族


    tel
    varchar
    20
    电话


    email
    varchar
    20
    邮箱


    address
    varchar
    200
    地址


    classId
    int
    8
    所属班级


    powerId
    int
    2
    权限


    password
    varchar
    20
    密码


    des
    text

    描述



    4.2.5 教师表教师表包含id、name、birthday、nation、tel、email、address、position、powerId、password、des十一个字段,id是主键,没有实际意义,tno存储教师的教师编号,name存储教师名称,birthday存储教师的出生日期,nation用来标注教师属于哪个民族,tel存储教师的电话号码,email存储教师的邮箱,address存储教师的住址,position存储教师的职位,powerId存储权限id,用来唯一标识教师的权限,password存储教师登录系统的密码,des是用来对教师做一个简短的描述,如表4.5所示。



    字段名
    字段类型
    字段长度
    字段描述




    id
    int
    8
    主键


    tno
    varchar
    11
    教师编号


    name
    varchar
    50
    姓名


    birthday
    date

    出生日期


    nation
    varchar
    50
    民族


    tel
    varchar
    20
    电话


    email
    varchar
    20
    邮箱


    address
    varchar
    200
    地址


    position
    varchar
    50
    职位


    powerId
    int
    2
    权限


    password
    varchar
    20
    密码


    des
    text

    描述



    4.2.6 权限表权限表包含id、power、des三个字段,id为主键,没有实际意义,power存储用户权限,des是对用户权限做一个简短的描述,如表4.6所示。



    字段名
    字段类型
    字段长度
    字段描述




    id
    int
    2
    主键


    power
    varchar
    50
    权限


    des
    text

    描述



    4.2.7 课程表课程表包含id、cname、des三个字段,id为主键,没有实际意义,cname存储课程名称,des是用来对存储的课程做一个简短的描述,如表4.7所示。



    字段名
    字段类型
    字段长度
    字段描述




    id
    int
    8
    主键


    cname
    varchar
    50
    课程名称


    des
    text

    描述



    4.2.8 题库选择题表题库选择题表包含id、question、answer、teachername、courseid五个字段,id为主键,没有实际意义,question存储选择题题目,answer存储选择题答案,teachername存储出题老师姓名,courseid存储课程id,用来唯一标识选择题属于哪门课程,如表4.8所示。



    字段名
    字段类型
    字段长度
    字段描述




    id
    int
    8
    主键


    question
    text

    题目


    answer
    char
    1
    标准答案


    teachername
    varchar
    50
    出题老师


    courseid
    int
    8
    课程



    4.2.9 题库判断题表题库判断题表包含id、question、answer、teachername、courseid五个字段,id为主键,没有实际意义,question存储判断题题目,answer存储判断题答案,teachername存储出题老师姓名,courseid存储课程id,用来唯一标识判断题属于哪门课程,如表4.9所示。



    字段名
    字段类型
    字段长度
    字段描述




    id
    int
    8
    主键


    question
    text

    题目


    answer
    int
    1
    标准答案


    teachername
    varchar
    50
    出题老师


    courseid
    int
    8
    课程



    4.2.10 题库填空题表题库填空题表包含id、question、answer、teachername、courseid五个字段,id为主键,没有实际意义,question存储填空题题目,answer存储填空题答案,teachername存储出题老师姓名,courseid存储课程id,用来唯一标识填空题属于哪门课程,如表4.10所示。



    字段名
    字段类型
    字段长度
    字段描述




    id
    int
    8
    主键


    question
    text

    题目


    answer
    text

    标准答案


    teachername
    varchar
    50
    出题老师


    courseid
    int
    8
    课程



    4.2.11 题库简答题表题库简答题表包含id、question、answer、teachername、courseid五个字段,id为主键,没有实际意义,question存储简答题题目,answer存储简答题答案,teachername存储出题老师姓名,courseid存储课程id,用来唯一标识简答题属于哪门课程,如表4.11所示。



    字段名
    字段类型
    字段长度
    字段描述




    id
    int
    8
    主键


    question
    text

    题目


    answer
    text

    标准答案


    teachername
    varchar
    50
    出题老师


    courseid
    int
    8
    课程



    4.2.12 试卷选择题表试卷选择题表包含id、papername、question、modelanswer、teachername、studentid、courseid、studentanswer、score九个字段,id为主键,没有实际意义,papername为试卷名称,用来标识试卷选择题属于哪份试卷,question存储试卷选择题题目,modelanswer存储试卷选择题题目的标准答案,teachername存储阅卷老师名字,studentid存储学生id,用来唯一标识存储的题目由哪个学生作答,courseid存储课程id,用来唯一标识题目属于哪门课程,studentanswer存储学生答题后的答案,score存储试卷选择题的分值,如表4.12所示。



    字段名
    字段类型
    字段长度
    字段描述




    id
    int
    8
    主键


    papername
    varchar
    200
    试卷名称


    question
    text

    题目


    modelanswer
    int
    1
    标准答案


    teachername
    varchar
    50
    阅卷老师


    studentid
    int
    11
    答题学生


    courseid
    int
    8
    课程


    studentanswer
    int
    1
    学生答案


    score
    int
    2
    分值



    4.2.13 试卷判断题表试卷判断题表包含id、papername、question、modelanswer、teachername、studentid、courseid、studentanswer、score九个字段,id为主键,没有实际意义,papername为试卷名称,用来标识试卷判断题属于哪份试卷,question存储试卷判断题题目,modelanswer存储试卷判断题题目的标准答案,teachername存储阅卷老师名字,studentid存储学生id,用来唯一标识存储的题目由哪个学生作答,courseid存储课程id,用来唯一标识题目属于哪门课程,studentanswer存储学生答题后的答案,score存储试卷判断题的分值,如表4.13所示。



    字段名
    字段类型
    字段长度
    字段描述




    id
    int
    8
    主键


    papername
    varchar
    200
    试卷名称


    question
    text

    题目


    modelanswer
    int
    1
    标准答案


    teachername
    varchar
    50
    阅卷老师


    studentid
    int
    11
    答题学生


    courseid
    int
    8
    课程


    studentanswer
    int
    1
    学生答案


    score
    int
    2
    分值



    4.2.14 试卷填空题表试卷填空题表包含id、papername、question、modelanswer、teachername、studentid、courseid、studentanswer、score九个字段,id为主键,没有实际意义,papername为试卷名称,用来标识试卷填空题属于哪份试卷,question存储试卷填空题题目,modelanswer存储试卷填空题题目的标准答案,teachername存储阅卷老师名字,studentid存储学生id,用来唯一标识存储的题目由哪个学生作答,courseid存储课程id,用来唯一标识题目属于哪门课程,studentanswer存储学生答题后的答案,score存储试卷填空题的分值,如表4.14所示。



    字段名
    字段类型
    字段长度
    字段描述




    id
    int
    8
    主键


    papername
    varchar
    200
    试卷名称


    question
    text

    题目


    modelanswer
    text

    标准答案


    teachername
    varchar
    50
    阅卷老师


    studentid
    int
    11
    答题学生


    courseid
    int
    8
    课程


    studentanswer
    text

    学生答案


    score
    int
    2
    分值



    4.2.15 试卷简答表试卷简答题表包含id、papername、question、modelanswer、teachername、studentid、courseid、studentanswer、score九个字段,id为主键,没有实际意义,papername为试卷名称,用来标识试卷简答题属于哪份试卷,question存储试卷简答题题目,modelanswer存储试卷简答题题目的标准答案,teachername存储阅卷老师名字,studentid存储学生id,用来唯一标识存储的题目由哪个学生作答,courseid存储课程id,用来唯一标识题目属于哪门课程,studentanswer存储学生答题后的答案,score存储试卷简答题的分值,如表4.12所示。



    字段名
    字段类型
    字段长度
    字段描述




    id
    int
    8
    主键


    papername
    varchar
    200
    试卷名称


    question
    text

    题目


    modelanswer
    text

    标准答案


    teachername
    varchar
    50
    阅卷老师


    studentid
    int
    11
    答题学生


    courseid
    int
    8
    课程


    studentanswer
    text

    学生答案


    score
    int
    2
    分值



    4.4 创建工程在MyEclipse中新建一个Web工程,工程命名为“online_examination_system”,并设置其为J2EE应用,工程的目录结构如图4.27所示。

    在线考试系统主要包括action、bean、dao、dao.impl、service、service.impl、util七个包(注:本文提及的类包一律省略类包前的com.online_examination_system),其中action类包主要是存放action类,用于实现前后台数据交换;bean包存放实体类,用于实现系统与数据库关联;dao包存放dao接口,用于存放系统与数据库进行数据交互的接口;dao.impl包存放dao的实现类,用于实现dao类对数据库的访问;service包存放逻辑控制接口,用于控制系统业务逻辑;service.impl存放service接口的实现类,用于实现service对系统的业务逻辑控制;util包存放工具类,用于实现类型转换。
    第五章 详细设计本章主要讲述了在线考试系统的各个功能页面的详细设计,每个功能的设计包括对该页面的一个简短介绍、运行效果图与核心代码,页面的文字介绍主要是从用户角度描述每个功能界面的操作方式,运行效果图是用户正确操作后该系统的显示效果,核心代码是实现每个功能界面的的关键代码。
    5.1 登录用户在浏览器上输入“http://localhost:8080/online_examination_system/”进入登录页面,在登录页面输入用户名、密码、选择相应的角色登录系统,登录页面如图5.1所示。

    核心代码如下:
    HttpServletRequest request = ServletActionContext.getRequest(); HttpSession session = request.getSession(); List<StudentBean> listStudentBean = new ArrayList<StudentBean>(); List<TeacherBean> listTeacherBean = new ArrayList<TeacherBean>(); StudentBean studentBean = null; TeacherBean teacherBean = null; String hql = ""; if(this.userClass.equals("student")){ hql = hql + "from StudentBean studentBean where studentBean.sno='" + this.username + "'" + " and studentBean.password='" + this.password + "'"; try { listStudentBean = this.studentService.findStudentByHql(hql); } catch (Exception e) { // TODO: handle exception return ERROR; } if(listStudentBean.size() == 0){ return ERROR; } studentBean = listStudentBean.get(0); session.setAttribute("user", studentBean); } else { hql = hql + "from TeacherBean teacherBean where teacherBean.tno='" + this.username + "'" + " and teacherBean.password='" + this.password + "'"; try { listTeacherBean = this.teacherService.findTeacherByHql(hql); } catch (Exception e) { // TODO: handle exception return ERROR; } if(listTeacherBean.size() == 0){ return ERROR; } teacherBean = listTeacherBean.get(0); session.setAttribute("user", teacherBean); }
    5.2 院系管理院系管理包含院系添加、院系查看、院系更新、院系删除四个功能,其中院系添加和院系查看直接在院系管理模块下就能操作,院系更新与院系删除需要在查询出院系信息后才能进行操作。
    5.2.1 院系添加管理员登录系统后选择院系管理,点添加院系信息进入院系添加页面,在该页面输入院系名称、创办日期与对添加的院系的描述,点添加按钮添加院系,院系添加页面如图5.2所示。

    核心代码如下:
    public String addDept() { try{ this.deptBean.setSetdate(DateUtils.strToDate(setDate)); this.deptService.addDept(this.deptBean); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    5.2.2 院系查看管理员登录系统后选择院系管理,点查看院系信息进入查看院系页面,该页面讲显示所有院系信息,院系查看页面如图5.3所示。

    核心代码如下:
    public String listDept() { try{ HttpServletRequest req = ServletActionContext.getRequest(); this.listDept = deptService.findAllDept(); req.setAttribute("listDept", this.listDept); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    5.2.3 院系更新管理员在查看院系信息页面点击要更新的院系信息后的更新,进入更新院系信息页面,在该页面将显示原院系信息,可以在原来院系信息的基础上修改所要更新的院系信息,院系更新页面如图5.4所示。

    核心代码如下:
    public String updateDept() { try{ this.deptBean.setSetdate(DateUtils.strToDate(setDate)); this.deptService.updateDept(this.deptBean); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    5.2.4 院系删除管理员在查看院系信息页面点击要删除的院系信息后的删除直接删除该条院系信息,删除院系信息页面如图5.5所示。

    核心代码如下:
    public String deleteDept(){ try{ HttpServletRequest req = ServletActionContext.getRequest(); int id = Integer.parseInt(req.getParameter("id")); this.deptBean = deptService.findDeptById(id); deptService.deleteDept(this.deptBean); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    5.3 专业信息管理专业管理包含专业添加、专业查看、专业更新、专业删除四个功能,其中专业添加和专业查看直接在专业管理模块下就能操作,专业更新与专业删除需要在查询出专业信息后才能进行操作。
    5.3.1 专业添加管理员登录系统后选择专业管理,点添加专业信息进入专业添加页面,在该页面输入专业名称、创办日期、所属院系与对添加的专业的描述,点添加按钮添加专业,专业添加页面如图5.6所示。

    核心代码如下:
    public String addMajor(){ try { this.majorService.addMajor(this.majorBean); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    5.3.2 专业查看管理员登录系统后选择专业管理,点查看专业信息进入属院系选择页面,院系选择页面如图5.7所示,在该页面上选择要查看的院系,点查看将显示所选院系下所有专业,专业查看页面如图5.8所示。

    核心代码如下:
    public String listMajor(){ try{ ActionContext ctx = ActionContext.getContext(); if(this.deptId == 0){ this.listMajor = this.majorService.findAllMajor(); } else { this.listMajor = this.majorService.findConditionMajor(this.deptId); } ctx.put("listMajor", this.listMajor); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    5.3.3 专业更新管理员在查看专业信息页面点击要更新的专业信息后的更新,进入更新专业信息页面,在该页面将显示原专业信息,可以在原来专业信息的基础上修改所要更新的专业信息,专业更新页面如图5.9所示。

    核心代码如下:
    public String updateMajor(){ try{ this.majorService.updateMajor(majorBean); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    5.3.4 专业删除管理员在查看专业信息页面点击要删除的专业信息后的删除直接删除该条专业信息,删除专业信息页面如图5.10所示。

    核心代码如下:
    public String deleteMajorById(){ try{ this.majorBean = this.majorService.findMajorById(this.majorId); this.majorService.deleteMajor(this.majorBean); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    5.4 班级信息管理班级管理包含班级添加、班级查看、班级更新、班级删除四个功能,其中班级添加和班级查看直接在班级管理模块下就能操作,班级更新与班级删除需要在查询出班级信息后才能进行操作。
    5.4.1 班级添加管理员登录系统后选择班级管理,点添加班级信息进入班级添加页面,在该页面输入班级名称、创办日期与对添加的班级的描述,点添加按钮添加班级,班级添加页面如图5.11所示。

    核心代码如下:
    public String addPoliticsClass(){ try{ this.politicsClassService.addPoliticsClassBean(this.politicsClassBean); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    5.4.2 班级查看管理员登录系统后选择班级管理,点查看班级信息进入班级查看限定条件页面,查看班级限定条件填写页面如图5.12所示,在该页面上填写要查看的班级名称、选择所属专业、负责老师等信息,点查看将显示限定条件下的所有班级,班级查看页面如图5.13所示。

    核心代码如下:
    public String searchPoliticsClass(){ try{ this.listMajorBean = this.majorService.findAllMajor(); this.listTeacherBean = this.teacherService.findAllTeacher(); this.listPoliticsClassBean = this.politicsClassService .searchPoliticsClass(this.politicsClassBean); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    5.4.3 班级更新管理员在查看班级信息页面点击要更新的班级信息后的更新,进入更新班级信息页面,在该页面将显示原班级信息,可以在原来班级信息的基础上修改所要更新的班级信息,班级更新页面如图5.14所示。

    核心代码如下:
    public String updatePoliticsClass(){ try { this.politicsClassService .updatePoliticsClass(this.politicsClassBean); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    5.4.4 班级删除管理员在查看专业信息页面点击要删除的专业信息后的删除直接删除该条专业信息,班级删除页面如图5.15所示。

    核心代码如下:
    public String deletePoliticsClass(){ try { this.politicsClassBean = this.politicsClassService .findPoliticsClassById(this.politicsClassBean.getId()); this.politicsClassService .deletePoliticsClass(this.politicsClassBean); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    5.5 用户信息管理用户管理包含教师添加、教师查看、教师更新、教师删除、学生添加、学生查看、学生更新、学生删除八个功能,其中教师添加、学生添加、教师查看和学生查看四个功能直接在用户管理模块下就能操作,教师更新与教师删除需要在查询出教师信息后才能进行操作,学生更新与学生删除需要在查询出学生信息后才能进行操作。
    5.5.1 教师添加管理员登录系统后选择用户管理,点添加教师信息进入教师添加页面,在该页面输入教师编号、教师名称、出生日期、民族、电话、邮箱、职位、密码、权限等信息,点添加按钮添加教师,教师添加页面如图5.16所示。

    核心代码如下:
    public String addTeacher(){ try{ this.teacherService.addTeacher(this.teacherBean); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    5.5.2 教师查看管理员登录系统后选择用户管理,点查看教师信息进入查看教师页面,该页面讲显示所有教师信息,教师查看页面如图5.17所示。

    核心代码如下:
    public String listTeacher(){ try { this.listTeachser = this.teacherService.findAllTeacher(); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    5.5.3 教师更新管理员在查看教师信息页面点击要更新的教师信息后的更新,进入更新教师信息页面,在该页面将显示原教师信息,可以在原来教师信息的基础上修改所要更新的教师信息,教师更新页面如图5.18所示。

    核心代码如下:
    public String updateTeacher(){ try { this.teacherService.updateTeacher(this.teacherBean); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    5.5.4 教师删除管理员在查看院系信息页面点击要删除的院系信息后的删除直接删除该条院系信息,教师删除页面如图5.19所示。

    核心代码如下:
    public String deleteTeacher(){ try { this.teacherBean = this.teacherService .findTeacherById(this.teacherBean.getId()); this.teacherService.deleteTeacher(this.teacherBean); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    5.5.5 学生添加管理员登录系统后选择用户管理,点添加学生信息进入学生添加页面,在该页面输入学生编号、学生名称、出生日期、民族、电话、邮箱、职位、密码、权限等信息,点添加按钮添加学生,学生添加页面如图5.20所示。

    核心代码如下:
    public String addStudent(){ try { this.studentService.addStudentBean(this.studentBean); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    5.5.6 学生查询管理员登录系统后选择用户管理,点查看学生信息进入学生查看限定条件页面,查看学生限定条件填写页面如图5.21所示,在该页面上填写要查看的学生学号、姓名、出生日期等限定条件,点查看将显示限定条件下的所有学生,学生查看页面如图5.22所示。

    核心代码如下:
    public String searchStudent(){ try{ this.listStudentBean = this.studentService .searchStudentBean(this.studentBean); this.listPoliticsClassBean = this.politicsClassService .findAllPoliticsClass(); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    5.5.7 学生更新管理员在查看学生信息页面点击要更新的学生信息后的更新,进入更新学生信息页面,在该页面将显示原学生信息,可以在原来学生信息的基础上修改所要更新的学生信息,学生更新页面如图5.23所示。

    核心代码如下:
    public String updateStudent(){ try { this.studentService.updateStudent(this.studentBean); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    5.5.8 学生删除管理员在查看院系信息页面点击要删除的院系信息后的删除直接删除该条院系信息,学生删除页面如图5.24所示。

    核心代码如下:
    public String deleteStudent(){ try { this.studentBean = this.studentService .findStudentById(this.studentBean.getId()); this.studentService.deleteStudent(this.studentBean); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    5.6 课程管理课程管理包含课程添加、课程查看、课程更新、课程删除四个功能,其中课程添加和课程查看直接在课程管理模块下就能操作,课程更新与课程删除需要在查询出课程信息后才能进行操作。
    5.6.1 课程添加管理员登录系统后选择课程管理,点添加课程进入课程添加页面,在该页面输入课程名称、课程描述信息,点添加按钮添加课程,课程添加页面如图5.25所示。

    核心代码如下:
    public String addCourse(){ try { this.courseService.addCourse(this.courseBean); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    5.6.2 课程查看管理员登录系统后选择课程管理,点查看课程进入查看课程页面,该页面讲显示所有课程信息,课程查看页面如图5.26所示。

    核心代码如下:
    public String listCourse(){ try { this.listCourseBean = this.courseService.findAllCourse(); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    5.6.3 课程更新管理员在查看课程信息页面点击要更新的课程后的更新,进入更新课程页面,在该页面将显示原课程信息,可以在原来课程信息的基础上修改所要更新的课程信息,课程更新页面如图5.27所示。

    核心代码如下:
    public String updateCourse(){ try{ this.courseService.updateCourse(this.courseBean); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    5.6.1 课程删除管理员在查看课程信息页面点击要删除的课程后的删除直接删除该条课程信息,课程删除页面如图5.28所示。

    核心代码如下:
    public String deleteCourse(){ try { this.courseBean = this.courseService.findCourseById(this.courseBean .getId()); this.courseService.deleteCourse(this.courseBean); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    5.7 题库管理题库管理包含题库选择题添加、题库选择题查看、题库选择题更新、题库选择题删除、题库判断题添加、题库判断题查看、题库判断题更新、题库判断题删除、题库填空题添加、题库填空题查看、题库填空题更新、题库填空题删除、题库简答题添加、题库简答题查看、题库简答题更新和题库简答题删除十六个功能,其中题库选择题添加、题库选择题查看、题库判断题添加、题库判断题查看、题库填空题添加、题库填空题查看、题库简单题添加和题库简答题查看直接在题库管理模块下就能操作,题库选择题更新与题库选择题删除需要再查询出题库选择题后操作,题库判断题更新与题库判断题删除需要再查询出题库判断题后操作,题库填空题更新和题库填空题删除需要再查询出题库填空题后操作,题库简单题更新与题库简答题删除需要再查询出题库简答题后操作。
    5.7.1 题库选择题添加教师登录系统后选择题库管理,点添加选择题进入题库选择题添加页面,在该页面选择题目所属课程、输入题目、正确答案,点添加按钮添加题库选择题,题库选择题添加页面如图5.29所示。

    核心代码如下:
    public String addQuestionBankSelect(){ try { this.questionBankSelectService .addQuestionBankSelect(this.questionBankSelectBean); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    5.7.2 题库选择题查看教师登录系统后选择题库管理,点查看选择题信息进入题库选择题查看限定条件填写页面,查看题库选择题限定条件填写页面如图5.30所示,在该页面上选择要查看的题库选择题的出题老师、所属课程以及题目关键字,点查看将显示限定条件下的所有题库选择题,题库选择题查看页面如图5.31所示。

    核心代码如下:
    public String searchQuestionBankSelect(){ try { this.listCourseBean = this.courseService.findAllCourse(); this.listQuestionBankSelectBean = this.questionBankSelectService .searchQuestionBankSelect(this.questionBankSelectBean); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    5.7.3 题库选择题更新教师在查看题库选择题页面点击要更新的选择题后的更新,进入更新题库选择题页面,在该页面将显示原选择题题目信息,可以在原题库选择题题目的基础上修改所要更新的选择题信息,题库选择题更新页面如图5.32所示。

    核心代码如下:
    public String updateQuestionBankSelect(){ try { this.questionBankSelectService .updateQuestionBankSelect(this.questionBankSelectBean); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    5.7.4 题库选择题删除教师在查看题库选择题页面点击要删除的选择题后的删除直接删除该选择题信息,题库选择题删除页面如图5.33所示。

    核心代码如下:
    public String deleteQuestionBankSelect(){ try { this.questionBankSelectBean = this.questionBankSelectService .findQuestionBankSelectById(this.questionBankSelectBean .getId()); this.questionBankSelectService .deleteQuestionBankSelect(this.questionBankSelectBean); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; }}
    5.7.5 题库判断题添加教师登录系统后选择“题库管理”,点添加“判断题进”入题库判断题添加页面,在该页面判断题目所属课程、输入题目、正确答案,点添加按钮添加题库判断题,题库判断题添加页面如图5.34所示。

    核心代码如下:
    public String addQuestionBankJudge(){ try { this.questionBankJudgeService .addQuestionBankJudge(this.questionBankJudgeBean); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    5.7.6 题库判断题查看教师登录系统后选择题库管理,点查看判断题信息进入题库判断题查看限定条件填写页面,查看题库判断题限定条件填写页面如图5.35所示,在该页面上判断要查看的题库判断题的出题老师、所属课程以及题目关键字,点查看将显示限定条件下的所有题库判断题,题库判断题查看页面如图5.36所示。

    核心代码如下:
    public String searchQuestionBankJudge(){ try { this.listCourseBean = this.courseService.findAllCourse(); this.listQuestionBankJudgeBean = this.questionBankJudgeService .searchQuestionBankJudge(this.questionBankJudgeBean); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    5.7.7 题库判断题更新教师在查看题库判断题页面点击要更新的判断题后的更新,进入更新题库判断题页面,在该页面将显示原判断题题目信息,可以在原题库判断题题目的基础上修改所要更新的判断题信息,题库判断题更新页面如图5.37所示。

    核心代码如下:
    public String updateQuestionBankJudgeAction(){ try { this.questionBankJudgeService .updateQuestionBankJudgeAction(this.questionBankJudgeBean); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    5.7.8 题库判断题删除教师在查看题库判断题页面点击要删除的判断题后的删除直接删除该判断题信息,题库判断题删除页面如图5.38所示。

    5.7.9 题库填空题添加教师登录系统后填空题库管理,点添加选填空进入题库填空题添加页面,在该页面填空题目所属课程、输入题目、正确答案,点添加按钮添加题库填空题,题库填空题添加页面如图5.39所示。

    核心代码如下:
    public String addQuestionBankFill(){ try { this.questionBankFillService .addQuestionBankFill(this.questionBankFillBean); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    5.7.10 题库填空题查看教师登录系统后选择题库管理,点查看填空题信息进入题库填空题查看限定条件填写页面,查看题库填空题限定条件填写页面如图5.40所示,在该页面上填空要查看的题库填空题的出题老师、所属课程以及题目关键字,点查看将显示限定条件下的所有题库填空题,题库填空题查看页面如图5.41所示。

    核心代码如下:
    public String searchQuestionBankFill(){ try { this.listCourseBean = this.courseService.findAllCourse(); this.listQuestionBankFillBean = this.questionBankFillService .searchQuestionBankFill(this.questionBankFillBean); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    5.7.11 题库填空题更新教师在查看题库填空题页面点击要更新的填空题后的更新,进入更新题库填空题页面,在该页面将显示原填空题题目信息,可以在原题库填空题题目的基础上修改所要更新的填空题信息,题库填空题更新页面如图5.42所示。

    核心代码如下:
    public String updateQuestionBankFill(){ try { this.questionBankFillService .updateQuestionBankFill(this.questionBankFillBean); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    5.7.12 题库填空题删除教师在查看题库填空题页面点击要删除的填空题后的删除直接删除该填空题信息,题库填空题删除页面如图5.43所示。

    核心代码如下:
    public String deleteQuestionBankFill(){ try { this.questionBankFillBean = this.questionBankFillService .findQuestionBankFillById(this.questionBankFillBean.getId()); this.questionBankFillService .deleteQuestionBankFill(this.questionBankFillBean); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    5.7.13 题库简答题添加教师登录系统后填空题库管理,点添加简答题进入题库简答题添加页面,在该页面选择题目所属课程、输入题目、正确答案,点添加按钮添加题库简答题,题库简答题添加页面如图5.44所示。

    核心代码如下:
    public String addQuestionBankShortAnswer(){ try { this.questionBankShortAnswerService .addQuestionBankShortAnswer(this.questionBankShortAnswerBean); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    5.7.14 题库简答题查看教师登录系统后选择题库管理,点查看简答题信息进入题库简答题查看限定条件填写页面,查看题库简答题限定条件填写页面如图5.45所示,在该页面上简答要查看的题库简答题的出题老师、所属课程以及题目关键字,点查看将显示限定条件下的所有题库简答题,题库简答题查看页面如图5.46所示。

    核心代码如下:
    public String searchQuestionBankShortAnswer(){ try { this.listQuestionBankShortAnswerBean = this.questionBankShortAnswerService .searchQuestionBankShortAnswer(this.questionBankShortAnswerBean); this.listCourseBean = this.courseService.findAllCourse(); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    5.7.15 题库简答题更新教师在查看题库简答题页面点击要更新的简答题后的更新,进入更新题库简答题页面,在该页面将显示原简答题题目信息,可以在原题库简答题题目的基础上修改所要更新的简答题信息,题库简答题更新页面如图5.47所示。

    核心代码如下:
    public String updateQuestionBankShortAnswer(){ try { this.questionBankShortAnswerService .updateQuestionBankShortAnswer(this.questionBankShortAnswerBean); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    5.7.16 题库简答题删除教师在查看题库简答题页面点击要删除的简答题后的删除直接删除该简答题信息,题库简答题删除页面如图5.48所示。

    核心代码如下:
    public String deleteQuestionBankShortAnswer(){ try { this.questionBankShortAnswerBean = this.questionBankShortAnswerService .findQuestionBankShortAnswerById(this.questionBankShortAnswerBean .getId()); this.questionBankShortAnswerService .deleteQuestionBankShortAnswer(this.questionBankShortAnswerBean); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    5.8 考试管理考试管理包含试卷生成、在线考试、教师阅卷三个功能,试卷生成在管理员登录系统后在考试管理中直接操作,在线考试为学生登录系统后再考试管理中选择在线考试进行试卷的选择,然后作答,老师阅卷为老师登录系统后点击教师阅卷选择所要批阅的试卷,然后再批阅试卷。
    5.8.1 试卷生成管理员登录系统后选择考试管理,点击“试卷生成”进入试卷生成信息填写页面,试卷生成如图5.49所示

    核心代码如下:
    public String createPaper(){ //生成试卷选择题 while (selectNumber != 0){ } //生成试卷填空题 while (fillNumber != 0){ } shortAnswerNumber = this.shortAnswerNumber; //生成试卷简答题 while (shortAnswerNumber != 0){ } return SUCCESS; }
    5.8.2 学生考试学生登录系统后选择考试管理,点击“在线考试”进入试卷选择页面,试卷选择页面如图5.50所示,点击开始考试进入考试页面,在考试页面选择相应的题型作答。选择题答题页面如图5.51、判断题答题页面图5.52、填空题答题页面图5.53、简答题答题页面图5.54所示。

    核心代码如下:
    public String examingSelect(){ PaperSelectBean paperSelectBean = this.paperSelectService.findPaperSelectById(this.paperSelectBean.getId()); paperSelectBean.setStudentanswer(this.paperSelectBean.getStudentanswer()); if (paperSelectBean.getModelanswer() == paperSelectBean.getStudentanswer()){ paperSelectBean.setGetscore(paperSelectBean.getScore()); } else { paperSelectBean.setGetscore(0); } this.studentid = (Integer) session.getAttribute("studentid"); this.paperName = (String) session.getAttribute("paperName"); this.paperSelectService.updatePaperSelect(paperSelectBean); this.listPaperSelectBean = this.paperSelectService.searchPaperSelect(this.studentid, this.paperName); return SUCCESS; }
    判断题答题页面

    核心代码如下:
    public String examingJudge(){ PaperJudgeBean paperJudgeBean = this.paperJudgeService.findPaperJudgeById(this.paperJudgeBean.getId()); paperJudgeBean.setStudentanswer(this.paperJudgeBean.getStudentanswer()); if (paperJudgeBean.getModelanswer() == paperJudgeBean.getStudentanswer()){ paperJudgeBean.setGetscore(paperJudgeBean.getScore()); } else { paperJudgeBean.setGetscore(0); } this.studentid = (Integer) session.getAttribute("studentid"); this.paperName = (String) session.getAttribute("paperName"); this.paperJudgeService.updatePaperJudge(paperJudgeBean); this.listPaperJudgeBean = this.paperJudgeService.searchPaperJudge(this.studentid, this.paperName); return SUCCESS; }
    填空题答题页面

    核心代码如下:
    public String examingFill(){ PaperFillBean paperFillBean = this.paperFillService.findPaperFillById(this.paperFillBean.getId()); paperFillBean.setStudentanswer(this.paperFillBean.getStudentanswer()); this.studentid = (Integer) session.getAttribute("studentid"); this.paperName = (String) session.getAttribute("paperName"); this.paperFillService.updatePaperFill(paperFillBean); this.listPaperFillBean = this.paperFillService.searchPaperFill(this.studentid, this.paperName); return SUCCESS; }
    简答题答题页面

    核心代码如下:
    public String examingShortAnswer(){ PaperShortAnswerBean paperShortAnswerBean = this.paperShortAnswerService.findPaperShortAnswerById(this.paperShortAnswerBean.getId()); paperShortAnswerBean.setStudentanswer(this.paperShortAnswerBean.getStudentanswer()); this.studentid = (Integer) session.getAttribute("studentid"); this.paperName = (String) session.getAttribute("paperName");this.paperShortAnswerService.updatePaperShortAnswer(paperShortAnswerBean); this.listPaperShortAnswerBean = this.paperShortAnswerService.searchShortAnswer(this.studentid, this.paperName); return SUCCESS; }
    5.8.3 教师阅卷教师登录系统后选择考试管理,点击“教师阅卷”进入试卷与题型选择页面,题型选择页面如图5.55所示,在该页面选择试卷与题型点击“开始阅卷”进入阅卷页面,判断题阅卷页面如图5.56、简答题阅卷页面图5.57所示。

    核心代码如下:
    public String listPaperQuetionType(){ try { this.listPaperName = this.paperSelectService.searchPaper(); return SUCCESS; } catch (Exception e) { e.printStackTrace(); return ERROR; } }
    判断题阅卷页面

    核心代码如下:
    public String updateFillBean(){ int temp = 0; this.listPaperFillBean = (List<PaperFillBean>)session.getAttribute("listPaperFillBean"); PaperFillBean paperFillBean = new PaperFillBean(); paperFillBean = this.paperFillService.findPaperFillById(this.paperFillBean.getId()); paperFillBean.setGetscore(this.paperFillBean.getGetscore()); this.paperFillService.updatePaperFill(paperFillBean); temp = this.index; this.index = temp; temp = this.listsize; this.listsize = temp; return SUCCESS; }
    简答题阅卷页面

    核心代码如下:
    public String updateShortAnswerBean(){ int temp = 0; this.listPaperShortAnswerBean = (List<PaperShortAnswerBean>)session.getAttribute("listPaperShortAnswerBean"); PaperShortAnswerBean paperShortAnswerBean = new PaperShortAnswerBean(); paperShortAnswerBean = this.paperShortAnswerService.findPaperShortAnswerById(this.paperShortAnswerBean.getId()); paperShortAnswerBean.setGetscore(this.paperShortAnswerBean.getGetscore()); this.paperShortAnswerService.updatePaperShortAnswer(paperShortAnswerBean); temp = this.index; this.index = temp; temp = this.listsize; this.listsize = temp; return SUCCESS; }
    5.9 成绩管理成绩管理只实现了学生查看本人成绩,学生在登录系统后选择成绩管理,点击查看成绩查看本人所有考试的成绩,成绩查看界面如图5.58所示。

    核心代码如下:
    public String findGradeCourse(){ for ( int i = 0; i < listcourseidsize; i ++){ for ( int j = 0 ; j < listPaperNamesize; j ++){ GradeBean gradeBean = new GradeBean(); selectBeans = this.paperSelectService.searchPaperSelect(this.studentBean.getId(), this.listcourseid.get(i).intValue(), this.listPaperName.get(j)); judgeBeans = this.paperJudgeService.searchPaperJudge(this.studentBean.getId(), this.listcourseid.get(i).intValue(), this.listPaperName.get(j)); fillBeans = this.paperFillService.searchPaperFill(this.studentBean.getId(), this.listcourseid.get(i).intValue(), this.listPaperName.get(j)); shortAnswerBeans = this.paperShortAnswerService.searchShortAnswer(this.studentBean.getId(), this.listcourseid.get(i).intValue(), this.listPaperName.get(j)); for (int k = 0; k < selectBeanslength; k ++){ n = n + selectBeans.get(k).getGetscore(); } for (int k = 0; k < judgeBeanslength; k ++){ n = n + judgeBeans.get(k).getGetscore(); } for (int k = 0; k < fillBeanslength; k ++){ n = n + fillBeans.get(k).getGetscore(); } for (int k = 0; k < shortAnswerBeanslength; k ++){ n = n + shortAnswerBeans.get(k).getGetscore(); } for(int k = 0; k < listCourseBeansizi; k ++){ if (selectBeans.get(0).getCourseid() == this.listCourseBean.get(k).getId()) gradeBean.setCourseName(this.listCourseBean.get(k).getName()); } gradeBean.setPaperName(selectBeans.get(0).getPapername()); gradeBean.setGrade(n); this.listGradeBean.add(gradeBean); } } return SUCCESS; }
    第六章 总结该在线考试系统以java作为开发语言,Struts2、Hibernate实现前后台数据管理,Spring实现业务逻辑管理,以MyEclipse作为开发工具,MySQL实现数据持久化,Tomcat部署整个系统,实现了院系管理、专业管理、班级管理、用户管理、课程管理、题库管理、考试管理、成绩管理八大功能。考试的主要参与者——学生在考试过程中出来要体现考生的姓名、学号、考试科目外还需要知道考生属于哪个班级,每个班级都属于特定的专业,每个专业又只属于一个院系,因此在该考试系统中出了实现基本的考试功能还实现了对学生的相关信息的管理,在添加院系时,每个院系的名称唯一,添加专业时,在院系管理中必须存在这样的院系才能添加相关专业,同样,添加班级是首先在专业中要有这样的专业,添加学生时也又必须存在学生所属班级。在学生信息管理这几大模块中,如果对学生所属班级的信息进行更改,那学生对应的所属班级信息会自动更改,更改了专业信息后,班级对应所属专业也会自动更改,修改院系信息后专业对应的所属专业也会自动修改。在线考试系统的另外一个优点就是针对不同的考试科目可以出不同的试卷,也许没门考试的难度不一样,因此该考试系统中实现的自主确定考试题目数量与自主设置每题分值是该考试系统的另一个优点。管理员可以根据考试的实际难易程度设定不同的考试题目数量,根据题目的难易程度设定不同类型的题目的分值,同时,在生成考试试卷时对于试卷生成后老师不能预览考试题目,试卷中的题目也是从数据库中随机抽取,因此这样实现了考试过程中的对试卷的保密性。学生在正个考试过程中能看到自己考试剩余时间,这能使考生在考试时合理安排答题时间。
    该考试系统虽已经相当完善,但是没有任何一个软件没有任何缺陷,同样该考试系统也存在着它特有的不足,该考试系统对数据的输入只能一个一个的添加,不能实现文件导入功能,这是该考试系统的最大不足,当然,该考试系统还有其他的不足之处,如页面的设计有些地方不是特别合理,因此改考试系统还有待完善。
    致谢在本系统的设计和实现过程中,老师给我了极大的帮助。老师在我设计系统的功能时给了我许多技术指导,并教会了我许多新的知识,拓宽了我的眼界。更为重要的是,在老师的帮助下让我体会到了当一名好的程序员是需要付出极大的耐心与努力,为将来的工作做好了心理上的准备。
    同时,在这毕业的最后,我也要感谢大学四年来与我共同生活,共同努力,共同学习进步的同学们,我们互帮互助,为我们自己的青春挥洒汗水,为我们的将来而努力。谢谢你们!
    参考文献[1] 何为东.在线考试系统设计[D]:[学士学位论文].武汉:武汉大学,2012.
    [2] 王子强.在线考试系统需求分析设计[EB/OL].http://wenku.baidu.com/view/d4002f37ee06eff9aef807c3.html
    [3] 吴国柱.基于Java的在线考试系统设计[D]:[学士学位论文].武汉:华中科技大学,2012.
    [4] 吴建,张晓东.JSP网络开发入门与实践[M].北京:人民邮电出版社,2007,7~15.
    [5] 计磊,李显,周伟.J2EE—-Eclipse Struts Hibernate Spring整合应用案例[M].北京:人民邮电出版社,2006,21~27.
    [6] 戎伟,张双.精通Struts—-Java流行服务器、框架、工具及整合应用[M].北京:人民邮电出版社,2007,19~31.
    [7] 范立峰,林果园.JavaWeb程序设计教程[M].北京:人民邮电出版社,2011,173~224.
    [8] 孟劫.精通Spring—-Java轻量级架构开发实践[M].北京:人民邮电出版社,2007,41~96.
    [9] 崔洋 贺亚茹.MySQL数据库应用从入门到精通[M].北京:中国铁道出版社,2012,24~72.
    [10] 克尼亚万,德克,曹旭东.深入剖析Tomcat[M].北京:机械工业出版社,2012,22~87.
    [11] 张海藩.软件工程导论 [M].北京:清华大学出版社,2008,55~73.
    [12] 王珊,萨师煊.数据库系统概论(第四版)[M].北京:高等教育出版社,2011,198~235.
    8 评论 223 下载 2018-09-27 22:47:00 下载需要14点积分
  • 基于JavaScript实现的数独游戏

    一、问题描述设计并实现一个基本的数独游戏。功能包括根据用户选择的级别给出初始盘面数字(级别越低,数字越多),并且能够实时监测并提示冲突;当用户完成后,保存最近十次的成绩(完成一次所需时间)以及最好成绩。
    二、问题分析数独是源自18世纪瑞士的一种数学游戏。是一种运用纸、笔进行演算的逻辑游戏。玩家需要根据9×9盘面上的已知数字,推理出所有剩余空格的数字,并满足每一行、每一列、每一个粗线宫(3*3)内的数字均含1-9,不重复。
    因此,核心问题就是我们需要得到一个含有一定数目空格的九宫格,并且一定存在一种填写方式使得九宫格满足数独的要求。
    我们可以先得到一个完整的,满足每一行、每一列、每一个粗线宫(3*3)内的数字均含1-9,不重复的数独表。然后随机得到数独表中空格的位置,在通过可视化生成游戏界面,实现交互功能。
    在用户与界面交互过程中,实时监测冲突是否产生,检验提交的答案是否正确,保存最近十次成绩和最好的一次成绩。
    三、算法设计本游戏为使用JavaScript开发的网页数独游戏,因此以下算法实现为JavaScript实现。
    经问题分析可得我们需要解决的几个核心子问题,并设计算法如下:
    3.1 传统回溯法生成数独表网上提供的数独算法多种多样,但绝大部分的核心思想依然是传统回溯法:通过一个9*9的二维数组sudu表示一个数独九宫格。
    首先初始化该数组,值都为0,然后将该九宫格的第一行依次随机填入1-9中的一个数字。接下来,从第二行开始,遍历该数组剩下的元素。
    每遍历到一个位置,随机填入1-9中的一个数字,然后判断这个九宫格目前的所有数字是否满足每一行、每一列、每一个粗线宫(3*3)内的数字均含1-9,不重复的要求。如果满足,继续向下一个位置遍历;如果不满足,则重新随机填写该位置。当重复填写了十次之后依然不满足要求,则回溯到上一个位置,重新填写该位置。
    直到遍历完所有数组后,我们也就得到了一个完整的数独表。用sudu表示。
    Math.floor(Math.random() * 9 + 1)可得到1-9范围内的随机整数。
    3.2 随机生成难度级别不同的数独由3.1得,sudu数组保存着完整的数独表,那么游戏中的有空格的数独表可以用9*9的二维数组curSudu表示,其中空格位置的数字设为0。如果填入了数字,则实时修改curSudu中该位置的值。
    通过空格个数的不同来区分数独难度级别,空格个数越多,难度级别越高。设计该游戏难度级别有三个,分别为,初级:15个空格;中级:30个空格;高级:45个空格。
    欲实现该需求,则先根据难度随机生成对应数目的空格的位置坐标(i,j),用数组表示为:blanks=[{row: i, column: j},……]。其中0 <= i <= 8, 0 <= j <= 8。通过Math.floor(Math.random() * 9)即可生成需要的i,j。curSudu一开始等于sudu,得到blanks后将curSudu对应空格的值设为0。
    3.3 用户填写数独时实时判断是否产生冲突用户每填写完一个数字,都会触发判断冲突事件。由于之前填过的数字都被判断过,所以只需要判断刚刚填入的数字所在的行,列,粗线宫内是否满足条件。
    首先保存刚刚填入的数字num,然后对所在的行,列,(3*3)区域,分别进行遍历,假如遍历到的数字等于刚刚填入的数字并且所在的位置不同,则该填入数字在九宫格内产生了冲突;否则没有冲突。
    3.4 保存最近十次成绩和最好成绩设计成绩的数据结构为:
    let user = { username: username, time: time}
    时间表示成绩高低,时间越短,成绩越好。
    每当游戏开始时,将当前时刻距 1970 年 1 月 1 日之间的毫秒数保存在全局变量time中。当提交答案,游戏结束时,time = ((new Date().getTime() - time) / 1000 / 60).toFixed(2),得到从开始到结束的时间,以分钟为单位,保留两位小数。
    每次提交成绩时,将用户名和成绩保存在user中。然后从数据中通过getRank()得到最近的十次成绩(用长度为10的一维数组ranks表示),通过getMaxGrade()得到最好的一次成绩maxGrade。
    将user.time与maxGrade.time进行比较,如果user.time比maxGrade.time更小,则maxGrade=user;然后将user插入ranks的最前面,去掉ranks最后一个元素,将ranks的长度保持在十。然后存入数据中。
    四、软件架构该软件是由JavaScript,HTML,CSS编写的的网页游戏,其中主要逻辑算法由JavaScript完成。而其软件架构为MVC模式
    4.1 控制层(Controller)JavaScript是一门事件驱动型的语言,通过在网页的特定DOM中绑定相应的事件监听,当用户在界面上特定的DOM元素上进行特定操作时就会触发对应的事件监听函数时,对逻辑进行处理。
    4.2 视图(View)该软件的界面由HTML和CSS决定,文件夹中的index.html和style.css会在浏览器的运行下生成对应的DOM树与样式,并在浏览器上渲染出界面。Js可以修改DOM树和样式,从而改变视图。
    4.3 模型(Model)sudu.js和index.js文件中保存着实现处理事件的对应代码。而数据保存在浏览器的localstorage中。因为由于规定对浏览器的限制,浏览器只能读取本地文件,却不能修改本地文件,所以在不使用服务器的情况下,本软件通过浏览器的localstorage来保存修改成绩。
    五、实现与测试5.1 软件实现生成数独表和判断是否冲突的函数
    在sudu.js中定义Sudu函数,返回函数:
    回溯法生成数独表:gennerateShudu(), 判断填入元素所在的行,列,(3*3)是否冲突:checkRow(), checkColumn(), checkNine()。
    5.2 软件测试游戏初始界面

    选择难度为“初级”后点击‘开始游戏’按钮后游戏开始界面

    在第1行第2列填入5,产生冲突后右边文本框中提示冲突,空格颜色变红

    在修改数字为9之后,冲突消失界面

    填满数字提交答案之前的界面

    点击‘提交结果’按钮后游戏界面,显示用时3.71min,要求输入用户名

    填入用户名

    点击‘提交’按钮,成绩保存后的界面

    点击‘排行榜’按钮,打开成绩榜单,显示最好的一次成绩与最近十次成绩,其中最近一次成绩yolanda:3.71min 保存在最前面

    六、性能评价传统的回溯法的性能较好,通过这种算法可以较快地得到满足数独要求的二维数组。但仍然存在可以优化的余地,不断的回溯的时间复杂度仍然较高,但在游戏中,并不碍事。
    但是因为浏览器本身对于内存管理以及计算机的问题,在多次游戏之后,浏览器很有可能会崩溃,此时关掉浏览器,重新打开文件即可。
    七、总结本次课程设计,我选择了编写数独游戏的课题,一开始时是准备用C语言编写,用easyx的图形库编写图形界面。但是发现easyx的图形库用起来仍然很麻烦。因此最后编写的语言选择了我最为熟悉的JavaScript,根据JavaScript的应用领域,可视化方面选择了网页版本。
    本次课程设计较为简单,主要难点便在于如何设计出性能较好的生成完整数独表的算法以及如何在不用服务器的情况下,让浏览器能够对本地的文件进行修改。通过查询资料与自己琢磨,选择了传统的回溯法,并且用JavaScript实现。在上文也提到,由于产商,语言制定者对浏览器和语言的限制,JavaScript无法通过浏览器对本地的文件进行修改,仅能做读取操作,在多方权衡之后,选择了浏览器自带的localStorage,将最近十次成绩的字符串和最好一次成绩的字符串缓存于浏览器中。
    通过本次课程设计,我对网页开发更加熟悉,对于浏览器的性能,特性,JavaScript语言的一些特性也更加了解。对于算法的设计也更加熟练,收益匪浅。
    1 评论 13 下载 2019-01-27 10:58:01 下载需要6点积分
  • 基于JSP和MYSQL实现的学生成绩管理系统

    1 概述1.1 开发背景随着学生数量的日渐增多,学生教务系统的数据量也不断增加,这无疑大大增加了教务系统的负担。如果能把负责学生成绩管理的模块独立出来形成一个独立的系统,便可以有效降低教务系统的数据量,不仅可以方便管理员对于所有学生的信息进行系统的管理,而且便于教师对学生成绩进行查询和修改,学生也可以查询自己的成绩。一个好的学生管理系统,正好可以满足当前的市场需求,提高工作效率,并取得一定的经济效益。本系统的创建就是为了减少人力、物力、时间的耗费。这些优点能够极大地提高学生成绩管理的效率。因此,开发一套合适的、兼容性好的系统是很有必要的。
    1.2 开发目标在规定的时间和经济范围内,交付一个基于JSP/SEVLET的成绩管理系统,该系统具备一下要求:

    有良好的前端展示,符合商业化的要求,同时便于用户操作,可以轻松上手
    具有可靠性,系统不同角色分配的权限合理,不泄露用户信息
    可以批量录入信息,实现对信息的增删改查
    反应迅速,能及时给出反馈

    1.3 设计原则为了保持良好的用户体验,设计系统的时候需要遵循以下通用原则:

    设计适当的表示导引。在用户浏览网站的时候,随时告知访问者他们身处何处,想要找的东西在哪里,如何找到想要找的东西
    设定期望并做出反馈。当用户提交表单,点击链接的时候,成功与否都需要网页及时给出反馈,保证用户不会因为久久得不到回复而对网站不信任
    设计要基于人机工程学。无论是字体、字号、字色、字间距的选择还是按钮的放置位置,按钮的数量等,这些都要考虑到用户的身体,即手、目、耳等
    考虑标准,保持一致。设计网站切不可因为如何好看如何设计,一定要保持前后风格一致
    考虑辨识。用户不喜记忆,所以网站设计一定要具有记忆,访问前后不能靠用户去辨识记忆
    考虑用户水平不一样。我们遵循的原则是做最简单的网站,然后再在其基础上添加特殊的功能和属性

    2 关键技术分析2.1 前端2.1.1 前端界面优化为了保证良好的用户体验,我们以HTML语言与CSS样式表为基础制作页面,js进行页面与组件间的逻辑实现。在界面美化上,我们保证所有页面风格一致,并具有自适应性的功能,可以随着分辨率的大小和设备的大小而变化,同时,也注意了不同代码在浏览器兼容性上的问题,所以在相应CSS文件里添加了不同浏览器所需的备用样式。在配色和布局上参考了一些优秀网站的设计,保证良好的交互性。
    2.1.2 前端技术前端主要使用html、css、js等基本技术,此外还用到bootstrap、jQuery、jstl等技术使得前端交互性增强。页面布局多采用相对布局,放大或缩小页面,页面内容分仍保持相对位置。
    Frame框架:用于实现页面的局部加载和刷新。
    jQuery技术:为了可以直接在某条记录上动态修改数据(即直接在当前页面修改而不是重新打开一个修改数据的页面),采用了jQuery技术,实现了在记录原位置直接修改数据。
    Echarts:为了使数据可视化,我们采用了百度的Echart图表技术http://echarts.baidu.com ,将数据如学生的成绩、课程的及格率等以条形统计图、动态水球图的形式展现,使得教师可以方便直观地分析学生的成绩情况(各分数段的人数,及格率等)。
    Ajax:AJAX 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。AJAX 通过在后台与服务器进行少量数据交换,使网页实现异步更新。这意味着可以在不重载整个页面的情况下,对网页的某些部分进行更新。传统的网页(不使用 AJAX)如果需要更新内容,必须重载整个页面。
    JSON: JavaScript Object Notation(JavaScript 对象表示法)JSON 是存储和交换文本信息的语法。类似 XML,比 XML 更小、更快,更易解析,是一种轻量级的数据传输方式。
    2.2 后端2.2.1 数据访问方式(DAO)DAO (DataAccessObjects 数据存取对象)是指位于业务逻辑和持久化数据之间实现对持久化数据的访问。通俗来讲,就是将数据库操作都封装起来。
    DAO 模式提供了访问关系型数据库系统所需操作的接口,将数据访问和业务逻辑分离对上层提供面向对象的数据访问接口。
    DAO 模式的优势就在于它实现了两次隔离。

    隔离了数据访问代码和业务逻辑代码。业务逻辑代码直接调用DAO方法即可,完全感觉不到数据库表的存在。分工明确,数据访问层代码变化不影响业务逻辑代码,这符合单一职能原则,降低了藕合性,提高了可复用性
    隔离了不同数据库实现。采用面向接口编程,如果底层数据库变化,如由 MySQL 变成 Oracle 只要增加 DAO 接口的新实现类即可,原有 MySQL 实现不用修改。这符合”开-闭”原则。该原则降低了代码的藕合性,提高了代码扩展性和系统的可移植性

    2.2.2 后端逻辑处理使用了三层架构(web层、service层和dao层)

    Controller层:(也叫web层)Controller层负责具体的业务模块流程的控制,在此层里面要调用Serice层的接口来控制业务流程。
    Service层:Service层主要负责业务模块的逻辑应用设计。调用DAO层存取数据。
    DAO层:DAO层主要是做数据持久层的工作,负责与数据库进行联络的一些任务都封装在此,DAO层的设计首先是设计DAO的接口,然后定义此接口的实现类,然后就可在模块中调用此接口来进行数据业务的处理,而不用关心此接口的具体实现类是哪个类,显得结构非常清晰。

    该三层架构的优点:

    开发人员可以只关注整个结构中的其中某一层
    可以很容易的用新的实现来替换原有层次的实现
    可以降低层与层之间的依赖
    有利于标准化
    利于各层逻辑的复用
    结构更加的明确
    在后期维护的时候,极大地降低了维护成本和维护时间

    2.3 数据库2.3.1 数据库的连接该系统采用mysql数据库,连接数据库用到jdbc数据库驱动。当系统比较庞大时,可以使用数据库连接池如c3p0、DBCP等。数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。
    2.3.2 数据库的设计该系统使用关系型数据库,建表时采用关系模型的数据结构,每个实体对应一张表,联系若有属性,也可对应一张表,如学生实体对应一张学生表,课程对应一张课程表,课程和学生之间是学习的关系,他们会产生一个属性-分数,因此也应该有一张学生课程表。
    设置表的主键、外键以及表之间的级联关系。如:成绩表中的学生必须是学生表中存在的学生,当删除学生表中某一个学生时,若两张表是级联删除关系时,成绩表中该学生的信息自动删除。更新时也一样。这样避免了后端逻辑实现的复杂性。
    2.4 服务器使用Tomcat服务器。Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。
    2.5 文件上传2.5.1 图片上传用到的工具:

    对于文件上传,浏览器在上传的过程中是将文件以流的形式提交到服务器端的,如果直接使用Servlet获取上传文件的输入流然后再解析里面的请求参数是比较麻烦,所以一般选择采用apache的开源工具common-fileupload这个文件上传组件。这个common-fileupload上传组件的jar包可以去apache官网上面下载common-fileupload是依赖于common-io这个包的。
    图片的储存(存取图片的相对路径而不是图片):
    如果直接将图片以二进制流的形式存到数据库中,既浪费时间有浪费空间,因而我们采用的方式是将图片的相对路径存到数据库中,这样极大地提高了访问数据库的速度,同时提高了数据库的空间利用率。
    2.5.2 批量导入数据(上传读取Excel)用到的工具:

    Apache POI是Apache软件基金会的开放源码函式库,POI提供API给Java程序对Microsoft Office格式档案读和写的功能。
    JXL只能对Excel进行操作,属于比较老的框架,它只支持到Excel 95-2000的版本。现在已经停止更新和维护。
    POI是apache的项目,可对微软的Word,Excel,ppt进行操作,包括office2003和2007,Excl2003和2007。poi现在一直有更新。所以现在主流使用POI。
    2.6 安全机制2.6.1 登录验证该系统有学生、教师和管理员三种不同身份的用户,系统分别为设置权限等级,当某用户登录时,系统会检索他们的等级,让他们访问对应的操作页面,学生进入学生系统模块,教师进入教师系统模块,管理员进入管理员系统模块。
    2.6.1 Filter技术用户可以通过url访问系统资源,对于一些敏感保密的资源需要有保护机制,因此采用filter技术。
    Filter也称之为过滤器,它是Servlet技术中最激动人心的技术,WEB开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能。
    同时,为了防止页面出现乱码,该系统中添加了编码过滤器。
    在该系统中,使用了过滤器,防止用户直接通过URL地址栏直接进入该系统,实现原理是,在过滤器中添加一个判断,判断用户的身份信息是否在session中,只有通过登录身份验证正确时,用户的身份才会添加到session,否则session中无该用户信息,过滤器将阻止非法访问。
    2.7 Junit4单元测试开发过程中采用Junit4单元测试技术,分别测试各个子功能模块的功能。
    在项目开发中,主要来测试DAO层(数据持久层)的数据的查询增删改功能,确保和数据库实现正确的数据存取功能。
    public class UserDaoTest { @Test public void selectUser() throws Exception { UserDao userDao = DaoFactory.getUserDaoInstance(); System.out.println(userDao.selectUser("123", "124")); } @Test public void insertUser() throws Exception { User user = new User("41612197", "123", "3"); UserDao userDao = DaoFactory.getUserDaoInstance(); userDao.insertUser(user); }
    3 系统需求分析3.1 可行性分析本节主要从经济可行性、技术可行性、操作可行性和法律可行性进行分析。
    3.1.1 经济可行性从经济可行性分析来讲,环境拥有建立该系统所需的网络设备及软件,具备了开发Web平台系统的基本条件;系统投资成本低,回报率高;人力资源方面消耗量少;而且,系统的生存周期短、系统工作负荷量不是很大、处理速度要求快。无论从横向还是纵向相比,本系统都是可以实行的。在线购物系统的设计和开发不仅是电子商务领域的重要组成部分,也是管理信息系统应用的主要方向之一。经以上分析,该系统可行。
    3.1.2 技术可行性从技术可行性来讲,本系统是基于jsp的Web开发的,jsp 是在前段和后端分离下产生的框架技术,已经被大家公认为经典的框架。而且,Java 语言技术也具有卓越的通用性、高效性、平台移植性和安全性,比较适合这个系统的开发和应用。在软件方面,考虑到系统实施的可行性,选择了现今比较流行的开发工具intelijidea进行开发管理平台的设计,使用MySQL数据库存储数据。
    在硬件方面,系统只需要一台内存4G,硬盘在500.0G的品牌机或兼容机。经以上分析,该系统可行。
    3.1.3 操作可行性本系统界面友好、整洁,操作简易,并配有相关说明,学习方便,管理人员对开发此应用系统的态度确定。
    经过以上分析,该系统可行。
    3.1.4 法律可行性考虑到使用到的软件产品的版权问题,软件服务器所使用的软件,应该尽量使用正版软件。如果正版软件价格高得难以接受,可以采用一些能满足系统运行的免费软件来替代。开发阶段可以使用试用版软件来替代,但正式发布时建议操作系统采用正版软件。
    该平台是作为课程设计,与商业无关,又因为是自主开发设计,因此不会构成侵权,在法律上可行。
    3.2 功能模块需求分析本节主要通过系统的主功能模块划分以及系统用例图来分析系统需求。
    3.2.1 主要功能模块划分学生成绩管理系统分为三个模块:学生模块、教师模块和管理员模块。

    学生模块的功能:个人信息的查询,部分信息的修改,本人成绩的查询
    教师模块的功能:个人信息的查询,成绩的录入、修改、查看和分析
    管理员模块的功能:对学生、教师、班级、课程的增删改查,课程的安排,系统用户的添加和删除,为学生分配班级等

    对于各功能的详细介绍,如表3-1所示:

    3.2.2 系统用例图根据3.2.1的主功能模块分析,学生成绩管理系统包括三个参与者:学生、教师、管理员。根据不同身份的用户和系统的交互活动不同,可以画出如下用例图:

    3.3 非功能性需求分析3.3.1系统的安全性通常在描述学生成绩管理系统安全性时,越是薄弱的部分,则往往越会受到攻击的影响。因为在攻击者在对系统进行攻击行为时,无论其出于何种目的,往往会找出该系统最易攻击的环节,从而沿阻力最小的路径行动。
    通常我们在使用系统的时候,都会经登录,输入所需的用户名及对应密码。为防止偷窥造成的密码泄露,而在对外显示时都会以“*”作为输入字符的代号。用户在登录时所使用的账号必须是数据库中已存的,且所输入的账号密码必须与数据库中存储的账号密码一致,否则都会造成登陆失败。无论是管理员还是学生或教师,为了报障学生信息系统的安全性,都必须要经过登陆方能进入系统。
    3.3.2 系统的可靠性为了防止用户的非法操作,系统通常会对不同用户提供不同的服务,并对其设置不同的操作权限。管理员可以使用基于Web的学生成绩管理系统甚至所有功能,性能可靠稳定。
    3.3.3 系统的易实用性和可扩展性一个完善的操作系统应具备以下特点:为保证系统界面具有操作简单,界面的设计以美观大方为主;系统还应该具备展示学生信息的功能,从而方便用户进行学生课程信息的浏览或比较;为方便教师分类查找,系统应该对学生的课程分类采取规范管理;为方便教师进行修改确认行为,我们队每个用户的使用权限加以完善;让学生及教师对系统内的信息修改具有自己可以操作的部分。除此之外,还有管理员的信息查询、访问次数的信息查询、成绩排行公示等功能,此外,为了方便管理者管理与教师进行成绩更新学生信息,还设置并完善的后台管理功能。
    4 系统概要设计4.1 系统概述学生信息管理系统主要包括三大模块:学生模块、教师模块和管理员模块。学生模块的功能有:查看和修改个人信息,查看成绩。教师模块的功能有:查看和修改个人资料,录入学生成绩、查看学生成绩、成绩的统计与分析。管理员模块的功能有:学生信息管理、教师信息管理、课程信息管理、班级信息管理(班级的查询增删)、安排课程、系统用户管理等。
    4.2 系统结构图
    4.3 系统结构分析学生成绩管理系统分为学生、教师和管理员三大模块。每个模块有分为几个子功能模块。这些子模块分别实现了不同的功能,学生模块的查询成绩功能,教师模块的个人信息管理、成绩的查询录入功能、系统统计分析成绩功能,管理员的学生信息管理、教师信息管理、基本信息管理和系统用户信息管理。
    学生登录后可进行的操作:

    查看个人资料(包括姓名、学号、照片、年级、联系方式和住址等)
    修改个人资料
    查询分数

    教师登陆后可进行的操作:

    查看个人资料(包括姓名、工号、照片、学位、职位等)
    修改个人资料
    录入学生成绩(可以批量导入)
    修改成绩
    查询学生成绩(可以根据课程名查询,也可以根据学号查询)
    成绩分析统计查询(系统统计分析每门课各个成绩段分布的人数,课程的及格率等,以条形图)

    管理员登陆后可进行的操作:

    管理学生信息(学生的添加删除等,可以批量导入)
    管理教师信息(教师的添加删除)
    基本信息管理(课程添加删除、班级添加删除、课程安排)
    系统用户信息管理(添加或删除系统用户,信息包括用户名、密码和权限等)

    4.4 数据库的设计与实现本系统采用mysql数据库,系统数据库名为ccs.数据库中有10张表。下面分别介绍不同的表。
    4.4.1 数据表的概述系统中包括:学生表(student)、教师表(teacher)、班级表(class)、课程表(course)、成绩表(score)、学生班级(student_class)、教师课程(teacher_class)、教师班级(teacher_class)、课程安排表(course_arrange)、系统用户表(user)
    4.4.2 数据表的结构student(学生表)
    学生表主要存取学生的学号、姓名、性别、年龄、专业、入学年份、电话、地址、照片,表结构如表4-1所示:



    序号
    字段名
    类型
    长度(字符)
    描述




    1
    studentId
    int
    11
    学生编号


    2
    studentName
    varchar
    255
    学生姓名


    3
    sex
    varchar
    255
    性别


    4
    age
    int
    20
    年龄


    5
    major
    varchar
    255
    专业


    6
    yearSchool
    int
    255
    入学年份


    7
    telephone
    varchar
    255
    电话号码


    8
    address
    varchar
    255
    地址


    9
    photo
    varchar
    255
    照片



    teacher(教师表)
    教师表主要存取教师的教师号、姓名、性别、学院、职位、学位、照片表结构如表4-2所示:



    序号
    字段名
    类型
    长度(字符)
    描述




    1
    teacherId
    int
    11
    教师编号


    2
    teacherName
    varchar
    255
    教师姓名


    3
    sex
    varchar
    255
    性别


    4
    college
    varchar
    255
    学院


    5
    professionalTitle
    varchar
    255
    职称


    6
    degree
    varchar
    255
    学位


    7
    photo
    varchar
    255
    照片



    class(班级表)
    班级表主要存取班级号、班级名称、学院,表结构如表4-3所示:



    序号
    字段名
    类型
    长度(字符)
    描述




    1
    classId
    int
    11
    班级编号


    2
    className
    varchar
    255
    班级名称


    3
    ofCollege
    varchar
    255
    学院



    score(成绩表)
    成绩表主要存取学生的学号、姓名、课程号、课程名、成绩。表结构如表4-4所示:



    序号
    字段名
    类型
    长度(字符)
    描述




    1
    studentId
    int
    11
    学生编号


    2
    courseId
    int
    11
    课程编号


    3
    studentName
    varchar
    255
    学生姓名


    4
    courseName
    varchar
    255
    课程名称


    5
    grade
    int
    5
    分数



    studentclass(学生\班级表)
    学生班级班级表主要存取学生学号、学生姓名、班级号、班级名,表结构如表4-5所示:



    序号
    字段名
    类型
    长度(字符)
    描述




    1
    studentId
    int
    20
    学生编号


    2
    studentName
    varchar
    255
    学生姓名


    3
    classId
    int
    11
    班级编号


    4
    className
    varchar
    255
    班级名称



    teacherclass(教师\班级表)
    教师班级表主要存取教师号、教师姓名、班级号、班级名称,表结构如表4-6所示:



    序号
    字段名
    类型
    长度(字符)
    描述




    1
    teacherId
    int
    11
    教师编号


    2
    teacherName
    varchar
    255
    教师姓名


    3
    classId
    int
    11
    班级编号


    4
    className
    varchar
    255
    班级名称



    teacher_course(教师课程表)
    教师课程表主要存取教师号、教师名称、课程号、课程名,结构如表4-7所示:



    序号
    字段名
    类型
    长度(字符)
    描述




    1
    teacherId
    int
    11
    教师编号


    2
    teacherName
    varchar
    255
    教师姓名


    3
    courseId
    int
    11
    课程编号


    4
    courseName
    varchar
    255
    课程名称



    course_arrange(课程安排表)
    课程安排表是存取课程安排信息。主要存取教师号、教师名、课程号、课程名、班级号、班级名。结构如表4-8所示:



    序号
    字段名
    类型
    长度(字符)
    描述




    1
    classId
    int
    11
    班级编号


    2
    className
    varchar
    255
    班级名称


    3
    courseId
    int
    11
    课程编号


    4
    courseName
    varchar
    255
    课程名称


    5
    teacherId
    int
    11
    教师编号


    6
    teacherName
    varchar
    255
    教师姓名



    course(课程表)
    课程表存取的是课程信息,包括课程号、课程名、学分,结构如表4-9所示:



    序号
    字段名
    类型
    长度(字符)
    描述




    1
    courseId
    int
    11
    课程编号


    2
    courseName
    varchar
    255
    课程名称


    3
    credit
    float
    10
    课程学分



    user(系统用户表)
    系统用户表存取的是该系统的用户的账号密码信息,结构如表4-10所示:



    序号
    字段名
    类型
    长度(字符)
    描述




    1
    username
    varchar
    255
    用户名


    2
    password
    varchar
    255
    密码


    3
    superuser
    varchar
    255
    超级用户



    4.4.3 ER图(实体-联系图)系统整体的实体关系图

    班级信息实体E-R图

    学生信息实体E-R图

    教师信息实体E-R图

    课程信息实体E-R图

    成绩信息实体E-R图

    课程安排信息实体E-R图

    学生班级信息实体E-R图

    教师班级信息实体E-R图

    教师课程实体E-R图

    系统用户信息实体E-R图

    4.5 系统活动图活动图(activity diagram,动态图)是阐明了业务用例实现的工作流程。业务工作流程说明了业务为向所服务的业务主角提供其所需的价值而必须完成的工作。业务用例由一系列活动组成,它们共同为业务主角生成某些工件。工作流程通常包括一个基本工作流程和一个或多个备选工作流程。工作流程的结构使用活动图来进行说明。
    4.5.1 学生活动图
    4.5.2 教师活动图
    4.5.3 管理员活动图
    5 详细设计与实现完成系统分析与概要设计后,系统的主要功能已经确定,最后通过编写代码来实现系统的设计。本章主要介绍学生成绩管理系统的实现,从而对系统有了视觉上的了解。
    5.1 代码约定5.1.1 前端代码约定
    使用正确的文档类型:始终在文档的首行声明文档类型:<!DOCTYPE html>
    使用小写元素名:混合大小写名称容易混乱;
    关闭所有 HTML 元素,空的也要关闭
    使用小写属性名
    属性值加引号(如果属性值包含值,则必须使用引号)
    始终对图像使用alt属性
    始终定义图像尺寸,浏览器会在图像加载之前为图像预留空间,可减少闪烁,
    避免长代码行,关键代码处添加注释
    用简单的语法链接样式表与JavaScript,尽量不要写在一个文件里
    文件命名使用小写,避免大小写混用

    5.1.2 后端代码约定
    文件编码:源文件编码格式为UTF-8。
    包名使用小写字母
    import不要使用通配符。即,不要出现类似这样的import语句:import java.util.*
    列长限制:一个项目可以选择一行120个字符的列限制,超出列长限制时换行,换行时缩进至少4个空格,缩进不要用tab
    注释:注释应少而精,代码的关键处应该有注释, 注释不能误导读者
    变量声明:每次只声明一个变量,不要使用组合声明,比如int a, b;需要变量时才声明,并尽快进行初始化
    命名约定:命名应该见名知意、简洁,避免拼音与英文混用
    命名风格:类名以UpperCamelCase风格编写;非常量字段名、方法名、参数名、局部变量名以lowerCamelCase风格编写;常量名以CONSTANT_CASE风格编写
    类成员顺序:每个类应该以某种逻辑去排序它的成员,维护者应该要能解释这种排序逻辑。比如,新的方法不能总是习惯性地添加到类的结尾。


    使用log而不是System.out.println()
    大括号与if, else, for, do, while语句一起使用,即使只有一条语句(或是空),也应该把大括号写上
    减少代码嵌套: (1)合并条件;(2)利用return以省略后面的else;(3)使用子方法
    异常处理:捕获的异常不能忽视,典型的响应方式是打印日志。

    5.2 登录模块5.2.1 登录模块功能和技术实现介绍该模块是进入系统的统一入口,用户需要输入用户名和密码信息,验证通过后才能进入主系统界面。在这里,系统可以根据用户的账号识别出用户的身份,进而导航到不同的主操作界面。
    技术:判断用户输入的信息与数据库中的用户信息进行对比,判断该用户是否输入正确的验证信息,如果身份信息正确,则根据其权限等级导航到对应的主界面。
    此外用到filter技术,防止通过在URL地址栏中直接输入统一资源定位符获取到敏感页面。
    5.2.2 截图展示
    5.2.3 关键代码//验证用户身份信息并匹配不同主页面public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username = request.getParameter("username"); String password = request.getParameter("password"); System.out.println(username); System.out.println(password); String str = loginService.login(username, password); HttpSession session = request.getSession(); session.setAttribute("username", username); session.setAttribute("superuser", str); if (str != null) { String s=null; switch (str){ case "1": s = "student"; break; case "2": s = "teacher"; break; case "3": s = "admin"; break; } String path ="/jsp/"+s+"/framework" + s + ".jsp"; request.getRequestDispatcher(request.getContextPath() +path).forward(request,response); } else { response.sendRedirect(request.getContextPath() + "/index.jsp"); }//过滤器:public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; HttpSession session = httpServletRequest.getSession(); if (session.getAttribute("username") == null || session.getAttribute("username").equals("")) { httpServletRequest.getRequestDispatcher("index.jsp").forward(servletRequest, servletResponse); }else { filterChain.doFilter(servletRequest, servletResponse); }}
    5.3 学生模块5.3.1 查询和修改个人资料功能介绍
    学生可以点击个人资料查看自己的个人信息,同时可以更新自己的照片,同时还有动态时钟显示。点击不同的指针可以查看月份、星期、时、分、秒。
    截图展示

    关键代码
    //图片上传(图片上传是上传到服务器,然后将图片的相对路径存到数据库)protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { if (!ServletFileUpload.isMultipartContent(req)) { PrintWriter writer = resp.getWriter(); writer.println("Error: 表单必须包含 enctype=multipart/form-data"); writer.flush(); return; } // 配置上传参数 DiskFileItemFactory factory = new DiskFileItemFactory(); // 设置内存临界值 - 超过后将产生临时文件并存储于临时目录中 factory.setSizeThreshold(MEMORY_THRESHOLD); // 设置临时存储目录 factory.setRepository(new File(System.getProperty("java.io.tmpdir"))); ServletFileUpload upload = new ServletFileUpload(factory); // 设置最大文件上传值 upload.setFileSizeMax(MAX_FILE_SIZE); // 设置最大请求值 (包含文件和表单数据) upload.setSizeMax(MAX_REQUEST_SIZE); // 中文处理 upload.setHeaderEncoding("UTF-8"); // 构造临时路径来存储上传的文件 // 这个路径相对当前应用的目录 String uploadPath = getServletContext().getRealPath("/") + File.separator + UPLOAD_DIRECTORY; // 如果目录不存在则创建 File uploadDir = new File(uploadPath); if (!uploadDir.exists()) { uploadDir.mkdir(); }//时钟显示(调用Echart图表,加载到该jsp文件中)部分代码:option3 = { tooltip: { // formatter: "{a}:{c}" backgroundColor: '#fff', borderColor: '#f60', borderWidth: '1px', textStyle: { color: '#333' }, formatter: function(param) { var time = Math.floor(param.value); if (param.seriesIndex === 0) { return '<em style="color:' + param.color + ';">当前小时:' + time + '</em>' } if (param.seriesIndex === 1) { return '<em style="color:' + param.color + ';">当前星期:' + time + '</em>' } if (param.seriesIndex === 2) { return '<em style="color:' + param.color + ';">当前月份:' + time + '</em>' }
    5.3.2 查询成绩功能介绍
    学生点击查询成绩可以看到自己每门课的成绩。
    截图

    关键代码
    从数据库中获取到成绩,在jsp页面中通过jstl中的遍历,将信息逐条显示。
    <% String username = (String) session.getAttribute("username"); List<Score> scoreList = (List) session.getAttribute("scoreList"); session.setAttribute("scoreList", scoreList);%><c:forEach var="sl" items="${scoreList}"> <tr> <td>${sl.studentId}</td> <td>${sl.courseId}</td> <td>${sl.studentName}</td> <td>${sl.courseName}</td> <td>${sl.grade}</td> </tr></c:forEach>
    5.4 教师模块5.4.1 查询和修改个人资料功能介绍
    教师可以查看个人信息,可以更新个人照片。
    功能截图

    5.4.2 录入学生成绩功能介绍
    教师可以通过两种方式录入学生成绩:手动一个一个输入和导入Excel成绩单。
    运行截图
    手动输入截图:

    Excel导入成绩结果:

    5.4.3 查询和修改成绩功能介绍
    教师可以输入课程名查询这门课的成绩,也可以输入学号查询某个学生的成绩,还可以直接修改成绩
    关键技术
    修改成绩时,用到了jQuery技术,遍历选中的某条记录,将其table标签变为input标签,并将原来的值保存并显示到input标签中。另外按钮的状态也在改变,有修改变为确定,点击“确定”,按钮重新变为“修改”。
    Ajax传值和json:将修改后的数据传到后端时,采用了ajax传值方式,将修改后的数据以json的形式传到后端,在后端通过解析,然后保存到数据库,将修改是否成功信息返回前端。
    运行截图



    5.4.4 统计分析学生成绩功能介绍
    教师在该功能中可以查看不同课程的各分数段人数以及该门课程的及格率。具体信息通过条形图和水球图展示。还可以同时分析多门课程的最高分、最低分、平均分等,可以查看表格数据、折线统计分析、条形堆叠统计分析、条形图统计分析等多种方式浏览分析结果,并且可以将分析结果下载为图片。
    在多门课成绩统一分析时,可以点击不同按钮,分别显示最高分、最低分、平均分等
    通过点击图表右侧的按钮,可以查看表格数据、折线统计分析、条形堆叠统计分析、条形图统计分析等多种方式浏览分析结果,并且可以将分析结果下载为图片。
    关键技术
    该功能用到了Echart技术,ajax技术,加载页面时通过ajax请求学生的成绩信息,将获取的值,赋给有JavaScript写好变量中。
    运行截图







    关键代码
    //分数统计passrate=(float) (num2+num3+num4)/(num1+num2+num3+num4);System.out.println("及格率为" + passrate);list.add(new ScorePeople("不及格", num1, passrate));list.add(new ScorePeople("60-69", num2));list.add(new ScorePeople("70-89", num3));list.add(new ScorePeople("90-100", num4));ObjectMapper mapper = new ObjectMapper();//提供java-json相互装换功能//综合分析var posList = [ 'left', 'right', 'top', 'bottom', 'inside', 'insideTop', 'insideLeft', 'insideRight', 'insideBottom', 'insideTopLeft', 'insideTopRight', 'insideBottomLeft', 'insideBottomRight'];
    5.5 管理员模块主要有四大功能木块:学生信息管理、教师信息管理、基本信息管理(班级、课程、课程安排)、系统用户信息管理
    5.5.1 学生信息管理功能介绍
    管理员可以查看、添加和删除学生信息,有两种添加方式:添加单个学生和批量导入学生信息。
    关键技术
    多级联动——在单个添加学生信息使,在选择学院、专业和班级时,使用了多级联动技术,即选择某个学院、专业的选项显示不同。班级也一样,比如选择了计算机科学学院,那么专业选项中只包含“计算机科学与技术(师范),计算机科学与技术(创新实验班),软件工程,信息系统与信息管理”4个选项。批量上传——管理员可以通过导入Excel文件,批量导入学生信息。
    运行截图

    关键代码
    //读取Excel文件导入学生信息public class ReadStudentsExcel { public List readStudentExcel(File file) { List<Student> studentList = new ArrayList<Student>(); try { InputStream inputStream = new FileInputStream(file.getAbsolutePath()); Workbook workbook = Workbook.getWorkbook(inputStream); int sheet_size = workbook.getNumberOfSheets(); List<String> eachStudent = new ArrayList<>(); Student student = null; for (int index = 0; index < sheet_size; index++) { Sheet sheet = workbook.getSheet(index); for (int i =1 ; i < sheet.getRows(); i++) { for (int j = 0; j < sheet.getColumns(); j++) { String cellinfo = sheet.getCell(j, i).getContents(); System.out.print(cellinfo+'\t'); eachStudent.add(j,cellinfo); }
    5.5.2 教师信息管理功能介绍
    管理员可以查看、添加和删除教师信息。
    运行截图

    5.5.3 基本信息管理班级信息管理
    功能介绍:查询、添加、删除班级
    运行截图:

    课程信息管理
    功能介绍:查询、添加、删除课程
    运行截图:

    安排课程
    功能介绍:管理员指定班级、课程、教师来安排课程
    运行截图:

    5.5.4 管理系统用户功能介绍:添加、删除系统用户。
    运行截图:
    3 评论 80 下载 2019-04-10 18:44:50 下载需要16点积分
  • 基于Android平台的个人理财软件的设计与实现

    摘要个人理财管理系统是基于Android系统开发的一款手机应用程序。它主要是为了满足人们在快节奏的生活中可以随时记下自己的收支情况的需求。个人理财管理系统与传统的记账方式相比,体现了它的便捷性、安全性及可扩展性。系统采用Eclipse+ Android Developer Tools作为开发工具,以SQLite为数据库。系统功能包括:用户账户的注册、用户切换、用户名密码修改、账户删除,语音识别记账,收入信息的增删改,支出信息的增删改,收入类型的增删,支出类型的增删,收入信息分类统计,支出信息分类统计,收入支出总额统计,数据还原、清空等等。系统具有界面简洁鲜明、功能便捷易用、操作人性化等特征。
    关键字:收支管理;Android;SQLite
    AbstractPersonal financial management system is based on the Android system developed a mobile application.It is mainly to meet those people who want to write down their income and expenditure more quickly and conveniently in the fast-paced life. Compared with the traditional method, Personal financial management system reflects more convenience,security and scalability. Especially in this day and age, people are on the side of the phone. System uses Eclipse and Android Developer Tools as a development tool and SQLite as the database. Android technology is now very mature, we can easily develop.Personal Financial Management System of the main functional modules,including: user account registration, user switching, username password changes, account deletion, Income information management,Expenditure information management,voice recognition accounting,Type of income management, Expenditure Type Manager, Income information classification statistics, expenditure information classification statistics, total income and expenditure statistics,Data reduction, data emptied and so on. The system has a simple and clear interface, easy-to-use features, user-friendly operation and other features.
    Keywords: Revenue and expenditure management; Android;SQLite
    1 绪论1.1 选题背景出门口诀“伸手要赏钱”分别代表着身份证、手机、钥匙、伞、钱。在后PC时代的今天,手机成为最重要的移动终端,是我们出门出行的必携物。以前,或许我们的手机只能为我们提供基本通信功能,而随着Android系统的诞生,我们的生活迎来了一场不亚于“工业革命”带给我们的巨大变化。基于Android系统的手机处理各类信息的能力得到了质的提升。而Android这个开源的操作系统,将享受这项优质服务的权利给了所有愿意使用它的人。?Android从08年9月的1.1版到去年的L版,一路走来,从基本走向优化,全方面地为我们提供了出色的网络、多媒体、通讯、娱乐等功能。这匹黑马,从诞生伊始到去年7月的“全球市场占有率达81.9%”,告诉我们它的出现是多么受人们欢迎。它备受追捧的原因不外乎它完全的对第三方软件开发商和我们这些开发者的开发。我们可以在它上面实现无限自主的“自定义”。它宛如一张白纸,我们可以在上面随意画出自己想要的东西。它,贵在给予了我们自由。
    1.2 课题研究的目的和意义随着高速经济化,我们的生活节奏越来越快。我们忙东忙西,总会容易忽略忘记了一些生活细节,比如收支管理。为了更好的释放一些时间来享受我们的生活,我们期待有这么一款软件来帮助管理这些小数据。建立在Android操作系统上的个人理财系统,方便我们随时随地地记录着这些零散的数据,从此我们不必再为收支费心,清心地查看数据统计结果是我们唯一要做的事。
    1.3 国内外现状和发展趋势安卓在手机上的应用使得手机的功能有了很大改善,这使得越来越多的入主要依靠手机查询大量信息,而用户们不断提高的需求也决定了越来越多的基于安卓平台的应用软件及系统的产生。
    若是基于安卓平台的个人理财系统得到广泛推广,人们能从该应用显而易见的了解到个人的财务状况,明确的使用自身钱财,了解到日常中支出比例,调整支出,正确理财。

    开放性手机平台:android是Google开发的基于Linux平台的开源手机操作系统。Google通过与运营商、设备制造商、手机公司和其他有关各方结成深层次的合作伙伴关系,希望借助建立标准化、开放式的智能手机操作系统,在移动产业内形成一个开放式的生态系统
    网络集成性很高:涵盖了生活中各个方面的网络应用,对长期使用网络、信息依赖度比较高的人群很合适
    Android具备创新性自从:Google开发出Android后,许多人认为其技术可信度要比其它操作系统略胜一筹,但这并不是用户购买Android智能手机的唯一原因。人们认为Android是一种相对较新的、又较为成熟的技术,在达到巅峰之前还有很大发展空间
    Android平台在数量上逐渐主宰市场:市场分析机构NPD发布的数据显示,2014年4-6月份发售的智能手机中,33%为Android手机,而RIM手机发售比例为28%,iPhone为22%
    Android在其它领域的拓展:android不仅促进了手机产业的发展,它的全面计算服务和丰富的功能支持,已将应用拓展到手机以外的其他领域。Android平台的通用性可以适用于不同的屏幕、有线和无线设备。Android的系统和应用程序开发人员将更多的涉足多媒体、移动互联网设备、数字视频和家庭娱乐设备、汽车、医药、网络、监测仪器和工业管理、机顶盒等新领域

    2 设计开发所用到的工具和技术2.1系统开发工具个人财务管理系统的开发及运行环境如下所述:

    操作系统:Windows7
    JDK环境:Java Development Kit version=1.7.0_45
    开发工具

    Eclipse version=4.2.0Android Software Development Kit version=4.4.2Android Developer Tools Build: v22.3.0-887826
    开发语言:Java、XML
    数据库软件:SQLite
    运行平台:Windows
    虚拟机:720P(1080x720)

    2.2 Android的介绍Android是一种基于Linux的自由及开放源代码的操作系统,主要使用于移动设备,如智能手机和平板电脑,由Google公司和开放手机联盟领导及开发。尚未有统一中文名称,中国大陆地区较多人使用“安卓”或“安致”。
    Android操作系统最初由Andy Rubin开发,主要支持手机。2005年8月由Google收购注资。2007年11月,Google与84家硬件制造商、软件开发商及电信营运商组建开放手机联盟共同研发改良Android系统。
    该平台由操作系统、中间件、用户界面和应用软件组成。它采用软件堆层(Software Stack,又名软件叠层)的架构,主要分为三部分。底层以Linux内核工作为基础,由C语言开发,只提供基本功能;中间层包括函数库Library和虚拟机Virtual Machine,由C++开发。最上层是各种应用软件,包括通话程序,短信程序等,应用软件则由各公司自行开发,以Java作为编写程序的一部分。不存在任何以往阻碍移动产业创新的专有权障碍,号称是首个为移动终端打造的真正开放和完整的移动软件。
    android分为四个层,从高层到低层分别是应用程序层、应用程序框架层、系统运行库层和linux核心层。
    蓝色的代表java程序,黄色的代码为运行JAVA程序而实现的虚拟机,绿色部分为C/C++语言编写的程序库,红色的代码内核(linux内核+driver)。在Application Framework之下,由C/C++的程序库组成,通过JNI完成从JAVA到C的调用。

    2.3 Eclipse的介绍eclipse-galileoEclipse 是一个开放源代码的、基于 Java 的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。幸运的是,Eclipse 附带了一个标准的插件集,包括 Java 开发工具(Java Development Tools,JDT)。
    Eclipse最初是由IBM公司开发的替代商业软件Visual Age for Java的下一代IDE开发环境,2001年11月贡献给开源社区,现在它由非营利软件供应商联盟Eclipse基金会(Eclipse Foundation)管理。 2003年,Eclipse 3.0选择OSGi服务平台规范为运行时架构。 2007年6月,稳定版3.3发布。2008年6月发布代号为Ganymede的3.4版。2009年7月发布代号为GALILEO的3.5版。
    Eclipse是著名的跨平台的自由集成开发环境(IDE)。最初主要用来Java语言开发,但是目前亦有人通过插件使其作为其他计算机语言比如C++和Python的开发工具。Eclipse的本身只是一个框架平台,但是众多插件的支持使得Eclipse拥有其他功能相对固定的IDE软件很难具有的灵活性。许多软件开发商以Eclipse为框架开发自己的IDE。
    Eclipse 最初由OTI和IBM两家公司的IDE产品开发组创建,起始于1999年4月。IBM提供了最初的Eclipse代码基础,包括Platform、JDT 和PDE。目前由IBM牵头,围绕着Eclipse项目已经发展成为了一个庞大的Eclipse联盟,有150多家软件公司参与到Eclipse项目中,其中包括Borland、Rational Software、Red Hat及Sybase等。Eclipse是一个开发源码项目,它其实是 Visual Age for Java的替代品,其界面跟先前的Visual Age for Java差不多,但由于其开放源码,任何人都可以免费得到,并可以在此基础上开发各自的插件,因此越来越受人们关注。近期还有包括Oracle在内的许多大公司也纷纷加入了该项目,并宣称Eclipse将来能成为可进行任何语言开发的IDE集大成者,使用者只需下载各种语言的插件即可。
    2.4 SQLite的介绍SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它包含在一个相对小的C库中。它是D.RichardHipp建立的公有领域项目。它的设计目标是嵌入式的,而且目前已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了。它能够支持Windows/Linux/Unix等等主流的操作系统,同时能够跟很多程序语言相结合,比如 Tcl、C#、PHP、Java等,还有ODBC接口,同样比起Mysql、PostgreSQL这两款开源的世界著名数据库管理系统来讲,它的处理速度比他们都快。SQLite第一个Alpha版本诞生于2000年5月。 至2015年已经有15个年头,SQLite也迎来了一个版本 SQLite 3已经发布。
    3 系统需求分析3.1 需求分析生活中记录日常花销、收入总是琐碎累心的。特别是纸质记录,除了携带不方便外,还很容易丢失损毁。
    这个时候,若是我们身边常带的手机上有这么一款可以随时记录收入和支出的软件,因为在记录收支的同时,会有一些注意事项需要标注,比如欠钱还钱注意事项。在对各项数据进行记录后,用户会希望看到对各类数据的统计,所以需要满足基本需求的统计模块。为了保证数据安全,增设账户模块。为了区分各用户的操作习惯,所以增设收入支出类型管理模块和系统设置模块。
    3.2 可行性分析3.2.1 系统可行性可行性分析实在目前市场己有的类似系统调查的基础上,辩证新系统的研发是否具备开发必要性和可能性,对新系统的研发从技术、经济、社会因素等多个方面进行相关的分析和研究,以避免造成不必要的投资失误,保证和提高新系统开发成功的把握。可行性研究的目的就是以最小的时间、金钱代价确定疑难问题是否能够妥善解决。
    3.2.2 技术可行性此系统需要在Android手机操作系统上运行,用Eclipse进行开发,数据库我选用轻量级的SqLite。开发所需的软件技术成熟稳定,且支持Android系统的手机也分布广泛,可以完全满足所以的开发需求。
    至于自己在Android方面虽然没有基础,鉴于之前有Java编程经验且时间充裕,有足够时间来学习空白的知识。
    3.2.3 经济可行性从市场经济来看,近年来,国人生活品质提升,逐渐重视生活品质,手机应用便成了人们生活的一部分。当今社会己步入了一个全新的信息时代,人类的每个活动都和“信息”紧密的联系在一起,小至个人的衣食住行,大及国家大事新闻发布,都依与信息的传播与发布,而社会中最活跃的,在市场经济高速发展的现在,手机应用普遍化,生活化,低端化成了不可阻挡的趋势。
    从技术经济来看,由于SQLite数据库和Android SDK都是开源的免费的开发学习工具,而且本系统使用灵活方便,技术也不是很复杂,开发周期较短,因此开发成本较低。市场前景非常看好,所以说技术经济方面来看,本项目也是可行的。由此可以看出本系统开发所产生的效益将大于投入,所以开发本项目是可行的、必要的。
    综上所述,个人理财系统充分利用了软硬件资源,技术成熟,成本低廉,操作简单,管理方便,使理财记账摆脱空间的限制,实现自动化处理和信息化管理,因此,本系统的实施是可行的。
    3.3 系统功能模块
    用户管理:可以设置当前用户
    类别维护:用户可以添加日常收入、日常支出的类别,并且可以删除相应的类别,填写类别详细
    日常收入:用户可以按照日常收入日期、金额、类别、备注进行数据的增添
    日常支出:用户可以按照日常支出日期、金额、类别、备注进行数据的增添
    收入支出统计:按照一定的数据查询条件,用户可以对数据进行统计


    4 系统总体设计4.1 系统总体设计分析系统设计是系统开发过程中的核心,从需求出发,总体上描述系统架构应该包含的组成要素。系统总体设计尽可能模块化,描述了各个模块之间的关联。模块化是一种很重要的设计思想,把一个复杂的系统分解为一些规模较小、功能简单的、更易于建立和修改的部分。一方面,各个模块具有相对独立性,可以分别加以设计实现;另一方面,模块之间的相互关系则通过一定的方式予以说明。各模块在这些关系的约束下共同构成一个统一的整体,完成系统的功能。
    总体设计的核心内容就是依据需求分析定义的功能,合理、有效地实现系统中定义的各种需求,包括模块设计、数据库设计等。
    4.2 系统流程图根据系统分析以及功能需求,系统的基本流程可以描述为:主界面→选择各子功能模块,如下图所示:

    4.3 系统特点
    目的明确:理财就是以管钱为中心,通过抓好赚钱、生钱、护钱,三个环节,管好自己手中的现金流动,让资产在保值的基础上,实现稳定持续的增长
    功能齐全:系统覆盖了理财所需要的功能,收支,统计
    适应性强:系统采用基于模型的设计思,用户的特点抽象出管理模型,根据模型进行系统设计,使系统具有很好的开放性的拓展性,能够高效率地适应各用户群体的需求

    4.4 数据库设计4.4.1 tb_account(账户表)tb_account用于管理系统各个用户信息。_id为用户的唯一标识,为表的主键,也为其他表的_id相对应。Username和pwd分别代表着用户名和密码。这两个是用户后期可以修改的。
    账户表中存在着一个特殊的用户:默认用户。它的用户名密码用户不可见。用户在没有登陆的情况下,数据保存在这个用户下方。



    字段名
    数据类型
    是否主键
    描述




    _id
    Integer
    Y
    用户id


    USERNAME
    VARCHAR(20)
    N
    用户名


    PWD
    VARCHAR(50)
    N
    密码



    4.4.2 tb_income(收入信息表)TYPE_ID与tb_itype表的type_id对应。No为收入信息的编号,不同用户的收入信息存入数据库的时候,都是以no=1为起始的,进而往后递增。



    字段名
    数据类型
    是否主键
    描述




    _id
    INTEGER
    N
    用户id


    NO
    INTEGER
    Y
    编号


    MONEY
    DECIMAL
    N
    收入金额


    TIME
    DATE
    N
    收入时间


    TYPE_ID
    INTEGER
    N
    收入类别


    HANDLER
    VARCHAR(100)
    N
    放款方


    MARK
    VARCHAR(200)
    N
    备注


    PHOTO
    VARCHAR(200)
    N
    照片


    KIND
    VARCHAR(10)
    N
    类别



    4.4.3 tb_pay(支出信息表)TYPE_ID与tb_ptype表的type_id对应。No为收入信息的编号,不同用户的支出信息存入数据库的时候,都是以no=1为起始的,进而往后递增。



    字段名
    数据类型
    是否主键
    描述




    _id
    INTEGER
    N
    用户id


    NO
    INTEGER
    Y
    自增


    MONEY
    DECIMAL
    N
    支出金额


    TIME
    DATE
    N
    支出时间


    TYPE_ID
    INTEGER
    N
    支出类别


    ADDRESS
    VARCHAR(100)
    N
    消费地点


    MARK
    VARCHAR(200)
    N
    备注


    PHOTO
    VARCHAR(200)
    N
    照片


    KIND
    VARCHAR(10)
    N
    类别



    4.4.4 tb_ptype(支出类型表)tb_ ptype为支出类型表。由于每个用户都有对应的用户习惯,在添加支出信息时,收入类型的种类、使用频率都会有所不同。在此设计支出类型表,可以为用户提供修改支出类型的服务,根据自己的使用频率修改支出类型。



    字段名
    数据类型
    是否主键
    可否为空
    描述




    _id
    INTEGER
    N
    N
    用户id


    no
    INTEGER
    Y
    N
    类型编号


    type_id
    INTEGER
    N
    N
    类型id



    4.4.5 tb_itype(收入类型表)tb_itype为收入类型表。由于每个用户都有对应的用户习惯,在添加收入信息时,收入类型的种类、使用频率都会有所不同。在此设计收入类型表,可以为用户提供修改收入类型的服务,根据自己的使用频率修改收入类型。



    字段名
    数据类型
    是否主键
    可否为空
    描述




    _id
    INTEGER
    N
    N
    用户id


    no
    INTEGER
    Y
    N
    类型编号


    type_id
    INTEGER
    N
    N
    类型id



    5 系统详细设计与实现5.1 主界面设计快速记个人记账软件主界面,有4个Fragment页面。

    主界面使用4个Fragment和PopupWindow生成,部分代码:
    /** * 显示PopupWindow弹出菜单 */ private void showPopupWindow(View parent) { DisplayMetrics dm = parent.getResources().getDisplayMetrics(); int w_screen = dm.widthPixels; int h_screen = dm.heightPixels; // System.out.println("你的设备w_screen:" + w_screen + " h_screen:" + // h_screen); if (popWindow == null) { LayoutInflater layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); View view = layoutInflater.inflate(R.layout.popwindow_layout, null); popWinLayout = (LinearLayout) view.findViewById(R.id.popwindow); // 创建一个PopuWidow对象 float radiowith = w_screen / 480.0f; float radioheight = h_screen / 800.0f; popWindow = new PopupWindow(view, (int) (popWinLayout.getLayoutParams().width), h_screen / 4); } // 使其聚集 ,要想监听菜单里控件的事件就必须要调用此方法 popWindow.setFocusable(true); pop_voiceView = (LinearLayout) popWinLayout .findViewById(R.id.pop_voice); pop_quickView = (LinearLayout) popWinLayout .findViewById(R.id.pop_quick); pop_voiceView.setOnClickListener(this); pop_quickView.setOnClickListener(this); // 设置允许在外点击消失 popWindow.setOutsideTouchable(true); // 设置背景,这个是为了点击“返回Back”也能使其消失,并且并不会影响你的背景 popWindow.setBackgroundDrawable(new BitmapDrawable()); // 设置菜单显示的位置 int xPos = (w_screen - popWinLayout.getLayoutParams().width) / 2; popWindow.showAsDropDown(parent, xPos, 12); // popWindow.showAsDropDown(parent, Gravity.CENTER, 0); // 监听菜单的关闭事件 popWindow.setOnDismissListener(new OnDismissListener() { @Override public void onDismiss() { // 改变显示的按钮图片为正常状态 changeButtonImage(); } }); // 监听触屏事件 popWindow.setTouchInterceptor(new OnTouchListener() { public boolean onTouch(View view, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_OUTSIDE) { // 改变显示的按钮图片为正常状态 changeButtonImage(); } return false; } }); } /** * 点击了“明细”按钮 */ private void clickFriendfeedBtn() { // 实例化Fragment页面 fragmentPage1 = new FragmentPage1(); // 得到Fragment事务管理器 FragmentTransaction fragmentTransaction = this .getSupportFragmentManager().beginTransaction(); // 替换当前的页面 fragmentTransaction.replace(R.id.frame_content, fragmentPage1); // 事务管理提交 fragmentTransaction.commit(); friendfeedFl.setSelected(true); friendfeedIv.setSelected(true); myfeedFl.setSelected(false); myfeedIv.setSelected(false); homeFl.setSelected(false); homeIv.setSelected(false); moreFl.setSelected(false); moreIv.setSelected(false); }
    此处省略类似的函数…
    private void clickPop_voiceBtn() { Intent intent = new Intent(MainActivity.this, AddPay.class);// 创建Intent对象 intent.putExtra("cwp.id", userid); intent.putExtra("cwp.voice", "");// 设置传递数据 startActivity(intent); } private void clickPop_quickBtn() { Intent intent = new Intent(MainActivity.this, AddPay.class);// 创建Intent对象 intent.putExtra("cwp.id", userid); startActivity(intent); } /** * 点击了中间按钮 */ private void clickToggleBtn() { showPopupWindow(plusImageView); // 改变按钮显示的图片为按下时的状态 plusImageView.setImageResource(R.drawable.toolbar_plusback); toggleImageView.setImageResource(R.drawable.toolbar_btn_pressed); } /** * 改变显示的按钮图片为正常状态 */ private void changeButtonImage() { plusImageView.setImageResource(R.drawable.toolbar_plus); toggleImageView.setImageResource(R.drawable.toolbar_btn_normal); }
    5.2 收入/支出插入数据界面添加软键盘,来添加收入/支出消费记录

    添加收/支部分代码,需要判断是添加模式还是修改模式,是添加收入还是添加支出:
    btnSaveButton.setOnClickListener(new OnClickListener() {// 为保存按钮设置监听事件 @SuppressLint("NewApi") @Override public void onClick(View arg0) { if (typemode == "add") { // 添加模式 String strMoney = txtMoney.getText().toString();// 获取金额文本框的值 if (type == "pay") { // 支出 if (!strMoney.isEmpty()) {// 判断金额不为空 // 创建InaccountDAO对象 PayDAO payDAO = new PayDAO(AddPay.this); // 创建Tb_inaccount对象 Tb_pay tb_pay = new Tb_pay( userid, payDAO.getMaxNo(userid) + 1, get2Double(strMoney), setTimeFormat(null), (spType.getSelectedItemPosition() + 1), txtAddress.getText().toString(), txtMark.getText().toString()); payDAO.add(tb_pay);// 添加收入信息 Toast.makeText(AddPay.this, "〖新增收入〗数据添加成功!", Toast.LENGTH_SHORT) .show(); gotoback(); } else { Toast.makeText(AddPay.this, "请输入收入金额!", Toast.LENGTH_SHORT).show(); } } else { // 收入 if (!strMoney.isEmpty()) {// 判断金额不为空 // 创建InaccountDAO对象 IncomeDAO incomeDAO = new IncomeDAO( AddPay.this); // 创建Tb_inaccount对象 Tb_income tb_income = new Tb_income( userid, incomeDAO.getMaxNo(userid) + 1, get2Double(strMoney), setTimeFormat(null), (spType.getSelectedItemPosition() + 1), txtInhandler.getText().toString(), txtMark.getText().toString()); System.out.println("money" + get2Double(strMoney)); incomeDAO.add(tb_income);// 添加收入信息 // 弹出信息提示 Toast.makeText(AddPay.this, "〖新增收入〗数据添加成功!", Toast.LENGTH_SHORT) .show(); gotoback(); } else { Toast.makeText(AddPay.this, "请输入收入金额!", Toast.LENGTH_SHORT).show(); } } } else { // 修改模式 if (type == "pay") { // 支出 if (!txtMoney.getText().toString().isEmpty()) {// 判断金额不为空 Tb_pay tb_pay = new Tb_pay(); // 创建Tb_pay对象 tb_pay.set_id(userid); // 设置userid tb_pay.setNo(Integer.parseInt(strno)); // 设置编号 tb_pay.setMoney(get2Double(txtMoney .getText().toString()));// 设置金额 tb_pay.setTime(setTimeFormat(txtTime .getText().toString()));// 设置时间 tb_pay.setType(spType .getSelectedItemPosition() + 1);// 设置类别 tb_pay.setAddress(txtAddress.getText() .toString());// 设置地点 tb_pay.setMark(txtMark.getText().toString());// 设置备注 payDAO.update(tb_pay);// 更新支出信息 Toast.makeText(AddPay.this, "〖数据〗修改成功!", Toast.LENGTH_SHORT).show(); gotoback(); } else { Toast.makeText(AddPay.this, "请输入收入金额!", Toast.LENGTH_SHORT).show(); } } else { // 收入 if (!txtMoney.getText().toString().isEmpty()) {// 判断金额不为空 Tb_income tb_income = new Tb_income();// 创建Tb_income对象 tb_income.set_id(userid);// 设置编号 tb_income.setNo(Integer.parseInt(strno));// 设置编号 tb_income.setMoney(get2Double(txtMoney .getText().toString()));// 设置金额 tb_income.setTime(setTimeFormat(txtTime .getText().toString()));// 设置时间 tb_income.setType(spType .getSelectedItemPosition() + 1);// 设置类别 tb_income.setHandler(txtInhandler.getText() .toString());// 设置付款方 tb_income.setMark(txtMark.getText() .toString());// 设置备注 incomeDAO.update(tb_income);// 更新收入信息 Toast.makeText(AddPay.this, "〖数据〗修改成功!", Toast.LENGTH_SHORT).show(); gotoback(); } else { Toast.makeText(AddPay.this, "请输入收入金额!", Toast.LENGTH_SHORT).show(); } } } } });
    软键盘部分代码:
    public void onKey(int primaryCode, int[] keyCodes) { Editable editable = ed.getText(); if (typemode.equals("ModifyInP")) { // 添加模式获取开始光标 ed.setSelection(editable.length()); } int start = ed.getSelectionStart(); if (primaryCode == Keyboard.KEYCODE_DELETE) { // 删除键 if (editable != null && editable.length() > 0) { if (start > 0) { editable.delete(start - 1, start); if (ed.getText().toString().indexOf(".") < 0) { a = true; } } } } else if (primaryCode == -7) { //隐藏键盘 hideKeyboard(); } else if (primaryCode == -8) { //小数点 if (start > 0 && a) { editable.insert(start, "."); a = false; } } else if (primaryCode == -9) { //语音识别 hideKeyboard(); dialogShowUtil.dialogShow("rotatebottom", "first", "", ""); } else { editable.insert(start, Character.toString((char) primaryCode)); } } }; public void showKeyboard() { //显示键盘 int visibility = keyboardView.getVisibility(); if (visibility == View.GONE || visibility == View.INVISIBLE) { keyboardView.setVisibility(View.VISIBLE); } } public void hideKeyboard() { //隐藏键盘 int visibility = keyboardView.getVisibility(); if (visibility == View.VISIBLE) { keyboardView.setVisibility(View.INVISIBLE); } }
    5.3 语音记账界面语音记账使用了百度语音识别api,通过响应用户的触发,调用api动态生成百度自定义的dialog来进行用户语音录音。当用户录入语音后,返回语音识别的数据,然后转为字符串并进行分析判断。目前主要以金额和类别来作为关键字来进行判断,首先通过将识别字符串通过跟收入/支出类别进行对比,如果存在相关类别即标记下当前录入的类别;如果没有当前这个类别,将会弹出自定义Dialog让用户去选择即使没有匹配的类别,是否依然要录入,如果“是”,该笔类型就会默认为“语音识别”类别,并会让用户去选择当前记录是“支出”还是“收入”,再去结合当前的金额录入数据;如果用户录入的类别同时存在于“收入”和“支出”之中,那么就会让用户去选择该笔记录是“收入”还是“支出”,在去结合当前的金额录入数据。另一方面,将识别字符串与自定义的列如“一”,“二”,“元”,“钱”…去对比,然后标记下这个金额的起始和结束位置,然后截取出来,并调用工具类把这个汉字的金额转变为阿拉伯数字。最后使用方法去判断当前是“添加模式”还是“修改模式”,然后再判断是“支出”还是“收入”,然后录入数据库。














    百度识别回调部分代码:
    mRecognitionListener = new DialogRecognitionListener() { // 百度识别返回数据 @Override public void onResults(Bundle results) { ArrayList<String> rs = results != null ? results .getStringArrayList(RESULTS_RECOGNITION) : null; if (rs != null && rs.size() > 0) { Recognition(rs.get(0)); // Toast.makeText(AddPay.this, rs.get(0), // Toast.LENGTH_SHORT).show(); } }};void VoiceRecognition() { // 百度语音识别// mResult.setText(null);mCurrentTheme = Config.DIALOG_THEME;if (mDialog != null) { mDialog.dismiss();}Bundle params = new Bundle();params.putString(BaiduASRDigitalDialog.PARAM_API_KEY, Constants.API_KEY); //百度语音api_keyparams.putString(BaiduASRDigitalDialog.PARAM_SECRET_KEY, Constants.SECRET_KEY);params.putInt(BaiduASRDigitalDialog.PARAM_DIALOG_THEME, //百度语音主题 Config.DIALOG_THEME);mDialog = new BaiduASRDigitalDialog(this, params); mDialog.setDialogRecognitionListener(mRecognitionListener);mDialog.getParams().putInt(BaiduASRDigitalDialog.PARAM_PROP, //百度识别类别 Config.CURRENT_PROP);mDialog.getParams().putString(BaiduASRDigitalDialog.PARAM_LANGUAGE,//百度识别语言 Config.getCurrentLanguage());mDialog.getParams().putBoolean( //百度识别音效相关 BaiduASRDigitalDialog.PARAM_START_TONE_ENABLE, Config.PLAY_START_SOUND);mDialog.getParams().putBoolean( BaiduASRDigitalDialog.PARAM_END_TONE_ENABLE, Config.PLAY_END_SOUND);mDialog.getParams().putBoolean( BaiduASRDigitalDialog.PARAM_TIPS_TONE_ENABLE, Config.DIALOG_TIPS_SOUND);mDialog.show();}/* * 识别结果处理函数 * * @param VoiceSave[0] 收入类别的值 * * @param VoiceSave[1] 金额的值 * * @param VoiceSave[3] 重复类别的值,仅用于显示提醒 * * @param VoiceSave[4] 支出类别的值 * * @param VoiceSave[5] "语音识别"类别的值 */private void Recognition(String t) { int mfirst = 100, mend = 0, temp = 0; Boolean ismoney = false, intype = false, outtype = false; String w = "", strmoney = "", inname = "1", outname = "2"; spdatalist = ptypeDAO.getPtypeName(userid); spdatalist2 = itypeDAO.getItypeName(userid); VoiceSave[2] = t; for (int i = 0; i < spdatalist.size(); i++) { // 判断是否包含支出 if (t.indexOf(spdatalist.get(i).toString()) > -1) { type = "pay"; intype = true; inname = spdatalist.get(i).toString(); VoiceSave[0] = Integer.toString(i); // VoiceSave[0]为收入类别的值 } } for (int i = 0; i < spdatalist2.size(); i++) { // 判断是否包含收入 if (t.indexOf(spdatalist2.get(i).toString()) > -1) { type = "income"; outtype = true; outname = spdatalist2.get(i).toString(); VoiceSave[4] = Integer.toString(i); // VoiceSave[4]为支出类别的值 } } for (int i = 0; i < number.length; i++) { // 判断是否包含金额,获得开头 if (t.indexOf(number[i]) > -1) { temp = t.indexOf(number[i]); if (temp < mfirst) { mfirst = temp; } } } for (int i = 0; i < money.length; i++) { // 判断是否包含金额,获得结尾 if (t.indexOf(money[i]) > -1) { temp = t.indexOf(money[i]); if (temp > -1 && temp >= mend) { mend = temp; } } } for (int i = 0; i < money2.length; i++) { // 判断是否包含金额,获得结尾 if (t.indexOf(money2[i]) > -1) { temp = t.indexOf(money2[i]); if (temp > -1 && temp >= mend) { mend = temp; } mend = mend + 1; } } if (!(mfirst == 100 || mend == 0)) { // 转换为阿拉伯数字 ismoney = true; strmoney = t.substring(mfirst, mend); DigitUtil Util = new DigitUtil(); VoiceSave[1] = Integer.toString(Util.parse(strmoney)); // 调用工具类处理汉字的金额 } if (intype && outtype) { // 如果含金额 if (outname.equals(inname)) { if (ismoney) { VoiceSave[3] = outname; // VoiceSave[3]为重复类别的值,仅用于显示提醒 dialogShowUtil.dialogShow("shake", "judge", t, w); // 如果含有金额 } else { w = "提示:\n你的话中没有包含消费或开支的<金额>\n"; dialogShowUtil.dialogShow("shake", "wrong", t, w); } } else { w = "**提示:\n一次只能记录一条记录哦\n"; // 如果含有收入并且支出的类别 dialogShowUtil.dialogShow("shake", "wrong", t, w); } } else { if (!((intype || outtype) || ismoney)) { // 如果不含金额 w = "**提示:\n你的话中没有包含<类别>(" + listToString(spdatalist, ',') + "," + listToString(spdatalist2, ',') + ")\n\n**提示:\n你的话中没有包含消费或开支的<金额>"; dialogShowUtil.dialogShow("shake", "wrong", t, w); } else if ((intype || outtype) && (!ismoney)) { w = "提示:\n你的话中没有包含消费或开支的<金额>\n"; dialogShowUtil.dialogShow("shake", "wrong", t, w); } else if ((!(intype || outtype)) && ismoney) { for (int i = 0; i < spdatalist.size(); i++) { // 判断是否包含支出 if ("语音识别".indexOf(spdatalist.get(i).toString()) > -1) { VoiceSave[5] = Integer.toString(i); VoiceSave[3] = "语音识别"; } } w = "**提示:\n你的话中没有包含<(默认)类别>(" + listToString(spdatalist, ',') + ")\n\n\n将会记录为<语音识别>类别,是否依然记录?\n"; dialogShowUtil.dialogShow("shake", "notype", t, w); } else { dialogShowUtil.dialogShow("rotatebottom", "OK", t, w); } }}
    Dialog部分处理代码:
    public void dialogShow(String showtype, String style,final String context1, String context2) {dialogBuilder = new NiftyDialogBuilder(ctx, R.style.dialog_untran); // 自定义dialogBuilderswitch (showtype) {case "rotatebottom":effect = Effectstype.RotateBottom;break;case "shake":effect = Effectstype.Shake;break;}switch (style) {case "first":dialogBuilder.withTitle("语音记账") // .withTitle(null) no title .withTitleColor("#FFFFFF") // def .withDividerColor("#11000000") // def .withMessage("语音格式:\n早餐在餐厅食了20元。\n\n") // .withMessage(null) no Msg .withMessageColor("#FFFFFF") // def .withIcon(ctx.getResources().getDrawable(R.drawable.icon)) .isCancelableOnTouchOutside(false) // def |// // isCancelable(true) .withDuration(700) // def .withEffect(effect) // def Effectstype.Slidetop .withButton1Text("取消") // def gone .withButton2Text("开始语音") // def gone .setButton1Click(new View.OnClickListener() { @Override public void onClick(View v) { dialogBuilder.dismiss(); } }).setButton2Click(new View.OnClickListener() { @Override public void onClick(View v) { dialogBuilder.dismiss(); ((AddPay) act).VoiceRecognition(); } }).show();break;case "notype":dialogBuilder.withTitle("识别成功") // .withTitle(null) no title .withTitleColor("#FFFFFF") // def .withDividerColor("#11000000") // def .withMessage("你刚刚说了“ " + context1 + "”\n\n" + context2) // .withMessage(null) no Msg .withMessageColor("#FFFFFF") // def .withIcon(ctx.getResources().getDrawable(R.drawable.icon)) .isCancelableOnTouchOutside(false) // def |// .withDuration(700) // def .withEffect(effect) // def Effectstype.Slidetop .withButton1Text("取消") // def gone .withButton2Text("是") // def gone .setButton1Click(new View.OnClickListener() { @Override public void onClick(View v) { dialogBuilder.dismiss(); } }).setButton2Click(new View.OnClickListener() { @Override public void onClick(View v) { dialogBuilder.dismiss(); VoiceDefault = "notype"; VoiceSave[3]=VoiceSave[3]; dialogShow("shake", "judge", context1, ""); } }).show();break;case "wrong":dialogBuilder .withTitle("识别失败") // .withTitle(null) no title .withTitleColor("#FFFFFF") // def .withDividerColor("#11000000") // def .withMessage( "你刚刚说了“ " + context1 + "”不符合格式,请再试一次\n\n" + context2) // .withMessage(null) no Msg .withMessageColor("#FFFFFF") // def .withIcon(ctx.getResources().getDrawable(R.drawable.icon)) .isCancelableOnTouchOutside(false) // def | // isCancelable(true) .withDuration(700) // def .withEffect(effect) // def Effectstype.Slidetop .withButton1Text("取消") // def gone .withButton2Text("再次语音") // def gone .setButton1Click(new View.OnClickListener() { @Override public void onClick(View v) { dialogBuilder.dismiss(); } }).setButton2Click(new View.OnClickListener() { @Override public void onClick(View v) { dialogBuilder.dismiss(); ((AddPay) act).VoiceRecognition(); } }).show();break;case "OK":dialogBuilder.withTitle("识别成功") // .withTitle(null) no title .withTitleColor("#FFFFFF") // def .withDividerColor("#11000000") // def .withMessage("成功!\n你刚刚说了“" + context1 + "”,\n是否确定要记录这条数据?") // .withMessage(null) no Msg .withMessageColor("#FFFFFF") // def .withIcon(ctx.getResources().getDrawable(R.drawable.icon)) .isCancelableOnTouchOutside(false) // def | // isCancelable(true) .withDuration(700) // def .withEffect(effect) // def Effectstype.Slidetop .withButton1Text("取消") // def gone .withButton2Text("确定") // def gone .setButton1Click(new View.OnClickListener() { @Override public void onClick(View v) { dialogBuilder.dismiss(); } }).setButton2Click(new View.OnClickListener() { @Override public void onClick(View v) { dialogBuilder.dismiss(); ((AddPay) act).VoiceSuccess(); } }).show();break;case "judge":dialogBuilder .withTitle("识别成功") // .withTitle(null) no title .withTitleColor("#FFFFFF") // def .withDividerColor("#11000000") // def .withMessage( "成功!\n你刚刚说了“" + context1 + "”,\n<" + VoiceSave[3] + ">类别需要你请确认该笔是<开支>还是<收入>?\n") // .withMessage(null) no Msg .withMessageColor("#FFFFFF") // def .withIcon(ctx.getResources().getDrawable(R.drawable.icon)) .isCancelableOnTouchOutside(false) // def | // isCancelable(true) .withDuration(700) // def .withEffect(effect) // def Effectstype.Slidetop .withButton1Text("开支") // def gone .withButton2Text("收入") // def gone .setButton1Click(new View.OnClickListener() { @Override public void onClick(View v) { dialogBuilder.dismiss(); type = "pay"; ((AddPay) act).VoiceSuccess(); } }).setButton2Click(new View.OnClickListener() { @Override public void onClick(View v) { dialogBuilder.dismiss(); type = "income"; ((AddPay) act).VoiceSuccess(); } }).show();break;case "quit":dialogBuilder.withTitle("退出程序") // .withTitle(null) no title .withTitleColor("#FFFFFF") // def .withDividerColor("#11000000") // def .withMessage("是否要退出程序?\n\n") // .withMessage(null) no Msg .withMessageColor("#FFFFFF") // def .withIcon(ctx.getResources().getDrawable(R.drawable.icon)) .isCancelableOnTouchOutside(false) // def | // isCancelable(true) .withDuration(700) // def .withEffect(effect) // def Effectstype.Slidetop .withButton1Text("取消") // def gone .withButton2Text("退出") // def gone .setButton1Click(new View.OnClickListener() { @Override public void onClick(View v) { dialogBuilder.dismiss(); } }).setButton2Click(new View.OnClickListener() { @Override public void onClick(View v) { SysApplication.getInstance().exit(); } }).show();break;}}
    5.4 类别维护界面增加或删除收入/支出类别,并判断类别是否重复是否为空。

    部分代码:
    private void inputTitleDialog() { final EditText inputServer = new EditText(InPtypeManager.this); inputServer.setFocusable(true); AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("添加类型").setView(inputServer) .setNegativeButton("取消", null); builder.setPositiveButton("确定", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { inputStr = inputServer.getText().toString(); int i = (int) itypeDAO.getCount(userid) + 1; if (inputStr.trim().equals("")) { Toast.makeText(InPtypeManager.this, "输入内容不能为空!", Toast.LENGTH_LONG).show(); refresh(); } else{if(flag){Toast.makeText(InPtypeManager.this, "不可以重复插入!", Toast.LENGTH_LONG).show();}else{if (type == 0) { itypeDAO.add(new Tb_itype(userid, i, inputStr)); } else { ptypeDAO.add(new Tb_ptype(userid, i, inputStr)); }} refresh(); } }); builder.show(); }
    5.5 收入/支出统计界面收入支出统计页面,通过使用第三方开发的图表显示控件,通过从SQLite获取的数据和计算所占总数的百分比,去初始化图表显示,然后通过线程更新当前的UI。左右的按钮是调用上一个月和下一个月的数据,然后重新调用该activity来显示。另外我还添加了侧滑菜单来让用户选择是“收入图表”和“支出图表”,另外用户还能选择指定的日期范围,数据也是调用该页面显示。

    public void initView() { time = new Time("GMT+8"); time.setToNow(); defaultMonth = time.month + 1;// 设置默认月份 defaultYear = time.year; intentr = getIntent(); userid = intentr.getIntExtra("cwp.id", 100000001); defaultMonth = intentr.getIntExtra("default", defaultMonth); defaultYear = intentr.getIntExtra("defaulty", defaultYear); type = intentr.getIntExtra("type", 0);// 为0,选择上下月,为1,选择任意时间 pieView = (PieView) this.findViewById(R.id.lotteryView); myButton = (MyButton) this.findViewById(R.id.MyBt); textView = (TextView) this.findViewById(R.id.MyTV); textView2 = (TextView) this.findViewById(R.id.MyTVbottom); example_left = (ImageView) findViewById(R.id.example_left); example_right = (ImageView) findViewById(R.id.example_right); example_center = (TextView) this.findViewById(R.id.example_center); textView.setOnClickListener(this); example_left.setOnClickListener(this); example_right.setOnClickListener(this); example_center.setText(String.valueOf(defaultYear) + "年 - " + String.valueOf(defaultMonth) + "月"); //获取数据 if (type == 0) { KindDatap = payDAO.getKDataOnMonth(userid, defaultYear, defaultMonth); } else { date1 = intentr.getStringExtra("date1"); date2 = intentr.getStringExtra("date2"); KindDatap = payDAO.getKDataOnDay(userid, date1, date2); } initItem(); //初始化数据 if (!(KindDatap.size() == 0)) { //当获取到数据时 Message msg = new Message(); msg.obj = pieView.getCurrentChartProp(); handler.sendMessage(msg); //发送消息,更新UI } pieView.setChartPropChangeListener(new ChartPropChangeListener() { @Override public void getChartProp(ChartProp chartProp) { Message msg = new Message(); msg.obj = chartProp; handler.sendMessage(msg); //发送消息,更新UI } }); pieView.start();}public Handler handler = new Handler() { //创建线程 public void handleMessage(android.os.Message msg) { ChartProp chartProp = (ChartProp) msg.obj; myButton.setBackgroundPaintColor(chartProp.getColor()); textView.setText(chartProp.getName()); textView2.setText(chartProp.getName2()); textView.setTextColor(chartProp.getColor()); };};/** * * Description:初始化转盘的颜色 * */public void initItem() { int i = 0; int fivecolor[] = new int[] { Color.rgb(56, 220, 244), Color.GREEN, Color.RED, Color.YELLOW, Color.CYAN }; if (KindDatap.size() == 0) { // 没有数据的情况 amount = "暂无数据"; // 无数据下总数的提示文字 } else { // 获取数据的情况 double sum = 0.00; for (KindData kp : KindDatap) { sum += kp.getAmount();// 取得总和 i++; } // 初始化数组 String names[] = new String[i]; float percent[] = new float[i]; String names2[] = new String[i]; int color[] = new int[i]; i = 0; for (KindData kp : KindDatap) { names[i] = ptypeDAO.getOneName(userid, kp.getKindname()); if (i < fivecolor.length) { color[i] = fivecolor[i]; // 使用自定义颜色 } else { color[i] = getRandomColor(); // 使用随机生成的颜色 } java.text.NumberFormat percentFormat = java.text.NumberFormat .getPercentInstance(); percentFormat.setMaximumFractionDigits(2); // 最大小数位数 // 自动转换成百分比显示. names2[i] = percentFormat.format(kp.getAmount() / sum) + ":¥" + kp.getAmount(); percent[i] = (float) (kp.getAmount() / sum); // 计算所占百分比 i++; } amount = Double.toString(sum); // 总数的费用 // 创建图表 ArrayList<ChartProp> acps = pieView.createCharts(i); int size = acps.size(); for (int k = 0; k < size; k++) { // 把数据传入图表 ChartProp chartProp = acps.get(k); chartProp.setColor(color[k]); chartProp.setPercent(percent[k]); chartProp.setName(names[k]); chartProp.setName2(names2[k]); } pieView.initPercents(); }}private int getRandomColor() {// 分别产生RBG数值 Random random = new Random(); int R = random.nextInt(255); int G = random.nextInt(255); int B = random.nextInt(255); return Color.rgb(R, G, B);}public static String getamount() { return amount;}@Overridepublic void onClick(View v) { switch (v.getId()) { case R.id.example_left: // 上一个月的按键 if (defaultMonth != 1) defaultMonth = defaultMonth - 1; else { defaultMonth = 12; defaultYear = defaultYear - 1; } Intent intentl = new Intent(PayChart.this, PayChart.class); intentl.putExtra("defaulty", defaultYear); intentl.putExtra("default", defaultMonth); intentl.putExtra("cwp.id", userid); startActivity(intentl); break; case R.id.example_right: // 下一个月的按键 if (defaultMonth != 12) defaultMonth = defaultMonth + 1; else { defaultMonth = 1; defaultYear = defaultYear + 1; } Intent intentr = new Intent(PayChart.this, PayChart.class); intentr.putExtra("defaulty", defaultYear); intentr.putExtra("default", defaultMonth); intentr.putExtra("cwp.id", userid); startActivity(intentr); overridePendingTransition(android.R.anim.slide_in_left,android.R.anim.slide_out_right); break; }}
    6 论文总结从一月开始,经过这三个月的学习奋斗,毕业设计到现在终于基本上已经完成了。在这段日子里从系统的需求分析开始,然后到对系统功能。
    进行详细设计,最后到系统的编码实现,最后到论文的完成,我通过查阅大量的图书与文献自学相关知识,同时也诚心请教同学和老师,通过与他们的交流我学到了很多专业知识和经验,让我受益匪浅,对相关专业知识有了一个新的认识,当遇到有不认识或未曾遇到过的错误,上网寻找资料,解决难题,让我懂得网上的资源是十分之多,令我获益良多,这些锻炼了我坚强的意志,同时也使我的专业知识更加扎实,让我在以后的人生职业道路上可以更加的自信和顽强。
    毕设做到现在,算是告一段落。谈起为什么选Android,我只能说感兴趣,而又没毅力。在没有压力的情况下,让我去专研一门不懂的东西可行性太低。只能说很了解自己的性子。我想通过这种方式逼自己,幸好没让自己失望。虽然此次的毕设没有很有技术含量,但还是算勉强够凑合吧。
    希望经过后期的维护肯定能达到使用要求,更好完善自己系统的功能。
    参考文献[1] FrankAbleson.Introductionto Android development[J].developerWorks,2009,10(7).
    [2] Ed Burnett, Hello, Android: Introducing Google’s Mobile Development Platform[J]. PRAGMATIC BOOKSHELF,2010.7:10-11.
    [3] 徐娜子.Android江湖 [M].电子工业出版社.2011.11
    [4] 郭志宏. Android应用开发详解[M].电子工业出版社. 2010.
    [5] 明日科技.Android从入门到精通 [M].清华大学出版社.2012.9
    [6] 杨丰盛.Android应用开发揭秘[M]. 机械工业出版社. 2010.
    [7] 孙宏明.Android手机程序设计入门、运用到精通 [M].中国水利水电出版社.2012.3
    [8] 张仕成.基于Google Android平台的应用程序开发与研究[j].电脑知识与技,术2009.(5)
    [9] 靳岩,姚尚朗. Google Android开发入门与sss实践[M].人民邮电出版社. 2009
    [10] 姚永明,吕建平. 基于Android平台的用户管理软件的设计与实现[J]. 西安文理学院学报(自然科学版),2013,01:79-83.
    [11] 武玉坤.基于Android移动学习平台的设计[J]. 计算机光盘软件与应用,2013,01:20-21+47.
    [12] 姜海岚.基于Android的多功能手机相册设计与实现[J]. 电脑知识与术,2013,15:3614-3616.
    [13] 李刚.疯狂Android讲义 [M].电子工业出版社.2011.6
    [14] 李宁.Android应用开发实战[M].第2版. 机械工业出版社.2013
    4 评论 110 下载 2019-03-24 12:23:31 下载需要19点积分
  • 基于JSP和SSH框架实现的班级管理系统

    1 系统需求的分析1.1 需求分析本项目所开发的班级管理系统完成学校对学生的班级信息的统计与管理,减少数据漏掉的情况,同时也节约人力、物力和财力,告别以往的人工统计。方便学生使用,学生可以在线查看班级信息,学生信息,课程信息,成绩信息,信息修改。
    1.1.1 系统管理员需求分析
    班级管理:管理员在登录之后可以对系统内的班级进行管理,包括对班级的信息进行增加、查询、修改和删除等操作
    课程管理:管理员在登录之后可以对系统内的课程进行管理,包括对课程的信息进行增加、查询、修改和删除等操作
    学生管理:管理员在登录之后可以对系统内学生进行管理,包括对学生的信息进行增加、查询、修改和删除等操作

    1.1.2 教师需求分析基本信息管理:

    成绩录入:管理员在后台添加、编辑成绩,进行成绩录入
    统计查询:统计某一个学生的所修课程信息、汇总出学分、不及格课程(标红)统计每一门课程的最高分、最低分和平均分

    1.2 任务概述1.2.1 目标本系统是为了更好管理班级信息而设计的。由于学校的学生繁多,包含的信息量大且复杂,有必要建立一个班级管理系统,使选班级管理工作规范化,系统化,程序化,提高信息处理的速度和准确性,能够及时、准确、有效的查询和修改选课排课相关信息。
    1.2.2 运行环境服务器配置包括硬件配置和软件配置,它们各自都有详细的要求,下面将分别进行介绍。

    硬件配置安装本软件之前,需要确保计算机具有如下配置,这也是最低硬件要求: 酷睿i5或更高的微处理器(或与之相当的处理器) 使用2G内存
    软件配置安装本软件之前,需要确保计算机已安装:Micorsoft Win10的操作系统

    1.3 系统完整性、安全性1.3.1 对服务的要求为保证该系统平稳、安全地运行,系统为用户提供的服务要有以下限制:

    管理员具有该系统的最高权限,负责系统维护,更新 教师可以查看个人的全部信息,修改个人信息,若要修改课程信息需向教务处申报,由管理员修改学生能查看个人的全部信息、自己的选课结果、修改个人信息,若要修改选课信息,需向教务处申报,由教务处修改
    1.3.2 对性能的要求
    此系统的数据输入大多数为字符串类型,也有表示成绩的浮点数,精度为6
    响应时间:对于管理人员输入的用户信息应该在人们所能接受的等待时间来确定,通常为1-2秒
    更新处理时间要求 :用户输入数据后,对于该操作人员输入的数据处理时间应该是毫秒级的。数据的转换和传输也应该在人们的接受的等待时间内

    灵活性

    在操作方式上若发生变化,如无法使用浏览器进入系统,那么,可以使用命令方式进入如:可以通过编写java程序与数据库联接,实现登录
    因为本系统开发是使用jsp实现,这种开发技术具有java一样的很好的系统移植性
    对于数据精度方面,应在数据库中加以限制;实现实体完整性,用户自定义完整性约束
    在设计界面结构和数据结构是应留有对以后扩充系统功能的余地,如可以在每个数据字段上设计多个自定义字段

    2 系统设计2.1 系统概要设计在需求明确、准备开始编码之前,要做概要设计。概要设计的主要任务是把需求分析得到的DFD转换为软件结构和数据结构。设计软件结构的具体任务是:将一个复杂系统按功能进行模块划分、建立模块的层次结构及调用关系、确定模块间的接口及人机界面等。数据结构设计包括数据特征的描述、确定数据的结构特性、以及数据库的设计。概要设计有多种方法。在早期有模块化方法、功能分解方法;在60年代后期提出了面向数据流和面向数据结构的设计方法;近年来又提出面向对象的设计方法等。本系统采用模块化软件设计,自顶向下逐层把软件系统划分成若干模块。每个模块完成一个特定的功能,最后将所有模块按照某种方法组合成一个整体,达到整个播放器所要求的功能。
    2.2 系统功能设计及描述本选课系统不仅要实现常见的选课功能,而且还要有友好的用户界面。其最终设计目标是:多用户可以同时对系统进行操作、实现学生选课及对个人信息的修改,实现老师可以查看学生选课情况及对个人信息的修改,实现管理员对教师,学生信息修改及对课程的管理、运行平台要求不高、界面美观舒适、操作简单、易维护、高稳定性,能满足用户的常见需求。
    主要划分为以下几个功能模块,每个功能模块的作用描述如下:

    班级管理:主要用来管理班级情况
    课程管理:管理员可以对课程进行管理确定课程的数量
    学生管理:管理员可以对学生进行管理,可以对学生增加或者删除
    成绩录入:将成绩提交到数据库
    统计查询:统计某一个学生的所修课程信息、汇总出学分、不及格课程(标红)统计每一门课程的最高分、最低分和平均分

    3 系统功能的实现3.1 学生登录界面
    关键代码
    <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><%String path = request.getContextPath();String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html> <head> <base href="<%=basePath%>"> <title>班级信息管理系统登陆界面</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <link rel="stylesheet" type="text/css" href="styles.css"> <link href="/Wopop_files/style_log.css" rel="stylesheet" type="text/css"> <link rel="stylesheet" type="text/css" href="/Wopop_files/style.css"> <link rel="stylesheet" type="text/css" href="/Wopop_files/userpanel.css"> <link rel="stylesheet" type="text/css" href="/Wopop_files/jquery.ui.all.css"> <script src="/Wopop_files/login.js"></script> <script src="/Wopop_files/JQuery.cookie.js"></script> <script src="/Wopop_files/google_jquery.min.js"></script> <script src="/Wopop_files/google_jquery-ui.min.js"></script><script language="JavaScript">function chkvalue(txt) { if(txt.value=="") alert("文本框里必须填写内容!");}</script></head> <body class="login" id="myform" method="post"><div class="login_m"><div class="login_logo"><h1><strong>班级信息管理系统</strong></h1></div><div class="login_boder"><div class="login_padding" id="login_model"> <h2>用户名</h2> <label> <input type="text" id="username" name="username" class="txt_input txt_input2" onfocus="if(this.value=='请输入用户名'){this.value='';}" onblur="if(this.value==''){this.value='请输入用户名';}" value="请输入用户名" value="${sid}"> </label> <h2>密码</h2> <label> <input type="password" name="password" id="password" class="txt_input" onfocus="if(this.value=='*******'){this.value='';}" onblur="if(this.value==''){this.value='*******';}" value="*******" value="${spassword}"> </label> <font size="3px;" style="color: red"> ${msg} </font> <div class="rem_sub"> <div class="rem_sub_l"> <p class="pull-left" align="right"><a id="iforget" href="register.action"> 注册</a></p> </div> <p class="forgot" ><a id="iforget" href="../index.jsp">忘记密码 ?     </a></p> </div> <div class="rem_sub"> <div class="rem_sub_l"> <input type="checkbox" name="checkbox" id="save_me"> <label for="checkbox">记住用户信息</label> </div> <label> <input type="submit" class="sub_button" name="button" id="button" value="登 录" style="opacity: 0.7;" onClick="IMG1_onclick2()" > </label> </div></div><div id="forget_model" class="login_padding" style="display:none"><br> <h1>Forgot password</h1> <br> <div class="forget_model_h2">(Please enter your registered email below and the system will automatically reset users’ password and send it to user’s registered email address.)</div> <label> <input type="text" id="usrmail" class="txt_input txt_input2"> </label> <div class="rem_sub"> <div class="rem_sub_l"> </div> <label> <input type="submit" class="sub_buttons" name="button" id="Retrievenow" value="Retrieve now" style="opacity: 0.7;">     <input type="submit" class="sub_button" name="button" id="denglou" value="Return" style="opacity: 0.7;">   </label> </div></div><!--login_padding Sign up end--></div><!--login_boder end--></div><!--login_m end--><br> <br> <p align="center"> 班级信息管理系统 <a href="http://www.stdu.edu.cn/" target="_blank" title="铁大官网"></a> - Made by <a >陈鹏</a></p></body><script type="text/javascript"> function IMG1_onclick2() { var sid = document.getElementById("username"); var spassword = document.getElementById("password"); var url = "login_check.action?sid=" + escape(sid.value) + "&spassword=" + escape(spassword.value); if (sid.value == "") { alert("用户名不能为空"); } else if (spassword.value == "") { alert("密码不能为空"); } else if (sid.value.length > 20 ||sid.value.length < 6) { alert("用户名小于6位或大于20位"); } else if (spassword.value.length > 20 || spassword.value.length < 6) { alert("密码小于6位或大于20位"); } else if (spassword.value=="******"||sid.value == "请输入用户名") { alert("请输入正确的用户信息"); } else if (CheckCode(sid.value) && CheckCode(sid.value) ) { alert("有特殊字符请重新填写!"); sid.value = ""; spassword.value = ""; }else { /* alert(0); */ window.location.href = url; } } function CheckCode(s) //有特殊字符为true { var containSpecial = RegExp(/[(\ )(\~)(\!)(\#)(\$)(\%)(\^)(\&)(\*)(\()(\))(\-)(\_)(\+)(\=)(\[)(\])(\{)(\})(\|)(\\)(\;)(\:)(\')(\")(\,)(\.)(\/)(\<)(\>)(\?)(\)]+/); return (containSpecial.test(s)); }</script></html>
    3.2 主页
    关键代码
    <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><%@ taglib uri="/struts-tags" prefix="s"%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html> <head> <title>班级信息管理系统</title> <%@include file="/WEB-INF/Z-views/common/head.jsp"%> <%-- <script type="text/javascript" src="/js/jquery1.js"></script> --%> <script type="text/javascript"> $(document).ready( function() { $(".div2").click( function() { $(this).next("div").slideToggle("slow").siblings( ".div3:visible").slideUp("slow"); }); }); function openurl(url) { var rframe = parent.document.getElementById("rightFrame"); rframe.src = url; }</script><style type="text/css">body { margin: 0; font-family: 微软雅黑; background-image: url(images/.jpg); background-repeat: no-repea; background-size: cover; background-attachment: fixed;}.top1 { position: absolute; top: 0px; width: 100%; height: 20px; text-align: center; color: black; font-size: 17px; font-height: 20px; font-family: 楷体;}.title {float:left; margin:-32px 20px; font-size: 35px; color: black; font-height: 30px; font-family: 隶书;}.top2 { position: absolute; top: 20px; width: 100%; height: 77px; text-align: center; color: #ccffff;}.left { position: absolute; left: 0px; top: 97px; width: 200px; height: 85%; border-right: 1px solid #9370DB; color: #000000; font-size: 20px; text-align: center;}.right { position: absolute; left: 200px; top:97px; width: 85.2%; height: 85%; border-top: 0px solid #484860; font-size: 14px; text-align: center;}.end { position: absolute; bottom: 0px; width: 100%; height: 30px; text-align: center; color: #556B2F; font-size: 17px; font-height: 20px; font-family: 楷体; background-color:ABD6E0}.div1 { text-align: center; width: 200px; padding-top: 10px;}.div2 { height: 40px; line-height: 40px; cursor: pointer; font-size: 18px; position: relative; border-bottom: #ccc 0px dotted;}.div3 { display: none; cursor: pointer; font-size: 15px;}.div3 ul { margin: 0; padding: 0;}.div3 li { height: 30px; line-height: 30px; list-style: none; border-bottom: #ccc 1px dotted; text-align: center;}.a { text-decoration: none; color: #000000; font-size: 15px;}.a1 { text-decoration: none; color: #000000; font-size: 18px;}</style> </head> <body> <div class="top1"> <marquee scrollAmount=2 width=300>欢迎使用该系统!</marquee> </div> <div class="top2"> <div class="title" > <br/> <span style="size:18px"> 班级信息管理系统</span> <br/> </div> <div class="fr top-links" align="right" > <br/> <br/> <a href="javascript:void(0);" onClick="openurl('help');"> <i class="ti-user"></i> <span style="color: black">登录用户:DeathGhost</span></a> </div> </div> <div class="left"> <div class="div1"> <a class="a" href="javascript:void(0);" onClick="openurl('student_Studentlist.action');"> <div class="div2"> <i class="ti-user"></i> 学生信息 </div> </a> <a class="a" href="javascript:void(0);" onClick="openurl('banji_Banlist.action');"> <div class="div2"> <i class="ti-view-list-alt"></i> 班级信息 </div> </a> <a class="a" href="javascript:void(0);" onClick="openurl('kecheng_KeChenglist.action');"> <div class="div2"> <i class="ti-text"></i> 课程信息 </div> </a> <a class="a" href="javascript:void(0);" onClick="openurl('score_ChengJilist.action');"> <div class="div2"> <i class="ti-pencil-alt2"></i> <span>成绩录入</span> </div> </a> <a class="a" href="javascript:void(0);" onClick="openurl('tongji');"> <div class="div2"> <i class="ti-map"></i> 统计查询 </div> </a> <a class="a" href="javascript:void(0);" onClick="openurl('help.action');"> <div class="div2"> <i class="ti-bell"></i> 系统信息 </div> </a> <a class="a" href="login.action" onclick="Exit_onclick()"> <div class="div2"> <i class="ti-export"></i> 退出系统 </div> </a> </div> </div> <div class="right"> <iframe id="rightFrame" name="rightFrame" width="100%" height="100%" scrolling="auto" marginheight="0" marginwidth="0" align="top" style="border: 0px solid #CCC; margin: 0; padding: 0;"> </iframe> <div class="copyright pull-right"> <p style="color:black"> 班级信息管理系统 <a href="http://www.stdu.edu.cn/" target="_blank" title="铁大官网">   </a></p> </div> </div> </body> <!-- 退出系统的验证 --> <script language="JavaScript"> function Exit_onclick() { if (confirm("确定退出系统吗?")) { return true; } else{ return false; } } </script></html>
    3.3 查看学生信息
    关键代码
    <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><%@ taglib uri="/struts-tags" prefix="s"%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html> <head> <title>学生信息管理界面</title> <%@include file="/WEB-INF/Z-views/common/head.jsp"%> </head><body><s:debug/><br/> <br/> <center> <s:form id="domainForm" action="student_Studentlist" theme="simple"> 名称: <s:textfield name="baseQuery.sname" placeholder="名称" />   <a href="javascript:;" onclick="go(1);" class="add">搜索 </a>   <a href="student_input.action" class="add">添加</a> <br /> <br /> <table border="1" align="center"> <tr style="background-color: PowderBlue;"><!--style="background-color: DodgerBlue;" --> <th width="15%" ><span><strong>学 号</strong></span></th> <th width="15%" ><span><strong>姓名</strong></span></th> <th width="12%" ><span><strong>性别</strong></span></th> <th width="15%" ><span><strong>密码</strong></span></th> <th width="11%" ><span><strong>联系电话</strong></span></th> <th width="11%" ><span><strong>家庭住址</strong></span></th> <th colspan="2" width="11%" align="center"><span><strong>操作</strong></span></th> </tr> <s:iterator value="pageList.rows" var="t"> <tr style="background-color: white;"> <td><s:property value='sid'/></td> <td><s:property value='sname'/></td> <td><s:property value='ssex'/></td> <td><s:property value='spassword'/></td> <td><s:property value='slxdh'/></td> <td><s:property value='sjtzz'/></td> <td><button type="button" class="btn1" onclick="updateDomain('student_updateAction.action?sid=<s:property value='sid'/>');">编辑</button></td> <td><button type="button" class="btn1" onclick="deleteDomain('student_delete.action?sid=<s:property value='sid'/>');">删除</button></td> </tr> </s:iterator> </table> <%@include file="/WEB-INF/Z-views/common/page.jsp"%> </s:form> </center></body><%-- <%@include file="/WEB-INF/Z-views/common/tail.jsp"%> --%></html>
    3.4 班级信息
    关键代码
    <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><%@ taglib uri="/struts-tags" prefix="s"%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head> <title>班级信息管理界面</title><%@include file="/WEB-INF/Z-views/common/head.jsp"%> </head><body><s:debug></s:debug><br/><br/> <center> <s:form id="domainForm" action="banji_Banlist.action" theme="simple"> 名称: <s:textfield name="baseQuery.bname" placeholder="名称" />   <a href="javascript:;" onclick="go(1);" class="add">搜索 </a>   <a href="banji_input.action" class="add">添加</a> <br /> <br /> <table border="1" > <tr style="background-color: ;"> <th width="20%" ><span><strong>编 号</strong></span></th> <th width="40%" ><span><strong>名 称</strong></span></th> <th colspan="2" width="20%"><span><strong>操作</strong></span></th> </tr> <s:iterator value="pageList.rows" var="st"> <tr style="background-color: white;"> <th><s:property value='bid'/> </th> <th><s:property value='bname'/></th> <td ><button type="button" class="" onclick="updateDomain('banji_actionUpdate.action?bid=<s:property value='bid'/>');">编辑</button></td> <td><button type="button" class="" onclick="deleteDomain('banji_delete.action?bid=<s:property value='bid'/>');">删除</button></td> </tr> </s:iterator> </table> <%@include file="/WEB-INF/Z-views/common/page.jsp"%> </s:form> </center></body><%-- <%@include file="/WEB-INF/Z-views/common/tail.jsp"%> --%><center></center></html>
    3.5 课程信息
    关键代码
    <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><%@ taglib uri="/struts-tags" prefix="s"%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html> <head> <title>课程信息管理界面</title> <%@include file="/WEB-INF/Z-views/common/head.jsp"%> </head><body><s:debug></s:debug><br/><br/> <center> <s:form id="domainForm" action="kecheng_KeChenglist" theme="simple"> 名称: <s:textfield name="baseQuery.kname" placeholder="名称" />   <a href="javascript:;" onclick="go(1);" class="add">搜索 </a>   <a href="kecheng_input.action" class="add">添加</a> <br /> <br /> <table border="1" > <tr style="background-color: PowderBlue;"> <th width="20%" ><span><strong>课程编号</strong></span></th> <th width="20%" ><span><strong>课程名称</strong></span></th> <th width="20%" ><span><strong>课程性质</strong></span></th> <th width="20%" ><span><strong>课程学分</strong></span></th> <th colspan="2" width="20%" ><span><strong>操作</strong></span></th> </tr> <s:iterator value="pageList.rows" status="st"> <tr style="background-color: white;"> <td><s:property value='kid'/></td> <td><s:property value='kname'/></td> <td><s:property value='knature'/></td> <td><s:property value='kxf'/></td> <td><button type="button" class="btn1" onclick="updateDomain('kecheng_actionUpdate.action?kid=<s:property value='kid'/>');">编辑</button></td> <td><button type="button" class="btn1" onclick="deleteDomain('kecheng_delete.action?kid=<s:property value='kid'/>');">删除</button></td> </tr> </s:iterator> </table> <%@include file="/WEB-INF/Z-views/common/page.jsp"%> </s:form> </center></body><%@include file="/WEB-INF/Z-views/common/tail.jsp"%></html>
    3.7 成绩录入
    关键代码
    <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><%@ taglib uri="/struts-tags" prefix="s"%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head> <title>学生成绩信息管理界面</title><%@include file="/WEB-INF/Z-views/common/head.jsp"%> <script type="text/javascript" src="/js/model/score.js"></script></head><body><s:debug/><br/><br/> <center> <s:form id="domainForm" action="chengji_ChengJilist.action" theme="simple"> 学生学号: <s:textfield name="baseQuery.sid" placeholder=" 学生学号" />     课程名称: <s:select onchange='$("#domainForm").submit();' style="height:23px;" list="#allCnames" name="baseQuery.kid" listValue="kname" listKey="kid" headerKey="-1 " headerValue="------------请选择------------" />  <a href="javascript:;" onclick="go(1);" style="position: relative; left: 3%">搜索 </a>   <br /> <br /> <table border="1" > <tr style="background-color: PowderBlue;"> <th width="18%" ><span><strong>学生学号</strong></span></th> <th width="18%" ><span><strong>学生姓名</strong></span></th> <th width="18%" ><span><strong>学生班级</strong></span></th> <th width="25%" ><span><strong>所选课程</strong></span></th> <th width="10%" ><span><strong>课程成绩</strong></span></th> <th colspan="2" width="5%"><span><strong>操作</strong></span></th> </tr> <s:iterator value="pageList.rows"><!-- var="t" --> <tr style="background-color: white;"> <td><s:property value="STable.sid"/></td> <td><s:property value="STable.sname"/></td> <td><s:property value="STable.BTable.bname"/></td> <td><s:property value="KTable.kname"/></td> <td><!-- <s:property value='ccj'/> --> <input name="ccj" id="ccj" value="${ccj}" /> </td> <td ><a onclick="update(this)"><button type="button">保存 </button></a></td> </tr> </s:iterator> </table> <%@include file="/WEB-INF/Z-views/common/page.jsp"%> </s:form> </center></body></html>
    3.8 统计查询
    关键代码
    <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><%@ taglib uri="/struts-tags" prefix="s"%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html> <head> <title>课程信息管理界面</title> <%@include file="/WEB-INF/Z-views/common/head.jsp"%> </head><body><br/> <s:form> <table border="1" align="center"> <tr ><!--style="background-color: DodgerBlue;" --> <th width="11%" ><span><strong>学 号</strong></span></th> <th width="11%" ><span><strong>姓名</strong></span></th> <th width="8%" ><span><strong>性别</strong></span></th> <th width="11%" ><span><strong>出生日期</strong></span></th> <th width="11%" ><span><strong>密码</strong></span></th> <th width="11%" ><span><strong>联系电话</strong></span></th> <th width="11%" ><span><strong>家庭住址</strong></span></th> <th width="11%" ><span><strong>班级编号</strong></span></th> <th colspan="2" width="11%" ><span><strong>操作</strong></span></th> </tr> <s:iterator value="pageList.rows" status="st"> <tr style="background-color: white;"> <td>${sid}</td> <td>${sname}</td> <td>${ssex}</td> <td>${spassword}</td> <td>${slxdh}</td> <td>${sjtzz}</td> <td>${sName}</td> <td><button type="button" class="btn1" onclick="updateDomain('role_input.action?roleId=${roleId}');">编辑</button></td> <td><button type="button" class="btn1" onclick="deleteDomain('role_delete.action?roleId=${roleId}');">删除</button></td> </tr> </s:iterator> </table> </s:form></body><%@include file="/WEB-INF/Z-views/common/tail.jsp"%></html>
    4 心得体会通过这次使用SSH框架编写大作业,初期对于搭框架就遇到了不少问题,后来问同学+百度解决了问题,在后来进行删除操作,涉及到了级联操作,添加的时候也存在主外键约束,有时候查程序感觉是对的,但是运行结果总是不对,就开始调试,在控制台打印文字,根据这些调试和打印出的文字,来使程序能够正确运行,总之,这次还是有不少收获,中间遇到的问题,以及需要注意的地方写了一个TXT文件,也方便以后查看。
    5 评论 105 下载 2018-11-05 17:04:45 下载需要6点积分
  • 基于Winpcap的网络数据包嗅探抓包和发包工具的设计与实现

    摘 要本文课题设计为基于WinPcap的开源网络数据处理工具程序包的设计与实现,使用MFC库和C++来实现。
    本文首先说明了课题研究的目的、意义和网络监听的背景,讨论了在网络数据处理领域领先的工具以及当前网络学习的困难,以此引出了本课题研究的需求和可行性分析。然后分析了网络结构,重要网络协议和网络数据传输的基本原理,介绍基于WinPcap、MFC实现网络监听。接着罗列了本程序设计思路和架构,功能流程及其实现逻辑,自顶向下,结合源码逐一展开描述。最后将程序的功能测试及其结果进行展示。
    关键词:网络协议,数据包,TCP/UDP,socket,ARP,WinPcap
    ABSTRACTThis article is designed to subject the design and realization of WinPcap open source tool for network data processing package based on the use of MFC and C++ libraries to achieve.
    This paper describes the purpose, significance and network monitoring research background ,discusses network data processing field-leading tools and the difficulty of the current network learning , this leads to the need for and feasibility of this research . Then it analyzes the basic principles of network architecture,network protocols, and important network data transmission introduced on WinPcap, MFC for network monitoring. Then a list of design ideas and architecture, functional flow of the program and its implementation logic ,top-down , one by one to start binding source description . Finally, the functions of the program and the test results show.
    KEYWORDS:network protocol, datagram, TCP/UDP, socket, ARP, WinPcap
    1 绪论1.1 网络与监听互联网时代与大数据时代的到来,愈发体现出了信息的快速增长与知识膨胀的力量。信息的不平等造就了当前这个时代的不平等,只有掌握了最新、最全的数据,才能在这个时代立足脚跟,快速前进。而网络,则是承载信息的天桥,伸手在信息的洪流中抓一把,逝去的会多于获得的,因此我们更加需要了解网络的结构和数据传输的协议、方式,以提供更高效的信息获取手段。
    然而,随着计算机的普及,网络已成为人们生活中不可或缺的工具,随之而来的非法入侵也不断加剧着计算机网络系统的安全问题。正是由于以太网中采用广播方式,因此,在某个广播域中可以监听到该域所有的信息包,利用一定是技术手段,还能向外拓展,侵入其他网络。
    网络监听是一种监视当前网络状态以及网络上数据传输的管理工具,它可以将计算机网卡设定成监听模式,从而获取该域网络上所传输的信息。对于网络管理员来说,网络监听技术可以用来查看、分析该域网络的性能和实时状态;对于入侵者来说,网络监听技术可以用来获得网络中传输的各种数据。为了保护网络信息的安全,必须采用网络监听技术进行反跟踪,时刻探明网络的安全现状,掌握先机,才能保证网络的信息安全。
    目前,市场上针对不同版本的操作系统,有不同版本的网络数据监听软件,如 Window9x 平台的NetXray、Win2000/xp平台下的Sniffer Pro以及UNIX 平台Netman和SunSniff 等等。
    其中要数翘楚的,有NAI公司推出的一款一流的网络管理和应用故障诊断分析软件Sniffer。不论是在有线网络还是在无线网络中,它都能够帮助网络管理人员进行实时的网络监视、数据包捕获以及故障诊断分析。对于在实时快速的网络问题故障诊断中,基于该便携式软件的解决方案具备很高的性价比,它能够让用户获得强大的网络管理和应用故障诊断能力。
    说到Sniffer就不得不提Wireshark了,我校网络工程专业在本科三年级的网络协议学习中,就使用WireShark来帮助获取本机网络中的实时数据,以此学习分析TCP/IP等协议。WireShark(前称Ethereal)是一个免费开源的网络数据包分析软件。网络数据包分析软件的功能是截取网络数据包,并尽可能显示出最为详细的网络数据包数据。
    以上介绍的均是由单纯软件实现的网络嗅探器,其功能相对硬件嗅探器来说还是较为简单。下面将罗列几款由硬件实现的网络嗅探器。硬件的网络嗅探器又称为网络分析仪(协议分析仪)。目前,网络分析仪的技术已经非常的成熟,产品种类繁多。但各种产品的功能略有差异,有的专精于某种协议,有的“包罗万象”。一般的情况下,大型的企业网络都会使用软硬件想结合的网络分析仪。如:1)安捷伦网络分析仪系统,2)Fluke Optiview 综合网络协议分析仪等。
    1.2 研究意义网络数据的传输、网络协议和网络结构对于计算机的使用者来说是透明的,它们非常重要但又十分复杂,需要初学者去好好了解和学习。然而,由于无法直观地获取到网络实时传输的数据,在课堂上学习到的都是相关的理论知识,对于学习者来说是无法具象化的,难以理解的。使用网络数据处理工具能解决这一问题,但是目前该领域的优秀工具使用起来步骤繁琐,在学习协议之前需要学习如何使用该工具,有着“先有鸡还是先有蛋”的矛盾,不适合网络协议的初学者使用。
    因此,本着开源的精神来做一款工具,帮助了解、学习网络数据的传输、网络协议和网络结构。本毕业设计软件是基于OSI(Open System Interconnection)参考模型以及一些常用的网络协议,采用C++语言,使用MFC库、WinPcap库和Visualstudio 2012作为开发工具编写本软件。本系统操作界面简单大方,功能较为齐备。是一个简单实用的网络数据处理软件,本系统具有以下基本功能:
    本机网卡扫描/选择,混杂模式抓包,协议过滤,数据包分析,数据包导入导出,自制ARP包。
    本软件的目的重在抓取经过本机网卡的数据包,对其进行加工处理、分析展示,供初学者学习、参考。
    2 网络数据处理工具需求分析2.1 网络数据处理工具的需求分析对于网络数据的处理,大部分软件或系统是不会直接读写该数据包的每一位的,因为有了各大协议的存在,它们只需要建立在OSI七层模型中的应用层就足够了。通过协议的转换,软件或系统可以从整个数据包中只读取需要的相应字段,之后再进行相应的数据处理。因此,在这里要说的是很重要的一部分功能,即捕获数据包。
    业务需求:

    满足网络管理员对网络内流量的实时观察。满足学生对网络协议的学习和练习使用。扫描本机网卡和本网段内活跃主机信息。满足对网卡内流过的数据读写操作 。
    性能需求:

    网卡混杂式捕获不造成2G内存计算机宕机。文件磁盘IO操作不造成软件奔溃。任何操作不造成GUI交互无响应。并发不造成内存泄露等问题。程序流程合理,不会造成逻辑上的死循环。
    可靠、可用性需求:

    在规定的条件下,在规定的时间内,软件不引起系统失效的概率。在规定的时间周期内,在所述条件下程序执行所要求的功能的能力。
    2.2 网络数据处理工具的可行性分析在大部分传输介质上,网络信息的传输是有被截获、监听的可能性的。在网络上,实施监听最好的地方是在网关、路由器和防火墙一类的位置,因为这些位置是网络数据的汇聚节点,经常有大量的数据经过它们,但它们的防御措施也相应得做地更好。大多数黑客用来监听网络传输的地方是以太网中任何一台连网的主机上,因为这是监听最方便的地方,数量多、防御能力低。因此,本工具也打算从个人计算机入手,进行网络数据处理的第一步:网络监听。下面,将主要从经济可行性、技术可行性、运行可行性和操作可行性等方面进行分析本毕业设计。
    2.2.1 经济可行性开发该软件所需的相关资料可以通过文献资料和网络进行调查采集,所需的软件系统、硬件平台等都易于获得,无需特殊工具,开发成本低,简单易实现,从经济角度来看,开发该软件经济可行。
    2.2.2 技术可行性因为以太网是一种广播型的网络,且目前人们使用的因特网多是使用以太网技术来实现的,所以大多数网络数据的截获是可以在以太网上实现的,其中可以分为共享式以太网监听和交换式以太网监听。因此,从技术角度来说,以某一网络域中的计算机为节点,监听该域网络,这种方式是可行的。
    2.2.3 运行可行性运行性是对组织结构的影响。该软件界面简洁,操作十分简单,只需要相应的windows系统和动态库(都是可以下载获得的)就可以运行起来,所以从运行角度上看,该软件是可行的。
    综上所述,该系统的开发从经济、技术、运行等方面完全可行。
    2.2.4 操作可行性分析开发所采用的工具是VisualStudio 2012,开发出的应用程序在windows系统的计算机上,以窗口的方式展现,接收鼠标和键盘的输入,以显示器为输出,符合当代计算机使用者的使用习惯,方便人们对网络数据的监视和处理,简单易上手。所以此软件在操作上是可行的。
    3 网络数据处理工具开发相关知识3.1 MFC框架的介绍MFC(Microsoft Foundation Classes)是微软基础类库的简称,MFC提供了面向对象的框架,采用面向对象技术,将大部分的WindowsAPI 封装到C++类中,以类成员函数的形式提供给程序开发人员调用。

    参见微软定义:https://msdn.microsoft.com/en-us/library/d06h2x6e.aspx
    3.2 OSI模型简介OSI(Open System Interconnection,开放系统互连)七层网络模型被称为开放式系统互联参考模型 ,是一个人为抽象的定义,它把网络概念从逻辑上分为了七层。 OSI 七层模型是一种框架性的设计,建立七层模型的主要目的,是为解决异种网络互连时所遇到的兼容性问题,分层的左右就是起到了“低耦合,高聚合”的效果。其最主要的功能就是帮助数据在不同类型的主机间实现传输。它的最大优点是将服务、接口和协议这三个抽象概念清晰地区分开来,通过七个层次化的结构模型使不同的系统不同的网络之间进行可靠的设备通讯。


    物理层:提供为建立、维护和拆除物理链路所需要的机械的、电气的、功能的和规程的特性 。数据链路层:负责网络寻址、错误侦测和修正等;提供数据链路的流控。网络层:决定数据的传输目的和寄转,添加网络表头(NH),如IP协议首部。传输层:添加传输表头(TH)形成数据包,如TCP协议首部。会话层:提供两进程之间建立、维护和结束会话连接的功能。表示层:转换数据格式使其与接收者系统兼容;完成数据转换、格式化和文本压缩。应用层:为应用软件而设定的层,不同软件有不同的通讯方式,如HTTP,FTP等。
    3.3 网络协议简介网络协议是为在计算机网络中进行数据交换而建立的规则、标准或约定的集合,由三个要素组成:

    语义:说明控制信息每个部分的意义。它规定了计算机需要发出何种控制信息,实现的的过程,以及做出何种响应。语法:用户数据与控制信息的相关结构、格式,以及各种意义数据出现的顺序。时序:对事件、协议发生顺序的详细说明。
    人们形象地把这三个要素描述为:语义表示要做什么,语法表示要怎么做,时序表示什么时候做。
    结合OSI模型,可以更好地理解抽象的“网络协议”这个概念,在硬件和软件的双重人为规定下,形成了各大网络协议。
    3.3.1 数据包嵌套形式
    数据通过OSI模型,被分片、包装、嵌套处理之后,最后形成了一个完整、符合以太网协议的数据包。
    3.3.2 以太网协议首部说明
    对应程序中的数据结构
    struct eth_hdr{ u_char eth_tar[6]; u_char eth_src[6]; u_short eth_op;};
    3.3.3 IP协议首部说明
    字节和数字的存储顺序是从右到左,依次是从低位到高位(Big Endian),而 网络存储顺序是从左到右,依次从低位到高位(Little Endian)。

    版本:占第一个字节的高四位。头长度:占第一个字节的低四位。
    服务类型:前三位为优先字段权,现已忽略。接下来四位用来表示最小延迟、 最大吞吐量、最高可靠性和最小费用。
    封包总长度:整个 IP 报长度,单位为字节。
    存活时间:就是封包的生存时间。
    协议:定义了数据的协议,分别为:TCP、UDP、ICMP 和 IGMP。定义为:
    #define PROTOCOL_TCP 0x06#define PROTOCOL_UDP 0x11#define PROTOCOL_ICMP 0x06#define PROTOCOL_IGMP 0x06
    检验和:首先将该字段设置为0,然后将IP头的每16位进行二进制取反求和,将结果保存在校验和字段。
    来源IP地址:将来源IP地址看作是32位数值则需要将网络字节顺序转化位主机字节顺序。
    目的IP地址:将目的IP地址看作是32位数值则需要将网络字节顺序转化位主机字节顺序。

    在网络协议中,IP协议是面向非连接的,所谓的面向非连接就是传递数据时,不检测网络是否连通,所以IP协议是不可靠的数据报协议。它主要负责在主机之间寻址和选择数据包路由。
    对应程序中的数据结构
    typedef struct ip_header{ u_char ver_ihl; // 版本 (4 bits) + 首部长度 (4 bits) u_char tos; // 服务类型(Type of service) u_short tlen; // 总长(Total length) u_short identification; // 标识(Identification) u_short flags_fo; // 标志位(Flags) (3 bits) + 段偏移量(Fragment offset) (13 bits) u_char ttl; // 存活时间(Time to live) u_char proto; // 协议(Protocol) u_short crc; // 首部校验和(Header checksum) ip_address saddr; // 源地址(Source address) ip_address daddr; // 目的地址(Destination address) u_int op_pad; // 选项与填充(Option + Padding)}ip_header;
    3.3.4 ICMP协议首部说明

    类型:一个8位类型字段,表示ICMP数据包类型(类型繁多,不一一列举)。代码:一个8位代码域,与类型组合,表示指定类型中的一个功能。如果一个类型中只有一种功能,代码域置为0。检验和:数据包中ICMP部分上的一个16位检验和。
    对应程序中的数据结构
    typedef struct _ICMPHeader{ UCHAR icmp_type; //消息类型 UCHAR icmp_code; //代码 USHORT icmp_checksum; //校验和 //下面是回显头 USHORT icmp_id; //用来惟一标识此请求的ID号,通常设置为进程ID USHORT icmp_sequence; //序列号 ULONG icmp_timestamp; //时间戳}ICMPHeader,*PICMPHeader;
    3.3.5 TCP协议首部说明

    TCP源端口(Source Port):源端口和IP地址的是用来标识报文的发送地址。TCP目的端口(Destination Port):16位的目的端口域指明报文接收计算机上的应用程序端口地址。序列号(Sequence Number):TCP连接发送方向接收方发送的封包顺序号,用于重组分片。确认序号(Acknowledge Number):接收方应答顺序号。头长度(HeaderLength):表示TCP头的双四字节数,转化为字节个数需乘以四。URG:紧急指针标志位,0为不使用,1为使用。ACK:请求/应答状态标志位。0为请求,1为应答。PSH:以最快的速度传输数据标志位。RST:连线复位标志位,先断开,再重建。SYN:同步连线序号标志位,用来建立连线。FIN:结束连线标志位。0是结束连线请求,1表示结束连线。窗口大小(Window):目的机使用十六位的域告诉源主机,它想收到的每个TCP数据段大小。校验和(CheckSum):这个校验和和IP的校验和不同,对首部数据和封包内容都进行校验。紧急指针(Urgent Pointer):当URG为1时才有效。
    对应程序中的数据结构
    typedef struct _tcp_hdr { unsigned short src_port; //源端口号 unsigned short dst_port; //目的端口号 unsigned int seq_no; //序列号 unsigned int ack_no; //确认号 #if LITTLE_ENDIAN unsigned char reserved_1:4; //保留6位中的4位首部长度 unsigned char thl:4; //tcp头部长度 unsigned char flag:6; //6位标志 unsigned char reseverd_2:2; //保留6位中的2位 #else unsigned char thl:4; //tcp头部长度 unsigned char reserved_1:4; //保留6位中的4位首部长度 unsigned char reseverd_2:2; //保留6位中的2位 unsigned char flag:6; //6位标志 #endif unsigned short wnd_size; //16位窗口大小 unsigned short chk_sum; //16位TCP检验和 unsigned short urgt_p; //16为紧急指针 }tcp_hdr;
    3.3.6 UDP协议首部说明

    源端口(SourcePort):十六位的源端口域包含初发送方端口号。源端口和IP地址表示了报文的返回地址。目的端口(DestinationPort):六位的目的端口域定义传输的目的地。这个端口指明报文接收计算机上的应用程序端口地址。封包长度(Length):UDP首部和封包数据的总长度。校验和(CheckSum):和TCP校验和一样,不仅对首部数据进行校验,还对封包的内容进行校验。
    对应程序中的数据结构
    typedef struct udp_header{ u_short sport; // 源端口(Source port) u_short dport; // 目的端口(Destination port) u_short len; // UDP数据包长度(Datagram length) u_short crc; // 校验和(Checksum)}udp_header;
    3.3.7 ARP协议首部说明

    硬件类型:指明了硬件接口类型,以太网的值为1;协议类型:指明了高层协议类型,IP为0x0800;硬件地址长度,协议长度:硬件地址和高层协议地址的长度,这样ARP报文就可以在任意硬件和任意协议的网络中使用;操作字段:报文的类型,ARP请求为1,ARP响应为2,RARP请求为3,RARP响应为4……;发送方硬件地址(0-3字节):源主机硬件地址的前3个字节;发送方硬件地址(4-5字节):源主机硬件地址的后3个字节;发送方IP地址(0-1字节):源主机硬件地址的前2个字节;发送方IP地址(2-3字节):源主机硬件地址的后2个字节;目的硬件地址(0-1字节):目的主机硬件地址的前2个字节;目的硬件地址(2-5字节):目的主机硬件地址的后4个字节;目的IP地址(0-3字节):目的主机的IP地址。
    对应程序中的数据结构
    //28字节的ARP头typedef struct _ARPHeader { USHORT hrd; //硬件地址空间,以太网中为ARPHRD_EHER USHORT eth_type; //以太网类型,ETHERTYPE_IP UCHAR maclen; //MAC地址的长度,为6 UCHAR iplen; //IP地址的长度,为4 USHORT opcode; //操作代码,ARPOP_REWUEST为请求,ARPOP_REPLY为响应 UCHAR smac[6]; //源MAC地址 UCHAR saddr[4]; //源IP地址 UCHAR dmac[6]; //目的MAC地址 UCHAR daddr[4]; //目的IP地址}ARPHeader,*PARPHeader;
    3.4 WinPcap简介本工具所使用的WinPcap大大降低了本软件的开发难度。由于Window操作系统没有提供可以直接捕获网络传输数据包的API,尽管提供了一些内核模块接口,但存在着严重的局限性。例如:IP Filter Driver只运行在 Windows 2000 下,且仅支持IP协议;对内核的操作和对文件I/O的操作都带来了设计上和使用上的复杂度飙升;Windows内核的不透明等。由于这些局限,使得Windows平台下的网络安全管理和分析工具无论在数量上,还是质量上都与Unix下有很大的差距。为此,微软研究院赞助了意大利一家开发机构,为Win32平台底层网络分析开发了一套有力且易扩展的体系结构——WinPcap,它将Unix支持的数据包捕获功能移植到Win32系统中,从而弥补了Windows操作系统在网络分析方面的欠缺。而且WinPcap完全开放,提供源代码,同时它提供了与Libpcap相兼容的函数接口,是的程序代码在不同操作系统平台间的可读性大大提升。
    WinPcap是一个基于Win32平台的,用于捕获数据包并进行网络分析的体系结构。它包括了一个内核级的数据包过滤器,一个低层的动态链接库(packet.dll),一个高层的,依赖于系统的动态链接库(wpcap.dll)。其结构图如下所示:

    4 网络数据处理工具的设计4.1 模块结构本网络数据处理工具是一般的PC桌面软件,由于都是二进制的数据分包,不适合使用数据库来存储数据,因此没有使用数据库,而是直接转储为.CAP后缀文件。开发之初,在构思软件时设计了一套软件架构,从底层网卡获取数据开始,由WinPcap混杂式获取数据包,交由Filter层过滤数据,经过过滤层之后的数据由提供给用户界面的各个功能模块进行相应的数据处理:导出、展示、包装等。抛开该网络数据监听功能,还提供一个手动创建数据包并发送的功能接口,并将这些功能都统一交付给捕获器处理。

    4.2 流程关系本程序流程其实不复杂,只是有一些流程需要有上下级关系,有一些流程需要并行,有一些流程需要串行互斥。正如下图所示,和捕获网络数据包有关的流程都必须先设置需要捕获的本机网卡,因为,就算一台PC,也可能会有多张网卡的情况(目前而言,这种情况是多数)。接着,开始捕获的数据包会经过过滤层,也就是自定义的过滤条件。然后被程序中的分派器分配到不同的协议分析模型下,不同的协议分析模型会对相应的协议进行相同格式地解析,并将解析后的数据交由下一级处理:展示和转储。
    上述所涉及到的流程是数据包捕获的主流程,下面说下其他功能流程:

    发包流程:在发包时,也必须选择用来发包的本机网卡(基本上都是以太网卡),然后手动填写相应协议的数据包格式,点击发包即可。这里需要对该协议每一个字段有一定的了解;导入导出流程:在导入导出流程中,不需要选择网卡(当然选择也是可以的),之后就是对文件的I/O操作和MFC的展示了,就不再多说;扫描本网段主机流程:同样需要选择网卡,然后在菜单中选择“scanhost”功能。由于本功能需要花费一定时间,同时不能与本工具的任意一个功能同步进行,所以需要等扫描结束,或者退出本次扫描,才能进行其他操作。

    4.3 功能设计及分析4.3.1 网卡扫描/选择
    在软件打开的界面,选择WinPcap扫描出来的本机网卡。只有在选择了网卡之后,才能进行相应的数据操作。下拉条中列出了本机的所有网卡,选中一块网卡,下方的静态文本框会显示相应的描述信息。关键代码如下所示:
    /***********************adapterDlg.cpp***********************//** * 单击combo box中条目时的回调函数 */void CAdapterDlg::OnCbnSelchangeAdapterCombo(){ //一些变量的声明和条件判断 … // 利用WinPcap遍历本机所有网卡,将用户选择的网卡存入adp_info对象中 for (adp_info.usedDevs = adp_info.allDevs; adp_info.usedDevs; adp_info.usedDevs = adp_info.usedDevs->next){ if (name == adp_info.usedDevs->name) { CString content = "Name: " + name + "\r\nDescription: " + adp_info.usedDevs->description; GetDlgItem(IDC_ADAPTER_EDIT)->SetWindowText(content); /** * 将具体信息存入适配器对象中 */ if ((adp_info.ahandler = pcap_open(adp_info.usedDevs->name, 65535, PCAP_OPENFLAG_PROMISCUOUS, 1000, NULL, adp_info.errBuf)) == NULL) { MessageBox("Open the adapter failed."); } break; } }}
    4.3.2 抓包
    在选中网卡后,即可开始抓包。每捕捉到一条不满足过滤条件(不会被过滤)的数据包,就会在List Ctronller中显示,并根据实际封包数据,显示相应信息。如果单机选中某一条目,还会在下方的两个静态文本框内显示更详细的十六进制内容。如果双击条目,还能弹出详细信息框。
    本功能在实现的时候,在多个层有不同功能代码。关键代码如下所示:
    /***********************captureDlg.cpp***********************//** * 开始、停止抓包 */void CcaptureDlg::OnBnClickedBegStp(){ //一些变量的声明和条件判断 … //清空列表 m_listctl.DeleteAllItems(); //锁定“扫描主机”菜单 GetMenu()->GetSubMenu(2)->EnableMenuItem(0, MF_BYPOSITION | MF_DISABLED | MF_GRAYED); //dump出去一个缓存包,这样内存就不用一直添加数据包内容了 hThreadCapture = (HANDLE)_beginthreadex(NULL, 0, (THREAD_PROC)captureDataInfo, (LPVOID)this, 0, NULL); // 设置界面、功能锁定 …}
    正如代码中判断的一样,根据adp_info对象中的isCapturing标志来表示是否进行多线程数据包捕获工作和转储、展示数据包工作。在开始捕获时,利用新的工作线程进行数据捕获处理,GUI线程依旧对用户提供快速响应。
    /***********************captureServiceImpl.cpp***********************//** * 显示捕获的数据包内容 * * @param lParam * @return */unsigned WINAPI captureDataInfo(LPVOID lParam){ … //打开导出文件 dumpfile = pcap_dump_open(adp_info.ahandler, lp->CapFilePath); … while ((res = pcap_next_ex(adp_info.ahandler, &(adp_info.header), &(adp_info.pkt_data))) >= 0 && adp_info.isCapturing){ // 读取并展示 readAndDisplay(res, lp, dumpfile, nIndex); } pcap_dump_close(dumpfile); return 1;}
    /** * 展示数据 * * @param res * @param lp * @param dumpFile * @param nIndex */void readAndDisplay(int res, CcaptureDlg *lp, pcap_dumper_t *dumpfile, long &nIndex){ // 一些变量声明和条件判断 … switch (ntohs(eth->ehr_ftp)) { case ETHERTYPE_ARP: // ARP协议处理 … break; case ETHERTYPE_REVARP: //RARP协议处理 … break; case ETHERTYPE_IP: //IP协议簇处理 … switch (ip_hdr->proto) { case IPTYPE_ICMP: // ICMP协议处理 … break; case IPTYPE_TCP: // TCP协议处理 … break; case IPTYPE_UPD: // UDP协议处理 … break; default: // 未知协议处理(未添加的协议) … break; } break; default: // 未知协议处理(未添加的协议) … break; } … //记录在缓存文件中 if(dumpfile != NULL){ pcap_dump((u_char *)dumpfile, adp_info.header, adp_info.pkt_data); } // 发送自定义消息WM_UPDATE_LIST给captureDlg SendMessageTimeout(lp->m_hWnd, WM_UPDATE_LIST, (WPARAM)&Data, 0, SMTO_BLOCK, 1000, NULL);}
    4.3.3 过滤
    过滤格式目前只支持当前这几种协议(分析功能也是如此)。可以多选需要查看的协议和根据接收包的IP地址来进行过滤。
    协议的过滤采用标志位设定的方式,相应的单选框对应相应的协议类型。
    4.3.4 分析
    双击软件主界面的任意一条捕捉的信息条目,将会弹出此“细节分析”对话框。正如其命名,其中包含了许多细节信息,例如:被捕捉的时间、数据包长度、源MAC地址、目标MAC地址、具体协议类型、协议数据内容(十六进制形式显示)等,帮助了解各种协议的数据格式和当前记录的详细数据内容。
    协议的分析其实是对主界面的List组件的单击和双击消息进行了重写,并分派给对应的协议分析器来分析对应的协议为相同的格式,不同的协议展示方式继承自同一个“分析父类”,以便程序扩展:
    /***********************AnalyseDispatcher.cpp***********************//** * 分析数据入口,分发数据处理器 * * @param res * @param data * @param header * @return */bool AnalyseDsipatcher::analyseData(CString &res, const u_char* data, pcap_pkthdr *header = NULL){ // 一些变量的声明 … // 协议头格式化,增加可读性 if (header != NULL) { … } // 以太网头首部数据获取 … // 协议分派,使用组合的设计模式 switch (ptype) { case ETHERTYPE_ARP: analyseService = new AnalyseARPService(); break; case ETHERTYPE_REVARP: res += "RARP datagram\r\n"; break; case ETHERTYPE_IP: analyseService = new AnalyseIPService(); break; default: res += "unknow\r\n"; break; } // 具体分析交由相应协议的分析派生类完成 analyseService->analyse(res, data); delete analyseService; analyseService = NULL; return true;}
    4.3.5 导入导出
    导入导出功能是对此次捕捉的数据转存。其实在本次捕捉时,已经实时dump为一个默认的,名为:capture_data.cap的,WinPcap格式数据包。
    导入导出数据包只是对系统文件的I/O操作,源码如下所示:
    /***********************captureDlg.cpp***********************//** * 导入抓取的数据包文件 */void CcaptureDlg::importData(){ // 一些变量的声明和条件条件判断 … CString extFileCategory = CString("capture data file|*") + dumpFileSuffix + CString("||"); CFileDialog fileDlg(TRUE/*打开文件*/, extFileCategory, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, extFileCategory); fileDlg.m_ofn.lpstrInitialDir = currentDir; if(fileDlg.DoModal()==IDOK){ CString openFilePath = fileDlg.GetPathName(); CapFilePath = openFilePath; DataIOService dataIOServiceInstance(openFilePath.GetBuffer(openFilePath.GetLength()), this); //清空列表 m_listctl.DeleteAllItems(); dataIOServiceInstance.display(); }}
    void CcaptureDlg::exportData(){ //一些变量的声明和条件判断 … CString extFileCategory = CString("capture data file|*") + dumpFileSuffix + CString("||"); CFileDialog fileDlg(FALSE/*写入文件*/, extFileCategory, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, extFileCategory); fileDlg.m_ofn.lpstrInitialDir = currentDir; if(fileDlg.DoModal()==IDOK){ CString outputFile = fileDlg.GetPathName(); DataIOService dataIOServiceInstance; if(false == dataIOServiceInstance.exportData(CapFilePath, outputFile, false)){ MessageBox("Cannot export file!"); } }}
    4.3.6 扫描本网段主机
    如图所示,本软件利用ARP协议,自动遍历本网段内的所有活跃主机,并把它们的MAC值和IP地址列出来。
    对于本网段内主机的扫描,采用了发送UDP报文给本网段内所有主机,在规定时间内查看是否有响应的方式来判断,源码如下所示:
    /***********************scanHostDlg.cpp***********************//** * 将CScanHostService类的构造放在另一个线程中,主线程用来绘窗口,提高用户体验 * * @Param lParam*/unsigned int WINAPI scanThread(LPVOID *lParam){ CScanHostDlg *pScanHostDlgPtr = (CScanHostDlg *)lParam; CScanHostService *hostScan = new CScanHostService(pScanHostDlgPtr); pScanHostDlgPtr->setHostScan(hostScan); … return 0;}
    /***********************scanHostServiceImpl.cpp***********************//** * 利用winsock取得本机MAC和网关MAC,也为后来取得网关IP打下坚实的基础!*/void CScanHostService::getMAC(){ //一些变量的声明和条件判断 … //接收线程开始 _beginthread(recvThread, 0, (void*)this); WSAStartup(MAKEWORD(2, 2, ), &wsadata); SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0); int err = GetLastError(); SOCKADDR_IN addr; addr.sin_family = AF_INET; addr.sin_port = htons(8888); addr.sin_addr.S_un.S_addr = destIP; sendto(sock, buff, strlen(buff), 0, (SOCKADDR*)&addr, sizeof(SOCKADDR_IN)); // 等待两秒 Sleep(2000); terminate = true; Sleep(1000); WSACleanup(); …}/** * 念念不忘,必有回响 * 接收发送出去的UDP报文,该报文有MAC地址*/void recvThread(void *lp){ //一些变量的声明和条件判断 … while (!np->getTerminate()){ //分析接收到的ARP回应 res = pcap_next_ex(adp_info.ahandler, &header, &pkt_data); if (res == 0) //超时时间到 continue; if (res >0){ mhdr = (EHR*)pkt_data; if (mhdr->ehr_ftp != 0x0008) continue; iphdr = (IP_HDR*)(pkt_data + 14); // 分析本机MAC地址 if (iphdr->daddr[0] == 9 && iphdr->daddr[1] == 2 && iphdr->daddr[2] == 0 && iphdr->daddr[3] == 5){ np->setMac(mhdr->ehr_sha); np->setGatewayMac(mhdr->ehr_tha); np->setGwayFlag(true); break; } } } _endthreadex(0);}
    /***********************scanHostServiceImpl.cpp***********************//** * 扫描主机 */bool CScanHostService::scanHost(){ // 一些变量声明和条件判断 … //打开接收线程 this->scanRevThreadHandle = (HANDLE)_beginthreadex(NULL, 0, (THREAD_PROC)scanRev, (LPVOID *)this, NULL, 0); Sleep(500); //扫描本网段活动主机,逐一遍历 u_int ip = (g_netmask & g_localip) + 1, tmp; u_char tp[4]; do{ tmp = ntohl(ip); memcpy(tp, &tmp, 4); fillARP(tp); pcap_sendpacket(adp_info.ahandler, (const u_char*)&arp, 42); ++ip; Sleep(50); } while ((ip&g_netmask) == (g_netmask&g_localip) && !terminate); …}/** * 主机扫描接收ARP包线程 * * @param lParam */unsigned int scanRev(LPVOID *lParam){ // 一些变量声明和条件判断 … if (pcap_next_ex(adp_info.ahandler, &header, &data) <= 0) continue; … if (htons(eth_hdr->ehr_ftp) == 0x0806) //只接收ARP包 { arp = (ARP*)data; if (ntohs(arp->arp_hdr.arp_op) == 0x0001) //只接收reply ARP包 continue; // 将接收到的特定回响存入“存活主机”队列 CListCtrl *m_list = np->getList(); int nCount = m_list->GetItemCount(); m_list->InsertItem(nCount, ""); m_list->SetItemText(nCount, 0, hostMac); m_list->SetItemText(nCount, 1, hostIp); }…}
    4.3.7 自制ARP包
    根据ARP的格式,填写正确的ARP信息,点击start发送。
    /***********************manualDlg.cpp***********************//** * 发送数据包线程 * @param lParam*/unsigned int WINAPI sendPackThread(LPVOID lParam){ CManualDlg *lp = (CManualDlg*)lParam; switch (lp->choice) { case ARP_TYPE: if (lp->fillARP() == false) break; lp->sendPacket(); break; // 其他协议 … } lp->isSending = false; lp->m_bn_beg_stp.SetWindowText("Start"); return 1;}/** * 启动按钮*/void CManualDlg::OnBnClickedStart(){ … //锁定“扫描主机”菜单 lp_Main->GetMenu()->GetSubMenu(2)->EnableMenuItem(0, MF_BYPOSITION | MF_DISABLED | MF_GRAYED); m_bn_beg_stp.SetWindowText("Stop"); //打开发送packet线程 _beginthreadex(NULL, 0, (THREAD_PROC)sendPackThread, (LPVOID)this, NULL, 0);…}
    4.4 网络数据处理工具开发方法软件工程中经常可以遇见的系统开发方法有:结构化开发方法、面向对象的开发方法、可视化开发技术。
    结构化开发方法依照功能分解的原则,自顶向下、逐步求精,最终实现软件功能。它以功能划分为基础,简单、实用。结构化开发方法将系统开发总共分为五个阶段:划定系统开发范围、系统分析、系统设计、系统实现和系统支持。
    面向对象的方法是将现实世界问题向抽象对象空间直接映射,以此实现对现实世界的直接模拟,它以数据为中心,而不是基于对功能的分解,所以软件结构相对来说稳定,软件的重用性、可靠性、可维护等特性都不错。
    可视化开发技术是由图形用户界面的兴起而发展起来的,是通过事件驱动实现应用软件的。良好的图形库简化了图形用户界面的设计和编码工作,将开发人员的注意力主要集中在程序的执行逻辑和工作流程上。
    对于本软件的可扩展性而言,继承和组合在各个协议的数据处理上有了单独的派生类,便于新增和减少对其他协议的支持。
    4.5 定义规范虽然这是单独完成的一个项目,但是作为一名程序员,是需要有行业规范来约束自己,帮助自己融入团队的。代码规范就是很重要的一点。
    4.5.1 代码注释规范1.头文件注释
    所有头文件开头都要加注释,写明文件创建时间、作者、用途、概述等。格式如下所示:
    /* * 属于:个人 * 创建日期:2016.5.12 * 作者:XXX * 功能说明:XXXXXXXXXXXXXXXXX */
    2.函数注释
    所有函数一定要注明函数的作用、参数的作用、返回值的作用等。格式如下所示:
    /**<summary> * </summary> * @param * @param * @return */
    3.常量变量注释
    所有的常量和变量,无论全局或者局部只要在代码中起关键作用的必须都加上注释。格式如下所示:
    // 变量或常量作用
    4.测试用例文件存放位置
    在项目的test目录下,新建与功能逻辑相同的目录结构树,在对应的目录下存放对应的测试用例;
    5.测试用例命名
    文件名:[被测试类名]Test.cpp格式,对应的类名为[被测试类名]Test,具体实现按照CppUnitTestFramework框架规定。
    4.5.2 代码命名规范采用驼峰命名法。

    类命名:英文,单词首字母大写,剩余字母小写。在意义上表达该类的作用,如:CaptureDlg,是捕获对话框类。函数命名:英文,首字母小写,剩余单词首字母大写,剩余字母小写。在意义上表达该函数的作用,如:analyseData,是分析数据的函数。
    5 网络数据处理工具测试与调试5.1 程序调试在开发软件的过程中无法避免会产生一些问题,除开最常见的环境错误,语法错误之外,还在实际的开发过程中遇到了许多其它的问题。首先,在构建整个软件主体的过程中,主要采用模块化的构建方式。从磁盘文件组织上来看,每一个独立的功能都是一个独立的文件,单独编写,单独测试,从物理上就已经把每一个单独的功能分开,减少因文件互相耦合度过高而出错的概率。
    在测试与调试过程中主要的调试工具是Visual Studio的错误语法提示和Debug断点单步调试,用来快速定位错误代码,和显示错误内存信息。
    5.2 工具的测试5.2.1 测试的意义及目的软件测试的目的,是在真实的软件运行环境下通过与软件起初的需求定义作比较,发现软件与软件/子软件设计文档和软件开发合同约定不符或与之矛盾的地方。
    5.2.2 测试框架由于开发集成工具是VisualStudio 2012,并采用MFC框架,所以打算使用Microsoft的原生测试框架:Microsoft::VisualStudio::CppUnitTestFramework。
    5.2.3 测试步骤测试是一门学问,有多重分类方式和多重测试技巧。由于本软件目前的功能几乎不涉及与其他主机的交互,同时也不需要对其他用户开放,只是对个人电脑的网卡数据包抓取和处理,因此,本工程只打算通过手工测试、单元测试、集成测试和系统测试来对系统的功能和稳定来做保证。
    开始是单元测试,集中对用源代码实现的每一个功能单元进行测试,尽量争取覆盖每一个函数,最终组合,检查各个程序模块是否正确地实现了所描述的功能。
    集成测试是把已测试过的模块拼凑、组装起来,主要是针对软件体系结构的构造进行测试。
    系统测试把已经经过确认的软件纳入实际运行环境中(分别在Windows XP、Windows 7、Windows 8中运行),与其它系统成份组合在一起进行环境相关的测试。
    5.3 测试用例设计本项目的测试用例根据功能模块进行划分,在业务逻辑代码同等级的目录结构下新建对应[ClassName]Test.cpp用例文件。

    这里列举两个例子:
    5.3.1 适配器选择测试用例#ifdef _TEST… TEST_CLASS(AdpInfoImplTest){ // 预备和清理工作,宏定义 TEST_CLASS_INITIALIZE(AdpInfoImplTestInit){ … } TEST_CLASS_CLEANUP(AdpInfoImplTestEnd){ … } TEST_METHOD(showAllDevs){ … // 断言 Assert::IsNotNull(dlg);… }#endif
    该测试用例对应于sourcecode->biz->analyse->AdpInfoImpl.cpp,被存放于test->biz->analyse->AdpInfoImplTest.cpp。需要注意TEST_CLASS_INITIALIZE、TEST_CLASS_CLEANUP、TEST_CLASS等宏的使用,具体参阅MSDN文档,此处统一使用了预编译字段_TEST,用来区分Debug模式、Release模式和自定义的unit test编译模式。
    5.3.2 分析功能测试用例… TEST_CLASS(AnalyseDsipatcherTest){ /** * header数据生成器 */ struct pcap_pkthdr *ARPHeaderGenerator(){ … } /** * 期望结果 */ CString ARPResGenerator(){ … } /** * 数据分派测试,ARP协议测试具体期望结果 */ TEST_METHOD(analyseARPDataTest){ // 数据准备 … analyser.analyseData(res, data, headerPtr); CString expectRes(this->ARPResGenerator()); // 结果断言 Assert::AreEqual(expectRes, res); } …#endif
    该测试用例对应于source code->biz->analyse-> AnalyseDsipatcher.cpp,被存放于test->biz->analyse-> AnalyseDsipatcherTest.cpp。这里展示了测试过程中的几个阶段:数据准备阶段,功能执行阶段和结果验证阶段,是比较典型、常规的测试步骤。
    由于目前代码中较为繁重的是图形处理过程,所以单纯的业务逻辑代码被覆盖后,显示的测试用例覆盖率不高,目前为34%,还有待提升。
    5.4 测试数据在每一个功能完成之后都会进行分模块的功能测试,以确保这个功能模块的正常运行,同时也是为了方便之后功能的整合。通过选择某些具有代表性的数据和某些边界数据来测试可以很好的测试程序的健壮性,以保证程序的良好运行。下表是本软件中选择某些主要功能所做的测试以及出现的问题和解决方案,以及应用此解决方案后产生的效果。



    功能模块
    测试方法
    测试数据
    测试出现过的问题
    解决方法
    结果




    网卡扫描
    在不同网卡数量机器上运行本软件
    各种网卡


    正常


    扫描本网段主机
    压力测试

    并发执行时,会出现重复机器条目或者程序崩溃
    设置本功能和其他功能以及自身为串行
    正常


    自制ARP包
    填写ARP欺骗包内容并发送
    ARP欺骗包


    正常


    导入导出
    手动测试
    随机几次实时抓包结果


    正常


    过滤
    设置过滤条件
    随机几次实时抓包


    正常


    抓包
    正常上网抓包
    随机几次上网过程


    正常



    结 论开源网络数据处理工具程序包采用了C++技术开发,利用MFC库、WinPcap库使程序界面和Windows系统接口对接,加入多线程并发技术以及多种设计模式,并以企业级规范开发。本程序代码已推送到github、gitlab以及coding.net上开源,并在CSDN上写明详细开发过程和程序代码介绍。如有意向者,可以阅读后提交请求共同开发。
    对于网络协议以及计算机对网络数据的处理,我一直以来都抱有强烈的好奇和热烈的兴趣,很想对计算机网络和操作系统一探究竟。于是,我就动手收集相关资料,从最基础的概念看起。在不断地积累中,我对计算机的认识和网络的认识越来越清晰,就好像自身如同一串二进制比特流,经过物理层,被网卡读取,被上层系统一层层剥去外壳,展示出自己的数据。我将本程序中的资料罗列在了纸上,大概都有10多张A2的白纸那么多,沉甸甸的,特别有成就感。“纸上得来终觉浅”,在明白了这些协议的关系和一些网络的概念后,我动手写了这个程序,来帮我更加直观和清晰地认识我从书上的知识。一边写,一边温习和学习新知识的过程是非常愉悦的,在完成这个工程后,趁着知识记忆深刻和项目脉络清晰,又高高兴兴地在网络上描述了自己的代码,将这些天来的成果展示给大家。
    参考文献[1] Stanley B. Lippman , Josée Lajoie , Barbara E. Moo. C++ Primer 第5版[M].
    北京:电子工业出版社, 2013.9
    [2] [美]ames F.Kurose / [美] Keith W. Ross.计算机网络(第4版):自顶向下方法. 机械工业出版社. 2009.11
    [3] Programming Applications for Microsoft Windows [M], Microsoft Press (October23, 1999)
    [4] msdn.microsoft.com .Microsoft API and reference catalog [OL]. 微软开发手册
    [5] www.winpcap.org/docs/docs_412/html/main.html. WinPcapDocumentation [OL]
    [6] https://www.wireshark.org [OL].WireShark官网
    [7] http://www.http-sniffer.com [OL].Sniffer官网
    [8] Beard, Jonathan C., Peng Li, and Roger D. Chamberlain. “RaftLib: a C++template library for high performance stream parallel processing.”InProceedings of the Sixth International Workshop on Programming Models andApplications for Multicores and Manycores, pp. 96-105. ACM, 2015.
    [9] 龙昱程. “基于 TCP 协议 的应用层协议设计” [M]. 信息通信 5 (2015): 69-70.
    [10] 李玉石. “基于 C++ Builder 的网络调试助手设计与实现” [M]. 品牌与标准化 5 (2015): 85-87.
    [11] 刘志蕾, 陈艳花, 冯志亮, 乔森, 张芳.”基于 TCP/IP 协议的测控系统的开发” [M]. 计算机测量与控制 23, no. 10 (2015): 3418-3420.
    [12] https://git-scm.com/documentation [OL].Github官方手册
    [13] 侯俊杰 . 深入浅出MFC[M] . 华中科技大学出版社 .2001.1
    [14] Kevin R. Fall. TCP/IP Illustrated, Vol. 1: The Protocols[M] 2nd Edition. Addison-WesleyProfessional. November 25, 2011
    [15] W. Richard Stevens. TCP/IP Illustrated, Vol. 2: The Implementation[M] 1stEdition. Addison-Wesley Professional. February 10, 1995
    [16] W. Richard Stevens. TCP/IP Illustrated, Vol. 3: TCP for Transactions, HTTP,NNTP, and the UNIX Domain Protocols[M] 1st Edition. Addison-Wesley Professional.Addison-Wesley Professional. January 29, 1996
    [17] 董越. 未雨绸缪:理解软件配置管理[M].电子工业出版社.2008-5
    [18] Ron Patton. 软件测试(第二版)[M].机械工业出版社.2006-4
    3 评论 57 下载 2018-11-25 09:14:47 下载需要20点积分
  • 基于PHP的网上商城

    第一章 需求分析1.1 引言伴随着Internet的蓬勃发展,网络购物中心作为电子商务的一种形式正以其高 效、低成本的优势,逐步成为新兴的经营模式和理念,人们已不再满足于信息浏览 和发布,而是渴望着能够充分享受网络所带来的更多的便利。的确,客户足不出户 便可以方便快捷的选购自己喜欢的商品,这正是网络购物中心为客户带来的好处。 网络商城将传统的商务流程电子化、数字化,一方面以电子流代替了实物流,可以大量减少人力、物力,降低了成本;另一方面突破了时间和空间的限制,使得 交易活动可以在任何时间、任何地点进行,从而大大提高了效率网络商城所具有的 开放性和全球性的特点,为企业创造了更多的贸易机会。网络商城使企业可以以相 近的成本进入全球电子化市场, 使得中小企业有可能拥有和大企业一样的信息资源, 提高了中小企业的竞争能力。网络商城重新定义了传统的流通模式,减少了中间环节,使得生产者和消费者的直接交易成为可能,从而在一定程度上改变了整个社会 经济运行的方式。网络商城一方面破除了时空的壁垒,另一方面又提供了丰富的信 息资源,为各种社会经济要素的重新组合提供了更多的可能,这将影响到社会的经 济布局和结构。 现在的购物商场成蓬勃向上发展的。
    1.2 需求分析一个网络购物系统,首先我们要保证客户能够很方便进行商品选择,系统应该具有分类选择商品功能,系统要实现购买功能。在系统的后台,管理员能够管理商品,商品分类,以及客户购买订单。 因此分析,本系统主要由前台和后台两部分组成,前台为客户端,顾客可以在此处购买商品,后台为商品管理端,实现对商品和订单的管理。
    第二章 系统分析2.1 开发环境根据用户的需求和实际的考察与分析,确定商城的开发环境,具体如下:

    服务器:从稳定性、广泛性及安全性方面综合考虑,采用市场主流的Web服务器软件Apache服务器
    数据库:采用最受欢迎的开源SQL数据库管理系统和被誉为PHP黄金搭档的MySQL
    开发框架:选用具有快速、兼容、开源、简单易学等特点的轻量级国产PHP开发框架—ThinkPHP

    2.4 系统运行环境该网上商城可运行在分辨率为1920×1080的chrome浏览器下。
    2.3 功能结构商城分为前台模块和后台模块。下面分别给出前、后台的功能结构图。


    2.4 目录结构目录结构即为think php 目录结构,再次不做介绍。
    第三章 数据库设计3.1 商品分类表(itcast_category)


    字段名
    数据类型
    描述




    cid
    Int unsigned
    主键ID,自动增长


    cname
    varchar(20)
    商品分类名称


    pcname
    varchar(20)
    父类分类名称



    3.2 商品表(itcast_goods)


    字段名
    数据类型
    描述




    gid
    varchar(255)
    主键ID,自动增长


    gname
    varchar(255)
    商品名称


    price
    int
    商品价格


    thumb
    varchar(255)
    商品图片路径


    status
    Enum(‘no’,’yes’)
    是否上下架,上架为yes,否则为no


    description
    text
    商品描述


    stock
    int
    商品库存


    cid
    Int unsigned
    商品分类ID


    sales
    int
    商品销量


    turn
    Int unique
    排序码,自动增长



    3.3 会员信息表(itcast_member)


    字段名
    数据类型
    描述




    mid
    Int unsigned
    主键ID,自动增长


    user
    varchar(20)
    会员昵称


    email
    varchar(30)
    会员电子邮件地址


    pwd
    char(32)
    会员登陆密码


    birthday
    date
    会员生日



    3.4 会员收货地址表(itcast_address)


    字段名
    数据类型
    描述




    aid
    Int unsigned
    主键ID,自动增长


    mid
    Int unsigned
    会员ID


    consignee
    varchar(20)
    收货人姓名


    phone
    varchar(11)
    电话号码


    postcode
    varchar(6)
    邮政编码


    address
    varchar(255)
    收货地址


    freight
    int
    运费



    3.5 购物车表(itcast_shopcart)


    字段名
    数据类型
    描述




    scid
    Int unsigned
    主键ID,自动增长


    mid
    Int unsigned
    会员ID


    addTime
    timestamp
    加入购物车时间


    gid
    varchar(255)
    商品ID


    num
    tinyint(3)
    商品数量



    3.6 购买记录表(itcast_record)


    字段名
    数据类型
    描述




    rid
    Int unsigned
    主键ID,自动增长


    mid
    Int unsigned
    会员ID


    time
    timestamp
    购买时间


    gid
    varchar(255)
    商品ID


    num
    tinyint(3)
    商品数量


    price
    Int unsigned
    商品单价



    3.7 折扣商品表(itcast_discountgoods)


    字段名
    数据类型
    描述




    dgid
    Int unsigned
    主键ID,自动增长


    gid
    varchar(255)
    商品ID


    discount
    int
    折扣



    第四章 具体功能实现4.1 前台具体功能4.1.1 公共部分

    显示登录后的用户昵称、退出登录
    分类搜索商品
    进入各个功能模块
    登录按钮
    登录后查看购买记录、进入购物车



    购物车简要信息:总价(折扣前)、数量
    商品分类,点击即可查看该分类下的商品



    显示当前销量最高的商品


    网站相关信息
    联系方式

    4.1.2 主页
    滚动广告


    推荐商品


    新品推荐

    4.1.3 商品列表页
    商品列表页
    例如,在任一页面的导航栏的搜索框中输入关键词——手机,选择“手机”分类,点击“搜索”按钮。

    之后就可进入商品列表页,该页面显示了所有在指定分类下与关键词相关的商品。

    此时可点击排序下拉菜单,对商品进行排序(默认按照新品排序),例如按照价格升序排列

    类似还可进行:“新品”、“价格降序”、“销量”方式排序。

    折扣商品列表页
    若点击导航栏的“特价优惠”链接,即进入折扣商品列表页

    4.1.4 商品详情页
    查看商品信息
    在任何页面的商品列表点击指定商品,即可查看该商品的详细信息。

    此时,点击“相关商品”按钮,可查看该与该商品相关的其他商品。
    有两种查看方式:



    加入购物车
    会员登录后,在详情页选择购买数量,点击“加入购物车”按钮即可将选择的商品加入购物车。
    4.1.5 会员注册、登录在任一页面的导航栏点击“登录”按钮,即可进入下图所示页面:

    左部为注册框,右部为登录框。

    注册
    在注册框中输入要创建账户的邮箱地址,点击“创建”按钮

    进入下图所示页面填写会员详细信息。

    点击“注册”按钮,即完成了会员注册操作。

    登录
    在登录页面填写正确的邮箱、密码和验证码,点击“登录”按钮即可成功登录。

    若验证码填写错误,页面将显示如下提示信息,并返回登录页面。
    若用户名或密码填写错误,页面将显示如下提示信息,并返回登录页面。
    4.1.6 购物车
    简介
    登录成功后即进入购物车页面(新用户购物车为空),用户可在该页面对加入购物车的商品进行购买。

    假设会员已将自己心仪的商品加入到购物车,购物车页面将自动计算总价。


    更改购物车商品
    此时,会员可通过点击每个商品的“+”、“-”按钮对该商品数量进行增加和减少操作。

    若会员不想购买某件商品,可点击“×”按钮从购物车中删除该商品。


    购买商品
    点击“购买”按钮即将购物车中的商品全部购买。

    4.1.7 购买记录点击上方导航栏的“购买记录”按钮或点击下方页脚的“购买记录”超链接

    即可进入购买记录页查看购买记录。

    4.2 后台具体功能4.2.1 登录页面在登录页面填写正确的用户名、密码和验证码,点击“登录”按钮即可成功登录。

    若验证码填写错误,页面将显示如下提示信息,并返回登录页面。
    若用户名或密码填写错误,页面将显示如下提示信息,并返回登录页面。
    4.2.2 公共部分
    显示标题
    显示管理员名称
    “前台首页”超链接
    “退出登录”按钮



    左部导航栏,点击链接可进入相对应模块
    4.2.3 首页欢迎页面,引导管理员进行操作。

    4.2.4 商品添加在左侧导航栏点击“商品添加”链接进入商品添加页面。
    依次按要求填写商品信息,上传图片。

    点击“确定”按钮,即完成商品添加操作。
    若商品编号重复,则不添加该商品,直接进入商品修改页修改该商品。

    4.2.5 商品查看、修改、删除
    查看
    在左侧导航栏点击“商品列表”链接进入商品列表页面。

    管理员可对指定分类下的商品进行排序(默认对所有商品按照新品排序),例如对“手机—手机”分类按照价格升序排序:


    修改
    点击“修改”可以对指定商品属性进行修改(商品编号不可修改)。


    删除
    点击“删除”

    点击“确定”

    可以看到商品列表中编号为“562390304003”的商品已经被删除。

    4.2.6 查看、添加、删除商品分类
    查看商品分类
    在左侧导航栏点击“商品分类”链接进入商品分类列表页面


    添加商品分类
    点击商品分类列表页的“添加分类”按钮,即可进入商品分类添加页面。例如,选择一级分类为“电脑/办公”,分类名称为“服务器”


    删除商品分类
    点击商品分类列表页每一个商品分类对应的的“删除”
    可以看到商品分类列表页中名为“服务器”的商品分类已经被删除。

    查看会员信息
    在左侧导航栏点击“会员管理”链接进入会员信息列表页面

    点击每个会员的“查看详情”操作,可以查看该会员的详细信息和购买记录

    第五章 总结与心得体会通过这次网上商城开发,让我清楚认识到软件工程的重要程度,软件项目涉及到以下阶段,即计划阶段、需求分析、软件设计、编码、测试阶段、运行维护等。经过一段时间的努力,我们终于完成了网上商城网站系统,基本实现了题目的基本要求。总的来说,在做这个毕业设计的过程中,我们查阅了大量关于网上销售的相关资料,切实地按照软件工程的步骤,从需求分析,概要设计,详细设计,数据库设计,再到编码,调试运行,测试等步骤。从中我学到了很多东西,对我们来说,无论是理论还是实践上都是一个较大幅度的提高,可以说是理论到实践的一个飞跃。我还了解了软件开发的大体过程,在当今竞争激烈的社会中只有学到本领才能有立足之地,通过这次综合实验也使我们知道做软件开发的辛苦,首先要有足够的耐心,要勇于面对密密麻麻的代码,无数遍的调试,和无数遍的修改,但是,当调试成功时,你就会感到这些努力的意义,成功的喜悦。软件开发,还要注意借鉴,查看已有的例子的代码,这样可以节省大量的时间,同时也实现了代码重用。此外,我知道了基础课的重要,要学好一门编程语言,一定要动手,实践是最好的方法!
    3 评论 85 下载 2019-05-23 17:50:58 下载需要13点积分
  • 基于Android Studio开发的笔记APP

    1 程序的运行环境、安装步骤
    运行环境

    JDK 1.8compileSdkVersion 27
    程序的组成部份

    Xml布局文件Java程序文件



    安装步骤
    在一台华为手机上安装软件下载apk,可以传到邮箱中然后下载,按照提示允许安装即可

    2 程序开发平台
    开发环境:Android Studio 3.1.0
    代码行数:1277


    3 程序功能说明提醒用户给予权限

    基本的保存、删除、重命名功能
    还可以将笔记截为长图分享到朋友圈、好友、微博等;编辑框里面的文字内容更换了叹墨咏黑第三方字体库。

    支持图文混排功能,可以从图库选择,也可以拍照获取
    亲切的笔记查询功能
    4 程序算法说明及面向对象实现技术方案
    本程序中定义了

    NoteInfo类实现了Serializable接口(可以放进Bundle数据包):为笔记列了id、name、type、content、time成员
    DBSchema类定义了数据库的基本名字、表名和各列的名
    MyDBhelper类是继承了SQLiteOpenHelper的子类
    DBdataSource类封装了各种对数据库的操作
    DataAdapter类是继承了BaseAdapter的子类,利用了ViewHolder设计模式,性能更加提升
    MyDate类可以获取时间,返回以下格式的时间返回以下格式的时间2018年1月22日 16:36:55
    NoteWrapper类封装了编辑笔记时的所有操作:保存、重命名、删除、截长图和各种对Bitmap的操作(获取比例、优化图片质量等等)
    SearchNoteAsyncTask类是继承了AsyncTask<Void ,Void,Void >的子类,负责调用Myrepository类里面的getNoteInfo()方法获取笔记信息,并通过接口返回给数据适配器
    StringFunction类封装了一些对字符串检错的方法
    Myrepository类负责从数据库调取所有笔记的信息
    MessageBox类封装了对话框负责和用户交互
    UiHelper类封装了提示信息的方法
    edit_activity类是编辑编辑的界面包括保存删除重命名分享的菜单栏、拍照按钮、图库按钮、EditText图文混排编辑框
    MainActivity类是第一个展示给用户的界面,包括查询笔记的编辑框、ListView列表、添加笔记按钮和长按批量删除

    5 技术亮点、关键点及其解决方案5.1 本程序的亮点
    更换了字体叹墨咏黑
    这个记事本程序支持图文混排
    可以从相机、图库调取图片
    可以将笔记分享为长图
    长按ListView多选删除
    必要的提醒:例如按两次退出程序

    5.2 本程序的技术关键点
    采用了多线程技术以提升程序的性能:主要在MainActivity中的onResume()方法 中进行异步访问数据库来获取列表的信息,这里主要用了AsyncTask这个轻量级异步任务类
    使用Sqlite来保存数据:表列项有时间(Text)、内容(Text)、类型(Text)、名字(Text)、ID号(Integer)。使用了SQLiteOpenHelper类
    应用了MVC模式来设计整个程序的架构
    使用了各种Bitmap类操作方法压缩、解析、优化、获取图片
    ListView长按实现Actionbar多选删除
    在编辑框贴上图片时将图片自动生成一个文件名存在/sdcard/myImage/文件夹
    利用SpannableString、ImageSpan和editable三个类加上对光标的操作实现在EditText的插入图片
    取EditText的内容时,图片会以路径的形式显示在String类型里,这时利用正则表达式从EditText中的内容解析出来图片路径,实现在正常的位置显示图片
    程序中大量使用了回调:例如异步访问数据库后将数据返回给适配器、保存新的笔记后将一个新NoteInfo类返回给edit_activity的noteInfo字段等等例子
    调用系统的相机、图库、获取用户权限和分享时启动相应的activity
    程序中大量使用了File文件操作:在EidtText插入图片时、获取屏幕截图时都存储、获取了文件
    在ListView的数据适配器中采用了ViewHolder设计模式
    退出程序时需要两次按回退建才能退出;没保存笔记时会提醒是否返回上一个界面,这些都复写了onkey()方法

    5.3 遇到的技术难点及对应的解决方案
    在EditText中插入图片时 由于有删除、新增时出现一下子两张的情况
    解决:在插入图片时,由于是插入图片的路径然后再解析图片路径获取图片,所有我们再路径最前面和最后面添加一个光标的换行,然后设置光标在最后位置
    之前从相机调取图片时速度很慢
    解决:原来是多加了一些Bitmap的操作,进行了优化后速度很快
    截长图时的背景问题,由于Canvas类是根据Bitmap绘制背景,可是一张空的Bitmap背景是黑色的,导致截图后背景位黑色
    解决:在Canvas绘制背景前将Bitmap用Scrollview的背景(在drawable文件夹下的一张图片)填充,然后利用Bitmap的拉伸图片的方法将Bitmap根据Scrollview的实际高度拉伸,这样背景就有了
    截图问题
    解决:将EditView放在ScrollView里,对ScrollView截长图
    解析EditText中的图片
    解决:利用正则表达式解析内容中的图片路径,然后内容和路径填充一个SpannableString类的对象,将该对象返回给EditText
    UI交互问题:例如弹出对话框要求用户保存、提醒用户未保存等等
    解决:封装了一个单独的对话框类,利用实现接口来写不同的操作
    防止解析图片时出现OOM
    解决:取系统中图片是必压缩一下

    6 简要开发过程
    查找资料确定要使用的算法与数据结构
    完成大体系统UI设计
    进行数据库功能开发
    数据库功能开发完成,并进行测试
    进行EditView的图片与文字混排功能开发
    EditView的图片与文字混排功能开发,并进行测试
    进行消息提示UI开发
    消息提示UI开发完成,并进行测试
    增加删除、重命名、保存、分享、批量删除的功能添加,并新增菜单栏
    删除、重命名、保存、分享、批量删除的功能完成,并进行测试
    对程序进行集成测试
    程序开发工作完毕,编写及整理文档
    3 评论 93 下载 2019-02-17 16:48:35 下载需要8点积分
显示 0 到 15 ,共 15 条
eject