27-ATM+购物车程序

1、需求

本章作业:

模拟实现一个ATM + 购物商城程序

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

示例代码 https://github.com/triaquae/py3_training/tree/master/atm

简易流程图:https://www.processon.com/view/link/589eb841e4b0999184934329

2、流程图

 

 

 

3.评语

 

 

 

 4.我的代码

# 软件组织结构
atm
│
├─bin       # 执行文件目录
│  └─__init__
│  └─__start__  # 执行文件
│
├─conf      # 配置文件目录
│  └─__init__
│  └─__settings__
│
├─core      # 核心代码
│  └─__init__
│  └─account.py     # 账户管理
│  └─atm_logics.py  # atm逻辑分发
│  └─atm_manage.py  # atm主文件
│  └─authentication.py  # 用户认证
│  └─db_handler.py      # 数据库操作
│  └─log.py     # 记录日志
│  └─main.py        # 入口文件
│  └─shopping.py    # 购物商城主文件
│
├─db        # 数据库目录
│   └─account   # 用户账户文件目录
│       └─alex.json
│       └─cat.json
│       └─jack.json
│       └─tom.json
│
├─logs      # 各种日志文件目录
│  └─account.log    # 账户管理log
│  └─atm.log        # atm操作log
│  └─login.log      # 登录操作log
│  └─shop.log       # 购物商城log
│
├─README
│
├─requirements.txt  # 依赖包
│
└─setup.py

 

 

# 文件如何调用的?
start.py---> main.py--->
    # 管理员
       ----> account.py
    # 普通用户
       # ATM
            ---> atm_manage.py---> atm_logics.py
       # 购物商城
            ---> shopping.py

 

 

 

# -*- coding:utf-8 -*-
import os
import sys

# 1.添加atm主目录到sys.path
# file_path = os.path.abspath(__file__)  # 文件绝对路径
# BASE_DIR1 = os.path.dirname(file_path)
# BASE_DIR2 = os.path.dirname(BASE_DIR1)  # atm主目录路径
# sys.path.append(BASE_DIR2)
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))


# 2.导入core/main 主程序
from core import main
if __name__ == '__main__':
    main.run()
start.py
# -*- coding:utf-8 -*-
import os
import logging

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
DB_PATH = '%s\\db\\account\\' % BASE_DIR

LOG_LEVEL = logging.INFO
LOG_PATH = '%s\\logs\\' % BASE_DIR
file_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

TRADE_TYPE = {
    'withdraw': {'action': 'subtract', 'interest': 0.05},
    'transfer': {'action': 'subtract', 'interest': 0},
    'pay': {'action': 'subtract', 'interest': 0},
    'transferred': {'action': 'plus', 'interest': 0},
    'repay': {'action': 'plus', 'interest': 0},
}

ADMIN_USER = 'admin'
ADMIN_PASSWORD = 'admin'
settings
# -*- coding:utf-8 -*-
import os

from conf import settings
from .db_handler import load_account_data
from .db_handler import save_account
from .db_handler import save_db

db_dir = settings.DB_PATH
account_list = os.listdir(db_dir)


def account_msg(*args):
    """查看所有账户,信用额度"""
    print("-----------\n账户  信用额度    是否冻结 ")
    for i in account_list:
        account = i.split('.json')[0]
        ret = load_account_data(account)   # db操作文件,取数据
        if ret['status'] == 0:
            if ret['data']['status'] == 0:
                cold_status = '未冻结'
            else:
                cold_status = '冻结'
            print("%s   %s      %s" % (ret['data']['id'], ret['data']['credit'], cold_status ))


def account_add(account, account_logger):
    """添加新用户"""
    account_name = input('请输入要添加的用户名:').strip()
    if account_name:
        ret = load_account_data(account_name)
        if ret['status'] == 0:
            print('该账户已经存在')
        else:
            save_account(account_name)
            print('添加成功,请重新登录查看')
            account_logger.info('account:%s be added by %s'%(account_name, account))


