Loading

ATM+购物车+管理员系统

ATM+购物车

点击下载源文件

需求分析

# 项目大致需求
  - 额度15000或自定义
  - 支持多账户登录
  - 可以查看账户余额
  - 可以提现(可自定义手续费比例)
  - 提供还款接口
  - 支持账户间转账
  - 记录每月日常消费流水
  - 实现购物商城,买东西加入购物车,调用信用卡接口结账
  - 提供管理接口,包括添加账户、用户额度,冻结账户等
  - ATM记录操作日志
  - 用户登录认证功能
# 提炼项目功能
  1.用户注册
  2.登录功能
  3.查看余额
  4.余额提现
  5.账户充值
  6.金额转账
  7.查看流水
  8.添加购物车
  9.查看购物车
  10.结算购物车
  11.管理员功能
    
    
# 项目大致技术栈
1.python基础核心编程
2.函数(装饰器)
3.常见内置模块(os,sys,json)

程序架构设计

1.程序设计的好处
    (1)思路清晰
    (2)不会出现写一半代码时推翻重写的情况
    (3方便自己或以后的同事更好维护
    
2.三层架构设计的好处
    (1)把每个功能都分层三部分,逻辑清晰
    (2)如果用户更换不同的用户界面或不同,
       的数据储存机制都不会影响接口层的核心
       逻辑代码,扩展性强。
    (3)可以在接口层,准确的记录日志与流水。
    
3.三层架构
    
    一 用户视图层
  用于与用户交互的,可以接受用户的输入,打印接口返回的数据。

    二 逻辑接口层
  接受 用户视图层 传递过来的参数,根据逻辑判断调用数据层加以处理,并返回一个结果给 用户视图层。

    三 数据处理层
  接受接口层传递过来的参数,做数据的处理
    - 保存数据  
    - 查看数据  
    - 更新数据
    - 删除数据

程序架构图:img

软件开发目录

conf:项目的配置信息
core:核心的代码
db:数据
interface:接口
lib:共用的一些功能
log:日志
readme:介绍项目的功能使用等
srart.py:项目的启动文件

image-20220410153915430

编写具体功能

创建用户功能字典及搭建用户图层

core中get_src代码如下

# 导入相关模块
from core import reginster,login,withdraw_balance,withdraw_trans_acc,withdraw_top_up,withdraw_flow,\
    shop,shopcar_cheak,shopcar_sett,admin

# 功能列表
func_dict = {
    '1':reginster.get_register,
    '2':login.get_login,
    '3':withdraw_balance.get_withdraw_balance,
    '4':withdraw_trans_acc.get_withdraw_trans_acc,
    '5':withdraw_top_up.get_withdraw_top_up,
    '6':withdraw_flow.get_withdraw_flow,
    '7':shop.get_shop,
    '8':shopcar_cheak.get_shopcar_cheak,
    '9':shopcar_sett.get_shopcar_sett,
    '10':admin.get_admin
}
def get_start():
    while True:
        print('''
        1.注册
        2.登录
        3.查看余额
        4.转账
        5.充值
        6,查看流水
        7,购物
        8.查看购物车
        9.清空购物车
        10.管理员功能
        ''')
        choise = input('请输入你想要选择的功能>>>:').strip()
        if choise in func_dict:
            func_name = func_dict[choise]
            func_name()
        else:
            print('输入有误')

start.py代码如下

# 启动脚本
import os
import sys
sys.path.append(os.path.dirname(__file__))
from core import get_src
if __name__ == '__main__':
    get_src.get_start()

conf.settings.py文件下的代码

import os
# 数据文件路径
BASE_DATA = os.path.dirname(os.path.dirname(__file__))
DB_DIR = os.path.join(BASE_DATA,'db')
if not os.path.exists(DB_DIR):
    os.mkdir(DB_DIR)

# 日志配置
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_DATA,'log')
# 如果不存在定义的日志目录就创建一个
if not os.path.isdir(LOG_DIR):
    os.mkdir(LOG_DIR)
logfile_name = 'ATM.log'
# log文件的全路径
logfile_path = os.path.join(LOG_DIR, logfile_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': 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配置
    },
}

db下的db_handle代码

import os
import json
from conf import settings

