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

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+购物车

猜你喜欢

转载自blog.csdn.net/qq_49821869/article/details/112007935
今日推荐