ATM+购物车
目录
1 需求
1.额度 15000或自定义 --> 注册功能
2.实现购物商城,买东西加入 购物车,调用信用卡接口结账 --> 购物功能、支付功能
3.可以提现,手续费5% --> 提现功能
4.支持多账户登录 --> 登录功能
5.支持账户间转账 --> 转账功能
6.记录每月日常消费流水 --> 记录流水功能
7.提供还款接口 --> 还款功能
8.ATM记录操作日志 --> 记录日志功能
9.提供管理接口,包括添加账户、用户额度,冻结账户等 -->管理员功能
10.用户认证用装饰器 --> 登录认证装饰器
2 功能
1.注册功能
2.登录功能
3.查看余额
4.提现功能
5.还款功能
6.转账功能
7.查看流水
8.购物功能
9.查看购物车
10.管理员功能
3 目录结构
ATM
-bin
-start.py
-conf
-settings.py
-db
-user_data
-ccc.py
...
-db_handler.py
-interface
-bank.py
-user.py
-shopping.py
-lib
-common.py
-log
-atm.log
-view
-src.py
4 思路及步骤
4.1 环境bin/start.py
程序的入口
4.1.1 将解释器添加到环境变量中
import os
import sys
sys.path.append(os.path.dirname(os.path.dirname(__file__)))
4.1.2 启动主程序
from view import src
if __name__ == '__main__':
src.run()
# 此时应该在view/src.py中定义主启动函数run()
4.2 配置conf/settings.py
存放配置信息
4.2.1 获取项目根目录路径
import os
BASE_PATH = os.path.dirname(os.path.dirname(__file__))
4.2.2 获取user_data目录路径
import os
USER_DATA_PATH = os.path.join(BASE_PATH, 'db', 'user_data')
4.2.3 获取log目录路径
import os
USER_LOG = os.path.join(BASE_PATH, 'log')
4.3 数据处理db/db_handle.py
数据处理层专门用来处理数据
4.3.1 保存用户信息save
import os
import json
from conf import settings
def save(username, user_dic):
user_path = os.path.join(settings.USER_DATA_PATH, f'{username}.json')
with open(user_path, 'wt', encoding='utf-8') as f:
json.dump(user_dic, f, ensure_ascii=False)
f.flush() # 强制刷入硬盘
4.3.2 返回用户信息select
import os
import json
from conf import settings
def select(username):
user_path = os.path.join(settings.USER_DATA_PATH, f'{username}.json')
if os.path.exists(user_path):
with open(user_path, 'rt', encoding='utf-8') as f:
user_dic = json.load(f)
return user_dic
4.4 视图层view/src
用户视图层
4.4.1 主启动函数run()
func_dic = {
"1": register,
"2": login,
"3": check_balance,
"4": withdrawal,
"5": repay,
"6": transfer,
"7": check_flow,
"8": shop,
"9": check_shopping_cart,
"10": admin,
"11": logout,
}
def run():
while True:
print(
=======================
1.注册
2.登录
3.查看余额
4.提现
5.还款
6.转账
7.查看流水
8.购物
9.查看购物车
10.管理员
11.注销
=======================
)
choice = input("请输入功能编号(按q退出程序):").strip()
if choice == "q":break
if choice not in func_dic:
print("请输入正确的编号!")
continue
func_dic.get(choice)() # 输入1相当于func_dic.get('1')()-->register()
4.4.2 功能实现
4.4.2.1 注册功能register()
from interface import user
user_login = {
'is_login': False,
'username': None
}
def register():
while True:
if not user_login['is_login']:
username = input("请输入注册用户名(按q退出):").strip()
if username == "q":break
password = input("请输入密码:").strip()
re_password = input("请再次输入密码:").strip()
if password == re_password:
msg = user.register_interface(username, password)
print(msg)
break
else:
print("两次密码不一致")
else:
print("登陆状态不可再注册")
break
4.4.2.2 登录功能login()
from interface import user
user_login = {
'is_login': False,
'username': None
}
def login():
count = 0
while True:
if not user_login['is_login']:
username = input("请输入登录名(按q退出):").strip()
if username == "q":break
user_info = user.get_user_info_interface(username)
if user_info:
if not user_info['locked']:
if count < 3:
password = input("请输入登录密码:").strip()
if password == user_info['password']:
user_login['is_login'] = True
user_login['username'] = username
print("登陆成功")
break
else:
count += 1
print("密码输入错误")
else:
user.locked_interface(username)
print("该用户密码错误输入次数过多,已被锁定")
break
else:
msg = user.unlocked_interface(username)
print(msg)
else:
print("该用户不存在")
else:
print("登录状态下不可再次登录")
break
1、判断用户登录状态,非登录状态继续执行代码,否则告诉用户登录情况下不能再次登录
2、用户输入登录名,判断输入是否为q,非q继续执行代码,否则退出
3、判断用户名是否存在,不存在继续执行代码,存在则告诉用户该用户名不存在
4、判断该用户是否被锁,没有则继续执行,被锁则调用解锁接口 等待解锁
5、判断密码输入次数,小于三次则继续执行,否则调用锁定接口 锁定账号
6、用户输入密码,判断密码是否正确,正确则登录,错误则输入次数加1
4.4.2.3 查看余额功能check_balance()
# 首先使用登录认证装饰器判断登陆状态
from lib import common
from interface import bank
user_login = {
'is_login': False,
'username': None
}
@common.check_login
def check_balance():
msg = bank.check_balance_interface(user_login['username'])
print("%s的余额是%s元" % (user_login['username'], msg))
4.4.2.4 提现功能withdrawal()
from lib import common
from interface import bank
user_login = {
'is_login': False,
'username': None
}
@common.check_login
def withdrawal():
while True:
print("""
=============================
0) 退出
1) 查看余额
2) 提现
=============================
""")
choice = input("请输入指令:").strip()
if choice == "0":break
elif choice == "1":
msg = bank.check_balance_interface(user_login['username'])
print(msg)
elif choice == "2":
money = input("请输入提现金额:").strip()
if money.isdigit:
money = int(money)
msg = bank.withdrawal_interface(user_login['username'], money)
print(msg)
break
else:
print("请输入正确的金额")
else:
print("请输入正确的提现指令")
1、装饰器判断登录状态,未登录则执行登陆程序,登陆则继续
2、用户选择功能,0->退出 1->查看余额 2->提现(循环功能)
3、0直接退出 1调用余额接口
4、2让用户输入提现金额,判断是否为数字,不为数字提示重新输入,为数字继续
5、调用提现接口,打印返回值
4.4.2.5 还款功能repay()
from lib import common
from interface import bank
user_login = {
'is_login': False,
'username': None
}
@common.check_login
def repay():
while True:
choice = input("""
=============================
0) 退出
1) 查询欠款金额
2) 还款
=============================
请输入选项:""").strip()
if choice == "0":break
elif choice == "1":
msg = bank.check_debt_interface(user_login['username'])
print(msg)
elif choice == "2":
money = input("请输入还款金额:").strip()
if money.isdigit():
money = int(money)
msg = bank.repay_interface(user_login['username'], money)
print(msg)
break
else:
print("请输入正确的金额")
else:
print("请输入正确的选项")
1、调用登录认证装饰器
2、用户选择还款功能 0退出 1查询欠款金额 2还款
3、1调用查询欠款接口查询
4、2用户输入还款金额,判断是否为数字
5、调用还款接口
4.4.2.6 转账功能transfer()
from lib import common
from interface import bank
user_login = {
'is_login': False,
'username': None
}
@common.check_login
def transfer():
while True:
print("""
=============================
0) 退出
1) 查看余额
2) 转账
=============================
""")
choice = input("请输入功能选项:").strip()
if choice == "0":break
elif choice == "1":
msg = bank.check_balance_interface(user_login['username'])
print(msg)
elif choice == "2":
to_username = input("请输入被转帐用户名:").strip()
if to_username == user_login['username']:
print("不能转账给自己")
continue
if user.get_user_info_interface(to_username):
money = input("请输入转账金额:").strip()
if money.isdigit():
money = int(money)
msg = bank.transfer_interface(user_login['username'], to_username, money)
print(msg)
break
else:
print("请输入正确的金额数")
else:
print("被转账用户不存在")
else:
print("请输入正确的选项")
1、调用登录认证装饰器
2、用户选择功能 0退出 1调用查看余额接口查看余额 2转账
3、2输入被转账用户名,判断是否为自己
4、调用提取信息接口判断用户是否存在,存在则继续,否则提示用户不存在
5、用户输入金额,判断是否为数字,是继续,否则提示输入正确的金额
6、调用转账接口
4.4.2.7 查看流水功能check_flow()
from lib import common
from interface import bank
user_login = {
'is_login': False,
'username': None
}
@common.check_login
def check_flow():
flow_list = bank.check_flow_interface(user_login['username'])
for line in flow_list:
print(line)
4.4.2.8 购物功能shop()
from lib import common
from interface import shopping
user_login = {
'is_login': False,
'username': None
}
@common.check_login
def shop():
good_list = [
['tesla', 10000],
['mac', 8000],
['thinkpad', 6000],
['火锅', 580],
['烧烤', 320],
['串串', 280],
['包子', 8],
['矿泉水',5]
]
good_cart = {}
balance = bank.check_balance_interface(user_login['username'])
amount_spend = 0
while True:
for i,line in enumerate(good_list):
print("%s 商品名:%s 单价:%s" % (i, line[0], line[1]))
choice = input("请输入商品序号(按q退出并购买):").strip()
if choice.isdigit():
choice = int(choice)
if 0 <= choice <= len(good_list)-1:
good_name = good_list[choice][0]
good_price = good_list[choice][1]
if balance >= good_price:
if good_name in good_cart:
good_cart[good_name]['count'] += 1
else:
good_cart[good_name] = {'price': good_price, 'count': 1}
amount_spend += good_price
balance -= good_price
print("%s已被加入购物车" % good_name)
print("目前购物车有:%s" % good_list)
print("余额:%s元" % balance)
else:
print("余额不足,剩余%s" % balance)
else:
print("该商品尚未上架")
elif choice == "q":
while True:
choice = input("请输入 y)结算 或 n)退出:").strip()
if choice == "y":
if amount_spend > 0:
password = input("请输入密码:").strip()
file_password = user.get_user_info_interface(user_login['username'])['password']
if password == file_password:
msg = shopping.buy_goods_interface(user_login['username'], good_cart, amount_spend)
print(msg)
break
else:
print("密码错误")
else:
print("没买东西结什么账啊")
break
elif choice == "n":
print("白加这么多东西在购物车")
break
else:
print("请输入正确的选项")
break
else:
print("请输入数字")
4.4.2.9 查看购物车功能check_shopping_cart()
from lib import common
from interface import shopping
user_login = {
'is_login': False,
'username': None
}
@common.check_login
def check_shopping_cart():
msg = shopping.check_shopping_cart_interface(user_login['username'])
print(msg)
4.4.2.10 管理员功能admin()
def admin():
pass
4.4.2.11 注销功能logout()
from lib import common
user_login = {
'is_login': False,
'username': None
}
@common.check_login
def logout():
if user_login['is_login']:
user_login['is_login'] = False
print("注销成功")
else:
print("请先登录")
4.5 公共lib/common
存放公共登陆方法,登录认证装饰器及日志模块
4.5.1 登陆认证装饰器
from view import src
# 登录认证装饰器
def check_login(func):
def inner(*args, **kwargs):
if src.user_login['is_login']:
res = func(*args, **kwargs)
return res
else:
print("暂未登录,请先登录后在使用本功能")
src.login()
return inner
4.5.2 日志
import os
import logging.config
from conf import settings
# 日志模块
standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]'
'[%(levelname)s][%(message)s]' # 其中name为getlogger指定的名字
simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s'
# 定义日志输出格式 结束
logfile_dir = settings.USER_LOG # log文件的目录
logfile_name = 'atm.log' # log文件名
# 如果不存在定义的日志目录就创建一个
if not os.path.isdir(logfile_dir):
os.mkdir(logfile_dir)
# log文件的全路径
logfile_path = os.path.join(logfile_dir, logfile_name)
# log配置字典
LOGGING_DIC = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'standard': {
'format': standard_format
},
'simple': {
'format': simple_format
},
},
'filters': {},
'handlers': {
# 打印到终端的日志
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler', # 打印到屏幕
'formatter': 'simple'
},
# 打印到文件的日志,收集info及以上的日志
'default': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler', # 保存到文件
'formatter': 'standard',
'filename': logfile_path, # 日志文件
'maxBytes': 1024*1024*5, # 日志大小 5M
'backupCount': 5,
'encoding': 'utf-8', # 日志文件的编码,再也不用担心中文log乱码了
},
},
'loggers': {
# logging.getLogger(__name__)拿到的logger配置
'': {
'handlers': ['default', 'console'], # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
'level': 'DEBUG',
'propagate': True, # 向上(更高level的logger)传递
},
},
}
def make_log(name):
logging.config.dictConfig(LOGGING_DIC) # 导入上面定义的logging配置
logger = logging.getLogger(name) # 生成一个log实例
return logger
4.6 接口interface
4.6.1 银行相关bank.py
4.6.1.1 查看余额接口
from db import db_handler
from lib import common
logger_bank = common.make_log('bank')
# 查看余额接口
def check_balance_interface(username):
user_info = db_handler.select(username)
res = user_info['balance']
# 记录日志
logger_bank.info("%s查看了余额" % username)
return res
4.6.1.2 提现接口
from db import db_handler
from lib import common
logger_bank = common.make_log('bank')
# 提现接口
def withdrawal_interface(username, money):
user_info = db_handler.select(username)
if user_info['balance'] >= money*1.05:
user_info['balance'] -= money*1.05
# 查看流水
user_info['flow'].append("%s成功提现%s元,扣除手续费%s元" % (username, money, money*0.05))
db_handler.save(username, user_info)
# 记录日志
logger_bank.info("%s成功提现%s元,扣除手续费%s元" % (username, money, money*0.05))
return "%s成功提现%s元,扣除手续费%s元" % (username, money, money*0.05)
else:
return "%s账户余额不足" % username
4.6.1.3 查询欠款接口
from db import db_handler
from lib import common
logger_bank = common.make_log('bank')
# 查询欠款接口
def check_debt_interface(username):
user_info = db_handler.select(username)
res = user_info['credit'] - user_info['balance']
if res > 0:
# 记录日志
logger_bank.info("%s查看了欠款" % username)
return "%s当前欠款%s元" % (username, res)
else:
return "%s当前并未欠款" % username
4.6.1.4 还款接口
from db import db_handler
from lib import common
logger_bank = common.make_log('bank')
# 还款接口
def repay_interface(username, money):
user_info = db_handler.select(username)
res = user_info['credit'] - user_info['balance']
if res >= money:
user_info['balance'] += money
# 记录流水
user_info['flow'].append("%s成功还款%s元" % (username, money))
db_handler.save(username, user_info)
# 记录日志
logger_bank.info("%s成功还款%s元" % (username, money))
return "%s成功还款%s元" % (username, money)
else:
return "还款金额超过欠款额度"
4.6.1.5 转账接口
from db import db_handler
from lib import common
logger_bank = common.make_log('bank')
# 转账功能接口
def transfer_interface(username, to_username, money):
user_info = db_handler.select(username)
to_user_info = db_handler.select(to_username)
if user_info['balance'] >= money:
user_info['balance'] -= money
to_user_info['balance'] += money
# 记录流水
user_info['flow'].append("您向%s转账%s元" % (to_username, money))
to_user_info['flow'].append("%s向您转账%s元" % (username, money))
db_handler.save(username, user_info)
db_handler.save(to_username, to_user_info)
# 记录日志
logger_bank.info("%s向%s转账%s元" % (username, to_username, money))
return "%s向%s转账%s元" % (username, to_username, money)
else:
return "%s账户余额不足" % username
4.6.1.6 查看流水接口
from db import db_handler
from lib import common
logger_bank = common.make_log('bank')
# 查看流水接口
def check_flow_interface(username):
user_info = db_handler.select(username)
# 查看流水
logger_bank.info("%s查看了流水" % username)
return user_info['flow']
4.6.1.7 消费接口
from db import db_handle
from lib import common
logger_bank = common.make_log('bank')
def consume_interface(username, money):
user_info = db_handler.select(username)
user_info['balance'] -= money
# 记录流水
user_info['flow'].append("%s消费了%s元" % (username, money))
db_handler.save(username, user_info)
# 记录日志
logger_bank.info("%s消费了%s元" % (username, money))
4.6.2 购物相关shopping.py
4.6.2.1 购买商品接口
from db import db_handler
from interface import bank
from lib import common
logger_shopping = common.make_log('shopping')
def buy_goods_interface(username, good_cart, money):
user_info = db_handler.select(username)
bank.consume_interface(username, money)
user_info['shopping_cart'] = good_cart
db_handler.save(username, user_info)
# 记录日志
logger_shopping.info("%s成功购买%s" % (username, good_cart))
return "%s成功购买%s" % (username, good_cart)
4.6.2.2 查看购物车接口
from db import db_handler
from lib import common
logger_shopping = common.make_log('shopping')
def check_shopping_cart_interface(username):
user_info = db_handler.select(username)
res = user_info['shopping_cart']
# 记录日志
logger_shopping.info("%s查看了购物车" % username)
return res
4.6.3 用户相关user.py
4.6.3.1 注册接口
from db import db_handler
from lib import common
logger_user = common.make_log('user')
# 注册接口
def register_interface(username, password, balance=15000):
user = db_handler.select(username) # 取到user_info = user_dic
if user: # 判断该用户名是否存在
return "该用户名已存在"
else:
user_dic = {
'username': username,
'password': password,
'credit': balance,
'balance': balance,
'locked': False,
'time': 0,
'flow': [],
'shopping_cart': {}
}
db_handler.save(username, user_dic)
# 记录日志
logger_user.info("%s注册成功" % username)
return "%s注册成功" % username
4.6.3.2 提取用户信息接口
from db import db_handler
# 提取用户信息
def get_user_info_interface(username):
user_info = db_handler.select(username)
return user_info
4.6.3.3 锁定用户接口
import time
from db import db_handler
from lib import common
logger_user = common.make_log('user')
# 锁定用户
def locked_interface(username):
user_info = db_handler.select(username)
user_info['locked'] = True
user_info['time'] = time.time()+20
db_handler.save(username, user_info)
# 记录日志
logger_user.info("%s被锁定了" % username)
4.6.3.4 解锁用户接口
import time
from db import db_handler
from lib import common
logger_user = common.make_log('user')
# 解锁用户接口
def unlocked_interface(username):
user_info = db_handler.select(username)
last_time = user_info['time'] - time.time()
if user_info['time'] <= time.time():
user_info['locked'] = False
db_handler.save(username, user_info)
# 记录日志
logger_user.info("%s用户已解锁" % username)
return "%s用户已经解锁,请重新登陆" % username
else:
return "%s用户已被锁,剩余锁定时间%s" % (username, last_time)