本文共 18667 字,大约阅读时间需要 62 分钟。
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 osimport sys# 添加解释器的环境变量sys.path.append(os.path.dirname(__file__))from core import srcif __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 osBASE_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 srcdef 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_authdef 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_authdef 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_authdef 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_authdef 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_authdef 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_authdef 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+购物车
转载地址:http://afxki.baihongyu.com/