小项目之ATM_shopping
项目从无到有会经历以下几个步骤:
- 需求分析
- 设计程序以及程序的架构
- 分任务开发程序
- 测试
- 上线运行
开发 ATM+购物车系统 , ATM的实现类似于银行自助提款机核心业务, 购物车的实现类似于淘宝商城购物系统.
# 需求分析
[需求]
- 额度15000或自定义 ---> 注册(信用卡)
- 支持多账户登录 ---> 登录
- 实现购物商城, 买东西加入购物车, 调用信用卡接口结账. ---> 购物
- 可以提现, 手续费5% ---> 提现
- 支持账户间转账 ---> 转账
- 记录日常消费流水 ---> 记录流水 消费的每一笔会有个记录
- 提供还款接口 ---> 还款接口
- ATM记录操作日志 ---> 日志
- 提供管理接口,包括添加用户、冻结账户等.. ---> 添加,冻结账户
- 用户认证用装饰器 ---> 装饰器
[分析] 9个功能
注册、登录、查看余额、转账、取款、还款、查看流水、购物、查看购买商品
# 设计程序架构!!!
core用户功能层 interface接口层 db数据处理层 log日志层 conf配置文件 lib公共方法文件
巴拉巴拉.一顿操作写在一起..扩展性很差!( ̄▽ ̄) 会被骂小菜鸡的.
用户功能层实现9大功能. 一般写的都是用户操作的代码...
接口层里面将用户功能层9大功能的接口设计分为了三个模块.(可以不分,分了逻辑更清晰.)
数据处理层设计了两个方法, 一个查,一个存.
公共方法层每一层都可以调用. eg: 时间格式转成字符串 每一层都可能会用的.
日志层 因为接口层主要是写逻辑的,所以在接口层加日志
数据流向
以注册功能为例,用户输入了用户名和密码,数据流向了接口层user模块,接口层会连通数据处理层,先通过select判断用户是否存在,若不存在,再连通数据处理层,将准备好的用户信息调用save方法保存到文件中. 不管怎样,都会反馈信息给用户!!
注意哦!用户功能层不能直接调用数据处理层, 在架构设计的时候做了一个隔离
以登陆功能为例,用户输入了用户名和密码,调用登录的接口,登录的接口再调用select查询方法将查询结果返回给接口层,接口层进行一些判断,若成功,将返回True给用户功能层.
将一个功能拆成了三个部分 以分层的形式来实现
牛B之处
- 强行分为几层,每一层代码量减少.逻辑变清晰了.
- 程序解藕, 可扩展性提高了 . 高内聚低耦合
- 要实现网页版移动版,只需要改动用户功能层就行!其余架构层不用动!
eg: 用户接口层 --- cmd版|网页版|移动版. - 要用数据库代替文件,只需要改动数据处理层即可
- 要实现网页版移动版,只需要改动用户功能层就行!其余架构层不用动!
# 分任务开发程序
登录 - 注册 - 装饰器(某些功能需要登录后才能使用) - 查看余额 - 转账 - 还款 - 取款 -
查看流水 - 购物 - 查看购买商品 - 写一个获取日志的公共方法
断点调试!!
跟着视频敲了一遍,自己再独自敲一遍. 为了偷懒 ,不想新建工程目录.就利用os模块和shutil模块写了十几行代码完成了文件夹的迭代复制,以及.py文件的清空处理...(详见,开胃小菜.md里的递归,目录操作)
第二遍一开始的时候,弄巧成拙写了个文件模块的相互调用...强行复习了一遍模块的导入与包..Hhhh...
在第二遍敲的过程中,**为了偷懒,**想自动添加函数到函数字典中,就写了一个没有wrapper的装饰器,与装饰器 auth_login登录验证一起作用时,出现了一点bug.. 在不断的摸索过程中知道了多个装饰器的工作原理.. (过程并不是那么的愉快... 很头疼 但突然抓住某一个点就解释通了)
在第二遍敲的过程中,接口层的逻辑没有按部就班的按照视频代码的思路.. 感觉自己的逻辑判断思路更清晰明了.. Hhh些许自恋(´▽`) 当然实现的效果是一样的. 先判断啥后判断啥个人习惯 因人而异吧.
确实这个架构挺牛批的,可以更好的理清思路... 明天开始面向对象之旅吧, Good night! world.
# 理清逻辑
学完面向对象我又回来啦.. 第三遍, 再敲一遍该项目.. 先是忙敲,结果事倍功半...
深刻体会到了一点: 在写之前要理清逻辑再搞!!! orz (流下悔恨的眼泪)
"""
准备阶段!
"""
■ start.py 配置sys.path、src.run()
■ setting.py 基本的初始配置(logging配置)
■ src.py 用户操作
先建好所有的功能函数 -- 主要就是命名
完成run函数. -- 用户通过数字选择功能
实现:有参装饰器构建功能字典`@func_to_dic('1')`;
`func_dic[choice]()`进行调用
# func_dic、func_to_dic 写在 common.py公共文件里
Ps: -- 体会下while True循环中continue替代if-else中else的逻辑.
login_user = {'name':''}
"""
register注册功能!
用户层 -- 逻辑层 -- 数据库层 (用户层与数据库层作了个“隔断”!)
"""
1.已经登录,不能注册
2.输入账号名和密码,两次密码不一致重新输入
3.调用register_interface(name,pwd)接口 # 注册需要写入文件,so要传递用户名和密码
register_interface的逻辑:
1> 查询该用户是否已经被注册? # 已注册用户是以json文件的形式在db目录中存在
`db_handler.select(name)`
这里简单粗暴的查看name对应的json文件是否存在,存在返回json文件反序列化后的user_dic.
`db_handler.save(user_dic)`
若不存在,则可以开始注册,即构建字典将用户信息(name、pwd、lock等)以json文件的格式进行保存.
2> 注意,返还给 用户层/前端 的数据格式是 (flag,msg)
体会下flag在while循环中的妙用!!
"""
login登录功能!
"""
1.已经登录,不能登录
2.输入账号名和密码,调用login_interface(name,pwd)接口
login_interface的逻辑:
查找该用户是否存在,若存在比对密码是否一致.若密码一致且该用户未被锁定,登录成功.
3.登录成功,写入全局的login_user字典中!
"""
log_out退出登录功能!
"""
Ps:写的代码有bug,若一开始没任何人注册,选择log_out功能时,会让你先登录..可是没人注册,登录不了啊.哈哈哈哈.
1.已经登录,才能退出登录. @auth_login 装饰器实现
@auth_login 装饰器的逻辑:
导入src.py模块,在真正执行退出登录功能之前先判断login_user['name']是否为空.
若为空,需先调用src.login()登录后,再跳转到退出登录功能界面!
Ps:回顾两个知识点,
多装饰器执行顺序 -- !! 其实就是 {'10':id(wrapper)} {'10':id(log_out)}的区别罢了.
循环导入问题 -- !! 同一个模块只会在第一次导入时执行其内部代码,再次导入该模块时,即便是该模块尚未完全加载完毕也不会去重复执行内部代码..
2.确认退出登录后,调用login_out_interface(login_user)接口.
注意,python是引用传递,login_user是可变类型,传递的是该字典的内存地址.
login_out_interface的逻辑:
直接将name键对应的值赋值为空字符串即可.
其余功能..就是思维逻辑流程图的问题啦.Hhh难得搞了.(⁎⁍̴̛ᴗ⁍̴̛⁎)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
第三遍加深了这些的理解 循环引用、多装饰器、json序列化与反序列化、python引用语义、流程判断continue、break的妙用、接口逻辑层返回前端/用户层 (flag,msg)的形式... 这5、6个小时血赚!真的,我哭死...(/ω\)