def account_cold(account, account_logger):
    """冻结用户"""
    account_name = input('请输入要冻结的用户名:')
    ret = load_account_data(account_name)
    if ret['status'] == 0:
        ret['data']['status'] = 1
        save_db(ret)
        print('该账户被冻结')
        account_logger.info('account:%s be frozen by %s' % (account_name, account))
    else:
        print('该账户不存在')


def account_manage(account, account_logger, *args):
    """账户管理menu"""
    fun_dict = {
        '1': account_msg,
        '2': account_add,
        '3': account_cold,
    }
    while True:
        msg = '''
        ----account管理 ---
        1、查看所有账户及额度
        2、添加账户
        3、冻结账户
        4、退出
        '''
        print(msg)

        choice = input(">>>").strip()
        if choice in fun_dict:
            fun_dict[choice](account, account_logger)
        elif choice == '4':
            exit()
        else:
            print('请重新输入')
account.py
# -*- coding:utf-8 -*-

from conf import settings
from .db_handler import save_db
from .db_handler import load_account_data


def trade(amount, trade_type, account_data, logger, *args):
    """交易中心,主要修改 余额"""

    if trade_type in settings.TRADE_TYPE:
        interest = amount * settings.TRADE_TYPE[trade_type]['interest']  # 利息
        old_balance = account_data['data']['balance']

        if settings.TRADE_TYPE[trade_type]['action'] == 'plus':
            new_balance = old_balance + interest + amount
        elif settings.TRADE_TYPE[trade_type]['action'] == 'subtract':
            new_balance = old_balance - interest - amount
            if new_balance < 0:
                balance = old_balance - interest
                print('你可以%s %s' % (trade_type, balance))
                return {'status': 1, 'error': '交易失败,余额不足'}

        # 写入文件
        account_data['data']['balance'] = new_balance
        data = save_db(account_data)

        # 加入日志
        logger.info('account:%s action:%s amount:%s interest:%s'
                    % (account_data['data']['id'], trade_type, amount, interest))
        return {'status': 0, 'msg': '交易成功', 'data': data}

    else:
        print('Trade type %s is not exist' % trade_type)
        return {'status': 1, 'error': '交易失败,交易类型不存在'}


def msg(account_data, *args, **kwargs):
    """打印账户信息"""
    print('账号信息'.center(40, '-'))
    account_dic = account_data['data']
    for k, v in enumerate(account_dic):
        if v not in 'password':
            print('%15s : %s' % (v, account_dic[v]))
    print('End'.center(40, '-'))


def withdraw(account_data, logger, *args, **kwargs):
    """提现操作"""
    withdraw_msg = '''
    --------余额信息-------
        额度:  %s
        余额:  %s
    ''' % (account_data['data']['credit'], account_data['data']['balance'])
    print(withdraw_msg)

    while True:
        withdraw_amount = input('请输入提现整数金额|b 退出:').strip()
        if len(withdraw_amount) > 0 and withdraw_amount.isdigit():
            withdraw_amount = float(withdraw_amount)
            if withdraw_amount < account_data['data']['balance']:
                trade_res = trade(withdraw_amount, 'withdraw', account_data, logger)
                if trade_res['status'] == 0:
                    account_data = trade_res['data']
                    print("New balance:%s" % account_data['data']['balance'])
                else:
                    print(trade_res['error'])

            else:
                print('余额不足,请重新输入')
        elif withdraw_amount == 'b':
            break


def transfer(account_data, logger, *args, **kwargs):
    """转账操作"""
    while True:
        trans_account = input('请输入转账账户|b 退出:').strip()
        if trans_account == 'b':
                break
        else:
            if trans_account not in account_data['data']['id']:
                find_res = load_account_data(trans_account)
                if find_res['status'] == 0:
                    while True:
                        trans_amount = input('转账金额|b 退出:').strip()
                        if len(trans_amount) > 0 and trans_amount.isdigit():
                            trans_account = float(trans_amount)

                            # 开始转账 account
                            trade_res = trade(trans_account, 'transfer', account_data, logger)
                            if trade_res['status'] == 0:
                                # 对方账户相当于增加钱
                                trade(trans_account, 'transferred', find_res, logger)
                                account_data = trade_res['data']
                                print("New balance:%s" % account_data['data']['balance'])
                            else:
                                print(trade_res['error'])
                        elif trans_amount == 'b':
                            break
                else:
                    print(find_res['error'])

            else:
                print('this account is yours')


