项目ATM
README.md
python解释器在此用的是3.6版本,其实哪个版本都可以。
附上:python解释器下载地址
官网:https://www.python.org/
需求分析 :
1.语言选择:python
2.技术架构
3.搭建框架
按照软件开发项目规范创建项目
4.数据库:mqsql,oracle,druid,在此不用数据库,暂时把信息写入文件
5.服务器linux
6.分析需求
1.额度15000或自定义
2.实现购物商城,买东西加入购物车 调用信用卡接口账号(暂时不做)
3.提线,手续费5%
4.每月22号出账单,每月10号为还款,过期未还,按欠款总则 万分之五 每日计息
5.多账户啊登录
6.账户转账
7.记录每月消费流水
8.还款接口(存款即充值)
9.ATM记录操作日志
10.管理接口,如添加账户,用户额度(余额)、冻结账户等...
11.用户认证装饰器
7.开发模式
1.瀑布开发(大部分公司):所有功能开发再去测试,提高效率,但是期间存在的问题或者bug多
2.敏捷开发(少数公司):开发一点交给测试测试一点,降低效率,期间存在的问题或者bug少
注意:
禅道软件测试bug
8.把需求转为功能
1.注册功能
2.登录功能
3.提现功能(5%的手续费)
4.转账功能
5.充值
6.查看余额
7.查看流水
8.日志
9.添加购物车
10.查看购物车
11.购买商品
注意:
1.面条版本
2.封装版本(功能的封装)
一、bin(start.py)
import os, sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from core import src
if __name__ == '__main__':
src.run()
二、conf(settings.py)
import os
import logging
import logging.config
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \
'[%(levelname)s][%(message)s]'
simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
ldj_log_path = os.path.join(BASE_DIR, 'log')
if not os.path.exists(ldj_log_path):
os.mkdir(ldj_log_path)
ldj_log_file_path = os.path.join(ldj_log_path, 'log.log')
LOGGING_DIC = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'standard': {
'format': standard_format
},
'simple': {
'format': simple_format
},
},
'filters': {},
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'simple'
},
'default': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler',
'formatter': 'standard',
'filename': ldj_log_file_path,
'maxBytes': 1024 * 1024 * 50,
'backupCount': 5,
'encoding': 'utf-8',
},
},
'loggers': {
'': {
'handlers': ['default', 'console'],
'level': 'DEBUG',
'propagate': True,
},
},
}
三、core(src.py)
import datetime
import hashlib
import json
import os
import time
from conf import settings
from lib import common
from interface import user
from interface import bank
from db import db_handler
'''定义一个全局变量'''
ldj_user_data = {
'is_login': False,
'username': None
}
def register():
'''
1.用户名和密码
2.用户名和密码的存储位置(1.一个文件存多个用户 2.一个文件存一个用户)
3.一般情况下,用户名不允许重复,验证用户名是否存在
4.记录一下日志
:return:
'''
print('开始注册~~~')
while True:
ldj_username = input('输入用户名:').strip()
ldj_password = input('输入密码:').strip()
if user.get_userinfo_interface(ldj_username):
print('对不起,用户{}已经存在'.format(ldj_username))
continue
user.register(ldj_username, ldj_password)
ctime = datetime.datetime.today()
ldj_logger = common.get_logger()
ldj_logger.debug('{}在{}注册了账号'.format(ldj_username, ctime))
print(f'{ldj_username}注册成功')
break
def login():
'''
这是登录功能...
:param username: 需要接收一个用户名
:param password: 需要接收一个密码,至少8位(数字、字母等的组合)
:return: 返回一个布尔值 True: False:
'''
print('开始登录~~~')
count = 0
while True:
ldj_username = input('输入用户名:').strip()
ldj_password = input('输入密码:').strip()
ldj_user_dict = user.get_userinfo_interface(ldj_username)
if not ldj_user_dict:
print('对不起,该用户不存在')
continue
ctime = datetime.datetime.now()
ldj_logger = common.get_logger()
if ldj_user_dict.get('locked'):
print('您的账户已被锁定,请本人持身份证前往该行柜台处理或者更改密码')
cmd = input('是否修改密码y? ')
if not cmd == 'y':
break
user.set_password(ldj_user_dict)
return login()
else:
ldj_tuple_data = common.get_md5(ldj_password, ldj_user_dict.get('random_str'))
if ldj_username == ldj_user_dict.get('username') and ldj_tuple_data[0][:16] == ldj_user_dict.get('password'):
ldj_user_data['is_login'] = True
ldj_user_data['username'] = ldj_username
print('登录成功')
ldj_logger.debug(f'{ldj_username}在{ctime}登录')
break
else:
print('用户名或密码错误')
count += 1
'''密码只允许输错3次,否则锁定账户'''
if count == 3:
ldj_user_dict['locked'] = True
user.locked_user(ldj_user_dict)
print('密码输入错误3次,您的账户已被锁定,请本人持身份证前往该行柜台处理')
ldj_logger.debug('%s在%s账号被锁定' % (ldj_username, ctime))
break
@common.login_auth
def repay():
'''
需求分析:
1.账户必须是登录状态
2.用户存入多少钱,验证输入的金额合法,输入的必须是数字
3.把用户的金额加到自己的账户
4.记录一下流水(只记录跟钱相关的)
5.记录一些日志(记录有用的信息,功能是用来排错)
:return:
'''
print('存款功能开始~~~~')
while True:
ldj_save_money = input('请输入存款金额:').strip()
if not ldj_save_money.isdigit():
print('输入的金额不正确')
continue
if int(ldj_save_money) < 0:
print('输入有金额不合法,请重新输入!')
continue
ldj_user_dict = user.get_userinfo_interface(f"{ldj_user_data.get('username')}.json")
if not ldj_user_dict:
print('异常错误')
continue
ldj_user_dict['account'] += int(ldj_save_money)
bank.repay(ldj_user_dict, ldj_save_money)
print('存款%s成功,开始潇洒潇洒' % ldj_save_money)
break
@common.login_auth
def withdraw():
'''
需求分析:
1.账户必须是登录状态
2.输入要提现的金额,验证合法性,数字
3.提现需要%5手续费
4.把账户中的金额减去提现金额和手续费,判断金额是否够,否则不能提
5.记录流水
6.记录日志
:return:
'''
print('提现功能开始~~')
while True:
ldj_get_money = input('请输入存款金额:').strip()
if not ldj_get_money.isdigit():
print('输入的金额不正确')
continue
if int(ldj_get_money) < 0:
print('输入有金额不合法,请重新输入!')
continue
ldj_user_dict = user.get_userinfo_interface(f'{ldj_user_data.get("username")}.json')
if not ldj_user_dict:
print('系统异常,请再次常识')
continue
if ldj_user_dict.get('account') >= int(ldj_get_money) * (1 + 0.005):
ldj_user_dict['account'] -= int(ldj_get_money) * (1 + 0.005)
bank.withdraw(ldj_user_dict, ldj_get_money)
print('提现{}成功'.format(ldj_get_money))
cmd = input('是否继续提现y?')
if not cmd == 'y':
break
continue
else:
print('对不起,您的余额不足')
@common.login_auth
def transfer():
'''
需求分析:
1.账户必须是登录状态,选择的用户必须存在
2.用户存入多少钱,验证输入的金额合法,输入的必须是数字
3.验证额度是否充足
4.把自己的额度减掉,把对方的额度加上
5.保存用户信息,双方用户
6.记录流水和日志,双方用户
:return:
'''
print('转账功能开始~~')
while True:
ldj_to_username = input('请输入要转账的用户名:').strip()
ldj_to_user_dict = user.get_userinfo_interface(ldj_to_username)
if not ldj_to_user_dict:
print('您输入的用户名不存在,请核查!')
continue
ldj_to_transfer_money = input('请输入转账金额:').strip()
if not ldj_to_transfer_money.isdigit():
print('输入的金额不正确')
continue
ldj_from_user_dict = user.get_userinfo_interface(ldj_user_data.get('username'))
if ldj_from_user_dict.get('account') >= int(ldj_to_transfer_money):
ldj_from_user_dict['account'] -= int(ldj_to_transfer_money)
bank.transfer()
print('转账成功,您可以飘了')
else:
print('对不起,您的余额不足,请核实')
cmd = input('是否继续转账y?').strip()
if not cmd == 'y':
break
continue
@common.login_auth
def check_money():
ldj_check_money_path = os.path.join(settings.BASE_DIR, 'db', f'{ldj_user_data.get("username")}.json')
if not os.path.isfile(ldj_check_money_path):
print('对不起,获取信息失败')
return
with open(ldj_check_money_path, 'r', encoding='utf8') as rfile:
ldj_check_money_dict = json.load(rfile)
ldj_bal_money = ldj_check_money_dict.get('account')
print('您当前的余额是:' + str(ldj_bal_money))
@common.login_auth
def check_log():
look_log_file = os.path.join(settings.BASE_DIR, 'log', 'log.log')
if not os.path.isfile(look_log_file):
print('日志文件不存在')
return
with open(look_log_file, 'r', encoding='utf8') as rf:
logs = rf.readlines()
for item in logs:
print(item)
@common.login_auth
def check_flow():
ldj_check_flow_path = os.path.join(settings.BASE_DIR, 'db', f'{ldj_user_data.get("username")}.json')
if not os.path.isfile(ldj_check_flow_path):
print('对不起,获取信息失败')
return
with open(ldj_check_flow_path, 'r', encoding='utf8') as rfile:
ldj_check_flow_dict = json.load(rfile)
ldj_check_flow = ldj_check_flow_dict.get('flow')
for item in ldj_check_flow:
print(item)
@common.login_auth
def add_car():
"""
需求分析:
1. 先有商品列表,展示出来
1. coffee 1000
2. computer 2000
3. iPhone 3000
4. bread 1000
5. milk 2000
2. 让用户选择商品编号,把商品信息存起来,
shopping_cart = {"coffee":{"price":"10000", "count":2}, "computer":{"price":"20000", "count":3}}
3. 把用户选择的商品信息添加到个人信息中
4. 记录日志
:return:
"""
print('添加购物车开始~~~')
ldj_shopping_cart = {}
while True:
ldj_goods_list = [
['coffee', 1000],
['computer', 2000],
['iPhone', 3000],
['bread', 1000],
['milk', 2000],
]
for i, j in enumerate(ldj_goods_list):
print(i + 1, j[0], j[1])
cmd = input('请输入商品的编号(输入q则添加商品到文件):').strip()
if cmd.isdigit():
cmd = int(cmd)
if cmd in range(1, len(ldj_goods_list) + 1):
ldj_goods_name = ldj_goods_list[cmd - 1][0]
ldj_goods_price = ldj_goods_list[cmd - 1][1]
if ldj_goods_name not in ldj_shopping_cart:
ldj_shopping_cart[ldj_goods_name] = {'price': ldj_goods_price, 'count': 1}
else:
ldj_shopping_cart[ldj_goods_name]['count'] += 1
else:
print('您输入的商品编号不在范围内')
elif cmd == 'q':
if ldj_shopping_cart:
ldj_shopping_cart_dict = user.get_userinfo_interface(ldj_user_data.get("username"))
ldj_shopping_cart_dict['shopping_cart'] = ldj_shopping_cart
db_handler.update(ldj_shopping_cart_dict)
print('商品已添加到购物车')
break
else:
print('输入的内容不合法')
@common.login_auth
def check_car():
ldj_check_car_dict = user.get_userinfo_interface(ldj_user_data.get("username"))
shop_cars = ldj_check_car_dict['shopping_cart']
print('显示购物车信息~~~~')
for index, key in enumerate(shop_cars):
print(key, shop_cars[key])
@common.login_auth
def buy_shopping():
total_price = 0
select_shopping = []
while True:
shopping_dict = user.get_userinfo_interface(user_data_dict.get('username'))
goods_carts = shopping_dict.get('shopping_cart')
for i, goods_cart in enumerate(goods_carts.keys()):
print(i+1, goods_cart, goods_carts[goods_cart]['price'])
res = input('请输入要购买的商品序号,多个商品用","分隔:').split(',')
res_list = list(res)
for choice in res_list:
if not choice.isdigit():
print('您的输入不合法')
continue
choice = int(choice)
if choice not in range(1, len(goods_carts) + 1):
print('超出范围。请重新输入')
continue
goods_name = list(goods_carts.keys())
price = goods_carts[goods_name[choice - 1]]['price']
select_shopping.append(goods_name[choice - 1])
total_price += price
if shopping_dict['account'] < total_price:
print('余额不足!')
break
logger = common.get_logger()
ctime = datetime.datetime.today()
shopping_dict['account'] -= total_price
shopping_dict['flow'].append(f'{shopping_dict.get("username")}在{ctime}购买了商品,'
f'现在账户余额是{shopping_dict.get("account")}元人民币')
logger.debug(f'{shopping_dict.get("username")}在{ctime}购买了商品,还有{shopping_dict.get("account")}元人民币')
for item in select_shopping:
cmd = input('是否清空购物车y?').strip()
if not cmd == 'y':
break
del shopping_dict['shopping_cart'][item]
logger.debug(f'{shopping_dict.get("username")}在{ctime}清空了购物车')
db_handler.update(shopping_dict)
cmd = input('是否继续购买商品y?').strip()
if not cmd == 'y':
break
func_dict = {
'1': register,
'2': login,
'3': repay,
'4': withdraw,
'5': transfer,
'6': check_money,
'7': check_log,
'8': check_flow,
'9': add_car,
'10': check_car,
'11': buy_shopping,
}
def run():
while True:
print('''
*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
1.注册功能
2.登录功能
3.存款功能
4.提现功能(0.5%的手续费)
5.转账功能
6.查看余额
7.查看日志
8.查看流水
9.添加购物车
10.查看购物车
11.购买商品
*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
''')
choice = input('请输入您选择(q:结束程序)的功能:').strip()
if not choice:
continue
if choice in func_dict:
func_dict.get(choice)()
elif choice == 'q':
exit(0)
else:
print('对不起,您的输入有误!!!')
四、db(db_handler.py与xxx.json/ xxx.txt)
'''
一般不用来处理跟数据相关的逻辑
'''
import json
import os
from conf import settings
def update(user_dict):
file_path = os.path.join(settings.BASE_DIR, 'db', f'{user_dict.get("username")}.json')
with open(file_path, 'w', encoding='utf8') as wf:
json.dump(user_dict, wf, indent=3, ensure_ascii=False)
五、lib(common.py)
import hashlib
import random
from core import src
def get_code(n):
code = ''
for item in range(n):
random_int = str(random.randint(0, 9))
chr_upper = chr(random.randint(65, 90))
chr_lower = chr(random.randint(97, 122))
temp = random.choice([random_int, chr_lower, chr_upper])
code += temp
return code
def get_md5(password, random_str=None):
'''
random_str=None是为了注册的时候不用传值,登录需要传值
:param password: 被加密的明文密码
:param random_str: 加密需要的盐
:param step 1: 就用随机生成的盐 2:就不用随机生成的盐 但是都需要将值传过来
:return:
'''
if not random_str:
random_str = get_code(5)
m = hashlib.md5()
m.update((password + random_str).encode('utf8'))
return m.hexdigest(), random_str
def login_auth(func):
def auth(*args, **kwargs):
'''
正常执行函数之前加入的功能:验证是否登录了,其实就是验证 ldj_user_data里面的 is_login 是否为 True
若为True,说明已经登录了,正常执行函数。否则没有登录,跳到登录页面
:param *args:打包参数,拆分参数
:param **kwargs:打包参数,拆分参数
:return:func函数返回的值
'''
if src.ldj_user_data.get('is_login'):
res = func(*args, **kwargs)
return res
else:
src.login()
return auth
六、log(log.log)
...
七、interface(user.py, bank.py, shopping.py)
一、user.py
import datetime
import json
import os
import hashlib
from lib import common
from db import db_handler
from conf import settings
from core import src
def get_userinfo_interface(username):
'''
判断用户是否存在?
1.若用户存在,则返回True
2.若不存在,返回False
:param username:
:return:
'''
file_path = os.path.join(settings.BASE_DIR, 'db', '{}.json'.format(username))
try:
with open(file_path, 'r', encoding='utf8') as rf:
return json.load(rf)
except FileNotFoundError:
return False
def register(username, password):
tuple_data = common.get_md5(password)
user_dict = {
'username': username,
'password': tuple_data[0][:16],
'random_str': tuple_data[1],
'account': 15000,
'locked': False,
'flow': [],
'shopping_cart': {}
}
db_handler.update(user_dict)
def locked_user(user_dict):
'''
在此只写跟用户锁定相关的功能
:param user_dict:
:return:
'''
db_handler.update(user_dict)
def set_password(user_dict):
print('更改密码开始~~~~')
if not user_dict:
print('系统异常,请核查')
return
password = input('请输入重置密码:').strip()
m = hashlib.md5()
random_str = common.get_code(5)
m.update((password + random_str).encode('utf8'))
ldj_set_pwd = m.hexdigest()[:16]
user_dict['password'] = ldj_set_pwd
user_dict['random_str'] = random_str
user_dict['locked'] = False
db_handler.update(user_dict)
logger = common.get_logger()
ctime = datetime.datetime.now()
logger.debug('%s在%s修改了密码' % (user_dict.get('username'), ctime))
二、bank.py
'''
这个功能写的是跟银行相关的功能
'''
import datetime
import time
from lib import common
from db import db_handler
from interface import user
def repay(user_dict, save_money):
ctime = datetime.datetime.now()
ldj_logger = common.get_logger()
ldj_logger.debug('%s在%s存款了%s元' % (user_dict.get('username'), ctime, save_money))
user_dict['flow'].append('%s在%s存款了%s元,现在余额%s元' % (user_dict.get('username'),
ctime, save_money, user_dict['account']))
db_handler.update(user_dict)
def withdraw(user_dict, get_money):
ldj_logger = common.get_logger()
ctime = datetime.datetime.today()
user_dict['flow'].append(f"{user_dict.get('username')}在{ctime}提现了{get_money}元,现在余额{user_dict['account']}")
ldj_logger.debug('{0}在{1}提现了{2}元人民币,余额{3}元人民币'.format(user_dict.get('username'),
ctime, get_money, user_dict['account']))
db_handler.update(user_dict)
def transfer(from_user_dict, to_username, transfer_money):
ldj_logger = common.get_logger()
ctime = time.strftime('%Y-%m-%d %H:%M:%S')
from_user_dict['account'] -= int(transfer_money)
from_user_dict['flow'].append('{}在{}转账了{}元给{},现在总金额{}'.format(from_user_dict.get('username'), ctime,
transfer_money, to_username,
from_user_dict['account']))
ldj_logger.debug('%s在%s转账%s给%s' % (from_user_dict.get('username'), ctime, transfer_money, to_username))
db_handler.update(from_user_dict)
to_user_dict = user.get_userinfo_interface(to_username)
to_user_dict['account'] += int(transfer_money)
to_user_dict['flow'].append('{}在{}收到了{}元来自{}的转账,现在总金额{}'.format(to_user_dict.get('username'),
ctime, transfer_money,
from_user_dict.get('username'),
to_user_dict['account']))
db_handler.update(to_user_dict)
ldj_logger.debug('{}在{}收到了{}元来自{}的转账,现在总金额{}'.format(to_user_dict.get('username'), ctime, transfer_money,
from_user_dict.get('username'), to_user_dict['account']))
三、shopping.py
...