用户微服务

一.用户微服务

1-1定义用户表结构
1.目录结构建立

在这里插入图片描述

2.model建立

位置:model/user.go

type BaseModel struct {
   ID        int32     `gorm:"primarykey"`
   CreatedAt time.Time `gorm:"column:add_time"`
   UpdatedAt time.Time `gorm:"column:update_time"`
   DeletedAt gorm.DeletedAt
   IsDeleted bool
}

type User struct {
   BaseModel
   Mobile   string     `gorm:"index:idx_mobile;unique;type:varchar(11);not null"`
   Password string     `gorm:"type:varchar(100);not null"`
   NickName string     `gorm:"type:varchar(20)"`
   Birthday *time.Time `gorm:"type:datetime"`
   Gender   string     `gorm:"column:gender;default:male;type:varchar(6) comment 'female表示女, male表示男'"`
   Role     int        `gorm:"column:role;default:1;type:int comment '1表示普通用户, 2表示管理员'"`
}
1-2同步表结构

代码位置:model/main/main.go

func main() {
	dsn := "root:123456@tcp(127.0.0.1:3306)/mxshop_user_srv?charset=utf8mb4&parseTime=True&loc=Local"

	newLogger := logger.New(
		log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
		logger.Config{
			SlowThreshold: time.Second, // 慢 SQL 阈值
			LogLevel:      logger.Info, // Log level
			Colorful:      true,        // 禁用彩色打印
		},
	)

	// 全局模式
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
		NamingStrategy: schema.NamingStrategy{
			SingularTable: true,
		},
		Logger: newLogger,
	})
	if err != nil {
		panic(err)
	}

	_ = db.AutoMigrate(&model.User{}) //此处应该有sql语句
}	
1-3md5加密
func genMd5(code string) string {
   Md5 := md5.New()
   _, _ = io.WriteString(Md5, code)
   return hex.EncodeToString(Md5.Sum(nil))
}
1-4md5盐值加密解决用户密码安全问题

[外链图片转存中…(img-N8JM5Fnl-1697009015930)]

	options := &password.Options{16, 100, 32, sha512.New}
	salt, encodedPwd := password.Encode("generic password", options)
	newPassword := fmt.Sprintf("$pbkdf2-sha512$%s$%s", salt, encodedPwd)
	fmt.Println(len(newPassword))
	fmt.Println(newPassword)

	passwordInfo := strings.Split(newPassword, "$")
	fmt.Println(passwordInfo)
	check := password.Verify("generic password", passwordInfo[2], passwordInfo[3], options)
	fmt.Println(check) // true
1-5定义proto接口

位置:proto/user.proto

syntax = "proto3";
import "google/protobuf/empty.proto";
option go_package = ".;proto";

service User{
    rpc GetUserList(PageInfo) returns (UserListResponse); // 用户列表
    rpc GetUserByMobile(MobileRequest) returns (UserInfoResponse); //通过mobile查询用户
    rpc GetUserById(IdRequest) returns (UserInfoResponse); //通过id查询用户
    rpc CreateUser(CreateUserInfo) returns (UserInfoResponse); // 添加用户
    rpc UpdateUser(UpdateUserInfo) returns (google.protobuf.Empty); // 更新用户
    rpc CheckPassWord(PasswordCheckInfo) returns (CheckResponse); //检查密码
}

message PasswordCheckInfo {
    string password = 1;
    string encryptedPassword = 2;
}


message CheckResponse{
    bool success = 1;
}

message PageInfo {
    uint32 pn = 1;
    uint32 pSize = 2;
}

message MobileRequest{
    string mobile = 1;
}

message IdRequest {
    int32 id = 1;
}

message CreateUserInfo {
    string nickName = 1;
    string passWord = 2;
    string mobile = 3;
}

message UpdateUserInfo {
    int32 id = 1;
    string nickName = 2;
    string gender = 3;
    uint64 birthDay = 4;
}

message UserInfoResponse {
    int32 id = 1;
    string passWord = 2;
    string mobile = 3;
    string nickName = 4;
    uint64 birthDay = 5;
    string gender = 6;
    int32 role = 7;
}

