ATM+购物车功能

ATM+购物车功能

一、项目需求

1.额度15000或自定义     -->  注册功能
2.实现购物商城,买东西加入购物车,调用信用卡接口结账  --> 购物功能、支付功能
3.可以提现,手续费5%   --> 提现功能
4.支持多账户登录  --> 登录功能
5.支持账户间转账  --> 转账功能
6.记录日常消费 -->  记录流水功能
7.提供还款接口 -->  还款功能
8.ATM记录操作日志 --> 记录日志功能
9.提供管理接口,包括添加账户、用户额度,冻结账户等。。。 ---> 管理员功能
10.用户认证用装饰器  --> 登录认证装饰器

二、 展示给用户选择的功能

1.注册功能
2.登陆功能
3.查看余额
4.提现功能
5.还款功能
6.转账功能
7.查看流水
8.添加购物车
9.查看购物车
10.结算购物车
11.管理员功能  

三、架构流程

采取MVC的架构方式     各层分开,编写接口
1、把每个功能都分层三部分,逻辑清晰
2、如果用户更换不同的用户界面或不同的数据库存储机制,不会影响接口层的核心逻辑代码,扩展性强。
3、可以在接口层,准确的记录日志与流水。

img

四、代码实现

start.py(程序的入口)

import os
import sys

base_dir = os.path.dirname(os.path.dirname(__file__))
sys.path.append(base_dir)

if __name__ == '__main__':
    from core import src
    src.run()

conf

setting.py(log日志的配置文件,项目的环境配置)

import os

BASE_DIR = os.path.dirname(os.path.dirname(__file__))
DB_DIR = os.path.join(BASE_DIR, 'db')
if not os.path.exists(DB_DIR):
    os.mkdir(DB_DIR)

# 提现手续费
MONEY_RATE = 0.05

# 我偷懒 不改大写了
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'

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

core

src.py

from interface import user_interface, bank_interface, shop_interface
from lib import common

is_login = {
    'username': ''
}


def register():
    # 1.获取用户相关数据
    username = input('请输入您的用户名>>>:').strip()
    password = input('请输入您的密码>>>:').strip()
    confirm_pwd = input('请确认您的密码>>>:').strip()
    # 2.判断两次密码是否一致
    if not password == confirm_pwd:
        print('两次密码不一致 请重新输入')
        return
    # 直接调用用户注册接口
    flag, msg = user_interface.register_interface(username, password)
    print(msg)


def login():
    # 1.获取用户相关数据
    username = input('请输入您的用户名>>>:').strip()
    password = input('请输入您的密码>>>:').strip()
    # 2.直接调用用户登录接口
    flag, msg = user_interface.login_interface(username, password)
    '''登录接口返回的数据值有两个 为了保证接口调用返回值的一致性 应该让所有的接口函数返回值个数保持一致 都应该变成两个'''
    if flag:
        is_login['username'] = username
    print(msg)


@common.login_auth
def check_balance():
    # 直接调用查看账户余额的接口
    flag, msg = bank_interface.check_balance_interface(is_login.get('username'))
    print(msg)


@common.login_auth
def withdraw():
    # 1.获取用户想要提现的具体金额即可
    target_money = input('请输入您想要提现的金额>>>:').strip()
    # 2.判断用户输入的是否是整数或者小数 100 123.23  正则表达式、代码处理
    '''转数字的操作 也可以封装成函数放到common.py中'''
    # 3.调用银行接口完成提现操作
    flag, msg = bank_interface.withdraw_interface(is_login.get('username'), target_money)
    print(msg)


@common.login_auth
def pay_back():
    # 1.直接获取用户想要充值的钱数
    target_money = input('请输入您想要充值的金额>>>:').strip()
    # 2.还需要判断是否是整数或者小数
    flag, msg = bank_interface.pay_back_interface(is_login.get('username'), target_money)
    print(msg)


@common.login_auth
def transfer():
    # 1.获取想要转账的用户名
    target_user = input('请输入您想要转账的用户>>>:').strip()
    # 2.获取想要转账的金额
    target_money = input('请输入您想要转账的金额>>>:').strip()
    # 3.直接调用转账的接口
    flag, msg = bank_interface.transfer_interface(is_login.get('username'), target_user, target_money)
    print(msg)


