ATM+购物车

ATM+购物车

一,项目开发流程

1.需求分析
	产品经理与架构师,根据客户的需求,理出一套比较容易编写的流程
2.架构设计
	架构师根据具体的业务需求选择 具体的开发编程语言与项目框架,所需要的数据库(主库,从库)。与开发目录规范,项目功能划分。项目的报价。
3.分组开发
	将项目拆分成多个小项目交给不同开发部门下的多个程序员。
4. 项目测试
	测试人员对项目进行全方面的测试。
'自己一定要多测试几遍,不要出现语法错误,不然提桶跑路'
5. 交付上线
	打包给运维人员进行维护。

二,需求分析

# 项目大致需求
  - 额度15000或自定义
  - 支持多账户登录
  - 可以查看账户余额
  - 可以提现(可自定义手续费比例)
  - 提供还款接口
  - 支持账户间转账
  - 记录每月日常消费流水
  - 实现购物商城,买东西加入购物车,调用信用卡接口结账
  - 提供管理接口,包括添加账户、用户额度,冻结账户等
  - ATM记录操作日志
  - 用户认证功能
# 提炼项目功能
  1.用户注册
  2.登录功能
  3.查看余额
  4.余额提现
  5.账户充值
  6.金额转账
  7.查看流水
  8.添加购物车
  9.查看购物车
  10.结算购物车
  11.管理员功能
# 项目大致技术栈
  1.python基础核心编程
  2.函数(装饰器)
  3.常见内置模块(os,sys,json)

三,架构设计

很多程序都是三层架构。
我们将ATM分为三层架构
第一层 - 展示层
	只展示功能选项,用户交互
第二层 - 逻辑层
	所有的业务逻辑
第三层 - 数据层
	做数据的增删改查提供给逻辑层
"""  等我们学了前端之后
    展示层cmd操作 —— 可以换成页面
    逻辑层普通Python代码 —— 可以换成django框架
    数据层json文件 —— 可以换成MySQL数据库"""

四,搭建项目目录

bin 
start.py    # 存放启动脚本
conf     
settings.py  # 存放配置文件
lib
common      # 存放公共功能
log        # 存放项目日志
core    
src.py      # 展示层

interface    # 核心逻辑层
shop_interface # 购物车接口
bank_interface # 银行接口
user_interface # 用户接口

db 
db_handlerl.py # 数据处理层
readme      # 说明

搭建功能框架(src.py展示层)

is_login= {'username':''}  # 全局变量 保存登录信息

# 用户注册
def register():
    pass
# 用户登录
def login():
    pass

# 查看余额
def check_balance():
    pass
  
# 余额提现
def Withdraw():
    pass

# 账户充值
def pay():
    pass

# 转账
def tranfer():
    pass

# 查看流水
def check_flow():
    pass

# 添加购物车
def add_shop_car():
    pass


# 查看购物车
def check_shop_car():
    pass


# 结算
def pay_shop_car():
    pass

# 管理员
def admin():
    pass

# 功能字典
func_dict = {
    '1': register,
    '2': login,
    '3': look_balance,
    '4': Withdraw,
    '5': pay,
    '6': tranfer,
    '7': cheak_water,
    '8': add_shop_car,
    '9': view_shop_car,
    '10': clear_shop_car,
    '11': admin


}



while True:
    print("""
      1.用户注册
      2.登录功能
      3.查看余额
      4.余额提现
      5.账户充值
      6.金额转账
      7.查看流水
      8.添加购物车
      9.查看购物车
      10.结算购物车
      11.管理员功能
    """)

    choice = input('请输入功能编号>>>:').strip()
    if choice in func_dict:
        func_name = func_dict.get(choice)
        func_name()
    else:
        print('好好输!')
        
   

启动文件(start.py)


import os
import sys
from core import src

# 动态获取根目录 并添加进sys.path   兼容性强,别人拿去也能用
base_dir = os.path.dirname(os.path.dirname(__file__))
sys.path.append(base_dir)

# 简易使用脚本的方式
if __name__ == '__main__':
    src.run()

配置文件(settings.py)

import os
import json

# 4. 拼接db文件夹路径  , 先 获取根目录
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
DB_DIR = os.path.join(BASE_DIR, 'db')
# 5.判断是否不存在,不存在直接创建文件夹
if not os.path.exists(DB_DIR):
    os.mkdir(DB_DIR)
"""方便后期需要db目录使用直接调用名字即可"""