message UserListResponse {
    int32 total = 1;
    repeated UserInfoResponse data = 2;
}
1-6用户列表接口

代码位置:handler/user.go

1.定义UserServer
type UserServer struct {
	proto.UnimplementedUserServer
}
2.初始化数据库连接

代码位置:global/global.go

var (
	DB           *gorm.DB
)

func init() {
   dsn := "root:123456@tcp(127.0.0.1:3306)/mxshop_user_srv?charset=utf8mb4&parseTime=True&loc=Local"
   newLogger := logger.New(
      log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
      logger.Config{
         SlowThreshold: time.Second, // 慢 SQL 阈值
         LogLevel:      logger.Info, // Log level
         Colorful:      true,        // 禁用彩色打印
      },
   )

   // 全局模式
   var err error
   DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
      NamingStrategy: schema.NamingStrategy{
         SingularTable: true,
      },
      Logger: newLogger,
   })
   if err != nil {
      panic(err)
   }
}
3.定义接口
func ModelToResponse(user model.User) proto.UserInfoResponse {
   userInfoRsp := proto.UserInfoResponse{
      Id:       user.ID,
      PassWord: user.Password,
      NickName: user.NickName,
      Gender:   user.Gender,
      Role:     int32(user.Role),
   }
   if user.Birthday != nil {
      userInfoRsp.BirthDay = uint64(user.Birthday.Unix())
   }
   return userInfoRsp
}
func Paginate(page, pageSize int) func(db *gorm.DB) *gorm.DB {
   return func(db *gorm.DB) *gorm.DB {
      if page == 0 {
         page = 1
      }

      switch {
      case pageSize > 100:
         pageSize = 100
      case pageSize <= 0:
         pageSize = 10
      }
      offset := (page - 1) * pageSize
      return db.Offset(offset).Limit(pageSize)
   }
}

// GetUserList 获取用户列表
func (s *UserServer) GetUserList(ctx context.Context, req *proto.PageInfo) (*proto.UserListResponse, error) {
   //获取用户列表
   var users []model.User
   result := global.DB.Find(&users)
   if result.Error != nil {
      return nil, result.Error
   }
   rsp := &proto.UserListResponse{}
   rsp.Total = int32(result.RowsAffected)

   global.DB.Scopes(Paginate(int(req.Pn), int(req.PSize))).Find(&users)

   for _, user := range users {
      userInfoRsp := ModelToResponse(user)
      rsp.Data = append(rsp.Data, &userInfoRsp)
   }
   return rsp, nil
}
1-7通过id和mobile查询用户
// GetUserByMobile 通过手机号码查询用户
func (s *UserServer) GetUserByMobile(ctx context.Context, req *proto.MobileRequest) (*proto.UserInfoResponse, error) {
   var user model.User
   result := global.DB.Where(&model.User{Mobile: req.Mobile}).First(&user)
   if result.RowsAffected == 0 {
      return nil, status.Errorf(codes.NotFound, "用户不存在")
   }
   if result.Error != nil {
      return nil, result.Error
   }
   userInfoRsp := ModelToResponse(user)
   return &userInfoRsp, nil
}

