Python学习笔记-练习编写ATM+购物车(购物商城)
作业需求: 模拟实现一个ATM + 购物商城程序:
1.额度 15000或自定义
2.实现购物商城,买东西加入 购物车,调用信用卡接口结账
3.可以提现,手续费5%
4.支持多账户登录
5.支持账户间转账
6.记录每月日常消费流水
7.提供还款接口
8.ATM记录操作日志
9.提供管理接口,包括添加账户、用户额度,冻结账户等。。。
10.用户认证用装饰器 一、软件定位,软件的基本功能。
实现一个简单的atm与购物车程序,
二、运行代码的方法: 安装环境、启动命令等。
用Python3.5写的,语法就是至此之前所学的,直接打开运行即可
三、目录总体结构设计。 ├── ATM #ATM主程目录
│ ├── __init__.py
│ ├── bin #ATM 执行文件 目录
│ │ ├── __init__.py
│ │ ├── atm.py #ATM 执行程序
│ │ ├── manage.py #信用卡管理
│ ├── conf #配置文件
│ │ ├── __init__.py
│ │ └── Settings.py #配置参数
│ ├── core #主要程序逻辑都 在这个目录 里
│ │ ├── __init__.py
│ │ ├── accounts.py #用于从文件里加载和存储账户数据
│ │ ├── auth.py #用户认证模块及主要功能函数
│ │ ├── db_handler.py #数据库连接引擎
│ │ ├── logger.py #日志记录模块
│ │ ├── main.py #主逻辑交互程序
│ │ ├── transaction.py #记账\还钱\取钱\与账户金额相关的操作,冻结或者锁定用户
│ ├── db #用户数据存储的地方
│ │ ├── __init__.py
│ │ ├── account_sample.py #生成一个初始的账户数据 ,把这个数据 存成一个 以这个账户id为文件名的文件,放在accounts目录 就行了,程序自己去会这里找
│ │ └── accounts #存各个用户的账户数据 ,一个用户一个文件
│ │ └── 123.json #新创建的用户账户示例文件
│ │ └── 1234.json #一个用户账户示例文件
│ │ └── 123456.json #一个用户账户示例文件
│ │ └── 6230001.json #管理用户账户示例文件
│ └── log #日志目录
│ ├── access.log #用户访问和操作的相关日志
│ └── login_in.log #登陆日志
└── shopping_mall #电子商城程序,需单独实现,主要实现购物的功能。
│ └── __init__.py
│ └── product.txt #存放商品的txt文件
│ └── shopping_list.txt #存放购物清单的txt.文件
│ └── shopping_mall.py #购物商城程序
├── README
四、简要说明,更详细点可以说明软件的基本原理。
1.程序从/bin/atm.py开始执行if __name__ == '__main__':
main.run()
2.程序转到/core/main.py下的run()函数,登陆时调用/core/auth的acc_login()进行登陆验证:用到了/core/auth下的acc_auth2()方法进行验证(此时传入的参数时用户输入的账户和密码)
acc_auth2中有调用了/core/db_handler下的db_handler()方法(参数是输入的账户名)在db_handler中只是进行判断是什么引擎,return file_db_handle(数据库引擎)解析文件,返回文件执行加载输入的用户的账户的所有数据
接下来判断是否为管理者账户,或者是否被冻结,若都不是,则判断输入的密码是否与数据库中的密码一样,在判断到期时间是否过期
所有都通过的话就返回这个账户的数据,之前已经创建了一个空字典,里面有是否验证:用户数据:用户账户:,判断是否被验证过,然后把用户数据临时的传递到里面,执行主循环函数
可以选择进入到购物商城,或者信用卡操作或者退出
1)购物商城
调用/shopping_mall/shopping_mall.py文件执行,主循环函数,选择你是商家还是用户,
①如果选择商家,商家有增加商品修改商品的功能
②如果选择用户,用户则有购物,刷信用卡消费的功能,当退出时打印消费清单
2)信用卡操作
调用/core/main.py下interactive(用户的所有数据)调用主循环函数,可以打印账户信息、还款、取款、转账、账单、退出等操作
①账户信息
②还款
③取款
④转账
⑤账单
⑥退出
3)若在账户登陆的时候进行输入的时管理员账户调用/bin/manage.py则可以对用户进行管理,解冻用户、冻结用户、申领新卡
①添加账户
②冻结账户
③解冻账户
④退出
五、常见问题说明。
日志没有实现,账单没有
六、以下为功能架构及部分实现功能代码