# 定义日志输出格式 开始
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'
# 自定义文件路径
"""自定义创建日志文件目录"""
LOG_DIR = os.path.join(BASE_DIR, 'log')
if not os.path.isdir(LOG_DIR):
    os.mkdir(LOG_DIR)
logfile_path = os.path.join(LOG_DIR, 'log.log')
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)传递
        },  # 当键不存在的情况下 (key设为空字符串)默认都会使用该k:v配置
        # '购物车记录': {
        #     'handlers': ['default','console'],  # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
        #     'level': 'WARNING',
        #     'propagate': True,  # 向上(更高level的logger)传递
        # },  # 当键不存在的情况下 (key设为空字符串)默认都会使用该k:v配置
    },
}

公共功能(common.py)

import hashlib
from core import src
from conf import settings
import logging
import logging.config


# 12. 定义加密函数
def get_hash(msg):
    md5 = hashlib.md5()
    md5.update(msg.encode('utf8'))
    hash_msg = md5.hexdigest()
    return hash_msg
"用于用户密码明文转密文"

# 定义验证登录装饰器
def login_auth(func_name):
    def inner(*args, **kwargs):
        if src.is_login.get('username'):
            res = func_name(*args, **kwargs)
            return res
        else:
            print('请先登录')
            src.login()

    return inner
"用于验证登录信息,登录后不用再次登录可直接访问所有功能"

# 定义转浮点型 函数
def float_num(target_money):
    try:
        target_money = float(target_money)
    except Exception as e:
        return False, '必须是数字'
    else:
        return True, target_money
"用于对用户转账提现充值的金额转换成 浮点型带一个小数点。兼容性更强"

# 定义日志函数
def get_log(msg):
    logging.config.dictConfig(settings.LOGGING_DIC)  # 加载日志字典
    logger1 = logging.getLogger(msg)  # 根据自己需求比如说, 登录记录
    return logger1
"用于给每个功能增加日志, 快捷方便"

数据处理层(db_hangler.py)

from conf import settings
import  os
import json


# 数据层

# 6. 保存文件的操作
def save(user_dict):
    # 获取用户名
    username = user_dict.get('username')
    # 拼接用户文件路径
    user_dir = os.path.join(settings.DB_DIR, f'{username}.json')
    # 打开文件 w模式不存在则直接新建
    with open(user_dir, 'w', encoding='utf8') as f:
        json.dump(user_dict, f, ensure_ascii=False)


# 7. 查询文件的操作
def select(username):
    # 拼接用户文件
    user_dir = os.path.join(settings.DB_DIR, f'{username}.json')
    # 判断用户名是否存在
    if os.path.exists(user_dir):
        # 存在则直接返回用户字典
        with open(user_dir, 'r', encoding='utf8') as f:
            return json.load(f)

项目功能详解

注册功能详解

展示层:
def register():
# 1.获取用户名与密码
username = input('请输入用户名').strip()
password = input('请输入密码').strip()
# 2.二次确认用户密码
password1 = input('请确认密码').strip()
# 3.判断两次密码是否一致
if not password1 == password:
   print('两次密码不一致')
   return
# 4.调用接口层-注册接口
flag, msg = user_interface.register_interface(username, password)   # 传参给接口层
# 5.获取返回值并打印
    print(msg)

注册接口层:
"缺什么模块导入什么模块"
def register_interface(username,password):   # 需要接收传参
# 12.调用公共功能 导入加密模块 
hash_pwd = common.get_hash(password)
# 6 构造用户字典
 user_dict = {
        'username': username,
        'password': hash_pwd,
        'balance': 15000,
        'shop_car': {},
        'is_lock': False,
        'water_flow':[]
    }
# 7 调用数据处理层  查询用户
user = db_handler.select(username)
# 8.判断用户是否存在 
if user:
    # 有值则返回给展示层
	return False, f'用户{username}已存在'    
# 9.无值则保存用户文件
db_handler.save(user_dict)
# 10.添加日志
logging1.info(f'{username}用户注册了')
# 11返回给展示层
return True, f'{username}注册成功'


数据处理层:
"缺什么模块导入什么模块"
# 保存文件的功能
def save(user_dict):
    # 获取用户名
    username = user_dict.get('username')
    # 拼接用户文件路径
    user_dir = os.path.join(settings.DB_DIR, f'{username}.json')
    # 打开文件 w模式不存在则直接新建
    with open(user_dir, 'w', encoding='utf8') as f:
        json.dump(user_dict, f, ensure_ascii=False)
   
