简介 |
ATM购物作业要求:
- 额度15000¥或自定义
- 实现购物商城,买东西加入购物车,调用信用卡接口结账
- 可以提现,手续费5%
- 每月22号出账单,每月10号为还款日,过期未还,按欠款总额的万分之5,每日计息
- 支持多账户登录
- 支持账户间转账
- 记录每月日常消费流水记录
- 提供还款接口
- ATM记录操作日志
- 提供管理接口,包括添加账户、用户额度、冻结账户等
程序要求:
分级目录,包含以下文件夹:
bin可执行文件
- atm.py start启动进入
- shopping.py 购物程序
- 要求每个程序块不超过10行config 配置文件
-user_db数据库
-log_format日志格式化modules/core/lib核心文件(不依赖其他文件,可通用的.py)
-提供的接口文件src连接文件(所有的业务逻辑代码)
-代码逻辑的实现模块log日志输出保存文件夹
- db数据库保存文件夹
文件目录 |
在ATM总文件夹下,建立bin、config、db、lib、src文件夹,如下图:
如何在不同文件夹下调用其他文件的py程序呢?
利用如下代码增加路径,并且位置有讲究:
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(_file_))))#增加当前绝对路径
放错位置的sys.path.append:
调整sys.path.append上下位置后,可顺利执行:
还包括了“F:\My_PyCharm_Code”路径
bin文件 |
admin.py |
只用于管理员的登录管理程序:
1、创建账户
2、删除账户
3、冻结账户
4、查询账户
5、添加管理员账户
#_*_coding:utf-8_*_
__author__ = 'Alex_XT'
import os
import sys
path_dir = sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))#增加当前绝对路径
for item in sys.path:
print(item)
from src import admin as src_admin
if __name__ == "__main__":
src_admin.run()
client.py |
只用于普通账户的
1、账户信息
2、还款
3、取款
4、转账
5、账单
#_*_coding:utf-8_*_
__author__ = 'Alex_XT'
import sys
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from src import client as src_client
if __name__ == "__main__":
src_client.run()
crontab.py |
#_*_coding:utf-8_*_
__author__ = 'Alex_XT'
from src import crontab as src_crontab
if __name__ == "__main__":
src_crontab.run()
config文件 |
settings.py |
这里存放了管理员的数据库db路径和普通用户成员的数据库路径:
#_*_coding:utf-8_*_
__author__ = 'Alex_XT'
import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
ADMIN_DIR_FOLDER = os.path.join(BASE_DIR,'db','admin')
USER_DIR_FOLDER = os.path.join(BASE_DIR,'db','userinfo')
探讨os模块的作用:
如果少写一个os.path.dirname的情况:
就是所在py文件路径
#_*_coding:utf-8_*_
__author__ = 'Alex_XT'
import os
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
# USER_DIR_FOLDER = os.path.join(os.path.dirname(os.path.dirname(BASE_DIR,"db","admin")))
print(BASE_DIR)
如果加上os.path.dirname的情况,会将上一级路径添上:
#_*_coding:utf-8_*_
__author__ = 'Alex_XT'
import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# USER_DIR_FOLDER = os.path.join(os.path.dirname(os.path.dirname(BASE_DIR,"db","admin")))
print(BASE_DIR)
如果加上os.path.join的情况,会将路径相拼接:
#_*_coding:utf-8_*_
__author__ = 'Alex_XT'
import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
USER_DIR_FOLDER = os.path.join(BASE_DIR,"db","admin")
print(USER_DIR_FOLDER)
os.path.dirname的情况:
与以上os.path.join的区别
os.path.exists用于判断路径是否存在:
if not os.path.exists(os.path.join(settings.ADMIN_DIR_FOLDER,username)):
print("用户名不存在")
避免“FileExistsError: [WinError 183] 当文件已存在时,无法创建该文件。”的错误!
os.walk用于读取路径文件夹和文件:
它是遍历文件夹下所有的文件:
其中
- root表示string,代表目录的路径;
- sub_folders表示list,包含了当前dirpath路径下所有的子目录名字(不包含目录路径);
- files表示list,包含了当前dirpath路径下所有的非目录子文件的名字(不包含目录路径);
print("有以下管理员:")
for root, sub_folders, files in os.walk(settings.ADMIN_DIR_FOLDER):
print(files)
注意,sub_folders和 files均不包含路径信息,如需完整路径,可使用os.path.join(root, sub_folders)
# image directories
for name in files:
images.append(os.path.join(root, name))
# get 10 sub-folder names
for name in sub_folders:
temp.append(os.path.join(root, name))
os.listdir()用于读取仅当前路径下的文件名:
os.listdir()函数得到的是仅当前路径下的文件名,不包括子目录中的文件,所以需要使用递归的方法得到全部文件名。
# -*- coding: utf-8 -*-
import os
def listdir(path, list_name):
for file in os.listdir(path):
file_path = os.path.join(path, file)
if os.path.isdir(file_path):
listdir(file_path, list_name)
elif os.path.splitext(file_path)[1]=='.jpeg':
list_name.append(file_path)
本例:
print("有以下账户:")
for sub_folders in os.listdir(settings.USER_DIR_FOLDER):
print(sub_folders)
lib文件 |
commons.py |
用于密文的设置,md5加密:
#_*_coding:utf-8_*_
__author__ = 'Alex_XT'
import hashlib
def md5(arg):
"""
md5加密
:param arg:
:return:
"""
obj = hashlib.md5()
obj.update(bytes(arg,encoding='utf-8'))#转为字节
return obj.hexdigest()
def check_paypasswd(CURRENT_USER_INFO,paypasswd):
if md5(paypasswd) == CURRENT_USER_INFO['pay_passwd']:
return True
else:
print("支付密码错误!")
return False
密文的读取与判断:
user_dict = json.load(open(os.path.join(settings.ADMIN_DIR_FOLDER,username),'r'))
if username == user_dict['username'] and commons.md5(password)==user_dict['password']:
CURRENT_USER_INFO['is_authenticated']=True
CURRENT_USER_INFO['current_user'] = username
return True
src文件 |
backend文件logger.py |
# _*_coding:utf-8_*_
__author__ = 'Alex_XT'
import logging
import os
from config import settings
def get_logger(card_num, struct_time):
if struct_time.tm_mday < 23:
file_name = "%s_%s_%d" % (struct_time.tm_year, struct_time.tm_mon, 22)#2017_9_22
else:
file_name = "%s_%s_%d" % (struct_time.tm_year, struct_time.tm_mon + 1, 22)#2017_10_22
file_handler = logging.FileHandler(
os.path.join(settings.USER_DIR_FOLDER, card_num, 'record', file_name),
encoding='utf-8'#用于中文编码输出
)
fmt = logging.Formatter(fmt="%(asctime)s: %(name)s: %(message)s")
file_handler.setFormatter(fmt)
logger1 = logging.Logger('user_logger', level=logging.INFO)
logger1.addHandler(file_handler)
return logger1
admin.py |
# _*_coding:utf-8_*_
__author__ = 'Alex_XT'
import os
import json
from lib import commons
from config import settings
CURRENT_USER_INFO = {'is_authenticated': False, 'current_user': None}
def init():
"""
初始化管理员信息
:return:
"""
dic = {'username': 'Alex', 'password': commons.md5('123')}
# 以dic['username']名字命名文件
json.dump(dic, open(os.path.join(settings.ADMIN_DIR_FOLDER, dic['username']), 'w', encoding='utf-8'))
def create_user():
"""
创建账户
:return:
"""
card_num = input("请输入新卡号:").strip()
# card_num = "6222040209028810"
# 创建目录:在card_num下创建
# 'record'
# "basic_info.json"
if not os.path.exists(os.path.join(settings.USER_DIR_FOLDER, card_num, 'record')):
os.makedirs(os.path.join(settings.USER_DIR_FOLDER, card_num, 'record'))
else:
print("该账户已存在!继续执行,信息会被覆盖!!!")
base_info = {'username': input("username:"), # 'Tim',
'card': card_num,
'password': commons.md5(input("password:")), # '123'
'pay_passwd': commons.md5(input("pay_passwd:")), # '123'
"credit": 15000, # 信用卡额度
"balance": 15000, # 本月可用额度
"saving": 0, # 储蓄金额
"enroll_date": "2016-01-01",
"expire_date": "2021-01-01",
'status': 0, # 0 = normal,1 = locked, 2 = disabled
"debt": [], # 欠款记录,如:
# [{'date':"2015-04-10","total_debt":80000,"balance_debt":5000},{'date':"2015-05-10","total_debt":80000,"balance_debt":5000}]
}
json.dump(base_info, open(os.path.join(settings.USER_DIR_FOLDER, card_num, "basic_info.json"), 'w'))
print("创建成功!")
def remove_user():
"""
移除用户
:return:
"""
pass
def locked_user():
"""
冻结账户
:return:
"""
pass
def search():
"""
搜索用户
:return:
"""
search_obj = input("1、管理员信息\n2、普通用户信息\n信息查询序号>>:")
if search_obj == "1":
print("有以下管理员:")
for root, sub_folders, files in os.walk(settings.ADMIN_DIR_FOLDER):
print(files)
info_obj = input("要查询的名字:")
if not os.path.exists(os.path.join(settings.ADMIN_DIR_FOLDER,info_obj)):
print("管理员名字写错了!")
else:
dic = json.load(open(os.path.join(settings.ADMIN_DIR_FOLDER,info_obj),'r'))
print(dic)
else:
print("有以下账户:")
for sub_folders in os.listdir(settings.USER_DIR_FOLDER):
print(sub_folders)
info_obj = input("要查询的账户:")
if not os.path.exists(os.path.join(settings.USER_DIR_FOLDER,info_obj)):
print("账户号写错了!")
else:
basic_info = json.load(open(os.path.join(settings.USER_DIR_FOLDER,info_obj,"basic_info.json"),'r'))
print(basic_info)
def add_admin():
dic = {"username": input("新增管理员名字:"),
"password": commons.md5(input("管理员密码:"))}
if not os.path.exists(os.path.join(settings.ADMIN_DIR_FOLDER, dic['username'])):
print("该管理员账户可用!")
json.dump(dic, open(os.path.join(settings.ADMIN_DIR_FOLDER, dic['username']), 'w', encoding='utf-8'))
else:
print("该管理员已存在!请重新添加!")
def main():
menu = """
1、创建账户
2、删除账户
3、冻结账户
4、查询账户
5、添加管理员账户
"""
print(menu)
menu_dic = {
'1': create_user,
'2': remove_user,
'3': locked_user,
'4': search,
'5': add_admin,
}
while True:
user_option = input("请选择菜单序号>>:").strip()
if user_option in menu_dic:
menu_dic[user_option]()
else:
print("没有该选项!")
def login():
"""
用户登录
:return:
"""
while True:
username = input("请输入用户名:")
password = input("请输入密码:")
if not os.path.exists(os.path.join(settings.ADMIN_DIR_FOLDER, username)):
print("用户名不存在")
else:
user_dict = json.load(open(os.path.join(settings.ADMIN_DIR_FOLDER, username), 'r'))
if username == user_dict['username'] and commons.md5(password) == user_dict['password']:
CURRENT_USER_INFO['is_authenticated'] = True
CURRENT_USER_INFO['current_user'] = username
return True
else:
print("密码错误!")
def run():
init()
ret = login()
if ret:
main()
client.py |
# _*_coding:utf-8_*_
__author__ = 'Alex_XT'
import os
import time
import json
from lib import commons
from config import settings
from src.backend import logger
CURRENT_USER_INFO = {}
def dump_current_user_info():
json.dump(CURRENT_USER_INFO,
open(os.path.join(settings.USER_DIR_FOLDER, CURRENT_USER_INFO['card'], "basic_info.json"), 'w'))
def write_record(message):
"""
账户记录
:param message:
:return:
"""
struct_time = time.localtime()
logger_obj = logger.get_logger(CURRENT_USER_INFO['card'], struct_time)
logger_obj.info(message)
def account_info():
"""
账户信息
:return:
"""
print(CURRENT_USER_INFO)
def repay():
"""
还款
:return:
"""
pass
def withdraw():
"""
提现
提现时,优先从自己余额中拿,如果余额不足,则使用信用卡(额度限制),提现需要手续费5%
:return:
"""
num = float(input("请输入提现金额>>:"))
if CURRENT_USER_INFO['saving'] >= num:
CURRENT_USER_INFO['saving'] -= num
write_record('%s - 储蓄账户:%d ' % ('提现', num))
dump_current_user_info()
else:
temp = num - CURRENT_USER_INFO['saving']
if CURRENT_USER_INFO['balance'] > (temp + temp * 0.05):
CURRENT_USER_INFO['balance'] -= temp
CURRENT_USER_INFO['balance'] -= temp * 0.05
write_record('%s - 储蓄账户:%f 信用卡账户:%f 手续费:%f' % ('提现', CURRENT_USER_INFO['saving'], temp, temp * 0.05))
else:
print("账户余额不足,无法完成体现!")
def transfer():
"""
转账
:return:
"""
pass
def print_bil():
pass
def go_shopping():
Product_List = [("Air Conditioner",50000),("Mac", 12000),("Apple", 5800), ("Bike", 800), ("Python", 80), ("NoteBook", 10)] # 使用了元组
Current_List = []
while True:
print("---index product price---")
for index, item in enumerate(Product_List):
print("{}{}{}{}{}{}$".format(" " * 3, index, " " * 10, item[0], " " * 9, item[1]))
print("\033[31;1m 按q退出购物\033[0m")
choose_item = input("Choose index: ")
if choose_item.isdigit():
choose_item = int(choose_item)
if (choose_item < len(Product_List)) and (choose_item >= 0):
Current_pro = Product_List[choose_item]
if Current_pro[1] < CURRENT_USER_INFO['saving']:
CURRENT_USER_INFO['saving'] -= Current_pro[1]
Current_List.append(Current_pro)
print(" Your Current List:%s Saving is: %s Balance is: \033[31;1m %s \033[0m" % (
Current_List, CURRENT_USER_INFO['saving'], CURRENT_USER_INFO['balance']))
write_record('%s - 储蓄账户:%f ' % ('消费', Current_pro[1]))
dump_current_user_info()
elif Current_pro[1] < CURRENT_USER_INFO['balance']:
print("\033[43;1m Your saving money is not enough!!! %s\033[0m" % CURRENT_USER_INFO['saving'])
Current_List.append(Current_pro)
print(" Your Current List:%s Saving is: %s Balance is: \033[31;1m %s \033[0m" % (
Current_List, CURRENT_USER_INFO['saving'], CURRENT_USER_INFO['balance']))
choose_pay = input("请选择付款方式:\n1、储蓄余额+信用卡;\n2、只信用卡;")
if choose_pay == "1":
paypasswd = input("你选择了另外支付用信用卡消费,请输入支付密码:")
temp = Current_pro[1] - CURRENT_USER_INFO['saving']
if commons.check_paypasswd(CURRENT_USER_INFO,paypasswd) and CURRENT_USER_INFO['balance'] > (temp):
CURRENT_USER_INFO['balance'] -= temp
message = '%s - 储蓄账户:%f - 信用卡支付:%f' % ('消费', CURRENT_USER_INFO['saving'], temp)
write_record(message)
CURRENT_USER_INFO['saving'] = 0
dump_current_user_info()
print(" Your Current List:%s Saving is: %s Balance is: \033[31;1m %s \033[0m" % (
Current_List, CURRENT_USER_INFO['saving'], CURRENT_USER_INFO['balance']))
print(message)
elif choose_pay == "2":
paypasswd = input("你只选择了信用卡消费,请输入支付密码:")
if commons.check_paypasswd(CURRENT_USER_INFO,paypasswd) and CURRENT_USER_INFO['balance'] > (Current_pro[1]):
CURRENT_USER_INFO['balance'] -= Current_pro[1]
message = '%s - 信用卡支付:%f' % ('消费', Current_pro[1])
write_record(message)
dump_current_user_info()
print(" Your Current List:%s Saving is: %s Balance is: \033[31;1m %s \033[0m" % (
Current_List, CURRENT_USER_INFO['saving'], CURRENT_USER_INFO['balance']))
print(message)
else:
print("余额不足,请退出!!!")
else:
print("\033[41;1m Invalid index! Continue... \033[0m")
continue
elif choose_item == "q":
print("-------Your List-------")
for item in Current_List:
print(item[0], "--->>", item[1])
# exit("Final saving is: \033[31;1m %s \033[0m ,final balance is: %s" % (
# CURRENT_USER_INFO['saving'], CURRENT_USER_INFO['balance']))
print("Final saving is: \033[31;1m %s \033[0m ,final balance is: %s" % (
CURRENT_USER_INFO['saving'], CURRENT_USER_INFO['balance']))
break
else:
print("\033[41;1m Wrong Command! Continue...\033[0m")
continue
def main():
menu = '''
1、账户信息
2、还款
3、取款/提现
4、转账
5、账单
6、购物
'''
menu_dic = {
'1': account_info,
'2': repay,
'3': withdraw,
'4': transfer,
'5': print_bil,
'6': go_shopping,
}
while True:
print(menu)
user_option = input("选择菜单序号>>:").strip()
if user_option in menu_dic:
menu_dic[user_option]()
else:
print("该选项不存在!")
def init(basic_info):
CURRENT_USER_INFO.update(basic_info)
def login():
"""
登录
:return:
"""
while True:
card_num = input("卡号:") # "6222040209028810"
if not os.path.exists(os.path.join(settings.USER_DIR_FOLDER, card_num)):
print("卡号不存在!")
else:
password = input("请输入密码:")
basic_info = json.load(open(os.path.join(settings.USER_DIR_FOLDER, card_num, "basic_info.json"), 'r'))
if basic_info['password'] == commons.md5(password):
init(basic_info)
print("登录成功!")
return True
else:
print("密码错误!")
def run():
ret = login()
if ret:
main()
crontab.py |
#_*_coding:utf-8_*_
__author__ = 'Alex_XT'
import os
import json
import time
from config import settings
from src.backend import logger
def main():
card_list = os.listdir(settings.USER_DIR_FOLDER)
for card in card_list:
basic_info = json.load(open(os.path.join(settings.USER_DIR_FOLDER,card,'basic_info.json')))
struct_time = time.localtime()
#循环账单列表,为每月的欠款计息,并写入到当月账单中
for item in basic_info['debt']:
interest = item['total_debt']*0.0005
if basic_info['saving']>=interest:
basic_info['saving']-=interest
else:
temp = interest-basic_info['saving']
basic_info['balance']-=temp
logger_obj = logger.get_logger(card,struct_time)
logger_obj.info("欠款利息 - %f - 备注:未还款日期%s;共欠款%f,未还款%f" % (interest, item['date'], item['total_debt'], item['balance_debt']))
json.dump(
basic_info,
open(os.path.join(settings.USER_DIR_FOLDER, basic_info['card'], "basic_info.json"), 'w')
)
# 如果当前等于10号(9号之前)
# 当前余额为负值,则将值添加到账单列表中,开始计息,同时,本月可用额度恢复。
if struct_time.tm_mday == 11 and basic_info['credit']>basic_info['balance']:
date = time.struct_time("%Y-%m-%d")
dic = {"date":date,
"total_debt":basic_info['credit']-basic_info['balance'],
"balance_debt":basic_info['credit']-basic_info['balance'],
}
basic_info['debt'].append(dic)
#恢复可用额度
basic_info['balance']=basic_info['credit']
json.dump(
basic_info,
open(os.path.join(settings.USER_DIR_FOLDER,basic_info['card'],"basic_info.json"),'w')
)
def run():
main()
实现效果 |
管理员 |
普通用户 |
登录
提现
购物
付款
退出
流水账信息
代码下载
参考
【1】python实现购物车程序 - 快递小可的博客 - CSDN博客
http://blog.csdn.net/sxingming/article/details/52334488
【2】Python获取指定文件夹下的文件名 - CSDN博客
http://blog.csdn.net/lsq2902101015/article/details/51305825