思路:ATM是一个单独程序,提供给消费的是一个接口core下的settlement.py,只做了个人的,没写管理的模块

   Shopping也是一个单独的,只做了一个购物的消费模块,没写商家模块,偷懒用了银行的数据库,用户名和密码都是用的一套的

具体目录如下:

Python设计超市收银描述流程图 模拟商场收银程序python_数据

Python设计超市收银描述流程图 模拟商场收银程序python_python_02

Python设计超市收银描述流程图 模拟商场收银程序python_数据_03

atm.py:

import os
import 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()

settings.py:
import os
import sys
import logging
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

DATABASE = {
'engine': 'file_storage',
'name':'accounts',
'path':"%s\\db" % BASE_DIR
}

LOG_LEVEL = logging.INFO
LOG_TYPES = {
'transaction':'transactions.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},
}

 accounts.py:

import json,os,sys,time

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_DIR)
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)
with open(account_file) as f:
 acc_data = json.load(f)
return acc_data
def dump_account(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

 auth.py:

import os
import sys
import json
import time
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_DIR)
from core import db_handler
from conf import settings
from core import logger

def acc_auth(account,password):
"""

 :param account: 信用卡账号
 :param password: 信用卡密码
 :return:
 """
 db_path = db_handler.db_handler(settings.DATABASE) #数据库存储的目录
 account_file = "%s\%s.json" %(db_path,account) #个人的json数据
 # print(account_file)
 if os.path.isfile(account_file): #还必须判断这个文件是否存在
 with open(account_file) as f:
 account_data = json.load(f) #还原成字典
 if account_data['password'] == password:
#mktime() 接收struct_time对象作为参数,返回用秒数来表示时间的浮点数,
 # time.strptime(string[, format])根据指定的格式把一个时间字符串解析为时间元组
 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:user_data在主函数定义是临时账户信息
 :param log_obj:log_obj执行完logger()之后返回的logger,可以调用相关方法写入日志信息
 :return:
 """
 retry_count = 0
 while user_data['is_authenticated'] is not True and retry_count < 3:
 account = input("请输入账号:").strip()
 password = input("请输入密码:").strip()
 auth = acc_auth(account,password)
if auth: #不是空代表验证成功了,因为验证成功之后会返回用户所有信息
 user_data['is_authenticated'] = True
 user_data['account_id'] = account
return auth #又返回了用户的全部数据
 retry_count +=1
 else:
 log_obj.error("account[%s] too many login attempts" %account)
exit()

db_handler.py:
"""
根据数据库的类型,对数据进行处理
"""
import os
def file_db_handle(conn_params):
"""
 数据库的存放的路径
 :param conn_params: 传入的数据是settings中的DATABASE
 :return:
 """
 l = os.sep
# print('file db:',conn_params)
 db_path = '%s\%s' %(conn_params['path'],conn_params['name'])
return db_path

def db_handler(conn_parms):
"""
 根据不同的类型,用不同的函数进行处理
 :param conn_parms:
 :return:
 """
 if conn_parms['engine'] == 'file_storage':
return file_db_handle(conn_parms)
elif conn_parms['engine'] == 'mysql':
pass
logger.py:

import logging
import os,sys
#想要从父类导入,需要先加入环境变量
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_DIR)
from conf import settings

def logger(log_type):

#create logger
 logger = logging.getLogger(log_type)
 logger.setLevel(settings.LOG_LEVEL)
#create file handler and set level to warning,这是会输出到屏幕上的
 ch = logging.StreamHandler()
 ch.setLevel(settings.LOG_LEVEL)
#创建输入到文件夹中的日志
 log_file = "%s/log/%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 - %(levelname)s - %(message)s')
#在日志中加入格式
 # ch.setFormatter(formatter)
 fh.setFormatter(formatter)
#将内容输入到创建的日志文件中
 # logger.addHandler(ch)
 logger.addHandler(fh)

return logger #这可以后边可以调用,直接写入日志

# #对logger进行测试
# a = logger("access")
# a.warn("test")

main.py:
import os
import sys,time

BARE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BARE_DIR)
from core import auth
from core import logger
from core import transaction
from core import accounts
from conf import settings

#logger()函数执行,并且返回了logger,可以进行调用warn方法进行日志写入
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['account_data'])
def repay(acc_data):
 account_data = user_data['account_data']
# for k,v in account_data.items():
 # print(k,v)
 current_balance = """
 ----------- BALANCE INFO --------
 Credit:%s
 Balance:%s
 """ %(account_data['credit'],account_data['balance'])
print(current_balance)
 back_flag = False #设置标志,输入b跳出while循环
 while not back_flag:
 repay_amount = input("\033[33;1mInput repay amount:\033[0m").strip()
if len(repay_amount) > 0 and repay_amount.isdigit():
# print('ddd 00')
 new_balance = transaction.make_transaction(trans_logger,account_data,'repay',repay_amount)
# print(new_balance)
 if new_balance:
print("""\033[42;1mNew Balance:%s\033[0m"""%(new_balance['balance']))
else:
print('\033[31;1m[%s] is not a valid amount,only accept integer!\033[0m' % repay_amount)
if repay_amount == 'b':
 back_flag = True

def withdraw(acc_data):
"""
 取款
 :param acc_data: 变成了数据库数据,而且是字典的格式
 :return:
 """
 accout_data = accounts.load_current_balance(acc_data['account_id']) #再去数据库拿一遍数据,有点多余
 current_balance = '''
 ------- BALANCE INFO -------
 Credit: %s
 Balance: %s
 '''%(accout_data['credit'],accout_data['balance'])
print(current_balance)
 back_flag = False
 while not back_flag:
 withdraw_amount = input("\033[33;1mInput withdraw amount:\033[0m").strip()
if len(withdraw_amount) > 0 and withdraw_amount.isdigit():
 new_balance = transaction.make_transaction(trans_logger,accout_data,'withdraw',withdraw_amount)
if new_balance:
print('''\033[42;1mNew Balance:%s\033[0m'''%(new_balance['balance']))
else:
print('\033[31;1m[%s] is not a valid amount,only accept integer!')
if withdraw_amount == 'b':
 back_flag = True

def transfer(acc_data):
"""

 :param acc_data: 变成了数据库数据,而且是字典的格式
 :return:
 """
 account_data = accounts.load_current_balance(acc_data['account_id'])
 current_balance = '''
 ------- BALANCE INFO -------
 Credit: %s
 Balance: %s'''%(account_data['credit'],account_data['balance'])
print(current_balance)

#钱数必须是数字
 back_flag = False
 while not back_flag:
 tranfer_amount = input("\033[33;1mInput tranfer amount:\033[0m").strip()
#只支持整数
 if len(tranfer_amount) > 0 and tranfer_amount.isdigit():

#调用transaction,传入账号信息,交易类型,交易金额,交易日志
 new_account_data = transaction.make_transaction(trans_logger,account_data,'transfer',tranfer_amount)
if new_account_data:
print("\033[42;1mNew Balance:%s\033[0m"%new_account_data['balance'])
else:
print('\033[31;1m[%s] is not a valid amount,only accept integer!')
if tranfer_amount == 'b':
 back_flag = True

def pay_check(acc_data): #账单功能
 back_flag = False
 while not back_flag:
 Time_input = input("\033[33;1mplease input time(Y-M-D),输入b退出:\033[0m").strip()
if Time_input == 'b': back_flag = True continue else: try: select_time = time.mktime(time.strptime(Time_input,'%Y-%m-%d')) # print(select_time) except Exception as ValueError: print("清输入正确的日期格式") continue # log_file = "atm_test/log/transactions.log" log_file = "%s/log/%s" % (settings.BASE_DIR, settings.LOG_TYPES["transaction"]) # print(log_file) with open(log_file,"r",encoding="utf-8") as f: for i in f: # for i in f : 是和大文件读写,不用写f,readlines() i_times = i[0:10] account_id = i[55:59] #字符串相同还判断不出来,必须转换成数字才行,估计数字类型的得这样做 account_id = int(account_id) user_data['account_data']['id'] = int(user_data['account_data']['id']) # print("account_id:%s" %account_id) i_time = time.mktime(time.strptime(i_times,'%Y-%m-%d')) # print(i_time) if i_time >= select_time and account_id == user_data['account_data']['id']: print(i)def logout(acc_data): """ 退出登录 :param acc_data: :return: """ q = input("\033[33;1mIf you want to quit,please input q:\033[0m").strip() if q == 'q': exit() else: print("请输入q")def interactive(acc_data): """ :param acc_data: :return: """ # 加u是为了防止中文乱码 menu = u""" --------- Oldboy Bank --------- \033[32;1m1.账号信息 2.还款 3.取款 4.转账 5.账单 6.退出 \033[0m"""#这里比较牛的是写个字典,所以数字对应的是函数的内存地址 menu_dic = { '1':account_info, '2':repay, '3':withdraw, '4':transfer, '5':pay_check, '6':logout } exit_flag = False #这个标记为有很重要,是循环的前提 while not exit_flag: print(menu) user_option = input(">>:").strip() if user_option in menu_dic: # for i in menu_dic 显示出的是字典的key menu_dic[user_option](acc_data)def run(): """ 认证字段为真,用户数据为数据库的数据,信息补全了 :return: """ #经过执行acc_login之后,如果认证成功会改变user_data中的数据,认证成功字段置为真,赋予相应账号, acc_data = auth.acc_login(user_data,access_logger) #acc_data变成了数据库数据,而且是字典的格式 if user_data['is_authenticated']: user_data['account_data'] = acc_data interactive(user_data) else: print("\033[31;1mOption does not exist!\033[0m")# run()
settlement.py:
import os
import sys,time

BARE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BARE_DIR)
from core import auth
from core import logger
from core import transaction
from core import accounts
from conf import settings

#logger()函数执行,并且返回了logger,可以进行调用warn方法进行日志写入
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['account_data'])
def repay(acc_data):
 account_data = user_data['account_data']
# for k,v in account_data.items():
 # print(k,v)
 current_balance = """
 ----------- BALANCE INFO --------
 Credit:%s
 Balance:%s
 """ %(account_data['credit'],account_data['balance'])
print(current_balance)
 back_flag = False #设置标志,输入b跳出while循环
 while not back_flag:
 repay_amount = input("\033[33;1mInput repay amount:\033[0m").strip()
if len(repay_amount) > 0 and repay_amount.isdigit():
# print('ddd 00')
 new_balance = transaction.make_transaction(trans_logger,account_data,'repay',repay_amount)
# print(new_balance)
 if new_balance:
print("""\033[42;1mNew Balance:%s\033[0m"""%(new_balance['balance']))
else:
print('\033[31;1m[%s] is not a valid amount,only accept integer!\033[0m' % repay_amount)
if repay_amount == 'b':
 back_flag = True

def withdraw(acc_data):
"""
 取款
 :param acc_data: 变成了数据库数据,而且是字典的格式
 :return:
 """
 accout_data = accounts.load_current_balance(acc_data['account_id']) #再去数据库拿一遍数据,有点多余
 current_balance = '''
 ------- BALANCE INFO -------
 Credit: %s
 Balance: %s
 '''%(accout_data['credit'],accout_data['balance'])
print(current_balance)
 back_flag = False
 while not back_flag:
 withdraw_amount = input("\033[33;1mInput withdraw amount:\033[0m").strip()
if len(withdraw_amount) > 0 and withdraw_amount.isdigit():
 new_balance = transaction.make_transaction(trans_logger,accout_data,'withdraw',withdraw_amount)
if new_balance:
print('''\033[42;1mNew Balance:%s\033[0m'''%(new_balance['balance']))
else:
print('\033[31;1m[%s] is not a valid amount,only accept integer!')
if withdraw_amount == 'b':
 back_flag = True

def transfer(acc_data):
"""

 :param acc_data: 变成了数据库数据,而且是字典的格式
 :return:
 """
 account_data = accounts.load_current_balance(acc_data['account_id'])
 current_balance = '''
 ------- BALANCE INFO -------
 Credit: %s
 Balance: %s'''%(account_data['credit'],account_data['balance'])
print(current_balance)

#钱数必须是数字
 back_flag = False
 while not back_flag:
 tranfer_amount = input("\033[33;1mInput tranfer amount:\033[0m").strip()
#只支持整数
 if len(tranfer_amount) > 0 and tranfer_amount.isdigit():

#调用transaction,传入账号信息,交易类型,交易金额,交易日志
 new_account_data = transaction.make_transaction(trans_logger,account_data,'transfer',tranfer_amount)
if new_account_data:
print("\033[42;1mNew Balance:%s\033[0m"%new_account_data['balance'])
else:
print('\033[31;1m[%s] is not a valid amount,only accept integer!')
if tranfer_amount == 'b':
 back_flag = True

def pay_check(acc_data): #账单功能
 back_flag = False
 while not back_flag:
 Time_input = input("\033[33;1mplease input time(Y-M-D),输入b退出:\033[0m").strip()
if Time_input == 'b': back_flag = True continue else: try: select_time = time.mktime(time.strptime(Time_input,'%Y-%m-%d')) # print(select_time) except Exception as ValueError: print("清输入正确的日期格式") continue # log_file = "atm_test/log/transactions.log" log_file = "%s/log/%s" % (settings.BASE_DIR, settings.LOG_TYPES["transaction"]) # print(log_file) with open(log_file,"r",encoding="utf-8") as f: for i in f: # for i in f : 是和大文件读写,不用写f,readlines() i_times = i[0:10] account_id = i[55:59] #字符串相同还判断不出来,必须转换成数字才行,估计数字类型的得这样做 account_id = int(account_id) user_data['account_data']['id'] = int(user_data['account_data']['id']) # print("account_id:%s" %account_id) i_time = time.mktime(time.strptime(i_times,'%Y-%m-%d')) # print(i_time) if i_time >= select_time and account_id == user_data['account_data']['id']: print(i)def logout(acc_data): """ 退出登录 :param acc_data: :return: """ q = input("\033[33;1mIf you want to quit,please input q:\033[0m").strip() if q == 'q': exit() else: print("请输入q")def interactive(acc_data): """ :param acc_data: :return: """ # 加u是为了防止中文乱码 menu = u""" --------- Oldboy Bank --------- \033[32;1m1.账号信息 2.还款 3.取款 4.转账 5.账单 6.退出 \033[0m"""#这里比较牛的是写个字典,所以数字对应的是函数的内存地址 menu_dic = { '1':account_info, '2':repay, '3':withdraw, '4':transfer, '5':pay_check, '6':logout } exit_flag = False #这个标记为有很重要,是循环的前提 while not exit_flag: print(menu) user_option = input(">>:").strip() if user_option in menu_dic: # for i in menu_dic 显示出的是字典的key menu_dic[user_option](acc_data)def run(): """ 认证字段为真,用户数据为数据库的数据,信息补全了 :return: """ #经过执行acc_login之后,如果认证成功会改变user_data中的数据,认证成功字段置为真,赋予相应账号, acc_data = auth.acc_login(user_data,access_logger) #acc_data变成了数据库数据,而且是字典的格式 if user_data['is_authenticated']: user_data['account_data'] = acc_data interactive(user_data) else: print("\033[31;1mOption does not exist!\033[0m")# run()
transaction.py:
import os,sys,json
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_DIR)
from conf import settings
from core import logger
from core import accounts

def make_transaction(log_obj,account_data,tran_type,amount,**others):
"""
 处理所有的交易信息
 :param log_obj:
 :param account_data: 用户账号
 :param tran_type: 交易类型
 :param amount: 交易额度
 :param others:日志等
 :return:
 """
 amount = float(amount)
if tran_type in settings.TRANSACTION_TYPE:
 interest = amount * settings.TRANSACTION_TYPE[tran_type]['interest']
 old_balance = account_data['balance']
if settings.TRANSACTION_TYPE[tran_type]['action'] == 'plus':
 new_balance = old_balance + amount + interest
elif settings.TRANSACTION_TYPE[tran_type]['action'] == 'minus':
 new_balance = old_balance - amount - interest
#check credit
 if new_balance < 0:
print("""\033[31;1mYour credit [%s] is not enough for this transaction
 \[-%s],your current balance is [%s]""" %(account_data['credit'],(amount + interest),old_balance))
return
 account_data['balance'] = new_balance
#将更改完的数据写入到json文件中
 account_data['balance'] = new_balance
 accounts.dump_account(account_data) #保存新的账目到文件中
 log_obj.info("account:%s action:%s amount:%s interest:%s" %
 (account_data['id'], tran_type, amount, interest))

return account_data
else:
print("\033[31;1mTransaction type [%s] is not exist!\033[0m" % tran_type)


shopping.py:
import os,sys,json,time
BARE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BARE_DIR)
from core import accounts
from conf import settings
from core import db_handler
from core import logger
from core import settlement
"""
购物车功能,只能用来进行购物
"""

product_list = []
#此处是对txt文档进行处理,只能处理成列表格式,如果是对JSON格式,就是load成字典格式
product = "%s/shopping/product.txt" % (settings.BASE_DIR)
with open (product,'r',encoding='utf-8') as f:
for line in f:
 line = line.strip() #去掉最后一个换行符
 index,item = line.split(":")
 product_list.append((index,item)) #以括号的格式变成一个元素,其实里边是一个个的元组
 # print(product_list)
 # print(len(product_list)) #元素的个数,以 1 开头
#enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标

#现在需要在ATM上写一个接口,提供用户输入用户名和账号,然后返回 用户的账号资金

def acc_auth(func): #函数里套函数就是高阶函数
 def deco(*args,**kwargs):
 log_obj = logger.logger('access')
 retry_count = 0
 while retry_count < 3:
 account = input("\033[32;1m登录账号:\033[0m").strip()
 password = input("\033[32;1m密码:\033[0m").strip()
 db_path = db_handler.db_handler(settings.DATABASE)
 account_file = "%s/%s.json" %(db_path,account)
if os.path.isfile(account_file):
with open(account_file,'r') as f:
 account_data = json.load(f)
if account_data['password'] == password:
 func(*args,**kwargs) #需要将认证完之后的数据传入到shopping中
 else:
print("\033[31;1mAccount ID or password is incorrect!\033[0m")
else:
print("\033[31;1mAccount [%s] does not exist!\033[0m" % account)
 retry_count +=1
 else:
 log_obj.error("shopping account [%s] too many login attempts" % account)
exit()
return deco

def print_product_list(): #可以显示小标和具体的数据
 for index,item in enumerate(product_list):
print(index,item)
@acc_auth #进行装饰器的认证
def user_shopping():
print("--------------------------"
 "--------------------------"
 "\n"
 " 欢迎进入购物菜单 "
 "\n")
 print_product_list() # 打印商品
 salary_account =settlement.settlement() #需要调用settlement模块的settlement()方法
 salary = salary_account['balance']
if salary > 0:
 shopping_list = []
while True:
 option = input("请输入你要购买的商品的编号:").strip()
if option.isdigit():
 option = int(option)
if option >= 0 and option <= len(product_list):
 p_item = product_list[option] #用户选择的商品,对应的是一个元组
 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))
 salary_account['balance'] = salary
 accounts.dump_account(salary_account) #将修改完的数据写到数据库中
 print("..........exit.........")
exit() #退出程序
 else:
exit('余额不足!')

user_shopping()