#  查询文件的操作
def select(username):
    # 拼接用户文件
    user_dir = os.path.join(settings.DB_DIR, f'{username}.json')
    # 判断用户名是否存在
    if os.path.exists(user_dir):
        # 存在则直接返回用户字典
        with open(user_dir, 'r', encoding='utf8') as f:
            return json.load(f)
"返回字典给用户方便后续各种 获取用户各种资料的操作"

登录功能详解

展示层:
def login():
	# 1.获取用户名密码
   username = input('请输入用户名').strip()
   password = input('请输入密码').strip()
   # 2.调用登录接口
   flag, msg = user_interface.login_interface(username, password)
   # 6 根据逻辑层的返回的结果判断用户是否登录成功
   if flag:
      # 记录登录状态
   	  is_login['username'] = username
    # 打印结果
   print(msg)

逻辑层:
def login_interface(username,password):
	# 3.调用数据层判断用户是否已存在
   user_dict = db_handler.select(username)
	# 判断已存在的情况
   if not user_dict:
       # 返回结果给 展示层
       return False, '用户不存在'
   # 4.给密码转成密文与 用户数据内的密文密码做比对
   hash_pwd = common.get_hash(password)
   # 5.判断密码是否一致
   if user_dict.get('password') == hash_pwd:
       # 一致则 添加日志并返回给展示层
       logging1.info(f'{username}用户成功登录')
       return True, f'用户{username}登陆成功'
   # 不一致返回信息给 展示层
   else:
       return False, f'密码错误'

数据处理层:
3. 逻辑层调用了数据处理层 以用户名查询了 文件是否存在,存在则返回用户字典给逻辑层

查看余额功能

1.先用验证用户登录信息的装饰器
展示层:
@common.login_auth
def check_balance():
    # 2.因已登录,直接调用查询接口  需要传参用户名(全局变量)
    flag, msg = bank_interface.check_balance_interface(is_login.get('username'))
	# 打印返回数据
    print(msg)

逻辑层:
def check_balance_interface(username):
    # 3.调用数据处理层 查询功能 获取用户字典
     user_dict = db_handler.select(username)
    # 4.获取用户余额
     user_balance = user_dict.get('balance')
    # 6.添加流水功能与添加日志
    logging1.debug(f'{username}用户查询了余额')
    ctime = time.strftime('%Y-%m-%d %H:%M:%S')
    user_dict['water_flow'].append(f'{ctime}:{username}查询了余额{user_balance}元')
    # 7 调用数据层,保存功能 保存流水
    db_handler.save(user_dict)
    # 5 返回数据给展示层
    return True, f'尊敬的{username}, 您的余额{user_balance}元'

数据层:
 3.接收逻辑层的用户名查找并返回用户字典给逻辑层
 7.保存了用户  流水记录

提现功能详解

展示层:
@common.login_auth  # 验证登录装饰器
def withdraw():
    # 1.获取提现金额
    target_money = input('请输入需要提现的金额').strip()
    # 2.调用 提现接口
    flag, msg = bank_interface.withdraw_interface(is_login.get('username'), target_money)
    print(msg)

逻辑层:
def withdraw_interface(username, target_money):
    # 3.调用数据层,获取用户字典
    user_dict = db_handler.select(username)
    # 4. 判断输入金额是否是数字,调用了公共功能 转浮点型
    flag, msg = common.float_num(target_money)
    if not flag:
        return False, '必须是数字'
    # 5. 获取用户余额
     user_blance = user_dict.get('balance')
    # 6.定义提现手续费
     bank_charge = 0.038
    # 7.判断余额是否充足 与 提现金额做比对
     if user_blance <= msg * (1 + bank_charge):  # (1 + 0.038) 提现金额包含手续费
        return False, '用户余额不足'
    # 8.扣除用户提现金额与手续费    
    user_dict['balance'] -= msg * (1 + bank_charge)
    # 10.保存用户流水
    ctime = time.strftime('%Y-%m-%d %H:%M:%S')
    user_dict['water_flow'].append(
        f'{ctime}:{username}提现了{msg}元,扣除手续费{msg * bank_charge}元,余额{user_dict.get("balance")}元')
    # 11.添加日志
    logging1.debug(f'{username}用户提现了{msg}元')
    # 9.调用数据处理层保存
    db_handler.save(user_dict)
    return True, f'{username}您好, 你本次提现{msg}元,扣除手续费{msg * bank_charge}元,余额{user_dict.get("balance")}元'

数据层:
就不多说了,详看上述调用数据层代码

充值功能详解

