ATM购物车项目总结

项目实现思路

ATM项目

ATM架构设计
	三层架构
    	core目录下的src.py(浏览器)              (展示层)
     	interface目录下的多个py文件(框架)      (核心逻辑层)
      	db目录下db_handler.py(数据库服务)      (数据处理层)

image

优先实现功能

在src.py的展示层写面条版的函数,先实现主题功能,暂时别想着优化

拆分函数

如下图是一个用户注册功能函数:
image
我们要做的是将其到interface、db构成三层架构
拆分到哪?
核心逻辑层 有user bank shop admin等文件,负责不同的功能, 将跟user相关的代码(登录、注册) 放入相应的user_innerface.py文件
然后如果在innerface层下需要进行数据操作,就调用db层的函数。
请注意:展示层只能访问逻辑层,不能访问数据层。

项目路径展示

image

项目启动文件 start.py

项目启动文件start.py可以放在项目根路径,也可以放在bin文件夹下。
该文件首先要做的第一件事就是使用os.path.dirname获取将当前项目路径,并将其导入sys.path。
然后就是调用展示层文件src.py。
使用if name == __name__:标志这个文件是启动文件。
image
请无视飘红,这是pycharm的小bug,实际程序是可以运行的。

配置文件 setting.py

image
配置文件应该存放的是一些不会变动的信息,如项目的根路径、数据库的路径、日志的存放位置。
当其他模块要用这些路径的时候的时候,从配置文件导入即可。注意当数据库db不存在时,settings.py会进行创建,所以在第三层一定要导入settings.py。

日志配置字典

可以先别急着实现日志功能,中后期再添加日志。
关于日志模块的配置信息,应该写在settings.py文件内,并且封装成函数方便调用:
image

日志函数

日志函数属于公共功能,应该放在common.py。
在每个接口文件的开头都放一个logger,方便调用,下面拿bank接口文件举例:
image
所有这个文件内产生的日志,都是银行日志。

展示层 src.py

用户注册

image

获取用户输入

获取用户输入包括 获取用户名和获取密码
可自行添加:退出、输入两次密码比对
这两个功能逻辑简单,可以放在展示层

md5加密

使用hashlib对用户输入的密码进行md5加密,将密文保存到数据层,下次用户登录直接比对密文。
展示层只调用加密函数,加密函数应该存放于lib/common.py路径下:
image

调user功能接口

将用户名、密文输入user接口函数:
image
接口函数在核心逻辑层,在user_interface.py文件内:
image
逻辑层调用数据层的select函数,检查用户是否注册。再建立用户信息字典,最后调用数据层的save函数,输入用户信息字典,保存数据。

用户登录

基本流程

1.获取用户输入
2.md5加密
3.调user功能接口
调user功能接口:
image

接口返回值一致性

这个接口返回两个值:登录状态,信息
请注意之前注册接口的返回值只有一个值,这样我们每次调接口都要查看到底有几个返回值,容易混淆。为了保持接口返回值一致性,应该都改成返回两个值
将用户名和密文传入login_interface函数:
这里需要注意的是,每次传入的参数,都需要进行校验,保证传入干净的数据。
image

登录成功修改全局字典

当登录成功后,会修改全局字典:
将全局字典'username'键的值从空改成当前用户。
image
全局字典对应的是cookies。也就是你登录网站之后,网站会给你返回一个通行证,浏览器帮你把这个通行证存在本地。下一次登录就拿出来给网站看,也就不需要重新输入用户名密码了。
还有一个原因就是,使用的用户多了之后,如果放在第二层、第三层则会增大服务器的开销,所以放在第一层让用户承担开销,更为合理。

登录校验装饰器

由于我们希望只有登录的用户,才能使用更多功能,所以要给其他函数添加装饰器。
image
只有当全局字典是有值的,才会执行被装饰函数。还记得吗,登录成功会将全局字典的值改为当前用户。

循环导入

装饰器理论上应该放入common.py,因为登录检验的逻辑不是我们应该给用户展示的,但是放入common.py容易引起循环导入:
image

查看余额

