Python函数实战之ATM与购物车系统

一、Python函数实战之ATM与购物车系统

  1. 项目需求:
    FUNC_MSG = {
    '0': '注销',
    '1': '登录',
    '2': '注册',
    '3': '查看余额',
    '4': '转账',
    '5': '还款',
    '6': '取款',
    '7': '查看流水',
    '8': '购物',
    '9': '购物车',
    'q': '退出'
    }
  2. 项目各模块实现
  • 配置文件(conf/setting.py)
点击查看代码
import os
import logging.config    #用于配置Python的日志系统
#(功能代码映射到中文描述的字典)
FUNC_MSG = {
    '0': '注销',
    '1': '登录',
    '2': '注册',
    '3': '查看余额',
    '4': '转账',
    '5': '还款',
    '6': '取款',
    '7': '查看流水',
    '8': '购物',
    '9': '购物车',
    'q': '退出'
}
# db/log文件路径
ATM_PATH = os.path.dirname(os.path.dirname(__file__))    #当前文件路径
DB_PATH = os.path.join(ATM_PATH, 'db')    #数据库目录
LOG_PATH = os.path.join(ATM_PATH, 'log')    #日志目录
GOODS_INFO_PATH = os.path.join(DB_PATH, 'goods_info.xlsx')    #商品信息Excel文件路径
# logging配置
"""
%(asctime)s:时间

%(levelname)s:日志级别

%(message)s:日志消息

%(filename)s:文件名

%(lineno)d:行号
"""
# 定义三种日志输出格式
standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \
                  '[%(levelname)s][%(message)s]'  # 其中name为getLogger()指定的名字;lineno为调用日志输出函数的语句所在的代码行
simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s'
# 定义日志输出格式 结束
# 日志目录设置(确保日志目录存在,不存在则创建。)
logfile_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))    # log文件的目录,需要自定义文件路径 # atm
logfile_dir = os.path.join(logfile_dir, 'log')
logfile_name = 'log.log'
if not os.path.isdir(logfile_dir):
    os.mkdir(logfile_dir)
# log文件的全路径
logfile_path = os.path.join(logfile_dir, logfile_name)
# log配置字典
LOGGING_DIC = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'standard': {
            'format': standard_format
        },
        'simple': {
            'format': simple_format
        },
    },
    'filters': {},    # filter可以不定义
    'handlers': {
        # 输出到控制台
        'console': {
            'level': 'INFO',
            'class': 'logging.StreamHandler',    # 打印到屏幕
            'formatter': 'simple'
        },
        # 输出到文件(打印到文件的日志,收集info及以上的日志)
        'default': {
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',    # 保存到文件
            'formatter': 'standard',
            'filename': logfile_path,    # 日志文件
            'maxBytes': 1024 * 1024 * 5,   # 日志大小 5M  (*****)
            'backupCount': 5,
            'encoding': 'utf8',     # 日志文件的编码,再也不用担心中文log乱码了
        },
    },
    'loggers': {
        # logging.getLogger(__name__)拿到的logger配置。
        # 如果''设置为固定值logger1,则下次导入必须设置成logging.getLogger('logger1')
        '':{
            # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
            'handlers': ['default', 'console'],
            'level': 'DEBUG',
            'propagate': False,    # 向上(更高level的logger)传递
        },
    },
}
  • 核心代码模块(core/src.py)
点击查看代码
from lib import common
from interface import user, bank, store

logger = common.load_logging_config('user')  # 加载日志配置,指定日志类别为user
user_auth = {'username': None}  # 初始化用户认证字典,记录当前登录用户
username = user_auth['username']


# 登录验证装饰器,确保只有登录用户能访问
@common.login_auth
def logout():
    username = None
    user_auth['username'] = None
    print(f'{username}已经注销')
    logger.info(f'{username}已经注销')  # 打印注销信息并记录日志


