ATM模拟实现新手练手小项目

ATM模拟实现新手练手小项目

文件目录:
ATM机模拟实现(一级目录)
​		——bin(二级目录)
​			——start.py
​		——conf(二级目录)
​			——settings.py
​		——core(二级目录)
​			——src.py
​		——db(二级目录)
​			——xxx.json
​		——lib(二级目录)
​			——common.py
​		——log(二级目录)
​			——xxx.log
​		——README(二级目录)

README

声明:这个项目是我学习函数这一块的时候练手写的,适合的人群也是一些刚刚新手,老鸟们求绕路。

这个小项目涉及的知识点:函数,模块,规划化目录,装饰器,日志,文件操作,python的几种数据类型。(具体的思路我会在文件中详细说明)

项目需求:用**规范化项目目录**的格式模拟一个ATM系统。
项目功能:

1. 登录(可支持多个账户(非同时)登录)。
2. 注册。
3. 查看余额。
4. 存钱。
5. 转账(给其他用户转钱)。
6. 查看账户流水。
7. 退出

提供的思路:ATM直译就是取款机,但是咱们是模拟一个取款机,此取款机可以完成实现存钱,转账,查看余额,以及查看账户流水等功能。

要求以及分值分配:
1. 利用装饰器完成登录验证功能(3,4,5,6功能需要验证)。

2. 登录功能要求:用户名、密码(密码需要md5加密)从文件中读取,进行三次验证,验证不成功则退出整个程序。

3. 注册功能要求:
   - 用户名要求:只能含有字母数字不能含有特殊字符并且要确保唯一性。
   - 密码的要求:长度在6与14个字符之间,密文存储。
   - 初始钱数:money: 0.
   - **注意**:每个用户的以上信息通过字典以及json模块,以 用户名.json的形式存储,用户的json文件存储在db文件夹中。

4. 查看余额功能要求:
   用户登录成功之后,选择此功能即可显示账户余额,并且将每次查看记录通过日志的方式记录到用户日志中(用户日志文件建议为:用户名.log)。

5. 存钱功能要求:
   用户通过输入存储的钱数,然后将存储的钱累加到用户名.json那个json文件的字典中,并且将每次存钱记录通过日志的方式记录到用户日志中(用户日志文件建议为:用户名.log)。

6. 转账功能要求:
   用户通过输入对方用户名以及转账钱数完成给对方转账功能。
        - 要检测输入的对方用户账户是否存在。
        - 要检测你账户余额是否够用。
         - 将每次转账记录通过日志的方式记录到用户日志中(用户日志文件建议为:用户名.log)。

7. 查看流水要求:
   用户通过选择此功能将用户专属的log打印出来。


文件目录说明:
bin:这个目录一般存放的是启动文件,程序的入口就是整个文件。
conf:这个目录一般存放的是配置文件,是一些不变的常量和路径等。
core:这个是项目的核心目录,下面的文件执行主业务功能,所以功能函数都写在这里面。
db:这个就是数据库目录,就是存放我们的数据的。
lib:这个是公用目录,它下面的文件写的代码是可以被重复使用的。
log:这个看名字就知道事日志目录,里面存放的肯定也是我们的日志文件。

bin

#__*__coding:utf-8__*__
"""
时间:2019/8/4
作者:pl
功能:模拟一个ATM系统
"""
import os
import sys

#这两行代码就是把当前目录上两级的目录加载到内存,不然我们就不能在平行文件之间调用py文件了。
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
sys.path.append(BASE_DIR)

#这个就是启动文件,启动只指向了core.src文件下的run()函数
from core.src import run
if __name__ == '__main__':
   run()

conf

#__*__coding:utf-8__*__
"""
时间:2019/8/4
作者:pl
功能:模拟一个ATM系统
"""
import os
import sys

#配置的静态路径
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
DB_PATH = os.path.join(BASE_DIR,'db1')

core

#__*__coding:utf-8__*__
"""
时间:2019/8/4
作者:pl
功能:模拟一个ATM系统
"""
import os
import tabulate
import hashlib
import json

from conf.settings import JSON_PATH
from conf import settings
from conf.settings import DB_PATH
from lib.common import wrapper,get_logger

#定义的一个全局的字典,login_flag记录登录的状态,username记录的是登录成功的用户名
dic_login_flag = {"login_flag":False,
                  "username":None,
                  }

