模擬實現一個ATM + 購物商城程序

做業需求:85%
1. 用戶額度 15000或自定義
2. 實現購物商城,買東西加入 購物車,調用信用卡功能進行結帳
3. 能夠提現,手續費5%
4. 支持多帳戶登陸
5. 支持帳戶間轉帳(用戶A轉帳給用戶B,A帳戶減錢、B帳戶加錢)
6. 記錄每個月平常消費流水
7. 提供還款功能
8. ATM記錄操做日誌(使用logging模塊記錄日誌)
9. 提供管理功能,包括添加帳戶、用戶額度,凍結帳戶等。。。
10. 用戶認證用裝飾器

示例代碼 https://github.com/triaquae/py3_training/tree/master/atm
簡易流程圖:https://www.processon.com/view/link/589eb841e4b0999184934329

編碼規範需求:15%
1. 代碼規範遵照pep8 (https://python.org/dev/peps/pep-0008/)
2. 函數有相應的註釋
3. 程序有文檔說明文件(README.md參考:https://github.com/csrftoken/vueDrfDemo)
4. 程序的說明文檔必須包含的內容:程序的實現的功能、程序的啓動方式、登陸用戶信息、程序的運行效果
5. 程序設計的流程圖:
(能夠參考:https://www.processon.com/view/link/589eb841e4b0999184934329)

 

分管理員和普通用戶登錄,登錄之後界面不同,不互通.vue

管理員用戶爲2222python

其餘都是普通用戶git

起始文件是atm_server,購物車在普通用戶面板裏面,github

目錄結構爲json

首先是啓動文件app

if __name__ == "__main__":

from atm import main
main.controller() # 入口

而後是日誌保存文件
transaction# 保存交易信息的模塊
access# 保存各類操做的模塊

用戶保存文件函數

普通用戶格式this

管理員用戶格式編碼

settings模塊,獲取目錄還有一些格式:spa

 

模塊代碼

import logging
import os, time


BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # 獲取到當前程序在的目錄

DB_PATH = "%s/db/account" % BASE_DIR # 在當前程序目錄上面加上文件保存的目錄

DB_PATH_1 = "%s/db/shopping/goods.txt" % BASE_DIR # 設置購物車商品列表的目錄
LOG_LEVEL = logging.INFO # 默認級別是INFO

LOG_TYPES = { # 判斷寫進那個日誌
'transaction': 'transactions.log',
'access': 'access.log',
}

LOG_PATH = os.path.join(BASE_DIR, 'logs') # 設置路徑

LOG_FORMAT = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') # 日誌格式模板

TRANSACTION_TYPE = {
'repay': {'action': 'plus', 'interest': 0},
'withdraw': {'action': 'minus', 'interest': 0.05},
'transfer': {'action': 'minus', 'interest': 0},
'consume': {'action': 'minus', 'interest': 0},
'collect': {'action': 'plus', 'interest': 0},
'shopping': {'action': 'minus', 'interest': 0}
}

下面是正餐:

 

這個是主程序main:

 

from .auth import authenticate
from .logger import logger
from atm import logics
from atm import admin_logics


transaction_logger = logger("transaction") # 保存交易信息的模塊
access_logger = logger("access") # 保存各類操做的模塊

features = [ # 功能列表
('帳號信息', logics.view_account_info),
('取現', logics.with_draw),
('還款', logics.pay_back),
('轉帳', logics.transfer),
('購物車', logics.shopping_cart)
]
the_admin = [ # 管理員功能列表
('修改用戶', admin_logics.modify_the_account),
('添加用戶', admin_logics.create_user),
('凍結用戶', admin_logics.freeze),

]


def admin(user_obj):
"""管理員功能分發"""
while True:
for index, feature in enumerate(the_admin): # 循環打印
print(index, feature[0])
choice = input("請選擇》》》(退出exit):").strip()
if not choice:
continue # 判斷值是否等於空
if choice.isdigit():
choice = int(choice)
if 0 <= choice < len(features): # 判斷輸入值是否正確
the_admin[choice][1](user_obj, transaction_logger=transaction_logger, access_logger=access_logger)
"""傳輸數據及日誌格式"""
if choice == 'exit':
exit("拜拜")


def login(func):
def entrance():
"""ATM登錄入口"""
user_obj = {
'is_authenticated': False,
'data': None
}
"""保存登錄信息和用戶信息"""

retry_count = 0
while user_obj['is_authenticated'] is not True: # 判斷用戶是否已登錄
account = input("\033[32;1m請輸入姓名:\033[0m").strip()
password = input("\033[32;1m請輸入密碼:\033[0m").strip()
auth_data = authenticate(account, password) # 驗證函數,並把值返回給auth_data
if auth_data:
if auth_data['status'] == 1:
print("該用戶以凍結!!")
continue

user_obj['is_authenticated'] = True # 當用戶成功登錄,把登錄狀態保存下來
user_obj['data'] = auth_data # 把返回的數據保存在data裏面
access_logger.info("user %s just logged in" % user_obj["data"]["id"])
""" 調用函數,把過程寫進日誌"""
if user_obj['data']['id'] == 2222:
print('您以登錄,身份:管理員!')
admin(user_obj)
else:
print("歡迎登錄")
func(user_obj) # 調用函數

else:
print("登錄失敗")
retry_count += 1
if retry_count == 3: # 判斷是否錯誤超過三次
msg = "user %s tried wrong password reached 3 times" % account
print(msg)
access_logger.info(msg) # 寫進日誌
break
if user_obj['is_authenticated']:
func()
return entrance


@login
def controller(user_obj): # 功能分發器

while True:
for index, feature in enumerate(features): # 循環打印
print(index, feature[0])
choice = input("請選擇》》》(退出exit):").strip()
if not choice:
continue # 判斷值是否等於空
if choice.isdigit():
choice = int(choice)
if 0 <= choice < len(features): # 判斷輸入值是否正確
features[choice][1](user_obj, transaction_logger=transaction_logger, access_logger=access_logger)
"""傳輸數據及日誌格式"""
if choice == 'exit':
exit("拜拜")

 


admin_logics模塊,是管理員功能模塊: 

 

 

 
#! /usr/bin/env python
# _*_ coding: utf-8 _*_

from .auth import authenticate
import time, os, json
from conf import settings



def create_user(*args, **kwargs):
"""添加帳號"""
logger = kwargs.get("access_logger")
while True:
name = input("請輸入用戶名(q退出):").strip()
if name == "q":
return
elif not name:
print("請從新輸入")
continue
pwd = input("請輸入密碼:").strip()
if not pwd:
print("請從新輸入!")
continue
if pwd != input("請肯定密碼:").strip():
print("兩次密碼不一致!")
continue
user = authenticate(name, pwd)
if user:
print("用戶已存在")
continue
new_user = {"balance": 15000, # 設置一個模版
"expire_date": "%s-%s" % (2+int(time.strftime("%Y")), time.strftime("%m-%d")),
"enroll_date": "%s" % time.strftime("%Y-%m-%d"),
"credit": 15000,
"id": name,
"status": 0,
"pay_day": 22,
"password": pwd}
"""將用戶輸入的信息打包 存到列表中"""
account_file = os.path.join(settings.DB_PATH,"%s.json" % name) # 按照輸入的名稱建立文件夾
f = open("%s" % account_file, "w") # 寫入內容
json.dump(new_user, f)
f.close()
logger.info("TO create a user %s,password is %s" % (name, pwd))
print("建立成功")


def modify_the_account(*args, **kwargs):
"""修改帳號"""
logger = kwargs.get("access_logger")
while True:
name = input("請輸入要修改的名稱(q退出):").strip()
if name == "q":
return
elif not name:
print("請從新輸入")
continue
account_file = os.path.join(settings.DB_PATH,"%s.json" % name)
if os.path.isfile(account_file): # 判斷這個文件是否存在
f = open(account_file)
data = json.load(f)
f.close()
print("名稱".center(50, '-'))
for k, v in data.items():
print("%15s. %s" % (k, v))
print("END".center(50, '-'))
else:
print("帳號不存在,請從新選擇")
continue
account = input("請輸入要修改的值(q退出):").strip()
if not account:
continue # 判斷值是否等於空
if account in data:
print("%s - %s" % (account, data[account]))
data_1 = input("請輸入值:").strip()
data[account] = data_1
account_file_1 = os.path.join(settings.DB_PATH,"%s.json"% data["id"])
f = open("%s.new" % account_file_1, "w")
json.dump(data, f)
f.close()
os.remove(account_file)
os.rename("%s.new" % account_file_1, account_file_1)
logger.info("%s modified %s act as %s" % (name, account, data_1))
print("以修改")
elif account == "q":
return

else:
print("輸入錯誤,請從新輸入!")
continue


def freeze(*args, **kwargs):
logger = kwargs.get("access_logger")
while True:
name = input("請輸入要凍結的用戶(q退出):")
if name == "q":
return
elif not name:
print("請從新輸入!")
continue
account_file = os.path.join(settings.DB_PATH,"%s.json"% name)
if os.path.isfile(account_file): # 判斷這個文件是否存在
f = open(account_file)
data = json.load(f)
f.close()
data["status"] = 1
account_file_1 = os.path.join(settings.DB_PATH,"%s.json" % data["id"])
f = open("%s.new" % account_file_1, "w")
json.dump(data, f)
f.close()
os.remove(account_file)
os.rename("%s.new" % account_file_1, account_file_1)
logger.info(" %s ln order to freeze " % name)
print("以凍結!")

else:
print("帳號不存在,請從新選擇")

 

 

 

 

logics是普通用戶功能模塊,裏面有購物車程序:

 
from .transaction import make_transaction
from .db_handler import load_account_data


def view_account_info(account_data, *args, **kwargs):
trans_logger = kwargs.get("transaction_logger") # 拿取logger
print("帳號額度".center(50, '-'))
for k, v in account_data['data'].items():
if k not in ('password',): # 當數值不是密碼時打印出來
print("%25s: %s" % (k, v))
print("END".center(50, '-'))


def with_draw(account_data, *args, **kwargs):
"""取款模塊"""
trans_logger = kwargs.get("transaction_logger")
current_balance = '''----------可用餘額----------
Credit : %s
Balance : %s
''' % (account_data['data']['credit'], account_data['data']['balance'])
print(current_balance)
back_flag = False # 記錄當前狀態,輸入exit退出
while not back_flag:
withdraw_amount = input("\033[33;1m請輸入金額:\033[0m").strip()
if len(withdraw_amount) > 0 and withdraw_amount.isdigit(): # 判斷輸入數值
withdraw_amount = int(withdraw_amount) # 轉換輸入數值
if (account_data['data']['balance'] / 2) >= withdraw_amount: # 判斷是否大於餘額的一半
transaction_result = make_transaction(trans_logger, account_data, 'withdraw', withdraw_amount)
if transaction_result['status'] == 0:
print('''\033[42;1mNew Balance:%s\033[0m''' % (account_data['data']['balance']))
else:
print(transaction_result)
else:
print("餘額不足,可提現餘額爲%s" % (int(account_data['data']['balance']/2)))
if withdraw_amount == "exit":
back_flag = True


def pay_back(account_data, *args, **kwargs):
"""還款模塊"""
trans_logger = kwargs.get("transaction_logger") # 日誌
current_balabce = '''-------------- BALANCE INFO--------------
Credit : %s
Balance : %s
''' % (account_data['data']['credit'], account_data['data']['balance'])
print(current_balabce) # 打印額度
pay_amount = input("\033[33;1m請輸入金額:\033[0m").strip() # 格式輸入值
if len(pay_amount) > 0 and pay_amount.isdigit():
pay_amount = int(pay_amount)
transaction_result = make_transaction(trans_logger, account_data, 'repay', pay_amount)
if transaction_result['status'] == 0:
print('''\033[42;1mNew Balance:%s\033[0m''' % (account_data['data']['balance']))
else:
print(transaction_result)


def transfer(account_data, *args, **kwargs):
"""轉帳模塊"""
account_1 = input("\033[32;1m請輸入轉帳用戶姓名:\033[0m").strip()
account_data_1 = load_account_data(account_1) # 調用函數,返回文件裏面的值
if account_data_1['status'] == 0: # 判斷是否返回了值
trans_logger = kwargs.get("transaction_logger")
current_balance = '''----------可用餘額----------
Credit : %s
Balance : %s
''' % (account_data['data']['credit'], account_data['data']['balance'])
print(current_balance)
back_flag = False # 記錄當前狀態,輸入exit退出
while not back_flag:
withdraw_amount = input("\033[33;1m請輸入金額:\033[0m").strip()
if len(withdraw_amount) > 0 and withdraw_amount.isdigit(): # 判斷輸入數值
withdraw_amount = int(withdraw_amount) # 轉換輸入數值
transaction_result = make_transaction(trans_logger, account_data, 'transfer', withdraw_amount)
"""調用模塊進行扣錢"""
if transaction_result['status'] == 0: # 判斷是否成功
make_transaction(trans_logger, account_data_1, 'collect', withdraw_amount)
"""調用模塊進行加錢"""
print('''\033[42;1m轉帳成功,餘額還有:%s\033[0m''' % (account_data['data']['balance']))
else:
print(transaction_result)
elif withdraw_amount == "exit":
back_flag = True
else:
print("用戶錯誤")


def shopping_cart(account_data, *args, **kwargs):
trans_logger = kwargs.get("transaction_logger")
goods = [{"name": "電腦", "price": 1999},
{"name": "鼠標", "price": 10},
{"name": "遊艇", "price": 20},
{"name": "美女", "price": 998}, ]
list_1 = []
list_2 = []
list_3 = []
for list_0 in goods:
list_1.append([list_0["name"], list_0["price"]])
dd = account_data['data']['balance']

while True:
wage = sum(list_3)
print('商品列表'.center(50, '-'))
for index, n in enumerate(list_1):
print(' %s . %s---%s' % (index, n[0], n[1]))
print('可用餘額'.center(50, '-'))
current_balance = '''
Credit : %s
Balance : %s
''' % (account_data['data']['credit'], account_data['data']['balance'])
print(current_balance)
print('-' * 50)
choice = input("請輸入商品編號,按 q 結算:")

if choice == "q":
print("已購商品列表".center(50, "-"))
for index_1, n_1 in enumerate(list_2):
print("\033[0;33m%s. %s %s\033[0m" % (index_1, n_1[0], n_1[1])) # 打印已購商品列表

transaction_result = make_transaction(trans_logger, account_data, 'shopping', wage)
if transaction_result['status'] == 0: # 判斷是否成功
print('''\033[42;1m購買成功,餘額還有:%s\033[0m''' % (account_data['data']['balance']))
exit()
else:
print(transaction_result)
elif choice == "exit":
exit()
print("拜拜!")
else:
if choice.isdigit(): # 判斷是不是數字
choice = int(choice)
if choice <= 3:
price = list_1[choice][1]

if dd >= wage:
list_3.append(price)
list_2.append(list_1[choice])
wage = sum(list_3)

print("\033[0;33m以加進購物車!\033[0m")
print("\033[0;33m 總價%s\033[0m" % wage)
else:
print("餘額不足!")
else:
print("沒有這個選項,請從新輸入!")
else:
print("沒有這個選項,請從新輸入!")
 

 auth模塊是判斷用戶登錄:

 

from .db_handler import load_account_data


def authenticate(account, password): # 登錄驗證模塊

account_data = load_account_data(account) # 調用函數,返回文件裏面的值
if account_data['status'] == 0: # 判斷是否返回了值
account_data = account_data['data'] # 提取用戶判斷
if password == account_data['password']: # 判斷密碼是否正確
return account_data # 返回從文件提出的數據
else:
return None # 不成功就返回None
else:
return None

 


db_handler是讀寫模塊:

 



import json, time, os
from conf import settings


def load_account_data(account): # 提取用戶文件模塊
account_file = os.path.join(settings.DB_PATH,"%s.json"% account) # 調用os模塊打開目錄文件,目錄路徑由模塊提供
if os.path.isfile(account_file): # 判斷文件是否存在
f = open(account_file) # 打開文件
data = json.load(f) # 序列話後保存在data裏面
f.close()
return {'status': 0, 'data': data} # 返回值
else:
return {'status': -1, 'error': "帳號不存在"}


def save_db(account_data):
"""寫入模塊"""
account_file = os.path.join(settings.DB_PATH,"%s.json" % account_data['id']) # 根據名稱設置路徑
if os.path.isfile(account_file): # 判斷這個文件是否存在
f = open("%s.new" % account_file, "w") # 建立一個同名稱的新文件
data = json.dump(account_data, f) # 把修改好的值寫進建立的文件
f.close() # 關閉文件
os.remove(account_file) # 刪除舊文件
os.rename("%s.new" % account_file, account_file) # 重命名新文件
return {"status": 0, 'data': data} # 返回


logger是日誌模塊:


import logging
from conf import settings
import os


def logger(log_type):
logger = logging.getLogger(log_type)
logger.setLevel(settings.LOG_LEVEL)

log_file = os.path.join(settings.LOG_PATH, settings.LOG_TYPES[log_type]) # 判斷是那個文件
fh = logging.FileHandler(log_file)

fh.setLevel(settings.LOG_LEVEL) # 設置日誌級別
formatter = settings.LOG_FORMAT # 設置日誌格式
fh.setFormatter(formatter)
logger.addHandler(fh) # 加上格式
return logger # 返回

transaction模塊是交易模塊:

 

from conf import settingsfrom .db_handler import save_dbdef make_transaction(logger, user_obj, tran_type, amount, **kwargs):    """交易模塊"""    amount = float(amount)    if tran_type in settings.TRANSACTION_TYPE:   # 判斷交易類型是否支持        interest = amount * settings.TRANSACTION_TYPE[tran_type]['interest']    # 計算利息        old_balabce = user_obj['data']['balance']    # 拿出舊餘額        if settings.TRANSACTION_TYPE[tran_type]['action'] == 'plus':   # 判斷交易類型,是加或者減            new_balance = old_balabce + amount + interest        elif settings.TRANSACTION_TYPE[tran_type]['action'] == 'minus':            new_balance = old_balabce - amount - interest            if new_balance < 0:  # 判斷餘額是否小於0                print('''\033[31;1mYour credit [%s] is not enough for this transaction [-%s],your current[%s]'''                      % (user_obj['data']['credit'], (amount + interest), old_balabce))                return {'status': 1, 'error': '交易失敗,餘額不足!'}   # 返回值,提示交易失敗        user_obj['data']['balance'] = new_balance  # 把新餘額保存在內存裏面        save_db(user_obj['data'])  # 用函數保存在帳號文件裏面        logger.info("account:%s  action:%s  amount:%s  interest:%s  balabce:%s" %                    (user_obj['data']['id'], tran_type, amount, interest, new_balance))        return {'status': 0, 'msg': '交易成功!'}    else:        print("\033[31;1mTransaction type [%s] is not exist!\033[0m" % tran_type)        return {'status': 1, 'error': '交易失敗,Transaction type [%s] is not exist!' % tran_type}謝謝欣賞,有問題或者建議請聯繫我郵箱861257034@qq.com
相關文章
相關標籤/搜索