def login():
    print('欢迎来到登录功能')
    # 声明修改全局变量
    global username
    count = 0
    while count < 3:
        login_username, pwd = common.input_username_pwd()
        # 登录是否成功(布尔值),登录结果消息(字符串),错误级别
        flag, msg, level = user.login_interface(login_username, pwd)
        print(msg)
        if flag:
            user_auth['username'] = login_username
            username = login_username
            logger.info(f'{username}登录成功')
            break
        if level in [2, 3]:
            user.locked_interface(login_username)
            print('你的账户已被锁定!')
            break
        count += 1


def register():
    print('欢迎来到注册界面')
    count = 0
    while count < 3:
        username, pwd = common.input_username_pwd()
        flag, msg = user.register_interface(username, pwd)
        # 判断是否注册成功
        print(msg)
        if flag:
            logger.info(f'{username}注册成功')
            break
        count += 1


@common.login_auth
def check_extra():
    print('欢迎来到查看余额界面')
    extra = bank.check_extra_interface(username)
    print(f'你的余额为{extra}')
    logger.info(f'{username}查看余额,余额为{extra}')
    return extra


@common.login_auth
def transfer():
    print('欢迎来到转账功能')
    from_username = username
    to_username = input('请输入需要转账的用户:')
    flag = common.check_user(to_username)
    if flag:
        money = int(input('请输入需要转账的金额:'))
        flag, msg = bank.transfer_interface(from_username, to_username, money)
        print(msg)
        if flag:
            logger.info(f'{from_username}向{to_username}转了{money}')
            print(f'{from_username}向{to_username}转了{money}')
    else:
        print('用户不存在')


@common.login_auth
def repay():
    print('欢迎来到还款功能')
    flag, msg = bank.repay_interface(username)
    print(msg)
    logger.info(f'{username}{msg}')


@common.login_auth
def withdraw():
    print('欢迎来到取款功能')
    money = int(input('取现多少钱:').strip())
    flag, msg = bank.withdraw_interface(username, money)
    print(msg)
    if flag:
        logger.info(f'{username}{msg}')
        return


@common.login_auth
def history():
    print('欢迎来到银行流水查看功能')
    s = bank.history_interface(username)
    print(f'流水为:\n{s}')
    logger.info(f'{username}查看了流水')


@common.login_auth
def shopping():
    print('欢迎来到购物功能')
    # print(f"当前用户状态: {user_auth}")
    # print(f"全局username: {username}")
    msg = store.shopping_interface()
    print(f'{username}{msg}')
    logger.info(f'{username}{msg}')


@common.login_auth
def shopping_car():
    print('欢迎来到购物车功能')
    shopping_car_dict = store.shopping_car_dict
    flag, msg = store.shopping_car_interface(shopping_car_dict, username)
    print(msg)
    if flag:
        logger.info(f'{username}{msg}')


def run():
    FUNC_DICT = {
        '0': logout,
        '1': login,
        '2': register,
        '3': check_extra,
        '4': transfer,
        '5': repay,
        '6': withdraw,
        '7': history,
        '8': shopping,
        '9': shopping_car,
    }
    from conf.settings import FUNC_MSG
    """运行函数,进行功能选择"""
    while True:
        for k, v in FUNC_MSG.items():
            print(f'{k}: {v}')
        func_choice = input('请选择你需要的功能,输入q退出:').strip()
        # 退出判断
        if func_choice == 'q':
            break
        # 获取功能
        func = FUNC_DICT.get(func_choice)
        if not func:
            print('沙雕,功能不存在')
            continue
        func()


if __name__ == '__main__':
    run()
  • 数据管理模块(db/db_handler.py)
点击查看代码
import os
import json
import numpy as np
import pandas as pd
from conf import settings


def save_json(username: str, content: dict):
    """保存为json文件"""
    user_path = os.path.join(settings.DB_PATH, f'{username}.json')    # 安全拼接路径
    with open(user_path, 'w', encoding='utf8') as fw:
        json.dump(content, fw)  # 将字典写入JSON文件

# 表示返回字典
def read_json(username: str) -> dict:
    """读取json文件"""
    user_path = os.path.join(settings.DB_PATH, f'{username}.json')
    with open(user_path, 'r', encoding='utf8') as fr:
        data = json.load(fr)    # 从JSON文件读取数据为字典
    return data