def login():
	"""
	登录功能
	:return:
	"""
	count = 1
	list_db_userinfo = os.listdir(DB_PATH)
	flag = True
	#控制循环,进入登录,成功或者失败3次就退出循环
	while flag:
		#3次失败,退出登录系统回到ATM主界面
		if count > 3:
			print ("您已经3次登录失败,自动退出系统!")
			flag = False
			return False
		#进入登录系统
		print ("*****欢迎进入ATM登录系统界面*****")
		log_username = input("username:")
		#判断该用户名是否存在
		if log_username+'.json' in list_db_userinfo:
			db_userinnfo = os.path.join(DB_PATH,log_username+'.json')
			dic_info = {}
			with open(db_userinnfo,'r',encoding='utf-8') as fp:
				dic_info = json.load(fp)
			log_password = input("password:")
			md5 = hashlib.md5()
			md5.update(log_password.encode('utf-8'))
			#判断用户名和密码是否正确
			if log_username == dic_info["username"] and md5.hexdigest() == dic_info["password"]:
				print (f"欢迎{log_username}登录成功!")
				dic_login_flag["login_flag"] = True
				dic_login_flag["username"] = log_username
				flag = False
			else:
				print ("密码错误!")
				count += 1
		else:
			print ("该用户不存在。")
			count += 1

def register():
	"""
	注册用户
	:return:
	"""
	#查看文件夹下所有的文件名
	list_db_userinfo = os.listdir(DB_PATH)
	while True:
		print("*****欢迎进入ATM注册系统界面*****")
		print ("温馨提示:用户名只能含有字母数字不能含有特殊字符,密码长度在6与14个字符之间。")
		reg_username = input("username:")
		reg_password = input("password:")
		#判断输入的用户名是否存在
		if reg_username+'.json' not in list_db_userinfo:
			if reg_username.isalnum() == True  and 5 < reg_password.__len__() <15:
				md5 = hashlib.md5()
				md5.update(reg_password.encode('utf-8'))
				dic_userinfo = {"username":reg_username,"password":md5.hexdigest(),"money":0}
				userinfo_path = os.path.join(DB_PATH,reg_username+'.json')
				#将序列化数据写进db下对应自己用户名文件里面
				with open(userinfo_path,'w',encoding='utf-8') as fp:
					json.dump(dic_userinfo,fp)
				print ("注册成功!")
				break
			else:
				print("您输入的用户名或密码不规范,请按照提示注册。")
		else:
			print ("该用户已经存在,请重新注册。")

@wrapper
def see_balane():
	"""
	查看余额
	:return:
	"""
	username_path = os.path.join(DB_PATH,dic_login_flag["username"]+".json")
	with open(username_path,'r',encoding='utf-8') as fp:
		dic_info = json.load(fp)
	print ("*******************")
	print (f"你的用户余额为:\n{dic_info['money']}")
	print("*******************")
	#把查看的日志写进log下的日志文件夹,但是我写进了db里面。
	logger = get_logger(dic_login_flag["username"])
	logger.info('查看账户余额')  # 记录该文件的运行状态

@wrapper
def save_money():
	"""
	存钱
	:return:
	"""
	dic_info = os.path.join(DB_PATH,dic_login_flag["username"]+'.json')
	money = input("请输入你存入的金额:")
	if money.isdecimal():
		with open(dic_info,'r',encoding='utf-8') as fp:
			dic = json.load(fp)
		dic["money"] = int(money) +int(dic["money"])
		with open(dic_info,'w',encoding='utf-8') as f:
			json.dump(dic,f)
		print ("存钱成功!")
		logger = get_logger(dic_login_flag["username"])
		logger.info(f'存入账户{money}¥')  # 记录该文件的运行状态


@wrapper
def transfer_accounts():
	"""
	转账
	:return:
	"""
	passive_user = input("请输入被转账方的用户名:")
	print("*******************")
	list_json = os.listdir(DB_PATH)
	own_json = os.path.join(DB_PATH,dic_login_flag["username"]+'.json')
	#被转账的用户是否存在,同时被转账的用户不应该自己
	if passive_user+'.json' in list_json and passive_user != dic_login_flag["username"]:
		passive_path = os.path.join(DB_PATH,passive_user+'.json')
		with open(own_json,'r',encoding='utf-8') as fp:
			dic = json.load(fp)
		while True:
			turn_money = input("输入转账金额:")
			if turn_money.isdecimal():
				#自己的前是否够
				if int(turn_money) <= int(dic["money"]):
					count_money = 0
					#把钱加到对方账户同时减少自己账户的钱
					with open(passive_path,'r',encoding='utf-8') as f:
						passive_dic = json.load(f)
						count_money = int(passive_dic["money"]) +int(turn_money)
						passive_dic["money"] = count_money
					with open(passive_path, 'w', encoding='utf-8') as f1:
						json.dump(passive_dic,f1)
					with open(own_json,'w',encoding='utf-8') as f2:
						dic["money"] = int(dic["money"]) - int(turn_money)
						# print (dic)
						json.dump(dic,f2)
					print("转账成功。")
					#记录日志
					logger = get_logger(dic_login_flag["username"])
					logger.info(f'向{passive_user}成功转入{turn_money}¥')  # 记录该文件的运行状态
					break
				else:
					print ("金额不足。")
			else:
				print ("输入的字符未识别。")
	else:
		print ("被转账的用户不存在!")

