一.用户微服务
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()
}
也可以用这个工具测试