目录
ATM项目三层结构
0 分析需求
作业需求:
模拟实现一个ATM + 购物商城程序
额度 15000或自定义
实现购物商城,买东西加入 购物车,调用信用卡接口结账
可以提现,手续费5%
支持多账户登录
支持账户间转账
记录每月日常消费流水
提供还款接口
ATM记录操作日志
提供管理接口,包括添加账户、用户额度,冻结账户等。。。
用户认证用装饰器
上述需求可以分为下面10个功能
# 登录
# 用户视图:1.接收输入用户名密码,转交逻辑接口
# 5.输出可视化结果
# 逻辑接口:2.将用户名交给数据处理层,查找数据
# 4.得到用户视图及数据处理的用户名密码,进行比较,将结果返回用户视图层
# 数据处理:3.将用户名对应的数据提取出来,交给逻辑接口
# 注册
# 用户视图:1.接收输入用户名密码,转交逻辑接口
# 6.输出可视化结果
# 逻辑接口:2.将用户名交给数据处理层,查找数据
# 4.得到用户名是否存在,若不存在将用户名密码交给数据处理存储,返回用户视图,若存在返回用户视图
# 数据处理:3.查找用户名对应的数据是否存在,结果交给逻辑接口
# 5.如果用户名不存在,收到用户名密码,存储数据
# 转账
# 用户视图:1.得到在线用户名,转账的用户名及金额,判断金额有效性
# 8.输出可视化结果
# 逻辑接口:2.将转账用户名交给数据处理层,查找是否存在
# 4.将在线用户名交给数据处理层,查看余额是否足够
# 6.若转账用户名存在且当前用户金额足够,计算结果,操作数据处理保存数据,返回用户视图
# 数据处理:3.查找用户名对应的数据是否存在,若不存在直接将结果交给逻辑接口
# 5.查看在线用户余额是否足够,若不足够直接将结果交给逻辑接口
# 7.若满足条件,存储数据
# 提现
# 用户视图:1.得到在线用户名,金额,判断金额有效性
# 6.输出可视化结果
# 逻辑接口:2.将在线用户名交给数据处理层,查看余额是否足够
# 4.若当前用户金额足够,计算结果,操作数据处理保存数据,返回用户视图
# 数据处理:3.查看在线用户余额是否足够,若不足够直接将结果交给逻辑接口
# 5.若满足条件,存储数据
# 还款
# 用户视图:1.得到在线用户名,金额,判断金额有效性
# 4.输出可视化结果
# 逻辑接口:2.计算结果,操作数据处理保存数据,返回用户视图
# 数据处理:3.存储数据
# 查看流水
# 用户视图:1.得到在线用户名
# 4.输出可视化结果
# 逻辑接口:2.操作数据处理查找数据,得到结果,返回用户视图
# 数据处理:3.查找在线用户流水数据,返回逻辑接口
# 查看余额
# 用户视图:1.得到在线用户名
# 4.输出可视化结果
# 逻辑接口:2.操作数据处理查找数据,得到结果,返回用户视图
# 数据处理:3.查找在线用户余额数据,返回逻辑接口
# 购物
# 用户视图:1.申请查看货物信息, 5.输出可视化的货物信息,
# 6.接收在线用户,选择货物编号及数量,简单判断有效性,若有效传入逻辑接口
# 7.输出可视化结果
# 逻辑接口:2.申请查看货物信息, 4.得到货物信息,返回视图层,
# 7.调取在线用户信息,更改,传入数据处理 9.得到结果返回用户视图
# 数据处理:3.查找货物信息,返回逻辑接口 8.保存用户数据
# 查看购物车
# 用户视图:1.得到在线用户名 5.输出可视化结果
# 6.询问是否结账,简单判断有效性,若有效传入逻辑接口
# 8.输出可视化结果
# 逻辑接口:2.操作数据处理查找数据, 4得到结果,返回用户视图
# 5.计算总价,调取在线用户信息,查看金额是否足够,如果足够则扣费,传入数据处理,不足则返回视图层结果
# 7.得到结果返回用户视图
# 数据处理:3.查找在线用户流水数据,返回逻辑接口 6.保存用户数据,返回结果
# 管理员功能
# 用户视图:1.得到在线用户名
# 2.输出可视化结果
# 逻辑接口:1.操作数据处理查看是否为管理员,得到结果,返回用户视图
# 2.若为管理员进入管理员功能
# 数据处理:查看在线用户是否为管理员,结果返回逻辑接口
1 用户视图层
主要为输入输出的显示及简单的逻辑判断
from interface import user_interface
from interface import shop_interface
from interface import bank_interface
from interface import admin_interface
from lib import common
online_user = None
# 登录
def login():
while 1:
print("登录功能执行中")
user_name = input("请输入用户名:[温馨提示:输入q退出]")
if user_name.lower() == "q":
break
password = input("请输入密码:")
flag, msg = user_interface.login_interface(user_name, password)
if flag is False:
print(msg)
continue
if flag is True:
print(msg)
global online_user
online_user = user_name
break
pass
# 注册
def register():
while 1:
print("===============注册功能执行中")
user_name = input("请输入用户名:[温馨提示:输入q退出]")
if user_name.lower() == "q":
break
flag, msg = user_interface.check_name(user_name)
if flag is False:
print(msg)
continue
password1 = input("请输入密码:")
password2 = input("请确认密码:")
if password1 == password2:
msg = user_interface.new_user(user_name, password1)
print(msg)
break
else:
print("两次密码不一致")
# 查看余额
@common.identify
def check_balance():
print("============查询余额功能执行中")
balance, limit = bank_interface.check_balance_interface(online_user)
print("您的余额为{}元,您的额度为{}元".format(balance, limit))
pass
# 提现
@common.identify
def withdraw():
while 1:
print("================提现功能执行中")
money = input("请输入要提现的金额:[温馨提示:输入q退出]")
if money.lower() == "q":
break
if not money.isdigit():
print("请输入数字")
continue
flag, msg = bank_interface.withdraw_interface(online_user, money)
if flag is False:
print(msg)
continue
else:
print(msg)
break
# 转账
@common.identify
def transfer():
while 1:
print("=============转账功能执行中")
money = input("请输入要转账的金额:[温馨提示:输入q退出]")
if money.lower() == "q":
break
if not money.isdigit():
print("请输入数字")
continue
to_user = input("请输入转账的账号:")
flag, msg = bank_interface.transfer_interface(online_user, to_user, money)
if flag is False:
print(msg)
continue
else:
print(msg)
break
# 还款
@common.identify
def pay_back():
while 1:
print("=============还款功能执行中")
money = input("请输入要还款的金额:[温馨提示:输入q退出]")
if money.lower() == "q":
break
if not money.isdigit():
print("请输入数字")
continue
flag, msg = bank_interface.pay_back_interface(online_user, money)
if flag is False:
print(msg)
continue
else:
print(msg)
break
# 查看流水
@common.identify
def check_record():
print("================查看流水功能执行中")
records = bank_interface.check_record_interface(online_user)
if len(records) == 0:
print("当前用户无记录")
for i in records:
print(i)
# 购物
@common.identify
def shopping():
while 1:
print("================购物功能执行中")
cargo_list = shop_interface.cargo_list_interface()
# ["mac", 20000]
for num, cargo in enumerate(cargo_list):
print("商品编号:[{}] 商品名:[{}] 单价:[{}]".format(num, cargo[0], cargo[1]))
choose = input("请输入选购的商品的编号:[温馨提示:输入q退出]")
if choose.lower() == "q":
break
if not choose.isdigit():
print("请输入正确的编号")
continue
choose = int(choose)
if choose not in range(len(cargo_list)):
print("请输入正确的编号")
continue
quantity = input("请输入选购的商品的数量:")
if not quantity.isdigit():
print("请输入正确的数量")
continue
cargo_info = [cargo_list[choose][0], cargo_list[choose][1], quantity]
msg = shop_interface.add_cargo_cart(online_user, cargo_info)
print(msg)
# 查看购物车
@common.identify
def check_shopping_cart():
print("======================查看购物车功能执行中")
shopping_cart = shop_interface.check_cart_interface(online_user)
# {"臭豆腐": [10, 14], "鸡": [100, 2]}
sum_price = 0
for name, info in shopping_cart.items():
price = info[0] * info[1]
sum_price += price
print("商品名:[{}] 数量:[{}] 单价:[{}] 总价:[{}]".format(name, info[0], info[1], price))
print("=========共计:[{}]元=======".format(sum_price))
close_choose = input("是否结账:[输入y结账]")
if close_choose.lower() == "y":
flag, msg = shop_interface.close_account(online_user, sum_price)
print(msg)
# 管理员功能
@common.identify
def admin():
print("=================管理员功能执行中")
admin_interface.check_admin(online_user)
func_dic = {
"1": ["注册", register],
"2": ["登录", login],
"3": ["转账", transfer],
"4": ["提现", withdraw],
"5": ["还款", pay_back],
"6": ["查询余额", check_balance],
"7": ["查看流水", check_record],
"8": ["购物", shopping],
"9": ["查看购物车", check_shopping_cart],
"10": ["管理员", admin]
}
def run():
while 1:
print("========wu超市========")
for num, func in func_dic.items():
print(num, func[0])
print("======== end ========")
choose = input("请输入功能编号:[温馨提示:输入q退出]")
if choose.lower() == "q":
break
if choose in func_dic:
func_dic[choose][1]()
else:
print("请输入正确的功能编号!")
2 逻辑接口层
主要为有效的输入数据与存储数据的对比,判断结果
2.1 管理员接口
import logging.config
from db import db_handler
from conf import settings
logging.config.dictConfig(settings.LOGGING_DIC)
logger = logging.getLogger('admin')
# 添加账户、用户额度,冻结账户
def check_admin(online_user):
user_dic = db_handler.select(online_user)
if user_dic["admin"] is False:
print("非管理员,无法使用该功能")
return
else:
run_admin(online_user)
def add_user(online_user):
from core import src
src.register()
def change_limit(online_user):
while 1:
change_user = input("请输入更改额度的用户名:[温馨提示:输入q退出]")
if change_user.lower() == "q":
break
user_dic = db_handler.select(change_user)
if not user_dic:
print("输入的用户名不存在")
continue
limit = input("请输入额度:")
if not limit.isdigit():
print("只能输入数字")
continue
user_dic["limit"] = int(limit)
db_handler.save_data(user_dic)
print("更改成功")
logger.info('{}更改{}额度'.format(online_user, change_user))
def freeze_user(online_user):
while 1:
freeze_user = input("请输入冻结的用户名:[温馨提示:输入q退出]")
if freeze_user.lower() == "q":
break
user_dic = db_handler.select(freeze_user)
if not user_dic:
print("输入的用户名不存在")
continue
user_dic["locked"] = True
db_handler.save_data(user_dic)
print("冻结用户{}成功".format(freeze_user))
logger.info('{}冻结{}'.format(online_user, freeze_user))
admin_func = {"0": ["添加账户", add_user],
"1": ["更改额度", change_limit],
"2": ["冻结用户", freeze_user]
}
def run_admin(online_user):
while 1:
for num, func in admin_func.items():
print("[{}]-----[{}]".format(num, func[0]))
cmd = input("请输入功能编号:[温馨提示:输入q退出]")
if cmd.lower() == "q":
break
if cmd not in admin_func:
print("无此功能")
continue
admin_func[cmd][1](online_user)
2.2 用户接口
import logging.config
from db import db_handler
from conf import settings
logging.config.dictConfig(settings.LOGGING_DIC)
logger = logging.getLogger('user')
# 检查用户名是否存在
def check_name(user):
if db_handler.select(user):
return False,"用户名已存在,请重新输入"
else:
return True,""
# 创建新用户
def new_user(user_name,password,limit = 15000):
password = db_handler.encrypt(password)
user_dic = {"user":user_name,"password":password,"balance":0,"limit":limit,"locked":False,"current_account":[],"shopping_cart":{},"admin":False}
db_handler.save_data(user_dic)
logger.info('{}注册'.format(user_name))
return "注册成功"
# 登录接口
def login_interface(user,password):
user_dic = db_handler.select(user)
if not user_dic:
return False,"用户名不存在,请重新输入"
if user_dic["locked"]:
return False,"该用户已被锁定"
password = db_handler.encrypt(password)
if user_dic["password"] == password:
logger.info('{}登录'.format(user))
return True,"登陆成功"
else:
return False,"密码错误,请重新输入"
2.3 银行接口
import logging.config
from db import db_handler
from conf import settings
logging.config.dictConfig(settings.LOGGING_DIC)
logger = logging.getLogger('bank')
def check_balance_interface(online_user):
user_dic = db_handler.select(online_user)
balance = user_dic["balance"]
limit = user_dic["limit"]
logger.info('{}查询余额'.format(online_user))
return balance, limit
def withdraw_interface(online_user, money):
user_dic = db_handler.select(online_user)
balance = user_dic["balance"]
money = float(money)
balance = float(balance)
cost = money * 1.05
if cost > balance:
return False, "余额不足,无法提现"
new_balance = balance - cost
user_dic["balance"] = new_balance
fee = cost - money
user_dic["current_account"].append("提现[{}]元,手续费[{}]".format(money, fee))
db_handler.save_data(user_dic)
logger.info('{}提现{}手续费{}'.format(online_user,money, fee))
return True, "[{}]提现[{}]成功,手续费为[{}]".format(online_user, money, fee)
def transfer_interface(online_user, to_user, money):
to_user_dic = db_handler.select(to_user)
if not to_user_dic:
return False, "转账用户不存在,请重新输入"
online_user_dic = db_handler.select(online_user)
online_user_balance = online_user_dic["balance"]
money = float(money)
balance = float(online_user_balance)
if money > balance:
return False, "余额不足,无法转账"
online_user_dic["balance"] -= money
to_user_dic["balance"] += money
online_user_dic["current_account"].append("转账[{}]元给[{}]".format(money, to_user))
to_user_dic["current_account"].append("收到[{}]转账[{}]元".format(online_user, money))
db_handler.save_data(online_user_dic)
db_handler.save_data(to_user_dic)
logger.info('{}转账给{}-{}'.format(online_user,to_user,money))
return True, "[{}]转账[{}]元成功".format(online_user, money)
def pay_back_interface(online_user, money):
user_dic = db_handler.select(online_user)
balance = user_dic["balance"]
money = float(money)
balance = float(balance)
balance += money
user_dic["balance"] = balance
user_dic["current_account"].append("还款[{}]".format(money))
db_handler.save_data(user_dic)
logger.info('{}还款{}'.format(online_user,money))
return True, "[{}]还款[{}]".format(online_user, money)
def check_record_interface(online_user):
user_dic = db_handler.select(online_user)
records = user_dic["current_account"]
return records
def charge(online_user, sum_price):
user_dic = db_handler.select(online_user)
balance = user_dic["balance"]
limit = user_dic["limit"]
if balance + limit < sum_price:
return False
user_dic["balance"] -= sum_price
user_dic["current_account"].append("购买消费[{}]".format(sum_price))
db_handler.save_data(user_dic)
logger.info("[{}]消费[{}]".format(online_user, sum_price))
return True
2.4 商店接口
from db import db_handler
from interface import bank_interface
def cargo_list_interface():
cargo_list = db_handler.get_cargo_list()
return cargo_list
def add_cargo_cart(online_user, cargo_info):
'''
:param online_user: 用户
:param cargo_info: [商品名,价格,数量]
:return:
'''
cargo_info[1] = int(cargo_info[1])
cargo_info[2] = int(cargo_info[2])
user_dic = db_handler.select(online_user)
if cargo_info[0] not in user_dic["shopping_cart"]:
user_dic["shopping_cart"][cargo_info[0]] = [cargo_info[1], cargo_info[2]]
else:
user_dic["shopping_cart"][cargo_info[0]][1] += cargo_info[2]
db_handler.save_data(user_dic)
return "商品[{}] [{}]个已成功加入购物车".format(cargo_info[0], cargo_info[2])
def check_cart_interface(online_user):
user_dic = db_handler.select(online_user)
shopping_cart = user_dic["shopping_cart"]
return shopping_cart
def close_account(online_user, sum_price):
res = bank_interface.charge(online_user, sum_price)
if res is False:
return False, "余额以及额度不足,无法结账"
else:
user_dic = db_handler.select(online_user)
user_dic["shopping_cart"] = {}
db_handler.save_data(user_dic)
return True, "结账成功,扣除{}元".format(sum_price)
3 数据处理层
主要为存储数据的提取,存储等
import os
import json
import hashlib
from conf import settings
def select(user):
user_file = os.path.join(settings.DB_USER_DATA_PATH, "{}.json".format(user))
if os.path.exists(user_file):
with open(user_file, "rb") as f:
user_dic = json.load(f)
return user_dic
def save_data(user_dic):
user = user_dic["user"]
user_file = os.path.join(settings.DB_USER_DATA_PATH, r"{}.json".format(user))
with open(user_file, "w",encoding="utf-8") as f:
json.dump(user_dic, f, ensure_ascii=False)
return True
def encrypt(password):
md5 = hashlib.md5()
md5.update(password.encode("utf-8"))
salt = "tank真帅"
md5.update(salt.encode("utf-8"))
res = md5.hexdigest()
return res
def get_cargo_list():
with open(settings.DB_CARGO_LIST_PATH,"rb") as f:
res = json.load(f)
return res
# def add_cargo():
# cargo = [["苹果",2],["mac",20000],["鸡",100],["臭豆腐",10]]
# with open(settings.DB_CARGO_LIST_PATH,"w",encoding="utf-8") as f:
# json.dump(cargo,f)
# add_cargo()
4 其他文件
4.1 配置文件
import os
BASE_PATH = os.path.dirname(os.path.dirname(__file__))
DB_PATH = os.path.join(BASE_PATH,"db")
DB_USER_DATA_PATH = os.path.join(DB_PATH,"user_data")
DB_CARGO_DATA_PATH = os.path.join(DB_PATH,"cargo_data")
DB_CARGO_LIST_PATH = os.path.join(DB_CARGO_DATA_PATH,"cargo_list.json")
LOG_PATH = os.path.join(BASE_PATH,"log")
LOG_LOG_PATH = os.path.join(LOG_PATH,"log.log")
standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \
'[%(levelname)s][%(message)s]'
simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
test_format = '%(asctime)s] %(message)s'
LOGGING_DIC = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'standard': {
'format': standard_format
},
'simple': {
'format': simple_format
},
'test': {
'format': test_format
},
},
'filters': {},
'handlers': {
#打印到终端的日志
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler', # 打印到屏幕
'formatter': 'simple'
},
#打印到文件的日志,收集info及以上的日志
'default': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,日志轮转
'formatter': 'standard',
# 可以定制日志文件路径
# BASE_DIR = os.path.dirname(os.path.abspath(__file__)) # log文件的目录
# LOG_PATH = os.path.join(BASE_DIR,'a1.log')
'filename': LOG_LOG_PATH, # 日志文件
'maxBytes': 1024*1024*50, # 日志大小 5M
'backupCount': 5,
'encoding': 'utf-8', # 日志文件的编码,再也不用担心中文log乱码了
},
'other': {
'level': 'DEBUG',
'class': 'logging.FileHandler', # 保存到文件
'formatter': 'test',
'filename': LOG_LOG_PATH,
'encoding': 'utf-8',
},
},
'loggers': {
#logging.getLogger(__name__)拿到的logger配置
'': {
'handlers': ['default', 'console'], # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
'level': 'DEBUG', # loggers(第一层日志级别关限制)--->handlers(第二层日志级别关卡限制)
'propagate': False, # 默认为True,向上(更高level的logger)传递,通常设置为False即可,否则会一份日志向上层层传递
},
'专门的采集': {
'handlers': ['other',],
'level': 'DEBUG',
'propagate': False,
},
},
}
4.2 通用文件
# 用户认证用装饰器
def identify(func):
def wrapper(*args,**kwargs):
from core.src import online_user
if online_user:
res = func(*args,**kwargs)
return res
else:
print("请先登录")
from core import src
src.login()
return wrapper
4.3 启动文件
from core import src
if __name__ == '__main__':
src.run()