2.2 - ATM+购物商城程序
要求:
模拟实现一个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
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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('\033[1;32maccount>>>:\033[0m').strip() 47 password = input('\033[1;32mpassword>>>:\033[0m').strip() 48 account_data = judge_account(account) 49 if account_data: 50 if account_data.get('password') == hash_password(password, account_data['salt']): 51 # print('\033[1;33m登录成功!\033[0m'.center(25, '-')) 52 account_info['is_auth'] = True 53 account_info['account'] = account 54 account_info['account_data'] = account_data 55 if account_data['status'] == 1: # 表示账号 被冻结 56 print('\033[1;31m账号:%s 被冻结!\033[0m'.center(30, '-') % account) 57 else: 58 if len(args) != 0: # 管理员 59 if args[0] == account: 60 logger.info('管理员account: %s - 登录成功' % account) 61 return account_info 62 else: 63 print('\033[1;31m您不是管理员!\033[0m'.center(25, '-')) 64 else: # 普通账号 65 logger.info('account: %s - 登录成功' % account) 66 return account_info 67 else: 68 print('\033[1;31m密码错误!\033[0m'.center(25, '-')) 69 else: 70 print('\033[1;31m账号:%s 不存在\033[0m' % account) 71 login_count += 1 72 else: 73 logger.critical('account: %s - 登录失败次数超过3次' % account) 74 exit()
db_handler.py
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 # -*- coding:utf-8 -*- 2 import os 3 import json 4 5 from .logger import set_logger 6 from .auth import judge_account 7 from conf.settings import( 8 DATABASE, TRANSACTION_TYPE 9 ) 10 11 trans_logger = set_logger('transactions') 12 13 14 def account_data_save(account, account_data): 15 # 修改文件的 公共方法 16 path = os.path.join(DATABASE['path'], account + '.json') 17 path_tmp = os.path.join(DATABASE['path'], account + '_tmp' + '.json') 18 if os.path.isfile(path): 19 with open(path_tmp, 'w', encoding='utf-8') as f: 20 json.dump(account_data, f) 21 os.replace(path_tmp, path) 22 return account_data 23 24 25 def save_json_common(account, new_balance, account_data): 26 # 内存中 替换余额 27 account_data['balance'] = round(new_balance, 2) 28 return account_data_save(account, account_data) 29 30 31 def transaction_logger(account, new_balance, account_data, arguments): 32 # 记录 日志的 公共方法 33 account_data_new = save_json_common(account, new_balance, account_data) 34 if account_data_new: 35 trans_logger.info('account: %s - account_type: %s - action: %s - interest: %s' % (account, arguments[0], arguments[1]['action'], arguments[1]['interest'])) 36 return account_data_new 37 38 39 def split_account(new_balance, account_info, *args, **kwargs): 40 # 区分开 是两个账号间的操作 还是一个账号 41 account = account_info['account'] 42 if len(kwargs) != 0: # 转账 两个账号 间的操作 43 transfer_data = save_json_common(kwargs['transfer_account'], kwargs['transfer_new_balance'], kwargs['transfer_data']) 44 if transfer_data: 45 return transaction_logger(account, new_balance, account_info['account_data'], args) 46 else: # 一个 账号 的操作 47 return transaction_logger(account, new_balance, account_info['account_data'], args) 48 49 50 def make_transaction(money, account_info, account_type, *args): 51 # 提现 还款 转账 公共方法 52 if account_type in TRANSACTION_TYPE: 53 transaction_type = TRANSACTION_TYPE[account_type] 54 interest = money * transaction_type['interest'] 55 old_balance = account_info['account_data']['balance'] 56 # 减钱的 操作 57 if transaction_type['action'] == 'minus': 58 if len(args) == 0: # 提现 或者 消费 没有附加的参数 59 new_balance = old_balance - money - interest 60 if new_balance < 0: 61 print('\033[1;31m交易失败,余额不足\033[0m') 62 else: 63 return split_account(new_balance, account_info, account_type, transaction_type) 64 else: # 转账 65 if args[0] == account_info['account']: 66 print('\033[1;31m注:同一账号,不允许转账\033[0m') 67 else: 68 transfer_data = judge_account(args[0]) 69 if transfer_data: 70 if transfer_data['status'] == 1: 71 print('\033[1;31m账号:%s 被冻结!\033[0m'.center(30, '-') % args[0]) 72 else: 73 new_balance = old_balance - money - interest 74 if new_balance < 0: 75 print('\033[1;31m交易失败,余额不足\033[0m') 76 else: 77 transfer_new_balance = transfer_data['balance'] + money 78 return split_account(new_balance, account_info, account_type, transaction_type, transfer_account = args[0], transfer_new_balance = transfer_new_balance, transfer_data = transfer_data) 79 else: 80 print('\033[1;31m账号:%s 不存在\033[0m' % args[0]) 81 # 加钱的 操作 82 elif transaction_type['action'] == 'plus': 83 new_balance = old_balance + money + interest 84 return split_account(new_balance, account_info, account_type, transaction_type) 85 86 else: 87 print('\033[1;31m%s 交易类型不存在!\033[0m' % account_type)
logger.py
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 # -*- coding:utf-8 -*- 2 import os 3 import logging 4 from logging import handlers 5 6 from conf.settings import( 7 LOG_LEVEL, BASE_DIR, LOG_FORMATTER 8 ) 9 10 11 def set_logger(name): 12 # 配置logger对象 13 logger = logging.getLogger(name) 14 logger.setLevel(LOG_LEVEL) 15 16 # fh = logging.FileHandler(os.path.join(BASE_DIR, 'log/'+name+'.log'), encoding='utf-8') 17 # fh = handlers.RotatingFileHandler(filename = os.path.join(BASE_DIR, 'log/'+name+'.log'), maxBytes=10, backupCount=2, encoding='utf-8') 18 fh = handlers.TimedRotatingFileHandler(filename = os.path.join(BASE_DIR, 'log/'+name+'.log'), when='S', interval=2, backupCount=2, encoding='utf-8') 19 logger.addHandler(fh) 20 21 fh_formatter = LOG_FORMATTER 22 fh.setFormatter(fh_formatter) 23 24 return logger
main.py
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 # -*- coding:utf-8 -*- 2 import re 3 4 from .logger import set_logger 5 from .db_handler import make_transaction 6 from .auth import ( 7 login, auth_login 8 ) 9 10 access_logger = set_logger('access') 11 12 13 @auth_login 14 def checkout(account_info, money): 15 # 购买商品之后 结账 16 account_data = make_transaction(money, account_info, 'consume') 17 if account_data: 18 print('账户:%s ->> 消费 \033[1;34m%s\033[0m -> 余额 \033[1;31m%s\033[0m' % (account_info['account'], money, account_data['balance'])) 19 exit() 20 else: 21 print('\033[1;31m结账失败!\033[0m') 22 23 24 def judge_num(num): 25 # 判断 int float 26 str_num = re.sub('\.', '', num, count=1) 27 if str_num.isdigit(): 28 return float(num) 29 30 31 @auth_login 32 def view_account(account_info): 33 # 查看账户信息 34 msg_info = '''\033[1;34m 35 -----账户:%s 的信息:----- 36 balance: %10s 37 credit: %10s 38 pay_day: %10s\033[0m 39 ''' % (account_info.get('account'), account_info['account_data']['balance'], account_info['account_data']['credit'], account_info['account_data']['pay_day']) 40 print(msg_info) 41 42 43 @auth_login 44 def withdraw(account_info): 45 # 提现 46 while True: 47 money = input('withdraw_money(q表示退出)>>>:').strip() 48 if not money: 49 continue 50 if money == 'q': 51 break 52 float_money = judge_num(money) 53 if float_money: 54 account_data = make_transaction(float_money, account_info, 'withdraw') 55 if account_data: 56 print('账户:%s -> 提现成功 -> 提现 \033[1;34m%s\033[0m -> 余额 \033[1;31m%s\033[0m' % (account_info['account'], money, account_data['balance'])) 57 else: 58 print('\033[1;31m提现失败!\033[0m') 59 else: 60 print('请输入\033[1;34m正确的\033[0m并\033[1;31m大于0\033[0m的金额'.center(45, '-')) 61 62 63 @auth_login 64 def pay_back(account_info): 65 # 还款 66 while True: 67 money = input('pay_back_money(q表示退出)>>>:').strip() 68 if not money: 69 continue 70 if money == 'q': 71 break 72 float_money = judge_num(money) 73 if float_money: 74 account_data = make_transaction(float_money, account_info, 'pay_back') 75 if account_data: 76 print('账户:%s -> 还款成功 -> 还款 \033[1;34m%s\033[0m -> 余额 \033[1;31m%s\033[0m' % (account_info['account'], money, account_data['balance'])) 77 else: 78 print('\033[1;31m还款失败!\033[0m') 79 else: 80 print('请输入\033[1;34m正确的\033[0m并\033[1;31m大于0\033[0m的金额'.center(45, '-')) 81 82 83 @auth_login 84 def transfer(account_info): 85 # 转账 86 while True: 87 transfer_account = input('transfer_account(q表示退出)>>>:').strip() 88 if not transfer_account: 89 continue 90 if transfer_account == 'q': 91 break 92 while True: 93 money = input('transfer_money(q表示退出)>>>:').strip() 94 if not money: 95 continue 96 if money == 'q': 97 return 98 float_money = judge_num(money) 99 if float_money: 100 account_data = make_transaction(float_money, account_info, 'transfer', transfer_account) 101 if account_data: 102 print('账户:%s -> 转账成功 -> 转账 \033[1;34m%s\033[0m -> 余额 \033[1;31m%s\033[0m' % (account_info['account'], money, account_data['balance'])) 103 else: # 交易失败余额不足的 或者 账号不存在的 或者 同一账号的 会直接退出 104 print('\033[1;31m转账失败!\033[0m') 105 return 106 else: 107 print('请输入\033[1;34m正确的\033[0m并\033[1;31m大于0\033[0m的金额'.center(45, '-')) 108 109 110 def quit_func(account_info): 111 # 退出 112 exit('bye bye ...') 113 114 115 def controller(account_info): 116 # 功能分发器 117 print('账户\033[0;34m%s\033[0m登录成功'.center(35, '*') % account_info.get('account')) 118 msg_num = '''\033[1;33m 119 --------start-------- 120 1. 账户信息 121 2. 提现 122 3. 还款 123 4. 转账 124 5. 退出 125 ---------end--------- 126 \033[0m 127 ''' 128 num_func = {'1': view_account, '2': withdraw, '3': pay_back, '4': transfer, '5': quit_func} # 字典的值可存放 地址 129 while True: 130 print(msg_num) 131 choice_num = input('num>>>:').strip() 132 if not choice_num: 133 continue 134 if choice_num in num_func: 135 num_func[choice_num](account_info) 136 else: 137 print('\033[1;31m请输入正确的序号\033[0m') 138 139 140 def run(): 141 account_info = login(access_logger) 142 if access_logger: 143 controller(account_info)
manager.py
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 # -*- coding:utf-8 -*- 2 import os 3 import json 4 import string 5 import random 6 from hashlib import md5 7 8 from .logger import set_logger 9 from .main import judge_num 10 from .db_handler import account_data_save 11 from .auth import( 12 login, judge_account, auth_login 13 ) 14 from conf.settings import( 15 MANAGER_ID, DATABASE, DEFAULT_ACCOUNT 16 ) 17 18 manager_logger = set_logger('manager') 19 20 21 def set_salt(): 22 # 每个用户的salt值不一样 库中的password就不一样 23 salt = ''.join(random.sample(string.digits + string.ascii_letters + string.punctuation, 8)) # 随机的8位盐 使得相同的密码 也有不同md5()值 24 md5_value = md5() 25 md5_value.update(b'123' + salt.encode('utf-8')) 26 return salt, md5_value.hexdigest() 27 28 29 def get_account(): 30 # 输入 id 得到账户信息 31 while True: 32 id_account = input('id>>>:').strip() 33 if id_account.isdigit(): 34 account_date = judge_account(id_account) 35 if account_date: 36 return account_date, id_account 37 else: 38 print('\033[1;31m账号:%s 不存在\033[0m' % id_account) 39 else: 40 print('\033[1;31m请输入整数的 id 账户\033[0m') 41 42 43 @auth_login 44 def add_acount(m_account_info): 45 # 添加账户 46 print('\033[1;34m管理员 %s 开始添加账户\033[0m'.center(30, '-') % m_account_info['account']) 47 while True: 48 id_account = input('id>>>:').strip() 49 if id_account.isdigit(): 50 account_date = judge_account(id_account) 51 if account_date: 52 print('\033[1;31m账号:%s 已存在\033[0m' % id_account) 53 else: 54 salt_pass = set_salt() 55 DEFAULT_ACCOUNT['id'] = int(id_account) 56 DEFAULT_ACCOUNT['password'] = salt_pass[1] 57 DEFAULT_ACCOUNT['salt'] = salt_pass[0] 58 with open(os.path.join(DATABASE['path'], id_account + '.json'), 'w', encoding='utf-8') as f: 59 json.dump(DEFAULT_ACCOUNT, f) 60 print('\033[1;32m账号:%s 添加成功\033[0m' % id_account) 61 manager_logger.info('管理员account:%s - 添加账户account:%s' % (m_account_info['account'], id_account)) 62 break 63 else: 64 print('\033[1;31m请输入整数的 id 账户\033[0m') 65 66 67 @auth_login 68 def credit_account(m_account_info): 69 # 设定用户 额度 70 account_tuple = get_account() 71 if account_tuple: 72 credit = input('new_credit>>>:').strip() 73 float_credit = judge_num(credit) 74 if float_credit: 75 account_tuple[0]['credit'] = round(float_credit, 2) 76 account_data = account_data_save(account_tuple[1], account_tuple[0]) 77 if account_data: 78 print('修改账号: \033[1;33m%s\033[0m 额度成功,现在额度为:\033[1;34m%s\033[0m' % (account_tuple[1], account_data['credit'])) 79 manager_logger.info('管理员account:%s - 修改账号account:%s - 修改额度credit:%s' % (m_account_info['account'], account_tuple[1], float_credit)) 80 else: 81 print('账号:%s 文件修改失败!' % account_tuple[1]) 82 else: 83 print('\033[1;31m注意:用户额度须为整数或小数\033[0m') 84 85 86 @auth_login 87 def freeze_account(m_account_info): 88 # 冻结账户 89 account_tuple = get_account() 90 if account_tuple: 91 if account_tuple[1] == m_account_info['account']: # 是管理员 本人 92 print('\033[1;31m您是管理员,不能冻结自己!\033[0m') 93 else: 94 if account_tuple[0]['status'] == 1: 95 print('账号是 \033[1;32m冻结\033[0m 状态,将 \033[1;34m解冻\033[0m') 96 account_tuple[0]['status'] = 0 97 else: 98 print('账号是 \033[1;32m解冻\033[0m 状态,将 \033[1;34m冻结\033[0m') 99 account_tuple[0]['status'] = 1 100 account_data = account_data_save(account_tuple[1], account_tuple[0]) 101 if account_data: 102 print('\033[1;33m修改账号的 冻结状态:%s 成功!\033[0m' % (account_tuple[1])) 103 manager_logger.info('管理员account:%s - 修改账号account:%s - 修改冻结状态成功' % (m_account_info['account'], account_tuple[1])) 104 else: 105 print('账号:%s 文件修改失败!' % account_tuple[1]) 106 107 108 def quit_func(m_account_info): 109 # 退出 110 exit('bye bye ...') 111 112 113 def controller(m_account_info): 114 # 功能分发器 115 print('\033[1;33m欢迎管理员 %s 登录\033[0m'.center(35, '-') % m_account_info.get('account')) 116 manager_choice = ''' 117 ------start------ 118 1. 添加账户 119 2. 用户额度 120 3. 冻结账户 121 4. 退出 122 ------end------ 123 ''' 124 dict_manager_info = {'1': add_acount, '2': credit_account, '3': freeze_account, '4': quit_func} 125 while True: 126 print(manager_choice) 127 num = input('num>>>:').strip() 128 if not num: 129 continue 130 if num in dict_manager_info: 131 dict_manager_info[num](m_account_info) 132 else: 133 print('\033[1;31m请重新选择\033[0m') 134 135 136 def run(): 137 manager_account_info = login(manager_logger, MANAGER_ID) 138 if manager_account_info: 139 controller(manager_account_info)
shopper.py
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 # -*- coding:utf-8 -*- 2 from conf.settings import GOODS 3 from .auth import login 4 from .main import ( 5 access_logger, checkout 6 ) 7 8 9 def show_buy_goods(goods_buyed): 10 # 展示购买的商品 11 all_money = 0 12 if len(goods_buyed) == 0: 13 return all_money 14 else: 15 print('您已购买以下商品:'.center(20, '-')) 16 for index, i in enumerate(goods_buyed, 1): 17 print('%s. %s %s' % (index, i['name'], i['price'])) 18 all_money += int(i['price']) 19 return all_money 20 21 22 def buy_goods(dict_goods): 23 # 购买商品 24 goods_buyed = [] 25 while True: 26 choice = input('num(q退出并结算)>>>:').strip() 27 if not choice: 28 continue 29 if choice == 'q': 30 return show_buy_goods(goods_buyed) 31 if choice in dict_goods: 32 goods_buyed.append(dict_goods[choice]) 33 print('\033[1;34m%s\033[0m 加入购物车' % dict_goods[choice]['name']) 34 else: 35 print('\033[1;31m重新选择\033[0m') 36 37 38 def show_goods(): 39 # 展示商品列表 40 dict_goods = {} 41 print('展示商品列表'.center(20,'-')) 42 for index, i in enumerate(GOODS): 43 dict_goods[str(index)] = i 44 print('%d. %s %d' % (index, i['name'], i['price'])) 45 return dict_goods 46 47 48 def run(): 49 account_info = login(access_logger) 50 if account_info['is_auth'] is True: 51 all_money = buy_goods(show_goods()) 52 checkout(account_info, all_money)
***.json
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 {"id": 999, "password": "4940e231b912f39abd7d98c81bed7053", "salt": "}fH>@&0o", "balance": 15000, "credit": 15000, "enroll_date": "2016-01-01", "expire_date": "2021-01-01", "pay_day": 22, "status": 0}
account_sample.py
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 # -*- coding:utf-8 -*- 2 import json 3 import random 4 import string 5 from hashlib import md5 6 7 salt = ''.join(random.sample(string.digits + string.ascii_letters + string.punctuation, 8)) 8 md5_value = md5() 9 md5_value.update(b'123'+salt.encode('utf-8')) 10 11 account_dic = { 12 'id': 999, 13 'password': md5_value.hexdigest(), 14 'salt': salt, 15 'balance': 15000, # 账户余额 16 'credit': 15000, # 信用额度 17 'enroll_date': '2016-01-01', 18 'expire_date': '2021-01-01', 19 'pay_day': 22, 20 'status': 0 # 0 = normal, 1 = locked, 2 = disabled 21 } 22 23 print(json.dumps(account_dic)) 24 # json.dump(account_dic, open('./accounts/999.json', 'w', encoding='utf-8'))
access.log
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 2018-03-04 11:33:52 AM - access - CRITICAL - account: - 登录失败次数超过3次 2 2018-03-04 11:34:03 AM - access - INFO - account: 1000 - 登录成功 3 2018-03-04 11:34:50 AM - access - INFO - account: 999 - 登录成功 4 2018-03-04 11:35:57 AM - access - INFO - account: 1000 - 登录成功 5 2018-03-04 11:38:23 AM - access - INFO - account: 1008 - 登录成功 6 2018-03-04 11:39:29 AM - access - INFO - account: 1008 - 登录成功 7 2018-03-04 11:40:17 AM - access - INFO - account: 1008 - 登录成功 8 2018-03-04 11:41:40 AM - access - INFO - account: 1008 - 登录成功 9 2018-03-04 11:43:41 AM - access - INFO - account: 1008 - 登录成功 10 2018-03-04 11:46:12 AM - access - CRITICAL - account: - 登录失败次数超过3次 11 2018-03-04 11:46:19 AM - access - INFO - account: 1000 - 登录成功 12 2018-03-04 11:50:57 AM - access - CRITICAL - account: 1010 - 登录失败次数超过3次 13 2018-03-04 11:51:03 AM - access - INFO - account: 999 - 登录成功 14 2018-03-04 11:51:08 AM - access - INFO - account: 1010 - 登录成功 15 2018-03-04 11:51:49 AM - access - INFO - account: 999 - 登录成功 16 2018-03-04 11:52:00 AM - access - INFO - account: 1010 - 登录成功 17 2018-03-04 11:54:02 AM - access - INFO - account: 1000 - 登录成功 18 2018-03-04 11:54:56 AM - access - INFO - account: 1000 - 登录成功 19 2018-03-04 11:55:38 AM - access - INFO - account: 1000 - 登录成功 20 2018-03-04 11:57:30 AM - access - INFO - account: 1000 - 登录成功 21 2018-03-04 12:02:44 PM - access - INFO - account: 999 - 登录成功 22 2018-03-04 12:02:57 PM - access - CRITICAL - account: 1000 - 登录失败次数超过3次 23 2018-03-04 12:03:04 PM - access - INFO - account: 1000 - 登录成功 24 2018-03-04 12:03:14 PM - access - INFO - account: 999 - 登录成功 25 2018-03-04 12:05:04 PM - access - INFO - account: 999 - 登录成功
manager.log
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 2018-03-04 11:34:18 AM - manager - CRITICAL - account: 123 - 登录失败次数超过3次 2 2018-03-04 11:34:24 AM - manager - INFO - 管理员account: 999 - 登录成功 3 2018-03-04 11:34:37 AM - manager - INFO - 管理员account:999 - 修改账号account:1000 - 修改冻结状态成功 4 2018-03-04 11:35:25 AM - manager - INFO - 管理员account:999 - 添加账户account:900 5 2018-03-04 11:35:38 AM - manager - INFO - 管理员account: 999 - 登录成功 6 2018-03-04 11:35:52 AM - manager - INFO - 管理员account:999 - 修改账号account:1000 - 修改冻结状态成功 7 2018-03-04 11:37:39 AM - manager - INFO - 管理员account:999 - 添加账户account:1008 8 2018-03-04 11:38:03 AM - manager - INFO - 管理员account:999 - 修改账号account:1008 - 修改额度credit:1200.0 9 2018-03-04 11:38:37 AM - manager - INFO - 管理员account:999 - 修改账号account:1008 - 修改冻结状态成功 10 2018-03-04 11:41:48 AM - manager - INFO - 管理员account: 999 - 登录成功 11 2018-03-04 11:41:52 AM - manager - INFO - 管理员account:999 - 修改账号account:1008 - 修改冻结状态成功 12 2018-03-04 11:43:50 AM - manager - INFO - 管理员account:999 - 修改账号account:1008 - 修改冻结状态成功 13 2018-03-04 11:48:13 AM - manager - CRITICAL - account: 1000 - 登录失败次数超过3次 14 2018-03-04 11:48:19 AM - manager - INFO - 管理员account: 999 - 登录成功 15 2018-03-04 11:48:26 AM - manager - INFO - 管理员account:999 - 添加账户account:1009 16 2018-03-04 11:48:32 AM - manager - INFO - 管理员account:999 - 添加账户account:23 17 2018-03-04 11:48:45 AM - manager - INFO - 管理员account:999 - 添加账户account:1010 18 2018-03-04 11:49:36 AM - manager - INFO - 管理员account:999 - 修改账号account:1009 - 修改额度credit:1000.0 19 2018-03-04 11:49:52 AM - manager - INFO - 管理员account:999 - 修改账号account:1009 - 修改冻结状态成功 20 2018-03-04 11:50:31 AM - manager - INFO - 管理员account: 999 - 登录成功 21 2018-03-04 11:50:38 AM - manager - INFO - 管理员account:999 - 添加账户account:123 22 2018-03-04 11:56:32 AM - manager - INFO - 管理员account: 999 - 登录成功 23 2018-03-04 11:56:38 AM - manager - INFO - 管理员account:999 - 修改账号account:1003 - 修改冻结状态成功 24 2018-03-04 11:56:53 AM - manager - INFO - 管理员account:999 - 修改账号account:1003 - 修改冻结状态成功 25 2018-03-04 12:03:37 PM - manager - INFO - 管理员account: 999 - 登录成功 26 2018-03-04 12:03:45 PM - manager - INFO - 管理员account:999 - 修改账号account:999 - 修改额度credit:123.0 27 2018-03-04 12:04:48 PM - manager - INFO - 管理员account:999 - 修改账号account:999 - 修改额度credit:15000.0
transactions.log
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 2018-03-04 11:36:10 AM - transactions - INFO - account: 1000 - account_type: withdraw - action: minus - interest: 0.05 2 2018-03-04 11:36:31 AM - transactions - INFO - account: 1000 - account_type: pay_back - action: plus - interest: 0 3 2018-03-04 11:36:34 AM - transactions - INFO - account: 1000 - account_type: pay_back - action: plus - interest: 0 4 2018-03-04 11:37:02 AM - transactions - INFO - account: 1000 - account_type: transfer - action: minus - interest: 0.05 5 2018-03-04 11:38:58 AM - transactions - INFO - account: 1008 - account_type: pay_back - action: plus - interest: 0 6 2018-03-04 11:42:17 AM - transactions - INFO - account: 1008 - account_type: withdraw - action: minus - interest: 0.05 7 2018-03-04 11:46:34 AM - transactions - INFO - account: 1000 - account_type: withdraw - action: minus - interest: 0.05 8 2018-03-04 11:46:42 AM - transactions - INFO - account: 1000 - account_type: pay_back - action: plus - interest: 0 9 2018-03-04 11:46:45 AM - transactions - INFO - account: 1000 - account_type: pay_back - action: plus - interest: 0 10 2018-03-04 11:47:48 AM - transactions - INFO - account: 1000 - account_type: transfer - action: minus - interest: 0.05 11 2018-03-04 11:51:15 AM - transactions - INFO - account: 1010 - account_type: consume - action: minus - interest: 0 12 2018-03-04 11:52:08 AM - transactions - INFO - account: 1010 - account_type: withdraw - action: minus - interest: 0.05 13 2018-03-04 11:55:22 AM - transactions - INFO - account: 1000 - account_type: pay_back - action: plus - interest: 0 14 2018-03-04 11:55:41 AM - transactions - INFO - account: 1000 - account_type: withdraw - action: minus - interest: 0.05 15 2018-03-04 11:55:47 AM - transactions - INFO - account: 1000 - account_type: pay_back - action: plus - interest: 0 16 2018-03-04 11:56:04 AM - transactions - INFO - account: 1000 - account_type: pay_back - action: plus - interest: 0 17 2018-03-04 11:56:06 AM - transactions - INFO - account: 1000 - account_type: pay_back - action: plus - interest: 0 18 2018-03-04 11:57:35 AM - transactions - INFO - account: 1000 - account_type: consume - action: minus - interest: 0
github:https://github.com/alice-bj/atm