ATM机+购物车

ATM机+购物车

 

 

 

 

 自己敲的:

bin

 

 

conf

settings

'''
存放配置信息
'''
import os

# 获取项目根目录
BASE_PATH = os.path.dirname(
    os.path.dirname(__file__)
)

# 获取user_data文件夹目录路径
USER_DATA_PATH = os.path.join(
    BASE_PATH, 'db', 'user_data'
)
print(USER_DATA_PATH)


"""
logging配置
"""

# 定义三种日志输出格式 开始
standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \
                  '[%(levelname)s][%(message)s]'  # 其中name为getlogger指定的名字
simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s'

# 定义日志输出格式 结束
# ****************注意1: log文件的目录
BASE_PATH = os.path.dirname(os.path.dirname(__file__))
logfile_dir = os.path.join(BASE_PATH, 'log')
# print(logfile_dir)

# ****************注意2: log文件名
logfile_name = 'atm.log'

# 如果不存在定义的日志目录就创建一个
if not os.path.isdir(logfile_dir):
    os.mkdir(logfile_dir)

# log文件的全路径
logfile_path = os.path.join(logfile_dir, logfile_name)

LOGGING_DIC = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'standard': {
            'format': standard_format
        },
        'simple': {
            'format': simple_format
        },
    },
    'filters': {},
    'handlers': {
        # 打印到终端的日志
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',  # 打印到屏幕
            'formatter': 'simple'
        },
        # 打印到文件的日志,收集info及以上的日志
        'default': {
            'level': 'DEBUG',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件
            'formatter': 'standard',
            'filename': logfile_path,  # 日志文件
            'maxBytes': 1024 * 1024 * 5,  # 日志大小 5M
            'backupCount': 5,
            'encoding': 'utf-8',  # 日志文件的编码,再也不用担心中文log乱码了
        },
    },
    'loggers': {
        # logging.getLogger(__name__)拿到的logger配置
        '': {
            'handlers': ['default', 'console'],  # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
            'level': 'DEBUG',
            'propagate': True,  # 向上(更高level的logger)传递
        },
    },
}

 

 

 

core

admin

from core import src
from interface import admin_interface

# 添加用户
def add_user():
    src.register()

# 修改用户额度
def change_balance():
    while True:
        # 输入修改的用户名
        change_user = input('请输入需要修改额度的用户:').strip()
        # 修改的用户额度
        money = input('请输入需要修改的用户额度:').strip()
        if not money.isdigit():
            continue
        # 调用修改额度接口
        flag, msg = admin_interface.change_balance_interface(
            change_user, money
        )
        if flag:
            print(msg)
            break
        else:
            print(msg)


# 冻结账户
def lock_user():
    while True:
        # 输入修改的用户名
        change_user = input('请输入需要修改额度的用户:').strip()

        flag, msg = admin_interface.lock_user_interface(change_user)
        if flag:
            print(msg)
            break
        else:
            print(msg)



# 管理员功能字典
admin_func = {
    '1': add_user,
    '2': change_balance,
    '3': lock_user,
}

def admin_run():
    while True:
        print('''
        1.添加账户
        2.修改额度
        3.冻结账户
        ''')
        choice = input('请输入管理员功能编号:').strip()

        if choice not in admin_func:
            print('请输入正确的功能编号')
            continue

        admin_func.get(choice)()

src

'''
存放用户视图层
'''

from interface import user_interface
from interface import bank_interface
from interface import shop_interface
from lib import common


# 1、注册功能
@common.login_auth
def register():
    while True:
        # 1)让用户输入用户名与密码进行校验
        username = input('请输入用户名:').strip()
        password = input('请输入密码:').strip()
        re_password = input('请确认密码:').strip()
        # 可以输入自定义金额

        # 小的逻辑处理:比如两次密码是否一致
        if password == re_password:
            # 2)调用接口层的注册接口,将用户名与密码交给接口层进行处理

            # res--->(False,'用户名已存在!')
            # res = user_interface.register_inerface(
            # flag,msg--->(flag--->False,msg--->'用户名已存在!')

            # (True,用户注册成功),(False,这测失败)
            flag, msg = user_interface.register_inerface(
                username, password
            )

            # 3)根据flag判断用户注册是否成功,用于控制break
            if flag:
                print(msg)
                break
            else:
                print(msg)


# 2、登录功能
@common.login_auth
def login():
    # 登录视图
    while True:
        # 1)让用户输入用户名与密码
        username = input('请输入用户名:').strip()
        password = input('请输入密码:').strip()

        # 2)调动接口层,将数据传给登录接口
        flag, msg = user_interface.login_interface(
            username, password
        )

        if flag:
            print(msg)
            global login_user  # 共享login_user信息
            login_user = username
            break
        else:
            print(msg)


# 3、查看余额
@common.login_auth
def check_balance():
    # 1)直接调动查看余额接口,获取用户余额
    balance = user_interface.check_bal_interface(
        login_user
    )

    print(f'用户{login_user}账户余额为:{balance}')


