一、项目介绍
ATM + 购物商城程序其实是通过模拟银行ATM机功能以及电商平台<购物商城程序>功能的实现,将前面大部分所学的知识点串联起来,
更好的去巩固python基础。这也是在学习python这门语言的第一个程序。
项目需求如下:
- 额度 15000或自定义
- 实现购物商城,买东西加入 购物车,调用信用卡接口结账
- 可以提现,手续费5%
- 支持多账户登录
- 支持账户间转账
- 记录每月日常消费流水
- 提供还款接口
- ATM记录操作日志
- 提供管理接口,包括添加账户、用户额度,冻结账户等...
- 用户认证功能
二、一个项目是如何从无到有的?
在互联网企业里,所有项目一开始都是没有的,那项目是怎么来的呢?其实是通过客户的需求,从而诞生出一些列的软件。那也就是
说我们无论开发什么项目,都是为了服务于人类,其实这也是计算机的使命,我们需要让机器替代人力去干活!所以企业中一个项目到底
是如何从无到有的:
1、需求分析
开发项目前,都必须找到相应的客户,让客户给企业提出项目的需求,以及需要实现的功能有哪些,拿到需求后再提取出一些列功能。
2、设计程序以及程序的架构
优点:
1>、逻辑清晰
2>、程序已维护
3>、提高开发效率
分层设计:
1>、用户功能层:给用户操作的功能、给用户展示信息
2>、接口层:业务逻辑的处理
3>、数据层:对数据的增删改查
项目目录介绍:
--bin:程序入口start.py,也可以放在根目录下面,这样环境变量可以少一层
--conf:配置文件settings.py,放一些配置信息
--core:核心代码src.py,处理一些业务逻辑
--db:数据存放路径db_handler.py ---在数据层
interface:接口层
log:存放日志
lib:common.py公共逻辑,环境变量等
3、分任务开发
在公司里面,开发项目需要这几种岗位人才:
UI设计: 软件的外观设计者,通过一些炫酷的设计,提高用户的对软件的体验感。
前端开发: UI设计仅仅只是把一些外观图设计出来,那前端开发需要把UI的设计图拿到之后,对软件界面的进行排版。
后端开发(python): 项目里业务以及功能的逻辑处理!
4、测试
测试工程师: 对后端以及前端开发好的项目进行功能和性能测试,测试的过程中出现bug就会立即让开发人员去修整,待整个
项目几乎没有bug,以及性能达到项目实际的预期,就会准备上线运行
黑盒:
对程序界面的功能进行测试。
白盒:
对程序的性能进行测试。
5、上线运行
运维工程师(linux): 拿到前面整个项目编写完的代码,部署到服务器,上线运行!
三、需求分析
需求分析就是把客户的一些项目需求,提取出一个个相应的功能。
#提取功能
--额度15000或自定义 --->1、注册
--实现购物商城,买东西加入购物车,调用信用卡接口结账 ----->2、购物车功能 3、支付功能
--可以提现,手续费%5% ----->4、提现
--支持多账户登录 ----->5、登录
--记录每月日常消费流水 ----->6、消费流水
--提供还款接口 ----->7、还款
--提供转账接口 ----->8、转账
--ATM记录操作日志 ----->9、日志
--提供管理接口,包括添加账户,用户额度,冻结账户等 ----->管理员功能
--用户认证功能 ----->登录认证装饰器
# 提取完后能后我们应该再分析出展示给用户的功能:
用户功能
-1、注册
-2、登录
-3、查看余额
-4、提现
-5、转账
-6、还款
-7、查看流水
-8、购物车
-9、查看购物车
-10、注销
四、设计程序以及程序架构
在写程序之前不是一拿到项目需求想到哪就写到哪。假设你的程序有成千上万个功能,你都把他们写在一个文件里面。还有用户输入、
逻辑处理、数据存储都写在一个函数里,会导致你的代码结构及其不清晰。而是应该对程序进行设计,设计出一种最优的程序架构!
那么接下来我们开始设计我们的程序架构,把一个程序架构分为三层:
记录日志,记录流水
用户功能层 接口层 数据处理层
接收数据,展示数据 业务逻辑处理 数据的处理
登录-----(接收)-------->登录接口----(验证)----> 查询数据
commone 公共方法
这个架构为何要这样设计呢?有何好处:
1 把每个功能都分成三个部分,逻辑清楚
2 如果要改用户的数据展示,或者换存储机制,只需动单独一个模块就可以了,扩展性强
3 为什么把日志模块放在接口层,因为所有的交互都会经过接口层,方便记录
五、程序目录设计
程序目录的设计应该对应着上面的架构图创建相应文件夹, 文件名可以随便取,但是最好要让别人看到就知道是干嘛用的。
--ATM
--conf 配置文件文件夹
--settings.py 放atm环境变量
--lib 公共方法文件夹
--common.py 登录认证装饰器
--db 数据处理层文件夹
--db.handler.py 查询功能,保存功能
--core 用户功能文件夹
--src.py 视图文件,给用户看到有什么可选功能(十个功能)
--interface 接口文件夹
--user_interface.py 用户接口文件(注册、登录、查看余额)
--bank_interface.py 银行接口文件(转账、还款、查看流水、支付)
--shop_interface.py 购物接口文件(添加购物车、第三方支付(会调用银行接口)、查看购物车)
--log 日志存放文件夹
--start.py 程序入口文件,启动文件(也可以放在bin目录,环境变量就需要修改)
--readme 程序说明文件
六、项目详解
1、程序入口
1>、入口程序从start.py文件开始,通过这里到达用户功能层
import os
import sys
from core import src #程序入口,需要导入用户功能层中的src.py文件
#项目目录添加到环境变量
sys.path.append(os.path.dirname(__file__))
if __name__ == '__main__':
#执行src中的run方法启动程序
src.run()
2、注册
1>、从start.py进入到在core的src.py(用户功能层)中,我们再run方法中列出我们所有的功能,编号与func_dic字典中键对应,
值是函数地址,用户输入以后调用register方法。
fun_dic = {
'1': register,
'2': login,
'3': check_balance,
'4': withdraw,
'5': transfer,
'6': repay,
'7': look_water_flow,
'8': shopping_car,
'9': check_shopping_car,
'10': logout
}
def run():
while True:
print('''
===========欢迎进入程序=======
-1、注册
-2、登录
-3、查看余额
-4、提现
-5、转账
-6、还款
-7、查看流水
-8、购物车
-9、查看购物车
-10、注销
'q':quit
=============end===============
''')
choose = input('请选择用户功能编号:').strip()
if choose == 'q':
break
if choose not in fun_dic:
print('请选择正确功能编号!!')
continue
fun_dic[choose]()
2>在register方法中接收用户输入的用户名,密码,重复密码,如果两次密码不一致,则从新输入,如果一致则调用interface
文件中user_interface.register_interface(user,pwd)方法(接口层),等待接口层返回,如果返回True这注册成功,
然后break,如果返回False,则展示错误信息,重新注册。
3>在user_interface.register_interface(user, pwd,banlance=15000)调用db文件夹中的db_handler.select(user)(数据处理层),
接收数据,如果有该用户的信息怎么返回False,'用户已存在'给用户功能层的register方法。如果不存在,定义user_dic字典存放数据格式,
调用db文件夹中的db_handler.save(user_dic)(数据处理层)方法,保存用户信息。并返回True,'注册成功'。
4>db文件夹中的db_handler.save(user_dic)(数据处理层),判断db路径是否存在,不存在就创建db文件,将以用户的用户名
为文件名,将用户信息转化为json数据存储。db文件夹中的db_handler.select(name)(数据处理层),settings.DB_PATH
表示db文件夹的路径,user_path表示用户的文件的路径,# 如user_path存在,就去读取json文件,读取出来的是用户字典
返回用户信息已字典的形式。
用户功能层:
from interface import user_interface
def register():
while True:
print('用户注册')
user = input('请输入用户名:').strip()
pwd = input('请输入密码:').strip()
re_pwd = input('请确认密码').strip()
#判断两次密码是否一致
if pwd == re_pwd:
# 既可以调用接口层interface中user_interface.py中的注册接口,传入用户名和密码,也可以保存状态来打印注册是否成功
flag, msg = user_interface.register_interface(user, pwd)
#接口返回的数据进行判断,如果为True
if flag:
#展示返回的信息
print(msg)
#结束功能
break
#如果返回的是False
else:
print(msg)
else:
print('两次密码输入不一致,请重新输入')
用户接口层:
from db import db_handler
#注册接口
def register_interface(user,pwd,banlance=15000):
#调用数据层db_handler中的select方法,查询要注册的用户是否存在
user_dic = db_handler.select(user)
#如果存在,则返回False 以及用户已存在
if user_dic:
return False,'用户已存在'
#设定写入数据格式--字典方便读取
user_dic = {
'user': user,#用户名
'pwd': pwd,#密码
'lock':False,#是否锁定
'balance': banlance,#余额
'flow': [],#流水记录
'shopping_car': {}#购物车商品
}
#注册接口调用数据层db_handler中的save方法进行数据保存
db_handler.save(user_dic)
#函数可以返回多个值
return True,'用户[%s]注册成功'%(user)
数据处理层:
#我们将用户名作为文件名,用户信息文件放在db文件夹
#导入配置文件conf中settings
from conf import settings
import json
import os
# 保存
def save(user_dic):
#判断db路径是否存在
if not os.path.exists(settings.DB_PATH):
os.mkdir(settings.DB_PATH)
#找到文件路径,写入文件
with open('%s/%s.json' % (settings.DB_PATH, user_dic['user']), 'w', encoding='utf-8') as f:
json.dump(user_dic, f)
# 从内存刷新到硬盘
f.flush()
# 查询
def select(user):
# settings.DB_PATH表示db文件夹的路径,user_path表示用户的文件的路径
user_path = '%s/%s.json' % (settings.DB_PATH, user)
# 如存在,就去读取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
else:
return False
配置文件层:
import os
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
DB_PATH = os.path.join(BASE_DIR, 'db')
通过上面的注册功能我们把数据层的功能已经完成,后续所有功能都不需要动,而且程序基本架构已经完成,后续执行在这个基础上增加功能代码就可以了。
3、登录
1>、在src.py(用户功能层)的run方法中添加“ 登入” 在func_dic添加login,然后实现login,就可以了,我们把上面的
run方法写好以后,要添加新的功能是不是特别简单。
2>、我们在src.py(用户功能层)中实现login方法,在login函数中接收用户输入的用户名和密码后,后调用interface中的
user_interface.login_interface(user,pwd)(接口层),等待接口层返回,如果返回True这将用户名存到user_info={'name':None}
中,作为保存登入状态,如果返回False则登入失败,显示失败原因。
3>、在interface文件夹中的user.login_interface(name,password)(接口层)方法中,调用db文件夹中的db_handler.select(name)(数据处理层)
方法,如果数据不存在则返回False,"用户不存在",如果有数据则判断取出的数据是否和用户输入的密码是否一致且用户没有被锁定。如果一致
且没有没有被锁定,则返回True,"登入成功",反之返回False,"密码错误或用户被锁定"
用户功能层:
# 登录功能标记,若登陆成功则添加name
user_info = {
'name': None
}
def login():
while True:
user = input('input you name:').strip()
pwd = input('input you pwd:').strip()
flag, msg = user_interface.login_interface(user, pwd)
if flag:
print(msg)
# 登陆成功的标记
user_info['name'] = user
break
else:
print(msg)
用户接口层:
#登录接口
def login_interface(user,pwd):
#判断用户是否存在
user_dic = db_handler.select(user)
if not user_dic:
return False,'用户不存在'
if pwd == user_dic['pwd'] :
return True,'用户[%s]登陆成功'%user
else:
return False,'密码错误'
4、登陆认证装饰器
1>、后续的功能我们要保证用户只有在登入的状态下才能使用,如果用户没有登入就让用户登入。那我们如何保证只有登入才能
使用,不然跳转到登入,我们用装饰器实现,要导入用户功能层中user_info['name']是否存在
公共方法:common.py
#存放公共功能
#登录认证装饰器
def login_auth(func):
from core import src
def inner(*args,**kwargs):
if src.user_info['name']:
print('已登录')
res = func(*args,**kwargs)
return res
else:
src.login()
return inner
5、查看余额
1>.在src.py(用户功能层)的run方法中添加“ 查看流水” 在func_dic添加check_balance,然后实现check_balance就可以了。
2>.在check_balance方法上加登入认证装饰器,保证用户是登入状态。
3>.在src.py(用户功能层)check_balance方法中调用interface文件夹下的user_interface.check_bal_interface(user_info['name'])方法,
4>.在interface文件夹下的interface文件中check_bal_interface方法调用db文件夹下的db_handle文件中的selec方法,获取用户的信息,并返回用户的余额。
用户功能层:
@common.login_auth
def check_balance():
bal = user_interface.check_bal_interface(user_info['name'])
print('尊敬的[%s]用户,您的余额为[%s]' % (user_info['name'], bal))
用户接口层:
#查看余额
def check_bal_interface(user):
user_dic = db_handler.select(user)
return user_dic['balance']
6、提现
1>、在src.py(用户功能层)的run方法中选择withdraw方法
2>、在withdraw方法上加登入认证装饰器,保证用户是登入状态。
3>、输入提现金额,判断是否是数字要转为整形,然后调用bank_interface.withdraw_interface(user_info['name'], money)
得到flag和msg两个值,判断flag是否为真
4>、interface文件夹bank_interface银行接口中withdraw_interface的方法,调用db_handler.select(user)得到返回字典,然后对
传入的money进行手续费后处理,判断字典中的金额是否大于手续后的金额,满足则更新,然后添加到重点的流水中,
然后调用db_handler.save(user_dic)进行保存
用户功能层:
# 提现----跟银行打交道(上面的注册,登录,查看余额是跟用户接口打交道)---要新建bank_interface
@common.login_auth
def withdraw():
while True:
money = input('请输入提现金额:').strip()
if money.isdigit():
money = int(money)
flag, msg = bank_interface.withdraw_interface(user_info['name'], money)
if flag:
print(msg)
break
else:
print(msg)
银行接口层:
# 提现接口---要去数据处理层来取金额,然后写流水到flow中,然后再保存到数据层
def withdraw_interface(user, money):
user_dic = db_handler.select(user)
money2 = money * 1.05
if user_dic['balance'] >= money2:
user_dic['balance'] -= money2
user_dic['flow'].append('用户[%s]提现[%s]元成功,余额为[%s]元' % (user, money, user_dic['balance']))
db_handler.save(user_dic)
return True, '用户[%s]提现[%s]元成功,余额为[%s]元' % (user, money, user_dic['balance'])
return False, '余额不足'
7、转账
用户功能层:
# 转账---跟银行打交道bank_interface
@common.login_auth
def transfer():
while True:
to_user = input('请输入目标用户:').strip()
money = input('请输入转账金额:').strip()
if money.isdigit():
money = int(money)
# 调用转账接口
flag, msg = bank_interface.transfer_interface(user_info['name'], to_user, money)
if flag:
print(msg)
break
else:
print(msg)
else:
print('请输入数字')
银行接口层:
# 转账接口
def transfer_interface(from_usre, to_user, money):
# 查看目标用户是否存在
to_user_dic = db_handler.select(to_user)
if not to_user_dic:
return False, '目标用户不存在'
# 查看转账用户--目标账户加钱,本身用户减钱
from_user_dic = db_handler.select(from_usre)
if from_user_dic['balance'] >= money:
to_user_dic['balance'] += money
from_user_dic['balance'] -= money
# 修改以后要先记录流水
to_user_dic['flow'].append('%s接收到%s的转账%s元' % (to_user, from_usre, money))
from_user_dic['flow'].append('%s给%s的转账%s元' % (from_usre, to_user, money))
# 然后要保存
db_handler.save(to_user_dic)
db_handler.save(from_user_dic)
return True, '%s给%s转账%s元成功' % (from_usre, to_user, money)
return False, '余额不足'
8、还款
用户功能层
# 还款---跟银行打交道bank_interface
@common.login_auth
def repay():
while True:
money = input('请输入还款金额').strip()
if money.isdigit():
money = int(money)
flag, msg = bank_interface.repay_interface(user_info['name'], money)
if flag:
print(msg)
break
else:
print(msg)
银行接口层:
# 还款接口
def repay_interface(user, money):
user_dic = db_handler.select(user)
user_dic['money'] += money
# 可以判断还款的金额是否还到15000.该功能没写完
# limit = 15000 - user_dic['balance']
# if money >= limit:
user_dic['flow'].append('%s还款%s元成功' % (user, money))
db_handler.save(user_dic)
return True, '%s还款成功' % user
9、查看流水
用户功能层
# 查看流水---跟银行打交道bank_interface
@common.login_auth
def look_water_flow():
flow_list = bank_interface.look_flow_interface(user_info['name'])
for line in flow_list:
print(line)
银行接口层:
# 查看流水接口
def look_flow_interface(user):
user_dic = db_handler.select(user)
return user_dic['flow']
10、购物车
用户功能层
# 购物车功能 ---购物接口---shop_interface
@common.login_auth
def shopping_car():
# 定义购物车字典
shoping_car_s = {}
# 设定商品总价
cost = 0
while True:
# 定义商品列表
goods_list = [
['凤爪', 50],
['鸡腿', 5],
['macbook pro', 10000],
['ipad', 3000]
]
# 查看用户金额,用于判断是否有钱购买
user_balance = user_interface.check_bal_interface(user_info['name'])
# 利用enumerate枚举 得到下标和元素
for index, goods in enumerate(goods_list):
print(index, goods) # 打印有啥商品
choice = input('请选择商品编号或者输入"q"结束购买:').strip()
if choice.isdigit():
choice = int(choice)
# 判断用户选的编号是否在商品类表内
if choice >= 0 and choice < len(goods_list):
# 根据下标选择,然后进行解压赋值得到商品名和价格
good, price = goods_list[choice]
if user_balance >= price:
if good in shoping_car_s:
shoping_car_s[good] += 1
else:
shopings_car = 1
cost += price
print('添加购物车成功,此时应消费%s元!' % cost)
else:
print('余额不足,请去充值')
else:
print('请选择正确的商品编号')
elif choice == 'q':
if cost == 0:
print('你什么都没购买')
break
# 调用添加购物车接口
shop_interface.save_shopping_car_interface(user_info['name'], shoping_car_s)
sure = input('确定购买? 输入y or n:').strip()
if sure == 'y':
# 调用支付接口----银行接口(付款借款)
flag, msg = shop_interface.pay_shop_interface(user_info['name'], cost)
if flag:
print(msg)
break
else:
print('添加购物车成功')
break
接口层:
购物接口层:
# 添加购物车接口
def save_shopping_car_interface(user, shoping_car):
# 获取用户的字典
user_dic = db_handler.select(user)
# 添加购物车
user_dic['shopping_car'] = shoping_car
# 或者另一种添加购物车方法
# user_dic['shopping_car'].setdefault(shoping_car)
# 保存购物车
db_handler.save(user_dic)
# 付款接口---在这里要调用银行接口
def pay_shop_interface(user, cost):
# 调用银行支付功能进行扣款
list1 = bank_interface.pay_money_interface(user, cost)
# 获取用户的字典信息
user_dic = db_handler.select(user)
# 清空购物车
user_dic['shopping_car'] = {}
return list1
银行接口层:
# 支付接口
def pay_money_interface(user, money):
# 获取用户的字典信息
user_dic = db_handler.select(user)
# 扣款
user_dic['balance'] -= money
# 更新流水
user_dic['flow'].append('%s 消费了%s元' % (user, money))
# 保存数据
db_handler.save(user_dic)
return True, '支付成功'
11、查看购物车
用户功能层
# 查看购物车功能---购物接口---shop_interface
@common.login_auth
def check_shopping_car():
shopping_car_s = shop_interface.look_shopping_car_interface(user_info['name'])
print(shopping_car_s)
购物接口层:
# 查看购物车接口
def look_shopping_car_interface(user):
user_dic = db_handler.select(user)
return user_dic['shopping_car']
12、注销
用户功能层
@common.login_auth
def logout():
user_info['name'] = None
print('注销成功')