python作業---模擬實現一個ATM + 購物商城程序


作業需求: 
模擬實現一個ATM + 購物商城程序

額度 15000或自定義
實現購物商城,買東西加入 購物車,調用信用卡接口結賬
可以提現,手續費5%
支持多賬戶登錄
支持賬戶間轉賬
記錄每月日常消費流水
提供還款接口
ATM記錄操作日志
提供管理接口,包括添加賬戶、用戶額度,凍結賬戶等。。。
用戶認證用裝飾器

作業思路
實現購物商城和信用卡的ATM功能
本程序有6個模塊,實現了購物和ATM的取款、還款、轉賬、賬單查看和用戶管理的功能。


程序結構:
test
├── README
├── ATM #ATM主程目錄
│ ├── __init__.py
│ ├── bin #ATM 執行文件 目錄
│ │ ├── __init__.py
│ │ ├── atm.py #ATM 執行程序
│ ├── conf #配置文件
│ │ ├── __init__.py
│ │ └── settings.py
│ ├── core #主要程序邏輯都 在這個目錄 里
│ │ ├── __init__.py
│ │ ├── accounts.py #用於從文件里加載和存儲賬戶數據
│ │ ├── auth.py #用戶認證模塊
│ │ ├── db_handle.py #數據庫連接引擎
│ │ ├── log.py #日志記錄模塊
│ │ ├── main.py #主邏輯交互程序
│ │ └── transaction.py #記賬\還錢\取錢等所有的與賬戶金額相關的操作都 在這
│ ├── db #用戶數據存儲的地方
│ │ ├── __init__.py
│ │ └── accounts #存各個用戶的賬戶數據 ,一個用戶一個文件
│ │ └── zcl.json #一個用戶賬戶示例文件
│ └── log #日志目錄
│ ├── __init__.py
│ ├── access.log #用戶訪問和操作的相關日志
│ └── transactions.log #所有的交易日志
└── shopping #電子商城程序
├── shopping_mol #購物商城的程序
└── __init__.py

開始先運行atm.py時執行程序,直接到main下,輸入正確用戶zcl和密碼abc,才能進行下一步的操作,然后列出atm的功能列表(還款、取款、轉賬、查看等)
shopping是一個獨立的程序,調用了還款的金額,購物結束后把剩余的金額在寫入到文件中,存入到信用卡中。


流程圖

 

shopping_mol

 1 import os,json
 2 
 3 dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 4 print(dir)
 5 file="%s/ATM/db/accounts/zcl.json"%dir
 6 print(file)
 7 with open(file, "r", encoding="utf-8") as f:
 8     account_data = json.load(f)
 9     print(account_data)
10 
11 
12 
13 product_list =[
14     ("Apple Iphone",6000),
15     ("Apple Watch",4600),
16     ("Books",600),
17     ("Bike",750),
18     ("cups",120),
19     ("Apple",50),
20     ("banana",60),
21 ]
22 shopping_list =[]
23 salary = account_data["balance"]
24 
25 while True:
26     for index,item in enumerate(product_list):
27         print (index,item)
28     user_choice = input ("Enter the serial number:")
29     if user_choice.isdigit():
30         user_choice = int (user_choice)
31         if user_choice <len (product_list) and user_choice >=0:
32             p_item = product_list[user_choice]
33             if p_item[1] <= salary:
34                 shopping_list.append(p_item)
35                 salary -= p_item[1]
36                 with open(file,"w+",encoding="utf-8") as f:
37                     account_data["balance"]=salary
38                     print(account_data)
39                     json.dump(account_data,f)
40                 print ("Added %s into your shopping cart,your current balance is %s"%(p_item,salary))
41             else:
42                 print ("Your balance is not enough!!")
43         else:
44             print ("The goods you entered do not exist")
45 
46     elif user_choice == "q":
47         print ("====shopping list====")
48         for p in shopping_list:
49             print (p)
50         print ("Your current balance is %s"%salary)
51         exit()
52     else:
53         print ("invalid option")
View Code

 

 

atm

 1 #ATM程序的執行文件
 2 import os
 3 import sys
 4 
 5 dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))   #找到路徑
 6 sys.path.insert(0,dir)                 # 添加路徑
 7 
 8 
 9 print(dir)
