27-ATM+购物车程序
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("-----------\n账户 信用额度 是否冻结 ") 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("\033[1;34m输入你要买的商品编号|返回上一层q:\033[0m").strip() if choice.isdigit(): choice = int(choice) if choice < len(goods): shopping_cart.append([goods[choice]['name'], goods[choice]['price']]) print('\033[1;32m>你购买了%s\033[0m' % goods[choice]['name']) cost += goods[choice]['price'] else: print('\033[1;31m你输入的商品不存在\033[0m') elif choice == 'q': if len(shopping_cart) > 0: print("\033[1;32m------你的购物车---------") for index, item in enumerate(shopping_cart): print(index, item[0], item[-1]) print("------------------------") print("你的账单:%s\033[0m" % cost) while True: choice = input('Do you want to pay? y|n :').strip() if choice.lower() == 'y': print('正在调用付款接口...') pay_res = trade(cost, 'pay', account_data, logger) if pay_res['status'] == 0: print(pay_res['msg']) # pay成功,写入日志 for i in shopping_cart: logger_shop.info('account:%s buy:%s cost:%s' % (account_data['data']['id'], i, cost)) else: print(pay_res['error']) print('请重新购物') break elif choice.lower() == 'n': print('请重新购物') break else: print('请重新输入') # 支付成功,购物车清零 shopping_cart = [] cost = 0 else: break else: print('\033[1;31;47m你输入的有误,请重新输入\033[0m')
{"id": "alex", "pay_day": 22, "expire_date": "2021-01-01", "credit": 15000, "status": 0, "enroll_date": "2016-01-02", "balance": 11768.0, "password": "abc"}
{"status": 0, "credit": 15000, "password": "abc", "pay_day": 22, "balance": 346.8999999999878, "id": "jack", "expire_date": "2021-01-01", "enroll_date": "2016-01-02"}
2018-03-01 16:59:14,767 - account - INFO - account:alex1 be added by admin 2018-03-01 16:59:21,329 - account - INFO - account:alex be frozen by admin 2018-03-01 16:59:28,051 - account - INFO - account:jack be frozen by admin 2018-03-01 21:45:09,287 - account - INFO - account: be added by admin 2018-03-01 21:49:29,694 - account - INFO - account:alex be frozen by admin 2018-03-01 21:49:47,597 - account - INFO - account:cat be added by admin 2018-03-02 10:26:48,621 - account - INFO - account:dog be added by admin 2018-03-02 10:34:23,346 - account - INFO - account:vvvv be added by admin 2018-03-02 10:49:29,096 - account - INFO - account:llll be added by admin 2018-03-02 10:53:21,614 - account - INFO - account:shengge be added by admin 2018-03-02 10:53:54,387 - account - INFO - account:tom1 be added by admin
2018-02-27 16:16:54,125 - atm - INFO - account:jack action:withdraw amount:100.0 interest:5.0 2018-02-27 16:17:10,761 - atm - INFO - account:jack action:pay amount:100.0 interest:0.0 2018-02-27 16:17:23,475 - atm - INFO - account:jack action:pay amount:100.0 interest:0.0 2018-02-27 16:17:31,526 - atm - INFO - account:jack action:trans amount:100.0 interest:0.0 2018-02-27 16:18:32,499 - atm - INFO - account:alex action:trans amount:100.0 interest:0
2018-02-26 17:25:17,207 - login - INFO - user jack logg in 2018-02-26 17:31:35,451 - login - INFO - user jack logg in 2018-02-27 00:03:18,650 - login - INFO - user ale try wrong to log 2018-02-27 00:03:21,692 - login - INFO - user alex logg in 2018-02-27 00:05:49,870 - login - INFO - user jack logg in 2018-02-27 00:07:57,278 - login - INFO - user jack try wrong to log 2018-02-27 00:07:59,868 - login - INFO - user jack logg in 2018-02-27 00:15:03,256 - login - INFO - user jack logg in
2018-02-28 12:09:44,197 - shop - INFO - account:jack buy:['鼠标', 10] cost:30 2018-02-28 12:09:44,199 - shop - INFO - account:jack buy:['游艇', 20] cost:30 2018-02-28 12:10:54,623 - shop - INFO - account:tom buy:['游艇', 20] cost:50 2018-02-28 12:10:54,623 - shop - INFO - account:tom buy:['鼠标', 10] cost:50 2018-02-28 12:10:54,623 - shop - INFO - account:tom buy:['游艇', 20] cost:50 2018-03-01 21:48:25,825 - shop - INFO - account:alex buy:['电脑', 1999] cost:3027 2018-03-01 21:48:25,825 - shop - INFO - account:alex buy:['鼠标', 10] cost:3027 2018-03-01 21:48:25,825 - shop - INFO - account:alex buy:['游艇', 20] cost:3027 2018-03-01 21:48:25,825 - shop - INFO - account:alex buy:['美女', 998] cost:3027
作者:Liangshuo 版本:示例版本 v1.0 本章作业: 模拟实现一个ATM + 购物商城程序 额度 15000或自定义 实现购物商城,买东西加入 购物车,调用信用卡接口结账 可以提现,手续费5% 支持多账户登录 支持账户间转账 记录每月日常消费流水 提供还款接口 ATM记录操作日志 提供管理接口,包括添加账户、用户额度,冻结账户等。。。 用户认证用装饰器 示例代码 https://github.com/triaquae/py3_training/tree/master/atm 简易流程图:https://www.processon.com/view/link/589eb841e4b0999184934329 # 软件组织结构 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
chardet==3.0.4 ipython==2.3.1 pyreadline==2.1
5.演示效果
(1)admin管理账户
(2)普通用户购物商城
(3)普通用户ATM