Python之ATM+购物车项目

readme.md(项目说明书):

# ATM + 购物车项目

# 一个项目是如何从无到有的

# 1.需求分析(需要关注)
    额度15000 或 自定义   ---> 注册功能
    实现购物商城,买东西加入 购物车,调用信用卡接口 结账 ---> 购物功能,支付功能
    可以提现,手续费5% ---> 提现功能
    支持多账户登录 ---> 登录功能
    支持账户间转账 ---> 转账功能
    记录消费流水 ---> 记录流水功能
    提供还款接口 ---> 还款功能
    ATM记录操作日志 ---> 记录日志功能
    提供管理接口,包括添加账户,修改用户额度,冻结账户等... --->管理员功能(添加账户,修改用户额度,冻结账户)
    用户认证用装饰器 ---> 登录认证装饰器
    
    # 用户界面允许用户能选择的功能
        1.注册功能
        2.登录功能
        3.查看余额
        4.提现功能
        5.还款功能
        6.转账功能
        7.查看流水
        8.购物功能
        9.查看购物车功能
        10.管理员功能
            - 添加账户,修改用户额度,冻结账户

# 2.程序的架构设计(需要关注)
    - 三层架构(*****)
        - 视图层:专门用于与用户交互;
            - 展示功能给用户看,接收用户输入的数据,将功能返回的结果展示给用户看的;
        - 接口层:专门做业务逻辑的处理:
            - 接收到用户输入的数据,进行逻辑判断,然后返回判断后的结果给视图层;
        
        - 数据层: 专门做数据的处理
            - 增加数据
            -删除数据
            -更新数据
            -查看数据
    
# 3. 分任务开发
    - 项目经理:(项目的负责人)
        - UI (1-2): 界面设计(app端的界面/web界面)
        -前端(1-2): 拿到UI的设计图纸,开发页面
        -后端(2-3): 开发业务逻辑
        -运维(1): 拿到开发好的代码,部署在'服务器',上线运行
        
        
# 4.测试软件

# 5.上线运营

start.py(启动文件):

# 启动程序入口
# 1.将当前ATM项目工程,添加解释器的环境变量
import os  # 与操作系统进行交互
import sys  # 与解释器进行交互

# 1) 获取ATM的目录路径
base_path = os.path.dirname(__file__)

# 2) 将项目路径添加到解释器的环境变量中
sys.path.append(base_path)

# 3) 将视图层中的执行函数,引用到当前位置执行
from core import src

if __name__ == '__main__':
    # 执行src模块中的run函数
    src.run()

conf(配置文件夹):

settings.py(配置文件):

# 配置文件(常量)
import os

# ATM根目录路径
# D:\python全栈15期\day18\ATM
BASE_PATH = os.path.dirname(os.path.dirname(__file__))

# DB目录路径
# D:\python全栈15期\day18\ATM\db
DB_PATH = os.path.join(BASE_PATH, 'db')



'''
日志配置信息
'''
"""
logging配置
"""

import os

# 定义三种日志输出格式 开始
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'

# 定义日志输出格式 结束
# log文件的目录 *******
LOG_DRI_PATH = os.path.join(BASE_PATH, 'log')

# *******
LOG_FILE_NAME = 'access.log'  # log文件名

# 如果不存在定义的日志目录就创建一个
if not os.path.isdir(LOG_DRI_PATH):
    os.mkdir(LOG_DRI_PATH)

# log文件的全路径 *******
LOG_FILE_PATH = os.path.join(LOG_DRI_PATH, LOG_FILE_NAME)


# 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': LOG_FILE_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)传递
        },
    },
}

lib(公共方法文件夹)

common.py(公共方法文件):

# 此处写公共方法
import os
from conf import settings


# 登录认证装饰器模板
def login_auth(func):
    # 解决循环导入问题
    from core import src
    def inner(*args, **kwargs):

        # 判断用户的登录状态,若有值代表已登录,通过校验
        if src.user_info.get('user'):
            res = func(*args, **kwargs)
            return res

        else:
            src.login()

    return inner


# 查看所有用户名
def get_user_dic():
    # os.listdir(传递一个文件夹的路径)  ---> 获取文件夹中所有文件的名字,扔进一个列表中
    # 获取db文件夹中所有文件的名字
    user_file_list = os.listdir(settings.DB_PATH)
    # print(user_file_list)
    # 存放用户名字的列表
    user_name_dic = {}

    number = 1
    for user_file_name in user_file_list:
        if user_file_name.endswith('.json'):
            # egon.json
            user_name = user_file_name.split('.')[0]
            user_name_dic[str(number)] = user_name
            number += 1
    return user_name_dic