// GetUserById 通过ID查询用户
func (s *UserServer) GetUserById(ctx context.Context, req *proto.IdRequest) (*proto.UserInfoResponse, error) {
	//通过id查询用户
	var user model.User
	result := global.DB.First(&user, req.Id)
	if result.RowsAffected == 0 {
		return nil, status.Errorf(codes.NotFound, "用户不存在")
	}
	if result.Error != nil {
		return nil, result.Error
	}

	userInfoRsp := ModelToResponse(user)
	return &userInfoRsp, nil
}
1-8新建用户
//  CreateUser 创建用户
func (s *UserServer) CreateUser(ctx context.Context, req *proto.CreateUserInfo) (*proto.UserInfoResponse, error) {
   //新建用户
   var user model.User
   result := global.DB.Where(&model.User{Mobile: req.Mobile}).First(&user)
   if result.RowsAffected == 1 {
      return nil, status.Errorf(codes.AlreadyExists, "用户已存在")
   }

   user.Mobile = req.Mobile
   user.NickName = req.NickName

   //密码加密
   options := &password.Options{16, 100, 32, sha512.New}
   salt, encodedPwd := password.Encode(req.PassWord, options)
   user.Password = fmt.Sprintf("$pbkdf2-sha512$%s$%s", salt, encodedPwd)

   result = global.DB.Create(&user)
   if result.Error != nil {
      return nil, status.Errorf(codes.Internal, result.Error.Error())
   }
   userInfoRsp := ModelToResponse(user)
   return &userInfoRsp, nil
}
1-9修改用户和校验密码接口
// UpdateUser 更新用户
func (s *UserServer) UpdateUser(ctx context.Context, req *proto.UpdateUserInfo) (*empty.Empty, error) {
   //个人中心更新用户
   var user model.User
   result := global.DB.First(&user, req.Id)
   if result.RowsAffected == 0 {
      return nil, status.Errorf(codes.NotFound, "用户不存在")
   }
   birthDay := time.Unix(int64(req.Birthday), 0)
   user.NickName = req.NickName
   user.Birthday = &birthDay
   user.Gender = req.Gender

   result = global.DB.Save(&user)
   if result.Error != nil {
      return nil, status.Errorf(codes.Internal, result.Error.Error())
   }
   return &empty.Empty{}, nil
}

// CheckPassWord 校验密码
func (s *UserServer) CheckPassWord(ctx context.Context, req *proto.PasswordCheckInfo) (*proto.CheckResponse, error) {
   //校验密码
   options := &password.Options{16, 100, 32, sha512.New}
   passwordInfo := strings.Split(req.EncryptedPassword, "$")
   check := password.Verify(req.Password, passwordInfo[2], passwordInfo[3], options)
   return &proto.CheckResponse{Success: check}, nil
}
1-10通过flag启动grpc服务
func main() {
   IP := flag.String("ip", "0.0.0.0", "ip地址")
   Port := flag.Int("port", 50051, "端口")

   flag.Parse()

   server := grpc.NewServer()
   proto.RegisterUserServer(server, &handler.UserServer{})
   lis, err := net.Listen("tcp", fmt.Sprintf("%s:%d", *IP, *Port))
   if err != nil {
      panic("failed to listen:" + err.Error())
   }
   err = server.Serve(lis)
   if err != nil {
      panic("failed to start")
   }
}
1-12测试用户微服务接口
package main

import (
	"context"
	"fmt"

	"google.golang.org/grpc"

	"mxshop_srvs/user_srv/proto"
)

var userClient proto.UserClient
var conn *grpc.ClientConn

func Init(){
	var err error
	conn, err = grpc.Dial("127.0.0.1:50051", grpc.WithInsecure())
	if err != nil {
		panic(err)
	}
	userClient = proto.NewUserClient(conn)
}

func TestGetUserList(){
	rsp, err := userClient.GetUserList(context.Background(), &proto.PageInfo{
		Pn:    1,
		PSize: 5,
	})
	if err != nil {
		panic(err)
	}
	for _, user := range rsp.Data {
		fmt.Println(user.Mobile, user.NickName, user.PassWord)
		checkRsp, err := userClient.CheckPassWord(context.Background(), &proto.PasswordCheckInfo{
			Password:          "admin123",
			EncryptedPassword: user.PassWord,
		})
		if err != nil {
			panic(err)
		}
		fmt.Println(checkRsp.Success)
	}
}

func TestCreateUser(){
	for i := 0; i<10; i++ {
		rsp, err := userClient.CreateUser(context.Background(), &proto.CreateUserInfo{
			NickName: fmt.Sprintf("bobby%d",i),
			Mobile: fmt.Sprintf("1878222222%d",i),
			PassWord: "admin123",
		})
		if err != nil {
			panic(err)
		}
		fmt.Println(rsp.Id)
	}
}

func main() {
	Init()
	//TestCreateUser()
	TestGetUserList()

	conn.Close()
}




也可以用这个工具测试
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值