10 print(sys.path)
11 
12 #將main.py里面的所有代碼封裝成main變量
13 from core import main
14 
15 if __name__ == '__main__':
16     main.run()
View Code

 

settings

 1 #配置文件
 2 import logging
 3 import os
 4 
 5 BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))#找到路徑
 6 
 7 LOGIN_LEVEL = logging.INFO#定義日志的記錄級別
 8 
 9 DATABASE = {
10     "db_tool":"file_storage",  #文件存儲,這里可拓展成數據庫形式的
11     "name":"accounts",         #db下的文件名
12     "path":"%s/db"%BASE_DIR
13 }
14 #print(DATABASE)
15 #日志類型
16 LOGIN_TYPE={
17     "access":"access.log",
18     "transaction":"transaction.log"
19 }
20 
21 #用戶交易類型,每個類型對應一個字典,包括帳戶金額變動方式,利息
22 TRANSACTION_TYPE={
23     "repay":{"action":"plus","interest":0},
24     "withdraw":{"action":"minus","interest":0.05},
25     "transfer":{"action":"minus","interest":0}
26 }
View Code

 

account

 1 """
 2 用於處理用戶信息的load or dump
 3 每進行一個操作就將信息更新到數據庫
 4 """
 5 from core import db_handle
 6 from conf import settings
 7 import json
 8 
 9 def load_account(account_id):
10     """
11     將用戶信息從文件中load出來
12     :return: 用戶信息的字典
13     """
14     #返回路徑  ATM/db/accounts
15     db_path = db_handle.handle(settings.DATABASE)
16     account_file = "%s/%s.json" % (db_path, account_id)
17     with open(account_file, "r", encoding="utf-8") as f:
18         account_data =  json.load(f)
19         return account_data
20 
21 
22 def dump_account(account_data):
23     """
24     將已更改的用戶信息更新到用戶文件
25     :param account_data: 每操作后用戶的信息
26     :return:
27     """
28     db_path = db_handle.handle(settings.DATABASE)
29     account_file = "%s/%s.json" % (db_path, account_data["id"])
30     with open(account_file, "w+", encoding="utf-8") as f:
31         json.dump(account_data, f)
32 
33     print("dump success")
View Code

 

 

auth

 1 #認證模塊
 2 import os
 3 import json
 4 import time
 5 
 6 from core import db_handle
 7 from conf import settings
 8 
 9 def access_auth(account,password,log_obj):
10     """
11     下面的access_login調用access_auth方法,用於登陸
12     :param acount: 用戶名
13     :param password: 密碼
14     :return:如果未超期,返回字典,超期則打印相應提示
15    """
16     db_path=db_handle.handle(settings.DATABASE) #調用db_handle下的handle方法,返回路徑/db/accounts
17     print(db_path)
18     account_file="%s/%s.json"%(db_path,account)  #用戶文件
19     print(account_file)
20     if os.path.isfile(account_file):                 #如果用戶文件存在(即用戶存在)
21         with open(account_file,"r",encoding="utf-8") as f:  #打開文件
22             account_data = json.load(f)       #file_data為字典形式
23             print(account_data)
24             if account_data["password"]==password:
25                 expire_time=time.mktime(time.strptime(account_data["expire_date"],"%Y-%m-%d"))
26                 #print(expire_time)
27                 #print(time.strptime(account_data["expire_date"],"%Y-%m-%d"))
28                 if time.time()>expire_time:     #如果信用卡已超期
29                     log_obj.error("Account [%s] had expired,Please contract the bank" % account)
30                     print("Account %s had expired,Please contract the bank"%account)
31                 else:                         #信用卡未超期,返回用戶數據的字典
32                     #print("return")
33                     log_obj.info("Account [%s] logging success" % account)
34                     return account_data
35             else:
36                 log_obj.error("Account or Passworddoes not correct!")
37                 print("Account or Passworddoes not correct!")
38     else:#用戶不存在
39         log_obj.error("Account [%s] does not exist!" % account)
40         print("Account [%s] does not exist!"%account)
41 
42 
43 def access_login(user_data,log_obj):
44     """
45     用記登陸,當登陸失敗超過三次則退出
46     :param user_data: main.py里面的字典
47     :return:若用戶帳號密碼正確且信用卡未超期,返回用戶數據的字典
48     """
49     retry=0
50     while not user_data["is_authenticated"] and retry<3:
51         account=input("Account:").strip()
52         password= input("Password:").strip()   #用戶帳號密碼正確且信用卡未超期,返回用戶數據的字典
53         user_auth_data=access_auth(account,password,log_obj)
54         if user_auth_data:
55             user_data["is_authenticated"]=True   #用戶認證為True
56             user_data["account_id"]=account       #用戶帳號ID為帳號名
57             print("welcome %s"%account)
58             return user_auth_data
59         retry+=1
60     else:
61         print("Account [%s] try logging too many times..." % account)
62         log_obj.error("Account [%s] try logging too many times..." % account)
63         exit()
View Code

 

 

