Python架构及部分ATM项目

一、一个项目是如何从无到有

1、需求分析

1.拿到项目,会先在客户那里一起讨论需求,
商量项目的功能是否能实现,周期与价格,得到一个需求文档。

2.最后在公司内部需要开一次会议,最终得到一个开发文档,
交给不同岗位的程序员进行开发。
    - Python: 后端,爬虫
    
    - 不同的岗位:
        - UI界面设计:
            - 设计软件的布局,会分局软件的外观切成一张张图片。
        
        - 前端:
            - 拿到UI交给他的图片,然后去搭建网页面。
            - 设计一些页面中,哪些位置需要接收数据,需要进行数据交互。
        
        - 后端:
            - 直接核心的业务逻辑,调度数据库进行数据的增删查改。
        
        - 测试:
            - 会给代码进行全面测试,比如压力测试,界面测试(CF卡箱子)。
        
        - 运维:
            - 部署项目。

2、程序的架构设计

1、程序设计的好处

1)思路清晰
2)不会出现写一半代码时推翻重写
3)方便自己或以后的同事更好维护

2、三层架构设计的好处

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

3、三层架构

  1 用户视图层

   用户与用户交互的,可以接受用户的输入,打印接口返回的数据

  2 逻辑接口层

   接收用户视图层传过来的参数,构建逻辑判断调用的数据层加以处理,并返回一个结果给用户视图层

  3 数据处理层

   接收接口传递过来的参数,做数据的增删改查

三 分任务开发

四 测试

五 上线


二、项目的说明书

项目:ATM + 购物车

项目需求:

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

项目思路流程框架图:

"用户视图层" 展示给用户选择的功能:

1、注册功能
2、登录功能
3、查看余额
4、提现功能
5、还款功能
6、转账功能
7、查看流水
8、购物功能
9、查看购物车
10、管理员功能

开始创建目录:

conf
	--settings 存放配置信息
core
	--src 用户视图层
interface  -- 逻辑接口层
	--user_interface 用户相关接口(注册、登录)
	--bank_interface 银行相关接口(查看余额,提现、还款、转账)
	--shop_interface 购物车相关接口(购物功能、查看购物车)
db
	--user_data
		--deimos.json
		--aaa.json
	--db_handler 数据处理层,专门用于处理数据
lib
	--common
log
	--access.log
start.py  -- 程序的入口

用户视图层相关代码:

'''
第一层: 用户视图层
'''

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

global_name = None
# 1、注册功能
# 面条版
'''
# 面条版
def register():
    while True:
        username = input('请输入您的账号:>>').strip()
        password = input('请输入您的密码:>>').strip()
        re_password = input('请重复输入您的账号:>>').strip()
        # 小逻辑处理
        if password == re_password:
            import json
            import os
            from conf import settings
            user_path = os.path.join(
                settings.USER_DATA_PATH, f'{username}.json'
            )
            # 查看用户是否存在
            if os.path.exists(user_path):
                print('请重新输入')
                continue
            # 若用户存在重新输入
                with open(user_path,'r',encoding='utf-8') as rf:
                    data = json.load(rf)
                if data:
                    print('账号存在,请重新输入')
                    continue
            # 用户不存在,则保存用户数据
            # 阻止用户的数据字典信息
            user_dic = {
                'username': username,
                'password': password,
                'balance': 15000,
                'flow': [],  # 用于记录用户流水的列表
                'shop_car': {},  # 用户记录用户购物车
                'locked': False  # 用于记录账户是否被冻结
            }
            # 用户数据,tank.json 存不是目的,目的是为了取数据
            with open(user_path, 'w', encoding='utf-8')as f:
                # ensure_ascii=False 显示文件当中的中文数据,使得美观
                json.dump(user_dic, f,ensure_ascii=False)
'''



# 注册功能
def register():
    while True:
        user_name = input('请输入您的账号:>>').strip()
        user_pwd = input('请输入您的密码:>>').strip()
        re_user_pwd = input('请再次输入您的密码:>>').strip()
        if user_pwd == re_user_pwd:
            flag, msg = user_interface.register_interface(user_name, user_pwd)
        if flag:
            print(msg)
            break
        else:
            print(msg)


