需求:
模擬實現一個ATM + 購物商城程序
- 額度 15000或自定義
- 實現購物商城,買東西加入 購物車,調用信用卡接口結賬
- 可以提現,手續費5%
- 每月22號出賬單,每月10號為還款日,過期未還,按欠款總額 萬分之5 每日計息
- 支持多賬戶登錄
- 支持賬戶間轉賬
- 記錄每月日常消費流水
- 提供還款接口
- ATM記錄操作日志
- 提供管理接口,包括添加賬戶、用戶額度,凍結賬戶等。。
此需求因第一次接觸,所以全是借鑒網友的blog
一、程序具體說明:
一、主程序day5_credit_card.py開始1、生成today今天的日期和星期數weekoftoday2、定義currusers:實例化modules下的users.py中的類Users3、初始化對賬單report.create_statement_main()(1)、從database目錄下的文件creditcard中讀取信用卡列表(2)、循環列表調用 create_card_statement(3)、create_card_statement函數說明3.1 獲取當前日期currday和today3.2 如果當天是22號出帳日:3.2.1 賦值startday,endday,startdate,enddate,statement_key出帳起始結束時間3.2.2 獲取卡號對應的消費流水列表dbapi.load_bill_report(cardno, startdate, enddate)3.2.3 statement_dict的信息追加到信用卡帳單信息中。4、循環菜單(1)、判斷用戶是否登錄,并輸出相應用戶信息未登錄輸出template.index_default_menu.format("", today, common.numtochr(weekoftoday))否則輸出template.index_logined_menu.format("歡迎您: {0}".format(curruser.name), today,common.numtochr(weekoftoday))(2)、輸出用戶選擇菜單(3)、輸入5,則退出菜單(4)、輸入1,首先加載當年用戶信息curruser.db_load(),然后調用doshopping(curruser)(5)、輸入2,首先加載當年用戶信息curruser.db_load(),然后調用user_login(curruser, today, weekoftoday)(6)、輸入3,調用card_center(curruser)信用卡中心(7)、輸入4,調用manager(curruser)后臺管理5、菜單1:doshopping商城函數(1)、實例化商城,調用shopping.Shopping()(2)、循環開始,輸出商品菜單shoppobj.welcome_menu(3)、輸入編號選擇并賦值shop_cassify_id,并進行輸入驗證判斷(4)、輸入值為4,進行購物車商品顯示shopping.Shopping.print_goods_list(shoppobj.shopping_cart)并顯示common.show_message("當前購物車共有 {0} 件商品,合計 {1} 元)(5)、輸入值為5,shoppobj.payfor_shopcart(userobj),無錯誤,輸出支付成功(6)、輸入值為1-3,則調用shoppobj.get_goods_list_by_typeid(),顯示商品分列表(7)、第二層循環開始,并顯示指定商品分類下的所有商品列表(Shopping類靜態方法)shopping.Shopping.print_goods_list(goods_list)(8)、選擇商品編號goods_id,加入購物車,并對輸入進行判斷8.1 如果輸入為q,返回上一層8.2 調用商品加入購物車函數shoppobj.add_shopping_card(goods_id)8.3 如果返回正確,則顯示購物車所有商品信息shopping.Shopping.print_goods_list(shoppobj.shopping_cart),并輸出common.show_message("已將商品加入購物車!", "INFORMATION")第三層循環開始,顯示“繼續購物(y) or 返回上一級(q)”,8.4 如果返回失敗,則輸出common.show_message("添加購物車失敗,請檢查輸入商品編號是否正確!", "ERROR"),并continue6、菜單2:user_login(curruser, today, weekoftoday)用戶登錄(1)、循環開始(2)、判斷用戶是否登錄成功userobj.islogin,2.1 如果已經登錄,則顯示個人中心菜單 template.index_user_center.format(userobj.name, today, common.numtochr(weekoftoday)) 2.2 輸出選擇功能菜單,選擇6,返回上級菜單2.3 如果選擇1-5,則通過反射來執行,從 template 模塊查找各按鍵對應的模塊func_dict = template.user_center_func2.4 根據輸出的數字,賦值相應的模塊名稱,modulepy = __import__(func_dict[_choose]["module"])2.5 如果選擇的是1,2,5,則賦值modulesobj 、classobj、func7、菜單3:card_center(curruser)信用卡中心(1)、判斷用戶是否登錄1.1 已登錄,則重新load一下數據userobj.db_load(),并獲取到綁定的信息卡信息cardno = userobj.bindcard1.2 獲得信用卡對象card = CreditCard(cardno)1.3 如果未登錄,則進行循環,讓用戶輸入信用卡和密碼進行認證(2)、顯示登錄輸出菜單index_ATM,循環開始:(3)、輸入common.input_msg("請選擇功能: ", ("1", "2", "3", "4", "5"))(4)、如果輸入是5,就返回上一層循環(5)、如果輸入是1,則查看信用卡信息(6)、如果輸入是2,則進行提現菜單6.1 如果card.frozenstatus狀態是1,則提示“卡已凍結,請聯系客服”6.2 否則提示“信用卡提現將收取 {0}% 的手續費!”6.3 循環開始,提示"請輸入要提現的金額(q返回):"6.4 輸入正確的話,提示輸入信用卡密碼進行確認6.5 執行提現函數card.fetch_money(float(cost), cardpasswd),并根據返回結果進行輸出相應信息(7)、如果輸入是3,則進入轉帳菜單7.1 如果card.frozenstatus狀態是1,則提示“卡已凍結,請聯系客服”7.2 否則提示“信用卡轉帳將收取 {0}% 的手續費!”7.3 循環開始,提示"請輸入要轉賬的卡號(q返回)"7.4 對卡號是數字,則生成一個trans_cardobj = CreditCard(trans_cardno)7.5 進行卡號判斷存在的話,提示是轉賬的金額,7.6 如果金額正確并輸入信用卡密碼進行確認7.5 執行轉帳函數card.translate_money(float(trans_cost), cardpasswd, trans_cardobj) ,并根據返回結果進行輸出相應信息 (8)、如果輸入是4,則進入還款菜單8.1 調用card.recreate_statement()函數,更新一下對賬單信息8.2 循環開始,獲取對賬單所有列表interest_list = card.load_statement_list()8.3 獲取還未還款的記錄并顯示message_info = report.print_statement_list(card.cardno, interest_list)8.4 如果有要還款的記錄,則顯示具體帳單信息8.5 請選擇還款的16位賬單號,然后進行判斷,8.6 輸出正確,則顯示指定單號的相應對賬單信息report.print_statement_detail8.7 輸入還款金額,并更新已還款金額 = 現在還的金額 + 已經還的金額8.8 需要還款數 = 消費總費用 + 利息 ,并判斷是否已還清,如果還清則設置interest_list[i][pay_serno]["isfinished"] = 18.9 如果未還清,則顯示“您尚未全部還款,請在還款日前盡快還款!”8.10 將還款后的信息寫入數據庫更新dbapi.write_statement_list(card.cardno, interest_list)8.11 顯示是否繼續還款。8、菜單4:主菜單后臺管理模塊manager(userobj):(1)、用戶是否登錄,是否是角色是否是admin(2)、是admin,進入循環,顯示template.index_admin(3)、選擇菜單,輸入1時,進入創建新用戶調用User類中的init_user_info()函數(4)、選擇菜單,輸入2時,進入刪除用戶菜單調用get_users()函數4.1 get_users() 顯示用戶的信息,用戶新建、刪除、解鎖用戶時顯示用戶基本信息4.2 輸入操作的用戶名4.3 創建一個用戶實例_deluser = Users(),并賦值_deluser.username = username4.4 _deluser.load_user_info()判斷,如果用戶名存在,load用戶信息成功,顯示用戶信息,返回用戶4.5 用戶不存在,返回失敗4.6 調用_user.del_user(),并顯示刪除用戶成功(5)、選擇菜單,輸入3時,進行鎖定用戶菜單調用get_users()函數,方式同上(6)、選擇菜單,輸入4時,進入發行信用卡菜單6.1 調用newcard = fill_card_info()函數,填充信用卡資料信息,返回一個信用卡對象6.2 循環開始,輸入卡號,并判斷卡號是否存在,不存在退出循環6.3 在依次輸入密碼,額度等信息,并返回一個信用卡對象(7)、選擇菜單,輸入5時,進入凍結信用卡7.1 輸入要操作卡號7.2 card = CreditCard(cardno),實例化信用卡7.3 判斷信用卡是否存在card.card_is_exists7.4 存在則輸入卡信息,并在次確定是否凍結7.5 card.frozenstatus = 1設置卡的標志位,card.update_card()更新信用卡文件(8)、選擇菜單,輸入0時,返回上層菜單二、包conf說明:1、errorcode.py介紹:定義系統錯誤代碼表:NO_ERROR,USER_NOT_EXISTS,CARD_NOT_BINDED,BALANCE_NOT_ENOUGHT,CARD_OWNER_ERROR,CARD_PASS_ERROR2、settings.py介紹:定義一系列變量值2.1 定義程序文件主目錄BASE_DIR,并加入環境變量2.2 定義數據庫信息DATABASE2.3 定義日志文件存放路徑LOG_PATH2.4 定義賬單報表文件路徑REPORT_PATH2.5 定義用戶登錄失敗最大次數ERROR_MAX_COUNT2.6 定義日息費率EXPIRE_DAY_RATE2.7 定義轉賬、提現手續費FETCH_MONEY_RATE2.8 定義信用額度CREDIT_TOTAL2.9 定義每月賬單日期STATEMENT_DAY3、template.py介紹:該模塊用來定義系統的菜單模板3.1 主程序中的主菜單index_default_menu3.2 主程序中的用戶登錄后的顯示菜單index_logined_menu3.3 主程序中的用戶中心菜單index_user_center3.4 用戶中心按鍵對應功能模塊user_center_func3.5 購物模塊的主菜單, menu 菜單在shopping模塊中內部構造shopping_index_menu 3.6 賬單報表顯示模板report_bill3.7 購物歷史記錄顯示模板shopping_history3.8 后臺管理模板index_admin3.9 ATM 管理模塊index_ATM3.10 顯示用戶基本信息模板user_info3.11 顯示信用卡基本信息模板card_info3.12 信用卡對賬單列表模板report_statement_list3.13 信用卡對賬單詳細模板report_statement_detail三、包database介紹:1、init_db.py介紹:1.1 定義商品列表_shopping_list1.2 定義用戶列表_user_list1.3 定義信用卡列表_creditcard_list1.4 函數init_db_shoppingmark() 初始化購物商城數據表 shoppingmark.db1.5 函數init_db_users() 初始化用戶數據表 users.db1.6 函數init_db_creditcard() 初始化信用卡數據表 creditcard.db1.7 執行init_database(),通過反射初始化數據表分別調用以上三個函數四、包dbhelper介紹:1、dbapi.py介紹:1.1 函數append_db_json(contant, filename),將信息以 json 格式寫入數據表文件(追加)1.2 函數write_db_json(contant, filename),將信息以 json 格式寫入數據表文件(覆蓋)1.3 函數load_data_from_db(tabename),從指定的數據表中獲取所有數據,通過 json 方式將數據返回1.4 函數load_bill_report(cardno, startdate, enddate),從信用卡對賬單中獲取指定卡的對賬信息數據, 獲取某一個時間段內的數據1.5 函數load_shop_history(user, startdate, enddate),查找報表記錄中的指定用戶的購物歷史記錄,將結果存入到列表中1.6 函數load_statement_list(cardno),從對賬單中獲取記錄1.7 函數write_statement_list(cardno, db_list),將對賬單記錄寫入對賬單文件,更新五、包modules介紹: 1、users.py介紹之類Users:1、定義私有變量:__database 值為database下的user.db2、定義用戶的靜態變量3、定義動態方法db_load(self.dict_user = dbapi.load_data_from_db(self.__database)),即調用dbapi模塊中的load_data_from_db方法來展示用戶信息4、定義login函數,輸入用戶名和密碼(1)、調用user_exists,判斷用戶是否存在,不存在則使用common.show_message進行異常顏色輸出。(2)、如存在則調用 用戶登錄模塊_user_login ,首先對輸入的密碼參數進行md5計算_password = common.encrypt(password),調用common模塊中的encrypt函數,并進行用戶信息的判斷的賦值(3)、判斷是否用戶被鎖定(4)、判斷用戶是否登錄成功,成功則break退出,失敗則輸出異常信息(5)、連續三次登錄失敗,則設置用戶鎖定標識為1,并update_user更新到user.db(6)、重置trycount 重置次數5、update_user即為將dict_user用戶列表信息進行回寫文件6、定義用戶存在函數user_exists、創建函數create_user、刪除函數del_user、鎖定函數unlock_user7、創建并init_user_info初始化用戶信息,輸入各種信息后,調用 create_user來生成用戶8、定義靜態方法user_auth,用于用戶登錄驗證裝飾器9、定義bind_card函數判斷卡綁定10、注銷用戶函數logout,將系統屬性置空11、個人中心 - 修改密碼函數modify_password12、修改用戶信息modify_user_info(1)、首先輸出當前的用戶信息(2)、輸入新的用戶信息(3)、輸入新的信用卡信息,并創建一個新的卡對象,調用CreditCard模塊:cardobj = CreditCard(4)、判斷信用卡是否存在(5)、輸入其他信息,并update_user回寫文件13、根據用戶名獲取用戶信息load_user_info2、shopping.py之類Shopping:1、定義私有變量:__welcome_title 菜單標題、__database 數據庫文件、__shop_report_file購物報表2、定義__init__: 特別定義方法(1)、獲取數據表數據self._get_shop_market()(2)、購物商城歡迎菜單self._construct_title_menu() 3、_get_shop_market方法:加載購物商品信息dbapi.load_data_from_db(shoppingmarket.db)4、_construct_title_menu方法:輸出購物商城菜單self.welcome_menu = self.__welcome_title.format(menu="".join(_menu))5、get_goods_list_by_typeid方法:根據用戶選擇的商品分類編號,獲取該分類下所有商品6、定義靜態方法print_goods_list:列表中的商品信息輸出到屏幕,商品列表或購物車商品列表(1)、輸出商品信息標題(2)、循環輸出商品具體信息7、定義方法add_shopping_card(self, goodsid):根據商品編號加入購物車(1)、定義變量_goods_tuple ,即具體的商品列表(2)、開始查找輸入的商品編號,并加入購物車列表中,并計算金額(3)、成功后break(4)、返回return 成功與否8、定義payfor_shopcart結算方法,并調用@Users.user_auth認證模塊作裝飾器購物車結算模塊,功能包括:購物車付款、購物記錄寫入文件(1)、判斷用戶是否綁定信用卡,如無,則返回錯誤,有,則繼續、(2)、實例化信用卡類cardobj = CreditCard(userobj.bindcard)(3)、判斷信用卡余額是否大于購買金額,如果不夠,輸出額度不夠,否則繼續(4)、調用common.create_serialno(),生成一個流水號(5)、調用卡的支付模塊進行支付cardobj.card_pay(self.shopping_cost, 1, serno)支付扣款 記錄消費流水對賬單,將發生了費用還沒有還款的賬單信息寫入文件 report_bill 中更新信用卡可透支余額信息到數據庫 creditcard.db(6)、記錄購物流水shopping_record,并寫入報表記錄文件shopping_history(7)、購物結算完成后將對象的購物車清空shopping_cart.clear(), 購物車商品總價清0 ,待下次購物(8)、返回錯誤代碼3、creditcard.py之類CreditCard:1、指定數據表__database的表creditcard2、定義信用卡額度,信用卡透支余額,信用卡日息,提現手續費率,信用卡狀態等變量3、定義_load_card_info函數,用戶輸入的卡號獲取信用卡信息4、定義card_is_exists函數,判斷輸入的信用卡是否存在5、定義card_pay(self, cost, paytype, sereialno)函數,信用卡支付,從信用卡可透支余額中扣費(1)、根據傳入的paytype的值,定義payfor的名稱,例:1:消費、2:轉賬、3:提現、4:手續費(2)、支付扣款self.credit_balance -= cost(3)、定義_tmp_bill_record,記錄消費流水對賬單(4)、將消費流水對賬單寫回到文件report_bill(5)、更新信用卡可透支余額信息到數據庫 creditcard.db6、定義新發行信用卡create_card函數,根據輸入的卡號密碼等信息并更新到creditcard.db7、定義信用卡更新update_card函數,根據輸入的卡號密碼等信息并更新到creditcard.db8、定義轉賬、提現時驗證操作_pay_check函數,轉賬、提現時驗證操作,判斷卡的余額與支付密碼是否正確。并 返回錯誤類型碼9、定義提現函數fetch_money(self, count, passwd)(1)、根據傳入的取現金額,計算取現+手續費總額(2)、調用_pay_check函數,根據返回值進行操作。(3)、如果返回值是errorcode.NO_ERROR,則調用card_pay函數將取現金額和手續費計帳,并回寫文件(4)、并返回errorcode.NO_ERROR10、定義信用卡轉賬函數translate_money(self, trans_count, passwd, trans_cardobj)(1)、根據傳入的轉帳金額,計算轉帳+手續費總額(2)、調用_pay_check函數,根據返回值進行操作。(3)、如果返回值是errorcode.NO_ERROR,則調用card_pay函數將轉帳金額和手續費計帳,并回寫文件(4)、并給對方卡充值,trans_cardobj.credit_balance += trans_count,并調用trans_cardobj.update_card()寫入數據庫文件(4)、并返回errorcode.NO_ERROR11、定義對賬單列表數據函數load_statement_list,調用dbapi.load_statement_list(self.cardno)12、定義recreate_statement函數,實現今天的日期將當前卡的對賬單重新生成,主要對過了還款日的賬單重新生 成利息信息(1)、獲取當前日期today(2)、獲取卡的對賬單信息card_statement = dbapi.load_statement_list(self.cardno)(3)、如果有記錄,進行循環讀取,并判斷isfinished字段是否是1,是則加記錄加到臨時列表tmp_list(4)、未還款,則獲取pdate還款時期,并判斷是否過期(5)、如果過期則計算利息:record[k]["interest"] = v["total"] * settings.EXPIRE_DAY_RATE * day_delta(6)、將過期或非過期的記錄都加到臨時列表tmp_list(7)、將更新過的列表寫入文件,替換原有信息dbapi.write_statement_list(self.cardno, tmp_list)4、common.py介紹:1、函數verification_code(),用來生成一個4位的驗證碼,并返回驗證碼2、函數encrypt(string),用來字符串加密3、函數write_log(content),用來寫錯誤日志4、函數get_chinese_num(uchar),用來計算漢字的個數5、函數show_message(msg, msgtype)根據msgtype類型,對print函數進行封裝,根據不同類型顯示不同顏色6、函數create_serialno(),用來生成一個消費、轉賬、提款時的流水號,不重復7、函數numtochr(num_of_weekday),將數字星期轉換為中文數字8、函數input_msg(message, limit_value=tuple()),判斷input輸入的信息是否為空的公共檢測函數,為空繼續輸入,不為空返回輸入的信息9、函數input_date(msg, default_date),對輸入的日期進行判斷是否正確 yyyy-mm-dd or yyyy-m-d5、report.py介紹: 賬單生成模塊1、導入calendar,timedelta等模塊2、函數get_date(),用來用戶輸入一個時間段,如果顯示報表是要提供開始、結束日期,返回開始,結束時間2.1 調用common.input_date來生成一個開始日期startdate2.2 調用common.input_date來生成一個結束日期enddate2.3 返回一個時間的字典3、函數print_shopping_history(userobj),個人中心 - 購物歷史記錄打印模塊4、函數print_bill_history(userobj) ,個人中心-賬單明細 打印模塊5、函數create_card_statement(cardno),生成信用卡對賬單6、函數create_statement_main(),卡對賬單初始化模塊,從卡數據庫文件中加載所有卡號,對所有卡調用生成對賬單模塊7、函數print_statement_list(cardno, list_info),將卡號對應的未還款記錄顯示出來8、函數print_statement_detail(cardno, serino, details),還款模塊 - 用戶選擇還款的單號后,顯示詳細的還款對賬單及流水信息程序說明
二、ATM 模擬程序說明
系統主程序為根目錄下的:day5_credit_card.py
1. 系統功能模塊
Day5_ATM 模擬程序是在python3.0 環境下開發
2. 系統目錄結構:
程序采用分層的方式編寫,包括數據庫訪問層、數據庫層(數據文件)、業務邏輯層(module 層),業務處理層(主程序)、
3.應用知識點:
a) 字典、列表、元組的操作
b) 文件的讀寫操作
c) 函數的應用
d) 類的使用
e) 裝飾器、反射
三、代碼如下:
1、主程序credit_card.py:


