go-商品微服务的开发-srv
一.项目初始化
1. 微服务接口分析

2. 数据库表分析

3. 订单支付全流程

4.目录结构
5.初始化model代码
创建goods_srv及model目录
创建goods_srv/model/models.py文件
from datetime import datetime
from peewee import *
from playhouse.shortcuts import ReconnectMixin
from playhouse.pool import PooledMySQLDatabase
from playhouse.mysql_ext import JSONField
from user_srv.settings.settings import ReconnectMysqlDatabase
# from goods_srv.settings import settings
class ReconnectMySQLDatabase(ReconnectMixin, PooledMySQLDatabase):
pass
db = ReconnectMySQLDatabase("mxshop_goods_srv", host="", port=888,
user="", password="")
DB = ReconnectMysqlDatabase(database="mxshop_goods_srv", host="",
port=9999,
user="", password="")
# 删除 - 物理删除和逻辑删除 - 物理删除 -假设你把某个用户数据 - 用户购买记录,用户的收藏记录,用户浏览记录啊
# 通过save方法做了修改如何确保只修改update_time值而不是修改add_time
class BaseModel(Model):
add_time = DateTimeField(default=datetime.now, verbose_name="添加时间")
is_deleted = BooleanField(default=False, verbose_name="是否删除")
update_time = DateTimeField(verbose_name="更新时间", default=datetime.now)
def save(self, *args, **kwargs):
# 判断这是一个新添加的数据还是更新的数据
if self._pk is not None:
# 这是一个新数据
self.update_time = datetime.now()
return super().save(*args, **kwargs)
@classmethod
def delete(cls, permanently=False): # permanently表示是否永久删除
if permanently:
return super().delete()
else:
return super().update(is_deleted=True)
def delete_instance(self, permanently=False, recursive=False, delete_nullable=False):
if permanently:
return self.delete(permanently).where(self._pk_expr()).execute()
else:
self.is_deleted = True
self.save()
@classmethod
def select(cls, *fields):
return super().select(*fields).where(cls.is_deleted == False)
class Meta:
database = DB
class Category(BaseModel):
name = CharField(max_length=20, verbose_name="名称")
parent_category = ForeignKeyField("self", verbose_name="父类别", null=True) # 一级类别可以没有父类别
level = IntegerField(default=1, verbose_name="级别")
is_tab = BooleanField(default=False, verbose_name="是否显示在首页tab")
class Brands(BaseModel):
# 品牌
name = CharField(max_length=50, verbose_name="名称", index=True, unique=True)
logo = CharField(max_length=200, null=True, verbose_name="图标", default="")
class Goods(BaseModel):
"""
商品, 分布式的事务最好的解决方案 就是不要让分布式事务出现
"""
category = ForeignKeyField(Category, verbose_name="商品类目", on_delete='CASCADE')
brand = ForeignKeyField(Brands, verbose_name="品牌", on_delete='CASCADE')
on_sale = BooleanField(default=True, verbose_name="是否上架")
goods_sn = CharField(max_length=50, default="", verbose_name="商品唯一货号")
name = CharField(max_length=100, verbose_name="商品名")
click_num = IntegerField(default=0, verbose_name="点击数")
sold_num = IntegerField(default=0, verbose_name="商品销售量")
fav_num = IntegerField(default=0, verbose_name="收藏数") # 库存是电商中一个重要的环节
market_price = FloatField(default=0, verbose_name="市场价格")
shop_price = FloatField(default=0, verbose_name="本店价格")
goods_brief = CharField(max_length=200, verbose_name="商品简短描述")
ship_free = BooleanField(default=True, verbose_name="是否承担运费")
images = JSONField(verbose_name="商品轮播图")
desc_images = JSONField(verbose_name="详情页图片")
goods_front_image = CharField(max_length=200, verbose_name="封面图")
is_new = BooleanField(default=False, verbose_name="是否新品")
is_hot = BooleanField(default=False, verbose_name="是否热销")
class GoodsCategoryBrand(BaseModel):
# 品牌分类
id = AutoField(primary_key=True, verbose_name="id")
category = ForeignKeyField(Category, verbose_name="类别")
brand = ForeignKeyField(Brands, verbose_name="品牌")
class Meta:
indexes = (
# 联合主键
(("category", "brand"), True),
)
class Banner(BaseModel):
"""
轮播的商品
"""
image = CharField(max_length=200, default="", verbose_name="图片url")
url = CharField(max_length=200, default="", verbose_name="访问url")
index = IntegerField(default=0, verbose_name="轮播顺序")
if __name__ == "__main__":
db.create_tables([Category, Goods, Brands, GoodsCategoryBrand, Banner])
c1 = Category(name="bobby1", level=1)
c2 = Category(name="bobby2", level=1)
c1.save()
# # c2.save()
# for c in Category.select():
# print(c.name, c.id)
# c1 = Category.get(Category.id==1)
# c1.delete_instance(permanently=True)
# # Category.delete().where(Category.id==2).execute()
6.创建proto文件
创建goods_srv/proto/goods.proto文件,定义好所有的接口
syntax = "proto3";
import "google/protobuf/empty.proto";
option go_package = ".;proto";
service Goods{
//商品接口
rpc GoodsList(GoodsFilterRequest) returns(GoodsListResponse);
//现在用户提交订单有多个商品GoodsListResponse,你得批量查询商品的信息吧
rpc BatchGetGoods(BatchGoodsIdInfo) returns(GoodsListResponse); //批量获取商品信息
rpc CreateGoods(CreateGoodsInfo) returns (GoodsInfoResponse);
rpc DeleteGoods(DeleteGoodsInfo) returns (google.protobuf.Empty);
rpc UpdateGoods(CreateGoodsInfo) returns (google.protobuf.Empty);
rpc GetGoodsDetail(GoodInfoRequest) returns(GoodsInfoResponse);
//商品分类
rpc GetAllCategorysList(google.protobuf.Empty) returns(CategoryListResponse); //获取所有的分类
//获取子分类
rpc GetSubCategory(CategoryListRequest) returns(SubCategoryListResponse);
rpc CreateCategory(CategoryInfoRequest) returns(CategoryInfoResponse); //新建分类信息
rpc DeleteCategory(DeleteCategoryRequest) returns(google.protobuf.Empty); //删除分类
rpc UpdateCategory(CategoryInfoRequest) returns(google.protobuf.Empty); //修改分类信息
//品牌和轮播图
rpc BrandList(BrandFilterRequest) returns(BrandListResponse); //批量获取品牌信息
rpc CreateBrand(BrandRequest) returns(BrandInfoResponse); //新建品牌信息
rpc DeleteBrand(BrandRequest) returns(google.protobuf.Empty); //删除品牌
rpc UpdateBrand(BrandRequest) returns(google.protobuf.Empty); //修改品牌信息
//轮播图
rpc BannerList(google.protobuf.Empty) returns(BannerListResponse); //获取轮播列表信息
rpc CreateBanner(BannerRequest) returns(BannerResponse); //添加banner图
rpc DeleteBanner(BannerRequest) returns(google.protobuf.Empty); //删除轮播图
rpc UpdateBanner(BannerRequest) returns(google.protobuf.Empty); //修改轮播图
//品牌分类
rpc CategoryBrandList(CategoryBrandFilterRequest) returns(CategoryBrandListResponse); //获取轮播列表信息
//通过category获取brands
rpc GetCategoryBrandList(CategoryInfoRequest) returns(BrandListResponse);
rpc CreateCategoryBrand(CategoryBrandRequest) returns(CategoryBrandResponse); //添加banner图
rpc DeleteCategoryBrand(CategoryBrandRequest) returns(google.protobuf.Empty); //删除轮播图
rpc UpdateCategoryBrand(CategoryBrandRequest) returns(google.protobuf.Empty); //修改轮播图
}
message CategoryListRequest {
int32 id = 1;
int32 level = 2;
}
message CategoryInfoRequest {
int32 id = 1;
string name = 2;
int32 parentCategory = 3;
int32 level = 4;
bool isTab = 5;
}
message DeleteCategoryRequest {
int32 id = 1;
}
message QueryCategoryRequest {
int32 id = 1;
string name = 2;
}
message CategoryInfoResponse {
int32 id = 1;
string name = 2;
int32 parentCategory = 3;
int32 level = 4;
bool isTab = 5;
}
message CategoryListResponse {
int32 total = 1;
repeated CategoryInfoResponse data = 2;
string jsonData = 3;
}
message SubCategoryListResponse {
int32 total = 1;
CategoryInfoResponse info = 2;
repeated CategoryInfoResponse subCategorys = 3;
}
message CategoryBrandFilterRequest {
int32 pages = 1;
int32 pagePerNums = 2;
}
message FilterRequest {
int32 pages = 1;
int32 pagePerNums = 2;
}
message CategoryBrandRequest{
int32 id = 1;
int32 categoryId = 2;
int32 brandId = 3;
}
message CategoryBrandResponse{
int32 id = 1;
BrandInfoResponse brand = 2;
CategoryInfoResponse category = 3;
}
message BannerRequest {
int32 id = 1;
int32 index = 2;
string image = 3;
string url = 4;
}
message BannerResponse {
int32 id = 1;
int32 index = 2;
string image = 3;
string url = 4;
}
message BrandFilterRequest {
int32 pages = 1;
int32 pagePerNums = 2;
}
message BrandRequest {
int32 id = 1;
string name = 2;
string logo = 3;
}
message BrandInfoResponse {
int32 id = 1;
string name = 2;
string logo = 3;
}
message BrandListResponse {
int32 total = 1;
repeated BrandInfoResponse data = 2;
}
message BannerListResponse {
int32 total = 1;
repeated BannerResponse data = 2;
}
message CategoryBrandListResponse {
int32 total = 1;
repeated CategoryBrandResponse data = 2;
}
message BatchGoodsIdInfo {
repeated int32 id = 1;
}
message DeleteGoodsInfo {
int32 id = 1;
}
message CategoryBriefInfoResponse {
int32 id = 1;
string name = 2;
}
message CategoryFilterRequest {
int32 id = 1;
bool isTab = 2;
}
message GoodInfoRequest {
int32 id = 1;
}
message CreateGoodsInfo {
int32 id = 1;
string name = 2;
string goodsSn = 3;
int32 stocks = 7; //库存,
float marketPrice = 8;
float shopPrice = 9;
string goodsBrief = 10;
string goodsDesc = 11;
bool shipFree = 12;
repeated string images = 13;
repeated string descImages = 14;
string goodsFrontImage = 15;
bool isNew = 16;
bool isHot = 17;
bool onSale = 18;
int32 categoryId = 19;
int32 brandId = 20;
}
message GoodsReduceRequest {
int32 GoodsId = 1;
int32 nums = 2;
}
message BatchCategoryInfoRequest {
repeated int32 id = 1;
int32 goodsNums = 2;
int32 brandNums = 3;
}
message GoodsFilterRequest {
int32 priceMin = 1;
int32 priceMax = 2;
bool isHot = 3;
bool isNew = 4;
bool isTab = 5;
int32 topCategory = 6;
int32 pages = 7;
int32 pagePerNums = 8;
string keyWords = 9;
int32 brand = 10;
}
message GoodsInfoResponse {
int32 id = 1;
int32 categoryId = 2;
string name = 3;
string goodsSn = 4;
int32 clickNum = 5;
int32 soldNum = 6;
int32 favNum = 7;
float marketPrice = 9;
float shopPrice = 10;
string goodsBrief = 11;
string goodsDesc = 12;
bool shipFree = 13;
repeated string images = 14;
repeated string descImages = 15;
string goodsFrontImage = 16;
bool isNew = 17;
bool isHot = 18;
bool onSale = 19;
int64 addTime = 20;
CategoryBriefInfoResponse category = 21;
BrandInfoResponse brand = 22;
}
message GoodsListResponse {
int32 total = 1;
repeated GoodsInfoResponse data = 2;
}
7.生成proto代码
#安装pip install grpcio -i https://pypi.douban.com/simple #pip install grpcio-tools -i https://pypi.douban.com/simple #生成proto:
python -m grpc_tools.protoc --python_out=. --grpc_python_out=. -I . goods.proto
8. 初始化配置文件
创建goods_srv/settings/settings.py文件,定义所有的变量,及nacos的配置信息
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import json
# 连接池
from playhouse.pool import PooledMySQLDatabase
# 防止出现连接断开查询失败
from playhouse.shortcuts import ReconnectMixin
import nacos
from loguru import logger
class ReconnectMysqlDatabase(ReconnectMixin, PooledMySQLDatabase):
pass
def update_cfg(args):
print(args)
logger.info("配置文件更新")
# content = json.loads(args['content'])
NACOS = {
"Host": "",
"Port": 80,
"NameSpace": "5d941e63-9fe5-416b-899e-ce35488d6b97",
"User": "nacos",
"Password": "",
"DataId": "goods-srv.json",
"Group": "dev"
}
client = nacos.NacosClient(f'{NACOS["Host"]}:{NACOS["Port"]}', namespace=NACOS["NameSpace"], ak=f"{NACOS['User']}",
sk=f"{NACOS['Password']}")
data = client.get_config(NACOS["DataId"], NACOS["Group"])
data = json.loads(data)
logger.info(data)
DB = ReconnectMysqlDatabase(database=data["mysql"]['database'], host=data["mysql"]["host"], port=data["mysql"]["port"],
user=data["mysql"]["user"], password=data["mysql"]["password"])
# consul
CONSUL_HOST = data["consul"]['host']
CONSUL_POST = data["consul"]["port"]
# 服务配置
SERVICE_NAME = data["name"]
SERVICE_TAGS = data["tags"]
9. nacos配置文件
(1)创建命名空间good

