目录

项目实现思路

ATM项目

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

优先实现功能

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

拆分函数

如下图是一个用户注册功能函数:



我们要做的是将其到interface、db构成三层架构

拆分到哪?

核心逻辑层 有user bank shop admin等文件,负责不同的功能, 将跟user相关的代码(登录、注册) 放入相应的user_innerface.py文件

然后如果在innerface层下需要进行数据操作,就调用db层的函数。

请注意:展示层只能访问逻辑层,不能访问数据层。

项目路径展示

项目启动文件 start.py

项目启动文件start.py可以放在项目根路径,也可以放在bin文件夹下。

该文件首先要做的第一件事就是使用os.path.dirname获取将当前项目路径,并将其导入sys.path。

然后就是调用展示层文件src.py。

使用if name == __name__:标志这个文件是启动文件。



请无视飘红,这是pycharm的小bug,实际程序是可以运行的。

配置文件 setting.py



配置文件应该存放的是一些不会变动的信息,如项目的根路径、数据库的路径、日志的存放位置。

当其他模块要用这些路径的时候的时候,从配置文件导入即可。注意当数据库db不存在时,settings.py会进行创建,所以在第三层一定要导入settings.py。

日志配置字典

可以先别急着实现日志功能,中后期再添加日志。

关于日志模块的配置信息,应该写在settings.py文件内,并且封装成函数方便调用:

日志函数

日志函数属于公共功能,应该放在common.py。

在每个接口文件的开头都放一个logger,方便调用,下面拿bank接口文件举例:



所有这个文件内产生的日志,都是银行日志。

展示层 src.py

用户注册

获取用户输入

获取用户输入包括 获取用户名和获取密码

可自行添加:退出、输入两次密码比对

这两个功能逻辑简单,可以放在展示层

md5加密

使用hashlib对用户输入的密码进行md5加密,将密文保存到数据层,下次用户登录直接比对密文。

展示层只调用加密函数,加密函数应该存放于lib/common.py路径下:

调user功能接口

将用户名、密文输入user接口函数:



接口函数在核心逻辑层,在user_interface.py文件内:



逻辑层调用数据层的select函数,检查用户是否注册。再建立用户信息字典,最后调用数据层的save函数,输入用户信息字典,保存数据。

用户登录

基本流程

1.获取用户输入

2.md5加密

3.调user功能接口

调user功能接口:

接口返回值一致性

这个接口返回两个值:登录状态,信息

请注意之前注册接口的返回值只有一个值,这样我们每次调接口都要查看到底有几个返回值,容易混淆。为了保持接口返回值一致性,应该都改成返回两个值

将用户名和密文传入login_interface函数:

这里需要注意的是,每次传入的参数,都需要进行校验,保证传入干净的数据。

登录成功修改全局字典

当登录成功后,会修改全局字典:

将全局字典'username'键的值从空改成当前用户。



全局字典对应的是cookies。也就是你登录网站之后,网站会给你返回一个通行证,浏览器帮你把这个通行证存在本地。下一次登录就拿出来给网站看,也就不需要重新输入用户名密码了。

还有一个原因就是,使用的用户多了之后,如果放在第二层、第三层则会增大服务器的开销,所以放在第一层让用户承担开销,更为合理。

登录校验装饰器

由于我们希望只有登录的用户,才能使用更多功能,所以要给其他函数添加装饰器。



只有当全局字典是有值的,才会执行被装饰函数。还记得吗,登录成功会将全局字典的值改为当前用户。

循环导入

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

查看余额

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

调bank功能接口

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

余额提现

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

调bank功能接口



传入用户名、提现金额两个参数。

接口函数在逻辑层:

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

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

手续费 免费额度

在用户提现时收取一定手续费,手续费的数据应该放在settings.py下,便于管理。

免费额度的意思是:在免费额度内,用户提现不需要手续费,如果超过额度,就需要手续费。

以支付宝为例:每个用户有2万元的免费额度,超出后按照0.1%收取手续费。

实现思路:可以在用户信息字典中添加一个键值对,存放该用户的提现免费额度。

账户充值

与余额提现很相似,故省略。

repay:



repay_interface:

用户转账

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

转账逻辑

关于转账需要注意:

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

查看流水

在发生充值、提现、转账、购物车结算,这种涉及到账户余额变化的事件时,要将金额变动的信息加入用户信息字典。只要有钱的变动,就应该有流水信息。



所以应该在上述函数中,都添加一些代码,将流水信息添加到用户字典。

添加时间信息 转账流水