# 4、提现功能
@common.login_auth
def withdraw():
    while True:
        # 1)让用户输入提现金额
        input_money = input('请输入提现金额:').strip()

        # 2)判断用户输入的金额是否是数字
        if not input_money.isdigit():
            print('请重新输入!')
            continue

        # 3)用户提现金额,将提现的金额交付给接口层来处理
        flag, msg = bank_interface.withdraw_interface(
            login_user, input_money
        )
        if flag:
            print(msg)
            break
        else:
            print(msg)


# 5、还款功能
@common.login_auth
def repay():
    while True:
        # 1)让用户输入还款金额
        input_money = input('请输入需要还款的金额:').strip()
        # 2) 判断用户输入的是否是数字
        if not input_money.isdigit():
            print('请输入正确的金额')
            continue
        input_money = int(input_money)

        # 3) 判断用户输入的金额大于0
        if input_money > 0:
            # 4) 调用还款接口
            flag, msg = bank_interface.repay_interface(
                login_user, input_money
            )
            if flag:
                print(msg)
                break
        else:
            print('输入金额不能小于0')


# 6、转账功能
@common.login_auth
def transfer():
    '''
    接收用户输入的转账金额
    接收用户输入的转账目标用户
    :return:
    '''
    while True:

        # 让用户输入转账用户与金额
        to_user = input('请输入转账目标用户:').strip()
        money = input('请输入转账金额:').strip()
        # 判断用户输入的金额是否是数字或者>0
        if not money.isdigit():
            print('请输入正确的金额:')
            continue

        money = int(money)

        if money > 0:
            # 调用转账接口
            flag, msg = bank_interface.transfer_interface(
                # 当前用户,目标用户,转账金额
                login_user, to_user, money
            )
            if flag:
                print(msg)
                break

            else:
                print(msg)

        else:
            print('请输入正确的金额!')


# 7、查看流水
@common.login_auth
def check_flow():
    # 直接调用查看流水接口
    flow_list = bank_interface.check_flow_interface(
        login_user
    )

    if flow_list:
        for flow in flow_list:
            print(flow)
    else:
        print('当前用户没有流水!')


# 8、购物功能
@common.login_auth
def shopping():
    # 不从文件中读取商品数据,直接写
    # 1)创建一个商品列表
    # shop_list = {
    #     '0':{'name':'苹果','price':10},
    # }

    # 列表套列表的商品数据
    shop_list = [
        ['苹果', 10],
        ['egon', 250],
        ['鸭脖', 30],
        ['MacBook', 20000],
    ]

    # 初始化当前购物车:
    shopping_car = {}
    while True:
        # 1)打印商品信息,让用户选择
        # 枚举:enumerate(可迭代对象)--->(可迭代对象的索引,索引对应的值)
        print('================欢迎光临==================')
        for index, shop in enumerate(shop_list):
            shop_name, shop_price = shop
            print(f'商品编号为:[{index}]',
                  f'商品名称:[{shop_name}]',
                  f'商品单价:[{shop_price}]')
        print('==============欢迎下次惠顾================')

        # 2)让用户根据商品编号进行选择
        choice = input('请输入商品编号(是否结账输入y or n):').strip()

        # 2.1)输入的是y ,进入支付结算功能
        if choice == 'y':
            if not shopping_car:
                print('购物车是空的,不能支付,请重新输入!')
                continue
            # 6)调用支付接口进行支付
            flag, msg = shop_interface.shopping_interface(
                login_user, shopping_car
            )
            if flag:
                print(msg)
                break
            else:
                print(msg)
        # 2.2)输入的是n ,添加购物车
        elif choice == 'n':
            # 判断当前用户是否添加过购物车
            if not shopping_car:
                print('购物车是空的,不能支付,请重新输入!')
                continue
            # 7)调用添加购物车接口
            flag, msg = shop_interface.add_shop_car_interface(
                login_user, shopping_car
            )
            if flag:
                print(msg)
                break

        if not choice.isdigit():
            print('请输入正确编号!')
            continue
        choice = int(choice)
        # 3)判断choice是否存在
        if choice not in range(len(shop_list)):
            print('请输入正确的编号!')
            continue
        # 4)获取商品名称与单价
        shop_name, shop_price = shop_list[choice]

        # 5)加入购物车--->必须先将用户的
        # 5.1)判断用户选择的商品是否重复,重复则+1
        if shop_name in shopping_car:
            # 添加商品数量
            shopping_car[shop_name][1] += 1
        else:
            # 否则数量默认为1
            # {'商品名称':['单价','数量']
            shopping_car[shop_name] = [shop_price, 1]
        print('当前购物车:',shopping_car)

# 清空购物车功能(课后作业)



# 9、查看购物车
@common.login_auth
def check_shop_car():
    # 直接调用查看购物车接口
    shop_car = shop_interface.check_shop_car_interface(login_user)
    print(shop_car)


# 10、管理员功能
@common.login_auth
def admin():
    from core import admin
    admin.admin_run()


# 创建函数功能字典
func_dic = {
    '1': register,
    '2': login,
    '3': check_balance,
    '4': withdraw,
    '5': repay,
    '6': transfer,
    '7': check_flow,
    '8': shopping,
    '9': check_shop_car,
    '10': admin,
}


