python之ATM


  每次做一點就發出來,大神不要嫌重復

 

  2016/11/4

  今天來搞ATM,反正逃不了的,說來慚愧,這個作業是我10/4號20天前拿到的,當時是萬臉蒙比的,今天又做了一點,現在算是百臉蒙比吧。

 

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


  額度 15000或自定義 
  實現購物商城,買東西加入 購物車,調用信用卡接口結賬   其實是兩套單獨程序
  可以提現,手續費5%  提現不能超過總余額一半   
  每月22號出賬單,每月10號為還款日,過期未還,按欠款總額 萬分之5 每日計息
  支持多賬戶登錄,每個用戶有單獨信息
  支持賬戶間轉賬,
  記錄每月日常消費流水
  提供還款接口
  ATM記錄操作日志
  提供管理接口,包括添加賬戶、用戶額度,凍結賬戶等。。。

 

 

二、需求分析:

  角色:

      管理員功能:
              增刪改查
              記錄日志
              基本信息
              額度 15000
            
      普通用戶功能:
          可以提現,手續費5%
          支持多賬戶登錄
          支持賬戶間轉賬
          記錄每月日常消費流水
          提供還款接口
          ATM記錄操作日志

 

三、文件創建

  剛開始的蒙比從不知道文件怎么創建開始?擦!怎么多需求,肯定不止一兩個文件的,那多個文件又是怎么建的?我還特的心血來潮花一個早上去圖去館研究一下,最后挺亂的,后來看了視頻,才發現有文件創建上是有開發規范的!

  

     

  bin  用於執行可執行文件

  conf  配置文件

  db  用於存放用戶數據

  log  日志,記錄相關信息

 

 

四、begin funny coding

  這里我每次寫一些,改一些,方便我這種小白看思路,想看最終版的直接拉到文章最后。

 

16/11/4  9:22

  今天實現了檢查帳戶是否存在,信用卡是否超期的功能。自我感覺良好,哈哈~

  

學到的新技能:

    1. 如何讓字體打印出來有顏色??

print("\033[31;1mAccount[%s]doesnotexist!\033[0m" % account)

    運行結果:Account [qee] does not exist!        打印出來是紅色的

 

    2.如何將一個文件內的字符串形式通過json轉化為相應的字典格式??

      用json.load()就好嘛,不過還是遇到一點小問題。

#account_file是文件的絕對路徑
with open(account_file, "r", encoding="utf-8") as f:   #打開文件
  file_data = json.load(account_file) print(file_data)
這樣竟然出錯了!! 錯誤信息:AttributeError:
'str' object has no attribute 'read'

  

  於是我去看了下json.load()的源碼。

def load(fp, cls=None, object_hook=None, parse_float=None,
        parse_int=None, parse_constant=None, object_pairs_hook=None, **kw):
    """Deserialize ``fp`` (a ``.read()``-supporting file-like object containing
    a JSON document) to a Python object.

    ``object_hook`` is an optional function that will be called with the
    result of any object literal decode (a ``dict``). The return value of
    ``object_hook`` will be used instead of the ``dict``. This feature
    can be used to implement custom decoders (e.g. JSON-RPC class hinting).

    ``object_pairs_hook`` is an optional function that will be called with the
    result of any object literal decoded with an ordered list of pairs.  The
    return value of ``object_pairs_hook`` will be used instead of the ``dict``.
    This feature can be used to implement custom decoders that rely on the
    order that the key and value pairs are decoded (for example,
    collections.OrderedDict will remember the order of insertion). If
    ``object_hook`` is also defined, the ``object_pairs_hook`` takes priority.

    To use a custom ``JSONDecoder`` subclass, specify it with the ``cls``
    kwarg; otherwise ``JSONDecoder`` is used.

    """
    return loads(fp.read(),
        cls=cls, object_hook=object_hook,
        parse_float=parse_float, parse_int=parse_int,
        parse_constant=parse_constant, object_pairs_hook=object_pairs_hook, **kw)
View Code

  

  注意這一句return loads(fp.read(),……),我以為搞個文件的絕對路徑就可以的,結果錯誤信息,當然是路徑沒有read()功能。

