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

简介

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用于读取路径文件夹和文件:
它是遍历文件夹下所有的文件:
其中

  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 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

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

何以问天涯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值