# 获取日志对象函数
import logging.config  # 到处logging模块的配置变量
def get_logger(user_type):
    # 1) 架子啊logging配置信息
    logging.config.dictConfig(settings.LOGGING_DIC)  # 导入上面定义的logging配置

    # 2) 获取 银行相关的 logger对象
    logger = logging.getLogger(user_type)  # 生成一个log实例

    # 3) logger.info(日志信息)  info:日志的级别
    # [INFO][2020-07-28 12:07:07,097][settings.py:97]tank 购买了 矮根派
    # logger.info('tank 购买了 矮根派')  # 记录该文件的运行状态

    # 4) 返回日志对象
    return logger

db(数据文件)

db_handler.py(数据处理功能文件):

# 数据层
import json
from conf import settings
import os


# 1、查看数据
def select(username):
    # 用户json文件的路径
    user_path = os.path.join(settings.DB_PATH, '%s.json' % username)

    # 判断当前注册用户是否存在,os.path.exists(需要判断的文件路径)
    if os.path.exists(user_path):
        # 读取文件中的数据
        with open(user_path, 'r', encoding='utf-8') as f:
            # f.read() ---> json_str ---> python
            user_dic = json.load(f)
            return user_dic


# 2、保存数据
def save(user_dic):
    # 用户json文件的路径
    user_path = os.path.join(settings.DB_PATH, '%s.json' % user_dic['name'])

    # 将数据写入文件中
    with open(user_path, 'w', encoding='utf-8') as f:
        # dict ---> json_str ---> f.write(json_str)   ----> json文件中
        json.dump(user_dic, f, ensure_ascii=False)
        # 文件刷新
        f.flush()


core(视图层)

src.py

# 视图层
from interface import user_interface
from interface import bank_interface
from interface import shop_interface
from lib import common

# 专门用于记录用户是否登录,登录状态
user_info = {
    'user': None
}


# 1.注册功能
# 拆分三层架构版本
def register():
    while True:
        # 1) 让用户输入用户名与密码
        username = input('请输入用户名: ').strip()
        password = input('请输入密码: ').strip()
        re_password = input('请输入密码: ').strip()

        # 2)小的逻辑判断
        if password == re_password:
            # 3) 再视图层中调用注册接口,将用户与密码传递给注册接口做 核心逻辑的处理
            # flag, msg = (False, '用户已存在,请重新输入')
            flag, msg = user_interface.register_interface(username, password)

            #  True, f'用户[{username}]注册成功'
            if flag:
                print(msg)
                break

            # False, '用户已存在,请重新输入'
            else:
                print(msg)

        else:
            print('两次密码不一致,请重新输入!')


# 2.登录功能
def login():
    while True:
        # 1) 让用户输入用户名与密码
        username = input('请输入用户名: ').strip()
        password = input('请输入密码: ').strip()

        # 3) 再视图层中调用 登录接口 ,将用户与密码传递给登录接口做 核心逻辑的处理
        flag, msg = user_interface.login_interface(username, password)

        #  True, f'用户[{username}]登录成功'
        if flag:
            print(msg)
            user_info['user'] = username
            break

        # False, '用户已存在,请重新输入'
        else:
            print(msg)


# 3.查看余额
@common.login_auth
def check_bal():
    # 直接调用查看余额接口层,获取数据
    bal = user_interface.check_bal_interface(user_info.get('user'))
    print(f'当前用户的余额为:[{bal}]')


# 4.提现功能
@common.login_auth
def withdraw():
    while True:
        # 1) 输入需要提现的金额
        balance = input('请输入需要提现的金额:').strip()
        # 小的逻辑判断,判断是否是数字,若是数字则转为int类型
        if not balance.isdigit():
            print('请输入数字类型!')
            continue

        # str ---> int
        balance = int(balance)

        # 2) 调用提现接口
        # flag, msg = (True, f'提现[{money}]成功! 手续费为:[{money2 - money}]')
        flag, msg = bank_interface.withdraw_interface(user_info.get('user'), balance)

        # 3)打印接口返回的结果
        if flag:
            print(msg)
            break

        else:
            print(msg)


