分类

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

资源列表

  • 基于QT实现的简易计算器

    一、实验内容使用 Qt 设计并实现带用户界面的简易计算器。计算器支持运算的数据类型为整数和小数。基本功能和 Windows10 自带的简易计算器相似。支持基本的加、减、乘、除四则运算。还支持一些单目运算:开方,平方,取倒数和取相反数。另外,还有两个主要的控制功能:清除和退格。本次实验中,一些错误提示及单目运算的使用方法本人完全参照 Window10 自带的简易计算器。
    二、代码结构实现过程中没有添加新类,所有的功能都在 MainWindow 主类中实现。类中添加的属性和方法如表 1 所示:



    属性/方法
    功能、作用




    bool lastIsNum
    上一个输入是否为数字


    bool lastIsSinOpt
    上一个输入是否为单目操作符


    bool lastIsLBracket
    记录上一个输入是否为左括号


    bool expClean
    下一次输入是否需要清空 expression label


    bool inputClean
    下一次输入是否需要清空 input label


    int lBracketNum
    当前左括号数


    int index
    左括号和弹幕操作符的位置


    QStack operands
    操作数栈


    QStack operators
    操作符栈


    void inputNumver(int)
    输入数字,修改 input label


    void inputOperators(QString)
    输入操作符,修改 expression label


    void inputSingleOpt(QString)
    输入单目操作符,修改 expression label


    void numberPush()
    输入数字的运算操作(数字进栈)


    void operatorPush(char)
    输入操作符的运算操作


    void singalOptPush(char)
    输入弹幕操作符的运算操作


    void optPriority(char)
    运算符优先级


    double calculateExp(double,double,char)
    双目运算计算



    三、算法介绍本实验使用的算法为经典的带括号表达式求值算法,算法流程如下:

    首先初始化两个栈,一个 double 类型的操作数栈(operands),一个 char类型的操作符栈(operators)。并且为了简化之后的实现过程,将’#’压入操作符栈中
    初始化操作符的优先级




    操作符
    优先级




    #
    -1


    (
    0


    + / -
    1


    × / ÷
    2




    若 读 取 到 一 个 数 值 num , 则 直 接 将 其 压 入 操 作 数 栈 中 。operands.push(num)
    若读取到一个操作符,则分为三种情况:

    若是双目操作符(加、减、乘、除),则与操作符栈顶操作符 opt 的优先级进行比较,若 optPriority(opt) > operators.top(),则直接将该操作符压入操作符栈,否则弹出栈顶操作符,并且从操作数栈中弹出两个操作数 num2 和num1,进行计算 calculateExp(num1,num2,opt)。将计算结果重新压入操作数栈中。循环该步骤,直到 optPriority(opt) > operators.top(),将 opt 压入操作符栈中若是左括号,则直接压入操作符栈中。若是右括号,则不断弹出操作符栈中的操作符,与两个操作数栈中的操作数,进行运算,直到弹出的操作符为左括号为止。并将运算结果压入操作数栈若是单目运算,则其优先级一定是最高的,所以直接弹出操作数栈的栈顶操作数 num,进行运算 opt(num)。并将运算结果压入操作数栈中
    当所有的操作数和操作符读取完毕后,若操作符栈顶元素不为’#’,则弹出操作符栈栈顶操作符 opt,并从操作数栈中弹出两个操作数 num2 和 num1,进行计算 calculateExp(num1,num2,opt)并将结果压入操作符栈。循环该步骤直到操作符栈顶元素为’#’,则此时操作数栈中的数即为表达式运算结果

    四、UML 图如图 4-1 为计算器用例图,为用户提供的功能主要有:清除(进行下一次运算),退格(当前输入回退一格),操作数输入(输入小数,整数),双目运算(加、减、乘、除),单目运算(平方,开方,取倒,取相反数)。

    类图如图 4-2 所示,只有一个 QMainWindow 类,所有功能都在其中实现。

    五、信号和槽5.1 信号与槽函数的连接数字和小数点
    connect(ui.zeroButton, SIGNAL(clicked()), this, SLOT(numberZero()));connect(ui.oneButton, SIGNAL(clicked()), this, SLOT(numberOne()));connect(ui.twoButton, SIGNAL(clicked()), this, SLOT(numberTwo()));connect(ui.threeButton, SIGNAL(clicked()), this, SLOT(numberThree()));connect(ui.fourButton, SIGNAL(clicked()), this, SLOT(numberFour()));connect(ui.fiveButton, SIGNAL(clicked()), this, SLOT(numberFive()));connect(ui.sixButton, SIGNAL(clicked()), this, SLOT(numberSix()));connect(ui.sevenButton, SIGNAL(clicked()), this, SLOT(numberSeven()));connect(ui.eightButton, SIGNAL(clicked()), this, SLOT(numberEight()));connect(ui.nineButton, SIGNAL(clicked()), this, SLOT(numberNine()));connect(ui.dotButton, SIGNAL(clicked()), this, SLOT(dot()));
    将数字和小数点 button 的 clicked()信号与其对应的槽函数连接,若释放clicked()信号,则触发相应的槽函数。其槽函数都基本相同。
    双目操作符
    connect(ui.plusButton, SIGNAL(clicked()), this, SLOT(plus()));connect(ui.minButton, SIGNAL(clicked()), this, SLOT(min()));connect(ui.mulButton, SIGNAL(clicked()), this, SLOT(multi()));connect(ui.divideButton, SIGNAL(clicked()), this, SLOT(divide()));connect(ui.equalButton, SIGNAL(clicked()), this, SLOT(dengyu()));
    单目操作符
    connect(ui.sqareButton, SIGNAL(clicked()), this, SLOT(sqareButton()));connect(ui.sqrtButton, SIGNAL(clicked()), this, SLOT(sqrtButton()));connect(ui.reciprocalButton, SIGNAL(clicked()), this, SLOT(reciprocal()));connect(ui.negateButton, SIGNAL(clicked()), this, SLOT(negate()));
    括号
    connect(ui.leftParentheseButton, SIGNAL(clicked()), this,SLOT(lbracket()));connect(ui.rightParentheseButton, SIGNAL(clicked()), this, SLOT(rbracket()));
    控制 button
    connect(ui.CButton, SIGNAL(clicked()), this, SLOT(clean()));connect(ui.backButton, SIGNAL(clicked()), this, SLOT(back()));
    5.2 槽函数执行功能


    槽函数
    执行功能




    void numberZero()
    调用 inputNumber(0),修改 inputLabel


    void numberOne()
    调用 inputNumber(1),修改 inputLabel


    void numberTwo()
    调用 inputNumber(2),修改 inputLabel


    void numberThree()
    调用 inputNumber(3),修改 inputLabel


    void numberFour()
    调用 inputNumber(4),修改 inputLabel


    void numberFive()
    调用 inputNumber(5),修改 inputLabel


    void numberSix()
    调用 inputNumber(6),修改 inputLabel


    void numberSeven()
    调用 inputNumber(7),修改 inputLabel


    void numberEight()
    调用 inputNumber(8),修改 inputLabel


    void numberNine()
    调用 inputNumber(9),修改 inputLabel


    void dot()
    修改 inputLabel,添加小数点


    void plus()
    修改 expressLabel,并进行运算


    void min()
    修改 expressLabel,并进行运算


    void multi()
    修改 expressLabel,并进行运算


    void divide()
    修改 expressLabel,并进行运算


    void dengyu()
    逐次弹出操作符栈和操作数栈进行运算,直到栈空


    void sqareButton()
    修改 expressLabel,调用运算函数,进行平方运算


    void sqrtButton()
    修改 expressLabel,调用运算函数,进行开方运算


    void reciprocal()
    修改 expressLabel,调用运算函数,进行倒数运算


    void negate()
    修改 expressLabel,调用运算函数,相反数运算


    void lbracket()
    修改 expressLabel,添加左括号


    void rbracket()
    修改 expresslabel,并调用运算函数运算


    void clean()
    清空所有的栈,初始化变量,初始化 label


    void back()
    修改 inputLabel,删除字符串结尾元素



    六、实验亮点本次实验中,自己考虑到了几乎所有可能导致程序奔溃的情况,使得计算器对各种错误情况都能做出正确“回应”。项目有比较强的鲁棒性,具体表现如下:
    6.1 除数为 0 的情况

    除数为 0 的情况实现比较简单,只需要在每次进行除法运算时候判断一下其除数是否为 0 即可。假如除数为 0,则将 inputLabel 设置为“The number can’t be divided by 0”,将 expressionLabel 设置为空。并且清空操作符栈和操作数栈,并且恢复全局变量的初始值。
    6.2 负数开方情况负数开方情况与除数为 0 情况类似,也比较简单,只需要在进行开放运算时候判断被开方数是否为 0 即可。若被开放数位 0,则将 inputLabel 设置位“Invalid input”,同时清空操作符栈和操作数栈,并且恢复全局变量的初始值。


    6.3 没有输入左括号,无法输入右括号如表 1 所示,定义主类的一个 lbracketNum 属性,并且将其初始值设置为 0。每当输入一个左括号时候,执行 lbracketNum++操作将其的值加 1。之后,当输入右括号时,判断 lbracketNum 的值,若 lbracketNum 的值大于等于 1,则可以输入右括号,反之,禁止输入右括号。若可以输入右括号,则修改 expressionLabel 的值,将其末尾添加右括号,并且执行 lbracketNum—,将 lbracketNum 的值减 1。代码如下:
    void Calculator::rbracket() { if ((lBracketNum) && (lastIsRBracket || lastIsNum ||lastIsSinOpt)) { inputOperators(")"); operatorPush(')'); }}
    由条件不难看出,当且仅当 lBracketNum 不为 0,并且上一个操作是右括号或者数字或者但目操作符时候,可以输入右括号。反之,点击右括号无效。
    6.4 输入一个数字后,直接输入左括号,形如:3此处我的处理方式与 Windows 自带计算器的处理方式是统一的。出入数字后,紧接着输入一个左括号,不会发生错误,而是当下次再输入一个操作符时候,该数字会进栈并显示在左括号后面。


    6.5 连续输入两个双目操作符,第二个操作符将无法输入见 表 1 , 主 类 中 有 三 个 属 性 与 此 有 关 : lastIsNum,lastIsSinOpt 和 lastIsRBracket。当上一个输入是一个数字时,lastIsNum 为 true,当上一个输入是单操作符时,lastIsOpt 为 true,当上一个输入是右括号时,lastIsBracket 为 true。
    分析不难得知,双目操作符仅可能出现在数字,单目操作符或者右括号后面。所以,实现代码如下:(以加法为例)
    void Calculator::plus() { if (lastIsNum || lastIsSinOpt || lastIsRBracket) { inputOperators("+"); operatorPush('+'); }}
    6.6 在一个数字中输入大于等于 1 个小数点,第二个小数点将无法输入在输入小数时,若输入了一个小数点后,还能再次输入小数点,例如产生类似 1.234.564 这样的数,这显然是不合法的。所以在输入一个小数点后,必须禁止小数点的输入。我采用的方法是,在每次输入小数点时,都获取 inputLabel 中的 QString,并利用 split 函数将其以“.”分割成数组保存在一个 QStringList 变量中,若该 list 长度不为 1,则说明已经存在一个小数点了,那么禁止小数点的输入,反之可以输入,代码实现如下:
    void Calculator::dot() { QString nu = ui.inputLabel->text(); QStringList list = nu.split('.'); if (list.size() == 1) { nu.append("."); ui.inputLabel->setText(nu); }}


    6.7 右括号存在时,直接输入左括号无法输入,形如:()同样地,如表 1 所示,主类中的 lastIsRBracket 属性,lastIsSinOpt 属性,lastIsLBracket 属性都与左括号的输入有关。当且仅当上一个输入不是右括号且不是但操作符或者上一个输入是左括号时候,可以输入左括号。实现代码如下:
    void Calculator::lbracket() { if ((!lastIsRBracket && !lastIsSinOpt || (lastIsLBracket)) { QString exp = ui.expressionLabel->text(); index = exp.length(); exp.append("("); ui.expressionLabel->setText(exp); operatorPush('('); } lBracketNum++;}
    七、总结讨论首先感谢老师的教导。毫不夸张地说,这已经是我大学以来第三次做带有可视化界面的计算器了。但是这是第一次用 Qt 做该实验。第一次使用的是VS 的 MFC,第二次使用的是 Java 做的,第三次则是本次。因此,在完成本次实验后,除了编码能力的提升外,我觉得于我而言更重要的是对于不同的语言,不同的开发框架的深入理解与体会。当时用 Java 做的计算器具有更多的功能,包括运算的历史记录,用户自定义变量的使用等等。其界面可以说完全是“画”出来的,没有 Qt 和 MFC 这种便捷式的“拖动”控件形成界面。而 Qt 和MFC 之间,MFC 的优势在于不需要进行信号与槽函数的连接,自己设计好控件后,双击控件即可自动生成与之对应的代码函数,不需要做更多的声明。Qt 的优势在于,代码逻辑和结构更加清楚,非常符合传统的 C++项目的结构,是经典的面向对象编程结构。MFC 代码更多的是看起来“一团糟”,于我这种新入门的人而言,刚刚接触 MFC 时候是很痛苦的。
    另外,不足的是,由于自己在法国,所以耽误了一些课程,导致对于信号与槽的掌握不够熟悉,从而多定义了很多冗余的槽函数,本来可以使代码更简洁。项目完成后才发现这一点,于我而言也是一个经验和教训。
    1 评论 2 下载 2019-08-25 08:49:59 下载需要8点积分
  • 基于QT实现的图的遍历演示

    1 问题分析和任务定义1.1 问题描述很多涉及图上操作的算法都是以图的遍历操作为基础的。试写一个程序,演示在连通的无向图上访问全部结点的操作。
    1.2 基本要求以邻接多重表为存储结构,实现连通无向图的深度优先和广度优先遍历。以用户指定的结点为起点,分别输出每种遍历下的结点访问序列和相应生成树的边集。
    1.3 测试数据任选国内城市,起点为合肥,暂时忽略里程。
    1.4 问题分析及任务定义此程序需要完成以下操作:使用文件读取数据,以邻接多重表为存储结构,构成一个连通的图。用户输入一个起始点,从起始点开始对图分别进行深度优先和广度优先遍历。分别输出每种遍历下的结点访问序列和相应的生产树的边集。
    程序的执行流程如下图所示。

    1.5 实现本程序需要解决的问题
    如何利用从文件读取的数据构成无向图
    如何实现从用户指定的点开始遍历
    遍历完后如何输出遍历过得点的序列和边集
    如何利用图形化动态的演示遍历过程
    如何提高用户体验

    本程序的难点在于如何创建邻接多重表,和如何遍历所有城市。
    设图的结点20-30个,每个结点用一个编号表示(如果一个图有n个结点,则它们的编号分别为1,2,…,n)。通过输入图的全部边(存于数据文件中,从文件读写)输入一个图,每个边为一个数对,可以对边的输入顺序作出某种限制。注意,生成树的边是有向边,端点顺序不能颠倒。
    2 数据结构的选择和概要设计城市与城市之间是没有方向的,构成的无向图采用邻接多重表来实现,主要表示无向图的的各个边和结点,并通过对邻接多重表的操作实现对无向图的深度优先和广度优先遍历。创建AMLGraph类,其包含的属性和方法如下图所示。

    其中adjMulList是顶点数组指针,所用的顶点结点以顺序方式存储在一维数组中;vexnum和edgenum是无向图的结点个数和边个数;cityMap类是用来存储所有从文件中读取的城市名和坐标信息的类,points、dfs_str、bfs_str是用来存储遍历的城市的坐标和名称的变量。
    GreatAMLGraph函数用来构建邻接多重表,LocationVex函数通过城市名找到该城市在表结点的位置。AMLGraph、~AMLGraph函数分别是类的构造函数和析构函数,BFS_Traverse、DFS_Traverse分别是广度优先和深度优先遍历的函数。
    VexNode结构体是点结点(城市结点),name是城市的名称,firstEdge是EdgeNode类型的指针,指向该顶点的第一条边的结点。
    EdgeNode结构体是边结点,isVisited为标志域,用来标记该条边是否已被访问过;ivex为顶点域,指示依附于这条边的一个顶点的位置(序号);jvex指示另一个顶点的位置;ilink为链域,指向依附于第一条边的结点。
    3 详细设计和编码3.1 邻接多重表的建立
    从文件中读取所有城市名、坐标和线路信息。利用头插法建立邻接多重表。
    for (int i = 0; i < edgenum; ++i){ EdgeNode * e = new EdgeNode; e->isVisited = false; string v1, v2; inFile >> v1 >> v2; int a = LocationVex(v1); int b = LocationVex(v2); e->ivex = a; e->jvex = b; e->ilink = adjMulList[a].firstEdge; e->jlink = adjMulList[b].firstEdge; adjMulList[a].firstEdge = e; adjMulList[b].firstEdge = e;}
    3.2 深度优先遍历先调用DFS_Traverse函数,初始化visited[i]数组,再调用DFS(i),其中i为用户指定城市的索引。在DFS函数中,先访问表结点中第i个点,在判断该顶点的另一端是否被访问,如果没有访问,就在调用DFS(i->jvex),形成递归,直到访问到该顶点的另一端被访问过后,在访问与i相连的其它边,当与i相连的所有边都被访问完时,结束循环。
    while (e){ if (e->ivex == i) { if (!visited[e->jvex]) DFS(e->jvex); e = e->ilink; } else { if (!visited[e->ivex]) DFS(e->ivex); e = e->jlink; }}
    e为EdgeNode类型的指针变量。时间复杂度为O(n+edgeNum)。
    3.3 广度优先遍历先调用BFS_Traverse函数,初始化visited[i]数组,再调用BFS(i),其中i为用户指定城市的索引。BFS函数中,先访问表结点中第i个点,并将该点放入队列q中,在循环中不断取出队列的第一个元素,直到队列为空时,所以点遍历完成,结束该循环。取出队列第一个元素后,再判断该顶点的另一端是否被访问,如果没有访问,就遍历该点,并将该点放入队列,当队列第一个元素的所有相连的点都被访问过后,循环结束,再从队列中取出第一个元素,进行循环遍历。
    while (!q.empty()){ p = q.front(); q.pop(); EdgeNode * e = adjMulList[p].firstEdge; while(e) { if (e->ivex == p) { if (!visited[e->jvex]) { visited[e->jvex] = 1; //std::cout << adjMulList[e->jvex].name + " "; bfs_str.append(adjMulList[e->jvex].name.substr(0, 2) + "->"); q.push(e->jvex); } e = e->ilink; } else if (e->jvex == p) { if (!visited[e->ivex]) { visited[e->ivex] = 1; bfs_str.append(adjMulList[e->ivex].name.substr(0, 2) + "->"); q.push(e->ivex); } e = e->jlink; } }}
    分析上面算法,每个顶点至多进一次队列,遍历过程实质上仍是寻找每个顶点的邻接点的过程,因此时间复杂度和深度优先搜素一样。
    4 上机调试过程调试过程中,一方面是解决产生的bug,另一个面是调整文件中的数据,保证数据的合理性。
    点的名称及坐标

    无向图的边

    5 测试结果及其分析程序运行界面

    遍历演示


    本程序以图形化的形式演示,用户能够任意指定起始点开始遍历,遍历的结果也清晰可见。其中显示了两种遍历的城市编号的序列和遍历的边集。
    6 用户使用说明本程序是在windows7下的Qt creator5.4.1环境下编写的,因为Qt本身就是跨平台图形界面软件,所以本程序支持跨平台运行,支持的平台包括Windows、Mac OS、Linux等桌面操作系统。
    执行本程序后,窗口上回现实所有城市的名称、编号和路线,输入框中默认填写的合肥的编号,该输入框支持模糊查询,可以输入城市名称、城市编号或编号加名称。点击按钮“演示”后,会以文本形式显示遍历城市的序列,以图形化的方式,显示遍历的城市之间的连线。并且可以反复搜索遍历,具有良好的用户体验。在该可执行文件目录下的map.txt是本程序的所依赖的数据来源,文件中的数据可供用户或开发者调整修改。
    参考文献[1] 王昆仑, 李红. 数据结构与算法. 北京: 中国铁道出版社, 2006年5月
    [2] Kay_Sprint. 邻接多重表存储无向图以及有关操作[EB/OL]. http://www.2cto.com/database/201111/110964.html, 2011-11-13/2017-2-16
    2 评论 26 下载 2018-10-31 11:48:00 下载需要9点积分
  • 基于JSP和MYSQL实现的图书馆管理系统

    一、概述基于Spring + Spring MVC + MyBatis的图书馆管理系统,使用Maven进行包管理。主要功能包括:图书查询、图书管理、图书编辑、读者管理、图书的借阅与归还以及借还日志记录等。
    二、环境配置2.1 开发环境
    Windows 10
    IntelliJ IDEA 2018.3

    2.2 运行配置
    首先安装Mysql5.7,设置用户名为root,密码为123456,并保证其在运行状态,并执行library.sql文件导入数据
    然后再配置Maven到环境变量中,在源代码目录下运行
    # mvn jetty:run
    使用浏览器访问 http://localhost:8080 即可进入系统

    三、概念设计用户分为两类:读者、图书馆管理员。图书馆管理员可以修改读者信息,修改书目信息,查看所有借还日志等;读者仅可以修改个人信息、借阅或归还书籍和查看自己的借还日志。


    四、数据库E-R图
    五、逻辑设计共有6个表:
    5.1 图书书目表book_info


    字段
    类型
    长度
    小数点
    NULL
    用途





    book_id
    bigint
    20
    0

    图书号



    name
    varchar
    20
    0

    书名



    author
    varchar
    15
    0

    作者



    publish
    varchar
    20
    0

    出版社



    ISBN
    varchar
    15
    0

    标准书号



    introduction
    text
    0
    0

    简介



    language
    varchar
    4
    0

    语言



    price
    decimal
    10
    2

    价格



    pub_date
    date
    0
    0

    出版时间



    class_id
    int
    11
    0

    分类号



    number
    int
    11
    0

    剩余数量



    5.2 数据库管理员表admin


    字段
    类型
    长度
    小数点
    NULL
    用途





    admin_id
    bigint
    20
    0

    账号



    password
    varchar
    15
    0

    密码



    username
    varchar
    15
    0

    用户名



    5.3 图书分类表class_info


    字段
    类型
    长度
    小数点
    NULL
    用途





    class_id
    int
    11
    0

    类别号



    class_name
    varchar
    15
    0

    类别名



    5.4 借阅信息表lend_list


    字段
    类型
    长度
    小数点
    NULL
    用途





    ser_num
    bigint
    20
    0

    流水号



    book_id
    bigint
    20
    0

    图书号



    reader_id
    bigint
    20
    0

    读者证号



    lend_date
    date
    0
    0

    借出日期



    back_date
    date
    0
    0

    归还日期



    5.5 借阅卡信息表reader_card


    字段
    类型
    长度
    小数点
    NULL
    用途





    reader_id
    bigint
    20
    0

    读者证号



    password
    varchar
    15
    0

    密码



    username
    varchar
    15
    0

    用户名



    5.6 读者信息表reader_info


    字段
    类型
    长度
    小数点
    NULL
    用途





    reader_id
    bigint
    20
    0

    读者证号



    name
    varchar
    10
    0

    姓名



    sex
    varchar
    2
    0

    性别



    birth
    date
    0
    0

    生日



    address
    varchar
    50
    0

    地址



    phone
    varchar
    15
    0

    电话



    六、功能展示6.1 首页登陆
    管理者账号:123456/123456
    读者账号:10000/123456


    6.2 管理员系统6.2.1 图书管理
    6.2.2 图书详情
    6.2.3 读者管理
    6.2.4 借还管理
    6.3 读者系统6.3.1 查看全部图书
    6.3.2 个人信息查看,可以修个个人信息
    6.3.3 个人借阅情况查看
    3 评论 21 下载 2019-05-09 11:00:42 下载需要13点积分
  • 基于QT实现的多人在线对战的五子棋游戏

    一、程序背景计算机网络的迅速发展,对游戏领域产生了巨大影响。尤其是随着信息时代的来临,人们越来越趋向于网络游戏来进行竞技,于是,各式各样的网络游戏应运而生,例如腾讯游戏,网易游戏。在这些平台上,五湖四海的玩家可以随意地竞争与合作,适当的娱乐可以很好地纾解生活压力,为家庭、事业都有巨大的积极作用。因此,开发和架构这种轻游戏平台符合时代发展的需要。
    二、设计原理及要求2.1 设计原理本系统主要由四个子模块组成:五子棋游戏、网络通信、用户系统、积分系统。
    2.2 设计要求
    采用C/S模式架构,能够同时支持至少40对玩家
    服务器端提供游戏大厅、游戏桌等
    对战平台提供的游戏:五子棋或其它等

    三、程序介绍此程序主要分为两个部分:服务端和客户端。
    游戏服务端用于存储和转发信息,收到用户的登录、准备、落子等主动信号,进行相关的数据运算,并发送回用户。
    游戏客户端可通过服务端的IP地址发送连接请求,然后登录到游戏大厅,没有账号则需要先注册。在服务端中会维护一个session列表,即在线用户名单,还有所有游戏桌状态数组,保存所有游戏桌的人数情况以及准备情况。有人进入游戏桌、开始准备游戏、开始游戏、落子、游戏结束、离开游戏桌等,服务端都会收到相应的数据,此时服务器作为一个存储器和转发站,保存所有用户的游戏状态,并转发消息给相关的用户。用户排名也会显示在对应的客户端上,可自行查看排行榜。本游戏人数理论上无上限,可修改宏定义的数值参数来设置人数上限。
    四、程序的功能描述4.1 开始会话启动客户端时,便会尝试连接至服务器。连接成功后会在服务器的会话列表中新增一个封装过的session类,这个session类保存会话用户的信息,包括用户ID、用户账号、用户排名、用户状态等。
    处理完成后向客户端发游戏大厅中所有桌子的游戏状态,将有人的桌子改变颜色。
    所有的传输的数据格式都是XML,以便于提取数据。服务端和客户端皆有特定的函数专门用来处理传输的XML数据,同时用递归的方式解决了Socket带来的数据缓冲区问题,即多条数据会合在一起发送给客户端,导致有一定的几率分析失败,产生逻辑上的错误。
    4.2 注册服务器收到用户的注册数据,便开始提取数据中的有用信息,包括用户名、密码,可注册的话则为其生成一个唯一的用户ID,确保此用户为本游戏平台中独一无二的玩家对象。接着向存储的数据中添加新用户的记录,初始化积分。客户端收到注册成功的信号,获取用户ID,开始正常游戏。注册后可通过点击游戏大厅的用户名按钮,来查看所有用户的数量(因为新账号的排名就是所有用户数量)。
    4.3 登录在客户端,用户输入其账号密码后,服务器收到用户的登录数据,提取中数据中的用户名和密码,和用户列表中的数据进行配对。如果失败则返回错误提示。如果匹配成功,则在session列表中逐一比较,避免同一用户多设备同时登录,扰乱游戏积分系统。登录后返回用户的ID、昵称、积分等个人信息。
    登录成功后可通过点击游戏大厅的用户名按钮,来查看自己所在的位置,以及所有用户的账号与排名。
    4.4 进入游戏桌这需要登录后才能进入游戏桌。每个游戏桌分为两个座位,分别为黑子、白子,先进来的玩家默认为黑棋,后进来的为白棋。玩家加入游戏桌后,对应位置上的区域将会出现头像,表示这个位置有人,状态文字也变成“未准备好”,出现“准备完毕”的按钮。
    大厅的桌子预览图列表中白色的座位图标会变成黑色,并且上面显示玩家昵称。
    4.5 离开游戏桌玩家离开桌子回到大厅时,游戏桌上的玩家头像消失,大厅中的游戏桌预览图列表的黑色座位变回白色,表示这个位置的玩家已经离开了,其他玩家随时可上去与其他玩家开始游戏。如果在激战中退出离开游戏桌,确认后即视为放弃比赛,主动认输,另一方无条件获胜。
    4.6 准备开始游戏刚开始加入游戏桌时皆为未准备状态,需要手动点击“准备完毕”表示自己已经准备好,随时可以开始游戏。双方玩家都能看到对方的准备状态,以便于自己是否确认开始进行对战。双方都准备完毕时,收到服务器发送的对战信号,将马上开始对战。
    4.7 开始游戏准备完毕时,客户端向服务端发送准备的信号,当同一桌双方玩家都准备完毕后,立即开始比赛。客户端发送start信号给游戏桌的双方客户端,客户端进行游戏初始化,包括棋盘数据清空、棋盘控件清空棋子和更新界面、设置黑子先手、五子棋AI初始化、确定各自棋子的颜色,开始进行五子棋对战。
    4.8 落子游戏双方的每一次落子都会向服务器发送落子的信号,传输的数据中同时含有自身的信息,比如桌子ID、座位ID等,避免在数据传输的过程中丢包而导致的一些逻辑问题。服务端判断收到的用户座位是不是与保存的用户状态的列表中的数据一致,如果不一致则是有错误,需要进行纠错;如果没有错误就转发落子的数据给另一方的客户端,在客户端中模拟玩家进行落子。遇到错误后服务端会进行处理,并返回尽量减少错误损失的数据,交给客户端处理。
    4.9 放弃游戏在对战中关闭棋盘窗口,则视为放弃本次比赛,对方无条件获胜。退出的一方将会警告扣除积分,但并不会真的扣除。胜利方也不会获得奖励,避免刷分操作。离开后失败方的头像将会消失,原来的头像下方的文字变为“对方已放弃”,并且字体变灰。
    判断双方处在对战中的逻辑是同一游戏桌的两名玩家都已经准备完毕,服务端中有一个维护所有桌子座位状态的数组,0为没有玩家, 1为有玩家但没有准备好,2为有玩家并且准备完毕。如果双方都已经准备,则会立即开始比赛,所以两边都准备的情况下必定是正在比赛。
    4.10 游戏胜利客户端落子时发送数据给服务端,接着调用五子棋的AI判断自己是不是胜利。如果是胜利,则发送胜利的信号给服务端,在服务端中分析对战的双方局势,判断是不是真的胜利。最后一颗棋子也会发送给失败方,以便于两边的棋谱同步。确定数据无误后在服务端通过落子数量获得胜利者应该获得的积分,转发给胜利的玩家客户端。失败者也会获得少量的积分奖励,积分约为胜利者的三分之一。游戏结束后双方的准备状态清空,胜利方状态变为金色,失败方状态变为灰色,同时需要重新点击“继续下一局”按钮来和对方继续下一句对战。
    4.11 积分奖励每胜利一局,都会获得大量的积分,计入自己的总积分。服务端会进行累加,并保存到用户数据中。失败者获得少量的积分,也会累加到用户数据中,并保存到文件,实时保存积分以便于下次启动服务端时重新读取数据,同时也是避免了数据丢失。
    4.12 用户排名在服务端中维护了一个用户列表,是从数据文件中读取的。每一个用户列表中都有封装好的用户信息,其中就包括了用户的积分。其中每一个积分都对应了一个用户ID,解决了修改用户昵称和密码后引起的后患。通过QT的qSort函数来对用户信息按照积分进行排序,通过简单的算法添加了对积分相同的用户的排名保护。遍历所有用户的排名、昵称、积分,用一个QString来保存XML格式的数据,然后传输到对应的客户端。接着在客户端使用自己编写的字符串提取函数提取出所有用户的排名、昵称、积分,使用制表符显示在一个小型对话框里面。客户端用户也可以在排行榜中看到自己所处的位置。
    五、系统的ER图下图是简易的ER图大纲:

    下图是完整的ER图以及各个子模块之间的联系:

    六、程序详细设计及主界面呈现6.1 游戏大厅界面启动程序后就建立了与服务端的连接,开启一个session,每个客户端被赋予一个独一无二的sessionID。服务器维护一个经过封装的会话list,通过sessionID来判断是哪个客户端。封装后的sessionInfo类,包含了sessionID、用户昵称、用户ID、游戏桌位置等,并且在和客户端进行通讯时判断用户的位置是否正确,如果有误则进行纠错处理,将系统损失降低到最小。
    启动时进入了游戏大厅,此时会显示所有游戏桌的状态。在客户端与服务端连接成功后,服务端就向客户端发送了特定的数据包。这段数据中涵盖了当前所有游戏桌的人数状态,采用简短的XML格式,包括每个游戏桌座位上的用户昵称、ID、准备状态等。

    如上图,每个游戏桌可分为桌子、两边的座位。每个座位默认是白色,表示没有人在这里。一旦有人进来,服务端会发送所有游戏桌的数据给所有在线的用户,客户端SocketUtil类收到数据,通过判断里面的kind关键字来分发数据给对应的窗口对象,游戏大厅窗口收到数据进行处理,座位变成黑色,表示已经有人了,同时在座位的上方打印玩家的昵称,用来辨认。上图中的第二张游戏桌的左边位置已经变黑,座位上面还显示了玩家的昵称helloLisa。
    如果没有登录,左上角只有一个“未登录”按钮,再进行任何操作例如点击登录按钮、点击游戏桌等,都会强制要求跳转到注册/登录界面,登录成功后才能继续操作。登录后的样子如下图所示。

    原先的“未登录”按钮变成了用户的昵称,而后面多了两条信息,分别表示用户的当前积分、个人排名。积分是在游戏后才能获得,而排名则是代表当前用户在所有用户之中的排行。相同积分的用户排名相同,而不是简单的按照名称排序。点击用户名按钮,可以看到所有用户的积分排名。

    点击用户名按钮,客户端发送查看排行榜的请求给服务端,服务端收到数据后访问所有用户,进行排序,并且将客户端需要的数据发送给客户端,包括排名、昵称、积分等,同样是封装成XML数据,客户端收到后弹出MessageBox对话框,读取所有用户排名,展示给用户看。
    点击任意一张桌子,都可以进入游戏等待界面。
    6.2 注册登录界面在游戏大厅中进行任何操作,如果没有登录的话,会强制要求跳转到注册登录界面。注册登录界面略显简陋,由两个垂直排列的对话框和两个水平排列的按钮组成,分别是账号、密码、注册、登录。

    注册时,如果用户昵称已经存在,则会提示注册失败,重新换个名字。因为用户名代表了这个用户的个性化,早点注册的用户有更多的可能获取到自己喜欢的用户名。

    登录时,如果用户已经在其他设备登录,则不允许再次登录。这是为了避免多人用同一个账号刷积分,保障积分系统的公平性与稳定性。

    如上图,一个客户端用helloLisa账号登录,而第二个客户端也尝试用同样的账号登录,这时就会返回登录错误,要求用户更换账号。
    注册成功后,通过MessageBox弹出“注册成功”的话语,并且提示用户牢记自己的账号和密码。

    登录成功后,通过MessageBox弹出“欢迎回来”字样的对话框,接着自动关闭登录窗口。

    登录后,游戏大厅会显示用户的信息,用户昵称、用户积分、用户排名。

    6.3 五子棋界面该界面由三部分横向的布局组成:左边是用户一的头像、状态、操作按钮,中间是五子棋棋盘的控件,右边是用户二的头像、状态、操作按钮。

    头像分为黑白两色,黑色表示该用户使用的是黑棋,白色则相反,代表白棋。头像下方的标签显示了用户的状态,刚进入游戏桌时默认为“未准备好”,而且状态标签下方的按钮会显示“准备完毕”。点击准备完毕后,“未准备好”标签变成醒目的红色,文字也变为“等待开始”。而原先的按钮文字变成“取消准备”。在游戏开始之前随时都可以取消准备,变成原先的状态,不会有任何影响。

    同一个游戏桌对面用户,不会显示准备按钮,因为无法干预对方的准备状态。而对面如果没有人,则不会显示头像,直至对手进入游戏桌,自动显示头像。


    当对手进入游戏桌后,黑色的“未准备好”字样变成了暗黄色的“未准备好”,用以提示己方用户已经有人进来了,改准备开始游戏了。等对方准备完毕,则立即开始游戏,不再给予等待时间。

    按照五子棋的规则,黑棋先下,即代表头像为黑色的用户一方先落子。用户可以用鼠标点击棋盘的任意一个位置,如果范围超出了棋盘或者已经落子,那么该次点击不会有任何效果,相等于没有进行操作。用户每下一个子,都会发送数据给服务端,由服务端进行保存状态和转发给对面的玩家。对面玩家在收到落子信号前不能进行任何的落子操作。

    对面玩家的客户端收到了都成服务器的<KIND>moves</KIND>文本信号,客户端在数据中提取出落子位置,游戏桌ID、用户座位、棋子横坐标、棋子纵坐标,然后由程序模拟对方用户进行自动操作。落完子后进入下一回合,刚落子的用户便无法操作,需要等待对方落子,继续由服务器进行转发落子信号,在自己的棋盘上落子。这样来回循环直至游戏结束。

    游戏胜利,由胜利的一方的客户端发送数据给服务端,然后服务端根据双方的状态判断用户端是否是真的胜利,若有错则进行纠错处理,发送逻辑出错的error信号。目前五子棋稳定性极高,还没有遇见过出错的情况。

    获胜的一方可以获得大量的积分奖励,游戏胜利后弹出对话框显示结果,并且显示奖励的积分数量。同时原先的头像下方的状态标签变为暗金色的“胜利”二字,对面失败者的状态标签则变成“失败”,并且是惨淡惨淡的灰色。

    失败者也会受到安慰的少量积分,大概是胜利者的三分之一不到。
    游戏结果出来之后,双方各自的操作按钮会继续显示,按钮文字变成“准备下一局”。点击此按钮则重新进入准备状态,红色的“等待开始”,可以进行下一局。
    此时如果有一方离开了游戏桌,那么其状态标签会改成灰色的“对方已离开”,头像也一起隐藏起来。

    除此之外,如果是在游戏途中离开,会询问“当前您正在对战,如果现在退出将会默认对方胜利,并且扣除自己的积分。是否继续?”。点Yes会退出游戏桌,惨淡收场。

    玩家中途退出后,剩下的一方将会立即获得胜利,但不会奖励积分。这是为了避免刷积分引起的操作。放弃掉的一方头像消失,状态变成“对手已放弃”。胜利者恢复到比赛开始的“未准备”状态,等待继续准备。

    6.4 服务端界面服务端主要用于数据存储和数据传输,无需手动干预,故界面仅由两部分组成,一是日志控件,二是调试控件。
    服务端在启动时会读取文件中存储的用户信息:用户ID、昵称、密码、排名,依次添加进一个封装后的UserInfo类,并且使用List构成一个用户数组,包含了所有的用户信息。打印用户数量至日志控件。

    在日志控件下面,是一排水平放置的调试功能的空间布局,分别是游戏桌ID、座位号、发送的数据文本。这片布局仅仅用来开发时进行调试,根据传输的数据和客户端的反应来判断逻辑有没有出错。目前已经完成了其调试的使命,暂时不需要理睬。
    在服务端界面中,可以看到所有传输的数据,例如下图新的客户端进入与登录的XML格式的数据。

    如上图,客户端启动后设置唯一的sessionID给客户端,并且保持连接。同时获取到所有游戏桌的玩家状态,用<T>标签包裹起来,返回给客户端,最后到客户端的游戏大厅中刷新,有人的座位变黑,并且显示相应位置的玩家昵称。
    在注册/登录后获取到账号密码,与用户列表中的信息进行匹配,如果登录成功就返回1,登录失败则返回0。图中的数据是登录成功,于是在<KIND>login</KIND>XML文本中标记了RESULT为1,并且添加上了存储在服务端的用户信息返回给客户端。客户端读取后显示在自己的界面之上展示给用户。

    上图是双方玩家准备完毕后的收发的数据,所有文本皆显示在上面。一个玩家准备,发送结果给游戏桌对面的玩家。如果双方都已经准备好了,则发送<KIND>start</KIND>内容给双方,开始游戏。每一个落子的信号也是封装在一个<KIND>moves</KIND>``文本之中,同时这串文本带有落子的位置,服务端收到落子信号后转发给游戏桌对面的玩家,对面玩家客户端收到信号后游戏界面存放五子棋数据的数据设置相应的位置为对方棋子,然后进入下一轮,即对方落子。接着对方也发送信号过来,继续互相传输落子数据,直至游戏结束。
    七、代码实现此处应该略过,毕竟是 gayhub,自己找源代码吧,注释很多的。
    八、总结总的来说,这次课程设计是做得最顺利的一次了……老师布置了题目之后,就在光棍节的那一天,那一个周末,我闲得无聊,就开始着手做课程设计了。既然都要做,那么就不如直接选个最有趣的吧,于是一眼就看中了棋牌类在线游戏平台。虽然觉得是最难的……但试试又无妨。
    以前已经做了好几次的五子棋,分别是安卓、C、Java,这次恰逢好机会就打算用C++做一个图形化的。而且刚好有空,便开始新建工程。
    虽然有些小意外,比如莫名失效的信号槽机制,我就调试了6个多小时……而且没有任何进展。不过这个应该不是我的锅吧。还有Session,怎么在刚开始连接的时候就知道是哪个用户,连接时好像并不能传送数据,总不能等到传送数据时再进行维护吧,于是想了一会儿,就干脆在新连接进入时增加了一个sessionID,还封装了一个SessionInfo类来进行维护会话列表。这些都很顺利,轻车熟路了。而且QT对Socket的封装真的特别特别友好,多线程什么的完全不需要我考虑,只需要把功能实现就行了。也不会产生阻塞、溢出什么的,实在是太省心了。
    然后一不小心,就差不多完成了。
    多人在线五子棋平台最麻烦的地方就是调试了,服务端只能一直写一直写,写好后再统一运行,出了错误也得重新打开两个客户端慢慢调试。因为debug机制只能调试一个客户端,干脆就直接在服务端输出调试的日志了,显示数据传输的所有内容,顺带还打印log。
    再加上了伪造发送数据的调试控件组,就是手动写数据发送给客户端,这样就能知道客户端的状况,以及跳过了数据发送出现的更加容易出现bug的隐患。这个调试的方法应该算是比较新颖的吧,而且确实非常好用呢。
    时间比较仓促,因为还有好几个项目要赶,就花了匆匆的两天时间大概24小时吧。除去无用的找框架bug的时间,粗略估计约15个小时。没有好好地修改界面,看起来界面会比较丑,但是无伤大雅,能看就好。等以后有空了再好好地修改界面,让它能够发布出去,给其他人尝尝鲜。
    虽然除了QT的socket用法以外,我并没有学到多少其他知识,但是也增加了一些开发经历,算是有不小的收获吧。
    九、服务器服务端主要用于数据存储和数据传输,无需手动干预,故界面仅由两部分组成,一是日志控件,二是调试控件。
    服务端在启动时会读取文件中存储的用户信息:用户ID、昵称、密码、排名,依次添加进一个封装后的UserInfo类,并且使用List构成一个用户数组,包含了所有的用户信息。打印用户数量至日志控件。

    在日志控件下面,是一排水平放置的调试功能的空间布局,分别是游戏桌ID、座位号、发送的数据文本。这片布局仅仅用来开发时进行调试,根据传输的数据和客户端的反应来判断逻辑有没有出错。目前已经完成了其调试的使命,暂时不需要理睬。
    在服务端界面中,可以看到所有传输的数据,例如下图新的客户端进入与登录的XML格式的数据。

    如上图,客户端启动后设置唯一的sessionID给客户端,并且保持连接。同时获取到所有游戏桌的玩家状态,用<T>标签包裹起来,返回给客户端,最后到客户端的游戏大厅中刷新,有人的座位变黑,并且显示相应位置的玩家昵称。
    在注册/登录后获取到账号密码,与用户列表中的信息进行匹配,如果登录成功就返回1,登录失败则返回0。图中的数据是登录成功,于是在<KIND>login</KIND>XML文本中标记了RESULT为1,并且添加上了存储在服务端的用户信息返回给客户端。客户端读取后显示在自己的界面之上展示给用户。

    上图是双方玩家准备完毕后的收发的数据,所有文本皆显示在上面。一个玩家准备,发送结果给游戏桌对面的玩家。如果双方都已经准备好了,则发送<KIND>start</KIND>内容给双方,开始游戏。每一个落子的信号也是封装在一个<KIND>moves</KIND>文本之中,同时这串文本带有落子的位置,服务端收到落子信号后转发给游戏桌对面的玩家,对面玩家客户端收到信号后游戏界面存放五子棋数据的数据设置相应的位置为对方棋子,然后进入下一轮,即对方落子。接着对方也发送信号过来,继续互相传输落子数据,直至游戏结束。
    1 评论 1 下载 2019-08-22 07:13:46 下载需要13点积分
  • 基于C++的银行存储管理系统

    一、实训目的通过课程设计,学会运用数据结构知识,针对具体应用,自己设计合理数据结构,确定存储结构,并能设计具体操作算法,选择使用具体语言进行实现。掌握C++较复杂程序的组织和设计过程,调试技巧。学习解决实际问题的能力。
    二、实训环境计算机windows xp或其它版本,VC6.0或更高版本,或其它语言环境。
    三、实习题目小明是一个计算机专业top student,祝贺他毕业了。并准备到银行参加工作。上班第一天,经理叫他编制一个实现一个活期储蓄处理程序,算作考查。上班第一天,一定要给领导一个好印象,小明二话没说,就答应了。现要你是小明了,请完成如下题目功能。储户开户、销户、存入、支出活动频繁,系统设计要求:

    能比较迅速地找到储户的帐户,以实现存款、取款记账
    能比较简单,迅速地实现插入和删除,以实现开户和销户的需要

    四、问题分析
    创建文件,用数组形式存储用户数据,开户时自定义用户账户、姓名、密码、开户金额
    用户登录时,输入正确的用户姓名、用户密码,完成登陆后即可进行存款、取款、查询、修改密码
    实现输入用户的账户名和密码,将其全部信息删除,进行销户

    五、逻辑结构和存储结构设计存储结构设计:该存储结构是链式存储结构,本系统主要用线性表结构类型来存储在“活期储蓄账目管理系统”中的信息。其中,结构体由4个分量构成:用户账号名、用户姓名、用户密码、开户金额。
    六、算法设计本系统采用链式结构存储储蓄账目管理。

    用户输入想开户的储户输入其姓名账户密码,然后显示开户成功,会有一个账户生成,然后开户成功
    用户登录需要输入账号名和密码,判断密码是否正确,如果错误则返回,然后点击登录,就可以进入管理系统
    用户的存取款和查询余额,首先在登录账户的基础上,选择存或取款,然后输入相应的金额,若是取款应判断其金额是否小于账户上的金额,如果不小于,则提示储户重新输入相应的金额,或者退出。 4、储户需要销户的账户,然后程序自动判断该账户是否存在,然后输入账户密码,若密码与账户相对应,则删除该账户

    七、空间复杂度和时间复杂度分析7.1 空间复杂度是程序运行所以需要的额外消耗存储空间,一般的递归算法就要有o(n)的空间复杂度了,简单说就是递归集算时通常是反复调用同一个方法,递归n次,就需要n个空间。
    7.2 时间复杂度一个算法花费的时间与算法中语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多。一个算法中的语句执行次数称为语句频度或时间频度。记为T(n)。
    一般情况下,算法中基本操作重复执行的次数是问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),使得当n趋近于无穷大时,T(n)/f (n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。记作T(n)=O(f(n)),称O(f(n)) 为算法的渐进时间复杂度,简称时间复杂度。
    在各种不同算法中,若算法中语句执行次数为一个常数,则时间复杂度为O(1),另外,在时间频度不相同时,时间复杂度有可能相同,如T(n)=n2+3n+4与T(n)=4n2+2n+1它们的频度不同,但时间复杂度相同,都为O(n2)。
    八、效果截图主界面

    开户界面

    账户信息界面


    退出界面

    九、收获和体会及不足这是第二次做课程设计了,但开始自己读题时,思路不是很清晰,也不明白这个程序就是要结合所学数据结构的思想,要首先定义了多个数组,包含了储户的各类信息,如姓名,账号,金额等等,并利用文件将用户信息加以存储。开户时将输入的信息写入文件,存款和取款时对用户的存款加以处理并更新到文件中。
    最后在分析好活期储蓄帐目管理的功能模块后,我开始编写代码,在代码调试的过成中,并不是一帆风顺的,这也是平常练的不够吧,经过几轮删删改改,终于做的差不多了,基本达到了老师的要求,但是还是有很多不足的地方。通过这次对银行管理系统的编写与调试,巩固了有关结构体的知识及其操作,锻炼了实际应用能力,同时对文件有了更进一步的了解及应用,明白文件的读写等相关知识。
    在不断地进行书面设计和上机调试的过程中,认识到掌握设计程序的思路非常重要,要正确处理算法与语法的关系,算法是程序的核心,是灵魂,语法是外壳是工具。但是光掌握语法也是不够的,应该还要把重点放在解题思路上。这个实验也让我认识到自己知识的有限,与同学讨论的益处,培养了我的团队意识。
    在这一周的课程设计中,我通过这次设计我也着实又感受了一次编程的乐趣,从中也学到了不少知识。书上和老师都说“程序=数据结构+算法”,但我在学习运用数据结构编程之前,并没能深刻体会到这一点,直到这次课程设计实践。
    我感受最深的一点是:以前用C编程,只是注重如何编写函数能够完成所需要的功能,似乎没有明确的战术,只是凭单纯的意识和简单的语句来堆砌出一段程序。现在编程感觉完全不同了。在编写一个程序之前,自己能够综合考虑各种因素,
    首先选取自己需要的数据结构,是树还是图或是别的什么?然后选定一种或几种存储结构来具体的决定后面的函数的主要风格。最后在编写每一个函数之前,可以仔细斟酌比对,挑选出最适合当前状况的算法。另外,我还体会到深刻理解数据结构的重要性。只有真正理解这样定义数据类型的好处,才能用好这样一种数据结构。了解典型数据结构的性质是非常有用的,它往往是编写程序的关键。
    通过这次课程设计逐渐提高了自己的程序设计和调试能力,通过上机实习,严整自己设计算法的正确性,学会了有效利用基本调试方法,查找出代码中的错误并且修27 改,我对参数的调用也进行了很多种尝试,已经能够相对准确的选择合适的参数形式来实现函数之间的数据传输交互了。
    这次试验也让我看到了自己的不足,许多关于C++的一些比较具体的东西还不太懂,比方说指针。这次课程设计也让我知道了自己的能力只有经过不断地锻炼才能提高。
    0 评论 2 下载 2019-08-19 10:11:48 下载需要5点积分
  • 基于QT的网络对战中国象棋游戏

    一、绪论1.1 项目开发意义看不惯现如今,无论是电脑端,还是手机端,亦或者是其他设备平台的上软件,无论是游戏、还是效率类的 App,均都充满着商业气息,至少启动页面都是带着广告,亦或是带着内置充值付费的内容,让使用者感觉不悦。个人对于中国传统的文化的爱好、编程的喜爱、开发的喜爱、逻辑思维的游戏喜好,外加因为毕业需要,和充分锻炼自己的编程开发的思维,提升自己的能力和其他方方面面。所以决定开发出这个基于 QT 的跨平台网络对战中国象棋游戏。
    在该中国象棋运行程序,充分的考虑到了缺少玩家、缺失网络和没有足够多的电子设备等情况,开发出了多个游戏策略模式,以应对不同情况的需求。且程序最大优点是免安装,操作简单,主流操作系统基本全面覆盖。也没有广告或者付费充值等内容。只做好简单的下棋游戏功能,让玩家感觉到一个开发者的无私的热情和中国象棋爱好者的那一份单纯的情怀。
    1.2 项目方案1.2.1 设计方案一尽量采用最稳定最新的开发工具进行编程。确保运行程序的稳定性和用户体验。其次是便于软件的开发和后期维护、简化流程。软件开发层次考虑:首要是尽量考虑采用最稳定的、其次再是最新的开发环境。这样充分保证软件开发出来之后的稳定性,以用户体验作为最大标准。且考虑到开发周期和变卡的便捷性。所以在编程开发的采用 Windows 10(专业工作站版本)、Ubuntu 16.01.5 (LTS)、 MacOS10.14、 IOS 11.4.1、 Flyme 7.1.5.0A(最新的稳定版、 基于 Android 7.0)、 Qt Creator 5.9.7(LTS)、Enigma+Virtual+Box+7.80。
    大概设计思路如下:
    利用一些 Qt Creator 提供的一些默认组件,来进行象棋和棋盘的绘画,对于象棋的每一个棋子的走棋规则,就利用网上的提供的一些库函数来进行调用。最后添加一些贴图来进行界面的美观美化。然后象棋的基础信息就存储在SQL Server 2012 创建的表里面。且开发一个类似于微信的聊天界面,用以玩家进行通信。且将所有的消息和注册用户的相关信息全部都存储到 SQL Server 2012 的数据库表里面。 最后将这个中国象棋界面和独立的聊天界面合二为一,当编码完成之后,进行多个系统平台的发布。
    1.2.2 设计方案二尽量采用最稳定最新的开发工具进行编程。确保运行程序的稳定性和用户体验。其次是便于软件的开发和后期维护、简化流程。
    软件开发层次考虑:
    首要是尽量考虑采用最稳定的、其次再是最新的开发环境。这样充分保证软件开发出来之后的稳定性,以用户体验作为最大标准。且考虑到开发周期和变卡的便捷性。所以在编程开发的采用 Windows 10(专业工作站版本)、Ubuntu 16.01.5 (LTS)、 MacOS10.14、 IOS 11.4.1、 Flyme 7.1.5.0A(最新的稳定版、 基于 Android 7.0)、 Qt Creator 5.9.7(LTS)、Enigma+Virtual+Box+7.80。
    大概设计思路如下:
    主要是使用 Qt Creator 的 C/C++功能,基本不使用控件拖拽,尽量使用手写代码生成、熟悉面向对象编程。提高手写代码能力。去掉数据库存储数据和为玩家相互聊天的功能。全程基本只采用 Qt Creator 独立开发。
    1.2.3 设计方案对比设计方案一重点在于开发工具的最新版本的使用, 和将数据存储在数据库里面,主要是使用 Qt Creator 和 Visual Studio 和 SQL Server 的融合使用。在不通的层次使用不同的开发工具。和加强熟悉多种工具的融合,提升整体的技术。
    设计方案二则是重点在于开发工具的稳定。但是对于数据的存储采用数据结构的方式, 棋子界面和棋子采用最基本的系统 API 进行绘画。关键熟悉面向对象的开发方式。
    综上两者比较,我就决定采用设计方案二。主要理由如下。使用最新版的工具开发,有时候会遇到许多问题,或者莫名的异常警告不通过。而又难以在网上搜寻到合适解决方案,导致项目搁浅。且尤其是采用 Visual Studio + QtCrator 开发,其中还需与多余的环境设置与修改,比较复杂;且 Qt Creator +SQL server 的开发,没有提现成的驱动,需要自己下载源码编译,也不容易编译通过。且上面这种配合方式我几个月前试过了,操作复杂不易实现。且本就是写 C/C++的项目。想着熟悉一下数据结构的知识,看着能不能自学之后达到融会贯通的地步。 且还相比前者方案,也更加便于软件的开发和项目的稳定性。赢得玩家好感。
    1.3 项目成品运行环境Windows 平台、 Linux 平台、 MacOS 平台、 Android 平台、 ios 平台等。以上平台只要是目前常用的主流系统平台,都可以运行;且之后也都是了解了一下,就连一些小众化的平台,比如 Windows CE/Mobile、嵌入式 Linux(Embedded Linux)、 Symbian、 Maemo、 MeeGo 等平台,只要设置好相关的环境之后进行编译生成,都可以基本无需要修改源码、 都是可以直接运行在其相关的平台上面。受限于硬件和软件设备和其他的环境限制, 外加时间和个人精力有限,使得无法在所有版本平台均测试运行。但是在主流的系统平台上面,均做了项目产品的演示和生成,且所有的参与测试的平台,均测试成功。其他小众环境平台,若有相关的需求,可以参考开发帮助文档和开源社区的帮助。 最后结果均运行对应程序时候,都无需安装特殊的运行库或者指定环境才能运行。且电脑端的中国象棋游戏程序均无需安装, 为绿色版, 点击即可以运行。
    1.4 开发工具介绍Qt Creator:一个跨平台集成开发环境(IDE),可为多个桌面,嵌入式和移动平台创建C ++和QML应用程序。它附带一个代码编辑器,并与用于在整个产品生命周期中设计,编码,测试,部署和维护软件的工具集成在一起。
    Enigma+Virtual+Box+7.80: 软件虚拟化工具,它可以将多个文件封装到应用程序主文件,从而制作成为单执行文件的绿色软件。它支持所有类型的文件格式,虚拟化后的软件不释放任何临时文件到您的硬盘,文件模拟过程仅在内存运行。
    VMware Workstation Pro:提供用户可在单一的桌面上同时运行不同的操作系统,和进行开发、测试 、部署新的应用程序的最佳解决方案。
    1.5 可行性分析1.5.1 技术可行性基于QT的跨平台网络对战象棋项目,采用Qt Creator作为界面设计和功能设计。且Qt提供的良好的封装机制使得Qt的模块化非常高,可重用性良好;还有着丰富的API接口提供和大量的开发文档提供支持[1],更是有全球性和中文的开源社区。数据方面采用数据结构和STL和容器来存储,无需额外使用数据库;亦是减少发布时候的复杂度。且自己自从大一开始,到现在,多次反复的学习和研究C/C++相关的知识,基本功比较扎实,加上具有良好的自学能力、沟通能力、和自己用Baidu和Google来解决开发过程遇到的出错和技术上的问题。另外还有指导老师可以依靠和寻求帮助,开源社区热心网友也有技术支撑。综上所述,所以在技术可行性是可行的。
    1.5.2 操作可行性基于QT的跨平台网络对战象棋的项目,其完全是根据不会怎么操作电脑和手机平板的象棋爱好者提供的量身定制,在运行流程上十分的简便清晰。且该运行程序的用户设计比较友好,功能分明,操作简单,且提供了相应的弹窗提示。综上所述,所以在操作可行性是可行的。
    1.5.3 法律可行性基于QT的跨平台网络对战象棋的项目,本就是自己对于毕业设计从决心开发的一款游戏,一开始就是想着只是提供象棋爱好者一份更好的选择。对于采用的Qt Creator,其中Qt虽然是商业公司的产品,但是走的却是开源路线,提供免费下载,全部都是开放源代码,非商业用途亦采用GPL的版权宣告。且本人也没有违反国家相关法律法规。综上所述,所以在法律可行性是可行的。
    1.5.4 经济可行性基于QT的跨平台网络对战象棋的项目,主要是对于一些商业化严重的游戏的市场局面看不过去,和个人对于传统文化的爱好来开发的;所以本就一开始想着就是开源、非盈利的想法而设计的一款产品。虽没钱可图,但是这样花费时间和精力开发,能够让一些爱好象棋的人在使用这款软件的时候感觉到开心,我就会感觉到很开心。综上所述,所以在经济可行性是可行的。
    二、项目思路分析2.1 需求以及实现需求一:界面美观,操作简易
    需求实现:界面整体分为三大板块;其中主界面大概以7:3分布其中主要的两大板块。第三板块“关于作者”以另外的小的界面单独显示。且只有三个点击按钮。和鼠标点击棋子不规则时候会无反应。符合下棋规则棋子才会移动,故操作简单。
    需求二:下载容易,支持多种主流的操作系统平台
    需求实现:使用QT来开发,本就支持许多的跨平台。然后当某一平台成功之后,只需要在其他平台也重新按照其平台流程发布一遍即可以。
    需求三:免安装,程序体积小
    需求实现:采用动态编译,将所需要的库函数文件已打包压缩成一个绿色的运行程序以实现上面需求。
    需求四:没有广告或者付费充值内容
    需求实现:个人兴趣开发作品,开发此款作品的一个理念是非盈利和开源。
    需求五:易于下载,且一定是正版发布
    需求实现:将其在作品和源码在github上面发布,确保用户下载一定是本人发布版本,而非是旁人可能破解之后的修改版。

    玩家和自己、电脑AI对战功能流程示意图


    多人网络游戏战功能流程示意图


    系统流程图

    三、总体设计3.1 设计思路框架对于这个基于QT的中国象棋游戏项目的项目,因QT本身提供的良好的封装机制,使得它的模块化非常高,可重用性良好;鉴于此优点和大学期间学过的“软件设计”一门课,使得在开发时候也选取模块化设计,实现充分的代码的重复利用性;且尽量使得程序代码之间的组合,达到高内聚、低耦合的效果。
    其中中国象棋的整体结构如下图所示:

    由上面的功能结构图可知,其中大致分为三个部分:①多种游戏模式部分,②对战计时部分,③作品信息部分。而这其中每一个部分都是有细分为其他的多个子部分,其中每一部分的详细框架结构如下。

    多种游戏模式部分,其本质计时下象棋对战部分,是整个项目的最核心区域地方。其中又是细分为三大子模块:

    玩家和自己对战:包含有棋子和基础的棋盘类,从而实现玩家和自己对战的详细游戏规则玩家和AI对战:继承前面的玩家和自己对战类,然后做相应的重载和扩充里面的功能哈数,修改成自己的玩家和AI对战的详细游戏规则多人网络对战:同样是继承前面的玩家和自己对战类,然后做相应的重载和扩充里面的功能哈数,修改成多人网络对战的详细游戏规则
    服务器端:还有相应的Socket套接字网络编程,给客户端发送自己下棋的步骤信息客户端:也有相应的Socket套接字网络编程,给服务器端端发送自己下棋的步骤信息

    对战计时部分,其核心本子就是一个独立运行的计时器功能部分。用于游戏时间的计时
    作品信息部分,其核心本质就是关于作者部分,即就是一个独立的Dialog作品详细介绍部分

    其中一开始,先是设计一个单纯的棋子类Class:ChessPieces,其中只包含每一个棋子的基础信息;
    然后后面新的棋子结构体Struct:POS来包含这个Class:ChessPieces,可以被后面设计的棋盘类所调用,进行初始化。
    紧接着就是结构体Struct:POS的作用是被后面的 棋盘类【Class:ChessBoard 玩家和自己对战类】;以及后来继承它的的两个子类 【Class:MachineGame 玩家和AI对战类 和 Class:NetworkGame 双人网络对战类】)来进行调用。
    其中在各自三种棋盘类【Class:ChessPieces、 Class:MachineGame、Class:NetworkGame 】里面分别详细定义和重载自己所需要的功能函数。比如将里面的玩家A和玩家A对战规则;继承之后修改为玩家A电脑AI对战的规则;又或者是继承重载之后,修改为玩家A和玩家B进行跨网络对战的规则。
    到此为止,整个游戏棋盘的核心下象棋的基础框架就出来了。接下来就是两个独立的模块,在逻辑上面没有太大关联。对于计时模块,利用QT提供的一些控件模块。来完成该相应的计时部分的功能。最后就是关于作者部分,单独的显示一个窗口出来,做一些特效出来,添加一些链接或者文字和图片等,来展示此作品的一些详细信息。
    3.2 界面UI设计整个界面是选用QT自带的Dilog界面和Widget界面背景。其中象棋绘画的部分,是调用系统底层的API函数来绘画的,通过相应的接口来处理进行一些特效,比如颜色和透明度的处理。对于计时器部分,添加好透明背景,作为相应的皮肤,且选用素材图片都是可以免费商用的素材。最后就是关于作者部分界面设计,就是采用的小界面小弹框模式的界面设计,来显示相关作者和作品信息。
    3.3 数据存储设计对于这个里面的相关数据的储存,因为所用到的数据量比较小,所以就直接去掉了采用关系型数据库的考虑,采用数据结构和容器来存储临时的数据。结构简单,且可以加深自己对于数据结构的灵活的运用。
    3.4 类的继承关系图
    3.5 具体各个类的详细设计预作用其中主要是有如上图的几个类,其中每详细类的作用如下:
    项目架构名称: ChineseChess
    设计作用:作为一个总的解决方案。其解决方案下面的项目总名称,有着对开发的项目的最简洁的称呼说明
    类视图:说明:每一个项目都首先创建的,作为一个文件夹,里面用来存放所有项目的.h、.cpp、.pro、图片等其他一系列的资源

    类名:POS
    设计作用:作为一个单独的Struct结构体,用来存放棋子的一个三要素,棋子的名称和棋盘里面横纵坐标
    类视图: 是一个Struct结构体

    类名:AboutAuthor
    设计作用:作为项目里面,展示关于作者和作品详细信息,属于一个独立的模块
    类视图: 是一个Class类。且里面主要是空白,其中主要信息是在一同创建的AboutAuthor.ui里面

    类名:ChessBoar
    设计作用:其是整个项目最核心的一个类,也是整个项目里面最为复杂的一个类。其中包含有多个结构体和类,棋子类等。其所负责主要功能是绘画棋盘和棋子,对所有棋子进行初始化,定了各个棋子的下棋规则制定。同时完成游戏模式之一的玩家与自己对战模式
    类视图: 说明:是一个Class类。作为后面的MachineGame玩家与电脑AI对战和NetworkGame多人网络对战的父类

    类名:ChessPieces
    设计作用:作为单独设计的棋子类,包含棋子的颜色、唯一ID属性、是否死亡等状态属性
    类视图: 说明:是一个Class类。和ChessBoar类是包含关系

    类名:ChessStep
    设计作用:棋子步骤类,主要是使用后面的容器而准备的。只有基本棋子击杀状态、现在棋盘行列值和目标前往的行列值
    类视图: 说明:是一个Class类

    类名:ChooseMainWindow
    设计作用:作为点击游戏开始,就会弹出来的一个选择窗口,关于选择哪一个游戏模式。然后设计最为选择游戏主要窗口
    类视图: 说明:是一个Class类

    类名:MachineGame
    设计作用:作为游戏的玩家与电脑AI对战的模式的实现
    类视图:是一个Class类。也是ChessBoar派生类

    类名:NetworkGame
    设计作用:作为游戏的多人网络对战的模式的实现
    类视图:是一个Class类。也是ChessBoar派生类。其中若是在不同电脑之间测试。记得修改网络协议的,所连接的服务器的IP;当然若是发现端口被占用,亦可以修改成一个大于数值(范围是从0 到65535),建议大于6000

    类名:SelectGameMode
    设计作用:选择游戏模式的Dialog界面
    类视图:是一个Class类。是一个手写的带.ui文件,而非使用设计师设计的

    3.6 详细功能结构模块设计3.6.1 整体的功能结构图设计
    3.6.2 选择游戏方式3.6.2.1 整体思路作为最主要、最核心的基类,也是最开始就单独保持运行的玩家和自己对战的游戏模块。要包含有棋子类,在创建和绘画棋盘的时候,要在构造函数里里面对32颗棋子进行初始化赋值。
    3.6.2.2 步骤
    绘画棋盘:使用系统自带的API函数接口,调用画家Qpainter、画笔drawLine。以及相关的设备上下文等,来绘画初步的函数图像。
    绘画棋子:对棋子进行初始化赋值,使得他们分别固定在对应的棋盘位置
    棋盘行列值和屏幕之间的像素值之间进行切换:创建调用的功能函数,为后面的点击屏幕和点击棋子和点击棋盘具体位置的,之间进行转换
    象棋轮流下:设置BOOL变量值,当属于红方或者黑方下了一步棋之后,就通过限制使得其对方下载,继续进行游戏,才能够接着下棋
    制定象棋的具体规则:要设置每一种棋子对应的走法规则。
    屏幕重绘:当每点击一次棋盘或者棋子之后,和发生鼠标键盘交互事件的时候,就会调用一次系统屏幕的刷新,来进行,屏幕的重绘
    判断谁胜谁负:专门写一个功能函数,看看红方或者黑方的所有棋子均无路可走或者己方的将被击杀,则会判定对方胜利,自己失败,从而结束游戏

    3.6.2.3 局部核心代码class ChessBoard : public QWidget{ Q_OBJECTpublic: explicit ChessBoard(QWidget *parent = 0); ~ChessBoard(); bool isDead(int id); int getStoneId(int row, int col); int getStoneCountAtLine(int row1, int col1, int row2, int col2); //车 炮 的功能辅助函数 判断两个点是否在一个直线上面,且返回直线之间的棋子个数 void whoWin(); //谁胜谁负 bool isChecked(QPoint pt, int& row, int& col); //是否选中该枚棋子。pt为输入参数; row, col为输出参数public: QPoint center(int row, int col); //象棋的棋盘的坐标转换成界面坐标 QPoint center(int id); virtual void paintEvent(QPaintEvent *); //绘画棋盘 void drawChessPieces(QPainter& painter, int id); //绘画单个具体的棋子 virtual void mousePressEvent(QMouseEvent *); //鼠标点击事件 virtual void clickPieces(int checkedID, int& row, int& col); //象棋移动的规则[将士象马车炮兵] bool canMove(int moveId, int killId, int row, int col); bool canMoveJIANG(int moveId, int killId, int row, int col); bool canMoveSHI(int moveId, int killId, int row, int col); bool canMoveXIANG(int moveId, int killId, int row, int col); bool canMoveMA(int moveId, int killId, int row, int col); bool canMoveCHE(int moveId, int killId, int row, int col); bool canMovePAO(int moveId, int killId, int row, int col); bool canMoveBING(int moveId, int killId, int row, int col); }
    3.6.2.4 运行效果
    3.6.3 玩家和AI对战模块3.6.3.1 整体思路提过上面的Class:ChessBoard类已经实现了两方进行象棋的轮流博弈。所以我们在这里的类通过继承Class:ChessBoard即可以了。接下来就是通过重载鼠标和键盘点击是事件,分别来判断是玩家还是电脑系统AI来进行选择棋子和走棋子。
    3.6.3.2 步骤
    重载鼠标点击事件:判断是玩家下棋还是电脑机器AI来进行下棋
    计电脑AI的走法

    计算出所有可能移动棋子的方法:当轮到AI选择棋子和移动棋子的时候。首先判断出AI所有可以移动的棋子,通过void getAllPossibleMoveStepAndNoKill ( Qvector <ChessStep\*>& steps )来计算出获得所有可能的移动步骤(不击杀对方棋子)、以及通过 void getAllPossibleMoveStep (Qvector < ChessStep > & steps)来计算出获得所有可能的移动步骤(击杀对方棋子)的走法,然后通过void saveStep(int moveID, int checkedID, int row, int col, QVector<ChessStep>& steps)来保存这里面的所有可能走法, 以及每一个可以移动棋子的可能走法,将其全部走法都保存在容器QVector<ChessStep\*>& steps里面 假设移动棋子:移动可走的算法里面的某具体的一个棋子的走法计算当局面分:通过你自己自定义的“有利局面”,用一个分数本来表示有利的大小。这里面具体怎么计算这个有利局面的局面分,就占到了很大一部分比例关于AI有多么的“聪明”撤回当前移动的棋子:通过悔棋步骤,撤回该棋子
    AI移动最有利一步棋子:通过遍历可走算法的保存下来的多个可能,然后计算每一个可能得局面分,选择局面分最大的一步走法作为电脑AI最佳的走法
    轮流博弈下棋:在玩家和电脑AI之间,轮流下棋,直到棋局出现胜负局面, 以终止游戏运行

    3.6.3.3 局部核心代码判断是玩家游戏还是AI游戏,并且进行轮流循环:
    void MachineGame::clickPieces(int checkedID, int &row, int &col){ if(m_bIsRed) //红方玩家时间 { chooseOrMovePieces(checkedID, row, col); if(!m_bIsRed) //黑方紧接着进行游戏 { machineChooseAndMovePieces(); //ToDo: 机器 黑方时间 } }}
    3.6.3.4 获得最好的移动步骤ChessStep* MachineGame::getBestMove(){ int maxScore = -10000; ChessStep* retStep = NULL; //------------------------ //有可击杀的红棋子就走击杀红棋子最优的一步 // 1.看看有那些步骤可以走 QVector<ChessStep*> steps; getAllPossibleMoveStep(steps); // 黑棋吃红棋的所有可能的步骤 //------------------------ //没有可击杀的红棋子就走最后的一步 QVector<ChessStep*> stepsAndNoKill; getAllPossibleMoveStepAndNoKill(stepsAndNoKill); // 黑棋移动所有可能的步骤(不吃红棋子) //2.试着走一下 for(QVector<ChessStep*>::iterator it = steps.begin(); it!=steps.end(); it++) { ChessStep* step = *it; fakeMove(step); int score = calcScore(); //3.计算最好的局面分 unFakeMove(step); if(score > maxScore) { maxScore = score; retStep = step; } } if(retStep != NULL) return retStep; //2.试着走一下 //从这种不击杀红棋子,只是单纯移动黑棋steps里面,随机抽选一种进行下棋 int nStepsCount = stepsAndNoKill.count(); qsrand(QTime(0,0,0).secsTo(QTime::currentTime())); //随机数种子, 0~MAX int temp =qrand()% nStepsCount; QVector<ChessStep*>::iterator it = stepsAndNoKill.begin(); retStep = it[temp]; if(retStep == NULL) whoWin(); //4.取最好的结果作为参考 return retStep;}
    3.6.3.5 运行效果
    3.6.4 双人网络对战(服务器端)模块3.6.4.1 整体思路同样是继承上面的思路,首先通过继承类Class:ChessBoard,然后改写部分虚函数,和添加一些额外的扩展来实现双人对战的类Class:NetworkGame。然后将自己所选中的棋子和走法通过TCP协议,Soctck套接字来传输相关的这些信息,然后对方刷新他自己的棋盘界面。然后下完棋之后仍然通过网络Soctck套接字将棋子和下法传输过来,再然后刷新自己的界面。这样往返交替循环下棋,达到局域网里面通信的下棋要求。
    3.6.4.2 步骤
    创建服务器的QtcpServer的m_tcpServer的套接字:制定使用QhostAddress :: Any的协议族,和端口。绑定主机和选择通信协议和接接口
    监听指定网段:监听网络里面其他客户机器,是否发来连接请求
    连接请求的客户机:通过connect()连接服务器和客户机,建立通信连接

    3.6.4.3 局部核心代码服务器端创建网络连接:
    NetworkGame::NetworkGame(bool isServer){ m_tcpServer = NULL; m_tcpSocket = NULL; if(isServer) //作为服务器端 { m_bIsTcpServer = true; m_tcpServer = new QTcpServer(this); m_tcpServer->listen(QHostAddress::Any, 9999); connect(m_tcpServer, SIGNAL(newConnection()),this, SLOT(slotNewConnection())); } else //作为客户端 { ....... }}
    3.6.4.4 运行效果

    3.6.5 双人网络对战(客户端)模块3.6.5.1 整体思路整体思路和服务器无差别,只不过是将客户端将消息发送到服务器。
    3.6.5.2 步骤
    创建套接字 :创建客户端的QtcpSocket套接字的m_tcpSocket
    连接服务器:因为只有一台电脑设备,这里是固定服务器地址为127.0.0.1。连接这一台电脑
    进行网络对战:进行游戏联机对战,一直到游戏运行结束

    3.6.5.3 局部核心代码客户端进行网络连接:
    NetworkGame::NetworkGame(bool isServer){ m_tcpServer = NULL; m_tcpSocket = NULL; if(isServer) //作为服务器端 { …… } else //作为客户端 { m_bIsTcpServer = false; m_tcpSocket = new QTcpSocket(this); m_tcpSocket->connectToHost(QHostAddress("127.0.0.1"), 9999); connect(m_tcpSocket, SIGNAL(readyRead()), this, SLOT(slotRecv())); }}

    这里客户端设置的是连接局域网里面的127.0.0.1,端口为9999服务器;若在两台实体客户机(可以是运行的系统不同)里面运行该程序。则是需要修改。
    3.6.6 对战计时模块3.6.6.1 整体思路利用Qt自带控件QLCDNumber来充当计时的显示屏,QpushButton按钮来作为启动开始和结束的开关。点击开始按钮,计时器后台开始工作。然后每隔一秒钟就刷新一下显示界面。使得看起来计时器是在工作。再点击一下暂停来停止,点击一下重置就会清零计时器的显示时间为00:00,可以开始下一轮的计时。
    3.6.6.2 步骤
    拖拉控件制作:拖拽设计界面的控件,做好布局的显示准备
    定义槽函数:定义各个按钮需要的对应功能,设计成为槽函数[9]
    连接信号与槽:使用connnect()连接控件发射的信号,和实施对应的槽函数,已完成相应的功能

    3.6.6.3 局部核心代码初始化计时器
    m_timer = new QTimer; //初始化定时器m_timeRecord = new QTime(0, 0, 0); //初始化时间m_bIsStart = false; //初始为还未计时connect(m_timer,SIGNAL(timeout()),this,SLOT(updateTime()));
    刷新时间槽函数
    void ChessBoard::updateTime(){ *m_timeRecord = m_timeRecord->addSecs(1); ui->lcdNumber->display(m_timeRecord->toString("hh:mm:ss")); if(m_bIsStart == false) { ui->pushButton_start->setText("开始"); } else if(m_bIsStart == true) { ui->pushButton_start->setText("暂停"); }}
    开始、暂停计时槽函数
    void ChessBoard::on_pushButton_start_clicked(){ if(!m_bIsStart) //尚未开始 开始计时 { m_timer->start(1000); } else //已经开始,暂停 { m_timer->stop(); } m_bIsStart = !m_bIsStart;}
    3.6.6.4 运行效果
    3.6.7 关于作者模块3.6.7.1 整体思路单独的设计一个继承于Widget界面的窗口,然后在里面写上一些想要显示的汉字文字,然后使用一个Qtextbrowser来承接这些,因为可以使得内容信息显示处于多行显示,和外加添加一些连链接、图片等。最后设置对应控件和背景图的层次关系,以及透明度等,就相当于设计好了带的皮肤,给人眼前一亮的感觉。
    3.6.7.2 步骤
    拖拽相关控件
    在控件里面写好相关的内容
    设置槽函数
    连接控件发出的信号和对应的槽函数,施行相应的功能

    3.6.7.3 局部核心代码初始化作者类
    m_pAbout = new AboutAuthor(); //关于作者
    关于作者槽函数
    void ChessBoard::on_pushButton_about_clicked(){ m_pAbout->setWindowTitle("关于作者"); m_pAbout->show();}
    3.6.7.4 运行效果
    3.7 其他细节设计3.7.1 多界面之间的切换实现主要就是一开始点击运行程序。提供一个游戏选择模式窗口,当选择结束之后;在编码部分,将选中的对象设置为生存状态,至少要保证其在运行期间创建的变量对象一直都在,当结束的时候,再来释放该对象分配的许多内存。这里需要注意的是,推荐是使用New一个产生的变量,而不是设置为static的全局变量,且还要将其显示为.show()状态,也不要忘记将次之前已经选择的,不需要的窗口对象使用delete释放掉,且外加.hide()隐藏起来。这样子就可以实现在多个界面之间窗口的来回切换。
    3.7.2 添加界面透明皮肤其实这个功能,是属于看起来困难,但是实施起来比较非常容易的一个步骤,大概简单的就是:在设计师界面,右键点击属性,插入一张图片,设置为置低下一层即可。
    3.7.3 给运行程序添加一个自定义图标这一功能的实现,有比较多的简单方法,这里我挑选一个比较•简单的方法说明我的实现。首先选择自己需要的个性化图片或者Logo的图标,然后采用在线格式转换没设置成.icon的格式图片,一般推荐大小为32x32或者64x64大小的。然后将其图片文件复制命令拷贝好到项目的根目录下面,然后利用记事本新建一个名为logo.rc的文件,logo.ico为转换后的图标名称,内容如下:
    IDI_ICON1 ICON DISCARDABLE “log0.ico”;紧接着就是在工程文件夹中新建一个images目录,将logo.ico、logo.rc放入文件夹中。再打开QT工程,将logo.ico、logo.rc添加进工程。然后在工程文件(*.pro)中加入一行:
    RC_FILE=images/logo.rc最后一步重新建工程,这时QT程序就有了一个漂亮的外观了,程序快捷方式效果如下图所示:

    四、项目演示效果
    1 评论 2 下载 2019-08-19 11:07:11 下载需要13点积分
  • 基于Python实现的孤立词语音识别系统

    1 任务介绍语音识别是通往真正的人工智能的不可缺少的技术。尽管能真正听懂人类说话的智能机器任然在未来不可捉摸的迷雾之中,但我们必须先解决如何识别出人类语音中包含的自然语言信息的问题。而数字信号处理技术将为这一任务赋能。在本课程项目的任务之中,我们面对的是一个简化的语音识别场景——即孤立词识别。
    我们针对 20 个关键词,采集了所有参与课程的同学朗读每个词 20 遍的语音。我将以此为数据集来构建一个能正确识别这 20 个关键词的孤立词识别系统。
    2 项目实现基于一学期跟随老师学习到的关于信号处理与语音识别技术的知识,我额外查阅多方资料,最终呈现出了我的语音识别系统与报告。
    我实现的语音识别系统的亮点有以下几个方面:

    说话人无关的孤立词识别是语音识别技术发展中一个里程碑。从现代的观点来看,如果将语言信号视作时间序列,那么孤立词识别就是一个模式识别中的分类问题。模式识别问题的解决一般分为特征提取与模型构建两个部分。我们将这两个部分分开处理,使得代码的实现更加具有结构性和层次性。报告也将这两部分的处理分开叙述
    我在整个系统的实现中,除了利用了数值处理函数包 numpy 和自动求导工具包 pytorch之外的所有核心代码都是单纯使用 python 实现。即真正锻炼了代码实现能力,也加深了对语音识别技术的理解。在报告中我也强调了各个方法和过程的代码实现,并将关键代码添加到附录之中以方便检阅
    特别地,我基于课堂上所学的蝶形变换方法,实现了以 2 为基的快速傅里叶变换,并运用到了频域特征的分析之中。这让我更加领略到该算法的优美
    根据我自行实现的快速傅里叶变换,实现了梅尔频率域的倒谱系数的计算,并根据通过梅尔滤波器之后得到梅尔频谱特征设计了基于卷积神经网络的识别算法
    我将计算出的频谱特征视为图片,因而可以使用近年来在大规模图片分类任务上大放异彩的卷积神经网络来进行分类识别。我采用了 2014 年在 ImageNet 的比赛上获胜的VGG Net 作为我们的识别模型,并使用了批归一化和 Dropout 手段来避免过拟合,提高模型的泛化能力

    2.1 预处理首先我对数据进行了清洗。
    各个同学上交的文件结构并不一致,有的是一个压缩包下包含所有文件,有的是一个压缩包中还有以自己的学号命名的文件夹,此外还有一些同学提交的压缩包是在 MacOS 上进行打包的,因此还有一个额外的缓存文件夹。这样的结构不利于我们对数据进行批量的读入。
    因此我编写了程序先解压所有压缩包,然后进行深度优先搜索来遍历所有文件夹,根据文件的命名规则把所有文件提取出来,按照 data/学号/文件名.wav 的格式统一存储。同时因为需要大规模地进行复制提取,为了效率的考量,我使用多线程的方式完成了这一任务。
    此外有几个文件显示已损坏而无法读取,以及一个文件录音长度大于两秒。为了数据的一致性,必须去除掉异常数据,但仅仅删除数据将导致样本不均衡的问题。为此我采用随机替换的方式,用同一个同学在同一个词下的另一个语音文件进行替换。这样就可以缓解数据缺失带来的样本不均衡的问题。

    同时,考虑到最终测试时是采用集外测试的方法,理论上讲应剔除女生的数据。
    在接下来的报告中,如无特殊声明,用 y0, y1 · · · , yt, · · · 来表示离散的信号构成的序列,用 sr来表示采样率。
    2.2 特征提取2.2.1 归一化因为数据在采集的时候声音强度并不一致,为了消除影响,我对每一段音频数据进行归一化。这里只需要对 y 使用 numpy.normailize 方法即可。
    2.2.2 预加重对读入的数据,我首先通过预加重的方法来补偿由于唇和空气导致的声音在产生和传播中高频部分的损失。预加重通常采用一个一阶的线性高通滤波器

    来实现,其中 α = 0.97。在代码实现上,只需要对数组进行加权差分即可。
    2.2.3 分帧一般情况下,语音信号是一种典型的非平稳信号,但是可以认为人说话的语调变换并不是是突然的,因此可假定语音信号在短时间 (10-30ms) 内是平稳的;故而在对语音信号进行分析时,我们可以将语音信号以 10-30ms 的间隔将连续的音频信号截取成一段一段来进行分析。
    用 y[i : j] 来表示 yi, yi+1, · · · , yj−1,为一帧包含为 j − i 个采样点的语音信号,其时长为( j−i)/sr。
    因此,当音频信号采样率为 48kHz 的时候,想要得到时长为 10-30ms 的信号帧,其应该包含480-1500 个采样点。
    另外,从分帧后信号的连续性考量,一般在分帧的时候会让相邻的两帧有 30%-50% 的重叠。分帧的实现是简单的,只需要使用 python 支持的数组切片 (slice) 方法即可方便地取出一帧对应的采样点。
    2.3 加窗对于每一帧数据,我们应当强调该帧数据内中间部分的数据,并将边缘的数据进行弱化。我将使用汉明窗

    作为窗函数。

    而所谓的加窗操作也就是用窗函数与一帧内的采样数据相乘


    考虑之后要进行短时傅里叶变换,对每一帧数据进行加窗的理由将更加充分。观察图 2中的一帧余弦信号,可以看到其开始和结束的位置是断开的。而我们在做离散时间傅里叶变换的时候是将该帧数据在时域上无限延拓成周期信号,这将导致延拓之后的信号在帧的开始与结束会发生跳变。直接进行离散傅里叶变换将导致信号泄露的现象。如图 3所示,左边是取出开始和结束连续的一帧信号的离散傅里叶变换的结果,而中间则没有加窗直接进行变换,右边是加了汉明窗再进行变换的结果。可以观察到中间的结果在信号的主要频率周围有明显的泄露现象,导致在原始数据中不存在的频率成分出现在频率域。而增加了汉明窗后,频率泄露的现象有了明显的改善。

    2.3.1 端点检测为了减少非语音的信号对识别的影响,我首先通过信号的时域特征来检测出语音的端点。可以使用短时平均过零率和短时能量来进行端点的检测。
    想要检测出语音信号的端点,最简单的想法就是通过信号的强度来判断,因为包含语音的信号强度会明显高于背景噪声。而短时能量就是刻画信号强度的一个有效的指标。对信号进行分帧之后,y[i : j] 的短时能量定义为

    即帧内信号的平方和。但如果仅仅采用短时能量作为端点检测的指标,我们将很难检测出清音信号,从而导致语音不完全,譬如说单词”Speech” 中开始的清音就无法检测出来。因此我们引入短时平均过零率这个指标

    即该帧内信号通过 0 的平均次数。
    因为无法检测出来的清音一般出现在单词的开始或者结束,所以我们采用以 SE 指标为主、SC 指标为辅的检测策略。首先观察到不少数据在一开始会有类似敲击键盘的的清脆噪声,导致 SE 指标较大,因此我去掉了前五帧信号;然后我设定一个阈值 E0,对于 SE > E0 的帧,可以断定其包含语音信号;取最开始判定为有语音的帧的开始和最末尾有语音的帧的结束作为粗糙的端点检测结果,再根据 SC 指标,设定一个平均过零率阈值 C0,根据粗糙的结果向前、向后扩展 SC > C0 的帧作为包含清音信号的判定条件。

    问题的难点在于阈值的设定,在多次试验后,我选择了 E0 = 0.98, C0 = 0.002 的参数,可以对大多数数据做到较好的端点检测效果。如图 4所示,上方为用红色标出检测结果的波形,中间为短时能量,下方为短时平均过零率。可以看到,如果仅仅使用短时能量进行检测,将无法测出开始的清音,而短时平均过零率的指标则将清音也一并检测出来了。
    2.3.2 快速傅里叶变换为了得到信号的频域特征,需要对每一帧信号进行离散傅里叶变换,即计算

    假设一帧数据的采样点数 N 是 2 的整次幂,那么

    从而

    用 Ek 表示偶数下标子序列的 DFT,Ok 表示奇数下标子序列的 DFT,则 k < N/2 时

    而 k > N/2 时

    这样就可以按照下标的奇偶性将待变换的信号序列拆成两半,递归地进行计算,然后在从递归计算的两个子序列的结果中计算出整个序列的离散傅里叶变换。该算法被称为基 2 的 CooleyTukey快速傅里叶变换。显然该算法计算量为 T(n) = 2T(n/2) + Θ(n)。根据主定理,该算法的时间复杂度为 Θ(n log(n))。
    在递归的过程中,数据的组合与流向的图样像蝴蝶一般对称而美妙,因此被称作蝶形变换。

    我用 python 实现了该算法,并将其利用到后续的梅尔频率域的频谱特征的计算之中。优美的算法通常在实现上也十分简洁。
    2.3.3 梅尔频率域特征梅尔频率域的理论依据来自于人耳听觉对频率的感受。耳蜗相当于一个滤波器组,在 1000hz以下为线性尺度滤波,在 1000hz 以上为对数尺度滤波。通过 fMel = 2595 + log(1 + f/700) 可以将普通频率变换到梅尔频率尺度,观察这个变换的图形会发现在 1000hz 以下会有更高的分辨率,这和人耳对低频信号更加敏感是相符的。在变换之后的梅尔频率尺度取等间隔的带通滤波器组求倒谱即可得到梅尔倒谱。

    对前述通过预加重、分帧、加汉明窗,离散时间傅里叶变换后得到的频谱变换到梅尔频率域,然后施加等间距的三角形滤波器,对滤波后的结果进行求和和取对数操作,即可得到梅尔频率尺度下的功率谱。

    仔细观察该功率谱的结果,对于同一个词来说,在谱上的图样具有明显的图形特征。我们想到可以将其视为图片,然后利用图片的分类技术对语音进行识别。
    传统上的语音识别特征会继续用离散余弦变换求出倒谱系数。余弦变换是傅里叶变换的一个变种,其结果是实数,没有虚部。离散余弦变换还有一个特点是,对于一般的语音信号,这一步的结果的前几个系数特别大,而后面的系数比较小,就可以忽略。这样就可以对特征进行降维,只需要十几个系数即可作为特征。这对于使用传统的模式识别的方法来说特别重要。
    因为无论是支持向量机、高斯混合模型都需要数据的特征在各个维度上是无关的。而不必要的高维度必然导致特征的相关性,从而使得决策面变得扭曲。但使用深度神经网络的方法则不用担心这一点,因为它可以拟合出任意决策面。
    至此完成了对特征的提取,接下来只需要根据特征图样对进行分类即可。
    2.4 识别模型2.4.1 模型构建我使用了 128 个梅尔滤波器,并使用 30ms 作为分帧长度,以及 10ms 的重叠,因此最终得到的图样大小为 128 × 98。
    我仿照 VGGNet 的设置,使用大小为 3 × 3 的卷积核,并构建了 8 层深度的卷积神经网络。因为经过每一层卷积之前都会对图片进行 1 × 1 的补 0 操作,所以对任意大小的图片都可以进行卷积。在经过卷积网络后,我使用一个多层感知器来作为最后的分类器。
    其具体结构如下:

    为了避免过拟合,我对多层感知机的神经元进行概率为 0.5 的 Dropout,并在每一层的卷积操作和非线性函数之间对进行批归一化。
    2.4.2 数据加载考虑到对模型进行训练和测试的复杂性,需要多种数据的加载方式。首先是对数据集进行划分,我需要将一部分同学的所有语音单独拿出来作为测试集,因为最终测试的时候的说话人并不在我们的训练数据中。其次是因为数据量有限,每个词只有不到 600 个的训练数据,为此我对数据进行了交叉验证的划分,采用 10 折交叉的数据划分方式。我利用 pytorch 提供的 Dataset 和 Dataloader 模块,编写了自己的数据加载器。
    2.4.3 模型训练利用 pytorch 提供的自动反向传播,利用交叉熵损失函数作为优化目标,对模型进行了训练。在一台配置为只需要迭代 5 遍测试数据集即可将模型训练至收敛。

    绘制出训练的损失曲线如下图:

    训练结束后,在训练集上能取得 99.5% 的正确率,而对于不在训练集中的说话人,也能取得98.0% 的正确率。说明该模型的确具有非常好的泛化能力。接下来我编写了交互界面,用于进行直接录音测试。
    2.5 识别交互为了方便测试系统的语音识别效果,我基于我之前设计的录音采集系统进行了修改,使得我的系统可以在任何兼容的浏览器上进行测试,避免了为了测试而配置一系列复杂的环境。
    2.5.1 前端界面基于 Web APIs 技术,通过浏览器获取用户的麦克风进行音频录制。完成录制之后通过 JQuery框架使用 ajax 技术进行语音的上传和获取识别结果,再通过 Vue 框架实现的自动绑定,进行结果展示。这样可以实现不用刷新页面即可进行测试的效果,使得使用体验与桌面应用无异。

    2.5.2 服务器端为了能使用训练好的模型进行测试,我利用 flask 编写了服务端。当其接受到前端传回的语音信号后,就会构建模型,并载入训练好的模型参数,然后进行识别。完成识别后,再将结果传回到前端进行显示。
    3 总结在本项目中,我实现了一个识别效果较好的孤立词识别系统。在这一系列过程中,我编写了大量代码,即锻炼了自己的能力,又讲一学期学到的知识真正运用到了实践之中,加深了对知识的印象和理解。
    2 评论 8 下载 2019-04-18 11:27:58 下载需要12点积分
  • 基于ASP.NET实现的大学BBS论坛

    一、分析与建模1.1 系统业务流程图
    1.2 可行性分析1.2.1 法律可行性该理工学院的BBS论坛系统是为了方便理工学院的大学生进行学习、生活、考研、就业、二手交易、心得体会交流的交流社区网上系统,且规范相关部门工作流程,也可以解决现阶段所大学生一些存在的许多问题,没有违反国家相关法律法规,该项目在法律上是完全可行的。
    1.2.2 技术可行性理工学院的BBS论坛系统,采用SQL Server数据库作为系统的数据存储容器,其强大的备份恢复机制保证了数据的安全性。同时,还是具有高效的结构化查询语言的支持,让系统可以在庞大的数据中很容易得到所需要的统计与分析。前台采用HTML+CSS+JavaScript+ASP.NET等技术,实现了较为友好的人机交互。目前,该项目所需的技术都已经有对应的产品,因此该项目在技术上是可行的。
    1.2.3 经济可行性经过有效的成本/效益分析所计算,得到的净现值是正数,说明该项目收益率为正数,有投资的价值。并且使用该理工BBS论坛系统可大大的增加我校理工的大学生在衣食住行学的多个方面的及时沟通,还可以与附近的湖北师范大学的学生一起进行思想上面的沟通,生活上面的便利交流。最为关键的是相当提供了一个有效的信息沟通平台。增加了许多的便利和一些事情的公开与透明。同时大提高学生学习的效率,缩短信息处理周期,也可以教学质量更上一个台阶,。且在还可以在成功运行试水之后,合适的话,到其他的大学进行推广。从而收取部分经济效益。达到回本和盈利的目的。综合以上分析,该系统的开发在经济上是可行的。
    1.2.4 操作可行性BBS论坛系统是完全根据我校大学生的具体需求量身定制了,在工作流上完全细心周到的服务 。此外,系统的用户交互设计较为友好,功能分明,操作简单,且提供了较为详细的用户简介使用手册和具有良好的新手指导功能。因此,该系统在操作上是可行的。
    1.3 功能需求分析与建模
    1.4 数据需求分析与建模任何一个软件系统本质上都是信息处理系统,对软件设计有深远影响,因此,必须分析系统的数据要求, 而这里为了把用户的数据要求清楚、准确地描述出来,我通过建立E-R图来进行描述,表示的是系统的E-R图。

    二、系统设计2.1 功能结构设计
    2.2 数据库设计2.2.1 实体以及相关的属性
    用户表(t_user)用户编号(唯一,自增长),邮箱,头像,手机号码,网名,密码,注册时间(唯一),性别,真实姓名,身份标识
    小板块表(t_section)小板块编号(唯一,自增长),所属大板块编号,用户编号,小版块名称,小版块标志
    大板块表(t_zone)大板块编号(唯一,自增长),大板块名称,大板块简介
    帖子表(t_topic)帖子编号(唯一,自增长),用户编号,小版块编号,帖子内容,修改帖子时间,第一次发帖时间,帖子标题,帖子点赞数,帖子置顶标记
    回帖表(t_reply)回复帖编号(唯一,自增长),最后回帖时间,回帖时间,回帖内容,回复的帖子标题

    2.2.2 实体之间的联系
    一个用户可以发表多个帖子(一对多的关系)
    一个用户也可以有回复多个回帖(一对多的关系)
    一个用户可以是多个小板块的版主(一对多的关系)
    一个小板块只能够是属于一个大板块空间的(一对一的关系)
    一个大板块空间是可以有多个小板块的(一对多的关系)
    一个帖子可以有多个回复的帖子(一对多的关系)

    2.2.3 物理模型图
    2.2.4 生成的属性信息等关系所有的表



    序号
    中文
    名称
    代码
    数据类型




    1
    回帖
    t_reply
    t_reply



    2
    小板块
    t_section
    t_section



    3
    帖子
    t_topic
    t_topic



    4
    用户
    t_user
    t_user



    5
    大板块
    t_zone
    t_zone




    所有表的属性



    序号
    中文
    名称
    代码
    数据类型




    1
    编号
    id
    id
    int


    2
    名称
    name
    name
    Variable characters (100)


    3
    描述
    description
    description
    Variable characters (200)


    4
    标号
    id
    id
    int


    5
    邮箱
    email
    email
    Variable characters (100)


    6
    头像
    face
    face
    Variable characters (200)


    7
    手机号
    mobile
    mobile
    Char(11)


    8
    网名
    nickname
    nickname
    Variable characters (50)


    9
    密码
    password
    password
    Char (20)


    10
    注册时间
    regtime
    regtime
    Data&Time


    11
    性别
    sex
    sex
    Char(2)


    12
    真实姓名
    truename
    truename
    Char(20)


    13
    身份类型
    type
    type
    Char(4)


    14
    编号
    id
    id
    int


    15
    大板块编号
    t_z_id
    t_z_id
    int


    16
    用户编号
    t_u_id
    t_u_id
    int


    17
    名称
    name
    name
    Char(50)


    18
    标志图片
    logo
    logo
    Char(20)


    19
    编号
    id
    id
    int


    20
    用户编号
    t_u_id
    t_u_id
    int


    21
    大板块编号
    t_s_id
    t_s_id
    int


    22
    内容
    content
    content
    Variable characters (1000)


    23
    修改时间
    modifytime
    modifytime
    Data&Time


    24
    回复时间
    publishtime
    publishtime
    Data&Time


    25
    标题
    title
    title
    Variable characters (100)


    26
    点赞数
    good
    good
    Char(4)


    27
    置顶标记
    top
    top
    Char(4)


    28
    编号
    id
    id
    int


    29
    帖子编号
    t_t_id
    t_t_id
    int


    30
    用户编号
    t_u_id
    t_u_id
    int


    31
    最后回复时间
    modifytime
    modifytime
    Data&Time


    32
    恢复时间
    publishtime
    publishtime
    Data&Time


    33
    内容
    content
    content
    Variable characters (100)


    34
    标题
    title
    title
    Variable characters (1000)



    2.3 关键算法设计在这里一共进行了三个相关的算法,分别是服务器异常断开、重启状态、等突发事件算法设计,头像图片上传算法设计和用户相关信息防泄漏算法与设计。
    2.3.1 服务器异常、突发事件算法设计
    2.3.2 图片文件上传算法设计
    2.3.3 用户相关信息防泄漏算法与设计
    2.4 UI设计2.4.1 用户登陆UI设计
    2.4.2 用户注册UI设计
    2.4.3 置顶、精品UI设计
    2.4.4 大板块查看UI设计
    2.4.5 小板块查看UI设计
    2.4.6 回帖跟帖UI设计
    2.4.7 发帖UI设计
    2.4.8 小板块增加删除主题UI设计
    2.4.9 删帖修改帖子UI设计
    2.4.10 个人中心UI设计
    2.4.11 后台登录UI设计
    2.4.12 大板块增加删除查询UI设计
    三、系统实现中的重难点3.1 系统实现环境该系统采用Microsoft Visual Studio 2015的.Net Frame 4.5作为框架,页面使用了Html+Javascript+ASP.Net作为B/S模式下的前台开发工具,C#作为B/S模式下的后台编码语言,ASP.NET作为B端的数据访问技术,SQL SERVER 2012作为后台的数据库,页面的加载、效果作为div+css作为网页布局。

    开发硬件环境:个人笔记本电脑
    开发软件环境:win10 x64/ Visual Studio 2015/ SQL SERVER 2012/谷歌浏览器稳定版/火狐浏览器

    3.2 系统编码重难点重难点之多条件查询
    3.2.1 界面
    3.2.2 web代码<div class="container-fluid"> <div id="tooBar" style="padding: 10px 0px 0px 10px;"> <button class="btn btn-primary" type="button" data-backdrop="static" data-toggle="modal" data-target="#dlg" onclick="return openAddDlg()">添加小板块</button>     <a href="#" role="button" class="btn btn-danger" onclick="javascrip:deleteSections()">批量删除</a> <form method="post" action="/backstage/SectionList.aspx" class="form-search" style="display: inline;">  小板块名称: <input name="sectionname" id="peisnname" value="<%=sectionname%>" type="text" class="input-medium search-query" placeholder="输入小板块名称..."/>  所属大板块: <select id="zid" name="zid"><option value="">请选择...</option> <%foreach( hua_bbs.Model.Zone zone in zoneList) {%> <option value="<%=zone.id%>" <%= zid == zone.id.ToString() ? "selected": "" %> ><%=zone.name%></option> <%} %> </select>  版主: <select id="uid" name="uid"><option value="">请选择...</option> <% foreach( hua_bbs.Model.User user in userList) {%> <option value="<%=user.id%>" <%= uid == user.id.ToString() ? "selected": ""%> ><%=user.nickname %></option> <%} %> </select>   <button type="submit" class="btn btn-primary" title="Search">查询 <i class="icon icon-search"></i></button> </form> </div> <div id="dlg" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-hidden="true" onclick="return resetValue()">×</button> <h3 id="myModalLabel">增加小板块</h3> </div> <div class="modal-body"> <form id="fm" action="SectionList.aspx" method="post" enctype="multipart/form-data"> <input type="hidden" value="saveOrUpdate" name="flag"/> <table> <tr> <td> <label class="control-label" for="sectionName">请输入小板块名称:</label> </td> <td> <input id="sectionName" type="text" name="ssname" placeholder="请输入…"> </td> </tr> <tr> <td> <img id="ImgPr" class="pull-left" style="width: 120px; height: 120px;" /> </td> <td> </td> </tr> <tr> <td> <label class="control-label" for="logo">上传logo:</label> </td> <td> <input type="file" id="logo" name="logo"> </td> </tr> <tr> <td> <label class="control-label" for="zone">请选择所属大板块:</label> </td> <td> <select id="zone" name="zzid"><option value="">请选择...</option> <%foreach( hua_bbs.Model.Zone zone in zoneList) {%> <option value="<%=zone.id%>"><%=zone.name%></option> <%} %> </select> </td> </tr> <tr> <td> <label class="control-label" for="masterNickName">版主:</label> </td> <td> <select id="masterNickName" name="uuid"><option value="">请选择...</option> <% foreach( hua_bbs.Model.User user in userList) {%> <option value="<%=user.id%>"><%=user.nickname %></option> <%} %> </select> <font id="info" style="color: red;"></font> </td> </tr> </table> <input id="id" type="hidden" name="ssid"> <input type="hidden" id="peipage" name="page"/> <input type="hidden" id="peizid" name="zid"/> <input type="hidden" id="peiuid" name="uid"/> <input type="hidden" id="peisectionname" name="sectionname"/> </form> </div> <div class="modal-footer"> <font id="error" style="color: red;"></font> <button class="btn" data-dismiss="modal" aria-hidden="true" onclick="return resetValue()">关闭</button> <button class="btn btn-primary" onclick="javascript:saveSection()">保存</button> <!-- <button class="btn btn-primary" type="submit">保存</button> --> </div> </div>
    3.2.3 数据库代码public bool MyUpdate(hua_bbs.Model.Section model) { StringBuilder strSql = new StringBuilder(); strSql.Append("update t_section set "); strSql.Append("name=@name,"); strSql.Append("logo=@logo,"); strSql.Append("t_u_id=@t_u_id,"); strSql.Append("t_z_id=@t_z_id"); strSql.Append(" where id=@id"); SqlParameter[] parameters = { new SqlParameter("@name", SqlDbType.VarChar,40), new SqlParameter("@logo", SqlDbType.VarChar,100), new SqlParameter("@id", SqlDbType.Int,4), new SqlParameter("@t_z_id", SqlDbType.Int,4), new SqlParameter("@t_u_id", SqlDbType.Int,4)}; parameters[0].Value = model.name; parameters[1].Value = model.logo; parameters[2].Value = model.id; parameters[3].Value = model.t_z_id; parameters[4].Value = model.t_u_id; int rows = DbHelperSQL.ExecuteSql(strSql.ToString(), parameters); if (rows > 0) { return true; } else { return false; } } /// <summary> /// 得到一个对象实体 /// </summary> public hua_bbs.Model.Section DataRowToSectionZoneUser(DataRow row) { hua_bbs.Model.Section model = new hua_bbs.Model.Section(); model._user = new Model.User(); model._zone = new Model.Zone(); if (row != null) { if (row["id"] != null && row["id"].ToString() != "") { model.id = int.Parse(row["id"].ToString()); } if (row["sname"] != null) { model.name = row["sname"].ToString(); } if (row["logo"] != null) { model.logo = row["logo"].ToString(); } //得到板主id if (row["uid"] != null && row["uid"].ToString() != "") { model.t_u_id = int.Parse(row["uid"].ToString()); } //得到主题id if (row["zid"] != null && row["zid"].ToString() != "") { model.t_z_id = int.Parse(row["zid"].ToString()); } //得到板主名称 if (row["nickname"] != null) { model._user.nickname = row["nickname"].ToString(); } //得到主题名称 if (row["zname"] != null) { model._zone.name = row["zname"].ToString(); } } return model; } //获得Section,Zone,User(3张表的)数据列表 public DataSet GetSectionZoneUserListByPage(string strWhere, string orderby, int startIndex, int endIndex) { StringBuilder strSql = new StringBuilder(); strSql.Append("SELECT * FROM ( "); strSql.Append(" SELECT ROW_NUMBER() OVER ("); if (!string.IsNullOrEmpty(orderby.Trim())) { strSql.Append("order by s." + orderby); } else { strSql.Append("order by s.id desc"); } strSql.Append(")AS Row, s.id,s.name as sname,s.logo,z.name as zname,u.nickname,z.id as zid,u.id as uid from " + "t_section s , t_zone z , t_user u where s.t_z_id = z.id and s.t_u_id = u.id "); if (!string.IsNullOrEmpty(strWhere.Trim())) { strSql.Append(strWhere); } strSql.Append(" ) TT"); strSql.AppendFormat(" WHERE TT.Row between {0} and {1}", startIndex, endIndex); return DbHelperSQL.Query(strSql.ToString()); } /// <summary> /// 获取记录总数 /// </summary> public int GetSectionZoneUserRecordCount(string strWhere) { StringBuilder strSql = new StringBuilder(); strSql.Append("select COUNT(*) from t_section s , t_zone z , t_user u where s.t_z_id = z.id and s.t_u_id = u.id "); if (strWhere.Trim() != "") { strSql.Append(strWhere); } object obj = DbHelperSQL.GetSingle(strSql.ToString()); if (obj == null) { return 0; } else { return Convert.ToInt32(obj); } }public bool MyDelete(int sectionid) { List<Topic> topicList = topicService.GetModelList("t_s_id='" + sectionid + "'"); foreach (Topic topic in topicList) { replyService.DeleteByTid(topic.id);//删除此主贴下的所有回帖 } topicDao.DeleteByTsid(sectionid);//删除此板块下的所有主贴 return dal.Delete(sectionid); } /// <summary> /// 获得数据列表(3表操作) /// </summary> public List<hua_bbs.Model.Section> DataTableToSectionZoneUserList(DataTable dt) { List<hua_bbs.Model.Section> modelList = new List<hua_bbs.Model.Section>(); int rowsCount = dt.Rows.Count; if (rowsCount > 0) { hua_bbs.Model.Section model; for (int n = 0; n < rowsCount; n++) { model = dal.DataRowToSectionZoneUser(dt.Rows[n]); if (model != null) { modelList.Add(model); } } } return modelList; }
    3.2.4 C#代码using hua_bbs.BLL;using hua_bbs.Model;using System;using System.Collections;using System.Collections.Generic;using System.IO;using System.Linq;using System.Web;using System.Web.UI;using System.Web.UI.WebControls;namespace WebApplication.backstage{ public partial class SectionList : System.Web.UI.Page { public List<Section> sectionList { set; get; } public string pageCode { set; get; } public string sectionname { set; get; } public string zid { set; get; } public string uid { set; get; } public List<User> userList { set; get; } public List<Zone> zoneList { set; get; } private SectionService sectionService = new SectionService(); //入口方法 protected void Page_Load(object sender, EventArgs e) { string flag = Request["flag"]; if (flag == null || "".Equals(flag)) { this.show(sender, e); } else if ("saveOrUpdate".Equals(flag)) { this.saveOrUpdate(sender, e); } else if ("delete".Equals(flag)) { this.delete(sender, e); } } public void delete(object sender, EventArgs e) { string ids = Request["ids"]; string[] strid = ids.Split(','); for (int i = 0; i < strid.Length; i++) { sectionService.MyDelete(Int32.Parse(strid[i])); } Response.Write(true); Response.End(); } //添加或修改板块信息 public void saveOrUpdate(object sender, EventArgs e) { int ssid = 0; if (!Int32.TryParse(Request["ssid"], out ssid)) { ssid = 0; } string ssname = Request["ssname"]; int zzid = Int32.Parse(Request["zzid"]); int uuid = Int32.Parse(Request["uuid"]); HttpPostedFile file = Request.Files["logo"];//获取上传的图片 string logo = ""; //关于上传图片的代码 if (file != null && !file.FileName.Equals("")) { string fileName = file.FileName;//得到上传图片的文件名字 string ext = Path.GetExtension(fileName);//后缀名 if (ext == ".jpg" || ext == ".gif" || ext == ".png" || ext == ".jpeg") { string newFileNames = Guid.NewGuid().ToString() + ext; logo = "/images/logo/" + newFileNames; //img文件夹前面在加一个/表示为根目录(一级目录) string fileSavePath = Request.MapPath(logo); file.SaveAs(fileSavePath);//保存图片到服务器指定的目录中去 } else { logo = "/face/yyy123.gif"; } } else { logo = "/face/yyy123.gif"; } Section section = new Section(); section.id = ssid; section.name = ssname; section.t_u_id = uuid; section.t_z_id = zzid; section.logo = logo; sectionService.saveOrUpdate(section); //添加或者修改完成之后 this.show(sender, e); } //进行查询操作数据展示 protected void show(object sender, EventArgs e) { int pageNumber = 0; if (!Int32.TryParse(Request["page"], out pageNumber)) { pageNumber = 1; } uid = Request["uid"]; zid = Request["zid"]; sectionname = Request["sectionname"];
    3.3 系统测试在这一阶段的软件测试里面,在此列举系统的部分功能的测试,为了让大家都了解测试在网站设计过程中重要性,所以在这里将测试流程以及结果如下表暂时出来、并且做一点说明。对于其中开始的黑盒测试做了如下的一些测试,有效输入的等价类测试、无效输入的等价类测试、合法输出的等价类测试、非法输出的等价类测试、边界值的测试。
    测试用例表



    测试对象
    测试方法
    测试用例
    预期结果
    实际结果
    说明
    测试结果




    个人中心信息修改
    白盒测试
    点击“个人中心”“编辑”,输入要修改的信息等,点击“保存”
    弹出对话框提示“修改个人信息成功”
    弹出对话框提示“修改个人信息成功”
    测试和预期相符合
    成功


    发布公告
    黑盒测试
    点击“发布公告”,编写内容,点击“发布”
    弹出对话框提示“发布成功”
    弹出对话框提示“发布成功”
    测试和预期相符合
    成功


    密码修改
    黑盒测试
    输入Asd2345678
    密码修改成功
    密码修改成功
    修改密码,测试通过
    成功


    身份证号修改
    黑盒测试
    输入身份证号
    身份证号码修改成功
    身份证号码修改成功
    修改身份证号码为18位,测试通过
    成功


    图片文件上传
    白盒测试
    点击“发布公告”,编写内容,点击“发布”
    弹出对话框提示“你确定要上传”,“上传文件成功”
    弹出对话框提示“你确定要上传”,“上传文件成功”
    测试和预期相符合
    成功


    注册
    黑盒测试
    点击文件专区,“图片文件上传”,输入相关的文件信息,以及文件的路径,选择点击“提交”,点击“确定”
    弹出对话框提示“该用户已存在”,并且有记录
    没有弹出对话框提示“已存在信息”,也没有数据
    测试和预期相不符合
    失败


    成绩分数修改
    黑盒测试
    输入0
    学生成绩录入成功
    学生成绩录入成功
    教师录入学生成绩,测试通过
    成功


    密码轻度检测
    黑盒测试
    输入Qwe1234
    错误,密码长度太短
    错误,密码长度太短
    用户更改7位长度密码,测试通过
    成功


    分数类型校验
    黑盒测试
    输入99.9
    错误,分数必须为正整数
    错误,分数必须为正整数
    教师录入成绩有小数,测试通过
    成功


    权限设置,修改成绩
    黑盒测试
    修改成绩
    错误,你没有权限执行该操作
    错误,你没有权限执行该操作
    学生试图修改成绩,测试通过
    成功


    分数查询
    黑盒测试
    点击成绩页面,查询分数
    查询成功
    查询成功
    教师查询成绩,测试通过
    成功



    四、总结作为这一阶段的小学期课程的学习,其中的收获发感觉颇丰,在此之前直接接触的基于C/S架构的开发,然后设计过程之中,表之间的联系,数据的架构的过程比较较少。缺乏知识的完备性和完善性。而恰好借助这段时间的学习,我开始接触和了解具体怎么设计一个B/S架构的系统。 在这个里面,这是我写的多个asp.net项目面写的费时间最长, 耗费精力最大,首次商业化的使用MVC三层架构开发,其中在DAO控制层和Servicer业务层开发耗费力气比较大。在这个学习的中间,我觉得其中里面比较重点的知识点有:分页代码查询,多条件查询,多表查询,且有些功能比较重要,登录采用MD5加密,和验证码验证,还有勾选记住我,可以免密码登录在一定日期中,且普通用户具有发帖,评论等功能。版主具有回帖,发帖,删帖,修改个人信息等功能,管理员可以从右下角后台管理进入,登录,对大板块,小板块进行增加,批量删除,普通用户封号,添加还有整体的换肤,等功能。
    通过如上的一系列的功能的实现,其中大部分的设计的功能都是有实现,且通过了测试,但是作为这次的BBS论坛的设计与实现,其中的过程还是又遇到一些不会无法实现的地方,但是在学习的过程中,有老师的耐心讲解和思路上面的引导,和实例的书写,和最后的经过自己百度和谷歌,查询一些博客和资料之后,自己终于解决了,还是感觉非常开心的。在这里也,也今后的之学习路上,感觉自己还是有很多的不会,但是我相信自己,经过自己的学习,多听有着丰富经验的人的话。可以避免少走很多的弯路。让自己学的又快的又稳。展望未来。也希望自己的以后能够学习更多的知识。
    1 评论 2 下载 2019-08-15 09:00:33 下载需要12点积分
  • 基于java和Sql Server数据库的停车场管理系统

    一、实验内容:实现停车场管理系统,应用于车辆的出、入管理。
    二、功能要求:包括车辆进出管理与系统管理等功能模块,可根据车辆停放时间及收费标准自动收费。用户需要事先办理停车卡并充值,停车卡分优惠卡和普通卡两类。

    车场管理:车辆入场、车辆出场
    信息查询:某时间段的出入场信息,当前在场信息,车辆历史停车记录及收费信息
    信息维护:用户及停车卡信息维护、充值等
    系统管理:车位信息,计费标准等

    系统包含两类用户:管理员用户和普通用户。
    管理员可以使用系统所有功能,普通用户只能查询车辆历史记录、用户信息、停车卡充值,查询计费标准。
    三、实验环境:
    Windows XP
    JDK 1.6
    Eclipse
    SQL Server
    备注:

    在XP平台开发DK(JavaDevelopment Kit)是Sun Microsystems针对Java开发员的产品Eclipse进行前台和程序设计,开发图形用户界面和停车收费功能实施
    SQL建立数据库

    四、需求分析与设计:4.1 需求分析:本软件具有如下主要功能:

    本系统包括两类用户:管理员用户和普通用户。管理员可以使用系统所有功能,普通用户只能查询车辆历史记录、用户信息(只限于个人信息)、查询计费标准、查询当前在场信息、查询出入场信息、当前可用车位信息、口令修改。具体模块划分为如下模块:车场管理模块、信息查询模块、信息维护模块、系统管理模块。
    车场管理模块:(应该分为车辆入场和车辆出场两部分)

    车辆入场功能描述:车辆进入停车场时进行登记,记录入场时间并指定车位。只有具有停车卡的车辆才可进场,没有办理停车卡的车辆,应先办理车卡。如果没有相应车位,不能入场;如果卡中余额低于100元,应先充值后再入场。满足条件的车辆,为其指定车位并记录入场时间。车卡分两种类型普通型和优惠型。车辆出场功能描述:车辆开出停车场时进行登记,记录出场的时间并进行自动收费(从卡上扣除)。根据车辆进场时间,出场时间及收费标准自动计算车主应该缴纳的费用。如果停车时间包含不足一小时的时间,超过30分钟按一小时计算,不足三十分钟不计算。如果卡上余额足够则直接扣除;如果卡上余额不足,则应先充值后再扣除相应费用。
    信息查询模块功能描述:在这个模块里用户可以查询出入场信息、当前在场信息、用户个人信息、用户历史记录、收费标准以及当前可用车位信息
    查询出入场信息功能描述: 查询当前在场信息户可以在这里查询到两种车位的总量及当前可有的车位数量。
    查询用户个人信息功能描述:登录的管理员可以根据卡号和名字查询用户信息。登陆的普通用户只可以查到自己的信息。
    查询用户历史记录功能描述:用户可以输入卡号查询相应卡号的历史记录,包括车位号、开始停车时间、结束停车时间、停车总时间、相应收取的费用。
    收费标准功能描述:用户可以在这里查询不同种类的车位和不同卡的计费标准。
    当前在场信息功能描述:用户可以在这里查询到当前在场的车辆信息,包括卡号,车位号,开始停车时间。
    当前可用车位信息功能描述:在这里用户可以查询当前可用的车位的信息,包括车位号、车位类型。
    信息维护模块在这个模块里用户可以实现用户注册、用户修改及用户充值
    用户注册功能描述:在这里管理员可添加新的用户(普通用户)。
    用户修改管理员在这里可以修改用户。这里会以表的形式显示所有的用户信息,包括用户的停车卡信息维护,充值信息等。管理员点击相应的一行用户信息,这行信息会自动填充到表下的面板里,用户可以在面板里修改用户信息,面板下面有两个按钮,修改、删除,点击相应的按钮可以实现相应的功能。
    用户充值功能描述:用户可以再这里查到自己的余额,并且可以在这里完成充值。
    系统管理模块功能描述:在这个模块里可以修改相应的车位信息计费标准、注册管理员、更改用户口令以及查看系统声明信息。
    管理员注册功能描述:管理员可以在这里添加新的管理员。
    更改口令功能描述:用户可以在这里更该自己的密码。注:操作员只可以修改自己的密码。
    计费标准管理功能描述:管理员可以在这里不同车位类型、不同车卡类型的收费标准。
    关于功能描述:用户可以在这里看到系统声明。

    4.2 界面设计登陆界面

    管理员主界面

    普通用户主界面

    车辆入场界面

    车辆出场界面

    计费标准界面

    当场在场信息界面

    用户历史信息界面

    用户个人信息界面

    普通用户个人信息界面

    出入场信息界面

    当前可用车位信息界面

    用户注册界面

    用户修改界面

    用户充值界面

    管理员注册界面

    更改口令界面

    计费标准管理界面

    关于界面

    五、数据库设计5.1 数据库关系图
    5.2 数据表的结构设计


    用户表:users








    字段名称
    数据类型
    可空
    默认值
    说明


    cardid
    int
    不可

    主键,用户的停车卡号


    name
    Nvarchar(20)
    不可

    用户姓名


    password
    Nvarchar(20)


    用户密码


    cardtype
    Nvarchar(20)


    停车卡类型


    userstype
    Nvarchar(20)


    用户类型


    carid
    int


    用户车牌号


    tel
    int


    用户电话号码


    overage
    int


    用户余额






    车位信息表:sit_infor








    字段名称
    数据类型
    可空
    默认值
    说明


    stationid
    int
    不可

    主键,车位号


    stationtype
    Nvarchar(20)
    不可

    车位类型






    停车收费卡收费表:charger








    字段名称
    数据类型
    可空
    默认值
    说明


    cardtype
    Nvarchar(6)


    车卡类型


    stationtype
    Nvarchar(20)


    车位类型(车卡类型与车位类型一起作为主键)


    charge
    int


    价格






    停车表:park








    字段名称
    数据类型
    可空
    默认值
    说明


    cardid
    int


    车卡号(外键)


    stationid
    int


    车位号(外键)


    parkid
    int

    1,每次增加一
    停车号,主键


    startpark
    datetime


    停车开始时间


    endpark
    datetime


    停车结束时间


    fee
    int


    停车的收费


    sumpark
    int


    停车总时间



    六、关键技术介绍6.1 在其他类中得到当前登录用户对象 实现方法:在LoginFrame类中设置两个静态方法,在其他类中只需要引入LoginFrame类,然后调用他的静态方法即可。方法体如下:
    public static users getUser() { return user; } public static void setUser(users user) { LoginFrame.user = user; }
    6.2 实现用户类型不同,主界面不同的功能 可以定义静态方法disMenu().当用户是普通用户时,调用disMenu()方法即可。具体实现如下
    public void disMenu() { mnuPark.setEnabled(false); mnuSever.setEnabled(false); mnuManZhuCe.setEnabled(false); mnuManCharge.setEnabled(false); } if(user.getUserstype().equals("管理员")) { MdiFrame frame1 = new MdiFrame();//创建一个主窗体 frame1.setVisible(true);//设置其可见 LoginFrame.this.setVisible(false);//设置登录窗体为不显示 } else {//判断用户名是否为null MdiFrame frame = new MdiFrame();//创建一个主窗体 frame.disMenu(); frame.setVisible(true);//设置其可见 LoginFrame.this.setVisible(false);//设置登录窗体为不显示 }
    6.3 怎么得到系统时间 SimpleDateFormat myfmt=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); String a4 = myfmt.format(new java.util.Date()).toString();
    6.4 怎么计算时间差值 try { java.util.Date now = myfmt.parse(a3);//a3是系统当前时间(即出场时间) java.util.Date date=myfmt.parse(a7);//a7是入场时间 int l=(int) (now.getTime()-date.getTime());//计算毫秒差值 day=l/(24*60*60*1000);//获取天数 hour=(l/(60*60*1000)-day*24);//获得小时 min=((l/(60*1000))-day*24*60-hour*60);//获得分钟 } catch (Exception e1) { JOptionPane.showMessageDialog(null,"消费计算错误"); } if(min < 30)//如果分钟小于30分钟 a8 = day*24+hour; else //如果分钟大于30分钟 a8 = day*24+hour+1;
    6.5 怎么让布局更优美 使用布局管理器; GridBagLayout,以更改密码界面为例:
    getContentPane().setLayout(new GridBagLayout()); setBounds(234, 129, 285, 223); final JLabel label_5 = new JLabel(); label_5.setText("登 录 名:"); final GridBagConstraints gridBagConstraints_11 = new GridBagConstraints(); gridBagConstraints_11.gridy = 2; gridBagConstraints_11.gridx = 0; getContentPane().add(label_5, gridBagConstraints_11);
    七、系统实现功能结构图
    15 评论 430 下载 2018-11-19 09:31:15 下载需要10点积分
  • 基于react框架和MYSQL数据库的社团管理系统

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

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

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

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

    用户名编辑修改密码

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

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

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

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



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




    id
    int(11)
    P

    自动递增


    login_name
    varchar(255)





    name
    varchar(255)





    password
    varchar(255)





    role
    int(1)


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


    isDel
    int(1)


    默认0



    学生表(student)



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




    stu_id
    varchar(255)
    P




    stu_name
    varchar(255)





    password
    varchar(255)





    sex
    int(1)


    0:男 1:女


    role
    int(1)


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


    tel
    varchar(255)





    社团表(community)



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




    id
    int(11)
    P

    自动递增


    title
    varchar(255)





    desp
    varchar(255)





    belong_dept
    int(11)





    manage_dept
    int(11)





    chairman_stu_id
    int(11)





    type
    int(11)





    create_time
    varchar(13)





    status
    int(1)


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



    社团-学生表(community_student)



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




    id
    int(11)
    P

    自动递增


    stu_id
    int(11)





    community_id
    int(11)





    role
    int(11)


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


    isDel
    int(11)





    社团类型表(community_type)



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




    id
    int(11)
    P

    自动递增


    type_name
    varchar(255)





    isDel
    int(11)


    默认0



    部门表(dept)



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




    id
    int(11)
    P

    自动递增


    dept_name
    varchar(255)





    isDel
    int(11)


    默认0



    公告表(notice)



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




    id
    int(11)
    P

    自动递增


    title
    varchar(255)





    author
    varchar(255)





    content
    text





    create_time
    varchar(255)





    isDel
    int(11)





    公告日志表(notice_log)



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




    id
    int(11)
    P

    自动递增


    notice_id
    int(11)





    stu_id
    int(11)





    create_time
    varchar(255)





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

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

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

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

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

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

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


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

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

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

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

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



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

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

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


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

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


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

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


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


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


    原密码错误会提示错误。
    0 评论 1 下载 2019-08-11 08:41:12 下载需要11点积分
  • 基于JAVA的图形填充

    Overview计算机图形学期末PJ

    多边形区域填充
    3D立方体投影展示程序

    RequirementsJDK1.6
    How to run1. git clone https://github.com/whimsycwd/GemetoryPJ.git2. mvn clean compile3. mvn exec:java -Dexec.mainClass="base.FillPolygon" # 多边形填充4. mvn exec:java -Dexec.mainClass="bonus.Cube" # 3D立方体投影Hot to Operate多边形填充

    鼠标点击n个点
    最后一个点双击
    这n个点组成的的多边形内部被染色

    示例:

    3D立方体投影

    w and s : Pitch
    a and d : Yaw
    q and e : Roll

    实现算法多边形填充
    跨立实验判断线段相交
    public double cross(Point a, Point b, Point c) { return (double) (b.x - a.x) * (c.y - a.y) - (double) (b.y - a.y) * (c.x - a.x); } public int sign(double value) { if (value > 1e-8) return 1; if (value < -1e-8) return -1; return 0; } public boolean intersection(Point a, Point b, Point c, Point d) { if (sign(cross(a,b,c)) * sign(cross(a, b, d)) > 0) return false; if (sign(cross(c,d,a)) * sign(cross(c,d,b)) > 0) return false; return true; }
    针对每个像素, 采用射线法, 奇数个与多边形相交的点则为内部点.
    private void fill() { Point sp = new Point(9998, 9999); int n = nodes.size(); for (int i = 0; i < WIDTH; ++i) { for (int j = 0; j < HEIGHT; ++j) { int intersectionNumber = 0; for (int k = 0; k < nodes.size(); ++k) { if (intersection(nodes.get(k), nodes.get((k + 1) % n), new Point(i,j), sp)) { ++intersectionNumber; } } if (intersectionNumber % 2 == 1) { Graphics g = p.getGraphics(); g.drawLine(i, j, i, j); } } }3D立方体投影
    图形旋转参考右手系旋转的方式
    // rotate along axis X double [][] matrixAlpha = new double[4][4]; matrixAlpha[0][0] = 1; matrixAlpha[1][1] = Math.cos(alpha); matrixAlpha[1][2] = Math.sin(alpha); matrixAlpha[2][1] = -Math.sin(alpha); matrixAlpha[2][2] = Math.cos(alpha); matrixAlpha[3][3] = 1; // rotate along axis Y; double [][] matrixBeta = new double[4][4]; matrixBeta[0][0] = Math.cos(beta); matrixBeta[0][2] = -Math.sin(beta); matrixBeta[1][1] = 1; matrixBeta[2][0] = Math.sin(beta); matrixBeta[2][2] = Math.cos(beta); matrixBeta[3][3] = 1; double [][] matrixGamma = new double[4][4]; matrixGamma[0][0] = Math.cos(gamma); matrixGamma[0][1] = Math.sin(gamma); matrixGamma[1][0] = -Math.sin(gamma); matrixGamma[1][1] = Math.cos(gamma); matrixGamma[2][2] = 1; matrixGamma[3][3] = 1; g.setColor(Color.white); g.clearRect(p.getX(), p.getY(), p.getWidth(), p.getHeight()); Point3D [] points2 = new Point3D[8]; for (int i = 0; i < 8; ++i) { double [] row = new double[4]; row[0] = points[i].getX(); row[1] = points[i].getY(); row[2] = points[i].getZ(); row[3] = 1; row = multiply(row, matrixAlpha); row = multiply(row, matrixBeta); row = multiply(row, matrixGamma); points2[i] = new Point3D(row[0], row[1], row[2]); drawDot(g, row[0], row[1]); }
    面染色采用 P497 画家算法, 先对平面的Z重心进行排序, 由远及近进行染色.
    // fill in color double [] center = new double[6]; for (int i = 0; i < 6; ++i) { center[i] = avg(points2, facet[i]); } int [] idx = new int[6]; for (int i = 0; i < 6; ++i) { idx[i] = i; } for (int i = 0; i < 6; ++i) { for (int j = i+1; j < 6; ++j) { if (center[idx[i]] > center[idx[j]]) { int t = idx[i]; idx[i] = idx[j]; idx[j] = t; } } } for (int i = 0; i < 6; ++i) { Polygon polygon = new Polygon(); for (int j = 0; j < 4; ++j) { int nodeId = facet[idx[i]][j]; polygon.addPoint(transform(points2[nodeId].getX()), transform(points2[nodeId].getY())); } g.setColor(penColor[idx[i]]); g.fillPolygon(polygon); }遇到的困难
    Point3D类属于javafx包, 不是标准包, 最终改为自定义的类
    图形渲染闪烁问题, 从AWT转化为Swing, 用其框架自动的Double-Buffering来避免闪烁问题, 其中在画图的时候应传递同一个Graphics 否则会导致画布刷新, 从而展示不出新的东西.

    参考文献
    http://docs.oracle.com/javase/tutorial/extra/fullscreen/doublebuf.html
    http://www.cnblogs.com/muding/archive/2013/02/19/3031447.html
    http://www.vineetmanohar.com/2009/11/3-ways-to-run-java-main-from-maven/
    http://mojo.codehaus.org/exec-maven-plugin/
    http://www.ibm.com/developerworks/cn/java/l-java3d/
    0 评论 1 下载 2019-08-09 13:58:24 下载需要8点积分
  • 基于C#和SQL SERVER的企业进销存管理系统的设计和实现

    摘要进销存管理是现代企业生产经营中的重要环节,是完成企业资源配置的重要管理工作,对企业生产经营效率的最大化发挥着重要作用。本文以我国中小企业的进销存管理为研究对象,描述了企业进销存管理系统从需求分析、系统设计、系统实现到系统测试的全周期。在本次设计确定了进销存系统的基础功能需求,深入剖析了企业进销存的业务流程,完成了基础设置模块、客户管理模块、产品管理模块、进销存管理模块和系统管理模块的功能设计。分析部分对系统的概念、特点、运行环境及数据库设计进行了分析描述;设计部分根据系统的总体描述、模块及结构图提出了系统的总体设计方案内容;实现部分介绍了系统中各个模块的实现方法,做到客观实际,具有通用性。
    关键词:进销存管理系统;.NET;B/S 模式;SQL
    ABSTRACTInventory management inmodern enterprise production and management is an important aspect. It alsoplay an important role to maximize the efficiency of the production andoperation of enterprises. Taking inventory management of SMEs in China for thestudy, this page describes the enterprise inventory management system fromrequirements analysis, system design, system implementation to a full cycle ofsystem testing. In this page to determine the basis functional requirements of Invoicingsystem, in-depth analysis of the business processes of enterprises Invoicingcompleted basic settings module, customer management module, product managementmodule, inventory management module and system management module functionaldesign. Part of the concept of systems analysis, features, operatingenvironment and database design are analyzed description; according to thegeneral description of the design part, modules and structural diagram of theproposed system design program content; implementation section introduces theimplementation of the system in each module methods to achieve the objectivereality, with versatility.
    Keywords: Inventory managementsystem; .NET;B/S module; SQL
    第1章 绪论1.1 课题背景随着现代企业管理理论的发展,以及在新形势下信息化技术在现代企业管理的不断应用,许多中小企业在激烈的市场竞争中也开始注意到企业信息化管理的重要性,部署了贴合自身业务的小型管理系统。但当前许多中小企业实现的管理信息系统都是非常独立的系统,采购、销售、库存管理都有独立的系统。大量的系统不利于数据共享和综合的统计分析。因此,中小企业迫切需要部署一个综合的进销存管理系统以实现数据的统一管理。进销存管理系统以库存管理为基础,主要完成企业的采购管理、销售管理、库存管理、客户管理和产品管理等工作,通过对销货进货、库存变更等操作过程的管理,为管理者提供详细企业运营状况,帮助企业打到采购、库存、销售的平衡,降低企业成本。
    1.2 国内外现状美日韩等发达国家因工业水平和计算机技术起步较早走在了世界的前列。自 20 世纪 60 年代起发展了许多实现企业全面管理的企业资源计(ERP系统)。这些 ERP 系统中均有进销存管理相关的模块[1]。但中小企业由于规模与资金限制,只在部分业务普及了信息化。这些软件系统中均具有进销存管理的相关业务模块,例如Communication Oriented Production and Information ControlSystem (COPICS)、商业计划和控制系统(BPCS)、NIPPON TELEGRAPH AND TELEPHONECORPORATION (NTT)系统,业务模块具有较高的集中度,同时也易于扩展,全世界许多企业使用这些管理系统来完成企业的进销存管理工作。美国的一份数据统计表明采用利用软件对企业进销存管理,可减少40%的积压库存,12%的企业生产力增[2]。
    在经济化浪潮下国内也涌现出一批优秀的进销存系统,如用友软件、金蝶公司、浪潮等的 ERP 系统,都在国内得到了广应用。但当前我国进销存管理系统存在的主要缺陷:①软件功能复杂。许多进销存管理系统包括从物流、生产到财务等多个领域,但许多功能并不适用于中小企业;②进销存信息单一,且缺乏对数据的分析功能。无法对进销存信息进行实时分析;③系统网络化程度低。许多传统的进销存管理系统只支持在局域网中进行使用,更无法满足当前网络经济时代的要求。国内进销存管理系统的研究已经从点到面,又向细节逐步深入的阶段,在各方面细节上不断完善系统,为企业进销存管理打造一个坚实的根基[3]。
    1.3 课题的目的和意义本课题的目的是研究设计实现企业进销存管理系统,并通过该系统实现以下目标:

    模块化进销存业务,使企业需求更加明了,方便用户操作
    灵活分配企业员工权责,优化管理
    实现中小企业进销存管理的持续使用

    本课题的意义如下:

    实时下订单
    实时查询库存数量
    为企业管理者提供科学的决策数据

    1.4 本文组织结构
    第一章为绪论,大致介绍本课题的研究背景以及国内外进销存管理系统的现状,以及应用进销存管理系统的主要意义
    第二章为开发进销存管理系统的相关技术,本章主要介绍了开发系统时使用的技术、语言、数据库技术和软件架构等方面
    第三章为进销存管理系统的分析与设计,本章主要说明了该系统的需求、系统的模块规划,以及系统数据库的设计及相应的截图
    第四章为进销存管理系统的实现,本章主要说明了系统的开发环境,介绍了系统各个模块所实现的功能
    第五章为总结与展望,本章主要总结了本系统的开发工作,并指出系统存在的不足,有待改进

    第2章 相关技术介绍2.1 ASP.NET技术ASP.NET技术是用微软开发并推行的,是一种使嵌入网页中的脚本可由因特网服务器执行的服务器端脚本技术,可用于在服务器上生成功能强大的Web应用程序,是ASP技术的升级版。ASP.NET支持多种类型语言的编程开发,其中包括常用的Visual Basic .NET、C#、Perl等语言。因为ASP .NET是基于通用语言的编译运行的程序,其实现完全依赖于虚拟机,所以它拥有跨平台性,ASP .NET构建的应用程序可以运行在几乎全部的平台上[4]。同时,ASP.NET还有许多功能强大的组件,例如DataList、DetailList和Gridview等,可以实现快速简便地与数据库进行连接,并且它本身还自带了服务器空间,简单好用[5]。
    2.2 C#语言C#是微软公司发布的一种面向对象的、运行于.NET Framework之上的高级程序设计语言。它是由C和C++衍生出来的,同时又综合了VB简单的可视化操作和C++的高运行效率,以其强大的操作能力、优雅的语法风格、创新的语言特性和便捷的面向组件编程的支持成为.NET开发的首选语言。
    2.3 SQL Server数据库SQL Server数据库是一个关系数据库管理系统,是一个可扩展、高性能、为分布式客户机/服务器计算所设计的数据库管理系统。它实现了与WindowsNT的有机结合,提供了基于事务的企业级信息管理系统方案[6]。SQL Server数据库具有如下特点:

    高性能的设计。SQL Server数据库可以充分利用Windows NT的优势
    系统管理的先进性。它支持Windows图形化管理工具,支持本地和远程的系统管理和配置
    强大的事务处理功能。SQL Server数据库采用了多种方法,以保证数据的完整性
    支持对称多处理器结构、存储过程、ODBC,并具有自主的SQL语言。SQL Server数据库以其内置的数据复制功能和强大的管理工具,以及其与Internet的紧密集成和开放的系统结构,为广大用户、开发人员和系统集成商提供了一个出众的数据库平台

    2.4 B/S结构B/S结构(即浏览器/服务器结构)是WEB兴起后的一种网络结构模式,其主要是利用日渐成熟的WWW浏览技术,结合VBScript和JavaScript等Script语言,再加上ActiveX技术,将系统功能实现的核心部分转移到了服务器上,简化了系统的开发,是一种全新的软件系统构造技术[7]。用户通过自己电脑的浏览器就可以在家访问数据库,不需要特别安装软件,使得系统的拓展十分便利。
    2.5 AJAXAjax是一种创建交互式网页的web开发技术。它并不是一种单一的、新的技术,而是由Javascript、CSS、Dom、XMLHttpRequest这四中技术集合而成的。它由JavaScript语言写成,使用CSS控制页面的样式,通过DOM使用模型,通过XMLHttpRequest向后台发送数据。Ajax技术很多时候被用于实现页面的局部刷新,大大提高了用户体验感。
    2.6 AndroidAndroid是一种基于Linux的自由及开放源代码的操作系统,主要使用于移动设备,如智能手机和平板电脑,由Google公司和开放手机联盟领导及开发。一个Android 应用是由多个Activity 组成的。这多个Activity 之间 可以进行相互跳转,例如,按下一个Button按钮后,可能会跳转到其他的Activity。和网页跳转稍微有些不一样的是,Activity 之间的跳转有可能返回值,例如,从Activity A 跳转到Activity B,那么当Activity B 运行结束的时候,有可能会给Activity A 一个返回值。这样做在很多时候是相当方便的。
    2.7 本章小结本章主要介绍了在进销存管理系统中使用的相关技术,例如C#和SQL Server数据库的相关介绍,B/S结构的简单阐述以及AJAX的原理。
    第3章 系统分析与设计3.1 系统需求分析进销存管理系统的开发过程中包括了前期准备,市场调研、系统分析、系统设计以及系统实现、测试、评价等步骤。由于调研对象主要集中在中小企业,所以其中的一些功能只做了一些简单的设计。根据系统开发的大小、规模以及应用范围的不同,各个步骤的要求和内容可能不尽相同。
    3.1.1 业务流程分析为了使用户能够更加直观地了解整个系统的业务关系、工作顺序以及相应的管理信息的流向,在此给出了本系统的业务流程图,如图3-1所示。本系统是进销存管理系统,主要实现企业库存的信息管理,设定价表、入货出货和实时查询企业库存[8]。其中,订单模块是本系统的重点。
    如图3-1所示,在本系统中,管理员拥有系统全部基础功能。如新增客户,新增产品和销售区域,新增业务代表等基础信息维护;设定价盘表,可按照不同销售区域对产品进行差异化定价;调整并审核销售单、退货单、收预收款等业务操作。管理员新增业务代表并审核通过后可由业务代表帮助客户下预售单,预售单在管理员审批通过后可由库管发货,审核期间可组合选择付款方式(预收款付款,先进结款等)。
    业务代表在本系统中扮演了重要角色,承担了企业与客户沟通的桥梁,在线下跑订单,下预售订单等都由业务代表完成。业务代表是可以关联车辆的,企业的每一辆运货车可以由一个司机加一个业务代表组合负责。司机负责货运,业务代表负责在手机APP为客户下订单,可下订单包括销售单,预售单,退货单,在手机端业务代表可任意组合自己的付款方式,灵活完成客户订单需求。
    产品价表是本系统的重点,管理员可针对不同渠道不同区域的不同产品差异化定价,尽可能符合当前区域的经济需求。管理员亦可对产品设定活动,活动包括买赠和减免,对于同一客户的下单可直接给业务代表赠送的权利,简化买赠活动为免费赠送单。灵活的定价方式可满足不同区域的消费需求。

    3.1.2 数据流程分析从业务流程图中,我们可以比较直观地了解到整个系统中各个实体以及各个模块之间的业务关系,以及作用顺序,但是对于各类信息的具体处理、存储及流向方面,则不是十分详细。而为了更加清晰地了解一个系统中的数据信息流向和处理存储过程,本文中给出了数据流程图。如图3-2所示,是本系统的数据流程图,从下图我们可以比较明确地知道整个系统的数据走向。

    3.1.3 功能性需求分析进销存管理系统的角色分为系统管理员、业务代表、司机、客户。
    系统管理员可以由系统使用方指定对象担任,人数由使用方决定,系统管理员不需要注册。系统管理员主要管理整个系统中基础信息的调整和订单审核,包括新增业务代表、新增产品、设定产品价表等。具体功能要求如下:

    管理授权:注册其他角色时需要在管理员授权
    修改权限:系统管理员可以根据需求修改系统人员和商品的信息的权限
    业务权限:系统管理员可以对订单进行审核发货和取消
    信息维护:系统管理员可以对整个系统中的信息进行维护
    审核功能:根据新增基础信息需求,审核信息

    业务代表是由管理员添加并审核通过的注册者,在系统中具有下预售单和车销单的权限。具体功能如下:

    客户管理:具有新增客户、修改已存在客户资料等功能
    车销管理:下车销订单
    预售管理:下预售订单
    库存管理:可申请移库,由主仓库移动至车仓库或由车仓库退货
    退货管理:可以查看有关自己的留言,回复他人对自己的提问
    其他信息:订单查询等

    司机是由管理员添加并审核通过的注册者,在系统中具有库存调拨的权限。具体功能如下:

    调拨管理:车销移库或退库
    订单管理:在车销订单中收款

    3.1.4 非功能性需求分析非功能性需求,指的是除了用户所需的功能要求以外,还应该满足的一些其他方面的要求。本系统以下几个方面进行分析:

    系统安全性:防止无权限人员进入系统修改相关信息[14]。若操作系统能给数据库用户分配角色,则安全性管理者必须要有修改操作系统用户安全性区域的操作系统权限
    性能需求:用户在软件响应速度、结果精度、运行时资源消耗量等方面的要求。如对计算机系统的要求,对工作频率、容量、存储量等方面的要求
    易操作需求:界面简单明了,命名明确易懂。如一些系统管理员不太熟悉电脑操作,系统界面必须一目了然、简单易懂、容易上手
    适应性:当外部条件有所改变的时候,系统根据外界因素改变。如时间变化,系统根据当前时间删除发布招募信息时过时时间的选项

    3.2 系统模块划分根据以上分析,可以将库存管理信息管理系统分为基础信息管理、采购管理、销售管理、库存管理、财务管理、报表平台等几个模块。系统管理员主要有信息的维护、审核、权限管理等功能具体,业务代表具有订单权限和新增客户权限,如图3-3所示:

    3.3 系统数据库设计在一个系统的开发过程中,数据库设计可谓是至关重要,一个数据库的设计成功与否,直接关系到整个系统的开发,甚至会影响到整个系统的成败。
    3.3.1 概念设计概念设计是由分析用户需求到生成概念产品的一系列有序的、可组织的、有目标的设计活动,它表现为一个由粗到精、由模糊到清晰、由抽象到具体的不断进化的过程。现在常用的概念模型是E-R模型。在本系统中,各实体直接的关系如图3-4所示:

    3.3.2 逻辑设计逻辑设计是将概念结构转变为某个DBMS支持的数据模型,也可称为优化。将本系统转换后就得到如下关系模型:

    商业客户基本信息表(ID,客户编号,全称,简称,行政城市,邮政编码,客户地址,送货地址,联系人姓名,电话号码,手机号码,传真,Email,企业网址,首要联系人,客户开门时间,客户打烊时间,营业执照号,组织机构代码证号,税务登记证号,客户类型,所有权属性,所有权人,备注,审核标志,新增日期,新增人,更新日期,更新人)
    供货商合作关系表(ID,客户ID,供货商,客户自编码,业务人员,客户合作状态,开始合作日期,截止合作日期,供货价盘表,渠道,区域,销售路线,拜访顺序,拜访模板,备注,审核标志,新增日期,新增人,更新日期,更新人)
    员工信息资料表(ID,姓名,性别,员工编号,身份证号,出生日期,行政城市,地址,邮编,电话号码,手机号码,电子邮件,开始工作日期,截止工作日期,管理片区,职务,在职状态,所有权属性,所有权人,备注,审核流程,审核标志,新增日期,新增人,更新日期,更新人)
    商品基本资料表(ID,产品名称,产品简称,商品编码(平台级编码),规格,整件单位,零售单位,包装系数,整件商品条码,零售商品条码,商品种类,商品品牌,商品类别,段位,整件重量,整件体积,厂家名称,生产厂商,厂家商品码,厂家商品ID,标准经销价,标准分销价,建议零售价,成本价,状态,保质期,所有权属性,所有权人,备注,审核标志,新增日期,新增人,更新日期,更新人)
    订货单信息(ID,订单号,供货商,订购商,业务员,供货价盘表,类别,状态,折扣金额,优惠金额,实际订货金额,要求送达时间,要求送达仓库,提交时间,确认时间,关联拜访记录,备注,审核标志,新增日期,新增人,更新日期,更新人)
    发货单信息(ID,单号,发货商,收货商,发货商仓库,收货商仓库,业务员,送货人,类别,制单模式,状态,供货价盘表,关联订货单,折扣金额,优惠金额,实际销售金额,送货车辆,预计送达时间,装车配货时间,发车时间,实际到达时间,关联拜访记录,备注,审核标志,新增日期,新增人,更新日期,更新人)
    实时库存表(ID,仓库,商品,批号,生产日期,数量,价格,最后更新时间,新增日期,备注)

    3.3.3 物理设计物理设计指的是根据数据库的逻辑结构来选定RDBMS,并设计和实施数据库的存储结构、存取方式等。本系统采用SQL Server数据库,根据以上的分析选取了主要的几张表展示,表之间的关系如图3-5所示

    3.4 本章小结本章节作为全文比较重要的一个章节,具体介绍了进销存管理系统的需求分析,其中包括了功能性需求分析和非功能性需求分析,分别从概念、逻辑、物理这三方面进行了说明。
    第4章 系统实现4.1 系统开发环境的选择本系统的开发是基于.NET平台,以VS2012为开发环境,将C#作为主要开发语言,SQL SERVER为数据库软件。
    4.2 系统通用方法的实现本系统是进销存管理系统,系统重心在下单及库存判断上,但是作为一个应用型系统,其他功能模块也是必不可少的。系统实现过程中运用了大量的ASP.NET控件,例如AJAX、GridView、DataList等。在本节中将介绍通用模块的实现方法。
    4.2.1 连接数据库在一个系统中,数据库的连接可以说是核心部分,至关重要。在本系统中,通过web.config文件增加数据库连接字符串[9],主要代码如下:
    <connectionStrings> <addname="VolunteerConnection"connectionString="Data Source=UGYRPLW5EAMIHEB;Initial Catalog=Volunteer;Persist Security Info=True;User ID=sa;Password=123456"providerName="System.Data.SqlClient"/></connectionStrings>
    并在App_Code文件夹中添加数据库连接方法connet(),在后续的系统功能程序编写中,全部通过调用该方法来实现数据库的连接,主要代码如下:
    privatestaticstring connectString = ConfigurationManager.ConnectionStrings["VolunteerConnection"].ConnectionString;publicstaticSqlConnection Connect(){SqlConnection con = newSqlConnection(connectString);return con;}
    4.2.2 数据的绑定显示作为一个数据管理系统,需要将系统中的部分数据展示出来,因此,数据的显示操作也是十分重要的[10]。在本系统中,数据的显示主要使用了DataList和GridView控件,控件类型的不同导致了这两种控件在使用方法上的差异。在DataList控件中,本文主要采用了DataSet方法来填充数据,该方法处理脱机数据,在多层应用程序中很有用,并且可以在任何时候查看DataSet中任意行的内容,允许修改查询结果的方法,还可以处理分级数据。在本系统中,该方法被用于每一个信息展示页面,例如订单列表页面,采购单列表页面,产品列表页面等,主要代码如下:
    SqlDataAdapter sqlda = newSqlDataAdapter(sqlstr, sqlconn);DataSet ds = newDataSet();sqlda.Fill(ds, "dtxx");
    GridView控件主要使用在系统管理员模块中,通过该控件自带的分页排序功能,可以使得管理员的操作更加简便快捷。GridView控件主要是通过使用SqlDataSource数据源控件来连接数据库的,所以在此不再累赘。
    4.2.3 分页栏DataList控件本身并不带有分页功能,为实现在使用DataList控件时也能方便分页,本系统使用了PagedDataSource数据源方法来绑定DataList控件,使其也能像GridView控件一样实现分页[11]。该方法主要通过PagedDataSource和DataSet方法相结合使用,主要代码如下:
    DataSet ds = newDataSet();sqlda.Fill(ds, "dtxx");PagedDataSource pds = newPagedDataSource();pds.DataSource = ds.Tables["dtxx"].DefaultView;this.DataList1.DataSource = pds;this.DataList1.DataBind();
    4.2.4 页面局部刷新和跳转在本系统中,因为牵涉到大量数据的分页方法,势必会造成在页面刷新过程中的卡顿和不流畅现象,为解决这一问题,本系统还使用了AJAX控件,来实现页面的局部刷新方法[12]。因为AJAX是开发环境中自带的一款封装的控件,所以在使用上也是非常方便的,主要代码如下:
    <asp:ScriptManagerID="ScriptManager1"runat="server"></asp:ScriptManager><asp:UpdatePanelID="UpdatePanel1"runat="server"><ContentTemplate> ……</ContentTemplate></asp:UpdatePanel>
    在AJAX控件中,无法使用Response.Write(“”)方法进行页面信息框的弹出提示已经页面的跳转,所以AJAX也有自己自身的一种提示方法,主要代码如下:
    ScriptManager.RegisterStartupScript(this.Page, this.GetType(), "click", "alert('已完成下单!');location.replace('dygl.aspx');", true);
    4.3 系统主要功能的实现如上节所述,本系统是进销存管理系统,订单模块是本系统的主要模块,该模块实现了下单和查询功能,以及后续的一些统计功能[14]。
    本系统实现的功能主要由两部分组成,即订单和库存这两个模块的功能。进入本系统时需要登录,进入系统会自动根据身份出现可浏览模块。界面如图4-1和4-2所示:


    4.3.1 系统管理员功能系统管理员是由系统开发者或者系统购买者直接指定的,不需要注册,系统管理员的信息直接由开发人员写进数据库中。但是要注意的是,系统管理员的选择应该慎重[15]。在本项目中系统管理员除了可以修改自己的个人信息之外,还有以下众多主要功能:
    1.基础资料管理:企业员工注册客户成功之后就无法自行修改,如果注册时用户信息填写错误,用户可以向管理员线下提起申请,管理员通过用户信息管理界面进行用户信息的修改。系统管理员也可以在该功能模块进行用户权限的修改。该页面主要使用GridView控件实现,在一定程度上节省了开发难度和开发时间,同时使用代码和textbox控件,可以通过关键字搜索用户信息,方便管理员对用户信息的维护。基础资料页面都提供对各类信息的检索方式,如客户界面的关键字会在后台自动匹配客户的名称,编码,简称等属性,而新增日期的时间段则会自动筛选所有符合录入时间的客户,合作状态可选取一种状态,也可选择所有状态下的客户。界面如图4-3所示:

    2.商品信息管理:本系统提供了商品管理这一模块,具有商品操作权限的用户可以自行为企业添加商品,并为产品设定不同类别。每个产品都可以归属于一个类别,可通过类别对产品进行筛选。界面如图4-4所示:

    3.采购管理:企业作为盈利组织,势必要投入一部分成本,而成本的一个重要组成部分便是销售产品的采购。本系统提供企业对产品的采购功能,采购数据会纳入企业进销存体系,作为库存盘点的数据来源。录入采购单据时刻选择企业自身录入的供货商,选择企业的一个仓库作为入库仓库,已完成的采购单管理员可在后台予以确认,采购单据页面会自动罗列出所有的采购商品和采购的总金额,界面如图4-5所示:

    4.订单管理:企业要实现盈利就必须有销量,在实际业务过程中业务代表承担了销售的大部分责任。对于小规模客户业务代表可跟车进行车销现款现货交易,对于大客户业务代表则可通过拜访客户下预售订单的模式销货,预售单在管理员审核通过后可进行派单。订单页面与采购单页面相似,不同的是企业自身已作为供应商,而订购上则是企业客户。订单页面会显示下单的业务员和销售类型及各类采购产品明细,界面如图4-6所示:

    5.库存管理:系统管理员可以在该模块中查看现有个库存中各产品的数量,也可在各仓库之间进行调拨操作并按期对仓库进行盘点。界面图如图4-7所示:

    库存盘点是系统的一个重点功能,用户可在新增盘点单的界面选择需要盘点的仓库,此时界面会以GridView的形式罗列出所有在售产品的现有库存量,并提供一个下拉列表选择盘盈或者盘亏并以文本框形式输入数量。对于每一个产品的库存,对应于后台PBM_DeliveryDetail数据表中的一行数据,在保存时循环GridView的每一行在PBM_DeliveryDetail中插入数据。
    6.价表管理:价表是进销存管理系统的基础,决定了系统的复杂程度。本系统允许多张报表同时存在,默认设置第一张价表为主价表。管理员可新增自定义价表关联渠道或客户类型,也可同时与渠道和客户类型绑定。价表界面图如4-8所示:

    在价表管理页面用户可看到当前价表关联的区域和取到以及是否为默认价表。用户可根据商品分类或关键字查找在价表中或不在价表中的商品,以审核的价表,对于不需要再出现在价表中的产品允许用户进行批量删除,同时不在价表中的产品也可添加到价表并自定义价格。
    4.3.2 业务代表功能
    如图4-9所示,为业务代表操作流程图。业务代表由管理员注册并分配账号后方可登陆系统,主要进行下车销单和预售订单操作。具体操作在手机端完成。用户具体功能如图4-10所示:

    客户信息管理:客户信息管理又分为以下几个功能
    客户信息新增——业务代表可根据实际新增自己管辖范围内的新客户,如果需要修改某些自己无权修改的信息时,可以向管理员申请修改。该模块通过手机APP的形式展现。如图4-11所示:
    客户拜访管理——业务代表在自己管辖的片区内可按照预定线路拜访客户,为提高拜访真实性系统提供对客户门头拍照功能。如图4-12所示:

    订单管理:业务代表对自己线路下的客户有下单权限,具体功能如下:
    车销订单—业务代表在随车拜访客户时可帮助客户下车销单。该模块位于手机APP上,如图4-13所示:预售订单—业务代表在自己管辖区域内拜访大客户时可帮助客户下预售单。该模块位于手机APP上,界面如图4-14所示:

    订单查询——业务代表在APP下单后可在PC端对自己的下单详情查看。界面如图4-15所示:

    4.4 本章小结本章主要描述了本系统的各个模块的功能实现,通过文字叙述和截图的方式展现了本系统的功能模块,分别从系统管理员和业务代表的角度介绍了本系统的功能。
    第5章 总结与展望5.1 总结在信息化建设日益普遍的今天,信息系统的使用以及愈加广泛,为了优化进销存的管理,本文在了解了各种进销存管理系统之后,开发设计了进销存管理系统,并做出如下介绍。
    本文的第一章着重介绍了进销存管理系统这一课题的背景和意义,以及它的研究目的,同时也介绍了本文的内容结构。第二章着重介绍了本系统的一些相关的技术概念,比如ASP.NET技术,SQL数据库等。第三章是本文的一个重点,主要内容是需求分析以及数据库的设计等。第四章详细介绍了本系统的一些功能的实现。
    本系统的重点在于管理员对商品的管理,业务代表下单和库存管理等;难点是针对不同区域和渠道设定不同的产品价格,创新点有系统权限的分配,对财务管理的纳入等。
    经过这段时间的学习和实践,我对课堂掌握的知识有了一个更好地巩固,对不理解和不熟练的一些方面也有了一个更清晰的概念,比如C#语言的应用,SQLServer数据库的应用,以及DIV+CSS的网页布局等方面。
    5.2 展望当然,由于技术方面的因素,本系统的许多方面还存在不足,比如:

    前期的数据库设计问题,导致在系统开发过程中需要不断修改数据库设计,使得数据库的设计不是十分规范
    目前本系统只采用了B/S架构进行开发,并没有结合C/S架构
    由于技术和设备问题,本系统无法自动提示用户相关的信息动态,只能是让用户经常登录自己进行查询
    除此之外,由于目前手机应用日益发达,开发手机APP势在必行,但由于时间和技术问题APP开发暂时只能落于纸上

    参考文献][1]郭旭.基于.NET的企业进销存管理系统的设计与实现[D].吉林大学,2014.
    [2]王洪迪.基于ASP.NET的燃气具企业的进销存系统设计与实现[D].合肥工业大学,2012.
    [3]于瑶瑶.企业进销存管理系统的设计与实现[D].山东大学,2009.
    [4]李洪涛.面向中小家电企业进销存管理系统的设计与开发[D].合肥工业大学,2010.
    [5]赵晓霞. 进销存管理系统的设计与实现[J]. 煤炭技术,2010,01:244-246.
    [6]赵韩,李洪涛,陈科. 基于ASP的中小企业进销存管理系统研究[J]. 微计算机信息,2010,33:43-45.
    [7]庄翔翔. 基于B/S架构的中小企业进销存管理系统的设计与实现[D].中国海洋大学,2010.
    [8]张彦芳,王春艳. 进销存管理系统的设计与实现[J]. 中国管理信息化,2008,07:14-16.
    [9]张岩,张宁. 基于C/S架构的超市进销存管理系统的设计与实现[J]. 北京石油化工学院学报,2013,04:45-50.
    [10]陶俊. 基于.NET的进销存管理系统设计与开发[J]. 江汉大学学报(自然科学版),2007,01:55-57.
    [11]王会青. 医药进销存管理系统设计与实现[J]. 山西科技,2007,03:57-58.
    [12]翁国秀,李露璐. 企业产品进销存管理系统的设计与实现[J]. 电脑知识与技术,2011,07:1493-1494+1506.
    [13]朱士高,朱军,朱彩霞. 基于WEB的企业进销存管理系统的设计[J]. 淮阴工学院学报,2007,05:62-64.
    [14]Xiao-yunJIANG,Pin CHEN,Rong ZHENG Department of Management Science,Xiamen University ofTechnology,Xiamen,China. Study of Modeling and Simulation of Flexsim-basedInventory Management System[A]. 中国机械工程学会工业工程分会、IEEE北京分会.Proceedings of 2010 IEEE the 17th International Conferenceon Industrial Engineering and Engineering Management(Volume 2)[C].中国机械工程学会工业工程分会、IEEE北京分会:,2010:4.
    [15]KaiWANG. The Research of Inventory Management Modes Based on Supply ChainManagement[A]. Chinese Industrial Engineering Institution,CMES、Institute ofElectrical and Electronic Engineers、Beijing Section.Proceedings of2012 IEEE 19th International Conference on Industrial Engineering andEngineering Management(IE&EM 2012)[C].Chinese Industrial EngineeringInstitution,CMES、Institute ofElectrical and Electronic Engineers、Beijing Section:,2012:5.
    2 评论 40 下载 2018-11-13 14:09:18 下载需要7点积分
  • 基于C++的菜鸟驿站

    1 绪论1.1设计背景随着社会的发展,互联网的兴起,网络购物渐成为人们购物的新时尚。快递行业也在网络购物的带动下蓬勃发展,成为人们生活中必不可少的一部分。所以一个智能的快递管理系统,会大大的方便人们的生活,提高快递运送的效率。
    随着快递数量的不断增多,如何将快递安全、准确的送到每一位消费者手上成为了一个问题。而一个智能的快递管理系统可以提高工作人员的效率,帮助工作人员快速清点货物信息。由于快递会在每个地区都设立站点,所以我们设计了一款名为菜鸟驿站管理系统的快递管理系统,用来帮助工作人员高效的完成工作。
    1.2 设计目标针对快递管理的实际需求,采用C语言作为开发语言,使用文件.txt作为数据库,设计并且开发了一个菜鸟驿站管理系统。系统主要包括用户功能模块和管理员功能模块。用户功能模块包括用户注册、用户快递查询、用户快递领取、用户信息修改等功能。管理员功能模块包括录入用户信息、删除用户信息、查询用户信息、修改用户信息、录入快递信息、删除快递信息、修改快递信息、查询快递信息、统计快递领取情况等功能,实现快递管理的信息化,提高工作效率。
    1.3 相关技术介绍本系统采用C语言编写,使用VS2015作为编译器编写系统程序代码,整个系统以.txt作为数据存储的文件。
    1.3.1 C语言简介C语言是一门通用计算机编程语言,广泛应用于底层开发。C语言的设计目标是提供一种能以简易的方式编译、处理低级存储器、产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。尽管C语言提供了许多低级处理的功能,但仍然保持着良好跨平台的特性,以一个标准规格写出的C语言程序可在许多电脑平台上进行编译,甚至包含一些嵌入式处理器(单片机或称MCU)以及超级电脑等作业平台。二十世纪八十年代,为了避免各开发厂商用的C语言语法产生差异,由美国国家标准局为C语言制定了一套完整的美国国家标准语法,称为ANSI C,作为C语言最初的标准。目前2011年12月8日,国际标准化组织(ISO)和国际电工委员会(IEC)发布的C11标准是C语言的第三个官方标准,也是C语言的最新标准,该标准更好的支持了汉字函数名和汉字标识符,一定程度上实现了汉字编程。
    1.4 文档组织结构本文第一部分绪论主要说明了系统的设计背景,设计目标以及C等相关技术;第二部需求分析阐述了系统功能和可行性分析,介绍了系统各个功能模块,并描述了系统的用例图和活动图,可行性分析介绍了系统开发的必要性和可能性;第三部分系统设计则介绍了系统结构设计;第四部分系统实现主要讲了登录模块、管理员模块和用户模块实现的功能;第五部分系统的测试主要介绍了对该系统的测试方法和详细过程。
    2 需求分析2.1系统功能概述系统包括两种不同权限的用户:第一种是用户,即普通用户,这类用户能够通过手机了解最新的快递动态信息,修改自己的个人信息、查询快递信息、注册账号等;第二种是管理员用户,管理员可以管理用户信息,管理和审核账户信息,管理和审核快递信息,还负责管理维护系统等。因此系统的设计可以分成三部分,用户模块、管理员模块和登陆模块。
    普通用户:

    使用手机、电脑来使用该系统;
    使用自己的账号注册并登陆系统;
    新用户注册成功之后,使用唯一的账号作为ID来识别;
    登陆系统可以查看个人信息和快递信息、修改个人信息等;
    不使用时可以退出登陆系统;
    可以根据单号查询和领取快递;

    管理员用户:

    通过管理员的用户名和密码登陆系统的管理员端;
    登陆系统后具有管理系统用户账户信息的功能;

    登陆后具有管理用户信息的功能,可以对用户信息进行增、删、改、查等操作;
    可以管理用户的快递;
    可以统计快递的领取情况,有无滞留时间过长的快递;
    可以设置新的管理员;
    可以查看快递的其他信息等。

    2.2 系统执行流程描述通过研究快递管理系统的需求分析,可以将系统设计成了三个主要模块,这三个模块是登陆模块,管理员模块,用户模块。

    登录模块,可选择用户登录和管理员登录,用户登录界面可以选择新用户注册和老用户登录具体的活动图如图所示。


    管理员模块,通过这个模块能够查看,添加,删除以及修改用户信息,能够修改快递、删除快递、增加快递、查询快递等操作。在管理员模块里面,所有的具体操作的是嵌套的,详细的活动图如图所示。


    用户模块,用户通过这个模块能够查询或者修改个人信息,能够查询快递信息和领取快递。每一个操作都是平行和嵌套的,该活动图如图所示。

    3 系统设计3.1 系统结构设计根据系统需求分析,可以设计出整个校友信息管理系统的总体结构。由于本系统包含两种不同权限的用户,每种用户进入系统后所能用的功能和权限不一样,因此在对系统结构设计时要分别考虑。

    管理员页面结构设计


    校友用户页面结构设计

    4 系统实现4.1 登陆模块实现用户注册模块。打开系统,进入欢迎界面,选择用户,如下图4-1所示。还没有账号可以点击注册,注册的时候输入账号、手机号、密码,然后点击注册的时候系统会调用regs()方法,该方法会给用户注册,然后将注册信息插入用户信息表中,然后就可以使用该账号登陆系统了。

    用户和管理员登陆。输入账号、密码,然后选择用户或者管理员,点击登录时系统会调用stu_land()方法,该方法会通过查询数据库相应的表来判断输入的信息是否正确;如果账号和密码都成功的在数据库中匹配成功的话,系统就会自动判断输入的账号和密码都正确,然后就会自动跳转至用户界面或者管理员界面;如果账号或者密码中的其中有一个没有在数据库中匹配成功话,管理系统就会弹出提示框,并且会显示相关的错误提示。
    4.2 管理员模块实现首页模块。输入正确的管理员登陆信息之后,系统判断用户名,密码和数据库中的信息匹配之后就会跳转到本系统的管理员首页,如图所示。

    维护用户信息模块。管理员首页通过后台的add_student()、display_all_student()和dele_student()方法查询、修改数据库中的用户信息,如图所示。

    录入用户信息。录入用户的账号、手机号等信息,录完之后,输入y保存,输入n取消,最后按enter返回,如图所示。

    浏览用户信息。可以浏览到用户的账号、姓名、手机号码和登录密码等基本信息,按enter键返回,如图所示。

    删除用户信息。输入要删除的学生账号,系统会自动删除该账号内的所有信息,如果输入账号信息有误,会提示错误,按enter键返回,如图所示。

    维护货物模块。选择维护货物模块,系统就会跳转到维护货物模块页面。如图所示。

    录入货物信息。按单号、手机号,系统会实时录入货物入库时间,并付给货物未领取的状态,录完之后,输入y保存,输入n取消,最后按enter返回,如图所示。

    浏览货物信息。可以浏览到用户的快递单号、手机号、入库时间、状态和取货码等信息,按enter键返回,如图所示。

    删除货物信息模块。如图所示。

    修改货物信息模块。如图所示。

    未取货物信息模块。如图所示。

    4.3 用户模块实现用户首页模块。以用户身份登录,同时输入的账号和密码都正确就会跳转到图所示的校友用户首页,显示用户模块的所有功能。输入对应的功能数字就可以使用相应的功能。操作之后系统就会待用户操作完成后,然后就会全部数据更新到文件中。如果密码或者账号输入不正确的话,系统会提示“请输入正确的账号和密码”。

    用户使用模块。输入对应的数字,进入对应的功能使用模块,可以完成用户的需求。
    查询个人信息。如图所示。

    修改个人信息。如图所示。

    领取快递。如图所示。

    返回主界面。如图所示。

    5 系统测试5.1 系统测试概述所谓系统测试是根据软件开发各阶段的规约和软件的内部结构,精心设计一批软件测试用例,并利用这些测试用例去运行程序,用来发现软件中不符合软件质量特性要求的过程。
    系统测试过程可以看成不断进行排错、测试、修改程序和文档,然后进行回归测试,直到软件达到用户的质量特性要求的一个循环过程。
    5.2 系统测试方法目前主要使用的系统测试方法有白盒测试和黑盒测试:

    白盒测试,这个方法可以对程序内部的具体逻辑进行测试,相当于微观的看待事物,详细的说就是根据具体的每一个方法测试它是否运行正确,是否有错误等;
    黑盒测试,该方法把程序当成一个整体,相当于宏观的看待事物,具体的说就是不看内部具体方法只是根据设计的需求来测试系统,然后判断是否符合设计的要求,是否能够正常使用该系统。

    5.3 系统的测试打开VS2015,运行程序源码,然后进入系统的欢迎界面了。本系统采用黑盒测试,对每一个模块进行用例测试。

    软件中登陆界面的测试
    首先,打开菜鸟驿站管理系统登陆界面,如表所示。



    编号
    输入操作
    预期结果
    测试结果




    1
    选择老用户,输入账号“1”,密码“student”,登陆
    登陆成功,跳转到 用户页面
    通过


    2
    选择管理员,输入账号“admin”,密码“admin”,登陆
    登陆成功,跳转到 管理员页面
    通过


    3
    选择新用户,输入账号“2”,密码“student”,注册,登录
    登陆成功,跳转到 用户页面
    通过


    4
    不输入任何信息,直接点击登陆
    登陆失败,提示输入账号
    通过


    5
    只输入密码“11”,不输入账号,点击登陆
    登陆失败,提示输入账号
    通过


    6
    选择老用户,输入账号“100”,密码“student”,登陆
    登陆失败,提示输入 账号错误
    通过


    7
    选择老用户,输入账号“1”,密码“stu”,登陆
    登陆失败,提示输入 密码错误
    通过




    用户账户信息管理测试
    接下来,对账号信息管理进行测试,如表所示。



    编号
    输入操作
    预期结果
    测试结果




    1
    打开查询功能
    查到个人信息
    通过


    2
    打开修改功能,修改完成后点击保存
    可以对个人信息进行修改
    通过


    3
    打开领取快递,输入用户快递的单号
    可以领取快递
    通过


    4
    打开返回主菜单
    系统返回主菜单
    通过




    管理员维护用户信息测试
    使用管理员模块对用户信息进行管理,如表所示。



    编号
    输入操作
    预期结果
    测试结果




    1
    打开录入功能,录入用户信息
    提示用户添加成功
    通过


    2
    打开修改功能,修改用户信息
    提示用户信息修改完成
    通过


    3
    打开删除功能,删除用户信息
    提示用户信息已删除
    通过


    4
    打开浏览功能,浏览用户信息
    输出用户信息
    通过




    管理员维护货物信息
    对管理员维护货物信息功能进行测试。



    编号
    输入操作
    预期结果
    测试结果




    1
    打开录入功能,录入货物信息
    提示货物信息添加成功
    通过


    2
    打开修改功能,修改货物信息
    提示货物信息修改完成
    通过


    3
    打开删除功能,删除货物信息
    提示货物信息已删除
    通过


    4
    打开浏览功能,浏览货物信息
    输出货物信息
    通过


    5
    打开统计货物信息
    会输出未领取货物的信息
    通过



    结束语通过对这个系统的设计和实现,我对该系统的了解更加深了。本系统主要是完成了用户信息管理、快递信息的管理以及信息统计分析等功能。通过本次学年设计,我对软件项目的开发流程以及所运用的知识如C语言等有了更多的自己的理解,锻炼了自己解决工程问题的能力以及提升了自己系统分析和设计能力。另一方面,本系统的实现,仍存在不足,如本人对于统计分析这个模块理解不足,以致在实现功能时没有考虑到具体的情况,造成某些功能设计的不是很合理。
    0 评论 4 下载 2019-08-07 10:51:07 下载需要10点积分
  • 基于WPF实现的简单绘图工具

    1、系统功能设计
    开发、测试环境

    开发环境: Visual Studio 2012 Premium运行框架:.net framework 4.5测试环境:Windows 8.1、Windows 7
    开发语言

    C#XAML

    1.1 前言当初选择这么一个软件来编写纯粹是出于对玩弄文字游戏的xx管理系统的不喜,但我们没有料想到,经实践表明,涉及实时跟踪鼠标键盘事件和实时绘图的软件编写难度远大于主要通过文字实现信息交互的xx管理系统。仅仅实现一个屏幕上图形的框选功能就让我改了六七遍代码,我的队友更是间断地找出了五个bug。当终于能够把整体功能流畅地实现时,我们对软件开发者的了解与敬意又加深了一层。
    1.2 总体功能描述当今图像处理越发普及,人们对于图像处理的需求也各不相同。而一些绘图软件存在过于复杂(如PS)或是只具备基础功能(如windows自带画图)的问题,因此我们开发一个基于Windows Presentation Foundation(WPF)的简单绘图工具。
    以下为程序的工作界面

    1.3 功能点说明


    功能类型
    功能点名称
    按键
    实现方式
    功能点描述




    基本功能
    主流类型图片载入
    Ctrl+O
    自己编写C#代码
    支持.png.jpg.gif.bmp.eps等主流图片类型的读入和加载。使用C#标准OpenFileDialog对象获得文件路径并且使用Uri读入之后转为BitmapImage,用Image控件显示


    基本功能
    图片保存
    Ctrl+S
    自己编写C#代码
    支持bmp(位图)和eps(矢量图)两种格式。使用SaveFileDialog对象获得文件路径之后用filestream输出。


    基本功能
    鼠标手绘图形
    鼠标拖框
    自己编写C#代码
    包括矩形、圆角矩形、圆、椭圆、直线、贝塞尔曲线等


    基本功能
    单击选择图形
    鼠标单击
    自己编写C#代码
    调用类接口SelectPoint来实现


    复杂功能
    框选图形
    鼠标拖框
    自己编写C#代码
    递归调用类接口SelectRect来实现


    复杂功能
    全选图形
    Ctrl+A
    自己编写C#代码
    调用类接口SelectAll来实现


    复杂功能
    多次选中图形
    Shift+鼠标拖框
    自己编写C#代码
    调用类接口MergeComposite来实现


    复杂功能
    被选择图形的闪烁

    自己编写C#代码
    通过来回设置选择的CompositeGraphic的isVisible属性来达到闪烁的目的。


    复杂功能
    删除图形
    Delete
    自己编写C#代码
    调用类接口Clear来实现


    复杂功能
    图形状态变更

    自己编写C#代码
    设置类属性DrawMode来实现。可以设置图形的边框粗细、颜色,以及内部颜色


    复杂功能
    调色板

    使用第三方库
    支持通过ARGB属性或是在图形中直接选色的方式给图形的边框和填充分别选色


    复杂功能
    拖动图形
    鼠标按住拖动
    自己编写C#代码
    调用类接口Move来实现


    复杂功能
    键盘移动
    Up,Down, Left,Right
    自己编写C#代码
    通过长按可快速移动


    复杂功能
    剪切选中图形
    Ctrl+X
    自己编写C#代码
    将选中的图形加入一个List,并从画布上删除选中的图形


    复杂功能
    复制选中图形
    Ctrl+C
    自己编写C#代码
    将选中的图形加入一个List,但不从画布上删除选中的图形


    复杂功能
    粘贴图形
    Ctrl+V
    自己编写C#代码
    对上述List中的所有成员递归调用接口 ICloneable.Clone。长按Ctrl+V可以连续粘贴


    统计功能
    图形个数统计

    自己编写C#代码
    递归调用类属性Count并将它和Label绑定


    用户体验优化
    打开和新建图片、关闭程序时的友情提醒。

    自己编写C#代码
    防止误删未完成画布中的内容。


    用户体验优化
    显示系统时间

    自己编写C#代码
    使用DateTime.Now.ToString方法


    错误处理
    载入图像时对不支持图像及无法处理的图像的抛出。

    自己编写C#代码
    若捕获异常,会弹出对话框



    2、系统总体结构2.1 概要设计按照“面向接口编程,而不是面向实现编程”的面向对象基本原则,在建立解决方案的时候我就将解决方案分成了Ccao-big-homework(UI,WPF工程项目,由我的队友邵键准同学实现,调用者)和Ccao-big-homework-core(实现,C#类库,由我实现,被调用者)。双方互不干涉,独立调试,在整个大作业过程中不曾因为接口耦合问题出现bug。
    由于采用了多种科学合理的设计模式(见下文各模块介绍),本类库在维持良好的可复用性的同时,不曾进行过任何强制类型转换,不曾进行过任何运行时类型判定,所有的多态都靠重载函数来实现,充分体现了面向对象的思想。
    3、具体实现3.1 GUI的设计GUI的设计采用了较为简洁的风格,设计完成后曾请求周边同学进行体验并对细节进行改进,力求用户体验较好。主体采用XAML语言,实现设计和功能的分离,并配之以C#的事件处理函数。程序取消了不怎么美观的窗口边框,并采取点住程序窗口任何一个位置均可拖动的方法。
    3.1.1 程序启动和关闭动画设计程序的主要窗口在开始和结束时都是通过淡入和淡出来呈现和销毁窗口,实现此效果使用了一个计时器,并让窗口的透明度随计时器而改变。下图为启动过程截图,可看见窗体还是半透明状态(请无视背景的代码)。
    3.1.2 程序启动界面的设计界面包括版本号与开发人员,“新建绘图”按钮在鼠标移上后会有高亮,点击进入绘图页面,点击“离开”按钮则直接调用Application.Current.Shutdown()函数销毁窗口。
    3.1.3 程序主体绘图界面设计主体绘图界面如下。

    上部为菜单栏,具体按钮功能见使用手册,实现了鼠标移到某个按键上时该按钮闪烁一次并且放大,同时调整整个工作框的布局。效果如下。

    左部为绘图框,选择后可使用鼠标绘制不同的图形,包括直线、圆、椭圆、正方形、长方形、贝塞尔曲线。其中贝塞尔曲线限于WPF提供的贝塞尔曲线构造函数的局限性,其必定从画布的左上角开始绘制。
    左下角为一个图标,无实际作用。
    3.1.4 程序图标的添加我们的程序可是有图标的哦!

    下面是调色板的图标。

    3.2 逻辑层主要实现解决方案Ccao-big-homework。
    3.2.1 工作窗口WorkWindow部分3.2.1.1 概述主要实现人与程序的交互,包括鼠标事件和键盘事件等。由于绘图软件基本是在一个窗口进行操作,一般使用组合而非继承的方式,因此类的结构比较扁平。类图如下。


    由于没有继承,全部实现在一个窗口里代码显得非常臃肿,因此我根据功能的不同将同一个窗口类分成了如下几个文件。

    下面逐步介绍各个文件实现的方法。
    3.2.1.2 BtnEvent实现菜单栏所有按钮功能,包括新建、打开、保存、退出、全选、复制、剪切、粘贴、样式选择按钮。实现方法是调用其他文件里的私有函数。
    3.2.1.3 FileEvent实现图片读写的相关方法。我们的程序支持使用文件夹视图来把文件保存到计算机的任意位置或是从计算机任意位置载入图片。同时还在现有图片未保存时弹出对话框提示用户要保存,防止了图片的误删,优化用户体验。

    实现画布的新建与刷新,并实现程序的退出功能。
    3.2.1.4 FishEyePanel实现上部菜单栏中当鼠标移到某个按键上时该按钮闪烁一次并且放大,同时调整整个工作框的布局。此处采用了组合的形式,FishEyePanel是一个新的类,在WorkWindow类中创建该类的对象,并布局到主窗口上。
    3.2.1.5 GraphicsOperation实现图像整体操作,包括:

    全选:调用队友提供的SelectRect接口,并把范围设置成整个画布大小的矩形,从而得到一个类型为List<CompositeGraphic>的对象,添加到selectedGraphics里
    复制:调用队友提供的Clone()函数,往clonedGraphics这个对象里添加对象
    剪切:复制的同时清空selectedGraphics
    粘贴:将clonedGraphics里的所有成员添加到总画布compositeGraphic的Children()成员中,从而实现绘制,并向右下角移动(10,10),从而和复制的原图区分开,然后清空clonedGraphics(),并再次调用复制函数(),从而实现粘贴的连续性,即复制一次可连续粘贴。下图为按下Ctrl+C后连续按下Ctrl+V的效果


    3.2.1.6 KeyBoard实现键盘的按键监控,包括:

    Ctrl+A:全选图像
    Ctrl+C:复制选中图像
    Ctrl+V:粘贴选中图像
    Ctrl+X:剪切选中图像
    Ctrl+W:关闭程序
    Ctrl+O:打开图片
    Ctrl+N:新建画布
    Ctrl+S:保存图片
    Delete:删除选中图像
    Key Up、Key Down、Key Left、Key Right:选中图像的上下左右移动
    Shift:按住时可多次增加选择已选中的图形

    3.2.1.7 MouseEvent我的工作中最难的部分,而且肩负调试队友代码的使命。

    Window_MouseLeftButtonDown事件:实现鼠标不在画布上时窗口根据鼠标的移动而拖动
    OnMouseLeftButtonDown事件:鼠标左键按下处理事件。主要记录鼠标开始移动的点startPoint,让画布捕捉到鼠标,并标记左侧的ToolBar是否选中rbSelect选择按钮
    OnMouseMove事件:鼠标移动时的处理事件。当鼠标移动且画布捕捉到鼠标的时候,若是发现rbSelect选择按钮被选中且当前有图形被选中且Shift键未被按下,那么说明这个鼠标事件需要的是移动图形,因而用虚直线实时绘制鼠标指示的移动路径;若是其他绘图按钮被选中,则说明要绘制图形,则用虚长方形或是虚直线实时绘制图形
    OnMouseLeftButtonUp事件:鼠标左键抬起的处理事件。首先判断鼠标抬起时和按下时位置是否相同。若是相同,则说明用户只是按了一下,那么不管左侧ToolBar是选中的什么,说明用户都是想选中一个图像,因此将左侧的工具条调整到rbSelect按钮,并且调用总画布compositrGraphic的SelectPoint方法,得到这个点选中的图像,将其加入selectGraphics,在这个List里面的对象,每隔0.5秒更改一次可见性,从外观看来,选中的图形会闪烁。若是鼠标移动了,且选项选中的是图形选项卡,则绘制对应的图形,并且刷新画布,并把之前实时跟踪鼠标的虚线图形删去。接下来判断选项卡是否为贝塞尔曲线选项,若是则把该点增加到贝塞尔曲线的List里,当List里的成员个数增加到4时,绘制一条贝塞尔曲线并清空该List。最后是最为复杂的rbSelect按钮,如果当时选中的图形为0,即selectGraphics为空,则说明用户想要选中他框选的图形,则调用SelectRect得到选中的所有图形,并把其加入selectGraphics中使其闪烁。若selectGraphics不为空,则说明用户想要移动选中的图形,则调用move方法移动选中的图形,并把selectGraphics清空。同时,如果整个过程中Shift键被按下且rbSelect被选中,说明用户想要增加选择图形,于是将选中的图形增加到selectGraphics里面。

    以上方法还各自特判了贝塞尔曲线绘制时的点四个点的情况。
    3.2.1.8 Paint各种图形的绘制,包括:

    直线
    长方形
    正方形

    椭圆
    圆角矩形
    贝塞尔曲线

    其中(1)~(6)的绘制方式基本相同,都是新建一个该图形的对象,然后根据传来的两个点确定图形的长宽和位置,将这个对象添加至总画布compositeGraphic,然后刷新画布。
    对于(7),绘制方式是在画布上点四个点,则出现贝塞尔曲线。
    3.2.1.9 其他零碎功能主要包括一些动画效果。启动时工具条的移入运用了ThicknessAnimation控件。为了美观我特意写了一个函数隐藏了工具条尾部的小箭头。选中图形闪烁的功能则使用了一个DispatcherTimer计时器,每过500ms就把selectGraphics的成员的可见性改变一次。图形个数统计调用总画布compositeGraphic的count属性,每隔0.5秒刷新一次。系统时间标签则使用DateTime.Now.ToString方法获取。
    3.2.2 启动窗口MainWindow部分这个窗口很简单,只实现了弹出WorkWindow和关闭窗口退出的功能。并贴了一张图,写了版本号。
    3.2.3 颜色拾取窗口StyleSettingWindow部分颜色拾取窗口我并没有花太多时间自己写,本来以为WPF自带调色板控件,结果发现没有,于是在网上找了一个扩展控件,并组合到WorkWindow类中,在按下上部菜单栏中的“样式选择”按钮时弹出。

    3.3 类库Ccao-big-homework-core-wpf的实现3.3.1概述。本类库(Ccao-big-homework-core-wpf.csproj)基于WPF,文件统一注释为”Du 2015.9”。

    由于本类库被设计作为实现图形操作的类的类库,所以各类之间比起“is-implemented-in-term-of-a“或者”has-a“来说更符合”is-a”关系,所以本类库大量使用了继承、多态、递归调用这些OO的方法,和UI相比更好的体现了这次大作业的教育目的。

    3.3.2 MyGraphic类基本的图像类。之所以声明为类而不是接口是因为有几个函数要有默认实现。
    下图是Mygraphic类的全部类图。各函数基本都是函数名自解释的,如果一眼看不出来有什么用可以参见源代码的注释。

    乍看上去,Ienumerator和Ienumerable两个接口(包括count,getenumerator,current,add,clear,dispose,reset等,可以使得这个类被像list一样用foreach遍历)看上去都不应该是MyGraphic类作为一个基本的图像类应该有的方法或者属性。事实上这里采用了Composite设计模式。为了使得单个对象和组合对象的使用具有一致性,其他库函数(以及用户)能够统一的使用组合结构中的所有对象(具体来说,使得我们不用在override各种函数的时候来写出诸如MyGraphic g as CompositeGraphic这种效率很低的运行时类型判定代码来),所以在MyGraphic类中定义了Composite类的各种接口,并且给以了默认实现(当然了由于MyGraphic不是CompositeGraphic,默认实现理应是空实现)。
    另外,MyGraphic类还实现了接口ICloneable,这个接口允许MyGraphic被深复制,因而实现了UI的复制-粘贴功能。
    IsVisible指示本图像是否会被画到画布上。
    Father指示MyGraphic类的父图像。Father类的set方法被重写来调用父图像的DisposeChildren方法来删除父图像维护列表中自己的存在。这样就能保证整个MyGraphic类是一棵树(树的顶点是由UI保存的一个Composite,事实上充当了画布的功能),为递归遍历创造了条件。所以同时,当Father被置null的时候,本图形就会因为失去一切引用而被C#的GC机制自动回收,相当于是被Dispose了。因此本程序的效率大大提高,本人的电脑在有1500个图形的时候还能够正常工作。
    SelectError指示点击一个点的时候允许多大的误差以选中一个图形。
    还有不少函数在MyGraphic类中被声明仅仅是为了作为接口被递归调用,例如SelectRect、Count属性等就是这样,当然整个程序中最重要的Draw也不例外。
    3.3.3 SingleModeGraphic类顾名思义,是整个图形有同一个drawmode的类,也是所有基本图形(basic_graphics)的基类。显然composite类由于可以add进各种drawmode的图形所以不可能能维持有同一个drawmode。

    drawmode指示一个给定border的geometry的图形如何被绘制,将调用drawmode的draw方法(见3.3.6)。
    重载了基类的SelectRect方法,当选中时返回包括自己的一个CompositeGraphic,反之返回null。
    getGeometry方法返回这个对象的border的geometry。
    3.3.4 CompositeGraphic类可以包含其他graphic的graphic的实现,或者说用windows画图打比方的话,就是那个选框功能。重载了IEnumerable和IEnumerator接口。

    首先,作为Composite设计模式的一部分,override了MyGraphic中的Composite相关的函数(isComposite, Clear, Add, MoveNext, Reset, Current),进行了事实上的操作。
    CompositeGraphic类维护一个私有的辅助list,用来保存每个成员相对于本体的左上角的相对位置。当CompositeGraphic类要执行某个操作的时候(例如Draw或者SelectRect),它就递归调用每个成员的相应操作,好像这个类不曾存在过一样(如果设置了isCombined = false,当这个标记被置true的时候整个CompositeGraphic将被视为是一个整体,类似于PPT或者Flash提供的“组合”功能)
    另外CompositeGraphic也像SingleModeGraphic一样有drawmode类型的属性backgroundmode,这是为了画出它的边界和背景色。边界总是一个矩形。
    当调用SelectRect的时候,由于递归调用的过程会产生大量很多层的CompositeGraphic(因为selectRect的原理是将所有被选中的图形改为连接到同一个Composite中,然后将这个Composite的父图形设为this),所以设计了MergeComposite,将一个Composite中的全部内容合并到另一个中,减少了递归的层次(在这种设计之下,递归最多两层),有效提高了运行效率。
    3.3.5 基本的图像类(basic_graphics)这些类都继承自SingleModeGraphic并且是Sealed类。有几个属性来记录自己的形状,并且override了getGeometry接口来确定自己的外形。如类图所显示的那样,新建一个基本的图像类是非常简单的,只需要实现getGeometry方法和Clone方法两个方法,并且定义几个足够确定图形位置和形状的属性,就能够被立刻应用到类库之中,像其他图形一样被选择、被移动、被更改颜色、被剪切、被复制、被粘贴,这充分体现了本类库良好的可扩充性。

    对于Ellipse、Line、Rectangle,WPF都提供了相应的Geometry对象来绘画,然而对于Bezier,WPF并没有提供相应的Geometry,而是提供了一个PathSegment——BezierSegment。BezierSegment只有三个参数(也就是说默认从原点开始画),第四个点需要通过设置PathFigure的起始点来确定。
    3.3.6 DrawMode类
    抽象类。指示如何绘制一个Geometry形状的Mygraphic为drawing。这个类其实做成接口也没什么问题。如果深究起来的话把绘图方式单独封装起来似乎也是一种设计模式,学名叫做Strategy模式。Strategy模式的好处也是显而易见的——如果直接将各种绘图方式以硬编码的方式写在SingleModeGraphic中,虽然可以少几个类,但是这意味着你可以在完全不破坏类库的情况下添加新的DrawMode,使得它易于切换、易于理解、易于扩展,进一步体现了作为一个类库的重要理念——可扩充性。(嘛,不过这毕竟只是大作业,虽说类库要可扩充性良好,但是也就我自己扩充我自己的类库玩罢了…但是当我发现WPF的drawing系列图像的构架思想竟然和我一开始手画的类图一模一样的时候,我心中别提有多得意了)
    3.3.7 DrawMode类的派生类
    这些类都继承自Drawmode类并且是Sealed类。有几个属性来记录自己的绘图方式,且override了draw来绘制。
    3.3.8 defaultConstant类
    储存一些常量。在GDI+中有System.Drawing.Color枚举,然而在WPF中似乎没有对应字段,为了方便类库的设计所以作为static readonly变量定义在这里,效果类似于C++的全局const。另外准备了defaultbrush(透明色)和defaultpen(黑色,宽2.0f)来作为GeometryMode的默认构造值。
    由于是常量,所以被作为partial类分散定义在drawmode的各个派生类中。
    3.3.9 DrawingUIElement类
    本来作为一个普适的类库有上面的类就已经能够绘图了;但是我所写的这个类库毕竟是个wpf类库,然而wpf的基本控件Canvas的Children只接受某种System.Windows.UIElement作为它的子成员。所以为了避免推倒重来,此处采用了Adapter(或者叫做Wrapper)设计模式,将本类库的接口类型(System.Windows.Media.Drawing)转换为我的调用方,邵键准同学的UI能够接受的参数UIElement。
    重写了OnRender方法,只要对它调用UIElement所固有的InvalidateVisual方法就能迫使控件重画,从而将drawing指示的画面显示在wpf界面上。
    3.4 类库Ccao-big-homework-core-winform的实现本类库(Ccao-big-homework-core-winform.csproj,并没有被本次大作业引用)是一开始我的队友尚未开始施工的时候编写而成的(文件注释为”Du 2015.8”),当时前期调研做的不够好,没有意识到wpf和winform不兼容,所以手贱就先写了一个winform版。Winform版实现比wpf版要早,Class Diagram也和wpf版类似,几乎所有的类名都一样,只是winform版基于GDI+,命名空间为System.Drawing,而wpf版基于WPF,命名空间为System.Windows。在此对于两个类库之间雷同的部分不再赘述。
    在本大作业中并没有用到winform类库(因为winform的界面远没有wpf美观),但是winform类库在我们往届的测试中证明是可以使用的(见github上9.6晚上的commit,hash为87ce7da,当时有一个基于VB的样例test基于winform测试了这个类库的可行性)。如果有兴趣做winform开发,可以尝试使用这个类库。
    本类库和wpf版的唯一区别在于draw的实现。在wpf版中,draw函数是一个普通的返回System.Windows.Media.Drawing的函数。然而在winform中我们采取的并不是Adapter模式,而是另一种著名设计模式Bridge模式:我们有一个接口IWindow实现drawpath和fillpath两个接口,然后draw函数接受参数IWindow,并在函数体内调用这两个接口进行绘图,没有返回值。这样做的好处在于可以将抽象部分与它的实现分离,使得双方都可以有独立的变化。无论类库将要面对怎样的UI(即便这是个WPF UI甚至是个VB项目,例如我的测试用项目),只要这个UI实现了接口IWindow,类库就可以不作任何修改的应用到这个UI上。
    以下是项目文件列表,除了IWindow外的所有类都和wpf版一样。

    4、项目总结4.1 亮点首先,由于采用了Composite模式,所以几乎所有的Composite接口都是通过递归调用来完成的,简洁明了,体现了合理设计模式的优越性。假使不采用Composite模式,那么(任举一例),SelectRect方法将不得不返回一个list<MyGraphic>,且不说这样子将使得这个类库丧失组合/打散的固有功能,而且返回list将使得对于这个list的每个操作都必须由客户端通过类似foreach MyGraphic g…这样子的调用方式手动对每个list的成员执行。现在只需要调用Composite的对应函数,就能通过递归调用在对客户隐藏的情况下执行了,体现了良好的封装。
    其次,本类库的可扩充性堪称良好。向本类库中加入新的几何图形只需要继承SingleModeGraphic并且重写getGraphic和Clone;向本类库中加入新的绘图方式只需要继承DrawMode并且重写Draw。
    4.2 OOP模式的优越性接口和实现的分离极大提高了我们的开发效率。只需要知道给接口提供什么参数,能得到什么结果就可以了,不需要知道它是如何被实现的,这样的黑箱极大地提高了我们的开发效率。
    4.3 版本控制的重要性版本控制不仅让我们能够清晰地了解自己的开发进度,而且在开发出现重大bug的时候,能够迅速地回滚到以前的版本。这样不会出现自己写了什么挂了都不知道的,结果只好全部删除重来的情况。况且,每一次在Git上的Commit都意味着我实现了新的功能,内心总能感到满足。
    4.4 自主解决困难的能力为了写大作业我估计百度了不下300次,基本开发的状态就是:想实现某功能—>发现自己不会—>百度google大法好—>会了—>实现该功能—>又想实现某功能。
    开发过程中我遇到过很多困难,举个例子:关闭窗口时我写了个淡出动画,结果发现动画刚开始运行就程序就被退出,表现为动画无法播放。百度了一个小时,终于找到解决办法:在给程序的OnClosing事件中,把close命令取消,然后坐等0.6秒动画放完后再退出程序。
    真正到开发程序才发现以前学的都是纸上谈兵,经过一份大作业的洗礼我收获了极大提升的编程技能。
    2 评论 15 下载 2019-04-02 10:31:11 下载需要12点积分
  • 基于C#的聊天程序

    1.引言1.1目的编写详细设计说明书是软件开发过程必不可少的部分,其目的是为了在完成需求分析说明书的基础上完成需求分析说明规定的各项模块的具体实现的设计工作。
    1.2定义套接字Socket:网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。
    端点IPEndPoint:由IP地址和端口号构成,端口对应进程。这两个组合起来可以唯一的标识网络中某台主机上的某一个进程。这样就有一个唯一的身份标识,后面可以进行通信了。
    TCP协议:TCP是一种面向连接的,可靠的,基于字节流的传输层通信协议。
    回环端口:即本机的一个虚拟端口,为IPAddress.Loopback,其IP地址为127.0.0.1
    2.总体设计2.1软件描述荆棘鸟聊天程序是基于Socket设计的一个windows桌面端多人聊天程序,采用C/S架构,服务器可以自定义监听的IP(可设置为仅能本机使用或允许联机使用),客户端也可以设置服务器的IP,但是客户端设置的IP必须与服务器监听的IP相同,否则会连接不上服务器,如果不自己设置IP,默认为只能本机使用,即127.0.0.1.
    2.2设计方法本软件采用传统的软件开发生命周期的方法,采用自顶向下,逐步求精的结构化的软件设计方法
    2.3软件结构2.3.1总体结构
    3. 模块设计说明3.1程序描述本程序分成两部分,分别是服务器端和客户端。服务器启动后,用户使用客户端连上服务器后即可进行多人聊天 。在一台机器上,服务器只能启动一个,而客户端可以启动很多个,但是客户端的总数目不得超过100个。对于服务器和客户端,均采用多线程异步处理机制,在关闭程序后会自动回收所有子线程。
    3.2功能本程序能实现多人聊天功能。服务器打开后,客户端需要通过设置正确的服务器IP来连接服务器,服务器也可以设置自己能监听的IP地址进行监听,如果客户端与服务器均不手动设置IP,那么默认的IP是本机的回环地址(IPAddress.Loopback),而且客户端的IP和服务器端的IP必须设置为相同的才能正常连接上。客户端连接服务器的要求是必须设置一个唯一的用户名,用户名若重复则会进行提示并需要重新设置一个唯一的用户名后才能连接上服务器。同时客户端也会检测用户名是否包含敏感词汇(会影响程序正常运行的词),若包含也需要重新设置用户名。客户端连上服务器后,能显示服务器状态信息(服务器是否关闭)、是否有用户上下线以及其他用户发出来的消息。服务器端能显示用户的连接与下线信息,以及服务器的状态信息。同时服务器也能恢复默认监听的IP地址,即IPaddress.Loopback(127.0.0.1),且服务器会显示当前监听的IP地址。
    3.3 性能对于1M的带宽的网络

    客户端连接上服务器的时间在10秒以内。
    客户端间通信的时间间隔不得超过10秒。

    3.4 输入项
    服务器:需要输入(选填)监听的IP,如果不填则默认是本地回环端口127.0.0.1,输入的IP必须为格式正确且服务器所能监听的IP,否则将会进行提示。
    客户端:

    需要输入唯一的用户名(前17个字符不能为“Server has closed”,否则会影响程序正常运行),且用户名不能为空。连接上服务器后用户名将不能进行修改。需要输入服务器的IP以进行连接,格式和服务器的监听的IP相同,且客户端输入的IP必须和服务器监听的IP相同,否则将无法连接上。连接上服务器后服务器的IP不可修改。允许在输入框中输入文本信息以进行多人聊天,但是仅限文本信息。

    3.5 输出项
    服务器 :

    能显示当前监听的IP,如果不自己设置监听IP,则默认为127.0.0.1。能显示服务器的状态,即时启动还是关闭。能显示客户端连接服务器的状态,当客户端连接或者断开连接时,服务器端会显示。
    客户端:

    显示服务器状态信息,即服务器是否已经关闭。显示其他用户发来的信息,即进行正常的聊天。显示其他用户的上下线信息。

    3.6 流程逻辑首先启动服务器(服务器程序打开后会自动启动,此时监听的端口为127.0.0.1)。然后启动客户端,设置用户名,设置服务器的IP(如果不设置则默认为127.0.0.1)。点击客户端的连接服务器按钮,检查用户名是否合法,不合法则直接终止并需要重新设置用户名后重试,若用户名合法,则检查客户端设置的IP格式是否正确,若不正确则中断连接并需要重新设置服务器IP。然后进行客户端与服务器端的连接。若客户端与服务器设置的IP不一样,或者服务器未开启,则会提示连接失败,服务器未打开。
    正常连接上后,其他在线的客户端会提示此用户上线了。服务器可以点击关闭服务器,此时所有客户端都会提示服务器已关闭并自动关闭客户端与服务器的连接。客户端也可以点击退出聊天,可以关闭与服务器之间的连接,此时服务器端与其他在线用户均能收到此用户已下线的消息。
    服务器端在服务器开启后会显示当前所监听IP,若选择重置,则会先断开所有客户端的连接,然后重新开启服务器,并监听所设置的IP,显示的当前监听的IP也会随之改变。此时所有客户端全部被强制下线,必须设置与服务器相同的IP并重新连接服务器后才能继续进行聊天。
    3.7 接口
    当新用户连接上服务器后,会给服务器发送 “用户名”+“$”,服务器读取到这种格式的数据会将’$’去掉之后将其当成新用户进行保存,与该用户的套接字一起以键值对形式保存在字典中,并将上线消息进行转发。
    当用户需要退出聊天时,会给服务器发送“$”,服务器读取到仅有一个‘$’的数据时会将对该用户的连接关闭,并将其从用户字典中删去,且将用户下线信息进行转发。
    clientSocket为客户端中用来连接服务器的套接字,客户端用clientSocket != null && clientSocket.Connected根据情况判断是否连接上了服务器或服务器是否关闭。
    当服务器向用户发送的消息为“#用户名#”的时候,表示该用户名已经有人使用,需要客户端更换用户名后才能重新连接。
    当服务器向客户端发送的消息的前17个字符为“Server has closed”时,表示服务器已经关闭。
    在服务器端代码中,类Broadcast的成员函数PushMessage(String msg, String uName, Boolean flag, Dictionary<String, Socket> clientList)是用来对所有客户端进行转发信息的。当flag为false时表示是系统消息,直接发送msg+DateTime.Now.ToString(),而如果是其他客户端发送的聊天信息需要转发时,则设置flag为true,会发送uName: msg+ DateTime.Now.ToString()。

    3.8 注释设计函数模块的注释放在函数的头部,关键语句的注释放在语句的上面一行或者语句的后面。对所有参考资料均在注视中标明来源网址,并抄下或者自己写下了关键性的注解。
    3.9 限制条件本程序只能在64位windows操作系统下运行,且该系统必须装有.NET 4.5及以上的框架。
    0 评论 2 下载 2019-08-05 11:45:33 下载需要5点积分
显示 0 到 15 ,共 15 条
eject