要求
1、啟動程式後,輸入使用者名稱密碼後,如果是第一次登入,讓使用者輸入工資,然後列印商品列表
2、允許使用者根據商品編號購買商品
3、使用者選擇商品後,檢測餘額是否夠,夠就直接扣款,不夠就提醒
4、可隨時退出,退出時,列印已購買商品和餘額
5、在使用者使用過程中, 關鍵輸出,如餘額,商品已加入購物車等訊息,需高亮顯示
6、使用者下一次登入後,輸入使用者名稱密碼,直接回到上次的狀態,即上次消費的餘額什麼的還是那些,再次登入可繼續購買
7、允許查詢之前的消費記錄
1)編寫思路
編寫思路參考下面GitHub連結中的流程圖
2)具體實現
# -*- coding:utf-8 -*-# Author:Chuixin Zeng# 匯入JSON模組import json# 匯入日期時間模組import datetime# 匯入系統OS模組import os# 自訂歡迎資訊info = '''1. 按"A"註冊2. 按"B"登入3. 按"Q"退出'''# 自訂錯誤提示資訊_error_info = '輸入有誤,請檢查後重新輸入!'# 定義一個_save_account函數# 用於將購物相關資訊儲存到JSON檔案,比如購物時間,購物曆史,購物列表,賬戶餘額,建立的新使用者和現有已存在使用者資訊def _save_account(database, filename='DataBase.json'): # 開啟並可寫檔案,若檔案已存在,則以前的內容將被清除 # 使用with as語句的效率更高,不需要單獨設定如何讀檔案之後再如何關閉檔案,一個with as搞定所有讀寫關閉操作 with open(filename,'w') as f: # f相當於一個變數,把開啟並修改檔案的操作賦予f json.dump(database,f) # json.dump是將一個Python資料類型列表進行json格式的編碼解析# 定義一個_load_database函數,用於從json檔案中讀取資訊,預設載入的資料庫是database.json檔案def _load_database(filename='DataBase.json'): with open(filename) as f: database = json.load(f) # 解碼JSON資料,將一個JSON編碼的字串轉換回一個Python資料結構 return database # 返回解碼後的資料# 定義一個函數_set_shopping_time,設定並記錄購物的時間,函數裡面定義了一個參數account,用於儲存賬戶資訊def _set_shopping_time(account): database = _load_database() # 設定要記錄到哪個資料庫,這裡使用的是前面定義好的函數_load_database定義的database.json d1 = datetime.datetime.now() # 設定購物時間為目前時間 d2 = d1.strftime('%Y-%m-%d %H:%M:%S') # 將目前時間進行格式轉換 database[account]['shopping_time'] = d2 # 將轉換好的時間記錄到字典裡面的shopping_time鍵上 _save_account(database) # 儲存購物時間到資料庫中,這裡的資料庫是指database.json檔案# 定義一個函數,用於擷取已經儲存過的購物時間def _get_datetime(account): database = _load_database() data = database[account]['shopping_time'] # 返回變數data的值,變數data儲存的就是account鍵對應的購物時間值,這個值是從json裡面解碼出來後到字典裡 # 由json到Python可識別的字典資料的解碼過程由_load_database函數完成 return data# 定義一個函數_get_shopping_history,用於查詢購物記錄def _get_shopping_history(account): database = _load_database() history = database[account]['shopping_list'] # 增加一個空列表,配合下邊for迴圈將購物清單中的重複項合并 aa = [] for i in history: # 將購物車裡面的shopping list和aa空列表進行對比,如果列表裡面沒有,就添加到列表 # 也就意味著,如果列表已經有了就不添加了,達到了購物車去重的功能 if i not in aa: aa.append(i) # 然後迴圈遍曆aa列表裡面的購物清單 for j in aa: # 統計購買的每件商品的數量,也就是aa列表裡面每件商品的數量,數量從history原始列表裡面取(未去重的列表) count = history.count(j) # 統計購買商品的日期,日期就是account字典對應的商品的日期 date = _get_datetime(account) # 列印購買的商品的數量、日期和商品名稱 print('您於%s購買了%s件%s' %(date,count,j))# 定義一個函數login,用於登入系統def _login(): database = _load_database() # 載入資料庫,使用的是前面定義好的載入資料庫的函數 blacklist = _load_database('BlackList.json') # 設定使用者的黑名單列表,在列表中的使用者將不允許登入到購物系統 print('歡迎登入購物系統!') # 列印歡迎資訊 # 第一個死迴圈 while True: account = input("請輸入您的帳號登入系統(按q退出):") if account in blacklist: # 如果賬戶在黑名單裡面,則退出登入 print("您的帳號已經被鎖定,請聯絡管理員處理!") _logout() # 直接調用下面定義好的_logout()函數 # 判斷如果使用者輸入的是q,就退出購物系統 elif account == 'q': _logout() # 判斷如果使用者在資料庫裡面,則繼續判斷使用者輸入的密碼是否正確 # 這裡使用while迴圈和count計數器,如果輸入錯誤密碼大於3次,則鎖定賬戶 elif account in database: count = 0 while count < 3: pwd = input('請輸入密碼:') # 如果使用者輸入的密碼和資料庫儲存的密碼匹配 if pwd == database[account]['pwd']: # 進入到死迴圈 while True: # 首先登入成功後,先擷取使用者賬戶的餘額,告訴使用者還剩多少錢,餘額通過_get_balance函數得到 account_balance = _get_balance(account) # 高亮列印賬戶的餘額資訊 print('您的賬戶餘額是\033[32m%d\033[0m'% account_balance) # 讓使用者輸入特定字母進入特定的菜單,並使用strip去除提示資訊前後的空格 command = input('按h查詢購物曆史,按s開始購物,按t儲值,開始購物後購物曆史將被清空:').strip() # 匯入使用者購物資訊資料庫 database = _load_database() # 判斷如果使用者輸入的是h,則查詢購物曆史 if command == 'h': # 判斷如果購物時間不為空白的話,證明使用者有購買曆史 if database[account]['shopping_time'] != None: # 載入函數_get_shopping_history,輸出購物曆史資訊 _get_shopping_history(account) else: print('您在本商城沒有購買過東西!') elif command == 't': # 如果使用者輸入的是t,則載入定義好的_top_up函數,執行儲值操作 _top_up(account) elif command == 's': # 如果使用者輸入的是s,則將字典中的購物列表和時間初始化為空白 # 注意:這個字典是從json檔案轉換過來的,裡面有曆史資料,所以需要先清空 # 等使用者購物完成後,新的字典資料,即購物資料,會寫回到json檔案 database[account]['shopping_list'] = [] database[account]['shopping_time'] = None # 調用_save_account函數,將清空的操作儲存到database檔案 _save_account(database) # 調用shopping函數,開始購物 _shopping(account) else: # 如果使用者的操作不符合上面所有的情況,則輸出錯誤資訊 # 這裡直接調用前面定義好的_error_info函數,輸出錯誤資訊 print(_error_info) else: count += 1 # 告訴使用者還有多少次機會嘗試登陸 print('輸入的密碼錯誤,你還有%s機會' % (3 - count)) # 將使用者帳號添加至blacklist,儲存成字典形式,value設定為None # 這裡使用了字典的setdefalut用法,如果字典中包含有給定鍵,則返回該鍵對應的值,否則返回為該鍵設定的值 blacklist.setdefault(account) # 使用者輸入三次錯誤操作之後,會跳出上面的while迴圈,然後列印資訊告訴使用者帳號鎖定 print('您的帳號已經鎖定!') # 將鎖定的賬戶資訊儲存到黑名單列表 _save_account(blacklist,'BlackList.json') # 調用退出登入的函數 _logout() else: print('帳號不存在,請重試!或輸入b返回上一層,輸入q,退出購物程式!')# 定義一個函數_set_shopping_list,用於設定購物清單def _set_shopping_list(account,c_name,num): database = _load_database() # 使用for迴圈添加購買的商品的數量 for i in range(num): # 將購買的商品添加到字典shopping_list鍵所對應的列表中 database[account]['shopping_list'].append(c_name) # 將購買資訊通過調用_save_account函數儲存到json檔案中 _save_account(database)# 定義一個函數_set_balance,用於計算購買商品後,所剩下的餘額def _set_balance(account,num): database = _load_database() # 購買商品後,扣除購買的商品的價格 database[account]['balance'] -= num # 將餘額資訊通過調用_save_account函數儲存到json檔案中 _save_account(database)# 定義一個函數,用於執行儲值操作def _top_up(account): database = _load_database() # 進入迴圈 while True: # 提供互動式介面讓使用者輸入要儲值的金額 num = input('請輸入您要儲值的金額:') # 判斷如果使用者輸入的是不是純數字,則將儲值後的金額更新到資料庫 if num.isdigit(): # 判斷輸入是否為純數字(儲值的資料必須為純數字) database[account]["balance"] += int(num) # 將str格式的"純數字"轉換為int格式 _save_account(database) # 儲存到檔案 account_balance = _get_balance(account) # 再從檔案中讀取餘額資訊 # 高亮列印儲值後的餘額資訊 print('您已成功儲值,您的餘額為\033[32m%d\033[0m元' % account_balance) # 上面已經列印餘額了,所以這裡定義一個return none代表不使用return的方式返回餘額 return None # 如果使用者輸入的不是純數字,則提示輸入錯誤,重新進行while迴圈,讓使用者重新輸入 else: print('您的輸入有誤,請重新輸入!')# 定義一個函數_get_balance,用於擷取賬戶餘額def _get_balance(account): database = _load_database() # 將字典中賬戶的餘額資訊讀取到account_balance變數 account_balance = database[account]['balance'] # 返回賬戶餘額變數的值 return account_balance# 定義一個函數_shopping,用於執行購物程式def _shopping(account): # 定義一個字典,用於儲存商品菜單 goods = {'家電類': {'電視': {'小米電視': 3999, '樂視電視': 4299}, '空調': {'格力空調': 8999, '海爾空調': 6000}, '冰箱': {'美的冰箱': 5099, '西門子冰箱': 4599}}, '3C類': {'電腦': {'聯想筆記本': 6888, 'mac air': 8009}, '手機': {'小米5': 1999, 'iPhone6': 5299}}, '生活類': {'鞋子': {'NIKE籃球鞋': 899, '安踏': 399}, 'T恤': {'森馬': 89, '真維斯': 75, '優衣庫': 97}}} # 第一級死迴圈 while True: # 通過使用enumerate來直接迴圈到產品名稱 for i in enumerate(goods.keys()): # 列印第一級菜單 print(i) # 提供互動式介面,讓使用者輸入要選擇的商品分類名稱,選擇一級菜單 choose = input('請輸入您要選擇的商品分類名稱(輸入q退出):').strip() # 定義一個Null 字元串,用於儲存二級菜單 str1 = '' if choose in goods: # 如果使用者輸入的商品分類正確,則將該商品分類下的所有商品列印出來 for i in enumerate(goods[choose].keys()): # 列印使用者選擇的一級菜單下面的二級菜單 print(i) # 將二級菜單追加添加到str1變數中 str1 += (i[1] + '\n') # 所有二級菜單均添加到str1之後,在str1後面加上: str1 += ':' # 第二級死迴圈 while True: # 分類來自於前面choose的互動式輸入,分類下面的列表來至於str1變數 # 讓使用者選擇一個二級菜單 c_name = input("請輸入您要選擇的%s分類名稱(輸入b返回,輸入q退出):\n%s"% (choose, str1)).strip() # 定義一個Null 字元串,用於儲存三級菜單 str2 = '' if c_name == 'b': # 如果使用者輸入的是b,則中斷當前的while迴圈,返回上上層迴圈,即返回到上層菜單 break elif c_name == 'q': # 如果使用者輸入的是q,則調用_log_out函數,退出登入 _logout(account) # 如果使用者輸入的名稱在二級菜單裡面,則通過for把使用者選擇的二級菜單商品下面的三級菜單遍曆出來 elif c_name in goods[choose]: for i in goods[choose][c_name].keys(): # 列印遍曆出來的二級菜單下面的三級菜單,同時列印物品和價格 # i來自於keys即鍵名稱(物品名稱),goods[choose][c_name][i]取的是鍵的值,即價格 print(i,goods[choose][c_name][i]) str2 += (i + '\n') str2 += ':' # 第三級死迴圈 while True: # 在互動式介面,讓使用者輸入要購買的商品的名稱,商品目錄來自於str2變數 p_name = input('請輸入您要購買的商品名稱(輸入b返回上一級,q退出,h查詢購物車):\n%s' % str2).strip() # 如果使用者輸入的是b,則跳出本層級的while死迴圈,退到上一級 if p_name == 'b': break # 如果使用者輸入的q,則直接調用_logout函數,退出購物 elif p_name == 'q': _logout(account) # 如果使用者輸入的h,則查詢json檔案中的購物曆史 elif p_name == 'h': database = _load_database() # 有購物曆史,則調用_get_shopping_history函數,輸出購物曆史 if database[account]['shopping_time'] != None: _get_shopping_history() # 梅花購物曆史的話,則告訴使用者無購物曆史 else: print('您在本商城沒有購物記錄!') # 如果使用者輸入的商品名稱在所選擇的商品分類中 elif p_name in goods[choose][c_name]: # 第四層死迴圈 while True: # 互動式介面讓使用者輸入要購買的商品的數量 num = input('請輸入要購買的商品的數量:').strip() # 如果使用者輸入的是純數字 if num.isdigit(): # 首先將純數字轉換為整型 num = int(num) # 定義一個account_balance變數,儲存現在的賬戶餘額 account_balance = _get_balance(account) # 定義一個價格變數,總價格 = 商品單價*數量,單價來自於字典,數量來自於num price = (goods[choose][c_name][p_name]) * num # 判斷如果賬戶餘額大於所購買的商品金額,才可以購買 if account_balance >= price: # 調用_set_balance函數,計算購買後的賬戶餘額是多少 # #計算方法在_set_balance函數裡面 _set_balance(account,price) # 提示使用者已經成功購買商品 print('您成功購買%d件商品:%s!'% (num,p_name)) # 調用_set_shopping_list函數,將購買的商品列表儲存到json檔案中 _set_shopping_list(account,p_name,num) # 定義account_balance變數,通過使用_get_balance函數重新擷取賬戶的餘額資訊 account_balance = _get_balance(account) # 列印告訴使用者,購買完商品之後剩下多少錢 print('您目前的賬戶餘額為\033[31m%d\033[0m元' % account_balance) # 調用_set_shopping_time函數,將購物時間寫入到json檔案 _set_shopping_time(account) # 退出當前的迴圈,到上一級迴圈,使用者可以選擇是否繼續購物 break # 如果使用者的賬戶餘額小於商品的金額,則告訴使用者餘額不足,無法購買 else: print('您的賬戶餘額不足,請您及時儲值!') g = input("儲值輸入t,返回上一級輸入b,退出輸入q':") if g == 'q': _logout() if g == 'b': break if g == 't': # 這裡我增加了一個調用_top_up函數,賬戶餘額不住的時候,馬上提示使用者儲值 _top_up(account) # 如果使用者購買商品時輸入的數量不是純數字類型,則調用_error_info函數,輸出錯誤資訊 else: print(_error_info) # 如果使用者輸入的購買的商品名稱不正確,則調用_error_info函數,輸出錯誤資訊 else: print(_error_info) # # 如果使用者輸入的購買的商品分類資訊不正確,則調用_error_info函數,輸出錯誤資訊 else: print('輸入錯誤,請重新輸入!') # 如果使用者輸入的不是商品分類,而是q,則調用_logout函數退出登入 elif choose == 'q': _logout(account) # 如果使用者輸入的資訊既不在商品分類中,也不是q,則告訴使用者重新輸入 else: print('輸入錯誤,請重新輸入!')#def _add_user(): # 首先調用_load_database函數,將json檔案解碼出來,放到database變數 database = _load_database('DataBase.json') # 執行while迴圈 while True: # 提供互動式介面,讓使用者輸入要建立的使用者名稱稱 username = input('請輸入您的賬戶名稱:').strip() # 判斷是否存在重名的使用者,如果有,則告訴使用者,繼續執行while迴圈,讓使用者輸入 if username in database: print('使用者名稱已經存在,不需要重複建立!') # 如果沒有,則使用者輸入資訊有效,中斷while迴圈 else: break # 執行while迴圈 while True: # 讓使用者輸入兩次密碼 pwd1 = input('請輸入您的賬戶密碼:').strip() pwd2 = input('請再次輸入您的賬戶密碼:').strip() # 對兩次輸入的密碼資訊進行比對 # 如果面膜不正確,則繼續執行while迴圈讓使用者重新輸入 if pwd1 != pwd2: print('2次輸入的密碼不通,請重新輸入') # 如果輸入的密碼是正確的,則告訴使用者建立賬戶成功 else: print('建立使用者成功,開始購物吧!') # 把使用者建立的賬戶和密碼資訊儲存到database字典中 # username為使用者帳號,pwd1為使用者密碼,balance為賬戶餘額,shopping_list為購物清單,shopping_time為購物時間 database[username] = {'pwd':pwd1,'balance':0,'shopping_list':[],'shopping_time':None} # 調用_save_account函數,將建立好的賬戶資訊和賬戶的初始餘額、初始購物時間資訊儲存到json檔案 _save_account(database) # 退出while迴圈 break# 設定預設變數account = None來判定帳號是否已經登入,如果沒有登入就退出,則不列印購物資訊# 如果已經登入過,則列印購物曆史資訊def _logout(account=None): if account != None: _get_shopping_history(account) exit('感謝您來購物!')# 定義一個main函數,用於初始登入介面的判斷def main(): # 執行while迴圈 while True: # 列印登入提示資訊,info資訊定義在程式的開頭,是一個字串類型 print(info) # 提供互動式介面,讓使用者輸入指令資訊 command = input('請輸入指令:').strip() # 如果使用者輸入的是大寫的A,則調用_add_user函數,建立新使用者 if command.upper() == 'A': _add_user() # 如果使用者輸入的是大寫的B,則調用_login函數,進行登入 elif command.upper() == 'B': _login() # 如果使用者輸入的是Q,則調用_logout函數,退出登入 elif command.upper() == 'Q': _logout() # 如果使用者沒有按照提示資訊進行輸入,則告訴使用者重新輸入,會重新執行while迴圈 else: print('輸入錯誤,請重新輸入')# 執行main函數if __name__ == '__main__': main()