查看流水,就是将用户信息字典的流水信息拿出来展示,这个功能实现很简单,不再赘述。

也可以给流水添加时间信息:



注意转账功能的流水,既需要给当前用户添加流水,也需要给被转账的用户添加流水。

日志也可以实现这个功能,建议后期再添加日志。

添加购物车

核心逻辑

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导入。

使用临时字典

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

展示临时字典

因为临时字典是在程序运行中临时存在的,所以无法通过查看文件中的用户信息,来看到临时字典里的东西。

只能及时展示,每次循环临时字典都会变动。

查看购物车

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

清空购物车

主要逻辑

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

管理员功能

校对用户信息字典

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



调用接口函数查看当前用户是不是管理员:



确认is_admin这个键的值:

校对全局变量

给全局变量字典扩充一个键值对:



登录成功后,会将这两个键值对的值都进行修改。如果是管理员登录则会将'is_admin'修改为true。

此时获取当前全局变量字典的信息,就可以进行是否为管理员的校验了。

有参装饰器

装饰器要能区分哪些是普通函数,哪些是管理员函数。所以要外界传一个参数,告诉装饰器现在装饰的是普通函数还是管理员函数。如果是管理员函数就多加一个分支,如果是普通函数就按照原来进行。



先校验用户是否登录,再校验当前功能函数级别。

如果这个函数是普通函数(装饰器传参为normal),则直接执行。

如果这个函数是管理员函数(type=='admin'),则校验全局字典的is_admin键值对。如果是True,则继续执行。

如果is_admin == false则是普通用户,就走else不然其使用管理员函数。

给函数装上有参装饰器:



这样看起来确实更加清晰推荐使用,但是装饰器获取的还是全局变量字典的值(cookies),全局变量在展示层,理论上用户可以修改,所以我觉得应该还是有安全隐患。但是也不钻牛角尖了,就这样了!!而且使用有参装饰器和全局字典就可以少写一个校验admin的接口函数。

冻结用户

冻结用户之前,管理员先要能查看所有的用户吧,所以要在数据层写一个查看所有用户的函数。



db目录大概是这样:



这个函数返回用户名的列表:



在接口层改用户的is_lock键值对:



在登录函数添加判断是否锁定的逻辑:

查看文件的函数

可以扩展这个查看文件的函数,给他传入参数(一个目录,文件后缀名),他给你返回一个此目录下所有你指定后缀名的文件。比如指定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

对传入数据进行校验

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

数据层 db_handler.py

数据层的优化

数据层总是需要用到项目路径(base_path)、数据库路径(db_path),这两个路径都是不变的。

在每个函数内都写很麻烦,我们可以将其放入配置文件conf/setting.py

优化前:



放入配置文件:

(当db不存在就创建)



在数据层(db_hander.py)导入即可form conf import setting

数据层的select函数



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

数据层的save函数



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

common文件到底有什么?

  1. 加密
  2. 用户登录装饰器
  3. 用户输入校验
  4. 日志函数

最新文章

  1. jQuery学习之路(7)- 用原生JavaScript实现jQuery的某些简单功能
  2. iOS AFNetworking中cookie重定向
  3. BZOJ 1564: [NOI2009]二叉查找树
  4. Java 使用Redis缓存工具的图文详细方法
  5. Oracle定义varchar2()类型存储汉字的长度问题
  6. Linux网桥设置
  7. sql server 判断是否存在数据库,表,列,视图
  8. ava中拦截器 过滤器 监听器都有什么区别
  9. workstack windows to openstack
  10. OC4_可变数组
  11. Oracle---->基本DDL
  12. 【linux】U-BOOT与linux kernel通信: struct tag
  13. Mysql PHP
  14. 大话Python正则表达式
  15. Windows 窗体中的事件顺序(WinForm)
  16. 异步请求取得json数据
  17. linux下redis的安装方法
  18. 好用的漂浮广告 jquery
  19. Spring data Jpa 分页从1开始,查询方法兼容 Mybatis,分页参数兼容Jqgrid
  20. java后端时间处理工具类,返回 "XXX 前" 的字符串

热门文章

  1. Mapping
  2. linux系统安装Confluence
  3. SQL基础语句入门
  4. VS中git概念解析与深度使用
  5. Linx__Ubuntu_APT
  6. Flink的异步算子的原理及使用
  7. 通过刷题HTML遇到的问题
  8. LeetCode------移动零(5)【数组】
  9. 2流高手速成记(之四):SpringBoot整合redis及mongodb
  10. 抛砖系列之redis监控命令