def save_excel(df, filename):
    """保存excel"""
    df.to_excel(filename)    # df(Pandas DataFrame对象),DataFrame保存为Excel


def read_excel(filename):
    """读取excel"""
    df = pd.read_excel(filename, index_col=0, header=0)
    return df


if __name__ == '__main__':
    # 测试JSON功能
    save_json('nick', {'name': 'nick'})
    data = read_json('nick')
    print(data)
    # 测试Excel功能
    arr = np.random.rand(3, 4)  # 创建3x4随机数组
    df = pd.DataFrame(arr)  # 转为DataFrame
    save_excel(df,'goods_info.xlsx')  # 应添加文件名参数
    # 读取商品信息
    data = read_excel(settings.GOODS_INFO_PATH)
    print(data)
  • 用户,银行,购物三方接口模块(interface/user.py,bank.py,store.py)

user.py

点击查看代码
from db import db_handler


def register_interface(username, pwd):
    """注册接口"""
    flag = common.check_user(username)
    if flag:
        return False, '用户已存在'
    else:
        content = {'username': username, 'pwd': pwd, 'extra': 15000, 'locked': 0}
        db_handler.save_json(username, content)
        return True, '用户注册成功'


def login_interface(username, pwd):
    # 判断用户是否存在
    flag = common.check_user(username)
    if not flag:
        return False, '用户不存在', 1
    # 判断用户是否锁定
    data = db_handler.read_json(username)
    if data['locked']:
        return False, '用户已经锁定,去解锁', 2
    if pwd == data['pwd']:
        return True, '登录成功', 0
    return False, '密码错误', 3


def locked_interface(username):
    """输入错误密码就锁住"""
    flag = common.check_user(username)
    data = db_handler.read_json(username)
    data['locked'] = 1
    db_handler.save_json(username, data)

bank.py

点击查看代码
import os
from conf import settings
from db import db_handler
def check_extra_interface(username):
    """检查余额接口"""
    data = db_handler.read_json(username)
    return data['extra']

def transfer_interface(from_username,to_username,money):
    from_username_data = db_handler.read_json(from_username)
    to_username_data = db_handler.read_json(to_username)
    if from_username_data['extra'] > money:
        to_username_data['extra'] += money
        from_username_data['extra'] -= money

        db_handler.save_json(from_username, from_username_data)
        db_handler.save_json(to_username, to_username_data)
        return True, '转账成功'
    return False,'转账失败'

def repay_interface(username):
    username_data = db_handler.read_json(username)
    extra = username_data['extra']
    if extra >= 15000:
        return True, '无需还款'
    else:
        username_data['extra'] = 15000
        db_handler.save_json(username, username_data)
        return True, f'还款{(15000 - extra)*1.005}成功'

def withdraw_interface(username,money):
    username_data = db_handler.read_json(username)
    if username_data['extra'] > money:
        username_data['extra'] -= money
        db_handler.save_json(username, username_data)
        return True,f'取现{money}成功'
    return False,'余额不足'

def history_interface(username):
    filename = os.path.join(settings.LOG_PATH,'log.log')
    with open(filename,'r',encoding='utf8') as fr:
        s = ''
        for line in fr:
            if line.split('[')[-1].startswith(username):
                s += line
    return s

store.py

点击查看代码
from db import db_handler
from conf import settings
from lib import common

shopping_car_dict = dict()


def read_goods_interface():
    df = db_handler.read_excel(settings.GOODS_INFO_PATH)
    return df


df = read_goods_interface()


def shopping_interface():
    while True:
        print(df)
        common.goods_visualize(df)
        # 打印商品信息
        goods = df.columns
        goods_choice = input('请选择需要的商品,输入q退出:').strip()
        if goods_choice == 'q':
            break
        if goods_choice in goods:
            count_choice = input('请输入需要购买商品数量,输入q退出').strip()
            if count_choice == 'q':
                break
            count_choice = int(count_choice)
            if int(df.loc['amount', goods_choice]) < count_choice:
                print('擦,库存不足')
                continue
            else:
                goods_price = int(df.loc['price', goods_choice])
                if shopping_car_dict.get(goods_choice):
                    shopping_car_dict[goods_choice] += (count_choice * goods_price)
                else:
                    shopping_car_dict[goods_choice] += (count_choice * goods_price)
                print(f'已经把{goods_choice}*{count_choice}加入购物车')
                df.loc['amount', goods_choice] -= count_choice
        else:
            print('把眼睛捡起来!')
    return f'已加入购物车{shopping_car_dict}'