@common.login_auth
def check_flow():
    # 直接调用查看流水的接口即可
    flag, msg = bank_interface.check_flow_interface(is_login.get('username'))
    if flag:  # msg = ['','','']
        for data in msg:
            print(data)
    else:
        print(msg)


@common.login_auth
def add_shop_car():
    """
    1.先获取商品数据
    2.打印商品数据并让用户选择
    3.用户一旦退出 调用接口完成购物车数据更新
    :return:
    """
    # 1.直接调用添加购物车接口
    flag, msg = shop_interface.add_shop_car_interface(is_login.get('username'))
    print(msg)


@common.login_auth
def check_shop_car():
    # 1.直接调用查看购物车接口
    flag, msg = shop_interface.check_shop_car_interface(is_login.get('username'))
    if flag:  # msg = {'商品名称':[个数,单价]}
        for name, data_list in msg.items():
            print(f"商品:{name} | 商品个数:{data_list[0]} | 商品单价:{data_list[1]}")
    else:
        print(msg)


@common.login_auth
def pay_shop_car():
    flag, msg = shop_interface.pay_shop_car_interface(is_login.get('username'))
    print(msg)

@common.login_auth
def admin():
    pass


func_dict = {
    '1': register,
    '2': login,
    '3': check_balance,
    '4': withdraw,
    '5': pay_back,
    '6': transfer,
    '7': check_flow,
    '8': add_shop_car,
    '9': check_shop_car,
    '10': pay_shop_car,
    '11': admin
}


def run():
    while True:
        print("""
        ====================ATM+购物车==================
        1.注册功能
        2.登陆功能
        3.查看余额
        4.提现功能
        5.还款功能
        6.转账功能
        7.查看流水
        8.添加购物车
        9.查看购物车
        10.结算购物车
        11.管理员功能
        ==============================================
        """)
        choice_num = input('请输入功能编号>>>:').strip()
        if choice_num in func_dict:
            func_dict.get(choice_num)()
        else:
            print('暂无该功能编号')

db

user_date (用户存放json的目录)

import os
import json
from conf import setting

# 查找数据
def select(username):
    user_file_path = os.path.join(setting.DB_DIR,f'{username}.json')
    if os.path.exists(user_file_path):
        with open(user_file_path,'r',encoding='utf8') as f:
            return json.load(f)

# 保存数据
def save(user_dict):
    username = user_dict.get('username')
    user_file_path = os.path.join(setting.DB_DIR, f'{username}.json')
    with open(user_file_path, 'w', encoding='utf8') as f:
        json.dump(user_dict, f,ensure_ascii=False)

interface

bank_interface.py(银行业务处理接口)

from db import db_handler
from lib import common
from conf import setting
import time


def check_balance_interface(username):
    # 查询当前登录用户的字典数据
    user_dict = db_handler.select(username)
    # 从字典中获取余额信息返回给第一层
    user_balance = user_dict.get('balance')
    return True, f'用户{username}的账户余额是{user_balance}'


def withdraw_interface(username, target_money):
    flag1, value = common.get_num(target_money)
    if not flag1:
        return False, '请输入符合要求的金额数字'
    # 获取当前登录用户的字典数据
    user_dict = db_handler.select(username)
    # 获取余额信息及提现手续费
    user_balance = user_dict.get('balance')
    if user_balance >= value * (1 + setting.MONEY_RATE):
        user_dict['balance'] -= value * (1 + setting.MONEY_RATE)
        # 添加流水信息
        ctime = time.strftime('%Y-%m-%d %H:%M:%S')
        user_dict['water_flow'].append(f'时间{ctime}:账户{username}成功提现{value},手续费为{value * setting.MONEY_RATE}')
        db_handler.save(user_dict)
        return True, f'尊敬的{username},您成功提现{value},手续费为{value * setting.MONEY_RATE},账户余额{user_dict.get("balance")}'
    return False, f'尊敬的{username},您账户余额不够提现,请先充值'