# 登录功能
def login():
    while True:
        user_name = input('请输入您的账号:>>').strip()
        user_pwd = input('请输入您的密码:>>').strip()
        flag, msg = user_interface.login_interface(user_name, user_pwd)
        if flag:
            print(msg)
            global global_name
            global_name = user_name
            break
        else:
            print(msg)


# 查看余额
@common.login_auth
def check_balance():
    balance = user_interface.check_balance_interface(global_name)
    print(f'用户{global_name}的账户余额为{balance}元')


# 提现功能
@common.login_auth
def withdraw():
    while True:
        input_money = input('请输入您要提现的金额:>>').strip()
        if not input_money.isdigit():
            print('格式不规范,请重新输入')
            continue
        flag, msg = bank_interface.withdraw_interface(global_name, input_money)
        if flag:
            print(msg)
            break
        else:
            print(msg)


# 还款功能
@common.login_auth
def repay():
    while True:
        input_money = input('请输入您要还的金额:>>').strip()
        if not input_money.isdigit():
            print('格式不规范,请重新输入')
            continue
        input_money = float(input_money)
        if input_money > 0:
            flag, msg = bank_interface.repay_interface(global_name, input_money)
            if flag:
                print(msg)
                break
        else:
            print('输入金额不能少于0')


# 转账功能
@common.login_auth
def transfer():
    while True:
        transfer_name = input('请输入转入的账号:>>').strip()
        money = input('请输入转账金额:>>').strip()
        if global_name == transfer_name:
            print('不能给自己转账!!')
            continue
        if not money.isdigit():
            print('格式错误,请重新输入正确金额!')
            continue
        money = float(money)
        if money > 0:
            flag, msg = bank_interface.transfer_interface(global_name, transfer_name, money)
            if flag:
                print(msg)
                break
            else:
                print(msg)
        else:
            print('格式错误,请重新输入正确金额!')


# 查看流水
@common.login_auth
def check_flow():
    flow = bank_interface.check_flow_interface(global_name)
    if flow:
        for line in flow:
            print(line)

    else:
        print('当前用户没有流水')


# 购物功能
@common.login_auth
def shopping():
    shop_list = [
        ['包子', 11],
        ['啤机', 12],
        ['鸡爪', 13],
        ['水杯', 14],
        ['酸奶', 16],
    ]
    shop_car = {}
    while True:
        print('========购物商城===========')
        for index,shop in enumerate(shop_list):
            shop_name,shop_price = shop
            print(f'商品编号为{index}\t',f'商品名称为{shop_name}\t',f'商品价格为{shop_price}\t')
        print('=======欢迎选购============')
        choice = input('请输入你要购买的商品,(是否结账输入n or y):>>').strip()
        if choice == 'n':
            if not shop_car:
                print('购物车是空的,不能添加')
                continue
            flag, msg = shop_interface.add_shop_car_interface(global_name, shop_car)
            if flag:
                print(msg)
                break
            else:
                print(msg)
        elif choice == 'y':
            if not shop_car:
                print('购物车是空的,不能支付')
                continue
            flag, msg = shop_interface.shopping_interface(global_name, shop_car)
            if flag:
                print(msg)
                break
            else:
                print(msg)
        if not choice.isdigit():
            print('请输入正确编号!')
            continue
        choice = int(choice)
        if choice not in range(len(shop_list)):
            print('请输入正确的编号!')
            continue
        shop_name,shop_price = shop_list[choice]
        if shop_name in shop_car:
            shop_car[shop_name][1]+=1
        else:
            shop_car[shop_name] = [shop_price,1]
        print('当前购物车',shop_car)


# 查看购物车
@common.login_auth
def check_shop_car():
    shop_car = shop_interface.check_shop_car_interface(global_name)
    print(shop_car)


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


# 创建函数字典
func_dict = {
    '0': exit,
    '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(
            """
            0、退出
            1、注册功能
            2、登录功能
            3、查看余额
            4、提现功能
            5、还款功能
            6、转账功能
            7、查看流水
            8、购物功能
            9、查看购物车
            10、管理员功能
            """
        )
        num = input('请输入功能编号:>>').strip()
        if num not in func_dict:
            print('请输入正确的编号!!')
            continue

        func_dict.get(num)()

逻辑接口层相关代码:

"""
银行相关接口
"""
from db import db_handler
from lib import common


bank_logger = common.get_logger(log_type='bank')


def withdraw_interface(username, money):
    user_dic = db_handler.select(username)
    balance = float(user_dic.get('balance'))

    money1 = float(money) * 1.05
    money2 = float(money) * 0.05
    if money1 <= balance:
        balance1 = balance - money1
        user_dic['balance'] = balance1
        flow = f'用户{username}提现成功,提现金额为{money}元,手续费为{round(money2, 2)}元'
        user_dic['flow'].append(flow)
        db_handler.save(user_dic)
        bank_logger.info(flow)
        return True, flow
    return False, '提现金额不足,请重新输入'


def repay_interface(username, money):
    user_dic = db_handler.select(username)
    user_dic['balance'] += float(money)
    flow = f'账户{username}还款成功,还款金额为{money}元'
    user_dic['flow'].append(flow)
    bank_logger.info(flow)
    db_handler.save(user_dic)
    return True, flow


def transfer_interface(global_name, transfer_name, money):
    login_dic = db_handler.select(global_name)
    transfer_dic = db_handler.select(transfer_name)
    if not transfer_dic:
        return False, f'目标用户不存在'
    if login_dic['balance'] >= money:
        login_dic['balance'] -= money
        transfer_dic['balance'] += money
        flow_login = f'用户{global_name}给用户{transfer_name}转账{money}元'
        flow_transfer = f'用户{transfer_name}收到用户{global_name}的转账{money}元'
        login_dic['flow'].append(flow_login)
        transfer_dic['flow'].append(flow_transfer)
        bank_logger.info(flow_login)
        bank_logger.info(flow_transfer)
        db_handler.save(login_dic)
        db_handler.save(transfer_dic)
        return True, flow_login
    else:
        return False, f'当前用户转账资金不足'


def check_flow_interface(name):
    login_dic = db_handler.select(name)
    return login_dic['flow']


def pay_interface(global_name,cost):
    user_dic = db_handler.select(global_name)
    if user_dic.get('balance') >=cost:
        user_dic['balance'] -= cost
        flow = f'用户{global_name}消费金额{cost}元'
        bank_logger.info(flow)
        user_dic['flow'].append(flow)
        db_handler.save(user_dic)
        return True
    else:
        return False



'''
第二层: 用户接口
'''
from db import db_handler
from lib import common

user_logger = common.get_logger(log_type='bank')


# 注册接口
def register_interface(user_name,user_pwd,balance = 15000):
    # 查看用户是否存在
    user_dic = db_handler.select(user_name)
    # 不存在则注册,并且做密码加密
    if user_dic:
        return False,f'账户{user_name}已存在'

    user_pwd = common.pwd_md5(user_pwd)
    # 用户字典信息
    user_dic = {
        'username':user_name,
        'password':user_pwd,
        'balance':balance,
        'flow':[],
        'shop_car':{},
        # 账户状态
        'locked':False
    }
    # 保存数据
    db_handler.save(user_dic)
    msg = f'{user_name}注册成功'
    user_logger.info(msg)
    return True,msg


# 登录接口
def login_interface(user_name,user_pwd):
    user_dic = db_handler.select(user_name)
    if user_dic:
        if user_dic.get('locked'):
            return False,f'账户{user_name}已经被锁定'
        user_pwd = common.pwd_md5(user_pwd)
        if user_pwd == user_dic.get('password'):
            msg = f'用户{user_name}登陆成功'
            user_logger.info(msg)
            return True,msg
        else:
            msg = f'用户{user_name}密码错误'
            user_logger.info(msg)
            return False,msg
    return False,f'用户{user_name}不存在,请重新输入'


# 查看账户余额
def check_balance_interface(username):
    user_dic = db_handler.select(username)
    return user_dic.get('balance')


'''
第三层: 购物车接口
'''
from db import db_handler
from lib import common


shop_logger = common.get_logger(log_type='shop')


def add_shop_car_interface(global_name, shopping_car):
    user_dic = db_handler.select(global_name)
    shop_car = user_dic.get('shop_car')
    for shop_name, price_number in shopping_car.items():
        number = price_number[1]
        if shop_name in shop_car:
            user_dic['shop_car'][shop_name][1] += number
        else:
            user_dic['shop_car'].update(
                {shop_name: price_number}
            )
    db_handler.save(user_dic)
    return True, '添加购物车成功'


