ATM+購物車

drrug發表於2020-12-18

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)

相關文章