一、程序需求

模拟实现一个ATM + 购物商城程序:
1.额度 15000或自定义
2.实现购物商城,买东西加入 购物车,调用信用卡接口结账
3.可以提现,手续费5%
4.每月22号出账单,每月10号为还款日,过期未还,按欠款总额 万分之5 每日计息(没写)
5.支持多账户登录
6.支持账户间转账
7.记录每月日常消费流水
8.提供还款接口
9.ATM记录操作日志
10.提供管理接口,包括添加账户、用户额度,冻结账户等。。。
11.用户认证用装饰器

脑图:

二、目录

 ├── 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 #存各个用户的账户数据 ,一个用户一个文件
│ │ └── .json #新创建的用户账户示例文件
│ │ └── .json #一个用户账户示例文件
│ │ └── .json #一个用户账户示例文件
│ │ └── .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则可以对用户进行管理,解冻    用户、冻结用户、申领新卡
  ①添加账户
  ②冻结账户
  ③解冻账户
  ④退出

四、主程序

1.bin目录下代码

 '''/bin/atm.py'''

 #!/usr/bin/env python
#-*- Coding:utf-8 -*-
# Author:Eric.Shen
import os,sys
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
#print(base_dir)
sys.path.append(base_dir)
from core import main if __name__ == '__main__':
main.run() '''/bin/manage.py''' #!/usr/bin/env python
#-*- Coding:utf-8 -*-
# Author:Eric.Shen
#管理端(提供管理接口,包括添加账户、用户额度,冻结账户)
#解冻账户
#from core.auth import login_required
from core import accounts
from core import transaction
#解冻账户
def unblock_account(acc_data):
user_input = input("请输入你要解冻的用户:")
flag = 0
#锁定用户
val = transaction.lock_or_not(user_input,flag)
if val == 0:
print("解冻成功!")
return
#冻结账户
def block_account(acc_data):
'''
冻结账户初步构想是,在linux里把他的权限改掉;
或者将其文件改名
:param acc_data:
:return:
'''
user_input = input("请输入你要冻结的用户:")
flag = 1
#锁定用户
val = transaction.lock_or_not(user_input,flag)
if val == 0:
print("冻结成功!")
return #添加账户、用户额度
def add_account(acc_data):
account = {
"id": None,
"balance": None,
"expire_date": None,
"enroll_date": None,
"credit": None,
"pay_day": None,
"password": None,
"status": None
}
menu = {
0: "账户(数字):",
1: "余额:",
2: "到期时间:",
3: "办卡时间:",
4: "信用额度:",
5: "还款日期:",
6: "密码:",
7: "默认:"}
menu_user = {
0: "id",
1: "balance",
2: "expire_date",
3: "enroll_date",
4: "credit",
5: "pay_day",
6: "password",
7: "status"
}
for i in range(8):
data = input("%s" % menu[i]).strip()
account['%s' % menu_user[i]] = data
accounts.dump_account(account)#写入文件
print("创建成功!")
return def logout(acc_data):
exit("程序退出!")
#管理界面主程序
def manage_main(acc_data): menu = u'''
---------管理界面---------
1.添加账户
2.冻结账户
3.解冻账户
4.退出'''
menu_dic = {
'': add_account,
'': block_account,
'': unblock_account,
'': logout
}
exit_flag = False
while not exit_flag:
print(menu)
user_option = input("请输入你的选择:")
if user_option in menu_dic:
menu_dic[user_option](acc_data)
else:
print("\033[31;1m选择不存在!\033[0m") .

2.conf目录下代码

 '''/conf/Settings.py'''

 #!/usr/bin/env python
#-*- Coding:utf-8 -*-
# Author:Eric.Shen
#参数配置文件
import os,sys,logging BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))#/Atm DATABASE = {
'engine': 'file_storage',
'name': 'accounts',
'path': "%s/db" % BASE_DIR#../Atm
} LOG_LEVEL = logging.INFO
LOG_TYPES = {
'transaction': 'transaction.log',
'access': 'access.log',
} #发生交易的配置类型
TRANSACTION_TYPE = {
'repay':{'action':'plus','interest':0},#还款
'withdraw':{'action':'minus','interest':0.05},#取现是降低可用余额
'transfer':{'action':'minus','interest':0.05},#转账是降低可用余额
'consume':{'action':'minus','interest':0},
}