展示层:
@common.login_auth
def pay():
    # 1.获取充值金额
    target_money = input('请输入您需要充值的金额').strip()
    # 2 调用 充值接口
    flag, msg = bank_interface.pay_intertace(is_login.get('username'), target_money)
    print(msg)
    
逻辑层:
def pay_intertace(username, target_money):
	# 3.获取用户字典
   user_dict = db_handler.select(username)
	# 4.调用公共功能 转成浮点型,并判断是否是数字
   flag, msg = common.float_num(target_money)
    if not flag:
        return False, '请输入数字'
   # 5.给用户充值
	user_dict['balance'] += msg
   # 7.添加流水记录
   ctime = time.strftime('%Y-%m-%d %H:%M:%S')
   user_dict['water_flow'].append(f'{ctime}:{username}本次充值{msg}元,余额{user_dict.get("balance")}元')
   # 8 添加日志
   logging1.debug(f'{username}用户充值了{msg}元')
   # 6.调用数据层 保存文件
   db_handler.save(user_dict)
   return True, f'{username}您好,你本次充值{msg}元,充值后余额{user_dict.get("balance")}元' 
      

转账功能详解

展示层:
@common.login_auth
def transfer():
    # 1.获取转账目标用户
    target_name = input('请输入您想要转账的用户').strip()
    # 2. 获取转账金额
    target_money = input('请输入你想要转多少金额').strip()
    # 3. 调用转账接口
    flag, msg = bank_interface.transfer_interface(is_login.get('username'), target_name, target_money)
    print(msg)

    
逻辑层:
def transfer_interface(username, target_name, target_money):
    # 4.调用查询接口 获取目标用户字典数据 判断用户是否不存在
    target_name_dict = db_handler.select(target_name)
    if not target_name_dict:
        return False, '用户不存在'
    # 5. 判断用户输入转账金额是否为数字
    flag, msg = common.float_num(target_money)
    if not flag:
        return False, '必须输入数字'
    # 6. 获取用户字典
    user_dict = db_handler.select(username)
    # 7 判断用户账号金额是否充足
    if not user_dict['balance'] >= msg:
        return False, '余额不足'
    # 8. 转账者扣除 转账金额
    user_dict['balance'] -= msg
    # 9 被转账者 增加 转账金额 并添加流水
    
    target_name_dict['balance'] += msg
    ctime = time.strftime('%Y-%m-%d %H:%M:%S')
    user_dict['water_flow'].append(f'{ctime}:{username}本次向{target_name}转账{msg}元,余额{user_dict.get("balance")}元')
    target_name_dict['water_flow'].append(f'{ctime}:{username}向您转账了{msg}元')
    # 11 添加日志
    logging1.debug(f'{username}用户向{target_name}转账了{msg}元')
    # 10. 保存双方数据
    db_handler.save(user_dict)
    db_handler.save(target_name_dict)

    return True, f'{username}您本次向{target_name}转账{msg}元,余额{user_dict.get("balance")}元'

查看流水详解

展示层:
@common.login_auth
def check_flow():
    # 1. 调用流水接口
    flag, msg = bank_interface.check_flow_interface(is_login.get('username'))
    if flag:
        # 为True 的情况下 循环获取  并打印数据
        for i in msg:
            print(i)
    else:
        print(msg)
        
        
逻辑层:
def check_flow_interface(username):
    # 2 调用数据层 获取用户字典
    user_dict = db_handler.select(username)
    # 3 判断流水是否没数据
    if user_dict.get('water_flow'):
        # 4.添加流水
        ctime = time.strftime('%Y-%m-%d %H:%M:%S')
        user_dict['water_flow'].append(f'{ctime}:{username}查看了流水')
        # 5 保存流水 
        db_handler.save(user_dict)
        # 6 添加日志
        logging1.debug(f'{username}用户查看了流水')
        # 7 返回数据给展示层
        return True,user_dict.get('water_flow')
    else:
        return False, '没有流水'

添加购物车详解

展示层:
@common.login_auth
def add_shop_car():
    # 调用购物车接口
    flag, msg = shop_interface.add_shop_car_interface(is_login.get('username'))
    print(msg)
    
    
