java超市管理系统 1.1(含源文件,后续会继续优化~)
文章目录
前言
继上一篇文章超市管理系统1.0,在增删改查的基础上,本篇中的程序加入了如下功能
- 两种用户身份的登录和切换功能
- 顾客用户的购买商品功能和支付功能
- 所有商品信息可存储到本地硬盘
另外,本篇中的程序在商品的存储结构上也由
ArrayList
集合改为了TreeMap
集合
关于增删改查的需求这里就不过多赘述了~这里只说一下前言中提及的三种新的功能所对应的需求
一. 两种用户身份的登录和切换功能
1 分析两种身份的权限及实现
我们首先先来分析一波:程序分为不同身份的用户的目的,无非就是为了区分
顾客
和超市管理者
这两种身份在使用程序时候,所访问的程序功能和数据是不同的。
显然,顾客的权限更少一些,面向顾客功能也少一些。
如下图所示:分别表示顾客登录的主菜单和管理员登录的主菜单
可以看到:管理员用户有所有功能和数据的访问权限;顾客用户只有查询、购买的功能和部分数据的访问权限
还有一点就是,用户在访问数据(例如查看商品信息、查询商品信息)的时候,不应该显示商品的进价。
我们就可以在显示商品信息的部分根据不同用户的身份,调用不同的方法来实现不同数据的显示。
我们该如何区分用户的身份呢?
我们可以定义:管理员用户有且仅有一套用户名和密码,登录成功后标记为
true
,表示此时是管理员用户正在使用;普通用户(顾客身份)登录后,标记为false
,表示此时为顾客正在使用。接着,把这个标记赋给一个变量(例如usersId
)把这个userId
变量在相应的方法中作为参数传递,达到不同身份,不同功能,不同权限的效果。
2 分析登录功能的实现
成功登录的核心满足条件就是:当用户名和密码同时与存储的内容相一致的时候,用户登录成功,否则失败。
因此,我们可以使用带缓冲的字符输入流
BufferedReader
向内存中读取数据,一次读取一行,再根据中间的等号把字符串分割为用户名和密码,并分别暂存到一个字符串类型的数组中,当用户输入的用户名和密码和查找到的某一条信息相同的时候,系统认定登陆成功,否则登陆失败。
@Override
public boolean isLogin(String userName, String passWord) {
boolean flag = false;
try {
//创建带有缓冲的字符输入流来给文件读取数据
BufferedReader br = new BufferedReader(new FileReader("O:\\javaSE练习" +
"\\src\\supermarket_manager_system_1_1_1\\user.txt"));
String line;
while ((line = br.readLine()) != null) {
String[] s = line.split("=");
if (s[0].equals(userName) && s[1].equals(passWord))
flag = true;
}
} catch (IOException e) {
e.printStackTrace();
}
return flag;
}
3 分析注册功能的实现
注册功能的实现思路为:(向内存中)输入自定义的用户名和密码后,通过IO流来把用户输入的账号和密码存储起来(本程序使用的是带有缓冲的字符输出流BufferedWriter
)
需要注意的就是传入参数的时候要注意信息的追加(即FileWriter的第二个参数为true)。
@Override
public void regist(UsersInfo usersInfo) throws IOException {
try {
//把用户信息存到文件中
//用户名和密码用等号隔开
String info = usersInfo.getUserName() + "=" + usersInfo.getPassWord();
//创建带有缓冲的字符输出流来给文件写入数据,创建了一个可以追加写入的FileWriter,避免了文件中之前的用户信息被覆盖
BufferedWriter bw = new BufferedWriter(new FileWriter("O:\\javaSE练习\\src" +
"\\supermarket_manager_system_1_1_1\\user.txt", true));
bw.write(info); //写入信息
bw.newLine(); //换行
bw.flush(); //刷新
bw.close(); //关闭
} catch (IOException e) {
e.printStackTrace();
}
}
4 分析切换用户功能的实现
用户使用这个系统时,如果需要切换身份来查看商品信息,那么只能重新登录,那么我们可以加入一个功能(编写一个方法)使得用户想切换身份的时候,可以跳转到登录界面。
case 3:
return wares; //当(管理员)用户对商品信息“增删改查完毕后”,从这里返回wares对象以实现刷新!!
只需要在相应的流程控制语句中,达到跳出当前流程并跳转到“登录界面”即可实现切换用户功能
二. 顾客用户的购买商品功能和支付功能【重点】
大体思路为设置相应的方法,通过调用实现功能
但是!在实现购买支付功能前,我们要明确以下几点:
- 在购买的过程中,如何对用户已经选择的商品进行存储?
- 对于多次购买同一件商品,如何保证用户输入购买的数量合法?
- 如何对用户已经选择的商品清单进行结算?
- 在结算前,用户想要更改购买的数量,这时候如何做到商品的库存和结算金额实现同步?
- 支付完毕后,如何使得商品的库存真正的减少?
- 顾客的“购物车合并”问题
下面让我们一一分析一下~
1.在购买的过程中,如何对用户已经选择的商品进行存储?
我们可以定义一个购物车类BuyCar
,这个类中有某种类型的属性,可以用于存储用户已经预购的商品信息。
对于存储这些信息(用户购买的商品id
和购买数量
),我们可以用集合中的ArrayList
、HashMap
、TreeMap
等常用集合来存储。
为了在结算时候便于增删商品信息以及结算时候便于计算总价格,我们采用TreeMap这样键值对的形式进行存储(自动使得购物车中的商品不重复),键为商品编号,值为商品购买数量。
这样购物车类的TreeMap<String, Integer>
类型的属性就确立了。
public class BuyCar {
private TreeMap<String, Integer> waresInBuyCar;
/**
* 构造方法
*/
public BuyCar(TreeMap<String, Integer> waresInBuyCar) {
this.waresInBuyCar = waresInBuyCar;
}
/**
* setter/getter
*/
public TreeMap<String, Integer> getWaresInBuyCar() {
return waresInBuyCar;
}
public void setWaresInBuyCar(TreeMap<String, Integer> waresInBuyCar) {
this.waresInBuyCar = waresInBuyCar;
}
}
2. 对于多次购买同一件商品,如何保证用户输入购买的数量合法?
当用户要修改购买数量或者要输入购买数量的时候,对用户的输入和所有商品信息中的库存进行比较,如果超出了库存量,则需要用户重新输入。这样就确保了所有商品的库存量非负,使得程序更加合理。
//当想要购买的数量大于库存或者小于0的时候,加入购物车失败
if (wantCount > allWares.get(tempId).getCount() || wantCount < 0) {
System.out.println("【提示】当前[" + allWares.get(tempId).getName() + "]的库存量为"
+ allWares.get(tempId).getCount() + ", 请您重新输入");
} else {
waresInBuyCar.put(tempId, wantCount); //将商品放入购物车
break;
}
3. 如何对用户已经选择的商品清单进行结算?
由于购物车中的属性是TreeMap
集合类型的,因此我们可以使用增强for循环来计算共需要支付的钱数。
//开始计算要支付多少钱
for (String wareId : buyCar.getWaresInBuyCar().keySet()) {
Integer buyingCount = buyCar.getWaresInBuyCar().get(wareId);
needPay += buyingCount * allWares.get(wareId).getOutPrice();
}
4. 在结算前,用户想要更改购买的数量,这时候如何做到商品的库存和结算金额实现同步?
把修改购物清单的代码放入while
循环中,在while
循环中不断对buyCar
购物车对象进行改变即可。
直到用户确认支付,这时候,就开始更新库存(新库存=旧库存-购买数量),然后while
循环结束,购买功能结束
/*
一定要刷新库存!!!!!!
*/
int oldCount; //购买前的库存(
int wantCount; //想要购买的数量
for (String wareId : buyCar.getWaresInBuyCar().keySet()) {
oldCount = allWares.get(wareId).getCount();
wantCount = buyCar.getWaresInBuyCar().get(wareId);
//新的库存 = 购买前的库存 - 购买的数量)
allWares.get(wareId).setCount(oldCount - wantCount);
} //刷新完毕!!!
5. 支付完毕后,如何使得商品的库存真正的减少?
一旦用户支付完毕,购买功能结束,购买功能对应的方法中,将所有商品allWares
这个TreeMap<String,Wares_Info>
类型的变量作为返回值返回,使得整个程序的商品信息都公用一份allWares
变量。
return allWares;
6. 顾客的“购物车合并”问题
购买功能中:我们在四类商品的每一个类中加入了可被购买的方法,这个方法的返回值类型是购物车BuyCar
类型,也就是说,如果用户选择购买四类商品,我们在每一个购买方法中新建一个存储该类商品的购物车,再把四个购物车整合到一起,形成一个总的购物车对象,再传入支付功能。
我们如何把这四个购物车整合到一起呢?
由于TreeMap中没有提供相应方法。因此我们可以自己写一个方法使得把一个TreeMap1
集合加入到另一个TreeMap2
集合中,返回TreeMap2
集合即可(下面为图解)
三. 所有商品信息可存储到本地硬盘
1. 字节流?字符流?序列化?应该使用哪个?
我们需要存储的是
allWares
(所有商品信息对象),对于对象的存储,我们只需要把相应的类实现Serializable
接口,然后把对象allWares
进行序列化进行存储,反序列化进行读取即可,同样我们可以自己写两个方法分别实现序列化和反序列化。
当程序第一次运行的时候,执行自动初始化所有商品信息的addTestData()
方法;当已经存在序列化的文件后,就可以直接读取序列化的文件了(如下图所示)
//通过反序列化读取信息
TreeMap<String, Wares_Info> allWares = null;
try {
allWares = Tools.readInfo();
} catch (IOException | ClassNotFoundException e) {
allWares = Tools.addTestData(); //如果用户首次使用,将会自动初始化商品列表,并生成waresInfo文件
}
当用户选择0退出程序的时候,我们需要对allWares
对象序列化,以存储用户对商品信息的操作(如下图所示)
case 0:
Show.showEndMenu();
/*
此处应该序列化allWares对象,以实现保存的效果
*/
Tools.saveInfo(wares);
System.exit(0);
四. 源码
源码可私信获取~