ATM+购物车+管理员系统
ATM+购物车
需求分析#
# 项目大致需求
- 额度15000或自定义
- 支持多账户登录
- 可以查看账户余额
- 可以提现(可自定义手续费比例)
- 提供还款接口
- 支持账户间转账
- 记录每月日常消费流水
- 实现购物商城,买东西加入购物车,调用信用卡接口结账
- 提供管理接口,包括添加账户、用户额度,冻结账户等
- ATM记录操作日志
- 用户登录认证功能
# 提炼项目功能
1.用户注册
2.登录功能
3.查看余额
4.余额提现
5.账户充值
6.金额转账
7.查看流水
8.添加购物车
9.查看购物车
10.结算购物车
11.管理员功能
# 项目大致技术栈
1.python基础核心编程
2.函数(装饰器)
3.常见内置模块(os,sys,json)
程序架构设计#
1.程序设计的好处
(1)思路清晰
(2)不会出现写一半代码时推翻重写的情况
(3方便自己或以后的同事更好维护
2.三层架构设计的好处
(1)把每个功能都分层三部分,逻辑清晰
(2)如果用户更换不同的用户界面或不同,
的数据储存机制都不会影响接口层的核心
逻辑代码,扩展性强。
(3)可以在接口层,准确的记录日志与流水。
3.三层架构
一 用户视图层
用于与用户交互的,可以接受用户的输入,打印接口返回的数据。
二 逻辑接口层
接受 用户视图层 传递过来的参数,根据逻辑判断调用数据层加以处理,并返回一个结果给 用户视图层。
三 数据处理层
接受接口层传递过来的参数,做数据的处理
- 保存数据
- 查看数据
- 更新数据
- 删除数据
软件开发目录#
conf:项目的配置信息
core:核心的代码
db:数据
interface:接口
lib:共用的一些功能
log:日志
readme:介绍项目的功能使用等
srart.py:项目的启动文件
编写具体功能#
创建用户功能字典及搭建用户图层#
core中get_src代码如下#
# 导入相关模块
from core import reginster,login,withdraw_balance,withdraw_trans_acc,withdraw_top_up,withdraw_flow,\
shop,shopcar_cheak,shopcar_sett,admin
# 功能列表
func_dict = {
'1':reginster.get_register,
'2':login.get_login,
'3':withdraw_balance.get_withdraw_balance,
'4':withdraw_trans_acc.get_withdraw_trans_acc,
'5':withdraw_top_up.get_withdraw_top_up,
'6':withdraw_flow.get_withdraw_flow,
'7':shop.get_shop,
'8':shopcar_cheak.get_shopcar_cheak,
'9':shopcar_sett.get_shopcar_sett,
'10':admin.get_admin
}
def get_start():
while True:
print('''
1.注册
2.登录
3.查看余额
4.转账
5.充值
6,查看流水
7,购物
8.查看购物车
9.清空购物车
10.管理员功能
''')
choise = input('请输入你想要选择的功能>>>:').strip()
if choise in func_dict:
func_name = func_dict[choise]
func_name()
else:
print('输入有误')
start.py代码如下#
# 启动脚本
import os
import sys
sys.path.append(os.path.dirname(__file__))
from core import get_src
if __name__ == '__main__':
get_src.get_start()
conf.settings.py文件下的代码#
import os
# 数据文件路径
BASE_DATA = os.path.dirname(os.path.dirname(__file__))
DB_DIR = os.path.join(BASE_DATA,'db')
if not os.path.exists(DB_DIR):
os.mkdir(DB_DIR)
# 日志配置
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_DATA,'log')
# 如果不存在定义的日志目录就创建一个
if not os.path.isdir(LOG_DIR):
os.mkdir(LOG_DIR)
logfile_name = 'ATM.log'
# log文件的全路径
logfile_path = os.path.join(LOG_DIR, logfile_name)
# 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配置
},
}
db下的db_handle代码
import os
import json
from conf import settings
# 保存数据到db文件夹
def save(data_dict):
username = data_dict['username']
file_path = os.path.join(settings.DB_DIR, f'{username}.json')
with open(file_path, 'w', encoding='utf8') as f:
json.dump(data_dict, f, ensure_ascii=False)
# 读取用户数据
def read(username):
file_path = os.path.join(settings.DB_DIR, f'{username}.json')
if os.path.exists(file_path):
with open(file_path, 'r', encoding='utf8') as f:
data = json.load(f)
return data
lib下的common代码#
# 阿巴阿巴阿巴
import hashlib
import logging.config
from conf import settings
from functools import wraps
# MD5加密
def get_md5_pwd(username,password):
md5 = hashlib.md5()
md5.update(username[0].encode('utf8')) # 动态加盐, 使用用户名第一个字符
md5.update(password.encode('utf8'))
md5_pwd = md5.hexdigest()
return md5_pwd
# 认证登录装饰器
def outer(func_name):
from interface import user_interface
from core import login
@wraps(func_name)
def inner(*args,**kwargs):
if user_interface.name['isname']:
res = func_name(*args,**kwargs)
return res
else:
print('先登录吧')
login.get_login()
return inner
# 日志模块
def get_logger(title):
logging.config.dictConfig(settings.LOGGING_DIC)
logger1 =logging.getLogger(title)
return logger1
注册功能#
用户视图层:获取用户的用户名密码
核心逻辑层:判断用户名密码是否为空、将密码使用MD5动态加盐、将处理好的数据发送给数据处理层存储起来,以用户名的方式创建一个json文件
用户注册视图层#
# 用户登录界面
from interface import user_interface
def get_register():
username = input('请输入用户名>>>>:').strip()
password = input('请输入密码>>>>:').strip()
confirm_pwd = input('确认密码>>>:').strip()
if password != confirm_pwd:
return '两次密码输入不一致'
msg = user_interface.user_register_interface(username,password)
print(msg)
用户注册逻辑层#
文件夹inerface下的user_interface的代码
from db import db_handle
from lib import common
logger = common.get_logger('用户业务')
name = {'isname':''}
def user_register_interface(username, password, balance=20000):
if len(username) == 0 and len(password) == 0: # 判断用户名或密码是否为空
return '用户名或密码不能为空'
res = db_handle.read(username) # 存在这个路径则直接返回(存在则表示已经有了此用户名)
if res:
return '用户名重复'
get_pwd = common.get_md5_pwd(username, password) # 调用MD5加密接口来实现密码加密
data_dict = {
'username': username, # 用户名
'password': get_pwd, # 密码
'balance': balance, # 余额
'flow': [], # 流水信息
'shop_car': {}, # 购物车
'is_lock': False, # 是否锁定状态
'is_admin': False, # 是否是管理员
}
db_handle.save(data_dict) # 通过数据处理层将用户数据保存起来
logger.debug(f'{username}注册成功')
return '注册成功'
登录功能#
用户在视图层输入账号和密码交给登录接口层
通过MD5加密模块来处理过后的密码来进行比对密码是否一致
接口层调用数据处理层的功能来判断用户是否登录成功
如果['is_lock']=True,那么直接返回退出
另外加了一个用户的登录状态变量name = {'isname' = ''}用于记录用户登录状态和一个用户登录认证装饰器
用户登录视图层#
from interface import user_interface
def get_login():
username = input('请输入用户名>>>:').strip()
password = input('请输入密码>>>:').strip()
msg = user_interface.user_login_interface(username, password)
print(msg)
用户登录逻辑层#
# 登录
def user_login_interface(username,password):
res = db_handle.read(username)
if res:
if res['is_lock']:
return '该账号已被拉入黑名单'
get_pwd = common.get_md5_pwd(username,password)
if get_pwd == res['password']:
name['isname'] = username
logger.debug(f'{username}登陆成功')
return '登录成功'
return '密码错误'
return '该用户不存在'
认证登录装饰器#
lib下的common里的代码
# 认证登录装饰器
def outer(func_name):
from interface import user_interface
from core import login
@wraps(func_name)
def inner(*args,**kwargs):
if user_interface.name['isname']:
res = func_name(*args,**kwargs)
return res
else:
print('先登录吧')
login.get_login()
return inner
查看余额#
用户登录后(未登录会被强制登录),查看余额,直接调用查看余额接口,查看余额接口调用数据处理层中的功能
查看余额用户视图层#
from lib import common
from interface import user_interface,wallet_interface
@common.outer
def get_withdraw_balance():
msg = wallet_interface.check_withdraw_interface(user_interface.name['isname'])
print(f'您的余额为{msg}元')
查看余额用户逻辑层#
from db import db_handle
from lib import common
logger = common.get_logger('钱包业务')
# 用户查看余额接口
def check_withdraw_interface(get_name):
data = db_handle.read(get_name)
logger.debug(f"{get_name}查看了余额")
return data['balance']
转账#
视图层
获取当前登录用户的信息
获取转账人的信息
获取转帐金额
判断是否是纯数字
核心层:
判断转账人是否存在
判断转账金额是否小于账户余额
将充值信息添加至用户字典的flow里
将转账后的数据重新写入用户数据
视图层代码#
from lib import common
from interface import user_interface,wallet_interface
@common.outer
def get_withdraw_trans_acc():
to_name = input('请输入你要转入账户的账户名>>>:').strip()
to_money = input('请输入你要转入的金额>>>:').strip()
if to_money.isdigit():
to_money = int(to_money)
res = wallet_interface.withdraw_trans_acc_interface(to_name,to_money,user_interface.name['isname'])
print(res)
else:
print('只能输入数字')
逻辑层代码#
def withdraw_trans_acc_interface(to_name,to_money,from_name):
from_name_data = db_handle.read(from_name)
if from_name_data['balance'] > to_money: # 判断余额是否足够
to_name_data = db_handle.read(to_name)
from_name_data['balance'] -= to_money
to_name_data['balance'] += to_money
info = f"向{to_name}转账{to_money}元"
info1 = f"收到{from_name}的转账{to_money}元"
from_name_data['flow'].append(info)
to_name_data['flow'].append(info1)
db_handle.save(from_name_data)
db_handle.save(to_name_data)
logger.debug(f'{from_name}{info}') # 记录转账人的日志
logger.debug(f'{to_name}{info1}') # 记录被转帐人的日志
return info
else:
return '余额不足'
充值#
视图层:
输入充值金额
判断是否是纯数字
将数据发送给逻辑层(登录人信息,充值金额)
逻辑层:
获取信息,将余额加上要充值的钱数
将充值信息添加至用户字典的flow里
将数据重新写入用户数据
视图层代码:#
from lib import common
from interface import user_interface,wallet_interface
@common.outer
def get_withdraw_top_up():
money = input('请输入你要充值的金额>>>:').strip()
if money.isdigit():
money = int(money)
res = wallet_interface.withdraw_top_up_interface(money,user_interface.name['isname'])
print(res)
逻辑层代码#
# 用户充值
def withdraw_top_up_interface(money,get_name):
data = db_handle.read(get_name)
data['balance'] += money
info = f'用户{get_name}成功充值{money}元'
data['flow'].append(info)
db_handle.save(data)
logger.debug(f'{info},当前余额为{data["balance"]}')
return info
查看流水#
视图层:
直接调用核心层查看流水接口,将登陆人信息发送给核心层
将拿到的列表进行for循环展示
核心层:
获取到用户数据,将数据里的flow对应的列表返回
视图层代码#
from lib import common
from interface import user_interface,wallet_interface
@common.outer
def get_withdraw_flow():
res = wallet_interface.check_withdraw_flow_interface(user_interface.name['isname'])
for i in res:
print(i)
逻辑层代码#
def check_withdraw_flow_interface(get_name):
data = db_handle.read(get_name)
logger.debug(f'{get_name}查看了流水')
return data['flow']
购物#
用户层:
直接调用商城添加购物车接口,发送登陆人信息
核心层:
打印出商品列表
新建一个临时的购物车字典
获取用户选择的商品编号
判断是否是纯数字
判断是否在商品编号范围里面
获取用户购买的商品数量
判断是否是纯数字
判断是否在临时购物车里面
不在则直接新增商品键值对
存在就增加商品价格和商品数量
如果用户输入Q,则直接退出程序
退出程序有以下判断
先获取用户的全部数据>>>再获取用户的购物车数据
判断临时的购物车的键是否在用户数据下的购物车里面
如果在则只新增商品价格和数量
不在则新增键值对
将购物车数据重新写入用户数据里面
将用户数据重新写入用户数据文件
视图层代码#
from interface import user_interface,shop_interface
from lib import common
@common.outer
def get_shop():
res = shop_interface.add_shop_car_interface(user_interface.name['isname'])
print(res)
核心层代码#
from db import db_handle, shop_list
from lib import common
logger = common.get_logger('购物业务')
# 上面代码为shop_interface里的不同接口共有的需求环境
# 添加购物车
def add_shop_car_interface(get_name):
good_list = shop_list.shop_list()
temp_shop_cat = {}
while True:
for i, j in enumerate(good_list):
print(f'商品编号:{i} 商品名称:{j[0]} 商品单价:{j[1]}')
get_num = input('请输入你想要购买的商品编号>>>输入Q退出>>>>:').strip().lower()
if get_num == 'q':
data = db_handle.read(get_name)
data_shop_car = data['shop_car']
for good_name in temp_shop_cat:
if good_name in data_shop_car:
data_shop_car[good_name][0] += temp_shop_cat[good_name][0]
data_shop_car[good_name][1] += temp_shop_cat[good_name][1]
else:
data_shop_car[good_name] = temp_shop_cat[good_name]
data['shop_car'] = data_shop_car
db_handle.save(data)
return '已退出购物'
if get_num.isdigit():
get_num = int(get_num)
if get_num in range(len(good_list)):
get_count = input('请输入你想要购买的数量>>>:').strip()
if get_count.isdigit():
get_count = int(get_count)
shop_name = good_list[get_num]
if shop_name[0] not in temp_shop_cat:
temp_shop_cat[shop_name[0]] = [shop_name[1] * get_count, get_count]
print(temp_shop_cat)
else:
gwc = temp_shop_cat[shop_name[0]]
gwc[0] += shop_name[1] * get_count
gwc[1] += get_count
temp_shop_cat[shop_name[0]] = gwc
print(temp_shop_cat)
else:
print('请输入数字')
else:
print('编号超出最大范围')
else:
print('请输入数字')
查看购物车#
视图层:
直接调用核心层查看购物车接口,将用户数据发送过去
将返回的值进行for循环打印展示
核心层:
获取用户字典返回shop_car键的值
视图层代码#
from interface import user_interface,shop_interface
from lib import common
@common.outer
def get_shopcar_cheak():
res = shop_interface.check_shopcar_list_interface(user_interface.name['isname'])
for i in res:
print(f"商品名字:{i} 商品价格:{res[i][0]} 商品数量:{res[i][1]}")
核心层代码#
# 查看购物车
def check_shopcar_list_interface(get_name):
data = db_handle.read(get_name)
return data['shop_car']
清空购物车#
视图层:
直接调用清空购物车的核心层接口,将登陆人的姓名传入
核心层:
获取登录人的购物车列表
创建一个计算金额的变量
将金额进行循环+在一起
判断用户余额是否足够
足够则支付
添加一个流水信息加在flow里面
将空的购物车字典重新写入用户数据
视图层代码#
from interface import user_interface, shop_interface
from lib import common
@common.outer
def get_shopcar_sett():
res = shop_interface.shop_car_sett_interface(user_interface.name['isname'])
print(res)
核心层代码#
# 清空购物车
def shop_car_sett_interface(get_name):
res = check_shopcar_list_interface(get_name)
data = db_handle.read(get_name)
money = 0
for i in res:
print(f"商品名字:{i} 商品价格:{res[i][0]} 商品数量:{res[i][1]}")
for good_name in res:
get_money = res[good_name][0]
money += get_money
print(f'总价{money}元')
choise = input('是否购买>>>>按1为购买,其他操作退出>>>>>>')
if choise == '1':
if data['balance'] > money:
data['balance'] -= money
data['shop_car'] = {}
info = f'消费了{money}元'
data['flow'].append(info)
db_handle.save(data)
logger.info(f'{get_name}{info}')
return info
else:
return '钱不够啊老铁'
else:
return '已退出'
管理员功能#
一个整体的功能,分为4个小功能(添加\移除黑名单,删除用户,退出)
管理员功能列表搭建#
判断当前登录账户是否是管理员
输入4则直接退出
视图层#
from core import admin_add_black_list,admin_remove_balcklist,admin_del_user
from lib import common
from interface import admin_interface,user_interface
func_dict = {
'1':admin_add_black_list.get_add_blacklist,
'2':admin_remove_balcklist.get_remove_blacklist,
'3':admin_del_user.get_del_user
}
@common.outer
def get_admin():
flag,res = admin_interface.is_admin_user(user_interface.name['isname'])
if flag:
print(res)
return
while True:
print('''
1.添加黑名单
2.移除黑名单
3.删除用户
4.退出
''')
choise = input('请输入你想执行的功能>>>:').strip()
if choise == '4':
return
if choise in func_dict:
func_name = func_dict[choise]
func_name()
else:
print('输入有误')
核心层代码#
# 验证是否是管理员
def is_admin_user(get_name):
data = db_handle.read(get_name)
if data['is_admin']:
return False,'管理员好'
return True, '你还不是管理员'
添加黑名单#
视图层:
获取要拉黑的名字,调用添加黑名单接口
核心层:
获取被要拉黑名字的数据,将数据字典的is_lock改为True
视图层代码#
from interface import admin_interface
def get_add_blacklist():
choise = input('请输入你想要封禁的用户名>>>:')
res = admin_interface.add_balck_inerface(choise)
print(res)
核心层代码#
# 添加黑名单
def add_balck_inerface(get_name):
data = db_handle.read(get_name)
if data:
data['is_lock'] = True
db_handle.save(data)
logger.debug(f'管理员{user_interface.name["isname"]}将用户{get_name}添加到了黑名单')
return f'用户{get_name}已被封禁'
else:
return '没有此用户'
移除黑名单#
原理和添加一样
视图层代码#
from interface import admin_interface
def get_remove_blacklist():
choise = input('请输入你想要解封的用户名>>>:')
res = admin_interface.remove_balck_inerface(choise)
print(res)
核心层代码#
def remove_balck_inerface(get_name):
data = db_handle.read(get_name)
if data:
data['is_lock'] = False
db_handle.save(data)
logger.debug(f'管理员{user_interface.name["isname"]}将用户{get_name}移除了黑名单')
return f'用户{get_name}已被解封'
else:
return '没有此用户'
删除用户#
用户层:
获取要删除用户的名字
核心层:
获取改名字所在的文件路径
如果存在则删除
视图层代码:#
from interface import admin_interface
def get_del_user():
choise = input('请输入你想要删除的用户名>>>:')
res = admin_interface.del_user_inerface(choise)
print(res)
逻辑层代码#
# 删除用户
def del_user_inerface(get_name):
res = os.path.join(settings.DB_DIR,f'{get_name}.json')
if os.path.exists(res):
os.remove(res)
logger.debug(f'管理员{user_interface.name["isname"]}将用户{get_name}删除了')
return f'{get_name}用户已删除'
else:
return '没有此用户'
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现