db_handle

 1 #處理與數據庫的交互,若是file_db_storage,返回路徑
 2 
 3 def file_db_handle(database):
 4     """
 5      數據存在文件
 6      :param database:
 7      :return: 返回路徑  ATM1/db/accounts
 8     """
 9     db_path="%s/%s"%(database["path"],database["name"])
10     #print(db_path)
11     return db_path
12 
13 # def mysql_db_handle(database):
14 #     """
15 #     處理mysql數據庫,這里用文件來存數據,
16 #     保留這個方法主要為了程序拓展性
17 #     :return:
18 #     """
19 #     pass
20 
21 def handle(database):
22     """
23     對某種數據庫形式處於是
24     本程序用的是文件處理file_storage
25     :param database: settings里面的DATABASE
26     :return: 返回路徑
27     """
28     if database["db_tool"]=="file_storage":
29         return file_db_handle(database)
30     # if database["db_tool"]=="mysql":
31     #     return mysql_db_handle(database)
View Code

 

 

log

 1 import logging
 2 from conf import settings
 3 from core import db_handle
 4 
 5 def log(logging_type):
 6     """
 7       main模塊調用access_logger = log.log("access")
 8      :param logging_type: "access"
 9      return: 返回logger日志對象
10     """
11     logger=logging.getLogger(logging_type)  #傳日志用例,生成日志對象
12     logger.setLevel(settings.LOGIN_LEVEL)   #設置日志級別
13 
14     ch = logging.StreamHandler()     #日志打印到屏幕,獲取對象
15     ch.setLevel(settings.LOGIN_LEVEL)
16 
17     # 獲取文件日志對象及日志文件
18     log_file="%s/log/%s"%(settings.BASE_DIR,settings.LOGIN_TYPE[logging_type])
19     fh = logging.FileHandler(log_file)
20     fh.setLevel(settings.LOGIN_LEVEL)
21 
22     # 日志格式
23     formatter=logging.Formatter("%(asctime)s-%(name)s-%(levelname)s-%(message)s")
24 
25     # 輸出格式
26     ch.setFormatter(formatter)
27     fh.setFormatter(formatter)
28 
29     #把日志打印到指定的handler
30     logger.addHandler(ch)
31     logger.addHandler(fh)
32 
33     return logger    #log方法返回logger對象
View Code

 

 

transaction

 1 """
 2 交易模塊,處理用戶金額移動
 3 """
 4 from conf import settings
 5 from core import account
 6 from core import log
 7 
 8 def make_transaction(account_data,transcation_type,amount,log_obj,**kwargs):
 9     """
10     處理用戶的交易
11     :param account_data:字典,用戶的帳戶信息
12     :param transaction_type:用戶交易類型,repay or withdraw...
13     :param amount:交易金額
14     :return:用戶交易后帳戶的信息
15     """
16     amount=float(amount)  #將字符串類型轉換為float類型
17     if transcation_type in settings.TRANSACTION_TYPE:
18         interest=amount*settings.TRANSACTION_TYPE[transcation_type]["interest"]   #利息計算
19         old_balace= account_data["balance"]  #用戶原金額
20         print(interest,old_balace)
21         # 如果帳戶金額變化方式是"plus",加錢
22         if settings.TRANSACTION_TYPE[transcation_type]["action"]=="plus":
23             new_balance=old_balace+amount+interest
24             log_obj.info("Your account repay%s,your account new balance is %s"%(amount,new_balance))
25             # 如果帳戶金額變化方式是"minus",減錢
26         elif settings.TRANSACTION_TYPE[transcation_type]["action"]=="minus":
27             new_balance=old_balace-amount-interest
28             log_obj.info("Your account withdraw%s,your account new balance is %s" % (amount, new_balance))
29             if new_balance<0:
30                 print("Your Credit [%s] is not enough for transaction [-%s], "
31                       "and Now your current balance is [%s]" % (account_data["credit"], (amount+interest), old_balace))
32                 return
33         account_data["balance"]=new_balance
34         account.dump_account(account_data)    #調用core下account模塊將已更改的用戶信息更新到用戶文件
35         return account_data
36     else:
37         print("Transaction is not exist!")
View Code

 

 

