【2.0】ATM功能实现

发布时间 2023-12-31 12:38:23作者: Chimengmeng
import os
from datetime import datetime


def create_path(*args):
    for path in args:
        if not os.path.exists(path):
            os.mkdir(path)


# 定义根路径
BASE_DIR = os.path.dirname(__file__)
# 声明数据库路径
DB_DIR = os.path.join(BASE_DIR, 'database')

# 声明用户名和密码路径
PWD_DIR = os.path.join(DB_DIR, 'Pwd')
BANK_DIR = os.path.join(DB_DIR, 'Bank')
LOG_DIR = os.path.join(DB_DIR, 'Log')
FLOW_DIR = os.path.join(DB_DIR, 'Flow')

# 构建 路径列表
path_list = [PWD_DIR, BANK_DIR, LOG_DIR, FLOW_DIR]
# 解包传值,逐个创建
create_path(*path_list)

# 定义全局用户信息字典
login_user_dict = {
    'username': '',
    'is_admin': False,
    'bank_id': "",
    'bank_pwd': "",
    "bank_balance": 0
}


# 获取到指定文件所在的文件路径
def get_tag_path(path):
    # 用户名和密码存储 : path='user_pwd.txt' ---> database/Pwd/user_pwd.txt
    if 'pwd' in path:
        path = os.path.join(PWD_DIR, path)
    # 银行信息存储 : path='username_bank.txt' ---> database/Bank/username_bank.txt
    elif 'bank' in path:
        path = os.path.join(BANK_DIR, path)
    # 日志信息存储 : path='username_log.txt' ---> database/Log/username_log.txt
    elif 'log' in path:
        path = os.path.join(LOG_DIR, path)
    # 流水信息存储 : path='username_flow.txt' ---> database/Flow/username_flow.txt
    elif 'flow' in path:
        path = os.path.join(FLOW_DIR, path)
    # 返回指定文件的新路径
    return path


# 读取数据并做数据处理
def read_data(tag=None, path=None, mode='r', encoding='utf-8'):
    '''

    :param tag: 标志位 : register login log flow
    :param path: 文件名
    :param mode: 文件操作模式
    :param encoding: 文件默认编码
    :return:
    '''
    # 【1】构建想要的路径  'user_pwd.txt' ---> database/Pwd/user_pwd.txt
    path = get_tag_path(path)
    # 【2】构建基础信息存储字典或列表
    # 【2.1】存储用户名和密码
    user_pwd_dict = {}
    # 【2.2】存储银行信息
    bank_data_dict = {}
    # 【2.3】存储日志/流水信息
    log_flow_list = []
    try:
        # 【3】未注册过的用户,肯定是不存在路径的,所以返回空的信息
        if tag == 'register' and not os.path.exists(path):
            return True, user_pwd_dict
        # 【4】读取数据
        with open(file=path, mode=mode, encoding=encoding) as fp:
            data = fp.read()
        # 【5】对数据结尾进行切分,因为每一行数据都是按照 \n 切分的
        data_list = data.split('\n')
        # 【6】去除掉最后的 空字符串
        data_list.pop()
        # 【7】根据标志位构建对应的数据字典
        # 【7.1】注册/登陆需要的数据
        if tag == 'register' or tag == 'login':
            # (1)遍历信息列表
            for data in data_list:
                # (2)对元素进行切割 username|password|role
                username, password, role = data.split('|')
                # (3)拼接数据字典  {'username':{'password':password,'role':role}}
                user_pwd_dict[username] = {'password': password, 'role': role}
            # (4)返回用户数据字典
            return True, user_pwd_dict
        # 【7.2】银行信息
        elif tag == "bank":
            # (1)遍历数据列表
            for data in data_list:
                # (2)对元素进行切割 username|bank_id|bank_pwd|bank_balance
                username, bank_id, bank_pwd, bank_balance = data.split('|')
                # (3)拼接数据字典 {'username':{'bank_id':bank_id,'bank_pwd':bank_pwd,'bank_balance':bank_balance}}
                bank_data_dict[username] = {'bank_id': 0 if bank_id == 'None' else int(bank_id),
                                            'bank_pwd': 0 if bank_pwd == 'None' else int(bank_pwd),
                                            'bank_balance': 0 if bank_balance == 'None' else int(bank_balance)}
            # (4)返回银行数据字典
            return True, bank_data_dict
        # 【7.3】日志/流水信息
        elif tag == 'log' or tag == 'flow':
            # (1)遍历数据列表
            for data in data_list:
                # (2)将每一条日志/流水添加到列表中即可
                log_flow_list.append(data)
            # (3)返回信息列表
            return True, log_flow_list
    except Exception as error:
        return False, error


