• python 模拟实现一个ATM + 购物商城程序


    思路:ATM是一个单独程序,提供给消费的是一个接口core下的settlement.py,只做了个人的,没写管理的模块

       Shopping也是一个单独的,只做了一个购物的消费模块,没写商家模块,偷懒用了银行的数据库,用户名和密码都是用的一套的

    具体目录如下:

    atm.py:

    import os
    import sys
    base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    # print(base_dir)
    sys.path.append(base_dir)

    from core import main

    if __name__ == '__main__':
    main.run()

    settings.py:
    import os
    import sys
    import logging
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

    DATABASE = {
    'engine': 'file_storage',
    'name':'accounts',
    'path':"%s\db" % BASE_DIR
    }

    LOG_LEVEL = logging.INFO
    LOG_TYPES = {
    'transaction':'transactions.log',
    'access':'access.log',
    }

    TRANSACTION_TYPE = {
    'repay':{'action':'plus','interest':0},
    'withdraw':{'action':'minus','interest':0.05},
    'transfer':{'action':'minus','interest':0.05},
    'consume':{'action':'minus','interest':0},
    }

     accounts.py:

    import json,os,sys,time

    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    sys.path.append(BASE_DIR)
    from core import db_handler
    from conf import settings

    def load_current_balance(account_id):
    """

    :param account_id:
    :return:返回账号的资金和其它信息
    """
    db_path = db_handler.db_handler(settings.DATABASE)
    account_file = "%s\%s.json" %(db_path,account_id)
    with open(account_file) as f:
    acc_data = json.load(f)
    return acc_data
    def dump_account(account_data):
    db_path = db_handler.db_handler(settings.DATABASE)
    account_file = "%s\%s.json" %(db_path,account_data['id'])
    with open(account_file,'w') as f:
    acc_data = json.dump(account_data,f)

    return True

     auth.py:

    import os
    import sys
    import json
    import time
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    sys.path.append(BASE_DIR)
    from core import db_handler
    from conf import settings
    from core import logger

    def acc_auth(account,password):
    """

    :param account: 信用卡账号
    :param password: 信用卡密码
    :return:
    """
    db_path = db_handler.db_handler(settings.DATABASE) #数据库存储的目录
    account_file = "%s\%s.json" %(db_path,account) #个人的json数据
    # print(account_file)
    if os.path.isfile(account_file): #还必须判断这个文件是否存在
    with open(account_file) as f:
    account_data = json.load(f) #还原成字典
    if account_data['password'] == password:
    #mktime() 接收struct_time对象作为参数,返回用秒数来表示时间的浮点数,
    # time.strptime(string[, format])根据指定的格式把一个时间字符串解析为时间元组
    exp_time_stamp = time.mktime(time.strptime(account_data['expire_date'],"%Y-%m-%d"))
    if time.time() > exp_time_stamp:
    print("33[31;1m你的账号[%s]已经过期,请联系银行服务中心33[0m"%account)
    else: #如果通过了,就返回账号的所有信息
    return account_data
    else:
    print("33[31;1m你的账号或者密码错误33[0m")

    else:
    print("33[31;1m账号[%s]不存在!33[0m"% account)

    def acc_login(user_data,log_obj):
    """
    登录接口
    :param user_data:user_data在主函数定义是临时账户信息
    :param log_obj:log_obj执行完logger()之后返回的logger,可以调用相关方法写入日志信息
    :return:
    """
    retry_count = 0
    while user_data['is_authenticated'] is not True and retry_count < 3:
    account = input("请输入账号:").strip()
    password = input("请输入密码:").strip()
    auth = acc_auth(account,password)
    if auth: #不是空代表验证成功了,因为验证成功之后会返回用户所有信息
    user_data['is_authenticated'] = True
    user_data['account_id'] = account
    return auth #又返回了用户的全部数据
    retry_count +=1
    else:
    log_obj.error("account[%s] too many login attempts" %account)
    exit()

    db_handler.py:

    """
    根据数据库的类型,对数据进行处理
    """
    import os
    def file_db_handle(conn_params):
    """
    数据库的存放的路径
    :param conn_params: 传入的数据是settings中的DATABASE
    :return:
    """
    l = os.sep
    # print('file db:',conn_params)
    db_path = '%s\%s' %(conn_params['path'],conn_params['name'])
    return db_path

    def db_handler(conn_parms):
    """
    根据不同的类型,用不同的函数进行处理
    :param conn_parms:
    :return:
    """
    if conn_parms['engine'] == 'file_storage':
    return file_db_handle(conn_parms)
    elif conn_parms['engine'] == 'mysql':
    pass
    logger.py:

    import logging
    import os,sys
    #想要从父类导入,需要先加入环境变量
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    sys.path.append(BASE_DIR)
    from conf import settings

    def logger(log_type):

    #create logger
    logger = logging.getLogger(log_type)
    logger.setLevel(settings.LOG_LEVEL)
    #create file handler and set level to warning,这是会输出到屏幕上的
    ch = logging.StreamHandler()
    ch.setLevel(settings.LOG_LEVEL)
    #创建输入到文件夹中的日志
    log_file = "%s/log/%s" %(settings.BASE_DIR,settings.LOG_TYPES[log_type])
    fh = logging.FileHandler(log_file)
    fh.setLevel(settings.LOG_LEVEL)
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    #在日志中加入格式
    # ch.setFormatter(formatter)
    fh.setFormatter(formatter)
    #将内容输入到创建的日志文件中
    # logger.addHandler(ch)
    logger.addHandler(fh)

    return logger #这可以后边可以调用,直接写入日志

    # #对logger进行测试
    # a = logger("access")
    # a.warn("test")

    main.py:

    import os
    import sys,time

    BARE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    sys.path.append(BARE_DIR)
    from core import auth
    from core import logger
    from core import transaction
    from core import accounts
    from conf import settings

    #logger()函数执行,并且返回了logger,可以进行调用warn方法进行日志写入
    trans_logger = logger.logger('transaction')
    access_logger = logger.logger('access')

    user_data = {
    'account_id':None,
    'is_authenticated':False,
    'account_data':None
    }
    def account_info(acc_data):
    print(user_data['account_data'])
    def repay(acc_data):
    account_data = user_data['account_data']
    # for k,v in account_data.items():
    # print(k,v)
    current_balance = """
    ----------- BALANCE INFO --------
    Credit:%s
    Balance:%s
    """ %(account_data['credit'],account_data['balance'])
    print(current_balance)
    back_flag = False #设置标志,输入b跳出while循环
    while not back_flag:
    repay_amount = input("33[33;1mInput repay amount:33[0m").strip()
    if len(repay_amount) > 0 and repay_amount.isdigit():
    # print('ddd 00')
    new_balance = transaction.make_transaction(trans_logger,account_data,'repay',repay_amount)
    # print(new_balance)
    if new_balance:
    print("""33[42;1mNew Balance:%s33[0m"""%(new_balance['balance']))
    else:
    print('33[31;1m[%s] is not a valid amount,only accept integer!33[0m' % repay_amount)
    if repay_amount == 'b':
    back_flag = True

    def withdraw(acc_data):
    """
    取款
    :param acc_data: 变成了数据库数据,而且是字典的格式
    :return:
    """
    accout_data = accounts.load_current_balance(acc_data['account_id']) #再去数据库拿一遍数据,有点多余
    current_balance = '''
    ------- BALANCE INFO -------
    Credit: %s
    Balance: %s
    '''%(accout_data['credit'],accout_data['balance'])
    print(current_balance)
    back_flag = False
    while not back_flag:
    withdraw_amount = input("33[33;1mInput withdraw amount:33[0m").strip()
    if len(withdraw_amount) > 0 and withdraw_amount.isdigit():
    new_balance = transaction.make_transaction(trans_logger,accout_data,'withdraw',withdraw_amount)
    if new_balance:
    print('''33[42;1mNew Balance:%s33[0m'''%(new_balance['balance']))
    else:
    print('33[31;1m[%s] is not a valid amount,only accept integer!')
    if withdraw_amount == 'b':
    back_flag = True

    def transfer(acc_data):
    """

    :param acc_data: 变成了数据库数据,而且是字典的格式
    :return:
    """
    account_data = accounts.load_current_balance(acc_data['account_id'])
    current_balance = '''
    ------- BALANCE INFO -------
    Credit: %s
    Balance: %s'''%(account_data['credit'],account_data['balance'])
    print(current_balance)

    #钱数必须是数字
    back_flag = False
    while not back_flag:
    tranfer_amount = input("33[33;1mInput tranfer amount:33[0m").strip()
    #只支持整数
    if len(tranfer_amount) > 0 and tranfer_amount.isdigit():

    #调用transaction,传入账号信息,交易类型,交易金额,交易日志
    new_account_data = transaction.make_transaction(trans_logger,account_data,'transfer',tranfer_amount)
    if new_account_data:
    print("33[42;1mNew Balance:%s33[0m"%new_account_data['balance'])
    else:
    print('33[31;1m[%s] is not a valid amount,only accept integer!')
    if tranfer_amount == 'b':
    back_flag = True

    def pay_check(acc_data): #账单功能
    back_flag = False
    while not back_flag:
    Time_input = input("33[33;1mplease input time(Y-M-D),输入b退出:33[0m").strip()
    if Time_input == 'b': back_flag = True continue else: try: select_time = time.mktime(time.strptime(Time_input,'%Y-%m-%d')) # print(select_time) except Exception as ValueError: print("清输入正确的日期格式") continue # log_file = "atm_test/log/transactions.log" log_file = "%s/log/%s" % (settings.BASE_DIR, settings.LOG_TYPES["transaction"]) # print(log_file) with open(log_file,"r",encoding="utf-8") as f: for i in f: # for i in f : 是和大文件读写,不用写f,readlines() i_times = i[0:10] account_id = i[55:59] #字符串相同还判断不出来,必须转换成数字才行,估计数字类型的得这样做 account_id = int(account_id) user_data['account_data']['id'] = int(user_data['account_data']['id']) # print("account_id:%s" %account_id) i_time = time.mktime(time.strptime(i_times,'%Y-%m-%d')) # print(i_time) if i_time >= select_time and account_id == user_data['account_data']['id']: print(i)def logout(acc_data): """ 退出登录 :param acc_data: :return: """ q = input("33[33;1mIf you want to quit,please input q:33[0m").strip() if q == 'q': exit() else: print("请输入q")def interactive(acc_data): """ :param acc_data: :return: """ # 加u是为了防止中文乱码 menu = u""" --------- Oldboy Bank --------- 33[32;1m1.账号信息 2.还款 3.取款 4.转账 5.账单 6.退出 33[0m"""#这里比较牛的是写个字典,所以数字对应的是函数的内存地址 menu_dic = { '1':account_info, '2':repay, '3':withdraw, '4':transfer, '5':pay_check, '6':logout } exit_flag = False #这个标记为有很重要,是循环的前提 while not exit_flag: print(menu) user_option = input(">>:").strip() if user_option in menu_dic: # for i in menu_dic 显示出的是字典的key menu_dic[user_option](acc_data)def run(): """ 认证字段为真,用户数据为数据库的数据,信息补全了 :return: """ #经过执行acc_login之后,如果认证成功会改变user_data中的数据,认证成功字段置为真,赋予相应账号, acc_data = auth.acc_login(user_data,access_logger) #acc_data变成了数据库数据,而且是字典的格式 if user_data['is_authenticated']: user_data['account_data'] = acc_data interactive(user_data) else: print("33[31;1mOption does not exist!33[0m")# run()

    settlement.py:

    import os
    import sys,time

    BARE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    sys.path.append(BARE_DIR)
    from core import auth
    from core import logger
    from core import transaction
    from core import accounts
    from conf import settings

    #logger()函数执行,并且返回了logger,可以进行调用warn方法进行日志写入
    trans_logger = logger.logger('transaction')
    access_logger = logger.logger('access')

    user_data = {
    'account_id':None,
    'is_authenticated':False,
    'account_data':None
    }
    def account_info(acc_data):
    print(user_data['account_data'])
    def repay(acc_data):
    account_data = user_data['account_data']
    # for k,v in account_data.items():
    # print(k,v)
    current_balance = """
    ----------- BALANCE INFO --------
    Credit:%s
    Balance:%s
    """ %(account_data['credit'],account_data['balance'])
    print(current_balance)
    back_flag = False #设置标志,输入b跳出while循环
    while not back_flag:
    repay_amount = input("33[33;1mInput repay amount:33[0m").strip()
    if len(repay_amount) > 0 and repay_amount.isdigit():
    # print('ddd 00')
    new_balance = transaction.make_transaction(trans_logger,account_data,'repay',repay_amount)
    # print(new_balance)
    if new_balance:
    print("""33[42;1mNew Balance:%s33[0m"""%(new_balance['balance']))
    else:
    print('33[31;1m[%s] is not a valid amount,only accept integer!33[0m' % repay_amount)
    if repay_amount == 'b':
    back_flag = True

    def withdraw(acc_data):
    """
    取款
    :param acc_data: 变成了数据库数据,而且是字典的格式
    :return:
    """
    accout_data = accounts.load_current_balance(acc_data['account_id']) #再去数据库拿一遍数据,有点多余
    current_balance = '''
    ------- BALANCE INFO -------
    Credit: %s
    Balance: %s
    '''%(accout_data['credit'],accout_data['balance'])
    print(current_balance)
    back_flag = False
    while not back_flag:
    withdraw_amount = input("33[33;1mInput withdraw amount:33[0m").strip()
    if len(withdraw_amount) > 0 and withdraw_amount.isdigit():
    new_balance = transaction.make_transaction(trans_logger,accout_data,'withdraw',withdraw_amount)
    if new_balance:
    print('''33[42;1mNew Balance:%s33[0m'''%(new_balance['balance']))
    else:
    print('33[31;1m[%s] is not a valid amount,only accept integer!')
    if withdraw_amount == 'b':
    back_flag = True

    def transfer(acc_data):
    """

    :param acc_data: 变成了数据库数据,而且是字典的格式
    :return:
    """
    account_data = accounts.load_current_balance(acc_data['account_id'])
    current_balance = '''
    ------- BALANCE INFO -------
    Credit: %s
    Balance: %s'''%(account_data['credit'],account_data['balance'])
    print(current_balance)

    #钱数必须是数字
    back_flag = False
    while not back_flag:
    tranfer_amount = input("33[33;1mInput tranfer amount:33[0m").strip()
    #只支持整数
    if len(tranfer_amount) > 0 and tranfer_amount.isdigit():

    #调用transaction,传入账号信息,交易类型,交易金额,交易日志
    new_account_data = transaction.make_transaction(trans_logger,account_data,'transfer',tranfer_amount)
    if new_account_data:
    print("33[42;1mNew Balance:%s33[0m"%new_account_data['balance'])
    else:
    print('33[31;1m[%s] is not a valid amount,only accept integer!')
    if tranfer_amount == 'b':
    back_flag = True

    def pay_check(acc_data): #账单功能
    back_flag = False
    while not back_flag:
    Time_input = input("33[33;1mplease input time(Y-M-D),输入b退出:33[0m").strip()
    if Time_input == 'b': back_flag = True continue else: try: select_time = time.mktime(time.strptime(Time_input,'%Y-%m-%d')) # print(select_time) except Exception as ValueError: print("清输入正确的日期格式") continue # log_file = "atm_test/log/transactions.log" log_file = "%s/log/%s" % (settings.BASE_DIR, settings.LOG_TYPES["transaction"]) # print(log_file) with open(log_file,"r",encoding="utf-8") as f: for i in f: # for i in f : 是和大文件读写,不用写f,readlines() i_times = i[0:10] account_id = i[55:59] #字符串相同还判断不出来,必须转换成数字才行,估计数字类型的得这样做 account_id = int(account_id) user_data['account_data']['id'] = int(user_data['account_data']['id']) # print("account_id:%s" %account_id) i_time = time.mktime(time.strptime(i_times,'%Y-%m-%d')) # print(i_time) if i_time >= select_time and account_id == user_data['account_data']['id']: print(i)def logout(acc_data): """ 退出登录 :param acc_data: :return: """ q = input("33[33;1mIf you want to quit,please input q:33[0m").strip() if q == 'q': exit() else: print("请输入q")def interactive(acc_data): """ :param acc_data: :return: """ # 加u是为了防止中文乱码 menu = u""" --------- Oldboy Bank --------- 33[32;1m1.账号信息 2.还款 3.取款 4.转账 5.账单 6.退出 33[0m"""#这里比较牛的是写个字典,所以数字对应的是函数的内存地址 menu_dic = { '1':account_info, '2':repay, '3':withdraw, '4':transfer, '5':pay_check, '6':logout } exit_flag = False #这个标记为有很重要,是循环的前提 while not exit_flag: print(menu) user_option = input(">>:").strip() if user_option in menu_dic: # for i in menu_dic 显示出的是字典的key menu_dic[user_option](acc_data)def run(): """ 认证字段为真,用户数据为数据库的数据,信息补全了 :return: """ #经过执行acc_login之后,如果认证成功会改变user_data中的数据,认证成功字段置为真,赋予相应账号, acc_data = auth.acc_login(user_data,access_logger) #acc_data变成了数据库数据,而且是字典的格式 if user_data['is_authenticated']: user_data['account_data'] = acc_data interactive(user_data) else: print("33[31;1mOption does not exist!33[0m")# run()
    transaction.py:

    import os,sys,json
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    sys.path.append(BASE_DIR)
    from conf import settings
    from core import logger
    from core import accounts

    def make_transaction(log_obj,account_data,tran_type,amount,**others):
    """
    处理所有的交易信息
    :param log_obj:
    :param account_data: 用户账号
    :param tran_type: 交易类型
    :param amount: 交易额度
    :param others:日志等
    :return:
    """
    amount = float(amount)
    if tran_type in settings.TRANSACTION_TYPE:
    interest = amount * settings.TRANSACTION_TYPE[tran_type]['interest']
    old_balance = account_data['balance']
    if settings.TRANSACTION_TYPE[tran_type]['action'] == 'plus':
    new_balance = old_balance + amount + interest
    elif settings.TRANSACTION_TYPE[tran_type]['action'] == 'minus':
    new_balance = old_balance - amount - interest
    #check credit
    if new_balance < 0:
    print("""33[31;1mYour credit [%s] is not enough for this transaction
    [-%s],your current balance is [%s]""" %(account_data['credit'],(amount + interest),old_balance))
    return
    account_data['balance'] = new_balance
    #将更改完的数据写入到json文件中
    account_data['balance'] = new_balance
    accounts.dump_account(account_data) #保存新的账目到文件中
    log_obj.info("account:%s action:%s amount:%s interest:%s" %
    (account_data['id'], tran_type, amount, interest))

    return account_data
    else:
    print("33[31;1mTransaction type [%s] is not exist!33[0m" % tran_type)


    shopping.py:

    import os,sys,json,time
    BARE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    sys.path.append(BARE_DIR)
    from core import accounts
    from conf import settings
    from core import db_handler
    from core import logger
    from core import settlement
    """
    购物车功能,只能用来进行购物
    """

    product_list = []
    #此处是对txt文档进行处理,只能处理成列表格式,如果是对JSON格式,就是load成字典格式
    product = "%s/shopping/product.txt" % (settings.BASE_DIR)
    with open (product,'r',encoding='utf-8') as f:
    for line in f:
    line = line.strip() #去掉最后一个换行符
    index,item = line.split(":")
    product_list.append((index,item)) #以括号的格式变成一个元素,其实里边是一个个的元组
    # print(product_list)
    # print(len(product_list)) #元素的个数,以 1 开头
    #enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标

    #现在需要在ATM上写一个接口,提供用户输入用户名和账号,然后返回 用户的账号资金

    def acc_auth(func): #函数里套函数就是高阶函数
    def deco(*args,**kwargs):
    log_obj = logger.logger('access')
    retry_count = 0
    while retry_count < 3:
    account = input("33[32;1m登录账号:33[0m").strip()
    password = input("33[32;1m密码:33[0m").strip()
    db_path = db_handler.db_handler(settings.DATABASE)
    account_file = "%s/%s.json" %(db_path,account)
    if os.path.isfile(account_file):
    with open(account_file,'r') as f:
    account_data = json.load(f)
    if account_data['password'] == password:
    func(*args,**kwargs) #需要将认证完之后的数据传入到shopping中
    else:
    print("33[31;1mAccount ID or password is incorrect!33[0m")
    else:
    print("33[31;1mAccount [%s] does not exist!33[0m" % account)
    retry_count +=1
    else:
    log_obj.error("shopping account [%s] too many login attempts" % account)
    exit()
    return deco

    def print_product_list(): #可以显示小标和具体的数据
    for index,item in enumerate(product_list):
    print(index,item)
    @acc_auth #进行装饰器的认证
    def user_shopping():
    print("--------------------------"
    "--------------------------"
    " "
    " 欢迎进入购物菜单 "
    " ")
    print_product_list() # 打印商品
    salary_account =settlement.settlement() #需要调用settlement模块的settlement()方法
    salary = salary_account['balance']
    if salary > 0:
    shopping_list = []
    while True:
    option = input("请输入你要购买的商品的编号:").strip()
    if option.isdigit():
    option = int(option)
    if option >= 0 and option <= len(product_list):
    p_item = product_list[option] #用户选择的商品,对应的是一个元组
    c_num = int(p_item[1]) #对应的是商品价格
    if salary >= c_num:
    shopping_list.append(p_item)
    salary -= c_num
    print("添加购物车成功,你的余额还有%s" %salary)
    else:
    print("你的余额不足,只剩%s元" % (salary))
    else:
    print("输入错误,请重新输入!")
    elif option == "q":
    print("----------------购物清单---------------")
    for s_list in shopping_list:
    print(s_list)
    print("你的余额为%s" % (salary))
    salary_account['balance'] = salary
    accounts.dump_account(salary_account) #将修改完的数据写到数据库中
    print("..........exit.........")
    exit() #退出程序
    else:
    exit('余额不足!')

    user_shopping()
















     



  • 相关阅读:
    从程序员到项目经理
    wumii 爆款总结经验
    快速的搭建JFinal的ORM框架示例
    Hibernate all-delete-orphan[转]
    HHvm Apache 2.4 Nginx建站环境搭建方法安装运行WordPress博客
    雷军是如何从程序员蜕变成职业经理人的
    Postgresql数据库数据简单的导入导出
    如何一年看50本好书?
    清除DNS解析缓存
    mysql 下 计算 两点 经纬度 之间的距离
  • 原文地址:https://www.cnblogs.com/qianyuyu/p/10039390.html
Copyright © 2020-2023  润新知