# 视图层主程序
def run():
    while True:
        print('''
        ===== ATM+购物车 =====
            1、注册功能
            2、登录功能
            3、查看余额
            4、提现功能
            5、还款功能
            6、转账功能
            7、查看流水
            8、购物功能
            9、查看购物车
            10、管理员功能
        =======   end    ======
        ''')

        choice = input('请输入功能编号:').strip()
        # 判断功能编号是否存在
        if choice not in func_dic:
            print('请输入正确的功能编号')
            continue
        # 用于选择功能函数
        func_dic.get(choice)()  # func_dic.get('1') ---->register()

 

 

 

db

shop_data

>>egon抱枕.json

>>有趣的用品.json

user_data

>>alex.json

>>egon.json

>>lucky.json

db_handler

'''
数据处理层
        -专门用户处理数据的
'''
import json
import os
from conf import settings


# 查看数据
def select(username):
    # 1) 接收接口层传过来的username用户名,拼接用户json路径
    user_path = os.path.join(
        settings.USER_DATA_PATH, f'{username}.json'
    )

    # 2)校验用户json文件是否存在
    if os.path.exists(user_path):
        # 3)打开数据,并返回给接口层
        with open(user_path, 'r', encoding='utf-8') as f:
            user_dic = json.load(f)
            return user_dic

    # 3)不return,默认return None


# 保存数据(添加数据或者更新数据)
def save(user_dic):
    # 1)拼接用户的数据字典
    username = user_dic.get('username')
    user_path = os.path.join(
        settings.USER_DATA_PATH, f'{username}.json'
    )
    # 2)保存用户数据
    with open('user_path', 'w', encoding='utf-8') as f:
        # ensure_ascii=False让文件中的中文数据,显示更加美观
        json.dump(user_dic, f, ensure_ascii=False)

 

 

 

 

 

 interface

admin_interface

from db import db_handler
from lib import common

# 根据不同的接口类型传入不同的日志对象
admin_logger = common.get_logger(log_type='admin')


# 修改额度接口
def change_balance_interface(username, money):
    user_dic = db_handler.select(username)

    if user_dic:
        # 修改额度
        user_dic['balance'] = int(money)

        # 保存修改后的用户数据
        db_handler.save(user_dic)
        # 记录日志
        msg = f'管理员修改用户:[{username}]额度修改成功!'
        admin_logger.info(msg)
        return True, msg

    return False, '修改额度用户不存在!'


# 冻结账户接口
def lock_user_interface(username):
    user_dic = db_handler.select(username)
    if user_dic:
        # 将locked的默认值改为True
        user_dic['locked'] = True
        db_handler.save(user_dic)
        msg = f'用户{username}冻结成功!'
        admin_logger.info(msg)
        return True, msg

    return False, '冻结用户不存在!'

bank_interface

'''
银行相关接口
'''

from db import db_handler
from lib import common

# 根据不同的接口类型传入不同的日志对象
bank_logger = common.get_logger(log_type='bank')


# 提现接口(手续费5%)
def withdraw_interface(username, money):
    # 1)先获取用户字典
    user_dic = db_handler.select(username)
    # 账户中的金额
    balance = int(user_dic.get('balance'))
    # 提现本金+手续费
    money2 = int(money) * 1.05  # 手续费5%,money2变成float类型

    # 判断用户金额是否足够
    if balance >= money2:
        # 2)修改用户字典进行修改
        balance -= money2
        user_dic['balance'] -= balance
        # 3)记录流水(需要在保存前操作)
        flow = f'用户[{username}]提现金额[{money}]成功,手续费为:[{money2 - float(money)}]'
        user_dic['flow'].append(flow)

        # 4)再保存数据或更新数据
        db_handler.save(user_dic)

        bank_logger.info(flow)
        return True, flow  # int和float类型不能互减

    return False, '提现金额不足,请重新输入!'


# 还款接口
def repay_interface(username, money):
    '''
    获取用户的金额
    给用户的金额做加钱的操作
    :return:
    '''
    # 获取用户字典
    user_dic = db_handler.select(username)

    # 直接做加钱的操作
    user_dic['balance'] += money

    # 记录流水
    flow = f'用户[{username}] 还款[{money}]成功  当前额度为:{user_dic["balance"]}'
    user_dic['flow'].append(flow)
    # 调用数据处理层,将修改后的数据更新
    db_handler.save(user_dic)

    return True, flow


# 转账接口
def transfer_interface(login_user, to_user, money):
    '''
    获取当前用户数据
    获取目标用户数据
    获取转账金额
    :return:
    '''
    # 获取当前用户的字典
    login_user_dic = db_handler.select(login_user)
    # 获取目标用户的字典
    to_user_dic = db_handler.select(to_user)
    # 判断目标用户是否存在
    if not to_user_dic:
        return False, '目标用户不存在'
    # 若用户存在则判当前用户的转账金额是否足够
    if login_user_dic['balance'] >= money:
        # 若足够,则开始给目标用户转账
        # 1)给当前用户做减钱操作
        login_user_dic['balance'] -= money
        # 2)给目标用户做加钱操作
        to_user_dic['balance'] += money
        # 3)记录流水当前用户与目标用户的流水
        # 当前用户流水
        login_user_flow = f'用户:[{login_user}] 给用户:[{to_user}]转账[{money}]成功!'
        login_user_dic['flow'].append(login_user_flow)
        # 目标用户流水
        to_user_flow = f'用户:[{to_user}] 接收用户:[{login_user}]转账[{money}]成功!'
        to_user_dic['flow'].append(to_user_flow)
        # 保存用户数据
        # 1) 调用数据处理层的save功能,保存当前用户数据
        db_handler.save(login_user_dic)
        # 2) 保存目标用户数据
        db_handler.save(to_user_dic)

        return True, login_user_flow, to_user_flow
    return False, '当前用户转账金额不足!'


