zoukankan      html  css  js  c++  java
  • ATM机+购物车

    ATM机+购物车

     

     

     

     

     自己敲的:

    bin

    conf

    settings

    '''
    存放配置信息
    '''
    import os
    
    # 获取项目根目录
    BASE_PATH = os.path.dirname(
        os.path.dirname(__file__)
    )
    
    # 获取user_data文件夹目录路径
    USER_DATA_PATH = os.path.join(
        BASE_PATH, 'db', 'user_data'
    )
    print(USER_DATA_PATH)
    
    
    """
    logging配置
    """
    
    # 定义三种日志输出格式 开始
    standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' 
                      '[%(levelname)s][%(message)s]'  # 其中name为getlogger指定的名字
    simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
    id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s'
    
    # 定义日志输出格式 结束
    # ****************注意1: log文件的目录
    BASE_PATH = os.path.dirname(os.path.dirname(__file__))
    logfile_dir = os.path.join(BASE_PATH, 'log')
    # print(logfile_dir)
    
    # ****************注意2: log文件名
    logfile_name = 'atm.log'
    
    # 如果不存在定义的日志目录就创建一个
    if not os.path.isdir(logfile_dir):
        os.mkdir(logfile_dir)
    
    # log文件的全路径
    logfile_path = os.path.join(logfile_dir, logfile_name)
    
    LOGGING_DIC = {
        'version': 1,
        'disable_existing_loggers': False,
        'formatters': {
            'standard': {
                'format': standard_format
            },
            'simple': {
                'format': simple_format
            },
        },
        'filters': {},
        'handlers': {
            # 打印到终端的日志
            'console': {
                'level': 'DEBUG',
                'class': 'logging.StreamHandler',  # 打印到屏幕
                'formatter': 'simple'
            },
            # 打印到文件的日志,收集info及以上的日志
            'default': {
                'level': 'DEBUG',
                'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件
                'formatter': 'standard',
                'filename': logfile_path,  # 日志文件
                'maxBytes': 1024 * 1024 * 5,  # 日志大小 5M
                'backupCount': 5,
                'encoding': 'utf-8',  # 日志文件的编码,再也不用担心中文log乱码了
            },
        },
        'loggers': {
            # logging.getLogger(__name__)拿到的logger配置
            '': {
                'handlers': ['default', 'console'],  # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
                'level': 'DEBUG',
                'propagate': True,  # 向上(更高level的logger)传递
            },
        },
    }

    core

    admin

    from core import src
    from interface import admin_interface
    
    # 添加用户
    def add_user():
        src.register()
    
    # 修改用户额度
    def change_balance():
        while True:
            # 输入修改的用户名
            change_user = input('请输入需要修改额度的用户:').strip()
            # 修改的用户额度
            money = input('请输入需要修改的用户额度:').strip()
            if not money.isdigit():
                continue
            # 调用修改额度接口
            flag, msg = admin_interface.change_balance_interface(
                change_user, money
            )
            if flag:
                print(msg)
                break
            else:
                print(msg)
    
    
    # 冻结账户
    def lock_user():
        while True:
            # 输入修改的用户名
            change_user = input('请输入需要修改额度的用户:').strip()
    
            flag, msg = admin_interface.lock_user_interface(change_user)
            if flag:
                print(msg)
                break
            else:
                print(msg)
    
    
    
    # 管理员功能字典
    admin_func = {
        '1': add_user,
        '2': change_balance,
        '3': lock_user,
    }
    
    def admin_run():
        while True:
            print('''
            1.添加账户
            2.修改额度
            3.冻结账户
            ''')
            choice = input('请输入管理员功能编号:').strip()
    
            if choice not in admin_func:
                print('请输入正确的功能编号')
                continue
    
            admin_func.get(choice)()

    src

    '''
    存放用户视图层
    '''
    
    from interface import user_interface
    from interface import bank_interface
    from interface import shop_interface
    from lib import common
    
    
    # 1、注册功能
    @common.login_auth
    def register():
        while True:
            # 1)让用户输入用户名与密码进行校验
            username = input('请输入用户名:').strip()
            password = input('请输入密码:').strip()
            re_password = input('请确认密码:').strip()
            # 可以输入自定义金额
    
            # 小的逻辑处理:比如两次密码是否一致
            if password == re_password:
                # 2)调用接口层的注册接口,将用户名与密码交给接口层进行处理
    
                # res--->(False,'用户名已存在!')
                # res = user_interface.register_inerface(
                # flag,msg--->(flag--->False,msg--->'用户名已存在!')
    
                # (True,用户注册成功),(False,这测失败)
                flag, msg = user_interface.register_inerface(
                    username, password
                )
    
                # 3)根据flag判断用户注册是否成功,用于控制break
                if flag:
                    print(msg)
                    break
                else:
                    print(msg)
    
    
    # 2、登录功能
    @common.login_auth
    def login():
        # 登录视图
        while True:
            # 1)让用户输入用户名与密码
            username = input('请输入用户名:').strip()
            password = input('请输入密码:').strip()
    
            # 2)调动接口层,将数据传给登录接口
            flag, msg = user_interface.login_interface(
                username, password
            )
    
            if flag:
                print(msg)
                global login_user  # 共享login_user信息
                login_user = username
                break
            else:
                print(msg)
    
    
    # 3、查看余额
    @common.login_auth
    def check_balance():
        # 1)直接调动查看余额接口,获取用户余额
        balance = user_interface.check_bal_interface(
            login_user
        )
    
        print(f'用户{login_user}账户余额为:{balance}')
    
    
    # 4、提现功能
    @common.login_auth
    def withdraw():
        while True:
            # 1)让用户输入提现金额
            input_money = input('请输入提现金额:').strip()
    
            # 2)判断用户输入的金额是否是数字
            if not input_money.isdigit():
                print('请重新输入!')
                continue
    
            # 3)用户提现金额,将提现的金额交付给接口层来处理
            flag, msg = bank_interface.withdraw_interface(
                login_user, input_money
            )
            if flag:
                print(msg)
                break
            else:
                print(msg)
    
    
    # 5、还款功能
    @common.login_auth
    def repay():
        while True:
            # 1)让用户输入还款金额
            input_money = input('请输入需要还款的金额:').strip()
            # 2) 判断用户输入的是否是数字
            if not input_money.isdigit():
                print('请输入正确的金额')
                continue
            input_money = int(input_money)
    
            # 3) 判断用户输入的金额大于0
            if input_money > 0:
                # 4) 调用还款接口
                flag, msg = bank_interface.repay_interface(
                    login_user, input_money
                )
                if flag:
                    print(msg)
                    break
            else:
                print('输入金额不能小于0')
    
    
    # 6、转账功能
    @common.login_auth
    def transfer():
        '''
        接收用户输入的转账金额
        接收用户输入的转账目标用户
        :return:
        '''
        while True:
    
            # 让用户输入转账用户与金额
            to_user = input('请输入转账目标用户:').strip()
            money = input('请输入转账金额:').strip()
            # 判断用户输入的金额是否是数字或者>0
            if not money.isdigit():
                print('请输入正确的金额:')
                continue
    
            money = int(money)
    
            if money > 0:
                # 调用转账接口
                flag, msg = bank_interface.transfer_interface(
                    # 当前用户,目标用户,转账金额
                    login_user, to_user, money
                )
                if flag:
                    print(msg)
                    break
    
                else:
                    print(msg)
    
            else:
                print('请输入正确的金额!')
    
    
    # 7、查看流水
    @common.login_auth
    def check_flow():
        # 直接调用查看流水接口
        flow_list = bank_interface.check_flow_interface(
            login_user
        )
    
        if flow_list:
            for flow in flow_list:
                print(flow)
        else:
            print('当前用户没有流水!')
    
    
    # 8、购物功能
    @common.login_auth
    def shopping():
        # 不从文件中读取商品数据,直接写
        # 1)创建一个商品列表
        # shop_list = {
        #     '0':{'name':'苹果','price':10},
        # }
    
        # 列表套列表的商品数据
        shop_list = [
            ['苹果', 10],
            ['egon', 250],
            ['鸭脖', 30],
            ['MacBook', 20000],
        ]
    
        # 初始化当前购物车:
        shopping_car = {}
        while True:
            # 1)打印商品信息,让用户选择
            # 枚举:enumerate(可迭代对象)--->(可迭代对象的索引,索引对应的值)
            print('================欢迎光临==================')
            for index, shop in enumerate(shop_list):
                shop_name, shop_price = shop
                print(f'商品编号为:[{index}]',
                      f'商品名称:[{shop_name}]',
                      f'商品单价:[{shop_price}]')
            print('==============欢迎下次惠顾================')
    
            # 2)让用户根据商品编号进行选择
            choice = input('请输入商品编号(是否结账输入y or n):').strip()
    
            # 2.1)输入的是y ,进入支付结算功能
            if choice == 'y':
                if not shopping_car:
                    print('购物车是空的,不能支付,请重新输入!')
                    continue
                # 6)调用支付接口进行支付
                flag, msg = shop_interface.shopping_interface(
                    login_user, shopping_car
                )
                if flag:
                    print(msg)
                    break
                else:
                    print(msg)
            # 2.2)输入的是n ,添加购物车
            elif choice == 'n':
                # 判断当前用户是否添加过购物车
                if not shopping_car:
                    print('购物车是空的,不能支付,请重新输入!')
                    continue
                # 7)调用添加购物车接口
                flag, msg = shop_interface.add_shop_car_interface(
                    login_user, shopping_car
                )
                if flag:
                    print(msg)
                    break
    
            if not choice.isdigit():
                print('请输入正确编号!')
                continue
            choice = int(choice)
            # 3)判断choice是否存在
            if choice not in range(len(shop_list)):
                print('请输入正确的编号!')
                continue
            # 4)获取商品名称与单价
            shop_name, shop_price = shop_list[choice]
    
            # 5)加入购物车--->必须先将用户的
            # 5.1)判断用户选择的商品是否重复,重复则+1
            if shop_name in shopping_car:
                # 添加商品数量
                shopping_car[shop_name][1] += 1
            else:
                # 否则数量默认为1
                # {'商品名称':['单价','数量']
                shopping_car[shop_name] = [shop_price, 1]
            print('当前购物车:',shopping_car)
    
    # 清空购物车功能(课后作业)
    
    
    
    # 9、查看购物车
    @common.login_auth
    def check_shop_car():
        # 直接调用查看购物车接口
        shop_car = shop_interface.check_shop_car_interface(login_user)
        print(shop_car)
    
    
    # 10、管理员功能
    @common.login_auth
    def admin():
        from core import admin
        admin.admin_run()
    
    
    # 创建函数功能字典
    func_dic = {
        '1': register,
        '2': login,
        '3': check_balance,
        '4': withdraw,
        '5': repay,
        '6': transfer,
        '7': check_flow,
        '8': shopping,
        '9': check_shop_car,
        '10': admin,
    }
    
    
    # 视图层主程序
    def run():
        while True:
            print('''
            ===== ATM+购物车 =====
                1、注册功能
                2、登录功能
                3、查看余额
                4、提现功能
                5、还款功能
                6、转账功能
                7、查看流水
                8、购物功能
                9、查看购物车
                10、管理员功能
            =======   end    ======
            ''')
    
            choice = input('请输入功能编号:').strip()
            # 判断功能编号是否存在
            if choice not in func_dic:
                print('请输入正确的功能编号')
                continue
            # 用于选择功能函数
            func_dic.get(choice)()  # func_dic.get('1') ---->register()

    db

    shop_data

    >>egon抱枕.json

    >>有趣的用品.json

    user_data

    >>alex.json

    >>egon.json

    >>lucky.json

    db_handler

    '''
    数据处理层
            -专门用户处理数据的
    '''
    import json
    import os
    from conf import settings
    
    
    # 查看数据
    def select(username):
        # 1) 接收接口层传过来的username用户名,拼接用户json路径
        user_path = os.path.join(
            settings.USER_DATA_PATH, f'{username}.json'
        )
    
        # 2)校验用户json文件是否存在
        if os.path.exists(user_path):
            # 3)打开数据,并返回给接口层
            with open(user_path, 'r', encoding='utf-8') as f:
                user_dic = json.load(f)
                return user_dic
    
        # 3)不return,默认return None
    
    
    # 保存数据(添加数据或者更新数据)
    def save(user_dic):
        # 1)拼接用户的数据字典
        username = user_dic.get('username')
        user_path = os.path.join(
            settings.USER_DATA_PATH, f'{username}.json'
        )
        # 2)保存用户数据
        with open('user_path', 'w', encoding='utf-8') as f:
            # ensure_ascii=False让文件中的中文数据,显示更加美观
            json.dump(user_dic, f, ensure_ascii=False)

     interface

    admin_interface

    from db import db_handler
    from lib import common
    
    # 根据不同的接口类型传入不同的日志对象
    admin_logger = common.get_logger(log_type='admin')
    
    
    # 修改额度接口
    def change_balance_interface(username, money):
        user_dic = db_handler.select(username)
    
        if user_dic:
            # 修改额度
            user_dic['balance'] = int(money)
    
            # 保存修改后的用户数据
            db_handler.save(user_dic)
            # 记录日志
            msg = f'管理员修改用户:[{username}]额度修改成功!'
            admin_logger.info(msg)
            return True, msg
    
        return False, '修改额度用户不存在!'
    
    
    # 冻结账户接口
    def lock_user_interface(username):
        user_dic = db_handler.select(username)
        if user_dic:
            # 将locked的默认值改为True
            user_dic['locked'] = True
            db_handler.save(user_dic)
            msg = f'用户{username}冻结成功!'
            admin_logger.info(msg)
            return True, msg
    
        return False, '冻结用户不存在!'

    bank_interface

    '''
    银行相关接口
    '''
    
    from db import db_handler
    from lib import common
    
    # 根据不同的接口类型传入不同的日志对象
    bank_logger = common.get_logger(log_type='bank')
    
    
    # 提现接口(手续费5%)
    def withdraw_interface(username, money):
        # 1)先获取用户字典
        user_dic = db_handler.select(username)
        # 账户中的金额
        balance = int(user_dic.get('balance'))
        # 提现本金+手续费
        money2 = int(money) * 1.05  # 手续费5%,money2变成float类型
    
        # 判断用户金额是否足够
        if balance >= money2:
            # 2)修改用户字典进行修改
            balance -= money2
            user_dic['balance'] -= balance
            # 3)记录流水(需要在保存前操作)
            flow = f'用户[{username}]提现金额[{money}]成功,手续费为:[{money2 - float(money)}]'
            user_dic['flow'].append(flow)
    
            # 4)再保存数据或更新数据
            db_handler.save(user_dic)
    
            bank_logger.info(flow)
            return True, flow  # int和float类型不能互减
    
        return False, '提现金额不足,请重新输入!'
    
    
    # 还款接口
    def repay_interface(username, money):
        '''
        获取用户的金额
        给用户的金额做加钱的操作
        :return:
        '''
        # 获取用户字典
        user_dic = db_handler.select(username)
    
        # 直接做加钱的操作
        user_dic['balance'] += money
    
        # 记录流水
        flow = f'用户[{username}] 还款[{money}]成功  当前额度为:{user_dic["balance"]}'
        user_dic['flow'].append(flow)
        # 调用数据处理层,将修改后的数据更新
        db_handler.save(user_dic)
    
        return True, flow
    
    
    # 转账接口
    def transfer_interface(login_user, to_user, money):
        '''
        获取当前用户数据
        获取目标用户数据
        获取转账金额
        :return:
        '''
        # 获取当前用户的字典
        login_user_dic = db_handler.select(login_user)
        # 获取目标用户的字典
        to_user_dic = db_handler.select(to_user)
        # 判断目标用户是否存在
        if not to_user_dic:
            return False, '目标用户不存在'
        # 若用户存在则判当前用户的转账金额是否足够
        if login_user_dic['balance'] >= money:
            # 若足够,则开始给目标用户转账
            # 1)给当前用户做减钱操作
            login_user_dic['balance'] -= money
            # 2)给目标用户做加钱操作
            to_user_dic['balance'] += money
            # 3)记录流水当前用户与目标用户的流水
            # 当前用户流水
            login_user_flow = f'用户:[{login_user}] 给用户:[{to_user}]转账[{money}]成功!'
            login_user_dic['flow'].append(login_user_flow)
            # 目标用户流水
            to_user_flow = f'用户:[{to_user}] 接收用户:[{login_user}]转账[{money}]成功!'
            to_user_dic['flow'].append(to_user_flow)
            # 保存用户数据
            # 1) 调用数据处理层的save功能,保存当前用户数据
            db_handler.save(login_user_dic)
            # 2) 保存目标用户数据
            db_handler.save(to_user_dic)
    
            return True, login_user_flow, to_user_flow
        return False, '当前用户转账金额不足!'
    
    
    # 查看流水接口
    def check_flow_interface(login_user):
        user_dic = db_handler.select(login_user)
        return user_dic.get('flow')
    
    
    # 支付接口
    def pay_interface(login_user, cost):
        user_dic = db_handler.select(login_user)
        # 判断用户金额是否足够
        if user_dic.get('balance') >= cost:
            # 减钱
            user_dic['balance'] -= cost
            # 记录消费流水
            flow = f'用户消费金额为:[{cost}$]'
            user_dic['flow'].append(flow)
            # 保存数据
            db_handler.save(user_dic)
    
            # 结果交给购物接口层处理
            return True
    
        return False

    shop_interface

    '''
    购物商城接口
    '''
    
    from db import db_handler
    from lib import common
    
    # 根据不同的接口类型传入不同的日志对象
    shop_logger = common.get_logger(log_type='shop')
    
    
    # 商品准备结算接口
    def shopping_interface(login_user, shopping_car):
        # 1)计算消费总额
        cost = 0
        for price_number in shopping_car.values():
            price, number = price_number
            cost += (price * number)
    
        # 导入银行接口
        from interface import bank_interface
        # 逻辑校验成功后,再调用银行的支付接口(支付前先交给bank_interface做)
        flag = bank_interface.repay_interface(login_user, shopping_car)
        if flag:
            msg = f'用户:[{login_user}]支付 [{cost}$] 成功,准备发货!'
            shop_logger.info(msg)
            return True, msg
        return False, '支付失败,金额不足'
    
    
    # 购物车添加接口
    def add_shop_car_interface(login_user, shopping_car):
        # 1)获取当前用户的购物车
        user_dic = db_handler.select(login_user)
        # 获取用户文件中的商品的数据
        shop_car = user_dic.get('shop_car')
    
        # 2)添加购物车
        # 2.1)判断当前用户选择的商品是否已经存在
        for shop_name, price_number in shopping_car.items():
            # 每个商品的数量
            number = price_number[1]
            # 2.2)若商品重复,则累加商品数量
            if shop_name in shop_car:
                # [单价,数量][1]--->数量
                user_dic['shop_car'][shop_name][1] += number
            else:
                # 2.3)若不是重复,更新到商品字典中
                user_dic['shop_car'].update(
                    {shop_name: price_number}
                )
        # 保存用户数据
        db_handler.save(user_dic)
    
        return True, '添加购物车成功!'
    
    
    # 查看购物车接口
    def check_shop_car_interface(username):
        user_dic = db_handler.select(username)
        return user_dic.get('shop_car')

    user_interface

    '''
    逻辑接口层
            用户层
    '''
    from db import db_handler
    from lib import common
    
    # 根据不同的接口类型传入不同的日志对象
    user_logger = common.get_logger(log_type='user')
    
    # 注册接口
    def register_inerface(username, password, balance=15000):
        # 2)查看用户是否存在
        # 2.1)调用数据处理层中的select函数,会返回用户字典或者None
        user_dic = db_handler.select(username)
        # {user: user,pwd:pwd...} or None
        # 若用户存在,则return,告诉用户重新输入
        if user_dic:
            # return(False,'用户名已存在!')
            return False, '用户名已存在!'
    
        # 3)若用户不存在,则保存用户数据
        # 做密码加密
        password = common.get_pwd_md5(password)
    
        # 3.1)组织用户的数据字典信息
        user_dic = {
            'username': username,
            'passsword': password,
            'balance': balance,
            # 用于记录用户流水的列表
            'flow': [],
            # 用于记录用户购物车
            'shop_car': {},
            # locked:用于记录用户是否被冻结
            # False:未冻结    True:已被冻结
            'locked': False
        }
        # 3.2)保存数据
        db_handler.save(user_dic)
        msg = f'{username}注册成功'
        # 3.3)记录日志
        user_logger.info(msg)
    
        return True, msg
    
    
    # 登录接口
    def login_interface(username, password):
        # 1)先查看当前用户数据是否存在,用于判断用户是否存在
        # {用户数据字典} or None
        user_dic = db_handler.select(username)
        # 用于判断用户是否存在
    
        # 若有冻结用户,则需要判断是否锁定
        if user_dic.get('locked'):
            return False, '该用户已被锁定!'
    
        # 2) 判断用户是否存在
        if user_dic:
            # 给用户输入的密码做一次加密
            password = common.get_pwd_md5(password)
            # 3) 校验密码是否一致
            if password == user_dic.get('password'):
                msg = f'用户:[{username}]登录成功!'
                user_logger.info(msg)
                return True, msg
            else:
                msg = f'用户:[{username}]密码错误!'
                user_logger.warn(msg)
                return False, msg
    
        msg = f'用户:[{username}]用户不存在,请重新输入!'
        return False, msg
    
    
    # 查看余额接口
    def check_bal_interface(username):
        user_dic = db_handler.select(username)
    
        return user_dic['balance']    #千万注意!!!一定要注意限定,否则就会读取所有的信息给用户了

    lib

    common

    '''
    存放公共方法
    '''
    import hashlib
    from conf import settings
    import logging.config
    
    
    # md5加密功能
    def get_pwd_md5(password):
        md5_obj = hashlib.md5()
        md5_obj.update(password.encode('utf-8'))
        salt = 'Tank,egon 最帅!'
        md5_obj.update(salt.encode('utf-8'))
    
        return md5_obj.hexdigest()
    
    
    # 登录认证装饰器
    def login_auth(func):
        from core import src
        def inner(*args, **kwargs):
            if src.login_user:
                res = func(*args, **kwargs)
                return res
            else:
                print('未出示证明,无法享受美好的功能服务')
                src.login()
    
        return inner
    
    
    # 添加日志功能:(日志功能在接口层使用)
    # 获取日志对象
    def get_logger(log_type):  # 传user,bank,购物商城
        '''
        :param  log_type: 比如是user日志,bank日志,购物商城日志
        :return:
        '''
        # 1)加载日志配置信息
        logging.config.dictConfig(
            settings.LOGGING_DIC
        )
        # 2)获取日志对象
        logger = logging.getLogger(log_type)
    
        return logger

    log(后续设计好自动生成)

    readme.txt

    # 项目的说明书
    ## 项目:ATM + 购物车
    
    # 项目需求:
        1.额度15000或自定义     -->  注册功能
        2.实现购物商城,买东西加入购物车,调用信用卡接口结账  --> 购物功能、支付功能
        3.可以提现,手续费5%   --> 提现功能
        4.支持多账户登录  --> 登录功能
        5.支持账户间转账  --> 转账功能
        6.记录日常消费 -->  记录流水功能
        7.提供还款接口 -->  还款功能
        8.ATM记录操作日志 --> 记录日志功能
        9.提供管理接口,包括添加账户、用户额度,冻结账户等。。。 ---> 管理员功能
        10.用户认证用装饰器  --> 登录认证装饰器
    
    
    ## "用户视图层" 展示给用户选择的功能
        1、注册功能
        2、登录功能
        3、查看余额
        4、提现功能
        5、还款功能
        6、转账功能
        7、查看流水
        8、购物功能
        9、查看购物车
        10、管理员功能
    
    
    
    
    # 一个项目如何从无到有
    ## 一 需求分析
    1.拿到项目,会先在客户那里一起讨论需求,商量项目的功能是否能实现
    得到一个需求文档
    2.不同岗位程序猿进行开发
    Python:后端,爬虫
    -不同的岗位
    
        -UI界面设计:
            -设计软件的布局,会分局软件的外观切成一张张图片
        -前端:
            -拿到UI交给他的图片,然后搭建网页
            -设计一些页面中,哪些位置需要接收数据,需要进行数据交互
        -后端:
            -直接核心的业务逻辑,调度数据库进行数据的增删改查
        -测试
            -会给代码进行全面测试,比如压力测试,界面测试(CF卡箱子)
        -运维:
            -部署项目
    
    
    
    ## 二 程序项目设计
    ### 1、程序设计的好处
        1)思路清晰
        2)不会出现写一半代码时推翻重写
        3)方便自己或以后的同事更好维护
    
    ### 2、三层架构设计的好处
        1)把每个功能都分层三部分,逻辑清晰
        2)如果用户更换不同的用户界面或不同,
        的数据储存机制都不会影响接口层的核心
        逻辑代码,扩展性强。
        3)可以在接口层,准确的记录日志与流水。
    
    ## 三 分任务开发
    ## 四 测试
    ## 五 上线

    start.py

    '''
    程序的入口
    '''
    import sys
    import os
    
    # 添加解释器的环境变量
    sys.path.append(
        os.path.dirname(__file__)
    )
    
    from core import src
    
    # 开始执行项目函数
    if __name__ == '__main__':
        # 先执行用户视图层
        src.run()

    ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    参考答案:

    bin

        
    #!_ * _ coding:utf-8 _ * _
    #__author__:“lucky”
    __init__.py
    #!_*_coding:utf-8_*_
    #__author__:"Alex Li"
    
    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()
    atm.py
     
    manage.py

     

    conf

    #!_ * _ coding:utf-8 _ * _
    #__author__:“lucky”
    __init__.py
    #!_*_coding:utf-8_*_
    #__author__:"Alex Li"
    import os
    import sys
    import logging
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    
    
    DATABASE = {
        'engine': 'file_storage', #support mysql,postgresql in the future
        '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},
    
    }
    settings.py

     

    core

        
    #!_ * _ coding:utf-8 _ * _
    #__author__:“lucky”
    __init__.py
    #!_*_coding:utf-8_*_
    #__author__:"lucky"
    
    import json
    import time
    from core import db_handler
    from conf import settings
    
    
    def load_current_balance(account_id):
        '''
        return account balance and other basic info
        :param account_id:
        :return:
        '''
        # db_path = db_handler.db_handler(settings.DATABASE)
        # account_file = "%s/%s.json" %(db_path,account_id)
        #
        db_api = db_handler.db_handler()
        data = db_api("select * from accounts where account=%s" % account_id)
    
        return data
    
        # with open(account_file) as f:
        #     acc_data = json.load(f)
        #     return  acc_data
    def dump_account(account_data):
        '''
        after updated transaction or account data , dump it back to file db
        :param account_data:
        :return:
        '''
        db_api = db_handler.db_handler()
        data = db_api("update accounts where account=%s" % account_data['id'],account_data=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
    accounts.py
    #!_*_coding:utf-8_*_
    #__author__:"lucky"
    import os
    from core import db_handler
    from conf import settings
    from core import logger
    import json
    import time
    
    
    
    def login_required(func):
        "验证用户是否登录"
    
        def wrapper(*args,**kwargs):
            #print('--wrapper--->',args,kwargs)
            if args[0].get('is_authenticated'):
                return func(*args,**kwargs)
            else:
                exit("User is not authenticated.")
        return wrapper
    
    
    def acc_auth(account,password):
        '''
        account auth func
        :param account: credit account number
        :param password: credit card password
        :return: if passed the authentication , retun the account object, otherwise ,return None
        '''
        db_path = db_handler.db_handler(settings.DATABASE)
        account_file = "%s/%s.json" %(db_path,account)
        print(account_file)
        if os.path.isfile(account_file):
            with open(account_file,'r') as f:
                account_data = json.load(f)
                if account_data['password'] == password:
                    exp_time_stamp = time.mktime(time.strptime(account_data['expire_date'], "%Y-%m-%d"))
                    if time.time() >exp_time_stamp:
                        print("33[31;1mAccount [%s] has expired,please contact the back to get a new card!33[0m" % account)
                    else: #passed the authentication
                        return  account_data
                else:
                    print("33[31;1mAccount ID or password is incorrect!33[0m")
        else:
            print("33[31;1mAccount [%s] does not exist!33[0m" % account)
    
    
    def acc_auth2(account,password):
        '''
        优化版认证接口
        :param account: credit account number
        :param password: credit card password
        :return: if passed the authentication , retun the account object, otherwise ,return None
        '''
        db_api = db_handler.db_handler()
        data = db_api("select * from accounts where account=%s" % account)
    
    
        if data['password'] == password:
            exp_time_stamp = time.mktime(time.strptime(data['expire_date'], "%Y-%m-%d"))
            if time.time() > exp_time_stamp:
                print("33[31;1mAccount [%s] has expired,please contact the back to get a new card!33[0m" % account)
            else:  # passed the authentication
                return data
        else:
            print("33[31;1mAccount ID or password is incorrect!33[0m")
    
    def acc_login(user_data,log_obj):
        '''
        account login func
        :user_data: user info data , only saves in memory
        :return:
        '''
        retry_count = 0
        while user_data['is_authenticated'] is not True and retry_count < 3 :
            account = input("33[32;1maccount:33[0m").strip()
            password = input("33[32;1mpassword:33[0m").strip()
            auth = acc_auth2(account, password)
            if auth: #not None means passed the authentication
                user_data['is_authenticated'] = True
                user_data['account_id'] = account
                #print("welcome")
                return auth
            retry_count +=1
        else:
            log_obj.error("account [%s] too many login attempts" % account)
            exit()
    auth.py
    #!_*_coding:utf-8_*_
    #__author__:"lucky"
    
    '''
    handle all the database interactions
    '''
    import json,time ,os
    from  conf import settings
    def file_db_handle(conn_params):
        '''
        parse the db file path
        :param conn_params: the db connection params set in settings
        :return:
        '''
        print('file db:',conn_params)
        #db_path ='%s/%s' %(conn_params['path'],conn_params['name'])
        return file_execute
    
    def db_handler():
        '''
        connect to db
        :param conn_parms: the db connection params set in settings
        :return:a
        '''
        conn_params = settings.DATABASE
        if conn_params['engine'] == 'file_storage':
            return file_db_handle(conn_params)
        elif conn_params['engine'] == 'mysql':
            pass #todo
    
    
    
    def file_execute(sql,**kwargs):
        conn_params = settings.DATABASE
        db_path = '%s/%s' % (conn_params['path'], conn_params['name'])
    
        print(sql,db_path)
        sql_list = sql.split("where")
        print(sql_list)
        if sql_list[0].startswith("select") and len(sql_list)> 1:#has where clause
            column,val = sql_list[1].strip().split("=")
    
            if column == 'account':
                account_file = "%s/%s.json" % (db_path, val)
                print(account_file)
                if os.path.isfile(account_file):
                    with open(account_file, 'r') as f:
                        account_data = json.load(f)
                        return account_data
                else:
                    exit("33[31;1mAccount [%s] does not exist!33[0m" % val )
    
        elif sql_list[0].startswith("update") and len(sql_list)> 1:#has where clause
            column, val = sql_list[1].strip().split("=")
            if column == 'account':
                account_file = "%s/%s.json" % (db_path, val)
                #print(account_file)
                if os.path.isfile(account_file):
                    account_data = kwargs.get("account_data")
                    with open(account_file, 'w') as f:
                        acc_data = json.dump(account_data, f)
                    return True
    db_handler.py
    #!_*_coding:utf-8_*_
    #__author__:"lucky"
    
    '''
    handle all the logging works
    '''
    
    import logging
    from conf import settings
    
    def logger(log_type):
    
        #create logger
        logger = logging.getLogger(log_type)
        logger.setLevel(settings.LOG_LEVEL)
    
    
        # create console handler and set level to debug
        ch = logging.StreamHandler()
        ch.setLevel(settings.LOG_LEVEL)
    
        # create file handler and set level to warning
        log_file = "%s/log/%s" %(settings.BASE_DIR, settings.LOG_TYPES[log_type])
        fh = logging.FileHandler(log_file)
        fh.setLevel(settings.LOG_LEVEL)
        # create formatter
        formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    
        # add formatter to ch and fh
        ch.setFormatter(formatter)
        fh.setFormatter(formatter)
    
        # add ch and fh to logger
        logger.addHandler(ch)
        logger.addHandler(fh)
    
        return logger
        # 'application' code
        '''logger.debug('debug message')
        logger.info('info message')
        logger.warn('warn message')
        logger.error('error message')
        logger.critical('critical message')'''
    logger.py
      1 #!_*_coding:utf-8_*_
      2 #__author__:"lucky"
      3 
      4 '''
      5 main program handle module , handle all the user interaction stuff
      6 '''
      7 
      8 from core import auth
      9 from core import accounts
     10 from core import logger
     11 from core import accounts
     12 from core import transaction
     13 from core.auth import login_required
     14 import time
     15 
     16 #transaction logger
     17 trans_logger = logger.logger('transaction')
     18 #access logger
     19 access_logger = logger.logger('access')
     20 
     21 
     22 #temp account data ,only saves the data in memory
     23 user_data = {
     24     'account_id':None,
     25     'is_authenticated':False,
     26     'account_data':None
     27 
     28 }
     29 
     30 def account_info(acc_data):
     31     print(user_data)
     32 
     33 @login_required
     34 def repay(acc_data):
     35     '''
     36     print current balance and let user repay the bill
     37     :return:
     38     '''
     39     account_data = accounts.load_current_balance(acc_data['account_id'])
     40     #for k,v in account_data.items():
     41     #    print(k,v )
     42     current_balance= ''' --------- BALANCE INFO --------
     43         Credit :    %s
     44         Balance:    %s''' %(account_data['credit'],account_data['balance'])
     45     print(current_balance)
     46     back_flag = False
     47     while not back_flag:
     48         repay_amount = input("33[33;1mInput repay amount:33[0m").strip()
     49         if len(repay_amount) >0 and repay_amount.isdigit():
     50             print('ddd 00')
     51             new_balance = transaction.make_transaction(trans_logger,account_data,'repay', repay_amount)
     52             if new_balance:
     53                 print('''33[42;1mNew Balance:%s33[0m''' %(new_balance['balance']))
     54 
     55         else:
     56             print('33[31;1m[%s] is not a valid amount, only accept integer!33[0m' % repay_amount)
     57 
     58         if repay_amount == 'b':
     59             back_flag = True
     60 def withdraw(acc_data):
     61     '''
     62     print current balance and let user do the withdraw action
     63     :param acc_data:
     64     :return:
     65     '''
     66     account_data = accounts.load_current_balance(acc_data['account_id'])
     67     current_balance= ''' --------- BALANCE INFO --------
     68         Credit :    %s
     69         Balance:    %s''' %(account_data['credit'],account_data['balance'])
     70     print(current_balance)
     71     back_flag = False
     72     while not back_flag:
     73         withdraw_amount = input("33[33;1mInput withdraw amount:33[0m").strip()
     74         if len(withdraw_amount) >0 and withdraw_amount.isdigit():
     75             new_balance = transaction.make_transaction(trans_logger,account_data,'withdraw', withdraw_amount)
     76             if new_balance:
     77                 print('''33[42;1mNew Balance:%s33[0m''' %(new_balance['balance']))
     78 
     79         else:
     80             print('33[31;1m[%s] is not a valid amount, only accept integer!33[0m' % withdraw_amount)
     81 
     82         if withdraw_amount == 'b':
     83             back_flag = True
     84 
     85 def transfer(acc_data):
     86     pass
     87 def pay_check(acc_data):
     88     pass
     89 def logout(acc_data):
     90     pass
     91 def interactive(acc_data):
     92     '''
     93     interact with user
     94     :return:
     95     '''
     96     menu = u'''
     97     ------- Oldboy Bank ---------
     98     33[32;1m1.  账户信息
     99     2.  还款(功能已实现)
    100     3.  取款(功能已实现)
    101     4.  转账
    102     5.  账单
    103     6.  退出
    104     33[0m'''
    105     menu_dic = {
    106         '1': account_info,
    107         '2': repay,
    108         '3': withdraw,
    109         '4': transfer,
    110         '5': pay_check,
    111         '6': logout,
    112     }
    113     exit_flag = False
    114     while not exit_flag:
    115         print(menu)
    116         user_option = input(">>:").strip()
    117         if user_option in menu_dic:
    118             print('accdata',acc_data)
    119             #acc_data['is_authenticated'] = False
    120             menu_dic[user_option](acc_data)
    121 
    122         else:
    123             print("33[31;1mOption does not exist!33[0m")
    124 def run():
    125     '''
    126     this function will be called right a way when the program started, here handles the user interaction stuff
    127     :return:
    128     '''
    129     acc_data = auth.acc_login(user_data,access_logger)
    130     if user_data['is_authenticated']:
    131         user_data['account_data'] = acc_data
    132         interactive(user_data)
    main.py
     1 #!_*_coding:utf-8_*_
     2 #__author__:"lucky"
     3 
     4 from conf import settings
     5 from core import accounts
     6 from core import logger
     7 #transaction logger
     8 
     9 
    10 
    11 def make_transaction(log_obj,account_data,tran_type,amount,**others):
    12     '''
    13     deal all the user transactions
    14     :param account_data: user account data
    15     :param tran_type: transaction type
    16     :param amount: transaction amount
    17     :param others: mainly for logging usage
    18     :return:
    19     '''
    20     amount = float(amount)
    21     if tran_type in  settings.TRANSACTION_TYPE:
    22 
    23         interest =  amount * settings.TRANSACTION_TYPE[tran_type]['interest']
    24         old_balance = account_data['balance']
    25         if settings.TRANSACTION_TYPE[tran_type]['action'] == 'plus':
    26             new_balance = old_balance + amount + interest
    27         elif settings.TRANSACTION_TYPE[tran_type]['action'] == 'minus':
    28             new_balance = old_balance - amount - interest
    29             #check credit
    30             if  new_balance <0:
    31                 print('''33[31;1mYour credit [%s] is not enough for this transaction [-%s], your current balance is
    32                 [%s]''' %(account_data['credit'],(amount + interest), old_balance ))
    33                 return
    34         account_data['balance'] = new_balance
    35         accounts.dump_account(account_data) #save the new balance back to file
    36         log_obj.info("account:%s   action:%s    amount:%s   interest:%s" %
    37                           (account_data['id'], tran_type, amount,interest) )
    38         return account_data
    39     else:
    40         print("33[31;1mTransaction type [%s] is not exist!33[0m" % tran_type)
    transaction.py

     

    db

    accounts文件夹:
    里面放一些需要存储的数据
    #!_*_coding:utf-8_*_
    #__author__:"lucky"
    __init__.py
    #!_*_coding:utf-8_*_
    #__author__:"lucky"
    
    
    import json
    acc_dic = {
        'id': 1234,
        'password': 'abc',
        'credit': 15000,
        'balance': 15000,
        'enroll_date': '2020-04-01',
        'expire_date': '2025-03-31',
        'pay_day': 22,
        'status': 0 # 0 = normal, 1 = locked, 2 = disabled
    }
    
    print(json.dumps(acc_dic))
    account_sample

     

    log

    #!_*_coding:utf-8_*_
    #__author__:"lucky"
    __init__.py
    会记录用户的一些行为,比如存款或者消费
    log.txt

     

    readme

    程序介绍:
        实现ATM常用功能
        功能全部用python的基础知识实现,用到了timeossysjsonopenlogging函数模块知识, 主要帮给大家一个简单的模块化编程的示例
    
        注意:只实现了"还款""取现功能"
    
    程序结构:
    day5-atm/
    ├── README
    ├── atm #ATM主程目录
    │   ├── __init__.py
    │   ├── bin #ATM 执行文件 目录
    │   │   ├── __init__.py
    │   │   ├── atm.py  #ATM 执行程序
    │   │   └── manage.py #ATM 管理端,未实现
    │   ├── 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 #存各个用户的账户数据 ,一个用户一个文件
    │   │       └── 1234.json #一个用户账户示例文件
    │   └── log #日志目录
    │       ├── __init__.py
    │       ├── access.log #用户访问和操作的相关日志
    │       └── transactions.log    #所有的交易日志
    └── shopping_mall #电子商城程序,需单独实现
        └── __init__.py
    readme.txt

     

  • 相关阅读:
    手机评測怎么看?
    【c++】简单的string类的几个基本函数
    免费软件怎样赚钱?
    【leetcode】Swap Nodes in Pairs (middle)
    【leetcode】Divide Two Integers (middle)☆
    【leetcode】4Sum(middle)
    【leetcode】Longest Common Prefix (easy)
    【leetcode】3Sum Closest(middle)
    【leetcode】Container With Most Water(middle)
    【leetcode】Palindrome Number (easy)
  • 原文地址:https://www.cnblogs.com/lucky-cat233/p/12616873.html
Copyright © 2011-2022 走看看