def shopping_car_interface(shopping_car_dict, username):
    goods_price = sum(shopping_car_dict.values())
    username_data = db_handler.read_json(username)
    if username_data['extra'] >= goods_price:
        username_data['extra'] -= goods_price
        username_data.update(shopping_car_dict)
        db_handler.save_json(username, username_data)
        new_shopping_car_dict = shopping_car_dict.copy()
        shopping_car_dict.clear()
        # 保存为excel文件
        db_handler.save_excel(df, settings.GOODS_INFO_PATH)
        return True, f'购物成功{new_shopping_car_dict}'
    else:
        shopping_car_dict.clear()
        return False, '穷逼也购物'


if __name__ == '__main__':
    read_goods_interface()

  • 公共功能模块(lib/common.py)
点击查看代码
import os
import hashlib
import logging    # 密码加密
import logging.config
from conf import settings
from interface import store

def login_auth(func):
    from core import src
    def wrapper(*args, **kwargs):
        # 判断是否登录
        if not src.user_auth.get('username'):    # 检查登录状态
            src.login()                          # 未登录则跳转登录
            res = func(*args, **kwargs)
            return res
        res = func(*args, **kwargs)              # 已登录直接执行函数
        return res

    return wrapper


def load_logging_config(name):
    logging.config.dictConfig(settings.LOGGING_DIC)    # 加载配置字典
    logger = logging.getLogger(name)     # 创建指定名称的日志记录器
    return logger


def check_user(username):
    """注册接口"""
    username_filename = os.path.join(settings.DB_PATH, f'{username}.json')
    if os.path.exists(username_filename):
        return True
    return None


def input_username_pwd():
    username = input('请输入你的用户名:').strip()
    pwd = input('请输入你的密码:').strip()
    # 密码加密
    m = hashlib.md5()    # 创建MD5哈希对象
    m.update(pwd.encode('utf-8'))    # 编码后更新哈希

    pwd = m.hexdigest()    # 获取十六进制哈希值
    return username, pwd


def goods_visualize(df):
    # 可写可不写
    """数据可视化"""
    import matplotlib.pyplot as plt
    from matplotlib.font_manager import FontProperties
    font = FontProperties(fname='')
    # 1. 准备数据
    goods_columns = df.columns.to_list()    # 获取商品名称列表
    goods_price = df.loc['price', :].to_list()

    price_index = list(range(len(goods_price)))
    goods_amount = df.loc['amount', :].to_list()
    amount_index = list(range(len(goods_amount)))
    # 2. 创建图形
    fig = plt.figure()
    ax1 = fig.add_subplot(121)
    # 3. 绘制价格条形图
    ax1.bar(price_index, goods_price, color='yellow')
    ax1.set_title('价格表', fontproperties=font)
    # 4. 设置X轴标签
    plt.xticks(amount_index, goods_columns, fontproperties=font, rotation=45)
    for i in amount_index:
        # 5. 添加数据标签
        plt.text(amount_index[i], goods_price[i], s=goods_price[i])
    # 同理绘制数量图...
    ax2 = fig.add_subplot(122)
    ax2.bar(price_index, goods_amount, color='green')
    ax2.set_title('数量表', fontproperties=font)
    plt.xticks(price_index, goods_columns, fontproperties=font, rotation=45)
    for i in amount_index:
        plt.text(amount_index[i], goods_price[i], s=goods_price[i])
    plt.suptitle('商品信息表', fontproperties=font, fontsize=20, weight='bold')
    plt.show()
    if __name__ == '__main__':
        # res = check_user('nick')
        # print(res)
        goods_visualize()   # 模块直接运行时测试可视化功能

posted @ 2025-08-08 23:03  handsomeyang++  阅读(6)  评论(0)    收藏  举报