def pay_back_interface(username, target_money):
    flag, value = common.get_num(target_money)
    if not flag:
        return False, '请输入符合要求的金额数字'
    # 获取当前字典数据
    user_dict = db_handler.select(username)
    # 获取充值钱数
    user_dict['balance'] += value
    # 添加流水信息
    ctime = time.strftime('%Y-%m-%d %H:%M:%S')
    user_dict['water_flow'].append(f'时间{ctime}:账户{username}您成功充值{value}目前账户余额为{user_dict.get("balance")}')
    # 保存用户数据
    db_handler.save(user_dict)
    return True, f'尊敬的{username},您成功充值{value},目前账户余额为{user_dict.get("balance")}'


def transfer_interface(current_user,target_user,target_money):
    # 获取转账用户的信息
    target_user_dict = db_handler.select(target_user)
    if not target_user_dict:
        return False, f'用户{target_user}不存在'
    flag, value = common.get_num(target_money)
    if not flag:
        return False, '请输入符合要求的整数或者小数'
    # 获取当前用户的信息
    current_user_dict = db_handler.select(current_user)
    # 判断余额是否充足
    if current_user_dict.get('balance') >= value:
        current_user_dict['balance'] -= value
        # 扣钱流水
        ctime = time.strftime('%Y-%m-%d %H:%M:%S')
        current_user_dict['water_flow'].append(f'时间{ctime}:给账户{target_user}转账{value}')
        target_user_dict['balance'] += value
        # 加钱流水
        target_user_dict['water_flow'].append(f'时间{ctime}:收到账户{target_user}转过来账{value}')
        db_handler.save(target_user_dict)
        db_handler.save(current_user_dict)
        return True,f'尊敬的{current_user},您成功向{target_user}转账{value},目前账户余额为{current_user_dict.get("balance")}'
    return False,'您当前余额不够转账'


def check_flow_interface(username):
    # 获取用户对应的用户字典
    user_dict = db_handler.select(username)
    if user_dict.get('water_flow'):
        return True,user_dict.get('water_flow')
    return False,'暂无该用户的流水记录'

shop_interface.py(购物业务接口处理)

from db import db_handler
from lib import common
import time

logger = common.get_logger('购物车模块')

def add_shop_car_interface(username):
    # 8.构造临时小字典存储商品信息
    temp_shop_car = {}
    while True:
        # 1.获取商品信息(目前是写死的 后期可以动态获取)
        good_list = [
            ['挂壁面', 3],
            ['印度飞饼', 22],
            ['极品木瓜', 666],
            ['土耳其土豆', 999],
            ['伊拉克拌面', 1000],
            ['董卓戏张飞公仔', 2000],
            ['仿真玩偶', 10000]
        ]
        # 2.循环打印商品信息供用户选择
        for num, good_data in enumerate(good_list):  # 0 []
            print(f"商品编号:{num}      |       商品名称:{good_data[0]}     |     商品单价:{good_data[1]}")
        # 3.获取用户输入的商品编号
        choice_num = input('请输入您想要购买的商品编号(q)>>>:').strip()
        '''10.添加结束标志 用于保存购物车数据'''
        if choice_num == 'q':
            # 11.获取当前登录用户的字典数据
            user_data_dict = db_handler.select(username)
            old_shop_car = user_data_dict.get('shop_car')  # {'印度飞饼':[10, 22]}
            # 12.保存购物车数据
            for g_name, g_list in temp_shop_car.items():
                if g_name in old_shop_car:
                    old_shop_car[g_name][0] += temp_shop_car[g_name][0]
                else:
                    old_shop_car[g_name] = g_list
            user_data_dict['shop_car'] = old_shop_car
            db_handler.save(user_data_dict)
            logger.info(f'{username}用户添加购物车成功')
            return True, '添加购物车成功 欢迎下次再来'
        # 4.判断编号是否是纯数字
        if not choice_num.isdigit():
            print('商品编号必须是纯数字')
            continue
        choice_num = int(choice_num)
        # 5.判断数字是否超出范围
        if choice_num not in range(len(good_list)):
            print('商品编号不在已存在的商品编号内 无法选择购买')
            continue
        # 6.根据商品编号获取商品信息
        target_good_list = good_list[choice_num]  # ['印度飞饼', 22]
        # 7.获取想要购买的商品个数
        good_num = input(f'请输入您想要购买的{target_good_list[0]}的商品数量>>>:').strip()
        if not good_num.isdigit():
            print('商品数量必须是纯数字')
            continue
        good_num = int(good_num)
        # 9.写入临时小字典中
        good_name = target_good_list[0]

        if good_name in temp_shop_car:
            temp_shop_car.get(good_name)[0] += good_num
        else:
            temp_shop_car[good_name] = [good_num, target_good_list[1]]


