目录

项目实现思路

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. 日志函数

ATM购物车项目总结的更多相关文章

  1. 阶段性项目 ATM+购物车项目

    ATM + 购物车https://www.cnblogs.com/kermitjam/articles/10687180.html readme 内容前戏: 一个项目是如何从无到有的. 一 需求分析 ...

  2. ATM + 购物车项目

    ''' 存放配置文件 ''' import os #获取项目根目录 BASE_PATH=os.path.dirname(os.path.dirname(__file__)) #获取用户目录 USER_ ...

  3. ATM+购物车项目流程

    目录 需求分析 架构设计 功能实现 搭建文件目录 conf配置文件夹 lib公共功能文件夹 db数据文件夹 interface业务逻辑层文件夹 core表现层文件夹 测试 最外层功能(src.py) ...

  4. ATM购物车+三层结构项目设计

    ATM购物车项目 模拟实现一个ATM + 购物商城程序. 该程序实现普通用户的登录注册.提现充值还款等功能,并且支持到网上商城购物的功能. 账户余额足够支付商品价格时,扣款支付:余额不足时,无法支付, ...

  5. ATM购物车程序项目规范(更新到高级版)

    ATM购物车程序(高级版) 之前的低级版本已经删除,现在的内容太多,没时间把内容上传,有时间我会把项目源码奉上! 我已经把整个项目源码传到群文件里了,需要的可以加主页qq群号.同时群内也有免费的学习资 ...

  6. python以ATM+购物车剖析一个项目的由来及流程

    ATM+购物车 一个项目是如何从无到有的 ''' 项目的由来,几个阶段 0.采集项目需求 1.需求分析 2.程序的架构设计 3.分任务开发 4.测试 5.上线运行 ''' 需求分析: # 对项目需求进 ...

  7. 项目: ATM+购物车

    ATM+购物车 项目文件: 介绍 以下为文件夹层次和内容: readme.md 1. 需求 模拟银行取款 + 购物全过程 1.注册 2.登录 3.提现 4.还款 5.转账 6.查看余额 7.查看购物车 ...

  8. day19 十九、ATM+购物车

    项目:ATM+购物车 作业需求:模拟实现一个ATM + 购物商城程序1.额度 15000或自定义 2.实现购物商城,买东西加入 购物车,调用信用卡接口结账 3.可以提现,手续费5% 4.每月22号出账 ...

  9. Python 入门基础16 -- ATM + 购物车

    ATM + 购物车 1.需求分析 2.设计程序以及程序的架构 设计程序的好处: - 扩展性强 - 逻辑清晰 3.分任务开发 4.测试 黑盒: 白盒: 对程序性能的测试 5.上线运行 # Tank -- ...

  10. Python实战之ATM+购物车

    ATM + 购物车 需求分析 ''' - 额度 15000或自定义 - 实现购物商城,买东西加入 购物车,调用信用卡接口结账 - 可以提现,手续费5% - 支持多账户登录 - 支持账户间转账 - 记录 ...

随机推荐

  1. Ingress

    一.需求背景 固定对外提供服务采用了NodePort方式映射并固定了30001端口,但是,该端口默认范围是30000~32767,并且我们的web服务一般都是80.443端口对外,因此我们产生了如下几 ...

  2. Logstash:如何处理 Logstash pipeline 错误信息

    转载自:https://elasticstack.blog.csdn.net/article/details/114290663 在我们使用 Logstash 的时候经常会出现一些错误.比如当我们使用 ...

  3. Elasticsearch准实时索引实现(数据写入到es分片并存储到文件中的过程)

    溢写到文件系统缓存 当数据写入到ES分片时,会首先写入到内存中,然后通过内存的buffer生成一个segment,并刷到文件系统缓存中,数据可以被检索(注意不是直接刷到磁盘) ES中默认1秒,refr ...

  4. loam详细代码解析与公式推导

    loam详细代码解析与公式推导(基础理论知识) 一.基础坐标变换 loam中欧拉角解算都采用R P Y 的解算方式,即先左乘R, 再左乘P, 最后左乘Y,用矩阵表示为: R = Ry * Rp * R ...

  5. 天天向上力量B

    N=eval(input()) up=pow(1+0.001*N,365) down=pow(1-0.001*N,365) print("{:.2f}, {:.2f}, {:.0f}&quo ...

  6. acwing349 黑暗城堡 (最短路径生成树)

    求出最短树,用乘法原理统计答案就行了(模拟prim过程). 不知道说什么了,直接上代码: 1 #include<cstring> 2 #include<iostream> 3 ...

  7. OnionArch - 采用DDD+CQRS+.Net 7.0实现的洋葱架构

    博主最近失业在家,找工作之余,看了一些关于洋葱(整洁)架构的资料和项目,有感而发,自己动手写了个洋葱架构解决方案,起名叫OnionArch.基于最新的.Net 7.0 RC1, 数据库采用Postgr ...

  8. Dubbo2.7详解

    Spring与Dubbo整合原理与源码分析 [1]注解@EnableDubbo @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTI ...

  9. 齐博x2向上滚动特效

    要实现图中圈起来的向上滚动特效,大家可以参考下面的代码 <!--滚动开始--> <style type="text/css"> .auto-roll{ he ...

  10. golang中的init初始化函数

    0.1.索引 https://waterflow.link/articles/1666090530880 1.概念 1.1.源文件里的代码执行顺序 init 函数是用于初始化应用程序状态的函数. 它不 ...