# 查看流水接口
def check_flow_interface(login_user):
    user_dic = db_handler.select(login_user)
    return user_dic.get('flow')


# 支付接口
def pay_interface(login_user, cost):
    user_dic = db_handler.select(login_user)
    # 判断用户金额是否足够
    if user_dic.get('balance') >= cost:
        # 减钱
        user_dic['balance'] -= cost
        # 记录消费流水
        flow = f'用户消费金额为:[{cost}$]'
        user_dic['flow'].append(flow)
        # 保存数据
        db_handler.save(user_dic)

        # 结果交给购物接口层处理
        return True

    return False

shop_interface

'''
购物商城接口
'''

from db import db_handler
from lib import common

# 根据不同的接口类型传入不同的日志对象
shop_logger = common.get_logger(log_type='shop')


# 商品准备结算接口
def shopping_interface(login_user, shopping_car):
    # 1)计算消费总额
    cost = 0
    for price_number in shopping_car.values():
        price, number = price_number
        cost += (price * number)

    # 导入银行接口
    from interface import bank_interface
    # 逻辑校验成功后,再调用银行的支付接口(支付前先交给bank_interface做)
    flag = bank_interface.repay_interface(login_user, shopping_car)
    if flag:
        msg = f'用户:[{login_user}]支付 [{cost}$] 成功,准备发货!'
        shop_logger.info(msg)
        return True, msg
    return False, '支付失败,金额不足'


# 购物车添加接口
def add_shop_car_interface(login_user, shopping_car):
    # 1)获取当前用户的购物车
    user_dic = db_handler.select(login_user)
    # 获取用户文件中的商品的数据
    shop_car = user_dic.get('shop_car')

    # 2)添加购物车
    # 2.1)判断当前用户选择的商品是否已经存在
    for shop_name, price_number in shopping_car.items():
        # 每个商品的数量
        number = price_number[1]
        # 2.2)若商品重复,则累加商品数量
        if shop_name in shop_car:
            # [单价,数量][1]--->数量
            user_dic['shop_car'][shop_name][1] += number
        else:
            # 2.3)若不是重复,更新到商品字典中
            user_dic['shop_car'].update(
                {shop_name: price_number}
            )
    # 保存用户数据
    db_handler.save(user_dic)

    return True, '添加购物车成功!'


# 查看购物车接口
def check_shop_car_interface(username):
    user_dic = db_handler.select(username)
    return user_dic.get('shop_car')

user_interface

'''
逻辑接口层
        用户层
'''
from db import db_handler
from lib import common

# 根据不同的接口类型传入不同的日志对象
user_logger = common.get_logger(log_type='user')

# 注册接口
def register_inerface(username, password, balance=15000):
    # 2)查看用户是否存在
    # 2.1)调用数据处理层中的select函数,会返回用户字典或者None
    user_dic = db_handler.select(username)
    # {user: user,pwd:pwd...} or None
    # 若用户存在,则return,告诉用户重新输入
    if user_dic:
        # return(False,'用户名已存在!')
        return False, '用户名已存在!'

    # 3)若用户不存在,则保存用户数据
    # 做密码加密
    password = common.get_pwd_md5(password)

    # 3.1)组织用户的数据字典信息
    user_dic = {
        'username': username,
        'passsword': password,
        'balance': balance,
        # 用于记录用户流水的列表
        'flow': [],
        # 用于记录用户购物车
        'shop_car': {},
        # locked:用于记录用户是否被冻结
        # False:未冻结    True:已被冻结
        'locked': False
    }
    # 3.2)保存数据
    db_handler.save(user_dic)
    msg = f'{username}注册成功'
    # 3.3)记录日志
    user_logger.info(msg)

    return True, msg


# 登录接口
def login_interface(username, password):
    # 1)先查看当前用户数据是否存在,用于判断用户是否存在
    # {用户数据字典} or None
    user_dic = db_handler.select(username)
    # 用于判断用户是否存在

    # 若有冻结用户,则需要判断是否锁定
    if user_dic.get('locked'):
        return False, '该用户已被锁定!'

    # 2) 判断用户是否存在
    if user_dic:
        # 给用户输入的密码做一次加密
        password = common.get_pwd_md5(password)
        # 3) 校验密码是否一致
        if password == user_dic.get('password'):
            msg = f'用户:[{username}]登录成功!'
            user_logger.info(msg)
            return True, msg
        else:
            msg = f'用户:[{username}]密码错误!'
            user_logger.warn(msg)
            return False, msg

    msg = f'用户:[{username}]用户不存在,请重新输入!'
    return False, msg