def check_shop_car_interface(username):
    # 1.获取当前登录用户的字典数据
    user_dict = db_handler.select(username)
    # 2.获取当前用户购物车数据
    shop_car = user_dict.get('shop_car')
    if shop_car:
        logger.info(f'用户{username}查看了购物车数据')
        return True, shop_car
    return False, '暂无购物车数据'


def pay_shop_car_interface(username):
    user_data_dict = db_handler.select(username)
    # 3.获取当前用户购物车数据及账户余额
    shop_car = user_data_dict.get('shop_car')  # {'印度飞饼':[10, 22],'公仔':[100, 100]}
    if not shop_car:
        return False, '购物车空空如也'
    current_balance = user_data_dict.get('balance')
    # 4.统计购物车商品总价
    total_money = 0
    for g_list in shop_car.values():  # [10, 22]     [100, 100]
        total_money += g_list[0] * g_list[1]
    # 5.比较余额是否充足
    if total_money > current_balance:
        return False, '你个穷逼 账户余额不够 再想办法 噶腰子去!!!'
    user_data_dict['balance'] -= total_money
    ctime = time.strftime('%Y-%m-%d %X')
    user_data_dict['water_flow'].append(f'时间{ctime}:购物消费了{total_money}')
    # 6.清空购物车
    user_data_dict['shop_car'] = {}
    logger.info(f'{username}疯狂消费了{total_money}')
    db_handler.save(user_data_dict)
    return True, f'尊敬的{username} 您本次消费{total_money} 卡上余额剩余{user_data_dict.get("balance")} 欢迎下次再来挥霍!!!'

user_interface.py(用户业务接口处理)

from db import db_handler

def register_interface(username,password):
    is_user = db_handler.select(username)
    # 判断用户名是否存在
    if is_user:
       return False, '用户名已存在'
    user_dict = {
        'username': username,
        'password': password,  # 密码后续应该加密
        'balance': 15000,
        'shop_car': {},
        'is_lock': False,
        'water_flow': [],
    }
    db_handler.save(user_dict)
    return True,f'用户名{username}注册成功'

def login_interface(username,password):
    user_dict = db_handler.select(username)
    if not user_dict:
        return False,'用户名不存在'
    if user_dict.get('password') == password:
        return True,'登录成功'
    else:
        return False, '密码错误'

lib

common.py(公用类,盐MD5加密,登录装饰器)

import hashlib
from core import src
import logging
import logging.config
from conf import settings


def get_hash(msg):
    md5 = hashlib.md5()
    md5.update(msg.encode('utf8'))
    return md5.hexdigest()


def login_auth(func_name):
    def inner(*args, **kwargs):
        if src.is_login.get('username'):
            res = func_name(*args, **kwargs)
            return res
        else:
            print('您暂未登录 请先去登录')
            src.login()

    return inner


def get_num(target_money):
    try:
        target_money = float(target_money)
    except ValueError:
        return False, '请输入整数或者小数'
    else:
        return True, target_money


def get_logger(msg):
    logging.config.dictConfig(settings.LOGGING_DIC)  # 自动加载字典中的配置
    logger1 = logging.getLogger(msg)
    return logger1

posted @ 2022-10-31 22:23  小王应该在学习!  阅读(95)  评论(0编辑  收藏  举报