main

  1 """
  2 主邏輯交互模塊
  3 """
  4 from core import auth
  5 from core import log
  6 from core import transaction
  7 from core import account
  8 from conf import settings
  9 from core import db_handle
 10 
 11 import os
 12 
 13 
 14 #用戶數據信息
 15 user_data = {
 16     'account_id':None,          #帳號ID
 17     'is_authenticated':False,  #是否認證
 18     'account_data':None        #帳號數據
 19 
 20 }
 21 
 22 #調用log文件下的log方法,返回日志對象
 23 access_logger = log.log("access")
 24 transaction_logger = log.log("transaction")
 25 
 26 
 27 
 28 def account_info(access_data):
 29     """
 30     access_data:包括ID,is_authenticaed,用戶帳號信息
 31     查看用戶帳戶信息
 32     :return:
 33     """
 34     print(access_data)
 35 
 36 
 37 
 38 
 39 def repay(access_data):
 40     """
 41     access_data:包括ID,is_authenticaed,用戶帳號信息
 42     還款
 43     :return:
 44     """
 45     print(access_data)
 46     print("repay")
 47     #調用account模塊的load_account方法,從數據庫從load出用戶信息
 48     account_data = account.load_account(access_data["account_id"])
 49     print(account_data)
 50     current_balance = """
 51     -------------BALANCE INFO--------------
 52     Credit:%s
 53     Balance:%s
 54     """ % (account_data["credit"], account_data["balance"])
 55     back_flag = False
 56     while not back_flag:
 57         print(current_balance)
 58         repay_amount = input("\033[31;1mInput repay amount(b=back):\033[0m").strip()
 59         #如果用戶輸入整型數字
 60         if len(repay_amount) > 0 and repay_amount.isdigit():
 61             #調用transaction模塊的方法,參數分別是用戶帳戶信息,交易類型,交易金額
 62             new_account_data = transaction.make_transaction(account_data, "repay", repay_amount,transaction_logger)
 63             if new_account_data:
 64                 print("\033[42;1mNew Balance:%s\033[0m" % new_account_data["balance"])
 65 
 66         else:
 67             print("\033[31;1m%s is not valid amount,Only accept interger!\033[0m" % repay_amount)
 68 
 69         if repay_amount =="b" or repay_amount == "back":
 70             back_flag = True
 71 
 72 def withdraw(access_data):
 73     """
 74     取款
 75     :return:
 76     """
 77     print(access_data)
 78     print("withdraw")
 79     # 調用account模塊的load_account方法,從數據庫從load出用戶信息
 80     account_data = account.load_account(access_data["account_id"])
 81     print(account_data)
 82     current_balance = """
 83        -------------BALANCE INFO--------------
 84        Credit:%s
 85        Balance:%s
 86        """ % (account_data["credit"], account_data["balance"])
 87     back_flag = False
 88     while not back_flag:
 89         print(current_balance)
 90         withdraw_amount = input("\033[31;1mInput withdraw amount(b=back):\033[0m").strip()
 91         # 如果用戶輸入整型數字
 92         if len(withdraw_amount) > 0 and withdraw_amount.isdigit():
 93             # 調用transaction模塊的方法,參數分別是用戶帳戶信息,交易類型,交易金額
 94             new_account_data = transaction.make_transaction(account_data, "withdraw", withdraw_amount,transaction_logger)
 95             if new_account_data:
 96                 print("\033[42;1mNew Balance:%s\033[0m" % new_account_data["balance"])
 97 
 98         else:
 99             print("\033[31;1m%s is not valid amount,Only accept interger!\033[0m" % withdraw_amount)