# 查看余额接口
def check_bal_interface(username):
    user_dic = db_handler.select(username)

    return user_dic['balance']    #千万注意!!!一定要注意限定,否则就会读取所有的信息给用户了

 

 

 

 

lib

common

'''
存放公共方法
'''
import hashlib
from conf import settings
import logging.config


# md5加密功能
def get_pwd_md5(password):
    md5_obj = hashlib.md5()
    md5_obj.update(password.encode('utf-8'))
    salt = 'Tank,egon 最帅!'
    md5_obj.update(salt.encode('utf-8'))

    return md5_obj.hexdigest()


# 登录认证装饰器
def login_auth(func):
    from core import src
    def inner(*args, **kwargs):
        if src.login_user:
            res = func(*args, **kwargs)
            return res
        else:
            print('未出示证明,无法享受美好的功能服务')
            src.login()

    return inner


# 添加日志功能:(日志功能在接口层使用)
# 获取日志对象
def get_logger(log_type):  # 传user,bank,购物商城
    '''
    :param  log_type: 比如是user日志,bank日志,购物商城日志
    :return:
    '''
    # 1)加载日志配置信息
    logging.config.dictConfig(
        settings.LOGGING_DIC
    )
    # 2)获取日志对象
    logger = logging.getLogger(log_type)

    return logger

 

 

 

log(后续设计好自动生成)

 

 

 

readme.txt

# 项目的说明书
## 项目:ATM + 购物车

# 项目需求:
    1.额度15000或自定义     -->  注册功能
    2.实现购物商城,买东西加入购物车,调用信用卡接口结账  --> 购物功能、支付功能
    3.可以提现,手续费5%   --> 提现功能
    4.支持多账户登录  --> 登录功能
    5.支持账户间转账  --> 转账功能
    6.记录日常消费 -->  记录流水功能
    7.提供还款接口 -->  还款功能
    8.ATM记录操作日志 --> 记录日志功能
    9.提供管理接口,包括添加账户、用户额度,冻结账户等。。。 ---> 管理员功能
    10.用户认证用装饰器  --> 登录认证装饰器


## "用户视图层" 展示给用户选择的功能
    1、注册功能
    2、登录功能
    3、查看余额
    4、提现功能
    5、还款功能
    6、转账功能
    7、查看流水
    8、购物功能
    9、查看购物车
    10、管理员功能




# 一个项目如何从无到有
## 一 需求分析
1.拿到项目,会先在客户那里一起讨论需求,商量项目的功能是否能实现
得到一个需求文档
2.不同岗位程序猿进行开发
Python:后端,爬虫
-不同的岗位

    -UI界面设计:
        -设计软件的布局,会分局软件的外观切成一张张图片
    -前端:
        -拿到UI交给他的图片,然后搭建网页
        -设计一些页面中,哪些位置需要接收数据,需要进行数据交互
    -后端:
        -直接核心的业务逻辑,调度数据库进行数据的增删改查
    -测试
        -会给代码进行全面测试,比如压力测试,界面测试(CF卡箱子)
    -运维:
        -部署项目



## 二 程序项目设计
### 1、程序设计的好处
    1)思路清晰
    2)不会出现写一半代码时推翻重写
    3)方便自己或以后的同事更好维护

### 2、三层架构设计的好处
    1)把每个功能都分层三部分,逻辑清晰
    2)如果用户更换不同的用户界面或不同,
    的数据储存机制都不会影响接口层的核心
    逻辑代码,扩展性强。
    3)可以在接口层,准确的记录日志与流水。

## 三 分任务开发
## 四 测试
## 五 上线

 

start.py

'''
程序的入口
'''
import sys
import os

# 添加解释器的环境变量
sys.path.append(
    os.path.dirname(__file__)
)

from core import src

# 开始执行项目函数
if __name__ == '__main__':
    # 先执行用户视图层
    src.run()

 

 

 

 

 

 

 

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

参考答案:

bin

    
#!_ * _ coding:utf-8 _ * _
#__author__:“lucky”
__init__.py
#!_*_coding:utf-8_*_
#__author__:"Alex Li"

import os
import sys
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
print(base_dir)
sys.path.append(base_dir)

from core import main

if __name__ == '__main__':
    main.run()
atm.py
 
manage.py

 

conf

#!_ * _ coding:utf-8 _ * _
#__author__:“lucky”
__init__.py
#!_*_coding:utf-8_*_
#__author__:"Alex Li"
import os
import sys
import logging
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


DATABASE = {
    'engine': 'file_storage', #support mysql,postgresql in the future
    'name':'accounts',
    'path': "%s/db" % BASE_DIR
}


LOG_LEVEL = logging.INFO
LOG_TYPES = {
    'transaction': 'transactions.log',
    'access': 'access.log',
}

TRANSACTION_TYPE = {
    'repay':{'action':'plus', 'interest':0},
    'withdraw':{'action':'minus', 'interest':0.05},
    'transfer':{'action':'minus', 'interest':0.05},
    'consume':{'action':'minus', 'interest':0},

}
settings.py

 