3.core目录下代码

 '''/core/accounts.py'''

 #!/usr/bin/env python
#-*- Coding:utf-8 -*-
# Author:Eric.Shen
#用于从文件里加载和存储账户数据
import json,time
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(settings.DATABASE)
# account_file = "%s/%s.json" %(db_path,account_id)
#
db_api = db_handler.db_handler()
data = db_api("select * from accounts where account=%s" % account_id)#在进行操作的时候在读取一遍数据中的数据(保证数据的最新)
return data#返回读取到的数据 # with open(account_file) as f:
# acc_data = json.load(f)
# return acc_data #写入文件数据
def dump_account(account_data):
''' :param account_data:
:return:
'''
db_api = db_handler.db_handler()
data = db_api("update accounts where account=%s" % account_data['id'],account_data = account_data) # db_path = db_handler.db_handler(settings.DATABASE)
# 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

accounts.py

 '''/core/auth.py'''

 #!/usr/bin/env python
#-*- Coding:utf-8 -*-
# Author:Eric.Shen
#用户认证模块
import json,time,os
from core import db_handler
from bin import manage
from conf import Settings
from core import logger
#装饰器(用于验证账户是否登陆过)
def login_required(func):
'''
验证用户是否登陆
:return:
'''
def wrapper(*args,**kwargs):
if args[0].get('is_authenticated'):
return func(*args,**kwargs)
else:
exit("用户不能认证")
return wrapper def acc_auth(account,password):
'''
账户验证函数
:return:
'''
db_path = db_handler.db_handler()
account_file = "%s/%s.json" %(db_path,account)
print(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: #passed the authentication
return account_data
else:
print("\033[31;1m账号或密码错误,请重新输入!\033[0m")
else:
print("\033[31;1m[%s]账户不存在!\033[0m" % account) def acc_auth2(account,password):
'''
优化版认证接口
:param
account:信用卡账户
password:信用卡密码
:return: 返回读取到的数据文件的所有账户数据
'''
db_api = db_handler.db_handler()
data = db_api("select * from accounts where account=%s" %account)#此处返回值为db_handler.py中的
# 得到的所有数据(读取到的这个账户的所有数据)赋值给data
if data["status"] == 2:#判断是否为管理者
manage.manage_main(data)
if data['status'] == 1:
print("你的账户已经被冻结,请联系管理员!\n")
option = input("请按b退出!")
if option == "b":
exit("程序已经退出!")
if data['password'] == password:#判断data中的password数据是否恒等于输入的password(此处如果继续执行,则账户密码完全正确)
#time.mktime 返回用秒数来表示时间的浮点数。
#实例结果:time.mktime(t) : 1234915418.000000
#time.strptime 根据指定的格式把一个时间字符串解析为时间元组
#实例结果:time.strptime(string[, format])
exp_time_stamp = time.mktime(time.strptime(data['expire_date'],"%Y-%m-%d"))#将数据文件中的expire_data时间
# 转为以秒计数的时间赋值给exp_time_stamp
if time.time() > exp_time_stamp:#判断当前以秒计算的数据是否大于数据文件中的数据
print("\033[31;1m[%s]账户以及过期,请重新激活!\033[0m" % account)
else:
return data#没有超时,则返回读取到的数据文件的所有内容
else:
print("\033[31;1m帐户名或者密码错误!\033[0m") def acc_login(user_data,log_obj):
'''
账户登陆函数
:param
user_data:用户信息数据,只存在内存中
:return: 账户密码都对的情况下,返回所有账户数据
'''
retry_count = 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_auth2(account,password)#解耦,将输入的账户和密码传入到acc_auth2函数中,进行验证
# (最后返回的是读取到的输入正确账户的所有数据)赋值给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()

auth.py

 '''/core/db_handler.py'''

 #!/usr/bin/env python
#-*- Coding:utf-8 -*-
# Author:Eric.Shen
#数据库连接引擎
#处理所有数据库交互
import json,time,os
from conf import Settings #解析文件数据路径
def file_db_handle(conn_params):
'''
解析数据库文件路径
:return:
'''
#print('file db:',conn_params)
return file_execute #数据库句柄
def db_handler():
'''
连接数据库
:return:
'''
conn_params = Settings.DATABASE#把Settings下的DATABASE的数据赋值给conn_params
if conn_params['engine'] == 'file_storage':#判断Settings下的DABASE是什么引擎,这里只用文件文件引擎
return file_db_handle(conn_params)#则把Settings下的DABASE的数据传给file_db_handle并返回
elif conn_params['engine'] == 'mysql':
pass#支持扩展,此次只作为一个说明 #文件执行
def file_execute(sql,**kwargs):
'''
传入sql语句,及其他变量,
:param sql: sql语句操作得到结果
:param kwargs: 其他得变量
:return:
'''
conn_params = Settings.DATABASE#把Settings下的DATABASE的数据赋值给conn_params,再一次赋值意味着得到最新得数据
db_path = '%s/%s' % (conn_params['path'],conn_params['name'])#数据库的文件路径 ../db/accounts
#print(sql,db_path)#sql = select * from accounts where account=%s %account(此时这个account等于程序开始时要求哟用户输入得数据)
sql_list = sql.split('where')#将上面得sql语句以where分开,(sql_list列表内容:'select * from accounts' ,"account='account' ")
#print(sql_list)
#startswith() 方法用于检查字符串是否是以指定子字符串开头,
# 如果是则返回 True,否则返回False。如果参数 beg 和 end 指定值,
# 则在指定范围内检查。
if sql_list[0].startswith('select') and len(sql_list) > 1:#判断sql_list列表中得第一个字符是select并且列表的长度是大于1的
column,val = sql_list[1].strip().split('=')#将sql_list列表第二个数据先去掉默认空格,并且以‘=’为界分开放入--》
#-->column = account , val = '此处为开始程序输入的账户'
#Python strip() 方法用于移除字符串头尾指定的字符(默认为空格)。
if column == 'account':#判断是否为account,然后做指定的操作(这里使用的是account)
account_file = '%s/%s.json' % (db_path,val)#这一步得到数据文件路径的文件绝对路径
#print(account_file)
if os.path.isfile(account_file):#使用绝对路径判断是否为文件,返回True
with open(account_file,'r') as f:#以只对的方式打开文件并把文件句柄赋值给f(用with方法打开不用自己写关闭文件的方法)
account_data = json.load(f)#json加载文件赋值给account_data
return account_data#返回account_data数据(将.json文件中的数据都都出来返回)
else:
exit("\033[31;1m[%s]账户不存在!\033[0m" % val)#若判断不是,则返回没有此用户
#写入数据
elif sql_list[0].startswith('update') and len(sql_list) > 1:
column, val = sql_list[1].strip().split('=')#将帐户名写入到val中
if column == 'account':
account_file = "%s/%s.json" % (db_path,val)
# if os.path.isfile(account_file):
account_data = kwargs.get("account_data")#得到账户数据
with open(account_file,'w') as f:
acc_data = json.dump(account_data,f)
#print(acc_data)
return True

db_handler.py

 '''/core/logger.py'''

 #!/usr/bin/env python
#-*- Coding:utf-8 -*-
# Author:Eric.Shen
#日志记录模块,处理所有日志工作 import logging
from conf import Settings def logger(log_type):
#创建日志
logger = logging.getLogger(log_type)
logger.setLevel(Settings.LOG_LEVEL) #创建控制台处理程序并将级别设置为调试
ch = logging.StreamHandler()
ch.setLevel(Settings.LOG_LEVEL)
#创建文件处理程序并设置级别为警告
log_file = "%s/logs/%s" %(Settings.BASE_DIR,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')
'''

logger.py

 #!/usr/bin/env python
#-*- Coding:utf-8 -*-
# Author:Eric.Shen
#记账\还钱\取钱\与账户金额相关的操作,冻结或者锁定用户
from conf import Settings
from core import accounts
from core import logger def make_transaction(log_obj,account_data,tran_type,amount,**kwargs):
'''
处理所有用户的所有交易
:param log_obj:
:param account_data: 用户最新的数据
:param tran_type: 交易类型
:param amount: 交易数量
:param other: 主要用于日志使用
:return: 返回最新的账户数据
'''
amount = float(amount)#转换为浮点型
if tran_type in Settings.TRANSACTION_TYPE:#判断传入的类型是否在配置参数里面
interest = amount * Settings.TRANSACTION_TYPE[tran_type]["interest"]#根据交易类型计算利息赋值给interest
old_balance = account_data['balance']#读取数据中账户余额
#还款操作
if Settings.TRANSACTION_TYPE[tran_type]['action'] == 'plus':#因为是信用卡,所以还款时提升可使用余额的操作,故计为加plus
new_balance = old_balance + amount + interest#执行的是信用卡的还款操作,计算方法是,旧余额+还款的钱和利息=最后的账户可用余额
#取现\转账操作
elif Settings.TRANSACTION_TYPE[tran_type]['action'] == 'minus':#因为是信用卡,所以取现都是降低可用余额的操作
new_balance = old_balance - amount - interest
#只属于转账的
if kwargs.get('re_account'):
#print(kwargs[0],kwargs[1])
re_account_data = accounts.load_current_balance(kwargs.get('re_account'))#得到要转入账户的所有数据
re_account_balance = re_account_data['balance'] + amount#得到转入账户余额的最新值
re_account_data['balance'] = re_account_balance#将最新的余额全部写入账户的余额中
print(re_account_data)
accounts.dump_account(re_account_data)#将最新的账户所有数据写入到文件中
if new_balance < 0:
print("\033[31;1m[%s]账户的信用余额不足以支付此次交易[-%s],你当前的余额是[%s]\033[0m"
% (account_data['creat'], (amount + interest), old_balance))
return
#转账
'''
elif Settings.TRANSACTION_TYPE[tran_type] == 'transfer' :
new_balance = old_balance - amount - interest#自己账户的最新余额
#读取转入的账户,写入转入金额
print(kwargs[0],kwargs[1])
re_account_data = accounts.load_current_balance(kwargs.get('re_account'))#得到要转入账户的所有数据
re_account_balance = re_account_data['balance'] + amount#得到转入账户余额的最新值
re_account_data['balance'] = re_account_balance#将最新的余额全部写入账户的余额中
print(re_account_data)
accounts.dump_account(re_account_data)#将最新的账户所有数据写入到文件中
''' account_data['balance'] = new_balance#将最新的余额写入到账户数据中
print(account_data)
accounts.dump_account(account_data)#将最新的账户余额写回文件
#写入日志
#log_obj.info('账户:%s,操作:%s,数量:%s,利息:%s' %(account_data['id'],tran_type,amount,interest))
return account_data
else:
print("\033[31;1m%s交易类型不存在\033[0m" % tran_type)
#冻结或者锁定用户
def lock_or_not(account,flag):
data = accounts.load_current_balance(account) if data["status"] == 1:
print("该账户已经锁定!")
if data['status']:
data["status"] = flag
accounts.dump_account(data)
return 0

transaction.py

 #!/usr/bin/env python
#-*- Coding:utf-8 -*-
# Author:Eric.Shen
#主程序句柄模块,处理所有用户交互内容
from core import auth
from core import accounts
from core import logger
from core import transaction
from core.auth import login_required
from shopping_mall import shopping_mall
from bin import manage
import time #交易日志
trans_logger = logger.logger('transaction')
#访问日志
access_logger = logger.logger('access') #临时账户数据,仅存在于内存中
user_data = {
'account_id':None,
'is_authenticated':False,
'account_data':None
} #账户信息
def account_info(acc_data):
print(user_data)
#还款
@login_required#装饰器,判断用户是否登陆
def repay(acc_data):
'''
打印当前余额,让用户偿还账单
:param acc_data:
:return:
'''
account_data = accounts.load_current_balance(acc_data['account_id'])#将用户账户名字传入到load_current_balance中
#返回最新的用户数据赋值给 account_data
current_balance = '''
---------银行信息----------
信用额度: %s
可用余额: %s
''' %(account_data['credit'],account_data['balance'])
print(current_balance)
back_flag = False
while not back_flag:
repay_amount = input("\033[33;1m输入你要还款的金额:\033[0m").strip()#还款金额
if len(repay_amount) > 0 and repay_amount.isdigit():
#print('ddd 00')
#将数据传入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']))
else:
print('\033[31;1m[%s]是无效的账户!\033[0m' % repay_amount)
if repay_amount == 'b':
back_flag =True
#取款
@login_required
def withdraw(acc_data):
'''
打印当前余额,让用户执行取款操作
:param acc_data:
:return:
'''
account_data = accounts.load_current_balance(acc_data['account_id'])
# 将用户账户名字传入到load_current_balance中
# 返回最新的用户数据赋值给 account_data
current_balance = ''' --------- 银行信息 --------
信用额度: %s
账户余额: %s''' % (account_data['credit'], account_data['balance'])
print(current_balance)
back_flag = False
while not back_flag:
withdraw_amount = input("\033[33;1m输入取款金额:\033[0m").strip()
if withdraw_amount == 'b':
return
if len(withdraw_amount) > 0 and withdraw_amount.isdigit():
new_balance = transaction.make_transaction(trans_logger,account_data,'withdraw', withdraw_amount)
if new_balance:
print('''\033[42;1m最新余额:%s\033[0m''' %(new_balance['balance']))
else:
print('\033[31;1m[%s]是无效的账户!\033[0m' % withdraw_amount) #转账
@login_required
def transfer(acc_data):
'''
打印当前余额,转账操作函数
:param acc_data:用户数据
:return:
'''
account_data = accounts.load_current_balance(acc_data['account_id'])
# 将用户账户名字传入到load_current_balance中
# 返回最新的用户数据赋值给 account_data
current_balance = ''' --------- 银行信息 --------
信用额度: %s
账户余额: %s''' % (account_data['credit'], account_data['balance'])
print(current_balance)
back_flag = False
while not back_flag:
reciprocal_account = input("\033[31;1m请输入对方帐户名:\033[0m").strip()#输入对方账户信息
transfer_amount = input("\033[31;1m转账金额:\033[0m").strip()#转账金额
if reciprocal_account or transfer_amount == 'b' :
return
if len(transfer_amount) > 0 and transfer_amount.isdigit():
new_balance = transaction.make_transaction(trans_logger,account_data,'transfer',
transfer_amount,re_account = reciprocal_account)
if new_balance:
print("\033[41;1m转账成功!\033[0m")
print("\033[42;1m您当前的余额为:%s\033[0m" %(new_balance["balance"]))
else:
print('\033[31;1m[%s] \033[0m') #账单
@login_required
def pay_check(acc_data):
pass
#退出
def logout(acc_data):
exit("程序已经退出!")
#购物商城
def shopping_mall_this(acc_data):
shopping_mall.main_menu(acc_data)
#管理窗口
def goto_manage():
manage.manage_main(user_data)
#菜单
def interactive(acc_data):
'''
与用户交互
:param acc_data: 验证过的用户的所用数据
:return:
'''
menu = u'''
-----------银行----------
\033[32;1m
1.账户信息
2.还款
3.取款
4.转账
5.账单
6.退出
\033[0m
'''
menu_dic = {
'': account_info,
'': repay,
'': withdraw,
'': transfer,
'': pay_check,
'': logout,
}
exit_flag = False
while not exit_flag:
print(menu)#打印出菜单,供用户选择
user_option = input("请输入你的选择:").strip()#输入用户的选择,过滤掉空格
if user_option == 'b':
return
if user_option in menu_dic:#用户的选择如果在这个菜单里
#print('accdata',acc_data)
menu_dic[user_option](acc_data)#用户选择执行的功能,把acc_data验证过的用户的所有数据(数据文件中的数据)
else:
print("\033[31;1m选择不存在!\033[0m")
#带有购物商场的主菜单
def main_menu(acc_data):
main_menu = u'''
----------主菜单---------
\033[32;1m
1.购物商城
2.银行卡操作
3.退出
\033[0m
'''
main_menu_dic = {
'':shopping_mall_this,
'':interactive,
'':logout,
}
exit_flag = False
while not exit_flag:
print(main_menu)
user_option = input("请输入你的选择:").strip()
if user_option == 'b':
return
if user_option in main_menu_dic:
main_menu_dic[user_option](acc_data)
else:
print("\033[31;1m选择不存在!\033[0m")
def run():
'''
当程序启动时,这个程序开始运行,处理关于用户的所有交互的内容
'''
acc_data = auth.acc_login(user_data,access_logger)#程序从这里开始,执行auth下的acc_login函数
# (返回的是验证过的正确的账户数据)赋值给acc_data(此时这里的数据为输入账户名字的数据文件的数据)
if user_data['is_authenticated']:
user_data['account_data'] = acc_data#把账户所有信息传给账户开始时的临时的账户数据空字典,
# 把所有的数据文件传给账户的账户数据里面,
#interactive(user_data)#把user_data里的所有数据传入菜单函数,进行下一步操作
main_menu(user_data)

main.py

4.db下目录下代码

/db/accounts/123.json

 {"pay_day": "", "enroll_date": "2018-02-19", "credit": "", "balance": "", "id": "", "expire_date": "2032-01-01", "password": "", "status": 0}

123.json

5.logs目录下代码

/logs/access.log&transaction.log

6.shopping_mall下代码

/shopping_mall/product.txt&shopping_list.txt

 #!/usr/bin/env python
#-*- Coding:utf-8 -*-
# Author:Eric.Shen
# !/usr/bin/env python
# -*- Coding:utf-8 -*-
# Author:Eric.Shen
# 2018.02.06
# path python3.5
# 优化版的购物车
# 用户入口:
# 1.商品的信息存到文件里
# 2.已购商品,余额记录
# 商家入口:
# 1.可以添加商品 2.修改商品价格
# 存储商品列表
import fileinput
from core import accounts product_list = []
f = open("D:\\Python_train\\day4\\Atm\\shopping_mall\\product.txt", "r") # 打开文件
for line in f.readlines():
line = line.strip() # 去掉最后一个换行符
index, item = line.split(":") # 以冒号分割得到前后两个数据
product_list.append((index, item)) # 添加的数据
f.close() def print_product_list():
for index, item in enumerate(product_list):
print(index, item) # 用户入口
# 用户购物
def user_shopping(account_data):
#salary = input("请输入你的薪水:")
salary = account_data['account_data']['balance']
print_product_list()
if salary > 0:
shopping_list = [] # 存放用户购物车清单
while True:
option = input("喜欢那个就买哪个(对应的标号):")
if option.isdigit():
option = int(option)
if option >= 0 and option <= len(product_list):
p_item = product_list[option] # 用户选择的商品
# print(product_list)
# print(p_item[1])
c_num = int(p_item[1])
if salary >= c_num:
shopping_list.append(p_item)
salary -= c_num
print("添加购物车成功,你的余额还有%s" % (salary))
else:
print("你的余额不足,只剩%s元" % (salary))
else:
print("输入错误,请重新输入!")
elif option == "q":
print("----------------购物清单---------------")
for s_list in shopping_list:
print(s_list)
print("你的余额为%s" % (salary))
account_data['account_data']['balance'] = salary
#print(account_data)
accounts.dump_account(account_data['account_data'])#写入文件
print("..........exit.........")
exit()
else:
print("无效的输入")
else:
exit("余额不足!") # 商家入口
# 商家添加商品
def add_product():
name_of_product = input("请输入你要添加的商品名字:")
price_of_product = input("请输入你要添加商品的价格:")
f = open("product.txt", "a")
f.write(str("\n" + name_of_product) + ": %s" % (price_of_product))
f.close()
print("添加成功!\nexit----------") # 修改商品价格
def change_price():
print_product_list() # 打印商品列表
choice = input("请输入你的选择:")
# name_of_change = input("请输入你要改变的商品名字")
price_of_change = input("请输入你要改变商品的价格:")
if choice.isdigit():
choice = int(choice)
if choice >= 0 and choice <= len(product_list):
p_item = product_list[choice] # 选择的商品
# c_num = int(p_item[1])#转换成int类型
for line in fileinput.input("product.txt", inplace="%s" % (choice)): # 对输入的选择行进行修改
line = line.replace("%s" % (p_item[1]), "%s" % (price_of_change)).strip()
print(line)
exit("修改成功!")
else:
print("输入无效")
else:
if choice == "q":
exit("退出") def main_menu(account_data):
print("--------------------------"
"--------------------------"
"\n"
" 欢迎进入购物菜单 "
"\n"
"\n"
"商家请按b,用户请按c\n"
"--------------------------"
"--------------------------")
c_num = input("请输入你的选择:") # 使用者选择
if c_num == "b":
print("--------------------------"
"--------------------------"
"\n"
" 欢迎进入商家界面 "
"\n"
"\n"
"添加商品请按a,修改价格请按c\n"
"--------------------------"
"--------------------------")
c_num2 = input("请输入你的选择:")
if c_num2 == "a":
# 实现添加商品功能
add_product()
if c_num2 == "c":
# 实现商品价格修改功能
change_price()
else:
print("输入有误!")
if c_num == "c":
print("--------------------------"
"--------------------------"
"\n"
" 欢迎进入用户界面 "
"\n"
"\n" "--------------------------"
"--------------------------")
# 购物功能
print(account_data)
user_shopping(account_data)
else:
print("输入有误程序退出!")

shopping_mall

五、README

 作者:Eric.shen
此次系统的设计仅用来学习python,开始于2018.2.13-19完(此系统,日志部分没有完善留着日后补充现在还没学到)
作业需求: 模拟实现一个ATM + 购物商城程序:
1.额度 15000或自定义
2.实现购物商城,买东西加入 购物车,调用信用卡接口结账
3.可以提现,手续费5%
4.每月22号出账单,每月10号为还款日,过期未还,按欠款总额 万分之5 每日计息(没写)
5.支持多账户登录
6.支持账户间转账
7.记录每月日常消费流水
8.提供还款接口
9.ATM记录操作日志
10.提供管理接口,包括添加账户、用户额度,冻结账户等。。。
11.用户认证用装饰器 一、软件定位,软件的基本功能。
实现一个简单的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则可以对用户进行管理,解冻用户、冻结用户、申领新卡
①添加账户
②冻结账户
③解冻账户
④退出
五、常见问题说明。
日志没有实现,账单没有实现

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

product.txt

iphone:
Mac pro:
Bike:
Watch:
Coffe:
Python book:
Book:

shopping_list.txt

('Coffe', )
('Pychon book', )

长风破浪会有时,直挂云帆济沧海。

欢迎多多提提意见

Python学习--------------Atm+购物车系统的更多相关文章

  1. python实现简单购物车系统(练习)

    #!Anaconda/anaconda/python #coding: utf-8 #列表练习,实现简单购物车系统 product_lists = [('iphone',5000), ('comput ...

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

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

  3. python学习:购物车程序

    购物车程序 product_list = [ ('mac',9000), ('kindle',800), ('tesla',900000), ('python book',105), ('bike', ...

  4. Python学习---Django路由系统【all】

    Django URL (路由系统) Django URL (路由系统): URL配置(URLconf)就像Django 所支撑网站的目录.它的本质是URL模式以及要为该URL模式调用的视图函数之间的映 ...

  5. ATM+购物车系统

    ATM目录 start.py import os,sys from core import src base_path=os.path.dirname(os.path.dirname(__file__ ...

  6. Python学习之购物车

    实现功能: 程序启动,提示用户输入用户名和密码,程序读取余额文件last_salary.txt内容(文件不存在则自动创建),若文件内容为空则提示“首次登录,请输入工资”: 用户可以输入商品编号进行购买 ...

  7. python 学习分享-购物车实操篇

    程序要求如下: '''购物车程序: 启动程序后,输入用户名密码后,如果是第一次登录,让用户输入工资,然后打印商品列表 允许用户根据商品编号购买商品 用户选择商品后,检测余额是否够,够就直接扣款,不够就 ...

  8. ATM系统和购物车系统 不需要文件支撑

    目录 ATM系统 购物车系统 ATM系统 #coding=utf8 #Version:python 3.6.4 #Tools:Python 2019.9.7 _data_ = '2019/9/7/01 ...

  9. [Python学习]错误篇二:切换当前工作目录时出错——FileNotFoundError: [WinError 3] 系统找不到指定的路径

    REFERENCE:<Head First Python> ID:我的第二篇[Python学习] BIRTHDAY:2019.7.13 EXPERIENCE_SHARING:解决切换当前工 ...

随机推荐

  1. Python爬虫爬取贴吧的帖子内容

    最近在看一个大神的博客,从他那里学会了很多关于python爬虫的知识,其实python如果想用在实际应用中,你需要了解许多,比如正则表达式.引入库.过滤字段等等,下面不多说,我下面的程序是爬取Ubun ...

  2. HDFS--大数据应用的基石

    近些年,由于智能手机的迅速普及推动移动互联网技术的蓬勃发展,全球数据呈现爆发式的增长.2018年5月企鹅号的统计结果:互联网每天新增的数据量达2.5*10^18字节,而全球90%的数据都是在过去的两年 ...

  3. 大数据入门第二十天——scala入门(二)scala基础02

    一. 类.对象.继承.特质 1.类 Scala的类与Java.C++的类比起来更简洁 定义: package com.jiangbei //在Scala中,类并不用声明为public. //Scala ...

  4. 20155227《网络对抗》Exp3 免杀原理与实践

    20155227<网络对抗>Exp3 免杀原理与实践 实践内容 正确使用msf编码器,msfvenom生成如jar之类的其他文件,veil-evasion,自己利用shellcode编程等 ...

  5. 20155305《网络对抗》Web安全基础实践

    20155305<网络对抗>Web安全基础实践 基础问题回答 SQL注入攻击原理,如何防御? 原理:SQL注入攻击指的是通过构建特殊的输入作为参数传入Web应用程序,而这些输入大都是SQL ...

  6. 20155306白皎 《网络对抗》 Exp9 Web安全基础实践

    20155306白皎 <网络对抗> Exp9 Web安全基础实践 一.基本问题回答 SQL注入攻击原理,如何防御 原理是: 就是通过把SQL命令插入到"Web表单递交" ...

  7. sinopia 搭建记录

    最近公司有个问题,一些公共部分每次都要手动发送,放到 git 上涉及到父子 git 问题,现在就想在内部搭建一个 npm,涉及到公共模块了就直接 npm update 更新一下.找到了 sinopia ...

  8. idea java方法中 传多个参数对象 的复制粘贴快速处理方法

    比如像这种的传多个参数对象,我是直接复制过来,然后把第一个字母改成大写,然后后面的实例对象敲一个第一个字符的小写,回车就直接出来了 在写调用参数的地方,ctrl+p 调出提示,然后按下提示里的实例的第 ...

  9. 使用plumbing命令来深入理解git add和git commit的工作原理

    前言: plumbing命令 和 porcelain命令 git中的命令分为plumbing命令和porcelain命令: porcelain命令就是我们常用的git add,git commit等命 ...

  10. 2018-07-09--记录一次gitlab迁移事件及遇到的问题

    一.事情起因 因机房服务器即将到期,需要将即将到期的服务器迁移至云上,迁移之前没有查看老环境的Gitlab是什么版本,直接装的Gitlab社区版,做数据导入时提示版本错误: [root@vpn-ser ...