(2)创建配置文件
goods-web.json
{
"name": "goods-web",
"port": 8021,
"goods_srv": {
"host": "127.0.0.1",
"port": 50051,
"name": "goods-srv"
},
"jwt": {
"key": "fx4Xg7YPud$jji$y3XCCsReQcvMuZim^"
},
"sms": {
"key": "",
"secrect": "",
"expire": 30
},
"redis": {
"host": "127.0.0.1",
"port": 6379
},
"consul": {
"host": "127.0.0.1",
"port": 8500
}
}
goods-srv.json
{
"consul": {
"host": "127.0.0.1",
"port": 8500
},
"mysql": {
"database": "mxshop_goods_srv",
"host": "",
"port":12345,
"user": "root",
"password": ""
},
"name": "goods-srv",
"tags": [
"python",
"srv"
]
}
10. handler业务逻辑代码初始化
创建goods_srv/handler/goods.py文件,这里是业务逻辑代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from loguru import logger
from goods_srv.model.models import Goods
from goods_srv.proto import goods_pb2, goods_pb2_grpc
class GoodsServicer(goods_pb2_grpc.GoodsServicer):
@logger.catch
def GoodsList(self, request: goods_pb2.GoodsFilterRequest, context):
# 商品列表页
pass
二.商品列表页的开发
1.代码
编辑goods_srv/handler/goods.py文件
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from loguru import logger
from goods_srv.model.models import Goods, Category
from goods_srv.proto import goods_pb2, goods_pb2_grpc
class GoodsServicer(goods_pb2_grpc.GoodsServicer):
def convert_model_to_message(self, goods):
info_rsp = goods_pb2.GoodsInfoResponse()
info_rsp.id = goods.id
info_rsp.categoryId = goods.category_id
info_rsp.name = goods.name
info_rsp.goodsSn = goods.goods_sn
info_rsp.clickNum = goods.click_num
info_rsp.soldNum = goods.sold_num
info_rsp.favNum = goods.fav_num
info_rsp.marketPrice = goods.market_price
info_rsp.shopPrice = goods.shop_price
info_rsp.goodsBrief = goods.goods_brief
info_rsp.shipFree = goods.ship_free
info_rsp.goodsFrontImage = goods.goods_front_image
info_rsp.isNew = goods.is_new
info_rsp.descImages.extend(goods.desc_images)
info_rsp.images.extend(goods.desc_images)
info_rsp.isHot = goods.is_hot
info_rsp.onSale = goods.on_sale
info_rsp.category.id = goods.category.id
info_rsp.category.name = goods.category.name
info_rsp.brand.id = goods.brand.id
info_rsp.brand.name = goods.brand.name
info_rsp.brand.logo = goods.brand.logo
return info_rsp
@logger.catch
def GoodsList(self, request: goods_pb2.GoodsFilterRequest, context):
# 商品列表页
rsp = goods_pb2.GoodsListResponse()
goods = Goods.select()
if request.keyWords:
goods = goods.where(Goods.name.contains(request.keyWords))
if request.isHot:
goods = goods.filter(Goods.is_hot == True)
if request.isNew:
goods = goods.filter(Goods.is_new == True)
if request.priceMin:
goods = goods.filter(Goods.shop_price >= request.priceMin)
if request.priceMax:
goods = goods.filter(Goods.shop_price <= request.priceMax)
if request.brand:
goods = goods.filter(Goods.brand_id == request.brand)
if request.topCategory:
# 通过category来查询商品, 这个category可能是一级、二级或者三级分类
# orm可以帮你提高效率的,但是不能因为使用orm而放弃了解sql语句
try:
ids = []
category = Category.get(Category.id == request.topCategory)
level = category.level
if level == 1:
# 源sql查询:select * from category c1 where c1.parent_category_id in (select c2.id from category c2 where c2.parent_category_id=130358)
c2 = Category.alias()
categorys = Category.select().where(Category.parent_category_id.in_(
c2.select(c2.id).where(c2.parent_category_id == request.topCategory)))
for category in categorys:
ids.append(category.id)
elif level == 2:
categorys = Category.select().where(Category.parent_category_id == request.topCategory)
for category in categorys:
ids.append(category.id)
elif level == 3:
ids.append(request.topCategory)
goods = goods.where(Goods.category_id.in_(ids))
except Exception as e:
print(e)
# 分页 limit offset
start = 0
per_page_nums = 10
if request.pagePerNums:
per_page_nums = request.pagePerNums
if request.pages:
start = per_page_nums * (request.pages - 1)
rsp.total = goods.count()
goods = goods.limit(per_page_nums).offset(start)
for good in goods:
rsp.data.append(self.convert_model_to_message(good))
return rsp
2.接口测试
import json
import grpc
import consul
from google.protobuf import empty_pb2
from goods_srv.proto import goods_pb2, goods_pb2_grpc
from goods_srv.settings import settings
class GoodsTest:
def __init__(self):
# 连接grpc服务器
c = consul.Consul(host="127.0.0.1", port=8500)
services = c.agent.services()
ip = ""
port = ""
for key, value in services.items():
if value["Service"] == settings.SERVICE_NAME:
ip = value["Address"]
port = value["Port"]
break
if not ip:
raise Exception()
channel = grpc.insecure_channel(f"{ip}:{port}")
self.goods_stub = goods_pb2_grpc.GoodsStub(channel)
def goods_list(self):
rsp: goods_pb2.GoodsListResponse = self.goods_stub.GoodsList(
goods_pb2.GoodsFilterRequest(keyWords="越南")
)
for item in rsp.data:
print(item.name, item.shopPrice)
# def batch_get(self):
# ids = [421, 422]
# rsp: goods_pb2.GoodsListResponse = self.goods_stub.BatchGetGoods(
# goods_pb2.BatchGoodsIdInfo(id=ids)
# )
# for item in rsp.data:
# print(item.name, item.shopPrice)
#
# def get_detail(self, id):
# rsp = self.goods_stub.GetGoodsDetail(goods_pb2.GoodInfoRequest(
# id=id
# ))
# print(rsp.name)
#
# def category_list(self):
# rsp = self.goods_stub.GetAllCategorysList(empty_pb2.Empty())
# data = json.loads(rsp.jsonData)
# print(data)
if __name__ == "__main__":
goods = GoodsTest()
goods.goods_list()
# goods.batch_get()
# goods.get_detail(421)
# goods.category_list()
三.批量获取商品信息
1.代码
编辑goods_srv/handler/goods.py文件
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from loguru import logger
from goods_srv.model.models import Goods, Category
from goods_srv.proto import goods_pb2, goods_pb2_grpc
class GoodsServicer(goods_pb2_grpc.GoodsServicer):
def convert_model_to_message(self, goods):
info_rsp = goods_pb2.GoodsInfoResponse()
info_rsp.id = goods.id
info_rsp.categoryId = goods.category_id
info_rsp.name = goods.name
info_rsp.goodsSn = goods.goods_sn
info_rsp.clickNum = goods.click_num
info_rsp.soldNum = goods.sold_num
info_rsp.favNum = goods.fav_num
info_rsp.marketPrice = goods.market_price
info_rsp.shopPrice = goods.shop_price
info_rsp.goodsBrief = goods.goods_brief
info_rsp.shipFree = goods.ship_free
info_rsp.goodsFrontImage = goods.goods_front_image
info_rsp.isNew = goods.is_new
info_rsp.descImages.extend(goods.desc_images)
info_rsp.images.extend(goods.desc_images)
info_rsp.isHot = goods.is_hot
info_rsp.onSale = goods.on_sale
info_rsp.category.id = goods.category.id
info_rsp.category.name = goods.category.name
info_rsp.brand.id = goods.brand.id
info_rsp.brand.name = goods.brand.name
info_rsp.brand.logo = goods.brand.logo
return info_rsp
@logger.catch
def GoodsList(self, request: goods_pb2.GoodsFilterRequest, context):
# 商品列表页
rsp = goods_pb2.GoodsListResponse()
goods = Goods.select()
if request.keyWords:
goods = goods.where(Goods.name.contains(request.keyWords))
if request.isHot:
goods = goods.filter(Goods.is_hot == True)
if request.isNew:
goods = goods.filter(Goods.is_new == True)
if request.priceMin:
goods = goods.filter(Goods.shop_price >= request.priceMin)
if request.priceMax:
goods = goods.filter(Goods.shop_price <= request.priceMax)
if request.brand:
goods = goods.filter(Goods.brand_id == request.brand)
if request.topCategory:
# 通过category来查询商品, 这个category可能是一级、二级或者三级分类
# orm可以帮你提高效率的,但是不能因为使用orm而放弃了解sql语句
try:
ids = []
category = Category.get(Category.id == request.topCategory)
level = category.level
if level == 1:
# 源sql查询:select * from category c1 where c1.parent_category_id in (select c2.id from category c2 where c2.parent_category_id=130358)
c2 = Category.alias()
categorys = Category.select().where(Category.parent_category_id.in_(
c2.select(c2.id).where(c2.parent_category_id == request.topCategory)))
for category in categorys:
ids.append(category.id)
elif level == 2:
categorys = Category.select().where(Category.parent_category_id == request.topCategory)
for category in categorys:
ids.append(category.id)
elif level == 3:
ids.append(request.topCategory)
goods = goods.where(Goods.category_id.in_(ids))
except Exception as e:
print(e)
# 分页 limit offset
start = 0
per_page_nums = 10
if request.pagePerNums:
per_page_nums = request.pagePerNums
if request.pages:
start = per_page_nums * (request.pages - 1)
rsp.total = goods.count()
goods = goods.limit(per_page_nums).offset(start)
for good in goods:
rsp.data.append(self.convert_model_to_message(good))
return rsp
@logger.catch
def BatchGetGoods(self, request: goods_pb2.BatchGoodsIdInfo, context):
"""批量获取商品详情,订单新建的时候可以使用"""
rsp = goods_pb2.GoodsListResponse()
goods = Goods.select().where(Goods.id.in_(list(request.id)))
rsp.total = goods.count()
for good in goods:
rsp.data.append(self.convert_model_to_message(good))
return rsp
2.接口测试
import json
import grpc
import consul
from google.protobuf import empty_pb2
from goods_srv.proto import goods_pb2, goods_pb2_grpc
from goods_srv.settings import settings
class GoodsTest:
def __init__(self):
# 连接grpc服务器
c = consul.Consul(host="127.0.0.1", port=8500)
services = c.agent.services()
ip = ""
port = ""
for key, value in services.items():
if value["Service"] == settings.SERVICE_NAME:
ip = value["Address"]
port = value["Port"]
break
if not ip:
raise Exception()
channel = grpc.insecure_channel(f"{ip}:{port}")
self.goods_stub = goods_pb2_grpc.GoodsStub(channel)
def goods_list(self):
rsp: goods_pb2.GoodsListResponse = self.goods_stub.GoodsList(
goods_pb2.GoodsFilterRequest(keyWords="越南")
)
for item in rsp.data:
print(item.name, item.shopPrice)
def batch_get(self):
ids = [421, 422]
rsp: goods_pb2.GoodsListResponse = self.goods_stub.BatchGetGoods(
goods_pb2.BatchGoodsIdInfo(id=ids)
)
for item in rsp.data:
print(item.name, item.shopPrice)
# def get_detail(self, id):
# rsp = self.goods_stub.GetGoodsDetail(goods_pb2.GoodInfoRequest(
# id=id
# ))
# print(rsp.name)
#
# def category_list(self):
# rsp = self.goods_stub.GetAllCategorysList(empty_pb2.Empty())
# data = json.loads(rsp.jsonData)
# print(data)
if __name__ == "__main__":
goods = GoodsTest()
# goods.goods_list()
#
goods.batch_get()
# goods.get_detail(421)
# goods.category_list()
四. 获取商品详情和删除商品
1.代码
以下为方法:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import grpc
from google.protobuf import empty_pb2
from loguru import logger
from goods_srv.model.models import Goods, Category, DoesNotExist
from goods_srv.proto import goods_pb2, goods_pb2_grpc
class GoodsServicer(goods_pb2_grpc.GoodsServicer):
def convert_model_to_message(self, goods):
info_rsp = goods_pb2.GoodsInfoResponse()
info_rsp.id = goods.id
info_rsp.categoryId = goods.category_id
info_rsp.name = goods.name
info_rsp.goodsSn = goods.goods_sn
info_rsp.clickNum = goods.click_num
info_rsp.soldNum = goods.sold_num
info_rsp.favNum = goods.fav_num
info_rsp.marketPrice = goods.market_price
info_rsp.shopPrice = goods.shop_price
info_rsp.goodsBrief = goods.goods_brief
info_rsp.shipFree = goods.ship_free
info_rsp.goodsFrontImage = goods.goods_front_image
info_rsp.isNew = goods.is_new
info_rsp.descImages.extend(goods.desc_images)
info_rsp.images.extend(goods.desc_images)
info_rsp.isHot = goods.is_hot
info_rsp.onSale = goods.on_sale
info_rsp.category.id = goods.category.id
info_rsp.category.name = goods.category.name
info_rsp.brand.id = goods.brand.id
info_rsp.brand.name = goods.brand.name
info_rsp.brand.logo = goods.brand.logo
return info_rsp
@logger.catch
def DeleteGoods(self, request: goods_pb2.DeleteGoodsInfo, context):
# 删除商品
try:
goods = Goods.get(Goods.id == request.id)
goods.delete_instance()
return empty_pb2.Empty()
except DoesNotExist:
context.set_code(grpc.StatusCode.NOT_FOUND)
context.set_details("记录不存在")
return empty_pb2.Empty()
except Exception as e:
context.set_code(grpc.StatusCode.INTERNAL)
context.set_details(str(e))
return empty_pb2.Empty()
@logger.catch
def GetGoodsDetail(self, request: goods_pb2.GoodInfoRequest, context):
# 获取商品的详情
try:
goods = Goods.get(Goods.id == request.id)
# 每次请求增加click_num
goods.click_num += 1
goods.save()
return self.convert_model_to_message(goods)
except DoesNotExist:
context.set_code(grpc.StatusCode.NOT_FOUND)
context.set_details("记录不存在")
return goods_pb2.GoodsInfoResponse()
2.接口测试
import json
import grpc
import consul
from google.protobuf import empty_pb2
from goods_srv.proto import goods_pb2, goods_pb2_grpc
from goods_srv.settings import settings
class GoodsTest:
def __init__(self):
# 连接grpc服务器
c = consul.Consul(host="127.0.0.1", port=8500)
services = c.agent.services()
ip = ""
port = ""
for key, value in services.items():
if value["Service"] == settings.SERVICE_NAME:
ip = value["Address"]
port = value["Port"]
break
if not ip:
raise Exception()
channel = grpc.insecure_channel(f"{ip}:{port}")
self.goods_stub = goods_pb2_grpc.GoodsStub(channel)
def goods_list(self):
rsp: goods_pb2.GoodsListResponse = self.goods_stub.GoodsList(
goods_pb2.GoodsFilterRequest(keyWords="越南")
)
for item in rsp.data:
print(item.name, item.shopPrice)
def batch_get(self):
ids = [421, 422]
rsp: goods_pb2.GoodsListResponse = self.goods_stub.BatchGetGoods(
goods_pb2.BatchGoodsIdInfo(id=ids)
)
for item in rsp.data:
print(item.name, item.shopPrice)
def get_detail(self, id):
rsp = self.goods_stub.GetGoodsDetail(goods_pb2.GoodInfoRequest(
id=id
))
print(rsp.name)
def category_list(self):
rsp = self.goods_stub.GetAllCategorysList(empty_pb2.Empty())
data = json.loads(rsp.jsonData)
print(data)
if __name__ == "__main__":
goods = GoodsTest()
# goods.goods_list()
#
# goods.batch_get()
goods.get_detail(421)
# goods.category_list()
五. 新建商品接口
1. 库存及下单的微服务设计