#改正: if os.path.isfile(account_file):     #如果用戶文件存在(即用戶存在)
    with open(account_file, "r", encoding="utf-8") as f:   #打開文件
        file_data = json.load(f) print(file_data)

運行正確,GOOD!

 

    3.如何把時間字符串轉化為時間戳(忘了的)

      先通過time.strptime()將時間字符串轉成struct_time格式,再通過time.mktime()將struct_time轉成時間戳。 

 

 

 11/4  文件形式

 

atm.py

""" ATM程序的執行文件 """
import os import sys dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))   #找到路徑
sys.path.insert(0, dir)     #添加路徑
print(dir) #將main.py里面所有代碼封裝成main變量
from core import main if __name__ == "__main__": #這里我剛開始用run()就爆錯了
    main.run()
View Code

 

settings.py

 1 """  2 初始化的配置  3 """  4  5 import logging  6 import os  7  8 #到ATM目錄,方便后面創建帳戶文件  9 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 10 11 LOGIN_LEVEL = logging.INFO #初始化日志級別 12 13 14 DATABASE = { 15 "db_tool":"file_storage", #文件存儲,這里可拓展成數據庫形式的 16 "name":"accounts", #db下的文件名 17 "path":"%s/db" % BASE_DIR 18 }
View Code

 

auth.py

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

 

db_handle.py

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

 

main.py

 1 """
 2 主邏輯交互模塊  3 """
 4 from core import auth  5 
 6 
 7 #用戶數據信息
 8 user_data = {  9     'account_id':None,          #帳號ID
10     'is_authenticated':False,  #是否認證
11     'account_data':None        #帳號數據
12 
13 } 14 
15 
16 def account_info(): 17     """
18  用戶帳戶信息 19  :return: 20     """
21     pass
22 
23 
24 def repay(): 25     """
26  還款 27  :return: 28     """
29     pass
30 
31 
32 def withdraw(): 33     """
34  退出 35  :return: 36     """
37     pass
38 
39 
40 def transfer(): 41     """
42  轉帳 43  :return: 44     """
45     pass
46 
47 
48 def paycheck(): 49     """
50  轉帳檢查 51  :return: 52     """
53     pass
54 
55 
56 def logout(): 57     """
58  退出登陸 59  :return: 60     """
61     pass
62 
63 
64 def interactive(): 65     """
66  交互 67  :return: 68     """
69     pass
70 
71 
72 
73 def run(): 74     """
75  當程序啟動時調用,用於實現主要交互邏輯 76  :return: 77     """
78     access_data = auth.access_login(user_data)   #調用認證模塊,返回用戶文件json.load后的字典
79     print("AA")
View Code

 

zcl.json

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

   測試結果1:

C:\Users\Administrator\PycharmProjects\laonanhai\ATM BB Account:zcl Password:abc C:\Users\Administrator\PycharmProjects\laonanhai\ATM/db/accounts C:\Users\Administrator\PycharmProjects\laonanhai\ATM/db/accounts C:\Users\Administrator\PycharmProjects\laonanhai\ATM/db/accounts/zcl.json {'id': 22, 'credit': 15000, 'password': 'abc', 'status': 0, 'expire_date': '2012-01-01', 'enroll_date': '2016-01-02', 'pay_day': 22, 'balance': 15000} 1325347200.0 time.struct_time(tm_year=2012, tm_mon=1, tm_mday=1, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=6, tm_yday=1, tm_isdst=-1) Account zcl had expired,Please contract the bank Account:
View Code

  測試結果2:

C:\Users\Administrator\PycharmProjects\laonanhai\ATM BB Account:zcl Password:abc C:\Users\Administrator\PycharmProjects\laonanhai\ATM/db/accounts C:\Users\Administrator\PycharmProjects\laonanhai\ATM/db/accounts C:\Users\Administrator\PycharmProjects\laonanhai\ATM/db/accounts/zcl.json {'expire_date': '2021-01-01', 'status': 0, 'enroll_date': '2016-01-02', 'balance': 15000, 'id': 22, 'password': 'abc', 'credit': 15000, 'pay_day': 22} 1609430400.0 time.struct_time(tm_year=2021, tm_mon=1, tm_mday=1, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=4, tm_yday=1, tm_isdst=-1) return Account:
View Code

 

  OK, 今日退朝,明日繼續!

 

