ATM+購物車
目錄
1 需求
1.額度 15000或自定義 --> 註冊功能
2.實現購物商城,買東西加入 購物車,呼叫信用卡介面結賬 --> 購物功能、支付功能
3.可以提現,手續費5% --> 提現功能
4.支援多賬戶登入 --> 登入功能
5.支援賬戶間轉賬 --> 轉賬功能
6.記錄每月日常消費流水 --> 記錄流水功能
7.提供還款介面 --> 還款功能
8.ATM記錄操作日誌 --> 記錄日誌功能
9.提供管理介面,包括新增賬戶、使用者額度,凍結賬戶等 -->管理員功能
10.使用者認證用裝飾器 --> 登入認證裝飾器
2 功能
1.註冊功能
2.登入功能
3.檢視餘額
4.提現功能
5.還款功能
6.轉賬功能
7.檢視流水
8.購物功能
9.檢視購物車
10.管理員功能
3 目錄結構
ATM
-bin
-start.py
-conf
-settings.py
-db
-user_data
-ccc.py
...
-db_handler.py
-interface
-bank.py
-user.py
-shopping.py
-lib
-common.py
-log
-atm.log
-view
-src.py
4 思路及步驟
4.1 環境bin/start.py
程式的入口
4.1.1 將直譯器新增到環境變數中
import os
import sys
sys.path.append(os.path.dirname(os.path.dirname(__file__)))
4.1.2 啟動主程式
from view import src
if __name__ == '__main__':
src.run()
# 此時應該在view/src.py中定義主啟動函式run()
4.2 配置conf/settings.py
存放配置資訊
4.2.1 獲取專案根目錄路徑
import os
BASE_PATH = os.path.dirname(os.path.dirname(__file__))
4.2.2 獲取user_data目錄路徑
import os
USER_DATA_PATH = os.path.join(BASE_PATH, 'db', 'user_data')
4.2.3 獲取log目錄路徑
import os
USER_LOG = os.path.join(BASE_PATH, 'log')
4.3 資料處理db/db_handle.py
資料處理層專門用來處理資料
4.3.1 儲存使用者資訊save
import os
import json
from conf import settings
def save(username, user_dic):
user_path = os.path.join(settings.USER_DATA_PATH, f{username}.json)
with open(user_path, 'wt', encoding='utf-8') as f:
json.dump(user_dic, f, ensure_ascii=False)
f.flush() # 強制刷入硬碟
4.3.2 返回使用者資訊select
import os
import json
from conf import settings
def select(username):
user_path = os.path.join(settings.USER_DATA_PATH, f{username}.json)
if os.path.exists(user_path):
with open(user_path, 'rt', encoding='utf-8') as f:
user_dic = json.load(f)
return user_dic
4.4 檢視層view/src
使用者檢視層
4.4.1 主啟動函式run()
func_dic = {
"1": register,
"2": login,
"3": check_balance,
"4": withdrawal,
"5": repay,
"6": transfer,
"7": check_flow,
"8": shop,
"9": check_shopping_cart,
"10": admin,
"11": logout,
}
def run():
while True:
print(
=======================
1.註冊
2.登入
3.檢視餘額
4.提現
5.還款
6.轉賬
7.檢視流水
8.購物
9.檢視購物車
10.管理員
11.登出
=======================
)
choice = input("請輸入功能編號(按q退出程式):").strip()
if choice == "q":break
if choice not in func_dic:
print("請輸入正確的編號!")
continue
func_dic.get(choice)() # 輸入1相當於func_dic.get('1')()-->register()
4.4.2 功能實現
4.4.2.1 註冊功能register()
from interface import user
user_login = {
'is_login': False,
'username': None
}
def register():
while True:
if not user_login['is_login']:
username = input("請輸入註冊使用者名稱(按q退出):").strip()
if username == "q":break
password = input("請輸入密碼:").strip()
re_password = input("請再次輸入密碼:").strip()
if password == re_password:
msg = user.register_interface(username, password)
print(msg)
break
else:
print("兩次密碼不一致")
else:
print("登陸狀態不可再註冊")
break
4.4.2.2 登入功能login()
from interface import user
user_login = {
'is_login': False,
'username': None
}
def login():
count = 0
while True:
if not user_login['is_login']:
username = input("請輸入登入名(按q退出):").strip()
if username == "q":break
user_info = user.get_user_info_interface(username)
if user_info:
if not user_info['locked']:
if count < 3:
password = input("請輸入登入密碼:").strip()
if password == user_info['password']:
user_login['is_login'] = True
user_login['username'] = username
print("登陸成功")
break
else:
count += 1
print("密碼輸入錯誤")
else:
user.locked_interface(username)
print("該使用者密碼錯誤輸入次數過多,已被鎖定")
break
else:
msg = user.unlocked_interface(username)
print(msg)
else:
print("該使用者不存在")
else:
print("登入狀態下不可再次登入")
break
1、判斷使用者登入狀態,非登入狀態繼續執行程式碼,否則告訴使用者登入情況下不能再次登入
2、使用者輸入登入名,判斷輸入是否為q,非q繼續執行程式碼,否則退出
3、判斷使用者名稱是否存在,不存在繼續執行程式碼,存在則告訴使用者該使用者名稱不存在
4、判斷該使用者是否被鎖,沒有則繼續執行,被鎖則呼叫解鎖介面 等待解鎖
5、判斷密碼輸入次數,小於三次則繼續執行,否則呼叫鎖定介面 鎖定賬號
6、使用者輸入密碼,判斷密碼是否正確,正確則登入,錯誤則輸入次數加1
4.4.2.3 檢視餘額功能check_balance()
# 首先使用登入認證裝飾器判斷登陸狀態
from lib import common
from interface import bank
user_login = {
'is_login': False,
'username': None
}
@common.check_login
def check_balance():
msg = bank.check_balance_interface(user_login['username'])
print("%s的餘額是%s元" % (user_login['username'], msg))
4.4.2.4 提現功能withdrawal()
from lib import common
from interface import bank
user_login = {
'is_login': False,
'username': None
}
@common.check_login
def withdrawal():
while True:
print("""
=============================
0) 退出
1) 檢視餘額
2) 提現
=============================
""")
choice = input("請輸入指令:").strip()
if choice == "0":break
elif choice == "1":
msg = bank.check_balance_interface(user_login['username'])
print(msg)
elif choice == "2":
money = input("請輸入提現金額:").strip()
if money.isdigit:
money = int(money)
msg = bank.withdrawal_interface(user_login['username'], money)
print(msg)
break
else:
print("請輸入正確的金額")
else:
print("請輸入正確的提現指令")
1、裝飾器判斷登入狀態,未登入則執行登陸程式,登陸則繼續
2、使用者選擇功能,0->退出 1->檢視餘額 2->提現(迴圈功能)
3、0直接退出 1呼叫餘額介面
4、2讓使用者輸入提現金額,判斷是否為數字,不為數字提示重新輸入,為數字繼續
5、呼叫提現介面,列印返回值
4.4.2.5 還款功能repay()
from lib import common
from interface import bank
user_login = {
'is_login': False,
'username': None
}
@common.check_login
def repay():
while True:
choice = input("""
=============================
0) 退出
1) 查詢欠款金額
2) 還款
=============================
請輸入選項:""").strip()
if choice == "0":break
elif choice == "1":
msg = bank.check_debt_interface(user_login['username'])
print(msg)
elif choice == "2":
money = input("請輸入還款金額:").strip()
if money.isdigit():
money = int(money)
msg = bank.repay_interface(user_login['username'], money)
print(msg)
break
else:
print("請輸入正確的金額")
else:
print("請輸入正確的選項")
1、呼叫登入認證裝飾器
2、使用者選擇還款功能 0退出 1查詢欠款金額 2還款
3、1呼叫查詢欠款介面查詢
4、2使用者輸入還款金額,判斷是否為數字
5、呼叫還款介面
4.4.2.6 轉賬功能transfer()
from lib import common
from interface import bank
user_login = {
'is_login': False,
'username': None
}
@common.check_login
def transfer():
while True:
print("""
=============================
0) 退出
1) 檢視餘額
2) 轉賬
=============================
""")
choice = input("請輸入功能選項:").strip()
if choice == "0":break
elif choice == "1":
msg = bank.check_balance_interface(user_login['username'])
print(msg)
elif choice == "2":
to_username = input("請輸入被轉帳使用者名稱:").strip()
if to_username == user_login['username']:
print("不能轉賬給自己")
continue
if user.get_user_info_interface(to_username):
money = input("請輸入轉賬金額:").strip()
if money.isdigit():
money = int(money)
msg = bank.transfer_interface(user_login['username'], to_username, money)
print(msg)
break
else:
print("請輸入正確的金額數")
else:
print("被轉賬使用者不存在")
else:
print("請輸入正確的選項")
1、呼叫登入認證裝飾器
2、使用者選擇功能 0退出 1呼叫檢視餘額介面檢視餘額 2轉賬
3、2輸入被轉賬使用者名稱,判斷是否為自己
4、呼叫提取資訊介面判斷使用者是否存在,存在則繼續,否則提示使用者不存在
5、使用者輸入金額,判斷是否為數字,是繼續,否則提示輸入正確的金額
6、呼叫轉賬介面
4.4.2.7 檢視流水功能check_flow()
from lib import common
from interface import bank
user_login = {
'is_login': False,
'username': None
}
@common.check_login
def check_flow():
flow_list = bank.check_flow_interface(user_login['username'])
for line in flow_list:
print(line)
4.4.2.8 購物功能shop()
from lib import common
from interface import shopping
user_login = {
'is_login': False,
'username': None
}
@common.check_login
def shop():
good_list = [
['tesla', 10000],
['mac', 8000],
['thinkpad', 6000],
['火鍋', 580],
['燒烤', 320],
['串串', 280],
['包子', 8],
['礦泉水',5]
]
good_cart = {}
balance = bank.check_balance_interface(user_login['username'])
amount_spend = 0
while True:
for i,line in enumerate(good_list):
print("%s 商品名:%s 單價:%s" % (i, line[0], line[1]))
choice = input("請輸入商品序號(按q退出併購買):").strip()
if choice.isdigit():
choice = int(choice)
if 0 <= choice <= len(good_list)-1:
good_name = good_list[choice][0]
good_price = good_list[choice][1]
if balance >= good_price:
if good_name in good_cart:
good_cart[good_name]['count'] += 1
else:
good_cart[good_name] = {'price': good_price, 'count': 1}
amount_spend += good_price
balance -= good_price
print("%s已被加入購物車" % good_name)
print("目前購物車有:%s" % good_list)
print("餘額:%s元" % balance)
else:
print("餘額不足,剩餘%s" % balance)
else:
print("該商品尚未上架")
elif choice == "q":
while True:
choice = input("請輸入 y)結算 或 n)退出:").strip()
if choice == "y":
if amount_spend > 0:
password = input("請輸入密碼:").strip()
file_password = user.get_user_info_interface(user_login['username']['password'])
if password == file_password:
msg = shopping.buy_goods_interface(user_login['username'], good_cart, amount_spend)
print(msg)
break
else:
print("密碼錯誤")
else:
print("沒買東西結什麼賬啊")
break
elif choice == "n":
print("白加這麼多東西在購物車")
break
else:
print("請輸入正確的選項")
break
else:
print("請輸入數字")
4.4.2.9 檢視購物車功能check_shopping_cart()
from lib import common
from interface import shopping
user_login = {
'is_login': False,
'username': None
}
@common.check_login
def check_shopping_cart():
msg = shopping.check_shopping_cart_interface(user_login['username'])
print(msg)
4.4.2.10 管理員功能admin()
def admin():
pass
4.4.2.11 登出功能logout()
from lib import common
user_login = {
'is_login': False,
'username': None
}
@common.check_login
def logout():
if user_login['is_login']:
user_login['is_login'] = False
print("登出成功")
else:
print("請先登入")
4.5 公共lib/common
存放公共登陸方法,登入認證裝飾器及日誌模組
4.5.1 登陸認證裝飾器
from view import src
# 登入認證裝飾器
def check_login(func):
def inner(*args, **kwargs):
if src.user_login['is_login']:
res = func(*args, **kwargs)
return res
else:
print("暫未登入,請先登入後在使用本功能")
src.login()
return inner
4.5.2 日誌
import os
import logging.config
from conf import settings
# 日誌模組
standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \
'[%(levelname)s][%(message)s]' # 其中name為getlogger指定的名字
simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s'
# 定義日誌輸出格式 結束
logfile_dir = settings.USER_LOG # log檔案的目錄
logfile_name = 'atm.log' # log檔名
# 如果不存在定義的日誌目錄就建立一個
if not os.path.isdir(logfile_dir):
os.mkdir(logfile_dir)
# log檔案的全路徑
logfile_path = os.path.join(logfile_dir, logfile_name)
# 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'
},
# 列印到檔案的日誌,收集info及以上的日誌
'default': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler', # 儲存到檔案
'formatter': 'standard',
'filename': logfile_path, # 日誌檔案
'maxBytes': 1024*1024*5, # 日誌大小 5M
'backupCount': 5,
'encoding': 'utf-8', # 日誌檔案的編碼,再也不用擔心中文log亂碼了
},
},
'loggers': {
# logging.getLogger(__name__)拿到的logger配置
'': {
'handlers': ['default', 'console'], # 這裡把上面定義的兩個handler都加上,即log資料既寫入檔案又列印到螢幕
'level': 'DEBUG',
'propagate': True, # 向上(更高level的logger)傳遞
},
},
}
def make_log(name):
logging.config.dictConfig(LOGGING_DIC) # 匯入上面定義的logging配置
logger = logging.getLogger(name) # 生成一個log例項
return logger
4.6 介面interface
4.6.1 銀行相關bank.py
4.6.1.1 檢視餘額介面
from db import db_handler
from lib import common
logger_bank = common.make_log('bank')
# 檢視餘額介面
def check_balance_interface(username):
user_info = db_handler.select(username)
res = user_info['balance']
# 記錄日誌
logger_bank.info("%s檢視了餘額" % username)
return res
4.6.1.2 提現介面
from db import db_handler
from lib import common
logger_bank = common.make_log('bank')
# 提現介面
def withdrawal_interface(username, money):
user_info = db_handler.select(username)
if user_info['balance'] >= money*1.05:
user_info['balance'] -= money*1.05
# 檢視流水
user_info['flow'].append("%s成功提現%s元,扣除手續費%s元" % (username, money, money*0.05))
db_handler.save(username, user_info)
# 記錄日誌
logger_bank.info("%s成功提現%s元,扣除手續費%s元" % (username, money, money*0.05))
return "%s成功提現%s元,扣除手續費%s元" % (username, money, money*0.05)
else:
return "%s賬戶餘額不足" % username
4.6.1.3 查詢欠款介面
from db import db_handler
from lib import common
logger_bank = common.make_log('bank')
# 查詢欠款介面
def check_debt_interface(username):
user_info = db_handler.select(username)
res = user_info['credit'] - user_info['balance']
if res > 0:
# 記錄日誌
logger_bank.info("%s檢視了欠款" % username)
return "%s當前欠款%s元" % (username, res)
else:
return "%s當前並未欠款" % username
4.6.1.4 還款介面
from db import db_handler
from lib import common
logger_bank = common.make_log('bank')
# 還款介面
def repay_interface(username, money):
user_info = db_handler.select(username)
res = user_info['credit'] - user_info['balance']
if res >= money:
user_info['balance'] += money
# 記錄流水
user_info['flow'].append("%s成功還款%s元" % (username, money))
db_handler.save(username, user_info)
# 記錄日誌
logger_bank.info("%s成功還款%s元" % (username, money))
return "%s成功還款%s元" % (username, money)
else:
return "還款金額超過欠款額度"
4.6.1.5 轉賬介面
from db import db_handler
from lib import common
logger_bank = common.make_log('bank')
# 轉賬功能介面
def transfer_interface(username, to_username, money):
user_info = db_handler.select(username)
to_user_info = db_handler.select(to_username)
if user_info['balance'] >= money:
user_info['balance'] -= money
to_user_info['balance'] += money
# 記錄流水
user_info['flow'].append("您向%s轉賬%s元" % (to_username, money))
to_user_info['flow'].append("%s向您轉賬%s元" % (username, money))
db_handler.save(username, user_info)
db_handler.save(to_username, to_user_info)
# 記錄日誌
logger_bank.info("%s向%s轉賬%s元" % (username, to_username, money))
return "%s向%s轉賬%s元" % (username, to_username, money)
else:
return "%s賬戶餘額不足" % username
4.6.1.6 檢視流水介面
from db import db_handler
from lib import common
logger_bank = common.make_log('bank')
# 檢視流水介面
def check_flow_interface(username):
user_info = db_handler.select(username)
# 檢視流水
logger_bank.info("%s檢視了流水" % username)
return user_info['flow']
4.6.1.7 消費介面
from db import db_handle
from lib import common
logger_bank = common.make_log('bank')
def consume_interface(username, money):
user_info = db_handler.select(username)
user_info['balance'] -= money
# 記錄流水
user_info['flow'].append("%s消費了%s元" % (username, money))
db_handler.save(username, user_info)
# 記錄日誌
logger_bank.info("%s消費了%s元" % (username, money))
4.6.2 購物相關shopping.py
4.6.2.1 購買商品介面
from db import db_handler
from interface import bank
from lib import common
logger_shopping = common.make_log('shopping')
def buy_goods_interface(username, good_cart, money):
user_info = db_handler.select(username)
bank.consume_interface(username, money)
user_info['shopping_cart'] = good_cart
db_handler.save(username, user_info)
# 記錄日誌
logger_shopping.info("%s成功購買%s" % (username, good_cart))
return "%s成功購買%s" % (username, good_cart)
4.6.2.2 檢視購物車介面
from db import db_handler
from lib import common
logger_shopping = common.make_log('shopping')
def check_shopping_cart_interface(username):
user_info = db_handler.select(username)
res = user_info['shopping_cart']
# 記錄日誌
logger_shopping.info("%s檢視了購物車" % username)
return res
4.6.3 使用者相關user.py
4.6.3.1 註冊介面
from db import db_handler
from lib import common
logger_user = common.make_log('user')
# 註冊介面
def register_interface(username, password, balance=15000):
user = db_handler.select(username) # 取到user_info = user_dic
if user: # 判斷該使用者名稱是否存在
return "該使用者名稱已存在"
else:
user_dic = {
'username': username,
'password': password,
'credit': balance,
'balance': balance,
'locked': False,
'time': 0,
'flow': [],
'shopping_cart': {}
}
db_handler.save(username, user_dic)
# 記錄日誌
logger_user.info("%s註冊成功" % username)
return "%s註冊成功" % username
4.6.3.2 提取使用者資訊介面
from db import db_handler
# 提取使用者資訊
def get_user_info_interface(username):
user_info = db_handler.select(username)
return user_info
4.6.3.3 鎖定使用者介面
import time
from db import db_handler
from lib import common
logger_user = common.make_log('user')
# 鎖定使用者
def locked_interface(username):
user_info = db_handler.select(username)
user_info['locked'] = True
user_info['time'] = time.time()+20
db_handler.save(username, user_info)
# 記錄日誌
logger_user.info("%s被鎖定了" % username)
4.6.3.4 解鎖使用者介面
import time
from db import db_handler
from lib import common
logger_user = common.make_log('user')
# 解鎖使用者介面
def unlocked_interface(username):
user_info = db_handler.select(username)
last_time = user_info['time'] - time.time()
if user_info['time'] <= time.time():
user_info['locked'] = False
db_handler.save(username, user_info)
# 記錄日誌
logger_user.info("%s使用者已解鎖" % username)
return "%s使用者已經解鎖,請重新登陸" % username
else:
return "%s使用者已被鎖,剩餘鎖定時間%s" % (username, last_time)