如何写ATM程序?

Python写的ATM程序

 

需求说明:

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

  

程序说明:

主菜单,进入主菜单显示如下:

【1】购物商城 【2】用户中心 【3】信用卡中心 【4】后台管理 【5】登录系统 【6】退出

购物商城:

显示各种商品和价格,选择对应的序号进行购买

用户中心:

【1】修改密码 【2】额度查询 【3】消费记录 【4】返回

信用卡中心:

【1】额度查询    【2】提现    【3】转账     【4】还款    【5】返回

后台管理,主要是admin用户进行管理:

【1】创建用户 【2】冻结信用卡 【3】解冻用户

【4】额度调整 【5】退出后台管理

登录系统:

未登录的用户需要进行登录

代码结构

#!/usr/bin/env python
#-*- coding:utf-8 -*-
# @Time    : 2017/10/19 22:18
# @Author  : lichuan
# @File    : creditcard.py

from config import template
import time
from datetime import datetime
from module import common
import pickle
from log import my_log_settings
import logging
from module.users import Users
import os,sys


#加在日志配置模块
my_log_settings.load_my_logging_cfg()
#访问日志logger,path=log/access.log
acess_logger=logging.getLogger(__name__)
#消费日志logger,path=log/shop.log
shop_logger=logging.getLogger('shop')

#用户认证函数
# @wraps
def auth(func):
    '''
    用户是否已经登录的认证装饰器
    :param func:
    :return:
    '''
    def warpper(*args,**kwargs):
        import pickle
        # userinfo=pickle.loads(open('.json','rb').read())\
        userinfos = load_user()
        if len(userinfos)!=0:
            func(*args,**kwargs)
        else:
            login()
            userinfos = load_user()
            if len(userinfos)!=0:
                func(*args,**kwargs)
    return warpper

@auth
def shop_center():
    '''
    购物商城界面选择
    :return:
    '''
    shop={'apple手机':7000,'魅族手机':2000,'小米手机':2500,'华为手机':4000,'小米笔记本':4000}
    shop_list=['apple手机','魅族手机','小米手机','华为手机','小米笔记本']
    salary=15000
    userinfo=load_user()
    # print(userinfo)
    buy_list={}
    salary=int(userinfo['salary'])
    shop_flag=True
    while shop_flag:
        print(template.shopping_index_menu)
        choice=input('请选择:').strip()
        if not choice.isdigit() or int(choice) not in range(1,7):
            print('输入错误,请重试!')
            continue
        if int(choice) == 6:
            Users[userinfo['name']]=userinfo
            dump_user(userinfo)
            print('退出购物商城,再见!')
            break
        else:
            key=shop_list[int(choice)-1]
            money=shop[key]
            if money > salary:
                print('剩余额度不够,请选择别的商品!')
                continue
            else:
                salary=salary-money
                username=userinfo['name']
                shop_logger.info('[%s]购买%s,花费%d元!' % (username,key,money))
                print('%s,价值%d元,已购买!' % (key,money))
                print('剩余额度:%d元' % salary)
                #更新信息到Users配置文件
                userinfo['salary']=salary
                if key in buy_list:
                    buy_list[key]+=1
                else:
                    buy_list[key] = 1
                userinfo['buy_list']=buy_list
                Users[userinfo['name']] = userinfo
                common.update_users(Users)

#从文件加载登录用户的信息
def load_user():
    '''
    从文件加载登录用户的信息
    :return: userinfo信息
    '''
    try:
        with open('.pkl', 'rb') as read_f:
            userinfo = {}
            userinfo = pickle.loads(read_f.read())
            # print(userinfo)
    except (FileNotFoundError,EOFError):
        pass
    return userinfo

#将登录用户信息重新写入.pkl
def dump_user(users):
    '''
    将users信息重新写入.pkl文件进行保存。
    :param users:users信息是个字典
    :return:
    '''
    with open('.pkl', 'w'):
        pass
    with open('.pkl', 'wb') as read_f:
        p = pickle.dumps(users)
        read_f.write(p)

