第一部分:项目需求
1、项目名称:电影院售票系统
2、功能概述:主要功能包括在影片列表中选择某一个时段的一场电影、选择座位和一个种类的电影票,系统创建电影票,计算价格并输出电影票价格。
3、具体功能如下:
注:已参考实际情景,在原述要求基础上做了微调和优化;展示用图也出自完成后的效果;在实际做项目的过程中,业务场景也要自己做扩充和预设。
(1)放映列表的展示
效果如下:
(2)不同种类的电影票
影院提供3类电影票:普通票、学生票和电影券(学生票有折扣,电影券输入券号免费)
(3)场次可供选择
用户可以通过选择场次、电影票类型以及空闲座位进行购票。电影票如果已售出,系统将提示此票已售出。
效果如图:
(4)提供异常信息提示
如果用户没有正确选择票的信息将提示异常。如果改票已被售出系统将给出提示。
(5)输出电影票
购票成功,输出(电影名+票的类型+时间+座位号+价格),将电影票输出到指定的txt文件中。
(6)保存销售情况
系统可以对销售情况进行保存,并允许对其进行恢复。
第二部分:设计思路
(一)整体思路及细化实现步骤
- 第一,解析XML文档并存入集合中:Film类→解析XML→ArrayList<Film>;集合的大小,是所有电影的总计场次
- 第二,显示电影信息,显示座位信息
- 第三,提示用户输入(电影名称、场次、座位号、票的种类)同时进行判断;同时对出票信息进行存储
- 第四,显示购票信息
- 第五,将购票信息存入集合,并序列化输出文档
(二)实现难点预估
- 1、解析XML的信息存储到ArrayList<Film>中
- 2、已售座位的排除判断:同一场次的座位不能重复售出;而在不同场次,该座位可以售出。
(三)主要的类
- 1、TheaterSystem.java
这是主代码,功能包括:读取包含电影信息的XML文档,生成用户页面,按步骤提示用户进行购票操作,对输入信息进行反馈和处理,存储销售信息等 - 2、Film.java
这是电影类,每一个电影的每一个场次都是一个单独的对象,其属性包含该电影该场次的所有信息。值得一提的是,座位集合seatList()也作为Film对象的一个属性,以方便对每一独立场次的已售座位进行判断。 - 3、Ticket.java
这是电影票类,用于存储每一张售出票的信息,并实现Serializable接口,以序列化读写。
(四)人机交互的场景描述
- 第一步:提示用户输入电影名称
如用户输入的电影名不在该放映列表中,提示用户当下没有该映票。并给用户重新选择影片或不购票直接退出系统的机会。 - 第二步:提示用户选择电影场次
首先根据用户给到的影片名,给用户呈现场次信息,然后提示用户输入相应序号进行选择。 - 第三步:提示用户选择购票类型
影院提供给用户3种类型的票:1、普通票,无折扣;2、学生票,打八折;3、电影券:免费观看。用户输入券号,与系统中的券号信息库进行匹配,如连续三次输入无效券号,则让用处重新选择购票类型。 - 第四步:根据票的类型执行相应的折扣
折扣信息如上所述。 - 第五步:提示用户输入座位号
首先根据用户选择的影片名和场次,给用户显示该场次所余下的座位,其中已售出座位已标记为“#”。先进行格式和有效性判断,如用户座位信息格式输入错误,则提示输入重新输入;如无误,再进行座位是否已售出的判断,对于已售出座位,则提示座位已售出,同样给予用户重新输入的机会。
对于本次售出的座位,在该影片该场次的seatList()集合中给予处理。 - 第六步:计算本次购票的折扣后价格
根据信息表中的原价和折扣进行计算。 - 第七步:使用本轮购票操作所获取的信息,生成一个Ticket实例,放入ticketList<Ticket>()集合中,实现对用户购票信息的存储。
- 第八步:询问用户是否继续购票。
如继续,则再次执行如上第一到第七步。如退出,显示该客户本次购票所买的所有票的信息。 - 第九步:显示购票信息
第三部分:代码
(一)存放电影信息的XML文档
<?xml version="1.0" encoding="UTF-8"?>
<ShowList>
<Movie>
<Name>七武士</Name>
<Poster>A war</Poster>
<Director>黑泽明</Director>
<Actor>若名</Actor>
<Type>战争</Type>
<Price>60</Price>
<Schedule>
<Item>09:00</Item>
<Item>13:00</Item>
</Schedule>
</Movie>
<Movie>
<Name>老无所依</Name>
<Poster>old man</Poster>
<Director>托马斯</Director>
<Actor>阿汤哥</Actor>
<Type>动作</Type>
<Price>50</Price>
<Schedule>
<Item>11:00</Item>
<Item>15:00</Item>
</Schedule>
</Movie>
</ShowList>
(二)Film类
package cn.nj.Day0621.theatersystem;
import java.util.ArrayList;
public class Film {
private String name = null;
private String poster = null;
private String director = null;
private String actor = null;
private String type = null;
private double price = 0;
private String item = null;
private ArrayList<String> seatList = new ArrayList<String>();
private ArrayList<String> givenSeatList = new ArrayList<String>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPoster() {
return poster;
}
public void setPoster(String poster) {
this.poster = poster;
}
public String getDirector() {
return director;
}
public void setDirector(String director) {
this.director = director;
}
public String getActor() {
return actor;
}
public void setActor(String actor) {
this.actor = actor;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public String getItem() {
return item;
}
public void setItem(String item) {
this.item = item;
}
public Film() {
super();
// TODO Auto-generated constructor stub
}
@Override
public String toString() {
return "Film [name=" + name + ", poster=" + poster + ", director="
+ director + ", actor=" + actor + ", type=" + type + ", price="
+ price + ", item=" + item + ", seatList=" + seatList
+ ", givenSeatList=" + givenSeatList + "]";
}
public Film(String name, String poster, String director, String actor,
String type, double price, String item, ArrayList<String> seatList,
ArrayList<String> givenSeatList) {
super();
this.name = name;
this.poster = poster;
this.director = director;
this.actor = actor;
this.type = type;
this.price = price;
this.item = item;
this.seatList = seatList;
this.givenSeatList = givenSeatList;
}
public ArrayList<String> getSeatList() {
return seatList;
}
public void setSeatList(ArrayList<String> seatList) {
this.seatList = seatList;
}
public ArrayList<String> getGivenSeatList() {
return givenSeatList;
}
public void setGivenSeatList(ArrayList<String> givenSeatList) {
this.givenSeatList = givenSeatList;
}
}
(三)Ticket类
package cn.nj.Day0621.theatersystem;
import java.io.Serializable;
public class Ticket implements Serializable{
//giveName,type,givenItem,givenSeat,discountPrice,
private String name;
private String type;
private String item;
private String seat;
private double price;
public Ticket() {
super();
// TODO Auto-generated constructor stub
}
public Ticket(String name, String type, String item, String seat,
double price) {
super();
this.name = name;
this.type = type;
this.item = item;
this.seat = seat;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getItem() {
return item;
}
public void setItem(String item) {
this.item = item;
}
public String getSeat() {
return seat;
}
public void setSeat(String seat) {
this.seat = seat;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "Ticket [name=" + name + ", type=" + type + ", item=" + item
+ ", seat=" + seat + ", price=" + price + "]";
}
}
(四)TheaterSystem
package cn.nj.Day0621.theatersystem;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Scanner;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
/**
* 说明:本版是在TheaterSystem.java基础上,去除部分注释和log4j的调试语句,并未做大的改动。
* 主要用于操作界面和代码的简洁化展示。
*
* @author Administrator
*
*/
public class TheaterSystemImproved {
private static final Logger logger = Logger.getLogger(TheaterSystemImproved.class);
private static ArrayList<Film> infoList = new ArrayList<Film>();//存放电影信息,集合的大小为总计场次。4个场次,对应4个场次
private static ArrayList<String> nameList = new ArrayList<String>();//存放每部电影的名字,便于直接对用户输入信息进行判断
private static ArrayList<Ticket> ticketList = new ArrayList<Ticket>();
private static ArrayList<String> seatList0 = new ArrayList<String>();//初始化之用
private static String pathName = "E:/forJavatest/saleInfo.txt";
/**
* 运行
*/
public static void main(String[] args){
TheaterSystemImproved ts = new TheaterSystemImproved();
ts.getInfo();
ts.showInfo();
ts.select();
ts.createFile(pathName);
ts.readFile(pathName);
}
/**
* 第一步,解析XML并放入文档
*/
public void getInfo(){
String path = "src/cn/nj/Day0621/theatersystem/film.xml";
Document doc = null;
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder;
try {
builder = factory.newDocumentBuilder();
doc = builder.parse(path);
NodeList movies = doc.getElementsByTagName("Movie");
for(int i=0;i<movies.getLength();i++){
String name =null;
String poster =null;
String director = null;
String actor = null;
String type = null;
double price = 0;
String item = null;
NodeList movieInfo = movies.item(i).getChildNodes();//分别获取《七武士》和《老无所依》两个标签下的所有的Node节点
for(int j=0;j<movieInfo.getLength();j++){//遍历其中的每一个节点,找到每一个最小子元素的内容,如从<Type>战争</Type>找到战争
if(movieInfo.item(j).getNodeName()=="Name"){
name = movieInfo.item(j).getTextContent();
nameList.add(name);
}
if(movieInfo.item(j).getNodeName()=="Poster"){
poster = movieInfo.item(j).getTextContent();
}
if(movieInfo.item(j).getNodeName()=="Director"){
director = movieInfo.item(j).getTextContent();
}
if(movieInfo.item(j).getNodeName()=="Actor"){
actor = movieInfo.item(j).getTextContent();
}
if(movieInfo.item(j).getNodeName()=="Type"){
type = movieInfo.item(j).getTextContent();
}
if(movieInfo.item(j).getNodeName()=="Price"){
String priceTem = movieInfo.item(j).getTextContent();
price = Double.valueOf(priceTem);//使用包装类的valueOf()方法,接收字符串作为参数
}
if(movieInfo.item(j).getNodeName()=="Schedule"){
NodeList items = movieInfo.item(j).getChildNodes();//获取Schedule节点下包含的所有子节点
//获取每部电影的场次数
for(int k=0;k<items.getLength();k++){
if(items.item(k).getNodeName().equals("Item")){//含义:如果这个子元素的元素名是Item
item = items.item(k).getTextContent();
Film film = new Film(name,poster,director,actor,type,price,item,new ArrayList<String>(),new ArrayList<String>());
infoList.add(film);
}
}
}
}
}
} catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 第二步,显示界面
*/
public void showInfo(){
System.out.println("**********************欢迎进入电影票选购系统**********************");
System.out.println("序号\t电影名称\t英文名称\t导演\t演员\t电影类型\t价格\t时间");
for(int i=0;i<infoList.size();i++){
System.out.println((i+1)+"\t"+infoList.get(i).getName()+"\t"+infoList.get(i).getPoster()+"\t"
+infoList.get(i).getDirector()+"\t"+infoList.get(i).getActor()+"\t"+infoList.get(i).getType()
+"\t"+infoList.get(i).getPrice()+"\t"+infoList.get(i).getItem());
}
System.out.println("下面为影院的座位结构图:");
System.out.println("\t\t\t屏幕");
for(int i=0;i<5;i++){
for(int j=0;j<7;j++){
String seat = (i+1)+"-"+(j+1);
System.out.print(seat+"\t");
seatList0.add(seat);
}
System.out.println();
}
//### 将seatList的元素初始化到每一个Film对象中
for(Film film:infoList){
for(String seat:seatList0){
film.getSeatList().add(seat);
}
}
}
/**
* 第三步:提示用户输入信息,并存储票务信息
*/
public void select(){
Scanner sc = new Scanner(System.in);
//这里稍后定义一个大循环,每当完成一轮的信息获取,则把票务信信息放到集合中,并继续下一轮
//****操作第八步:询问是否继续购票****
int buyCount = 0;//该客客本次购票的张数。
while(true){
boolean isHere = false;//判断用户是否选择在该影院观影
String givenName = null;
String givenItem = null;
String ticketType = null;//购票类型
int count = 10;//折扣
String givenSeat = null;
Double price = 0.0;//原价
Double discountPrice = 0.0;//折扣后价格
int indexOfThis = -1;//获取用户输入的场次信息后,结合电影名得到该对象在集合中的索引。方便后续直接找到该对象并获取信息
ArrayList<String> freeTickets = new ArrayList<String>();//这是赠送票(免费电影券)的票号信息。
freeTickets.add("193004028");
freeTickets.add("193004139");
freeTickets.add("193004957");
freeTickets.add("193004192");
freeTickets.add("193004090");
freeTickets.add("193004991");
freeTickets.add("193006002");
//****操作第一步:输入电影名称****
while(true){
System.out.println("请输入电影名称:");
givenName = sc.next();
if(nameList.contains(givenName)){
isHere = true;
break;
}
System.out.println("抱歉,暂时没有该的电影。\n如继续购票请输入1,如退出系统请输入0。");
int choice = sc.nextInt();
if(choice==0){
System.out.println("本影院将不断引进更多优秀电影,欢迎您下次再来!");
break;
}
}
if(isHere){//用户选择在该影院观影
//****操作第二步:输入电影播放时间****
System.out.println("电影《"+givenName+"》的放映时间为:");
int itemOrder = 0;
ArrayList<String> itemOrders = new ArrayList<String>();
for(Film film:infoList){
if(film.getName().equals(givenName)){
price = film.getPrice();//这里顺带把原价提出来。
itemOrders.add(film.getItem());
System.out.print("场次"+(itemOrder+1)+": "+film.getItem()+"\t");
itemOrder++;
}
}
System.out.println("\n请选择观影场次(输入序号):");
int index = sc.nextInt()-1;
givenItem = itemOrders.get(index);
//****操作第三步:输入购票类型****
String[] ticketTypes = {"普通票","学生票","电影券"};
int Typechoice = -1;
boolean flag1 = false;//如果客户连续三次输入错误电影券票号,则要求从购票类型重新操作。
while(!flag1){//选票成功,flag1变为true;选票失败,继续选票
while(true){//这一循环是处理客户输入上述以外的票类型
System.out.println("请输入您要购买的票的类型(输入序号):\n1.普通票\t2.学生票\t3.电影券");
Typechoice = sc.nextInt();
//****操作第四步:根据票的类型执行相应的折扣****
if(Typechoice==1 || Typechoice==2 || Typechoice==3){
switch(Typechoice){
case 1:
flag1 = true;
ticketType = ticketTypes[0];
count = 10;//普通票,无折扣
break;
case 2:
flag1 = true;
ticketType = ticketTypes[1];
count = 8;//学生票,打8折
break;
case 3:
int total = 0;
while(total<3){
System.out.println("请输入9位电影券票号:");//有三次输入的机会,若均输错则跳会购票类型选择处
String ticketNumber = sc.next();
if(freeTickets.contains(ticketNumber)){
flag1 = true;
ticketType = ticketTypes[2];
count = 0;
freeTickets.remove(ticketNumber);
break;
} else{
total++;
}
}
if(total>=3){
System.out.println("您已连续三次输入错误,请重新选择购票类型。");
break;
}
break;
}
break;
}else{
System.out.println("您输入的序号有误,请重新输入");
}
}
}
//****操作第五步:输入座位号****
//思路:由该场次找到该对象,然后该对象的座位集合进行判断和更新;先判断如尚未售出则售出,然后在该场次内剔除该座位
System.out.println("该场次现有可供选择的座位如下(标“#”座位已售出):");
System.out.println("\t\t\t屏幕");
for(int i=0;i<infoList.size();i++){
if(infoList.get(i).getItem().equals(givenItem)){
indexOfThis = i;//### 获取到了该对象在infoList中对应的下标
//先显示该场次的座位
ArrayList<String> seatTemp = infoList.get(indexOfThis).getSeatList();
for(int j=1;j<=5;j++){
for(int k=(7*(j-1)+1);k<=7*j;k++){
System.out.print(seatTemp.get(k-1)+"\t");
}
System.out.println("");
}
//再进行选座位和查重;注意也要对输入座位格式错误进行处理
while(true){
System.out.println("请输入您需要的座位号:");
givenSeat = sc.next();
if(seatList0.contains(givenSeat)){//注意:先判断是否是seatList0中的元素,再继续往下判断。如先是否为givenSeatList再seatList,如果输入11,那么也是不包含在getGivenSeatList()中的,但是也不在seatList中
if(!infoList.get(indexOfThis).getGivenSeatList().contains(givenSeat)){
infoList.get(indexOfThis).getGivenSeatList().add(givenSeat);
int index2 = infoList.get(indexOfThis).getSeatList().indexOf(givenSeat);
infoList.get(indexOfThis).getSeatList().set(index2, "#");//将已售出座位标记位“#”
break;
}else{
System.out.println("您选择的座位号已售出,请重新选择。");
}
}else{
System.out.println("您的输入有误,请重新输入。");
}
}
}
}
//****操作第六步:计算本次购票的折扣后价格****
discountPrice = price*count/10;
//****操作第七步:将本次购票信息存入集合中****
//思路:定义一个Ticket对象;再定义一个单集合ArrayList,里面存放每一个ticket的信息;再定义一个的集合,里面存放每一集合——→换思路:一个集合即可,从末尾开始遍历Ticket
Ticket ticket = new Ticket(givenName,ticketType,givenItem,givenSeat,discountPrice);
ticketList.add(ticket);
}
//以上,单张购票流程整个结束,下面询问是否继续购票及显示已购票信息
buyCount++;
System.out.println("是否继续购票:Y/N");
String choice = sc.next();
if(choice.equalsIgnoreCase("N")){
//****操作第九步:显示购票信息****
int order = 1;
int length = ticketList.size();
System.out.println("您已购票的信息如下:");
for(int j=1;j<=buyCount;j++){
Ticket ticTem = ticketList.get(length-j);
System.out.println(order+"、 "+"电影《"+ticTem.getName()+"》,"+ticTem.getType()+",放映开始时间为"
+ticTem.getItem()+",座位号为"+ticTem.getSeat()+",价格为"+ticTem.getPrice()+"元。");
order++;
}
System.out.println("祝您观影愉快!");
break;
}
}
}
/**
* 第四步:序列化输出
*/
public void createFile(String path){
FileOutputStream fos = null;
ObjectOutputStream oos = null;
try {
fos = new FileOutputStream(path);
oos = new ObjectOutputStream(fos);
oos.writeObject(ticketList);
System.out.println("\n销售信息已成功保存(展示给后台)。");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally{
try {
oos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
fos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* 第五步:读序列化文档
*/
public void readFile(String path){
System.out.println("销售信息恢复如下(展示给后台):");
FileInputStream fis = null;
ObjectInputStream ois = null;
try {
fis = new FileInputStream(path);
ois = new ObjectInputStream(fis);
//注意强转
ArrayList<Ticket> ticket = (ArrayList<Ticket>)ois.readObject();
System.out.println(ticket);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally{
try {
ois.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
fis.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}