实现航班订票功能与WebService实现身份证验证
一、系统模块分析
这一部分主要针对提出的方案进行简要的分析。包括:订票系统的使用对象,以及这些对象分别可以实现的操作。这里不同的对象群体可以进行不同的操作,两者操作互不影响,但是信息可能有交叉。
在该系统中,将对象分为了普通用户和航班公司管理员两大类。
a) 普通用户
用户需要实现的操作有:
- 信息注册与登录 ,每一个用户都有唯一的信息身份与之匹配,保证订票时信息互不冲突,这里并不打算实现以游客登录的方式进入订票系统;
- 航班信息查询,主要包括用户查询对应的机票信息,查询方式有按时间查询,按出发到达城市查询等;
- 订票 ,查询到对应的航班后,用户可以确认订单,这里在订单进行过程中仅限用户进行一次订票,且仅有一次退票和改签的机会;
- 个人信息查询与修改,这里用户可以修改自己的信息,并且可以查询自己的订单信息。
b) 管理员
管理员需要实现的操作有:
- 信息登录 ,相对于用户,管理员只是很少一部分群体,而且管理员是系统指定的部分群体,实现对航班信息的维护和管理,管理员有专用的密码,有固定的管理员号码,没有注册和游客登录功能;
- 航班信息设置,管理员需要设置航班的航班号,开始和结束的城市,起飞时间,历经时长以及票数;
- 航班管理 ,用户提交订单、退票和改签时,需要管理员及时与用户反馈。
二、UML建模示例
1、航班管理系统UML类图表示
依据上述分析,该系统中分为用户和管理员两大类。其中用户类中描述的对象有:注册、登录、订票、查询、改签、退票、信息修改。用户类图表示如下图所示。
管理员类有:登录、航班管理、航班信息管理。管理员类图如下所示:
2、航班管理系统UML用例图表示
根据实际情形分析,该系统共分为三个层次。首先是系统的顶层管理。顶层管理将整个系统分为了管理员和用户两大类。顶层系统控制着整个航班管理系统的正常运行,其用例图如下:
顶层将系统分为了用户和管理员两大类。其中管理员需要执行操作时必须登录系统,否则默认为用户执行。管理员用例图如下:
用户在查询航班信息时可以以游客身份登录,但是在执行其他操作(如订票功能)必须登录信息,如果查询不到用户信息,必须注册,否则无法执行。用户的用例图如下:
3、航班管理系统UML时序图表示
UML时序图是通过描述对象之间发送消息的时间顺序显示多个对象之间的动态协作。它可以表示用例的行为顺序,当执行一个用例行为时,其中的每条消息对应一个类操作或状态机中引起转换的触发事件。
在本例中,主要分析了以下事件的时序图:
- 管理员添加航班信息 :
- 用户注册功能:
- 管理员或用户修改个人信息 :
- 用户退票功能 :
三、设计模式分析
设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。
在该系统中,软件设计模式得到了充足的运用。在这里,主要借助visual studio开发工具,利用winform平台实现了用户端的功能,即有用户的注册与登录功能实现,除此之外,实现了游客登录,但是游客仅可以查看有哪些航班可以飞行,并不能订票或查询航班号。用户查询到相关航班号后,可以订票(这里暂时没有实现退票或改签操作)。
1、简单工厂模式的运用
简单工厂模式,可以依据参数的不同返回不同类的实例,被创建的实例通常具有相同的父类。如果需要什么,只需向其中传入参数,而无需知其中的细节。
在该例中,初始有固定的注册名和密码,这里赋值为“20211015”,但是,用户并不能一直使用这一种字符串,否则会有很大的局限性。因此就有了用户注册功能。用户注册需要固定自己的UID和密码,这里并没有使用数据库实现,因此就无需实现UID重复等问题。用户通过注册获得自己的UID和密码后,就可以顺利登陆系统。而如果游客登录系统,那么将无法进入后续的订票功能。在这里,需要多次向不同form传入参数,有用户UID和密码。
2、观察者模式的运用
观察者模式,定义了对象之间一种一对多的依赖关系,让一个对象的改变能够影响其他对象。一个对象的状态或行为的变化将导致其他对象的状态或行为也发生改变,它们之间将产生联动。
在该例中,将用户作为一个观察目标,用户需要得到航班公司所固定的飞行城市,航班号等。除此之外,用户为了完整使用航班系统所提供的功能,航班管理系统需要限制用户:只有拥有完整UID和密码的用户才能订票。如果用户密码错误,将无法正常登陆,如果游客登录,就不能使用订票功能。
3、代码实现与解释
(注意:背景图选自网络)
- 用户登录界面 :
首先进入界面的即是用户登录界面:
public partial class UserLoginForm : Form
{
public UserLoginForm()
{
InitializeComponent();
}
//构造函数,这里主要用来获取从用户注册界面获取用户注册的UID和密码
string a;
string b;
public UserLoginForm(string str1, string str2)
{
InitializeComponent();
a = str1;
b = str2;
}
//这里是登录按钮的设置,默认UID和密码是20211015
public void loginBtn_Click(object sender, EventArgs e)
{
string UserUID_ = userUID.Text;
string UserPassword_ = userPassWord.Text;
if(UserUID_ == "")
{
MessageBox.Show("请输入UID!");
}
else if(UserUID_ != "" && UserPassword_ == "")
{
MessageBox.Show("请输入密码!");
}
if(UserUID_ != "")
{
if(UserUID_ != "20211015" && UserUID_ != a)
{
MessageBox.Show("该用户不存在!");
}
else
{
if((UserUID_ == "20211015" && UserPassword_ != "20211015") || (UserUID_ == a && UserPassword_ != b))
{
MessageBox.Show("密码错误!请重新输入!");
}
else
{
MessageBox.Show("登录成功!");
//登录成功后,用户可以进入主窗口订票
this.Hide();
MainWindow From3 = new MainWindow(UserUID_, UserPassword_);
From3.Show();
}
}
}
}
//这里接入注册界面
public void signBtn_Click(object sender, EventArgs e)
{
UserSignForm a = new UserSignForm();
a.ShowDialog();
}
//快速登录,传参不需要任何参数,这样后面界面接收不到用户的UID,用户无法实现后续功能
private void QuickBtn_Click(object sender, EventArgs e)
{
this.Hide();
MainWindow Form3 = new MainWindow();
Form3.Show();
}
}
- 用户注册界面 :
起始用户没有自己的UID,需要注册才能拥有自己的UID和密码:
public partial class UserSignForm : Form
{
public UserSignForm()
{
InitializeComponent();
}
//用户注册依次实现用户UID(限制8位数字)、密码和密码确认(限制8位数字)
//联系方式(限制11位数字,联系方式在后续功能实现中还有重要运用,主要用户确认用户订票、改签或退票)
//只有以上四种条件全部满足才能注册成功,逻辑相对容易
public void button1_Click(object sender, EventArgs e)
{
string UserSignUID_ = UserSignUID.Text;
string UserSignPassword_ = UserSignPassword.Text;
string UserSignPassword1_ = UserSignPassword1.Text;
string UserTel_ = UserTel.Text;
if(UserSignUID_ == "" || UserSignPassword_ == "" || UserSignPassword1_ == "" || UserTel_ == "")
{
MessageBox.Show("请输入完整信息!");
}
Regex reg1 = new Regex("^[0-9]*");
if (UserSignUID_ != "" && UserSignPassword_ != "" && UserSignPassword1_ != "" && UserTel_ != "")
{
if (reg1.IsMatch(UserSignUID_) && UserSignUID_.Length == 8)
{
if(reg1.IsMatch(UserSignPassword_) && UserSignPassword_.Length == 8)
{
if(UserSignPassword_ == UserSignPassword1_)
{
if(reg1.IsMatch(UserTel_) && UserTel_.Length == 11)
{
MessageBox.Show("注册成功!即将返回登录界面重新登录!");
this.Close();
UserLoginForm From01 = new UserLoginForm(UserSignUID_, UserSignPassword_);
From01.ShowDialog();
}
else
{
MessageBox.Show("用户输入联系方式有误!");
}
}
else
{
MessageBox.Show("两次密码输入不一致!请重新输入!");
}
}
else
{
MessageBox.Show("密码必须是8位数字!");
}
}
else if(UserSignUID_ != "")
{
MessageBox.Show("用户UID必须为8位数字!");
}
}
}
}
- 主界面 :
主界面主要实现用户城市选择和日期确定,只有符合实际规则才能进入订票界面:
public partial class MainWindow : Form
{
public MainWindow()
{
InitializeComponent();
}
//获取用户UID,从用户登录界面,主要为后续功能实现铺垫
string i;
string j;
public MainWindow(string str1, string str2)
{
InitializeComponent();
i = str1;
j = str2;
}
private void StartCity_SelectedIndexChanged(object sender, EventArgs e)
{
StartCity.DropDownStyle = ComboBoxStyle.DropDownList;
}
//获取用户年月日的选择
private void MainWindow_Load(object sender, EventArgs e)
{
YearOutput.Text = dateTimePicker1.Value.Year.ToString();
MonthOutput.Text = dateTimePicker1.Value.Month.ToString();
DayOutput.Text = dateTimePicker1.Value.Day.ToString();
}
//查询按钮的实现,首先同城之间不可购票
//其次,不可以预订今天或以前的票
//还有购票日期的限制,这里主要考虑到不同航空图的使用
private void button1_Click(object sender, EventArgs e)
{
string year = dateTimePicker1.Value.Year.ToString();
string month = dateTimePicker1.Value.Month.ToString();
string day = dateTimePicker1.Value.Day.ToString();
int y = int.Parse(year);
int y_ = int.Parse(YearOutput.Text);
int m = int.Parse(month);
int m_ = int.Parse(MonthOutput.Text);
int d = int.Parse(day);
int d_ = int.Parse(DayOutput.Text);
if(StartCity.Text == "" || EndCity.Text == "")
{
MessageBox.Show("请选择出发或到达的城市!");
}
else
{
if(StartCity.Text == EndCity.Text)
{
MessageBox.Show("同城之间不可购票!");
}
else
{
if(y != 2021 || (m < 10 && m > 12))
{
MessageBox.Show("仅能购买2021-10-01到2021-12-31之间的票!");
}
else if(m < m_ || (m == m_ && d < d_))
{
MessageBox.Show("不可以购买今天或者以前的票!");
}
else
{
MessageBox.Show("查询成功!即将跳转订票页面!");
this.Hide();
CertainWindow Form4 = new CertainWindow(i, j, StartCity.Text, EndCity.Text);
Form4.Show();
}
}
}
}
}
- 确定界面 :
确认界面主要实现确认用户进入订票系统,如果检测不到用户UID则进入失败:
public partial class CertainWindow : Form
{
public CertainWindow()
{
InitializeComponent();
}
//用户UID,从登录界面,只有这两个参数不为空才能进入购票界面
string c;
string d;
public CertainWindow(string str1, string str2, string str3, string str4)
{
InitializeComponent();
c = str1;
d = str2;
//城市名,从Mainwindow
textBox1.Text = str3;
textBox2.Text = str4;
}
//确认按钮,如果没有参数则返回登录界面登录
//否则进入进度条,进入购票界面
private void button1_Click(object sender, EventArgs e)
{
if(c == null || d == null)
{
MessageBox.Show("抱歉!你没有购票权限!");
this.Close();
UserLoginForm From1 = new UserLoginForm();
From1.Show();
}
else
{
button1.Enabled = false;
progressBar1.Minimum = 0;
progressBar1.Maximum = 1000;
progressBar1.Step = 1;
for(int i = 0; i < 1000; i++)
{
progressBar1.PerformStep();
}
this.Hide();
BookWindow Form5 = new BookWindow(textBox1.Text, textBox2.Text);
Form5.Show();
}
}
//取消界面,直接进入主界面用户可重新选择城市和购票日期
private void button2_Click(object sender, EventArgs e)
{
this.Hide();
MainWindow From31 = new MainWindow();
From31.Show();
}
}
- 购票界面 :
购票界面主要实现票种查询,然后选择合适的航班购票即可:
public partial class BookWindow : Form
{
public BookWindow()
{
InitializeComponent();
}
//城市,从CertainWindow
string m;
string n;
//确定一趟航班的基本信息
public class Flight
{
public string StartCity_;
public string EndCity_;
public string Number;
public string StartTime;
public string Time;
};
int x = 0; //符合的航班数目
//v是需要定义航班的趟数,这里没有定义,需要自定义
public Flight[] fl = new Flight[v];
string Start, End, Num, STime, Tim;
public BookWindow(string str1, string str2)
{
InitializeComponent();
m = str1;
n = str2;
for(int k = 0; k < v; k++)
{
fl[k] = new Flight();
}
/*
这里需要大量定义航班的信息,这里已经删除测试所需的航班信息数据
*/
for (int i = 0; i < 5; i++)
{
if (m == fl[i].StartCity_ && n == fl[i].EndCity_)
{
Start = fl[i].StartCity_;
End = fl[i].EndCity_;
Num = fl[i].Number;
STime = fl[i].StartTime;
Tim = fl[i].Time;
OutPut(Start, End, Num, STime, Tim);
x++;
continue;
}
}
//如果航班计数器为0,则表明没有合适的航班选择
if (x == 0)
{
OutputText.Text = "不存在符合要求的航班!";
}
}
//必须输入合适的航班号才能订票,否则不能订票
public void CertainBtn_Click(object sender, EventArgs e)
{
string[] CertainNum;
CertainNum = new string[x];
if (x == 0)
{
this.Hide();
MainWindow From32 = new MainWindow();
From32.Show();
}
else
{
for (int j = 0; j < v; j++)
{
int p = 0;
if (m == fl[j].StartCity_ && n == fl[j].EndCity_)
{
CertainNum[p] = fl[j].Number;
p++;
continue;
}
}
}
string str0 = InputText.Text;
int u = 0;
for(int v = 0; v < x; v++)
{
if(str0 == CertainNum[v])
{
u++;
MessageBox.Show("订票成功!");
break;
}
}
if(u == 0)
{
MessageBox.Show("请填写正确的航班号!");
}
else
{
this.Hide();
UserLoginForm From12 = new UserLoginForm();
From12.Show();
}
}
//取消按钮,返回主界面用户重新选择城市和日期
private void CancelBtn_Click(object sender, EventArgs e)
{
this.Hide();
MainWindow From31 = new MainWindow();
From31.Show();
}
//输出信息的函数定义
public void OutPut(string str1, string str2, string str3, string str4, string str5)
{
OutputText.Text += "出发地:" + str1 + "\r\n" + "到达地:" + str2 + "\r\n" + "航班号:" + str3 + "\r\n" + "起飞时间:" + str4 + "\r\n" + "历时:" + str5 + "\r\n";
}
}
四、Web Service实现身份证验证
1、SOAP简介
简单对象访问协议是交换数据的一种协议规范,是一种轻量的、简单的、基于XML(标准通用标记语言下的一个子集)的协议,它被设计成在WEB上交换结构化的和固化的信息。
SOAP采用了已经广泛使用的两个协议:HTTP 和XML(标准通用标记语言下的一个子集)。HTTP用于实现 SOAP 的RPC 风格的传输, 而XML 是它的编码模式。采用几行代码和一个XML 解析器, HTTP 服务器( MS 的 IIS 或 Apache) 立刻成为SOAP 的 ORBS。SOAP 通讯协议使用 HTTP 来发送XML 格式的信息。HTTP与RPC 的协议很相似,它简单、 配置广泛,并且对防火墙比其它协议更容易发挥作用。HTTP 请求一般由 Web 服务器软件(如 IIS 和Apache)来处理, 但越来越多的应用服务器产品正在支持HTTP。XML 作为一个更好的网络数据表达方式(NDR)。SOAP 把 XML 的使用代码化为请求和响应参数编码模式, 并用HTTP 作传输。具体地讲, 一个SOAP 方法可以简单地看作遵循SOAP编码规则的HTTP请求和响应, 一个 SOAP终端则可以看作一个基于HTTP 的URL, 它用来识别方法调用的目标。像CORBA/ IIOP一样, SOAP不需要具体的对象绑定到一个给定的终端, 而是由具体实现程序来决定怎样把对象终端标识符映像到服务器端的对象。
2、Java实现身份证验证功能
- 创建服务端 :
使用Eclipse,首先创建Web Service Project,命名为The Service,即创建一个服务端,服务端的功能是接受客户端用户所输入的身份证号码,判断所输入的身份证号码是否符合规则,符合则返回true,否则为false,并将用户输入的身份证号码展示(用户输入身份证号必须为18位,否则异常退出)。
实验默认为JDK1.8版本。使用时注意与Eclipse版本相符,如果不匹配需要手动调配。
package com.hyan.service;
//这里是创建对象的包所在的位置
import javax.jws.WebService;
import javax.xml.ws.Endpoint;
@WebService
public class ServiceHello {
private static String[] validtable = {"1","0","X","9","8","7","6","5","4","3","2"};
public static int adjustmentfactor(int digit)
{
int sum = 1;
for(int i = 0; i < digit; i++)
{
sum = sum << 1;
}
return sum;
}
public boolean helloworld(String name)
{
if(name.length() != 18)
{
return false;
}
else
{
return true;
}
}
public String getValue(String name) {
return name;
}
//判断身份证号码是否合法的函数
public boolean judge(String name)
{
boolean flag = false;
boolean f = helloworld(name);
if(f == false)
return false;
else
{
String validatecode = name.substring(17,18);
String selfcode = name.substring(0,17);
String code[] = new String[17];
for(int i = 0; i < 17; i++)
{
code[i] = selfcode.substring(i,i+1);
}
int sum = 0;
for(int i = 0; i < code.length; i++)
{
int yi = adjustmentfactor(i+1)%11;
int count = Integer.parseInt(code[code.length-i-1]);
sum += (count*yi);
}
String valdate = validtable[sum%11];
if(valdate.equalsIgnoreCase(validatecode))
{
flag = true;
}
return flag;
}
}
public static void main(String[] args) {
// 双引号中内容为URL地址,注意端口号必须唯一
Endpoint.publish("http://localhost:9009/Service/ServiceHello",new ServiceHello());
System.out.println("service success");
}
}
- 创建客户端 :
同样使用Eclipse,创建Web Service Project,命名为The Client,即创建一个客户端,客户端主要实现接受用户输入的身份证号码,然后判断是否合法,并显示输出结果。
package com.hyan.test;
import com.hyan.client.ServiceHello;
import com.hyan.client.ServiceHelloService;
import java.util.Scanner;
public class ServiceTest {
public static void main(String[] args) {
// 用户可以反复输入身份证号校验,如果长度不为18位则异常退出
while(true)
{
System.out.print("Please Input your IDcard Number : ");
Scanner sc = new Scanner(System.in);
String name = sc.nextLine();
ServiceHello hello = new ServiceHelloService().getServiceHelloPort();
String name1 = hello.getValue(name);
boolean name2 = hello.judge(name);
System.out.println("My IDcard Number:" + name1);
System.out.println("The result of Validation:" + name2);
}
}
}
- 命令窗口 :
格式:wsimport -s “src目录” -p “生成类所在包名” -keep “wsdl发布地址”
示例:
wsimport -s D:\ProgramCat\eclipse_EE_workspace\TheClient\src\main\java -p com.hyan.client -keep http://localhost:9009/Service/ServiceHello?wsdl
说明:
1)"src目录"地址不可含空格
2)“wsdl发布地址”不要漏了“?wsdl”
成功后显示如下:
- 最终测试 :
编译客户端代码,调用服务端程序,最终结果如下:
编译服务器端代码,最终显示service success,则表明编译成功。
在浏览器输入测试地址:http://localhost:9009/Service/ServiceHello?wsdl,最终显示结果如下:
地址栏中,service是固定的,serviceHello是项目名称,?wdsl是固定的页面。
看到该内容则表明XML发布成功!
五、实验总结
本次实验基于软件架构课程所学知识,完成了从系统架构及模块设计,UML建模画图以及相关功能的部分代码实现。实验中运用了多种设计模式展现了软件架构设计的多样性与严谨性。实验中也遇到一些困难例如设计用例图的逻辑展现以及代码实现web服务开发中的客户端对接口的调用。最终在老师指导与小组成员共同努力下解决了相关问题完成了实验。此次实验收获很多,加深了对软件设计相关概念理解与实践经验,相信对之后的软件设计学习会起到很好的指引作用。