# 5.还款功能
@common.login_auth
def repay():
    while True:
        # 1) 让用户输入还款金额
        money = input('请输入需要还款的金额: ').strip()

        # 2) 小的逻辑判断,判断是否是数字,若是数字则转为int类型
        if not money.isdigit():
            print('请输入数字类型!')
            continue
        money = int(money)

        # 3) 调用还款接口
        msg = bank_interface.repay_interface(user_info.get('user'), money)
        print(msg)
        break


# 6.转账功能
@common.login_auth
def transfer():
    while True:
        # 1) 让用户输入转账的目标用户
        target_user = input('请输入转账目标用户: ').strip()

        # 2) 让用户输入转账的金额
        money = input('请输入转账金额: ').strip()
        if not money.isdigit():
            print('请输入数字类型!')
            continue

        money = int(money)

        # 3)调用 银行中的 转账接口 将 “目标用户、当前登录用户、转账金额”
        flag, msg = bank_interface.transfer_interface(
            target_user, user_info.get('user'), money)

        if flag:
            print(msg)
            break
        else:
            print(msg)


# 7.查看流水
@common.login_auth
def check_flow():
    # 1) 直接调用查看 流水接口
    # [流水,]   /   None
    flow_list = bank_interface.check_flow_interface(user_info.get('user'))

    if flow_list:
        for line in flow_list:
            print(line)


# 8.购物功能
@common.login_auth
def shopping():
    # 购物车
    # {'商品名': [数量, 单价]}
    shop_car = {}
    # 总价
    cost = 0

    while True:
        # 1) 循环打印所有商品信息
        # 组织商品数据,并通过枚举打印
        good_list = [
            ['广东凤爪', 50],  # 0
            ['矮根派', 2],  # 1
            ['oldboy牌坦克', 100000],  # 2
            ['macbook', 20000],  # 3
            ['公仔', 150]  # 4
        ]
        # 枚举 enumerate(good_list)   ----> return (good_list的索引, 索引对应的值)
        # line ---> (索引, 索引对应的值)
        # index, good_l ---> (索引, 索引对应的值)
        print('========== 欢迎来到有趣商店 =========')
        for index, good_l in enumerate(good_list):
            # print(index, good_l)
            # 组织商品打印格式
            good_name, good_price = good_l
            print(f'商品编号: {index}  商品名称: {good_name} 商品单价: {good_price}')

        print('========== 欢迎客官下次再来 =========')

        # 2) 让用户去选择商品编号
        choice = input('请输入商品编号: ').strip()

        # 3) 判断当前用户输入的编号是否是数字
        if not choice.isdigit():
            print('请输入数字')
            continue
        choice = int(choice)

        # 4) 判断当前用户输入的编号是否在商品列表的 "索引范围内"
        if choice not in range(len(good_list)):
            print('编号有误,请重新输入')
            continue

        # 5)添加购物车,当用户没选择一个商品时,计算总价
        # 获取用户选择的商品:当前选择的商品名与商品单价
        good_n, good_p = good_list[choice]

        # 若当前选择的商品已经存在购物车中,则给商品数量 +1
        if good_n in shop_car:
            shop_car[good_n][0] += 1

        # 若当前选择的商品不存在购物车中,则给商品数量设置初始值 为 1
        # 作业: 课下写一个可以选择数量的功能,周五考
        else:
            shop_car[good_n] = [1, good_p]

        # 6) 用户每次选择完商品后  统计总价
        cost += good_p

        # 7) 让用户输入 是否确认购买若输入 y 则调用支付接口,进行结算,
        # 若输入的是 n 则调用 添加购物车接口,清空当前功能购物车字典,并退出购物功能
        choice2 = input('请输入y确认购买,或输入n退出并加入购物车, 输入 其他键 则继续选择商品:  ').strip()

        if choice2 == 'y':
            # 调用支付接口
            msg = bank_interface.pay_interface(
                user_info.get('user'), cost
            )
            print(msg)
            # 清空临时购物车shop_car
            shop_car = {}
            break

        elif choice2 == 'n':
            # 调用添加购物车接口
            msg = shop_interface.add_shop_car_interface(
                user_info.get('user'), shop_car)
            print(msg)
            # 清空临时购物车shop_car
            shop_car = {}
            break

        print('当前购物车:', shop_car)