# 保存用户数据
def save_data(mode='a', data=None, encoding='utf-8', path=None):
    '''

    :param mode: 默认文件操作模式 a 追加
    :param data: 需要存储的数据
    :param encoding: 默认编码
    :param path: 文件名
    :return: bool,成功/失败
    '''
    try:
        # 【1】获取文件详细路径
        path = get_tag_path(path)
        # 【2】打开文件。写入数据
        with open(file=path, mode=mode, encoding=encoding) as fp:
            # 【3】写入数据 + '\n' 每一行是一条数据
            fp.write(data + '\n')
        # 【4】反馈信息
        return True, f'数据写入成功'
    except Exception as error:
        return False, error


# 获取用户名和密码的公共函数
def get_user_pwd():
    username = input("请输入用户名 :>>>> ").strip()
    password = input("请输入密码 :>>>> ").strip()
    return username, password


# 创建指定数据格式
def create_data_format(tag=None, **kwargs):
    # 【1】默认构建的参数格式为 | 作为拼接的字符串数据
    # 原理:遍历当前关键字参数(字典)的所有值,进行 .join() 拼接
    data = '|'.join(kwargs.values())
    # 【2】标志位声明并做数据处理
    # 【2.1】标志位为当前数据格式为日志/流水数据格式
    # {'username':'username','msg':msg}
    if tag == 'log' or tag == 'flow':
        data = f'当前用户 {kwargs.get("username")} 于 {datetime.now().strftime("%Y年%m月%d日 %H时%M分%S秒")} :>>>> {kwargs.get("msg")} !'
    # 【3】将构建好的数据信息返回
    return data


# 注册函数
def register():
    # 【1】获取用户名和密码
    username, password = get_user_pwd()
    if username == 'dream' and password == '521':
        role = 'admin'
    else:
        role = 'normal'
    # 【2】校验当前用户名是否注册过
    flag, user_pwd_dict = read_data(tag='register', path='user_pwd.txt')
    # 【3】校验当前文件如果存在,则进行用户名是否存在过的校验
    if username in user_pwd_dict.keys():
        return False, f'当前用户名 {username} 已经注册过了!'
    # 【4】当前文件不存在或返回的是空字典
    if flag or not user_pwd_dict:
        # 【5】开始注册,构建数据格式
        # 【5.1】构建用户名和密码数据  username|password|role
        pwd_data = create_data_format(username=username, password=password, role=role)
        # 保存数据  data=需要保存的数据, path=保存数据的路径
        save_data(data=pwd_data, path='user_pwd.txt')
        # 【5.2】构建银行初始化信息数据  bank_username|bank_id|bank_pwd|bank_balance
        bank_data = create_data_format(bank_username=username, bank_id='None', bank_pwd='None', bank_balance='None')
        # 保存数据  data=需要保存的数据, path=保存数据的路径
        save_data(data=bank_data, path=f'{username}_bank.txt')
        # 【5.3】构建日志信息
        log_data = create_data_format(tag='log', username=username, msg='注册成功!')
        # 保存数据  data=需要保存的数据, path=保存数据的路径
        save_data(data=log_data, path=f'{username}_log.txt')
        # 【6】反馈信息
        return True, log_data


