需求:
模拟实现一个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
自己会好好弄明白,并自己写出属于自己的代码!!!!