__author__ = 'ZFH'
# 2018.09.26
#-*- Coding:utf-8 -*- import os,sys BASE_DIR_Atm = os.path.dirname(os.path.dirname(__file__))
BASE_DIR_PRO = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) sys.path.append(BASE_DIR_Atm)
sys.path.append(BASE_DIR_PRO) print(sys.path) from core import main if __name__ == '__main__':
main.run()
online/Atm/bin/arm.py
__author__ = 'ZFH'
# 2018.09.26
# -*- coding:utf-8 -*-
import os,logging
BASE_DIR_Atm = os.path.dirname(os.path.dirname(__file__)) DATABASE = {
'engine':'file_storage',
'name':'account',
'path':"%s/dbs" % BASE_DIR_Atm
} LOG_LEVEL = logging.INFO
LOG_TYPES = {
'transaction':'transaction.log',
'access':'access.log'
} #发生交易的配置类型
TRANSACTION_TYPE = {
'repay':{'action':'plus','interest':0}, #还款
'withdrawal':{'action':'minus','interest':0.05}, #取现扣费,即降低可用余额
'transfer':{'action':'minus','interest':0.05}, #转账扣费,即降低可用余额
'consumption':{'action':'minus','interest':0} #消费
}
online/Atm/conf/settings.py
__author__ = 'ZFH'
# 2018.09.26
# -*- coding:utf-8 -*-
#用于从文件里加载和存储账户数据
import json
from core import db_handler
from conf import settings def load_current_balance(account_id):
'''
返回账号余额其他信息
:param account_id:用户账户的名字
:return:返回最新读到的数据文件中的最新数据
'''
db_path = db_handler.db_handler()
account_file = "%s/%s.json" %(db_path,account_id)
with open(account_file,'r') as f:
acc_data = json.load(f)
return acc_data #写入文件
def dump_account(account_data):
db_path = db_handler.db_handler()
account_file = "%s/%s.json" %(db_path,account_data['id'])
with open(account_file,'w') as f:
acc_data = json.dump(account_data,f)
return True
online/Atm/core/accounts.py
__author__ = 'ZFH'
# 2018.09.26
# -*- coding:utf-8 -*-
# 用户认证模块
import os,json,time
from core import db_handler #装饰器(用于验证账户是否登陆过)
def login_required(func):
def wrapper(*args,**kwargs):
if args[0].get('is_authenticated'):
return func(*args,**kwargs)
else:
exit("用户认证失败")
return wrapper def acc_auth(account,password):
'''
账号验证函数
:param account:
:param password:
:return:
'''
db_path = db_handler.db_handler()
account_file = '%s/%s.json' % (db_path,account)
# print('account_file:',account_file)
# print('account_file:',type(account_file))
if os.path.isfile(account_file):
with open(account_file,'r') as f:
account_data = json.load(f)
if account_data['password'] == password:
exp_time_stamp = time.mktime(time.strptime(account_data['expire_date'],"%Y-%m-%d"))
if time.time() > exp_time_stamp:
print("\033[31;1m[%s]账户已经注销,请重新申领账户!\033[0m" % account)
else:
return account_data
else:
print("\033[31;1m账号或密码错误,请重新输入!\033[0m")
else:
print("\033[31;1m[%s]账户不存在!---\033[0m" % account) def acc_login(user_data,log_obj):
'''
账户登陆函数
:param user_data:用户信息数据,只存在内存中
:param log_obj:
:return: 账户密码都对的情况下,返回所有账户数据
'''
retry_count = 0; #初始化重试次数为0
while user_data['is_authenticated'] is not True and retry_count < 3: #如果没有验证过,或循环此时没超过三次就执行下面的
account = input("\033[32;1m账户:\033[0m").strip()#输入账户
password = input("\033[32;1m密码:\033[0m").strip()#输入密码
auth = acc_auth(account,password) #解耦,将输入的账户和密码传入到acc_auth2函数中,进行验证最后返回的是读取到的输入正确账户的所有数据)赋值给auth
# print('auth:',auth)
if auth:
user_data['is_authenticated'] = True #登陆成功,将只存在与内存中的数据中的是否验证改为True
user_data['account_id'] = account #将只存在与内存中的数据中的账户id改为账户名字(开始输入的帐户名)
return auth #这一步操作就是验证此账户是否登陆,然后返回账户的所有数据(数据文件中的所有数据)
retry_count += 1
else:
log_obj.error("[%s]账户太多次尝试" % account)
exit()
online/Atm/core/auth.py
__author__ = 'ZFH'
# 2018.09.26
# -*- coding:utf-8 -*-
import os,sys,time,json from conf import settings #数据库句柄
def db_handler():
'''
连接数据库
:return:
'''
conn_params = settings.DATABASE #conf下配置的数据库地址
# print("conn_params:",conn_params)
# print('conn_params:',type(conn_params))
if conn_params['engine'] == 'file_storage': #判断Settings下的DABASE是什么引擎,这里只用文件文件引擎
return file_db_handler(conn_params) #则把Settings下的DABASE的数据传给file_db_handle并返回
elif conn_params['engine'] == 'mysql':
pass #支持数据源类型扩展 def file_db_handler(conn_params):
'''
file db操作
:return:
'''
# print('file_db:',conn_params)
db_path = '%s/%s' % (conn_params['path'],conn_params['name'])
return db_path
online/Atm/core/db_handler.py
__author__ = 'ZFH'
# 2018.09.26
# -*- coding:utf-8 -*-
#日志记录模块,处理所有日志工作 import logging
from conf import settings def logger(log_type):
#create logger
logger = logging.getLogger(log_type)
logger.setLevel(settings.LOG_LEVEL) #创建控制台处理程序并将级别设置为调试
ch = logging.StreamHandler()
ch.setLevel(settings.LOG_LEVEL) #创建文件处理程序并设置级别为警告
log_file = "%s/%s" % (settings.BASE_DIR_Atm,settings.LOG_TYPES[log_type])
fh = logging.FileHandler(log_file)
fh.setLevel(settings.LOG_LEVEL) #创建格式化程序
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levename)s- %(message)s') #添加格式化的CH和FH
ch.setFormatter(formatter)
fh.setFormatter(formatter) #添加CH和FH到loggerh
logger.addHandler(ch)
logger.addHandler(fh) return logger
'''
#应用程序代码
logger.debug('debug message')
'''
online/Atm/core/logger.py
__author__ = 'ZFH'
# 2018.09.26
# -*- coding:utf-8 -*- import os,time,sys
from core import auth
from core.auth import login_required
from core import db_handler
from core import logger
from core import transaction
from core import logger
from conf import settings
from core import accounts
import json #访问日志
access_logger = logger.logger('access')
#交易日志
trans_logger= logger.logger('transaction') #用户数据信息
user_data = {
'account_id':None, #账号ID
'is_authenticated':False, #是否认证
'account_data':None #账号数据
}
#调用log文件下的log方法,返回日志对象 def run():
# print("测试调用不同路径")
acc_data = auth.acc_login(user_data,access_logger) #程序从这里开始,执行auth下的acc_login函
# print('acc_data:',acc_data)
# (返回的是验证过的正确的账户数据)赋值给acc_data(此时这里的数据为输入账户名字的数据文件的数据) if user_data['is_authenticated']:
user_data['account_data'] = acc_data #把账户所有信息传给账户开始时的临时的账户数据空字典,
# 把所有的数据文件传给账户的账户数据里面,
# print(acc_data)
main_menu(user_data) def account_info(acc_data):
'''
账户信息
:param acc_data:账户信息
:return:
'''
account_info = acc_data
creidt = account_info['account_data']['credit']
balance = account_info['account_data']['balance']
pay_day = account_info['account_data']['pay_day']
print(' creidt: %s \n balance: %s \n pay_day: %s' % (creidt,balance,pay_day)) def login_out(acc_data):
'''
用户退出登录
:param acc_data: 退出用户
:return:
'''
exit('退出') def main_menu(acc_data):
'''
展现主菜单,并让用户选择功能
1.银行卡操作
2.购物商城
3.退出
:param acc_data: 传入赋值的临时账户信息
:return:
'''
main_menu = u'''
--------主菜单--------
\033[32;1m
1.银行卡操作
2.购物商城
e.退出
\033[0m
'''
main_menu_dic = {
'':'银行卡操作',
'':'购物商城',
'e':'直接退出',
}
exit_flag = False
while not exit_flag:
print(main_menu)
user__main_option = input('请输入您的选择的操作:').strip()
if user__main_option in main_menu_dic:
# print('acc_data:',acc_data)
if user__main_option == 'e':
login_out(acc_data)
return
else:
interactive(acc_data,user__main_option)
else:
print('\033[31;1m选择不存在!\033[0m') def interactive(acc_data,user__main_option):
'''
处理主菜单选择
:param acc_data: 缓存记录的用户信息
:param user__main_option: 主菜单用户选择
:return:
'''
if user__main_option == '':
interactive_bank(acc_data)
elif user__main_option == '':
interactive_shaopping(acc_data)
return interactive def interactive_shaopping(acc_data):
'''
展现购物菜单
:param acc_data: 缓存记录的用户信息
:return:
'''
return def interactive_bank(acc_data):
'''
展现银行卡主菜单,并让用户选择功能
1.查看账户
2.取款
3.还款
4.转账
5.账单
e.直接退出
返回上一层
:param acc_data: 传入赋值的临时账户信息
:param user_option: 传入用户上一层选择
:return:
'''
print('进入下一个菜单')
main_menu_bank = u'''
--------银行卡主菜单--------
\033[32;1m
1.查看账户
2.取款
3.还款
4.转账
5.账单
e.直接退出
\033[0m
'''
main_menu_bank_dic = {
'':'查看账户',
'':'取款',
'':'还款',
'':'转账',
'':'账单',
'e':'直接退出',
} exit_flag = False
while not exit_flag:
print(main_menu_bank)
user_option = input('请输入您的选择的操作(\033[32;1m返回上一层请输入r\033[0m):').strip()
if user_option in main_menu_bank_dic:
if user_option == 'e':
login_out(acc_data)
break
elif user_option == '':
account_info(acc_data)
user_option = input('\033[32;1m返回上一层请输入r\033[0m:').strip()
if user_option == 'r':
interactive_bank(acc_data)
else:
print('\033[31;1m选择不存在!\033[0m返回上一层请输入r,直接退出请输入e')
account_info(acc_data)
user_option = input('\033[32;1m返回上一层请输入r\033[0m:').strip()
if user_option =='r':
interactive_bank(acc_data)
elif user_option == 'e':
login_out(acc_data)
break
elif user_option == '':
withdrawal(acc_data)
elif user_option == '':
repay(acc_data)
elif user_option == 'r':
return main_menu(acc_data)
else:
print('\033[31;1m选择不存在!请按提示重新输入!!!\033[0m') @login_required #调用用户验证装饰器
def withdrawal(acc_data):
'''
取款操作,打印可取款额度
:param acc_data:
:return:
'''
account_data = accounts.load_current_balance(acc_data['account_id'])
current_balance = '''
---------银行信息----------
信用额度: %s
可用余额: %s
账单日: %s
''' % (account_data['credit'],account_data['balance'],account_data['pay_day'])
print(current_balance)
withdrawal_flag = False
while not withdrawal_flag:
withdrawal_amount = input("\033[33;1m输入取款金额:\033[0m").strip()
if len(withdrawal_amount) > 0 and withdrawal_amount.isdigit():
#将数据传入make_transaction中(交易日志,用户数据,交易类型,还款金额)进行操作,最后返回的是最新操作之后的账户数据
new_balance = transaction.make_transaction(trans_logger,account_data,'withdrawal',withdrawal_amount)
if new_balance:
print('''\033[42;1m最新的余额:%s\033[0m''' %(new_balance['balance']))
return
if withdrawal_flag == 'b':
withdrawal_flag == True
elif withdrawal_amount == 'e':
interactive_bank(acc_data)
else:
print('\033[31;1m[%s]是无效的账户!\033[0m' % withdrawal_amount)
if withdrawal_amount == 'b':
withdrawal_flag == True @login_required #调用用户验证装饰器
def repay(acc_data):
'''
还款操作,打印当前余额
:param acc_data:
:return:
'''
account_data = accounts.load_current_balance(acc_data['account_id'])
current_balance = '''
---------银行信息----------
信用额度: %s
可用余额: %s
账单日: %s
''' % (account_data['credit'],account_data['balance'],account_data['pay_day'])
print(current_balance)
repay_flag = False
while not repay_flag:
repay_amount = input("\033[33;1m输入你要还款的金额(重选操作请输入e):\033[0m").strip()
if len(repay_amount) > 0 and repay_amount.isdigit():
#将数据传入make_transaction中(交易日志,用户数据,交易类型,还款金额)进行操作,最后返回的是最新操作之后的账户数据
new_balance = transaction.make_transaction(trans_logger,account_data,'repay',repay_amount)
if new_balance:
print('''\033[42;1m最新的余额:%s\033[0m''' %(new_balance['balance']))
return
if repay_amount == 'b':
repay_flag == True
elif repay_amount == 'e':
interactive_bank(acc_data)
else:
print('\033[31;1m[%s]是无效的账户!\033[0m' % repay_amount)
if repay_amount == 'b':
repay_flag == True
online/Atm/core/main.py
__author__ = 'ZFH'
# 2018.09.26
# -*- coding:utf-8 -*-
#记账\还钱\取钱\与账户金额相关的操作,冻结或者锁定用户
# 交易处理模块
from conf import settings
from core import db_handler
from core import accounts import json,time def make_transaction(log_obj,account_data,tran_type,amount,**kwargs):
'''
处理所有用户的交易
:param log_obj: 写入日志
:param account_data:用户最新数据
:param tran_type:交易类型
:param amount:交易金额
:param kwargs:其他参数
:return:返回最新的账户数
'''
amount = float(amount) #转换为浮点数
if tran_type in settings.TRANSACTION_TYPE: #判断传入的类型是否在配置参数里面
interest = round(amount * settings.TRANSACTION_TYPE[tran_type]['interest'],2) #根据交易类型计算利息赋值给interest
old_lalance = float(account_data['balance']) #读取数据中账户余额
# print('interest:',type(interest))
# print('amount:',type(amount))
# print('old_lalance:',type(old_lalance)) #还款操作
if settings.TRANSACTION_TYPE[tran_type]['action'] == 'plus': #因为是信用卡,所以需要判断操作操作,加plus
# print(settings.TRANSACTION_TYPE)
new_balance = old_lalance + amount + interest #执行的是信用卡的还款操作,计算方法是,旧余额+还款的钱和利息=最后的账户可用余额
#取现
elif settings.TRANSACTION_TYPE[tran_type]['action'] == 'minus' and settings.TRANSACTION_TYPE[tran_type] == 'transfer': #因为是信用卡,所以取现都是降低可用余额的操作
pass
else:
balance = old_lalance - amount - interest
if balance > 0:
new_balance = balance
else:
print("\033[31;1m余额不足\033[0m")
return account_data['balance'] = new_balance
# print(account_data)
accounts.dump_account(account_data)
return account_data else:
print("\033[31;1m%s交易类型不存在\033[0m" % tran_type)
online/Atm/core/transaction.py
{"enroll_date": "2018-09-27", "id": "", "expire_date": "2032-01-01", "status": 0, "password": "", "pay_day": "", "balance": 513.8, "credit": ""}
online/Atm/dbs/account/123.json
Python学习笔记-练习编写ATM+购物车(购物商城)的更多相关文章
- 吴裕雄--天生自然python学习笔记:编写网络爬虫代码获取指定网站的图片
我们经常会在网上搜索井下载图片,然而一张一张地下载就太麻烦了,本案例 就是通过网络爬虫技术, 一次性下载该网站所有的图片并保存 . 网站图片下载并保存 将指定网站的 .jpg 和 .png 格式的图片 ...
- Python学习笔记(十三)
Python学习笔记(十三): 模块 包 if name == main 软件目录结构规范 作业-ATM+购物商城程序 1. 模块 1. 模块导入方法 import 语句 import module1 ...
- python学习笔记目录
人生苦短,我学python学习笔记目录: week1 python入门week2 python基础week3 python进阶week4 python模块week5 python高阶week6 数据结 ...
- OpenCV之Python学习笔记
OpenCV之Python学习笔记 直都在用Python+OpenCV做一些算法的原型.本来想留下发布一些文章的,可是整理一下就有点无奈了,都是写零散不成系统的小片段.现在看 到一本国外的新书< ...
- Python学习笔记,day5
Python学习笔记,day5 一.time & datetime模块 import本质为将要导入的模块,先解释一遍 #_*_coding:utf-8_*_ __author__ = 'Ale ...
- 近期Python学习笔记
近期Python 学习笔记--一篇文入门python 作者:Pleiades_Antares(www.cnblogs.com/irischen) 写在前面的话 想学Python已经许久,一年多以前(应 ...
- Python学习笔记(15)- os\os.path 操作文件
程序1 编写一个程序,统计当前目录下每个文件类型的文件数,程序实现如图: import os def countfile(path): dict1 = {} # 定义一个字典 all_files = ...
- Python学习笔记010——匿名函数lambda
1 语法 my_lambda = lambda arg1, arg2 : arg1 + arg2 + 1 arg1.arg2:参数 arg1 + arg2 + 1 :表达式 2 描述 匿名函数不需要r ...
- 雨痕 的《Python学习笔记》--附脑图(转)
原文:http://www.pythoner.com/148.html 近日,在某微博上看到有人推荐了 雨痕 的<Python学习笔记>,从github上下载下来看了下,确实很不错. 注意 ...
随机推荐
- BZOJ 2141 排队 (三维偏序CDQ+树状数组)
题目大意:略 洛谷传送门 和 [CQOI2015]动态逆序对 这道题一样的思路 一开始的序列视为$n$次插入操作 把每次交换操作看成四次操作,删除$x$,删除$y$,加入$x$,加入$y$ 把每次操作 ...
- static和extern的作用域--题目
#include <stdio.h> ; int main(void) { , sum = , count = ; ,count++) // count = 2 { ; count++; ...
- IE6 浏览器常见兼容问题 共23个
1.<!DOCTYPE HTML>文档类型的声明. 产生条件:IE6浏览器,当我们没有书写这个文档声明的时候,会触发IE6浏览器的怪异解析现象: 解决办法:书写文档声明. 2.不同浏览器当 ...
- HDU 1350 Taxi Cab Scheme
Taxi Cab Scheme Time Limit: 10000ms Memory Limit: 32768KB This problem will be judged on HDU. Origin ...
- 拷贝构造函数不能传值,只能传引用,而且一般是传const引用
为什么呢?因为传值函数,需要调用拷贝构造函数,那就层层循环无止境了.
- poj3061 Subsequence ,尺取法
A sequence of N positive integers (10 < N < 100 000), each of them less than or equal 10000, a ...
- OpenCV特征点检測------Surf(特征点篇)
Surf(Speed Up Robust Feature) Surf算法的原理 ...
- Activity管理笔记
文章仅记录自己学习该模块时的一点理解,看到哪写到哪.所以特别散. AMS管理四大组件外加进程管理,当中最庞大的算是Activity了吧. 1.AMS中对ActivityStack划分为两类.当中一类是 ...
- WPF silverlight获取子控件(获取DataTemplate里的子控件)
public static class VisualTreeExtensions { /// <summary> /// 获取父节点控件 /// </summary> /// ...
- setsockopt 设置socket
1.closesocket(一般不会立即关闭而经历TIME_WAIT的过程)后想继续重用该socket:BOOL bReuseaddr=TRUE;setsockopt(s,SOL_SOCKET ,SO ...