def check_init_bank(func):
    def inner(*args, **kwargs):
        if login_user_dict.get('bank_id') == 'None':
            return False, f'{login_user_dict["username"]} 还没有激活银行卡!'
        return func(*args, **kwargs)

    return inner


# 初始化银行信息
def init_bank_data(username):
    '''

    :param username: 需要初始化的用户名
    :return:
    '''
    # 【1】获取到当前登录用户的银行数据
    # {'username':{'bank_id':'None', 'bank_pwd':'None', 'bank_balance':'None'}}
    flag, bank_data_dict = read_data(tag='bank', path=f"{username}_bank.txt")
    # 【2】出错判断
    if not flag:
        return flag, bank_data_dict
    # 【3】从指定银行数据中切取出来当前用户的银行信息
    # {'bank_id':'None', 'bank_pwd':'None', 'bank_balance':'None'}
    bank_data = bank_data_dict.get(username)
    login_user_dict['bank_id'] = bank_data.get('bank_id')
    login_user_dict['bank_pwd'] = bank_data.get('bank_pwd')
    login_user_dict['bank_balance'] = bank_data.get('bank_balance')
    return True, bank_data_dict


def login():
    # 【1】获取用户名和密码
    username, password = get_user_pwd()
    # 【2】获取历史用户注册信息
    flag, user_pwd_dict = read_data(tag='login', path='user_pwd.txt')
    # 【3】根据当前用户名从历史用户信息中将当前用户信息筛选出来
    user_pwd_data = user_pwd_dict.get(username)
    # 【4】判断当前用户信息是否存在(文件不存在或者是用户名不在历史用户信息中)
    if not flag or not user_pwd_data:
        return flag, '请先注册,谢谢!'
    # 【5】校验密码,密码不正确
    if password != user_pwd_data.get('password'):
        log_data = create_data_format(tag='log', username=username, msg=f'用户名{username}登录失败,密码错误!')
        return False, log_data
    # 【6】更新当前用户登陆信息
    login_user_dict['username'] = username
    if user_pwd_data.get('role') == 'admin':
        login_user_dict['is_admin'] = True
    else:
        login_user_dict['is_admin'] = False
    # 【7】初识化银行信息
    init_bank_data(username=username)
    # 【8】记录日志
    log_data = create_data_format(tag='log', username=username, msg=f'用户名{username}登录成功!')
    save_data(data=log_data, path=f'{username}_log.txt')
    # 【9】返回信息
    return True, log_data


# 校验用户是否登录
def login_auth(func):
    def inner(*args, **kwargs):
        if not login_user_dict['username']:
            return False, '未登录,请先登录!'
        return func(*args, **kwargs)

    return inner


@login_auth
@check_init_bank
def active_bank_card():
    # 【0】从已经登录的信息中获取到登陆用户的用户名
    username = login_user_dict['username']
    # 激活银行卡
    # 【1】获取银行卡号:必须是数字并且必须是六位
    bank_id = input("请输入银行账号 :>>>> ").strip()
    if not bank_id.isdigit() or len(bank_id) != 6:
        return False, f'银行账号必须为数字,且必须为6位!'
    # 【2】获取银行密码:必须是数字,并且必须是三位
    bank_pwd = input("请输入银行密码 :>>>> ").strip()
    if not bank_pwd.isdigit() or len(bank_pwd) != 3:
        return False, f'银行密码必须为数字,且必须为3位!'
    # 【3】默认余额为 1000 元
    bank_balance = 1000
    # 【4】构建银行信息 : username|bank_id|bank_pwd|bank_balance
    bank_data = create_data_format(username=username, bank_id=bank_id, bank_pwd=bank_pwd, balance=str(bank_balance))
    save_data(path=f'{username}_bank.txt', data=bank_data)
    # 【5】构建日志信息
    log_data = create_data_format(tag='log', username=username, msg=f'用户{username}激活银行卡成功!')
    save_data(path=f'{username}_log.txt', data=log_data)
    # 【6】构建流水信息
    flow_data = create_data_format(tag='flow', username=username,
                                   msg=f'用户{username}激活银行卡成功!余额为{bank_balance}')
    save_data(path=f'{username}_flow.txt', data=flow_data)
    init_bank_data(username)
    return True, log_data


