题目
在上次作业的基础上增加购买等交互
代码
# 商品类
# 用户类(父类)
# 普通用户类(子类)
# VIP用户类(子类)
# 管理员类(子类)
# 商家类
# 商品类
# 异常信息类
# 自定义装饰器类
import random
import time
from abc import ABCMeta, abstractmethod
from functools import wraps
# 错误信息类,用来规范化raise抛出的的错误信息
class ErrorType:
def __init__(self, code, msg):
self.code = code
self.msg = msg
@staticmethod
def ParamsError(errMsg):
return ErrorType(1001, "参数错误:" + errMsg)
@staticmethod
def LoginError(errMsg):
return ErrorType(1002, "登录失败:" + errMsg)
@staticmethod
def NotLoginError():
return ErrorType(1003, "你还未登录!无法进行操作!")
@staticmethod
def StockNotEnoughError():
return ErrorType(1004, "库存不足!")
@staticmethod
def BalanceNotEnoughError():
return ErrorType(1005, "余额不足!")
@staticmethod
def GoodsNotExistError():
return ErrorType(1006, "商品不存在!")
def __str__(self):
return f"错误码:{self.code}, 错误信息 - {self.msg}"
# 自定义装饰器工具类
class CustomDecoratorUtils:
# 以下两个函数为自定义装饰器,在调用被装饰器修饰的方法时,会走这两个装饰器函数。
# 用于对原本函数进行功能添加,例如在设置商品属性前,要进行登录验证,那么装饰器就用于登录验证
@staticmethod
def user_login(func):
@wraps(func)
def wrap(self, *args, **kwargs):
"""可多次试错的登录接口,设置重试次数,默认为3次"""
if self.isLogin:
# 已经登录过了,无需再次登录,直接调用目标函数
return func(self, *args, **kwargs)
raise ValueError(ErrorType.NotLoginError())
return wrap
@staticmethod
def catch_errors(func):
"""装饰器:该函数用于在运行func函数时若报错,则捕获异常"""
# 这里主要用于捕获当用户设置属性时,若属性值不符合要求,函数会抛出异常,被该装饰器捕获。
@wraps(func)
def wrapper(self, *args, **kwargs):
try:
func(self, *args, **kwargs)
return True, None
except ValueError as _e:
print(" - 操作失败:", _e)
return False, _e
return wrapper
class Goods:
__goods_id: str = "000" # 商品编号
def __init__(self,
goods_name: str, purchase_price: float,
sale_price: float, discount: float, stock: int):
# 这里设置goods_id时,使用self.id_number()方法,使用@classmethod装饰器。
# 这样每次创建对象时,都会自动调用该方法,在上一个创建对象的id的基础上递增id:001 002 003
self.__goods_id = self.id_number() # 递增商品编号
# 以下赋值操作,表面上是直接赋值,其实底层已经通过@xxx.setter装饰器,调用了对应的赋值方法
# 这样做的好处是,可以在赋值时,进行一些额外的操作,比如传入的参数是否合法。
self.__goods_name = goods_name # 商品名称
self.__purchase_price = purchase_price # 进货单价 单位:元
self.__sale_price = sale_price # 销售单价 单位:元
self.__discount = discount # 销售折扣 0.0~1.0
self.__stock = stock # 库存 单位:件
# 使用 @classmethod 装饰器,对goods_id进行递增,有记忆性
@classmethod
def id_number(cls):
# 向前补0,补三位,比如:001 002 003
cls.__goods_id = str(int(cls.__goods_id) + 1).zfill(3)
return cls.__goods_id
@property
def goods_id(self):
return self.__goods_id
@property
def goods_name(self):
return self.__goods_name
# 通过 @xxx.setter 装饰器,可以将赋值操作变成方法进行调用。类似于修改底层的赋值操作。
@goods_name.setter
def goods_name(self, goods_name: str):
"""修改商品名称,需要管理员验证"""
# 商品名称不能为None和空
if goods_name is None or goods_name == "":
raise ValueError(ErrorType.ParamsError("商品名称不能为空"))
self.__goods_name = goods_name
@property
def purchase_price(self):
return self.__purchase_price
@purchase_price.setter
def purchase_price(self, purchase_price: float):
"""修改进货价,需要管理员验证"""
# 进货价不能为负数
if purchase_price < 0:
raise ValueError(ErrorType.ParamsError("进货价不能为负数"))
self.__purchase_price = purchase_price
@property
def sale_price(self):
return self.__sale_price
@sale_price.setter
def sale_price(self, sale_price: float):
"""修改单价,需要管理员验证"""
# 单价不能为负数
if sale_price < 0:
raise ValueError(ErrorType.ParamsError("单价不能为负数"))
self.__sale_price = sale_price
@property
def discount(self):
return self.__discount
@discount.setter
def discount(self, discount: float):
"""修改折扣,需要管理员验证"""
# 折扣范围 0.0~1.0
if 0.0 <= discount <= 1.0:
self.__discount = discount
else:
raise ValueError(ErrorType.ParamsError("折扣范围 0.0~1.0"))
@property
def stock(self):
return self.__stock
@stock.setter
def stock(self, stock: int):
"""修改库存,需要管理员验证"""
# 库存不能为负数
if stock < 0:
raise ValueError(ErrorType.ParamsError("库存不能为负数"))
self.__stock = stock
# 定义抽象类:用户类(抽象类的一个特点是它不能直接被实例化)
# 抽象类的目的就是让别的类继承它并实现特定的抽象方法
class User(metaclass=ABCMeta):
__username: str # 用户名
__password: str # 密码
__isLogin: bool # 是否登录
def __init__(self, username, password):
self.__username = username
self.__password = password
self.__isLogin = False
def login(self, username: str, password: str) -> bool:
"""身份验证"""
if self.__username == username and self.__password == password:
self.__isLogin = True
return True
else:
# 登录失败就重置登录状态
self.__isLogin = False
return False
def logout(self):
"""注销"""
self.__isLogin = False
# 抽象方法:打印用户信息
@abstractmethod
def show_user_info(self):
pass
def __str__(self):
return self.show_user_info()
@property
def username(self):
return self.__username
@property
def password(self):
return self.__password
@property
def isLogin(self):
return self.__isLogin
@staticmethod
def custom_login(userList, retry_times=3):
print(" - 请登录!你有{}次机会.".format(retry_times))
for i in range(retry_times):
_username = input("请输入用户名:")
_password = input("请输入密码:")
for _user in userList:
if _user.login(_username, _password):
if type(_user) == Admin:
print(" - 登录成功!欢迎回来~ 你的身份是 管理员!")
elif type(_user) == VipConsumer:
print(" - 登录成功!欢迎回来~ 你的身份是 VIP用户~")
else:
print(" - 登录成功!欢迎回来~ 你的身份是 普通用户~")
return _user
print(" - 登录失败!你还有", retry_times - i - 1, "次机会!")
print(" - 登录失败!账号不存在或密码错误!")
return None
# 消费者类,比User类多了购买的行为和属性,还有背包和购买记录等。
class Consumer(User):
def __init__(self, username: str, password: str, balance: float):
super().__init__(username, password)
self.__balance = balance
self.__backpack = []
self.__records = []
@CustomDecoratorUtils.user_login
@CustomDecoratorUtils.catch_errors
def buy(self, goods: Goods, num: int) -> bool:
# 判断库存是否充足
if goods.stock < num:
raise ValueError(ErrorType.StockNotEnoughError())
total_pay = goods.sale_price * num
# 判断余额是否充足
if self.__balance < total_pay:
raise ValueError(ErrorType.BalanceNotEnoughError())
# 扣除余额
self.__balance -= total_pay
# 扣除库存
goods.stock -= num
# 添加到背包
self.__backpack.append({
"goods_name": goods.goods_name,
"num": num
})
# 添加到购买记录
self.__records.append({
"goods_name": goods.goods_name,
"num": num,
"sale_price": goods.sale_price,
"total_price": total_pay,
"buy_time": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
})
return True
# 获取购买记录
@CustomDecoratorUtils.user_login
@CustomDecoratorUtils.catch_errors
@property
def records(self):
return self.__records
# 获取背包
@CustomDecoratorUtils.user_login
@CustomDecoratorUtils.catch_errors
@property
def backpack(self):
return self.__backpack
@property
def balance(self):
return self.__balance
@property
def username(self):
return super().username
def show_user_info(self):
text = f"【普通消费者】\n用户名:{super().username}\n余额:{self.balance}\n背包:\n"
for index in range(len(self.__backpack)):
text += f"{index + 1}. {self.__backpack[index]['goods_name']} x {self.__backpack[index]['num']}\n"
text += "购买记录:\n"
for index in range(len(self.__records)):
text += f"{index + 1}. {self.__records[index]['goods_name']} x {self.__records[index]['num']} " \
f"单价:{self.__records[index]['sale_price']}元 总价:{self.__records[index]['total_price']}元 " \
f"购买时间:{self.__records[index]['buy_time']}\n"
return text
# 会员用户类:可以享受折扣
class VipConsumer(Consumer):
def __init__(self, username: str, password: str, balance: float):
super().__init__(username, password, balance)
self.__balance = balance
self.__backpack = []
self.__records = []
@CustomDecoratorUtils.user_login
@CustomDecoratorUtils.catch_errors
def buy(self, goods: Goods, num: int):
# 判断库存是否充足
if goods.stock < num:
raise ValueError(ErrorType.StockNotEnoughError())
total_pay = goods.sale_price * num * goods.discount
# 判断余额是否充足
if self.__balance < total_pay:
raise ValueError(ErrorType.BalanceNotEnoughError())
# 扣除余额
self.__balance -= total_pay
# 扣除库存
goods.stock -= num
# 添加到背包
self.__backpack.append({
"goods_name": goods.goods_name,
"num": num
})
# 添加到购买记录
self.__records.append({
"goods_name": goods.goods_name,
"num": num,
"sale_price": goods.sale_price,
"discount": goods.discount,
"total_price": total_pay,
"buy_time": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
})
def show_user_info(self):
text = f"【尊贵的VIP用户】\n用户名:{super().username}\n余额:{self.__balance}\n背包:\n"
for index in range(len(self.__backpack)):
text += f"{index + 1}. {self.__backpack[index]['goods_name']} x {self.__backpack[index]['num']}\n"
text += "购买记录:\n"
for index in range(len(self.__records)):
record_item = self.__records[index]
text += f"{index + 1}. {record_item['goods_name']} x {record_item['num']} " \
f"单价:{record_item['sale_price']}元 享受折扣:{record_item['sale_price']}折 " \
f"总价:{record_item['total_price']}元 购买时间:{record_item['buy_time']}\n"
return text
@property
def username(self):
return super().username
# 管理员类
class Admin(User):
def __init__(self, username: str, password: str):
super().__init__(username, password)
def show_user_info(self):
text = f"【管理员】\n用户名:{super().username}\n"
text += "————————————————————————"
return text
# 店铺类
class Shop:
def __init__(self, shop_name, balance, admins, goods_list):
self.__shop_name = shop_name
self.__goods_list = goods_list
self.__balance = balance # 余额
self.__admins = admins # 管理员列表
# 采集库存
@CustomDecoratorUtils.catch_errors
def purchase_stock(self, goods: Goods, num):
# 从店铺商品中找到这个商品
for index in range(len(self.__goods_list)):
if self.__goods_list[index].goods_name == goods.goods_name:
# 增加库存
self.__goods_list[index].stock += num
# 采购商品,扣除余额
self.__balance -= goods.purchase_price * num
return True
raise ValueError(ErrorType.GoodsNotExistError())
@property
def shop_name(self):
return self.__shop_name
@shop_name.setter
def shop_name(self, shop_name):
self.__shop_name = shop_name
@property
def goods_list(self):
return self.__goods_list
@staticmethod
def show_all_goods(shop_name: str, goods_list: list):
print(f"———————— 欢迎来到【{shop_name}】 ————————")
print("{0:<10}{1:<10}{2:<10}{3:<10}{4:<10}".format("商品编号", "商品名称", "销售单价",
"销售折扣", "库存"))
for goodsManageItem in goods_list:
# 左对齐,保留2位小数
print(
"{0:<13}{1:<12}{2:<12.2f}{3:<12.2f}{4:<12.2f}".format(
goodsManageItem.goods_id,
goodsManageItem.goods_name,
goodsManageItem.sale_price,
goodsManageItem.discount,
goodsManageItem.stock)
)
print("———————————————————————————————————")
def show_shop_detail(self):
text = f"【{self.__shop_name}】\n余额:{self.__balance}\n管理员:\n"
for index in range(len(self.__admins)):
text += f"{index + 1}. {self.__admins[index].username}\n"
text += "商品列表:\n"
for index in range(len(self.__goods_list)):
goods_item = self.__goods_list[index]
text += f"{index + 1}. {goods_item.goods_name} 单价:{goods_item.sale_price}元 " \
f"折扣:{goods_item.discount} 折 库存:{goods_item.stock}\n"
return text
def __str__(self):
return self.show_shop_detail()
def getAction(actionList):
global YourUser
print(f" - {'' if YourUser is None else YourUser.username} 你的菜单如下:")
for index in range(len(actionList)):
print(f"{actionList[index]}")
_action = input("请输入你想进行的操作序号(输入q则退出):")
if _action == "":
print(" - 您没有输入操作序号!")
return -1
if _action == "q":
print(" - 退出程序!")
return -2
try:
_action = int(_action)
except ValueError:
print(" - 您输入的操作序号不是数字!")
return -1
if _action not in range(1, len(actionList) + 1):
print(" - 您输入的操作序号不在范围内!")
return -1
return _action
def menu_user(_action):
global YourUser
global userList
global globalShop
if _action == 1:
_user = User.custom_login(userList)
if _user is None:
return False
YourUser = _user
return True
elif _action == 2:
if YourUser is None:
print(" - 请先登录!")
return False
YourUser.logout()
print(" - 退出登录成功!")
return True
elif _action == 3:
if YourUser is None:
print(" - 请先登录!")
return False
print(YourUser.show_user_info())
return True
def menu_goods(_action):
global YourUser
global globalShop
if YourUser is None:
print(" - 请先登录!")
return False
if type(YourUser) == Admin:
print(" - 管理员无法购买商品!")
return False
if _action == 1:
Shop.show_all_goods(globalShop.shop_name, globalShop.goods_list)
goods_id = input("请输入你要购买的商品编号(输入q则退出):")
if goods_id == "":
print(" - 您没有输入商品编号!")
return False
if goods_id == "q":
return True
for goods in globalShop.goods_list:
if goods.goods_id == goods_id:
_num = input("请输入你要购买数量:")
try:
_num = int(_num)
except ValueError:
print(" - 您输入的数量不是数字!")
return False
YourUser.buy(goods, _num)
return True
print(" - 您输入的商品编号不存在!")
return False
elif _action == 2:
# 打印购买记录
print(YourUser.show_user_info())
return True
elif _action == 3:
# 打印背包
print(YourUser.show_user_info())
return True
def menu_shop(_action):
global YourUser
global globalShop
global warehouse
if YourUser is None:
print(" - 请先登录!")
return False
if type(YourUser) != Admin:
print(" - 非管理员无法操作此菜单!")
return False
if _action == 1:
print(" ------ 仓库如下 ------- ")
print("{0:<10}{1:<10}{2:<10}{3:<10}{4:<10}".format("商品编号", "商品名称", "销售单价",
"销售折扣", "库存"))
for warehouseItem in warehouse:
# 左对齐,保留2位小数
print(
"{0:<13}{1:<12}{2:<12.2f}{3:<12.2f}{4:<12.2f}".format(
warehouseItem.goods_id,
warehouseItem.goods_name,
warehouseItem.sale_price,
warehouseItem.discount,
warehouseItem.stock)
)
print(" ----------------------")
goods_id = input("请输入你要采购的商品编号(输入q则退出):")
if goods_id == "":
print(" - 您没有输入商品编号!")
return False
if goods_id == "q":
return True
for goods in warehouse:
if goods.goods_id == goods_id:
_num = input("请输入你要采购数量:")
try:
_num = int(_num)
except ValueError:
print(" - 您输入的数量不是数字!")
return False
if globalShop.purchase_stock(goods, _num):
print(" - 采购成功!")
return True
print(" - 您输入的商品编号不存在!")
return False
elif _action == 2:
# 修改店铺名称
_shop_name = input("请输入你要修改的店铺名称(输入q则退出):")
if _shop_name == "":
print(" - 您没有输入店铺名称!")
return False
if _shop_name == "q":
return True
globalShop.shop_name = _shop_name
print(" - 修改店铺名称成功!")
return True
elif _action == 3:
# 打印店铺信息
print(globalShop)
return True
if __name__ == '__main__':
# 进货仓库
warehouse = [
Goods(goods_name="苹果", purchase_price=5, sale_price=10, discount=0.8, stock=100),
Goods(goods_name="香蕉", purchase_price=2, sale_price=5, discount=0.5, stock=100),
Goods(goods_name="橘子", purchase_price=3, sale_price=6, discount=0.6, stock=100),
]
# 创建消费者(多态)
userList = [
Admin(username="admin1", password="123456"), # 店铺管理员1
Admin(username="admin2", password="123456"), # 店铺管理员2
Consumer(username="user1", password="123456", balance=100), # 普通消费者
Consumer(username="user2", password="123456", balance=100),
VipConsumer(username="vip", password="123456", balance=100), # Vip消费者
]
adminList = [userList[0], userList[1]]
# 初始化商店,设置管理员进去
globalShop = Shop(shop_name="海哥超市", balance=1000, admins=adminList, goods_list=warehouse)
# 随便采购一些商品
globalShop.purchase_stock(warehouse[0], random.randint(1, 20))
globalShop.purchase_stock(warehouse[1], random.randint(1, 20))
globalShop.purchase_stock(warehouse[2], random.randint(1, 20))
globalShop.show_all_goods(globalShop.shop_name, globalShop.goods_list)
menuFatherList = ["1. 账号管理", "2. 商品交互", "3. 商品管理", "4. 退出程序"]
menuItemList = [
[
"1. 登录账号",
"2. 退出登录",
"3. 查看我的全部信息",
"4. 返回上级菜单",
],
[
"1. 购买商品",
"2. 查看我的购买记录",
"3. 查看我的背包",
"4. 返回上级菜单",
],
[
"1. 采购商品",
"2. 修改店铺名称",
"3. 查看店铺数据信息",
"4. 返回上级菜单",
]
]
YourUser = None
while True:
action_father = getAction(menuFatherList)
if action_father == -1:
continue
if action_father == -2 or action_father == 4:
break
# 选择子菜单
while True:
action_item = getAction(menuItemList[action_father - 1])
if action_item == -1:
continue
if action_item == -2 or action_item == 4:
break
if action_father == 1:
menu_user(action_item)
elif action_father == 2:
menu_goods(action_item)
elif action_father == 3:
menu_shop(action_item)
··`