需求:
1.额度1500或自定义
2.实现购物商城,买东西加入 购物车,调用信用卡接口结账
3.可以提现、存款、余额查询和转账等
4.打印指定日期间购物清单
5.支持多账户
6.ATM记录操作所有日志
7.提供管理接口,包括查询账户、添加账户、注销账户,冻结解冻账户等
8.日志功能用装饰器
写这个小项目之前先构思了一下,画个流程图,然后设置好代码的目录结构,实现方式等,把框架写好了,接下来就是一个个小功能的实现啦。
流程图如下:
代码目录结构如下:
我先写了一个用户类,专门提供用户管理。主要功能如下:
1、账户信息查询
2、添加新账户
3、删除账户
4、修改密码
5、修改手机号
6、冻结账户
7、解冻账户
8、修改账户信用额度
把一些环境变量信息写在conf目录下的settings.py里
# -*- coding: utf-8 -*-
import time
import os
from core import users as u
# 管理员账号密码
adminName = 'admin'
adminPassword = 'admin'
# 当前时间
timeYear = time.strftime('%Y%m%d', time.localtime(time.time()))
timeDay = time.strftime('%H%M%S', time.localtime(time.time()))
# 信用卡额度
creditLimit = 15000
# 用户信息文件
userInfoFile = "../db/user-info.json"
# 商品信息文件
goodsInfoFile = '../db/goods-info.json'
# 用户购物车文件
def ShoppingCarFile(username,year=timeYear,day=timeDay):
dirs = '../db/shoppinCar/%s' % username
if not os.path.exists(dirs):
os.makedirs(dirs)
shoppingInfoFile = '%s/shopping-info_%s%s.json' % (dirs,year,day)
#print(shoppingInfoFile)
return shoppingInfoFile
# 日志文件
logFile = '../log/atm.log'
# log功能,使用装饰器
def log(func):
def wrapper(*args, **kwargs):
users = u.Users().getUser()
f = open(logFile, "a", encoding="utf-8")
if func.__doc__ != '查询余额' and func.__doc__ != '修改密码':
f.write('%s\t%s%s%s元\n' % (time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()),users[args[1]]['username'],func.__doc__,args[-1]))
else:
f.write('%s\t%s%s\n' % (time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()),users[args[1]]['username'],func.__doc__))
f.close()
return func(*args, **kwargs)
return wrapper
settings.py
接下来就是开始写core目录下的users.py了,把以上的各个小功能逐一实现。
# -*- coding: utf-8 -*-
import os,sys
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
sys.path.append(BASE_DIR)
from conf import settings as ss
import json
class Users(object):
'''用户类,管理用户接口'''
def getUser(self):
'''获取账户信息'''
try:
with open(ss.userInfoFile, "r", encoding="utf-8") as f:
users = json.load(f)
#print(users)
except:
print('have no users inof!')
users = {}
return users
def setUser(self,users):
'''重新写入账户信息'''
with open(ss.userInfoFile, 'w') as f :
json.dump(users , f , indent="\t")
#print(users)
def checkUser(self,username):
'''检查用户是否存在'''
users = self.getUser()
for userID in users.keys():
if username == users[userID]['username']:
#print('账户名已存在!')
return True
return False
def getUserID(self,username):
'''通过账户名获取账户id值'''
users = self.getUser()
for userID in users.keys():
if username == users[userID]['username']:
return userID
print('账户不存在!!!')
return self.turnBack()
def addUser(self,username,password,phone=None,limit=ss.creditLimit,isLock=False):
'''添加账户'''
users=self.getUser()
# 字典key值自动加1
max_id = "000000"
if users != {}:
max_id = max(users.keys())
userID = str(int(max_id) + 1).zfill(6) # id自动+1,补足6位
users[userID] = {
'username':username,
'password':password,
'phone':phone,
'limit':limit,
'isLock':isLock
}
self.setUser(users)
print(''.center(50,'#'))
print('账户\033[42;1m%s\033[0m添加成功' % users[userID]['username'])
print('账户名: %s' % users[userID]['username'])
print('手机号: %s' % users[userID]['phone'])
print('信用卡额度: %s' % users[userID]['limit'])
print(''.center(50,'#'))
return True
def delUser(self,userID):
'''账户注销'''
users=self.getUser()
try:
print('账户\033[41;1m%s\033[0m已删除!' % users[userID]['username'])
users.pop(userID)
except:
print('账户不存在!')
return False
self.setUser(users)
return True
def frozenUser(self,userID):
'''冻结账户'''
users=self.getUser()
users[userID]['isLock'] = True
self.setUser(users)
print('账户%s已冻结!' % users[userID]['username'] )
def thawUser(self,userID):
'''解冻账户'''
users=self.getUser()
users[userID]['isLock'] = False
self.setUser(users)
print('账户%s已解除冻结!' % users[userID]['username'] )
def userLoggin(self):
'''用户登录验证'''
for i in range (1,4):
username = input('请输入用户名:')
if self.checkUser(username):
userID = self.getUserID(username)
users=self.getUser()
if users[userID]['isLock']:
print('账户已被冻结,请联系管理员!')
exit(-1)
password = input('请输入密码:')
if self.checkPassword(userID,password):
return username,userID
else:
print('密码错误!')
else:
print('账户不存在!')
print('操作过于频繁!')
return False,False
def checkPassword(self,userID,password):
'''校验密码是否正确'''
users = self.getUser()
if password == users[userID]['password']:
return True
else:
return False
@ss.log
def changePasswd(self,userID):
'''修改密码'''
for i in range(1,4):
old_passwd = input('请输入旧密码:')
# 校验旧密码是否正确
check = self.checkPassword(userID,old_passwd)
if check:
for j in range(1,4):
if j == 3:
print('操作过于频繁')
return False
new_passwd1 = input('请输入新密码:')
if len(new_passwd1) < 8:
print('密码不能少于8位!')
continue
if new_passwd1 == old_passwd:
print('新密码不能和旧密码一样!')
continue
new_passwd2 = input('请再次确认新密码:')
if new_passwd1 != new_passwd2:
print('两次输入的密码不一致!')
continue
else:
users=self.getUser()
users[userID]['password'] = new_passwd1
self.setUser(users)
print('密码修改成功!')
return True
else:
print('旧密码输入错误!')
print('操作过于频繁。')
return False
def changePhone(self,userID,phone):
'''修改手机号'''
users=self.getUser()
users[userID]['phone'] = phone
self.setUser(users)
print('手机号修改完成!')
print('账户名: %s' % users[userID]['username'])
print('手机号: %s' % users[userID]['phone'])
def changeLimit(self,userID,limit):
'''修改账户信用额度'''
users=self.getUser()
users[userID]['limit'] = limit
self.setUser(users)
print('账户%s的信用额度已更新为%s' % (users[userID]['username'],users[userID]['limit']))
def turnBack(self):
'''返回上级菜单'''
print(''.center(15,'*'))
print( "1、返回上级")
print( "2、退出")
print(''.center(15,'*'))
while True:
choise = input("请选择:")
if choise == '1':
self.userManagement()
elif choise == '2':
print ("谢谢使用!")
exit(-1)
else:
print ("输入有误,请重新输入!")
def userManagement(self):
'''账户管理,总入口函数'''
print('''请选择账户管理操作:
1、账户信息查询
2、添加新账户
3、删除账户
4、修改密码
5、修改手机号
6、冻结账户
7、解冻账户
8、修改账户信用额度''')
while True:
choise=input().strip()
if choise.isdigit():
if '1' == choise:
# 账户信息查询
users = self.getUser()
print(''.center(50,'#'))
for i in users.keys():
print('账户名: %s' % users[i]['username'])
print('手机号: %s' % users[i]['phone'])
print('信用卡额度: %s' % users[i]['limit'])
print(''.center(50,'#'))
self.turnBack()
break
elif '2' == choise:
# 添加新账户
username = input('请输入用户名:')
# 校验用户名是否已存在
if self.checkUser(username):
print('账户已存在!')
self.turnBack()
break
password = input('请输入密码:')
phone = input('请输入手机号:')
self.addUser(username,password,phone)
self.turnBack()
break
elif '3' == choise:
# 删除账户
username = input('请输入要删除的账号名:')
userID = self.getUserID(username)
self.delUser(userID)
self.turnBack()
break
elif '4' == choise:
# 修改密码
username = input('请输入要修改密码的账号名:')
userID = self.getUserID(username)
self.changePasswd(userID)
self.turnBack()
break
elif '5' == choise:
# 修改手机号
username = input('请输入要修改手机号的账号名:')
userID = self.getUserID(username)
phone = input('请输入新手机号:')
self.changePhone(userID,phone)
self.turnBack()
break
elif '6' == choise:
# 冻结账户
username = input('请输入要冻结的账号名:')
userID = self.getUserID(username)
self.frozenUser(userID)
self.turnBack()
break
elif '7' == choise:
# 解冻账户
username = input('请输入要解除冻结的账号名:')
userID = self.getUserID(username)
self.thawUser(userID)
self.turnBack()
break
elif '8' == choise:
# 修改账户信用额度
username = input('请输入要更改信用额度的账号名:')
userID = self.getUserID(username)
limit = input('请输入要更改的信用额度:')
if limit.isdigit():
self.changeLimit(userID,limit)
else:
print('输入无效,更新失败!')
self.turnBack()
break
else:
print('请输入有效数字!')
else:
print('请输入有效数字!')
def run():
users=Users()
users.userManagement()
if __name__ == '__main__':
run()
users.py
把用户类实现后,接下来是管理员的商品类啦,可以实现商品的上架、下架、修改商品价格已经商品查询功能等
# -*- coding: utf-8 -*-
import os,sys
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
sys.path.append(BASE_DIR)
from conf import settings as ss
import json
class Goods(object):
'''商品类'''
def getGoods(self):
'''获取账户信息'''
try:
with open(ss.goodsInfoFile, "r", encoding="utf-8") as f:
goods = json.load(f)
#print(goods)
except:
print('have no goods inof!')
goods = []
#print(goods)
return goods
def setGoods(self,goods):
'''写入商品信息'''
with open(ss.goodsInfoFile, "w", encoding="utf-8") as f:
json.dump(goods, f, ensure_ascii=False, indent="\t")
def printGoods(self):
'''打印商品列表'''
goods = self.getGoods()
print(''.center(20,'-'))
print('所有商品列表如下:')
print(''.center(20,'-'))
print('序号\t\t名称\t\t价格')
print(''.center(20,'-'))
for name,price in goods:
print(goods.index([name,price]), '\t', name,"\t",price)
print(''.center(20,'-'))
def checkPrice(self,price):
'''校验输入的价格是否为有效数字'''
if price.isdigit():
return int(price.strip())
else:
print('请输入有效数字!')
return False
def checkName(self,name):
'''校验商品名称是否存在'''
goods = self.getGoods()
for goodName,price in goods:
if name == goodName:
return True
return False
def goodsShelves(self):
'''商品上架'''
name = input("请输入要上架的商品名称").strip()
if self.checkName(name):
print('商品已存在!')
return False
price=input("请输入该商品的价格")
if self.checkPrice(price):
goods = self.getGoods()
goods.append([name,int(price)])
print('商品\033[32;1m%s\033[0m已上架完成!价格为:\033[32;1m%s\033[0m' % (name,int(price)))
self.setGoods(goods)
return True
def goodDelete(self):
'''商品下架'''
name = input("请输入要下架的商品名称").strip()
if not self.checkName(name):
print('商品不存在!')
return False
else:
goods = self.getGoods()
for goodName,goodsPrice in goods:
if goodName == name:
goods.remove([goodName,goodsPrice])
#print(goods)
print('商品\033[41;1m%s\033[0m已下架成功!' % name)
self.setGoods(goods)
return True
def goodSearch(self,name):
'''商品搜索查询'''
if not self.checkName(name):
print('商品不存在!')
return False
else:
goods = self.getGoods()
for goodName,goodsPrice in goods:
if name == goodName:
# 获取商品在list中的index值
index = goods.index([goodName,goodsPrice])
#print(index)
print('商品价格为:')
print(name,'\t',goods[index][1])
return (goods,index,True)
def goodModify(self,name):
'''修改商品价格'''
goods,index,Flag = self.goodSearch(name)
#print(Flag,'\n',goods,"\n",index)
price=input("请要修改商品的价格")
if self.checkPrice(price):
goods[index][1] = int(price)
self.setGoods(goods)
print('商品价格修改完成!')
print(goods[index][0],"\t","\033[32;1m%s\033[0m" % goods[index][1] )
return True
def turnBack(self):
'''返回上级菜单'''
print(''.center(15,'*'))
print( "1、返回上级")
print( "2、退出")
print(''.center(15,'*'))
while True:
choise = input("请选择:")
if choise == '1':
self.goodManagement()
elif choise == '2':
print ("谢谢使用!")
exit(-1)
else:
print ("输入有误,请重新输入!")
def goodManagement(self):
'''用户选择'''
print("请选择操作:")
print("1、打印商品列表")
print("2、商品上架")
print("3、商品下架")
print("4、修改商品价格")
print("5、商品查询")
while True:
choice = input().strip()
if choice.isdigit():
choice = int(choice)
if 1 == choice:
# 打印商品列表
self.printGoods()
self.turnBack()
break
elif 2 == choice:
# 商品上架
self.goodsShelves()
self.turnBack()
break
elif 3 == choice:
# 商品下架
self.goodDelete()
self.turnBack()
break
elif 4 == choice:
# 修改商品价格
name = input("请要修改商品价格的名称:").strip()
self.goodModify(name)
self.turnBack()
break
elif 5 == choice:
# 商品查询
name = input("请输入要查询商品价格的名称:").strip()
self.goodSearch(name)
self.turnBack()
break
else:
print("输入有误,请重新输入")
else:
print("请输入有效数字!")
def run():
goods = Goods()
goods.goodManagement()
if __name__ == '__main__':
run()
goods.py
emmm,管理员的两大块就完成啦,接下来就是普通用户的一些操作啦,这个开发顺序也是要稍微注意一下的,先实现管理员的功能,就有了普通用户的信息嘛,然后再实现普通用户,这就可以直接用已有的账户信息啦,然后接着实现ATM功能,再实现购物功能,这样购物结算的时候就可以调用AMT结算啦。
废话不多说,看看ATM类的主要功能:
1、存款
2、取款
3、转账
4、查询余额
5、修改密码
# -*- coding: utf-8 -*-
import os,sys
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
sys.path.append(BASE_DIR)
from core import users as u
from conf import settings as ss
class ATM(object):
'''ATM类'''
@ss.log
def deposit(self,userID,money):
'''存款'''
# 调用用户类的获取用户信息方法
users = u.Users().getUser()
try:
users[userID]['money'] = int(users[userID]['money']) + money
except:
# 第一次存钱
users[userID]['money'] = money
# 调用用户类,重新写入用户信息
u.Users().setUser(users)
#print('账户%s已存入%s元!' % (users[userID]['username'],money) )
#print('总余额为: %s 元' % users[userID]['money'])
return True
@ss.log
def withdrawals(self,userID,money):
'''取款'''
users = u.Users().getUser()
try:
int(users[userID]['money'])
if money > users[userID]['money']:
print('余额不足!!')
return False
else:
users[userID]['money'] -= money
except:
print('余额不足!')
return False
u.Users().setUser(users)
#print('账户%s已取出%s元!' % (users[userID]['username'],money) )
#print('总余额为: %s 元' % users[userID]['money'])
return True
@ss.log
def transfer(self,userID1,userID2,money):
'''转账'''
# 直接调用取款、存款函数,取款成功了再存款。
if self.withdrawals(userID1,money):
self.deposit(userID2,money)
return True
return False
@ss.log
def queryBalance(self,userID):
'''查询余额'''
users = u.Users().getUser()
try:
print('账户%s的余额为:\n%s元' % (users[userID]['username'],users[userID]['money']))
except:
print('账户%s的余额为:\n0元' % (users[userID]['username']))
def turnBack(self,userID):
'''返回上级菜单'''
print(''.center(15,'*'))
print( "1、返回上级")
print( "2、退出")
print(''.center(15,'*'))
while True:
choise = input("请选择:")
if choise == '1':
self.atmOperation(userID)
elif choise == '2':
print ("谢谢使用!")
exit(-1)
else:
print ("输入有误,请重新输入!")
def inputMoney(self,userID):
'''输入金额'''
money = input()
if money.isdigit():
return int(money)
else:
print('输入不是有效数字!')
return self.turnBack(userID)
def atmOperation(self,userID):
'''ATM操作,总入口函数'''
print('''请选择:
1、存款
2、取款
3、转账
4、查询余额
5、修改密码''')
while True:
choise=input().strip()
if '1' == choise:
# 存款
print('请输入存款金额:')
money = self.inputMoney(userID)
self.deposit(userID,money)
print('存入%s元成功!' % (money))
self.turnBack(userID)
break
elif '2' == choise:
# 取款
print('请输入取款金额!')
money = self.inputMoney(userID)
if self.withdrawals(userID,money):
print('已取出%s元!请收好!' % (money))
self.turnBack(userID)
break
elif '3' == choise:
# 转账
username = input('请输入要转账的账户名:')
checkUser = u.Users().checkUser(username)
if checkUser:
print('请输入要转账金额:')
money = self.inputMoney(userID)
userID2 = u.Users().getUserID(username)
if self.transfer(userID,userID2,money):
print('已成功向%s转账%s元!' % (username, money))
else:
print('账户不存在!')
self.turnBack(userID)
break
elif '4' == choise:
# 查询余额
self.queryBalance(userID)
self.turnBack(userID)
break
elif '5' == choise:
# 修改密码
u.Users().changePasswd(userID)
self.turnBack(userID)
break
else:
print('输入有误!')
def run():
atm = ATM()
print('欢迎登录瑞士银行!!')
# 登录验证,通过后返回userID值
username,userID = u.Users().userLoggin()
if userID:
atm.atmOperation(userID)
if __name__ == '__main__':
run()
atm.py
接下来就是普通用户的购物,这个比其他功能复杂点的地方就是不同的用户购物信息要分类存储。
# -*- coding: utf-8 -*-
import os,sys
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
sys.path.append(BASE_DIR)
from core import goods as g
from core import atm as a
from conf import settings as ss
from core import users as u
import json
class ShoppingCar(object):
'''用户购物'''
def getBuyList(self,username):
'''读取用户购物车清单'''
try:
with open(ss.ShoppingCarFile(username), "r", encoding="utf-8") as f:
buyList = json.load(f)
#print(buyList)
except:
print('have no goods inof!')
buyList = []
#print(buyList)
return buyList
def setBuyList(self,username,buyList):
'''将用户购物清单写入文件中'''
with open(ss.ShoppingCarFile(username), "w", encoding="utf-8") as f:
json.dump(buyList, f, ensure_ascii=False, indent="\t")
def printBuyList(self,username):
'''打印购物车列表'''
sum = 0
buyList = self.getBuyList(username)
print(''.center(20,'-'))
print('购买商品列表如下:')
print(''.center(20,'-'))
print('名称\t\t价格')
print(''.center(20,'-'))
for name,price in buyList:
print(name,"\t",price)
sum = sum + int(price)
print(''.center(20,'-'))
print('总计:\t %s' % sum)
return sum
def buyList(self,username,choice):
'''请选择购物'''
buyList = self.getBuyList(username)
goods = g.Goods().getGoods()
if choice < len(goods) and choice >=0:
print(goods[choice])
buyList.append(goods[choice])
#print(buyList)
self.setBuyList(username,buyList)
print('加入购物车成功')
else:
print('商品未上架!')
return buyList
def shopping(self,username,userID):
'''购物'''
goods = g.Goods().printGoods()
print(goods)
while True:
print('请选择,停止购物按q')
choice = input().strip()
if choice != 'q':
if choice.isdigit():
choice = int(choice)
self.buyList(username,choice)
else:
print('请输入有效数字!')
else:
# 获取购物车总价
sum = self.printBuyList(username)
print('开始结算!')
n = 2
while n >=0:
password = input('请输入银行卡密码:')
if u.Users().checkPassword(userID,password):
# 调用银行卡接口结算
if a.ATM().withdrawals(userID,sum):
print('结算成功!')
return True
else:
print('结算失败!')
else:
if n !=0:
print('密码错误!你还有%s次尝试机会!' % n)
else:
print('尝试次数已用完,结算失败!')
n -=1
# 清空本次购物信息
#buyList = ['结算失败!']
#self.setBuyList(username, buyList)
os.remove(ss.ShoppingCarFile(username))
return False
def run():
s = ShoppingCar()
print('欢迎登录XXX网上购物商城!')
# 登录验证,通过后返回userID值
username,userID = u.Users().userLoggin()
if userID:
s.shopping(username,userID)
if __name__ == '__main__':
run()
shopping.py
最后就是写一个总的入口功能啦,在bin/main.py里去实现调用以上各个功能。
# -*- coding: utf-8 -*-
# 2019/7/10 16:57
import os,sys
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
sys.path.append(BASE_DIR)
from core import goods as g
from core import atm as a
from conf import settings as ss
from core import users as u
from core import shopping as s
def checkAdmin():
'''校验管理员账号密码'''
i = 2
while i >= 0:
username = input('请输入管理员账号:')
if username == ss.adminName:
j = 2
while j >= 0:
password = input('请输入管理员密码:')
if password == ss.adminPassword:
return True
else:
print('管理员密码输入有误!')
if j == 0:
print('账号已锁定,请10分钟后重新尝试!')
return False
else:
print('你还有%s次尝试机会!' % j)
j -=1
else:
print('管理员账号输入有误!')
if i != 0:
print('你还有%s次尝试机会!' % i)
else:
print('账号已锁定,请10分钟后重新尝试!')
i -=1
return False
def averageUser():
'''普通用户操作'''
print('''请选择操作:
1、购买商品
2、ATM操作''')
while True:
choice = input().strip()
if '1' == choice:
s.run()
break
elif '2' == choice:
a.run()
break
else:
print('输入有误!')
def adminManagement():
'''管理员操作'''
print('''请选择操作:
1、商品管理
2、用户管理''')
while True:
choice = input().strip()
if '1' == choice:
g.run()
break
elif '2' == choice:
u.run()
break
else:
print('输入有误!')
def run():
'''程序入口函数'''
print('''欢迎进入ATM+购物程序!
请选择操作:
1、普通用户
2、管理员''')
while True:
choice = input().strip()
if '1' == choice:
averageUser()
break
elif '2' == choice:
if checkAdmin():
adminManagement()
break
else:
print('输入有误!')
if __name__ == '__main__':
run()
main.py
再给ATM的操作加上日志功能,这里用了装饰器,直接应用到ATM操作的函数上,而不改变原来函数的调用方式。就写了一个log函数,放在settings.py里啦,就不再单独写一个py文件。
源码链接:https://github.com/caiqinxiong/ATM-Shopping.git