# 取款功能
@login_auth
@check_init_bank
def withdraw_money():
    username = login_user_dict['username']
    # 【1】获取银行卡号:必须是数字并且必须是六位
    out_balance = input("请输入取款金额 :>>>> ").strip()
    # 【2】判断当前的取款金额
    # 【2.1】必须是数字
    if not out_balance.isdigit():
        return False, '非法的余额!'
    # 【2.2】转为数字类型
    out_balance = int(out_balance)
    # 判断当前取款金额必须大于 0
    if out_balance < 0:
        return False, f'当前取款金额必须大于基础金额0!'
    # 【2.3】判断当前取款金额必须小于当前余额
    print(login_user_dict)
    if out_balance > login_user_dict['bank_balance']:
        return False, f'当前余额不足!'
    # 【3】获取银行密码:必须是数字,并且必须是三位
    bank_pwd = input("请输入银行密码 :>>>> ").strip()
    if not bank_pwd.isdigit() or len(bank_pwd) != 3:
        return False, f'银行密码必须为数字,且必须为3位!'
    bank_pwd = int(bank_pwd)
    # 【4】校验当前取款密码
    if bank_pwd != login_user_dict['bank_pwd']:
        return False, f'当前取款密码错误!'
    # 【5】校验通过,开始取钱
    login_user_dict['bank_balance'] -= out_balance
    # 【6】构建数据格式,存储数据
    # 【6.1】构建银行数据格式
    bank_data = create_data_format(bank_username=username, bank_id=str(login_user_dict['bank_id']),
                                   bank_pwd=str(bank_pwd), bank_balance=str(login_user_dict['bank_balance']))
    save_data(path=f'{username}_bank.txt', data=bank_data)
    # 【6.2】构建日志数据格式
    log_data = create_data_format(tag='log', username=username,
                                  msg=f'取款 {out_balance} , 当前余额为{login_user_dict["bank_balance"]}')
    save_data(path=f'{username}_log.txt', data=log_data)
    # 【6.3】构建流水数据格式
    flow_data = create_data_format(tag='flow', username=username,
                                   msg=f'取款金额为 {out_balance} , 余额为 {login_user_dict["bank_balance"]}')
    save_data(path=f'{username}_flow.txt', data=flow_data)
    return True, log_data


# 获取指定用户的银行信息
def get_bank_data(username):
    flag, bank_data_dict = read_data(tag='bank', path=f"{username}_bank.txt")
    if not flag:
        return flag, bank_data_dict
    bank_data = bank_data_dict.get(username)
    return True, bank_data