逻辑层:
def add_shop_car_interface(username):
    # 1.定义临时购物车
    temp_shop_car = {}
    while True:
        # 2.获取商品信息
        good_list = [
            ['挂壁面', 3],
            ['印度飞饼', 22],
            ['极品木瓜', 666],
            ['土耳其土豆', 999],
            ['伊拉克拌面', 1000],
            ['董卓戏张飞公仔', 2000],
            ['仿真玩偶', 10000]
        ]
        for num, good_date in enumerate(good_list):
            print(f'商品编号:{num}   |    商品名称:{good_date[0]}    |    商品单价:{1}')
        # 3.获取用户购买的商品编号
        choice_num = input('请输入您想要购买的商品编号(q)').strip()
        if choice_num == 'q':
            # 12.保存购物车,获取用户购物车
            user_dict = db_handler.select(username)
            real_shop_car = user_dict.get('shop_car')
            # 13. 循环获取临时购物车数据判断是否已存在真实购物车内
            for g_name, g_list in temp_shop_car.items():
                if g_name in real_shop_car:
                    # 13.1 存在则 真实购物车商品数量+ 临时购物车商品数量
                    real_shop_car[g_name][0] += temp_shop_car[g_name][0]
                else:
                    # 13.2 不存在则 真实购物商品名 = 临时购物车列表g_list = [数量,单价]
                    real_shop_car[g_name] = g_list
            # 14 保存购物车
            user_dict['shop_car'] = real_shop_car
            # 15 打开文件保存数据
            db_handler.save(user_dict)
            # 16 添加日志
            logging.warning(f'{username}添加了购物车')
            return True, '购物车添加成功'
        # 4. 判断是否是纯数字
        if not choice_num.isdigit():
            print('商品编号必须是纯数字')
        # 5.转换类型
        choice_num = int(choice_num)
        # 6.判断是否超出范围
        if choice_num not in range(len(good_list)):
            print('商品编号不存在')
        # 7 根据商品编号获取商品名
        target_good = good_list[choice_num]
        # 8. 获取用户想要购买的商品数量
        good_num = input(f'请输入您想要购买{target_good[0]}的数量').strip()
        # 9. 判断是否是数字
        if not good_num.isdigit():
            print('必须是全数字')
        # 10 获取商品名
        good_num = int(good_num)
        good_name = target_good[0]
        # 11.判断是否已存在临时购物车内
        if good_name in temp_shop_car:
            # 11.1 存在则 购物车内商品数量   +  购买商品数量
            temp_shop_car[good_name][0] += good_num
        else:
            # 11.2 不存在则   商品名 = [数量,单价]
            temp_shop_car[good_name] = [good_num, target_good[1]]

查看购物车详解

展示层:
@common.login_auth
def check_shop_car():
    # 1.调用查看购物车接口
    flag, msg = shop_interface.check_shop_car_interface(is_login.get('username'))
    # 2.结果为True
    if flag:
        # 循环获取用户购物车 列表 打印购物车内商品信息
        for i, j in msg.items():
            print(f'商品名:{i} |   商品数量:{j[0]}   |   商品单价:{j[1]}')
    else:
        print(msg)
        
逻辑层:
def check_shop_car_interface(username):
    # 3 调用数据层,获取用户数据
    user_dict = db_handler.select(username)
    real_shop_car = user_dict.get('shop_car')
    # 4.判断获取的用户字典是否没有数据
    if not real_shop_car:
        # 5 保存日志
        logging.warning(f'{username}查看了购物车')
        return False, '购物车是空的'
    return True, real_shop_car

结算购物车详解

展示层:
@common.login_auth
def pay_shop_car():
    # 1.调用结算购物车接口
    flag, msg = shop_interface.pay_shop_car_interface(is_login.get('username'))
    print(msg)

接口层:
def pay_shop_car_interface(username):
    # 2 调用数据层获取用户数据
    user_dict = db_handler.select(username)
    # 3. 获取用户购物车
    user_shop_car = user_dict.get('shop_car')
    # 4. 计算购物总价
    money = 0
    for g_date in user_shop_car.values():
        money += g_date[0] * g_date[1]
    # 5.获取用户余额
    user_balance = user_dict.get('balance')
    if user_balance < money:
        return False, '用户余额不足'
    # 6 结算金额
    user_dict['balance'] -= money
    # 7 清空购物车
    user_dict['shop_car'] = {}
    # 8 添加流水
    ctime = time.strftime('%Y-%m-%d %H:%M:%S')
    user_dict['water_flow'].append(f'{username}结算了购物车商品。总价{money},余额{user_dict.get("balance")}元')
    # 9 记录日志,调用接口保存数据
    logging.warning(f'{ctime}:{username}结算了购物车商品。总价{money}')
    # 10.保存流水与购物车
    db_handler.save(user_dict)
    return True, f'{username}, 您本次消费了{money}元,余额{user_dict.get("balance")}元'

posted @ 2022-10-31 20:30  李阿鸡  阅读(34)  评论(0编辑  收藏  举报
Title