作业题目: 模拟实现一个ATM + 购物商城程序
1、额度 15000或自定义 2、实现购物商城,买东西加入 购物车,调用信用卡接口结账 3、可以提现,手续费5% 4、支持多账户登录 5、支持账户间转账 6、记录每月日常消费流水 7、提供还款接口 8、ATM记录操作日志 9、提供管理接口,包括添加账户、用户额度,冻结账户等。。。 10、用户认证用装饰器
流程图:
代码目录结构图:
atm
│ ATM.pdf
│ main_server.py -- 程序入口
│ README
│ __init__.py
│
│
├─config
│ │ setting.py ---- 配置文件
│ │ __init__.py
│ │
│ └─__pycache__
│ setting.cpython-36.pyc
│ __init__.cpython-36.pyc
│
├─core
│ │ admin.py -- 后台管理
│ │ ATM_operation.py --- ATM操作
│ │ auth.py --- 账户验证
│ │ db_handler.py --- 文件操作
│ │ loggers.py --- 日志
│ │ main.py ---主程序入口
│ │ make.py --- 交易中心
│ │ shopping.py --- 购物商城
│ │ __init__.py
│ │
│ └─__pycache__
│ admin.cpython-36.pyc
│ ATM_operation.cpython-36.pyc
│ auth.cpython-36.pyc
│ db_handler.cpython-36.pyc
│ loggers.cpython-36.pyc
│ main.cpython-36.pyc
│ make.cpython-36.pyc
│ shopping.cpython-36.pyc
│ __init__.cpython-36.pyc
│
│
│
├─db
│ │ __init__.py
│ │
│ └─accounts --- 账户文件
│ 12.json
│ 123.json
│ 1234.json
│ 14.json
│ admin.json
│ __init__.py
│
├─log
│ │ access.log --- 普通用户操作日志
│ │ admin.log --- 管理员操作日志
│ │ shopping.log ---- 消费日志
│ │ transaction.log ---- 交易日志
│ │ __init__.py
│ │
│ └─__pycache__
│ __init__.cpython-36.pyc
│
└─shopp
│ __init__.py
│
└─__pycache__
__init__.cpython-36.pyc
main_server.py -- 程序入口
1 import os,sys 2 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 3 sys.path.append(BASE_DIR) 4 if __name__ == '__main__': 5 from core import main 6 main.entrance()
main.py ---主程序入口
1 from .auth import authentication 2 from .loggers import logger_log 3 from.ATM_operation import with_draw 4 from .ATM_operation import transfer 5 from .ATM_operation import pay_back 6 from .ATM_operation import view_account_info 7 from .shopping import shoppings 8 from .admin import admin_load 9 from .db_handler import load_account 10 from .db_handler import save_db 11 12 access_logger = logger_log('access') 13 transaction_logger = logger_log('transaction') 14 15 16 info = [ 17 ('查看账户信息',view_account_info), 18 ('转账',transfer), 19 ('还款',pay_back), 20 ('取现',with_draw), 21 ('购物商城',shoppings), 22 ('后台管理',admin_load) 23 ] 24 25 def conroller(use_boj): 26 '功能分发器' 27 while True: 28 for i,v in enumerate(info): 29 print(i,v[0]) 30 choice = input('请输入您的选择:').strip() 31 if not choice:continue 32 if choice.isdigit(): 33 choice = int(choice) 34 if choice < len(info) and choice >= 0: 35 info[choice][1](use_boj,transaction_logger=transaction_logger,access_logger=access_logger) 36 def entrance(): 37 '程序入口' 38 user_obj = { 39 'is_authentication':False, 40 'data':None 41 } 42 retry_count = 0 43 while user_obj['is_authentication'] is not True: 44 account = input('请输入账号:').strip() 45 password = input('请输入密码:').strip() 46 auth_data = authentication(account,password) 47 if auth_data: 48 user_obj['is_authentication'] = True 49 user_obj['data'] = auth_data 50 print('欢迎登陆'.center(50,'*')) 51 access_logger.info('%s已登陆'%user_obj['data']['id']) 52 conroller(user_obj) 53 else: 54 print('账号密码错误!') 55 retry_count += 1 56 if retry_count == 3: 57 msg = '用户%s已登陆3失败'%account 58 access_logger.error(msg) 59 60 break
auth.py --- 账户验证
1 from .db_handler import load_account 2 3 def authentication(account,password): 4 '验证账户' 5 account_data = load_account(account) 6 # print(account_data) 7 if account_data['status'] == 0: 8 account_data = account_data['data'] 9 if password == account_data['password']: 10 # print(account_data) 11 return account_data 12 else: 13 return None 14 else: 15 return '账户已被锁定!' 16 def auth(fun): 17 def wrapper(*args,**kwargs): 18 if args[0].get('is_authentication'): 19 return fun(*args,**kwargs) 20 else: 21 print('用户不能登陆!') 22 return wrapper
db_handler.py --- 文件操作
1 import json,os 2 from config import setting 3 4 def load_account(account): 5 '读取文件' 6 account_file = os.path.join(setting.BASE_DB,'%s.json'%account) 7 # print(account_file) 8 if os.path.isfile(account_file): 9 f = open(account_file) 10 data = json.load(f) 11 f.close() 12 return {'status':0,'data':data} 13 else: 14 return {'status':-1,'error':'文件不存在'} 15 16 def save_db(account_data): 17 '保存文件' 18 account_file = os.path.join(setting.BASE_DB,'%s.json'%account_data) 19 print(account_file) 20 if os.path.isfile(account_file): 21 f = open('%s.new'%account_file,'w') 22 data = json.dump(account_data,f) 23 f.close() 24 os.remove(account_file) 25 os.rename('%s.new'%account_file,account_file) 26 return {'status':0,'data':data} 27 else: 28 return {'status':-1,'error':'文件不存在'}
ATM_operation.py --- ATM操作
1 from .loggers import logger_log 2 from .make import make_transfer 3 from .auth import auth 4 5 def view_account_info(account_data,*args,**kwargs): 6 '查询账户信息' 7 # print(account_data) 8 trans_logger = kwargs.get('transaction_logger') 9 print('账户个人信息'.center(50,'*')) 10 for k,v in account_data['data'].items(): 11 if k not in ('password'): 12 print('%15s:%s'%(k,v)) 13 print('END'.center(50,'*')) 14 trans_logger.info('%s查询了账户信息'%account_data['data']['id']) 15 @auth 16 def transfer(account_data,*args,**kwargs): 17 '账户转账' 18 trans_logger = kwargs.get('transaction_logger') 19 print('账户余额'.center(50,'*')) 20 print('credit:%s ' 21 'blance:%s'%(account_data['data']['credit'],account_data['data']['balance'])) 22 flag_exit = False 23 while not flag_exit: 24 receiving_account = input('请输入接收账号或按B返回:').strip() 25 transfer_amount = input('请输入转账金额按B返回:').strip() 26 if len(transfer_amount) > 0 and transfer_amount.isdigit(): 27 transfer_amount = int(transfer_amount) 28 if (account_data['data']['balance'] / 2) >= transfer_amount: 29 transaction_result = make_transfer(trans_logger,account_data,'transfer',transfer_amount, 30 receiving_account=receiving_account) 31 if transaction_result['status'] == 0: 32 print('成功转账%s元,余额还有%s'%(transfer_amount,account_data['data']['balance'])) 33 else: 34 print(transaction_result) 35 else: 36 print('余额不足,可转账%s元'%int(account_data['data']['balance']/2)) 37 if receiving_account == 'b'.lower(): 38 flag_exit = True 39 @auth 40 def with_draw(account_data,*args,**kwargs): 41 '取现' 42 trans_logger = kwargs.get('transaction_logger') 43 print('账户余额'.center(50,'*')) 44 print('credit:%s ' 45 'blance:%s'%(account_data['data']['credit'],account_data['data']['balance'])) 46 flag_exit = False 47 while not flag_exit: 48 withdraw_amount = input('请输入提现的金额或按按B返回:').strip() 49 if len(withdraw_amount) > 0 and withdraw_amount.isdigit(): 50 withdraw_amount = int(withdraw_amount) 51 if (account_data['data']['balance'] / 2) >= withdraw_amount: 52 transaction_result = make_transfer(trans_logger,account_data,'withdraw',withdraw_amount) 53 if transaction_result['status'] == 0: 54 print('成功提现%s元,余额还有%s'%(withdraw_amount,account_data['data']['balance'])) 55 else: 56 print(transaction_result) 57 else: 58 print('余额不足,可提取%s元'%int(account_data['data']['balance']/2)) 59 if withdraw_amount == 'b'.lower(): 60 flag_exit = True 61 @auth 62 def pay_back(account_data,*args,**kwargs): 63 '还款' 64 trans_logger = kwargs.get('transaction_logger') 65 print('账户余额'.center(50, '*')) 66 print('credit:%s ' 67 'blance:%s' % (account_data['data']['credit'], account_data['data']['balance'])) 68 flag_exit = False 69 while not flag_exit: 70 payback_amount = input('请输入还款的金额按B返回:').strip() 71 if len(payback_amount) > 0 and payback_amount.isdigit(): 72 payback_amount = int(payback_amount) 73 transaction_result = make_transfer(trans_logger, account_data, 'repay', payback_amount) 74 if transaction_result['status'] == 0: 75 print('成功还款%s元,当前余额还有%s' % (payback_amount, account_data['data']['balance'])) 76 else: 77 print(transaction_result) 78 else: 79 print('输入错误,请重新输入!') 80 if payback_amount == 'b'.lower(): 81 flag_exit = True
make.py --- 交易中心
1 from config import setting 2 from .db_handler import save_db 3 from .db_handler import load_account 4 5 6 def make_transfer(logger,user_obj,trans_type,amount,**kwargs): 7 '交易中心,进行账户间的加减' 8 amount = float(amount) 9 if trans_type in setting.TRANSACTION_TYPE: 10 interest = amount * setting.TRANSACTION_TYPE[trans_type]['interest'] 11 old_balance = user_obj['data']['balance'] 12 if setting.TRANSACTION_TYPE[trans_type]['action'] == 'transfer': 13 re_account = load_account(kwargs.get('receiving_account')) 14 re_account_data = re_account['data']['balance'] 15 re_account_data_balance = amount + re_account_data + interest 16 new_balance = old_balance - amount - interest 17 re_account['data']['balance'] = re_account_data_balance 18 user_obj['data']['balance'] = new_balance 19 save_db(re_account['data']) 20 save_db(user_obj['data']) 21 elif setting.TRANSACTION_TYPE[trans_type]['action'] == 'plus': 22 new_balance = amount + old_balance + interest 23 elif setting.TRANSACTION_TYPE[trans_type]['action'] == 'minus': 24 new_balance = old_balance - amount - interest 25 if new_balance < 0: 26 print('对不起,您的信用额度%s对当前的交易是不够的-%s,你当前的信用额度是%s元'%(user_obj['credit'],(amount+interest) 27 ,old_balance)) 28 return {'status':-1,'error':'交易失败,余额不足'} 29 user_obj['data']['balance'] = new_balance 30 save_db(user_obj['data']) 31 32 logger.info('account:%s action:%s amount:%s interest:%s balance:%s'% 33 (user_obj['data']['id'],trans_type,amount,interest,new_balance)) 34 return {'status':0,'msg':'交易成功'} 35 else: 36 print('对不起,%s交易类型不支持'%trans_type) 37 return {'status':1,'error':'交易失败,不支持的类型%s'%trans_type}
loggers.py --- 日志
1 from config import setting 2 import logging 3 import os 4 from logging import handlers 5 def logger_log(log_type): 6 log = logging.getLogger(log_type) 7 log.setLevel(setting.LOG_LEVE) 8 9 file_log = os.path.join(setting.LOG_PATH,setting.LOG_TYPE[log_type]) 10 fh = handlers.TimedRotatingFileHandler(file_log,when='D',interval=3,encoding='utf-8') 11 log.addHandler(fh) 12 13 file = setting.LOG_FORMATTER 14 fh.setFormatter(file) 15 return log
shopping.py --- 购物商城
1 from .make import make_transfer 2 from .db_handler import save_db 3 from .loggers import logger_log 4 from.auth import auth 5 6 7 shopping_logger = logger_log('shopping') 8 def shoppings(account_data,*args,**kwargs): 9 trans_logger = kwargs.get('transaction_logger') 10 exit_flag = False 11 goods = [ 12 ["电脑",1999], 13 ["鼠标",10], 14 ["游艇", 20], 15 ["美女", 998], 16 ] 17 18 shopping_list = {} 19 while not exit_flag: 20 print('-----------商品列表-------') 21 for i, v in enumerate(goods): 22 print(i, v) 23 24 choice = input('请输入商品的编号|q退出|y结账:').strip() 25 26 if choice.isdigit(): 27 choice = int(choice) 28 if choice >= 0 and choice < len(goods): 29 product = goods[choice] 30 31 if product[0] in shopping_list: 32 shopping_list[product[0]][1] += 1 33 else: 34 shopping_list[ product[0] ] =[ product[1],1] 35 print('%s已成功加入购物车'%product[0]) 36 37 elif choice == 'y'.lower(): 38 print('购物车'.center(50,'*')) 39 id_cat = 1 40 total = 0 41 print('id 商品 数量 单价 总价') 42 for key in shopping_list: 43 print('%s %s %s %s %s'%(id_cat, 44 key, 45 shopping_list[key][1], 46 shopping_list[key][0], 47 shopping_list[key][1]*shopping_list[key][0])) 48 id_cat += 1 49 total += shopping_list[key][1]*shopping_list[key][0] 50 buckles_money(account_data, total) 51 elif choice == 'q'.lower(): 52 break 53 @auth 54 def buckles_money(account_data,total,*args,**kwargs): 55 settle_acount = input('是否买单|y是|q退出:').strip() 56 if settle_acount == 'y': 57 shopping_data = float(total) 58 print('账户余额'.center(50, '*')) 59 print('credit:%s ' 60 'blance:%s' % (account_data['data']['credit'], account_data['data']['balance'])) 61 old_balance = account_data['data']['balance'] 62 if shopping_data <= old_balance: 63 new_balance = old_balance - shopping_data 64 print('已购买成功,您总共花了%s元,现在余额是%s元'%(shopping_data,new_balance)) 65 exit() 66 else: 67 print('余额不足,您现在的余额是%s元'%new_balance) 68 account_data['data']['balance'] = new_balance 69 save_db(account_data['data']) 70 shopping_logger.info('account:%s consume:%s balance:%s' % 71 (account_data['data']['id'], shopping_data, new_balance)) 72 elif settle_acount == 'q': 73 exit()
setting.py ---- 配置文件
1 import os,sys 2 import logging 3 4 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 5 6 BASE_DB = "%sdb\accounts"%BASE_DIR 7 8 9 LOG_LEVE = logging.INFO 10 11 LOG_TYPE = { 12 'access':'access.log', 13 'transaction':'transaction.log', 14 'shopping':'shopping.log', 15 'admin':'admin.log' 16 } 17 18 LOG_FORMATTER =logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') 19 LOG_PATH = os.path.join(BASE_DIR,'log') 20 21 TRANSACTION_TYPE = { 22 'repay':{'action':'plus', 'interest':0}, 23 'withdraw':{'action':'minus', 'interest':0.05}, 24 'transfer':{'action':'transfer', 'interest':0.05}, 25 }
admin.py -- 后台管理
1 from config import setting 2 import os 3 import json 4 from .db_handler import save_db 5 from .loggers import logger_log 6 7 admin_logger = logger_log('admin') 8 9 def add_account(account): 10 '增加账户' 11 dic = { 12 'id':None, 13 'password':None, 14 "credit":None, 15 "balance": None, 16 "enroll_date":None, 17 "expire_date": None, 18 "pay_day": None, 19 "status": 0 20 } 21 account_file = os.path.join(setting.BASE_DB, '%s.json' %account) 22 print(account_file) 23 if os.path.isfile(account_file): 24 print('账户已存在!') 25 else: 26 add_id = input('id:').strip() 27 if add_id.isdigit(): 28 add_id = int(add_id) 29 add_passwd = input('passwd:').strip() 30 add_credit = input('credit:').strip() 31 if add_credit.isdigit(): 32 add_credit = int(add_credit) 33 balance = input('balance:').strip() 34 if balance.isdigit(): 35 balance = int(balance) 36 enroll_date = input('enroll_date:').strip() 37 expire_date = input('expire_date:').strip() 38 pay_day = input('pay_day:').strip() 39 if pay_day.isdigit(): 40 pay_day = int(pay_day) 41 dic['id'] = add_id 42 dic['password'] = add_passwd 43 dic['credit'] = add_credit 44 dic['balance'] = balance 45 dic['enroll_date'] = enroll_date 46 dic['expire_date'] = expire_date 47 dic['pay_day'] = pay_day 48 f = open(os.path.join(setting.BASE_DB,'%s.json'%dic['id']),'w') 49 json.dump(dic,f) 50 f.close() 51 print('账户已添加成功!') 52 admin_logger.info('账户:%s已成功添加,信用额为%s元'%(dic['id'],dic['credit'])) 53 54 55 def modify_line(account): 56 '修改信用额度' 57 account_file = os.listdir(setting.BASE_DB) 58 modify_file = input('请输入要修改的账号:').strip() 59 modify_file = '%s.json'%modify_file 60 for i,v in enumerate(account_file): 61 if modify_file == v: 62 f = open(os.path.join(setting.BASE_DB,v),'r') 63 data = json.load(f) 64 f.close() 65 print('当前额度%s元'%data['credit']) 66 modifyline = input('请输入修改的额度:').strip() 67 if modifyline.isdigit(): 68 modifyline = int(modifyline) 69 data['credit'] = modifyline 70 save_db(data) 71 print('已成功修改%s额度,目前信用额度为%s元'%(modify_file,data['credit'])) 72 admin_logger.info('已成功修改账户:%s额度,目前信用额度为%s元'%(modify_file,data['credit'])) 73 74 def unfreeze_account(account): 75 '账户解锁' 76 unfreeze_file = os.listdir(setting.BASE_DB) 77 unfreeze = input('请输入要修改的账号:').strip() 78 unfreeze = '%s.json'%unfreeze 79 for i,v in enumerate(unfreeze_file): 80 if unfreeze == v: 81 f = open(os.path.join(setting.BASE_DB,v),'r') 82 data = json.load(f) 83 f.close() 84 unfreeze_acc = input('是否解冻账号:').strip() 85 if unfreeze_acc == 'y'.lower(): 86 data['status'] = 0 87 print('账户:%s已解冻'%unfreeze) 88 else: 89 print('输入错误!') 90 save_db(data) 91 admin_logger.info('账户:%s已解冻'%unfreeze) 92 admin_msg = [ 93 ('账户添加',add_account), 94 ('修改额度',modify_line), 95 ('解冻账户',unfreeze_account) 96 ] 97 def admin_load(account,*args,**kwargs): 98 admin_id = account['data']['id'] 99 if admin_id == 8888: 100 for i, v in enumerate(admin_msg): 101 print(i, v[0]) 102 choice = input('请选择|q退出:').strip() 103 if choice.isdigit(): 104 choice = int(choice) 105 if choice < len(admin_msg) and choice >= 0: 106 admin_msg[choice][1](account) 107 elif choice == 'q'.lower(): 108 exit() 109 else: 110 print('不是管理员!') 111 exit()
账户文件
1 import json 2 acc_dic = { 3 'id': 1234, 4 'password': 'abc', 5 'credit': 15000, 6 'balance': 15000, 7 'enroll_date': '2016-01-02', 8 'expire_date': '2021-01-01', 9 'pay_day': 22, 10 'status': 0 # 0 = normal, 1 = locked, 2 = disabled 11 } 12 13 print(json.dumps(acc_dic))