分类

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

资源列表

  • Python实现的基于SVM、LR、GBDT和决策树算法进行垃圾短信识别和分类

    摘 要短信业务的迅猛发展在丰富了人们的沟通方式的同时,同样遭受到垃圾短信的困扰。对于运营商来说,垃圾短信造成基础设施资源的巨大浪费;对于移动用户来说,大量的垃圾短信使用户不能够及时查看正常的短信,干扰了用户的正常生活。垃圾短信的识别已经成为一个亟待解决的问题,而传统的基于黑白名单、关键字进行过滤的效果有限,不能起到很好的识别效果。针对该问题,我们基于垃圾短信的文本内容,将文本分类算法应用到垃圾短信的分类中。我们使用了SVM、LR、GBDT和决策树算法进行垃圾短信的识别工作,最后我们制作了线上演示系统。结果显示系统在垃圾短信的识别上有着良好的表现。
    关键词:垃圾短信、文本分类、GBDT、LR
    一、概述垃圾短信日益成为困扰运营商和手机用户的难题,严重影响人们的日常生活。根据腾讯《2018年上半年手机安全报告》[1]显示,2018年上半年,用户通过腾讯手机管家共举报垃圾短信近8.89亿条,其中广告类短信占比高达96.71%。除了广告类短信,根据短信内容不同,骚扰类、欺诈类、非法广告短信、危害国家安全、散布谣言、侮辱或诽谤他人的短信都属于垃圾短信的范畴。
    对于运营商来说,大量的垃圾短信会耗费过多的资源,造成资源的浪费,并增加了网络遭到恶意攻击的风险;对于用户来说,大量的垃圾短信使用户无法及时查看到有用的信息,带来不良的用户体验。因而对垃圾短信进行有效识别是十分必要的。传统的基于黑白名单、关键词过滤等方法对于垃圾短信的识别效果有限。从垃圾短信的内容角度进行考虑,垃圾短信的识别问题可以归为文本二分类问题,目前在文本的二分类问题方面已经有诸多成熟的方法,因而可以将这些方法应用到垃圾短信的识别工作中。
    我们针对垃圾短信的文本内容,对垃圾短信的文本进行特征提取并利用SVM、LR、GBDT、决策树方法进行分类工作,最后我们制作了线上演示系统来识别垃圾短信。本文的组织结构如下:第一部分介绍了垃圾短信的概念与现状及本文的主要工作;第二部分介绍了文本分类的相关工作;第三部分为数据分析;第四部分为研究方法,介绍了我们所使用的分类算法;第五部分为实验设计,介绍了实验的过程及结果;第六部分为总结,包括对实验总结与人员分工情况。
    二、相关工作文本分类最早可以追溯到20世纪60年代,在这之前主要是采用手工分类的方法。进入60年代后,Maron发表了具有里程碑作用的论文《Automatic Indexing: An Experimental Inquiry》[2],采用贝叶斯公式进行文本分类,大大推进了文本分类工作。在该文中,Maron还假设特征间是相互独立的,这就是后来被广泛采用的“贝叶斯假设”。
    在随后的二十多年,主要是采用知识工程(Knowledge Engineering, KE)的方法进行文本分类,它通过在专家知识基础上手工建立一系列分类规则来构建分类器。知识工程方法需要大量领域的专家和工程师参与,势必耗费很多人力物力,当电子文档急剧增长时将无法满足需求。这种方法最典型的应用实例为由Carnegie Group开发的CONSTRUE系统[3],该系统用来对路透社的新闻稿件自动分类。
    直到进入20世纪90年代,随着Internet的迅猛发展,为了能够更好地处理大量的电子文档,并且伴随着人工智能、机器学习、模式识别、统计理论等学科的发展,基于知识工程的文本分类方法渐渐退出了历史舞台,文本分类技术进入了更深入的自动分类时代。由于基于机器学习的自动文本分类系统几乎可以达到与人类专家相当的正确度,但是却不需要任何知识工程师或领域专家的干预,节约了大量的人力,并且分类效率远远高于人类专家。
    常用的文本分类算法主要包括三大类。一类是基于概率和信息理论的分类算法,如朴素贝叶斯算法(Naive Bayes),最大熵算法(Maximum Entropy)等;另一类是基于TFIDF权值计算方法的分类算法,这类算法包括Rocchio算法,TFIDF算法,k近邻算法(k Nearest Neighbors)等;第三类是基于知识学习的分类算法,如决策树(Decision Tree),人工神经网络(Artificial Neural Networks),支持向量机(Support Vector Machine),逻辑回归模型(Logistic Regression)等算法。
    三、数据分析在进行数据分类之前,首先我们对数据进行了分析。此次垃圾短信分类共有80万条标注数据,其中垃圾数据80000条,其余为非垃圾数据。由此可见数据中正负样本不均衡的问题非常严重。
    考虑到上采样方法会导致过拟合,下采样会浪费过多的实验数据面对这样的情况,我们的思路是改变错分数据的代价。对不同的数据赋予不同的权重,使得不同类别的错分代价不同。我们对垃圾短信赋予更高的权重,使其在分类过程中被错分的代价更大,根据经验,我们将权重比例设置为9:1。
    四、研究方法4.1 逻辑回归(Logistic Regression)逻辑回归模型一般都是用来二分类情况的,只有0,1两类。定义一个样本 是类别1的概率为:

    该函数的输出值的取值范围为[0,1],所以记:

    逻辑回归模型的代价函数为:

    然后用梯度下降就可以求得w,带入最开始的概率公式,只要概率大于0.5,就可以划分到类别1。
    4.2 支撑向量机(SVM)SVM的核心思想是求出一组权重系数,在线性表示之后可以进行分类。我们先使用一组训练集来训练SVM中的权重系数,然后可以对测试集进行分类。
    说的更加高大上一些:SVM就是先训练出一个分割超平面(separation hyperplane),该平面就是分类的决策边界,分在平面两边的是两个类别的数据。显然,经典的SVM算法只适用于两类分类问题,当然,经过改进之后,SVM也可以适用于多类分类问题。

    我们希望找到离分隔超平面最近的点,并确保它们离分隔面的距离尽可能远。这里点到分隔面的距离被称为间隔(margin)。我们希望这个间隔尽可能的大。支持向量(support vector)就是离分隔超平面最近的那些点,我们要最大化支持向量到分隔面的距离。那么为了达到上面的目的,我们就要解决这样的一个问题:如何计算一个点到分隔面的距离? 这里我们可以借鉴几何学中点到直线的距离,在SVM中,需要计算的是点到超平面的距离:

    除了定义距离之外,需要设定两类数据的类别标记分别为1和-1,这样做是因为我们将标记y和距离相乘,不管属于哪一类,乘积都是一个正数,这样有利于我们设计目标函数。这样我们便可以定义我们SVM的目标函数:

    然后我们可以转化为凸二次规划:

    构造拉格朗日函数:

    通过KKT条件和对偶问题等方法就可以求解了。
    4.3 决策树(Decision Tree)决策树(Decision Tree)是是分类问题中最常用的模型之一,它的优势在于:①能够接受类别型的特征,②分类效果与其他分类算法相当,③训练和测试的效率高。决策树模型呈树形结构,在分类问题中,表示基于特征对实例进行分类的过程。决策树学习通常包括三个步骤:特征选择、决策树的生成和决策树的修剪。决策树学习常用的算法有ID3、C4.5和CART。
    决策树在构建子节点时需要选择合适的特征进行决策,划分数据,生成子节点。“合适”指的是尽量大的减少划分后子数据集的混杂度(尽可能使同一个类别的数据分到同一个子节点中)。ID3算法使用信息增益来进行特征选择,C4.5使用信息增益率进行特征选择,C&RT作为分类树时使用基尼系数进行特征选择,作为回归树时使用最小平方误差进行特征选择。
    决策树的生成采用贪心策略,从根节点开始扩张(自顶向下),已建立的树不再改变。每次选择局部信息增益(率)最大的特征划分数据,只考虑当前节点之后选择哪个特征来建立子节点。对子节点递归调用以上方法,直到达到停止条件(所有特征的信息增益都很小或者没有特征可以选择,early-stopping)停止建树。
    在垃圾文本识别的问题中,将文本类的数据转化为词向量进行计算,因而需要对数值型的数据进行处理。决策树对于数值型的特征的处理办法通常是将特征离散化,寻找阈值,将数值划分为一个或多个区间。
    4.4 梯度提升决策树(GBDT)GBDT(Gradient Boosting Decision Tree)又叫MART(Multiple Additive Regression Tree),属于集成学习中的boosting分支,是一种迭代的决策树算法,该算法由多棵决策树组成,所有树的预测加权累加起来做最终预测。
    决策树(DT),分为分类树和回归树两种。梯度提升决策树的决策树主要是针对回归树的,回归树是可以用于回归的决策树模型,一个回归树对应着输入空间(即特征空间)的一个划分以及在划分单元上的输出值。与分类树不同的是,回归树对输入空间的划分采用一种启发式的方法,会遍历所有输入变量,找到最优的切分变量和最优的切分点,即选择特征和它的取值将输入空间划分为两部分,然后重复这个操作。
    提升(B),即在原来模型的基础之上做进一步提升,提升决策树BDT的基本思想是采用多棵决策树串行建模。具体过程为,对于第一棵树之后的每一棵决策树,都基于前一棵决策树的输出进行二次建模,整个串行建模过程相当于对预测结果朝目标值进行修正。
    梯度(G)。梯度的大小反映了当前预测值与目标值之间的距离。因此,上面B所述的串行决策树模型,除开第一棵决策树使用原始预测指标建树,之后的每一棵决策树都用前一棵决策树的预测值与目标值计算出来的负梯度(可以理解为残差或者增量)来建树。这相当于给分错的样本加权多次分类,使样本最终的残差趋近于0。除开第一棵树的其他树,由于都是对目标的残差或增量进行建模预测,因此GBDT模型只需把过程中每一棵决策树的输出结果累加,便可得到最终的预测输出。
    GBDT分类算法在思想上和回归算法没有区别,但是由于样本输出不是连续的值,而是离散的类别,导致无法直接从输出类别去拟合类别输出的误差。为解决此问题,使用类似于逻辑回归的对数似然损失函数的方法,也就是说用的是类别的预测概率值和真实概率值来拟合损失函数。
    对于二元GBDT,如果用类似于逻辑回归的对数似然损失函数,则损失函数表示为:

    则此时的负梯度误差为:

    对于生成的决策树,各个叶子结点的最佳残差拟合值为:

    由于上式比较难优化,一般使用近似值代替,近似值为:

    除了负梯度误差计算和各个叶子结点的最佳残差拟合外,二元GBDT分类和GDBT回归算法过程相同。
    五、实验设计5.1 逻辑回归(Logistic Regression)模型5.1.1 逻辑回归分类器训练要做特征提取,首先要进行分词,这里使用了jieba精准分词模式。接下来使用TFIDF方式来构建词向量。

    在sklearn中,与逻辑回归有关的有三个类。LogisticRegression,LogisticRegressionCV,Logistic_regression_path。LogisticRegression需要每次自己指定正则化系数,而LogisticRegressionCV使用了交叉验证来选择正则化系数。Logistic_regression_path它拟合数据后不能直接用来做预测,而是用来选择合适的回归系数和正则化系数,主要用在模型选择的时候。
    本实验使用的是LogisticRegression。相应的参数调优为:

    正则化系数选择L2
    Solver优化参数选择开源的liblinear库实现,内部使用了坐标轴下降法来迭代优化损失函数
    分类方法选择参数ovr,即无论多少元逻辑回归,我们都可以看做二元逻辑回归
    类型权重参数采用默认值

    5.1.2 实验结果及分析本实验通过分类的准确度,召回率,f1值对模型进行评估。
    在没有考虑交叉验证的情况下,结果如表1所示:




    Precision
    Recall
    F1-score




    0
    0.99
    1.00
    0.99


    1
    0.97
    0.93
    0.95



    可以看到分类效果比较理想,但是因为垃圾短信类型分布不平衡,即使将短信全部分类为正常短信也能获得0.9以上的正确率。所以后期可以修改垃圾短信和正常短信的权重,来达到更好的分类效果。
    考虑了交叉验证(5折)的分类结果如表2所示:




    Precision
    Recall
    F1-score




    1
    0.98396488
    0.95705903
    0.97002055


    2
    0.98321227
    0.95694792
    0.96961127


    3
    0.98328924
    0.95963542
    0.97108295


    4
    0.98345064
    0.95965278
    0.97116779


    5
    0.98493729
    0.95808681
    0.9710238


    Average
    0.9837
    0.9583
    0.9706



    交叉集的验证结果一定程度反映了分类器真实的预测能力。从表2中可以看出模型达到了初步的分类效果。
    在测试集上我使用了五万条数据用来测试(之前没有被用于训练),最终的精确率为0.98,召回率为0.95,f1值为0.97,该结果与训练集比较接近。
    5.2 支持向量机(Support Vector Machine)模型5.2.1 数据预处理首先将短信和标签分别提取出来并存储,然后利用jieba对短信内容进分词,提取文本特征,也就是将文本数据转化成特征向量的过程。比较常用的文本特征表示法为词袋法:不考虑词语出现的顺序,每个出现过的词汇单独作为一列特征。这些不重复的特征词汇集合为词表,每一个文本都可以在很长的词表上统计出一个很多列的特征向量。如果每个文本都出现的词汇,一般被标记为停用词,不计入特征向量。本模型并没有去除停用词。
    5.2.2 训练模型通过上面的过程,我们准备好了待训练的数据和训练需要的参数,其实可以理解为这个准备工作就是在为svm.train()函数准备实参的过程。来用svm.train()函数进行训练,并使用joblib保存模型:
    self.clf.fit(self.training_data, self.training_target)joblib.dump(self.clf, 'model/SVM_sklearn.pkl')5.2.3 模型评估这里用的混淆矩阵来评估模型,表3为SVM模型分类结果:



    Confusion matrix





    8950
    24


    5
    968







    Precision
    Recall
    F1-score
    Support




    0
    1.00
    1.00
    1.00
    80901


    1
    0.99
    0.98
    0.99
    9099


    Micro avg
    1.00
    1.00
    1.00
    90000


    Macro avg
    1.00
    0.99
    0.99
    90000


    Wighted avg
    1.00
    1.00
    1.00
    90000



    可以看到,即使训练部分数据时,效果也很出色,符合预期值。SVM是一种有坚实理论基础的新颖的小样本学习方法。它基本上不涉及概率测度及大数定律等,因此不同于现有的统计方法。从本质上看,它避开了从归纳到演绎的传统过程,实现了高效的从训练样本到预报样本的“转导推理”,大大简化了通常的分类和回归等问题。
    5.3 决策树(Decision Tree)模型5.3.1 决策树分类器训练数据划分
    为了对训练的模型的效果进行有效测试,将数据集划分为训练集和验证集两个部分,使用sklearn的StratifiedKFold()函数将数据分成五个部分,每次随机选取四个部分作为训练集,另一部分作为验证集。

    特征提取
    使用python的jieba工具包来进行分词的操作,对全部垃圾短信数据进行精准模式分词,分词后使用TF-IDF算法来构建短信内容的特征向量。
    在sklearn工具包中,TfidfVectorizer类可以用来计算词语的TF-IDF值。对训练集和验证集分别做处理,得到每一条短信的TF-IDF特征。

    模型训练
    在sklearn工具包中,使用DecisionTreeClassifier()来实现决策树模型,并设定特征选择方法为基尼系数,并将分割策略设置为best,为了防止过拟合,在一个节点中含有少于2个样本的情况下停止分割。
    应用到系统中的模型
    使用全量数据来训练线上模型,并使用joblib保存生成的TF-IDF特征数据及决策树模型数据。

    5.3.2 实验结果及分析使用5折交叉验证来进行评估模型,使用准确率、召回率和F1-score作为评价指标,测试的各项指标的结果如表4所示。




    Precision
    Recall
    F1-Score




    1
    0.975948473
    0.954100694
    0.964900927


    2
    0.974029417
    0.953736111
    0.963775952


    3
    0.976212743
    0.953361111
    0.964651613


    4
    0.974507729
    0.953420139
    0.963848606


    5
    0.974512832
    0.951822917
    0.963034244


    Average
    0.975042239
    0.953288194
    0.96404251



    从实验结果可以看出,决策树模型对于垃圾短信的分类有良好的效果,但仍有很大的提升空间,在大规模数据的情况下,仍会有大量的垃圾短信不能被正确识别。后期考虑对于垃圾文本的内容特征进行进一步提取,如考虑文本中的情感特征,特殊字符数量等,进行进一步分析。
    5.4 梯度提升决策树(GBDT)模型5.4.1 GBDT分类器训练特征生成
    首先使用jieba分词库对所有短信数据进行精准模式分词,得到分词后的短信集合。由于单纯统计词频会导致一些几乎在所有短信中都出现过的词的重要性非常高,而这些词在对短信是否是垃圾短信的分类上是完全不具有可判别性的特征,相比之下,那些在一条短信中词频高而在其他短信中出现频率很少的词汇对判别这条短信是否是垃圾短信更具有重要的判别性。所以,使用TF-IDF公式生成的词语的特征更适合用来作为垃圾短信分类的特征。在scikit-learn库中存在TfidfTransformer类用来计算该特征,通过调用fit_transform函数将所有分词后的短信数据转换为TF-IDF特征,同时保存下来特征文件。
    模型训练
    在scikit-learn库中,针对梯度提升决策树算法的建模,在sklearn.ensemble中存在GradientBoostingClassifier这个类,使用该类创建梯度提升决策树模型,直接使用默认配置参数对生成的TF-IDF短信数据特征进行拟合,即可得到最终的GBDT分类器模型,将生成的模型保存供垃圾短信分类测试使用。
    5.4.2 实验结果及分析对训练的分类器采用5折交叉验证去评估模型的实际效果,评价指标选择的是准确率(Precision_macro)、召回率(Recall_macro)和F度量(F1-score_macro),测试的各项指标的结果如表5所示。



    Metric
    Precision
    Recall
    F1-Score




    1
    0.97670188
    0.88220139
    0.92291139


    2
    0.97676571
    0.88470833
    0.92454531


    3
    0.9716426
    0.88649653
    0.92373754


    4
    0.978278
    0.88268
    0.923806


    5
    0.9794757
    0.8881007
    0.9277328


    Average
    0.976572786
    0.88483739
    0.924546608



    从表5中可以看出,GBDT算法的预测准确率是比较高的,但是召回率相比之下就比较低了,这导致了F1-Score结果也不是很高。初步分析,使用默认配置的GBDT算法进行模型训练的垃圾短信预测效果一般,后期可以考虑对其它的配置进行实验。首先,增加“子模型数”用来降低整体模型的方差,且不会对子模型的偏差和方差有任何影响,模型的准确度一般会随着“子模型数”的增加而提高;其次,尝试调整“最大树深度”、“最大叶结点数”、“分裂所需最小样本数”、“叶结点最小样本数”等超参数细粒度地调整决策树的结构,防止模型过拟合现象的发生,提高模型的泛化能力和垃圾短信分类能力。
    5.5 垃圾短信识别系统我们线上的垃圾短信识别系统的系统界面如下所示。

    系统可以对在输入框中输入的垃圾短信进行识别,在下方显示各个模型的识别结果,并统计每个模型识别该条短信所用时间。
    系统对短信进行识别结果为垃圾短信

    系统对垃圾短信进行识别结果为非垃圾短信

    六、总结在本次实验中,通过垃圾短信进行识别,对数据挖掘的常用方法有了较为全面的认识,能够使用python对各类算法进行实现和应用。在本次实验中,SVM模型对于垃圾短信的分类效果最好。
    参考文献[1] 腾讯安全移动实验室2018年上半年手机安全报告.腾讯安全移动实验室.[EB/OL]. https://m.qq.com/security_lab/news_detail_471.html.2018-07-23.
    [2] Maron M E . Automatic Indexing: An Experimental Inquiry[J]. Journal of the Acm, 1961, 8(3):404-417.
    [3] 王爽. 基于知识库的自动分类系统设计与实现[D]. 厦门大学, 2007.
    8 评论 71 下载 2019-03-07 10:09:55 下载需要15点积分
  • 基于C#的超市进存销管理系统

    第一章需求分析1.1 需求分析1.2 用例模型及分析类图的描述1.2.1 用例Use Case:账号密码登录
    参与者:用户
    主事件流:

    用户选择账号登录选项并输入账号密码
    将用户输入的EmpLoginName和EmpLoginPwd与数据库中相应的字段进行匹配
    若匹配成功,则跳转页面,转到主窗口。反之,则提示登陆失败

    1.2.2 用例图用例模型本系统以管理员对数据库的操作为主,实现用例图如下:

    根据对用例的分析,做出用例图如上,管理员主要利用本系统,实现对进货信息、库存信息、销售信息和职工信息、供应商信息的管理。系统采用VS环境开发,实现C/S结构,管理员对各个信息的修改都直接写入数据库。
    1.3 分析类1.3.1 用户登录模块用户登录用例图
    用户登录的用例图,如图1-3-1-1所示:

    用户登录时序图
    如图1-3-1-2所示,表示用户登录的时序图。

    用户登录分析类图(协作图)
    如图1-3-1-3所示,表示用户登录的协作图。

    1.3.2 进货模块进货用例图
    进货的用例图,如图1-3-2-1所示:

    用户登录时序图
    如图1-3-2-2所示,表示用户登录的时序图。

    用户登录分析类图(协作图)
    如图1-3-2-3所示,表示用户登录的协作图。

    1.3.3 销售模块进货用例图
    进货的用例图,如图1-3-3-1所示:

    用户登录时序图
    如图1-3-3-2所示,表示用户登录的时序图

    用户登录分析类图(协作图)
    如图1-3-3-3所示,表示用户登录的协作图

    1.3.4 库存模块进货用例图
    进货的用例图,如图1-3-4-1所示:

    用户登录时序图
    如图1-3-4-2所示,表示用户登录的时序图。

    用户登录分析类图(协作图)
    如图1-3-4-3所示,表示用户登录的协作图。

    1.3.5 职工管理模块进货用例图
    进货的用例图,如图1-3-5-1所示:

    用户登录时序图
    如图1-3-5-2所示,表示用户登录的时序图

    用户登录分析类图(协作图)
    如图1-3-5-3所示,表示用户登录的协作图

    1.3.6 供应商管理模块进货用例图
    进货的用例图,如图1-3-6-1所示:

    用户登录时序图
    如图1-3-6-2所示,表示用户登录的时序图

    用户登录分析类图(协作图)
    如图1-3-6-3所示,表示用户登录的协作图

    第二章 概要分析2.1 系统架构设计
    2.2 数据库设计2.2.1 数据库总体概念设计数据库总体E-R图如图2-2-1所示。

    2.2.2 数据库概念设计通过对于系统的需求分析,整套系统可以设计出六个实体,他们分别是职工实体、供应商实体、进货实体、销售实体、库存实体。
    系统的使用者涉及到多种用户,多以需要一个用户表来保存登陆账号和登陆密码。
    职工表E-R图

    职工数据库表

    供应商表E-R图

    供应商数据库表

    进货表E-R图

    进货数据库表

    销售表E-R图

    销售表

    库存数据库表E-R图

    库存数据库表

    2.3 系统类图设计分析系统,本系统主要包含数据库类和操作类。数据库类包括有进货信息数据库、销售信息数据库、库存信息数据库。操作类主要是对数据库的操作,包括有添加进货、销售、库存、职工、供应商详细信息两个操作。其中添加进货单可以对进货、销售、库存信息数据库执行添加,修改,删除、查找的操作,添加职工、供应商信息可以对物品信息数据库执行添加修改删除的操作。操作类还包含对数据的查询操作,可以根据关键字进行查询;分析以上各个类,作出类图如2-4所示。

    第三章 系统详细设计及实现3.1 系统功能描述本系统主要实现登陆注册模拟超市进货增删改查、销售增删改查、库存增删改查以及对超市内员工的增删和超市供应商的增删。
    3.2 用户登录界面用户登录界面如图3-2所示。

    3.3 职工管理功能职工管理如图3-3所示。

    3.4 供应商管理功能供应商管理如图3-4所示。

    3.5 进货管理功能进货增删改管理如图3-5-1所示。

    进货查询如图3-5-2所示。

    3.6 销售管理功能销售增删改管理如图3-6-1所示。

    销售查询如图3-6-2所示。

    3.7 库存管理功能库存增删改管理如图3-7-1所示。

    库存查询如图3-7-2所示。

    3.8 系统主页面系统主页面如图3-8所示。

    3.9 退出系统退出系统功能如图3-9所示。
    5 评论 84 下载 2019-09-02 12:27:33 下载需要13点积分
  • Java飞机大战游戏设计与实现

    1 概述1.1 项目简介本次Java课程设计是做一个飞机大战的游戏,应用Swing编程,完成一个界面简洁流畅、游戏方式简单,玩起来易于上手的桌面游戏。该飞机大战项目运用的主要技术即是Swing编程中的一些窗口类库、事件监听以及贴图技术。
    1.2 实训功能说明1.2.1 基本功能
    通过键盘,方向键和ASWD键可控制战机的位置,空格键和鼠标左键发射子弹
    界面中敌机出现的位置,以及敌机和Boss炸弹的发射均为随机的,敌机与敌机炸弹、Boss炸弹均具有一定的速度,且随着关卡难度的增大,数量和速度均随着关卡数增加而增加
    对于随机产生的敌机和敌机炸弹,若超过矩形区域,则释放该对象
    添加碰撞效果,包括战机子弹打中敌机爆炸、敌机炸弹打中战机爆炸、战机与敌机相撞爆炸、战机子弹与敌机炸弹相撞爆炸、战机子弹打中Boss、战机与Boss碰撞以及战机吃到血包七种碰撞效果。且碰撞发生后子弹、炸弹、血包均消失,战机生命值减一,敌机和Boss生命值减少当前战机炮弹威力的生命值,若敌机或Boss生命值归零,则删除敌机或Boss
    血包:随着关卡游戏进程的进行,会出现一定量的血包供战机补给生命值,血包会在客户区矩形框内运动,10秒后消失;若战机在10秒内吃到血包,则会增加5点生命值知道生命值上限
    每关中战机有三条命,每条命10点生命值,生命使用完后,会进入GameOver界面显示得分数,并提供重新开始游戏和退出功能
    游戏提供10个关卡,每个关卡需要打死相应关卡的敌机数量才能进入Boss模式,打败Boss之后将会进入下一关。10关通关后,显示通关界面,并提供重新开始游戏和退出游戏的功能选项
    暂停功能:游戏进行过程中按下Z键可进入暂停模式,再按Z则返回游戏
    无敌模式:游戏进行过程中按下Y键可进入无敌模式,再按Y则返回正常游戏。该模式下战机生命值不会减少,可供测试使用
    魔法值:游戏进行过程中,战机魔法值会随着时间递增到上限10,魔法值供战机道具功能的使用,过一个关卡魔法值不清零
    战机大招:当战机魔法值为10满状态时,按下X键消耗所有魔法值可发动大招,对屏幕中的敌机进行清屏,Boss扣50点血量
    防护罩:当魔法值不为0时,按下C键可打开防护罩道具,该状态下战机处于无敌状态,不会损失生命值,但魔法值会随着防护罩开启慢慢降低
    战机升级功能:战机子弹单个威力为1,在魔法值不为0时,按下V键开启升级战机模式,战机图标变为动画,子弹威力变成两倍。(若同时开启防护罩和战机升级,则魔法值递减速度翻倍)

    1.2.2 附加功能
    为游戏界面每个关卡添加了滚动背景图片和背景音乐,并在敌机发送炮弹、战机发射子弹、战机击中敌机、敌机击中战机、战机敌机相撞、敌机战机子弹相撞、战机吃到血包、战机大招、战机升级、战机防护罩、游戏结束时均添加了音效
    为美化游戏界面,采用了一部分全民飞机大战图标,并添加了爆炸动画和升级战机动画特效,背景音乐采用微信飞机大战背景音乐和相关特效音效
    为游戏设置了不同的关卡,每个关卡难度不同,敌机与敌机炸弹的速度随着关卡增大而加快,进入第五关以后敌机从上下方均会随机出现,且随机发射炸弹
    前五关卡敌机从上方飞出,速度一定,战机每打掉一架敌机则增加一分,当战机得分超过该关卡所需分数(和关卡数相关)则可进入Boss模式,打败Boss进入下一关;进入第六关以后,敌机分别从上下两方飞出。随着关卡数增加,敌机数量增加,速度增快,敌机炮弹数量和速度也相应增加,进入Boss所需分数增加,Boss生命值和火力也随着关卡数的增加而增加,游戏难度陡然直升
    游戏界面中显示当前状态下的关卡数、当前命数、当前得分、战机血条、战机魔法条、无敌模式提醒和战机道具提醒,Boss模式下还有Boss血条
    增加了鼠标控制战机位置这一效果,战绩的位置随着鼠标的移动而移动,并且点击鼠标左键可使得战机发射子弹
    进入游戏先进入欢迎界面,欢迎界面中显示游戏使用说明,点击鼠标左键和空格键开始游戏。游戏过程中战机命数使用完、通关均有相应界面进行提醒,用户可选择重新开始游戏或退出游戏

    2 相关技术2.1 Timer定时器技术本次项目采用了Java的Timer定时器和TimerTask任务,Timer周期性地在每经过一个指定的时间间隔后就通知TimerTask一次,让TimerTask按照Timer设定的周期循环地执行任务。本程序中使用多个定时器,分别控制不同的功能,分别是屏幕刷新Timer,敌机产生Timer,魔法值变化Timer,血包生命周期Timer。
    2.2 透明贴图实现技术绘制透明位图的关键就是创建一个“掩码”位图(mask bitmap),这个“掩码”位图是一个单色位图,它是位图中图像的一个单色剪影。
    整个绘制过程需要使用到ImageUtil类的createImageByMaskColorEx静态方法,把传入的原图BufferedImage中的指定颜色的背景去掉,返回去掉背景的BufferedImage对象,传送到窗口进行展示。
    2.3 游戏对象列表Java类库中提供了丰富的List接口的实现方法,本项目采用ArrayList存放游戏运行过程中的游戏对象。
    滚动背景模块
    // 欢迎界面图像列表private static BufferedImage titleImage;// 游戏背景对象public static Scene scene;
    各游戏对象
    public static MyPlane myplane = null;Enemy enemy = null;public static Boss boss = null;Bomb bomb = null;Ball ball = null;Explosion explosion = null;Blood blood = null;
    存储游戏对象的对象列表
    public static List<Enemy> enemyList = new ArrayList<Enemy>();public static List<MyPlane> meList = new ArrayList<MyPlane>();public static List<Bomb> bombList = new ArrayList<Bomb>();public static List<Ball> ballList = new ArrayList<Ball>();public static List<Explosion> explosionList = new ArrayList<Explosion>();public static List<Blood> bloodList = new ArrayList<Blood>();
    游戏运行相关参数
    int speed; // 战机的速度,方向键控制public static int myLife; // 为战机设置生命值public static int lifeNum; // 战机命条数public static int myScore; // 战机的得分public static int passScore; // 当前关卡得分数public static int lifeCount; // 血包产生控制参数public static boolean bloodExist; // 标记屏幕中是否存在血包public static int magicCount; // 魔法值,控制能否发大招public static int bossBlood; // Boss血量public static int passNum; // 记录当前关卡
    游戏运行相关标志位
    public static boolean isPass; // 是否通关的标志public static boolean isPause; // 是否暂停public static boolean isBoss; // 标记是否进入Bosspublic static boolean bossLoaded; // 标记Boss出场完成public static boolean isProtect; // 标记是否开启防护罩public static boolean isUpdate; // 标记战机是否升级public static boolean test; // 无敌模式参数public static int isStop; // 标记游戏停止public static boolean isStarted; // 标记欢迎界面是否加载完成
    2.4获取矩形区域使用 Rectangle的intersects方法来判断两个源矩形是否有重合的部分。如果有重合,返回true,没有从何返回false。
    2.5 List<BufferedImage>处理爆炸效果爆炸效果是连续的显示一系列的图片。如果把每一张图片都在要显示的时候进行加载,占用的时间是非常多的,必然后导致程序的可行性下降。List<BufferedImage>是一个“图象列表”是相同大小图象的集合,每个图象都可由其基于零的索引来参考。可以用来存放爆炸效果的一组图片,通过Timer消息连续循环绘制出List<BufferedImage>中的多张图片做成的爆炸效果。
    3 总体设计与详细设计3.1 系统模块划分该飞机大战游戏程序分为游戏滚动背景绘制模块、各游戏对象绘制模块、游戏对象之间的碰撞模块、爆炸效果产生模块、游戏界面输出玩家得分关卡信息模块、战机道具技能维护模块、消息处理模块、视图生命周期维护模块。
    其中在游戏对象绘制模块中,战机是唯一对象,在游戏开始时产生该对象,赋予其固定的生命值,当其与敌机对象、敌机炸弹碰撞时使其生命值减一,直至生命值为零,便删除战机对象。敌机对象与敌机炸弹对象的绘制中采用定时器技术,定时产生。爆炸对象初始化为空,当游戏过程中即时发生碰撞时,在碰撞位置产生爆炸对象,添加到爆炸链表中,并根据爆炸素材图像分八帧进行输出,达到动画特效。
    3.2 主要功能模块3.2.1 系统对象类图
    GameObject是各个游戏对象的抽象父类,继承自Object类,其他的类:战机类、敌机类、爆炸类、子弹类、炸弹类、血包类、文字类都继承了此类,Boss类继承敌机类。每个游戏对象类中既继承了来自父类GameObject的属性,又有自己的特有属性和方法。
    GameObject类介绍:
    // 该游戏对象在窗口中的坐标位置protected Point point;// 在窗口中绘制该游戏对象图标public boolean draw(Graphics g, JPanel panel, boolean pause);// 获取该游戏对象图标在窗口中的矩形框,进行碰撞检测时使用public Rectangle getRect();// 加载该游戏对象对应的图像到内存,方便之后的显示调用public static boolean loadImage(BufferedImage image, String source);
    继承GameObject的其他游戏对象类也都重载了相关方法,以便于各个游戏对象在程序的调用过程中调用方式的一致性。
    3.2.2 项目包和类层次结构图

    MyPanel:JPanel的继承类,是飞机大战窗口的总显示面板,其中包含了游戏运行过程中的大量全局参数和全局标记位
    SpaceWar:程序的入口,窗口对象启动的位置,并在此对相关事件进行了监听
    Ball:敌机炮弹类,继承GameObject,有加载图片,获取矩形框,获取对象位置,绘制回想等游戏对象通用操作方法
    Blood:血包类,继承GameObject,有加载图片,获取矩形框,获取对象位置,绘制回想等游戏对象通用操作方法
    Bomb:战机炮弹类,继承GameObject,有加载图片,获取矩形框,获取对象位置,绘制回想等游戏对象通用操作方法
    Boss:Boss类,继承GameObject,有加载图片,获取矩形框,获取对象位置,绘制回想等游戏对象通用操作方法
    Enemy:敌机类,继承GameObject,有加载图片,获取矩形框,获取对象位置,绘制回想等游戏对象通用操作方法
    Explosion:爆炸效果类,继承GameObject,有加载图片,获取矩形框,获取对象位置,绘制图片等游戏对象通用操作方法
    GameObject:游戏对象基类,有加载图片,获取矩形框,获取对象位置,绘制图片的游戏对象统一的方法,统一游戏对象的操作方式
    MyPlane:战机类,继承GameObject,有加载图片,获取矩形框,获取对象位置,绘制图片等游戏对象通用操作方法
    Scene:场景类,实现了背景滚动,并根据关卡显示不同的背景图片
    EnemyTask:实现了TimerTask接口,随着Timer计时器的调用而随时间产生敌机对象和敌机炮弹对象,实现自动产生敌机的效果
    MagicTask:实现了TimerTask接口,随着Timer计时器的调用而随时间改变魔法值
    RefreshTask:实现了TimerTask接口,随着Timer计时器的调用而随时间刷新窗口界面
    AudioUtil:音频操作工具类,提供播放背景音乐和操作音效
    ImageUtil:图片加工工具类,实现了透明贴图方法和背景图片拼接

    3.2.3 系统主程序活动图
    3.2.4 系统部分流程图飞机大战游戏执行流程图

    定时器产生敌机和炸弹流程图

    血包执行流程图

    4 编码实现4.1 滚动背景在滚动背景的初始化方法和释放方法添加背景音乐播放和释放
    //场景类public class Scene { private int beginY;// 背景的Y坐标 private List<BufferedImage> images; public Scene() { this.images = new ArrayList<BufferedImage>(); } // 初始化场景 public boolean initScene() { // 加载开始图片 BufferedImage buffer; try { buffer = ImageUtil.copyImage(ImageIO.read(new File( "images/start.bmp"))); this.images.add(buffer); // 如果加载失败, 返回false for (int i = 1; i <= 6; i++) { buffer = ImageUtil.copyImage(ImageIO.read(new File( "images/background" + i + ".bmp"))); this.images.add(buffer); } } catch (IOException e) { e.printStackTrace(); return false; } // 背景起始坐标为0 this.beginY = 0; // 播放背景音乐 AudioUtil.playBackground(); return true; } // 绘制场景 public void stickScene(Graphics graphics, int index, ImageObserver observer) { if (index == -1) index = 0; else index = index % 6 + 1; BufferedImage image = images.get(index); // 窗口滑在图片中间 if (beginY >= 0 && beginY + SpaceWar.WINDOWS_HEIGHT <= image.getHeight()) { BufferedImage buffer = image.getSubimage(0, beginY, image.getWidth(), SpaceWar.WINDOWS_HEIGHT); graphics.drawImage(buffer, 0, 0, SpaceWar.WINDOWS_WIDTH, SpaceWar.WINDOWS_HEIGHT, observer); } else if (beginY < 0) { // 超出图片上界 BufferedImage imageUp = image.getSubimage(0, image.getHeight() + beginY, image.getWidth(), -beginY); graphics.drawImage(imageUp, 0, 0, SpaceWar.WINDOWS_WIDTH, -beginY, observer); graphics.drawImage(image, 0, -beginY, SpaceWar.WINDOWS_WIDTH, SpaceWar.WINDOWS_HEIGHT, observer); if (-beginY > SpaceWar.WINDOWS_HEIGHT) { beginY = image.getHeight() + beginY; } } } // 移动背景 public void moveBg() { // 移动背景 beginY -= 1; } // 释放内存资源 public void releaseScene() { for (int i = 0; i < 7; i++) if (images.get(i) != null) images.get(i).flush(); // 关闭背景音乐 AudioUtil.stopBackground(); } public int getBeginY() { return beginY; } public void setBeginY(int beginY) { this.beginY = beginY; }}//在MyPanel中刷新滚动// 滚动背景scene.stickScene(g, -1, this);scene.moveBg();
    4.2 显示战机if (myplane != null) { myplane.draw(g, this, isPause, isProtect);}
    4.3 随机产生敌机和敌机炮弹、Boss炮弹 //随机添加敌机,敌机随机发射炸弹,此时敌机速度与数量和关卡有关 // 根据关卡数产生敌机 if (MyPanel.passNum <= 5) { // 前五关只有一个方向的敌机 Enemy enemy = new Enemy(Enemy.ENEMY_SPEED, 1);// 设置敌机的方向,从上方飞出 enemyList.add(enemy);// 随机产生敌机 if (new Random().nextInt(2) == 0) {// 控制敌机炮弹发出频率 Ball ball = new Ball( enemy.getPoint().x + Enemy.ENEMY_WIDTH / 2, enemy.getPoint().y + Enemy.ENEMY_HEIGHT, enemy.getDirection()); ball.setBallSpeed(enemy.getSpeed()+2); MyPanel.ballList.add(ball); // 音效 AudioUtil.play(AudioUtil.AUDIO_BALL); } } else if (MyPanel.passNum > 5) {// 第五关之后,两个方向的敌机 Enemy enemy1 = new Enemy(Enemy.ENEMY_SPEED, 1);// 设置敌机的方向,从上方飞出 enemy1.setSpeed(Enemy.ENEMY_SPEED + (new Random().nextInt(2) + MyPanel.passNum - 1)); enemyList.add(enemy1); Enemy enemy2 = new Enemy(Enemy.ENEMY_SPEED, -1);// 设置敌机的方向,从下方飞出 enemy2.setSpeed(Enemy.ENEMY_SPEED + (new Random().nextInt(2) + MyPanel.passNum - 1)); enemyList.add(enemy2); int rand = new Random().nextInt(3); if (rand == 0) {// 控制敌机炮弹发出频率 Ball ball = new Ball(enemy1.getPoint().x + Enemy.ENEMY_WIDTH / 2, enemy1.getPoint().y + Enemy.ENEMY_HEIGHT, enemy1.getDirection()); ball.setBallSpeed(enemy1.getSpeed()+2); MyPanel.ballList.add(ball); // 音效 AudioUtil.play(AudioUtil.AUDIO_BALL); } if (rand == 1) {// 控制敌机炮弹发出频率 Ball ball = new Ball(enemy2.getPoint().x + Enemy.ENEMY_WIDTH / 2, enemy2.getPoint().y, enemy2.getDirection()); ball.setBallSpeed(enemy2.getSpeed()+2); MyPanel.ballList.add(ball); // 音效 AudioUtil.play(AudioUtil.AUDIO_BALL); } } if (MyPanel.isBoss) { // Boss发射子弹 // 敌机炸弹产生定时器触发 // 设置定时器产生敌机炸弹 Ball ball1 = new Ball(MyPanel.boss.getPoint().x + Boss.BOSS_WIDTH / 2, MyPanel.boss.getPoint().y + Boss.BOSS_HEIGHT, 1); ball1.setBallSpeed(Ball.BALL_SPEED + (MyPanel.passNum - 1) * 2); MyPanel.ballList.add(ball1); Ball ball2 = new Ball(MyPanel.boss.getPoint().x + 5, MyPanel.boss.getPoint().y + Boss.BOSS_HEIGHT, 1); ball2.setBallSpeed(Ball.BALL_SPEED + (MyPanel.passNum - 1) * 2); MyPanel.ballList.add(ball2); Ball ball3 = new Ball(MyPanel.boss.getPoint().x + Boss.BOSS_WIDTH - 5, MyPanel.boss.getPoint().y + Boss.BOSS_HEIGHT, 1); ball3.setBallSpeed(Ball.BALL_SPEED + (MyPanel.passNum - 1) * 2); MyPanel.ballList.add(ball3); Ball ball4 = new Ball(MyPanel.boss.getPoint().x + Boss.BOSS_WIDTH / 2 + 85, MyPanel.boss.getPoint().y + Boss.BOSS_HEIGHT, 1); ball4.setBallSpeed(Ball.BALL_SPEED + (MyPanel.passNum - 1) * 2); MyPanel.ballList.add(ball4); Ball ball5 = new Ball(MyPanel.boss.getPoint().x + Boss.BOSS_WIDTH / 2 - 85, MyPanel.boss.getPoint().y + Boss.BOSS_HEIGHT, 1); ball5.setBallSpeed(Ball.BALL_SPEED + (MyPanel.passNum - 1) * 2); MyPanel.ballList.add(ball5); // 音效 AudioUtil.play(AudioUtil.AUDIO_BALL); }
    4.4 显示战机发射子弹for (int i = 0; i < bombList.size(); i++) { bomb = bombList.get(i); if (bomb == null) continue; bomb.setCurrentIndex(i); bomb.isUpdate = isUpdate; if (!bomb.draw(g, this, isPause)) i--; }
    4.5 碰撞检测,以战机子弹集中敌机为例if (MyPanel.myplane != null && !MyPanel.isPause) { // 子弹打中敌机 boolean flag = false; for (int i = 0; i < MyPanel.bombList.size(); i++) { Bomb bomb = MyPanel.bombList.get(i); if (bomb == null) continue; Rectangle bombRectangle = bomb.getRect(); for (int j = 0; j < MyPanel.enemyList.size(); j++) { Enemy enemy = MyPanel.enemyList.get(j); if (enemy == null) continue; Rectangle enemyRectangle = enemy.getRect(); if (enemyRectangle.intersects(bombRectangle)) { Explosion explosion = new Explosion( (bomb.getPoint().x + Bomb.BOMB_WIDTH / 2 - Explosion.EXPLOSION_WIDTH / 2), (bomb.getPoint().y + Bomb.BOMB_HEIGHT / 2 - Explosion.EXPLOSION_WIDTH / 2)); MyPanel.explosionList.add(explosion); // 音效 AudioUtil.play(AudioUtil.AUDIO_EXPLOSION); // 爆炸后删除子弹 MyPanel.bombList.remove(i); i--; // 敌机生命值减少 enemy.life -= MyPanel.isUpdate ? 2 : 1; if (enemy.life <= 0) { // 增加得分 MyPanel.passScore++; // 删除敌机 MyPanel.enemyList.remove(j); j--; } // 炮弹已删除,直接跳出本循环 flag = true; break; } } if (flag) continue; if (MyPanel.isBoss && bomb != null) { // 获得战机子弹的矩形区域 Rectangle bombRect = bomb.getRect(); // 获得Boss的矩形区域 Rectangle bossRect = MyPanel.boss.getRect(); // 判断两个矩形区域是否有交接 if (bombRect.intersects(bossRect)) { // 将爆炸对象添加到爆炸链表中 Explosion explosion = new Explosion( (bomb.getPoint().x + Bomb.BOMB_WIDTH / 2 - Explosion.EXPLOSION_WIDTH / 2), (bomb.getPoint().y + Bomb.BOMB_HEIGHT / 2 - Explosion.EXPLOSION_WIDTH / 2)); MyPanel.explosionList.add(explosion); // 音效 AudioUtil.play(AudioUtil.AUDIO_EXPLOSION); // 爆炸后删除子弹 MyPanel.bombList.remove(i); i--; bomb = null; // 是Boss,不删除敌机,只扣血 MyPanel.bossBlood -= MyPanel.isUpdate ? 2 : 1; if (MyPanel.bossBlood <= 0) { Explosion explosion1 = new Explosion( MyPanel.boss.getPoint().x, MyPanel.boss.getPoint().y); MyPanel.explosionList.add(explosion1); Explosion explosion2 = new Explosion( (MyPanel.boss.getPoint().x + Boss.BOSS_WIDTH), (MyPanel.boss.getPoint().y + Boss.BOSS_HEIGHT)); MyPanel.explosionList.add(explosion2); Explosion explosion3 = new Explosion( (MyPanel.boss.getPoint().x + Boss.BOSS_WIDTH), (MyPanel.boss.getPoint().y)); MyPanel.explosionList.add(explosion3); Explosion explosion4 = new Explosion( (MyPanel.boss.getPoint().x), (MyPanel.boss.getPoint().y + Boss.BOSS_HEIGHT)); MyPanel.explosionList.add(explosion4); Explosion explosion5 = new Explosion( (MyPanel.boss.getPoint().x + Boss.BOSS_WIDTH / 2 - Explosion.EXPLOSION_WIDTH / 2), (MyPanel.boss.getPoint().y + Boss.BOSS_HEIGHT / 2 - Explosion.EXPLOSION_WIDTH / 2)); explosion5.setBossDie(true);// 标记最后一个炸弹,炸完之后跳入下一关 MyPanel.explosionList.add(explosion5); MyPanel.boss = null; // 过关的标志变量 // isPause = TRUE; // CMyPlane* temp = myplane; // myplane = new CMyPlane(FALSE); MyPanel.myplane = null; MyPanel.isPass = true; MyPanel.isBoss = false; } } } } }
    4.6 显示爆炸效果for (int i = 0; i < explosionList.size(); i++) { explosion = explosionList.get(i); if (explosion == null) continue; boolean b = explosion.draw(g, this, isPause); if (!b) { explosionList.remove(i); i--; } }
    4.7 血包功能游戏三分之一和三分之二进程时刻出现血包
    //开启血包 if (MyPanel.myplane != null && MyPanel.myLife > 0 && !MyPanel.isPause) { // 关卡打了三分之一三分之二处出现血包 if (MyPanel.passScore > (MyPanel.PASS_SCORE + MyPanel.passNum * 5) * MyPanel.lifeCount / 3) { // 若屏幕中有未吃掉的血包,这次不产生血包 if (!MyPanel.bloodExist) { MyPanel.lifeCount++; // 产生血包 Blood blood = new Blood(); MyPanel.bloodList.add(blood); MyPanel.bloodExist = true; bloodTimer = new Timer(); bloodTimer.schedule(new TimerTask() { @Override public void run() { bloodTimer.cancel(); bloodTimer = null; MyPanel.bloodExist = false; // 声明血包位置 for (int i = 0; i < MyPanel.bloodList.size(); i++) { MyPanel.bloodList.remove(i); i--; } } }, 10000); } else MyPanel.lifeCount++; } } //血包定时器,10秒后血包消失 bloodTimer = new Timer(); bloodTimer.schedule(new TimerTask() { @Override public void run() { bloodTimer.cancel(); bloodTimer = null; MyPanel.bloodExist = false; // 声明血包位置 for (int i = 0; i < MyPanel.bloodList.size(); i++) { MyPanel.bloodList.remove(i); i--; } } }, 10000); //显示血包 if (myplane != null && !isPause) { // 检索血包链表,非空时在所在位置显示 int i = 0; while (i < bloodList.size()) { blood = bloodList.get(i); if (blood == null) continue; blood.draw(g, this, false); i++; }// while }//血包碰撞检测 if (MyPanel.myplane != null && !MyPanel.isPause) { // 吃到血包 // 声明血包位置 for (int i = 0; i < MyPanel.bloodList.size(); i++) { Blood blood = MyPanel.bloodList.get(i); // 获得血包矩形 Rectangle bloodbRect = blood.getRect(); // 获得战机矩形 Rectangle planeRect = MyPanel.myplane.getRect(); // 判断两个矩形区域是否有交接 if (bloodbRect.intersects(planeRect)) {// 音效 AudioUtil.play(AudioUtil.AUDIO_BLOOD); // 加血效果 MyPanel.myLife += 5; if (MyPanel.myLife > MyPanel.DEFAULT_LIFE) MyPanel.myLife = MyPanel.DEFAULT_LIFE; // TODO 声音 // 加血后血包删除 MyPanel.bloodList.remove(i); i--; break; }// if }// for }
    4.8 通关和死亡消息页面if (isStop == FLAG_RESTART) { Font textFont = new Font("宋体", Font.BOLD, 20); g.setFont(textFont); // 设置透明背景 // cdc.SetBkMode(TRANSPARENT); g.setColor(Color.red); g.drawString("哇,恭喜你已通关!", SpaceWar.WINDOWS_WIDTH / 2 - 100, SpaceWar.WINDOWS_HEIGHT / 2 - 30); g.drawString("您的得分为:" + myScore, SpaceWar.WINDOWS_WIDTH / 2 - 100, SpaceWar.WINDOWS_HEIGHT / 2 - 10); g.drawString("COME ON !重新开始?Y/N", SpaceWar.WINDOWS_WIDTH / 2 - 100, SpaceWar.WINDOWS_HEIGHT / 2 + 10); return; } else if (isStop == FLAG_STOP) { Font textFont = new Font("宋体", Font.BOLD, 20); g.setFont(textFont); // 设置透明背景 // cdc.SetBkMode(TRANSPARENT); g.setColor(Color.red); // 显示最后结果 g.drawString("GAME OVER!", SpaceWar.WINDOWS_WIDTH / 2 - 100, SpaceWar.WINDOWS_HEIGHT / 2 - 30); g.drawString("您的得分为:" + myScore, SpaceWar.WINDOWS_WIDTH / 2 - 100, SpaceWar.WINDOWS_HEIGHT / 2 - 10); g.drawString("COME ON !重新开始?Y/N", SpaceWar.WINDOWS_WIDTH / 2 - 100, SpaceWar.WINDOWS_HEIGHT / 2 + 10); return; }
    4.9 魔法值控制维护public class MagicTask extends TimerTask { @Override public void run() { if (MyPanel.myplane != null && !MyPanel.isPause && MyPanel.isStarted) { // 防护罩和战机升级没打开,魔法值递增 if (!MyPanel.isProtect && !MyPanel.isUpdate) { MyPanel.magicCount++; if (MyPanel.magicCount > 10) MyPanel.magicCount = 10; } // 判断是否打开防护罩 if (MyPanel.isProtect) { // 开启防护罩魔法值递减 MyPanel.magicCount--; if (MyPanel.magicCount <= 0) { MyPanel.magicCount = 0; MyPanel.isProtect = false; } } // 判断是否升级战机 if (MyPanel.isUpdate) { // 战机升级,魔法值递减 MyPanel.magicCount--; if (MyPanel.magicCount <= 0) { MyPanel.magicCount = 0; MyPanel.isUpdate = false; MyPanel.myplane.isUpdate = MyPanel.isUpdate; } } } }}
    4.10 得分到达关卡需求,进入Boss // 进入Boss int pScore = MyPanel.PASS_SCORE + MyPanel.passNum * 5; // TODO调试条件 // if (MyPanel.myplane != null && MyPanel.passScore >= 3 && // !MyPanel.isPause&&!MyPanel.isBoss) if (MyPanel.myplane != null && MyPanel.passScore >= pScore && !MyPanel.isPause && !MyPanel.isBoss) { // 进入Boss MyPanel.isBoss = true; MyPanel.boss = new Boss(1); MyPanel.boss.setSpeed(Boss.BOSS_SPEED + MyPanel.passNum - 1); MyPanel.boss.life = Boss.BOSS_LIFE + MyPanel.passNum * 50;// Boss总血量 MyPanel.bossBlood = Boss.BOSS_LIFE + MyPanel.passNum * 50;// 当前Boss血量 // Boss出场,暂停游戏 MyPanel.bossLoaded = false; // 重新设置Boss的子弹产生频率,增强Boss子弹发射频率 MyPanel.enemyTimer.cancel(); MyPanel.enemyTimer = null; MyPanel.enemyTimer = new Timer(); MyPanel.enemyTimer.schedule(new EnemyTask(MyPanel.enemyList), 0, 2000 - MyPanel.passNum * 120); }//显示Boss if (myplane != null && boss != null && !isPause && isBoss) { boolean status = boss.draw(g, this, passNum, isPause); if (status) bossLoaded = true; }
    4.11 检测标记位isPass,判断打赢Boss,进入下一关if (MyPanel.isPass) { MyPanel.isPass = false; if (MyPanel.passNum == 10)// 10关 { // 重新初始化数据 MyPanel.killTimer(); MyPanel.myplane = new MyPlane(false); MyPanel.isPause = true; MyPanel.isStop = MyPanel.FLAG_RESTART; // 清屏 }// if else { MyPanel.killTimer(); MyPanel.isPause = true; // 保存所需数据 int tScore = MyPanel.myScore + MyPanel.passScore; int tPassNum = MyPanel.passNum + 1; boolean tTest = MyPanel.test; int magic = MyPanel.magicCount; // 重新开始游戏 MyPanel.Restart(); MyPanel.myplane = new MyPlane(false); MyPanel.myScore = tScore; MyPanel.passNum = tPassNum; MyPanel.magicCount = magic; MyPanel.test = tTest; }// else }// if
    4.12 消息监听4.12.1 按键监听 // 按键监听 frame.addKeyListener(new KeyListener() { @Override public void keyTyped(KeyEvent e) { // TODO Auto-generated method stub } @Override public void keyReleased(KeyEvent e) { // TODO Auto-generated method stub } @Override public void keyPressed(KeyEvent event) { if (MyPanel.myplane != null && !MyPanel.isPause) { switch (event.getKeyCode()) { case KeyEvent.VK_LEFT: int x = MyPanel.myplane.getPoint().x - MyPanel.DEFAULT_SPEED; if (x < 0) x = 0; MyPanel.myplane.setPoint(new Point(x, MyPanel.myplane .getPoint().y)); break; case KeyEvent.VK_RIGHT: int x1 = MyPanel.myplane.getPoint().x + MyPanel.DEFAULT_SPEED; if (x1 > SpaceWar.WINDOWS_WIDTH - Bomb.BOMB_WIDTH) x1 = SpaceWar.WINDOWS_WIDTH - Bomb.BOMB_WIDTH; MyPanel.myplane.setPoint(new Point(x1, MyPanel.myplane .getPoint().y)); break; case KeyEvent.VK_UP: int y = MyPanel.myplane.getPoint().y - MyPanel.DEFAULT_SPEED; if (y < 0) y = 0; MyPanel.myplane.setPoint(new Point(MyPanel.myplane .getPoint().x, y)); break; case KeyEvent.VK_DOWN: int y1 = MyPanel.myplane.getPoint().y + MyPanel.DEFAULT_SPEED; if (y1 > SpaceWar.WINDOWS_HEIGHT) y1 = SpaceWar.WINDOWS_HEIGHT; MyPanel.myplane.setPoint(new Point(MyPanel.myplane .getPoint().x, y1)); break; case KeyEvent.VK_SPACE: Bomb bomb1 = new Bomb( MyPanel.myplane.getPoint().x + 10, MyPanel.myplane.getPoint().y, 1, MyPanel.isUpdate); MyPanel.bombList.add(bomb1); Bomb bomb2 = new Bomb(MyPanel.myplane.getPoint().x + MyPlane.PLANE_WIDTH - 40, MyPanel.myplane .getPoint().y, 1, MyPanel.isUpdate); MyPanel.bombList.add(bomb2); // 音效 AudioUtil.play(AudioUtil.AUDIO_BOMB); break; case KeyEvent.VK_C: // 开启防护罩 MyPanel.isProtect = true; // 音效 AudioUtil.play(AudioUtil.AUDIO_PROTECT); break; case KeyEvent.VK_V: // 战机升级 MyPanel.isUpdate = true; MyPanel.myplane.isUpdate = true; // 音效 AudioUtil.play(AudioUtil.AUDIO_UPDATE); break; case KeyEvent.VK_Y: if (MyPanel.isStop == 0) { // 无敌模式开关 if (MyPanel.test == false) MyPanel.test = true; else MyPanel.test = false; } break; case KeyEvent.VK_X:// 大招 if (MyPanel.bossLoaded) { // 战机发大招 if (MyPanel.magicCount >= 10) { MyPanel.magicCount -= 10; // 清空敌机 for (int i = 0; i < MyPanel.enemyList.size(); i++) { Enemy enemy = MyPanel.enemyList.get(i); // 将爆炸对象添加到爆炸链表中 Explosion explosion = new Explosion( (enemy.getPoint().x + Enemy.ENEMY_WIDTH / 2), (enemy.getPoint().y + Enemy.ENEMY_HEIGHT / 2)); MyPanel.explosionList.add(explosion); // 删除敌机 MyPanel.enemyList.remove(i); // 增加得分 MyPanel.passScore++; }// for if (MyPanel.isBoss) { // 将爆炸对象添加到爆炸链表中 Explosion explosion = new Explosion( MyPanel.boss.getPoint().x + Boss.BOSS_WIDTH / 2, MyPanel.boss.getPoint().y + Boss.BOSS_HEIGHT / 2); MyPanel.explosionList.add(explosion); MyPanel.bossBlood -= 50; if (MyPanel.bossBlood <= 0) { // boss死,过关 // 过关的标志变量 MyPanel.boss = null; // 过关的标志变量 MyPanel.isPause = true; MyPanel.myplane = new MyPlane(false); MyPanel.isPass = true; MyPanel.isBoss = false; } } // 清空敌机炮弹 for (int i = 0; i < MyPanel.ballList.size(); i++) { MyPanel.ballList.remove(i); } // 音效 AudioUtil.play(AudioUtil.AUDIO_DAZHAO); } } break; default: break; } } // 暂停 if (event.getKeyCode() == KeyEvent.VK_Z) { if (MyPanel.isPause) MyPanel.isPause = false; else MyPanel.isPause = true; } // 取消键N else if (event.getKeyCode() == KeyEvent.VK_N) { if (MyPanel.isStop != 0) { JDialog dialog = new JDialog(frame); dialog.setTitle("关于"); dialog.setSize(400, 200); dialog.setLocation(450, 200); JButton button = new JButton(); button.setText("确定"); button.setSize(100, 50); button.setLocation(200, 120); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { System.exit(0); } }); dialog.add(button); dialog.setVisible(true); } } // 确认键Y if (MyPanel.isStop != 0 && event.getKeyCode() == KeyEvent.VK_Y) { MyPanel.isStop = 0; MyPanel.Restart(); } // 按空格进入游戏 if (!MyPanel.isStarted && event.getKeyCode() == KeyEvent.VK_SPACE) { MyPanel.isStarted = true; MyPanel.scene.setBeginY(0); } } });
    4.12.2 鼠标移动监听frame.addMouseMotionListener(new MouseMotionListener() { @Override public void mouseMoved(MouseEvent arg0) { if (MyPanel.myplane != null && !MyPanel.isPause) MyPanel.myplane.setPoint(arg0.getPoint()); } @Override public void mouseDragged(MouseEvent arg0) { } });
    4.12.3 鼠标左键发射子弹和开始界面进入游戏frame.addMouseListener(new MouseListener() { @Override public void mouseReleased(MouseEvent arg0) { } @Override public void mousePressed(MouseEvent event) { if (MyPanel.myplane != null && !MyPanel.isPause) { if (event.getButton() == MouseEvent.BUTTON1) { // 左键 if (MyPanel.myplane != null && !MyPanel.isPause) { Bomb bomb1 = new Bomb( MyPanel.myplane.getPoint().x + 10, MyPanel.myplane.getPoint().y, 1, MyPanel.isUpdate); MyPanel.bombList.add(bomb1); Bomb bomb2 = new Bomb(MyPanel.myplane.getPoint().x + MyPlane.PLANE_WIDTH - 40, MyPanel.myplane .getPoint().y, 1, MyPanel.isUpdate); MyPanel.bombList.add(bomb2); // 音效 AudioUtil.play(AudioUtil.AUDIO_BOMB); } if (!MyPanel.isStarted) { MyPanel.isStarted = true; MyPanel.scene.setBeginY(0); } } } } @Override public void mouseExited(MouseEvent arg0) { } @Override public void mouseEntered(MouseEvent arg0) { } @Override public void mouseClicked(MouseEvent arg0) { } });
    4.13 生命周期4.13.1游戏重新开始public static void Restart() { // TODO: 在此处添加游戏重新开始初始化参数 // 战机重新加载 MyPanel.myplane = new MyPlane(false); scene.setBeginY(0); // 清空敌机链表 if (MyPanel.enemyList.size() > 0) MyPanel.enemyList.removeAll(MyPanel.enemyList); // 清空战机链表 if (MyPanel.meList.size() > 0) MyPanel.meList.removeAll(MyPanel.meList); // 清空战机子弹链表 if (MyPanel.bombList.size() > 0) MyPanel.bombList.removeAll(MyPanel.bombList); // 清空敌机炸弹链表 if (MyPanel.ballList.size() > 0) MyPanel.ballList.removeAll(MyPanel.ballList); // 清空爆炸链表 if (MyPanel.explosionList.size() > 0) MyPanel.explosionList.removeAll(MyPanel.explosionList); // 清空血包列表 if (MyPanel.bloodList.size() > 0) MyPanel.bloodList.removeAll(MyPanel.bloodList); // 参数重新初始化 MyPanel.myLife = DEFAULT_LIFE; MyPanel.lifeNum = DEFAULT_LIFE_COUNT; MyPanel.myScore = 0; MyPanel.passScore = 0; MyPanel.passNum = DEFAULT_PASS; MyPanel.isPass = false; MyPanel.isPause = false; MyPanel.lifeCount = 1; MyPanel.magicCount = 0; MyPanel.bloodExist = false; MyPanel.bossBlood = Boss.BOSS_LIFE; MyPanel.isBoss = false; MyPanel.bossLoaded = true; MyPanel.isProtect = false; MyPanel.isUpdate = false; MyPanel.test = false; MyPanel.boss = null; // isStarted = FALSE; initTimer(); }
    4.13.2 生命值归零,游戏结束public static void gameOver() { // 结束游戏界面 // 释放计时器 killTimer(); // 计算最后得分 myScore += passScore; // 播放游戏结束音乐 // 清屏 // 音效 AudioUtil.play(AudioUtil.AUDIO_GAMEOVER); isStop = FLAG_STOP; // TODO System.out.println("-----------gameOver"); }
    5 课程设计中遇到的主要问题及解决方法5.1 滚动背景实现问题要实现滚动背景,需要在背景图上取出客户区矩形大小的区域,并按照Timer进行递进,并且要在背景图边界处衔接好返回背景图开头位置,实现一张背景图的循环反复,达到滚动背景图的目的,刚开始由于不懂得如何实现开头结尾处的衔接问题,导致滚动背景图实现难度大,后来经由网上查阅的资料得知要把背景图加载两份,第二份背景图开头衔接在第一份结尾处,让客户区矩形在该两份背景图上进行滑动,当滑动到第二份背景图时,再把第一份接到第二份结尾处,从而实现循环反复滚动背景。其中实现的难点在于控制好客户区矩形坐标和背景图上要显示的图片块位于图片上的坐标的对应关系。
    5.2 背景音乐循环播放和游戏音效同时输出问题由于平时接触Swing太少,对Swing操作多媒体文件颇为陌生,对于Java的音频播放也是颇为陌生,通过网上查找了很多相关资料,才把AudioClip对音频的播放功能实现,而且刚开始使用,没注意到音频文件播放完的释放问题,导致程序运行一阶段之后出现了内存溢出而崩溃的情况。
    5.3 多帧位图素材的动画特效实现问题飞机大战中的爆炸显示是通过一张带有八个帧的位图进行连续输出实现爆炸特效,刚开始只是简单的在一个while循环中循环八次,结果不尽如人意,后来联想到帧和时间的对应关系,在每一次TimerTask调用时输出一帧,并在爆炸对象中用progress标记位记录播放位置,等到八个帧播放结束时,返回播放结束标记位,在TimerTask中检测并删除播放完成的该爆炸对象。
    5.4 游戏结束和游戏重新开始的游戏资源维护问题该游戏由于实现了多个额外功能,且都是带有全局意义的,因此放置了较多标记位来标记每个状态,通过这些标记位来控制游戏进程中的各个状态,因此在游戏结束和重新开始游戏时,各个标记位的正确重置将会决定重新开始游戏之后的游戏状态。还由于这些操作可能会在TimerTask类调用过程中进行中断,因此程序运行中途的中断时的游戏参数的维护对程序的正确执行至关重要。
    6 课程设计体会由于对Swing接触不多,因此在开发过程中走了很多弯路,但是也是这次的实训,让我了解了Swing的事件处理机制,了解了如何在屏幕中绘制需要向用户展示的图形信息,学会了使用基本的操作对屏幕中绘制的图形进行控制,也了解了Java的Timer定时器的机制,学习了Sun封装好的Swing类库,学会遇到问题到JDK文档中查询Api,并对Eclipse的使用和编程也进行了提高。
    在开发过程中遇到了大量的异常,通过此次开发,也学会了遇到错误如何慢慢通过IDE的错误信息进行错误查找和代码排错更正,通过添加断点调试程序一步步监视程序运行的具体过程,从而找到程序运行出错的原因。
    本次的课程设计的飞机大战代码是基于前一次的MFC飞机大战进行移植的,因此,在模块的分布和设计方面大多维持原来C++的布局,因此,本次项目开发的源码层次接口没有严格的按照Java的规范,这是这个项目的不足之处,如果时间允许,应该着手对其中的代码布局进行相应的修改,以符合Java的风格,增强代码的可读性。
    7 游戏演示游戏画面1

    游戏画面2
    10 评论 774 下载 2018-11-04 23:00:52 下载需要12点积分
  • 基于Android平台的个人理财软件的设计与实现

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

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

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

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

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

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

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


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

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

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



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




    _id
    Integer
    Y
    用户id


    USERNAME
    VARCHAR(20)
    N
    用户名


    PWD
    VARCHAR(50)
    N
    密码



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



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




    _id
    INTEGER
    N
    用户id


    NO
    INTEGER
    Y
    编号


    MONEY
    DECIMAL
    N
    收入金额


    TIME
    DATE
    N
    收入时间


    TYPE_ID
    INTEGER
    N
    收入类别


    HANDLER
    VARCHAR(100)
    N
    放款方


    MARK
    VARCHAR(200)
    N
    备注


    PHOTO
    VARCHAR(200)
    N
    照片


    KIND
    VARCHAR(10)
    N
    类别



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



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




    _id
    INTEGER
    N
    用户id


    NO
    INTEGER
    Y
    自增


    MONEY
    DECIMAL
    N
    支出金额


    TIME
    DATE
    N
    支出时间


    TYPE_ID
    INTEGER
    N
    支出类别


    ADDRESS
    VARCHAR(100)
    N
    消费地点


    MARK
    VARCHAR(200)
    N
    备注


    PHOTO
    VARCHAR(200)
    N
    照片


    KIND
    VARCHAR(10)
    N
    类别



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



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




    _id
    INTEGER
    N
    N
    用户id


    no
    INTEGER
    Y
    N
    类型编号


    type_id
    INTEGER
    N
    N
    类型id



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



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




    _id
    INTEGER
    N
    N
    用户id


    no
    INTEGER
    Y
    N
    类型编号


    type_id
    INTEGER
    N
    N
    类型id



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

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

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














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

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

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

    摘 要随着Android手机操作系统的普及,基于Android的的软件也越来越受到广大用户的欢迎。本文将为大家介绍基于Android的家庭理财系统,通过该系统,可以随时记录用户的收入及支出等信息。系统主要包括数据库的搭建和维护以及前端开发两个方面,前端主要包括登录注册模块,收入模块,支出模块,收支便签、信息统计等方面。数据库搭建要求数据一致性、完整性和安全性,而前端要求功能完备,易使用等特点。本系统仅应用于个人理财,提供财务数据统一管理,以便做出合理的财务决策。其界面友好,操作简单,除具备基本的财务信息管理外,还提供了信息统计等,为方便操作,设置有部分常用功能快捷工具栏。
    通过设计该系统,可以掌握Android布局文件的设计,掌握SQLite数据库的使用,掌握公共类的设计及使用等,提高自己的软件开发水平,提高工作设计能力。通过与指导老师交流,从中学习知识吸取经验,在技术和软件思想上同时得到锻炼和提高,从而使总体水平升到一个新的高度。
    关键词:家庭理财;数据库;信息管理
    AbstractWith the popularity of Android mobile operating system, Android based software has also become more and more popular among users. This article will introduce the family financial management system based on Android. Through this system, we can record the user’s income and expenditure information at any time. The system mainly includes two aspects of the construction and maintenance of database and front-end development. The front end mainly includes login registration module, revenue module, expenditure module, revenue and expenditure memo, information statistics and so on. Database construction requires data consistency, integrity and security, while front-end requirements, full-featured, easy to use and so on. The system is only applied to personal financial management, providing financial data management in order to make reasonable financial decisions. The interface is friendly and the operation is simple. Besides the basic financial information management, it also provides the information statistics and so on. In order to facilitate the operation, there are some commonly used function shortcut toolbar.
    Through the design of the system, can master the design of Android layout file, master the use of SQLite database, to master the design and use of public class, improve their level of software development, improve the working ability of design. By communicating with the instructor, learning knowledge, learning from experience, and exercising both technical and software thinking at the same time, raises the overall level to a new height.
    Key Words: Family financial management, database, information management
    1.引言理财通是基于Android的家庭理财系统,可以有效地记录和管理家庭的收支,合理规划和支配家庭资金,是家庭理财的好帮手。它利用计算机技术,使家庭理财逐步信息化,从而形成由家庭成员与计算机共同构成服务于家庭的人机信息财务管理系统。后台主要包括数据库的建立,前端主要包括登录模块,收入模块,支出模块,收支便签、信息统计等方面。开发出家庭理财系统,将我们的生活管理的更加方便和高效。
    2.相关技术与环境该app运行在win7操作系统中,使用的JDK版本为Java SE Development KET(JDK) version6,开发工具为Eclipse3.7.1+Android4.0,开发的语言为Java、XML,其中用到的技术有SQLite、Sms、pieChart等。
    3.需求分析3.1 模块功能分析3.1.1 登录模块用户登录系统之前,需要进行的登录,若用户没用注册,则需要进行注册。

    用textview与edittext设置用户名与密码框
    设计三个button,分别用于登录,注册,退出三个事件的响应
    验证用户名与密码,匹配成功进入主界面

    3.1.2 收入模块用户进入主界面,可以对自己的收入进行管理,如新增收入,查看收入。

    设计新增收入布局文件
    设置收入时间
    添加收入信息
    设计收入信息浏览布局文件
    显示所有的收入信息
    单击指定项时打开详细的信息

    3.1.3 支出模块用户进入主界面,可以对自己的收入进行管理,如新增收入,查看收入。

    设计新增收入布局文件
    设置收入时间
    添加收入信息
    设计收入信息浏览布局文件
    显示所有的收入信息
    单击指定项时打开详细的信息

    3.1.4 数据管理用户能够对收支数据进行管理以及对便签进行管理。

    收入管理:用户通过点击收入管理进入到收入管理界面,可以修改自己的收入金额,时间类别以及添加备注等
    支出管理:用户通过点击支出管理进入到支出管理界面,可以修改自己的支出金额,时间类别以及添加备注等
    便签管理:用户通过点击便签管理进入到便签管理界面,可以修改自己的便签,加入自己想添加的话

    3.1.5 信息统计通过饼状图的形式为用户提供清晰的数据,让用户能够清晰的看清自己的钱花在了哪,用在了哪里。
    3.1.6 系统设置当用户忘记密码或需要更换密码时,系统设置可以为用户提供这个功能。
    3.1.7 版本更新由于应用的的功能会逐步完善,因此每一个应用都需要更新,所以如果有新的版本出现,系统将会给于用户是否选择更新的权力,点击确认,将下载最新版本的app。
    3.2 功能结构图分析3.2.1 模块分析图
    3.2.2 用例图
    3.2.3 系统流程图
    3.3 数据库分析建模3.3.1 数据库分析家庭理财通是一款运行在Android系统上的程序,在Android系统中,继承了一种轻量型的数据库,即SQLite,使用该数据库可以像使用SQL Server 数据库或者Orancle数据库一样来存储数据。
    3.3.2 数据表分析 tb_user 用户表



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




    username
    Vachar(20)

    用户名


    password
    Vachar(20)

    用户密码



    tb_outaccount 支出信息表



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




    id
    Integer

    编号


    money
    Decimal

    支出金额


    time
    Vachar(10)

    支出时间


    type
    Vachar(10)

    支出类别


    address
    Vachar(10)

    支出地点


    mark
    Vachar(10)

    备注



    tb_inaccount 收入信息表



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




    id
    Integer

    编号


    money
    Vachar(10)

    支出金额


    time
    Vachar(10)

    支出时间


    type
    Vachar(10)

    支出类别


    handler
    Vachar(10)

    支出地点


    mark
    Vachar(10)

    备注



    tb_flag 便签信息表



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




    id
    integer

    编号


    flag
    Vachar(20)

    便签内容



    4.实现效果4.1 系统升级模块系统升级需要用两个不同版本的apk,当版本低的时候,会自动下载升级,相关代码如下所示:
    try { HttpClient client = new DefaultHttpClient(); /*连接超时*/ HttpConnectionParams.setConnectionTimeout(client.getParams(), 5000); /*请求超时*/ HttpConnectionParams.setSoTimeout(client.getParams(), 5000); HttpGet httpGet = new HttpGet("http://192.168.12.64:8080/lgl/updateinfo.html"); HttpResponse execute = client.execute(httpGet); if (execute.getStatusLine().getStatusCode() == 200) { // 请求和响应都成功了 HttpEntity entity = execute.getEntity(); String result = EntityUtils.toString(entity, "gbk"); // 创建jsonObject对象 JSONObject jsonObject = new JSONObject(result); versionEntity = new VersionEntity(); String code = jsonObject.getString("code"); versionEntity.versioncode = code; String des = jsonObject.getString("des"); versionEntity.description = des; String apkurl = jsonObject.getString("apkurl"); versionEntity.apkurl = apkurl; if (!mVersion.equals(versionEntity.versioncode)) { // 版本号不一致 handler.sendEmptyMessage(MESSAGE_SHOEW_DIALOG); } else{ handler.sendEmptyMessageDelayed(MESSAGE_ENTERHOME,4000); } } } catch (ClientProtocolException e) { handler.sendEmptyMessage(MESSAGE_NET_EEOR); e.printStackTrace(); } catch (IOException e) { handler.sendEmptyMessage(MESSAGE_IO_EEOR); e.printStackTrace(); } catch (JSONException e) { handler.sendEmptyMessage(MESSAGE_JSON_EEOR); e.printStackTrace(); }
    确认更新的界面,如图所示:

    更新过程中,如图所示:

    安装过程如图所示

    4.2 登录模块为用户设置账号密码,当登录时检测,用户是否存在,错误则提示,正确就进入主界面,关键代码如下:
    Intent intent=new Intent(LoginActivity.this,MainActivity.class);UserDao udo=new UserDao(LoginActivity.this);if(udo.getCount()==0){ Toast.makeText(LoginActivity.this, "没有用户,无法登陆!", Toast.LENGTH_SHORT).show();}else{ if(udo.find().getUsername().equals(nameedit.getText().toString())) { if(udo.find().getPassword().equals(pwdedit.getText().toString())) { startActivity(intent); } else{ Toast.makeText(LoginActivity.this, "请输入正确的密码!", Toast.LENGTH_SHORT).show(); } } else{ Toast.makeText(LoginActivity.this, "请输入正确的用户名!", Toast.LENGTH_SHORT).show();
    登录界面,如图所示:

    4.3 收入模块4.3.1 新增收入相关代码如下
    public void onClick(View arg0) { // TODO Auto-generated method stub String strInMoney = txtInMoney.getText().toString();// 获取金额文本框的值 if (!strInMoney.isEmpty()) {// 判断金额不为空 // 创建InaccountDAO对象 InaccountDAO inaccountDAO = new InaccountDAO( AddInaccountActivity.this); // 创建Tb_inaccount对象 Inaccount tb_inaccount = new Inaccount( inaccountDAO.getMaxId() + 1, Double.parseDouble(strInMoney), txtInTime.getText().toString(), spInType.getSelectedItem().toString(), txtInHandler.getText().toString(), txtInMark.getText().toString()); inaccountDAO.add(tb_inaccount);// 添加收入信息 // 弹出信息提示 Toast.makeText(AddInaccountActivity.this, "添加成功!", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(AddInaccountActivity.this, "请输入收入金额!", Toast.LENGTH_SHORT).show(); } } });
    点击新增收入,弹出新增收入界面,如图所示:

    4.3.2 我的收入点击我的收入,弹出我的收入界面,如图所示:

    4.4 支出模块4.4.1 新增支出相关代码如下:
    protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_add_outaccount); txtMoney = (EditText) findViewById(R.id.txtMoney);// 获取金额文本框 txtTime = (EditText) findViewById(R.id.txtTime);// 获取时间文本框 txtAddress = (EditText) findViewById(R.id.txtAddress);// 获取地点文本框 txtMark = (EditText) findViewById(R.id.txtMark);// 获取备注文本框 spType = (Spinner) findViewById(R.id.spType);// 获取类别下拉列表 btnSaveButton = (Button) findViewById(R.id.btnSave);// 获取保存按钮 btnCancelButton = (Button) findViewById(R.id.btnCancel);// 获取取消按钮 txtTime.setOnClickListener(new OnClickListener() {// 为时间文本框设置单击监听事件 @Override public void onClick(View arg0) { // TODO Auto-generated method stub showDialog(DATE_DIALOG_ID);// 显示日期选择对话框 } }); btnSaveButton.setOnClickListener(new OnClickListener() {// 为保存按钮设置监听事件 @Override public void onClick(View arg0) { // TODO Auto-generated method stub String strMoney = txtMoney.getText().toString();// 获取金额文本框的值 if (!strMoney.isEmpty()) {// 判断金额不为空 // 创建OutaccountDAO对象 OutaccountDAO outaccountDAO = new OutaccountDAO( AddOutaccountActivity.this); // 创建Tb_outaccount对象 Outaccount tb_outaccount = new Outaccount(outaccountDAO.getMaxId() + 1, Double.parseDouble(strMoney), txtTime.getText().toString(), spType.getSelectedItem().toString(), txtAddress.getText().toString(), txtMark.getText().toString()); outaccountDAO.add(tb_outaccount);// 添加支出信息 // 弹出信息提示 TotalMoney(); Toast.makeText(AddOutaccountActivity.this, "数据添加成功!", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(AddOutaccountActivity.this, "请输入支出金额!", Toast.LENGTH_SHORT).show(); } } });
    点击新增支出,弹出新增支出界面,如图所示:

    4.4.2 我的支出点击我的支出,弹出我的支出界面,如图所示:

    4.5 便签模块点击收支便签,弹出新增便签界面,如图所示:

    4.6 数据管理模块点击数据管理,进入数据管理界面,界面如图所示:

    将收入的数据按照类型分别相加,计算出比例,相关代码如下所示:
    List<Inaccount> iin; if(string[1].equals("Year")){ iin=inaccountDAO.findByYear(dada[0].trim()); } else{ String[] str=dada[1].split("0"); iin=inaccountDAO.findByMonth(str[1]); } for(Inaccount in:iin){ String type=in.getType(); if(type.equals("工资")){ a+=in.getMoney(); } else if(type.equals("兼职")){ b+=in.getMoney(); } else if(type.equals("股票")){ c+=in.getMoney(); } else if(type.equals("基金")){ d+=in.getMoney(); } }}float quarterly1 = (float) (a/(a+b+c+d)); float quarterly2 = (float) (b/(a+b+c+d)); float quarterly3 = (float) (c/(a+b+c+d));float quarterly4 = (float) (d/(a+b+c+d));yValues.add(new Entry(quarterly1, 0)); yValues.add(new Entry(quarterly2, 1));yValues.add(new Entry(quarterly3, 2));yValues.add(new Entry(quarterly4, 3)); //y轴的集合 PieDataSet pieDataSet = new PieDataSet(yValues, "信息"); pieDataSet.setSliceSpace(0f); //设置个饼状图之间的距离 ArrayList<Integer> colors = new ArrayList<Integer>(); // 饼图颜色 colors.add(Color.rgb(205, 205, 205)); colors.add(Color.rgb(114, 188, 223)); colors.add(Color.rgb(255, 123, 124)); colors.add(Color.rgb(57, 135, 200)); pieDataSet.setColors(colors); DisplayMetrics metrics = getResources().getDisplayMetrics(); float px = 5 * (metrics.densityDpi / 160f); pieDataSet.setSelectionShift(px); // 选中态多出的长度 PieData pieData = new PieData(xValues, pieDataSet);return pieData;
    当用户点击统计时,弹出的界面为:

    4.7 账号设置模块将文本框中的用户名与密码获取到数据库中,并使用更新方法,更新用户的账号密码,相关代码如下:
    username = (EditText) findViewById(R.id.uname);// 获取密码文本框password=(EditText)findViewById(R.id.password);btnSet = (Button) findViewById(R.id.btnSet);// 获取设置按钮btnsetCancel = (Button) findViewById(R.id.btnsetCancel);// 获取取消按钮btnSet.setOnClickListener(new OnClickListener() {// 为设置按钮添加监听事件 @Override public void onClick(View arg0) { // TODO Auto-generated method stub UserDao userDAO = new UserDao(SyssetActivity.this);// 创建PwdDAO对象 User user = new User(username.getText().toString(),password.getText().toString());// 根据输入的密码创建Tb_pwd对象 if (userDAO.getCount() == 0) {// 判断数据库中是否已经设置了密码 userDAO.add(user);// 添加用户密码 } else { userDAO.update(user);// 修改用户密码 } // 弹出信息提示 Toast.makeText(SyssetActivity.this, "用户信息设置成功!", Toast.LENGTH_LONG).show(); }});
    当用户点击账号设置时,弹出的界面为:

    总结为期一周的安卓实践周即将结束了,这一周,我们小组学习到了很多的知识,从Android知识本身来说,我们通过前三天对家庭理财通的实现,让我们回忆并加强了Android的基本知识,再次熟悉了activity的跳转,提示信息的设置,数据库的建立与使用,集合与适配器的匹配等等,我们每天通过敲代码不断地加强自己的Android知识,熟悉安卓的基本命令,能够一点一点的进步,小组内的学习氛围浓厚,不懂的问题相互讨论都得到了解决,每个人都学到了不少的知识。同时,后两天的功能扩展更是提高了我们学习能力,主动的运用互联网的优势,去学习开源的一些技术,例如piechart,json格式,版本的升级等。与知识本身无关的,这五天小组的成员之间互帮互助,共同进步才是最大的收获。
    1 评论 3 下载 2021-04-02 14:19:27 下载需要11点积分
  • 基于Python的PyGame实现的横板动作小游戏

    游戏玩法玩家操纵主角进行移动和攻击,主角有四种攻击,不同操作惊醒不同的攻击和动画播放,敌人可以对玩家进行攻击
    代码思想利用精灵类绘制主角,自定义People类进行数据逻辑上的更新,Enemy类负责数据变更和绘图。利用列表模仿队列的方式,对玩家的攻击操作惊醒先后判断。UPDATE函数可以让玩家一次最多攻击一定数量的敌人,维持游戏平衡性。玩家类和敌人类中存在temp、clock变量和timetick函数,用来更新绘图上的数据。每个类都有JudgeList,避免敌人和玩家多次造成伤害,敌人的速度方向始终朝着玩家,利用三角函数实现。敌人用一个列表存储。
    11 评论 103 下载 2018-10-31 12:43:57 下载需要10点积分
  • 基于JSP和MySQL的网上订餐管理系统的设计与实现

    摘 要随着科学技术与经济的快速发展,网络信息技术也有了显著的提升与进步,当今的社会是一个集数字化,网络化,信息化的,并且是以网络为核心的现代化社会。伴随信息互联网的高速成长,使得互联网应用也走进家家户户的日常生活。网上订餐作为一种新的生活方式,更加新颖。
    毕业设计中,界面的设计主要使用了在jsp插入HTML语言以及JavaScript对系统的页面进行相关的加工处理完善以使得页面实现的效果尽可能的满足美观的要求。而对于后台的设计主要是使用了javaSE基础编程,及javaEE中的Jsp页面的动态编程,servlet处理交互逻辑,并且使用jdbc连接数据库,数据库则选用了更为高效的MySql数据库。
    本论文就此次毕业设计的系统内容,从餐品的管理,餐品的分类以及查询,到订餐车实现,客户对订单的处理,再到系统对订餐和餐品的管理。系统从业务流程的角度上分析,完成了订餐系统的基本功能,可是使得用户通过互联网进行点餐以及交易。相比于以往的订餐方式,网上订餐更加便捷,高效,对于餐厅更加节省人力,有利于管理,对于顾客更加省时省力。
    关键字:网上订餐系统;JSP;系统管理
    AbstractWith the rapid development of science and technology and economy, network information technology has also been improved and significant progress, the society is a set of digital, network, information, and network as the core of the modern society. With the rapid growth of information and Internet, Internet applications have entered daily life in every family. Online ordering as a new way of life, more innovative.
    The graduation design, the interface design of the main use of the HTML language and JavaScript system to insert page processing related to the perfect page to achieve the effect of as much as possible to meet the aesthetic requirements in jsp. For the background of the design is mainly used javaSE based programming, dynamic programming and javaEE Jsp page, servlet interactive logic, and use JDBC to connect to the database, the database is more efficient in MySql database.
    This paper on the graduation design of the system content, from the management of the meal, the classification of food and query, to the order cart, customer order processing, and then to the system of ordering and food management. From the point of view of the business process, the system has completed the basic functions of the ordering system, but it allows users to order and trade via the internet. Compared to the previous way of ordering, online ordering more convenient and efficient, for restaurants to save more manpower, is conducive to management, for customers more time and effort.
    Keywords: online ordering system; JSP; system management
    绪 论随着我国在本世纪科学技术与经济的快速提高,网络信息技术也有了显著的提升与进步。在我们平日生活中有很多生活方式以及习惯随着周围信息化的快速提高也有和以往相比变化越发的翻天覆地。本次毕业设计“基于jsp的网上订餐系统的设计与实现”,相比与之前传统的电话订餐,门店订餐这种靠无线电话,人工传递的方式的信息途径,这些方式解决问题的效率很低,店家(餐厅)不能及时将餐厅对菜品的调整通知顾客,顾客对于用餐需求也不能进行调整,线下的交流并不能形成方便快捷的交互。很多时候对于,餐厅以及顾客都是一种损失。由于上述缺陷,为了适应当前时代信息快速发展,数据大爆炸的趋势,采用先进的发展的网上订餐管理系统使上述的种种缺陷得以得到缓解和解决,精简了餐厅的工作流程,顾客的订餐更加便捷,从而提高了餐厅的营业额。由于网络的的快速发展,所以订餐系统随着网络也要有一定的发展与变化。因此毕业设计“基于jsp的网上订餐系统的设计与实现”是非常有必要的。网上订餐系统对订餐所涉及的两个主要用户:客户与店家(餐厅),在实现网上订餐之前,双方进行订餐工作,双方都需要花费大量不必要的时间精力。而快速发展的网络技术,使得订餐系统中所要使用的管理流程在很大程度上做到了极大的简化了,使得餐厅的管理系统更加快速高效,更加便捷。提高餐厅工作人员的整体工作效率,更加给使用订餐系统的顾客更加便捷的使用享受和良好的用户体验。
    从始至终人们都寻求便捷,舒适的生活方式。网络作为提高生活水平的重要工具,在出现后,人们逐渐对其产生了依赖,在依赖的同时也希望网络能给自己带来更方便的帮助,从而大量的销售网站,娱乐网站等等也就慢慢诞生了,这个网上订餐系统是一种基于B/S架构的一种系统模式,订餐系统网站的上手非常的方便,即使第一次登陆,通过网站的指引也会让您订餐非常顺利便捷,如同多次订餐的老顾客一般。
    此次毕业设计的系统,网上订餐系统的开发与设计,在系统可靠性,以及项目的扩展性上占有极大优势。此外对一些java相关技术进行了部分研究,设计并且实现系统,同时为以后自己的可能还要做的其他项目打下了有效以及坚实的基础。
    1 系统概述与技术介绍网上订餐系统作为一个销售管理系统,首先在页面的结构上一定要相对有自己的独特之处,凸显餐厅的风格,餐品的质量。只有如此才能从一开始吸引顾客的目光,通过美观的页面满足顾客对此类订餐系统的好感依赖以及业务功能的需求。与此同时,系统也要有先对强的实用性,系统的实用性不强,这样的系统会是的顾客产生厌恶心理,不利于一个餐厅的未来发展。当然,作为一个销售管理系统,要对顾客的点餐做数据统计,这样才能方便餐厅对该顾客的用餐习惯分析,更好的对顾客实施更加人性化的服务。
    1.1 课题背景上世纪90年代,互联网逐渐走进人们的视野,在不知不觉中改变着社会的商品交易方式。国内各大企业从认识到互联网的重要性之后,一直坚持不断的探索网上交易的可能。但是由于网络生活具有很大的虚拟性,商家的信誉很难得到保证、有关网上交易的法规未出台、物流的滞后以及其他的一些问题,都成为了网络交易发展的绊脚石。但是,社会的进步是具有持续性的,21世纪以来,各个瓶颈问题包括网上支付、物流速递等一一被攻破,网上交易的黄金时代也随之来临。
    调查显示,由于科技的进步极大地繁荣了物质生产生活,人们对于生活的便利性要求进一步提高,网上交易特别是以食品外卖为代表与大众日常生活休戚相关的网络交易量将在未来几年达到高峰。
    网上交易这一方式给人们的消费模式注入了一股新的活力,极大地冲击了原有的传统购物消费习惯。网上订餐这一方式,在90后人群中拥有大量的支持者,促使他们做出选择的,不仅仅是这一形式的便利性,还包括相关网站对于消费者的引导。
    1.2 B/S结构的介绍此次系统的设计过程主要运用到了我们在学校以及公司经常提及使用的B/S结构。所谓的B/S系统就是浏览器、服务器的系统结构。网上订餐系统使用B/S结构,主要就是需要服务端的计算机安装数据库以及服务器。而在客户端,一个浏览器就足够。服务器在服务端运行,浏览器在客户端运行,
    B/S结构作为目前最先进的软件构造技术,在用户使用的浏览器向服务器提交了一些请求之后,服务器端如果接收到了用户在浏览器端发送的请求之后,服务器端对从浏览器接收而来的请求对其进行相应的业务逻辑处理,随后将其完成处理后的结果返回给浏览器所在的客户端。
    B/S结构也是有很多种的,例如:针对OA系统的开发,这些系统主要包括应用程序的研发(基于C/S结构的开发),以及使用传统的C/S结构的跟网络技术结合的混合应用,以及到目前为止是我们常常用到的网站制作的技术。但是不可否认的是,每中结构以及技术都有自己的优点以及缺点:在此前的C/S结构中,更多的它是一种比较传统的使用较为广泛的软件开发的模式,主要是通过客户端以及数据库两层结构完成系统的实现,在两层之间还可以加入其他层次与结构,C/S作为先前传统的软件开发标准以及开发设计结构,在伴随网络和软件的开发技术快速进步,在各种新兴技术不断出现下,被取代或弥补了其结构的很多缺陷,于是也被其衍生出来的新兴结构技术在系统开发的过程中渐渐的将其取代。
    1.3 JSP技术的介绍此次网上订餐系统中页面主要使用到的技术是java的JSP技术,JSP技术之所以被广泛被使用到各种项目中去,主要也是因为其自身拥有的很多功能,这使得在项目中可以实现满足开发人员,程序员对项目所需要的效果。
    作为JSP技术的基础,servlet技术在此前的开发中一直被开发人员所应用。JSP技术的优点:

    进过一次的开发和编写,可以在多种平台运行:因为JSP/Servlet都是基于Java变成语言的,因此具有其编程语言的一个主要优点——平台无关性,这个就是著名的的“一次编写,随处运行(WORA – Write Once, Run Anywhere)”
    系统的多平台支持性:在目前已知的所有平台JSP技术可以进行任意的环境开发,并且在平台中将项目部署在任意的环境里,相比于其他技术,可以根据自己项目的需求任意的环境中拓展。这些优势相比ASP/PHP的局限性是显而易见的
    强大的可伸缩性:JSP可以运行小到从有一个jar文件,大到多台服务器进行集群和负载均衡,多到多台Application进行transaction,还有消息处理,从一台服务器到无数台服务器,Java程序编程语言显示了其巨大的能量
    功能得多态多样以及获得大多数开发工具的技术支持:这个优势和ASP有点类似,java拥有众多的的开发工具并且极为优秀好用。并且都是免费下载使用的,而且可以成功稳定得在多个平台上顺利运行
    JSP标签可扩充性:JSP对Web网页的动态构建主要是利用脚本技术和标签,JSP技术站在开发者的角度考虑允许开发者拓展JSP标签,定制专属的标签库,xml标签拥有很好的兼容性以及强大的功能,网页制作者可以充分利用,减少对脚本语言的大量依赖,利用自己定制的标签,使网页制作程序员降低了制作网页的难度以及复杂度

    1.4 JavaScript语言介绍此次毕业设计,在其中的前台jsp页面中大量的使用了前端脚本语言JavaScript。 JavaScript编程语言以其独有特性,使得他在目前的大多数项目的前台页面设计以及编程都占有重要的一席之地。由于js设计来的产品会主要运行在用户的浏览器客户端,使得只要用户点击页面的一些按钮或者特殊位置,就可以触发特定的事件进行操作,在页面的JavaScript脚本就会将事件传给客户端(浏览器),在客户端被设计好的事件就会执行进行相应的事件处理完成一系列的操作。
    网上订餐管理系统在前台页面的编写设计中就多次使用到了JavaScript这种流行的前端编程语言,众多的优势,使得很多项目的前台都是它和它发展衍生的众多类库编写的,例如我们熟知的:jQuery,easyUI,Extjs等在众多的丰富的前端js类库。
    网上订餐管理系统在很多地方都用到了JavaScript脚本语言,例如:顾客以及管理员登录页面时候的,检出数据是否有效,包括重复为空等场景。
    1.5 MySQL数据库介绍首先要说的是MySQL是一个开源免费的数据库。这也是为什么作为一个小型关系型数据库管理系统,却获得了大量的使用,当然这并不是它的最大优点。MySQL数据库拥有很多优点,比如说:MySql的适应性很好,运行和反应速度快,使用的可靠性高。与此同时,结构化查询语句是MySql数据库系统主要使用的数据库管理方式,结构化查询语言也是目前在数据库管理语言中最受人们欢迎的,也是最为常用的。因此在很多的项目系统开发中都会被开发人员关注,经常使用。由于数据库开发者的允许,MySql作为开源的软件,在官方网站和很多其他网站都可下载到各种版本的MySql数据库。并且根据个人或者开发团队的项目需要对下载的代码做出合理的修改。由此我们可以看出,作为一个小型的关系型数据库,在一些项目的管理上是一个很好的选择,当然这也是在此项目没有超出MySql的数据处理能力的范围之外。
    1.6 MyEclipse介绍此次毕业设计主要是面向顾客用户在前台页面订餐,后台管理人员对前台页面出来的数据请求进行数据处理,并对客户端页面进行及时调整,完善的管理系统。
    MyEclipse开发软件对系统项目的开发更加方便快捷,开发项目的管理更具有条理性。相比于被广泛使用的Eclipse开发软件,MyEclipse在Eclipse开发软件的基础上进行了详细的优化和功能的完善,并且对Eclipse的开发环境进行了优化,最大的限度进行了拓展。使用Eclipse中很多时候我们自己要寻找安装插件,但是MyEclipse基本会帮开发人员在使用之前就集成好了,这对于开发人员的开发是十分便利的,因此目前正在广泛使用于企业级集成开发中去。而且,MyEclipse开发软件在应用程序的整合方面也显得十分成功,如果开发人员能十分熟悉Eclipse,那么对MyEclipse的使用也一定不会陌生,因为MyEclipse的快捷键与Eclipse基本保持一致。
    1.7 MVC模式介绍此次毕业设计为了能够在设计实现上达到最初的想法及要求。并且使完成的系统可以具有一个优秀系统所拥有的一些优点:安全性高,可移植性好,跨品台性高,拓展性优秀,还能具有分布式结构。因此在项目中用到了前文所介绍的B/S结构体系,我所设计实现的订餐系统正式基于Java编程语言的B/S设计模式的。为了更加完善系统,结合在大学时期学过的计算机软件知识,我还用到了另一个结构模式,MVC三层结构,MVC三层结构主要是指基于模型model,视图view以及控制controller的结构模型,而MVC正是这三者英文的首字母缩写。
    控制器,视图,模型三层软件设计模式是MVC软件设计模式的根本。这三层模式对应到了实际项目系统中的web服务器。目前,在网上订餐管理系统中主要使用的MVC模式,如下图。

    由以上的结构图可以得知,在此次的项目系统中,前台界面页面的主要任务就是使用系统的用户可以正常的使用系统逻辑正确完成所要实现的业务。当我们使用网上订餐管理系统的时候,仅仅需要在用户的本地计算机,移动通讯设备安装一个浏览器,在这里使用的浏览器不会限制其版本,开发团队以及类型。用户就可以通过这个浏览器作为客户端与我们的网上订餐管理系统建立网络连接,用户在使用的浏览器中发送需求信息,交由在系统当中的业务逻辑进行准确的数据处理,这些过程主要是由MVC结构中的对应层次进行处理的。业务逻辑得到准确的执行处理后,将处理好的数据进行数据库的数据交互。在模型层中,数据访问层必不可少,他主要实现了当前的系统(网上订餐管理系统)对数据的增、删、改、查等数据处理操作。
    由上述的阐述,我们可以得出MVC设计模式具有众多的优势,其中主要是有:

    低耦合性,高内聚性
    有利于开发者使用,极高的重用性
    可以简单方便的使用,快速的部署
    具有比较好的可维护性,易于未来的维护
    具有生命周期本身成本较低的优势
    软件过程化的管理方法更有利于开发的进行

    2 系统需求分析2.1 开发环境此次毕业设计“基于JSP的网上订餐管理系统”的开发计算机环境主要是:

    学校配发的戴尔品牌笔记本电脑,型号是惠普242G1
    处理器为英特尔酷睿i5 3代系列
    内存容量为 4GB
    显示屏 14英寸
    显卡芯片品牌NVIDIA
    型号NVIDIA GeForce GT 730M+Intel GMA HD 4000
    硬盘品牌希捷,容量500GB
    使用的系统是Windows7旗舰版 SP1

    在我开发完成的订餐项目系统中,主要使用的数据库是当前非常受欢迎的开源免费的数据库MySQL。进行项目系统开发,发布管理的软件也是MyEclipse工具。使用到的技术,主要是JSP技术,以及前端流行的脚本语言JavaScript,还有JAVA编程语言,HTML标签 等。此次完成的系统项目,订餐管理系统是不需要使用户安装客户端程序就可以使用,用户只需要使用浏览器就可以正常的,完整的使用订餐管理系统的全部所有功能。
    2.2 需求分析参照软件开发的标准规范,参考很多成功的软件开发案例,软件开发很重要的一环就是软件需求分析。软件需求分析当中很重要的一点就是通过这次分析,真正准确了解到用户的真实需求,完整准确地项目设计以及编码开发,这样才能在项目完成之后,交给用户一个满意的系统。不仅如此,同时还能在软件开发之初,对软件项目进行风险评估。最后完成一次完整的软件开发设计。
    软件项目的需求分析这个在软件开发过程中至关重要的一环,无论在什么时候都占有着不可忽视的地位,不能被省略或者敷衍而过。这正是因为它在整个的项目开发当中有着举足轻重的作用。因为软件项目的需求分析,才能在项目的设计开发之前,充分理解用户需要,准确的明确开发的方向。在开发的过程中知道。什么功能是必须实现的,什么工作是必须完成的。对系统进行全面的定义,准确,具体的需求,才能更好的实现在项目开发当中具有极其重要的功能。
    一个完善优秀的项目系统在开发之初,开发的初期就要准备充足的资料,进行项目的需求分析。只有在这个阶段完成一个明细,详尽的需求分析,我们才能在后期的设计,编码中更好的实现系统功能,完成一个用户满意的系统。
    2.3 可行性研究分析在开发的前期,对一个项目系统进行可行性分析,这是软件开发过程中不可或缺的工作。从多个角度,维度进行可行性分析,可以将项目分析的更加透彻具体,从项目的方方面面来深入理解项目系统。
    2.3.1 技术可行性分析在目前大多数的公司使用到的软件开发工具是MyEclipse ,例如我目前实习的公司就是用的是MyEclipse2016,因此我在我此次的项目系统,网上订餐管理系统就是用到了MyEclipse 软件开发工具。ava作为一当下最受欢迎关注的编程语言,历久弥新,而且方便灵活使用,是此次开发系统的不二之选。
    2.2.2 经济的可行性分析此次毕业设计,在整个项目系统设计编码完成之后,用户不需要在自己所在的客户端安装任何的客户端应用程序,只需要正常上网就可以完成对系统的访问和使用,除此之外,只要保证项目系统被正确的部署在服务器上,并且已经正常运行。那么,用户就可在任何连接互联网的浏览器上对系统进行访问。
    2.2.3 法律可行性分析此次毕业设计“基于JSP的网上订餐管理系统的设计与实现”,不仅可以提高餐厅对订餐的管理效率,为顾客对订餐需求上提供便利,提供更加贴心优质的服务,良好的用户使用体验。而且在整个的开发过程中都符合具体的软件开发流程及规范的。在项目的的开发过程中使用到的都是一些开源的免费的数据库以及开发人员主要使用的开发工具,参考以及使用到的一些代码,也都是在开源社区和论坛分享的主要开源代码。因此,此次的网上订餐管理系统是不存在任何问题,在法律上是可行的,满足法律可行性的所有要求。
    为了实现这些要求,我对界面页面的开发做到最大化的简单易懂,将功能实现尽可能的减少不必要的步骤,做到让顾客在最短的事件内可以快速订餐,突出餐厅餐品的特色。做到只要会使用电脑查看网页,就能成功点餐。因此,在操作可行性上也十分可靠。
    3 系统概要设计3.1 系统设计概述系统的设计阶段作为在项目开的发整个过程中,最为复杂的一环,其实也是在项目开发的整个过程中极为重要的的一环,必须经过,必不可缺少。在进行系统项目的设计之前,说明系统项目已经完成了对其自身的分析与设计阶段,这是一个具有发展性的过程,将从分析阶段得出的有些抽象的用户需求进行具体的实现,编码。在对系统项目的设计过程中需要考虑众多因素,例如在系统实现过程中所面对的内在环境和外在因素,对项目进行良好的业务和功能完善以及应该具有的敬业态度。
    作为主要的任务和目标,在系统设计阶段就是为了满足在系统分析阶段提出的用户要求.因此,要从用户的要求出发在不仅可以满足系统分析阶段得出的对系统项目逻辑功能的同时,还要充分考虑到多方因素,包括技术,成本以及时间环境等相关方面。对于不论是在正在进行中的整体的设计结构还是局部的每一个部分,都有相关的技术和针对性的应对方案。在合理的要求和基础上,对项目系统所必要的需求进行满足,并且提出该系统具体到实处的实行方案,使其可以得到完整的实现,完成最初的目标以及要求。
    系统设计的主要方向以及相关要点:

    在系统设计的阶段,主要工作涉及到了绝大多数都是技术以及专业知识
    为了满足用户的需求,在系统项目的设计过程中,可以对之前已经得出的系统项目分析结论作出一些修改,当然,这是基于非原则性上的
    使整个项目系统最终可以获得成功,不仅仅需要完善的分析,严谨的设计,更需要系统项目的设计环境。这个环境不仅仅是技术环境与管理环境,更是两者的有效结合。因此,这是急不可缺的重要一点

    有上述可知系统设计的要点,那么为了更好地设计系统项目就需要了解系统设计的原则:

    易用性原则:对项目做系统设计是为了,开发出的系统项目,可以满足用户提出的需求,达到用户的满意。这种满足需求表现在各个方面,例如:用户的业务需求,用户的逻辑需求,以及使用户处理部分业务时候更加简便,提高用户的用户体验
    开发阶段原则:开发项目系统软件,在系统技术选择阶段主要是站在系统的数据结构的角度进行设计与开发,而实现功能阶段则有不同,这是需要站到用户需求以及开发人员实现的角度考虑的
    业务完整原则:设计实现的项目系统软件,要保证在完成业务的同时保证数据的完整性,减少数据的无处,例如数据库中常常出现的脏读,误读等数据误差
    规范化的业务原则:在最初的设计阶段中,系统主要是完成用户需求要求的业务,但是在实际的操作总。对于系统项目的要求是更高的,它不仅要正确的完成用户的业务要求,也要面对完善的业务流程,以及系统安全性

    3.2 系统功能分析作为网上订餐管理系统主要是为顾客提供便捷,全面的订餐服务,重点要放在面对顾客的页面功能上,其次是管理员对餐品以及餐厅的管理,系统主要模块的设计:

    前台模块

    食品展示模块食品查询模块购物车模块收银台模块用户维护模块订单查询模块公告模块
    后台模块

    食品管理模块用户管理模块管理员维护模块订单管理模块公告管理模块留言管理模块

    如图3-1系统功能层次图所示。
    主要包括这几个方面,其中顾客订餐功能模块是主要重点模块,其次是管理员管理功能模块。当订餐系统管理员使用拥有管理权限的管理员用登录管理员页面的时候。可以看到目前订餐系统中保存的用户信息数据,菜品的数据,员工的数据以及状态,订餐的具体信息,采购原材料的具体情况,以及对顾客页面的管理,例如:留言板,以及营养小贴士等等。通过管理员对餐厅的管理,给予顾客最好的网上订餐服务。
    顾客订餐功能的相对应的模块:主要是顾客进入订餐系统网站之后,可以看到整体的订餐系统网站结构,进行系统的客户端登录,注册,以及对现登陆用户的信息修改,搜索菜品,点餐,订餐,支付,留言的相关功能,通过众多的订餐系统功能。
    管理员系统管理功能相关模块:主要是餐厅管理员从后台管理页面登录管理系统之后,对订餐系统的一些具体业务的调整,可以注册新管理员,以及对现有登录管理员信息的修改,查看分析管理目前注册的顾客的信息,查看修改调整现有菜品的具体情况,新增,删除,完善菜品;对目前餐厅原材料的采购和统计;以及对餐厅员工的管理,新增删除和调整状态;更重要的是可以对目前已知订餐的及时管理,还有就是对顾客订餐页面的一些人性化管理,查看顾客留言,修改给顾客查看的营养小贴士。
    通过这个两个主要的模块,可以完成订餐系统的综合管理,包括了顾客的主要功能,注册,登录,点餐等;以及管理员对餐厅多方面的管理,完成管理员对顾客的服务交互。

    3.3 系统用例图通过管理员用例图,可以清晰的,直观的看到管理员在网上订餐管理系统中对用户订餐以及用户本身的一些主要的操作:

    管理员可以管理餐品信息

    管理员可以添加菜品信息管理员可以删除菜品信息
    管理员可以管理订单信息
    管理员可以管理订餐系统中注册的用户信息

    管理员可以删除用户注册信息管理员可以修改用户信息
    管理员可以对留言进行管理
    管理员可以对用户进行管理

    管理员用例图图,如图3-2所示。

    通过用户(顾客)用例图,可以更加直观的理解在网上订餐管理系统中主要的客户端服务,用户主要使用的功能以及进行的操作,如下:

    游客可以注册会员信息
    游客可以浏览菜品
    游客可以查看公告
    注册会员的用户可以通过注册信息登录
    会员可以在订餐页面浏览菜品信息
    会员可以修改会员信息
    会员可以对正在进行的订餐进行修改
    会员可以对完成的订餐进行查询
    会员可以进行留言

    因此,为了可以更加准确,生动的理解上述所说的用户主要使用的订餐服务功能。绘制用例图就很方便的展示了用户使用主要使用的功能,用户(顾客)用例图,如图3-3所示。

    对于主要功能的业务处理,例如网上订餐管理系统整个业务逻辑处理中最为主要的一部分就是订单处理部分,为了更加直观的理解整个业务逻辑,最对订单的具体的操作如下:

    可以通过用户操作对订单信息,菜品信息进行添加
    可以通过用户操作对订单信息,菜品信息进行处理
    可以通过用户操作对订单信息,菜品信息进行删除

    3.4 系统流程图想对订餐管理系统进行操作,管理员需要登录订餐管理系统的后台管理系统,才能进行一系列的对应管理操作。如果登录时候出现异常,登录失败等状况,系统会要求管理员核对身份密码后重新登录。登录成功之后,就可以通过订餐管理系统的后台模块对整个订餐管理系统进行正确有效的管理。对订单的处理,对菜品的修改,添加,删除。对员工的添加,删除,管理。对原材料的采购登记。订餐管理系统目前的概况,都可以从订餐管理系统的管理员模块进行查看,分析,以及做出及时的相应调整。
    如图,是订餐管理系统的后台管理员模块操作的基本流程图,通过路程图,可以更为直观的看到管理员进行登录操作时的系统业务流程,进行登录,成功进行模块的管理,失败则重新登录等操作,如图3-4管理员流程图所示。

    4 数据库设计与实现4.1 数据库的结构设计一个项目系统软件的设计开发,数据库可以说是必不可少的,没有数据库,用户的数据无法保存,系统的使用人员无法处理数据,那么整个系统可以说是没有意义,以及没有任何使用价值的。在系统项目中,数据库就是用来给用户,管理员等各种使用系统的角色进行数据存储以及取出等交互动作的。不仅如此,根据数据的数据结构的不同,数据库还会对应有不同的存储方式。
    网上订餐管理系统,主要是使用流行的关系数据库MySQL,MySQL是一个开源的免费的关系型数据库,在开发者中极为流行,极为受欢迎。他的众多优点,例如:开源,免费使用,对服务器占用更小,速度更快,是系统更加流畅。这些都使得,系统软件开发的陈本较低,用户体验更佳。
    此次,我们毕业设计“基于JSP的网上订餐管理系统的设计与实现”中主要使用到了MySQL数据库,以下,就对主要的数据表结构进行描述与说明。
    4.1.1 概念结构设计本数据库为满足网上订餐系统的要求,保证使用者信息准确充分的同时,系统的安全性也同样重要。所以,至少要拥有以下功能:

    建立数据表保存使用者注册、登录、验证等功能所需要的数据
    建立两张表,分别保存食品分类信息和食品的详细信息所需要的数据,对食品分类信息分类管理
    使用者确认下单后出现的订单应当记录,包括订单号、地址等详细信息
    订单自动生成滞后,需要记录订单的状态
    除系统管理员外,其他人不能进行系统维护,所以要建立一张表保存管理员的信息
    使用者会对餐品提出意见,那么应该有留言表,留言表内含标题、日期和留言详情

    根据以上信息,网上订餐系统的E-R图如图4-1所示。

    4.1.2 表概要说明网上订餐管理系统主要包括了9张数据表,分别是餐品菜品信息数据表c_book,用户顾客注册数据表表member,管理员数据表admin,餐品菜品类别数据表c_booktype,用户评论数据表comment,订餐车数据表gouwuche,营养小贴士数据表news,订单数据表num,订单详情信息数据表orderbook。其中最为重要的5张数据表,将会在详细描述其表结构。
    4.1.3 数据表的结构餐品菜品信息表c_book
    餐品菜品信息数据表主要是用来保存菜品的基本信息,价格,菜名,描述等,该表的结构如表4-1所示。



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




    id
    int
    4

    ID


    name
    varchar
    50

    菜名


    author
    varchar
    50

    厨师


    price
    varchar
    50

    价钱


    number
    varchar
    50

    编号


    type
    varchar
    50

    类别


    Sum
    varchar
    50

    数量


    Remark
    varchar
    50

    描述


    tate
    varchar
    50

    原材料


    imgpath
    varchar
    150

    菜品图片



    管理员信息数据表admin
    管理员信息数据表,主要是适用于保存管理员的基本信息。表结构如表4-2所示。



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




    id
    int
    4

    ID


    name
    varchar
    50

    用户名


    quanxian
    varchar
    10

    权限


    pwd
    varchar
    50

    密码



    菜品类型数据表 c_booktype
    菜品类型数据表,主要保存了当前订餐系统的菜品类型,类型可以根据餐厅的需求,管理员对其进行增加,删除,以及修改等操作管理,如表4-3所示。



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




    Id
    int
    4

    id


    name
    varchar
    50

    菜品类型名



    用户(顾客)注册信息数据表member
    用户注册信息数据表主要保存了,使用订餐管理系统的用户注册的信息,这张数据表极为重要,表结构如表4-4所示。



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




    Id
    int
    4

    id


    name
    varchar
    50

    用户名


    pwd
    varchar
    50

    密码


    sex
    varchar
    50

    性别


    realname
    varchar
    50

    真实姓名


    age
    varchar
    50

    年龄


    card
    varchar
    50

    身份证号


    address
    varchar
    150

    地址


    phone
    varchar
    50

    电话(手机)


    email
    varchar
    50

    电子邮件


    code
    varchar
    50

    邮政编码


    type
    varchar
    50

    会员



    订单详情信息数据表orderbook
    订餐详细信息数据表,主要记录了顾客订餐之后,完成的订餐,订餐中主要记录了此时用户完成的订单的详细信息。数据表的结构如表4-5所示。



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




    id
    int
    4

    id


    userid
    varchar
    50

    用户id编号


    bookid
    varchar
    50

    菜品id标号


    booksum
    varchar
    50

    菜品数量


    times
    varchar
    50

    订餐时间



    5 系统的实现5.1 前台模块首页作为网上订餐管理系统,展现给用户的客户端前台页面是整个项目中最为重要的一环,而首页也是使用户的留下主要印象,重要的得分点之一。一个好的首页能为系统做良好的推广效应,展现系统的主要功能,使得用户可以通过简单快捷的熟悉上手,掌握使用方法,实现自己当前的需求。前台客户端页面首页,运行效果如图5-1。

    如图5-1,我们可以看到,用户使用的首页展示了系统客户端的主要功能,包括,用户的登录,一些管理员希望用户看到的信息,以及目前餐厅主要的菜品,还有就是重要的模块功能导航栏,通过导航栏的点击,用户可以进入相应的功能模块,实现自己目前的需求,包括:详细的点餐,会员中心,订餐车,留言,找回密码等主要用户使用功能。
    首页的部分JSP页面代码:
    <table border="0" width="99%" cellspacing="0" cellpadding="0"height="100%"><tr><td><% ArrayList alNewly = (ArrayList) request.getAttribute("books"); ArrayList alType = (ArrayList) request.getAttribute("type"); int tem = 0; int x = 0; if (alNewly.size() % 3 == 0) { tem = alNewly.size() / 3; } else { tem = alNewly.size() / 3 + 1; }for (int n = 1; n <= tem; n++) { %><table cellspacing="0" cellpadding="3" width="710" border="0"><tr><% for (int i = n * 3 - 3; i < alNewly.size(); i++) { ArrayList alNewlyRow = (ArrayList) alNewly.get(i); %><td align="middle" width="710"> <table border="0" cellspacing="0" cellpadding="0"><tr><td colspan="2" align="center" bgcolor="#FFE3BB"><font color="#ff9900"><% for (int j = 0; j < alType.size(); j++) { ArrayList alTypeRow = (ArrayList) alType.get(j); if (alTypeRow.get(0).equals(alNewlyRow.get(8))) { %><a href="servlet/SearchServlet?types=<%=aTypeRow.get(0)%>"><%=alTypeRow.get(1)%></a><% break;}}%></font>
    5.2 用户注册登录管理用户在使用订餐管理系统客户端页面进行订餐。首先需要登录系统,登录之后才能享受订餐系统带来的便捷快速的订餐服务,如果当前还没有可以登录的账号,首页提供有注册用户的入口。针对已经登录的用户,若果想对自己当前所使用的用户信息进行一些调整,系统也提供了会员中心管理页面,来满足此类用户的需求。如图5-2为用户登录功能的页面:

    用户进行登录之后就可以进行点餐,搜索等相应功能的操作了。用户登录的程序流程图如图5-3所示。

    在用户进行注册成为系统的正式用户之后,就可以进行功能的使用了,但是如果出现对当前用户信息的变更操作,就可以在用户的会员中心,对当前登录的用户信息进行修改,如图5-4为用户会员中心页面效果图:

    5.3 用户订餐管理功能订餐管理系统,作为用户使用的客户端的主要功能就是给用户带来便利的网上点餐服务,此次设计实现的基于JSP的网上订餐管理系统,点餐服务主要通过两个页面来展示菜品使用户进行点餐操作,一是首页点餐,首页会展示目前主要的菜品,以及销售排行和新上菜品,根据这些推荐信息,可以给用户带来更加贴心的用户体验;二是搜索点餐,可以通过首页上方的工具栏,点击进入搜索点餐页面,通过菜品的名称或者菜品所属的类别进行针对性的搜索,然后进行点餐,如图5-5为首页点餐页面效果图。

    如图5-6为搜索点餐页面效果图。

    搜索点餐页面,通过针对性的搜索可以使用户快速的找到自己需要的菜品,进而完成自己的点餐过程。
    进行菜品搜索的servlet部分主要代码是:
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Validate vd = new Validate(); String name = vd.getUnicode(request.getParameter("name")); String types = vd.getUnicode(request.getParameter("types")); String sql = "select * from s "; String[] args = {"id","name","author","publishing","number","price","storage","brief","type","tate","sums","imgpath","price1","sums1"}; if(name != null && !name.equals("")){ sql += "where name like '%"+name+"%'";//构建对菜品名称查询的SQL语句 } if(types != null && !types.equals("")){ sql += "where type='"+types+"'";//构建对菜品类别查询的SQL语句 } SelectBean sb = new SelectBean(); ArrayList al = sb.select(sql, args); request.setAttribute("search", al); RequestDispatcher rd=request.getRequestDispatcher("/qiantai/search.jsp");//跳转到前台菜品搜索页面 rd.forward(request,response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request,response); }
    当用户找到自己心仪的菜品之后,进行点餐操作,所选菜品点餐成功之后,将跳转到订餐车页面,订餐车页面将展示目前为止,用户所选的菜品,如图5-7为订餐车页面效果图。

    在购物车页面中,用户可以清晰的看到目前所点的菜品信息,也可以根据目前自己的需求对已经点的菜品进行操作,例如修改数量,或者删除目前选中的菜品,甚至清空,如果认为还有所需要的菜品,可以点击继续点餐,进入点餐页面,继续点餐操作。当用户认为菜品足够,可以点击收银台,进行订单的提交。
    进行提交之后,将进入收银台页面,点击在线支付,将提供支付宝链接,这样就可以支持网上在线支付,使得用户可以付款进而完成当前的订单。如图5-9为收银台页面效果图。

    5.4 用户其他功能操作页面网上订餐管理系统,不仅能让用户进行注册,登录,修改信息的操作,更重要的是让用户得到良好的订餐功能体验。因此,用户除了可以操作上述的订餐页面之外,也可由有其他更加人性化的功能,例如在此次毕业设计“基于JSP的网上订餐管理系统”中出现的营养小贴士,以及在线留言功能。
    营养小贴士可以用过管理员在后台进行编写,让点餐的用户看到,提供更加贴心的服务,而在线留言功能可以让用户对当前订餐管理系统中存在的问题,以及改进意见进行表达,达到更好的交互体验。如图5-10为用户在线留言页面效果图。

    5.5 后台管理员登录,注册功能作为一个订餐管理系统,仅仅有用户功能的页面是远远不够的,要提供更加全面的服务,为餐厅管理员提供功能管理页面就显得十分重要。通过系统的后台管理管理页面,对餐厅做出实时的管理,了解用户点餐情况以及目前员工的管理,这样才能更好运作系统,使得用户拥有更好的用户订餐体验。
    如图5-11为后台管理员登录页面效果图,5-11为注册管理员,以及修改管理员页面效果图。

    通过登录,管理员可以进入后台管理页面对订餐系统进行管理,可以对当前登录管理员进行修改以及添加新管理员。

    5.6 后台管理员菜品管理管理员通过添加新菜品,以及修改原有菜品,对订餐系统中的菜品进行调整,通过添加菜品的类别可以对菜品进行更加完善的管理。添加菜品时候,通过添加菜名,原材料,价格,图片,以及添加目前拥有的菜品类别等信息,来给用户展示一道菜的方方面面,这样使用户通过网络放心的进行订餐。如图5-13为添加菜品页面效果图。

    5.7 后台员工及会员管理管理员登录后台管理系统之后,不仅能对现有系统注册的会员进行管理,还可以对当前餐厅的员工进行管理,可以添加,删除,修改员工信息,根据目前员工的工作状态也可以在系统中进行标注。添加了员工搜索功能,这样方便了网上订餐管理系统,后台管理模块对员工的综合管理,如图5-14为员工管理页面效果图。

    5.8 后台材料采购管理作为一个订餐管理系统,要将每一天做菜使用的原材料进行统计管理,这样才是一个完善的订餐管理系统。对于原材料采购的记录管理,这样更能方便餐厅管理员,对整个餐厅进行全面的管理,做到对每一天的食材的把控,部分食材采购量的调整,对餐厅运营的成本的管理把控。如图5-15为原材料采购管理页面效果图。

    通过对原材料的统计把控,以及对一段时间的采购单种类及数量的分析,可以知道什么时候需要采购什么材料,采购的数量的参考量,采购的具体方向。对近期,原材料价格走向进行统计分析,得出对餐厅最为合适的采购方案,避免不必要的采购,或采购的数量不准确,导致的餐厅管理成本的提高,这都是需要注意以及及时避免的。
    5.9 销售订餐及统计管理作为网上订餐管理系统,对于当前订餐订单的管理是不可或缺的,因为只有对近期订单进行分析才能知道,用户喜欢的菜品,进而进行针对化的应对,挺高服务质量,对销量高的菜品背后的工作者—厨师,进行奖励管理,提高工作效率,菜品的质量保证。对近期采购单的统计,有效的看出采购方向,和采购数量,为今后采购提供了有力的数据参考保障,避免采购人员出现不必要的事物,餐厅承担不必要的损失。如图5-16为销售订单管理页面效果图。

    如图可以看到,管理页面提供了多样的搜索条件用来搜索符合条件的订餐,例如:用户id,菜品名称,订购数量,这些搜索条件可以快速定位符合条件的订单,然后反馈给管理员来进行订单的管理。
    除此之外,还有统计管理功能,如图5-17为统计管理功能页面效果图:

    如图,统计管理页面可以清晰,完整的看到当日的订餐销售量,销售信息,以及当日对原材料的采购量信息,这方便了管理员对餐厅的管理,也为用户提供快速,优质的使用体验实现了坚实的保障。
    使管理员对当前餐厅的实时状况有了深刻的认识,哪些菜品销量高,需要提前准备,原材料需要大量采购;哪些菜品销量过低,需要减少原材料的采购,甚至下架。近期的用户更喜欢,哪些类别的菜品,是否与天气季节相关,需要对员工厨师进行具体化的调整,提供了有力的数据佐证,与数据分析的资料。这都对今后餐厅的发展十分重要,也是网上订餐管理系统可以被餐厅使用的重要原因之一。
    6 系统测试在大学四年的专业知识学习中,可以得知。作为完整的规范的软件开发流程,在完成系统设计,编码开发之后,进行软件测试是非常重要的一环。是对完成开发,即将投入使用的系统的一次性能以及质量的综合鉴定。在项目正式上线使用之前,进行的必要工作。也是对项目最初的需求分析,系统的总体设计,编码开发的检验,多方面工作的综合最终审核。
    6.1 测试方法在项目进行完整的整合与交互中,对项目系统进行全面的测试,对项目的整体进行问题的查找,在这个过程中耗时相对而言较多。但是也正是因为系统测试的存在,使得系统有了综合质量的保证。
    为了确保网上订餐系统的稳定运行,本节对主要功能模块进行测试。
    6.2 测试流程写好测试的数据以及案例,并对测试数据进行设计逻辑的处理,得出期望结果。
    6.2.1 前台首页测试
    查询模块的测试:首先点击主页正中间的查询栏,在查询栏里中输入要查找的菜品关键字,如果弹出所查找菜品的信息,则说明该模块可行。对该模块进行大量数据的测试,验证其成功无错误
    菜品分类浏览模块的测试:单击页面左边的菜品分类信息,选定某一种类后,系统会弹出属于该类别的所有菜品信息。对给模块进行大量数据的测试,验证其成功无错误,该模块能够完整地实现功能

    6.2.2 购物车模块测试在本模块中首先对修改数量单元进行了测试,如果填写的数量为可行的有效数字如1、2、3,则系统提示修改成功,如果输入的制服非法,系统默认修改数量为1。通过随机数据的测试,验证其成功无错误。假如使用者订购了同一个商品,系统就会对该商品的购买数量进行累加。
    然后对购物车是否能满足使用者的购买数量进行测试,向购物车中添加大量的食品,查看购物车是否会发生崩溃现象。经过测试,购物车完全满足设计要求,达到了预期目标。
    6.2.3 菜品管理模块测试
    添加功能的测试:在菜品添加的功能下,对于已经选定的菜品名、菜品信息、菜品的图片、价格、特价、菜品种类进行添加。通过下拉的选择列表选择菜品的类别,对于菜品图片的添加,需要选择本地的菜品图片,然后上传,当所有必须的食品信息都已经填写完毕的时候,即可上传添加。完成添加以后,随机使用几组不完整的数据对本功能进行测试,均显示失败
    删除功能的测试:以上述添加的这一条记录为例,点击删除按钮,会跳出是否删除的提示信息,点击确定按钮之后,系统返回删除成功的信息。对于菜品分类信息的删除,则需要在本分类之下已经没有菜品信息,否则将不能删除

    6.2.4 会员注册模块测试在会员注册模块中涉及到了三项数据,项目分别为密码和确认密码、使用者真实姓名、住址、性别、E_mail、QQ。测试这个模块准备的数据有:

    979332、979332、王艺博、太原市万柏林区、男、123456@qq.com、123456
    岑志云、12345、1234岑志云、太原市万柏林区、男、bzhi@123.com、6434634

    点击注册按钮,输入第一组数据,单击注册按钮网站会提示表单信息不能为空,因为表单信息要全部填写,而昵称没有填写。接着测试第二组数据,两次所输入的密码并不一样,系统会弹出提示。
    6.3 系统测试结果当本次毕业设计完成完整的项目开发之后,对本次的“基于JSP的网上订餐管理系统设计与实现”,进行了相应的测试,主要的情况如下:
    在项目开发的过程中,每一个进程的每一步都需要被全面的考虑到,从而确保系统在个个时期具备可控性和稳定性。本章注重对前台首页、购物车模块、使用者注册模块和商品管理模块进行了详细的测试说明。对这些模块进行大量数据的测试,验证其成功无错误。
    参考文献[1] 康牧,JSP动态网站开发实用教程,清华大学出版社,2009.
    [2] 戴维尔,JavaScript程序员教程,电子工业出版社,2010.
    [3] 杨学瑜,高立军,软件开发过程与项目管理,电子工业出版社,2008.
    [4].唐友国 湛洪波著.JSP网站开发详解.电子工业出版社2008年10月.
    [5]卫红春等著.信息系统分析与设计[M].北京.清华大学出版社.2009.
    [6] 张文静 林琪著.JSP程序设计.人民邮电出版社.2005.
    [7] 张孝祥、徐明华, JAVA基础与案例开发详解, 清华大学出版社, 2009.
    [8] 张洁才,Java web应用开发教程,科学出版社,2005.
    [9] 戴维尔,JavaScript程序员教程,电子工业出版社,2010.
    [10] James D. McCaffrey等著.NET软件测试自动化之道[M]. 北京.电子工业出版社.2007.
    [11] 黎连业、王华、李淑春,软件测试与测试技术,清华大学出版社,2009.
    [12] 张大方 李玮等著.软件测试技术与管理[M].湖南.湖南大学出版社,2007.
    [13] (美)Elliotte Rusty Harold著.Java Network Programming.东南大学出版社 .2005.
    [14] (美)H.M.Deitel,P.Jdeitel著.Java程序设计教程(第五版). 清华大学出版社.2004.
    [15] (美)David flanagan著.Java技术手册(第五版).东南大学出版社.2006.
    [16] (美)John J.Patrick著.SQL基础(第二版).清华大学出版社.2004.
    8 评论 173 下载 2020-08-04 12:30:59 下载需要14点积分
  • 基于Java和MySQL实现的即时通讯系统

    一、系统功能说明1.1 系统简介该系统名为《即时通讯系统》,别名为wechat,是基于Java网络编程TCP协议的一个及时通讯桌面程序。
    系统分为两部分,服务端和客户端,在服务端主要功能有控制服务的开关,在线人数的显示,在后台的功 能上,服务端为客户端提供信息转发和在线、离线判断的功能,当发送方的接收方为离线时,服务端会将信息存储在数据库中,在接收方下次登录的时候,会发出离线消息。客户端的主要功能是与某个人进行实时通讯聊天,当接收方不在线时,可发送离线消息,同时客户端还具有群发的功能和设置在线或隐身的状态。
    1.2 系统主要功能即时通讯系统,本系统由2部分组成:

    服务端功能

    查看在线人数列表控制服务的开关客户端消息的转发
    客户端功能

    终端登录需要后台验证,并能够设置在线或隐身状态当通讯双方均在线时,双方可互发消息当对方不在线时,可发离线消息

    1.3 系统开发环境本即时通讯系统采用了Eclipse开发平台,jdk采用1.8版本,数据库采用Mysql数据库以及Mysql Navicat的可视化的数据库管理工具,Tomcat7.0 版本和window8.1操作系统。
    二、系统设计2.1 系统总体设计2.1.1 功能结构图
    2.1.2 服务端设计在服务器端 用一个HashMap< userName,socket> 维护所有用户相关的信息,从而能够保证和所有的用户进行通讯。
    服务器向客户端发的消息格式设计:
    命令关键字@#发送方@#消息内容登录

    msg @#server @# 用户[userName]登录了 (给客户端显示用的)
    cmdAdd@#server @# userName (给客户端维护在线用户列表用的)

    退出

    msg @#server @# 用户[userName]退出了 (给客户端显示用的)
    cmdRed@#server @# userName (给客户端维护在线用户列表用的)

    发送

    msg @#消息发送者( msgs[3] ) @# 消息内容 (msgs[2])
    2.1.3 客户端设计客户端向服务器发的消息格式设计:
    命令关键字@#接收方@#消息内容@#发送方连接

    userName —-握手的线程serverSocket专门接收该消息,其它的由服务器新开的与客户进行通讯的socket来接收
    退出

    exit@#全部@#null@#userName
    发送

    on @# JList.getSelectedValue() @# tfdMsg.getText() @# tfdUserName.getText()
    2.2 网络通信协议该系统为了保证消息的可靠性,全部采用TCP协议,用户登录时通过TCP协议与服务端建立连接,服务端根据客户端建立的连接向客户端发送系统消息或者是转发发送方的消息。
    2.3 数据库设计系统包括服务端后台管理和客户端用户两部分。

    服务端后台管理可以管理服务器的连接,监听所有的用户状态,控制服务器的连接
    客户端用户可以给列表中的其他用户发送消息,包括离线消息和在线消息;还可以设置ip地址和端口号,以及查看帮助的版本信息

    系统数据库是利用Mysql数据库的navicat软件创建的,本系统用到的数据库表主要有:用户表(user)、离线消息表(message),具体如下:
    用户表:用于存储用户的账号信息

    离线消息表:用于存储发送的离线信息

    2.4 数据存储该系统使用MySQL作为数据存储的数据库,主要有两张表,一张用户表用于存储用户信息数据,当用户新注册一个账号时,用户数据表会增加一条记录;,用来存储用户所发送的离线消息。两张表的字段如下:
    2.4.1 用户表初始的数据如下:

    -- 向用户表中插入数据 --insert into user(account,pwd,state) values('王嫣兰','1','离线');insert into user(account,pwd,state) values('赵雯楚','1','离线');insert into user(account,pwd,state) values('李嘉丽','1','离线');insert into user(account,pwd,state) values('廖美欢','1','离线');insert into user(account,pwd,state) values('卢超素','1','离线');insert into user(account,pwd,state) values('张琦','1','离线');insert into user(account,pwd,state) values('何昶源','1','离线');insert into user(account,pwd,state) values('王启宁','1','离线');insert into user(account,pwd,state) values('任孟凯','1','离线');insert into user(account,pwd,state) values('孙明峰','1','离线');
    2.4.2 离线消息表数据如下:(初始时无数据)

    2.5 关键类和包结构设计

    messageDao:用于离线消息表的数据库访问对象,包含了对message对象的各种数据库操作
    userDao:用于用户表的数据库访问对象,包含了对user对象的各种数据库操作
    JDBCutils:封装了数据库连接、关闭、修改和查询等操作的集合
    message:离线消息表对应的实体类
    user:用户表对应的实体类
    login:登录界面与业务逻辑处理类
    register:注册界面与业务逻辑处理类
    ClientForm:客户端平台与业务逻辑处理类
    ServerForm:服务端平台与业务逻辑处理类

    2.6 界面设计2.6.1 登录界面输入账号密码,点击“登录”按钮,进入用户页面;点击“注册”按钮进入,注册界面。

    2.6.2 注册界面输入账号密码,点击“注册”按钮,若账号密码无误,提示注册成功;否则,注册失败。点击“返回”按钮,返回到登录界面。

    2.6.3 用户界面输入正确的账号密码,进入到用户界面,界面右边可以查看好友状态,界面上边可以修改自己的状态,选中右边的好友就可以在界面下边给他发送消息。

    2.6.4 用户互发消息功能

    2.6.5 用户登陆显示离线消息(如无不显示)用户“任孟凯”先给用户“何昶源”发送了离线消息,登陆用户“何昶源”查看是否有离线消息。

    如下图所示,用户“何昶源”已收到离线消息。

    2.6.6 用户设置“隐身”状态用户在任孟凯设置为“隐身”状态

    2.6.7 用户群发消息用户“任孟凯”给所有在线用户发送了消息。

    如下图,两个在线用户“孙明峰”和“张琦”都收到了”任孟凯”发送的消息。

    2.6.8 后台界面后台服务器管理界面,左边页面记录用户状态,右边显示在线的用户列表。
    左上角控制菜单栏,有开启服务器按钮,只有开启之后,用户才能连接上服务器。
    2 评论 8 下载 2021-03-14 10:00:11 下载需要10点积分
  • 基于JSP和MYSQL数据库实现的宾馆酒店信息管理系统

    一、系统开发平台1.1 介绍宾馆管理信息系统是现代商业社会宾馆酒店单位不可缺少的部分,在“互联网+”的影响下,这套网站的使用对于宾馆的决策者和管理者来说都至关重要,所以宾馆管理信息系统应该能够为用户提供充足的信息和快捷的登记结账手段。但一直以来人们使用传统人工的方式管理宾馆入住情况,这种管理方式存在着许多缺点,如效率低、保密性差,另外时间一长,将产生大量的账本数据,这对于查找、更新和维护以及后期的计算报表都带来了不少的困难。
    本系统采用MyEclipse Professional 2014 JDK 1.8 以及Tomcat 8.0作为开发工具,JSP + Servlet + JavaBean为开发语言,采用MySQL 最新版5.7.13建立数据库,先在MySQL中设计并制作各部分需要调用的数据库,并进行初始房间数据的输入,再进行网页界面的设计和事物逻辑的编写,在指导老师的帮助下,已经基本上成功地实现了设计要求。其中数据库设计和程序逻辑设计是系统的核心部分。通过对数据库的概念设计、逻辑设计、物理设计和系统的模块设计,使得系统界面简洁美观,功能明确,方便了宾馆管理人员的操作。
    1.2 开发语言
    JSP+Servlet+JavaBean
    HTML
    CSS
    JavaScript
    Jquery3.1.0
    BootStrap框架

    用JavWeb做网站设计流程清晰、结构合理,有良好的可扩充性和耦合性。
    1.3 开发工具
    MyEclipse Professional 2014
    JDK 1.8
    Tomcat 8.0

    1.4 数据库mysql 5.7.13MySQL数据库简称MySQL,是一款由瑞典MySQL AB公司开发并且广泛应用在于小型企业或者组织中的小型数据管理系统,MySQL数据库体积小、速度快、总体拥有成本低,开发简便快捷,性能还是不错的。
    1.5 操作系统
    Microsoft Windows 10
    二、数据库规划2.1 任务陈述一套功能强大而又使用简便的客房管理软件,适用于宾馆管理、酒店管理、招待所管理。主要功能:团体,散客入住客人的信息登记,换房,正常退房,挂账退房,挂单补款结账,预定管理,贵宾卡管理,押金管理,各种强大的宾馆统计报表,异常处理。
    本宾馆管理信息系统包括VIP信息管理、房间信息管理、客户信息管理、账单信息管理四大模块。管理员拥有完全的权限管理系统,给客户登记住宿信息,修改客户信息、查看当前住宿情况,查看剩余房间及当前入住房间状况,增加房间信息,修改房间信息,办理用户结账、挂账,办理挂账补款,查看历史账单等等一系列功能。
    2.2 任务目标本系统主要可以实现以下任务目标:

    系统可以准确地记录和查询住户信息,包括住户的订单号、姓名、性别、身份证号、所住房间号、联系方式、是否是VIP、入住时间等
    系统可以对宾馆的房间情况进行管理,包括添加修改删除房间信息、添加修改删除某房间容量大小信息等
    系统可以准确地对VIP信息进行管理,包括VIP的账号、姓名、性别、身份证号、联系方式、VIP等级、办理时间等
    系统可以对用户订单结账信息进行管理,包括根据订单信息以及是否是VIP进行账单的计算、对账单的查询
    系统可以对挂账结算的用户账单进行挂账记录,以及对挂账补款进行挂账的删除
    系统可以对结账的账单信息以及挂单补款的账单信息进行记录,添加到历史纪录表中,可以对历史纪录进行查询,不可以注销
    异常处理

    三、系统定义3.1 系统边界本宾馆管理信息系统包括VIP信息管理、房间信息管理、客户信息管理、账单信息管理四大模块。该管理信息系统的其他部分包括管理员的登录(管理员是写死的)。
    数据库系统的内部构成元素与外部其他部分之间的信息关系如下图所示:

    3.2 用户视图
    房间管理

    查询、添加、修改、删除房间信息
    客户管理

    查询、添加、修改、删除客户信息
    VIP管理

    查询、添加、修改、删除VIP信息
    账单管理

    查询缴费信息、添加挂单信息、删除挂单信息、添加历史账单、查询历史账单

    四、需求分析此宾馆管理信息系统的主要功能如下图所示:

    4.1 用户需求说明4.1.1 数据需求其中需求数据为:

    顾客基本信息记录

    订单号、姓名、性别、身份证、房间号、联系方式、是否是VIP、入住时间
    VIP基本信息记录

    账号、姓名、性别、身份证号、联系方式、VIP等级、办理时间
    房间基本信息记录

    房间号、房间大小、房间价格、剩余容量、押金
    账单历史纪录基本信息记录

    订单号、价格、支付时间
    挂单信息基本信息记录

    订单号、价格、挂单时间
    管理员信息记录

    管理员编号、管理员姓名、管理员密码

    4.1.2 事务需求
    数据录入

    录入顾客的基本信息
    订单号、姓名、性别、身份证、房间号、联系方式、是否是VIP、入住时间
    录入房间的信息
    房间号、房间大小、房间价格、剩余容量、押金
    录入VIP的基本信息
    账号、姓名、性别、身份证号、联系方式、VIP等级、办理时间
    录入历史记录账单的信息
    订单号、价格、支付时间
    录入挂单的信息
    订单号、价格、挂单时间

    数据更新/删除

    顾客住房等一系列信息的更新/删除房间大小等信息的更新VIP信息的更新/删除挂单信息的删除
    数据查看

    列出顾客详细信息按照身份证号列出顾客基本信息列出订单详细信息按照订单号列出账单的详细信息列出VIP用户详细信息按照VIP帐号列出VIP成员的详细信列出所有房间的详细信息根据房间号列出房间的基本信息

    4.2 系统需求说明该宾馆管理信息系统需要较强的数据处理功能,在搜索查询账单方面理应具有较快的响应速度,能够处理多方面的数据请求。系统能够有效的处理各种异常,具有较好的健壮性。
    4.2.1 初始数据库
    大约有50个房间,每个房间或者能容纳两人,或者容纳三人,每个房间有自己的押金表名订这间房间需要缴纳的押金费用
    有一张空闲的VIP信息表,历史订单表,待付账单表
    有一个预先设置好的管理员编号、姓名和密码表,用来核对管理员登录时输入信息是否正确

    4.2.2 安全性
    登录管理员必须有账号密码
    必须验证VIP账号才能进行VIP用户的登记

    4.2.3 站界面前端语言,运用BootStrap框架,大大简化了编写CSS的过程。
    五、数据库逻辑设计5.1 ER图:本系统的ER图如下:

    该ER图包括Customer、Room、VIPCard、haspayed、leavewithoutpay五个实体和登记、付款、核对、挂单补齐四个联系。
    5.2 数据字典5.2.1 从数据字典中抽取出来的系统实体描述Customer

    vipCard

    Room

    Leavewithoutpay

    Haspayed

    5.2.2 从数据字典中抽取出来的联系的描述


    实体
    多样性
    联系
    多样性
    实体




    customer
    m
    登记
    n
    Room


    customer
    n
    核对
    1
    vipcard


    customer
    m
    付款
    1
    Haspayed


    customer
    m
    挂单
    1
    Leavewithoutpay


    leavewithoutpay
    1
    补款
    1
    Haspayed



    六、数据库物理设计6.1 索引由于本系统数据不多,查询量相对较小,故没有建立索引。
    6.2 安全机制
    提供了充足的异常处理机制,能够捕获由各种错误引发的异常
    系统登录时要凭用户名和密码进入,输入正确时才可以进入系统。用户需要选择自己的身份(普通用户或者VIP用户),若VIP账号输入正确,系统可以为其计算价格时打折。其他普通用户没有优惠

    七、应用程序设计7.1 功能模块该学生管理信息系统共有四个模块:登记模块、结账模块、VIP信息管理模块、房间信息管理模块,各个模块实现的功能如下:
    7.1.1 登记模块
    四种登记选择:普通个人、VIP个人、普通团体、VIP团体
    按身份证查找并更新客户信息,包括换房功能
    查看当前住户详细信息

    7.1.2 结账模块
    结账退房,从当前住客中删除此订单信息,将此订单加入到历史账单
    挂单退房,从当前住客中删除此订单信息,将此账单加入待付账单
    挂单补款,从待付账单中删除此订单,加入到历史账单
    查看所有待付账单,根据订单号查看待付账单
    查看所有的历史账单

    7.1.3 VIP信息管理模块
    办理VIP,添加信息到vipcard表
    注销VIP,根据VIP账号删除此VIP信息
    VIP信息修改,根据VIP账号修改
    显示所有VIP用户的详细信息

    7.1.4 房间信息管理模块
    添加房间,添加信息到room表
    房间信息修改,根据房间号修改房间的基本信息
    房间信息的详细显示

    7.2 界面设计管理员登记界面

    用户登记界面

    普通用户登记页面

    VIP用户登记界面

    普通团体用户登记界面

    VIP团体登记

    登记完成后提示需要缴纳押金数目

    修改用户信息

    当前住户情况

    结账退房,自动根据订单号计算用户需要缴纳的价格

    挂单退房,根据订单号

    挂单补齐

    历史账单

    办理VIP页面

    根据VIP账号注销VIP账户

    VIP信息显示

    根据VIP账号修改VIP信息

    添加房间

    根据房间号修改房间基本信息

    房间信息显示

    八、总结8.1 系统优点
    本系统功能齐全,在有限时间内将需求的功能完全实现。前后端事务处理逻辑设计合理,能够很好的满足实际操作需求
    界面简捷美观,说明清晰,操作简单,容易上手
    数据库设计内容具体详细,条理清晰,关系明确,能够遵循数据库设计的准则来描述信息关系,可以稳定地为系统提供服务
    信息提示系统细致完善,对于用户可能发生的错误操作,给予错误信息提示。

    8.2 系统不足
    历史账单会逐渐增加,随着时间会让系统运行逐渐减慢速度,需要手动删除历史账单数据
    数据库的设计上仍然不尽如人意,某些信息的处理不够实际化

    8.3 经验与收获
    通过这次数据库课程设计,进一步了解了数据库内部的操作机制,熟练了自己在数据库编程方面的能力
    调试时可以用System.out.println输出变量的值以检验错误发生在什么地方,或者在某个语句下放入System.out.println(“••••••••••••”)来检验程序是否执行到该语句
    写程序的时候不要只注重结果,更要注重过程,一个习惯早期容易形成,而要是到了后期就很难去改正,所以现在起就要有好习惯,要注重每一个小的细节
    一个系统不是一两天就能完成的,在此系统开发期间,每天都要开发一点,中间尽量不要间断,会影响之前的开发思路,尽量一口气开发完
    9 评论 252 下载 2018-11-05 23:27:11 下载需要14点积分
  • 基于C语言和SQL SERVER数据库实现的图书管理系统

    摘 要本文根据《数据库应用系统设计》课程要求而做。选择图书馆管理系统设计与开发是因为觉得图书馆管理系统对我们的帮助很大,并且经常去图书馆,对图书馆的大部分功能及流程还是比较了解,而且现在有些地方可能还不够完善。这次课程设计目标是建立一个比较好的图书馆管理系统,方便学生查询,方便管理员管理,节省时间,提高效率。
    关键词:图书馆数据库,图书馆管理系统,嵌入式SQL
    第1章 概述1.1 项目背景随着科技的发展,尤其是计算机技术的迅猛发展,图书馆管理的问题从以往的人工管理,到现在的电脑化,系统化,是对图书馆管理方法的质的飞跃,这些技术不仅让图书馆管理变得更加方便、快捷、提高效率,对于用户来说也是有极大地帮助,系统化的图书馆让人们可以更方便的去找书,借书,还书等等一系列的功能,从而让图书馆实现了它的最大化价值。同时对于图书馆本身来说,通过系统,它可以提高图书管理的效率,也是图书馆的科学化、正规化管理的最好方法。所以,图书馆管理系统的研究开发,是一个特别值得去做的研究。
    1.2 编写目的根据所学的数据库系统与程序设计的知识,针对图书管理系统,进行系统的需求分析,系统设计,数据库设计,编码,测试等,完成题目要求的功能,从而达到掌握开发一个小型数据库的目的。
    1.3 软件定义本系统采用VC++6.0集成开发系统作为前台开发Windows窗体控制平台,采用SQL Server 2008作为后台数据库的管理程序。
    1.4 开发环境本系统适合运用于图书馆针对管理借阅者借阅图书。
    下面简单介绍一下本系统的运行环境:

    操作系统:Windows 7
    数据库服务器:Microsoft SQL Server2008
    编程工具:VC++6.0
    绘图工具:Microsoft Visio 2010

    第2章 需求分析2.1 问题陈述2.1.1 基本功能图书管理系统,实现读者信息的借阅证号、姓名、年龄、职业等信息的管理;实现对图书信息的图书号、作者名、出版社、单价等信息点的管理,实现借阅信息的借阅证号、书号、时间等信息的管理,实现对逾期读者的管理。
    2.1.2 数据库需要实现的功能创建存储过程查询图书的数量;创建视图查询借阅逾期信息;创建触发器当增加、删除、修改读者信息或者图书信息时自动修改相应表的数据更新;建立数据库相关表之间的参照完整性约束。
    2.1.3 系统需要实现的功能能实现以下主要功能:

    图书基本情况的录入、修改、删除等基本操作
    实现借书功能
    实现还书功能
    实现对所有购进图书的分类查询和分类统计
    能够按书名、作者等分类查询现有图书的数量
    对超期的情况能自动给出提示信息

    2.2 业务处理流程
    2.3 数据流图图书管理系统数据流图

    读者借书还书数据流图

    读者查询图书数据流图

    采购员采购图书数据流图

    管理员维护图书馆数据流图

    2.4 数据字典2.4.1 图书管理员 Admin_id CHAR(7) Admin_password CHAR(8) Admin_name NCHAR(5)
    2.4.2 读者 Reader_id CHAR(7) Reader_password CHAR(8) Reader_name NCHAR(5) Reader_sex NCHAR(1) Reader_age TINYINT Reader_Company NVARCHAR(20) Reader_work NVARCHAR(20)
    2.4.3 图书信息表 Book_id CHAR(6) Book_name NVARCHAR(20) Book_writer NCHAR(5) Book_publisher NVARCHAR(20) Book_price SMALLINT Book_introduction NVARCHAR(100) Book_type NVARCHAR(20)
    2.4.4 借阅信息表 Reader_id CHAR(7) Book_id CHAR(6) Borrow_time DATE
    2.4.5 图书馆采购员 Shopper_id CHAR(7) Shopper_name NCHAR(5) Shopper_sex NCHAR(1) Shopper_age TINYINT
    2.4.6 图书采购信息表 Shopper_id CHAR(7) Book_id CHAR(6) Shop_time DATE,
    2.4.7 图书历史借阅表 Reader_id CHAR(7) Book_id CHAR(6) Borrow_time DATE Return_time DATE
    第3章 概念结构设计3.1 ER模型图3.1.1 实体ER图图书ER图

    图书管理员ER图

    图书采购员ER图

    读者ER图

    3.1.2 实体联系ER图
    3.1.3 完整ER模型图
    第4章 逻辑结构设计4.1 关系模式针对图书管理信息系统的需求,通过对借书流程的分析以及对ER图的分析,设计如下面的关系模式:

    读者信息表,包括的数据项有:(借阅证号、密码、姓名、性别、年龄、所在单位、职业)
    图书信息表,包括的数据项有:(书号、书名、作者、出版社、价格、内容简介、图书类型、库存)
    借书信息表,包括的数据项有:(书号、借书时间、借阅证号)
    还书信息表,包括的数据项有:(书号、还书时间、借阅证号)
    管理员信息表,包括的数据项有:(管理员号、密码、姓名)
    采购员信息表,包括的数据项有:(采购员号、姓名、性别、年龄)
    采购信息表,包括的数据项有:(采购员号、书号、采购时间)

    4.2 规范化基本表经过对初始关系模式的规范化处理,以下关系模式中不存在部分函数依赖和传递函数依赖,已经达到3NF。

    读者信息表,包括的数据项有:(借阅证号、密码、姓名、性别、年龄、所在单位、职业)
    图书信息表,包括的数据项有:(书号、书名、作者、出版社、价格、内容简介、图书类型)
    借阅信息表,包括的数据项有:(书号、借阅证号、借书时间)
    管理员信息表,包括的数据项有:(管理员号、密码、姓名)
    采购员信息表,包括的数据项有:(采购员号、姓名、性别、年龄)
    采购信息表,包括的数据项有:(采购员号、书号、采购时间)
    借阅历史表,包括的数据项有:(书号、借阅证号、借书时间、还书时间)

    模型中画双划线的是主码,并且借阅证号和书号是借阅关系的外码,采购员号和书号是采购关系的外码。书号、借阅证号、借书时间是借阅历史表的外码。
    5.3 数据库关系图
    第6章 物理结构设计6.1 系统功能结构图
    6.2 功能描述
    用户登录

    选择登录系统的用户类型,进行不同的功能使用
    查看图书信息

    查看数据库中各种图书的信息,进行其他操作
    修改个人信息

    对数据库中的个人信息进行更新
    还书

    借阅信息的更新
    借书

    插入借阅信息
    查看借阅记录

    输出个人的借阅情况
    读者管理

    对读者进行增、删、改操作
    图书管理

    对图书进行增、删、改操作
    查看当前逾期信息

    查看数据库中当前用户逾期罚款信息

    第7章 代码设计和界面设计7.1代码设计7.1.1 数据库的连接本系统是通过VC嵌入式SQL来进行数据库的连接的,主要代码如下:
    int CONNECT(){ EXEC SQL CONNECT TO MS-20170511WSQV.Library_342 USER abc.abcd; if(sqlca->sqlcode==0) { printf("Connection to SQL Server established\n"); } else { printf("ERROR: Connection to SQL Server failed\n"); return 1; } }int DISCONNECT(){ EXEC SQL DISCONNECT ALL; if(sqlca->sqlcode==0) { printf("DISConnection to SQL Server established\n"); } else { printf("ERROR: DISConnection to SQL Server failed\n"); return 1; } return 1;}
    7.1.2 主函数main系统程序的主函数设计,通过调用其他函数来实现管理图书系统;
    int main(){ int select_num; int login_num; CONNECT(); start_meun(); while(1) { scanf("%d",&select_num); switch(select_num) { case 1:break; case 0://退出系统 { system("cls"); DISCONNECT(); printf(" --------------------------------------------------\n"); printf(" -= 感谢使用,再见! =- \n"); printf(" --------------------------------------------------\n"); system("pause"); exit(0); } default:printf("输入错误,请重新输入:");continue; } break; } while(1){ login_meun();//登录界面函数 login_num=login ();//登录处理 switch(login_num) { case 1:admin();break; case 2:reader();break; } }}
    7.1.3 头文件包含头文件、全局变量定义以及函数声明:
    #include<stdio.h>#include<stdlib.h>#include<string.h>char LReader_num[20];/**********函数声明**********/int CONNECT();int DISCONNECT();void start_meun(); //初始界面void login_meun(); //登录界面函数int login (void); //用户登录函数void admin_menu(); //管理员功能界面函数void reader_meun(); //读者功能界面函数void admin(); //管理员函数void reader(); //读者函数void admin_reader(); //管理读者函数void admin_book(); //管理图书函数void allborrow(); //查看借阅情况函数void overdue(); //超期罚款函数void adminreader_menu();void adminbook_menu();void addreader(); //增加读者函数void deletereader(); //删除读者函数void updatereader(); //修改读者函数void addbook(); //增加图书函数void deletebook(); //删除图书函数void updatebook(); //修改图书函数void borrowbook(); //借书函数void returnbook(); //还书函数void searchpersonborrow();//查看个人借阅函数void updateme(); //修改个人信息函数void allbook(); //查看所有图书函数void allreader(); //查看所有读者函数
    7.1.4 用户登录函数本系统用户分为两类,一类是读者,一类是管理员,分别拥有不一样的功能权限;区分两者的方法是链接数据库查询登录用户是否在数据库中的读者表中或者管理员表中;代码如下:
    for(;;){ printf("管理员账号:"); scanf("%s",&admin_num); printf("登陆密码:"); scanf("%s",&admin_pass); EXEC SQL SELECT Admin_id, Admin_password INTO :admin_num,:admin_pass FROM Admin WHERE Admin_id=:admin_num AND Admin_password=:admin_pass; if(sqlca->sqlcode!=0) { printf("发生了错误%d\n",SQLCODE); count+=1; printf("帐号密码错误,请重新输入\n"); if(count>=3) { system("cls"); DISCONNECT(); printf(" ---------------------------------------------------\n"); printf(" -= 感谢使用,再见! =- \n"); printf(" ---------------------------------------------------\n"); system("pause"); exit(0); } continue; } printf("登录成功!\n"); break;}
    7.1.5 读着管理管理员对于读者的管理操作,包括增删改,增加读者代码如下:
    void addreader(){ char YN; printf("输入读者的ID:"); scanf("%s",&LReader_id); printf("输入读者的密码:"); scanf("%s",&LReader_password); printf("输入读者的名字:"); scanf("%s",&LReader_name); printf("输入读者的性别:"); scanf("%s",&LReader_sex); printf("输入读者的年龄:"); scanf("%d",&LReader_age); printf("输入读者的所在单位:"); scanf("%s",&LReader_Company); printf("输入读者的职业:"); scanf("%s",&LReader_work); EXEC SQL INSERT INTO Reader VALUES(:LReader_id,:LReader_password,:LReader_name,:LReader_sex,:LReader_age,:LReader_Company,:LReader_work); if(sqlca->sqlcode!=0) /*sqlcode!=0,表示操作不成功*/ { printf("发生了错误%d\n",SQLCODE); return 1; } else { printf("增加成功!"); } printf("是否需要打印(Y/N):"); do{scanf("%s",&YN);} while(YN != 'N' && YN != 'n' && YN != 'Y' && YN != 'y'); if (YN == 'y' || YN == 'Y') { printf("\n%-10s\t%-10s\t%-10s\t%-10s\t%-10s\t%-10s\t%-10s\n", "Reader_id", "Reader_password", "LReader_name", "Reader_sex", "Reader_age", "Reader_Company", "Reader_work"); printf("%-10s\t%-10s\t%-10s\t%-10s\t%-10d\t%-10s\t%-10s\n", LReader_id, LReader_password, LReader_name, LReader_sex, LReader_age, LReader_Company, LReader_work); printf("打印完成!返回主菜单!\n"); return 1; } printf("操作完成!返回主菜单!\n");}
    7.1.6 输出所有读者运用游标,链接数据库将表中所有信息打印出来;代码如下:
    void allborrow(){ int count=0; EXEC SQL DECLARE AX CURSOR FOR SELECT Reader_id,Book_id,Borrow_time FROM Borrow; EXEC SQL OPEN AX; for ( ; ; ) { EXEC SQL FETCH AX INTO :LReader_id,:LBook_id,:LBorrow_time; if (sqlca->sqlcode!=0) { break; } if(count++ == 0) { printf("借阅情况<未还>:"); printf("\n%-10s %-10s %-15s\n", "Reader_id", "Book_id", "Borrow_time"); } printf("%-10s %-10s %-15s\n", LReader_id, LBook_id, LBorrow_time); } EXEC SQL CLOSE AX; count=0; EXEC SQL DECLARE BX CURSOR FOR SELECT Reader_id,Book_id,Borrow_time,Return_time FROM History; EXEC SQL OPEN BX; for ( ; ; ) { EXEC SQL FETCH BX INTO :LReader_id,:LBook_id,:LBorrow_time,:LReturn_time :LNull; if (sqlca->sqlcode!=0) { printf("打印完成!返回主菜单!\n"); break; } if(count++ == 0) { printf("借阅历史<已还>:"); printf("\n%-10s %-10s %-15s %-15s\n", "Reader_id", "Book_id", "Borrow_time", "Return_time"); } printf("%-10s %-10s %-15s %-15s\n", LReader_id, LBook_id, LBorrow_time, LReturn_time); } EXEC SQL CLOSE BX;}
    7.1.7 图书管理管理员对于图书的管理操作,包括增删改,删除图书代码如下:
    void deletebook(){ char YN; printf("请输入你要删除的图书的ID:"); scanf("%s",&LBook_id); EXEC SQL SELECT Book_id, Book_name, Book_writer, Book_publisher, Book_price, Book_introduction, Book_type, Book_num, Book_allnum INTO :LBook_id, :LBook_name, :LBook_writer:LNull,:LBook_publisher:LNull,:LBook_price:LNull,:LBook_introduction:LNull,:LBook_type,:LBook_num,:LBook_allnum FROM Book WHERE Book_id=:LBook_id; if(sqlca->sqlcode!=0) /*sqlcode!=0,表示操作不成功*/ { printf("发生了错误%d\n",SQLCODE); return 1; } printf("\n%-10s\t%-10s\t%-10s\t%-10s\t%-10s\t%-10s\t%-10s\t%-10s\t%-10s\n", "Book_id", "Book_name", "Book_writer", "Book_publisher", "Book_price", "Book_intro..", "Book_type", "Book_num", "Book_allnum"); printf("%-10s\t%-10s\t%-10s\t%-10s\t%-10d\t%-10s\t%-10s\t%-10d\t%-10d\n", LBook_id, LBook_name, LBook_writer, LBook_publisher, LBook_price, LBook_introduction, LBook_type, LBook_num, LBook_allnum); printf("是否删除该图书(Y/N):"); do{scanf("%s",&YN);} while(YN != 'N' && YN != 'n' && YN != 'Y' && YN != 'y'); if (YN == 'y' || YN == 'Y') { EXEC SQL DELETE FROM Book WHERE Book_id=:LBook_id; if(sqlca->sqlcode!=0) /*sqlcode!=0,表示操作不成功*/ { printf("发生了错误%d\n",SQLCODE); return 1; } else { printf("删除成功!返回主菜单!\n"); } }}
    7.1.8 借阅信息函数查看当前借阅的信息,连接数据库,输出已还借阅信息和未还的借阅信息,代码如下:
    void allborrow(){ int count=0; EXEC SQL DECLARE AX CURSOR FOR SELECT Reader_id,Book_id,Borrow_time FROM Borrow; EXEC SQL OPEN AX; for ( ; ; ) { EXEC SQL FETCH AX INTO :LReader_id,:LBook_id,:LBorrow_time; if (sqlca->sqlcode!=0) { break; } if(count++ == 0) { printf("借阅情况<未还>:"); printf("\n%-10s %-10s %-15s\n","Reader_id","Book_id","Borrow_time"); } printf("%-10s %-10s %-15s\n",LReader_id,LBook_id,LBorrow_time); } EXEC SQL CLOSE AX; count=0; EXEC SQL DECLARE BX CURSOR FOR SELECT Reader_id,Book_id,Borrow_time,Return_time FROM History; EXEC SQL OPEN BX; for ( ; ; ) { EXEC SQL FETCH BX INTO :LReader_id,:LBook_id,:LBorrow_time,:LReturn_time :LNull; if (sqlca->sqlcode!=0) { printf("打印完成!返回主菜单!\n"); break; } if(count++ == 0) { printf("借阅历史<已还>:"); printf("\n%-10s %-10s %-15s %-15s\n", "Reader_id", "Book_id", "Borrow_time", "Return_time"); } printf("%-10s %-10s %-15s %-15s\n", LReader_id, LBook_id, LBorrow_time, LReturn_time); } EXEC SQL CLOSE BX;}
    7.1.9 逾期信息函数连接数据库查看视图,输出超期罚款信息,代码如下:
    void overdue(){ int count=0; EXEC SQL DECLARE QX CURSOR FOR SELECT * FROM view_overdue; EXEC SQL OPEN QX; for ( ; ; ) { EXEC SQL FETCH QX INTO :LReader_id,:LReader_name,:LBook_name,:LBorrow_time,:LOver_time,:LFine; if (sqlca->sqlcode!=0) { printf("发生了错误%d\n",SQLCODE); printf("打印完成!返回主菜单!\n"); break; } if(count++ == 0) { printf("\n%-10s\t%-10s\t%-10s\t%-10s\t%-10s\t%-10s\n", "Reader_id", "LReader_name", "Book_id", "Borrow_time", "超期[天]", "罚款[元]"); } printf("%-10s\t%-10s\t%-10s\t%-10s\t%-10d\t%-10f\n", LReader_id, LReader_name, LBook_name, LBorrow_time, LOver_time, LFine); } EXEC SQL CLOSE QX;}
    7.1.10 借书函数读者对于图书的借阅操作,代码如下:
    void borrowbook(){ char YN; allbook(); printf("请输入你要借阅的图书的ID:"); scanf("%s",&LBook_id); EXEC SQL SELECT Book_id,Book_name,Book_writer,Book_publisher,Book_price, Book_introduction,Book_type,Book_num,Book_allnum INTO :LBook_id,:LBook_name,:LBook_writer:LLNull,:LBook_publisher:LLNull, :LBook_price:LLNull,:LBook_introduction:LLNull,:LBook_type,:LBook_num,:LBook_allnum FROM Book WHERE Book_id=:LBook_id; if(sqlca->sqlcode!=0) /*sqlcode!=0,表示操作不成功*/ { printf("发生了错误%d\n",SQLCODE); return 1; } printf("\n%-10s\t%-10s\t%-10s\t%-10s\t%-10s\t%-10s\t%-10s\t%-10s\t%-10s\n","Book_id", "Book_name", "Book_writer", "Book_publisher", "Book_price", "Book_intro...", "Book_type", "Book_num", "Book_allnum"); printf("%-10s\t%-10s\t%-10s\t%-10s\t%-10d\t%-10s\t%-10s\t%-10d\t%-10d\n", LBook_id, LBook_name, LBook_writer, LBook_publisher, LBook_price, LBook_introduction, LBook_type, LBook_num, LBook_allnum); printf("是否借阅该图书(Y/N):"); do{scanf("%s",&YN);} while(YN != 'N' && YN != 'n' && YN != 'Y' && YN != 'y'); if (YN == 'y' || YN == 'Y') { printf("输入借阅时间(2017-XX-XX):"); scanf("%s",&LBorrow_time); EXEC SQL INSERT INTO Borrow(Reader_id,Book_id,Borrow_time) VALUES(:LReader_num,:LBook_id,:LBorrow_time); if(sqlca->sqlcode!=0) /*sqlcode!=0,表示操作不成功*/ { printf("发生了错误%d\n",SQLCODE); return 1; } printf("借阅成功!打印借阅信息!\n"); EXEC SQL SELECT Reader_id,Book_id,Borrow_time INTO :LReader_id,:LBook_id,:LBorrow_time FROM Borrow WHERE Reader_id=:LReader_num AND Book_id=:LBook_id; if(sqlca->sqlcode!=0) /*sqlcode!=0,表示操作不成功*/ { printf("发生了错误%d\n",SQLCODE); return 1; } printf("\n%-10s %-10s %-15s\n","Reader_id","Book_id","Borrow_time"); printf("%-10s %-10s %-15s\n",LReader_id,LBook_id,LBorrow_time); printf("操作完成!返回主菜单!\n"); }}
    7.1.11 还书函数读者对于图书的借阅操作,代码如下:
    void returnbook(){ char YN; printf("请输入你要归还的图书的ID:"); scanf("%s",&LBook_id); EXEC SQL SELECT Reader_id,Book_id,Borrow_time INTO :LReader_id,:LBook_id,:LBorrow_time FROM Borrow WHERE Reader_id=:LReader_num AND Book_id=:LBook_id; if(sqlca->sqlcode!=0) /*sqlcode!=0,表示操作不成功*/ { printf("发生了错误%d\n",SQLCODE); return 1; } printf("\n%-10s %-10s %-15s\n","Reader_id","Book_id","Borrow_time"); printf("%-10s %-10s %-15s\n",LReader_id,LBook_id,LBorrow_time); printf("是否归还该图书(Y/N):"); do{scanf("%s",&YN);} while(YN != 'N' && YN != 'n' && YN != 'Y' && YN != 'y'); if (YN == 'y' || YN == 'Y') { printf("输入归还时间(2017-XX-XX):"); scanf("%s",&LReturn_time); EXEC SQL DELETE FROM Borrow WHERE Reader_id=:LReader_num AND Book_id=:LBook_id; if(sqlca->sqlcode!=0) /*sqlcode!=0,表示操作不成功*/ { printf("发生了错误%d\n",SQLCODE); return 1; } EXEC SQL UPDATE History SET Return_time=:LReturn_time WHERE Reader_id=:LReader_num AND Book_id=:LBook_id AND Borrow_time=:LBorrow_time; if(sqlca->sqlcode!=0) /*sqlcode!=0,表示操作不成功*/ { printf("发生了错误%d\n",SQLCODE); return 1; } printf("归还成功!打印信息!\n"); EXEC SQL SELECT Reader_id,Book_id,Borrow_time ,Return_time INTO :LReader_id,:LBook_id,:LBorrow_time,:LReturn_time:LLNull FROM History WHERE Reader_id=:LReader_num AND Book_id=:LBook_id AND Borrow_time=:LBorrow_time; if(sqlca->sqlcode!=0) /*sqlcode!=0,表示操作不成功*/ { printf("发生了错误%d\n",SQLCODE); return 1; } printf("\n%-10s %-10s %-15s %-15s\n", "Reader_id", "Book_id", "Borrow_time", "Return_time"); printf("%-10s %-10s %-15s %-15s\n", LReader_id, LBook_id, LBorrow_time, LReturn_time); printf("操作完成!返回主菜单!\n"); }}
    7.1.12 个人信息修改函数读者对于自己的个人信息修改操作;代码如下:
    void updateme(){ char YN; printf("你的个人信息:\n"); EXEC SQL SELECT Reader_id,Reader_password, Reader_name,Reader_sex,Reader_age,Reader_Company,Reader_work INTO :LReader_id,:LReader_password,:LReader_name,:LReader_sex:LNull, :LReader_age:LNull,:LReader_Company:LNull,:LReader_work:LNull FROM Reader WHERE Reader_id=:LReader_num; if(sqlca->sqlcode!=0) /*sqlcode!=0,表示操作不成功*/ { printf("发生了错误%d\n",SQLCODE); return 1; } printf("\n%-10s\t%-10s\t%-10s\t%-10s\t%-10s\t%-10s\t%-10s\n", "Reader_id", "Reader_password", "LReader_name", "Reader_sex", "Reader_age", "Reader_Company", "Reader_work"); printf("%-10s\t%-10s\t%-10s\t%-10s\t%-10d\t%-10s\t%-10s\n", LReader_id, LReader_password, LReader_name, LReader_sex, LReader_age, LReader_Company, LReader_work); printf("是否修改(Y/N):"); do{scanf("%s",&YN);} while(YN != 'N' && YN != 'n' && YN != 'Y' && YN != 'y'); if (YN == 'y' || YN == 'Y') { printf("输入新的密码:"); scanf("%s",&LReader_password); printf("输入新的名字:"); scanf("%s",&LReader_name); printf("输入新的性别:"); scanf("%s",&LReader_sex); printf("输入新的年龄:"); scanf("%d",&LReader_age); printf("输入新的所在单位:"); scanf("%s",&LReader_Company); printf("输入新的职业:"); scanf("%s",&LReader_work); EXEC SQL UPDATE Reader SET Reader_password=:LReader_password,Reader_name=:LReader_name, Reader_sex=:LReader_sex,Reader_age=:LReader_age, Reader_Company=:LReader_Company,Reader_work=:LReader_work WHERE Reader_id=:LReader_num; if(sqlca->sqlcode!=0) /*sqlcode!=0,表示操作不成功*/ { printf("发生了错误%d\n",SQLCODE); return 1; } else { printf("修改成功!查看修改结果!\n"); EXEC SQL SELECT Reader_id,Reader_password,Reader_name, Reader_sex,Reader_age,Reader_Company,Reader_work INTO :LReader_id,:LReader_password,:LReader_name,:LReader_sex:LNull, :LReader_age:LNull,:LReader_Company:LNull,:LReader_work:LNull FROM Reader WHERE Reader_id=:LReader_id; printf("\n%-10s\t%-10s\t%-10s\t%-10s\t%-10s\t%-10s\t%-10s\n", "Reader_id", "Reader_password", "LReader_name", "Reader_sex", "Reader_age", "Reader_Company", "Reader_work"); printf("%-10s\t%-10s\t%-10s\t%-10s\t%-10d\t%-10s\t%-10s\n", LReader_id, LReader_password, LReader_name, LReader_sex, LReader_age, LReader_Company, LReader_work); } printf("操作完成!返回主菜单!\n"); return 1; } printf("操作完成!返回主菜单!\n"); }
    7.2界面设计7.2.1 初始界面
    7.2.2 登录界面
    7.2.3 管理员功能界面
    7.2.4 读者功能界面
    7.2.5 图书管理界面
    7.2.6 读者管理界面
    第8章 总结对于此次数据库系统设计实验,主要有两方面的总结;
    一方面是对于实验中各种错误的总结;实验中遇到过很多错误,一些粗心的小错误到对于不熟悉的知识点运用的错误,多次导致实验的进展缓慢,在查阅错误的原因以及反复修改中,错误的积累,对之后的更加流畅也有了一定的帮助,前期的错误较多,后期之间越来越少,从错误中吸取了不少经验,很有帮助;
    另一方面是实验的设计考虑不周全导致的错误的总结;对数据库的设计由开始的初步设计,缺少了一些深入的思考,导致对数据库的表的划分创建不太完善,表项也有不合理之处,到了后面的设计才发现设计的数据库的结构有漏洞,不得已又修改完善数据库的结构,导致大面积修改的情况,增加了工作量;
    所以,在以上错误的认知上总结,今后做事做设计都要考虑清楚,不贪图一时的轻松,而是应该做到完善,尽可能的考虑到所有的可能出现的漏洞,避免重复不必要的工作,也很感谢在实现遇到困难时帮助我的同学,也很感谢老师这学期的教学,让我更加深入的学习了数据库的知识,通过这次的数据库设计,加深了对数据库系统的基本概念、原理和技术的理解和掌握,将理论与实际相结合,应用现有的数据建模工具和数据库管理系统软件,规范、科学地完成一个小型数据库应用系统的设计与实现,并在此基础上提高实际动手能力和创新能力。
    参考文献[1] 王珊、萨师煊,数据库系统概论(第五版),北京:高等教育出版社,2014.9
    [2] 刘振安,C程序设计课程设计(第二版),北京:机械工业出版社,2009.11
    [3] 郭有强,C语言程序设计实验指导与课程设计,北京:清华大学出版社,2009.2
    7 评论 546 下载 2018-11-18 21:57:39 下载需要12点积分
  • 基于JSP实现的在线投票系统

    一、设计方案1.1 投票系统的功能组成投票系统功能有:选择投票和个人操作及设置,投票设置,投票结果分析,投票操作保障。
    1.1.1 选择投票和个人操作及设置
    列出所有正在进行的投票活动的简略信息,供投票人选择:(也应可查看已有结果的投票活动情况)列出信息有:投票活动的名称、发起人、投票人数、投票时间等
    点击选择某投票活动后,列出该次投票活动的详细内容介绍,可选择进入投票
    进行投票操作:要先输入验证信息(如系统自动检测该机IP地址是否合法,检测投票人是否合法,检测验证码是否合法等),如无错误才可以进行投票,按投票规则填写投票提交表,提交完成投票
    设置及记录服务器地址:即让客户端机器能顺利连接到服务器端,并且该客户端IP地址合法等
    个人资料管理:是投票人的个人资料登记、密码设置等,可参考论坛式的注册、登陆、管理模式

    1.1.2 投票设置
    设置候选人资料:

    填写个人信息(包括姓名、性别、年龄、民族、出生年月日、政治面貌、家庭住址、联系方式等……)编码:由系统自动生成,投票前后需一致推荐意见:事迹介绍、或者是个人介绍等
    设置投票时间:设置该次投票的开始及结束时间,投票人只能在投票有效期间投票
    设置投票类型、投票规则:如该次投票是单选、还是多选、(选多少人)、还是评分制(最低分、最高分多少)、是否可投弃权票、是否可投反对票、多选最终选出多少人、评分制最终选出多少人等
    设置合法投票者:设置投票机器的IP、投票人的名单等

    1.1.3 投票结果分析
    排名结果:单选的结果、多选的结果(按得票数排列,胜出人显著显示)、评分制结果(按分数排列,胜出人显著显示)
    投票的统计信息:(需要做到实时变化以及最终结果显示)包括投票的剩余时间、投票的人数情况等

    1.1.4 投票操作保障
    IP验证:验证投票人的机器IP地址是否合法
    时间验证:投票时间的控制,时间到即结束该次投票活动(对局域网,可不用考虑延时问题; 但如果是基于internet的投票,要考虑: 即客户端投票时,还在有效投票时间内,但数据传到服务器端, 已经过了有效投票时间, 这时应该如何计算? 如果要使得系统设计得更合理, 希望能实现按投票当时的时间,而不是按数据到达时间, 又要防止客户端在时间上欺骗, 应该如何设计?)
    投票人验证:投票人是本系统用户,但要验证其是否享有对某次投票活动的投票权利,并且验证其帐号、密码的正确性,不可多次投票
    投票对象验证:所投的人是否存在于候选人列表中,或是否符合本次投票活动规则(因为某些投票活动可另填自己认为可以的候选人),如不符合是否当弃权处理
    验证码验证:防止利用软件连续投票,或自动投票

    1.2 投票系统的界面组成投票系统前台界面

    投票系统后台界面

    1.3 投票系统的算法、数据结构1.3.1 投票系统前端前端主要用到了盒子模型,使用到的数据结构主要是数组,集合来用于存储从数据库查询到的投票列表以及候选人信息。
    1.3.2 投票系统后台后台使用到的算法主要是查找算法,例如从数组或集合中查找到该用户,或者查找到用户点击的投票选项。数据结构使用了Map,ArrayList来存储提示信息和用户session。
    二、开发过程2.1 投票系统的设计2.1.1 数据库设计根据实验要求,抽取出四个数据库表,分别是用户表(user),投票活动表(item),候选人信息表(item_options),投票结果表(result)。其中投票活动表和候选人表是一对多关系,投票活动表和投票结果表也是一对多关系,用户表和投票活动表是一对一关系。四个表的ER图如下所示:

    2.1.2 系统架构设计本投票系统使用MVC架构,使用JSP+HTML+JQuery+CSS作为视图层,Servlet作为控制器,JDBC+MySQL作为数据模型层。架构图如下:

    2.1.3 页面设计使用盒子模型,分为上中下三个盒子,其中数据显示主要在中间区域。设计如下:

    2.2 投票系统的实现2.2.1 实现技术利用Java语言,使用JavaWeb技术体系中的:JSP+Servlet+JDBC+MySQL技术,来开发一个web端的投票系统。
    2.2.2 数据访问层实现(请看db包和dao包下的所有文件。这里只贴DB.java)public class DB { public static final String DB_DRIVER = "com.mysql.jdbc.Driver"; public static final String DB_URL = "jdbc:mysql://127.0.0.1:3306/db_vote?characterEncoding=utf-8"; public static final String USER = "root"; public static final String PWD = "123456"; private Connection con = null; private Statement stmt = null; private PreparedStatement pstmt = null; public DB() { createConnection(); try { stmt = con.createStatement(); } catch (SQLException ex) { System.err.println("Error: " + ex.getMessage()); } } public DB(String preparedSql) { createConnection(); try { pstmt = con.prepareStatement(preparedSql,Statement.RETURN_GENERATED_KEYS); } catch (SQLException ex) { System.err.println("Error: " + ex.getMessage()); } } private void createConnection() { try { Class.forName(DB_DRIVER); con = DriverManager.getConnection(DB_URL, USER, PWD); } catch (ClassNotFoundException ex) { System.err.println("Error: 类不存在!" + ex.getMessage()); } catch (SQLException ex) { System.err.println("Error: 连接数据库失败!" + ex.getMessage()); } } public ResultSet select(String sql) { ResultSet rs = null; try { rs = stmt.executeQuery(sql); } catch (SQLException ex) { System.err.println("Error: " + ex.getMessage()); } return rs; } //preparedStatement的查询方法 public ResultSet select() { ResultSet rs = null; try { rs = pstmt.executeQuery(); } catch (SQLException ex) { System.err.println("Error: " + ex.getMessage()); } return rs; } public int update(String sql) { int result = 0; try { result = stmt.executeUpdate(sql); } catch (SQLException ex) { System.err.println("Error: " + ex.getMessage()); } return result; } //preparedStatement的更新 public int update() { int result = 0; try { result = pstmt.executeUpdate(); } catch (SQLException ex) { System.err.println("Error: " + ex.getMessage()); } return result; } public int getInsertId(){ int autoInckey = -1; ResultSet rs = null; // 获取结果 try { rs = pstmt.getGeneratedKeys(); if (rs.next()){ autoInckey = rs.getInt(1); } } catch (SQLException e) { e.printStackTrace(); } return autoInckey; } //以下方法为使用动态SQL语句方式时,设置prestmt的参数的方法 //其他类型的参数对应的方法,请自行补充 public void setString(int index, String value) { try { pstmt.setString(index, value); } catch (SQLException ex) { System.err.println("Error: " + ex.getMessage()); } } public void setInt(int index, int value) { try { pstmt.setInt(index, value); } catch (SQLException ex) { System.err.println("Error: " + ex.getMessage()); } } public void setLong(int index, long value) { try { pstmt.setLong(index, value); } catch (SQLException ex) { System.err.println("Error: " + ex.getMessage()); } } public void setDouble(int index, double value) { try { pstmt.setDouble(index, value); } catch (SQLException ex) { System.err.println("Error: " + ex.getMessage()); } } public void close() { try { if (stmt != null) { stmt.close(); } if (pstmt != null) { pstmt.close(); } if (con != null) { con.close(); } } catch (SQLException ex) { System.err.println("Error: " + ex.getMessage()); } }}
    2.2.3 业务逻辑层实现(请看service包下的所有(14个)文件。这里只贴首页的控制器代 码IndexServlet.java以及点击投票的代码IndexVoteIdServlet.java)IndexServlet.java
    @WebServlet({"/index","/index/vote/list","/index/vote/search"})public class IndexServlet extends HttpServlet { private static final long serialVersionUID = 7209195686057464382L; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //由于get传递参数要乱码,所以弄成post,这个问题之后在解决 //System.out.println("搜索投票"); request.setCharacterEncoding("UTF-8"); ItemDao itemDao = new ItemDao(); String path = request.getRequestURI(); List itemList; if (path.indexOf("/search") > -1){ String content = request.getParameter("content").trim(); itemList = itemDao.getSearchItemList(content); request.setAttribute("itemList",itemList); request.setAttribute("active",2); request.getRequestDispatcher("/WEB-INF/view/vote.jsp").forward(request,response); } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(); request.setCharacterEncoding("UTF-8"); ItemDao itemDao = new ItemDao(); String path = request.getRequestURI(); List itemList; if (path.indexOf("vote/list") > -1){ session.setAttribute("active",2); itemList = itemDao.getAllItemList(); request.setAttribute("itemList",itemList); request.getRequestDispatcher("/WEB-INF/view/vote.jsp").forward(request,response); }else { session.setAttribute("active",1); itemList = itemDao.getLimitItemList(5);//获取最新五条 request.setAttribute("itemList",itemList); request.getRequestDispatcher("/WEB-INF/view/index.jsp").forward(request,response); } }}
    IndexVoteIdServlet.java
    @WebServlet(name = "IndexVoteIdServlet",urlPatterns = "/index/vote")public class IndexVoteIdServlet extends HttpServlet { private static final long serialVersionUID = -6818273018879095397L; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(); int id = Integer.parseInt(request.getParameter("id")); String[] selectIds = request.getParameterValues("answer"); String ip = this.getRemortIP(request); ResultDao resultDao = new ResultDao(); User user = (User)session.getAttribute("user"); int success = resultDao.add(user.getId(),id,selectIds,ip); if (success > 0){ session.setAttribute("msg","投票成功"); response.sendRedirect(request.getContextPath() + "/index/vote/list"); }else{ session.setAttribute("msg","投票失败,请不要用同一ip投票"); response.sendRedirect(request.getContextPath() + "/index/vote?id=" + id); } } @SuppressWarnings("rawtypes") protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(); session.setAttribute("active",2); int id = Integer.parseInt(request.getParameter("id")); ItemDao itemDao = new ItemDao(); OptionDao optionDao = new OptionDao(); Item item = itemDao.getItemById(id); if (itemDao.getVoteStatus(item.getStartTime(),item.getStopTime()) != 2){ //判断是否可以投票,即投票是否结束,防攻击 response.sendRedirect(request.getContextPath() + "/index/vote/list"); return; } List optionList = optionDao.getOptionList(item.getId(),item.getAllVoteCount()); request.setAttribute("item",item); request.setAttribute("optionList",optionList); String isWaiver="不可以弃权"; String isOppose="不可以投反对票"; System.out.println(item.getIsWaiver()); System.out.println(item.getIsOppose()); if( "1".equals(item.getIsWaiver()) ){ isWaiver="可以弃权"; } if( "1".equals(item.getIsOppose()) ){ isOppose="可以投反对票"; } if( "1".equals(item.getType()) ) { request.setAttribute("type","(单选,"+isWaiver+","+isOppose+")"); }else if( "2".equals(item.getType()) ){ request.setAttribute("type","(多选,可选人数:"+item.getNumber()+","+isWaiver+","+isOppose+")"); }else { request.setAttribute("type","(评分制,可选人数:"+item.getNumber()+","+isWaiver+","+isOppose+")"); } request.getRequestDispatcher("/WEB-INF/view/info.jsp").forward(request,response); } /*获取用户的ip,防止多次投票*/ private String getRemortIP(HttpServletRequest request) { if (request.getHeader("x-forwarded-for") == null) { return request.getRemoteAddr(); } return request.getHeader("x-forwarded-for"); }}
    2.2.4 表现层实现(请查看../WebContent/WEB-INF/view下的所有(14个)文件,这里只贴添 加投票的页面代码:addvote.jsp)<%@ page contentType="text/html;charset=UTF-8" language="java" %><%@include file="head.jsp"%><div class="container-wrap"> <footer> <div class="row copyright" > <div class="col-md-12 text-center"> <big>新增投票</big> </div> </div> </footer></div><!-- END container-wrap --><div class="container-wrap"> <div id="fh5co-contact"> <div class="row"> <div class="col-md-2 col-md-push-1 animate-box"> </div> <div class="col-md-6 col-md-push-1 animate-box"> <form action="${pageContext.request.contextPath}/admin/vote/add" method="post" id="vote_add_form"> <div class="row"> <div class="col-md-12"> <div class="form-group"> <label for="" class="col-sm-3 control-label">主题</label> <div class="col-sm-9"> <input type="text" name="theme" class="form-control" placeholder=""> </div> </div> </div> <div class="col-md-12"> <div class="form-group"> <label for="" class="col-sm-3 control-label">发起人</label> <div class="col-sm-9"> <input type="text" class="form-control" value="${user.realName}" placeholder="" disabled> </div> </div> </div> <div class="col-md-12"> <div class="form-group"> <label for="" class="col-sm-3 control-label">发起时间</label> <div class="col-sm-9"> <input type="text" name="start_time" class="form-control" placeholder="如2018-10-30 15:07:02"> </div> </div> </div> <div class="col-md-12"> <div class="form-group"> <label for="" class="col-sm-3 control-label">结束时间</label> <div class="col-sm-9"> <input type="text" name="stop_time" class="form-control" placeholder="如2018-10-30 15:07:02"> </div> </div> </div> <div class="col-md-12"> <div class="form-group"> <label for="" class="col-sm-3 control-label">投票类型</label> <div class="col-sm-9"> <select name="type" id="" class="form-control"> <option value="1">单选</option> <option value="2">多选</option> <option value="3">评分制</option> </select> </div> </div> </div> <div class="col-md-12"> <div class="form-group"> <label for="" class="col-sm-3 control-label">投票规则</label> <div class="col-sm-9"> <select name="is_waiver" class="form-control"> <option value ="0" selected="selected">不能弃权</option> <option value ="1">可以弃权</option> </select> <select name="is_oppose" class="form-control"> <option value ="0" selected="selected">不能反对</option> <option value ="1">可以反对</option> </select> <input type="text" name="number" class="form-control" placeholder="多选或评分制的可选人数"> </div> </div> </div> <div class="col-md-12"> <div class="form-group"> <label for="" class="col-sm-3 control-label">候选人名单</label> <div class="col-sm-9"> <textarea rows="5" cols="7" name="option_content" class="form-control" placeholder="候选人名之间用按回车键(换行)隔开,如:
陈铭海
李世民"></textarea> </div> </div> </div> <div class="col-md-12"> <div class="form-group"> <label for="" class="col-sm-4 control-label"></label> <input type="button" id="submit1" name="select" value="提 交" class="btn btn-primary btn-modify"> <a href="${pageContext.request.contextPath}/admin"><input type="button" value="返 回 " class="btn btn-primary btn-modify"></a> </div> </div> </div> </form> </div> </div> </div></div><!-- END container-wrap --><%@include file="foot.jsp"%><script type="text/javascript"> $('#submit1').click(function () { var date = /^(\d{4})-(\d{2})-(\d{2})\s+(\d{1,2}):(\d{1,2}):(\d{1,2})$/; var theme = $('input[name=theme]').val(); var start_time = $('input[name=start_time]').val(); var stop_time = $('input[name=stop_time]').val(); var number = $('input[name=number]').val(); var option_content = $('textarea').val(); var type= $('select[name=type]').val(); if(!theme || (theme.length > 255)){ alert('主题不能为空且长度不能超过255个字符'); return; } if(!start_time){ alert('发起时间不能为空'); return; } if(!date.test(start_time)){ alert('发起时间要符合日期格式'); return; } if(!stop_time){ alert('结束时间不能为空'); return; } if(!date.test(stop_time)){ alert('结束时间要符合日期格式'); return; } if(type !="1" && !number){ alert('多选或评分制类型的投票的可选人数不能为空'); return; } if(!option_content){ alert('选项不能为空'); return; } var start_date = new Date(start_time); var stop_date = new Date(stop_time); if (stop_date.getTime() < start_date.getTime()){ alert('结束时间要比开始时间大'); return; } document.getElementById('vote_add_form').submit(); });</script>
    2.3 投票系统的测试2.3.1 黑盒测试(经过测试,发现功能基本完成)安全机制(防止同一ip重复投票,未登录不能投票,非管理员不能进入后台)

    用户登录成功后,查看所有投票活动

    管理员进入后台

    管理员进入后台后,可以添加投票活动

    给新添投票活动投票

    2.3.2 系统测试经过各个系统软件的集合,测试结果显示本软件暂时没有发现bug。
    三、技术讨论3.1 存在问题
    没有实现评分制的投票类型
    解决方案:由于在数据库设计阶段有预留该字段,因此可以一个输入评分框给用户输入来解决
    没有对用户登录次数进行限制
    解决方案:可以在后台对用户每次登录,在session里添加一次,再用ajax和在后台进行测试
    当访问人数多时,可能会卡
    解决方案:可以使用缓存来缓存所有投票活动,或者或者前几天的投票活动,来加快访问速度

    3.2 改进方向
    可以加上安全机制,例如使用 Spring Security框架来加入安全机制
    可以继续开发手机APP或微信小程序这些手机端应用
    可以使用Redis作为缓存加快访问速度
    10 评论 147 下载 2019-04-17 10:43:35 下载需要14点积分
  • 基于C#和Sql Server的餐厅点餐系统

    1 需求分析1.1 信息要求目前大多数酒店由于规模的限制,忽略了点菜系统的重要性。点菜系统专为具有一定规模和经济条件的 大型酒店设计,通过集成从顾客定桌、点菜、上菜到结账等一系列功能,为每个环节明确分工,并通过可视 化的软件支持,有效减小人为差错的概率,代之以高效、便捷、准确的数字化服务系统,使酒店的管理更加 规范化。 因此该系统应当可以对餐桌信息、菜谱信息、职工信息以及顾客信息进行管理,同时点菜系统少不了订 单,因此也应该能对订单包括过去、现在、未来订单进行管理。
    1.2 处理要求系统应当允许对服务员信息、菜单信息、厨师信息、房间信息、餐桌信息的管理:

    查询、增、删、改及预定服务:顾客可以根据自己的需求,预定不同型号的房间或大厅,餐桌和时间就餐
    点菜功能:顾客可以自行点菜,也可以让管理员代为服务,点菜后能生成订单为对其进行后序的做菜上菜结账评价等服务
    厨师做菜服务:厨师会根据订单状态主动工作。厨师和菜分别分组,每组厨师和一组菜一一对应,该组 每位厨师会做该组所有的菜。厨师做菜管理:厨师可以获得自己的待做菜单,并对已做的菜进行标记
    结账、评价服务:审核菜单,协助顾客结账。结账完成后可以对订单进行评价,选出自己喜欢的菜。在查看自己个人信息时可以看到自己常点的菜。评价除了是对菜的评价,也是对做这道菜的厨师进行评价,方便日后拓展对厨师业绩进行考核的功能

    1.3 安全性与完整性要求顾客能对自己的信息进行编辑,不能对其他信息进行任何编辑。对于房间和餐桌信息也只能看到可用的 房间和餐桌。而且无法获取职工的信息包括其工作状态。同时顾客没有任何删除数据的权限,即使是自己的 数据也无法删除。管理员用于几乎对所有实体信息进行编辑,包括增、删、查、改。当然在操作前应当检查操作的数据是 否合法,例如增加顾客时,顾客联系方式应当是数字,且固话应当是 8 位或 12 位,手机应当是 11 位等。 这些理应属于数据库的用户定义完整性约束,但在此交由高级语言实现。 但是对于部分信息只能由系统产生和修改,例如订单号的生成是自动完成的,管理员也无法干预。顾客评价后系统会对相应的菜谱和厨师进行业绩属性的修改,这也是系统自动完成的。与订单相关的信息目前 只能由系统增加和更改其状态(是否上菜或结账),这部分信息为了保证完整新和安全性,是无法删除的。 再例如房间和餐桌的信息,房间中的餐桌数和使用情况也是有系统自动更新完成,为防止用户不当操作,不允许管理员直接对这部分信息进行修改。 数据库设计时应当考虑实体有实体完整性约束,实体之间联系产生的表具有参照完整新约束,对于部分 属性有根据语义定义的用户定义完整性约束。
    2 概念结构设计2.1 六个实体根据需求分析,本系统共有六个实体,即服务员,厨师,顾客,房间,餐桌,菜谱,他们的属性如
    2.3 中的 E-R 图所示2.2 实体之间的联系大部分联系是多对多的联系,如服务员服务顾客等;也有一对一联系,如一个顾客只能坐一张桌子;一 对多的联系主要体现在一个房间可以有多张餐桌。大部分联系是二元联系,而且会产生其他属性,如顾客和餐桌的联系会产生一个订单号。
    2.3 完整 E-R 图
    3 逻辑结构设计3.1 实体对应的关系模式
    Chref ( COid, COname, COsex, COjob, COifwork, CObethumbup )
    Waiter (Wid, Wname, Wsex, Wifwork )
    Desk ( Did, Droomid, Dpersoncount, Difuse )
    Room (Rid, Rname, Rdeskcount, Rpersoncount, Rusedesks )
    Customer ( Ctellphone, Cname, Csex, Ccometimes, Cregularfood1, Cregularfood2, Cregularfood3 )
    Menu ( Fid, Ftype, Fname, Fprice, Fcooktime, Fthumbup )

    3.2 联系对应的关系模式
    厨师工作记录:ChrefMenu (COid, Fid, Ordernum, Ifthumbup )
    订单记录:CustomerDesk ( Ctellphone, Did, Ordernum, Orderdate, Ifcheckout, Ordermoney)
    顾客点菜历史:CustomerMenu (Ctellphone, Fid, Lastordertime, Ordercount, Ifthumbup )
    上菜状态记录:DeskMenu (Did, Fid, Ordernum, Ifservedish )

    4 数据库实施4.1 数据库的创建4.1.1 表的创建其中包含了实体完整性,即主码必须唯一且不为空。默认违约处理是拒绝插入和修改。详细表的建立见 如下截图。


    4.1.2 用户定义完整性(CHECK)对于部分属性,其值有语义要求,因而需要定义 CHECK 约束,如顾客、职工的性别(只能是男或女), 职工的工作状态(只能是空闲或繁忙),上菜状态和订单结账(只能是是或否),部分截图如下:



    4.1.3 参照完整性参照完整性约束主要是外键的定义。在本系统中,除顾客表外其他表的顾客联系方式都是外键,被参照 关系是顾客表。除订单表外的订单号都是外键,被参照关系是订单表,厨师编号、菜编号、房间号同理,它 们对应的被参照关系是厨师表,菜谱表,房间表。下图是 sql server 中自动生成的数据库关系图,比较直观 地看到参照完整性情况。

    4.2 数据库的操作根据需求分析知道具体要对具体表的操作。
    4.2.1 增(插入,insert)顾客预订或点菜后,实际上是只要顾客选择好座位后会生成订单并插入到订单表中,对应的的插入语句 是:为了方便日后维护,参数化 sql 的语句的建立不用 string 类而是用 StringBuilder 类,该类在处理大量字 符串时效率低,但是应对 sql 语句这种不长的字符串不会对效率产生副作用,而且建立过程十分直观,方便日后维护。
    4.2.2 删(delete)删除操作顾客是没有的,只有管理员,而且仅仅限于对顾客信息,职工信息,餐桌信息,房间信息。其 他信息是无法删除的。由于考虑到参照完整性约束,这些删除操作都不是独立的,应当考虑到参照表。
    4.2.3 查(select)查询是最常见的数据库操作,本系统允许用户根据需求查询,例如点击某类菜的种类,就能看到该种类 下的菜谱,再例如点击对应的厨师,应当能显示该厨师的工作记录等。同时点菜时支持对菜名的模糊查询。
    4.2.4 改(update)管理员对数据的修改和删除一样限制在几个表中,顾客其实也可以间接修改数据,例如点赞某道菜的时 候实际上是在修改点赞情况。其他的修改操作均有系统自动完成,例如上菜状态更新,账单状态更新等,还有对顾客常点的菜的更新和厨师被点赞的次数都是系统完成。
    5 数据库运行和维护5.1 数据库与 C#的连接将连接字符串保存在配置文件中,调用时直接调用配置文件中的信息这样做的好处是程序移植时只需要修改配置文件中的连接字符串即可,不需要修改任何程序,同时也方便程序编写时的调用。连接字符串的修改也非常简单,可以有系统自动生成。

    连接字符串使用 C#提供的 SqlConnection 类,包含在命名空间 system.Data.SqlClient 中,调用构造函数时 需要传参,参数是连接字符串。
    5.2 高级语言 C#对数据库的操作引用较为典型的 ADO.NET 结构图,其中为了能尽可能的练习 sql 语句,本次实验没有用到 DataAdapter 下的 4 个 Command 对象,这些方法是系统根据用户直接在 DataAdapter 上的修改操作自动生成对应的 sql 语句去更新数据库,但仅限于当个表的数据。同时也没有使用右边的 DataSet 和 XML,因为 DataSet 是针对 需要连接外服务器数据库而设定的本地高速缓冲区,但实验所用的数据库是本地的,而且操作量不大, DataSet 本身也比较占用资源。

    5.3 防止非法数据插入数据库的提示这部分主要是通过 C#提供的 MessageBox 控件和下拉框完成。非法数据的来源主要是用户的不正当操 作,例如数据长度不正确,格式不正确等。下面展示在新增顾客时,顾客输入联系方式时意外输入非数字字 符的情况,这是通过 MessageBox 完成提示。以及新增餐桌时需要输入餐桌所在的房间,这是通过下拉框实 现。


    5.4 意外退出系统后重起系统的初始化操作由于厨师做菜等功能并非由数据库完成,而是存放在内存中,系统一旦退出数据无法存入到数据库(例如做菜剩余的时间)。如果再次进入系统时不对还没完成的订单进行检索,那么这些订单的没上的菜在系统 中将一直不会被厨师烹饪。因此进入系统后会先检索是否还有没上的菜,有的话会加入到做菜队列,并触发 厨师做菜的事件。核心代码如下:
    5.5 正常退出系统的数据库更新操作正常注销退出系统时,会对已完成订单和菜进行归总,更新厨师被点赞次数以及更新顾客常点的菜,保证数据的完整性。
    6 系统演示、使用手册6.1 开始界面开始界面有当前时间,欢迎词,还有使用者身份选择。进入管理员需要输入账号和密码。

    6.2 顾客界面6.2.1 添加顾客信息不需要注册,顾客只需要输入自己的信息,系统会自动检索数据库,如果没有则自动插入,有则把信息 查找出来并显示。信息包括姓名、联系方式、常点的菜。

    6.2.2 修改顾客信息点击修改信息,左边的框会可以以输入,修改完信息后点击确定即可。
    6.2.3 预定功能在菜单栏中选择“进入用户界面”

    在弹出的窗口中输入信息,点击“确定”

    选择“预定”

    填写预定信息,然后点击“预约”,即可完成预约

    6.2.4 就餐(选座,就餐)就餐步骤前两步同预约操作前两步,完成后进入如下页面





    6.3 管理员界面上面是菜单栏,点击对应的按钮会显示相应的界面,下面是状态栏,显示单位、时间以及作者。详细功 能见下面描述。

    6.4 房间和餐桌管理6.4.1 增加和修改房间信息

    若要修改房间信息,只需要选中要修改的房间,点击“修改房间信息”,然后修改房间名即可。
    6.4.2 增加餐桌进入“房间和餐桌信息”页面,在“当前房间的餐桌概况”中选择“添加餐桌”,录入餐桌信息后,点 击“确定”,即可完成操作

    6.4.3 删除房间和餐桌
    6.5 职工管理6.5.1 界面介绍职工管理界面分为三部分:服务员信息,厨师基本信息,做菜历史。“服务员信息”栏和“厨师基本信息”栏都提供三种功能:雇佣、解雇、编辑功能。管理员可以雇佣和解雇员工,或者修改员工的信息。“做 菜历史”栏可以查看厨师的做菜信息,查看该厨师做的菜是否被点赞。

    6.5.2 对职工信息的操作(增、删、改)以添加厨师信息为例:点击“雇佣”,在弹窗输入员工信息,点击添加,即可完成操作。删除操作只需 要选中要删除的员工,点击解雇,即可删除。编辑操作只需选中要修改信息的员工,点击修改,填写新的信息,即可完成 。

    6.6 订单管理6.6.1 界面介绍当在用户界面内,完成前面的操做提交订单之后,即可点击“订单管理”进入订单管理页面对订单进行 管理。如下图所示:在“当前订单”页面我们可以看到当前正在处理的订单,点击正在处理的订单,我们可 以在右侧查看该订单内所点的菜是否已经全部上菜。若菜已经全部显示“是”则表示菜已全部上齐,则顾客结账时可以点击结账按钮,进行结账。除此之外我们同样可以在订单管理页面对历史订单进行查看(该部分 在下面会单独介绍)。

    6.6.2 查看历史订单在订单管理中我们同样可以查看历史订单的情况,方便餐厅管理人员对老顾客所喜爱的菜色进行统计, 以达到可以为用户推荐菜色的功能。在“历史订单”窗口中我们可以对历史订单进行点击查看历史订单中所 包含的菜的种类以及菜的价格。在历史订单中我们可以对日期进行选择查看具体某一天之前的订单,当取 消对该项的选择时,则表示只查看当天的订单情况。

    6.6.3 点菜功能订单管理页面中同样存在点菜的选项,该选项是为了给使用预约功能的顾客点菜所使用的。当根据订单 号查询到该预约信息时,我们可以点击点菜,为该订单进行点菜。

    6.6.4 结账功能当订单中的菜,全部上完后。点击结账(如下图:)会弹出下图左侧的页面,方便用户对订单中的菜进行核对,以及对价格存在问题的菜进行查看,确定所应付款金额。

    6.6.5 点评功能
    6.7 顾客管理6.7.1 界面介绍在主页面中点击“顾客管理”页面可以对顾客的信息进行操作和分析。顾客管理页面由所有顾客信息, 当前顾客信息,顾客的光顾记录,和点菜历史四部分组成。通过这四部分我们可以完成对所有顾客的信息和订单进行管理。各部分具体的功能在下面会有具体的介绍:

    6.7.2 对顾客信息的操作(增、删、改)在所有顾客信息部分我们可以对顾客的信息进行增加、删除、和修改的操作。下面,我们以删除顾客信 息的操作进行详细讲解。在所有顾客信息窗口中点击要删除的用户,就可以在当前顾客信息中看到顾客的 具体信息,此时,点击删除顾客信息,就会弹出操作确认的信息窗口,避免使用者错误操作,造成用户信息 的丢失。若该操作无误,则点击确认即可删除当前所选的顾客的信息;点击顾客之后即可在当前顾客信息处 完成对顾客信息的修改;点击新增顾客后输入当前顾客信息即可完成新增顾客的操作。

    6.7.3 光顾功能选中顾客信息后,点击光顾即可为顾客添加新的订单。在当前顾客信息下面点击光顾按键,则会弹出房间和餐桌信息界面,此界面下的操作和介绍我们在之前都有介绍,在此处就不再赘述。

    6.7.4 光顾历史记录在所有顾客信息部分,点击某个顾客。在光顾记录部分会显示该顾客在本餐厅的所有的订单情况。以及 该顾客的所有点菜历史,其中包括每种菜被点的次数和该顾客对这种菜的评价信息。该部分主要被用于数 据库有用信息的挖掘,了解顾客对每种菜的喜爱程度,从而做到为顾客推荐本店所添加的新的菜色,以及对餐厅未来菜色的发展策略提供理论依据。

    6.8 菜谱管理6.8.1 界面介绍菜谱管理主要是为了完成对餐厅菜谱的菜进行增加、删除、和修改而创建的,该页面除了完成最初设立 的功能外,还可以根据不同菜的具体信息对菜进行查找。同时还可以查询点过这道菜的顾客都有哪些。最主要的是该也也可以调用已有的用户信息为用户点菜提供便利。

    6.8.2 对菜谱的基本操作(增、删、改)在菜谱中点击一种菜之后点击修改,会弹出图中所示的窗口,使用者只要输入所要修改的信息之后,再 次点击修改菜谱窗口中的修改,即可确认对菜的信息进行修改,此时菜谱中就会显示修改后的信息。点击添 加按键,录入菜的信息后再次确认即可完成对菜的添加;对菜的删除,选中一种菜后点击删除再次确认即可 完成对菜的删除。

    6.8.3 快速查找和模糊查找快速查找和模糊查找,是为了避免客户点菜时总是要翻阅全部菜单浪费时间而设计的。因为快速查找只 要输入菜的名字就可以对菜进行定位比较简单,所以这里我们对它不再做过多的介绍;模糊查找用户只要 输入部分信息,系统既可以对数据库进行访问,然后返回能够匹配到用户输入的关键字的菜。

    6.8.4 快速下单快速下单是为了方便用户的点菜而设计的。只要在菜谱管理页面,点击“点菜”,即可选择数据库中已 有的顾客,为他选择餐桌。在该页面直接就可以为顾客点菜。因为具体点菜操作在前面我们都已经详细介绍 过了,在这里我们就不再对其进行赘述。
    7 体会一开始走入了一个误区,那就是为了省空间而尽量少的建立表,其实这是一个极端。举个例子,当时我 一致认为顾客的订单中的菜可以通过视图来查看,因为视图在数据库中属于外模式,不会占用磁盘存储空 间,而且能用有效地限制用户的权限,而视图名就是订单号,这样通过订单号就能对订单进行操作。实际上 这个想法是不好的,因为订单承载了很多信息,从系统可拓展性来说,这是不利的,而且实际上对视图的操 作不仅仅会限制用户的操作,也会限制自身编程的操作。因此我们前后期大部分修改了我们的需求分析和 概念结构设计。
    对于数据库的建立,我深深地每个表建立一个唯一的 id 标识是很重要的,这个 id 标识是主属性。即使 实体中原本的属性就有能唯一标识元组的属性,但也不应该直接设置为主码,而应该再设置一个 id,使用关 系数据库自身提供的自增长类型即可,这样无论什么用户都不能对这个 id 修改,插入元组时数据库会自动 生成。这样做的好处是因为主码的值是可能修改的,例如顾客的联系方式,没有 id 的话不利于参照完整性 的实现,尤其是修改操作。
    本次实验的核心工作是高级语言 C#编码与数据库的连接,之前没接触过 C#,纯属是现学现用,感觉学 到了很多,也提高了自己的自学能力。同时让我基本入门了一门语言,C#真是一门有趣的高级语言。也就是 在编程的时候和书上所学的知识结合起来,才进一步领略到数据库的重要性和数据库的应用,以及进一步 理解书上的知识,例如游标的概念,其实和高级语言中的流是一个道理的,这种东西在使用时有一定的规则 需要注意,例如不能两个同一对象流一起使用等。实验使我对高级语言的编写和调试以及一个项目的过程,
    数据库的设计都有了进一步的掌握。实际应用中的数据库设计会比我们课程设计要复杂得多,这次实验也 让我们对流程有所了解,方便日后的深入学习。 在实际编程时候,sql 语句往往是动态的,需要根据动态参数设定,这当中最容易出错的是字符串,由 于 sql sever 查询语句中的字符串时用单引号的,但是高级语言一般都是双引号的,这方面容易出错。在者 就是再编辑 sql 语句的时候容易把单引号漏掉,导致数据库操作异常。该问题本质上还是 sql 语句的练习不 够多而造成的,因此平常要注意多加练习。 当然我也认识到我们的系统还是有很多不足,除了数据库设计上的不足外,还有系统本身功能的不足, 例如顾客如果订很多张餐桌的话,每个单子的菜必须一致,且确定订单和结账都要操作多次,不利于用户体 验,再者不能重复点菜。各个表的主码中,只有订单号是系统自动生成,其他均由顾客自己输入,不利于管理。许多过程不可逆,例如无法没有取消订单功能等。还有很多细节上的还有缺陷,日后应当多加学习。
    7 评论 239 下载 2019-03-12 11:56:11 下载需要14点积分
  • 基于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);
    七、系统实现功能结构图
    46 评论 1403 下载 2018-11-19 09:32:08 下载需要14点积分
  • 基于Java实现的邮件发送客户端

    一、实验目的与要求要求学生掌握 Socket 编程中流套接字的技术,以及邮件的发送。

    要求学生掌握利用 Socket 进行编程的技术
    不能采用现有的工具,必须自己一步一步,根据协议进行操作
    要求每一次操作,必须点击下一步才能继续
    了解邮件发送格式
    必须采用图形界面,可以编辑发送内容
    可选,建立自己的邮件服务器
    发送邮件可以发给自己的邮件服务器,也可以发给已知邮件服务器
    要求可以查看得到发送的邮件

    二、实验内容2.1 电子邮件概述电子邮件系统具有三个主要组成构件,即用户代理、邮件服务器以及邮件协议。主要的应用层协议就是简单邮件传送协议 SMTP(Simple Mail Transfer Protocol)、邮局协议 POP3(Post Office Protocol)以及 1993 年提出的通用互联网邮件扩充 MIME(Multipurpose Internet Mail Extensions)。
    邮件服务器是电子邮件基础结构的核心,其功能是发送和接收邮件,同时还向发件人报告邮件传送的结果(已交付、被拒绝、丢失等),按照客户服务器方式工作。
    邮件服务器使用两种不同的协议。一种协议用于用户代理向邮件服务器发送邮件或者在邮件服务器之间发送邮件,如本实验中的 SMTP;另一种协议用于用户代理从邮件服务器读取邮件,如邮局协议 POP3。
    用户代理 UA(User Agent)是用户与电子邮件系统的接口,大多数情况下它就是运行在用户电脑中的一个程序。因此用户代理又称为电子邮件客户端软件。用户代理向用户提供一个友好的接口(目前主要是窗口界面)来发送和接收邮件。
    电子邮件最主要的组成构件

    2.2 邮件发送与 SMTP 协议SMTP 规定了在两个相互通信的 SMTP 进程之间应如何交换信息。SMTP 规定了 14 条命令和 21 种应答信息(部分可见参考与附录)。每条命令用几个字母组成,每一种应答信息一百只有一行,有 3 位数字的代码开始,SMTP 的流程可以简单抽象成下图。

    通过 Windows 命令行下的 Telnet,可以简单的实现 SMTP 发送邮件的简单过程。

    建立 TCP 连接:telnet smtp.qq.com 25 <地址,熟知端口>
    客户端发送 HELO 命令标识发件人自己的身份,并得到服务器返回的状态
    客户端 AUTH LOGIN 登陆,Base64 编码用户的邮件地址和密码
    邮件传送是从 MAIL 命令开始的,传送邮件的主体
    以<CR><LF>.<CR><LF>作为邮件内容的结束
    连接释放 QUIT 命令

    telnet 使用 SMTP 发送电子邮件

    使用上述命令时的问题(图中的 Warning):

    很奇怪的输入问题,输入过程回退或者输入法为中文时可能会返回 502 Invalid(见错误码),输入时需注意
    MAIL FROM 和 RCPT TO 命令的格式容易出错,使用<>(很多相关博客中并没有<>,我怀疑他们的是错的)
    此处需要有一个 CRLF,这是 MAIL 邮件的格式



    2.3 关于邮件读取与 pop3 协议POP 协议是一种用于接收电子邮件的协议,发送端的邮件根据 SMTP 协议将被转发给一直处于插电状态的 POP 服务器。客户端再根据 POP 协议从 POP 服务器接收对方发来的邮件。在这个过程中,为了防止他人盗窃邮件内容,还要进行用户验证。具体过程是:邮件发送到服务器上,电子邮件客户端调用邮件客户机程序以连接服务器,并下载所有未阅读的电子邮件。这种离线访问模式是一种存储转发服务,将邮件从邮件服务器端送到个人终端机器上,一般是 PC 机或 MAC。
    一旦邮件发送到 PC 机或 MAC 上,邮件服务器上的邮件将会被删除。但目前的POP3 邮件服务器大都可以“只下载邮件,服务器端并不删除”,也就是改进的POP3 协议。

    简单来说,我们需要做的事情就是使用 socket 套接字建立连接,操作 socket 的输入流输和输出流,以达到 Telnet 的效果。
    一个不恰当的比喻:整个邮件发送-传输-接收过程,用户代理(即我们要实现的功能)向对应的 STMP 服务器(比如 stmp.qq.com)发送邮件就像把真实邮件送到附近邮局就可以默认是发送了,邮局把这信件发送的接收者所在的邮局,中间不会停留在某个邮局,pop 协议取邮件即接受者的用户代理去邮局取出邮件。
    2.4 电子邮件的信息格式
    MIME, 全称为“Multipurpose Internet Mail Extensions”, 比较确切的中文名称为“多用途互联网邮件扩展”。它是当前广泛应用的一种电子邮件技术规范,基本内容定义于 RFC 2045-2049。自然,MIME 邮件就是符合 MIME 规范的电子邮件,或者说根据 MIME 规范编码而成的电子邮件。在 MIME 出台之前,使用RFC 822 只能发送基本的 ASCII 码文本信息,邮件内容如果要包括二进制文件、声音和动画等,实现起来非常困难。MIME 提供了一种可以在邮件中附加多种不同编码文件的方法,弥补了原来的信息格式的不足。
    邮件头包含了发件人、收件人、主题、时间、MIME 版本、内容的类型、内容的传输编码方式等重要信息。每条信息称为一个域,由域名后加冒号(“ : ”)和信息内容构成,可以是一行,也可以占用多行。域的首行必须顶头写(即左边不能有空白字符(空格和制表符));续行则必须以空白字符打头,且第一个空白字符不是信息本身固有的,解码时要过滤掉。另外,邮件头中不允许出现空行。
    MIME 电子邮件头部

    MIME 电子邮件段头

    MIME 电子邮件主体格式

    -MIME 电子邮件样例

    如果在邮件中要添加附件,必须定义 multipart/mixed 段;如果存在内嵌资源,至少要定义 multipart/related 段;如果纯文本与超文本共存,至少要定义multipart/alternative 段。其实很容易察觉,MIME 邮件就像我们实体信件一样,信封注明了一些信息。
    三、设计与实现3.1 总体框架框架图

    代码结构

    3.2 Java socket 编程使用关于 java 的 socket 编程,可以看到大致流程是这样的,在建立 socket 连接后,问题转变为如何使用 smtp 和 pop 协议对流进行操作。在连接过程中通过输入输出流读取和发送指令。


    3.3 登陆页 LoginPage
    将登陆功能封装到 User 类中,使用 java 的 socket 连接 SMTP 服务器,并且捕获socket 的未知主机异常(UnknownHostException)、连接异常 ConnectException、连接超时异常(SocketTimeoutException),并且得到 socket 的输入流和输出流。

    根据 HELO 的返回是不是 250(不是说没服务器忙于其他)和 AUTH LOGIN 命令的返回码是不是(235 说明用户名密码正确)确定登陆是否成功,然后释放连接。

    登陆按钮的点击触发事件。


    3.4 主页面 mainPage此模块绘制页面,主要做一些按钮的监听事件和负责显示文字。


    3.5 写信页 SmtpPage这部分将实现 SMTP 发送邮件功能,数据流可以看主体框架得到。

    通过将发送邮件封装成 send 类,通过图形部分得到收件人、主题、内容、附件;在 send 类中实现邮件的发送。监听发送按钮,触发点击时,捕获各个文本域信息传递给 send 类发送。

    首先,由于已经使用账号登陆成功,所以接下来发送邮件不必去做检验账号和密码处理,但是 socket 的异仍需要捕获。简单的流程框架就是使用 telnet 发送邮件的流程。不过需要处理的是:对各个部分的编码、以及带附件以后采用 mixed 格式,那么将包含几个 multipart,分别由于—boundary 分割开,在每一个 multipart 中都将在此声明一个子 part-boundary 用于在此 multipart 中分割。另外需要声明此部分内容的文本格式(假如是文本的话),以及编码方式。然后,在下一个multipart 就可以类似的加入附件,可参考以下流程图。
    对于正常不带附件的邮件,那么只需要在一个 part 中描述编码的内容即可,当然也可以多个 multipart 的文本,不过对于实现来说容易,但是在 pop 解析部分变得异常艰难。
    send 类发送的流程图


    对应上述流程图的附件或正常文本格式的头部。
    优雅的 smtpPage 发送界面图

    友好的提示

    登陆 QQ 邮箱检查上面发送的邮件,并打开附件 PDF 可以看到正常,说明编码和解码没有问题。


    3.6 读信页 popPage此模块堪称为最难部分亦是最复杂部分,不亚于写一个编译词法语法分析。
    首先由上述实验内容部分可以得到MIME邮件的基本格式,这其中首先要对smtp 的命令关键字进行识别,然后在 string 中截取出文字格式、编码方式、boundary、filename 、 multipart-type 等 内 容 , 由 于 邮 件 都 是 以 —mainboundary— 和 <CR><LF>.<CR><LF> 结束,可作为一切循环的终止条件,以防进入死循环。
    pop 读取解析邮件

    -doMailBody 流程图


    由于各种用户代理之间的差异,无可避免的存在了多种情况,如编码和未编码,编码中又有 UTF-8 和 gb2312、gbk 等,有一些是没有双引号”修饰的;未编码可能就相对简单。而其他部分也或多或少存在相似的处理。
    截取 from 的 type 和 codetype 代码。


    popPage 实验结果

    popPage 实验结果检验-1

    邮件是可以自己给自己发送的,默认附件是自动下载的,如上打开附件没有问题。尝试着解析其他人发来的邮件,此部分堪称最难,由于各种不同的用户代理采用都是 MIME,但是很多细节格式并不一致,比如 from、subject、boundary 的位置顺序并不是固定的。实话说,解析邮件不亚于一个编译器。
    以下解析其他人发来的邮件结果。
    popPage 实验结果检验-2

    popPage 实验结果检验-3

    一封邮件的原文大致如下,不带附件的。

    一封带附件的邮件原文,可以观察 boundary 和 partboundary,对照着上述流程图。

    附件解码代码。

    四、心得体会首先,我觉得应当先说明遇到的那些问题:
    在实现 telent 发送邮件时,从网络上搜索参考的大部分博客在 mail from 和rcpt to 指令上皆没有<>,导致停滞了很久。
    在没有知道 Windowsbuilder 之前,我企图去画图形界面,简直就是天方夜谭;好在参考报告模板里提到了 Windowsbuilder,于是让图形化变得简单,从而专注于功能的实现。
    由于之前没有 java 图形编程经验,基本一切从 0 开始,学习 java swing,容器面板、文本域、按钮、label 等的使用,如何设置图标,如何事件监听触发。
    在 socket 连接中,好在 java 有各种异常的捕获,也因此减少了很多问题,还有其他的数据越界之类的异常,真的省了不少精力。
    在实现邮件的解析部分真的令人崩溃,代码必须适配各种不一样的邮件格式,即使他们采用的是同一个标准,一不小心就死循环。而那些嵌套 mixed 的就更可怕了,boundary 的子 part boundary……比起来,解析邮件就简单多。
    编码部分亦是一大头痛问题,因为有些邮件原文采用 UTF-8/GBK/gb2312,或Base64 或 qp 或明文,需要把各个词语截取下来,然而有一些会多个空格,或是没有引号”…….不禁感叹制定标准和建立标准体系多么重要,秦始皇车同轨书同文是多么伟大。
    实话说,解析原件部分简直和编译的词法语法分析似曾相识,不可能兼容所有的邮件,所以有些 bug 总是要存在的。当然,经过本次工程实验的,不管是对SMTP/POP 协议、电子邮件格式,还是 java、流程图和 eclipse 的使用上都大有收获。因为之前并没有实际上手过 java,导致一开始犹豫 C++还是 java,最后是馋了 java 的 swing 图形才敢于上手,至于最终结果也是满意的。
    在没有清晰的框架概念之前,最后不要下手写代码,不然会一改再改,面目全非。现代人的世界离不开网络,我们并不是孤独在一条路上行走,固然有众多前辈留下了宝贵的经验,值得去参考和借鉴。为什么不呢?闭门造车的时代早已逝去。即使是重复造轮子,那也要尽量造的圆一些。
    参考与附录[1] Socket 收发邮件—SMTP 和 POP3https://blog.csdn.net/iteye_18655/article/details/81400129
    [2] SMTP 邮件传输协议发送邮件和附件https://www.cnblogs.com/sdgwc/p/3324368.html
    [3] Socket 计算机网络—电子邮件https://blog.csdn.net/NEUChords/article/details/92090423
    [4] SMTP 错误码/建议解决方法https://blog.csdn.net/chenfei_5201213/article/details/10138969
    [5] SMTP errors and reply codeshttps://serversmtp.com/smtp-error/
    [6] 电子邮件基本格式(MIME 格式)https://blog.csdn.net/weixin_37958284/article/details/77186279
    [7] MIME 格式介绍https://www.cnblogs.com/robinhood/articles/540464.html
    [8] MIME 参考手册https://www.w3school.com.cn/media/media_mimeref.asp
    类图
    2 评论 1 下载 2021-04-14 11:35:58 下载需要11点积分
  • 基于ASP.NET和Oracle数据库实现的农场信息管理系统

    一、引言1.1 文档目的本文档为 农产品基础数据库系统项目管理系统详细设计,本文档用于描述 农产品基础数据库系统各部分的细节实现,为编码提供参考依据。本文档的读者范围为:系统设计人员农产品基础数据库系统,模块设计人员,编码人员,测试人员。
    本文档作为双方就建立 农产品基础数据库系统需求理解达成一致共识的基础文件,作为双方界定项目范围、签订合同的主要基础,也作为本项目验收的主要依据。本项目为软件开发,不包含不部署硬件。
    1.2 项目背景随着农村经济的发展,建立农产品基础数据库,对农产品的价格进行监控,分析和决策,对于增加农民收入,抵御价格风险,指导播种面积,具有重要的意思。
    本项目由项目领导小组负责监督执行,任务完成后,本系统将运行在微软的.net平台上。
    1.3 术语Microsoft .NET Framework:Microsoft .NET Framework是用于的新编程模型。它将强大的功能与新技术结合起来,用于构建具有视觉上引人注目的用户体验的应用程序,实现跨技术边界的无缝通信,并且能支持各种业务流程。
    中间件:中间件是一种独立的软件或服务程序,分布式应用软件借助这种软件在不同的技术之间共享资源。中间件位于客户机/ 服务器的操作系统之上,管理计算机资源和网络通讯,是连接两个独立应用程序或独立系统的。相连接的系统,即使它们具有不同的接口,但通过中间件相互之间仍能交换信息。执行中间件的一个关键途径是信息传递。通过中间件,应用程序可以工作于多平台或环境。
    1.4 命名约定
    类(Classes):类名是个一名词,采用大小写混合的方式,每个单词的首字母大写。例:class User
    接口(Interfaces):大小写规则与类名相同。并且以大写i为开头。例:Interfaces IUserService
    方法(Methods):方法名是一个动词,采用大小写混合的方式,第一个单词的首字母小写,其后单词的首字母大写,例:runFast();
    变量(Variables):采用大小写混合的方式,第一个单词的首字母小写,其后单词的首字母大写。变量名不应以下划线或美元符号开头 ,例:User user
    常量(Constants):类常量和ANSI常量的声明,应该全部大写,单词间用下划线隔开,例:static final int MAX_WIDTH = 100
    在对包、类、接口、方法、变量、常量命名时一定要名称反映意图的原则,避免使用非常用缩写
    代码规范:有些不易理解的变量或函数应作注释,难懂的代码要有注释,在文件的开始处有该文件的用途描述。一定要保持注释的一致性代码组织要清晰,{,},(,),if,else,do,while,for,case等要对应整齐,缩进全部用Tab键。变量的定义要集中,行数间要有空行分开,一个程序中的空行数目最好占8%-16%。多态函数和功能相近的函数集中在一起代码应该简洁、清楚并讲述了所发生的一切,我们的目标应该是写出最清晰的代码,而不是最巧妙的代码代码的重用要仔细,要讲相关的代码也拷贝过来,注意那段代码也许不适合你的应用场景删掉从来没有用过的函数或变量,大篇幅注释掉的代码也应该删除,以免使程序混乱难度
    工程文件组织规范:一个工程往往包含很多文件,向工程中加入文件或删除工程中的文件要慎重,避免把工程损坏。工程中不起作用的文件或类应删除,工程目录下的非工程文件也应该移走,保持工程的清洁,避免混淆难以管理一个比较大的工程应留有一定的消息接口或插件接口工程的版本控制要严格,版本格式为xx.xx.xx,必要时使用Build次数或日期。高版本尽量兼容低版本的用法、数据或协议工程的编译宏定义和工程参数设置应正确,每作一个新工程时应检查工程参数是否正确工程文件应经常备份,备份时注明备份日期和主要增加的功能
    类组织规范:类力求封装好,严格区分public,private,protect等作用域,如果一个函数与本类有莫大的关系,可以作为该类的静态成员函数,不用或少用友元函数破坏类封装性的方法和技巧类的成员变量在构造函数或初始化函数中应赋初值。指针在构造函数中赋NULL,析构时DEL_EMPTY它,以免内存泄露
    用户界面规范:风格统一,按照对方要求修改,下面效果图仅参考有四大类型的用户界面:对话框、单文档界面、其他界面对话框要易用且简洁,字体和控件的组织搭配要得体,能简单不复杂,各控件的焦点、Tab顺序等要研究。在简洁易用的前提下,力求个性化,设计得更友好。程序各对话框的分割要保持一致单文档和多文档界面的程序功能可以做得很好,也便于扩充和管理。其中菜单、工具栏、状态栏等设计要有特色。菜单按一定的分类弹出,必要时设计成多套菜单,在重要的窗口或区域应能弹出右键,实现常用操作。工具栏上放最常用的操作按钮,必要时动态更换按钮。状态栏显示足够多的消息

    二、软件系统的结构2.1系统的总体架构项目的架构设计遵循平台化、组件化的设计思想,采用统一的数据交换、统一的接口标准、统一的安全保障。
    项目的架构设计遵循平台化、组件化的设计思想,采用统一的数据交换、统一的接口标准、统一的安全保障。系统由下到上分为三层主框架:数据采集层、存储分析层、应用逻辑层,总体建设框架如下图所示。

    农业基本信息、分类信息数据库:主要包括各种农业产品分类数据库、产品描述数据库信息管理、维护,基础字典管理、单位管理等。

    网络爬虫:负责从各大农业信息网站爬取农业产品、价格数据
    格式检查、清洗、转换:负责把从各网站的数据,对齐,处理、分析存入到大数据库中。
    存储分析:利用Spark完成分析查询,mongodb完成存储等
    安全体系:包括权限管理,安全管理、数据备份等
    标准体系:农产品基本信息描述等各种标准管理
    应用逻辑:数据可视化,领导驾驶舱,大数据展示,数据查询,指标趋势分析、决策应用等

    2.2 程序关系图
    2.3 软件角色定义我们可以将这些项目工作的参与者分为四级:从软件角度考虑依次抽象为:

    系统管理员:负责系统基础数据的配置、管理,农产品分类数据的管理,农产品基本信息的管理
    工作人员:农产品信息查询,统计分析,数据导出,采集数据的管理等
    领导:指标分析结果查看,统计数据分析等

    2.4 程序系统的结构用一系列图表列出本程序系统内的每个程序(包括每个模块和子程序)的名称、标识符和它们之间的层次结构关系。
    三、程序设计说明从本章开始,逐个地给出各个层次中的每个程序的设计考虑。对于一个具体的模块,尤其是层次比较低的模块或子程序,其很多条目的内容往往与它所隶属的上一层 模块的对应条目的内容相同,在这种情况下,只要简单地说明这一点即可。
    3.1 信息门户3.1.1 程序描述信息门户(Portal)位于地勘项目管理系统体系结构中的最上层,实现各应用与用户的交互服务过程,是地勘项目管理系统的信息集中展示的窗口,为各类用户提供一个统一的信息服务入口。同时,信息门户根据用户身份提供满足其需求的特定信息和应用系统,为用户提供个性化的服务。
    3.1.2 功能用户登录
    所有用户均通过信息门户登录地勘项目管理系统,通过统一身份认证、基于身份/角色的权限分配策略实现单点登录。



    输入
    处理
    输出




    页面加载
    调用用户的个人信息及能操作的模块信息
    显示其有权操作的功能模块及个人信息


    输入用户名、密码、验证码
    控制器提交到认证中心进行认证
    认证通过返回信息门户view;不通过,返回登陆view及错误信息。



    信息门户主页面



    输入
    处理
    输出




    目录树中所选的单位节点
    调用manager.findDw取得下级单位列表
    在显示单位信息表显示下级单位列表


    在菜单功能列表中点击某功能
    客户端执行主功能区新增tab的命令
    在功能区新增一tab加载相应的功能模块


    点注销按钮
    清除其登陆信息
    重定向到登陆页面


    点修改密码按钮
    客户端执行打开window的命令
    显示用户修改密码的窗口,并提供保存、取消按钮



    3.1.3 用户界面设计登陆页面

    信息门户主页面

    3.2 基础信息维护3.2.1 部门管理部门管理是对下属单位的部门信息进行维护,即添加,修改,删除。



    输入
    处理
    输出




    页面加载
    按单位级别返回部门数据
    显示部门目录树


    目录树列表中点击某部门信息
    客户端从服务器端获取部门相关信息加载到编辑信息窗口
    显示部门代码、上级部门代码、部门名称和描述等信息



    3.2.2 角色管理角色管理是独立用户管理中的另一种角度,在系统维护的权限赋予上,可直接通过角色进行权限的转移。另外一种角色的使用是可归之于在使用上的赋权或称授权。



    输入
    处理
    输出




    页面加载
    返回已定义角色数据
    显示角色列表


    列表中选择某角色信息
    客户端从服务器端获取角色相关信息
    显示角色英文名,中文名及备注等信息


    分配角色模块权限
    给选择的权限添加系统模块并授权
    角色具有相应的操作权限



    3.2.3 用户及权限管理用户及权限管理是地勘系统使用的基础,包括对用户信息的添加、修改、删除、查询,以及给用户配置角色。



    输入
    处理
    输出




    页面加载
    返回已定义角色数据
    显示角色列表


    输入用户名、角色类型
    客户端进行查询,从服务器端获取相关信息
    显示用户编号、用户名、联系方式、电子邮件、是否管理员及备注等信息


    单击新增、编辑、删除和角色授权按钮
    在编辑界面中对用户数据进行操作,授予用户角色权限
    新增用户,对用户信息进行变更



    用户及权限管理页面设计


    3.2.4 基础信息描述对系统中的常用代码进行维护,农产品基础信息描述谷物类,其他参考相应国家标准。



    序号
    名称




    1
    名称


    2
    净含量


    3
    规格


    4
    生产者名称


    5
    生产者地址


    6
    生产者联系方式


    7
    代理商名称


    8
    代理商地址


    9
    代理商联系方式


    10
    进口商名称


    11
    进口商地址


    12
    进口商联系方式


    13
    经销者名称


    14
    经销者地址


    15
    经销者联系方式


    16
    委托单位名称


    17
    委托单位地址


    18
    受委托单位名称


    19
    受委托单位地址


    20
    统一社会信用号


    21
    产地


    22
    原产国


    23
    地区


    24
    生产日期


    25
    保质期


    26
    存储条件


    27
    运输条件


    28
    食品生产许可证编号


    29
    产品标准代码


    30
    图片


    31
    商标


    32
    品牌


    33
    单价


    34
    包装


    35
    检验报告


    36
    认证证书


    37
    辐照食品


    38
    转基因食品


    39
    营养成分


    40
    质量等级


    41
    批号


    42
    食用方法


    43
    适宜人群


    44
    致敏物质


    45
    感官标志


    46
    理化指标


    47
    食品安全指标


    48
    色泽


    49
    气味


    50
    水分




    3.2.5 农产品分类管理农业部农产品标准分类-2010

    3.3 农产品信息采集3.3.1 农产品信息管理包括添加、修改、删除、导入、导出,包括:农产品生产企业/农村合作社、生产面积、品种、产量、上市时间、质量和技术水平、加工企业、贮存条件、保质期、售价、传统销售渠道及比例、自主品牌拥有情况、农产品电商企业等,不固定信息及信息类型、不限制信息数量
    3.3.2 农产品数据抓取使用爬虫技术,自动在互联网抓取农产品信息,并保存在系统中。

    3.4 农产品信息分析3.4.1 农产品指标分析对农产品信息进行统计分析

    3.4.2 农产品指标趋势分析对农产品各项指标进行趋势分析及预测

    3.5 农产品信息显示3.5.1 农产品信息查询根据条件查询农产品信息,并导出

    3.5.2 农农产品信息展示提供领导驾驶舱页面,展示包括各项指标、分布、趋势、决策分析等,能够适配2×2个55寸大屏(分辨率:1920×1080)展示

    3.6 数据维护模块系统管理数据库备份与还原功能

    3.7 系统管理3.7.1 系统日志管理系统日志包括登陆日志和操作日志,主要是记录用户登陆详细信息和日常使用操作信息。包括日志的查询与删除功能。



    输入
    处理
    输出




    页面加载
    按日期或类型返回数据
    显示日志列表信息


    列表中选择查询日期或类型信息
    客户端从服务器端获取相关信息,对其进行相应的操作(查看详细、删除)
    显示登陆日志和操作日志信息



    系统日志管理页面设计

    3.7.2 系统信息设置
    3.7.3 用户管理改个人信息

    修改登录密码
    1 评论 2 下载 2021-04-14 08:11:16 下载需要11点积分
显示 30 到 45 ,共 15 条
eject