思路總結-1:

  1.amt.py運行時調用core目錄下的main.py

  2.main.py調用core目錄下的auth.py進行登陸和信用卡認證

  3.auth.py調用db_handle.py,返回帳號文件的路徑,進行信用卡帳戶檢查

  4.auth.py進行登陸認證,登陸失敗不能超過三次(未完全實現)

 

 

 

2016/11/5  10:38

  今天完善了登陸功能,實現了日志功能,寫了一點點交互功能,比如查看帳戶信息,自我感覺良好。

 

學到新技能:

  1.看下面這段代碼

 1 msg = (
 2         """
 3         -------------ZhangChengLiang Bank---------------
 4         \033[31;1m 1.  賬戶信息
 5         2.  還款
 6         3.  取款
 7         4.  轉賬
 8         5.  賬單
 9         6.  退出
10         \033[0m"""
11     )
12     menu_dic = {
13         "1":account_info,
14         "2":repay,
15         "3":withdraw,
16         "4":transfer,
17         "5":paycheck,
18         "6":logout
19     }
20     flag = False
21     while not flag:
22         print(msg)
23         choice = input("<<<:").strip()
24         if choice in menu_dic:
25             #很重要!!省了很多代碼,不用像之前一個一個判斷!
26             menu_dic[choice](acc_data)
27 
28         else:
29             print("\033[31;1mYou choice doesn't exist!\033[0m")
View Code

 

當然,在面向對象可以用反射(我還沒有過),但這里我感覺用得相當巧!!不用像我之前一個一個用if...else..來判斷。

 

  2.掌握日志的用法

之前只是知道,寫單獨的一個文件,這次綜合的運用,感覺我對日志的用法已經相當有信心了。

 

16/11/5  文件目錄

 

 與昨天的基本沒有什么變化,只是運行時在log包下多了個access.log文件還有在core包下多了log.py文件

 

11/5  代碼展示

 

atm.py 不變

 

settings.py

  只多了個LOGIN_TYPE字典,用於后面生成access.log日志文件

 1 """
 2 初始化的配置  3 """
 4 
 5 import logging  6 import os  7 
 8 #到ATM目錄,方便后面創建帳戶文件
 9 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 10 
11 LOGIN_LEVEL = logging.INFO    #初始化日志級別
12 
13 LOGIN_TYPE = { 14     "access":"access.log"
15 } 16 
17 
18 DATABASE = { 19     "db_tool":"file_storage",   #文件存儲,這里可拓展成數據庫形式的
20     "name":"accounts",          #db下的文件名
21     "path":"%s/db" % BASE_DIR 22 }
View Code

 

auth.py

  完善了登陸功能,日志功能

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

 

db_handle.py不變

 

log.py

   實現日志功能,我里我先文件和屏幕都有輸出。

 1 import logging  2 from conf import settings  3 
 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對象
34 
35     # logger.debug('debugmessage')
36     # logger.info('infomessage')
37     # logger.warn('warnmessage')
38     # logger.error('errormessage')
39     # logger.critical('criticalmessage')
View Code

 

