项目目录搭建
1.bin
start.py # 存放启动脚本
2.conf
settings.py # 存放配置文件
3.lib
common # 存放公共功能
4.log # 存放项目日志
5.core
src.py # 展示层
6.interface # 核心逻辑层
shop_interface # 购物车接口
bank_interface # 银行接口
user_interface # 用户接口
admin_interface # 管理员接口
db
db_handlerl.py # 数据处理层
readme # 说明
框架搭建(src.py)
is_login= {'username':''} # 全局变量 保存登录信息
# 用户注册
def register():
pass
# 用户登录
def login():
pass
# 查看余额
def check_balance():
pass
# 余额提现
def Withdraw():
pass
# 账户充值
def pay():
pass
# 转账
def tranfer():
pass
# 查看流水
def check_flow():
pass
# 添加购物车
def add_shop_car():
pass
# 查看购物车
def check_shop_car():
pass
# 结算
def pay_shop_car():
pass
# 管理员
def admin():
pass
# 功能字典
func_dict = {
'1': register,
'2': login,
'3': look_balance,
'4': Withdraw,
'5': pay,
'6': tranfer,
'7': cheak_water,
'8': add_shop_car,
'9': view_shop_car,
'10': clear_shop_car,
'11': admin
}
while True:
print("""
1.用户注册
2.登录功能
3.查看余额
4.余额提现
5.账户充值
6.金额转账
7.查看流水
8.添加购物车
9.查看购物车
10.结算购物车
11.管理员功能
""")
choice = input('请输入功能编号>>>:').strip()
if choice in func_dict:
func_name = func_dict.get(choice)
func_name()
else:
print('好好输!')
启动文件(start.py文件代码)
import os
import sys
base_dir = os.path.dirname(os.path.dirname(__file__)) # 1.找到根目录
sys.path.append(base_dir) # 2.在将根目录添加至环境变量,不受用户和路径限制
if __name__ == '__main__':
from core import src
src.run() # 3.调用run()函数,开始展示功能
配置文件
import os
# 获取根目录路径,如果不存在则创建
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
DB_DIR = os.path.join(BASE_DIR, 'db')
if not os.path.exists(DB_DIR):
os.mkdir(DB_DIR)
MONETY_RATE = 0.05 # 定义手续费费率,因为不常修改所以放在配置文件大写
# 记录日志字典
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'
LOG_DIR = os.path.join(BASE_DIR, 'log')
if not os.path.isdir(LOG_DIR):
os.mkdir(LOG_DIR)
LOGFILE_PATH = os.path.join(LOG_DIR, 'ATM.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)传递
}, # 当键不存在的情况下 (key设为空字符串)默认都会使用该k:v配置
# '购物车记录': {
# 'handlers': ['default','console'], # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
# 'level': 'WARNING',
# 'propagate': True, # 向上(更高level的logger)传递
# }, # 当键不存在的情况下 (key设为空字符串)默认都会使用该k:v配置
},
}
公共文件(common文件代码)
"""
代码中加密,装饰器,判断数字是否符合要求,日志都是公共功能,因此放在common中
"""
import hashlib
import logging
import logging.config
from core import src
from conf import settings
def get_hash(user_pwd):
md5 = hashlib.md5()
md5.update(user_pwd.encode('utf8'))
return md5.hexdigest()
def login_auth(func):
def inner(*args, **kwargs):
if src.is_login.get('username'):
res = func(*args, **kwargs)
return res
else:
print('您尚未登陆,请先登陆')
src.login()
return inner
def get_num(target_money):
try:
target_money = float(target_money)
except:
return False, '请输入整数或小数'
else:
return True, target_money
def get_logger(msg):
logging.config.dictConfig(settings.LOGGING_DIC) # 自动加载字典中的配置
logger1 = logging.getLogger(msg)
return logger1
数据库操作相关代码(db_handler文件代码)
"""
此文件主要用于数据库文件的存取,存取需要多次用到,所以直接封成函数放在数据库操作文件中方便使用
"""
import os
import json
from conf import settings
def save(user_dict):
user_name = user_dict.get('username')
user_file_path = os.path.join(settings.DB_DIR, f'{user_name}.json')
with open(user_file_path, 'w', encoding='utf8') as f:
json.dump(user_dict, f, ensure_ascii=False)
def select(user_name):
user_file_path = os.path.join(settings.DB_DIR, f'{user_name}.json')
if os.path.exists(user_file_path):
with open(user_file_path, 'r', encoding='utf8') as f:
return json.load(f)
注册功能
"""
注册功能应首先在第一层获取用户输入,简单逻辑判断之后传给第二层,进行逻辑判断,涉及是否已注册功能及存取需要用到db_handler中的函数
"""
src代码:
def register():
user_name = input('请输入您的用户名>>>:').strip() # 1.获取用户输入
user_pwd = input('请输入您的密码>>>:').strip()
confirm_pwd = input('请再次输入您的密码>>>:').strip()
if not user_pwd == confirm_pwd: # 2.将用户两次输入的密码进行对比,如果不一致则退出运行,重新选择任务编号
print('两次密码输入不一致,请再次输入')
return
flag, msg = user_interface.register_interface(user_name, user_pwd) # 3.用两个变量名接收interface层注册函数的返回值
"""
由于本项目部分函数需要返回两个返回值,因此统一用两个变量名来接收函数的返回值,部分函数返回值可能用不上,只是为了统一格式
"""
print(msg)
user_interface代码:
def register_interface(user_name, user_pwd): # 1.定义注册接口函数
user_dict = db_handler.select(user_name) # 2.通过db_handler中的select()函数获取函数的返回值
if user_dict: # 3.如果有返回值,说明用户已存在,结束注册功能
return False, f'用户名{user_name}已注册'
user_pwd = common.get_hash(user_pwd) # 4.给传入的密码加密
user_dict = { # 5.构造字典
'username': user_name,
'password': user_pwd,
'balance': 15000,
'shop_car': {},
'is_lock': False,
'water_flow': []
}
db_handler.save(user_dict) # 6.调用db_handler中的save()函数来保存构造的字典
logger.info(f'用户{user_name}注册成功')
return True, f'用户{user_name}注册成功' # 7.返回返回值
登录功能
src代码:
def login():
user_name = input('请输入您的用户名>>>:').strip() # 1.获取用户输入
user_pwd = input('请输入您的密码>>>:').strip()
flag, msg = user_interface.login_inrterface(user_name, user_pwd)
if flag: # 3.用两个变量名接收interface层注册函数的返回值
is_login['username'] = user_name # 4.记录登录状态
print(msg)
interface代码:
def login_inrterface(user_name, user_pwd):
user_dict = db_handler.select(user_name) # 1.通过select()函数来返回用户字典或None
if not user_dict: # 2.逻辑判断:如果没有返回字典,说明用户未注册,将合适的返回值返回给用户
return False, f'用户{user_name}未注册'
user_pwd = common.get_hash(user_pwd) # 3.如果用户已注册,先获取加密后的密码
if user_pwd == user_dict.get('password'): # 4.将用户输入后的密码和存储的密码进行对比(都是加密后的密码)
logger.info(f'用户{user_name}登陆成功')
return True, '登陆成功'
return False, '密码错误' # 5.如果对比不成功返回布尔值和密码错误
查看余额
src代码:
@common.login_auth # 1.通过添加装饰器来限制用户执行该功能前必须先登陆
def check_balance():
user_name = is_login.get('username') # 2.因为用户登录成功时修改了登录状态,所以可以直接通过is_login拿到用户的用户名
flag, msg = bank_interface.check_balance_interface(user_name) # 3.用2个变量名来接收interface()函数的返回值
print(msg) # 4.打印返回的信息
interface代码:
def check_balance_interface(username):
user_dict = db_handler.select(username) # 1.通过传入的用户名从select()函数中通过返回值拿到用户字典
user_balance = user_dict.get('balance') # 2.获取到用户的余额
logger.debug(f'用户{username}查看了自己的账户余额')
return True, f'尊敬的{username},您的账户余额为{user_balance}' # 3.将布尔值True和账户余额返回给src中的msg
提现功能
src代码:
@common.login_auth
def withdraw():
user_name = is_login.get('username') # 1.通过is_login拿到用户的用户名
target_money = input('请输入您想要提现的金额>>>:').strip() # 2.获取用户提现金额
flag, msg = bank_interface.withdraw_interface(user_name, target_money) # 3.用2个变量名来接收interface()函数的返回值
print(msg)
interface代码:
def withdraw_interface(username, target_money):
flag, value = common.get_num(target_money) # 1.通过common中的get_num()函数来判断数字是否符合要求
if not flag: # 2.如果不符合将返回值返回给第一层,重新输入
return False, '请输入符合要求的金额'
user_dict = db_handler.select(username) # 3.通过select()获取用户字典,因为此时是从is_login获取到的用户名,所以字典肯定存在,无需判断
user_balance = user_dict.get('balance') # 4.获取用户余额
if user_balance >= value * (1 + settings.MONETY_RATE): # 5.判断用户余额是否大于提现金额加手续费,因为手续费不常修改,所以放在settings中大写
user_dict['balance'] -= value * (1 + settings.MONETY_RATE) # 6.修改用户字典中的余额,考虑手续费
ctime = time.strftime('%Y-%m-%d %H:%M:%S') # 7.记录时间,用于记录流水
user_dict['water_flow'].append(
f'{username}于{ctime}提现{value},手续费{value * settings.MONETY_RATE},账户余额{user_dict.get("balance")}')
db_handler.save(user_dict) # 8.添加流水
logger.debug(f'用户{username}提现{value},手续费{value * settings.MONETY_RATE},账户余额{user_dict.get("balance")}')
return True, f'尊敬的{username}成功提现{value},手续费{value * settings.MONETY_RATE},账户余额{user_dict.get("balance")}'
return False, f'尊敬的{username}, 您的账户余额不足,无法提现' # 9.通过逻辑判断返回不同的返回值
充值功能
src代码:
@common.login_auth
def pay_back():
user_name = is_login.get('username')
target_money = input('请输入您想充值的金额>>>:').strip()
flag, msg = bank_interface.pay_back_interface(user_name, target_money)
print(msg)
interface代码:
def pay_back_interface(user_name, target_money):
flag, value = common.get_num(target_money)
if not flag:
return False, '请输入符合要求的金额数字'
user_dict = db_handler.select(user_name)
user_dict['balance'] += value
ctime = time.strftime('%Y-%m-%d %H:%M:%S')
user_dict['water_flow'].append(f'{user_name}于{ctime}充值{value},账户余额{user_dict.get("balance")}')
db_handler.save(user_dict)
logger.debug(f'用户{user_name}充值{value},账户余额{user_dict.get("balance")}')
return True, f'尊敬的{user_name},您于{ctime}成功充值{value},账户余额{user_dict.get("balance")}'
转账功能
src代码:
@common.login_auth
def transfer():
user_name = is_login.get('username')
target_name = input('请输入您想转账的用户名>>>:').strip()
target_money = input('请输入您想转账的金额>>>:').strip()
flag, msg = bank_interface.transfer_interface(user_name, target_name, target_money)
print(msg)
interface代码:
def transfer_interface(user_name, target_name, target_money):
target_dict = db_handler.select(target_name)
if not target_dict:
return False, f'用户{target_name}未注册'
flag, value = common.get_num(target_money)
if not flag:
return False, '请输入符合要求的数字'
user_dict = db_handler.select(user_name)
if user_dict.get('balance') >= value:
user_dict['balance'] -= value
ctime = time.strftime('%Y-%m-%d %H:%M:%S')
user_dict['water_flow'].append(
f'尊敬的{user_name},您于{ctime}给{target_name}转账{value},账户余额{user_dict.get("balance")}')
target_dict['balance'] += value
target_dict['water_flow'].append(
f'尊敬的{target_name},{user_name}于{ctime}给您转账{value},账户余额{target_dict.get("balance")}')
db_handler.save(user_dict)
db_handler.save(target_dict)
logger.debug(
f'用户{user_name}给{target_name}转账{value},{user_name}账户余额{user_dict.get("balance")},{target_name}账户余额{target_dict.get("balance")}')
return True, f'尊敬的{user_name}, 您于{ctime}给{target_name}转账{value},账户余额{user_dict.get("balance")}'
return False, f'您的账户余额不足,无法转账'
查看流水
src代码:
@common.login_auth
def check_flow():
user_name = is_login.get('username')
flag, msg = bank_interface.check_flow_interface(user_name)
if flag:
for data in msg:
print(data)
else:
print(msg)
interface代码:
def check_flow_interface(username):
user_dict = db_handler.select(username)
if user_dict.get('water_flow'):
logger.debug(f'用户{username}查看了流水')
return True, user_dict.get('water_flow')
else:
logger.debug(f'用户{username}查看了流水')
return False, '您暂无流水'
添加购物车
"""
添加购物车主要功能可以写在第二层,第一层主药方用户交互功能
"""
src代码:
@common.login_auth
def add_shop_car():
user_name = is_login.get('username')
flag, msg = shop_interface.add_shop_car_interface(user_name)
print(msg)
interface代码:
def add_shop_car_interface(user_name):
tem_list = {}
while True:
good_list = [
['挂壁面', 3],
['印度飞饼', 22],
['极品木瓜', 666],
['土耳其土豆', 999],
['伊拉克拌面', 1000],
['董卓戏张飞公仔', 2000],
['仿真玩偶', 10000]
]
for i, j in enumerate(good_list, start=1):
print(f"""
商品编号:{i} | 商品名称:{j[0]} | 商品单价:{j[1]}
""")
choice = input('请输入您的商品编号(q)>>>:').strip()
if choice == 'q':
user_dict = db_handler.select(user_name)
shop_car = user_dict.get('shop_car')
for i in tem_list:
if i in shop_car:
shop_car.get(i)[0] += tem_list.get(i)[0]
else:
shop_car[i] = tem_list.get(i)
user_dict['shop_car'] = shop_car
db_handler.save(user_dict)
logger.info(f'用户{user_name}添加了购物车')
return True, f'用户{user_name}购物车添加完毕'
if not choice.isdigit():
print('商品编号必须是纯数字')
continue
choice = int(choice)
if not choice in range(1, len(good_list) + 1):
print('没有该商品')
continue
num = input(f'请输入您购买{good_list[choice - 1][0]}的数量>>>:')
if not num.isdigit():
print('商品数量必须是纯数字')
continue
num = int(num)
target_list = good_list[choice - 1]
target_good = target_list[0]
if target_good in tem_list:
tem_list.get(target_good)[0] += num
else:
tem_list[target_good] = [num, target_list[1]]
查看购物车功能
src.py代码:
@common.login_auth
def check_shop_car():
user_name = is_login.get('username')
flag, msg = shop_interface.check_shop_car_interface(user_name)
if flag:
for i in msg:
print(f"""
商品名称:{i} | 商品数量:{msg.get(i)[0]} | 商品单价:{msg.get(i)[1]}
""")
else:
print(msg)
interface代码:
def check_shop_car_interface(user_name):
user_dict = db_handler.select(user_name)
shop_car = user_dict.get('shop_car')
if shop_car:
logger.info(f'用户{user_name}查看了购物车')
return True, shop_car
else:
logger.info(f'用户{user_name}查看了购物车')
return False, '您的购物车为空'
结算购物车功能
src代码:
@common.login_auth
def pay_shop_car():
user_name = is_login.get('username')
flag, msg = shop_interface.pay_shop_car_interface(user_name)
print(msg)
interface代码:
def pay_shop_car_interface(user_name):
user_dict = db_handler.select(user_name)
shop_car = user_dict.get('shop_car') # {'极品木瓜': [9, 666], '伊拉克拌面': [5, 1000]}
current_balance = user_dict.get('balance')
money = 0
for i in shop_car.values(): # i:[9, 666]
money += i[0] * i[1]
if money > current_balance:
return False, f'您的账户余额不足,无法结算'
current_balance -= money
shop_car = {}
user_dict['balance'] = current_balance
user_dict['shop_car'] = {}
user_dict['water_flow'].append(f'{user_name},您本次消费{money},账户余额{user_dict.get("balance")}')
db_handler.save(user_dict)
logger.info(f'用户{user_name}结算了购物车,本次消费{money},账户余额{user_dict.get("balance")}')
return True, f'尊敬的{user_name},您本次消费{money},账户余额{user_dict.get("balance")}'