# 保存数据到db文件夹
def save(data_dict):
    username = data_dict['username']
    file_path = os.path.join(settings.DB_DIR, f'{username}.json')
    with open(file_path, 'w', encoding='utf8') as f:
        json.dump(data_dict, f, ensure_ascii=False)

# 读取用户数据
def read(username):
    file_path = os.path.join(settings.DB_DIR, f'{username}.json')
    if os.path.exists(file_path):
        with open(file_path, 'r', encoding='utf8') as f:
            data = json.load(f)
            return data

lib下的common代码

# 阿巴阿巴阿巴
import hashlib
import logging.config
from conf import settings
from functools import wraps

# MD5加密
def get_md5_pwd(username,password):
    md5 = hashlib.md5()
    md5.update(username[0].encode('utf8'))  # 动态加盐, 使用用户名第一个字符
    md5.update(password.encode('utf8'))
    md5_pwd = md5.hexdigest()
    return md5_pwd

# 认证登录装饰器
def outer(func_name):
    from interface import user_interface
    from core import login
    @wraps(func_name)
    def inner(*args,**kwargs):
        if user_interface.name['isname']:
            res = func_name(*args,**kwargs)
            return res
        else:
            print('先登录吧')
            login.get_login()
    return inner

# 日志模块
def get_logger(title):
    logging.config.dictConfig(settings.LOGGING_DIC)
    logger1 =logging.getLogger(title)
    return logger1

注册功能

用户视图层:获取用户的用户名密码

核心逻辑层:判断用户名密码是否为空、将密码使用MD5动态加盐、将处理好的数据发送给数据处理层存储起来,以用户名的方式创建一个json文件

用户注册视图层

# 用户登录界面
from interface import user_interface
def get_register():
    username = input('请输入用户名>>>>:').strip()
    password = input('请输入密码>>>>:').strip()
    confirm_pwd = input('确认密码>>>:').strip()
    if password != confirm_pwd:
        return '两次密码输入不一致'
    msg = user_interface.user_register_interface(username,password)
    print(msg)

用户注册逻辑层

文件夹inerface下的user_interface的代码

from db import db_handle
from lib import common
logger = common.get_logger('用户业务')
name = {'isname':''}

def user_register_interface(username, password, balance=20000):
    if len(username) == 0 and len(password) == 0:  # 判断用户名或密码是否为空
        return '用户名或密码不能为空'
    res = db_handle.read(username)  # 存在这个路径则直接返回(存在则表示已经有了此用户名)
    if res:
        return '用户名重复'
    get_pwd = common.get_md5_pwd(username, password)  # 调用MD5加密接口来实现密码加密
    data_dict = {
        'username': username,  # 用户名
        'password': get_pwd,  # 密码
        'balance': balance,  # 余额
        'flow': [],  # 流水信息
        'shop_car': {},  # 购物车
        'is_lock': False,  # 是否锁定状态
        'is_admin': False,  # 是否是管理员
    }
    db_handle.save(data_dict)  # 通过数据处理层将用户数据保存起来
    logger.debug(f'{username}注册成功')
    return '注册成功'

登录功能

用户在视图层输入账号和密码交给登录接口层

通过MD5加密模块来处理过后的密码来进行比对密码是否一致

接口层调用数据处理层的功能来判断用户是否登录成功

如果['is_lock']=True,那么直接返回退出

另外加了一个用户的登录状态变量name = {'isname' = ''}用于记录用户登录状态和一个用户登录认证装饰器

用户登录视图层

from interface import user_interface


def get_login():
    username = input('请输入用户名>>>:').strip()
    password = input('请输入密码>>>:').strip()
    msg = user_interface.user_login_interface(username, password)
    print(msg)

用户登录逻辑层

# 登录
def user_login_interface(username,password):
    res = db_handle.read(username)
    if res:
        if res['is_lock']:
            return '该账号已被拉入黑名单'
        get_pwd = common.get_md5_pwd(username,password)
        if get_pwd == res['password']:
            name['isname'] = username
            logger.debug(f'{username}登陆成功')
            return '登录成功'
        return '密码错误'
    return '该用户不存在'

认证登录装饰器

lib下的common里的代码

# 认证登录装饰器
def outer(func_name):
    from interface import user_interface
    from core import login
    @wraps(func_name)
    def inner(*args,**kwargs):
        if user_interface.name['isname']:
            res = func_name(*args,**kwargs)
            return res
        else:
            print('先登录吧')
            login.get_login()
    return inner

查看余额

用户登录后(未登录会被强制登录),查看余额,直接调用查看余额接口,查看余额接口调用数据处理层中的功能

查看余额用户视图层

from lib import common
from interface import user_interface,wallet_interface
@common.outer
def get_withdraw_balance():
    msg = wallet_interface.check_withdraw_interface(user_interface.name['isname'])
    print(f'您的余额为{msg}元')

查看余额用户逻辑层

from db import db_handle
from lib import common
logger = common.get_logger('钱包业务')
# 用户查看余额接口
def check_withdraw_interface(get_name):
    data = db_handle.read(get_name)
    logger.debug(f"{get_name}查看了余额")
    return data['balance']

转账

视图层

获取当前登录用户的信息

获取转账人的信息

获取转帐金额

判断是否是纯数字

核心层:

判断转账人是否存在

判断转账金额是否小于账户余额

将充值信息添加至用户字典的flow里

将转账后的数据重新写入用户数据

视图层代码

from lib import common
from interface import user_interface,wallet_interface
@common.outer
def get_withdraw_trans_acc():
    to_name = input('请输入你要转入账户的账户名>>>:').strip()
    to_money = input('请输入你要转入的金额>>>:').strip()
    if to_money.isdigit():
        to_money = int(to_money)
        res = wallet_interface.withdraw_trans_acc_interface(to_name,to_money,user_interface.name['isname'])
        print(res)
    else:
        print('只能输入数字')

逻辑层代码

def withdraw_trans_acc_interface(to_name,to_money,from_name):
    from_name_data = db_handle.read(from_name)
    if from_name_data['balance'] > to_money:  # 判断余额是否足够
        to_name_data = db_handle.read(to_name)
        from_name_data['balance'] -= to_money
        to_name_data['balance'] += to_money
        info = f"向{to_name}转账{to_money}元"
        info1 = f"收到{from_name}的转账{to_money}元"
        from_name_data['flow'].append(info)
        to_name_data['flow'].append(info1)
        db_handle.save(from_name_data)
        db_handle.save(to_name_data)
        logger.debug(f'{from_name}{info}')  # 记录转账人的日志
        logger.debug(f'{to_name}{info1}')  # 记录被转帐人的日志

        return info
    else:
        return '余额不足'

充值

视图层:

​ 输入充值金额

​ 判断是否是纯数字

​ 将数据发送给逻辑层(登录人信息,充值金额)

逻辑层:

​ 获取信息,将余额加上要充值的钱数

​ 将充值信息添加至用户字典的flow里

​ 将数据重新写入用户数据

视图层代码:

from lib import common
from interface import user_interface,wallet_interface
@common.outer
def get_withdraw_top_up():
    money = input('请输入你要充值的金额>>>:').strip()
    if money.isdigit():
        money = int(money)
        res = wallet_interface.withdraw_top_up_interface(money,user_interface.name['isname'])
        print(res)

逻辑层代码

# 用户充值
def withdraw_top_up_interface(money,get_name):
    data = db_handle.read(get_name)
    data['balance'] += money
    info = f'用户{get_name}成功充值{money}元'
    data['flow'].append(info)
    db_handle.save(data)
    logger.debug(f'{info},当前余额为{data["balance"]}')
    return info

查看流水

视图层:

​ 直接调用核心层查看流水接口,将登陆人信息发送给核心层

​ 将拿到的列表进行for循环展示

核心层:

​ 获取到用户数据,将数据里的flow对应的列表返回

视图层代码

from lib import common
from interface import user_interface,wallet_interface
@common.outer
def get_withdraw_flow():
    res = wallet_interface.check_withdraw_flow_interface(user_interface.name['isname'])
    for i in res:
        print(i)

逻辑层代码

def check_withdraw_flow_interface(get_name):
    data = db_handle.read(get_name)
    logger.debug(f'{get_name}查看了流水')
    return data['flow']

购物

用户层:

​ 直接调用商城添加购物车接口,发送登陆人信息

核心层:

​ 打印出商品列表

​ 新建一个临时的购物车字典

​ 获取用户选择的商品编号

​ 判断是否是纯数字

​ 判断是否在商品编号范围里面

​ 获取用户购买的商品数量

​ 判断是否是纯数字

​ 判断是否在临时购物车里面

​ 不在则直接新增商品键值对

​ 存在就增加商品价格和商品数量

​ 如果用户输入Q,则直接退出程序

​ 退出程序有以下判断

​ 先获取用户的全部数据>>>再获取用户的购物车数据

​ 判断临时的购物车的键是否在用户数据下的购物车里面

​ 如果在则只新增商品价格和数量

​ 不在则新增键值对

​ 将购物车数据重新写入用户数据里面

​ 将用户数据重新写入用户数据文件

视图层代码

from interface import user_interface,shop_interface
from lib import common
@common.outer
def get_shop():
    res = shop_interface.add_shop_car_interface(user_interface.name['isname'])
    print(res)

核心层代码

from db import db_handle, shop_list
from lib import common
logger = common.get_logger('购物业务')
# 上面代码为shop_interface里的不同接口共有的需求环境
# 添加购物车

def add_shop_car_interface(get_name):
    good_list = shop_list.shop_list()
    temp_shop_cat = {}
    while True:
        for i, j in enumerate(good_list):
            print(f'商品编号:{i}    商品名称:{j[0]}    商品单价:{j[1]}')
        get_num = input('请输入你想要购买的商品编号>>>输入Q退出>>>>:').strip().lower()
        if get_num == 'q':
            data = db_handle.read(get_name)
            data_shop_car = data['shop_car']
            for good_name in temp_shop_cat:
                if good_name in data_shop_car:
                    data_shop_car[good_name][0] += temp_shop_cat[good_name][0]
                    data_shop_car[good_name][1] += temp_shop_cat[good_name][1]
                else:
                    data_shop_car[good_name] = temp_shop_cat[good_name]
            data['shop_car'] = data_shop_car
            db_handle.save(data)
            return '已退出购物'
        if get_num.isdigit():
            get_num = int(get_num)
            if get_num in range(len(good_list)):
                get_count = input('请输入你想要购买的数量>>>:').strip()
                if get_count.isdigit():
                    get_count = int(get_count)
                    shop_name = good_list[get_num]
                    if shop_name[0] not in temp_shop_cat:
                        temp_shop_cat[shop_name[0]] = [shop_name[1] * get_count, get_count]
                        print(temp_shop_cat)
                    else:
                        gwc = temp_shop_cat[shop_name[0]]
                        gwc[0] += shop_name[1] * get_count
                        gwc[1] += get_count
                        temp_shop_cat[shop_name[0]] = gwc
                        print(temp_shop_cat)
                else:
                    print('请输入数字')
            else:
                print('编号超出最大范围')
        else:
            print('请输入数字')

查看购物车

视图层:

​ 直接调用核心层查看购物车接口,将用户数据发送过去

​ 将返回的值进行for循环打印展示

核心层:

​ 获取用户字典返回shop_car键的值

视图层代码

from interface import user_interface,shop_interface
from lib import common
@common.outer
def get_shopcar_cheak():
    res = shop_interface.check_shopcar_list_interface(user_interface.name['isname'])
    for i in res:
        print(f"商品名字:{i}    商品价格:{res[i][0]}     商品数量:{res[i][1]}")

核心层代码

# 查看购物车
def check_shopcar_list_interface(get_name):
    data = db_handle.read(get_name)
    return data['shop_car']

清空购物车

视图层:

​ 直接调用清空购物车的核心层接口,将登陆人的姓名传入

核心层:

​ 获取登录人的购物车列表

​ 创建一个计算金额的变量

​ 将金额进行循环+在一起

​ 判断用户余额是否足够

​ 足够则支付

​ 添加一个流水信息加在flow里面

​ 将空的购物车字典重新写入用户数据

视图层代码

from interface import user_interface, shop_interface
from lib import common
@common.outer
def get_shopcar_sett():
    res = shop_interface.shop_car_sett_interface(user_interface.name['isname'])
    print(res)

