12

项目:ATM+购物车(超级详解,已完结)

 3 years ago
source link: https://blog.csdn.net/qq_49821869/article/details/112007935
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

python基础要打牢,ATM+购物车拿来练手不错,另外也可以了解开发软件的流程。总之感觉nice()。

2020.12.31今天是2020年最后一天,意义非凡,ATM+购物车是我第一次写的比较正规庞大的项目……原谅我没做过课设,感觉其实用三层架构来写也没那么难。(不过我打算上周天和上周一写完的…执行力不够)且当跨年礼物吧…2021再继续哈哈哈哈哈。

一、 需求分析

模拟实现一个ATM + 购物商城程序

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

"用户视图层" 展示给用户选择的功能
    1、注册功能
    2、登录功能
    3、查看余额
    4、提现功能
    5、还款功能
    6、转账功能
    7、查看流水
    8、购物功能
    9、查看购物车
    10、管理员功能

二、程序的架构分析

一个项目怎么从无到有

1.1需求分析

客户提出需求,公司拿到项目
——》公司出人去(一般一个前端一个后端)和客户讨论需求、商品需求功能的可实现性、项目价格、开发周期等,得到一个需求文档
——》公司内部开会得到一个开发文档交给不同岗位的程序员开发。

	- Python: 后端,爬虫
    
    - 不同的岗位:
        - UI界面设计:
            - 设计软件的布局,会分局软件的外观切成一张张图片。
        
        - 前端:
            - 拿到UI交给他的图片,然后去搭建网页面。
            - 设计一些页面中,哪些位置需要接收数据,需要进行数据交互。
        
        - 后端:
            - 直接核心的业务逻辑,调度数据库进行数据的增删查改。
        
        - 测试:
            - 会给代码进行全面测试,比如压力测试,界面测试(CF卡箱子)。
        
        - 运维:
            - 部署项目。

1.2程序的架构设计

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

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

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

程序架构图:
程序架构图

1.3分任务开发

1.4测试

1.5上线

三、软件开发目录

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

在这里插入图片描述

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

core中src代码如下

# 1.注册功能
def register():
    ...
#2.登录功能
def login():
    ...
#3.查看余额
def check_banlance():
    ...
#4.提现功能
def withdraw():
    ...
#5.还款功能
def repay():
    ...
#6.转账功能
def transfer():
    ...
#7.查看流水
def check_flow():
    ...
#8.购物功能
def shopping():
    ...
#9.查看购物车
def check_shop_car():
    ...
#10.管理员功能
def admin():
    ...

fuc_dic = {
    '1':register,
    '2':login,
    '3':check_banlance,
    '4':withdraw,
    '5':repay,
    '6':transfer,
    '7':check_flow,
    '8':shopping,
    '9':check_shop_car,
    '10':admin
}
def run():
    while True:
        print('''
    ====== ATM+购物车======
        1、注册功能
	    2、登录功能
	    3、查看余额
	    4、提现功能
	    5、还款功能
	    6、转账功能
	    7、查看流水
	    8、购物功能
	    9、查看购物车
	    10、管理员功能
	====== THE END======
	''')
        #接受用户输入
        choice = input('请输入命令编号:').strip()
        #判断命令编号是否合法
        if choice in fuc_dic:
            fuc_dic[choice]()
        else:
            print('请输入合法的命令编号!')

start.py代码如下


import os
import sys

# 添加解释器的环境变量

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

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

五、详细写各个功能

1、注册功能

核心逻辑分析

用户在视图层输入账号和密码,将账号和密码交给逻 辑 处理层,逻辑处理层调用数据处理层的功能来判断账号和密码是否存在。存在与否,则返回相应结果给逻辑处理层(接口层),若账号密码存在,则接口层直接返回,若不存在需要接口层需要完成注册功能,接口层需要做的是组织用户的信息,然后调用数据处理层的功能将用户信息写入相应json文件并返回,最终由接口层将是否注册成功返回给用户视图层。

def register():
    while True:
        # 1.用户输入用户名和密码进行校验
        name = input('请输入用户名:').strip()
        pwd = input('请输入密码:').strip()
        re_pwd = input('请确认密码').strip()
        # 2.小的逻辑判断
        if pwd == re_pwd:
            # 调用接口层来判断是否注册成功
            res,tag= user_interface.register_interface(name,pwd)
            if res:
                print(tag)
                break
            else:
                print(tag)

interface中的注册接口

