博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Python学习笔记-练习编写ATM+购物车(购物商城)
阅读量:6545 次
发布时间:2019-06-24

本文共 17644 字,大约阅读时间需要 58 分钟。

作业需求: 模拟实现一个ATM + 购物商城程序:     1.额度 15000或自定义     2.实现购物商城,买东西加入 购物车,调用信用卡接口结账     3.可以提现,手续费5%     4.支持多账户登录     5.支持账户间转账     6.记录每月日常消费流水     7.提供还款接口     8.ATM记录操作日志     9.提供管理接口,包括添加账户、用户额度,冻结账户等。。。     10.用户认证用装饰器 一、软件定位,软件的基本功能。     实现一个简单的atm与购物车程序, 二、运行代码的方法: 安装环境、启动命令等。     用Python3.5写的,语法就是至此之前所学的,直接打开运行即可 三、目录总体结构设计。 ├── ATM #ATM主程目录 │   ├── __init__.py │   ├── bin                    #ATM 执行文件 目录 │   │   ├── __init__.py │   │   ├── atm.py                 #ATM 执行程序 │   │   ├── manage.py              #信用卡管理 │   ├── conf                   #配置文件 │   │   ├── __init__.py │   │   └── Settings.py            #配置参数 │   ├── core                   #主要程序逻辑都 在这个目录 里 │   │   ├── __init__.py │   │   ├── accounts.py            #用于从文件里加载和存储账户数据 │   │   ├── auth.py                #用户认证模块及主要功能函数 │   │   ├── db_handler.py          #数据库连接引擎 │   │   ├── logger.py              #日志记录模块 │   │   ├── main.py                #主逻辑交互程序 │   │   ├── transaction.py         #记账\还钱\取钱\与账户金额相关的操作,冻结或者锁定用户 │   ├── db                     #用户数据存储的地方 │   │   ├── __init__.py │   │   ├── account_sample.py   #生成一个初始的账户数据 ,把这个数据 存成一个 以这个账户id为文件名的文件,放在accounts目录 就行了,程序自己去会这里找 │   │   └── accounts            #存各个用户的账户数据 ,一个用户一个文件 │   │       └── 123.json           #新创建的用户账户示例文件 │   │       └── 1234.json          #一个用户账户示例文件 │   │       └── 123456.json        #一个用户账户示例文件 │   │       └── 6230001.json       #管理用户账户示例文件 │   └── log                    #日志目录 │        ├── access.log              #用户访问和操作的相关日志 │        └── login_in.log            #登陆日志 └── shopping_mall               #电子商城程序,需单独实现,主要实现购物的功能。 │        └── __init__.py │        └── product.txt             #存放商品的txt文件 │        └── shopping_list.txt       #存放购物清单的txt.文件 │        └── shopping_mall.py        #购物商城程序 ├── 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 __author__ = 'ZFH' 2 # 2018.09.26 3 #-*- Coding:utf-8 -*- 4  5 import os,sys 6  7  8 BASE_DIR_Atm = os.path.dirname(os.path.dirname(__file__)) 9 BASE_DIR_PRO = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))10 11 sys.path.append(BASE_DIR_Atm)12 sys.path.append(BASE_DIR_PRO)13 14 print(sys.path)15 16 from core import main17 18 if __name__ == '__main__':19     main.run()
online/Atm/bin/arm.py

 