#!/usr/bin/env python # -*- encoding:utf-8 -*-import os,sysfrom datetime import date, datetime from conf import template, errorcode from conf import settings from modules import shopping, common from dbhelper import dbapi from modules.users import Users from modules.creditcard import CreditCard from modules import reportdef doshopping(userobj):"""購物商城模塊,進行購物部分的所有處理:param userobj: 一個用戶對象,如果用戶未登錄,在支付模塊會通過裝飾器來登錄:return:"""# 實例化商城shoppobj = shopping.Shopping()# 選擇商品類型exitflag = Falsewhile not exitflag:# 開始菜單print(shoppobj.welcome_menu)shop_cassify_id = input("請選擇商品分類編號[1-3]: ").strip().lower()if not shop_cassify_id: continueif shop_cassify_id == "0":exitflag = Truecontinueif int(shop_cassify_id) not in range(1, 6):common.show_message("請選擇正確的商品類型編號!", "ERROR")continueelif shop_cassify_id == "4":# 查看購物車 shopping.Shopping.print_goods_list(shoppobj.shopping_cart)common.show_message("當前購物車共有 {0} 件商品,合計 {1} 元 !".format(len(shoppobj.shopping_cart),shoppobj.shopping_cost), "INFORMATION")continueelif shop_cassify_id == "5":# 購物結算dealresult = shoppobj.payfor_shopcart(userobj)if dealresult == errorcode.NO_ERROR:common.show_message("支付完成!", "INFORMATION")else:# 獲取用戶選擇的商品類型編號shoppobj.goods_classify_id = shop_cassify_id# 獲得商品類型編號對應的商品列表goods_list = shoppobj.get_goods_list_by_typeid()if not goods_list:common.show_message("未找到商品信息!", "NOTICE")continue# 開始選擇商品,添加到購物車choose_goods_flag = Truewhile choose_goods_flag:# 顯示指定商品分類下的所有商品列表(Shopping類靜態方法) shopping.Shopping.print_goods_list(goods_list)goods_id = input("選擇商品編號,加入購物車(q返回上一級): ").strip().lower()if not goods_id: continue# 返回上一級if goods_id == "q":choose_goods_flag = Falsecontinueelse:# 將選擇商品加入購物車result = shoppobj.add_shopping_card(goods_id)if result:# 添加成功,顯示購物車所有商品信息 shopping.Shopping.print_goods_list(shoppobj.shopping_cart)common.show_message("已將商品加入購物車!", "INFORMATION")# 是否繼續添加nextflag = Falsewhile not nextflag:donext = input("繼續購物(y) or 返回上一級(q):").strip().lower()if donext == "y":breakelif donext == "q":choose_goods_flag = Falsebreakelse:continueelse:# 添加購物車失敗common.show_message("添加購物車失敗,請檢查輸入商品編號是否正確!", "ERROR")continuedef user_login(userobj, today, weekoftoday):"""主菜單的2號菜單登錄系統模塊:param userobj: 當前用戶對象:param today: 菜單顯示的日期:param weekoftoday: 菜單顯示的星期:return:"""quitflag = Falsewhile not quitflag:if userobj.islogin:# 如果用戶已經登錄,菜單功能2為個人中心,調用另一個菜單模板 index_user_centerprint(template.index_user_center.format(userobj.name, today, common.numtochr(weekoftoday)))_chooseflag = Falsewhile not _chooseflag:_choose = input("選擇功能:")if _choose not in ("1", "2", "3", "4", "5", "6"):common.show_message("選擇正確的功能編號!", "ERROR")continueelse:_chooseflag = True# 返回上級菜單if _choose == "6":quitflag = Trueelse:# 根據用戶按鍵開始處理,從 template 模塊查找各按鍵對應的模塊,通過反射來執行func_dict = template.user_center_funcmodulepy = __import__(func_dict[_choose]["module"])# 1,2,5號鍵為users類方法,if _choose in ('1', '2', '5'):modulesobj = getattr(modulepy, "users")classobj = getattr(modulesobj, "Users")func = getattr(classobj, func_dict[_choose]["func"])else:# 3,4為 report 模塊的方法modulesobj = getattr(modulepy, "report")func = getattr(modulesobj, func_dict[_choose]["func"])func(userobj)else:# 用戶未登錄,調用 Users類的登錄模塊 userobj.login()quitflag = Truedef card_center(userobj):if userobj.islogin:# 重新load一下數據 userobj.db_load()cardno = userobj.bindcard# 獲得信用卡對象card = CreditCard(cardno)else:# 未登錄信用卡input_flag = Falsewhile not input_flag:cardno = input("請輸入信用卡卡號: ").strip().lower()if cardno.isnumeric():card = CreditCard(cardno)if card.card_is_exists:pwd = input("請輸入密碼:")if common.encrypt(pwd) == card.password:common.show_message("登錄成功", "NOTICE")input_flag = Truecontinueelse:common.show_message("卡號不存在,請重新輸入!", "ERROR")continueelse:common.show_message("卡號無效!", "ERROR")continueshow_template = template.index_ATMquitflag = Falsewhile not quitflag:print(show_template.format(cardno=card.cardno))_choose = common.input_msg("請選擇功能: ", ("1", "2", "3", "4", "5"))# 返回if _choose == "5":quitflag = Truecontinue# 查看信用卡信息if _choose == "1":common.show_message(template.card_info.format(cardno=card.cardno,owner=card.owner,total=card.credit_total,balance=card.credit_balance,status="正常" if card.frozenstatus == 0 else "凍結"), "NOTICE")# 提現if _choose == "2":if card.frozenstatus == 1:common.show_message("卡已凍結,請聯系客服!", "ERROR")else:common.show_message("信用卡提現將收取 {0}% 的手續費!".format(settings.FETCH_MONEY_RATE * 100), "NOTICE")quitflag = Falsewhile not quitflag:cost = common.input_msg("請輸入要提現的金額(q返回):")if cost.isnumeric():cardpasswd = common.input_msg("請輸入信用卡密碼:")# 執行提現操作exe_result = card.fetch_money(float(cost), cardpasswd)if exe_result == errorcode.NO_ERROR:common.show_message("已完成提現!", "NOTICE")if exe_result == errorcode.BALANCE_NOT_ENOUGHT:common.show_message("信用卡可透支余額不足!", "ERROR")if exe_result == errorcode.CARD_PASS_ERROR:common.show_message("信用卡密碼錯誤!", "ERROR")elif cost == "q":quitflag = Truecontinueelse:common.show_message("輸入錯誤!", "ERROR")# 轉賬if _choose == "3":if card.frozenstatus == 1:common.show_message("此卡已凍結,請聯系客服!", "ERROR")else:common.show_message("信用卡轉賬將收取 {0}% 的手續費!".format(settings.FETCH_MONEY_RATE * 100), "NOTICE")quitflag = Falsewhile not quitflag:trans_cardno = common.input_msg("請輸入要轉賬的卡號(q返回):")if trans_cardno.isnumeric():# 生成一個卡對象, 驗證卡號是否存在trans_cardobj = CreditCard(trans_cardno)# 卡號不存在返回主菜單if not trans_cardobj.card_is_exists:common.show_message("卡號不存在,請確認!", "ERROR")quitflag = Truecontinueelse:# 卡號存在trans_cost = common.input_msg("請輸入要轉賬的金額: ")# 如果輸入的均為數字if trans_cost.isnumeric():comfirm = common.input_msg("確定要給卡號 {0} 轉入人民幣 {1} 元嗎(y/n)?".format(trans_cardobj.cardno,trans_cost),("y", "n"))if comfirm == "y":cardpasswd = common.input_msg("請輸入信用卡密碼:")# 執行轉賬操作exe_result = card.translate_money(float(trans_cost), cardpasswd, trans_cardobj)if exe_result == errorcode.NO_ERROR:common.show_message("轉賬完成!", "NOTICE")if exe_result == errorcode.BALANCE_NOT_ENOUGHT:common.show_message("信用卡可透支余額不足!", "ERROR")if exe_result == errorcode.CARD_PASS_ERROR:common.show_message("信用卡密碼錯誤!", "ERROR")else:common.show_message("輸入錯誤!", "ERROR")elif trans_cardno == "q":quitflag = Truecontinueelse:common.show_message("輸入錯誤!", "ERROR")# 還款if _choose == "4":# 更新一下對賬單信息 card.recreate_statement()quitflag = Falsewhile not quitflag:# 獲取對賬單所有列表interest_list = card.load_statement_list()# 獲取還未還款的記錄并顯示message_info = report.print_statement_list(card.cardno, interest_list)# 如果有要還款的記錄if len(message_info) > 0:common.show_message(message_info, "NOTICE")# 輸入要還款的單號serino_list = list()for order in interest_list:serino_list.append(list(order.keys())[0])serino_list.append("q")pay_serno = common.input_msg("請選擇還款的16位賬單號(q退出):", tuple(serino_list))if pay_serno == "q":quitflag = Truecontinueelse:for i in range(len(interest_list)):for k, details in interest_list[i].items():if k == pay_serno:# 顯示指定單號的相信對賬單信息 common.show_message(report.print_statement_detail(card.cardno,pay_serno,details),"NOTICE")pay_fee = common.input_msg("請輸入還款金額:")if pay_fee.isnumeric():# 更新已還款金額 = 現在還的金額 + 已經還的金額total_payed = details["payed"] + float(pay_fee)interest_list[i][pay_serno]["payed"] = total_payed# 全還了嗎?需要還款數 = 消費總費用 + 利息need_pay = details["total"] + details["interest"]if total_payed >= need_pay:# 還款數大于等于需要還款數,則更新已還款字段信息interest_list[i][pay_serno]["isfinished"] = 1else:# 沒全部還款common.show_message("您尚未全部還款,請在還款日前盡快還款!", "NOTICE")# 將還款后的信息寫入數據庫更新 dbapi.write_statement_list(card.cardno, interest_list)# 還款成功common.show_message("還款成功", "NOTICE")# 是否繼續iscontinue = common.input_msg("繼續還款嗎(y/n)?", ("y", "n"))if iscontinue == "n":quitflag = Trueelse:common.show_message("輸入數據不正確,請重新輸入!", "ERROR")else:common.show_message("無賬單信息!", "NOTICE")quitflag = Truedef get_users():"""顯示用戶的信息,用戶新建、刪除、解鎖用戶時顯示用戶基本信息:return:"""username = common.input_msg("請輸入用戶名:")# 創建一個用戶實例_deluser = Users()_deluser.username = username# 如果用戶名存在,load用戶信息成功if _deluser.load_user_info():# 先顯示一下用戶的信息common.show_message(template.user_info.format(username=_deluser.username,name=_deluser.name,mobile=_deluser.mobile,role=_deluser.role,isdel="否" if _deluser.isdel == 0 else "是",islocked="否" if _deluser.islocked == 0 else "是",bindcard=_deluser.bindcard), "NOTICE")return _deluserelse:common.show_message("用戶名不存在!", "ERROR")return Falsedef fill_card_info():"""填充信用卡資料信息:return: 返回一個信用卡對象"""retry_flag = Falsewhile not retry_flag:cardno = common.input_msg("請輸入卡號:")cardobj = CreditCard(cardno)if cardobj.card_is_exists:common.show_message("卡號已存在,請重新輸入卡號", "ERROR")continueelse:retry_flag = Truecontinuecardobj.password = common.input_msg("請輸入密碼:")cardobj.credit_total = common.input_msg("信用額度(default:{0}):".format(cardobj.credit_total))cardobj.credit_balance = cardobj.credit_totalcardobj.owner = common.input_msg("所有者:")return cardobjdef manager(userobj):"""主菜單后臺管理模塊:param userobj: 當前登錄用戶對象:return:"""if userobj.islogin:if userobj.role == "admin":quit_flag = Falsewhile not quit_flag:_show_template = template.index_adminprint(_show_template.format(username=userobj.name))_choose = input("選擇操作功能: ").strip().lower()# 創建新用戶if _choose == "1":_newuser = Users()# 調用初始化用戶函數創建新用戶 _newuser.init_user_info()# 刪除用戶if _choose == "2":_user = get_users()if _user:confirm = common.input_msg("確定要刪除此用戶嗎(y/n)?", ("y", "n"))if confirm == "y":_user.del_user()common.show_message("用戶刪除成功!", "NOTICE")# 解鎖用戶if _choose == "3":_user = get_users()if _user:confirm = common.input_msg("確認解鎖嗎(y/n)?", ("y", "n"))if confirm == "y":_user.unlock_user()common.show_message("用戶解鎖成功!", "NOTICE")# 發行信用卡if _choose == "4":newcard = fill_card_info()newcard.create_card()common.show_message("發卡成功!", "NOTICE")# 凍結信用卡if _choose == "5":cardno = common.input_msg("輸入卡號:")card = CreditCard(cardno)if not card.card_is_exists:common.show_message("卡號不存在", "ERROR")else:# 調用模板顯示卡信息common.show_message(template.card_info.format(cardno=card.cardno,owner=card.owner,total=card.credit_total,balance=card.credit_balance,status="正常" if card.frozenstatus == 0 else "凍結"), "INFORMATION")confirm = common.input_msg("確認要凍結此卡嗎(y/n)?", ("y", "n"))if confirm == "y":card.frozenstatus = 1card.update_card()common.show_message("此卡已凍結!", "NOTICE")# 退出if _choose == "0":quit_flag = Trueelse:# 不是 admin 角色無權限common.show_message("權限不夠!", "ERROR")else:common.show_message("請先登錄系統!", "NOTICE")userobj.login()if __name__ == "__main__":today = datetime.now().strftime("%Y-%m-%d")weekoftoday = date.weekday(datetime.now())curruser = Users()# 初始化對賬單 report.create_statement_main()# -------- 開始主程序 -------------------exitflag = Falsewhile not exitflag:# 如果用戶登錄了,顯示登錄的界面; 未登錄顯示未登錄的界面if not curruser.islogin:print(template.index_default_menu.format("", today, common.numtochr(weekoftoday)))else:print(template.index_logined_menu.format("歡迎您: {0}".format(curruser.name), today,common.numtochr(weekoftoday)))choose = common.input_msg("選擇功能編號[1-5]: ", ("1", "2", "3", "4", "5")).strip()if choose == "5":exitflag = Truecontinue# 1 購物商城if choose == "1":curruser.db_load()doshopping(curruser)# 2 用戶登錄if choose == "2":curruser.db_load()user_login(curruser, today, weekoftoday)# 3 信用卡管理if choose == "3":card_center(curruser)# 4 后臺管理if choose == "4":manager(curruser)credit_card
2、配置文件包conf下代碼:
? ? ?2.1 ?參數配置文件settings.py:


#!/usr/bin/env pythonimport os,sys# 程序文件主目錄 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # 添加環境變量 sys.path.append(BASE_DIR)# 數據庫信息 DATABASE = dict(engineer="file", dbpath=os.path.join(BASE_DIR, "database"), tables={"users": "users","shopping": "shoppingmark","creditcard": "creditcard"})# 日志文件存放路徑 LOG_PATH = os.path.join(BASE_DIR, "logs") # 賬單報表文件路徑 REPORT_PATH = os.path.join(BASE_DIR, "report") # 用戶登錄失敗最大次數 ERROR_MAX_COUNT = 3 # 日息費率 EXPIRE_DAY_RATE = 0.0005 # 轉賬、提現手續費 FETCH_MONEY_RATE = 0.05 # 信用額度 CREDIT_TOTAL = 10000 # 每月賬單日期(默認每月22日為賬單日) STATEMENT_DAY = 6settings
? ? ?2.2 ?界面顯示模板文件templates.py:


#!/usr/bin/env python """ 該模塊用來定義系統的菜單模板 """ # 主程序中的主菜單 index_default_menu = ''' -------------------------------------------------------------------------ATM 模擬程序{0} 今天 {1} 星期{2} ------------------------------------------------------------------------- 【1】進入商城 【2】登錄系統 【3】信用卡中心 【4】后臺管理 【5】退出系統 '''# 主程序中的用戶登錄后的顯示菜單 index_logined_menu = ''' -------------------------------------------------------------------------ATM 模擬程序{0} 今天 {1} 星期{2} ------------------------------------------------------------------------- 【1】進入商城 【2】用戶中心 【3】信用卡中心 【4】后臺管理 【5】退出系統 '''# 主程序中的用戶中心菜單 index_user_center = ''' -------------------------------------------------------------------------用戶中心當前用戶:{0} 今天 {1} 星期{2} ------------------------------------------------------------------------- 【1】修改密碼 【2】修改資料 【3】我的賬單 【4】購物記錄 【5】注銷 【6】返回'''# 用戶中心按鍵對應功能模塊 user_center_func = {"1": {"module": "modules.users", "func": "modify_password"},"2": {"module": "modules.users", "func": "modify_user_info"},"3": {"module": "modules.report", "func": "print_bill_history"},"4": {"module": "modules.report", "func": "print_shopping_history"},"5": {"module": "modules.users", "func": "logout"} }# 購物模塊的主菜單, menu 菜單在shopping模塊中內部構造 shopping_index_menu = '''=================================================================================== == 信用卡購物商城 == ==================================================================================={menu}'''# 賬單報表顯示模板 report_bill = ''' ------------------------------------------------------------------------------用戶中心 - 賬單明細卡號:{cardno} 賬單時間:{startdate} 至 {enddate} ------------------------------------------------------------------------------交易時間 交易類型 交易金額 流水號 {billdetail}'''# 購物歷史記錄顯示模板 shopping_history = ''' ------------------------------------------------------------------------------用戶中心 - 購物明細用戶:{username} 購物時間:{startdate} 至 {enddate} ------------------------------------------------------------------------------ '''# 后臺管理模板 index_admin = ''' ------------------------------------------------------------------------------后臺管理用戶:{username} ------------------------------------------------------------------------------ 【1】創建用戶 【2】刪除用戶 【3】解鎖用戶 【4】發行信用卡 【5】凍結信用卡 【0】退出后臺管理 '''# ATM 管理模塊 index_ATM = ''' ------------------------------------------------------------------------------信用卡管理中心卡號:{cardno} ------------------------------------------------------------------------------ 【1】我的信用卡 【2】提現 【3】轉賬 【4】還款 【5】返回 '''# 顯示用戶基本信息模板 user_info = ''' ------------------------------------------------------------------------------用戶基本信息用 戶 名:{username} 姓 名:{name} 手 機:{mobile} 用戶權限:{role} 是否鎖定:{islocked} 是否刪除:{isdel} 信用卡號:{bindcard} ------------------------------------------------------------------------------ '''# 顯示信用卡基本信息模板 card_info = ''' -----------------------------------------------------------------------------------信用卡基本信息卡號:{cardno} 所有人:{owner} 信用額度:{total} 剩余額度:{balance} 狀態:{status} ----------------------------------------------------------------------------------- ''' report_statement_list = ''' ----------------------------------------------------------------------------------信用卡對賬單列表 卡號:{cardno} ----------------------------------------------------------------------------------賬單號 還款日 應還款 已還款 {show_msg} '''report_statement_detail = ''' ---------------------------------------------------------------------------------------信用卡對賬單卡號:{cardno} 賬單編號:{serino} ---------------------------------------------------------------------------------------賬單日 賬單日期范圍 還款日 應還款 已還款 利息 {billdate} {sdate} 至 {edate} {pdate} {total} {payed} {interest} --------------------------------------------------------------------------------------- 賬單明細:交易時間 交易類型 交易金額 流水號 {details} '''templates.py
? ? ?2.3?錯誤代碼文件errorcode.py:


#!/usr/bin/env python """ 系統錯誤代碼表 """ NO_ERROR = 99999 # 系統正常返回 USER_NOT_EXISTS = 10000 # 用戶名不存在 CARD_NOT_BINDED = 10001 # 用戶未綁定信用卡 BALANCE_NOT_ENOUGHT = 10002 # 信用卡余額不足 CARD_OWNER_ERROR = 10003 # 綁定卡時輸入的卡號與卡的所有人不一致 CARD_PASS_ERROR = 10004 # 信用卡密碼錯誤 errorcode
3、數據庫database下代碼:
? ? ?3.1 ?初始化數據模塊init_db.py:


#!/usr/bin/env python import json,os,sysBASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.append(BASE_DIR)from conf import settings from modules import common# 初始化數據表 _shopping_list = {"1": {"typename": "食品生鮮", "product": ({"no": "0001", "name": "進口牛奶 歐德堡(Oldenburger)1L*12", "price": 95},{"no": "0002", "name": "樂虎氨基酸維生素功能飲料250ML*24罐", "price": 120},{"no": "0003", "name": "意大利進口 Ferrero Rocher費列羅巧克力(盒)", "price": 205},{"no": "0004", "name": "品湖韻陽澄湖大閘蟹禮券1388型 8只", "price": 458},{"no": "0005", "name": "三胖蛋 正宗內蒙原味大瓜子218g*6桶/禮", "price": 168},{"no": "0006", "name": "臻味山珍禮盒 吉年納福1600g干菌山珍大禮包", "price": 256})},"2": {"typename": "數碼產品", "product": ({"no": "1001", "name": "佳能(Canon) EOS 6D 單反機身(不含鏡頭) 家庭套餐", "price": 9326},{"no": "1002", "name": "360安全路由P1 3mm纖薄設計 大戶型智能無線路由器", "price": 89},{"no": "1003", "name": "小米 4c 高配版 全網通 白色 移動聯通電信", "price": 1499},{"no": "1004", "name": "華為(HUAWEI)榮耀手環zero 經典黑短", "price": 399},{"no": "1005", "name": "步步高(BBK)家教機S2 香檳金 32G ", "price": 3468},{"no": "1006", "name": "Apple MacBook Air 13.3英寸筆記本電腦", "price": 6988})},"3": {"typename": "男裝女裝", "product": ({"no": "2001", "name": "伊蓮娜2016新款連衣裙英倫格子針織連衣裙假兩件裙加厚", "price": 163},{"no": "2002", "name": "Maxchic瑪汐2016春時尚分割修身圓領修身長袖連衣裙", "price": 235},{"no": "2003", "name": "2015新冬款加厚加絨牛仔褲女保暖小腳褲大碼彈力鉛筆褲", "price": 169},{"no": "2004", "name": "秋冬新款青年男士休閑連帽羽絨服男韓版修身短款外套", "price": 319},{"no": "2005", "name": "AAPE秋冬季新款 時尚潮牌猿人頭袖英文字母印花男士休閑", "price": 298},{"no": "2006", "name": "2015秋冬新款加絨保暖套頭衛衣 15541707 BC17灰花灰", "price": 159})} }_user_list = {"test": {"password": "12345", "name": "測試", "mobile": "13511111111", "islocked": 0, "bindcard": "1001012345","role": "user", "isdel": 0},"super": {"password": "12345", "name": "Admin", "mobile": "15257157418", "islocked": 0, "bindcard": "1001010002","role": "admin", "isdel": 0} }_creditcard_list = {"1001012345": {"password": "12345", "credit_total": 10000, "credit_balance": 10000,"owner": "test", "frozenstatus": 0},"1001010002": {"password": "12345", "credit_total": 10000, "credit_balance": 10000,"owner": "super", "frozenstatus": 0} }# 初始化購物商城數據表 shopping_list.db def init_db_shoppingmark():_db_file = os.path.join(settings.DATABASE['dbpath'], "shoppingmark.db")with open(_db_file, "w+") as f:f.write(json.dumps(_shopping_list))# 初始化用戶數據表 user_list.db def init_db_users():_db_file = os.path.join(settings.DATABASE['dbpath'], "users.db")with open(_db_file, "w+") as fu:for k, v in _user_list.items():# 獲得用戶設置的密碼tmppassword = _user_list[k]['password']# 對密碼進行加密encrypassword = common.encrypt(tmppassword)# 修改明文密碼_user_list[k]['password'] = encrypasswordfu.write(json.dumps(_user_list))# 初始化信用卡數據表 creditcard.db def init_db_creditcard():_db_file = os.path.join(settings.DATABASE['dbpath'], "creditcard.db")with open(_db_file, "w+") as fc:for k, v in _creditcard_list.items():tmppassword = _creditcard_list[k]['password']encrypassword = common.encrypt(tmppassword)_creditcard_list[k]['password'] = encrypasswordfc.write(json.dumps(_creditcard_list))# 初始化數據表 def init_database():tables = list(settings.DATABASE['tables'].values()) # 數據表名稱列表database = settings.DATABASE['dbpath'] # 數據表存放路徑for _table in tables:# 如果表不存在if not os.path.exists(os.path.join(database, "{0}.db".format(_table))):print("Table {0}.db create successfull".format(_table))# 通過反射初始化數據表if hasattr(sys.modules[__name__], "init_db_{0}".format(_table)):init_func = getattr(sys.modules[__name__], "init_db_{0}".format(_table))init_func()else:common.write_error_log("init table {0} failed,no function init_db_{0} found".format(_table))if __name__ == "__main__":init_database()init_db
執行init_db腳本可生成json格式的users.db、shoppingmark.db和creditcard.db
內容如下:
? ? ?3.2 ?數據文件:


{"test": {"isdel": 0, "name": "\u6d4b\u8bd5", "password": "8ad8dd76a16dca9ffcfc970a807a6378", "islocked": 0, "role": "user", "bindcard": "1001012345", "mobile": "13511111111"}, "super": {"isdel": 0, "name": "Admin", "password": "8ad8dd76a16dca9ffcfc970a807a6378", "islocked": 0, "role": "admin", "bindcard": "1001010002", "mobile": "15257157418"}}