从全局字典获取当前用户名

调bank功能接口

这个功能比较简单,先调用bank接口(之前是user接口),再调用select函数得到用户信息字典,返回用户的余额数据即可。
image

余额提现

从全局字典获取当前用户名

调bank功能接口

image
传入用户名、提现金额两个参数。
接口函数在逻辑层:
image

判断用户输入是否是整数或小数

对用户输入的数字,我们要进行校验,可以将校验的代码封装成函数,放在common.py。需要用的时候直接调用。这里建议在第二层做校验用户输入,第一层只能输入数据。
image

手续费 免费额度

在用户提现时收取一定手续费,手续费的数据应该放在settings.py下,便于管理。
免费额度的意思是:在免费额度内,用户提现不需要手续费,如果超过额度,就需要手续费。
以支付宝为例:每个用户有2万元的免费额度,超出后按照0.1%收取手续费。
实现思路:可以在用户信息字典中添加一个键值对,存放该用户的提现免费额度。

账户充值

与余额提现很相似,故省略。
repay:
image
repay_interface:
image

用户转账

转账相对来说,比前面复杂一点点,需要指名给谁转账:
image

转账逻辑

关于转账需要注意:

  1. 不能转账给自己
  2. 确认被转账的人存在
  3. 确认用户余额足够转账
  4. 确认转账之后当前用户金额减少
  5. 确认被转账用户金额增加
    image

查看流水

在发生充值、提现、转账、购物车结算,这种涉及到账户余额变化的事件时,要将金额变动的信息加入用户信息字典。只要有钱的变动,就应该有流水信息。
image
所以应该在上述函数中,都添加一些代码,将流水信息添加到用户字典。

添加时间信息 转账流水

查看流水,就是将用户信息字典的流水信息拿出来展示,这个功能实现很简单,不再赘述。
也可以给流水添加时间信息:
image
注意转账功能的流水,既需要给当前用户添加流水,也需要给被转账的用户添加流水。
日志也可以实现这个功能,建议后期再添加日志。

添加购物车

核心逻辑

user_data = {'shop_car': {'印度飞饼': [20, 22]}}  # 现在的字典
swap_dict = {'印度飞饼': [100, 22], '仿真玩偶': [1, 10000]}  # 临时字典
for good in swap_dict:  # 1.键一个一个取出来
    if good in user_data.get('shop_car'):  # 2.如果商品在user-data中
        user_data.get('shop_car').get(good)[0] += swap_dict.get(good)[0]  # #3.修改数量的值
    else:
        user_data.get('shop_car')[good] = [swap_dict.get(good)[0], swap_dict.get(good)[1]]  # 如果不存在就新增键值对
print(user_data)

展示商品

用户需要先查看商品,才能添加购物车。可以在逻辑层写一个展示商品的函数。商品数据也可以放在逻辑层。商品数据也可以保存在文件,做成配置项,从settings导入。
image

使用临时字典

什么叫临时字典?在添加购物车时,我们运行用户循环添加,直到用户输入q则停止添加。如果不使用临时字典,则用户每次添加,都会进行一次数据层的文件写入。使用临时字典,则是将数据保存在字典中,等到用户输入q,再将字典中的数据一次性写入文件。总得来说就是减少了写入的次数。
image

展示临时字典

因为临时字典是在程序运行中临时存在的,所以无法通过查看文件中的用户信息,来看到临时字典里的东西。
只能及时展示,每次循环临时字典都会变动。

查看购物车

调用shop接口查看用户字典即可,很简单,不再赘述。购物车是空的时候,要进行提示。
image

清空购物车

主要逻辑

从用户字典读出当前购物车信息,对商品价格进行计算,比对余额是否充足,将余额减去商品价格,最后将购物车清空,记录金额变动的流水,最后写入用户信息字典。

管理员功能

校对用户信息字典

管理员用户也是用户,只不过他有一个键值对比较特殊is_admin:true。已经登录的用户想使用管理员功能,就要先进行用户字典中数据值的校对,如果is_admin == true则可以使用管理员功能。说明一点,对于普通的用户注册,它们的信息字典从创建开始默认:is_admin:false。也可以设置只有管理员用户才有这个键值对。
image
调用接口函数查看当前用户是不是管理员:
image
确认is_admin这个键的值:
image

