Python学习--------------Atm+购物车系统
一、程序需求
模拟实现一个ATM + 购物商城程序:
1.额度 15000或自定义
2.实现购物商城,买东西加入 购物车,调用信用卡接口结账
3.可以提现,手续费5%
4.每月22号出账单,每月10号为还款日,过期未还,按欠款总额 万分之5 每日计息(没写)
5.支持多账户登录
6.支持账户间转账
7.记录每月日常消费流水
8.提供还款接口
9.ATM记录操作日志
10.提供管理接口,包括添加账户、用户额度,冻结账户等。。。
11.用户认证用装饰器
脑图:
二、目录
1 ├── ATM #ATM主程目录 2 │ ├── __init__.py 3 │ ├── bin #ATM 执行文件 目录 4 │ │ ├── __init__.py 5 │ │ ├── atm.py #ATM 执行程序 6 │ │ ├── manage.py #信用卡管理 7 │ ├── conf #配置文件 8 │ │ ├── __init__.py 9 │ │ └── Settings.py #配置参数 10 │ ├── core #主要程序逻辑都 在这个目录 里 11 │ │ ├── __init__.py 12 │ │ ├── accounts.py #用于从文件里加载和存储账户数据 13 │ │ ├── auth.py #用户认证模块及主要功能函数 14 │ │ ├── db_handler.py #数据库连接引擎 15 │ │ ├── logger.py #日志记录模块 16 │ │ ├── main.py #主逻辑交互程序 17 │ │ ├── transaction.py #记账\还钱\取钱\与账户金额相关的操作,冻结或者锁定用户 18 │ ├── db #用户数据存储的地方 19 │ │ ├── __init__.py 20 │ │ ├── account_sample.py #生成一个初始的账户数据 ,把这个数据 存成一个 以这个账户id为文件名的文件,放在accounts目录 就行了,程序自己去会这里找 21 │ │ └── accounts #存各个用户的账户数据 ,一个用户一个文件 22 │ │ └── 123.json #新创建的用户账户示例文件 23 │ │ └── 1234.json #一个用户账户示例文件 24 │ │ └── 123456.json #一个用户账户示例文件 25 │ │ └── 6230001.json #管理用户账户示例文件 26 │ └── log #日志目录 27 │ ├── access.log #用户访问和操作的相关日志 28 │ └── login_in.log #登陆日志 29 └── shopping_mall #电子商城程序,需单独实现,主要实现购物的功能。 30 │ └── __init__.py 31 │ └── product.txt #存放商品的txt文件 32 │ └── shopping_list.txt #存放购物清单的txt.文件 33 │ └── shopping_mall.py #购物商城程序 34 ├── README
三、简要说明
1.程序从/bin/atm.py开始执行if __name__ == '__main__':
main.run()
2.程序转到/core/main.py下的run()函数,登陆时调用/core/auth的acc_login()进行登陆验证:用到了/core/auth下的acc_auth2()方法进行验证(此时传入的参数时用户输入的账户和密码)
acc_auth2中有调用了/core/db_handler下的db_handler()方法(参数是输入的账户名)在db_handler中只是进行判断是什么引擎,return file_db_handle(数据库引擎)解析文件,返回文件执行加载输入的用户的账户的所有数据
接下来判断是否为管理者账户,或者是否被冻结,若都不是,则判断输入的密码是否与数据库中的密码一样,在判断到期时间是否过期
所有都通过的话就返回这个账户的数据,之前已经创建了一个空字典,里面有是否验证:用户数据:用户账户:,判断是否被验证过,然后把用户数据临时的传递到里面,执行主循环函数
可以选择进入到购物商城,或者信用卡操作或者退出
1)购物商城
调用/shopping_mall/shopping_mall.py文件执行,主循环函数,选择你是商家还是用户,
①如果选择商家,商家有增加商品修改商品的功能
②如果选择用户,用户则有购物,刷信用卡消费的功能,当退出时打印消费清单
2)信用卡操作
调用/core/main.py下interactive(用户的所有数据)调用主循环函数,可以打印账户信息、还款、取款、转账、账单、退出等操作
①账户信息
②还款
③取款
④转账
⑤账单
⑥退出
3)若在账户登陆的时候进行输入的时管理员账户调用/bin/manage.py则可以对用户进行管理,解冻 用户、冻结用户、申领新卡
①添加账户
②冻结账户
③解冻账户
④退出
四、主程序
1.bin目录下代码
1 '''/bin/atm.py''' 2 3 4 #!/usr/bin/env python 5 #-*- Coding:utf-8 -*- 6 # Author:Eric.Shen 7 import os,sys 8 base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 9 #print(base_dir) 10 sys.path.append(base_dir) 11 from core import main 12 13 14 if __name__ == '__main__': 15 main.run() 16 17 18 19 20 21 22 23 '''/bin/manage.py''' 24 25 26 27 #!/usr/bin/env python 28 #-*- Coding:utf-8 -*- 29 # Author:Eric.Shen 30 #管理端(提供管理接口,包括添加账户、用户额度,冻结账户) 31 #解冻账户 32 #from core.auth import login_required 33 from core import accounts 34 from core import transaction 35 #解冻账户 36 def unblock_account(acc_data): 37 user_input = input("请输入你要解冻的用户:") 38 flag = 0 39 #锁定用户 40 val = transaction.lock_or_not(user_input,flag) 41 if val == 0: 42 print("解冻成功!") 43 return 44 #冻结账户 45 def block_account(acc_data): 46 ''' 47 冻结账户初步构想是,在linux里把他的权限改掉; 48 或者将其文件改名 49 :param acc_data: 50 :return: 51 ''' 52 user_input = input("请输入你要冻结的用户:") 53 flag = 1 54 #锁定用户 55 val = transaction.lock_or_not(user_input,flag) 56 if val == 0: 57 print("冻结成功!") 58 return 59 60 #添加账户、用户额度 61 def add_account(acc_data): 62 account = { 63 "id": None, 64 "balance": None, 65 "expire_date": None, 66 "enroll_date": None, 67 "credit": None, 68 "pay_day": None, 69 "password": None, 70 "status": None 71 } 72 menu = { 73 0: "账户(数字):", 74 1: "余额:", 75 2: "到期时间:", 76 3: "办卡时间:", 77 4: "信用额度:", 78 5: "还款日期:", 79 6: "密码:", 80 7: "默认:"} 81 menu_user = { 82 0: "id", 83 1: "balance", 84 2: "expire_date", 85 3: "enroll_date", 86 4: "credit", 87 5: "pay_day", 88 6: "password", 89 7: "status" 90 } 91 for i in range(8): 92 data = input("%s" % menu[i]).strip() 93 account['%s' % menu_user[i]] = data 94 accounts.dump_account(account)#写入文件 95 print("创建成功!") 96 return 97 98 99 100 def logout(acc_data): 101 exit("程序退出!") 102 #管理界面主程序 103 def manage_main(acc_data): 104 105 menu = u''' 106 ---------管理界面--------- 107 1.添加账户 108 2.冻结账户 109 3.解冻账户 110 4.退出''' 111 menu_dic = { 112 '1': add_account, 113 '2': block_account, 114 '3': unblock_account, 115 '4': logout 116 } 117 exit_flag = False 118 while not exit_flag: 119 print(menu) 120 user_option = input("请输入你的选择:") 121 if user_option in menu_dic: 122 menu_dic[user_option](acc_data) 123 else: 124 print("\033[31;1m选择不存在!\033[0m") 125 126 127 128 129 .
2.conf目录下代码
1 '''/conf/Settings.py''' 2 3 4 #!/usr/bin/env python 5 #-*- Coding:utf-8 -*- 6 # Author:Eric.Shen 7 #参数配置文件 8 import os,sys,logging 9 10 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))#/Atm 11 12 DATABASE = { 13 'engine': 'file_storage', 14 'name': 'accounts', 15 'path': "%s/db" % BASE_DIR#../Atm 16 } 17 18 LOG_LEVEL = logging.INFO 19 LOG_TYPES = { 20 'transaction': 'transaction.log', 21 'access': 'access.log', 22 } 23 24 #发生交易的配置类型 25 TRANSACTION_TYPE = { 26 'repay':{'action':'plus','interest':0},#还款 27 'withdraw':{'action':'minus','interest':0.05},#取现是降低可用余额 28 'transfer':{'action':'minus','interest':0.05},#转账是降低可用余额 29 'consume':{'action':'minus','interest':0}, 30 }
3.core目录下代码
1 '''/core/accounts.py''' 2 3 4 #!/usr/bin/env python 5 #-*- Coding:utf-8 -*- 6 # Author:Eric.Shen 7 #用于从文件里加载和存储账户数据 8 import json,time 9 from core import db_handler 10 from conf import Settings 11 12 #返回账户余额和其他基础信息(返回最新的数据) 13 def load_current_balance(account_id): 14 ''' 15 返回账户余额和其他基础信息 16 :param account_id: 用户账户的名字 17 :return: 返回最新读到的数据文件中的最新数据 18 ''' 19 # db_path = db_handler.db_handler(settings.DATABASE) 20 # account_file = "%s/%s.json" %(db_path,account_id) 21 # 22 db_api = db_handler.db_handler() 23 data = db_api("select * from accounts where account=%s" % account_id)#在进行操作的时候在读取一遍数据中的数据(保证数据的最新) 24 return data#返回读取到的数据 25 26 # with open(account_file) as f: 27 # acc_data = json.load(f) 28 # return acc_data 29 30 #写入文件数据 31 def dump_account(account_data): 32 ''' 33 34 :param account_data: 35 :return: 36 ''' 37 db_api = db_handler.db_handler() 38 data = db_api("update accounts where account=%s" % account_data['id'],account_data = account_data) 39 40 # db_path = db_handler.db_handler(settings.DATABASE) 41 # account_file = "%s/%s.json" %(db_path,account_data['id']) 42 # with open(account_file, 'w') as f: 43 # acc_data = json.dump(account_data,f) 44 return True
1 '''/core/auth.py''' 2 3 4 #!/usr/bin/env python 5 #-*- Coding:utf-8 -*- 6 # Author:Eric.Shen 7 #用户认证模块 8 import json,time,os 9 from core import db_handler 10 from bin import manage 11 from conf import Settings 12 from core import logger 13 #装饰器(用于验证账户是否登陆过) 14 def login_required(func): 15 ''' 16 验证用户是否登陆 17 :return: 18 ''' 19 def wrapper(*args,**kwargs): 20 if args[0].get('is_authenticated'): 21 return func(*args,**kwargs) 22 else: 23 exit("用户不能认证") 24 return wrapper 25 26 def acc_auth(account,password): 27 ''' 28 账户验证函数 29 :return: 30 ''' 31 db_path = db_handler.db_handler() 32 account_file = "%s/%s.json" %(db_path,account) 33 print(account_file) 34 if os.path.isfile(account_file): 35 with open(account_file,'r') as f: 36 account_data = json.load(f) 37 if account_data['password'] == password: 38 exp_time_stamp = time.mktime(time.strptime(account_data['expire_date'], "%Y-%m-%d")) 39 if time.time() >exp_time_stamp: 40 print("\033[31;1m[%s]账户已经注销,请重新申领账户!\033[0m" % account) 41 else: #passed the authentication 42 return account_data 43 else: 44 print("\033[31;1m账号或密码错误,请重新输入!\033[0m") 45 else: 46 print("\033[31;1m[%s]账户不存在!\033[0m" % account) 47 48 def acc_auth2(account,password): 49 ''' 50 优化版认证接口 51 :param 52 account:信用卡账户 53 password:信用卡密码 54 :return: 返回读取到的数据文件的所有账户数据 55 ''' 56 db_api = db_handler.db_handler() 57 data = db_api("select * from accounts where account=%s" %account)#此处返回值为db_handler.py中的 58 # 得到的所有数据(读取到的这个账户的所有数据)赋值给data 59 if data["status"] == 2:#判断是否为管理者 60 manage.manage_main(data) 61 if data['status'] == 1: 62 print("你的账户已经被冻结,请联系管理员!\n") 63 option = input("请按b退出!") 64 if option == "b": 65 exit("程序已经退出!") 66 if data['password'] == password:#判断data中的password数据是否恒等于输入的password(此处如果继续执行,则账户密码完全正确) 67 #time.mktime 返回用秒数来表示时间的浮点数。 68 #实例结果:time.mktime(t) : 1234915418.000000 69 #time.strptime 根据指定的格式把一个时间字符串解析为时间元组 70 #实例结果:time.strptime(string[, format]) 71 exp_time_stamp = time.mktime(time.strptime(data['expire_date'],"%Y-%m-%d"))#将数据文件中的expire_data时间 72 # 转为以秒计数的时间赋值给exp_time_stamp 73 if time.time() > exp_time_stamp:#判断当前以秒计算的数据是否大于数据文件中的数据 74 print("\033[31;1m[%s]账户以及过期,请重新激活!\033[0m" % account) 75 else: 76 return data#没有超时,则返回读取到的数据文件的所有内容 77 else: 78 print("\033[31;1m帐户名或者密码错误!\033[0m") 79 80 81 def acc_login(user_data,log_obj): 82 ''' 83 账户登陆函数 84 :param 85 user_data:用户信息数据,只存在内存中 86 :return: 账户密码都对的情况下,返回所有账户数据 87 ''' 88 retry_count = 0#初始化重试次数 89 while user_data['is_authenticated'] is not True and retry_count < 3:#如果没有验证过,或循环此时没超过三次就执行下面的 90 account = input("\033[32;1m账户:\033[0m").strip()#输入账户 91 password = input("\033[32;1m密码:\033[0m").strip()#输入密码 92 auth = acc_auth2(account,password)#解耦,将输入的账户和密码传入到acc_auth2函数中,进行验证 93 # (最后返回的是读取到的输入正确账户的所有数据)赋值给auth 94 if auth: 95 user_data['is_authenticated'] = True#登陆成功,将只存在与内存中的数据中的是否验证改为True 96 user_data['account_id'] = account#将只存在与内存中的数据中的账户id改为账户名字(开始输入的帐户名) 97 return auth#这一步操作就是验证此账户是否登陆,然后返回账户的所有数据(数据文件中的所有数据) 98 retry_count += 1 99 else: 100 log_obj.error("[%s]账户太多次尝试" % account) 101 exit()
1 '''/core/db_handler.py''' 2 3 4 5 #!/usr/bin/env python 6 #-*- Coding:utf-8 -*- 7 # Author:Eric.Shen 8 #数据库连接引擎 9 #处理所有数据库交互 10 import json,time,os 11 from conf import Settings 12 13 #解析文件数据路径 14 def file_db_handle(conn_params): 15 ''' 16 解析数据库文件路径 17 :return: 18 ''' 19 #print('file db:',conn_params) 20 return file_execute 21 22 #数据库句柄 23 def db_handler(): 24 ''' 25 连接数据库 26 :return: 27 ''' 28 conn_params = Settings.DATABASE#把Settings下的DATABASE的数据赋值给conn_params 29 if conn_params['engine'] == 'file_storage':#判断Settings下的DABASE是什么引擎,这里只用文件文件引擎 30 return file_db_handle(conn_params)#则把Settings下的DABASE的数据传给file_db_handle并返回 31 elif conn_params['engine'] == 'mysql': 32 pass#支持扩展,此次只作为一个说明 33 34 #文件执行 35 def file_execute(sql,**kwargs): 36 ''' 37 传入sql语句,及其他变量, 38 :param sql: sql语句操作得到结果 39 :param kwargs: 其他得变量 40 :return: 41 ''' 42 conn_params = Settings.DATABASE#把Settings下的DATABASE的数据赋值给conn_params,再一次赋值意味着得到最新得数据 43 db_path = '%s/%s' % (conn_params['path'],conn_params['name'])#数据库的文件路径 ../db/accounts 44 #print(sql,db_path)#sql = select * from accounts where account=%s %account(此时这个account等于程序开始时要求哟用户输入得数据) 45 sql_list = sql.split('where')#将上面得sql语句以where分开,(sql_list列表内容:'select * from accounts' ,"account='account' ") 46 #print(sql_list) 47 #startswith() 方法用于检查字符串是否是以指定子字符串开头, 48 # 如果是则返回 True,否则返回False。如果参数 beg 和 end 指定值, 49 # 则在指定范围内检查。 50 if sql_list[0].startswith('select') and len(sql_list) > 1:#判断sql_list列表中得第一个字符是select并且列表的长度是大于1的 51 column,val = sql_list[1].strip().split('=')#将sql_list列表第二个数据先去掉默认空格,并且以‘=’为界分开放入--》 52 #-->column = account , val = '此处为开始程序输入的账户' 53 #Python strip() 方法用于移除字符串头尾指定的字符(默认为空格)。 54 if column == 'account':#判断是否为account,然后做指定的操作(这里使用的是account) 55 account_file = '%s/%s.json' % (db_path,val)#这一步得到数据文件路径的文件绝对路径 56 #print(account_file) 57 if os.path.isfile(account_file):#使用绝对路径判断是否为文件,返回True 58 with open(account_file,'r') as f:#以只对的方式打开文件并把文件句柄赋值给f(用with方法打开不用自己写关闭文件的方法) 59 account_data = json.load(f)#json加载文件赋值给account_data 60 return account_data#返回account_data数据(将.json文件中的数据都都出来返回) 61 else: 62 exit("\033[31;1m[%s]账户不存在!\033[0m" % val)#若判断不是,则返回没有此用户 63 #写入数据 64 elif sql_list[0].startswith('update') and len(sql_list) > 1: 65 column, val = sql_list[1].strip().split('=')#将帐户名写入到val中 66 if column == 'account': 67 account_file = "%s/%s.json" % (db_path,val) 68 # if os.path.isfile(account_file): 69 account_data = kwargs.get("account_data")#得到账户数据 70 with open(account_file,'w') as f: 71 acc_data = json.dump(account_data,f) 72 #print(acc_data) 73 return True
1 '''/core/logger.py''' 2 3 #!/usr/bin/env python 4 #-*- Coding:utf-8 -*- 5 # Author:Eric.Shen 6 #日志记录模块,处理所有日志工作 7 8 import logging 9 from conf import Settings 10 11 def logger(log_type): 12 #创建日志 13 logger = logging.getLogger(log_type) 14 logger.setLevel(Settings.LOG_LEVEL) 15 16 #创建控制台处理程序并将级别设置为调试 17 ch = logging.StreamHandler() 18 ch.setLevel(Settings.LOG_LEVEL) 19 #创建文件处理程序并设置级别为警告 20 log_file = "%s/logs/%s" %(Settings.BASE_DIR,Settings.LOG_TYPES[log_type]) 21 fh = logging.FileHandler(log_file) 22 fh.setLevel(Settings.LOG_LEVEL) 23 #创建格式化程序 24 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levename)s- %(message)s') 25 26 #添加格式化的CH和FH 27 ch.setFormatter(formatter) 28 fh.setFormatter(formatter) 29 30 #添加CH和FH到loggerh 31 logger.addHandler(ch) 32 logger.addHandler(fh) 33 34 return logger 35 36 #应用程序代码 37 '''logger.debug('debug message') 38 '''
1 #!/usr/bin/env python 2 #-*- Coding:utf-8 -*- 3 # Author:Eric.Shen 4 #记账\还钱\取钱\与账户金额相关的操作,冻结或者锁定用户 5 from conf import Settings 6 from core import accounts 7 from core import logger 8 9 def make_transaction(log_obj,account_data,tran_type,amount,**kwargs): 10 ''' 11 处理所有用户的所有交易 12 :param log_obj: 13 :param account_data: 用户最新的数据 14 :param tran_type: 交易类型 15 :param amount: 交易数量 16 :param other: 主要用于日志使用 17 :return: 返回最新的账户数据 18 ''' 19 amount = float(amount)#转换为浮点型 20 if tran_type in Settings.TRANSACTION_TYPE:#判断传入的类型是否在配置参数里面 21 interest = amount * Settings.TRANSACTION_TYPE[tran_type]["interest"]#根据交易类型计算利息赋值给interest 22 old_balance = account_data['balance']#读取数据中账户余额 23 #还款操作 24 if Settings.TRANSACTION_TYPE[tran_type]['action'] == 'plus':#因为是信用卡,所以还款时提升可使用余额的操作,故计为加plus 25 new_balance = old_balance + amount + interest#执行的是信用卡的还款操作,计算方法是,旧余额+还款的钱和利息=最后的账户可用余额 26 #取现\转账操作 27 elif Settings.TRANSACTION_TYPE[tran_type]['action'] == 'minus':#因为是信用卡,所以取现都是降低可用余额的操作 28 new_balance = old_balance - amount - interest 29 #只属于转账的 30 if kwargs.get('re_account'): 31 #print(kwargs[0],kwargs[1]) 32 re_account_data = accounts.load_current_balance(kwargs.get('re_account'))#得到要转入账户的所有数据 33 re_account_balance = re_account_data['balance'] + amount#得到转入账户余额的最新值 34 re_account_data['balance'] = re_account_balance#将最新的余额全部写入账户的余额中 35 print(re_account_data) 36 accounts.dump_account(re_account_data)#将最新的账户所有数据写入到文件中 37 if new_balance < 0: 38 print("\033[31;1m[%s]账户的信用余额不足以支付此次交易[-%s],你当前的余额是[%s]\033[0m" 39 % (account_data['creat'], (amount + interest), old_balance)) 40 return 41 #转账 42 ''' 43 elif Settings.TRANSACTION_TYPE[tran_type] == 'transfer' : 44 new_balance = old_balance - amount - interest#自己账户的最新余额 45 #读取转入的账户,写入转入金额 46 print(kwargs[0],kwargs[1]) 47 re_account_data = accounts.load_current_balance(kwargs.get('re_account'))#得到要转入账户的所有数据 48 re_account_balance = re_account_data['balance'] + amount#得到转入账户余额的最新值 49 re_account_data['balance'] = re_account_balance#将最新的余额全部写入账户的余额中 50 print(re_account_data) 51 accounts.dump_account(re_account_data)#将最新的账户所有数据写入到文件中 52 ''' 53 54 account_data['balance'] = new_balance#将最新的余额写入到账户数据中 55 print(account_data) 56 accounts.dump_account(account_data)#将最新的账户余额写回文件 57 #写入日志 58 #log_obj.info('账户:%s,操作:%s,数量:%s,利息:%s' %(account_data['id'],tran_type,amount,interest)) 59 return account_data 60 else: 61 print("\033[31;1m%s交易类型不存在\033[0m" % tran_type) 62 #冻结或者锁定用户 63 def lock_or_not(account,flag): 64 data = accounts.load_current_balance(account) 65 66 if data["status"] == 1: 67 print("该账户已经锁定!") 68 if data['status']: 69 data["status"] = flag 70 accounts.dump_account(data) 71 return 0
1 #!/usr/bin/env python 2 #-*- Coding:utf-8 -*- 3 # Author:Eric.Shen 4 #主程序句柄模块,处理所有用户交互内容 5 from core import auth 6 from core import accounts 7 from core import logger 8 from core import transaction 9 from core.auth import login_required 10 from shopping_mall import shopping_mall 11 from bin import manage 12 import time 13 14 15 #交易日志 16 trans_logger = logger.logger('transaction') 17 #访问日志 18 access_logger = logger.logger('access') 19 20 #临时账户数据,仅存在于内存中 21 user_data = { 22 'account_id':None, 23 'is_authenticated':False, 24 'account_data':None 25 } 26 27 #账户信息 28 def account_info(acc_data): 29 print(user_data) 30 #还款 31 @login_required#装饰器,判断用户是否登陆 32 def repay(acc_data): 33 ''' 34 打印当前余额,让用户偿还账单 35 :param acc_data: 36 :return: 37 ''' 38 account_data = accounts.load_current_balance(acc_data['account_id'])#将用户账户名字传入到load_current_balance中 39 #返回最新的用户数据赋值给 account_data 40 current_balance = ''' 41 ---------银行信息---------- 42 信用额度: %s 43 可用余额: %s 44 ''' %(account_data['credit'],account_data['balance']) 45 print(current_balance) 46 back_flag = False 47 while not back_flag: 48 repay_amount = input("\033[33;1m输入你要还款的金额:\033[0m").strip()#还款金额 49 if len(repay_amount) > 0 and repay_amount.isdigit(): 50 #print('ddd 00') 51 #将数据传入make_transaction中(交易日志,用户数据,交易类型,还款金额)进行操作,最后返回的是最新操作之后的账户数据 52 new_balance = transaction.make_transaction(trans_logger,account_data,'repay',repay_amount) 53 if new_balance: 54 print('''\033[42;1m最新的余额:%s\033[0m''' %(new_balance['balance'])) 55 else: 56 print('\033[31;1m[%s]是无效的账户!\033[0m' % repay_amount) 57 if repay_amount == 'b': 58 back_flag =True 59 #取款 60 @login_required 61 def withdraw(acc_data): 62 ''' 63 打印当前余额,让用户执行取款操作 64 :param acc_data: 65 :return: 66 ''' 67 account_data = accounts.load_current_balance(acc_data['account_id']) 68 # 将用户账户名字传入到load_current_balance中 69 # 返回最新的用户数据赋值给 account_data 70 current_balance = ''' --------- 银行信息 -------- 71 信用额度: %s 72 账户余额: %s''' % (account_data['credit'], account_data['balance']) 73 print(current_balance) 74 back_flag = False 75 while not back_flag: 76 withdraw_amount = input("\033[33;1m输入取款金额:\033[0m").strip() 77 if withdraw_amount == 'b': 78 return 79 if len(withdraw_amount) > 0 and withdraw_amount.isdigit(): 80 new_balance = transaction.make_transaction(trans_logger,account_data,'withdraw', withdraw_amount) 81 if new_balance: 82 print('''\033[42;1m最新余额:%s\033[0m''' %(new_balance['balance'])) 83 else: 84 print('\033[31;1m[%s]是无效的账户!\033[0m' % withdraw_amount) 85 86 87 #转账 88 @login_required 89 def transfer(acc_data): 90 ''' 91 打印当前余额,转账操作函数 92 :param acc_data:用户数据 93 :return: 94 ''' 95 account_data = accounts.load_current_balance(acc_data['account_id']) 96 # 将用户账户名字传入到load_current_balance中 97 # 返回最新的用户数据赋值给 account_data 98 current_balance = ''' --------- 银行信息 -------- 99 信用额度: %s 100 账户余额: %s''' % (account_data['credit'], account_data['balance']) 101 print(current_balance) 102 back_flag = False 103 while not back_flag: 104 reciprocal_account = input("\033[31;1m请输入对方帐户名:\033[0m").strip()#输入对方账户信息 105 transfer_amount = input("\033[31;1m转账金额:\033[0m").strip()#转账金额 106 if reciprocal_account or transfer_amount == 'b' : 107 return 108 if len(transfer_amount) > 0 and transfer_amount.isdigit(): 109 new_balance = transaction.make_transaction(trans_logger,account_data,'transfer', 110 transfer_amount,re_account = reciprocal_account) 111 if new_balance: 112 print("\033[41;1m转账成功!\033[0m") 113 print("\033[42;1m您当前的余额为:%s\033[0m" %(new_balance["balance"])) 114 else: 115 print('\033[31;1m[%s] \033[0m') 116 117 118 #账单 119 @login_required 120 def pay_check(acc_data): 121 pass 122 #退出 123 def logout(acc_data): 124 exit("程序已经退出!") 125 #购物商城 126 def shopping_mall_this(acc_data): 127 shopping_mall.main_menu(acc_data) 128 #管理窗口 129 def goto_manage(): 130 manage.manage_main(user_data) 131 #菜单 132 def interactive(acc_data): 133 ''' 134 与用户交互 135 :param acc_data: 验证过的用户的所用数据 136 :return: 137 ''' 138 menu = u''' 139 -----------银行---------- 140 \033[32;1m 141 1.账户信息 142 2.还款 143 3.取款 144 4.转账 145 5.账单 146 6.退出 147 \033[0m 148 ''' 149 menu_dic = { 150 '1': account_info, 151 '2': repay, 152 '3': withdraw, 153 '4': transfer, 154 '5': pay_check, 155 '6': logout, 156 } 157 exit_flag = False 158 while not exit_flag: 159 print(menu)#打印出菜单,供用户选择 160 user_option = input("请输入你的选择:").strip()#输入用户的选择,过滤掉空格 161 if user_option == 'b': 162 return 163 if user_option in menu_dic:#用户的选择如果在这个菜单里 164 #print('accdata',acc_data) 165 menu_dic[user_option](acc_data)#用户选择执行的功能,把acc_data验证过的用户的所有数据(数据文件中的数据) 166 else: 167 print("\033[31;1m选择不存在!\033[0m") 168 #带有购物商场的主菜单 169 def main_menu(acc_data): 170 main_menu = u''' 171 ----------主菜单--------- 172 \033[32;1m 173 1.购物商城 174 2.银行卡操作 175 3.退出 176 \033[0m 177 ''' 178 main_menu_dic = { 179 '1':shopping_mall_this, 180 '2':interactive, 181 '3':logout, 182 } 183 exit_flag = False 184 while not exit_flag: 185 print(main_menu) 186 user_option = input("请输入你的选择:").strip() 187 if user_option == 'b': 188 return 189 if user_option in main_menu_dic: 190 main_menu_dic[user_option](acc_data) 191 else: 192 print("\033[31;1m选择不存在!\033[0m") 193 def run(): 194 ''' 195 当程序启动时,这个程序开始运行,处理关于用户的所有交互的内容 196 ''' 197 acc_data = auth.acc_login(user_data,access_logger)#程序从这里开始,执行auth下的acc_login函数 198 # (返回的是验证过的正确的账户数据)赋值给acc_data(此时这里的数据为输入账户名字的数据文件的数据) 199 if user_data['is_authenticated']: 200 user_data['account_data'] = acc_data#把账户所有信息传给账户开始时的临时的账户数据空字典, 201 # 把所有的数据文件传给账户的账户数据里面, 202 #interactive(user_data)#把user_data里的所有数据传入菜单函数,进行下一步操作 203 main_menu(user_data)
4.db下目录下代码
/db/accounts/123.json
1 {"pay_day": "22", "enroll_date": "2018-02-19", "credit": "15000", "balance": "123", "id": "123", "expire_date": "2032-01-01", "password": "123", "status": 0}
5.logs目录下代码
/logs/access.log&transaction.log
6.shopping_mall下代码
/shopping_mall/product.txt&shopping_list.txt
1 #!/usr/bin/env python 2 #-*- Coding:utf-8 -*- 3 # Author:Eric.Shen 4 # !/usr/bin/env python 5 # -*- Coding:utf-8 -*- 6 # Author:Eric.Shen 7 # 2018.02.06 8 # path python3.5 9 # 优化版的购物车 10 # 用户入口: 11 # 1.商品的信息存到文件里 12 # 2.已购商品,余额记录 13 # 商家入口: 14 # 1.可以添加商品 2.修改商品价格 15 # 存储商品列表 16 import fileinput 17 from core import accounts 18 19 product_list = [] 20 f = open("D:\\Python_train\\day4\\Atm\\shopping_mall\\product.txt", "r") # 打开文件 21 for line in f.readlines(): 22 line = line.strip() # 去掉最后一个换行符 23 index, item = line.split(":") # 以冒号分割得到前后两个数据 24 product_list.append((index, item)) # 添加的数据 25 f.close() 26 27 28 def print_product_list(): 29 for index, item in enumerate(product_list): 30 print(index, item) 31 32 33 # 用户入口 34 # 用户购物 35 def user_shopping(account_data): 36 #salary = input("请输入你的薪水:") 37 salary = account_data['account_data']['balance'] 38 print_product_list() 39 if salary > 0: 40 shopping_list = [] # 存放用户购物车清单 41 while True: 42 option = input("喜欢那个就买哪个(对应的标号):") 43 if option.isdigit(): 44 option = int(option) 45 if option >= 0 and option <= len(product_list): 46 p_item = product_list[option] # 用户选择的商品 47 # print(product_list) 48 # print(p_item[1]) 49 c_num = int(p_item[1]) 50 if salary >= c_num: 51 shopping_list.append(p_item) 52 salary -= c_num 53 print("添加购物车成功,你的余额还有%s" % (salary)) 54 else: 55 print("你的余额不足,只剩%s元" % (salary)) 56 else: 57 print("输入错误,请重新输入!") 58 elif option == "q": 59 print("----------------购物清单---------------") 60 for s_list in shopping_list: 61 print(s_list) 62 print("你的余额为%s" % (salary)) 63 account_data['account_data']['balance'] = salary 64 #print(account_data) 65 accounts.dump_account(account_data['account_data'])#写入文件 66 print("..........exit.........") 67 exit() 68 else: 69 print("无效的输入") 70 else: 71 exit("余额不足!") 72 73 74 # 商家入口 75 # 商家添加商品 76 def add_product(): 77 name_of_product = input("请输入你要添加的商品名字:") 78 price_of_product = input("请输入你要添加商品的价格:") 79 f = open("product.txt", "a") 80 f.write(str("\n" + name_of_product) + ": %s" % (price_of_product)) 81 f.close() 82 print("添加成功!\nexit----------") 83 84 85 # 修改商品价格 86 def change_price(): 87 print_product_list() # 打印商品列表 88 choice = input("请输入你的选择:") 89 # name_of_change = input("请输入你要改变的商品名字") 90 price_of_change = input("请输入你要改变商品的价格:") 91 if choice.isdigit(): 92 choice = int(choice) 93 if choice >= 0 and choice <= len(product_list): 94 p_item = product_list[choice] # 选择的商品 95 # c_num = int(p_item[1])#转换成int类型 96 for line in fileinput.input("product.txt", inplace="%s" % (choice)): # 对输入的选择行进行修改 97 line = line.replace("%s" % (p_item[1]), "%s" % (price_of_change)).strip() 98 print(line) 99 exit("修改成功!") 100 else: 101 print("输入无效") 102 else: 103 if choice == "q": 104 exit("退出") 105 106 107 def main_menu(account_data): 108 print("--------------------------" 109 "--------------------------" 110 "\n" 111 " 欢迎进入购物菜单 " 112 "\n" 113 "\n" 114 "商家请按b,用户请按c\n" 115 "--------------------------" 116 "--------------------------") 117 c_num = input("请输入你的选择:") # 使用者选择 118 if c_num == "b": 119 print("--------------------------" 120 "--------------------------" 121 "\n" 122 " 欢迎进入商家界面 " 123 "\n" 124 "\n" 125 "添加商品请按a,修改价格请按c\n" 126 "--------------------------" 127 "--------------------------") 128 c_num2 = input("请输入你的选择:") 129 if c_num2 == "a": 130 # 实现添加商品功能 131 add_product() 132 if c_num2 == "c": 133 # 实现商品价格修改功能 134 change_price() 135 else: 136 print("输入有误!") 137 if c_num == "c": 138 print("--------------------------" 139 "--------------------------" 140 "\n" 141 " 欢迎进入用户界面 " 142 "\n" 143 "\n" 144 145 "--------------------------" 146 "--------------------------") 147 # 购物功能 148 print(account_data) 149 user_shopping(account_data) 150 else: 151 print("输入有误程序退出!")
五、README
1 作者:Eric.shen 2 此次系统的设计仅用来学习python,开始于2018.2.13-19完(此系统,日志部分没有完善留着日后补充现在还没学到) 3 作业需求: 4 5 模拟实现一个ATM + 购物商城程序: 6 1.额度 15000或自定义 7 2.实现购物商城,买东西加入 购物车,调用信用卡接口结账 8 3.可以提现,手续费5% 9 4.每月22号出账单,每月10号为还款日,过期未还,按欠款总额 万分之5 每日计息(没写) 10 5.支持多账户登录 11 6.支持账户间转账 12 7.记录每月日常消费流水 13 8.提供还款接口 14 9.ATM记录操作日志 15 10.提供管理接口,包括添加账户、用户额度,冻结账户等。。。 16 11.用户认证用装饰器 17 18 一、软件定位,软件的基本功能。 19 实现一个简单的atm与购物车程序, 20 二、运行代码的方法: 安装环境、启动命令等。 21 用Python3.5写的,语法就是至此之前所学的,直接打开运行即可 22 三、目录结构。 23 24 ├── ATM #ATM主程目录 25 │ ├── __init__.py 26 │ ├── bin #ATM 执行文件 目录 27 │ │ ├── __init__.py 28 │ │ ├── atm.py #ATM 执行程序 29 │ │ ├── manage.py #信用卡管理 30 │ ├── conf #配置文件 31 │ │ ├── __init__.py 32 │ │ └── Settings.py #配置参数 33 │ ├── core #主要程序逻辑都 在这个目录 里 34 │ │ ├── __init__.py 35 │ │ ├── accounts.py #用于从文件里加载和存储账户数据 36 │ │ ├── auth.py #用户认证模块及主要功能函数 37 │ │ ├── db_handler.py #数据库连接引擎 38 │ │ ├── logger.py #日志记录模块 39 │ │ ├── main.py #主逻辑交互程序 40 │ │ ├── transaction.py #记账\还钱\取钱\与账户金额相关的操作,冻结或者锁定用户 41 │ ├── db #用户数据存储的地方 42 │ │ ├── __init__.py 43 │ │ ├── account_sample.py #生成一个初始的账户数据 ,把这个数据 存成一个 以这个账户id为文件名的文件,放在accounts目录 就行了,程序自己去会这里找 44 │ │ └── accounts #存各个用户的账户数据 ,一个用户一个文件 45 │ │ └── 123.json #新创建的用户账户示例文件 46 │ │ └── 1234.json #一个用户账户示例文件 47 │ │ └── 123456.json #一个用户账户示例文件 48 │ │ └── 6230001.json #管理用户账户示例文件 49 │ └── log #日志目录 50 │ ├── access.log #用户访问和操作的相关日志 51 │ └── login_in.log #登陆日志 52 └── shopping_mall #电子商城程序,需单独实现,主要实现购物的功能。 53 │ └── __init__.py 54 │ └── product.txt #存放商品的txt文件 55 │ └── shopping_list.txt #存放购物清单的txt.文件 56 │ └── shopping_mall.py #购物商城程序 57 ├── README 58 四、简要说明,更详细点可以说明软件的基本原理。 59 1.程序从/bin/atm.py开始执行if __name__ == '__main__': 60 main.run() 61 2.程序转到/core/main.py下的run()函数,登陆时调用/core/auth的acc_login()进行登陆验证:用到了/core/auth下的acc_auth2()方法进行验证(此时传入的参数时用户输入的账户和密码) 62 acc_auth2中有调用了/core/db_handler下的db_handler()方法(参数是输入的账户名)在db_handler中只是进行判断是什么引擎,return file_db_handle(数据库引擎)解析文件,返回文件执行加载输入的用户的账户的所有数据 63 接下来判断是否为管理者账户,或者是否被冻结,若都不是,则判断输入的密码是否与数据库中的密码一样,在判断到期时间是否过期 64 所有都通过的话就返回这个账户的数据,之前已经创建了一个空字典,里面有是否验证:用户数据:用户账户:,判断是否被验证过,然后把用户数据临时的传递到里面,执行主循环函数 65 可以选择进入到购物商城,或者信用卡操作或者退出 66 1)购物商城 67 调用/shopping_mall/shopping_mall.py文件执行,主循环函数,选择你是商家还是用户, 68 ①如果选择商家,商家有增加商品修改商品的功能 69 ②如果选择用户,用户则有购物,刷信用卡消费的功能,当退出时打印消费清单 70 2)信用卡操作 71 调用/core/main.py下interactive(用户的所有数据)调用主循环函数,可以打印账户信息、还款、取款、转账、账单、退出等操作 72 ①账户信息 73 ②还款 74 ③取款 75 ④转账 76 ⑤账单 77 ⑥退出 78 3)若在账户登陆的时候进行输入的时管理员账户调用/bin/manage.py则可以对用户进行管理,解冻用户、冻结用户、申领新卡 79 ①添加账户 80 ②冻结账户 81 ③解冻账户 82 ④退出 83 五、常见问题说明。 84 日志没有实现,账单没有实现
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
product.txt
iphone: 5288 Mac pro: 12000 Bike: 800 Watch: 36000 Coffe: 39 Python book: 120 Book: 100
shopping_list.txt
('Coffe', 39) ('Pychon book', 120)
长风破浪会有时,直挂云帆济沧海。
欢迎多多提提意见