1 __author__ = 'ZFH' 2 # 2018.09.26 3 # -*- coding:utf-8 -*- 4 import os,logging 5 BASE_DIR_Atm = os.path.dirname(os.path.dirname(__file__)) 6  7 DATABASE = { 8     'engine':'file_storage', 9     'name':'account',10     'path':"%s/dbs" % BASE_DIR_Atm11 }12 13 LOG_LEVEL = logging.INFO14 LOG_TYPES = {15     'transaction':'transaction.log',16     'access':'access.log'17 }18 19 #发生交易的配置类型20 TRANSACTION_TYPE = {21     'repay':{
'action':'plus','interest':0}, #还款22 'withdrawal':{
'action':'minus','interest':0.05}, #取现扣费,即降低可用余额23 'transfer':{
'action':'minus','interest':0.05}, #转账扣费,即降低可用余额24 'consumption':{
'action':'minus','interest':0} #消费25 }
online/Atm/conf/settings.py
1 __author__ = 'ZFH' 2 # 2018.09.26 3 # -*- coding:utf-8 -*- 4 #用于从文件里加载和存储账户数据 5 import json 6 from core import db_handler 7 from conf import settings 8  9 def load_current_balance(account_id):10     '''11     返回账号余额其他信息12     :param account_id:用户账户的名字13     :return:返回最新读到的数据文件中的最新数据14     '''15     db_path = db_handler.db_handler()16     account_file = "%s/%s.json" %(db_path,account_id)17     with open(account_file,'r') as f:18         acc_data = json.load(f)19         return acc_data20 21 #写入文件22 def dump_account(account_data):23     db_path = db_handler.db_handler()24     account_file = "%s/%s.json" %(db_path,account_data['id'])25     with open(account_file,'w') as f:26         acc_data = json.dump(account_data,f)27     return True
online/Atm/core/accounts.py
1 __author__ = 'ZFH' 2 # 2018.09.26 3 # -*- coding:utf-8 -*- 4 # 用户认证模块 5 import os,json,time 6 from core import db_handler 7  8 #装饰器(用于验证账户是否登陆过) 9 def login_required(func):10     def wrapper(*args,**kwargs):11         if args[0].get('is_authenticated'):12             return func(*args,**kwargs)13         else:14             exit("用户认证失败")15     return wrapper16 17 def acc_auth(account,password):18     '''19     账号验证函数20     :param account:21     :param password:22     :return:23     '''24     db_path = db_handler.db_handler()25     account_file = '%s/%s.json' % (db_path,account)26 #    print('account_file:',account_file)27 #    print('account_file:',type(account_file))28     if os.path.isfile(account_file):29         with open(account_file,'r') as f:30             account_data = json.load(f)31             if account_data['password'] == password:32                 exp_time_stamp = time.mktime(time.strptime(account_data['expire_date'],"%Y-%m-%d"))33                 if time.time() > exp_time_stamp:34                     print("\033[31;1m[%s]账户已经注销,请重新申领账户!\033[0m" % account)35                 else:36                     return account_data37             else:38                 print("\033[31;1m账号或密码错误,请重新输入!\033[0m")39     else:40         print("\033[31;1m[%s]账户不存在!---\033[0m" % account)41 42 def acc_login(user_data,log_obj):43    '''44    账户登陆函数45    :param user_data:用户信息数据,只存在内存中46    :param log_obj:47    :return: 账户密码都对的情况下,返回所有账户数据48    '''49    retry_count = 0;  #初始化重试次数为050    while user_data['is_authenticated'] is not True and retry_count < 3: #如果没有验证过,或循环此时没超过三次就执行下面的51        account = input("\033[32;1m账户:\033[0m").strip()#输入账户52        password = input("\033[32;1m密码:\033[0m").strip()#输入密码53        auth = acc_auth(account,password) #解耦,将输入的账户和密码传入到acc_auth2函数中,进行验证最后返回的是读取到的输入正确账户的所有数据)赋值给auth54 #       print('auth:',auth)55        if auth:56            user_data['is_authenticated'] = True #登陆成功,将只存在与内存中的数据中的是否验证改为True57            user_data['account_id'] = account #将只存在与内存中的数据中的账户id改为账户名字(开始输入的帐户名)58            return auth  #这一步操作就是验证此账户是否登陆,然后返回账户的所有数据(数据文件中的所有数据)59        retry_count += 160    else:61        log_obj.error("[%s]账户太多次尝试" % account)62        exit()
online/Atm/core/auth.py
1 __author__ = 'ZFH' 2 # 2018.09.26 3 # -*- coding:utf-8 -*- 4 import os,sys,time,json 5  6 from conf import settings 7  8  9 10 #数据库句柄11 def db_handler():12     '''13     连接数据库14     :return:15     '''16     conn_params = settings.DATABASE   #conf下配置的数据库地址17 #    print("conn_params:",conn_params)18 #    print('conn_params:',type(conn_params))19     if conn_params['engine'] == 'file_storage': #判断Settings下的DABASE是什么引擎,这里只用文件文件引擎20         return file_db_handler(conn_params) #则把Settings下的DABASE的数据传给file_db_handle并返回21     elif conn_params['engine'] == 'mysql':22         pass #支持数据源类型扩展23 24 def file_db_handler(conn_params):25     '''26     file db操作27     :return:28     '''29 #    print('file_db:',conn_params)30     db_path = '%s/%s' % (conn_params['path'],conn_params['name'])31     return db_path
online/Atm/core/db_handler.py
1 __author__ = 'ZFH' 2 # 2018.09.26 3 # -*- coding:utf-8 -*- 4 #日志记录模块,处理所有日志工作 5  6 import logging 7 from conf import settings 8  9 def logger(log_type):10     #create logger11     logger = logging.getLogger(log_type)12     logger.setLevel(settings.LOG_LEVEL)13 14     #创建控制台处理程序并将级别设置为调试15     ch = logging.StreamHandler()16     ch.setLevel(settings.LOG_LEVEL)17 18     #创建文件处理程序并设置级别为警告19     log_file = "%s/%s" % (settings.BASE_DIR_Atm,settings.LOG_TYPES[log_type])20     fh = logging.FileHandler(log_file)21     fh.setLevel(settings.LOG_LEVEL)22 23     #创建格式化程序24     formatter = logging.Formatter('%(asctime)s - %(name)s - %(levename)s- %(message)s')25 26     #添加格式化的CH和FH27     ch.setFormatter(formatter)28     fh.setFormatter(formatter)29 30     #添加CH和FH到loggerh31     logger.addHandler(ch)32     logger.addHandler(fh)33 34     return logger35     '''36     #应用程序代码37     logger.debug('debug message')38     '''
online/Atm/core/logger.py
__author__ = 'ZFH'# 2018.09.26# -*- coding:utf-8 -*-import os,time,sysfrom core import authfrom core.auth import login_requiredfrom core import db_handlerfrom core import loggerfrom core import transactionfrom core import loggerfrom conf import settingsfrom core import accountsimport json#访问日志access_logger = logger.logger('access')#交易日志trans_logger= logger.logger('transaction')#用户数据信息user_data = {    'account_id':None,                             #账号ID    'is_authenticated':False,                      #是否认证    'account_data':None                            #账号数据}#调用log文件下的log方法,返回日志对象def run():#    print("测试调用不同路径")    acc_data = auth.acc_login(user_data,access_logger)                     #程序从这里开始,执行auth下的acc_login函#    print('acc_data:',acc_data)#  (返回的是验证过的正确的账户数据)赋值给acc_data(此时这里的数据为输入账户名字的数据文件的数据)    if user_data['is_authenticated']:        user_data['account_data'] = acc_data                #把账户所有信息传给账户开始时的临时的账户数据空字典,        # 把所有的数据文件传给账户的账户数据里面,#        print(acc_data)        main_menu(user_data)def account_info(acc_data):    '''    账户信息    :param acc_data:账户信息    :return:    '''    account_info = acc_data    creidt = account_info['account_data']['credit']    balance = account_info['account_data']['balance']    pay_day =  account_info['account_data']['pay_day']    print(' creidt: %s \n balance: %s \n pay_day: %s' % (creidt,balance,pay_day))def login_out(acc_data):    '''    用户退出登录    :param acc_data: 退出用户    :return:    '''    exit('退出')def main_menu(acc_data):    '''    展现主菜单,并让用户选择功能    1.银行卡操作    2.购物商城    3.退出    :param acc_data: 传入赋值的临时账户信息    :return:    '''    main_menu = u'''    --------主菜单--------    \033[32;1m    1.银行卡操作    2.购物商城    e.退出    \033[0m    '''    main_menu_dic = {        '1':'银行卡操作',        '2':'购物商城',        'e':'直接退出',    }    exit_flag = False    while not exit_flag:        print(main_menu)        user__main_option = input('请输入您的选择的操作:').strip()        if user__main_option in main_menu_dic:#            print('acc_data:',acc_data)            if user__main_option == 'e':                login_out(acc_data)                return            else:                interactive(acc_data,user__main_option)        else:            print('\033[31;1m选择不存在!\033[0m')def interactive(acc_data,user__main_option):    '''    处理主菜单选择    :param acc_data: 缓存记录的用户信息    :param user__main_option: 主菜单用户选择    :return:    '''    if user__main_option == '1':        interactive_bank(acc_data)    elif user__main_option == '2':        interactive_shaopping(acc_data)    return interactivedef interactive_shaopping(acc_data):    '''    展现购物菜单    :param acc_data: 缓存记录的用户信息    :return:    '''    returndef interactive_bank(acc_data):    '''    展现银行卡主菜单,并让用户选择功能    1.查看账户    2.取款    3.还款    4.转账    5.账单    e.直接退出    返回上一层    :param acc_data: 传入赋值的临时账户信息    :param user_option: 传入用户上一层选择    :return:    '''    print('进入下一个菜单')    main_menu_bank = u'''    --------银行卡主菜单--------    \033[32;1m        1.查看账户        2.取款        3.还款        4.转账        5.账单        e.直接退出    \033[0m    '''    main_menu_bank_dic = {        '1':'查看账户',        '2':'取款',        '3':'还款',        '4':'转账',        '5':'账单',        'e':'直接退出',    }    exit_flag = False    while not exit_flag:        print(main_menu_bank)        user_option = input('请输入您的选择的操作(\033[32;1m返回上一层请输入r\033[0m):').strip()        if user_option in main_menu_bank_dic:            if user_option == 'e':                login_out(acc_data)                break            elif user_option == '1':                account_info(acc_data)                user_option = input('\033[32;1m返回上一层请输入r\033[0m:').strip()                if user_option == 'r':                    interactive_bank(acc_data)                else:                    print('\033[31;1m选择不存在!\033[0m返回上一层请输入r,直接退出请输入e')                    account_info(acc_data)                    user_option = input('\033[32;1m返回上一层请输入r\033[0m:').strip()                    if user_option =='r':                        interactive_bank(acc_data)                    elif user_option == 'e':                       login_out(acc_data)                       break            elif user_option == '2':                withdrawal(acc_data)            elif user_option == '3':                repay(acc_data)        elif user_option == 'r':            return main_menu(acc_data)        else:            print('\033[31;1m选择不存在!请按提示重新输入!!!\033[0m')@login_required   #调用用户验证装饰器def withdrawal(acc_data):    '''    取款操作,打印可取款额度    :param acc_data:    :return:    '''    account_data = accounts.load_current_balance(acc_data['account_id'])    current_balance = '''    ---------银行信息----------    信用额度: %s    可用余额: %s    账单日:   %s    ''' % (account_data['credit'],account_data['balance'],account_data['pay_day'])    print(current_balance)    withdrawal_flag = False    while not withdrawal_flag:        withdrawal_amount = input("\033[33;1m输入取款金额:\033[0m").strip()        if len(withdrawal_amount) > 0 and withdrawal_amount.isdigit():            #将数据传入make_transaction中(交易日志,用户数据,交易类型,还款金额)进行操作,最后返回的是最新操作之后的账户数据            new_balance = transaction.make_transaction(trans_logger,account_data,'withdrawal',withdrawal_amount)            if  new_balance:                print('''\033[42;1m最新的余额:%s\033[0m''' %(new_balance['balance']))                return            if withdrawal_flag == 'b':                withdrawal_flag == True        elif withdrawal_amount == 'e':            interactive_bank(acc_data)        else:            print('\033[31;1m[%s]是无效的账户!\033[0m' % withdrawal_amount)        if withdrawal_amount == 'b':            withdrawal_flag == True@login_required   #调用用户验证装饰器def repay(acc_data):    '''    还款操作,打印当前余额    :param acc_data:    :return:    '''    account_data = accounts.load_current_balance(acc_data['account_id'])    current_balance = '''    ---------银行信息----------    信用额度: %s    可用余额: %s    账单日:   %s    ''' % (account_data['credit'],account_data['balance'],account_data['pay_day'])    print(current_balance)    repay_flag = False    while not repay_flag:        repay_amount = input("\033[33;1m输入你要还款的金额(重选操作请输入e):\033[0m").strip()        if len(repay_amount) > 0 and repay_amount.isdigit():            #将数据传入make_transaction中(交易日志,用户数据,交易类型,还款金额)进行操作,最后返回的是最新操作之后的账户数据            new_balance = transaction.make_transaction(trans_logger,account_data,'repay',repay_amount)            if  new_balance:                print('''\033[42;1m最新的余额:%s\033[0m''' %(new_balance['balance']))                return            if repay_amount == 'b':                repay_flag == True        elif repay_amount == 'e':            interactive_bank(acc_data)        else:            print('\033[31;1m[%s]是无效的账户!\033[0m' % repay_amount)        if repay_amount == 'b':            repay_flag == True
online/Atm/core/main.py
1 __author__ = 'ZFH' 2 # 2018.09.26 3 # -*- coding:utf-8 -*- 4 #记账\还钱\取钱\与账户金额相关的操作,冻结或者锁定用户 5 # 交易处理模块 6 from conf import settings 7 from core import db_handler 8 from core import accounts 9 10 import json,time11 12 def make_transaction(log_obj,account_data,tran_type,amount,**kwargs):13     '''14     处理所有用户的交易15     :param log_obj: 写入日志16     :param account_data:用户最新数据17     :param tran_type:交易类型18     :param amount:交易金额19     :param kwargs:其他参数20     :return:返回最新的账户数21     '''22     amount = float(amount)    #转换为浮点数23     if tran_type in settings.TRANSACTION_TYPE:      #判断传入的类型是否在配置参数里面24         interest = round(amount * settings.TRANSACTION_TYPE[tran_type]['interest'],2)    #根据交易类型计算利息赋值给interest25         old_lalance = float(account_data['balance'])      #读取数据中账户余额26 #        print('interest:',type(interest))27 #        print('amount:',type(amount))28 #        print('old_lalance:',type(old_lalance))29 30         #还款操作31         if settings.TRANSACTION_TYPE[tran_type]['action'] == 'plus':      #因为是信用卡,所以需要判断操作操作,加plus32 #            print(settings.TRANSACTION_TYPE)33             new_balance = old_lalance + amount + interest     #执行的是信用卡的还款操作,计算方法是,旧余额+还款的钱和利息=最后的账户可用余额34          #取现35         elif settings.TRANSACTION_TYPE[tran_type]['action'] == 'minus' and settings.TRANSACTION_TYPE[tran_type] == 'transfer':    #因为是信用卡,所以取现都是降低可用余额的操作36             pass37         else:38             balance = old_lalance - amount - interest39             if balance > 0:40                 new_balance = balance41             else:42                 print("\033[31;1m余额不足\033[0m")43                 return44 45         account_data['balance'] = new_balance46 #        print(account_data)47         accounts.dump_account(account_data)48         return account_data49 50     else:51         print("\033[31;1m%s交易类型不存在\033[0m" % tran_type)
online/Atm/core/transaction.py

 

