python基础要打牢,ATM+购物车拿来练手不错,另外也可以了解开发软件的流程。总之感觉nice(▽)。
2020.12.31今天是2020年最后一天,意义非凡,ATM+购物车是我第一次写的比较正规庞大的项目……原谅我没做过课设,感觉其实用三层架构来写也没那么难。(不过我打算上周天和上周一写完的…执行力不够)且当跨年礼物吧…2021再继续哈哈哈哈哈。
模拟实现一个ATM + 购物商城程序
1. 额度 15000或自定义 ==》注册功能
2.实现购物商城,买东西加入 购物车,调用信用卡接口结账 ==》购物功能、支付功能
3.可以提现,手续费5% ==》提现功能
4.支持多账户登录 ==》登陆功能
5.支持账户间转账 ==》转账功能
6.记录日常消费流水 ==》记录流水功能
7.提供还款接口 ==》还款功能
8.ATM记录操作日志 ==》日志功能
9.提供管理接口,包括添加账户、用户额度,冻结账户等。。。==》管理功能
10.用户认证用装饰器==》登陆认证装饰器
"用户视图层" 展示给用户选择的功能
1、注册功能
2、登录功能
3、查看余额
4、提现功能
5、还款功能
6、转账功能
7、查看流水
8、购物功能
9、查看购物车
10、管理员功能
客户提出需求,公司拿到项目
——》公司出人去(一般一个前端一个后端)和客户讨论需求、商品需求功能的可实现性、项目价格、开发周期等,得到一个需求文档
——》公司内部开会得到一个开发文档交给不同岗位的程序员开发。
- Python: 后端,爬虫
- 不同的岗位:
- UI界面设计:
- 设计软件的布局,会分局软件的外观切成一张张图片。
- 前端:
- 拿到UI交给他的图片,然后去搭建网页面。
- 设计一些页面中,哪些位置需要接收数据,需要进行数据交互。
- 后端:
- 直接核心的业务逻辑,调度数据库进行数据的增删查改。
- 测试:
- 会给代码进行全面测试,比如压力测试,界面测试(CF卡箱子)。
- 运维:
- 部署项目。
1.程序设计的好处
1)思路清晰
2)不会出现写一半代码时推翻重写的情况
3)方便自己或以后的同事更好维护
2.三层架构设计的好处
1)把每个功能都分层三部分,逻辑清晰
2)如果用户更换不同的用户界面或不同,
的数据储存机制都不会影响接口层的核心
逻辑代码,扩展性强。
3)可以在接口层,准确的记录日志与流水。
3.三层架构
一 用户视图层
用于与用户交互的,可以接受用户的输入,打印接口返回的数据。
二 逻辑接口层
接受 用户视图层 传递过来的参数,根据逻辑判断调用数据层加以处理,并返回一个结果给 用户视图层。
三 数据处理层
接受接口层传递过来的参数,做数据的处理
- 保存数据
- 查看数据
- 更新数据
- 删除数据
conf:项目的配置信息
core:核心的代码
db:数据
interface:接口
lib:共用的一些功能
log:日志
readme:介绍项目的功能使用等
srart.py:项目的启动文件
core中src代码如下
# 1.注册功能
def register():
...
#2.登录功能
def login():
...
#3.查看余额
def check_banlance():
...
#4.提现功能
def withdraw():
...
#5.还款功能
def repay():
...
#6.转账功能
def transfer():
...
#7.查看流水
def check_flow():
...
#8.购物功能
def shopping():
...
#9.查看购物车
def check_shop_car():
...
#10.管理员功能
def admin():
...
fuc_dic = {
'1':register,
'2':login,
'3':check_banlance,
'4':withdraw,
'5':repay,
'6':transfer,
'7':check_flow,
'8':shopping,
'9':check_shop_car,
'10':admin
}
def run():
while True:
print('''
====== ATM+购物车======
1、注册功能
2、登录功能
3、查看余额
4、提现功能
5、还款功能
6、转账功能
7、查看流水
8、购物功能
9、查看购物车
10、管理员功能
====== THE END======
''')
#接受用户输入
choice = input('请输入命令编号:').strip()
#判断命令编号是否合法
if choice in fuc_dic:
fuc_dic[choice]()
else:
print('请输入合法的命令编号!')
start.py代码如下
import os
import sys
# 添加解释器的环境变量
sys.path.append(os.path.dirname(__file__))
from core import src
if __name__ == '__main__':
src.run()
核心逻辑分析
用户在视图层输入账号和密码,将账号和密码交给逻 辑 处理层,逻辑处理层调用数据处理层的功能来判断账号和密码是否存在。存在与否,则返回相应结果给逻辑处理层(接口层),若账号密码存在,则接口层直接返回,若不存在需要接口层需要完成注册功能,接口层需要做的是组织用户的信息,然后调用数据处理层的功能将用户信息写入相应json文件并返回,最终由接口层将是否注册成功返回给用户视图层。
def register():
while True:
# 1.用户输入用户名和密码进行校验
name = input('请输入用户名:').strip()
pwd = input('请输入密码:').strip()
re_pwd = input('请确认密码').strip()
# 2.小的逻辑判断
if pwd == re_pwd:
# 调用接口层来判断是否注册成功
res,tag= user_interface.register_interface(name,pwd)
if res:
print(tag)
break
else:
print(tag)
interface中的注册接口
def register_interface(username,password,balance = 15000):
# 1.调用数据处理层中的select函数会返回用户信息字典或者None
if db_handler.select(username):
return False,'用户已存在,请重新输入!'
else:
password = common.get_pwd_md5(password)
#1.组织用户信息,准备注册
user_dic = {
'username': username,
'password': password,
'balance': balance,
# 用于记录用户流水的列表
'flow': [],
# 用于记录用户购物车
'shop_car': {
},
# locked:用于记录用户是否被冻结
# False: 未冻结 True: 已被冻结
'locked': False
}
# 2.调用数据处理层的保存函数将用户信息写入文件
#密码加密
db_handler.save(user_dic)
return True, f'{username} 注册成功!'
数据处理层功能的select功能
def select(username):
# 1.接收用户名,拼接用户信息的完整json路径
user_path = os.path.join(
settings.BASE_PATH,f'{username}.json'
)
# 2.判断用户json文件是否存在
if os.path.exists(user_path):
with open(user_path,'r',encoding='utf-8') as f:
user_dic = json.load(f)
return user_dic
# 不return默认返回None
conf下的配置信息
import os
BASE_PATH = os.path.dirname(
os.path.dirname(__file__))
lib中的common.py密码加密功能
# 密码加密
def get_pwd_md5(pwd):
m = hashlib.md5(pwd.encode('utf-8'))
salt = 'wangxunzhidashuaibi'
m.updae(salt.encode('utf-8'))
return m.hexdegist()
核心逻辑分析
用户在视图层输入账号和密码交给登录接口层,接口层调用数据处理层的功能来判断用户是否登录成功,另外加了一个用户的登录状态变量login_user用于记录用户登录状态和一个用户登录认证装饰器。
def login():
while True:
name = input('请输入你的账号:').strip()
password = input('请输入你的密码:').strip()
# 登录接口:
#登录成功返回True,登录成功
res,tag = user_interface.login_interface(name,password)
if res:
global login_user
login_user = name
print(tag)
break
else:
print(tag)
interface中的登录接口
def login_interface(name,password):
# 调用数据处理层的select功能,来校验用户登录是否成功
# 返回user_dic 或者None
res = db_handler.select(name)
if res:
# 给用户输入的密码加密
password = common.get_pwd_md5(password)
if password == res.get('password'):
return True,'登录成功!'
else:
return False,'密码错误!'
return False,'用户不存在!请重新输入!'
用户登录认证
from core import src
def login_auth(func):
def outter(*args,**kwargs):
if src.login_user:
res = func(*args,**kwargs)
return res
else:
print('未登录,无法使用该功能!请登录!')
src.login()
return outter
核心逻辑分析
用户登录后(未登录会被强制登录),查看余额,直接调用查看余额接口,查看余额接口调用数据处理层中的功能,完成任务。
@common.login_auth
def check_banlance():
#直接调用查看余额接口
#返回余额
banlance = user_interface.check_balance_interface(login_user)
print(f'====用户{login_user}余额为{banlance}====')
interface查看余额接口
# 查看余额接口
def check_bal_interface(username):
user_dic = db_handler.select(username)
return user_dic['balance']
核心逻辑分析
用户在登录后(未登录会强制登录),在用户视图层输入提现金额,调用接口层中的提现接口,在提现接口中调用了数据处理层中的功能返回给接口层,接口层完成逻辑处理后将结果返回视图层。
记录流水和日志:要在接口层接入记录日志的功能。
原因:接口层是处理逻辑的核心所在,用户每次有提现/还款等功能都需要经过接口层。
注意:
转账要注意手续费的处理,以及用户输入的是否为数字等细节判断。
#4.提现功能
@common.login_auth
def withdraw():
while True:
# 1.让用户输入提现金额
money = input('请输入提现金额:').strip()
# 2.小逻辑判断:判断用户输入的是否为数字
if money.isdigit():
# 调用提现接口层
# 返回(bool,str)
flag,msg=bank_interface.withdraw_interface(login_user,money)
if flag:
print(msg)
break
else:
print(msg)
else:
print('您的输入不合法,请您输入数字,比如1220')
interface提现接口
def withdraw_interface(name,money):
#1.拿到用户信息
user_dic = db_handler.select(name)
#2.查看账户的余额
balance = user_dic.get('balance')
#判断余额是否大于提现金额和手续费
if balance >= int(money) * 1.05:
balance -= int(money) * 1.05
user_dic['balance'] = balance
#记录流水
flow =f'用户{name}提现{money}成功!' \
f'手续费为{int(money)*0.05},余额为{balance}'
user_dic['flow'].append(flow)
#保存或者更新用户数据
db_handler.save(user_dic)
#流水既输出在屏幕上,也保存在文件中
bank_logger.info(flow)
return True,flow
else:
return False,'提现失败!请重新输入金额!'
common.py中日志功能(在接口层记录日志)
def get_logger(log_type): # log_type ---> user
'''
:param log_type: 比如是 user日志,bank日志,购物商城日志
:return:
'''
# 1、加载日志配置信息
logging.config.dictConfig(
settings.LOGGING_DIC
)
# 2、获取日志对象
logger = logging.getLogger(log_type)
return logger
settings.py新增日志配置字典
"""
logging配置
"""
# 定义三种日志输出格式 开始
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'
# 定义日志输出格式 结束
# ****************注意1: log文件的目录
# BASE_PATH = os.path.dirname(os.path.dirname(__file__))
logfile_dir = os.path.join(BASE_PATH, 'log')
# print(logfile_dir)
# ****************注意2: log文件名
logfile_name = 'atm.log'
# 如果不存在定义的日志目录就创建一个
if not os.path.isdir(logfile_dir):
os.mkdir(logfile_dir)
# log文件的全路径
logfile_path = os.path.join(logfile_dir, logfile_name)
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)传递
},
},
}
interface中新增bank_log日志对象
#银行流水日志对象
bank_logger = common.get_logger('bank')
核心逻辑分析
用户在视图层输入还款金额,由接口层进行与数据处理层进行交互,完成功能。
@common.login_auth
def repay():
while True:
money = input('请输入你要还款的金额:').strip()
if money.isdigit():
if eval(money) >=0:
# 接入还款接口
flag,msg = bank_interface.repay_interface(login_user,money)
if flag:
print(msg)
break
else:
print('金额不能小于0!请重新输入!')
else:
print('必须输入数字!请重新输入!')
interface还款接口
def repay_interface(name,money):
#1.得到用户信息字典
user_dic = db_handler.select(name)
#2.将还款金额加入用户余额
user_dic['balance'] += eval(money)
#3.记录流水
flow = f'====用户{name}还款{money}元成功,' \
f'现有{user_dic["balance"]}元===='
bank_logger.info(flow)
#4.保存或者更新用户信息
db_handler.save(user_dic)
return True,flow
核心逻辑分析
用户输入要转入的账户和金额,传入转账接口,转账接口利用数据处理层的select和save完成功能。
def transfer():
while True:
#1.接收转账对象及金额
name = input('请输入你要转账对象:').strip()
money = input('请输入你要转账金额:').strip()
#2.判断用户输入的金额是否为数字以及是否>0
if money.isdigit():
if eval(money) > 0:
flag,msg=bank_interface.transfer_interface(name,money)
if flag:
print(msg)
break
else:
print(msg)
else:
print('转账金额必须大于0!')
else:
print('必须输入数字!')
转账接口
def transfer_interface(name,money):
#1.判断被转账用户是否存在
#接入数据处理层的查找功能,返回user_dic 或者None
res = db_handler.select(name)
if not res:
return False,'目标用户不存在!'
#2.获取当前用户数据
user_dic = db_handler.select(src.login_user)
#3.判断当前用户的余额是否够转账
money = eval(money)
if user_dic.get('balance') > money:
user_dic['balance'] -= money
res['balance'] += money
else:
return False,'用户的钱不够转账!'
#4.记录流水
from_log_flow = f'用户给用户{name}转账{money}元成功!'
user_dic['flow'].append(from_log_flow)
to_user_flow = f'用户接受用户{src.login_user}转账{money}元成功!'
res['flow'].append(to_user_flow)
bank_logger.info(f'用户{src.login_user}给用户{name}转账{money}元成功!')
#5.更新用户数据
db_handler.save(user_dic)
db_handler.save(res)
return True,from_log_flow
核心逻辑分析
用户在视图层选择查看流水功能,调用查看流水接口,流水接口调用数据处理层的查看功能完成功能。
#7.查看流水
@common.login_auth
def check_flow():
#接入查看流水接口
res = bank_interface.check_flow_interface(login_user)
if res:
n = 1
for i in res:
print(f'{n}:{i}')
n+=1
else:
print('用户{name}无流水记录!')
查看流水接口
def check_flow_interface(login_user):
user_dic = db_handler.select(login_user)
return user_dic.get('flow')
核心逻辑分析
用户在视图层选择购物后==》
看到商品信息==》
选择购物/支付/加入购物车==》
1.购物:需要对用户输入的指令进行判定
2.支付:计算当前购物车总花费,接入支付接口,完成支付
3.加入购物车:读取之前的购物车,若存在加入重复的商品 名,则数量增加,否则新增购物,最后更新购物车
#8.购物功能
@common.login_auth
def shopping():
# 1.打印商品信息
shop_list = []
print('============欢迎来到有趣用品商城============')
with open(r'F:\ATM+购物车\db\shoppings.txt',encoding='utf-8') as f:
n = 1 # 记录商品编号
for line in f:
shop_name,shop_price = line.strip().split(':')
shop_price = eval(shop_price)
shop_list.append((shop_name,shop_price))
print(f'商品编号:{n},'
f'商品名称:{shop_name},'
f'商品单价:{shop_price}')
n+=1
print('================24小时服务================')
# 2.初始化购物车
shopping_car = {
}
#3.模拟用户购物
while True:
choice = input('请输入商品编号(若结账输入y 添加购物车输入n):').strip()
#3.1选择结账
if choice == 'y':
# 判断购物车是否为空 ==》
#空==》不能支付
#否则==》调用支付接口
if not shopping_car:
print('购物车为空,无法支付!')
else:
#调用商品结算接口
flag,msg=shop_interface.shopping_interface(shopping_car,login_user)
if flag:
print(msg)
break
else:
print(msg)
elif choice == 'n':
#判断购物车内是否为空
# 空==》提示用户选择商品
# 非空 ==》代表用户选择了商品加入了购物车
if not shopping_car:
print('当前购物车为空,请在该购物车内添加商品!')
else:
#接入添加购物车接口
res = shop_interface.add_shop_car(login_user,shopping_car)
if res:
print('添加购物车成功!')
break
else:
print('添加购物车失败!')
else:
#模拟用户选择商品
#判断用户输入的编号是否合法
if not choice.isdigit():
print('请输入数字编号!')
elif not type(eval(choice)) is int:
print('请输入整数编号!')
elif eval(choice) > n or eval(choice) < 1:
print('请输入1-{n}内的编号!')
else:
#获取用户所选商品的名字和单价
shop_name,shop_price = shop_list[eval(choice)-1]
#采用{'name':[price,number]}形式组织购物车
if shop_name in shopping_car:
shopping_car[shop_name][1]+=1
else:
shopping_car[shop_name] = [shop_price,1]
print(f'当前购物车{shopping_car}')
商品结算接口和添加购物车接口
#商品结算接口
def shopping_interface(shopping_car,name):
#1.计算用户商品总花销
cost= 0
# 采用{'name':[price,number]}形式组织购物车
for i in shopping_car.values():
price,number = i
cost+=price*number
#2.支付
#调用银行支付接口
flag,msg=bank_interface.pay_interface(name,cost)
return flag,msg
#添加购物车接口
def add_shop_car(name,shopping_car):
# 获取用户购物车
use_dic = db_handler.select(name)
user_car = use_dic['shop_car']
#添加购物车
for shop_name,shop_price_num in shopping_car.items():
shop_price,shop_num = shop_price_num
#判断商品在不在购物车里面,若在则数量增加
#否则将商品添加到购物车里
#购物车组织{商品名:[单价,数量]}
if shop_name in user_car:
use_dic['shop_car'][shop_name][1] += shop_num
else:
use_dic['shop_car'][shop_name] = [shop_price,shop_num]
#等同于use_dic['shop_car'].update({shop_name:[shop_price,shop_num]})
#更新购物车数据
db_handler.save(use_dic)
return True
支付接口
# 支付接口
def pay_interface(login_user, cost):
user_dic = db_handler.select(login_user)
# 判断用户金额是否足够
if user_dic.get('balance') >= cost:
# 减钱
user_dic['balance'] -= cost
# 记录消费流水
flow = f'用户消费金额: [{cost}$]'
user_dic['flow'].append(flow)
# 保存数据
db_handler.save(user_dic)
# return True 或 return False 交给购物接口来做处理
return True
return False
核心逻辑分析
和查看余额一样,直接调用接口层,利用数据处理层查看功能完成任务。
#9.查看购物车
@common.login_auth
def check_shop_car():
#调用查看购物车接口
res = shop_interface.check_spc(login_user)
if res:
for i in res:
print(i)
else:
print('用户购物车内没东西!')
查看购物车接口
def check_spc(name):
user_dic = db_handler.select(name)
shop_car = user_dic['shop_car']
return shop_car
核心逻辑分析
有点像写一个小的ATM+购物车,只不过功能不一样。
管理员需要有的功能分析如下:
1.添加用户
2.修改用户额度
3.冻结用户
与写整个项目不同,管理员功能相对来说比较独立,我们在写这个功能的时候,同样采取三层架构。
即管理员视图层=》管理员接口层=》管理员数据处理层,但这里处理的是用户的数据,其实也是用户数据处理层。
#添加用户
def add_user():
...
#修改用户额度
def change_balance():
...
#冻结用户
def lock_user():
...
#管理员功能字典
admin_dic={
}
#管理员视图层
def admin_run():
print('=====欢迎进入管理员功能区=====')
#管理员功能字典
admin_dic={
'0':['退出',None],
'1':['添加用户',add_user],
'2':['修改用户额度',change_balance],
'3':['冻结用户',lock_user]
}
#管理员视图层
def admin_run():
print('=====欢迎进入管理员功能区=====')
#展示管理员功能
while True:
for k,v in admin_dic:
print(f'{k}:{v[0]}')
#管理员选择功能
choice = input('请输入您想要使用的管理员功能').strip()
#判断用户输入的合法性
if choice not in admin_dic:
print('请您输入合法的数字!')
else:
admin_dic[choice][1]()
1、添加用户功能
#添加用户
def add_user():
#相当于注册一个用户
src.register()
2、修改用户额度功能
#修改用户额度
def change_balance():
#获取要修改对象的账号和所要修改的额度
while True:
name = input('请输入要修改的账号:').strip()
balance = input('请输入要修改的额度:').strip()
#判断输入额度的合法性
if not balance.isdigit():
print('输入不合法!')
else:
#修改用户的额度,调用修改用户额度接口
#修改成功返回 True和日志
flag,msg = admin_interface.change_balance(name,balance)
if flag:
print(msg)
break
print(msg)
修改用户额度接口
#修改用户额度
def change_balance(name,money):
#获取用户的信息字典
#接入数据处理层查看数据:返回用户信息字典或者None
user_dic = db_handler.select(name)
if user_dic:
#修改额度
user_dic['balance'] = eval(money)
#记录流水
flow = f'管理员{src.login_user}修改{name}用户额度为{money}成功!'
admin_logger.info(flow)
#保存数据
db_handler.save(user_dic)
return True,flow
return False,'用户不存在!'
3、冻结用户功能
#冻结用户
def lock_user():
while True:
name = input('请您输入要冻结的账号名').strip()
#调用冻结用户接口
flag,msg = admin_interface.lock_user_interface(name)
if flag:
print(msg)
break
print(msg)
冻结用户接口
#冻结用户
def lock_user_interface(name):
#拿到用户信息字典
user_dic = db_handler.select(name)
if user_dic:
#locked:用于记录用户是否被冻结
# False: 未冻结 True: 已被冻结
user_dic['locked'] = 'True'
#记录日志
msg =f'用户{name}被冻结!'
admin_logger.info(msg)
#保存数据
db_handler.save(user_dic)
return True,msg
return False,'用户不存在!'
ps:ATM+购物车项目压缩包私我
ATM+购物车