def repay(account_data, logger, *args, **kwargs):
    """还款操作"""
    pay_msg = '''
    --------余额信息-------
        额度:  %s
        余额:  %s
    ''' % (account_data['data']['credit'], account_data['data']['balance'])
    print(pay_msg)

    while True:
        pay_amount = input('请输入还款金额|b 退出:').strip()
        if len(pay_amount) > 0 and pay_amount.isdigit():
            pay_amount = float(pay_amount)
            trade_res = trade(pay_amount, 'repay', account_data, logger)
            if trade_res['status'] == 0:
                account_data = trade_res['data']
                print("New balance:%s" % account_data['data']['balance'])
            else:
                print(trade_res['error'])

        elif pay_amount == 'b':
            break
atm_logics.py
# -*- coding:utf-8 -*-
from . import atm_logics


def main(account_data, logger, *args, **kwargs):
    """atm模块分发器,进行分发"""
    fun_dict = {
        '1': atm_logics.msg,
        '2': atm_logics.withdraw,
        '3': atm_logics.transfer,
        '4': atm_logics.repay,
    }

    while True:
        msg = '''
        ----ATM ---
        1、查看信用卡信息
        2、提现
        3、转账
        4、还款
        5、返回上一层
        '''
        print(msg)

        choice = input(">>>").strip()
        if choice in fun_dict:
            fun_dict[choice](account_data, logger, *args, **kwargs)
        elif choice == '5':
            break
        else:
            print('请重新输入')
atm_manage.py
# -*- coding:utf-8 -*-
from .db_handler import load_account_data


def auth(*args):
    """根据account,password认证用户"""
    account, password = args
    account_data = load_account_data(account)
    if account_data['status'] == 0:
        account_data = account_data['data']
        if password == account_data['password']:
            return account_data
        else:
            return None
    else:
        return None
authentication.py
# -*- coding:utf-8 -*-
import os
import json

from conf import settings


def load_account_data(account):
    """根据输入的account,查找用户文件"""
    account_file = settings.DB_PATH + "%s.json" % account  # account文件路径
    if os.path.isfile(account_file):
        with open(account_file, 'r', encoding='utf8') as f:
            data = json.load(f)
            return {'status': 0, 'data': data}
    else:
        return {'status': -1, 'error': 'account file is not exist'}


def save_db(account_data):
    """根据account_data,找到对应的json文件,写入文件中"""
    data = account_data['data']
    account_file = settings.DB_PATH + "%s.json" % data['id']   # account文件路径
    if os.path.isfile(account_file):
        new_file = account_file + '.new'
        with open(new_file, 'w', encoding='utf8') as f:
            json.dump(data, f)
        os.replace(new_file, account_file)
        return {'status': 0, 'data': data}
    else:
        return {'status': -1, 'error': 'account file is not exist'}


def save_account(account):
    """写入新用户数据,创建文件"""
    data = {"expire_date": "2021-01-01",
            "credit": 15000, "status": 0,
            "id": account, "password": "abc",
            "enroll_date": "2016-01-02",
            "balance": 15000,
            "pay_day": 22}
    account_file = settings.DB_PATH + "%s.json" % account
    print(account_file)
    with open(account_file, 'w', encoding='utf8') as f:
        json.dump(data, f)
        f.flush()    # 对应新建的用户文件,如何实时刷入硬盘中
    return {'status': 0, 'data': data}
db_handle.py
# -*- coding:utf-8 -*-
import logging

from conf import settings


def log_handle(log_type):
    logger = logging.getLogger(log_type)
    logger.setLevel(settings.LOG_LEVEL)

    log_file_name = settings.LOG_PATH + log_type + ".log"
    fh = logging.FileHandler(log_file_name)
    fh.setLevel(settings.LOG_LEVEL)

    logger.addHandler(fh)
    fh.setFormatter(settings.file_formatter)
    return logger
log.py
# -*- coding:utf-8 -*-

from .authentication import auth
from .log import log_handle
from .shopping import main as shop_main
from .atm_manage import main as atm_main
from .account import account_manage
from conf import settings

atm_logger = log_handle('atm')
login_logger = log_handle('login')
shop_logger = log_handle('shop')
account_logger = log_handle('account')


def login_type(auth_type):
    def login(fun):
        if auth_type == 'user_auth':
            def inner():
                user_obj = {
                    'is_authed': False,
                    'data': None
                }
                count = 0
                while True:
                    account = input('请输入账号:').strip()
                    password = input('请输入密码:').strip()

                    # 普通用户
                    auth_data = auth(account, password)  # 认证
                    if auth_data:
                        if auth_data['status'] == 0:
                            user_obj['is_authed'] = True
                            user_obj['data'] = auth_data

                            # 登录成功,加入日志
                            login_logger.info('user %s login ' % account)
                            print('welcome to ATM & SHOP')
                            fun(user_obj, atm_logger, shop_logger)
                        else:
                            print('该用户已经被冻结')
                            login_logger.info('cold_user %s login ' % account)
                    else:
                        login_logger.info('user %s try wrong to log ' % account)
                        print('error account or password')

                    count += 1
                    if count == 3:
                        msg = 'user %s is try more times' % account
                        print(msg)
                        # 登录不成功,加入日志
                        login_logger.info('user %s try wrong  to log reached 3 times' % account)
                        break
            return inner

        elif auth_type == 'admin_auth':
            def inner():
                admin_user = settings.ADMIN_USER
                admin_pwd = settings.ADMIN_PASSWORD
                count = 0
                while True:
                    account = input('请输入账号:').strip()
                    password = input('请输入密码:').strip()
                    if account == admin_user and password == admin_pwd:
                        # 登录成功,加入日志
                        login_logger.info('user %s login ' % account)
                        print('welcome to account manage')
                        fun(account, account_logger)
                    else:
                        login_logger.info('user %s try wrong to log ' % account)
                        print('error account or password')

                    count += 1
                    if count == 3:
                        msg = 'user %s is try more times' % account
                        print(msg)
                        # 登录不成功,加入日志
                        login_logger.info('user %s try wrong  to log reached 3 times' % account)
                        break
            return inner
    return login


@login_type('user_auth')
def identity(user_obj, atm_logger, shop_logger):
    """用户认证成功"""
    atm_mall(user_obj, atm_logger, shop_logger)


@login_type('admin_auth')
def admin(account, account_logger):
    """管理员认证成功"""
    account_manage(account, account_logger)


def run():
    fun_dict = {
        '1': admin,
        '2': identity,
    }
    while True:
        msg = '''
        ---------
        1.管理员
        2.普通用户
        3.退出
        '''
        print(msg)

        choice = input(">>>").strip()

        if choice in fun_dict:
            fun_dict[choice]()
        elif choice == '3':
            exit()
        else:
            print('请重新输入')


def atm_mall(account_data, logger, logger_shop, *args, **kwargs):
    """atm——mall程序入口"""
    fun_dict = {
        '1': shop_main,
        '2': atm_main,
    }
    while True:
        msg = '''
        ----ATM & SHOP---
        1、购物商城
        2、ATM
        3、退出
        '''
        print(msg)

        choice = input(">>>").strip()

        if choice in fun_dict:
            fun_dict[choice](account_data, logger, logger_shop)
        elif choice == '3':
            exit()
        else:
            print('请重新输入')
main.py
# -*- coding:utf-8 -*-
from .atm_logics import *