def shopping_interface(global_name, shop_car):
    cost = 0
    for price_number in shop_car.values():
        price,number = price_number
        cost += float(price*number)
        from interface import bank_interface
        flag = bank_interface.pay_interface(global_name,cost)
        if flag:
            msg = f'用户{global_name}支付成功,支付金额为{cost}元'
            shop_logger.info(msg)
            return True,msg
        return False,'支付失败,余额不足'


def check_shop_car_interface(global_name):
    user_dic = db_handler.select(global_name)
    return user_dic['shop_car']

数据处理层相关代码:

'''
第三层: 数据处理层
用于专门处理数据的
'''
import os
from conf import settings
import json


def select(user_name):
    user_path = os.path.join(settings.USER_DATA_PATH,f'{user_name}.json')
    if os.path.exists(user_path):
        with open(user_path,'r',encoding='utf-8')as f:
            user_dic = json.load(f)
            return user_dic


def save(user_dic):
    username = user_dic.get('username')
    user_path = os.path.join(settings.USER_DATA_PATH,f'{username}.json')
    with open(user_path,'w',encoding='utf-8')as f:
        json.dump(user_dic,f,ensure_ascii=False)

管理员相关代码:

"""
admin.py
"""
from core import src
from interface import admin_interface

def add_user():
    src.register()


def changer_balance():
    while True:
        change_name = input('请输入需要修改额度的账户:>>').strip()
        money = input('请输入需要修改额度:>>').strip()
        if not money.isdigit():
            print('格式错误')
            continue
        flag,msg = admin_interface.changer_balance_interface(change_name,money)
        if flag:
            print(msg)
            break
        else:
            print(msg)


def lock_user():
    while True:
        lock_name = input('请输入需要冻结的账户:>>').strip()
        flag, msg = admin_interface.lock_user_interface(lock_name)
        if flag:
            print(msg)
            break
        else:
            print(msg)


admin_dic = {
    '1':add_user,
    '2':changer_balance,
    '3':lock_user,

}


def admin_run():
    while True:
        print("""
        1、添加账户
        2、修改额度
        3、冻结账户
        """)
        num = input('请输入功能编号:>>').strip()
        if num not in admin_dic:
            print('请输入正确的编号!!')
            continue

        admin_dic.get(num)()

"""
admin_interface.py
"""        
from db import db_handler
from lib import common

admin_logger = common.get_logger(log_type='admin')


def changer_balance_interface(change_name,money):
    user_dic = db_handler.select(change_name)
    if user_dic:
        user_dic['balance'] = float(money)
        db_handler.save(user_dic)
        msg = f'管理员修改用户{change_name}额度修改成功!'
        admin_logger.info(msg)
        return True,msg
    return False,'账户不存在'


def lock_user_interface(lock_name):
    user_dic = db_handler.select(lock_name)
    if user_dic:
        user_dic['locked'] = True
        db_handler.save(user_dic)
        msg = f'{lock_name}账户冻结成功'
        admin_logger.info(msg)
        return True,msg
    return False,'冻结用户不存在'

项目配置:

import os

BASE_PATH = os.path.dirname(os.path.dirname(__file__))

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)传递
        },
    },
}

公共方法:

import hashlib
from conf import settings
import logging.config


def pwd_md5(user_pwd):
    md5_obj = hashlib.md5()
    md5_obj.update(user_pwd.encode('utf-8'))
    salt = '不要想着破解密码了'
    md5_obj.update(salt.encode('utf-8'))
    return md5_obj.hexdigest()


# 登录认证装饰器
def login_auth(func):
    from core import src
    def wrapper(*args,**kwargs):
        if src.global_name:
            res = func(*args,**kwargs)
            return res
        else:
            print('您还没有登录,请登录后再使用该功能')
            src.login()
    return wrapper


def get_logger(log_type):
    logging.config.dictConfig(
        settings.LOGGING_DIC
    )
    logger = logging.getLogger(log_type)
    return logger


posted @ 2020-04-02 21:30  Lance_王  阅读(315)  评论(0编辑  收藏  举报