def register_interface(username,password,balance = 15000):
    # 1.调用数据处理层中的select函数会返回用户信息字典或者None
    if db_handler.select(username):
        return False,'用户已存在,请重新输入!'
    else:
        password = common.get_pwd_md5(password)
        #1.组织用户信息,准备注册
        user_dic = {
            'username': username,
            'password': password,
            'balance': balance,
            # 用于记录用户流水的列表
            'flow': [],
            # 用于记录用户购物车
            'shop_car': {},
            # locked:用于记录用户是否被冻结
            # False: 未冻结   True: 已被冻结
            'locked': False
        }
        # 2.调用数据处理层的保存函数将用户信息写入文件
        #密码加密
        db_handler.save(user_dic)
        return True, f'{username} 注册成功!'

数据处理层功能的select功能

def select(username):
    # 1.接收用户名,拼接用户信息的完整json路径
    user_path = os.path.join(
        settings.BASE_PATH,f'{username}.json'
    )

    # 2.判断用户json文件是否存在
    if os.path.exists(user_path):
        with open(user_path,'r',encoding='utf-8') as f:
            user_dic = json.load(f)
            return user_dic
    # 不return默认返回None

conf下的配置信息

import os

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

lib中的common.py密码加密功能

# 密码加密
def get_pwd_md5(pwd):
    m = hashlib.md5(pwd.encode('utf-8'))
    salt = 'wangxunzhidashuaibi'
    m.updae(salt.encode('utf-8'))
    return m.hexdegist()

2、登录功能

核心逻辑分析
用户在视图层输入账号和密码交给登录接口层,接口层调用数据处理层的功能来判断用户是否登录成功,另外加了一个用户的登录状态变量login_user用于记录用户登录状态和一个用户登录认证装饰器。

def login():
    while True:
        name = input('请输入你的账号:').strip()
        password = input('请输入你的密码:').strip()
        # 登录接口:
        #登录成功返回True,登录成功
        res,tag = user_interface.login_interface(name,password)
        if res:
            global login_user
            login_user = name
            print(tag)
            break
        else:
            print(tag)

interface中的登录接口

def login_interface(name,password):
    # 调用数据处理层的select功能,来校验用户登录是否成功

    # 返回user_dic 或者None
    res = db_handler.select(name)
    if res:
        # 给用户输入的密码加密
        password = common.get_pwd_md5(password)
        if password == res.get('password'):
            return True,'登录成功!'
        else:
            return False,'密码错误!'
    return False,'用户不存在!请重新输入!'

用户登录认证

from core import src
def login_auth(func):
    def outter(*args,**kwargs):
        if src.login_user:
            res = func(*args,**kwargs)
            return res
        else:
            print('未登录,无法使用该功能!请登录!')
            src.login()
    return outter

3、查看余额

核心逻辑分析
用户登录后(未登录会被强制登录),查看余额,直接调用查看余额接口,查看余额接口调用数据处理层中的功能,完成任务。

@common.login_auth
def check_banlance():
    #直接调用查看余额接口
    #返回余额
    banlance = user_interface.check_balance_interface(login_user)
    print(f'====用户{login_user}余额为{banlance}====')

interface查看余额接口


# 查看余额接口
def check_bal_interface(username):
    user_dic = db_handler.select(username)
    return user_dic['balance']

4、提现功能

核心逻辑分析
用户在登录后(未登录会强制登录),在用户视图层输入提现金额,调用接口层中的提现接口,在提现接口中调用了数据处理层中的功能返回给接口层,接口层完成逻辑处理后将结果返回视图层。

记录流水和日志:要在接口层接入记录日志的功能。
原因:接口层是处理逻辑的核心所在,用户每次有提现/还款等功能都需要经过接口层。

注意:
转账要注意手续费的处理,以及用户输入的是否为数字等细节判断。

#4.提现功能
@common.login_auth
def withdraw():
    while True:
        # 1.让用户输入提现金额
        money = input('请输入提现金额:').strip()

        # 2.小逻辑判断:判断用户输入的是否为数字
        if money.isdigit():
            # 调用提现接口层
            # 返回(bool,str)
            flag,msg=bank_interface.withdraw_interface(login_user,money)
            if flag:
                print(msg)
                break
            else:
                print(msg)
        else:
            print('您的输入不合法,请您输入数字,比如1220')

interface提现接口