def main(account_data, logger, logger_shop, *args):
    goods = [
        {"name": "电脑", "price": 1999},
        {"name": "鼠标", "price": 10},
        {"name": "游艇", "price": 20},
        {"name": "美女", "price": 998}
    ]
    shopping_cart = []
    cost = 0

    # 2.商品menu循环
    while True:
        print("----------商品列表 --------")
        for index, item in enumerate(goods):
            print("%s   %s  %s" % (index, item['name'], item['price']))
        choice = input("\033[1;34m输入你要买的商品编号|返回上一层q:\033[0m").strip()
        if choice.isdigit():
            choice = int(choice)
            if choice < len(goods):
                shopping_cart.append([goods[choice]['name'], goods[choice]['price']])
                print('\033[1;32m>你购买了%s\033[0m' % goods[choice]['name'])
                cost += goods[choice]['price']

            else:
                print('\033[1;31m你输入的商品不存在\033[0m')

        elif choice == 'q':
            if len(shopping_cart) > 0:
                print("\033[1;32m------你的购物车---------")
                for index, item in enumerate(shopping_cart):
                    print(index, item[0], item[-1])
                print("------------------------")
                print("你的账单:%s\033[0m" % cost)

                while True:
                    choice = input('Do you want to pay? y|n :').strip()
                    if choice.lower() == 'y':
                        print('正在调用付款接口...')
                        pay_res = trade(cost, 'pay', account_data, logger)
                        if pay_res['status'] == 0:
                            print(pay_res['msg'])

                            # pay成功,写入日志
                            for i in shopping_cart:
                                logger_shop.info('account:%s buy:%s cost:%s' % (account_data['data']['id'], i, cost))
                        else:
                            print(pay_res['error'])
                            print('请重新购物')
                        break

                    elif choice.lower() == 'n':
                        print('请重新购物')
                        break
                    else:
                        print('请重新输入')

                # 支付成功,购物车清零
                shopping_cart = []
                cost = 0

            else:
                break

        else:
            print('\033[1;31;47m你输入的有误,请重新输入\033[0m')
shopping.py

 

 

{"id": "alex", "pay_day": 22, "expire_date": "2021-01-01", "credit": 15000, "status": 0, "enroll_date": "2016-01-02", "balance": 11768.0, "password": "abc"}
alex.json
{"status": 0, "credit": 15000, "password": "abc", "pay_day": 22, "balance": 346.8999999999878, "id": "jack", "expire_date": "2021-01-01", "enroll_date": "2016-01-02"}
jack.json
2018-03-01 16:59:14,767 - account - INFO - account:alex1 be added by admin
2018-03-01 16:59:21,329 - account - INFO - account:alex be frozen by admin
2018-03-01 16:59:28,051 - account - INFO - account:jack be frozen by admin
2018-03-01 21:45:09,287 - account - INFO - account: be added by admin
2018-03-01 21:49:29,694 - account - INFO - account:alex be frozen by admin
2018-03-01 21:49:47,597 - account - INFO - account:cat be added by admin
2018-03-02 10:26:48,621 - account - INFO - account:dog be added by admin
2018-03-02 10:34:23,346 - account - INFO - account:vvvv be added by admin
2018-03-02 10:49:29,096 - account - INFO - account:llll be added by admin
2018-03-02 10:53:21,614 - account - INFO - account:shengge be added by admin
2018-03-02 10:53:54,387 - account - INFO - account:tom1 be added by admin
acconunt.log
2018-02-27 16:16:54,125 - atm - INFO - account:jack action:withdraw amount:100.0 interest:5.0
2018-02-27 16:17:10,761 - atm - INFO - account:jack action:pay amount:100.0 interest:0.0
2018-02-27 16:17:23,475 - atm - INFO - account:jack action:pay amount:100.0 interest:0.0
2018-02-27 16:17:31,526 - atm - INFO - account:jack action:trans amount:100.0 interest:0.0
2018-02-27 16:18:32,499 - atm - INFO - account:alex action:trans amount:100.0 interest:0
atm.log
2018-02-26 17:25:17,207 - login - INFO - user jack logg in 
2018-02-26 17:31:35,451 - login - INFO - user jack logg in 
2018-02-27 00:03:18,650 - login - INFO - user ale try wrong to log 
2018-02-27 00:03:21,692 - login - INFO - user alex logg in 
2018-02-27 00:05:49,870 - login - INFO - user jack logg in 
2018-02-27 00:07:57,278 - login - INFO - user jack try wrong to log 
2018-02-27 00:07:59,868 - login - INFO - user jack logg in 
2018-02-27 00:15:03,256 - login - INFO - user jack logg in 
login.log
2018-02-28 12:09:44,197 - shop - INFO - account:jack buy:['鼠标', 10] cost:30
2018-02-28 12:09:44,199 - shop - INFO - account:jack buy:['游艇', 20] cost:30
2018-02-28 12:10:54,623 - shop - INFO - account:tom buy:['游艇', 20] cost:50
2018-02-28 12:10:54,623 - shop - INFO - account:tom buy:['鼠标', 10] cost:50
2018-02-28 12:10:54,623 - shop - INFO - account:tom buy:['游艇', 20] cost:50
2018-03-01 21:48:25,825 - shop - INFO - account:alex buy:['电脑', 1999] cost:3027
2018-03-01 21:48:25,825 - shop - INFO - account:alex buy:['鼠标', 10] cost:3027
2018-03-01 21:48:25,825 - shop - INFO - account:alex buy:['游艇', 20] cost:3027
2018-03-01 21:48:25,825 - shop - INFO - account:alex buy:['美女', 998] cost:3027
shop.log

 

 