校对全局变量

给全局变量字典扩充一个键值对:
image
登录成功后,会将这两个键值对的值都进行修改。如果是管理员登录则会将'is_admin'修改为true。
此时获取当前全局变量字典的信息,就可以进行是否为管理员的校验了。
image

有参装饰器

装饰器要能区分哪些是普通函数,哪些是管理员函数。所以要外界传一个参数,告诉装饰器现在装饰的是普通函数还是管理员函数。如果是管理员函数就多加一个分支,如果是普通函数就按照原来进行。
image
先校验用户是否登录,再校验当前功能函数级别。
如果这个函数是普通函数(装饰器传参为normal),则直接执行。
如果这个函数是管理员函数(type=='admin'),则校验全局字典的is_admin键值对。如果是True,则继续执行。
如果is_admin == false则是普通用户,就走else不然其使用管理员函数。
给函数装上有参装饰器:
image
这样看起来确实更加清晰推荐使用,但是装饰器获取的还是全局变量字典的值(cookies),全局变量在展示层,理论上用户可以修改,所以我觉得应该还是有安全隐患。但是也不钻牛角尖了,就这样了!!而且使用有参装饰器和全局字典就可以少写一个校验admin的接口函数。

冻结用户

冻结用户之前,管理员先要能查看所有的用户吧,所以要在数据层写一个查看所有用户的函数。
image
db目录大概是这样:
image
这个函数返回用户名的列表:
image
在接口层改用户的is_lock键值对:
image
在登录函数添加判断是否锁定的逻辑:
image

查看文件的函数

可以扩展这个查看文件的函数,给他传入参数(一个目录,文件后缀名),他给你返回一个此目录下所有你指定后缀名的文件。比如指定txt,就给你返回目录下所有文本文件。

解冻用户

会冻结难道不会解冻吗?
提示:获取db目录所有被冻结的用户名单

def unlock_user():
    cold_user = []
    user_list = db_handler.check_file()
    # 1.获取所有被冻结的用户
    for user in user_list:
        user_data_dict = db_handler.select(user)
        cold_massage = user_data_dict.get('is_lock')
        if cold_massage:
            cold_user.append(user)
        else:
            continue
    # 2.展示被冻用户
    for num, user in enumerate(cold_user):
        print(f'编号{num},用户名:{user}')
    while True:
        # 3.解冻
        admin_choice = input(f'请输入要冻结的用户编号>>>')
        if admin_choice.lower() == 'q':
            break
        if not admin_choice.isdigit():
            print('输入错误')
            continue
        admin_choice = int(admin_choice)
        if admin_choice not in list(range(len(cold_user))):
            print('超出范围')
            continue
        user_name = cold_user[admin_choice]
        user_data_dict = db_handler.select(user_name)
        user_data_dict['is_lock'] = False
        print(f'解冻{user_name}成功')
        db_handler.save(user_data_dict)

逻辑层 interface

image

对传入数据进行校验

逻辑层会接受到用户传入的数据,为了安全性,我们必须对传入数据进行校验。

数据层 db_handler.py

image

数据层的优化

数据层总是需要用到项目路径(base_path)、数据库路径(db_path),这两个路径都是不变的。
在每个函数内都写很麻烦,我们可以将其放入配置文件conf/setting.py
优化前:
image
放入配置文件:
(当db不存在就创建)
image
在数据层(db_hander.py)导入即可form conf import setting

数据层的select函数

image
注意:当用户存在,返回用户信息的字典,当用户不存在,返回None。

数据层的save函数

image
ensure_ascii参数保证写入的时候不会把中文转成unicode编码,增强可读性。

common文件到底有什么?

  1. 加密
  2. 用户登录装饰器
  3. 用户输入校验
  4. 日志函数
posted @ 2022-10-30 23:34  passion2021  阅读(77)  评论(0编辑  收藏  举报