def withdraw_interface(name,money):
    #1.拿到用户信息
    user_dic = db_handler.select(name)
    #2.查看账户的余额
    balance = user_dic.get('balance')
    #判断余额是否大于提现金额和手续费
    if balance >= int(money) * 1.05:
        balance -= int(money) * 1.05
        user_dic['balance'] = balance

        #记录流水
        flow =f'用户{name}提现{money}成功!' \
                    f'手续费为{int(money)*0.05},余额为{balance}'
        user_dic['flow'].append(flow)
        #保存或者更新用户数据
        db_handler.save(user_dic)
        #流水既输出在屏幕上,也保存在文件中
        bank_logger.info(flow)

        return True,flow
    else:
        return False,'提现失败!请重新输入金额!'

common.py中日志功能(在接口层记录日志)

def get_logger(log_type):  # log_type ---> user
    '''
    :param log_type: 比如是 user日志,bank日志,购物商城日志
    :return:
    '''
    # 1、加载日志配置信息
    logging.config.dictConfig(
        settings.LOGGING_DIC
    )

    # 2、获取日志对象
    logger = logging.getLogger(log_type)

    return logger

settings.py新增日志配置字典

"""
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'
id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s'

# 定义日志输出格式 结束
# ****************注意1: log文件的目录
# BASE_PATH = os.path.dirname(os.path.dirname(__file__))
logfile_dir = os.path.join(BASE_PATH, 'log')
# print(logfile_dir)

# ****************注意2: log文件名
logfile_name = 'atm.log'

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

# log文件的全路径
logfile_path = os.path.join(logfile_dir, logfile_name)

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)传递
        },
    },
}

interface中新增bank_log日志对象

#银行流水日志对象
bank_logger = common.get_logger('bank')

5、还款功能

核心逻辑分析
用户在视图层输入还款金额,由接口层进行与数据处理层进行交互,完成功能。

@common.login_auth
def repay():
    while True:
        money = input('请输入你要还款的金额:').strip()
        if money.isdigit():
            if eval(money) >=0:
                # 接入还款接口
                flag,msg = bank_interface.repay_interface(login_user,money)
                if flag:
                    print(msg)
                    break
            else:
                print('金额不能小于0!请重新输入!')
        else:
            print('必须输入数字!请重新输入!')

interface还款接口

def repay_interface(name,money):
    #1.得到用户信息字典
    user_dic = db_handler.select(name)
    #2.将还款金额加入用户余额
    user_dic['balance'] += eval(money)
    #3.记录流水
    flow = f'====用户{name}还款{money}元成功,' \
           f'现有{user_dic["balance"]}元===='
    bank_logger.info(flow)
    #4.保存或者更新用户信息
    db_handler.save(user_dic)

    return True,flow


6、转账功能

核心逻辑分析
用户输入要转入的账户和金额,传入转账接口,转账接口利用数据处理层的select和save完成功能。

def transfer():
    while True:
        #1.接收转账对象及金额
        name = input('请输入你要转账对象:').strip()
        money = input('请输入你要转账金额:').strip()
        #2.判断用户输入的金额是否为数字以及是否>0
        if money.isdigit():
            if eval(money) > 0:
                flag,msg=bank_interface.transfer_interface(name,money)
                if flag:
                    print(msg)
                    break
                else:
                    print(msg)

            else:
                print('转账金额必须大于0!')
        else:
            print('必须输入数字!')

def transfer_interface(name,money):
    #1.判断被转账用户是否存在
    #接入数据处理层的查找功能,返回user_dic 或者None
    res = db_handler.select(name)
    if not res:
        return False,'目标用户不存在!'

    #2.获取当前用户数据
    user_dic = db_handler.select(src.login_user)

    #3.判断当前用户的余额是否够转账
    money = eval(money)
    if user_dic.get('balance') > money:
        user_dic['balance'] -= money
        res['balance'] += money
    else:
        return False,'用户的钱不够转账!'

    #4.记录流水
    from_log_flow = f'用户给用户{name}转账{money}元成功!'
    user_dic['flow'].append(from_log_flow)
    to_user_flow = f'用户接受用户{src.login_user}转账{money}元成功!'
    res['flow'].append(to_user_flow)
    bank_logger.info(f'用户{src.login_user}给用户{name}转账{money}元成功!')

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

    return True,from_log_flow

7、查看流水

核心逻辑分析
用户在视图层选择查看流水功能,调用查看流水接口,流水接口调用数据处理层的查看功能完成功能。

#7.查看流水
@common.login_auth
def check_flow():
    #接入查看流水接口
    res = bank_interface.check_flow_interface(login_user)
    if res:
        n = 1
        for i in res:
            print(f'{n}:{i}')
            n+=1
    else:
        print('用户{name}无流水记录!')

查看流水接口

def check_flow_interface(login_user):
    user_dic = db_handler.select(login_user)
    return user_dic.get('flow')

8、购物功能

核心逻辑分析
用户在视图层选择购物后==》
看到商品信息==》
选择购物/支付/加入购物车==》

1.购物:需要对用户输入的指令进行判定
2.支付:计算当前购物车总花费,接入支付接口,完成支付
3.加入购物车:读取之前的购物车,若存在加入重复的商品	名,则数量增加,否则新增购物,最后更新购物车
#8.购物功能
@common.login_auth
def shopping():
    # 1.打印商品信息
    shop_list = []
    print('============欢迎来到有趣用品商城============')
    with open(r'F:\ATM+购物车\db\shoppings.txt',encoding='utf-8') as f:
        n = 1 # 记录商品编号
        for line in f:
            shop_name,shop_price  = line.strip().split(':')
            shop_price = eval(shop_price)
            shop_list.append((shop_name,shop_price))
            print(f'商品编号:{n},'
                  f'商品名称:{shop_name},'
                  f'商品单价:{shop_price}')
            n+=1
    print('================24小时服务================')
    # 2.初始化购物车
    shopping_car = {}

    #3.模拟用户购物
    while True:
        choice = input('请输入商品编号(若结账输入y 添加购物车输入n):').strip()

        #3.1选择结账
        if choice == 'y':
            # 判断购物车是否为空 ==》
            #空==》不能支付
            #否则==》调用支付接口
            if not shopping_car:
                print('购物车为空,无法支付!')
            else:
                #调用商品结算接口
                flag,msg=shop_interface.shopping_interface(shopping_car,login_user)
                if flag:
                    print(msg)
                    break
                else:
                    print(msg)


        elif choice == 'n':
            #判断购物车内是否为空
            # 空==》提示用户选择商品
            # 非空 ==》代表用户选择了商品加入了购物车
            if not shopping_car:
                print('当前购物车为空,请在该购物车内添加商品!')
            else:
                #接入添加购物车接口
                res = shop_interface.add_shop_car(login_user,shopping_car)
                if res:
                    print('添加购物车成功!')
                    break
                else:
                    print('添加购物车失败!')
        else:
            #模拟用户选择商品
            #判断用户输入的编号是否合法
            if not choice.isdigit():
                print('请输入数字编号!')
            elif not type(eval(choice)) is  int:
                print('请输入整数编号!')
            elif eval(choice) > n or eval(choice) < 1:
                print('请输入1-{n}内的编号!')
            else:
                #获取用户所选商品的名字和单价
                shop_name,shop_price = shop_list[eval(choice)-1]
                #采用{'name':[price,number]}形式组织购物车
                if shop_name in shopping_car:
                    shopping_car[shop_name][1]+=1
                else:
                    shopping_car[shop_name] = [shop_price,1]
                print(f'当前购物车{shopping_car}')

商品结算接口和添加购物车接口

#商品结算接口
def shopping_interface(shopping_car,name):
    #1.计算用户商品总花销
    cost= 0
    # 采用{'name':[price,number]}形式组织购物车
    for i in shopping_car.values():
        price,number = i
        cost+=price*number

    #2.支付
    #调用银行支付接口
    flag,msg=bank_interface.pay_interface(name,cost)
    return flag,msg

#添加购物车接口
def add_shop_car(name,shopping_car):
    # 获取用户购物车
    use_dic = db_handler.select(name)
    user_car = use_dic['shop_car']

    #添加购物车
    for shop_name,shop_price_num in shopping_car.items():
        shop_price,shop_num  = shop_price_num
        #判断商品在不在购物车里面,若在则数量增加
        #否则将商品添加到购物车里
        #购物车组织{商品名:[单价,数量]}
        if shop_name in user_car:
            use_dic['shop_car'][shop_name][1] += shop_num
        else:
            use_dic['shop_car'][shop_name] = [shop_price,shop_num]
            #等同于use_dic['shop_car'].update({shop_name:[shop_price,shop_num]})
        #更新购物车数据
        db_handler.save(use_dic)
        return True
# 支付接口
def pay_interface(login_user, cost):
    user_dic = db_handler.select(login_user)

    # 判断用户金额是否足够
    if user_dic.get('balance') >= cost:
        # 减钱
        user_dic['balance'] -= cost

        # 记录消费流水
        flow = f'用户消费金额: [{cost}$]'
        user_dic['flow'].append(flow)

        # 保存数据
        db_handler.save(user_dic)

        # return True 或 return False 交给购物接口来做处理
        return True

    return False

9、查看购物车

核心逻辑分析
和查看余额一样,直接调用接口层,利用数据处理层查看功能完成任务。

#9.查看购物车
@common.login_auth
def check_shop_car():
    #调用查看购物车接口
    res = shop_interface.check_spc(login_user)
    if res:
        for i in res:
            print(i)
    else:
        print('用户购物车内没东西!')

查看购物车接口

def check_spc(name):
    user_dic = db_handler.select(name)
    shop_car = user_dic['shop_car']
    return shop_car

10、管理员功能

核心逻辑分析
有点像写一个小的ATM+购物车,只不过功能不一样。

管理员需要有的功能分析如下:
1.添加用户
2.修改用户额度
3.冻结用户

与写整个项目不同,管理员功能相对来说比较独立,我们在写这个功能的时候,同样采取三层架构。
即管理员视图层=》管理员接口层=》管理员数据处理层,但这里处理的是用户的数据,其实也是用户数据处理层。
#添加用户
def add_user():
    ...

#修改用户额度
def change_balance():
    ...

#冻结用户
def lock_user():
    ...

#管理员功能字典
admin_dic={}

#管理员视图层
def admin_run():
    print('=====欢迎进入管理员功能区=====')
    

10.1搭建管理员视图层以及管理员功能字典

#管理员功能字典
admin_dic={
    '0':['退出',None],
    '1':['添加用户',add_user],
    '2':['修改用户额度',change_balance],
    '3':['冻结用户',lock_user]
}

#管理员视图层
def admin_run():
    print('=====欢迎进入管理员功能区=====')
    #展示管理员功能
    while True:
        for k,v in admin_dic:
            print(f'{k}:{v[0]}')
    
        #管理员选择功能
        choice = input('请输入您想要使用的管理员功能').strip()
        
        #判断用户输入的合法性
        if choice not in admin_dic:
            print('请您输入合法的数字!')
        else:
            admin_dic[choice][1]()

10.2实现管理员各个功能

1、添加用户功能

#添加用户
def add_user():
    #相当于注册一个用户
    src.register()

2、修改用户额度功能

#修改用户额度
def change_balance():
    #获取要修改对象的账号和所要修改的额度
    while True:
        name = input('请输入要修改的账号:').strip()
        balance = input('请输入要修改的额度:').strip()

        #判断输入额度的合法性
        if not balance.isdigit():
            print('输入不合法!')
        else:
            #修改用户的额度,调用修改用户额度接口
            #修改成功返回 True和日志
            flag,msg = admin_interface.change_balance(name,balance)
            if flag:
                print(msg)
                break
            print(msg)

修改用户额度接口

#修改用户额度
def change_balance(name,money):
    #获取用户的信息字典
    #接入数据处理层查看数据:返回用户信息字典或者None
    user_dic = db_handler.select(name)

    if user_dic:
        #修改额度
        user_dic['balance'] = eval(money)
        #记录流水
        flow = f'管理员{src.login_user}修改{name}用户额度为{money}成功!'
        admin_logger.info(flow)
        #保存数据
        db_handler.save(user_dic)
        return True,flow
    return False,'用户不存在!'

3、冻结用户功能

#冻结用户
def lock_user():
    while True:
        name = input('请您输入要冻结的账号名').strip()

        #调用冻结用户接口
        flag,msg = admin_interface.lock_user_interface(name)
        if flag:
            print(msg)
            break
        print(msg)

冻结用户接口

#冻结用户
def lock_user_interface(name):
    #拿到用户信息字典
    user_dic = db_handler.select(name)
    if user_dic:
        #locked:用于记录用户是否被冻结
        # False: 未冻结   True: 已被冻结
        user_dic['locked'] = 'True'
        #记录日志
        msg =f'用户{name}被冻结!'
        admin_logger.info(msg)
        #保存数据
        db_handler.save(user_dic)
        return True,msg
    return False,'用户不存在!'

六、演示视频

ps:ATM+购物车项目压缩包私我

ATM+购物车


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK