评价系统(3)

kratos编写流程

正如前面的提到的,kratos是protobuf驱动的框架,使用kratos proto add添加模板。
kratos proto add api/review/v1/review.proto

syntax = "proto3";

package api.review.v1;
import "google/api/annotations.proto";
option go_package = "review-service/api/review/v1;v1";
option java_multiple_files = true;
option java_package = "api.review.v1";

service Review {
    rpc CreateReview (CreateReviewRequest) returns (CreateReviewReply){
        option (google.api.http) ={
            post: "/v1/review",
            body: "*",
        };
    };
    rpc UpdateReview (UpdateReviewRequest) returns (UpdateReviewReply);
    rpc DeleteReview (DeleteReviewRequest) returns (DeleteReviewReply);
    rpc GetReview (GetReviewRequest) returns (GetReviewReply);
    rpc ListReview (ListReviewRequest) returns (ListReviewReply);
}

message CreateReviewRequest {
    int64 userID = 1;
    int64 orderID = 2;
    int32 score = 3;
    int32 serviceScore = 4;
    int32 expressScore = 5;
    string content = 6;
    string picInfo = 7;
    string videoInfo = 8;
    bool anonymous = 9;
}
message CreateReviewReply {
    int64 reviewID = 1;
}

message UpdateReviewRequest {}
message UpdateReviewReply {}

message DeleteReviewRequest {}
message DeleteReviewReply {}

message GetReviewRequest {}
message GetReviewReply {}

message ListReviewRequest {}
message ListReviewReply {}

生成客户端,服务端代码

kratos proto client api/review/v1/review.proto
kratos proto server api/review/v1/review.proto -t internal/service 

服务此时通过server->service->diz->data调用。
正如之前章节提到,此时service处理来的请求,调用diz层最后由data保存到数据库。

type ReviewService struct {
    pb.UnimplementedReviewServer
        //依赖注入:service 层调用 biz 层的 Usecase 来执行业务逻辑
    uc *biz.ReviewUsecase
}

func NewReviewService(uc *biz.ReviewUsecase) *ReviewService {
    return &ReviewService{uc: uc}
}

func (s *ReviewService) CreateReview(ctx context.Context, req *pb.CreateReviewRequest) (*pb.CreateReviewReply, error) {
    fmt.Printf("[service] CreateReview, req:%#v\n", req)
    var anonymous int32
    if req.Anonymous {
        anonymous = 1
    }
    review, err := s.uc.CreateReview(ctx, &model.ReviewInfo{
        UserID:       req.UserID,
        OrderID:      req.OrderID,
        Score:        req.Score,
        ServiceScore: req.Score,
        ExpressScore: req.ExpressScore,
        Content:      req.Content,
        PicInfo:      req.PicInfo,
        VideoInfo:    req.VideoInfo,
        Anonymous:    anonymous,
        Status:       0,
    })
    return &pb.CreateReviewReply{ReviewID: review.ReviewID}, err
}
func (s *ReviewService) UpdateReview(ctx context.Context, req *pb.UpdateReviewRequest) (*pb.UpdateReviewReply, error) {
    return &pb.UpdateReviewReply{}, nil
}
func (s *ReviewService) DeleteReview(ctx context.Context, req *pb.DeleteReviewRequest) (*pb.DeleteReviewReply, error) {
    return &pb.DeleteReviewReply{}, nil
}
func (s *ReviewService) GetReview(ctx context.Context, req *pb.GetReviewRequest) (*pb.GetReviewReply, error) {
    return &pb.GetReviewReply{}, nil
}
func (s *ReviewService) ListReview(ctx context.Context, req *pb.ListReviewRequest) (*pb.ListReviewReply, error) {
    return &pb.ListReviewReply{}, nil
}

实现具体逻辑的diz

//diz/review.go
// ReviewRepo  is a ReviewRepo .
//data层即数据库应该进行的操作
type ReviewRepo interface {
    SaveReview(context.Context, *model.ReviewInfo) (*model.ReviewInfo, error)
    GetReviewByOrderID(context.Context, int64) ([]*model.ReviewInfo, error)
}

// ReviewUsecase is a Review usecase.
//连接data和service的结构体
type ReviewUsecase struct {
    repo ReviewRepo
    log  *log.Helper
}

// NewGreeterUsecase new a Review usecase.
func NewReviewUsecase(repo ReviewRepo, logger log.Logger) *ReviewUsecase {
    return &ReviewUsecase{repo: repo, log: log.NewHelper(logger)}
}

// CreateGreeter creates a Review, and returns the new Review.
func (uc *ReviewUsecase) CreateReview(ctx context.Context, review *model.ReviewInfo) (*model.ReviewInfo, error) {
//首先判断该订单是否已经评论过,如果评论过就返回
    uc.log.WithContext(ctx).Debugf("[biz] CreateReview, req:%v", review)
    reviews, err := uc.repo.GetReviewByOrderID(ctx, review.OrderID)
    if err != nil {
        return nil, errors.New("查询数据库失败")
    }
    if len(reviews) > 0 {
        // 已经评价过
        fmt.Printf("订单已评价, len(reviews):%d\n", len(reviews))
        return nil, fmt.Errorf("订单:%d已评价", review.OrderID)
    }
  否则通过雪花算法生成ID
    review.ReviewID = snowflake.GenID()
//保存评论
    return uc.repo.SaveReview(ctx, review) 
}

接着是data

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