核心层代码

# 清空购物车
def shop_car_sett_interface(get_name):
    res = check_shopcar_list_interface(get_name)
    data = db_handle.read(get_name)
    money = 0
    for i in res:
        print(f"商品名字:{i}    商品价格:{res[i][0]}     商品数量:{res[i][1]}")

    for good_name in res:
        get_money = res[good_name][0]
        money += get_money
    print(f'总价{money}元')
    choise = input('是否购买>>>>按1为购买,其他操作退出>>>>>>')
    if choise == '1':
        if data['balance'] > money:
            data['balance'] -= money
            data['shop_car'] = {}
            info = f'消费了{money}元'
            data['flow'].append(info)
            db_handle.save(data)
            logger.info(f'{get_name}{info}')
            return info
        else:
            return '钱不够啊老铁'
    else:
        return '已退出'

管理员功能

一个整体的功能,分为4个小功能(添加\移除黑名单,删除用户,退出)

管理员功能列表搭建

判断当前登录账户是否是管理员

输入4则直接退出

视图层

from core import admin_add_black_list,admin_remove_balcklist,admin_del_user
from lib import common
from interface import admin_interface,user_interface
func_dict = {
    '1':admin_add_black_list.get_add_blacklist,
    '2':admin_remove_balcklist.get_remove_blacklist,
    '3':admin_del_user.get_del_user
}
@common.outer
def get_admin():
    flag,res = admin_interface.is_admin_user(user_interface.name['isname'])
    if flag:
        print(res)
        return
    while True:
        print('''
        1.添加黑名单
        2.移除黑名单
        3.删除用户
        4.退出 
        ''')
        choise = input('请输入你想执行的功能>>>:').strip()
        if choise == '4':
            return
        if choise in func_dict:
            func_name = func_dict[choise]
            func_name()
        else:
            print('输入有误')

核心层代码

# 验证是否是管理员
def is_admin_user(get_name):
    data  = db_handle.read(get_name)
    if data['is_admin']:
        return False,'管理员好'
    return True, '你还不是管理员'

添加黑名单

视图层:

​ 获取要拉黑的名字,调用添加黑名单接口

核心层:

​ 获取被要拉黑名字的数据,将数据字典的is_lock改为True

视图层代码

from interface import admin_interface
def get_add_blacklist():
    choise = input('请输入你想要封禁的用户名>>>:')
    res = admin_interface.add_balck_inerface(choise)
    print(res)

核心层代码

# 添加黑名单
def add_balck_inerface(get_name):
    data = db_handle.read(get_name)
    if data:
        data['is_lock'] = True
        db_handle.save(data)
        logger.debug(f'管理员{user_interface.name["isname"]}将用户{get_name}添加到了黑名单')
        return f'用户{get_name}已被封禁'
    else:
        return '没有此用户'

移除黑名单

原理和添加一样

视图层代码

from interface import admin_interface
def get_remove_blacklist():
    choise = input('请输入你想要解封的用户名>>>:')
    res = admin_interface.remove_balck_inerface(choise)
    print(res)

核心层代码

def remove_balck_inerface(get_name):
    data = db_handle.read(get_name)
    if data:
        data['is_lock'] = False
        db_handle.save(data)
        logger.debug(f'管理员{user_interface.name["isname"]}将用户{get_name}移除了黑名单')
        return f'用户{get_name}已被解封'
    else:
        return '没有此用户'

删除用户

用户层:

​ 获取要删除用户的名字

核心层:

​ 获取改名字所在的文件路径

​ 如果存在则删除

视图层代码:

from interface import admin_interface
def get_del_user():
    choise = input('请输入你想要删除的用户名>>>:')
    res = admin_interface.del_user_inerface(choise)
    print(res)

逻辑层代码

# 删除用户
def del_user_inerface(get_name):
    res = os.path.join(settings.DB_DIR,f'{get_name}.json')
    if os.path.exists(res):
        os.remove(res)
        logger.debug(f'管理员{user_interface.name["isname"]}将用户{get_name}删除了')
        return f'{get_name}用户已删除'
    else:
        return '没有此用户'
posted @ 2022-04-10 16:58  香菜根  阅读(25)  评论(0编辑  收藏  举报