main.py

  增加了一點用交互功能(未完成)

 1 """
 2 主邏輯交互模塊  3 """
 4 from core import auth  5 from core import log  6 
 7 
 8 #用戶數據信息
 9 user_data = {  10     'account_id':None,          #帳號ID
 11     'is_authenticated':False,  #是否認證
 12     'account_data':None        #帳號數據
 13 
 14 }  15 
 16 #調用log文件下的log方法,返回日志對象
 17 access_logger = log.log("access")  18 
 19 
 20 def account_info(acc_data):  21     """
 22  查看用戶帳戶信息  23  :return:  24     """
 25     print(acc_data)  26 
 27 
 28 def repay(acc_data):  29     """
 30  還款  31  :return:  32     """
 33     pass
 34 
 35 
 36 def withdraw():  37     """
 38  退出  39  :return:  40     """
 41     pass
 42 
 43 
 44 def transfer():  45     """
 46  轉帳  47  :return:  48     """
 49     pass
 50 
 51 
 52 def paycheck():  53     """
 54  轉帳檢查  55  :return:  56     """
 57     pass
 58 
 59 
 60 def logout():  61     """
 62  退出登陸  63  :return:  64     """
 65     pass
 66 
 67 
 68 def interactive(acc_data):  69     """
 70  用戶交互  71  :return:  72     """
 73     msg = (  74         """
 75  -------------ZhangChengLiang Bank---------------  76  \033[31;1m 1. 賬戶信息  77  2. 還款  78  3. 取款  79  4. 轉賬  80  5. 賬單  81  6. 退出  82  \033[0m"""
 83  )  84     menu_dic = {  85         "1":account_info,  86         "2":repay,  87         "3":withdraw,  88         "4":transfer,  89         "5":paycheck,  90         "6":logout  91  }  92     flag = False  93     while not flag:  94         print(msg)  95         choice = input("<<<:").strip()  96         if choice in menu_dic:  97             #很重要!!省了很多代碼,不用像之前一個一個判斷!
 98  menu_dic[choice](acc_data)  99 
100         else: 101             print("\033[31;1mYou choice doesn't exist!\033[0m") 102 
103 
104 
105 def run(): 106     """
107  當程序啟動時調用,用於實現主要交互邏輯 108  :return: 109     """
110     # 調用認證模塊,返回用戶文件json.load后的字典,傳入access_logger日志對象
111     access_data = auth.access_login(user_data, access_logger) 112     print("AA") 113     if user_data["is_authenticated"]:       #如果用戶認證成功
114         print("has authenticated") 115         #將用戶文件的字典賦給user_data["account_data"]
116         user_data["account_data"] = access_data 117         interactive(user_data)   #用戶交互開始
View Code

 

 

16/5 測試用例

 

 1 C:\Users\Administrator\PycharmProjects\laonanhai\ATM
 2 BB
 3 Account:123
 4 Password:333
 5 2016-11-05 23:03:55,210-access-ERROR-Account [123] does not exist!
 6 C:\Users\Administrator\PycharmProjects\laonanhai\ATM/db/accounts
 7 C:\Users\Administrator\PycharmProjects\laonanhai\ATM/db/accounts
 8 C:\Users\Administrator\PycharmProjects\laonanhai\ATM/db/accounts/123.json
 9 Account [123] does not exist!
10 Account:zcl
11 Password:abc
12 C:\Users\Administrator\PycharmProjects\laonanhai\ATM/db/accounts
13 C:\Users\Administrator\PycharmProjects\laonanhai\ATM/db/accounts
14 C:\Users\Administrator\PycharmProjects\laonanhai\ATM/db/accounts/zcl.json
15 {'balance': 15000, 'enroll_date': '2016-01-02', 'expire_date': '2021-01-01', 'status': 0, 'id': 22, 'password': 'abc', 'credit': 15000, 'pay_day': 22}
16 2016-11-05 23:04:08,977-access-INFO-Account [zcl] logging success
17 1609430400.0
18 time.struct_time(tm_year=2021, tm_mon=1, tm_mday=1, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=4, tm_yday=1, tm_isdst=-1)
19 return
20 welcome
21 AA
22 has authenticated
23 
24         -------------ZhangChengLiang Bank---------------
25          1.  賬戶信息
26         2.  還款
27         3.  取款
28         4.  轉賬
29         5.  賬單
30         6.  退出
31         
32 <<<:1
33 {'account_data': {'balance': 15000, 'enroll_date': '2016-01-02', 'expire_date': '2021-01-01', 'status': 0, 'id': 22, 'password': 'abc', 'credit': 15000, 'pay_day': 22}, 'is_authenticated': True, 'account_id': 'zcl'}
34 
35         -------------ZhangChengLiang Bank---------------
36          1.  賬戶信息
37         2.  還款
38         3.  取款
39         4.  轉賬
40         5.  賬單
41         6.  退出
View Code

 

思路總結-2:

 

  1.log.py設置日志級別、生成日志對象,返回logger日志對象

  2.main.py在調用auth.py認證模塊時,傳logger對象

  3.通過logger對象進行一系列操作

  4.main.py中run()方法調用interactive交互接口

  5.interactive()方法通過用戶選擇進行交互(大部分還未實現)

 