#用户登录函数
def login():
    '''
    用户登录函数,对用户名密码进行校验,用户密码采用加密模块hashlib进行加盐加密
    :return:
    '''
    err_count=0
    login_flag=True
    while login_flag:
        username=input('please input your username: ').strip()
        password=input('please input your password: ').strip()
        if username in Users:
            if Users[username]['password'] == common.encrypt(password) and Users[username]['islocked'] == 0:
                userinfo=Users[username]
                err_count = 0
                with open('.pkl','wb') as write_f:
                    p=pickle.dumps(userinfo)
                    write_f.write(p)
                acess_logger.info(str(username)+' login success!')
                print(str(username)+' login success!')
                login_flag=False
            elif Users[username]['islocked'] != 0:
                print('user is locked ! cannot login!')
                err_count = 0
                login_flag=False
                break
            else:
                print('name or password is wrong!!!')
                acess_logger.info(str(username)+' login Falied ,password is wrong')
                err_count+=1
                #错误登录3次以上,锁定用户
                if err_count >= 3:
                    Users[username]['islocked']=1
                    acess_logger.info(str(username)+' user locked!')
                    print(str(username)+' user locked!')
                    common.update_users(Users)
                    break
        else:
            print('name or password is wrong!')
            # err_count+=1

@auth
def user_center(today,weekday):
    '''
    用户登录后进入的用户个人中心界面
    :param name:用户名称
    :param today:
    :param weekday:星期几
    :return:
    '''
    center_flag=True
    userinfo=load_user()
    name=userinfo['name']
    while center_flag:
        print(template.index_user_center.format(name,today,weekday))
        choice=input('please input your choice:').strip()
        if not choice.isdigit() or int(choice) not in range(1,5):
            print('input wrong,please try again!')
            continue
        if int(choice) == 4:
            print('用户中心和您再见!')
            center_flag=False
            break
        elif int(choice) == 1:
            common.modify_passwd(userinfo)
        elif int(choice) == 2:
            query_salary()
        elif int(choice) == 3:
            buylist=common.buy_list(userinfo['name'])
            for b in buylist:
                print(b,end='')

#额度查询函数,显示信用卡基本信息
def query_salary():
    status_all=['正常','被锁定']
    userinfo=load_user()
    salary=userinfo['salary']
    total_salary=userinfo['total_salary']
    cardno=userinfo['bindcard']
    name=userinfo['name']
    status=status_all[0] if userinfo['islocked'] ==0  else status_all[1]
    print(template.card_info.format(cardno,name,total_salary,salary,status))
    # print(template.card_info.format(str(cardno),name,str(total_salary),str(salary),status))

#转账函数
def forward_cash():
    userinfo = load_user()
    salary = userinfo['salary']
    u_card_no = userinfo['bindcard']
    card_list=[]
    print('您现在剩余的可用额度为:%d' %salary)
    card_no=input('请输入对方的卡号:').strip()
    money=input('请输入转账的金额:').strip()
    if not money.isdigit():
        print('金额输入有误!')
        return
    for k in Users:
        if Users[k]['bindcard'] != u_card_no:
            card_list.append(Users[k]['bindcard'])
    if card_no not in card_list:
        print('卡号有误')
        return
    if int(money) > salary:
        print('转账金额超出你的信用额度!')
        return
    #减去自己的额度
    salary=salary-int(money)
    userinfo['salary']=salary
    dump_user(userinfo)
    Users[userinfo['name']]['salary']=salary
    #增加别人的额度
    for k in Users:
        if card_no == Users[k]['bindcard']:
            Users[k]['salary']+=int(money)
    common.update_users(Users)
    print('[%s]转账%d元给%s,手续费%d元' % (userinfo['name'],int(money),card_no))
    shop_logger.info('[%s]转账%d元给%s' % (userinfo['name'],int(money),card_no))

#提现函数
def draw_cash():
    cash=input('请输入提现金额:').strip()
    if not cash.isdigit() or int(cash) < 0:
        print('金额输入错误!')
        return
    userinfo=load_user()
    salary=userinfo['salary']
    if int(cash) > salary:
        print('你的额度是%s,额度不够!' % salary)
        return
    else:
        salary = salary - int(cash)*1.05
        userinfo['salary']=salary
        dump_user(userinfo)
        Users[userinfo['name']]['salary'] = salary
        common.update_users(Users)
        query_salary()
        shop_logger.info('[%s]取现%d元,手续费%d元!' % (userinfo['name'],int(cash),int(cash)*0.05))
        print('[%s]取现%d元,手续费%d元!' % (userinfo['name'],int(cash),int(cash)*0.05))

#信用卡还款
def repay_salary():
    repay_money=input('请输入还款金额:').strip()
    if not repay_money.isdigit():
        print('金额有误!')
        return
    else:
        repay_money=int(repay_money)
    userinfo = load_user()
    userinfo['salary'] = userinfo['salary']+repay_money
    dump_user(userinfo)
    Users[userinfo['name']]=userinfo
    common.update_users(Users)
    query_salary()
    shop_logger.info('[%s]还款%d元' % (userinfo['name'], repay_money))
    print('[%s]还款%d元' % (userinfo['name'], repay_money))

