1. 定义用户表结构
package model
import "time"
import "gorm.io/gorm"
type BaseModel struct {
ID int32 `gorm:"primary_key;AUTO_INCREMENT"`
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;not null;type:int comment '1表示普通用户,2表示管理员'"`
}
2.同步表结构
package main
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"gorm.io/gorm/schema"
"log"
"mxshop_server/usr_srv/model"
"os"
"time"
)
func main() {
dsn := "root:password@tcp(119.23.xxx.xxx:3306)/mxshop_user_srv?charset=utf8mb4&parseTime=True&loc=Local"
newLogger := logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags),
logger.Config{
SlowThreshold: time.Second,
LogLevel: logger.Info,
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{})
}
3. 使用md5盐值加密密码
options := &password.Options{16, 100, 32, sha512.New}
salt, encodePwd := password.Encode("generic password", options)
newPassword := fmt.Sprintf("$pbkdf2-sha512$%s$%s", salt, encodePwd)
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)
4. proto结构表定义
syntax = "proto3";
option go_package = ".;proto";
import "google/protobuf/empty.proto";
service User {
rpc GetUserList(PageInfo) returns (UserListResponse);
rpc GetUserByMobile(MobileRequest) returns (UserInfoResponse);
rpc GetUserById(IdRequest) returns (UserInfoResponse);
rpc CreateUser(CreateUserInfo) returns (UserInfoResponse);
rpc UpdateUser(UpdateUserInfo) returns (google.protobuf.Empty);
rpc CheckPassword(PasswordCheckInfo) returns (CheckReponse);
rpc DeleteUser(IdRequest) returns (google.protobuf.Empty);
}
message PasswordCheckInfo {
string password = 1;
string encryptedPassword = 2;
}
message CheckReponse {
bool success = 1;
}
message UpdateUserInfo {
int32 id = 1;
string nickName = 2;
string gender = 3;
uint64 birthDay = 4;
}
message CreateUserInfo {
string nickName = 1;
string passWord = 2;
string mobile = 3;
}
message PageInfo {
uint32 pn = 1;
uint32 pSize = 2;
}
message MobileRequest {
string mobile = 1;
}
message IdRequest {
int32 id = 1;
}
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;
}
5. 用户服务service接口的实现
package handler
import (
"context"
"crypto/sha512"
"fmt"
"github.com/anaskhan96/go-password-encoder"
"github.com/golang/protobuf/ptypes/empty"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/emptypb"
"gorm.io/gorm"
"mxshop_server/usr_srv/global"
"mxshop_server/usr_srv/model"
"strings"
"time"
)
import "mxshop_server/usr_srv/model/proto"
type UserServer struct {
}
func ModelToResponse(user model.User) proto.UserInfoResponse {
userInfoRsp := proto.UserInfoResponse{
Id: user.ID,
PassWord: user.Password,
Mobile: user.Mobile,
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)
}
}
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
}
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
}
func (s *UserServer) GetUserById(ctx context.Context, req *proto.IdRequest) (*proto.UserInfoResponse, error) {
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
}
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 = model.User{
Nickname: req.NickName,
Mobile: req.Mobile,
Password: req.PassWord,
}
options := &password.Options{16, 100, 32, sha512.New}
salt, encodePwd := password.Encode(req.PassWord, options)
user.Password = fmt.Sprintf("$pbkdf2-sha512$%s$%s", salt, encodePwd)
result = global.DB.Create(&user)
if result.Error != nil {
return nil, status.Errorf(codes.Internal, result.Error.Error())
}
userInfoRsp := ModelToResponse(user)
return &userInfoRsp, nil
}
func (s *UserServer) UpdateUser(ctx context.Context, req *proto.UpdateUserInfo) (*emptypb.Empty, error) {
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
}
birthDay := time.Unix(int64(req.BirthDay), 0)
user = model.User{
Nickname: req.NickName,
Gender: req.Gender,
Birthday: &birthDay,
}
result = global.DB.Save(&user)
if result.Error != nil {
return nil, status.Error(codes.Internal, result.Error.Error())
}
return &empty.Empty{}, nil
}
func (s *UserServer) CheckPassword(ctx context.Context, req *proto.PasswordCheckInfo) (*proto.CheckReponse, error) {
options := &password.Options{16, 100, 32, sha512.New}
passwordInfo := strings.Split(req.EncryptedPassword, "$")
fmt.Println(passwordInfo)
check := password.Verify(req.Password, passwordInfo[2], passwordInfo[3], options)
return &proto.CheckReponse{Success: check}, nil
}
func (s *UserServer) DeleteUser(ctx context.Context, req *proto.IdRequest) (*emptypb.Empty, error) {
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, status.Error(codes.Internal, result.Error.Error())
}
global.DB.Delete(&user)
return &emptypb.Empty{}, nil
}
6. 注册grpc服务+flag初始化ip和端口地址
package main
import (
"flag"
"fmt"
"google.golang.org/grpc"
"mxshop_server/usr_srv/handler"
"mxshop_server/usr_srv/proto"
"net"
)
func main() {
IP := flag.String("ip", "0.0.0.0", "ip地址")
Port := flag.Int("port", 50051, "端口号")
flag.Parse()
fmt.Println("ip: ", *IP)
fmt.Println("port: ", *Port)
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 grpc:" + err.Error())
}
}
7. 测试用例
package main
import (
"context"
"fmt"
"google.golang.org/grpc"
"mxshop_server/usr_srv/proto"
"time"
)
var userClient proto.UserClient
var conn *grpc.ClientConn
func Init() {
var err error
conn, err = grpc.Dial("localhost: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: 10,
})
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)
}
}
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 TestUpdateUser() {
rsp, err := userClient.UpdateUser(context.Background(), &proto.UpdateUserInfo{
Id: 1,
NickName: "bobby2333",
Gender: "male",
BirthDay: uint64(time.Now().Unix()),
})
if err != nil {
panic(err)
}
fmt.Println(rsp)
}
func TestDeleteUser() {
rsp, err := userClient.DeleteUser(context.Background(), &proto.IdRequest{
Id: 1,
})
if err != nil {
panic(err)
}
fmt.Println(rsp)
}
func TestGetUserById() {
rsp, err := userClient.GetUserById(context.Background(), &proto.IdRequest{
Id: 1,
})
if err != nil {
panic(err)
}
fmt.Println(rsp)
}
func main() {
Init()
TestGetUserList()
TestCreateUser()
TestUpdateUser()
TestGetUserById()
TestDeleteUser()
defer conn.Close()
}