# 9.查看购物车功能
@common.login_auth
def check_shop_car():
    # 1.直接调用查看购物车接口
    shop_car = shop_interface.check_shop_car_interface(user_info.get('user'))
    print(shop_car)


# 10.管理员功能
@common.login_auth
def admin():
    from core import admin
    admin_func_dic = {
        '1': admin.add_user,
        '2': admin.change_bal,
        '3': admin.lock_user,
        '4': admin.unlock_user,
    }
    while True:
        print('''
        1.添加账户
        2.修改用户额度
        3.冻结账户
        4.解冻结账户
        ''')
        choice = input('请输入功能编号: ').strip()

        # 判断用户选择的编号是否再函数功能字典中
        if choice not in admin_func_dic:
            print('编号有误,请重新输入!')
            continue

        # func_dic.get('1')   --->  register()
        admin_func_dic.get(choice)()





# 函数功能字典
func_dic = {
    '1': register,
    '2': login,
    '3': check_bal,
    '4': withdraw,
    '5': repay,
    '6': transfer,
    '7': check_flow,
    '8': shopping,
    '9': check_shop_car,
    '10': admin,
}


# 提供给用户选择的函数
def run():
    while True:
        print('''
        1.注册功能
        2.登录功能
        3.查看余额
        4.提现功能
        5.还款功能
        6.转账功能
        7.查看流水
        8.购物功能
        9.查看购物车功能
        10.管理员功能
        ''')

        choice = input('请输入功能编号: ').strip()

        # 判断用户选择的编号是否再函数功能字典中
        if choice not in func_dic:
            print('编号有误,请重新输入!')
            continue

        # func_dic.get('1')   --->  register()
        func_dic.get(choice)()


interface(接口层)

user_interface(用户接口层):

# 用户功能接口
from db import db_handler
from lib import common

user_logger = common.get_logger('user')


# 注册接口
def register_interface(username, password):
    # 逻辑处理   --->  接口层
    # 1) 去数据处理层获取用户数据
    # user_dic --->  用户字典 / None
    user_dic = db_handler.select(username)

    # 2) 若user_dic有数据,则代表用户存在,返回用户已存在,请重新输入给
    if user_dic:
        return False, '用户已存在,请重新输入'  # ---》 return (False, '用户已存在,请重新输入')

    # 3) 若用户不存在,则可以注册
    # 组织用户的数据,转成json数据 --> json_str 保存到文件中
    user_dic = {
        'name': username,
        'pwd': password,
        'balance': 15000,
        'flow': [],
        # 'shop_car': {'商品名称': ['商品数量', '单价']}
        'shop_car': {},
        'locked': False
    }

    # 4) 调用数据层中的保存数据功能save,将字典传递过去进行数据的保存
    db_handler.save(user_dic)

    user_logger.info(f'用户[{username}]注册成功')

    # 5) 返回注册结果给用户视图层
    return True, f'用户[{username}]注册成功'


# 登录接口
def login_interface(username, password):
    # 1) 先去数据层,获取用户字典数据
    # 用户字典 / None
    user_dic = db_handler.select(username)

    # 2) 判断用户是否存在,若存在则校验密码
    if user_dic:
        if password == user_dic.get('pwd'):
            return True, f'用户[{username}]登录成功!'

        else:
            return False, '密码错误,请重新输入'

    # 3) 若不存在,则返回 错误信息给视图层
    else:
        return False, '用户不存在,请重新输入'


# 查看余额接口
def check_bal_interface(username):
    user_dic = db_handler.select(username)

    return user_dic.get('balance')


bank_interface(银行接口层):

# 银行功能接口
from db import db_handler
import time
from lib import common

bank_logger = common.get_logger('bank')


# 提现接口
def withdraw_interface(username, money):
    # 查看当前用户的字典数据
    user_dic = db_handler.select(username)

    # 可以提现,手续费5%
    # 1) 判断用户账户的金额是否,大于等于 提现金额 + 5%手续费
    # 提现金额 + 5%手续费    money + money * 0.05
    money2 = money * 1.05
    if user_dic.get('balance') >= money2:
        # 给当前用户字典金额减钱
        user_dic['balance'] -= money2

        # 制作一个流水
        flow_str = f'{time.strftime("%Y-%m-%d %X")}  提现[{money}]成功! 手续费为:[{money2 - money}]'

        # 记录日志
        bank_logger.info(flow_str)

        # 更新数据前,添加流水
        user_dic['flow'].append(flow_str)

        db_handler.save(user_dic)

        return True, flow_str

    else:
        return False, '余额不足,快去充钱!'