core

    
#!_ * _ coding:utf-8 _ * _
#__author__:“lucky”
__init__.py
#!_*_coding:utf-8_*_
#__author__:"lucky"

import json
import time
from core import db_handler
from conf import settings


def load_current_balance(account_id):
    '''
    return account balance and other basic info
    :param account_id:
    :return:
    '''
    # db_path = db_handler.db_handler(settings.DATABASE)
    # account_file = "%s/%s.json" %(db_path,account_id)
    #
    db_api = db_handler.db_handler()
    data = db_api("select * from accounts where account=%s" % account_id)

    return data

    # with open(account_file) as f:
    #     acc_data = json.load(f)
    #     return  acc_data
def dump_account(account_data):
    '''
    after updated transaction or account data , dump it back to file db
    :param account_data:
    :return:
    '''
    db_api = db_handler.db_handler()
    data = db_api("update accounts where account=%s" % account_data['id'],account_data=account_data)

    # db_path = db_handler.db_handler(settings.DATABASE)
    # account_file = "%s/%s.json" %(db_path,account_data['id'])
    # with open(account_file, 'w') as f:
    #     acc_data = json.dump(account_data,f)

    return True
accounts.py
#!_*_coding:utf-8_*_
#__author__:"lucky"
import os
from core import db_handler
from conf import settings
from core import logger
import json
import time



def login_required(func):
    "验证用户是否登录"

    def wrapper(*args,**kwargs):
        #print('--wrapper--->',args,kwargs)
        if args[0].get('is_authenticated'):
            return func(*args,**kwargs)
        else:
            exit("User is not authenticated.")
    return wrapper


def acc_auth(account,password):
    '''
    account auth func
    :param account: credit account number
    :param password: credit card password
    :return: if passed the authentication , retun the account object, otherwise ,return None
    '''
    db_path = db_handler.db_handler(settings.DATABASE)
    account_file = "%s/%s.json" %(db_path,account)
    print(account_file)
    if os.path.isfile(account_file):
        with open(account_file,'r') as f:
            account_data = json.load(f)
            if account_data['password'] == password:
                exp_time_stamp = time.mktime(time.strptime(account_data['expire_date'], "%Y-%m-%d"))
                if time.time() >exp_time_stamp:
                    print("\033[31;1mAccount [%s] has expired,please contact the back to get a new card!\033[0m" % account)
                else: #passed the authentication
                    return  account_data
            else:
                print("\033[31;1mAccount ID or password is incorrect!\033[0m")
    else:
        print("\033[31;1mAccount [%s] does not exist!\033[0m" % account)


def acc_auth2(account,password):
    '''
    优化版认证接口
    :param account: credit account number
    :param password: credit card password
    :return: if passed the authentication , retun the account object, otherwise ,return None
    '''
    db_api = db_handler.db_handler()
    data = db_api("select * from accounts where account=%s" % account)


    if data['password'] == password:
        exp_time_stamp = time.mktime(time.strptime(data['expire_date'], "%Y-%m-%d"))
        if time.time() > exp_time_stamp:
            print("\033[31;1mAccount [%s] has expired,please contact the back to get a new card!\033[0m" % account)
        else:  # passed the authentication
            return data
    else:
        print("\033[31;1mAccount ID or password is incorrect!\033[0m")

def acc_login(user_data,log_obj):
    '''
    account login func
    :user_data: user info data , only saves in memory
    :return:
    '''
    retry_count = 0
    while user_data['is_authenticated'] is not True and retry_count < 3 :
        account = input("\033[32;1maccount:\033[0m").strip()
        password = input("\033[32;1mpassword:\033[0m").strip()
        auth = acc_auth2(account, password)
        if auth: #not None means passed the authentication
            user_data['is_authenticated'] = True
            user_data['account_id'] = account
            #print("welcome")
            return auth
        retry_count +=1
    else:
        log_obj.error("account [%s] too many login attempts" % account)
        exit()
auth.py
#!_*_coding:utf-8_*_
#__author__:"lucky"

'''
handle all the database interactions
'''
import json,time ,os
from  conf import settings
def file_db_handle(conn_params):
    '''
    parse the db file path
    :param conn_params: the db connection params set in settings
    :return:
    '''
    print('file db:',conn_params)
    #db_path ='%s/%s' %(conn_params['path'],conn_params['name'])
    return file_execute

def db_handler():
    '''
    connect to db
    :param conn_parms: the db connection params set in settings
    :return:a
    '''
    conn_params = settings.DATABASE
    if conn_params['engine'] == 'file_storage':
        return file_db_handle(conn_params)
    elif conn_params['engine'] == 'mysql':
        pass #todo



def file_execute(sql,**kwargs):
    conn_params = settings.DATABASE
    db_path = '%s/%s' % (conn_params['path'], conn_params['name'])

    print(sql,db_path)
    sql_list = sql.split("where")
    print(sql_list)
    if sql_list[0].startswith("select") and len(sql_list)> 1:#has where clause
        column,val = sql_list[1].strip().split("=")

        if column == 'account':
            account_file = "%s/%s.json" % (db_path, val)
            print(account_file)
            if os.path.isfile(account_file):
                with open(account_file, 'r') as f:
                    account_data = json.load(f)
                    return account_data
            else:
                exit("\033[31;1mAccount [%s] does not exist!\033[0m" % val )

    elif sql_list[0].startswith("update") and len(sql_list)> 1:#has where clause
        column, val = sql_list[1].strip().split("=")
        if column == 'account':
            account_file = "%s/%s.json" % (db_path, val)
            #print(account_file)
            if os.path.isfile(account_file):
                account_data = kwargs.get("account_data")
                with open(account_file, 'w') as f:
                    acc_data = json.dump(account_data, f)
                return True