# 转账功能
@login_auth
@check_init_bank
def transfer_money():
    from_username = login_user_dict['username']
    # 【1】获取当前需要转账的金额
    out_balance = input("请输入转账金额 :>>>> ").strip()
    # 【2】判断当前的取款金额
    # 【2.1】必须是数字
    if not out_balance.isdigit():
        return False, '非法的余额!'
    # 【2.2】转为数字类型
    out_balance = int(out_balance)
    # 判断当前取款金额必须大于 0
    if out_balance < 0:
        return False, f'当前取款金额必须大于基础金额0!'
    # 【2.3】判断当前取款金额必须小于当前余额
    # print(login_user_dict)
    if out_balance > login_user_dict['bank_balance']:
        return False, f'当前余额不足!'
    # 【3】获取对方用户账户和银行卡号
    to_username = input("请输入对方用户名 :>>>> ").strip()
    # 判断对方用户是否存在
    # 【4】获取对方银行账号
    to_bank_id = input("请输入对方银行卡号 :>>>> ").strip()
    if not to_bank_id.isdigit() or len(to_bank_id) != 6:
        return False, f'银行账号必须为数字,且必须为6位!'
    to_bank_id = int(to_bank_id)
    # 【5】获取对方用户的银行信息
    flag, to_bank_data = get_bank_data(username=to_username)
    if not flag:
        return flag, to_bank_data
    # 【6】判断用户信息是否正确
    if to_bank_id != to_bank_data['bank_id']:
        return False, f'当前转账账号与目标账号不一致'
    # 【7】获取银行密码:必须是数字,并且必须是三位
    bank_pwd = input("请输入银行密码 :>>>> ").strip()
    if not bank_pwd.isdigit() or len(bank_pwd) != 3:
        return False, f'银行密码必须为数字,且必须为3位!'
    bank_pwd = int(bank_pwd)
    # 【8】校验当前取款密码
    if bank_pwd != login_user_dict['bank_pwd']:
        return False, f'当前取款密码错误!'
    # 【9】转账开始
    # 【9.1】自己的钱减少
    login_user_dict['bank_balance'] -= out_balance
    # 【9.2】对方的钱增加
    to_bank_data['bank_balance'] += out_balance
    # 【10】创建银行数据
    # 【10.1】自己的银行数据
    my_user_bank_data = create_data_format(tag='bank', username=from_username,
                                           bank_id=str(login_user_dict['bank_id']),
                                           bank_pwd=str(login_user_dict['bank_pwd']),
                                           bank_balance=str(login_user_dict['bank_balance']))
    save_data(path=f'{from_username}_bank.txt', data=my_user_bank_data)
    # 【10.2】对方的银行数据
    to_user_bank_data = create_data_format(tag='bank', username=to_username,
                                           bank_id=str(to_bank_data['bank_id']),
                                           bank_pwd=str(to_bank_data['bank_pwd']),
                                           bank_balance=str(to_bank_data['bank_balance'])
                                           )
    save_data(path=f'{to_username}_bank.txt', data=to_user_bank_data)
    # 【11】记录日志和流水
    # 【11.1】自己的日志
    from_user_log = create_data_format(tag='log', username=from_username,
                                       msg=f'向 {to_username} 转账 {out_balance} 成功')
    save_data(path=f'{from_username}_log.txt', data=from_user_log)
    # 【11.2】对方的日志
    to_user_log = create_data_format(tag='log', username=to_username,
                                     msg=f'收到 {from_username} 转账 {out_balance} 成功')
    save_data(path=f'{to_username}_log.txt', data=to_user_log)
    # 【12.1】自己的流水
    from_user_flow = create_data_format(tag='flow', username=from_username,
                                        msg=f'向 {to_username} 转账 {out_balance} 成功 余额为 {login_user_dict["bank_balance"]}')
    save_data(path=f'{from_username}_flow.txt', data=from_user_flow)
    # 【12.2】对方的流水
    to_user_flow = create_data_format(tag='flow', username=to_username,
                                      msg=f'收到 {from_username} 转账 {out_balance}成功 余额为 {to_bank_data["bank_balance"]}')
    save_data(path=f'{to_username}_flow.txt', data=to_user_flow)
    return True, from_user_log


# 充值金额
@login_auth
@check_init_bank
def recharge_money():
    username = login_user_dict['username']
    # 【1】获取当前需要转账的金额
    balance = input("请输入充值金额 :>>>> ").strip()
    # 【2】判断当前的取款金额
    # 【2.1】必须是数字
    if not balance.isdigit():
        return False, '非法的余额!'
    # 【2.2】转为数字类型
    balance = int(balance)
    # 判断当前取款金额必须大于 0
    if balance < 0:
        return False, f'当前存款金额必须大于基础金额0!'
    # 【3】获取当前银行密码
    bank_pwd = input("请输入银行密码 :>>>> ").strip()
    if not bank_pwd.isdigit() or len(bank_pwd) != 3:
        return False, f'银行密码必须为数字,且必须为3位!'
    bank_pwd = int(bank_pwd)
    # 【4】校验当前存款密码
    if bank_pwd != login_user_dict['bank_pwd']:
        return False, f'当前银行密码错误!'
    # 【5】校验通过,开始取钱
    login_user_dict['bank_balance'] += balance
    # 【6】构建银行信息
    bank_data = create_data_format(tag='bank', username=username,
                                   bank_id=str(login_user_dict['bank_id']),
                                   bank_pwd=str(login_user_dict['bank_pwd']),
                                   bank_balance=str(login_user_dict['bank_balance']))
    save_data(path=f'{username}_bank.txt', data=bank_data)
    # 【7】构建日志信息
    user_log = create_data_format(tag='log', username=username,
                                  msg=f'充值成功 当前余额 {login_user_dict["bank_balance"]}')
    save_data(path=f'{username}_log.txt', data=user_log)
    # 【8】构建流水信息
    user_flow = create_data_format(tag='flow', username=username,
                                   msg=f'充值成功 当前余额 {login_user_dict["bank_balance"]}')
    save_data(path=f'{username}_flow.txt', data=user_flow)
    return True, user_log


# 打印流水
@login_auth
@check_init_bank
def check_flow():
    flag, data_list = read_data(tag='flow', path=f'{login_user_dict["username"]}_flow.txt')
    if not flag:
        return flag, data_list
    print(f'''打印当前用户 {login_user_dict["username"]} 流水开始''')
    for data in data_list:
        print(data)
    return True, f'''打印当前用户 {login_user_dict["username"]} 流水结束'''


# 打印银行信息
@login_auth
@check_init_bank
def check_bank_info():
    # 【1】获取到当前登录用户
    username = login_user_dict['username']
    # 【2】获取历史用户注册信息
    flag, user_bank_dict = read_data(tag='bank', path=f'{username}_bank.txt')
    # 【3】根据当前用户名从历史用户信息中将当前用户信息筛选出来
    user_bank_data = user_bank_dict.get(username)
    # 打印银行信息如下
    msg = f'''
    当前用户 {username} 信息如下 
        用户 名 :>> {username}
        银行账号 :>> {user_bank_data["bank_id"]}
        银行余额 :>> {user_bank_data["bank_balance"]}
    '''
    return True, msg


# 打印日志
@login_auth
@check_init_bank
def check_log():
    flag, data_list = read_data(tag='log', path=f'{login_user_dict["username"]}_log.txt')
    if not flag:
        return flag, data_list
    print(f'''打印当前用户 {login_user_dict["username"]} 日志开始''')
    for data in data_list:
        print(data)
    return True, f'''打印当前用户 {login_user_dict["username"]} 日志结束'''


func_menu = '''
===================用户功能菜单=====================
                  1.注册
                  2.登陆
                  3.激活银行卡
                  4.取款
                  5.转账
                  6.充值余额
                  7.查看流水
                  8.查看银行信息(查看自己的卡号、余额、流水等信息)
                  9.查看个人日志
======================欢迎使用=======================
'''

func_dict = {
    1: register,
    2: login,
    3: active_bank_card,
    4: withdraw_money,
    5: transfer_money,
    6: recharge_money,
    7: check_flow,
    8: check_bank_info,
    9: check_log
}


def main():
    while True:
        print(func_menu)
        func_id = input("请输入功能ID :>>>> ").strip()
        if not func_id.isdigit():
            print(f"请输入合法的功能ID :>>>> {func_id}")
            continue
        func_id = int(func_id)
        if func_id not in func_dict:
            print(f"未找到对应的功能ID :>>>> {func_id}")
            continue
        func = func_dict.get(func_id)
        flag, msg = func()
        if flag:
            print(msg)
        else:
            print(msg)
            continue


main()