包与ATM+购物车(面条版)

今日内容概要

  • ATM和购物车
    image

今日内容详细

# 什么是包?什么是模块?
	一个py文件就是一个模块
	包就是多个模块功能的结合体

# 如何创建包?包和文件夹的区别?
	包下有一个__init__.py文件
	而文件夹是没有的

"""
模块首次被导入发生了3件事?
	1. 产生一个名称空间
	2. 执行被导入的py文件,然后把执行的数据都丢到名称空间
	3. 执行文件中产生一个变量指向这个名称空间

包首次被导入发生了3件事?
	1. 产生一个包的名称空间
	2. 执行被导入包下的__init__.py文件,然后把执行的数据都丢到包的名称空间
	3. 执行文件中产生一个变量aaa指向这个包的名称空间

"""

image

ATM项目讲解

1. 开发项目的模式:
	# 瀑布模式
    	项目开发完成之后,一并提交测试 禅道
    # 敏捷开发
    	开发一个功能,测试一个功能
        
        
    ATM使用瀑布模式
	1. 需求分析
        	产品经理
            模拟实现一个ATM + 购物商城程序
'''
        额度 15000或自定义
        实现购物商城,买东西加入 购物车,调用信用卡接口结账
        可以提现,手续费5%
        支持多账户登录
        支持账户间转账
        记录每月日常消费流水
        提供还款接口
        ATM记录操作日志 
        提供管理接口,包括添加账户、用户额度,冻结账户等。。。
        用户认证用装饰器
'''
	2. 程序设计或者架构设计
    	 # 需求转为功能
            1.注册
            2.登录
            3.转账
            4.提现
            5.充值
            6.查看余额
            7.查看流水
            8.加入购物车
            9.查看购物车
            10.退出登录
            11.结束
        
        # 软件开发目录规范
            bin:
            	start.py启动文件
            core:
            	主要逻辑
                def login():
                    pass
                ...
            conf(config):
                settings.py
            db:
                db.py
            lib(library):
                common.py
            log:
                日志文件
                log.log
            interface:
                接口
                bank.py
                user.py
                shopping.py
            readme.txt
            requirements.txt
            
      	3. 分任务分模块开发
        
      	4. 测试
        
      	5. 上线
 
# 启动文件一般放在bin目录,或者项目根目录

image

启动脚本(start.py)

from core import src


def run():
    while True:
        print('''
    **********欢迎光临**********
    
    1.注册
    2.登录
    3.转账
    4.提现
    5.充值
    6.查看余额
    7.查看流水
    8.加入购物车
    9.查看购物车
    10.退出登录
    11.结束
    
    **********欢迎光临**********
    ''')
        func_dict = {
            '1': src.register,
            '2': src.login,
            '3': src.transfer,
            '4': src.withdrawal,
            '5': src.recharge,
            '6': src.view_balance,
            '7': src.view_flow,
            '8': src.add_cart,
            '9': src.view_cart,
            '10': src.log_out
        }
        choice = input('请选择功能>>>: ').strip()
        # 如果客户输入的不在选项中
        if not choice:
            print('输入不合法')
            continue

        if choice == '11':
            print('欢迎下次再来!!!')
            return

        if choice not in func_dict:
            print('请选择1-10之间的数字')
            continue

        # 正常输入
        func_dict[choice]()

# 判断当前文件为执行文件才开始执行
if __name__ == '__main__':
    run()


配置文件(settings.py)

import os


BASE_DIR = os.path.dirname(os.path.dirname(__file__))


# 导入日志模块
import os
import 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'  # 111

BASE_DIR = os.path.dirname(os.path.dirname(__file__))
LOG_DIR = os.path.join(BASE_DIR, 'log')
# 判断当前路径是否存在
if not os.path.isdir(LOG_DIR):
    os.mkdir(LOG_DIR)
# 拼接日志文件路径
logfile_path = os.path.join(LOG_DIR, 'log.log')


# log配置字典
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'],  # ['default', 'console']这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
            'level': 'DEBUG',
            'propagate': True,  # 向上(更高level的logger)传递
        },  # 当键不存在的情况下 (key设为空字符串)默认都会使用该k:v配置
    },
}

公共功能(common.py)

from core import src


# 登录验证的装饰器
def login_auth(func):
    def inner(*args, **kwargs):
        # 执行函数之前的代码
        # 判断是不是登录
        if src.user_data['is_login']:
            return func(*args, **kwargs)
        # 执行函数之后的代码
        else:
            src.login()
    return inner


import logging
import logging.config
from conf import settings