db_handler.py
#!_*_coding:utf-8_*_
#__author__:"lucky"

'''
handle all the logging works
'''

import logging
from conf import settings

def logger(log_type):

    #create logger
    logger = logging.getLogger(log_type)
    logger.setLevel(settings.LOG_LEVEL)


    # create console handler and set level to debug
    ch = logging.StreamHandler()
    ch.setLevel(settings.LOG_LEVEL)

    # create file handler and set level to warning
    log_file = "%s/log/%s" %(settings.BASE_DIR, settings.LOG_TYPES[log_type])
    fh = logging.FileHandler(log_file)
    fh.setLevel(settings.LOG_LEVEL)
    # create formatter
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

    # add formatter to ch and fh
    ch.setFormatter(formatter)
    fh.setFormatter(formatter)

    # add ch and fh to logger
    logger.addHandler(ch)
    logger.addHandler(fh)

    return logger
    # 'application' code
    '''logger.debug('debug message')
    logger.info('info message')
    logger.warn('warn message')
    logger.error('error message')
    logger.critical('critical message')'''
logger.py
  1 #!_*_coding:utf-8_*_
  2 #__author__:"lucky"
  3 
  4 '''
  5 main program handle module , handle all the user interaction stuff
  6 '''
  7 
  8 from core import auth
  9 from core import accounts
 10 from core import logger
 11 from core import accounts
 12 from core import transaction
 13 from core.auth import login_required
 14 import time
 15 
 16 #transaction logger
 17 trans_logger = logger.logger('transaction')
 18 #access logger
 19 access_logger = logger.logger('access')
 20 
 21 
 22 #temp account data ,only saves the data in memory
 23 user_data = {
 24     'account_id':None,
 25     'is_authenticated':False,
 26     'account_data':None
 27 
 28 }
 29 
 30 def account_info(acc_data):
 31     print(user_data)
 32 
 33 @login_required
 34 def repay(acc_data):
 35     '''
 36     print current balance and let user repay the bill
 37     :return:
 38     '''
 39     account_data = accounts.load_current_balance(acc_data['account_id'])
 40     #for k,v in account_data.items():
 41     #    print(k,v )
 42     current_balance= ''' --------- BALANCE INFO --------
 43         Credit :    %s
 44         Balance:    %s''' %(account_data['credit'],account_data['balance'])
 45     print(current_balance)
 46     back_flag = False
 47     while not back_flag:
 48         repay_amount = input("\033[33;1mInput repay amount:\033[0m").strip()
 49         if len(repay_amount) >0 and repay_amount.isdigit():
 50             print('ddd 00')
 51             new_balance = transaction.make_transaction(trans_logger,account_data,'repay', repay_amount)
 52             if new_balance:
 53                 print('''\033[42;1mNew Balance:%s\033[0m''' %(new_balance['balance']))
 54 
 55         else:
 56             print('\033[31;1m[%s] is not a valid amount, only accept integer!\033[0m' % repay_amount)
 57 
 58         if repay_amount == 'b':
 59             back_flag = True
 60 def withdraw(acc_data):
 61     '''
 62     print current balance and let user do the withdraw action
 63     :param acc_data:
 64     :return:
 65     '''
 66     account_data = accounts.load_current_balance(acc_data['account_id'])
 67     current_balance= ''' --------- BALANCE INFO --------
 68         Credit :    %s
 69         Balance:    %s''' %(account_data['credit'],account_data['balance'])
 70     print(current_balance)
 71     back_flag = False
 72     while not back_flag:
 73         withdraw_amount = input("\033[33;1mInput withdraw amount:\033[0m").strip()
 74         if len(withdraw_amount) >0 and withdraw_amount.isdigit():
 75             new_balance = transaction.make_transaction(trans_logger,account_data,'withdraw', withdraw_amount)
 76             if new_balance:
 77                 print('''\033[42;1mNew Balance:%s\033[0m''' %(new_balance['balance']))
 78 
 79         else:
 80             print('\033[31;1m[%s] is not a valid amount, only accept integer!\033[0m' % withdraw_amount)
 81 
 82         if withdraw_amount == 'b':
 83             back_flag = True
 84 
 85 def transfer(acc_data):
 86     pass
 87 def pay_check(acc_data):
 88     pass
 89 def logout(acc_data):
 90     pass
 91 def interactive(acc_data):
 92     '''
 93     interact with user
 94     :return:
 95     '''
 96     menu = u'''
 97     ------- Oldboy Bank ---------
 98     \033[32;1m1.  账户信息
 99     2.  还款(功能已实现)
100     3.  取款(功能已实现)
101     4.  转账
102     5.  账单
103     6.  退出
104     \033[0m'''
105     menu_dic = {
106         '1': account_info,
107         '2': repay,
108         '3': withdraw,
109         '4': transfer,
110         '5': pay_check,
111         '6': logout,
112     }
113     exit_flag = False
114     while not exit_flag:
115         print(menu)
116         user_option = input(">>:").strip()
117         if user_option in menu_dic:
118             print('accdata',acc_data)
119             #acc_data['is_authenticated'] = False
120             menu_dic[user_option](acc_data)
121 
122         else:
123             print("\033[31;1mOption does not exist!\033[0m")
124 def run():
125     '''
126     this function will be called right a way when the program started, here handles the user interaction stuff
127     :return:
128     '''
129     acc_data = auth.acc_login(user_data,access_logger)
130     if user_data['is_authenticated']:
131         user_data['account_data'] = acc_data
132         interactive(user_data)
main.py
 1 #!_*_coding:utf-8_*_
 2 #__author__:"lucky"
 3 
 4 from conf import settings
 5 from core import accounts
 6 from core import logger
 7 #transaction logger
 8 
 9 
