作业需求:

模拟实现一个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+购物车(购物商城)的更多相关文章

  1. 吴裕雄--天生自然python学习笔记:编写网络爬虫代码获取指定网站的图片

    我们经常会在网上搜索井下载图片,然而一张一张地下载就太麻烦了,本案例 就是通过网络爬虫技术, 一次性下载该网站所有的图片并保存 . 网站图片下载并保存 将指定网站的 .jpg 和 .png 格式的图片 ...

  2. Python学习笔记(十三)

    Python学习笔记(十三): 模块 包 if name == main 软件目录结构规范 作业-ATM+购物商城程序 1. 模块 1. 模块导入方法 import 语句 import module1 ...

  3. python学习笔记目录

    人生苦短,我学python学习笔记目录: week1 python入门week2 python基础week3 python进阶week4 python模块week5 python高阶week6 数据结 ...

  4. OpenCV之Python学习笔记

    OpenCV之Python学习笔记 直都在用Python+OpenCV做一些算法的原型.本来想留下发布一些文章的,可是整理一下就有点无奈了,都是写零散不成系统的小片段.现在看 到一本国外的新书< ...

  5. Python学习笔记,day5

    Python学习笔记,day5 一.time & datetime模块 import本质为将要导入的模块,先解释一遍 #_*_coding:utf-8_*_ __author__ = 'Ale ...

  6. 近期Python学习笔记

    近期Python 学习笔记--一篇文入门python 作者:Pleiades_Antares(www.cnblogs.com/irischen) 写在前面的话 想学Python已经许久,一年多以前(应 ...

  7. Python学习笔记(15)- os\os.path 操作文件

    程序1 编写一个程序,统计当前目录下每个文件类型的文件数,程序实现如图: import os def countfile(path): dict1 = {} # 定义一个字典 all_files = ...

  8. Python学习笔记010——匿名函数lambda

    1 语法 my_lambda = lambda arg1, arg2 : arg1 + arg2 + 1 arg1.arg2:参数 arg1 + arg2 + 1 :表达式 2 描述 匿名函数不需要r ...

  9. 雨痕 的《Python学习笔记》--附脑图(转)

    原文:http://www.pythoner.com/148.html 近日,在某微博上看到有人推荐了 雨痕 的<Python学习笔记>,从github上下载下来看了下,确实很不错. 注意 ...

随机推荐

  1. [读书笔记] Python 数据分析 (八)画图和数据可视化

    ipython3 --pyplot pyplot: matplotlib 画图的交互使用环境

  2. 【安装配置Redis】

    目录 安装 配置 Redis官网:https://redis.io Redis是完全开源免费的,遵守BSD协议. Redis是一个高性能的key-value数据库. @ *** Redis具有以下特点 ...

  3. python基础知识部分练习大全

    python基础知识部分练习大全   1.执行 Python 脚本的两种方式 答:1.>>python ../pyhton.py 2. >>python.py   #必须在首行 ...

  4. 如何让myeclipse左边选中文件后自动关联右边树

    在左侧项目树的右上角下拉菜单里有link with editor 点击即可

  5. C语言 将十六进制字符串转为十六进制数 (二进制、十进制都适用)

    主要利用 long int strtol(const char *nptr,char **endptr,int base); 函数 函数说明: 参数base范围从2至36,或0.参数base代表采用的 ...

  6. 2015 Multi-University Training Contest 7 hdu 5371 Hotaru's problem

    Hotaru's problem Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) ...

  7. ASP.NET-Razor常用方法

    1.使用Scripts.Render()引入脚本 @sectionScrits{ @Scripts.Render("~/bundles/jquery") } 2.使用@Html.H ...

  8. CC2540/CC2541 : Set the Peripheral Being Advertising while It is Being Connected

    There is possible to set your CC254X be scanable when it is in connection. But, based on my test,the ...

  9. linux使用windows中编辑的文件,格式问题

    参考:https://blog.csdn.net/yongan1006/article/details/8142527 运行脚本时出现了这样一个错误,打开之后并没有找到所谓的^M,查了之后才知道原来是 ...

  10. 移动端video播放时不弹出页面层

    移动端视频在播放时会主动弹出页面,有的浏览器不会.对那些会的浏览器进行处理: 直接加上下面三个属性即可,兼容方面就不说了,微信上是很ok的. <video x5-playsinline=&quo ...