分类

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

资源列表

  • 基于汇编语言的俄罗斯方块游戏

    报告说明此次报告根据自己的兴趣编了个简单的游戏,模仿经典游戏俄罗斯方块。左右方向键控制方块的水平位置,上方向键旋转方块,END键水平翻转之。有简单的积分功能,并能根据得分多少调整游戏难度,即方块下落速度。
    游戏甚少创新,权为练笔。
    程序说明游戏有一个依得分而改变的周期,在每一个周期中,用10h号BIOS中断调用检查键盘缓冲是否为空,若不为空,则读出缓冲数据,根据输入指示,调整游戏内部状态,最后根据内部记录的状态用适当的方法表现在屏幕上。
    主程序流程
    初始化游戏环境:
    清除得分,清空屏幕,初始化相关数据结构产生两个方块组,一个为即将显示的,一个为下一个要显示的。
    进入游戏循环:
    延时,达到一定的游戏周期,在此期间也检查用户输入根据输入,应用相应功能,如:ESC键则跳至4,控制方向键则检查并改变当前方块位置响应输入,F3键主动提高游戏关数,DEL键游戏暂停……。根据更新的内部数据状态,执行相应检查,如:是否满行?是否游戏结束?是否游戏完成?……
    无条件跳转至游戏循环。退出程序
    附加说明
    随机数的的产生,首先从CMOS实时钟读取的秒数作为随机数的初始值,其后再根据线性同余法产生下一个数。
    游戏区域暂定为12*20,为了纪录区域中每个格子是否已被方块占据,用了一个长度为20的字数组,每个字的一位代表区域中的一个方格,这样计算满行时容易判断,设置位也较为容易。不过由于16位汇编指令中并没有bt之类位操作指令所以,测试某位是否置位时,也相当令人不快。
    为了更好的控制方块左右移动以及完成旋转水平、翻转等操作,在方块组中选取了一个方块作为参照点,极大简化了编写代码的过程。
    )游戏难度,具体到此游戏就是方块自动下落的速度,通过设置延时长短控制,随着得分的增加,Level也增加,所对应的延时缩短,下落速度加快,为了减小游戏复杂度,在较高关数时方块组形状(共16种,其中2种相同,为仅一个格的穿透方块,设为2增加出现概率)趋于正常。
    输入的获取,首先调用INT 10h的1号功能,判断键盘缓冲区是否为空,若不为空再用0号功能读取,以读取到的输入改变内部状态,即方块位置、游戏状态等。

    编写步骤
    先构思出一个大概的框架
    将构建出的框架上.逐步分解
    逐步添加子程序
    不断添加代码、DEBUG,最后测试。

    结果(部分画面截图)


    分析与讨论
    虽然是用汇编语言编写代码,现代一些优秀的编程思想仍然是极为有用的,如面向对象,能很大程度为编写代码提供导航。思维决不局限于所用的编程语言,绝大部分编程语言在功能上是完全可以等价的,而思维决定一切,C到C++ 的发展,功能上并无不同,不过在语法上C++为我们提供了便利,对事物的理解也更为直观。如果用VC++写此程序就不用那么艰难了。不过,编写代码过程中很多体悟却不能仅通过几行高级语言的代码所能得到的。
    程序虽说不算大,但为了更好的控制其变化,我使用了tortoiseSVN工具,其对版本的控制程度非同一般,源代码交给它,非常放心。同时IDE用的是MasmPlus,至少它提供了语法高亮显示、子程序快速跳转(其实也不够快)、快速检索等功能。
    Makefile文件的使用也省了不少事,通常是交待它一下怎样通过编译、连接出最终可执行文件后就不用管它了。 所以说好的工具仍是不可忽视的。

    结论所谓万事开头难,像汇编这样非常不让人省心的语言,看来看去就那几个单调的指令,阅读别人的代码,总有些乏味,只有真正开始自己编写时,才能知道其实它并不难,相反它把所有细节全部展现在我们面前,不像有些高级语言为了通用性,隐藏了很多细节。在用高级语言编写代码的时候,总觉得有些糊涂度日。不过话说回来,两者之间的生产效率却不是汇编所能企及的。
    在用汇编写代码时,每一条指令的执行都能直接感受其变化到,这时候需要有十分的耐心。有时候一个不留神寄存器写错了名,半天没得到自己想要的结果还不知道错在哪。我在编写此程序过程中,就曾有写错的时候,浪费了不少时间。
    所以随时DEBUG是必须的,一旦错误扩大了范围,要找到它是要费很多手脚的。
    最后,在编任何或大或小的程序之前都需要事先有个清晰的规划,然而我却通常是想到哪写到哪,最后做了不少无用功,可惜了那宝贵的时间。
    1 评论 0 下载 2019-04-22 22:44:23 下载需要7点积分
  • 基于Vue + Node + MongoDB的响应式药品商城系统的分析与设计

    摘要随着科学技术的不断发展,中国的传统行业已经受到了互联网浪潮的不断冲击,在将来很大一部分会被互联网所取代。在传统的医药行业,传统的药品销售方式都是实体药店销售,不仅成本高,还会受到时间、空间的限制,加上药店自身经营管理手段的落后,该销售模式在将来可能退出如今的市场经济。想要有所突破,提高销售利润,将实体药店线下销售模式与互联网线上销售模式相结合是一个不错的选择。
    本文以构建一套在线药品商城销售系统为最终目标,系统采用B/S 架构,开发过程遵循前后端分离开发原则,前端开发使用的是HTML+CSS+JS 三大前端技术和 VUE 框架,后端开发使用了Node.js的Express框架加MongoDB数据库组合,前后端数据交互采用的是 VUE 官方推荐的 Axios 库,主要实现的功能有:前台药品列表的展示;药品列表多条件查询;购物车中药品增删改;用户登录;后台管理员登录、后台药品信息管理、后台用户信息管理等多个功能。在用户体验方面,页面开发采取响应式布局设计,完美兼容PC端和iPad、手机等移动设备。
    通过本次系统开发发现,Vue+ Node 组合开发的药品商城帮助实体药店开拓了一条新的销售渠道,能够高效提高实体药店的日常工作效率,节省了大量的人力、物力成本。
    关键词:药品商城;Node; 响应式布局
    ABSTRACTWith the continuousdevelopment of science and technology, China’s traditional industries have beenconstantly impacted by the wave of the Internet. In the future, a large partwill be replaced by the Internet. In the traditional pharmaceutical industry,traditional drug sales are sold in physical pharmacies, which are not onlycostly, but also subject to time and space constraints, as well as thepharmacy’s own management and management methods. The sales model cannot adaptto the current market economy. To make a breakthrough and increase salesprofits, it is a good choice to combine the offline sales model of physicalpharmacies with the online sales model of the Internet..
    This paper aims tobuild an online drugstore sales system. The system adopts the B/S architecture.The development process follows the separation development principle of thefront and back ends. The front-end development uses the HTML+CSS+JS front-endtechnology and VUE framework. Development and use of a combination of Node +MongoDB, front and back end data exchange is used by the VXI officialrecommended Axios library, the main functions are: front drug list display;drug list multi-condition query; shopping cart drugs add or delete changes;user login ; Background administrator login, background drug information management,back-end user information management and other functions. In terms of userexperience, the drug display module adopts a responsive layout design and isperfectly compatible with mobile devices such as PCs and iPads and mobilephones.
    Through thedevelopment of this system, it was discovered that the drug store developed byVue + Node has helped the physical pharmacy open up a new sales channel, whichcan effectively improve the daily work efficiency of the physical pharmacy, andsave a lot of manpower and material costs
    Keyword:Drug Mall, Node, Responsive layout
    1 绪论1.1 选题背景和意义当下的中国,各个行业都受到了互联网的抨击与挑战,都已经掀起了向互联网发展的浪潮。在这样的发展趋势下, 优胜劣汰,竞争将日趋激烈。药品行业作为传统行业,关系着中国百姓的基本生活,也在寻求突破与发展。在国家不断推进医疗改革的影响之下,大部分药品企业将网络销售作为他们产品的第一销售渠道,同时结合信息化的管理系统对日常的销售工作进行合理的统计与安排。
    在中国市场经济体制确定后,实体药房如雨后春笋,纷纷出现在大众视野,紧随其后的是管理制度的不规范,销售方式不合理。很多中小型药店或者社区门诊药房并没有突破传统,还是手工管理药品信息和单一的实体店销售。在中国现有的条件和大环境下,实体药店要充分与互联网相结合,借助互联网进行日常管理,销售。相比于大的医院、药厂,在实体药店这片市场领域,还存在着较大的空缺与市场,实体药店药品管理销售系统正好可以添补空缺、占领市场,实现日常药品管理的智能化,减少工作人员的成本与劳动量,提高日常的工作效率和药品的销售利润。
    1.2 药品销售的现状在进入21世纪后,国家逐渐重视起老百姓的民生问题,在药品行业,国家始终在不断尝试与创新,为人们提供最基本的医疗保障证,不再让老百姓看不起病,买不起药,为药品市场增添了活力。
    古代药铺门前都会挂上一副对联: 但愿世间人无病,何愁架上药生尘,然而在如今的信息社会,各大药商为了提高药品销售利润,打着铺天盖地的宣传广告。往往在广告中药品疗效被夸大其词,肆意炒作,及其误导消费者,更进一步,有些药房的销售人员,为了提高业务销量,更是做起了“购药满88送鸡蛋10个”等促销活动,而有些医生,一个很常见的感冒发烧,能开出几百块钱的药给病人,一味地想赚钱,而未树立正常的医生形象、医德品质,会让顾客慢慢去而远之,最后形成生病就去大医院的局面,小诊所无人问津,严重导致社会资源使用不均匀。
    2 关键技术的分析与抉择2.1 技术路线使用的技术路线是 Vue + Node + MongoDB ,采用前后端分离的方式开发基于MVVM 模式的药品在线销售系统。
    MVVM(Model, View, ViewModel)架构是在MVC框架的基础上衍生出来的一种新的架构,Model用于逻辑处理,View负责页面展示,ViewModel用可比二者之间的桥梁、纽带,View与Model的变化,都会通过ViewModel 传递到对方身上,这就是MVVM架构所实现的双向数据绑定,其通信方式如图 2-1 MVVM 通信。

    2.1.1 技术可行性分析所使用的技术都是最几年比较流行,用户使用量大的编程语言,在各大公司的真正开发业务中也广泛使用,在全球最大的代码开源网站Github 上已经有十几万的点赞数,各大语言的交流社区活跃,在实际开发过程中,遇到的技术难题,也会有很多教程和朋友提供帮助。
    2.1.2 操作可行性分析该系统在操作使用上,用户无需进行任何程序的安装,只需要使用手机、平板、电脑等设备上安装的浏览器,即可访问和使用该系统,简单而不单一的页面设计更加方便用户的使用。
    2.2 Node技术及其Vue框架介绍2.2.1 Node.js的介绍Node.js相比于其他后端语言,算是非常年轻了,诞生于2009年,它是一个能在Service端运行 JavaScript 的平台[3],同时Node.js 基于全球最大的包管理器 NPM, 使得Node得到广泛的传播和使用。在实际开发Node.js过程中,更有不同集成好的框架可供选择。
    2.2.2 Vue.js的介绍Vue.js 前端框架作为MVVM架构的一个代表,是中国技术牛人尤雨溪在2014年首次发布的个人开源项目,相比于无所不能的 JQuery库和同MVVM架构的React、Angular框架,Vue.js 提供简单的API,上手简单,学习曲线平缓使得发展迅猛,其主要特点体现在响应式编程、组件化、模块化、前端路由、稳定性等方面。
    本系统由于前后端逻辑分离,所以页面的跳转需要在前台进行路由设定,用到了 Vue-Router 路由框架, 只需将组件与路由一一映射,在路由变化的同时,组件也会发生变化,从而实现了页面之间的跳转。
    Vue 组件是组成页面的重要元素,不同页面页面的相同部分可以提取成一个组件,组件是以 .vue 结尾的文件,一个 Vue 组件的基本格式和内容组成如下。
    <template> <div class="test-container"> </div></template><script>export default { name: 'test', data () { return { } }}</script><style scoped>.test-container{ width: 100%; height: 100%; background-color: #f5f5f5;}</style>
    如上代码,一个名为text的Vue组件会将整个组件的样式、行为代码写进一个文件,更加方便了代码的维护。
    如何使用这个text 组件,只需要在页面,或路由配置文件中引入即可,页面引入方式如下代码展示:
    <template> <div id="app"> <test></test> </div></template><script>import Text from './test'export default { name: 'test', components: {Text}, data () { return { } }}</script><style>#app{ width: 100%; height: 100%; background-color: #f5f5f5;}</style>
    2.3 MongoDB 数据库的介绍本次系统开发过程及其存储数据所使用的是MongoDB 数据库,MongoDB数据库也被称之为 NoSQL 数据库,相比于MySQL数据库,不需要编写复杂的SQL查询语句,其最大亮点是使用JSON风格语法。同时前后端的数据交互也都使用的JSON形式来传输,大大方便了数据的存储与查询。
    3 系统分析3.1 需求分析需求分析作为软件工程中的一个关键的部分,主要任务是确定系统所要完成的功能,对整个系统提出完整、准确、规范、清晰、具体的要求。需求分析师和软件工程师需要从用户提出的需求出发,分析提炼,挖掘用户内心真实想要的,并将其转换为产品需求。必要的需求分析有利于提早发现错误,避免在系统开发过程中需求不断变化,提高技术人员的开发效率,降低开发成本,保证软件质量。
    3.1.1 功能需求本系统在功能设计上遵循模块化开发实现,将整体系统分为六大的功能模块,如图所示。
    根据需求分析,系统在大体上分为前、后台两大部分,具体 功能可分为药品展示、药品信息管理、用户操作、用户信息管理、购物车管理和订单信息管理六个模块。具体如图3-1所示:

    3.1.2 性能需求本系统作为实体药店的管理销售系统,在用户、访问量将不断增加等情况下,高并发、系统安全、加载速度慢等一系列性能问题随之而来,确保系统运行在一个安全、平稳、可靠的环境下,需要做到如下几点:

    信息安全:在大数据环境下,个人信息的安全行无比重要,系统做到最基本的安全保障是让用户使用系统的前提。对于药品商城系统而言,更是尤为重要
    及时稳定性:在多用户同时访问、使用系统时,对服务器带来巨大的压力,往往会导致页面无法加载、数据不刷新、出错等一系列高并发问题
    健壮性:人们访问系统所使用的终端设备各不相同,想要做到完美的用户体验,系统代码必须具有健壮性,使其能在不同分辨率的设备上、不同厂商的高低版本上都有很好的页面展示及其功能的实现。在开发过程中。应尽量采用模块化开发,提供数据、程序模块的独立性

    3.2 数据流程图数据流程图以图形化方式反映出系统所要完成的功能。利用数据流程图能方便用户更加清晰地阐述系统需求、有利于开发人员更好的开发等。具体如图3-2所示:

    4 系统设计系统设计也称物理设计,起着承上启下的重要作用,在系统分析的基础上,将分析阶段反应用户需求的逻辑模型装换为可以具体实施的物理模型[6],在经过系统分析后,将得到系统新的逻辑模型。
    4.1 系统结构设计在技术日益更新的网络时代,用户需求不断变化,相应的信息系统的规模和复杂性也在随之改变。不同需求、功能的信息系统对系统所使用的体系结构有着不一样的要求,对于开发人员而言,在开发使用了不同的体系结构的系统时,所需要使用到的技术、开发周期以及系统在实际应用等方面都存在区别。选择合理正确的系统体系结构对整个系统起着关键作用。
    本次系统开发所采用的体系结构是B/S架构,即浏览器/服务器架构,它是在WEB 不断发展的背景下,在C/S 架构(客户机/服务器架构)的基础上变化和改进后的一种新型架构模式。同C/S 架构相比,客户端无需安装制定的软件,只需要通过使用浏览器与WEB服务器通信实现数据交互。可以在不同的操作系统和终端设备下工作,而且具有统一的UI设计和逻辑交互。B/S 架构大大方便了客户端的工作,所有的数据的处理、存储、更新等都在服务器端,这样保证了用户无需升级更新,始终访问的是最新的数据。
    系统的结构设计如图4-1所示:

    用户使用本系统时,页面首次加载、页面跳转、登录退出等等操作都会触发浏览器发送请求给服务器,服务器中的WEB服务器将处理解析这些请求,服务器中的应用层部分调用业务逻辑与数据库进行交互,将请求到的数据处理后传回WEB层进行数据渲染后反馈给客户。
    4.2 系统功能设计功能设计是在最初的需求分析基础上,对系统因具备的功能进行概念性构建的创造活动。为了让整个开发流程更加清晰、高效,系统的功能设计应该分不同的模块进行,各个模块功能应采用一致的设计思路,简化设计的难度,提高代码的可读性与可维护性。
    本系统的功能设计分成商品展示、用户操作、后台管理三大模块。如图4-2所示:

    4.3 数据库设计4.3.1 数据库设计概述数据库相当于系统的仓库,存储着重要数据。用户所有的操作最终都是在同数据库进行数据交互,想要高效处理数据,好的用户体验,规范化的数据库设计是前提条件。
    规范化数据库设计通常会有以下两个基本原则:
    规范性:数据库规范设计是指使用正确的数据结构、字段类型来存储数据,确保在操作、维护数据库时,能对数据进行正常的处理;
    数据冗余:在数据库设计中,应避免数据的冗余,过多的数据冗余不仅会占用更多的存储空间,对后期数据库的维护和检查带来不必要的麻烦。
    4.3.2 数据库的选择数据库的读写速度直接影响系统的用户体验,所以从一开始选择合适本系统的数据库尤为重要。从目前主流的数据库来看,莫过于MySQL和MongoDB,MySQL属于关系型数据库,而MongoDB被称为NoSQL 的数据库,二者在数据存储的方式上有很大差别,代表着不同的设计思路。
    本系统选择的是MongoDB 数据库,主要考虑到以下几点:

    MongoDB 使用了以JSON(JS对象表示法)为基础的文档存储模式,前后端数据交互通常也采用JSON数据格式,所以在数据存储、读取方面,MongoDB无需进行数据格式的转换等,更加直观
    MongoDB相比于MySQL数据库而言,对于那些不懂后端开发的前端开发人员来说更加友好,在短时间内能快速学习、掌握并应用的实际的开发中

    4.3.3 数据库集合设计本系统数据库包含两个数据集合:Drugs 药品数据集和User 用户数据集,数据集字段表如表4-1所示:



    Drugs 药品数据集







    序号
    字段名
    类型
    备注


    1
    drugId
    String
    药品ID


    2
    drugName
    String
    药品名称


    3
    salePrice
    Number
    药品售价


    4
    drugImage
    String
    药品图片


    5
    drugType
    Number
    药品类别


    6
    drugCount
    Number
    药品数量


    7
    checked
    String
    是否选中


    8
    isHot
    Boolean
    是否热销


    9
    isSee
    Boolean
    是否可见


    序号
    字段名
    类型
    备注


    User 用户数据集





    1
    userId
    String
    用户ID


    2
    userName
    String
    用户名


    3
    userPwd
    String
    用户密码


    4
    orderList
    Array
    订单信息


    5
    cartList
    Array
    购物车信息


    6
    addressList
    Array
    地址信息



    5 系统实现5.1 前台页面的原型设计系统的前台是与用户进行交互的平台,前台页面效果相当于系统的外衣,决定着用户对系统的第一印象,好的前台体验效果才能吸引用户使用,在存在竞争对手时,用户往往会选择体验更好的产品。
    “一图胜千言”,在系统正式开发之前,需要讨论需求和技术实现,设计好页面原型,避免在后面开发中的大规模修改,减少不必要的工作。
    本系统前台主要用于商品的展示和用户选购商品,在编程实现上没有采用传统的Table页面布局方式,而是引入一种功能更强的、灵活性更好的 DIV+CSS 页面布局。前台一共包括首页、药品列表页、购物车页、生成订单页等四个页面。首页原型图和药品列表页原型图分别如图5-1和5-2所示:


    5.2 组件化的开发思想上面展示出系统首页和药品列表页的简单原型设计图,不难发现两个页面有相同的部分,头部 Header 导航和底部 Footer 信息,对于这些每个页面都相同的部分,为了减少重复工作和后期代码维护,需要将其从页面代码中抽离出来,形成单独的组件,只需在页面中引入即可,后期维护时,也只需修改单独组件。正因如此,本系统完全采用组件化的开发思想,页面是由大小组件组合而成,最小的按钮都可提取成组件。
    正因为每个页面由组件组成,所以说页面之间的切换其实就是组件之间的切换。本系统开发所使用的前端技术Vue正好符合开发需求,Vue 常用于开发SPA单页面应用,单页面应用就是基于组件和路由,Vue-Router设定好路由与组件之间的映射,从何实现页面的切换。
    5.3 响应式布局的实现响应式布局是一种网络页面设计布局,能将PC桌面设备上的网页内容在移动设备上进行优化排版,使用户能够在移动设备上更方便使用和操作。
    何时选择响应式布局开发?正如上面所提到,响应式布局开发是将PC 端的项目更好的展示在移动端,PC、移动端二者共用一个项目,因此,当系统的PC端页面和移动端页面十分相似,可以选择响应式布局开发。但对于那些内容复杂、使用量多的系统网站来说,为了追求更好的用户体验,PC端和移动端完全分开,分成两个不同项目开发,独立开发管理,更加科学高效。
    如何实现响应式开发?首先介绍系统开发所使用到的一套响应式栅格布局,它预设5个响应尺寸:xs、sm、md、lg和xl , 代表着设备的不同屏幕尺寸,其底层实现思想借用的是媒体查询( @media )来创建不同屏幕尺寸的关键分界点阀值。
    5.4 购物车操作实现从日常网上购物体验流程:打开网站 > 搜索、浏览商品 > 加入购物车 > 生成、确定订单并支付来看,购物车算是本次系统的最为关键之处,跟用户、药品、订单、库存、促销等其它模块都挂钩。
    购物车功能的用户体验好坏直接会影响用户是否直接消费,在设计购物车功能时我们需要考虑到以下几点:

    购物车的入口在哪?本系统在头部导航头提供了直接进入购物车入口,同时在用户浏览商品时,可直接添加商品到购物车
    购物车有哪些基本的展示和操作? 由于移动端、电脑端的屏幕尺寸不同,在移动端展示时会隐藏不常用的信息,但基本的信息都不可缺少,同时需要提供的基本操作包括药品数量的增,减、单件药品移除、多选框的选中取消、动态计算单件商品的总价和所有勾选商品的总价等
    有哪些细节应该考虑?在进入购物车之前我们应该判断用户是否登录,登录后方可进入购物车页面,并将展示用户之前加入到购物车的商品,在进行商品数量改变时,需要设定一个阀值,就是在商品数量仅剩一时,应该禁用减少按钮的操作,只能通过删除来移除商品

    6 结论与展望本系统在技术实现上使用了当下流行的技术和框架,功能方面能够满足小型的实体药店正常的工作使用,能够极大提高工作人员的开发效率和药店的营业额,能够帮助药店在实现全自动管理的道路上更进一步。
    同时,本系统也有不少的遗憾与不足,由于个人独自开发,开发周期短,所考虑的肯定无法很全面,可能很多用户需求没考虑到,同时页面现实效果太过普通,同时整个开发流程缺少了代码编程规范和代码测试。
    对于系统的展望,希望能以本系统为基础,向微信公众号、小程序靠拢,让用户能直接通过微信访问本系统。
    参考文献[1]祥保玉. 响应式内容管理系统的设计与实现[D].哈尔滨工业大学,2017.
    [2]曾德强.中国之痛:医疗行业内幕大揭秘[M],2016(1)
    [3] 李兴华. 基于WebSocket的移动即时通信系统[D]. 重庆大学, 2013.
    [4] 沈姝. NoSQL数据库技术及其应用研究[D]. 南京信息工程大学, 2012.
    [5] 魏桂梅. 面向医学院的医疗低值易耗品管理系统设计与实现[D]. 中国海洋大学, 2010.
    [6] 慎东峰. 邮政储蓄中间业务管理信息系统及实施策略研究[D]. 吉林大学, 2006.
    [7] 冯永祥,杨寒,李雷孝. 基于云计算平台的药品经营监督管理系统的研究与设计[J]. 内蒙古工业大学学报(自然科学版),2015,14(03):10-15
    [8] 康文靖.医院药库管理信息系统分析[J]. 基层医学论坛, 2015,24(12):34
    [9] 赵清华,林学华.基于DIV+CSS的网页布局技术应用研究[J].现代计算机(专业版),2010(05):140-142.
    [10] 邢希,田兴彦,王世运.响应式Web设计方法的研究[J].琼州学院学报,2013,20(02):36-38.
    [11] 王世明,李丹丹. 药品销售管理系统的设计与实现[J].产业与科技论坛,2015,05(05):23-25
    网站界面截图
    2 评论 13 下载 2018-11-17 17:18:41 下载需要13点积分
  • 基于MNIST的手写数字识别


    分了两个模型

    Softmax ModelCNN Mmodel
    使用框架

    Keras

    1 mnist 数据数据集获取方式一:使用 tf.contrib,.learn 模块加载 mnist 数据集(弃用),如下
    #使用 tf.contrib.learn 模块加载 MNIST 数据集(Deprecated 弃用)import tensorflow as tffrom tensorflow.examples.tutorials.mnist import input_datamnist = input_data.read_data_sets('./mnist/dataset/') 这种方法官方已经遗弃了
    运行之后会出现 Warning 提示,该方式已经不推荐使用。
    方式二:使用 keras.dataset 模块加载 mnist 数据集,如下:
    from keras.datasets import mnist(x_train, y_train), (x_test,y_test) = mnist.load_data('mnist/mnist.npz') ##这里是相对路径,其实绝对路径在这里哦 C:\Users\korey\.keras\datasets\mnist\mnist.npz
    这种方式要比第一种简单很多,但是要注意这里的 Path 是个相对路径。
    训练集:60000;测试集:10000;
    可视化数据集(15 个)

    训练集数据分布

    观察到数据分布均匀,下面进行 Model 搭建。
    2 Softmax Model2.1 数据处理Softmax 网络的数据输入是对像素点的输入,图片为 28*28,那么展开就有 784 个像素 点,代码说明如下:
    X_train = x_train.reshape(60000, 784) # (60000,784) X_test = x_test.reshape(10000, 784)#(10000,784) #将数据类型装换为 float32,如果不怎么做的话,后面的归一化操作得到的只有 0 和 1 两个数 X_train = X_train.astype('float32') X_test = X_test.astype('float32') #数据归一化 X_train /= 255 X_test /= 255
    2.2 one-hot 编码对于 softmax 网络的输出是每个类对应的概率,然后最大概率则是预测的类别,所以这里需要对标签进行 one-hot 编码,对于高层的 Keras 框架来说,one-hot 编码已经被封装好了,如下:
    #one-hot from keras.utils import np_utils n_classes =10 Y_train = np_utils.to_categorical(y_train, n_classes) ## 实现 one-hot 编码Y_test = np_utils.to_categorical(y_test, n_classes)
    举个例子:原始标签如果是 5,那么对应的 one-hot 编码后应该是[0., 0., 0., 0., 1., 0., 0., 0., 0., 0.]。
    2.3 Keras Softmax Model利用 Keras 的 Sequential 模型,可以对框架进行快速搭建,如下:

    可视化结果如下:

    2.4 模型保存和加载采用 model.save()进行模型保存,利用 load_model 模块进行加载,详细见代码。
    3 CNN Model3.1 数据处理这里注意一下 channel_last(默认)和 channel_first 的区别:
    """数据规范化""" from keras import backend as K #在 C:\Users\korey\.keras\keras.json 配置文件中查看默认的数据 shape 是 channels_last img_rows, img_cols = 28, 28 if K.image_data_format() == 'channels_first': #[batch, channel, height, width] x_train = x_train.reshape(x_train.shape[0], 1, img_rows, img_cols) x_test = x_test.reshape(x_test.shape[0], 1, img_rows, img_cols) input_shape = (1, img_rows, img_cols) else:# channel_last [batch, height, width, cbannel] x_train = x_train.reshape(x_train.shape[0], img_rows, img_cols, 1) x_test = x_test.reshape(x_test.shape[0], img_rows, img_cols, 1) input_shape = (img_rows, img_cols, 1) # 数据类型转换 float32 X_train = x_train.astype('float32') X_test = x_test.astype('float32') # 数据归一化 X_train /= 255 X_test /= 255
    3.2 one-hot 编码(同上 softmax 网络)3.3 Keras CNN Model
    结果可视化如下:(效果要比单纯的 softmax 网络要好)

    3.4 模型保存和加载利用 model.save() 和 load_model 模块。
    1 评论 1 下载 2019-04-22 19:15:35 下载需要7点积分
  • 基于VC++实现的中国象棋-双人象棋游戏

    1 问题描述1.1 要求设计棋盘,棋子等数据,两个玩家可以依次输入数据控制某一个棋子的移动,并且可以判断一步棋是否合法,不合法不能移动,游戏可以存档,读档,记录下棋的过程,如能按棋谱走棋更佳。
    1.2 问题分析1.2.1 象棋记谱规则
    红方从右往左为一至九列(用汉字),黑方从左往右为1至9列(用阿拉伯数字)
    若欲移动的棋子那列没有与该棋子相同的棋子,则记为:

    棋子+列号+动作+步数,其中,列号按6.1的规则产生,动作有进、退、平三种:往对方棋盘方向走为进,往己方棋盘方向走为退,在同一行上移动为平步数:若棋子移动时不改变列,则记为移动的行数(红方汉字黑方阿拉伯数字);若棋子移动时改变列或棋子走斜线,则记为移动到的新列号。例如:
    马三进一 红方第三列的马向黑方棋盘方向走一步,移动到第一列卒3进1 黑方第3列的卒向红方棋盘方向走1步,未改变列车二平五 红方第二列的车走到第五列,未改变行象9退7 黑方第9列的象向己方棋盘方向走一步,移动到第七列

    该列有重子且移动的棋子不为兵、卒,则记为:
    位置+棋子+动作+步数,其中动作和步数的记法与上述相同位置分为前、后两种,在己方前进方向的为前,己方后退方向的为后。例如:
    前炮进一 红方位于棋盘前方的炮前进一步(红方共两个炮,不会混淆)

    该列有重子且移动的棋子不为兵、卒,则记为:
    位置+列号+动作+步数,其中动作和步数的记法与6.2相同位置分为前、中前、中、中后、后五种,望文生义。例如:
    前五进一 红方位于最前方的兵前进一步


    1.2.2 目标
    在控制台界面绘制棋盘和棋子
    设计光标系统,可用键盘方向键控制棋子移动,能用鼠标更佳
    可判断每步棋是否合法,不合法不能移动
    可存档、读档、记录下棋过程
    按棋谱走棋(可选)
    计时、暂停(可选)
    自动判断胜负(可选)
    联机对战(可选)

    1.3 设计描述1.3.1 模块本程序共有六个模块:绘图模块、控制模块、菜单模块、文本与文件模块、DeBug模块、行棋模块。
    1.3.2 各模块的大致功能如下
    绘图模块:提供基础绘图函数(绘制直线、矩形、圆形、文本);绘制棋子、棋盘、光标
    控制模块:根据调用者给定的键值/动作映射表将键盘按键解析成对应动作(如将光标上键解析成光标上移动作 ACT_KEY_UP)
    菜单模块:调用绘图函数绘制游戏功能选择菜单;对菜单选择(键盘按键)做出响应并返回对应动作
    文本与文件模块:实现游戏存档、读档;实现显示下棋过程;实现按棋谱走棋
    DeBug模块:打印程序运行过程中的错误信息,方便调试
    行棋模块:管理棋盘、棋子、光标数据;判断棋子移动是否合法;移动棋子;监控游戏状态;调用绘图函数绘制界面;调用菜单函数显示菜单;调用按键解析函数响应键盘按键;调用文本与文件函数实现存档、读档、显示下棋过程;main函数在该模块中

    1.3.3 模块间的依赖关系如下
    绘图模块依赖行棋模块的常量和数据类型声明(如棋盘行列数、棋盘结构),但不依赖任何模块的函数
    控制模块无依赖项
    菜单模块依赖绘图模块的基础绘图函数;依赖控制模块的键值和行为定义
    文本与文件模块依赖控制模块的游戏状态枚举;依赖行棋模块的常量和数据类型声明;依赖行棋模块的ChessMove函数(实现按棋谱走棋)
    DeBug模块无依赖项
    行棋模块依赖其他所有模块

    1.3.4 文件列表行棋模块

    chess.cpp:主函数及走棋函数实现
    chess.h:棋子棋盘与走棋函数声明

    控制模块

    control.cpp:控制函数实现
    control.h:按键及控制函数声明

    DeBug模块

    debug.cpp:调试与错误处理函数实现
    debug.h:调试与错误处理函数声明

    绘图模块

    graphics.cpp:绘图函数实现
    graphics.h:绘图函数声明
    style.h:界面样式声明

    菜单模块

    menu.cpp:菜单函数实现
    menu.h:菜单函数声明

    文本与文件模块

    text.cpp:文本处理函数实现
    text.h:文本处理函数声明

    辅助头文件

    win32api.h:Windows API 函数声明
    其他文件

    mainpage.h:使用Doxygen生成文档时显示的首页
    1.3.5 枚举棋子类型枚举 enum ChessType,枚举了红黑方的所有棋子类型,包括无棋子。 其中,红方名称中间带_R_,黑方带_K_ (因R和B过于相似,故不用B而用K代表黑方Black)
    枚举值如下所示:
    CHESS_NULL // 无棋子CHESS_R_SHUAI // 红方帅CHESS_R_JU // 红方车CHESS_R_MA // 红方马CHESS_R_PAO // 红方炮CHESS_R_XIANG // 红方相CHESS_R_SHI // 红方仕CHESS_R_BING // 红方兵CHESS_K_JIANG // 黑方将CHESS_K_JU // 黑方车CHESS_K_MA // 黑方马CHESS_K_PAO // 黑方炮CHESS_K_XIANG // 黑方象CHESS_K_SHI // 黑方士CHESS_K_ZU // 黑方卒
    游戏状态枚举enum GameState
    枚举值如下所示:
    GSTAT_ACTIVE // 游戏正在进行GSTAT_PAUSE // 游戏暂停GSTAT_STOP // 游戏结束GSTAT_DRAW // 和棋GSTAT_RED_WIN // 红方玩家获胜GSTAT_BLACK_WIN // 黑方玩家获胜
    玩家枚举enum Player,用来标识玩家是红方还是黑方。 象棋规则中红方先手,黑方后手。
    枚举值如下所示:
    PLY_BOTH // 红方玩家和黑方玩家在同一台电脑上,两者交替操作PLY_RED // 当前为红方玩家PLY_BLACK // 当前为黑方玩家
    动作类型枚举enum ActionType
    枚举值如下所示:
    ACT_UNKNOWN // 未知动作ACT_KEY_UP // 向上移动光标ACT_KEY_DOWN // 向下移动光标ACT_KEY_LEFT // 向左移动光标ACT_KEY_RIGHT// 向右移动光标ACT_KEY_LOCK // 锁定/解锁棋子(锁定棋子再移动光标时棋子也相应移动,再解锁棋子将其放到新位置)ACT_STOP_GAME// 结束游戏ACT_BACK_GAME// 返回游戏ACT_START_GAME// 开始游戏ACT_SAVE_GAME// 保存游戏ACT_LOAD_GAME// 加载游戏ACT_SHOW_MENU// 显示菜单ACT_SHOW_STEP// 查看下棋过程ACT_REFRESH_SCREEN// 刷新屏幕显示ACT_SHOW_ABOUT// 显示关于菜单
    1.3.6 数据结构棋盘结构struct ChessBoard,包括棋盘格子数据、红黑方的位置等。



    char
    map[10][9]





    棋盘格子数据, 一个十行九列的二维数组,前五行为黑方棋盘,后五行为红方棋盘。 (当前玩家是黑方时,虽将黑方绘制在下,但黑方棋盘数据仍在前五行。) 每个数组元素代表棋盘上的一个交叉点,其值为棋子类型枚举值。


    char
    player



    本机玩家,其值为玩家枚举类型,用于联网对战标识玩家,目前无意义。


    char
    activePlayer



    当前活动玩家,其值为玩家枚举类型。该玩家可移动棋子。


    char
    chessLocked



    棋子是否锁定,锁定为1,未锁定为0


    struct ChessPos
    lockedCursor



    锁定棋子的光标位置


    struct ChessPos
    cursorRed



    红方玩家光标


    struct ChessPos
    cursorBlack



    黑方玩家光标


    char
    redChessNum



    红方棋子数


    char
    blackChessNum



    黑方棋子数


    char
    redShuaiExists



    红方帅是否存在,是1否0


    char
    blackJiangExists



    黑方将是否存在,是1否0


    char
    gameState



    游戏状态


    char
    logFileName[100]



    游戏日志文件名称(不包括路径)



    棋子坐标结构struct ChessPos,也用于光标坐标。



    unsigned char
    line





    行号


    unsigned char
    row



    列号



    按键键值结构struct KeyCode



    signed char
    first





    第一字节(键值单字节这里存键值,键值双字节这里存后一个字节)


    signed char
    second



    第二字节(键值单字节这里为-1,键值双字节这里存前一个字节)



    按键状态结构struct KeyState



    char
    ctrlState





    ctrl键状态,0未按下,1按下


    char
    altState



    alt键状态,0未按下,1按下


    char
    shiftState



    shift键状态,0未按下,1按下


    signed char
    lastKey



    存储上一个键。如果按键的键值是双字节则存储按键的首字节。



    1.3.7 行棋模块// 主函数int main(); // 游戏流程控制函数void StartGame(struct ChessBoard *cp); // 初始化棋盘void InitChessBoard(struct ChessBoard *cp, enum Player player);// 移动棋子 char MoveChess(struct ChessBoard *cp, struct ChessPos sourPos, struct ChessPos destPos);
    1.3.8 控制模块// 匹配按键键值与行为char MatchKey (signed char key, struct KeyCode *keycode, int keycodeSize, struct KeyState *keyStat);// 解析按键键值为动作类型char ParseKey(signed char key, struct KeyState *keyStat, char *actionMap, struct KeyCode *keyMap, int actionSize, int keycodeSize);
    1.3.9 绘图模块// 绘制棋盘void DrawChessBoard(struct ChessBoard *cp);// 绘制光标void DrawCursor(struct ChessPos cursor, char player);// 绘制棋子void DrawChess(char chessType, struct ChessPos pos);// 绘制所有棋子void DrawAllChess(struct ChessBoard *cp);// 绘制指定位置的光标void DrawCursorPos(struct ChessBoard *cp, struct ChessPos pos);// 绘制指定位置的棋子 void DrawChessPos(struct ChessBoard *cp, struct ChessPos pos);// 绘制棋盘局部 void DrawChessBoardArea(struct ChessBoard *cp, int line, int row);
    1.3.10 菜单模块// 绘制主菜单并返回用户选择的事件char MainMenuSelect();// 绘制游戏菜单并返回用户选择的事件char GameMenuSelect(); // 绘制玩家获胜游戏菜单 char PlayerWinSelect(char player);//绘制无法存档提示框 void ShowCannotSaveNotice();// 绘制存档失败提示框void ShowSaveFailedNotice(); // 绘制载入存档选择框char LoadSaveSelect(char *title);// 绘制无法载入存档提示框void ShowCannotLoadNotice();// 绘制关于提示框void ShowAbout(); // 查看下棋过程 void ShowStep(struct ChessBoard *cp);
    1.3.11 文本与文件模块// 将棋子走法转换成棋谱char ChessMoveToManual(struct ChessBoard *cp, struct ChessPos sourPos, struct ChessPos destPos, char *result);// 初始化游戏存档目录int InitGameSaveDir()// 写游戏日志 int WriteGameLog(char *fileName, char *content);// 保存游戏int SaveGame(struct ChessBoard *cp, char *fileName);// 加载游戏 int LoadGame(struct ChessBoard *cp, char *fileName);// 解析棋谱(未完成,尚不可用)char ParseMaunal(struct ChessBoard *cp, FILE *fp);
    1.3.12 DeBug模块// 调试与错误处理函数void printErr(char *str);
    2 编码与调试2.1 主界面
    2.2 主函数流程图
    2.3 程序使用说明本游戏为双人中国象棋游戏,由两个玩家在同一台电脑上进行游戏。
    本游戏用键盘操作,暂不支持鼠标。游戏过程中您可以通过菜单查看按键说明。
    在运行ChineseChess.exe后,您将看到本游戏的主菜单,您可以按数字键(主键盘小键盘区均可)选择对应项目:

    2.3.1 新游戏选择“新游戏”后,您将看到一盘已经摆放好的中国象棋,如下图:

    您可以通过键盘上的上、下、左、右方向键或W、S、A、D字母键移动光标(绿色方框)来选择棋子。建议左侧的玩家使用W、S、A、D而右侧的玩家使用方向键。
    选好棋子后,按下空格键或者回车键(小键盘区回车也可)选中棋子。选中棋子后光标会变成红色(若无变化,则选择的不是己方的棋子。下棋过程中红黑两方交替移动棋子,请注意区分)。
    光标变红后再移动,会出现一个新的绿色光标,将其移到希望棋子去的位置,按下空格或回车确认移动。若棋子可以移动至此,则棋子将移动至此,并切换红黑玩家。
    若一方吃掉了另一方的将军(将或者帅),则您会看到玩家获胜提示,如图:

    选择您需要的项即可。
    2.3.2 游戏内菜单游戏进行过程中,您可以按Esc键或F8键弹出游戏内菜单,如图:

    选择您需要的操作即可,该功能也可用于暂停游戏。
    2.3.3 查看下棋过程在游戏内菜单选择“查看下棋过程”可查看用棋谱表示的下棋过程,如图:

    2.3.4 存档并退出在游戏内菜单选择“存档并退出”后,您将看到如下界面:

    您可以按下数字键0到9来选择存档的序号,这也意味着您最多只能保存10个存档。
    若存档成功,该盘棋将自动结束并返回主菜单,若您希望继续棋局,可以从主菜单载入存档。
    若存档失败会有错误提示,并且棋局将不会自动结束。
    2.3.5 载入存档在主菜单选择“载入存档”后,您将看到如下界面:

    输入您存档时所输入的序号即可载入对应的棋局。
    若存档存在,棋局将会恢复到存档时的状态并允许您继续游戏。
    若存档不存在或损坏,您将看到错误提示。
    2.3.6 备注
    由于该游戏不能自动重绘界面,所以当游戏窗口改变大小,或者被其他窗口挡住时,部分或全部画面会消失,这是正常现象,请不要紧张。如果您遇到这种情况,您可以按F5键刷新显示
    游戏存档文件和下棋过程记录文件位于与主程序ChineseChess.exe同目录下的ChineseChess文件夹内,若您需要把游戏连同存档一起转移到其他地方,同时复制ChineseChess.exe和ChineseChess文件夹即可

    2.4 程序验证性测试2.4.1 对局部重绘的验证在StartGame()函数里调用DrawChessBoardArea()函数重绘整个棋盘:
    int i;int j;for (i=0; i<10; i++){ for (j=0; j<9; j++) { DrawChessBoardArea(cp, i, j); }}若棋盘重绘后不正常,说明DrawChessBoardArea()函数未正确实现。
    若棋盘重绘后与重绘前(用全部刷新绘制的)一致,则有理由相信DrawChessBoardArea()函数已正确实现。
    2.4.2 验证结果运行测试代码,发现重绘完后九宫的斜线错位,对DrawChessBoardArea()进行调整后整个棋盘恢复正常,验证成功。
    2.5 对无法存档提示的验证当程序无法创建存档文件夹或者存档文件夹不可写时,程序将在开始运行时显示无法存档提示框。
    为了创造“程序无法创建存档文件夹或者存档文件夹不可写”的情况,在NTFS文件系统中运行该程序,并在ChineseChess文件夹属性的安全选项卡中给EveryOne用户设置拒绝写入。
    2.5.1 验证结果设置拒绝写入权限后,重新运行程序,程序果然弹出无法存档提示框(正常情况下不会弹出)。验证成功。
    3 总结3.1 遇到的问题3.1.1 无法获取控制台句柄使用Windows API的GDI函数绘图需要提供窗口句柄,但是windows.h这没有获取控制台窗口句柄的函数,这样就无法绘图。
    解决
    在网上搜到了GetConsoleWindow()函数,然后创建了一个头文件win32api.h来声明它。
    3.1.3 由于每走一步棋都要刷新一次,所以画面非常闪烁解决
    编写了一个新函数DrawChessBoardArea()用来刷新指定坐标棋子的显示,改全部刷新为局部刷新,仅对有改变的位置进行刷新。
    3.2 最满意的地方我对控制模块的实现非常满意,这使我不需要在每个函数里switch (key),只要提交一个键值/动作的映射表给ParseKey()函数就可以得到用户按键对应的动作了。
    而且这样还能非常方便的支持多字节键值(比如F功能键和光标键就是双字节的,传统的switch() 很难处理),一个动作绑定多个按键,甚至组合键。
    3.3 存在的问题
    因为时间紧张,按棋谱走棋功能最终没有完成,感觉很遗憾
    被其他窗口挡住会造成画面残缺或消失是本程序的一大BUG,但是看上去解决不了了。自己实现窗口重绘消息机制?!
    如果控制台字体大小不是默认值,则游戏启动后窗口的大小会非常奇怪。我花了很久时间解决这个问题但是失败了,因为控制台窗口的逻辑大小是按照字符来计算的,比如我把窗口大小定义为80*42,则窗口的实际大小一行能够显示80个半角字符,共能显示42行。
    但逻辑大小不能决定窗口的实际大小,很显然,若增大控制台字体,窗口的实际大小就会变大,这样棋盘右侧或下方就会出现很多空白的部分,非常难看。我试图用SetWindowPos调整窗口的大小,然后绝望的发现实际大小小于逻辑大小后,窗口会出现滚动条!我只好放弃。

    3.4 收获与感言有人说,控制台程序是没有UI设计的。然而我却看到了虽然是字符界面,但是依然非常精美的vim文本编辑器和elinks网页浏览器。在控制台里(当然,准确来说是终端,linux终端功能比win32控制台强大)可能浏览网页吗?不管人们是否相信,我看到了,elinks做到了。而我今天所做的,也是一个控制台程序。并且毫无疑问,这个控制台程序有UI设计。通过一点一点的查询,我从Windows API的海洋中找到了我需要的所有绘图函数,并且最后真的绘制出了一个像模像样的棋盘,这真是一个奇迹。
    2 评论 5 下载 2018-11-07 16:26:46 下载需要5点积分
  • 基于JSP实现的网上点餐系统

    1 软件项目开发模式
    螺旋开发模式

    适合于项目前期部分需求不确定的情况, 对于每一个模块一个个开发:分析、设计、编码、测试、上线好处: 降低软件风险! (做出的产品要尽量满足客户需求!)
    瀑布模式

    先进行所有模块的需求分析,当分析结束后,才进入项目下一个阶段, 即设计、编码、测试、上线 好处: 更容易进行项目把控,即项目质量控制

    2 需求分析软件工程师: 了解需求的途径?

    需求文档
    项目经理
    项目的系统原型(美工设计师)
    客户

    “餐馆王”系统功能

    餐桌模块
    菜类别模块(菜系)
    菜信息(菜品)
    订单

    详细分析

    后台录入的餐桌, 要在前台首页显示; 且只显示未预定
    后台录入的菜类别, 在前台主页显示
    后台录入的菜信息,在前台主页显示
    前台生成订单后,在后台显示订单详细

    3 数据库设计3.1 创建数据库CREATE DATABASE hotel CHARACTER SET utf8;USE hotel;-- 1. 餐桌表CREATE TABLE dinnerTable( id INT PRIMARY KEY AUTO_INCREMENT, -- 餐桌主键 tableName VARCHAR(20), -- 餐桌名 tableStatus INT DEFAULT 0, -- 餐桌状态:0,空闲; 1,预定 orderDate DATETIME);
    3.2 菜类别表CREATE TABLE foodType( id INT PRIMARY KEY AUTO_INCREMENT, -- 类别主键 typeName VARCHAR(20) -- 类别名称);
    3.3 菜品种表CREATE TABLE food( id INT PRIMARY KEY AUTO_INCREMENT, -- 主键 foodName VARCHAR(20), -- 菜名称 foodType_id INT, -- 所属菜系, 外键字段 price DOUBLE, -- 价格 mprice DOUBLE, -- 会员价格 remark VARCHAR(200), -- 简介 img VARCHAR(100) -- 图片);
    3.4 订单表(订单基本信息)CREATE TABLE orders( id INT PRIMARY KEY AUTO_INCREMENT, -- 主键 table_id INT, -- 外键: 餐桌编号 orderDate DATETIME, -- 下单日期 totalPrice DOUBLE, -- 订单所有菜需要的总金额 orderStatus INT DEFAULT 0 -- 订单状态: 0,未结账; 1,已结账);
    3.5 订单明细表(主要是菜品种)CREATE TABLE orderDetail( id INT PRIMARY KEY AUTO_INCREMENT, -- 主键 orderId INT, -- 外键:引入的是订单表的主键 food_id INT, -- 外键:引用的是菜信息表的主键 foodCount INT -- 菜的数量);
    3.6 添加关系-- 添加菜品与菜类别的关系约束ALTER TABLE food ADD CONSTRAINT fk_food_foodType_id FOREIGN KEY(foodType_id) REFERENCES foodType(id);-- 订单表: 与餐桌表的关系ALTER TABLE orders ADD CONSTRAINT order_table_id FOREIGN KEY(table_id) REFERENCES dinnertable(id);-- 订单明细: 与订单表的关系ALTER TABLE orderDetail ADD CONSTRAINT orderDetail_order_id FOREIGN KEY(orderId) REFERENCES orders(id);-- 订单明细: 与菜信息的关系ALTER TABLE orderDetail ADD CONSTRAINT orderDetail_food_id FOREIGN KEY(food_id) REFERENCES food(id);
    4 系统设计
    开源组件及jar文件:

    数据库驱动包(1个)C3P0连接池包(2个)DbUtils组件 (1个)BeanUtils组件(2个)FileUpload组件(2个)
    配置

    C3p0配置文件
    分层

    Entity/dao/service/servlet
    图解




    2 评论 10 下载 2019-01-04 10:54:55 下载需要6点积分
  • 基于JSP实现的学生成绩管理系统

    1 引言1.1 任务简介
    多用户管理:用户分管理员,学生
    网页界面设计:利用HTML和CSS实现客户端前台设计
    类间关系的设计、数据库表格设计
    数据库数据的增(录入)、删、改、查等基本功能
    JSP中Requests Response内置对象的使用;
    数据库表格结构的生成(SQL脚本)
    前台JS校验设计
    DOM技术实现
    其他扩展功能
    开发环境与技术:IDEA、Java 语言、JDK 1.7、MySQL 6.0

    1.2 需求分析本学生成绩管理系统分为管理员登录,学生登录,其中管理员可以实现增加学生成绩信息,删除学生成绩信息,修改学生成绩信息,查找学生成绩信息,按照学生GPA进行排名,其中学生可以实现登录查询成绩功能,能否成功登录取决于数据库中是否有该学生。
    2可行性分析2.1 社会可行性分析成绩是每一个大学生都会接触到的东西,不仅学生为之心动,为了管理学生的成绩,如果不用计算机来实现,老师们也会感觉很头疼麻烦,开发学生成绩管理系统后,让学生的成绩管理变的更加方便,学生也可以实现自助查询成绩功能,老师们也可以对学生成绩进行排名等。
    2.2 经济可行性分析该系统的开发调查主要是需要软件,这些软件都是免费的。主要的花销在于服务器的维护,除此之外没有其他的花销。
    2.3 法律可行性分析系统开发及维护所使用工具和技术及数据信息不违反法律。
    3系统设计3.1 系统功能设计学生成绩管理系统具备以下功能:管理员学生分权限登录,增加学生成绩信息,删除学生成绩信息,修改学生成绩信息,查询学生成绩,按照学生GPA进行排名。

    分权限登录:系统分为管理员和学生用户两个权限,因为管理员就一个,所以账号秘密固定,但是学生有很多,所以在学生登录的过程中要对数据库中的学生进行查询,如果存在,而且密码正确,才能成功登录
    增加学生成绩:该功能可以将学生的姓名,学号,一系列成绩,登录密码等信息录入系统
    删除学生成绩:该功能可以将学生的信息全部列举出来,然后选择想要删除的学生的信息
    修改学生成绩:该功能可以将学生的所有信息全部列举出来,然后再选择想要修改的学生的信息,然后弹出一个修改框,再做仔细的修改
    查询学生成绩信息:该功能可以在输入学号后显示出学生的各项成绩信息
    按照学生GPA进行排名:该功能可以将所有学生按照GPA进行排名,并显示出来

    3.2 算法流程设计根据需求分析,将本系统分为以下模块,模块图如下:

    4 关键技术及代码实现4.1 基本功能4.1.1 Web前端设计与实现4.1.1.1 HTML关键技术及应用include file技术
    <html> <head> <title> 学生成绩管理系统 </title> </head> <body bgcolor="#FFFFFF"> <%@ include file="include_head_JSP2.jsp"%> </body></html>
    Bootstrap 框架技术
    <%@page contentType= "text/html; charset=UTF-8" language= "java" errorPage=""%> <!DOCTYPE html><html> <head> <style> body{ background-image: url("1281116171866201.jpg"); } </style> <title>学生管理系统</title> <meta name= "viewport" content = "width=device-width, initial-scale= 1.0"> <!-引入Bootstrap-> <link href="bootstrap.css" rel="stylesheet"> </head> <body> <center> <hi>学生成绩管理系统</hi></center> <br> <center> <div class ="container"> <a href= "selectLogin .j sp" class= "btn btn-info" role= "button">管理员</a> <a href= "studentLogin.jsp" class= "btn btn-info" role= "button">学生</a> </div></center> <!– jQuery (Bootstrap 的 JavaScript 插件需要引⼊ jQuery) –> <script src=”jquery-3.2.1.min.js”></script> <!– 包括所有已编译的插件 –> <script src=”bootstrap.min.js”></script> </body> </html>
    4.1.1.2 CSS关键技术及应用Bootstrap.css技术
    大量运用Boostrap前段框架中的CSS样式,美化登录界面,表格,按钮。
    响应式表格:
    <table class=”table table-condensed table-hover” >
    美化按钮:
    <a href=”Findinfo2.jsp” class=”btn btn-info” role=”button”> 返回 </a>
    4.1.1.3 JS校验设计及应用登录界面的校验
    <script language=”javascript” type=””> function checkform() { //验证输⼊数据的合法性 if (form1.teacherName.value==””) { alert(” ⽤户名不能为空。”); return false; } if (form1.teacherPassword.value==””) { alert(” 密码不能为空。”); return false; }}</script>
    往数据库写⼊数据的检验
    function checkform(){ //验证输⼊数据的合法性 if (form1.id.value==””) { alert(” 学号不能为空。”); return false; } if (isNaN(form1.id.value)) { alert(” 学号只能为数字”); return false; } if (form1.name.value==””) { alert(” 姓名不能为空。”); return false; } if (form1.cppstring.value==””) { alert(”C++ 成绩不能为空。”); return false; } if (isNaN(form1.cppstring.value)) { alert(”C++ 成绩只能为数字”); return false; } else { var num=parseFloat(form1.cppstring.value); if (num<0||num>100) { alert(”C++ 成绩必须在 0-100 之间!”); return false; } } if (form1.cirstring.value==””) { alert(” 电路成绩不能为空。”); return false; } if (isNaN(form1.cirstring.value)) { alert(” 电路成绩只能为数字”); return false; } else { var num=parseFloat(form1.cirstring.value); if (num<0||num>100) { alert(” 电路成绩必须在 0-100 之间!”); return false; } } if (form1.Ewritestring.value==””) { alert(” 英语读写成绩不能为空。”); return false; } if (isNaN(form1.Ewritestring.value)) { alert(” 英语读写成绩只能为数字”); return false; } else { var num=parseFloat(form1.Ewritestring.value); if (num<0||num>100) { alert(” 英语读写成绩必须在 0-100 之间!”); return false; } } if (form1.Elistenstring.value==””) { alert(” 英语听说成绩不能为空。”); return false; } if (isNaN(form1.Elistenstring.value)) { alert(” 英语听说成绩只能为数字”); return false; } else { var num=parseFloat(form1.Elistenstring.value); if (num<0||num>100) { alert(” 英语听说成绩必须在 0-100 之间!”); return false; } } if (form1.physicsstring.value==””) { alert(” ⼤学物理成绩不能为空。”); return false; } if (isNaN(form1.physicsstring.value)) { alert(” ⼤学物理成绩只能为数字”); return false; } else { var num=parseFloat(form1.physicstring.value); if (num<0||num>100) { alert(” ⼤学物理成绩必须在 0-100 之间!”); return false; } } if (form1.prostring.value==””) { alert(” 概率论成绩不能为空。”); return false; } if (isNaN(form1.prostring.value)) { alert(” 概率论成绩只能为数字”); return false; } else { var num=parseFloat(form1.prostring.value); if (num<0||num>100) { alert(” 概率论成绩必须在 0-100 之间!”); return false; } } if (form1.hisstring.value==””) { alert(” 近代史成绩不能为空。”); return false; } if (isNaN(form1.hisstring.value)) { alert(” 近代史成绩只能为数字”); return false; } else { var num=parseFloat(form1.hisstring.value); if (num<0||num>100) { alert(” 近代史成绩必须在 0-100 之间!”); return false; } } if (form1.xingzhengstring.value==””) { alert(” 形势与政策成绩不能为空。”); return false; } if(isNaN(form1.xingzhengstring.value)) { alert(” 形势与政策成绩只能为数字”); return false; } else { var num=parseFloat(form1.xingzhengstring.value); if (num<0||num>100) { alert(” 形势与政策成绩必须在 0-100 之间!”); return false; } } if (form1.pestring.value==””) { alert(” 体育成绩不能为空。”); return false; } if (isNaN(form1.pestring.value)) { alert(” 体育成绩只能为数字”); return false; } else { var num=parseFloat(form1.pestring.value); if (num<0||num>100) { alert(” 体育成绩必须在 0-100 之间!”); return false; } } if (form1.discretestring.value==””) { alert(” 离散数学成绩不能为空。”); return false; } if (isNaN(form1.discretestring.value)) { alert(” 离散数学成绩只能为数字”); return false; } else { var num=parseFloat(form1.discretestring.value); if (num<0||num>100) { alert(” 离散数学成绩必须在 0-100 之间!”); return false; } } if (form1.rank.value==””) { alert(” 查询密码成绩不能为空。”); return false; } if (form1.rank.value.length<1||form1.rank.value.length>20) { alert(” 密码超出了范围(1 ~ 20)”); return false; }}</script>
    4.1.1.4 DOM 关键技术及应用DOM 实际上是以面向对象方式描述的文档模型。DOM 定义了表⽰和修改⽂档所需的对象、这些对象的⾏为和属性以及这些对象之间的关系。可以把 DOM 认为是页面上数据和结构的一个树形表示,不过页面当然可能并不是以这种树的方式具体实现。 通过 JavaScript,您可以重构整个 HTML 文档。您可以添加、移除、改变或重排页面上的项目。 例如使用 Dom 技术来进⾏ Js 校验:
    <script language=”javascript” type=””> function checkform() { //验证输⼊数据的合法性 if (form1.teacherName.value==””) { alert(” ⽤户名不能为空。”); return false; } if (form1.teacherPassword.value==””) { alert(” 密码不能为空。”); return false; }} </script>
    4.1.2 Web 后台设计及实现本学生管理系统后台设计使用了 Java 脚本,Java servlet 过滤器,以及 Java 数据库技术等。
    4.1.2.1 数据库设计及 SQL 脚本生成数据库脚本
    CREATE TABLE ‘grade‘ ( ‘id‘ varchar(30) NOT NULL, ‘name‘ varchar(30) NOT NULL, ‘cpp‘ varchar(30) NOT NULL, ‘circuit‘ varchar(30) NOT NULL, ‘Ewrite‘ varchar(30) NOT NULL, ‘Elisten‘ varchar(30) NOT NULL, ‘physics‘ varchar(30) NOT NULL, ‘probability‘ varchar(30) NOT NULL, ‘history‘ varchar(30) NOT NULL, ‘xingzheng‘ varchar(30) NOT NULL, ‘pe‘ varchar(30) NOT NULL, ‘discrete‘ varchar(30) NOT NULL, ‘overall‘ varchar(30) DEFAULT ’0’, ‘gpa‘ varchar(30) DEFAULT ’0’, ‘rank‘ varchar(30) DEFAULT ’0’ )
    4.1.2.2 增删改查功能实现增加学生成绩信息
    String idstring=ChangeEncoding(request.getParameter(”id”).trim()); String namestring=ChangeEncoding(request.getParameter(”name”).trim());String cppstring=request.getParameter(”cppstring”); String cirstring=request.getParameter(”cirstring”); String Ewritestring=request.getParameter(”Ewritestring”);String Elistenstring=request.getParameter(”Elistenstring”); String physicsstring=request.getParameter(”physicsstring”);String prostring=request.getParameter(”prostring”);String hisstring=request.getParameter(”hisstring”); String xingzhengstring=request.getParameter(”xingzhengstring”);String pestring=request.getParameter(”pestring”);String discretestring=request.getParameter(”discretestring”); String overallstring=getOvarall(cppstring,cirstring,Ewritestring,Elistenstring, physicsstring,prostring,hisstring,xingzhengstring,pestring,discretestring); String gpastring=getGPA(cppstring,cirstring,Ewritestring,Elistenstring, physicsstring,prostring,hisstring,xingzhengstring,pestring,discretestring); String rankstring=request.getParameter(”rank”); //构造 SQL 语句 Stringsql=”insertintograde(id,name,cpp,circuit,Ewrite,Elisten,physics,probability,history,xingzheng”+”,pe,discrete,overall,gpa,rank)”+”VALUES(’”+idstring+”’,’”+namestring+”’,’”+cppstring+”’,’”+cirstring+”’,’”+Ewritestring+”’,’”+Elistenstring+”’,’”+physicsstring+”’,’”+prostring+”’,’”+hisstring+”’,’”+xingzhengstring+”’,’”+pestring+”’,’”+discretestring+”’,’”+overallstring+”’,’”+gpastring+”’,’”+rankstring+”’)”; String DBDRIVER = ”org.gjt.mm.mysql.Driver” ; // 定义 MySQL 数据库的连接地址 String DBURL = ”jdbc:mysql://localhost:3306/student”; // MySQL 数据库的连接⽤户名 String DBUSER = ”root” ;// MySQL 数据库的连接密码String DBPASS = ”xzk520521”;try { Class.forName(DBDRIVER).newInstance();} catch (ClassNotFoundException e) { out.print(” 错误”); e.printStackTrace();}try { Connection conn=DriverManager.getConnection(DBURL, DBUSER, DBPASS); Statement stmt=conn.createStatement(); stmt.executeUpdate(sql); out.print(”<center>”); out.println(”<P><font size=2’>”+” 向数据库增加学⽣信息”+”</font>”); out.println(”<P><font size=2’>”+” 该学⽣信息数据已经成功添加到数据库。”+”</font>”); out.print(”</center>”); stmt.close(); conn.close(); } catch(SQLException e) { out.print(” 错误”); e.printStackTrace(); }
    删除学生成绩
    String DBDRIVER = ”org.gjt.mm.mysql.Driver” ;// 定义 MySQL 数据库的连接地址String DBURL = ”jdbc:mysql://localhost:3306/student”; // MySQL 数据库的连接⽤户名 String DBUSER = ”root” ; // MySQL 数据库的连接密码 String DBPASS = ”xzk520521”; String id=codeToString(request.getParameter(”id”).trim());//构造 SQL 语句 String sql=”delete from grade where id=’”+id+”’”; try { Class.forName(DBDRIVER).newInstance(); } catch (ClassNotFoundException e) { out.print(” 错误”); e.printStackTrace();} try{ Connection conn=DriverManager.getConnection(DBURL, DBUSER, DBPASS); Statement stmt=conn.createStatement(); stmt.executeUpdate(sql); out.print(”<center>”); out.println(”<P><font size=2 color=’blue’>”+” 向数据库删除学⽣信息数据”+”</font>”); out.println(”<P><fontsize=2’>”+”学号为:”+id+”的学⽣数据信息已经被成功删除。”+”</font>”); out.print(”</center>”); stmt.close(); conn.close(); }catch(SQLException e) { out.print(” 错误”); e.printStackTrace(); }
    查找学生成绩信息
    String DBDRIVER = ”org.gjt.mm.mysql.Driver” ; // 定义 MySQL 数据库的连接地址 String DBURL = ”jdbc:mysql://localhost:3306/student” ; // MySQL 数据库的连接⽤户名 String DBUSER = ”root” ; // MySQL 数据库的连接密码 String DBPASS = ”xzk520521”; String id=request.getParameter(”id”); String sql=”select * from grade where id=’”+id+”’”;//设置查询 SQL 语句try { Class.forName(DBDRIVER).newInstance(); } catch (ClassNotFoundException e) { out.print(” 错误”); e.printStackTrace(); } try { Connection conn=DriverManager.getConnection(DBURL, DBUSER, DBPASS); Statement stmt=conn.createStatement(); ResultSet rs=stmt.executeQuery(sql); if(!rs.next()) { out.print(”<center>”); out.println(”<P><fontsize=2color=’blue’>”+”该学⽣尚未录⼊系统,请前去录⼊!”+”</font>”); } else{ dec.idField.setText(rs.getString(”id”)); dec.nameField.setText(rs.getString(”name”)); dec.cppField.setText(rs.getString(”cpp”)); dec.cirField.setText(rs.getString(”circuit”)); dec.EwriteField.setText(rs.getString(”Ewrite”)); dec.ElistenField.setText(rs.getString(”Elisten”)); dec.phyField.setText(rs.getString(”physics”)); dec.proField.setText(rs.getString(”probability”)); dec.historyField.setText(rs.getString(”history”)); dec.xingzhengField.setText(rs.getString(”xingzheng”)); dec.peField.setText(rs.getString(”pe”)); dec.discreteField.setText(rs.getString(”discrete”)); dec.overallField.setText(rs.getString(”overall”)); dec.gpaField.setText(rs.getString(”gpa”)); } rs.close(); stmt.close(); conn.close();}catch (SQLException e) { e.printStackTrace(); }
    修改学生成绩信息
    String idstring=ChangeEncoding(request.getParameter(”id”).trim()); String namestring=ChangeEncoding(request.getParameter(”name”).trim()); String cppstring=request.getParameter(”cpp”); String cirstring=request.getParameter(”circuit”); String Ewritestring=request.getParameter(”Ewrite”); String Elistenstring=request.getParameter(”Elisten”); String physicsstring=request.getParameter(”physics”); String prostring=request.getParameter(”probability”);String hisstring=request.getParameter(”history”);String xingzhengstring=request.getParameter(”xingzheng”);String pestring=request.getParameter(”pe”);String discretestring=request.getParameter(”discrete”); Stringoverallstring=getOvarall(cppstring,cirstring,Ewritestring,Elistenstring,physicsstring,prostring,hisstring,xingzhengstring,pestring,discretestring); Stringgpastring=getGPA(cppstring,cirstring,Ewritestring,Elistenstring,physicsstring,prostring,hisstring,xingzhengstring,pestring,discretestring); String rankstring=request.getParameter(”rank”); String DBDRIVER = ”org.gjt.mm.mysql.Driver” ; // 定义 MySQL 数据库的连接地址 String DBURL = ”jdbc:mysql://localhost:3306/student” ; // MySQL 数据库的连接⽤户名 String DBUSER = ”root” ; // MySQL 数据库的连接密码 String DBPASS = ”xzk520521”; String id=ChangeEncoding(request.getParameter(”id”).trim());//构造 SQL 语句 Stringsql=”updategradesetid=’”+idstring+”’,name=’”+namestring+”’,cpp=’”+cppstring+”’,circuit=’”+cirstring+”’,Ewrite=’”+Ewritestring+”’,Elisten=’”+Elistenstring+”’,physics=’”+physicsstring+”’,probability=’”+prostring+”’,history=’”+hisstring+ ”’,xingzheng=’”+xingzhengstring+”’,pe=’”+pestring+”’,discrete=’”+discretestring+”’,overall=’”+overallstring+”’,gpa=’”+ gpastring+”’,rank=’”+ rankstring+”’where id=’”+id+”’”; try { Class.forName(DBDRIVER).newInstance();} catch (ClassNotFoundException e) { out.print(” 错误”); e.printStackTrace();}try { Connection conn=DriverManager.getConnection(DBURL, DBUSER, DBPASS); Statement stmt=conn.createStatement(); stmt.executeUpdate(sql); out.print(”<center>”); out.println(”<P><font size=2 color=’blue’>”+” 向数据库修改学⽣信息数据”+”</font>”); out.println(”<P><fontsize=2’>”+”学号为:”+id+”的学⽣数据信息已经被成功修改。”+”</font>”); out.print(”</center>”); stmt.close(); conn.close();} catch(SQLException e) { out.print(” 错误”); e.printStackTrace(); }
    显⽰——按照学⽣的 GPA 进⾏排名并打印
    <% String DBDRIVER = ”org.gjt.mm.mysql.Driver” ; // 定义 MySQL 数据库的连接地址String DBURL = ”jdbc:mysql://localhost:3306/student” ; // MySQL 数据库的连接⽤户名 String DBUSER = ”root” ; // MySQL 数据库的连接密码 String DBPASS = ”xzk520521”; int realrank=1; String sql=”select * from grade ORDER BY gpa DESC”;//设置查询 SQL 语句 try { Class.forName(DBDRIVER).newInstance();}catch (ClassNotFoundException e) { out.print(” 错误”); e.printStackTrace(); } try { Connection conn=DriverManager.getConnection(DBURL, DBUSER, DBPASS); Statement stmt=conn.createStatement(); ResultSet rs=stmt.executeQuery(sql); %> <center><h1> 学⽣成绩 GPA 排名表 </h1></center> <hr> <table class=”table table-condensed table-hover” > <thead> <tr > <th> 学号 </th> <th> 姓名 </th> <th>C++ 成绩 </th> <th> 电路成绩 </th> <th> 英语读写 </th> <th> 英语听说 </th> <th> ⼤学物理 </th> <th> 概率论 </th> <th> 近代史 </th> <th> 形势与政策 </th> <th> 体育 </th> <th> 离散数学 </th> <th> 总分 </th> <th>GPA</th> <th> 排名 </th> </tr> </thead> <% while (rs.next()){ //获取学⽣数据表中的记录 %> <tr > <td><%=rs.getString(”id”)%></td> <td><%=rs.getString(”name”)%></td> <td><%=rs.getString(”cpp”)%></td> <td><%=rs.getString(”circuit”)%></td> <td><%=rs.getString(”Ewrite”)%></td> <td><%=rs.getString(”Elisten”)%></td> <td><%=rs.getString(”physics”)%></td> <td><%=rs.getString(”probability”)%></td> <td><%=rs.getString(”history”)%></td> <td><%=rs.getString(”xingzheng”)%></td> <td><%=rs.getString(”pe”)%></td> <td><%=rs.getString(”discrete”)%></td> <td><%=rs.getString(”overall”)%></td> <td><%=rs.getString(”gpa”)%></td> <td><%=realrank++%></td> </tr> <% } rs.close(); stmt.close(); conn.close(); } catch (SQLException e){ e.printStackTrace(); } %> </table>
    4.1.3 Java-Serviet设计与实现Java-Serviet技术主要用于过滤器的实现,过滤器就是不能再未登录的情况下直接用URL 地址访问操作界面,用Java-Serviet技术实现如下:
    userLoginCheckFilter.java
    package mywebapp; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.servlet.FilterConfig;import javax.servlet.ServletException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession; public class userLoginCheckFilter implements Filter { public static final Stringloginpage = ”index.jsp”; public void destroy(){ } public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)throws ServletException, IOException { HttpServletRequest request = (HttpServletRequest)servletRequest; HttpServletResponse response = (HttpServletResponse)servletResponse; String currentURL = request.getRequestURI(); String ctxPath = request.getContextPath(); //除掉项⽬名称时访问页⾯当前路径 String targetURL = currentURL.substring(ctxPath.length()); System.out.println(targetURL); List<String>info = new ArrayList<String>(); HttpSession session = request.getSession(false); //对当前页⾯进⾏判断,如果当前页⾯不为登录页⾯ if(!((”/index.jsp”.equals(targetURL)) || (”/selectLogin.jsp”.equals(targetURL)) || (”/studentLogin.jsp”.equals(targetURL)))){ //在不为登陆页⾯时,再进⾏判断,如果不是登陆页⾯也没有 session 则跳转到登录页⾯ if(session == null || session.getAttribute(”admin”) == null){ info.add(”You are not logged in!”); request.setAttribute(”info”,info); request.getRequestDispatcher(loginpage).forward(request,response); return; } else{ //这⾥表⽰正确,会去寻找下⼀个链,如果不存在,则进⾏正常的页⾯跳转 filterChain.doFilter(request, response); return; } } else{ //这⾥表⽰如果当前页⾯是登陆页⾯,跳转到登陆页⾯ try{ filterChain.doFilter(request, response); } catch (Exception e){ throw e; } return; } } public void init(FilterConfig filterConfig)throws ServletException{ }}
    web.xml ⽂件
    <?xml version=”1.0” encoding=”UTF-8”?> <web-app xmlns=”http://xmlns.jcp.org/xml/ns/javaee” version=”3.1”><filter> <filter-name>userLoginCheckFilter</filter-name> <filter-class>mywebapp.userLoginCheckFilter</filter-class></filter> <filter-mapping> <filter-name>userLoginCheckFilter</filter-name> <url-pattern>*.jsp</url-pattern> </filter-mapping> </web-app>
    4.1.4 JSP内置对象的使用本成绩管理系统用了4种JSP内置对象,分别是Request、Response、Out、Session。下面分别列出:
    4.1.4.1 RequestString idstring=ChangeEncoding(request.getParameter(”id”).trim()); String namestring=ChangeEncoding(request.getParameter(”name”).trim()); String cppstring=request.getParameter(”cpp”); String cirstring=request.getParameter(”circuit”);String Ewritestring=request.getParameter(”Ewrite”); String Elistenstring=request.getParameter(”Elisten”); String physicsstring=request.getParameter(”physics”);String prostring=request.getParameter(”probability”); String hisstring=request.getParameter(”history”);String xingzhengstring=request.getParameter(”xingzheng”); String pestring=request.getParameter(”pe”); String discretestring=request.getParameter(”discrete”);
    4.1.4.2 Responseresponse.sendRedirect(”teacharMenu.jsp”);
    4.1.4.3 Outout.print(”<center>”); out.println(”<P><font size=2 color=’blue’>”+” 管理员⽤户或密码错误”+”</font>”);
    4.1.4.4 Sessionsession.setAttribute(”teacherName”,name); session.setAttribute(”teacherPassword”,password); String user=(String)session.getAttribute(”teacherName”); String pwd=(String)session.getAttribute(”teacherPassword”);
    4.1.5 多用户管理设计与实现首先进入系统时出现选择用户,如果选择管理员需要知道管理员账号密码,如果选择学生, 需要该学生的学号在数据库中而且密码正确。
    5 系统演示登录

    增加


    删除


    查找


    修改

    排名

    6 总结6.1 系统缺陷与不足
    系统运用的查找算法都是暴力算法,如果面对很多数据的话,系统运行会缓慢,可以运用二分查找算法或者改变存储的数据结构进行优化
    系统编码不够灵活,编码转换方面容易产生乱码

    6.2 系统可扩展功能
    增加选课功能
    增加多个管理员功能
    增加学生自助修改密码功能
    增加一系列功能做成教务系统
    3 评论 37 下载 2018-11-05 12:31:35 下载需要6点积分
  • 基于JAVA的聊天室

    聊天室系统
    一、课程设计要求与目的目的:编写一个小型Java聊天室系统,掌握Java网络通信、多线程、IO文件操作等高级应用编程技能。
    完成如下功能:

    多客户端模式下,实现客户与客户的单独通信,要求信息通过服务器中转
    端到端的通信,实现并行通信模式(不再是你说一句,我说一句,一端的信息发送不受另一端的影响)
    实现端到端的文件传输
    添加图形界面(选做)

    二、系统设计服务器端:分为两个class文件,一个是MultiTalkServer类,还有一个是ServerThread类。MultiTalkServer实现的功能有:监听客户请求,根据得到的Socket对象和客户计数创建服务线程,并启动。将serverSocket.accept()返回的socket作为参数,传入到ServerThread中。
    ServerThread实现的功能有:判断是否下线,判断IP地址是否正确,判断目标客户是否在线,判断是否传输文件,获取目标的socket套接字。

    三、系统实现服务器端:

    MultiTalkServer:
    public class MultiTalkServer{ 静态成员变量,记录当前客户的个数 public static void main(String args[]) { 初始化 新建服务器套接字 while(listening){//监听 监听到客户请求,根据得到的Socket对象和客户计数创建服务线程,并启动它 }
    ServerThread:
    public class ServerThread extends Thread{ 数据成员: 保存与本线程相关的Socket对象/保存本进程的客户计数新建哈希表,用于保存用户的Ip与socket的键值对String IP用于保存IP地址的模板 构造方法 初始化socket变量 初始化clientnum变量将IP和socket放入哈希表中 splitMessage判断用户是否退出当发送空消息或发送“quit”时则退出 用于发送文件 SendDocument 用于创建输入输出流并收发文件 线程主体run() //获取哈希表的大小并输出; //创建输入输出流 while(true) {//监听 ①判断是否要下线如果下线则发送quit并将下线用户套接字移除 ②判断输入的IP是否为正确的IP地址如果IP地址不能和模板相匹配,则输出Wrong IP ③判断目标客户是否在线如果不在线则输出:The Client you call is not online! ④判断是否传输文件 获取目标的Socket套接字 调用模块进行文件发送 关闭Socket输出流 关闭Socket输入流 关闭Socket}
    四、系统测试4.1 收发消息基本功能测试在用户输入对象的IP 发送的消息后,对方可以收到

    4.2 收发文件进本功能测试在用户输入对象的IP +消息+文件路径后,对方可以收到

    4.3 异常测试4.3.1 发消息4.3.1.1 格式错误4.3.1.2 格式正确
    IP地址错误
    目标客户不在线

    4.4 发文件4.4.1 格式错误会当成消息发送出去
    4.4.2 格式正确
    IP地址错误
    目标客户不在线
    找不到文件
    文件重名(文件覆盖)

    会成功发送,但是会将源文件覆盖。
    五、课程设计总结在本次课程设计中,学会了Java网络编程。实现了端到端通信,实现并行通信模式(不再是你说一句,我说一句,一端的信息发送不受另一端的影响),实现端到端的文件传输。学会了收发消息、收发文件的相关操作,输入输出流的包装,知道了做项目的基本流程。理解了Java要按接口编程的核心思想。
    1 评论 1 下载 2019-04-20 23:25:07 下载需要5点积分
  • 基于C#实现的双人对战与道具赛的俄罗斯方块小游戏

    1.系统总体设计要完成本系统中俄罗斯方块,必须考虑以下几个问题:

    如何判断一个方块下落到底部或是发生碰撞
    如何预测一个方块最终能下落到达的位置并给以玩家提示
    如何判断一个方块在进行左右移动或是旋转时不碰触边界与其他方块
    如何让一个方块时时下落更新
    如何将道具俄罗斯方块与普通俄罗斯方块区分

    为此,需要为上面的几个判断依次写明方法,最后可重用,而对于俄罗斯方块的位置可以使用数组存放。
    特别的,对于双人俄罗斯方块,还需要考虑:

    要实现本玩家消除多行时给对面玩家添加相应行数
    要实现本玩家消除特定行时给对面玩家添加捣乱方块

    为此,需要写明2个方法记录自己相应的消除行数以及添加给对面玩家。
    数据结构:数组,全局变量。
    2.系统功能设计本程序的主要功能如下图所示:


    运行程序进入主界面,选择单人或双人游戏
    单人游戏

    用方向键来移动,上方向键来旋转下落过程中方块会实时产生下落预测图方块碰到左右边界无法继续移动,方块旋转后产生的图形超过边界或碰到已有方块则不能旋转,方块下落到边界或碰到已有方块则不再下落并随机生成新方块新方块有一定几率是特殊方块,红色方块能令所在行随机消除,绿色则是随机填充当方块产生的初始位置被填充,游戏结束
    双人游戏

    玩家一用方向键移动,上方向键旋转,玩家二用wasd移动,w键旋转2内bcde当玩家连续消除2行或3行方块时,给对手增加这些方块当玩家连续消除4行方块时,给对方添加特殊方块,红色方块能令所在行随机消除,绿色则是随机填充
    游戏结束

    3.类的设计


    类名
    Form1




    属性
    int x1 玩家一方块一的x坐标


    属性
    int y1 玩家一方块一的y坐标


    属性
    int x2 玩家一方块二的x坐标


    属性
    int y2 玩家一方块二的y坐标


    属性
    int x3 玩家一方块三的x坐标


    属性
    int y3 玩家一方块三的y坐标


    属性
    int x4 玩家一方块四的x坐标


    属性
    int y4 玩家一方块四的y坐标


    属性
    int x5 玩家二方块一的x坐标


    属性
    int y5 玩家二方块一的y坐标


    属性
    int x6 玩家二方块二的x坐标


    属性
    int y6 玩家二方块二的y坐标


    属性
    int x7 玩家二方块三的x坐标


    属性
    int y7 玩家二方块三的y坐标


    属性
    int x8 玩家二方块四的x坐标


    属性
    int y8 玩家二方块四的y坐标


    属性
    int size 方块的长宽


    属性
    int youtu[,] 记录玩家一各个区域有无方块


    属性
    int P2youtu[,] 记录玩家二各个区域有无方块


    属性
    int type 记录玩家一方块代号


    属性
    int P2type 记录玩家二方块代号


    属性
    int turn 记录玩家一方块旋转次数


    属性
    int P2turn 记录玩家二方块旋转次数


    属性
    int specialtype 记录玩家一特殊方块代号


    属性
    int P2specialtype 记录玩家二特殊方块代号


    属性
    bool onlyone 记录是否是单人模式


    方法
    private bool meetnow(int tempx1,int tempx2,int tempx3,int tempx4, int tempy1, int tempy2,int tempy3,int tempy4,int[,] temp) //旋转后是否碰撞的计算函数


    方法
    private void special_init(ref int tempspecialtype)//特殊方块生成


    方法
    private void block_init(ref int type,ref int turn)//玩家一普通方块生成


    方法
    private void P2block_init(ref int P2type, ref int P2turn) //玩家二普通方块生成


    方法
    private void tianblock_init(ref int x1,ref int x2,ref int y1,ref int y2,ref int x3,ref int x4,ref int y3,ref int y4)//田子型block初始化


    方法
    private void long_init(ref int x1, ref int x2, ref int y1, ref int y2,ref int x3,ref int x4,ref int y3,ref int y4)//长条型block,type1


    方法
    private void turn0_long_init(ref int tempx1, ref int tempx2, ref int tempy1, ref int tempy2, ref int tempx3, ref int tempx4, ref int tempy3, ref int tempy4,ref int tempturn)//长条型block旋转


    方法
    private void turn1_long_init(ref int tempx1, ref int tempx2, ref int tempy1, ref int tempy2, ref int tempx3, ref int tempx4, ref int tempy3, ref int tempy4, ref int tempturn)//长条型block旋转2


    方法
    private void tublock_init(ref int x1, ref int x2, ref int y1, ref int y2, ref int x3, ref int x4, ref int y3, ref int y4)//凸型block初始化


    方法
    private void turn0_tublock_init(ref int tempx1, ref int tempx2, ref int tempy1, ref int tempy2, ref int tempx3, ref int tempx4, ref int tempy3, ref int tempy4,ref int tempturn)//凸型block旋转


    方法
    private void turn1_tublock_init(ref int tempx1, ref int tempx2, ref int tempy1, ref int tempy2, ref int tempx3, ref int tempx4, ref int tempy3, ref int tempy4, ref int tempturn)//凸型block旋转2


    方法
    private void turn2_tublock_init(ref int tempx1, ref int tempx2, ref int tempy1, ref int tempy2, ref int tempx3, ref int tempx4, ref int tempy3, ref int tempy4, ref int tempturn)//凸型block旋转3


    方法
    private void turn3_tublock_init(ref int tempx1, ref int tempx2, ref int tempy1, ref int tempy2, ref int tempx3, ref int tempx4, ref int tempy3, ref int tempy4, ref int tempturn)//凸型block旋转4


    方法
    private void RZblock_init(ref int x1, ref int x2, ref int y1, ref int y2, ref int x3, ref int x4, ref int y3, ref int y4)//RZ型block


    方法
    private void turn0_RZblock_init(ref int tempx1, ref int tempx2, ref int tempy1, ref int tempy2, ref int tempx3, ref int tempx4, ref int tempy3, ref int tempy4,ref int tempturn)//RZ型block旋转


    方法
    private void turn1_RZblock_init(ref int tempx1, ref int tempx2, ref int tempy1, ref int tempy2, ref int tempx3, ref int tempx4, ref int tempy3, ref int tempy4,ref int tempturn) //RZ型block旋转2


    方法
    private void LZblock_init(ref int x1, ref int x2, ref int y1, ref int y2, ref int x3, ref int x4, ref int y3, ref int y4)//LZ型block


    方法
    private void turn0_LZblock_init(ref int tempx1, ref int tempx2, ref int tempy1, ref int tempy2, ref int tempx3, ref int tempx4, ref int tempy3, ref int tempy4,ref int tempturn) //LZ型block旋转


    方法
    private void turn1_LZblock_init(ref int tempx1, ref int tempx2, ref int tempy1, ref int tempy2, ref int tempx3, ref int tempx4, ref int tempy3, ref int tempy4,ref int tempturn)// LZ型block旋转2


    方法
    private void LLblock_init(ref int x1, ref int x2, ref int y1, ref int y2, ref int x3, ref int x4, ref int y3, ref int y4)//LL型block


    方法
    private void turn0_LLblock_init(ref int tempx1, ref int tempx2, ref int tempy1, ref int tempy2, ref int tempx3, ref int tempx4, ref int tempy3, ref int tempy4,ref int tempturn) //LL型block旋转


    方法
    private void turn1_LLblock_init(ref int tempx1, ref int tempx2, ref int tempy1, ref int tempy2, ref int tempx3, ref int tempx4, ref int tempy3, ref int tempy4,ref int tempturn) //LL型block旋转1


    方法
    private void turn2_LLblock_init(ref int tempx1, ref int tempx2, ref int tempy1, ref int tempy2, ref int tempx3, ref int tempx4, ref int tempy3, ref int tempy4,ref int tempturn) //LL型block旋转2


    方法
    private void turn3_LLblock_init(ref int tempx1, ref int tempx2, ref int tempy1, ref int tempy2, ref int tempx3, ref int tempx4, ref int tempy3, ref int tempy4,ref int tempturn) //LL型block旋转3


    方法
    private void RLblock_init(ref int x1, ref int x2, ref int y1, ref int y2, ref int x3, ref int x4, ref int y3, ref int y4)//RL型block,


    方法
    private void turn0_RLblock_init(ref int tempx1, ref int tempx2, ref int tempy1, ref int tempy2, ref int tempx3, ref int tempx4, ref int tempy3, ref int tempy4,ref int tempturn)


    方法
    private void turn1_RLblock_init(ref int tempx1, ref int tempx2, ref int tempy1, ref int tempy2, ref int tempx3, ref int tempx4, ref int tempy3, ref int tempy4,ref int tempturn)


    方法
    private void turn2_RLblock_init(ref int tempx1, ref int tempx2, ref int tempy1, ref int tempy2, ref int tempx3, ref int tempx4, ref int tempy3, ref int tempy4,ref int tempturn)


    方法
    private void turn3_RLblock_init(ref int tempx1, ref int tempx2, ref int tempy1, ref int tempy2, ref int tempx3, ref int tempx4, ref int tempy3, ref int tempy4,ref int tempturn)


    方法
    private void button1_Click(object sender, EventArgs e)//单人游戏开始


    方法
    private void button2_Click(object sender, EventArgs e) 双人游戏


    方法
    private void timer2_Tick(object sender, EventArgs e) 双人游戏页面实时刷新


    方法
    private void timer1_Tick(object sender, EventArgs e) 单人游戏页面实时刷新


    方法
    protected override bool ProcessCmdKey(ref Message msg, Keys keyData) 键盘监听方法重写



    主要成员方法的流程图
    private bool meetnow(int tempx1,int tempx2,int tempx3,int tempx4, int tempy1, int tempy2,int tempy3,int tempy4,int[,] temp) //旋转后是否碰撞的计算函数

    private void block_init(ref int type,ref int turn)

    private void button1_Click(object sender, EventArgs e)//单人游戏开始

    4.主程序设计
    定义了变量int x1-x8,y1-y8 记录方块坐标
    定义了变量int size 记录方块大小
    定义了变量两个数组int youtu[,],int P2youtu[,] 存放已经下落的方块
    定义了变量 int type P2type 记录玩家方块代号
    定义了变量int turn int P2turn 记录玩家方块旋转次数
    定义了变量int specialtype int P2specialtype 记录玩家特殊方块代号
    定义了变量bool onlyone 记录是否是单人模式
    主要的功能实现是在Form1.cs中实现的

    在 类 Form1中

    根据游戏区的长宽对方块大小初始化中
    实时刷新页面下落方块以及生成下落预测页面,并检测当前方块是否发生碰撞,如果发生碰撞,则调用block_init重新生成新方块,如果最上方已经有方块,则游戏结束
    通过方法ProcessCmdKey(ref Message msg, Keys keyData)与meetnow,实现玩家用方向键或wasd键改变方块坐标并检测该坐标是否有方块,以此实现方块的左右移动,加速下落,旋转方块

    5.调试分析
    画图闪烁
    解决方式:不在控件上直接绘图,使用双缓冲,先构造新的位图,在其上绘图,然后将picturebox的图片设置为该图,而不是在picturebox上绘图。
    如何让方块保留在图上同时有让新的方块下落
    解决方法:构造两个新的位图,一个为背景图,一个为前景图。
    键盘响应事件对方向键无反应
    解决方法:重写键盘响应事件。
    如果一开始就按下方向键会报错
    解决方法:如果一开始就按下方向键会报错,因为是将坐标转换为数组的10*20行这样y1/size的坐标就是负的, 数组没有负的,故在该方法执行前判断是否y大于0。
    如果向左或向右突然插空到已有方块上方,图形会向下一格再绘图,而不是like检测到碰撞
    解决方法:更改原先绘图算法,原先算法有问题,在实时刷新时是先向下下落再检测是否会碰撞,未考虑键盘事件的影响,故将算法改为实时刷新时先检测碰撞再绘制图片。
    左右移动碰撞被阻拦会闪烁
    解决方法:键盘按下时是单独处理的 这时候也应该进行绘图。
    图片旋转后总是下降一行
    解决方法:键盘按下时是单独处理的 刷新写在计时器,应该旋转也写一个。
    双人在给对方增加行的时候下面有方块的全部往上挪,也即如果该列有方块,会填满到该列最上层
    解决方法:原先算法有问题,在给对方增加行的时候 不止上一行等于当前这行,当前这行也得置
    特殊方块中的生长方块上线行生长几乎一样,消除方块消除也总是一样,给对面增加行的时候留空也几乎一样
    解决方法:C#random是伪随机,短时间内生成的随机数几乎一样,故使用guid做随机数种子真随机
    在最后一行旋转时报超出数组边界
    解决方法:最后一行旋转产生的图片可能会超过边界,这时候要先判断是否到底

    6.测试结果启动程序

    下落方块

    特殊方块1

    特殊方块1效果

    特殊方块2

    特殊方块2效果
    1 评论 2 下载 2019-04-20 16:16:43 下载需要14点积分
  • 基于JAVA的葫芦娃小游戏


    使用javaFx作为框架编写,并根据MVC框架将视图和控制分离
    双击SimpleHuluGameFx.jar运行游戏_
    或进入proj文件夹使用命令mvn package执行测试并生成jar可执行文件,java -jar target\hulagame-1.0-SNAPSHOT.jar运行游戏
    120181227.xml为一次游戏的存档,正确读档后会重现妖怪的一次胜利过程(剩下最后一击由您完成)

    1. 使用方法1.1 运行游戏通过双击ljy.jar运行游戏,准备界面
    点击“开始游戏”开始一个新游戏,点击“读取文件”读取一个存档,点击右上角的”X“退出游戏。所有窗口都可以拖动调整位置
    1.2 新游戏开始一个新游戏后会首先进入模式选择界面,共有“控制人类战斗”,“控制腰妖怪战斗”,“自动战斗”,““双人模式”四种游戏模式。

    控制人类战斗:点击左侧的葫芦兄弟头像即可开始协助人类战斗。
    控制妖怪战斗:点击右侧的蝎子精头像即可开始协助妖怪战斗。
    自动战斗:点击自动战斗后人类和妖怪将由电脑控制开始战斗。
    双人模式:两名玩家分别控制人类和妖怪互相进攻。

    开始游戏后会有一段入场动画,等到所有人物入场完毕后即可开始游戏。

    1.2.1 移动使用鼠标选中一个角色后再点击空白区域即可将该角色移动到目的地。无法移动到有角色的位置
    1.2.2 攻击选中一个角色后左侧会弹出技能栏,由上到下有三个按钮分别为普通攻击、对群攻击和必杀技攻击。不同角色和攻击种类的攻击效果不同。

    普通攻击: 普通攻击可以使用无限次,没有远程属性的角色会攻击面前的敌人,具有远程属性的角色可以攻击在同一行上远距离的敌人。
    对群攻击:对群攻击会消耗一定的MP,会对前、上、下三个位置的敌人造成伤害。
    必杀技攻击:必杀技攻击每个角色每回合只能使用一次,不消耗MP,具有极高的伤害,攻击范围同普通攻击。

    无法使用的攻击方式不会在属性栏中再出现
    1.2.3 治疗某些角色为辅助角色,只能够为己方治疗,不能够攻击敌人。人类方为具有远程治疗能力的爷爷,妖怪方面为具有近程治疗能力的蛇精,使用方法和攻击一致。
    1.2.4 人物状态鼠标移动到角色上时下方会弹出角色的属性栏,包括角色的HP和MP。属性框会在无操作两秒后隐藏。

    以后可能会加入攻击力等更详尽的信息。

    1.2.5 死亡角色HP耗尽后会死亡并变成墓碑,退出战斗。
    1.2.6 人类人类阵营包括七个葫芦娃和爷爷。

    爷爷:
    爷爷是人类阵营的辅助角色,可以远程治疗其他人类。
    葫芦兄弟:
    大娃HP最多(120),并且必杀技攻击力极强(90),不过普通攻击(10)和对群攻击(20)较弱。二娃为远程攻击角色,并且对群攻击可以释放出闪电,威力极强(40)。三娃为远程攻击角色,普通攻击很弱(5),不过必杀技很强(80),可以在后方协助近战角色实现斩首战术。四娃负责主要攻击输出,生命值很低(60),不过对群攻击(40)和必杀技(90)都极高,在战场上经常需要爷爷的治疗。五娃为综合能力最强的角色,三种攻击力分别为(20,30,90),且血量为(100)。六娃血量(150)和普通攻击(30)都很高,适合冲锋在最前方。七娃是最弱的角色。

    1.2.7 妖怪妖怪分为如下三种,分别具有不同特性:

    蝎子精:
    是小妖怪的首领,妖怪阵营的首领,具有极强的攻击力和血量,如果将其击溃则小妖精不会再变换阵型。蝎子精使用普通攻击\必杀技时其他小喽啰也会一起普通攻击\必杀技蝎子精使用对群攻击时其他小喽啰会变阵Tips: 蝎子精在第二回合后可以使用群攻型必杀技,即带领所有小妖怪使用必杀技。Tips: 注意!蝎子精胆子很小如果受到大量伤害会变阵退到阵型的最后方,因此最好抓住机会在一个回合内一击必杀。
    小喽啰:
    最基础的小妖怪,不具有远程攻击能力,攻击力和HP值都很低,不过数量众多。
    蛇精:
    辅助型妖怪,具有比爷爷还要强大的对群治疗能力,会在每回合对妖怪进行一次治疗。Tips: 为了避免被小妖怪消耗致死,需要尽快击破蛇精后减少小妖怪的数量。


    以后可能会增加双人对战模式。

    1.2.8 回合本游戏为回合制游戏,为平衡人类和妖怪的实力,每个回合协助人类时具有三次有效操作机会(移动,攻击,治疗),协助妖怪时有两次有效。
    使用妖怪阵营时操纵小妖怪布阵不消耗操作机会

    人类剩余操作数会以红色圆球的方式在窗口上方显示:


    妖怪剩余操作数会以红色圆球的方式在窗口上方显示:

    玩家操作用尽后会进入敌人的攻击回合,包含如下三个部分:

    妖怪的自动攻击(玩家选择人类阵营):
    所有攻击型妖怪具有一次任意方式攻击机会蛇精会寻找受伤的妖怪治疗蝎子精可以选择是否变阵
    人类的自动攻击(玩家选择妖怪阵营):
    所有葫芦娃移动一次到合适的攻击地点所有葫芦娃可以进行一次攻击,近战葫芦娃会优先使用必杀技,远程葫芦娃会将必杀技留给蝎子精爷爷会寻找受伤的葫芦娃治疗

    下面是操纵人类战斗两个回合的演示:

    1.2.9 胜负判定当一方阵营全部被消灭后游戏结束,弹出结束提示,游戏结束后可以继续操作人物,不过系统不再保存人物行为。
    1.2.10 自动战斗自动战斗模式下人类和妖怪会自主按照自己的战斗逻辑战斗,此时无法控制角色,只能查看角色状态。
    下面是一小局自动战斗的演示:

    1.3 保存游戏随时可以保存游戏,点击游戏界面右上角的保存图标

    即可保存当前进度,游戏进度会保存为xml文件。
    1.4 读取进度在准备界面点击“读取文件”后选择一个之前保存的文件即可开始读取。目前的实现的自动播放一遍之前的所有游戏过程后,此阶段无法控制人物。在加载完毕后即可继续游戏。
    为了增强游戏体验,读取进度时角色移动动画和攻击动画都会加倍播放。
    1.5 退出游戏在准备界面和游戏界面右上角都有关闭按钮

    点击即可关闭游戏。
    2. 设计思路此版本和之前版本设计思路完全不同,采用了MVC设计模式,javaFx开发框架。下面从Model, View和Control三个方面简述设计思路。
    2.1 资源管理为了方便资源管理,将程序需要的所有属性和资源都封装到抽象类Configs和ViewBundle中。
    Configs中保存窗口布局相关的数值属性和游戏中人物贴图和其他游戏中使用的系统贴图,所有贴图通过ArrayList储存,并提供static final int下标按照如下命名格式进行索引。
    //贴图索引 public static final int INDEX_PREBACKGROUND = 0; public static final int INDEX_BACKGROUND = 1; public static final int INDEX_START = 2; public static final int INDEX_LOAD = 3; public static final int INDEX_CLOSE = 4; public static final int INDEX_DCLOSE = 5; //... //布局属性 public static int WIN_HEIGHT; public static int WIN_WIDTH; public static int TOP_MARAGIN; public static int BOTTOM_MARAGIN; public static int LEFT_MARGIN; public static int RIGHT_MARGIN; //...
    ViewBundle保存所有角色的攻击效果贴图,同样按照index寻址
    3. MVC模式说明3.1 模型Model本游戏主要模型即角色(Charactor)。
    3.1.1 角色每个角色都运行在一个线程中,具有攻击力、HP、MP等战斗属性和当前位置、移动、存活等其他属性。Charactor的主要逻辑如下:
    /** 角色在真实画布上的位置*/ public AtomicInteger PositionX=new AtomicInteger(0), PositionY=new AtomicInteger(0); /** 当前是否正在执行操作*/ public AtomicBoolean avaliable=new AtomicBoolean(true); /** 用于接收命令*/ public AtomicInteger cmd=new AtomicInteger(0); @Override public void run() { while (true) { try { Thread.sleep(50); } catch (Exception e) { e.printStackTrace(); } cmdHandler(); //命令处理函数 } } /** 在循环中等待新命令*/ protected void cmdHandler() { switch (cmd.get()) { case 0: break; //当前无任务 case 1: WalkToDst(); cmd.set(0);break; //1 号命令为移动到目的地 case 2: Attack1(); cmd.set(0); break; //2 号命令为普通攻击 case 3: Attack2(); cmd.set(0); break; //3 号命令为对群攻击 case 4: Attack3(); cmd.set(0); break; //4 号命令为必杀攻击 default: break; } }
    移动:角色移动过程中会在线程中定时更新自己的坐标和当前贴图的描述,OutlookManager定期使用对应的的贴图重绘即可形成移动的动画。
    攻击:每个攻击效果(Bullet)对象具有自己的位置和贴图属性,Charactor通过一个静态类bulletController获得攻击效果移动的逻辑后更新Bullet的位置,OutlookManager定期将攻击效果绘制在Canvas上形成攻击动画。
    3.2 视图View在javaFx中我使用的主要视图控件为没有布局的Pane,游戏界面的主要控件如下:

    场景Stage:
    场景中的Pane负责承载所有其他UI对象,并接收鼠标行为,并将MouseEvent传递给battleManager做出解析。
    画布Canvas:
    画布用来绘制所有战斗动画效果并形成一个FPS动画,由视图控制类OutlookManager控制每秒更新36次,形成连贯动画。
    技能栏SelectionBar:
    技能栏负责显示当前人物可用的技能,接收使用技能的命令,并传递给战斗控制类battleManager处理。当前无选择角色时会隐藏,重新选中角色后battleManager会通知技能栏更新数据并弹出。
    属性栏CInfoBar:
    属性栏负责显示当前鼠标经过的角色当前属性,内置一个计时器,会在无操作两秒后隐藏,battleManager在需要出现时通知属性栏弹出。

    3.3 控制Control游戏中主要的控制类有三个:控制战斗逻辑的battleManager,负责刷新界面的OutlookManager,控制人类行为的hBot类和负责控制妖怪行为的mBot类,其中后三者都为前者的成员变量。所有控制类都有一个角色数组储存所有角色的引用。

    battleManager
    battleManager会创建所有角色并开始其线程,其主要逻辑如下:

    static final int ACTION_CLICKED = 0; static final int ACTION_MOVEABOVE = 1; /** 接收一个鼠标事件*/ public Charactor newAction(iPoint loc, int type) { //重放时不能控制角色 if(type == ACTION_CLICKED && autoplaying) return null; for(Charactor x: creatures) { if(x.alive && x.avaliable.get()) { iPoint vp = virtualField.rpTovp(x.PositionX.get()+Configs.B_SIZE/2, x.PositionY.get()+Configs.B_SIZE/2); if(loc.x==vp.x && loc.y==vp.y) { if(type == ACTION_CLICKED) //如果是点击则为选择一个人类角色 ChatSelected = x; if(type == ACTION_MOVEABOVE || (!bind.get() && !x.monster)) //如果为鼠标经过则为显示角色属性 return x; } } } if(type == ACTION_CLICKED) { if(ChatSelected!=null && ChatSelected.monster) ChatSelected = null; //TODO: 通知当前选中角色移动,保存玩家操作并减少玩家步数 if(!bind.get() && ChatSelected != null) { ... } } return null; } /** 玩家步数减少1,并且判断是否轮到敌人进攻*/ public void stepDecrease() { if(End) return; stepRemain--; //步数减一 view.hint.set(stepRemain); if(stepRemain==0) //轮到妖怪回合 { bind.set(true); //禁用玩家操作 new Thread(()->{ while(true) //等待所有角色结束当前行为 { ... } bot.nextMove(); //阻塞等待怪物完成自己的回合 bind.set(false); //启用玩家操作 }).start(); stepRemain = 3; view.hint.set(stepRemain); } } /** 游戏结束,通知View执行结束动画并终止战斗逻辑*/ public void GameEnd(boolean monster) { ... }

    OutlookManager

    OutlookManager本身为一个线程,每1/36s会重绘一次Canvas制作逐帧动画,需要注意的是所有与视图相关操作都需要在主线程中完成,这里在重绘时使用了Platform.runlater避免出现同步问题。
    mBot和hBot

    Bot类封装的是电脑控制的战斗逻辑,提供public void nextMove()接口,计算、通知、并阻塞等待所有妖怪\人类完成自己的行为。

    4. 其他重要组件4.1 游戏保存SaveStack负责保存游戏进度,提供如下接口:
    /** * 添加一次操作信息 * @param chatId 角色ID * @param dstx 移动目的地横坐标 * @param dsty 移动目的地纵坐标 * @param type 操作类型 */ public void addMove(int chatId, int dstx, int dsty, int type)
    每次玩家做出有效操作后battleManager或SelectionBar会调用此接口保存玩家操作,点击保存按钮后会将当前xml树写入文件。保存时采用xml文件,格式如下:
    <?xml version="1.0" encoding="UTF-8" standalone="no"?><battle> <ctime>2018-12-19 02:38:22</ctime> <battle> <move ChatId="3" X="2" Y="3" fmt="0">0</move> <move ChatId="3" X="0" Y="0" fmt="0">4</move> <move ChatId="3" X="0" Y="0" fmt="0">2</move> <move ChatId="8" X="0" Y="0" fmt="0">5</move> <move ChatId="9" X="9" Y="0" fmt="0">0</move> </battle></battle>
    4.2 读取进度AutoPlayer负责解析xml文件并在线程中控制每个角色的行为,battleManager会一直设置禁用玩家操作直到读取结束(此线程结束)。
    4.3 线程同步问题
    所有只涉及同步读写的简单同步问题(如角色的位置)都使用Atomic变量保证同步。
    所有视图相关操作都在主线程中完成。
    属性栏和技能栏的自动弹出/消失存在较复杂的同步问题,如在弹出过程中接收到消失指令。这里使用两个Lock锁和一个boolean变量hide保证同步具体逻辑见CinfoBar.java或SelectionBar.java中的Hide和Showup方法。
    发现在变阵时会出现如下问题:

    例:A,B线程在数组中移动,其中A: 1->2、B: 3->1 <br>    B: clear 3 <br>    B: write 1 <br>    A: clear 1 <br>    A: write 2 <br>如按照上述顺序执行则B会消失。

    已经通过移动后重新刷新cmap修复。

    4.4 战场透视映射由于之前视图时通过虚拟坐标映射、并且将视图和控制分离,因此实际上不需要对模型进行修改,这也是MVC模式的方便之处。
    只需在OutLookManager绘制FPS每一帧时进行透视坐标转换即可,透视算法在知道四个基准点的映射前后位置实际上可以看作线性插值。之前方格的布局是通过Configs.B_SIZE设定的,为了完成透视映射为Configs增加如下方法:
    /** 判断坐标(x,y)是否在row, col透视后的四边形中。* * @return 如果在四边形中则返回此四边形的属性Block, 否则返回null */ public static Block IsInBlock(double x, double y, int row, int col) /** * 获得一个透视后方格的属性 * @param x 透视前纵坐标 * @param y 透视前横坐标 * @return Block对象,描述方格的属性 */ public static Block SPEC_MID_SIZE(int x, int y)
    其中Block(Configs.java)中包含了透视映射后的梯形位置信息。
    4.5 注解在Annotations.java文件中定义了注解类型IsCharactor和用于解析注解并生成说明文档的HintGenerater类。
    4.5.1 IsCharactorIsCharactor包含一个人物的姓名、战斗属性等基本属性,对所有继承自Charactor类的角色都添加此注解。
    4.5.2 HintGeneraterHintGenerater使用了单例模式,每次运行游戏只会生成一个文档,并且生成完毕后立刻销毁HintGenerater对象,主要代码如下:
    class HintGenerater{ static HintGenerater generater; static Class[] clazzlist = {Grandpa.class, Scorpion.class, Snake.class}; static String helpstring = ""; static{ generater = new HintGenerater(); } private HintGenerater(){ } //隐藏初始化器 public static boolean GenerateHintDoc(){ if(generater==null) return false; CucurbitBoys[] brothers = CucurbitBoys.values(); for(CucurbitBoys boy:brothers) { ...//由于葫芦娃是通过enum类生成的,因此无法需要单独解析 } for(Class clazz:clazzlist) { if(clazz.isAnnotationPresent(IsCharactor.class)) { ...//解析注解内容 } } //保存到文件Hint.txt中 generater = null; //生成后即销毁HintGenerater return true; }}
    4.6单元测试使用JUnit实现了两个测试,检测是否缺失资源文件和检测UI布局属性是否正确、检测虚拟坐标和真实坐标相互转换算法是否正确。
    需要注意使用JavaFx直接做测试时会抛出Internal graphics not initialized yet异常,我直接为AppTest类增加了一个JFXPanel初始化Graphic。
    测试代码如下:
    @Test public void testConfigs() { try{ Configs.initialize(); }catch (IllegalArgumentException e){ assertTrue("贴图加载失败,请重新下载贴图",false); //如果缺少贴图则在加载是会抛出无效URL的异常 }catch (Exception e){ assertTrue("未知加载错误",false); //其他异常 } //MARK: 左侧边界+右侧边界+战斗区域 = 窗口宽度 assertTrue("横向布局参数错误",Configs.LEFT_MARGIN + Configs.B_SIZE*Configs.B_WNUM + Configs.RIGHT_MARGIN == Configs.WIN_WIDTH); //MARK: 上方边界+上方边界+战斗区域 = 窗口高度 assertTrue("纵向布局参数错误",Configs.TOP_MARAGIN + Configs.B_SIZE*Configs.B_HNUM + Configs.BOTTOM_MARAGIN == Configs.WIN_HEIGHT); assertTrue( true ); } @Test public void testVpToRp() { Configs.initialize(); Random random = new Random(); //MARK:100次随机坐标转换测试, 测试坐标应该落在同一个格子中,即返回的虚拟坐标为(3,2) iPoint result; for(int i=0;i<100;i++) { //相当于随机在B_SIZE*B_SIZE的方格内选点后判断在哪个格子内 result = virtualField.rpTovp(Configs.LEFT_MARGIN + 3 * Configs.B_SIZE + random.nextInt(Configs.B_SIZE-1), Configs.TOP_MARAGIN + 2 * Configs.B_SIZE + random.nextInt(Configs.B_SIZE-1)); assertTrue( "实际坐标到虚拟坐标转换错误",result.x == 3 && result.y == 2); } result = virtualField.vpTorp(0,0); //测试(0,0)虚拟方格的起始坐标应该恰好为上方边界宽度和左侧边界宽度。 assertTrue( "虚拟坐标到实坐标转换错误",result.x == Configs.LEFT_MARGIN && result.y == Configs.TOP_MARAGIN); assertTrue(true); }
    1 评论 0 下载 2019-04-19 19:53:14 下载需要6点积分
  • 基于中央定位服务器的P2P网络聊天系统设计

    1 需求分析即时通讯 (Instant Messaging) 是目前 Internet 上最为流行的通讯方式,各种各样的即时通讯软件也层出不穷;服务提供商也提供了越来越丰富的通讯服务功能。不容置疑,Internet 已经成为真正的信息高速公路。
    微信 (Wechat) 是腾讯公司于 2011 年 1 月 21 日推出的一个为智能终端提供即时通讯服务的免费应用程序。截止到 2016 年第二季度,微信已覆盖中国 94% 以上的智能手机,并由其界面简练的特性使得多数人放弃使用 QQ 转而使用微信。
    本项目要求实现基于中央定位服务器的 P2P 网络聊天系统设计。客户端与服务器之间使用 C-S通信,客户端之间对话使用 P2P 进行通信,这两点即为本项目需要实现的核心。
    2 总体设计2.1 整体流程图
    2.2 状态机
    2.3 选做内容本项目完成了所有的必做任务,选做内容如下。
    动态表情发送 本项目实现了对动态表情发送的功能,支持的文件格式为 gif。
    聊天记录查询 使用 SQL 数据库对聊天记录进行存储。在每次登出时,将所有聊天记录存入数据库;在每次登录成功后,读取数据库中的聊天记录,以实现在登出后再次登录时,能够查询之前的聊天记录。
    快速查找好友并发起对话 一般来讲,每个人会有数百个好友。如果想与某一位好友对话时,如果一点一点翻联系人列表会耗费大量时间,故设计快速查找好友功能以实现对话的快速发起。
    新消息提醒功能 在收到消息时,在列表的正下方会进行消息提醒,即提醒发来新消息的人的学号。
    2.4 开发环境本项目基于 VS2017 及 Blend for VS2017,使用 C# 的 WPF 框架进行开发。其中逻辑编写使用 VS2017,界面的开发基于 Blend。
    3 详细设计该系统主要分为三个模块:逻辑层、界面层以及数据层。三个层次彼此分明:逻辑层和数据层通过初始化时载入数据库以及退出程序时数据保存至数据库的方式对接;逻辑层和界面层通过事件对接。
    3.1 界面层界面层通过对.xaml 文件的编写进行设计。其中,界面风格采用了 material 风格。所设计的聊天界面如下。其中按键分别为添加好友、添加群聊1、好友查找跳转、动态表情发送、文件传输、语音发送2、消息列表3。上方学号旁的绿灯表示在线,若不在线则为灰色。

    3.2 逻辑层3.2.1 账号登录上线及下线账号登录上线即通过对服务器发送指令,账号密码均正确后即登录成功。与服务器之间的交互使用了 C# 中的 System.Net 以及 System.Net.Socket 库4。下线操作即在程序完全退出前,对服务器发送相关指令,基本操作与登录上线相似。
    与服务器的交互过程为,客户端向服务器发起连接,连接后再发送相关的指令。
    3.2.2 通讯录 (查询好友是否在线)查询好友是否在线即向服务器询问所要查询的好友的在线状态及其 IP 地址。若好友在线,则好友指示灯亮起;若好友不在线或者用户不存在以及输入格式出现错误,会通过 MessageBox 进行提醒。
    3.2.3 P2P 通信P2P 通信分为发送部分和接收部分。下面分别叙述本项目的传输协议、P2P 发送以及 P2P 侦听部分。
    协议部分 本项目的协议共 14 位字节,例:20160114980000。其中,前 10 位为学号,第 11 位和第 12 位为类别,第 13 位和第 14 位为扩展位。
    P2P 发送部分 P2P 发送部分与和服务器之间的交互的发送部分几乎相同,即先与 Peer 进行连接,再进行发送。发送前要对相关的信息转换为与协议相关的格式。
    P2P 侦听部分 P2P 接收部分单独开了两个线程进行接收。在其中一个线程下不断循环进行侦听,另一个线程监测是否有新消息提醒,若有新消息提醒,则使用 Invoke 对界面进行更新,以防止程序出现崩溃。侦听在聊天界面的初始化时开始,在完全退出程序前停止侦听。停止侦听采用软停止,即对自己发送 1 个字节的 0,当侦听到时,退出循环即停止侦听。
    3.2.4 文件传输协议说明 文件传输的应用层协议为,在 14 位通信协议的基础上,先是用一个字节表示文件名字的长度,再用若干字节表示文件名字,剩下的为文件的具体内容。
    文件传输 文件传输功能能够传输的最大文件限制在 1G 左右,这一点仿照微信对文件大小限制在100M。除此之外,接收文件时需要手动保存的路径,若放弃选择路径则表示拒绝接收文件。
    3.2.5 聊天记录查询内存记录 为了在程序中记录消息记录,定义了两个聊天记录类,即全部聊天记录类和单条聊天记录类。具体如下。
    //单条聊天记录纪录public class chatHistorySingle{ public string Speaker; //谁说的话 public int chatid; public string Mess; public string Date = "2019/1/1"; public string ModeFile = "0"; public chatHistorySingle(string speaker, int Chatid, string mess,string modefile,string date) { Speaker = speaker; chatid = Chatid; Mess = mess; ModeFile = modefile; Date = date; }}//整个聊天记录public class chatHistory{ public string Monitor; //主机(谁登录的) public string Contact; //给谁发 public List<chatHistorySingle> History = new List<chatHistorySingle>(); public int preLoadHistoryID = 1; public chatHistory(string monitor, string contact) { Monitor = monitor; Contact = contact; } public void AddHistory(chatHistorySingle chs) { History.Add(chs); }}
    其中单条聊天记录类用于记录说话的人、聊天记录、记录 ID、时间、文件类型;整个聊天记录类用于记录登录 ID、联系人 ID 以及众多的单条聊天记录。
    数据库记录 通过 SQL 接口,将聊天记录信息存入数据库。且在每次界面初始化时,将数据读入,以实现在登出后再次登录时,能够再次查询之前的聊天记录。
    3.2.6 动态表情发送动态表情发送与发送文件类似,将收到的动图自动存在当前文件夹中的 Image 文件夹中。通过使用 WPF 的 MediaElement 控件,以及在后端调整其帧使得其能够循环播放显示。
    3.2.7 好友查询及跳转通过使用 C# 的 Lambda 表达式,查询好友列表中是否存在好友且得到其 index,通过与界面层的结合实现跳转功能,便于在数百个好友中迅速跳转到想要对话的好友。
    3.2.8 新消息提醒为了达到即时通讯的目的,需要在接收到消息时了解到消息的发送者是谁,所以需要进行消息提醒操作。使用 SnackbarMessage 控件进行对新消息的提醒,便于操作。在点击 NewMessage 之后,提醒框会自动消失。
    3.2.9 实时显示提示信息在查找好友和添加好友时,实现对提示信息的实时显示,使用户体验更好。
    实时显示提示信息 (添加好友时好友不在线)

    实时显示提示信息 (添加好友时好友格式输入错误)

    实时显示提示信息 (查询好友时,该好友不为你的好友,无法跳转)

    3.3 数据层数据层主要调用了 SQLite 数据库。存储数据分别为登录用户学号、消息发送者学号、联系人学号、聊天消息记录、日期、消息类型、消息的 ID。

    4 结果分析4.1 登录界面登录界面使用了 Card、Texblock、Button、passwordBox 控件。账号密码默认为 2016011498和 net2018。除此之外,隐藏了窗口边缘并自制了最小化和关闭按键,以保证界面的美观,同时拖动窗口的任意位置可以对窗口进行拖动。

    4.2 聊天主界面向中央服务器发出查询,若无误则登录成功进入聊天主界面。
    添加好友 点击左上第一个按键,进行好友添加。添加过程中会有实时提醒输入格式是否正确、好友是否在线、好友是否存在。若好友在线,则添加入通讯录中。

    发送消息点击左侧的通讯录即跳转至与其聊天的界面。在对话框中发送消息,以及发送动图和文件。相关信息会显示在对话框中,且设定滚动条自动追踪最后一条消息。其中文字消息的发送可以点击 Send 键或者直接回车发送。其中,右上的亮灯表示该好友在线。

    查找好友并跳转 点击左上方第三个按键,则弹出好友查询对话框,在输入学号的过程中同样也会做即时提醒。若为好友则直接跳转与其之间的聊天界面。

    发送/接收动态表情 本项目只支持发送并显示动态表情,即电机 StickerButton,然后选择动图并进行发送。

    发送/接收文件 本项目限制最大文件大小为 1G,这一点模仿微信。发送文件即点击文件发送按键,并选择发送的文件。

    接收到文件时会自动弹出保存窗口,选择保存的路径,并且左小角会提醒文件的来源。

    退出程序 若要退出程序,则点击右上角的按键,点击 Quit 再确认即退出。退出后所有数据将存入数据库。

    5 总结与反思由于这学期其他课程众多的大作业以及期末复习的压力,本次计网大作业我在 2018 年底写了整整四天。四天里一直专心的写计网大作业,且这是我第一次使用 WPF 进行界面设计以及第一次使用 C# 进行网络通信编写。本次大作业极大的锻炼了我的快速学习能力以及泛化能力。
    除了对计网课上所讲的相关内容在实践过后有了更近一步的了解,我逐渐感觉到 WPF 的厉害之处。这四天里,一天半界面设计,两天半逻辑编写,以及对 WPF 各种特性的测试,让我越发越感受到其远远超过 Winform 的地方,在编写的过程中让我回想起当初人智作业用 Winform 编写时各种不规范的编程习惯,比如 Winform 的界面更新建议另开线程 (Invoke),否则可能在界面同时刷新时程序会出现崩溃。但是,我了解的 WPF 可能只是冰山一角,比如本次大作业的编写由于时间关系,并没有使用 WPF 的优点之一的 binding 功能。这可能是一种遗憾。
    不过更遗憾的是,我没有去实现群聊和语音功能。一是期末复习压力,时间不够,二是由于群聊的编写可能要稍微修改一下我之前的代码架构,可能会耗费太多的时间。此外,再进行深入思考之后,我感觉或许微信的群聊功能是基于中央服务器的,即可能不是 P2P 的。如果可以的话,其实可以让我们也对服务器部分的代码进行编写,这样更能锻炼我们的应用能力,进而对相关知识有更进一步的了解。
    参考文献[1] https://docs.microsoft.com/en-us/dotnet/index (网络部分)
    [2] https://github.com/MaterialDesignInXAML/MaterialDesignInXamlToolkit (界面部分)
    1 评论 1 下载 2019-04-19 18:15:48 下载需要14点积分
  • 基于Python实现的手写数字识别

    一、准备
    测试准备:pycharm,windows自带画图功能,python3.5
    安装python3.5
    将解压的文件夹拖入pycharm中,设置解释器路径为:文件解压路径\ML_num\venv\Scripts\python.exe
    若要调用文件,只要点入文件并点击右上角绿色三角或鼠标右键点 run 相应文件

    二、手写数字识别调用UI.py 得如图界面:

    测试可用windows自带画图功能,刷子选用喷枪,粗细第3个,尽量喷清楚,图片后缀为.png图片大小为400*400像素(其他大小也行只要不小于32*32)如下:

    点击选择文件找到测试文件,选择是否使用备份(默认使用)再点击开始识别, 可选择自动识别或手动识别,手动识别截图时鼠标左键按住拖动即可(数字周围尽量有些许空,若识别错误,很大原因是由于截的图左右空格太小、太大),再输入真实值,结果为:文件名为输入的真实值_最大数字.txt。以下结果为文件名为:6_3.txt 如图:

    三、摄像头务必使用笔记本电脑,数字尽量写得大一些,尽量在光线明亮处拍摄,点击摄像头,如图:

    按q键退出,其他键拍摄,之后点击开始识别与第二部分相同。
    四、介绍
    使用的非标准库:opencv,numpy,pillow
    机器学习技术:KNN邻近
    引用:《机器学习实战》源码并对其中部分内容修改,增加部分操作;部分代码参考cnblog与CSDN

    文件介绍


    data_set:备份文件夹
    images:图片文件
    testDigits:测试数据
    testDigits(测试):正确率测试数据
    trainingDigits:训练数据
    vedio_cut.py:摄像头拍摄
    hand_number.py:截取图片并修改大小为32*32, 转为灰度图
    zero_one.py:将灰度图转为01矩阵并存入testDigits文件夹内
    kNN.py:k值邻近算法识别数字并统计结果, 可以选择使用备份数据训练识别或重新开始
    get_next_num.py:得到指定数据文件数量
    check:测试正确率
    UI.py:使用tkinter库构造界面
    新增im:样例图片
    1 评论 3 下载 2019-04-19 10:14:01 下载需要7点积分
  • 基于JSP的MVC框架实现的图书推荐系统展示平台网站

    摘 要推荐系统是目前互联网中最常见的一种智能产品形式。由于网络中信息量的快速增长以及图书出版行业出版量的攀升,人们需要一种办法,来解决信息过载的问题。此外,用户访问网络是为了获取信息,但并不是所有的访问都有很强的目的性,所以对于这些没有明确的目的的访问,就需要智能系统把一些用户可能感兴趣的信息推送给用户。基于这些需求,图书网站的推荐功能就变得非常重要。
    本文首先对图书推荐系统的发展历史做了介绍,然后对开发图书推荐系统所需的项目管理工具(Maven、Git)、数据持久化工具(MyBatis,Spring MVC框架)和Bootstrap前端开发框架进行了简单分析,在此基础上,设计并开发了一套基于Web的图书推荐系统展示平台,主要工作可概括为四个方面。

    对图书推荐系统的结构进行了重新设计。主要是在Spring MVC框架的基础上,将系统分为了三层:Web层、服务与模块层、数据层,并对每层的结构与需要完成的功能做了定义。
    基于MySQL数据库管理系统,设计并建立了图书推荐系统所需的数据库,该数据库的数据共分为三个部分:原始数据、清洗后数据和用户数据。
    从便于操作和使用的角度出发,设计了图书推荐系统的页面,主要包括首页、搜索、展示、登录、注册等页面。
    对整个开发过程以及系统组成的三个主要类:控制器类、模块类与视图类进行了分析。

    经过后期的数据库优化与功能测试,系统与同类网站相比,性能良好。
    关键词:基于Web的图书推荐系统;展示平台;MVC框架;Web系统设计
    AbstractRecommender system is one of the most common intelligent productform in the internet recently. Since the growth of the information in thenetwork as well as the growth of the amount of books, people need to solve theproblem of information overload. Meanwhile, the users access to the network toobtain information, but not all have a strong purpose. So the system must pushsome information that user might be interested in to who accesses the websitewithout strong purpose. That is why it is important for website to providerecommendation information.
    First of all, we described the history of book recommender system.Then, made an introductory of the tool and frameworks used in development:Maven, the project management tool, Git, the version control system, MyBatis,the SQL Mapping Framework for Java, Spring MVC framework and Bootstrap, thefront-end framework. Finally, designed and developed a display platform ofweb-based book recommender system on that. The main work is as follows.

    Made redesign of the structureof the book recommendation system. Divided the system into three levels: theweb level, the service and model level and the data level based on the SpringMVC framework. Made the definition of the structure and functions to be done oneach layer.
    Designed and built the databaseof the book recommender system based on the MySQL database management system.Database is divided into three parts: Original data, washed data and user data.
    From the perspective of the easeof operation and use, designed the pages of the book recommender system. Pageshave been divided into index, search, display and login and register parts.
    Made analyses of the wholeprocess of development and the three main classes: controller, model and view.

    After database optimization and functional testing, system performsgood compared with similar sites.
    Key Words:Web-based BookRecommender System;Display Platform;MVC Framework;Web System Design
    引 言图书推荐功能在图书馆网站,图书销售网站,以及图书分享网站中都占有很重要的地位。它可以根据长尾理论挖掘图书数据,改善用户体验,增加用户的粘性,解决信息过载问题。
    本论文的目的就是要构建一个图书推荐系统的展示平台,将图书信息以及推荐内容,显示在页面之上。并且支持用户的注册登录,用来提供数据,使得推荐算法可以依照此数据来计算出用户喜好,进而向用户推送推荐信息。
    在推荐系统领域,最为成功的该属亚马逊网站,在亚马逊的收入中,有20%~30%的都来自于推荐系统。而在图书馆等非盈利性网站,图书推荐系统也对优化用户的浏览体验以及均衡每本书的借阅率,都有促进作用。
    系统后台是基于Java的Spring MVC框架与Tomcat服务器,前台基于浏览器、HTML与jQuery页面展示技术,数据库基于MySQL开源数据库。利用MVC框架,可以方便地构建出健壮、扩展性强的应用。MySQL数据库,作为最热门的开源数据库,提供了完整的关系数据库支持与成熟的数据存储解决方案。浏览器展示技术,使用户不需要在操作系统中安装独立的应用,并且使得系统的访问不局限于某个操作系统,适用面变得更广。系统提供推荐内容的展示,图书排行的展示,图书搜索,图书详细内容展示,用户登录注册等基本的图书推荐系统所具有的功能。界面使用Bootstrap框架,布局对用户更加友好。
    整个系统通过实现一个轻量的框架,来提供完整的图书推荐功能,后续的功能可以方便地在此基础上扩展。相对于其它的系统,有小巧、灵活的优点,并且本系统全部采用开源软件,因此几乎为零成本。在引用第三方库的过程中,不对其源码进行修改,减少耦合,使得之后第三方库的升级也不会对系统有大的影响。系统在一台机器上部署,并且在同一网段的另一台机器上访问测试后,性能表现良好。
    1 文献综述如今,推荐系统无处不在。在网上逛商城,购物,在音乐网站听歌,在社交网站发表自己的感受以及分享照片,网站服务提供商都会收集用户的访问记录以及用户的喜好,对用户的行为进行分析并且根据用户喜好以及用户群体统计信息,给用户提供相似物品的推荐以及广告推送,进而增加商家的销量,提高网站质量。
    一般认为,推荐系统这个研究领域源于协同过滤算法的提出。从它的诞生到现在20多年中,很多学者和公司对推荐系统的发展做出了重要的贡献。随着信息技术和互联网的发展,人们逐渐从信息匮乏的时代走入了信息过载的时代[1]。而图书出版物等作为一个传统的信息载体,在人类发展的过程中不断增多,一些大的互联网公司与网络图书销售商,比如谷歌、亚马逊等,也一直在为图书的数字化做着贡献。从2004年谷歌宣布他的图书搜索服务之后,到2012年,共有两千万本图书扫描、经由光学字符识别并存储于数字化数据库中作为搜索数据[2]。这些数据为图书推荐提供了丰富的基础。
    本系统采用BS结构,对数据库中的图书内容进行查询、处理,对查询结果进行展示,并且提供用户注册登录以及采集用户访问记录的功能。在服务器端采用Spring MVC框架与对象持久化技术对图书信息进行处理,在客户端使用浏览器技术对数据进行展示与交互,最终完成图书通过WEB平台展示、推荐与相关图书搜索的功能。
    1.1 课题背景1.1.1 图书推荐系统发展背景随着互联网的快速发展,网络中的图书信息量迅速增长,图书种类也日趋繁多,用户通过互联网要获得自己感兴趣的图书文章需要花费越来越多的时间。由此催生了图书推荐系统。图书推荐系统的基本作用是依据用户的访问记录,特定行为,分析用户的喜好,主动向用户推荐可能喜欢的图书与文献给用户,供用户参考[3]。推荐系统满足了用户个性化的需求,节省了用户搜寻信息的时间,获得和用户喜好相关的最热门最新的图书。在许多商务网站,社交网站中,都使用推荐系统来向用户推荐商品。
    在线售卖领域,亚马逊拥有最好的图书推荐系统。在过去的十多年里,亚马逊投入了大量的精力去建立一个包含大众推荐和个人记录的高效个人推荐系统,其最显著特点就是所有的推荐都建立在对顾客以往的浏览记录和购买记录之上[4]。
    亚马逊可以向用户精确的投放畅销书以及经典书推荐信息,每月有评有畅销书排行榜,通过简洁美观的介绍与醒目的导航,引导读者阅读与购买。另外,在畅销书的介绍中,还有本书在相关的分类栏目中的排名,并且有链接指向此分类的畅销排行榜,读者可以方便的查询同类书中最畅销的书籍。在书籍介绍底端还有购买过此书的人还购买了的书籍,购买的比例,以及同类书籍的推荐信息。在首页还有促销书的推荐,用于吸引没有明确目的,而想要以优惠的价格购书的消费者。在经典书方面,亚马逊推出年度畅销图书排行,年度编辑推荐100本畅销书等手段,依据惯性定律,进一步推动图书的销售。其中排序规则还根据用户的评价,人气、出版日期等,迎合不同人的喜好。
    1.1.2 主要技术发展背景Spring框架是一个开源的应用框架,基于Java平台的控制反转容器。第一个版本是Rod Johnson在他2002年十月出版的《ExpertOne-on-One J2EE Design and Development》书中发布的。第一次在Apache 2.0 license下发布是在2003年六月,在2004年三月,发布了第一个里程碑:1.0版本。在2006年,Spring框架1.2.6版本获得了Jolt productivity award和JAX Innovation Award。当前的版本3.2.2是在2013年三月发布的。
    Spring Framework的核心特性可以被用于任何的Java应用。尽管SpringFramework没有强加任何特殊的编程模块,它依旧在Java社区变得流行,并且有取代EnterpriseJavaBean模块的趋势。
    在2001年,Clinton Begin开始一个叫做iBATIS的项目,刚开始,开发的重点是一个加密的软件解决方案。iBATIS项目发布的第一个软件名字叫做Secrets,一个个人的数据加密和登录工具。Secrets完全的用Java实现,并且在开源协议下发布。
    2002年,Clinton开发了一个应用叫做JPetStore来展示Java可以比.NET更高效,JPetStore 1.0影响很大,并且其中用到的数据库层吸引了社区的注意。很快,iBATISDatabase Layer 1.0项目启动了,其中包括两个组件:iBATIS DAO和iBATIS SQL Maps。2004年,iBATIS 2.0发布,它是在保持原有特性上的全新设计。Clinton将iBATIS这个名字和源代码都捐献给了Apache SoftwareFundation,这个项目在ASF一直持续了六年。最终,iBATIS DAO被抛弃了,因为有更好的DAO框架的出现,比如Spring Framework。
    在2010年5月19日,iBATIS 3.0发布了,同时,开发团队决定将项目转移到Google Code。由于iBATIS这个名称已经捐赠给了ASF,所以项目改用了新名称MyBatis。
    jQuery是一个多浏览器的JavaScript库,目的是简化客户端的HTML脚本编程。John Resig在2006年一月发布,目前有一个Dave Methvin领导的开发小组进行开发。在一万个最热门的网站中,他被65%的网站使用。jQuery是目前最流行的JavaScript库。
    MySQL是世界最广泛使用的开源关系数据库管理系统。它作为一个系统服务,允许多用户访问多个数据库。
    MySQL开发项目将代码依照GNUGeneral Public License发布。MySQL被一家瑞典的公司MySQLAB拥有并且提供赞助,这家公司现在隶属于Oracle。
    1.2 开展研究的意义图书推荐系统web平台用于支持图书内容的获取和图书推荐的展示。如果没有此平台,那么图书推荐系统就无法收集用户行为,统计用户喜好,展示推荐结果以及实现推荐系统的原有意义[5]。任何一个推荐系统都需要网络或者客户端平台来展示[6]。而构建这样一个后台服务器与浏览器客户端共同协作展示的平台,对于图书推荐系统具有重要的意义。
    1.3 论文研究内容本课题意图开发一套轻量、扩展性强、功能完善,性能良好的图书推荐系统。系统的最终目标是实现对图书内容进行展示、查询、用户登录注册,还有推荐信息的展示功能。用户通过浏览器与后台系统的Java Servlet、数据库交互,完成展示、查询与用户管理的目的。
    研究的着重点在于通过现有流行的开源系统、框架,来搭建轻量级的目标Web系统,在实现过程中更注重于代码结构与开发方法,所以并不追求功能的繁杂冗余。系统中数据来源于豆瓣,数据的抓取不是本课题研究的内容。为了使数据适用于Web系统,重新构建了表,并对数据进行了二次清洗。由于是展示平台,并不涉及图书推荐算法的实现与搜索功能的完善,所以搜索结果由数据库查询获得。但是在开发过程中为图书推荐与社会化图书搜索预留了接口,可以方便的加入这些功能。
    1.4 论文的组织结构
    第一章,文献综述。先讲了课题的背景,从图书推荐历史和技术发展史两个方面介绍,之后介绍了开展研究的意义,最后明确了研究的工作重点。第二章,框架与工具介绍。对Maven、Git等项目管理工具,Spring MVC、MyBatis、Bootstrap、jQuery等开发框架进行了介绍。第三章,基于Web的图书推荐系统展示平台的设计。对此Web系统所用到的数据库结构、页面原型、模块组成、总体的功能进行了论述。第四章,基于Web的图书推荐系统展示平台的实现。介绍了对数据的二次清洗、数据库的创建、数据的导入、工程的搭建、模块的开发以及测试等。并介绍了测试过程中做的一些性能优化工作。最后,结论。对本系统的优缺点做了一个总结,展望了一下未来的发展方向。
    2 框架与工具介绍2.1 Maven介绍2.1.1 Maven概述Maven是一个主要用于Java项目的自动化构建工具。Maven与Apache Ant工具具有相似的目的,但是他们是基于不同的理念并且以不同的方式来工作。Ant还可以用来构建和管理C#,Ruby,Scala以及其他的语言开发的项目,但是Maven原生并不支持这些。Maven项目由Apache Software Fundation托管,之前是Jakarta Project项目的一个部分[7]。
    Maven使用XML文件来描述需要构建的软件项目、项目的依赖模块和组件、构建顺序、目录结构以及需要的插件。他具有预定义的目标来实现确切的任务,比如编译源码并打包。
    Maven动态地从一个或多个库中下载Java库和Maven插件,比如Maven 2 Central Repository,并且将下载的内容保存到本地的缓存中。这个本地的下载缓存可以被本地项目更新,公共的库也可以被更新。
    Maven使用基于插件的体系来构建,他允许通过标准的输入控制任何的应用。理论上说,这将允许任何人为任何语言来在这个平台上编写插件。实际上,支持和使用除Java外其它语言的插件数量已经微乎其微。目前,只有支持.NET框架以及一个C/C++的原生插件还在被维护。
    2.1.2 Maven概念项目对象模型(POM):一个项目对象模型提供一个项目的所有配置属性。一般配置包括项目名称,owner和它的依赖。也可以通过插件,配置构建过程的各个阶段。比如说,用户可以配置编译插件,让它使用Java 1.5来编译,或者指定在一些单元测试失败以后,依旧打包工程。大的项目应该分成几个模块或者子项目,每个模块拥有自己的POM。用户可以编写一个根POM,通过这个根POM来使用一条命令来编译所有的模块。POM也可以从其它POM文件继承配置信息。所有的POM文件默认继承自Super POM。Super POM提供默认的配置,比如默认源代码目录、默认插件,等等。
    插件:大部分Maven的功能是通过插件来实现的。插件提供一组目标,并且可以通过以下的语法来执行:
    mvn [plugin-name]:[goal-name]
    比如说,一个Java项目可以使用compiler-plugin的complile-goal来编译:
    mvn compiler:compileMaven插件提供构建、测试、源码控制管理、运行web服务、生成Eclipse项目文件等等功能。插件由pom.xml文件中的<plugins>标签区域引入并且配置。
    然而,如果构建、测试、打包一个软件项目需要手动运行下面每条goal,那么,它将会变得非常笨重:
    mvn compiler:compilemvn surefire:testmvn jar:jarMaven的生命周期概念处理这类问题。
    插件是扩展Maven的主要方式。可以通过继承org.apache.maven.plugin.AbstractMojo类来开发Maven插件。
    构建生命周期:构建生命周期是一个用来给出为了达到goal所执行语句顺序的列表。Maven的一个标准生命周期叫做“默认生命周期”,它包括以下语句:

    process-resourcescompileprocess-test-resourcestest-compiletestpackageinstalldeploy
    插件提供的goal可以与不同的生命周期阶段相关联。比如说,默认情况下,“compiler:compile”的goal与编译阶段有关,而goal“surefire:test”与测试阶段有关。考虑下面的命令:
    mvn test
    当以上的命令被执行,Maven运行所有的在test语句之前的goal,直到test语句。在这种情况下,Maven运行“resources:resources”goal,然后是“compiler:compile”,等等,直到最终运行“surefire:test”goal。
    Maven也有标准用来清理项目和生成项目站点的语句。如果清理是默认生命周期的一部分,那么项目将在每次构建的时候被清理,这显然不是想要的。所以,清理有它自己的生命周期。
    标准的生命周期允许新用户能够通过一个简单的语句准确的构建、测试和安装每个Maven项目:
    mvn install依赖:Maven的依赖处理机制围绕一个坐标系统识别单个软件库或者模块。比如说,一个项目需要Hibernate库,仅仅需要在POM文件中声明Hibernate项目,Maven将自动下载依赖和Hibernate自身的依赖,并且把它们存储在本地的仓库中。Maven 2 CentralRepository是默认的用来搜索的库,但是也可以在POM文件中自定义仓库。
    还有一些其他的搜索引擎,比如说Maven Central,可以被用来去不同的开源库和框架中寻找模块。
    在同一个机器上开发的项目之间可以通过本地仓库来互相依赖对方。本地仓库是一个简单的目录结构,充当下载的依赖包的缓存与本地构建的项目的集中存储的地方。Maven命令mvn install构建一个项目,并且把它的二进制文件放到本地仓库中。然后其它的项目可以通过在POM文件中添加项目来引用这个工程。
    2.2 Git介绍2.2.1 Git概述Git是一个分布式的版本控制系统和源代码管理系统。它最初是Linus Torvalds为Linux内核开发而设计开发的,从那以后,Git就被许多其它的项目用来管理代码[8]。每个Git工作目录都是一个完整的仓库,保存着完整的历史和全部的版本跟踪能力,不需要依赖于网络连接或者一个中央服务器。Git是基于GNU GeneralPublic License version 2的开源软件。
    Git设计灵感来自于BitKeeper和Monotone。Git最初被设计成一个底层的版本控制系统引擎,在它上方其他人可以编写前端,比如说Cogito和StGIT。然而,核心的Git项目最终变成一个可以直接使用的完整的版本控制系统。由于受BitKeeper的严重影响,Torvalds故意地试着避免传统的方法,最终实现了一个独一无二的设计。
    2.2.2 Git特性Git的设计来源于Torvalds在开发Linux过程中积累的管理大的分布式项目的经验和对文件系统性能的知识,以及在短期内构建一个可以工作的系统的需求。这些影响导致了以下实现方案:
    1.对非线性开发的强大支持
    Git支持快速的分支和合并操作,并且包含专门的工具用来可视化和导航一个非线性的开发历史。Git的一个核心设想是:由于项目被分给好多个人来完成,修改会更多的被合并,而不是被写入。在Git中,分支是非常轻量的:一个分支仅仅是一次提交的关联,通过其父提交历史,整个分支结构就可以创建出来。
    2.分布式开发
    就像Darcs、BitKeeper、Mercurial、SVK、Bazaar和Monotone,Git给每个开发者一份整个开发历史的本地副本,并且修改记录会从一个这样的本地仓库复制给另一个。这些修改作为附加的开发分支导入进来,并且可以合并到本地的开发分支中。
    3.兼容已存在的系统和协议
    仓库可以通过HTTP、FTP、rsync或者通过建立在纯socket、ssh、或HTTP上的Git协议来推送。Git还有一个CVS服务器模拟,用来让使用CVS客户端和IDE插件的用户来使用Git仓库。Subversion和SVK仓库可以直接使用git-svn。
    4.高效处理大型项目
    Torvalds描述Git处理速度非常快、可扩展,并且Mozilla对其做的测试显示,它比一些版本控制系统快一个数量级,从本地存储的仓库获取比从远程服务器快一百倍。Git甚至在项目历史变得很大的时候速度依旧不会减慢。
    5.历史记录加密认证
    Git历史记录以这样一种方式存储:某次提交的id号取决于之前的完整的开发历史。一旦被发布了,就不可能再去修改旧的版本而不产生记录。这种结构就像一个hash树,但是在每个节点和叶子上,都有附加的数据。
    6.基于工具的设计
    Git是C语言程序,它有一些shell脚本来对其进行封装。尽管大部分的脚本为了提升速度和可移植性,已经用C语言重写过,但是依旧有一些并且很容易的就可以将组件和Git连接起来。
    7.可控的合并机制
    作为工具设计的一部分,Git具有对不能完成的合并有良好的定义模块,并且它还具有多种算法来完成合并。不能自动合并的部分它会最终提示用户,并且由用户来手动编辑。
    8.垃圾回收机制
    放弃等操作会在数据库中留下无用的对象。这是基本上是逐渐增长的历史记录的一个小碎片。Git将会在足够多的松散对象积累够之后,自动的进行垃圾回收。垃圾回收也可以使用git gc –prune命令来调用。
    9.定期对象包装
    Git将每个新创建的对象作为一个独立的文件来保存,尽管每个都会压缩,但是这依旧会占用大量的空间并且降低效率。这个问题是用一种叫做packfile的单一文件存储大量的对象在一个包中来解决的。每个packfile创建一个对应的索引文件,用来表示packfile中的每个对象的偏移地址。新创建的对象依旧独立存储,定期的打包,以保持空间利用率。打包仓库的过程会非常消耗计算机资源。相对于允许对象松散的存在于仓库中,git允许在空闲时间花费较大的开销来将他们打包。Git会定期的自动重新打包对象,不过,也可以使用git gc命令来手动打包。为了保证文件的完整性,每个packfile和它的索引内部都有SHA-1校验码,并且packfile的文件名包含一个SHA-1校验码。输入git fsck命令即可验证完整性。
    2.3 MVC模式与SpringFramework框架2.3.1 MVC模式MVC模式是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。
    MVC模式的目的是实现一种动态的程序设计,使后续对程序的修改和扩展简化,并且使程序某一部分的重复利用成为可能。除此之外,此模式通过对复杂度的简化,使程序结构更加直观[9]。软件系统通过对自身基本部分分离的同时也赋予了各个基本部分应有的功能。组件合作关系如图2.1所示。

    模型(Model):“数据模型”用于封装与应用程序的业务逻辑和数据处理相关的方法。“模型”有对数据直接访问的权力,例如对数据库的访问。“模型”不依赖于“视图”和“控制器”,也就是说,模型不关心它会被如何显示或是如何被操作。但是模型中的数据的变化一般会通过一种刷新机制被公布。为了实现这种机制,那些用于监视此模型的视图必须事先在此模型上注册,从而,视图可以了解数据模型上发生的变化。
    视图(View):视图层能够实现数据有目的的显示(理论上,这不是必需的)。在视图中一般没有程序上的逻辑。为了实现视图上的刷新功能,视图需要访问它监视的数据模型(Model),因此应该事先在被它监视的数据那里注册。
    控制器(Controller):控制器起到不同层面间的组织作用,用于控制应用程序的流程。它处理事件并做出响应。“事件”包括用户的行为和数据模型上的改变。
    2.3.2 Spring MVC框架Spring Framework提供它自己的MVC web应用程序框架,这并不是开发之初计划的。由于当时流行的Jakarta Struts web框架的设计很糟糕,并且其他框架也有许多不足之处,Spring开发者决定去编写他们自己的web框架。最重要的一点,他们觉得在表现与请求处理层之间没有完整的分离,并且请求处理层和模块层也没有完全分离。
    就像Struts,Spring MVC也是一个基于请求的框架。每个接口的目的是让Spring MVC用户能够清晰、容易的实现MVC框架的应用。所有的接口都是与Servlet API紧耦合的[10]。这个对Servlet API的紧耦合被某些开发者看做是Spring的失败,它不能提供一个高层次的对基于web的应用的抽象层。然而,这样耦合确保了Servlet API能够给开发者提供一个高度抽象的框架来使得开发变得容易[11]。
    Spring MVC框架围绕一个核心servlet来给控制器分发请求并且提供一些其它的功能来方便web应用的开发,Spring的DispatcherServlet就是用来干这些的。它将Spring IoC容器完全集成在一起,这样你就可以使用所有其它的Spring特性。
    Spring Web MVC DispatcherServlet的请求处理工作流如图2.2所示。DispatcherServlet是一种“Front Controller”设计模式的表现(这种设计模式在许多其它的web框架中也很常见)。

    DispatcherServlet是一个真实存在的Servlet(它继承自HttpServlet基类),并且在web应用的web.xml文件中声明。那些你想要DispatcherServlet处理的请求必须在同一个web.xml文件中使用URL映射来映射到这个Servlet。这是标准的J2EE servlet配置。图2.3是对DispatcherServlet的声明和映射。

    在上面的配置示例中,所有的以.form结尾的请求都会被”example” DispatcherServlet来处理。这仅仅是配置Spring Web MVC的第一步。
    在Web MVC框架中,每个DispatcherServlet都有自己的WebApplicationContext,这个WebApplicationContext继承了根WebApplicationContext定义的所有的beans。图2.4为Spring Web MVC中的上下文层次关系。

    框架将会在初始化DispatcherServlet的时候,在你web应用的WEB-INF目录中寻找一个名字叫[servlet-name]-servlet.xml的文件,然后创建在那个文件里定义的beans。
    WebApplicationContext是一个对单纯的ApplicationContext的扩展,对web应用增加了一些额外的特性。它区别于普通的ApplicationContext的地方是,它可以解析主题,并且它知道自己与哪个servlet相关联。
    Spring DispatcherServlet有一些特定的beans来实现处理请求和获取正确的视图返回给用户。这些beans被包括在Spring框架中并且能在WebApplicationContext中配置,就像其它的beans一样的配置方式。
    一些比较重要的接口定义以及他们的功能列举如下:

    HandlerMapping:选择对象来处理来自内部或者外部的请求。HandlerAdapter:对请求进行处理。Controller:介于Model和View之间来管理请求和返回合适的响应。他就像一个门,来对传入的请求进行处理。它从Model获取数据,传递给View并将View展现出来。View:用于向客户端返回数据。一些请求可能会直接请求到视图,而不经过模块部分,其它的请求三者都要经过。ViewResolver:根据逻辑名称为视图选择一个View。LocaleResolver:获取并且保存用户的位置信息。MultipartResolver:提供对文件上传的请求的封装。
    以上的每个接口对整个框架都有重要的作用。这些接口提供的抽象层用处非常大,所有继承自这些接口的类都按照同样的特性建立在Servlet API之上。开发者可以自由的去编写他们的实现类。Spring MVC使用Java的java.util.Map接口作为数据Model,数据Model与View共同生成最终的返回页面[12]。
    2.4 MyBatis介绍MyBatis是一个在Java和.NET平台的持久层框架,它将SQL声明与存储过程使用XML描述[13]。MyBatis是一个自由软件,基于Apache License 2.0发布,它的原名为iBATIS[14]。
    MyBatis允许你可以使用所有的数据库功能,比如存储过程、视图、任何复杂的查询,以及数据库厂商所专有的功能。它经常被用来处理遗留数据库、规范化的数据库或者是需要完全的控制SQL执行的时候。
    它相对于JDBC简化了编码,SQL语句仅仅需要一行代码便可以执行。这样便节约了时间,避免了一些常见的失误,比如忘记关闭数据库连接、编写了一个错误的数据映射、超过了结果集的数量限制或者是查询结果不仅仅是一条。
    MyBatis提供了一种映射引擎,将SQL结果映射到对象树中。SQL语句可以通过使用内置的有XML类似语法的语言来动态的建立,或者通过使用Velocity集成插件来使用Apache Velocity建立。MyBatis集成在Spring Framework和Google Guice中,这个特性可以允许自由的构建商业应用并且不需要调用任何的MyBatis接口。
    MyBatis支持声明式的数据缓存。MyBatis集成了:OSCache,EHcache,Hazelcast和Memcached,并且支持集成自定义的缓存工具。
    MyBatis提供一套自动生成代码的工具:MyBatis Generator。MyBatis Generator将会检查一个数据库表(或者许多表),然后生成MyBatis项目用来支持增删改查操作。
    2.5 Bootstrap介绍2.5.1 Bootstrap概述Bootstrap是一个用来创建网站和web应用的工具集。它包含许多基于HTML和CSS的设计模板:排版、表单、按钮、图表、导航、其他的界面组件以及可选的JavaScript扩展。它是GitHub上最流行的项目,NASA和MSNBC网站都在使用此框架。
    Bootstrap有相对不完整的对HTML 5和CSS 3的支持,但是它兼容所有的主流浏览器。从2.0版本开始,它开始支持响应式设计。这意味着网站页面的视图设计根据设备的特点来动态的调整。
    Bootstrap是Twitter发布在GitHub上的开源软件。开发者可以参与项目并且对平台贡献自己的力量。
    2.5.2 结构和功能Bootstrap是模块化的,包含一系列的LESS样式,用来实现工具的不同部件。一个叫做bootstrap.less的样式表包含了组件的样式。开发者可以选择自己需要的组件来创建自己的bootstrap.less文件。使用LESS样式表语言允许用户使用变量、函数、运算符、嵌套选择器,以及所谓的mixins。
    自从2.0版本开始,在Bootstrap网站上添加了自定义的配置选项。开发者可以在一个表单上选择想要的组件和调整参数,然后生成配置好的预编译CSS样式表。
    1.栅格系统和响应式设计
    Bootstrap默认支持940像素宽度的栅格布局。另外,开发者可以使用一个可变宽度的布局。工具集中有四个参数来使用不同的分辨率和设备类型:手机、竖屏、横屏格式、平板电脑、有低分辨率和高分辨率的电脑。页面会自动根据这些分辨率来调整列的宽度。
    2.CSS样式表
    Bootstrap提供一系列的样式表来为主要的HTML组件提供基本的样式定义。这样可以对不同的浏览器和不同宽度的设备提供一套统一的文字格式、表格、表单项目布局。
    3.可重用的组件
    除了常用的HTML元素,Bootstrap包含一些其它的样式元素:带有高级特性的按钮(比如按钮组、带有下拉选择的按钮、导航列表、横向、纵向的标签、面包屑导航、分页等等)、标签、高级的排版功能、缩略图、带格式的警告信息以及进度条。
    4.JavaScript插件
    JavaScript组件基于jQuery JavaScript库。它提供附加的用户界面元素,比如对话框、工具提示以及轮播。他们也扩展了一些已经存在的页面元素,包括:一个输入框自动完成的功能。
    2.6 jQuery介绍jQuery是一套跨浏览器的JavaScript库,简化HTML与JavaScript之间的操作。目前是由 Dave Methvin 领导的开发团队进行开发。全球前10000个访问最高的网站中,有59%使用了jQuery,是目前最受欢迎的JavaScript库[15]。
    jQuery 是开源软件,使用MIT许可证授权。jQuery的语法设计使得许多操作变得容易,如操作文档对象、选择DOM元素、创建动画效果、处理事件、以及开发Ajax程序。jQuery 也提供了给开发人员在其上创建插件的能力。这使开发人员可以对底层交互与动画、高级效果和高级主题化的组件进行抽象化。模块化的方式使 jQuery 函数库能够创建功能强大的动态网页以及网络应用程序[16]。
    jQuery有下列特色:

    跨浏览器的DOM元素选择DOM巡访与更改:支持CSS 1-3事件(Events)CSS操纵特效和动画(移动显示位置、淡入、淡出)Ajax延伸性(Extensibility)工具:例如浏览器版本(已取消自带,改由jQuery Migrate plugin外挂提供)和each函数。JavaScript插件轻量级
    jQuery 1.8.0版时(自带Sizzle.js):



    文件
    行数(行)
    大小(KB)




    jquery-1.8.0.min.js
    2
    91


    jquery-1.8.0.js
    9228
    254




    DHTML DOM选择器与链式语法
    经由jQuery的DHTML DOM选择器,可以更容易的操作在复杂的树状HTML中的任何DHTML DOM对象,并可用链式语法对同一对象的不同属性进行操作。
    例如:
    $("p.surprise").addClass("ohmy").show("slow");
    相当于:查找HTML的<p>标签,且其class为”surprise”的DHTML DOM对象,将其Class属性多加上一个”ohmy”(通常是配CSS的定义做显示时的配色修改),打开显示。


    CSS 1-3选择器:支持CSS选择器选定DOM对象。跨浏览器:跨浏览器的AJAX解决方式,支持InternetExplorer 6.0+、Opera 9.0+、Firefox 2+、Safari 2.0+、Google Chrome 1.0+简单:较其它JavaScript库更易于入门。
    2.7 小结本章简要介绍了开发过程中用到的几个比较重要的工具与框架。在项目创建和最后的部署过程中,Maven工具为项目的依赖的解决和编译打包提供了很好的解决方案。而在项目开发过程中,Git工具为代码管理、版本控制以及进度统计,都提供了很大的帮助[17]。
    本章还介绍了四种框架:Spring MVC、MyBatis、Bootstrap和jQuery。前两者是后台框架,提供了一系列的类与接口,用于实现系统功能、简化与美化代码结构设计,并提供了一些工具来方便开发者实现相应的功能[18]。后两者为前台框架,简化了前台的开发过程,提供封装好的方法来让前台交互更加方便,界面更加友好。这四种框架为开发的核心框架,它们极大的提高了生产率,并且使最终的产品不仅内部结构,而且外部表现都健壮、美观。
    3 基于Web的图书推荐系统展示平台设计3.1 系统总体设计3.1.1 总体功能描述本课题是要开发一套轻量级、功能完善的基于Web的图书推荐系统展示平台。利用此平台,对图书信息进行展示与推荐,对用户提供注册与登录功能。由于原始数据为从豆瓣依照网站网页结构抓取得来,并不适用于系统直接读取,于是需要将原始数据表格进行重新设计,遍历分离所需数据存入新设计的表中,使其可以更便捷的查询与处理。开发过程需要考虑Spring MVC框架,将功能按照模块、视图、控制器三部分分离,模块与视图适度的模块化使其可以较好的重用。beans使用注解来注入,这样可以提高小的个人项目的开发效率。在开发开始,配置Maven来解决需要的依赖包,创建Git仓库,来控制版本。
    3.1.2 系统模块组成首先将系统在Spring MVC的基础上分为了三层,分别为:Web层,服务及模块层,数据层,而Web层中,分为Controller与View模块,View为Controller服务,按照预定义的格式来展示Controller的数据。Controller将数据访问与一些公共的逻辑算法交给Model来处理,Model将处理结果交还给Controller。而Model类就像前面Controller给他的任务,负责与界面无关的逻辑计算与数据库的访问、以及其它格式数据的获取。结构如图3.1所示。

    Web层负责处理用户的请求,其中Controller会接收DispatcherServlet分发过来的请求,Controller调用服务与模块层中的模块,进过逻辑计算,生成最终的数据,将数据通过键值对的方式,将视图通过字符串方式传递给DispatcherServlet,DispatcherServlet再读取对应的View,使用View作为模板,生成最终的页面,返回给用户。
    服务与模块层负责逻辑计算与数据获取。其中MyBatis Mapping模块为通过MyBatis Generator自动生成的DAO类,以及自定义的DAO类,用于连接MySQL数据库并且执行增删改查操作。而逻辑计算与数据获取模块包含了公共方法类,某些特殊的算法计算,以及对配置文件的查询取值。
    最底下的数据层,包括数据库系统与文件系统,是用来存储数据与配置的层。其中数据库采用MySQL数据库,配置文件使用Java自带的.properties文件。
    三层之间是互相独立的,只有最近的两层之间可以访问:服务与模块层只可以访问数据层,而Web层只可以调用服务与模块层。其中,服务与模块层中的逻辑计算与数据获取模块,每个模块之间是相互独立的,模块与模块之间不可以互相访问,这样用来降低耦合性,每个模块完成一个完整的任务。由于Web请求的模式决定Web应用只能是被动接收请求,并且Web应用没有涉及费时的网络获取,在代码中没有回调函数,所以层与层之间的调用为单向的,即模块层只可调用数据层,让数据层来执行操作,然后返回数据给模块,Web层调用模块层,将一些逻辑计算与数据获取的过程交给模块来完成,结果返回给Web层,而不可能模块层主动的调用Web层,来对其中的值进行更改,而后返回给用户一个新的页面。在Web层中,View模块只负责对数据进行格式化,生成最终用户页面,因此,它只接收Controller模块的值,而与程序的其它层次模块之间不可以通信。这样,在编写Controller模块时,并不需要了解数据库的组织结构以及配置文件的具体文件名等,只需要知道调用哪个模块,将需要的参数传入模块,模块返回的值就是所需要的数据。而在编写View模块时,也不需要了解其它各个层次都有什么作用,只需要分析页面哪个部分的数据是动态获取的,然后将此部分数据安排给Controller,让Controller传过来就可以了,这样将不同模块之间的耦合性降到了最低。而Controller就像一个乐队的指挥,按照需求调用各个模块,让系统的各个部分井然有序的工作。
    3.2 模块详细设计3.2.1 数据库设计1.原始数据库
    原始数据库存储从豆瓣抓取下来的数据,分为三张表:book_author_info,book_online_info,book_publishing_info。
    book_author_info表存储作者信息,分为图书编号、作者姓名、作者简介与介绍四列。作者简介内容包括作者的生平以及与此书和作者都有关的一些事件介绍,介绍列与前者相同。作者姓名列存储了各个作者加国籍的信息,需要进行清洗。
    book_online_info表存储图书的一些社会化信息,分为图书编号、标签、访问次数、5星评价数量、4星评价数量、3星评价数量、2星评价数量、1星评价数量、想读用户数量、在读用户数量、已读用户数量、还想阅读的书 这十二列。其中标签列将所有的标签,带上打标签的次数都放到了同一个字符串中,需要对其清洗。
    book_publishing_info表存储了图书的出版信息,分为图书编号、ISBN号、书名、作者姓名、图书描述、图书目录、包装类型、定价、总页数、出版社名称、出版时间以及图书封面缩略图名称这十二列。作者姓名与book_author_info表的作者姓名列重复,而定价、总页数等数据使用varchar来存储,出版社也没有构建序号,不便于检索,所以这些内容都需要清洗整理。
    2.清洗后数据库
    清洗后的数据库是可以直接拿来网站使用的,从原始数据库中数据清洗整合出来的数据,分为九张表:book_info,tag_info,book_tag_relation,author_info,book_author_relation,nationality_info,publisher_info,binding_type,book_relation,内容如表3.1-3.9所示。
    book_info表内容



    表名称
    书籍详细信息表




    列名称
    图书编号、ISBN号、书名、书籍描述、书籍目录、包装类型序号、定价、页数、出版社序号、出版时间、封面图片名称、评论数量、5星评价数量、4星评价数量、3星评价数量、2星评价数量、1星评价数量、想读人数、正在读人数、已读人数


    描述
    其中书名数据类型由原来的text转换为varchar,便于构建索引,提高查询速度。将原先耦合在一起的包装、出版社数据独立出来,只在表中引入其序号,便于按不同类型来查询。将定价和页数的数据类型由varchar改为int,方便程序读取,提高查询速度。定价数据类型并没有采用double,因为它是固定的两位小数,精确到分即可,并没有必要采用比较大的double类型。



    tag_info表内容



    表名称
    标签内容表




    列名称
    标签序号、标签名称、标签数量以及标签等级


    描述
    标签序号为自动生成的这个标签唯一的序号,用来识别标签。标签名称是清洗出的单个的标签,使用varchar存储,便于生成索引,提高搜索速度,标签数量为在所有图书的标签中,标签出现的次数。



    book_tag_relation表内容



    表名称
    图书与标签关系表




    列名称
    图书编号、标签序号以及使用次数


    描述
    图书编号为book_info表中的主键,标签序号为tag_info表中的主键,使用次数为给这本书打这个标签的用户的数量。



    author_info表内容



    表名称
    作者信息表




    列名称
    作者编号、作者姓名、作者简介内容、作者的国籍编号


    描述
    作者编号为自动生成,是表中的主键,作者姓名是将原始数据中的作者姓名清洗出单个的作者后,放入表中的。改用varchar存储,便于生成索引。国籍编号为nationality_info的主键。



    book_author_relation表内容



    表名称
    书籍与作者关联表




    列名称
    图书编号、作者编号


    描述
    图书编号对应book_info表主键,作者编号对应author_info表主键。



    nationality_info表内容



    表名称
    国籍信息表




    列名称
    国籍编号、国籍名称


    描述
    国籍编号为自动生成的主键,国籍名称为从作者信息中解析出来的作者的国籍。



    publisher_info表内容



    表名称
    出版社信息表




    列名称
    出版社的序号、出版社名称


    描述
    出版社序号为自动生成的主键,出版社名称为从原始数据中清洗出来,转换为varchar格式的数据,方便生成索引以提高查询速度。



    binding_type表内容



    表名称
    包装类型信息表




    列名称
    包装类型序号、包装类型名称


    描述
    包装类型序号为自动生成的主键,而包装类型名称为从原始数据中清洗出来,转换为varchar类型的数据。



    book_relation表内容



    表名称
    相关借阅信息表




    列名称
    书籍编号、相关借阅书籍编号、相关借阅书籍名称


    描述
    书籍编号与相关书籍借阅编号都来自于book_info的主键。而相关借阅书籍名称为书籍的字符串名称,因为有部分相关借阅书籍可能不在数据库中,所以有必要将其字符串名称存于表中,可以拿去搜索或者进行其他一些操作。



    3.用户数据
    用户数据是用户的登陆注册以及访问记录的表。其中的数据为Web应用自己生成,而不是预先装入系统之中的,分为:user_info,user_visit_history,user_search_history,内容如表3.10-3.12所示。
    user_info表内容



    表名称
    用户信息表




    列名称
    用户编号、用户名、密码、用户邮箱、用户状态、用户口令


    描述
    其中用户编号为自动生成的主键,用户名为用户注册时所起的名称,与密码一起用于登录。密码使用SHA算法加密,用户邮箱为注册时填写,用于激活账户与找回密码之用。用户状态分三种:0:已注册,未激活,1:注册并激活,2:密码已丢失,需要重新找回。用户口令取自第一次登录的session id,用于保持用户持久登录状态。



    user_visit_history表内容



    表名称
    用户访问记录表




    列名称
    用户编号、用户访问图书编号、访问时间、访问来源、访问来源图书编号


    描述
    用户编号对应user_info表中的主键,用户访问图书编号为用户打开的图书介绍页面中图书的编号,访问时间为打开图书介绍页面的时间,访问来源分三类:0:首页,1:搜索结果,2:其它图书推荐,其中访问来源为2的,需要填写访问来源图书编号。



    user_search_history表内容



    表名称
    用户搜索记录表




    列名称
    用户编号、用户搜索关键词、搜索时间


    描述
    用户编号对应user_info表中的主键,搜索关键词为用户在搜索框中输入的关键词,搜索时间为发生搜索事件的时间。



    3.2.2 页面原型设计使用快速原型工具Axure RP Pro,根据功能设计了需要实现的页面的原型:首页、搜索结果页、图书展示页、注册页面、登录页面。生成的首页原型效果如图3.2所示。

    首页顶部包含一个logo,链接到本页;搜索框,提供对图书的搜索入口。以及登陆和注册链接,用于用户的注册与登录管理。正文部分分为四个大模块,名称分别为:相关图书推荐、分类热门图书、图书分类以及图书排行。图书推荐模块列出单本书的缩略图与简要介绍,而分类热门模块列出了几个大的图书分类,以及几本此分类下的比较热门的图书。图书分类模块列出了所有的图书分类,用户可以直接点入查看此分类下的所有图书。图书排行列出前九本最热门的图书。页脚部分注明页面版权信息,以及创建年份。
    搜索页面页眉页脚与首页相同,正文部分为一个搜索结果列表,显示搜索结果中的15条记录,每条记录显示图书缩略图、书名、作者、出版社、出版日期、价格以及评分。正文底部是分页,列出了结果的页数,可通过点击来查看后面的搜索结果。右侧列出了热门图书列表,用于向用户推荐。
    图书展示页面页眉页脚与首页也相同,正文部分分四大块,第一块为图书基本信息,包括标题、缩略图以及一些图书在版编目信息:作者、出版社、出版时间、页数、定价、装帧、ISBN号。同时还显示评分以及各个星评分数量。第二块为内容简介,是对书内容的简要介绍以及图书目录。第三块为作者简介,是对图书作者以及译者的简要介绍。第四块为相关推荐,展示阅读此书的人还阅读了的书籍。由于原始数据并不包含用户评论,因此用户评论的内容并没有加入展示。
    注册页面和登录页面较为简单,用户填写用户名、邮箱、密码、确认密码,之后点击注册即可。登录时,用户输入用户名、密码,然后登录系统,会跳转到首页,首页右侧顶端登录注册不再显示,改为用户名与退出。
    3.3 小结本章介绍了本课题所研究的基于Web的图书推荐系统展示平台的系统总体设计与详细设计。总体设计主要讲述了系统设计的层次结构,并且规定了系统各层各模块之间的组织结构与通信规则。详细设计部分对数据库进行了设计,列出了各个表的结构与描述。并且使用页面原型工具构建了简单的页面,来作为之后页面开发与功能模块划分的依据。总体合计与详细设计在系统开发之前,对之后系统的结构与功能都起到非常重要的影响,结构的好坏直接影响到系统的性能。此章是下一步实现系统的必要步骤和重要依据。
    4 基于Web的图书推荐系统展示平台实现4.1 环境的搭建4.1.1 数据库的建立与数据的导入PowerDesigner创建一个物理数据模型,配置好数据库类型,添加表结构,将在详细设计中所设计的数据库信息与列信息、自增列属性输入到表结构中,生成如图4.1所示的物理结构设计图。

    使用其自动生成工具,生成用于创建数据库的SQL脚本。之后,使用MySQL工具导入到数据库中。同时,将原始数据也使用MySQL工具导入到数据库中。
    4.1.2 工程建立使用Eclipse创建一个Spring MVC项目,系统会自动生成一套目录结构,如图4.2所示。


    src/main/java目录用来存放项目的主体部分的源代码,所有的Controller模块、Model模块,以及DAO的Java类,都放在这里,在发布的时候,这里的源码会在编译成class文件后,放入WEB-INF目录下的classes目录。src/main/resources目录用来存放项目的配置文件以及MyBatis的Mapping文件。在部署过程中,也会被放入WEB-INF目录下的classes目录中。src/test/java目录用来存放项目的测试类,src/test/resource目录用来存放项目的测试配置文件,这些都会在部署时,放入WEB-INF目录下的test-classes目录中。JRE System Library包含系统中安装的JRE的库,在项目创建时,可以选择版本。Maven Dependencies包含了在Maven的POM配置文件中所配置的依赖包,这些包在工程创建时,由Maven从Maven仓库中下载到本地缓存,并且链接到工程中。src目录分main和test,而main/webapp目录下有resources与WEB-INF目录,其中resources目录是在servlet-context.xml中配置的,用于存放页面中的资源的目录,分为css、img、js三个目录,WEB-INF目录分为classes、spring与views目录以及web.xml文件,web.xml文件为Java Servlet的标准配置文件,Spring就在这里配置进去。classes为应用发布时,.class文件的目录,spring目录为spring配置文件存放的目录,用于修改配置,添加beans用于注入等。views目录为视图模块存放的地方,使用jsp作为视图文件。target目录为自动编译的目录,目录中有所有类、测试类的编译结果.class文件,以及Maven的配置文件pom.xml。pom.xml文件为Maven的配置文件,它包含了项目的基本配置、依赖包以及插件配置。项目创建时,默认只有Spring MVC的基本配置。
    4.1.3 版本控制为了开发的方便,防止不必要的损失以及对进度的掌控,项目一开始便进行了版本控制,在GitHub网站创建了一个私有仓库,项目的根目录下初始化了Git本地仓库,配置好全局变量:用户名、邮箱以及SSH key,项目目录下加入远程的GitHub仓库,之后便可以添加文件、提交更改并且推送到远程服务器上。
    修改提交的节点选择在每次一个功能完成之后或者是对之前的文件需要进行比较大的修改之前。功能完成之后提交,可以确保自己能够定期的跟踪到完整的可运行的项目,不至于两次提交之间项目变化过大,如果想要修改,那就不容易找到一个合适的检出时间点。进行比较大的修改之前提交,虽然可能一个功能并没有完成,但是修改过程中可能要参考到之前的部分功能,所以仍有必要将其保存提交。在修改完之后,最终确定了用新的方案,再次提交。
    4.1.4 MyBatis配置MyBatis官方提供了一个自动生成代码的工具:MyBatis Generator(MBG)。它会检测数据库的所有表,并且生成可以用来访问数据库表的代码。这样可以减轻最初访问数据库所需编写代码的工作量。MBG提供了所有常用的数据库操作:增删改查。对于单表的操作,只需要使用这一套生成的类即可,生成结果如图4.3所示。

    在生成的文件中,dao为mapper接口,存放TableNameMapper.java文件,用来在配置中注入或者使用SqlSession来获取实例,对数据库表执行增删改查操作。model为表结构的类TableName.java和查询条件构造类TableNameExample.java,TableName.java用于存储对应表的响应条目值,用来实现update和insert操作,以及查询出结果的存储。TableNameExample.java用来构造where语句,用于执行select操作。在数据库中有多于两个列的类型为TEXT或者BLOB,那么除了生成TableName.java,还会生成一种TableNameWithBLOBs.java的文件,其中TableName.java负责存储一般的数据类型,TableNameWithBLOBs.java文件负责存储TableName.java中所有数据之外,还包括了TEXT和BLOB类型的数据。mapper目录存储xml配置文件,用于支持在TableNameMapper.java文件中定义的操作。
    将代码和配置文件加入到工程中之后,会出现编译错误,显示一些引用的类不存在于工程中。查找原因,发现是因为项目中没有加入数据库与MyBatis的依赖,修改pom.xml配置文件,加入spring-jdbc、mybatis、mybatis-spring与mysql-connector-java依赖配置。
    在src/main/resources加入mybatis-config.xml,用来为MyBatis提供连接数据库的配置与Mapper类集合的配置。创建Mysql.properties文件,将配置写入文件中以便复用。
    4.2 数据的清洗由于原始数据并不能直接拿来使用,因此需要按照之前设计的数据库,将三个表中的原始数据清洗后,存入新设计的表中,程序流程如图4.5所示。
    程序会先定义起始和终止图书编号,之后,从第一个图书编号开始,通过主键,查询到图书的数据,将需要的值取出,比如作者信息。作者信息包括了作者国籍、作者姓名以及其它作者姓名,格式如图4.4所示。

    开始打算通过一个完整的正则表达式来对作者信息进行解析,但是由于Java的正则表达式并不能够分辨出中文标点与中文文字,因此,先对字符串做初步的清理:将“编著”、“译者”、“主编”替换为英文斜杠“/”来区分不同的作者,将中英文逗号、中英文括号等内容都替换为英文分号“;”,用于区分国籍与作者。之后,使用将字符串按照斜杠来分割成一个字符串数组,数组中每个字符串代表一个可能带有国家信息的作者名称,使用正则表达式:”^(;([\u4e00-\u9fa5]+);)?([\w\u4e00-\u9fa5\.•]+)”来匹配,取出可能以分号加中文字符开头的,作为国籍,以英文字符或者中文字符组成的连续的词作为作者姓名。
    之后,拿国籍信息去国籍信息表中查询,没有此项,则作为一个新的条目插入,有则不做处理。在有的数据清洗过程中,比如标签,它有一个统计数据,那么如果表中有这个标签,会将统计数据增加一个。作者姓名与国籍类似,采用相同的方法来存储。之后便查询下一条图书记录。

    为了加快读取速度,每次的读取并不只是一条,而是多条图书数据一起读取出来,这样会一次将较多的数据调入内存,降低磁盘IO操作,加快速度。可是由于每本书有三到五个作者,每个作者和国籍都会在解析出来之后变为一个独立的需要插入到数据库中或者去数据库中查询的条目,随着数据条数的增多,同时提交的事务数量会加倍增长,MySQL系统就出现了session数量不足的错误。于是,将每次取出的条数减少,并且在每次操作完成一组数据后,提交并关闭数据库,在需要操作前,再打开数据库。这样就能够即时的关闭用完的session,不会出现由于大量已结束的事务占用session而报错的问题。
    同样为了加快数据清洗速度,使用两台电脑,一台运行数据库系统,另一台运行Java程序,MySQL系统打开网络用户的访问权限和所在系统的防火墙3306端口,另一台连接并处理数据。由于数据库操作占比较大的时间,所以瓶颈依旧在运行数据库的系统中,不过相对与在同一个系统中,CPU占用和内存占用有一定程度的下降。
    4.3 系统开发按照总体设计阶段的分层,将系统分为三个包:cn.edu.ustb.controller、cn.edu.ustb.dm、cn.edu.ustb.model。如图4.6所示。

    4.3.1 控制器类controller包为系统结构中的controller模块,根据功能,划分为了五个类:BookInfoController.java负责图书详细信息的展示,IndexController.java负责首页的视图内容获取展示,LoginController.java负责登录信息的处理,RegisterListController.java负责对注册信息的处理,ResultListController.java负责处理查询。
    controller类将SqlSessionFactory使用注解的方式注入类中,并且使用注解来实现Controller类与请求映射。使用Log4j工具来输出日志。借鉴Objective-C的方式,使用setter/getter方法来获取变量,以便延迟加载以及提高利用率。代码结构如图4.7所示。

    在最开始,Mapper都是使用注解来注入进来的,但是发现SqlSession的开启与关闭不受到控制,完全靠系统来自动完成,那么在并发数量过大之后,大量线程占用session,很容易出现session数量过多的问题,其他人访问不了网站。将Mapper的获取方法改为了使用SqlSession的getMapper方法来获取,这样就可以完全控制session开启时间、结束时间。在每次访问页面的时候,包括浏览器会话没有关闭时刷新,都会重新开启一个新的SqlSession,获取新的Mapper实例,然后执行数据库操作,最后,关闭数据库连接。这样,能够即时的回收过期的SqlSession,防止大量无用的session占用数据库资源。
    在使用getter/setter方法时,刚开始将SqlSession的获取放到了getter中,这样会首先检测有没有实例,没有实例再创建,意图是为了延迟加载,在用到的地方才初始化它,并且防止每次使用都创建新实例。可是在实际中却发现,如果用户刷新页面,或者点击分页按钮,系统会抛出错误,说数据库已关闭,无法执行查询操作。原来每次访问,在浏览器没有结束会话时,Web容器会将Controller类的实例保存在内存中,而每次请求只会执行RequestMapping所指定的函数。于是修改SqlSession的获取方式,在浏览器每次发起请求时,通过SqlSessionFactory类的openSession函数来获取一个SqlSession实例。
    Mapper的获取也受到了影响,因为每次访问都会创建一个新的SqlSession实例,那么Mapper如果不为null的话,就不会重新创建Mapper实例,这样,Mapper的SqlSession将是已经关闭的session,它不能够执行任何数据库操作。因此,在每次访问时,会将所有的Mapper都重置为null,以使其重新初始化。
    4.3.2 模块类模块类包含一些页面需要的数据结构,对数据的加工函数以及分页功能实现。其中,BookClassifyItemModel.java类为单纯的书籍按照分类来显示信息的模块,其中,借鉴了MyBatis的Example类的方法,添加了一个内部类,在父类中编写了创建内部类的函数,用于创建图书列表。
    BookListItemModel.java类为图书的基本信息展示类,用在了图书推荐、图书排行、查询结果展示以及图书详细信息中。在类中提供了计算得分的函数,以及格式化日期的函数,用于在页面中显示。
    SearchResultPaginationModel.java类为查询结果分页模块,用于支持查询的分页显示以及分页功能。由于数据量巨大,为了提高查询效率,分页查询并没有采用MyBatis的分页查询方法,MyBatis会在第一次查询时,将所有的符合条件的结果读入内存中,之后再根据分页条件来显示,这样,虽然会在页面跳转的时候很快,但在第一次查询时,会有大量的磁盘IO操作,在数据类大的情况下,会对系统性能造成很大的影响,而搜索结果大部分用户只是关注前几页,后面的结果访问量并不大,这样就有些得不偿失。分页查询采用数据库的limit条件,只在每次查询时获取每一页要显示的数据,在创建了索引以后,这个查询过程是非常迅速的,只将需要的数据读入内存。查询效率提高了,就需要自己来实现分页。
    4.3.3 视图类视图使用jsp作为页面,引入了JSTL的c库来辅助生成布局。视图文件如图4.8所示。

    header.jsp为页面顶端的logo、搜索栏与登录注册按钮的部分。footer.jsp为页面底部版权信息的内容。pagination.jsp为分页,根据SearchResultPaginationModel.java的内容来生成分页。
    bookInfo.jsp负责显示图书的详细内容,index.jsp负责首页内容的显示,login和register负责登录与注册页面,resultList为搜索结果,只是单纯的搜索结果列表,用于分页时,通过AJAX请求来局部刷新,减少流量。resultListPag为搜索结果页面,是页面的框架,其中引入了resultList,作为第一次访问时,搜索结果的展示。
    每个页面都引入header.jsp与footer.jsp,用来引入所需要的布局文件与页面脚本,构建起基本的页面框架。页面导航栏的布局采用Bootstrap的导航栏样式,登录可以从导航栏上直接输入来登录。搜索条件分为标题、作者、出版社,可以对这三者进行查询。
    采用JSTL的标准c标签库,方便的实现循环(c:forEach)、判断(c:if、c:when),
    页面整体布局采用Bootstrap的响应式布局,首页、搜索结果页将正文部分分为左右两块,图书信息页面只有一个块。在首页中各个块中,每本书作为一个row类型,每个row又分为两个span,用于分割左右两块。一部分布局是由自定义的main.css文件来定制。而自定义的JavaScript也由在footer中引入的main.js来定制那些比如搜索按钮点击事件、分页按钮点击事件、登录等等。
    分页按钮的样式采用了Bootstrap的分页按钮,参考Amazon查询结果的分页效果,在页数多于9页的情况下,翻到中部,则只显示部分挨着的页码,结合SearchResultPaginationModel类的结构,完成了查询的分页。
    4.4 分析及调优首页为所有页面中最为复杂的页面,需要查询四块内容,页面效果如图4.9所示。

    四块内容分别需要按照各自的查询条件来排序,然后取前几个符合条件的结果。在刚开始,没有对数据库优化之前,页面打开速度几乎需要3秒,对查询SQL进行分析,发现,大部分操作时间都消耗在了排序上,于是对排序条件创建了索引,首页的首次打开延迟变得小于1秒,并且由于在控制器中使用getter\setter,部分没有参数的数据会在查询之后一直留在内存中,不会进行第二次查询,所以刷新会返回304,页面内容没有改变。

    图4.10为查询结果页面。由于此平台不涉及搜索算法的研究,所以搜索结果为从数据库字符串中like出来的。考虑到数据库巨大,若不做处理,将会严重影响查询效率。于是按照查询特点,对图书标题、出版日期两列做了索引,查询速度有了成倍的提升。再加上每次查询使用limit,磁盘内存间的交换操作减少了许多。

    图4.11为图书信息的展示页面,对图书的缩略图、内容以及作者简介做了展示,还有登录失败后会跳转到的登录页面,注册按钮点击后跳转到的注册页面,相对于其它两个相对功能较单一,性能也没有太大的提升空间。
    4.5 性能测试使用Chrome浏览器的开发者工具来进行测试。结果如图4.12所示,首次加载首页需要等待6ms,其它静态的css与js文件几乎不需要时间,在统计结果中,显示为0。之后刷新,Tomcat会从内存中直接取得返回结果,统计如图4.13所示,由图可知,加载页面仅需要用时2ms。

    在查询结果页面,查询一个关键词“IOS”,页面的等待时间为223ms。同样由于缓存的作用,刷新的等待时长变为8ms。结果如图4.14与4.15所示。

    4.6 小结本章介绍了整个系统的每个模块的整体设计、详细设计以及实现方式。并进行了简单的分析以及性能的调优,最终系统运行性能良好,稳定,系统结构简洁,功能完善。
    结 论随着计算机网络技术的发展,越来越多的电子商务网站、图书分析网站以及一些图书馆系统都采用了图书推荐系统这种智能化的解决方案,它有效的实践了长尾理论,增加了用户粘性。
    本文对基于Web的图书推荐系统的展示平台做了初步研究,开发了一个平台,开发过程包括网站数据库设计、数据清洗、系统结构设计、系统搭建、版本控制、性能调优等,功能模块包括图书展示、图书搜索、图书推荐、用户登录注册等,还对设计的过程、网站的结构、项目的管理方法进行了探索,对项目中遇到的难点给出了一些解决方案,起到的效果也比较不错。
    研究的核心部分是系统的设计与工具的使用。由于系统开发采用MVC结构,系统各个层级与模块之间的关系以及数据库结构,都需要一个具有高扩展性和可复用的设计,否则在开发后期或是在后期维护阶段,都会因为工程大小的增长而带来耦合性的急剧上升,最终导致功能实现与错误修改变得异常艰难。一些开源的开发框架与项目管理工具在系统开发过程中会极大地减轻开发者浪费在无关事务上的工作量,开发者不需要去操心布局怎样实现才能美观,怎样达到浏览器兼容,以及怎样去管理版本,控制查看进度,去哪里下载依赖包,项目需要哪些依赖,依赖包怎样升级等等。工具也可以减少开发者的重复劳动,比如MyBatis Generator可以自动生成单表操作的Mapper文件。
    开发中的难点是数据的清洗过程与数据库查询效率的优化。数据的清洗过程需要读取出记录后,对数据解析、去新表查询,根据查询结果插入到新表或者更新新表。操作比较复杂,由于数据的格式极不规整,在解析时,花了许多时间研究数据的格式的可能性,编写代码去除干扰数据,并用正则表达式解析它。一开始数据查询效率极低,在分析了SQL查询语句之后,对数据相应字段创建了索引,才使得数据查询的速度符合期望,解决了查询的效率问题。
    参考文献[1]项亮,陈义,王益著.推荐系统实践[M].北京:人民邮电出版社,2012.
    [2]Vincent, L.Google Book Search: DocumentUnderstanding on a Massive Scale[J].Document Analysis and Recognition,2007: 819 - 823
    [3]裴玉洁著.采用数据挖掘技术的自动化推荐系统的研究[D].2012.
    [4]吴定勇,王峰著.亚马逊书店的网络售书之道[J].北京:当代传播,2008, (6): 123-125.
    [5]李连焕,刘建东著.基于Web日志挖掘的图书借阅推荐算法研究[J].北京:硅谷,2012,(6): 93-94.
    [6]古丽拜天.卡米尔著.基于Web数据挖掘的智能推荐研究[D].湖南:中南大学,2010.
    [7]李俊杰著.Maven在企业Java软件产品中的应用[D].北京:北京邮电大学,2011.
    [8]刘悦之著.基于Git的分布式版本控制系统的设计与实现[D].上海:同济大学,2012.
    [9]张琛,吴跃,邱会中著.基于Structs+Spring+Hibernate的整合架构及其在电信业中的应用[D].四川成都:电子科技大学,2006.
    [10]陆荣幸,郁洲,阮永良,王志强著.J2EE平台上MVC设计模式的研究与实现[J].计算机应用研究,2003, 20(3): 144-146.
    [11]Praveen Gupta, Prof. M.C. Govil. MVC DesignPattern for the multi framework distributed applications using XML, spring andstruts framework[J].International Journal on Computer Science and Engineering,2010, 2(4):1047-1051.
    [12]冯润民著.基于SSH的高校学生管理系统设计与实现[J].北京:计算机工程,2009, 35(6): 280-282.
    [13]DaveMinter,Jeff Linwood著.Hibernate基础教程[M].陈剑瓯等译.北京:人民邮电出版社,2008.
    [14]孙强,孙龙清,邱小彬著.基于Structs+Spring+iBATIS的轻量级Web应用框架研究[J].北京:计算机应用与软件,2008, 25(10): 135-137.
    [15]王庆民著.基于Web图书推荐系统设计[J].山西:晋图学刊,2011, (1): 35-37.
    [16]曾庆辉,邱玉辉著.一种基于协作过滤的电子图书推荐系统[J].北京:计算机科学,2005,32(6): 147-150.
    [17]张富国著.电子商务协同过滤推荐系统的研究与进展[D].江西:江西财经大学信息管理学院,2010.
    [18]田元,宋纬华,李婷婷著.基于Markov链的图书推荐系统的研究与设计[J].陕西:西安理工大学图书馆,2012, 32(6): 79-82.
    致 谢本课题是在指导老师的亲切关怀和悉心指导下完成的。老师对提交文档的细心浏览,总能挑出不合要求的地方,指导我改正。对我的毕设进度也一直很关心,提醒我按时提交。老师给予我很大帮助,在项目初期确定题目给我很大帮助。在此,谨向老师们表示由衷的感谢。
    另外,要感谢学姐、同学以及学弟,在项目初期,与老师一起讨论我的题目与内容,在整个毕设过程中,学姐对我提交的文档、论文以及整个毕设中一些注意事项为我提供指导意见。感谢同学,在我上班时间帮我打印、找老师签字以及提交文档,还有对我关于毕业一些疑问的解答。
    最后,感谢我使用的所有开源、自由软件的开发者们。你们无私的工作引导了各个领域的技术创新,改变了人们的生活方式。没有前人的工作,我不可能完成这个课题,感谢你们,感谢所有关心、帮助过本课题的老师、同学们!
    8 评论 32 下载 2018-09-29 21:17:50 下载需要17点积分
  • 基于Python和SQL SERVER数据库实现的实验信息综合管理系统

    1 需求分析1.1 需求来源本系统是对于实验综合信息进行管理的系统。
    1.1.1 功能需求
    学生用户:修改系统登陆密码、查询选择的课程、查询选择课程对应的实验课表、导出查询到的课程数据、实验课表数据
    教师用户:修改系统登陆密码、修改个人联系方式、查询教授的课程、查询实验安排、增加课程信息、增加实验信息、按学号查询出勤率、按班级查询出勤率、导出查询到的数据记录
    管理员用户:增加学生信息、修改学生信息、增加教师信息、修改教师信息、增加课程信息、修改课程信息,增加实验信息、修改实验信息,增加实验室信息、修改实验室信息,更新实验打卡信息

    1.1.2 数据需求系统涉及的数据表为8个表,管理员信息表(ADMINB)、教师表(JSB)、教师课程表(JSKCB)、课程表(KCB)、实验打卡表(SYDKB)、实验室表(SYSB)、实验信息表(SYXXB)、学生表(XSB)。
    其中管理员信息表包含的基本属性为管理员用户名、登陆密码。教师表包含的几本书新为教师姓名、联系方式、登陆密码。教师课程表包含的基本属性为教师姓名、课程代码。课程表包含的基本属性为课程代码、课程名称、开课学院。实验打卡表包含的基本属性为实验编号、学生学号、打卡状态。实验室表包含的基本属性为实验中心、实验分室、上课地点。实验信息表包含的基本属性为实验编号、课程代码、实验项目、上课老师、辅助教师、上课日期、星期几、上课地点。学生表包含的基本属性为学号、姓名、班级、登陆密码。
    1.1.3 性能需求
    要求系统在本地访问时数据具有可靠性、运行速度快,简单快捷
    要求系统能在多系统(Windows、Linux)、多终端(PC、手机)

    1.2 设计目的利用服务器和大容量存储等最新的硬件设备,以及数据库和网络技术所开发出的实验信息管理系统使用户能对大量的实验综合信息进行高效的管理。通过实验信息管理系统,信息录入、查询等原先繁复枯燥的工作的效率得到了显著的提升;更重要的是数据的准确性和安全性也同时得到了保证。
    1.3 设计要求不同类型的用户能够通过本系统使用不同的功能,对实验综合信息进行管理。
    1.4 开发工具及相关技术通过采用Microsoft SQL Server等大型关系型数据库,实验的各项数据的存储更为规范和完整。数据库技术也使得实验数据的备份和恢复变得简单便捷。而在硬件方面,采取RAID5等存储解决方案组成的磁盘阵列,以极低的存储成本极大的提高了数据的安全性。通过采用python平台开发,采用了基于B/S的三层开发结构(UI、BLL、DAL)。同时运用tkinter等GUI的最新技术,提供美观实用的系统界面和顺畅的用户体验。
    2 总体设计2.1 总体结构设计为了满足实验综合信息管理系统各方面信息的管理功能,需要明确用户的各个功能。数据库中应该有教师、学生以及管理员的基本信息,这些来自于本系统。教师、学生只能在客户端进行操作。管理员可以使用管理端操作,修改、删除、增加教师、学生的信息。此类权限不对教师、学生用户开放。除此之外,还要通过登录名和密码严格限定登陆者的身份并且控制相应的操作权限,只有管理员有权限修改账户以及密码,防止其他密码泄露造成的其他问题。
    2.1.1 模块设计系统模块设计:

    2.2.2 模块功能描述“管理员用户”子系统针对管理员用户的日常工作流程,分为“学生管理”,“教师管理”,“课程管理”,“实验管理”,“实验室管理”等子模块。其模块具体功能描述如下:

    “学生管理”模块:该模块对学生信息进行维护,如对增加新入学学生的信息,为转专业学生更改班级信息,为改名学生更改姓名信息,删除退学、毕业学生信息,为忘记登陆密码的学生用户重新设置用户密码等
    “教师管理”模块:该模块对教师信息进行维护,如对增加新入职老师的信息,删除离职老师的信息,为忘记登陆密码的教师用户重新设置登陆密码等
    “课程管理”模块:该模块对课程信息进行维护,如对课程名称、开课学院等信息系的修改,增加新开设课程的信息
    “实验管理”模块:该模块对实验信息进行维护,如对实验下属各种信息的修改调整、增加新的实验信息、更新实验打卡信息等
    “实验室管理”模块:该模块对实验室信息进行维护,如对实验中心、实验分室、实验教师信息的修改、增加等

    “教师用户”子系统针对教师用户的日常使用流程,分为“学生管理”模块,“课程管理”模块,“实验管理”模块、“个人管理”模块。其模块具体功能描述如下:

    “学生管理”模块:该模块对学生信息进行查询,如对班级整体实验打卡记录的查询,对指定具体学号的学生实验打卡记录的查询,对查询到的信息进行导出等
    “课程管理”模块:该模块对课程信息进行查询与维护,如将新教授的课程信息录入系统,查询正在教授的课程信息等
    “实验管理”模块:该模块对实验信息进行查询与维护,如查询已安排为授课教师的实验信息,添加新的实验信息等
    “个人管理”模块:该模块对教师个人信息进行维护,如修改自己的登陆密码、修改个人的联系方式等

    “学生用户”子系统针对学生用户的日常使用流程,分为“个人管理”模块,“课程管理”模块,“实验管理”模块等。其模块具体功能描述如下:

    “个人管理”模块:该模块对学生用户个人信息进行维护,如修改自己的登陆密码等
    “课程管理”模块:该模块对课程信息进行查询,如查询自己选择的课程信息等
    “实验管理”模块:该模块对实验信息进行查询,如查询自己选择的课程所安排的实验信息等

    2.2 数据库设计2.2.1 概念结构设计
    管理员信息表(ADMINB)主要用于在系统中储存管理员信息,包含了管理员的登陆ID、密码,其中登陆ID是主键
    教师表(JSB)主要用于在系统中储存教师信息,包含了教师用户的姓名、密码、联系方式,其中姓名是主键。管理员用户可对该表进行维护
    教师课程表(JSKCB)主要用于在系统中储存教师授课信息,包含了教师姓名、课程代码。管理员用户、教师用户可对该表进行维护
    课程表(KCB)主要用于储存课程信息,包含了课程代码、开课学院、课程名称,其中课程代码是主键。管理员用户可对该表进行维护
    实验打卡表(SYDKB)主要用于储存学生打卡信息,包含了实验编号,学生学号、打卡状态。管理员用户可对该表进行维护,教师用户可对该表进行查询
    实验室表(SYSB)主要用于储存实验室信息,包含了实验中心、实验分室、实验教室,其中实验教室是主键。管理员用户可对该表进行维护
    实验信息表(SYXXB)主要用于储存实验信息,包含了实验编号、课程代码、实验项目、上课老师、辅助教师、上课日期、星期几、上课地点,其中实验编号是主键。管理员用户、教师用户可对该表进行维护
    学生表(XSB)主要用于储存学生信息,包含了学号、姓名、班级、登陆密码,其中学号是主键。管理员用户可对该表进行维护

    系统E-R图

    2.2.2 逻辑结构设计根据E-R模型转换关系结构:

    教师实体集(JSB)可以转换化为关系:
    教师用户(姓名,联系方式,登陆密码)

    管理员实体集(ADMINB)可以转化为关系:
    管理员用户(登陆ID,密码)

    课程实体集(KCB)可以转化为关系:
    课程(课程代码,开课学院,课程名称)

    实验室实体集(SYSB)可以转化为关系:
    实验室(实验分室、实验中心、实验教室)

    实验信息实体集(SYXXB)可以转化为关系:
    实验信息(实验编号、课程代码、实验项目、上课老师、辅助教师、上课日期、星期几)

    学生实体集(XSB)可以转换为关系:
    学生(姓名、学号、班级)

    教师与课程之间是n:m的联系,可以转化为一个关系:
    教师课程(JSKCB)(教师姓名、课程编号)

    学生与实验打卡信息之间是1:m的联系,可以对学生关系进行拓展,但由于打卡信息较多,进行扩展会导致数据库冗余信息较多,故单独转化为一个关系:
    学生打卡(XSDKB)(学号、实验编号、打卡信息)

    实验室与实验信息之间是n:m的联系,可以对实验信息进行拓展,不必单独转为一个关系:
    实验信息(实验编号、课程代码、实验项目、上课老师、辅助教师、上课日期、星期几、实验教室)
    2.2.3 物理结构设计教师表(JSB)



    字段名称
    数据类型
    字段长度
    是否为空
    备注




    姓名
    varchar
    20
    No
    主码


    联系电话
    varchar
    20
    Yes



    登陆密码
    varchar
    255
    Yes



    教师课程表(JSKCB)



    字段名称
    数据类型
    字段长度
    是否为空
    备注




    课程代码
    varchar
    20
    No



    上课教师
    varchar
    20
    No



    课程表(KCB)



    字段名称
    数据类型
    字段长度
    是否为空
    备注




    课程代码
    varchar
    20
    No
    主码


    开课学院
    varchar
    20
    No



    课程名称
    varchar
    255
    No



    **实验打卡表(SYDKB)



    字段名称
    数据类型
    字段长度
    是否为空
    备注




    实验编号
    varchar
    20
    No



    学号
    varchar
    20
    No



    实际上课时间
    varchar
    255
    No



    状态
    varchar
    255
    No



    实验室表(SYDKB)



    字段名称
    数据类型
    字段长度
    是否为空
    备注




    实验中心
    varchar
    255
    No



    实验分室
    varchar
    255
    No



    实验教室
    varchar
    255
    No
    主码



    实验信息表(SYXXB)



    字段名称
    数据类型
    字段长度
    是否为空
    备注




    实验编号
    varchar
    255
    No
    主码


    课程代码
    varchar
    255
    No



    实验项目
    varchar
    255
    No



    上课老师
    varchar
    255
    No



    辅助教师
    varchar
    255
    Yes



    上课日期
    varchar
    255
    No



    星期几
    varchar
    255
    No



    实验教室
    varchar
    255
    No



    学生表(XSB)



    字段名称
    数据类型
    字段长度
    是否为空
    备注




    姓名
    varchar
    20
    No



    学号
    char
    11
    No
    主码


    班级
    varchar
    255
    No



    登陆密码
    varchar
    255
    Yes



    管理员表(XSB)



    字段名称
    数据类型
    字段长度
    是否为空
    备注




    登陆ID
    varchar
    20
    No
    主码


    登陆密码
    varchar
    255
    No



    3 详细设计3.1 数据库的创建create database SYDK
    3.2 表的创建import pyodbccnxn = pyodbc.connect('DRIVER={SQL Server};SERVER=127.0.0.1;DATABASE=SYDK;UID=sa;PWD=test')cursor = cnxn.cursor()cursor.execute(""" Create Table XSB ( 班级 varchar(20), 学号 char(11), 姓名 varchar(20), 登陆密码 varchar(255) ) """)#学生表cursor.execute(""" Create Table KCB ( 课程代码 varchar(20), 课程名称 varchar(255), 开课学院 varchar(20) ) """)#课程表cursor.execute(""" Create Table JSB ( 姓名 varchar(20), 联系电话 varchar(20), 登陆密码 varchar(255) ) """)#教师表cursor.execute(""" Create Table SYXXB ( 实验编号 varchar(20), 课程代码 varchar(20), 实验项目 varchar(20), 上课老师 varchar(20), 辅助教师 varchar(20), 上课日期 varchar(255), 星期几 varchar(20), 实验中心 varchar(255), 实验分室 varchar(255), 上课地点 varchar(255) ) """)#实验信息表cursor.execute(""" Create Table SYDKB ( 实验编号 varchar(20), 学号 varchar(20), 实际上课时间 varchar(255), 状态 varchar(255) ) """)#实验打卡表cursor.execute(""" Create Table SYSB ( 实验中心 varchar(255), 实验分室 varchar(255), 上课地点 varchar(255) ) """)#实验室表cursor.execute(""" Create Table JSKCB ( 课程代码 varchar(20), 上课老师 varchar(20) ) """)#教师课程表cursor.execute(""" Create Table ADMINB ( id varchar(20), password varchar(20) ) """)#管理员表cursor.commit()cursor.close()
    3.3 数据设定本系统对接南京信息工程大学实践教学综合管理平台,通过访问平台接口,以正则表达式的方法,获取得到教师信息、学生信息、实验信息,从其内含的依赖关系衍生出实验打卡表,实验室表,课程表,教室课程表。同时,管理员用户通过本系统,也可调用该程式对数据库中信息进行维护工作。核心代码如下:
    cnxn = pyodbc.connect('DRIVER={SQL Server};SERVER=127.0.0.1;DATABASE=SYDK;UID=sa;PWD=test') cursor = cnxn.cursor() cursor.execute("SELECT * FROM SYXXB WHERE 实验编号="+str(i)) row = cursor.fetchall() print(i) if (len(row)!=0): cursor.close() continue url="http://sjjx.nuist.edu.cn/syjx/admin/experiment/looksyzx.aspx?type=kb&id="+str(i) try: req=urllib.request.Request(url) resp=urllib.request.urlopen(req) except: print("oppops") continue data=resp.read().decode('GBK') w1='<td width=\'610\' height=\'25\' style=\'padding-left:5px;\'>' w2='</td></tr>' pat=re.compile(w1+'(.*?)'+w2,re.S) sybz=pat.findall(data) if(len(sybz)==0): continue del(sybz[9]) w1='<td height=\'25\' style=\'padding-left:5px; width: 82px;\'>' w2='</td>' pat2=re.compile(w1+'(.*?)'+w2,re.S) syb=pat2.findall(data) del(syb[9]) for j in range(0,len(syb)): print (syb[j]+':'+sybz[j]) if(sybz[4].find("(")!=-1): tmp=sybz[4] tmp=tmp[0:tmp.find("(")] sybz[4]=tmp print(sybz[4]) print(sybz[5]) w1='<td align=\'center\'>' w2='</td>' pat3=re.compile(w1+'(.*?)'+w2,re.S) xsbz=pat3.findall(data) cursor.execute("SELECT * FROM JSB WHERE 姓名=\'"+sybz[4]+"\'") row = cursor.fetchall() if (len(row)==0): cursor.execute("insert into JSB values ("+"\'"+sybz[4]+"\'"+",\'"+sybz[5]+"\'"+",\'\')") cursor.execute("SELECT * FROM KCB WHERE 课程代码="+sybz[0])cursor.commit();cursor.close();
    3.4 模块设定3.4.1 登陆模块本模块供不同类型的用户登陆使用,通过单选框的选择,可以选择登陆的用户类型,输入账号密码后,登陆本系统。系统访问数据库判断密码情况,若密码错误则无法成功登陆,访问请求被系统拒绝,若密码正确则进入对应的综合管理模块。

    其核心代码如下:
    from tkinter import *from CoCenter import *import tkinter.messageboxclass LoginFrame (Frame): def __init__(self,master): def ButtonClick(self): s1 = self.ent1.get() s2 = self.ent2.get() if(self.v.get() == 0): self.login(s1,s2,"XSB","学号") elif(self.v.get() == 1): self.login(s1,s2,"JSB","姓名") elif(self.v.get() == 2): self.login(s1,s2,"admin") else: showinfo(title = '错误', message = '未选择登陆方式!') def login(self,name,pwd,tbname="XSB",ab="学号"): if(tbname=="admin"): import pyodbc cnxn = pyodbc.connect('DRIVER={SQL Server};SERVER=127.0.0.1;DATABASE=SYDK;UID=sa;PWD=test') cursor = cnxn.cursor() cursor.execute("SELECT password FROM ADMINB WHERE id =\'" +name+"\'") row = cursor.fetchall() cursor.close() if(len(row)==0): tkinter.messagebox.showinfo(title = '错误', message = '用户名不存在') else: if(getVal(str(row[0]))==pwd): tkinter.messagebox.showinfo(title = '成功', message = '登陆成功')
    3.4.2 综合管理模块学生用户管理模块
    学生用户登陆成功后,则可以进入学生用户管理模块,通过点击学生用户管理模块上的按钮,可以调用相应的信息维护模块。

    其核心代码如下:
    self.button=tk.Button(frame,text = "更改登陆密码",pady=10,command = self.XSinfoUpdate)self.button.grid(row = 0,column = 0,padx=10)self.button1=tk.Button(frame,text = "查询个人选课",pady=10,command = self.XSCrouseQry)self.button1.grid(row = 0,column = 1,padx=10,pady=15)self.button2=tk.Button(frame,text = "查询实验课表",pady=10,command = self.XSCrouseQry2)self.button2.grid(row = 0,column = 2,padx=10)self.geometry('310x80')
    教师用户管理模块
    教师用户登陆成功后,则可以进入教师用户管理模块,通过点击教师用户管理模块上的按钮,可以调用相应的信息维护模块。

    其核心代码如下:
    self.button=tk.Button(frame, text = "更改个人信息",pady=10,command = self.JSinfoUpdate)self.button.grid(row = 0,column = 0,padx=10,pady=10)self.button1=tk.Button(frame,text = "更改登陆密码",pady=10,command = self.JSPwdUpdate)self.button1.grid(row = 0,column = 1)self.button2=tk.Button(frame,text = "查询所教课程",pady=10,command = self.JSCrouseQry)self.button2.grid(row = 1,column = 0,padx=10,pady=10)self.button3=tk.Button(frame,text = "查询实验信息",pady=10,command = self.JSExpQry)self.button3.grid(row = 1,column = 1)
    管理员用户管理模块
    管理员用户登陆成功后,则可以进入管理员用户管理模块,通过点击管理员用户管理模块上的按钮,可以调用相应的信息维护模块。

    其核心代码如下:
    self.button=tk.Button(frame,text = "添加学生信息",pady=10,command = self.AdStuAdd)self.button.grid(row = 0,column = 0,padx=10,pady=10)self.button1=tk.Button(frame,text = "修改学生信息",pady=10,command = self.AdStuUpdate)self.button1.grid(row = 0,column = 1,padx=10,pady=10)self.button2=tk.Button(frame,text = "添加教师信息",pady=10,command = self.AdJSAdd)self.button2.grid(row = 1,column = 0,padx=10,pady=10)self.button3=tk.Button(frame,text = "修改教师信息",pady=10,command = self.AdJSUpdate)self.button3.grid(row = 1,column = 1,padx=10,pady=10)self.button4=tk.Button(frame,text = "添加课程信息",pady=10,command = self.AdKCAdd)self.button4.grid(row = 2,column = 0,padx=10,pady=10)self.button5=tk.Button(frame,text = "修改课程信息",pady=10,command = self.AdKCUpdate)self.button5.grid(row = 2,column = 1,padx=10,pady=10)self.button6=tk.Button(frame,text = "添加实验信息",pady=10,command = self.AdExpAdd)self.button6.grid(row = 3,column = 0,padx=10,pady=10)self.button6=tk.Button(frame,text = "修改实验信息",pady=10,command = self.AdExpUpdate)self.button6.grid(row = 3,column = 1,padx=10,pady=10)self.button6=tk.Button(frame,text = "添加实验室信息",pady=10,command = self.AdExpPlaceAdd)self.button6.grid(row = 4,column = 0,padx=10,pady=10)
    3.4.3 信息维护模块用户信息维护模块
    根据不同用户,调用不同的信息维护模块,具体描述如下:
    学生用户调用本模块对登陆密码进行维护

    其核心代码如下:
    Sql("EXEC XS_INFO_UPDATE \'"+self.name+"\',\'"+str(self.newp.get())+"\'",2)tk.messagebox.showinfo('提示','修改成功')
    教师用户调用本模块对登陆密码、联系方式进行维护。

    其核心代码如下:
    Sql("EXEC JS_PWD_UPDATE \'"+self.name+"\',\'"+str(self.newp.get())+"\'",2)tk.messagebox.showinfo('提示','修改成功')
    管理员用户调用本模块对学生的所有属性、教师用户的所有属性进行维护。

    其核心代码如下:
    if(self.element=="STUDENT"): if(self.flag=="ADD"): Sql("INSERT INTO XSB VALUES (\'"+str(self.clsname.get())+"\',\'"+str(self.id.get())+"\',\'"+str(self.name.get())+"\',\'"+str(self.pwd.get())+"\')",2) else: Sql("UPDATE XSB SET 班级=\'"+str(self.clsname.get())+"\',姓名=\'"+str(self.name.get())+ "\',登陆密码=\'"+str(self.pwd.get())+"\' \n WHERE 学号=\'"+str(self.id.get())+"\'",2)if(self.element=="TEACHER"): if(self.flag=="ADD"): Sql("INSERT INTO JSB VALUES (\'"+str(self.id.get())+"\',\'"+str(self.tel.get())+"\',\'"+str(self.pwd.get())+"\')",2) else: Sql("UPDATE JSB SET 联系电话=\'"+str(self.tel.get())+ "\',登陆密码=\'"+str(self.pwd.get())+"\' \n WHERE 姓名=\'"+str(self.id.get())+"\'",2)
    课程信息维护模块
    不同用户对于课程信息维护模块具有不同的权限,具体如下:
    学生用户具有对课程信息的查询权限,对于查询到的课程信息,学生用户可以选择是否导出为excel表格形式:

    其核心代码如下:
    row=Sql("EXEC XS_Course_Qry1 \'"+ self.stuid+"\'",1)table=PrettyTable(["课程代码","课程名称","开课学院"])row1=getVal(row)for i in range(0,int(len(row1)/3)):table.add_row([row1[i*3],row1[i*3+1],row1[i*3+2]])tk.messagebox.showinfo(title = '课程', message = table)a=tk.messagebox.askokcancel('提示', '是否导出选课表')
    教师用户具有对课程信息的查询、创建权限,对于查询到的课程信息,教师用户可以选择是否导出为excel表格形式:

    核心代码如下:
    row=Sql("EXEC JS_Course_Qry \'"+ self.stuid+"\'",1)table=PrettyTable(["课程代码","课程名称","开课学院"])row1=getVal(row)for i in range(0,int(len(row1)/3)):table.add_row([row1[i*3],row1[i*3+1],row1[i*3+2]])tk.messagebox.showinfo(title = '课程', message = table)
    管理用户具有对课程信息的所有权限:

    其核心代码如下:
    if(self.element=="CROUSE"): if(self.flag=="ADD"): Sql("INSERT INTO KCB VALUES (\'"+str(self.id.get())+"\',\'"+str(self.name.get())+"\',\'"+str(self.inst.get())+"\')",2) elif(self.flag=="UPDATE"): Sql("UPDATE KCB SET 开课学院=\'"+str(self.inst.get())+ "\',课程名称=\'"+str(self.name.get())+"\' \n WHERE 课程代码=\'"+str(self.id.get())+"\'",2) else: Sql("INSERT INTO KCB VALUES (\'"+str(self.id.get())+"\',\'"+str(self.name.get())+"\',\'"+str(self.inst.get())+"\')",2) tmp=str(self.flag).split("2") Sql("INSERT INTO JSKCB VALUES (\'"+str(self.id.get())+"\',\'"+str(tmp[1])+"\')",2)
    实验信息维护模块
    根据不同用户,调用不同的信息维护模块,具体描述如下:
    学生用户对于实验信息具有查询权限,对于查询到的实验信息,可以选择是否导出为EXCEL表格

    其核心代码如下:
    row =Sql("EXEC XS_Course_Qry2 \'"+ self.stuid+"\'",1) table=PrettyTable(["实验编号","课程代码","实验项目", "上课老师","辅助教师","上课日期","星期几", "实验中心","实验分室","上课地点"]) row1=getVal(row) for i in range(0,int(len(row1)/10)): table.add_row([row1[i*10],row1[i*10+1],row1[i*10+2], row1[i*10+3],row1[i*10+4],row1[i*10+5], row1[i*10+6],row1[i*10+7],row1[i*10+8],row1[i*10+9]]) tk.messagebox.showinfo(title = '课表', message = table) a=tk.messagebox.askokcancel('提示', '是否导出课表')
    教师用户对于实验信息具有查询、修改权限,对于查询到的实验信息,可以导出为EXCEL表格

    其核心代码如下:
    if(self.element=="EXP"): if(self.flag=="ADD"): Sql("INSERT INTO SYXXB VALUES (\'"+str(self.id.get())+"\',\'"+str(self.kcdm.get())+"\',\'"+str(self.syxm.get())+"\',\'"+str(self.skls.get())+"\',\'" +str(self.fzjs.get())+"\',\'"+str(self.skrq.get())+"\',\'"+str(self.xqj.get())+"\',\'"+str(self.syzx.get())+"\',\'"+str(self.syfs.get())+"\',\'"+str(self.skdd.get())+"\')",2) tk.messagebox.showinfo('提示','更新成功!')
    同时,教师用户对于实验打卡信息具有查询权限,可以通过学生学号查询打卡信息,也可以通过所教班级查询打卡信息,并将打卡信息导出到EXCEL表格。

    其核心代码如下:
    for i in range(0,len(row1)): if(len(row1[i])==11): row=Sql("EXEC JS_STU_QRY5 \'"+self.name+"\',\'"+row1[i]+"\'",1) if(len(row)==0): continue else: row=Sql(""" declare @f1 float declare @f2 float EXEC JS_STU_Qry2 \'"""+str(row1[i])+"\',\'"+self.name+"""\',@f1 output,@f2 output SELECT @f1,@f2""",1) row2=getVal(row) table.add_row([str(row1[i]),str(row2[0]),str(row2[1])]) tk.messagebox.showinfo(title = '查询结果', message =table)
    管理员用户对于实验信息具有全部权限

    核心代码如下:
    Sql("UPDATE SYXXB SET 课程代码=\'"+str(self.kcdm.get())+"\',实验项目=\'" +str(self.syxm.get())+"\',上课老师=\'" +str(self.skls.get())+"\',辅助教师=\'" +str(self.fzjs.get())+"\',上课日期=\'" +str(self.skrq.get())+"\',星期几=\'" +str(self.xqj.get())+"\',实验中心=\'" +str(self.syzx.get())+"\',实验分室=\'" +str(self.syfs.get())+"\',上课地点=\'" +str(self.skdd.get()) +"\' \n WHERE 实验编号=\'"+str(self.id.get())+"\'",2)
    实验室信息维护模块
    管理员用户对实验室信息具有全部权限

    其核心代码如下:
    if(self.element=="EXPPLACE"): if(self.flag=="ADD"): Sql("INSERT INTO SYSB VALUES (\'"+str(self.syzx.get())+"\',\'"+str(self.syfs.get())+"\',\'"+str(self.skdd.get())+"\')",2) else: Sql("UPDATE SYSB SET 实验中心=\'"+str(self.syzx.get())+ "\',实验分室=\'"+str(self.syfs.get())+"\' \n WHERE 上课地点=\'"+str(self.skdd.get())+"\'",2)
    4 总结整个系统的设计过程对于我来说算是个学习、探索的过程,通过实践和对比别人开发程序的过程。在整个设计过程中,出现过很多的问题,很多繁琐的东西都需要反复的修改,主要是前期工作不彻底,对系统的需求分析的要求认识不够清楚,使得在后边的工作中不得不经常反复去修改。使我体会到设置中每一步的重要性。所以在分析一个问题时,我们需要站在一个有远见的高度。
    虽然时间紧迫但我学会了很多,也感到自身知识的贫乏,希望在日后的努力中能做出更完善的系统。
    2 评论 17 下载 2018-10-24 21:35:32 下载需要11点积分
  • 基于JAVA FX的葫芦娃游戏

    一、总体描述1.1 战斗方式相遇之时一决雌雄!
    设计的战斗方式为近战,只有双方阵营的生物处在相邻的单元格时,才会触发战斗。

    与图中红色格子相邻的只有绿色格子,灰色格子与红色格子不相邻。
    1.2 战斗空间在一个15×15的二维矩阵格子上,葫芦娃与妖精双方排好队形准备战斗。

    葫芦娃总是在空间左侧排好阵型,妖精总是在空间右侧排好阵型。葫芦娃可供选择的阵有四个,分别是:长蛇阵、鹤翼阵、雁行阵、冲轭阵(其他四种阵型因为人数限制葫芦娃一方无法摆出),妖精一方可以排出全部八种阵型。在战斗开始之前的任何时刻,玩家都可选择更换双方阵型。
    当双方排好阵后,点击开始按钮即展开战斗(或者直接按下空格键,程序会调用默认阵型并展开战斗)。
    1.3 多线程部分每个生物体都是一个线程,多个线程同时运行,共享地图信息。当任何一个线程对共享信息进行操作时,共享数据会在此时被锁定,以保证线程安全,同时也避免了多个生物体跑到同一个单元格、多个生物体杀死一个生物体的问题。
    1.4 判断获胜方判断获胜方有两个层面的意思:

    第一个层面是大局上的胜利,是妖精一方获胜还是葫芦娃一方得胜
    第二个层面是局部的胜利,当两个阵营中的两个个体相遇,一决生死时,用什么决定哪一个存活

    解决第一个问题所采用的办法是在三十秒内,若战斗结束,则仍有生物存活一方获胜;第二个问题的解决办法是以一个根据生物体战斗力所计算出的获胜概率,随机确定获胜者。
    1.5 回放将战斗中的数据信息保存为一个.txt文件,点击回放按钮后,读取该文件进行回放。因为是以字符的方式保存生物体位置信息,所以每次战斗所产生的文件不会超过200KB。
    二、细节阐述2.1 JavaFX采用scene builder来设计JavaFX界面,拖拽操作的方式降低了自己编写fxml文件的难度。
    2.2 maven整个工程以maven组织,运行起来比较简单。
    2.3 模块说明Main包
    包括两个类,主函数Main类和MainControl类,用于和用户交互。
    MainControl中设置了十四个按钮,四个葫芦娃阵型按钮,八个妖精阵型按钮,一个开始按钮,一个复盘按钮。
    Formation包
    包括九个类,一个Formation基类和八个阵型子类。继承关系如下图所示:

    在阵型子类中让生物体“复苏”。
    create包
    包括七个类,一个Creature基类和六个生物体子类,另外还包含两个葫芦娃的枚举类型。

    Creature基类中有以下几种对生物体的基础描述
    public int power; //力量public int speed; //速度public int agility; //敏捷public int X; //x轴坐标public int Y; //y轴坐标public boolean justorevil; //所属阵营public String name; //名称
    每一个生物体都有这七个基础属性,前三个属性用来描述生物体的战斗力。因为葫芦娃的特殊性,所以用枚举类型CalabashName和Color来描述他们各自的名称和颜色信息
    Classification包
    只有一个类,Classification类,用来描述生物体所属阵营。
    InitImage包
    只有一个InitImage类,用来把图片加载到GUI界面上。
    Thread包
    包含三个类,一个主线程类MainThread,一个子线程类MyThread,以及一个文件类。主线程类在开启战斗时创建,子线程类由主线程类创建,每一个子线程类对应一个生物体对象。文件类在复盘的时候创建。
    2.5 二维空间的产生每个方格都是拖拽在JavaFX界面上的矩形,组成了一个15×15的二维矩阵,在这个二维矩阵上覆盖了JavaFX中的canvas控件,用以获得二维矩阵的像素点坐标。
    2.6 运行流程有两种方式开启战斗,一种是当程序运行起来后,直接按下空格键选择默认阵型进行战斗,另一种是玩家自主选择合适的阵型,然后点击开始按钮进行战斗。
    摆阵

    2.6.1 自主择阵的方式下当玩家为葫芦娃选择一种阵型后,程序将进入阵型类执行,在具体的阵型中,会让葫芦娃“活过来”,并将他们安排在二维矩阵中每个个体应当处于的位置。位置信息用一个全局的地图表存储。该地图表为
    List<Creature> fieldlist
    地图表大小为225,每个元素代表二维矩阵中的一个格子上站立的生物体。该地图表初始化为“Empty”生物体,代表所有位置都没有生物站立。在具体的阵型中,当为葫芦娃或妖精排出阵型后,地图表对应位置原本的“Empty”类型生物体将被葫芦娃或妖精替换。
    当双方都排好阵型后,点击开始按钮,程序创建一个主线程,这个主线程执行两部分操作:第一部分是刷新战斗界面显示并将战斗画面记录下来,第二部分是为每一个生物体创建一个线程。这样会避免在每个生物体的线程中直接刷新界面造成线程不安全的问题。在每个生物体的线程中,他们将调用各自的move方法进行移动,倘若检测到自己的邻居有对手,双方将会一决雌雄。
    //在主线程的run方法里为每个生物体对象创建线程for(int i=0;i<fieldlist.size();i++){ if(fieldlist.get(i).name!="empty") { allThread.add(new Thread(new MyThread(mainCanvas,fieldlist.get(i),new ReentrantLock(),fieldlist))); }}for (Thread t:allThread) { t.start();}
    //在主线程的run方法里刷新界面并记录界面数据InitImage.setImage(mainCanvas.getGraphicsContext2D(), fieldlist);writeFile();
    胜负的判定来源于两个随机数,这两个随机数的取值范围上限是战斗双方战斗力的值。战斗力被描述为三种属性:力量、敏捷、速度。对于不同的生物体,这三种属性的初始值不同,并且战斗力并非是这三种属性的简单叠加,生成战斗力的计算方式为:战斗力 = a*力量 + b*敏捷+c*速度。a,b,c的取值与每个生物体的能力侧重有关,比如对于葫芦娃中的大娃来说,他力量初始值比别的要高一些,同时在战斗中,他的战力更多来源于力量,所以a的取值也会相对较高。生成两个随机数后,将会选取随即数大的一方作为获胜方,失败的一方将会变成“尸体”留在原地,同时作为路障拦在那里。
    程序中同时也设置了随机死亡与消失机制——生物体掉入对方之前设下的陷阱中,将有可能死亡并留下“尸体”,也有可能直接消失在地图中。而且,已经成为“尸体”的生物体也有可能消失在地图中,以此来保证战斗的结果永远不可能在新的战斗中复现。
    2.6.2 按下空格键开始按下空格键,程序将会为战斗双方都选择默认的长蛇阵,然后生成全局地图表开始运行。
    2.7 获胜判定对于两个生物体间的胜负判定不再赘述,这里主要介绍全局的胜负判定机制。
    由于葫芦娃阵营永远只有八个“生物体”,而妖精阵营有可能因为选择的阵型而导致生物体数目远远多于葫芦娃阵营。倘若葫芦娃生物体数目小于妖精阵营,为了平衡这种数目间的不对等,程序在双方阵营战斗开始之前对葫芦娃进行了一定程度的战力加持。
    在一场战斗中,若战斗双方生物体数目相同,则在三十秒之内,倘若一方全军覆没,则另一方获胜;若三十秒到达时,战斗双方都有生物体存活,则强制结束战斗,获胜方为“生物体存活数目相对占比较多一方”。比如葫芦娃一方还有两个生物体存活,妖精一方初始有15个生物体,最终还有三个生物体存活,则判定为葫芦娃获胜,因为葫芦娃存活数目占比为 1/4,妖精一方存活占比为 1/5.

    葫芦娃移动
    葫芦娃们永远是向前移动——除非碰到传送门。所谓传送门是指葫芦娃前方的“尸体”,只要葫芦娃们与妖精相遇并打败了对手,那么他们就可借助妖精身上残余的力量实现一次随机传送,随机传送总是趋向于把他们送到妖精的大本营中某一块空地。一旦葫芦娃走到妖精大本营的尽头,他就会在自己的“兵线”上从头巡视,如果这条兵线上仍有妖精的话。
    妖精移动
    蝎子精带领着妖精们“傻乎乎”的,只知道一股脑往前冲,遇到葫芦娃们,他们会与之决一死战,然后借着葫芦娃“尸体”上残余的神力完成一次随机传送。他们也是趋向于传送到葫芦娃的大本营中。妖精们不懂得寻找对手,他们永远只会呆在自己的兵线上不动——即倘若不传送,他们的y坐标不会改变。
    爷爷移动
    爷爷是葫芦娃阵营中重要而薄弱的一环,所以他总是在自己的大本营中来来回回巡视,除非有妖精冲破防锁,与他相遇,否则他一点危险也不会有。
    蛇精
    蛇精与爷爷移动方式基本相同,不过她不懂得绕开“尸体”。
    2.8 面向对象思想
    继承:Calabash,Grandpa,Monster,Snake,Scorpion,Empty这些类继承自Creature;阵型类继承自阵型主类
    封装:将setImage方法封装在InitImage类内部,对外部提供访问接口
    多态:设置阵型时,先生成具体生物体,然后将这个对象装入地图表中。地图表(fieldlist)是一个Creature主类的List,而每个对象是Creature类的子类。比如葫芦娃类(Calabash)继承自Creature

    fieldlist.set(0, new Calabash(...));
    三、优点与不足3.1 优点
    多阵型:程序为葫芦娃和妖精两方阵营提供了所有可能的阵型,玩家可自主选择阵型,在选择阵型后支持更换阵型
    平衡性:为了保证游戏的公平性,程序中对葫芦娃和妖精阵营的总体战力进行了平衡,以保证双方胜率大致相等
    不可重复性:每一局战斗都是独一无二的,几乎不可能有两局战斗会完全相同
    不可预测性:战斗的结果不可能被准确预测,即便某一方战至只剩一兵一卒,他也有可能翻盘获胜
    时间代价低:每局战斗限制在30s以内,等待战斗结束不会耗费太久

    3.2 不足
    趣味性有限:由于采用近战一决生死的方式,每两个生物体间的战斗结束太过迅速,趣味性较低
    文件读和写方法在不同包中实现:这里没有给文件读写一个比较合适的封装
    界面不够美观:原本应当给界面一个合适的场景,但因为设计上的一些缺陷,导致加载出的场景图片不能被很好的呈现出来。
    生物体属性不可见:每个生物体有他的能力侧重点,譬如大娃力大无穷,这些最终被描述为战斗力的一部分,但在观众看来,所有生物体都是以相同的速度随即进行战斗,界面上没能体现出生物体能力的不同
    文件路径不可选择:生成的记录文件被命名为test放在E盘下,如果玩家对自己磁盘进行了重命名或者本身没有E盘,程序运行将会出错

    四、实验中遇到的问题4.1 maven打包上的问题maven打包编译器的相关报错

    查看别人的工程中pom.xml文件,发现是自己没有在这个文件中指明编译器,添加以下内容后解决了问题。

    maven打包不可识别的符号

    当查看相关位置的字符后,发现是maven打包不支持中文字符,只好把工程中的中文字符全部改成英文。
    4.2 GUI显示上的问题debug过程中,单步调试运行,发现执行了move方法,地图上某个位置的生物体移动了位置,并且执行了相关的setImage方法,但界面上显示出来的仍然是初始的阵型。只有当测试方法执行完毕,所有生物体都移动一次后,界面上才会显示出最终结果。
    于是将每一次执行move方法后的地图打印出来,看到生物体确实移动了,但界面就是不刷新。这个问题令我百思不得其解,后来我的同学告诉我,在程序本身线程下,GUI界面就是这样显示的,只有所有动作结束,它才会刷新。
    4.3 开启多线程后的相关问题开启多线程后,遇到了很多无法查到解决方案的异常,一些是因为battle部分有问题到这错误,还有一些是多线程数据安全上的问题,其中有一个是JavaFX的Canvas相关报错。原因在于我给每个生物体对象的线程都传了这样一个参数 Canvas mainCanvas,他们用的是同一个Canvas,同一个Canvas只能得到一个二维像素点坐标,但多线程同时运行,会多次获得二维像素点坐标,所以导致了异常。
    解决办法:设置一个主线程,在主线程中开启子线程,mainCanvas只在主线程被调用,子线程只执行各个对象的move方法。
    4.4 Java语言相关问题字符串比较:对于字符串a和字符串b,c++中可以直接进行 a==b的判定,但在Java中没有重载该操作符,于是换用a.equal(b)来进行比较。
    五、参考资料
    理解Java特性之多态 https://www.cnblogs.com/chenssy/p/3372798.html
    Java的Integer和int的区别 https://blog.csdn.net/login_sonata/article/details/71001851
    JavaFX入门(二):JavaFX和FXML https://blog.csdn.net/theonegis/article/details/50181339
    Java中extends和implements的区别 https://www.cnblogs.com/sharpel/p/5859753.html
    使用maven来管理Java项目 https://www.cnblogs.com/tzyy/p/4768859.html
    Java中多线程 https://www.cnblogs.com/wxd0108/p/5479442.html
    JavaFX中的键盘事件 https://blog.csdn.net/alanzyy/article/details/48628991
    Java文件创建、删除、读写等操作 https://www.cnblogs.com/chen-lhx/p/5610678.html
    Java中多线程的测试和调试 http://www.cnblogs.com/wuyepeng/p/9733877.html
    深度解析runnable接口 https://blog.csdn.net/zxw136511485/article/details/53032658
    1 评论 1 下载 2019-04-18 19:36:59 下载需要7点积分
  • 基于C#实现的电影院售票管理系统

    一、引言1.1选题背景随着互联网和电子商务的快速发展,网上购物已经成了现代人生活中很重要的一种方式,如:数码产品、生活用品、化妆品护肤品等,只要是人们需要到的东西,基本都可以在网上购买。除了购买各种物品,现代人的生活也不再向过去一样单调,除了学习和工作之余,人们的娱乐生活也逐渐丰富,最普遍的娱乐休闲方式之一就是到电影院看电影,那么传统的电影订票窗口显然已经不能满足人们的需要了,所以开发一个电影院网上订票系统是非常必要和可行的。以前传统的电影票订票方法是去电影院的购票窗口查看电影的上映时间、场次、可选座位等信息再进行购票,人们往往需要排队才能买到电影票,这样不仅浪费了人们宝贵的时间,而且电影院工作人员的工作量也很大,对于这种低效率、浪费时间的事情,完全可以以网上购票的方式来改变。
    为了提高劳动的效率、节约成本、提高服务质量,我们小组开发了此款系统,用以方便影院的售票和客户的购买,通过这个系统,可以很快的实现会员注册、登录、购票,后台管理员可以新增影片、排片等基本操作。
    二、需求分析2.1 用户需求需要在网上购买电影票的用户可使用本系统,进行会员的注册登陆之后可以进行网上查询电影、购买电影票等操作,省去了去电影院实体窗口排队买票的繁 琐程序。
    2.2 系统功能分析
    新用户的注册、登录,用户数据能存在后台数据库中
    电影的录入、删除、查询、修改
    电影的排片
    管理员查询会员级别等信息
    会员查询影片信息
    会员购买电影票

    2.3 条件与限制系统可以实现一些基本的购票功能,但系统较简单,尚存在很多缺陷,不能实现完善和全面的功能。
    缺陷:

    首先要使用admin进行登录才能开始注册会员
    购票后无法查看购票信息
    不可以支持选座
    购票时不能通过搜索影片名字等来查找影片

    三、模块设计3.1 系统流程图系统流程图如图一所示:

    系统功能图如图二所示:

    3.2 系统使用指南对于用户
    首先,系统使用者先通过admin登录,进入到新用户注册页面,以管理员的身份为用户注册一个新的会员账号,已注册好的账号密码自动保存在后台数据库中,用户下次可以直接使用已注册的会员账号登录本系统进行电影的查询、购票等操作。
    对于管理员
    首先,管理员可以为新用户注册不同级别的会员账号、查看会员的信息。其次,管理员可在系统后台做电影的录入、删除、查询、修改等基本操作,除此之外,添加好影片后可以对电影进行的排片。
    四、数据库设计及实现4.1 系统E-R图E-R图如图三所示:

    4.2 逻辑结构设计(关系数据库设计)
    顾客(Cus、CusCard、CusType、CusTel(key))
    登陆(UserName、UsePwd、UserType)
    电影(Mname(key)、MBZ、MLanguage、MTYPE、MDirector、MACT、MTime)
    排片(MName(key)、MPrice、MRoom、Mcount、MTime、MBRQ、MERQ)
    上映(ID(key)、MName、MPrice、MRoom、MCount、MTime、MRQ)
    购票(PID(key)、Cname、GPCount、GPJE、MName、SYRQ、FYT、GPRQ、CZY)

    4.3 数据库主要代码及触发器会员信息
    CREATE TABLE [dbo].[Cus]( [CusTel] VARCHAR(50) NOT NULL PRIMARY KEY, [CusName] VARCHAR(50) NOT NULL, [CusCard] VARCHAR(50) NULL, [CusType] VARCHAR(50) NULL)
    触发器
    SET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOALTER TRIGGER [dbo].[InsertULogin] on [dbo].[Cus]after insertasdeclare @username varchar(50)declare @userpwd varchar(50)select @username=CusTel from inserted select @userpwd= right(CusCard,6) from inserted insert into ULogin values(@username,@userpwd,'C')
    购票信息
    CREATE TABLE [dbo].[GP]( [PID] VARCHAR(50) NOT NULL PRIMARY KEY, [CName] VARCHAR(50) NOT NULL, [GPCount] INT NULL, [GPJE] FLOAT NULL, [MName] VARCHAR(50) NULL, [SYRQ] VARCHAR(50) NULL, [GPRQ] VARCHAR(50) NULL, [CZY] VARCHAR(50) NULL, [FYT] VARCHAR(50) NULL)
    触发器
    SET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOALTER TRIGGER [dbo].[UpdatePP] on [dbo].[GP]after insertasdeclare @MName varchar(50)declare @SYRQ varchar(50)declare @count intselect @MName= MName,@SYRQ=SYRQ,@count=GPCount from inserted update MovieSY set mcount=mcount-@count where MName=@MName and MRQ=@SYRQ
    电影信息
    CREATE TABLE [dbo].[Movie]( [MName] VARCHAR(50) NOT NULL PRIMARY KEY, [MLanguage] VARCHAR(50) NOT NULL, [MDirector] VARCHAR(50) NULL, [MAct] VARCHAR(50) NULL, [MName] INT NULL, [MBZ] VARCHAR(255) NULL, [MType] VARCHAR(50) NULL)
    上映信息
    CREATE TABLE [dbo].[MovieSY]( [ID] INT NOT NULL PRIMARY KEY, [MName] VARCHAR(50) NOT NULL, [MRoom] INT NULL, [MCount] varchar(50) NULL, [MName] INT NULL, [MTime] VARCHAR(50) NULL, [MRQ] VARCHAR(50) NULL)
    排片信息
    CREATE TABLE [dbo].[PP]( [MName] VARCHAR(50) NOT NULL PRIMARY KEY, [MPrice] INT NOT NULL, [MRoom] VARCHAR(50) NULL, [MCount] INT NULL, [MTime] VARCHAR(50) NULL, [MBRQ] VARCHAR(50) NULL, [MERQ] VARCHAR(50) NULL)
    触发器
    SET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOALTER TRIGGER [dbo].[InsertPP] on [dbo].[PP]after insertasdeclare @MName varchar(50)declare @BRQ varchar(50)declare @ERQ varchar(50)declare @MTime varchar(50)declare @MRoom varchar(50)declare @MPrice intdeclare @MCount intdeclare @MBRQ datetimedeclare @MERQ datetimedeclare @RQ varchar(50)select @MName= MName,@BRQ=MBRQ,@ERQ=MERQ,@MTime=MTime,@MRoom=MRoom,@MPrice=MPrice, @MCount=MCount from inserted set @MBRQ=CONVERT (datetime,@BRQ)set @MERQ=CONVERT (datetime,@ERQ)while @MBRQ<=@MERQbegin set @rq=CONVERT(varchar(50), @MBRQ, 112)insert into MovieSY values(@MName,@MPrice,@MRoom,@MCount,@MTime,@RQ) set @MBRQ=DATEADD(DAY,1,@MBRQ)endCREATE TABLE [dbo].[MovieSY]( [UserName] VARCHAR(50) NOT NULL PRIMARY KEY, [UserPwd] VARCHAR(50) NOT NULL, [UserType] VARCHAR(50) NULL)
    五、系统设计与实现5.1系统开发环境
    硬件环境

    Intel Pentium 166MHz或以上;内存:需要至少512MHZ;
    软件环境

    运行于Windows2010版的操作系统之上;SQL Server2008数据库;Visual Studio2013;

    5.2 功能模块
    输入功能模块
    查询显示功能模块
    查询、售票功能模块
    登录、注册功能模块

    5.3 系统主要页面展示登录、注册页面

    登陆后的页面

    会员注册页面

    会员信息查询页面

    添加影片页面

    查询修改影片页面

    排片页面

    “排片”页面通过连接数据库,可以搜索影片的名字、导演、主演来选择影片,再输入票价,放映大厅,座位数,放映时间等信息,最后确认排片。
    购票页面

    购票页面通过对获得的已经被排片的电影进行选择而进行购票这一操作。在购票前需填入购票数量、联系电话,系统会自动计算合计金额。之后点击购票按钮则能进行购票操作。但一旦购票成功则不能进行退票操作,也不能查看所购买的电影票。
    数据库页面

    六、主要特色6.1 系统实用性系统首先可以进行新用户的注册,用户数据能存在后台数据库中。管理员可以进行电影的录入、删除、查询、修改,添加好影片后进行电影的排片。退出登录后再使用之前注册的会员登录,可以购买电影票。
    6.2 突出优势和特色(创新点)
    购票时可以看到影片的导演、主演等信息
    用户分为钻石用户,白金用户和普通用户,对用户进行了分类

    7 小结数据库技术课程设计是一次对课堂所学知识的灵活应用,是理论知识与实践的相结合。经过了一周的课程设计,本系统基本达到了当初的设计要求,设计上也基本合理。我们不仅对数据库系统的认识更加深入,同时也掌握了面向实体的系统分析的基本方法,对VS也有了新的认识,也知道了要有坚持不懈,不惧困难的精神,才能取得成功。一个简单的系统,每一个细节都需要在实践中去挖掘并进行进一步的修改完善。本次课设让我们受益匪浅,在分析问题以及解决问题等方面的能力有所提高,也是一次很好的同学之间交流合作的机会。数据库技术的用途很广,还有很多值得我们学习,希望今后能有更多这样的机会。
    3 评论 16 下载 2019-01-01 18:20:12 下载需要18点积分
显示 0 到 15 ,共 15 条
eject