#信用卡中心程序
@auth
def card_center():
    '''
    信用卡中心程序
    :return:
    '''
    func={
        '1': query_salary,
        '2': draw_cash,
        '3': forward_cash,
        '4': repay_salary,
    }
    card_flag=True
    while card_flag:
        #初始化打印信息
        userinfo=load_user()
        user_name=userinfo['name']
        card_no=userinfo['bindcard']
        print(template.index_card_center.format(user_name,card_no))
        choice=input('请选择:').strip()
        if not choice.isdigit() or int(choice) not in range(1,6):
            print('输入错误,请重试!')
            continue
        if int(choice) == 5:
            print('信用卡中心和您再见!')
            break
        else:
            func[choice]()


#后台管理程序
@auth
def manager():
    func={
        '1':common.create_card,
        '2':common.lock_card,
        '3':common.unlock_card,
        '4':common.modify_salary,
    }
    userinfo=load_user()
    if userinfo['name'] != 'admin':
        print('只有admin用户可以进入后台管理!')
        return
    manager_flag=True
    while manager_flag:
        print(template.index_admin.format(userinfo['name']))
        choice=input('请选择:').strip()
        if not choice.isdigit() or int(choice) not in range(1,6):
            print('输入错误!')
            continue
        if int(choice) == 5:
            print('后台管理和您再见!')
            manager_flag=False
            break
        else:
            func[choice]()

if __name__ == '__main__':
    Flag=True
    # Flag=False
    while Flag:
        userinfo = load_user()
        # print(userinfo)
        # print(userinfo['name'])
        username = ''
        # username = userinfo['name'] if len(userinfo) != 0 else ''
        today=time.strftime('%Y-%m-%d',time.localtime())
        weekday=common.numTo_characters(datetime.now().weekday())
        print(template.index_default_menu.format(username,today,weekday))
        choice = input("请选择:").strip()
        if not choice.isdigit():
            print("输入错误,请重新输入")
            continue
        if int(choice) not in range(1,7):
            print("输入错误,,请重新输入")
            continue
        if int(choice) == 1:
            shop_center()
        elif int(choice) == 2:
            user_center(today,weekday)
        elif int(choice) == 3:
            card_center()
        elif int(choice) == 4:
            manager()
        elif int(choice) == 5:
            login()
        elif int(choice) == 6:
            with open('.pkl','w',encoding='utf-8'):
                pass
            print("再见!")
            Flag=False

creditcard.py
#!/usr/bin/env python
#-*- coding:utf-8 -*-
# @Time    : 2017/10/20 15:28
# @Author  : lichuan
# @File    : template.py


"""
该模块用来定义系统的菜单模板,用的网上别人的模板。
"""
# 主程序中的主菜单

index_default_menu = '''
-------------------------------------------------------------------------
                             ATM 模拟程序

{0}                                        今天 {1}   星期{2}
-------------------------------------------------------------------------
【1】购物商城 【2】用户中心 【3】信用卡中心 【4】后台管理 【5】登录系统 【6】退出
'''

#购物商城界面
shopping_index_menu = '''
    ==================================================================================
    =                                                                                =
    =                                 信用卡购物商城                                   =
    =                                                                                =
    ==================================================================================

    【1】apple手机  7000元
    【2】魅族手机    2000元 
    【3】小米手机    2500元 
    【4】华为手机    4000元 
    【5】小米笔记本  4000元 
    【6】退出
    '''

index_card_center = '''
------------------------------------------------------------------------------
                               信用卡管理中心

用户:{0} 卡号:{1}
------------------------------------------------------------------------------
【1】额度查询    【2】提现    【3】转账     【4】还款    【5】返回
'''
# 显示信用卡基本信息模板
card_info = '''
-----------------------------------------------------------------------------------
                               信用卡基本信息

卡号:{0}   所有人:{1}  信用额度:{2}  剩余额度:{3} 状态:{4}
-----------------------------------------------------------------------------------
'''
index_user_center = '''
-------------------------------------------------------------------------
                                用户中心

当前用户:{0}                                   今天 {1}   星期{2}
-------------------------------------------------------------------------
【1】修改密码 【2】额度查询 【3】消费记录 【4】返回

'''
# 后台管理模板
index_admin = '''
------------------------------------------------------------------------------
                               后台管理

用户:{0}
------------------------------------------------------------------------------
【1】创建用户               【2】冻结信用卡         【3】解冻用户
【4】额度调整               【5】退出后台管理
'''