100 
101         if withdraw_amount == "b" or withdraw_amount == "back":
102             back_flag = True
103 
104 
105 def transfer(access_data):
106     """
107     轉帳
108     :return:
109     """
110     print(access_data)
111     print("transfer")
112     # 調用account模塊的load_account方法,從數據庫從load出用戶信息
113     account_data = account.load_account(access_data["account_id"])
114     print(account_data)
115     current_balance = """
116            -------------BALANCE INFO--------------
117            Credit:%s
118            Balance:%s
119            """ % (account_data["credit"], account_data["balance"])
120     back_flag = False
121     while not back_flag:
122         print(current_balance)
123         transfer_amount = input("\033[31;1mInput transfer amount(b=back):\033[0m").strip()
124         # 如果用戶輸入整型數字
125         if len(transfer_amount) > 0 and transfer_amount.isdigit():
126             # 調用transaction模塊的方法,參數分別是用戶帳戶信息,交易類型,交易金額
127             new_account_data = transaction.make_transaction(account_data, "transfer", transfer_amount,transaction_logger)
128             if new_account_data:
129                 print("\033[42;1mNew Balance:%s\033[0m" % new_account_data["balance"])
130             new_account_data2 = transaction.make_transaction(account_data, "repay", new_account_data["balance"],transaction_logger)
131             if new_account_data2:
132                 print("\033[42;1mNew Balance2:%s\033[0m" % new_account_data2["balance"])
133 
134         else:
135             print("\033[31;1m%s is not valid amount,Only accept interger!\033[0m" % transfer_amount)
136 
137         if transfer_amount == "b" or transfer_amount == "back":
138             back_flag = True
139 
140 
141 def paycheck(access_data):
142     """
143     賬單查看
144     :return:
145     """
146 
147     time=input("please input time(Y-M-D):")
148     log_file = "%s/log/%s" % (settings.BASE_DIR, settings.LOGIN_TYPE["transaction"])
149     print(log_file)
150     with open (log_file,"r",encoding="utf-8") as f :
151         for i in f.readlines():
152             if time == i[0:10]:
153                 print(i)
154             elif time == i[0:7]:
155                 print(i)
156             elif time == i[0:4]:
157                 print(i)
158 
159 
160 
161 
162 def logout(access_data):
163     """
164     退出登陸
165     :return:
166     """
167     q = input("If you want to quit,please input q:")
168     if q =="q":
169         exit()
170 
171 
172 def interactive(access_data,**kwargs):
173     """
174     用戶交互
175     :return:
176     """
177     msg = (
178         """
179         -------------ZhangChengLiang Bank---------------
180         \033[31;1m
181         1.  賬戶信息
182         2.  存款
183         3.  取款
184         4.  轉賬
185         5.  賬單
186         6.  退出
187         \033[0m"""
188     )
189     menu_dic = {
190         "1":account_info,
191         "2":repay,
192         "3":withdraw,
193         "4":transfer,
194         "5":paycheck,
195         "6":logout
196     }
197     flag = False
198     while not flag:
199         print(msg)
200         choice = input("<<<:").strip()
201         if choice in menu_dic:
202             #很重要!!省了很多代碼,不用像之前一個一個判斷!
203             menu_dic[choice](access_data)
204 
205         else:
206             print("\033[31;1mYou choice doesn't exist!\033[0m")
207 
208 
209 
210 def run():
211     """
212     當程序啟動時調用,用於實現主要交互邏輯
213     :return:
214     """
215     # 調用認證模塊,返回用戶文件json.load后的字典,傳入access_logger日志對象
216     access_data = auth.access_login(user_data, access_logger)
217     print("AA")
218     if user_data["is_authenticated"]:       #如果用戶認證成功
219         print("has authenticated")
220         #將用戶文件的字典賦給user_data["account_data"]
221         user_data["account_data"] = access_data
222         interactive(user_data)   #用戶交互開始
View Code

 

 

.json

{"status": 0, "expire_date": "2021-01-01", "credit": 15000, "pay_day": 22, "balance": 13650, "enroll_date": "2016-01-02", "id": 22, "password": "abc"}

 

 

 

 
       


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM