要求:
模拟实现一个ATM + 购物商城程序
1.额度 15000或自定义
2.实现购物商城,买东西加入 购物车,调用信用卡接口结账
3.可以提现,手续费5%
4.支持多账户登录
5.支持账户间转账
6.记录每月日常消费流水
7.提供还款接口
8.ATM记录操作日志
9.提供管理接口,包括添加账户、用户额度,冻结账户等。。。
10.用户认证用装饰器
示例代码 https://github.com/triaquae/py3_training/tree/master/atm
简易流程图:https://www.processon.com/view/link/589eb841e4b0999184934329
---------------------------------------------------------------
做项目的步骤:
1.分析功能,需求
账户数据的增删改查 ,提现 转账 还款 --> 加钱 减钱
日志
模块间的互相调用
功能的重用
2.画流程图 https://www.processon.com/
https://www.processon.com/view/link/589eb841e4b0999184934329
3.搭框架
---------------------------------------------------------------
注意事项:
1.一个账户一个文件(.json),防止多个用户同时读数据,修改文件后,会覆盖别人已经修改好的文件!
2.num_func = {'1': view_account, '2': withdraw, '3': pay_back, '4': transfer, '5': quit}
choice_num = input('num>>>:').strip()
num_func[choice_num](choice_num)
字典 value 的值可以放 (地址)
3.settings文件要重视,防止以后修改需求,可自定义配置
4. json.dump(account_info['account_data'], f)
f.close()
os.replace(path_tmp, path)
防止断电 .json 数据修改后,还未保存到硬盘上,所以一般写到 新文件中 再替换
5.注意写的 模块公用,模块内的参数 一定不能写成死的!
6.注意写的 函数公用,函数内的参数 一定要注意公用!
7.公共的 函数,不要涉及到与用户交互!
8.imoprt ***.py 引入模块时,引入模块得顺序:内置模块,第三方模块,自定义模块
引入模块,一般到函数级别from *** import (***,***),为了可维护,程序可读性。无论加载一个模块还是加载一个函数,函数所在得模块都会被加载到内存中!
9.load(open('**.py','r')) 和 with open('**.py','r') as f: 建议用后者 不需要关闭文件 python会自动关 但是f.close()是个好习惯 f.close()
f.flush() 会触发磁盘写操作 增加io压力 增加系统负载 但是 特殊情况下 可以这么做
10.变量在哪里使用就在哪里定义,因为函数传参是有一定代价的!
11.密码md5加盐salt
用户注册时:
用户输入【账号】和【密码】(以及其他用户信息);
系统为用户生成【Salt值】;
系统将【Salt值】和【用户密码】连接到一起;
对连接后的值进行散列,得到【Hash值】;
将【Hash值1】和【Salt值】分别放到数据库中。
用户登录时:
用户输入【账号】和【密码】;
系统通过用户名找到与之对应的【Hash值】和【Salt值】;
系统将【Salt值】和【用户输入的密码】连接到一起;
对连接后的值进行散列,得到【Hash值2】(注意是即时运算出来的值);
比较【Hash值1】和【Hash值2】是否相等,相等则表示密码正确,否则表示密码错误。
----------------------------------------------------------------------------
README:
1.管理员的账号:999 密码:123 2.普通账号:1000 / 1001 / 1002 密码:123
atm.py
1 # -*- coding:utf-8 -*- 2 import os 3 import sys 4 sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 5 6 from core import( 7 main, manager, shopper 8 ) 9 10 11 def main_run(): 12 main.run() 13 14 15 def manager_run(): 16 manager.run() 17 18 19 def shopper_run(): 20 shopper.run() 21 22 23 def quit_func(): 24 exit('bye bye ...') 25 26 27 def choice(): 28 choice_msg = ''' 29 -----start----- 30 1.普通账户 31 2.管理员 32 3.购物者 33 4.退出 34 ------end------ 35 ''' 36 dict_choice = {'1': main_run, '2': manager_run, '3': shopper_run, '4': quit_func} 37 while True: 38 print(choice_msg) 39 choice_num = input('num>>>:').strip() 40 if choice_num in dict_choice: 41 dict_choice[choice_num]() 42 else: 43 print('请输入正确的序号!') 44 45 46 if __name__ == '__main__': 47 choice()
settings.py
1 # -*- coding:utf-8 -*- 2 import os 3 import logging 4 import time 5 import datetime 6 7 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 8 9 DATABASE = { 10 'engine:': 'file_storage', 11 'path': os.path.join(BASE_DIR, 'db/accounts') 12 } 13 14 LOG_LEVEL = logging.INFO 15 LOG_FORMATTER = logging.Formatter(fmt='%(asctime)s - %(name)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %I:%M:%S %p') 16 17 TRANSACTION_TYPE = { 18 'withdraw': {'action': 'minus', 'interest': 0.05}, 19 'pay_back': {'action': 'plus', 'interest': 0}, 20 'transfer': {'action': 'minus', 'interest': 0.05}, 21 'consume': {'action': 'minus', 'interest': 0} 22 } 23 24 GOODS = [ 25 {"name": "电脑", "price": 1999}, 26 {"name": "鼠标", "price": 10}, 27 {"name": "游艇", "price": 20}, 28 {"name": "美女", "price": 998} 29 ] 30 31 MANAGER_ID = '999' 32 33 datetime_tmp = datetime.datetime.now().replace(year=2050, month=1, day=1) 34 DEFAULT_ACCOUNT = { 35 'id': 0, 36 'password': '', 37 'salt': '', 38 'balance': 15000, 39 'credit': 15000, 40 'enroll_date': time.strftime('%Y-%m-%d', time.localtime()), 41 'expire_date': '%s-%s-%s' % (datetime_tmp.year, datetime_tmp.month, datetime_tmp.day), 42 'pay_day': 22, 43 'status': 0 44 }
auth.py
1 # -*- coding:utf-8 -*- 2 import os 3 import json 4 from hashlib import md5 5 6 from conf.settings import DATABASE 7 8 9 def auth_login(func): 10 # 装饰器 11 def inner(*args, **kwargs): 12 if args[0]['is_auth']: 13 return func(*args, **kwargs) 14 else: 15 exit('您还未登录!') 16 return inner 17 18 19 def hash_password(password, salt): 20 # 用户输入的密码 + salt 得到hash值 21 hash_value = md5() 22 hash_value.update((password+salt).encode('utf-8')) 23 return hash_value.hexdigest() 24 25 26 def judge_account(account): 27 # 判断用户是否存在 存在返回json数据 28 path_json = DATABASE['path'] 29 file_path = os.path.join(path_json, account+'.json') 30 if os.path.isfile(file_path): 31 with open(file_path, 'r', encoding='utf-8') as f: 32 account_data = json.load(f) 33 f.close() 34 return account_data 35 36 37 def login(logger, *args): 38 # 判断账号密码是否正确 39 account_info = { 40 'is_auth': False, 41 'account': None, 42 'account_data': None 43 } 44 login_count = 0 45 while login_count < 3: 46 account = input('