#'----------------------------------------------------------------------------------------------------------------------------------------------------------------------'

template.py
#!/usr/bin/env python
#-*- coding:utf-8 -*-
# @Time    : 2017/10/20 15:46
# @Author  : lichuan
# @File    : common.py

import hashlib
from module.users import Users
import os
import re
from log import my_log_settings
import logging

my_log_settings.load_my_logging_cfg()
acess_logger=logging.getLogger(__name__)

def numTo_characters(num):
    '''
    传入数字,转换成星期几中的几
    :param num:
    :return:汉字
    '''
    if num in range(0,7):
        week=('一','二','三','四','五','六','日')
        return week[num]

def encrypt(str):
    '''
    对传入字符串进行加盐加密
    :param str: 需要进行加密的字符串
    :return: 返回加密过的字符串
    '''
    encrpt=hashlib.md5()
    encrpt.update(bytes('admin1234nnnnnn',encoding='utf-8'))
    encrpt.update(bytes(str,encoding='utf-8'))
    return encrpt.hexdigest()

def update_users(Users):
    '''
    更新Users信息的函数
    :param Users: 用户信息,是个字典
    :return:
    '''
    import os
    user_path=os.path.dirname(os.path.abspath(__file__))+'\\users.py'
    with open(user_path,'w',encoding='utf-8') as write_f:
        Users_new='Users='+str(Users)
        write_f.write(Users_new)

#修改密码
def modify_passwd(userinfo):
    '''
    用于更新Users用户密码信息
    :param userinfo: 传入用户信息
    :param new_passwd: 新的密码信息
    :return:
    '''
    old_passwd=input('请输入现在密码:')
    new_passwd=input('请输入新密码:')
    pattern_new_passwd=input('请再次输入新密码:')
    old_passwd=encrypt(old_passwd)
    if new_passwd != pattern_new_passwd:
        print('两次输入密码不一致!')
        return
    if old_passwd != userinfo['password']:
        print('密码错误!')
        return
    encrypt_passwd=encrypt(new_passwd)
    userinfo['password']=encrypt_passwd
    Users[userinfo['name']]=userinfo
    update_users(Users)
    acess_logger.info('%s修改了用户密码!' % userinfo['name'])
    print('修改密码成功!')

#用户的购物消费信息
def buy_list(username):
    buy=[]
    shop_path=os.path.normpath(os.path.join(
        os.path.abspath(__file__),
        os.pardir,
        os.pardir,
        'log',
        'shop.log'
    ))
    with open(shop_path,'r',encoding='utf-8') as read_f:
        lines=read_f.readlines()
        r='.*\[%s\].*' %username
        patern = re.compile(r)
        for line in lines:
            if patern.match(line):
                buy.append(line)
        return buy

#创建信用卡用户
def create_card():
    flag=True
    while True:
        username=input('请输入name:').strip()
        if len(username) <= 0:
            print('输入错误!')
            flag = False
            break
        if username in Users:
            print('用户已存在!')
            continue
        mobile=input('请输入手机号:').strip()
        if len(mobile) <= 0:
            print('输入错误!')
            flag = False
            break
        for i in Users:
            if Users[i]['mobile'] == mobile:
                print('手机号已经存在!')
                flag=False
                break
        card_no = input('请输入卡号:').strip()
        if len(card_no) <= 0 or not card_no.isdigit():
            print('输入错误!')
            flag = False
            break
        for i in Users:
            if Users[i]['bindcard'] == card_no:
                print('卡号已经存在!')
                flag = False
                break
        passwd = input('请输入密码:').strip()
        encrypt_passwd=encrypt(passwd)
        userinfo={
         'isdel': 0,
         'name': username,
         'password': encrypt_passwd,
         'islocked': 0,
         'salary': 15000,
         'total_salary': 15000,
         'bindcard': card_no,
         'mobile': mobile,
         'buy_list': {}
         }
        Users[username]=userinfo
        update_users(Users)
        acess_logger.info('新创建用户%s' %  username)
        print('新创建用户%s已成功' %  username)
        flag=False

#冻结信用卡
def lock_card():
    name=input('请输入要冻结的用户名:').strip()
    if name == 'admin':
        print('不能冻结admin账号!')
        return
    if name in Users:
        Users[name]['islocked'] = 1
        update_users(Users)
        acess_logger.info('%s用户被冻结' %name)
        print('%s用户被成功冻结' %name)
    else:
        print('用户不存在!')


#解冻信用卡
def unlock_card():
    name = input('请输入要解冻的用户名:').strip()
    if name in Users:
        Users[name]['islocked'] = 0
        update_users(Users)
        acess_logger.info('%s用户被解冻' % name)
        print('%s用户被成功解冻' % name)
    else:
        print('用户不存在!')