# 还款接口
def repay_interface(username, money):

    # 1) 先将当前用户字典取出来
    user_dic = db_handler.select(username)

    # 2)直接加钱
    user_dic['balance'] += money

    # 制作一个流水
    flow_str = f'{time.strftime("%Y-%m-%d %X")} 还款[{money}]成功!'

    # 记录日志
    bank_logger.info(flow_str)

    # 更新数据前,添加流水
    user_dic['flow'].append(flow_str)

    # 3)再更新数据
    db_handler.save(user_dic)

    return f'还款[{money}]成功!'


# 转账接口
def transfer_interface(target_name, login_name, money):
    '''
    :param target_name: 目标用户
    :param login_name: 当前用户
    :param money: 转账金额
    :return: bool, str
    '''
    # 1) 判断目标用户是否存在,若不存在,则直接返回False,错误信息
    target_user_dic = db_handler.select(target_name)
    if not target_user_dic:
        return False, '目标用户不存在!'

    # 2)若存在,获取当前用户的字典数据
    login_user_dic = db_handler.select(login_name)

    # 3)判断当前用户金额是否 大于等于 转账金额
    if login_user_dic.get('balance') >= money:
        # 4)若条件成立,则开始给 “目标用户 加钱  , 当前用户 减钱”
        target_user_dic['balance'] += money
        login_user_dic['balance'] -= money

        # 制作两个流水
        # 当前用户流水
        flow_str1 = f'{time.strftime("%Y-%m-%d %X")} 用户:[{login_name}] 给 用户:[{target_name}] 转账 [{money}]元!'

        # 记录日志
        bank_logger.info(flow_str1)

        # 目标用户流水
        flow_str2 = f'{time.strftime("%Y-%m-%d %X")} 用户:[{target_name}] 接收到 用户:[{login_name}] 转账 [{money}]元!'

        # 更新数据前,添加流水
        # 记录当前用户流水
        login_user_dic['flow'].append(flow_str1)
        # 记录目标用户流水
        target_user_dic['flow'].append(flow_str2)

        # 5)数据修改后,更新数据,最后返回  True, 转账成功  给视图层
        db_handler.save(target_user_dic)
        db_handler.save(login_user_dic)
        return True, '转账成功'

    else:
        return False, '穷*,账户金额不足。'


# 查看流水接口
def check_flow_interface(username):

    # 1) 直接查数据
    user_dic = db_handler.select(username)

    # 2) 判断流水是否有数据
    if user_dic.get('flow'):
        return user_dic.get('flow')


# 支付接口
def pay_interface(username, cost):

    # 1) 查看当前用户的余额是否 >= 商品总价cost
    user_dic = db_handler.select(username)

    if user_dic.get('balance') >= cost:
        user_dic['balance'] -= cost

        # 2) 记录支付流水
        flow_str = f'{time.strftime("%Y-%m-%d %X")} 用户:[{username}] 购物支付金额为: [{cost}]'
        user_dic['flow'].append(flow_str)

        # 3) 更新用户数据
        db_handler.save(user_dic)

        return '支付成功'
    else:
        return '余额不足,退出程序!'


shop_interface(商店接口层):

# 购物商城功能接口
from db import db_handler


# 添加购物车接口
def add_shop_car_interface(username, shop_car):
    # 1) 获取当前登录用户的字典
    user_dic = db_handler.select(username)

    # 2) 给当前用户字典中的购物车 更新
    # 先循环用户选择的临时购物车
    for good_name in shop_car:
        # 判断当前选择的购物车 是否有商品在 用户字典的购物车中
        if good_name in user_dic.get('shop_car'):
            # user_dic['shop_car'][good_name][0] 用户字典中的购物车 商品的数量
            # shop_car[good_name][0]  在购物功能中传递过来的购物车 商品数量
            user_dic['shop_car'][good_name][0] += shop_car[good_name][0]
        else:
            user_dic['shop_car'].update(
                {good_name: shop_car[good_name]})

    db_handler.save(user_dic)

    return '添加购物车成功!'


# 查看购物车接口
def check_shop_car_interface(username):
    # 1) 获取当前用户的字典
    user_dic = db_handler.select(username)

    # 2) 直接将购物车返回给视图层
    return user_dic.get('shop_car')

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值