# 使用配置字典
def get_logger(username):
    logging.config.dictConfig(settings.LOGGING_DIC)  # 自动加载字典中的配置
    logger1 = logging.getLogger(username)  # 功能名实际应用时自行更改
    return logger1

image

核心功能(src.py)

import json
import os
from conf import settings
from lib import common

user_data = {
    'username': None,
    'is_login': False
}


# 1.注册功能

def register():
    print('开始进行注册>>> ')
    # 如果user_dict['is_login']为True 说明已经登录了

    if user_data['is_login']:
        print('当前已经登录成功,无法进行注册')
        return
    while True:
        # 先获取用户名 比对是否已经注册过
        username = input('username>>>: ').strip()
        """
        要先用获取到的用户名对比是否已经存在
        每个用户信息都单独存一个.json文件
        """
        '''验证用户名是否存在,其实验证的是用户文件路径是否存在'''
        # 拼接文件名
        # 到配置文件中拿path = os.path.dirname(os.path.dirname(__file__))
        path = settings.BASE_DIR
        # 把客户文件拼接到根目录下
        path_file = os.path.join(path, 'db', '%s.json' % username)
        # 判断文件名是否存在
        if os.path.exists(path_file):
            print('用户名已经存在请重新输入')
            continue
        # 若不存在文件路径则继续获取用户信息
        password = input('password>>>: ').strip()
        balance = int(input('balance>>>: ').strip())
        
        user_dict = {'username': username, 'password': password, 'balance': balance, 'locked': False, 'flow': [],
                     'shopping_cart': []}
        '''
        哪些数据可以直接写入文件?
            1. 字符串
            2. 二进制
        '''
        # 写入路径中
        with open(path_file, 'w', encoding='utf-8') as f:
            json.dump(user_dict, f)
            print('用户 %s 注册成功咯' % username)

            logger1 = common.get_logger('注册功能')  # 调用日志
            # 写入日志
            logger1.debug('%s 注册成功' % username)

            break


# 2.登录功能
def login():
    # 如果user_dict['is_login']为True 说明已经登录了

    if user_data['is_login']:
        print('当前已经登录成功,无法再进行登录')
        return
    print('开始进行登录>>> ')
	count = 0
    while True:
        username = input('username>>>: ').strip()
        # 还是要先判断是否存在用户信息
        path = settings.BASE_DIR
        path_file = os.path.join(path, 'db', '%s.json' % username)
        # 如果没有该用户文件
        if not os.path.exists(path_file):
            print('该用户目前尚未注册,请先进行注册')
            return
        # 否则存在用户文件 则继续获取用户密码
        password = input('password>>>: ').strip()
        # 读取用户信息进行比对
        with open(path_file, 'r', encoding='utf-8') as f:
            res = json.load(f)
        if password == res.get('password'):
            print('用户 %s 登录成功' % username)

            logger1 = common.get_logger('登录功能')  # 调用日志
            logger1.debug('%s 登录成功' % username)

            # 更改判断用户是否已经登录过的字典 用于全局认证
            user_data['username'] = username
            user_data['is_login'] = True
            break
        else:
            print('用户名或密码错误')
			# 输错三次锁定账户登录
            count += 1
            if count == 3:
                res['locked'] = True
                with open(path_file, 'w', encoding='utf-8') as f1:
                    json.dump(res, f1)

