Go-kratos 框架商城微服务实战之商品服务 (六) 商品分类

这篇开始咱们就要编写商品服务了。一个电商的商品设计是比较复杂的,咱们这里不过多的深究表是否合理,是否漏写之类的问题,主要是为了搞明白 kratos 的使用和微服务相关的调用关系。当然我真正的编写时也会尽可能的让此项目的商品设计合理一些。但大量的表设计呀,重复性的 curd 就不会再文章中体现了,具体参看 GitHub 上的源码,当然你觉得不合理的地方,也可以给项目提 PR。

文章写的不清晰的地方可通过 GitHub 源码进行查看, 也感谢您指出不足之处,欢迎大佬指教。

注:竖排 … 代码省略,为了保持文章的篇幅简洁,我会将一些不必要的代码使用竖排的 . 来代替,你在复制本文代码块的时候,切记不要将 . 也一同复制进去。

准备工作

由于前面几篇文章(第一篇和第四篇)都已经写过了,如何初始化一个 kratos 项目,并修改部分主要的文件,来变成自己的服务并编写业务。所以此篇文章就不做重复性的工作了,这篇编写一个商品分类的创建,让商品服务先存在。废话少说开始写

Goods 服务目录存放的位置

// 整体的项目 目录结构如下
|-- kratos-shop
    |-- service
        |-- user // 原先的用户服务 grpc
        |-- goods // 新增的商品服务 grpc
    |-- shop //  interface

编写业务

  • 修改 goods 服务下的 goods.proto
syntax = "proto3";

package goods.v1;

import "google/protobuf/empty.proto";

option go_package = "goods/api/goods/v1;v1";

service Goods {
  rpc CreateCategory(CategoryInfoRequest) returns(CategoryInfoResponse);
}

message CategoryInfoRequest {
  int32 id = 1;
  string name = 2;
  int32 parentCategory = 3;
  int32 level = 4;
  bool isTab = 5;
  int32 sort = 6;
}

message CategoryInfoResponse {
  int32 id = 1;
  string name = 2;
  int32 parentCategory = 3;
  int32 level = 4;
  bool isTab = 5;
  int32 sort = 6;
}
  • 修改 service 目录下的 category.go
// service/goods/internal/service/category.go

package service

import (
    "context"
    "encoding/json"
    v1 "goods/api/goods/v1"
    "goods/internal/biz"
    "google.golang.org/protobuf/types/known/emptypb"
)

// CreateCategory 创建分类
func (g *GoodsService) CreateCategory(ctx context.Context, r *v1.CategoryInfoRequest) (*v1.CategoryInfoResponse, error) {
    result, err := g.cac.CreateCategory(ctx, &biz.CategoryInfo{
        Name:           r.Name,
        ParentCategory: r.ParentCategory,
        Level:          r.Level,
        IsTab:          r.IsTab,
        Sort:           r.Sort,
    })
    if err != nil {
        return nil, err
    }

    return &v1.CategoryInfoResponse{
        Id:             result.ID,
        Name:           result.Name,
        ParentCategory: result.ParentCategory,
        Level:          result.Level,
        IsTab:          result.IsTab,
        Sort:           result.Sort,
    }, nil
}
  • 修改 biz 目录下的 category.go
package biz

import (
    "context"
    "github.com/go-kratos/kratos/v2/log"
)

type Category struct {
    ID               int32
    Name             string
    ParentCategoryID int32
    SubCategory      []*Category
    Level            int32
    IsTab            bool
    Sort             int32
}

type CategoryInfo struct {
    ID             int32
    Name           string
    ParentCategory int32
    Level          int32
    IsTab          bool
    Sort           int32
}

type CategoryRepo interface {
    AddCategory(context.Context, *CategoryInfo) (*CategoryInfo, error)
}

type CategoryUsecase struct {
    repo CategoryRepo
    log  *log.Helper
}

func NewCategoryUsecase(repo CategoryRepo, logger log.Logger) *CategoryUsecase {
    return &CategoryUsecase{repo: repo, log: log.NewHelper(logger)}
}