@wrapper
def pipeline_record():
	"""
	查看用户流水
	:return:
	"""
	log_path = os.path.join(DB_PATH,dic_login_flag["username"]+'.log')
	f =  open(log_path,'r',encoding='utf-8')
	print (f"*****{dic_login_flag['username']}的日志记录*****")
	for i in f:
		print (i)
	print ("********************")


def logout():
	"""
	退出
	:return:
	"""
	return False

def menu():
	menu_bar = [[1, '登录'], [2, '注册'], [3, '查看余额'], [4, '存钱'], [5, '转账'], [6, '查看账户流水'], [7, '退出']]
	print("--------欢迎进入ATM机主界面-------")
	print(format(tabulate.tabulate(menu_bar, headers=["id", "功能"], tablefmt="grid")))
	print("****************************")

def run():
	"""
	主逻辑函数
	:return:
	"""
	#功能映射,将函数名和数字之间做映射封装到字典中
	dic_func = dict(enumerate([login,register,see_balane, save_money, transfer_accounts, pipeline_record, logout], 1))
	#进入循环,也就是进入ATM的页面选择系统
	while True:
		menu()
		user_select = input("请输入你的选项(id):")
		if user_select.isdecimal():
			if int(user_select) in dic_func.keys():
				if int(user_select) in [1,2,7]:
					# re接收执行的函数的返回值
					re = dic_func[int(user_select)]()
				#登录成功才能进入以下的几个函数实现的功能
				else:
					#re接收执行的函数的返回值
					re = dic_func[int(user_select)](login,dic_login_flag)
					#如果返回False就退出循环,也就是退出程序
				if re == False:
					break
			else:
				print ("您输入的id有误,请重新输入。")
		else:
			print ("您输入的id有误,请重新输入。")

db

xxx.json
xxx.json

lib

#__*__coding:utf-8__*__
"""
时间:2019/8/4
作者:pl
功能:模拟一个ATM系统
"""
from logging import config
from conf.settings import DB_PATH
import os
import logging

def wrapper(f):
	"""
	装饰器,在登录某些功能的时候需要先进行登录操作
	:param f: 执行的功能函数
	:return: 返回功能函数的返回值
	"""
	def inner(login,dic_login_flag):
		#判断传入的标志位是否登录
		if dic_login_flag["login_flag"] == False:
			login()
			#登录自动返回
			if dic_login_flag["login_flag"] == True:
				ret = f()
				return ret
		#没有登录就执行登录
		else:
			ret = f()
			return ret
	return inner



def get_logger(username):
	"""
	功能介绍:这个函数实现的就是日志的记录功能,这个是日志的标准化模板。
	具体的介绍请访问我的blog查看:https://www.chpl.top
	:param username:
	:return:
	"""
	# 定义三种日志输出格式 开始

	# standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \
	#                   '[%(levelname)s][%(message)s]' #其中name为getlogger指定的名字

	simple_format = f'{username}在 %(asctime)s %(message)s'

	# id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s'

	# # log文件的全路径
	# logfile_path = log_path

	# log配置字典
	LOGGING_DIC = {'version': 1, 'disable_existing_loggers': False, 'formatters': {# 'standard': {
		#     'format': standard_format
		# },
		'simple': {'format': simple_format}, }, 'filters': {}, 'handlers': {# 打印到终端的日志
		'stream': {'level': 'INFO', 'class': 'logging.StreamHandler',  # 打印到屏幕
			'formatter': 'simple'}, # 打印到文件的日志,收集info及以上的日志
		'file': {'level': 'INFO', 'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件
			'formatter': 'simple', 'filename': None,  # 日志文件
			'maxBytes': 1024 * 1024 * 1024,  # 日志大小 5M
			'backupCount': 5, 'encoding': 'utf-8',  # 日志文件的编码,再也不用担心中文log乱码了
		}, }, 'loggers': {# logging.getLogger(__name__)拿到的logger配置
		'': {# 'handlers': ['stream', 'file'],  # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
			'handlers': ['file'], 'level': 'INFO', 'propagate': True,  # 向上(更高level的logger)传递
		}, }, }

	path = os.path.join(DB_PATH, username + '.log')
	LOGGING_DIC['handlers']['file']['filename'] = path
	config.dictConfig(LOGGING_DIC)  # 导入上面定义的logging配置
	logger = logging.getLogger(__name__)  # 生成一个log实例
	return logger

log

xxx.log
xxx.log

代码下载地址:https://github.com/pl1649947109/pl_share.git

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值