Python购物流程结合ATM接口实现

来源:互联网 发布:joo yeon seo韩国 编辑:程序博客网 时间:2024/06/02 03:33

简介

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 osimport syspath_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_adminif __name__ == "__main__":    src_admin.run()

client.py

只用于普通账户的
1、账户信息
2、还款
3、取款
4、转账
5、账单

#_*_coding:utf-8_*___author__ = 'Alex_XT'import sysimport ossys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))from src import client as src_clientif __name__ == "__main__":    src_client.run()

crontab.py

#_*_coding:utf-8_*___author__ = 'Alex_XT'from src import crontab as src_crontabif __name__ == "__main__":    src_crontab.run()

config文件

settings.py

这里存放了管理员的数据库db路径和普通用户成员的数据库路径:

#_*_coding:utf-8_*___author__ = 'Alex_XT'import osBASE_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 osBASE_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 osBASE_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 osBASE_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用于读取路径文件夹和文件:
它是遍历文件夹下所有的文件:
其中

  1. root表示string,代表目录的路径;
  2. sub_folders表示list,包含了当前dirpath路径下所有的子目录名字(不包含目录路径);
  3. 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 osdef 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 hashlibdef 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 loggingimport osfrom config import settingsdef 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 osimport jsonfrom lib import commonsfrom config import settingsCURRENT_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:    """    passdef locked_user():    """    冻结账户    :return:    """    passdef 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 osimport timeimport jsonfrom lib import commonsfrom config import settingsfrom src.backend import loggerCURRENT_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:    """    passdef 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:    """    passdef print_bil():    passdef 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")            continuedef 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 osimport jsonimport timefrom config import settingsfrom src.backend import loggerdef 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