10 
11 def make_transaction(log_obj,account_data,tran_type,amount,**others):
12     '''
13     deal all the user transactions
14     :param account_data: user account data
15     :param tran_type: transaction type
16     :param amount: transaction amount
17     :param others: mainly for logging usage
18     :return:
19     '''
20     amount = float(amount)
21     if tran_type in  settings.TRANSACTION_TYPE:
22 
23         interest =  amount * settings.TRANSACTION_TYPE[tran_type]['interest']
24         old_balance = account_data['balance']
25         if settings.TRANSACTION_TYPE[tran_type]['action'] == 'plus':
26             new_balance = old_balance + amount + interest
27         elif settings.TRANSACTION_TYPE[tran_type]['action'] == 'minus':
28             new_balance = old_balance - amount - interest
29             #check credit
30             if  new_balance <0:
31                 print('''\033[31;1mYour credit [%s] is not enough for this transaction [-%s], your current balance is
32                 [%s]''' %(account_data['credit'],(amount + interest), old_balance ))
33                 return
34         account_data['balance'] = new_balance
35         accounts.dump_account(account_data) #save the new balance back to file
36         log_obj.info("account:%s   action:%s    amount:%s   interest:%s" %
37                           (account_data['id'], tran_type, amount,interest) )
38         return account_data
39     else:
40         print("\033[31;1mTransaction type [%s] is not exist!\033[0m" % tran_type)
transaction.py

 

db

accounts文件夹:
里面放一些需要存储的数据
#!_*_coding:utf-8_*_
#__author__:"lucky"
__init__.py
#!_*_coding:utf-8_*_
#__author__:"lucky"


import json
acc_dic = {
    'id': 1234,
    'password': 'abc',
    'credit': 15000,
    'balance': 15000,
    'enroll_date': '2020-04-01',
    'expire_date': '2025-03-31',
    'pay_day': 22,
    'status': 0 # 0 = normal, 1 = locked, 2 = disabled
}

print(json.dumps(acc_dic))
account_sample

 

log

#!_*_coding:utf-8_*_
#__author__:"lucky"
__init__.py
会记录用户的一些行为,比如存款或者消费
log.txt

 

readme

程序介绍:
    实现ATM常用功能
    功能全部用python的基础知识实现,用到了time\os\sys\json\open\logging\函数\模块知识, 主要帮给大家一个简单的模块化编程的示例

    注意:只实现了"还款""取现功能"

程序结构:
day5-atm/
├── README
├── atm #ATM主程目录
│   ├── __init__.py
│   ├── bin #ATM 执行文件 目录
│   │   ├── __init__.py
│   │   ├── atm.py  #ATM 执行程序
│   │   └── manage.py #ATM 管理端,未实现
│   ├── conf #配置文件
│   │   ├── __init__.py
│   │   └── settings.py
│   ├── core #主要程序逻辑都 在这个目录 里
│   │   ├── __init__.py
│   │   ├── accounts.py  #用于从文件里加载和存储账户数据
│   │   ├── auth.py      #用户认证模块
│   │   ├── db_handler.py   #数据库连接引擎
│   │   ├── logger.py       #日志记录模块
│   │   ├── main.py         #主逻辑交互程序
│   │   └── transaction.py  #记账\还钱\取钱等所有的与账户金额相关的操作都 在这
│   ├── db  #用户数据存储的地方
│   │   ├── __init__.py
│   │   ├── account_sample.py #生成一个初始的账户数据 ,把这个数据 存成一个 以这个账户id为文件名的文件,放在accounts目录 就行了,程序自己去会这里找
│   │   └── accounts #存各个用户的账户数据 ,一个用户一个文件
│   │       └── 1234.json #一个用户账户示例文件
│   └── log #日志目录
│       ├── __init__.py
│       ├── access.log #用户访问和操作的相关日志
│       └── transactions.log    #所有的交易日志
└── shopping_mall #电子商城程序,需单独实现
    └── __init__.py
readme.txt

 

posted @ 2020-04-01 23:40  凌醉枫  阅读(270)  评论(0编辑  收藏  举报