Scott

kratos如何在自建的模板上快速构建一个用户微服务 22 days ago

微服务
6382个字符
共有8人围观

kratos如何在自建的模板上快速构建一个用户微服务

1. 定义proto文件

syntax = "proto3";

package api.v1;

import "google/protobuf/any.proto";
import "google/api/annotations.proto";
import "google/protobuf/struct.proto";

option go_package = "kratos_blog/api/v1;v1";
option java_multiple_files = true;
option java_package = "api.v1";

service User {
	rpc CreateUser (SignupRequest) returns (ResponseMessage){
		option (google.api.http) = {
		  post: "/api/v1/user/create"
		  body: "*"
		};
	};
	rpc Login(LoginRequest) returns (ResponseMessage){
		option (google.api.http) = {
		  post: "/api/v1/user/login"
		  body: "*"
		};
	};
}

message LoginRequest {
    string username = 1;
	string password = 2;
}

message SignupRequest {
	string username = 1;
	string password = 2;
	string re_password = 3;
	string email = 4;
	string nickname = 5;
}


message ResponseMessage {
	int32 code = 1;                  // 状态码
	string message = 2;              // 状态信息
	// google.protobuf.Any data = 3;    // 响应数据,可以是任意类型
	oneof dt { //one of data
		google.protobuf.Any any_data = 3;  // 保留原始 Any 功能
		google.protobuf.Value data = 4; // 使用更灵活的 JSON 结构
	}
}

2. ent定义user schema并生成CURD代码

user.go

package schema

import (
	"entgo.io/ent"
	"entgo.io/ent/schema/field"
)

// User holds the schema definition for the User entity.
type User struct {
	ent.Schema
}

// Fields of the User.
func (User) Fields() []ent.Field {
	return []ent.Field{
		field.String("username").Unique(),
		field.String("password"),
		field.String("email"),
		field.String("nickname"),
		field.Int64("create_time"),
		field.Bool("is_deleted"),
		field.Bool("allowed_login"),
	}
}

// Edges of the User.
func (User) Edges() []ent.Edge {
	return nil
}

3. biz

package biz

import (
	"context"
	"kratos_blog/ent"

	"github.com/go-kratos/kratos/v2/log"
)

//request
// 实体类
type User struct {
	//table各字段
	Username      string                 `json:"username"`
	Password      string                 `json:"password"`
	RePassword    string                 `json:"re_password"`
	Email         string                 `json:"email"`
	Nickname      string                 `json:"nickname"`
	CreateTime int64 `json:"create_time"`
	IsDeleted bool `json:"is_deleted"`
	AllowedLogin bool `json:"allowed_login"`
}

type LoginForm struct{
	Username      string                 `json:"username"`
	Password      string                 `json:"password"`
}

//repo抽象类接口
type UserRepo interface {
	Create(context.Context, *User) (*ent.User, error)
	Login(context.Context,*LoginForm)(bool,int,error)
}

// UserUsecase is a User usecase.
type UserUsecase struct {
	repo UserRepo
	log  *log.Helper
}

// NewUserUsecase new a User usecase.
func NewUserUsecase(repo UserRepo, logger log.Logger) *UserUsecase {
	return &UserUsecase{repo: repo, log: log.NewHelper(logger)}
}

func (u *UserUsecase) CreateUser(ctx context.Context, user *User) (*ent.User, error)  {
	u.log.Info("creating user...")
	return u.repo.Create(ctx,user)
}

func (u *UserUsecase) VerifyLoginPassword(ctx context.Context, form *LoginForm) (bool, int, error)  {
	u.log.Info("login...")
	return u.repo.Login(ctx,form)
}

4. data

package data

import (
	"context"
	"time"

	"kratos_blog/ent"
	"kratos_blog/ent/user"
	"kratos_blog/internal/biz"

	"github.com/go-kratos/kratos/v2/log"
)

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

// NewUserRepo
func NewUserRepo(data *Data, logger log.Logger) biz.UserRepo {
	return &userRepo{
		data: data,
		log:  log.NewHelper(logger),
	}
}

func (u *userRepo) Create(ctx context.Context, user *biz.User) (*ent.User, error) {
	_user,err:=u.data.client.User.Create().SetUsername(user.Username).
	SetPassword(user.Password).
	SetEmail(user.Email).
	SetNickname(user.Nickname).
	SetCreateTime(time.Now().Unix()).
	SetIsDeleted(false).
	SetAllowedLogin(false).Save(ctx)

	if err!=nil{
		return nil, err
	}
	return _user, nil
}



func (u *userRepo) Login(ctx context.Context, form *biz.LoginForm) (bool, int, error) {
	//query user
	_u, err:=u.data.client.User.Query().Where(user.UsernameEQ(form.Username)).Only(ctx)
	if err!=nil{
		return false,0,err
	}
	if _u.Password == form.Password {
		return true, _u.ID,nil
	}
	return false,0,nil
}

5. service

package service

import (
	"context"
	"os"
	"time"

	pb "kratos_blog/api/v1"
	"kratos_blog/internal/biz"
	"kratos_blog/internal/conf"
	"kratos_blog/pkg/jwt"

	"github.com/go-kratos/kratos/v2/log"
	"google.golang.org/protobuf/types/known/structpb"
)

type UserService struct {
	pb.UnimplementedUserServer
	uc *biz.UserUsecase
	jwt *conf.Jwt  // 添加JWT配置字段
}

func NewUserService(uc *biz.UserUsecase, jwt *conf.Jwt) *UserService {
	return &UserService{
		uc: uc,
		jwt: jwt,
	}
}

func (s *UserService) CreateUser(ctx context.Context, req *pb.SignupRequest) (*pb.ResponseMessage, error) {
	logger := log.NewStdLogger(os.Stdout)
	helper := log.NewHelper(logger)
	
	helper.Infof("received request to create user %v",req)
	
	u, err:=s.uc.CreateUser(ctx,&biz.User{
		Username: req.Username,
		Password    :req.Password,
		Email       :req.Email,
		Nickname      :req.Nickname,
	})

	if err!=nil{
		helper.Errorf("creating user error: %v",err)
		return &pb.ResponseMessage{
			Code: 2001,
			Message: "创建用户失败",
		}, nil
	}
	
	// 使用 Value 而不是 Any
	data, _ := structpb.NewValue(map[string]interface{}{
		"id":       u.ID,
		"username": u.Username,
	})

	return &pb.ResponseMessage{
        Code:    200,
        Message: "创建用户成功",
        Dt: &pb.ResponseMessage_Data{
            Data: data,
        },
    }, nil
	
}


func (s *UserService) Login(ctx context.Context,req *pb.LoginRequest) (*pb.ResponseMessage, error) {
	
	flag,uid,err:=s.uc.VerifyLoginPassword(ctx,&biz.LoginForm{
		Username: req.Username,
		Password: req.Password,
	})
	
	if !flag || err!=nil{
		return &pb.ResponseMessage{
			Code: 2001,
			Message: "登录失败",
		},nil
	}

	expiresDuration:= time.Duration(s.jwt.Expires)* time.Second
	
	// 生成JWT
	
	token, err := jwt.GenerateToken(s.jwt.Secret, expiresDuration, int64(uid))
	if err != nil {
		return  &pb.ResponseMessage{
			Code: 2001,
			Message: "生成token失败",
		},nil
	}

	data, _ := structpb.NewValue(map[string]interface{}{
		"token":       token,
	})

	return &pb.ResponseMessage{
        Code:    200,
        Message: "登录成功",
        Dt: &pb.ResponseMessage_Data{
            Data: data,
        },
    }, nil

}