# 3.转账功能
@common.login_auth
def transfer():
    print('开始进行转账')
    while True:
        username = input('请输入收款方姓名(q to sign out)>>>: ').strip()
        # 还是要先判断是否存在用户信息
        path = settings.BASE_DIR
        path_file = os.path.join(path, 'db', '%s.json' % username)

        if username == 'q':
            return

        elif username == user_data['username']:
            print('付款账户和收款账户不能为同一人')
            continue
        # 如果没有该用户文件
        elif not os.path.exists(path_file):
            print('收款用户不存在,请输入其他用户')
            continue
        elif not username:
            print('输入不合法,请重新输入>>>')
            continue

        while True:
            # 收款用户存在 获取转账金额
            money = (input('请输入转账金额>>>: ').strip())
            # 先判断付款账户余额充足
            path_file2 = os.path.join(path, 'db', '%s.json' % user_data['username'])
            with open(path_file2, 'r', encoding='utf-8') as f2:
                res2 = json.load(f2)

            # 如果输入的不是纯数字
            if not money.isdigit():
                print('输入金额不合法,请重新输入')
                continue
            elif money <= '0':
                print('输入金额不合法,请重新输入')
                continue
            elif not money:
                continue

            # 如果付款账户余额充足
            elif res2['balance'] >= int(money):
                # 将获取的用户输入转成整型存入文本
                money = int(money)
                # 读出收款用户文件内容
                with open(path_file, 'r', encoding='utf-8') as f:
                    res = json.load(f)
                if username == res['username']:
                    res['balance'] += money

                    res['flow'].append('%s 收到一笔来自于 %s 的汇款 %s¥' % (username, user_data['username'], money))
                    # 将收款方收到的钱存入收款账户文本中
                    with open(path_file, 'w', encoding='utf-8') as f1:
                        json.dump(res, f1, ensure_ascii=False)

                    logger1 = common.get_logger('转账功能')  # 调用日志
                    logger1.debug('%s 收到一笔来自于 %s 的汇款 %s¥' % (username, user_data['username'], money))
                    # 付款账户内要减去转出的金额
                    # path = settings.BASE_DIR
                    # path_file2 = os.path.join(path, 'db', '%s.json' % user_data['username'])
                    # with open(path_file2, 'r', encoding='utf-8') as f2:
                    #     res2 = json.load(f2)

                    # 将付款账户的金额减去已经转出的 并写入付款用户文本
                    res2['balance'] -= money
                    res2['flow'].append(
                        '%s 向用户 %s 汇出了 %s¥, 余额剩余>>>: %s¥' % (user_data['username'], username, money, res2['balance']))

                    with open(path_file2, 'w', encoding='utf-8') as f3:
                        json.dump(res2, f3, ensure_ascii=False)
                        print('转账成功\n你的账户剩余 %s¥' % res2['balance'])

                        logger1 = common.get_logger('转账功能')  # 调用日志
                        logger1.debug('%s 向用户 %s 汇出了 %s¥, 余额剩余>>>: %s¥' % (
                            user_data['username'], username, money, res2['balance']))

                        return
            else:
                print('付款账户余额不足,穷逼一个还想转账')


# 4.提现功能
@common.login_auth  # 还是先要判断用户是否已经登录
def withdrawal():
    print('开始进行提现>>> ')
    while True:
        money = input('输入提现金额>>>: ')
        # 验证输入情况
        # 如果输入为空
        if not money: continue
        # 如果输入的不是纯数字
        if not money.isdigit():
            print('输入金额不合法')
            continue
        # 用户输入的是字符串 无法进行加减 所以先转为整型
        money = int(money)
        # 验证用户余额是否足够
        # 先按照文件路径读取用户数据出来
        path = settings.BASE_DIR
        path_file = os.path.join(path, 'db', '%s.json' % user_data['username'])
        with open(path_file, 'r', encoding='utf-8') as f:
            res = json.load(f)
        # 提现收取5%手续费 所以按照1.05倍对比
        if res['balance'] >= money * 1.05:
            # 减去用户提现的金额和手续费
            res['balance'] -= money * 1.05

            res['flow'].append('提现了 %s¥, 余额剩余 %s¥' % (money, res['balance']))

            # 将剩余的余额写入用户文件内
            with open(path_file, 'w', encoding='utf-8') as f1:
                json.dump(res, f1, ensure_ascii=False)
            print('提现成功')
            print('当前账户余额为>>>: %s¥' % res['balance'])
            logger1 = common.get_logger('提现功能')  # 调用日志
            logger1.debug('用户 %s 成功提现了 %s¥,当前账户余额为>>>: %s¥' % (user_data['username'], money, res['balance']))
            return
        else:
            print('穷逼一个,没钱别来玩')
            break


# 5.充值功能
@common.login_auth  # 还是先要判断用户是否已经登录
def recharge():
    print('开始进行充值')
    while True:
        recharge_amount = input('请输入充值金额>>>: ').strip()
        # 验证输入情况
        # 如果输入为空
        if not recharge_amount:
            print('输入金额不合法')
            continue
        # 如果输入的不是纯数字
        elif not recharge_amount.isdigit():
            print('输入金额不合法')
            continue
        recharge_amount = int(recharge_amount)
        # 读取用户信息
        path = settings.BASE_DIR
        path_file = os.path.join(path, 'db', '%s.json' % user_data['username'])
        with open(path_file, 'r', encoding='utf-8') as f:
            res = json.load(f)
            res['balance'] += recharge_amount

            res['flow'].append('充值 %s¥ 成功,账户余额为>>>: %s¥' % (recharge_amount, res['balance']))

        with open(path_file, 'w', encoding='utf-8') as f1:
            json.dump(res, f1, ensure_ascii=False)
            print('充值成功,当前账户余额为>>>: %s¥' % res['balance'])

            logger1 = common.get_logger('充值功能')  # 调用日志
            logger1.debug('充值成功,当前账户余额为>>>: %s¥' % res['balance'])
            return