2. 新建商品接口代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import grpc
from google.protobuf import empty_pb2
from loguru import logger
from goods_srv.model.models import Goods, Category, DoesNotExist
from goods_srv.proto import goods_pb2, goods_pb2_grpc
class GoodsServicer(goods_pb2_grpc.GoodsServicer):
@logger.catch
def CreateGoods(self, request: goods_pb2.GoodInfoRequest, context):
# 新建商品
try:
category = Category.get(Category.id == request.categoryId)
except DoesNotExist as e:
context.set_code(grpc.StatusCode.NOT_FOUND)
context.set_details("商品分类不存在")
return goods_pb2.GoodsInfoResponse()
try:
brand = Brands.get(Brands.id == request.brandId)
except DoesNotExist as e:
context.set_code(grpc.StatusCode.NOT_FOUND)
context.set_details("品牌不存在")
return goods_pb2.GoodsInfoResponse()
goods = Goods()
goods.brand = brand
goods.category = category
goods.name = request.name
goods.goods_sn = request.goodsSn
goods.market_price = request.marketPrice
goods.shop_price = request.shopPrice
goods.goods_brief = request.goodsBrief
goods.ship_free = request.shipFree
goods.images = list(request.images)
goods.desc_images = list(request.descImages)
goods.goods_front_image = request.goodsFrontImage
goods.is_new = request.isNew
goods.is_hot = request.isHot
goods.on_sale = request.onSale
goods.save()
# TODO 此处完善库存的设置 - 分布式事务
return self.convert_model_to_message(goods)
五. 更新商品接口
1.代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import grpc
from google.protobuf import empty_pb2
from loguru import logger
from goods_srv.model.models import Goods, Category, DoesNotExist, Brands
from goods_srv.proto import goods_pb2, goods_pb2_grpc
class GoodsServicer(goods_pb2_grpc.GoodsServicer):
@logger.catch
def UpdateGoods(self, request: goods_pb2.GoodInfoRequest, context):
# 商品更新
try:
category = Category.get(Category.id == request.categoryId)
except DoesNotExist as e:
context.set_code(grpc.StatusCode.NOT_FOUND)
context.set_details("商品分类不存在")
return goods_pb2.GoodsInfoResponse()
try:
brand = Brands.get(Brands.id == request.brandId)
except DoesNotExist as e:
context.set_code(grpc.StatusCode.NOT_FOUND)
context.set_details("品牌不存在")
return goods_pb2.GoodsInfoResponse()
try:
goods = Goods.get(Goods.id == request.id)
except DoesNotExist as e:
context.set_code(grpc.StatusCode.NOT_FOUND)
context.set_details("商品不存在")
return goods_pb2.GoodsInfoResponse()
goods.brand = brand
goods.category = category
goods.name = request.name
goods.goods_sn = request.goodsSn
goods.market_price = request.marketPrice
goods.shop_price = request.shopPrice
goods.goods_brief = request.goodsBrief
goods.ship_free = request.shipFree
goods.images = list(request.images)
goods.desc_images = list(request.descImages)
goods.goods_front_image = request.goodsFrontImage
goods.is_new = request.isNew
goods.is_hot = request.isHot
goods.on_sale = request.onSale
goods.save()
# TODO 此处完善库存的设置 - 分布式事务
return self.convert_model_to_message(goods)
六. 获取商品全部分类接口
1.代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import json
import grpc
from google.protobuf import empty_pb2
from loguru import logger
from goods_srv.model.models import Goods, Category, DoesNotExist, Brands
from goods_srv.proto import goods_pb2, goods_pb2_grpc
class GoodsServicer(goods_pb2_grpc.GoodsServicer):
@classmethod
def category_model_to_dict(cls, category):
re = dict()
re["id"] = category.id
re["name"] = category.name
re["level"] = category.level
re["parent"] = category.parent_category_id
re["is_tab"] = category.is_tab
return re
@logger.catch
def GetAllCategorysList(self, request: empty_pb2.Empty, context):
# 商品的分类
"""
[{
"name":"xxx",
"id":xxx,
"sub_category":[
{
"id":xxx,
"name":"xxx”,
“sub_category”:[
]
}
]
},{}, {}, {}]
"""
level1 = []
level2 = []
level3 = []
category_list_rsp = goods_pb2.CategoryListResponse()
category_list_rsp.total = Category.select().count()
for category in Category.select():
# 还有什么用
category_rsp = goods_pb2.CategoryInfoResponse()
category_rsp.id = category.id
category_rsp.name = category.name
if category.parent_category_id:
category_rsp.parentCategory = category.parent_category_id
category_rsp.level = category.level
category_rsp.isTab = category.is_tab
category_list_rsp.data.append(category_rsp)
if category.level == 1:
level1.append(self.category_model_to_dict(category))
elif category.level == 2:
level2.append(self.category_model_to_dict(category))
elif category.level == 3:
level3.append(self.category_model_to_dict(category))
# 开始整理
for data3 in level3:
for data2 in level2:
if data3["parent"] == data2["id"]:
if "sub_category" not in data2:
data2["sub_category"] = [data3]
else:
data2["sub_category"].append(data3)
for data2 in level2:
for data1 in level1:
if data2["parent"] == data1["id"]:
if "sub_category" not in data1:
data1["sub_category"] = [data2]
else:
data1["sub_category"].append(data2)
category_list_rsp.jsonData = json.dumps(level1)
return category_list_rsp
七. 商品分类的其他接口
1.代码
创建,更新和删除
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import json
import grpc
from google.protobuf import empty_pb2
from loguru import logger
from goods_srv.model.models import Goods, Category, DoesNotExist, Brands
from goods_srv.proto import goods_pb2, goods_pb2_grpc
class GoodsServicer(goods_pb2_grpc.GoodsServicer):
@logger.catch
def GetSubCategory(self, request: goods_pb2.CategoryListRequest, context):
"""获取每一个分类的子分类"""
category_list_rsp = goods_pb2.SubCategoryListResponse()
try:
category_info = Category.get(Category.id == request.id)
category_list_rsp.info.id = category_info.id
category_list_rsp.info.name = category_info.name
category_list_rsp.info.level = category_info.level
category_list_rsp.info.isTab = category_info.is_tab
if category_info.parent_category:
category_list_rsp.info.parentCategory = category_info.parent_category_id
except DoesNotExist:
context.set_code(grpc.StatusCode.NOT_FOUND)
context.set_details('记录不存在')
return goods_pb2.SubCategoryListResponse()
categorys = Category.select().where(Category.parent_category == request.id)
category_list_rsp.total = categorys.count()
for category in categorys:
category_rsp = goods_pb2.CategoryInfoResponse()
category_rsp.id = category.id
category_rsp.name = category.name
if category_info.parent_category:
category_rsp.parentCategory = category_info.parent_category_id
category_rsp.level = category.level
category_rsp.isTab = category.is_tab
category_list_rsp.subCategorys.append(category_rsp)
return category_list_rsp
@logger.catch
def CreateCategory(self, request: goods_pb2.CategoryInfoRequest, context):
"""新建分类"""
try:
category = Category()
category.name = request.name
if request.level != 1:
category.parent_category = request.parentCategory
category.level = request.level
category.is_tab = request.isTab
category.save()
category_rsp = goods_pb2.CategoryInfoResponse()
category_rsp.id = category.id
category_rsp.name = category.name
if category.parent_category:
category_rsp.parentCategory = category.parent_category.id
category_rsp.level = category.level
category_rsp.isTab = category.is_tab
except Exception as e:
context.set_code(grpc.StatusCode.INTERNAL)
context.set_details('插入数据失败:'+str(e))
return goods_pb2.CategoryInfoResponse()
return category_rsp
@logger.catch
def DeleteCategory(self, request: goods_pb2.DeleteCategoryRequest, context):
"""删除分类"""
try:
category = Category.get(request.id)
category.delete_instance()
# TODO 删除响应的category下的商品
return empty_pb2.Empty()
except DoesNotExist:
context.set_code(grpc.StatusCode.NOT_FOUND)
context.set_details('记录不存在')
return empty_pb2.Empty()
@logger.catch
def UpdateCategory(self, request: goods_pb2.CategoryInfoRequest, context):
"""更新分类"""
try:
category = Category.get(request.id)
if request.name:
category.name = request.name
if request.parentCategory:
category.parent_category = request.parentCategory
if request.level:
category.level = request.level
if request.isTab:
category.is_tab = request.isTab
category.save()
return empty_pb2.Empty()
except DoesNotExist:
context.set_code(grpc.StatusCode.NOT_FOUND)
context.set_details('记录不存在')
return empty_pb2.Empty()
八. 轮播图接口
1.代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import json
import grpc
from google.protobuf import empty_pb2
from loguru import logger
from goods_srv.model.models import Goods, Category, DoesNotExist, Brands, Banner
from goods_srv.proto import goods_pb2, goods_pb2_grpc
class GoodsServicer(goods_pb2_grpc.GoodsServicer):
# #轮播图
@logger.catch
def BannerList(self, request: empty_pb2.Empty, context):
# 获取分类列表
rsp = goods_pb2.BannerListResponse()
banners = Banner.select()
rsp.total = banners.count()
for banner in banners:
banner_rsp = goods_pb2.BannerResponse()
banner_rsp.id = banner.id
banner_rsp.image = banner.image
banner_rsp.index = banner.index
banner_rsp.url = banner.url
rsp.data.append(banner_rsp)
return rsp
@logger.catch
def CreateBanner(self, request: goods_pb2.BannerRequest, context):
banner = Banner()
banner.image = request.image
banner.index = request.index
banner.url = request.url
banner.save()
banner_rsp = goods_pb2.BannerResponse()
banner_rsp.id = banner.id
banner_rsp.image = banner.image
banner_rsp.url = banner.url
return banner_rsp
@logger.catch
def DeleteBanner(self, request: goods_pb2.BannerRequest, context):
try:
banner = Banner.get(request.id)
banner.delete_instance()
return empty_pb2.Empty()
except DoesNotExist:
context.set_code(grpc.StatusCode.NOT_FOUND)
context.set_details('记录不存在')
return empty_pb2.Empty()
@logger.catch
def UpdateBanner(self, request: goods_pb2.BannerRequest, context):
try:
banner = Banner.get(request.id)
if request.image:
banner.image = request.image
if request.index:
banner.index = request.index
if request.url:
banner.url = request.url
banner.save()
return empty_pb2.Empty()
except DoesNotExist:
context.set_code(grpc.StatusCode.NOT_FOUND)
context.set_details('记录不存在')
return empty_pb2.Empty()
八. 品牌相关接口
1.代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import json
import grpc
from google.protobuf import empty_pb2
from loguru import logger
from goods_srv.model.models import Goods, Category, DoesNotExist, Brands, Banner, GoodsCategoryBrand
from goods_srv.proto import goods_pb2, goods_pb2_grpc
class GoodsServicer(goods_pb2_grpc.GoodsServicer):
# 品牌相关的接口
@logger.catch
def BrandList(self, request: empty_pb2.Empty, context):
# 获取品牌列表
rsp = goods_pb2.BrandListResponse()
brands = Brands.select()
rsp.total = brands.count()
for brand in brands:
brand_rsp = goods_pb2.BrandInfoResponse()
brand_rsp.id = brand.id
brand_rsp.name = brand.name
brand_rsp.logo = brand.logo
rsp.data.append(brand_rsp)
return rsp
@logger.catch
def CreateBrand(self, request: goods_pb2.BrandRequest, context):
brands = Brands.select().where(Brands.name == request.name)
if brands:
context.set_code(grpc.StatusCode.ALREADY_EXISTS)
context.set_details('记录已经存在')
return goods_pb2.BrandInfoResponse()
brand = Brands()
brand.name = request.name
brand.logo = request.logo
brand.save()
rsp = goods_pb2.BrandInfoResponse()
rsp.id = brand.id
rsp.name = brand.name
rsp.logo = brand.logo
return rsp
@logger.catch
def DeleteBrand(self, request: goods_pb2.BrandRequest, context):
try:
brand = Brands.get(request.id)
brand.delete_instance()
return empty_pb2.Empty()
except DoesNotExist:
context.set_code(grpc.StatusCode.NOT_FOUND)
context.set_details('记录不存在')
return empty_pb2.Empty()
@logger.catch
def UpdateBrand(self, request: goods_pb2.BrandRequest, context):
try:
brand = Brands.get(request.id)
if request.name:
brand.name = request.name
if request.logo:
brand.logo = request.logo
brand.save()
return empty_pb2.Empty()
except DoesNotExist:
context.set_code(grpc.StatusCode.NOT_FOUND)
context.set_details('记录不存在')
return empty_pb2.Empty()
@logger.catch
def CategoryBrandList(self, request: empty_pb2.Empty, context):
# 获取品牌分类列表
rsp = goods_pb2.CategoryBrandListResponse()
category_brands = GoodsCategoryBrand.select()
#分页
start = 0
per_page_nums = 10
if request.pagePerNums:
per_page_nums = request.PagePerNums
if request.pages:
start = per_page_nums * (request.pages - 1)
category_brands = category_brands.limit(per_page_nums).offset(start)
rsp.total = category_brands.count()
for category_brand in category_brands:
category_brand_rsp = goods_pb2.CategoryBrandResponse()
category_brand_rsp.id = category_brand.id
category_brand_rsp.brand.id = category_brand.brand.id
category_brand_rsp.brand.name = category_brand.brand.name
category_brand_rsp.brand.logo = category_brand.brand.logo
category_brand_rsp.category.id = category_brand.category.id
category_brand_rsp.category.name = category_brand.category.name
category_brand_rsp.category.parentCategory = category_brand.category.parent_category_id
category_brand_rsp.category.level = category_brand.category.level
category_brand_rsp.category.isTab = category_brand.category.is_tab
rsp.data.append(category_brand_rsp)
return rsp
@logger.catch
def GetCategoryBrandList(self, request, context):
#获取某一个分类的所有品牌
rsp = goods_pb2.BrandListResponse()
try:
category = Category.get(Category.id == request.id)
category_brands = GoodsCategoryBrand.select().where(GoodsCategoryBrand.category == category)
rsp.total = category_brands.count()
for category_brand in category_brands:
brand_rsp = goods_pb2.BrandInfoResponse()
brand_rsp.id = category_brand.brand.id
brand_rsp.name = category_brand.brand.name
brand_rsp.logo = category_brand.brand.logo
rsp.data.append(brand_rsp)
except DoesNotExist as e:
context.set_code(grpc.StatusCode.NOT_FOUND)
context.set_details('记录不存在')
return rsp
return rsp
@logger.catch
def CreateCategoryBrand(self, request: goods_pb2.CategoryBrandRequest, context):
category_brand = GoodsCategoryBrand()
try:
brand = Brands.get(request.brandId)
category_brand.brand = brand
category = Category.get(request.categoryId)
category_brand.category = category
category_brand.save()
rsp = goods_pb2.CategoryBrandResponse()
rsp.id = category_brand.id #是另外一种思路
return rsp
except DoesNotExist:
context.set_code(grpc.StatusCode.NOT_FOUND)
context.set_details('记录不存在')
return goods_pb2.CategoryBrandResponse()
except Exception as e:
context.set_code(grpc.StatusCode.INTERNAL)
context.set_details('内部错误')
return goods_pb2.CategoryBrandResponse()
@logger.catch
def DeleteCategoryBrand(self, request: goods_pb2.CategoryBrandRequest, context):
try:
category_brand = GoodsCategoryBrand.get(request.id)
category_brand.delete_instance()
return empty_pb2.Empty()
except DoesNotExist:
context.set_code(grpc.StatusCode.NOT_FOUND)
context.set_details('记录不存在')
return empty_pb2.Empty()
@logger.catch
def UpdateCategoryBrand(self, request: goods_pb2.CategoryBrandRequest, context):
try:
category_brand = GoodsCategoryBrand.get(request.id)
brand = Brands.get(request.brandId)
category_brand.brand = brand
category = Category.get(request.categoryId)
category_brand.category = category
category_brand.save()
return empty_pb2.Empty()
except DoesNotExist:
context.set_code(grpc.StatusCode.NOT_FOUND)
context.set_details('记录不存在')
return empty_pb2.Empty()
except Exception as e:
context.set_code(grpc.StatusCode.INTERNAL)
context.set_details('内部错误')
return empty_pb2.Empty()


浙公网安备 33010602011771号