func (c *CategoryUsecase) CreateCategory(ctx context.Context, r *CategoryInfo) (*CategoryInfo, error) {
    cateInfo, err := c.repo.AddCategory(ctx, r)
    if err != nil {
        return nil, err
    }
    return cateInfo, nil
}
  • 修改 data 目录下的 category.go

package data

import (
    "context"
    "errors"
    "fmt"
    "github.com/go-kratos/kratos/v2/log"
    "goods/internal/biz"
    "gorm.io/gorm"
    "time"
)

// Category 商品分类表
type Category struct {
    ID               int32          `gorm:"primarykey;type:int" json:"id"`
    Name             string         `gorm:"type:varchar(50);not null;comment:分类名称" json:"name"`
    ParentCategoryID int32          `json:"parent_id"`
    ParentCategory   *Category      `json:"-"`
    SubCategory      []*Category    `gorm:"foreignKey:ParentCategoryID;references:ID" json:"sub_category"`
    Level            int32          `gorm:"column:level;default:1;not null;type:int;comment:分类的级别" json:"level"`
    IsTab            bool           `gorm:"comment:是否显示;default:false" json:"is_tab"`
    Sort             int32          `gorm:"comment:分类排序;default:99;not null;type:int" json:"sort"`
    CreatedAt        time.Time      `gorm:"column:add_time" json:"created_at"`
    UpdatedAt        time.Time      `gorm:"column:update_time" json:"updated_at"`
    DeletedAt        gorm.DeletedAt `json:"deleted_at"`
}

type CategoryRepo struct {
    data *Data
    log  *log.Helper
}

// NewCategoryRepo .
func NewCategoryRepo(data *Data, logger log.Logger) biz.CategoryRepo {
    return &CategoryRepo{
        data: data,
        log:  log.NewHelper(logger),
    }
}


func (r *CategoryRepo) AddCategory(ctx context.Context, req *biz.CategoryInfo) (*biz.CategoryInfo, error) {
    cMap := map[string]interface{}{}
    cMap["name"] = req.Name
    cMap["level"] = req.Level
    cMap["is_tab"] = req.IsTab
    cMap["sort"] = req.Sort
    cMap["add_time"] = time.Now()
    cMap["update_time"] = time.Now()

    // 去查询父类目是否存在
    if req.Level != 1 {
        var categories Category
        if res := r.data.db.First(&categories, req.ParentCategory); res.RowsAffected == 0 {
            return nil, errors.New("商品分类不存在")
        }
        cMap["parent_category_id"] = req.ParentCategory
    }

    result := r.data.db.Model(&Category{}).Create(&cMap)
    if result.Error != nil {
        return nil, result.Error
    }
    var value int32
    value, ok := cMap["parent_category_id"].(int32)
    if !ok {
        value = 0
    }
    res := &biz.CategoryInfo{
        Name:           cMap["name"].(string),
        ParentCategory: value,
        Level:          cMap["level"].(int32),
        IsTab:          cMap["is_tab"].(bool),
        Sort:           cMap["sort"].(int32),
    }
    return res, nil
}

测试创建分类

注:这里有个很重要的点,修改 config 配置的时候,需要指定数据库,这里商品服务是独立,故不能跟用户服务用同一个库,此处用的是一个全新的 shop_goods 库。consul 和 trace 的配置保持一致,但主入口 main 文件中的服务名称要修改一下。

如果你用的是 goland 编辑器这里提供一个测试 rpc 接口的方法,就不用再编写指定的测试文件了。

编辑器打开 goods.proto 文件,看到如图:

点击这按钮你会跳到一个全新的文件,如图:

注意记得把请求的 IP 和 Port 修改成自己的,然后点击绿色的按钮,测试一下吧。

结束语

OK 此篇文章就写到这里了,虽然有很多点都没写,但我相信,你肯定会把没写的流程补上,比如如何把 biz 层的 Usecase 注入到 Goods 服务中,如何编写 data 层的数据库连接之类的。

感谢您的耐心阅读,动动手指点个赞吧。

本作品采用《CC 协议》,转载必须注明作者和本文链接
微信搜索:上帝喜爱笨人
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!