#调整额度
def modify_salary():
    name = input('请输入要调整额度的用户名:').strip()
    total_salary=input('请输入新额度:').strip()
    if not total_salary.isdigit():
        print('额度错误!')
        return
    if name in Users:
        Users[name]['salary'] = int(total_salary) - (Users[name]['total_salary']-Users[name]['salary'])
        Users[name]['total_salary'] = int(total_salary)
        update_users(Users)
        acess_logger.info('%s用户额度调整为%s' % (name,total_salary))
        print('%s用户额度调整为%s' % (name,total_salary))
    else:
        print('用户不存在!')

if __name__ == '__main__':
    # modify_passwd({},'bbb')
    # print('it is common')
    r=buy_list('alex')
    for i in r:
        print(i,end='')

common.py

 

Users={'alex': {'isdel': 0, 'name': 'alex', 'password': 'bc5b9cb3e4ab483335edab3347f3c102', 'islocked': 0, 'salary': 52187.4, 'total_salary': 15000, 'bindcard': '1001012345', 'mobile': '13511111111', 'buy_list': {'apple手机': 1, '魅族手机': 1, '小米手机': 1, '华为手机': 1, '小米笔记本': 2}}, 'egon': {'isdel': 0, 'name': 'egon', 'password': 'bc5b9cb3e4ab483335edab3347f3c102', 'islocked': 0, 'salary': 16950, 'total_salary': 15000, 'bindcard': '1001012346', 'mobile': '13511111112', 'buy_list': {}}, 'admin': {'isdel': 0, 'name': 'admin', 'password': 'bc5b9cb3e4ab483335edab3347f3c102', 'islocked': 0, 'salary': 15000, 'total_salary': 15000, 'role': 'admin', 'bindcard': '1001010002', 'mobile': '15257157418'}, 'lit': {'isdel': 0, 'name': 'lit', 'password': 'bc5b9cb3e4ab483335edab3347f3c102', 'islocked': 0, 'salary': 50000, 'total_salary': 50000, 'bindcard': '1001012347', 'mobile': '13520381333', 'buy_list': {}}}

users.py
"""
logging配置
"""

import os
import logging.config

# 定义三种日志输出格式 开始

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'

shop_format= '[%(asctime)s]%(message)s'

# 定义日志输出格式 结束

logfile_dir = os.path.dirname(os.path.abspath(__file__))  # log文件的目录

logfile_name = 'access.log'  # log文件名

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

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

# log配置字典
LOGGING_DIC = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'standard': {
            'format': standard_format
        },
        'simple': {
            'format': simple_format
        },
        'id_simple': {
            'format': id_simple_format
        },
        'shop_format': {
            'format': shop_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乱码了
        },
        'boss': {
            'level': 'DEBUG',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件
            'formatter': 'standard',
            'filename': 'boss.log',  # 日志文件
            'maxBytes': 1024 * 1024 * 5,  # 日志大小 5M
            'backupCount': 5,
            'encoding': 'utf-8',  # 日志文件的编码,再也不用担心中文log乱码了
        },
        'shop': {
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件
            'formatter': 'shop_format',
            'filename': shop_path,  # 日志文件
            'encoding': 'utf-8',  # 日志文件的编码,再也不用担心中文log乱码了
        },
    },
    'loggers': {
        #logger1=logging.getLogger(__name__)拿到的logger配置
        '': {
            'handlers': ['default',],  # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
            'level': 'DEBUG',
            'propagate': True,  # 向上(更高level的logger)传递
        },
        #logger1=logging.getLogger('collect')拿到的logger配置
        'collect': {
            'handlers': ['boss',],  # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
            'level': 'DEBUG',
            'propagate': True,  # 向上(更高level的logger)传递
        },
        'shop': {
            'handlers': ['shop'],  # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
            'level': 'INFO',
            'propagate': True,  # 向上(更高level的logger)传递
        },
    },
}


def load_my_logging_cfg():
    logging.config.dictConfig(LOGGING_DIC)  # 导入上面定义的logging配置
    # logger = logging.getLogger(__name__)  # 生成一个log实例
    # logger = logging.getLogger('shopping')  # 生成一个log实例
    # logger.info('It works2!')  # 记录该文件的运行状态

if __name__ == '__main__':
    load_my_logging_cfg()

my_log_settings.py

 

posted @ 2018-09-17 00:07  蓝胖子之心  阅读(202)  评论(0编辑  收藏  举报