1 {
"enroll_date": "2018-09-27", "id": "123", "expire_date": "2032-01-01", "status": 0, "password": "123", "pay_day": "22", "balance": 513.8, "credit": "15000"}
online/Atm/dbs/account/123.json

 

 

 

 

 

 

转载于:https://www.cnblogs.com/fameg/p/9790200.html

你可能感兴趣的文章
c++学习笔记和思考
查看>>
27.Docker集群部署
查看>>
DNS保存
查看>>
IOS 多线程02-pthread 、 NSThread 、GCD 、NSOperationQueue、NSRunLoop
查看>>
第一周冲刺第五天博客
查看>>
[LeetCode]Longest Increasing Path in a Matrix
查看>>
C++基础之适配器
查看>>
集合set-深入学习
查看>>
C#语言学习——面向对象的几大原则
查看>>
zk 常用资料整理(转)
查看>>
JavaScript 字符串操作
查看>>
Android中asset文件夹和raw文件夹区别
查看>>
Fuel 30 分钟快速安装openstack 分类: 软件插件学习 ...
查看>>
第二章家庭作业 2.78
查看>>
Android 下拉刷新上拉载入 多种应用场景 超级大放送(上)
查看>>
Risc-V指令集
查看>>
Python进阶04 函数的参数对应
查看>>
C语言结构体的“继承”
查看>>
WebView之禁止调用第三方浏览器
查看>>
POJ 3468 A Simple Problem with Integers(线段树 区间更新)
查看>>