1、需求
本章作业:
模拟实现一个ATM + 购物商城程序
- 额度 15000或自定义
- 实现购物商城,买东西加入 购物车,调用信用卡接口结账
- 可以提现,手续费5%
- 支持多账户登录
- 支持账户间转账
- 记录每月日常消费流水
- 提供还款接口
- ATM记录操作日志
- 提供管理接口,包括添加账户、用户额度,冻结账户等。。。
- 用户认证用装饰器
示例代码 https://github.com/triaquae/py3_training/tree/master/atm
简易流程图:https://www.processon.com/view/link/589eb841e4b0999184934329
2、流程图
3.评语
4.我的代码
# 软件组织结构 atm │ ├─bin # 执行文件目录 │ └─__init__ │ └─__start__ # 执行文件 │ ├─conf # 配置文件目录 │ └─__init__ │ └─__settings__ │ ├─core # 核心代码 │ └─__init__ │ └─account.py # 账户管理 │ └─atm_logics.py # atm逻辑分发 │ └─atm_manage.py # atm主文件 │ └─authentication.py # 用户认证 │ └─db_handler.py # 数据库操作 │ └─log.py # 记录日志 │ └─main.py # 入口文件 │ └─shopping.py # 购物商城主文件 │ ├─db # 数据库目录 │ └─account # 用户账户文件目录 │ └─alex.json │ └─cat.json │ └─jack.json │ └─tom.json │ ├─logs # 各种日志文件目录 │ └─account.log # 账户管理log │ └─atm.log # atm操作log │ └─login.log # 登录操作log │ └─shop.log # 购物商城log │ ├─README │ ├─requirements.txt # 依赖包 │ └─setup.py
# 文件如何调用的? start.py---> main.py---> # 管理员 ----> account.py # 普通用户 # ATM ---> atm_manage.py---> atm_logics.py # 购物商城 ---> shopping.py
# -*- coding:utf-8 -*- import os import sys # 1.添加atm主目录到sys.path # file_path = os.path.abspath(__file__) # 文件绝对路径 # BASE_DIR1 = os.path.dirname(file_path) # BASE_DIR2 = os.path.dirname(BASE_DIR1) # atm主目录路径 # sys.path.append(BASE_DIR2) sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) # 2.导入core/main 主程序 from core import main if __name__ == '__main__': main.run()
# -*- coding:utf-8 -*- import os import logging BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) DB_PATH = '%s\db\account\' % BASE_DIR LOG_LEVEL = logging.INFO LOG_PATH = '%s\logs\' % BASE_DIR file_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') TRADE_TYPE = { 'withdraw': {'action': 'subtract', 'interest': 0.05}, 'transfer': {'action': 'subtract', 'interest': 0}, 'pay': {'action': 'subtract', 'interest': 0}, 'transferred': {'action': 'plus', 'interest': 0}, 'repay': {'action': 'plus', 'interest': 0}, } ADMIN_USER = 'admin' ADMIN_PASSWORD = 'admin'
# -*- coding:utf-8 -*- import os from conf import settings from .db_handler import load_account_data from .db_handler import save_account from .db_handler import save_db db_dir = settings.DB_PATH account_list = os.listdir(db_dir) def account_msg(*args): """查看所有账户,信用额度""" print("----------- 账户 信用额度 是否冻结 ") for i in account_list: account = i.split('.json')[0] ret = load_account_data(account) # db操作文件,取数据 if ret['status'] == 0: if ret['data']['status'] == 0: cold_status = '未冻结' else: cold_status = '冻结' print("%s %s %s" % (ret['data']['id'], ret['data']['credit'], cold_status )) def account_add(account, account_logger): """添加新用户""" account_name = input('请输入要添加的用户名:').strip() if account_name: ret = load_account_data(account_name) if ret['status'] == 0: print('该账户已经存在') else: save_account(account_name) print('添加成功,请重新登录查看') account_logger.info('account:%s be added by %s'%(account_name, account)) def account_cold(account, account_logger): """冻结用户""" account_name = input('请输入要冻结的用户名:') ret = load_account_data(account_name) if ret['status'] == 0: ret['data']['status'] = 1 save_db(ret) print('该账户被冻结') account_logger.info('account:%s be frozen by %s' % (account_name, account)) else: print('该账户不存在') def account_manage(account, account_logger, *args): """账户管理menu""" fun_dict = { '1': account_msg, '2': account_add, '3': account_cold, } while True: msg = ''' ----account管理 --- 1、查看所有账户及额度 2、添加账户 3、冻结账户 4、退出 ''' print(msg) choice = input(">>>").strip() if choice in fun_dict: fun_dict[choice](account, account_logger) elif choice == '4': exit() else: print('请重新输入')
# -*- coding:utf-8 -*- from conf import settings from .db_handler import save_db from .db_handler import load_account_data def trade(amount, trade_type, account_data, logger, *args): """交易中心,主要修改 余额""" if trade_type in settings.TRADE_TYPE: interest = amount * settings.TRADE_TYPE[trade_type]['interest'] # 利息 old_balance = account_data['data']['balance'] if settings.TRADE_TYPE[trade_type]['action'] == 'plus': new_balance = old_balance + interest + amount elif settings.TRADE_TYPE[trade_type]['action'] == 'subtract': new_balance = old_balance - interest - amount if new_balance < 0: balance = old_balance - interest print('你可以%s %s' % (trade_type, balance)) return {'status': 1, 'error': '交易失败,余额不足'} # 写入文件 account_data['data']['balance'] = new_balance data = save_db(account_data) # 加入日志 logger.info('account:%s action:%s amount:%s interest:%s' % (account_data['data']['id'], trade_type, amount, interest)) return {'status': 0, 'msg': '交易成功', 'data': data} else: print('Trade type %s is not exist' % trade_type) return {'status': 1, 'error': '交易失败,交易类型不存在'} def msg(account_data, *args, **kwargs): """打印账户信息""" print('账号信息'.center(40, '-')) account_dic = account_data['data'] for k, v in enumerate(account_dic): if v not in 'password': print('%15s : %s' % (v, account_dic[v])) print('End'.center(40, '-')) def withdraw(account_data, logger, *args, **kwargs): """提现操作""" withdraw_msg = ''' --------余额信息------- 额度: %s 余额: %s ''' % (account_data['data']['credit'], account_data['data']['balance']) print(withdraw_msg) while True: withdraw_amount = input('请输入提现整数金额|b 退出:').strip() if len(withdraw_amount) > 0 and withdraw_amount.isdigit(): withdraw_amount = float(withdraw_amount) if withdraw_amount < account_data['data']['balance']: trade_res = trade(withdraw_amount, 'withdraw', account_data, logger) if trade_res['status'] == 0: account_data = trade_res['data'] print("New balance:%s" % account_data['data']['balance']) else: print(trade_res['error']) else: print('余额不足,请重新输入') elif withdraw_amount == 'b': break def transfer(account_data, logger, *args, **kwargs): """转账操作""" while True: trans_account = input('请输入转账账户|b 退出:').strip() if trans_account == 'b': break else: if trans_account not in account_data['data']['id']: find_res = load_account_data(trans_account) if find_res['status'] == 0: while True: trans_amount = input('转账金额|b 退出:').strip() if len(trans_amount) > 0 and trans_amount.isdigit(): trans_account = float(trans_amount) # 开始转账 account trade_res = trade(trans_account, 'transfer', account_data, logger) if trade_res['status'] == 0: # 对方账户相当于增加钱 trade(trans_account, 'transferred', find_res, logger) account_data = trade_res['data'] print("New balance:%s" % account_data['data']['balance']) else: print(trade_res['error']) elif trans_amount == 'b': break else: print(find_res['error']) else: print('this account is yours') def repay(account_data, logger, *args, **kwargs): """还款操作""" pay_msg = ''' --------余额信息------- 额度: %s 余额: %s ''' % (account_data['data']['credit'], account_data['data']['balance']) print(pay_msg) while True: pay_amount = input('请输入还款金额|b 退出:').strip() if len(pay_amount) > 0 and pay_amount.isdigit(): pay_amount = float(pay_amount) trade_res = trade(pay_amount, 'repay', account_data, logger) if trade_res['status'] == 0: account_data = trade_res['data'] print("New balance:%s" % account_data['data']['balance']) else: print(trade_res['error']) elif pay_amount == 'b': break
# -*- coding:utf-8 -*- from . import atm_logics def main(account_data, logger, *args, **kwargs): """atm模块分发器,进行分发""" fun_dict = { '1': atm_logics.msg, '2': atm_logics.withdraw, '3': atm_logics.transfer, '4': atm_logics.repay, } while True: msg = ''' ----ATM --- 1、查看信用卡信息 2、提现 3、转账 4、还款 5、返回上一层 ''' print(msg) choice = input(">>>").strip() if choice in fun_dict: fun_dict[choice](account_data, logger, *args, **kwargs) elif choice == '5': break else: print('请重新输入')
# -*- coding:utf-8 -*- from .db_handler import load_account_data def auth(*args): """根据account,password认证用户""" account, password = args account_data = load_account_data(account) if account_data['status'] == 0: account_data = account_data['data'] if password == account_data['password']: return account_data else: return None else: return None
# -*- coding:utf-8 -*- import os import json from conf import settings def load_account_data(account): """根据输入的account,查找用户文件""" account_file = settings.DB_PATH + "%s.json" % account # account文件路径 if os.path.isfile(account_file): with open(account_file, 'r', encoding='utf8') as f: data = json.load(f) return {'status': 0, 'data': data} else: return {'status': -1, 'error': 'account file is not exist'} def save_db(account_data): """根据account_data,找到对应的json文件,写入文件中""" data = account_data['data'] account_file = settings.DB_PATH + "%s.json" % data['id'] # account文件路径 if os.path.isfile(account_file): new_file = account_file + '.new' with open(new_file, 'w', encoding='utf8') as f: json.dump(data, f) os.replace(new_file, account_file) return {'status': 0, 'data': data} else: return {'status': -1, 'error': 'account file is not exist'} def save_account(account): """写入新用户数据,创建文件""" data = {"expire_date": "2021-01-01", "credit": 15000, "status": 0, "id": account, "password": "abc", "enroll_date": "2016-01-02", "balance": 15000, "pay_day": 22} account_file = settings.DB_PATH + "%s.json" % account print(account_file) with open(account_file, 'w', encoding='utf8') as f: json.dump(data, f) f.flush() # 对应新建的用户文件,如何实时刷入硬盘中 return {'status': 0, 'data': data}
# -*- coding:utf-8 -*- import logging from conf import settings def log_handle(log_type): logger = logging.getLogger(log_type) logger.setLevel(settings.LOG_LEVEL) log_file_name = settings.LOG_PATH + log_type + ".log" fh = logging.FileHandler(log_file_name) fh.setLevel(settings.LOG_LEVEL) logger.addHandler(fh) fh.setFormatter(settings.file_formatter) return logger
# -*- coding:utf-8 -*- from .authentication import auth from .log import log_handle from .shopping import main as shop_main from .atm_manage import main as atm_main from .account import account_manage from conf import settings atm_logger = log_handle('atm') login_logger = log_handle('login') shop_logger = log_handle('shop') account_logger = log_handle('account') def login_type(auth_type): def login(fun): if auth_type == 'user_auth': def inner(): user_obj = { 'is_authed': False, 'data': None } count = 0 while True: account = input('请输入账号:').strip() password = input('请输入密码:').strip() # 普通用户 auth_data = auth(account, password) # 认证 if auth_data: if auth_data['status'] == 0: user_obj['is_authed'] = True user_obj['data'] = auth_data # 登录成功,加入日志 login_logger.info('user %s login ' % account) print('welcome to ATM & SHOP') fun(user_obj, atm_logger, shop_logger) else: print('该用户已经被冻结') login_logger.info('cold_user %s login ' % account) else: login_logger.info('user %s try wrong to log ' % account) print('error account or password') count += 1 if count == 3: msg = 'user %s is try more times' % account print(msg) # 登录不成功,加入日志 login_logger.info('user %s try wrong to log reached 3 times' % account) break return inner elif auth_type == 'admin_auth': def inner(): admin_user = settings.ADMIN_USER admin_pwd = settings.ADMIN_PASSWORD count = 0 while True: account = input('请输入账号:').strip() password = input('请输入密码:').strip() if account == admin_user and password == admin_pwd: # 登录成功,加入日志 login_logger.info('user %s login ' % account) print('welcome to account manage') fun(account, account_logger) else: login_logger.info('user %s try wrong to log ' % account) print('error account or password') count += 1 if count == 3: msg = 'user %s is try more times' % account print(msg) # 登录不成功,加入日志 login_logger.info('user %s try wrong to log reached 3 times' % account) break return inner return login @login_type('user_auth') def identity(user_obj, atm_logger, shop_logger): """用户认证成功""" atm_mall(user_obj, atm_logger, shop_logger) @login_type('admin_auth') def admin(account, account_logger): """管理员认证成功""" account_manage(account, account_logger) def run(): fun_dict = { '1': admin, '2': identity, } while True: msg = ''' --------- 1.管理员 2.普通用户 3.退出 ''' print(msg) choice = input(">>>").strip() if choice in fun_dict: fun_dict[choice]() elif choice == '3': exit() else: print('请重新输入') def atm_mall(account_data, logger, logger_shop, *args, **kwargs): """atm——mall程序入口""" fun_dict = { '1': shop_main, '2': atm_main, } while True: msg = ''' ----ATM & SHOP--- 1、购物商城 2、ATM 3、退出 ''' print(msg) choice = input(">>>").strip() if choice in fun_dict: fun_dict[choice](account_data, logger, logger_shop) elif choice == '3': exit() else: print('请重新输入')
# -*- coding:utf-8 -*- from .atm_logics import * def main(account_data, logger, logger_shop, *args): goods = [ {"name": "电脑", "price": 1999}, {"name": "鼠标", "price": 10}, {"name": "游艇", "price": 20}, {"name": "美女", "price": 998} ] shopping_cart = [] cost = 0 # 2.商品menu循环 while True: print("----------商品列表 --------") for index, item in enumerate(goods): print("%s %s %s" % (index, item['name'], item['price'])) choice = input("