{"2": {"product": [{"no": "1001", "name": "\u4f73\u80fd\uff08Canon\uff09 EOS 6D \u5355\u53cd\u673a\u8eab\uff08\u4e0d\u542b\u955c\u5934\uff09 \u5bb6\u5ead\u5957\u9910", "price": 9326}, {"no": "1002", "name": "360\u5b89\u5168\u8def\u7531P1 3mm\u7ea4\u8584\u8bbe\u8ba1 \u5927\u6237\u578b\u667a\u80fd\u65e0\u7ebf\u8def\u7531\u5668", "price": 89}, {"no": "1003", "name": "\u5c0f\u7c73 4c \u9ad8\u914d\u7248 \u5168\u7f51\u901a \u767d\u8272 \u79fb\u52a8\u8054\u901a\u7535\u4fe1", "price": 1499}, {"no": "1004", "name": "\u534e\u4e3a\uff08HUAWEI\uff09\u8363\u8000\u624b\u73afzero \u7ecf\u5178\u9ed1\u77ed", "price": 399}, {"no": "1005", "name": "\u6b65\u6b65\u9ad8\uff08BBK\uff09\u5bb6\u6559\u673aS2 \u9999\u69df\u91d1 32G ", "price": 3468}, {"no": "1006", "name": "Apple MacBook Air 13.3\u82f1\u5bf8\u7b14\u8bb0\u672c\u7535\u8111", "price": 6988}], "typename": "\u6570\u7801\u4ea7\u54c1"}, "1": {"product": [{"no": "0001", "name": "\u8fdb\u53e3\u725b\u5976 \u6b27\u5fb7\u5821(Oldenburger)1L*12", "price": 95}, {"no": "0002", "name": "\u4e50\u864e\u6c28\u57fa\u9178\u7ef4\u751f\u7d20\u529f\u80fd\u996e\u6599250ML*24\u7f50", "price": 120}, {"no": "0003", "name": "\u610f\u5927\u5229\u8fdb\u53e3 Ferrero Rocher\u8d39\u5217\u7f57\u5de7\u514b\u529b(\u76d2)", "price": 205}, {"no": "0004", "name": "\u54c1\u6e56\u97f5\u9633\u6f84\u6e56\u5927\u95f8\u87f9\u793c\u52381388\u578b 8\u53ea", "price": 458}, {"no": "0005", "name": "\u4e09\u80d6\u86cb \u6b63\u5b97\u5185\u8499\u539f\u5473\u5927\u74dc\u5b50218g*6\u6876/\u793c", "price": 168}, {"no": "0006", "name": "\u81fb\u5473\u5c71\u73cd\u793c\u76d2 \u5409\u5e74\u7eb3\u798f1600g\u5e72\u83cc\u5c71\u73cd\u5927\u793c\u5305", "price": 256}], "typename": "\u98df\u54c1\u751f\u9c9c"}, "3": {"product": [{"no": "2001", "name": "\u4f0a\u83b2\u5a1c2016\u65b0\u6b3e\u8fde\u8863\u88d9\u82f1\u4f26\u683c\u5b50\u9488\u7ec7\u8fde\u8863\u88d9\u5047\u4e24\u4ef6\u88d9\u52a0\u539a", "price": 163}, {"no": "2002", "name": "Maxchic\u739b\u6c502016\u6625\u65f6\u5c1a\u5206\u5272\u4fee\u8eab\u5706\u9886\u4fee\u8eab\u957f\u8896\u8fde\u8863\u88d9", "price": 235}, {"no": "2003", "name": "2015\u65b0\u51ac\u6b3e\u52a0\u539a\u52a0\u7ed2\u725b\u4ed4\u88e4\u5973\u4fdd\u6696\u5c0f\u811a\u88e4\u5927\u7801\u5f39\u529b\u94c5\u7b14\u88e4", "price": 169}, {"no": "2004", "name": "\u79cb\u51ac\u65b0\u6b3e\u9752\u5e74\u7537\u58eb\u4f11\u95f2\u8fde\u5e3d\u7fbd\u7ed2\u670d\u7537\u97e9\u7248\u4fee\u8eab\u77ed\u6b3e\u5916\u5957", "price": 319}, {"no": "2005", "name": "AAPE\u79cb\u51ac\u5b63\u65b0\u6b3e \u65f6\u5c1a\u6f6e\u724c\u733f\u4eba\u5934\u8896\u82f1\u6587\u5b57\u6bcd\u5370\u82b1\u7537\u58eb\u4f11\u95f2", "price": 298}, {"no": "2006", "name": "2015\u79cb\u51ac\u65b0\u6b3e\u52a0\u7ed2\u4fdd\u6696\u5957\u5934\u536b\u8863 15541707 BC17\u7070\u82b1\u7070", "price": 159}], "typename": "\u7537\u88c5\u5973\u88c5"}}shoppingmarket


{"1001010002": {"credit_total": 10000, "owner": "super", "password": "8ad8dd76a16dca9ffcfc970a807a6378", "frozenstatus": 0, "credit_balance": 275}, "1001012345": {"credit_total": 10000, "owner": "test", "password": "8ad8dd76a16dca9ffcfc970a807a6378", "frozenstatus": 0, "credit_balance": 9537}}
4、數據訪問層dbhelper下的dbapi.py:
? ? ?數據訪問層模塊,讀寫數據


#!/usr/bin/env python """ __author: super 數據庫訪問層: 提供從數據文件、報表文件中讀取數據的接口 將數據寫入到數據文件的接口 """ import os,json from conf import settings from modules.common import write_error_log,write_logdef append_db_json(contant, filename):"""將信息以 json 格式寫入數據表文件(追加):param contant: 要寫入的 json 格式內容:param filename: 要寫入的數據表文件名:return: 無返回"""try:with open(filename, 'a+') as fa:fa.write(json.dumps(contant))fa.write("\n")except Exception as e:write_error_log(e)def write_db_json(contant, filename):"""將信息以 json 格式寫入數據表文件(覆蓋):param contant: 寫入的json數據內容:param filename: 要寫入的文件名:return: 無返回結果"""try:with open(filename, 'w+') as fw:fw.write(json.dumps(contant))except Exception as e:write_error_log(e)def load_data_from_db(tabename):"""從指定的數據表中獲取所有數據,通過 json 方式將數據返回:param tabename: 數據文件名:return: 返回所有結果"""try:with open(tabename, 'r+') as f:return json.load(f)except Exception as e:write_error_log(e)def load_bill_report(cardno, startdate, enddate):"""從信用卡對賬單中獲取指定卡的對賬信息數據, 獲取某一個時間段內的數據:param enddate: 查詢記錄的結束日期:param startdate: 查詢記錄的開始日期:param cardno: 信用卡卡號:return: 信用卡對賬單數據 ,返回結果為 list 數據類型"""r_file = os.path.join(settings.REPORT_PATH, "report_bill")result = []try:with open(r_file, "r+") as f:for line in f:_record = json.loads(line)if _record["cardno"] == cardno:if startdate <= _record["starttime"] <= enddate:result.append(_record)return resultexcept Exception as e:write_error_log(e)def load_shop_history(user, startdate, enddate):"""查找報表記錄中的指定用戶的購物歷史記錄,將結果存入到列表中:param user: 用戶名:param startdate: 開始日期:param enddate: 結束日期:return: 結果 dict_list"""_file = os.path.join(settings.REPORT_PATH, "shopping_history")result = list()try:with open(_file, "r") as f:for line in f:_record = json.loads(line)# 如果找到指定用戶記錄if list(_record.keys())[0] == user:if list(_record.values())[0]["time"] >= startdate <= enddate:result.append(_record)return resultexcept Exception as e:write_error_log("dbapi > load_shop_history > {0}".format(e))def load_statement_list(cardno):"""從對賬單中獲取記錄,:param cardno: 對賬單文件:return: 返回一個列表"""file = os.path.join(settings.REPORT_PATH, "statement_{0}".format(cardno))result_list = list()try:if os.path.isfile(file):with open(file, 'r+') as f:for line in f:statement = json.loads(line)result_list.append(statement)return result_listexcept Exception as e:write_error_log("dbapi > load_statement_list > {0}".format(e))def write_statement_list(cardno, db_list):"""將對賬單記錄寫入對賬單文件,更新:param cardno: 對賬單的卡號:param db_list: 新的對賬信息列表:return:"""file = os.path.join(settings.REPORT_PATH, "statement_{0}".format(cardno))with open(file, 'w+') as f:for newrecord in db_list:f.write(json.dumps(newrecord))f.write("\n")dbapi
5、業務邏輯層modules下代碼:
? ? ?5.1 公共函數模塊common.py:


#!/usr/bin/env python import hashlib,random,os,time,loggingfrom datetime import datetime, date from conf import settingsdef verification_code():"""生成一個4位的驗證碼:return: 返回驗證碼"""_code = list()for i in range(4):ra = random.randrange(4)if i == ra:_code.append(chr(random.randrange(97, 122)))else:_code.append(str(i))result = ''.join(_code)return resultdef encrypt(string):"""字符串加密函數:param string: 待加密的字符串:return: 返回加密過的字符串"""ha = hashlib.md5(b'oldboy')ha.update(string.encode('utf-8'))result = ha.hexdigest()return resultdef write_error_log(content):"""寫錯誤日志:param content: 日志信息:return: 無返回,寫入文件 error.log"""_content = "\n{0} : {1} ".format(datetime.now().strftime("%Y-%m-%d %X"), content)_filename = os.path.join(settings.LOG_PATH, "errlog.log")with open(_filename, "a+") as fa:fa.write(_content)def write_log(content,levelname):"""寫正常登錄,退出,轉帳,取現日志:param content: 日志信息:return: 無返回,寫入文件 sysinfo.log"""_filename = os.path.join(settings.LOG_PATH, "sysinfo.log")logging.basicConfig(level=logging.INFO,encoding = "UTF-8",format='%(asctime)s %(levelname)s %(message)s',datefmt='%Y-%m-%d %H:%M:%S %p',filename=_filename,filemode='a+')if levelname == 'debug':logging.debug(content)elif levelname == 'info':logging.info(content)elif levelname == 'warning':logging.warning(content)elif levelname == 'error':logging.error(content)elif levelname == 'critical':logging.critical(content)else:show_message('輸入錯誤',"ERROR")# 獲取漢字個數 def get_chinese_num(uchar):i = 0for utext in uchar:if u'\u4e00' <= utext <= u'\u9fa5':i += 1return idef show_message(msg, msgtype):"""對print函數進行封裝,根據不同類型顯示不同顏色:param msg: 顯示的消息體:param msgtype: 消息類型:return: 返回格式化過的內容"""if msgtype == "NOTICE":show_msg = "\n\033[1;33m{0}\033[0m\n".format(msg)elif msgtype == "ERROR":show_msg = "\n\033[1;31m{0}\033[0m\n".format(msg)elif msgtype == "INFORMATION":show_msg = "\n\033[1;32m{0}\033[0m\n".format(msg)else:show_msg = "\n{0}\n".format(msg)print(show_msg)def create_serialno():"""生成一個消費、轉賬、提款時的流水號,不重復:return: 流水號"""serno = "{0}{1}".format(datetime.now().strftime("%Y%m%d%H%M%S"), str(int(time.time())))return sernodef numtochr(num_of_weekday):"""將數字星期轉換為中文數字:param num_of_weekday: 星期幾的數字字符( 0,1,2,3,4,5,6):return: 中文 星期幾"""chrtuple = ( '一', '二', '三', '四', '五', '六','日')num = int(num_of_weekday)return chrtuple[num]def input_msg(message, limit_value=tuple()):"""判斷input輸入的信息是否為空的公共檢測函數,為空繼續輸入,不為空返回輸入的信息:param limit_value: 對輸入的值有限制,必須為limit_value的值;ex:("admin","user"):param message: input()函數的提示信息:return: 返回輸入的信息"""is_null_flag = Truewhile is_null_flag:input_value = input(message).strip().lower()if not input_value:show_message("輸入不能為空!", "ERROR")continueelif len(limit_value) > 0:if input_value not in limit_value:show_message("輸入的值不正確,請重新輸入!", "ERROR")continueelse:is_null_flag = Falseelse:is_null_flag = Falsecontinuereturn input_valuedef input_date(msg, default_date):"""對輸入的日期進行判斷是否正確 yyyy-mm-dd or yyyy-m-d:param msg:輸入提示信息:param default_date: 默認日期:return:返回日期 str類型"""check_flag = Falsewhile not check_flag:strdate = input(msg).strip()if not strdate:strdate = default_datetry:date_list = strdate.split("-")result = date(int(date_list[0]), int(date_list[1]), int(date_list[2]))check_flag = Trueexcept ValueError:show_message("輸入日期不合法,請重新輸入!", "ERROR")continuereturn result.strftime("%Y-%m-%d")common
? ? ?5.2 用戶類模塊users.py:


#!/usr/bin/env python import os from conf import settings, errorcode, template from modules import common from modules.creditcard import CreditCard from dbhelper import dbapiclass Users(object):# 用戶數據庫__database = "{0}.db".format(os.path.join(settings.DATABASE['dbpath'], settings.DATABASE["tables"]["users"]))def __init__(self):self.username = "" # 登錄名self.password = "" # 登錄密碼self.bindcard = "" # 綁定卡self.islogin = False # 登錄狀態self.name = "" # 姓名self.mobile = "" # 手機self.islocked = 0 # 是否鎖定self.role = "user" # 賬戶權限self.trycount = 0 # 登錄嘗試次數self.isdel = 0 # 用戶刪除標識self.code = common.verification_code()self.dict_user = {}self.db_load()def db_load(self):self.dict_user = dbapi.load_data_from_db(self.__database)def _user_login(self, password,code):"""用戶登錄驗證模塊,對用戶對象進行判斷,登錄成功后返回一個新的用戶對象:return:"""# 對輸入的密碼加密_password = common.encrypt(password)for user, details in self.dict_user.items():# 找到用戶名if user == self.username and not details["isdel"]:# 是否被鎖定if details["islocked"] == 0:# 賬戶未鎖定,驗證密碼if details["password"] == _password and code == self.code:self.islogin = Trueself.bindcard = details["bindcard"]self.name = details["name"]self.mobile = details["mobile"]self.role = details["role"]self.isdel = details["isdel"]self.islocked = details["islocked"]self.password = passwordbreakelse:# 密碼錯誤,失敗1次self.trycount += 1else:# 賬戶鎖定了self.islocked = 1def login(self):"""用戶登錄過程函數,輸入用戶名和密碼后調用內部方法 _user_login進行登錄驗證:return:"""while self.trycount < settings.ERROR_MAX_COUNT:self.username = input("用戶名: ")password = input("密 碼: ")common.show_message("驗證碼:{0}".format(self.code),"INFORMATION")check_code = input("請輸入驗證碼:")if not self.user_exists:common.show_message("用戶名不存在!", "ERROR")continue# 調用用戶登錄方法進行登錄 self._user_login(password,check_code)# 用戶鎖定就直接退出if self.islocked:common.show_message("該用戶已被鎖定,請聯系系統管理員!", "ERROR")self.trycount = 0break# 登錄成功 退出登錄if self.islogin:breakelse:common.show_message("用戶名密碼錯誤", "NOTICE")else:# 失敗3次了還輸? 鎖了你self.islocked = 1# 更新用戶信息self.dict_user[self.username]["islocked"] = self.islockedself.update_user()common.show_message("輸入錯誤次數過多,請聯系系統管理員!", "ERROR")# 將用戶的登錄嘗試次數恢復初始值 0self.trycount = 0def update_user(self):"""用戶數據更新方法,用戶修改信息、用戶賬戶鎖定、解鎖等操作之后更新數據庫文件:return:"""try:'''_password = common.encrypt(self.password)self.dict_user[self.username]["password"] = _passwordself.dict_user[self.username]["islocked"] = self.islockedself.dict_user[self.username]["name"] = self.nameself.dict_user[self.username]["mobile"] = self.mobileself.dict_user[self.username]["bindcard"] = self.bindcardself.dict_user[self.username]["isdel"] = self.isdelself.dict_user[self.username]["role"] = self.role'''# 寫入數據庫文件dbapi.write_db_json(self.dict_user, self.__database)return Trueexcept Exception as e:common.write_error_log(e)return False@propertydef user_exists(self):"""判斷用戶名是否存在,用戶注冊時判斷,存在返回True, 否則返回False:return: True / False"""if self.username in list(self.dict_user.keys()):return Trueelse:return Falsedef create_user(self):"""新創建一個用戶,將用戶數據同步寫入到數據庫文件:return:"""self.dict_user[self.username] = dict(password=common.encrypt(self.password),name=self.name,mobile=self.mobile,bindcard=self.bindcard,role=self.role,islocked=0,isdel=0,)dbapi.write_db_json(self.dict_user, self.__database)def del_user(self):"""刪除用戶,邏輯刪除:return:"""self.dict_user[self.username]["isdel"] = 1self.update_user()def unlock_user(self):self.dict_user[self.username]["islocked"] = 0self.update_user()def init_user_info(self):"""創建用戶,完善用戶資料信息:return:"""is_null_flag = Truewhile is_null_flag:self.username = input("登錄用戶名(小寫字母):").strip().lower()if not self.username:common.show_message("用戶名不能為空", "ERROR")continueelif self.user_exists:common.show_message("該用戶名已存在", "ERROR")continueelse:is_null_flag = Falsecontinueself.name = common.input_msg("姓名:")self.password = common.input_msg("密碼:")self.mobile = common.input_msg("手機:")self.role = common.input_msg("用戶權限(user/admin):", ("admin", "user"))self.create_user()common.show_message("用戶創建成功!", "NOTICE")@staticmethoddef user_auth(func):"""用戶登錄驗證裝飾器, userobj 為登錄用戶對象,未登錄時可以傳入一個空對象:param func: 被裝飾的函數:return:"""def login_check(self, userobj):# 用戶還未登錄if not userobj.islogin:common.show_message("用戶未登錄,請先登錄系統!", "NOTICE")# 開始用戶登錄 userobj.login()# 登錄成功了嗎if userobj.islogin:return func(self, userobj)else:common.show_message("登錄失敗,請聯系系統管理員!", "ERROR")else:return func(self, userobj)return login_checkdef bind_card(self, cardobj):"""用戶綁定信用卡,調用該方法綁定卡時,先實例化卡對象,再判斷卡是否有效(卡是否存在、卡密碼是否正確):param cardobj: 信用卡對象:return: 成功 99999 / 失敗 錯誤碼"""# 判斷用戶輸入的卡號和實際卡號所有人是不是一致的if self.username == cardobj.owner:self.bindcard = cardobj.cardnoself.update_user()return errorcode.NO_ERRORelse:return errorcode.CARD_OWNER_ERRORdef logout(self):"""注銷當前用戶,將系統屬性置空:return:"""self.islogin = Falseself.bindcard = ""self.mobile = ""self.name = ""self.password = ""self.username = ""common.show_message("注銷成功", "NOTICE")def modify_password(self):"""個人中心 - 修改密碼:return:"""_not_null_flag = Falsetry:while not _not_null_flag:_new_password = input("輸入新密碼: ").strip()_confirm_password = input("再次輸入確認密碼:").strip()if not _new_password or not _confirm_password:common.show_message("密碼不能為空,請重新輸入!", "ERROR")continueif _new_password != _confirm_password:common.show_message("兩次輸入密碼不一致,請重新輸入!", "NOTICE")continue_not_null_flag = Trueself.password = _new_password_password = common.encrypt(self.password)self.dict_user[self.username]["password"] = _passwordself.update_user()common.show_message("密碼修改成功!", "INFORMATIOM")return Trueexcept Exception as e:common.write_error_log(e)return Falsedef modify_user_info(self):"""打印用戶信息:return: 用戶信息字符串"""if self.islocked == 1:currstatus = "賬戶鎖定"else:currstatus = "賬戶正常"frmuser = template.user_info.format(username=self.username,name=self.name,mobile=self.mobile,bindcard=self.bindcard,role=self.role,islocked="是" if self.islocked == 1 else "否",isdel="是" if self.isdel == 1 else "否")# 打印用戶信息common.show_message(frmuser, "NOTICE")# 開始修改common.show_message("請輸入新的資料,若不更新直接回車:", "NOTICE")new_name = input("姓名({0}); ".format(self.name))new_mobile = input("手機({0}): ".format(self.mobile))self.name = self.name if len(new_name) == 0 else new_nameself.mobile = self.mobile if len(new_mobile) == 0 else new_mobile# 輸入信用卡卡號_card_noerror = Falsewhile not _card_noerror:new_bindcard = input("綁定卡({0}): ".format(self.bindcard))if len(new_bindcard) > 0:# 創建一個卡對象cardobj = CreditCard(new_bindcard)if not cardobj.card_is_exists:common.show_message("您輸入的卡號不存在!", "ERROR")elif cardobj.owner != self.username:common.show_message("您輸入的卡號非法,請聯系系統管理員!", "ERROR")else:# 都正確了self.bindcard = new_bindcard_card_noerror = Trueelse:_card_noerror = True# 更新用戶資料庫變量self.dict_user[self.username]["name"] = self.nameself.dict_user[self.username]["mobile"] = self.mobileself.dict_user[self.username]["bindcard"] = self.bindcardif self.update_user():common.show_message("信息更新成功!", "NOTICE")else:common.show_message("更新失敗,查看日志!", "ERROR")def load_user_info(self):"""根據用戶名獲取用戶信息:return: 用戶對象"""if self.user_exists:user_detail = self.dict_user[self.username]self.name = user_detail["name"]self.bindcard = user_detail["bindcard"]self.islocked = user_detail["islocked"]self.role = user_detail["role"]self.isdel = user_detail["isdel"]self.mobile = user_detail["mobile"]return Trueelse:return Falseusers
? ? ?5.3 購物類模塊shopping.py:


#!/usr/bin/env python import os from datetime import datetime from conf import settings, errorcode, template from modules.users import Users from modules.creditcard import CreditCard from modules import common from dbhelper import dbapiclass Shopping(object):# 購物商城界面__welcome_title = template.shopping_index_menu# 購物商城商品數據庫__database = "{0}.db".format(os.path.join(settings.DATABASE['dbpath'], settings.DATABASE["tables"]["shopping"]))# 購物記錄報表存放文件__shop_report_file = os.path.join(settings.REPORT_PATH, "shopping_history")def __init__(self):# 存放購物車列表self.shopping_cart = []# 購物總費用self.shopping_cost = 0# 用戶選擇的商品分類 keyself.goods_classify_id = ""# 數據表中所有商品信息self.shop_market = dict()# 購物商城歡迎菜單self.welcome_menu = ""self._get_shop_market() # 獲取數據表數據self._construct_title_menu() # 購物商城歡迎菜單def _get_shop_market(self):"""獲取購物商城所有商品信息,存入類字段(shop_market):return: self.shop_market"""self.shop_market = dbapi.load_data_from_db(self.__database)def _construct_title_menu(self):"""構建歡迎菜單,存入類字段(welcome_menu):return: self.welcome_menu"""_menu = []keys = list(self.shop_market.keys())# 按類型編號排序 keys.sort()for goods_type_id in keys:goods_type_name = self.shop_market[goods_type_id]['typename']_menu.append("[{0}] {1} ".format(goods_type_id, goods_type_name))_menu.append("[{0}] {1} ".format("4", "查看購物車"))_menu.append("[{0}] {1} ".format("5", "購物結算"))_menu.append("[{0}] {1} ".format("0", "退出商城"))self.welcome_menu = self.__welcome_title.format(menu="".join(_menu))def get_goods_list_by_typeid(self):"""根據用戶選擇的商品分類編號,獲取該分類下所有商品,返回結果為tuple:return: 返回tuple類型: 指定分類商品下的所有商品信息"""if self.goods_classify_id not in list(self.shop_market.keys()):return Noneelse:return self.shop_market[self.goods_classify_id]['product']@staticmethoddef print_goods_list(goods_list):"""將goods_list列表中的商品信息輸出到屏幕,商品列表或購物車商品列表:param goods_list: 要打印的商品信息,類型為tuple:return: 輸出到屏幕"""_goodlist = goods_listprint("|{0}|{1}|{2}|".format('商品編號'.center(11), '商品名稱'.center(50), '商品價格(RMB)'.center(10)))print('%s' % '-' * 95)for goods in _goodlist:chinese_num = common.get_chinese_num(goods['name'])len_name = len(goods['name'])space_str = (55 - len_name - chinese_num) * " "print('| %-12s | %s |%15s|' % (goods['no'], goods['name'] + space_str, str(goods['price'])))def add_shopping_card(self, goodsid):"""根據用戶輸入的商品編號,將商品編號加入購物車,如果商品編號不存在返回False,添加成功返回True:param goodsid: 商品編號:return: 成功 True / 失敗 False"""exist_flag = False# 從商品列表中獲取指定類型的所有商品信息(tuple)_goods_tuple = self.shop_market[self.goods_classify_id]['product']# 開始查找輸入的商品編號for goods in _goods_tuple:if goods['no'] == goodsid:self.shopping_cart.append(goods)self.shopping_cost += goods['price']exist_flag = Truebreakreturn exist_flag@Users.user_authdef payfor_shopcart(self, userobj):"""購物車結算模塊,功能包括:購物車付款、購物記錄寫入文件、:param kwargs: 字典參數 {cost=購物車金額, userobj=用戶對象}:return:"""# 判斷用戶有沒有綁定信用卡if not userobj.bindcard:# 用戶沒有綁定信用卡,直接返回錯誤,在外層綁卡return errorcode.CARD_NOT_BINDEDelse:# 用戶綁定了信用卡了, 獲取信用卡信息(實例化對象)cardobj = CreditCard(userobj.bindcard)# 卡余額夠嗎if cardobj.credit_balance < self.shopping_cost:common.show_message("您的信用卡本月額度不夠! ", "NOTICE")return errorcode.BALANCE_NOT_ENOUGHTelse:# 生成一個流水號serno = common.create_serialno()# 調用卡的支付模塊進行支付cardobj.card_pay(self.shopping_cost, 1, serno)# 記錄購物流水shopping_record = {userobj.username: {"time": datetime.now().strftime("%Y-%m-%d %H:%M"),"cost": self.shopping_cost,"serno": serno,"detail": self.shopping_cart}}# 寫入報表記錄文件dbapi.append_db_json(shopping_record, self.__shop_report_file)# 購物結算完成后將對象的購物車清空, 購物車商品總價清0 ,待下次購物 self.shopping_cart.clear()self.shopping_cost = 0return errorcode.NO_ERRORshopping
? ? ?5.4 信用卡類模塊creditcard.py:


#!/usr/bin/env python import os from datetime import datetime, date, timedelta from conf import settings, errorcode from modules import common from dbhelper import dbapiclass CreditCard(object):__database = "{0}.db".format(os.path.join(settings.DATABASE['dbpath'], settings.DATABASE["tables"]["creditcard"]))def __init__(self, cardno):# 信用卡卡號self.cardno = cardno# 信用卡密碼self.password = ""# 卡所有者self.owner = ""# 信用卡額度self.credit_total = settings.CREDIT_TOTAL# 信用卡透支余額self.credit_balance = settings.CREDIT_TOTAL# 信用卡日息self.dayrate = settings.EXPIRE_DAY_RATE# 提現手續費率self.feerate = settings.FETCH_MONEY_RATE# 所有信用卡數據self.credit_card = {}# 信用卡是否存在標識#self.card_is_exists = True# 信用卡狀態(是否凍結)self.frozenstatus = 0# 獲取卡的信息 self._load_card_info()def _load_card_info(self):"""根據用戶輸入的卡號獲取信用卡信息,如果卡號不存在就返回False:return: 信用卡對象"""exists_flag = Falseself.credit_card = dbapi.load_data_from_db(self.__database)for key, items in self.credit_card.items():if key == self.cardno:self.password = self.credit_card[self.cardno]['password']self.credit_total = self.credit_card[self.cardno]['credit_total']self.credit_balance = self.credit_card[self.cardno]['credit_balance']self.owner = self.credit_card[self.cardno]['owner']self.frozenstatus = self.credit_card[self.cardno]['frozenstatus']exists_flag = Truebreak#self.card_is_exists = exists_flag @propertydef card_is_exists(self):if self.cardno in list(self.credit_card.keys()):return Trueelse:return Falsedef card_pay(self, cost, paytype, sereialno):"""信用卡支付,從信用卡可透支余額中扣費:param sereialno: 流水號:param cost: 消費金額 float類型:param paytype: 消費類型 int類型 ( 1:消費、2:轉賬、3:提現、4:手續費 ) 對于2,3類型的支付要扣手續費,單記錄一條流水單:return:"""if paytype == 1:payfor = "消費"elif paytype == 2:payfor = "轉賬"elif paytype == 3:payfor = "提現"elif paytype == 4:payfor = "手續費"else:payfor = "未知"# 支付扣款self.credit_balance -= cost# 記錄消費流水對賬單,將發生了費用還沒有還款的賬單信息寫入文件 report_bill 中_tmp_bill_record = dict(cardno="{0}".format(self.cardno),starttime=datetime.strftime(datetime.now(), "%Y-%m-%d %H:%M"),payfor=payfor,cost=cost,serialno=sereialno)dbapi.append_db_json(_tmp_bill_record, os.path.join(settings.REPORT_PATH, "report_bill"))# 更新信用卡可透支余額信息到數據庫 creditcard.dbself.credit_card[self.cardno]["credit_balance"] = self.credit_balancedbapi.write_db_json(self.credit_card, self.__database)def create_card(self):"""新發行一張行用卡:return:"""password = common.encrypt(self.password)self.credit_card[self.cardno] = dict(password=password,credit_total=self.credit_total,credit_balance=self.credit_balance,owner=self.owner,frozenstatus=self.frozenstatus)# 保存到數據庫dbapi.write_db_json(self.credit_card, self.__database)def update_card(self):password = common.encrypt(self.password)self.credit_card[self.cardno]["password"] = passwordself.credit_card[self.cardno]["owner"] = self.ownerself.credit_card[self.cardno]["credit_total"] = self.credit_totalself.credit_card[self.cardno]["credit_balance"] = self.credit_balanceself.credit_card[self.cardno]["frozenstatus"] = self.frozenstatus# 寫入數據庫dbapi.write_db_json(self.credit_card, self.__database)def _pay_check(self, cost, password):"""轉賬、提現時驗證操作,判斷卡的余額與支付密碼是否正確。并返回錯誤類型碼:param cost: 轉賬、提現金額(包含手續費):param password: 支付密碼:return: 錯誤碼"""totalfee = cost# 提現金額及手續費和大于余額,if totalfee > self.credit_balance:return errorcode.BALANCE_NOT_ENOUGHTelif common.encrypt(password) != self.password:return errorcode.CARD_PASS_ERRORelse:return errorcode.NO_ERRORdef fetch_money(self, count, passwd):"""提現:param count: 提現金額:param passwd:信用卡提現密碼:return: 返回錯誤類型碼"""totalfee = count + count * self.feeratecheck_result = self._pay_check(totalfee, passwd)if check_result == errorcode.NO_ERROR:# 扣取提現金額并寫入數據庫,生成賬單self.card_pay(count, 3, common.create_serialno())# 扣取手續費并寫入數據庫, 生成賬單self.card_pay(count * self.feerate, 4, common.create_serialno())return errorcode.NO_ERRORelse:return check_resultdef translate_money(self, trans_count, passwd, trans_cardobj):"""信用卡轉賬模塊:param trans_count: 要轉賬的金額:param passwd: 信用卡密碼:param trans_cardobj: 對方卡號對應的卡對象:return: 轉賬結果"""totalfee = trans_count + trans_count * self.feeratecheck_result = self._pay_check(totalfee, passwd)if check_result == errorcode.NO_ERROR:# 先扣款,生成消費流水賬單self.card_pay(trans_count, 2, common.create_serialno())# 扣手續費, 生成消費流水賬單self.card_pay(trans_count * self.feerate, 4, common.create_serialno())# 給對方卡充值,并寫入數據庫文件trans_cardobj.credit_balance += totalfeetrans_cardobj.update_card()return errorcode.NO_ERRORelse:return check_resultdef load_statement_list(self):"""獲取要還款的對賬單列表數據,僅包含對賬單號、還款日、應還款額、已還款額:return: 對賬單列表"""# 獲取要顯示的信息list_info = dbapi.load_statement_list(self.cardno)return list_infodef recreate_statement(self):"""根據今天的日期將當前卡的對賬單重新生成,主要對過了還款日的賬單重新生成利息信息:return:"""# 獲取當前日期today = datetime.strptime(date.today().strftime("%Y-%m-%d"), "%Y-%m-%d")# 獲取所有卡的對賬單信息card_statement = dbapi.load_statement_list(self.cardno)tmp_list = list()# 如果有記錄if len(card_statement) > 0:for record in card_statement:for k, v in record.items():# 如果已經還款了,將對賬單放入臨時列表中if v["isfinished"] == 1:tmp_list.append(record)else:# 還未還款? 獲取還款日期pay_day = datetime.strptime(v["pdate"], "%Y-%m-%d")# 如果還款日大于當前日期,無利息day_delta = (today - pay_day).daysif day_delta > 0:# 過了還款日了,計算利息 = 總費用 * 日息 * 超過天數interest = v["total"] * settings.EXPIRE_DAY_RATE * day_delta# 更新利息信息記錄record[k]["interest"] = interest# 將更新過的記錄寫入臨時列表 tmp_list.append(record)else:# 沒有過還款日直接寫入臨時列表 tmp_list.append(record)# 都處理完了,將更新過的列表寫入文件,替換原有信息 dbapi.write_statement_list(self.cardno, tmp_list)else:# 此卡沒有對賬單記錄passcreditcard
? ? ?5.5 報表模塊report.py:


#!/usr/bin/env python """ 賬單生成模塊: 從report_bill中便利所有流水記錄,獲取所有卡號 將卡號存放到列表中 對列表進行集合類型轉換 根據集合中的唯一的卡號信息,每個卡號生成一個文件 cardno_startdate_enddate報表文件,文件中存放字典信息對賬單 將對賬的費用統計信息接入一個統計文件 ,包括字段:卡號、對應詳單文件名、賬單日期、還款日期、應還款金額、已還款金額、 {"detail":{[],[]}" """ import calendar import os from datetime import datetime, timedelta from datetime import date from dbhelper import dbapi from conf import template from conf import settings from modules import common from modules.shopping import Shoppingdef get_date():"""用戶輸入一個時間段,如果顯示報表是要提供開始、結束日期,返回開始,結束時間:return: 字典格式,{"start":startdate, "end": enddate}"""startdate = common.input_date("輸入查詢開始日期(yyyy-mm-dd)[default:2016-01-01]: ", "2016-01-01")enddate = common.input_date("輸入查詢結束日期(yyyy-mm-dd)[default: today]: ", date.today().strftime("%Y-%m-%d"))return {"start": startdate, "end": enddate}def print_shopping_history(userobj):"""個人中心 - 購物歷史記錄打印模塊:param userobj: 用戶對象:return: 顯示指定時間段的購物歷史記錄"""date_between = get_date()start = date_between["start"]end = date_between["end"]# 通過dbapi獲得要查詢的記錄列表,結果為dict_list類型history_list = dbapi.load_shop_history(userobj.username, start, end)# 獲取模板文件樣式_template = template.shopping_historycommon.show_message(_template.format(username=userobj.username,startdate=start,enddate=end), "NOTICE")if not history_list:common.show_message("無購物記錄!", "NOTICE")else:for record in history_list:# 獲取消費信息_tmprec = list(record.values())[0]common.show_message("\n流水號:{0} 時間:{1} 消費金額:{2}\n".format(_tmprec["serno"],_tmprec["time"],_tmprec["cost"]), "NOTICE")# 調用Shopping的類方法打印詳單Shopping.print_goods_list(_tmprec["detail"])def print_bill_history(userobj):"""個人中心-賬單明細 打印模塊:param userobj: 用戶對象:return:"""dates = get_date()startdate = dates["start"]enddate = dates["end"]# 保存所有賬單流水的記錄列表,數據為一個字符串msglist = list()# 獲取顯示模板_template = template.report_bill# 獲取符合條件的賬單明細記錄(dict_list 類型)_recordlist = dbapi.load_bill_report(userobj.bindcard, startdate, enddate)for record in _recordlist:tmpmsg = "{time} {costtype} {cost} {crdno}".format(time=record["starttime"],costtype=record["payfor"].ljust(10),cost=str(record["cost"]).ljust(6),crdno=record["serialno"])msglist.append(tmpmsg)# 填充模板并打印common.show_message(_template.format(cardno=userobj.bindcard,startdate=startdate,enddate=enddate,billdetail="\n".join(msglist)), "NOTICE")def create_card_statement(cardno):"""生成信用卡對賬單:param cardno:卡號:return:"""# 獲得當前時間currday = datetime.now().strftime("%Y-%m-%d")today = date.today().day# 如果是賬單日22號 開始生成賬單if today == settings.STATEMENT_DAY:# 生成對賬單數據庫中存放對賬單數據的字典 key startday = (datetime.now() + timedelta(days=-30)).strftime("%Y%m{0}".format(settings.STATEMENT_DAY))endday = datetime.now().strftime("%Y%m%d")statement_key = "{start}{end}".format(start=startday, end=endday)# 從對賬單流水中計算出要還款的金額startdate = (datetime.now() + timedelta(days=-30)).strftime("%Y-%m-{0} 00:00:00".format(settings.STATEMENT_DAY))enddate = (datetime.now() + timedelta(days=1)).strftime("%Y-%m-%d 00:00:00")# 獲取卡號對應的消費流水記錄列表bill_list = dbapi.load_bill_report(cardno, startdate, enddate)statement_total = 0.00# 如果有消費記錄,生成對賬單if len(bill_list) > 0:for bill in bill_list:# 獲取一個對賬單日期的消費總費用statement_total += bill["cost"]# 獲取還款日期 下個月10號statement_pdate = (datetime.now() + timedelta(days=30)).strftime("%Y-%m-10")# 生成對賬單的統計數據字典{"2016012220160222":{"賬單日":currday,# "賬單范圍":startdate至enddate,# "賬單金額":statement_bill,# "已還款":0,# "還款日":pdate# "已還完":0}} statement_dict = {statement_key: {"billdate": currday,"startdate": startdate,"enddate": enddate,"total": statement_total,"payed": 0,"pdate": statement_pdate,"interest": 0,"isfinished": 0}}# 對賬單文件名file_name = os.path.join(settings.REPORT_PATH, "statement_{0}".format(cardno))# 寫入文件 dbapi.append_db_json(statement_dict, file_name)def create_statement_main():"""卡對賬單初始化模塊,從卡數據庫文件中加載所有卡號,對所有卡調用生成對賬單模塊:return:"""_database = "{0}.db".format(os.path.join(settings.DATABASE['dbpath'], settings.DATABASE["tables"]["creditcard"]))card_list = dbapi.load_data_from_db(_database)cards = list(card_list.keys())for cardno in cards:create_card_statement(cardno)def print_statement_list(cardno, list_info):"""將卡號對應的未還款記錄顯示出來,:param cardno: 卡號:param list_info: 信用卡對賬單信息:return: 無返回"""# 獲取顯示模板show_template = template.report_statement_listtmpstrlist = list()# 如果獲取到了對賬單數據if len(list_info) > 0:for record in list_info:for k, v in record.items():# 如果還未全部還完款if v["isfinished"] == 0:tmpmsg = "{sno}{pdate}{spay}{payed}".format(sno=k.ljust(20),pdate=v["pdate"].ljust(13),spay=str(v["total"]).ljust(13),payed=str(v["payed"]))tmpstrlist.append(tmpmsg)tmpstrlist.append("您目前共有 {0} 個賬單".format(len(tmpstrlist)))# 填充模板,將填充后的魔板信息返回result = show_template.format(cardno=cardno, show_msg="\n".join(tmpstrlist))return resultelse:return ""def print_statement_detail(cardno, serino, details):"""還款模塊 - 用戶選擇還款的單號后,顯示詳細的還款對賬單及流水信息:param cardno: 信用卡卡號:param serino: 對賬單編號:param statement_list: 對賬單列表:return: 返回填充后的模板信息"""# 獲取顯示模板show_template = template.report_statement_detail# 獲取指定編號的詳細信息_billdate = details["billdate"] # 賬單日_sdate = details["startdate"] # 賬單開始日期_edate = details["enddate"] # 賬單結束日期_total = details["total"] # 費用總額_payed = details["payed"] # 已還款金額_pdate = details["pdate"] # 還款日_interest = details["interest"] # 利息# 獲取詳細的流水清單_flows = list()_recordlist = dbapi.load_bill_report(cardno, _sdate, _edate)for info in _recordlist:tmpmsg = "{time} {costtype} {cost} {crdno}".format(time=info["starttime"],costtype=info["payfor"].ljust(10),cost=str(info["cost"]).ljust(6),crdno=info["serialno"])_flows.append(tmpmsg)# 填充模板result_message = show_template.format(cardno=cardno,serino=serino,billdate=_billdate,sdate=_sdate[0:10],edate=_edate[0:10],pdate=_pdate,total=_total,payed=_payed,interest=_interest,details="\n".join(_flows))return result_messagereport
6、報表目錄report下生成的文件:
? ? ?6.1 消費流水記錄report_bill:


{"serialno": "201603061903271457262207", "cost": 463, "payfor": "\u6d88\u8d39", "cardno": "1001012345", "starttime": "2016-03-06 19:03"} {"serialno": "201603061903331457262213", "cost": 0, "payfor": "\u6d88\u8d39", "cardno": "1001012345", "starttime": "2016-03-06 19:03"} {"serialno": "201603062148271457272107", "payfor": "\u6d88\u8d39", "starttime": "2016-03-06 21:48", "cost": 9725, "cardno": "1001010002"} {"serialno": "201603062148351457272115", "payfor": "\u6d88\u8d39", "starttime": "2016-03-06 21:48", "cost": 0, "cardno": "1001010002"}
? ? ?6.2 購物歷史記錄shopping_history:


{"test": {"time": "2016-03-06 19:03", "detail": [{"price": 95, "name": "\u8fdb\u53e3\u725b\u5976 \u6b27\u5fb7\u5821(Oldenburger)1L*12", "no": "0001"}, {"price": 205, "name": "\u610f\u5927\u5229\u8fdb\u53e3 Ferrero Rocher\u8d39\u5217\u7f57\u5de7\u514b\u529b(\u76d2)", "no": "0003"}, {"price": 163, "name": "\u4f0a\u83b2\u5a1c2016\u65b0\u6b3e\u8fde\u8863\u88d9\u82f1\u4f26\u683c\u5b50\u9488\u7ec7\u8fde\u8863\u88d9\u5047\u4e24\u4ef6\u88d9\u52a0\u539a", "no": "2001"}], "cost": 463, "serno": "201603061903271457262207"}} {"test": {"time": "2016-03-06 19:03", "detail": [], "cost": 0, "serno": "201603061903331457262213"}} {"super": {"serno": "201603062148271457272107", "time": "2016-03-06 21:48", "cost": 9725, "detail": [{"price": 9326, "no": "1001", "name": "\u4f73\u80fd\uff08Canon\uff09 EOS 6D \u5355\u53cd\u673a\u8eab\uff08\u4e0d\u542b\u955c\u5934\uff09 \u5bb6\u5ead\u5957\u9910"}, {"price": 399, "no": "1004", "name": "\u534e\u4e3a\uff08HUAWEI\uff09\u8363\u8000\u624b\u73afzero \u7ecf\u5178\u9ed1\u77ed"}]}} {"super": {"serno": "201603062148351457272115", "time": "2016-03-06 21:48", "cost": 0, "detail": []}}shopping_history
? ? ?6.3 對帳單statement_卡號:


{"201602620160306": {"payed": 0, "pdate": "2016-04-10", "total": 9725.0, "startdate": "2016-02-6 00:00:00", "interest": 0, "isfinished": 0, "enddate": "2016-03-07 00:00:00", "billdate": "2016-03-06"}}
7、日志文件目錄logs:
? ? ? 7.1 錯誤日志記錄errlog.log


2016-02-08 18:43:09 : Expecting value: line 1 column 113 (char 112) 2016-02-10 15:35:52 : 'dict' object has no attribute 'key' 2016-02-10 15:38:19 : 'dict' object has no attribute 'key' 2016-02-10 15:41:33 : 'dict' object has no attribute 'key' 2016-02-10 15:45:49 : 'dict' object has no attribute 'key' 2016-02-10 15:49:35 : dbapi > load_shop_history > 'dict' object has no attribute 'key' 2016-02-16 09:27:52 : [Errno 2] No such file or directory: '1001012345' 2016-02-16 09:41:54 : [Errno 2] No such file or directory: '1001012345' 2016-02-16 09:50:20 : dbapi > load_statement_list > Extra data: line 1 column 203 - line 1 column 405 (char 202 - 404) 2016-02-16 09:56:42 : dbapi > load_statement_list > Extra data: line 1 column 203 - line 1 column 405 (char 202 - 404) 2016-03-16 12:23:15 : dbapi > load_statement_list > Extra data: line 1 column 208 - line 2 column 1 (char 207 - 410) 2016-03-16 12:31:06 : dbapi > load_statement_list > Extra data: line 1 column 208 - line 2 column 1 (char 207 - 410) 2016-03-05 14:36:51 : dbapi > load_statement_list > Expecting value: line 2 column 1 (char 1) 2016-03-06 18:34:44 : [Errno 2] No such file or directory: 'D:\\python\\pycharm\\python34\\day5\\CreditCard\\database\\users.db' 2016-03-06 18:34:44 : [Errno 2] No such file or directory: 'D:\\python\\pycharm\\python34\\day5\\CreditCard\\database\\creditcard.db' 2016-03-06 18:35:09 : [Errno 2] No such file or directory: 'D:\\python\\pycharm\\python34\\day5\\CreditCard\\database\\users.db' 2016-03-06 18:35:09 : [Errno 2] No such file or directory: 'D:\\python\\pycharm\\python34\\day5\\CreditCard\\database\\creditcard.db' 2016-03-06 18:42:06 : [Errno 2] No such file or directory: '__database' 2016-03-06 18:42:38 : [Errno 2] No such file or directory: '__database' 2016-03-06 18:42:55 : [Errno 2] No such file or directory: '__database' 2016-03-06 18:43:51 : [Errno 2] No such file or directory: '__database' 2016-03-06 18:44:21 : name 'json' is not defined 2016-03-06 18:46:08 : name 'json' is not defined 2016-03-06 18:48:03 : name 'json' is not defined 2016-03-06 18:48:10 : name 'json' is not defined 2016-03-06 18:48:47 : name 'json' is not defined 2016-03-06 18:49:02 : name 'json' is not defined 2016-03-06 18:49:11 : name 'json' is not defined 2016-03-06 18:50:52 : name 'json' is not defined errlog.log
?
自己會好好弄明白,并自己寫出屬于自己的代碼!!!!
?