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()

  

  

 

posted @ 2022-11-15 11:34  wanghhhh  阅读(106)  评论(0)    收藏  举报