作者:Liangshuo
版本:示例版本 v1.0


本章作业:
模拟实现一个ATM + 购物商城程序
额度 15000或自定义
实现购物商城,买东西加入 购物车,调用信用卡接口结账
可以提现,手续费5%
支持多账户登录
支持账户间转账
记录每月日常消费流水
提供还款接口
ATM记录操作日志
提供管理接口,包括添加账户、用户额度,冻结账户等。。。
用户认证用装饰器
示例代码 https://github.com/triaquae/py3_training/tree/master/atm
简易流程图:https://www.processon.com/view/link/589eb841e4b0999184934329


# 软件组织结构
atm
│
├─bin       # 执行文件目录
│  └─__init__
│  └─__start__  # 执行文件
│
├─conf      # 配置文件目录
│  └─__init__
│  └─__settings__
│
├─core      # 核心代码
│  └─__init__
│  └─account.py     # 账户管理
│  └─atm_logics.py  # atm逻辑分发
│  └─atm_manage.py  # atm主文件
│  └─authentication.py  # 用户认证
│  └─db_handler.py      # 数据库操作
│  └─log.py     # 记录日志
│  └─main.py        # 入口文件
│  └─shopping.py    # 购物商城主文件
│
├─db        # 数据库目录
│   └─account   # 用户账户文件目录
│       └─alex.json
│       └─cat.json
│       └─jack.json
│       └─tom.json
│
├─logs      # 各种日志文件目录
│  └─account.log    # 账户管理log
│  └─atm.log        # atm操作log
│  └─login.log      # 登录操作log
│  └─shop.log       # 购物商城log
│
├─README
│
├─requirements.txt  # 依赖包
│
└─setup.py


# 文件如何调用的?
start.py---> main.py--->
    # 管理员
       ----> account.py
    # 普通用户
       # ATM
            ---> atm_manage.py---> atm_logics.py
       # 购物商城
            ---> shopping.py
README

 

chardet==3.0.4
ipython==2.3.1
pyreadline==2.1
requirements.txt

 

 

5.演示效果

(1)admin管理账户

 

  (2)普通用户购物商城

 

  

  (3)普通用户ATM

 

posted @ 2018-02-24 10:56  venicid  阅读(361)  评论(4编辑  收藏  举报