# 6.查询余额
@common.login_auth  # 还是先要判断用户是否已经登录
def view_balance():
    print('开始查询余额')
    path = settings.BASE_DIR
    path_file = os.path.join(path, 'db', '%s.json' % user_data['username'])
    with open(path_file, 'r', encoding='utf-8') as f:
        res = json.load(f)
        print('当前账户余额为>>>: %s¥' % res['balance'])

        logger1 = common.get_logger('查询余额功能')  # 调用日志
        logger1.debug('用户 %s 查询了余额,当前账户余额为>>>: %s¥' % (user_data['username'], res['balance']))

        return True


# 7.查询流水
@common.login_auth  # 还是先要判断用户是否已经登录
def view_flow():
    print('开始查询流水')
    path = settings.BASE_DIR
    path_file = os.path.join(path, 'db', '%s.json' % user_data['username'])
    with open(path_file, 'r', encoding='utf-8') as f:
        res = json.load(f)
        res1 = res['flow']
        for v in res1:
            print(v)
        logger1 = common.get_logger('查询流水功能')  # 调用日志
        logger1.debug('用户 %s 查询了账户流水' % user_data['username'])

        return True


# 8.加入购物车
@common.login_auth
def add_cart():
    print('开始浏览商品咯')
    # 定义一个字典便于存放商品 {'商品名称':{'price':100, 'count':2}
    shopping_cart = {}
    while True:
        # 先列出商品与价格
        product_list = [
            ['iphone', 10000],
            ['mi', 1999],
            ['huawei', 5000],
            ['samsung', 7000]
        ]
        # 用枚举的方法给列表中商品一一赋值
        for k, v in enumerate(product_list, start=1):
            print(k, v[0], v[1])
            '''
            结果:
            1 iphone 10000
            2 mi 1999
            3 huawei 5000
            4 samsung 7000
            '''
        # 让用户自己选择商品编号
        number = input('请选择你喜欢的商品编号(q to sign out)>>>: ')
        if number.isdigit():
            number = int(number)
            # print(len(product_list))  4
            # number = number - 1
            if number > len(product_list):
                print('没有该商品编号')
                continue
            trade_name = product_list[number-1][0]
            trade_price = product_list[number-1][1]

            # 验证商品是否存在
            if trade_name not in shopping_cart:

                # shopping_cart 实际为{'tea':{'price':goods_price, 'count':1},\
                # 'coffee':{'price':goods_price, 'count':1}}
                # 如果商品不在
                shopping_cart[trade_name] = {'price': trade_price, 'count': 1}
            else:
                shopping_cart[trade_name]['count'] += 1

            print('继续选择其他商品')
        elif number == 'q':
            # 保存商品信息到用户文本中
            #  判断商品不能为空
            if shopping_cart:
                # 先读取用户文件内商品字典
                path = settings.BASE_DIR
                path_file = os.path.join(path, 'db', '%s.json' % user_data['username'])
                with open(path_file, 'r', encoding='utf-8') as f:
                    user_dict = json.load(f)
                # 创建商品字典
                user_dict['shopping_cart'] = shopping_cart
                with open(path_file, 'w', encoding='utf-8') as f1:
                    json.dump(user_dict, f1, ensure_ascii=False)
                print('添加购物车成功')
                logger1 = common.get_logger('添加购物车功能')  # 调用日志
                logger1.debug('用户 %s 添加了 %s 手机到购物车中' % (user_data['username'], trade_name))
                break
        else:
            print('输入不合法')



# 9.查看购物车
@common.login_auth
def view_cart():
    print('开始查询购物车')
    while True:
        path = settings.BASE_DIR
        path_file = os.path.join(path, 'db', '%s.json' % user_data['username'])
        with open(path_file, 'r', encoding='utf-8') as f:
            user_dict = json.load(f)
        print(user_dict['shopping_cart'])
        logger1 = common.get_logger('查看购物车功能')  # 调用日志
        logger1.debug('用户 %s 查询了购物车' % user_data['username'])
        break





# 10.退出登录
def log_out():
    # 先判断是否登录 已经登录才可以退出
    if user_data['is_login']:
        choice = input('确认退出请按y,否则将返回主页').strip()
        if choice == 'y':
            # 用户确认退出 将全局标志位改为False即可
            user_data['is_login'] = False
            logger1 = common.get_logger('退出登录功能')  # 调用日志
            logger1.debug('用户 %s 退出了登录状态' % user_data['username'])
            return
        else:
            return
    else:
        print('当前未登录,无需退出')

image

posted @ 2021-11-30 17:10  Deity_JGX  阅读(51)  评论(0)    收藏  举报