11/5 OK,明日繼續!

 

 

 

11/6  半夜三更

 

   今天都在實現用戶的交互功能,我只實現了存款功能repay,別看只實現了一個,我拓展性搞得還可以的!其他未實現,但感覺套路都是差不多的。

 

 

(重要)思路VS思考

 

在實現用戶交互功能之一(存款)時,我的思考如下:

  1. 在main.py下的repay()方法中搞個while循環,讓用戶輸入存款的金額
  2. 接着我須要搞一個模塊來處理存款的數據,比如說將用戶更新后的balance寫到用戶數據庫中
  3. 再想,不對!按2的說法,我同樣也需要為提現寫一個模塊,轉帳寫一個模塊,取款寫一個模塊…,因為這些交互都使用戶的金額數據產生變化
  4. 按第3的做法,拓展性不好,也是要我的老命啊! 那我應該搞一個通用的模塊,於是我寫了transaction.py來處理用戶交易
  5. 接下來注意了,這個點我想了很久!
  • 在transaction.py中的make_transaction() 方法中我傳的是用戶信息的字典(登陸時從用戶數據庫中load出來的)
  • 在make_transaction()寫到差不多時,我意識到需要另寫一個模塊來處理用戶更新后的信息,寫到用戶數據庫中去(用戶每進行數據的操作,都要及時寫都數據庫中去)
  • 於是我建了account.py模塊,在模塊寫了dump_account()方法
  • 問題來了,當我第二次存款時,發現我第一次存的錢不見了!比如我原來有15000,第一次存500,第二次想再存時,發現帳戶金額還是15000,擦,尷尬了。
  • 分析這個問題,很簡單,原因在於我第二次調用repay()時沒有從用戶數據庫中load()出來,導致內存中用戶數據不變(當然make_transaction中的用戶數據是變的,才能將更新后的用戶數據寫到數據庫中去,不過這兩份用戶數據只是實參與形參,無直接關系,不影響),在repay中打印出來的balance/credit是不變的!!
  • finally,我在每次調用repay時,都先將用戶信息讀出來,就完美解決了!      

 

  6. 到這里我大悟,每次用戶調用ATM交互功能,應先將用戶信息讀出來,每一個功能結束后都應將信息更新到數據庫中去

 

 

11/6 新技能

 

1.用戶交易模塊的拓展性驚人

def make_transaction(account_data, transaction_type, amount): 這里的transaction_type交易類型(存,取,轉…)

 

在settings.py定義好交易的類型,拓展性很好!

1 #用戶交易類型,每個類型對應一個字典,包括帳戶金額變動方式,利息
2 TRANSACTION_TYPE = { 3     "repay":{"action":"plus", "interest":0},  #存款
4     "withdraw":{"action":"minus", "interest": 0.05}  #取款(提現)
5 }



2. if num == "b" or "back"

  這里我SB了,哈哈~

1 num = input(">>>>:") 2 if num == "b" or "back": 3     print("OK") 4 else: 5     print("ERROR")

這里我測試時,明明輸入的不是"b" 或者"back", 但結果卻是“OK", 一度讓我懷疑人生,於是我做了上面的測試。

仔細一想, or "back",“back” 不為0,所以這個判斷為True, 一定輸出的是"OK".      .......以后再也不敢了~

 

11/6  文件目錄

 

  與11/5多了transaction.py模塊,account.py模塊,test.py是我自己測試用的,可以忽略~

 

 

代碼展示:

main.py

  實現了還款功能

  1 """
  2 主邏輯交互模塊
  3 """
  4 from core import auth
  5 from core import log
  6 from core import transaction
  7 from core import account
  8 
  9 
 10 #用戶數據信息
 11 user_data = {
 12     'account_id':None,          #帳號ID
 13     'is_authenticated':False,  #是否認證
 14     'account_data':None        #帳號數據
 15 
 16 }
 17 
 18 #調用log文件下的log方法,返回日志對象
 19 access_logger = log.log("access")
 20 
 21 
 22 def account_info(acc_data):
 23     """
 24     acc_data:包括ID,is_authenticaed,用戶帳號信息
 25     查看用戶帳戶信息
 26     :return:
 27     """
 28     print(acc_data)
 29 
 30 
 31 def repay(acc_data):
 32     """
 33     acc_data:包括ID,is_authenticaed,用戶帳號信息
 34     還款
 35     :return:
 36     """
 37     print(acc_data)
 38     print("??")
 39     #調用account模塊的load_account方法,從數據庫從load出用戶信息
 40     account_data = account.load_account(acc_data["id"])
 41     print(account_data)
 42     current_balance = """
 43     -------------BALANCE INFO--------------
 44     Credit:%s
 45     Balance:%s
 46     """ % (account_data["credit"], account_data["balance"])
 47     back_flag = False
 48     while not back_flag:
 49         print(current_balance)
 50         repay_amount = input("\033[31;1mInput repay amount(b=back):\033[0m").strip()
 51         #如果用戶輸入整型數字
 52         if len(repay_amount) > 0 and repay_amount.isdigit():
 53             #調用transaction模塊的方法,參數分別是用戶帳戶信息,交易類型,交易金額
 54             new_account_data = transaction.make_transaction(account_data, "repay", repay_amount)
 55             if new_account_data:
 56                 print("\033[42;1mNew Balance:%s\033[0m" % new_account_data["balance"])
 57 
 58         else:
 59             print("\033[31;1m%s is not valid amount,Only accept interger!\033[0m" % repay_amount)
 60 
 61         if repay_amount =="b" or repay_amount == "back":
 62             back_flag = True
 63 
 64 def withdraw():
 65     """
 66     取款
 67     :return:
 68     """
 69     pass
 70 
 71 
 72 def transfer():
 73     """
 74     轉帳
 75     :return:
 76     """
 77     pass
 78 
 79 
 80 def paycheck():
 81     """
 82     轉帳檢查
 83     :return:
 84     """
 85     pass
 86 
 87 
 88 def logout():
 89     """
 90     退出登陸
 91     :return:
 92     """
 93     pass
 94 
 95 
 96 def interactive(acc_data):
 97     """
 98     用戶交互
 99     :return:
100     """
101     msg = (
102         """
103         -------------ZhangChengLiang Bank---------------
104         \033[31;1m 1.  賬戶信息
105         2.  存款
106         3.  取款
107         4.  轉賬
108         5.  賬單
109         6.  退出
110         \033[0m"""
111     )
112     menu_dic = {
113         "1":account_info,
114         "2":repay,
115         "3":withdraw,
116         "4":transfer,
117         "5":paycheck,
118         "6":logout
119     }
120     flag = False
121     while not flag:
122         print(msg)
123         choice = input("<<<:").strip()
124         if choice in menu_dic:
125             #很重要!!省了很多代碼,不用像之前一個一個判斷!
126             menu_dic[choice](acc_data)
127 
128         else:
129             print("\033[31;1mYou choice doesn't exist!\033[0m")
130 
131 
132 
133 def run():
134     """
135     當程序啟動時調用,用於實現主要交互邏輯
136     :return:
137     """
138     # 調用認證模塊,返回用戶文件json.load后的字典,傳入access_logger日志對象
139     access_data = auth.access_login(user_data, access_logger)
140     print("AA")
141     if user_data["is_authenticated"]:       #如果用戶認證成功
142         print("has authenticated")
143         #將用戶文件的字典賦給user_data["account_data"]
144         user_data["account_data"] = access_data
145         interactive(user_data)   #用戶交互開始
View Code

 

settings.py

  增加了用戶交易類型TRANSACTION_TYPE{}

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

 

transaction.py

  處理用戶交易的模塊

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

 

account.py

  將用戶信息讀出或寫入的模塊

 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

 