func (r *reviewRepo) SaveReview(ctx context.Context, info *model.ReviewInfo) (*model.ReviewInfo, error) {
    //TODO implement me
    err := r.data.query.ReviewInfo.WithContext(ctx).Save(info)

    return info, err
}

func (r *reviewRepo) GetReviewByOrderID(ctx context.Context, i int64) ([]*model.ReviewInfo, error) {
    return r.data.query.ReviewInfo.
        WithContext(ctx).
        Where(r.data.query.ReviewInfo.OrderID.Eq(i)).
        Find()
}

func NewReviewRepo(data *Data, logger log.Logger) biz.ReviewRepo {
    return &reviewRepo{
        data: data,
        log:  log.NewHelper(logger),
    }
}

接下来就是补充调用配置和补充依赖注入。可以看到有mysql和雪花id都需要加载配置项,而kratos框架的config也是由protobuf生成定义的。
依次补充依赖注入。从server->service->diz->data

//server/server.go
var ProviderSet = wire.NewSet(NewGRPCServer, NewHTTPServer)
//service/service.go
// ProviderSet is service providers.
var ProviderSet = wire.NewSet(NewReviewService)
//diz/diz.go
// ProviderSet is biz providers.
var ProviderSet = wire.NewSet(NewReviewUsecase)
//data/data.go
/*不同于其他依赖注入的地方,在data层中我们需要对原代码进行修改
// ProviderSet is data providers.
var ProviderSet = wire.NewSet(NewData, NewTodoRepo)

// Data .
type Data struct {
    // TODO wrapped database client
    // db *gorm.DB
    // db sqlx.DB
}

// NewData .
func NewData(c *conf.Data, logger log.Logger) (*Data, func(), error) {
    cleanup := func() {
        log.NewHelper(logger).Info("closing the data resources")
    }
    return &Data{}, cleanup, nil
}
*/
// ProviderSet is data providers.
var ProviderSet = wire.NewSet(NewData, NewReviewRepo, NewDB)

// Data .
type Data struct {
    // TODO wrapped database client
    query *query.Query
    log   *log.Helper
}

// NewData .
//连接数据库
func NewData(db *gorm.DB, logger log.Logger) (*Data, func(), error) {
    cleanup := func() {
        log.NewHelper(logger).Info("closing the data resources")
    }
    // 非常重要!为GEN生成的query代码设置数据库连接对象
      //以便我们不需要每个包都用query.useDB()
    query.SetDefault(db)

    return &Data{query: query.Q, log: log.NewHelper(logger)}, cleanup, nil
}

func NewDB(cfg *conf.Data) (*gorm.DB, error) {
    switch strings.ToLower(cfg.Database.GetDriver()) {
    case "mysql":
        return gorm.Open(mysql.Open(cfg.Database.GetSource()))
    case "sqlite":
        return gorm.Open(sqlite.Open(cfg.Database.GetSource()))
    }
    return nil, errors.New("connect db fail unsupported db driver")
}


使用wire生成

// wireApp init kratos application.
func wireApp(*conf.Server, *conf.Data, log.Logger) (*kratos.App, func(), error) {
    panic(wire.Build(server.ProviderSet, data.ProviderSet, biz.ProviderSet, service.ProviderSet, newApp))
}

conf/conf.proto

syntax = "proto3";
package kratos.api;

option go_package = "review-service/internal/conf;conf";

import "google/protobuf/duration.proto";
//需要添加的有Snowflake 中的开始时间和machine_id  ,其他都由模板生成。
message Bootstrap {
  Server server = 1;
  Data data = 2;
  Snowflake snowflake = 3;
}

message Snowflake{
  string start_time = 1;
  int64 machine_id = 2;
}

message Server {
  message HTTP {
    string network = 1;
    string addr = 2;
    google.protobuf.Duration timeout = 3;
  }
  message GRPC {
    string network = 1;
    string addr = 2;
    google.protobuf.Duration timeout = 3;
  }
  HTTP http = 1;
  GRPC grpc = 2;
}

message Data {
  message Database {
    string driver = 1;
    string source = 2;
  }
  message Redis {
    string network = 1;
    string addr = 2;
    google.protobuf.Duration read_timeout = 3;
    google.protobuf.Duration write_timeout = 4;
  }
  Database database = 1;
  Redis redis = 2;
}

接着使用雪花算法,需要现在主函数中初始化

    if err := snowflake.Init(
        bc.Snowflake.StartTime,
        bc.Snowflake.MachineId,
    ); err != nil {
        panic(err)
    }

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 服务构建,服务注册与服务发现 在流程图中可以发现 ,他们都通过调用 来对评价进行操作。 :对商品进行评价,获取用户...
    Wss_Study阅读 15评论 0 0
  • Kratos是一个Go语言实现的微服务框架,说得更准确一点,它更类似于一个使用Go构建微服务的工具箱,开发者可以按...
    Wss_Study阅读 9评论 0 0
  • 编写中间件和错误处理。 中间件是一种位于请求处理链中的组件,用来在请求到达业务逻辑前、或响应返回客户端之前,进行一...
    Wss_Study阅读 14评论 0 0
  • 数据结构队列集合链表、数组字典、关联数组栈树二叉树完全二叉树平衡二叉树二叉查找树(BST)红黑树B-,B+,B*树...
    jackcooper阅读 3,309评论 1 50
  • Bazel构建Kratos微服务指南 Kratos是一个微服务框架,既然是微服务,那么一个工程下肯定会存在不少的服...
    喵了个咪的呀阅读 417评论 0 1