分类

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

资源列表

  • 基于Python的TCP和UDP数据流的带宽竞争分析

    第1章 绪论1.1 题目来源本课题来源于计算机网络实践与应用课程的创新型实验,题目为“TCP和UDP数据流的带宽竞争分析”。实验的主要目的是通过编程实现多进程TCP和UDP数据传输模块,编写相关的TCP/UDP流利用带宽测量软件,设计实验,让多个TCP和UDP流竞争有限带宽,分析结果,进而掌握TCP和UDP流传输的特点,了解工作原理和工作过程,并通过测试验证其竞争机制。
    1.2 项目意义在实际应用中,一个端设备往往同时存在多个TCP和UDP的连接。通过此项目,我们可以了解多连接情况下TCP和UDP的运行性能与特点,从而在实际的应用中合理分配混合数据流的TCP和UDP的连接数,从而提高数据流的传输性能,充分利用有限的带宽,提高传输效率。
    1.3 项目创新分析该项目通过实现编程代码来进行TCP和UDP的传输和监听,创新性地将TCP和UDP的相关内容特点与现实问题(有限带宽竞争)相联系,可以让我们从新的角度理解课程中以及书本上的TCP和UDP的特点,增强了我们用实践来检验理论知识的创新意识。
    第2章 与项目相关的主要技术及其分析2.1 socket编程(python)Socket套接字是网络通信过程中端点的抽象表示,是程序进行网络通信的基础,使应用层能使用传输层提供的服务(TCP和UDP),进而实现TCP和UDP的数据传输模块。
    本次实验中,我们使用python中的socket包,分别在client和server中将socket与IP地址和端口号建立对应关系,进而实现TCP和UDP的发送和监听。
    2.2 多线程技术多线程技术建立多个线程来收发数据,可以用来同时建立多个连接,进而可以在一个程序中进行TCP和UDP的同时收发,进而实现可控的连接数对网络带宽的竞争。
    使用python中的threading包来实现函数在多个线程的同时运行。
    2.3 流量的实时计算与可视化(python)通过调用pyshark(基于wireshark的Python库)来进行实时捕获,分析每秒内不同目的端口接收的TCP和UDP数据包,计算流量。
    使用python中的matplotlib来实时绘制TCP和UDP的流量曲线图,从而进行流量的可视化,体现TCP和UDP的竞争关系。
    2.4 GUI编程(python)Tkinter 是 Python 的标准 GUI 库。Python 使用 Tkinter 可以快速的创建 GUI 应用程序,从而使整个操作过程直接明了,易于控制。
    第3章 混合数据流带宽竞争分析实现3.1 实验方案用python实现客户端与服务器端的应用程序client.py与server.py,以及在服务器端运行的流量监听工具Monitor.py。
    使用两台计算机设备,分别作为服务器端和客户端。
    先在服务器端运行程序Server.exe(生成的可执行文件)与Monitor.py,再在客户端指定服务器IP地址与要发送的文件,进行多种情况下的TCP和UDP的竞争传输,在服务器端查看传输过程以及monitor的流量图像。
    3.2 客户端程序的实现用来向服务器指定端口发送数据包,TCP的目的端口是10241,UDP的目的端口是10240。

    TCP套接字connect服务器的10241端口,建立连接,再从本地读取文件,发送文件大小后,再发送数据包(分组大小设置为BATCH_SIZE),在发送数据的同时打印已发送数据的大小
    UDP进行无连接传输,直接向服务器的10240端口发送文件(如果先发送文件大小, 可能丢包)
    为TCP和UDP传输分别启用一个线程,用户自行选择传输方式(只进行TCP传输,只进行UDP传输,两者同时进行)

    运行效果

    3.3 服务器端程序的实现3.3.1 服务器端程序Server.py用来接收数据包,在10240接收UDP数据包,在10241接收TCP数据包。

    同样启用两个线程,设置两个socket分别绑定10240和10241,用来接收数据,使用listen方法进行监听,并将接受的数据包写入指定文件中
    TCP连接能先获知接受数据的大小

    实现效果

    3.3.2 流量计算工具Monitor.py通过调用pyshark(基于wireshark的Python库)进行实时捕获 ,分析每秒内目的端口为10240的UDP数据包和10241的TCP数据包,计算流量,使用matplotlib实时绘制TCP和UDP流量曲线图。
    3.4 带宽竞争的实现按照3.1中的实验方案进行操作,Client端要发送大量数据来竞争带宽,因此在操作过程中选择一个较大的文件进行TCP和UDP的传输,确保达到有限带宽内的竞争。
    第4章 项目测试4.1 测试方案
    基本操作过程按照3.1实验方案进行
    TCP和UDP同时启动时,进行实验,观察流量监听界面,进行分析
    TCP先启动时,UDP后启动时,进行实验分析
    UDP先启动时,TCP后启动时,进行实验分析

    4.2 测试结果TCP和UDP同时启动时,流量监听界面如下
    g)
    发现在同时启动的情况下,TCP传输相对于UDP传输有一个滞后的过程,UDP发送很多数据后TCP才会进行发送。TCP是面向连接的传输层服务,开始时需要建立连接(三次握手),而UDP是无连接的,直接进行数据的发送。因此会出现这种现象。
    TCP先启动时,UDP后启动时,流量监听界面如下


    UDP先启动时,TCP后启动时,流量监听界面如下


    经过以上监测到的竞争过程,我们可以发现:
    在TCP和UDP竞争中,UDP会占用更多的带宽。(还可能出现UDP完全压制TCP传输的现象,如下图所示)只有在UDP传输完成后,TCP才能恢复到较高的速率进行传输。

    第5章 结论在测试中,带宽占用率过高时,TCP和UDP竞争中,由于二者对网络拥塞的反应是不同的。TCP具有拥塞控制机制,对拥塞的处理是降低自身的传输速率,从而避免丢包的发生。而UDP缺少端到端的拥塞控制,进而在链路带宽的占用上处于优势,但会丢失一些数据包,其传输是不可靠的。
    竞争会使TCP流得不到公平的带宽,因此应当探索一种机制,来处理TCP和UDP的竞争过程。
    1 评论 2 下载 2019-10-22 13:18:16 下载需要13点积分
  • 基于java的仿QQ聊天工具

    一 需求分析本系统是基于java开发的聊天室。有用户注册、用户登陆、修改密码、忘记密码、添加好友、用户聊天、群聊功能。如果服务器还没有启动,则客户端是不可以登陆、注册、忘记密码,如果在运行过程中,服务器断开则系统会有提示,聊天对象如果下线发送消息后会有弹窗提示,添加好友后有是否添加好友成功提示。
    二 概要设计在客户端:当用户登录后,生成唯一的socket, 存放在Client实体类中,在整个客户端就一个Client类和一个socket。有一个窗口控制器——ChatUIList,用来记录用户和好友聊天框是否打开,当收到消息后,首先在ChatUIList中查询是否有好友的窗口,如果没有则新建聊天框弹出并显示消息,如果存在与好友的窗口则将消息追加到原聊天框并重新着重显示该窗口。在客户端还拥有一个“命令控制中心”——ChatTread类,在ChatTread类中判断并处理来自服务器中的命令(消息),如果是“message”那么客户端收到是来自好友的消息,如果是“requeste_add_friend”则是好友申请命令,类似有“WorldChat”,“accept_add_friend”,“refuse_to_add”,“changepwd”等命令。
    在服务端:有多个socket,用SockList管理连接成功的用户名及其socket。同样在服务端也有一个“命令控制中心”——ServerTread类,它负责处理来自客户端的命令(消息),判断命令的类型,并正确处理他们,给出处理结果和判断是否处理成功,将处理后的命令转发给正确的用户。
    功能设计如下:

    注册功能
    设计一个注册UI(RegisterUI)类,在打开程序后,模仿QQ在左下方有一个注册按钮,点击注册按钮后弹出注册页面,用户填完必填信息后由客户端将命令发送给服务端(如果服务器在线),服务器收到“register”命令后,连接数据库判断,如果注册成功则返回注册成功消息并弹窗提示,如果失败则弹窗提示注册失败。
    登录功能
    打开客户端后,类似QQ有登录按钮,当用户填完用户账号和用户密码并提交后,客户端将登录请求发送给给服务端判断(如果服务端在线),如果密码正确则用户登录成功,显示朋友列表(FriendsUI),否则提示密码错误或账号不存在。
    忘记密码
    在客户端右下侧有忘记密码按钮,用户点击按钮后弹出忘记密码页面(ForgetUI),用户填写用户账号后客户端将消息发往服务器,服务器在数据库中检测该账号是否存在,如果存在则显示提示问题,如果不存在则提示账号不存在。用户填写完后续相关信息后,点击“重置密码”按钮后,如果找回密码答案正确则向服务器发送修改密码请求,如果失败则弹窗提示密码错误。最后服务器将处理结果(修改密码是否成功)返还给客户端。
    单独聊天(私聊)
    用户登录成功后,双击好友后,首先判断用户与该好友是否有聊天框存在,如果不存在则创建新的聊天框(ChatUI)并在ChatUIList中登记,如果存在则将改聊天框突出显示。用户可以再聊天页面(ChatUI)发送消息,如果好友不在线,服务器会返回好友不在线提示,客户端弹窗提示,如果好友在线收到消息则无提示(类似Linux,没有消息就是好消息)。好友收到消息时,在ChatUIList中查询是否有与该好友的聊天窗口,如果没有则新建窗口显示并在ChatUIList中注册,如果存在则直接将消息追加到聊天窗口上并突出显示。
    多人聊天(群聊)
    这里实现的多人聊天式世界喊话,即在线用户都能收到世界喊话的消息,没有好友限制,实现与单独聊天类似。不同的是,服务器收到“世界喊话”命令后,在SocketList中查询当前在线用户,并将世界喊话消息发送给这些用户。
    添加好友
    在好友列表页面左下角有添加好友按钮,点击该按钮后弹出添加好友框(AddFriendUI),在添加好友框中重复输入两次欲添加的好友name便可向服务器发送好友请求。当用户收到好友请求后,同意或拒绝都像添加方反馈,添加成功后重新登录便可刷新好友列表。
    修改密码
    在好友列表右下方有修改密码按钮,点击该按钮后弹出修改密码框(ChangePwdUI),在这里只需要重复输入两次新密码即可修改密码,是否修改成功服务器都会做出应答,客户端有弹窗提示。在服务端对数据库进行操作,由于可能数据库会出错,如果数据库未成功修改密码,那么要提醒客户。

    三 详细设计与实现3.1 开发环境此项目运行在Windows 10上,使用Eclipse作为IDE,用MySQL作为数据库。以Java为主要设计语言。
    3.2 系统总体结构设计如下图所示:

    3.3 系统流程逻辑设计如下图所示:

    3.4 数据库表设计数据库名为myqquser, 此数据库中若干个表,一个用户表(tb_user),用来存储用户的信息,如用户名,用户密码,用户问题,用户答案;每一个用户有一个好友表,好友表里存着用户的好友名。
    3.4.1 数据库构成此时数据库中有三个用户分别为inforSec、 zzz、sdust,tb_user中存放着用户的信息,inforSec_friends中存放着inforSec的好友信息,zzz_friends存放着zzz的好友信息,sdust_friends中存放着sdust的好友列表。

    3.4.2 用户表(tb_user)的结构及存放的数据
    3.4.3 某一用户好友表结果及数据
    3.5 客户端关键类与方法UI类
    MainFrame、FriendsUI、ChatUI、ChangeUI、AddFriendUI、RegeditUI、ForgetUI分别为主窗口页面、好友列表页面、聊天窗口、修改密码窗口、添加好友窗口、注册窗口、忘记密码窗口。这一部分主要是显示处理和逻辑处理。
    消息处理、通信类

    ChatUIList类主要记录客户端打开的聊天页面,处理与好友的消息弹窗Client类中有socket负责和服务器通信ChatTreat类是客户端的消息处理中心,处理来自服务器的各种消息并做出相应
    3.6 服务端关键类与方法UI类
    服务器端只有一个UI页面——StartServerFrame,因为服务器没有太多消息要显示,所以一个启动窗口即可。
    消息处理、通信类
    Service类中有socket,负责与客户端建立通信,每个建立的通信都存储在SocketList中,供服务器查询哪些用户上线。ServerThread类是服务器端的控制中心,负责处理来自用户端的消息,并转发给正确的用户,有时还会对数据库进行操作。
    数据库处理相关
    DBHelper类负责和数据库建立连接,UserService类负责处理具体的和数据库交互的内容,如查询用户账号和密码是否匹配、修改密码、注册用户、忘记密码、添加好友等操作。
    3.7 客户端关键代码3.7.1 主页面(MainFrame)public class MainFrame extends JFrame implements ActionListener, FocusListener { private static final long serialVersionUID = 1L; private static final String _txt_account = "QQ密码/手机/邮箱"; private static final String _txt_pwd = "密码"; private static final String _txt_title = "QQ登录"; private static final String _txt_registe = "注册"; private static final String _txt_forget = "忘记密码"; private static final String _txt_blank = ""; private JTextField account; private JPasswordField pwd; private JLabel upper_frame; private JPanel lower_frame, center_frame; private JButton login, register, forget; MainFrame() { //部分的形成 init(); //整体形成 add(upper_frame, BorderLayout.NORTH); add(center_frame, BorderLayout.CENTER); add(lower_frame, BorderLayout.SOUTH); ImageIcon logo = new ImageIcon("image/logo.jpg"); //左上方小图标 setIconImage(logo.getImage()); setBounds(500, 230, 430, 360); //设定大小及位置 setResizable(false); //登录框大小固定,不允许通过拖、拉改变大小 setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); //设置窗口右上角的叉号,点击叉号窗口关闭 注意不能EXIT_ON_CLOSE做参数的,用它时候使用的是System.exit方法退出应用程序。故会关闭所有窗口。 setTitle(_txt_title); setVisible(true); //说明数据模型已经构造好了,允许JVM可以根据数据模型执行paint方法开始画图并显示到屏幕上,一般放在最后一句 } public void init() { //账号输入块 account = new JTextField(_txt_account); account.setName("account"); account.setForeground(Color.gray); account.addFocusListener(this); //产生事件响应用户行为 //密码输入块 pwd = new JPasswordField(_txt_pwd); pwd.setName("pwd"); pwd.setForeground(Color.gray); pwd.setEchoChar('\0'); //启动后密码框内一定为 “密码” pwd.addFocusListener(this); //注册模块 register = new JButton(_txt_registe); register.setBorderPainted(false); register.setBorder(BorderFactory.createRaisedBevelBorder()); //斜面边框(凸) register.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); register.addActionListener(this); //忘记密码模块 forget = new JButton(_txt_forget); forget.setBorderPainted(false); forget.setBorder(BorderFactory.createRaisedBevelBorder()); forget.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); forget.addActionListener(this); //login按钮模块 login = new JButton(); ImageIcon login_image = new ImageIcon("image/login_image.png"); login_image.setImage(login_image.getImage().getScaledInstance(430, 35, Image.SCALE_DEFAULT)); login.setIcon(login_image); login.setBackground(Color.white); login.setBorderPainted(false); //如果进度条应该绘制边框,则为 true;否则为 false login.setBorder(null); //设置此组件的边框 无 login.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); //将光标设为 “小手”形状 login.addActionListener(this); //qq登录框架上半部分(无按钮之类的内容,只有一张图片) ImageIcon upper_image = new ImageIcon("image/upper_image.png"); upper_image.setImage(upper_image.getImage().getScaledInstance(430, 160, Image.SCALE_DEFAULT)); upper_frame = new JLabel(upper_image); //qq登录中间部分 (账号、密码、 注册、forget) center_frame = new JPanel(); center_frame.setName("center_frame"); center_frame.setLayout(null); center_frame.setLayout(new GridLayout(3, 3, 3, 15)); //这里用到3行3列的空间, 用空格填充 center_frame.add(new JLabel(_txt_blank, JLabel.CENTER)); center_frame.add(account); center_frame.add(new JLabel(_txt_blank, JLabel.CENTER)); center_frame.add(new JLabel(_txt_blank, JLabel.CENTER)); center_frame.add(pwd); center_frame.add(new JLabel(_txt_blank, JLabel.CENTER)); center_frame.add(register); center_frame.add(new JLabel(_txt_blank, JLabel.CENTER)); center_frame.add(forget); center_frame.setBackground(Color.white); //qq登录框架的下半部分login lower_frame = new JPanel(); lower_frame.setName("lower_frame"); lower_frame.setLayout(null); //lower_frame.setLayout(new GridLayout(1, 3, 3, 15)); lower_frame.setLayout(new GridLayout(0, 1)); lower_frame.add(login); } //按钮的点击事件用actionPerformed @Override public void actionPerformed(ActionEvent e){ /* * 1:如果点击了登录按钮 首先判断帐号或者密码是否为空 然后封装为CommandTranser对象 向服务器发送数据 服务器通过与数据库的比对 * 来验证帐号密码, * 2:如果点击了注册账号就弹出注册页面, 信息填写完整后连接服务器 * 3:如果点击了忘记密码弹出找回密码页面 */ //处理登录(login)页面 if(e.getSource() == login){ String user_name = account.getText().trim(); String user_pwd = new String(pwd.getPassword()).trim(); if("".equals(user_name) || user_name == null || _txt_account.equals(user_name)) { JOptionPane.showMessageDialog(null, "请输入帐号!!"); return; } if("".equals(user_pwd) || user_pwd == null || _txt_pwd.equals(user_pwd)) { JOptionPane.showMessageDialog(null, "请输入密码!!"); return; } User user = new User(user_name, user_pwd); CommandTranser cmd = new CommandTranser(); cmd.setCmd("login"); cmd.setData(user); cmd.setReceiver(user_name); cmd.setSender(user_name); //实例化客户端 连接服务器 发送数据 密码是否正确? Client client = new Client(); //创建唯一的客户端(用于接受服务器发来的消息, socket接口), client.sendData(cmd); //发送数据 cmd = client.getData(); //接受反馈的消息 if(cmd != null) { if(cmd.isFlag()) { this.dispose(); //关闭MainFrame页面 JOptionPane.showMessageDialog(null, "登陆成功"); user = (User)cmd.getData(); FriendsUI friendsUI = new FriendsUI(user, client); //将user的全部信息传到FriendsUI中,并将唯一与服务器交流的接口传到FriendUI中 这里传client仅为了发送消息 ChatTread thread = new ChatTread(client, user, friendsUI); //这里传client为了收消息, 整个客户端用一个 ChatTread,一个client thread.start(); }else { JOptionPane.showMessageDialog(this, cmd.getResult()); } } } //处理注册(register)页面 if(e.getSource() == register){ RegisterUI registerUI = new RegisterUI(this); // } //处理找回密码(forget)页面 if(e.getSource() == forget){ ForgetUI forgetUI = new ForgetUI(this); } } //鼠标的点击或移动之类的用focuslistener @Override public void focusGained(FocusEvent e) { //处理账号输入框 if(e.getSource() == account){ if(_txt_account.equals(account.getText())){ account.setText(""); account.setForeground(Color.BLACK); } } //处理密码输入框 if(e.getSource() == pwd){ if(_txt_pwd.equals(pwd.getText())){ pwd.setText(""); pwd.setEchoChar('*'); pwd.setForeground(Color.BLACK); } } } @Override public void focusLost(FocusEvent e) { //处理账号输入框 if(e.getSource() == account){ if("".equals(account.getText())){ account.setForeground(Color.gray); account.setText(_txt_account); } } //处理密码输入框 if(e.getSource() == pwd){ if("".equals(pwd.getText())){ pwd.setForeground(Color.gray); pwd.setText(_txt_pwd); pwd.setEchoChar('\0'); } } } public static void main(String[] args) { // TODO Auto-generated method stub MainFrame mainframe = new MainFrame(); }}
    3.7.2 消息处理核心——ChatTread类public class ChatTread extends Thread{ private Client client; private boolean isOnline = true; private User user; //如果同意好友请求, 则刷新好友列表 private FriendsUI friendsUI; //刷新好友列表用 private String username; //如果创建新的聊天窗口(chatUI)那么必须将username传进去 用来发送消息 public ChatTread(Client client, User user, FriendsUI friendsUI) { this.client = client; this.user = user; this.friendsUI = friendsUI; this.username = user.getUsername(); //this.chat_windows = chat_windows; } public boolean isOnline() { return isOnline; } public void setOnline(boolean isOnline) { this.isOnline = true; } //run()方法是不需要用户来调用的,当通过start方法启动一个线程之后,当线程获得了CPU执行时间, //便进入run方法体去执行具体的任务。注意,继承Thread类必须重写run方法,在run方法中定义具体要执行的任务 @Override public void run() { if(!isOnline) { JOptionPane.showMessageDialog(null, "unbelievable !!!"); return; } while(isOnline) { CommandTranser cmd = client.getData(); //与服务器端相同处理接收到的消息(命令) //这里处理来自服务器的消息(命令) if(cmd != null) { execute(cmd); //System.out.println(cmd.getCmd()); } } } //处理消息(命令) private void execute(CommandTranser cmd) { //登录、忘记密码、注册消息未在此处处理 System.out.println(cmd.getCmd()); //聊天消息请求 if("message".equals(cmd.getCmd())) { if(cmd.isFlag() == false) { JOptionPane.showMessageDialog(null, cmd.getResult()); return; } //查询是否有与该好友的窗口该窗口 String friendname = cmd.getSender(); ChatUI chatUI = ChatUIList.getChatUI(friendname); if(chatUI == null) { chatUI = new ChatUI(username, friendname, username, client); ChatUIEntity chatUIEntity = new ChatUIEntity(); chatUIEntity.setName(friendname); chatUIEntity.setChatUI(chatUI); ChatUIList.addChatUI(chatUIEntity); } else { chatUI.show(); //如果以前创建过仅被别的窗口掩盖了 就重新显示 } Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat( "yy-MM-dd hh:mm:ss a"); String message = friendname + "说:" + (String) cmd.getData() + "\t" + sdf.format(date) + "\n"; chatUI.getChatWin().append(message); //追加消息 return; } if("WorldChat".equals(cmd.getCmd())) { //查询是否有与该好友的窗口该窗口 String friendname = cmd.getSender(); ChatUI chatUI = ChatUIList.getChatUI("WorldChat"); if(chatUI == null) { chatUI = new ChatUI("WorldChat", "WorldChat", user.getUsername(), client); ChatUIEntity chatUIEntity = new ChatUIEntity(); chatUIEntity.setName("WorldChat"); chatUIEntity.setChatUI(chatUI); ChatUIList.addChatUI(chatUIEntity); } else { chatUI.show(); //如果以前创建过仅被别的窗口掩盖了 就重新显示 } Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat( "yy-MM-dd hh:mm:ss a"); String message = friendname + "说:" + (String) cmd.getData() + "\t" + sdf.format(date) + "\n"; chatUI.getChatWin().append(message); //追加消息 return; } if("requeste_add_friend".equals(cmd.getCmd())) { if(cmd.isFlag() == false) { JOptionPane.showMessageDialog(null, cmd.getResult()); return; } String sendername = cmd.getSender(); int flag = JOptionPane.showConfirmDialog(null, "是否同意" + sendername + "的好友请求", "好友请求", JOptionPane.YES_NO_OPTION); System.out.println(flag); if(flag == 0) { cmd.setCmd("accept_add_friend"); } else { cmd.setCmd("refuse_add_friend"); } cmd.setSender(username); cmd.setReceiver(sendername); client.sendData(cmd); return; } if("accept_add_friend".equals(cmd.getCmd())) { JOptionPane.showMessageDialog(null, cmd.getResult()); return; } if("refuse_to_add".equals(cmd.getCmd())) { JOptionPane.showMessageDialog(null, cmd.getResult()); return; } if("changepwd".equals(cmd.getCmd())) { JOptionPane.showMessageDialog(null, cmd.getResult()); return; } return; }}
    3.7.3 负责通信——Client 类public class Client { private int port = 2222; private String Sever_address = "127.0.0.1"; //服务器主机ip private Socket socket; //实例化, 建立连接 public Client(){ try { socket = new Socket(Sever_address, port); } catch(UnknownHostException e) { JOptionPane.showMessageDialog(null, "服务器端未开启"); }catch(IOException e) { JOptionPane.showMessageDialog(null, "服务器端未开启"); } } public Socket getSocket() { return socket; } public void setSocket(Socket socket) { this.socket = socket; } //向服务端发送数据 public void sendData(CommandTranser cmd) { ObjectOutputStream oos = null; //主要的作用是用于写入对象信息与读取对象信息。 对象信息一旦写到文件上那么对象的信息就可以做到持久化了 try { if(socket == null) { return; } oos = new ObjectOutputStream(socket.getOutputStream()); oos.writeObject(cmd); } catch(UnknownHostException e) { JOptionPane.showMessageDialog(null, "服务器端未开启"); }catch(IOException e) { JOptionPane.showMessageDialog(null, "服务器端未开启"); } } //接受服务端发送的消息 public CommandTranser getData() { ObjectInputStream ois = null; CommandTranser cmd = null; if(socket == null) { //System.out.println("weishenme"); return null; } try { ois = new ObjectInputStream(socket.getInputStream()); cmd = (CommandTranser) ois.readObject(); } catch (IOException e) { return null; } catch (ClassNotFoundException e) { return null; } return cmd; }}
    3.8 服务端关键代码3.8.1 CommandTranser类——用于命令传递public class CommandTranser implements Serializable { private static final long serialVersionUID = 1L; private String sender = null;// 发送者 private String receiver = null;// 接受者 private Object data = null; // 传递的数据 private boolean flag = false; // 指令的处理结果 private String cmd = null; // 服务端要做的指令 private String result = null; //处理结果 public String getSender() { return sender; } public String setSender(String sender) { return this.sender = sender; } public String getReceiver() { return receiver; } public String setReceiver(String receiver) { return this.receiver = receiver; } public Object getData() { return data; } public Object setData(Object data) { return this.data = data; } public boolean isFlag() { return flag; } public boolean setFlag(boolean flag) { return this.flag = flag; } public String getResult() { return result; } public String setResult(String result) { return this.result = result; } public String getCmd() { return cmd; } public String setCmd(String cmd) { return this.cmd = cmd; }}
    3.8.2 ServerThread类——服务器端的消息控制中心public class ServerThread extends Thread{ private Socket socket; public ServerThread(Socket socket) { this.socket = socket; } @Override public void run() { ObjectInputStream ois = null; ObjectOutputStream oos1 = null; ObjectOutputStream oos2 = null; //ObjectOutputStream oos3 = null; while(socket != null) { try { ois = new ObjectInputStream(socket.getInputStream()); CommandTranser cmd = (CommandTranser) ois.readObject(); //执行命令来自客户端的请求 cmd = execute(cmd); //消息对话请求,服务器将sender发来的消息发送给receiver if("message".equals(cmd.getCmd())) { //如果 msg.ifFlag即 服务器处理成功 可以向朋友发送信息 如果服务器处理信息失败 信息发送给发送者本人 if(cmd.isFlag()) { //System.out.println("对方在线"); oos1 = new ObjectOutputStream(SocketList.getSocket(cmd.getReceiver()).getOutputStream()); } else { //System.out.println("对方未在线"); oos2 = new ObjectOutputStream(socket.getOutputStream()); } } if ("WorldChat".equals(cmd.getCmd())) { HashMap<String, Socket> map = SocketList.getMap(); Iterator<Map.Entry<String, Socket>> it = map.entrySet().iterator(); while(it.hasNext()) { Map.Entry<String, Socket> entry = it.next(); if(!entry.getKey().equals(cmd.getSender())) { oos1 = new ObjectOutputStream(entry.getValue().getOutputStream()); oos1.writeObject(cmd); } } continue; } //登录请求 将数据发送给sender if ("login".equals(cmd.getCmd())) { oos1 = new ObjectOutputStream(socket.getOutputStream()); } //注册请求 将数据发送给sender if ("register".equals(cmd.getCmd())) { System.out.println("向客户端发送消息"); oos1 = new ObjectOutputStream(socket.getOutputStream()); } //添加好友请求将数据发送给 receiver if ("requeste_add_friend".equals(cmd.getCmd())) { //在线,将请求发给receiver if(cmd.isFlag()) { oos1 = new ObjectOutputStream(SocketList.getSocket(cmd.getReceiver()).getOutputStream()); } else { //不管在不在线都要向发送方提示消息发送成功 oos2 = new ObjectOutputStream(socket.getOutputStream()); } } //同意添加好友请求将数据发送给 receiver和sender if ("accept_add_friend".equals(cmd.getCmd())) { //无论是否成功插入数据库都要将结果反馈,但有可能最初请求的客户下线了 oos1 = new ObjectOutputStream(socket.getOutputStream()); if(SocketList.getSocket(cmd.getReceiver()) != null) { oos2 = new ObjectOutputStream(SocketList.getSocket(cmd.getReceiver()).getOutputStream()); } } //拒绝添加好友请求将数据发送给 receiver if ("refuse_to_add".equals(cmd.getCmd())) { //被拒绝方在线 if(cmd.isFlag()) { oos1 = new ObjectOutputStream(SocketList.getSocket(cmd.getReceiver()).getOutputStream()); }else { //被拒方不在线则向拒绝方发送消息 oos2 = new ObjectOutputStream(socket.getOutputStream()); } } //修改资料请求 发送给sender 功能暂未实现 if ("changeinfo".equals(cmd.getCmd())) { oos1 = new ObjectOutputStream(socket.getOutputStream()); } //修改密码请求 将数据发送给sender if ("changepwd".equals(cmd.getCmd())) { oos1 = new ObjectOutputStream(socket.getOutputStream()); } //忘记密码 发送给sender if ("forgetpwd".equals(cmd.getCmd())) { oos1 = new ObjectOutputStream(socket.getOutputStream()); } //用户下线 if("logout".equals(cmd.getCmd())) { // } if(oos1 != null) { oos1.writeObject(cmd); } if(oos2 != null) { oos2.writeObject(cmd); } } catch(IOException e) { socket = null; } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } //处理客户端发来的命令 private CommandTranser execute(CommandTranser cmd) { //登录请求 if("login".equals(cmd.getCmd())) { UserService userservice = new UserService(); User user = (User)cmd.getData(); cmd.setFlag(userservice.checkUser(user)); //如果登陆成功,将该客户端加入已经连接成功的map集合里面 并且开启此用户的接受线程 if(cmd.isFlag()) { // 将该线程加入连接成功的map集合 SocketEntity socketEntity = new SocketEntity(); socketEntity.setName(cmd.getSender()); socketEntity.setSocket(socket); SocketList.addSocket(socketEntity); //从数据库获取其好友列表并将其好友列表发送至客户端 cmd.setData(userservice.getFriendsList(user)); cmd.setResult("登陆成功"); } else { cmd.setResult("密码错误"); } } //注册请求 if("register".equals(cmd.getCmd())) { UserService userservice = new UserService(); User user = (User)cmd.getData(); cmd.setFlag(userservice.registerUser(user)); //如果注册成功 if(cmd.isFlag()) { SocketEntity socketEntity = new SocketEntity(); socketEntity.setName(cmd.getSender()); socketEntity.setSocket(socket); SocketList.addSocket(socketEntity); cmd.setData(userservice.getFriendsList(user)); //刚注册的肯定没有好友 cmd.setResult("注册成功"); } else { cmd.setResult("注册失败可能该用户已存在"); } } //修改资料请求 功能暂未实现 if("changeInfo".equals(cmd.getCmd())) { UserService userservice = new UserService(); User user = (User)cmd.getData(); cmd.setFlag(userservice.changeInfo(user)); if(cmd.isFlag()) { cmd.setResult("修改信息成功"); } else { cmd.setResult("修改信息失败"); } } //添加好友 if("requeste_add_friend".equals(cmd.getCmd())) { //检查用户是否在线 if(SocketList.getSocket(cmd.getReceiver()) != null) { cmd.setFlag(true); cmd.setResult("对方收到了您的好友请求"); } else { cmd.setFlag(false); cmd.setResult("当前用户不在线或者改用户不存在"); } } //同意添加好友请求 if("accept_add_friend".equals(cmd.getCmd())) { UserService userservice = new UserService(); cmd.setFlag(userservice.addFriend(cmd.getReceiver(), cmd.getSender())); if(cmd.isFlag()) { cmd.setResult("好友添加成功请重新登陆刷新"); } else { cmd.setResult("服务器故障导致添加好友失败或者您们已经为好友"); } } //拒绝添加好友 if("refuse_to_add".equals(cmd.getCmd())) { //检查是否在线 if(SocketList.getSocket(cmd.getReceiver()) != null) { cmd.setFlag(true); cmd.setResult("您被 " + cmd.getSender() + " 拒绝了"); } else { cmd.setFlag(false); cmd.setResult("对方不在线不知道你拒绝了他的好友请求"); } } //发送消息指令 if("message".equals(cmd.getCmd())) { //检查是否在线 if(SocketList.getSocket(cmd.getReceiver()) != null) { //System.out.println("神奇"); cmd.setFlag(true); //cmd.setResult("对方成功收到您的消息"); } else { //System.out.println("神奇啊"); cmd.setFlag(false); cmd.setResult("当前用户不在线"); } } if("WordChat".equals(cmd.getCmd())) { cmd.setFlag(true); } //忘记密码指令 这里最后要讲用户的问题和答案返回 if("forgetpwd".equals(cmd.getCmd())) { UserService userservice = new UserService(); User user = (User)cmd.getData(); user = userservice.getUser(user); //如果用户存在 if(user != null ) { cmd.setResult("查询成功"); cmd.setData(user); cmd.setFlag(true); } else { cmd.setResult("用户可能不存在"); cmd.setFlag(false); } } if ("changepwd".equals(cmd.getCmd())) { UserService userservice = new UserService(); User user = (User)cmd.getData(); cmd.setFlag(userservice.changePassword(user)); System.out.println("there 111 "); System.out.println(user.getUsername()); if(cmd.isFlag()) { cmd.setResult("修改密码成功"); }else { cmd.setResult("修改密码失败"); } } if("logout".equals(cmd.getCmd())) { SocketList.getSocket(cmd.getSender()); } return cmd; }}
    3.8.3 DBHelper类——连接数据库public class DBHelper { private static final String driver = "com.mysql.cj.jdbc.Driver"; private static final String url = "jdbc:mysql://localhost:3306/myqquser?&useSSL=false&serverTimezone=UTC"; private static final String username = "root"; private static final String password = "zzzz"; private static Connection con = null; //静态代码负责加载驱动 static { try { Class.forName(driver); //Class.forName(xxx.xx.xx)的作用是要求JVM查找并加载指定的类,也就是说JVM会执行该类的静态代码段 } catch(ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() { if(con == null) { try { con = DriverManager.getConnection(url, username, password); } catch(SQLException e) { e.printStackTrace(); } } return con; }}
    3.8.4 UserService类——对数据库进行增删查改public class UserService { //login验证账号密码 public boolean checkUser(User user) { PreparedStatement stmt = null; //PreparedStatement是用来执行SQL查询语句的API之一 Connection conn = null; //与特定数据库的连接(会话)。在连接上下文中执行 SQL 语句并返回结果 ResultSet rs = null; //是数据中查询结果返回的一种对象,可以说结果集是一个存储查询结果的对象,但是结果集并不仅仅具有存储的功能,他同时还具有操纵数据的功能,可能完成对数据的更新等 conn = DBHelper.getConnection(); String sql = "select * from tb_user where user_name =? and user_pwd =?"; try { stmt = conn.prepareStatement(sql); stmt.setString(1, user.getUsername()); stmt.setString(2, user.getUserpwd()); rs = stmt.executeQuery(); if(rs.next()) { return true; } } catch(SQLException e) { e.printStackTrace(); } finally{ try { if(rs != null) { rs.close(); } if(stmt != null) { stmt.close(); } } catch(SQLException e) { e.printStackTrace(); } } return false; } //登陆后向客户端发送其好友列表 public User getFriendsList(User user) { PreparedStatement stmt = null; Connection conn = null; ResultSet rs = null; conn = DBHelper.getConnection(); String sql = "select * from " + user.getUsername() + "_friends"; ArrayList<String> friendslist = new ArrayList<String>(); //这里假设好友不超过20个 try { stmt = conn.prepareStatement(sql); //stmt.setString(1, user.getUsername() + "_friends"); 这样的话会报错 rs = stmt.executeQuery(); int count = 0; while(rs.next()) { friendslist.add(rs.getString(2)); //获取好友name count++; } user.setFriendsNum(count); user.setFriendsList(friendslist); return user; } catch(SQLException e) { e.printStackTrace(); } finally{ try { if(rs != null) { rs.close(); } if(stmt != null) { stmt.close(); } } catch(SQLException e) { e.printStackTrace(); } } return user; } //用户注册 public boolean registerUser(User user) { PreparedStatement stmt1 = null; //PreparedStatement是用来执行SQL查询语句的API之一 PreparedStatement stmt2 = null; PreparedStatement stmt3 = null; Connection conn = null; ResultSet rs = null; int insertFlag = 0; int creatFlag = 0; conn = DBHelper.getConnection(); String sql = "select * from tb_user where user_name =?"; String insertusersql = "insert into tb_user (user_name, user_pwd, user_question, user_ans) values(?, ?, ?, ?)"; String creatfriendstabsql = "CREATE TABLE " + user.getUsername() + "_friends " + "(id INT NOT NULL AUTO_INCREMENT, name VARCHAR(45) NOT NULL, PRIMARY KEY (id))"; try { stmt1 = conn.prepareStatement(sql); stmt1.setString(1, user.getUsername()); rs = stmt1.executeQuery(); if(rs.next()) { System.out.println("该用户已存在" + user.getUsername() + "***"); //用户已被注册 return false; } else { System.out.println("该用户不存在" + user.getUsername() + "***"); //向用户表插入数据 stmt2 = conn.prepareStatement(insertusersql); stmt2.setString(1, user.getUsername()); stmt2.setString(2, user.getUserpwd()); stmt2.setString(3, user.getUserQuestion()); stmt2.setString(4, user.getUserAnswer()); insertFlag = stmt2.executeUpdate(); System.out.println("向表中插入数据" + user.getUsername() + "***" + insertFlag); //创建好友表 stmt3 = conn.prepareStatement(creatfriendstabsql); creatFlag = stmt3.executeUpdate(); System.out.println("创建表" + user.getUsername() + "***" + creatFlag); if(insertFlag == 1) { return true; } System.out.println("不高兴" + user.getUsername() + "***"); //return true; } } catch(SQLException e) { e.printStackTrace(); } finally{ try { if(rs != null) { rs.close(); } if(stmt1 != null) { stmt1.close(); } if(stmt2 != null) { stmt2.close(); } if(stmt3 != null) { stmt3.close(); } } catch(SQLException e) { e.printStackTrace(); } } return false; } //添加好友 public boolean addFriend(String sender, String receiver) { PreparedStatement stmt1 = null; PreparedStatement stmt2 = null; Connection conn = null; int updateResult1 = 0; int updateResult2 = 0; conn = DBHelper.getConnection(); String sql1 = "insert into " + sender + "_friends (name) values(?)"; //String sql1 = "insert into ? (name) values(?)"; String sql2 = "insert into " + receiver + "_friends (name) values(?)"; //String sql2 = "insert into ? (name) values(?)"; try { stmt1 = conn.prepareStatement(sql1); stmt2 = conn.prepareStatement(sql2); stmt1.setString(1, receiver); stmt2.setString(1, sender); updateResult1 = stmt1.executeUpdate(); updateResult2 = stmt2.executeUpdate(); if(updateResult1 == 1 && updateResult2 == 1) { return true; } else { // 如果插入不成功的话,应该将插入成功的删除....这里不做处理了 } } catch(SQLException e) { e.printStackTrace(); } finally{ try { if(stmt1 != null) { stmt1.close(); } if(stmt2 != null) { stmt2.close(); } } catch(SQLException e) { e.printStackTrace(); } } return false; } //修改信息 public boolean changeInfo(User user) { return false; } //修改密码 忘记密码 public boolean changePassword(User user) { PreparedStatement stmt1 = null; //PreparedStatement是用来执行SQL查询语句的API之一 PreparedStatement stmt2 = null; //PreparedStatement是用来执行SQL查询语句的API之一 Connection conn = null; //与特定数据库的连接(会话)。在连接上下文中执行 SQL 语句并返回结果 ResultSet rs = null; //是数据中查询结果返回的一种对象,可以说结果集是一个存储查询结果的对象,但是结果集并不仅仅具有存储的功能,他同时还具有操纵数据的功能,可能完成对数据的更新等 int updateFlag = 0; conn = DBHelper.getConnection(); //String sql = "select * from tb_user where user_question =? and user_ans =?"; String updatesql = "update tb_user set user_pwd =? where user_name = ?"; try { stmt2 = conn.prepareStatement(updatesql); stmt2.setString(1, user.getUserpwd()); stmt2.setString(2, user.getUsername()); updateFlag = stmt2.executeUpdate(); if(updateFlag == 1) return true; //} } catch(SQLException e) { e.printStackTrace(); } finally{ try { if(rs != null) { rs.close(); } if(stmt1 != null) { stmt1.close(); } if(stmt2 != null) { stmt2.close(); } } catch(SQLException e) { e.printStackTrace(); } } return false; } //获得用户的相关信息 public User getUser(User user) { PreparedStatement stmt1 = null; PreparedStatement stmt2 = null; Connection conn = null; ResultSet rs = null; conn = DBHelper.getConnection(); String sql = "select * from tb_user where user_name =?"; try { stmt1 = conn.prepareStatement(sql); stmt1.setString(1, user.getUsername()); rs = stmt1.executeQuery(); if(rs.next()) { user.setUsername(rs.getString("user_name")); user.setUserAnswer(rs.getString("user_ans")); user.setUserQuestion(rs.getString("user_question")); return user; } } catch(SQLException e) { e.printStackTrace(); } finally{ try { if(rs != null) { rs.close(); } if(stmt1 != null) { stmt1.close(); } if(stmt2 != null) { stmt2.close(); } } catch(SQLException e) { e.printStackTrace(); } } return null; }}
    四 运行测试注册界面如下:



    登录界面如下:

    修改密码界面如下:

    私聊、群聊界面如下:
    2 评论 213 下载 2018-10-29 15:12:06 下载需要6点积分
  • 基于C#和Access的智能聊天机器人

    一、软件说明1.1 功能说明一个可以自动回复的聊天机器人.
    1.2 解决什么样的实际问题用于娱乐,解闷。
    1.3 性能说明软件还存在一定的BUG,有待改进。
    1.4 程序类型说明娱乐性应用程序。
    二、软件设计2.1 软件整体结构图及模块划分
    软件的结构只有一个聊天界面,分别有输入框,显示聊天信息框,发送按钮和退出按钮,导入词库按钮和调教按钮。
    2.2 在VS的“解决方案资源管理器”中的文件说明
    解决方案中,有5个类,其中:

    ”聊天机器人”类是主类,窗口显示类
    ”DataBase”是连接数据的类,本程序用到的是access数据库
    “Iostreams”类是文件操作类,主要是读取一个txt文件,用于导入词库
    ”Regugar”类是正则表达式品配类,用于品配聊天的问题
    ”Robot”类是一个机器人对象

    Resources文件是用于存放图片文件。
    2.3 各模块对应的主要对象和方法2.3.1 ”聊天机器人”类主要属性
    private Robot A; //机器人对象private string Ask; //发送信息private string Answer; //返回信息private int isTeaching; //调教模式private Point mouseOff; //鼠标移动位置变量private bool leftFlag; //标签是否为左键private string FileName; //txt文件路径private Thread T1; //创建一个线程
    主要方法
    /////发送按扭private void button_sent_Click(object sender, EventArgs e){ ///正常聊天状态 if (isTeaching == 0) { Ask = this.richTextBox_sent.Text; //设置为右对齐 this.richTextBox1_accept.SelectionAlignment = HorizontalAlignment.Right; this.richTextBox1_accept.AppendText(Ask + ":主人 \n"); Answer = A.answer(Ask); //设置为左对齐 this.richTextBox1_accept.SelectionAlignment = HorizontalAlignment.Left; this.richTextBox1_accept.AppendText(" "+ A.name + ":" + Answer + "\n"); } ///////调教机器人状态 else if (isTeaching == 1) { Ask = this.richTextBox_sent.Text; this.richTextBox1_accept.SelectionAlignment = HorizontalAlignment.Right; this.richTextBox1_accept.AppendText(Ask + ":主人 \n"); isTeaching = 2; } else { Answer = this.richTextBox_sent.Text; this.richTextBox1_accept.SelectionAlignment = HorizontalAlignment.Left; this.richTextBox1_accept.AppendText(" "+A.name+":" + Answer + "\n"); A.learn(Ask,Answer); isTeaching = 1; } //自动滚动到插入位置 this.richTextBox1_accept.ScrollToCaret(); //清空发送框 this.richTextBox_sent.Clear();}///导入词库private void Openfile(){ //导入词库 Iostreams.Import(FileName); T1.Abort();}
    2.3.2 ”DataBase”类//根据接收到的字符串返回数据public static List<conversation> search(string sql){ List<conversation> answer = new List<conversation>(); conversation con = null; OleDbConnection conn = new OleDbConnection(@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|ChatRobot.mdb"); ///连接数据库 conn.Open(); //执行sql查询 OleDbCommand com = new OleDbCommand(sql, conn); OleDbDataReader reader = com.ExecuteReader(); //读取查询结果 while (reader.Read()) { con = new conversation(reader.GetString(0), reader.GetString(1)); answer.Add(con); } conn.Close(); return answer;}
    2.3.3 “Iostreams”类public static void Import(string File_Name){ using (StreamReader sr = new StreamReader(File_Name, Encoding.Default)) { string line1,line2; //按行读取 while((line1=sr.ReadLine())!= null) { line2 = sr.ReadLine(); DataBase.Insert(line1, line2); sr.ReadLine(); } }}
    2.3.4 ”Regugar”类//正则表达式品配public static bool match(string a, string ask){ //简单的正则表达式 Regex re = new Regex(@"[\s\S]*" + a + @"[\s\S]*"); //品配成功则返回true,否则返回false return re.IsMatch(ask);}
    2.3.5 ”Robot”类//问答函数public string answer(string ask){ List<conversation> answers = new List<conversation>(); string answer=null,a=null; ///sql语句品所有字符的模糊搜索 a = "SELECT ask,answer FROM [chatwords] WHERE (ask LIKE'%[" + ask + "]%') ORDER BY ask DESC"; answers = DataBase.search(a); if (answers.Count != 0) { for (int i = 0; i < answers.Count; i++) { if (Regular.match(answers[i].ask,ask )) return answers[i].answer; } } answer = "听不懂"; //返回包括有输入的任一字的ask和answer return answer;}
    2.4 数据库设计或数据文件说明数据库设计,只创建一个chatwords表,用于储存ask语句和answer语句。

    数据文件.txt文件。问和答不空行,每个问答之间要空一行,必须要按这样的格式才能正确导入词库。

    三、测试测试用例列表地和软件运行截图

    智能聊天机器人的词库是存在数据库中的,当我输入信息如”你好”,点击发送按钮后,程序获取输入信息,然后通过输入信息把据库中包含有输入信息的任一字符的”ask”的所有问答数据找出来,再通过正则表达式,反过来,用查找到的”ask”品配输入信息,一旦品配成功,则返回对应的answer。

    通过penFileDialog按键,打开对话框,默认为打开txt文件。
    四、附录机器人的词库是用酷Q免费提供的词库。
    主要用到的控件有:

    richTextBox
    pictureBox
    Botton
    Label等等
    1 评论 2 下载 2019-10-20 11:44:53 下载需要15点积分
  • 基于深度学习的医疗图像预分类平台开发与实现

    摘 要本文课题设计是基于深度学习中支持向量机算法的医疗图像预分类平台的开发与实现。现阶段,医疗图像中病情的确认都由医生进行判定,从效率方面来讲,人工判断的效率远远小于使用机器学习判断医疗图像中病情的效率,因此,本文课题设计旨在设计实现算法将医疗图像进行分类,产生新的符合机器进行判断的医疗图像。配合使用深度学习中的卷积神经网络经过训练对医疗图像进行一定程度的解读,用来辅助或代替医生诊断病情。使用机器进行病情的判断可以辅助医生判断病情,大大减少医生的工作量,解放生产力。
    在实现分类医疗图像的算法过程中,核心是机器学习中的支持向量机算法,开发环境是python环境。由于dicom格式是医疗图像标准格式,因此实现算法的第一步是对dicom格式医疗图像文件进行读取解析,使用python中的第三方模块pydicom模块进行读取解析。读取文件之后需要将数据进行一系列操作转换成可用于分类器分类的格式,本文课题设计中使用opencv模块对数据进行处理,首先进行图像的截取,之后进行灰度化处理、二值化处理,将格式转化为可用于分类的格式。紧接着进行核心操作,即使用分类器进行分类,使用python第三方模块中的libsvm模块对训练集数据进行训练,训练出分类器模型,之后使用训练好的模型对数据进行预测,得出预测的结果。将图像数据分类后,需要进行判断,将正确的分类的图像数据保存到固定的位置,生成新的png格式图像以便于后续的再次使用,这将生产最后的结果,使用python第三方模块中的opencv模块。
    本文课题设计的最终结果将会产生新的png格式的医疗图像,生成的新医疗图像是直接截取原始图像的病情部位,去除大量无用信息,实现原始dicom格式医疗图像的有用信息分类转化为易于操作的png格式图像,生成的新医疗图像将被应用到卷积神经网络解读医疗图像病情方面,最终实现病情解读判断效率的大幅度提升,解放生产力。最后会对对程序进行展开分析,展示了程序的设计思路、功能流程及其实现的逻辑,结合程序的源码部分展开描述。
    关键词:深度学习,支持向量机,dicom,pydicom,libsvm
    ABSTRACTThe design of this paper is based on the development and implementation of medical image pre-classification platform based on support vector machine algorithm in depth learning. At present, the confirmation of the condition in the medical image is determined by the doctor. In terms of efficiency, the efficiency of manual judgment is far less than that of using the machine to judge the condition of the medical image. Therefore, the purpose of this paper is to design and implement the algorithm. Medical images are classified, resulting in new medical images that meet the machine’s judgment. Conjugate the use of deep learning in the convolution of the neural network through training to conduct a certain degree of interpretation of medical images to assist or replace the doctor to diagnose the condition. The use of the machine to determine the condition can help the doctor to determine the condition, greatly reducing the work load of doctors, the liberation of productivity.
    In the process of implementing the classification of medical images, the core is the support vector machine algorithm in machine learning, and the development environment is python environment. Since the dicom format is the standard format for medical images, the first step in implementing the algorithm is to read the dicom format medical image file and use the third-party module pydicom module in python for reading and parsing. After reading the file, you need to convert the data into a series of operations that can be used in the classification of the classifier. In this paper, the opencv module is used to process the data. The image is taken first, then the gray scale is processed and binarized , The format into a format can be used for classification.Followed by the core operation, that is, using the classifier to classify, use the libsvm module in the python third party module to train the training set data, train the classifier model, and then use the trained model to predict the data, result. After sorting the image data, it is necessary to make a judgment,save the correct sorted image data to a fixed location, generate a new png format image for subsequent re-use, which will produce the final result using python in the third party module Opencv module.
    The final result of the design of this topic will produce a new png-style medical image, the new medical image generated is a direct removal of the original image of the disease site, remove a large number of useless information to achieve the original dicom format medical image useful information into easy to operate The png format image, the generated new medical image will be applied to the convolution of the neural network to read the medical image of the disease, the ultimate realization of disease interpretation to determine the efficiency of a substantial increase in the liberation of productivity.Finally, the program will be carried out on the analysis, showing the program design ideas, functional processes and the realization of the logic, combined with the source part of the program to start description.
    Key words:deep learning, support vector machine, dicom, pydicom, libsvm
    1 绪论本章旨在说明基于深度学习的医疗图像预分类平台的研究目的、研究背景,以及发展现状和对该平台的研究意义。
    1.1 基于深度学习的医疗图像预分类平台未来的时代是人工智能的时代。人工智能的核心是机器学习,更确切的说,深度学习是人工智能的灵魂。所以研究深度学习和机器学习是有意义的。在互联网时代与大数据时代,机器学习是一门难以避开的学科,任何通过数据进行训练的学习型算法研究都属于机器学习。机器学习涉及许多学科,如概率论、统计学等。机器学习研究的是机器怎样模拟人类,并实现人类的学习行为。机器学习是人工智能的核心,计算机的智能化确切来说离不开机器学习方面的内容。机器学习算法种类较多,适用于不同的场景,例如线性回归、K均值(K-means,基于原型的目标函数聚类方法)、决策树、随机森林、支持向量机(SVM)以及人工神经网络(ANN)等一些算法。
    本次课题设计是开发基于深度学习的医疗图像预分类平台。医疗图像病情的解读从技术角度来讲是可以用机器识别辅助或者代替人工识别,现阶段深度学习中的卷积神经网络技术可以很好的完成这一点,而使用卷积神经网络进行识别需要对图像数据进行一定的格式转化、分类及去噪处理。医疗图像预分类平台就是基于该信息点出发,对原始医疗图像进行去噪处理、分类器分类,产生新的医疗图像。而DICOM标准是医疗图像的规范,因此首先是对dicom格式医疗图像进行读取解析,该平台的核心点在于分类器的分类,由于支持向量机算法较为符合该平台对于分类的要求,因此分类器使用的是机器学习中的支持向量机算法作为分类器进行图像分类。
    1.2 研究意义本文课题设计是研究基于深度学习的医疗图像预分类平台,首先来分析医疗图像预分类的好处。自1985年DICOM(医学数字成像和通信)标准发布以来,被广泛用于放射医疗,心血管成像以及放射诊疗诊断设备,在我国DICOM是唯一被接受的医疗影响国际规范。而解读医疗图像在诊断时期很重要,病变基本都发生在体内,现阶段只能通过X射线、CT、核磁共振、超声等方式获取的扫描图像来判断病情。
    现阶段,医学图像都由医生来进行解译,这需要医生具有一定的经验才能进行。使用计算机读取DICOM格式[6]图片数据,可以将图片进行截取等操作,并对截取后的图像数据通过机器学习中的支持向量机算法处理,由于医疗图片是属于线性不可分的,所以使用支持向量机的方法。支持向量机遇到线性不可分问题时,通过一个非线性映射p,将样本空间映射到一个高维乃至无穷维的特征空间中,也就是Hilber空间,使得原本的样本空间中的非线性可分的问题转化为特征空间的线性可分的问题。一般情况下,升维会增加计算的复杂性,甚至会引起“维数灾难”[2],支持向量机应用核函数[9]的展开定理,引入松弛变量[8],就不需要知道非线性映射的显式表达式,使得不增加计算的复杂性,在某种程度上避免了“维数灾难”。使用支持向量机算法作为分类器对截取后的图像进行分类,对正确分类的图像进行保存,产生新的png格式医疗图像,从而适用于深度学习进行处理。深度学习中的卷积神经网络经过训练可以对新生成的医疗图像进行一定程度的解读,用来辅助或代替医生诊断病情。
    深度学习的方法在处理医学影像数据方面发展迅速,在可见的将来,随着万物互联而来的海量医疗数据甚至可以让深度学习中的卷积神经网络代替医生进行诊断。另外,本次课题设计的研究能够提高自己的学习能力,在发现问题、分析问题和解决问题方面能够有一定深层次的了解。同时,对现今仍然火热的机器学习的知识了解的更进一步,对模型的建立、算法的实现方面有了更深的掌握。
    1.3 论文结构在本篇基于深度学习的医疗图像预分类平台的论文中,第一章说明了医疗图像预分类平台是什么以及研究意义,第二章对图像预分类平台分析需求和可行性,第三章说明了医疗图像预分类平台中支持向量机的应用,第四章说明了医疗图像预分类平台开发的设计,第五章介绍了医疗图像预分类平台开发测试和调试。
    2 医疗图像预分类平台需求分析本章旨在说明开发医疗图像预分类平台的需求,并从经济、技术、运行、操作方面进行可行性分析。
    2.1 医疗图像预分类平台的需求分析本文课题设计是基于深度学习的医疗图像预分类平台和实现,该平台是对医疗图像进行预分类,而dicom格式在我国是唯一被接受的医疗国际规范,研究的基础数据就是dicom格式医疗图像,因此首先要做的就是对dicom格式医疗图像文件的读取。在功能、性能及系统环境方面有一定的要求:
    功能需求:在上述内容中,首先要有批量导入和读取DICOM格式的图片信息功能,在这方面需要使用python中的pydicom第三方模块对dicom格式图像进行读取分析。之后需要有对图片进行截取,截取后返回新的图片,然后需要有将图片处理成libsvm输入格式的功能,在这方面opencv模块中的函数较为符合该需求。紧接着需要对图片使用libsvm进行分类,最后对有用的图片进行保存功能。
    性能需求:由于程序使用python中的libsvm库,需要大量的数据进行训练,同样需要大量数据进行预测分类,还需要对图像进行截取、灰度转化、二值化等操作,因此需要对数据进行处理时考虑到速度因素。
    环境需求:硬件需要一台笔记本电脑,系统环境需要Ubuntu,开发环境需要python,python中需要有libsvm、pydicom、opencv等第三方模块,spyder较为符合作为开发工具。
    现阶段,病人的医疗图像基本都是由医生进行诊断,使得医生的工作量过大,另外,其效率也并不是很高。而通过计算机将病人的医疗图像批量导入,深度学习中的卷积神经网络经过训练可以批量对医疗图像进行一定程度的解读,用来辅助或代替医生诊断病情。在使用深度学习中的卷积神经网络过程中,如果直接使用医疗设备拍出的医疗图像,由于图像中的噪音过大,无用信息过多,因此需要进行预分类处理。本文课题设计中,主要是对颈动脉血管进行分析处理,因此除颈动脉血管的横截面和纵截面外的其他图像都是属于无用的医疗图像,因此需要首先对批量导入的进行分类,分为有用图像的横截面、纵截面和无用图像,然后将有用的图像中的噪音切除,最后保存的图像中噪音大大减少。
    2.2 医疗图像预分类平台的可行性分析医疗图像处理是现代医学的重要手段之一,DICOM是部署最为广泛的医疗图像信息标准之一,医疗图像预分类平台将DICOM格式的医疗图像读取,并进行截取、分类操作后保存成新的图像。深度学习中的卷积神经网络对新的图像读取,之后运行在训练好的模型中并得出结果,对结果进行判断就可以了解图像中的部分是否发生病变。下文中,本文主要从经济可行性、技术可行性、运行可行性、和操作可行性等方面对医疗图像预分类平台进行分析。
    2.1.1 经济可行性开发医疗图像预分类平台所需要的相关资料可以通过互联网进行查询,也可以通过文献资料进行查询。开发该平台所需要的ubuntu系统可以从网上免费下载,python2.7环境以及libsvm、pydicom、opencv模块都是开源的资源,硬件只需要一台普通笔记本电脑,都易于获取,所需要的开发工具spyder也可以免费下载,无需其他特殊付费软件,开发成本低,简单易实现,从经济角度来看开发该程序经济具有可行性。
    2.1.2 技术可行性在医疗图像预分类平台中,医疗DICOM格式标准图像,在python中有对应的pydicom模块能够对其进行读取、分析、写入,可以对隐私信息进行读取,简单易使用;而opencv模块安装之后,使用简单,读取普通图片,只需要简单的调用函数就可以实现功能,较难的部分也已封装,简单的调用即可;台湾林智仁教授开发的libsvm第三方模块相比于普通的SVM来说,大大减少了难度,操作性强。因此,从技术角度来说,开发医疗图像预分类平台是具有可行性的。
    2.1.3 运行可行性运行性是整体组织结构产生的结果。该程序分为四个文件,但只需要在主函数中调用main函数,并输入相应的路径就可以与运行,只需安装好python2.7的开发环境,并将pydicom、opencv和libsvm模块安装在python中即可。因此,从运行角度来看,开发医疗图像预分类平台是可行的。
    2.1.4 操作可行性开发所采用的工具是spyder,开发出的程序可以在任何配置好环境了的计算机上运行,以console的形式展现运行过程及运行结果,将结果保存在固定位置,以显示器未输出,用户不需要关心运行过程,只看运行结果以及保存后的新图像文件,简单易使用。所以,该程序在操作上具有可行性。
    2.3 小结本章对医疗图像预分类平台分析需求,并从经济、技术、运行、操作等方面说明了该平台的可行性。
    3 医疗图像预分类中支持向量机的应用医疗图像预分类平台核心是分类器,而分类器算法为支持向量机算法,配合python读取dicom格式图像文件及opencv模块实现图像的去噪,形成医疗图像预分类平台。本章旨在说明支持向量机算法应用及配套应用。
    3.1 支持向量机的介绍基于深度学习的医疗图像预分类平台的核心是分类器,分类器的核心算法是支持向量机算法。通俗来讲,它是一种二类分类模型,基本模型定义为特征空间上的间隔最大的线性分类器,即支持向量机的学习策略便是间隔最大化,最终转化为凸二次规划问题的求解。
    对于线性分类问题,线性分类支持向量机是一种非常有效的方法。但是,大多数情况下分类问题是非线性的,这是可以使用非线性支持向量机,主要特点是引入了核技巧。非线性问题往往不好求解,如果用解线性分类的问题的方法来解决,就比较容易了。事实上,确实是这样的,进行非线性变换时分为两步:首先使用一个变化将原空间中的数据映射到新的空间;然后在新空间中用线性分类学习方法从训练数据中学习分类模型。这就是核技巧。
    核技巧的想法是在学习与预测中只定义核函数K(x, z),而不显示的定义映射函数。常用的核函数有多项式核函数、高斯核函数、字符串核函数等,适用于不同的场景。
    支持向量机原理图如图3-1,支持向量机工作流程图如图3-2。

    3.2 医疗图像标准dicom协议简介基于深度学习的医疗图像预分类平台的基础数据保存在DICOM格式医疗图像文件中,因此首先需要对DICOM格式医疗图像文件进行读取解析。医学数字成像和通信,缩写为DICOM,是医学图像和相关信息的国际标准。它定义了质量能满足临床需要的可用于数据交换的医学图像格式,被广泛用于放射医疗。DICOM标准的推出与实现,推动了远程放射学系统、图像管理与通信系统的研究与发展。在我国,DICOM是唯一被接受的医疗影像国际规范。在DICOM中,使用了对应的资料结构信息进行详述,定义了Patient,Study,Series,Image等4层来存储信息。DICOM将每个影像文件包裹成一个物件IOD,而一个IOD可以被分为像素数据和影像资料,前者通过描述图像中的每个点的值来组合一个医学图像,后者则是包含了病人资料信息。DICOM存储格式如图3-3。

    DICOM3.0支持在网络环境中使用标准的网络协议的操作,如OSI和TCP/IP。DICOM是定义在ISO/OSI七层结构中的最高三层,底层部分同样符合TCP/IP结构。
    3.3 python中处理图像模块简介基于深度学习的医疗图像预分类平台首先需要获取DICOM格式图像数据,之后需要对图像数据进行去噪截取、灰度转化、二值化处理、格式转化、分类器分类等过程,最后生成新的医疗图像。使用的python开发语言环境容易安装,python是比较好用和强大的语言,python的强大之处主要体现在它的第三方模块的多样性,在处理图像方面,有多个模块可以支持。在处理DICOM格式医疗图像方面,本文课题设计主要使用pydicom模块;在处理图像方面,本文主要采用了opencv模块;在图像分类方面,本文主要采用了libsvm模块。下文中,将展开对这几个模块的详细说明。
    3.1.1 pydicom模块简介pydicom是一个处理DICOM文件的python软件包,它可以通过非常容易的“Pythonic”方式对DICOM格式进行提取和修改,修改后的数据还可以生成新的DICOM文件。Pydicom是一个纯python语言的包,只要安装了Numpy模块,就可以在Python解释器下任何平台上运行。但是pydicom有其局限性,局限性在于无法处理压缩像素图像以及无法处理分帧动画图像。虽然pydicom有着一些局限性,但依然是非常有用的python模块。
    Python中的pydicom模块在操作性方面非常简单。首先需要导入pydicom模块,由于pydicom0.9.9之前都是dicom,1.0之后为pydicom,而这里所用的是dicom。dicom读取文件后的格式如图3-4。

    pydicom模块与其他一些图像显示模块配合起来,将会达到更好的展示效果,诸如matplotlib、PIL、pylab等一些模块。有时候还需要将修改后的dicom数据保存成新的文件,或者写入新的文件,从操作性方面来说,不具有难度,简单易使用。
    总体来说,纯python编写的pydicom模块大大减少了开发者的时间和精力,除了其局限性,在其余方面,pydicom是一个处理dicom格式文件的一个非常不错的模块。
    3.1.2 opencv模块说明opencv如今已经发布到了3.2的版本,但由于本文课题设计使用的是python2.7,网上的opencv2的资料相对多一些,因此,这里使用的是opencv2的版本。Opencv是英特尔开源计算机视觉库,由一系列C语言的函数和少量的C++的类构成,实现了图像处理的计算机视觉方面的很多通用的算法,拥有包括300多个C函数的跨平台的中、高层API,而且opencv不依赖于外部库,这就意味着,安装简单,不用考虑依赖关系。Opencv支持性较好,支持C++、C、python和java等语言,在操作系统方面,支持windows、linux、Mac OS、iOS和Android。
    opencv目前只支持bmp、jpg、png、tiff等常用格式的读取,首先读取图像,创建一个窗口,在此窗口中显示图像,最后释放窗口。对于保存图像,操作非常简单,直接使用cv2.imwrite即可。
    过滤是图像处理中的基本任务,可以移除噪音、提取感兴趣的可视特征、允许图像重采样等等。由于课题设计中要处理图像的噪音,这里介绍初级滤波操作,可以使用低通滤波器、高斯模糊、中值滤波等。低通滤波目标在于降低图像的变化率,是线性的;高斯模糊通过分配权重来重新计算周围点的值;中值滤波是非线性过滤器,在处理椒盐现象有比较大的作用。代码展示如下:
    result = cv2.blur(img, (5,5)) # 低通滤波gaussianResult = cv2.GaussianBlur(img, (5,5), 1.5) # 高斯模糊medianResult = cv2.medianBlur(img, 5) # 中值滤波
    Sobel算子也是一种过滤器,优势在于它是带有方向的,使用的原理是极值处就是边缘。图像处理中轮廓检测业务也经常用到,使用cv2.findContours()函数可以很好的获得图像的轮廓。
    3.1.3 libsvm模块说明libsvm是台湾林智仁教授与2001年开发的支持向量机库,对数据进行分类或者回归非常方便,支持java、python、C、C++等开发语言。在python中使用,需要首先导入libsvm的库:
    from svmutil import *y, x = svm_read_problem(’train1.txt’) # 读入训练数据yt, xt = svm_read_problem(’test1.txt’) # 训练测试数据m = svm_train(y, x) # 训练svm_predict(yt, xt, m) # 测试
    上述代码中同样给出了libsvm的基本操作,包括读取训练数据、训练测试数据、训练操作以及测试操作,这些操作基本都是直接调用libsvm的接口。
    在libsvm的使用中,参数的选择依然非常重要,在训练和预测过程中,需要一系列参数来调整控制。
    对于libsvm运行后的结果,同样要进行分析。#iter为迭代次数, nu 与前面的操作参数-n nu相同, obj为SVM文件转换为的二次规划求解得到的最小值, rho 为判决函数的常数项b, nSV 为支持向量个数, nBSV为边界上的支持向量个数,Total nSV为支持向量总个数。Libsvm运行后结果格式如图3-5。

    总体来说,libsvm用法简单,操作方便,只需要调用接口就可以使用,对参数的调整是libsvm模块的重中之重,相比于原来的SVM,大大减少了其复杂度。
    3.4 小结本章对支持向量机进行了说明,并对医疗图像标准dicom协议以及python处理图像的一些模块进行了说明,主要对pydicom模块、opencv模块以及libsvm模块进行了说明。
    4 医疗图像预分类平台开发的设计本章旨在说明医疗图像预分类平台开发过程中的设计,以及各功能模块的说明及代码展示。
    4.1 模块结构图本医疗图像预分类平台是属于非桌面软件,是属于后台程序,直接将数据存储在文件夹中。在设计程序时,安装从前到后的顺序将数据进行传输。首先进行设计的是dicom格式医疗图像的读取,将数据读取之后转化成可用格式数据,再将图像二值化后截取有用部分,去除噪音,之后将数据转化成libsvm的输入格式进行分类,最后判断分类后的图像是否是可用图像,如果是,则将分类后的图像保存成PNG格式图像,否则将其舍弃。流程图如图4-1。

    4.2 流程关系图本次课题设计中程序的流程总体来说是非常简单的,基本上是两条线性结构,训练数据线性结构与预测数据线性结构形成一个十字交叉型的结构,不用考虑太多内容,需要考虑的是性能问题。本程序中的核心是libsvm的分类器对图像进行分类。
    基本的流程是,首先将用于训练模型的dicom格式文件循环读取完毕,然后对数据进行格式转换,转换成libsvm分类器可用的格式,然后进行分类器的训练,将训练后的模型保存在文件中,这是第一条线性结构。然后读取用于预测的dicom格式文件循环读取,对图像进行去噪、截取等操作,将返回的数据进行转换,转化成libsvm分类器所用格式,直接调用保存好的模型,输入测试数据进行分类,最后判断分类后的图片是否是有用图片,如果是则保存图片,否则舍弃。
    上述为主要的两条流程,是一个十字形的结构,按照先训练后预测的思路运行程序,在进行去噪过程中是先进行灰度转化,然后进行二值化处理,最后截取图像形成一个新的图像。
    由于处理dicom格式的文件,在进行去噪处理和格式转化的过程中需要花费一定的时间,因此训练一次将花费大量时间,因此,本程序设计中编写了几个工具函数,存储训练后的模型至文件中以及从文件中读取模型等。流程关系图如图4-2。

    4.3 功能设计及分析由于本平台是对dicom医疗图像进行预分类的平台,处理的数据的基础是dicom格式文件,本平台主要是对颈动脉血管图像进行截取、处理、分类操作,由于使用的图像数据中既有颈动脉血管的横截面类型,也有纵截面类型,因此在分类器分类的类型中分类的颈动脉血管的横截面和纵截面类型都属于正确分类,下面将展示颈动脉血管图像,如图4-3。

    4.3.1 读取dicom格式文件并使用opencv去噪医疗图像预分类平台是基于深度学习的对dicom格式医疗图像进行分类的平台,由于医疗图像的格式文件基本上是存储成dicom格式的文件,因此本程序中需要有读取dicom格式文件数据的功能。本段程序中根据传入的文件路径,使用python中的pydicom第三方模块对dicom格式图像文件进行读取。本段代码中的读取dicom格式图像文件部分调用的是pydicom模块中的read_file函数接口,获取到一定格式的数据并存在dicom_data变量中。
    将获取到的数据通过DicomtoRGB函数转化成RGB图像数据并存入image变量中,与此同时调用loadFileInformation函数获取隐私信息,之后调用opencv模块中的cvtColor函数将image进行灰度转化,即将图像RGB图像转化为灰度图。
    将灰度图数据经过带有方向的Sobel算子过滤器过滤,之后转回uint8,再使用低通滤波将图像平滑化处理,处理后的结果保存在blurred变量中。将blurred变量调用opencv中的threshold函数进行二值化处理,调用opencv中的getStructuringElement函数定义矩形结构元素,调用opencv中的morphologyEx函数设定闭运算,然后进行形态学上的腐蚀操作和膨胀操作对图像进行处理,按照先腐蚀后膨胀的顺序进行操作最终检测图像中所需部分的轮廓,将轮廓进行绘制,之后进行截取,形成新的图像数据并返回。
    本段代码功能的实现总共分为三个函数,分别是DicomtoRGB函数、yaxpb函数以及picSplitResize_dicom函数,yaxpb函数的作用是实现线性变换,DicomtoRGB函数的作用是将dicom格式转化为RGB格式,picSplitResize_dicom函数的作用是对图像进行去噪截取转化处理。
    本段代码主要的作用就是对dicom格式图像进行读取、隐私信息提取和去噪截取操作,会返回截取后的图像数据。结果对比图如图4-4。
    关键代码:
    def DicomtoRGB(self, dicomfile, bt, wt): '''将dicom图片文件转换成RGB''' #创建一个黑色空白图片 image = np.zeros((dicomfile.Rows, dicomfile.Columns, 3), np.uint8) #获取图片的宽度和高度 i=0 j=0 while i<dicomfile.Rows: j=0 while j<dicomfile.Columns: #线性变换 color = self.yaxpb(dicomfile.pixel_array[i][j],bt,wt) #同样的R,G,B的值获取灰度标度 image[i][j] = (color,color,color) j=j+1 i=i+1 return image def yaxpb(self, pxvalue, bt, wt): '''线性变换''' if pxvalue < bt: y=0 elif pxvalue > wt: y=255 else: y=pxvalue*255/(wt-bt)-255*bt/(wt-bt) return y def picSplitResize_dicom(self, path): '''截取图片'''#image = cv2.imread(path) dicom_data = dicom.read_file(path)image=self.DicomtoRGB(dicom_data,bt=0,wt=2048)gim = GetInfomation()info = gim.loadFileInformation(dicom_data) #获取隐私信息gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY) #灰度转化#cv2.imshow('0', gray) #带有方向的过滤器gradX = cv2.Sobel(gray, ddepth=cv2.cv.CV_32F, dx=1, dy=0, ksize=-1)gradY = cv2.Sobel(gray, ddepth=cv2.cv.CV_32F, dx=0, dy=1, ksize=-1)gradient = cv2.subtract(gradX, gradY)gradient = cv2.convertScaleAbs(gradient) #转回uint8blurred = cv2.blur(gradient,(225,30)) #用低通滤波来平滑图像(_, thresh) = cv2.threshold(blurred, 90, 255, cv2.THRESH_OTSU+cv2.THRESH_BINARY)#二值化kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (25,77)) #OpenCV定义的结构元素 closed = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel) #闭运算closed = cv2.erode(closed, None, iterations=4) #腐蚀图像closed = cv2.dilate(closed, None, iterations=4) #膨胀图像(cnts, _) = cv2.findContours(closed.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) #检测物体轮廓c = sorted(cnts, key=cv2.contourArea, reverse=True)[0]rect = cv2.minAreaRect(c)box = np.int0(cv2.cv.BoxPoints(rect))cv2.drawContours(image, [box], -1, (0, 255, 0), 3) #绘制轮廓Xs = [i[0] for i in box]Ys = [i[1] for i in box]Xs.remove(min(Xs))Xs.remove(max(Xs))Ys.remove(min(Ys))Ys.remove(max(Ys))x1 = min(Xs)x2 = max(Xs)y1 = min(Ys)y2 = max(Ys)hight = y2 - y1width = x2 - x1cropImg = image[y1:y1+hight, x1:x1+width]#cv2.imshow('test', cropImg)#cv2.waitKey(0) return cropImg
    下面将展示上述代码运行前后的结果对比图如图4-4所示,左边图像为原始图像,即直接从dicom格式文件中读取到的图像,右边图像为去噪后图像,运行本段代码进行灰度转化、二值化处理并进行截取后的图像。

    4.3.2 读取隐私信息医疗图像预分类平台是对dicom格式医疗图像的读取解析以及获得正确分类的平台,由于诊断病情需要病人信息,而病人信息在dicom格式医疗图像中存储,因此解读dicom格式文件获取病人隐私信息。
    在这段代码中,主要功能是获取病人隐私信息并存储在字典中返回。由于医疗图像是以dicom格式进行存储的,并且医疗图像中必须要存储病人的隐私信息和病情信息,因此在进行读取dicom格式文件时,需要同时将隐私信息读取进入程序,之后根据需求将病人隐私信息进行相应的操作。在本段代码中使用python的pydicom第三方模块读取dicom格式文件,主要使用read_file函数,并将数据存储在变量中。声明GetInfomation的对象,将数据传入对象中,并调用loadFileInformation函数。在该函数中,将dicom格式图像数据的PatientID、PatientName、PatientBirthDate、PatientSex、StudyID、StudyData、StudyTime、InstitutionName、Manufacturer及NumberOfFrames属性中的数据保存在information字典中,并返回字典值。
    总体来讲,本段代码实现了读取病人隐私信息的功能,主要获取病人号(PatientID)、病人姓名(PatientName)、病人出生日期(PatientBirthDate)、病人性别(PatientSex)、StudyID、StudyDate、InstitutionName、Manufacturer、NumberOfFrames等方面。
    关键代码:
    class GetInfomation(): def loadFileInformation(ds): '''获取隐私信息''' information = {} information['PatientID'] = ds.PatientID information['PatientName'] = ds.PatientName information['PatientBirthDate'] = ds.PatientBirthDate information['PatientSex'] = ds.PatientSex information['StudyID'] = ds.StudyID information['StudyDate'] = ds.StudyDate information['StudyTime'] = ds.StudyTime information['InstitutionName'] = ds.InstitutionName information['Manufacturer'] = ds.Manufacturer information['NumberOfFrames'] = ds.NumberOfFrames return information
    4.3.3 读取训练数据医疗图像预分类平台本质上来讲,是机器学习的具体应用,是将医疗图像进行预分类的平台,获得正确分类的图像,而在机器学习中需要读取数据后进行后续操作,并且需要大量的数据以用于训练模型。
    本次课题设计中使用的是机器学习中支持向量机的有监督学习算法,因此需要首先将所有训练数据读入内存中,机器学习进行训练时需要成百上千的数据,有甚者,需要几十万条数据才能进行训练,如果训练数据太少容易造成欠拟合现象,造成分类器不能正确分类的现象。
    由于本次实验测试时使用的是png和jpg格式的图像,因此本程序设计中设计有两个函数分别是对dicom格式图像文件训练数据的读取和对普通格式图像文件的读取,首先将展示对dicom格式图像文件的读取代码。
    在读取dicom格式图像文件的函数中,需要传入dicom格式图像文件所在的文件夹路径以及文件后缀名,将文件夹中所有文件的绝对路径存储在path_list_dcm_test列表中,然后将dicom文件路径存储在path_list_dcm列表中,最终调用picSplitResize函数以及resize函数将数据转化为可用于分类的格式,将每张图像的类型标记及图像数据分别存在train_labels和train_images列表中,并将值返回。
    关键代码:
    def traindata_dicom(path_s, suffix): train_images = [] train_labels = [] path_list = [] path_list_dcm_test = [] path_list_dcm = [] dt2d = DicomTo2Data() while os.path.isdir(path_s): for pa in os.listdir(path_s): if os.path.isdir(path_s+'/'+pa): path_list.append(path_s+'/'+pa) path_list_dcm_test.append(path_s+'/'+pa) if len(path_list) != 0: path_s = path_list[0] del path_list[0] else: path_s = '' for p in path_list_dcm_test: for info in os.listdir(p): if info[-4:] == '.dcm' or info[-4:] == '.DIC': path_list_dcm.append(p) break for path in path_list_dcm: for files in glob.glob(path + '/*.' + suffix): filepath,filename = os.path.split(files) if filename[-6:-4] != '-1': train_labels.append(1) else: train_labels.append(-1) pic = dt2d.picSplitResize(filepath + '/' + filename) picNew = resize(pic,(30,30)) train_images.append(picNew[0]) return train_images,train_labels
    上述代码是对dicom格式图像文件训练数据的循环读取,并将训练数据调用picSplitResize函数进行灰度转化、二值化处理和格式转化,之后调用resize函数将转化后的数据进行归一化处理,最后返回正确格式的训练数据以及训练数据标签。由于在测试过程中的输入数据用png或jpg格式进行代替,能减少运行时间,下面将展示对普通png和jpg格式图像文件训练数据的读取。
    对于png或jpg图像文件的的读取函数,只需要传入文件夹路径,由于传入的文件夹路径下存在三个文件夹,分别是错误的分类、颈动脉血管横截面及颈动脉血管纵截面,标记分别为-1、1、2。同样调用picSplitResize函数以及resize函数,最终将图像数据和图像标记存入train_images和train_labels列表中并返回值。
    关键代码:
    def traindata(path_s): train_images = [] train_labels = [] path_list = [] dt2d = DicomTo2Data() for pa in os.listdir(path_s): path_list.append(path_s+'/'+pa) for path in path_list: for files in glob.glob(path + '/*'): filepath,filename = os.path.split(files) if path.split('/')[-1] == 'negative': train_labels.append(-1) elif path.split('/')[-1] == 'positive': train_labels.append(1) elif path.split('/')[-1] == 'us_images': train_labels.append(2) pic = dt2d.picSplitResize(filepath + '/' + filename) picNew = resize(pic,(30,30)) train_images.append(picNew[0]) return train_images,train_labels
    上述两段代码都有两个返回值,分别是train_images和train_labels列表,所代表的内容分别是训练图像数据和训练图像数据标签。
    4.3.4 归一化图片医疗图像预分类平台是首先对医疗图像进行一系列操作后,最终进行分类的平台。在对图像使用支持向量机分类器进行分类时,数据量较大,抵抗几何变换的攻击较弱,因此使用归一化处理,使图像能抵抗几何变换的攻击,同样可以找出图像中的那些不变量,将图像归为一个系列。
    在本段程序中,循环对图像数组中的数据转化,首先调用Image模块中的fromarray函数将图像转化为Image对象,之后通过调用size接口获取到Image的大小,将图像放置在中间位置。通过调用Image模块中的new函数创建一张背景为黑色的图片,之后通过调用point函数加强黑白色彩,调用numpy模块中的array函数将数据转化为一维化,通过[float(x)/255 for x in imgResizeArray]代码将0-255之间的色彩数值转化为0-1之间的数值,最终图像数据被添加到picNew列表中并返回。
    这段程序主要实现的是将图像进行归一化处理的功能,方便在后续的分类器中对图像进行分类操作。
    关键代码:
    def resize(picArray,size): '''调整图片大小,先归一化图片,并把原图片放在中间,返回的数据格式 [[],[],.....]''' picNew = [] for i in range(len(picArray)): imgPIL = Image.fromarray(picArray[i]) h,w = imgPIL.size newH = w//2 - h//2 #把图片放在中间 imgEmpty = Image.new('L',(w,w),0) #创建一张背景为黑色的图片 imgEmpty.paste(imgPIL,(newH,0)) imgResize = imgEmpty.resize(size,Image.ANTIALIAS) imgResize0255 = imgResize.point(lambda x: 255 if x > 10 else 0) #0是黑,255是白,黑白加强 imgResizeArray = np.array(imgResize0255).flatten().tolist() #转换为一维 imgResizeArraySmaller = [float(x)/255 for x in imgResizeArray] #把0-255转成0-1 picNew.append(imgResizeArraySmaller) #imgResize0255.show() #imgResize0255.save('//home//pandas//workspace//gradution//' + str(i) + '.jpg') return picNew
    4.3.5 使用opencv对图像进行处理医疗图像预分类平台是对dicom格式图像进行读取解析分类的一个平台,核心是使用支持向量机算法对图像进行分类,使用python中的libsvm 第三方模块,由于libsvm输入格式有一定的要求,因此需要将图像数据转化为可输入的格式。
    本段程序将图像数据转化为可用于分类器分类的数据输入格式,picSplitResize函数传入参数为image图像数据,首先调用opencv模块中的cvtColor函数进行灰度转化,将转化后的结果存储在gray变量中,之后调用opencv模块中的dilate和erode函数先膨胀后腐蚀,再进行腐蚀后膨胀,处理后的数据存储在dilated变量中。将dilated变量通过opencv中的medianBlur函数进行细化,然后调用opencv模块的findContours函数进行二值化处理并查找图像数据的轮廓。最后通过调用一个循环将数据转化为可用于分类器分类的输入格式,在循环中首先通过opencv中的formarray函数将array转化成cvmat,然后通过opencv中的GetImage函数将cvmat转化为iplimage,最终将iplimage转换成cvmat,再将cvmat转换成数组。
    这段程序的主要功能在于格式转化,主要使用python中的opencv模块处理医疗图像,下面将本段程序的代码展示出来。
    关键代码:
    def picSplitResize(self, image): '''分割影片尺寸大小''' #image = cv2.imread(path) #dicom_data = dicom.read_file(path) #image=self.DicomtoRGB(dicom_data,bt=0,wt=2048) #灰度转化 gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY) #ret , bin = cv2.threshold(gray, 100,255, cv2.THRESH_BINARY) #cv2.imshow('2',gray) #膨胀后腐蚀 dilated = cv2.dilate(gray, cv2.getStructuringElement(cv2.MORPH_RECT,(2, 2))) eroded = cv2.erode(dilated, cv2.getStructuringElement(cv2.MORPH_RECT,(2, 2))) #腐蚀后膨胀 eroded = cv2.erode(eroded, cv2.getStructuringElement(cv2.MORPH_RECT,(2, 2))) dilated = cv2.dilate(eroded, cv2.getStructuringElement(cv2.MORPH_RECT,(2, 2))) #细化 median = cv2.medianBlur(dilated, 3) median1 = cv2.medianBlur(dilated, 3) #轮廓查找,查找前必须转换成黑底白字 contours, heirs = cv2.findContours(median1,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) #i = 0 pic = [] dictPic = {} for tours in contours: #rc[0] 表示图像左上角的纵坐标,rc[1] 表示图像左上角的横坐标,rc[2] 表示图像的宽度,rc[3] 表示图像的高度, rc = cv2.boundingRect(tours) #array转换成cvmat image1M = cv.fromarray(median) #cvmat转换成iplimage image1Ip = cv.GetImage(image1M) cv.SetImageROI(image1Ip,rc) imageCopy = cv.CreateImage((rc[2], rc[3]),cv2.IPL_DEPTH_8U, 1) cv.Copy(image1Ip,imageCopy) cv.ResetImageROI(image1Ip) #将iplimage转换成cvmat,然后将cvmat转换成数组,将图像左上角的纵坐标和图像的数组元素放到字典里 dictPic[rc[0]] = np.asarray(cv.GetMat(imageCopy)) pic.append(np.asarray(cv.GetMat(imageCopy))) #i = i+1 sortedNum = sorted(dictPic.keys()) for i in range(len(sortedNum)): pic[i] = dictPic[sortedNum[i]] #cv2.waitKey(0) return pic
    4.3.6 使用libsvm进行训练并进行预测分类医疗图像预分类平台是将dicom格式医疗图像读取解析之后,再通过机器学习中的支持向量机将医疗图像进行分类,而该平台中最核心的部分就是支持向量机算法分类器分类部分。
    下面这段程序是所有代码程序中最重要、最核心的部分,其余部分代码都是这段代码的辅助。本次课题设计的核心其实就是分类,找出正确的医疗图像并进行去噪处理,在这段代码中,主要使用了libsvm的模块来实现支持向量机的分类操作。首先将训练数据、训练标签以及参数确定下来,通过libsvm函数中的svm_problem函数将训练标签和训练数据读入分类器,通过svm_parameter函数将参数确定,之后通过调用libsvm模块中的svm_train函数训练模型。之后先确定预测数据的标签,然后调用svm_predict函数对未知的图像数据进行预测,并将预测结果值返回
    在这段函数代码中,参数需要训练数据、训练数据标签以及预测数据,首先要进行训练,训练出模型,然后使用训练好的模型对未知图像数据进行预测分类,代码如下。
    关键代码:
    def predictPIC(train_images, train_labels, picdata): '''创建LibSVM分类器,返回值为识别出的内容''' prob = svm_problem(train_labels,train_images) param = svm_parameter('-s 0 -t 2 -c 1.2 -g 2.8') prob = svm_train(prob,param) labels = [1] * len(picdata) flag = svm_predict(labels,picdata,prob) return flag
    下面将展示运行本段代码之后的结果图,本段代码是libsvm分类器对图像进行分类的的最后结果图,运行一个测试数据的分类结果图。#iter为迭代次数, nu 与前面的操作参数-n nu相同,obj为SVM文件转换为的二次规划求解得到的最小值, rho 为判决函数的常数项b, nSV 为支持向量个数, nBSV为边界上的支持向量个数, Total nSV为支持向量总个数分类器分类结果图如图4-5。

    4.3.7 保存新图像医疗图像预分类平台最终的结果需要生成经过去噪处理的新医疗图像。
    使用支持向量机分类器对预测图像进行分类后,还需要对图像进行最后一步操作,即将数据保存成png格式图像以便于后续操作。主要调用opencv模块的imwrite函数将最终分类的图像数据保存成png格式的图像文件,生成的新图像文件命名与原始图像文件命名相同,但保存在固定的文件位置。现将代码展示如下。
    关键代码:
    def makePNG(self, image_info, file_name): res = cv2.resize(image_info, (255,255),interpolation = cv2.INTER_LINEAR) name = file_name.split('/')[-1][:-4]cv2.imwrite("/home/pandas/workspace/python/graduation/DICOM_IMAGE/error/"+name+'.png', res, [int(cv2.IMWRITE_PNG_COMPRESSION), 9])
    4.4 医疗图像预分类平台开发方法医疗图像预分类平台开发使用的是面向对象和结构化开发混合的开发方法,本次开发由于没有使用可视化开发技术,因此下面将不会对可视化开发技术进行介绍。
    面向对象开发的方式是将真实的问题转化映射,映射到抽象对象空间,实际上,它是将现实世界中的事物进行模拟,主要是以大量的数据为中心,因而,面向对象开发方式较为稳定,程序中代码的重用性和可维护性比较好。
    对于结构化开发方式来说,主要是按照功能进行开发,将功能进行分解开发,从上而下,最终实现需求的功能。以功能的划分为开发基础。总共分为以下几个阶段:划分功能范围、功能分析、功能设计、功能实现以及功能支持。
    本程序中代码量相对比较少,因此主要是以类和函数混合的方式实现,从扩展性方面来讲,相对较好。
    4.5 定义规范作为一名开发者,需要有一定的开发规范,从而实习代码的可读性和简洁性。这不仅是了方便自己,更是为了培养出团队合作的基础,从此方面来讲,定义代码规范是非常重要的一点规则。
    4.5.1 代码注释规范1.函数注释
    由于函数名的命名就是函数具体作用信息的英文单词组合,因此只有部分函数有注释信息,注释信息是使用’’’’’’的注释方式来注明函数的具体作用,如下:
    def makeOnePicture(file_name, train_images, train_labels): '''调用predictPIC函数进行预测并截取图片''' #info = loadFileInformation(dicom_file) dt2d = DicomTo2Data() image = dt2d.picSplitResize_dicom(file_name) pic = dt2d.picSplitResize(image) picdata = resize(pic,(30,30)) result = predictPIC(train_images, train_labels, picdata) if result[0][0] == 1.0 or result[0][0] == 2.0: dt2d.makePNG(image, file_name)
    2.类注释
    类名的命名方式和函数名称命名的方式基本类似,因此也是部分类有注释信息,主要是说明类的作用信息,大部分类没有注释说明,主要是通过类中的属性函数进行注释。
    3.语句注释
    语句注释中有三种:第一种是对用于的测试语句的注释,在代码中可忽略不计;第二种是在代码的后面空四个空格然后使用“#”进行注释,用于说明该行代码中语句的作用;第三种是单独一行使用“#”进行注释,作用范围直至空行或下一个注释行之间,用于说明从注释行到空行之间所有代码的作用,示例如下:
    def resize(picArray,size): '''调整图片大小,先归一化图片,并把原图片放在中间,返回的数据格式 [[],[],.....]''' picNew = [] for i in range(len(picArray)): imgPIL = Image.fromarray(picArray[i]) h,w = imgPIL.size newH = w//2 - h//2 #把图片放在中间 imgEmpty = Image.new('L',(w,w),0) #创建一张背景为黑色的图片 imgEmpty.paste(imgPIL,(newH,0)) imgResize = imgEmpty.resize(size,Image.ANTIALIAS) imgResize0255 = imgResize.point(lambda x: 255 if x > 10 else 0) #0是黑,255是白,黑白加强 imgResizeArray = np.array(imgResize0255).flatten().tolist() #转换为一维 imgResizeArraySmaller = [float(x)/255 for x in imgResizeArray] #把0-255转成0-1 picNew.append(imgResizeArraySmaller) #imgResize0255.show() #imgResize0255.save('//home//pandas//workspace//gradution//' + str(i) + '.jpg') return picNew
    4.5.2 代码命名规范对于类名采用驼峰命名法,英文,有语义单词首字母全部需要大写,剩余有语义单词小写,从类作用方面命名,如:GetInfomation,意义是获取信息,即获取病人隐私信息。
    对于函数命名采用驼峰命名法或者下划线命名法。驼峰命名法首个有语义单词首字母小写,其余有语义单词首字母大写,其余字母小写。命名法如:loadFileInformation、traindata_dicom。
    对于变量,主要使用下划线命名法,从变量作用方面进行命名,如:train_images。
    4.6 小结本章主要对医疗图像预分类平台的设计进行了说明,首先展示了模块结构图和流程关系图,然后对代码中的每一块功能进行说明,之后说明了开发的方法,并说明了代码定义规范。
    5 医疗图像预分类平台开发测试与调试本章旨在说明医疗图像预分类平台开发中测试与调试过程,分别对程序调试和工具测试以及测试数据进行说明。
    5.1 程序调试在进行软件程序开发的过程中,难免会遇到各种各样的问题,不仅仅是常见的开发环境问题和语法问题,还有许多其它问题在实际开发过程中。在开发该程序中,采用模块化、结构化的开发方式,按照功能进行开发,同样按照功能进行测试,单独测试,便于检查出现问题时问题的来源。
    在测试调试过程中,主要使用Spyder软件进行程序的开发和测试过程,使用该软件中的错误语法提示信息和单元测试运行后的结果以及打印语句对错误进行定位和显示。
    5.2 工具的测试5.2.1 测试的意义及目的软件测试的目的,从本质上讲,是为了程序的有效可靠可运行性,比较软件最初的需求和在真实运行环境下的功能的差别,从而对差异之处进行一定程度的修正,以达到预期的功能和结果。
    5.2.2 测试步骤测试是有一定的方式、步骤以及技巧的。这段程序中不涉及与其他机器之间的操作,只是本机Ubuntu系统中的代码的运行,因此,在测试程序中只运用了手工测试、单元测试和系统测试的测试方法对系统的功能进行测试,以追求稳定可靠的程序。
    首先要进行单元测试,对源代码中的每一个功能单元进行测试,基本要达到覆盖每一个类以及函数,最终组合起来,检查各个功能部分是否正确地实现了功能模块。
    系统测试把经过确认的功能单元进行组合,纳入到实际运行的环境中,即Ubuntu系统中的python2.7开发环境中进行运行,将整个程序统一在系统中整体运行,查看结果的一种测试。
    5.3 测试数据在每一个功能单元完成后都会进行相应的分模块功能测试以确保该模块的正常运行,也同样是为了方便后续对功能进行整合。在对每一个功能单元进行测试时选择训练数据中的一部分进行测试,如在测试去噪功能、格式转化功能等,而对分类器进行测试时,需要使用的是所有训练数据以及部分预测数据等,在对dicom文件读取功能进行测试时,使用的是部分dicom数据。通过选择某些具有代表性的数据进行测试可以测试程序的健壮性,确保程序的良好运行。下面将本程序中选择主要功能所做的测试,并展示所出现的问题和解决方法,还有最终效果。



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




    读取dicom文件、去噪
    使用一个dicom文件作为输入数据,
    dicom文件
    不能处理压缩过dicom文件
    使用未压缩的dicom图像文件
    可以读取并去噪


    读取隐私信息
    手动测试
    dicom文件
    有些隐私信息没有
    读取关键隐私信息即可
    正常


    读取训练数据
    输入路径循环读取数据
    所有训练数据


    正常


    归一化图片
    手动测试
    一个图像数据


    正常


    opencv处理图像
    对图像数据进行处理
    图像数据


    正常


    libsvm训练预测分类
    训练数据,预测数据
    训练、预测数据


    正常


    保存新图像
    分类后图像数据保存在固定位置
    图像数据


    正常



    5.4 小结本章主要说明了医疗图像预分类平台的测试和调试过程,主要从程序的调试、工具的测试以及测试数据方面进行了说明。
    6 结论医疗图像预分类平台采用python技术进行开发,使用了pydicom模块读取dicom格式图像文件,opencv模块对图像进行去噪和格式转换以及libsvm模块进行图像分类处理,最后保存成新图像,使用企业级规范开发。本程序代码已推送到github上,但是并未书写说明文档,仅仅是在代码中有注释。
    本次基于深度学习的医疗图像预分类平台的输入数据是DICOM格式医疗图像文件,该平台对DICOM格式医疗图像读取后,进行去噪处理并进行截取,形成新的图像文件,然后将新的医疗图像文件进行格式转化,转化成可用于支持向量机输入的格式,之后将新格式的医疗图像数据通过支持向量机分类器进行分类,主要调用libsvm模块。最后对分类正确的图像数据进行保存操作,保存成PNG格式图像。以上就是该医疗图像预分类平台的功能作用。
    参考文献[1] Andrew Ng.Machine Learning[OL].2017.3 https://www.coursera.org/learn/machine-learning
    [2] 李航.统计学习方法[M]. 北京:清华大学出版社, 2012.3
    [3] TOBY SEGARAN.集体智慧编程[M]. 北京:电子工业出版社,2009.1
    [4] [美]\ 哈林顿著 李锐等 译.机器学习实战[M]. 北京:人民邮电出版社,2013.6
    [5] 林意,廖琴枝.基于无损水印的DICOM文件头信息篡改检测[J].计算机工程,2016:151-155,162.
    [6] 龙华飞,唐月华,陈泓伶.PACS系统中DICOM医学图像格式解析[J].中国数字医学,2014: 29-31.
    [7] 梁卫星,陈平平,张戈,徐洋洋.DICOM文件剖析及读取设计[J].现代计算机:普及版,2014:72-75.
    [8] 王云英,阎满富.C-支持向量机及其改进[J].唐山师范学院学报,2012.
    [9] 张立,王渊民.基于核函数的非线性支持向量机[J].科技展望,2017.
    [10] LAROBINA M,MURINO L.Medical image file format FAQ[J].J Digit Imaging, 2014:200-206.
    [11] Richard Curran,Nel Wognum,Milton Borsato,Josip Stjepandi&cacute, Wim J. C. Verhagen, Rodrigo Meira de Andrade,Anderson Luis Szejka,Osiris Canciglieri Junior.A Gingival Mucosa Geometric Modelling to Support Dental Pros thesis Design[M].IOS Press:2015.
    [12] William Cheng-Chung Chu,Han-Chieh Chao, Stephen Jenn-Hwa Yang, Yu-Ann Chen,Pau-Choo Chung.Enhanced Local Support Vector Machine With Fast Cross-Validation Capability[M].IOS Press:2015.
    [13] KhazendarS, Sayasneh A,Alassam H,et al.Automated characterisation of ultrasound images of ovarian tumours: the diagnostic accuracy of a support vector machine and image processing with a local binary pattern operator[J].2015:7-15.
    [14] Ma Y,Wang L,Li L.A Parallel and Convergent Support Vector Machine Based on Map Reduce[M]//Computer Engineering and Networking.Springer International Publishing,2014:585-592.
    [15] Sharifi M A, Souri A H.A hybrid LS-HE and LS-SVM model to predict time series of precipitable water vapor derived from GPS measurements[J].Arabian Journal of Geosciences,2015:7257-7272.
    2 评论 15 下载 2018-10-03 22:33:13 下载需要14点积分
  • 基于win32控制台应用程序的双人俄罗斯方块小游戏

    1. 课题概述1.1 课题目标和主要内容使用visual studio 2015在win32控制台应用程序下用多线程实现双人同时进行俄罗斯方块的桌面游戏。最终将要完成的效果如图1.1所示,左右共两片工作区,也是游戏的主题;工作区旁边是记录当前游戏的级别和分数以及下一个掉落方块类别,根据工作区的不同各自通过键盘上的’w’, ’a’, ’ s’, ’d’和’↑’, ’←’, ’↓’, ’→’来控制方块的旋转、下移、左移、右移。

    1.2 系统的主要功能
    能够同时两个人进行俄罗斯方块的对战
    每个俄罗斯方块具备基本的旋转、左移、右移和下落操作
    当其中一个俄罗斯方块消掉一行时,另一个俄罗斯方块将增加一行
    能够调节方块的下落速度
    能够保存玩家的历史最高分
    双重模式供玩家选择
    能够根据模式的不同播放音乐

    2. 系统设计2.1 系统总体框架俄罗斯方块游戏的设计思路遵循MVC设计模式,该模式是一种使用Model-View-Controller(模型-视图-控制器)设计创建应用程序的模式。MVC框架图如图2.1所示。

    Model(模型):表示应用程序的数据的存取
    View(视图):显示界面,根据Model提供的不同数据,显示不同的界面
    Controller(控制器):处理用户界面的交互,以及向模型发送更新的数据

    MVC分层有助于管理复杂的应用程序,这样可以在一段时间内专门关注一个方面。同时也让应用程序的测试更加容易,并且简化了分组开发。不同的开发人员可同时开发视图、控制器逻辑和业务逻辑。
    对于俄罗斯方块游戏来说,需要的设计步骤如下:

    设计Model。即工作区和方块的存储结构用来方便地存储数据
    设计View。即如何根据数据显示不同的界面
    设计Controller。即如何用键盘控制数据的变化


    2.2 系统详细设计2.2.1 模块划分图及描述
    初始化模块:完成初始界面的显示,以及数据的初始化。该函数封装了GetStdHandle()控制台API
    基本的显示函数:在制定位置显示指定颜色的字符串
    BOOL textout(HANDLE hOutput, //控制台句柄int x,int y, //显示坐标WORD wColors[], //显示颜色int nColors, //颜色数量LPTSTR lpszString); //显示的字符内容
    方块的显示和擦除:完成在指定位置显示/擦除指定颜色、形状的方块。

    方块显示:在x,y坐标位置显示长和宽为w和h,颜色为wColors的方块,方块形状由数组a表示,整型数p、q确定工作区位置。void DrawBlocks(int a[], int w, int h, int x, int y, WORD wColors[], int nColors, int p, int q);
    方块擦除:擦除x,y,y坐标位置的长和宽为w和h,颜色为wColors的方块,方块形状由数组a表示,整型数p、q确定工作区位置。void ClearSquare(int a[], int w, int h, int x, int y, int p, int q);

    方块旋转:完成每种方块4个方向的旋转。
    void Turn(int a[][4], int w, int h, int *x, int y, int MAP[][MAPW]);
    判断方块是否能够下落。
    bool IsAvailable(int a[], int x, int y, int w, int h, int MAP[][MAPW]);
    方块满一行后消行:
    参数说明:在工作区row位置的一行,然后将该行之上的方块下移。其中数组m表示工作区,整型数p、q确定工作区位置。
    void DeleteLine(int m[][MAPW], int row, int p, int q);
    给另一工作区加行:参数说明:在另一工作区,将工作区方块整体上移,然后在最底下一行位置增加一行。其中数组m表示工作区,整型数p、q确定工作区位置。
    void AddLine(int m[][MAPW], int row, int p, int q);
    获取历史最高分:在程序所在路径下生成一个MaxScore.txt文件用来记录最高分。
    int GetMaxScore();
    整体清屏参数说明:默认为0,整体清空界面,参数值为1时清空左工作区内方块,参数值为2时清空右工作区内的方块。
    void ClearBlank(int i=0);
    判断重新开始还是结束游戏
    void Change();
    游戏说明
    void GameRule();
    难度选择
    void LevelChoose();
    正经模式:按照要求实现双人多线程俄罗斯方块,能够完成相关操作。
    void DoubleMode();
    地狱模式:改变背景音乐风格,随机确定初始下落位置。其他同正经模式。
    void HeheMode();
    模式选择:选择正经模式、地狱模式、游戏说明还是退出游戏。
    void ModeAlter();
    游戏结束:退出游戏时界面,打印GAME OVER。
    void GameOver();
    线程处理函数:左右工作区分别对应线程0和线程1
    DWORD WINAPI ThreadFunc0(LPVOID lpParam);DWORD WINAPI ThreadFunc(LPVOID lpParam);

    2.2.2 程序流程图及描述按照面向过程的程序设计思想,自顶向下逐步求精的方法,首先需要根据基本功能进行模块划分,设计程序流程。俄罗斯方块游戏的基本的运行模式就是根据用户的按键,显示、翻转、移动不同的方块,因此其流程结构详见附件系统主流程1,系统主流程2,键盘处理流程。
    2.2.3 存储结构、内存分配
    工作区数据存储:游戏中可划分出两个工作区,在两个区域内方块根据操作进行移动、翻转,用两个两位数组来分别保存两个工作区中每个位置信息点的信息。程序中选用工作区的大小是12*20,由常量MAPW和MAPH表示。
    #define MAPW 12 //地图的宽度#define MAPH 20 //地图的高度

    方块数据存储:俄罗斯方块有各种不同的形状,基本形状有7种,每一种形状采用4*4的数组来存储,分别使用三维数组来存储。
    int b[7][4][4] = { { { 1 },{ 1,1,1 } }, { { 0,2 },{ 2,2,2 } }, { { 3,3 },{ 0,3,3 } }, { { 0,0,4 },{ 4,4,4 } }, { { 0,5,5 },{ 5,5 } }, { { 6,6,6,6 } }, { { 7,7 },{ 7,7 } } };
    3. 程序运行结果分析3.1 输出显示方式用Drawblock函数更新方块,配合Textout函数更新游戏参数。
    欢迎界面

    模式选择界面

    正经双人模式界面

    3.2 操作流程程序开始后,进入模式选择界面,可选择游戏说明,或直接开始游戏,模式选择完毕后则选择游戏难度,之后正式开始进行游戏。游戏中中可任意按空格键暂停游戏,或任意按回车键返回模式选择界面,以及按ESC键直接退出游戏。
    3.3 运行效果整体来说程序相对流畅,基本符合要求,各种界面之间切换自然,衔接不会有太多延迟感,音乐循环播放也没有感觉到切换的卡顿。
    4. 总结4.1 课题的难点和关键点4.1.1 线程同步本程序中使用双线程,难点在于两个线程彼此同步调配,二者均同从键盘按键中获取键值进行相应操作,为避免二者相互冲突,故引进临界区变量,当某一线程读取键值时锁定使另一线程暂时无法读取,由于临界区锁定时间短,人难以察觉出细微变化,故不影响整体速度。
    4.1.2 增加一行当某一玩家成功消行时,对另一玩家实行加行操作。难点在于重绘上移后的工作区。仿照消行函数Deleteline()的逻辑,从上向下逐行扫描更新。一方面,由于不同于消行操作,加行时的绘制起始点行列,须认真计算,否则容易出现绘制异常;另一方面,由于绘制方块函数Drawblock()逻辑原因,对于工作区内块值为零的区域,不执行任何操作,故当该工作区内方块整体上移时,对于某些排列空隙有可能上移后被填满,所以对于块值为零区域,单独调用Textout()函数在其区域打印char[3]的空格进行擦除,但同时造成下落方块的暂时消失,可以在循环按键处理中更新下落方块,使由于擦除造成的方块闪烁感消失。
    本程序中设定由于对方消行所增加的新行不在具有能够消除的属性,故要增加两个整型数分别记录增加的行,方便在调用消行函数时消去的行不在惩罚增加行之中。
    4.1.3 线程结束当游戏一方到达顶部时,游戏结束后重新开始需要将原有两个线程结束掉,该问题属于程序设计逻辑问题。解决思想如下:
    当一方到达顶部时,设置该对应线程结束完成标志ok1(如线程0)和设置结束另一线程命令标志flag2,之后return结束该线程;当另一线程接收结束命令flag2后,设置该对应线程结束完成标志ok2,之后return结束该线程;
    当满足两个线程结束标志ok1和ok2时将退出当前游戏模式,跳转到模式选择模式。
    4.2 本课题的评价从最初的一个子线程配合一个主线程,到现在的两个相似的子线程交叉调度;
    从最初的单人模式,到现在正经双人和地狱双人模式;从只能游戏一次,到现在任意返回、任意退出、无限循环开始。可以说本课题完成费了不少时间精力,完成相对满意。但程序仍有一些不够完善的地方,例如长条形方块下落时再不足旋转空间处旋转造成旋转出工作区外、两个线程对键盘读取键值的处理不够理想,容易引起漏键的现象。希望随着编程能力的提高将来能够更好地将这些问题优化好,做成一个成熟应用程序。
    4.3 心得体会通过此次程序设计实践,一方面,对win32控制台应用程序有了新的认识,不在仅局限使用标准输入输出流cin/cout进行界面交互,通过使用API 函数可以使编程编得更加方便,增加对句柄的熟悉度;另一方面,学习了多线程编程的一些相关概念及函数调用,在debug时明白了线程之间切换的大概过程,让我debug能力得到了很好地提高。对于此类程序编写时,要注意其可扩展性,方便后期修改,模块要区分的清除很重要的一点就是,程序设计时,一定要整理后各个模块以及各个模块间衔接的逻辑关系,尽可能地避免逻辑错误。
    5. 参考文献[1] 张宏,黄小诚. VC++语言多线程编程及其实现[J]. 科技信息(科学教研). 2008(07)
    [2] 韩玉坤,介龙梅. 浅谈多线程编程技术及方法[J]. 电脑学习. 2010(02)
    [3] 胡吉全,李明星. 基于VC多线程技术的优化设计方法[J]. 交通与计算机. 2005(02)
    [4] 王日宏. 基于VC的Win32多线程同步问题[J]. 计算机系统应用. 2004(07)
    1 评论 2 下载 2019-06-10 17:29:56 下载需要9点积分
  • 基于Python的Django框架实现的中式快餐厅管理信息系统网站

    1 初步调研随着餐饮业的连锁和国外餐饮巨头的进入,餐饮业的竞争将越来越激烈:要想在竞争中处于不败之地,必须在管理、服务等方面提高服务管理意识。面对当前餐饮业普遍的产业化程度低,管理手段、管理技术落后等问题,使用计算机管理在很大程度上可以帮助餐饮企业克服这些困难。计算机管理系统可以提高餐饮企业的管理水平,节省大量人力、物力和财力,使经营管理更加科学有序、经营物流清晰,经营状况详实、准确。
    1.1 研究现状我国餐饮业的餐饮信息管理系统的开发,规模大小各不相同,开发方法、模式、平台也不尽相同。有的是购买的系统,有的是自己开发,有的还进行了多次开发,因为开发人员和方法的原因,系统维护和升级都比较困难。信息共享性差,数据冗余大,维护困难。
    随着科学技术的不断发展,计算机科学日趋成熟,信息处理能力也日渐强大,在人类社会日常生活中也日益广泛,逐步渗透到各个领域。为了解决服务行业的高速发展和管理所产生的各种问题,各种各样的信息化管理软件日益涌现,给不同行业的信息管理带来了不可估量的经济价值。结合餐饮业自身的特点及业务流程,当下主流系统的问题分析如下:
    解决方案不完整:部分系统只有收银模块、点菜模块,然而点菜模块并没有和厨房管理仓库管理联系起来,有时会在点菜过程中要求库存不足或者没有的菜,大大影响顾客对企业的影响,人员流失在所难免。
    业务管理不够人性化:部分系统并不能即时的了解库存,进货,从而造成库存不足,不能及时满足客户要求,同时也不能及时实现客人点单,结账等情况,浪费客户时间,从而影响客户对企业的信誉度,也不能提供多种结账方式,不能给客户带来便利,这些不足使得整个企业需要花费更多的时间和精力来填补,从而大大影响了企业的工作效率。
    系统交互能力弱:部分系统的交互界面易用性差,操作不够简单,使得工作人员难以上手,必须花费一定的时间去了解和熟悉系统,从而员工的工作效率低下,不能及时满足客户需求。
    系统决策能力差:部分系统并不能对客户用餐数据进行分析,不能给决策者提供可靠的数据支持,这也直接影响了企业的前景与发展。
    酒店管理软件稳定性差:国内的酒店管理软件源自于国外,但是并没有剔除掉原有软件的弊端,而是全部接受,并且还没有随着国外软件的更新而改进,也没有在国内大环境下提出新的适合中国人的餐饮管理软件,原有的管理软件仍然是模仿手工管理的过程,但是没有考虑大数据对传统行业的影响,数据分析,用户的偏好分析等都没有提出新的有效的解决方案。
    2 企业目标餐饮企业管理是指餐饮企业管理者在了解市场的前提下,通过执行计划、组织、指挥、控制、协调等职能,使企业形成最大营业能力,保证实现经济效益和社会效益的过程。因此,我们通过餐饮业管理信息系统对餐厅内的一系列组织结构进行划分,并做出规划,通过系统的分析和调配,对市场进行分析,再加上对以往数据的分析,形成最优的配比,以实现在正常运作下,使花费的费用最少的情况下,达到的效率最高、收益最大化。
    3 技术方案拟采用Django+Mysql+Nginx+Windows方案。Django是基于Python定制的Web框架,采用MTV的框架模式,即模型M,模板T和视图V。框架的对象关系映射ORM可使用Python类形式定义数据模型,ORM将模型与关系数据库连接起来,得到一个非常容易使用的数据库API,同时也可以在Django中使用原始的SQL语句。使用Django的主要目的是简便、快速地开发数据库驱动的网站。Django将被部署在较为轻量的Nginx服务器上,Nginx配置简洁,支持高并发连接,同时又有很好的CPU使用效率。搭配Mysql数据库,四者构成完全开源的开发平台,文档完善,且拥有庞大的开发者社区。
    4 系统规划4.1 餐厅组织结构
    4.2 企业过程
    4.3 U/C矩阵基于企业过程生成数据类

    5 系统的界限
    5.1 有关组织的信息分析5.1.1有关人员方面的信息(人员及构成)餐厅内部的各职能部门由总经理全面领导,总经理将招聘准则交给人事部门,又由人事部门负责招聘,对招聘进来的人员进行管理分配,将符合各部门要求的职员分配给各部门,所有员工均有人事部门统一进行协调排班。前台由服务人员,收银人员,配送人员组成,服务人员负责服务顾客,传递订单的职能;收银人员负责收款,传递收费报表;配送人员负责成品的递送。厨房由配菜员,厨房经理,厨师组成,厨房经理负责同方的统一调度,配菜员负责将订单信息转化为配菜信息,并通过配菜信息交给仓库进行拣选,厨师负责加工产品。仓库由库存管理员,拣选员,清点员组成,仓库管理员负责对仓库的信息统一进行管理,拣选员负责将厨房发来的配菜单惊醒整理并拣选菜品,清点员负责将新到的货品对照进货单进行清点。采购部门由采购员组成,采购员负责进行缺货物料的采购。人事部门由人事管理员与绩效考核员组成,人事管理员负责招聘、管理、分配餐厅内的人员,效绩考核员负责根据考核单进行工资评定。财务部门由财务会计与工资发放员组成,财务会计负责汇各类财务单工资发放员负责工资的发放。
    5.1.2 有关工作方面和工作环境方面的信息工作方面:前台进行与客户的主要交互,因此前台人员应符合餐厅的服务理念,使顾客尽量能够满足我们的服务;厨房进行生产加工,这里的员工应该具备较强的技能,使每道菜都有其相应的价值;库存与采购主要与物料进行接触,应保证物料的质量与数量,这里的人员应具备较强的责任心与观察力,使库存不出任何差错;人事应具有较强的管理能力,使餐厅整个人员流动处于平衡状态,使工作有条不紊的进行;财务应具备较强的统计能力,使餐厅的收支准确无误。
    工作环境方面的信息:餐厅内部前台的环境是整个餐厅的核心,也是顾客进行消费的场所,这里的工作环境没有很大的变动,但一般都是琐碎的工作;厨房与库存实际上是处于一体的,厨房与库存不分家;采购是厨房的外接,主要环境是市场与库存;人事与财务都是管理层,人员的工作一般都是业务的处理,都有相应的办公室配备。
    5.2 业务功能调查
    5.3 业务流程调查

    订单信息:顾客点单产生的信息汇总
    配送状态:外卖小哥通过第三方平台传至前台的实时配送情况
    账单信息:收银台通过汇总报给财务的费用报表
    缺货告知:前台通过可生产菜品余量与以生产数量对比,当数量不足告知顾客不可选的菜品
    菜品明细:前台根据订单信息产生的菜品所需原料的明细单
    加工进度:厨房根据每个工位上菜品所处的未加工/在加工/已完成状态告知给前台
    物料单:厨房生产所需原料的明细单
    可生产菜品余量:仓库根据每天的库存余量计算出对应可生产菜品的数量



    已取消菜品:顾客因需求变动取消订单中的菜品
    取消菜品信息:顾客取消的菜品的明细单
    取消订单信息:顾客取消订单的明细
    退款单:顾客因故取消订单产生的费用明细



    预备菜品:厨房根据订单信息产生的菜品而做的物料准备工作
    菜品明细:前台通过订单产生的菜品所需物料明细单
    取料单:厨房所需取得的物料的明细
    任务分配单:厨房根据具体订单与工位状态产生的生产计划
    工位信息:工位所处的状态
    出菜信息:生产加工完成的菜品的信息



    缺货信息单:仓库根据库存监控得到库存不足的物料的明细单
    菜品需求预测:前台根据以往数据产生的需求旺盛的菜品的种类明细
    采购账单:采购所需费用的明细单
    物料商品订单:采购部根据缺货信息单制定的物料明细单
    费用单:采购所需的费用
    到货验收单:入库检查时收到的单据



    招聘准则:餐厅经理通过企业文化制定的员工要求
    职工信息表:招收进入餐厅的职工信息
    员工工资报表:员工工资的具体费用单据
    员工分配表:人事管理部门通过员工的具体信息产生的分工表
    服务反馈信息:通过前台收集的反馈单产生的反馈信息



    结算费用报表:收银员汇总的顾客消费费用的表单
    采购费用报表:采购所需费用的具体明细单
    报销费用:财务通过采购费用报表产生的报销金额
    钱款:顾客消费产生的费用
    工资标准:人事部通过不同工种的具体要求产生的薪资标准
    工资:员工收到的薪资
    财务汇总单:财务部门通过各类费用报表产生的收支明细汇总

    5.4 数据汇总5.4.1 ER模型
    5.4.2 数据项
    5.4.3 U/C矩阵
    5.5 数据流程图环境图

    零层图

    前台服务二级DFD

    厨房生产加工二级DFD

    库存采购二级DFD

    人事二级DFD

    财务二级DFD

    5.6 数据字典5.6.1 数据结构
    5.6.2 数据流
    5.6.3 数据存储
    5.6.4 外部实体
    5.6.5 逻辑处理
    6 数学模型6.1 厨房生产模型本系统采用多工位-多菜品(即一个工位可以独立加工多种菜品,一种菜品可以由多个工位加工)的生产模式。工位-菜品关系附表。
    对于所有订单菜品,定义各工位正在加工的、及其后一个待加工菜品为已分配菜品,其余的(如果存在)为未分配菜品,本模型在先到先服务的菜品加工顺序下以最小化每个订单完成时间为目标来决策未分配菜品的加工工位。
    每当一个订单到来既进行一次决策,若工位当前空闲或仅有一个正在加工菜品,则决策所得该工位的前2或1个可加工菜品直接分配给该工位作为已分配菜品,否则为未分配菜品,待下一订单到达时重新决策,每个工位按当前决策结果顺序加工。
    目标函数如下:

    其中, Fj为由j订单的菜品i实际完成时间fi,j组成的列向量,Ej为订单j的菜品i预期完成时间ei,j组成的列向量,a为订单超时惩罚系数,fi,j与决策变量即j订单i菜品加工工位xi,j存在确定函数关系:

    即根据工位xi,j及加工顺序qi,j(先到先服务,即取决于 )依次递推可得j订单的菜品i实际完成时间,而

    其中tj为订单j下单时间,ti,j 为菜品加工所需时间。
    该模型使用遗传算法求解,变量表述如下:

    其中,ai为菜品编号,bj为订单编号,xi,j为工位编号,qi,j为工位内加工顺序,形如:

    xk列向量即为个体编码,取种群数量100,交叉概率0.3,变异概率0.2,迭代次数100,以如下订单菜品信息为例进行求解(暂未考虑不同工位对同菜品加工时间不同):

    优化结果[1, 2, 3, 4, 1, 3, 4, 3, 4, 2, 3, 1, 3, 2, 1, 2, 2, 1, 4],相较随机分配可提升约25%的总体工作效率,且在任务数量不多时可大幅降低订单超时率,本例订单超时率较随机分配降低约30%。
    7.系统设计7.1 功能结构图设计7.1.1 层次模块结构图
    7.1.2 H图
    7.2 系统流程图7.2.1 系统流程-顶层
    7.2.2 系统流程-前台
    7.2.3 系统流程-厨房
    7.2.4 系统流程-库存采购
    7.2.5 系统流程-人事
    7.2.6 系统流程-财务
    7.3 程序设计7.3.1 确认订单程序
    7.3.2 厨房任务分配流程
    8 程序截图首页
    4 评论 102 下载 2018-11-06 11:52:45 下载需要8点积分
  • 基于Android Studio开发的笔记APP

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

    JDK 1.8compileSdkVersion 27
    程序的组成部份

    Xml布局文件Java程序文件



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

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


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

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

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

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

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

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

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

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

    摘 要分布式文件系统,在当今集群存储中起着重要的作用,其中moosefs更是当中的佼佼者,他是一种分布式网络系统,它分布在多个计算机节点上面,每个节点只会存储整个文件的一部分内容,它有多重备份,易于部署,方便扩容等诸多的优点,而且它能够统一接口,使用它就像使用普通的文件系统一样,而且它对小文件的处理,性能优秀。它采用当今主流的Master /Chunk 设计,而且有强大的日志功能,数据恢复更有保证。
    本文所提及实现的是一个基于分布式文件系统的网盘,主要是通过大量的分布式存储节点提供存储服务,再通过指定的服务器进行挂在到本地,对外提供存储服务。提供统一的开放接口,提供给开发者使用。
    此分布式网盘,可以提供数据自动备份,将数据备份为3份,备份到不同的节点至上,从而使得数据的安全性大大的提高。
    整个开放分布式网盘,主要由三个部分组成,Moosefs,分布式网盘,分布式网盘开放接口。Moosefs主要提供分布式存储,备份,冗余等功能,分布式网盘主要提供逻辑上的操作,开放接口,是分布式网盘对外提供服务的桥梁,开发者通过API来使用分布式网盘,从而提高分布式网盘的使用范围。
    关键字: 分布式文件系统,Moosefs,数据恢复
    AbstractThe distributed file system plays an important role in today’s clustered storage, which moosefs is the leader among them, it is a distributed network system, it distribution in multiple computer take nodes above, each node only will store the entirepart of the contents of the file, it has multiple backup, easily deploy, convenient expansion, and it to unified interface, use it just like ordinary file system,and its small file handing, performance is excellent. It takes the Master/Chunk of today’s mainstream design, and robust logging functionality, data recovery is more assured.
    This article is a reference implementation of a distributed is a network-base distributed file system disk, primarily through a large number of distributed storage nodes providing storage services, and then by the specified server hanging on to local , to provide storage service.
    This distributed network that provides automatic data backup, data backup 3, first backup to a different node so that greatly improve data security.
    Open distributed Web site as a whole, is composed of three key components, Moosefs, distributed networks, distributed network of open interfaces. Moosefs primarily provide a distributed storage, backup, redundant functions, operation on a distributed network disk provides a logical, open interfaces, distributed Web site provides services that bridge developer API to use a distributed network, so as to improve the use of distributed network disk.
    Key words: distributed file system; Moosefs; data recovery
    1 引言1.1 课题背景与现状现在网络技术的发展和深入的应用,互联网上的信息呈现爆炸式的增长,大数据,大批量的小文件等等的处理能力越来越重要,尤其是以javascript,css,图片,音频,和视频等文件形式的小中型数据的增长尤为迅速,如何高效的,方便的,安全的,存储和管理这些数据,并且快速的读取,写入等等,已经成为了业界一个难题。
    现在计算机技术,人们的需求,正在迅速的发展,几百万亿字节的存储,已经是司空见惯了,cpu,内存等等指标正在不断的提高,但是磁盘的存储能力却一直很难有质的飞跃,对于大数据,大量的文件的处理,人们一直为之头疼。人们对于存储系统的速度,容量以及可靠性提出了极高的要求。
    由此,分布式文件系统油运而生,若能解决这个问题,则未来几年的存储将会不再是Internet发展的瓶颈或者是制约因素,而且基于大数据的应用,更加能够促进国民经济的发展。
    1.2 选题价值与意义关与分布式存储,将信息分布式存储,首先对骨干网络的带宽相对要求较低,其次是有较高的容错性能,之后,分布式存储,相对于集中式存储,每个节点相对独立,而且数据分散,数据的安全性的得到了保证。
    选择研究分布式存储,当前,最火的词汇就是云存储,移动与联网,而这两个东西,都离不开分布式存储,因为他们都会产生大量的数据,大量的数据就需要好的存储系统,对存储要求更高。而本地文件系统由于是单个节点本身的局限性,已经很难满足海量数据存取的需求了,因此,不得不借助分布式文件系统来解决这一难题,把系统的负载转移到多个节点之上。如果我能在学校了解到这方面的知识,将会更好的适应外面的企业。符合外面企业的要求。
    1.3 研究内容在这个课题中,我主要的研究内容是基于Moosefs的分布式存储,其他的内容也会涉及,和Moosefs进行对比。而且我更侧重与中小文件的性能,对大文件的要求不高。
    1.4 论文结构本文第一章为引言主要是介绍技术的背景和现状,研究这个问题的意义所在。第二章主要是进行分布式文件系统基础知识的讲解,包括流行的hadoop,GFS,TFS等当前流行的文件系统的介绍。第三章主要是将Moosefs文件系统的系统架构,包括它系统架构,机器架构等相关的知识介绍。第四章主要是介绍的基于Moosefs文件系统搭建的网盘实现,包括系统分析,设计,实现等核心技术问题。第五章主要是对搭建的网盘进行测试。
    2 分布式文件系统的介绍2.1 研究方法和技术路线当前的存储分为两大类型,分别是分布式存储和集中式存储。
    关于集中式共享存储,这类多处理机在目前至多有几十个处理器。由于处理器数目较小,可通过大容量的Cache和总线互连使各处理器共享一个单独的集中式存储器。因为只有一个单独的主存,而且从各处理器访问该存储器的时间是相同的,所以这类机器有时被称为UMA(Uniform Memory Access)机器。它的优点就是数据集中,方便管理,但是也存在许多的缺点,其中由于数据太集中,容灾性差,是它致命的缺点,如下图2.1.1是它的基本架构图。

    而关于分布式存储,它具有分布的物理存储器。为支持较大数目的处理器,存储器必须分布到各个处理器上,而非采用集中式,否则存储器系统将不能满足处理器带宽的要求。系统中每个结点包含了处理器、存储器、I/O以及互连网络接口。随着处理器性能的迅速提高和处理器对存储器带宽要求的不断增加,甚至在较小规模的多处理机系统中,采用分布式存储器结构也优于采用集中式共享存储器结构(这也是不按规模大小进行分类的一个原因)。当然,分布式存储器结构需要高带宽的互连。

    将存储器分布到各结点有两个好处,其一,如果大多数的访问是针对本结点的局部存储器,则可降低对存储器和互连网络的带宽要求,其二,对局部存储器的访问延迟低。分布式存储器体系结构最主要的缺点是处理器之间的通信较为复杂,且各处理器之间访问延迟较大。
    通常情况下,I/O和存储器一样也分布于多处理机的各结点当中。每个结点内还可能包含较小数目(2~8)的处理器,这些处理器之间可采用另一种技术(例如通过总线)互连形成簇(cluster),这样形成的结点叫做超结点。采用超结点对机器的基本运行原理没有影响。由于采用分布式存储器结构的机器之间的主要差别在于通信方法和分布式存储器的逻辑结构方面,所以本章将集中讨论每个结点只有一个处理器的机器。
    2.2 Hadoop中HDFS介绍Hadoop分布式文件系统(HDFS)被设计成适合运行在通用硬件(commodity hardware)上的分布式文件系统。它和现有的分布式文件系统有很多共同点。但同时,它和其他的分布式文件系统的区别也是很明显的。HDFS(Hadoop Distributed File System)是一个高度容错性的系统,适合部署在廉价的机器上。HDFS能提供高吞吐量的数据访问,非常适合大规模数据集上的应用。HDFS放宽了一部分POSIX约束,来实现流式读取文件系统数据的目的。HDFS在最开始是作为Apache Nutch搜索引擎项目的基础架构而开发的。(陆嘉桓 etc 2012),以下是HDFS的架构图。

    HDFS中进行数据存储的时候,将数据分成多分存储到不同的节点中。如下图2.2.2 所示,HDFS有如下的有点,如硬件故障检测和自动快速恢复,因为HDFS设计之初就以大量的廉价机器为设计初衷,它把硬件故障认为是常态而不是异常。其次,流式数据的数据访问模型,HDFS是被手机城市和处理批量的,而不是用户交互式,它的重点是数据的吞吐量,而不是数据的反应时间。再者,大数据集,典型的HDFS文件大小是GB到TB的级别,他提供了很高的聚合数据带宽,一个集群之中支持数百个节点,故而保证了可以支持到大数据集,而且有简单的一致性模型,HDFS设计之初,就对文件操作进行了定位,为一次写多次读取的操作模式,一个文件一旦被创建,写入,关闭之后就不需要修改。通过这个简单化的数据一致性问题和并使得高吞吐量的数据访问变成了可能。先进的NameNode 和DataNode设计,HDFS是一个主从结构,一个HDFS由一个NameNode,它是管理文件命名空间和调节客户端访问文件的主服务器,还有许许多多的DataNode,他是管理对应节点的存储,HDFS对外开放文件命名空间,而且允许用户数据源文件的形式进行存储。优秀的文件系统的元数据的持久化功能,HDFS的命名空间是由NameNode节点来存储。NameNode使用叫做EditLog的事务日志来持久记录每一个对文件系统数据的改变,从而保证文件系统的持久化。

    当然,HDFS也有一些缺点,如对小文件处理较为困难,因为HDFS默认64M,只有当本地机器存储超过此空间才会将数据写入到HDFS中,所以若遇到大量的小文件,就相对吃力了,而且如果访问大量的小文件,需要从一个DataNode跳到另外一个DataNode,从而严重影响性能。再者,访问有一定的延时,主要是两个方面,由于HDFS设计之初,就是以吞吐量为重点,从而相对忽略了延时问题,其二是Java本身效率不高。最后,也是最为人诟病的,只能支持单个Master,如Master出现了故障,整个集群将不能提供服务,从而造成不能预计的损失
    2.3 传统NFS文件系统介绍NFS是Network File System的简写,即网络文件系统.网络文件系统是FreeBSD支持的文件系统中的一种,也被称为NFS. NFS允许一个系统在网络上与他人共享目录和文件。通过使用NFS,用户和程序可以像访问本地文件一样访问远端系统上的文件。
    NFS至少有两个主要部分:一台服务器和一台(或者更多)客户机。客户机远程访问存放在服务器上的数据。为了正常工作,一些进程需要被配置并运行。
    NFS 有很多实际应用。下面是比较常见的一些,多个机器共享一台CDROM或者其他设备。这对于在多台机器中安装软件来说更加便宜跟方便。在大型网络中,配置一台中心 NFS 服务器用来放置所有用户的home目录可能会带来便利。这些目录能被输出到网络以便用户不管在哪台工作站上登录,总能得到相同的home目录。几台机器可以有通用的/usr/ports/distfiles 目录。这样的话,当您需要在几台机器上安装port时,您可以无需在每台设备上下载而快速访问源码。
    以下是NFS最显而易见的好处,本地工作站使用更少的磁盘空间,因为通常的数据可以存放在一台机器上而且可以通过网络访问到。用户不必在每个网络上机器里头都有一个home目录。Home目录 可以被放在NFS服务器上并且在网络上处处可用。诸如软驱,CDROM,和 Zip(是指一种高储存密度的磁盘驱动器与磁盘)之类的存储设备可以在网络上面被别的机器使用。这可以减少整个网络上的可移动介质设备的数量。
    当然NFS也有很多的缺点,容错性不佳,和其他分布式文件系统相比,NFS不具备数据备份功能,而且日志功能较为简单,很难进行数据恢复。最不好的是他的可扩展性能,NFS只能支持类UNIX系统,对windows系统不支持,而且同时使用用户较低。
    2.4 闭源的GFS介绍GFS是一个可扩展的分布式文件系统,用于大型的、分布式的、对大量数据进行访问的应用。它运行于廉价的普通硬件上,但可以提供容错功能。它可以给大量的用户提供总体性能较高的服务。
    GFS 也就是 google File System,google公司为了存储海量搜索数据而设计的专用文件系统。以下是GFS的体系架构图。

    GFS有点很多高可用性,如果一个GFS客户失效,数据可以通过其它的GFS高效访问,可扩展性能,不需要中心数据中心,容易扩展存储的容量和访问的带宽,对大数据处理能力强。容错能力强,它的数据同时会在多个chunk server上进行备份,具有相当的容错性能。
    当然GFS也有许多的缺点,如没有实现像POSIX那样标准的API,从而若使用GFS,则很多上层的应用需要修改。文件在目录中按层次组织起来并有路径名标示,对大量的小文件处理不佳,当大量的小文件访问的时候,将严重的影响性能,单Master设计,Master会压力过大,可能会让整个集群宕机。
    2.5 开源的TFS介绍TFS(Taobao File System)是一个高可扩展、高可用、高性能、面向互联网服务的分布式文件系统,主要针对海量的非结构化数据,它构筑在普通的Linux机器集群上,可为外部提供高可靠和高并发的存储访问。TFS为淘宝提供海量小文件存储,通常文件大小不超过1M,满足了淘宝对小文件存储的需求,被广泛地应用在淘宝各项应用中。它采用了HA架构和平滑扩容,保证了整个文件系统的可用性和扩展性。同时扁平化的数据组织结构,可将文件名映射到文件的物理地址,简化了文件的访问流程,一定程度上为TFS提供了良好的读写性能。
    TFS有不少有优点,首先,它是由两个NameServer节点组成(一主一备)和多个DataServer组成,而且这些服务程序是作为一个用户级别的程序运行在普通Linux机器上面,它是将大量的小文件合并为一个大文件来进行存储 。同时为了容灾,NameServer采用HA结构,即两台机器互为热备份,同时运行,一主一备,当主机宕机后,迅速切换到备份NameServer中,对外提供服务。TFS设计是针对小文件设计的而且会将写请求尽可能的均匀的分布在不同的DataServer之中,而且用平滑的扩容机制,当需要系统扩容时,只需要对将相对应的心DataServer部署好,启动即可。
    当然也有不少缺点,如对大文件处理不佳,因为TFS大小的块大小为64M,设计初衷是为小文件设计,所以大文件处理不佳。
    2.6 较新Moosefs文件系统介绍Moosefs是一款网络分布式文件系统,它把数据分散在多台服务器上,但是对用户来讲,看到的只是一个源。MFS也像其他类Unix文件系统一样,包含了层次结构(目录树),存储着文件属性(权限,最后访问和修改时间),可以创建特殊的文件(块设备,字符设备,管道,套接字),符号连接,硬链接等。
    MooseFS文件系统结构包括以下四种角色:管理服务器managing server (master),元数据日志服务器Metalogger server(Metalogger),数据存储服务器data servers(chunkservers),客户机挂载使用client computers。
    Moosefs拥有以下的特性,首先,它是免费的,因为它支持的是GPL协议,其次它拥有通用文件系统,不需要修改上层应用就可以使用,可以直接挂载到本地,像使用本地文件一样,并且可以在线扩容,体系架构可伸缩性极强。(官方的case可以扩到70台了!),而且部署简单,只需要修改一下配置文件就能运行了。高可用,可设置任意的文件冗余程度,提供比raid1+0更高的冗余级别,而绝对不会影响读或者写的性能,只会加速!可回收在指定时间内删除的文件,“回收站”提供的是系统级别的服务,不怕误操作了,提供类似oralce 的闪回等高级dbms的即时回滚特性!提供netapp,emc,ibm等商业存储的snapshot特性,可以对整个文件甚至在正在写入的文件创建文件的快照,google filesystem的一个c实现。
    MFS 的读取数据过程:

    Client当需要一个数据的时候,首先向master server发起查询请求
    管理服务器检索自己的数据,获取到数据所在的可用的数据服务器位置ip|port|chunkid
    管理服务器将数据服务器的地址发送给客户端
    客户端向具体的数据服务器发起数据获取请求
    数据服务器发送给客户端

    如下图所示,MFS读取数据时原理图。

    MFS的写数据过程:

    当客户端有数据写需求时,首先向管理服务器提供文件元数据信息请求存储地址(元数据信息如:文件名|大小|份数等)
    管理服务器根据写文件的元数据信息,到数据服务器创建新的数据块
    数据服务器返回创建成功的消息
    管理服务器将数据服务器的地址返回给客户端(chunkIP|port|chunkid)
    客户端向数据服务器写数据
    数据服务器返回给客户端写成功的消息
    客户端将此次写完成结束信号和一些信息发送到管理服务器来更新文件的长度和最后修改时间
    一个写操作就此完成

    下面是MFS 写数据原理图如图2.6.2 所示:

    MFS的删除文件过程:

    客户端有删除操作时,首先向Master发送删除信息
    Master定位到相应元数据信息进行删除,并将chunk server上块的删除操作加入队列异步清理
    响应客户端删除成功的信号
    MFS修改文件内容的过程
    客户端有修改文件内容时,首先向Master发送操作信息
    Master申请新的块给.swp文件
    客户端关闭文件后,会向Master发送关闭信息
    Master会检测内容是否有更新,若有,则申请新的块存放更改后的文件,删除原有块和.swp文件块
    若无,则直接删除.swp文件块

    MFS重命名文件的过程:

    客户端重命名文件时,会向Master发送操作信息
    Master直接修改元数据信息中的文件名;返回重命名完成信息

    MFS遍历文件的过程:

    遍历文件不需要访问chunk server,当有客户端遍历请求时,向Master发送操作信息
    Master返回相应元数据信息
    客户端接收到信息后显示

    2.7 各种分布式文件系统的概要分析首先,文本首先对需求进行分析,对这个文件系统的定位是大量普通用户使用的一个海量存储文件系统。正常情况下,普通用户的文件以文档,图片,音频,视频居多。大部分文件在10M以下。
    其次,普通的用户,一般情况下以上传信息居多。而且文件系统要易于扩展,方便扩容,再者文件系统要有较强的容错性,最好自身带有备份功能。
    关于Hadoop的分析,HDFS上,每一个文件都需要在namenode上建立一个索引,一个索引大约150byte,当小文件比较多的时候(小于一个block被HDFS视为一个小文件),就会产生许许多多的索引文件,一方面,会大量的占用NameNode的内存空间,另一方面,就是索引文件过大,会让索引速度变得很慢。HDFS的设计思路是多次读,少量写操作,这个和我们定位有所不同。
    关于NFS的分析,NFS 搭建简单,但是可扩展性能差,NFS不带备份功能。NFS 可以搭建的最大容量有限。
    关于GFS的分析,由于是GFS闭源,即使性能很优秀,但是不能使用。
    关于TFS的分析,对小文件处理优秀,但对稍大的文件处理困难。
    关于Moosefs的分析,MFS自带备份功能,MFS对小文件处理能力强,MFS对大文件处理能力强,MFS自带日志功能,MFS可扩展性能强。
    综上所述,选择Moosefs。原因如下,首先,它对大文件,小文件的支持力度较高,其次它有着较强的可扩展性能,再者容错备份功能齐全。故而选之。
    3 文件系统的架构3.1 平台要求目前Moosefs只能支持类UNIX操作系统,如Linux内核的操作系统,Unix如Solaris,MacOx等,不支持Windows。
    若是Linux内核,建议使用2.6或者以上内核,才能很好的支持到FUSE功能,可以将Moosefs很好的挂在到本地使用。
    3.2 机器架构
    Moosefs主要是由四种类型的角色,它们分别是Master(主控服务器),主要负责各个数据存储服务器的管理,文件读写调度,文件空间回收以及恢复,多节点的拷贝等等功能,Metalogger(日志服务器)主要负责Master服务器的变化的日志文件,若Master Server出现问题的时候,可以接替进行工作,ChunkServers负责管理服务器,听从Master调度,并提供存储空间,为客户提供数据传输,Client,主要是通过fuse内核接口挂接远程的Master服务器上面,看起来共享的文件系统和本地的Uinx文件系统使用效果一样。
    3.3 文件系统架构
    Moosefs分布式文件系统,主要是通过一个master来管理多个chunkservers进行分布式存储,让整个chukservers群中的存储空间得到共享,通过metalogger服务器进行日志记录和数据删除后的回滚。用户通过fuse来将分布式文件系统挂载到本地,想访问普通的文件系统一样,提工一个抽象,使得用户在不需要更改上层应用的情况下,可以使用分布式文件系统。
    3.4 分布式文件系统的搭建采用了8台主机搭建实验使用的分布式文件系统,一台主服务器,作为Master,一台日志服务器,用于日志备份和记录,四台ShunkServer,用于存储数据,之后将MFS挂载在两台机器上面,有两台clinet对外提供服务。
    以下是文件系统的架构图,如下图3.5.1所示。它们统一安装的Archlinux 内核版本是Linux 3.8,java版本是JDK7,安装了FUSE。

    3.5 性能测试


    二级100*100目录
    创建
    列表
    删除




    单片15k.5 ext3 client单进程
    real 0m0.762s user 0m0.048s sys 0m0.261s
    real 0m0.179s user 0m0.036s sys 0m0.125s
    real 0m0.492s user 0m0.036s sys 0m0.456s


    单片15k.5 ext3 client 10并发进程
    real 0m0.724s user 0m0.015s sys 0m0.123s
    real 0m0.057s user 0m0.006s sys 0m0.025s
    real 0m0.226s user 0m0.010s sys 0m0.070s


    6chunkserver cache client单进程
    real 0m2.084s user 0m0.036s sys 0m0.252s
    real 0m4.964s user 0m0.043s sys 0m0.615s
    real 0m6.661s user 0m0.046s sys 0m0.868s


    6chunkserver cache client 10并发进程
    real 0m1.422s user 0m0.007s sys 0m0.050s
    real 0m2.022s user 0m0.008s sys 0m0.108s
    real 0m2.318s user 0m0.008s sys 0m0.136s



    进行小文件性能测试,采取的是两级目录,1000*1000的方式。



    二级1000*1000目录
    创建
    列表
    删除




    单片15k.5 ext3 client单进程
    real 11m37.531s user 0m4.363s sys 0m37.362s
    real 39m56.940s user 0m9.277s sys 0m48.261s
    real 41m57.803s user 0m10.453s sys 3m11.808s


    单片15k.5 ext3 client 10并发进程
    real 11m7.703s user 0m0.519s sys 0m10.616s
    real 39m30.678s user 0m1.031s sys 0m4.962s
    real 40m23.018s user 0m1.043s sys 0m19.618s


    6chunkserver cache client单进程
    real 3m17.913s user 0m3.268s sys 0m30.192s
    real 11m56.645s user 0m3.810s sys 1m10.387s
    real 12m14.900s user 0m3.799s sys 1m26.632s


    6chunkserver cache client 10并发进程
    real 1m13.666s user 0m0.328s sys 0m3.295s
    real 4m31.761s user 0m0.531s sys 0m10.235s
    real 4m26.962s user 0m0.663s sys 0m13.117s



    由此分析可见,当进行单盘多进程操作的时候,性能提升的空间有限,主要是IO瓶颈制约,甚至消耗了大量的系统调用,当进行多盘的时候,性能有了巨大的提升。
    4 开放网盘的实现方式4.1 需求分析首先我们对本系统进行定位,系统供开发者使用,他们一般都具备一定的计算机水平,可以熟练的操作计算机,数据库,操作系统等等。而且要具备如下的功能,具有上传,下载功能的开放接口,具有权限控制的接口,具有安全快捷认证的用户接口,具有注册用户,修改用户信息的接口,具有显示目录信息的接口具有删除文件,目录的接口,具有移动文件,目录的接口,具有数据删除后恢复功能,并提供开放接口。而且要自动帮用户进行数据备份。
    4.2 系统分析系统主要用例,开发者通过用户的授权,获取权限,进行操作,如修改文件的目录,获取文件的信息,下载文件,修改文件内容,移动文件等操作。

    Aouth2.0授权顺序图

    开发者首先向系统请求一个临时令牌,系统验证开发者的身份后,授予一个临时的令牌,开发者获取了临时令牌后,通过自己的信息和临时令牌,将用户引导至系统的授权页面请求用户的授权,在这个过程中临时令牌和开发者毁掉连接发送给系统,用户在系统提供的页面输入用户名和密码,然后授权该开发者访问锁请求的资源,授权成功后,系统将引导用户返回到开发者的页面,开发者根据临时令牌,从系统中获取访问的临牌,系统根据临时临牌和用户的授权情况授予开发者访问令牌,开发者获得令牌后,访问系统中用于受保护的资源。
    用户获取目录信息顺序图,如下图 4.2.3所示,用户请求获取指定目录的信息,系统首先查看请求中是否含有授权,若有授权则通过,否则将拒绝。当权限允许的时候,调用封装好的Driver进行访问指定的路径,获取目录信息,若不存在目录,则返回空,存在则将之保存为对象传给xml生成器,xml生成器生成xml,并将之返回给Servlet,Servlet将生成xml的响应回复给用户。

    以下是上传文件顺序图用户请求上传指定文件,系统首先验证用户的请求是否合法,若有授权则通过,否则将拒绝,当权限允许的时候,首先对文件的类型进行检测,是否符合系统要求,不符合则拒绝,之后获取上传文件列表,将文件上传至指定的目录中,首先判断目录是否存在,若不存在则递归的创建目录,并上传,最后生成上传报告,并将上传报告返回给用户。

    4.3 网盘的架构普通用户,通过开发者的应用来进行使用网盘,开发者同过网盘提供的API来进行访问网盘,进行操作,而网盘和分布式文件系统相对独立,网盘调用分布式文件系统中封装好的接口,进行分布式文件系统的使用。
    总体来说,设计的时候,把网盘分成了3层架构,分别是存储层,逻辑层(基础管理层),应用接口交互层。
    存储层采用Moosefs来做为存储实现的。由Moosefs来进行存储,将其挂在到本地使用,对外提供服务。
    逻辑层,首先有一些本地I/O的底层驱动,还有和数据库访问的驱动封装,其他的就是网盘的核心部分,包括用户认证,上传下载文件,数据封装等等。
    最表层的是应用接口层,主要是通过API来调用逻辑层的实现,从而可以使用到网盘。

    网盘的架构图,本文采用的是MVC架构模型。

    View:主要是通过jsp 和servlet来进行展现,主要是用于和API进行交互
    Control:主要是自己进行写逻辑,DAO是主要是实现简易的与mysql进行通讯的,而FSDriver主要是封装了很多常用的访问文件系统的接口
    Model:主要使用的JavaBean


    4.4 数据表结构用户注册的实现中,我们采取的是简单的认证体制,我们建立一个数据库表。
    我们当获取了规定用户的密码必须上传为SHA1,不能上传明文默认是以ID为主键,而且ID可以自己增长,不需要有默认值,用户名默认不支持中文,优先使用英文,关于密码,我们将密码进行了SHA1加密,当获得了密码后立刻进行SHA1加密,故系统本身不知道密码,从而保证用户的隐私,UserKey,是用于存储用户访问令牌,通过这个和表UserKey表进行关联,从而获取用户的访问令牌,保证用户同时可以拥有多个访问令牌。
    User Table



    名称
    类型
    长度
    是否允许空值
    默认值
    是否是主键
    说明




    ID
    Int
    64



    编号


    UserName
    Char
    40

    Null

    用户名


    Password
    Char
    60

    Null

    密码


    Sex
    Char
    10



    性别


    UserKey
    Char
    60

    Null

    访问的令牌



    对于开发者,我们创建了另外一个表来存储信息,ID是表的主键,用于区别不同的开发者,而且不需要有是否有空,AppKey 用于当开发者申请开发者身份的时候获取的唯一绝对的认证,可以通过这个AppKey来确定一个开发者,AppSecret,主要使用Aouth认证的时候,通过AppSecret获得唯一的认证的临时令牌。
    Developer Table



    名称
    类型
    长度
    是否允许空值
    默认值
    是否是主键
    说明




    ID
    Int
    64



    编号


    UserName
    char
    40

    Null

    用户名


    Password
    Char
    60

    Null

    密码


    Sex
    Char
    10



    性别


    AppKey
    Char
    60

    Null

    应用的key


    AppSecret
    Char
    60

    Null

    应用的密码



    为了保证多个开发者可以为同一个用户服务,而创建的这个表,其中存储的主要是用户的授权UserKey 和AppKey的对应关系。
    User Table



    名称
    类型
    长度
    是否允许空值
    默认值
    是否是主键
    说明




    ID
    Int
    64



    编号


    UserName
    char
    40

    Null

    用户名


    UserKey
    Char
    60

    Null

    用户令牌


    AppKey
    Char
    60

    Null

    应用的key



    4.5 核心代码实现上传功能上,我们采用的是FileUpload这个开源库,FileUpload 是 Apache commons下面的一个子项目,用来实现Java环境下面的文件上传功能。它可以支持上传一个或者是多个文件,并且支持控制缓存空间的控制,可以手动的控制缓存的位置,是在硬盘或者是内存都是可以实现的。还可以限制上传文件的大小。支持RFC 1867标准。而且API简单易学。下面是使用FileUpload的核心代码,实现以下功能,包括进行文件类型过略,上传文件列表,设置缓存,进行目录检测,设置响应体格式等等工作。当上传成功后,将会生成上传日志,并将上传的日志展示给用户,让用户知道自己上传成功。
    PrintWriter out = response.getWriter(); if(!ServletFileUpload.isMultipartContent(request)){ return ; }List<FileItem> filelist = null; try{ filelist = sfu.parseRequest(request); }catch(FileUploadException ex){ out.println("upload file error "+ex.toString());} if (filelist ==null || filelist.size() ==0){ out.println("could not find the file "); return; } Iterator fileItr = filelist.iterator(); while(fileItr.hasNext()){ FileItem fileItem = null; String path = null; long size = 0; fileItem = (FileItem)fileItr.next(); if(fileItem == null||fileItem.isFormField()){ continue; }String t_ext = t_name.substring(t_name.lastIndexOf(".") + 1); int allowFlag = 0; int allowedExtCount = allowedExt.length;for (; allowFlag < allowedExtCount; allowFlag++) { if (allowedExt[allowFlag].equals(t_ext)) break; path = fileItem.getName(); size = fileItem.getSize(); if("".equals(path)||size == 0){ out.println("Upload file error"); return ; } String t_name = path.substring(path.lastIndexOf("\\") + 1); String t_ext = t_name.substring(t_name.lastIndexOf(".") + 1); int allowFlag = 0; int allowedExtCount = allowedExt.length; for (; allowFlag < allowedExtCount; allowFlag++) { if (allowedExt[allowFlag].equals(t_ext)) break; } if (allowFlag == allowedE String u_name = request.getRealPath("/") +"imagesUploaded/"+t_name; if(!new File(request.getRealPath("/")+"imagesUploaded").isDirectory()) new File(request.getRealPath("/")+"imagesUploaded").mkdirs();}
    删除文件功能,即删除用户对应的文件或者目录,删除完成后,返回是否删除成功。代码首先判断文件是否存在,之后判断文件权限是否允许,最后才决定是否删除,删除后会有返回信息产生。
    以下是删除文件的核心代码:
    public void removefiles(HttpServletRequest request, HttpServletResponse response) throws IOException{ /*获得writer*/ PrintWriter out = response.getWriter(); RemoveFile remove = new RemoveFile();/创建一个removed的类/ String RemovePath = (String) request.getAttribute("RemovePath"); if (RemovePath == null){ out.println("path is null"); return ; } String temp = null; try { temp = "/mnt/mfs"+ RemovePath; remove.RmFile(temp); } catch (InterruptedException e) { // TODO Auto-generated catch block out.println("remove file error"+RemovePath); System.out.println("remove file error" +temp);}/*信息返回*/ out.println("OK to remove " + RemovePath); }
    获取文件信息,当用户想知道自己某个目录的信息的时候,可以调用此接口,它将获取目录的信息,进行过滤加工,将信息封装为xml返回给用户。
    我主要是用的是一个javax中原生的xml库,来生成xml,生成了xml之后,再将xml发送给用户。
    首先我获取文件的相关信息,把它们通过正则表达式来进行切割,分到不同的数组中,之后利用javax.xml下面的类来进行生成xml。
    下面是关键代码:
    通过系统调用,获取对应的文件目录信息,保存在对象之中。
    while ((lineStr = inBr.readLine()) != null) { /*对获取的信息进行整理*/ temp = lineStr.split("\\s{1,10}"); if (temp.length >5){ if (Permission.charAt(0) =='d'){ Dirctory = true; }else{ Dirctory = false; } Group = temp[2]; User = temp[3]; Size = temp[4]; CreateDate = temp[5]+" "+temp[6]+" "+temp[7]; Name = temp[8]; result.add(new NetDiskFile(Name,User,CreateDate,Size,Permission,Dirctory)); }}
    以下是生成xml的核心代码:
    Public static Document generateXml(List<NetDiskFile>list){ try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); doc = builder.newDocument(); root = doc.createElement("NetDiskFileInfo"); doc.appendChild(root); } catch (Exception e) { e.printStackTrace(); return null;//如果出现异常,则不再往下执行 } int len = list.size() ; Element element ; for (int i = 0; i < len; i++) { NetDiskFile NetDiskFile=list.get(i); element=doc.createElement("FileInfo"+(i+1)); element.setAttribute("fileName",""+NetDiskFile.getName()); element.setAttribute("User",NetDiskFile.getUser()); element.setAttribute("Group",NetDiskFile.getGroup()); element.setAttribute("Permission",NetDiskFile.getPermission()); element.setAttribute("Size",String.valueOf(NetDiskFile.getSize())); element.setAttribute("CreateDate",NetDiskFile.getCreateDate()); element.setAttribute("Dirctory",String.valueOf(NetDiskFile.getDirctory())); root.appendChild(element); } return doc;}
    5 测试用例由于做的主要是开放接口,故测试程序写的比较简陋。
    5.1 用户认证需要以下信息 用户名 密码 以及用户授权的key_number,首先,开发者通过引导用户到系统的授权页面,如下

    当用户授权后,若用户通过验证,则提示用户是否授权,如下图5.1.3 所示,其中,里面包含了许多的信息,比如你的用户名,你网盘里面的部分照片,开者者将获取的权限等等信息都会显示出来。若认证失败,则出现如下图 5.1.2所示。


    若认证成功则提供一串授权码,但必须是开发者去访问系统,则能得到一个授权码,也就是我们所说的令牌,如下图 5.1.4所示。

    经过测试 结果正确。
    5.2 上传下载上传文件功能测试:
    上传文件,只能上传指定类型的文件,如MP3,jpg等等,这是没有上传前,存储目录的情况 ,如下图 5.2.1 所示。

    首先我们测试上传一张图片,如图 5.2.2所示。

    上传后,网盘目录信息如图5.2.3所示可见,网盘是可以实现上传图片等功能,可以上传到指定的目录之中,而且可以获得文件的大小,修改时间,文件类型等内容。
    现在测试上传失败,上传一个vsd的绘图文件,来进行测试,结果如图所示,当我们上传文件时,如图 5.2.4所示,当上传之后,由于失败的到的结果是如图5.2.5 所示,显示上传失败,之后我们在查看目录信息,如图5.2.6所示,依旧是没有添加,据此,我们可以得到网盘实现了过略文件类型的功能。



    现在我们查看目录的信息,可以看到,目录中没有添加到刚才上传文件的任何信息,和刚才一样,所以可以据此判断,网盘具有拒绝指定类型的文件上传的功能。

    上传失败,和预期一样,所以网盘实现了拒绝指定文件类型的功能。符合要求。
    5.3 获取目录信息现在测试显示目录信息的接口,若访问,则显示目录的信息,如下图5.3.1 所示。

    如图所示,可以获取目录的相关信息,并生成对应的xml文件,其中包含了文件名称,大小,权限,时间等信息,符合要求。
    5.4 数据恢复功能现在测试数据恢复功能,首先删除刚才上传的图片,在回收站中,会有如下页面,如图 5.4.1所示。

    当选中需要恢复的文件后,单击恢复按钮,就能恢复。之后查看目录的信息,如下图5.4.2 所示,所以由此可见,网盘具有删除恢复功能。

    6 总结与展望大数据和分布式存储,是将来互联网发展所必须的,现在,微软对它旗下的Skydirver进行了全面的升级,国内的金山,有道,坚果等网盘巨头,都对自己的网盘进行了升级,可以相信,将来网盘将变成一种必须的工具,他方便访问,随时读取,没有地域的限制。所以,我相信,网盘和分布式将会大行其道。分布式的存储,将更加的流行,未来是大数据的时代,分布式能够很好的解决这个问题。
    参考文献
    董黎刚.分布式系统中的调度与缓存技术[M] .浙江工商大学出版社,2010
    明日科技编著. JSP开发技术大全[M]. 人民邮电出版社,2007
    麻志毅.面向对象分析与设计[M]. 机械工业出版社,2008
    陆嘉桓. Hadoop实战[M]. 机械工业出版社,2012
    刘振安,董兰芳. 面向对象技术与UML. 北京: 机械工业出版社,2007
    张晓东. Java 数据库高级教程[M]. 北京: 清华大学出版社, 2004
    周忠,吴威. 分布式虚拟环境[M],科学出版社
    刘军. 文件系统原理,机械工业出版社,2005
    淘宝核心技术. 淘宝文件系统[EB/OL] 2013 http://tfs.taobao.org/index.html
    George Coulouris .分布式系统概念与设计[M].机械工业出版社,2013
    Amand Rajarman,Jeffrey David Ullman Mining of Massive Datasets[M],人民邮电出版社
    Michat Borgchowski . Installing Moosefs Step by Tutorial[EB/OL] Gemius SA 2010 http://www.moosefs.org/tl_files/manpageszip/moosefs-step-by-step-tutorial-cn-v.1.1.pdf
    1 评论 1 下载 2019-10-14 11:30:45 下载需要15点积分
  • 基于Java Web开发方式实现的Android二维码支付系统APP

    一、产品设计思想模拟基于加密的二维码实现电子交易的系统,实现交易的迅速便捷,可以在web浏览器和移动端同时使用。采用Google.zxing接口实现二维码的生成和解析,采用加密协议传送,产品类似webapp形式呈现。开发利用DAO的设计模式,由javabean的VO,预先设定各项操作的接口类,真正实现操作的代理类和集成各项代理的工厂类,该设计模式使工程构造简明清晰,并且减少了耦合度。

    代码运行环境

    IDE:Eclipse Mars.1 Release (4.5.1)Jdk: 1.8
    数据库:mysql 5.7
    前端外部插件:bootstrap,jquery,
    后端外部插件:Google.zxing
    编辑工具

    eclipsewebstormDreamweaver

    二、采用的安全技术
    采用Google的zxing二维码生成与读取接口对信息进行加密,可以将支付信息保存入二维码中
    采用MD5加密算法,获取用户标识的不可逆散列值
    使用时间戳,限制二维码使用周期
    传输过程中使用SSL加密传输,保证传输过程安全

    三、验证过程
    如图所示,收款人提出收款后,根据其当前的用户名和时间戳分别产生HASH散列值,将散列值相拼接,然后使用Google的Zxing工具转化为二维码;付款人拍摄到二维码后,根据固定位数分离出收款人用户名的散列值和二维码生成时间的散列值,在数据库中遍历所有用户的的用户名的MD5散列值与目标对比,若匹配,则找到收款人,否则没有收款目标,拒绝收款,并打印警告;再使用当前时间段的时间戳生成MD5散列值,与目标MD5散列值比较,若相等,则说明支付时间与收款码生成时间在同一个时间间隔中,否则二维码过期,不进行交易。只有在找到收款人且二维码未过期的前提下才进行交易。
    后来增加了Tomcat的传输过程安全保护机制,又在本地使用JDK 自带的 keytool工具生成证书,将证书应用于Tomcat,在Tomcat服务器中配置启动了HTTPS协议替代HTTP协议,使用8443端口替代了8080端口,传输过程中的数据是用SSL协议加密后的密文,这样就增强了传输过程中的安全性。
    四、主要数据结构4.1 用户类用来存储用户信息
    public class user { private String username; private String password; private double balance; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public double getBalance() { return balance; } public void setBalance(double balance) { this.balance = balance; }}
    4.2 历史支付记录类用来存储
    public class payhistory { private String payer; private String accepter; private int money; private Timestamp time; public String getPayer() { return payer; } public void setPayer(String payer) { this.payer = payer; } public String getAccepter() { return accepter; } public void setAccepter(String accepter) { this.accepter = accepter; } public int getMoney() { return money; } public void setMoney(int money) { this.money = money; } public Timestamp getTime() { return time; } public void setTime(Timestamp time) { this.time = time; }}
    五、主要数据库表5.1 用户表
    Userid:用户标识
    Password:用户密码
    Balance:用户账号余额


    5.2 支付历史记录表
    Id:主键,唯一标识一次记录,无实际含义,自增
    Payer:付款人用户标识
    Accepter:收款人用户标识
    Money:交易金额
    Time:交易完成时间


    六、主要代码解析本项目使用DAO设计模式,代码框架如下

    Vo中保存映射数据表的简单java类;dao定义需要实现操作的接口;Impl中是接口的真实实现类,完成的是数据库的具体操作,但是不负责数据库的打开与关闭;Proxy,代理类,主要是完成数据库的打开、关闭,并且调用真实实现类对象操作;factory,工厂类,通过工厂类取得一个DAO的实例化对象;main,实现具体的服务。
    Css保存样式文件,js保存JavaScript文件,image保存生成的二维码图片文件,image_accept保存需要解析的收款二维码的照片/图片,web.xml是servlet映射文件,lib中保存引入的jar包。
    实现的主要功能如下:
    6.1 收款收款功能通过生成与收款人和交易时间相匹配的二维码来实现。用户登录之后使用用户名进行MD5加密,获取对应的散列值,再加上当前时间(使用currentTimeMillis方法获取,其得到与1970年一月一日之间的时间差,进而得到当前所在的十分钟的时间标识)。以字符串的形式与用户散列值进行拼接得到唯一表示此次收款交易的信息。使用Google.zxing将其转化为二维码形式,并在视图中显示出来。
    Gather.java
    String hash_userid = new md5().hashCode(userid.replaceAll("\n|\r", "")).substring(0, 5);//获取当前时间long t=System.currentTimeMillis()/(2*60*1000);//以十分钟为单位划分时间String tString = Long.toString(t);String text = hash_userid + tString;//二维码的内容text = text.replaceAll("\r|\n", "");int width = 400;int height = 400;String format = "png";Hashtable hints= new Hashtable();hints.put(EncodeHintType.CHARACTER_SET, "utf-8");BitMatrix bitMatrix = null;try { bitMatrix = new MultiFormatWriter().encode(text, BarcodeFormat.QR_CODE, width, height,hints);} catch (WriterException e) { // TODO Auto-generated catch block e.printStackTrace();}StringBuffer buf = new StringBuffer();buf.append("D:\\eclipse_x86_workspace\\payment3\\WebContent\\image\\to_");buf.append(userid);buf.append(".png");String pathname = buf.toString();System.out.println(pathname);pathname = pathname.replaceAll("\r|\n", "");//去掉可能出现的回车和换行System.out.println("换行第一次出现的地方:"+pathname.indexOf("\n"));File outputFile = new File(pathname);MatrixToImageWriter.writeToFile(bitMatrix, format, outputFile); String result = pathname.substring(45);System.out.println(result);
    6.2 付款付款时先扫描二维码,将扫描的图片传回服务器后进行读取。获取二维码中字符串,其前5位为用户HASH标识,之后部分为二维码生成的时间表示。先匹配时间,若当前时间与二维码中时间标识不同(相隔十分钟以上),则输出二维码已过期,停止交易;二维码未过期,再去数据库中匹配用户标识,即select所有用户,分别对其用户名进行MD5加密,取前五位(由于样本数量较少,考虑到二维码识别难度,暂取五位),若与二维码中用户标识相同,则找到收款人,进行交易,给收款人加上交易金额,给付款人减去交易金额,并将交易活动记录入数据库。
    //判断二维码是否过期long t=System.currentTimeMillis()/(10*60*1000);//以十分钟为单位划分时间String tNow = Long.toString(t);if (!tNow.equals(tString)) { out.println("out");}//开始计算收款人的useridtry{ accepter = DAOFactory.getUserDAOInstance().findAccepter(accepter);}catch(Exception e){ e.printStackTrace();}ph.setTime(new Timestamp(System.currentTimeMillis()));try{ //付款人减去金额 DAOFactory.getUserDAOInstance().pay(ul1, money);}catch(Exception e){ e.printStackTrace();}try{ //收款人加上金额 DAOFactory.getUserDAOInstance().accept(ul2, money);}catch(Exception e){ e.printStackTrace();}try{ //记录交易信息 DAOFactory.getPayhistoryDAOInstance().addHistory(ph);}catch(Exception e){ e.printStackTrace();}//付款人减去交易金额public boolean pay(user ul,double money) throws Exception { boolean flag = false; String sql = "update user set balance = balance - ? where userid = ? "; this.ps = this.conn.prepareStatement(sql); this.ps.setDouble(1, money); this.ps.setString(2, ul.getUsername()); System.out.println("ul.getBalance() - money:" + (ul.getBalance() - money)); System.out.println("ul.getUsername():" + ul.getUsername()); System.out.println(sql); this.ps.executeUpdate(); this.ps.close(); return flag;}//收款人加上交易金额public boolean accept(user ul, double money) throws Exception { boolean flag = false; String sql = "update user set balance = balance + ? where userid = ? "; this.ps = this.conn.prepareStatement(sql); this.ps.setDouble(1, money); this.ps.setString(2, ul.getUsername()); System.out.println(sql); this.ps.executeUpdate(); this.ps.close(); return flag;}public boolean addHistory(payhistory ph) throws Exception { System.out.println("into impl"); boolean flag = false; String sql1 = "insert into payhistory(payer,accepter,money,time) values(?,?,?,?)"; this.ps = this.conn.prepareStatement(sql1); this.ps.setString(1, ph.getPayer()); this.ps.setString(2, ph.getAccepter()); this.ps.setInt(3, ph.getMoney()); this.ps.setTimestamp(4, ph.getTime()); System.out.println(sql1); if(this.ps.executeUpdate()>0){ flag = true; } this.ps.close(); return flag;}
    6.3 余额查询先在DAO中定义活动的接口,然后在Impl中具体实现与表的交互,Proxy提供数据库代理,Factory提供活动入口,查询user表中相关元组即可。
    UserDAOImpl.java
    public double searchBalance(user ul) throws Exception { double balance = 0; String sql = "select * from user where userid = ? "; this.ps = this.conn.prepareStatement(sql); String userid = ul.getUsername(); //System.out.println("ul.getUsername():" + ul.getUsername()); this.ps.setString(1, ul.getUsername().replaceAll("\n|\r", "")); ResultSet rs = this.ps.executeQuery(); while(rs.next()){ System.out.println("i am here"); balance = rs.getDouble("balance"); } this.ps.close(); return balance;}
    6.4 查询历史记录用户登录之后,使用用户名遍历payhistory表即可。使用JSP打印出历史记录的表格(由于ajax的局部刷新的视觉效果不好,所以显示到另一页面,页面之间使用jQuery-params传值)。
    <% request.setCharacterEncoding("utf-8");%><% try { String keyWord = request.getParameter("kw"); //String keyWord = "admin"; System.out.println("keyword:" + keyWord); if (keyWord == null) { keyWord = ""; } List<payhistory> all = DAOFactory.getPayhistoryDAOInstance().findAll(keyWord); Iterator<payhistory> iter = all.iterator();%><center> <table border = "-2"class="table" style = "color:white;"> <tr> <td>payer</td> <td>accepter</td> <td>money</td> <td>time</td> </tr> <% while (iter.hasNext()) { payhistory ph = iter.next(); %> <tr> <td><%=ph.getPayer()%></td> <td><%=ph.getAccepter()%></td> <td><%=ph.getMoney()%></td> <td><%=ph.getTime()%></td> </tr> <% } %> </table></center><% } catch (Exception e) { e.printStackTrace(); }%>
    七、测试数据和结果首先,将项目使用Tomcat服务器在本地跑起来,打开WiFi,使用移动设备连入,则设备与服务器处于同一局域网中。
    在终端使用ipconfig指令获取WiFi局域网的IP地址,例如172.60.2.1,则在连入设备中输入https://172.60.2.1:8080/payment3 即可进入项目界面。
    注册一个名为admin用户,默认新注册用户账户金额都为10000元

    查看数据库,确实存在此用户

    使用admin账号登录

    图7
    收款,生成收款二维码

    注册另一账户,root,打开root账号的付款界面(选择文件就是调用摄像头)

    拍摄二维码

    输入金额,确定支付

    八、心得体会这个项目整体做了两个星期左右,主攻了大约一个星期,我负责后端,和二维码生成与读取以及服务器的SSL加密配置。
    项目开始时需要选择平台,原本打算使用Android写一个APP,不过由于笔记本电脑无法运行Android studio,又考虑到自己以前还是做过一些javaweb项目的,所以选择使用webAPP的方式呈现。
    使用bootstrap框架可以很好地支持移动端的响应式布局,使用ajax进行前后端之间的传值,用DAO设计模式以避开后端代码之间的耦合度太高问题,使用servlet处理请求,轻车熟路,这一部分做地很爽。
    二维码生成与破解是调用别人的接口来实现的。开始时选择的是一个日本公司的接口,只有两个jar包,而且方法也不算复杂,甚得我心,但是实现效果是在不够理想,只要光线稍微变化一些就识别不出来,屏幕上有一些暗影也不行。后来改成了Google的zxing,虽然效果也不算太好,同一二维码只有从特定的角度拍摄才能识别出来,但是至少不太复杂的二维码是没有问题的(后来在stackflow里看到有人使用hints,可以提高二维码的识别成功率,试用了一下,几乎没什么区别)。
    开始认为二维码算是一种加密算法,但是随着项目的推进和原型的成型,意识到二维码只是一种可逆的变化,加密程度太低。后来想到调用哈希算法取用户名的散列值,这样只有服务器端可以遍历数据表,确定二维码对应的收款人,而别人即使得到了二维码也没什么用。由于MD5为128位,SHA1位160位,为了处理二维码方便起见,选择了MD5算法。
    MD5算法调用java的security包内的方法稍加修改即可,这部分实现起来还是比较容易的,时间戳可以调用currentTimeMiles方法可以获取自1970年1月1日以来的毫秒数,将其除以十分钟(10*60*1000),即可得到当前所在十分钟时间区间的标识(即在十分钟内付款为有效)以此作为判断二维码是否过期的标准。
    3 评论 4 下载 2019-03-18 08:40:14 下载需要12点积分
  • 基于JSP的图书馆管理系统设计与实现

    摘 要随着科学技术的进步,计算机行业的迅速发展,大大提高人们的工作效率。计算机信息处理系统的引进已彻底改变了许多系统的经营管理 。
    图书管理系统是学校管理机制中的重要组成部分,通过对图书管理系统的运行管理机制进行调查研究,开发了此图书系统。本系统中解决了学校图书管理事务中的常用基本问题以及相关统计工作。本系统中包含6个功能模块:系统设置,读者管理,图书管理,图书借还,系统查询和更改口令。
    本系统使有jsp进行网页界面的设计,使用MVC设计模式,采用了开源框架Struts,它采用了当今软件设计的最新技术,具有开发效率高、设计灵活、生成的软件界面友好美观等特点。本系统中通过JDBC驱动和数据库进行无缝连接,后端的数据库是mysql,也是一个开源的数据库系统,该数据库具有较高的完整性,一致性和安全性。
    关键词:图书管理;信息管理;jsp;struts
    AbstractWith the progress of science and technology, the astonishing rapid development of the computer industry has been improving people’s working efficiency greatly.The introduction of computerized information system has sharply changed the management in many systems in many fields.
    The management system of the library takes an important role in the administration of school organization. I desigen the system after the thorough investigations about the library management system’s mechanism. This system contains with reader informantion management model, book information management model, books borrowing and returning including system information query and password setting.
    The system is contrived with Java Server Pages Techonolege as well as Struts,the software design mode of MVC with open source framework techonolege, which makes this system have the advantages of efficiently designed with beauteous and friendly interface . This system use jdbc driver to connect the mysql database server,which is also an open source database system for its users. The batabase was desigend with highly integrity, security, and consistency.
    Key words: book management, management of information ,jsp,struts
    1.绪论1.1 毕业设计主要任务
    实现图书馆对所藏图书的按类别、书名等多方面的查询,最大的方便读者和图书馆工作人员对所需图书的查询
    建立图书馆外借读者数据库,方便工作人员对读者进行有效管理
    建立图书馆工作人员数据库,限定每个工作人员对软件操作的权限,最大限度的保护数据库
    实现图书馆对新书入库,旧书注销的简单处理,并且建立书籍档案,方便进货
    实现图书馆

    1.2 目前图书管理系统存在的问题1.2.1 检索速度慢、效率低因为图书馆的藏书种类多、数量多,将藏书准确地分门别类,快速检索,手工进行非常困难往往是终于查到了二伟的信息,馆中没有此书或已被别人借走。图书馆的规模越大,这个问题越突出。
    1.2.2 借书、还书工作量大借书、还书频率越大,说明图书馆的作用越大,然而随之而来的大量的借书、还书登记、实存图书的更新以及借出图书超期、遗失等的处理,其工作量之大,往往是人工操作所难以胜任的。而且经常会出现这样那样的差错。
    1.2.3 图书统计工作难、藏书更新不能及时完成。图书馆的图书应根据科学技术的发展和教学工作的需要及时添加和更新,然而由于藏书数量及图书种类越来越多,加上自然损耗,人为破坏,使图书的统计工作难以及时完成,藏书的更新也就很难有针对性地进行,藏书的知识结构得不到良好地控制。
    我校也是一所发展中的高校,近儿年的发展速度很快,图书馆的规模和藏书数量也不断的扩大,为了解决海量图书的管理问题,改变传统的管理方式也是迫在眉睫了。
    1.3 课题意义随着计算机的广泛应用,其逐步成为现代化的标志。图书馆或者一些企业内部,甚至是书店,在正常运行过程中总是面对大量的读者信息,书籍信息以及两者相互作用产生的借书信息、还书信息。因此需要对读者资源、书籍资源、借书信息、还书信息进行管理,及时了解各个环节中信息的变更,要对因此而产生的单据进行及时的处理,为了提高图书馆或者企业内部对图书存销的自动化的管理,能够更快速的满足读者的要求,提高各种工作的效率,现对其设计相应的系统,以达到上述的目的[1]。
    图书管理系统的主要功能是实现图书馆图书借阅和归还的管理的自动化。围绕这一主要功能,本系统涉及到以下核心功能:借阅管理,归还管理。除了这些核心功能外,还包括一些基本和辅助功能,它们是:用户管理、图书馆参数管理、图书管理、统计查询。
    1.4 论文的工作和安排本次设计的目标是,开发一个图书馆借阅管理系统。借助该系统,管理员通过快捷可靠的数据库管理,方便的管理图书馆的信息资料,规范化的管理读者用户,设定不同用户权限,并能通过互联网向读者提供更为方便的在线查询服务,方便读者的使用,最终达到提高图书馆资源利用效率的目的。
    论文设计和实现了图书借阅管理系统系统,可以根据用户的不同权限,对图书馆的的各种信息进行添加、删除、修改或查询操作。论文分为五个部分:

    第一章即本章绪论,简述图书馆借阅管理系统这个课题的背景情况以及开发本系统的意义
    第二章为需求分析,本章中明确了系统需要实现的功能,分析了系统的用例,并介绍根据系统的需求选择的开发工具和技术的概况
    第三章是总体设计,详细描述了本系统中数据库的设计情况,并给出了系统总体界面的设计方案
    第四章为程序设计与编码各主要功能模块的实现方法和部分关键代码,同时提供了个主要界面运行的参考图片,以更直观了解系统的实现情况
    第五章是软件测试,测试系统功能实现并对测试结果进行记录分析
    第六章为结束语,为此次毕业设计做一个总结,总结所获得的经验和体会

    2.图书借阅管理需求分析2.1 可行性分析采用现代化统一的计算机信息网站系统,能够有效优化图书馆管理系统,使其高效的发挥最大作用,能够迅捷的为读者提供相应的服务。开发本系统的可行性研究如下:
    2.1.1 技术可行性技术上的可行性分析主要分析技术条件能否顺利完成开发工作,软、硬件能否满足需要。本系统采用JSP开发出友好美观的人机界面,便于用户理解、操作。数据库管理系统采用MySQL,它能够处理大量数据,同时保持数据的完整性、安全性。因此本系统的开发平台已成熟可行。硬件方面,在科技飞速发展的今天,硬件更新速度越来越快,容量越来越大,可靠性越来越高,价格越来越便宜,因此硬件平台也能够满足本系统所需[2]。
    2.1.2 经济可行性鉴于计算机技术发展异常迅猛,在硬件软件配置以及开发技术均以可行的情况下开发这样一个管理系统成本不会很高,但其可以大大提高图书馆的工作效率,也是图书馆管理发展的必然趋势,其必将有比较宽阔的市场,因此改性统在经济可行性上时可行的[2]。
    2.2 图书借阅管理系统需求概述2.2.1 系统目标该系统主要建立一个基于B/S模式的图书馆借阅管理系统,面对当起很多小型图书管理仍是人工管理带来的检索速度慢,效率低,借阅归还图书量大,图书统计工作量大,藏书不能完成及时更新的问题,该系统可以对跟系统的三个用户类型的使用实现:

    对于读者在本系统的应用下可实现按照各种方式(如:书名,编号,作者)查询图书馆的藏书请客,方便的借阅图书,续借图书,归还图书,能够查询自己的借阅图书情况
    对于图书馆工作人员能够实现方便的对图书进行查询,方便的进行读者借阅情况查询,方便的进行借书还书处理等,便捷的对图书信息进行添加、修改、删除,分类管理等操作,对读者信息进行相关添加,修改,分类管理等操作
    对于系统管理员可以对图书馆信息进行修改更新操作,对系统用户进行添加、修改、删除、权限设置等操作,对图书馆的办证参数进行修改维护等操作功能

    2.2.2 用户类和用户特性图书借阅管理系统是一个基于B/S模式的对图书馆进行高效率管理的应用系统,它的用户主要是读者和图书管理员,学生通过该系统进行图书查询进而对自己需要的图书进行借阅及自己的借阅情况进行查询,图书管理员则通过本系统实现对图书及读者的高效管理,除此之外,还需要一个系统管理员对不同的用户进行权限的设置等操作[2]。
    三类用户的具体描述如下表所示:



    用户类
    描述




    读者
    读者是该系统的重要的使用角色,他们通过该系统查询自己需要的图书信息,并像图书管理员提出借阅图书的申请进而借阅自己所需的图书,还可以通过对自己借阅情况进行查询。


    图书管理员
    图书管理员是该系统的另一个重要使用者,图书管理员通过该系统进行图书的增加,修改,删除,分类管理等操作,实现对读者借阅归还续接图书的方便操作,实现对系统中图书,读者,读者借阅情况的查询,信息更改维护等操作,管理读者类型,对不同类型读者可借阅的图书数量进行设置等图书馆的基本操作。


    系统员
    系统管理员主要是图书管理系统中用户的管理,实现用户添加修改删除以及用户权限设置等操作,实现对图书馆基本信息的修改维护等操作,还包括对图书馆书架的设置操作,以及校外读者办证所需费用、证件有效期等参数的设置。



    2.3 图书借阅管理系统需求模型2.3.1 功能描述图书借阅管理系统的主要任务是实现读者迅速检索查询,方便借阅归还图书,图书管理员高效的完成系统的各项基本操作,系统管理员是管理用户设置权限等操作[3],从图2.1可以看出图书借阅管理系统要完成一下功能:

    登录:读者、图书管理员,系统管理员进入该系统必须登录,身份验证正确了才可以进入该系统,以不同身份进入该系统所对应的系统使用权限是不同的
    系统设置功能:系统管理员可以设置图书馆相关的参数信息
    用户管理功能:对系统用户进行添加,修改,删除,权限设置等操作
    查询功能:对图书馆的馆藏图书,借阅历史,读者用户等信息进行查询
    其他功能:系统管理员可以修改自己的密码,并且拥有其他用户所拥有的所有功能

    下面的系统用例图描述了整个系统用户之间的动作联系及功能模块的概述[4]。

    2.3.2 图书管理员详细功能描述
    读者管理功能:对读者的类型和读者档案进行管理,包括添加,修改,删除读者类型和读者用户的相关信息,管理不同类型读者借阅图书的数量
    图书管理功能:包括对图书类型和具体图书信息的管理,可以增加,修改,删除图书,丰富具体图书的信息,对不同图书进行分类操作
    图书借阅功能:可以完成对读者借阅,续接和还书的操作
    系统查询功能:查询图书相关资料,借阅历史和借阅到期题型
    修改密码功能:可以修改自己的登录密码

    2.3.3 读者详细功能描述
    修改登陆密码:修改自己的登录密码
    查询功能:对图书馆图书信息进行查询,对自己当前借阅书籍进行查询,对图书规划到期题型进行查看

    2.3.4 主要用例的用例描述图书借阅借阅管理系统涉及到的用例包括:图书借阅,图书归还,读书查询,读者信息管理,图书信息管理,用户管理等,现就系统的主要用例图书借阅,图书归还,图书查询进行详细分析。
    用例“图书借阅”



    用例名称
    图书借阅




    标识符
    UC-1


    参与者
    读者,图书管理员


    描述
    读书可以通过查询等方式获得自己想借阅的图书的名称,编号,等其他可唯一识别的信息,向图书管理员提出借阅请求,管理员在系统中记录相应信息,将图书交给读者,借阅成功。


    前置条件
    1. 登录;2. 进入图书借阅的页面


    后置条件
    1. 更新图书借阅列表;


    主干过程
    1.0 借阅图书 1. 读者请求借阅新地图书,并提供自己的编号 2. 系统显示读者借阅情况的表单 3. 读者提供想借阅的图书的标号 4. 系统存储读者和借阅的图书,并将之存储到数据库中 5. 系统更新借阅列表


    分支过程
    1.1 选择重置(第3步后分支出来) 1. 读者选择重置 2. 系统刷新该页面


    异常
    1.0.E.1 读者借阅已满(第2步) 1.读者借阅的数目已经到达自己借阅的上线 2. 系统将错误信息显示在借阅页面 3. 系统重新启动该用例


    被包含用例



    被扩展用例



    优先级




    用例“图书归还”



    用例名称
    图书归还




    标识符
    UC-1


    参与者
    读者,图书管理员


    描述
    读者将自己借阅的图书归还图书馆。


    前置条件
    1. 登录;2. 进入图书归还页面


    后置条件
    1. 更新图书归还列表;


    主干过程
    1.0 图书归还 1. 读者请求归还借阅的图书并提供自己的编号 2. 系统显示该读者的借阅信息表 3. 使用者填将要归还的图书交给管理员 4. 管理员输入图书编号,系统存储归还信息,并将之存储到数据库中 5. 系统更图书归还列表


    分支过程
    1.1 选择重置(第3步后分支出来) 1. 读者选择重置 2. 系统刷新该页面


    异常
    1.0.E.1 读者借阅超时(第4步) 1.该书超出了应该归还的时间范围 2. 系统将罚款信息显示在归还页面 3. 需要缴纳罚金


    被包含用例



    被扩展用例



    优先级




    用例“图书查询”



    用例名称
    图书查询




    标识符
    UC-3


    参与者
    读者,图书管理员


    描述
    读者通过图书的标号,名称等信息对相应的图书进行查询。


    前置条件
    1. 登录; 2. 转到图书查询页面


    后置条件
    1. 查询页面显示相应的图书的信息


    主干过程
    图书查询 1. 读者输入想要查询的图书信息 2. 系统显示相应的图书信息 3. 读者点击读书名称,跳转到图书详细信息链接页面


    分支过程
    输入信息时(第2步后) 1. 系统显示:请选择查询依据 2. 用户进行相应选择 重新查询(第2步后) 1. 系统已经显示了相应图书信息 2. 读者想查询其他图书


    异常
    5.0.E.1 查询的图书不存在(第2步后) 1. 读者输入的图书信息不能在数据库中查询到 2. 系统显示暂时无该图书信息 3. 重新启动该用例


    被包含用例



    被扩展用例



    优先级




    3.总体设计3.1 数据库设计3.1.1 数据库设计概述数据库是整个系统的基石,数据库的设计优劣直接影响到整个系统的设计成败,本节对数据库的设计进行专门阐述[5]。
    数据库是数据管理的最新技术。十多年来数据库管理系统已从专用的应用程序发展成为通用的系统软件。由于数据库具有数据结构化,最低冗余度,较高的程序与数据独立性,易于扩充,易于编制应用程序等优点,较大的信息系统都是建立在数据库设计之上的。因此不仅大型计算机及中小型计算机,甚至微型机都配有数据库管理系统[6]。
    数据库系统的出现使信息系统从以加工数据的程序为中心转向围绕共享的数据库为中心的新阶段。这样既便于数据的集中管理,又有利于应用程序的研制和维护,提高了数据的利用性和相容性,提高了决策的可靠性。目前,数据库已经成为现代信息系统不可分割的重要组成部分。数据库技术也是计算机领域中发展最快的技术之一。
    数据库设计是把现实世界的实体模型与需求转换成数据库的模型的过程,它是建立数据库应用系统的核心问题。数据库及其应用的性能都建立在良好的数据库设计的基础之上,数据库的数据是一切操作的基础,如果数据库设计不好,那么其它一切用于提高数据库性能的方法收效都是有限的。数据库设计的关键是如何使设计的数据库能合理地存储用户的数据,方便用户进行数据处理[6]。
    设计数据库必须遵循一定的规则,在关系型数据库中,这种规则就是范式,范式是符合某一种级别的关系模式的集合。一般人们设计数据库遵循第三范式。即:数据库表中不包含已在其他表中包含的非主关键字信息。采用范式减少了数据冗余,节约了存储空间,同时加快了增、删、改的速度[6]。
    整个系统所包括的信息有图书信息、读者信息、留言信息、图书借阅信息、图书归还信息、系统用户信息、读者类型信息。可将这些信息抽象为下列系统所需要的数据项和数据结构:

    图书信息(编号,图书名称,图书类型,作者,译者,ISBN号,价格,出版社,所在书架,入库时间,操作员)
    图书类型(编号,名称,可借阅天数)
    读者信息(编号,姓名,性别,条形码,读者类型,出生年月,有效证件,证件号码,登记日期,电话,邮箱,操作员)
    读者类型(名称,可借阅图书本数)
    图书借阅信息(图书编号,读者ID,借出时间,应还时间,是否归还,操作员)
    图书归还信息(图书编号,读者ID,归还时间,操作员)
    用户(编号,用户名称,密码)
    图书馆信息(编号,名称,馆长,电话,地址,邮箱,创建日期,简介)
    图书馆参数信息(编号,办证费用,有效期限)

    在这里使用E-R图描述了图书借阅管理系统的数据模型。图3.1图书借阅管理系统E-R图描述了该系统所涉及到的实体以及他们之间的关系。具体结构如下图所示:

    3.1.2 图书信息表结构设计图书信息表主要用于存储图书馆中所藏图书的相关信息,其中的相关信息是在图书入库时由操作员进行添加完善,此表主要用于读者和图书管理员对馆中图书的查询,系统用户根据图书的某个属性进行查询,便可得知图书的其他相关信息,其中图书所在书架属性是便于读者借阅时对图书的寻找,图书价格是在读者不慎将图书遗失时对遗失图书进行赔偿的依据。表的具体结果如下:
    图书信息表tb_bookinfo



    字段名称
    数据类型
    字段长度
    是否为空
    说明




    barcode
    varchar
    30
    No
    Key


    bookname
    varchar
    70
    No



    author
    varchar
    30
    Yes
    作者


    translator
    varchar
    30
    Yes
    译者


    ISBN
    varchar
    20
    No



    price
    float
    8,2
    No
    图书价格


    bookcase
    varchar
    10
    No
    图书所在书架


    press
    varchar
    70
    No
    出版社


    intime
    date

    No
    图书入库时间


    operator
    varchar
    30
    No
    操作员


    page
    int
    10
    Yes
    图书页码



    3.1.3 图书类型信息表结构设计该表的设计主要是方便对图书的分类,和对图书的查询,在实际应用中图书管理员就是根据图书类型的不同将之分列在不同的书架,以方便读者的借阅寻找,其中可借阅天数项设置了不同类型图书的借阅期限,可根据图书的具体情况进行不同的维护管理,表的具体结构设计如下:
    图书类型信息表tb_booktype



    字段名称
    数据类型
    字段长度
    是否为空
    说明




    ID
    int
    10
    No
    Key


    bookname
    varchar
    30
    No



    days
    int
    10
    No
    可借阅天数



    3.1.4 读者信息表结构设计读者信息表的设计是为了图书馆管理员对读者进行管理,其中读者ID,不同类型证件的号码都是唯一的,是读者在借阅图书时需要输入对读者身份进行识别的信息,读者邮件电话等信息室为了与读者进行联系,读者类型信息决定了读者一次性可借阅的图书的数量,注册时间可用于查询计算读者身份有效的期限,操作员是为了便于对信息才操作的查询。表的具体结构设计如下:
    读者信息表tb_reader



    字段名称
    数据类型
    字段长度
    是否为空
    说明




    Id
    int
    10
    No
    Key


    name
    varchar
    20
    No



    sex
    varchar
    4
    No



    barcode
    varchar
    30
    No



    readertype
    varchar
    11
    No
    读者类型


    tel
    varchar
    20
    Yes
    电话


    e-mail
    varchar
    100
    Yes



    paperType
    varchar
    10
    No
    证件类型


    PaperNO.
    Varchar
    20
    No
    证件号码


    birthday
    date

    Yes



    intime
    date

    No
    登记时间


    operator
    varchar
    30
    No
    操作员



    3.1.5 读者类型信息表结构设计此表的设计是为了对不同身份的读者进行分类方便读者的管理,其中可借阅图书数量的属性设定,是根据读者需求的不同对起权限进行的设置,用于规定不同类型读者一次可借阅的图书数量,表的具体结构设计如下:
    读者类型信息表tb_readertype



    字段名称
    数据类型
    字段长度
    是否为空
    说明




    ID
    int
    10
    No
    Key


    readername
    varchar
    50
    No



    number
    int
    4
    No
    可借图书本数



    3.1.6 图书借阅信息表结构设计该表的设计是用于对读者借阅图书进行管理,表中图书ID属性是对借阅图书的唯一性识别标识,读者ID号记录借阅的相应读者,结出时间记录了相应的归还时间,以及归还时是否超时,操作员是对借阅进行操作的人员的记录方便日后的查询,是否归还标识可查询当起读书是否被归还,表的具体结构设计如下:
    图书借阅信息表 tb_borrow



    字段名称
    数据类型
    字段长度
    是否为空
    说明




    ID
    int
    10
    No
    Key


    readerID
    varchar
    10
    No



    bookID
    int
    10
    No



    borrowTime
    date

    No
    结出时间


    backTime
    date

    No
    应归还时间


    operator
    varchar
    30
    No
    操作员


    ifback
    tinyint
    1
    No
    是否归还



    3.1.7 图书归还信息表结构设计与图书借阅信息表形成对照的是图书归还信息表,该表的设计除了像上表一样把借阅的图书与相应的借阅者进行对应的联系以外还记录了读者应归还图书的时间,以此判断读者的借阅是否超时,表的具体结构设计如下:
    图书归还信息表tb_giveback



    字段名称
    数据类型
    字段长度
    是否为空
    说明




    ID
    int
    10
    No
    Key


    readerID
    varchar
    11
    No



    bookID
    int
    11
    No



    backTime
    date

    No
    归还时间


    operator
    varchar
    30
    No
    操作员



    3.1.8 用户信息表结构设计该表的设计用于记录图书管理系统用户的信息,方便对用户的管理,表中包括用户ID 和用户的登录密码,表的具体结构设计如下:
    系统用户信息表tb_user



    字段名称
    数据类型
    字段长度
    是否为空
    说明




    ID
    int
    10
    No
    Key


    name
    varchar
    30
    No



    password
    varchar
    30
    No



    3.1.9 图书馆信息表结构设计该表包含了图书馆的名称,地址,联系方式,建馆时间,简介等信息,是对图书馆基本属性信息的描述,方便外界读者对图书馆的了解,表的具体结构设计如下:
    图书馆信息表 tb_library



    字段名称
    数据类型
    字段长度
    是否为空
    说明




    ID
    int
    10
    No
    Key


    libraryname
    varchar
    50
    No



    curator
    varchar
    10
    No



    tel
    varchar
    20
    Yes
    结出时间


    address
    varchar
    100
    No
    应归还时间


    e-mail
    varchar
    100
    No



    url
    varchar
    100
    No
    图书馆网站


    createDate
    varchar

    No
    创馆时间


    introduce
    text

    Yes
    图书馆简介



    3.1.10 办证参数信息表结构设计该表是为校外人员设计的,对于需要到图书馆进行借阅的读者可办理临时的借阅证用于对图书的借阅凭证,表中包含了办证所需费用以及证件的有效期限,表的具体结果设计如下:
    图书证参数信息表tb_parameter



    字段名称
    数据类型
    字段长度
    是否为空
    说明




    ID
    int
    10
    No
    Key


    Cost
    int
    10
    No
    办证费用


    validity
    int
    10
    No
    有效时间



    3.2 系统总体结构设计该系统在Windows98/2000/XP环境下,主要采用JSP开发工具,MySQL数据库来设计,开发过程与成果应符合GB/T 11457-1995软件工程术语,GB/T 8567-1988计算机软件产品开发文件编制指南等[7]。
    3.2.1 图书管理系统总体结构图绘制系统结构图的过程,实际上就是对系统功能模块进行分解设计的过程,即合理地将数据流程图转变为所需要的系统结构图[8]。
    系统结构图将会使读者和用户能直观的了解系统的结构模式,理解系统的各个功能的结构,能很好地方便用户使用和理解整个系统。
    本系统的结构图如下:

    根据需求分析的结果,按照“低耦合、高内聚”的原则,本系统将划分为以下主要功能模块:系统管理员功能模块,读者管理功能模块,图书管理功能模块,图书借还功能模块;系统查询功能模块[8]。
    3.2.2 系统管理员模块功能该模块主要包括图书馆信息设置,用户管理,参数设置,书架设置。

    图书馆信息设置:该功能选项用于系统管理员对图书馆名称,地址,联系方式,简介等信息的管理更新,以便于读者和外界人士对图书馆的了解。该功能是对td_library表进行维护修改等操作,修改后的信息将被保存在该表中
    用户设置:该功能子模块主要是系统管理员对系统用户的管理,通过此子模块的功能实现可以对用户进行添加、修改、删除、权限设置等操作,该子模块能将图书馆的不同工作细化到不同的相关人员,极大地提高了图书馆的工作效率。该操作是对tb_users表进行操作,在对用户进行了相关的操作后把操作后的最新信息存放在该表中
    图书馆参数设置:通过该子模块设置在图书馆办理临时读者证的费用及证件有效期限。该操作是对于tb_parameter表进行,并把操作后的最新数据存放在该表中

    3.2.3 读者管理模块功能该模块主要包含读者类型管理和读者信息管理两个子模块:

    读者类型管理:该子模块是对图书馆系统用户读者的类型进行维护,修改等操作,在此模块中主要设置不同类型读者一次性可借阅的图书的数量,该操作是对于tb_resderType表进行,并将操作结果保存在该表中
    读者信息管理:该子模块是对读者的基本信息进行管理,可以对读者的基本信息进行添加,修改,删除操作,这下操作均是对tb_resder表进行的,并将操作后的结果保存在该表中

    3.2.4 图书管理模块功能图书管理功能模块可分为图书类型管理和图书信息管理两个子模块,其各自的实现分别如下面表中所示:
    图书类型管理描述




    图书类型管理




    功能描述
    对图书进行类型设置分类,并对不同类型图书可被借阅的天数进行设置


    访问的数据库表
    图书类型表:tb_bookType


    进行的操作
    添加、修改、删除图书类型,对不同类型图书可被借阅的天数进行设置


    产生的结果
    对图书类型进行管理,对不同类型图书参数进行设置


    结果存储位置或输出
    结果存储在图书类型表(tb_bookType)中,结果在图书类型查询页面输出



    图书信息管理描述




    图书信息管理




    功能描述
    对图书进行基本操作和信息管理


    访问的数据库表
    图书类型表:tb_bookType


    进行的操作
    添加、修改、删除图书,对图书的编号、所在书架、价格、出版社等基本信息进行管理


    产生的结果
    对图书基本操作管理,对不同图书参数进行各自信息的设置管理


    结果存储位置或输出
    结果存储在图书类型表(tb_book)中,结果在图书查询页面输出



    3.2.5 图书借还模块功能该功能模块主要实现对读者借阅、续接、归还图书的操作,其中子模块各自的描述如下各表所列:
    图书借阅描述




    图书借阅管理




    功能描述
    对读者借阅图书进行基本操作和信息管理


    访问的数据库表
    图书信息表: tb_bookinfo 读者信息表:tb_reader 读者类型信息表:tb_resderType


    进行的操作
    对读者借阅图书进行管理


    产生的结果
    读者借阅成功,系统对借阅信息进行记录


    结果存储位置或输出
    结果存储在图书借阅表(tb_borrow)中,结果在图书借阅查询页面输出



    图书续借描述




    图书续接管理




    功能描述
    对读者借阅图书进行提续接操作


    访问的数据库表
    图书借阅表: tb_borrow


    进行的操作
    对借阅的图书进行续接


    产生的结果
    读者归还日期延后一个月


    结果存储位置或输出
    结果存储在图书借阅表(tb_borrow)中



    图书归还描述




    图书归还管理




    功能描述
    对读者归还图书进行基本操作和信息管理


    访问的数据库表
    图书借阅信息表: tb_borrow 读者类型信息表:tb_resderType


    进行的操作
    对读者借阅图书进行管理


    产生的结果
    读者借阅成功,系统对借阅信息进行记录


    结果存储位置或输出
    结果存储在图书归还表(tb_giveback)中



    3.2.6 系统查询模块功能该模块包括对图书馆藏书进行查询,对读者借阅情况进行查询,以及对借阅到期和超期的读者进行提醒的信息,其中三个子模块的各自实习如下所示:
    图书查询描述




    图书查询




    功能描述
    系统用户对馆藏图书信息进行查询操作


    访问的数据库表
    图书信息表: tb_book


    进行的操作
    用户通过图书的编号,作者,出版社等信息对图书进行相关查询


    产生的结果
    读者查询到相应的图书或系统提醒查询的图书不存在


    结果存储位置或输出
    结果在图书查询页面输出



    图书借阅查询描述




    图书借阅查询




    功能描述
    系统用户对读者借阅图书信息进行查询操作


    访问的数据库表
    图书借阅表: tb_borrow


    进行的操作
    用户通过图书的编号,读者编号等信息对摸个读者或某本图书的借阅情况进行相关查询


    产生的结果
    查询到相应的读者或图书得借阅情况


    结果存储位置或输出
    结果在图书查询页面输出



    图书借阅到期提醒描述




    图书借阅到期提醒管理




    功能描述
    对读者借阅的到期图书进行提醒


    访问的数据库表
    图书借阅表: tb_borrow 图书归还表:tb_giveback 读者信息表:tb_reader 读者类型信息表:tb_resderType


    进行的操作
    对借阅到期和超期的读者进行提醒


    产生的结果
    向借阅到期和借阅超期的读者发送邮件等提醒信息


    结果存储位置或输出
    结果存储在图书借阅到期提醒表



    4.程序设计与编码4.1 开发平台与工具4.1.1 J2EE平台J2EE ,即是Java2平台企业版(Java 2 Platform Enterprise Edition),是原Sun公司(现已被甲骨文公司收购)为企业级应用推出的标准平台。它简化了企业解决方案的开发、部署和管理相关复杂问题的体系结构,J2EE技术的基础就是核心Java平台或Java 2平台的标准版,J2EE不仅巩固了标准版中的许多优点,例如”编写一次、随处运行”的特性、方便存取数据库的JDBC API、CORBA技术以及能够在Internet应用中保护数据的安全模式等等,同时还提供了对 EJB(Enterprise JavaBeans)、Java Servlets API、JSP(Java Server Pages)以及XML技术的全面支持。其最终目的就是成为一个能够使企业开发者大幅缩短投放市场时间的体系结构。J2EE体系结构提供中间层集成框架用来满足无需太多费用而又需要高可用性、高可靠性以及可扩展性的应用的需求。通过提供统一的开发平台,J2EE降低了开发多层应用的费用和复杂性,同时提供对现有应用程序集成强有力支持,完全支持Enterprise JavaBeans,有良好的向导支持打包和部署应用,添加目录支持,增强了安全机制,提高了性能[9]。
    在开发图书馆借阅管理系统的过程中,应用Myeclipse6.0.1,它可以在数据库和J2EE的开发、发布,以及应用程序服务器的整合方面极大的提高工作效率。Myeclipse是功能丰富的J2EE集成开发环境,包括了完备的编码、调试、测试和发布功能,完整支持HTML, Struts, JSF, CSS, Javascript, SQL[10]。
    4.1.2 WEB服务器和数据库在系统的开发过程中使用的Web应用服务器是Tomcat,是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache、SUN和其他一些公司及个人共同开发而成。由于有了Sun 的参与和支持,最新的Servlet 和JSP 规范总是能在Tomcat 中得到体现。Tomcat是一个小型的轻量级应用服务器,它运行时占用的系统资源小、扩展性好、支持负载平衡和邮件服务等开发应用系统常用的功能,因此在中小型系统和并发访问用户不是很多的时候,经常被使用[11]。
    使用MySQL作为数据库开发工具。MySQL是一个小型关系型数据库管理系统,开发者为瑞典MySQL AB公司。在2008年1月16号被Sun公司收购。目前MySQL被广泛地应用在Internet上的中小型网站中。由于其体积小、速度快、总体拥有成本低,尤其是开放源码这一特点,许多中小型网站为了降低网站总体拥有成本而选择了MySQL作为网站数据库[11]。
    为了开发的便捷快速,使用struts第三方插件。Struts是一个全新的MVC框架,实在WebWork基础上发展起来的[12]。
    开发系统用的系统工具如表4.1所示:



    工具名称
    用途




    JDK 1.6.0_11
    Java 开发工具包


    MyEclipse
    J2EE集成开发环境


    MySQL 5.0
    小型关系数据库管理系统


    SQLYog 6.1
    MySQL图形化数据库管理工具


    Tomcat 6.0
    Web应用服务器


    Struts 1.0
    第三方插件,可扩展的Java EE Web框架



    4.2 程序设计4.2.1 程序设计概述在设计的Web层应用了著名的MVC模式,V有JSP来实现,为了业务逻辑和表示的分离.它是基于Web应用系统,它的客户端使用Broswer,然后是Web层的应用,业务逻辑层(有EJB实现),资源管理层。客户请求浏览页面,一般Web层的View有JSP组成,并且使用了大量Taglib。把每个请求映射到某个HTMLAction类来响应它。HTML Action类是一个标准的类,执行选择的HTML Action。使用MVC模式减少了代码的复制,即减少了代码的维护,由于模型返回的格式不带任何显示格式,因而模型可以直接应用于接口的使用,还因为MVC模型把不同的模型和不同的视图组合在一起完成不同的请求,因此,控制层可以说包含了用户请求权限的概念[13]。
    在设计中还因应用了Struts框架,Struts跟Tomcat、Turbine等诸多Apache项目一样,是开源软件,这是它的一大优点。使开发者能更深入的了解其内部实现机制[11]。
    除此之外,Struts的优点主要集中体现在Taglib和页面导航。Taglib是Struts的标记库,灵活动用,能大大提高开发效率。页面导航使系统的脉络更加清晰。通过一个配置文件,即可把握整个系统各部分之间的联系,这对于后期的维护有着莫大的好处[11]。
    4.2.2 数据库与Web服务器的连接数据库连接时采用连接池技术链接MySQL,具体代码实现如下:
    public ConnDB(){ try { InputStream in=getClass().getResourceAsStream(propFileName); prop.load(in); //通过输入流对象加载Properties文件 dbClassName = prop.getProperty("DB_CLASS_NAME"); //获取数据库驱动 dbUrl = prop.getProperty("DB_URL", "jdbc:mysql://127.0.0.1:3306/db_librarySys?user=root&password=123&useUnicode=true"); } catch (Exception e) { e.printStackTrace(); //输出异常信息 } } public static Connection getConnection() { Connection conn = null; try { Class.forName(dbClassName).newInstance(); conn = DriverManager.getConnection(dbUrl); } catch (Exception ee) { ee.printStackTrace(); } if (conn == null) { System.err.println( "警告: DbConnectionManager.getConnection() 获得数据库链接失败.\r\n\r\n链接类型:" + dbClassName + "\r\n链接位置:" + dbUrl); } return conn; } /* * 功能:执行查询语句 */ public ResultSet executeQuery(String sql) { try { conn = getConnection(); stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); rs = stmt.executeQuery(sql); } catch (SQLException ex) { System.err.println(ex.getMessage()); } return rs; } /* * 功能:执行更新操作 */ public int executeUpdate(String sql) { int result = 0; try { conn = getConnection(); //调用getConnection()方法构造Connection对象的一个实例conn stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); result = stmt.executeUpdate(sql); //执行更新操作 } catch (SQLException ex) { result = 0; } return result; } /* * 功能:关闭数据库的连接 */ public void close() { try { if (rs != null) { rs.close(); } if (stmt != null) { stmt.close(); } if (conn != null) { conn.close(); } } catch (Exception e) { e.printStackTrace(System.err); } }}
    4.2.3 登录模块程序设计本模块主要是用户通过图书管理系统的首页登录进入该系统。用户输入正确的用户名和密码,系统会根据用户的身份进行相应权限划分;如果登录信息有错误,则系统提示登入错误的信息,并且禁止系统用户进行任何操作。图书借阅管理系统的登录主页面如图4.1所示。

    用户在登录页面写好用户名和密码,选择登录,登录成功则跳转到系统的首页,否则提示错误信息[14]。在服务器端进行用户身份验证的程序流程图如图4.2程序流程图所示。

    4.2.4 系统管理员功能模块的实现本模块中最主要的是管理系统用户,设置图书馆信息等操作,其中管理系统用户包括对他们进行添加,修改,删除及权限划分操作。
    其中系统管理员读系统用户进行管理的操作页面如下图所示:

    其中对系统用户权限的设置是该操作的重要部分也是系统提高效率的关键所在,起具体实现代码为:
    private ActionForward managerModify(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { ManagerForm managerForm = (ManagerForm) form; managerForm.setId(managerForm.getId()); //获取并设置管理员ID号 managerForm.setName(managerForm.getName()); //获取并设置管理员名称 managerForm.setPwd(managerForm.getPwd()); //获取并设置管理员密码 managerForm.setSysset(managerForm.getSysset()); //获取并设置系统设置权限 managerForm.setReaderset(managerForm.getReaderset()); //获取并设置读者管理权限 managerForm.setBookset(managerForm.getBookset()); //获取并设置图书管理权限 managerForm.setBorrowback(managerForm.getBorrowback()); //获取并设置图书借还权限 managerForm.setSysquery(managerForm.getSysquery()); //获取并设置系统查询权限 int ret = managerDAO.update(managerForm); //调用设置管理员权限的方法 if (ret == 0) { request.setAttribute("error", "设置管理员权限失败!");//保存错误提示信息到error参数中 return mapping.findForward("error"); //转到错误提示页面 } else { return mapping.findForward("managerModify"); //转到权限设置成功页面 } }
    4.2.5 读者管理功能模块的实现读者管理主要实现对读者的类型管理和信息管理两个功能子模块,其中读者类型管理中需要设置不同类型读者一次性可借阅的图书数量,读者信息管理则主要管理维护读者的基本信息,其各自操作界面如下所示:
    读者类型管理功能界面

    读者信息管理功能界面

    该管理功能在设置读者信息时需要输入读者姓名,性别,条形码,选择读者类别,有效证件,证件号码,电话,e-mail等信息。其中的姓名,性别,条形码,证件号码是必填选项。
    4.2.6 查询功能模块的实现查询功能模块包括图书查询,读者借阅查询和借阅到期提醒三个子功能模块。
    图书查询可根据图书的条形码,书名,作者,出版社等不同信息进行查询,其操作界面图下图所示:

    实现该功能中根据读者不同需求的按条件查询实现语句如下:
    public Collection query(String strif){BookForm bookForm=null;Collection bookColl=new ArrayList();String sql="";if(strif!="all" && strif!=null && strif!=""){ sql="select * from (select b.*,c.name as bookcaseName,p.pubname as publishing,t.typename from tb_bookinfo b left join tb_bookcase c on b.bookcase=c.id join tb_publishing p on b.ISBN=p.ISBN join tb_booktype t on b.typeid=t.id where b.del=0) as book where book."+strif+"'";}else{ sql="select b.*,c.name as bookcaseName,p.pubname as publishing,t.typename from tb_bookinfo b left join tb_bookcase c on b.bookcase=c.id join tb_publishing p on b.ISBN=p.ISBN join tb_booktype t on b.typeid=t.id where b.del=0";}System.out.println("图书查询时的SQL:"+sql);ResultSet rs=conn.executeQuery(sql);try { while (rs.next()) { bookForm=new BookForm(); bookForm.setBarcode(rs.getString(1)); bookForm.setBookName(rs.getString(2)); bookForm.setTypeId(rs.getInt(3)); bookForm.setAuthor(rs.getString(4)); bookForm.setTranslator(rs.getString(5)); bookForm.setIsbn(rs.getString(6)); bookForm.setPrice(Float.valueOf(rs.getString(7))); //此处必须进行类型转换 bookForm.setPage(rs.getInt(8)); bookForm.setBookcaseid(rs.getInt(9)); bookForm.setInTime(rs.getString(10)); bookForm.setOperator(rs.getString(11)); bookForm.setDel(rs.getInt(12)); bookForm.setId(Integer.valueOf(rs.getString(13))); bookForm.setBookcaseName(rs.getString(14)); bookForm.setPublishing(rs.getString(15)); bookForm.setTypeName(rs.getString(16)); bookColl.add(bookForm); }} catch (SQLException ex) { ex.printStackTrace();}conn.close();return bookColl;}
    借阅查询子模块是对图书当前状态和读者当前的借阅情况进行查询,具有此权限的用户登录并进入该操作界面后选中相应的查询条件,并输入相应的查询信息,系统即可在页面显示被查询的图书的状态,或相应读者的当起借阅情况,此外,还可以输入相应时间段内的所有借阅清单,或选择某个读者查询他在某个时间段内的借阅情况,查询某个时间段内的此子功能操作界面为:

    系统中同时选中日期和限制条件进行查询时,程序是在条件查询的基础上选中符合时间条件限制的内容,其实现代码为:
    if (flag.length == 2) { if (request.getParameter("f") != null) { str = request.getParameter("f") + " like '%" + request.getParameter("key") + "%'"; } System.out.println("日期和条件"); String sdate = request.getParameter("sdate"); String edate = request.getParameter("edate"); String str1 = null; if (sdate != null && edate != null) { str1 = "borrowTime between '" + sdate + "' and '" + edate + "'"; } str = str + " and borr." + str1; System.out.println("条件和日期:" + str);}
    借阅到期提醒子模块是将系统当前时间与应归还的时间进行比较,如果系统当前时间以超过图书应归还时间,相应的读者借阅信息便会被显示在借阅到期提醒界面,其界面为:

    4.2.7 图书管理功能模块的实现与读者管理模块类似,图书管理模块也分为图书类型管理和图书档案管理两个子模块,其中图书类型管理功能处理将图书分类提高管理效率外还设置了不同类型图书可借阅的天数,对不同类型图书进行区别管理,图书档案管理是管理维护图书馆藏书的基本信息。这两个子模块的操作界面为:
    图书类型管理界面

    图书档案管理界面

    图书档案中包含图书的条形码,图书名称,图书类型,作者,译者,价格,出版社,所在书架等图书基本信息,其中作者,译者,页码是备选信息,可以不填。
    4.2.8 图书借还功能模块的实现该功能模块可分为图书借阅,图书归还,图书续借三个子模块.
    图书借阅该子模块实现系统的借阅功能,读者向图书管理员提供自己的编号会显示出读者当起的借阅情况,再提供要借阅的图书标号,图书管理员将信息记录在系统中,借阅成功,其操作界面如下图:

    如果在借阅时所要借阅的图书不存在或者读者借阅已达借阅图书的上线,则读者不能接续借阅图书,系统将给出相应提示,此功能的实现代码为:
    private ActionForward bookborrow(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response){ //查询读者信息 //此处一定不能使用该语句进行转换 readerForm.setBarcode(request.getParameter("barcode")); ReaderForm reader = (ReaderForm) readerDAO.queryM(readerForm); request.setAttribute("readerinfo", reader); //查询读者的借阅信息request.setAttribute("borrowinfo",borrowDAO.borrowinfo(request.getParameter("barcode"))); //完成借阅 String f = request.getParameter("f"); String key = request.getParameter("inputkey"); if (key != null && !key.equals("")) { String operator = request.getParameter("operator"); BookForm bookForm=bookDAO.queryB(f, key); if (bookForm!=null){ int ret = borrowDAO.insertBorrow(reader, bookDAO.queryB(f, key), operator); if (ret == 1) { request.setAttribute("bar", request.getParameter("barcode")); return mapping.findForward("bookborrowok"); } else { request.setAttribute("error", "添加借阅信息失败!"); return mapping.findForward("error"); } }else{ request.setAttribute("error", "没有该图书!"); return mapping.findForward("error"); } } return mapping.findForward("bookborrow");}
    图书归还该子模块实现系统的图书归还功能,读者向图书管理员提供自己的编号会显示出读者当起的借阅情况,再提供要归还的图书标号,图书管理员将信息记录在系统中,归还成功,其操作界面如下图:

    5.软件测试5.1 软件测试的方法与步骤该系统在本地服务器上进行运行和调试,鉴于对系统的内部结构和处理算法的完全了解以及对系统功能的全面掌握对系统进行白盒测试和黑盒测试。
    在开发软件系统的过程中,需要面对错综复杂的问题,因此,在软件生存周期的每个阶段都不可避免地会产生错误。测试目的在于:发现错误而执行一个程序的过程,测试重要发现一个发现其中尚未发现的错误。
    本系统的测试阶段信息流程下图所示[15]。

    为了设计出有效地测试方案按照下面准则进行测试:所有测试都应追溯到用户需求;在完成了需求模型就要着手制定测试计划,在编码之前最所有测试工作进行计划和设计;运用Pareto原理着重对占出现错误80%的容易出错的20%的模块进行测试,从小规模开始逐步进行大规模测试,通常先重点测试单个程序模块再转向集成的模块簇;精心设计测试方案,尽可能充分覆盖程序逻辑使之达到要求的可靠性[15]。
    按照软件工程的观点,软件测试(主要是指多模块程序的测试)共包括4个层次。

    单元测试。单元测试的用例从单元详细设计中导出。在单元测试中可以采用功能性测试和结构性测试两种
    集成测试和确认测试。这一阶段的任务,是通过了单元测试的模块逐步组装起来,通过测试与纠错,最终得到一个满足需求的目标软件
    验证测试。在这个测试步骤中所发现的往往是需求规格说明的错误。一般来说,系统测试是功能性测试,不是结构性测试[15]

    在整个测试中,采用了白盒测试和黑盒测试相结合的方法。
    5.2 测试用例设计与测试用例的运行过程及测试结果分析5.2.1模块测试登录模块测试


    用例
    测试数据
    预期结果




    UC1:输入正确的用户名和密码
    用户名:admin密码:admin
    系统跳转到登陆成功页面


    UC2:输入空用户名
    用户名:
    请输入用户名


    UC3:输入用户名和空密码
    用户名:admin 密码:
    请输入密码


    UC4:输入用户名和错误的密码
    用户名:admin 密码:321ff
    您输入的用户名或密码错误



    当用户输入的用户名或密码错误是系统会跳转到出错页面并提示“您输入的用户名或密码错误”,用户点击确定,系统会重新返回登陆页面,其中错误信息提示页面如图5.1所示:

    更改密码模块测试用户登录系统后可对自己的登录密码进行更改,更改过程要求用户提供原始密码,输入新密码,重新输入新密码进行确认,如果用户输入的原始密码错误,或两次输入的新密码不一致将无法执行密码更新操作,系统会给出相应的提示,起显示页面为:

    5.2.2集成测试把经过单元测试的模块放在一起形成一个子系统并对它进行测试用于检验模块相互间的协调和通信,这里主要对图书管理员功能系统进行了测试,经过检验其功能均得以顺利实现,下图为管理员操作的读者借阅模块成功运行界面:

    把经过测试的子系统装配成一个完整的系统进行测试,经过黑盒测试于白盒测试相结合的方式,对整个系统的各个功能模块进行了测试,并调试改正其中的设计和编码错误,经过这个环节的操作整个系统的功能基本实现成功运行。
    5.2.3 验收测试在进行了以上的测试工作后,将整个软件系统作为单一的实体进行测试,测试内容与系统测试基本类似,但它是在用户积极参与下进行的,此测试过程主要使用实际数据,进行测试,验证测试的目的是验证系统确实能够满足用户的需求,经过这个环节的实际数据测试,系统的各个功能实现都达到了系统需求设计的要求。
    5.3 评价测试过程严格按照测试的流程,经过单元测试、子系统测试和系统测试。通过单元测试,查找出了系统各模块内部的错误;通过子系统测试,发现了模块间相互协调和通信上的错误;通过系统的集成测试,发现了软件设计过程中存在的错误。通过改正错误的设计和实现部分,保证了图书借阅管理系统可以完成需求分析中制定的需求[16]。
    从上面的测试中可以看出,此系统可以完成用户登录、系统设置、图书管理、读者管理管理、借阅管理和系统查询等功能。读者在本系统的应用下可实现按照各种方式(如:书名,编号,作者)查询图书馆的藏书请客,方便的借阅图书,续借图书,归还图书,能够查询自己的借阅图书情况。图书馆工作人员能够实现方便的对图书进行查询,方便的进行读者借阅情况查询,方便的进行借书还书处理等,便捷的对图书信息进行添加、修改、删除,分类管理等操作,对读者信息进行相关添加,修改,分类管理等操作。系统管理员可以对图书馆信息进行修改更新操作,对系统用户进行添加、修改、删除、权限设置等操作,对图书馆的办证参数进行修改维护等操作功能。测试阶段的工作一方面发现了系统的各种错误,另外一方面也验证了修改后的系统能够实现提高图书馆工作效率的预期目的。
    6.结束语6.1 工作成果在MyEclipse开发环境下,以J2EE为平台,运用JSP网络编程语言和Struts框架和MySQL数据库完成了B/S模式的图书馆借阅管理系统。系统基本上实现了预期的各项功能,达到了任务书中的主要设计内容的各项任务,整个系统也在经过测试和不断地改正之后能够顺利运行。基本上实现了开题报告中的需求设计在程序编写,该系统能够实现:

    读者在本系统的应用下可实现按照各种方式(如:书名,编号,作者)查询图书馆的藏书请客,方便的借阅图书,续借图书,归还图书,能够查询自己的借阅图书情况
    图书馆工作人员能够实现方便的对图书进行查询,方便的进行读者借阅情况查询,方便的进行借书还书处理等,便捷的对图书信息进行添加、修改、删除,分类管理等操作,对读者信息进行相关添加,修改,分类管理等操作
    系统管理员可以对图书馆信息进行修改更新操作,对系统用户进行添加、修改、删除、权限设置等操作,对图书馆的办证参数进行修改维护等操作功能

    6.2 改进意见该系统在使用第三方插件Strusts时采用的是1.0版本的,若使用更新的2.0版本会更大的改进系统的开发便捷性和可维护性,系统在数据库与Web服务器的连接时采用的是连接池技术,若在数据库连接时使用现在流行的第三方插件Hibernate3.0,会使系统有更强的数据库映射关系,进而极大简化开发过程,还有就是系统在一些信息设置的细节上还存在不足,可以对系统的相关字段进行格式设置和异常处理,使数据更加规范化[11]。
    其次,本系统只对传统书籍进行了最基本的管理,随着计算机技术的飞速发展,应将各个图书馆的图书进行联网交流,使读者可以远程查询图书馆的藏书等信息,还应开发对电子书的管理,形成网上图书资源共享平台,使读者可以在线阅读电子书资料,并设置用户权限,使管理员可以上传图书资料信息,高级用户可以下载网上电子书资源,从而实现信息资源最大化被共享。
    0 评论 5 下载 2019-10-10 17:12:38 下载需要20点积分
  • 基于PHP的网上购物系统设计与实现

    摘 要随着Internet技术的发展,人们的日常生活已经离不开网络。未来社会人们的生活和工作将越来越依赖于Internet技术的发展,也将越来越数字化、网络化、电子化、虚拟化。电子商务也随着网络的发展日益和人们的生活贴近。Internet的发展历程以及目前的应用状况和发展趋势,可以充分地相信网络技术将极大的改变我们的生活和工作方式,甚至社会的价值观也会发生某种变化。本设计尝试用PHP在网络上架构一个动态的网上商品购物网站,以使每一位顾客不用出门在家里就能够通过上网来轻松购物。
    本文详细介绍了基于PHP的网上购物系统的系统结构设计、实现方法和操作流程,并介绍了系统各个模块的主要功能和数据库设计及功能。该系统采用B/S结构,采用PHP技术进行动态窗体页面的设计。后台数据库选用MySQL 数据库。系统设计并实现了会员注册及登录模块,会员基本信息模块,会员购物模块,产品搜索模块,管理员商品管理模块,用户管理模块,订单管理模块,信息管理模块等一系列功能模块。最后,对网上购物系统进行了系统测试,各个模块功能运行良好。
    关键字:Internet,PHP,B/S,网上购物系统
    AbstractAlong with the development of internet technology, daily life can not go on without the network. The life and work of the social people of future will depend on the development of digital technology more and more , digitization, networking, electronization, virtual more and more. E-commerce presses close to people’s life day by day along with the development of network too.Developing course and present application state and development trend of Internet can believe network technology will change life and working way of us greatly, and even change the values of the society to some extent.This paper tries to build up a dynamic e-commerce website in the network with PHP, so that each customer needn’t go out and can do some shopping easily through internet at home 。
    This paper describes system structure design, implementation method and operation process based on PHP, and introduces main functions of various modules about the system and database design. The system uses B/S structure, and conducts design of dynamic form page by PHP technology. Background database selects MySQL database. System design realizes a series of function modules such as user registration and landing module, user basic information module, shopping module, searching goods module, the management of goods ,the management of user,the management of orders ,the management of news and so on. Finally, the paper tests Shopping Inline System, and the result finds that kinds of modules operate well.
    Keyword: Internet, PHP, B/S, e-Shopping
    1 绪论1.1 开发背景及意义电子商务是伴随着信息经济的脚步,在20世纪90年代兴起于美国、欧洲等发达国家的一个新概念。目前,国际上对电子商务尚无统一的定义。一般来说,狭义的概念是指在开放的国际互联网及其它网络的环境下,通过服务器的运用,实现消费者的网上购物、企业间商业交易的一种新型交易运营模式。即电子商务=Web(服务器)+IT(信息技术)+企业业务。
    电子商务的类型:从其交易对象来看,电子商务的应用包括企业与企业之间的电子商务 (B2B)、企业与消费者之间的电子商务(B2C)、企业与政府之间的电子商务 (B2G)、政府与消费者之间的电子商务(G2C)、企业内部的电子商务。
    电子商务的本质特点:电子商务的重点在于商务、电子商务的核心竞争力在于信息平台、电子商务的使命在于利用互联网技术对传统资源的整合。
    电子商务实现贸易的全球化、直接化、网络化,发展电子商务是不可阻挡的趋势,而我国的电子商务尚不完善。要实现全过程的电子商务,就要加快网络基础设施建设和网络互联,实现金融电子化及海关、税务、行政监管部门电子化,市场环境网上支付,建立安全体系认证结构,健全相关法律法规等等。
    1.2 国内外电子商务现状以下是根据CNNIC(中国互联网络信息中心)公布的中国B2C电子商务发展报告来进一步分析目前的网上购物的现状。(主要引用其中的分析图表)

    从上面的图可以看出网上购物选择节约时间和操作方便的分别占46.7%和44.2%,这说明随着生活节奏的加快,人们越来越希望拥有简单快捷的购物方式。

    从上面的图中可以看出人们总希望直接可以拿到物品,而不需要耽搁自己的时间,如果是送货上门,也可以当面检查所购的物品,这也表现出人们对厂商信誉的担忧。

    从上面的图中可以看出目前两个最严重的问题是安全性和产品服务。
    1.3 本文研究的内容本文详细介绍了基于PHP的网上购物系统的系统结构设计、实现方法和操作流程,并介绍了系统各个模块的主要功能和数据库设计及功能。该系统采用B/S结构,采用PHP技术进行动态窗体页面的设计。后台数据库选用MySQL 数据库。系统设计并实现了会员注册及登录模块,会员基本信息模块,会员购物模块,产品搜索模块,管理员商品管理模块,用户管理模块,订单管理模块等一系列功能模块。最后,对网上购物系统进行了系统测试,各个模块功能运行良好。
    1.4 开发可行性分析1.4.1 管理上的可行性把企业业务延伸到Internet上,建立电子商务网站,对运营商业企业来说,是企业对外展示信息,从事商务活动的窗口和界面。因此,高中层管理人员不会对此有很大的抵触情绪,主管领导也能够相应的支持。
    1.4.2 技术上的可行性根据系统目标需求分析得结果来看,不需要通过购买专业软件,仅采用phpStudyAdmin为开发环境,Apachec为服务器,选择PHP语言就可以解决相关的问题,从而保证系统功能的实现。
    1.4.3 经济上的可行性商业企业在运营过程中 ,经常会遇到产品的宣传受到限制,采购商或顾客只能通过上门咨询、电话沟通等方式进行各种信息的获取,受一定的时间与物理空间的局限并且成本较高。而本系统可以帮助企业正确认识电子商务技术在企业中的重要地位,以少量的时间和资金建立企业信息门户网站并架设一定范围的商务网络,以此来是企业与顾客间的经济活动变得更灵活、更主动。
    1.4.4 执行可行性本系统采用IE浏览界面,是大家熟悉的,对于用户只需要具有一般的计算机知识的人员都可以轻松上手。而且整个系统采用最友好的交互界面,简洁明了,不需要对数据库非常了解。因此,该系统的操作是可行的。
    2 系统需求分析2.1 目标和任务网上购物基于B/S模式, 通过对一些典型电子商务网站的考察、分析,并结合企业要求, 开发一套拥有前后台的信息化管理系统,需满足以下要求:

    统一友好的操作界面,保证系统的易用性
    商品分类详尽,可按不同类别查看商品信息
    为客户提供通过一个购物车模型就能够购买多个商品项的机制
    提供简单的安全模型,让客户必须先进行登录,才允许购买购物车的内容
    设计网站后台操作平台,管理网站的各项基本数据
    对不同的管理者设置不同的权限

    系统的主要任务有:

    处理对象:主要涉及的数据有:商品、顾客(会员)、顾客组、管理员(用户)、管理员组、订单、邮寄方式等
    处理功能要求:本系统完成商品、顾客(会员)、管理员(用户)、订单等之间的功能联系,形成一个有效的循环系统。具体包括以下几点:1,顾客注册为会员;2,管理员发布商品;3,订单管理;4,顾客组管理;5,管理员权限的管理等

    2.2 系统功能分析2.2.1 功能性需求概述在线购物系统是商家和购买者直接接触的桥梁,商家通过管理在线购物系统,向购买者发布、展示产品,而购买者也通过网站和商家交流、订购产品,从而达到现实交易的目的。
    在这个交易过程前,商家需要将产品信息录入,并且在前台显示出来,这其实就是一个信息发布系统,顾客在看到产品后,可能会有许多的问题,也就是需要针对产品留言,然后企业做出相应的回答,这个留言也属于信息发布的一部分。
    在交易过程中,顾客一般是不允许匿名购买的,顾客需要留下现实的个人信息才能收到现实中的产品,相信作为顾客,也很愿意方便线下联系。接着是在线模拟交易过程,顾客完成所有的选取后,就可以付账了,这里需要和支付网关连接。付账之后就是企业方通过后台的程序发现有新交易并进行线下处理。
    在交易完成后,企业方需要对近期销售情况进行统计,如会员资料的查询、修订等。
    通过对电子交易流程的划分,在线购物系统的系统功能主要由3部分组成:信息发布系统、会员管理系统和电子交易系统。电子商务的本质更趋向于电子交易,也就是前台和客户打交道的页面。
    通过细化系统功能,很明显电子交易部分是重点,电子交易页面需要完成哪些功能呢?
    逛在线购物系统和逛超市相似,进入超市时,顾客习惯拿购物筐或者购物车,再开始逛。逛超市时会不断查看想买的东西,直到确定买的那个产品的位置,在确定的过程中,会不断将其他物品放到购物车中,然后再将不适合的东西拿出,一直到结账。在选取物品时会发现,超市总是把相关的产品放在一个位置,以方便消费者,同时消费者也会拿出两个相同类型的东西进行比较。
    通过对超市购物的分析,了解了应该如何去模拟一个现实的交易过程。首先,需要一个产品的展示,方便顾客查询,还要有产品比较的数据,更有助于消费者比较,选择自己满意的产品;然后,要有一个购物车,方便顾客查看自己选中的产品,也可以把需要购买的产品放入购物车或者从购物车中拿出不需要的产品;最后是结账,和超市结账有很大区别,电子商务不允许匿名买卖。付账和收到产品之间有一个相对较长的时间,而且企业和顾客也不是面对面的,企业需要收集有效的信息,才能把产品送到顾客手中,所以结账时要确定用户是否登录,以收集用户的资料。
    通过以上分析可知,本系统分成两个模块:前台浏览模块和后台管理模块。
    前台浏览模块可再分为两个模块:产品浏览模块和购物模块,涉及产品首页浏览、推荐产品浏览、产品列表,以及查看产品详细内容、查看评论等功能。购物模块主要充当购物车作用,包括选取产品、对比产品、会员注册、登录及网上支付等功能。
    后台管理模块可分为3个模块:会员管理模块、产品管理模块和订单管理模块。后台管理模块主要涉及产品分类目录管理,以及产品内容的管理。会员管理模块主要是查看和修改,包括管理会员的基本资料以及购买的产品等详细信息。订单管理模块主要涉及对顾客订购的产品管理,按照订单的状态大致分为待处理的订单和已经处理的订单。

    2.2.2 系统流程分析在后台管理模块中,管理员需要先添加产品栏目分类,再进入产品列表页面,选择指定的产品栏目进行产品添加操作。在会员提交订单之后,管理员可以通过订单管理模块查看订单,并根据实际情况更改订单状态,管理员也可以通过会员管理模块查看会员信息。前台界面则是会员选取需要购买的商品,并提交订单。

    2.2.3 系统目录结构系统目录结构的规划非常重要,这样可以更加方便管理程序,体现出清晰的思路。通过对系统结构的分析,可以按照功能模块来划分系统目录结构。

    由图可以看出,系统文件保存在dzsw目录中,其下有13个子目录:admin目录主要保存后台管理的程序:data主要保存一些log信息,方便以后的查看:htmlarea目录保存文本编辑代码:includes目录保存公共模块中的代码,如MySQL连接之类的封装类等;install目录保存安装程序,方便把系统移植到其他的机器上,也方便初始化网站;js目录包括程序中需要使用的JavaScript程序;languages目录主要是为了界面的多国语言需要设计的,根据不同语言的需要调用不同的文件;modules目录保存连接支付网关的程序;myaccount目录保存关于本站的一些信息;styles目录保存网站的样式文件;templates目录保存模板文件;upload目录保存所有的上传文件,这个文件夹需要特别关照,在发布网站后,这个目录中的任何东西都是不能执行的,否则将产生隐患。
    2.3 数据库分析2.3.1 数据库需求分析数据库需求分析是所有数据库系统设计的最重要一步,在这个步骤里需要对系统的所有数据存储进行分析和整理,以便得出具有稳定性和扩展性的存储结构,这也是数据建模的过程。
    通过分析,不难得出主要有4个重要的实体对象,即产品分类存储表、产品内容存储表、订单存储表及会员存储表。 这4个表是在线购物系统的核心,所有的产品必须归属于某个类别,而这些产品分类的类别是不固定的,所以就需要一个分类存储表来存储产品的分类栏目。
    2.3.2 数据库概念结构设计根据以上对数据库的分析,就可以设计出能够满足用户需求的各种实体,以及它们之间的关系,为后面的逻辑结构设计打下基础。这些实体包含各种具体信息,通过相互之间的作用形成数据的流动。
    下面是在线购物系统的E-R模型图。

    2.4 系统开发环境2.4.1 硬件平台系统所需的硬件平台:PⅡ以上的CPU,512M以上内存,硬盘80G以上,windows xp操作系统。
    2.4.2 服务器平台
    WWW服务器:Apache
    数据库软件:MYSQL

    2.4.3 开发工具开发工具使用:phpStudyAdmin
    2.4.4 技术构架系统采用B/S技术架构。
    2.6 数据字典2.6.1 数据元素
    名字:会员名称描述:顾客用来登陆网站的唯一标识数据类型:字符长度:96
    名字:产品ID描述:用来表示产品的唯一标识数据类型:整型长度:10
    名字:订单ID描述:用来表示顾客订单的唯一标识数据类型:整型长度:8
    名字:管理员ID描述:用来表示管理员的唯一标识数据类型:整型长度:4
    名字:邮寄方式ID描述:用来表示邮递的唯一标识数据类型:整型长度:8
    名字:顾客组ID描述:用来表示打折顾客组的唯一标识数据类型:整型长度:6

    2.6.2 数据存储
    名字:顾客信息描述:注册为会员的个人信息组成:自动编号ID,用户名(Email),密码,组名,消费金额,注册日期。
    名字:顾客组信息描述:存储顾客的打折优惠组成:自动编号,名称,折扣。
    名字:产品信息描述:发布的产品的信息描述组成:自动编号ID,产品名称,产品单位,产品价格,产品描述。
    名字:订单信息描述:会员发出订单的信息组成:自动编号ID,用户联系方式,订单内容,邮寄方式。
    名字:管理员信息描述:系统管理的信息组成:管理员ID,管理员名称,管理员类别,管理员密码。
    名字:邮寄方式信息描述:送货方式信息组成:方式编号ID,名称,费用。

    3 系统概要设计3.1 系统结构设计系统结构设计是结构化系统分析与结构化程序设计之间的接口。结构化系统设计运用了一套标准的设计准则工具,采用模块化方法进行新系统层次关系和模块分解设计,结构化系统设计的核心是模块分解设计,其基本思想是以系统的逻辑功能设计和数据流关系为基础,根据数据流程图和数据字典,借助于一套标准的设计准则和图表工具,通过“自上而下”的逐层分解,把系统划分为多个大小适当、功能明确、具有一定独立性、并易于实现的模块,从而把复杂系统设计转化为多个简单模块的设计。
    3.1.1 系统体系结构目前,Web项目较为流行的开发模式是三层逻辑体系结构,本系统的开发按照Windows DNA (Windows Distributed Internet Application,分布式互联网应用结构),采用基于XML的三层B/S体系结构。Windows DNA提供了一种开发三层结构Web应用程序的通用方法是一个电子商务开发的良好环境。
    MVC的设计思想简介,MVC的设计思想将平台划分为表现层、业务层、数据访问层、数据库层。
    表现层(Presentation Tier)对应于MVC的V(View),负责系统的表示逻辑,主要是每个.php 页面。
    业务层(Business Tier)体现系统的业务逻辑。业务层起到了Business Delegate的作用,屏蔽了表示层,有利于重用业务模型。
    数据访问/集成层(Data Access/Integration Tier)负责进行数据访问及系统间交互操作。数据访问组件负责与关系型数据库打交道,进行具体的增删改查(CRUD)操作。系统集成代理/适配器组件负责与软件产品和外部系统打交道,交换数据和消息。数据访问/集成层组件意识不到业务层的具体逻辑,通过约定的接口让业务层来调用。
    MVC三层逻辑体系结构如图3-1所示。

    3.1.2 系统总体功能结构根据系统分析的结果数据流程图所描述的系统的逻辑模型,把数据流程图上各个处理模块进一步分解,确定系统的层次结构关系,把逻辑模型变为物理模型。
    在管理信息系统的设计过程中,系统划分的一般原则如下:

    子系统要具有独立性,即应把相对独立的事务处理过程作为一个子系统。这样的子系统具有较高的内聚性,与其它的藕合较弱,便于软件开发、使用及日后系统维护
    子系统的划分结果应使数据冗余度较小,应避免部分数据在多个子系统中出现,尤其是修改,以保证系统的安全性、可靠性和数据的一致性
    子系统的划分应能满足目前和日后管理上的需要,功能应齐全,操作要方便且为今后发展留有接口
    子系统的划分应以该单位的管理职能为依据,应尽量避免过分依赖于组织机构,以防止机构变更后为系统带来不必要的麻烦

    从公司的组织结构上出发,把整个管理信息系统划分成多个子系统。每个子系统的功能相对独立,但是子系统之间有必要的数据联系。
    对电子购物商城系统的数据流图采用变换中心法和事务中心法进行分析,对其逐步进行功能分解,直到分解成为含义明确、功能单一的单元功能模块,从而得到系统的功能模块结构图,即系统的功能图。如图3-2,反映了电子购物商城的系统功能模块的划分。

    1、前台模块详细功能描述其中,用户信息管理以及订单和购物车是禁止匿名用户访问的内容。

    商品信息模块:商品浏览(首页显示部分普通商品列表)、特价商品浏览(首页显示特价商品列表)、商品分类浏览、商品详细信息
    购物车模块:添加商品到购物车、购物车信息修改、结账
    用户信息管理模块:注册新用户、登录、用户修改密码、用户个人资料管理
    订单模块:查询个人订单列表、查询某笔订单的详细信息、取消订单

    2、后台模块详细功能描述
    管理员信息管理:管理员登录、添加新管理员、删除管理员、修改密码
    商品信息管理:添加新商品、修改商品信息、删除商品
    订单管理:管理订单是否确认、是否付款、是否发货、是否归档,并且对相应信息进行查询

    3.1.3 系统总体物理结构电子购物各个部门在不同的地点办公,网络拓扑结构以交换机为基础,各部门通过主交换机连接到公司主服务器,电子购物有不同办公地点。根据这个特点,系统利用微机局域网作为系统的基本物理结构。为了提高系统的可靠性和可扩展性,网络采用以网络集线器为基础,以双绞线为传输介质的星型结构。在每个业务发生点以及每个管理员分别配制微型计算机作为系统结点工作站,系统功能的实现,子系统间的数据共享与交互通过网络服务器完成。电子购物的系统网络拓扑图如图3.3所示。

    系统具体物理配置如下:
    利用公司已经架设好的企业局域网,主服务器采用:服务器、交换机、网络集线器,传输介质采用10M屏蔽双绞线为介质的星型网络拓扑结构,服务器作为系统功能实现和数据共享中心,应具有较好的性能和可靠性。在此基础上再配上一台提供web服务的服务器和一台MYSQL数据库服务器即可实现对本系统的全部物理支持。
    系统具体硬件配置如下:

    WWW服务器:INTER P4 2.4 512M DDR ROM 80GB 硬盘
    数据库服务器:INTER P4 2.4 512M DDR ROM 80GB 硬盘
    客户机:目前流行的PC兼容机
    网络连接设备:路由器、交换机、打印机、扫描仪等

    系统具体软件配置如下:

    操作系统:Microsoft公司Windown2000Server,网络协议采用TCP/IP协议
    WWW服务器:Apache
    数据库软件:MYSQL

    3.2 数据库结构设计3.2.1 数据库概念结构设计根据需求分析中的结果,数据库的关系模型设计如下:

    会员(顾客ID,顾客名称,顾客组,消费金额,注册日期,联系方式)
    产品(产品编号,产品名称,售价,数量,图片,备注)
    管理员(管理员编号,密码,管理员组)
    订单(编号,顾客名称,内容,备注)
    顾客组(ID,类别,名称,折扣)
    管理员组(ID,类别,名称,权限)
    邮寄方式(ID ,名称,费用计算)

    3.2.2 数据库逻辑设计经过需求分析,概念结构设计,了解了系统的存储结构需求后,可以得出8个表结构,即顾客组设置表、顾客表、产品评论表、产品列表、订单表和邮递方式表、管理员表、管理员组表。
    顾客组设置表是为了存储顾客的打折优惠,这里记录的事组的打折信息。表结构如表3-1所示。
    顾客组设置表(usergroups)结构



    序 号
    字 段
    描 述
    类型和长度
    主 键
    可 空
    默认值




    1
    Groupid
    自动编号
    Smallint(6)





    2
    Classes
    类型
    Varchar(30)





    3
    Grouptitle
    组名称
    Varchar(30)





    4
    groupdiscount
    折扣
    float


    0



    顾客信息表主要是记录顾客的详细信息,其中groupid是指定顾客组的编号,用户的名称采用Email,记录用户的登录时间可以方便的查看最后的登录时间,这样有助于对客户的跟踪服务。表结构如表3-2所示。
    顾客信息表(customers)结构



    序 号
    字 段
    描 述
    类型和长度
    主 键
    可 空
    默认值




    1
    Customers_id
    自动编号
    Smallint(6)





    2
    Email
    用户名称
    Varchar(30)





    3
    Grouid
    组名称
    Int(10)





    4
    Regdate
    注册时间
    Int(10)


    0


    5
    Lastvisit
    最后登录时间
    Int(10)


    0


    6
    Money
    消费总金额
    Decimal(15.2)


    0


    7
    QQ
    QQ联系方式
    Varchar(30)





    8
    msn
    msn联系方式
    Varchar(30)






    产品评论表主要是针对产品的评论信息,其中products_id就是产品的编号,date_add就是记录产品的添加时间,viewed是表示这条评论被浏览的次数。表结构如表3-3所示。
    产品分类表(classes)结构



    序 号
    字 段
    描 述
    类型和长度
    主 键
    可 空
    默认值




    1
    rid
    自动编号
    Mediumint(8)





    2
    Email
    用户名称
    Varchar(30)





    3
    Products_id
    产品 id
    Int(10)





    4
    review
    评论内容
    text


    0


    5
    Date_added
    添加时间
    Int(10)


    0


    6
    viewed
    被浏览次数
    Mediumint(8)


    0



    产品的信息列表记录的是产品的基础信息,包括产品的价格、重量、名称、所属分类、详细描述等字段。表结构如表3-4所示。
    产品表(products)结构



    序 号
    字 段
    描 述
    类型和长度
    主 键
    可 空
    默认值




    1
    Products_id
    自动编号
    Mediumint(8)





    2
    Classes_id
    分类编号
    Mediumint(8)





    3
    name
    产品名称
    Varchar(64)





    4
    model
    产品单位
    Varchar(64)





    5
    image
    产品展示图片链接
    Varchar(64)





    6
    price
    产品价格
    Decimal(15.2)


    0


    7
    weight
    产品重量
    Decimal(5.2)


    0


    8
    description
    产品描述
    Text





    9
    manufacturer
    生产厂商
    Varchar(64)






    订单表主要记录的就是用户订单的详细信息,包括购买人、购买人的联系方式、产品的价格、邮寄方式等信息。由于每次用户可能会定购多种产品,所以订单将分成两个表来记录,一张用来记录联系信息,一张表用来记录联系信息和产品之间的关联。表结构如表3-5和表3-6所示。
    订单表联系信息(orders)结构



    序 号
    字 段
    描 述
    类型和长度
    主 键
    可 空
    默认值




    1
    Order_id
    自动编号
    Mediumint(8)





    2
    D_name
    用户联系姓名
    Varchar(100)





    3
    cid
    会员id
    Int(10)





    4
    D_street_address
    会员地址
    Varchar(100)





    5
    D_tel_mobile
    会员联系方式
    Varchar(100)





    6
    Orders_status
    订单状态
    Varchar(100)





    7
    Shipping_method
    邮递方式
    Int(10)






    订单产品信息关联表(orders_products)结构



    序 号
    字 段
    描 述
    类型和长度
    主 键
    可 空
    默认值




    1
    opid
    自动编号
    Mediumint(8)





    2
    Orderd_id
    于orders表关联id
    Int(10)





    3
    Products_id
    产品关联id
    Int(10)





    4
    price
    产品价格
    Decimal(15.2)


    0


    5
    Final_price
    最终价格
    Decimal(15.2)


    0



    邮寄方式表主要是为计算邮寄费用产生的,不同的邮寄方式会带来不同的邮寄费用。表结构如表3-7所示。
    邮寄方式表(shipping)结构



    序 号
    字 段
    描 述
    类型和长度
    主 键
    可 空
    默认值




    1
    id
    自动编号
    Mediumint(8)





    2
    Shippingname
    费用名称
    varchar(200)





    3
    Fee
    费用计算公式
    Varchar(200)






    管理组设置表是为了存储管理员的不同管理权限。表结构如表3-8所示。
    管理组设置表(Admingroups)结构



    序 号
    字 段
    描 述
    类型和长度
    主 键
    可 空
    默认值




    1
    AdminGroupid
    自动编号
    Smallint(6)





    2
    Classes
    类型
    Varchar(30)





    3
    Grouptitle
    组名称
    Varchar(30)





    4
    Allow_class_see
    查看会员组
    float


    0


    5
    Allow_class_add
    添加会员组
    Int(10)


    0


    6
    Allow_class_delete
    删除会员组
    Int(10)


    0


    7
    Allow_product_see
    查看商品
    Int(10)


    0


    8
    Allow_product_edit
    编辑商品
    Int(10)


    0


    9
    Allow_product_add
    上传商品
    Int(10)


    0


    10
    Allow_product_delete
    删除商品
    Int(10)


    0



    管理员信息表主要是记录管理员的信息。表结构如表3-9所示。
    管理员信息表(customers)结构



    序 号
    字 段
    描 述
    类型和长度
    主 键
    可 空
    默认值




    1
    Admin_id
    自动编号
    Smallint(6)





    2
    name
    名称
    Varchar(30)





    3
    password
    密码
    Varchar(30)





    4
    admingroupsid
    管理组
    Varchar(30)





    5
    createdate
    创建时间
    Int(10)





    6
    lastvisit
    最后登录时间
    Int(10)






    4 系统详细设计4.1 系统设计在系统总体设计阶段已经提到过本系统采用MVC体系架构,但MVC架构具体是怎么实现的呢?下面我将介绍系统的具体实现功能。
    4.2 公共模块设计结构合理的系统都是经过改良的,将共同的东西取出来放置到相同的地方供引用和调用时设计系统必须要做的工作。
    4.2.1 数据库公共模块PHP中将创建数据库连接connection 独立出来做成公用的文件是最常用的做法,我将其命名为config.php,放置在include目录下。这个目录用来存储供整个系统公用的目录。当页面需要使用数据库连接时,只需要通过PHP的<? Require ‘include/db_mysql.php’;?>引用公用文件即可,这样不但避免了每个页面都编写数据连接,而且也能避免当数据连接发生变化时去修改每一个页面的麻烦。Db_mysql.php中使用了两种方式来获取与数据库连接:一种是获取已有的连接mysql_pconnect($dbuser,$dbpw),这样无须重复的建立连接,节约了资源:另外一种方式是mysql_connect($dbhost,$dbuser,$dbpw),这种方式是获取一个新的连接。在获取连接资源时,通常会传递3个参数,这里分别是$dbhost,$dbuser,$dbpw,代表的是带有端口号的主机地址,登录用户名和密码。
    Config.php中的定义代码如下所示:
    Define(‘DB_SERVER’,’localhost’);//数据库服务器Define(‘DB_SERVER_USERNAME’,’root’);//数据库用户名Define(‘DB_SERVER_PASSWORD’,’’);//数据库密码Define(‘DB_DATABASE’,’shoopdb’);//数据库名
    4.2.2 common.php和golbal.php单元Common.php是公共函数的文件,将公共函数独立出来可以大大节约编程时间及编程量,也为后期维护带来很大方便。
    Global文件是对全局变量的处理。
    4.3 产品模块设计4.3.1 产品类型设计产品一般有大类和小类之分,多级栏目使用树结构来展示。可以单击编辑和删除来修改。

    分类编辑修改分类名称和是否在页面显示。

    4.3.2 产品添加设计把产品作为一个类封装起来可方便对产品进行各种处理。Includes中的cla.products_p.php和cla.products.php文件就是对产品的封装。
    首先,添加产品的基本信息。

    添加完产品的基本信息后单击提交按钮,系统会向服务器端提交产品的基本信息,之后就会自动转向详细信息添加页面;如果添加产品基本信息不成功,将转向错误显示页面。
    完成提交后,程序会自动转向产品详细信息添加页面。该页面分为5个子页面。

    如添加错误可以修改。

    商品图片添加页面。

    4.3.3 产品信息查询查询页面可以通过商品名称、状态、基本信息、详细说明来查找产品信息。


    4.4 前台页面模块设计这里主要涉及产品的前台展示和购物车的编码制作等。
    4.4.1 首页设计作为在线购物系统,首要的任务是展示产品。除了展示产品,还可以加入其他元素来丰富页面。为了在有限的首页空间展示最吸引客户的信息,可把特价产品和新到产品的前5名展示在首页。

    在设置前台时,会把这些信息缓存起来,以方便再次调用此页面时不需要再次读取数据库,这样可以提高读取效率,也可以减轻服务器端的压力。Fun.common.php中的cacheexists($filename,$pam=‘’)函数就是用来验证是否在硬盘中有缓存,如果没有,将生成缓存文件。Cacheeesxists($filename,$pam=‘’)的实现代码如下所示:
    Function cacheexists($filename,$lpam=’’){ If($pam==’’){ $pam=$filename; } If(!file_exists(DIR_SHOOPD.”data/cache/cache_”.$filename.”.php”)){ Updataecache($pam); } Return DIR_SHOOPD.”data/cache/cache_”.$filename.”.php”;}
    4.4.2 产品详细信息用户在首页上看到感兴趣的产品后,对商品做进一步的了解。

    单击“您对此商品有话要说,您可以点击这里发表评论。”后就进入评论页面。可以看到URL http://127.0.0.1/dzsw/writerviems.php?products_id=23 ,这里是把商品的编号传递到评论页面,找到评论商品的ID。

    评论提交后,首先判断消费者是否已经登录,如果用户已经登录,需要把评论信息和会员昵称关联起来。需对Email进行验证,使用的是正则表达式。
    在添加评论之后返回商品展示页面,就可以看到针对商品的评论了。

    4.4.3 购物车设计购物车的实现流程:购物清单(购物车)->收货/送货人地址->选择送货方式->选择付款方式->确认并提交订单。
    购物车模块有两个链接,进入购物车和点击结账。“进入购物车”主要是显示已经放入购物车的详细信息,而“点击结账”的页面就是结账页面。

    4.4.4 结账处理单击购物车中的“结账”链接就可以进入结账页面。
    4.4.5 会员注册和登录页面设计注册会员需要填入会员的基本信息,这里的注册名是消费者的Email地址,用这个作为用户登录名的好处是可以强制性的让用户输入一个Email,而且Email地址是不会重复的。获取Email地址后,就可以通过这个Email地址给消费者发送订单处理等信息。在注册会员之后还需要让用户填入详细信息。

    将会员的详细信息和基本信息分开的好处是,用户在填入某信息出错时,只需要重复填入少量资料,避免了用户的麻烦。
    输入收货人地址后就是选择发送方式。

    在中国,一般提供了3种送货方式,即中国邮政的EMS、国内快递和普通快递。这3种方式的送货费用不同,计算方法为:产品的总重量*每公斤运送价格+其他固定费用。

    4.4.6 会员账户管理顾客在购买产品后,需要对自己的产品进行跟踪,需要知道自己购买产品的状态。

    4.5 订单管理模块设计订单分类,按照订单的状态分为:新订单、已审核订单、等待付款订单、部分付款订单、全部付款订单、已确认付款订单、取消订单等。

    4.6 会员管理模块设计会员管理模块是电子商务的最后一个模块。会员添加在前台已经完成,后台只需要一个浏览模块。

    还有一个会员组的选项,对于在线购物来说,打折时很有必要的,而打折方式是按照不同的会员组来设定的。

    5 系统测试5.1 系统测试5.1.1 测试的目的软件测试的目标和定义:

    测试是为了发现程序中的错误而执行程序的过程
    好的测试方案是极可能发现迄今为止尚未发现的错误的测试方案
    成功的测试是发现了至今为止尚未发现的错误的测试

    在对本网络书店销售管理系统测试时,也正是基于上述观点来进行的,测试的目的不是证明系统是完美的,而是发现问题,更好的解决问题,使系统不断完善,满足客户的需求。
    5.1.2 测试的准则为了能设计出行之有效的测试方案,必须深入理解并正确运用指导软件测试的基本准则。下面列举主要的测试准则:

    所有的测试都应该追溯到用户需求应该在开始测试之前就制定出测试计划
    把Pareto原理应用到软件测试中。Pareto原理说明,测试发现的错误中80%很可能是由程序中的20%模块造成的
    应该从“小模块”测试开始,并逐步进行“大模块”测试
    穷举测试是不可能的
    为了达到最佳的测试效果,应该由独立的第三方从事测试工作

    5.1.3 测试的方法测试任何产品都有两种方法:如果已经知道了产品应该具有的功能,可以通过测试来检验是否每个功能正常使用;如果知道了产品的内部工作过程,可以通过测试来检测产品内部动作是否按照说明书的规定正常工作。前一种称为黒盒测试又称功能测试,后一种方法称为白盒测试又称结构测试。
    在测试本系统时,采用的是黑盒测试来设计测试用例,而且在测试过程中,由于条件原因,不能完全按照测试规则由第三方来测试,测试过程和方案设计由作者完成。
    5.2 单元测试实例5.2.1 测试一在管理员登陆时,用户名或密码有一项为空或者填写错误,系统是否出现预先设定的操作提示。

    预期效果:输入错误及时报错
    具体操作:用户名、密码、任意一项为空或者填写有误
    结果:都出现相应的错误原因的信息提示
    结论:要求管理员必须填写正确的用户名和密码,才能进入管理页面,测试成功

    5.2.2 测试二发布产品或修改本站的公告﹑返回前台,看其是否更新成功。

    预期效果:发布和修改过后能及时更新内容
    具体操作:通过管理页面发布产品功能发布产品,修改公告功能修改公告的内容,看其是否发布修改成功
    结果:显示发布的新产品,公告内容更新为修改后的信息
    结论:发布产品,修改公告功能正常,测试成功

    5.2.3 测试三顾客浏览商品放入购物车后结账,如不登录能否提交订单。

    预期效果:该用户不能提交订单
    具体操作:前台顾客不登录提交订单
    结果:该订单无法提交,转入登录页面
    结论:购物车功能完善,测试成功。

    5.2.4 测试四顾客注册为新会员,注册号重复,密码没有6位。

    预期效果:注册失败,提示错误
    具体操作:注册号重复,密码设为5位
    结果:提示注册号已存在,密码至少6位
    结论:顾客注册功能正常,测试成功。

    5.2.5 测试五发货失败,取消订单。

    预期效果:顾客账户页面订单取消,已付款退回
    具体操作:在后台订单管理页面取消一已付款订单
    结果:前台会员账户页面显示订单取消,余额增加
    结论:订单功能正常,测试成功

    5.3 综合测试在以上测试的基础上,对系统功能进行了整体测试,依次检验系统功能是否符合系统开发的目标。经过使用大量的数据多次进行系统测试,发现系统存在的问题并及时改进,最终实现了网上购物系统的开发目标。
    5.4 系统的维护主要包括四个方面
    程序的维护:修改部分或全部程序,这种维护往往是在条件发生变化或原系统的效率低的情况下进行的
    数据文件的维护:按照用户的要求对数据文件进行不定期的修改
    代码的维护:随着系统的发展和变化,可能会出现旧代码不能适应新要求的问题,因此,有必要变更代码,予以维护
    硬件的维护:对系统所使用的设备进行维护

    6 总结本文讲述了在线购物系统的系统分析和设计,介绍了后台的商品发布、订单管理、会员管理以及前台的购物管理。
    在线购物系统只是所有在线购物系统的雏形,后台的商品发布、订单管理、会员管理和前台的购物管理是所有在线购物共有的特征。根据本实例的最初设计思想,对方案如下改进意见:
    完善系统功能除了给用户浏览商品外,提供给用户一个接口,用户对产品提出自己的建议,比如设计方面,包装方面,企业可以在网上搞一些活动,比如意见征集,这样提高用户参与的积极性,同时企业可以从用户的意见以及反馈信息中,找到使企业可持续发展的途径。只要是服务性的行业最根本的还是要坚持以客户为中心的原则。
    完善业务流程根据实际情况,使业务流程规范化。做到充分利用企业的资源,市场的资源,人力资源。使工作业务流程能够更合理,更有序的进行。
    完善人员培训机制对员工进行定期的培训,让员工了解市场,了解企业发展状况,了解客户需求。对员工进行一定的理论方面的培训。加强信息观念,这样提高了员工的素质,更加有利于员工参与工作,并胜任工作。在全球都提倡信息化的今天,面对我国的实际情况,所有人都应当提高自己的信息素养,只有这样我们才能很好的和世界贸易接轨,我国的电子商务才能有较快的发展,我国的经济发展才会有保证。
    通过开发这个项目,巩固了以前所学的理论知识,提高了自己理论联系实际的能力,为今后进一步学习新技术打下一个很好的基础,为走上社会实际工作岗位做好了充足的准备,为今后的职业发展打下良好的基础。面对以后各项挑战,我有信心做得更好!
    当然,由于时间和技术上的原因,该系统还是存在一些问题和不足的地方,比如在数据容量方面,由于没有经过大量数据的测试,数据库能承受多大负荷还不能确定,在美工方面,由于缺少美工经验,所以很多页面看起来可能会不那么漂亮美观,在一些细小的处理上可能还缺少进一步的考虑。
    0 评论 2 下载 2019-10-10 16:59:37 下载需要15点积分
  • 基于Android Studio实现的2048游戏

    1 需求分析1.1 背景与意义1.1.1 手机应用市场发展现状随着4G越来越普及以及手机应用的日益丰富还有智能水平的不断提高,从便携性和随身性这两方面来考虑,电脑所带来的体验已经不能跟手机相提并论了,它已经完美的超越了电脑。
    现如今Android、苹果等各智能手机已经基本占领整个手机市场,从而使得更多应用的出现,而手机游戏应用在其中占领主要位置。
    随着Android智能手机的普及以及游戏种类的多元化,使得Android手机游戏用户规模保持着稳步增长之势。
    1.1.2 国内外现状目前国内外的Android开发还是主要以应用开发为主,主要分成三类:企业应用、通用应用及游戏应用。企业应用的开发主要是一些大公司为了自己的品牌而开发的;通用应用主要是一些创业型公司或者独立开发者为了自己盈利开发的应用;游戏应用目前和通用应用相同。
    2048小游戏是一款最近风靡全球的手机游戏,简单的游戏模式和趣味的玩法,几乎游戏下载排行榜的前20名都可以看到“它的身影”。
    1.1.3 此游戏的意义现如今,手机游戏已在我们的生活中占据一席之地,并在一步步的壮大。可以说,随着它的迅猛发展,现今的手机游戏已经不单单是一种缓解压力的工具,而是形成了一种文化现象。随着游戏软件在市场的一步步壮大,与其有关的文化也随之传播。
    2048游戏的制作属于电子游戏中益智类小游戏,它做到了娱乐性、趣味性、教育性相统一。益智类的游戏即是需要去开动大脑思考从而获得游戏的胜利。简单的益智类游戏可以使玩家在娱乐中不断地开发大脑。这样一来就实现了在娱乐中学习。
    1.2 系统需求分析1.2.1 系统功能需求分析系统主要实现以下的几个功能:呈现游戏界面、重新开始游戏、当前分数和最高分数、游戏帮助等功能。
    重新开始游戏是当玩家无法满足当前进度时点击此按钮就会重新开始游戏,如果玩家处于不同关卡时提示重新开始游戏还是停留在此关卡。游戏帮助是当新手玩此游戏时无法知道游戏玩法时给予相应的提示。呈现游戏界面是游戏开始时主界面在游戏区域会生成4x4的矩阵同时在矩阵里面随机生成两个2或4的游戏卡片。当前分数和最高分数是显示此局玩家所获得的分数和历史上最高的分数,如果当前的分数超过最高的分数,那么最高分数显示当前的分数。如下图所示:

    1.2.2 游戏的基本规则在开始游戏后玩家通过滑动屏幕来操控卡片的移动方向,当卡片滑动中如果有两张卡片相同且他们中间也没有其他卡片时,在滑动的过程中这两张卡片会合并,显示为这两张卡片之和。在滑动过程中有三张卡片相同时只会合并向滑动方向两张卡片。在滑动中如果有两张卡片一样同时又有一张卡片的值跟这两张卡片相加的值时,滑动只会使那两张相同的卡片合并而不会接着让合并后的卡片和另一张卡片合并。
    2 系统分析与设计2.1 系统流程设计游戏进入开始页面,能够进入游戏的主界面并开始普通开局,从主界面能够重新开始游戏、查看帮助和进入关卡选择界面。当玩家点击重新开始按钮会弹出相应的对话框让玩家选择,如果玩家选择“是”时则重新开始游戏,如果选择“否”则返回游戏界面不做任何处理。在开始界面按返回按钮时则会退出游戏。
    游戏流程如下图所示:

    2.2 系统模块设计从总体出发,将该系统划分为三大模块:“菜单设计”、“界面设计”和“算法设计”。
    2.2.1 菜单设计菜单的实现是在游戏界面,可进一步划分为三个模块,分别是:“重新开始”、“退出游戏”、“游戏帮助”,如图所示:

    2.2.2 界面设计


    开始界面
    游戏界面









    2.2.3 算法设计当有两张卡片相同时,向它们可以碰撞的方向滑屏,卡片会移动到最底边并生成其两倍数字的卡片,并且生成一个“2”或者“4”的卡片。如图所示:



    生成2
    生成4









    当有两张卡片相同时,且在它们相同的方向有张跟他们之和的卡片,向它们可以碰撞的方向滑屏,相同的卡片会移动到无法移动的位置并生成其两倍数字的卡片,但合成的方向不会跟那两张数字的卡片合并,并且生成一个“2”或者“4”的卡片。如图所示:



    生成2
    生成4









    当界面上没有空位并且两两相邻的卡片不相同时游戏结束。如图所示:

    2.3 本章小结本章主要对游戏所实现的功能进行需求分析,分析了图形的特点和实现的可行性。对系统的性能进行了详细的分析。对系统的流程,系统所需的图形文件,系统的总体架构和系统用例进行了设计。通过本章的分析、设计能更加具体的了解系统功能,对系统所要实现的功能和图形文件有了更深的认识。为下一章系统功能的具体实现提供了可靠的参考依据。
    3 系统实现3.1 开始界面的实现游戏的主界面是按钮,只是为了实现界面的跳转,当玩家点击开始游戏就会调用loginActivity.java,此函数让页面跳转到游戏界面开始游戏,代码及图片如下所示:
    public class loginActivity extends MainActivity { protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.login); }}

    3.2 游戏界面的实现游戏界面主要是在activity_main.xml中当前分数、最高分数、重新开始按钮、退出游戏按钮、游戏帮助按钮、帮助按钮,当跳转到游戏界面时就会调用并执行MainActivity.java函数来展示游戏界面,代码及图片如下所示:
    protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);}

    3.3 游戏滑动卡片移动的实现当玩家滑动屏幕时,主要通过GameView函数来监听玩家手指滑动的位置,先通过获取开始坐标和结束坐标,然后通过比较结束坐标跟开始坐标的差值来判断玩家是怎么滑动屏幕的。判断出玩家的滑动轨迹后,通过调用swipeLeft、swipeRight、swipeUp、swipeDown方法来实现卡片的移动,代码及图片如下所示:
    private void initGameView(){ setColumnCount(4); //将面板设置成4列 setBackgroundColor(0xffbbada0); System.out.println("initGameView"); setOnTouchListener(new View.OnTouchListener() { /* * startX:手机刚开始在屏幕上的X坐标 * startY:手机刚开始在屏幕上的Y坐标 * offsetX,offsetY,分别是手指在屏幕上的X,Y上的偏移量 */ private float startX,startY,offsetX,offsetY; @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: startX = event.getX(); startY = event.getY(); break; case MotionEvent.ACTION_UP: offsetX = event.getX() - startX; offsetY = event.getY() - startY; if(Math.abs(offsetX) > Math.abs(offsetY)){ if(offsetX < -5){ swipeLeft(); System.out.println("Left"); }else if(offsetX > 5){ swipeRight(); System.out.println("Right"); } } else{ if(offsetY < -5){ swipeUp(); System.out.println("Up"); }else if(offsetY > 5){ swipeDown(); System.out.println("Down"); } } break; } return true; } });}//向左滑动public void swipeLeft(){ boolean meger = false; for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { for (int x1 = x+1; x1 < 4; x1++) { if(cardsMap[x1][y].getNum()>0){ if(cardsMap[x][y].getNum()<=0){ /* * 将下标为(x,y)所在位置的卡片上的数字 * 设置为,坐标为(x1,y)所在位置的卡片上的值; * 第二步,将坐标(x1,y)所在位置的卡片上的数字 * 设置为0 * (即:变成空卡片) */ cardsMap[x][y].setNum( cardsMap[x1][y].getNum()); cardsMap[x1][y].setNum(0); x--; meger =true; break; }else if(cardsMap[x][y].equals(cardsMap[x1][y])){ cardsMap[x][y].setNum(cardsMap[x][y].getNum()*2); cardsMap[x1][y].setNum(0); MainActivity.getMainActivity(). addScore(cardsMap[x][y].getNum()); meger = true; } break; } } } } if(meger){ addRandomNum(); checkComplete(); }}//向右滑动public void swipeRight(){ boolean meger = false; for (int y = 0; y < 4; y++) { for (int x = 3; x >=0; x--) { for (int x1 = x-1; x1 >= 0; x1--) { if(cardsMap[x1][y].getNum()>0){ if(cardsMap[x][y].getNum()<=0){ /* * 将下标为(x,y)所在位置的卡片上的数字 * 设置为,坐标为(x1,y)所在位置的卡片上的值; * 第二步,将坐标(x1,y)所在位置的卡片上的数字 * 设置为0 * (即:变成空卡片) */ cardsMap[x][y].setNum( cardsMap[x1][y].getNum()); cardsMap[x1][y].setNum(0); x++; meger =true; break; }else if(cardsMap[x][y].equals(cardsMap[x1][y])){ cardsMap[x][y].setNum(cardsMap[x][y].getNum()*2); cardsMap[x1][y].setNum(0); MainActivity.getMainActivity(). addScore(cardsMap[x][y].getNum()); meger =true; }break; } } } } if(meger){ addRandomNum(); checkComplete(); }}//向上滑动public void swipeUp(){ boolean meger = false; for (int x= 0; x< 4; x++) { for (int y = 0; y < 4; y++) { for (int y1 = y+1; y1 < 4; y1++) { if(cardsMap[x][y1].getNum()>0){ if(cardsMap[x][y].getNum()<=0){ /* * 将下标为(x,y)所在位置的卡片上的数字 * 设置为,坐标为(x1,y)所在位置的卡片上的值; * 第二步,将坐标(x1,y)所在位置的卡片上的数字 * 设置为0 * (即:变成空卡片) */ cardsMap[x][y].setNum( cardsMap[x][y1].getNum()); cardsMap[x][y1].setNum(0); y--; meger =true; break; }else if(cardsMap[x][y].equals(cardsMap[x][y1])){ cardsMap[x][y].setNum(cardsMap[x][y].getNum()*2); cardsMap[x][y1].setNum(0); MainActivity.getMainActivity(). addScore(cardsMap[x][y].getNum()); meger =true; } break; } } } } if(meger){ addRandomNum(); checkComplete(); }}//向下滑动public void swipeDown(){ boolean meger = false; for (int x = 0; x< 4; x++) { for (int y = 3; y>= 0;y--) { for (int y1 = y-1; y1 >=0; y1--) { if(cardsMap[x][y1].getNum()>0){ if(cardsMap[x][y].getNum()<=0){ /* * 将下标为(x,y)所在位置的卡片上的数字 * 设置为,坐标为(x1,y)所在位置的卡片上的值; * 第二步,将坐标(x1,y)所在位置的卡片上的数字 * 设置为0 * (即:变成空卡片) */ cardsMap[x][y].setNum( cardsMap[x][y1].getNum()); cardsMap[x][y1].setNum(0); y++; meger =true; break; }else if(cardsMap[x][y].equals(cardsMap[x][y1])){ cardsMap[x][y].setNum(cardsMap[x][y].getNum()*2); cardsMap[x][y1].setNum(0); MainActivity.getMainActivity(). addScore(cardsMap[x][y].getNum()); meger =true; } break; } } } } if(meger){ addRandomNum(); checkComplete(); }}



    效果1
    效果2









    3.4 重新开始游戏功能的实现当玩家点击游戏界面的重新开始游戏时,会弹出给玩家选择的对话框,让玩家选择“是”时游戏会重新开始,代码及图片如下所示:
    public void onClick(View view){ AlertDialog.Builder dialog3 = new AlertDialog.Builder(this); dialog3.setTitle("提示:"); dialog3.setMessage("你确定重新开始吗?"); dialog3.setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }); dialog3.setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }); dialog3.show(); }

    3.5 退出游戏功能的实现当玩家中途有事想退出游戏时会弹出给玩家选择的对话框,让玩家选择“是”时游戏会退出,代码及图片如下所示:
    public void onClick(View view){ AlertDialog.Builder dialog = new AlertDialog.Builder(this); dialog.setTitle("提示:"); dialog.setMessage("你确定要离开吗?"); dialog.setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { System.exit(0); } }); dialog.setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }); dialog.show(); }

    3.6 游戏帮助功能的实现当新玩家进入到游戏且不知道此游戏玩法时,玩家可以点击游戏帮助按钮来了解游戏玩法,点击按钮时游戏会弹出对话框显示游戏玩法,代码及图片如下所示:
    public void onClick(View view){ AlertDialog.Builder dialog2 = new AlertDialog.Builder(this); dialog2.setTitle("hey,guy!"); dialog2.setMessage("这么简单的游戏你确定需要帮助?"); dialog2.setNegativeButton("继续玩~", new DialogInterface.OnClickListener({ @Override public void onClick(DialogInterface dialog, int which) { } }); dialog2.show();}

    3.7 本章小结本章主要阐述本游戏相关功能的实现,详细的讲述了主界面的实现和各按钮功能的实现。
    4 测试本章主要对系统的功能进行测试,此次测试只是进行简单的调试,来确定游戏的各项功能是否能够正常运行。
    4.1 游戏流程测试该测试主要验证游戏能否实现场景的切换,当界面在开始界面时只显示按钮画面,当玩家点击此界面的开始按钮时跳转到游戏界面,如图所示:



    效果1
    效果2









    4.2 游戏模式该测试主要是测试游戏能否正常运行,当玩家滑动屏幕时能否正常的移动和当卡片相同时是否能够相加,还有就是测试游戏是否能正常结束。



    效果1
    效果2









    4.3 本章小结本章是对游戏系统进行简单的测试,通过测试可以看出此游戏可以正常的工作,同时一些功能也能够实现。
    5 总结本次课程设计的内容大部分都是参照课堂所讲以及一些网站给出的各种建议写的,在写的过程中遇到了很多问题,学到了很多东西。期间大概是因为基础不够好,只是找错误就花了很长时间。不过正因为这些错误,才能学到更多的知识,才能把知识点掌握的更牢靠,对于一些没有实现的功能,之后我一定会多花费些时间研究出来。我的课程设计优化的空间还相当大,希望老师能给出指导!
    6 评论 253 下载 2018-12-20 18:49:18 下载需要8点积分
  • 基于QT实现的基础图形绘图系统

    摘 要PaintWorks实现了图形数据的输入、编辑、填充、裁剪、变换、显示与保存功能。可绘制图形包括直线、圆、椭圆、多边形、曲线和3D六面体。交互界面简洁美观,操作实用方便。
    本绘图系统采用了面向对象设计,以C++11为基础,交互使用了Qt,基础图形的绘制使用了OpenGL,直线绘制使用了Bresenham算法,圆的绘制使用了中点圆生成算法,椭圆绘制用了中点椭圆生成算法,曲线绘制使用了3次Bézier曲线,多边形填充使用了多边形扫描填充算法,线段裁剪使用了梁友栋-Barsky算法,多边形裁剪使用了逐边裁剪算法。
    1 软件概述1.1 软件用途本软件主要用于基础图形绘制与保存,绘制完成之后可以对图形进行进行编辑、填充、裁剪等操作。
    1.2 特点本软件操作与显示简洁自然,简单方便。
    图形越新,所在图层越高;当用户选中某一图形时会自动切换到该图形对应的绘制模式并将该图形移动到最高图层。
    本软件的所有操作均可通过点击鼠标完成,其中,PaintWorks为使用频繁的操作与较为精细的操作提供了快捷键。
    2 软件安装为了免去用户安装的烦恼,PaintWorks给出了绿色免安装版,解压之后直接点击安装包中的PaintWorks.exe便可运行。(注:经过测试,不解压就运行可能会导致未知错误)
    3 使用指南3.1 图形绘制3.1.1 画笔颜色选取点击上方工具栏中的颜色选取工具(或按快捷键Ctrl+P),即可打开颜色选取对话框选取画笔颜色,之后线条颜色和填充色均为所选取的颜色。

    若不选取颜色,则默认线条颜色为黑色,填充色为绿色。
    3.1.2 2D图形PaintWorks可以绘制直线、圆、椭圆、多边形、曲线等二维图形,并可以对圆和多边形进行填充。绘制前首先点击菜单栏中的“新建画布”(或按快捷键Ctrl+N)新建画布。

    直线绘制:点击工具栏中的 按钮(或按快键键Alt+L),即可切换到直线绘制模式(软件启动默认绘制模式为直线)。点下鼠标左键并拖动,松开鼠标左键便可完成绘制,点下和松开的位置分别为直线的两端点。

    圆绘制:点击工具栏中的 按钮(或按快捷键Alt+C),即可切换到圆绘制模式。点下鼠标左键并拖动,松开鼠标左键便可完成绘制,点下鼠标左键的点为圆心,拖动的距离为圆的半径。点击工具栏中的 工具(或按快捷键Ctrl+F),即可完成对圆的填充。


    椭圆绘制:点击工具栏中的 按钮(或按快捷键Alt+E),即可切换到椭圆绘制模式。点下鼠标左键并拖动,松开鼠标左键便可完成绘制,拖动的横向偏移量为椭圆的横轴长度,纵向偏移量为椭圆的纵轴长度,拖动位移的中点为椭圆中心。

    多边形绘制:点击工具栏中的 按钮(或按快捷键Alt+P),即可切换到多边形绘制模式。单击鼠标左键绘制多边形第一个顶点,之后多次单击绘制多边形其他顶点,若要完成绘制只需单击第一个顶点,如图7. 选择填充色为绿色然后点击工具栏中的 工具(或按快捷键Ctrl+F)。


    曲线绘制:点击工具栏中的 按钮(或按快捷键Alt+V),即可切换到曲线绘制模式。先后单击4次鼠标左键绘制曲线的4个控制点,便可完成曲线的绘制。

    3.1.3 3D图形PaintWorks可以绘制3D六面体。点击点击菜单栏中的“新建3D画布”(或按快捷键Ctrl+T)新建3D画布标签页。

    此时新3D标签页中已经绘制完成一个3D六面体,如图11. 按键盘上的左右方向键可以控制这个六面体旋转。


    3.2 图形选中与编辑3.2.1 图形选中刚绘制完成的图形默认已经被选中,其关键点用蓝色圆点标记(如3.1.2中各图所示)。若要选中其他图形,只需点击想要选中的图形,该图形就会被选中且浮动到最高图层。

    3.2.2 图形编辑点击并拖动已选中图形的蓝色标记点,即可对图形形状进行编辑,操作与图形绘制时类似。

    直线标记点:两端点
    圆标记点:矩形四个端点
    椭圆标记点:矩形四个端点
    多边形标记点:各个顶点
    曲线标记点:四个控制点

    3.3 图形变换3.3.1 平移在选中情况下,图形中心有一个带十字的中心标记点。点击并拖动这个中心标记点,即可拖动这个图形。

    3.3.2 旋转在选中情况下,图形中心标记点有一条蓝色的直线延长出来的一个蓝色标记点,称为旋转标记点,旋转标记点与中心标记点的连线称为handle,如图14,中心标记点上方的蓝色圆点即为旋转标记点。点击并拖动这个旋转标记点,即可旋转该图形。


    直线旋转:以直线中点旋转
    圆的旋转:以圆心旋转,但由于圆是中心对称图形,所以旋转中视觉上没有明显效果
    椭圆旋转:以中心旋转,但由于椭圆旋转之后可以会出现变形,所以椭圆只能进行90度旋转,即只有在handle水平或垂直时做90度的旋转
    多边形旋转、曲线旋转:均绕中心标记点旋转

    3.3.3 缩放用户对当前选中的图形进行放大或缩小,放缩的中心为该图形的中心标记点。点击工具栏的放大与缩小按钮分别可以进行放大与缩小(快捷键为Ctrl + + 和Ctrl + -),如下图左右两侧图为曲线缩小和放大的效果。



    缩小
    原图
    放大










    3.4 图形裁剪用户可以使用工具栏中的 工具对当前选中的图形进行裁剪,裁剪仅限直线或多边形。
    首先点击这个工具(或按快捷键Ctrl+C),这个工具会显示被按下,此时可以在画布中绘制出一个红色的虚线框作为裁剪窗口。

    此时可以拖动红色虚线框的四个顶点对裁剪窗口进行调整,也可以直接重新绘制一个裁剪窗口。
    确定裁剪窗口之后,再次点击 工具(或按快捷键Ctrl+C),便可完成裁剪。其中需要说明的是,裁剪之后图形会被重绘,因为当前选择的画笔颜色为绿色,所以轮廓为绿色,与填充色融为了一体(而不是轮廓消失了)。

    3.5 图形删除与保存3.5.1 删除与清空
    用户可以对当前选中的图形、画布进行删除或清空操作
    要删除当前图形,只需点击工具栏中的 (或按快捷键Delete)即可将该图形删去
    要清空当前画布,只需点击工具栏中的 (或按快捷键Ctrl+Delete)即可清空画布

    3.5.2 保存图形用户可以保存画布上的图形为BMP文件(普通画布与3D画布均可保存),点击菜单栏中的“文件”>“另存为…”,即可打开一个文件保存对话框。


    输入文件名,选择路径,便可把当前画布的图像保存成BMP格式的文件。
    3.6 出错处理经过测试,本软件鲁棒性较好,但仍难以避免一些意外发生,如果出现了错误,重启即可。
    3.7 注意事项
    在新图形绘制过程中不要点到原有图形上,否则会选中原有图形,中断新图形的绘制。即便如此,原先的绘制信息也不会丢失,将绘制模式切换回来就可以继续绘制了
    由于裁剪算法所限,在裁剪凹多边形的一些特定情况下可能会出现一些不完全正确的裁剪结果

    4 系统框架设计4.1 开发环境


    操作系统
    Windows 10家庭中文版




    编程语言
    C++ 11


    外部库
    Qt 5.4,OpenGL 2.7


    编译器
    MinGW 4.9.1 32bit


    IDE
    Qt Creator 3.3.0



    4.2 系统架构概览用Qt向用户提供交互,用OpenGL提供底层绘制,考虑到Qt与OpenGL的兼容性,使用了继承自QWidget的子类QGLWidget实现Qt环境下的OpenGL绘制:

    QMainWindow负责与用户交互,并以标签页的形式集成了QGLWidget
    QGLWidget负责绘制本标签页的内容,并集成了各类FigureControl,用于将本标签页的交互信号送给不同的图形控制类
    FigureControl负责该类型图形的交互,有LineControl、CircleControl、EllipseControl、PolygonControl等,用于分别提供不同图形的不同交互方式
    Figure负责保存该图形的数据信息,包括顶点、圆心、半径以及轮廓点、填充点等,集成了Point类,图形绘制通过调用Point的draw函数完成
    Point负责实现绘制单个普通点或标记点,并向上提供其他常用功能函数支持

    系统核心架构图

    其中GLWidget派生出一个GL3DWidget,作为3D标签页,用于3D图形的绘制;在FigureControl层有一个CutWindow类,用于绘制裁剪窗口。系统架构中的聚集关系如下图:

    Point类作为轮廓和填充的构成部分聚集在Figure类中;Figure类的数组放在FigureControl中,由FigureControl处理本Figure的相关交互;CutWindow用于管理裁剪窗口;GLWidget为2D画布的标签页,GL3DWidget继承自GLWidget,作为3D画布标签页;所有标签页聚集在QMainWindow中,由QMainWindow与用户交互并向下发送消息。
    4.3 图形管理类:FigureControl图形管理类之间的关系较为简单,主要用于存储本图形的数据,并进行与本图形相关的交互,如鼠标点击、键盘按下、图形绘制、设置聚焦、裁剪当前图形等,类继承关系如下图:

    4.4 图形类:FigureFigure为所有图形的虚基类,继承关系如下图:

    Figure类提供所有图形都应有的共同接口:

    基础绘制:绘制图形、绘制图形标记点、清空图形
    基础变换:平移、旋转、缩放
    判断某点是否在该图形上


    SimpleFigure是对所有简单图形的抽象,SimpleFigure的轮廓均直接由点构成。而Polygon的边为Line,所以不属于SimpleFigure。SimpleFigure中提供了通用的draw()、clear()、isOn()。
    Area是对所有可填充图形的抽象,用于填充点管理,提供了与填充相关的函数。
    Line、Circle、Ellipse、Curve、Polygon分别为直线、圆、椭圆、曲线、多边形。其中由于Ellipse和Polygon与OpenGL库中命名冲突,所以在实现中命名为MyEllipse和MyPolygon,但相应代码文件名仍为Ellipse和Polygon。
    2 评论 53 下载 2018-12-10 20:51:16 下载需要8点积分
  • 基于SSH网上商城的设计与实现

    摘 要本文讲述了基于SSH框架的网上商城购物系统的设计与实现。所谓的网上商城购物系统是通过网站推广互联企业的商品和技术服务,并使客户随时可以了解企业和企业的产品,为客户提供在线服务和订单处理功能。
    从长期的战略目标来说,网上商城购物网站不仅是商品和服务的推广,而是通过Internet、企业内部网和企业外部网,将买家与卖家、厂商和合作伙伴紧密结合在了一起,因而消除了时间与空间带来的障碍。
    网上商城购物提供了多种检索途径,可以从分类、新品、特价等途径进行检索,快捷准确。与传统销售方式相比,在线销售能够提供海量商品信息。网上商城购物最突出的优点是:不再限制消费者的购买时段,扩大和巩固了客户群,从而增加了商品企业的核心竞争力,节省实际开店时需要投入的成本和租用费用。
    关键词:商城,在线销售,订单处理,在线支付
    ABSTRACTThis thesis relate to the design and implementation of based on the mode of SSH technical on-line sales digital products system. The so-called on-line sales digital product system through the Web site to promote Internet business of digital technology products and services, customers can learn more about the business and enterprise products, providing customers with the functions of online services and order processing.
    From the long-term strategic objectives, sales of digital products on-line site is not just digital products and services promotion, through the Internet, intranet and extranet make buyers and sellers, manufacturers and partners in the close connection together, thus eliminating the obstacles of time and space.
    Online sales of digital products that provide a variety of ways to retrieve, such as classification, new products and bargain products, the search way is faster and more accurate. Compared with the traditional way of marketing, online sales of digital products that can provide mass information. The most prominent advantage of online sales digital products are: no longer limit the consumers’ time to buy, consolidate and expand the customer base, thus increase the competitive ability of digital product business, and save the cost and rental cost of opening a shop actually.
    Key words: digital products, online sale, order processing, online payment 
    第一章 绪论1.1 课题背景互联网的兴起从本质上改变了现商品交易方式。由于互联网的兴起,国内各大企业从上个世纪产生了通过网络进行销售经营商品的想法。但是由于互联网上信誉难以保证、网络的商业环境的不成熟等一系列的原因,局限了网上商城的发展速度。进入21世纪以后,随着网络技术的发展、网络制度的健全,制约网上交易的各个瓶颈问题逐一被击破,各企业也纷纷的加入到电子商务的洪潮之中。
    中国互联网协会网络营销工作委员会调查显示,随着国内产业环境和消费市场的成熟,网络购物尤其是以商品为代表的网购将在今年实现更大发展。
    1999年底,随着互联网高潮来临。中国网络购物的用户规模不断上升。2010年中国网络购物市场延续用户规模、交易规模的双增长态势。2010年中国网络购物市场交易规模接近5000亿,达4980.0亿元,占到社会消费品零售总额的3.2%;同时,网络购物用户规模达到1.48亿,在网民中的渗透率达30.8%(《2013-2017中国网络购物行业市场前瞻与投资预测分析报告》统计数据显示)。对于一些传统企业而言,通过一些传统的营销手段已经很难对现今的市场形成什么重大的改变了。如果想将企业的销售渠道完全打开,企业就必需引进新的思维和新的方法。而网络购物正好为现今的传统企业提供了一个很好的机会与平台,传统企业通过借助第三方平台和建立自有平台纷纷试水网络购物,构建合理的网络购物平台、整合渠道、完善产业布局成为传统企业未来发展重心和出路。
    网络购物这一消费方式给人们生活带来的变化,除了购物场所的改变以外,更重要的是大大改变了传统的消费行为习惯,无论是否在网上购物,上网查看产品的相关信息,已经成为购物决策环节的重要组成部分,这一环节对购物行为的实际发生起到了至关重要的指导作用。
    1.2 目的和意义与传统方式销售相比在线销售有很多的优点如:
    1.2.1 检索便捷在线销售提供了多种检索途径,可以从分类、新品、特价等途径进行检索,快捷准确。
    1.2.2 信息量大与传统销售方式相比,在线销售能够提供海量产品信息。
    1.2.3 成本低,风险小,无地域限制网络销售最突出的优点是:不再限制消费者的购买时间和地点,同时大大降低中小商家开店的成本,提高其生存。
    1.3 系统设计思想对于典型的数据库管理系统,尤其是对于像网上商城购物系统这样的数据流量特别大的网络管理系统,必须要满足使用方便、操作灵活等要求。本系统在设计时应该满足以下几个目标:

    界面简洁,数据存储准确,信息处理安全可靠
    便于顾客快捷方便地查询商品信息
    实现网上购物的功能
    系统公告公布网上商城购物系统网站内的最新信息
    顾客可以随时查看自己的订单信息
    对顾客输入的数据,系统进行严格的数据检验,尽可能排除人为的错误
    系统最大限度地实现了易维护性和易操作性
    系统运行稳定、安全可靠

    第二章 系统开发工具及技术背景2.1 系统运行平台设置本系统的运行环境设置分为硬件环境和软件环境。
    2.1.1 硬件环境


    硬件
    配置信息




    处理器(CPU)
    英特尔 Celeron(赛扬) 1005M @ 1.90GHz 双核


    主 板
    宏碁 EA40_HC


    内 存
    8 GB ( 金士顿 DDR3L 1600MHz )


    硬 盘
    希捷 ST500LT012-9WS142 ( 500 GB / 5400 转/分 )


    显 卡
    英特尔 Ivy Bridge Graphics Controller ( 2176 MB / 宏碁 )


    显示器
    友达 AUO183C ( 14 英寸 )


    光驱
    建兴 DVD A DS8A9SH DVD刻录机



    2.1.2 软件环境


    环境
    软件




    操作系统
    windows7 64bit


    开发工具
    eclipse Mars2.0


    数据库
    mysql


    服务器
    tomcat7.0 64bit



    2.2 开发工具及技术简介2.2.1 开发工具简介eclipseEclipse 是一个开放源代码的、基于Java的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。幸运的是,Eclipse 附带了一个标准的插件集,包括Java开发工具(Java Development Kit,JDK)。
    MysqlMySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS (Relational Database Management System,关系数据库管理系统) 应用软件。
    MySQL是一种关系数据库管理系统,关系数据库将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,这样就增加了速度并提高了灵活性。
    MySQL所使用的 SQL 语言是用于访问数据库的最常用标准化语言。MySQL 软件采用了双授权政策,分为社区版和商业版,由于其体积小、速度快、总体拥有成本低,尤其是开放源码这一特点,一般中小型网站的开发都选择 MySQL 作为网站数据库。
    由于其社区版的性能卓越,搭配 PHP 和 Apache 可组成良好的开发环境。

    高效性:使得使用者可以降低开发和管理他们的数据基础设施的时间和成本
    智能性:提供了一个全面的平台,可以在用户需要的时候向其发送观察和信息
    可信任性:使得使用者可以以很高的安全性、可靠性和可扩展性来运行他们最关键任务的应用程序

    2.2.2 技术简介本系统采用目前较流行的一种Web应用程序开源框架——SSH框架。该框架由struts、spring及hibernate集成,能帮助开发人员在短期内清晰快捷的构建Web应用程序。其中以Struts作为其整体基础架构实现MVC的分离及控制业务跳转,并以Hibernate框架对持久层提供支持,再由Spring管理struts和hibernate。
    采用该开发模型,不仅有效的实现视图、模型与控制器的彻底分离,同时还实现了持久层与业务逻辑层的分离。因此当程序前端发生变化,仅需要微微改动模型层,并且当数据库产生变化时也不会对前端产生影响,由此提高了系统的可用性,从而有利于团队成员并行工作,大大提高了开发效率。
    第三章 系统分析3.1 可行性分析3.1.1 经济可行性经济可行性是指开发软件的设计成本与其经济效益相比是否相适宜。该套系统相比传统商城节约了房租并不受时间限制,大大节约了商家的成本,也提高了商家销售渠道,使得商家利益更大化。但此系统开发成本并不高。综上所述,该系统在经济可行性上可行。
    3.1.2 技术可行性该系统在软件方面采用了目前较稳定的eclipse工具和安全性较高的SSH框架进行开发管理平台的设计并选择mysql数据库作为数据的存储。软件满足安全、稳定、可行。
    此外,在硬件方面,由于现在科技发展快,硬件的配置较高,系统的开发与运行都是是可行的。
    3.1.3 运行可能性该网上商城购物系统操作简单,能为商家大大节约其成本,也让顾客实现了随时随地购物愿望。
    3.2 需求分析3.2.1 服务需求经过对各大商品购物网站及实际商品企业销售过程的考察、分析,要求本系统具有以下功能:

    统一友好的操作界面,保证系统的易用性,方便用户的操作
    具备商品信息的展示功能,方便浏览者对商品信息进行浏览与比较
    规范的商品分类,方便浏览者分类查找商品
    完善的购物车功能与用户结账功能
    设置网站公告信息模块,使浏览者能够及时了解网站的各项动态变化,及销售的新品、特价活动等信息
    商品销售排行功能
    订单信息查询功能、商品信息查询功能
    全面的后台管理功能,以方便管理员对网站信息进行管理及维护

    3.2.2 购物主流程
    3.3 功能分析3.3.1 系统前台功能需求
    商品展示模块:通过添加新品显示新品上市,销售排行展示自动显示商品销售量前的进行显示
    商品查询模块:按商品的类别查询商品的相关信息
    购物车模块:用户添加商品至购物车,查看购物车中的商品,从购物车中移除不满意的商品,清空购物车中的产品,修改所要购买的商品的数量
    收银台模块:用户满意购物车中的产品后进行结账并填写订单信息
    用户维护模块:为用户提供了用户注册、用户登录的功能
    订单查询模块:用户通过查看订单能够了解到自己的当前订单信息及历史订单记录


    3.3.2 系统后台功能需求
    商品管理模块:按类别查询商品信息,并进行维护商品的信息
    顾客管理模块:由于要保护顾客的信息,此模块仅实现查看顾客信息及删除错误顾客信息,与前台顾客查看和维护个人信息区别开
    一级分类管理模块:用于管理维护一级分类
    二级分类管理模块:用于管理维护二级分类
    订单管理模块:管理员查询订单详情并修改订单信息及完成订单受理


    第四章 数据库设计对于一个电子商务网站而言,为了支持较大的访问量带来的数据访问需求,使用桌面型的数据库管理系统是不能满足需要的,而且安全性也没有充分保障。因此,需要使用大型商业化企业级服务用途的数据库管理系统,如Mysql,Oracle等。本系统采用Mysql 数据库管理系统。
    4.1 数据分析对于本系统的数据库的需求而言,由于其主要是用于信息的提供、保存、更新和查询等。因此,需要分析该系统功能所隐含的对数据应用的需求,从而确定数据库的结构。

    用户注册、登录、验证等功能需要对用户信息建立数据表,其中的数据项可能包括用户E_mail、昵称、密码、住址等
    查看商品分类信息和商品详细信息等功能既需要对商品大小类别进行管理,也需要针对每一个商品进行管理,因此至少需要两张数据表
    用户购物后产生的订单需要进行管理,这里也包括订单的基本信息和详细信息等
    订单生成后,在订单处理的过程中,需要出货等,因此可能需要记录订单的发送情况
    需要系统管理员对该系统进行管理,因而需要建立管理员信息数据表,其中的数据项包括管理员ID、密码等

    这样,至少要创建如下的数据结构和数据项:

    用户信息,包括用户ID,用户名等数据项
    管理员信息,包括管理员ID,密码等数据项
    商品信息,包括商品ID,产品名称、单价、特价等数据项
    商品一级类别信息,包括电子ID,类别名称等数据项
    商品二级类别信息,包括电子ID,类别名称等数据项
    订单信息,包括订单ID,用户编号,订货地址等数据项
    订单明细信息,包括订单ID,商品ID,订货时间等数据项

    4.2 数据库的详细设计4.2.1 数据库E-R图
    4.2.2 数据库表根据此商品在线销售系统的实际情况,本系统的数据库命名为db_shoping,db_shoping数据库中共分为8张数据表。
    商品信息表此表用于记录网站所有商品的基本信息,是与商品相关联的商品的类别表。



    序号
    名称
    别名
    类型
    长度(字符)




    1
    pid
    编号
    Int
    255


    2
    pname
    商品名
    varchar
    255


    3
    market_price
    市场价
    varchar
    0


    4
    shop_price
    商城价
    varchar
    0


    6
    image
    存放商品图片
    int
    255


    7
    pdesc
    特价
    int
    255


    8
    is_hot
    普通商品
    int
    11


    9
    pdate
    上架日期
    datetime
    0


    00
    csid
    商品种类号
    varchar
    255



    商品一级类别表此表用于记录商品一级类别的基本信息,为方便用户查询商品和管理员管理商品信息而设立。



    序号
    名称
    别名
    类型
    长度(字符)




    1
    cd
    种类编号
    varchar
    255


    2
    cname
    种类名称
    Varchar
    255


    3
    describe
    种类描述
    Varchar
    255



    商品二级类别表此表用于记录商品二级类别的基本信息,为方便用户查询商品和管理员管理商品信息而设立。



    序号
    名称
    别名
    类型
    长度(字符)




    1
    csid
    二级种类编号
    varchar
    255


    2
    csname
    种类名称
    varchar
    255


    3
    cid
    一级种类名
    varchar
    255


    4
    describe
    种类描述
    varchar
    255



    订单信息表此表用于记录用户的基本订单信息,使用户方便查看自己的订单信息同时也为管理员出货提供基本订单信息。



    序号
    名称
    别名
    类型
    长度(字符)




    1
    oid
    订单ID
    varchar
    255


    2
    total
    总金额统计
    double
    0


    3
    ordertime
    订单生成日期
    datetime
    0


    4
    state
    订单状态
    int
    11


    5
    name
    姓名
    varchar
    20


    6
    phone
    联系方式
    varchar
    20


    7
    addr
    收货地址
    varchar
    50


    8
    uid
    用户ID
    varchar
    255



    订单明细信息表此表用于记录用户订单的详细信息,为用户提供详细的商品订单信息,同时也为管理员出货提供用户所订的商品的具体信息。



    序号
    名称
    别名
    类型
    长度(字符)




    1
    itemid
    订单明细编号
    varchar
    255


    2
    count
    商品数量
    int
    11


    3
    subtotal
    统计
    double
    0


    4
    pid
    商品id
    varchar
    255


    5
    oid
    订单id
    varchar
    255


    6
    uid
    用户id
    varchar
    255



    用户信息表此表用于记录用户的基本信息,是用户购买商品的权限基础,为用户维护和管理员管理用户提供信息。



    序号
    名称
    别名
    类型
    长度(字符)




    1
    uid
    用户ID
    varchar
    255


    2
    username
    用户名
    Varchar
    255


    3
    password
    用户密码
    Varchar
    255


    4
    name
    真实姓名
    Varchar
    255


    5
    email
    用户邮箱
    Varchar
    255


    6
    phone
    联系方式
    Varchar
    255


    7
    addr
    收货地址
    Varchar
    255


    8
    state
    账号状态
    Varchar
    255


    9
    code
    加密
    Varchar
    255



    管理员信息表此表用于记录不同权限管理员的基本信息,可以管理系统的所有信息。



    序号
    名 称
    别 名
    类 型
    长度(字符)




    1
    adminid
    管理员ID
    varchar
    255


    2
    UserName
    管理员名称
    Varchar
    255


    3
    password
    管理员密码
    Varchar
    255



    第五章 详细设计与实现本系统网站的主界面设计结构采用了上左右的结构,上部分为主功能菜单区,主要是显示网站的主功能,左侧为用户注册、登录,公告浏览,商品查询,右部分为信息显示区,销售排行榜 主要是为方便用户提供了一些最新上架的商品信息。
    5.1 系统前台基本功能设计与实现本节对系统前台主要功能设计进行详细的说明。
    由于网上购物必须保证用户的正确性、购物的安全性,需要对用户资料进行有效的管理,因此用户管理是电子商务网站的重要内容。它主要包括用户注册、登录和验证、用户注册和用户注销等功能。
    5.1.1 注册实现目标用户单击注册后需要输入密码和确认密码、用户真实姓名、住址、性别、E_mail、。这里需要用户通过E_mail注册是考虑到两点:第一,用户的E_mail是惟一的;第二,通过用户的E_mail可以很方便快捷的与用户取得联系。用户在提交注册信息后,系统会对注册页面填写的正确性进行检查。对于不符合要求的填写会给出必要的提示,并返回修改;对于符合要求的注册表单,用户提交信息后也要返回相应的欢迎信息,用户提交的正确注册信息将被存入系统的数据库中。

    实现过程所属页面名:userReg.jsp
    表单属性:name=”form1” action=”<%=path%>/userReg.action” method=”post”



    名称
    类型
    含义




    username
    Text
    输入用户名称


    password
    Password
    输入密码


    repassword
    Password
    输入确认密码


    name
    Text
    输入真实姓名


    addr
    Text
    输入住址


    phone
    Text
    输入联系方式


    e-mail
    Text
    输入E-mail地址


    同意以下协议并注册
    submit
    提交用户信息



    5.1.2 登录和验证顾客登录程序流程描述首先要进行身份验证顾客的用户名和密码,如果用户名存在,则登录成功,反之,当用户名不存在的时候,提示信息:“登录失败:用户名或密码错误或用户未激活!”,返回登录界面重新输入用户名和密码。

    实现目标就本系统的购物过程而言,在顾客在浏览商品时,不需行登录,但在使用购物车的时候,必须登录,并实现整个购物的过程。顾客登录需验证用户名及密码,然后系统决定是否有权限进行操作,如进行使用者信息修改、购物车及账单清算等。
    实现过程所属页面名:index.jsp



    名称
    类型
    含义




    userName
    Text
    输入用户名


    password
    Password
    输入密码


    注册
    button
    跳转注册页面


    登录
    button
    跳转登录页面



    5.1.3 用户注销实现目标已注册的用户只要登录本系统后便可以通过单击退出即可注销该用户,注销后可继续注册用户或者登录其他用户。

    5.1.4 商品展示模块设计与实现实现目标为了方便顾客选购商品,本系统采用了两种展示产品的方式。一种是以类别查询为基础查看商品,另外一种是新品上架、特价产品和销售排行的展台。以销售排行查看商品为例,顾客在首页的的销售排行即可查看销售排行,方便顾客查看畅销的商品相关信息及详细信息。

    实现过程所属页面名:index.jsp
    通过查询orderItem表中按统计出的商品销售数量排序。
    5.1.5 购物车模块的设计与实现购物车程序流程描述用户选购商品后添加到购物车中,在购物车中进行查看,同时对不满意的产品可以进行移除,对要购买的产品可以移除产品和清空产品。用户决定购买产品后可以去收银台进行结账。

    实现目标用户购买产品的核心功能是购物车和订单提交。购物车模块的设计包括:顾客添加商品到购物车和顾客对购物车的管理,如清空购物车和将购物车中商品从购物车中移除等。购物车要具有持续性,而且不限制用户购买商品的数量,也就是购物车可以显示很多商品。

    实现过程所属页面名: cart.jsp
    页面属性:\<a href="/shop/order_save.action" id="submit" class="submit"\>
    //将购物项添加到购物车中:执行的方法public String addCart(){ //封装一个CartIItem对象 CartItem cartItem=new CartItem(); //设置数量 cartItem.setCount(count); //根据pid进行查询商品 Product product=productService.findByPid(pid); //设置商品 cartItem.setProduct(product); //将购物项添加购物车 //购物车存在session Cart cart=getCart(); cart.addCart(cartItem); return "addCart";}//清空购物车public String clearCart(){ //获得购物车对象,调用清空的方法 Cart cart=getCart(); cart.clearCart(); return "clearCart";}//从购物车中移除购物项public String removeCart(){ //获得购物车对象 Cart cart=getCart(); cart.removeCart(pid); return "removeCart";}//我的购物车:执行的方法public String myCart(){ return "myCart";}
    5.1.6 收银台模块的设计与实现实现目标用户选择完商品后,需要确认购物车中内容,即结帐,点击下一步,此时页面跳转到结账页面。在结帐页面,需要用户填写收货地址、收货人、联系电话、付款方式,以便生成订单。当完成一次交易所需要的用户方面的信息都已齐全,只须生成订单即可进入配送过程。如果用户所填写的订单信息正确,系统会返回给用户此次交易所产生的订单号码。

    实现过程所属页面名:order.jsp
    表单属性:action=”/shop/order_payOrder.action” method=”post”



    名称
    类型
    含义




    userName
    Text
    收货人账号


    userRealname
    Text
    收货人姓名


    userTel
    Text
    收货人联系电话


    userAddress
    Text
    收货人地址


    odderSonghuodizhi
    Text
    送货住址



    5.1.7 查看订单模块的设计与实现实现目标用户填写完订单并提交成功后可以通过查看订单按钮看到自己的订单及详细信息。

    实现过程所属页面名:orderList.jsp
    5.2 系统后台管理功能设计与实现本节对系统后台功能设计进行详细的说明。

    5.2.1 基本信息管理设计与实现实现目标该功能主要针对用户信息、分类菜单、商品信息,订单信息的增删查改,这些功能的设计需要运用在基本信息的每一个页面中。在对此类基本信息进行添加、删除和更新操作的同时,操作所执行的结果需要通过sql语句返回到该系统的数据库中,从而在相应的页面上刷新并显示出新的结果集。模块的输出成功后将有成功的提示信息。
    实现过程所属页面名:home.jsp
    客户管理主要是使用userDAO中下的findById和findAll方法来删除和获取数据库中的数据。进入客户管理界面时通过findAll方法来显示所有用户的信息,点击删除时,通过findById方法获取指定的用户ID,使用sql语句进行操作。
    5.2.2 订单信息管理设计与实现实现目标该模块是针对顾客的订单明细信息管理功能进行的详细设计,主要是对顾客订单信息的查看、删除和订单受理的操作。同时管理员可以就此查看系统中各类订单的详细信息。
    实现过程订单管理模块使用了AdminOrderAction中的方法,当用户提交订单时,订单状态默认为未受理。管理员查看订单时调用了findAll显示到页面中。
    5.2.3 商品信息管理设计与实现实现目标在商品管理模块中除了基本的信息管理功能外,为了方便顾客浏览到商品的真实外貌,在添加商品模块中增加上传商品图片的功能。
    管理员在商品添加页面中输入添加的商品基本信息,我将商品分为特价和非特价商品来管理。然后进入图片上传页面,在上传文件位置上添加图片后点击提交,提交成功后有提示信息弹出。完成后查看商品信息时,管理员查看商品图片信息。
    实现过程所属页面名:product下的list.jsp
    本模块主要是对商品表进行管理,所以就是一些查询和删除操作,我在做的时候,主要讲商品分为热门和非热门商品,在管理时也是分开管理,主要通过判断is_hot来区分是否为热门商品。
    第六章 系统测试与性能分析完成需求分析、系统设计和程序编码等前期工作后,我得到了该系统的源程序。然而,在没有经过系统测试及性能分析前之前的各阶段中该系统程序都可能遗留下一定的错误和缺陷,若不及时查找出这些错误和缺陷并进行修改,这个软件产品就可能产生不稳定或者是不能正常使用,严重的还可能会导致巨大的损失。因此系统测试与性能分析是必不可少的。
    系统整体测试步骤:

    单元测试单元测试是整体测试中的第一步,通常在编码阶段进行。在源程序代码经过编译、评审、确认没有语法错误之后,便可开始进行单元测试。
    集成测试集成测试在单元测试完成之后,将所有的模块概要设计要求组装成系统时进行的测试,主要目标是发现与接口有关的问题。
    确认测试经过集成测试之后,接口错误已经发现被发现并改正了,接下便要进行确认测试。所谓确认测试就是验证所开发软件的功能性及其他特性是否符合软件需求规格说明书的要求。所以,确认测试又被称为有效性测试。
    系统测试系统测试是更大范围内进行测试,它将经过确认测试的软件作为整个基于计算机的系统的一个元素,在实际运行环境下,对系统进行的一系列集成和确认测试。

    6.1 前台首页的测试测试过程是单击页面左边的商品类别,选中某一类别后,就可以在页面右边显示该类别下的所有商品。通过对该功能的重复大量测试,该模块能够准确地实现此项功能。
    6.2 购物车模块的测试在本模块中首先对修改数量单元进行了测试,所修改的数量如果是有效数字,则修改所购商品数量成功,否则修改失败,系统自动将商品数量重置为“1”。通过随机测试了几组数据,其修改的数量和累计的金额均正确。如果客户购买了同一个商品,则就会进行该商品购买数量的累加,不会重复出现。然后针对购物车应该不限制顾客购买商品的数量这一原则进行测试,通过大量向购物车中添加商品来进行测试,测试结果为购物车中可以显示所添加的所有商品信息,测试结果取得了预期的效果。

    6.3 用户注册模块的测试在用户注册模块中涉及到了三项数据,项目分别为密码、用户真实姓名、住址、性别、联系方式、E_mail、QQ。测试这个模块准备的数据有:

    huqingkagn、huqika、kang、455689427@qq.com、15232152856、sjz、1
    ccc、ccc、ccc、aaa@shop.com、ccc、ccc、1
    ddd、ddd、ddd、aaa@shop.com、ccc、ccc、1
    bbb、bbb、bbb、aaa@shop.com、bbb、bbb、1

    首先进入注册页面,输入第一组数据,单击注册按钮系统会提示表单信息不能为空,因为表单信息要全部填写,而真实姓名没有写值。然后测试第二组数据,可以明显看出所输入的密码与确认密码是不一致的。然后测试第四组数据,其中九项均是系统正确的数据,因此测试通过。在填写用户名时,用户可以查看自己的用户名是否可以使用。
    6.4 商品管理模块的测试6.4.1 添加功能的测试对商品添加模块准备的商品名、商品描述、商品图片、市场价、商城价、商品种类,分别准备的测试数据为:数码、手机、诺基亚、3300、3000。其中类别的选择是通过下拉列表框来选择,图片单击上传图片后,找到图片的地址点击确定就可以上传,单击添加按钮,执行添加操作,数据提交后,就完成了商品的添加。之后,进行了几项测试,如漏填了几项数据、上传图片格式不正确,这些数据的操作结果都是失败。

    6.4.2 删除功能的测试以编号为4的这条记录为例,通过单击该条记录上的“删除”链接按钮,对商品信息进行删除的操作,数据提交后,ajax方式直接局部刷新,通过对该模块的浏览,该信息的确已被删除,查无此条记录,说明此模块运行无误。
    第七章 结论网上商城购物系统采用了比较流行的SSH模式,使用当前较流行的SSH框架和拥有较高安全性与稳定性的数据库Mysql来完成系统的设计与开发。系统充分考虑了用户的需求,最终达到了预期效果,并添加了一些附加功能,使系统更加人性化。操作者可以随时对所有的信息进行查询,并且每个模块都提供信息浏览的功能。
    本系统实现了商品企业通过互联网向消费者推销自己的商品和服务,在网上商城购物系统网站消费的顾客可以通过计算机网络搜索到自己需要的信息,购买自己需要的商品,在线下达订单。然后由网上商城购物系统后台来处理相关的订单信息、网站维护等信息。
    0 评论 8 下载 2019-10-08 17:48:36 下载需要20点积分
  • 基于Jsp的OA系统的设计与实现

    摘 要学习和研究办公自动化中涉及到的知识和技术是实现办公自动化系统的前提条件。通过学习研究,掌握了其中的关键技术之后,结合自身的理解,对其做出了相应的表述。同时也成功的实现了系统中涉及到的一些模块。这些实现了的功能模块,在日常办公中都能被用到。具有一定的实用价值。
    办公自动化(Office Automation,OA)的诞生离不开科技不断发展的趋势推动,虽然今天它已趋于完善。但在信息蓬勃发展的今天依旧是一项非常活跃和具备顽强生命力的技术。办公自动化在企业当中的应用具有无法取缔的重要意义。对办公自动化的学习和研究是有必要的。
    办公自动化有很多的优点:避免对资源不必要的浪费,节约成本,高效管理等,它能让管理变得规范和科学。在查阅了很多与OA系统相关的资料后,对其进行了深入的学习。在了解到办公自动化现在的发展状况后,对学习和研究它的现实意义进行了深入的分析。
    为了开发出更加完善的系统,本文以J2EE为技术基础,企业级的MVC设计模式为前提,科学的对系统进行了分析,阐述了OA系统的核心技术及其他工具的使用。在掌握Spring、SpringMVC、Mybatis等框架技术之后,运用Maven工具进行项目的管理,为办公自动化系统的开发做了充足的准备。
    关键词:办公自动化,J2EE,Maven,SSM
    AbstractLearning and studying the knowledge and technology involved in oa is the precondition to realize oa system. Through study and research, master the key technology, combined with their own understanding, make the corresponding expression. At the same time, some modules involved in the system have been successfully implemented. These realized the function module, can be used in the daily office. It has certain practical value.
    The birth of Office Automation (OA) is driven by the continuous development of science and technology, although it has been improved today. However, it is still a very active and tenacious technology in today’s information boom. The application of office automation in enterprises is of great significance. The study and research of office automation is necessary.
    Office automation has many advantages: avoid unnecessary waste of resources, save costs, efficient management, etc., it can make management become standardized and scientific. After consulting a lot of materials related to OA system, I made an in-depth study of it. After understanding the current development of office automation, the practical significance of learning and studying it is analyzed in depth.
    In order to develop a more perfect system, this paper takes J2EE as the technical basis and the enterprise-level MVC design pattern as the premise, scientifically analyzes the system, and expounds the use of OA system’s core technology and other tools. After mastering Spring, SpringMVC, Mybatis and other framework technologies, I used Maven tool to manage the project and made sufficient preparation for the development of office automation system.
    Key words: Office Automation, J2EE, Maven,SSM
    第一章 前言在技术发展和社会需求变更的推动下,办公自动化也在不断的成熟起来。随着信息爆炸时代的来临,为了挖掘、分析、传输和存储人们日常生活中所产生的各种数据以分析其潜在价值。各种技术的发展日新月异,其中通讯技术的发展尤为迅猛(5G已不在停留在概念上)。分布式数据库也得到了广泛的运用。这些技术的发展使得办公自动化有了更大的提升空间。
    公文在传统的办公模式下,多是手工传递处理,这种方式存在安全性不足、信息难以进行统计和分析等弊端,最主要的还是协同办公难以实现。跟传统的办公模式相比,办公自动化有着效率高、资源共享及时、以及节约成本等优势。
    现在的办公自动化系统,不止能为管理者的决策提供科学和正确的指导,还能提高领导的管理水平,同时推动了各个单位的信息化建设的进程。办公自动化在各个单位中都起着举足轻重的作用。
    随着技术的发展和社会的进步,办公自动化的内涵和外延也在不断的发生变化。也就是说它是一个动态的定义。为了应对现代化办公模式及其需求的不断发展,这就对办公自动化的整体系统结构和处理能力以及运作平台提出了越来越高的要求。办公自动化是信息时代发展下的必然产物。在协同办公理念深入人心的当今社会,办公自动化的使用程度将成为一个国家现代化进程的重要指标和衡量标准。
    1.1 项目背景目前国内外还存在着很多日常办公运用手工操作的企业和单位。这样将导致大量的信息不能得到及时的共享和充分的利用。而且随着时间的推移,不断累积的信息将成为企业和单位的一种负担,光是对信息的管理、分类、索引、利用就极其困难,对于企业和单位来说不仅是一种时间成本的投入,还是对人力和财力的损耗,最重要的是工作效率低下,决策者还难以从中挖取出有用的数据来用作决策的依据。长此以往,信息的不断累积对企业和单位来说将是有百害而无一利的。办公自动化的意义自然不言而喻。
    过去的办公自动化系统,在当时的技术条件限制下存在很多的缺陷,早已不能胜任现代办公的需求。集成化程度低,不能实现充分的资源共享,系统韧性差等因素成为了其致命的缺点。特别是过去的这些办公软件,大都是静态的。由于不能适应信息时代的需求,导致了它们的应用范围有很大的局限性,应用效果也很差。因此在信息化时代下的办公自动化系统需要具备更强劲的功能。在降低企业成本,充分利用企业内部资源,加快业务流程,显著提高工作效率和应对环境变化等方面都要有良好的表现。
    1.2 OA简介办公自动化简称为OA(Office Automation)是集计算机科学、通讯技术、系统科学、行为科学为一体的综合性技术。现代的办公自动化系统采用的是Internet/Intranet技术,基于工作流的概念,使得企业内部的员工能够方便快捷地共享信息,高效地协同工作;改变传统复杂、低效的手工办公方式,以实现迅速、全方位的信息采集和信息处理,为企业的管理和决策提供科学的依据。企业实现办公自动化的程度也是衡量其实现现代化管理的标准。办公自动化不仅兼顾个人办公效率的提高,更重要的是能够实现群体系统工作。凭借网络,这种交流和协调几乎可以在瞬间完成。
    1.2.1 办公自动化的国内外研究情况不管在过去还是在现在,办公自动化的核心都是公文处理。由于公文处理本身的特性限制,这对办公自动化系统在公文处理的流程控制方面提出了更高的要求。
    目前的办公自动化系统在企业中得到了广泛的运用,它的工作流管理自然也就延伸到日常办公,和事务处理流程中去了,比如会议的管理、财务报销审批、休假审批等等。
    办公自动化系统通过对工作流的管理,使得公文在流转、审批、发布等方面极大的提高了其效率。实现了办公管理的规范化和信息管理的规范化,极大的降低了企业的运行成本。
    现在国内主流的OA系统有泛微、蓝凌等,其主要功能模块见图1.1和图1.2:



    国外OA系统的代表有EGroupware,其主要功能模块如图2.3所示:

    对比分析上图可以看出,这些OA系统中都有流程管理这个模块。要实现对这个模块的管理,还需要有相应的软件系统作为支撑。即工作流管理系统(WFMS),工作流管理系统的主要作用是:调用和管理业务过程中的各种活动的先后顺序和资源,以此达到自动化办公的目的。
    1.2.2 本文的主要内容及章节安排本文对办公自动化进行了分析和研究、并且还将JBPM(Java Business Process Management)和SSM框架技等技术应用在了办公自动化领域中,通过Maven这样的项目管理利器来对项目进行管理,从一定程度上保证了系统整体架构的稳定性和具体实现代码的鲁棒性,致力于实现系统各个模块之间的“高内聚、低耦合”度,使得系统各个模块的复用程度、系统功能的扩展性和可维护性都得以提高;通过使用管理工具Maven还可以轻松的实现对系统中各个模块的管理。
    本文的章节安排和主要的研究内容所下所述:

    第一章节:前言主要概述了本文的研究背景和这个课题在国内外的研究情况,并且还通过对项目背景的简述,说明了对办公自动化进行研究的意义。
    第二章节:主要是相应架构技术和开发环境的概述,首先介绍了所要用到的相关技术的理论知识,然后对Spring、SpringMVC、Mybatis等框架的运行机制进行分析,它们是构建OA系统的关键技术。
    第三章节:这一章节对系统开发进行了必要的分析,为系统设计环节提供了相应的理论基础。
    第四章节:系统设计是实现系统开发的主要环节和前提条件,对办公自动化系统的主要模块进行了详细的分析和设计,为后续的具体实现奠定基础。
    第五章节:对系统的具体实现也作了简要描述叙述的同时,还对开发环境的搭建,框架的整合进行了简要的说明,在系统实现章节附录了相应模块的核心代码。
    第六章节:总结全文,同时对系统的进一步提升和完善提出改进意见。

    第二章 系统架构技术的研究及简单的环境搭建概述2.1 Web应用体系结构技术总是在不断发展和进步的,企业级应用程序的架构也不例外,在多年的发展演进过程中,它的设计思想也产生了巨大的变化。分析它的发展历史,可以发现:在大型机和个人计算机盛行的时期,很多的应用程序都是运用的两级体系结构,在这种结构中,服务端的主要职责是数据的存储和检索,而绝大多数的业务逻辑还交由客户端来进行处理。随着应用程序中需求的增多,其中包含的业务逻辑变得越来越复杂的时候,客户端就显得不堪重负啦,为了减轻客户端的压力而将系统中的业务逻辑剥离出来,形成单独的一部分,就这样衍生出了三层体系结构。
    三层体系结构的划分如图2.1所示。


    表现层:可以理解为jsp、html等页面。
    业务逻辑层:主要任务是对程序中与业务流程进行处理,为前端页面提供相应的业务服务。
    数据持久层:主要是对数据进行存储。

    运用这样的三层结构,可以实现让每一层在功能实现上有一个清晰的界限,这样三层之间是相互分离的,但它们之间存在彼此进行通信和交互的接口。
    软件开发采用这种三层体系结构,可以让软件系统在可扩展性、可复用性、和安全性上得到很到的提升,还也有助于系统易管理性的提高。
    2.2 B/S架构介绍B/S架构是一种对C/S架构进行改进后的架构,它不需要安装任何专门的软件,对操作系统也没有任何限制,由于只需要客户端上具备浏览器就行,这种架构实现了客户端的统一和平台无关性。在这种模式下,系统功能的核心实现集中在了服务器上,这样可以简化系统的开发、维护和使用。
    使用架构,可以使得软件维护和升级方式变得简单,由于浏览器就是它的客户端,所以根本不需要做任何的维护,只需要对服务器进行管理即可。用户量的增多也不会对它的升级维持产生影响。甚至还可以实现远程维护、升级和共享。管理人员只需要管理服务器就行了,软件的维护成本较低。
    也正是因为B/S架构的特性,所以客户机越来越“廋”,但服务器却因此变得越来越“胖”,在这种架构之下,服务器运行数据的负荷较重,为避免发生服务器“崩溃”,造成无法弥补的损失。使用这种架构往往需要配备数据库存储服务器来以防万一。
    2.3 j2EE简介和JDK的安装配置Java编程主要有三个方向:Java SE(Java Platform , Standard Edition),它是Java中的基础部分,也称J2SE,主要用于桌面开发,同时它也是Java EE(Java Platform , Enterprise Edition)的基础。Java EE也称为J2EE主要应用于Web开发。最后一个是Java ME(Java Platform , Micro Edition)也称J2ME,主要应用于移动端的开发。
    J2EE这种技术架构,它有很多组件,主要用来简化和规范系统的开发和部署,从而可以提高代码的移植性、安全性和复用性。因为它是Java技术中的一部分,所以Java语言所具备的特点它也都具备,面向对象、高性能、跨平台(操作系统)的。而且“一次编译,到处运行”。
    俗话说:“工欲善其事,必先利其器”,如果想要通过使用J2EE架构进行系统开发,首先需要进行JDK(Java Development Kit)的安装及其配置。具体的安装配置如下:
    下载JDK的安装包:虽然JDK是sun公司开发的产品,但由于sun公司已经被Oracle公司收购,在下载安装包的时候需要到Oracle官网上去根据自己的操作系统进行下载。具体的下载过程就不再赘述,接下来说一下JDK的环境变量的配置方式。
    我的电脑->系统属性->高级系统设置->环境变量->新建



    变量名
    变量值




    JAVA_HOME
    D:\DEV\jdk1.8(具体配置视安装目录而定)


    CLASSPATH
    .;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar; (固定写法)



    最后在path路径中添加变量值:%JAVA_HOME%\bin和%JAVA_HOME%\jre\bin即可。
    测试:运用组合键“win+r”输入CMD命令进入控制台,然后在控制台输入java–version、java、javac等命令进行测试。
    得到图2.2、图2.3和图2.4所示则表示JDK安装配置成功。



    2.4 Jsp简介Jsp(Java Server Page)是一种动态网页的综合性技术。它的产生是由于程序员在开发的过程中发现,Servlet用来做界面非常的不方便。于是就想到了用一种新的技术来做界面,就这样Jsp技术就出现了。Jsp技术可以用下面这个公式来进行表示:公式:Jsp=html+java片段+Jstl+Javascript+css。其实Jsp的本质还是Servlet,Jsp和html最大的区别仅是多了一些Java片段而已,所以写Jsp就和写html差不多。
    相比html来说:html只能为用户提供静态数据,而jsp技术允许在页面中嵌套Java代码,能够为用户提供动态数据。
    相比Servlet来说:Servlet要对数据进行排版难度较大, Jsp不止是可以通过Java产生动态数据,它还综合了JavaScript和css技术,所以能够很轻易的对数据进行排版。
    Jsp的运行原理如图2.5所示:

    说明:Jsp页面报错实际上是报的Servlet即xxx_jsp.java中的错误,在查找错误的时候应当找到与该Jsp对应的Servlet中去查找相应的错误。
    2.5 MVC简介MVC是一种设计模式,所谓设计模式就是开发人员在长期的开发过程中提炼和总结出来的一套优秀的应对一般问题的解决方案。
    MVC当中的M代表model,可以理解为JavaBean,V即视图,是系统与用户进行交互的窗口,可以理解为Jsp页面或其他的前端页面。其中C是Controller控制器,主要用于处理业务逻辑。采用MVC这样的三层模式,可以有效的将视图、业务逻辑和数据持久层分离开来,它们之间相互分离,仅通过业务逻辑来进行调用。
    例如这样一个简单的需求,前端Jsp页面的用户通过输入用户名和密码发起一个登陆请求,请求将传达到控制器,控制器通过对数据库的调用来进行数据比对,如果在数据库中不存在那么一个用户,那么控制器将返回一个失败信息给前端页面。
    这种设计模式极大程度上的增加了程序的健壮性,可维护性和易于扩展。
    J2EE中几种重要的开发模式:
    Model1模式: Jsp文件是Model1模式的基础,它是由一些独立的Jsp文件,和一些Java Class组成的(不是必须的),这些Jsp文件通过从Http 请求中获得所需要的数据,并进行业务逻辑的处理,处理的结果通过服务器响应返回前端界面。
    Modell模式有开发简单、开发速度快,适合开发小项目等优点。缺点是,表现层和业务逻辑层混在一起,首先这样显得很乱,代码的可读性差,后期维护工作难以进行,同时也不利于多个人进行协同开发。Model1模式原理如图2.6所示:

    分层模式即Model1.x模式(MV模式,分层),是在Model1模式的基础上演变而来,将业务逻辑和表现层分离开来,但是业务逻辑和数据模型模型依旧放在一起也不利于系统的维护和扩展,其原理如图2.7所示:

    MVC模式要求在开发的过程当中,把数据的输入、输出和显示分进行分离。
    MVC模式的原理,如图2.8所示:

    2.6 Spring简介首先说一下Spring是什么?
    Spring是一个为了解决软件开发复杂性而被开发出来的,优秀而且开源的容器框架,来看一下如图2.9所示的Spring层次原理图。

    从图中可以看出Spring是一个横跨三层(Web层、业务层、数据持久层)的框架,之所以把它称之为容器框架,是因为它的核心主要是用于配置bean,并且维护bean与bean之间的关系。这里说到的bean是Spring框架中一个非常重要的概念,它可以是Java中的任何一种对象,Spring的核心配置文件包含并管理了应用中对象的配置和生命周期,从这个层面上来说,可以将其看成是一种容器。
    Spring中提出了控制反转技术(IoC),和面向切面编程(AOP)的思想等促进了应用软件模块之间的松耦合度。能够有效的提升软件的稳定性和可测试性。
    Spring命名的由来,起初在Rod Johnson的Expert One-on-One Java EE Design and Development(专家一对一J2EE设计与开发)一书中Spring被称为“Interface21”(当时它使用com.interface21这个包名)。对于使用过EJB来开发过J2EE的人,一定知道这门技术在开始学习和应用的时候非常的艰苦。Spring出现后采用简单的JavaBean代替了EJB,并且还提供了更多的企业应用功能。Spring代表了传统J2EE“冬天”之后的一个新的开始,而且为了激励开发者社区,然后就有人提出了用“春天”这样一个简洁优雅的词来命名。
    Spring框架由七个明确定义的模块组成,详见图2.10。

    Spring框架的组成模块之间是相互独立的,在实际开发的过程中,可以根据自己的需求选用其一部分子框架即可,然后应用其他的框架方案加以代替。例如:它所提供的ORM(对象关系模型)框架,就可以采用Mybatis框架来进行代替。如果采用了其他的框架来进行替代的话,就涉及到框架的整合。在它的核心配置文件中,应当合理的进行配置,如图2.11所示:

    框架多是通过Java的反射机制来实现的,至于其内部的实现细节在此不做叙述,在此只对其运行原理进行说明,Spring的运行原理如下图2.12所示:

    Spring开发提倡接口编程,配合di(Dependency Injection)技术可以更好的达到程序间的解耦,使用框架的时候需要到其官网去下载jar包,然后导入到工程目录中去才能进行使用,对于项目工程来说,可以理解为第三方库。
    2.7 SpringMVC简介SpringMVC是Spring框架的子模块,它是类似于Struts2的一个MVC框架,在实际的开发中,接收浏览器的请求响应,对数据进行处理,然后返回页面进行显示。上手难度较Struts2简单得多,而且由于Struts2所暴露出来的安全问题,SpringMVC已经成为了大多数企业优先选择的框架。
    运用SpringMVC框架进行开发,程序员只需要把精力放在处理器Handler的编写及视图比如JSP页面的编写上即可。
    学习框架实际上学习的就是其配置文件,使用框架有很多好处,但是相应的也会存在一定的限制,开发的过程中应当遵从其相应的规范。
    在运用SpringMVC进行开发的时候,需要在它的核心配置文件中,进行相应的配置。其中主要的是对映射器、处理器、视图解析器、视图过滤器还有Handler进行配置如图2.13和图2.14所示:

    SpringMVC的试图过滤器需要到Java Web工程的Web.xml文件中去进行配置。

    编写SpringMVC中的映射器、处理器有两种方式,一种是注解的方式,还有一种是非注解的方式,在运用注解方式的时候应当注意:

    注解方式的映射器
    spring3.1之前使用org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping注解映射器。spring3.1之后使用 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping注解映射器。

    注解方式的处理器
    spring3.1之前使用org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter处理器适配器。spring3.1之后使用 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter处理器适配器。
    SpringMVC的映射器和处理器在使用的时候需要配对使用,版本需要一致,不然在项目运行的时候会报错。SpringMVC还提供了<mvc:annotation-driven/>标签来配置映射器和处理器,Handler的配置还可以通过包扫描的方式进行配置:
    <context:component-scan base-package="包含controller的包名 ">。
    2.8 Mybatis简介Mybatis可以理解为一种不完全的ORM(对象关系模型)框架,它是Apache下的顶级项目,对于Mybatis来说对JDBC的封装程度没有Hibernate深,所以它对开发人员有着更高的要求,开发者需要自己编写SQL语句。
    Mybatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。它可以使用简单的XML或注解来配置和映射原生类型、接口和Java的POJO为数据库中的记录。
    Mybatis是数据持久层框架,Mybatis对于大型数据库的优化和适配性较强。适用于需求变化周期快,数据处理量较大的系统。Hibernate采用了更加自然的面向对象的视角来持久化Java应用中的数据,让开发者不再关注底层数据库的细节。Mybatis在这方面没有特定的文档说明,这样便于开发者发挥自身想法来对对象进行管理。
    Mybatis运行原理如图2.15所示:

    Mybatis的核心是mapper.xml文件,简单的说就是配置SQL语句。
    2.9 Maven的简介Maven是一种优秀的项目管理工具,它为构建项目提供了一套完整的生命周期框架。
    由于Maven使用了标准的目录结构和生命周期。所以在多个开发团队进行开发的情况下,它可以按照设置标准在很短的时间内,完成相关的配置工作。由于大部分项目的设置都很简单,并且可以重复使用,Maven让开发者的工作更轻松。同时它可以自动的完成:创建报表、检查、构建和测试等工作。
    系统在运用框架开发的过程中,需要到各个框架的官网上去下载jar包,运用Maven的话,只需要在其核心文件中添加依赖即可。可以方便的进行管理、节约时间、提高工作效率。
    总的来说,Maven可以帮助开发人员简化和标准化项目的建设。它主要有负责处理编译,分配文档和管理团队协作等功能。Maven可以增加项目中模块的可重用性。
    Maven项目的结构和内容在pom.xml文件中声明,pom.xml文件这个文件是整个Maven系统的基本单元。
    管理工具Maven的安装步骤如下:

    进行下载安装:到Maven的官网去下载和系统对应的文件。由于是下载好的文件是压缩包格式,只需要将它进行解压就能完成安装了。
    配置环境变量:此电脑系统属性高级系统设置环境变量
    点击新增变量,变量名为:MAVEN_HOME,变量值就是maven解压位置的路径。
    接着点击编辑Path变量,在有变量值的那一栏中追加 %MAVEN_HOME%\bin即可。
    测试:win+R->cmd->调出控制台->输入命令(mvn –version)。得到图2.16所示的结果,那么环境变量就配置完成。


    对Maven的settings.xml文件进行配置,可以实现其默认仓库的转移:
    根据Maven的安装路径找到它的配置文件(settings.xml),用记事本打开之后找到“<localRepository>”这对标签,并在这对标签中间填写上指定的路径,如图2.17所示:

    接着配置Maven,由于到它本身的中央仓库去下载jar包会非常慢,这里将其更改为阿里云的,在settings文件中找到“<mirros>”这对标。
    然后在标签当中增加阿里云maven镜像,如图2.18所示:

    至此,Maven的安装配置完成。
    2.10 本章小结本章节通过对办公自动化系统中所到使用到的相关技术进行了简要的叙述,对使用的框架的原理及配置文件进行了简要的说明,通过对理论知识学习和相关技术的研究,为办公自动化系统的开发提供了一定的理论基础,让办公自动化系统的实现成为可能,同时还做了一些必要的准备,安装JDK和Maven,并且对环境变量进行了配置。
    第三章 系统分析3.1 系统的可行性分析在准备开发一个项目的时候,对其进行可行性分析是必不可少的一个重要环节,通过对其进行可行性分析,可以为我们的决策(是否对该软件进行开发)提供科学的依据。可行性分析可以帮助我们在开发项目的过程中以最小的代价,并且尽可能的在短时间内来确定相应的问题是否能够得到解决。通过确定系统的相关约束和限制,以此来分析准备开发的系统需求和规模等是否能够具体实现,实现后能否为企业带来经济效益的最大化。总而言之,只有通过认真的对系统进行可行性分析,才能有效的避免在开发过程中的阻抗问题。
    3.1.1 技术可行性字面上的意思就是通过现有掌握的技术是否能够实现这个项目?
    本课题的技术基础是Java,结合掌握了现有的一些优秀的框架Spring(容器框架)、SpringMVC(MVC框架)、Mybatis(数据持久层框架)技术的基础上,结合项目管理工具(Maven)来对办公自动化系统进行开发。通过将这些框架进行整合来实现具体的开发,可以增加系统的稳定性、可维护性以及可扩展性以此来达到快速稳健的构建项目的目的。
    数据的存储采用了开源的MySQL数据库,虽然它是开源的,但是在安全性做得较好,数据处理的能力极强,并且它是一个最轻量级的数据库,系统要求没那么高。
    开发工具采用具有错误代码提示功能的Eclipse(一个免费的集成开发工具),它所提供的错误代码提示功能对于初学者来说是比较重要的(不怕出错,就怕不知道哪儿出错)。
    在长期的学习过程中,对于相关技术的掌握让我对办公自动化系统的开发成为可能。
    3.1.2 经济可行性从企业的出发点来看,就是某个软件开发出来之后所带来的经济效益是否
    会大于其开发成本。如果该软件所带来的经济效益能够远远大于其开发成本,那么决策者将决定对其进行资金注入。
    就个人的毕业设计而言,所采用的相关技术以及开发工具等都是免费的。所以并不存在因为经济压力而开发不下去的情况。
    3.1.3 操作可行性该办公自动化系统应用Tomcat容器进行部署,前端页面运用了JSP技术。在项目进行发布的时候,只需要通过浏览器输入对应的URL就可以实现访问和操作。该系统具有交互界面简单,操作容易,方便管理等特点。
    综上所述,本系统可以进行实际开发。
    3.2需求分析3.2.1 功能性需求分析本系统从功能的实现上主要分为日常办公、综合信息、系统管理三个模块,初始情况下只为系统设置一个用户admin,这个用户是系统的超级管理员,拥有操作这个系统的最高权限(系统管理、个人信息管理、审批管理、公告管理等)。
    3.2.2 日常办公工作流是办公自动化系统的核心所在,在最初的办公模式下,工作流的流动是基于纸张表单、人工传递的方式进行逐级流动的,最后再又管理者进行审批签字。对资源来说是一种浪费,最主要的还是工作效率不高,企业中应用办公系统,员工只用通过电脑来填写好相关的表单即可,表单会按照系统定义好的流程,自行的流动向下指定的审批者,审批者在接收到表单后,可以根据需要对其进行相应的更改,大大的提高了员工的工作效率,同时也提升了公司的核心竞争力。在这一模块中审批管理是其核心。系统需要将消息分发给对应的审批人员,最后根据审批人员的抉择来确定最后的结果。公告的发布修改删除只能是管理员才具备的权限。每次公告的更新数据都会同步到数据库中去,在其他用户登录系统的时候可以及时的接收到公告通知。
    3.2.3 综合信息企业的管理离不开对数据的处理,例如:一个新员工在正式进入公司的时候,要对其信息进行录入、备份,在其职位、薪水等发生变化的时候要对相应的信息进行更改。在员工离职的时候需要将其相应的信息进行删除。企业中信息的交流处理(业务流程)是办公系统中重要的组成部分。采用传统人工的方式进行处理,工作效率低下。大量累积的信息需要进行筛选、分类、归档。如果仅靠人工进行操作,工作量大、不易管理、在对信息进行查找的时候工作难度大,对于企业来说是一笔不小的资金投入,因此需通过系统来实现其管理的便捷性,以此提高工作效率、降低运作成本。
    3.2.4 系统管理系统管理是针对具有系统管理权限的人员对系统进行维护而开发的基础模块。
    系统管理的主要模块主要包含(用户管理、岗位管理、权限管理、部门管理)等模块。

    用户管理模块要具体实现的共能为:添加、删除、更新、用户列表等。
    系统的岗位管理模块的主要功能为:添加、删除、更新、岗位列表等。
    部门管理主要实现的主要功能为:添加、删除、更新、部门列表等。

    3.2.5 非功能性需求分析对系统的非功能性需求进行分析是必要的,虽然它与系统的实现无关。但也是要重点考虑到的一个环节,它具体包括了系统的安全性、可靠性、健壮性、可扩展性等。这些属性是衡量系统好坏的关键。
    系统开发完成之后应该具有操作简单、良好的交互性(用于提升用户使用的满意度、和良好的使用体验)、界面具有友好性(针对用户的误操作给出相应的提示信息)等特性。系统应该充分的考虑用户的操作习惯,针对这些需求对系统各项功能进行严格的测试,并进行逐步完善,以此来增加系统本身的实用性。
    3.3 本章小结本章节通过对系统的可行性进行了简要的分析,为是否开发该系统提供了科学的理论依据。通过对系统功能性需求分析的阐述,说明了系统主要的几个模块以及它们应该包含和实现哪些具体的功能。最后通过系统非功能性的需求分析,对系统还需实现哪些与系统主体功能无关的细节进行了补充和说明。
    第四章 系统设计4.1 系统的架构设计这个办公自动化系统,采用的是B/S架构、运用了MVC模式进行开发,主体包括了数据持久层,核心业务层以及应用层(即前端视图界面—-用户实现操作的交互界面),运用框架技术结合Maven管理实现对系统的构建,系统整体架构如图4.1所示:

    在此系统架构下,项目工程的文件结构如图4.2所示:

    4.2 系统模块设计系统模块的合理划分是系统成功的关键,因为OA系统模块众多,如果事先不做好模块划分,开发过程中的时间成本的投入将会提高,还可能造成做无用功的现象。对系统的各个模块进行合理的划分,能够促进系统功能的具体实现。
    系统模块大致分为系统管理、个人信息管理、审批管理、公告管理等(模块)。如图4.3所示:

    系统管理模块又分员工管理、部分管理和岗位管理,如图4-4所示:

    系统的个人信息管理模块,主要功能是对用户进行CRUD操作,如图4.5所示:

    系统的审批模块,主要由请假申请、申请查询和待审批申请组成,如图4.6所示:

    系统的公告管理模块,主要实现:管理员对公告的增、删、改操作,用户对公告可以进行的操作是查询,如图4.7所示:

    4.3 数据库设计数据库是系统的核心组成之一,主要用于对各种信息进行增删改查操作。数据库设计得是否合理,对整个系统有着深远的影响。
    4.3.1概念结构设计为了方便描述系统各模块间的联系,应设计与之对应的实体,实体中需要包含相关的实体属性。这里通过“请假”这个具体的事务流程为主线来贯穿系统中所涉及的实体。企业内部某个部门的员工(仅仅具有普通权限的系统用户)在要请假的时候,应该向具有相应权限的管理人员(具有系统管理权限的用户)发起“请假”申请,然后由管理人员对“请假”进行审批,最终将审批的结果返回给申请人。
    这个业务中所包含的实体有:用户、部门、请假、审批、权限和角色。
    (1)用户实体用于存储用户的相应信息,用户实体如图4.8所示:

    (2)部门实体包含了部门名称和描述等属性,部门实体,如图4.9所示:

    (3)系统的请假实体主要包含的信息,如图4.10所示:

    (4)审批实体主要包含了一下信息,详见图4.11:

    (5)权限这个实体存储了与权限相关的信息,如图4.12所示:

    (6)用户角色实体主要用于存储用户在企业中担任的角色,角色实体如图4.13所示:

    4.3.2 系统E-R图E-R图是描述实体之间的关系图,本系统的E-R图,如图4.14所示:

    4.3.3 表结构的详细设计表结构设计的好坏,对系统的运行性能起着决定性的因素。不合理的设计还可能导致在开发的过程中遇到很多难以解决的问题,甚至导致开发难以进行下去。所以在设计表的时候应该遵循一定的关系模式。设计的时候至少需要满足第一范式的要求(即保证表中的每个属性的值域都是不可再分的数据项),目的是为了保证数据的完整性和可靠性避免数据冗余。
    (1)用户表用保存用户的相关信息,表结构如表4.1所示:
    表4.1系统用户表(t_wl_user)



    字段名
    类型
    长度
    是否为空
    是否主键
    说明




    user_id
    int
    20


    用户ID


    depart_id
    int
    20


    部门ID


    role_id
    int
    20


    角色ID


    login_id
    varchar
    40


    登陆ID


    userName
    varchar
    25


    用户名


    userPassword
    varchar
    30


    用户密码


    sex
    char
    2


    用户性别


    phoneNumber
    varchar
    20


    手机号码


    email
    varchar
    30


    电子邮件


    description
    varchar
    90


    描述


    createTime
    dateTime



    注册时间



    (2)部门表用于存储部门的相关信息,表结构如表4.2所示:
    表4.2系统部门表(t_wl_department)



    字段名
    类型
    长度
    是否为空
    是否主键
    说明




    depart_id
    int
    20


    部门ID


    name
    varchar
    25


    部门名称


    description
    varchar
    90


    描述


    parent_id
    int
    20


    上级ID



    (3)角色表用于存储角色的相关信息,表结构如表4.3所示:
    表4.3系统角色表(t_wl_role)



    字段名
    类型
    长度
    是否为空
    是否主键
    说明




    role_id
    int
    20


    角色ID


    name
    varchar
    25


    角色名称


    description
    varchar
    90


    描述



    (4)权限表用于保存权限信息,表结构如表4.4所示:
    表4.4系统权限表(t_wl_power)



    字段名
    类型
    长度
    是否为空
    是否主键
    说明




    id
    int
    20


    ID


    name
    varchar
    25


    名称


    url
    varchar
    130


    url


    parent_id
    int
    20


    上级权限ID



    (5)公告表用于保存公告信息,表结构如表4.5所示:
    表4.5系统公告表(t_wl_notice)



    字段名
    类型
    长度
    是否为空
    是否主键
    说明




    notice_id
    int
    20


    公告ID


    user_id
    int
    20


    创建人


    content
    longtext



    公告内容


    createTime
    dateTime



    创建时间



    (6)请假表用于保存请假信息,表结构如表4.6所示:
    表4.6系统请假表(t_wl_leave)



    字段名
    类型
    长度
    是否为空
    是否主键
    说明




    leave_id
    int
    20


    请假ID


    user_id
    int
    20


    请假人


    approver_id
    int
    20


    审批人


    startTime
    dateTime



    开始时间


    endTime
    dateTime



    结束时间


    days
    int
    2


    请假天数


    reason
    varchar
    90


    请假原因


    result
    varchar
    10


    请假结果


    createTime
    dateTime



    创建时间


    status
    varchar
    10


    请假状态


    type
    varchar
    10


    请假类型



    (7)审批表用于保存请假的审批信息,表结构如表4.7所示:
    表4.7系统审批表(t_wl_leaveApprover)



    字段名
    类型
    长度
    是否为空
    是否主键
    说明




    approver_id
    int
    20


    ID


    leave_id
    int
    20


    请假ID


    user_id
    int
    20


    审批人


    username
    varchar
    25


    请假人


    status
    varchar
    10


    审批状态


    advice
    varchar
    10


    审批意见


    createTime
    dateTime



    创建时间



    (8)消息表用于保存待审批消息,表结构如表4.8所示:
    表4.8系统消息表(t_wl_message)



    字段名
    类型
    长度
    是否为空
    是否主键
    说明




    message_id
    int
    20


    消息ID


    user_id
    int
    20


    消息归属


    leave_id
    int
    20


    消息来源


    title
    varchar
    45


    消息标题


    watch
    char
    2


    是否查看



    至此,系统中各个表的表结构设计完成。
    4.4 核心业务流程设计工作流的设计是办公自动化系统的核心。针对本系统,通过“请假”这一业务来进行工作流设计,具体流程为见图4.15所示:

    4.5 本章小结通过“请假”这一业务来贯穿整个章节,对系统的架构设计、模块设计、数据库设计和核心业务流程设计进行了详细的说明,并且给出了进行过合理设计实体图、E-R图和表结构。
    第五章 系统实现完成以上过程后,现在进行办公自动化系统的开发。选择合适的开发工具和框架实现开发。严格按照框架约束和MVC模式的要求,遵循Java的命名规范,对代码进行优化、抽取和封装,最终完成一个高性能的办公自动化系统。
    5.1 开发环境搭建在安装配置了JDK和Maven的基础上,现在进行Eclipse(集成开发工具)的安装。注意事项:Eclipse的安装需要选用与之版本对应的JDK版本才能成功运行。

    在官方网站下载安装好Eclipse2018-12后运行(因为下载好的文件是压缩包格式,安装过程只需要将其解压到指定目录即可)。
    Eclipse在第一次运行的时候需要先选择它的工作空间(工作空间自行指定),如图5.1所示:



    新建一个Maven工程,如图5.2所示:

    到选择工作空间的时候把“Use default Workspace location”勾选好即可,在选择类型的时候需要选择webapp,如图5.3所示:

    选择完毕后点击下一步,最后为工程命名(myDesign),如图5.4所示:

    在命明完成后点击完成即可。

    Maven工程新建完毕之后对其配置文件(pom.xml)文件进行配置部署,添加开发所需的依赖。如图5.5和5.6所示:



    进行框架整合,先按照SSM框架的整合规范建立相应的文件结构,然后对系统中所用到框架的配置文件进行相应的配置,目的是完成框架的整合。Mybatis的配置如图5.7所示:

    Spring配置文件的部分配置如图5.8所示:

    5.2 通用功能实现由于CRUD操作是各模块数据通用的,故而将其抽取为公共代码部分,采用面向接口编程的方式,具体模块的实例需要运用到的时候对其进行实现即可。主要代码如下:
    public interface UserService { public User findUser(String username, String userpwd) ; public List<User> selectAll(); public void add(User u); public void del(int id); public User getById(int id); public void update(User u); public List<User> getByLike(String keywords);}
    5.3 登陆模块实现登陆界面采用html+js+css来具体实现,效果如图5.9所示:

    下面附录的是实现登陆主要代码:
    // 申明这是一个控制器@Controllerpublic class Usercontroller { // 注入Service @Autowired UserService service; //url映射 @RequestMapping("/login") public String login(){ return "login"; } //登陆验证 @RequestMapping("/main") public String checkLogin(@RequestParam(value = "username") String username, @RequestParam(value = "userpwd") String userpwd){ User user = service.findUser(username, userpwd); if(user!=null) { return "main"; }else { return "err"; } }
    5.4 系统模块的实现系统的主要模块有:角色管理、部门管理、用户管理三大模块,基础部分均为数据的CRUD操作,其中还包含用户列表的分页功能。页面效果如图5.10所示:

    部分核心代码如下:
    //后台核心代码@RequestMapping(value = "/user")public ModelAndView selectAll() {ModelAndView mav = new ModelAndView(); mav.addObject("userList", service.selectAll()); return mav;}
    //前端界面的关键代码:<tr> <th class="table-check"><input type="checkbox" id="chkAll"></th> <th class="table-id">ID</th><th class="table-title">姓名</th> <th class="table-type">电话</th> <th class="table-author am-hide-sm-only">性别</th> <th class="table-set">操作</th></tr></thead><tbody><c:forEach items="${userList}" var="user"> <tr> <td><input type="checkbox" name="chk"></td> <td>${user.id}</td><td>${user.name}</td> <td>${user.phonenumber}</td><td class="am-hide-sm-only"><span class="am-badge am-badge-primary">${user.sex}</span></td><td>
    5.5 本章小结本章节主要叙述了开发环境的搭建,说明了搭建开发环境时的注意事项。在环境搭环节就系统框架的整合进行了简要的说明,最后对系统的具体实现也作了简要的描述并附录了相应模块的核心代码。
    0 评论 3 下载 2019-10-08 17:11:18 下载需要20点积分
显示 0 到 15 ,共 15 条
eject