測試用例 

 1 C:\Users\Administrator\PycharmProjects\laonanhai\ATM
 2 BB
 3 Account:zcl
 4 Password:abc
 5 C:\Users\Administrator\PycharmProjects\laonanhai\ATM/db/accounts
 6 C:\Users\Administrator\PycharmProjects\laonanhai\ATM/db/accounts
 7 C:\Users\Administrator\PycharmProjects\laonanhai\ATM/db/accounts/zcl.json
 8 {'balance': 26000.0, 'expire_date': '2021-01-01', 'credit': 15000, 'status': 0, 'enroll_date': '2016-01-02', 'password': 'abc', 'pay_day': 22, 'id': 'zcl'}
 9 1609430400.0
10 time.struct_time(tm_year=2021, tm_mon=1, tm_mday=1, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=4, tm_yday=1, tm_isdst=-1)
11 return
12 2016-11-07 13:42:24,678-access-INFO-Account [zcl] logging success
13 welcome
14 AA
15 has authenticated
16 
17         -------------ZhangChengLiang Bank---------------
18          1.  賬戶信息
19         2.  存款
20         3.  取款
21         4.  轉賬
22         5.  賬單
23         6.  退出
24         
25 <<<:2
26 {'is_authenticated': True, 'account_data': {'balance': 26000.0, 'expire_date': '2021-01-01', 'credit': 15000, 'status': 0, 'enroll_date': '2016-01-02', 'password': 'abc', 'pay_day': 22, 'id': 'zcl'}, 'account_id': None, 'id': 'zcl'}
27 ??
28 C:\Users\Administrator\PycharmProjects\laonanhai\ATM/db/accounts
29 {'balance': 26000.0, 'expire_date': '2021-01-01', 'credit': 15000, 'status': 0, 'enroll_date': '2016-01-02', 'password': 'abc', 'pay_day': 22, 'id': 'zcl'}
30 
31     -------------BALANCE INFO--------------
32     Credit:15000
33     Balance:26000.0
34     
35 Input repay amount(b=back):4500
36 0.0 26000.0
37 C:\Users\Administrator\PycharmProjects\laonanhai\ATM/db/accounts
38 dump success
39 New Balance:30500.0
40 
41     -------------BALANCE INFO--------------
42     Credit:15000
43     Balance:26000.0
44     
45 Input repay amount(b=back):back
46 back is not valid amount,Only accept interger!
47 
48         -------------ZhangChengLiang Bank---------------
49          1.  賬戶信息
50         2.  存款
51         3.  取款
52         4.  轉賬
53         5.  賬單
54         6.  退出
55         
56 <<<:2
57 {'is_authenticated': True, 'account_data': {'balance': 26000.0, 'expire_date': '2021-01-01', 'credit': 15000, 'status': 0, 'enroll_date': '2016-01-02', 'password': 'abc', 'pay_day': 22, 'id': 'zcl'}, 'account_id': None, 'id': 'zcl'}
58 ??
59 C:\Users\Administrator\PycharmProjects\laonanhai\ATM/db/accounts
60 {'balance': 30500.0, 'expire_date': '2021-01-01', 'credit': 15000, 'status': 0, 'enroll_date': '2016-01-02', 'password': 'abc', 'pay_day': 22, 'id': 'zcl'}
61 
62     -------------BALANCE INFO--------------
63     Credit:15000
64     Balance:30500.0
65     
66 Input repay amount(b=back):10000
67 0.0 30500.0
68 C:\Users\Administrator\PycharmProjects\laonanhai\ATM/db/accounts
69 dump success
70 New Balance:40500.0
71 
72     -------------BALANCE INFO--------------
73     Credit:15000
74     Balance:30500.0
75     
76 Input repay amount(b=back):
View Code

 

 

思路總結-3 

  1.repay還款功能調用account.py通過ID讀出用戶信息

  2.repay還款功能調用transaction.py交易模塊

  3.transaction.py交易模塊調用account.py更新用戶數據到數據庫

  4.settings.py里的TRANSACTION_TYPE字典傳入交易方法,實現拓展性

 

今日超神,明日繼續實現交互功能或體息一天!

 

 

11/8

 做了還款,取款,轉帳功能,都是套路了,難度不大。

咋晚給舍友看了我做了東西,竟然被舍友說,亮點在哪里。時間有限,生命有限,繼續學習之路,這個小項目就到此為止了。

ATM壓縮包:http://pan.baidu.com/s/1hrNF5TM

 


免責聲明!

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



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